From 431400d49cac4bac944fc2d989921003314667ae Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Sat, 21 Mar 2026 09:22:05 +0000 Subject: [PATCH 0001/5214] openrisc: mm: Fix section mismatch between map_page and __set_fixmap This warning was reported by the kernel test robot: WARNING: modpost: vmlinux: section mismatch in reference: __set_fixmap+0x84 (section: .text.unlikely) -> map_page.isra.0 (section: .init.text) With commit 4735037b5d9b ("openrisc: Add text patching API support") the __set_fixmap function was moved out of the .init.text section. However, the map_page helper that it uses was not moved. This was not noticed on gcc 15.1.0 where map_page gets inlined unlike lkp@intel.com which uses gcc 10.5.0. Fix this by also moving the map_page helper function out of the init section. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603211503.E8mMETO3-lkp@intel.com/ Fixes: 4735037b5d9b ("openrisc: Add text patching API support") Signed-off-by: Stafford Horne --- arch/openrisc/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index 78fb0734cdbc..50aee0ea18b7 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -196,7 +196,7 @@ void __init mem_init(void) return; } -static int __init map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) +static int map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) { p4d_t *p4d; pud_t *pud; From fc59cad7e896fdb7a07bd91f7c0952e2a0e12b36 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 29 Jul 2025 12:31:47 +0100 Subject: [PATCH 0002/5214] Input: lm8323 - remove space before newline There is an extraneous space before a newline in a dev_err message. Remove it Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250729113147.1924862-1-colin.i.king@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/lm8323.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index e19442c6f80f..d9df10484755 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -259,7 +259,7 @@ static void process_keys(struct lm8323_chip *lm) ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN); if (ret < 0) { - dev_err(&lm->client->dev, "Failed reading fifo \n"); + dev_err(&lm->client->dev, "Failed reading fifo\n"); return; } key_fifo[ret] = 0; From b99a1f0f18ee50445907f55069e88bcfd8947383 Mon Sep 17 00:00:00 2001 From: Jie Wang Date: Thu, 23 Apr 2026 13:39:34 +0000 Subject: [PATCH 0003/5214] gfs2: fix quota init duplicate scan gfs2_quota_init() checks for duplicate quota_change IDs while holding qd_lock and the quota hash bucket bitlock. That path used gfs2_qd_search_bucket(), which takes a lockref reference via lockref_get_not_dead(). On PREEMPT_RT this may sleep, which is not allowed under the bucket bitlock, triggering "sleeping function called from invalid context". Use a no-ref bucket lookup in this path, then continue duplicate handling without taking a lockref there. Refactor gfs2_qd_search_bucket() to build on top of the no-ref helper so lookup traversal stays in one place. This patch fixes a bug reported by syzbot. Reported-by: syzbot+642d0561f78362d67d3f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=642d0561f78362d67d3f Tested-by: syzbot+642d0561f78362d67d3f@syzkaller.appspotmail.com Signed-off-by: Jie Wang Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 5290865f27f1..934397248fe7 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -254,9 +254,13 @@ static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, str return NULL; } -static struct gfs2_quota_data *gfs2_qd_search_bucket(unsigned int hash, - const struct gfs2_sbd *sdp, - struct kqid qid) +/* + * Lookup variant for callers which already hold qd_lock + bucket lock. + */ +static struct gfs2_quota_data * +gfs2_qd_search_bucket_noref(unsigned int hash, + const struct gfs2_sbd *sdp, + struct kqid qid) { struct gfs2_quota_data *qd; struct hlist_bl_node *h; @@ -264,12 +268,22 @@ static struct gfs2_quota_data *gfs2_qd_search_bucket(unsigned int hash, hlist_bl_for_each_entry_rcu(qd, h, &qd_hash_table[hash], qd_hlist) { if (!qid_eq(qd->qd_id, qid)) continue; - if (qd->qd_sbd != sdp) - continue; - if (lockref_get_not_dead(&qd->qd_lockref)) { - list_lru_del_obj(&gfs2_qd_lru, &qd->qd_lru); + if (qd->qd_sbd == sdp) return qd; - } + } + + return NULL; +} + +static struct gfs2_quota_data * +gfs2_qd_search_bucket(unsigned int hash, const struct gfs2_sbd *sdp, struct kqid qid) +{ + struct gfs2_quota_data *qd; + + qd = gfs2_qd_search_bucket_noref(hash, sdp, qid); + if (qd && lockref_get_not_dead(&qd->qd_lockref)) { + list_lru_del_obj(&gfs2_qd_lru, &qd->qd_lru); + return qd; } return NULL; @@ -1458,7 +1472,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) spin_lock(&qd_lock); spin_lock_bucket(hash); - old_qd = gfs2_qd_search_bucket(hash, sdp, qc_id); + old_qd = gfs2_qd_search_bucket_noref(hash, sdp, qc_id); if (old_qd) { fs_err(sdp, "Corruption found in quota_change%u" "file: duplicate identifier in " @@ -1467,7 +1481,6 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) spin_unlock_bucket(hash); spin_unlock(&qd_lock); - qd_put(old_qd); gfs2_glock_put(qd->qd_gl); kmem_cache_free(gfs2_quotad_cachep, qd); From 942202677f8f2ee448a6a2feb06aeeaf520342e3 Mon Sep 17 00:00:00 2001 From: Jie Wang Date: Tue, 21 Apr 2026 16:32:07 +0000 Subject: [PATCH 0004/5214] gfs2: move quota_init qc iterator increment Move qc++ from the loop body into the for-loop increment expression in gfs2_quota_init(). This keeps iterator progression explicit and avoids mixing pointer advance with duplicate-slot handling in the loop body. Signed-off-by: Jie Wang Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 934397248fe7..91e9975d25e8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1447,7 +1447,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) qc = (struct gfs2_quota_change *)(bh->b_data + sizeof(struct gfs2_meta_header)); for (y = 0; y < sdp->sd_qc_per_block && slot < sdp->sd_quota_slots; - y++, slot++) { + y++, slot++, qc++) { struct gfs2_quota_data *old_qd, *qd; s64 qc_change = be64_to_cpu(qc->qc_change); u32 qc_flags = be32_to_cpu(qc->qc_flags); @@ -1455,7 +1455,6 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) USRQUOTA : GRPQUOTA; struct kqid qc_id = make_kqid(&init_user_ns, qtype, be32_to_cpu(qc->qc_id)); - qc++; if (!qc_change) continue; From 4e2e14c2b19095f7e82a98e202eb5b07a8c9f83c Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:01:31 -0700 Subject: [PATCH 0005/5214] Input: stmfts - fix the MODULE_LICENSE() string Replace the bogus "GPL v2" with "GPL" as MODULE_LICNSE() string. The value does not declare the module's exact license, but only lets the module loader test whether the module is Free Software or not. See commit bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL" vs. "GPL v2" bogosity") in the details of the issue. The fix is to use "GPL" for all modules under any variant of the GPL. Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-1-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 8af87d0b6eb6..def6bd0c8e05 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -807,4 +807,4 @@ module_i2c_driver(stmfts_driver); MODULE_AUTHOR("Andi Shyti "); MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); From 04336c6e93ad00fc1c51c561ad65edb96caecc70 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:02:06 -0700 Subject: [PATCH 0006/5214] Input: stmfts - use dev struct directly Makes the code better readable and noticably shorter. Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-2-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index def6bd0c8e05..86159e3b1a8b 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -619,6 +619,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata) static int stmfts_probe(struct i2c_client *client) { + struct device *dev = &client->dev; int err; struct stmfts_data *sdata; @@ -627,7 +628,7 @@ static int stmfts_probe(struct i2c_client *client) I2C_FUNC_SMBUS_I2C_BLOCK)) return -ENODEV; - sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL); + sdata = devm_kzalloc(dev, sizeof(*sdata), GFP_KERNEL); if (!sdata) return -ENOMEM; @@ -639,13 +640,13 @@ static int stmfts_probe(struct i2c_client *client) sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd"; sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd"; - err = devm_regulator_bulk_get(&client->dev, + err = devm_regulator_bulk_get(dev, ARRAY_SIZE(sdata->regulators), sdata->regulators); if (err) return err; - sdata->input = devm_input_allocate_device(&client->dev); + sdata->input = devm_input_allocate_device(dev); if (!sdata->input) return -ENOMEM; @@ -664,8 +665,7 @@ static int stmfts_probe(struct i2c_client *client) input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0); - sdata->use_key = device_property_read_bool(&client->dev, - "touch-key-connected"); + sdata->use_key = device_property_read_bool(dev, "touch-key-connected"); if (sdata->use_key) { input_set_capability(sdata->input, EV_KEY, KEY_MENU); input_set_capability(sdata->input, EV_KEY, KEY_BACK); @@ -685,20 +685,20 @@ static int stmfts_probe(struct i2c_client *client) * interrupts. To be on the safe side it's better to not enable * the interrupts during their request. */ - err = devm_request_threaded_irq(&client->dev, client->irq, + err = devm_request_threaded_irq(dev, client->irq, NULL, stmfts_irq_handler, IRQF_ONESHOT | IRQF_NO_AUTOEN, "stmfts_irq", sdata); if (err) return err; - dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n"); + dev_dbg(dev, "initializing ST-Microelectronics FTS...\n"); err = stmfts_power_on(sdata); if (err) return err; - err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata); + err = devm_add_action_or_reset(dev, stmfts_power_off, sdata); if (err) return err; @@ -715,13 +715,13 @@ static int stmfts_probe(struct i2c_client *client) * without LEDs. The ledvdd regulator pointer will be * used as a flag. */ - dev_warn(&client->dev, "unable to use touchkey leds\n"); + dev_warn(dev, "unable to use touchkey leds\n"); sdata->ledvdd = NULL; } } - pm_runtime_enable(&client->dev); - device_enable_async_suspend(&client->dev); + pm_runtime_enable(dev); + device_enable_async_suspend(dev); return 0; } From 18ac49e763af7e10064438da3d8f1c864db29a55 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:05:12 -0700 Subject: [PATCH 0007/5214] Input: stmfts - switch to devm_regulator_bulk_get_const Switch to devm_regulator_bulk_get_const() to stop setting the supplies list in probe(), and move the regulator_bulk_data struct in static const. Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-3-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 86159e3b1a8b..b01d363ad3e2 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -69,9 +69,9 @@ #define STMFTS_MAX_FINGERS 10 #define STMFTS_DEV_NAME "stmfts" -enum stmfts_regulators { - STMFTS_REGULATOR_VDD, - STMFTS_REGULATOR_AVDD, +static const struct regulator_bulk_data stmfts_supplies[] = { + { .supply = "vdd" }, + { .supply = "avdd" }, }; struct stmfts_data { @@ -82,7 +82,7 @@ struct stmfts_data { struct touchscreen_properties prop; - struct regulator_bulk_data regulators[2]; + struct regulator_bulk_data *supplies; /* * Presence of ledvdd will be used also to check @@ -523,8 +523,8 @@ static int stmfts_power_on(struct stmfts_data *sdata) int err; u8 reg[8]; - err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators), - sdata->regulators); + err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies), + sdata->supplies); if (err) return err; @@ -589,8 +589,7 @@ static void stmfts_power_off(void *data) struct stmfts_data *sdata = data; disable_irq(sdata->client->irq); - regulator_bulk_disable(ARRAY_SIZE(sdata->regulators), - sdata->regulators); + regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); } static int stmfts_enable_led(struct stmfts_data *sdata) @@ -638,11 +637,10 @@ static int stmfts_probe(struct i2c_client *client) mutex_init(&sdata->mutex); init_completion(&sdata->cmd_done); - sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd"; - sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd"; - err = devm_regulator_bulk_get(dev, - ARRAY_SIZE(sdata->regulators), - sdata->regulators); + err = devm_regulator_bulk_get_const(dev, + ARRAY_SIZE(stmfts_supplies), + stmfts_supplies, + &sdata->supplies); if (err) return err; From 9ea34baf20cd41e5fe4aa8f7bc3283196b0a2bc9 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:08:55 -0700 Subject: [PATCH 0008/5214] Input: stmfts - abstract reading information from the firmware Improves readability and makes splitting power on function in following commit easier. Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-4-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 35 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index b01d363ad3e2..1381a9ce23f2 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -518,22 +518,11 @@ static struct attribute *stmfts_sysfs_attrs[] = { }; ATTRIBUTE_GROUPS(stmfts_sysfs); -static int stmfts_power_on(struct stmfts_data *sdata) +static int stmfts_read_system_info(struct stmfts_data *sdata) { int err; u8 reg[8]; - err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies), - sdata->supplies); - if (err) - return err; - - /* - * The datasheet does not specify the power on time, but considering - * that the reset time is < 10ms, I sleep 20ms to be sure - */ - msleep(20); - err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_INFO, sizeof(reg), reg); if (err < 0) @@ -547,6 +536,28 @@ static int stmfts_power_on(struct stmfts_data *sdata) sdata->config_id = reg[4]; sdata->config_ver = reg[5]; + return 0; +} + +static int stmfts_power_on(struct stmfts_data *sdata) +{ + int err; + + err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies), + sdata->supplies); + if (err) + return err; + + /* + * The datasheet does not specify the power on time, but considering + * that the reset time is < 10ms, I sleep 20ms to be sure + */ + msleep(20); + + err = stmfts_read_system_info(sdata); + if (err) + return err; + enable_irq(sdata->client->irq); msleep(50); From 85c3d6e410eb16df8c359c935c4a9321b7d430c0 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:46:36 -0700 Subject: [PATCH 0009/5214] Input: stmfts - disable regulators and disable irq when power on fails We must power off regulators and ensure that IRQ is disabled when failing at power on phase. Create stmfts_configure function to limit use of goto. Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-5-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 59 +++++++++++++++++++----------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 1381a9ce23f2..d52c4873dfb0 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -539,29 +539,10 @@ static int stmfts_read_system_info(struct stmfts_data *sdata) return 0; } -static int stmfts_power_on(struct stmfts_data *sdata) +static int stmfts_configure(struct stmfts_data *sdata) { int err; - err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies), - sdata->supplies); - if (err) - return err; - - /* - * The datasheet does not specify the power on time, but considering - * that the reset time is < 10ms, I sleep 20ms to be sure - */ - msleep(20); - - err = stmfts_read_system_info(sdata); - if (err) - return err; - - enable_irq(sdata->client->irq); - - msleep(50); - err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); if (err) return err; @@ -586,13 +567,49 @@ static int stmfts_power_on(struct stmfts_data *sdata) if (err) return err; + return 0; +} + +static int stmfts_power_on(struct stmfts_data *sdata) +{ + int err; + + err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies), + sdata->supplies); + if (err) + return err; + + /* + * The datasheet does not specify the power on time, but considering + * that the reset time is < 10ms, I sleep 20ms to be sure + */ + msleep(20); + + err = stmfts_read_system_info(sdata); + if (err) + goto err_disable_regulators; + + enable_irq(sdata->client->irq); + + msleep(50); + + err = stmfts_configure(sdata); + if (err) + goto err_disable_irq; + /* * At this point no one is using the touchscreen * and I don't really care about the return value */ - (void) i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); + (void)i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); return 0; + +err_disable_irq: + disable_irq(sdata->client->irq); +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); + return err; } static void stmfts_power_off(void *data) From 9ecb0c045bebd832f75505fc86b590cfda0cd947 Mon Sep 17 00:00:00 2001 From: Petr Hodina Date: Sun, 26 Apr 2026 13:56:55 -0700 Subject: [PATCH 0010/5214] Input: stmfts - use client to make future code cleaner Make code cleaner, compiler will optimize it away anyway. Preparation for FTM5 support, where more steps are needed. Signed-off-by: Petr Hodina Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-6-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index d52c4873dfb0..1955419a0db5 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -772,9 +772,10 @@ static int stmfts_runtime_suspend(struct device *dev) static int stmfts_runtime_resume(struct device *dev) { struct stmfts_data *sdata = dev_get_drvdata(dev); + struct i2c_client *client = sdata->client; int ret; - ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT); + ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT); if (ret) dev_err(dev, "failed to resume device: %d\n", ret); From dd78ec391fbd56fad39e3c6d9a37e82df0439289 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 26 Apr 2026 13:57:18 -0700 Subject: [PATCH 0011/5214] dt-bindings: input: touchscreen: st,stmfts: Introduce reset GPIO FTS has associated reset GPIO, document it. Signed-off-by: David Heidelberg Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260409-stmfts5-v4-7-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/touchscreen/st,stmfts.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml index 12256ae7df90..64c4f24ea3dd 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml @@ -40,6 +40,10 @@ properties: vdd-supply: description: Power supply + reset-gpios: + description: Reset GPIO (active-low) + maxItems: 1 + required: - compatible - reg From 8a1f9de80e45951b43d6e1e287a07f7d94cadbe8 Mon Sep 17 00:00:00 2001 From: Petr Hodina Date: Sun, 26 Apr 2026 13:57:41 -0700 Subject: [PATCH 0012/5214] Input: stmfts - add optional reset GPIO support Add support for an optional "reset-gpios" property. If present, the driver drives the reset line high at probe time and releases it during power-on, after the regulators have been enabled. Signed-off-by: Petr Hodina Co-developed-by: David Heidelberg Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260409-stmfts5-v4-8-64fe62027db5@ixit.cz Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 1955419a0db5..43158d03ecc1 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -5,6 +5,7 @@ // Copyright (c) 2017 Andi Shyti #include +#include #include #include #include @@ -77,6 +78,7 @@ static const struct regulator_bulk_data stmfts_supplies[] = { struct stmfts_data { struct i2c_client *client; struct input_dev *input; + struct gpio_desc *reset_gpio; struct led_classdev led_cdev; struct mutex mutex; @@ -539,6 +541,15 @@ static int stmfts_read_system_info(struct stmfts_data *sdata) return 0; } +static void stmfts_reset(struct stmfts_data *sdata) +{ + gpiod_set_value_cansleep(sdata->reset_gpio, 1); + msleep(20); + + gpiod_set_value_cansleep(sdata->reset_gpio, 0); + msleep(50); +} + static int stmfts_configure(struct stmfts_data *sdata) { int err; @@ -585,6 +596,9 @@ static int stmfts_power_on(struct stmfts_data *sdata) */ msleep(20); + if (sdata->reset_gpio) + stmfts_reset(sdata); + err = stmfts_read_system_info(sdata); if (err) goto err_disable_regulators; @@ -617,6 +631,10 @@ static void stmfts_power_off(void *data) struct stmfts_data *sdata = data; disable_irq(sdata->client->irq); + + if (sdata->reset_gpio) + gpiod_set_value_cansleep(sdata->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies), sdata->supplies); } @@ -672,6 +690,11 @@ static int stmfts_probe(struct i2c_client *client) if (err) return err; + sdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sdata->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sdata->reset_gpio), + "Failed to get GPIO 'reset'\n"); + sdata->input = devm_input_allocate_device(dev); if (!sdata->input) return -ENOMEM; From 26b760d0f8148e4ee73936577b8b3be51dbb64d7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 26 Apr 2026 14:25:23 -0700 Subject: [PATCH 0013/5214] Input: stmfts - fix formatting issues Fix a few formatting issues reported by checkpatch. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmfts.c | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 43158d03ecc1..454f7e822e9c 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -109,7 +109,7 @@ struct stmfts_data { }; static int stmfts_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); @@ -252,7 +252,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata) u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; switch (event[0]) { - case STMFTS_EV_CONTROLLER_READY: case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: case STMFTS_EV_STATUS: @@ -265,7 +264,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata) } switch (event[0] & STMFTS_MASK_EVENT_ID) { - case STMFTS_EV_MULTI_TOUCH_ENTER: case STMFTS_EV_MULTI_TOUCH_MOTION: stmfts_report_contact_event(sdata, event); @@ -287,9 +285,9 @@ static void stmfts_parse_events(struct stmfts_data *sdata) case STMFTS_EV_ERROR: dev_warn(&sdata->client->dev, - "error code: 0x%x%x%x%x%x%x", - event[6], event[5], event[4], - event[3], event[2], event[1]); + "error code: 0x%x%x%x%x%x%x", + event[6], event[5], event[4], + event[3], event[2], event[1]); break; default: @@ -406,7 +404,7 @@ static void stmfts_input_close(struct input_dev *dev) } static ssize_t stmfts_sysfs_chip_id(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -414,7 +412,8 @@ static ssize_t stmfts_sysfs_chip_id(struct device *dev, } static ssize_t stmfts_sysfs_chip_version(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -422,7 +421,7 @@ static ssize_t stmfts_sysfs_chip_version(struct device *dev, } static ssize_t stmfts_sysfs_fw_ver(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -430,7 +429,7 @@ static ssize_t stmfts_sysfs_fw_ver(struct device *dev, } static ssize_t stmfts_sysfs_config_id(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -438,7 +437,8 @@ static ssize_t stmfts_sysfs_config_id(struct device *dev, } static ssize_t stmfts_sysfs_config_version(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -446,7 +446,8 @@ static ssize_t stmfts_sysfs_config_version(struct device *dev, } static ssize_t stmfts_sysfs_read_status(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); u8 status[4]; @@ -461,7 +462,8 @@ static ssize_t stmfts_sysfs_read_status(struct device *dev, } static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); @@ -469,8 +471,8 @@ static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, } static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) + struct device_attribute *attr, + const char *buf, size_t len) { struct stmfts_data *sdata = dev_get_drvdata(dev); unsigned long value; @@ -487,8 +489,8 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, if (hover != sdata->hover_enabled) { if (sdata->running) { err = i2c_smbus_write_byte(sdata->client, - value ? STMFTS_SS_HOVER_SENSE_ON : - STMFTS_SS_HOVER_SENSE_OFF); + value ? STMFTS_SS_HOVER_SENSE_ON : + STMFTS_SS_HOVER_SENSE_OFF); if (err) return err; } From babaad95670d1871860306beddd7ef1c4f114ffe Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 13 Apr 2026 11:02:43 +0200 Subject: [PATCH 0014/5214] thunderbolt: debugfs: Don't stop reading SB registers if just one fails The GEN4 TxFFE register is not part of the USB4 v1.0 specification, so understandably some pre-USB4v2 retimers (like the Parade PS8830) don't seem to implement it. The immediate idea to counter this would be to introduce a version check for that specific register, but on a second thought, the current flow only returns a quiet -EIO if there's any failures, without hinting at what the actual problem is. To take care of both of these issues, simply print an error line for each SB register read that fails and go on with attempting to read the others. Note that this is not quite in-spec behavior ("The SB Register Space registers shall have the structure and fields described in Table 4-17. Registers not listed in Table 4-20 are undefined and shall not be used."), but it's the easiest fix that shouldn't have real-world bad side effects. Fixes: 6d241fa00159 ("thunderbolt: Add sideband register access to debugfs") Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 042f6a0d0f7f..d089d2decedd 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -2361,8 +2361,10 @@ static int sb_regs_show(struct tb_port *port, const struct sb_reg *sb_regs, memset(data, 0, sizeof(data)); ret = usb4_port_sb_read(port, target, index, regs->reg, data, regs->size); - if (ret) - return ret; + if (ret) { + seq_printf(s, "0x%02x \n", regs->reg); + continue; + } seq_printf(s, "0x%02x", regs->reg); for (j = 0; j < regs->size; j++) From 78abd04ea19608dc8a025ba5b95cd21c5a86d3e6 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:10 -0600 Subject: [PATCH 0015/5214] iio: buffer: check return value of iio_compute_scan_bytes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check return value of iio_compute_scan_bytes() as it can return an error. The result is moved to an output parameter while we are touching this as we will need to add a second output parameter in a later change. The return type of iio_buffer_update_bytes_per_datum() also had to be changed to propagate the error. Signed-off-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 46f36a6ed271..71dfc81cb9e5 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -764,7 +764,8 @@ static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) } static int iio_compute_scan_bytes(struct iio_dev *indio_dev, - const unsigned long *mask, bool timestamp) + const unsigned long *mask, bool timestamp, + unsigned int *scan_bytes) { unsigned int bytes = 0; int length, i, largest = 0; @@ -790,8 +791,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, largest = max(largest, length); } - bytes = ALIGN(bytes, largest); - return bytes; + *scan_bytes = ALIGN(bytes, largest); + + return 0; } static void iio_buffer_activate(struct iio_dev *indio_dev, @@ -836,18 +838,23 @@ static int iio_buffer_disable(struct iio_buffer *buffer, return buffer->access->disable(buffer, indio_dev); } -static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, - struct iio_buffer *buffer) +static int iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, + struct iio_buffer *buffer) { unsigned int bytes; + int ret; if (!buffer->access->set_bytes_per_datum) - return; + return 0; - bytes = iio_compute_scan_bytes(indio_dev, buffer->scan_mask, - buffer->scan_timestamp); + ret = iio_compute_scan_bytes(indio_dev, buffer->scan_mask, + buffer->scan_timestamp, &bytes); + if (ret) + return ret; buffer->access->set_bytes_per_datum(buffer, bytes); + + return 0; } static int iio_buffer_request_update(struct iio_dev *indio_dev, @@ -855,7 +862,10 @@ static int iio_buffer_request_update(struct iio_dev *indio_dev, { int ret; - iio_buffer_update_bytes_per_datum(indio_dev, buffer); + ret = iio_buffer_update_bytes_per_datum(indio_dev, buffer); + if (ret) + return ret; + if (buffer->access->request_update) { ret = buffer->access->request_update(buffer); if (ret) { @@ -898,6 +908,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, struct iio_buffer *buffer; bool scan_timestamp; unsigned int modes; + int ret; if (insert_buffer && bitmap_empty(insert_buffer->scan_mask, masklength)) { @@ -985,8 +996,11 @@ static int iio_verify_update(struct iio_dev *indio_dev, scan_mask = compound_mask; } - config->scan_bytes = iio_compute_scan_bytes(indio_dev, - scan_mask, scan_timestamp); + ret = iio_compute_scan_bytes(indio_dev, scan_mask, scan_timestamp, + &config->scan_bytes); + if (ret) + return ret; + config->scan_mask = scan_mask; config->scan_timestamp = scan_timestamp; From cb27d8c18fa311ad87fca4c2e7f1d73ec0cb440d Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:11 -0600 Subject: [PATCH 0016/5214] iio: buffer: cache timestamp offset in scan buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cache the offset (in bytes) for the timestamp element in a scan buffer. This will be used later to ensure proper alignment of the timestamp element in the scan buffer. The new field could not be placed in struct iio_dev_opaque because we will need to access it in a static inline function later, so we make it __private instead. It is only intended to be used by core IIO code. Signed-off-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 14 +++++++++++--- include/linux/iio/iio.h | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 71dfc81cb9e5..d59ab57dc994 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -765,7 +765,8 @@ static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const unsigned long *mask, bool timestamp, - unsigned int *scan_bytes) + unsigned int *scan_bytes, + unsigned int *timestamp_offset) { unsigned int bytes = 0; int length, i, largest = 0; @@ -787,6 +788,10 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, return length; bytes = ALIGN(bytes, length); + + if (timestamp_offset) + *timestamp_offset = bytes; + bytes += length; largest = max(largest, length); } @@ -848,7 +853,7 @@ static int iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, return 0; ret = iio_compute_scan_bytes(indio_dev, buffer->scan_mask, - buffer->scan_timestamp, &bytes); + buffer->scan_timestamp, &bytes, NULL); if (ret) return ret; @@ -892,6 +897,7 @@ struct iio_device_config { unsigned int watermark; const unsigned long *scan_mask; unsigned int scan_bytes; + unsigned int scan_timestamp_offset; bool scan_timestamp; }; @@ -997,7 +1003,8 @@ static int iio_verify_update(struct iio_dev *indio_dev, } ret = iio_compute_scan_bytes(indio_dev, scan_mask, scan_timestamp, - &config->scan_bytes); + &config->scan_bytes, + &config->scan_timestamp_offset); if (ret) return ret; @@ -1155,6 +1162,7 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, indio_dev->active_scan_mask = config->scan_mask; ACCESS_PRIVATE(indio_dev, scan_timestamp) = config->scan_timestamp; indio_dev->scan_bytes = config->scan_bytes; + ACCESS_PRIVATE(indio_dev, scan_timestamp_offset) = config->scan_timestamp_offset; iio_dev_opaque->currentmode = config->mode; iio_update_demux(indio_dev); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 2c91b7659ce9..ecbaeecbe0ac 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -584,6 +584,8 @@ struct iio_buffer_setup_ops { * and owner * @buffer: [DRIVER] any buffer present * @scan_bytes: [INTERN] num bytes captured to be fed to buffer demux + * @scan_timestamp_offset: [INTERN] cache of the offset (in bytes) for the + * timestamp in the scan buffer * @available_scan_masks: [DRIVER] optional array of allowed bitmasks. Sort the * array in order of preference, the most preferred * masks first. @@ -610,6 +612,7 @@ struct iio_dev { struct iio_buffer *buffer; int scan_bytes; + unsigned int __private scan_timestamp_offset; const unsigned long *available_scan_masks; unsigned int __private masklength; From 99577250145316cb5840867f0278ddd8e0de8689 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:12 -0600 Subject: [PATCH 0017/5214] iio: buffer: ensure repeat alignment is a power of two MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use roundup_pow_of_two() in the calculation of iio_storage_bytes_for_si() when scan_type->repeat > 1 to ensure that the size is a power of two. storagebits is always going to be a power of two bytes, so we only need to apply this to the repeat factor. The storage size is also used for alignment, and we want to ensure that all alignments are a power of two. The only repeat in use in the kernel currently is for quaternions, which have a repeat of 4, so this does not change the result for existing users. Signed-off-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index d59ab57dc994..252ce4a6f913 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -750,7 +750,7 @@ static int iio_storage_bytes_for_si(struct iio_dev *indio_dev, bytes = scan_type->storagebits / 8; if (scan_type->repeat > 1) - bytes *= scan_type->repeat; + bytes *= roundup_pow_of_two(scan_type->repeat); return bytes; } From d05e3c1460e47aa61e58cc6947cf7c9fc1371d7e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 7 Mar 2026 19:44:13 -0600 Subject: [PATCH 0018/5214] iio: buffer: fix timestamp alignment when quaternion in scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix timestamp alignment when a scan buffer contains an element larger than sizeof(int64_t). Currently s32 quaternions are the only such element, and the one driver that has this (hid-sensor-rotation) has a workaround in place already so this change does not affect it. Previously, we assumed that the timestamp would always be 8-byte aligned relative to the end of the scan buffer, but in the case of a scan buffer a 16-byte quaternion vector, scan_bytes == 32, but the timestamp needs to be placed at offset 16, not 24. ts_offset is now a value in bytes so we have to change how the array access is done. Signed-off-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index d37f82678f71..8fd0d57d238f 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -34,8 +34,16 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, void *data, int64_t timestamp) { if (ACCESS_PRIVATE(indio_dev, scan_timestamp)) { - size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; - ((int64_t *)data)[ts_offset] = timestamp; + size_t ts_offset = ACCESS_PRIVATE(indio_dev, scan_timestamp_offset); + + /* + * The size of indio_dev->scan_bytes is always aligned to the + * largest scan element's alignment (see iio_compute_scan_bytes()). + * So there may be padding after the timestamp. ts_offset contains + * the offset in bytes that was already computed for correctly + * aligning the timestamp. + */ + *(int64_t *)(data + ts_offset) = timestamp; } return iio_push_to_buffers(indio_dev, data); From 5de55ae1300b4bd70eb39b6664b6c733eee08228 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 14 Mar 2026 16:38:25 -0500 Subject: [PATCH 0019/5214] iio: imu: bno055: add explicit scan buf layout Move the scan buf.chans array into a union along with a struct that gives the layout of the buffer with all channels enabled. Although not technically required in this case, if there had been a different number of items before the quaternion, there could have been a subtle bug with the special alignment needed for the quaternion channel data and the array would have been too small. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/bno055.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index c96fec2ebb3e..d0101607a9b3 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -210,9 +210,30 @@ struct bno055_priv { u8 uid[BNO055_UID_LEN]; struct gpio_desc *reset_gpio; bool sw_reset; - struct { - __le16 chans[BNO055_SCAN_CH_COUNT]; - aligned_s64 timestamp; + union { + IIO_DECLARE_BUFFER_WITH_TS(__le16, chans, BNO055_SCAN_CH_COUNT); + /* + * This struct is not used, but it is here to ensure proper size + * and alignment of the scan buffer above (because of the extra + * requirements of the quaternion field). Technically it is not + * needed in this case, because other fields just happen to make + * things correctly aligned already. But it is better to be + * explicit about the requirements anyway. The actual contents + * of the scan buffer will vary depending on which channels are + * enabled. + */ + struct { + __le16 acc[3]; + __le16 magn[3]; + __le16 gyr[3]; + __le16 yaw; + __le16 pitch; + __le16 roll; + IIO_DECLARE_QUATERNION(__le16, quaternion); + __le16 lia[3]; + __le16 gravity[3]; + aligned_s64 timestamp; + }; } buf; struct dentry *debugfs; }; From 45c45b3f83d636563d005e10e07c198c18b488db Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 1 Mar 2026 17:46:48 -0600 Subject: [PATCH 0020/5214] iio: orientation: hid-sensor-rotation: use ext_scan_type Make use of ext_scan_type to handle the dynamic realbits size of the quaternion data. This lets us implement it using static data rather than having to duplicate the channel info for each driver instance. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 71 +++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 5a5e6e4fbe34..4a11e4555099 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -39,6 +39,27 @@ static const u32 rotation_sensitivity_addresses[] = { HID_USAGE_SENSOR_ORIENT_QUATERNION, }; +enum { + DEV_ROT_SCAN_TYPE_16BIT, + DEV_ROT_SCAN_TYPE_32BIT, +}; + +static const struct iio_scan_type dev_rot_scan_types[] = { + [DEV_ROT_SCAN_TYPE_16BIT] = { + .sign = 's', + .realbits = 16, + /* Storage bits has to stay 32 to not break userspace. */ + .storagebits = 32, + .repeat = 4, + }, + [DEV_ROT_SCAN_TYPE_32BIT] = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .repeat = 4, + }, +}; + /* Channel definitions */ static const struct iio_chan_spec dev_rot_channels[] = { { @@ -50,23 +71,14 @@ static const struct iio_chan_spec dev_rot_channels[] = { BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_HYSTERESIS), - .scan_index = 0 + .scan_index = 0, + .has_ext_scan_type = 1, + .ext_scan_type = dev_rot_scan_types, + .num_ext_scan_type = ARRAY_SIZE(dev_rot_scan_types), }, IIO_CHAN_SOFT_TIMESTAMP(1) }; -/* Adjust channel real bits based on report descriptor */ -static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan, - int size) -{ - chan->scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - chan->scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - chan->scan_type.storagebits = sizeof(u32) * 8; - chan->scan_type.repeat = 4; -} - /* Channel read_raw handler */ static int dev_rot_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -141,9 +153,25 @@ static int dev_rot_write_raw(struct iio_dev *indio_dev, return ret; } +static int dev_rot_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + switch (rot_state->quaternion.size / 4) { + case sizeof(s16): + return DEV_ROT_SCAN_TYPE_16BIT; + case sizeof(s32): + return DEV_ROT_SCAN_TYPE_32BIT; + default: + return -EINVAL; + } +} + static const struct iio_info dev_rot_info = { .read_raw_multi = &dev_rot_read_raw, .write_raw = &dev_rot_write_raw, + .get_current_scan_type = &dev_rot_get_current_scan_type, }; /* Callback handler to send event after all samples are received and captured */ @@ -212,7 +240,6 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, /* Parse report which is specific to an usage id*/ static int dev_rot_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, - struct iio_chan_spec *channels, unsigned usage_id, struct dev_rot_state *st) { @@ -226,9 +253,6 @@ static int dev_rot_parse_report(struct platform_device *pdev, if (ret) return ret; - dev_rot_adjust_channel_bit_mask(&channels[0], - st->quaternion.size / 4); - dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index, st->quaternion.report_id); @@ -287,22 +311,13 @@ static int hid_dev_rot_probe(struct platform_device *pdev) return ret; } - indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels, - sizeof(dev_rot_channels), - GFP_KERNEL); - if (!indio_dev->channels) { - dev_err(&pdev->dev, "failed to duplicate channels\n"); - return -ENOMEM; - } - - ret = dev_rot_parse_report(pdev, hsdev, - (struct iio_chan_spec *)indio_dev->channels, - hsdev->usage, rot_state); + ret = dev_rot_parse_report(pdev, hsdev, hsdev->usage, rot_state); if (ret) { dev_err(&pdev->dev, "failed to setup attributes\n"); return ret; } + indio_dev->channels = dev_rot_channels; indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels); indio_dev->info = &dev_rot_info; indio_dev->name = name; From 5c2290c054f0746f34934540e7cb5160278f7e2b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 19 Feb 2026 15:39:36 +0100 Subject: [PATCH 0021/5214] iio: adc: ad7191: Don't check for specific errors when parsing properties Instead of checking for the specific error codes (that can be considered a layering violation to some extent) check for the property existence first and then either parse it, or apply a default value. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7191.c | 63 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c index d9cd903ffdd2..51ec199fb06f 100644 --- a/drivers/iio/adc/ad7191.c +++ b/drivers/iio/adc/ad7191.c @@ -154,27 +154,18 @@ static int ad7191_config_setup(struct iio_dev *indio_dev) const u32 gain[4] = { 1, 8, 64, 128 }; static u32 scale_buffer[4][2]; int odr_value, odr_index = 0, pga_value, pga_index = 0, i, ret; + const char *propname; u64 scale_uv; st->samp_freq_index = 0; st->scale_index = 0; - ret = device_property_read_u32(dev, "adi,odr-value", &odr_value); - if (ret && ret != -EINVAL) - return dev_err_probe(dev, ret, "Failed to get odr value.\n"); + propname = "adi,odr-value"; + if (device_property_present(dev, propname)) { + ret = device_property_read_u32(dev, propname, &odr_value); + if (ret) + return dev_err_probe(dev, ret, "Failed to get %s.\n", propname); - if (ret == -EINVAL) { - st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW); - if (IS_ERR(st->odr_gpios)) - return dev_err_probe(dev, PTR_ERR(st->odr_gpios), - "Failed to get odr gpios.\n"); - - if (st->odr_gpios->ndescs != 2) - return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n"); - - st->samp_freq_avail = samp_freq; - st->samp_freq_avail_size = ARRAY_SIZE(samp_freq); - } else { for (i = 0; i < ARRAY_SIZE(samp_freq); i++) { if (odr_value != samp_freq[i]) continue; @@ -186,6 +177,17 @@ static int ad7191_config_setup(struct iio_dev *indio_dev) st->samp_freq_avail_size = 1; st->odr_gpios = NULL; + } else { + st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW); + if (IS_ERR(st->odr_gpios)) + return dev_err_probe(dev, PTR_ERR(st->odr_gpios), + "Failed to get odr gpios.\n"); + + if (st->odr_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n"); + + st->samp_freq_avail = samp_freq; + st->samp_freq_avail_size = ARRAY_SIZE(samp_freq); } mutex_lock(&st->lock); @@ -200,22 +202,12 @@ static int ad7191_config_setup(struct iio_dev *indio_dev) mutex_unlock(&st->lock); - ret = device_property_read_u32(dev, "adi,pga-value", &pga_value); - if (ret && ret != -EINVAL) - return dev_err_probe(dev, ret, "Failed to get pga value.\n"); + propname = "adi,pga-value"; + if (device_property_present(dev, propname)) { + ret = device_property_read_u32(dev, propname, &pga_value); + if (ret) + return dev_err_probe(dev, ret, "Failed to get %s.\n", propname); - if (ret == -EINVAL) { - st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); - if (IS_ERR(st->pga_gpios)) - return dev_err_probe(dev, PTR_ERR(st->pga_gpios), - "Failed to get pga gpios.\n"); - - if (st->pga_gpios->ndescs != 2) - return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n"); - - st->scale_avail = scale_buffer; - st->scale_avail_size = ARRAY_SIZE(scale_buffer); - } else { for (i = 0; i < ARRAY_SIZE(gain); i++) { if (pga_value != gain[i]) continue; @@ -227,6 +219,17 @@ static int ad7191_config_setup(struct iio_dev *indio_dev) st->scale_avail_size = 1; st->pga_gpios = NULL; + } else { + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get pga gpios.\n"); + + if (st->pga_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n"); + + st->scale_avail = scale_buffer; + st->scale_avail_size = ARRAY_SIZE(scale_buffer); } st->temp_gpio = devm_gpiod_get(dev, "temp", GPIOD_OUT_LOW); From 7216b9f7e9fe34a1623cceee920caede8929078e Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:47:33 +0100 Subject: [PATCH 0022/5214] iio: imu: st_lsm6dsx: Fix check for invalid samples from FIFO The DRDY_MASK feature implemented in sensor chips marks gyroscope and accelerometer invalid samples (i.e. samples that have been acquired during the settling time of sensor filters) with the special values 0x7FFFh, 0x7FFE, and 0x7FFD. The driver checks FIFO samples against these special values in order to discard invalid samples; however, it does the check regardless of the type of samples being processed, whereas this feature is specific to gyroscope and accelerometer data. This could cause valid samples to be discarded. Fix the above check so that it takes into account the type of samples being processed. To avoid casting to __le16 * when checking sample values, clean up the type representation for data read from the FIFO. Fixes: 960506ed2c69 ("iio: imu: st_lsm6dsx: enable drdy-mask if available") Signed-off-by: Francesco Lavra Acked-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 5b28a3ffcc3d..8b79ade8e7de 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -365,8 +365,6 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, return 0; } -#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ - sizeof(s64)) + sizeof(s64)) /** * st_lsm6dsx_read_fifo() - hw FIFO read routine * @hw: Pointer to instance of struct st_lsm6dsx_hw. @@ -537,16 +535,23 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) } #define ST_LSM6DSX_INVALID_SAMPLE 0x7ffd -static int -st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, - u8 *data, s64 ts) +static bool st_lsm6dsx_check_data(u8 tag, __le16 *data) +{ + if ((tag == ST_LSM6DSX_GYRO_TAG || tag == ST_LSM6DSX_ACC_TAG) && + (s16)le16_to_cpup(data) >= ST_LSM6DSX_INVALID_SAMPLE) + return false; + + return true; +} + +static int st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, + __le16 *data, s64 ts) { - s16 val = le16_to_cpu(*(__le16 *)data); struct st_lsm6dsx_sensor *sensor; struct iio_dev *iio_dev; /* invalid sample during bootstrap phase */ - if (val >= ST_LSM6DSX_INVALID_SAMPLE) + if (!st_lsm6dsx_check_data(tag, data)) return -EINVAL; /* @@ -609,7 +614,13 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) * must be passed a buffer that is aligned to 8 bytes so * as to allow insertion of a naturally aligned timestamp. */ - u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); + struct { + union { + __le16 data[3]; + __le32 fifo_ts; + }; + aligned_s64 timestamp; + } iio_buff = { }; u8 tag; bool reset_ts = false; int i, err, read_len; @@ -648,7 +659,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) for (i = 0; i < pattern_len; i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) { - memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], + memcpy(&iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], ST_LSM6DSX_SAMPLE_SIZE); tag = hw->buff[i] >> 3; @@ -659,7 +670,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], * B3 = ts[31:24] */ - ts = le32_to_cpu(*((__le32 *)iio_buff)); + ts = le32_to_cpu(iio_buff.fifo_ts); /* * check if hw timestamp engine is going to * reset (the sensor generates an interrupt @@ -670,7 +681,8 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) reset_ts = true; ts *= hw->ts_gain; } else { - st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, + st_lsm6dsx_push_tagged_data(hw, tag, + iio_buff.data, ts); } } From fda05af070e7bc0403a487fc87378d2e1323f529 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:47:40 +0100 Subject: [PATCH 0023/5214] iio: Replace 'sign' field with union in struct iio_scan_type This field is used to differentiate between signed and unsigned integers. A following commit will extend its use in order to add support for non- integer scan elements; therefore, replace it with a union that contains a more generic 'format' field. This union will be dropped when all drivers are changed to use the format field. Opportunistically replace character literals with symbolic constants that represent the set of allowed values for the format field. Signed-off-by: Francesco Lavra Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/buffers.rst | 4 ++-- include/linux/iio/iio.h | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index 63f364e862d1..e16abaf826fe 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -78,7 +78,7 @@ fields in iio_chan_spec definition:: /* other members */ int scan_index struct { - char sign; + char format; u8 realbits; u8 storagebits; u8 shift; @@ -98,7 +98,7 @@ following channel definition:: /* other stuff here */ .scan_index = 0, .scan_type = { - .sign = 's', + .format = IIO_SCAN_FORMAT_SIGNED_INT, .realbits = 12, .storagebits = 16, .shift = 4, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index ecbaeecbe0ac..e03b7e912d7d 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -176,9 +176,25 @@ struct iio_event_spec { unsigned long mask_shared_by_all; }; +/** + * define IIO_SCAN_FORMAT_SIGNED_INT - signed integer data format + * + * &iio_scan_type.format value for signed integers (two's complement). + */ +#define IIO_SCAN_FORMAT_SIGNED_INT 's' + +/** + * define IIO_SCAN_FORMAT_UNSIGNED_INT - unsigned integer data format + * + * &iio_scan_type.format value for unsigned integers. + */ +#define IIO_SCAN_FORMAT_UNSIGNED_INT 'u' + /** * struct iio_scan_type - specification for channel data format in buffer - * @sign: 's' or 'u' to specify signed or unsigned + * @sign: Deprecated, use @format instead. + * @format: Data format, can have any of the IIO_SCAN_FORMAT_* + * values. * @realbits: Number of valid bits of data * @storagebits: Realbits + padding * @shift: Shift right by this before masking out realbits. @@ -189,7 +205,10 @@ struct iio_event_spec { * @endianness: little or big endian */ struct iio_scan_type { - char sign; + union { + char sign; + char format; + }; u8 realbits; u8 storagebits; u8 shift; From cdd445d4b2a910ade742c3d6a86bc3fca4da9e0c Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:47:46 +0100 Subject: [PATCH 0024/5214] iio: tools: Add support for floating-point types in buffer scan elements A subsequent commit will add floating-point support to the ABI; enhance the iio_generic_buffer tool to be able to parse this new data format. Signed-off-by: Francesco Lavra Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 70 ++++++++++++++++++++++++++++++---- tools/iio/iio_utils.c | 12 +++--- tools/iio/iio_utils.h | 4 +- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index bc82bb6a7a2a..000193612aad 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -89,12 +90,19 @@ static void print1byte(uint8_t input, struct iio_channel_info *info) */ input >>= info->shift; input &= info->mask; - if (info->is_signed) { + switch (info->format) { + case 's': { int8_t val = (int8_t)(input << (8 - info->bits_used)) >> (8 - info->bits_used); printf("%05f ", ((float)val + info->offset) * info->scale); - } else { + break; + } + case 'u': printf("%05f ", ((float)input + info->offset) * info->scale); + break; + case 'f': + printf(" "); + break; } } @@ -112,12 +120,30 @@ static void print2byte(uint16_t input, struct iio_channel_info *info) */ input >>= info->shift; input &= info->mask; - if (info->is_signed) { + switch (info->format) { + case 's': { int16_t val = (int16_t)(input << (16 - info->bits_used)) >> (16 - info->bits_used); printf("%05f ", ((float)val + info->offset) * info->scale); - } else { + break; + } + case 'u': printf("%05f ", ((float)input + info->offset) * info->scale); + break; + case 'f': { +#if defined(__FLT16_MAX__) + union { + uint16_t u; + _Float16 f; + } converter; + + converter.u = input; + printf("%05f ", ((float)converter.f + info->offset) * info->scale); +#else + printf(" "); +#endif + break; + } } } @@ -135,12 +161,26 @@ static void print4byte(uint32_t input, struct iio_channel_info *info) */ input >>= info->shift; input &= info->mask; - if (info->is_signed) { + switch (info->format) { + case 's': { int32_t val = (int32_t)(input << (32 - info->bits_used)) >> (32 - info->bits_used); printf("%05f ", ((float)val + info->offset) * info->scale); - } else { + break; + } + case 'u': printf("%05f ", ((float)input + info->offset) * info->scale); + break; + case 'f': { + union { + uint32_t u; + float f; + } converter; + + converter.u = input; + printf("%05f ", (converter.f + info->offset) * info->scale); + break; + } } } @@ -158,7 +198,8 @@ static void print8byte(uint64_t input, struct iio_channel_info *info) */ input >>= info->shift; input &= info->mask; - if (info->is_signed) { + switch (info->format) { + case 's': { int64_t val = (int64_t)(input << (64 - info->bits_used)) >> (64 - info->bits_used); /* special case for timestamp */ @@ -167,8 +208,21 @@ static void print8byte(uint64_t input, struct iio_channel_info *info) else printf("%05f ", ((float)val + info->offset) * info->scale); - } else { + break; + } + case 'u': printf("%05f ", ((float)input + info->offset) * info->scale); + break; + case 'f': { + union { + uint64_t u; + double f; + } converter; + + converter.u = input; + printf("%05f ", (converter.f + info->offset) * info->scale); + break; + } } } diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index c5c5082cb24e..e274a0d8376b 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -70,7 +70,7 @@ int iioutils_break_up_name(const char *full_name, char **generic_name) /** * iioutils_get_type() - find and process _type attribute data - * @is_signed: output whether channel is signed + * @format: output channel format * @bytes: output how many bytes the channel storage occupies * @bits_used: output number of valid bits of data * @shift: output amount of bits to shift right data before applying bit mask @@ -83,7 +83,7 @@ int iioutils_break_up_name(const char *full_name, char **generic_name) * * Returns a value >= 0 on success, otherwise a negative error code. **/ -static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, +static int iioutils_get_type(char *format, unsigned int *bytes, unsigned int *bits_used, unsigned int *shift, uint64_t *mask, unsigned int *be, const char *device_dir, int buffer_idx, @@ -93,7 +93,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, int ret; DIR *dp; char *scan_el_dir, *builtname, *builtname_generic, *filename = 0; - char signchar, endianchar; + char formatchar, endianchar; unsigned padint; const struct dirent *ent; @@ -140,7 +140,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, ret = fscanf(sysfsfp, "%ce:%c%u/%u>>%u", &endianchar, - &signchar, + &formatchar, bits_used, &padint, shift); if (ret < 0) { @@ -162,7 +162,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, else *mask = (1ULL << *bits_used) - 1ULL; - *is_signed = (signchar == 's'); + *format = formatchar; if (fclose(sysfsfp)) { ret = -errno; fprintf(stderr, "Failed to close %s\n", @@ -487,7 +487,7 @@ int build_channel_array(const char *device_dir, int buffer_idx, if ((ret < 0) && (ret != -ENOENT)) goto error_cleanup_array; - ret = iioutils_get_type(¤t->is_signed, + ret = iioutils_get_type(¤t->format, ¤t->bytes, ¤t->bits_used, ¤t->shift, diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index 663c94a6c705..8c72f002d050 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -32,7 +32,7 @@ extern const char *iio_dir; * @shift: amount of bits to shift right data before applying bit mask * @mask: a bit mask for the raw output * @be: flag if data is big endian - * @is_signed: is the raw value stored signed + * @format: format of the raw value * @location: data offset for this channel inside the buffer (in bytes) **/ struct iio_channel_info { @@ -46,7 +46,7 @@ struct iio_channel_info { unsigned shift; uint64_t mask; unsigned be; - unsigned is_signed; + char format; unsigned location; }; From b29f00ef08a7afdedc0b36cc0bead579d6977ced Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:47:53 +0100 Subject: [PATCH 0025/5214] iio: ABI: Add support for floating-point numbers in buffer scan elements In the data storage description of a scan element, the first character after the colon can have the values 's' and 'u' to specify signed and unsigned integers, respectively. Add 'f' as an allowed value to specify floating-point numbers formatted according to the IEEE 754 standard. Signed-off-by: Francesco Lavra Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 33 +++++++++++++----------- Documentation/driver-api/iio/buffers.rst | 3 ++- Documentation/iio/iio_devbuf.rst | 3 ++- include/linux/iio/iio.h | 7 +++++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 4fc9f6bd4281..851cfe4f0a05 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1510,21 +1510,24 @@ Contact: linux-iio@vger.kernel.org Description: Description of the scan element data storage within the buffer and hence the form in which it is read from user-space. - Form is [be|le]:[s|u]bits/storagebits[>>shift]. - be or le specifies big or little endian. s or u specifies if - signed (2's complement) or unsigned. bits is the number of bits - of data and storagebits is the space (after padding) that it - occupies in the buffer. shift if specified, is the shift that - needs to be applied prior to masking out unused bits. Some - devices put their data in the middle of the transferred elements - with additional information on both sides. Note that some - devices will have additional information in the unused bits - so to get a clean value, the bits value must be used to mask - the buffer output value appropriately. The storagebits value - also specifies the data alignment. So s48/64>>2 will be a - signed 48 bit integer stored in a 64 bit location aligned to - a 64 bit boundary. To obtain the clean value, shift right 2 - and apply a mask to zero the top 16 bits of the result. + Form is [be|le]:[f|s|u]bits/storagebits[>>shift]. + be or le specifies big or little endian. f means floating-point + (IEEE 754 binary format), s means signed (2's complement), u means + unsigned. bits is the number of bits of data and storagebits is the + space (after padding) that it occupies in the buffer; when using a + floating-point format, bits must be one of the width values defined + in the IEEE 754 standard for binary interchange formats (e.g. 16 + indicates the binary16 format for half-precision numbers). shift, + if specified, is the shift that needs to be applied prior to + masking out unused bits. Some devices put their data in the middle + of the transferred elements with additional information on both + sides. Note that some devices will have additional information in + the unused bits, so to get a clean value the bits value must be + used to mask the buffer output value appropriately. The storagebits + value also specifies the data alignment. So s48/64>>2 will be a + signed 48 bit integer stored in a 64 bit location aligned to a 64 + bit boundary. To obtain the clean value, shift right 2 and apply a + mask to zero the top 16 bits of the result. For other storage combinations this attribute will be extended appropriately. diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index e16abaf826fe..8779022e3da5 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -37,9 +37,10 @@ directory contains attributes of the following form: * :file:`index`, the scan_index of the channel. * :file:`type`, description of the scan element data storage within the buffer and hence the form in which it is read from user space. - Format is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift] . + Format is [be|le]:[f|s|u]bits/storagebits[Xrepeat][>>shift] . * *be* or *le*, specifies big or little endian. + * *f*, specifies if floating-point. * *s* or *u*, specifies if signed (2's complement) or unsigned. * *bits*, is the number of valid data bits. * *storagebits*, is the number of bits (after padding) that it occupies in the diff --git a/Documentation/iio/iio_devbuf.rst b/Documentation/iio/iio_devbuf.rst index dca1f0200b0d..e91730fa3cea 100644 --- a/Documentation/iio/iio_devbuf.rst +++ b/Documentation/iio/iio_devbuf.rst @@ -83,9 +83,10 @@ and the relevant _type attributes to establish the data storage format. Read-only attribute containing the description of the scan element data storage within the buffer and hence the form in which it is read from userspace. Format -is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift], where: +is [be|le]:[f|s|u]bits/storagebits[Xrepeat][>>shift], where: - **be** or **le** specifies big or little-endian. +- **f** specifies if floating-point. - **s** or **u** specifies if signed (2's complement) or unsigned. - **bits** is the number of valid data bits. - **storagebits** is the number of bits (after padding) that it occupies in the diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index e03b7e912d7d..96b05c86c325 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -190,6 +190,13 @@ struct iio_event_spec { */ #define IIO_SCAN_FORMAT_UNSIGNED_INT 'u' +/** + * define IIO_SCAN_FORMAT_FLOAT - floating-point data format + * + * &iio_scan_type.format value for IEEE 754 floating-point numbers. + */ +#define IIO_SCAN_FORMAT_FLOAT 'f' + /** * struct iio_scan_type - specification for channel data format in buffer * @sign: Deprecated, use @format instead. From dd59392a3ec23dbe3a3fabadcf3bb847e9592ccd Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:47:59 +0100 Subject: [PATCH 0026/5214] iio: ABI: Add quaternion axis modifier This modifier applies to the IIO_ROT channel type, and indicates a data representation that specifies the {x, y, z} components of the normalized quaternion vector. Signed-off-by: Francesco Lavra Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 15 +++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 1 + 4 files changed, 18 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 851cfe4f0a05..60dfeff8f2c9 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1755,6 +1755,21 @@ Description: measurement from channel Y. Units after application of scale and offset are milliamps. +What: /sys/bus/iio/devices/iio:deviceX/in_rot_quaternionaxis_raw +KernelVersion: 7.1 +Contact: linux-iio@vger.kernel.org +Description: + Raw value of {x, y, z} components of the quaternion vector. These + components represent the axis about which a rotation occurs, and are + subject to the following constraints: + + - the quaternion vector is normalized, i.e. w^2 + x^2 + y^2 + z^2 = 1 + - the rotation angle is within the [-pi, pi] range, i.e. the w + component (which represents the amount of rotation) is non-negative + + These constraints allow the w value to be calculated from the other + components: w = sqrt(1 - (x^2 + y^2 + z^2)). + What: /sys/.../iio:deviceX/in_energy_en What: /sys/.../iio:deviceX/in_distance_en What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 22eefd048ba9..bd6f4f9f4533 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -157,6 +157,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_ACTIVE] = "active", [IIO_MOD_REACTIVE] = "reactive", [IIO_MOD_APPARENT] = "apparent", + [IIO_MOD_QUATERNION_AXIS] = "quaternionaxis", }; /* relies on pairs of these shared then separate */ diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 6d269b844271..d7c2bb223651 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -113,6 +113,7 @@ enum iio_modifier { IIO_MOD_ACTIVE, IIO_MOD_REACTIVE, IIO_MOD_APPARENT, + IIO_MOD_QUATERNION_AXIS, }; enum iio_event_type { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 03ca33869ce8..df6c43d7738d 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -145,6 +145,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_ACTIVE] = "active", [IIO_MOD_REACTIVE] = "reactive", [IIO_MOD_APPARENT] = "apparent", + [IIO_MOD_QUATERNION_AXIS] = "quaternionaxis", }; static bool event_is_known(struct iio_event_data *event) From cd4e1141bff8b86c75c425acb84eca453e936a9d Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 24 Mar 2026 09:48:07 +0100 Subject: [PATCH 0027/5214] iio: imu: st_lsm6dsx: Add support for rotation sensor Some IMU chips in the LSM6DSX family have sensor fusion features that combine data from the accelerometer and gyroscope. One of these features generates rotation vector data and makes it available in the hardware FIFO as a quaternion (more specifically, the X, Y and Z components of the quaternion vector, expressed as 16-bit half-precision floating-point numbers). Add support for a new sensor instance that allows receiving sensor fusion data, by defining a new struct st_lsm6dsx_fusion_settings (which contains chip-specific details for the sensor fusion functionality), and adding this struct as a new field in struct st_lsm6dsx_settings. In st_lsm6dsx_core.c, populate this new struct for the LSM6DSV and LSM6DSV16X chips, and add the logic to initialize an additional IIO device if this struct is populated for the hardware type being probed. Note: a new IIO device is being defined (as opposed to adding channels to an existing device) because the rate at which sensor fusion data is generated may not match the data rate from any of the existing devices. Tested on LSM6DSV16X. Signed-off-by: Francesco Lavra Acked-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/Makefile | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 28 +- .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 9 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 58 +++++ .../iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c | 241 ++++++++++++++++++ 5 files changed, 331 insertions(+), 7 deletions(-) create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile index 57cbcd67d64f..19a488254de3 100644 --- a/drivers/iio/imu/st_lsm6dsx/Makefile +++ b/drivers/iio/imu/st_lsm6dsx/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \ - st_lsm6dsx_shub.o + st_lsm6dsx_shub.o st_lsm6dsx_fusion.o obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 07b1773c87bd..767aadfe7061 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -294,6 +294,7 @@ enum st_lsm6dsx_sensor_id { ST_LSM6DSX_ID_EXT0, ST_LSM6DSX_ID_EXT1, ST_LSM6DSX_ID_EXT2, + ST_LSM6DSX_ID_FUSION, ST_LSM6DSX_ID_MAX }; @@ -301,6 +302,17 @@ enum st_lsm6dsx_ext_sensor_id { ST_LSM6DSX_ID_MAGN, }; +struct st_lsm6dsx_fusion_settings { + const struct iio_chan_spec *chan; + int chan_len; + struct st_lsm6dsx_reg odr_reg; + int odr_hz[ST_LSM6DSX_ODR_LIST_SIZE]; + int odr_len; + struct st_lsm6dsx_reg fifo_enable; + struct st_lsm6dsx_reg page_mux; + struct st_lsm6dsx_reg enable; +}; + /** * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings * @i2c_addr: I2c slave address list. @@ -388,6 +400,7 @@ struct st_lsm6dsx_settings { struct st_lsm6dsx_hw_ts_settings ts_settings; struct st_lsm6dsx_shub_settings shub_settings; struct st_lsm6dsx_event_settings event_settings; + struct st_lsm6dsx_fusion_settings fusion_settings; }; enum st_lsm6dsx_fifo_mode { @@ -510,6 +523,9 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val); int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name); int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len); +int st_lsm6dsx_fusion_probe(struct st_lsm6dsx_hw *hw, const char *name); +int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); +int st_lsm6dsx_fusion_set_odr(struct st_lsm6dsx_sensor *sensor, bool enable); int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable); static inline int @@ -564,12 +580,14 @@ st_lsm6dsx_get_mount_matrix(const struct iio_dev *iio_dev, static inline int st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) { - if (sensor->id == ST_LSM6DSX_ID_EXT0 || - sensor->id == ST_LSM6DSX_ID_EXT1 || - sensor->id == ST_LSM6DSX_ID_EXT2) + switch (sensor->id) { + case ST_LSM6DSX_ID_EXT0 ... ST_LSM6DSX_ID_EXT2: return st_lsm6dsx_shub_set_enable(sensor, enable); - - return st_lsm6dsx_sensor_set_enable(sensor, enable); + case ST_LSM6DSX_ID_FUSION: + return st_lsm6dsx_fusion_set_enable(sensor, enable); + default: + return st_lsm6dsx_sensor_set_enable(sensor, enable); + } } static const diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 8b79ade8e7de..777d479f10c0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -88,6 +88,7 @@ enum st_lsm6dsx_fifo_tag { ST_LSM6DSX_EXT0_TAG = 0x0f, ST_LSM6DSX_EXT1_TAG = 0x10, ST_LSM6DSX_EXT2_TAG = 0x11, + ST_LSM6DSX_ROT_TAG = 0x13, }; static const @@ -226,8 +227,11 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, u8 data; /* Only internal sensors have a FIFO ODR configuration register. */ - if (sensor->id >= ARRAY_SIZE(hw->settings->batch)) + if (sensor->id >= ARRAY_SIZE(hw->settings->batch)) { + if (sensor->id == ST_LSM6DSX_ID_FUSION) + return st_lsm6dsx_fusion_set_odr(sensor, enable); return 0; + } batch_reg = &hw->settings->batch[sensor->id]; if (batch_reg->addr) { @@ -585,6 +589,9 @@ static int st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, case ST_LSM6DSX_EXT2_TAG: iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; break; + case ST_LSM6DSX_ROT_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_FUSION]; + break; default: return -EINVAL; } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 450cb5b47346..630e2cae6f19 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -94,6 +94,26 @@ #define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f +/* Raw values from the IMU are 16-bit half-precision floating-point numbers. */ +#define ST_LSM6DSX_CHANNEL_ROT \ +{ \ + .type = IIO_ROT, \ + .modified = 1, \ + .channel2 = IIO_MOD_QUATERNION_AXIS, \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type = { \ + .format = IIO_SCAN_FORMAT_FLOAT, \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + .repeat = 3, \ + }, \ + .ext_info = st_lsm6dsx_ext_info, \ +} + static const struct iio_event_spec st_lsm6dsx_ev_motion[] = { { .type = IIO_EV_TYPE_THRESH, @@ -153,6 +173,11 @@ static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const struct iio_chan_spec st_lsm6dsx_fusion_channels[] = { + ST_LSM6DSX_CHANNEL_ROT, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { { .reset = { @@ -1492,6 +1517,33 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, }, + .fusion_settings = { + .chan = st_lsm6dsx_fusion_channels, + .chan_len = ARRAY_SIZE(st_lsm6dsx_fusion_channels), + .odr_reg = { + .addr = 0x5e, + .mask = GENMASK(5, 3), + }, + .odr_hz[0] = 15, + .odr_hz[1] = 30, + .odr_hz[2] = 60, + .odr_hz[3] = 120, + .odr_hz[4] = 240, + .odr_hz[5] = 480, + .odr_len = 6, + .fifo_enable = { + .addr = 0x44, + .mask = BIT(1), + }, + .page_mux = { + .addr = 0x01, + .mask = BIT(7), + }, + .enable = { + .addr = 0x04, + .mask = BIT(1), + }, + }, }, { .reset = { @@ -2899,6 +2951,12 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } + if (hw->settings->fusion_settings.chan) { + err = st_lsm6dsx_fusion_probe(hw, name); + if (err) + return err; + } + if (hw->irq > 0) { err = st_lsm6dsx_irq_setup(hw); if (err < 0) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c new file mode 100644 index 000000000000..2e72c7ba94dd --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_lsm6dsx IMU sensor fusion + * + * Copyright 2026 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +static int +st_lsm6dsx_fusion_get_odr_val(const struct st_lsm6dsx_fusion_settings *settings, + u32 odr_mHz, u8 *val) +{ + int odr_hz = odr_mHz / MILLI; + int i; + + for (i = 0; i < settings->odr_len; i++) { + if (settings->odr_hz[i] == odr_hz) + break; + } + if (i == settings->odr_len) + return -EINVAL; + + *val = i; + return 0; +} + +/** + * st_lsm6dsx_fusion_page_enable - Enable access to sensor fusion configuration + * registers. + * @hw: Sensor hardware instance. + * + * Return: 0 on success, negative value on error. + */ +static int st_lsm6dsx_fusion_page_enable(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *mux; + + mux = &hw->settings->fusion_settings.page_mux; + + return regmap_set_bits(hw->regmap, mux->addr, mux->mask); +} + +/** + * st_lsm6dsx_fusion_page_disable - Disable access to sensor fusion + * configuration registers. + * @hw: Sensor hardware instance. + * + * Return: 0 on success, negative value on error. + */ +static int st_lsm6dsx_fusion_page_disable(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *mux; + + mux = &hw->settings->fusion_settings.page_mux; + + return regmap_clear_bits(hw->regmap, mux->addr, mux->mask); +} + +static int st_lsm6dsx_fusion_set_odr_locked(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + const struct st_lsm6dsx_fusion_settings *settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + int err; + + settings = &hw->settings->fusion_settings; + if (enable) { + const struct st_lsm6dsx_reg *reg = &settings->odr_reg; + u8 odr_val; + u8 data; + + st_lsm6dsx_fusion_get_odr_val(settings, sensor->hwfifo_odr_mHz, + &odr_val); + data = ST_LSM6DSX_SHIFT_VAL(odr_val, reg->mask); + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + data); + if (err) + return err; + } + + return regmap_assign_bits(hw->regmap, settings->fifo_enable.addr, + settings->fifo_enable.mask, enable); +} + +int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *en_reg; + int err; + + guard(mutex)(&hw->page_lock); + + en_reg = &hw->settings->fusion_settings.enable; + err = st_lsm6dsx_fusion_page_enable(hw); + if (err) + return err; + + err = regmap_assign_bits(hw->regmap, en_reg->addr, en_reg->mask, enable); + if (err) { + st_lsm6dsx_fusion_page_disable(hw); + return err; + } + + return st_lsm6dsx_fusion_page_disable(hw); +} + +int st_lsm6dsx_fusion_set_odr(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + int err; + + guard(mutex)(&hw->page_lock); + + err = st_lsm6dsx_fusion_page_enable(hw); + if (err) + return err; + + err = st_lsm6dsx_fusion_set_odr_locked(sensor, enable); + if (err) { + st_lsm6dsx_fusion_page_disable(hw); + return err; + } + + return st_lsm6dsx_fusion_page_disable(hw); +} + +static int st_lsm6dsx_fusion_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->hwfifo_odr_mHz / MILLI; + *val2 = (sensor->hwfifo_odr_mHz % MILLI) * (MICRO / MILLI); + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int st_lsm6dsx_fusion_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + const struct st_lsm6dsx_fusion_settings *settings; + int err; + + settings = &sensor->hw->settings->fusion_settings; + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + u32 odr_mHz = val * MILLI + val2 * (MILLI / MICRO); + u8 odr_val; + + /* check that the requested frequency is supported */ + err = st_lsm6dsx_fusion_get_odr_val(settings, odr_mHz, &odr_val); + if (err) + return err; + + sensor->hwfifo_odr_mHz = odr_mHz; + return 0; + } + default: + return -EINVAL; + } +} + +static int st_lsm6dsx_fusion_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(indio_dev); + const struct st_lsm6dsx_fusion_settings *settings; + + settings = &sensor->hw->settings->fusion_settings; + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = settings->odr_hz; + *type = IIO_VAL_INT; + *length = settings->odr_len; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info st_lsm6dsx_fusion_info = { + .read_raw = st_lsm6dsx_fusion_read_raw, + .read_avail = st_lsm6dsx_fusion_read_avail, + .write_raw = st_lsm6dsx_fusion_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +int st_lsm6dsx_fusion_probe(struct st_lsm6dsx_hw *hw, const char *name) +{ + const struct st_lsm6dsx_fusion_settings *settings; + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + int ret; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return -ENOMEM; + + settings = &hw->settings->fusion_settings; + sensor = iio_priv(iio_dev); + sensor->id = ST_LSM6DSX_ID_FUSION; + sensor->hw = hw; + sensor->hwfifo_odr_mHz = settings->odr_hz[0] * MILLI; + sensor->watermark = 1; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->info = &st_lsm6dsx_fusion_info; + iio_dev->channels = settings->chan; + iio_dev->num_channels = settings->chan_len; + ret = snprintf(sensor->name, sizeof(sensor->name), "%s_fusion", name); + if (ret >= sizeof(sensor->name)) + return -E2BIG; + iio_dev->name = sensor->name; + + /* + * Put the IIO device pointer in the iio_devs array so that the caller + * can set up a buffer and register this IIO device. + */ + hw->iio_devs[ST_LSM6DSX_ID_FUSION] = iio_dev; + + return 0; +} From 671b8541c7d62818e0fb8ac7fe8a4086fc7c842e Mon Sep 17 00:00:00 2001 From: Carlos Jones Jr Date: Tue, 31 Mar 2026 09:24:56 +0800 Subject: [PATCH 0028/5214] iio: adc: ltc2309: add read delay for ltc2305 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LTC2305 requires a minimum 1.6μs delay between the I2C write operation (channel selection) and the subsequent read operation to allow the chip to process the command and prepare the result. While not explicitly documented in the datasheet, this timing requirement was identified by the hardware designer as necessary for reliable operation. Add a read_delay_us field to both the ltc2309_chip_info and ltc2309 device structures to support chip-specific timing requirements. Use fsleep() to implement the delay when non-zero, with LTC2305 set to 2μs (1.6μs requirement rounded up). LTC2309 does not require additional delay beyond inherent I2C bus timing. This extends the existing LTC2305 support added in (commit 8625d418d24b ("iio: adc: ltc2309: add support for ltc2305")) with the missing inter-transaction delay. Signed-off-by: Carlos Jones Jr Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2309.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 316256edf150..094966289922 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -9,7 +9,9 @@ * * Copyright (c) 2023, Liam Beguin */ +#include #include +#include #include #include #include @@ -34,12 +36,14 @@ * @client: I2C reference * @lock: Lock to serialize data access * @vref_mv: Internal voltage reference + * @read_delay_us: Chip-specific read delay in microseconds */ struct ltc2309 { struct device *dev; struct i2c_client *client; struct mutex lock; /* serialize data access */ int vref_mv; + unsigned int read_delay_us; }; /* Order matches expected channel address, See datasheet Table 1. */ @@ -118,12 +122,14 @@ static const struct iio_chan_spec ltc2309_channels[] = { struct ltc2309_chip_info { const char *name; const struct iio_chan_spec *channels; + unsigned int read_delay_us; int num_channels; }; static const struct ltc2309_chip_info ltc2305_chip_info = { .name = "ltc2305", .channels = ltc2305_channels, + .read_delay_us = 2, .num_channels = ARRAY_SIZE(ltc2305_channels), }; @@ -151,6 +157,9 @@ static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, return ret; } + if (ltc2309->read_delay_us) + fsleep(ltc2309->read_delay_us); + ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2); if (ret < 0) { dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret)); @@ -206,6 +215,7 @@ static int ltc2309_probe(struct i2c_client *client) ltc2309->dev = &indio_dev->dev; ltc2309->client = client; + ltc2309->read_delay_us = chip_info->read_delay_us; indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; From 7a89047a361c347cafe98f432d3df1686592ecce Mon Sep 17 00:00:00 2001 From: Carlos Jones Jr Date: Tue, 31 Mar 2026 09:24:57 +0800 Subject: [PATCH 0029/5214] iio: adc: ltc2309: Optimize chip_info structure layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the ltc2309_chip_info structure with better type safety and memory efficiency: - Add __counted_by_ptr() annotation to the channels pointer, linking it to num_channels for improved bounds checking and kernel hardening - Reorder structure fields to minimize padding: * Place read_delay_us before num_channels * This reduces struct size and eliminates internal gaps - Reorder field initialization to match the structure definition order The __counted_by_ptr() attribute enables compile-time and runtime verification that array accesses to channels[] stay within the bounds specified by num_channels, improving memory safety. Signed-off-by: Carlos Jones Jr Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2309.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 094966289922..87b78d0353f1 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -121,22 +121,22 @@ static const struct iio_chan_spec ltc2309_channels[] = { struct ltc2309_chip_info { const char *name; - const struct iio_chan_spec *channels; unsigned int read_delay_us; int num_channels; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); }; static const struct ltc2309_chip_info ltc2305_chip_info = { .name = "ltc2305", - .channels = ltc2305_channels, .read_delay_us = 2, .num_channels = ARRAY_SIZE(ltc2305_channels), + .channels = ltc2305_channels, }; static const struct ltc2309_chip_info ltc2309_chip_info = { .name = "ltc2309", - .channels = ltc2309_channels, .num_channels = ARRAY_SIZE(ltc2309_channels), + .channels = ltc2309_channels, }; static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, From 3bfc60fc10121ea3640b11c5c11cb9be72817186 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 5 Apr 2026 21:39:23 -0700 Subject: [PATCH 0030/5214] iio: adc: ti-ads7950: switch to using guard() notation guard() notation allows early returns when encountering errors, making control flow more obvious. Use it. Reviewed-by: David Lechner Reviewed-by: Bartosz Golaszewski Signed-off-by: Dmitry Torokhov Tested-by: David Lechner Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 79 +++++++++++++----------------------- 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 028acd42741f..6e9ea9cc33bf 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -299,18 +299,19 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) struct ti_ads7950_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->slock); - ret = spi_sync(st->spi, &st->ring_msg); - if (ret < 0) - goto out; + do { + guard(mutex)(&st->slock); - iio_push_to_buffers_with_ts_unaligned(indio_dev, &st->rx_buf[2], - sizeof(*st->rx_buf) * - TI_ADS7950_MAX_CHAN, - iio_get_time_ns(indio_dev)); + ret = spi_sync(st->spi, &st->ring_msg); + if (ret) + break; + + iio_push_to_buffers_with_ts_unaligned(indio_dev, &st->rx_buf[2], + sizeof(*st->rx_buf) * + TI_ADS7950_MAX_CHAN, + iio_get_time_ns(indio_dev)); + } while (0); -out: - mutex_unlock(&st->slock); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -321,20 +322,16 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) struct ti_ads7950_state *st = iio_priv(indio_dev); int ret, cmd; - mutex_lock(&st->slock); + guard(mutex)(&st->slock); + cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch)); st->single_tx = cmd; ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) - goto out; + return ret; - ret = st->single_rx; - -out: - mutex_unlock(&st->slock); - - return ret; + return st->single_rx; } static int ti_ads7950_get_range(struct ti_ads7950_state *st) @@ -400,9 +397,8 @@ static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, int value) { struct ti_ads7950_state *st = gpiochip_get_data(chip); - int ret; - mutex_lock(&st->slock); + guard(mutex)(&st->slock); if (value) st->cmd_settings_bitmask |= BIT(offset); @@ -410,11 +406,8 @@ static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, st->cmd_settings_bitmask &= ~BIT(offset); st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); - ret = spi_sync(st->spi, &st->scan_single_msg); - mutex_unlock(&st->slock); - - return ret; + return spi_sync(st->spi, &st->scan_single_msg); } static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) @@ -423,13 +416,12 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) bool state; int ret; - mutex_lock(&st->slock); + guard(mutex)(&st->slock); /* If set as output, return the output */ if (st->gpio_cmd_settings_bitmask & BIT(offset)) { state = st->cmd_settings_bitmask & BIT(offset); - ret = 0; - goto out; + return state; } /* GPIO data bit sets SDO bits 12-15 to GPIO input */ @@ -437,7 +429,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) - goto out; + return ret; state = (st->single_rx >> 12) & BIT(offset); @@ -446,12 +438,9 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) - goto out; + return ret; -out: - mutex_unlock(&st->slock); - - return ret ?: state; + return state; } static int ti_ads7950_get_direction(struct gpio_chip *chip, @@ -467,9 +456,8 @@ static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset, int input) { struct ti_ads7950_state *st = gpiochip_get_data(chip); - int ret = 0; - mutex_lock(&st->slock); + guard(mutex)(&st->slock); /* Only change direction if needed */ if (input && (st->gpio_cmd_settings_bitmask & BIT(offset))) @@ -477,15 +465,11 @@ static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset, else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset))) st->gpio_cmd_settings_bitmask |= BIT(offset); else - goto out; + return 0; st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st); - ret = spi_sync(st->spi, &st->scan_single_msg); -out: - mutex_unlock(&st->slock); - - return ret; + return spi_sync(st->spi, &st->scan_single_msg); } static int ti_ads7950_direction_input(struct gpio_chip *chip, @@ -508,9 +492,9 @@ static int ti_ads7950_direction_output(struct gpio_chip *chip, static int ti_ads7950_init_hw(struct ti_ads7950_state *st) { - int ret = 0; + int ret; - mutex_lock(&st->slock); + guard(mutex)(&st->slock); /* Settings for Manual/Auto1/Auto2 commands */ /* Default to 5v ref */ @@ -518,17 +502,12 @@ static int ti_ads7950_init_hw(struct ti_ads7950_state *st) st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) - goto out; + return ret; /* Settings for GPIO command */ st->gpio_cmd_settings_bitmask = 0x0; st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st); - ret = spi_sync(st->spi, &st->scan_single_msg); - -out: - mutex_unlock(&st->slock); - - return ret; + return spi_sync(st->spi, &st->scan_single_msg); } static int ti_ads7950_probe(struct spi_device *spi) From 1c3b46b9dec4349f81f432b8ad0829714d3757ba Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 5 Apr 2026 21:39:24 -0700 Subject: [PATCH 0031/5214] iio: adc: ti-ads7950: simplify check for spi_setup() failures spi_setup() specifies that it returns 0 on success or negative error on failure. Therefore we can simply check for the return code being 0 or not. Reviewed-by: David Lechner Reviewed-by: Bartosz Golaszewski Signed-off-by: Dmitry Torokhov Tested-by: David Lechner Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 6e9ea9cc33bf..c31c706c92a9 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -520,7 +520,7 @@ static int ti_ads7950_probe(struct spi_device *spi) spi->bits_per_word = 16; spi->mode |= SPI_CS_WORD; ret = spi_setup(spi); - if (ret < 0) { + if (ret) { dev_err(&spi->dev, "Error in spi setup\n"); return ret; } From 86f6d0949e1272d97f91f550bf6f0ccd79fbf17e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 5 Apr 2026 21:39:25 -0700 Subject: [PATCH 0032/5214] iio: adc: ti-ads7950: switch to using devm_regulator_get_enable_read_voltage() The regulator is enabled for the entire time the driver is bound to the device, and we only need to access it to fetch voltage, which can be done at probe time. Switch to using devm_regulator_get_enable_read_voltage() which simplifies probing and unbinding code. Suggested-by: David Lechner Reviewed-by: Bartosz Golaszewski Signed-off-by: Dmitry Torokhov Tested-by: David Lechner Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 48 ++++++++++-------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index c31c706c92a9..0b98c8e7385d 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -334,19 +334,9 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) return st->single_rx; } -static int ti_ads7950_get_range(struct ti_ads7950_state *st) +static unsigned int ti_ads7950_get_range(struct ti_ads7950_state *st) { - int vref; - - if (st->vref_mv) { - vref = st->vref_mv; - } else { - vref = regulator_get_voltage(st->reg); - if (vref < 0) - return vref; - - vref /= 1000; - } + unsigned int vref = st->vref_mv; if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V) vref *= 2; @@ -375,11 +365,7 @@ static int ti_ads7950_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = ti_ads7950_get_range(st); - if (ret < 0) - return ret; - - *val = ret; + *val = ti_ads7950_get_range(st); *val2 = (1 << chan->scan_type.realbits) - 1; return IIO_VAL_FRACTIONAL; @@ -573,30 +559,25 @@ static int ti_ads7950_probe(struct spi_device *spi) spi_message_init_with_transfers(&st->scan_single_msg, st->scan_single_xfer, 3); - /* Use hard coded value for reference voltage in ACPI case */ - if (ACPI_COMPANION(&spi->dev)) - st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT; - mutex_init(&st->slock); - st->reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(st->reg)) { - ret = dev_err_probe(&spi->dev, PTR_ERR(st->reg), - "Failed to get regulator \"vref\"\n"); - goto error_destroy_mutex; - } + /* Use hard coded value for reference voltage in ACPI case */ + if (ACPI_COMPANION(&spi->dev)) { + st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT; + } else { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "Failed to get regulator \"vref\"\n"); - ret = regulator_enable(st->reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n"); - goto error_destroy_mutex; + st->vref_mv = ret / 1000; } ret = iio_triggered_buffer_setup(indio_dev, NULL, &ti_ads7950_trigger_handler, NULL); if (ret) { dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - goto error_disable_reg; + goto error_destroy_mutex; } ret = ti_ads7950_init_hw(st); @@ -636,8 +617,6 @@ static int ti_ads7950_probe(struct spi_device *spi) iio_device_unregister(indio_dev); error_cleanup_ring: iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - regulator_disable(st->reg); error_destroy_mutex: mutex_destroy(&st->slock); @@ -652,7 +631,6 @@ static void ti_ads7950_remove(struct spi_device *spi) gpiochip_remove(&st->chip); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(st->reg); mutex_destroy(&st->slock); } From 71ddf1d8ffe06a1c0ab8f1ab71c08d5b38983f75 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 5 Apr 2026 21:39:26 -0700 Subject: [PATCH 0033/5214] iio: adc: ti-ads7950: complete conversion to using managed resources All resources that the driver needs have managed API now. Switch to using them to make code clearer and drop ti_ads7950_remove(). Reviewed-by: David Lechner Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Tested-by: David Lechner Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 70 ++++++++++++------------------------ 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 0b98c8e7385d..882b280d9e0b 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -506,10 +506,8 @@ static int ti_ads7950_probe(struct spi_device *spi) spi->bits_per_word = 16; spi->mode |= SPI_CS_WORD; ret = spi_setup(spi); - if (ret) { - dev_err(&spi->dev, "Error in spi setup\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "Error in spi setup\n"); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) @@ -517,8 +515,6 @@ static int ti_ads7950_probe(struct spi_device *spi) st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - st->spi = spi; info = spi_get_device_match_data(spi); @@ -559,7 +555,9 @@ static int ti_ads7950_probe(struct spi_device *spi) spi_message_init_with_transfers(&st->scan_single_msg, st->scan_single_xfer, 3); - mutex_init(&st->slock); + ret = devm_mutex_init(&spi->dev, &st->slock); + if (ret) + return ret; /* Use hard coded value for reference voltage in ACPI case */ if (ACPI_COMPANION(&spi->dev)) { @@ -573,24 +571,22 @@ static int ti_ads7950_probe(struct spi_device *spi) st->vref_mv = ret / 1000; } - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &ti_ads7950_trigger_handler, NULL); - if (ret) { - dev_err(&spi->dev, "Failed to setup triggered buffer\n"); - goto error_destroy_mutex; - } + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ti_ads7950_trigger_handler, + NULL); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to setup triggered buffer\n"); ret = ti_ads7950_init_hw(st); - if (ret) { - dev_err(&spi->dev, "Failed to init adc chip\n"); - goto error_cleanup_ring; - } + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to init adc chip\n"); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device\n"); - goto error_cleanup_ring; - } + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to register iio device\n"); /* Add GPIO chip */ st->chip.label = dev_name(&st->spi->dev); @@ -605,33 +601,12 @@ static int ti_ads7950_probe(struct spi_device *spi) st->chip.get = ti_ads7950_get; st->chip.set = ti_ads7950_set; - ret = gpiochip_add_data(&st->chip, st); - if (ret) { - dev_err(&spi->dev, "Failed to init GPIOs\n"); - goto error_iio_device; - } + ret = devm_gpiochip_add_data(&spi->dev, &st->chip, st); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to init GPIOs\n"); return 0; - -error_iio_device: - iio_device_unregister(indio_dev); -error_cleanup_ring: - iio_triggered_buffer_cleanup(indio_dev); -error_destroy_mutex: - mutex_destroy(&st->slock); - - return ret; -} - -static void ti_ads7950_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ti_ads7950_state *st = iio_priv(indio_dev); - - gpiochip_remove(&st->chip); - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - mutex_destroy(&st->slock); } static const struct spi_device_id ti_ads7950_id[] = { @@ -674,7 +649,6 @@ static struct spi_driver ti_ads7950_driver = { .of_match_table = ads7950_of_table, }, .probe = ti_ads7950_probe, - .remove = ti_ads7950_remove, .id_table = ti_ads7950_id, }; module_spi_driver(ti_ads7950_driver); From b9b7a64576b80020b99452f32cae5639403d4198 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:44 +0300 Subject: [PATCH 0034/5214] iio: adc: ad7949: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index b35d299a3977..ebc629bcfd4d 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -341,8 +341,8 @@ static int ad7949_spi_probe(struct spi_device *spi) } else if (spi_is_bpw_supported(spi, 8)) { spi->bits_per_word = 8; } else { - dev_err(dev, "unable to find common BPW with spi controller\n"); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "unable to find common BPW with spi controller\n"); } /* Setup internal voltage reference */ @@ -357,8 +357,8 @@ static int ad7949_spi_probe(struct spi_device *spi) ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_4096; break; default: - dev_err(dev, "unsupported internal voltage reference\n"); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "unsupported internal voltage reference\n"); } /* Setup external voltage reference, buffered? */ @@ -382,10 +382,9 @@ static int ad7949_spi_probe(struct spi_device *spi) if (ad7949_adc->refsel & AD7949_CFG_VAL_REF_EXTERNAL) { ret = regulator_enable(ad7949_adc->vref); - if (ret < 0) { - dev_err(dev, "fail to enable regulator\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, + "fail to enable regulator\n"); ret = devm_add_action_or_reset(dev, ad7949_disable_reg, ad7949_adc->vref); @@ -396,16 +395,10 @@ static int ad7949_spi_probe(struct spi_device *spi) mutex_init(&ad7949_adc->lock); ret = ad7949_spi_init(ad7949_adc); - if (ret) { - dev_err(dev, "fail to init this device: %d\n", ret); - return ret; - } - - ret = devm_iio_device_register(dev, indio_dev); if (ret) - dev_err(dev, "fail to register iio device: %d\n", ret); + return dev_err_probe(dev, ret, "fail to init this device\n"); - return ret; + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id ad7949_spi_of_id[] = { From bca122aff0a3d4bc1e5725e6a5c52e82c323a840 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:45 +0300 Subject: [PATCH 0035/5214] iio: adc: ad7780: add dev variable Add a local struct device pointer to simplify repeated &spi->dev dereferences throughout the probe function. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7780.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 24d2dcad8f4d..4c1990e27213 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -307,11 +307,12 @@ static void ad7780_reg_disable(void *reg) static int ad7780_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct ad7780_state *st; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -329,11 +330,11 @@ static int ad7780_probe(struct spi_device *spi) indio_dev->num_channels = 1; indio_dev->info = &ad7780_info; - ret = ad7780_init_gpios(&spi->dev, st); + ret = ad7780_init_gpios(dev, st); if (ret) return ret; - st->reg = devm_regulator_get(&spi->dev, "avdd"); + st->reg = devm_regulator_get(dev, "avdd"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); @@ -343,15 +344,15 @@ static int ad7780_probe(struct spi_device *spi) return ret; } - ret = devm_add_action_or_reset(&spi->dev, ad7780_reg_disable, st->reg); + ret = devm_add_action_or_reset(dev, ad7780_reg_disable, st->reg); if (ret) return ret; - ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7780_id[] = { From cd256a392bb5950a599962c328c6db07da2f02af Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:46 +0300 Subject: [PATCH 0036/5214] iio: adc: ad7780: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7780.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 4c1990e27213..26382b1f8c9e 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -264,16 +264,12 @@ static const struct iio_info ad7780_info = { static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st) { - int ret; - st->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); - if (IS_ERR(st->powerdown_gpio)) { - ret = PTR_ERR(st->powerdown_gpio); - dev_err(dev, "Failed to request powerdown GPIO: %d\n", ret); - return ret; - } + if (IS_ERR(st->powerdown_gpio)) + return dev_err_probe(dev, PTR_ERR(st->powerdown_gpio), + "Failed to request powerdown GPIO\n"); if (!st->chip_info->is_ad778x) return 0; @@ -282,20 +278,16 @@ static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st) st->gain_gpio = devm_gpiod_get_optional(dev, "adi,gain", GPIOD_OUT_HIGH); - if (IS_ERR(st->gain_gpio)) { - ret = PTR_ERR(st->gain_gpio); - dev_err(dev, "Failed to request gain GPIO: %d\n", ret); - return ret; - } + if (IS_ERR(st->gain_gpio)) + return dev_err_probe(dev, PTR_ERR(st->gain_gpio), + "Failed to request gain GPIO\n"); st->filter_gpio = devm_gpiod_get_optional(dev, "adi,filter", GPIOD_OUT_HIGH); - if (IS_ERR(st->filter_gpio)) { - ret = PTR_ERR(st->filter_gpio); - dev_err(dev, "Failed to request filter GPIO: %d\n", ret); - return ret; - } + if (IS_ERR(st->filter_gpio)) + return dev_err_probe(dev, PTR_ERR(st->filter_gpio), + "Failed to request filter GPIO\n"); return 0; } @@ -339,10 +331,9 @@ static int ad7780_probe(struct spi_device *spi) return PTR_ERR(st->reg); ret = regulator_enable(st->reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable specified AVdd supply\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable specified AVdd supply\n"); ret = devm_add_action_or_reset(dev, ad7780_reg_disable, st->reg); if (ret) From b659f18deffce462caf1846a181141fa10dd648e Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:47 +0300 Subject: [PATCH 0037/5214] iio: adc: ad7793: add dev variable Add a local struct device pointer to simplify repeated &spi->dev dereferences throughout the probe function. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 8ff7b70d6632..21c667e37b31 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -774,7 +774,8 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { static int ad7793_probe(struct spi_device *spi) { - const struct ad7793_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device *dev = &spi->dev; + const struct ad7793_platform_data *pdata = dev_get_platdata(dev); struct ad7793_state *st; struct iio_dev *indio_dev; int ret, vref_mv = 0; @@ -789,7 +790,7 @@ static int ad7793_probe(struct spi_device *spi) return -ENODEV; } - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; @@ -798,7 +799,7 @@ static int ad7793_probe(struct spi_device *spi) ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); if (pdata->refsel != AD7793_REFSEL_INTERNAL) { - ret = devm_regulator_get_enable_read_voltage(&spi->dev, "refin"); + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); if (ret < 0) return ret; @@ -816,7 +817,7 @@ static int ad7793_probe(struct spi_device *spi) indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = st->chip_info->iio_info; - ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); if (ret) return ret; @@ -824,7 +825,7 @@ static int ad7793_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7793_id[] = { From d145e05835bf853a00d28f4109b8883e5a3c67c9 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:48 +0300 Subject: [PATCH 0038/5214] iio: adc: ad7793: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 21c667e37b31..624530cce938 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -278,8 +278,8 @@ static int ad7793_setup(struct iio_dev *indio_dev, id &= AD7793_ID_MASK; if (id != st->chip_info->id) { - ret = -ENODEV; - dev_err(&st->sd.spi->dev, "device ID query failed\n"); + ret = dev_err_probe(&st->sd.spi->dev, -ENODEV, + "device ID query failed\n"); goto out; } @@ -338,8 +338,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, return 0; out: - dev_err(&st->sd.spi->dev, "setup failed\n"); - return ret; + return dev_err_probe(&st->sd.spi->dev, ret, "setup failed\n"); } static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, @@ -780,15 +779,11 @@ static int ad7793_probe(struct spi_device *spi) struct iio_dev *indio_dev; int ret, vref_mv = 0; - if (!pdata) { - dev_err(&spi->dev, "no platform data?\n"); - return -ENODEV; - } + if (!pdata) + return dev_err_probe(dev, -ENODEV, "no platform data?\n"); - if (!spi->irq) { - dev_err(&spi->dev, "no IRQ?\n"); - return -ENODEV; - } + if (!spi->irq) + return dev_err_probe(dev, -ENODEV, "no IRQ?\n"); indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) From 655c5e04e1aba4895525095863ac786ff51bd906 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:49 +0300 Subject: [PATCH 0039/5214] iio: adc: ad7292: add dev variable Add a local struct device pointer to simplify repeated &spi->dev dereferences throughout the probe function. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7292.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index a398973f313d..64d40b053582 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -253,12 +253,13 @@ static const struct iio_info ad7292_info = { static int ad7292_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct ad7292_state *st; struct iio_dev *indio_dev; bool diff_channels = false; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -271,7 +272,7 @@ static int ad7292_probe(struct spi_device *spi) return -EINVAL; } - ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); if (ret < 0 && ret != -ENODEV) return ret; @@ -281,7 +282,7 @@ static int ad7292_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad7292_info; - device_for_each_child_node_scoped(&spi->dev, child) { + device_for_each_child_node_scoped(dev, child) { diff_channels = fwnode_property_read_bool(child, "diff-channels"); if (diff_channels) @@ -296,7 +297,7 @@ static int ad7292_probe(struct spi_device *spi) indio_dev->channels = ad7292_channels; } - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7292_id_table[] = { From 567485a8e6afac7162550286095335bfa9792bd8 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:50 +0300 Subject: [PATCH 0040/5214] iio: adc: ad7292: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7292.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index 64d40b053582..e5ad83d2240a 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -267,10 +267,9 @@ static int ad7292_probe(struct spi_device *spi) st->spi = spi; ret = ad7292_spi_reg_read(st, AD7292_REG_VENDOR_ID); - if (ret != ADI_VENDOR_ID) { - dev_err(&spi->dev, "Wrong vendor id 0x%x\n", ret); - return -EINVAL; - } + if (ret != ADI_VENDOR_ID) + return dev_err_probe(dev, -EINVAL, + "Wrong vendor id 0x%x\n", ret); ret = devm_regulator_get_enable_read_voltage(dev, "vref"); if (ret < 0 && ret != -ENODEV) From d9e85469a7db69ed2950eeb4a0ff7a87419dbd86 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:51 +0300 Subject: [PATCH 0041/5214] iio: adc: ad7791: add dev variable Add a local struct device pointer to simplify repeated &spi->dev dereferences throughout the probe function. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7791.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index 041fc25e3209..ab1ab0d88492 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -407,7 +407,8 @@ static void ad7791_reg_disable(void *reg) static int ad7791_probe(struct spi_device *spi) { - const struct ad7791_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device *dev = &spi->dev; + const struct ad7791_platform_data *pdata = dev_get_platdata(dev); struct iio_dev *indio_dev; struct ad7791_state *st; int ret; @@ -417,13 +418,13 @@ static int ad7791_probe(struct spi_device *spi) return -ENXIO; } - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - st->reg = devm_regulator_get(&spi->dev, "refin"); + st->reg = devm_regulator_get(dev, "refin"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); @@ -431,7 +432,7 @@ static int ad7791_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, ad7791_reg_disable, st->reg); + ret = devm_add_action_or_reset(dev, ad7791_reg_disable, st->reg); if (ret) return ret; @@ -447,7 +448,7 @@ static int ad7791_probe(struct spi_device *spi) else indio_dev->info = &ad7791_no_filter_info; - ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); if (ret) return ret; @@ -455,7 +456,7 @@ static int ad7791_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7791_spi_ids[] = { From db935b8cac8bb091e884fe68231835234a266a85 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:52 +0300 Subject: [PATCH 0042/5214] iio: adc: ad7791: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7791.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index ab1ab0d88492..bcdc19e799aa 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -413,10 +413,8 @@ static int ad7791_probe(struct spi_device *spi) struct ad7791_state *st; int ret; - if (!spi->irq) { - dev_err(&spi->dev, "Missing IRQ.\n"); - return -ENXIO; - } + if (!spi->irq) + return dev_err_probe(dev, -ENXIO, "Missing IRQ.\n"); indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) From a88442f40854ace0a078581d4dfda53f32d1a7f3 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:53 +0300 Subject: [PATCH 0043/5214] iio: adc: ad7280a: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7280a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index ba12a3796e2b..f50e2b3121bf 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -990,8 +990,8 @@ static int ad7280_probe(struct spi_device *spi) st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1600ns; break; default: - dev_err(dev, "Firmware provided acquisition time is invalid\n"); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "Firmware provided acquisition time is invalid\n"); } } else { st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns; From ba1ad6b2de799b6c1a869bd2add44bacf0a97344 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:54 +0300 Subject: [PATCH 0044/5214] iio: adc: ad7768-1: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 73fb734d06b2..598936e47fd2 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -1871,10 +1871,8 @@ static int ad7768_probe(struct spi_device *spi) return ret; ret = ad7768_setup(indio_dev); - if (ret < 0) { - dev_err(&spi->dev, "AD7768 setup failed\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "AD7768 setup failed\n"); init_completion(&st->completion); ret = devm_mutex_init(&spi->dev, &st->pga_lock); From 8b6e7388290f71cad96fb5f9279ec7c933f40258 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:55 +0300 Subject: [PATCH 0045/5214] iio: adc: ad9467: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Reviewed-by: Tomas Melin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad9467.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 0bf67437508f..0c377f9a7f25 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -1349,11 +1349,10 @@ static int ad9467_probe(struct spi_device *spi) return ret; id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID); - if (id != st->info->id) { - dev_err(dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", - id, st->info->id); - return -ENODEV; - } + if (id != st->info->id) + return dev_err_probe(dev, -ENODEV, + "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", + id, st->info->id); if (st->info->num_scales > 1) indio_dev->info = &ad9467_info; From 5bcbbcfb7c91b9ab759397d7ca5b317a87284665 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 30 Mar 2026 14:18:56 +0300 Subject: [PATCH 0046/5214] iio: adc: ad4062: use dev_err_probe() Use dev_err_probe() instead of dev_err() in the probe path to ensure proper handling of deferred probing and to simplify error handling. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4062.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c index 222499a45b9b..8e5984055b15 100644 --- a/drivers/iio/adc/ad4062.c +++ b/drivers/iio/adc/ad4062.c @@ -436,10 +436,9 @@ static int ad4062_check_ids(struct ad4062_state *st) return ret; val = be16_to_cpu(st->buf.be16); - if (val != AD4062_I3C_VENDOR) { - dev_err(dev, "Vendor ID x%x does not match expected value\n", val); - return -ENODEV; - } + if (val != AD4062_I3C_VENDOR) + return dev_err_probe(dev, -ENODEV, + "Vendor ID x%x does not match expected value\n", val); return 0; } From 7328d444c19eb31a2b6033969ff4fd170309326c Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Mon, 30 Mar 2026 19:15:27 +0100 Subject: [PATCH 0047/5214] iio: adc: ti-ads8688: use read_avail for available attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the in_voltage_scale_available and in_voltage_offset_available attributes from legacy IIO_DEVICE_ATTR with custom show functions to the IIO framework's read_avail callback. This uses the framework's built-in support for _available attributes, removing the need for manual sysfs formatting. Precompute the available scale values at probe time since they depend on the reference voltage which does not change after initialization. Reviewed-by: Nuno Sá Signed-off-by: Gabriel Rondon Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads8688.c | 70 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index b0bf46cae0b6..ebd2826a7ff6 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -17,7 +16,6 @@ #include #include #include -#include #define ADS8688_CMD_REG(x) (x << 8) #define ADS8688_CMD_REG_NOOP 0x00 @@ -66,6 +64,7 @@ struct ads8688_state { const struct ads8688_chip_info *chip_info; struct spi_device *spi; unsigned int vref_mv; + int scale_avail[3][2]; enum ads8688_range range[8]; union { __be32 d32; @@ -114,37 +113,9 @@ static const struct ads8688_ranges ads8688_range_def[5] = { } }; -static ssize_t ads8688_show_scales(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "0.%09u 0.%09u 0.%09u\n", - ads8688_range_def[0].scale * st->vref_mv, - ads8688_range_def[1].scale * st->vref_mv, - ads8688_range_def[2].scale * st->vref_mv); -} - -static ssize_t ads8688_show_offsets(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset, - ads8688_range_def[3].offset); -} - -static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, - ads8688_show_scales, NULL, 0); -static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO, - ads8688_show_offsets, NULL, 0); - -static struct attribute *ads8688_attributes[] = { - &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage_offset_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ads8688_attribute_group = { - .attrs = ads8688_attributes, +static const int ads8688_offset_avail[] = { + -(1 << (ADS8688_REALBITS - 1)), + 0 }; #define ADS8688_CHAN(index) \ @@ -155,6 +126,9 @@ static const struct attribute_group ads8688_attribute_group = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ | BIT(IIO_CHAN_INFO_SCALE) \ | BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ .scan_index = index, \ .scan_type = { \ .sign = 'u', \ @@ -369,11 +343,34 @@ static int ads8688_write_raw_get_fmt(struct iio_dev *indio_dev, return -EINVAL; } +static int ads8688_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads8688_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(st->scale_avail) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OFFSET: + *vals = ads8688_offset_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ads8688_offset_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_info ads8688_info = { .read_raw = &ads8688_read_raw, + .read_avail = &ads8688_read_avail, .write_raw = &ads8688_write_raw, .write_raw_get_fmt = &ads8688_write_raw_get_fmt, - .attrs = &ads8688_attribute_group, }; static irqreturn_t ads8688_trigger_handler(int irq, void *p) @@ -426,6 +423,11 @@ static int ads8688_probe(struct spi_device *spi) st->vref_mv = ret == -ENODEV ? ADS8688_VREF_MV : ret / 1000; + for (unsigned int i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + st->scale_avail[i][0] = 0; + st->scale_avail[i][1] = ads8688_range_def[i].scale * st->vref_mv; + } + st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data]; spi->mode = SPI_MODE_1; From 2e8a75ac286a51eccd51b68715960563a2ee9c4d Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Fri, 17 Apr 2026 13:02:24 +0000 Subject: [PATCH 0048/5214] iio: magnetometer: ak8975: remove unnecessary braces Remove unnecessary braces at single if statement block. No functional change. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b648b0afa573..44782c26698b 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -541,9 +541,9 @@ static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode) data->def->ctrl_modes[mode]; ret = i2c_smbus_write_byte_data(data->client, data->def->ctrl_regs[CNTL], regval); - if (ret < 0) { + if (ret < 0) return ret; - } + data->cntl_cache = regval; /* After mode change wait at least 100us */ usleep_range(100, 500); From d2ed8a2f630abe69d87eeffb2781df9237d7c1dd Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:17 +0530 Subject: [PATCH 0049/5214] iio: accel: adxl313_core: Use devm-managed mutex initialization Use devm_mutex_init() to tie the mutex lifetime to the device and improve debugging when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index bcc11dabdf22..1fc96b7b0f1f 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -1240,7 +1240,9 @@ int adxl313_core_probe(struct device *dev, data->regmap = regmap; data->chip_info = chip_info; - mutex_init(&data->lock); + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; indio_dev->name = chip_info->name; indio_dev->info = &adxl313_info; From c27837e49fd1fa0eae1b6d3988d2ae5a9d924739 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:18 +0530 Subject: [PATCH 0050/5214] iio: accel: adxl313: Use dev_err_probe() dev_err_probe() makes error code handling simpler and handles deferred probe nicely (avoid spamming logs). Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313_core.c | 6 ++---- drivers/iio/accel/adxl313_i2c.c | 10 ++++------ drivers/iio/accel/adxl313_spi.c | 11 ++++------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 1fc96b7b0f1f..6dc918c4ae17 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -1252,10 +1252,8 @@ int adxl313_core_probe(struct device *dev, indio_dev->available_scan_masks = adxl313_scan_masks; ret = adxl313_setup(dev, data, setup); - if (ret) { - dev_err(dev, "ADXL313 setup failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADXL313 setup failed\n"); int_line = adxl313_get_int_type(dev, &irq); if (int_line == ADXL313_INT_NONE) { diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index b67ff0b4dc54..6736b83f23bd 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -65,6 +65,7 @@ MODULE_DEVICE_TABLE(of, adxl313_of_match); static int adxl313_i2c_probe(struct i2c_client *client) { const struct adxl313_chip_info *chip_data; + struct device *dev = &client->dev; struct regmap *regmap; /* @@ -75,13 +76,10 @@ static int adxl313_i2c_probe(struct i2c_client *client) regmap = devm_regmap_init_i2c(client, &adxl31x_i2c_regmap_config[chip_data->type]); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing i2c regmap\n"); - return adxl313_core_probe(&client->dev, regmap, chip_data, NULL); + return adxl313_core_probe(dev, regmap, chip_data, NULL); } static struct i2c_driver adxl313_i2c_driver = { diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index dedb0885c277..d096ea0632ba 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -70,6 +70,7 @@ static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) static int adxl313_spi_probe(struct spi_device *spi) { const struct adxl313_chip_info *chip_data; + struct device *dev = &spi->dev; struct regmap *regmap; int ret; @@ -83,14 +84,10 @@ static int adxl313_spi_probe(struct spi_device *spi) regmap = devm_regmap_init_spi(spi, &adxl31x_spi_regmap_config[chip_data->type]); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", - PTR_ERR(regmap)); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing spi regmap\n"); - return adxl313_core_probe(&spi->dev, regmap, - chip_data, &adxl313_spi_setup); + return adxl313_core_probe(dev, regmap, chip_data, &adxl313_spi_setup); } static const struct spi_device_id adxl313_spi_id[] = { From 1ed49c5e6b6da868ff226706d54919e1e10cf991 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:19 +0530 Subject: [PATCH 0051/5214] iio: accel: adxl380: Use devm-managed mutex initialization Use devm_mutex_init() to tie the mutex lifetime to the device and improve debugging when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl380.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index e7bb32fbc475..7dca5523091f 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -1967,7 +1967,9 @@ int adxl380_probe(struct device *dev, struct regmap *regmap, st->chip_info = chip_info; st->odr = ADXL380_ODR_DSM; - mutex_init(&st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; indio_dev->channels = adxl380_channels; indio_dev->num_channels = ARRAY_SIZE(adxl380_channels); From 07fd62916c7d2adb65926b989d337c7bfc7b2357 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:20 +0530 Subject: [PATCH 0052/5214] iio: accel: adxl355_core: Use devm-managed mutex initialization Use devm_mutex_init() to tie the mutex lifetime to the device and improve debugging when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 8f90c58f4100..a310f8e37e3d 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -802,7 +802,9 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, data->dev = dev; data->op_mode = ADXL355_STANDBY; data->chip_info = chip_info; - mutex_init(&data->lock); + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; indio_dev->name = chip_info->name; indio_dev->info = &adxl355_info; From 70cc2c65c23ba212c6de61a727131ebf94a66610 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:21 +0530 Subject: [PATCH 0053/5214] iio: accel: adxl355: Use dev_err_probe() dev_err_probe() makes error code handling simpler and handles deferred probe nicely (avoid spamming logs). Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 24 ++++++++---------------- drivers/iio/accel/adxl355_i2c.c | 11 ++++------- drivers/iio/accel/adxl355_spi.c | 11 ++++------- 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index a310f8e37e3d..03e5744d9667 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -336,10 +336,8 @@ static int adxl355_setup(struct adxl355_data *data) return ret; do { - if (--retries == 0) { - dev_err(data->dev, "Shadow registers mismatch\n"); - return -EIO; - } + if (--retries == 0) + return dev_err_probe(data->dev, -EIO, "Shadow registers mismatch\n"); /* * Perform a software reset to make sure the device is in a consistent @@ -775,10 +773,8 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) irq); ret = devm_iio_trigger_register(data->dev, data->dready_trig); - if (ret) { - dev_err(data->dev, "iio trigger register failed\n"); - return ret; - } + if (ret) + return dev_err_probe(data->dev, ret, "iio trigger register failed\n"); indio_dev->trig = iio_trigger_get(data->dready_trig); @@ -814,18 +810,14 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, indio_dev->available_scan_masks = adxl355_avail_scan_masks; ret = adxl355_setup(data); - if (ret) { - dev_err(dev, "ADXL355 setup failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADXL355 setup failed\n"); ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &adxl355_trigger_handler, NULL); - if (ret) { - dev_err(dev, "iio triggered buffer setup failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n"); irq = fwnode_irq_get_byname(dev_fwnode(dev), "DRDY"); if (irq > 0) { diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c index 1a512c7b792b..19490a6e1f35 100644 --- a/drivers/iio/accel/adxl355_i2c.c +++ b/drivers/iio/accel/adxl355_i2c.c @@ -23,6 +23,7 @@ static const struct regmap_config adxl355_i2c_regmap_config = { static int adxl355_i2c_probe(struct i2c_client *client) { struct regmap *regmap; + struct device *dev = &client->dev; const struct adxl355_chip_info *chip_data; chip_data = i2c_get_match_data(client); @@ -30,14 +31,10 @@ static int adxl355_i2c_probe(struct i2c_client *client) return -ENODEV; regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", - PTR_ERR(regmap)); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing i2c regmap\n"); - return PTR_ERR(regmap); - } - - return adxl355_core_probe(&client->dev, regmap, chip_data); + return adxl355_core_probe(dev, regmap, chip_data); } static const struct i2c_device_id adxl355_i2c_id[] = { diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c index 869e3e57d6f7..347ed62b6582 100644 --- a/drivers/iio/accel/adxl355_spi.c +++ b/drivers/iio/accel/adxl355_spi.c @@ -26,6 +26,7 @@ static const struct regmap_config adxl355_spi_regmap_config = { static int adxl355_spi_probe(struct spi_device *spi) { const struct adxl355_chip_info *chip_data; + struct device *dev = &spi->dev; struct regmap *regmap; chip_data = spi_get_device_match_data(spi); @@ -33,14 +34,10 @@ static int adxl355_spi_probe(struct spi_device *spi) return -EINVAL; regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", - PTR_ERR(regmap)); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing spi regmap\n"); - return PTR_ERR(regmap); - } - - return adxl355_core_probe(&spi->dev, regmap, chip_data); + return adxl355_core_probe(dev, regmap, chip_data); } static const struct spi_device_id adxl355_spi_id[] = { From f710a0fa462ce5fc356ab4a77787b49fc1f47f7b Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:22 +0530 Subject: [PATCH 0054/5214] iio: accel: adxl367: Use devm-managed mutex initialization Use devm_mutex_init() to tie the mutex lifetime to the device and improve debugging when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl367.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 0c04b2bb7efb..63a0b182824f 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -1445,7 +1445,9 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, st->context = context; st->ops = ops; - mutex_init(&st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; indio_dev->channels = adxl367_channels; indio_dev->num_channels = ARRAY_SIZE(adxl367_channels); From 24ab1d9a2fc4c1e4f2546bebcee2b420295120a0 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:23 +0530 Subject: [PATCH 0055/5214] iio: accel: adxl372: Use devm-managed mutex initialization Use devm_mutex_init() to tie the mutex lifetime to the device and improve debugging when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 545a21e5a308..1a6ba94f54f4 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1299,7 +1299,9 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->irq = irq; st->chip_info = chip_info; - mutex_init(&st->threshold_m); + ret = devm_mutex_init(dev, &st->threshold_m); + if (ret < 0) + return ret; indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); From d47d6bdc81cfe56a1e7af40528ac81162a547e1b Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 17 Apr 2026 18:19:24 +0530 Subject: [PATCH 0056/5214] iio: accel: adxl372: Use dev_err_probe() dev_err_probe() makes error code handling simpler and handles deferred probe nicely (avoid spamming logs). Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 1a6ba94f54f4..e375d068a3f5 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1316,10 +1316,8 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, } ret = adxl372_setup(st); - if (ret < 0) { - dev_err(dev, "ADXL372 setup failed\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "ADXL372 setup failed\n"); if (chip_info->fifo_supported) { ret = adxl372_buffer_setup(indio_dev); From 0ce10ae5eb91235e69901b55b9a6d8d90e9cbf6a Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Fri, 17 Apr 2026 10:16:22 +0000 Subject: [PATCH 0057/5214] iio: frequency: ad9832: remove kernel.h proxy header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove kernel.h proxy header and add replacement headers (array_size.h, dev_printk.h, kstrtox.h, mod_devicetable.h, mutex, types.h, asm/byteorder.h) to maintain atomicity. Moved asm/div64.h header below generic headers. Additionally, add bitops.h for BIT_ULL() macro. Audited using the include-what-you-use tool. Signed-off-by: Joshua Crofts Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index b87ea1781b27..c0b7852f1c84 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -5,21 +5,25 @@ * Copyright 2011 Analog Devices Inc. */ -#include - +#include #include -#include +#include #include -#include +#include #include -#include +#include +#include #include +#include #include -#include #include #include +#include #include +#include +#include + #include #include From 13af23fb52a836e0636cdd5b8814d5c96af2aa39 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 16 Apr 2026 09:32:40 +0000 Subject: [PATCH 0058/5214] iio: frequency: ad9834: clean up includes Cleanup include headers by removing proxy kernel.h header and unnecessary list.h, interrupt.h, workqueue.h and slab.h headers. Added additional headers that were previously included from kernel.h. Verified using the include-what-you-use tool. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index bdb2580e29bf..330db78fe766 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -5,18 +5,21 @@ * Copyright 2010-2011 Analog Devices Inc. */ +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include + +#include #include #include From dcc80f2fdff721ced4ea1ef7a3ea43f3fbe0b27a Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 15 Apr 2026 10:37:43 +0530 Subject: [PATCH 0059/5214] iio: ssp_sensors: cleanup codestyle warning Reported by checkpatch: FILE: drivers/iio/common/ssp_sensors/ssp_spi.c WARNING: Prefer __packed over __attribute__((__packed__)) +} __attribute__((__packed__)); Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c index 6c81c0385fb5..8c1e15d61db7 100644 --- a/drivers/iio/common/ssp_sensors/ssp_spi.c +++ b/drivers/iio/common/ssp_sensors/ssp_spi.c @@ -29,7 +29,7 @@ struct ssp_msg_header { __le16 length; __le16 options; __le32 data; -} __attribute__((__packed__)); +} __packed; struct ssp_msg { u16 length; From a9ecd9a121752f2d7bb69da264bda65b6b6e6c6e Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Wed, 15 Apr 2026 10:37:44 +0530 Subject: [PATCH 0060/5214] iio: ssp_sensors: cleanup codestyle check Reported by checkpatch: FILE: drivers/iio/common/ssp_sensors/ssp_spi.c CHECK: Macro argument '...' may be better as '(...)' to avoid precedence issues Signed-off-by: Sanjay Chitroda Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c index 8c1e15d61db7..08ed92859be0 100644 --- a/drivers/iio/common/ssp_sensors/ssp_spi.c +++ b/drivers/iio/common/ssp_sensors/ssp_spi.c @@ -6,7 +6,7 @@ #include "ssp.h" #define SSP_DEV (&data->spi->dev) -#define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW)) +#define SSP_GET_MESSAGE_TYPE(data) ((data) & (3 << SSP_RW)) /* * SSP -> AP Instruction @@ -119,9 +119,9 @@ static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset, } #define SSP_GET_BUFFER_AT_INDEX(m, index) \ - (m->buffer[SSP_HEADER_SIZE_ALIGNED + index]) + ((m)->buffer[SSP_HEADER_SIZE_ALIGNED + (index)]) #define SSP_SET_BUFFER_AT_INDEX(m, index, val) \ - (m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val) + ((m)->buffer[SSP_HEADER_SIZE_ALIGNED + (index)] = val) static void ssp_clean_msg(struct ssp_msg *m) { From f4d618c6185101efa072fd5845000fee2074b8b8 Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Apr 2026 22:33:06 +0530 Subject: [PATCH 0061/5214] iio: dac: mcp4821: fix spelling mistake in enum name Fix a typo in the enum name mcp4821_supported_drvice_ids by renaming it to mcp4821_supported_device_ids. This improves code readability and consistency. Signed-off-by: Nikhil Gautam Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4821.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 748bdca9a964..29187f2a9d3c 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -31,7 +31,7 @@ /* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */ #define MCP4821_2X_GAIN_VREF_MV 4096 -enum mcp4821_supported_drvice_ids { +enum mcp4821_supported_device_ids { ID_MCP4801, ID_MCP4802, ID_MCP4811, From 090839ecff2b30fa87b73f5001cdb150673a4d4c Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Apr 2026 22:33:07 +0530 Subject: [PATCH 0062/5214] iio: dac: mcp4821: move state initialization outside switch Move the iio_priv() call outside the switch statement in mcp4821_read_raw() to avoid repeating it in multiple cases. No functional change. Signed-off-by: Nikhil Gautam Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4821.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 29187f2a9d3c..102ae3718514 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -115,11 +115,10 @@ static int mcp4821_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct mcp4821_state *state; + struct mcp4821_state *state = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_RAW: - state = iio_priv(indio_dev); *val = state->dac_value[chan->channel]; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 416e983c913742cfae1720982720fe06fc7d3bba Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Tue, 14 Apr 2026 22:33:08 +0530 Subject: [PATCH 0063/5214] iio: dac: mcp4821: add configurable gain support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for configuring the DAC gain using the GA bit The MCP4821 supports two gain settings: - 1x gain → 2.048V full-scale - 2x gain → 4.096V full-scale Scale write support is added in the IIO interface. Only scale values advertised via the scale_available attribute are accepted, ensuring consistency between the configured gain and exposed scale values. Signed-off-by: Nikhil Gautam Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4821.c | 132 +++++++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 22 deletions(-) diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index 102ae3718514..18b5934fb8a2 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -12,13 +12,13 @@ * MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf * * TODO: - * - Configurable gain * - Regulator control */ #include #include #include +#include #include #include @@ -26,10 +26,30 @@ #include #define MCP4821_ACTIVE_MODE BIT(12) +#define MCP4821_GAIN_ENABLE BIT(13) #define MCP4802_SECOND_CHAN BIT(15) -/* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */ -#define MCP4821_2X_GAIN_VREF_MV 4096 +/* DAC uses an internal Voltage reference of 2.048V */ +#define MCP4821_VREF_MV 2048 + +/* + * MCP48xx DAC output: + * + * Vout = (Vref * D / 2^N) * G + * + * where: + * - Vref = 2.048V (internal reference) + * - N = DAC resolution (12 bits for MCP4821) + * - G = gain selection: + * 1x when GA bit = 1 + * 2x when GA bit = 0 (default) + * + * Therefore full-scale voltage is: + * - 1x gain: 2.048V + * - 2x gain: 4.096V + * + * Scale = Vfull-scale / 2^N + */ enum mcp4821_supported_device_ids { ID_MCP4801, @@ -43,6 +63,8 @@ enum mcp4821_supported_device_ids { struct mcp4821_state { struct spi_device *spi; u16 dac_value[2]; + int gain; + int scale_avail[4]; }; struct mcp4821_chip_info { @@ -57,6 +79,7 @@ struct mcp4821_chip_info { .channel = (channel_id), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ .scan_type = { \ .realbits = (resolution), \ .shift = 12 - (resolution), \ @@ -121,8 +144,9 @@ static int mcp4821_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: *val = state->dac_value[chan->channel]; return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: - *val = MCP4821_2X_GAIN_VREF_MV; + *val = MCP4821_VREF_MV * state->gain; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; default: @@ -130,6 +154,17 @@ static int mcp4821_read_raw(struct iio_dev *indio_dev, } } +static void mcp4821_calc_scale(int vref_mv, int resolution, + int *val, int *val2) +{ + s64 tmp; + int micro; + + tmp = (s64)vref_mv * MICRO >> resolution; + *val = div_s64_rem(tmp, MICRO, µ); + *val2 = micro; +} + static int mcp4821_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -138,35 +173,85 @@ static int mcp4821_write_raw(struct iio_dev *indio_dev, u16 write_val; __be16 write_buffer; int ret; + int v, v2; - if (val2 != 0) + switch (mask) { + case IIO_CHAN_INFO_RAW: + + if (val2 != 0) + return -EINVAL; + + if (val < 0 || val >= BIT(chan->scan_type.realbits)) + return -EINVAL; + + write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift; + if (chan->channel) + write_val |= MCP4802_SECOND_CHAN; + + /* GA bit = 1 -> 1x gain */ + if (state->gain == 1) + write_val |= MCP4821_GAIN_ENABLE; + + write_buffer = cpu_to_be16(write_val); + ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer)); + if (ret) { + dev_err(&state->spi->dev, "Failed to write to device: %d", ret); + return ret; + } + + state->dac_value[chan->channel] = val; + return 0; + + case IIO_CHAN_INFO_SCALE: + mcp4821_calc_scale(MCP4821_VREF_MV, chan->scan_type.realbits, &v, &v2); + if (val == v && val2 == v2) { + state->gain = 1; + return 0; + } + + mcp4821_calc_scale(MCP4821_VREF_MV * 2, + chan->scan_type.realbits, &v, &v2); + if (val == v && val2 == v2) { + state->gain = 2; + return 0; + } return -EINVAL; - - if (val < 0 || val >= BIT(chan->scan_type.realbits)) + default: return -EINVAL; - - if (mask != IIO_CHAN_INFO_RAW) - return -EINVAL; - - write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift; - if (chan->channel) - write_val |= MCP4802_SECOND_CHAN; - - write_buffer = cpu_to_be16(write_val); - ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer)); - if (ret) { - dev_err(&state->spi->dev, "Failed to write to device: %d", ret); - return ret; } +} - state->dac_value[chan->channel] = val; +static inline void mcp4821_init_avail_gain(struct mcp4821_state *state, + int resolution) +{ + state->scale_avail[0] = MCP4821_VREF_MV; + state->scale_avail[1] = resolution; + state->scale_avail[2] = MCP4821_VREF_MV * 2; + state->scale_avail[3] = resolution; +} - return 0; +static int mcp4821_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct mcp4821_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = state->scale_avail; + *type = IIO_VAL_FRACTIONAL_LOG2; + *length = ARRAY_SIZE(state->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } } static const struct iio_info mcp4821_info = { .read_raw = &mcp4821_read_raw, .write_raw = &mcp4821_write_raw, + .read_avail = &mcp4821_read_avail, }; static int mcp4821_probe(struct spi_device *spi) @@ -182,12 +267,15 @@ static int mcp4821_probe(struct spi_device *spi) state = iio_priv(indio_dev); state->spi = spi; + /* default gain is 2x */ + state->gain = 2; info = spi_get_device_match_data(spi); indio_dev->name = info->name; indio_dev->info = &mcp4821_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = info->channels; indio_dev->num_channels = info->num_channels; + mcp4821_init_avail_gain(state, info->channels[0].scan_type.realbits); return devm_iio_device_register(&spi->dev, indio_dev); } From 4617091a7616aeaa174506045733b20a5398c2a9 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:17 +0300 Subject: [PATCH 0064/5214] iio: light: vcnl4000: validate device by prod ID instead of table ID Add a new field for vcnl4000_chip_spec and check if we have the right device by that instead of the index from enum table. This leaves the enum table being used only for picking the right vcnl4000_chip_spec, allowing us to drop it later on. Reviewed-by: Andy Shevchenko Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 9650dbc41f2b..72d68d54864e 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -234,6 +234,7 @@ struct vcnl4000_chip_spec { const int(*als_it_times)[][2]; const int num_als_it_times; const unsigned int ulux_step; + const int prod_id; }; static const struct i2c_device_id vcnl4000_id[] = { @@ -265,12 +266,12 @@ static int vcnl4000_init(struct vcnl4000_data *data) prod_id = ret >> 4; switch (prod_id) { case VCNL4000_PROD_ID: - if (data->id != VCNL4000) + if (data->chip_spec->prod_id != VCNL4000_PROD_ID) dev_warn(&data->client->dev, "wrong device id, use vcnl4000"); break; case VCNL4010_PROD_ID: - if (data->id != VCNL4010) + if (data->chip_spec->prod_id != VCNL4010_PROD_ID) dev_warn(&data->client->dev, "wrong device id, use vcnl4010/4020"); break; @@ -1901,6 +1902,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .int_reg = VCNL4040_INT_FLAGS, .ps_it_times = &vcnl4040_ps_it_times, .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + .prod_id = VCNL4040_PROD_ID, }, [VCNL4000] = { .prod = "VCNL4000", @@ -1911,6 +1913,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4000_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), .info = &vcnl4000_info, + .prod_id = VCNL4000_PROD_ID, }, [VCNL4010] = { .prod = "VCNL4010/4020", @@ -1924,6 +1927,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .irq_thread = vcnl4010_irq_thread, .trig_buffer_func = vcnl4010_trigger_handler, .buffer_setup_ops = &vcnl4010_buffer_ops, + .prod_id = VCNL4010_PROD_ID, }, [VCNL4040] = { .prod = "VCNL4040", @@ -1941,6 +1945,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .als_it_times = &vcnl4040_als_it_times, .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), .ulux_step = 100000, + .prod_id = VCNL4040_PROD_ID, }, [VCNL4200] = { .prod = "VCNL4200", @@ -1958,6 +1963,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .als_it_times = &vcnl4200_als_it_times, .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), .ulux_step = 24000, + .prod_id = VCNL4200_PROD_ID, }, }; From b2362b824f977a0a08f8d16c36510cabd6f58ce8 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:18 +0300 Subject: [PATCH 0065/5214] iio: light: vcnl4000: drop enum id table in favor of chip structs Instead of creating an enum table with chip IDs, store pointers to structs directly. This drops the association between chip structs and enum IDs and allows for easier addition or removal of new devices. Reviewed-by: Andy Shevchenko Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 205 +++++++++++++++++------------------ 1 file changed, 98 insertions(+), 107 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 72d68d54864e..cbe0a9e056ec 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -185,14 +185,6 @@ static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ -enum vcnl4000_device_ids { - CM36672P, - VCNL4000, - VCNL4010, - VCNL4040, - VCNL4200, -}; - struct vcnl4200_channel { u8 reg; ktime_t last_measurement; @@ -202,7 +194,6 @@ struct vcnl4200_channel { struct vcnl4000_data { struct i2c_client *client; - enum vcnl4000_device_ids id; int rev; int al_scale; int ps_scale; @@ -237,18 +228,6 @@ struct vcnl4000_chip_spec { const int prod_id; }; -static const struct i2c_device_id vcnl4000_id[] = { - { "cm36672p", CM36672P }, - { "cm36686", VCNL4040 }, - { "vcnl4000", VCNL4000 }, - { "vcnl4010", VCNL4010 }, - { "vcnl4020", VCNL4010 }, - { "vcnl4040", VCNL4040 }, - { "vcnl4200", VCNL4200 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, vcnl4000_id); - static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) { /* no suspend op */ @@ -1889,82 +1868,84 @@ static const struct iio_info vcnl4040_info = { .read_avail = vcnl4040_read_avail, }; -static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { - [CM36672P] = { - .prod = "CM36672P", - .init = vcnl4200_init, - .measure_proximity = vcnl4200_measure_proximity, - .set_power_state = vcnl4200_set_power_state, - .channels = cm36672p_channels, - .num_channels = ARRAY_SIZE(cm36672p_channels), - .info = &vcnl4040_info, - .irq_thread = vcnl4040_irq_thread, - .int_reg = VCNL4040_INT_FLAGS, - .ps_it_times = &vcnl4040_ps_it_times, - .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), - .prod_id = VCNL4040_PROD_ID, - }, - [VCNL4000] = { - .prod = "VCNL4000", - .init = vcnl4000_init, - .measure_light = vcnl4000_measure_light, - .measure_proximity = vcnl4000_measure_proximity, - .set_power_state = vcnl4000_set_power_state, - .channels = vcnl4000_channels, - .num_channels = ARRAY_SIZE(vcnl4000_channels), - .info = &vcnl4000_info, - .prod_id = VCNL4000_PROD_ID, - }, - [VCNL4010] = { - .prod = "VCNL4010/4020", - .init = vcnl4000_init, - .measure_light = vcnl4000_measure_light, - .measure_proximity = vcnl4000_measure_proximity, - .set_power_state = vcnl4000_set_power_state, - .channels = vcnl4010_channels, - .num_channels = ARRAY_SIZE(vcnl4010_channels), - .info = &vcnl4010_info, - .irq_thread = vcnl4010_irq_thread, - .trig_buffer_func = vcnl4010_trigger_handler, - .buffer_setup_ops = &vcnl4010_buffer_ops, - .prod_id = VCNL4010_PROD_ID, - }, - [VCNL4040] = { - .prod = "VCNL4040", - .init = vcnl4200_init, - .measure_light = vcnl4200_measure_light, - .measure_proximity = vcnl4200_measure_proximity, - .set_power_state = vcnl4200_set_power_state, - .channels = vcnl4040_channels, - .num_channels = ARRAY_SIZE(vcnl4040_channels), - .info = &vcnl4040_info, - .irq_thread = vcnl4040_irq_thread, - .int_reg = VCNL4040_INT_FLAGS, - .ps_it_times = &vcnl4040_ps_it_times, - .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), - .als_it_times = &vcnl4040_als_it_times, - .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), - .ulux_step = 100000, - .prod_id = VCNL4040_PROD_ID, - }, - [VCNL4200] = { - .prod = "VCNL4200", - .init = vcnl4200_init, - .measure_light = vcnl4200_measure_light, - .measure_proximity = vcnl4200_measure_proximity, - .set_power_state = vcnl4200_set_power_state, - .channels = vcnl4040_channels, - .num_channels = ARRAY_SIZE(vcnl4000_channels), - .info = &vcnl4040_info, - .irq_thread = vcnl4040_irq_thread, - .int_reg = VCNL4200_INT_FLAGS, - .ps_it_times = &vcnl4200_ps_it_times, - .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), - .als_it_times = &vcnl4200_als_it_times, - .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), - .ulux_step = 24000, - .prod_id = VCNL4200_PROD_ID, - }, +static const struct vcnl4000_chip_spec cm36672p_spec = { + .prod = "CM36672P", + .init = vcnl4200_init, + .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, + .channels = cm36672p_channels, + .num_channels = ARRAY_SIZE(cm36672p_channels), + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4040_INT_FLAGS, + .ps_it_times = &vcnl4040_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + .prod_id = VCNL4040_PROD_ID, +}; + +static const struct vcnl4000_chip_spec vcnl4000_spec = { + .prod = "VCNL4000", + .init = vcnl4000_init, + .measure_light = vcnl4000_measure_light, + .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, + .channels = vcnl4000_channels, + .num_channels = ARRAY_SIZE(vcnl4000_channels), + .info = &vcnl4000_info, + .prod_id = VCNL4000_PROD_ID, +}; + +static const struct vcnl4000_chip_spec vcnl4010_spec = { + .prod = "VCNL4010/4020", + .init = vcnl4000_init, + .measure_light = vcnl4000_measure_light, + .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, + .channels = vcnl4010_channels, + .num_channels = ARRAY_SIZE(vcnl4010_channels), + .info = &vcnl4010_info, + .irq_thread = vcnl4010_irq_thread, + .trig_buffer_func = vcnl4010_trigger_handler, + .buffer_setup_ops = &vcnl4010_buffer_ops, + .prod_id = VCNL4010_PROD_ID, +}; + +static const struct vcnl4000_chip_spec vcnl4040_spec = { + .prod = "VCNL4040", + .init = vcnl4200_init, + .measure_light = vcnl4200_measure_light, + .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, + .channels = vcnl4040_channels, + .num_channels = ARRAY_SIZE(vcnl4040_channels), + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4040_INT_FLAGS, + .ps_it_times = &vcnl4040_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + .als_it_times = &vcnl4040_als_it_times, + .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), + .ulux_step = 100000, + .prod_id = VCNL4040_PROD_ID, +}; + +static const struct vcnl4000_chip_spec vcnl4200_spec = { + .prod = "VCNL4200", + .init = vcnl4200_init, + .measure_light = vcnl4200_measure_light, + .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, + .channels = vcnl4040_channels, + .num_channels = ARRAY_SIZE(vcnl4000_channels), + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4200_INT_FLAGS, + .ps_it_times = &vcnl4200_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), + .als_it_times = &vcnl4200_als_it_times, + .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), + .ulux_step = 24000, + .prod_id = VCNL4200_PROD_ID, }; static const struct iio_trigger_ops vcnl4010_trigger_ops = { @@ -1991,7 +1972,6 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) static int vcnl4000_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); const char * const regulator_names[] = { "vdd", "vio", "vled" }; struct device *dev = &client->dev; struct vcnl4000_data *data; @@ -2005,8 +1985,7 @@ static int vcnl4000_probe(struct i2c_client *client) data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - data->id = id->driver_data; - data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; + data->chip_spec = i2c_get_match_data(client); ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), regulator_names); @@ -2081,32 +2060,32 @@ static int vcnl4000_probe(struct i2c_client *client) static const struct of_device_id vcnl_4000_of_match[] = { { .compatible = "capella,cm36672p", - .data = (void *)CM36672P, + .data = &cm36672p_spec, }, /* Capella CM36686 is fully compatible with Vishay VCNL4040 */ { .compatible = "capella,cm36686", - .data = (void *)VCNL4040, + .data = &vcnl4040_spec, }, { .compatible = "vishay,vcnl4000", - .data = (void *)VCNL4000, + .data = &vcnl4000_spec, }, { .compatible = "vishay,vcnl4010", - .data = (void *)VCNL4010, + .data = &vcnl4010_spec, }, { .compatible = "vishay,vcnl4020", - .data = (void *)VCNL4010, + .data = &vcnl4010_spec, }, { .compatible = "vishay,vcnl4040", - .data = (void *)VCNL4040, + .data = &vcnl4040_spec, }, { .compatible = "vishay,vcnl4200", - .data = (void *)VCNL4200, + .data = &vcnl4200_spec, }, { } }; @@ -2148,6 +2127,18 @@ static int vcnl4000_runtime_resume(struct device *dev) static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, vcnl4000_runtime_resume, NULL); +static const struct i2c_device_id vcnl4000_id[] = { + { "cm36672p", (kernel_ulong_t)&cm36672p_spec }, + { "cm36686", (kernel_ulong_t)&vcnl4040_spec }, + { "vcnl4000", (kernel_ulong_t)&vcnl4000_spec }, + { "vcnl4010", (kernel_ulong_t)&vcnl4010_spec }, + { "vcnl4020", (kernel_ulong_t)&vcnl4010_spec }, + { "vcnl4040", (kernel_ulong_t)&vcnl4040_spec }, + { "vcnl4200", (kernel_ulong_t)&vcnl4200_spec }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vcnl4000_id); + static struct i2c_driver vcnl4000_driver = { .driver = { .name = VCNL4000_DRV_NAME, From 8078daf88638ef0a280447ebba7ed8a496711e2e Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:19 +0300 Subject: [PATCH 0066/5214] iio: light: vcnl4000: move device tree entries into one line Make device tree entries one line each to save space and LOC. Signed-off-by: Erikas Bitovtas Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index cbe0a9e056ec..52f60b372d2f 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2058,35 +2058,14 @@ static int vcnl4000_probe(struct i2c_client *client) } static const struct of_device_id vcnl_4000_of_match[] = { - { - .compatible = "capella,cm36672p", - .data = &cm36672p_spec, - }, + { .compatible = "capella,cm36672p", .data = &cm36672p_spec }, /* Capella CM36686 is fully compatible with Vishay VCNL4040 */ - { - .compatible = "capella,cm36686", - .data = &vcnl4040_spec, - }, - { - .compatible = "vishay,vcnl4000", - .data = &vcnl4000_spec, - }, - { - .compatible = "vishay,vcnl4010", - .data = &vcnl4010_spec, - }, - { - .compatible = "vishay,vcnl4020", - .data = &vcnl4010_spec, - }, - { - .compatible = "vishay,vcnl4040", - .data = &vcnl4040_spec, - }, - { - .compatible = "vishay,vcnl4200", - .data = &vcnl4200_spec, - }, + { .compatible = "capella,cm36686", .data = &vcnl4040_spec }, + { .compatible = "vishay,vcnl4000", .data = &vcnl4000_spec }, + { .compatible = "vishay,vcnl4010", .data = &vcnl4010_spec }, + { .compatible = "vishay,vcnl4020", .data = &vcnl4010_spec }, + { .compatible = "vishay,vcnl4040", .data = &vcnl4040_spec }, + { .compatible = "vishay,vcnl4200", .data = &vcnl4200_spec }, { } }; MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); From 857cb79df06b2890c2338295ab2f35e943f30d7b Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:20 +0300 Subject: [PATCH 0067/5214] iio: light: vcnl4000: move power state function into device-managed action Move power state setting into a device-managed action to get rid of fail_poweroff goto label and remove it from vcnl4000_remove() function. Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 52f60b372d2f..1e636e6f51da 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1970,6 +1970,18 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) return devm_iio_trigger_register(&client->dev, trigger); } +static void vcnl4000_cleanup(void *data) +{ + struct iio_dev *indio_dev = data; + struct vcnl4000_data *chip = iio_priv(indio_dev); + struct device *dev = &chip->client->dev; + int ret; + + ret = chip->chip_spec->set_power_state(chip, false); + if (ret) + dev_warn(dev, "Failed to power down (%pe)", ERR_PTR(ret)); +} + static int vcnl4000_probe(struct i2c_client *client) { const char * const regulator_names[] = { "vdd", "vio", "vled" }; @@ -2004,6 +2016,10 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(dev, vcnl4000_cleanup, indio_dev); + if (ret) + return ret; + dev_dbg(dev, "%s Ambient light/proximity sensor, Rev: %02x\n", data->chip_spec->prod, data->rev); @@ -2041,19 +2057,20 @@ static int vcnl4000_probe(struct i2c_client *client) ret = pm_runtime_set_active(dev); if (ret < 0) - goto fail_poweroff; + return ret; ret = iio_device_register(indio_dev); if (ret < 0) - goto fail_poweroff; + goto fail_register; pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, VCNL4000_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(dev); return 0; -fail_poweroff: - data->chip_spec->set_power_state(data, false); + +fail_register: + pm_runtime_set_suspended(dev); return ret; } @@ -2073,18 +2090,11 @@ MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); static void vcnl4000_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct vcnl4000_data *data = iio_priv(indio_dev); - int ret; pm_runtime_dont_use_autosuspend(&client->dev); pm_runtime_disable(&client->dev); iio_device_unregister(indio_dev); pm_runtime_set_suspended(&client->dev); - - ret = data->chip_spec->set_power_state(data, false); - if (ret) - dev_warn(&client->dev, "Failed to power down (%pe)\n", - ERR_PTR(ret)); } static int vcnl4000_runtime_suspend(struct device *dev) From de1839edfe36642328ed7b3fd49b948a4aca03c8 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:21 +0300 Subject: [PATCH 0068/5214] iio: light: vcnl4000: make pm_runtime_enable() device-managed Replace pm_runtime_set_active() and pm_runtime_enable() with their device-managed counterpart to remove them from vcnl4000_remove(). Reviewed-by: Andy Shevchenko Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 1e636e6f51da..3d0287799ba5 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2055,23 +2055,18 @@ static int vcnl4000_probe(struct i2c_client *client) return ret; } - ret = pm_runtime_set_active(dev); - if (ret < 0) + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) return ret; ret = iio_device_register(indio_dev); if (ret < 0) - goto fail_register; + return ret; - pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, VCNL4000_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(dev); return 0; - -fail_register: - pm_runtime_set_suspended(dev); - return ret; } static const struct of_device_id vcnl_4000_of_match[] = { @@ -2091,10 +2086,7 @@ static void vcnl4000_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - pm_runtime_dont_use_autosuspend(&client->dev); - pm_runtime_disable(&client->dev); iio_device_unregister(indio_dev); - pm_runtime_set_suspended(&client->dev); } static int vcnl4000_runtime_suspend(struct device *dev) From de1cbc4b213d034d76164ebbfd4172ffbc5f618d Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 14 Apr 2026 15:37:22 +0300 Subject: [PATCH 0069/5214] iio: light: vcnl4000: register an IIO device with a device-managed function Use a device-managed counterpart of iio_device_register() and remove the redundant iio_device_unregister() call in driver remove function, allowing us to remove vcnl4000_remove() function altogether. Reviewed-by: Andy Shevchenko Signed-off-by: Erikas Bitovtas Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 3d0287799ba5..c08927e34b4e 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2059,8 +2059,8 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) + ret = devm_iio_device_register(dev, indio_dev); + if (ret) return ret; pm_runtime_set_autosuspend_delay(dev, VCNL4000_SLEEP_DELAY_MS); @@ -2082,13 +2082,6 @@ static const struct of_device_id vcnl_4000_of_match[] = { }; MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); -static void vcnl4000_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); -} - static int vcnl4000_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); @@ -2128,7 +2121,6 @@ static struct i2c_driver vcnl4000_driver = { }, .probe = vcnl4000_probe, .id_table = vcnl4000_id, - .remove = vcnl4000_remove, }; module_i2c_driver(vcnl4000_driver); From d4c8667bc90ea2442ca2c814c19900242cceb4e7 Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Sun, 12 Apr 2026 15:07:35 +0530 Subject: [PATCH 0070/5214] iio: adc: ad799x: sort headers alphabetically Reorder header includes to maintain proper alphabetical ordering. No functional changes. Reviewed-by: David Lechner Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 108bb22162ef..f37f1fda2dc4 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -18,23 +18,23 @@ * ad7998 and similar chips. */ -#include +#include #include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include #include #include From fa2d96d2896093a01115491c8e672d5694b4e23a Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Sun, 12 Apr 2026 15:07:36 +0530 Subject: [PATCH 0071/5214] iio: adc: ad799x: use local device pointer in probe Introduce a local device pointer 'dev' in ad799x_probe() and use it throughout the function instead of accessing &client->dev repeatedly. Suggested-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index f37f1fda2dc4..bf0575585a59 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -783,6 +783,7 @@ static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { static int ad799x_probe(struct i2c_client *client) { + struct device *dev = &client->dev; const struct i2c_device_id *id = i2c_client_get_device_id(client); int ret; int extra_config = 0; @@ -791,7 +792,7 @@ static int ad799x_probe(struct i2c_client *client) const struct ad799x_chip_info *chip_info = &ad799x_chip_info_tbl[id->driver_data]; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; @@ -807,7 +808,7 @@ static int ad799x_probe(struct i2c_client *client) /* TODO: Add pdata options for filtering and bit delay */ - st->reg = devm_regulator_get(&client->dev, "vcc"); + st->reg = devm_regulator_get(dev, "vcc"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); ret = regulator_enable(st->reg); @@ -816,17 +817,17 @@ static int ad799x_probe(struct i2c_client *client) /* check if an external reference is supplied */ if (chip_info->has_vref) { - st->vref = devm_regulator_get_optional(&client->dev, "vref"); + st->vref = devm_regulator_get_optional(dev, "vref"); ret = PTR_ERR_OR_ZERO(st->vref); if (ret) { if (ret != -ENODEV) goto error_disable_reg; st->vref = NULL; - dev_info(&client->dev, "Using VCC reference voltage\n"); + dev_info(dev, "Using VCC reference voltage\n"); } if (st->vref) { - dev_info(&client->dev, "Using external reference voltage\n"); + dev_info(dev, "Using external reference voltage\n"); extra_config |= AD7991_REF_SEL; ret = regulator_enable(st->vref); if (ret) @@ -853,7 +854,7 @@ static int ad799x_probe(struct i2c_client *client) goto error_disable_vref; if (client->irq > 0) { - ret = devm_request_threaded_irq(&client->dev, + ret = devm_request_threaded_irq(dev, client->irq, NULL, ad799x_event_handler, From 21450969a7e86ee46dd6c546ae639057aa403b53 Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Sun, 12 Apr 2026 15:07:37 +0530 Subject: [PATCH 0072/5214] iio: adc: ad799x: use a static buffer for scan data Currently, rx_buf is dynamically allocated using kmalloc() every time ad799x_update_scan_mode() is called. This can lead to memory leaks if the scan mask is updated multiple times. Drop the dynamic allocation and replace it with a static buffer at the end of the state structure using IIO_DECLARE_BUFFER_WITH_TS(). This eliminates the allocation overhead, prevents leaks, and removes the need for manual kfree() on driver removal. Suggested-by: David Lechner Reviewed-by: David Lechner Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index bf0575585a59..d8389b6e19b5 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -39,6 +39,7 @@ #include #define AD799X_CHANNEL_SHIFT 4 +#define AD799X_MAX_CHANNELS 8 /* * AD7991, AD7995 and AD7999 defines @@ -133,8 +134,8 @@ struct ad799x_state { unsigned int id; u16 config; - u8 *rx_buf; unsigned int transfer_size; + IIO_DECLARE_BUFFER_WITH_TS(__be16, rx_buf, AD799X_MAX_CHANNELS); }; static int ad799x_write_config(struct ad799x_state *st, u16 val) @@ -217,7 +218,8 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) } b_sent = i2c_smbus_read_i2c_block_data(st->client, - cmd, st->transfer_size, st->rx_buf); + cmd, st->transfer_size, + (u8 *)st->rx_buf); if (b_sent < 0) goto out; @@ -234,11 +236,6 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev, { struct ad799x_state *st = iio_priv(indio_dev); - kfree(st->rx_buf); - st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (!st->rx_buf) - return -ENOMEM; - st->transfer_size = bitmap_weight(scan_mask, iio_get_masklength(indio_dev)) * 2; @@ -896,7 +893,6 @@ static void ad799x_remove(struct i2c_client *client) if (st->vref) regulator_disable(st->vref); regulator_disable(st->reg); - kfree(st->rx_buf); } static int ad799x_suspend(struct device *dev) From 97c3fae0dbc4bf62eac7a1798b723c258e85980d Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Sun, 12 Apr 2026 15:07:38 +0530 Subject: [PATCH 0073/5214] iio: adc: ad799x: cache regulator voltages during probe Since the reference voltage for this ADC is not expected to change at runtime, determine the active reference voltage (either VREF or VCC) during probe() and cache it in a single variable in the state structure. Suggested-by: Jonathan Cameron Suggested-by: David Lechner Reviewed-by: David Lechner Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index d8389b6e19b5..e37bb64edd2b 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -135,6 +136,9 @@ struct ad799x_state { u16 config; unsigned int transfer_size; + + int vref_uV; + IIO_DECLARE_BUFFER_WITH_TS(__be16, rx_buf, AD799X_MAX_CHANNELS); }; @@ -303,14 +307,7 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if (st->vref) - ret = regulator_get_voltage(st->vref); - else - ret = regulator_get_voltage(st->reg); - - if (ret < 0) - return ret; - *val = ret / 1000; + *val = st->vref_uV / (MICRO / MILLI); *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; } @@ -829,9 +826,20 @@ static int ad799x_probe(struct i2c_client *client) ret = regulator_enable(st->vref); if (ret) goto error_disable_reg; + ret = regulator_get_voltage(st->vref); + if (ret < 0) + goto error_disable_vref; + st->vref_uV = ret; } } + if (!st->vref) { + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + st->vref_uV = ret; + } + st->client = client; indio_dev->name = id->name; From 99d709987276632a9260dc75e36c15e61b4b5083 Mon Sep 17 00:00:00 2001 From: Archit Anant Date: Sun, 12 Apr 2026 15:07:39 +0530 Subject: [PATCH 0074/5214] iio: adc: ad799x: convert to fully managed resources and drop remove() Convert the driver's remaining manual resource management to use the devm_ infrastructure, allowing for the complete removal of the ad799x_remove() function and the simplification of the probe error paths. Specifically: - Initialize the mutex using devm_mutex_init() and move it to the top of probe() (before IRQ registration) to prevent a race condition where an interrupt could attempt to take an uninitialized lock. - Use devm_add_action_or_reset() to ensure that the VCC and VREF regulators are disabled safely and in the correct order during driver teardown or probe failure. - Refactor the optional VREF error handling path for better readability. - Convert iio_triggered_buffer_setup() and iio_device_register() to their devm_ variants. Because all resources are now managed by the devm core, the unwinding order is guaranteed to follow the reverse order of allocation. All manual error handling goto labels in ad799x_probe() have been removed. Suggested-by: Jonathan Cameron Suggested-by: David Lechner Suggested-by: Andy Shevchenko Reviewed-by: David Lechner Signed-off-by: Archit Anant Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 70 +++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index e37bb64edd2b..0ceedea5df0b 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -775,6 +775,11 @@ static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { }, }; +static void ad799x_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad799x_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -794,6 +799,10 @@ static int ad799x_probe(struct i2c_client *client) /* this is only used for device removal purposes */ i2c_set_clientdata(client, indio_dev); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + st->id = id->driver_data; if (client->irq > 0 && chip_info->irq_config.info) st->chip_config = &chip_info->irq_config; @@ -809,15 +818,19 @@ static int ad799x_probe(struct i2c_client *client) if (ret) return ret; + ret = devm_add_action_or_reset(dev, ad799x_reg_disable, st->reg); + if (ret) + return ret; + /* check if an external reference is supplied */ if (chip_info->has_vref) { st->vref = devm_regulator_get_optional(dev, "vref"); ret = PTR_ERR_OR_ZERO(st->vref); - if (ret) { - if (ret != -ENODEV) - goto error_disable_reg; + if (ret == -ENODEV) { st->vref = NULL; dev_info(dev, "Using VCC reference voltage\n"); + } else if (ret) { + return ret; } if (st->vref) { @@ -825,10 +838,15 @@ static int ad799x_probe(struct i2c_client *client) extra_config |= AD7991_REF_SEL; ret = regulator_enable(st->vref); if (ret) - goto error_disable_reg; + return ret; + + ret = devm_add_action_or_reset(dev, ad799x_reg_disable, st->vref); + if (ret) + return ret; + ret = regulator_get_voltage(st->vref); if (ret < 0) - goto error_disable_vref; + return ret; st->vref_uV = ret; } } @@ -836,7 +854,7 @@ static int ad799x_probe(struct i2c_client *client) if (!st->vref) { ret = regulator_get_voltage(st->reg); if (ret < 0) - goto error_disable_reg; + return ret; st->vref_uV = ret; } @@ -851,12 +869,12 @@ static int ad799x_probe(struct i2c_client *client) ret = ad799x_update_config(st, st->chip_config->default_config | extra_config); if (ret) - goto error_disable_vref; + return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, &ad799x_trigger_handler, NULL); if (ret) - goto error_disable_vref; + return ret; if (client->irq > 0) { ret = devm_request_threaded_irq(dev, @@ -868,39 +886,10 @@ static int ad799x_probe(struct i2c_client *client) client->name, indio_dev); if (ret) - goto error_cleanup_ring; + return ret; } - mutex_init(&st->lock); - - ret = iio_device_register(indio_dev); - if (ret) - goto error_cleanup_ring; - - return 0; - -error_cleanup_ring: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_vref: - if (st->vref) - regulator_disable(st->vref); -error_disable_reg: - regulator_disable(st->reg); - - return ret; -} - -static void ad799x_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ad799x_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - iio_triggered_buffer_cleanup(indio_dev); - if (st->vref) - regulator_disable(st->vref); - regulator_disable(st->reg); + return devm_iio_device_register(dev, indio_dev); } static int ad799x_suspend(struct device *dev) @@ -970,7 +959,6 @@ static struct i2c_driver ad799x_driver = { .pm = pm_sleep_ptr(&ad799x_pm_ops), }, .probe = ad799x_probe, - .remove = ad799x_remove, .id_table = ad799x_id, }; module_i2c_driver(ad799x_driver); From e0c6843248ec45c691695b04d1676c67870d6d59 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 11 Apr 2026 17:13:33 -0500 Subject: [PATCH 0075/5214] iio: adc: ti-ads7950: use spi_optimize_message() Use spi_optimize_message() to reduce CPU usage during buffered reads. On hardware with support for SPI_CS_WORD, this reduced the CPU usage of the threaded interrupt by about 5%. On hardware without support, this should reduce CPU usage even more since it won't have to split the SPI transfers each time the interrupt handler is called. The .update_scan_mode() callback hand to be moved to the buffer preenable callback since the SPI transfer mode can't be changed after spi_optimize_message() has been called. (The buffer postenable callback can't be used because it happens after the trigger is enabled, so the SPI message needs to be optimized before that.) The indent of the pointer to ti_ads7950_read_raw() in the assignment is changed since there is no longer anything else in the struct to align with since removal of use of the pointer to ti_ads7950_update_scan_mode(). Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 65 ++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 882b280d9e0b..39a074bce6d5 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -267,31 +267,6 @@ static const struct ti_ads7950_chip_info ti_ads7961_chip_info = { .num_channels = ARRAY_SIZE(ti_ads7961_channels), }; -/* - * ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new - * scan mask - */ -static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *active_scan_mask) -{ - struct ti_ads7950_state *st = iio_priv(indio_dev); - int i, cmd, len; - - len = 0; - for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) { - cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i)); - st->tx_buf[len++] = cmd; - } - - /* Data for the 1st channel is not returned until the 3rd transfer */ - st->tx_buf[len++] = 0; - st->tx_buf[len++] = 0; - - st->ring_xfer.len = len * 2; - - return 0; -} - static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -375,8 +350,42 @@ static int ti_ads7950_read_raw(struct iio_dev *indio_dev, } static const struct iio_info ti_ads7950_info = { - .read_raw = &ti_ads7950_read_raw, - .update_scan_mode = ti_ads7950_update_scan_mode, + .read_raw = &ti_ads7950_read_raw, +}; + +static int ti_ads7950_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + u32 len = 0; + u32 i; + u16 cmd; + + for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->num_channels) { + cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i)); + st->tx_buf[len++] = cmd; + } + + /* Data for the 1st channel is not returned until the 3rd transfer */ + st->tx_buf[len++] = 0; + st->tx_buf[len++] = 0; + + st->ring_xfer.len = len * 2; + + return spi_optimize_message(st->spi, &st->ring_msg); +} + +static int ti_ads7950_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + + spi_unoptimize_message(&st->ring_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ti_ads7950_buffer_setup_ops = { + .preenable = ti_ads7950_buffer_preenable, + .postdisable = ti_ads7950_buffer_postdisable, }; static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, @@ -573,7 +582,7 @@ static int ti_ads7950_probe(struct spi_device *spi) ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, &ti_ads7950_trigger_handler, - NULL); + &ti_ads7950_buffer_setup_ops); if (ret) return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); From 1619d52fce78b47021a2f1aa5b95f7986ab66c0f Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Sat, 11 Apr 2026 10:46:47 +0530 Subject: [PATCH 0076/5214] iio: dac: ad3552r: use field_get() for power-down bit read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use field_get() for the per-channel DAC power-down bit instead of an open-coded mask-and-shift sequence. No functional change. Signed-off-by: Piyush Patle Reviewed-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3552r.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index 93c33bc3e1be..f206bba3a701 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -167,8 +167,7 @@ static int ad3552r_read_raw(struct iio_dev *indio_dev, mutex_unlock(&dac->lock); if (err < 0) return err; - *val = !((tmp_val & AD3552R_MASK_CH_DAC_POWERDOWN(ch)) >> - __ffs(AD3552R_MASK_CH_DAC_POWERDOWN(ch))); + *val = !field_get(AD3552R_MASK_CH_DAC_POWERDOWN(ch), tmp_val); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = dac->ch_data[ch].scale_int; From 6cc18c0dc2886df14095b89a84d016b6d4c84fb3 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Sat, 11 Apr 2026 03:11:25 +0530 Subject: [PATCH 0077/5214] iio: adc: nxp-sar-adc: use field_get() for EOC bit check Use field_get() here now that runtime-mask support exists, and drop the obsolete TODO. Since NXP_SAR_ADC_EOC_CH(c) is BIT(c), the resulting !-test is semantically identical. No functional change. Reviewed-by: David Lechner Signed-off-by: Piyush Patle Acked-by: Daniel Lezcano Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9d9f2c76bed4..8340c041e7bc 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -317,11 +317,7 @@ static int nxp_sar_adc_read_data(struct nxp_sar_adc *info, unsigned int chan) ceocfr = readl(NXP_SAR_ADC_CEOCFR0(info->regs)); - /* - * FIELD_GET() can not be used here because EOC_CH is not constant. - * TODO: Switch to field_get() when it will be available. - */ - if (!(NXP_SAR_ADC_EOC_CH(chan) & ceocfr)) + if (!field_get(NXP_SAR_ADC_EOC_CH(chan), ceocfr)) return -EIO; cdr = readl(NXP_SAR_ADC_CDR(info->regs, chan)); From e15c8934e6ed27624e4b2ed37619074c98be52ac Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:31 +0100 Subject: [PATCH 0078/5214] iio: dac: ad3552r: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3552r.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index f206bba3a701..16db94fef9d4 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -628,7 +628,9 @@ static int ad3552r_probe(struct spi_device *spi) if (!dac->model_data) return -EINVAL; - mutex_init(&dac->lock); + err = devm_mutex_init(&spi->dev, &dac->lock); + if (err) + return err; err = ad3552r_init(dac); if (err) From de960173ae966b92ccc5426347c8d7f4db488a8e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:32 +0100 Subject: [PATCH 0079/5214] iio: dac: ad7303: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7303.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index a88cc639047d..1c2960fa9743 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -218,7 +218,9 @@ static int ad7303_probe(struct spi_device *spi) st->spi = spi; - mutex_init(&st->lock); + ret = devm_mutex_init(&spi->dev, &st->lock); + if (ret) + return ret; st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); if (IS_ERR(st->vdd_reg)) From b8c15f8589e6a716e5141ef87c742a542553ed65 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:33 +0100 Subject: [PATCH 0080/5214] iio: dac: ad5758: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5758.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 4ed4fda76ea9..8e6fb46cce4d 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -851,7 +851,9 @@ static int ad5758_probe(struct spi_device *spi) st->spi = spi; - mutex_init(&st->lock); + ret = devm_mutex_init(&spi->dev, &st->lock); + if (ret) + return ret; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad5758_info; From e7394ce0e5bb7fe86b8f9b3ceabf197ff40287a8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:34 +0100 Subject: [PATCH 0081/5214] iio: dac: ad5755: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5755.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index d0e5f35462b1..cc6d56140d66 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -827,8 +827,9 @@ static int ad5755_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = AD5755_NUM_CHANNELS; - mutex_init(&st->lock); - + ret = devm_mutex_init(&spi->dev, &st->lock); + if (ret) + return ret; pdata = ad5755_parse_fw(&spi->dev); if (!pdata) { From 81227f33a84db1448caab226f25f6b9e21bf18ac Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:35 +0100 Subject: [PATCH 0082/5214] iio: dac: ad5686: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 4b18498aa074..9a384c50929b 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -494,7 +494,9 @@ int ad5686_probe(struct device *dev, indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; - mutex_init(&st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; switch (st->chip_info->regmap_type) { case AD5310_REGMAP: From be186094ee51c26bfb0e8cd6a46286e7e413abe7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:36 +0100 Subject: [PATCH 0083/5214] iio: dac: ltc2664: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2664.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ltc2664.c b/drivers/iio/dac/ltc2664.c index 67f14046cf77..616806615d3d 100644 --- a/drivers/iio/dac/ltc2664.c +++ b/drivers/iio/dac/ltc2664.c @@ -675,7 +675,9 @@ static int ltc2664_probe(struct spi_device *spi) st->chip_info = chip_info; - mutex_init(&st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; st->regmap = devm_regmap_init_spi(spi, <c2664_regmap_config); if (IS_ERR(st->regmap)) From 8eedc312f1a1ac69ff322e846bb5d58362017945 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Apr 2026 20:37:37 +0100 Subject: [PATCH 0084/5214] iio: addac: ad74115: Use devm_mutex_init() Use devm_mutex_init() which is helpful with CONFIG_DEBUG_MUTEXES. Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74115.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c index f8b04d86b01f..41e0b1d334cc 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -1835,7 +1835,10 @@ static int ad74115_probe(struct spi_device *spi) st = iio_priv(indio_dev); st->spi = spi; - mutex_init(&st->lock); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + init_completion(&st->adc_data_completion); indio_dev->name = AD74115_NAME; From 77fb0dc77249891340160063a5c91b2225d2a1be Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 1 Apr 2026 08:58:02 -0300 Subject: [PATCH 0085/5214] dt-bindings: iio: adc: ad4130: Document interrupts property The Data Ready/FIFO interrupt has a special behavior that inverts the IRQ polarity when devices with FIFO support enter FIFO mode, while using normal polarity for data ready. Document the interrupts property to clarify this special behavior for users. Acked-by: Krzysztof Kozlowski Signed-off-by: Jonathan Santos Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml index d00690a8d3fb..fcc00e5cfd54 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml @@ -32,6 +32,10 @@ properties: interrupts: maxItems: 1 + description: | + Data Ready / FIFO interrupt. For devices with FIFO support, the + interrupt polarity specified here is inverted when the device enters + FIFO mode, and normal for data ready. interrupt-names: description: | From 277a2d241c089fa885f734e6cf57f30883399571 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 1 Apr 2026 08:58:12 -0300 Subject: [PATCH 0086/5214] dt-bindings: iio: adc: ad4130: Add new supported parts Extend binding support for AD4129-4/8, AD4130-4, and AD4131-4/8 ADC variants. Dropped a reference to driver in the binding whilst here. Acked-by: Krzysztof Kozlowski Signed-off-by: Jonathan Santos Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adi,ad4130.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml index fcc00e5cfd54..cc38617bb829 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml @@ -5,19 +5,30 @@ $id: http://devicetree.org/schemas/iio/adc/adi,ad4130.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD4130 ADC device driver +title: Analog Devices AD4130 family ADCs maintainers: - Cosmin Tanislav description: | - Bindings for the Analog Devices AD4130 ADC. Datasheet can be found here: + Bindings for the Analog Devices AD4130 family ADCs. + Datasheets can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4129-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4129-8.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4130-4.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD4130-8.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4131-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4131-8.pdf properties: compatible: enum: + - adi,ad4129-4 + - adi,ad4129-8 + - adi,ad4130-4 - adi,ad4130 + - adi,ad4131-4 + - adi,ad4131-8 reg: maxItems: 1 From 1ebc22b9b32fc4319538f95bd65f9dee5a5e6c38 Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Sun, 5 Apr 2026 18:37:26 -0300 Subject: [PATCH 0087/5214] iio: adc: ad4170: use lookup table for gpio mask selection Both ad4170_gpio_direction_input() and ad4170_gpio_direction_output() duplicate the same switch statement to map a GPIO offset to its corresponding mask. Replace the switch with a static lookup table, simplifying the code and avoiding duplication. This also makes future extensions easier. No functional change intended. Signed-off-by: Guilherme Ivo Bozi Acked-by: Marcelo Schmitt Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4170-4.c | 47 ++++++++++++-------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 77af0e6b2c59..627cbf5a37b0 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -1699,34 +1699,29 @@ static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) return ret; } +static const unsigned long gpio_masks[] = { + AD4170_GPIO_MODE_GPIO0_MSK, + AD4170_GPIO_MODE_GPIO1_MSK, + AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO3_MSK, +}; + static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) { struct iio_dev *indio_dev = gpiochip_get_data(gc); struct ad4170_state *st = iio_priv(indio_dev); - unsigned long gpio_mask; int ret; if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - switch (offset) { - case 0: - gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK; - break; - case 1: - gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK; - break; - case 2: - gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK; - break; - case 3: - gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK; - break; - default: + if (offset >= ARRAY_SIZE(gpio_masks)) { ret = -EINVAL; goto err_release; } - ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, + gpio_masks[offset], AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset)); err_release: @@ -1740,7 +1735,6 @@ static int ad4170_gpio_direction_output(struct gpio_chip *gc, { struct iio_dev *indio_dev = gpiochip_get_data(gc); struct ad4170_state *st = iio_priv(indio_dev); - unsigned long gpio_mask; int ret; ret = ad4170_gpio_set(gc, offset, value); @@ -1750,24 +1744,13 @@ static int ad4170_gpio_direction_output(struct gpio_chip *gc, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - switch (offset) { - case 0: - gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK; - break; - case 1: - gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK; - break; - case 2: - gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK; - break; - case 3: - gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK; - break; - default: + if (offset >= ARRAY_SIZE(gpio_masks)) { ret = -EINVAL; goto err_release; } - ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, + gpio_masks[offset], AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset)); err_release: From d01d624c28e77bc592ed8e1a9e817edec5826031 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 21 Apr 2026 06:58:37 +0000 Subject: [PATCH 0088/5214] iio: magnetometer: hid-sensor-magn-3d: prefer 'u32' type Use 'u32' instead of bare 'unsigned' to resolve checkpatch.pl warnings and correct type use as defined in the struct hid_sensor_hub_callbacks. No functional change. Acked-by: Srinivas Pandruvada Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index c673f9323e47..b01dd53eb100 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -280,7 +280,7 @@ static const struct iio_info magn_3d_info = { /* Callback handler to send event after all samples are received and captured */ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, void *priv) { struct iio_dev *indio_dev = platform_get_drvdata(priv); @@ -302,7 +302,7 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, /* Capture samples in local storage */ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, - unsigned usage_id, + u32 usage_id, size_t raw_len, char *raw_data, void *priv) { @@ -350,7 +350,7 @@ static int magn_3d_parse_report(struct platform_device *pdev, struct hid_sensor_hub_device *hsdev, struct iio_chan_spec **channels, int *chan_count, - unsigned usage_id, + u32 usage_id, struct magn_3d_state *st) { int i; From ce80292ead5bb42b50a6b63e44fd95c0edf9d334 Mon Sep 17 00:00:00 2001 From: Kevin Tung Date: Mon, 20 Apr 2026 20:52:52 +0800 Subject: [PATCH 0089/5214] iio: adc: rtq6056: add i2c_device_id support Add i2c_device_id table to support legacy I2C instantiation. Update probe to use i2c_get_match_data() so device data can be retrieved consistently for both OF and legacy I2C instantiation. Signed-off-by: Kevin Tung Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rtq6056.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index 2bf3a09ac6b0..e2b1da13c0d3 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -728,7 +728,7 @@ static int rtq6056_probe(struct i2c_client *i2c) if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; - devdata = device_get_match_data(dev); + devdata = i2c_get_match_data(i2c); if (!devdata) return dev_err_probe(dev, -EINVAL, "Invalid dev data\n"); @@ -871,6 +871,13 @@ static const struct richtek_dev_data rtq6059_devdata = { .set_average = rtq6059_adc_set_average, }; +static const struct i2c_device_id rtq6056_id[] = { + { "rtq6056", (kernel_ulong_t)&rtq6056_devdata }, + { "rtq6059", (kernel_ulong_t)&rtq6059_devdata }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rtq6056_id); + static const struct of_device_id rtq6056_device_match[] = { { .compatible = "richtek,rtq6056", .data = &rtq6056_devdata }, { .compatible = "richtek,rtq6059", .data = &rtq6059_devdata }, @@ -885,6 +892,7 @@ static struct i2c_driver rtq6056_driver = { .pm = pm_ptr(&rtq6056_pm_ops), }, .probe = rtq6056_probe, + .id_table = rtq6056_id, }; module_i2c_driver(rtq6056_driver); From 0179a95bbb8ce2fc36ba3766c7db5c7b4dd180b9 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Mon, 20 Apr 2026 15:34:45 +0400 Subject: [PATCH 0090/5214] iio: adc: ti-ads7924: Use guard(mutex) in ADC read helper Replace mutex_lock()/mutex_unlock() pair with guard(mutex)() and move the lock into ads7924_get_adc_result(). Keeping the guard in the helper makes the locking scope match the operation being protected. Suggested-by: Jonathan Cameron Signed-off-by: Giorgi Tchankvetadze Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7924.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c index bbcc4fc22b6e..5f294595a415 100644 --- a/drivers/iio/adc/ti-ads7924.c +++ b/drivers/iio/adc/ti-ads7924.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -198,6 +199,8 @@ static int ads7924_get_adc_result(struct ads7924_data *data, if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS) return -EINVAL; + guard(mutex)(&data->lock); + if (data->conv_invalid) { int conv_time; @@ -227,9 +230,7 @@ static int ads7924_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&data->lock); ret = ads7924_get_adc_result(data, chan, val); - mutex_unlock(&data->lock); if (ret < 0) return ret; From 2c5ebb7708274ac1e41cbd3248ed87aa3a1cee71 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 1 Apr 2026 08:58:22 -0300 Subject: [PATCH 0091/5214] iio: adc: ad4130: Add SPI device ID table Add SPI device ID table to enable non-device tree based device binding. The id_table provides a fallback matching mechanism when of_match_table cannot be used, which is required for proper SPI driver registration. Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Santos Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4130.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 5567ae5dee88..d7aaf57ab87a 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -2109,12 +2109,19 @@ static const struct of_device_id ad4130_of_match[] = { }; MODULE_DEVICE_TABLE(of, ad4130_of_match); +static const struct spi_device_id ad4130_id_table[] = { + { "ad4130" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4130_id_table); + static struct spi_driver ad4130_driver = { .driver = { .name = AD4130_NAME, .of_match_table = ad4130_of_match, }, .probe = ad4130_probe, + .id_table = ad4130_id_table, }; module_spi_driver(ad4130_driver); From 71c1a1b376b3a7520c8f59f81ab6a28d77938ff5 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 1 Apr 2026 08:58:34 -0300 Subject: [PATCH 0092/5214] iio: adc: ad4130: introduce chip info for future multidevice support Introduce a chip_info structure to abstract device-specific parameters and prepare the driver for supporting multiple AD4130 family variants. Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Santos Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4130.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index d7aaf57ab87a..b064744e8da8 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -224,6 +224,14 @@ enum ad4130_pin_function { AD4130_PIN_FN_VBIAS = BIT(3), }; +struct ad4130_chip_info { + const char *name; + unsigned int max_analog_pins; + const struct iio_info *info; + const unsigned int *reg_size; + const unsigned int reg_size_length; +}; + /* * If you make adaptations in this struct, you most likely also have to adapt * ad4130_setup_info_eq(), too. @@ -268,6 +276,7 @@ struct ad4130_state { struct regmap *regmap; struct spi_device *spi; struct clk *mclk; + const struct ad4130_chip_info *chip_info; struct regulator_bulk_data regulators[4]; u32 irq_trigger; u32 inv_irq_trigger; @@ -394,10 +403,10 @@ static const char * const ad4130_filter_types_str[] = { static int ad4130_get_reg_size(struct ad4130_state *st, unsigned int reg, unsigned int *size) { - if (reg >= ARRAY_SIZE(ad4130_reg_size)) + if (reg >= st->chip_info->reg_size_length) return -EINVAL; - *size = ad4130_reg_size[reg]; + *size = st->chip_info->reg_size[reg]; return 0; } @@ -1291,6 +1300,14 @@ static const struct iio_info ad4130_info = { .debugfs_reg_access = ad4130_reg_access, }; +static const struct ad4130_chip_info ad4130_8_chip_info = { + .name = "ad4130-8", + .max_analog_pins = 16, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), +}; + static int ad4130_buffer_postenable(struct iio_dev *indio_dev) { struct ad4130_state *st = iio_priv(indio_dev); @@ -1504,7 +1521,7 @@ static int ad4130_validate_diff_channel(struct ad4130_state *st, u32 pin) return dev_err_probe(dev, -EINVAL, "Invalid differential channel %u\n", pin); - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return 0; if (st->pins_fn[pin] == AD4130_PIN_FN_SPECIAL) @@ -1536,7 +1553,7 @@ static int ad4130_validate_excitation_pin(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Invalid excitation pin %u\n", pin); @@ -1554,7 +1571,7 @@ static int ad4130_validate_vbias_pin(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Invalid vbias pin %u\n", pin); @@ -1730,7 +1747,7 @@ static int ad4310_parse_fw(struct iio_dev *indio_dev) ret = device_property_count_u32(dev, "adi,vbias-pins"); if (ret > 0) { - if (ret > AD4130_MAX_ANALOG_PINS) + if (ret > st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Too many vbias pins %u\n", ret); @@ -1994,6 +2011,8 @@ static int ad4130_probe(struct spi_device *spi) st = iio_priv(indio_dev); + st->chip_info = device_get_match_data(dev); + memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); init_completion(&st->completion); mutex_init(&st->lock); @@ -2011,9 +2030,9 @@ static int ad4130_probe(struct spi_device *spi) spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, ARRAY_SIZE(st->fifo_xfer)); - indio_dev->name = AD4130_NAME; + indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &ad4130_info; + indio_dev->info = st->chip_info->info; st->regmap = devm_regmap_init(dev, NULL, st, &ad4130_regmap_config); if (IS_ERR(st->regmap)) @@ -2056,7 +2075,7 @@ static int ad4130_probe(struct spi_device *spi) ad4130_fill_scale_tbls(st); st->gc.owner = THIS_MODULE; - st->gc.label = AD4130_NAME; + st->gc.label = st->chip_info->name; st->gc.base = -1; st->gc.ngpio = AD4130_MAX_GPIOS; st->gc.parent = dev; @@ -2104,13 +2123,14 @@ static int ad4130_probe(struct spi_device *spi) static const struct of_device_id ad4130_of_match[] = { { .compatible = "adi,ad4130", + .data = &ad4130_8_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4130_of_match); static const struct spi_device_id ad4130_id_table[] = { - { "ad4130" }, + { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4130_id_table); From ec98c3b501573fa6649b228b4881cde33f0ae6de Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 1 Apr 2026 08:58:50 -0300 Subject: [PATCH 0093/5214] iio: adc: ad4130: add new supported parts Add support for AD4129-4/8, AD4130-4, and AD4131-4/8 variants. The AD4129 series supports the same FIFO interface as the AD4130 but with reduced resolution (16-bit). The AD4131 series lacks FIFO support, so triggered buffer functionality is introduced. The 4-channel variants feature fewer analog inputs, GPIOs, and sparse pin mappings for VBIAS, analog inputs, and excitation currents. The driver now handles these differences with chip-specific configurations, including pin mappings and GPIO counts. Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Santos Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4130.c | 452 ++++++++++++++++++++++++++++++++------- 1 file changed, 376 insertions(+), 76 deletions(-) diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index b064744e8da8..7f39f3484062 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include @@ -30,6 +32,9 @@ #include #include #include +#include +#include +#include #define AD4130_NAME "ad4130" @@ -40,6 +45,7 @@ #define AD4130_ADC_CONTROL_REG 0x01 #define AD4130_ADC_CONTROL_BIPOLAR_MASK BIT(14) #define AD4130_ADC_CONTROL_INT_REF_VAL_MASK BIT(13) +#define AD4130_ADC_CONTROL_CONT_READ_MASK BIT(11) #define AD4130_INT_REF_2_5V 2500000 #define AD4130_INT_REF_1_25V 1250000 #define AD4130_ADC_CONTROL_CSB_EN_MASK BIT(9) @@ -54,7 +60,9 @@ #define AD4130_IO_CONTROL_REG 0x03 #define AD4130_IO_CONTROL_INT_PIN_SEL_MASK GENMASK(9, 8) #define AD4130_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 4) +#define AD4130_4_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 6) #define AD4130_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 0) +#define AD4130_4_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 2) #define AD4130_VBIAS_REG 0x04 @@ -125,6 +133,28 @@ #define AD4130_INVALID_SLOT -1 +static const unsigned int ad4129_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, + [AD4130_FIFO_CONTROL_REG] = 3, + [AD4130_FIFO_STATUS_REG] = 1, + [AD4130_FIFO_THRESHOLD_REG] = 3, + [AD4130_FIFO_DATA_REG] = 2, +}; + static const unsigned int ad4130_reg_size[] = { [AD4130_STATUS_REG] = 1, [AD4130_ADC_CONTROL_REG] = 2, @@ -147,6 +177,24 @@ static const unsigned int ad4130_reg_size[] = { [AD4130_FIFO_DATA_REG] = 3, }; +static const unsigned int ad4131_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, +}; + enum ad4130_int_ref_val { AD4130_INT_REF_VAL_2_5V, AD4130_INT_REF_VAL_1_25V, @@ -224,12 +272,26 @@ enum ad4130_pin_function { AD4130_PIN_FN_VBIAS = BIT(3), }; +/* Pin mapping for AIN0..AIN7, VBIAS_0..VBIAS_7 */ +static const u8 ad4130_4_pin_map[] = { + 0x00, 0x01, 0x04, 0x05, 0x0A, 0x0B, 0x0E, 0x0F, /* 0 - 7 */ +}; + +/* Pin mapping for AIN0..AIN15, VBIAS_0..VBIAS_15 */ +static const u8 ad4130_8_pin_map[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0 - 7 */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 8 - 15 */ +}; + struct ad4130_chip_info { const char *name; unsigned int max_analog_pins; + unsigned int num_gpios; const struct iio_info *info; const unsigned int *reg_size; const unsigned int reg_size_length; + const u8 *pin_map; + bool has_fifo; }; /* @@ -303,6 +365,7 @@ struct ad4130_state { u32 mclk_sel; bool int_ref_en; bool bipolar; + bool buffer_wait_for_irq; unsigned int num_enabled_channels; unsigned int effective_watermark; @@ -310,6 +373,7 @@ struct ad4130_state { struct spi_message fifo_msg; struct spi_transfer fifo_xfer[2]; + struct iio_trigger *trig; /* * DMA (thus cache coherency maintenance) requires any transfer @@ -321,9 +385,13 @@ struct ad4130_state { u8 reg_write_tx_buf[4]; u8 reg_read_tx_buf[1]; u8 reg_read_rx_buf[3]; - u8 fifo_tx_buf[2]; - u8 fifo_rx_buf[AD4130_FIFO_SIZE * - AD4130_FIFO_MAX_SAMPLE_SIZE]; + union { + struct { + u8 fifo_tx_buf[2]; + u8 fifo_rx_buf[AD4130_FIFO_SIZE * AD4130_FIFO_MAX_SAMPLE_SIZE]; + }; + IIO_DECLARE_BUFFER_WITH_TS(u32, scan_channels, AD4130_MAX_CHANNELS); + }; }; static const char * const ad4130_int_pin_names[] = { @@ -514,7 +582,8 @@ static int ad4130_gpio_init_valid_mask(struct gpio_chip *gc, /* * Output-only GPIO functionality is available on pins AIN2 through - * AIN5. If these pins are used for anything else, do not expose them. + * AIN5 for some parts and AIN2 through AIN3 for others. If these pins + * are used for anything else, do not expose them. */ for (i = 0; i < ngpios; i++) { unsigned int pin = i + AD4130_AIN2_P1; @@ -535,8 +604,14 @@ static int ad4130_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct ad4130_state *st = gpiochip_get_data(gc); - unsigned int mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, - BIT(offset)); + unsigned int mask; + + if (st->chip_info->num_gpios == AD4130_MAX_GPIOS) + mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); + else + mask = FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); return regmap_update_bits(st->regmap, AD4130_IO_CONTROL_REG, mask, value ? mask : 0); @@ -597,10 +672,43 @@ static irqreturn_t ad4130_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct ad4130_state *st = iio_priv(indio_dev); - if (iio_buffer_enabled(indio_dev)) - ad4130_push_fifo_data(indio_dev); - else + if (iio_buffer_enabled(indio_dev)) { + if (st->chip_info->has_fifo) + ad4130_push_fifo_data(indio_dev); + else if (st->buffer_wait_for_irq) + complete(&st->completion); + else + iio_trigger_poll(st->trig); + } else { complete(&st->completion); + } + + return IRQ_HANDLED; +} + +static irqreturn_t ad4130_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int data_reg_size = ad4130_data_reg_size(st); + struct spi_transfer xfer = { }; + unsigned int num_en_chn; + int ret; + + num_en_chn = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + xfer.rx_buf = st->scan_channels; + xfer.len = data_reg_size * num_en_chn; + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan_channels, + iio_get_time_ns(indio_dev)); + +err_out: + iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } @@ -1300,12 +1408,77 @@ static const struct iio_info ad4130_info = { .debugfs_reg_access = ad4130_reg_access, }; -static const struct ad4130_chip_info ad4130_8_chip_info = { - .name = "ad4130-8", +static const struct iio_info ad4131_info = { + .read_raw = ad4130_read_raw, + .read_avail = ad4130_read_avail, + .write_raw_get_fmt = ad4130_write_raw_get_fmt, + .write_raw = ad4130_write_raw, + .update_scan_mode = ad4130_update_scan_mode, + .debugfs_reg_access = ad4130_reg_access, +}; + +static const struct ad4130_chip_info ad4129_4_chip_info = { + .name = "ad4129-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4129_8_chip_info = { + .name = "ad4129-8", .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4130_4_chip_info = { + .name = "ad4130-4", + .max_analog_pins = 16, + .num_gpios = 2, .info = &ad4130_info, .reg_size = ad4130_reg_size, .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4130_8_chip_info = { + .name = "ad4130-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4131_4_chip_info = { + .name = "ad4131-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4131_8_chip_info = { + .name = "ad4131-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_8_pin_map, }; static int ad4130_buffer_postenable(struct iio_dev *indio_dev) @@ -1315,44 +1488,89 @@ static int ad4130_buffer_postenable(struct iio_dev *indio_dev) guard(mutex)(&st->lock); - ret = ad4130_set_watermark_interrupt_en(st, true); + if (st->chip_info->has_fifo) { + ret = ad4130_set_watermark_interrupt_en(st, true); + if (ret) + return ret; + + ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); + if (ret) + return ret; + + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); + if (ret) + return ret; + } + + ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); if (ret) return ret; - ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); - if (ret) - return ret; + /* + * When using triggered buffer, Entering continuous read mode must + * be the last command sent. No configuration changes are allowed until + * exiting this mode. + */ + if (!st->chip_info->has_fifo) { + ret = regmap_update_bits(st->regmap, AD4130_ADC_CONTROL_REG, + AD4130_ADC_CONTROL_CONT_READ_MASK, + FIELD_PREP(AD4130_ADC_CONTROL_CONT_READ_MASK, 1)); + if (ret) + return ret; + } - ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); - if (ret) - return ret; - - return ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); + return 0; } static int ad4130_buffer_predisable(struct iio_dev *indio_dev) { struct ad4130_state *st = iio_priv(indio_dev); unsigned int i; + u32 temp; int ret; guard(mutex)(&st->lock); + if (!st->chip_info->has_fifo) { + temp = 0x42; + reinit_completion(&st->completion); + + /* + * In continuous read mode, when all samples are read, the data + * ready signal returns high until the next conversion result is + * ready. To exit this mode, the command must be sent when data + * ready is low. In order to ensure that condition, wait for the + * next interrupt (when the new conversion is finished), allowing + * data ready to return low before sending the exit command. + */ + st->buffer_wait_for_irq = true; + if (!wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000))) + dev_warn(&st->spi->dev, "Conversion timed out\n"); + st->buffer_wait_for_irq = false; + + /* Perform a read data command to exit continuous read mode (0x42) */ + ret = spi_write(st->spi, &temp, 1); + if (ret) + return ret; + } + ret = ad4130_set_mode(st, AD4130_MODE_IDLE); if (ret) return ret; - ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); - if (ret) - return ret; + if (st->chip_info->has_fifo) { + ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); + if (ret) + return ret; - ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); - if (ret) - return ret; + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); + if (ret) + return ret; - ret = ad4130_set_watermark_interrupt_en(st, false); - if (ret) - return ret; + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } /* * update_scan_mode() is not called in the disable path, disable all @@ -1427,6 +1645,34 @@ static const struct iio_dev_attr *ad4130_fifo_attributes[] = { NULL }; +static const struct iio_trigger_ops ad4130_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad4130_triggered_buffer_setup(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + int ret; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4130_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad4130_trigger_handler, + &ad4130_buffer_ops); +} + static int _ad4130_find_table_index(const unsigned int *tbl, size_t len, unsigned int val) { @@ -1513,6 +1759,17 @@ static int ad4130_parse_fw_setup(struct ad4130_state *st, return 0; } +static unsigned int ad4130_translate_pin(struct ad4130_state *st, + unsigned int logical_pin) +{ + /* For analog input pins, use the chip-specific pin mapping */ + if (logical_pin < st->chip_info->max_analog_pins) + return st->chip_info->pin_map[logical_pin]; + + /* For internal channels, pass through unchanged */ + return logical_pin; +} + static int ad4130_validate_diff_channel(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; @@ -1931,9 +2188,14 @@ static int ad4130_setup(struct iio_dev *indio_dev) * function of P2 takes priority over the GPIO out function. */ val = 0; - for (i = 0; i < AD4130_MAX_GPIOS; i++) - if (st->pins_fn[i + AD4130_AIN2_P1] == AD4130_PIN_FN_NONE) - val |= FIELD_PREP(AD4130_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + for (i = 0; i < st->chip_info->num_gpios; i++) { + if (st->pins_fn[i + AD4130_AIN2_P1] == AD4130_PIN_FN_NONE) { + if (st->chip_info->num_gpios == 2) + val |= FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + else + val |= FIELD_PREP(AD4130_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + } + } val |= FIELD_PREP(AD4130_IO_CONTROL_INT_PIN_SEL_MASK, st->int_pin_sel); @@ -1943,21 +2205,23 @@ static int ad4130_setup(struct iio_dev *indio_dev) val = 0; for (i = 0; i < st->num_vbias_pins; i++) - val |= BIT(st->vbias_pins[i]); + val |= BIT(ad4130_translate_pin(st, st->vbias_pins[i])); ret = regmap_write(st->regmap, AD4130_VBIAS_REG, val); if (ret) return ret; - ret = regmap_clear_bits(st->regmap, AD4130_FIFO_CONTROL_REG, - AD4130_FIFO_CONTROL_HEADER_MASK); - if (ret) - return ret; + if (st->chip_info->has_fifo) { + ret = regmap_clear_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_HEADER_MASK); + if (ret) + return ret; - /* FIFO watermark interrupt starts out as enabled, disable it. */ - ret = ad4130_set_watermark_interrupt_en(st, false); - if (ret) - return ret; + /* FIFO watermark interrupt starts out as enabled, disable it. */ + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } /* Setup channels. */ for (i = 0; i < indio_dev->num_channels; i++) { @@ -1965,10 +2229,14 @@ static int ad4130_setup(struct iio_dev *indio_dev) struct iio_chan_spec *chan = &st->chans[i]; unsigned int val; - val = FIELD_PREP(AD4130_CHANNEL_AINP_MASK, chan->channel) | - FIELD_PREP(AD4130_CHANNEL_AINM_MASK, chan->channel2) | - FIELD_PREP(AD4130_CHANNEL_IOUT1_MASK, chan_info->iout0) | - FIELD_PREP(AD4130_CHANNEL_IOUT2_MASK, chan_info->iout1); + val = FIELD_PREP(AD4130_CHANNEL_AINP_MASK, + ad4130_translate_pin(st, chan->channel)) | + FIELD_PREP(AD4130_CHANNEL_AINM_MASK, + ad4130_translate_pin(st, chan->channel2)) | + FIELD_PREP(AD4130_CHANNEL_IOUT1_MASK, + ad4130_translate_pin(st, chan_info->iout0)) | + FIELD_PREP(AD4130_CHANNEL_IOUT2_MASK, + ad4130_translate_pin(st, chan_info->iout1)); ret = regmap_write(st->regmap, AD4130_CHANNEL_X_REG(i), val); if (ret) @@ -2018,17 +2286,19 @@ static int ad4130_probe(struct spi_device *spi) mutex_init(&st->lock); st->spi = spi; - /* - * Xfer: [ XFR1 ] [ XFR2 ] - * Master: 0x7D N ...................... - * Slave: ...... DATA1 DATA2 ... DATAN - */ - st->fifo_tx_buf[0] = AD4130_COMMS_READ_MASK | AD4130_FIFO_DATA_REG; - st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; - st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); - st->fifo_xfer[1].rx_buf = st->fifo_rx_buf; - spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, - ARRAY_SIZE(st->fifo_xfer)); + if (st->chip_info->has_fifo) { + /* + * Xfer: [ XFR1 ] [ XFR2 ] + * Master: 0x7D N ...................... + * Slave: ...... DATA1 DATA2 ... DATAN + */ + st->fifo_tx_buf[0] = AD4130_COMMS_READ_MASK | AD4130_FIFO_DATA_REG; + st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; + st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); + st->fifo_xfer[1].rx_buf = st->fifo_rx_buf; + spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, + ARRAY_SIZE(st->fifo_xfer)); + } indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -2077,7 +2347,7 @@ static int ad4130_probe(struct spi_device *spi) st->gc.owner = THIS_MODULE; st->gc.label = st->chip_info->name; st->gc.base = -1; - st->gc.ngpio = AD4130_MAX_GPIOS; + st->gc.ngpio = st->chip_info->num_gpios; st->gc.parent = dev; st->gc.can_sleep = true; st->gc.init_valid_mask = ad4130_gpio_init_valid_mask; @@ -2088,9 +2358,12 @@ static int ad4130_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, - &ad4130_buffer_ops, - ad4130_fifo_attributes); + if (st->chip_info->has_fifo) + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &ad4130_buffer_ops, + ad4130_fifo_attributes); + else + ret = ad4130_triggered_buffer_setup(indio_dev); if (ret) return ret; @@ -2100,37 +2373,64 @@ static int ad4130_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to request irq\n"); - /* - * When the chip enters FIFO mode, IRQ polarity is inverted. - * When the chip exits FIFO mode, IRQ polarity returns to normal. - * See datasheet pages: 65, FIFO Watermark Interrupt section, - * and 71, Bit Descriptions for STATUS Register, RDYB. - * Cache the normal and inverted IRQ triggers to set them when - * entering and exiting FIFO mode. - */ - st->irq_trigger = irq_get_trigger_type(spi->irq); - if (st->irq_trigger & IRQF_TRIGGER_RISING) - st->inv_irq_trigger = IRQF_TRIGGER_FALLING; - else if (st->irq_trigger & IRQF_TRIGGER_FALLING) - st->inv_irq_trigger = IRQF_TRIGGER_RISING; - else - return dev_err_probe(dev, -EINVAL, "Invalid irq flags: %u\n", - st->irq_trigger); + if (st->chip_info->has_fifo) { + /* + * When the chip enters FIFO mode, IRQ polarity is inverted. + * When the chip exits FIFO mode, IRQ polarity returns to normal. + * See datasheet pages: 65, FIFO Watermark Interrupt section, + * and 71, Bit Descriptions for STATUS Register, RDYB. + * Cache the normal and inverted IRQ triggers to set them when + * entering and exiting FIFO mode. + */ + st->irq_trigger = irq_get_trigger_type(spi->irq); + if (st->irq_trigger & IRQF_TRIGGER_RISING) + st->inv_irq_trigger = IRQF_TRIGGER_FALLING; + else if (st->irq_trigger & IRQF_TRIGGER_FALLING) + st->inv_irq_trigger = IRQF_TRIGGER_RISING; + else + return dev_err_probe(dev, -EINVAL, "Invalid irq flags: %u\n", + st->irq_trigger); + } return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id ad4130_of_match[] = { + { + .compatible = "adi,ad4129-4", + .data = &ad4129_4_chip_info + }, + { + .compatible = "adi,ad4129-8", + .data = &ad4129_8_chip_info + }, + { + .compatible = "adi,ad4130-4", + .data = &ad4130_4_chip_info + }, { .compatible = "adi,ad4130", .data = &ad4130_8_chip_info }, + { + .compatible = "adi,ad4131-4", + .data = &ad4131_4_chip_info + }, + { + .compatible = "adi,ad4131-8", + .data = &ad4131_8_chip_info + }, { } }; MODULE_DEVICE_TABLE(of, ad4130_of_match); static const struct spi_device_id ad4130_id_table[] = { + { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info }, + { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info }, + { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info }, { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, + { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info }, + { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4130_id_table); From af6dd359cd9682bff5cdd087d0982a153937b9c9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 Apr 2026 19:35:46 +0200 Subject: [PATCH 0094/5214] iio: adc: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a9dedbb8eb46..1115e81ac45b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1354,7 +1354,7 @@ config QCOM_SPMI_VADC be called qcom-spmi-vadc. config QCOM_SPMI_ADC5 - tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC" + tristate "Qualcomm SPMI PMIC5 ADC" depends on SPMI select REGMAP_SPMI select QCOM_VADC_COMMON @@ -1374,7 +1374,7 @@ config QCOM_SPMI_ADC5 be called qcom-spmi-adc5. config QCOM_SPMI_ADC5_GEN3 - tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC" + tristate "Qualcomm SPMI PMIC5 GEN3 ADC" depends on SPMI && THERMAL select REGMAP_SPMI select QCOM_VADC_COMMON From d7117c14cd6b3cfdf7206395d8e9c4d7d96107ff Mon Sep 17 00:00:00 2001 From: Lucas Ivars Cadima Ciziks Date: Fri, 24 Apr 2026 09:43:39 -0300 Subject: [PATCH 0095/5214] iio: adc: ad7280a: Extract chan->address bit fields into named local variables Extract the upper and lower bytes of chan->address into named local variables devaddr and ch across ad7280_read_raw(), ad7280_show_balance_timer() and ad7280_store_balance_timer() to improve readability and avoid inline bit manipulation in function calls. Suggested-by: Andy Shevchenko Signed-off-by: Lucas Ivars Cadima Ciziks Co-developed-by: Matheus Giarola Signed-off-by: Matheus Giarola Co-developed-by: Felipe Ribeiro de Souza Signed-off-by: Felipe Ribeiro de Souza Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7280a.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index f50e2b3121bf..5a01c3fc7cbb 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -516,12 +516,13 @@ static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev, char *buf) { struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; unsigned int msecs; int ret; mutex_lock(&st->lock); - ret = ad7280_read_reg(st, chan->address >> 8, - (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG); + ret = ad7280_read_reg(st, devaddr, ch + AD7280A_CB1_TIMER_REG); mutex_unlock(&st->lock); if (ret < 0) @@ -538,6 +539,8 @@ static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev, const char *buf, size_t len) { struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; int val, val2; int ret; @@ -552,8 +555,7 @@ static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev, return -EINVAL; mutex_lock(&st->lock); - ret = ad7280_write(st, chan->address >> 8, - (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG, 0, + ret = ad7280_write(st, devaddr, ch + AD7280A_CB1_TIMER_REG, 0, FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val)); mutex_unlock(&st->lock); @@ -881,6 +883,8 @@ static int ad7280_read_raw(struct iio_dev *indio_dev, long m) { struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; int ret; switch (m) { @@ -889,8 +893,7 @@ static int ad7280_read_raw(struct iio_dev *indio_dev, if (chan->address == AD7280A_ALL_CELLS) ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); else - ret = ad7280_read_channel(st, chan->address >> 8, - chan->address & 0xFF); + ret = ad7280_read_channel(st, devaddr, ch); mutex_unlock(&st->lock); if (ret < 0) @@ -900,7 +903,7 @@ static int ad7280_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6_REG) + if (ch <= AD7280A_CELL_VOLTAGE_6_REG) *val = 4000; else *val = 5000; From 9d5129c73bd0151e0f165b88e968a91ceef91275 Mon Sep 17 00:00:00 2001 From: Lucas Ivars Cadima Ciziks Date: Fri, 24 Apr 2026 09:43:40 -0300 Subject: [PATCH 0096/5214] iio: adc: ad7280a: use cleanup helpers guard() and scoped_guard() for mutex locking Replace open-coded mutex_lock/unlock pairs with the cleanup-based guard() and scoped_guard() helpers in ad7280a_write_thresh(), ad7280_show_balance_timer(), ad7280_store_balance_sw(), ad7280_store_balance_timer() and ad7280_read_raw(). This removes the need for the err_unlock label and explicit mutex_unlock() calls, as the lock is now automatically released when the function returns or the guarded scope exits, regardless of the exit path. Signed-off-by: Lucas Ivars Cadima Ciziks Co-developed-by: Matheus Giarola Signed-off-by: Matheus Giarola Co-developed-by: Felipe Ribeiro de Souza Signed-off-by: Felipe Ribeiro de Souza Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7280a.c | 56 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 5a01c3fc7cbb..01c2f55a680c 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -496,7 +496,8 @@ static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev, devaddr = chan->address >> 8; ch = chan->address & 0xFF; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + if (readin) st->cb_mask[devaddr] |= BIT(ch); else @@ -505,7 +506,6 @@ static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev, ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG, 0, FIELD_PREP(AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK, st->cb_mask[devaddr])); - mutex_unlock(&st->lock); return ret ? ret : len; } @@ -521,10 +521,8 @@ static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev, unsigned int msecs; int ret; - mutex_lock(&st->lock); - ret = ad7280_read_reg(st, devaddr, ch + AD7280A_CB1_TIMER_REG); - mutex_unlock(&st->lock); - + scoped_guard(mutex, &st->lock) + ret = ad7280_read_reg(st, devaddr, ch + AD7280A_CB1_TIMER_REG); if (ret < 0) return ret; @@ -554,10 +552,10 @@ static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev, if (val > 31) return -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + ret = ad7280_write(st, devaddr, ch + AD7280A_CB1_TIMER_REG, 0, FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val)); - mutex_unlock(&st->lock); return ret ? ret : len; } @@ -739,7 +737,8 @@ static int ad7280a_write_thresh(struct iio_dev *indio_dev, if (val2 != 0) return -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + switch (chan->type) { case IIO_VOLTAGE: value = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ @@ -750,22 +749,20 @@ static int ad7280a_write_thresh(struct iio_dev *indio_dev, ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, 1, value); if (ret) - break; + return ret; st->cell_threshhigh = value; - break; + return 0; case IIO_EV_DIR_FALLING: addr = AD7280A_CELL_UNDERVOLTAGE_REG; ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, 1, value); if (ret) - break; + return ret; st->cell_threshlow = value; - break; + return 0; default: - ret = -EINVAL; - goto err_unlock; + return -EINVAL; } - break; case IIO_TEMP: value = (val * 10) / 196; /* LSB 19.6mV */ value = clamp(value, 0L, 0xFFL); @@ -775,31 +772,23 @@ static int ad7280a_write_thresh(struct iio_dev *indio_dev, ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, 1, value); if (ret) - break; + return ret; st->aux_threshhigh = value; - break; + return 0; case IIO_EV_DIR_FALLING: addr = AD7280A_AUX_ADC_UNDERVOLTAGE_REG; ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, 1, value); if (ret) - break; + return ret; st->aux_threshlow = value; - break; + return 0; default: - ret = -EINVAL; - goto err_unlock; + return -EINVAL; } - break; default: - ret = -EINVAL; - goto err_unlock; + return -EINVAL; } - -err_unlock: - mutex_unlock(&st->lock); - - return ret; } static irqreturn_t ad7280_event_handler(int irq, void *private) @@ -888,13 +877,13 @@ static int ad7280_read_raw(struct iio_dev *indio_dev, int ret; switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&st->lock); + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&st->lock); + if (chan->address == AD7280A_ALL_CELLS) ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); else ret = ad7280_read_channel(st, devaddr, ch); - mutex_unlock(&st->lock); if (ret < 0) return ret; @@ -902,6 +891,7 @@ static int ad7280_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: if (ch <= AD7280A_CELL_VOLTAGE_6_REG) *val = 4000; From fde18a8a1dd7e8384d1db17435e7b3813b0512cc Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Wed, 22 Apr 2026 14:56:59 +0300 Subject: [PATCH 0097/5214] dt-bindings: iio: dac: mcp47feb02: Fix I2C address in example Change example reg value from 0 to 0x60 in order to use a valid I2C address Signed-off-by: Ariana Lazar Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml index d2466aa6bda2..350e80e4dbe0 100644 --- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml @@ -280,9 +280,9 @@ examples: #address-cells = <1>; #size-cells = <0>; - dac@0 { + dac@60 { compatible = "microchip,mcp47feb02"; - reg = <0>; + reg = <0x60>; vdd-supply = <&vdac_vdd>; vref-supply = <&vref_reg>; From 6d78ab5687ff2b949680b86d3e57fa8d7c6a21fa Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Wed, 22 Apr 2026 14:53:14 +0300 Subject: [PATCH 0098/5214] dt-bindings: iio: dac: mcp47feb02: fix reg property value bounds Replace minItems/maxItems with minimum/maximum to describe the reg property as a single channel number with 8 possible values (0-7) Signed-off-by: Ariana Lazar Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml index 350e80e4dbe0..b4ca5b6e743a 100644 --- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml @@ -161,8 +161,8 @@ patternProperties: properties: reg: description: The channel number. - minItems: 1 - maxItems: 8 + minimum: 0 + maximum: 7 label: description: Unique name to identify which channel this is. From 96aa96c029bf4b4309b27ffb17dcbe0c0d660e2a Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Wed, 22 Apr 2026 16:40:52 +0300 Subject: [PATCH 0099/5214] dt-bindings: iio: dac: mcp47feb02: fix example indentation Correct inconsistent indentation in the example and use consistent 4-space indentation. Signed-off-by: Ariana Lazar Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../iio/dac/microchip,mcp47feb02.yaml | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml index b4ca5b6e743a..d131f136bd15 100644 --- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml @@ -281,22 +281,22 @@ examples: #address-cells = <1>; #size-cells = <0>; dac@60 { - compatible = "microchip,mcp47feb02"; - reg = <0x60>; - vdd-supply = <&vdac_vdd>; - vref-supply = <&vref_reg>; + compatible = "microchip,mcp47feb02"; + reg = <0x60>; + vdd-supply = <&vdac_vdd>; + vref-supply = <&vref_reg>; - #address-cells = <1>; - #size-cells = <0>; - channel@0 { - reg = <0>; - label = "Adjustable_voltage_ch0"; - }; + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + label = "Adjustable_voltage_ch0"; + }; - channel@1 { - reg = <0x1>; - label = "Adjustable_voltage_ch1"; - }; - }; + channel@1 { + reg = <0x1>; + label = "Adjustable_voltage_ch1"; + }; + }; }; ... From 2450368e2e81716d9bd04a4631f5a480581881ec Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Wed, 22 Apr 2026 07:45:05 -0500 Subject: [PATCH 0100/5214] iio: proximity: srf08: Replace sprintf() with sysfs_emit() Replace sprintf() function calls with sysfs_emit() and sysfs_emit_at(). While the current code is fine, sysfs_emit() is preferred over sprintf(), and will help modernize the driver. Signed-off-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf08.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index d7e4cc48cfbf..92a37ba331f6 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -226,7 +226,7 @@ static int srf08_read_raw(struct iio_dev *indio_dev, static ssize_t srf08_show_range_mm_available(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "[0.043 0.043 11.008]\n"); + return sysfs_emit(buf, "[0.043 0.043 11.008]\n"); } static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO, @@ -238,8 +238,8 @@ static ssize_t srf08_show_range_mm(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct srf08_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d.%03d\n", data->range_mm / 1000, - data->range_mm % 1000); + return sysfs_emit(buf, "%d.%03d\n", + data->range_mm / 1000, data->range_mm % 1000); } /* @@ -316,10 +316,10 @@ static ssize_t srf08_show_sensitivity_available(struct device *dev, for (i = 0; i < data->chip_info->num_sensitivity_avail; i++) if (data->chip_info->sensitivity_avail[i]) - len += sprintf(buf + len, "%d ", - data->chip_info->sensitivity_avail[i]); + len += sysfs_emit_at(buf, len, "%d ", + data->chip_info->sensitivity_avail[i]); - len += sprintf(buf + len, "\n"); + len += sysfs_emit_at(buf, len, "\n"); return len; } @@ -332,11 +332,8 @@ static ssize_t srf08_show_sensitivity(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct srf08_data *data = iio_priv(indio_dev); - int len; - len = sprintf(buf, "%d\n", data->sensitivity); - - return len; + return sysfs_emit(buf, "%d\n", data->sensitivity); } static ssize_t srf08_write_sensitivity(struct srf08_data *data, From 1a5fc4b5ddeeebacb22fead349b9dba3b2a1056a Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 25 Apr 2026 15:54:27 +0300 Subject: [PATCH 0101/5214] dt-bindings: iio: light: Document Avago APDS9900/9901 ALS/Proximity sensor Document Avago APDS-9900/9901 combined ALS/IR-LED/Proximity sensor. Signed-off-by: Svyatoslav Ryhel Acked-by: Rob Herring (Arm) Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/light/tsl2772.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/light/tsl2772.yaml b/Documentation/devicetree/bindings/iio/light/tsl2772.yaml index d81229857944..9921ccaa64a0 100644 --- a/Documentation/devicetree/bindings/iio/light/tsl2772.yaml +++ b/Documentation/devicetree/bindings/iio/light/tsl2772.yaml @@ -26,6 +26,8 @@ properties: - amstaos,tmd2672 - amstaos,tsl2772 - amstaos,tmd2772 + - avago,apds9900 + - avago,apds9901 - avago,apds9930 reg: From 2f640a4d0fe181b6b02ea570bb64969e6e9322bd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 25 Apr 2026 15:54:28 +0300 Subject: [PATCH 0102/5214] iio: tsl2772: Add support for Avago APDS9900/9901 ALS/Proximity sensor The Avago APDS9900/9901 has a similar register layout to the TAOS/AMS TSL2772 but features a unique set of configurations. Add support for the APDS9900/9901 into the TSL2772 driver by adding the required device-specific configurations. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index c8f15ba95267..9ba8140c8bc1 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -127,6 +127,7 @@ enum { tmd2672, tsl2772, tmd2772, + apds9900, apds9930, }; @@ -221,6 +222,12 @@ static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { { 0, 0 }, }; +static const struct tsl2772_lux apds9900_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 52000, 115960 }, + { 36400, 73840 }, + { 0, 0 }, +}; + static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { { 52000, 96824 }, { 38792, 67132 }, @@ -238,6 +245,7 @@ static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { [tmd2672] = tmd2x72_lux_table, [tsl2772] = tsl2x72_lux_table, [tmd2772] = tmd2x72_lux_table, + [apds9900] = apds9900_lux_table, [apds9930] = apds9930_lux_table, }; @@ -289,6 +297,7 @@ static const int tsl2772_int_time_avail[][6] = { [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, + [apds9900] = { 0, 2720, 0, 2720, 0, 696000 }, [apds9930] = { 0, 2730, 0, 2730, 0, 699000 }, }; @@ -316,6 +325,7 @@ static const u8 device_channel_config[] = { [tmd2672] = PRX2, [tsl2772] = ALSPRX2, [tmd2772] = ALSPRX2, + [apds9900] = ALSPRX, [apds9930] = ALSPRX2, }; @@ -530,6 +540,7 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev) case tmd2672: case tsl2772: case tmd2772: + case apds9900: case apds9930: if (!(ret & TSL2772_STA_PRX_VALID)) { ret = -EINVAL; @@ -1367,6 +1378,7 @@ static int tsl2772_device_id_verif(int id, int target) return (id & 0xf0) == TRITON_ID; case tmd2671: case tmd2771: + case apds9900: return (id & 0xf0) == HALIBUT_ID; case tsl2572: case tsl2672: @@ -1898,6 +1910,8 @@ static const struct i2c_device_id tsl2772_idtable[] = { { "tmd2672", tmd2672 }, { "tsl2772", tsl2772 }, { "tmd2772", tmd2772 }, + { "apds9900", apds9900 }, + { "apds9901", apds9900 }, { "apds9930", apds9930 }, { } }; @@ -1915,6 +1929,8 @@ static const struct of_device_id tsl2772_of_match[] = { { .compatible = "amstaos,tmd2672" }, { .compatible = "amstaos,tsl2772" }, { .compatible = "amstaos,tmd2772" }, + { .compatible = "avago,apds9900" }, + { .compatible = "avago,apds9901" }, { .compatible = "avago,apds9930" }, { } }; From 9c482946235362f81bf20da94e1c1f467a03c08f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 25 Apr 2026 15:54:29 +0300 Subject: [PATCH 0103/5214] misc: Remove old APDS990x driver The APDS990x driver in misc lacks DeviceTree support, and no mainline pre-DT board files configured this device using apds990x_platform_data. This driver belongs to a legacy group of ambient light sensor drivers in drivers/misc/ that predates the migration to DT and the standard IIO ABI. Since the Avago APDS9900/9901 ALS/Proximity sensor is now supported by the tsl2772 IIO driver and there are no active users in the kernel tree, remove this old implementation. Acked-by: Greg Kroah-Hartman Signed-off-by: Svyatoslav Ryhel Signed-off-by: Jonathan Cameron --- Documentation/misc-devices/apds990x.rst | 128 --- Documentation/misc-devices/index.rst | 1 - drivers/misc/Kconfig | 10 - drivers/misc/Makefile | 1 - drivers/misc/apds990x.c | 1284 ----------------------- include/linux/platform_data/apds990x.h | 65 -- 6 files changed, 1489 deletions(-) delete mode 100644 Documentation/misc-devices/apds990x.rst delete mode 100644 drivers/misc/apds990x.c delete mode 100644 include/linux/platform_data/apds990x.h diff --git a/Documentation/misc-devices/apds990x.rst b/Documentation/misc-devices/apds990x.rst deleted file mode 100644 index e2f75577f731..000000000000 --- a/Documentation/misc-devices/apds990x.rst +++ /dev/null @@ -1,128 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -====================== -Kernel driver apds990x -====================== - -Supported chips: -Avago APDS990X - -Data sheet: -Not freely available - -Author: -Samu Onkalo - -Description ------------ - -APDS990x is a combined ambient light and proximity sensor. ALS and proximity -functionality are highly connected. ALS measurement path must be running -while the proximity functionality is enabled. - -ALS produces raw measurement values for two channels: Clear channel -(infrared + visible light) and IR only. However, threshold comparisons happen -using clear channel only. Lux value and the threshold level on the HW -might vary quite much depending the spectrum of the light source. - -Driver makes necessary conversions to both directions so that user handles -only lux values. Lux value is calculated using information from the both -channels. HW threshold level is calculated from the given lux value to match -with current type of the lightning. Sometimes inaccuracy of the estimations -lead to false interrupt, but that doesn't harm. - -ALS contains 4 different gain steps. Driver automatically -selects suitable gain step. After each measurement, reliability of the results -is estimated and new measurement is triggered if necessary. - -Platform data can provide tuned values to the conversion formulas if -values are known. Otherwise plain sensor default values are used. - -Proximity side is little bit simpler. There is no need for complex conversions. -It produces directly usable values. - -Driver controls chip operational state using pm_runtime framework. -Voltage regulators are controlled based on chip operational state. - -SYSFS ------ - - -chip_id - RO - shows detected chip type and version - -power_state - RW - enable / disable chip. Uses counting logic - - 1 enables the chip - 0 disables the chip -lux0_input - RO - measured lux value - - sysfs_notify called when threshold interrupt occurs - -lux0_sensor_range - RO - lux0_input max value. - - Actually never reaches since sensor tends - to saturate much before that. Real max value varies depending - on the light spectrum etc. - -lux0_rate - RW - measurement rate in Hz - -lux0_rate_avail - RO - supported measurement rates - -lux0_calibscale - RW - calibration value. - - Set to neutral value by default. - Output results are multiplied with calibscale / calibscale_default - value. - -lux0_calibscale_default - RO - neutral calibration value - -lux0_thresh_above_value - RW - HI level threshold value. - - All results above the value - trigs an interrupt. 65535 (i.e. sensor_range) disables the above - interrupt. - -lux0_thresh_below_value - RW - LO level threshold value. - - All results below the value - trigs an interrupt. 0 disables the below interrupt. - -prox0_raw - RO - measured proximity value - - sysfs_notify called when threshold interrupt occurs - -prox0_sensor_range - RO - prox0_raw max value (1023) - -prox0_raw_en - RW - enable / disable proximity - uses counting logic - - - 1 enables the proximity - - 0 disables the proximity - -prox0_reporting_mode - RW - trigger / periodic. - - In "trigger" mode the driver tells two possible - values: 0 or prox0_sensor_range value. 0 means no proximity, - 1023 means proximity. This causes minimal number of interrupts. - In "periodic" mode the driver reports all values above - prox0_thresh_above. This causes more interrupts, but it can give - _rough_ estimate about the distance. - -prox0_reporting_mode_avail - RO - accepted values to prox0_reporting_mode (trigger, periodic) - -prox0_thresh_above_value - RW - threshold level which trigs proximity events. diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst index 081e79415e38..f911edaecbfa 100644 --- a/Documentation/misc-devices/index.rst +++ b/Documentation/misc-devices/index.rst @@ -13,7 +13,6 @@ fit into other categories. ad525x_dpot amd-sbi - apds990x bh1770glc c2port dw-xdata-pcie diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 00683bf06258..390256ed91f4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -381,16 +381,6 @@ config SENSORS_BH1770 To compile this driver as a module, choose M here: the module will be called bh1770glc. If unsure, say N here. -config SENSORS_APDS990X - tristate "APDS990X combined als and proximity sensors" - depends on I2C - help - Say Y here if you want to build a driver for Avago APDS990x - combined ambient light and proximity sensor chip. - - To compile this driver as a module, choose M here: the - module will be called apds990x. If unsure, say N here. - config HMC6352 tristate "Honeywell HMC6352 compass" depends on I2C diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b32a2597d246..fed47c7672b9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_RPMB) += rpmb-core.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o -obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c deleted file mode 100644 index b69c3a1c94d1..000000000000 --- a/drivers/misc/apds990x.c +++ /dev/null @@ -1,1284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * This file is part of the APDS990x sensor driver. - * Chip is combined proximity and ambient light sensor. - * - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Samu Onkalo - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register map */ -#define APDS990X_ENABLE 0x00 /* Enable of states and interrupts */ -#define APDS990X_ATIME 0x01 /* ALS ADC time */ -#define APDS990X_PTIME 0x02 /* Proximity ADC time */ -#define APDS990X_WTIME 0x03 /* Wait time */ -#define APDS990X_AILTL 0x04 /* ALS interrupt low threshold low byte */ -#define APDS990X_AILTH 0x05 /* ALS interrupt low threshold hi byte */ -#define APDS990X_AIHTL 0x06 /* ALS interrupt hi threshold low byte */ -#define APDS990X_AIHTH 0x07 /* ALS interrupt hi threshold hi byte */ -#define APDS990X_PILTL 0x08 /* Proximity interrupt low threshold low byte */ -#define APDS990X_PILTH 0x09 /* Proximity interrupt low threshold hi byte */ -#define APDS990X_PIHTL 0x0a /* Proximity interrupt hi threshold low byte */ -#define APDS990X_PIHTH 0x0b /* Proximity interrupt hi threshold hi byte */ -#define APDS990X_PERS 0x0c /* Interrupt persistence filters */ -#define APDS990X_CONFIG 0x0d /* Configuration */ -#define APDS990X_PPCOUNT 0x0e /* Proximity pulse count */ -#define APDS990X_CONTROL 0x0f /* Gain control register */ -#define APDS990X_REV 0x11 /* Revision Number */ -#define APDS990X_ID 0x12 /* Device ID */ -#define APDS990X_STATUS 0x13 /* Device status */ -#define APDS990X_CDATAL 0x14 /* Clear ADC low data register */ -#define APDS990X_CDATAH 0x15 /* Clear ADC high data register */ -#define APDS990X_IRDATAL 0x16 /* IR ADC low data register */ -#define APDS990X_IRDATAH 0x17 /* IR ADC high data register */ -#define APDS990X_PDATAL 0x18 /* Proximity ADC low data register */ -#define APDS990X_PDATAH 0x19 /* Proximity ADC high data register */ - -/* Control */ -#define APDS990X_MAX_AGAIN 3 - -/* Enable register */ -#define APDS990X_EN_PIEN (0x1 << 5) -#define APDS990X_EN_AIEN (0x1 << 4) -#define APDS990X_EN_WEN (0x1 << 3) -#define APDS990X_EN_PEN (0x1 << 2) -#define APDS990X_EN_AEN (0x1 << 1) -#define APDS990X_EN_PON (0x1 << 0) -#define APDS990X_EN_DISABLE_ALL 0 - -/* Status register */ -#define APDS990X_ST_PINT (0x1 << 5) -#define APDS990X_ST_AINT (0x1 << 4) - -/* I2C access types */ -#define APDS990x_CMD_TYPE_MASK (0x03 << 5) -#define APDS990x_CMD_TYPE_RB (0x00 << 5) /* Repeated byte */ -#define APDS990x_CMD_TYPE_INC (0x01 << 5) /* Auto increment */ -#define APDS990x_CMD_TYPE_SPE (0x03 << 5) /* Special function */ - -#define APDS990x_ADDR_SHIFT 0 -#define APDS990x_CMD 0x80 - -/* Interrupt ack commands */ -#define APDS990X_INT_ACK_ALS 0x6 -#define APDS990X_INT_ACK_PS 0x5 -#define APDS990X_INT_ACK_BOTH 0x7 - -/* ptime */ -#define APDS990X_PTIME_DEFAULT 0xff /* Recommended conversion time 2.7ms*/ - -/* wtime */ -#define APDS990X_WTIME_DEFAULT 0xee /* ~50ms wait time */ - -#define APDS990X_TIME_TO_ADC 1024 /* One timetick as ADC count value */ - -/* Persistence */ -#define APDS990X_APERS_SHIFT 0 -#define APDS990X_PPERS_SHIFT 4 - -/* Supported ID:s */ -#define APDS990X_ID_0 0x0 -#define APDS990X_ID_4 0x4 -#define APDS990X_ID_29 0x29 - -/* pgain and pdiode settings */ -#define APDS_PGAIN_1X 0x0 -#define APDS_PDIODE_IR 0x2 - -#define APDS990X_LUX_OUTPUT_SCALE 10 - -/* Reverse chip factors for threshold calculation */ -struct reverse_factors { - u32 afactor; - int cf1; - int irf1; - int cf2; - int irf2; -}; - -struct apds990x_chip { - struct apds990x_platform_data *pdata; - struct i2c_client *client; - struct mutex mutex; /* avoid parallel access */ - struct regulator_bulk_data regs[2]; - wait_queue_head_t wait; - - int prox_en; - bool prox_continuous_mode; - bool lux_wait_fresh_res; - - /* Chip parameters */ - struct apds990x_chip_factors cf; - struct reverse_factors rcf; - u16 atime; /* als integration time */ - u16 arate; /* als reporting rate */ - u16 a_max_result; /* Max possible ADC value with current atime */ - u8 again_meas; /* Gain used in last measurement */ - u8 again_next; /* Next calculated gain */ - u8 pgain; - u8 pdiode; - u8 pdrive; - u8 lux_persistence; - u8 prox_persistence; - - u32 lux_raw; - u32 lux; - u16 lux_clear; - u16 lux_ir; - u16 lux_calib; - u32 lux_thres_hi; - u32 lux_thres_lo; - - u32 prox_thres; - u16 prox_data; - u16 prox_calib; - - char chipname[10]; - u8 revision; -}; - -#define APDS_CALIB_SCALER 8192 -#define APDS_LUX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) -#define APDS_PROX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) - -#define APDS_PROX_DEF_THRES 600 -#define APDS_PROX_HYSTERESIS 50 -#define APDS_LUX_DEF_THRES_HI 101 -#define APDS_LUX_DEF_THRES_LO 100 -#define APDS_DEFAULT_PROX_PERS 1 - -#define APDS_TIMEOUT 2000 -#define APDS_STARTUP_DELAY 25000 /* us */ -#define APDS_RANGE 65535 -#define APDS_PROX_RANGE 1023 -#define APDS_LUX_GAIN_LO_LIMIT 100 -#define APDS_LUX_GAIN_LO_LIMIT_STRICT 25 - -#define TIMESTEP 87 /* 2.7ms is about 87 / 32 */ -#define TIME_STEP_SCALER 32 - -#define APDS_LUX_AVERAGING_TIME 50 /* tolerates 50/60Hz ripple */ -#define APDS_LUX_DEFAULT_RATE 200 - -static const u8 again[] = {1, 8, 16, 120}; /* ALS gain steps */ - -/* Following two tables must match i.e 10Hz rate means 1 as persistence value */ -static const u16 arates_hz[] = {10, 5, 2, 1}; -static const u8 apersis[] = {1, 2, 4, 5}; - -/* Regulators */ -static const char reg_vcc[] = "Vdd"; -static const char reg_vled[] = "Vled"; - -static int apds990x_read_byte(struct apds990x_chip *chip, u8 reg, u8 *data) -{ - struct i2c_client *client = chip->client; - s32 ret; - - reg &= ~APDS990x_CMD_TYPE_MASK; - reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; - - ret = i2c_smbus_read_byte_data(client, reg); - *data = ret; - return (int)ret; -} - -static int apds990x_read_word(struct apds990x_chip *chip, u8 reg, u16 *data) -{ - struct i2c_client *client = chip->client; - s32 ret; - - reg &= ~APDS990x_CMD_TYPE_MASK; - reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; - - ret = i2c_smbus_read_word_data(client, reg); - *data = ret; - return (int)ret; -} - -static int apds990x_write_byte(struct apds990x_chip *chip, u8 reg, u8 data) -{ - struct i2c_client *client = chip->client; - s32 ret; - - reg &= ~APDS990x_CMD_TYPE_MASK; - reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; - - ret = i2c_smbus_write_byte_data(client, reg, data); - return (int)ret; -} - -static int apds990x_write_word(struct apds990x_chip *chip, u8 reg, u16 data) -{ - struct i2c_client *client = chip->client; - s32 ret; - - reg &= ~APDS990x_CMD_TYPE_MASK; - reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; - - ret = i2c_smbus_write_word_data(client, reg, data); - return (int)ret; -} - -static int apds990x_mode_on(struct apds990x_chip *chip) -{ - /* ALS is mandatory, proximity optional */ - u8 reg = APDS990X_EN_AIEN | APDS990X_EN_PON | APDS990X_EN_AEN | - APDS990X_EN_WEN; - - if (chip->prox_en) - reg |= APDS990X_EN_PIEN | APDS990X_EN_PEN; - - return apds990x_write_byte(chip, APDS990X_ENABLE, reg); -} - -static u16 apds990x_lux_to_threshold(struct apds990x_chip *chip, u32 lux) -{ - u32 thres; - u32 cpl; - u32 ir; - - if (lux == 0) - return 0; - else if (lux == APDS_RANGE) - return APDS_RANGE; - - /* - * Reported LUX value is a combination of the IR and CLEAR channel - * values. However, interrupt threshold is only for clear channel. - * This function approximates needed HW threshold value for a given - * LUX value in the current lightning type. - * IR level compared to visible light varies heavily depending on the - * source of the light - * - * Calculate threshold value for the next measurement period. - * Math: threshold = lux * cpl where - * cpl = atime * again / (glass_attenuation * device_factor) - * (count-per-lux) - * - * First remove calibration. Division by four is to avoid overflow - */ - lux = lux * (APDS_CALIB_SCALER / 4) / (chip->lux_calib / 4); - - /* Multiplication by 64 is to increase accuracy */ - cpl = ((u32)chip->atime * (u32)again[chip->again_next] * - APDS_PARAM_SCALE * 64) / (chip->cf.ga * chip->cf.df); - - thres = lux * cpl / 64; - /* - * Convert IR light from the latest result to match with - * new gain step. This helps to adapt with the current - * source of light. - */ - ir = (u32)chip->lux_ir * (u32)again[chip->again_next] / - (u32)again[chip->again_meas]; - - /* - * Compensate count with IR light impact - * IAC1 > IAC2 (see apds990x_get_lux for formulas) - */ - if (chip->lux_clear * APDS_PARAM_SCALE >= - chip->rcf.afactor * chip->lux_ir) - thres = (chip->rcf.cf1 * thres + chip->rcf.irf1 * ir) / - APDS_PARAM_SCALE; - else - thres = (chip->rcf.cf2 * thres + chip->rcf.irf2 * ir) / - APDS_PARAM_SCALE; - - if (thres >= chip->a_max_result) - thres = chip->a_max_result - 1; - return thres; -} - -static inline int apds990x_set_atime(struct apds990x_chip *chip, u32 time_ms) -{ - u8 reg_value; - - chip->atime = time_ms; - /* Formula is specified in the data sheet */ - reg_value = 256 - ((time_ms * TIME_STEP_SCALER) / TIMESTEP); - /* Calculate max ADC value for given integration time */ - chip->a_max_result = (u16)(256 - reg_value) * APDS990X_TIME_TO_ADC; - return apds990x_write_byte(chip, APDS990X_ATIME, reg_value); -} - -/* Called always with mutex locked */ -static int apds990x_refresh_pthres(struct apds990x_chip *chip, int data) -{ - int ret, lo, hi; - - /* If the chip is not in use, don't try to access it */ - if (pm_runtime_suspended(&chip->client->dev)) - return 0; - - if (data < chip->prox_thres) { - lo = 0; - hi = chip->prox_thres; - } else { - lo = chip->prox_thres - APDS_PROX_HYSTERESIS; - if (chip->prox_continuous_mode) - hi = chip->prox_thres; - else - hi = APDS_RANGE; - } - - ret = apds990x_write_word(chip, APDS990X_PILTL, lo); - ret |= apds990x_write_word(chip, APDS990X_PIHTL, hi); - return ret; -} - -/* Called always with mutex locked */ -static int apds990x_refresh_athres(struct apds990x_chip *chip) -{ - int ret; - /* If the chip is not in use, don't try to access it */ - if (pm_runtime_suspended(&chip->client->dev)) - return 0; - - ret = apds990x_write_word(chip, APDS990X_AILTL, - apds990x_lux_to_threshold(chip, chip->lux_thres_lo)); - ret |= apds990x_write_word(chip, APDS990X_AIHTL, - apds990x_lux_to_threshold(chip, chip->lux_thres_hi)); - - return ret; -} - -/* Called always with mutex locked */ -static void apds990x_force_a_refresh(struct apds990x_chip *chip) -{ - /* This will force ALS interrupt after the next measurement. */ - apds990x_write_word(chip, APDS990X_AILTL, APDS_LUX_DEF_THRES_LO); - apds990x_write_word(chip, APDS990X_AIHTL, APDS_LUX_DEF_THRES_HI); -} - -/* Called always with mutex locked */ -static void apds990x_force_p_refresh(struct apds990x_chip *chip) -{ - /* This will force proximity interrupt after the next measurement. */ - apds990x_write_word(chip, APDS990X_PILTL, APDS_PROX_DEF_THRES - 1); - apds990x_write_word(chip, APDS990X_PIHTL, APDS_PROX_DEF_THRES); -} - -/* Called always with mutex locked */ -static int apds990x_calc_again(struct apds990x_chip *chip) -{ - int curr_again = chip->again_meas; - int next_again = chip->again_meas; - int ret = 0; - - /* Calculate suitable als gain */ - if (chip->lux_clear == chip->a_max_result) - next_again -= 2; /* ALS saturated. Decrease gain by 2 steps */ - else if (chip->lux_clear > chip->a_max_result / 2) - next_again--; - else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) - next_again += 2; /* Too dark. Increase gain by 2 steps */ - else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT) - next_again++; - - /* Limit gain to available range */ - if (next_again < 0) - next_again = 0; - else if (next_again > APDS990X_MAX_AGAIN) - next_again = APDS990X_MAX_AGAIN; - - /* Let's check can we trust the measured result */ - if (chip->lux_clear == chip->a_max_result) - /* Result can be totally garbage due to saturation */ - ret = -ERANGE; - else if (next_again != curr_again && - chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) - /* - * Gain is changed and measurement result is very small. - * Result can be totally garbage due to underflow - */ - ret = -ERANGE; - - chip->again_next = next_again; - apds990x_write_byte(chip, APDS990X_CONTROL, - (chip->pdrive << 6) | - (chip->pdiode << 4) | - (chip->pgain << 2) | - (chip->again_next << 0)); - - /* - * Error means bad result -> re-measurement is needed. The forced - * refresh uses fastest possible persistence setting to get result - * as soon as possible. - */ - if (ret < 0) - apds990x_force_a_refresh(chip); - else - apds990x_refresh_athres(chip); - - return ret; -} - -/* Called always with mutex locked */ -static int apds990x_get_lux(struct apds990x_chip *chip, int clear, int ir) -{ - int iac, iac1, iac2; /* IR adjusted counts */ - u32 lpc; /* Lux per count */ - - /* Formulas: - * iac1 = CF1 * CLEAR_CH - IRF1 * IR_CH - * iac2 = CF2 * CLEAR_CH - IRF2 * IR_CH - */ - iac1 = (chip->cf.cf1 * clear - chip->cf.irf1 * ir) / APDS_PARAM_SCALE; - iac2 = (chip->cf.cf2 * clear - chip->cf.irf2 * ir) / APDS_PARAM_SCALE; - - iac = max(iac1, iac2); - iac = max(iac, 0); - - lpc = APDS990X_LUX_OUTPUT_SCALE * (chip->cf.df * chip->cf.ga) / - (u32)(again[chip->again_meas] * (u32)chip->atime); - - return (iac * lpc) / APDS_PARAM_SCALE; -} - -static int apds990x_ack_int(struct apds990x_chip *chip, u8 mode) -{ - struct i2c_client *client = chip->client; - s32 ret; - u8 reg = APDS990x_CMD | APDS990x_CMD_TYPE_SPE; - - switch (mode & (APDS990X_ST_AINT | APDS990X_ST_PINT)) { - case APDS990X_ST_AINT: - reg |= APDS990X_INT_ACK_ALS; - break; - case APDS990X_ST_PINT: - reg |= APDS990X_INT_ACK_PS; - break; - default: - reg |= APDS990X_INT_ACK_BOTH; - break; - } - - ret = i2c_smbus_read_byte_data(client, reg); - return (int)ret; -} - -static irqreturn_t apds990x_irq(int irq, void *data) -{ - struct apds990x_chip *chip = data; - u8 status; - - apds990x_read_byte(chip, APDS990X_STATUS, &status); - apds990x_ack_int(chip, status); - - mutex_lock(&chip->mutex); - if (!pm_runtime_suspended(&chip->client->dev)) { - if (status & APDS990X_ST_AINT) { - apds990x_read_word(chip, APDS990X_CDATAL, - &chip->lux_clear); - apds990x_read_word(chip, APDS990X_IRDATAL, - &chip->lux_ir); - /* Store used gain for calculations */ - chip->again_meas = chip->again_next; - - chip->lux_raw = apds990x_get_lux(chip, - chip->lux_clear, - chip->lux_ir); - - if (apds990x_calc_again(chip) == 0) { - /* Result is valid */ - chip->lux = chip->lux_raw; - chip->lux_wait_fresh_res = false; - wake_up(&chip->wait); - sysfs_notify(&chip->client->dev.kobj, - NULL, "lux0_input"); - } - } - - if ((status & APDS990X_ST_PINT) && chip->prox_en) { - u16 clr_ch; - - apds990x_read_word(chip, APDS990X_CDATAL, &clr_ch); - /* - * If ALS channel is saturated at min gain, - * proximity gives false posivite values. - * Just ignore them. - */ - if (chip->again_meas == 0 && - clr_ch == chip->a_max_result) - chip->prox_data = 0; - else - apds990x_read_word(chip, - APDS990X_PDATAL, - &chip->prox_data); - - apds990x_refresh_pthres(chip, chip->prox_data); - if (chip->prox_data < chip->prox_thres) - chip->prox_data = 0; - else if (!chip->prox_continuous_mode) - chip->prox_data = APDS_PROX_RANGE; - sysfs_notify(&chip->client->dev.kobj, - NULL, "prox0_raw"); - } - } - mutex_unlock(&chip->mutex); - return IRQ_HANDLED; -} - -static int apds990x_configure(struct apds990x_chip *chip) -{ - /* It is recommended to use disabled mode during these operations */ - apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); - - /* conversion and wait times for different state machince states */ - apds990x_write_byte(chip, APDS990X_PTIME, APDS990X_PTIME_DEFAULT); - apds990x_write_byte(chip, APDS990X_WTIME, APDS990X_WTIME_DEFAULT); - apds990x_set_atime(chip, APDS_LUX_AVERAGING_TIME); - - apds990x_write_byte(chip, APDS990X_CONFIG, 0); - - /* Persistence levels */ - apds990x_write_byte(chip, APDS990X_PERS, - (chip->lux_persistence << APDS990X_APERS_SHIFT) | - (chip->prox_persistence << APDS990X_PPERS_SHIFT)); - - apds990x_write_byte(chip, APDS990X_PPCOUNT, chip->pdata->ppcount); - - /* Start with relatively small gain */ - chip->again_meas = 1; - chip->again_next = 1; - apds990x_write_byte(chip, APDS990X_CONTROL, - (chip->pdrive << 6) | - (chip->pdiode << 4) | - (chip->pgain << 2) | - (chip->again_next << 0)); - return 0; -} - -static int apds990x_detect(struct apds990x_chip *chip) -{ - struct i2c_client *client = chip->client; - int ret; - u8 id; - - ret = apds990x_read_byte(chip, APDS990X_ID, &id); - if (ret < 0) { - dev_err(&client->dev, "ID read failed\n"); - return ret; - } - - ret = apds990x_read_byte(chip, APDS990X_REV, &chip->revision); - if (ret < 0) { - dev_err(&client->dev, "REV read failed\n"); - return ret; - } - - switch (id) { - case APDS990X_ID_0: - case APDS990X_ID_4: - case APDS990X_ID_29: - snprintf(chip->chipname, sizeof(chip->chipname), "APDS-990x"); - break; - default: - ret = -ENODEV; - break; - } - return ret; -} - -#ifdef CONFIG_PM -static int apds990x_chip_on(struct apds990x_chip *chip) -{ - int err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), - chip->regs); - if (err < 0) - return err; - - usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); - - /* Refresh all configs in case of regulators were off */ - chip->prox_data = 0; - apds990x_configure(chip); - apds990x_mode_on(chip); - return 0; -} -#endif - -static int apds990x_chip_off(struct apds990x_chip *chip) -{ - apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); - regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); - return 0; -} - -static ssize_t apds990x_lux_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - ssize_t ret; - u32 result; - long time_left; - - if (pm_runtime_suspended(dev)) - return -EIO; - - time_left = wait_event_interruptible_timeout(chip->wait, - !chip->lux_wait_fresh_res, - msecs_to_jiffies(APDS_TIMEOUT)); - if (!time_left) - return -EIO; - - mutex_lock(&chip->mutex); - result = (chip->lux * chip->lux_calib) / APDS_CALIB_SCALER; - if (result > (APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE)) - result = APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE; - - ret = sprintf(buf, "%d.%d\n", - result / APDS990X_LUX_OUTPUT_SCALE, - result % APDS990X_LUX_OUTPUT_SCALE); - mutex_unlock(&chip->mutex); - return ret; -} - -static DEVICE_ATTR(lux0_input, S_IRUGO, apds990x_lux_show, NULL); - -static ssize_t apds990x_lux_range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", APDS_RANGE); -} - -static DEVICE_ATTR(lux0_sensor_range, S_IRUGO, apds990x_lux_range_show, NULL); - -static ssize_t apds990x_lux_calib_format_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", APDS_CALIB_SCALER); -} - -static DEVICE_ATTR(lux0_calibscale_default, S_IRUGO, - apds990x_lux_calib_format_show, NULL); - -static ssize_t apds990x_lux_calib_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", chip->lux_calib); -} - -static ssize_t apds990x_lux_calib_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - unsigned long value; - int ret; - - ret = kstrtoul(buf, 0, &value); - if (ret) - return ret; - - chip->lux_calib = value; - - return len; -} - -static DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, apds990x_lux_calib_show, - apds990x_lux_calib_store); - -static ssize_t apds990x_rate_avail(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i; - int pos = 0; - - for (i = 0; i < ARRAY_SIZE(arates_hz); i++) - pos += sprintf(buf + pos, "%d ", arates_hz[i]); - sprintf(buf + pos - 1, "\n"); - return pos; -} - -static ssize_t apds990x_rate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", chip->arate); -} - -static int apds990x_set_arate(struct apds990x_chip *chip, int rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(arates_hz); i++) - if (rate >= arates_hz[i]) - break; - - if (i == ARRAY_SIZE(arates_hz)) - return -EINVAL; - - /* Pick up corresponding persistence value */ - chip->lux_persistence = apersis[i]; - chip->arate = arates_hz[i]; - - /* If the chip is not in use, don't try to access it */ - if (pm_runtime_suspended(&chip->client->dev)) - return 0; - - /* Persistence levels */ - return apds990x_write_byte(chip, APDS990X_PERS, - (chip->lux_persistence << APDS990X_APERS_SHIFT) | - (chip->prox_persistence << APDS990X_PPERS_SHIFT)); -} - -static ssize_t apds990x_rate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - unsigned long value; - int ret; - - ret = kstrtoul(buf, 0, &value); - if (ret) - return ret; - - mutex_lock(&chip->mutex); - ret = apds990x_set_arate(chip, value); - mutex_unlock(&chip->mutex); - - if (ret < 0) - return ret; - return len; -} - -static DEVICE_ATTR(lux0_rate_avail, S_IRUGO, apds990x_rate_avail, NULL); - -static DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, apds990x_rate_show, - apds990x_rate_store); - -static ssize_t apds990x_prox_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - ssize_t ret; - struct apds990x_chip *chip = dev_get_drvdata(dev); - - if (pm_runtime_suspended(dev) || !chip->prox_en) - return -EIO; - - mutex_lock(&chip->mutex); - ret = sprintf(buf, "%d\n", chip->prox_data); - mutex_unlock(&chip->mutex); - return ret; -} - -static DEVICE_ATTR(prox0_raw, S_IRUGO, apds990x_prox_show, NULL); - -static ssize_t apds990x_prox_range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", APDS_PROX_RANGE); -} - -static DEVICE_ATTR(prox0_sensor_range, S_IRUGO, apds990x_prox_range_show, NULL); - -static ssize_t apds990x_prox_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", chip->prox_en); -} - -static ssize_t apds990x_prox_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - unsigned long value; - int ret; - - ret = kstrtoul(buf, 0, &value); - if (ret) - return ret; - - mutex_lock(&chip->mutex); - - if (!chip->prox_en) - chip->prox_data = 0; - - if (value) - chip->prox_en++; - else if (chip->prox_en > 0) - chip->prox_en--; - - if (!pm_runtime_suspended(dev)) - apds990x_mode_on(chip); - mutex_unlock(&chip->mutex); - return len; -} - -static DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, apds990x_prox_enable_show, - apds990x_prox_enable_store); - -static const char *reporting_modes[] = {"trigger", "periodic"}; - -static ssize_t apds990x_prox_reporting_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", - reporting_modes[!!chip->prox_continuous_mode]); -} - -static ssize_t apds990x_prox_reporting_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - int ret; - - ret = sysfs_match_string(reporting_modes, buf); - if (ret < 0) - return ret; - - chip->prox_continuous_mode = ret; - return len; -} - -static DEVICE_ATTR(prox0_reporting_mode, S_IRUGO | S_IWUSR, - apds990x_prox_reporting_mode_show, - apds990x_prox_reporting_mode_store); - -static ssize_t apds990x_prox_reporting_avail_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%s %s\n", reporting_modes[0], reporting_modes[1]); -} - -static DEVICE_ATTR(prox0_reporting_mode_avail, S_IRUGO | S_IWUSR, - apds990x_prox_reporting_avail_show, NULL); - - -static ssize_t apds990x_lux_thresh_above_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", chip->lux_thres_hi); -} - -static ssize_t apds990x_lux_thresh_below_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", chip->lux_thres_lo); -} - -static ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target, - const char *buf) -{ - unsigned long thresh; - int ret; - - ret = kstrtoul(buf, 0, &thresh); - if (ret) - return ret; - - if (thresh > APDS_RANGE) - return -EINVAL; - - mutex_lock(&chip->mutex); - *target = thresh; - /* - * Don't update values in HW if we are still waiting for - * first interrupt to come after device handle open call. - */ - if (!chip->lux_wait_fresh_res) - apds990x_refresh_athres(chip); - mutex_unlock(&chip->mutex); - return ret; - -} - -static ssize_t apds990x_lux_thresh_above_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_hi, buf); - - if (ret < 0) - return ret; - return len; -} - -static ssize_t apds990x_lux_thresh_below_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_lo, buf); - - if (ret < 0) - return ret; - return len; -} - -static DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR, - apds990x_lux_thresh_above_show, - apds990x_lux_thresh_above_store); - -static DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR, - apds990x_lux_thresh_below_show, - apds990x_lux_thresh_below_store); - -static ssize_t apds990x_prox_threshold_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", chip->prox_thres); -} - -static ssize_t apds990x_prox_threshold_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - unsigned long value; - int ret; - - ret = kstrtoul(buf, 0, &value); - if (ret) - return ret; - - if ((value > APDS_RANGE) || (value == 0) || - (value < APDS_PROX_HYSTERESIS)) - return -EINVAL; - - mutex_lock(&chip->mutex); - chip->prox_thres = value; - - apds990x_force_p_refresh(chip); - mutex_unlock(&chip->mutex); - return len; -} - -static DEVICE_ATTR(prox0_thresh_above_value, S_IRUGO | S_IWUSR, - apds990x_prox_threshold_show, - apds990x_prox_threshold_store); - -static ssize_t apds990x_power_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", !pm_runtime_suspended(dev)); -} - -static ssize_t apds990x_power_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - unsigned long value; - int ret; - - ret = kstrtoul(buf, 0, &value); - if (ret) - return ret; - - if (value) { - pm_runtime_get_sync(dev); - mutex_lock(&chip->mutex); - chip->lux_wait_fresh_res = true; - apds990x_force_a_refresh(chip); - apds990x_force_p_refresh(chip); - mutex_unlock(&chip->mutex); - } else { - if (!pm_runtime_suspended(dev)) - pm_runtime_put(dev); - } - return len; -} - -static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, - apds990x_power_state_show, - apds990x_power_state_store); - -static ssize_t apds990x_chip_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct apds990x_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%s %d\n", chip->chipname, chip->revision); -} - -static DEVICE_ATTR(chip_id, S_IRUGO, apds990x_chip_id_show, NULL); - -static struct attribute *sysfs_attrs_ctrl[] = { - &dev_attr_lux0_calibscale.attr, - &dev_attr_lux0_calibscale_default.attr, - &dev_attr_lux0_input.attr, - &dev_attr_lux0_sensor_range.attr, - &dev_attr_lux0_rate.attr, - &dev_attr_lux0_rate_avail.attr, - &dev_attr_lux0_thresh_above_value.attr, - &dev_attr_lux0_thresh_below_value.attr, - &dev_attr_prox0_raw_en.attr, - &dev_attr_prox0_raw.attr, - &dev_attr_prox0_sensor_range.attr, - &dev_attr_prox0_thresh_above_value.attr, - &dev_attr_prox0_reporting_mode.attr, - &dev_attr_prox0_reporting_mode_avail.attr, - &dev_attr_chip_id.attr, - &dev_attr_power_state.attr, - NULL -}; - -static const struct attribute_group apds990x_attribute_group[] = { - {.attrs = sysfs_attrs_ctrl }, -}; - -static int apds990x_probe(struct i2c_client *client) -{ - struct apds990x_chip *chip; - int err; - - chip = kzalloc_obj(*chip); - if (!chip) - return -ENOMEM; - - i2c_set_clientdata(client, chip); - chip->client = client; - - init_waitqueue_head(&chip->wait); - mutex_init(&chip->mutex); - chip->pdata = client->dev.platform_data; - - if (chip->pdata == NULL) { - dev_err(&client->dev, "platform data is mandatory\n"); - err = -EINVAL; - goto fail1; - } - - if (chip->pdata->cf.ga == 0) { - /* set uncovered sensor default parameters */ - chip->cf.ga = 1966; /* 0.48 * APDS_PARAM_SCALE */ - chip->cf.cf1 = 4096; /* 1.00 * APDS_PARAM_SCALE */ - chip->cf.irf1 = 9134; /* 2.23 * APDS_PARAM_SCALE */ - chip->cf.cf2 = 2867; /* 0.70 * APDS_PARAM_SCALE */ - chip->cf.irf2 = 5816; /* 1.42 * APDS_PARAM_SCALE */ - chip->cf.df = 52; - } else { - chip->cf = chip->pdata->cf; - } - - /* precalculate inverse chip factors for threshold control */ - chip->rcf.afactor = - (chip->cf.irf1 - chip->cf.irf2) * APDS_PARAM_SCALE / - (chip->cf.cf1 - chip->cf.cf2); - chip->rcf.cf1 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / - chip->cf.cf1; - chip->rcf.irf1 = chip->cf.irf1 * APDS_PARAM_SCALE / - chip->cf.cf1; - chip->rcf.cf2 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / - chip->cf.cf2; - chip->rcf.irf2 = chip->cf.irf2 * APDS_PARAM_SCALE / - chip->cf.cf2; - - /* Set something to start with */ - chip->lux_thres_hi = APDS_LUX_DEF_THRES_HI; - chip->lux_thres_lo = APDS_LUX_DEF_THRES_LO; - chip->lux_calib = APDS_LUX_NEUTRAL_CALIB_VALUE; - - chip->prox_thres = APDS_PROX_DEF_THRES; - chip->pdrive = chip->pdata->pdrive; - chip->pdiode = APDS_PDIODE_IR; - chip->pgain = APDS_PGAIN_1X; - chip->prox_calib = APDS_PROX_NEUTRAL_CALIB_VALUE; - chip->prox_persistence = APDS_DEFAULT_PROX_PERS; - chip->prox_continuous_mode = false; - - chip->regs[0].supply = reg_vcc; - chip->regs[1].supply = reg_vled; - - err = regulator_bulk_get(&client->dev, - ARRAY_SIZE(chip->regs), chip->regs); - if (err < 0) { - dev_err(&client->dev, "Cannot get regulators\n"); - goto fail1; - } - - err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), chip->regs); - if (err < 0) { - dev_err(&client->dev, "Cannot enable regulators\n"); - goto fail2; - } - - usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); - - err = apds990x_detect(chip); - if (err < 0) { - dev_err(&client->dev, "APDS990X not found\n"); - goto fail3; - } - - pm_runtime_set_active(&client->dev); - - apds990x_configure(chip); - apds990x_set_arate(chip, APDS_LUX_DEFAULT_RATE); - apds990x_mode_on(chip); - - pm_runtime_enable(&client->dev); - - if (chip->pdata->setup_resources) { - err = chip->pdata->setup_resources(); - if (err) { - err = -EINVAL; - goto fail4; - } - } - - err = sysfs_create_group(&chip->client->dev.kobj, - apds990x_attribute_group); - if (err < 0) { - dev_err(&chip->client->dev, "Sysfs registration failed\n"); - goto fail5; - } - - err = request_threaded_irq(client->irq, NULL, - apds990x_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW | - IRQF_ONESHOT, - "apds990x", chip); - if (err) { - dev_err(&client->dev, "could not get IRQ %d\n", - client->irq); - goto fail6; - } - return err; -fail6: - sysfs_remove_group(&chip->client->dev.kobj, - &apds990x_attribute_group[0]); -fail5: - if (chip->pdata && chip->pdata->release_resources) - chip->pdata->release_resources(); -fail4: - pm_runtime_disable(&client->dev); -fail3: - regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); -fail2: - regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); -fail1: - kfree(chip); - return err; -} - -static void apds990x_remove(struct i2c_client *client) -{ - struct apds990x_chip *chip = i2c_get_clientdata(client); - - free_irq(client->irq, chip); - sysfs_remove_group(&chip->client->dev.kobj, - apds990x_attribute_group); - - if (chip->pdata && chip->pdata->release_resources) - chip->pdata->release_resources(); - - if (!pm_runtime_suspended(&client->dev)) - apds990x_chip_off(chip); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); - - kfree(chip); -} - -#ifdef CONFIG_PM_SLEEP -static int apds990x_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct apds990x_chip *chip = i2c_get_clientdata(client); - - apds990x_chip_off(chip); - return 0; -} - -static int apds990x_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct apds990x_chip *chip = i2c_get_clientdata(client); - - /* - * If we were enabled at suspend time, it is expected - * everything works nice and smoothly. Chip_on is enough - */ - apds990x_chip_on(chip); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static int apds990x_runtime_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct apds990x_chip *chip = i2c_get_clientdata(client); - - apds990x_chip_off(chip); - return 0; -} - -static int apds990x_runtime_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct apds990x_chip *chip = i2c_get_clientdata(client); - - apds990x_chip_on(chip); - return 0; -} - -#endif - -static const struct i2c_device_id apds990x_id[] = { - { "apds990x" }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, apds990x_id); - -static const struct dev_pm_ops apds990x_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(apds990x_suspend, apds990x_resume) - SET_RUNTIME_PM_OPS(apds990x_runtime_suspend, - apds990x_runtime_resume, - NULL) -}; - -static struct i2c_driver apds990x_driver = { - .driver = { - .name = "apds990x", - .pm = &apds990x_pm_ops, - }, - .probe = apds990x_probe, - .remove = apds990x_remove, - .id_table = apds990x_id, -}; - -module_i2c_driver(apds990x_driver); - -MODULE_DESCRIPTION("APDS990X combined ALS and proximity sensor"); -MODULE_AUTHOR("Samu Onkalo, Nokia Corporation"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/apds990x.h b/include/linux/platform_data/apds990x.h deleted file mode 100644 index 37684f68c04f..000000000000 --- a/include/linux/platform_data/apds990x.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * This file is part of the APDS990x sensor driver. - * Chip is combined proximity and ambient light sensor. - * - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Samu Onkalo - */ - -#ifndef __APDS990X_H__ -#define __APDS990X_H__ - - -#define APDS_IRLED_CURR_12mA 0x3 -#define APDS_IRLED_CURR_25mA 0x2 -#define APDS_IRLED_CURR_50mA 0x1 -#define APDS_IRLED_CURR_100mA 0x0 - -/** - * struct apds990x_chip_factors - defines effect of the cover window - * @ga: Total glass attenuation - * @cf1: clear channel factor 1 for raw to lux conversion - * @irf1: IR channel factor 1 for raw to lux conversion - * @cf2: clear channel factor 2 for raw to lux conversion - * @irf2: IR channel factor 2 for raw to lux conversion - * @df: device factor for conversion formulas - * - * Structure for tuning ALS calculation to match with environment. - * Values depend on the material above the sensor and the sensor - * itself. If the GA is zero, driver will use uncovered sensor default values - * format: decimal value * APDS_PARAM_SCALE except df which is plain integer. - */ -struct apds990x_chip_factors { - int ga; - int cf1; - int irf1; - int cf2; - int irf2; - int df; -}; -#define APDS_PARAM_SCALE 4096 - -/** - * struct apds990x_platform_data - platform data for apsd990x.c driver - * @cf: chip factor data - * @pdrive: IR-led driving current - * @ppcount: number of IR pulses used for proximity estimation - * @setup_resources: interrupt line setup call back function - * @release_resources: interrupt line release call back function - * - * Proximity detection result depends heavily on correct ppcount, pdrive - * and cover window. - * - */ - -struct apds990x_platform_data { - struct apds990x_chip_factors cf; - u8 pdrive; - u8 ppcount; - int (*setup_resources)(void); - int (*release_resources)(void); -}; - -#endif From 29df31ae3e8a0152dd8e8c2376816aad2f233473 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Apr 2026 19:24:52 +0100 Subject: [PATCH 0104/5214] pinctrl: renesas: rzg2l: Add SR register cache for PM suspend/resume Include the SR (Slew Rate) register in the PM suspend/resume register cache. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260413182456.811543-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 38 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 1c6b115e65d8..f5f645ba92e0 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -322,6 +322,7 @@ struct rzg2l_pinctrl_pin_settings { * @pupd: PUPD registers cache * @ien: IEN registers cache * @smt: SMT registers cache + * @sr: SR registers cache * @sd_ch: SD_CH registers cache * @eth_poc: ET_POC registers cache * @oen: Output Enable register cache @@ -336,6 +337,7 @@ struct rzg2l_pinctrl_reg_cache { u32 *ien[2]; u32 *pupd[2]; u32 *smt[2]; + u32 *sr[2]; u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; @@ -2760,6 +2762,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) if (!cache->smt[i]) return -ENOMEM; + cache->sr[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->sr[i]), + GFP_KERNEL); + if (!cache->sr[i]) + return -ENOMEM; + /* Allocate dedicated cache. */ dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, sizeof(*dedicated_cache->iolh[i]), @@ -2772,6 +2779,12 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) GFP_KERNEL); if (!dedicated_cache->ien[i]) return -ENOMEM; + + dedicated_cache->sr[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->sr[i]), + GFP_KERNEL); + if (!dedicated_cache->sr[i]) + return -ENOMEM; } pctrl->cache = cache; @@ -3003,7 +3016,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; for (u32 port = 0; port < nports; port++) { - bool has_iolh, has_ien, has_pupd, has_smt; + bool has_iolh, has_ien, has_pupd, has_smt, has_sr; u32 off, caps; u8 pincnt; u64 cfg; @@ -3024,6 +3037,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen has_ien = !!(caps & PIN_CFG_IEN); has_pupd = !!(caps & PIN_CFG_PUPD); has_smt = !!(caps & PIN_CFG_SMT); + has_sr = !!(caps & PIN_CFG_SR); if (suspend) RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PFC(off), cache->pfc[port]); @@ -3075,6 +3089,15 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen cache->smt[1][port]); } } + + if (has_sr) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SR(off), + cache->sr[0][port]); + if (pincnt >= 4) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SR(off) + 4, + cache->sr[1][port]); + } + } } } @@ -3089,7 +3112,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b * port offset are close together. */ for (i = 0, caps = 0; i < pctrl->data->n_dedicated_pins; i++) { - bool has_iolh, has_ien; + bool has_iolh, has_ien, has_sr; u32 off, next_off = 0; u64 cfg, next_cfg; u8 pincnt; @@ -3110,6 +3133,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b /* And apply them in a single shot. */ has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)); has_ien = !!(caps & PIN_CFG_IEN); + has_sr = !!(caps & PIN_CFG_SR); pincnt = hweight8(FIELD_GET(RZG2L_SINGLE_PIN_BITS_MASK, cfg)); if (has_iolh) { @@ -3120,7 +3144,10 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IEN(off), cache->ien[0][i]); } - + if (has_sr) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SR(off), + cache->sr[0][i]); + } if (pincnt >= 4) { if (has_iolh) { RZG2L_PCTRL_REG_ACCESS32(suspend, @@ -3132,6 +3159,11 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b pctrl->base + IEN(off) + 4, cache->ien[1][i]); } + if (has_sr) { + RZG2L_PCTRL_REG_ACCESS32(suspend, + pctrl->base + SR(off) + 4, + cache->sr[1][i]); + } } caps = 0; } From d0fc9f8eb2ce55eb00dbfdc0f19c844df5aee5b8 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Apr 2026 19:24:53 +0100 Subject: [PATCH 0105/5214] pinctrl: renesas: rzg2l: Handle RZ/V2H(P) IOLH configuration in PM cache Include PIN_CFG_IOLH_RZV2H in the IOLH capability checks when saving and restoring pin configuration registers. On RZ/V2H(P), RZ/V2N, and RZ/G3E, the IOLH configuration is defined by the PIN_CFG_IOLH_RZV2H capability. The previous implementation did not account for this, causing the IOLH registers to be skipped during PM save/restore. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260413182456.811543-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index f5f645ba92e0..164429ac20d9 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -3131,7 +3131,8 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b } /* And apply them in a single shot. */ - has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)); + has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | + PIN_CFG_IOLH_C | PIN_CFG_IOLH_RZV2H)); has_ien = !!(caps & PIN_CFG_IEN); has_sr = !!(caps & PIN_CFG_SR); pincnt = hweight8(FIELD_GET(RZG2L_SINGLE_PIN_BITS_MASK, cfg)); From 25e71db480358c1993f91e69078cb5d9b26cd705 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Apr 2026 19:24:54 +0100 Subject: [PATCH 0106/5214] pinctrl: renesas: rzg2l: Add NOD register cache for PM suspend/resume Include the NOD (N-ch Open Drain) register in the PM suspend/resume register cache. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260413182456.811543-5-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 37 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 164429ac20d9..bcedc79d80da 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -323,6 +323,7 @@ struct rzg2l_pinctrl_pin_settings { * @ien: IEN registers cache * @smt: SMT registers cache * @sr: SR registers cache + * @nod: NOD registers cache * @sd_ch: SD_CH registers cache * @eth_poc: ET_POC registers cache * @oen: Output Enable register cache @@ -338,6 +339,7 @@ struct rzg2l_pinctrl_reg_cache { u32 *pupd[2]; u32 *smt[2]; u32 *sr[2]; + u32 *nod[2]; u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; @@ -2767,6 +2769,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) if (!cache->sr[i]) return -ENOMEM; + cache->nod[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->nod[i]), + GFP_KERNEL); + if (!cache->nod[i]) + return -ENOMEM; + /* Allocate dedicated cache. */ dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, sizeof(*dedicated_cache->iolh[i]), @@ -2785,6 +2792,12 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) GFP_KERNEL); if (!dedicated_cache->sr[i]) return -ENOMEM; + + dedicated_cache->nod[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->nod[i]), + GFP_KERNEL); + if (!dedicated_cache->nod[i]) + return -ENOMEM; } pctrl->cache = cache; @@ -3016,7 +3029,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; for (u32 port = 0; port < nports; port++) { - bool has_iolh, has_ien, has_pupd, has_smt, has_sr; + bool has_iolh, has_ien, has_pupd, has_smt, has_sr, has_nod; u32 off, caps; u8 pincnt; u64 cfg; @@ -3038,6 +3051,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen has_pupd = !!(caps & PIN_CFG_PUPD); has_smt = !!(caps & PIN_CFG_SMT); has_sr = !!(caps & PIN_CFG_SR); + has_nod = !!(caps & PIN_CFG_NOD); if (suspend) RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PFC(off), cache->pfc[port]); @@ -3098,6 +3112,15 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen cache->sr[1][port]); } } + + if (has_nod) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + NOD(off), + cache->nod[0][port]); + if (pincnt >= 4) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + NOD(off) + 4, + cache->nod[1][port]); + } + } } } @@ -3112,7 +3135,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b * port offset are close together. */ for (i = 0, caps = 0; i < pctrl->data->n_dedicated_pins; i++) { - bool has_iolh, has_ien, has_sr; + bool has_iolh, has_ien, has_sr, has_nod; u32 off, next_off = 0; u64 cfg, next_cfg; u8 pincnt; @@ -3135,6 +3158,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b PIN_CFG_IOLH_C | PIN_CFG_IOLH_RZV2H)); has_ien = !!(caps & PIN_CFG_IEN); has_sr = !!(caps & PIN_CFG_SR); + has_nod = !!(caps & PIN_CFG_NOD); pincnt = hweight8(FIELD_GET(RZG2L_SINGLE_PIN_BITS_MASK, cfg)); if (has_iolh) { @@ -3149,6 +3173,10 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SR(off), cache->sr[0][i]); } + if (has_nod) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + NOD(off), + cache->nod[0][i]); + } if (pincnt >= 4) { if (has_iolh) { RZG2L_PCTRL_REG_ACCESS32(suspend, @@ -3165,6 +3193,11 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b pctrl->base + SR(off) + 4, cache->sr[1][i]); } + if (has_nod) { + RZG2L_PCTRL_REG_ACCESS32(suspend, + pctrl->base + NOD(off) + 4, + cache->nod[1][i]); + } } caps = 0; } From eea549769cb7cae28cc3158befa2de1b7e92bf58 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Apr 2026 19:24:55 +0100 Subject: [PATCH 0107/5214] pinctrl: renesas: rzg2l: Handle PUPD for RZ/V2H(P) dedicated pins in PM On RZ/V2H(P), dedicated pins support pull-up/pull-down configuration via PIN_CFG_PUPD. Add PUPD handling for dedicated pins in the PM save/restore path. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260413182456.811543-6-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index bcedc79d80da..bc2154b69514 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -2798,6 +2798,12 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) GFP_KERNEL); if (!dedicated_cache->nod[i]) return -ENOMEM; + + dedicated_cache->pupd[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->pupd[i]), + GFP_KERNEL); + if (!dedicated_cache->pupd[i]) + return -ENOMEM; } pctrl->cache = cache; @@ -3135,7 +3141,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b * port offset are close together. */ for (i = 0, caps = 0; i < pctrl->data->n_dedicated_pins; i++) { - bool has_iolh, has_ien, has_sr, has_nod; + bool has_iolh, has_ien, has_sr, has_nod, has_pupd; u32 off, next_off = 0; u64 cfg, next_cfg; u8 pincnt; @@ -3159,6 +3165,7 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b has_ien = !!(caps & PIN_CFG_IEN); has_sr = !!(caps & PIN_CFG_SR); has_nod = !!(caps & PIN_CFG_NOD); + has_pupd = !!(caps & PIN_CFG_PUPD); pincnt = hweight8(FIELD_GET(RZG2L_SINGLE_PIN_BITS_MASK, cfg)); if (has_iolh) { @@ -3177,6 +3184,11 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + NOD(off), cache->nod[0][i]); } + if (has_pupd) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), + cache->pupd[0][i]); + } + if (pincnt >= 4) { if (has_iolh) { RZG2L_PCTRL_REG_ACCESS32(suspend, @@ -3198,6 +3210,11 @@ static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, b pctrl->base + NOD(off) + 4, cache->nod[1][i]); } + if (has_pupd) { + RZG2L_PCTRL_REG_ACCESS32(suspend, + pctrl->base + PUPD(off) + 4, + cache->pupd[1][i]); + } } caps = 0; } From 79b5d6970b4e5b7b7ee4d7fdb18e950086b72bde Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 26 Mar 2026 11:06:35 +0000 Subject: [PATCH 0108/5214] clk: renesas: rzg2l: Drop always-false check in rzg3s_cpg_pll_clk_recalc_rate() Drop the unwanted check in rzg3s_cpg_pll_clk_recalc_rate() as the function is SoC-specific. Reviewed-by: Claudiu Beznea Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260326110648.29389-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index abfd8634d2be..910c16a369a5 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1107,9 +1107,6 @@ static unsigned long rzg3s_cpg_pll_clk_recalc_rate(struct clk_hw *hw, u32 nir, nfr, mr, pr, val, setting; u64 rate; - if (pll_clk->type != CLK_TYPE_G3S_PLL) - return parent_rate; - setting = GET_REG_SAMPLL_SETTING(pll_clk->conf); if (setting) { val = readl(priv->base + setting); From 78db1faa6b681da20ec167268b28778ebb0f98b7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 26 Mar 2026 11:06:36 +0000 Subject: [PATCH 0109/5214] clk: renesas: rzg2l: Add support for enabling PLLs Add support for enabling PLL clocks in the RZ/G3L CPG driver to turn off some PLLs, if they are not in use (e.g. PLL6, PLL7). Introduce .is_enabled() and .enable() callbacks to handle PLL state transitions. With the .enable() callback, the PLL will be turned ON only when the PLL consumer device is enabled; otherwise, it will remain off. Define new macros for PLL standby and monitor registers to facilitate this process. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260326110648.29389-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 67 +++++++++++++++++++++++++++++++++ drivers/clk/renesas/rzg2l-cpg.h | 4 ++ 2 files changed, 71 insertions(+) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 910c16a369a5..f98b6eb4f501 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -58,6 +58,13 @@ #define RZG3S_DIV_NF GENMASK(12, 1) #define RZG3S_SEL_PLL BIT(0) +#define RZG3L_PLL_STBY_OFFSET(x) (GET_REG_SAMPLL_CLK1(x) - 0x4) +#define RZG3L_PLL_STBY_RESETB BIT(0) +#define RZG3L_PLL_STBY_RESETB_WEN BIT(16) +#define RZG3L_PLL_MON_OFFSET(x) (GET_REG_SAMPLL_CLK1(x) + 0x8) +#define RZG3L_PLL_MON_RESETB BIT(0) +#define RZG3L_PLL_MON_LOCK BIT(4) + #define CLK_ON_R(reg) (reg) #define CLK_MON_R(reg) (0x180 + (reg)) #define CLK_RST_R(reg) (reg) @@ -1175,6 +1182,63 @@ rzg2l_cpg_pll_clk_register(const struct cpg_core_clk *core, return pll_clk->hw.clk; } +static int rzg3l_cpg_pll_clk_is_enabled(struct clk_hw *hw) +{ + struct pll_clk *pll_clk = to_pll(hw); + struct rzg2l_cpg_priv *priv = pll_clk->priv; + u32 val = readl(priv->base + RZG3L_PLL_MON_OFFSET(pll_clk->conf)); + u32 mon_val = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; + + /* Ensure both RESETB and LOCK bits are set */ + return (mon_val == (val & mon_val)); +} + +static int rzg3l_cpg_pll_clk_endisable(struct clk_hw *hw, bool enable) +{ + struct pll_clk *pll_clk = to_pll(hw); + struct rzg2l_cpg_priv *priv = pll_clk->priv; + u32 stby_offset, mon_offset; + u32 val, mon_val; + int ret; + + stby_offset = RZG3L_PLL_STBY_OFFSET(pll_clk->conf); + mon_offset = RZG3L_PLL_MON_OFFSET(pll_clk->conf); + + if (enable) { + val = RZG3L_PLL_STBY_RESETB_WEN | RZG3L_PLL_STBY_RESETB; + mon_val = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; + } else { + val = RZG3L_PLL_STBY_RESETB_WEN; + mon_val = 0; + } + + writel(val, priv->base + stby_offset); + + /* ensure PLL is in normal/standby mode */ + ret = readl_poll_timeout_atomic(priv->base + mon_offset, val, mon_val == + (val & (RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK)), + 10, 100); + if (ret) + dev_err(priv->dev, "Failed to %s PLL 0x%x/%pC\n", enable ? + "enable" : "disable", stby_offset, hw->clk); + + return ret; +} + +static int rzg3l_cpg_pll_clk_enable(struct clk_hw *hw) +{ + if (rzg3l_cpg_pll_clk_is_enabled(hw)) + return 0; + + return rzg3l_cpg_pll_clk_endisable(hw, true); +} + +static const struct clk_ops rzg3l_cpg_pll_ops = { + .is_enabled = rzg3l_cpg_pll_clk_is_enabled, + .enable = rzg3l_cpg_pll_clk_enable, + .recalc_rate = rzg3s_cpg_pll_clk_recalc_rate, +}; + static struct clk *rzg2l_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, void *data) @@ -1258,6 +1322,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_SAM_PLL: clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg2l_cpg_pll_ops); break; + case CLK_TYPE_G3L_PLL: + clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg3l_cpg_pll_ops); + break; case CLK_TYPE_G3S_PLL: clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg3s_cpg_pll_ops); break; diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 10baf9e71a6e..ebd612d117c0 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -123,6 +123,7 @@ enum clk_types { CLK_TYPE_IN, /* External Clock Input */ CLK_TYPE_FF, /* Fixed Factor Clock */ CLK_TYPE_SAM_PLL, + CLK_TYPE_G3L_PLL, CLK_TYPE_G3S_PLL, /* Clock with divider */ @@ -152,6 +153,9 @@ enum clk_types { DEF_TYPE(_name, _id, _type, .parent = _parent) #define DEF_SAMPLL(_name, _id, _parent, _conf) \ DEF_TYPE(_name, _id, CLK_TYPE_SAM_PLL, .parent = _parent, .conf = _conf) +#define DEF_G3L_PLL(_name, _id, _parent, _conf, _default_rate) \ + DEF_TYPE(_name, _id, CLK_TYPE_G3L_PLL, .parent = _parent, .conf = _conf, \ + .default_rate = _default_rate) #define DEF_G3S_PLL(_name, _id, _parent, _conf, _default_rate) \ DEF_TYPE(_name, _id, CLK_TYPE_G3S_PLL, .parent = _parent, .conf = _conf, \ .default_rate = _default_rate) From f232745679fe1b8dfc545d2bbb4b5cd1c50b3237 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 26 Mar 2026 11:06:37 +0000 Subject: [PATCH 0110/5214] clk: renesas: r8a08g046: Add support for PLL6 Add support for the PLL6 clk by registering it with rzg2l-cpg driver. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260326110648.29389-4-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 6759957980f2..fed9607af216 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -29,6 +29,9 @@ #define G3L_DIVPL2B_STS DDIV_PACK(G3L_CLKDIVSTATUS, 5, 1) #define G3L_DIVPL3A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 8, 1) +/* PLL 1/4/6/7 configuration registers macro. */ +#define G3L_PLL1467_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) + enum clk_ids { /* Core Clock Outputs exported to DT */ LAST_DT_CORE_CLK = R9A08G046_USB_SCLK, @@ -45,6 +48,7 @@ enum clk_ids { CLK_PLL2_DIV2, CLK_PLL3, CLK_PLL3_DIV2, + CLK_PLL6, /* Module Clocks */ MOD_CLK_BASE, @@ -78,6 +82,8 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { /* Internal Core Clocks */ DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), + DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, G3L_PLL1467_CONF(0x54, 0x58, 0), + 500000000UL), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), From d55a0dfec40b6b820a0a391904daf8e74d6f0ec9 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 26 Mar 2026 11:06:38 +0000 Subject: [PATCH 0111/5214] clk: renesas: r9a08g046: Add GBETH clocks and resets Add clock and reset entries for the Gigabit Ethernet Interfaces (GBETH 0-1) IPs found on the RZ/G3L SoC. This includes various dividers and mux clocks needed by these two GBETH IPs. Also add the tx, tx-180, rx, rx-180, rmii, rmii-tx and rmii-rx clocks to the r9a08g046_no_pm_mod_clk table to avoid enabling both normal and RMII clocks by the PM framework. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260326110648.29389-5-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 143 ++++++++++++++++++++++++++++ drivers/clk/renesas/rzg2l-cpg.h | 6 ++ 2 files changed, 149 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index fed9607af216..578a8004feeb 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -18,17 +18,35 @@ #define G3L_CPG_PL2_DDIV (0x204) #define G3L_CPG_PL3_DDIV (0x208) #define G3L_CLKDIVSTATUS (0x280) +#define G3L_CPG_ETH_SSEL (0x410) +#define G3L_CPG_ETH_SDIV (0x434) /* RZ/G3L Specific division configuration. */ #define G3L_DIVPL2A DDIV_PACK(G3L_CPG_PL2_DDIV, 0, 2) #define G3L_DIVPL2B DDIV_PACK(G3L_CPG_PL2_DDIV, 4, 2) #define G3L_DIVPL3A DDIV_PACK(G3L_CPG_PL3_DDIV, 0, 2) +#define G3L_SDIV_ETH_A DDIV_PACK(G3L_CPG_ETH_SDIV, 0, 2) +#define G3L_SDIV_ETH_B DDIV_PACK(G3L_CPG_ETH_SDIV, 4, 1) +#define G3L_SDIV_ETH_C DDIV_PACK(G3L_CPG_ETH_SDIV, 8, 2) +#define G3L_SDIV_ETH_D DDIV_PACK(G3L_CPG_ETH_SDIV, 12, 1) /* RZ/G3L Clock status configuration. */ #define G3L_DIVPL2A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 4, 1) #define G3L_DIVPL2B_STS DDIV_PACK(G3L_CLKDIVSTATUS, 5, 1) #define G3L_DIVPL3A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 8, 1) +/* RZ/G3L Specific clocks select. */ +#define G3L_SEL_ETH0_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 0, 1) +#define G3L_SEL_ETH0_RX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 1, 1) +#define G3L_SEL_ETH0_RM SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 2, 1) +#define G3L_SEL_ETH0_CLK_TX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 3, 1) +#define G3L_SEL_ETH0_CLK_RX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 4, 1) +#define G3L_SEL_ETH1_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 8, 1) +#define G3L_SEL_ETH1_RX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 9, 1) +#define G3L_SEL_ETH1_RM SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 10, 1) +#define G3L_SEL_ETH1_CLK_TX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 11, 1) +#define G3L_SEL_ETH1_CLK_RX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 12, 1) + /* PLL 1/4/6/7 configuration registers macro. */ #define G3L_PLL1467_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) @@ -49,12 +67,29 @@ enum clk_ids { CLK_PLL3, CLK_PLL3_DIV2, CLK_PLL6, + CLK_PLL6_DIV10, + CLK_SEL_ETH0_TX, + CLK_SEL_ETH0_RX, + CLK_SEL_ETH0_RM, + CLK_SEL_ETH1_TX, + CLK_SEL_ETH1_RX, + CLK_SEL_ETH1_RM, + CLK_ETH0_TR, + CLK_ETH0_RM, + CLK_ETH1_TR, + CLK_ETH1_RM, /* Module Clocks */ MOD_CLK_BASE, }; /* Divider tables */ +static const struct clk_div_table dtable_2_20[] = { + { 0, 2 }, + { 1, 20 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_4_128[] = { { 0, 4 }, { 1, 8 }, @@ -63,6 +98,13 @@ static const struct clk_div_table dtable_4_128[] = { { 0, 0 }, }; +static const struct clk_div_table dtable_4_200[] = { + { 0, 4 }, + { 1, 20 }, + { 2, 200 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_8_256[] = { { 0, 8 }, { 1, 16 }, @@ -71,6 +113,18 @@ static const struct clk_div_table dtable_8_256[] = { { 0, 0 }, }; +/* Mux clock names tables. */ +static const char * const sel_eth0_tx[] = { ".div_eth0_tr", "eth0_txc_tx_clk" }; +static const char * const sel_eth0_rx[] = { ".div_eth0_tr", "eth0_rxc_rx_clk" }; +static const char * const sel_eth0_rm[] = { ".pll6_div10", "eth0_rxc_rx_clk" }; +static const char * const sel_eth1_tx[] = { ".div_eth1_tr", "eth1_txc_tx_clk" }; +static const char * const sel_eth1_rx[] = { ".div_eth1_tr", "eth1_rxc_rx_clk" }; +static const char * const sel_eth1_rm[] = { ".pll6_div10", "eth1_rxc_rx_clk" }; +static const char * const sel_eth0_clk_tx_i[] = { ".sel_eth0_tx", ".div_eth0_rm" }; +static const char * const sel_eth0_clk_rx_i[] = { ".sel_eth0_rx", ".div_eth0_rm" }; +static const char * const sel_eth1_clk_tx_i[] = { ".sel_eth1_tx", ".div_eth1_rm" }; +static const char * const sel_eth1_clk_rx_i[] = { ".sel_eth1_rx", ".div_eth1_rm" }; + static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { /* External Clock Inputs */ DEF_INPUT("extal", CLK_EXTAL), @@ -86,6 +140,17 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { 500000000UL), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), + DEF_FIXED(".pll6_div10", CLK_PLL6_DIV10, CLK_PLL6, 1, 10), + DEF_MUX(".sel_eth0_tx", CLK_SEL_ETH0_TX, G3L_SEL_ETH0_TX, sel_eth0_tx), + DEF_MUX(".sel_eth0_rx", CLK_SEL_ETH0_RX, G3L_SEL_ETH0_RX, sel_eth0_rx), + DEF_MUX(".sel_eth0_rm", CLK_SEL_ETH0_RM, G3L_SEL_ETH0_RM, sel_eth0_rm), + DEF_MUX(".sel_eth1_tx", CLK_SEL_ETH1_TX, G3L_SEL_ETH1_TX, sel_eth1_tx), + DEF_MUX(".sel_eth1_rx", CLK_SEL_ETH1_RX, G3L_SEL_ETH1_RX, sel_eth1_rx), + DEF_MUX(".sel_eth1_rm", CLK_SEL_ETH1_RM, G3L_SEL_ETH1_RM, sel_eth1_rm), + DEF_DIV(".div_eth0_tr", CLK_ETH0_TR, CLK_PLL6, G3L_SDIV_ETH_A, dtable_4_200), + DEF_DIV(".div_eth1_tr", CLK_ETH1_TR, CLK_PLL6, G3L_SDIV_ETH_C, dtable_4_200), + DEF_DIV(".div_eth0_rm", CLK_ETH0_RM, CLK_SEL_ETH0_RM, G3L_SDIV_ETH_B, dtable_2_20), + DEF_DIV(".div_eth1_rm", CLK_ETH1_RM, CLK_SEL_ETH1_RM, G3L_SDIV_ETH_D, dtable_2_20), /* Core output clk */ DEF_G3S_DIV("P0", R9A08G046_CLK_P0, CLK_PLL2_DIV2, G3L_DIVPL2B, G3L_DIVPL2B_STS, @@ -94,6 +159,21 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { dtable_4_128, 0, 0, 0, NULL), DEF_G3S_DIV("P3", R9A08G046_CLK_P3, CLK_PLL2_DIV2, G3L_DIVPL2A, G3L_DIVPL2A_STS, dtable_4_128, 0, 0, 0, NULL), + DEF_FIXED("HP", R9A08G046_CLK_HP, CLK_PLL6_DIV10, 1, 1), + DEF_MUX_FLAGS("ETHTX01", R9A08G046_CLK_ETHTX01, G3L_SEL_ETH0_CLK_TX_I, sel_eth0_clk_tx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHRX01", R9A08G046_CLK_ETHRX01, G3L_SEL_ETH0_CLK_RX_I, sel_eth0_clk_rx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHTX11", R9A08G046_CLK_ETHTX11, G3L_SEL_ETH1_CLK_TX_I, sel_eth1_clk_tx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHRX11", R9A08G046_CLK_ETHRX11, G3L_SEL_ETH1_CLK_RX_I, sel_eth1_clk_rx_i, + CLK_SET_RATE_PARENT), + DEF_FIXED("ETHRM0", R9A08G046_CLK_ETHRM0, CLK_SEL_ETH0_RM, 1, 1), + DEF_FIXED("ETHTX02", R9A08G046_CLK_ETHTX02, CLK_SEL_ETH0_TX, 1, 1), + DEF_FIXED("ETHRX02", R9A08G046_CLK_ETHRX02, CLK_SEL_ETH0_RX, 1, 1), + DEF_FIXED("ETHRM1", R9A08G046_CLK_ETHRM1, CLK_SEL_ETH1_RM, 1, 1), + DEF_FIXED("ETHTX12", R9A08G046_CLK_ETHTX12, CLK_SEL_ETH1_TX, 1, 1), + DEF_FIXED("ETHRX12", R9A08G046_CLK_ETHRX12, CLK_SEL_ETH1_RX, 1, 1), }; static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { @@ -107,6 +187,46 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_REG1, BIT(2))), DEF_MOD("dmac_pclk", R9A08G046_DMAC_PCLK, R9A08G046_CLK_P3, 0x52c, 1, MSTOP(BUS_REG1, BIT(3))), + DEF_MOD("eth0_clk_axi", R9A08G046_ETH0_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 0, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_clk_axi", R9A08G046_ETH1_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 1, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_clk_chi", R9A08G046_ETH0_CLK_CHI, R9A08G046_CLK_P1, 0x57c, 2, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_clk_chi", R9A08G046_ETH1_CLK_CHI, R9A08G046_CLK_P1, 0x57c, 3, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_tx_i", R9A08G046_ETH0_CLK_TX_I, R9A08G046_CLK_ETHTX01, 0x57c, 4, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_tx_180_i", R9A08G046_ETH0_CLK_TX_180_I, R9A08G046_CLK_ETHTX02, 0x57c, 4, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_tx_i", R9A08G046_ETH1_CLK_TX_I, R9A08G046_CLK_ETHTX11, 0x57c, 5, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_tx_180_i", R9A08G046_ETH1_CLK_TX_180_I, R9A08G046_CLK_ETHTX12, 0x57c, 5, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_rx_i", R9A08G046_ETH0_CLK_RX_I, R9A08G046_CLK_ETHRX01, 0x57c, 6, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_rx_180_i", R9A08G046_ETH0_CLK_RX_180_I, R9A08G046_CLK_ETHRX02, 0x57c, 6, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_rx_i", R9A08G046_ETH1_CLK_RX_I, R9A08G046_CLK_ETHRX11, 0x57c, 7, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_rx_180_i", R9A08G046_ETH1_CLK_RX_180_I, R9A08G046_CLK_ETHRX12, 0x57c, 7, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_ptp_ref_i", R9A08G046_ETH0_CLK_PTP_REF_I, R9A08G046_CLK_HP, 0x57c, 8, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_ptp_ref_i", R9A08G046_ETH1_CLK_PTP_REF_I, R9A08G046_CLK_HP, 0x57c, 9, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_rmii_i", R9A08G046_ETH0_CLK_RMII_I, R9A08G046_CLK_ETHRM0, 0x57c, 10, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_rmii_i", R9A08G046_ETH1_CLK_RMII_I, R9A08G046_CLK_ETHRM1, 0x57c, 11, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_tx_i_rmii", R9A08G046_ETH0_CLK_TX_I_RMII, R9A08G046_CLK_ETHTX01, 0x57c, 12, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_rx_i_rmii", R9A08G046_ETH0_CLK_RX_I_RMII, R9A08G046_CLK_ETHRX01, 0x57c, 12, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_tx_i_rmii", R9A08G046_ETH1_CLK_TX_I_RMII, R9A08G046_CLK_ETHTX11, 0x57c, 13, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_rx_i_rmii", R9A08G046_ETH1_CLK_RX_I_RMII, R9A08G046_CLK_ETHRX11, 0x57c, 13, + MSTOP(BUS_PERI_COM, BIT(3))), DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, MSTOP(BUS_MCPU2, BIT(1))), }; @@ -117,6 +237,8 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_IA55_RESETN, 0x818, 0), DEF_RST(R9A08G046_DMAC_ARESETN, 0x82c, 0), DEF_RST(R9A08G046_DMAC_RST_ASYNC, 0x82c, 1), + DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), + DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), }; @@ -131,6 +253,23 @@ static const unsigned int r9a08g046_crit_resets[] = { R9A08G046_DMAC_RST_ASYNC, }; +static const unsigned int r9a08g046_no_pm_mod_clks[] = { + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_180_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_180_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RMII_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_180_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_180_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RMII_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_I_RMII, +}; + const struct rzg2l_cpg_info r9a08g046_cpg_info = { /* Core Clocks */ .core_clks = r9a08g046_core_clks, @@ -147,6 +286,10 @@ const struct rzg2l_cpg_info r9a08g046_cpg_info = { .num_mod_clks = ARRAY_SIZE(r9a08g046_mod_clks), .num_hw_mod_clks = R9A08G046_BSC_X_BCK_BSC + 1, + /* No PM modules Clocks */ + .no_pm_mod_clks = r9a08g046_no_pm_mod_clks, + .num_no_pm_mod_clks = ARRAY_SIZE(r9a08g046_no_pm_mod_clks), + /* Resets */ .resets = r9a08g046_resets, .num_resets = R9A08G046_BSC_X_PRESET_BSC + 1, /* Last reset ID + 1 */ diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index ebd612d117c0..0e63b62e8435 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -188,6 +188,12 @@ enum clk_types { .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ .mux_flags = CLK_MUX_READ_ONLY) +#define DEF_MUX_FLAGS(_name, _id, _conf, _parent_names, _flag) \ + DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .mux_flags = CLK_MUX_HIWORD_MASK, \ + .flag = _flag) #define DEF_SD_MUX(_name, _id, _conf, _sconf, _parent_names, _mtable, _clk_flags, _notifier) \ DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, .sconf = _sconf, \ .parent_names = _parent_names, \ From da000c4d5f1de7e83778cc0fb3974d2a9af2933c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 14:23:38 +0100 Subject: [PATCH 0112/5214] clk: renesas: r9a08g046: Add GPIO clocks/resets Add GPIO clock and reset entries. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260330132349.149391-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 578a8004feeb..dc4108325d9d 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -174,6 +174,7 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_FIXED("ETHRM1", R9A08G046_CLK_ETHRM1, CLK_SEL_ETH1_RM, 1, 1), DEF_FIXED("ETHTX12", R9A08G046_CLK_ETHTX12, CLK_SEL_ETH1_TX, 1, 1), DEF_FIXED("ETHRX12", R9A08G046_CLK_ETHRX12, CLK_SEL_ETH1_RX, 1, 1), + DEF_FIXED("OSCCLK", R9A08G046_OSCCLK, CLK_EXTAL, 1, 1), }; static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { @@ -229,6 +230,8 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_PERI_COM, BIT(3))), DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, MSTOP(BUS_MCPU2, BIT(1))), + DEF_MOD("gpio_hclk", R9A08G046_GPIO_HCLK, R9A08G046_OSCCLK, 0x598, 0, + MSTOP(BUS_PERI_CPU, BIT(6))), }; static const struct rzg2l_reset r9a08g046_resets[] = { @@ -240,6 +243,9 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), + DEF_RST(R9A08G046_GPIO_RSTN, 0x898, 0), + DEF_RST(R9A08G046_GPIO_PORT_RESETN, 0x898, 1), + DEF_RST(R9A08G046_GPIO_SPARE_RESETN, 0x898, 2), }; static const unsigned int r9a08g046_crit_mod_clks[] __initconst = { From 4ea266768a258e94a0e2376f5345d4303ab8074f Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 14:23:39 +0100 Subject: [PATCH 0113/5214] clk: renesas: r9a08g046: Add CA55 core clocks Add CA55 core clock entries. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260330132349.149391-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index dc4108325d9d..d47aeaea2d6e 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -17,6 +17,7 @@ /* RZ/G3L Specific registers. */ #define G3L_CPG_PL2_DDIV (0x204) #define G3L_CPG_PL3_DDIV (0x208) +#define G3L_CPG_CA55CORE_DDIV (0x234) #define G3L_CLKDIVSTATUS (0x280) #define G3L_CPG_ETH_SSEL (0x410) #define G3L_CPG_ETH_SDIV (0x434) @@ -25,6 +26,10 @@ #define G3L_DIVPL2A DDIV_PACK(G3L_CPG_PL2_DDIV, 0, 2) #define G3L_DIVPL2B DDIV_PACK(G3L_CPG_PL2_DDIV, 4, 2) #define G3L_DIVPL3A DDIV_PACK(G3L_CPG_PL3_DDIV, 0, 2) +#define G3L_DIV_CA55_CORE0 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 0, 3) +#define G3L_DIV_CA55_CORE1 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 4, 3) +#define G3L_DIV_CA55_CORE2 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 8, 3) +#define G3L_DIV_CA55_CORE3 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 12, 3) #define G3L_SDIV_ETH_A DDIV_PACK(G3L_CPG_ETH_SDIV, 0, 2) #define G3L_SDIV_ETH_B DDIV_PACK(G3L_CPG_ETH_SDIV, 4, 1) #define G3L_SDIV_ETH_C DDIV_PACK(G3L_CPG_ETH_SDIV, 8, 2) @@ -34,6 +39,10 @@ #define G3L_DIVPL2A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 4, 1) #define G3L_DIVPL2B_STS DDIV_PACK(G3L_CLKDIVSTATUS, 5, 1) #define G3L_DIVPL3A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 8, 1) +#define G3L_DIV_CA55_CORE0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 12, 1) +#define G3L_DIV_CA55_CORE1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 13, 1) +#define G3L_DIV_CA55_CORE2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 14, 1) +#define G3L_DIV_CA55_CORE3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 15, 1) /* RZ/G3L Specific clocks select. */ #define G3L_SEL_ETH0_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 0, 1) @@ -62,6 +71,7 @@ enum clk_ids { CLK_ETH1_RXC_RX_CLK_IN, /* Internal Core Clocks */ + CLK_PLL1, CLK_PLL2, CLK_PLL2_DIV2, CLK_PLL3, @@ -84,6 +94,16 @@ enum clk_ids { }; /* Divider tables */ +static const struct clk_div_table dtable_1_32[] = { + { 0, 1 }, + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 4, 16 }, + { 5, 32 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_2_20[] = { { 0, 2 }, { 1, 20 }, @@ -134,6 +154,8 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_INPUT("eth1_rxc_rx_clk", CLK_ETH1_RXC_RX_CLK_IN), /* Internal Core Clocks */ + DEF_G3L_PLL(".pll1", CLK_PLL1, CLK_EXTAL, G3L_PLL1467_CONF(0x4, 0x8, 0x100), + 1200000000UL), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, G3L_PLL1467_CONF(0x54, 0x58, 0), @@ -153,6 +175,14 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_DIV(".div_eth1_rm", CLK_ETH1_RM, CLK_SEL_ETH1_RM, G3L_SDIV_ETH_D, dtable_2_20), /* Core output clk */ + DEF_G3S_DIV("IC0", R9A08G046_CLK_IC0, CLK_PLL1, G3L_DIV_CA55_CORE0, G3L_DIV_CA55_CORE0_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC1", R9A08G046_CLK_IC1, CLK_PLL1, G3L_DIV_CA55_CORE1, G3L_DIV_CA55_CORE1_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC2", R9A08G046_CLK_IC2, CLK_PLL1, G3L_DIV_CA55_CORE2, G3L_DIV_CA55_CORE2_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC3", R9A08G046_CLK_IC3, CLK_PLL1, G3L_DIV_CA55_CORE3, G3L_DIV_CA55_CORE3_STS, + dtable_1_32, 0, 0, 0, NULL), DEF_G3S_DIV("P0", R9A08G046_CLK_P0, CLK_PLL2_DIV2, G3L_DIVPL2B, G3L_DIVPL2B_STS, dtable_8_256, 0, 0, 0, NULL), DEF_G3S_DIV("P1", R9A08G046_CLK_P1, CLK_PLL3_DIV2, G3L_DIVPL3A, G3L_DIVPL3A_STS, From c03f83f2a36291acca0b6638e91ab384fc319945 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 14:23:40 +0100 Subject: [PATCH 0114/5214] clk: renesas: r9a08g046: Add WDT clocks and reset Add WDT clock and reset entries. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260330132349.149391-4-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index d47aeaea2d6e..a311e17958d1 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -218,6 +218,10 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_REG1, BIT(2))), DEF_MOD("dmac_pclk", R9A08G046_DMAC_PCLK, R9A08G046_CLK_P3, 0x52c, 1, MSTOP(BUS_REG1, BIT(3))), + DEF_MOD("wdt0_pclk", R9A08G046_WDT0_PCLK, R9A08G046_CLK_P0, 0x548, 0, + MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("wdt0_clk", R9A08G046_WDT0_CLK, R9A08G046_OSCCLK, 0x548, 1, + MSTOP(BUS_REG0, BIT(0))), DEF_MOD("eth0_clk_axi", R9A08G046_ETH0_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 0, MSTOP(BUS_PERI_COM, BIT(2))), DEF_MOD("eth1_clk_axi", R9A08G046_ETH1_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 1, @@ -270,6 +274,7 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_IA55_RESETN, 0x818, 0), DEF_RST(R9A08G046_DMAC_ARESETN, 0x82c, 0), DEF_RST(R9A08G046_DMAC_RST_ASYNC, 0x82c, 1), + DEF_RST(R9A08G046_WDT0_PRESETN, 0x848, 0), DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), From 6b99bb5e6ebec07815c0ad742862bafa386797ff Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 14:23:41 +0100 Subject: [PATCH 0115/5214] clk: renesas: r9a08g046: Add SCIF{1..5} clocks and resets Add SCIF{1..5} clock and reset entries. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260330132349.149391-5-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index a311e17958d1..962094157cab 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -264,6 +264,16 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_PERI_COM, BIT(3))), DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, MSTOP(BUS_MCPU2, BIT(1))), + DEF_MOD("scif1_clk_pck", R9A08G046_SCIF1_CLK_PCK, R9A08G046_CLK_P0, 0x584, 1, + MSTOP(BUS_MCPU2, BIT(2))), + DEF_MOD("scif2_clk_pck", R9A08G046_SCIF2_CLK_PCK, R9A08G046_CLK_P0, 0x584, 2, + MSTOP(BUS_MCPU2, BIT(3))), + DEF_MOD("scif3_clk_pck", R9A08G046_SCIF3_CLK_PCK, R9A08G046_CLK_P0, 0x584, 3, + MSTOP(BUS_MCPU2, BIT(4))), + DEF_MOD("scif4_clk_pck", R9A08G046_SCIF4_CLK_PCK, R9A08G046_CLK_P0, 0x584, 4, + MSTOP(BUS_MCPU2, BIT(5))), + DEF_MOD("scif5_clk_pck", R9A08G046_SCIF5_CLK_PCK, R9A08G046_CLK_P0, 0x584, 5, + MSTOP(BUS_MCPU3, BIT(4))), DEF_MOD("gpio_hclk", R9A08G046_GPIO_HCLK, R9A08G046_OSCCLK, 0x598, 0, MSTOP(BUS_PERI_CPU, BIT(6))), }; @@ -278,6 +288,11 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), + DEF_RST(R9A08G046_SCIF1_RST_SYSTEM_N, 0x884, 1), + DEF_RST(R9A08G046_SCIF2_RST_SYSTEM_N, 0x884, 2), + DEF_RST(R9A08G046_SCIF3_RST_SYSTEM_N, 0x884, 3), + DEF_RST(R9A08G046_SCIF4_RST_SYSTEM_N, 0x884, 4), + DEF_RST(R9A08G046_SCIF5_RST_SYSTEM_N, 0x884, 5), DEF_RST(R9A08G046_GPIO_RSTN, 0x898, 0), DEF_RST(R9A08G046_GPIO_PORT_RESETN, 0x898, 1), DEF_RST(R9A08G046_GPIO_SPARE_RESETN, 0x898, 2), From f34ad4b0b4678d20697f93a79f013d0b6b1d7136 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 14:23:42 +0100 Subject: [PATCH 0116/5214] clk: renesas: r9a08g046: Add I2C clocks and resets Add I2C{0..3} clock and reset entries. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260330132349.149391-6-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 962094157cab..ce9503c3cfd1 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -262,6 +262,14 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_PERI_COM, BIT(3))), DEF_COUPLED("eth1_rx_i_rmii", R9A08G046_ETH1_CLK_RX_I_RMII, R9A08G046_CLK_ETHRX11, 0x57c, 13, MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("i2c0_pclk", R9A08G046_I2C0_PCLK, R9A08G046_CLK_P0, 0x580, 0, + MSTOP(BUS_MCPU2, BIT(10))), + DEF_MOD("i2c1_pclk", R9A08G046_I2C1_PCLK, R9A08G046_CLK_P0, 0x580, 1, + MSTOP(BUS_MCPU2, BIT(11))), + DEF_MOD("i2c2_pclk", R9A08G046_I2C2_PCLK, R9A08G046_CLK_P0, 0x580, 2, + MSTOP(BUS_MCPU2, BIT(12))), + DEF_MOD("i2c3_pclk", R9A08G046_I2C3_PCLK, R9A08G046_CLK_P0, 0x580, 3, + MSTOP(BUS_MCPU2, BIT(13))), DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, MSTOP(BUS_MCPU2, BIT(1))), DEF_MOD("scif1_clk_pck", R9A08G046_SCIF1_CLK_PCK, R9A08G046_CLK_P0, 0x584, 1, @@ -287,6 +295,10 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_WDT0_PRESETN, 0x848, 0), DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), + DEF_RST(R9A08G046_I2C0_MRST, 0x880, 0), + DEF_RST(R9A08G046_I2C1_MRST, 0x880, 1), + DEF_RST(R9A08G046_I2C2_MRST, 0x880, 2), + DEF_RST(R9A08G046_I2C3_MRST, 0x880, 3), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), DEF_RST(R9A08G046_SCIF1_RST_SYSTEM_N, 0x884, 1), DEF_RST(R9A08G046_SCIF2_RST_SYSTEM_N, 0x884, 2), From 38acb2a1a0ced15f61342347bba8aa57775802ea Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Fri, 10 Apr 2026 19:35:21 +0300 Subject: [PATCH 0117/5214] clk: renesas: r9a09g077: Add MTU3 module clock The Renesas RZ/T2H (R9A09G077) and RZ/N2H (R9A09G087) SoCs have a MTU3 block connected to the PCLKH and with a module clock controlled by register 0x308, bit 0. Add support for the module clock. Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260410163530.383818-2-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g077-cpg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r9a09g077-cpg.c b/drivers/clk/renesas/r9a09g077-cpg.c index 93b15e06a19b..f777601a23b9 100644 --- a/drivers/clk/renesas/r9a09g077-cpg.c +++ b/drivers/clk/renesas/r9a09g077-cpg.c @@ -257,6 +257,7 @@ static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = { DEF_MOD("spi0", 104, CLK_SPI0ASYNC), DEF_MOD("spi1", 105, CLK_SPI1ASYNC), DEF_MOD("spi2", 106, CLK_SPI2ASYNC), + DEF_MOD("mtu3", 200, R9A09G077_CLK_PCLKH), DEF_MOD("adc0", 206, R9A09G077_CLK_PCLKH), DEF_MOD("adc1", 207, R9A09G077_CLK_PCLKH), DEF_MOD("adc2", 225, R9A09G077_CLK_PCLKM), From f924a4041f4ad6f6772f437596f0249e46edfb79 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 23 Apr 2026 01:36:28 +0200 Subject: [PATCH 0118/5214] clk: renesas: r8a7740: Add ZT/ZTR trace clocks Implement support for the ZT trace bus and ZTR trace clocks on R-Mobile A1. Signed-off-by: Marek Vasut Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260422233744.149872-3-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/clk-r8a7740.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c index 635d59ead499..31a79674583e 100644 --- a/drivers/clk/renesas/clk-r8a7740.c +++ b/drivers/clk/renesas/clk-r8a7740.c @@ -37,6 +37,8 @@ static struct div4_clk div4_clks[] = { { "zg", CPG_FRQCRA, 16 }, { "b", CPG_FRQCRA, 8 }, { "m1", CPG_FRQCRA, 4 }, + { "ztr", CPG_FRQCRB, 20 }, + { "zt", CPG_FRQCRB, 16 }, { "hp", CPG_FRQCRB, 4 }, { "hpp", CPG_FRQCRC, 20 }, { "usbp", CPG_FRQCRC, 16 }, From 6aa2b11ce527c23e6a472204ee74fdf4a7411231 Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Tue, 31 Mar 2026 17:44:42 +0100 Subject: [PATCH 0119/5214] staging: most: dim2: remove unnecessary NULL check in service_done_flag() Remove the !hdm_ch check. hdm_ch is derived from dev->hch + ch_idx (pointer arithmetic on a struct member), so it can never be NULL. Keep only the !hdm_ch->is_initialized check. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/acuoL4DRi0pmsQY1@stanley.mountain Signed-off-by: Gabriel Rondon Link: https://patch.msgid.link/20260331164443.47682-1-grondon@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/dim2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 7953d4626752..f5bd7d44f165 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -271,7 +271,7 @@ static void service_done_flag(struct dim2_hdm *dev, int ch_idx) unsigned long flags; u8 *data; - if (!hdm_ch || !hdm_ch->is_initialized) + if (!hdm_ch->is_initialized) return; spin_lock_irqsave(&dim_lock, flags); From fde67f5b1fc77f3c6701b31751643c11526927bc Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Wed, 1 Apr 2026 11:10:38 +0100 Subject: [PATCH 0120/5214] staging: most: dim2: remove unnecessary NULL check in try_start_dim_transfer() Remove the !hdm_ch check. Although hdm_ch is a function parameter, it is already dereferenced on the preceding line to initialize head (head = &hdm_ch->pending_list), so a NULL check after that point is dead code. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/acwjEHyEYg0V3OyC@stanley.mountain Signed-off-by: Gabriel Rondon Link: https://patch.msgid.link/20260401101038.24304-1-grondon@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/dim2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index f5bd7d44f165..327790436653 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -165,7 +165,7 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch) unsigned long flags; struct dim_ch_state st; - if (!hdm_ch || !hdm_ch->is_initialized) + if (!hdm_ch->is_initialized) return -EINVAL; spin_lock_irqsave(&dim_lock, flags); From 62cf6eb023a89dee27e2413c5a503468e07e0f4a Mon Sep 17 00:00:00 2001 From: Hadi Chokr Date: Wed, 1 Apr 2026 14:57:11 +0200 Subject: [PATCH 0121/5214] staging: most/net: remove dead code from skb_to_mamac() and skb_to_mep() The overflow checks in skb_to_mamac() and skb_to_mep() are always false: mdp_len = (skb->len - ETH_HLEN) + MDP_HDR_LEN = skb->len + 2 mep_len = skb->len + MEP_HDR_LEN = skb->len + 8 Remove these checks to clean up the code. Signed-off-by: Hadi Chokr Link: https://patch.msgid.link/20260401125711.80822-1-hadichokr@icloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/net/net.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index fffdb60cd230..07fd18e9096d 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -80,11 +80,6 @@ static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) unsigned int payload_len = skb->len - ETH_HLEN; unsigned int mdp_len = payload_len + MDP_HDR_LEN; - if (mdp_len < skb->len) { - pr_err("drop: too large packet! (%u)\n", skb->len); - return -EINVAL; - } - if (mbo->buffer_length < mdp_len) { pr_err("drop: too small buffer! (%d for %d)\n", mbo->buffer_length, mdp_len); @@ -132,11 +127,6 @@ static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo) u8 *buff = mbo->virt_address; unsigned int mep_len = skb->len + MEP_HDR_LEN; - if (mep_len < skb->len) { - pr_err("drop: too large packet! (%u)\n", skb->len); - return -EINVAL; - } - if (mbo->buffer_length < mep_len) { pr_err("drop: too small buffer! (%d for %d)\n", mbo->buffer_length, mep_len); From 57b5b1f45057caf56d8f501a9a68fca5a08fc6d5 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sat, 11 Apr 2026 15:21:36 +0530 Subject: [PATCH 0122/5214] staging: most: net: remove filename from top-of-file comment Remove the filename reference from the top-of-file comment block, to resolve a checkpatch.pl warning. The filename comment is not useful and can become outdated if the file is renamed. No functional changes. Signed-off-by: Shyam Sunder Reddy Padira Link: https://patch.msgid.link/20260411095255.4890-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/net/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 07fd18e9096d..15fa2e410e4c 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * net.c - Networking component for Mostcore + * Networking component for Mostcore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ From 467716c1cc19bc7172b7ac23a1d8bf791f5c0c6b Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Sun, 12 Apr 2026 23:23:18 +0100 Subject: [PATCH 0123/5214] staging: most: video: remove redundant cleanup in comp_exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit most_deregister_component() already calls disconnect_channel() for every linked channel via bus_for_each_dev() in core.c, which invokes comp_disconnect_channel() to remove each entry from the video_devices list and tear down the V4L2 device. The manual cleanup loop in comp_exit() duplicates this work and is guarded by a stale comment claiming that "mostcore currently doesn't call disconnect_channel() for linked channels" — but the core has since been fixed to do exactly that. Remove the redundant manual cleanup loop, the outdated comment, and the BUG_ON() assertion that checked for a condition that can no longer occur. Signed-off-by: Gabriel Rondon Link: https://patch.msgid.link/20260412222318.65045-1-grondon@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/video/video.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 04351f8ccccf..98988b0d9330 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -555,29 +555,8 @@ static int __init comp_init(void) static void __exit comp_exit(void) { - struct most_video_dev *mdev, *tmp; - LIST_HEAD(free_list); - - /* - * As the mostcore currently doesn't call disconnect_channel() - * for linked channels while we call most_deregister_component() - * we simulate this call here. - * This must be fixed in core. - */ - spin_lock_irq(&list_lock); - list_replace_init(&video_devices, &free_list); - spin_unlock_irq(&list_lock); - - list_for_each_entry_safe(mdev, tmp, &free_list, list) { - list_del_init(&mdev->list); - comp_unregister_videodev(mdev); - v4l2_device_disconnect(&mdev->v4l2_dev); - v4l2_device_put(&mdev->v4l2_dev); - } - most_deregister_configfs_subsys(&comp); most_deregister_component(&comp); - BUG_ON(!list_empty(&video_devices)); } module_init(comp_init); From d0856045f0e9fc96c97fd86e5936b55c6e4db247 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 1 Apr 2026 11:50:24 +0000 Subject: [PATCH 0124/5214] staging: sm750fb: constify fix_id array Add the missing 'const' qualifier to the static fix_id array to ensure the pointer array itself is immutable. Originally: static const char *fix_id[2]; The strings are constant, but the pointer array itself is writable. With the change: static const char * const fix_id[2]; Both the strings and the pointer array are immutable, allowing the compiler to treat the object as read-only. Verified by inspecting the generated object file with 'nm': 00000000000002b8 0000000000000010 r fix_id.3 The 'r' flag indicates the symbol is placed in a read-only section. This does not change runtime behavior as fix_id is never modified. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260401115024.89-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 9f3e3d37e82a..c9fd73d2620d 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -731,7 +731,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) lynx750_ext, NULL, vesa_modes, }; int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE}; - static const char *fix_id[2] = { + static const char * const fix_id[2] = { "sm750_fb1", "sm750_fb2", }; From 94c938a0c158635a26212f2dbf6887d969e9b19e Mon Sep 17 00:00:00 2001 From: Shubham Chakraborty Date: Tue, 7 Apr 2026 13:18:03 +0530 Subject: [PATCH 0125/5214] staging: sm750fb: Rename sm750_pnltype enum values to upper case Rename the sm750_pnltype enum values from mixed/CamelCase style to upper-case names to follow kernel naming conventions for constants. Signed-off-by: Shubham Chakraborty Link: https://patch.msgid.link/20260407074805.14505-2-chakrabortyshubham66@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 6 +++--- drivers/staging/sm750fb/sm750.h | 6 +++--- drivers/staging/sm750fb/sm750_hw.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index c9fd73d2620d..f91d08b90f8f 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -880,11 +880,11 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src) } else if (!strncmp(opt, "nocrt", strlen("nocrt"))) { sm750_dev->nocrt = 1; } else if (!strncmp(opt, "36bit", strlen("36bit"))) { - sm750_dev->pnltype = sm750_doubleTFT; + sm750_dev->pnltype = SM750_DOUBLE_TFT; } else if (!strncmp(opt, "18bit", strlen("18bit"))) { - sm750_dev->pnltype = sm750_dualTFT; + sm750_dev->pnltype = SM750_DUAL_TFT; } else if (!strncmp(opt, "24bit", strlen("24bit"))) { - sm750_dev->pnltype = sm750_24TFT; + sm750_dev->pnltype = SM750_24TFT; } else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) { g_hwcursor &= ~0x1; } else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) { diff --git a/drivers/staging/sm750fb/sm750.h b/drivers/staging/sm750fb/sm750.h index 67b9bfa23f41..49a79d0a8a2e 100644 --- a/drivers/staging/sm750fb/sm750.h +++ b/drivers/staging/sm750fb/sm750.h @@ -13,9 +13,9 @@ #endif enum sm750_pnltype { - sm750_24TFT = 0, /* 24bit tft */ - sm750_dualTFT = 2, /* dual 18 bit tft */ - sm750_doubleTFT = 1, /* 36 bit double pixel tft */ + SM750_24TFT = 0, /* 24bit tft */ + SM750_DUAL_TFT = 2, /* dual 18 bit tft */ + SM750_DOUBLE_TFT = 1, /* 36 bit double pixel tft */ }; /* vga channel is not concerned */ diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index a2798d428663..79e6344a729f 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -128,12 +128,12 @@ int hw_sm750_inithw(struct sm750_dev *sm750_dev, struct pci_dev *pdev) ~(PANEL_DISPLAY_CTRL_DUAL_DISPLAY | PANEL_DISPLAY_CTRL_DOUBLE_PIXEL); switch (sm750_dev->pnltype) { - case sm750_24TFT: + case SM750_24TFT: break; - case sm750_doubleTFT: + case SM750_DOUBLE_TFT: val |= PANEL_DISPLAY_CTRL_DOUBLE_PIXEL; break; - case sm750_dualTFT: + case SM750_DUAL_TFT: val |= PANEL_DISPLAY_CTRL_DUAL_DISPLAY; break; } From 1121e3084095d162eb68bde350016e8257a37eb5 Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Wed, 8 Apr 2026 18:12:10 +0000 Subject: [PATCH 0126/5214] staging: sm750fb: fix off-by-one in lynxfb_ops_setcolreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bounds check used regno > 256 instead of regno >= 256, allowing regno == 256. Valid indices are 0–255, resulting in an out-of-bounds write. Also remove the regno < 256 check in the truecolor path, as it is always true with the corrected guard. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260408181210.9672-1-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index f91d08b90f8f..8f43eea2868b 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -531,7 +531,7 @@ static int lynxfb_ops_setcolreg(unsigned int regno, var = &info->var; ret = 0; - if (regno > 256) { + if (regno >= 256) { dev_err(info->device, "regno = %d\n", regno); return -EINVAL; } @@ -553,7 +553,7 @@ static int lynxfb_ops_setcolreg(unsigned int regno, goto exit; } - if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) { + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { u32 val; if (var->bits_per_pixel == 16 || From ec5f630241316dc594502c6c384cbf818a1d224b Mon Sep 17 00:00:00 2001 From: Kenet Jovan Sokoli Date: Sat, 18 Apr 2026 16:36:20 +0200 Subject: [PATCH 0127/5214] staging: sm750fb: remove unused functions The functions sm750_enable_i2c() and sm750_hw_cursor_set_data2() are defined and declared but never used. Following the driver's TODO list to remove unused code, this patch deletes these dead functions. Verified by compilation and cppcheck. Signed-off-by: Kenet Jovan Sokoli Link: https://patch.msgid.link/20260418143620.845355-1-deep@crimson.net.eu.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/ddk750_power.c | 17 ---------- drivers/staging/sm750fb/ddk750_power.h | 5 --- drivers/staging/sm750fb/sm750_cursor.c | 43 -------------------------- drivers/staging/sm750fb/sm750_cursor.h | 2 -- 4 files changed, 67 deletions(-) diff --git a/drivers/staging/sm750fb/ddk750_power.c b/drivers/staging/sm750fb/ddk750_power.c index 12834f78eef7..eaba3bc2e01a 100644 --- a/drivers/staging/sm750fb/ddk750_power.c +++ b/drivers/staging/sm750fb/ddk750_power.c @@ -126,20 +126,3 @@ void sm750_enable_gpio(unsigned int enable) sm750_set_current_gate(gate); } - -/* - * This function enable/disable the I2C Engine - */ -void sm750_enable_i2c(unsigned int enable) -{ - u32 gate; - - /* Enable I2C Gate */ - gate = peek32(CURRENT_GATE); - if (enable) - gate |= CURRENT_GATE_I2C; - else - gate &= ~CURRENT_GATE_I2C; - - sm750_set_current_gate(gate); -} diff --git a/drivers/staging/sm750fb/ddk750_power.h b/drivers/staging/sm750fb/ddk750_power.h index 5cbb11986bb8..1c4f054d7276 100644 --- a/drivers/staging/sm750fb/ddk750_power.h +++ b/drivers/staging/sm750fb/ddk750_power.h @@ -33,9 +33,4 @@ void sm750_enable_dma(unsigned int enable); */ void sm750_enable_gpio(unsigned int enable); -/* - * This function enable/disable the I2C Engine - */ -void sm750_enable_i2c(unsigned int enable); - #endif diff --git a/drivers/staging/sm750fb/sm750_cursor.c b/drivers/staging/sm750fb/sm750_cursor.c index 7ede144905c9..f0338e6e76b1 100644 --- a/drivers/staging/sm750fb/sm750_cursor.c +++ b/drivers/staging/sm750fb/sm750_cursor.c @@ -130,46 +130,3 @@ void sm750_hw_cursor_set_data(struct lynx_cursor *cursor, u16 rop, } } } - -void sm750_hw_cursor_set_data2(struct lynx_cursor *cursor, u16 rop, - const u8 *pcol, const u8 *pmsk) -{ - int i, j, count, pitch, offset; - u8 color, mask; - u16 data; - void __iomem *pbuffer, *pstart; - - /* in byte*/ - pitch = cursor->w >> 3; - - /* in byte */ - count = pitch * cursor->h; - - /* in byte */ - offset = cursor->max_w * 2 / 8; - - data = 0; - pstart = cursor->vstart; - pbuffer = pstart; - - for (i = 0; i < count; i++) { - color = *pcol++; - mask = *pmsk++; - data = 0; - - for (j = 0; j < 8; j++) { - if (mask & (1 << j)) - data |= ((color & (1 << j)) ? 1 : 2) << (j * 2); - } - iowrite16(data, pbuffer); - - /* assume pitch is 1,2,4,8,...*/ - if (!(i & (pitch - 1))) { - /* need a return */ - pstart += offset; - pbuffer = pstart; - } else { - pbuffer += sizeof(u16); - } - } -} diff --git a/drivers/staging/sm750fb/sm750_cursor.h b/drivers/staging/sm750fb/sm750_cursor.h index 88fa02f6377a..51ba0da0270c 100644 --- a/drivers/staging/sm750fb/sm750_cursor.h +++ b/drivers/staging/sm750fb/sm750_cursor.h @@ -10,6 +10,4 @@ void sm750_hw_cursor_set_pos(struct lynx_cursor *cursor, int x, int y); void sm750_hw_cursor_set_color(struct lynx_cursor *cursor, u32 fg, u32 bg); void sm750_hw_cursor_set_data(struct lynx_cursor *cursor, u16 rop, const u8 *data, const u8 *mask); -void sm750_hw_cursor_set_data2(struct lynx_cursor *cursor, u16 rop, - const u8 *data, const u8 *mask); #endif From 1fa2fa6866ae56eafc893ee784709122ae73bc0d Mon Sep 17 00:00:00 2001 From: Kosugi Souta Date: Sat, 4 Apr 2026 09:59:38 +0900 Subject: [PATCH 0128/5214] staging: greybus: fix alignment to match open parenthesis Fix the checkpatch.pl check "Alignment should match open parenthesis" by adjusting the indentation in authenticate.c. Signed-off-by: Kosugi Souta Link: https://patch.msgid.link/20260404005939.116701-3-k.souta0926@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/firmware/authenticate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Documentation/firmware/authenticate.c b/drivers/staging/greybus/Documentation/firmware/authenticate.c index 0ef88b7d24de..ba4b16b04557 100644 --- a/drivers/staging/greybus/Documentation/firmware/authenticate.c +++ b/drivers/staging/greybus/Documentation/firmware/authenticate.c @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) } printf("Authenticated, result (%02x), sig-size (%02x)\n", - authenticate.result_code, authenticate.signature_size); + authenticate.result_code, authenticate.signature_size); close_fd: close(fd); From 9ef11656cc444a32ff56a2badd2fab44437afbab Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 31 Mar 2026 21:13:56 -0500 Subject: [PATCH 0129/5214] staging: rtl8723bs: Remove dead code Remove commented out code and remove if statement with no body but a line of commented out code. Signed-off-by: Ethan Tidmore Link: https://patch.msgid.link/20260401021357.1176600-2-ethantidmore06@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ap.c | 68 +------------------------ 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 4b4012411011..1b67c5883a66 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -20,8 +20,6 @@ void init_mlme_ap_info(struct adapter *padapter) INIT_LIST_HEAD(&pacl_list->acl_node_q.queue); spin_lock_init(&pacl_list->acl_node_q.lock); - /* pmlmeext->bstart_bss = false; */ - start_ap_mode(padapter); } @@ -32,8 +30,6 @@ void free_mlme_ap_info(struct adapter *padapter) struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - /* stop_ap_mode(padapter); */ - pmlmepriv->update_bcn = false; pmlmeext->bstart_bss = false; @@ -377,7 +373,6 @@ void update_bmc_sta(struct adapter *padapter) if (psta) { psta->aid = 0;/* default set to 0 */ - /* psta->mac_id = psta->aid+4; */ psta->mac_id = psta->aid + 1;/* mac_id = 1 for bc/mc stainfo */ pmlmeinfo->FW_sta_info[psta->mac_id].psta = psta; @@ -448,12 +443,6 @@ void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta) struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv; struct ht_priv *phtpriv_sta = &psta->htpriv; u8 cur_ldpc_cap = 0, cur_stbc_cap = 0, cur_beamform_cap = 0; - /* set intf_tag to if1 */ - /* psta->intf_tag = 0; */ - - /* psta->mac_id = psta->aid+4; */ - /* psta->mac_id = psta->aid+1;//alloc macid when call rtw_alloc_stainfo(), */ - /* release macid when call rtw_free_stainfo() */ /* ap mode */ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, true); @@ -544,9 +533,6 @@ void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta) memset(&psta->sta_stats, 0, sizeof(struct stainfo_stats)); - /* add ratid */ - /* add_ratid(padapter, psta); move to ap_sta_info_defer_update() */ - spin_lock_bh(&psta->lock); psta->state |= _FW_LINKED; spin_unlock_bh(&psta->lock); @@ -567,9 +553,6 @@ static void update_ap_info(struct adapter *padapter, struct sta_info *psta) /* HT related cap */ if (phtpriv_ap->ht_option) { - /* check if sta supports rx ampdu */ - /* phtpriv_ap->ampdu_enable = phtpriv_ap->ampdu_enable; */ - /* check if sta support s Short GI 20M */ if ((phtpriv_ap->ht_cap.cap_info) & cpu_to_le16(IEEE80211_HT_CAP_SGI_20)) phtpriv_ap->sgi_20m = true; @@ -621,11 +604,6 @@ static void update_hw_ht_param(struct adapter *padapter) pmlmeinfo->SM_PS = (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & 0x0C) >> 2; - - /* */ - /* Config current HT Protection mode. */ - /* */ - /* pmlmeinfo->HT_protection = pmlmeinfo->HT_info.infos[1] & 0x3; */ } void start_bss_network(struct adapter *padapter) @@ -662,16 +640,11 @@ void start_bss_network(struct adapter *padapter) pmlmeext->bstart_bss = true; /* todo: update wmm, ht cap */ - /* pmlmeinfo->WMM_enable; */ - /* pmlmeinfo->HT_enable; */ if (pmlmepriv->qospriv.qos_option) pmlmeinfo->WMM_enable = true; if (pmlmepriv->htpriv.ht_option) { pmlmeinfo->WMM_enable = true; pmlmeinfo->HT_enable = true; - /* pmlmeinfo->HT_info_enable = true; */ - /* pmlmeinfo->HT_caps_enable = true; */ - update_hw_ht_param(padapter); } @@ -694,7 +667,6 @@ void start_bss_network(struct adapter *padapter) rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VO, (u8 *)(&acparm)); acparm = 0x005E4317; /* VI */ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VI, (u8 *)(&acparm)); - /* acparm = 0x00105320; // BE */ acparm = 0x005ea42b; rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_BE, (u8 *)(&acparm)); acparm = 0x0000A444; /* BK */ @@ -711,18 +683,8 @@ void start_bss_network(struct adapter *padapter) rtw_hal_set_hwreg(padapter, HW_VAR_DO_IQK, NULL); if (!pmlmepriv->cur_network.join_res) { /* setting only at first time */ - /* u32 initialgain; */ - - /* initialgain = 0x1e; */ - - /* disable dynamic functions, such as high power, DIG */ - /* Save_DM_Func_Flag(padapter); */ - /* Switch_DM_Func(padapter, DYNAMIC_FUNC_DISABLE, false); */ - /* turn on all dynamic functions */ Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true); - - /* rtw_hal_set_hwreg(padapter, HW_VAR_INITIAL_GAIN, (u8 *)(&initialgain)); */ } /* set channel, bwmode */ @@ -739,21 +701,17 @@ void start_bss_network(struct adapter *padapter) if ((cbw40_enable) && (pht_info->infos[0] & BIT(2))) { /* switch to the 40M Hz mode */ - /* pmlmeext->cur_bwmode = CHANNEL_WIDTH_40; */ cur_bwmode = CHANNEL_WIDTH_40; switch (pht_info->infos[0] & 0x3) { case 1: - /* pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER; */ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER; break; case 3: - /* pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER; */ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER; break; default: - /* pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE; */ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE; break; } @@ -789,8 +747,6 @@ void start_bss_network(struct adapter *padapter) /* update bc/mc sta_info */ update_bmc_sta(padapter); - - /* pmlmeext->bstart_bss = true; */ } int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) @@ -836,12 +792,9 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) /* beacon interval */ /* ie + 8; 8: TimeStamp, 2: Beacon Interval 2:Capability */ p = rtw_get_beacon_interval_from_ie(ie); - /* pbss_network->configuration.beacon_period = le16_to_cpu(*(unsigned short*)p); */ pbss_network->configuration.beacon_period = get_unaligned_le16(p); /* capability */ - /* cap = *(unsigned short *)rtw_get_capability_from_ie(ie); */ - /* cap = le16_to_cpu(cap); */ cap = get_unaligned_le16(ie); /* SSID */ @@ -1075,12 +1028,6 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) pmlmepriv->htpriv.ht_option = false; - if ((psecuritypriv->wpa2_pairwise_cipher & WPA_CIPHER_TKIP) || - (psecuritypriv->wpa_pairwise_cipher & WPA_CIPHER_TKIP)) { - /* todo: */ - /* ht_cap = false; */ - } - /* ht_cap */ if (pregistrypriv->ht_enable && ht_cap) { pmlmepriv->htpriv.ht_option = true; @@ -1098,7 +1045,6 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pbss_network); /* issue beacon to start bss network */ - /* start_bss_network(padapter, (u8 *)pbss_network); */ rtw_startbss_cmd(padapter, RTW_CMDF_WAIT_ACK); /* alloc sta_info for ap itself */ @@ -1117,9 +1063,6 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) pmlmepriv->cur_network.join_res = true;/* for check if already set beacon */ - /* update bc/mc sta_info */ - /* update_bmc_sta(padapter); */ - return ret; } @@ -1475,14 +1418,12 @@ void update_beacon(struct adapter *padapter, u8 ie_id, u8 *oui, u8 tx) { struct mlme_priv *pmlmepriv; struct mlme_ext_priv *pmlmeext; - /* struct mlme_ext_info *pmlmeinfo; */ if (!padapter) return; pmlmepriv = &padapter->mlmepriv; pmlmeext = &padapter->mlmeextpriv; - /* pmlmeinfo = &(pmlmeext->mlmext_info); */ if (!pmlmeext->bstart_bss) return; @@ -1540,10 +1481,8 @@ void update_beacon(struct adapter *padapter, u8 ie_id, u8 *oui, u8 tx) spin_unlock_bh(&pmlmepriv->bcn_update_lock); - if (tx) { - /* send_beacon(padapter);//send_beacon must execute on TSR level */ + if (tx) set_tx_beacon_cmd(padapter); - } } /* @@ -1847,8 +1786,6 @@ u8 ap_free_sta(struct adapter *padapter, psta->htpriv.agg_enable_bitmap = 0x0;/* reset */ psta->htpriv.candidate_tid_bitmap = 0x0;/* reset */ - /* report_del_sta_event(padapter, psta->hwaddr, reason); */ - /* clear cam entry / key */ rtw_clearstakey_cmd(padapter, psta, true); @@ -1888,9 +1825,7 @@ void rtw_sta_flush(struct adapter *padapter) list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; - /* spin_unlock_bh(&pstapriv->asoc_list_lock); */ ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING); - /* spin_lock_bh(&pstapriv->asoc_list_lock); */ } spin_unlock_bh(&pstapriv->asoc_list_lock); @@ -2017,7 +1952,6 @@ void start_ap_mode(struct adapter *padapter) pmlmepriv->update_bcn = false; - /* init_mlme_ap_info(padapter); */ pmlmeext->bstart_bss = false; pmlmepriv->num_sta_non_erp = 0; From 2143a136db62b353b18fed69327a3a3391ff5bc8 Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Tue, 31 Mar 2026 21:13:57 -0500 Subject: [PATCH 0130/5214] staging: rtl8723bs: Rename pHT_info_ie to ht_info_ie Convert variable pHT_info_ie to ht_info_ie to comply with snake_case and to remove Hungarian notation. Signed-off-by: Ethan Tidmore Link: https://patch.msgid.link/20260401021357.1176600-3-ethantidmore06@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 1b67c5883a66..f213bb8e173c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -754,7 +754,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) int ret = _SUCCESS; u8 *p; u8 *ht_caps_ie = NULL; - u8 *pHT_info_ie = NULL; + u8 *ht_info_ie = NULL; struct sta_info *psta = NULL; u16 cap, ht_cap = false; uint ie_len = 0; @@ -1007,7 +1007,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) &ie_len, (pbss_network->ie_length - _BEACON_IE_OFFSET_)); if (p && ie_len > 0) - pHT_info_ie = p; + ht_info_ie = p; switch (network_type) { case WIRELESS_11B: @@ -1038,7 +1038,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) HT_caps_handler(padapter, (struct ndis_80211_var_ie *)ht_caps_ie); - HT_info_handler(padapter, (struct ndis_80211_var_ie *)pHT_info_ie); + HT_info_handler(padapter, (struct ndis_80211_var_ie *)ht_info_ie); } pbss_network->length = From 0f8f9ab86998c1f781abd797836b28247a2789c7 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Thu, 2 Apr 2026 18:45:50 +0300 Subject: [PATCH 0131/5214] staging: rtl8723bs: remove dead code in rtw_io.c Remove the commented variable declarations as they are not used anywhere. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260402-cleanup_rtw_io-v1-1-874b9747de6b@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_io.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_io.c b/drivers/staging/rtl8723bs/core/rtw_io.c index 53ba99e1de00..ff16027ee129 100644 --- a/drivers/staging/rtl8723bs/core/rtw_io.c +++ b/drivers/staging/rtl8723bs/core/rtw_io.c @@ -27,7 +27,6 @@ u8 rtw_read8(struct adapter *adapter, u32 addr) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; u8 (*_read8)(struct intf_hdl *pintfhdl, u32 addr); @@ -39,7 +38,6 @@ u8 rtw_read8(struct adapter *adapter, u32 addr) u16 rtw_read16(struct adapter *adapter, u32 addr) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; u16 (*_read16)(struct intf_hdl *pintfhdl, u32 addr); @@ -51,7 +49,6 @@ u16 rtw_read16(struct adapter *adapter, u32 addr) u32 rtw_read32(struct adapter *adapter, u32 addr) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; u32 (*_read32)(struct intf_hdl *pintfhdl, u32 addr); @@ -63,7 +60,6 @@ u32 rtw_read32(struct adapter *adapter, u32 addr) int rtw_write8(struct adapter *adapter, u32 addr, u8 val) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val); @@ -78,7 +74,6 @@ int rtw_write8(struct adapter *adapter, u32 addr, u8 val) int rtw_write16(struct adapter *adapter, u32 addr, u16 val) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val); @@ -92,7 +87,6 @@ int rtw_write16(struct adapter *adapter, u32 addr, u16 val) int rtw_write32(struct adapter *adapter, u32 addr, u32 val) { - /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ struct io_priv *pio_priv = &adapter->iopriv; struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val); From c31cf908075a7349263bf2604f49f5425c942a81 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Thu, 2 Apr 2026 18:45:51 +0300 Subject: [PATCH 0132/5214] staging: rtl8723bs: replace tabs used as separators with spaces Replace tabs in variable initialization lines with spaces to make code more consistent. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260402-cleanup_rtw_io-v1-2-874b9747de6b@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_io.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_io.c b/drivers/staging/rtl8723bs/core/rtw_io.c index ff16027ee129..c793ae7ea5c4 100644 --- a/drivers/staging/rtl8723bs/core/rtw_io.c +++ b/drivers/staging/rtl8723bs/core/rtw_io.c @@ -28,7 +28,7 @@ u8 rtw_read8(struct adapter *adapter, u32 addr) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; u8 (*_read8)(struct intf_hdl *pintfhdl, u32 addr); _read8 = pintfhdl->io_ops._read8; @@ -39,7 +39,7 @@ u8 rtw_read8(struct adapter *adapter, u32 addr) u16 rtw_read16(struct adapter *adapter, u32 addr) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; u16 (*_read16)(struct intf_hdl *pintfhdl, u32 addr); _read16 = pintfhdl->io_ops._read16; @@ -50,7 +50,7 @@ u16 rtw_read16(struct adapter *adapter, u32 addr) u32 rtw_read32(struct adapter *adapter, u32 addr) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; u32 (*_read32)(struct intf_hdl *pintfhdl, u32 addr); _read32 = pintfhdl->io_ops._read32; @@ -61,7 +61,7 @@ u32 rtw_read32(struct adapter *adapter, u32 addr) int rtw_write8(struct adapter *adapter, u32 addr, u8 val) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val); int ret; @@ -75,7 +75,7 @@ int rtw_write8(struct adapter *adapter, u32 addr, u8 val) int rtw_write16(struct adapter *adapter, u32 addr, u16 val) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val); int ret; @@ -88,7 +88,7 @@ int rtw_write16(struct adapter *adapter, u32 addr, u16 val) int rtw_write32(struct adapter *adapter, u32 addr, u32 val) { struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; int (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val); int ret; @@ -103,7 +103,7 @@ u32 rtw_write_port(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem) { u32 (*_write_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pmem); struct io_priv *pio_priv = &adapter->iopriv; - struct intf_hdl *pintfhdl = &pio_priv->intf; + struct intf_hdl *pintfhdl = &pio_priv->intf; _write_port = pintfhdl->io_ops._write_port; From b3e254c620858197fdb843be11c59b3fff660884 Mon Sep 17 00:00:00 2001 From: Xiyuan Guo Date: Thu, 2 Apr 2026 21:18:59 -0400 Subject: [PATCH 0133/5214] staging: rtl8723bs: fix include guard comment in rtw_cmd.h The #endif comment at the bottom of the file mistakenly referred to _CMD_H_ instead of the actual include guard macro __RTW_CMD_H_ defined at the top of the file. Signed-off-by: Xiyuan Guo Link: https://patch.msgid.link/20260403011859.307665-1-tommyguo039@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_cmd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h index c4c3edee809d..3014fcf7bb85 100644 --- a/drivers/staging/rtl8723bs/include/rtw_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h @@ -712,4 +712,4 @@ enum { #define _GetRFReg_CMD_ _Read_RFREG_CMD_ #define _SetRFReg_CMD_ _Write_RFREG_CMD_ -#endif /* _CMD_H_ */ +#endif /* __RTW_CMD_H_ */ From 9d3fb34054c383cb814e45b7edb364a74c4203f5 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:03:10 +0200 Subject: [PATCH 0134/5214] staging: rtl8723bs: remove multiple blank lines in rtw_btcoex.c Removes multiple blank lines in rtw_btcoex.c. Discovered and verified using checkpatch.pl Signed-off-by: Linus Probert Link: https://patch.msgid.link/20260403220310.1824969-3-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 0191a943f0a4..27dfd00b4249 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -65,7 +65,6 @@ void rtw_btcoex_LPS_Leave(struct adapter *padapter) { struct pwrctrl_priv *pwrpriv; - pwrpriv = adapter_to_pwrctl(padapter); if (pwrpriv->pwr_mode != PS_MODE_ACTIVE) { From a2e327e2d0ef61ab6584cf4a0e9767dcf127df5a Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 2 Apr 2026 23:13:16 +0000 Subject: [PATCH 0135/5214] staging: rtl8723bs: remove unnecessary parentheses in os_intfs.c Remove redundant parentheses around &padapter->xmitpriv and &padapter->recvpriv. The parentheses are unnecessary and removing them improves readability. No functional change. Signed-off-by: Hungyu Lin Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260402231316.4243-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index e943dcea1a21..e843e9cf681a 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -288,8 +288,8 @@ static int rtw_net_set_mac_address(struct net_device *pnetdev, void *p) static struct net_device_stats *rtw_net_get_stats(struct net_device *pnetdev) { struct adapter *padapter = rtw_netdev_priv(pnetdev); - struct xmit_priv *pxmitpriv = &(padapter->xmitpriv); - struct recv_priv *precvpriv = &(padapter->recvpriv); + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + struct recv_priv *precvpriv = &padapter->recvpriv; padapter->stats.tx_packets = pxmitpriv->tx_pkts;/* pxmitpriv->tx_pkts++; */ padapter->stats.rx_packets = precvpriv->rx_pkts;/* precvpriv->rx_pkts++; */ From 885b1d8379323aa1777d021d4b0315871dca12eb Mon Sep 17 00:00:00 2001 From: Paarth Mahadik Date: Sat, 4 Apr 2026 12:56:26 +0530 Subject: [PATCH 0136/5214] staging: rtl8723bs: fix logical continuation style Logical continuation should be on the previous line, move && to the end of the preceding line and align the continuation with the opening parenthesis. Signed-off-by: Paarth Mahadik Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260404072626.134642-1-paarth.mahadik@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 27dfd00b4249..055ca95ff5a7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -10,8 +10,8 @@ void rtw_btcoex_MediaStatusNotify(struct adapter *padapter, u8 media_status) { - if ((media_status == RT_MEDIA_CONNECT) - && (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) { + if ((media_status == RT_MEDIA_CONNECT) && + (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) { rtw_hal_set_hwreg(padapter, HW_VAR_DL_RSVD_PAGE, NULL); } From ab92b4aabf126dbea25c84dcd64dd319dcfa5c84 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:09 +0000 Subject: [PATCH 0137/5214] staging: rtl8723bs: simplify _rtw_init_xmit_priv control flow Replace goto-based error handling in _rtw_init_xmit_priv() with direct returns to simplify the control flow. No functional changes intended. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-2-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 39 ++++++++--------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 7bce0343d59f..f64ec8e271ed 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -38,7 +38,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) int i; struct xmit_buf *pxmitbuf; struct xmit_frame *pxframe; - signed int res = _SUCCESS; + int res; spin_lock_init(&pxmitpriv->lock); spin_lock_init(&pxmitpriv->lock_sctx); @@ -75,8 +75,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) if (!pxmitpriv->pallocated_frame_buf) { pxmitpriv->pxmit_frame_buf = NULL; - res = _FAIL; - goto exit; + return _FAIL; } pxmitpriv->pxmit_frame_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_frame_buf), 4); @@ -111,10 +110,8 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitpriv->pallocated_xmitbuf = vzalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4); - if (!pxmitpriv->pallocated_xmitbuf) { - res = _FAIL; - goto exit; - } + if (!pxmitpriv->pallocated_xmitbuf) + return _FAIL; pxmitpriv->pxmitbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_xmitbuf), 4); @@ -133,7 +130,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) fsleep(10 * USEC_PER_MSEC); res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); if (res == _FAIL) - goto exit; + return _FAIL; } pxmitbuf->phead = pxmitbuf->pbuf; @@ -162,8 +159,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) if (!pxmitpriv->xframe_ext_alloc_addr) { pxmitpriv->xframe_ext = NULL; - res = _FAIL; - goto exit; + return _FAIL; } pxmitpriv->xframe_ext = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->xframe_ext_alloc_addr), 4); pxframe = (struct xmit_frame *)pxmitpriv->xframe_ext; @@ -194,10 +190,8 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitpriv->pallocated_xmit_extbuf = vzalloc(NR_XMIT_EXTBUFF * sizeof(struct xmit_buf) + 4); - if (!pxmitpriv->pallocated_xmit_extbuf) { - res = _FAIL; - goto exit; - } + if (!pxmitpriv->pallocated_xmit_extbuf) + return _FAIL; pxmitpriv->pxmit_extbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_xmit_extbuf), 4); @@ -211,10 +205,8 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitbuf->buf_tag = XMITBUF_MGNT; res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, MAX_XMIT_EXTBUF_SZ + XMITBUF_ALIGN_SZ, true); - if (res == _FAIL) { - res = _FAIL; - goto exit; - } + if (res == _FAIL) + return _FAIL; pxmitbuf->phead = pxmitbuf->pbuf; pxmitbuf->pend = pxmitbuf->pbuf + MAX_XMIT_EXTBUF_SZ; @@ -243,10 +235,8 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, MAX_CMDBUF_SZ + XMITBUF_ALIGN_SZ, true); - if (res == _FAIL) { - res = _FAIL; - goto exit; - } + if (res == _FAIL) + return _FAIL; pxmitbuf->phead = pxmitbuf->pbuf; pxmitbuf->pend = pxmitbuf->pbuf + MAX_CMDBUF_SZ; @@ -258,7 +248,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = rtw_alloc_hwxmits(padapter); if (res == _FAIL) - goto exit; + return _FAIL; rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); for (i = 0; i < 4; i++) @@ -270,8 +260,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) rtw_hal_init_xmit_priv(padapter); -exit: - return res; + return _SUCCESS; } void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv) From 76b4ffc748101691711b6cff15d112abbd14f2f8 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:10 +0000 Subject: [PATCH 0138/5214] staging: rtl8723bs: make rtw_alloc_hwxmits static The rtw_alloc_hwxmits() function is only used within this file. Make it static to limit its scope. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-3-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 80 ++++++++++---------- drivers/staging/rtl8723bs/include/rtw_xmit.h | 1 - 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index f64ec8e271ed..b86c9c2f3b93 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -33,6 +33,46 @@ void _rtw_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv) INIT_LIST_HEAD(&psta_xmitpriv->apsd); } +static s32 rtw_alloc_hwxmits(struct adapter *padapter) +{ + struct hw_xmit *hwxmits; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + + pxmitpriv->hwxmit_entry = HWXMIT_ENTRY; + + pxmitpriv->hwxmits = NULL; + + pxmitpriv->hwxmits = kzalloc_objs(*hwxmits, pxmitpriv->hwxmit_entry, + GFP_ATOMIC); + if (!pxmitpriv->hwxmits) + return _FAIL; + + hwxmits = pxmitpriv->hwxmits; + + if (pxmitpriv->hwxmit_entry == 5) { + hwxmits[0] .sta_queue = &pxmitpriv->bm_pending; + + hwxmits[1] .sta_queue = &pxmitpriv->vo_pending; + + hwxmits[2] .sta_queue = &pxmitpriv->vi_pending; + + hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; + + hwxmits[4] .sta_queue = &pxmitpriv->be_pending; + } else if (pxmitpriv->hwxmit_entry == 4) { + hwxmits[0] .sta_queue = &pxmitpriv->vo_pending; + + hwxmits[1] .sta_queue = &pxmitpriv->vi_pending; + + hwxmits[2] .sta_queue = &pxmitpriv->be_pending; + + hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; + } else { + } + + return _SUCCESS; +} + s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) { int i; @@ -1852,46 +1892,6 @@ s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) return res; } -s32 rtw_alloc_hwxmits(struct adapter *padapter) -{ - struct hw_xmit *hwxmits; - struct xmit_priv *pxmitpriv = &padapter->xmitpriv; - - pxmitpriv->hwxmit_entry = HWXMIT_ENTRY; - - pxmitpriv->hwxmits = NULL; - - pxmitpriv->hwxmits = kzalloc_objs(*hwxmits, pxmitpriv->hwxmit_entry, - GFP_ATOMIC); - if (!pxmitpriv->hwxmits) - return _FAIL; - - hwxmits = pxmitpriv->hwxmits; - - if (pxmitpriv->hwxmit_entry == 5) { - hwxmits[0] .sta_queue = &pxmitpriv->bm_pending; - - hwxmits[1] .sta_queue = &pxmitpriv->vo_pending; - - hwxmits[2] .sta_queue = &pxmitpriv->vi_pending; - - hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; - - hwxmits[4] .sta_queue = &pxmitpriv->be_pending; - } else if (pxmitpriv->hwxmit_entry == 4) { - hwxmits[0] .sta_queue = &pxmitpriv->vo_pending; - - hwxmits[1] .sta_queue = &pxmitpriv->vi_pending; - - hwxmits[2] .sta_queue = &pxmitpriv->be_pending; - - hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; - } else { - } - - return _SUCCESS; -} - void rtw_free_hwxmits(struct adapter *padapter) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index 544468f57692..b0189a703d28 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -457,7 +457,6 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter); void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv); -s32 rtw_alloc_hwxmits(struct adapter *padapter); void rtw_free_hwxmits(struct adapter *padapter); From 312989a3b030bbe8749f271713c67f67d0617cf9 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:11 +0000 Subject: [PATCH 0139/5214] staging: rtl8723bs: convert rtw_alloc_hwxmits to return errno Convert rtw_alloc_hwxmits() to return 0 on success and -ENOMEM on failure. Update the caller to check for non-zero return values. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-4-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index b86c9c2f3b93..befe74a1bc4c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -45,7 +45,7 @@ static s32 rtw_alloc_hwxmits(struct adapter *padapter) pxmitpriv->hwxmits = kzalloc_objs(*hwxmits, pxmitpriv->hwxmit_entry, GFP_ATOMIC); if (!pxmitpriv->hwxmits) - return _FAIL; + return -ENOMEM; hwxmits = pxmitpriv->hwxmits; @@ -70,7 +70,7 @@ static s32 rtw_alloc_hwxmits(struct adapter *padapter) } else { } - return _SUCCESS; + return 0; } s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) @@ -287,7 +287,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) } res = rtw_alloc_hwxmits(padapter); - if (res == _FAIL) + if (res) return _FAIL; rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); From e7c3d33739e1e0d3159916bd30826d1ee9bb9477 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:12 +0000 Subject: [PATCH 0140/5214] staging: rtl8723bs: move rtw_os_xmit_resource_alloc to rtw_xmit.c Move rtw_os_xmit_resource_alloc() into core/rtw_xmit.c and make it static so the xmit init helpers live together. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-5-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 13 +++++++++++++ drivers/staging/rtl8723bs/include/xmit_osdep.h | 1 - drivers/staging/rtl8723bs/os_dep/xmit_linux.c | 13 ------------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index befe74a1bc4c..249947a7d910 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -73,6 +73,19 @@ static s32 rtw_alloc_hwxmits(struct adapter *padapter) return 0; } +static int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz, u8 flag) +{ + if (alloc_sz > 0) { + pxmitbuf->pallocated_buf = kzalloc(alloc_sz, GFP_KERNEL); + if (!pxmitbuf->pallocated_buf) + return _FAIL; + + pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); + } + + return _SUCCESS; +} + s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) { int i; diff --git a/drivers/staging/rtl8723bs/include/xmit_osdep.h b/drivers/staging/rtl8723bs/include/xmit_osdep.h index 880344bffe2f..5b351652e31b 100644 --- a/drivers/staging/rtl8723bs/include/xmit_osdep.h +++ b/drivers/staging/rtl8723bs/include/xmit_osdep.h @@ -30,7 +30,6 @@ extern netdev_tx_t rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetde void rtw_os_xmit_schedule(struct adapter *padapter); -int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz, u8 flag); void rtw_os_xmit_resource_free(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 free_sz, u8 flag); extern uint rtw_remainder_len(struct pkt_file *pfile); diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index dc0b77f38b1a..54a6d54400e2 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -46,19 +46,6 @@ signed int rtw_endofpktfile(struct pkt_file *pfile) return false; } -int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz, u8 flag) -{ - if (alloc_sz > 0) { - pxmitbuf->pallocated_buf = kzalloc(alloc_sz, GFP_KERNEL); - if (!pxmitbuf->pallocated_buf) - return _FAIL; - - pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); - } - - return _SUCCESS; -} - void rtw_os_xmit_resource_free(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 free_sz, u8 flag) { if (free_sz > 0) From f5be742ee90dbbd3f6ed196ad42b5fae4e0d096c Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:13 +0000 Subject: [PATCH 0141/5214] staging: rtl8723bs: convert rtw_os_xmit_resource_alloc to return errno Convert rtw_os_xmit_resource_alloc() to return 0 on success and -ENOMEM on failure. Update the callers to check for non-zero return values. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-6-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 249947a7d910..fedfc968057b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -78,12 +78,12 @@ static int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf if (alloc_sz > 0) { pxmitbuf->pallocated_buf = kzalloc(alloc_sz, GFP_KERNEL); if (!pxmitbuf->pallocated_buf) - return _FAIL; + return -ENOMEM; pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); } - return _SUCCESS; + return 0; } s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) @@ -179,10 +179,10 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) /* Tx buf allocation may fail sometimes, so sleep and retry. */ res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); - if (res == _FAIL) { + if (res) { fsleep(10 * USEC_PER_MSEC); res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); - if (res == _FAIL) + if (res) return _FAIL; } @@ -258,7 +258,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitbuf->buf_tag = XMITBUF_MGNT; res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, MAX_XMIT_EXTBUF_SZ + XMITBUF_ALIGN_SZ, true); - if (res == _FAIL) + if (res) return _FAIL; pxmitbuf->phead = pxmitbuf->pbuf; @@ -288,7 +288,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, MAX_CMDBUF_SZ + XMITBUF_ALIGN_SZ, true); - if (res == _FAIL) + if (res) return _FAIL; pxmitbuf->phead = pxmitbuf->pbuf; From b4c71a9978ab26dd5e3f28fb81c2489dc01ca98e Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 8 Apr 2026 11:13:14 +0000 Subject: [PATCH 0142/5214] staging: rtl8723bs: convert _rtw_init_xmit_priv to return errno Convert _rtw_init_xmit_priv() to return 0 on success and negative error codes on failure. Update the caller to check for non-zero return values. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260408111314.19329-7-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 18 +++++++++--------- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 5 ++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index fedfc968057b..c6cf23b9c98a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -128,7 +128,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) if (!pxmitpriv->pallocated_frame_buf) { pxmitpriv->pxmit_frame_buf = NULL; - return _FAIL; + return -ENOMEM; } pxmitpriv->pxmit_frame_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_frame_buf), 4); @@ -164,7 +164,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitpriv->pallocated_xmitbuf = vzalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4); if (!pxmitpriv->pallocated_xmitbuf) - return _FAIL; + return -ENOMEM; pxmitpriv->pxmitbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_xmitbuf), 4); @@ -183,7 +183,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) fsleep(10 * USEC_PER_MSEC); res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); if (res) - return _FAIL; + return res; } pxmitbuf->phead = pxmitbuf->pbuf; @@ -212,7 +212,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) if (!pxmitpriv->xframe_ext_alloc_addr) { pxmitpriv->xframe_ext = NULL; - return _FAIL; + return -ENOMEM; } pxmitpriv->xframe_ext = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->xframe_ext_alloc_addr), 4); pxframe = (struct xmit_frame *)pxmitpriv->xframe_ext; @@ -244,7 +244,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) pxmitpriv->pallocated_xmit_extbuf = vzalloc(NR_XMIT_EXTBUFF * sizeof(struct xmit_buf) + 4); if (!pxmitpriv->pallocated_xmit_extbuf) - return _FAIL; + return -ENOMEM; pxmitpriv->pxmit_extbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitpriv->pallocated_xmit_extbuf), 4); @@ -259,7 +259,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, MAX_XMIT_EXTBUF_SZ + XMITBUF_ALIGN_SZ, true); if (res) - return _FAIL; + return res; pxmitbuf->phead = pxmitbuf->pbuf; pxmitbuf->pend = pxmitbuf->pbuf + MAX_XMIT_EXTBUF_SZ; @@ -289,7 +289,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) MAX_CMDBUF_SZ + XMITBUF_ALIGN_SZ, true); if (res) - return _FAIL; + return res; pxmitbuf->phead = pxmitbuf->pbuf; pxmitbuf->pend = pxmitbuf->pbuf + MAX_CMDBUF_SZ; @@ -301,7 +301,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = rtw_alloc_hwxmits(padapter); if (res) - return _FAIL; + return res; rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); for (i = 0; i < 4; i++) @@ -313,7 +313,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) rtw_hal_init_xmit_priv(padapter); - return _SUCCESS; + return 0; } void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv) diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index e843e9cf681a..0f8fda08138e 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -636,6 +636,8 @@ void rtw_reset_drv_sw(struct adapter *padapter) u8 rtw_init_drv_sw(struct adapter *padapter) { + int res; + rtw_init_default_value(padapter); rtw_init_hal_com_default_value(padapter); @@ -653,7 +655,8 @@ u8 rtw_init_drv_sw(struct adapter *padapter) init_mlme_ext_priv(padapter); - if (_rtw_init_xmit_priv(&padapter->xmitpriv, padapter) == _FAIL) + res = _rtw_init_xmit_priv(&padapter->xmitpriv, padapter); + if (res) goto free_mlme_ext; if (_rtw_init_recv_priv(&padapter->recvpriv, padapter) == _FAIL) From 6801cdb0f7b4fd6d35faae486682f3d75737cd18 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:21 +0200 Subject: [PATCH 0143/5214] staging: rtl8723bs: rename global function Efuse_CalculateWordCnts Renames the function Efuse_CalculateWordCnts to rtw_efuse_calculate_word_counts in order to conform to linux code style. Discovered with checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-2-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 4 ++-- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 099320e4eb50..7fb0c4ad7583 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -10,7 +10,7 @@ /* 11/16/2008 MH Add description. Get current efuse area enabled word!!. */ u8 -Efuse_CalculateWordCnts(u8 word_en) +rtw_efuse_calculate_word_counts(u8 word_en) { u8 word_cnts = 0; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index e794fe3caf9d..652127af736d 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -687,7 +687,7 @@ static void hal_ReadEFuse_WiFi( addr += 2; } } else { - eFuse_Addr += Efuse_CalculateWordCnts(wden)*2; + eFuse_Addr += rtw_efuse_calculate_word_counts(wden)*2; } } @@ -779,7 +779,7 @@ static void hal_ReadEFuse_BT( addr += 2; } } else { - eFuse_Addr += Efuse_CalculateWordCnts(wden)*2; + eFuse_Addr += rtw_efuse_calculate_word_counts(wden)*2; } } diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 191ffdf593d4..df430c9fd25b 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -68,7 +68,7 @@ struct efuse_hal { u8 fakeBTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN]; }; -u8 Efuse_CalculateWordCnts(u8 word_en); +u8 rtw_efuse_calculate_word_counts(u8 word_en); u8 efuse_OneByteRead(struct adapter *padapter, u16 addr, u8 *data); u8 EFUSE_Read1Byte(struct adapter *padapter, u16 Address); From 8cb5425cd8ed0f3c7732fd0752b6258f082ad590 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:22 +0200 Subject: [PATCH 0144/5214] staging: rtl8723bs: efuse_OneByteRead() -> rtw_efuse_one_byte_read() Renames efuse_OneByteRead to rtw_efuse_one_byte_read in order to conform to kernel coding style. Discovered using the checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-3-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 2 +- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 22 +++++++++++-------- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 7fb0c4ad7583..a7b120cc70a3 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -83,7 +83,7 @@ u16 Address) /* 11/16/2008 MH Read one byte from real Efuse. */ u8 -efuse_OneByteRead( +rtw_efuse_one_byte_read( struct adapter *padapter, u16 addr, u8 *data) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 652127af736d..355167883f08 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -651,7 +651,7 @@ static void hal_ReadEFuse_WiFi( hal_EfuseSwitchToBank(padapter, 0); while (AVAILABLE_EFUSE_ADDR(eFuse_Addr)) { - efuse_OneByteRead(padapter, eFuse_Addr++, &efuseHeader); + rtw_efuse_one_byte_read(padapter, eFuse_Addr++, &efuseHeader); if (efuseHeader == 0xFF) break; @@ -659,7 +659,7 @@ static void hal_ReadEFuse_WiFi( if (EXT_HEADER(efuseHeader)) { /* extended header */ offset = GET_HDR_OFFSET_2_0(efuseHeader); - efuse_OneByteRead(padapter, eFuse_Addr++, &efuseExtHdr); + rtw_efuse_one_byte_read(padapter, eFuse_Addr++, &efuseExtHdr); if (ALL_WORDS_DISABLED(efuseExtHdr)) continue; @@ -678,10 +678,12 @@ static void hal_ReadEFuse_WiFi( for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { /* Check word enable condition in the section */ if (!(wden & (0x01< Date: Sat, 4 Apr 2026 00:33:23 +0200 Subject: [PATCH 0145/5214] staging: rtl8723bs: rename EFUSE_Read1Byte() to rtw_efuse_read_1_byte() Renames EFUSE_Read1Byte to rtw_efuse_read_1_byte in order to conform to kernel code style. Discovered using checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-4-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 6 +++--- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 3 ++- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index a7b120cc70a3..d6212b3b0b1d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -26,7 +26,7 @@ rtw_efuse_calculate_word_counts(u8 word_en) } /*----------------------------------------------------------------------------- - * Function: EFUSE_Read1Byte + * Function: rtw_efuse_read_1_byte * * Overview: Copy from WMAC fot EFUSE read 1 byte. * @@ -42,7 +42,7 @@ rtw_efuse_calculate_word_counts(u8 word_en) * */ u8 -EFUSE_Read1Byte( +rtw_efuse_read_1_byte( struct adapter *Adapter, u16 Address) { @@ -79,7 +79,7 @@ u16 Address) } else return 0xFF; -} /* EFUSE_Read1Byte */ +} /* rtw_efuse_read_1_byte */ /* 11/16/2008 MH Read one byte from real Efuse. */ u8 diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 355167883f08..941c2ebef9e2 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -1559,7 +1559,8 @@ void Hal_ReadRFGainOffset( if (!AutoloadFail) { Adapter->eeprompriv.EEPROMRFGainOffset = PROMContent[EEPROM_RF_GAIN_OFFSET]; - Adapter->eeprompriv.EEPROMRFGainVal = EFUSE_Read1Byte(Adapter, EEPROM_RF_GAIN_VAL); + Adapter->eeprompriv.EEPROMRFGainVal = rtw_efuse_read_1_byte(Adapter, + EEPROM_RF_GAIN_VAL); } else { Adapter->eeprompriv.EEPROMRFGainOffset = 0; Adapter->eeprompriv.EEPROMRFGainVal = 0xFF; diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index b8cd9e694f56..7b78fd5e583b 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -71,7 +71,7 @@ struct efuse_hal { u8 rtw_efuse_calculate_word_counts(u8 word_en); u8 rtw_efuse_one_byte_read(struct adapter *padapter, u16 addr, u8 *data); -u8 EFUSE_Read1Byte(struct adapter *padapter, u16 Address); +u8 rtw_efuse_read_1_byte(struct adapter *padapter, u16 Address); void EFUSE_ShadowMapUpdate(struct adapter *padapter, u8 efuseType); void EFUSE_ShadowRead(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value); void Rtw_Hal_ReadMACAddrFromFile(struct adapter *padapter); From 3f188a336f95824f5d46532fb7c56c1c6faacb6c Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:24 +0200 Subject: [PATCH 0146/5214] staging: rtl8723bs: EFUSE_ShadowMapUpdate -> rtw_efuse_shadow_map_update Renames EFUSE_ShadowMapUpdate to rtw_efuse_shadow_map_update in order to conform to kernel code style. Discovered using checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-5-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 6 +++--- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 4 ++-- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index d6212b3b0b1d..f873250e0f7e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -201,7 +201,7 @@ static void efuse_ShadowRead4Byte(struct adapter *padapter, u16 Offset, u32 *Val } /* efuse_ShadowRead4Byte */ /*----------------------------------------------------------------------------- - * Function: EFUSE_ShadowMapUpdate + * Function: rtw_efuse_shadow_map_update * * Overview: Transfer current EFUSE content to shadow init and modify map. * @@ -216,7 +216,7 @@ static void efuse_ShadowRead4Byte(struct adapter *padapter, u16 Offset, u32 *Val * 11/13/2008 MHC Create Version 0. * */ -void EFUSE_ShadowMapUpdate(struct adapter *padapter, u8 efuseType) +void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType) { struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); u16 mapLen = 0; @@ -230,7 +230,7 @@ void EFUSE_ShadowMapUpdate(struct adapter *padapter, u8 efuseType) /* PlatformMoveMemory((void *)&pHalData->EfuseMap[EFUSE_MODIFY_MAP][0], */ /* void *)&pHalData->EfuseMap[EFUSE_INIT_MAP][0], mapLen); */ -} /* EFUSE_ShadowMapUpdate */ +} /* rtw_efuse_shadow_map_update */ /*----------------------------------------------------------------------------- diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 941c2ebef9e2..3dff8aa2badb 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -1188,12 +1188,12 @@ void Hal_InitPGData(struct adapter *padapter, u8 *PROMContent) if (!pEEPROM->bautoload_fail_flag) { /* autoload OK. */ if (!pEEPROM->EepromOrEfuse) { /* Read EFUSE real map to shadow. */ - EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI); + rtw_efuse_shadow_map_update(padapter, EFUSE_WIFI); memcpy(PROMContent, pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); } } else {/* autoload fail */ if (!pEEPROM->EepromOrEfuse) - EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI); + rtw_efuse_shadow_map_update(padapter, EFUSE_WIFI); memcpy(PROMContent, pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); } } diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 7b78fd5e583b..e73f9396e9ca 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -72,7 +72,7 @@ u8 rtw_efuse_calculate_word_counts(u8 word_en); u8 rtw_efuse_one_byte_read(struct adapter *padapter, u16 addr, u8 *data); u8 rtw_efuse_read_1_byte(struct adapter *padapter, u16 Address); -void EFUSE_ShadowMapUpdate(struct adapter *padapter, u8 efuseType); +void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType); void EFUSE_ShadowRead(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value); void Rtw_Hal_ReadMACAddrFromFile(struct adapter *padapter); u32 Rtw_Hal_readPGDataFromConfigFile(struct adapter *padapter); From 7a590f2660887a88f6b97eaeac22cfdfe9569a39 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:25 +0200 Subject: [PATCH 0147/5214] staging: rtl8723bs: rename EFUSE_ShadowRead() to rtw_efuse_shadow_read() Renames EFUSE_ShadowRead to rtw_efuse_shadow_read to conform to kernel code style. Discovered using checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-6-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 6 +++--- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 2 +- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index f873250e0f7e..94b00d3c61cf 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -234,7 +234,7 @@ void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType) /*----------------------------------------------------------------------------- - * Function: EFUSE_ShadowRead + * Function: rtw_efuse_shadow_read * * Overview: Read from efuse init map !!!!! * @@ -249,7 +249,7 @@ void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType) * 11/12/2008 MHC Create Version 0. * */ -void EFUSE_ShadowRead(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value) +void rtw_efuse_shadow_read(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value) { if (Type == 1) efuse_ShadowRead1Byte(padapter, Offset, (u8 *)Value); @@ -258,4 +258,4 @@ void EFUSE_ShadowRead(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value) else if (Type == 4) efuse_ShadowRead4Byte(padapter, Offset, (u32 *)Value); -} /* EFUSE_ShadowRead*/ +} /* rtw_efuse_shadow_read*/ diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index f2f73c65a636..3b3fac981a27 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -574,7 +574,7 @@ static bool HalDetectPwrDownMode(struct adapter *Adapter) struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(Adapter); - EFUSE_ShadowRead(Adapter, 1, 0x7B/*EEPROM_RF_OPT3_92C*/, (u32 *)&tmpvalue); + rtw_efuse_shadow_read(Adapter, 1, 0x7B/*EEPROM_RF_OPT3_92C*/, (u32 *)&tmpvalue); /* 2010/08/25 MH INF priority > PDN Efuse value. */ if (tmpvalue & BIT4 && pwrctrlpriv->reg_pdnmode) diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index e73f9396e9ca..1012831c7eb5 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -73,7 +73,7 @@ u8 rtw_efuse_one_byte_read(struct adapter *padapter, u16 addr, u8 *data); u8 rtw_efuse_read_1_byte(struct adapter *padapter, u16 Address); void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType); -void EFUSE_ShadowRead(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value); +void rtw_efuse_shadow_read(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value); void Rtw_Hal_ReadMACAddrFromFile(struct adapter *padapter); u32 Rtw_Hal_readPGDataFromConfigFile(struct adapter *padapter); From 33c453a4c20ba0a53309753528441a4004465d54 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:26 +0200 Subject: [PATCH 0148/5214] staging: rtl8723bs: remove two unused function prototypes Removes two unused functions Rtw_Hal_ReadMACAddrFromFile and Rtw_Hal_readPGDataFromConfigFile from rtw_efuse.h. The functions only existed in this header. No implementation or calls were present in the code. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-7-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 1012831c7eb5..4e8980d86965 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -74,7 +74,5 @@ u8 rtw_efuse_one_byte_read(struct adapter *padapter, u16 addr, u8 *data); u8 rtw_efuse_read_1_byte(struct adapter *padapter, u16 Address); void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType); void rtw_efuse_shadow_read(struct adapter *padapter, u8 Type, u16 Offset, u32 *Value); -void Rtw_Hal_ReadMACAddrFromFile(struct adapter *padapter); -u32 Rtw_Hal_readPGDataFromConfigFile(struct adapter *padapter); #endif From 3ec28f0550f0981a1304fc70c07bf22a077155b1 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Sat, 4 Apr 2026 00:33:27 +0200 Subject: [PATCH 0149/5214] staging: rtl8723bs: remove space before tab Removes a space before tab according to kernel code style. Discovered using the checkpatch.pl tool. Signed-off-by: Linus Probert Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260403223327.1831215-8-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_efuse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 4e8980d86965..808ba94a5998 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -31,7 +31,7 @@ enum { #define EFUSE_REPEAT_THRESHOLD_ 3 /* */ -/* The following is for BT Efuse definition */ +/* The following is for BT Efuse definition */ /* */ #define EFUSE_BT_MAX_MAP_LEN 1024 #define EFUSE_MAX_BANK 4 From b8b6a662bc02692638a86b9dac7b675768baa26e Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:26 +0300 Subject: [PATCH 0150/5214] staging: rtl8723bs: remove unnecessary rtw_bug_check() function Remove the rtw_bug_check() function as it does nothing and always returns 'true', making any checks on its result meaningless. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-2-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme.c | 10 ---------- drivers/staging/rtl8723bs/include/osdep_service.h | 9 --------- 2 files changed, 19 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index ddfc56f0253d..8e9387ad2810 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -351,9 +351,6 @@ int is_same_network(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst, u8 fea u16 s_cap, d_cap; __le16 tmps, tmpd; - if (rtw_bug_check(dst, src, &s_cap, &d_cap) == false) - return false; - memcpy((u8 *)&tmps, rtw_get_capability_from_ie(src->ies), 2); memcpy((u8 *)&tmpd, rtw_get_capability_from_ie(dst->ies), 2); @@ -457,11 +454,6 @@ static void update_current_network(struct adapter *adapter, struct wlan_bssid_ex { struct mlme_priv *pmlmepriv = &adapter->mlmepriv; - rtw_bug_check(&pmlmepriv->cur_network.network, - &pmlmepriv->cur_network.network, - &pmlmepriv->cur_network.network, - &pmlmepriv->cur_network.network); - if (check_fwstate(pmlmepriv, _FW_LINKED) && (is_same_network(&pmlmepriv->cur_network.network, pnetwork, 0))) { update_network(&pmlmepriv->cur_network.network, pnetwork, adapter, true); rtw_update_protection(adapter, (pmlmepriv->cur_network.network.ies) + sizeof(struct ndis_802_11_fix_ie), @@ -486,8 +478,6 @@ void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *t list_for_each(plist, phead) { pnetwork = list_entry(plist, struct wlan_network, list); - rtw_bug_check(pnetwork, pnetwork, pnetwork, pnetwork); - if (is_same_network(&pnetwork->network, target, feature)) { target_find = 1; break; diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 955e8678dc26..9392af858995 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -69,15 +69,6 @@ static inline void flush_signals_thread(void) } #define rtw_warn_on(condition) WARN_ON(condition) - -static inline int rtw_bug_check(void *parg1, void *parg2, void *parg3, void *parg4) -{ - int ret = true; - - return ret; - -} - #define _RND(sz, r) ((((sz)+((r)-1))/(r))*(r)) extern void rtw_free_netdev(struct net_device *netdev); From 67aa92b0956d3309c8a9fcd3aa142aaf9a0a2218 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:27 +0300 Subject: [PATCH 0151/5214] staging: rtl8723bs: remove unused _rtw_init_queue() function header The _rtw_init_queue() function header is declared in osdep_service.h, but it is not defined anywhere and all the code using it is commented out, so we can remove the header and the commented out code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-3-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_recv.c | 3 --- drivers/staging/rtl8723bs/core/rtw_sta_mgt.c | 1 - drivers/staging/rtl8723bs/core/rtw_xmit.c | 4 ---- drivers/staging/rtl8723bs/include/osdep_service.h | 2 -- 4 files changed, 10 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index f78194d508df..df9c59449de5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -21,9 +21,6 @@ void _rtw_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv) spin_lock_init(&psta_recvpriv->lock); - /* for (i = 0; iblk_strms[i]); */ - INIT_LIST_HEAD(&psta_recvpriv->defrag_q.queue); spin_lock_init(&psta_recvpriv->defrag_q.lock); } diff --git a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c index 07a6db1d2317..1e5bc9f4cd4b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c +++ b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c @@ -67,7 +67,6 @@ u32 _rtw_init_sta_priv(struct sta_priv *pstapriv) spin_lock_init(&pstapriv->sta_hash_lock); - /* _rtw_init_queue(&pstapriv->asoc_q); */ pstapriv->asoc_sta_count = 0; INIT_LIST_HEAD(&pstapriv->sleep_q.queue); spin_lock_init(&pstapriv->sleep_q.lock); diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index c6cf23b9c98a..9320ce86f1b9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -98,10 +98,6 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) init_completion(&pxmitpriv->xmit_comp); init_completion(&pxmitpriv->terminate_xmitthread_comp); - /* - * Please insert all the queue initialization using _rtw_init_queue below - */ - pxmitpriv->adapter = padapter; INIT_LIST_HEAD(&pxmitpriv->be_pending.queue); diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 9392af858995..664c67e48f06 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -60,8 +60,6 @@ int _rtw_netif_rx(struct net_device *ndev, struct sk_buff *skb); #define rtw_netif_rx(ndev, skb) _rtw_netif_rx(ndev, skb) -extern void _rtw_init_queue(struct __queue *pqueue); - static inline void flush_signals_thread(void) { if (signal_pending(current)) From e387a9ef06ca1a8be171874915c5d27bbd9ee7d1 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:28 +0300 Subject: [PATCH 0152/5214] staging: rtl8723bs: remove the header of non-existent _kfree() function Remove the _kfree() function header, as it is not defined anywhere and is not called anywhere. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-4-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/osdep_service.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 664c67e48f06..82def452b335 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -54,8 +54,6 @@ extern int RTW_STATUS_CODE(int error_code); -void _kfree(u8 *pbuf, u32 sz); - int _rtw_netif_rx(struct net_device *ndev, struct sk_buff *skb); #define rtw_netif_rx(ndev, skb) _rtw_netif_rx(ndev, skb) From 5c534b5f7fd180d1333aa7b448231dffc07773a0 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:29 +0300 Subject: [PATCH 0153/5214] staging: rtl8723bs: remove unused rtw_sprintf() macro Remove the unused rtw_sprintf() macro, which is a wrapper around the standard Kernel function snprintf(). Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-5-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/osdep_service.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 82def452b335..cd73fa294a56 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -87,10 +87,5 @@ bool rtw_cbuf_push(struct rtw_cbuf *cbuf, void *buf); void *rtw_cbuf_pop(struct rtw_cbuf *cbuf); struct rtw_cbuf *rtw_cbuf_alloc(u32 size); -/* String handler */ -/* - * Write formatted output to sized buffer - */ -#define rtw_sprintf(buf, size, format, arg...) snprintf(buf, size, format, ##arg) #endif From ba46e6205566cea837cfb9b9cef9f13623602bd4 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:30 +0300 Subject: [PATCH 0154/5214] staging: rtl8723bs: remove the rtw_warn_on() macro Remove the rtw_warn_on() macro, which simply calls WARN_ON(), and replace all its uses with the standard WARN_ON(). Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-6-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_mlme.c | 2 +- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_xmit.c | 2 +- drivers/staging/rtl8723bs/hal/hal_com.c | 2 +- drivers/staging/rtl8723bs/include/osdep_service.h | 1 - drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++---- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index c1185c25ed36..72d5905a48e9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -1603,7 +1603,7 @@ static void rtw_btinfo_hdl(struct adapter *adapter, u8 *buf, u16 buf_len) cmd_idx = info->cid; if (info->len > buf_len - 2) { - rtw_warn_on(1); + WARN_ON(1); len = buf_len - 2; } else { len = info->len; diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 8e9387ad2810..3ecf4f979c03 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1697,7 +1697,7 @@ int rtw_select_roaming_candidate(struct mlme_priv *mlme) struct wlan_network *candidate = NULL; if (!mlme->cur_network_scanned) { - rtw_warn_on(1); + WARN_ON(1); return ret; } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 5f00fe282d1b..3d7beb83887d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -3082,7 +3082,7 @@ int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int pow else rtw_hal_macid_wakeup(padapter, psta->mac_id); } else { - rtw_warn_on(1); + WARN_ON(1); } do { @@ -5890,7 +5890,7 @@ int rtw_chk_start_clnt_join(struct adapter *padapter, u8 *ch, u8 *bw, u8 *offset bool connect_allow = true; if (!ch || !bw || !offset) { - rtw_warn_on(1); + WARN_ON(1); connect_allow = false; } diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 6a7c09db4cd9..c04419c9c3fc 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -577,7 +577,7 @@ s16 rtw_camid_alloc(struct adapter *adapter, struct sta_info *sta, u8 kid) netdev_dbg(adapter->pnetdev, FUNC_ADPT_FMT " group key with invalid key id:%u\n", FUNC_ADPT_ARG(adapter), kid); - rtw_warn_on(1); + WARN_ON(1); goto bitmap_handle; } @@ -623,7 +623,7 @@ s16 rtw_camid_alloc(struct adapter *adapter, struct sta_info *sta, u8 kid) netdev_dbg(adapter->pnetdev, FUNC_ADPT_FMT " group key id:%u no room\n", FUNC_ADPT_ARG(adapter), kid); - rtw_warn_on(1); + WARN_ON(1); goto bitmap_handle; } diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 9320ce86f1b9..dba146b696e0 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -2037,7 +2037,7 @@ inline bool xmitframe_hiq_filter(struct xmit_frame *xmitframe) allow = true; else if (registry->hiq_filter == RTW_HIQ_FILTER_DENY_ALL) { } else - rtw_warn_on(1); + WARN_ON(1); return allow; } diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 728a2171fbcb..4e3cd1547e01 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -576,7 +576,7 @@ void SetHwReg(struct adapter *adapter, u8 variable, u8 *val) switch (variable) { case HW_VAR_INIT_RTS_RATE: - rtw_warn_on(1); + WARN_ON(1); break; case HW_VAR_SEC_CFG: { diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index cd73fa294a56..99c26b446f72 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -64,7 +64,6 @@ static inline void flush_signals_thread(void) flush_signals(current); } -#define rtw_warn_on(condition) WARN_ON(condition) #define _RND(sz, r) ((((sz)+((r)-1))/(r))*(r)) extern void rtw_free_netdev(struct net_device *netdev); diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index fd3bae31b0ed..a55c6808755e 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -377,7 +377,7 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) rtw_cfg80211_inform_bss(padapter, cur_network); } else { if (!scanned) { - rtw_warn_on(1); + WARN_ON(1); return; } if (!memcmp(&scanned->network.ssid, &pnetwork->ssid, @@ -385,7 +385,7 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) !memcmp(scanned->network.mac_address, pnetwork->mac_address, ETH_ALEN)) rtw_cfg80211_inform_bss(padapter, scanned); else - rtw_warn_on(1); + WARN_ON(1); } if (!rtw_cfg80211_check_bss(padapter)) @@ -418,7 +418,7 @@ void rtw_cfg80211_indicate_connect(struct adapter *padapter) struct wlan_network *scanned = pmlmepriv->cur_network_scanned; if (!scanned) { - rtw_warn_on(1); + WARN_ON(1); goto check_bss; } @@ -427,7 +427,7 @@ void rtw_cfg80211_indicate_connect(struct adapter *padapter) sizeof(struct ndis_802_11_ssid))) rtw_cfg80211_inform_bss(padapter, scanned); else - rtw_warn_on(1); + WARN_ON(1); } check_bss: From db700421536a82b78d69dfa35317644200458557 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 7 Apr 2026 17:33:31 +0300 Subject: [PATCH 0155/5214] staging: rtl8723bs: remove unused BIT33..BIT36 macros These bit definitions are not used anywhere in the driver. Removing them clears the header and eliminates dead code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260407143622.9767-7-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/osdep_service.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 99c26b446f72..86b321e2081d 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -47,10 +47,6 @@ #define BIT30 0x40000000 #define BIT31 0x80000000 #define BIT32 0x0100000000 -#define BIT33 0x0200000000 -#define BIT34 0x0400000000 -#define BIT35 0x0800000000 -#define BIT36 0x1000000000 extern int RTW_STATUS_CODE(int error_code); From 6bda91484dc97acf52507caa77b18ae141c41a56 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sat, 4 Apr 2026 11:48:02 +0000 Subject: [PATCH 0156/5214] staging: rtl8723bs: remove redundant returns in rtw_mlme_ext.c Remove redundant return statements at the end of void functions in rtw_mlme_ext.c. No functional change. Signed-off-by: Hungyu Lin Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260404114802.11242-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 3d7beb83887d..21ff01482b48 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -2419,9 +2419,6 @@ void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p dump_mgntframe(padapter, pmgntframe); - - return; - } static int _issue_probereq(struct adapter *padapter, @@ -3879,9 +3876,6 @@ void site_survey(struct adapter *padapter) issue_action_BSSCoexistPacket(padapter); } } - - return; - } /* collect bss info from Beacon and Probe request/response frames. */ @@ -4416,9 +4410,6 @@ void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame rtw_enqueue_cmd(pcmdpriv, pcmd_obj); pmlmeext->sitesurvey_res.bss_cnt++; - - return; - } void report_surveydone_event(struct adapter *padapter) @@ -4460,9 +4451,6 @@ void report_surveydone_event(struct adapter *padapter) psurveydone_evt->bss_cnt = pmlmeext->sitesurvey_res.bss_cnt; rtw_enqueue_cmd(pcmdpriv, pcmd_obj); - - return; - } void report_join_res(struct adapter *padapter, int res) @@ -4554,9 +4542,6 @@ void report_wmm_edca_update(struct adapter *padapter) pwmm_event->wmm = 0; rtw_enqueue_cmd(pcmdpriv, pcmd_obj); - - return; - } void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsigned short reason) From b5eb6736f9b1ad2bc424d76afe9bae92f8ab0b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bera=20Y=C3=BCzl=C3=BC?= Date: Sat, 4 Apr 2026 15:49:48 +0300 Subject: [PATCH 0157/5214] staging: rtl8723bs: remove unused function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused struct members from _io_ops. With removal of this members some functions in sdio_ops.c became useless so they are also removed. Signed-off-by: Bera Yüzlü Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260404124947.52549-2-b9788213@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_ops.c | 116 --------------------- drivers/staging/rtl8723bs/include/rtw_io.h | 16 --- 2 files changed, 132 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 8cece69dbf79..653395ff4b1c 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -191,51 +191,6 @@ static u32 sdio_read32(struct intf_hdl *intfhdl, u32 addr) return val; } -static s32 sdio_readN(struct intf_hdl *intfhdl, u32 addr, u32 cnt, u8 *buf) -{ - struct adapter *adapter; - u8 mac_pwr_ctrl_on; - u8 device_id; - u16 offset; - u32 ftaddr; - u8 shift; - s32 err; - - adapter = intfhdl->padapter; - err = 0; - - ftaddr = _cvrt2ftaddr(addr, &device_id, &offset); - - rtw_hal_get_hwreg(adapter, HW_VAR_APFM_ON_MAC, &mac_pwr_ctrl_on); - if ( - ((device_id == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || - (!mac_pwr_ctrl_on) || - (adapter_to_pwrctl(adapter)->fw_current_in_ps_mode) - ) - return sd_cmd52_read(intfhdl, ftaddr, cnt, buf); - - /* 4 bytes alignment */ - shift = ftaddr & 0x3; - if (shift == 0) { - err = sd_read(intfhdl, ftaddr, cnt, buf); - } else { - u8 *tmpbuf; - u32 n; - - ftaddr &= ~(u16)0x3; - n = cnt + shift; - tmpbuf = kmalloc(n, GFP_ATOMIC); - if (!tmpbuf) - return -ENOMEM; - - err = sd_read(intfhdl, ftaddr, n, tmpbuf); - if (!err) - memcpy(buf, tmpbuf + shift, cnt); - kfree(tmpbuf); - } - return err; -} - static s32 sdio_write8(struct intf_hdl *intfhdl, u32 addr, u8 val) { u32 ftaddr; @@ -295,73 +250,6 @@ static s32 sdio_write32(struct intf_hdl *intfhdl, u32 addr, u32 val) return err; } -static s32 sdio_writeN(struct intf_hdl *intfhdl, u32 addr, u32 cnt, u8 *buf) -{ - struct adapter *adapter; - u8 mac_pwr_ctrl_on; - u8 device_id; - u16 offset; - u32 ftaddr; - u8 shift; - s32 err; - - adapter = intfhdl->padapter; - err = 0; - - ftaddr = _cvrt2ftaddr(addr, &device_id, &offset); - - rtw_hal_get_hwreg(adapter, HW_VAR_APFM_ON_MAC, &mac_pwr_ctrl_on); - if ( - ((device_id == WLAN_IOREG_DEVICE_ID) && (offset < 0x100)) || - (!mac_pwr_ctrl_on) || - (adapter_to_pwrctl(adapter)->fw_current_in_ps_mode) - ) - return sd_cmd52_write(intfhdl, ftaddr, cnt, buf); - - shift = ftaddr & 0x3; - if (shift == 0) { - err = sd_write(intfhdl, ftaddr, cnt, buf); - } else { - u8 *tmpbuf; - u32 n; - - ftaddr &= ~(u16)0x3; - n = cnt + shift; - tmpbuf = kmalloc(n, GFP_ATOMIC); - if (!tmpbuf) - return -ENOMEM; - err = sd_read(intfhdl, ftaddr, 4, tmpbuf); - if (err) { - kfree(tmpbuf); - return err; - } - memcpy(tmpbuf + shift, buf, cnt); - err = sd_write(intfhdl, ftaddr, n, tmpbuf); - kfree(tmpbuf); - } - return err; -} - -static void sdio_read_mem( - struct intf_hdl *intfhdl, - u32 addr, - u32 cnt, - u8 *rmem -) -{ - sdio_readN(intfhdl, addr, cnt, rmem); -} - -static void sdio_write_mem( - struct intf_hdl *intfhdl, - u32 addr, - u32 cnt, - u8 *wmem -) -{ - sdio_writeN(intfhdl, addr, cnt, wmem); -} - /* * Description: *Read from RX FIFO @@ -463,14 +351,10 @@ void sdio_set_intf_ops(struct adapter *adapter, struct _io_ops *ops) ops->_read8 = &sdio_read8; ops->_read16 = &sdio_read16; ops->_read32 = &sdio_read32; - ops->_read_mem = &sdio_read_mem; - ops->_read_port = &sdio_read_port; ops->_write8 = &sdio_write8; ops->_write16 = &sdio_write16; ops->_write32 = &sdio_write32; - ops->_writeN = &sdio_writeN; - ops->_write_mem = &sdio_write_mem; ops->_write_port = &sdio_write_port; } diff --git a/drivers/staging/rtl8723bs/include/rtw_io.h b/drivers/staging/rtl8723bs/include/rtw_io.h index adf1de4d7924..3c99a2bc19bd 100644 --- a/drivers/staging/rtl8723bs/include/rtw_io.h +++ b/drivers/staging/rtl8723bs/include/rtw_io.h @@ -18,24 +18,8 @@ struct _io_ops { int (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val); int (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val); int (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val); - int (*_writeN)(struct intf_hdl *pintfhdl, u32 addr, u32 length, u8 *pdata); - int (*_write8_async)(struct intf_hdl *pintfhdl, u32 addr, u8 val); - int (*_write16_async)(struct intf_hdl *pintfhdl, u32 addr, u16 val); - int (*_write32_async)(struct intf_hdl *pintfhdl, u32 addr, u32 val); - - void (*_read_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pmem); - void (*_write_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pmem); - - u32 (*_read_interrupt)(struct intf_hdl *pintfhdl, u32 addr); - - u32 (*_read_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pmem); u32 (*_write_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pmem); - - u32 (*_write_scsi)(struct intf_hdl *pintfhdl, u32 cnt, u8 *pmem); - - void (*_read_port_cancel)(struct intf_hdl *pintfhdl); - void (*_write_port_cancel)(struct intf_hdl *pintfhdl); }; struct intf_hdl { From 69c07e2a4fced90b15a6fba1006b743fd348b4d3 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sat, 4 Apr 2026 11:03:11 +0000 Subject: [PATCH 0158/5214] staging: rtl8723bs: remove redundant return in report_join_res() The return statement at the end of this void function is redundant and can be removed. No functional change. Signed-off-by: Hungyu Lin Reviewed-by: Luka Gejak Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260404110311.10917-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 21ff01482b48..7d3b0d22943a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -4498,9 +4498,6 @@ void report_join_res(struct adapter *padapter, int res) rtw_enqueue_cmd(pcmdpriv, pcmd_obj); - - return; - } void report_wmm_edca_update(struct adapter *padapter) From ce0e10c130c30ad480d83b32d8508183113d585e Mon Sep 17 00:00:00 2001 From: Prithvi Tambewagh Date: Thu, 9 Apr 2026 19:20:22 +0530 Subject: [PATCH 0159/5214] staging: rtl8723bs: move constant to right side of test in comparison Move constant from the left side to the right side of the test in a comparison, where ==, !=, <=, >=, <, > operators are used, fixing the checkpatch warning: Comparisons should place the constant on the right side of the test. Signed-off-by: Prithvi Tambewagh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260409135026.137904-2-activprithvi@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/hal/HalBtc8723b2Ant.c | 4 ++-- drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c | 2 +- drivers/staging/rtl8723bs/hal/hal_com.c | 2 +- drivers/staging/rtl8723bs/hal/hal_com_phycfg.c | 4 ++-- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 18 +++++++++--------- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/ieee80211.h | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c index d32dbf94858f..58f6cf063498 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c @@ -2211,7 +2211,7 @@ static void halbtc8723b2ant_RunCoexistMechanism(struct btc_coexist *pBtCoexist) } algorithm = halbtc8723b2ant_ActionAlgorithm(pBtCoexist); - if (pCoexSta->bC2hBtInquiryPage && (BT_8723B_2ANT_COEX_ALGO_PANHS != algorithm)) { + if (pCoexSta->bC2hBtInquiryPage && (algorithm != BT_8723B_2ANT_COEX_ALGO_PANHS)) { halbtc8723b2ant_ActionBtInquiry(pBtCoexist); return; } else { @@ -2490,7 +2490,7 @@ void EXhalbtc8723b2ant_BtInfoNotify( return; } - if (BT_INFO_SRC_8723B_2ANT_WIFI_FW != rspSource) { + if (rspSource != BT_INFO_SRC_8723B_2ANT_WIFI_FW) { pCoexSta->btRetryCnt = pCoexSta->btInfoC2h[rspSource][2] & 0xf; /* [3:0] */ pCoexSta->btRssi = pCoexSta->btInfoC2h[rspSource][3] * 2 + 10; diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 8f6849f2277e..0780847e287d 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -301,7 +301,7 @@ static void GetDeltaSwingTable_8723B( u16 rate = *(pDM_Odm->pForcedDataRate); u8 channel = pHalData->CurrentChannel; - if (1 <= channel && channel <= 14) { + if (channel >= 1 && channel <= 14) { if (IS_CCK_RATE(rate)) { *TemperatureUP_A = pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKA_P; *TemperatureDOWN_A = pRFCalibrateInfo->DeltaSwingTableIdx_2GCCKA_N; diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 4e3cd1547e01..b99dee6a7ed3 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -107,7 +107,7 @@ u8 hal_com_config_channel_plan( pHalData->bDisableSWChannelPlan = false; chnlPlan = def_channel_plan; - if (0xFF == hw_channel_plan) + if (hw_channel_plan == 0xFF) auto_load_fail = true; if (!auto_load_fail) { diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index bdd595a99b98..2746da0a9846 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -466,11 +466,11 @@ u8 PHY_GetTxPowerIndexBase( if (IS_CCK_RATE(Rate)) txPower = pHalData->Index24G_CCK_Base[RFPath][chnlIdx]; - else if (MGN_6M <= Rate) + else if (Rate >= MGN_6M) txPower = pHalData->Index24G_BW40_Base[RFPath][chnlIdx]; /* OFDM-1T */ - if ((MGN_6M <= Rate && Rate <= MGN_54M) && !IS_CCK_RATE(Rate)) + if ((Rate >= MGN_6M && Rate <= MGN_54M) && !IS_CCK_RATE(Rate)) txPower += pHalData->OFDM_24G_Diff[RFPath][TX_1S]; if (BandWidth == CHANNEL_WIDTH_20) { /* BW20-1S, BW20-2S */ diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 3dff8aa2badb..f20f0a60fec1 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -405,11 +405,11 @@ s32 rtl8723b_FirmwareDownload(struct adapter *padapter, bool bUsedWoWLANFw) break; } _FWDownloadEnable(padapter, false); - if (_SUCCESS != rtStatus) + if (rtStatus != _SUCCESS) goto fwdl_stat; rtStatus = _FWFreeToGo(padapter, 10, 200); - if (_SUCCESS != rtStatus) + if (rtStatus != _SUCCESS) goto fwdl_stat; fwdl_stat: @@ -1169,15 +1169,15 @@ s32 rtl8723b_InitLLTTable(struct adapter *padapter) static void hal_get_chnl_group_8723b(u8 channel, u8 *group) { - if (1 <= channel && channel <= 2) + if (channel >= 1 && channel <= 2) *group = 0; - else if (3 <= channel && channel <= 5) + else if (channel >= 3 && channel <= 5) *group = 1; - else if (6 <= channel && channel <= 8) + else if (channel >= 6 && channel <= 8) *group = 2; - else if (9 <= channel && channel <= 11) + else if (channel >= 9 && channel <= 11) *group = 3; - else if (12 <= channel && channel <= 14) + else if (channel >= 12 && channel <= 14) *group = 4; } @@ -1225,7 +1225,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( memset(pwrInfo24G, 0, sizeof(struct TxPowerInfo24G)); - if (0xFF == PROMContent[eeAddr+1]) + if (PROMContent[eeAddr+1] == 0xFF) AutoLoadFail = true; if (AutoLoadFail) { @@ -2042,7 +2042,7 @@ static void hw_var_set_bcn_func(struct adapter *padapter, u8 variable, u8 *val) val8 &= ~(EN_BCN_FUNCTION | EN_TXBCN_RPT); /* Always enable port0 beacon function for PSTDMA */ - if (REG_BCN_CTRL == bcn_ctrl_reg) + if (bcn_ctrl_reg == REG_BCN_CTRL) val8 |= EN_BCN_FUNCTION; rtw_write8(padapter, bcn_ctrl_reg, val8); diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index f50726d2ed0c..ff39077deb69 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -415,7 +415,7 @@ int rtl8723bs_xmit_thread(void *context) if (signal_pending(current)) { flush_signals(current); } - } while (_SUCCESS == ret); + } while (ret == _SUCCESS); complete(&pxmitpriv->SdioXmitTerminate); diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h index fbb12fe31a6c..a3f519e2f6d2 100644 --- a/drivers/staging/rtl8723bs/include/ieee80211.h +++ b/drivers/staging/rtl8723bs/include/ieee80211.h @@ -394,8 +394,8 @@ enum { }; #define IS_HT_RATE(_rate) (_rate >= MGN_MCS0 && _rate <= MGN_MCS31) -#define IS_CCK_RATE(_rate) (MGN_1M == _rate || _rate == MGN_2M || _rate == MGN_5_5M || _rate == MGN_11M) -#define IS_OFDM_RATE(_rate) (MGN_6M <= _rate && _rate <= MGN_54M && _rate != MGN_11M) +#define IS_CCK_RATE(_rate) (_rate == MGN_1M || _rate == MGN_2M || _rate == MGN_5_5M || _rate == MGN_11M) +#define IS_OFDM_RATE(_rate) (_rate >= MGN_6M && _rate <= MGN_54M && _rate != MGN_11M) /* NOTE: This data is for statistical purposes; not all hardware provides this From c38b274967a769d8e53e2a908d94e16991e808e4 Mon Sep 17 00:00:00 2001 From: Prithvi Tambewagh Date: Thu, 9 Apr 2026 19:20:23 +0530 Subject: [PATCH 0160/5214] staging: rtl8723bs: remove empty if statement block Remove empty if statement block for cleaning up code. Signed-off-by: Prithvi Tambewagh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260409135026.137904-3-activprithvi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 0780847e287d..6ab65e9e8bee 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -1415,9 +1415,6 @@ static void phy_IQCalibrate_8723B( } } - if (0x00 == PathAOK) { - } - /* path B IQK */ if (is2T) { From 5e49201454f366f81785518a7050de9128ebb9f1 Mon Sep 17 00:00:00 2001 From: Prithvi Tambewagh Date: Thu, 9 Apr 2026 19:20:24 +0530 Subject: [PATCH 0161/5214] staging: rtl8723bs: simplify boolean return in IsFrameTypeCtrl() Replace the simple if-else statement which checked value of GetFrameType(pframe), if equal to WIFI_CTRL_TYPE, function IsFrameTypeCtrl() returned true else false, with a single return statement returning true only if GetFrameType(pframe) == WIFI_CTRL_TYPE otherwise returns false. Signed-off-by: Prithvi Tambewagh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260409135026.137904-4-activprithvi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/wifi.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/wifi.h b/drivers/staging/rtl8723bs/include/wifi.h index 230b2c4ffd3b..2876f15f13d1 100644 --- a/drivers/staging/rtl8723bs/include/wifi.h +++ b/drivers/staging/rtl8723bs/include/wifi.h @@ -275,10 +275,7 @@ static inline unsigned char *get_hdr_bssid(unsigned char *pframe) static inline int IsFrameTypeCtrl(unsigned char *pframe) { - if (WIFI_CTRL_TYPE == GetFrameType(pframe)) - return true; - else - return false; + return GetFrameType(pframe) == WIFI_CTRL_TYPE; } /*----------------------------------------------------------------------------- Below is for the security related definition From ec400f25bbd164ae59c7846cb5f13fe0e7a44743 Mon Sep 17 00:00:00 2001 From: Prithvi Tambewagh Date: Thu, 9 Apr 2026 19:20:25 +0530 Subject: [PATCH 0162/5214] staging: rtl8723bs: use read_poll_timeout_atomic in _is_fw_read_cmd_down Replace the existing rtw_read8() and do-while loop mechanism with read_poll_timeout_atomic() from , in _is_fw_read_cmd_down() which is a standard Linux macro, ensuring polling REG_HMETFR efficiently. Signed-off-by: Prithvi Tambewagh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260409135026.137904-5-activprithvi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index 12416e499ac3..4bdc8e314015 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "hal_com_h2c.h" #define MAX_H2C_BOX_NUMS 4 @@ -18,20 +19,15 @@ static u8 _is_fw_read_cmd_down(struct adapter *padapter, u8 msgbox_num) { - u8 read_down = false; - int retry_cnts = 100; - u8 valid; + int ret; - do { - valid = rtw_read8(padapter, REG_HMETFR) & BIT(msgbox_num); - if (0 == valid) { - read_down = true; - } - } while ((!read_down) && (retry_cnts--)); - - return read_down; + ret = read_poll_timeout_atomic(rtw_read8, + valid, !(valid & BIT(msgbox_num)), + 0, 500, false, + padapter, REG_HMETFR); + return !ret; } From 8cc9d4a1b821231b13c8042f67f535e2993f3334 Mon Sep 17 00:00:00 2001 From: Prithvi Tambewagh Date: Thu, 9 Apr 2026 19:20:26 +0530 Subject: [PATCH 0163/5214] staging: rtl8723bs: remove duplicate rate checks in PHY_GetTxPowerIndexBase() The code previously checked (Rate >= MGN_MCS0 && Rate <= MGN_MCS7) condition twice - once for the (BandWidth == CHANNEL_WIDTH_20) check and once for the (BandWidth == CHANNEL_WIDTH_40) check. Fix if statement formatting to move that if check as an outer if check to improve code formatting. Signed-off-by: Prithvi Tambewagh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260409135026.137904-6-activprithvi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_com_phycfg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index 2746da0a9846..45dbe1782bae 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -473,11 +473,10 @@ u8 PHY_GetTxPowerIndexBase( if ((Rate >= MGN_6M && Rate <= MGN_54M) && !IS_CCK_RATE(Rate)) txPower += pHalData->OFDM_24G_Diff[RFPath][TX_1S]; - if (BandWidth == CHANNEL_WIDTH_20) { /* BW20-1S, BW20-2S */ - if (MGN_MCS0 <= Rate && Rate <= MGN_MCS7) + if (Rate >= MGN_MCS0 && Rate <= MGN_MCS7) { + if (BandWidth == CHANNEL_WIDTH_20) /* BW20-1S, BW20-2S */ txPower += pHalData->BW20_24G_Diff[RFPath][TX_1S]; - } else if (BandWidth == CHANNEL_WIDTH_40) { /* BW40-1S, BW40-2S */ - if (MGN_MCS0 <= Rate && Rate <= MGN_MCS7) + else if (BandWidth == CHANNEL_WIDTH_40) /* BW40-1S, BW40-2S */ txPower += pHalData->BW40_24G_Diff[RFPath][TX_1S]; } From 7deae505ec9270b902a739bb06c621f8be47bb19 Mon Sep 17 00:00:00 2001 From: Jinemon Tama Date: Fri, 10 Apr 2026 10:42:11 +0900 Subject: [PATCH 0164/5214] staging: rtl8723bs: fix spacing around operators in rtl8723b_phycfg.c Fix various spacing issues reported by checkpatch.pl to improve code readability and conform to the Linux kernel coding style. These changes are purely cosmetic and do not alter the functional behavior of the driver. Signed-off-by: Jinemon Tama Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260410014214.10684-2-osjin83@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/hal/rtl8723b_phycfg.c | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 7fac1c2ba8e0..b3dfc2320b68 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -19,7 +19,7 @@ static u32 phy_CalculateBitShift(u32 BitMask) u32 i; for (i = 0; i <= 31; i++) { - if (((BitMask>>i) & 0x1) == 1) + if (((BitMask >> i) & 0x1) == 1) break; } return i; @@ -109,18 +109,18 @@ static u32 phy_RFSerialRead_8723B( NewOffset = Offset; if (eRFPath == RF_PATH_A) { - tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter2|MaskforPhySet, bMaskDWord); - tmplong2 = (tmplong2 & (~bLSSIReadAddress)) | (NewOffset<<23) | bLSSIReadEdge; /* T65 RF */ - PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2|MaskforPhySet, bMaskDWord, tmplong2&(~bLSSIReadEdge)); + tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter2 | MaskforPhySet, bMaskDWord); + tmplong2 = (tmplong2 & (~bLSSIReadAddress)) | (NewOffset << 23) | bLSSIReadEdge; /* T65 RF */ + PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2 | MaskforPhySet, bMaskDWord, tmplong2 & (~bLSSIReadEdge)); } else { - tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter2|MaskforPhySet, bMaskDWord); - tmplong2 = (tmplong2 & (~bLSSIReadAddress)) | (NewOffset<<23) | bLSSIReadEdge; /* T65 RF */ - PHY_SetBBReg(Adapter, rFPGA0_XB_HSSIParameter2|MaskforPhySet, bMaskDWord, tmplong2&(~bLSSIReadEdge)); + tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter2 | MaskforPhySet, bMaskDWord); + tmplong2 = (tmplong2 & (~bLSSIReadAddress)) | (NewOffset << 23) | bLSSIReadEdge; /* T65 RF */ + PHY_SetBBReg(Adapter, rFPGA0_XB_HSSIParameter2 | MaskforPhySet, bMaskDWord, tmplong2 & (~bLSSIReadEdge)); } - tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter2|MaskforPhySet, bMaskDWord); - PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2|MaskforPhySet, bMaskDWord, tmplong2 & (~bLSSIReadEdge)); - PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2|MaskforPhySet, bMaskDWord, tmplong2 | bLSSIReadEdge); + tmplong2 = PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter2 | MaskforPhySet, bMaskDWord); + PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2 | MaskforPhySet, bMaskDWord, tmplong2 & (~bLSSIReadEdge)); + PHY_SetBBReg(Adapter, rFPGA0_XA_HSSIParameter2 | MaskforPhySet, bMaskDWord, tmplong2 | bLSSIReadEdge); udelay(10); @@ -129,16 +129,16 @@ static u32 phy_RFSerialRead_8723B( udelay(10); if (eRFPath == RF_PATH_A) - RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter1|MaskforPhySet, BIT8); + RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter1 | MaskforPhySet, BIT8); else if (eRFPath == RF_PATH_B) - RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter1|MaskforPhySet, BIT8); + RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter1 | MaskforPhySet, BIT8); if (RfPiEnable) { /* Read from BBreg8b8, 12 bits for 8190, 20bits for T65 RF */ - retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBackPi|MaskforPhySet, bLSSIReadBackData); + retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBackPi | MaskforPhySet, bLSSIReadBackData); } else { /* Read from BBreg8a0, 12 bits for 8190, 20 bits for T65 RF */ - retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBack|MaskforPhySet, bLSSIReadBackData); + retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBack | MaskforPhySet, bLSSIReadBackData); } return retValue; @@ -203,7 +203,7 @@ static void phy_RFSerialWrite_8723B( /* */ /* Put write addr in [5:0] and write data in [31:16] */ /* */ - DataAndAddr = ((NewOffset<<20) | (Data&0x000fffff)) & 0x0fffffff; /* T65 RF */ + DataAndAddr = ((NewOffset << 20) | (Data & 0x000fffff)) & 0x0fffffff; /* T65 RF */ /* */ /* Write Operation */ /* */ @@ -266,7 +266,7 @@ void PHY_SetRFReg_8723B( if (BitMask != bRFRegOffsetMask) { Original_Value = phy_RFSerialRead_8723B(Adapter, eRFPath, RegAddr); BitShift = phy_CalculateBitShift(BitMask); - Data = ((Original_Value & (~BitMask)) | (Data<nCur40MhzPrimeSC>>1)); + PHY_SetBBReg(Adapter, rCCK0_System, bCCKSideBand, (pHalData->nCur40MhzPrimeSC >> 1)); PHY_SetBBReg(Adapter, rOFDM1_LSTF, 0xC00, pHalData->nCur40MhzPrimeSC); - PHY_SetBBReg(Adapter, 0x818, (BIT26|BIT27), (pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); + PHY_SetBBReg(Adapter, 0x818, (BIT26 | BIT27), (pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); break; default: break; From 44ccc09fd2ae01ff452fd32f639d2d34ce5505e1 Mon Sep 17 00:00:00 2001 From: Jinemon Tama Date: Fri, 10 Apr 2026 10:42:12 +0900 Subject: [PATCH 0165/5214] staging: rtl8723bs: remove space after type cast Remove the unnecessary space between a type cast and the variable as reported by checkpatch.pl. This improves code consistency and conforms to the Linux kernel coding style. Signed-off-by: Jinemon Tama Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260410014214.10684-3-osjin83@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index b3dfc2320b68..51e9f06b2c63 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -531,7 +531,7 @@ u8 PHY_GetTxPowerIndex( struct hal_com_data *pHalData = GET_HAL_DATA(padapter); s8 txPower = 0, powerDiffByRate = 0, limit = 0; - txPower = (s8) PHY_GetTxPowerIndexBase(padapter, RFPath, Rate, BandWidth, Channel); + txPower = (s8)PHY_GetTxPowerIndexBase(padapter, RFPath, Rate, BandWidth, Channel); powerDiffByRate = PHY_GetTxPowerByRate(padapter, RF_PATH_A, Rate); limit = phy_get_tx_pwr_lmt( @@ -551,7 +551,7 @@ u8 PHY_GetTxPowerIndex( if (txPower > MAX_POWER_INDEX) txPower = MAX_POWER_INDEX; - return (u8) txPower; + return (u8)txPower; } void PHY_SetTxPowerLevel8723B(struct adapter *Adapter, u8 Channel) From b168d5457da898303dd54fac45789e6ca54dff87 Mon Sep 17 00:00:00 2001 From: Jinemon Tama Date: Fri, 10 Apr 2026 10:42:13 +0900 Subject: [PATCH 0166/5214] staging: rtl8723bs: wrap long lines in rtl8723b_phycfg.c Wrap lines that exceed the 100-column limit in rtl8723b_phycfg.c to resolve checkpatch.pl warnings. Arguments are aligned with the open parenthesis where appropriate to maintain readability. Signed-off-by: Jinemon Tama Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260410014214.10684-4-osjin83@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 51e9f06b2c63..498f7b25a2b5 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -395,7 +395,8 @@ int PHY_BBConfig8723B(struct adapter *Adapter) PHY_SetRFReg(Adapter, RF_PATH_A, 0x1, 0xfffff, 0x780); - rtw_write8(Adapter, REG_SYS_FUNC_EN, FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB); + rtw_write8(Adapter, REG_SYS_FUNC_EN, + FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | FEN_BB_GLB_RSTn | FEN_BBRSTB); rtw_write8(Adapter, REG_AFE_XTAL_CTRL + 1, 0x80); From 79e643e2dc1f17e22d966bb4c552ec3c854016ca Mon Sep 17 00:00:00 2001 From: Jinemon Tama Date: Fri, 10 Apr 2026 10:42:14 +0900 Subject: [PATCH 0167/5214] staging: rtl8723bs: remove unnecessary blank lines in rtl8723b_phycfg.c Remove unnecessary blank lines throughout rtl8723b_phycfg.c to clean up the code and adhere to the Linux kernel coding style. Signed-off-by: Jinemon Tama Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260410014214.10684-5-osjin83@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 498f7b25a2b5..10eb96105f83 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -25,7 +25,6 @@ static u32 phy_CalculateBitShift(u32 BitMask) return i; } - /** * PHY_QueryBBReg_8723B - Read "specific bits" from BB register. * @Adapter: @@ -46,10 +45,8 @@ u32 PHY_QueryBBReg_8723B(struct adapter *Adapter, u32 RegAddr, u32 BitMask) BitShift = phy_CalculateBitShift(BitMask); return (OriginalValue & BitMask) >> BitShift; - } - /** * PHY_SetBBReg_8723B - Write "Specific bits" to BB register (page 8~). * @Adapter: @@ -80,10 +77,8 @@ void PHY_SetBBReg_8723B( } rtw_write32(Adapter, RegAddr, Data); - } - /* */ /* 2. RF register R/W API */ /* */ @@ -141,7 +136,6 @@ static u32 phy_RFSerialRead_8723B( retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBack | MaskforPhySet, bLSSIReadBackData); } return retValue; - } /** @@ -210,7 +204,6 @@ static void phy_RFSerialWrite_8723B( PHY_SetBBReg(Adapter, pPhyReg->rf3wireOffset, bMaskDWord, DataAndAddr); } - /** * PHY_QueryRFReg_8723B - Query "Specific bits" to RF register (page 8~). * @Adapter: @@ -272,12 +265,10 @@ void PHY_SetRFReg_8723B( phy_RFSerialWrite_8723B(Adapter, eRFPath, RegAddr, Data); } - /* */ /* 3. Initial MAC/BB/RF config by reading MAC/BB/RF txt. */ /* */ - /*----------------------------------------------------------------------------- * PHY_MACConfig8192C - Config MAC by header file or parameter file. * @@ -329,7 +320,6 @@ static void phy_InitBBRFRegisterDefinition(struct adapter *Adapter) pHalData->PHYRegDef[RF_PATH_B].rfLSSIReadBack = rFPGA0_XB_LSSIReadBack; pHalData->PHYRegDef[RF_PATH_A].rfLSSIReadBackPi = TransceiverA_HSPI_Readback; pHalData->PHYRegDef[RF_PATH_B].rfLSSIReadBackPi = TransceiverB_HSPI_Readback; - } static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) @@ -373,7 +363,6 @@ static int phy_BB8723b_Config_ParaFile(struct adapter *Adapter) return _SUCCESS; } - int PHY_BBConfig8723B(struct adapter *Adapter) { int rtStatus = _SUCCESS; @@ -614,7 +603,6 @@ static void phy_PostSetBwMode8723B(struct adapter *Adapter) u8 SubChnlNum = 0; struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - /* 3 Set Reg668 Reg440 BW */ phy_SetRegBW_8723B(Adapter, pHalData->CurrentChannelBW); @@ -725,13 +713,11 @@ static void PHY_HandleSwChnlAndSetBW8723B( if (!pHalData->bSetChnlBW && !pHalData->bSwChnl) return; - if (pHalData->bSwChnl) { pHalData->CurrentChannel = ChannelNum; pHalData->CurrentCenterFrequencyIndex1 = ChannelNum; } - if (pHalData->bSetChnlBW) { pHalData->CurrentChannelBW = ChnlWidth; pHalData->nCur40MhzPrimeSC = ExtChnlOffsetOf40MHz; From 9499461705399717d0fdea16255bedf88945faff Mon Sep 17 00:00:00 2001 From: Kenet Jovan Sokoli Date: Tue, 14 Apr 2026 10:13:25 +0200 Subject: [PATCH 0168/5214] staging: rtl8723bs: remove unused struct rtw_regulatory The struct rtw_regulatory is never used in the rtl8723bs driver. Functions taking it as a parameter are always passed NULL and perform no logic with it. Remove the dead code and the struct. Suggested-by: Dan Carpenter Signed-off-by: Kenet Jovan Sokoli Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260414081325.142313-1-deep@crimson.net.eu.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/wifi.h | 11 ----------- drivers/staging/rtl8723bs/os_dep/wifi_regd.c | 18 ++++++------------ 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/wifi.h b/drivers/staging/rtl8723bs/include/wifi.h index 2876f15f13d1..92b7882de15c 100644 --- a/drivers/staging/rtl8723bs/include/wifi.h +++ b/drivers/staging/rtl8723bs/include/wifi.h @@ -447,15 +447,4 @@ struct regd_pair_mapping { u16 reg_2ghz_ctl; }; -struct rtw_regulatory { - char alpha2[2]; - u16 country_code; - u16 max_power_level; - u32 tp_scale; - u16 current_rd; - u16 current_rd_ext; - int16_t power_limit; - struct regd_pair_mapping *regpair; -}; - #endif /* _WIFI_H_ */ diff --git a/drivers/staging/rtl8723bs/os_dep/wifi_regd.c b/drivers/staging/rtl8723bs/os_dep/wifi_regd.c index f9c4d487badf..c48a1e3ba4ac 100644 --- a/drivers/staging/rtl8723bs/os_dep/wifi_regd.c +++ b/drivers/staging/rtl8723bs/os_dep/wifi_regd.c @@ -83,23 +83,19 @@ static void _rtw_reg_apply_flags(struct wiphy *wiphy) } static int _rtw_reg_notifier_apply(struct wiphy *wiphy, - struct regulatory_request *request, - struct rtw_regulatory *reg) + struct regulatory_request *request) { /* Hard code flags */ _rtw_reg_apply_flags(wiphy); return 0; } -static const struct ieee80211_regdomain *_rtw_regdomain_select(struct - rtw_regulatory - *reg) +static const struct ieee80211_regdomain *_rtw_regdomain_select(void) { return &rtw_regdom_rd; } -static void _rtw_regd_init_wiphy(struct rtw_regulatory *reg, - struct wiphy *wiphy, +static void _rtw_regd_init_wiphy(struct wiphy *wiphy, void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request * @@ -113,7 +109,7 @@ static void _rtw_regd_init_wiphy(struct rtw_regulatory *reg, wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG; wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS; - regd = _rtw_regdomain_select(reg); + regd = _rtw_regdomain_select(); wiphy_apply_custom_regulatory(wiphy, regd); /* Hard code flags */ @@ -124,12 +120,10 @@ void rtw_regd_init(struct wiphy *wiphy, void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)) { - _rtw_regd_init_wiphy(NULL, wiphy, reg_notifier); + _rtw_regd_init_wiphy(wiphy, reg_notifier); } void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { - struct rtw_regulatory *reg = NULL; - - _rtw_reg_notifier_apply(wiphy, request, reg); + _rtw_reg_notifier_apply(wiphy, request); } From ab713f524e235e7b1ea8fa8bf22d4ce429de98a7 Mon Sep 17 00:00:00 2001 From: Aadarsh Mandal Date: Tue, 14 Apr 2026 12:05:34 +0530 Subject: [PATCH 0169/5214] staging: rtl8723bs: remove commented-out code Remove code that is not used anywhere in driver. Signed-off-by: Aadarsh Mandal Reviewed-by: Dan Carpenter Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260414063534.16697-1-aadarshmandal9354@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 29 -------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index 3b3fac981a27..c803d97b1eeb 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -46,8 +46,6 @@ u8 _InitPowerOn_8723BS(struct adapter *padapter) u16 value16; u32 value32; u8 ret; -/* u8 bMacPwrCtrlOn; */ - /* all of these MUST be configured before power on */ @@ -69,10 +67,6 @@ u8 _InitPowerOn_8723BS(struct adapter *padapter) value16 |= EnPDN; /* Enable HW power down and RF on */ rtw_write16(padapter, REG_APS_FSMCO, value16); - /* Enable CMD53 R/W Operation */ -/* bMacPwrCtrlOn = true; */ -/* rtw_hal_set_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); */ - rtw_write8(padapter, REG_CR, 0x00); /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */ value16 = rtw_read16(padapter, REG_CR); @@ -359,7 +353,6 @@ static void _InitNetworkType(struct adapter *padapter) value32 = rtw_read32(padapter, REG_CR); /* TODO: use the other function to set network type */ -/* value32 = (value32 & ~MASK_NETTYPE) | _NETTYPE(NT_LINK_AD_HOC); */ value32 = (value32 & ~MASK_NETTYPE) | _NETTYPE(NT_LINK_AP); rtw_write32(padapter, REG_CR, value32); @@ -410,9 +403,6 @@ static void _InitAdaptiveCtrl(struct adapter *padapter) value32 |= RATE_RRSR_CCK_ONLY_1M; rtw_write32(padapter, REG_RRSR, value32); - /* CF-END Threshold */ - /* m_spIoBase->rtw_write8(REG_CFEND_TH, 0x1); */ - /* SIFS (used in NAV) */ value16 = _SPEC_SIFS_CCK(0x10) | _SPEC_SIFS_OFDM(0x10); rtw_write16(padapter, REG_SPEC_SIFS, value16); @@ -486,9 +476,6 @@ static void _initSdioAggregationSetting(struct adapter *padapter) { struct hal_com_data *pHalData = GET_HAL_DATA(padapter); - /* Tx aggregation setting */ -/* sdio_AggSettingTxUpdate(padapter); */ - /* Rx aggregation setting */ HalRxAggr8723BSdio(padapter); @@ -640,9 +627,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) return _SUCCESS; } - /* Disable Interrupt first. */ -/* rtw_hal_disable_interrupt(padapter); */ - ret = _InitPowerOn_8723BS(padapter); if (ret == _FAIL) return _FAIL; @@ -661,8 +645,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) rtl8723b_InitializeFirmwareVars(padapter); -/* SIC_Init(padapter); */ - if (pwrctrlpriv->reg_rfoff) pwrctrlpriv->rf_pwrstate = rf_off; @@ -704,8 +686,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) pHalData->RfRegChnlVal[1] = PHY_QueryRFReg(padapter, (enum rf_path)1, RF_CHNLBW, bRFRegOffsetMask); - - /* if (!pHalData->bMACFuncEnable) { */ _InitQueueReservedPage(padapter); _InitTxBufferBoundary(padapter); @@ -745,11 +725,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) rtw_hal_set_chnl_bw(padapter, padapter->registrypriv.channel, CHANNEL_WIDTH_20, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HAL_PRIME_CHNL_OFFSET_DONT_CARE); - /* Record original value for template. This is arough data, we can only use the data */ - /* for power adjust. The value can not be adjustde according to different power!!! */ -/* pHalData->OriginalCckTxPwrIdx = pHalData->CurrentCckTxPwrIdx; */ -/* pHalData->OriginalOfdm24GTxPwrIdx = pHalData->CurrentOfdm24GTxPwrIdx; */ - rtl8723b_InitAntenna_Selection(padapter); /* */ @@ -791,8 +766,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) /* ack for xmit mgmt frames. */ rtw_write32(padapter, REG_FWHW_TXQ_CTRL, rtw_read32(padapter, REG_FWHW_TXQ_CTRL) | BIT(12)); -/* pHalData->PreRpwmVal = SdioLocalCmd52Read1Byte(padapter, SDIO_REG_HRPWM1) & 0x80; */ - { pwrctrlpriv->rf_pwrstate = rf_on; @@ -1080,8 +1053,6 @@ static void _ReadPROMContent(struct adapter *padapter) pEEPROM->EepromOrEfuse = (eeValue & BOOT_FROM_EEPROM) ? true : false; pEEPROM->bautoload_fail_flag = (eeValue & EEPROM_EN) ? false : true; -/* pHalData->EEType = IS_BOOT_FROM_EEPROM(Adapter) ? EEPROM_93C46 : EEPROM_BOOT_EFUSE; */ - _ReadEfuseInfo8723BS(padapter); } From c7951bd684af2e2652dca1c2bf6e08cf4d28688d Mon Sep 17 00:00:00 2001 From: Josh Hesketh Date: Sun, 12 Apr 2026 16:06:32 +0100 Subject: [PATCH 0170/5214] staging: rtl8723bs: fix whitespace issues in sdio_halinit.c Remove spaces before tabs in comments and fix extra spaces to address checkpatch warnings. Signed-off-by: Josh Hesketh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260412150633.12071-2-josh.hesketh@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index c803d97b1eeb..9a73da0d205b 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -817,7 +817,7 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) /* */ /* Description: */ -/* RTL8723e card disable power sequence v003 which suggested by Scott. */ +/* RTL8723e card disable power sequence v003 which suggested by Scott. */ /* */ /* First created by tynli. 2011.01.28. */ /* */ @@ -829,7 +829,7 @@ static void CardDisableRTL8723BSdio(struct adapter *padapter) /* Run LPS WL RFOFF flow */ HalPwrSeqCmdParsing(padapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, rtl8723B_enter_lps_flow); - /* ==== Reset digital sequence ====== */ + /* ==== Reset digital sequence ====== */ val = rtw_read8(padapter, REG_MCUFWDL); if ((val & RAM_DL_SEL) && padapter->bFWReady) /* 8051 RAM code */ @@ -852,7 +852,7 @@ static void CardDisableRTL8723BSdio(struct adapter *padapter) val |= BIT(0); rtw_write8(padapter, REG_RSV_CTRL + 1, val); - /* ==== Reset digital sequence end ====== */ + /* ==== Reset digital sequence end ====== */ bMacPwrCtrlOn = false; /* Disable CMD53 R/W */ rtw_hal_set_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); @@ -954,13 +954,13 @@ void rtl8723bs_interface_configure(struct adapter *padapter) } /* */ -/* Description: */ -/* We should set Efuse cell selection to WiFi cell in default. */ +/* Description: */ +/* We should set Efuse cell selection to WiFi cell in default. */ /* */ -/* Assumption: */ -/* PASSIVE_LEVEL */ +/* Assumption: */ +/* PASSIVE_LEVEL */ /* */ -/* Added by Roger, 2010.11.23. */ +/* Added by Roger, 2010.11.23. */ /* */ static void _EfuseCellSel(struct adapter *padapter) { @@ -1046,7 +1046,7 @@ static void _ReadEfuseInfo8723BS(struct adapter *padapter) static void _ReadPROMContent(struct adapter *padapter) { struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); - u8 eeValue; + u8 eeValue; eeValue = rtw_read8(padapter, REG_9346CR); /* To check system boot selection. */ @@ -1057,11 +1057,11 @@ static void _ReadPROMContent(struct adapter *padapter) } /* */ -/* Description: */ -/* Read HW adapter information by E-Fuse or EEPROM according CR9346 reported. */ +/* Description: */ +/* Read HW adapter information by E-Fuse or EEPROM according CR9346 reported. */ /* */ -/* Assumption: */ -/* PASSIVE_LEVEL (SDIO interface) */ +/* Assumption: */ +/* PASSIVE_LEVEL (SDIO interface) */ /* */ /* */ static s32 _ReadAdapterInfo8723BS(struct adapter *padapter) @@ -1174,14 +1174,14 @@ void SetHwRegWithBuf8723B(struct adapter *padapter, u8 variable, u8 *pbuf, int l } /* */ -/* Description: */ -/* Query setting of specified variable. */ +/* Description: */ +/* Query setting of specified variable. */ /* */ u8 GetHalDefVar8723BSDIO( struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue ) { - u8 bResult = _SUCCESS; + u8 bResult = _SUCCESS; switch (eVariable) { case HAL_DEF_IS_SUPPORT_ANT_DIV: From 016f33c1a9a946de46ad8e0d06e542e07e944836 Mon Sep 17 00:00:00 2001 From: Josh Hesketh Date: Sun, 12 Apr 2026 16:06:33 +0100 Subject: [PATCH 0171/5214] staging: rtl8723bs: remove unnecessary else after return in sdio_halinit.c Remove else branch following a conditional return statement Signed-off-by: Josh Hesketh Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260412150633.12071-3-josh.hesketh@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index 9a73da0d205b..8b928182c031 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -638,11 +638,11 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) padapter->bFWReady = false; pHalData->fw_ractrl = false; return ret; - } else { - padapter->bFWReady = true; - pHalData->fw_ractrl = true; } + padapter->bFWReady = true; + pHalData->fw_ractrl = true; + rtl8723b_InitializeFirmwareVars(padapter); if (pwrctrlpriv->reg_rfoff) From f051bc718b00446709e00e6b54bd6b4e2d4ce447 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Wed, 15 Apr 2026 10:56:38 +0200 Subject: [PATCH 0172/5214] staging: rtl8723bs: clean up memcpy() in rtw_check_bcn_info Move the ssid memcpy() inside the ie null-check to avoid calling it with a NULL-derived pointer (p + 2) when the ie is missing. While the kernel handles 0-length memcpy() safely as a no-op, keeping the call outside the check is confusing and poor practice. This change improves code readability. Signed-off-by: Luka Gejak Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260415085638.6427-1-luka.gejak@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index c04419c9c3fc..3762ae6c4c65 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1204,8 +1204,8 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len) ssid_len = *(p + 1); if (ssid_len > NDIS_802_11_LENGTH_SSID) ssid_len = 0; + memcpy(bssid->ssid.ssid, (p + 2), ssid_len); } - memcpy(bssid->ssid.ssid, (p + 2), ssid_len); bssid->ssid.ssid_length = ssid_len; if (memcmp(bssid->ssid.ssid, cur_network->network.ssid.ssid, 32) || From 5802af57b224713ae817654c102980627bded435 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 21 Apr 2026 08:15:15 +0300 Subject: [PATCH 0173/5214] staging: rtl8723bs: remove dump_chip_info() function The dump_chip_info() function formats chip version information into a local 128-byte buffer using the scnprintf(). This buffer was previously passed to the DBG_871X macro. Commit 968b15adb0ea ("staging: rtl8723bs: remove all DBG_871X logs") removed the macro, leaving the buffer formatted but never used or output anywhere. dump_chip_info() is now useless, so remove it and its call from ReadChipVersion8723B(). Signed-off-by: Nikolay Kulikov Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260421051551.1694-1-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_com.c | 42 ------------------- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 2 - drivers/staging/rtl8723bs/include/hal_com.h | 2 - 3 files changed, 46 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index b99dee6a7ed3..b4b135a9ca22 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -30,48 +30,6 @@ void rtw_hal_data_deinit(struct adapter *padapter) } } - -void dump_chip_info(struct hal_version chip_version) -{ - char buf[128]; - size_t cnt = 0; - - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "Chip Version Info: CHIP_8723B_%s_", - IS_NORMAL_CHIP(chip_version) ? "Normal_Chip" : "Test_Chip"); - - if (IS_CHIP_VENDOR_TSMC(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "TSMC_"); - else if (IS_CHIP_VENDOR_UMC(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "UMC_"); - else if (IS_CHIP_VENDOR_SMIC(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "SMIC_"); - - if (IS_A_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "A_CUT_"); - else if (IS_B_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "B_CUT_"); - else if (IS_C_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "C_CUT_"); - else if (IS_D_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "D_CUT_"); - else if (IS_E_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "E_CUT_"); - else if (IS_I_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "I_CUT_"); - else if (IS_J_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "J_CUT_"); - else if (IS_K_CUT(chip_version)) - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "K_CUT_"); - else - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, - "UNKNOWN_CUT(%d)_", chip_version.CUTVersion); - - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "1T1R_"); - - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "RomVer(%d)\n", chip_version.ROMVer); -} - - #define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 /* diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index f20f0a60fec1..74cc2de963f7 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -856,8 +856,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) pHalData->MultiFunc |= ((value32 & GPS_FUNC_EN) ? RT_MULTI_FUNC_GPS : 0); pHalData->PolarityCtl = ((value32 & WL_HWPDN_SL) ? RT_POLARITY_HIGH_ACT : RT_POLARITY_LOW_ACT); - dump_chip_info(ChipVersion); - pHalData->VersionID = ChipVersion; return ChipVersion; diff --git a/drivers/staging/rtl8723bs/include/hal_com.h b/drivers/staging/rtl8723bs/include/hal_com.h index 483f0390addc..cf775585c8e2 100644 --- a/drivers/staging/rtl8723bs/include/hal_com.h +++ b/drivers/staging/rtl8723bs/include/hal_com.h @@ -92,8 +92,6 @@ enum rt_media_status { u8 rtw_hal_data_init(struct adapter *padapter); void rtw_hal_data_deinit(struct adapter *padapter); -void dump_chip_info(struct hal_version ChipVersion); - u8 /* return the final channel plan decision */ hal_com_config_channel_plan( struct adapter *padapter, From 59214b899b5c846c9231d582d2fd163a961a0461 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Sun, 12 Apr 2026 10:45:52 -0400 Subject: [PATCH 0174/5214] staging: fbtft: Use %pe format specifier for error pointers The %pe format specifier resolves error pointers to their symbolic representation. Previously %ld with PTR_ERR() was being used, %pe is a better alternative. Fixes the following coccinelle warnings reported by coccicheck: WARNING: Consider using %pe to print PTR_ERR() Testing: I do not own the hardware, therefore I could not perform hardware testing. Compile tested only. Signed-off-by: Mahad Ibrahim Link: https://patch.msgid.link/20260412144552.18493-1-mahad.ibrahim.dev@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fbtft/fb_ssd1351.c | 3 +-- drivers/staging/fbtft/fbtft-core.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c index 6736b09b2f45..15524b80035a 100644 --- a/drivers/staging/fbtft/fb_ssd1351.c +++ b/drivers/staging/fbtft/fb_ssd1351.c @@ -218,8 +218,7 @@ static void register_onboard_backlight(struct fbtft_par *par) &bl_props); if (IS_ERR(bd)) { dev_err(par->info->device, - "cannot register backlight device (%ld)\n", - PTR_ERR(bd)); + "cannot register backlight device (%pe)\n", bd); return; } par->info->bl_dev = bd; diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 3da42c8ca6e3..ca0c38221c16 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -187,8 +187,7 @@ void fbtft_register_backlight(struct fbtft_par *par) &fbtft_bl_ops, &bl_props); if (IS_ERR(bd)) { dev_err(par->info->device, - "cannot register backlight device (%ld)\n", - PTR_ERR(bd)); + "cannot register backlight device (%pe)\n", bd); return; } par->info->bl_dev = bd; From 559101d0c8ade0f34c373844a16588bcf068fe38 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:27 +0300 Subject: [PATCH 0175/5214] staging: rtl8723bs: remove wrapper rtw_hal_chip_configure() Remove the rtw_hal_chip_configure() function, as it's just a wrapper that calls rtl8723bs_interface_configure() directly. Instead, call rtl8723bs_interface_configure() from the appropriate places. This will reduce code complexity and improve readability by removing unnecessary abstraction. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-2-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 1 - drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 3 ++- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 27c0c0198714..491208431396 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,11 +7,6 @@ #include #include -void rtw_hal_chip_configure(struct adapter *padapter) -{ - rtl8723bs_interface_configure(padapter); -} - void rtw_hal_read_chip_info(struct adapter *padapter) { ReadAdapterInfo8723BS(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index b193854bfe6e..7629cef95b06 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -195,7 +195,6 @@ void rtw_hal_get_hwreg(struct adapter *padapter, u8 variable, u8 *val); void rtw_hal_set_hwreg_with_buf(struct adapter *padapter, u8 variable, u8 *pbuf, int len); -void rtw_hal_chip_configure(struct adapter *padapter); void rtw_hal_read_chip_info(struct adapter *padapter); void rtw_hal_read_chip_version(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d0feb28b7043..f733692784bb 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -6,6 +6,7 @@ ******************************************************************************/ #include #include +#include #include #ifndef dev_to_sdio_func @@ -265,7 +266,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct rtw_hal_read_chip_version(padapter); - rtw_hal_chip_configure(padapter); + rtl8723bs_interface_configure(padapter); hal_btcoex_Initialize((void *)padapter); From 5212b74d6edd6d76b2e9163c3f37862863432629 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:28 +0300 Subject: [PATCH 0176/5214] staging: rtl8723bs: remove wrapper rtw_hal_read_chip_info() Remove the rtw_hal_read_chip_info() function, as it's just a wrapper that calls ReadAdapterInfo8723BS() directly. Replace all its calls to the ReadAdapterInfo8723BS() function. This will remove an extra level of abstraction and simplify the code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-3-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 1 - drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 491208431396..da250179fc92 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,11 +7,6 @@ #include #include -void rtw_hal_read_chip_info(struct adapter *padapter) -{ - ReadAdapterInfo8723BS(padapter); -} - void rtw_hal_read_chip_version(struct adapter *padapter) { rtl8723b_read_chip_version(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 7629cef95b06..8f5f2c63fc10 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -195,7 +195,6 @@ void rtw_hal_get_hwreg(struct adapter *padapter, u8 variable, u8 *val); void rtw_hal_set_hwreg_with_buf(struct adapter *padapter, u8 variable, u8 *pbuf, int len); -void rtw_hal_read_chip_info(struct adapter *padapter); void rtw_hal_read_chip_version(struct adapter *padapter); u8 rtw_hal_get_def_var(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index f733692784bb..98eff17be9b6 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -271,7 +271,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct hal_btcoex_Initialize((void *)padapter); /* 3 6. read efuse/eeprom data */ - rtw_hal_read_chip_info(padapter); + ReadAdapterInfo8723BS(padapter); /* 3 7. init driver common data */ if (rtw_init_drv_sw(padapter) == _FAIL) From 84bb1ead7e8bcde8a25885462797bfe29cb09b9b Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:29 +0300 Subject: [PATCH 0177/5214] staging: rtl8723bs: remove wrapper rtw_hal_read_chip_version() Remove rtw_hal_read_chip_version() function, as it's just a wrapper that calls rtl8723b_read_chip_version() directly. Replace all its calls to the rtl8723b_read_chip_version(). This will remove an extra level of abstraction and simplify the code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-4-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 2 -- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index da250179fc92..c3a238cb0b1e 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,11 +7,6 @@ #include #include -void rtw_hal_read_chip_version(struct adapter *padapter) -{ - rtl8723b_read_chip_version(padapter); -} - void rtw_hal_def_value_init(struct adapter *padapter) { rtl8723bs_init_default_value(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 8f5f2c63fc10..0d2f60ce29af 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -195,8 +195,6 @@ void rtw_hal_get_hwreg(struct adapter *padapter, u8 variable, u8 *val); void rtw_hal_set_hwreg_with_buf(struct adapter *padapter, u8 variable, u8 *pbuf, int len); -void rtw_hal_read_chip_version(struct adapter *padapter); - u8 rtw_hal_get_def_var(struct adapter *padapter, enum hal_def_variable eVariable, void *pValue); void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index 98eff17be9b6..e487e238d737 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -264,7 +264,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct if (rtw_init_io_priv(padapter, sdio_set_intf_ops) == _FAIL) goto free_hal_data; - rtw_hal_read_chip_version(padapter); + rtl8723b_read_chip_version(padapter); rtl8723bs_interface_configure(padapter); From 43f292e66797e538ad655dd2af576faa572167f8 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:30 +0300 Subject: [PATCH 0178/5214] staging: rtl8723bs: remove wrapper rtw_hal_def_value_init() Remove unnecessary wrapper and call rtl8723bs_init_default_value() function directly to simplify code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-5-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 2 -- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index c3a238cb0b1e..1322713fec6c 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,11 +7,6 @@ #include #include -void rtw_hal_def_value_init(struct adapter *padapter) -{ - rtl8723bs_init_default_value(padapter); -} - void rtw_hal_free_data(struct adapter *padapter) { /* free HAL Data */ diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 0d2f60ce29af..f46989ce9e3b 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -181,8 +181,6 @@ typedef s32 (*c2h_id_filter)(u8 *c2h_evt); #define RX_PNOWakeUp 0x55 #define AP_WakeUp 0x66 -void rtw_hal_def_value_init(struct adapter *padapter); - void rtw_hal_free_data(struct adapter *padapter); void rtw_hal_dm_init(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index 0f8fda08138e..75820b62bb38 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -550,7 +550,7 @@ static void rtw_init_default_value(struct adapter *padapter) rtw_update_registrypriv_dev_network(padapter); /* hal_priv */ - rtw_hal_def_value_init(padapter); + rtl8723bs_init_default_value(padapter); /* misc. */ RTW_ENABLE_FUNC(padapter, DF_RX_BIT); @@ -609,7 +609,7 @@ void rtw_reset_drv_sw(struct adapter *padapter) struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(padapter); /* hal_priv */ - rtw_hal_def_value_init(padapter); + rtl8723bs_init_default_value(padapter); RTW_ENABLE_FUNC(padapter, DF_RX_BIT); RTW_ENABLE_FUNC(padapter, DF_TX_BIT); From afeb5b6de99bc2f3861f498a313005069140abb3 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:31 +0300 Subject: [PATCH 0179/5214] staging: rtl8723bs: remove wrapper rtw_hal_free_data() Remove unnecessary wrapper and call rtw_hal_data_deinit() function directly to simplify code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-6-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 6 ------ drivers/staging/rtl8723bs/include/hal_intf.h | 2 -- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 1322713fec6c..43b9bf9712c8 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,12 +7,6 @@ #include #include -void rtw_hal_free_data(struct adapter *padapter) -{ - /* free HAL Data */ - rtw_hal_data_deinit(padapter); -} - void rtw_hal_dm_init(struct adapter *padapter) { rtl8723b_init_dm_priv(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index f46989ce9e3b..e7cfb255f8f6 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -181,8 +181,6 @@ typedef s32 (*c2h_id_filter)(u8 *c2h_evt); #define RX_PNOWakeUp 0x55 #define AP_WakeUp 0x66 -void rtw_hal_free_data(struct adapter *padapter); - void rtw_hal_dm_init(struct adapter *padapter); uint rtw_hal_init(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index 75820b62bb38..e2bcc6503deb 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -737,7 +737,7 @@ u8 rtw_free_drv_sw(struct adapter *padapter) /* kfree((void *)padapter); */ - rtw_hal_free_data(padapter); + rtw_hal_data_deinit(padapter); /* free the old_pnetdev */ if (padapter->rereg_nd_name_priv.old_pnetdev) { From f12a93326e3a73a53cc184960f5a1e94f3e05802 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:32 +0300 Subject: [PATCH 0180/5214] staging: rtl8723bs: remove wrapper rtw_hal_dm_init() Remove unnecessary wrapper and call rtl8723b_init_dm_priv() function directly to simplify code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-7-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 2 -- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 43b9bf9712c8..4448082c14da 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -7,11 +7,6 @@ #include #include -void rtw_hal_dm_init(struct adapter *padapter) -{ - rtl8723b_init_dm_priv(padapter); -} - static void rtw_hal_init_opmode(struct adapter *padapter) { enum ndis_802_11_network_infrastructure networkType = Ndis802_11InfrastructureMax; diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index e7cfb255f8f6..721501ca3b02 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -181,8 +181,6 @@ typedef s32 (*c2h_id_filter)(u8 *c2h_evt); #define RX_PNOWakeUp 0x55 #define AP_WakeUp 0x66 -void rtw_hal_dm_init(struct adapter *padapter); - uint rtw_hal_init(struct adapter *padapter); uint rtw_hal_deinit(struct adapter *padapter); void rtw_hal_stop(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index e2bcc6503deb..d55a68b27096 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -677,7 +677,7 @@ u8 rtw_init_drv_sw(struct adapter *padapter) rtw_init_pwrctrl_priv(padapter); - rtw_hal_dm_init(padapter); + rtl8723b_init_dm_priv(padapter); return _SUCCESS; From 330ece18af96d6a97d29b05fc33a42534b8643ec Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:33 +0300 Subject: [PATCH 0181/5214] staging: rtl8723bs: remove wrapper rtw_hal_enable_interrupt() Remove unnecessary wrapper and call EnableInterrupt8723BSdio() function directly to simplify code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-8-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 1 - drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 3 +-- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 4448082c14da..529648036f58 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -94,11 +94,6 @@ void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariab SetHalODMVar(padapter, eVariable, pValue1, bSet); } -void rtw_hal_enable_interrupt(struct adapter *padapter) -{ - EnableInterrupt8723BSdio(padapter); -} - void rtw_hal_disable_interrupt(struct adapter *padapter) { DisableInterrupt8723BSdio(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 721501ca3b02..6af2337822ff 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -193,7 +193,6 @@ u8 rtw_hal_get_def_var(struct adapter *padapter, enum hal_def_variable eVariable void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet); -void rtw_hal_enable_interrupt(struct adapter *padapter); void rtw_hal_disable_interrupt(struct adapter *padapter); u8 rtw_hal_check_ips_status(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index e487e238d737..2e573a0a255e 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -204,8 +204,7 @@ static void sd_intf_start(struct adapter *padapter) if (!padapter) return; - /* hal dep */ - rtw_hal_enable_interrupt(padapter); + EnableInterrupt8723BSdio(padapter); } static void sd_intf_stop(struct adapter *padapter) From 629af58ab8273a5f78d8d2de13755cf670d50c52 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:35 +0300 Subject: [PATCH 0182/5214] staging: rtl8723bs: rename ReadAdapterInfo8723BS() to snake_case Rename function ReadAdapterInfo8723BS() to rtw_read_adapter_info() to comply with Linux kernel coding style and fix checkpatch.pl warning. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-10-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 2 +- drivers/staging/rtl8723bs/include/rtl8723b_recv.h | 2 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index 8b928182c031..b3366830edbb 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -1089,7 +1089,7 @@ static s32 _ReadAdapterInfo8723BS(struct adapter *padapter) return _SUCCESS; } -void ReadAdapterInfo8723BS(struct adapter *padapter) +void rtw_read_adapter_info(struct adapter *padapter) { /* Read EEPROM size before call any EEPROM function */ padapter->EepromAddressSize = GetEEPROMSize8723B(padapter); diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_recv.h b/drivers/staging/rtl8723bs/include/rtl8723b_recv.h index 783f64de0aec..e9ebecb224a6 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_recv.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_recv.h @@ -90,6 +90,6 @@ void rtl8723b_process_phy_info(struct adapter *padapter, void *prframe); void rtl8723b_read_chip_version(struct adapter *padapter); void rtl8723bs_init_default_value(struct adapter *padapter); void rtl8723bs_interface_configure(struct adapter *padapter); -void ReadAdapterInfo8723BS(struct adapter *padapter); +void rtw_read_adapter_info(struct adapter *padapter); #endif diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index 2e573a0a255e..04b3836dab73 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -270,7 +270,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct hal_btcoex_Initialize((void *)padapter); /* 3 6. read efuse/eeprom data */ - ReadAdapterInfo8723BS(padapter); + rtw_read_adapter_info(padapter); /* 3 7. init driver common data */ if (rtw_init_drv_sw(padapter) == _FAIL) From bff3de1f84f8c5fcff6deee7b619f7064b5d4e19 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 19 Apr 2026 10:19:36 +0300 Subject: [PATCH 0183/5214] staging: rtl8723bs: rename EnableInterrupt8723BSdio() to snake_case Rename function EnableInterrupt8723BSdio() to rtw_sdio_enable_interrupt() and format its description to comply with Linux kernel coding style. Declare this function without 'extern' prototype in the .h file to fix checkpatch.pl warning. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260419072034.19824-11-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_ops.c | 19 ++++++++----------- drivers/staging/rtl8723bs/include/sdio_ops.h | 2 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 653395ff4b1c..77a6a5c7ec7e 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -596,17 +596,14 @@ void InitSysInterrupt8723BSdio(struct adapter *adapter) haldata->SysIntrMask = (0); } -/* */ -/* Description: */ -/* Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain. */ -/* */ -/* Assumption: */ -/* 1. Using SDIO Local register ONLY for configuration. */ -/* 2. PASSIVE LEVEL */ -/* */ -/* Created by Roger, 2011.02.11. */ -/* */ -void EnableInterrupt8723BSdio(struct adapter *adapter) +/* + * Enable SDIO Host Interrupt Mask configuration on SDIO local domain. + * + * Assumption: + * 1. Using SDIO Local register ONLY for configuration. + * 2. PASSIVE LEVEL + */ +void rtw_sdio_enable_interrupt(struct adapter *adapter) { struct hal_com_data *haldata; __le32 himr; diff --git a/drivers/staging/rtl8723bs/include/sdio_ops.h b/drivers/staging/rtl8723bs/include/sdio_ops.h index c7559a884608..7bb4b2047cc1 100644 --- a/drivers/staging/rtl8723bs/include/sdio_ops.h +++ b/drivers/staging/rtl8723bs/include/sdio_ops.h @@ -27,7 +27,7 @@ extern u8 CheckIPSStatus(struct adapter *padapter); extern void InitInterrupt8723BSdio(struct adapter *padapter); extern void InitSysInterrupt8723BSdio(struct adapter *padapter); -extern void EnableInterrupt8723BSdio(struct adapter *padapter); +void rtw_sdio_enable_interrupt(struct adapter *padapter); extern void DisableInterrupt8723BSdio(struct adapter *padapter); extern u8 HalQueryTxBufferStatus8723BSdio(struct adapter *padapter); extern void HalQueryTxOQTBufferStatus8723BSdio(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index 04b3836dab73..bcaaf85c386d 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -204,7 +204,7 @@ static void sd_intf_start(struct adapter *padapter) if (!padapter) return; - EnableInterrupt8723BSdio(padapter); + rtw_sdio_enable_interrupt(padapter); } static void sd_intf_stop(struct adapter *padapter) From 4ff0ba8eaa11d00bcb8cca3b86265c688e3ef215 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Mon, 20 Apr 2026 11:46:42 +0700 Subject: [PATCH 0184/5214] staging: rtl8723bs: remove unused offset in phase 2 _BlockWrite() Commit c1314fe4d28f ("staging: rtl8723bs: remove all RT_TRACE logs in hal/ and os_dep/") removed the unnecessary RT_TRACE logs in hal/ and os_dep/ files, but left the variable "offset" in the Phase 2 _BlockWrite() function unused, so delete it. This is reported by Coverity Scan with CID 1408950 as UNUSED_VALUE. Signed-off-by: Robertus Diawan Chris Reviewed-by: Luka Gejak Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260420044651.164450-1-robertusdchris@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 74cc2de963f7..9a91fd0a96eb 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -68,8 +68,6 @@ static int _BlockWrite(struct adapter *padapter, void *buffer, u32 buffSize) /* 3 Phase #2 */ if (remainSize_p1) { - offset = blockCount_p1 * blockSize_p1; - blockCount_p2 = remainSize_p1/blockSize_p2; remainSize_p2 = remainSize_p1%blockSize_p2; } From 1384971796f0f705fbd3cea68d676807bc915329 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sat, 18 Apr 2026 22:49:02 +0530 Subject: [PATCH 0185/5214] staging: rtl8723bs: os_dep: remove unnecessary braces for single statement Remove redundant braces around single statement blocks to follow kernel coding style and improve readability. Signed-off-by: Shyam Sunder Reddy Padira Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260418171904.16479-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index bcaaf85c386d..97a1e0da3034 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -145,9 +145,8 @@ static void sdio_deinit(struct dvobj_priv *dvobj) sdio_claim_host(func); sdio_disable_func(func); - if (dvobj->irq_alloc) { + if (dvobj->irq_alloc) sdio_release_irq(func); - } sdio_release_host(func); } From aa107ae83d9f8bdacc5888b3137e04f18970ae6b Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sat, 18 Apr 2026 22:39:06 +0530 Subject: [PATCH 0186/5214] staging: rtl8723bs: os_dep: remove redundant else in rtw_dev_unload Remove the unnecessary else block following a break statment to simplify the control flow and improve code readability. Signed-off-by: Shyam Sunder Reddy Padira Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260418170908.16257-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index d55a68b27096..b80f1d03ed5a 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -989,12 +989,10 @@ void rtw_dev_unload(struct adapter *padapter) rtw_stop_drv_threads(padapter); while (atomic_read(&pcmdpriv->cmdthd_running)) { - if (cnt > 5) { + if (cnt > 5) break; - } else { - cnt++; - msleep(10); - } + cnt++; + msleep(10); } /* check the status of IPS */ From 7dbc9fd714387388ebe049a619513e60b5c442e8 Mon Sep 17 00:00:00 2001 From: Mohammed Rizwan Kaniyate Date: Sat, 25 Apr 2026 16:53:27 +0530 Subject: [PATCH 0187/5214] staging: rtl8723bs: remove multiple blank lines in core/ Remove multiple consecutive blank lines. Issue reported by checkpatch.pl Signed-off-by: Mohammed Rizwan Kaniyate Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260425112327.215355-1-mrizwank004@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 1 - .../staging/rtl8723bs/core/rtw_ioctl_set.c | 1 - drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 48 ------------------- drivers/staging/rtl8723bs/core/rtw_recv.c | 25 ---------- .../staging/rtl8723bs/core/rtw_wlan_util.c | 1 - 5 files changed, 76 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 94b00d3c61cf..a7a636449813 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -232,7 +232,6 @@ void rtw_efuse_shadow_map_update(struct adapter *padapter, u8 efuseType) /* void *)&pHalData->EfuseMap[EFUSE_INIT_MAP][0], mapLen); */ } /* rtw_efuse_shadow_map_update */ - /*----------------------------------------------------------------------------- * Function: rtw_efuse_shadow_read * diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index c70541f95a73..32c0af6af699 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -340,7 +340,6 @@ u8 rtw_set_802_11_infrastructure_mode(struct adapter *padapter, return true; } - u8 rtw_set_802_11_disassociate(struct adapter *padapter) { struct mlme_priv *pmlmepriv = &padapter->mlmepriv; diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 7d3b0d22943a..cfd3eb253350 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -520,7 +520,6 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _PROBEREQ_IE_OFFSET_, WLAN_EID_SSID, (int *)&ielen, len - WLAN_HDR_A3_LEN - _PROBEREQ_IE_OFFSET_); - /* check (wildcard) SSID */ if (p) { if (is_valid_p2p_probereq) @@ -773,7 +772,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) if (pstat->auth_seq == 0) pstat->expire_to = pstapriv->auth_to; - if ((pstat->auth_seq + 1) != seq) { status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto auth_fail; @@ -822,7 +820,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) } } - /* Now, we are going to issue_auth... */ pstat->auth_seq = seq + 1; @@ -831,7 +828,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) if (pstat->state & WIFI_FW_AUTH_SUCCESS) pstat->auth_seq = 0; - return _SUCCESS; auth_fail: @@ -958,7 +954,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) else /* WIFI_REASSOCREQ */ ie_offset = _REASOCREQ_IE_OFFSET_; - if (pkt_len < sizeof(struct ieee80211_hdr_3addr) + ie_offset) return _FAIL; @@ -988,7 +983,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) pstat->state |= WIFI_FW_ASSOC_STATE; } - pstat->capability = capab_info; /* now parse all ieee802_11 ie to point to elems */ @@ -1124,7 +1118,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) else pstat->flags |= WLAN_STA_MAYBE_WPS; - /* AP support WPA/RSN, and sta is going to do WPS, but AP is not ready */ /* that the selected registrar of AP is _FLASE */ if ((psecuritypriv->wpa_psk > 0) @@ -1159,13 +1152,11 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) copy_len = min(sizeof(pstat->wpa_ie), wpa_ie_len + 2u); } - if (copy_len > 0) memcpy(pstat->wpa_ie, wpa_ie-2, copy_len); } - /* check if there is WMM IE & support WWM-PS */ pstat->flags &= ~WLAN_STA_WME; pstat->qos_option = 0; @@ -1238,13 +1229,11 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) } else pstat->flags &= ~WLAN_STA_HT; - if ((pmlmepriv->htpriv.ht_option == false) && (pstat->flags&WLAN_STA_HT)) { status = WLAN_STATUS_CHALLENGE_FAIL; goto OnAssocReqFail; } - if ((pstat->flags & WLAN_STA_HT) && ((pstat->wpa2_pairwise_cipher&WPA_CIPHER_TKIP) || (pstat->wpa_pairwise_cipher&WPA_CIPHER_TKIP))) { @@ -1285,13 +1274,11 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) goto OnAssocReqFail; - } else { pstapriv->sta_aid[pstat->aid - 1] = pstat; } } - pstat->state &= (~WIFI_FW_ASSOC_STATE); pstat->state |= WIFI_FW_ASSOC_SUCCESS; @@ -1485,7 +1472,6 @@ unsigned int OnDeAuth(struct adapter *padapter, union recv_frame *precv_frame) associated_clients_update(padapter, updated); } - return _SUCCESS; } @@ -2115,7 +2101,6 @@ void issue_beacon(struct adapter *padapter, int timeout_ms) pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; pwlanhdr = (struct ieee80211_hdr *)pframe; - fctrl = &(pwlanhdr->frame_control); *(fctrl) = 0; @@ -2204,12 +2189,10 @@ void issue_beacon(struct adapter *padapter, int timeout_ms) pframe = rtw_set_ie(pframe, WLAN_EID_ERP_INFO, 1, &erpinfo, &pattrib->pktlen); } - /* EXTERNDED SUPPORTED RATE */ if (rate_len > 8) pframe = rtw_set_ie(pframe, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (cur_network->supported_rates + 8), &pattrib->pktlen); - /* todo:HT for adhoc */ _issue_bcn: @@ -2281,7 +2264,6 @@ void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p pattrib->pktlen = pattrib->hdrlen; pframe += pattrib->hdrlen; - if (cur_network->ie_length > MAX_IE_SZ) return; @@ -2405,19 +2387,16 @@ void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p pframe = rtw_set_ie(pframe, WLAN_EID_ERP_INFO, 1, &erpinfo, &pattrib->pktlen); } - /* EXTERNDED SUPPORTED RATE */ if (rate_len > 8) pframe = rtw_set_ie(pframe, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (cur_network->supported_rates + 8), &pattrib->pktlen); - /* todo:HT for adhoc */ } pattrib->last_txcmdsz = pattrib->pktlen; - dump_mgntframe(padapter, pmgntframe); } @@ -2446,7 +2425,6 @@ static int _issue_probereq(struct adapter *padapter, pattrib = &pmgntframe->attrib; update_mgntframe_attrib(padapter, pattrib); - memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; @@ -2590,7 +2568,6 @@ void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short pframe += sizeof(struct ieee80211_hdr_3addr); pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); - if (psta) { /* for AP mode */ memcpy(pwlanhdr->addr1, psta->hwaddr, ETH_ALEN); memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN); @@ -2651,7 +2628,6 @@ void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short le_tmp = cpu_to_le16(pmlmeinfo->auth_seq); pframe = rtw_set_fixed_ie(pframe, _AUTH_SEQ_NUM_, (unsigned char *)&le_tmp, &(pattrib->pktlen)); - /* setting status code... */ le_tmp = cpu_to_le16(status); pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, (unsigned char *)&le_tmp, &(pattrib->pktlen)); @@ -2680,7 +2656,6 @@ void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short dump_mgntframe(padapter, pmgntframe); } - void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_info *pstat, int pkt_type) { struct xmit_frame *pmgntframe; @@ -2705,7 +2680,6 @@ void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_i pattrib = &pmgntframe->attrib; update_mgntframe_attrib(padapter, pattrib); - memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; @@ -2718,7 +2692,6 @@ void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_i memcpy(GetAddr2Ptr(pwlanhdr), myid(&(padapter->eeprompriv)), ETH_ALEN); memcpy(GetAddr3Ptr(pwlanhdr), get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN); - SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); pmlmeext->mgnt_seq++; if ((pkt_type == WIFI_ASSOCRSP) || (pkt_type == WIFI_REASSOCRSP)) @@ -2877,7 +2850,6 @@ void issue_assocreq(struct adapter *padapter) if (pmlmeext->cur_channel == 14) /* for JAPAN, channel 14 can only uses B Mode(CCK) */ sta_bssrate_len = 4; - /* for (i = 0; i < sta_bssrate_len; i++) { */ /* */ @@ -2886,12 +2858,10 @@ void issue_assocreq(struct adapter *padapter) break; } - for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) { if (pmlmeinfo->network.supported_rates[i] == 0) break; - /* Check if the AP's supported rates are also supported by STA. */ for (j = 0; j < sta_bssrate_len; j++) { /* Avoid the proprietary data rate (22Mbps) of Handlink WSG-4000 AP */ @@ -2913,7 +2883,6 @@ void issue_assocreq(struct adapter *padapter) goto exit; /* don't connect to AP if no joint supported rate */ } - if (bssrate_len > 8) { pframe = rtw_set_ie(pframe, WLAN_EID_SUPP_RATES, 8, bssrate, &(pattrib->pktlen)); pframe = rtw_set_ie(pframe, WLAN_EID_EXT_SUPP_RATES, (bssrate_len - 8), (bssrate + 8), &(pattrib->pktlen)); @@ -2970,7 +2939,6 @@ void issue_assocreq(struct adapter *padapter) if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK) pframe = rtw_set_ie(pframe, WLAN_EID_VENDOR_SPECIFIC, 6, REALTEK_96B_IE, &(pattrib->pktlen)); - pattrib->last_txcmdsz = pattrib->pktlen; dump_mgntframe(padapter, pmgntframe); @@ -3067,7 +3035,6 @@ int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int pow struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; struct sta_info *psta; - /* da == NULL, assume it's null data for sta to ap*/ if (!da) da = get_my_bssid(&(pmlmeinfo->network)); @@ -3117,7 +3084,6 @@ s32 issue_nulldata_in_interrupt(struct adapter *padapter, u8 *da) struct mlme_ext_priv *pmlmeext; struct mlme_ext_info *pmlmeinfo; - pmlmeext = &padapter->mlmeextpriv; pmlmeinfo = &pmlmeext->mlmext_info; @@ -3286,7 +3252,6 @@ static int _issue_deauth(struct adapter *padapter, unsigned char *da, pattrib->last_txcmdsz = pattrib->pktlen; - if (wait_ack) { ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe); } else { @@ -3608,7 +3573,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen)); pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen)); - /* */ if (pmlmepriv->num_FortyMHzIntolerant > 0) { u8 iedata = 0; @@ -3619,7 +3583,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) } - /* */ memset(ICS, 0, sizeof(ICS)); if (pmlmepriv->num_sta_no_ht > 0) { @@ -3660,7 +3623,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - for (i = 0; i < 8; i++) { int j, k = 0; @@ -3688,7 +3650,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) } - pattrib->last_txcmdsz = pattrib->pktlen; dump_mgntframe(padapter, pmgntframe); @@ -4164,7 +4125,6 @@ void start_clnt_auth(struct adapter *padapter) pmlmeinfo->link_count = 0; pmlmeext->retry = 0; - netdev_dbg(padapter->pnetdev, "start auth\n"); issue_auth(padapter, NULL, 0); @@ -4172,7 +4132,6 @@ void start_clnt_auth(struct adapter *padapter) } - void start_clnt_assoc(struct adapter *padapter) { struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; @@ -4219,7 +4178,6 @@ static void process_80211d(struct adapter *padapter, struct wlan_bssid_ex *bssid u8 channel; u8 i; - pregistrypriv = &padapter->registrypriv; pmlmeext = &padapter->mlmeextpriv; @@ -4493,10 +4451,8 @@ void report_join_res(struct adapter *padapter, int res) memcpy((unsigned char *)(&(pjoinbss_evt->network.network)), &(pmlmeinfo->network), sizeof(struct wlan_bssid_ex)); pjoinbss_evt->network.join_res = pjoinbss_evt->network.aid = res; - rtw_joinbss_event_prehandle(padapter, (u8 *)&pjoinbss_evt->network); - rtw_enqueue_cmd(pcmdpriv, pcmd_obj); } @@ -4582,7 +4538,6 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi memcpy((unsigned char *)(&(pdel_sta_evt->macaddr)), MacAddr, ETH_ALEN); memcpy((unsigned char *)(pdel_sta_evt->rsvd), (unsigned char *)(&reason), 2); - psta = rtw_get_stainfo(&padapter->stapriv, MacAddr); if (psta) mac_id = (int)psta->mac_id; @@ -4766,7 +4721,6 @@ void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) /* update bc/mc sta_info */ update_bmc_sta(padapter); - /* turn on dynamic functions */ Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true); @@ -5079,7 +5033,6 @@ void link_timer_hdl(struct timer_list *t) struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) { pmlmeinfo->state = WIFI_FW_NULL_STATE; report_join_res(padapter, -3); @@ -5265,7 +5218,6 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) /* set_msr(padapter, _HW_STATE_NOLINK_); */ set_msr(padapter, _HW_STATE_STATION_); - rtw_hal_set_hwreg(padapter, HW_VAR_MLME_DISCONNECT, NULL); } diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index df9c59449de5..e11127992cfd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -57,7 +57,6 @@ signed int _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *pada precvframe = (union recv_frame *) precvpriv->precv_frame_buf; - for (i = 0; i < NR_RECVFRAME; i++) { INIT_LIST_HEAD(&(precvframe->u.list)); @@ -177,9 +176,6 @@ int rtw_free_recvframe(union recv_frame *precvframe, struct __queue *pfree_recv_ return _SUCCESS; } - - - signed int _rtw_enqueue_recvframe(union recv_frame *precvframe, struct __queue *queue) { @@ -189,7 +185,6 @@ signed int _rtw_enqueue_recvframe(union recv_frame *precvframe, struct __queue * /* INIT_LIST_HEAD(&(precvframe->u.hdr.list)); */ list_del_init(&(precvframe->u.hdr.list)); - list_add_tail(&(precvframe->u.hdr.list), get_list_head(queue)); if (padapter) @@ -254,7 +249,6 @@ u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter) return cnt; } - signed int rtw_enqueue_recvbuf_to_head(struct recv_buf *precvbuf, struct __queue *queue) { spin_lock_bh(&queue->lock); @@ -402,7 +396,6 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p bmic_err = true; } - if (bmic_err == true) { /* double check key_index for some timing issue , */ /* cannot compare with psecuritypriv->dot118021XGrpKeyid also cause timing issue */ @@ -769,8 +762,6 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * } else ret = _FAIL; - - if (bmcast) *psta = rtw_get_bcmc_stainfo(adapter); else @@ -814,7 +805,6 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p goto exit; } - /* check BSSID */ if (is_zero_ether_addr(pattrib->bssid) || is_zero_ether_addr(mybssid) || @@ -855,14 +845,12 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p /* */ memcpy(pattrib->bssid, mybssid, ETH_ALEN); - *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get sta_info */ if (!*psta) { ret = _FAIL; goto exit; } - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { /* Special case */ ret = RTW_RX_HANDLED; @@ -1106,7 +1094,6 @@ static union recv_frame *recvframe_defrag(struct adapter *adapter, pnextrframe = (union recv_frame *)plist; pnfhdr = &pnextrframe->u.hdr; - /* check the fragment sequence (2nd ~n fragment frame) */ if (curfragnum != pnfhdr->attrib.frag_num) { @@ -1194,7 +1181,6 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re /* free current defrag_q */ rtw_free_recvframe_queue(pdefrag_q, pfree_recv_queue); - /* Then enqueue the 0~(n-1) fragment into the defrag_q */ /* spin_lock(&pdefrag_q->lock); */ @@ -1233,7 +1219,6 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re } - if ((prtnframe) && (prtnframe->u.hdr.attrib.privacy)) { /* after defrag we must check tkip mic code */ if (recvframe_chkmic(padapter, prtnframe) == _FAIL) { @@ -1346,7 +1331,6 @@ static signed int validate_recv_data_frame(struct adapter *adapter, union recv_f if (ret == _FAIL || ret == RTW_RX_HANDLED) goto exit; - if (!psta) { ret = _FAIL; goto exit; @@ -1356,7 +1340,6 @@ static signed int validate_recv_data_frame(struct adapter *adapter, union recv_f /* psta->signal_quality = prxcmd->sq; */ precv_frame->u.hdr.psta = psta; - pattrib->amsdu = 0; pattrib->ack_policy = 0; /* parsing QC field */ @@ -1374,7 +1357,6 @@ static signed int validate_recv_data_frame(struct adapter *adapter, union recv_f pattrib->hdrlen = pattrib->to_fr_ds == 3 ? 30 : 24; } - if (pattrib->order)/* HT-CTRL 11n */ pattrib->hdrlen += 4; @@ -1824,7 +1806,6 @@ static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl, un /* spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); */ /* spin_lock(&ppending_recvframe_queue->lock); */ - phead = get_list_head(ppending_recvframe_queue); plist = get_next(phead); @@ -1843,7 +1824,6 @@ static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl, un } - /* spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); */ /* spin_lock(&ppending_recvframe_queue->lock); */ @@ -1959,7 +1939,6 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor /* error condition; */ } - /* Update local variables. */ bPktInBuf = false; @@ -2034,7 +2013,6 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * goto _err_exit; } - /* s4. */ /* Indication process. */ /* After Packet dropping and Sliding Window shifting as above, we can now just indicate the packets */ @@ -2062,7 +2040,6 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * return _FAIL; } - void rtw_reordering_ctrl_timeout_handler(struct timer_list *t) { struct recv_reorder_ctrl *preorder_ctrl = @@ -2070,7 +2047,6 @@ void rtw_reordering_ctrl_timeout_handler(struct timer_list *t) struct adapter *padapter = preorder_ctrl->padapter; struct __queue *ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue; - if (padapter->bDriverStopped || padapter->bSurpriseRemoved) return; @@ -2218,7 +2194,6 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) return ret; } - s32 rtw_recv_entry(union recv_frame *precvframe) { struct adapter *padapter; diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 3762ae6c4c65..cc1a7497764c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -340,7 +340,6 @@ void set_channel_bwmode(struct adapter *padapter, unsigned char channel, unsigne center_ch = rtw_get_center_ch(channel, bwmode, channel_offset); - /* set Channel */ if (mutex_lock_interruptible(&(adapter_to_dvobj(padapter)->setch_mutex))) return; From 4e2475038cba7681fb1a92ba5cafdf4e93764e4c Mon Sep 17 00:00:00 2001 From: Henrique Cazarim Date: Thu, 23 Apr 2026 17:12:12 -0300 Subject: [PATCH 0188/5214] staging: rtl8723bs: add spaces arround | Fix checkpatch error "CHECK: spaces preferred around that '|'" in rtw_ioctl_set.c:154. Signed-off-by: Henrique Cazarim Link: https://patch.msgid.link/20260423201212.77701-1-hcazarim@yahoo.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ioctl_set.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index 32c0af6af699..69cbf89f0745 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -151,7 +151,7 @@ u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) goto release_mlme_lock; - if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) { + if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE) == true) { if ((pmlmepriv->assoc_ssid.ssid_length == ssid->ssid_length) && (!memcmp(&pmlmepriv->assoc_ssid.ssid, ssid->ssid, ssid->ssid_length))) { if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == false) { From 8417e4cd6ce06f1f79a22091bc6cc82677490315 Mon Sep 17 00:00:00 2001 From: Swati Agarwal Date: Fri, 3 Apr 2026 15:34:34 +0530 Subject: [PATCH 0189/5214] dt-bindings: usb: Add Genesys Logic GL3590 hub Add the binding for the USB3.2 Genesys Logic GL3590 hub. GL3590 hub requires 1.2V and 3.3V supplies for operation. Signed-off-by: Swati Agarwal Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260403100435.3477729-2-swati.agarwal@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/genesys,gl850g.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index 9a94b2a74a1e..e8b8c03f87a0 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -15,6 +15,7 @@ properties: - usb5e3,608 - usb5e3,610 - usb5e3,620 + - usb5e3,625 - usb5e3,626 reg: true @@ -26,6 +27,10 @@ properties: description: The regulator that provides 3.3V or 5.0V core power to the hub. + vdd12-supply: + description: + The regulator that provides 1.2V power to the hub. + peer-hub: true ports: @@ -56,6 +61,7 @@ allOf: properties: peer-hub: false vdd-supply: false + vdd12-supply: false - if: properties: @@ -68,6 +74,18 @@ allOf: properties: peer-hub: true vdd-supply: true + vdd12-supply: false + + - if: + properties: + compatible: + contains: + enum: + - usb5e3,625 + then: + properties: + peer-hub: true + vdd-supply: true unevaluatedProperties: false From 9a71ac508974d44d42d358f42e6a766ec67a73d1 Mon Sep 17 00:00:00 2001 From: Swati Agarwal Date: Fri, 3 Apr 2026 15:34:35 +0530 Subject: [PATCH 0190/5214] usb: misc: onboard_usb_hub: Add Genesys Logic GL3590 hub support Add support for the GL3590 4 ports USB3.2 hub. Reviewed-by: Dmitry Baryshkov Signed-off-by: Swati Agarwal Link: https://patch.msgid.link/20260403100435.3477729-3-swati.agarwal@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_dev.c | 1 + drivers/usb/misc/onboard_usb_dev.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 7cdbdfe07a76..11508ed4df25 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -665,6 +665,7 @@ static const struct usb_device_id onboard_dev_id_table[] = { { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 HUB */ + { USB_DEVICE(VENDOR_ID_GENESYS, 0x0625) }, /* Genesys Logic GL3590 USB 3.2 HUB */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */ diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index ac1aa3e122ad..3523f8f8a149 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -101,6 +101,13 @@ static const struct onboard_dev_pdata genesys_gl852g_data = { .is_hub = true, }; +static const struct onboard_dev_pdata genesys_gl3590_data = { + .reset_us = 50, + .num_supplies = 2, + .supply_names = { "vdd", "vdd12" }, + .is_hub = true, +}; + static const struct onboard_dev_pdata usb_a_conn_data = { .num_supplies = 1, .supply_names = { "vbus" }, @@ -146,6 +153,7 @@ static const struct of_device_id onboard_dev_match[] = { { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,625", .data = &genesys_gl3590_data, }, { .compatible = "usb5e3,626", .data = &genesys_gl852g_data, }, { .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, From e68fddb47aad85ebd294a051243066f29da20d8d Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 20 Apr 2026 12:23:57 +0200 Subject: [PATCH 0191/5214] usb: cdnsp: add support for eUSB2v2 port The Cadence CDNSP controller optionally supports eUSB2 (embedded USB2) port. While this port type operates logically like high-speed USB 2.0, it utilizes a different physical layer signaling. This patch: - Extends the port detection logic to recognize the eUSB2 protocol. - Tracks the eUSB2 port offset in the cdnsp_device structure. - Ensures that eUSB2 ports are correctly handled during Link State transitions, specifically forcing L0 when LPM is capable, similar to standard USB 2.0 ports. Signed-off-by: Pawel Laszczak Acked-by: Peter Chen Link: https://patch.msgid.link/20260420-eusb2v2_upstream-v2-1-9883645e2ede@cadence.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-gadget.c | 49 ++++++++++++++------- drivers/usb/cdns3/cdnsp-gadget.h | 1 + drivers/usb/cdns3/cdnsp-mem.c | 73 ++++++++++++++++++++++---------- drivers/usb/cdns3/cdnsp-ring.c | 9 ++-- 4 files changed, 90 insertions(+), 42 deletions(-) diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 6b3815f8a6e5..2c71c77e6ec3 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -124,20 +124,28 @@ void cdnsp_set_link_state(struct cdnsp_device *pdev, } static void cdnsp_disable_port(struct cdnsp_device *pdev, - __le32 __iomem *port_regs) + struct cdnsp_port *port) { - u32 temp = cdnsp_port_state_to_neutral(readl(port_regs)); + u32 temp; - writel(temp | PORT_PED, port_regs); + if (!port->exist) + return; + + temp = cdnsp_port_state_to_neutral(readl(&port->regs->portsc)); + writel(temp | PORT_PED, &port->regs->portsc); } static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev, - __le32 __iomem *port_regs) + struct cdnsp_port *port) { - u32 portsc = readl(port_regs); + u32 portsc; + if (!port->exist) + return; + + portsc = readl(&port->regs->portsc); writel(cdnsp_port_state_to_neutral(portsc) | - (portsc & PORT_CHANGE_BITS), port_regs); + (portsc & PORT_CHANGE_BITS), &port->regs->portsc); } static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) @@ -944,7 +952,7 @@ void cdnsp_set_usb2_hardware_lpm(struct cdnsp_device *pdev, struct usb_request *req, int enable) { - if (pdev->active_port != &pdev->usb2_port || !pdev->gadget.lpm_capable) + if (pdev->active_port == &pdev->usb3_port || !pdev->gadget.lpm_capable) return; trace_cdnsp_lpm(enable); @@ -1310,20 +1318,26 @@ static int cdnsp_run(struct cdnsp_device *pdev, break; } - if (speed >= USB_SPEED_SUPER) { + if (pdev->usb3_port.exist && speed >= USB_SPEED_SUPER) { writel(temp, &pdev->port3x_regs->mode_addr); cdnsp_set_link_state(pdev, &pdev->usb3_port.regs->portsc, XDEV_RXDETECT); } else { - cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_disable_port(pdev, &pdev->usb3_port); } - cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc, - XDEV_RXDETECT); + if (pdev->usb2_port.exist) { + cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc, + XDEV_RXDETECT); + writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6); + } + + if (pdev->eusb_port.exist) + cdnsp_set_link_state(pdev, &pdev->eusb_port.regs->portsc, + XDEV_RXDETECT); cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6); ret = cdnsp_start(pdev); if (ret) { @@ -1469,8 +1483,10 @@ static void cdnsp_stop(struct cdnsp_device *pdev) cdnsp_ep_dequeue(&pdev->eps[0], req); } - cdnsp_disable_port(pdev, &pdev->usb2_port.regs->portsc); - cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_disable_port(pdev, &pdev->usb2_port); + cdnsp_disable_port(pdev, &pdev->usb3_port); + cdnsp_disable_port(pdev, &pdev->eusb_port); + cdnsp_disable_slot(pdev); cdnsp_halt(pdev); @@ -1479,8 +1495,9 @@ static void cdnsp_stop(struct cdnsp_device *pdev) temp = readl(&pdev->ir_set->irq_pending); writel(IMAN_IE_CLEAR(temp), &pdev->ir_set->irq_pending); - cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port.regs->portsc); - cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port); + cdnsp_clear_port_change_bit(pdev, &pdev->eusb_port); + cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port); /* Clear interrupt line */ temp = readl(&pdev->ir_set->irq_pending); diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index a91cca509db0..c44bca348a41 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -1474,6 +1474,7 @@ struct cdnsp_device { unsigned int link_state; struct cdnsp_port usb2_port; + struct cdnsp_port eusb_port; struct cdnsp_port usb3_port; struct cdnsp_port *active_port; u16 test_mode; diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index a2a1b21f2ef8..5d8cdc91927d 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -1088,11 +1088,9 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev) pdev->dcbaa, pdev->dcbaa->dma); pdev->dcbaa = NULL; - - pdev->usb2_port.exist = 0; - pdev->usb3_port.exist = 0; - pdev->usb2_port.port_num = 0; - pdev->usb3_port.port_num = 0; + memset(&pdev->usb2_port, 0, sizeof(struct cdnsp_port)); + memset(&pdev->eusb_port, 0, sizeof(struct cdnsp_port)); + memset(&pdev->usb3_port, 0, sizeof(struct cdnsp_port)); pdev->active_port = NULL; } @@ -1133,6 +1131,18 @@ static void cdnsp_add_in_port(struct cdnsp_device *pdev, port_offset = CDNSP_EXT_PORT_OFF(temp); port_count = CDNSP_EXT_PORT_COUNT(temp); + if (port == &pdev->eusb_port) { + /* + * If controller has usb2 + eusb port then eusb is as + * second port + */ + if (port_count == 2) + port_offset++; + + if (port_count == 1 && pdev->usb2_port.exist) + return; + } + trace_cdnsp_port_info(addr, port_offset, port_count, port->maj_rev); port->port_num = port_offset; @@ -1152,13 +1162,10 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) base = &pdev->cap_regs->hc_capbase; offset = cdnsp_find_next_ext_cap(base, 0, EXT_CAP_CFG_DEV_20PORT_CAP_ID); - pdev->port20_regs = base + offset; - - offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP); - pdev->port3x_regs = base + offset; + if (offset) + pdev->port20_regs = base + offset; offset = 0; - base = &pdev->cap_regs->hc_capbase; /* Driver expects max 2 extended protocol capability. */ for (i = 0; i < 2; i++) { @@ -1173,26 +1180,46 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) cdnsp_add_in_port(pdev, &pdev->usb3_port, base + offset); - if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02 && - !pdev->usb2_port.port_num) - cdnsp_add_in_port(pdev, &pdev->usb2_port, - base + offset); + if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02) { + if (!pdev->usb2_port.port_num && pdev->port20_regs) + cdnsp_add_in_port(pdev, &pdev->usb2_port, + base + offset); + + if (!pdev->eusb_port.port_num) + cdnsp_add_in_port(pdev, &pdev->eusb_port, + base + offset); + } } - if (!pdev->usb2_port.exist || !pdev->usb3_port.exist) { - dev_err(pdev->dev, "Error: Only one port detected\n"); + if (!pdev->usb2_port.exist && !pdev->eusb_port.exist && + !pdev->usb3_port.exist) { + dev_err(pdev->dev, "Error: No port detected\n"); return -ENODEV; } - trace_cdnsp_init("Found USB 2.0 ports and USB 3.0 ports."); + if (pdev->usb2_port.exist) { + pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->usb2_port.port_num - 1)); + trace_cdnsp_init("Found USB 2.0 port."); + } - pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *) - (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * - (pdev->usb2_port.port_num - 1)); + if (pdev->eusb_port.exist) { + pdev->eusb_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->eusb_port.port_num - 1)); + trace_cdnsp_init("Found eUSB 2.0 port."); + } - pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *) - (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * - (pdev->usb3_port.port_num - 1)); + if (pdev->usb3_port.exist) { + offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP); + pdev->port3x_regs = base + offset; + + pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->usb3_port.port_num - 1)); + trace_cdnsp_init("Found USB 3.x port."); + } return 0; } diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 0758f171f73e..715658c981ff 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -259,7 +259,7 @@ static bool cdnsp_room_on_ring(struct cdnsp_device *pdev, */ static void cdnsp_force_l0_go(struct cdnsp_device *pdev) { - if (pdev->active_port == &pdev->usb2_port && pdev->gadget.lpm_capable) + if (pdev->active_port != &pdev->usb3_port && pdev->gadget.lpm_capable) cdnsp_set_link_state(pdev, &pdev->active_port->regs->portsc, XDEV_U0); } @@ -763,6 +763,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) if (port_id == pdev->usb2_port.port_num) { port = &pdev->usb2_port; + } else if (port_id == pdev->eusb_port.port_num) { + port = &pdev->eusb_port; } else if (port_id == pdev->usb3_port.port_num) { port = &pdev->usb3_port; } else { @@ -779,7 +781,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) cdnsp_enable_slot(pdev); } - if (port_id == pdev->usb2_port.port_num) + if ((pdev->usb2_port.exist && port_id == pdev->usb2_port.port_num) || + (pdev->eusb_port.exist && port_id == pdev->eusb_port.port_num)) cdnsp_set_usb2_hardware_lpm(pdev, NULL, 1); else writel(PORT_U1_TIMEOUT(1) | PORT_U2_TIMEOUT(1), @@ -808,7 +811,7 @@ static void cdnsp_handle_port_status(struct cdnsp_device *pdev, port_regs = pdev->active_port->regs; - if (port_id == pdev->usb2_port.port_num) + if (port_id == pdev->usb2_port.port_num || port_id == pdev->eusb_port.port_num) port2 = true; new_event: From 62911bc82b0332aee7546156800d3516500fa1e1 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 23 Apr 2026 17:53:55 +0800 Subject: [PATCH 0192/5214] usb: gadget: udc: skip pullup() if already connected The device controller may update vbus status via usb_udc_vbus_handler(), which tries to connect the gadget even though gadget_bind_driver() has already called usb_udc_connect_control_locked(). This causes pullup() to be called twice. Avoid this by checking if gadget->connected is true. This also set gadget->connected as false in usb_gadget_activate() if it became connected while it was being deactivated. Otherwise, usb_gadget_connect_locked will return early and pullup() won't be called. Signed-off-by: Xu Yang Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260423095355.2673035-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e8861eaad907..60340ff9edbf 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -712,6 +712,9 @@ static int usb_gadget_connect_locked(struct usb_gadget *gadget) goto out; } + if (gadget->connected) + goto out; + if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) { /* * If the gadget isn't usable (because it is deactivated, @@ -885,8 +888,10 @@ int usb_gadget_activate(struct usb_gadget *gadget) * If gadget has been connected before deactivation, or became connected * while it was being deactivated, we call usb_gadget_connect(). */ - if (gadget->connected) + if (gadget->connected) { + gadget->connected = false; ret = usb_gadget_connect_locked(gadget); + } unlock: mutex_unlock(&gadget->udc->connect_lock); From b379998c8261d458dc3cc7e09ef8ccbfa9d2335f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 21 Apr 2026 10:34:58 +0800 Subject: [PATCH 0193/5214] dt-bindings: usb: cdns,usb3: document USBSSP controller support Update the Cadence USBSS DRD binding to document that it also covers the USBSSP (SuperSpeed Plus, USB 3.1 gen2x1) controller. Both USBSS and USBSSP share the same DRD/OTG register interface, so the driver auto-detects the controller version at runtime: no additional compatible string is needed. Changes to the binding: - Update title and add description - maximum-speed: add super-speed-plus Assisted-by: Cursor:claude-4.6-opus Acked-by: Rob Herring (Arm) Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260421023459.506145-2-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/cdns,usb3.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index a199e5ba6416..2d95fb7321af 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml @@ -4,11 +4,17 @@ $id: http://devicetree.org/schemas/usb/cdns,usb3.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Cadence USBSS-DRD controller +title: Cadence USBSS and USBSSP DRD controller maintainers: - Pawel Laszczak +description: + Cadence USB dual-role controller. Covers USBSS (SuperSpeed, USB 3.0) and + USBSSP (SuperSpeed Plus, USB 3.1 gen2x1). Both variants share the same + DRD/OTG register interface, so the driver auto-detects the controller + version at runtime. + properties: compatible: const: cdns,usb3 @@ -49,7 +55,7 @@ properties: cdns3 to type C connector. maximum-speed: - enum: [super-speed, high-speed, full-speed] + enum: [super-speed-plus, super-speed, high-speed, full-speed] phys: minItems: 1 From e4d7362dc9cd50b0fc74c8649aa241376c48936c Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 21 Apr 2026 10:34:59 +0800 Subject: [PATCH 0194/5214] usb: cdns3: Add USBSSP platform driver support Expose Cadence USBSSP through the same platform path as USBSS, trim Kconfig and Makefile: one core loadable object plus separate glue .ko files. Single cdns.ko bundles core, DRD, the generic "cdns,usb3" platform driver in cdns3-plat.c, optional host.o, and optional gadget objects. Use CONFIG_USB_CDNS3_GADGET as a bool to compile gadget support into that module. Remove duplicate MODULE_* declarations from cdns3-plat.c now that it links into the same module. Kconfig: the generic platform driver is selected via CONFIG_USB_CDNS3. Move CONFIG_USB_CDNSP_PCI beside CONFIG_USB_CDNS3_PCI_WRAP under "Platform glue driver support". SoC glue entries (TI, i.MX, StarFive) depend only on CONFIG_USB_CDNS3. Tighten CONFIG_USB_CDNS_SUPPORT dependencies so the umbrella follows host or gadget when either is built as a module. Match host and gadget bools to the cdns.ko tristate with USB=USB_CDNS3 and USB_GADGET=USB_CDNS3 instead of comparing against USB_CDNS_SUPPORT. Link host.o when CONFIG_USB_CDNS3_HOST is enabled and use that symbol in host-export.h, removing the redundant CONFIG_USB_CDNS_HOST indirection. Export cdns_core_init_role and reorganize the function cdns_init, and controller version could be gotten before the gadget init function is decided per controller. Keep host_init / gadget_init callbacks in struct cdns, so core.c does not need direct linkage to host or gadget objects. Refactor cdnsp-pci.c into a thin PCI-to-platform wrapper. drivers/usb/Makefile: descend into drivers/usb/cdns3/ only when CONFIG_USB_CDNS_SUPPORT is enabled. Assisted-by: Cursor:claude-4.6-opus Suggested-by: Arnd Bergmann Signed-off-by: Peter Chen Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260421023459.506145-3-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 2 - drivers/usb/cdns3/Kconfig | 112 +++++++--------- drivers/usb/cdns3/Makefile | 46 +++---- drivers/usb/cdns3/cdns3-gadget.c | 1 + drivers/usb/cdns3/cdns3-plat.c | 27 +++- drivers/usb/cdns3/cdnsp-gadget.c | 1 + drivers/usb/cdns3/cdnsp-pci.c | 209 +++++++++++++----------------- drivers/usb/cdns3/core.c | 45 ++----- drivers/usb/cdns3/core.h | 5 +- drivers/usb/cdns3/gadget-export.h | 10 +- drivers/usb/cdns3/host-export.h | 4 +- 11 files changed, 198 insertions(+), 264 deletions(-) diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6f3c86149887..eecbd631fdab 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -14,8 +14,6 @@ obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_ISP1760) += isp1760/ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ -obj-$(CONFIG_USB_CDNS3) += cdns3/ -obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ obj-$(CONFIG_USB_FOTG210) += fotg210/ diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 0a514b591527..39ad23d1ada8 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -1,6 +1,9 @@ config USB_CDNS_SUPPORT tristate "Cadence USB Support" - depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA + depends on USB_SUPPORT && HAS_DMA + depends on USB || USB_GADGET + depends on USB if !USB_GADGET + depends on USB_GADGET if !USB select USB_XHCI_PLATFORM if USB_XHCI_HCD select USB_ROLE_SWITCH help @@ -8,44 +11,49 @@ config USB_CDNS_SUPPORT dual-role controller. It supports: dual-role switch, Host-only, and Peripheral-only. -config USB_CDNS_HOST - bool - if USB_CDNS_SUPPORT config USB_CDNS3 - tristate "Cadence USB3 Dual-Role Controller" + tristate "Cadence USB dual-role controller (USBSS and USBSSP)" depends on USB_CDNS_SUPPORT help - Say Y here if your system has a Cadence USB3 dual-role controller. - It supports: dual-role switch, Host-only, and Peripheral-only. + Say Y or M here if your system has an on-chip Cadence USB + dual-role controller. This covers both USBSS (USB 3.0) and + USBSSP (SuperSpeed Plus) IP; the driver detects the variant at + runtime. - If you choose to build this driver is a dynamically linked - as module, the module will be called cdns3.ko. -endif + The core driver (core, DRD, generic platform binding for the + "cdns,usb3" device tree compatible, optional host and gadget) + builds as one module named cdns.ko when built as a loadable + module. + + It supports: dual-role switch, Host-only, and Peripheral-only. if USB_CDNS3 +config USB_CDNS3_HOST + bool "Cadence USB host controller (xHCI)" + depends on USB=y || USB=USB_CDNS3 + help + Say Y here to enable host controller functionality for Cadence + USBSS and USBSSP dual-role controllers. + + The host controller is xHCI compliant and uses the standard + xHCI driver. + config USB_CDNS3_GADGET - bool "Cadence USB3 device controller" + bool "Cadence USB device controller (USBSS and USBSSP)" depends on USB_GADGET=y || USB_GADGET=USB_CDNS3 help - Say Y here to enable device controller functionality of the - Cadence USBSS-DEV driver. + Say Y here to include Cadence USB device (gadget) support for + both USBSS (USB 3.0) and USBSSP (SuperSpeed Plus) IP in the + cdns.ko module. The implementation is selected at runtime from + the detected controller version. - This controller supports FF, HS and SS mode. It doesn't support - LS and SSP mode. + USBSS gadget supports FF, HS and SS mode (not LS or SSP). + USBSSP gadget supports FF, HS, SS and SSP mode (not LS). -config USB_CDNS3_HOST - bool "Cadence USB3 host controller" - depends on USB=y || USB=USB_CDNS3 - select USB_CDNS_HOST - help - Say Y here to enable host controller functionality of the - Cadence driver. - - Host controller is compliant with XHCI so it will use - standard XHCI driver. +comment "Platform glue driver support" config USB_CDNS3_PCI_WRAP tristate "Cadence USB3 support on PCIe-based platforms" @@ -58,6 +66,17 @@ config USB_CDNS3_PCI_WRAP If you choose to build this driver as module it will be dynamically linked and module will be called cdns3-pci.ko +config USB_CDNSP_PCI + tristate "Cadence USBSSP support on PCIe-based platforms" + depends on USB_PCI && ACPI + default USB_CDNS3 + help + If you're using the USBSSP Core IP with a PCIe, please say + 'Y' or 'M' here. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdnsp-pci.ko + config USB_CDNS3_TI tristate "Cadence USB3 support on TI platforms" depends on ARCH_K3 || COMPILE_TEST @@ -81,6 +100,7 @@ config USB_CDNS3_IMX config USB_CDNS3_STARFIVE tristate "Cadence USB3 support on StarFive SoC platforms" depends on ARCH_STARFIVE || COMPILE_TEST + default USB_CDNS3 help Say 'Y' or 'M' here if you are building for StarFive SoCs platforms that contain Cadence USB3 controller core. @@ -89,45 +109,7 @@ config USB_CDNS3_STARFIVE If you choose to build this driver as module it will be dynamically linked and module will be called cdns3-starfive.ko -endif -if USB_CDNS_SUPPORT +endif # USB_CDNS3 -config USB_CDNSP_PCI - tristate "Cadence CDNSP Dual-Role Controller" - depends on USB_CDNS_SUPPORT && USB_PCI && ACPI - help - Say Y here if your system has a Cadence CDNSP dual-role controller. - It supports: dual-role switch Host-only, and Peripheral-only. - - If you choose to build this driver is a dynamically linked - module, the module will be called cdnsp.ko. -endif - -if USB_CDNSP_PCI - -config USB_CDNSP_GADGET - bool "Cadence CDNSP device controller" - depends on USB_GADGET=y || USB_GADGET=USB_CDNSP_PCI - help - Say Y here to enable device controller functionality of the - Cadence CDNSP-DEV driver. - - Cadence CDNSP Device Controller in device mode is - very similar to XHCI controller. Therefore some algorithms - used has been taken from host driver. - This controller supports FF, HS, SS and SSP mode. - It doesn't support LS. - -config USB_CDNSP_HOST - bool "Cadence CDNSP host controller" - depends on USB=y || USB=USB_CDNSP_PCI - select USB_CDNS_HOST - help - Say Y here to enable host controller functionality of the - Cadence driver. - - Host controller is compliant with XHCI so it uses - standard XHCI driver. - -endif +endif # USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 48dfae75b5aa..b2e4ba6a49a3 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -3,42 +3,28 @@ CFLAGS_cdns3-trace.o := -I$(src) CFLAGS_cdnsp-trace.o := -I$(src) -cdns-usb-common-y := core.o drd.o -cdns3-y := cdns3-plat.o +obj-$(CONFIG_USB_CDNS3) += cdns.o -ifeq ($(CONFIG_USB),m) -obj-m += cdns-usb-common.o -obj-m += cdns3.o -else -obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns-usb-common.o -obj-$(CONFIG_USB_CDNS3) += cdns3.o -endif - -cdns-usb-common-$(CONFIG_USB_CDNS_HOST) += host.o -cdns3-$(CONFIG_USB_CDNS3_GADGET) += cdns3-gadget.o cdns3-ep0.o +cdns-y := core.o drd.o cdns3-plat.o +cdns-$(CONFIG_USB_CDNS3_HOST) += host.o ifneq ($(CONFIG_USB_CDNS3_GADGET),) -cdns3-$(CONFIG_TRACING) += cdns3-trace.o +cdns-y += cdns3-gadget.o cdns3-ep0.o \ + cdnsp-ring.o cdnsp-gadget.o \ + cdnsp-mem.o cdnsp-ep0.o endif +ifneq ($(CONFIG_TRACING),) +ifneq ($(CONFIG_USB_CDNS3_GADGET),) +cdns-y += cdns3-trace.o cdnsp-trace.o +endif +endif + +## +# Platform-specific glue layers (PCI wrappers, SoC integration) +## obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o +obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-pci.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o - -cdnsp-udc-pci-y := cdnsp-pci.o - -ifdef CONFIG_USB_CDNSP_PCI -ifeq ($(CONFIG_USB),m) -obj-m += cdnsp-udc-pci.o -else -obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-udc-pci.o -endif -endif - -cdnsp-udc-pci-$(CONFIG_USB_CDNSP_GADGET) += cdnsp-ring.o cdnsp-gadget.o \ - cdnsp-mem.o cdnsp-ep0.o - -ifneq ($(CONFIG_USB_CDNSP_GADGET),) -cdnsp-udc-pci-$(CONFIG_TRACING) += cdnsp-trace.o -endif diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 8382231af357..90c2aea93cce 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3512,3 +3512,4 @@ int cdns3_gadget_init(struct cdns *cdns) return 0; } +EXPORT_SYMBOL_GPL(cdns3_gadget_init); diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 735df88774e4..3fe3109a3688 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -21,6 +21,7 @@ #include "core.h" #include "gadget-export.h" +#include "host-export.h" #include "drd.h" static int set_phy_power_on(struct cdns *cdns) @@ -44,6 +45,19 @@ static void set_phy_power_off(struct cdns *cdns) phy_power_off(cdns->usb2_phy); } +static int cdns3_plat_gadget_init(struct cdns *cdns) +{ + if (cdns->version < CDNSP_CONTROLLER_V2) + return cdns3_gadget_init(cdns); + else + return cdnsp_gadget_init(cdns); +} + +static int cdns3_plat_host_init(struct cdns *cdns) +{ + return cdns_host_init(cdns); +} + /** * cdns3_plat_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -64,6 +78,8 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev = dev; cdns->pdata = dev_get_platdata(dev); + if (cdns->pdata && cdns->pdata->override_apb_timeout) + cdns->override_apb_timeout = cdns->pdata->override_apb_timeout; platform_set_drvdata(pdev, cdns); @@ -143,12 +159,16 @@ static int cdns3_plat_probe(struct platform_device *pdev) if (ret) goto err_phy_power_on; - cdns->gadget_init = cdns3_gadget_init; - ret = cdns_init(cdns); if (ret) goto err_cdns_init; + cdns->gadget_init = cdns3_plat_gadget_init; + cdns->host_init = cdns3_plat_host_init; + ret = cdns_core_init_role(cdns); + if (ret) + goto err_cdns_init; + device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -336,6 +356,3 @@ static struct platform_driver cdns3_driver = { module_platform_driver(cdns3_driver); MODULE_ALIAS("platform:cdns3"); -MODULE_AUTHOR("Pawel Laszczak "); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver"); diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 2c71c77e6ec3..a5275c2fb43b 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -2092,3 +2092,4 @@ int cdnsp_gadget_init(struct cdns *cdns) return 0; } +EXPORT_SYMBOL_GPL(cdnsp_gadget_init); diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 566d94e49102..432007cfe695 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Cadence PCI Glue driver. + * Cadence USBSSP PCI Glue driver. * * Copyright (C) 2019 Cadence. * @@ -16,7 +16,19 @@ #include #include "core.h" -#include "gadget-export.h" + +struct cdnsp_wrap { + struct platform_device *plat_dev; + struct resource dev_res[6]; + int devfn; +}; + +#define RES_IRQ_HOST_ID 0 +#define RES_IRQ_PERIPHERAL_ID 1 +#define RES_IRQ_OTG_ID 2 +#define RES_HOST_ID 3 +#define RES_DEV_ID 4 +#define RES_DRD_ID 5 #define PCI_BAR_HOST 0 #define PCI_BAR_OTG 0 @@ -26,16 +38,16 @@ #define PCI_DEV_FN_OTG 1 #define PCI_DRIVER_NAME "cdns-pci-usbssp" -#define PLAT_DRIVER_NAME "cdns-usbssp" +#define PLAT_DRIVER_NAME "cdns-usb3" -#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 +#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { /* * Gets the second function. - * Platform has two function. The fist keeps resources for - * Host/Device while the secon keeps resources for DRD/OTG. + * Platform has two function. The first keeps resources for + * Host/Device while the second keeps resources for DRD/OTG. */ if (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL); @@ -48,11 +60,12 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) static int cdnsp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct device *dev = &pdev->dev; - struct pci_dev *func; + struct platform_device_info plat_info; + static struct cdns3_platform_data pdata; + struct cdnsp_wrap *wrap; struct resource *res; - struct cdns *cdnsp; - int ret; + struct pci_dev *func; + int ret = 0; /* * For GADGET/HOST PCI (devfn) function number is 0, @@ -79,146 +92,105 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, } pci_set_master(pdev); + if (pci_is_enabled(func)) { - cdnsp = pci_get_drvdata(func); + wrap = pci_get_drvdata(func); } else { - cdnsp = kzalloc_obj(*cdnsp); - if (!cdnsp) { + wrap = kzalloc_obj(*wrap); + if (!wrap) { ret = -ENOMEM; goto put_pci; } } - /* For GADGET device function number is 0. */ - if (pdev->devfn == 0) { - resource_size_t rsrc_start, rsrc_len; + res = wrap->dev_res; - /* Function 0: host(BAR_0) + device(BAR_1).*/ - dev_dbg(dev, "Initialize resources\n"); - rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV); - rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV); - res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev"); - if (!res) { - dev_dbg(dev, "controller already in use\n"); - ret = -EBUSY; - goto free_cdnsp; - } + if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) { + /* Function 0: host(BAR_0) + device(BAR_2). */ + dev_dbg(&pdev->dev, "Initialize Device resources\n"); + res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV); + res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV); + res[RES_DEV_ID].name = "dev"; + res[RES_DEV_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSSP-DEV physical base addr: %pa\n", + &res[RES_DEV_ID].start); - cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len); - if (!cdnsp->dev_regs) { - dev_dbg(dev, "error mapping memory\n"); - ret = -EFAULT; - goto free_cdnsp; - } + res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].name = "xhci"; + res[RES_HOST_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSSP-XHCI physical base addr: %pa\n", + &res[RES_HOST_ID].start); - cdnsp->dev_irq = pdev->irq; - dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n", - &rsrc_start); + /* Interrupt for XHCI */ + wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_HOST_ID].name = "host"; + wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ; - res = &cdnsp->xhci_res[0]; - res->start = pci_resource_start(pdev, PCI_BAR_HOST); - res->end = pci_resource_end(pdev, PCI_BAR_HOST); - res->name = "xhci"; - res->flags = IORESOURCE_MEM; - dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n", - &res->start); - - /* Interrupt for XHCI, */ - res = &cdnsp->xhci_res[1]; - res->start = pdev->irq; - res->name = "host"; - res->flags = IORESOURCE_IRQ; + /* Interrupt for device. It's the same as for HOST. */ + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral"; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ; } else { - res = &cdnsp->otg_res; - res->start = pci_resource_start(pdev, PCI_BAR_OTG); - res->end = pci_resource_end(pdev, PCI_BAR_OTG); - res->name = "otg"; - res->flags = IORESOURCE_MEM; - dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n", - &res->start); + res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].name = "otg"; + res[RES_DRD_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "CDNSP-DRD physical base addr: %pa\n", + &res[RES_DRD_ID].start); /* Interrupt for OTG/DRD. */ - cdnsp->otg_irq = pdev->irq; + wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_OTG_ID].name = "otg"; + wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ; } - /* - * Cadence PCI based platform require some longer timeout for APB - * to fixes domain clock synchronization issue after resuming - * controller from L1 state. - */ - cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; - pci_set_drvdata(pdev, cdnsp); - if (pci_is_enabled(func)) { - cdnsp->dev = dev; - cdnsp->gadget_init = cdnsp_gadget_init; - - ret = cdns_init(cdnsp); - if (ret) - goto free_cdnsp; + /* set up platform device info */ + pdata.override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; + memset(&plat_info, 0, sizeof(plat_info)); + plat_info.parent = &pdev->dev; + plat_info.fwnode = pdev->dev.fwnode; + plat_info.name = PLAT_DRIVER_NAME; + plat_info.id = pdev->devfn; + plat_info.res = wrap->dev_res; + plat_info.num_res = ARRAY_SIZE(wrap->dev_res); + plat_info.dma_mask = pdev->dma_mask; + plat_info.data = &pdata; + plat_info.size_data = sizeof(pdata); + wrap->devfn = pdev->devfn; + /* register platform device */ + wrap->plat_dev = platform_device_register_full(&plat_info); + if (IS_ERR(wrap->plat_dev)) { + ret = PTR_ERR(wrap->plat_dev); + kfree(wrap); + goto put_pci; + } } - device_wakeup_enable(&pdev->dev); - if (pci_dev_run_wake(pdev)) - pm_runtime_put_noidle(&pdev->dev); - - return 0; - -free_cdnsp: - if (!pci_is_enabled(func)) - kfree(cdnsp); - + pci_set_drvdata(pdev, wrap); put_pci: pci_dev_put(func); - return ret; } static void cdnsp_pci_remove(struct pci_dev *pdev) { - struct cdns *cdnsp; + struct cdnsp_wrap *wrap; struct pci_dev *func; func = cdnsp_get_second_fun(pdev); - cdnsp = (struct cdns *)pci_get_drvdata(pdev); + wrap = pci_get_drvdata(pdev); - if (pci_dev_run_wake(pdev)) - pm_runtime_get_noresume(&pdev->dev); + if (wrap->devfn == pdev->devfn) + platform_device_unregister(wrap->plat_dev); - if (pci_is_enabled(func)) { - cdns_remove(cdnsp); - } else { - kfree(cdnsp); - } + if (!pci_is_enabled(func)) + kfree(wrap); pci_dev_put(func); } -static int __maybe_unused cdnsp_pci_suspend(struct device *dev) -{ - struct cdns *cdns = dev_get_drvdata(dev); - - return cdns_suspend(cdns); -} - -static int __maybe_unused cdnsp_pci_resume(struct device *dev) -{ - struct cdns *cdns = dev_get_drvdata(dev); - unsigned long flags; - int ret; - - spin_lock_irqsave(&cdns->lock, flags); - ret = cdns_resume(cdns); - spin_unlock_irqrestore(&cdns->lock, flags); - cdns_set_active(cdns, 1); - - return ret; -} - -static const struct dev_pm_ops cdnsp_pci_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume) -}; - static const struct pci_device_id cdnsp_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), .class = PCI_CLASS_SERIAL_USB_DEVICE }, @@ -230,13 +202,10 @@ static const struct pci_device_id cdnsp_pci_ids[] = { }; static struct pci_driver cdnsp_pci_driver = { - .name = "cdnsp-pci", + .name = PCI_DRIVER_NAME, .id_table = cdnsp_pci_ids, .probe = cdnsp_pci_probe, .remove = cdnsp_pci_remove, - .driver = { - .pm = &cdnsp_pci_pm_ops, - } }; module_pci_driver(cdnsp_pci_driver); @@ -245,4 +214,4 @@ MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids); MODULE_ALIAS("pci:cdnsp"); MODULE_AUTHOR("Pawel Laszczak "); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Cadence CDNSP PCI driver"); +MODULE_DESCRIPTION("Cadence CDNSP PCI wrapper"); diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index f0e32227c0b7..6a8d1fefbc0d 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -21,7 +21,6 @@ #include #include "core.h" -#include "host-export.h" #include "drd.h" static int cdns_idle_init(struct cdns *cdns); @@ -80,7 +79,7 @@ static void cdns_exit_roles(struct cdns *cdns) * * Returns 0 on success otherwise negative errno */ -static int cdns_core_init_role(struct cdns *cdns) +int cdns_core_init_role(struct cdns *cdns) { struct device *dev = cdns->dev; enum usb_dr_mode best_dr_mode; @@ -96,23 +95,13 @@ static int cdns_core_init_role(struct cdns *cdns) * can be restricted later depending on strap pin configuration. */ if (dr_mode == USB_DR_MODE_UNKNOWN) { - if (cdns->version == CDNSP_CONTROLLER_V2) { - if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) && - IS_ENABLED(CONFIG_USB_CDNSP_GADGET)) - dr_mode = USB_DR_MODE_OTG; - else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST)) - dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET)) - dr_mode = USB_DR_MODE_PERIPHERAL; - } else { - if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && - IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) - dr_mode = USB_DR_MODE_OTG; - else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) - dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) - dr_mode = USB_DR_MODE_PERIPHERAL; - } + if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && + IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_OTG; + else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) + dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_PERIPHERAL; } /* @@ -137,11 +126,8 @@ static int cdns_core_init_role(struct cdns *cdns) dr_mode = best_dr_mode; if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { - if ((cdns->version == CDNSP_CONTROLLER_V2 && - IS_ENABLED(CONFIG_USB_CDNSP_HOST)) || - (cdns->version < CDNSP_CONTROLLER_V2 && - IS_ENABLED(CONFIG_USB_CDNS3_HOST))) - ret = cdns_host_init(cdns); + if (cdns->host_init) + ret = cdns->host_init(cdns); else ret = -ENXIO; @@ -197,11 +183,14 @@ static int cdns_core_init_role(struct cdns *cdns) goto err; } + dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); + return 0; err: cdns_exit_roles(cdns); return ret; } +EXPORT_SYMBOL_GPL(cdns_core_init_role); /** * cdns_hw_role_state_machine - role switch state machine based on hw events. @@ -469,14 +458,8 @@ int cdns_init(struct cdns *cdns) if (ret) goto init_failed; - ret = cdns_core_init_role(cdns); - if (ret) - goto init_failed; - spin_lock_init(&cdns->lock); - dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); - return 0; init_failed: cdns_drd_exit(cdns); @@ -576,5 +559,5 @@ EXPORT_SYMBOL_GPL(cdns_set_active); MODULE_AUTHOR("Peter Chen "); MODULE_AUTHOR("Pawel Laszczak "); MODULE_AUTHOR("Roger Quadros "); -MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver"); +MODULE_DESCRIPTION("Cadence USBSS/USBSSP DRD driver (core, DRD, platform, optional host/gadget)"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 801be9e61340..bca973b999a4 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -45,6 +45,7 @@ struct cdns3_platform_data { unsigned long quirks; #define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) #define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1) + u32 override_apb_timeout; /* 0 = use default (e.g. for PCI) */ }; /** @@ -82,6 +83,7 @@ struct cdns3_platform_data { * @override_apb_timeout: hold value of APB timeout. For value 0 the default * value in CHICKEN_BITS_3 will be preserved. * @gadget_init: pointer to gadget initialization function + * @host_init: pointer to host initialization function */ struct cdns { struct device *dev; @@ -120,13 +122,14 @@ struct cdns { spinlock_t lock; struct xhci_plat_priv *xhci_plat_data; u32 override_apb_timeout; - int (*gadget_init)(struct cdns *cdns); + int (*host_init)(struct cdns *cdns); }; int cdns_hw_role_switch(struct cdns *cdns); int cdns_init(struct cdns *cdns); int cdns_remove(struct cdns *cdns); +int cdns_core_init_role(struct cdns *cdns); #ifdef CONFIG_PM_SLEEP int cdns_resume(struct cdns *cdns); diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h index c37b6269b001..60c3177db62c 100644 --- a/drivers/usb/cdns3/gadget-export.h +++ b/drivers/usb/cdns3/gadget-export.h @@ -10,9 +10,10 @@ #ifndef __LINUX_CDNS3_GADGET_EXPORT #define __LINUX_CDNS3_GADGET_EXPORT -#if IS_ENABLED(CONFIG_USB_CDNSP_GADGET) +#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET) int cdnsp_gadget_init(struct cdns *cdns); +int cdns3_gadget_init(struct cdns *cdns); #else static inline int cdnsp_gadget_init(struct cdns *cdns) @@ -20,13 +21,6 @@ static inline int cdnsp_gadget_init(struct cdns *cdns) return -ENXIO; } -#endif /* CONFIG_USB_CDNSP_GADGET */ - -#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET) - -int cdns3_gadget_init(struct cdns *cdns); -#else - static inline int cdns3_gadget_init(struct cdns *cdns) { return -ENXIO; diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h index cf92173ecf00..34fd1f1ad59d 100644 --- a/drivers/usb/cdns3/host-export.h +++ b/drivers/usb/cdns3/host-export.h @@ -9,7 +9,7 @@ #ifndef __LINUX_CDNS3_HOST_EXPORT #define __LINUX_CDNS3_HOST_EXPORT -#if IS_ENABLED(CONFIG_USB_CDNS_HOST) +#if IS_ENABLED(CONFIG_USB_CDNS3_HOST) int cdns_host_init(struct cdns *cdns); @@ -22,6 +22,6 @@ static inline int cdns_host_init(struct cdns *cdns) static inline void cdns_host_exit(struct cdns *cdns) { } -#endif /* USB_CDNS_HOST */ +#endif /* CONFIG_USB_CDNS3_HOST */ #endif /* __LINUX_CDNS3_HOST_EXPORT */ From 398ee113f15c1e8e62535e54f22fb4db340c7835 Mon Sep 17 00:00:00 2001 From: Kamlesh Kumar Date: Fri, 24 Apr 2026 17:09:46 +0530 Subject: [PATCH 0195/5214] ima: Fix sigv3 signature handling for EVM_IMA_XATTR_DIGSIG ima_get_hash_algo() only recognizes version 2 signatures when the xattr type is EVM_IMA_XATTR_DIGSIG. Since sigv3 signatures also use EVM_IMA_XATTR_DIGSIG as the xattr type, version 3 must be accepted as well to correctly determine the hash algorithm. Additionally, ima_validate_rule() does not include IMA_SIGV3_REQUIRED in the allowed flags bitmask for MODULE_CHECK, KEXEC_KERNEL_CHECK, and KEXEC_INITRAMFS_CHECK hook functions. As a result, policy rules with "appraise_type=sigv3" are rejected for these functions. Add version 3 to the accepted versions in ima_get_hash_algo() for EVM_IMA_XATTR_DIGSIG, and add IMA_SIGV3_REQUIRED to the allowed flags for MODULE_CHECK, KEXEC_KERNEL_CHECK, and KEXEC_INITRAMFS_CHECK in ima_validate_rule(). Signed-off-by: Kamlesh Kumar Tested-by: Stefan Berger Fixes: de4c44a7f559 ("ima: add support to require IMA sigv3 signatures") Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 5 +++-- security/integrity/ima/ima_policy.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index de963b9f3634..2dd231567710 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -195,8 +195,9 @@ enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value, return sig->hash_algo; case EVM_IMA_XATTR_DIGSIG: sig = (typeof(sig))xattr_value; - if (sig->version != 2 || xattr_len <= sizeof(*sig) - || sig->hash_algo >= HASH_ALGO__LAST) + if ((sig->version != 2 && sig->version != 3) || + xattr_len <= sizeof(*sig) || + sig->hash_algo >= HASH_ALGO__LAST) return ima_hash_algo; return sig->hash_algo; case IMA_XATTR_DIGEST_NG: diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f7f940a76922..b1c010e8eb13 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1313,7 +1313,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | - IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) + IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS | + IMA_SIGV3_REQUIRED)) return false; break; From 69fc6474236d9edda6983623e4282f2bdfd8e3d8 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Wed, 22 Apr 2026 07:34:51 -0400 Subject: [PATCH 0196/5214] ima: return error early if file xattr cannot be changed During early boot, the filesystem is read-only and any changes to xattrs are not allowed. This fails in case of ext4 because changing xattr starts an ext4 transaction which fails with the following warning. WARNING: fs/ext4/ext4_jbd2.c:75 at ext4_journal_check_start+0x63/0xa0 [ext4], CPU#1: systemd-sysroot/561 CPU: 1 UID: 0 PID: 561 Comm: systemd-sysroot Not tainted 6.19.12-1-default #1 PREEMPT(voluntary) openSUSE Tumbleweed c2dfc3c9d9f6f1233251c5d4410574fe82a348ee Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS unknown 02/02/2022 RIP: 0010:ext4_journal_check_start+0x63/0xa0 [ext4] Call Trace: __ext4_journal_start_sb+0x3e/0x180 [ext4 6d025f3bc52c89a957b89a89d211fadf5e9434e1] ext4_xattr_set+0x104/0x150 [ext4 6d025f3bc52c89a957b89a89d211fadf5e9434e1] __vfs_setxattr+0x9a/0xd0 __vfs_setxattr_noperm+0x76/0x1f0 ima_appraise_measurement+0x23e/0xe40 ima_d_path+0x5a/0xd0 process_measurement+0xb29/0xc40 ? copy_from_kernel_nofault+0x21/0xe0 ? fscrypt_file_open+0xc0/0xe0 ? ext4_file_open+0x60/0x490 [ext4 6d025f3bc52c89a957b89a89d211fadf5e9434e1] ? bpf_prog_31efb7c56239148b_restrict_filesystems+0xab/0x126 ? __bpf_prog_exit+0x23/0xd0 ? __bpf_tramp_exit+0xd/0x50 ? bpf_trampoline_6442530367+0x9f/0xea ima_file_check+0x57/0x80 security_file_post_open+0x50/0xf0 path_openat+0x493/0x1650 do_filp_open+0xc7/0x170 Detect the state of the file early and return the error. Signed-off-by: Goldwyn Rodrigues Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2dd231567710..18d0d9154317 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -90,6 +90,11 @@ static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint) int rc, offset; u8 algo = iint->ima_hash->algo; + if (IS_RDONLY(d_inode(dentry))) + return -EROFS; + if (IS_IMMUTABLE(d_inode(dentry))) + return -EPERM; + if (algo <= HASH_ALGO_SHA1) { offset = 1; iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST; From 56c2ca0ae7cb9254c4c2b82baa0afe29feaa274e Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Sun, 19 Apr 2026 13:43:06 +0200 Subject: [PATCH 0197/5214] dt-bindings: clock: rockchip,rk3588-cru: add I2S MCLK output to IO clock IDs Add clock identifiers for the four I2S MCLK output to IO gate clocks on RK3588, needed by board DTS files where the codec requires MCLK from the SoC on an external IO pin. Acked-by: Krzysztof Kozlowski Signed-off-by: Daniele Briguglio Tested-by: Ricardo Pardini Link: https://patch.msgid.link/20260419-rk3588-mclk-gate-grf-v4-1-513a42dd1dcc@superkali.me Signed-off-by: Heiko Stuebner --- include/dt-bindings/clock/rockchip,rk3588-cru.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h index 0c7d3ca2d5bc..7528034cff56 100644 --- a/include/dt-bindings/clock/rockchip,rk3588-cru.h +++ b/include/dt-bindings/clock/rockchip,rk3588-cru.h @@ -734,6 +734,10 @@ #define PCLK_AV1_PRE 719 #define HCLK_SDIO_PRE 720 #define PCLK_VO1GRF 721 +#define I2S0_8CH_MCLKOUT_TO_IO 722 +#define I2S1_8CH_MCLKOUT_TO_IO 723 +#define I2S2_2CH_MCLKOUT_TO_IO 724 +#define I2S3_2CH_MCLKOUT_TO_IO 725 /* scmi-clocks indices */ From 28820fc7983b9c8e160c0095067a570bdfcae1f0 Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Sun, 19 Apr 2026 13:43:07 +0200 Subject: [PATCH 0198/5214] clk: rockchip: allow grf_type_sys lookup in aux_grf_table Remove the grf_type_sys exclusion from the auxiliary GRF table lookup in rockchip_clk_register_branches(). Previously, branches with grf_type_sys always used ctx->grf directly, bypassing the aux_grf_table. This is a problem on SoCs like RK3588 where ctx->grf points to the PHP_GRF (set via the CRU's rockchip,grf phandle), but GATE_GRF clock entries need to access the SYS_GRF instead. With this change, grf_type_sys branches first check the aux_grf_table, and fall back to ctx->grf if no entry is found. This is backwards compatible: on SoCs that do not register grf_type_sys in the aux_grf_table, the behavior is unchanged. Reviewed-by: Nicolas Frattaroli Signed-off-by: Daniele Briguglio Tested-by: Ricardo Pardini Link: https://patch.msgid.link/20260419-rk3588-mclk-gate-grf-v4-2-513a42dd1dcc@superkali.me Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index e8b3b0b9a4f8..911e6b610618 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -509,10 +509,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, clk = NULL; /* for GRF-dependent branches, choose the right grf first */ - if ((list->branch_type == branch_grf_mux || - list->branch_type == branch_grf_gate || - list->branch_type == branch_grf_mmc) && - list->grf_type != grf_type_sys) { + if (list->branch_type == branch_grf_mux || + list->branch_type == branch_grf_gate || + list->branch_type == branch_grf_mmc) { hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) { if (agrf->type == list->grf_type) { grf = agrf->grf; From 32d1d88c4165d0da31d3bfda912e80e8110d6fc1 Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Sun, 19 Apr 2026 13:43:08 +0200 Subject: [PATCH 0199/5214] clk: rockchip: add helper to register auxiliary GRFs Add rockchip_clk_add_grf() as a helper to register an auxiliary GRF into the clock provider's aux_grf_table. This encapsulates the struct rockchip_aux_grf allocation and hashtable insertion, so SoC clock drivers do not need to open-code it. Suggested-by: Heiko Stuebner Signed-off-by: Daniele Briguglio Link: https://patch.msgid.link/20260419-rk3588-mclk-gate-grf-v4-3-513a42dd1dcc@superkali.me Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk.c | 18 ++++++++++++++++++ drivers/clk/rockchip/clk.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 911e6b610618..ee8c79b938d3 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -429,6 +429,24 @@ void rockchip_clk_of_add_provider(struct device_node *np, } EXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider); +int rockchip_clk_add_grf(struct rockchip_clk_provider *ctx, + struct regmap *grf, + enum rockchip_grf_type type) +{ + struct rockchip_aux_grf *aux_grf; + + aux_grf = kzalloc_obj(*aux_grf); + if (!aux_grf) + return -ENOMEM; + + aux_grf->grf = grf; + aux_grf->type = type; + hash_add(ctx->aux_grf_table, &aux_grf->node, type); + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_clk_add_grf); + void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, struct rockchip_pll_clock *list, unsigned int nr_pll, int grf_lock_offset) diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index cf0f5f11c34b..9e3503e2ffc2 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -1329,6 +1329,9 @@ struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np, void rockchip_clk_finalize(struct rockchip_clk_provider *ctx); void rockchip_clk_of_add_provider(struct device_node *np, struct rockchip_clk_provider *ctx); +int rockchip_clk_add_grf(struct rockchip_clk_provider *ctx, + struct regmap *grf, + enum rockchip_grf_type type); unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list, unsigned int nr_clk); void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, From 06c990bffdbea7cf655e728f4423ecd13fb030f6 Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Sun, 19 Apr 2026 13:43:09 +0200 Subject: [PATCH 0200/5214] soc: rockchip: rk3588: add SYS_GRF SOC_CON6 register offset Add the RK3588_SYSGRF_SOC_CON6 register offset to the RK3588 GRF header. This register contains the I2S MCLK output to IO gate bits, needed by the clock driver. Signed-off-by: Daniele Briguglio Reviewed-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260419-rk3588-mclk-gate-grf-v4-4-513a42dd1dcc@superkali.me Signed-off-by: Heiko Stuebner --- include/soc/rockchip/rk3588_grf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/soc/rockchip/rk3588_grf.h b/include/soc/rockchip/rk3588_grf.h index 02a7b2432d99..db0092fc66ad 100644 --- a/include/soc/rockchip/rk3588_grf.h +++ b/include/soc/rockchip/rk3588_grf.h @@ -19,4 +19,6 @@ /* Whether the LPDDR5 is in 2:1 (= 0) or 4:1 (= 1) CKR a.k.a. DQS mode */ #define RK3588_PMUGRF_OS_REG6_LP5_CKR BIT(0) +#define RK3588_SYSGRF_SOC_CON6 0x0318 + #endif /* __SOC_RK3588_GRF_H */ From 02b9b0bb626989b947d82bbe4e050f0254e2046d Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Sun, 19 Apr 2026 13:43:10 +0200 Subject: [PATCH 0201/5214] clk: rockchip: rk3588: add GATE_GRF clocks for I2S MCLK output to IO The I2S MCLK outputs on RK3588 are gated by bits in the SYS_GRF register SOC_CON6 (offset 0x318). These gates control whether the internal CRU MCLK signals reach the external IO pins connected to audio codecs. The kernel should explicitly manage these gates so that audio functionality does not depend on bootloader register state. This is analogous to what was done for RK3576 SAI MCLK outputs [1]. Register the SYS_GRF as an auxiliary GRF with grf_type_sys using rockchip_clk_add_grf(), and add GATE_GRF entries for all four I2S MCLK output gates: - I2S0_8CH_MCLKOUT_TO_IO (bit 0) - I2S1_8CH_MCLKOUT_TO_IO (bit 1) - I2S2_2CH_MCLKOUT_TO_IO (bit 2) - I2S3_2CH_MCLKOUT_TO_IO (bit 7) Board DTS files that need MCLK on an IO pin can reference these clocks, e.g.: clocks = <&cru I2S0_8CH_MCLKOUT_TO_IO>; Tested on the Youyeetoo YY3588 (RK3588) with an ES8388 codec on I2S0. [1] https://lore.kernel.org/r/20250305-rk3576-sai-v1-2-64e6cf863e9a@collabora.com/ Tested-by: Ricardo Pardini Signed-off-by: Daniele Briguglio Link: https://patch.msgid.link/20260419-rk3588-mclk-gate-grf-v4-5-513a42dd1dcc@superkali.me Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3588.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 1694223f4f84..2ba9976654cf 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -5,11 +5,13 @@ */ #include +#include #include #include #include #include #include +#include #include "clk.h" #define RK3588_GRF_SOC_STATUS0 0x600 @@ -892,6 +894,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(8), 0, GFLAGS), MUX(I2S2_2CH_MCLKOUT, "i2s2_2ch_mclkout", i2s2_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(30), 2, 1, MFLAGS), + GATE_GRF(I2S2_2CH_MCLKOUT_TO_IO, "i2s2_2ch_mclkout_to_io", "i2s2_2ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), COMPOSITE(CLK_I2S3_2CH_SRC, "clk_i2s3_2ch_src", gpll_aupll_p, 0, RK3588_CLKSEL_CON(30), 8, 1, MFLAGS, 3, 5, DFLAGS, @@ -907,6 +911,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(8), 4, GFLAGS), MUX(I2S3_2CH_MCLKOUT, "i2s3_2ch_mclkout", i2s3_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(32), 2, 1, MFLAGS), + GATE_GRF(I2S3_2CH_MCLKOUT_TO_IO, "i2s3_2ch_mclkout_to_io", "i2s3_2ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), GATE(PCLK_ACDCDIG, "pclk_acdcdig", "pclk_audio_root", 0, RK3588_CLKGATE_CON(7), 11, GFLAGS), GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio_root", 0, @@ -935,6 +941,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(7), 10, GFLAGS), MUX(I2S0_8CH_MCLKOUT, "i2s0_8ch_mclkout", i2s0_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(28), 2, 2, MFLAGS), + GATE_GRF(I2S0_8CH_MCLKOUT_TO_IO, "i2s0_8ch_mclkout_to_io", "i2s0_8ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), GATE(HCLK_PDM1, "hclk_pdm1", "hclk_audio_root", 0, RK3588_CLKGATE_CON(9), 6, GFLAGS), @@ -2220,6 +2228,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_PMU_CLKGATE_CON(2), 13, GFLAGS), MUX(I2S1_8CH_MCLKOUT, "i2s1_8ch_mclkout", i2s1_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_PMU_CLKSEL_CON(9), 2, 2, MFLAGS), + GATE_GRF(I2S1_8CH_MCLKOUT_TO_IO, "i2s1_8ch_mclkout_to_io", "i2s1_8ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), GATE(PCLK_PMU1, "pclk_pmu1", "pclk_pmu0_root", CLK_IS_CRITICAL, RK3588_PMU_CLKGATE_CON(1), 0, GFLAGS), GATE(CLK_DDR_FAIL_SAFE, "clk_ddr_fail_safe", "clk_pmu0", CLK_IGNORE_UNUSED, @@ -2439,6 +2449,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] = { static void __init rk3588_clk_early_init(struct device_node *np) { struct rockchip_clk_provider *ctx; + struct regmap *sys_grf; unsigned long clk_nr_clks, max_clk_id1, max_clk_id2; void __iomem *reg_base; @@ -2479,6 +2490,11 @@ static void __init rk3588_clk_early_init(struct device_node *np) &rk3588_cpub1clk_data, rk3588_cpub1clk_rates, ARRAY_SIZE(rk3588_cpub1clk_rates)); + /* Register SYS_GRF for I2S MCLK output to IO gate clocks */ + sys_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3588-sys-grf"); + if (!IS_ERR(sys_grf)) + rockchip_clk_add_grf(ctx, sys_grf, grf_type_sys); + rockchip_clk_register_branches(ctx, rk3588_early_clk_branches, ARRAY_SIZE(rk3588_early_clk_branches)); From 01f9557233e3c04be5c6705864d390682f5d7236 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:01:05 +0200 Subject: [PATCH 0202/5214] mtd: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 7408f34f0c68..d488213b631f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -304,7 +304,7 @@ config MTD_NAND_HISI504 Enables support for NAND controller on Hisilicon SoC Hip04. config MTD_NAND_QCOM - tristate "QCOM NAND controller" + tristate "Qualcomm NAND controller" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM help From 6b07cdff176bc5f8fef459b85a7e2ea09e68543f Mon Sep 17 00:00:00 2001 From: Haoyu Lu Date: Fri, 10 Apr 2026 21:37:34 +0800 Subject: [PATCH 0203/5214] mtd: mtdoops: replace simple_strtoul with kstrtouint Replace deprecated simple_strtoul with kstrtouint for better error handling and type safety. The kstrtouint function provides stricter validation, automatically rejecting inputs like "123abc" that simple_strtoul would partially accept. Using kstrtouint avoids unsigned long to int conversion and is more appropriate for MTD device indices which are non-negative integers. Signed-off-by: Haoyu Lu Signed-off-by: Miquel Raynal --- drivers/mtd/mtdoops.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index b88083751a0c..39df7ce8f55f 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -403,8 +403,7 @@ static struct mtd_notifier mtdoops_notifier = { static int __init mtdoops_init(void) { struct mtdoops_context *cxt = &oops_cxt; - int mtd_index; - char *endp; + unsigned int mtd_index; if (strlen(mtddev) == 0) { pr_err("mtd device (mtddev=name/number) must be supplied\n"); @@ -421,9 +420,9 @@ static int __init mtdoops_init(void) /* Setup the MTD device to use */ cxt->mtd_index = -1; - mtd_index = simple_strtoul(mtddev, &endp, 0); - if (*endp == '\0') + if (kstrtouint(mtddev, 0, &mtd_index) == 0) { cxt->mtd_index = mtd_index; + } cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) From a92a9a49288d1aad67bd12457c80a1e3f463d85b Mon Sep 17 00:00:00 2001 From: Haoyu Lu Date: Fri, 10 Apr 2026 21:37:35 +0800 Subject: [PATCH 0204/5214] mtd: mtdsuper: replace simple_strtoul with kstrtouint Modernize string-to-number conversion in mtdsuper.c by replacing simple_strtoul with kstrtouint. This change provides proper type safety for MTD device numbers which are non-negative integers. Using kstrtouint avoids unsigned long to int conversion and is more appropriate for device indices. The debug output format specifier is updated to %u for unsigned int. Signed-off-by: Haoyu Lu Signed-off-by: Miquel Raynal --- drivers/mtd/mtdsuper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index b7e3763c47f0..c709ff7aa6b5 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -132,12 +132,12 @@ int get_tree_mtd(struct fs_context *fc, } else if (isdigit(fc->source[3])) { /* mount by MTD device number name */ - char *endptr; + unsigned int mtdnr_val; - mtdnr = simple_strtoul(fc->source + 3, &endptr, 0); - if (!*endptr) { + if (kstrtouint(fc->source + 3, 0, &mtdnr_val) == 0) { + mtdnr = mtdnr_val; /* It was a valid number */ - pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr); + pr_debug("MTDSB: mtd%%d, mtdnr %u\n", mtdnr_val); return mtd_get_sb_by_nr(fc, mtdnr, fill_super); } } From d462c8e89e84bfb6417e6b4c88e0cb7cc747ba41 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 17 Apr 2026 10:51:46 +0200 Subject: [PATCH 0205/5214] PCI: Stop setting cached power state to 'unknown' on unbind When a PCI device is unbound from its driver, pci_device_remove() sets the cached power state in pci_dev->current_state to PCI_UNKNOWN. This was introduced by commit 2449e06a5696 ("PCI: reset pci device state to unknown state for resume") to invalidate the cached power state in case the system is subsequently put to sleep. For bound devices, the cached power state is set to PCI_UNKNOWN in pci_pm_suspend_noirq(), immediately before entering system sleep. Extend to unbound devices for consistency. This obviates the need to change the cached power state on unbind, so stop doing so. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/af7d11d3ceb231acc90829f7a5c8400c2446744f.1776415510.git.lukas@wunner.de --- drivers/pci/pci-driver.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d10ece0889f0..2bfefd8db526 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -512,13 +512,6 @@ static void pci_device_remove(struct device *dev) /* Undo the runtime PM settings in local_pci_probe() */ pm_runtime_put_sync(dev); - /* - * If the device is still on, set the power state as "unknown", - * since it might change by the next time we load the driver. - */ - if (pci_dev->current_state == PCI_D0) - pci_dev->current_state = PCI_UNKNOWN; - /* * We would love to complain here if pci_dev->is_enabled is set, that * the driver should have called pci_disable_device(), but the @@ -893,7 +886,7 @@ static int pci_pm_suspend_noirq(struct device *dev) if (!pm) { pci_save_state(pci_dev); - goto Fixup; + goto set_unknown; } if (pm->suspend_noirq) { @@ -945,6 +938,7 @@ static int pci_pm_suspend_noirq(struct device *dev) goto Fixup; } +set_unknown: pci_pm_set_unknown_state(pci_dev); /* From ee7471fe968d210939be9046089a924cd23c8c3b Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Fri, 17 Apr 2026 15:24:36 +0200 Subject: [PATCH 0206/5214] PCI: Skip Resizable BAR restore on read error pci_restore_rebar_state() uses the Resizable BAR Control register to decide how many BARs to restore (nbars) and which BAR each iteration addresses (bar_idx). When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7, past the spec's valid ranges for both fields. pci_resource_n() then returns an unrelated resource slot, whose size is used to derive a nonsensical value written back to the Resizable BAR Control register. Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it --- drivers/pci/rebar.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index 39f8cf3b70d5..11965947c4cb 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -231,6 +231,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) return; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { @@ -238,6 +241,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) int bar_idx, size; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); From f34f1712229d71ce4286440fef12526fd4590b37 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Fri, 17 Apr 2026 15:24:37 +0200 Subject: [PATCH 0207/5214] PCI/IOV: Skip VF Resizable BAR restore on read error sriov_restore_vf_rebar_state() uses the VF Resizable BAR Control register to decide how many VF BARs to restore (nbars) and which VF BAR each iteration addresses (bar_idx). bar_idx indexes into dev->sriov->barsz[], which has only PCI_SRIOV_NUM_BARS (6) entries. When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7. The barsz[] access then goes out of bounds. UBSAN reports this as: UBSAN: array-index-out-of-bounds in drivers/pci/iov.c:948:51 index 7 is out of range for type 'resource_size_t [6]' Observed on an NVIDIA RTX PRO 1000 GPU (GB207GLM) that stopped responding during a failed GC6 power state exit. The subsequent pci_restore_state() invoked sriov_restore_vf_rebar_state() while config reads returned 0xffffffff, triggering the splat. Bail out if any VF Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further VF BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. This mirrors the guard in pci_restore_rebar_state(). Fixes: 5a8f77e24a30 ("PCI/IOV: Restore VF resizable BAR state after reset") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/44a4ae53ec2825816b816c85cd378430d9a95cc6.1776429882.git.mnencia@kcore.it --- drivers/pci/iov.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 91ac4e37ecb9..08df9bace13d 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev) return; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { int bar_idx, size; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl); size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]); ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE; From 73ae5392b0cfcce02efa23ee7123d8764ef070a8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 21 Apr 2026 16:11:01 +0530 Subject: [PATCH 0208/5214] PCI/pwrctrl: Move pci_pwrctrl_is_required() earlier in file Move pci_pwrctrl_is_required() earlier in the file so it can be used by pci_pwrctrl_power_off_device() and pci_pwrctrl_power_on_device(). Signed-off-by: Manivannan Sadhasivam [bhelgaas: split to its own patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260421104102.12322-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 84 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 97cff5b8ca88..a692aeaee81a 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -139,6 +139,48 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +/* + * Check whether the pwrctrl device really needs to be created or not. The + * pwrctrl device will only be created if the node satisfies below requirements: + * + * 1. Presence of compatible property with "pci" prefix to match against the + * pwrctrl driver (AND) + * 2. At least one of the power supplies defined in the devicetree node of the + * device (OR) in the remote endpoint parent node to indicate pwrctrl + * requirement. + */ +static bool pci_pwrctrl_is_required(struct device_node *np) +{ + struct device_node *endpoint; + const char *compat; + int ret; + + ret = of_property_read_string(np, "compatible", &compat); + if (ret < 0) + return false; + + if (!strstarts(compat, "pci")) + return false; + + if (of_pci_supply_present(np)) + return true; + + if (of_graph_is_present(np)) { + for_each_endpoint_of_node(np, endpoint) { + struct device_node *remote __free(device_node) = + of_graph_get_remote_port_parent(endpoint); + if (remote) { + if (of_pci_supply_present(remote)) { + of_node_put(endpoint); + return true; + } + } + } + } + + return false; +} + static int __pci_pwrctrl_power_off_device(struct device *dev) { struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); @@ -268,48 +310,6 @@ int pci_pwrctrl_power_on_devices(struct device *parent) } EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); -/* - * Check whether the pwrctrl device really needs to be created or not. The - * pwrctrl device will only be created if the node satisfies below requirements: - * - * 1. Presence of compatible property with "pci" prefix to match against the - * pwrctrl driver (AND) - * 2. At least one of the power supplies defined in the devicetree node of the - * device (OR) in the remote endpoint parent node to indicate pwrctrl - * requirement. - */ -static bool pci_pwrctrl_is_required(struct device_node *np) -{ - struct device_node *endpoint; - const char *compat; - int ret; - - ret = of_property_read_string(np, "compatible", &compat); - if (ret < 0) - return false; - - if (!strstarts(compat, "pci")) - return false; - - if (of_pci_supply_present(np)) - return true; - - if (of_graph_is_present(np)) { - for_each_endpoint_of_node(np, endpoint) { - struct device_node *remote __free(device_node) = - of_graph_get_remote_port_parent(endpoint); - if (remote) { - if (of_pci_supply_present(remote)) { - of_node_put(endpoint); - return true; - } - } - } - } - - return false; -} - static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent) { From 95c4920701dfca6a8cf4986112898382fa7afc0f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 21 Apr 2026 16:11:01 +0530 Subject: [PATCH 0209/5214] PCI/pwrctrl: Do not try to power on/off devices that don't need pwrctrl pci_pwrctrl_is_required() detects whether a device needs PCI pwrctrl support. It is currently used in pci_pwrctrl_create_device(), but not in pci_pwrctrl_power_{on/off}_device() APIs. This leads to pwrctrl core trying to power on/off incompatible devices like USB hub downstream ports defined in DT. Add this check to prevent pwrctrl core from poking at wrong devices. Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Reported-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam [bhelgaas: split pci_pwrctrl_is_required() move to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260421104102.12322-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index a692aeaee81a..b5a0a14d316e 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -199,6 +199,9 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) for_each_available_child_of_node_scoped(np, child) pci_pwrctrl_power_off_device(child); + if (!pci_pwrctrl_is_required(np)) + return; + pdev = of_find_device_by_node(np); if (!pdev) return; @@ -255,6 +258,9 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) return ret; } + if (!pci_pwrctrl_is_required(np)) + return 0; + pdev = of_find_device_by_node(np); if (!pdev) return 0; From 951ebc18d181af1d90deddbc0ef3269061d6e03c Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Thu, 23 Apr 2026 10:30:51 -0700 Subject: [PATCH 0210/5214] PCI/P2PDMA: Avoid returning a provider for non_mappable_bars Extend the checks in pcim_p2pdma_init() and pcim_p2pdma_provider() to exclude functions that have pdev->non_mappable_bars set. Consumers such as VFIO were previously able to map these for access by the CPU or P2P. Update the comment on non_mappable_bars to show it refers to any access, not just userspace CPU access. Fixes: 372d6d1b8ae3c ("PCI/P2PDMA: Refactor to separate core P2P functionality from memory allocation") Suggested-by: Alex Williamson Signed-off-by: Matt Evans Signed-off-by: Bjorn Helgaas Reviewed-by: Niklas Schnelle Reviewed-by: Alex Williamson Link: https://patch.msgid.link/20260423173051.1999679-1-mattev@meta.com --- drivers/pci/p2pdma.c | 6 +++++- include/linux/pci.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 7c898542af8d..adb17a4f6939 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -262,6 +262,9 @@ int pcim_p2pdma_init(struct pci_dev *pdev) struct pci_p2pdma *p2p; int i, ret; + if (pdev->non_mappable_bars) + return -EOPNOTSUPP; + p2p = rcu_dereference_protected(pdev->p2pdma, 1); if (p2p) return 0; @@ -318,7 +321,8 @@ struct p2pdma_provider *pcim_p2pdma_provider(struct pci_dev *pdev, int bar) { struct pci_p2pdma *p2p; - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM) || + pdev->non_mappable_bars) return NULL; p2p = rcu_dereference_protected(pdev->p2pdma, 1); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..1e6802017d6b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -508,7 +508,7 @@ struct pci_dev { unsigned int no_command_memory:1; /* No PCI_COMMAND_MEMORY */ unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */ unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ - unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ + unsigned int non_mappable_bars:1; /* BARs can't be mapped by CPU or peers */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ From bb115505a9f1f79b888aa0ccb28f42f05edea07f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 26 Mar 2026 17:13:11 -0500 Subject: [PATCH 0211/5214] PCI: Remove MPS/MRRS Kconfig settings (CONFIG_PCIE_BUS_*) Revert b0e85c3c8554 ("PCI: Add Kconfig options for MPS/MRRS strategy"), which allowed build-time selection of the "off", "default", "safe", "performance", or "peer2peer" strategies for MPS and MRRS configuration. These strategies can be selected at boot-time using the "pci=pcie_bus_tune_*" kernel parameters. Per the discussion mentioned below, these Kconfig options were added to work around a hardware defect in a WiFi device used in a cable modem. The defect occurred only when the device was configured with MPS=128, and Kconfig was a way to avoid that setting. It was easier for the modem vendor to use Kconfig and update the kernel image than to change the kernel parameters. Neither Kconfig nor kernel parameters are a complete solution because the broken WiFi device may be used in other systems where it may be configured with MPS=128 and be susceptible to the defect. Remove the Kconfig settings to simplify the MPS code. If we can identify the WiFi device in question, we may be able to make a generic quirk to avoid the problem on all system. This is not a fix and should not be backported to previous kernels. Link: https://lore.kernel.org/all/CA+-6iNzd0RJO0L021qz8CKrSviSst6QehY-QtJxz_-EVY0Hj0Q@mail.gmail.com Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260326221311.1356180-1-bhelgaas@google.com --- drivers/pci/Kconfig | 57 --------------------------------------------- drivers/pci/pci.c | 10 -------- 2 files changed, 67 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 33c88432b728..0c7408509ba2 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -251,63 +251,6 @@ config PCI_DYNAMIC_OF_NODES Once this option is selected, the device tree nodes will be generated for all PCI bridges. -choice - prompt "PCI Express hierarchy optimization setting" - default PCIE_BUS_DEFAULT - depends on EXPERT - help - MPS (Max Payload Size) and MRRS (Max Read Request Size) are PCIe - device parameters that affect performance and the ability to - support hotplug and peer-to-peer DMA. - - The following choices set the MPS and MRRS optimization strategy - at compile-time. The choices are the same as those offered for - the kernel command-line parameter 'pci', i.e., - 'pci=pcie_bus_tune_off', 'pci=pcie_bus_safe', - 'pci=pcie_bus_perf', and 'pci=pcie_bus_peer2peer'. - - This is a compile-time setting and can be overridden by the above - command-line parameters. If unsure, choose PCIE_BUS_DEFAULT. - -config PCIE_BUS_TUNE_OFF - bool "Tune Off" - help - Use the BIOS defaults; don't touch MPS at all. This is the same - as booting with 'pci=pcie_bus_tune_off'. - -config PCIE_BUS_DEFAULT - bool "Default" - help - Default choice; ensure that the MPS matches upstream bridge. - -config PCIE_BUS_SAFE - bool "Safe" - help - Use largest MPS that boot-time devices support. If you have a - closed system with no possibility of adding new devices, this - will use the largest MPS that's supported by all devices. This - is the same as booting with 'pci=pcie_bus_safe'. - -config PCIE_BUS_PERFORMANCE - bool "Performance" - help - Use MPS and MRRS for best performance. Ensure that a given - device's MPS is no larger than its parent MPS, which allows us to - keep all switches/bridges to the max MPS supported by their - parent. This is the same as booting with 'pci=pcie_bus_perf'. - -config PCIE_BUS_PEER2PEER - bool "Peer2peer" - help - Set MPS = 128 for all devices. MPS configuration effected by the - other options could cause the MPS on one root port to be - different than that of the MPS on another, which may cause - hot-added devices or peer-to-peer DMA to fail. Set MPS to the - smallest possible value (128B) system-wide to avoid these issues. - This is the same as booting with 'pci=pcie_bus_peer2peer'. - -endchoice - config VGA_ARB bool "VGA Arbitration" if EXPERT default y diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..ee1b0e34231c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -120,17 +120,7 @@ unsigned long pci_hotplug_bus_size = DEFAULT_HOTPLUG_BUS_SIZE; /* PCIe MPS/MRRS strategy; can be overridden by kernel command-line param */ -#ifdef CONFIG_PCIE_BUS_TUNE_OFF -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF; -#elif defined CONFIG_PCIE_BUS_SAFE -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_SAFE; -#elif defined CONFIG_PCIE_BUS_PERFORMANCE -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE; -#elif defined CONFIG_PCIE_BUS_PEER2PEER -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PEER2PEER; -#else enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT; -#endif /* * The default CLS is used if arch didn't set CLS explicitly and not From fb4836551fc7b22f94f051846c60234c31869b64 Mon Sep 17 00:00:00 2001 From: josh ziegler Date: Mon, 20 Apr 2026 21:20:59 -0400 Subject: [PATCH 0212/5214] Documentation: PCI: Fix typos Fix "chose" -> "choose" in pci.rst Fix "result an" -> "result in an" in pciebus-howto.rst Signed-off-by: josh ziegler Signed-off-by: Bjorn Helgaas Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260421012059.251492-1-joshziegler76@gmail.com --- Documentation/PCI/pci.rst | 2 +- Documentation/PCI/pciebus-howto.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index f4d2662871ab..be35e9a1ee75 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -338,7 +338,7 @@ the PCI_IRQ_MSI and PCI_IRQ_MSIX flags will fail, so try to always specify PCI_IRQ_INTX as well. Drivers that have different interrupt handlers for MSI/MSI-X and -legacy INTx should chose the right one based on the msi_enabled +legacy INTx should choose the right one based on the msi_enabled and msix_enabled flags in the pci_dev structure after calling pci_alloc_irq_vectors. diff --git a/Documentation/PCI/pciebus-howto.rst b/Documentation/PCI/pciebus-howto.rst index 375d9ce171f6..9cc133ccdeec 100644 --- a/Documentation/PCI/pciebus-howto.rst +++ b/Documentation/PCI/pciebus-howto.rst @@ -97,7 +97,7 @@ register its service with the PCI Express Port Bus driver (see section 5.2.1 & 5.2.2). It is important that a service driver initializes the pcie_port_service_driver data structure, included in header file /include/linux/pcieport_if.h, before calling these APIs. -Failure to do so will result an identity mismatch, which prevents +Failure to do so will result in an identity mismatch, which prevents the PCI Express Port Bus driver from loading a service driver. pcie_port_service_register From b38fa168a848b8ddd3c3d767d7d1daaf8ccadcf8 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 20 Apr 2026 13:40:28 +0200 Subject: [PATCH 0213/5214] usb: typec: mux: ps883x: Power the retimer off when not in use When there's nothing going through the retimer, there's no reason to keep it online. Put it in reset when possible to save power. Also, remove the register cache-compare optimization as it makes little sense now that the chip resets during almost all transitions and tracking the validity of that cache becomes a headache. Suggested-by: Jack Pham Signed-off-by: Konrad Dybcio Acked-by: Heikki Krogerus Tested-by: Anthony Ruhier Link: https://patch.msgid.link/20260420-topic-ps883x_unused_reset-v1-1-7aabf7004d2a@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 350 ++++++++++++++++++--------------- 1 file changed, 191 insertions(+), 159 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 1256252eceed..f52443638ee2 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -61,167 +61,9 @@ struct ps883x_retimer { struct mutex lock; /* protect non-concurrent retimer & switch */ enum typec_orientation orientation; - u8 cfg0; - u8 cfg1; - u8 cfg2; + bool in_reset; }; -static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, - int cfg1, int cfg2) -{ - struct device *dev = &retimer->client->dev; - int ret; - - if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2) - return 0; - - ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); - if (ret) { - dev_err(dev, "failed to write conn_status_0: %d\n", ret); - return ret; - } - - ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1); - if (ret) { - dev_err(dev, "failed to write conn_status_1: %d\n", ret); - return ret; - } - - ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2); - if (ret) { - dev_err(dev, "failed to write conn_status_2: %d\n", ret); - return ret; - } - - retimer->cfg0 = cfg0; - retimer->cfg1 = cfg1; - retimer->cfg2 = cfg2; - - return 0; -} - -static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) -{ - struct typec_thunderbolt_data *tb_data; - const struct enter_usb_data *eudo_data; - int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; - int cfg1 = 0x00; - int cfg2 = 0x00; - - if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) - cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; - - if (state->alt) { - switch (state->alt->svid) { - case USB_TYPEC_DP_SID: - cfg1 |= CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_HPD_LEVEL; - - switch (state->mode) { - case TYPEC_DP_STATE_C: - cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - fallthrough; - case TYPEC_DP_STATE_D: - cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; - break; - default: /* MODE_E */ - break; - } - break; - case USB_TYPEC_TBT_SID: - tb_data = state->data; - - /* Unconditional */ - cfg2 |= CONN_STATUS_2_TBT_CONNECTED; - - if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE) - cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; - - if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX) - cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT; - break; - default: - dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", - state->alt->svid); - return -EOPNOTSUPP; - } - } else { - switch (state->mode) { - case TYPEC_STATE_SAFE: - /* USB2 pins don't even go through this chip */ - case TYPEC_MODE_USB2: - break; - case TYPEC_STATE_USB: - case TYPEC_MODE_USB3: - cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - break; - case TYPEC_MODE_USB4: - eudo_data = state->data; - - cfg2 |= CONN_STATUS_2_USB4_CONNECTED; - - if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE) - cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; - break; - default: - dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", - state->mode); - return -EOPNOTSUPP; - } - } - - return ps883x_configure(retimer, cfg0, cfg1, cfg2); -} - -static int ps883x_sw_set(struct typec_switch_dev *sw, - enum typec_orientation orientation) -{ - struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw); - int ret = 0; - - ret = typec_switch_set(retimer->typec_switch, orientation); - if (ret) - return ret; - - mutex_lock(&retimer->lock); - - if (retimer->orientation != orientation) { - retimer->orientation = orientation; - - ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, - CONN_STATUS_0_ORIENTATION_REVERSED, - orientation == TYPEC_ORIENTATION_REVERSE); - if (ret) - dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); - } - - mutex_unlock(&retimer->lock); - - return ret; -} - -static int ps883x_retimer_set(struct typec_retimer *rtmr, - struct typec_retimer_state *state) -{ - struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr); - struct typec_mux_state mux_state; - int ret = 0; - - mutex_lock(&retimer->lock); - ret = ps883x_set(retimer, state); - mutex_unlock(&retimer->lock); - - if (ret) - return ret; - - mux_state.alt = state->alt; - mux_state.data = state->data; - mux_state.mode = state->mode; - - return typec_mux_set(retimer->typec_mux, &mux_state); -} - static int ps883x_enable_vregs(struct ps883x_retimer *retimer) { struct device *dev = &retimer->client->dev; @@ -291,6 +133,193 @@ static void ps883x_disable_vregs(struct ps883x_retimer *retimer) regulator_disable(retimer->vdd33_supply); } +static void ps883x_reset(struct ps883x_retimer *retimer) +{ + if (retimer->in_reset) + return; + + gpiod_set_value(retimer->reset_gpio, 1); + ps883x_disable_vregs(retimer); + retimer->in_reset = true; +} + +static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, + int cfg1, int cfg2, bool reset) +{ + struct device *dev = &retimer->client->dev; + int ret; + + if (reset) { + ps883x_reset(retimer); + + return 0; + } else if (retimer->in_reset) { + ret = ps883x_enable_vregs(retimer); + if (ret) + return ret; + + gpiod_set_value(retimer->reset_gpio, 0); + + /* firmware initialization delay */ + msleep(60); + + retimer->in_reset = false; + } + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); + if (ret) { + dev_err(dev, "failed to write conn_status_0: %d\n", ret); + return ret; + } + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1); + if (ret) { + dev_err(dev, "failed to write conn_status_1: %d\n", ret); + return ret; + } + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2); + if (ret) { + dev_err(dev, "failed to write conn_status_2: %d\n", ret); + return ret; + } + + return 0; +} + +static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) +{ + struct typec_thunderbolt_data *tb_data; + const struct enter_usb_data *eudo_data; + int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; + int cfg1 = 0x00; + int cfg2 = 0x00; + bool reset = false; + + if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) + cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; + + if (state->alt) { + switch (state->alt->svid) { + case USB_TYPEC_DP_SID: + cfg1 |= CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; + + switch (state->mode) { + case TYPEC_DP_STATE_C: + cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; + fallthrough; + case TYPEC_DP_STATE_D: + cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + default: /* MODE_E */ + break; + } + break; + case USB_TYPEC_TBT_SID: + tb_data = state->data; + + /* Unconditional */ + cfg2 |= CONN_STATUS_2_TBT_CONNECTED; + + if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + + if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX) + cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", + state->alt->svid); + return -EOPNOTSUPP; + } + } else { + switch (state->mode) { + /* SAFE can be transient or point to an actual disconnect */ + case TYPEC_STATE_SAFE: + reset = retimer->orientation == TYPEC_ORIENTATION_NONE; + break; + /* USB2 pins don't even go through this chip */ + case TYPEC_MODE_USB2: + reset = true; + break; + case TYPEC_STATE_USB: + case TYPEC_MODE_USB3: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + case TYPEC_MODE_USB4: + eudo_data = state->data; + + cfg2 |= CONN_STATUS_2_USB4_CONNECTED; + + if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", + state->mode); + return -EOPNOTSUPP; + } + } + + return ps883x_configure(retimer, cfg0, cfg1, cfg2, reset); +} + +static int ps883x_sw_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw); + int ret = 0; + + ret = typec_switch_set(retimer->typec_switch, orientation); + if (ret) + return ret; + + guard(mutex)(&retimer->lock); + + if (retimer->orientation != orientation) { + retimer->orientation = orientation; + + /* + * Orientation notifications usually come prior to mode switch + * events. If the retimer is already in reset, we still want to + * cache the new orientation value for the subsequent ps883x_set(). + */ + if (retimer->in_reset) + return 0; + + ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + CONN_STATUS_0_ORIENTATION_REVERSED, + orientation == TYPEC_ORIENTATION_REVERSE); + if (ret) + dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); + } + + return ret; +} + +static int ps883x_retimer_set(struct typec_retimer *rtmr, + struct typec_retimer_state *state) +{ + struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr); + struct typec_mux_state mux_state; + int ret = 0; + + mutex_lock(&retimer->lock); + ret = ps883x_set(retimer, state); + mutex_unlock(&retimer->lock); + + if (ret) + return ret; + + mux_state.alt = state->alt; + mux_state.data = state->data; + mux_state.mode = state->mode; + + return typec_mux_set(retimer->typec_mux, &mux_state); +} + static int ps883x_get_vregs(struct ps883x_retimer *retimer) { struct device *dev = &retimer->client->dev; @@ -422,6 +451,9 @@ static int ps883x_retimer_probe(struct i2c_client *client) } } + /* Keep the retimer in reset until a Type-C notification comes */ + ps883x_reset(retimer); + sw_desc.drvdata = retimer; sw_desc.fwnode = dev_fwnode(dev); sw_desc.set = ps883x_sw_set; From 8bdb0b3830eaf588fcd7c76c1893d05d871600d2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 24 Apr 2026 18:42:01 -0700 Subject: [PATCH 0214/5214] usb: typec: intel_pmc_mux: combine kzalloc + kcalloc A flexible array member can be used to combine allocations and simplify handling slightly. Add __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260425014201.439251-1-rosenp@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 1698428654ab..e22b070a140f 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -151,13 +151,14 @@ struct pmc_usb { u8 num_ports; struct device *dev; struct intel_scu_ipc_dev *ipc; - struct pmc_usb_port *port; struct acpi_device *iom_adev; void __iomem *iom_base; u32 iom_port_status_offset; u8 iom_port_status_size; struct dentry *dentry; + + struct pmc_usb_port port[] __counted_by(num_ports); }; static struct dentry *pmc_mux_debugfs_root; @@ -731,27 +732,25 @@ static int pmc_usb_probe(struct platform_device *pdev) { struct fwnode_handle *fwnode = NULL; struct pmc_usb *pmc; + u8 num_ports; int i = 0; int ret; - pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); - if (!pmc) - return -ENOMEM; - device_for_each_child_node(&pdev->dev, fwnode) - pmc->num_ports++; + num_ports++; /* The IOM microcontroller has a limitation of max 4 ports. */ - if (pmc->num_ports > 4) { + if (num_ports > 4) { dev_err(&pdev->dev, "driver limited to 4 ports\n"); return -ERANGE; } - pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports, - sizeof(struct pmc_usb_port), GFP_KERNEL); - if (!pmc->port) + pmc = devm_kzalloc(&pdev->dev, struct_size(pmc, port, num_ports), GFP_KERNEL); + if (!pmc) return -ENOMEM; + pmc->num_ports = num_ports; + pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev); if (!pmc->ipc) return -EPROBE_DEFER; From 0677deacad502d11d86c1ba5c635aa733803ab9b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:00:45 +0200 Subject: [PATCH 0215/5214] USB: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427070044.17974-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 0b56b773dbdf..91b62acb537b 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -145,7 +145,7 @@ config USB_APPLEDISPLAY Displays over USB. This driver provides a sysfs interface. config USB_QCOM_EUD - tristate "QCOM Embedded USB Debugger(EUD) Driver" + tristate "Qualcomm Embedded USB Debugger(EUD) Driver" depends on ARCH_QCOM || COMPILE_TEST select QCOM_SCM select USB_ROLE_SWITCH From 25bd55f46032656012eecdc6eabd62f2685a2ccc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Apr 2026 16:32:10 +0200 Subject: [PATCH 0216/5214] usb: udc: pxa: remove unused platform_data None of the remaining boards put useful data into the platform_data structures, so effectively this only works with DT based probing. Remove all code that references this data, to stop using the legacy gpiolib interfaces. The pxa27x version already supports gpio descriptors, while the pxa25x version now does it the same way. Reviewed-by: Andy Shevchenko Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260427143300.2887692-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/devices.c | 7 ---- arch/arm/mach-pxa/gumstix.c | 1 - arch/arm/mach-pxa/udc.h | 8 ----- drivers/usb/gadget/udc/pxa25x_udc.c | 41 ++++++++---------------- drivers/usb/gadget/udc/pxa25x_udc.h | 2 +- drivers/usb/gadget/udc/pxa27x_udc.c | 35 ++++---------------- drivers/usb/gadget/udc/pxa27x_udc.h | 2 -- include/linux/platform_data/pxa2xx_udc.h | 15 --------- 8 files changed, 20 insertions(+), 91 deletions(-) delete mode 100644 arch/arm/mach-pxa/udc.h diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c index 7695cfce01a1..edad956a1483 100644 --- a/arch/arm/mach-pxa/devices.c +++ b/arch/arm/mach-pxa/devices.c @@ -11,7 +11,6 @@ #include #include -#include "udc.h" #include #include #include "irqs.h" @@ -83,10 +82,6 @@ void __init pxa_set_mci_info(const struct pxamci_platform_data *info, pr_err("Unable to create mci device: %d\n", err); } -static struct pxa2xx_udc_mach_info pxa_udc_info = { - .gpio_pullup = -1, -}; - static struct resource pxa2xx_udc_resources[] = { [0] = { .start = 0x40600000, @@ -108,7 +103,6 @@ struct platform_device pxa25x_device_udc = { .resource = pxa2xx_udc_resources, .num_resources = ARRAY_SIZE(pxa2xx_udc_resources), .dev = { - .platform_data = &pxa_udc_info, .dma_mask = &udc_dma_mask, } }; @@ -119,7 +113,6 @@ struct platform_device pxa27x_device_udc = { .resource = pxa2xx_udc_resources, .num_resources = ARRAY_SIZE(pxa2xx_udc_resources), .dev = { - .platform_data = &pxa_udc_info, .dma_mask = &udc_dma_mask, } }; diff --git a/arch/arm/mach-pxa/gumstix.c b/arch/arm/mach-pxa/gumstix.c index 1713bdf3b71e..6074815a4bca 100644 --- a/arch/arm/mach-pxa/gumstix.c +++ b/arch/arm/mach-pxa/gumstix.c @@ -39,7 +39,6 @@ #include "pxa25x.h" #include -#include "udc.h" #include "gumstix.h" #include "devices.h" diff --git a/arch/arm/mach-pxa/udc.h b/arch/arm/mach-pxa/udc.h deleted file mode 100644 index 9a827e32db98..000000000000 --- a/arch/arm/mach-pxa/udc.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * arch/arm/mach-pxa/include/mach/udc.h - * - */ -#include - -extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info); - diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index b3d58d7c3a77..594d67193763 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -12,7 +12,7 @@ /* #define VERBOSE_DEBUG */ #include -#include +#include #include #include #include @@ -261,24 +261,12 @@ static void nuke (struct pxa25x_ep *, int status); /* one GPIO should control a D+ pullup, so host sees this device (or not) */ static void pullup_off(void) { - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - int off_level = mach->gpio_pullup_inverted; - - if (gpio_is_valid(mach->gpio_pullup)) - gpio_set_value(mach->gpio_pullup, off_level); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); + gpiod_set_value(the_controller->pullup_gpio, 0); } static void pullup_on(void) { - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - int on_level = !mach->gpio_pullup_inverted; - - if (gpio_is_valid(mach->gpio_pullup)) - gpio_set_value(mach->gpio_pullup, on_level); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_CONNECT); + gpiod_set_value(the_controller->pullup_gpio, 1); } #if defined(CONFIG_CPU_BIG_ENDIAN) @@ -1190,8 +1178,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) udc = container_of(_gadget, struct pxa25x_udc, gadget); - /* not all boards support pullup control */ - if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) + if (!udc->pullup_gpio) return -EOPNOTSUPP; udc->pullup = (is_active != 0); @@ -2343,19 +2330,17 @@ static int pxa25x_udc_probe(struct platform_device *pdev) /* other non-static parts of init */ dev->dev = &pdev->dev; - dev->mach = dev_get_platdata(&pdev->dev); dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (gpio_is_valid(dev->mach->gpio_pullup)) { - retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup, - GPIOF_OUT_INIT_LOW, "pca25x_udc GPIO PULLUP"); - if (retval) { - dev_dbg(&pdev->dev, - "can't get pullup gpio %d, err: %d\n", - dev->mach->gpio_pullup, retval); - goto err; - } + dev->pullup_gpio = devm_gpiod_get_index_optional(&pdev->dev, "pullup", 0, + GPIOD_OUT_HIGH); + if (IS_ERR(dev->pullup_gpio)) { + dev_dbg(&pdev->dev, + "can't get pullup gpio err: %ld\n", + PTR_ERR(dev->pullup_gpio)); + retval = PTR_ERR(dev->pullup_gpio); + goto err; } timer_setup(&dev->timer, udc_watchdog, 0); @@ -2439,7 +2424,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) struct pxa25x_udc *udc = platform_get_drvdata(dev); unsigned long flags; - if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) + if (!udc->pullup_gpio) WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 6ab6047edc83..3452cf54286c 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h @@ -112,7 +112,7 @@ struct pxa25x_udc { struct device *dev; struct clk *clk; - struct pxa2xx_udc_mach_info *mach; + struct gpio_desc *pullup_gpio; struct usb_phy *transceiver; u64 dma_mask; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 1abea0d48c35..640f81988c04 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -1423,14 +1422,7 @@ static const struct usb_ep_ops pxa_ep_ops = { */ static void dplus_pullup(struct pxa_udc *udc, int on) { - if (udc->gpiod) { - gpiod_set_value(udc->gpiod, on); - } else if (udc->udc_command) { - if (on) - udc->udc_command(PXA2XX_UDC_CMD_CONNECT); - else - udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT); - } + gpiod_set_value(udc->gpiod, on); udc->pullup_on = on; } @@ -1521,7 +1513,7 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) struct pxa_udc *udc = to_gadget_udc(_gadget); int ret; - if (!udc->gpiod && !udc->udc_command) + if (!udc->gpiod) return -EOPNOTSUPP; dplus_pullup(udc, is_active); @@ -2380,26 +2372,11 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); static int pxa_udc_probe(struct platform_device *pdev) { struct pxa_udc *udc = &memory; - int retval = 0, gpio; - struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); + int retval = 0; - if (mach) { - gpio = mach->gpio_pullup; - if (gpio_is_valid(gpio)) { - retval = devm_gpio_request_one(&pdev->dev, gpio, - GPIOF_OUT_INIT_LOW, - "USB D+ pullup"); - if (retval) - return retval; - udc->gpiod = gpio_to_desc(mach->gpio_pullup); - - if (mach->gpio_pullup_inverted ^ gpiod_is_active_low(udc->gpiod)) - gpiod_toggle_active_low(udc->gpiod); - } - udc->udc_command = mach->udc_command; - } else { - udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); - } + udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); + if (IS_ERR(udc->gpiod)) + return PTR_ERR(udc->gpiod); udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 31bf79ce931c..2c28b691010a 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -426,7 +426,6 @@ struct udc_stats { * @usb_gadget: udc gadget structure * @driver: bound gadget (zero, g_ether, g_mass_storage, ...) * @dev: device - * @udc_command: machine specific function to activate D+ pullup * @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none) * @transceiver: external transceiver to handle vbus sense and D+ pullup * @ep0state: control endpoint state machine state @@ -452,7 +451,6 @@ struct pxa_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct device *dev; - void (*udc_command)(int); struct gpio_desc *gpiod; struct usb_phy *transceiver; diff --git a/include/linux/platform_data/pxa2xx_udc.h b/include/linux/platform_data/pxa2xx_udc.h index bc99cc6a3c5f..c1e4d03bae2c 100644 --- a/include/linux/platform_data/pxa2xx_udc.h +++ b/include/linux/platform_data/pxa2xx_udc.h @@ -10,21 +10,6 @@ #ifndef PXA2XX_UDC_H #define PXA2XX_UDC_H -struct pxa2xx_udc_mach_info { - int (*udc_is_connected)(void); /* do we see host? */ - void (*udc_command)(int cmd); -#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */ -#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */ - - /* Boards following the design guidelines in the developer's manual, - * with on-chip GPIOs not Lubbock's weird hardware, can have a sane - * VBUS IRQ and omit the methods above. Store the GPIO number - * here. Note that sometimes the signals go through inverters... - */ - bool gpio_pullup_inverted; - int gpio_pullup; /* high == pullup activated */ -}; - #ifdef CONFIG_PXA27x extern void pxa27x_clear_otgph(void); #else From a2083fd1fa7aa0ef5cd8fd92396da0de2d0654b0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:00:53 +0200 Subject: [PATCH 0217/5214] serial: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427070052.18097-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9aa61c93d7bc..3e519d001e02 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -913,13 +913,13 @@ config SERIAL_MSM_CONSOLE select SERIAL_EARLYCON config SERIAL_QCOM_GENI - tristate "QCOM on-chip GENI based serial port support" + tristate "Qualcomm on-chip GENI based serial port support" depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_GENI_SE select SERIAL_CORE config SERIAL_QCOM_GENI_CONSOLE - bool "QCOM GENI Serial Console support" + bool "Qualcomm GENI Serial Console support" depends on SERIAL_QCOM_GENI select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON From f9915120d004a8e9e64afee138dd448cd7cad9bc Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Fri, 17 Apr 2026 23:13:37 +0100 Subject: [PATCH 0218/5214] remoteproc: Dead code cleanup in Kconfig for STM32_RPROC There is already an 'if REMOTEPROC' condition wrapping this config option, making the 'depends on REMOTEPROC' statement a duplicate dependency (dead code). I propose leaving the outer 'if REMOTEPROC...endif' and removing the individual 'depends on REMOTEPROC' statement. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Acked-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20260417221337.286313-1-julianbraha@gmail.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index ee54436fea5a..c78e431b7b2d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -316,7 +316,6 @@ config ST_SLIM_REMOTEPROC config STM32_RPROC tristate "STM32 remoteproc support" depends on ARCH_STM32 || COMPILE_TEST - depends on REMOTEPROC select MAILBOX help Say y here to support STM32 MCU processors via the From e628f6a6c33ac647bb904c35a674a0f664c99efe Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 17 Apr 2026 09:07:44 +0200 Subject: [PATCH 0219/5214] dt-bindings: clock: qcom: document the Milos GX clock controller Qualcomm GX(graphics) is a clock controller which has PLLs, clocks and Power domains (GDSC), but the requirement from the SW driver is to use the GDSC power domain from the clock controller to recover the GPU firmware in case of any failure/hangs. The rest of the resources of the clock controller are being used by the firmware of GPU. This module exposes the GDSC power domains which helps the recovery of Graphics subsystem. Milos can reuse the qcom,kaanapali-gxclkctl.h header due to similarity of the hardware block, and also reuse of the Linux driver. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20260417-milos-gxclkctl-v3-1-08f5988c43a2@fairphone.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,milos-gxclkctl.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml new file mode 100644 index 000000000000..fbcb5d3f3e3d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,milos-gxclkctl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Graphics Power Domain Controller on Milos + +maintainers: + - Luca Weiss + +description: | + Qualcomm GX(graphics) is a clock controller which has PLLs, clocks and + Power domains (GDSC). This module provides the power domains control + of gxclkctl on Qualcomm SoCs which helps the recovery of Graphics subsystem. + + See also: + include/dt-bindings/clock/qcom,kaanapali-gxclkctl.h + +properties: + compatible: + enum: + - qcom,milos-gxclkctl + + reg: + maxItems: 1 + + power-domains: + description: + Power domains required for the clock controller to operate + items: + - description: GFX power domain + - description: GPUCC(CX) power domain + + '#power-domain-cells': + const: 1 + +required: + - compatible + - reg + - power-domains + - '#power-domain-cells' + +additionalProperties: false + +examples: + - | + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@3d64000 { + compatible = "qcom,milos-gxclkctl"; + reg = <0x0 0x03d64000 0x0 0x6000>; + power-domains = <&rpmhpd RPMHPD_GFX>, + <&gpucc 0>; + #power-domain-cells = <1>; + }; + }; +... From 3df6b9dbd24e1610854c17a8ec4ac146481b8e42 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 17 Apr 2026 09:07:45 +0200 Subject: [PATCH 0220/5214] clk: qcom: Add support for GXCLK for Milos GXCLKCTL (Graphics GX Clock Controller) is a block dedicated to managing clocks for the GPU subsystem on GX power domain. The GX clock controller driver manages only the GX GDSC and the rest of the resources of the controller are managed by the firmware. We can use the existing kaanapali driver for Milos as well since the GX_CLKCTL_GX_GDSC supported by the Linux driver requires the same configuration. Reviewed-by: Jagadeesh Kona Reviewed-by: Taniya Das Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20260417-milos-gxclkctl-v3-2-08f5988c43a2@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Makefile | 2 +- drivers/clk/qcom/gxclkctl-kaanapali.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 89d07c35e4d9..462c7615a6d7 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -189,7 +189,7 @@ obj-$(CONFIG_SM_GPUCC_8450) += gpucc-sm8450.o obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o obj-$(CONFIG_SM_GPUCC_8750) += gpucc-sm8750.o gxclkctl-kaanapali.o -obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o +obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o gxclkctl-kaanapali.o obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o obj-$(CONFIG_SM_TCSRCC_8650) += tcsrcc-sm8650.o diff --git a/drivers/clk/qcom/gxclkctl-kaanapali.c b/drivers/clk/qcom/gxclkctl-kaanapali.c index 40d856378a74..7b0af0ba1e68 100644 --- a/drivers/clk/qcom/gxclkctl-kaanapali.c +++ b/drivers/clk/qcom/gxclkctl-kaanapali.c @@ -53,6 +53,7 @@ static const struct qcom_cc_desc gx_clkctl_kaanapali_desc = { static const struct of_device_id gx_clkctl_kaanapali_match_table[] = { { .compatible = "qcom,glymur-gxclkctl" }, { .compatible = "qcom,kaanapali-gxclkctl" }, + { .compatible = "qcom,milos-gxclkctl" }, { .compatible = "qcom,sm8750-gxclkctl" }, { } }; From 404f80bd09bb2c1c378d52aed2679c860c2dd592 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Thu, 26 Mar 2026 17:26:43 +0800 Subject: [PATCH 0221/5214] dt-bindings: clock: amlogic: Fix redundant hyphen in "amlogic,t7-gp1--pll" string. Fix redundant hyphen in "amlogic,t7-gp1--pll" string. Fixes: 5437753728ac ("dt-bindings: clock: add Amlogic T7 PLL clock controller") Signed-off-by: Ronald Claveau Signed-off-by: Jian Hu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260326092645.1053261-2-jian.hu@amlogic.com Signed-off-by: Jerome Brunet --- .../devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml index 49c61f65deff..b488d92b7984 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml +++ b/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml @@ -72,7 +72,7 @@ allOf: contains: enum: - amlogic,t7-gp0-pll - - amlogic,t7-gp1--pll + - amlogic,t7-gp1-pll - amlogic,t7-hifi-pll - amlogic,t7-pcie-pll - amlogic,t7-mpll From ca89c88bcf69daca829044c638a8163d5ce47af0 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Thu, 26 Mar 2026 17:26:44 +0800 Subject: [PATCH 0222/5214] dt-bindings: clock: amlogic: t7: Add missing mpll3 parent clock The mpll3 clock is one parent clock of the sd_emmc and mipi_isp clocks on the Amlogic T7 SoC, but was missing from t7-peripherals-clkc.yaml bindings. Add the mpll3 clock source to the T7 peripherals clock controller input clock list, so that sd_emmc and mipi_isp can use it. For logical consistency, place the required mpll3 entry before the optional entry. This change breaks the ABI, but while the amlogic,t7-peripherals-clkc bindings have been merged upstream, the corresponding DT has not been merged yet. Thus, no real users or systems are affected. Fixes: b4156204e0f5 ("dt-bindings: clock: add Amlogic T7 peripherals clock controller") Signed-off-by: Jian Hu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260326092645.1053261-3-jian.hu@amlogic.com Signed-off-by: Jerome Brunet --- .../bindings/clock/amlogic,t7-peripherals-clkc.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml index 55bb73707d58..a4b214a941ea 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml +++ b/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml @@ -24,7 +24,7 @@ properties: const: 1 clocks: - minItems: 14 + minItems: 15 items: - description: input oscillator - description: input sys clk @@ -40,12 +40,13 @@ properties: - description: input gp1 pll - description: input mpll1 - description: input mpll2 + - description: input mpll3 - description: external input rmii oscillator (optional) - description: input video pll0 (optional) - description: external pad input for rtc (optional) clock-names: - minItems: 14 + minItems: 15 items: - const: xtal - const: sys @@ -61,6 +62,7 @@ properties: - const: gp1 - const: mpll1 - const: mpll2 + - const: mpll3 - const: ext_rmii - const: vid_pll0 - const: ext_rtc @@ -97,7 +99,8 @@ examples: <&gp0 1>, <&gp1 1>, <&mpll 4>, - <&mpll 6>; + <&mpll 6>, + <&mpll 8>; clock-names = "xtal", "sys", "fix", @@ -111,6 +114,7 @@ examples: "gp0", "gp1", "mpll1", - "mpll2"; + "mpll2", + "mpll3"; }; }; From 38eec41ded8621c75e5772af5fccde906fcd8276 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 12:30:06 +0200 Subject: [PATCH 0223/5214] pinctrl: tegra: Enable easier compile testing Currently NVIDIA Tegra pin controller drivers cannot be compile tested, unless ARCH_TEGRA is selected. That partially defeats the purpose of compile testing, since ARCH_TEGRA is pulled when building platform kernels. Solve it and allow compile testing independently of ARCH_TEGRA choice which requires few less usual changes: 1. Descent in Makefile in to drivers/pinctrl/tegra/ unconditionally, because there is no menu option. 2. Depend on COMMON_CLK for PINCTRL_TEGRA20, because it uses clk_register_mux(). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/Makefile | 2 +- drivers/pinctrl/tegra/Kconfig | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f7d5d5f76d0c..9d33fa28a096 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -93,7 +93,7 @@ obj-y += starfive/ obj-$(CONFIG_PINCTRL_STM32) += stm32/ obj-y += sunplus/ obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ -obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-y += tegra/ obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_VISCONTI) += visconti/ diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index 660d101ea367..3e8789871f0f 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -1,43 +1,45 @@ # SPDX-License-Identifier: GPL-2.0-only config PINCTRL_TEGRA - bool + bool "NVIDIA Tegra pin controllers common" if COMPILE_TEST && !ARCH_TEGRA select PINMUX select PINCONF config PINCTRL_TEGRA20 - bool + bool "NVIDIA Tegra20 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA + depends on COMMON_CLK config PINCTRL_TEGRA30 - bool + bool "NVIDIA Tegra30 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA114 - bool + bool "NVIDIA Tegra114 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA124 - bool + bool "NVIDIA Tegra124 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA210 - bool + bool "NVIDIA Tegra210 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA186 - bool + bool "NVIDIA Tegra186 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA194 - bool + bool "NVIDIA Tegra194 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA234 - bool + bool "NVIDIA Tegra234 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA config PINCTRL_TEGRA_XUSB - def_bool y if ARCH_TEGRA + bool "NVIDIA Tegra XUSB pin controller" if COMPILE_TEST && !ARCH_TEGRA + default y if ARCH_TEGRA select GENERIC_PHY select PINCONF select PINMUX From 32ba46cede2807215d6c503f27cf554226ecaa9f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 15:04:56 +0200 Subject: [PATCH 0224/5214] pinctrl: realtek: Enable compile testing Enable compile testing for Realtek pin controller drivers for increased build and static checkers coverage. PINCTRL_RTD uses pinconf_generic_dt_node_to_map(), thus needs OF. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Yu-Chun Lin Signed-off-by: Linus Walleij --- drivers/pinctrl/Makefile | 2 +- drivers/pinctrl/realtek/Kconfig | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 9d33fa28a096..b054cfb99348 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -82,7 +82,7 @@ obj-y += nuvoton/ obj-y += nxp/ obj-$(CONFIG_PINCTRL_PXA) += pxa/ obj-y += qcom/ -obj-$(CONFIG_ARCH_REALTEK) += realtek/ +obj-$(CONFIG_PINCTRL_RTD) += realtek/ obj-$(CONFIG_PINCTRL_RENESAS) += renesas/ obj-$(CONFIG_PINCTRL_SAMSUNG) += samsung/ obj-y += sophgo/ diff --git a/drivers/pinctrl/realtek/Kconfig b/drivers/pinctrl/realtek/Kconfig index 054e85db99e7..a156c4ef556e 100644 --- a/drivers/pinctrl/realtek/Kconfig +++ b/drivers/pinctrl/realtek/Kconfig @@ -2,8 +2,8 @@ config PINCTRL_RTD tristate "Realtek DHC core pin controller driver" - depends on ARCH_REALTEK - default y + depends on ARCH_REALTEK || (COMPILE_TEST && OF) + default ARCH_REALTEK select PINMUX select GENERIC_PINCONF select REGMAP_MMIO @@ -11,22 +11,22 @@ config PINCTRL_RTD config PINCTRL_RTD1619B tristate "Realtek DHC 1619B pin controller driver" depends on PINCTRL_RTD - default y + default ARCH_REALTEK config PINCTRL_RTD1319D tristate "Realtek DHC 1319D pin controller driver" depends on PINCTRL_RTD - default y + default ARCH_REALTEK config PINCTRL_RTD1315E tristate "Realtek DHC 1315E pin controller driver" depends on PINCTRL_RTD - default y + default ARCH_REALTEK config PINCTRL_RTD1625 tristate "Realtek DHC 1625 pin controller driver" depends on PINCTRL_RTD - default y + default ARCH_REALTEK help This driver enables support for the pin controller on the Realtek RTD1625 SoCs. From 0e6ba181b7982bd82a92698d2c8eec621d4eef9d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 15:04:57 +0200 Subject: [PATCH 0225/5214] pinctrl: aspeed: Enable compile testing outside of ARCH_ASPEED Since inception in commit 4d3d0e4272d8 ("pinctrl: Add core support for Aspeed SoCs"), the Aspeed pin controller drivers cannot be compile tested, unless ARCH_ASPEED is selected. . That partially defeats the purpose of compile testing, since ARCH_ASPEED is pulled when building platform kernels. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index b054cfb99348..9320ffae5f31 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -66,7 +66,7 @@ obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o obj-y += actions/ -obj-$(CONFIG_ARCH_ASPEED) += aspeed/ +obj-$(CONFIG_PINCTRL_ASPEED) += aspeed/ obj-y += bcm/ obj-$(CONFIG_PINCTRL_BERLIN) += berlin/ obj-y += cirrus/ From 93d8c6c0e1879d098ff478511032b42a6b26aae0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 15:04:58 +0200 Subject: [PATCH 0226/5214] pinctrl: vt8500: Enable compile testing Enable compile testing for Realtek pin controller drivers for increased build and static checkers coverage. PINCTRL_WMT uses gpiochip_get_data(), thus needs GPIOLIB. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/Makefile | 2 +- drivers/pinctrl/vt8500/Kconfig | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 9320ffae5f31..78135ee963db 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -97,4 +97,4 @@ obj-y += tegra/ obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_VISCONTI) += visconti/ -obj-$(CONFIG_ARCH_VT8500) += vt8500/ +obj-$(CONFIG_PINCTRL_WMT) += vt8500/ diff --git a/drivers/pinctrl/vt8500/Kconfig b/drivers/pinctrl/vt8500/Kconfig index 2ca00b54b7a8..1a40c153a82a 100644 --- a/drivers/pinctrl/vt8500/Kconfig +++ b/drivers/pinctrl/vt8500/Kconfig @@ -3,16 +3,17 @@ # VIA/Wondermedia PINCTRL drivers # -if ARCH_VT8500 +if ARCH_VT8500 || COMPILE_TEST config PINCTRL_WMT bool select PINMUX select GENERIC_PINCONF + select GPIOLIB config PINCTRL_VT8500 bool "VIA VT8500 pin controller driver" - depends on ARCH_WM8505 + depends on ARCH_WM8505 || COMPILE_TEST select PINCTRL_WMT help Say yes here to support the gpio/pin control module on @@ -20,7 +21,7 @@ config PINCTRL_VT8500 config PINCTRL_WM8505 bool "Wondermedia WM8505 pin controller driver" - depends on ARCH_WM8505 + depends on ARCH_WM8505 || COMPILE_TEST select PINCTRL_WMT help Say yes here to support the gpio/pin control module on @@ -28,7 +29,7 @@ config PINCTRL_WM8505 config PINCTRL_WM8650 bool "Wondermedia WM8650 pin controller driver" - depends on ARCH_WM8505 + depends on ARCH_WM8505 || COMPILE_TEST select PINCTRL_WMT help Say yes here to support the gpio/pin control module on @@ -36,7 +37,7 @@ config PINCTRL_WM8650 config PINCTRL_WM8750 bool "Wondermedia WM8750 pin controller driver" - depends on ARCH_WM8750 + depends on ARCH_WM8750 || COMPILE_TEST select PINCTRL_WMT help Say yes here to support the gpio/pin control module on @@ -44,7 +45,7 @@ config PINCTRL_WM8750 config PINCTRL_WM8850 bool "Wondermedia WM8850 pin controller driver" - depends on ARCH_WM8850 + depends on ARCH_WM8850 || COMPILE_TEST select PINCTRL_WMT help Say yes here to support the gpio/pin control module on From 61b5deb5a968f7a82124f9b2591a6a151ed7273a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 13:10:48 +0200 Subject: [PATCH 0227/5214] dt-bindings: pinctrl: nvidia,tegra234: Add missing required block Binding should require 'reg' property, because address space cannot be missing in the hardware and is already needed by the Linux drivers. Require also 'compatible' by convention, although it is not strictly necessary. Fixes: 857982138b79 ("dt-bindings: pinctrl: Document Tegra234 pin controllers") Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij --- .../bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml | 4 ++++ .../devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml index db8224dfba2c..56fb9cf763ef 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml @@ -58,6 +58,10 @@ patternProperties: drive_soc_gpio27_pee6, drive_ao_retention_n_pee2, drive_vcomp_alert_pee1, drive_hdmi_cec_pgg0 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml index f5a3a881dec4..bd305a34eee2 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml @@ -115,6 +115,10 @@ patternProperties: drive_sdmmc1_dat2_pj4, drive_sdmmc1_dat1_pj3, drive_sdmmc1_dat0_pj2 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: From 39dcfa63dc479c97ba90170b972116030a4e184d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 13:10:49 +0200 Subject: [PATCH 0228/5214] dt-bindings: pinctrl: nvidia,tegra234: Correctly use additionalProperties The binding does not reference any other schema, thus should use "additionalProperties: false" to disallow any undocumented properties. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml | 2 +- .../devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml index 56fb9cf763ef..4910dc8e8aeb 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml @@ -62,7 +62,7 @@ required: - compatible - reg -unevaluatedProperties: false +additionalProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml index bd305a34eee2..52b3d40e8839 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml @@ -119,7 +119,7 @@ required: - compatible - reg -unevaluatedProperties: false +additionalProperties: false examples: - | From 58631a03cf647ea93c2060d87d45f0c019fbfbbc Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 15 Apr 2026 16:59:24 +0530 Subject: [PATCH 0229/5214] dt-bindings: pinctrl: qcom: add IPQ9650 pinctrl Add device tree bindings for IPQ9650 TLMM block. Signed-off-by: Kathiravan Thirumoorthy Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- .../bindings/pinctrl/qcom,ipq9650-tlmm.yaml | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,ipq9650-tlmm.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq9650-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq9650-tlmm.yaml new file mode 100644 index 000000000000..549eaa6aa11b --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq9650-tlmm.yaml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,ipq9650-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ9650 TLMM pin controller + +maintainers: + - Bjorn Andersson + - Kathiravan Thirumoorthy + +description: + Top Level Mode Multiplexer pin controller in Qualcomm IPQ9650 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,ipq9650-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 27 + + gpio-line-names: + maxItems: 54 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-ipq9650-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-ipq9650-tlmm-state" + additionalProperties: false + +$defs: + qcom-ipq9650-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|[1-4][0-9]|5[0-3])$" + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ atest_char_start, atest_char_status0, atest_char_status1, + atest_char_status2, atest_char_status3, atest_tic_en, + audio_pri_mclk_in0, audio_pri_mclk_out0, audio_pri_mclk_in1, + audio_pri_mclk_out1, audio_pri, audio_sec, audio_sec_mclk_in0, + audio_sec_mclk_out0, audio_sec_mclk_in1, audio_sec_mclk_out1, + core_voltage_0, core_voltage_1, core_voltage_2, core_voltage_3, + core_voltage_4, cri_rng0, cri_rng1, cri_rng2, dbg_out_clk, + gcc_plltest_bypassnl, gcc_plltest_resetn, gcc_tlmm, gpio, + mdc_mst, mdc_slv0, mdc_slv1, mdio_mst, mdio_slv, mdio_slv0, + mdio_slv1, pcie0_clk_req_n, pcie0_wake, pcie1_clk_req_n, + pcie1_wake, pcie2_clk_req_n, pcie2_wake, pcie3_clk_req_n, + pcie3_wake, pcie4_clk_req_n, pcie4_wake, pll_bist_sync, + pll_test, pwm, qdss_cti_trig_in_a0, qdss_cti_trig_in_a1, + qdss_cti_trig_in_b0, qdss_cti_trig_in_b1, qdss_cti_trig_out_a0, + qdss_cti_trig_out_a1, qdss_cti_trig_out_b0, qdss_cti_trig_out_b1, + qdss_traceclk_a, qdss_tracectl_a, qdss_tracedata_a, qspi_data, + qspi_clk, qspi_cs_n, qup_se0, qup_se1, qup_se2, qup_se3, + qup_se4, qup_se5, qup_se6, qup_se7, resout, rx_los0, rx_los1, + rx_los2, sdc_clk, sdc_cmd, sdc_data, tsens_max, tsn ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq9650-tlmm"; + reg = <0x01000000 0x300000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 54>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; + + qup-uart1-default-state { + pins = "gpio43", "gpio44"; + function = "qup_se6"; + drive-strength = <8>; + bias-pull-down; + }; + }; From 3c8e7ba0e3996723f7e4be7c982826a9c2be828b Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 15 Apr 2026 16:59:25 +0530 Subject: [PATCH 0230/5214] pinctrl: qcom: Introduce IPQ9650 TLMM driver Qualcomm's IPQ9650 comes with a TLMM block, like all other platforms, so add a driver for it. Signed-off-by: Kathiravan Thirumoorthy Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig.msm | 9 + drivers/pinctrl/qcom/Makefile | 1 + drivers/pinctrl/qcom/pinctrl-ipq9650.c | 762 +++++++++++++++++++++++++ 3 files changed, 772 insertions(+) create mode 100644 drivers/pinctrl/qcom/pinctrl-ipq9650.c diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 836cdeca1006..0d6f698e26ec 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -120,6 +120,15 @@ config PINCTRL_IPQ9574 Qualcomm Technologies Inc. IPQ9574 platform. Select this for IPQ9574. +config PINCTRL_IPQ9650 + tristate "Qualcomm Technologies, Inc. IPQ9650 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for + the Qualcomm Technologies Inc. TLMM block found on the + Qualcomm Technologies Inc. IPQ9650 platform. Select this for + IPQ9650. + config PINCTRL_KAANAPALI tristate "Qualcomm Technologies Inc Kaanapali pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 84bda3ada874..f0bb1920b27b 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_IPQ5424) += pinctrl-ipq5424.o obj-$(CONFIG_PINCTRL_IPQ8074) += pinctrl-ipq8074.o obj-$(CONFIG_PINCTRL_IPQ6018) += pinctrl-ipq6018.o obj-$(CONFIG_PINCTRL_IPQ9574) += pinctrl-ipq9574.o +obj-$(CONFIG_PINCTRL_IPQ9650) += pinctrl-ipq9650.o obj-$(CONFIG_PINCTRL_KAANAPALI) += pinctrl-kaanapali.o obj-$(CONFIG_PINCTRL_MSM8226) += pinctrl-msm8226.o obj-$(CONFIG_PINCTRL_MSM8660) += pinctrl-msm8660.o diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9650.c b/drivers/pinctrl/qcom/pinctrl-ipq9650.c new file mode 100644 index 000000000000..64e443aa31b2 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-ipq9650.c @@ -0,0 +1,762 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + }, \ + .nfuncs = 10, \ + } + +static const struct pinctrl_pin_desc ipq9650_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); + +enum ipq9650_functions { + msm_mux_atest_char_start, + msm_mux_atest_char_status0, + msm_mux_atest_char_status1, + msm_mux_atest_char_status2, + msm_mux_atest_char_status3, + msm_mux_atest_tic_en, + msm_mux_audio_pri_mclk_in0, + msm_mux_audio_pri_mclk_out0, + msm_mux_audio_pri_mclk_in1, + msm_mux_audio_pri_mclk_out1, + msm_mux_audio_pri, + msm_mux_audio_sec, + msm_mux_audio_sec_mclk_in0, + msm_mux_audio_sec_mclk_out0, + msm_mux_audio_sec_mclk_in1, + msm_mux_audio_sec_mclk_out1, + msm_mux_core_voltage_0, + msm_mux_core_voltage_1, + msm_mux_core_voltage_2, + msm_mux_core_voltage_3, + msm_mux_core_voltage_4, + msm_mux_cri_rng0, + msm_mux_cri_rng1, + msm_mux_cri_rng2, + msm_mux_dbg_out_clk, + msm_mux_gcc_plltest_bypassnl, + msm_mux_gcc_plltest_resetn, + msm_mux_gcc_tlmm, + msm_mux_gpio, + msm_mux_mdc_mst, + msm_mux_mdc_slv0, + msm_mux_mdc_slv1, + msm_mux_mdio_mst, + msm_mux_mdio_slv, + msm_mux_mdio_slv0, + msm_mux_mdio_slv1, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie0_wake, + msm_mux_pcie1_clk_req_n, + msm_mux_pcie1_wake, + msm_mux_pcie2_clk_req_n, + msm_mux_pcie2_wake, + msm_mux_pcie3_clk_req_n, + msm_mux_pcie3_wake, + msm_mux_pcie4_clk_req_n, + msm_mux_pcie4_wake, + msm_mux_pll_bist_sync, + msm_mux_pll_test, + msm_mux_pwm, + msm_mux_qdss_cti_trig_in_a0, + msm_mux_qdss_cti_trig_in_a1, + msm_mux_qdss_cti_trig_in_b0, + msm_mux_qdss_cti_trig_in_b1, + msm_mux_qdss_cti_trig_out_a0, + msm_mux_qdss_cti_trig_out_a1, + msm_mux_qdss_cti_trig_out_b0, + msm_mux_qdss_cti_trig_out_b1, + msm_mux_qdss_traceclk_a, + msm_mux_qdss_tracectl_a, + msm_mux_qdss_tracedata_a, + msm_mux_qspi_data, + msm_mux_qspi_clk, + msm_mux_qspi_cs_n, + msm_mux_qup_se0, + msm_mux_qup_se1, + msm_mux_qup_se2, + msm_mux_qup_se3, + msm_mux_qup_se4, + msm_mux_qup_se5, + msm_mux_qup_se6, + msm_mux_qup_se7, + msm_mux_resout, + msm_mux_rx_los0, + msm_mux_rx_los1, + msm_mux_rx_los2, + msm_mux_sdc_clk, + msm_mux_sdc_cmd, + msm_mux_sdc_data, + msm_mux_tsens_max, + msm_mux_tsn, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", + "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", + "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", + "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", + "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", +}; + +static const char *const atest_char_start_groups[] = { + "gpio21", +}; + +static const char *const atest_char_status0_groups[] = { + "gpio33", +}; + +static const char *const atest_char_status1_groups[] = { + "gpio35", +}; + +static const char *const atest_char_status2_groups[] = { + "gpio22", +}; + +static const char *const atest_char_status3_groups[] = { + "gpio23", +}; + +static const char *const atest_tic_en_groups[] = { + "gpio53", +}; + +static const char *const audio_pri_mclk_in0_groups[] = { + "gpio53", +}; + +static const char *const audio_pri_mclk_out0_groups[] = { + "gpio53", +}; + +static const char *const audio_pri_mclk_in1_groups[] = { + "gpio51", +}; + +static const char *const audio_pri_mclk_out1_groups[] = { + "gpio51", +}; + +static const char *const audio_pri_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const audio_sec_mclk_in0_groups[] = { + "gpio37", +}; + +static const char *const audio_sec_mclk_out0_groups[] = { + "gpio37", +}; + +static const char *const audio_sec_mclk_in1_groups[] = { + "gpio37", +}; + +static const char *const audio_sec_mclk_out1_groups[] = { + "gpio37", +}; + +static const char *const audio_sec_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48", +}; + +static const char *const core_voltage_0_groups[] = { + "gpio16", +}; + +static const char *const core_voltage_1_groups[] = { + "gpio17", +}; + +static const char *const core_voltage_2_groups[] = { + "gpio33", +}; + +static const char *const core_voltage_3_groups[] = { + "gpio34", +}; + +static const char *const core_voltage_4_groups[] = { + "gpio35", +}; + +static const char *const cri_rng0_groups[] = { + "gpio6", +}; + +static const char *const cri_rng1_groups[] = { + "gpio7", +}; + +static const char *const cri_rng2_groups[] = { + "gpio8", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio46", +}; + +static const char *const gcc_plltest_bypassnl_groups[] = { + "gpio33", +}; + +static const char *const gcc_plltest_resetn_groups[] = { + "gpio35", +}; + +static const char *const gcc_tlmm_groups[] = { + "gpio34", +}; + +static const char *const mdc_mst_groups[] = { + "gpio22", +}; + +static const char *const mdc_slv0_groups[] = { + "gpio20", +}; + +static const char *const mdc_slv1_groups[] = { + "gpio14", +}; + +static const char *const mdio_mst_groups[] = { + "gpio23", +}; + +static const char *const mdio_slv_groups[] = { + "gpio46", + "gpio47", +}; + +static const char *const mdio_slv0_groups[] = { + "gpio21", +}; + +static const char *const mdio_slv1_groups[] = { + "gpio15", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio24", +}; + +static const char *const pcie0_wake_groups[] = { + "gpio26", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio27", +}; + +static const char *const pcie1_wake_groups[] = { + "gpio29", +}; + +static const char *const pcie2_clk_req_n_groups[] = { + "gpio51", +}; + +static const char *const pcie2_wake_groups[] = { + "gpio53", +}; + +static const char *const pcie3_clk_req_n_groups[] = { + "gpio40", +}; + +static const char *const pcie3_wake_groups[] = { + "gpio42", +}; + +static const char *const pcie4_clk_req_n_groups[] = { + "gpio30", +}; + +static const char *const pcie4_wake_groups[] = { + "gpio32", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio47", +}; + +static const char *const pll_test_groups[] = { + "gpio39", +}; + +static const char *const pwm_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio16", + "gpio17", "gpio33", "gpio34", "gpio35", "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47", "gpio48", +}; + +static const char *const qdss_cti_trig_in_a0_groups[] = { + "gpio53", +}; + +static const char *const qdss_cti_trig_in_a1_groups[] = { + "gpio29", +}; + +static const char *const qdss_cti_trig_in_b0_groups[] = { + "gpio42", +}; + +static const char *const qdss_cti_trig_in_b1_groups[] = { + "gpio43", +}; + +static const char *const qdss_cti_trig_out_a0_groups[] = { + "gpio51", +}; + +static const char *const qdss_cti_trig_out_a1_groups[] = { + "gpio27", +}; + +static const char *const qdss_cti_trig_out_b0_groups[] = { + "gpio40", +}; + +static const char *const qdss_cti_trig_out_b1_groups[] = { + "gpio44", +}; + +static const char *const qdss_traceclk_a_groups[] = { + "gpio45", +}; + +static const char *const qdss_tracectl_a_groups[] = { + "gpio46", +}; + +static const char *const qdss_tracedata_a_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio20", "gpio21", + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const qspi_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qspi_clk_groups[] = { + "gpio5", +}; + +static const char *const qspi_cs_n_groups[] = { + "gpio4", +}; + +static const char *const qup_se0_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio51", "gpio53", +}; + +static const char *const qup_se1_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", "gpio27", "gpio29", +}; + +static const char *const qup_se2_groups[] = { + "gpio27", "gpio29", "gpio33", "gpio34", +}; + +static const char *const qup_se3_groups[] = { + "gpio16", "gpio17", "gpio20", "gpio21", +}; + +static const char *const qup_se4_groups[] = { + "gpio14", "gpio15", "gpio40", "gpio42", "gpio43", "gpio44", +}; + +static const char *const qup_se5_groups[] = { + "gpio40", "gpio42", "gpio45", "gpio46", "gpio47", "gpio48", +}; + +static const char *const qup_se6_groups[] = { + "gpio43", "gpio44", "gpio51", "gpio53", +}; + +static const char *const qup_se7_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const resout_groups[] = { + "gpio49", +}; + +static const char *const rx_los0_groups[] = { + "gpio39", "gpio47", "gpio50", +}; + +static const char *const rx_los1_groups[] = { + "gpio38", "gpio46", +}; + +static const char *const rx_los2_groups[] = { + "gpio37", "gpio45", +}; + +static const char *const sdc_clk_groups[] = { + "gpio5", +}; + +static const char *const sdc_cmd_groups[] = { + "gpio4", +}; + +static const char *const sdc_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const tsens_max_groups[] = { + "gpio14", +}; + +static const char *const tsn_groups[] = { + "gpio50", +}; + +static const struct pinfunction ipq9650_functions[] = { + MSM_PIN_FUNCTION(atest_char_start), + MSM_PIN_FUNCTION(atest_char_status0), + MSM_PIN_FUNCTION(atest_char_status1), + MSM_PIN_FUNCTION(atest_char_status2), + MSM_PIN_FUNCTION(atest_char_status3), + MSM_PIN_FUNCTION(atest_tic_en), + MSM_PIN_FUNCTION(audio_pri_mclk_in0), + MSM_PIN_FUNCTION(audio_pri_mclk_out0), + MSM_PIN_FUNCTION(audio_pri_mclk_in1), + MSM_PIN_FUNCTION(audio_pri_mclk_out1), + MSM_PIN_FUNCTION(audio_pri), + MSM_PIN_FUNCTION(audio_sec), + MSM_PIN_FUNCTION(audio_sec_mclk_in0), + MSM_PIN_FUNCTION(audio_sec_mclk_out0), + MSM_PIN_FUNCTION(audio_sec_mclk_in1), + MSM_PIN_FUNCTION(audio_sec_mclk_out1), + MSM_PIN_FUNCTION(core_voltage_0), + MSM_PIN_FUNCTION(core_voltage_1), + MSM_PIN_FUNCTION(core_voltage_2), + MSM_PIN_FUNCTION(core_voltage_3), + MSM_PIN_FUNCTION(core_voltage_4), + MSM_PIN_FUNCTION(cri_rng0), + MSM_PIN_FUNCTION(cri_rng1), + MSM_PIN_FUNCTION(cri_rng2), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(gcc_plltest_bypassnl), + MSM_PIN_FUNCTION(gcc_plltest_resetn), + MSM_PIN_FUNCTION(gcc_tlmm), + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(mdc_mst), + MSM_PIN_FUNCTION(mdc_slv0), + MSM_PIN_FUNCTION(mdc_slv1), + MSM_PIN_FUNCTION(mdio_mst), + MSM_PIN_FUNCTION(mdio_slv), + MSM_PIN_FUNCTION(mdio_slv0), + MSM_PIN_FUNCTION(mdio_slv1), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie0_wake), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(pcie1_wake), + MSM_PIN_FUNCTION(pcie2_clk_req_n), + MSM_PIN_FUNCTION(pcie2_wake), + MSM_PIN_FUNCTION(pcie3_clk_req_n), + MSM_PIN_FUNCTION(pcie3_wake), + MSM_PIN_FUNCTION(pcie4_clk_req_n), + MSM_PIN_FUNCTION(pcie4_wake), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_test), + MSM_PIN_FUNCTION(pwm), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b1), + MSM_PIN_FUNCTION(qdss_traceclk_a), + MSM_PIN_FUNCTION(qdss_tracectl_a), + MSM_PIN_FUNCTION(qdss_tracedata_a), + MSM_PIN_FUNCTION(qspi_data), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs_n), + MSM_PIN_FUNCTION(qup_se0), + MSM_PIN_FUNCTION(qup_se1), + MSM_PIN_FUNCTION(qup_se2), + MSM_PIN_FUNCTION(qup_se3), + MSM_PIN_FUNCTION(qup_se4), + MSM_PIN_FUNCTION(qup_se5), + MSM_PIN_FUNCTION(qup_se6), + MSM_PIN_FUNCTION(qup_se7), + MSM_PIN_FUNCTION(resout), + MSM_PIN_FUNCTION(rx_los0), + MSM_PIN_FUNCTION(rx_los1), + MSM_PIN_FUNCTION(rx_los2), + MSM_PIN_FUNCTION(sdc_clk), + MSM_PIN_FUNCTION(sdc_cmd), + MSM_PIN_FUNCTION(sdc_data), + MSM_PIN_FUNCTION(tsens_max), + MSM_PIN_FUNCTION(tsn), +}; + +static const struct msm_pingroup ipq9650_groups[] = { + [0] = PINGROUP(0, sdc_data, qspi_data, _, _, _, _, _, _, _), + [1] = PINGROUP(1, sdc_data, qspi_data, _, _, _, _, _, _, _), + [2] = PINGROUP(2, sdc_data, qspi_data, _, _, _, _, _, _, _), + [3] = PINGROUP(3, sdc_data, qspi_data, _, _, _, _, _, _, _), + [4] = PINGROUP(4, sdc_cmd, qspi_cs_n, _, _, _, _, _, _, _), + [5] = PINGROUP(5, sdc_clk, qspi_clk, _, _, _, _, _, _, _), + [6] = PINGROUP(6, qup_se0, pwm, _, cri_rng0, qdss_tracedata_a, _, _, _, _), + [7] = PINGROUP(7, qup_se0, pwm, _, cri_rng1, qdss_tracedata_a, _, _, _, _), + [8] = PINGROUP(8, qup_se0, pwm, _, cri_rng2, qdss_tracedata_a, _, _, _, _), + [9] = PINGROUP(9, qup_se0, pwm, _, qdss_tracedata_a, _, _, _, _, _), + [10] = PINGROUP(10, qup_se1, pwm, _, _, qdss_tracedata_a, _, _, _, _), + [11] = PINGROUP(11, qup_se1, pwm, _, _, qdss_tracedata_a, _, _, _, _), + [12] = PINGROUP(12, qup_se1, _, qdss_tracedata_a, _, _, _, _, _, _), + [13] = PINGROUP(13, qup_se1, _, qdss_tracedata_a, _, _, _, _, _, _), + [14] = PINGROUP(14, qup_se4, mdc_slv1, tsens_max, _, qdss_tracedata_a, _, _, _, _), + [15] = PINGROUP(15, qup_se4, mdio_slv1, _, qdss_tracedata_a, _, _, _, _, _), + [16] = PINGROUP(16, core_voltage_0, qup_se3, pwm, _, _, _, _, _, _), + [17] = PINGROUP(17, core_voltage_1, qup_se3, pwm, _, _, _, _, _, _), + [18] = PINGROUP(18, _, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, _, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, mdc_slv0, qup_se3, _, qdss_tracedata_a, _, _, _, _, _), + [21] = PINGROUP(21, mdio_slv0, qup_se3, atest_char_start, _, qdss_tracedata_a, _, _, _, _), + [22] = PINGROUP(22, mdc_mst, atest_char_status2, _, _, _, _, _, _, _), + [23] = PINGROUP(23, mdio_mst, atest_char_status3, _, _, _, _, _, _, _), + [24] = PINGROUP(24, pcie0_clk_req_n, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, pcie0_wake, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, pcie1_clk_req_n, qup_se2, qup_se1, _, qdss_cti_trig_out_a1, _, _, _, _), + [28] = PINGROUP(28, _, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, pcie1_wake, qup_se2, qup_se1, _, qdss_cti_trig_in_a1, _, _, _, _), + [30] = PINGROUP(30, pcie4_clk_req_n, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, _, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, pcie4_wake, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, core_voltage_2, qup_se2, gcc_plltest_bypassnl, pwm, atest_char_status0, _, _, _, _), + [34] = PINGROUP(34, core_voltage_3, qup_se2, gcc_tlmm, pwm, _, _, _, _, _), + [35] = PINGROUP(35, core_voltage_4, gcc_plltest_resetn, pwm, atest_char_status1, _, _, _, _, _), + [36] = PINGROUP(36, audio_pri, qup_se7, qdss_tracedata_a, _, _, _, _, _, _), + [37] = PINGROUP(37, audio_pri, qup_se7, audio_sec_mclk_out0, audio_sec_mclk_in0, rx_los2, qdss_tracedata_a, _, _, _), + [38] = PINGROUP(38, audio_pri, qup_se7, rx_los1, qdss_tracedata_a, _, _, _, _, _), + [39] = PINGROUP(39, audio_pri, qup_se7, audio_sec_mclk_out1, audio_sec_mclk_in1, pll_test, rx_los0, _, qdss_tracedata_a, _), + [40] = PINGROUP(40, pcie3_clk_req_n, qup_se5, qup_se4, _, qdss_cti_trig_out_b0, _, _, _, _), + [41] = PINGROUP(41, _, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, pcie3_wake, qup_se5, qup_se4, _, qdss_cti_trig_in_b0, _, _, _, _), + [43] = PINGROUP(43, qup_se4, qup_se6, pwm, _, qdss_cti_trig_in_b1, _, _, _, _), + [44] = PINGROUP(44, qup_se4, qup_se6, pwm, _, qdss_cti_trig_out_b1, _, _, _, _), + [45] = PINGROUP(45, qup_se5, rx_los2, audio_sec, pwm, _, qdss_traceclk_a, _, _, _), + [46] = PINGROUP(46, qup_se5, rx_los1, audio_sec, mdio_slv, pwm, dbg_out_clk, qdss_tracectl_a, _, _), + [47] = PINGROUP(47, qup_se5, rx_los0, audio_sec, mdio_slv, pll_bist_sync, pwm, _, _, _), + [48] = PINGROUP(48, qup_se5, audio_sec, pwm, _, _, _, _, _, _), + [49] = PINGROUP(49, resout, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, tsn, rx_los0, _, _, _, _, _, _, _), + [51] = PINGROUP(51, pcie2_clk_req_n, qup_se6, qup_se0, audio_pri_mclk_out1, audio_pri_mclk_in1, qdss_cti_trig_out_a0, _, _, _), + [52] = PINGROUP(52, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, pcie2_wake, qup_se6, qup_se0, audio_pri_mclk_out0, audio_pri_mclk_in0, qdss_cti_trig_in_a0, _, atest_tic_en, _), +}; + +static const struct msm_pinctrl_soc_data ipq9650_tlmm = { + .pins = ipq9650_pins, + .npins = ARRAY_SIZE(ipq9650_pins), + .functions = ipq9650_functions, + .nfunctions = ARRAY_SIZE(ipq9650_functions), + .groups = ipq9650_groups, + .ngroups = ARRAY_SIZE(ipq9650_groups), + .ngpios = 54, +}; + +static const struct of_device_id ipq9650_tlmm_of_match[] = { + { .compatible = "qcom,ipq9650-tlmm", }, + {}, +}; + +static int ipq9650_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &ipq9650_tlmm); +} + +static struct platform_driver ipq9650_tlmm_driver = { + .driver = { + .name = "ipq9650-tlmm", + .of_match_table = ipq9650_tlmm_of_match, + }, + .probe = ipq9650_tlmm_probe, +}; + +static int __init ipq9650_tlmm_init(void) +{ + return platform_driver_register(&ipq9650_tlmm_driver); +} +arch_initcall(ipq9650_tlmm_init); + +static void __exit ipq9650_tlmm_exit(void) +{ + platform_driver_unregister(&ipq9650_tlmm_driver); +} +module_exit(ipq9650_tlmm_exit); + +MODULE_DESCRIPTION("QTI IPQ9650 TLMM driver"); +MODULE_LICENSE("GPL"); From 728bf8ecac7733848a214b95b1aba41e0ee3ffb9 Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Sun, 19 Apr 2026 00:41:24 +0530 Subject: [PATCH 0231/5214] pinctrl: pinconf-generic: Use kmemdup_array() over kmemdup() using kmemdup_array instead of kmemdup ,as it is more readable and matches the intent of the api. tested with w=1, no new warnings introduced. Signed-off-by: Yash Suthar Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf-generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 64ed28309788..d333ddd44298 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -419,7 +419,7 @@ int pinconf_generic_parse_dt_config(struct device_node *np, * Now limit the number of configs to the real number of * found properties. */ - *configs = kmemdup(cfg, ncfg * sizeof(unsigned long), GFP_KERNEL); + *configs = kmemdup_array(cfg, ncfg, sizeof(unsigned long), GFP_KERNEL); if (!*configs) { ret = -ENOMEM; goto out; From 5502ca2ae63ce02f1c1ce03aea3135d606e91060 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 23 Apr 2026 04:43:19 +0000 Subject: [PATCH 0232/5214] dt-bindings: pinctrl: qcom,eliza-tlmm: Split QUP lane mirror alternates Several QUP lanes have MIRA/MIRB mirror routings that let the same lane be muxed out on alternative GPIOs. On Eliza these were all collapsed under the base function name (e.g. qup1_se6), which prevented boards from selecting the mirror variants. Add explicit function names for each mirror lane, matching the pattern already established by qcom,sm8550-tlmm and related bindings. Signed-off-by: Alexander Koskovich Acked-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- .../bindings/pinctrl/qcom,eliza-tlmm.yaml | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml index 282650426487..be7b4680045f 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml @@ -86,16 +86,21 @@ $defs: qdss_gpio_tracectl, qdss_gpio_tracedata, qlink_big_enable, qlink_big_request, qlink_little_enable, qlink_little_request, qlink_wmss, qspi0, qspi_clk, - qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se3, qup1_se4, - qup1_se5, qup1_se6, qup1_se7, qup2_se0, qup2_se1, - qup2_se2, qup2_se3, qup2_se4, qup2_se5, qup2_se6, - qup2_se7, resout_gpio, sd_write_protect, sdc1, sdc2, - sdc2_fb_clk, tb_trig_sdc1, tb_trig_sdc2, tmess_prng0, - tmess_prng1, tmess_prng2, tmess_prng3, tsense_pwm1, - tsense_pwm2, tsense_pwm3, tsense_pwm4, uim0_clk, - uim0_data, uim0_present, uim0_reset, uim1_clk, uim1_data, - uim1_present, uim1_reset, usb0_hs, usb_phy, vfr_0, vfr_1, - vsense_trigger_mirnat, wcn_sw_ctrl ] + qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se2_l2_mira, + qup1_se2_l2_mirb, qup1_se2_l3_mira, qup1_se2_l3_mirb, + qup1_se3, qup1_se4, qup1_se5, qup1_se6, qup1_se6_l1_mira, + qup1_se6_l1_mirb, qup1_se6_l3_mira, qup1_se6_l3_mirb, + qup1_se7, qup1_se7_l0_mira, qup1_se7_l0_mirb, + qup1_se7_l1_mira, qup1_se7_l1_mirb, qup2_se0, qup2_se1, + qup2_se2, qup2_se3, qup2_se3_l0_mira, qup2_se3_l0_mirb, + qup2_se3_l1_mira, qup2_se3_l1_mirb, qup2_se4, qup2_se5, + qup2_se6, qup2_se7, resout_gpio, sd_write_protect, sdc1, + sdc2, sdc2_fb_clk, tb_trig_sdc1, tb_trig_sdc2, + tmess_prng0, tmess_prng1, tmess_prng2, tmess_prng3, + tsense_pwm1, tsense_pwm2, tsense_pwm3, tsense_pwm4, + uim0_clk, uim0_data, uim0_present, uim0_reset, uim1_clk, + uim1_data, uim1_present, uim1_reset, usb0_hs, usb_phy, + vfr_0, vfr_1, vsense_trigger_mirnat, wcn_sw_ctrl ] required: - pins From 1bd5c56253c534840382d01f80e6a6a40ff01dd6 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 23 Apr 2026 04:43:27 +0000 Subject: [PATCH 0233/5214] dt-bindings: pinctrl: qcom,eliza-tlmm: Split QUP1_SE4 lanes QUP1_SE4 shares GPIO_36 & GPIO_37 for both L0/L1 and L3/L2 so the function name cannot be the same or the alternate function cannot be selected. Split them up into individual lane functions so boards can specify. Signed-off-by: Alexander Koskovich Acked-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml index be7b4680045f..fa0177529277 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml @@ -88,7 +88,8 @@ $defs: qlink_little_request, qlink_wmss, qspi0, qspi_clk, qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se2_l2_mira, qup1_se2_l2_mirb, qup1_se2_l3_mira, qup1_se2_l3_mirb, - qup1_se3, qup1_se4, qup1_se5, qup1_se6, qup1_se6_l1_mira, + qup1_se3, qup1_se4_l0, qup1_se4_l1, qup1_se4_l2, + qup1_se4_l3, qup1_se5, qup1_se6, qup1_se6_l1_mira, qup1_se6_l1_mirb, qup1_se6_l3_mira, qup1_se6_l3_mirb, qup1_se7, qup1_se7_l0_mira, qup1_se7_l0_mirb, qup1_se7_l1_mira, qup1_se7_l1_mirb, qup2_se0, qup2_se1, From cef1554c4f7dff8ac3a542b89c2c83afdf734d23 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 23 Apr 2026 04:43:37 +0000 Subject: [PATCH 0234/5214] pinctrl: qcom: eliza: Split QUP lane mirror alternates Several QUP lanes have MIRA/MIRB mirror routings which are collapsed under a single function name (e.g. qup1_se6). This is an issue because it means there are multiple functions defined for a given pin that share the same name: [42] = PINGROUP(42, qup1_se6, qup1_se2, qup1_se6... So when you select pin 42 and request function qup1_se6, it will select the first instance of it in this group, which just happens to be QUP1_SE6_L2, making the second instance (QUP1_SE6_L1_MIRA) effectively unreachable. Split each of these lanes that has an alternative GPIO into their own function so they can actually be selected, following the pattern seen in pinctrl-sm8550.c. Signed-off-by: Alexander Koskovich Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-eliza.c | 132 +++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 18 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index c1f756cbcdeb..8f74756771b8 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -563,15 +563,31 @@ enum eliza_functions { msm_mux_qup1_se0, msm_mux_qup1_se1, msm_mux_qup1_se2, + msm_mux_qup1_se2_l2_mira, + msm_mux_qup1_se2_l2_mirb, + msm_mux_qup1_se2_l3_mira, + msm_mux_qup1_se2_l3_mirb, msm_mux_qup1_se3, msm_mux_qup1_se4, msm_mux_qup1_se5, msm_mux_qup1_se6, + msm_mux_qup1_se6_l1_mira, + msm_mux_qup1_se6_l1_mirb, + msm_mux_qup1_se6_l3_mira, + msm_mux_qup1_se6_l3_mirb, msm_mux_qup1_se7, + msm_mux_qup1_se7_l0_mira, + msm_mux_qup1_se7_l0_mirb, + msm_mux_qup1_se7_l1_mira, + msm_mux_qup1_se7_l1_mirb, msm_mux_qup2_se0, msm_mux_qup2_se1, msm_mux_qup2_se2, msm_mux_qup2_se3, + msm_mux_qup2_se3_l0_mira, + msm_mux_qup2_se3_l0_mirb, + msm_mux_qup2_se3_l1_mira, + msm_mux_qup2_se3_l1_mirb, msm_mux_qup2_se4, msm_mux_qup2_se5, msm_mux_qup2_se6, @@ -978,7 +994,23 @@ static const char *const qup1_se1_groups[] = { }; static const char *const qup1_se2_groups[] = { - "gpio52", "gpio53", "gpio54", "gpio52", "gpio55", "gpio53", "gpio40", "gpio42", "gpio30", + "gpio52", "gpio53", "gpio40", "gpio42", "gpio30", +}; + +static const char *const qup1_se2_l2_mira_groups[] = { + "gpio54", +}; + +static const char *const qup1_se2_l2_mirb_groups[] = { + "gpio52", +}; + +static const char *const qup1_se2_l3_mira_groups[] = { + "gpio55", +}; + +static const char *const qup1_se2_l3_mirb_groups[] = { + "gpio53", }; static const char *const qup1_se3_groups[] = { @@ -994,11 +1026,43 @@ static const char *const qup1_se5_groups[] = { }; static const char *const qup1_se6_groups[] = { - "gpio40", "gpio42", "gpio54", "gpio42", "gpio40", "gpio55", + "gpio40", "gpio42", +}; + +static const char *const qup1_se6_l1_mira_groups[] = { + "gpio42", +}; + +static const char *const qup1_se6_l1_mirb_groups[] = { + "gpio54", +}; + +static const char *const qup1_se6_l3_mira_groups[] = { + "gpio40", +}; + +static const char *const qup1_se6_l3_mirb_groups[] = { + "gpio55", }; static const char *const qup1_se7_groups[] = { - "gpio81", "gpio78", "gpio80", "gpio114", "gpio114", "gpio78", + "gpio78", "gpio114", +}; + +static const char *const qup1_se7_l0_mira_groups[] = { + "gpio81", +}; + +static const char *const qup1_se7_l0_mirb_groups[] = { + "gpio78", +}; + +static const char *const qup1_se7_l1_mira_groups[] = { + "gpio80", +}; + +static const char *const qup1_se7_l1_mirb_groups[] = { + "gpio114", }; static const char *const qup2_se0_groups[] = { @@ -1014,7 +1078,23 @@ static const char *const qup2_se2_groups[] = { }; static const char *const qup2_se3_groups[] = { - "gpio79", "gpio116", "gpio97", "gpio100", "gpio100", "gpio116", + "gpio100", "gpio116", +}; + +static const char *const qup2_se3_l0_mira_groups[] = { + "gpio79", +}; + +static const char *const qup2_se3_l0_mirb_groups[] = { + "gpio116", +}; + +static const char *const qup2_se3_l1_mira_groups[] = { + "gpio97", +}; + +static const char *const qup2_se3_l1_mirb_groups[] = { + "gpio100", }; static const char *const qup2_se4_groups[] = { @@ -1236,15 +1316,31 @@ static const struct pinfunction eliza_functions[] = { MSM_PIN_FUNCTION(qup1_se0), MSM_PIN_FUNCTION(qup1_se1), MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se2_l2_mira), + MSM_PIN_FUNCTION(qup1_se2_l2_mirb), + MSM_PIN_FUNCTION(qup1_se2_l3_mira), + MSM_PIN_FUNCTION(qup1_se2_l3_mirb), MSM_PIN_FUNCTION(qup1_se3), MSM_PIN_FUNCTION(qup1_se4), MSM_PIN_FUNCTION(qup1_se5), MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se6_l1_mira), + MSM_PIN_FUNCTION(qup1_se6_l1_mirb), + MSM_PIN_FUNCTION(qup1_se6_l3_mira), + MSM_PIN_FUNCTION(qup1_se6_l3_mirb), MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup1_se7_l0_mira), + MSM_PIN_FUNCTION(qup1_se7_l0_mirb), + MSM_PIN_FUNCTION(qup1_se7_l1_mira), + MSM_PIN_FUNCTION(qup1_se7_l1_mirb), MSM_PIN_FUNCTION(qup2_se0), MSM_PIN_FUNCTION(qup2_se1), MSM_PIN_FUNCTION(qup2_se2), MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se3_l0_mira), + MSM_PIN_FUNCTION(qup2_se3_l0_mirb), + MSM_PIN_FUNCTION(qup2_se3_l1_mira), + MSM_PIN_FUNCTION(qup2_se3_l1_mirb), MSM_PIN_FUNCTION(qup2_se4), MSM_PIN_FUNCTION(qup2_se5), MSM_PIN_FUNCTION(qup2_se6), @@ -1326,9 +1422,9 @@ static const struct msm_pingroup eliza_groups[] = { [37] = PINGROUP(37, qup1_se4, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), [38] = PINGROUP(38, _, _, _, _, _, _, _, _, _, _, _), [39] = PINGROUP(39, _, _, _, _, _, _, _, _, _, _, _), - [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), + [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6_l3_mira, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), [41] = PINGROUP(41, _, _, _, _, _, _, _, _, _, _, _), - [42] = PINGROUP(42, qup1_se6, qup1_se2, qup1_se6, qdss_gpio_tracedata, gnss_adc0, ddr_pxi1, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se6, qup1_se2, qup1_se6_l1_mira, qdss_gpio_tracedata, gnss_adc0, ddr_pxi1, _, _, _, _, _), [43] = PINGROUP(43, _, _, _, _, _, _, _, _, _, _, _), [44] = PINGROUP(44, qup1_se3, _, _, _, _, _, _, _, _, _, _), [45] = PINGROUP(45, qup1_se3, _, _, _, _, _, _, _, _, _, _), @@ -1338,10 +1434,10 @@ static const struct msm_pingroup eliza_groups[] = { [49] = PINGROUP(49, _, _, _, _, _, _, _, _, _, _, _), [50] = PINGROUP(50, sdc2_fb_clk, _, _, _, _, _, _, _, _, _, _), [51] = PINGROUP(51, _, _, _, _, _, _, _, _, _, _, _), - [52] = PINGROUP(52, qup1_se2, pcie1_clk_req_n, qup1_se2, ddr_bist_complete, qdss_gpio_tracedata, _, vsense_trigger_mirnat, _, _, _, _), - [53] = PINGROUP(53, qup1_se2, qup1_se2, gcc_gp1, ddr_bist_stop, _, qdss_gpio_tracedata, _, _, _, _, _), - [54] = PINGROUP(54, qup1_se2, qup1_se6, qdss_gpio_tracedata, gnss_adc1, atest_usb, ddr_pxi0, _, _, _, _, _), - [55] = PINGROUP(55, qup1_se2, dp0_hot, qup1_se6, _, gnss_adc0, atest_usb, ddr_pxi0, _, _, _, _), + [52] = PINGROUP(52, qup1_se2, pcie1_clk_req_n, qup1_se2_l2_mirb, ddr_bist_complete, qdss_gpio_tracedata, _, vsense_trigger_mirnat, _, _, _, _), + [53] = PINGROUP(53, qup1_se2, qup1_se2_l3_mirb, gcc_gp1, ddr_bist_stop, _, qdss_gpio_tracedata, _, _, _, _, _), + [54] = PINGROUP(54, qup1_se2_l2_mira, qup1_se6_l1_mirb, qdss_gpio_tracedata, gnss_adc1, atest_usb, ddr_pxi0, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se2_l3_mira, dp0_hot, qup1_se6_l3_mirb, _, gnss_adc0, atest_usb, ddr_pxi0, _, _, _, _), [56] = PINGROUP(56, usb0_hs, tsense_pwm1, tsense_pwm2, tsense_pwm3, tsense_pwm4, _, _, _, _, _, _), [57] = PINGROUP(57, sd_write_protect, _, _, _, _, _, _, _, _, _, _), [58] = PINGROUP(58, _, _, _, _, _, _, _, _, _, _, _), @@ -1364,10 +1460,10 @@ static const struct msm_pingroup eliza_groups[] = { [75] = PINGROUP(75, cci_i2c_scl, _, phase_flag, _, _, _, _, _, _, _, _), [76] = PINGROUP(76, cci_i2c_sda, cci_timer, prng_rosc2, _, phase_flag, _, _, _, _, _, _), [77] = PINGROUP(77, cci_i2c_scl, jitter_bist, _, _, _, _, _, _, _, _, _), - [78] = PINGROUP(78, qup1_se7, qup1_se7, _, phase_flag, _, _, _, _, _, _, _), - [79] = PINGROUP(79, qspi0, mdp_vsync, qup2_se3, _, _, _, _, _, _, _, _), - [80] = PINGROUP(80, pcie0_clk_req_n, qup1_se7, _, phase_flag, _, _, _, _, _, _, _), - [81] = PINGROUP(81, wcn_sw_ctrl, qup1_se7, dbg_out_clk, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, qup1_se7, qup1_se7_l0_mirb, _, phase_flag, _, _, _, _, _, _, _), + [79] = PINGROUP(79, qspi0, mdp_vsync, qup2_se3_l0_mira, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, pcie0_clk_req_n, qup1_se7_l1_mira, _, phase_flag, _, _, _, _, _, _, _), + [81] = PINGROUP(81, wcn_sw_ctrl, qup1_se7_l0_mira, dbg_out_clk, _, _, _, _, _, _, _, _), [82] = PINGROUP(82, _, _, _, _, _, _, _, _, _, _, _), [83] = PINGROUP(83, _, _, _, _, _, _, _, _, _, _, _), [84] = PINGROUP(84, uim0_data, _, _, _, _, _, _, _, _, _, _), @@ -1383,10 +1479,10 @@ static const struct msm_pingroup eliza_groups[] = { [94] = PINGROUP(94, qlink_wmss, _, _, _, _, _, _, _, _, _, _), [95] = PINGROUP(95, qlink_big_request, _, _, _, _, _, _, _, _, _, _), [96] = PINGROUP(96, qlink_big_enable, _, _, _, _, _, _, _, _, _, _), - [97] = PINGROUP(97, uim1_data, qspi0, qup2_se3, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, uim1_data, qspi0, qup2_se3_l1_mira, _, _, _, _, _, _, _, _), [98] = PINGROUP(98, uim1_clk, qspi0, _, _, _, _, _, _, _, _, _), [99] = PINGROUP(99, uim1_reset, qspi0, _, _, _, _, _, _, _, _, _), - [100] = PINGROUP(100, uim1_present, qspi0, qup2_se3, coex_uart2_tx, qup2_se3, mdp_vsync, _, _, _, _, _), + [100] = PINGROUP(100, uim1_present, qspi0, qup2_se3, coex_uart2_tx, qup2_se3_l1_mirb, mdp_vsync, _, _, _, _, _), [101] = PINGROUP(101, _, _, _, _, _, _, _, _, _, _, _), [102] = PINGROUP(102, _, _, _, _, _, _, _, _, _, _, _), [103] = PINGROUP(103, _, _, _, _, _, _, _, _, _, _, _), @@ -1400,9 +1496,9 @@ static const struct msm_pingroup eliza_groups[] = { [111] = PINGROUP(111, coex_uart1_tx, _, _, _, _, _, _, _, _, _, _), [112] = PINGROUP(112, coex_uart1_rx, _, _, _, _, _, _, _, _, _, _), [113] = PINGROUP(113, _, nav_gpio3, _, _, _, _, _, _, _, _, _), - [114] = PINGROUP(114, qup1_se7, qup1_se7, _, qdss_gpio_tracedata, _, _, _, _, _, _, _), + [114] = PINGROUP(114, qup1_se7, qup1_se7_l1_mirb, _, qdss_gpio_tracedata, _, _, _, _, _, _, _), [115] = PINGROUP(115, _, qspi0, cci_async_in, _, _, _, _, _, _, _, _), - [116] = PINGROUP(116, qspi0, coex_uart2_rx, qup2_se3, qup2_se3, _, _, _, _, _, _, _), + [116] = PINGROUP(116, qspi0, coex_uart2_rx, qup2_se3, qup2_se3_l0_mirb, _, _, _, _, _, _, _), [117] = PINGROUP(117, nav_gpio1, _, vfr_1, _, _, _, _, _, _, _, _), [118] = PINGROUP(118, nav_gpio2, _, _, _, _, _, _, _, _, _, _), [119] = PINGROUP(119, nav_gpio0, _, _, _, _, _, _, _, _, _, _), From 4f5b1f4e770b959dfef196f0e00297043b03cf72 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 23 Apr 2026 04:43:46 +0000 Subject: [PATCH 0235/5214] pinctrl: qcom: eliza: Split QUP1_SE4 lanes QUP1_SE4 shares GPIO_36 & GPIO_37 for both L0/L1 and L3/L2 so the function name cannot be the same or the alternate function cannot be selected. Split them up into individual lane functions so boards can specify. Signed-off-by: Alexander Koskovich Reviewed-by: Abel Vesa Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-eliza.c | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index 8f74756771b8..40e263e35b45 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -568,7 +568,10 @@ enum eliza_functions { msm_mux_qup1_se2_l3_mira, msm_mux_qup1_se2_l3_mirb, msm_mux_qup1_se3, - msm_mux_qup1_se4, + msm_mux_qup1_se4_l0, + msm_mux_qup1_se4_l1, + msm_mux_qup1_se4_l2, + msm_mux_qup1_se4_l3, msm_mux_qup1_se5, msm_mux_qup1_se6, msm_mux_qup1_se6_l1_mira, @@ -1017,8 +1020,20 @@ static const char *const qup1_se3_groups[] = { "gpio44", "gpio45", "gpio46", "gpio47", }; -static const char *const qup1_se4_groups[] = { - "gpio36", "gpio37", "gpio37", "gpio36", +static const char *const qup1_se4_l0_groups[] = { + "gpio36", +}; + +static const char *const qup1_se4_l1_groups[] = { + "gpio37", +}; + +static const char *const qup1_se4_l2_groups[] = { + "gpio37", +}; + +static const char *const qup1_se4_l3_groups[] = { + "gpio36", }; static const char *const qup1_se5_groups[] = { @@ -1321,7 +1336,10 @@ static const struct pinfunction eliza_functions[] = { MSM_PIN_FUNCTION(qup1_se2_l3_mira), MSM_PIN_FUNCTION(qup1_se2_l3_mirb), MSM_PIN_FUNCTION(qup1_se3), - MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se4_l0), + MSM_PIN_FUNCTION(qup1_se4_l1), + MSM_PIN_FUNCTION(qup1_se4_l2), + MSM_PIN_FUNCTION(qup1_se4_l3), MSM_PIN_FUNCTION(qup1_se5), MSM_PIN_FUNCTION(qup1_se6), MSM_PIN_FUNCTION(qup1_se6_l1_mira), @@ -1418,8 +1436,8 @@ static const struct msm_pingroup eliza_groups[] = { [33] = PINGROUP(33, qup1_se1, ibi_i3c, host2wlan_sol, gcc_gp3, _, _, _, _, _, _, _), [34] = PINGROUP(34, qup1_se1, qup1_se5, tb_trig_sdc1, ddr_bist_start, qdss_gpio_tracedata, _, _, _, _, _, _), [35] = PINGROUP(35, qup1_se1, qup1_se5, tb_trig_sdc2, gcc_gp2, qdss_gpio_tracedata, _, _, _, _, _, _), - [36] = PINGROUP(36, qup1_se4, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), - [37] = PINGROUP(37, qup1_se4, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se4_l0, qup1_se4_l3, ibi_i3c, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se4_l1, qup1_se4_l2, ibi_i3c, _, _, _, _, _, _, _, _), [38] = PINGROUP(38, _, _, _, _, _, _, _, _, _, _, _), [39] = PINGROUP(39, _, _, _, _, _, _, _, _, _, _, _), [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6_l3_mira, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), From 352f5621b8c472bf328f452446bce85c00837223 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 20 Apr 2026 18:55:55 +0800 Subject: [PATCH 0236/5214] pinctrl: pinconf-generic: fix properties bitmap leak in parse_fw_cfg() In parse_fw_cfg(), if fwnode_property_match_property_string() fails with -ENOENT, the code returns directly and leaks the bitmap. Use __free(bitmap) for automatic cleanup to fix the leak. Fixes: 9c105255108b ("pinctrl: pinconf-generic: perform basic checks on pincfg properties") Signed-off-by: Felix Gu Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf-generic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index d333ddd44298..95c803c40a7d 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -225,10 +225,9 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, unsigned int count, unsigned long *cfg, unsigned int *ncfg) { - unsigned long *properties; int i, test; - properties = bitmap_zalloc(count, GFP_KERNEL); + unsigned long *properties __free(bitmap) = bitmap_zalloc(count, GFP_KERNEL); for (i = 0; i < count; i++) { u32 val; @@ -263,7 +262,6 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, if (ret) { pr_err("%pfw: conflicting setting detected for %s\n", fwnode, par->property); - bitmap_free(properties); return -EINVAL; } } @@ -295,7 +293,6 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode, pr_err("%pfw: cannot have multiple drive configurations\n", fwnode); - bitmap_free(properties); return 0; } From b31617184621fe6121fd4e9b19eea52ab3cdce10 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Apr 2026 10:33:46 +0200 Subject: [PATCH 0237/5214] pinctrl: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski [linusw@kernel.org: Also fix the new IPQ9650] Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 24 ++++----- drivers/pinctrl/qcom/Kconfig.msm | 86 ++++++++++++++++---------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 80af372a1147..3accf0b489bb 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -49,7 +49,7 @@ config PINCTRL_QCOM_SSBI_PMIC devices are pm8058 and pm8921. config PINCTRL_LPASS_LPI - tristate "Qualcomm Technologies Inc LPASS LPI pin controller driver" + tristate "Qualcomm LPASS LPI pin controller driver" select PINMUX select PINCONF select GENERIC_PINCONF @@ -61,7 +61,7 @@ config PINCTRL_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SoCs. config PINCTRL_MILOS_LPASS_LPI - tristate "Qualcomm Technologies Inc Milos LPASS LPI pin controller driver" + tristate "Qualcomm Milos LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -71,7 +71,7 @@ config PINCTRL_MILOS_LPASS_LPI platform. config PINCTRL_SC7280_LPASS_LPI - tristate "Qualcomm Technologies Inc SC7280 and SM8350 LPASS LPI pin controller driver" + tristate "Qualcomm SC7280 and SM8350 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -81,7 +81,7 @@ config PINCTRL_SC7280_LPASS_LPI and SM8350 platforms. config PINCTRL_SDM660_LPASS_LPI - tristate "Qualcomm Technologies Inc SDM660 LPASS LPI pin controller driver" + tristate "Qualcomm SDM660 LPASS LPI pin controller driver" depends on GPIOLIB depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI @@ -91,7 +91,7 @@ config PINCTRL_SDM660_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SDM660 platform. config PINCTRL_SM4250_LPASS_LPI - tristate "Qualcomm Technologies Inc SM4250 LPASS LPI pin controller driver" + tristate "Qualcomm SM4250 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -100,7 +100,7 @@ config PINCTRL_SM4250_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SM4250 platform. config PINCTRL_SDM670_LPASS_LPI - tristate "Qualcomm Technologies Inc SDM670 LPASS LPI pin controller driver" + tristate "Qualcomm SDM670 LPASS LPI pin controller driver" depends on GPIOLIB depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI @@ -110,7 +110,7 @@ config PINCTRL_SDM670_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SDM670 platform. config PINCTRL_SM6115_LPASS_LPI - tristate "Qualcomm Technologies Inc SM6115 LPASS LPI pin controller driver" + tristate "Qualcomm SM6115 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -119,7 +119,7 @@ config PINCTRL_SM6115_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SM6115 platform. config PINCTRL_SM8250_LPASS_LPI - tristate "Qualcomm Technologies Inc SM8250 LPASS LPI pin controller driver" + tristate "Qualcomm SM8250 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -128,7 +128,7 @@ config PINCTRL_SM8250_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SM8250 platform. config PINCTRL_SM8450_LPASS_LPI - tristate "Qualcomm Technologies Inc SM8450 LPASS LPI pin controller driver" + tristate "Qualcomm SM8450 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -137,7 +137,7 @@ config PINCTRL_SM8450_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SM8450 platform. config PINCTRL_SC8280XP_LPASS_LPI - tristate "Qualcomm Technologies Inc SC8280XP LPASS LPI pin controller driver" + tristate "Qualcomm SC8280XP LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -146,7 +146,7 @@ config PINCTRL_SC8280XP_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SC8280XP platform. config PINCTRL_SM8550_LPASS_LPI - tristate "Qualcomm Technologies Inc SM8550 LPASS LPI pin controller driver" + tristate "Qualcomm SM8550 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help @@ -156,7 +156,7 @@ config PINCTRL_SM8550_LPASS_LPI platform. config PINCTRL_SM8650_LPASS_LPI - tristate "Qualcomm Technologies Inc SM8650 LPASS LPI pin controller driver" + tristate "Qualcomm SM8650 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST depends on PINCTRL_LPASS_LPI help diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 0d6f698e26ec..b78d6410ed2e 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -16,7 +16,7 @@ config PINCTRL_APQ8084 Qualcomm TLMM block found in the Qualcomm APQ8084 platform. config PINCTRL_ELIZA - tristate "Qualcomm Technologies Inc Eliza pin controller driver" + tristate "Qualcomm Eliza pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -26,7 +26,7 @@ config PINCTRL_ELIZA If unsure, say N. config PINCTRL_GLYMUR - tristate "Qualcomm Technologies Inc Glymur pin controller driver" + tristate "Qualcomm Glymur pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -36,7 +36,7 @@ config PINCTRL_GLYMUR If unsure, say N. config PINCTRL_HAWI - tristate "Qualcomm Technologies Inc Hawi pin controller driver" + tristate "Qualcomm Hawi pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -53,7 +53,7 @@ config PINCTRL_IPQ4019 Qualcomm TLMM block found in the Qualcomm IPQ4019 platform. config PINCTRL_IPQ5018 - tristate "Qualcomm Technologies, Inc. IPQ5018 pin controller driver" + tristate "Qualcomm IPQ5018 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -69,7 +69,7 @@ config PINCTRL_IPQ8064 Qualcomm TLMM block found in the Qualcomm IPQ8064 platform. config PINCTRL_IPQ5210 - tristate "Qualcomm Technologies Inc IPQ5210 pin controller driver" + tristate "Qualcomm IPQ5210 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -77,7 +77,7 @@ config PINCTRL_IPQ5210 Technologies Inc IPQ5210 platform. config PINCTRL_IPQ5332 - tristate "Qualcomm Technologies Inc IPQ5332 pin controller driver" + tristate "Qualcomm IPQ5332 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -85,7 +85,7 @@ config PINCTRL_IPQ5332 Technologies Inc IPQ5332 platform. config PINCTRL_IPQ5424 - tristate "Qualcomm Technologies, Inc. IPQ5424 pin controller driver" + tristate "Qualcomm IPQ5424 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -94,7 +94,7 @@ config PINCTRL_IPQ5424 IPQ5424. config PINCTRL_IPQ8074 - tristate "Qualcomm Technologies, Inc. IPQ8074 pin controller driver" + tristate "Qualcomm IPQ8074 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -103,7 +103,7 @@ config PINCTRL_IPQ8074 IPQ8074. config PINCTRL_IPQ6018 - tristate "Qualcomm Technologies, Inc. IPQ6018 pin controller driver" + tristate "Qualcomm IPQ6018 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -112,7 +112,7 @@ config PINCTRL_IPQ6018 IPQ6018. config PINCTRL_IPQ9574 - tristate "Qualcomm Technologies, Inc. IPQ9574 pin controller driver" + tristate "Qualcomm IPQ9574 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -121,7 +121,7 @@ config PINCTRL_IPQ9574 IPQ9574. config PINCTRL_IPQ9650 - tristate "Qualcomm Technologies, Inc. IPQ9650 pin controller driver" + tristate "Qualcomm IPQ9650 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for @@ -130,7 +130,7 @@ config PINCTRL_IPQ9650 IPQ9650. config PINCTRL_KAANAPALI - tristate "Qualcomm Technologies Inc Kaanapali pin controller driver" + tristate "Qualcomm Kaanapali pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -253,28 +253,28 @@ config PINCTRL_QCS404 TLMM block found in the Qualcomm QCS404 platform. config PINCTRL_QCS615 - tristate "Qualcomm Technologies QCS615 pin controller driver" + tristate "Qualcomm QCS615 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the TLMM block found on the Qualcomm QCS615 platform. config PINCTRL_QCS8300 - tristate "Qualcomm Technologies QCS8300 pin controller driver" + tristate "Qualcomm QCS8300 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux and pinconf driver for the Qualcomm TLMM block found on the Qualcomm QCS8300 platform. config PINCTRL_QDF2XXX - tristate "Qualcomm Technologies QDF2xxx pin controller driver" + tristate "Qualcomm QDF2xxx pin controller driver" depends on ACPI help This is the GPIO driver for the TLMM block found on the Qualcomm Technologies QDF2xxx SOCs. config PINCTRL_QDU1000 - tristate "Qualcomm Technologies Inc QDU1000/QRU1000 pin controller driver" + tristate "Qualcomm QDU1000/QRU1000 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf, and gpiolib driver for the @@ -282,14 +282,14 @@ config PINCTRL_QDU1000 Technologies Inc QDU1000 and QRU1000 platforms. config PINCTRL_SA8775P - tristate "Qualcomm Technologies Inc SA8775P pin controller driver" + tristate "Qualcomm SA8775P pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux and pinconf driver for the Qualcomm TLMM block found on the Qualcomm SA8775P platforms. config PINCTRL_SAR2130P - tristate "Qualcomm Technologies Inc SAR2130P pin controller driver" + tristate "Qualcomm SAR2130P pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -297,7 +297,7 @@ config PINCTRL_SAR2130P Technologies Inc SAR2130P platform. config PINCTRL_SC7180 - tristate "Qualcomm Technologies Inc SC7180 pin controller driver" + tristate "Qualcomm SC7180 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -305,7 +305,7 @@ config PINCTRL_SC7180 Technologies Inc SC7180 platform. config PINCTRL_SC7280 - tristate "Qualcomm Technologies Inc SC7280 pin controller driver" + tristate "Qualcomm SC7280 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -313,7 +313,7 @@ config PINCTRL_SC7280 Technologies Inc SC7280 platform. config PINCTRL_SC8180X - tristate "Qualcomm Technologies Inc SC8180x pin controller driver" + tristate "Qualcomm SC8180x pin controller driver" depends on (OF || ACPI) depends on ARM64 || COMPILE_TEST help @@ -322,7 +322,7 @@ config PINCTRL_SC8180X Technologies Inc SC8180x platform. config PINCTRL_SC8280XP - tristate "Qualcomm Technologies Inc SC8280xp pin controller driver" + tristate "Qualcomm SC8280xp pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -330,7 +330,7 @@ config PINCTRL_SC8280XP Technologies Inc SC8280xp platform. config PINCTRL_SDM660 - tristate "Qualcomm Technologies Inc SDM660 pin controller driver" + tristate "Qualcomm SDM660 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -338,7 +338,7 @@ config PINCTRL_SDM660 Technologies Inc SDM660 platform. config PINCTRL_SDM670 - tristate "Qualcomm Technologies Inc SDM670 pin controller driver" + tristate "Qualcomm SDM670 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -346,7 +346,7 @@ config PINCTRL_SDM670 Technologies Inc SDM670 platform. config PINCTRL_SDM845 - tristate "Qualcomm Technologies Inc SDM845 pin controller driver" + tristate "Qualcomm SDM845 pin controller driver" depends on (OF || ACPI) depends on ARM64 || COMPILE_TEST help @@ -355,7 +355,7 @@ config PINCTRL_SDM845 Technologies Inc SDM845 platform. config PINCTRL_SDX55 - tristate "Qualcomm Technologies Inc SDX55 pin controller driver" + tristate "Qualcomm SDX55 pin controller driver" depends on ARM || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -363,7 +363,7 @@ config PINCTRL_SDX55 Technologies Inc SDX55 platform. config PINCTRL_SDX65 - tristate "Qualcomm Technologies Inc SDX65 pin controller driver" + tristate "Qualcomm SDX65 pin controller driver" depends on ARM || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -371,7 +371,7 @@ config PINCTRL_SDX65 Technologies Inc SDX65 platform. config PINCTRL_SDX75 - tristate "Qualcomm Technologies Inc SDX75 pin controller driver" + tristate "Qualcomm SDX75 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -379,7 +379,7 @@ config PINCTRL_SDX75 Technologies Inc SDX75 platform. config PINCTRL_SM4450 - tristate "Qualcomm Technologies Inc SM4450 pin controller driver" + tristate "Qualcomm SM4450 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -387,7 +387,7 @@ config PINCTRL_SM4450 Technologies Inc SM4450 platform. config PINCTRL_SM6115 - tristate "Qualcomm Technologies Inc SM6115,SM4250 pin controller driver" + tristate "Qualcomm SM6115,SM4250 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -395,7 +395,7 @@ config PINCTRL_SM6115 Technologies Inc SM6115 and SM4250 platforms. config PINCTRL_SM6125 - tristate "Qualcomm Technologies Inc SM6125 pin controller driver" + tristate "Qualcomm SM6125 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -403,7 +403,7 @@ config PINCTRL_SM6125 Technologies Inc SM6125 platform. config PINCTRL_SM6350 - tristate "Qualcomm Technologies Inc SM6350 pin controller driver" + tristate "Qualcomm SM6350 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -411,7 +411,7 @@ config PINCTRL_SM6350 Technologies Inc SM6350 platform. config PINCTRL_SM6375 - tristate "Qualcomm Technologies Inc SM6375 pin controller driver" + tristate "Qualcomm SM6375 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -419,7 +419,7 @@ config PINCTRL_SM6375 Technologies Inc SM6375 platform. config PINCTRL_SM7150 - tristate "Qualcomm Technologies Inc SM7150 pin controller driver" + tristate "Qualcomm SM7150 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -427,7 +427,7 @@ config PINCTRL_SM7150 Technologies Inc SM7150 platform. config PINCTRL_MILOS - tristate "Qualcomm Technologies Inc Milos pin controller driver" + tristate "Qualcomm Milos pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -435,7 +435,7 @@ config PINCTRL_MILOS Technologies Inc Milos platform. config PINCTRL_SM8150 - tristate "Qualcomm Technologies Inc SM8150 pin controller driver" + tristate "Qualcomm SM8150 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -443,7 +443,7 @@ config PINCTRL_SM8150 Technologies Inc SM8150 platform. config PINCTRL_SM8250 - tristate "Qualcomm Technologies Inc SM8250 pin controller driver" + tristate "Qualcomm SM8250 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -451,7 +451,7 @@ config PINCTRL_SM8250 Technologies Inc SM8250 platform. config PINCTRL_SM8350 - tristate "Qualcomm Technologies Inc SM8350 pin controller driver" + tristate "Qualcomm SM8350 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -459,7 +459,7 @@ config PINCTRL_SM8350 Technologies Inc SM8350 platform. config PINCTRL_SM8450 - tristate "Qualcomm Technologies Inc SM8450 pin controller driver" + tristate "Qualcomm SM8450 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -467,7 +467,7 @@ config PINCTRL_SM8450 Technologies Inc SM8450 platform. config PINCTRL_SM8550 - tristate "Qualcomm Technologies Inc SM8550 pin controller driver" + tristate "Qualcomm SM8550 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -475,7 +475,7 @@ config PINCTRL_SM8550 Technologies Inc SM8550 platform. config PINCTRL_SM8650 - tristate "Qualcomm Technologies Inc SM8650 pin controller driver" + tristate "Qualcomm SM8650 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -483,7 +483,7 @@ config PINCTRL_SM8650 Technologies Inc SM8650 platform. config PINCTRL_SM8750 - tristate "Qualcomm Technologies Inc SM8750 pin controller driver" + tristate "Qualcomm SM8750 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the @@ -491,7 +491,7 @@ config PINCTRL_SM8750 Technologies Inc SM8750 platform. config PINCTRL_X1E80100 - tristate "Qualcomm Technologies Inc X1E80100 pin controller driver" + tristate "Qualcomm X1E80100 pin controller driver" depends on ARM64 || COMPILE_TEST help This is the pinctrl, pinmux, pinconf and gpiolib driver for the From 87182ef0bf93c283b2ab350fa4d1e0b098eb1324 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 24 Apr 2026 18:40:29 -0700 Subject: [PATCH 0238/5214] pinctrl: starfive: jh7110: use struct_size Instead of an extra kcalloc, Use a flexible array member to combine allocations. Saves a pointer in the struct. Signed-off-by: Rosen Penev Acked-by: Hal Feng Signed-off-by: Linus Walleij --- drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 12 +++++------- drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index e44480e71ea8..3572e8edd9f3 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -857,17 +857,15 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) return -EINVAL; } +#if IS_ENABLED(CONFIG_PM_SLEEP) + sfp = devm_kzalloc(dev, struct_size(sfp, saved_regs, info->nsaved_regs), + GFP_KERNEL); +#else sfp = devm_kzalloc(dev, sizeof(*sfp), GFP_KERNEL); +#endif if (!sfp) return -ENOMEM; -#if IS_ENABLED(CONFIG_PM_SLEEP) - sfp->saved_regs = devm_kcalloc(dev, info->nsaved_regs, - sizeof(*sfp->saved_regs), GFP_KERNEL); - if (!sfp->saved_regs) - return -ENOMEM; -#endif - sfp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfp->base)) return PTR_ERR(sfp->base); diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h index 2da2d6858008..188fc9d96269 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h @@ -21,7 +21,7 @@ struct jh7110_pinctrl { /* register read/write mutex */ struct mutex mutex; const struct jh7110_pinctrl_soc_info *info; - u32 *saved_regs; + u32 saved_regs[]; }; struct jh7110_gpio_irq_reg { From ea449889703578da86e548f5aed46387e10da748 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 08:04:18 -0400 Subject: [PATCH 0239/5214] KVM: VMX: remove regs argument of __vmx_vcpu_run Registers are reachable through vcpu_vmx, no need to pass a separate pointer to the regs[] array. No functional change intended. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/kvm-asm-offsets.c | 1 + arch/x86/kvm/vmx/vmenter.S | 58 +++++++++++++++------------------- arch/x86/kvm/vmx/vmx.c | 3 +- arch/x86/kvm/vmx/vmx.h | 3 +- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/arch/x86/kvm/kvm-asm-offsets.c b/arch/x86/kvm/kvm-asm-offsets.c index 24a710d37323..36ac61724dd7 100644 --- a/arch/x86/kvm/kvm-asm-offsets.c +++ b/arch/x86/kvm/kvm-asm-offsets.c @@ -24,6 +24,7 @@ static void __used common(void) if (IS_ENABLED(CONFIG_KVM_INTEL)) { BLANK(); + OFFSET(VMX_vcpu_arch_regs, vcpu_vmx, vcpu.arch.regs); OFFSET(VMX_spec_ctrl, vcpu_vmx, spec_ctrl); } } diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 8a481dae9cae..0a09288a8d29 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -11,24 +11,24 @@ #define WORD_SIZE (BITS_PER_LONG / 8) -#define VCPU_RAX __VCPU_REGS_RAX * WORD_SIZE -#define VCPU_RCX __VCPU_REGS_RCX * WORD_SIZE -#define VCPU_RDX __VCPU_REGS_RDX * WORD_SIZE -#define VCPU_RBX __VCPU_REGS_RBX * WORD_SIZE +#define VCPU_RAX (VMX_vcpu_arch_regs + __VCPU_REGS_RAX * WORD_SIZE) +#define VCPU_RCX (VMX_vcpu_arch_regs + __VCPU_REGS_RCX * WORD_SIZE) +#define VCPU_RDX (VMX_vcpu_arch_regs + __VCPU_REGS_RDX * WORD_SIZE) +#define VCPU_RBX (VMX_vcpu_arch_regs + __VCPU_REGS_RBX * WORD_SIZE) /* Intentionally omit RSP as it's context switched by hardware */ -#define VCPU_RBP __VCPU_REGS_RBP * WORD_SIZE -#define VCPU_RSI __VCPU_REGS_RSI * WORD_SIZE -#define VCPU_RDI __VCPU_REGS_RDI * WORD_SIZE +#define VCPU_RBP (VMX_vcpu_arch_regs + __VCPU_REGS_RBP * WORD_SIZE) +#define VCPU_RSI (VMX_vcpu_arch_regs + __VCPU_REGS_RSI * WORD_SIZE) +#define VCPU_RDI (VMX_vcpu_arch_regs + __VCPU_REGS_RDI * WORD_SIZE) #ifdef CONFIG_X86_64 -#define VCPU_R8 __VCPU_REGS_R8 * WORD_SIZE -#define VCPU_R9 __VCPU_REGS_R9 * WORD_SIZE -#define VCPU_R10 __VCPU_REGS_R10 * WORD_SIZE -#define VCPU_R11 __VCPU_REGS_R11 * WORD_SIZE -#define VCPU_R12 __VCPU_REGS_R12 * WORD_SIZE -#define VCPU_R13 __VCPU_REGS_R13 * WORD_SIZE -#define VCPU_R14 __VCPU_REGS_R14 * WORD_SIZE -#define VCPU_R15 __VCPU_REGS_R15 * WORD_SIZE +#define VCPU_R8 (VMX_vcpu_arch_regs + __VCPU_REGS_R8 * WORD_SIZE) +#define VCPU_R9 (VMX_vcpu_arch_regs + __VCPU_REGS_R9 * WORD_SIZE) +#define VCPU_R10 (VMX_vcpu_arch_regs + __VCPU_REGS_R10 * WORD_SIZE) +#define VCPU_R11 (VMX_vcpu_arch_regs + __VCPU_REGS_R11 * WORD_SIZE) +#define VCPU_R12 (VMX_vcpu_arch_regs + __VCPU_REGS_R12 * WORD_SIZE) +#define VCPU_R13 (VMX_vcpu_arch_regs + __VCPU_REGS_R13 * WORD_SIZE) +#define VCPU_R14 (VMX_vcpu_arch_regs + __VCPU_REGS_R14 * WORD_SIZE) +#define VCPU_R15 (VMX_vcpu_arch_regs + __VCPU_REGS_R15 * WORD_SIZE) #endif .macro VMX_DO_EVENT_IRQOFF call_insn call_target @@ -68,7 +68,6 @@ /** * __vmx_vcpu_run - Run a vCPU via a transition to VMX guest mode * @vmx: struct vcpu_vmx * - * @regs: unsigned long * (to guest registers) * @flags: VMX_RUN_VMRESUME: use VMRESUME instead of VMLAUNCH * VMX_RUN_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl * VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO: vCPU can access host MMIO @@ -94,12 +93,6 @@ SYM_FUNC_START(__vmx_vcpu_run) push %_ASM_ARG1 /* Save @flags (used for VMLAUNCH vs. VMRESUME and mitigations). */ - push %_ASM_ARG3 - - /* - * Save @regs, _ASM_ARG2 may be modified by vmx_update_host_rsp() and - * @regs is needed after VM-Exit to save the guest's register values. - */ push %_ASM_ARG2 lea (%_ASM_SP), %_ASM_ARG2 @@ -107,6 +100,9 @@ SYM_FUNC_START(__vmx_vcpu_run) ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL + /* Reload @vmx, _ASM_ARG1 may be modified by vmx_update_host_rsp(). */ + mov WORD_SIZE(%_ASM_SP), %_ASM_DI + /* * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the * host's, write the MSR. @@ -115,7 +111,6 @@ SYM_FUNC_START(__vmx_vcpu_run) * there must not be any returns or indirect branches between this code * and vmentry. */ - mov 2*WORD_SIZE(%_ASM_SP), %_ASM_DI #ifdef CONFIG_X86_64 mov VMX_spec_ctrl(%rdi), %rdx cmp PER_CPU_VAR(x86_spec_ctrl_current), %rdx @@ -142,8 +137,8 @@ SYM_FUNC_START(__vmx_vcpu_run) * an LFENCE to stop speculation from skipping the wrmsr. */ - /* Load @regs to RAX. */ - mov (%_ASM_SP), %_ASM_AX + /* Load @vmx to RAX. */ + mov WORD_SIZE(%_ASM_SP), %_ASM_AX /* Load guest registers. Don't clobber flags. */ mov VCPU_RCX(%_ASM_AX), %_ASM_CX @@ -162,7 +157,7 @@ SYM_FUNC_START(__vmx_vcpu_run) mov VCPU_R14(%_ASM_AX), %r14 mov VCPU_R15(%_ASM_AX), %r15 #endif - /* Load guest RAX. This kills the @regs pointer! */ + /* Load guest RAX. This kills the @vmx pointer! */ mov VCPU_RAX(%_ASM_AX), %_ASM_AX /* @@ -172,7 +167,7 @@ SYM_FUNC_START(__vmx_vcpu_run) * do VERW. Else, do nothing (no mitigations needed/enabled). */ ALTERNATIVE_2 "", \ - __stringify(testl $VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO, WORD_SIZE(%_ASM_SP); \ + __stringify(testl $VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO, (%_ASM_SP); \ jz .Lskip_mmio_verw; \ VERW; \ .Lskip_mmio_verw:), \ @@ -180,7 +175,7 @@ SYM_FUNC_START(__vmx_vcpu_run) __stringify(VERW), X86_FEATURE_CLEAR_CPU_BUF_VM /* Check @flags to see if VMLAUNCH or VMRESUME is needed. */ - testl $VMX_RUN_VMRESUME, WORD_SIZE(%_ASM_SP) + testl $VMX_RUN_VMRESUME, (%_ASM_SP) jz .Lvmlaunch /* @@ -215,8 +210,8 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) /* Temporarily save guest's RAX. */ push %_ASM_AX - /* Reload @regs to RAX. */ - mov WORD_SIZE(%_ASM_SP), %_ASM_AX + /* Reload @vmx to RAX. */ + mov 2*WORD_SIZE(%_ASM_SP), %_ASM_AX /* Save all guest registers, including RAX from the stack */ pop VCPU_RAX(%_ASM_AX) @@ -241,9 +236,6 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) xor %ebx, %ebx .Lclear_regs: - /* Discard @regs. The register is irrelevant, it just can't be RBX. */ - pop %_ASM_AX - /* * Clear all general purpose registers except RSP and RBX to prevent * speculative use of the guest's values, even those that are reloaded diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a29896a9ef14..7dd4aff29026 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7468,8 +7468,7 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, if (vcpu->arch.cr2 != native_read_cr2()) native_write_cr2(vcpu->arch.cr2); - vmx->fail = __vmx_vcpu_run(vmx, (unsigned long *)&vcpu->arch.regs, - flags); + vmx->fail = __vmx_vcpu_run(vmx, flags); vcpu->arch.cr2 = native_read_cr2(); vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index db84e8001da5..53793d530a69 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -370,8 +370,7 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu); void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp); void vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, unsigned int flags); unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx); -bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned long *regs, - unsigned int flags); +bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned int flags); void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu); void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set); From 034c9c37b6d74fbb08de436029341da6afd877b1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Oct 2022 17:16:26 -0400 Subject: [PATCH 0240/5214] KVM: VMX: more cleanups to __vmx_vcpu_run Slightly improve register allocation, loading vmx only once before vmlaunch/vmresume. This also makes the code slightly more similar to the one for AMD processors, in that both keep the pointer to struct vcpu_vmx or vcpu_svm in %rdi. The code for restoring the guest value of SPEC_CTRL is also the same for Intel and AMD. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmenter.S | 85 ++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 0a09288a8d29..efac31cedfde 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -98,11 +98,11 @@ SYM_FUNC_START(__vmx_vcpu_run) lea (%_ASM_SP), %_ASM_ARG2 call vmx_update_host_rsp - ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL - /* Reload @vmx, _ASM_ARG1 may be modified by vmx_update_host_rsp(). */ mov WORD_SIZE(%_ASM_SP), %_ASM_DI + ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL + /* * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the * host's, write the MSR. @@ -122,9 +122,9 @@ SYM_FUNC_START(__vmx_vcpu_run) mov PER_CPU_VAR(x86_spec_ctrl_current), %ecx xor %eax, %ecx mov VMX_spec_ctrl + 4(%edi), %edx - mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %edi - xor %edx, %edi - or %edi, %ecx + mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %esi + xor %edx, %esi + or %esi, %ecx je .Lspec_ctrl_done #endif mov $MSR_IA32_SPEC_CTRL, %ecx @@ -137,28 +137,25 @@ SYM_FUNC_START(__vmx_vcpu_run) * an LFENCE to stop speculation from skipping the wrmsr. */ - /* Load @vmx to RAX. */ - mov WORD_SIZE(%_ASM_SP), %_ASM_AX - /* Load guest registers. Don't clobber flags. */ - mov VCPU_RCX(%_ASM_AX), %_ASM_CX - mov VCPU_RDX(%_ASM_AX), %_ASM_DX - mov VCPU_RBX(%_ASM_AX), %_ASM_BX - mov VCPU_RBP(%_ASM_AX), %_ASM_BP - mov VCPU_RSI(%_ASM_AX), %_ASM_SI - mov VCPU_RDI(%_ASM_AX), %_ASM_DI + mov VCPU_RAX(%_ASM_DI), %_ASM_AX + mov VCPU_RCX(%_ASM_DI), %_ASM_CX + mov VCPU_RDX(%_ASM_DI), %_ASM_DX + mov VCPU_RBX(%_ASM_DI), %_ASM_BX + mov VCPU_RBP(%_ASM_DI), %_ASM_BP + mov VCPU_RSI(%_ASM_DI), %_ASM_SI #ifdef CONFIG_X86_64 - mov VCPU_R8 (%_ASM_AX), %r8 - mov VCPU_R9 (%_ASM_AX), %r9 - mov VCPU_R10(%_ASM_AX), %r10 - mov VCPU_R11(%_ASM_AX), %r11 - mov VCPU_R12(%_ASM_AX), %r12 - mov VCPU_R13(%_ASM_AX), %r13 - mov VCPU_R14(%_ASM_AX), %r14 - mov VCPU_R15(%_ASM_AX), %r15 + mov VCPU_R8 (%_ASM_DI), %r8 + mov VCPU_R9 (%_ASM_DI), %r9 + mov VCPU_R10(%_ASM_DI), %r10 + mov VCPU_R11(%_ASM_DI), %r11 + mov VCPU_R12(%_ASM_DI), %r12 + mov VCPU_R13(%_ASM_DI), %r13 + mov VCPU_R14(%_ASM_DI), %r14 + mov VCPU_R15(%_ASM_DI), %r15 #endif - /* Load guest RAX. This kills the @vmx pointer! */ - mov VCPU_RAX(%_ASM_AX), %_ASM_AX + /* Load guest RDI. This kills the @vmx pointer! */ + mov VCPU_RDI(%_ASM_DI), %_ASM_DI /* * Note, ALTERNATIVE_2 works in reverse order. If CLEAR_CPU_BUF_VM is @@ -207,29 +204,29 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) UNWIND_HINT_RESTORE ENDBR - /* Temporarily save guest's RAX. */ - push %_ASM_AX + /* Temporarily save guest's RDI. */ + push %_ASM_DI - /* Reload @vmx to RAX. */ - mov 2*WORD_SIZE(%_ASM_SP), %_ASM_AX + /* Reload @vmx to RDI. */ + mov 2*WORD_SIZE(%_ASM_SP), %_ASM_DI - /* Save all guest registers, including RAX from the stack */ - pop VCPU_RAX(%_ASM_AX) - mov %_ASM_CX, VCPU_RCX(%_ASM_AX) - mov %_ASM_DX, VCPU_RDX(%_ASM_AX) - mov %_ASM_BX, VCPU_RBX(%_ASM_AX) - mov %_ASM_BP, VCPU_RBP(%_ASM_AX) - mov %_ASM_SI, VCPU_RSI(%_ASM_AX) - mov %_ASM_DI, VCPU_RDI(%_ASM_AX) + /* Save all guest registers, including RDI from the stack */ + mov %_ASM_AX, VCPU_RAX(%_ASM_DI) + mov %_ASM_CX, VCPU_RCX(%_ASM_DI) + mov %_ASM_DX, VCPU_RDX(%_ASM_DI) + mov %_ASM_BX, VCPU_RBX(%_ASM_DI) + mov %_ASM_BP, VCPU_RBP(%_ASM_DI) + mov %_ASM_SI, VCPU_RSI(%_ASM_DI) + pop VCPU_RDI(%_ASM_DI) #ifdef CONFIG_X86_64 - mov %r8, VCPU_R8 (%_ASM_AX) - mov %r9, VCPU_R9 (%_ASM_AX) - mov %r10, VCPU_R10(%_ASM_AX) - mov %r11, VCPU_R11(%_ASM_AX) - mov %r12, VCPU_R12(%_ASM_AX) - mov %r13, VCPU_R13(%_ASM_AX) - mov %r14, VCPU_R14(%_ASM_AX) - mov %r15, VCPU_R15(%_ASM_AX) + mov %r8, VCPU_R8 (%_ASM_DI) + mov %r9, VCPU_R9 (%_ASM_DI) + mov %r10, VCPU_R10(%_ASM_DI) + mov %r11, VCPU_R11(%_ASM_DI) + mov %r12, VCPU_R12(%_ASM_DI) + mov %r13, VCPU_R13(%_ASM_DI) + mov %r14, VCPU_R14(%_ASM_DI) + mov %r15, VCPU_R15(%_ASM_DI) #endif /* Clear return value to indicate VM-Exit (as opposed to VM-Fail). */ From 47cd3a2277d1247d92a3545e7f5f64f0750d1dfa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 03:19:46 -0400 Subject: [PATCH 0241/5214] KVM: SVM: prepare for making SPEC_CTRL switch common with VMX Remove the local labels and restrict RESTORE_*_BODY to just the restore code itself. Since the alternatives are different between VMX and SVM, having labels in the per-vendor file and jumps in another would be too confusing. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/vmenter.S | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index d47c5c93c991..aa655b519fe0 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -39,10 +39,8 @@ ALTERNATIVE_2 "", \ "jmp 800f", X86_FEATURE_MSR_SPEC_CTRL, \ "", X86_FEATURE_V_SPEC_CTRL -801: .endm -.macro RESTORE_GUEST_SPEC_CTRL_BODY -800: +.macro RESTORE_GUEST_SPEC_CTRL_BODY guest_spec_ctrl:req, label:req /* * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the * host's, write the MSR. This is kept out-of-line so that the common @@ -53,24 +51,23 @@ * and vmentry. */ #ifdef CONFIG_X86_64 - mov SVM_spec_ctrl(%rdi), %rdx + mov \guest_spec_ctrl, %rdx cmp PER_CPU_VAR(x86_spec_ctrl_current), %rdx - je 801b + je \label movl %edx, %eax shr $32, %rdx #else - mov SVM_spec_ctrl(%edi), %eax + mov \guest_spec_ctrl, %eax mov PER_CPU_VAR(x86_spec_ctrl_current), %ecx xor %eax, %ecx - mov SVM_spec_ctrl + 4(%edi), %edx + mov 4 + \guest_spec_ctrl, %edx mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %esi xor %edx, %esi or %esi, %ecx - je 801b + je \label #endif mov $MSR_IA32_SPEC_CTRL, %ecx wrmsr - jmp 801b .endm .macro RESTORE_HOST_SPEC_CTRL @@ -78,10 +75,8 @@ ALTERNATIVE_2 "", \ "jmp 900f", X86_FEATURE_MSR_SPEC_CTRL, \ "", X86_FEATURE_V_SPEC_CTRL -901: .endm -.macro RESTORE_HOST_SPEC_CTRL_BODY spec_ctrl_intercepted:req -900: +.macro RESTORE_HOST_SPEC_CTRL_BODY guest_spec_ctrl:req, spec_ctrl_intercepted:req, label:req /* Same for after vmexit. */ mov $MSR_IA32_SPEC_CTRL, %ecx @@ -92,28 +87,27 @@ cmpb $0, \spec_ctrl_intercepted jnz 998f rdmsr - movl %eax, SVM_spec_ctrl(%_ASM_DI) - movl %edx, SVM_spec_ctrl + 4(%_ASM_DI) + movl %eax, \guest_spec_ctrl + movl %edx, 4 + \guest_spec_ctrl 998: /* Now restore the host value of the MSR if different from the guest's. */ #ifdef CONFIG_X86_64 mov PER_CPU_VAR(x86_spec_ctrl_current), %rdx - cmp SVM_spec_ctrl(%rdi), %rdx - je 901b + cmp \guest_spec_ctrl, %rdx + je \label movl %edx, %eax shr $32, %rdx #else mov PER_CPU_VAR(x86_spec_ctrl_current), %eax - mov SVM_spec_ctrl(%edi), %esi + mov \guest_spec_ctrl, %esi xor %eax, %esi mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %edx - mov SVM_spec_ctrl + 4(%edi), %edi + mov 4 + \guest_spec_ctrl, %edi xor %edx, %edi or %edi, %esi - je 901b + je \label #endif wrmsr - jmp 901b .endm #define SVM_CLEAR_CPU_BUFFERS \ @@ -162,6 +156,7 @@ SYM_FUNC_START(__svm_vcpu_run) /* Clobbers RAX, RCX, RDX (and ESI on 32-bit), consumes RDI (@svm). */ RESTORE_GUEST_SPEC_CTRL +801: /* * Use a single vmcb (vmcb01 because it's always valid) for @@ -242,6 +237,7 @@ SYM_FUNC_START(__svm_vcpu_run) * and RSP (pointer to @spec_ctrl_intercepted). */ RESTORE_HOST_SPEC_CTRL +901: /* * Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be @@ -295,8 +291,12 @@ SYM_FUNC_START(__svm_vcpu_run) pop %_ASM_BP RET - RESTORE_GUEST_SPEC_CTRL_BODY - RESTORE_HOST_SPEC_CTRL_BODY (%_ASM_SP) +800: + RESTORE_GUEST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), 801b + jmp 801b +900: + RESTORE_HOST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), (%_ASM_SP), 901b + jmp 901b 10: cmpb $0, _ASM_RIP(virt_rebooting) jne 2b @@ -362,6 +362,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) /* Clobbers RAX, RCX, and RDX (@hostsa), consumes RDI (@svm). */ RESTORE_GUEST_SPEC_CTRL +801: /* Get svm->current_vmcb->pa into RAX. */ mov SVM_current_vmcb(%rdi), %rax @@ -378,6 +379,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) /* Clobbers RAX, RCX, RDX, consumes RDI (@svm) and RSI (@spec_ctrl_intercepted). */ RESTORE_HOST_SPEC_CTRL +901: /* * Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be @@ -391,8 +393,12 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) FRAME_END RET - RESTORE_GUEST_SPEC_CTRL_BODY - RESTORE_HOST_SPEC_CTRL_BODY %sil +800: + RESTORE_GUEST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), 801b + jmp 801b +900: + RESTORE_HOST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), %sil, 901b + jmp 901b 3: cmpb $0, virt_rebooting(%rip) jne 2b From 4d33fd3819a36708031a7f689efea344332592a4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 27 Apr 2026 13:57:52 -0400 Subject: [PATCH 0242/5214] KVM: SVM: pass struct vcpu_svm to msr_write_intercepted Remove an unnecessary difference between Intel and AMD. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index e7fdd7a9c280..0a2b85db8977 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -661,7 +661,7 @@ static void clr_dr_intercepts(struct vcpu_svm *svm) svm_mark_intercepts_dirty(svm); } -static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) +static bool msr_write_intercepted(struct vcpu_svm *svm, u32 msr) { /* * For non-nested case: @@ -672,8 +672,7 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) * If the L02 MSR bitmap does not intercept the MSR, then we need to * save it. */ - void *msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm : - to_svm(vcpu)->msrpm; + void *msrpm = is_guest_mode(&svm->vcpu) ? svm->nested.msrpm : svm->msrpm; return svm_test_msr_bitmap_write(msrpm, msr); } @@ -2764,7 +2763,7 @@ static bool sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, { return is_sev_es_guest(vcpu) && vcpu->arch.guest_state_protected && msr_info->index != MSR_IA32_XSS && - !msr_write_intercepted(vcpu, msr_info->index); + !msr_write_intercepted(to_svm(vcpu), msr_info->index); } static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) @@ -4414,7 +4413,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { bool force_immediate_exit = run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT; struct vcpu_svm *svm = to_svm(vcpu); - bool spec_ctrl_intercepted = msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL); + bool spec_ctrl_intercepted = msr_write_intercepted(svm, MSR_IA32_SPEC_CTRL); trace_kvm_entry(vcpu, force_immediate_exit); @@ -4556,7 +4555,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vcpu->arch.regs_avail &= ~SVM_REGS_LAZY_LOAD_SET; - if (!msr_write_intercepted(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL)) + if (!msr_write_intercepted(svm, MSR_AMD64_PERF_CNTR_GLOBAL_CTL)) rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, vcpu_to_pmu(vcpu)->global_ctrl); trace_kvm_exit(vcpu, KVM_ISA_SVM); From bfac263f913b613e70115a33c9f62ebac0426da2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 15 Apr 2026 10:26:59 -0400 Subject: [PATCH 0243/5214] KVM: SVM: adopt the same VMX_RUN_* flags as VMX Rename VMX_RUN_* to KVM_ENTER_* (to not confuse with KVM_RUN_* that already exists) and replace SVM's spec_ctrl_intercepted with that same bitmask. Note that KVM_ENTER_SAVE_SPEC_CTRL has the opposite polarity compared to spec_ctrl_intercepted. That is, the guest value of the MSR must be saved if it writes are *not* intercepted. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 14 +++++++++----- arch/x86/kvm/svm/svm.h | 4 ++-- arch/x86/kvm/svm/vmenter.S | 23 ++++++++++++----------- arch/x86/kvm/vmenter.h | 9 +++++++++ arch/x86/kvm/vmx/run_flags.h | 9 --------- arch/x86/kvm/vmx/vmenter.S | 12 ++++++------ arch/x86/kvm/vmx/vmx.c | 13 +++++++------ arch/x86/kvm/vmx/vmx.h | 3 +-- 8 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 arch/x86/kvm/vmenter.h delete mode 100644 arch/x86/kvm/vmx/run_flags.h diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 0a2b85db8977..353e464d6d94 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -50,6 +50,7 @@ #include "trace.h" +#include "vmenter.h" #include "svm.h" #include "svm_ops.h" @@ -4377,7 +4378,7 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) return EXIT_FASTPATH_NONE; } -static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, bool spec_ctrl_intercepted) +static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, unsigned enter_flags) { struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, vcpu->cpu); struct vcpu_svm *svm = to_svm(vcpu); @@ -4399,10 +4400,10 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, bool spec_ctrl_in amd_clear_divider(); if (is_sev_es_guest(vcpu)) - __svm_sev_es_vcpu_run(svm, spec_ctrl_intercepted, + __svm_sev_es_vcpu_run(svm, enter_flags, sev_es_host_save_area(sd)); else - __svm_vcpu_run(svm, spec_ctrl_intercepted); + __svm_vcpu_run(svm, enter_flags); raw_local_irq_disable(); @@ -4413,7 +4414,10 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) { bool force_immediate_exit = run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT; struct vcpu_svm *svm = to_svm(vcpu); - bool spec_ctrl_intercepted = msr_write_intercepted(svm, MSR_IA32_SPEC_CTRL); + unsigned enter_flags = 0; + + if (!msr_write_intercepted(svm, MSR_IA32_SPEC_CTRL)) + enter_flags |= KVM_ENTER_SAVE_SPEC_CTRL; trace_kvm_entry(vcpu, force_immediate_exit); @@ -4496,7 +4500,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL)) x86_spec_ctrl_set_guest(svm->virt_spec_ctrl); - svm_vcpu_enter_exit(vcpu, spec_ctrl_intercepted); + svm_vcpu_enter_exit(vcpu, enter_flags); if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL)) x86_spec_ctrl_restore_host(svm->virt_spec_ctrl); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index a10668d17a16..01f30f0b4eb1 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -1007,9 +1007,9 @@ static inline void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_sa /* vmenter.S */ -void __svm_sev_es_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted, +void __svm_sev_es_vcpu_run(struct vcpu_svm *svm, unsigned int flags, struct sev_es_save_area *hostsa); -void __svm_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted); +void __svm_vcpu_run(struct vcpu_svm *svm, unsigned int flags); #define DEFINE_KVM_GHCB_ACCESSORS(field) \ static __always_inline u64 kvm_ghcb_get_##field(struct vcpu_svm *svm) \ diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index aa655b519fe0..2bc7ecb338a9 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -7,6 +7,7 @@ #include #include #include "kvm-asm-offsets.h" +#include "vmenter.h" #define WORD_SIZE (BITS_PER_LONG / 8) @@ -76,7 +77,7 @@ "jmp 900f", X86_FEATURE_MSR_SPEC_CTRL, \ "", X86_FEATURE_V_SPEC_CTRL .endm -.macro RESTORE_HOST_SPEC_CTRL_BODY guest_spec_ctrl:req, spec_ctrl_intercepted:req, label:req +.macro RESTORE_HOST_SPEC_CTRL_BODY guest_spec_ctrl:req, enter_flags:req, label:req /* Same for after vmexit. */ mov $MSR_IA32_SPEC_CTRL, %ecx @@ -84,8 +85,8 @@ * Load the value that the guest had written into MSR_IA32_SPEC_CTRL, * if it was not intercepted during guest execution. */ - cmpb $0, \spec_ctrl_intercepted - jnz 998f + testl $KVM_ENTER_SAVE_SPEC_CTRL, \enter_flags + jz 998f rdmsr movl %eax, \guest_spec_ctrl movl %edx, 4 + \guest_spec_ctrl @@ -115,8 +116,8 @@ /** * __svm_vcpu_run - Run a vCPU via a transition to SVM guest mode - * @svm: struct vcpu_svm * - * @spec_ctrl_intercepted: bool + * @svm: struct vcpu_svm * + * @enter_flags: u32 */ SYM_FUNC_START(__svm_vcpu_run) push %_ASM_BP @@ -274,7 +275,7 @@ SYM_FUNC_START(__svm_vcpu_run) xor %r15d, %r15d #endif - /* "Pop" @spec_ctrl_intercepted. */ + /* "Pop" @enter_flags. */ pop %_ASM_BX pop %_ASM_BX @@ -335,8 +336,8 @@ SYM_FUNC_END(__svm_vcpu_run) /** * __svm_sev_es_vcpu_run - Run a SEV-ES vCPU via a transition to SVM guest mode - * @svm: struct vcpu_svm * - * @spec_ctrl_intercepted: bool + * @svm: struct vcpu_svm * + * @enter_flags: u32 */ SYM_FUNC_START(__svm_sev_es_vcpu_run) FRAME_BEGIN @@ -355,7 +356,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) /* * Save volatile registers that hold arguments that are needed after - * #VMEXIT (RDI=@svm and RSI=@spec_ctrl_intercepted). + * #VMEXIT (RDI=@svm and RSI=@enter_flags). */ mov %rdi, SEV_ES_RDI (%rdx) mov %rsi, SEV_ES_RSI (%rdx) @@ -377,7 +378,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) /* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */ FILL_RETURN_BUFFER %rax, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT - /* Clobbers RAX, RCX, RDX, consumes RDI (@svm) and RSI (@spec_ctrl_intercepted). */ + /* Clobbers RAX, RCX, RDX, consumes RDI (@svm) and RSI (@enter_flags). */ RESTORE_HOST_SPEC_CTRL 901: @@ -397,7 +398,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) RESTORE_GUEST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), 801b jmp 801b 900: - RESTORE_HOST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), %sil, 901b + RESTORE_HOST_SPEC_CTRL_BODY SVM_spec_ctrl(%_ASM_DI), %esi, 901b jmp 901b 3: cmpb $0, virt_rebooting(%rip) diff --git a/arch/x86/kvm/vmenter.h b/arch/x86/kvm/vmenter.h new file mode 100644 index 000000000000..29cdae650069 --- /dev/null +++ b/arch/x86/kvm/vmenter.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __KVM_X86_VMENTER_H +#define __KVM_X86_VMENTER_H + +#define KVM_ENTER_VMRESUME BIT(0) +#define KVM_ENTER_SAVE_SPEC_CTRL BIT(1) +#define KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(2) + +#endif /* __KVM_X86_VMENTER_H */ diff --git a/arch/x86/kvm/vmx/run_flags.h b/arch/x86/kvm/vmx/run_flags.h deleted file mode 100644 index 6a87a12135fb..000000000000 --- a/arch/x86/kvm/vmx/run_flags.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __KVM_X86_VMX_RUN_FLAGS_H -#define __KVM_X86_VMX_RUN_FLAGS_H - -#define VMX_RUN_VMRESUME BIT(0) -#define VMX_RUN_SAVE_SPEC_CTRL BIT(1) -#define VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(2) - -#endif /* __KVM_X86_VMX_RUN_FLAGS_H */ diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index efac31cedfde..d776286fe738 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -7,7 +7,7 @@ #include #include #include "kvm-asm-offsets.h" -#include "run_flags.h" +#include "vmenter.h" #define WORD_SIZE (BITS_PER_LONG / 8) @@ -68,9 +68,9 @@ /** * __vmx_vcpu_run - Run a vCPU via a transition to VMX guest mode * @vmx: struct vcpu_vmx * - * @flags: VMX_RUN_VMRESUME: use VMRESUME instead of VMLAUNCH - * VMX_RUN_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl - * VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO: vCPU can access host MMIO + * @flags: KVM_ENTER_VMRESUME: use VMRESUME instead of VMLAUNCH + * KVM_ENTER_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl + * KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO: vCPU can access host MMIO * * Returns: * 0 on VM-Exit, 1 on VM-Fail @@ -164,7 +164,7 @@ SYM_FUNC_START(__vmx_vcpu_run) * do VERW. Else, do nothing (no mitigations needed/enabled). */ ALTERNATIVE_2 "", \ - __stringify(testl $VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO, (%_ASM_SP); \ + __stringify(testl $KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO, (%_ASM_SP); \ jz .Lskip_mmio_verw; \ VERW; \ .Lskip_mmio_verw:), \ @@ -172,7 +172,7 @@ SYM_FUNC_START(__vmx_vcpu_run) __stringify(VERW), X86_FEATURE_CLEAR_CPU_BUF_VM /* Check @flags to see if VMLAUNCH or VMRESUME is needed. */ - testl $VMX_RUN_VMRESUME, (%_ASM_SP) + testl $KVM_ENTER_VMRESUME, (%_ASM_SP) jz .Lvmlaunch /* diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 7dd4aff29026..a8039b0f9392 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -73,6 +73,7 @@ #include "x86_ops.h" #include "smm.h" #include "vmx_onhyperv.h" +#include "vmenter.h" #include "posted_intr.h" #include "mmu/spte.h" @@ -964,12 +965,12 @@ static bool msr_write_intercepted(struct vcpu_vmx *vmx, u32 msr) return vmx_test_msr_bitmap_write(vmx->loaded_vmcs->msr_bitmap, msr); } -unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx) +unsigned int __vmx_vcpu_enter_flags(struct vcpu_vmx *vmx) { unsigned int flags = 0; if (vmx->loaded_vmcs->launched) - flags |= VMX_RUN_VMRESUME; + flags |= KVM_ENTER_VMRESUME; /* * If writes to the SPEC_CTRL MSR aren't intercepted, the guest is free @@ -977,11 +978,11 @@ unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx) * it after vmexit and store it in vmx->spec_ctrl. */ if (!msr_write_intercepted(vmx, MSR_IA32_SPEC_CTRL)) - flags |= VMX_RUN_SAVE_SPEC_CTRL; + flags |= KVM_ENTER_SAVE_SPEC_CTRL; if (cpu_feature_enabled(X86_FEATURE_CLEAR_CPU_BUF_VM_MMIO) && kvm_vcpu_can_access_host_mmio(&vmx->vcpu)) - flags |= VMX_RUN_CLEAR_CPU_BUFFERS_FOR_MMIO; + flags |= KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO; return flags; } @@ -7395,7 +7396,7 @@ void noinstr vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, if (!cpu_feature_enabled(X86_FEATURE_MSR_SPEC_CTRL)) return; - if (flags & VMX_RUN_SAVE_SPEC_CTRL) + if (flags & KVM_ENTER_SAVE_SPEC_CTRL) vmx->spec_ctrl = native_rdmsrq(MSR_IA32_SPEC_CTRL); /* @@ -7586,7 +7587,7 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) kvm_wait_lapic_expire(vcpu); /* The actual VMENTER/EXIT is in the .noinstr.text section. */ - vmx_vcpu_enter_exit(vcpu, __vmx_vcpu_run_flags(vmx)); + vmx_vcpu_enter_exit(vcpu, __vmx_vcpu_enter_flags(vmx)); /* All fields are clean at this point */ if (kvm_is_using_evmcs()) { diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 53793d530a69..be6a1dc2f69f 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -15,7 +15,6 @@ #include "vmcs.h" #include "vmx_ops.h" #include "../cpuid.h" -#include "run_flags.h" #include "../mmu.h" #include "common.h" @@ -369,7 +368,7 @@ struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr); void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu); void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp); void vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, unsigned int flags); -unsigned int __vmx_vcpu_run_flags(struct vcpu_vmx *vmx); +unsigned int __vmx_vcpu_enter_flags(struct vcpu_vmx *vmx); bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned int flags); void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu); From 4b275d0f8736c5843431c0d7b9178e367ed448c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 27 Apr 2026 04:59:54 -0400 Subject: [PATCH 0244/5214] KVM: SVM: extract RESTORE_*_SPEC_CTRL_BODY out of svm/vmenter.S Pure code movement, in preparation for using these macros for VMX as well. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/vmenter.S | 62 ----------------------------------- arch/x86/kvm/vmenter.h | 66 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 2bc7ecb338a9..f523d9e49839 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -41,35 +41,6 @@ "jmp 800f", X86_FEATURE_MSR_SPEC_CTRL, \ "", X86_FEATURE_V_SPEC_CTRL .endm -.macro RESTORE_GUEST_SPEC_CTRL_BODY guest_spec_ctrl:req, label:req - /* - * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the - * host's, write the MSR. This is kept out-of-line so that the common - * case does not have to jump. - * - * IMPORTANT: To avoid RSB underflow attacks and any other nastiness, - * there must not be any returns or indirect branches between this code - * and vmentry. - */ -#ifdef CONFIG_X86_64 - mov \guest_spec_ctrl, %rdx - cmp PER_CPU_VAR(x86_spec_ctrl_current), %rdx - je \label - movl %edx, %eax - shr $32, %rdx -#else - mov \guest_spec_ctrl, %eax - mov PER_CPU_VAR(x86_spec_ctrl_current), %ecx - xor %eax, %ecx - mov 4 + \guest_spec_ctrl, %edx - mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %esi - xor %edx, %esi - or %esi, %ecx - je \label -#endif - mov $MSR_IA32_SPEC_CTRL, %ecx - wrmsr -.endm .macro RESTORE_HOST_SPEC_CTRL /* No need to do anything if SPEC_CTRL is unset or V_SPEC_CTRL is set */ @@ -77,39 +48,6 @@ "jmp 900f", X86_FEATURE_MSR_SPEC_CTRL, \ "", X86_FEATURE_V_SPEC_CTRL .endm -.macro RESTORE_HOST_SPEC_CTRL_BODY guest_spec_ctrl:req, enter_flags:req, label:req - /* Same for after vmexit. */ - mov $MSR_IA32_SPEC_CTRL, %ecx - - /* - * Load the value that the guest had written into MSR_IA32_SPEC_CTRL, - * if it was not intercepted during guest execution. - */ - testl $KVM_ENTER_SAVE_SPEC_CTRL, \enter_flags - jz 998f - rdmsr - movl %eax, \guest_spec_ctrl - movl %edx, 4 + \guest_spec_ctrl -998: - /* Now restore the host value of the MSR if different from the guest's. */ -#ifdef CONFIG_X86_64 - mov PER_CPU_VAR(x86_spec_ctrl_current), %rdx - cmp \guest_spec_ctrl, %rdx - je \label - movl %edx, %eax - shr $32, %rdx -#else - mov PER_CPU_VAR(x86_spec_ctrl_current), %eax - mov \guest_spec_ctrl, %esi - xor %eax, %esi - mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %edx - mov 4 + \guest_spec_ctrl, %edi - xor %edx, %edi - or %edi, %esi - je \label -#endif - wrmsr -.endm #define SVM_CLEAR_CPU_BUFFERS \ ALTERNATIVE "", __CLEAR_CPU_BUFFERS, X86_FEATURE_CLEAR_CPU_BUF_VM diff --git a/arch/x86/kvm/vmenter.h b/arch/x86/kvm/vmenter.h index 29cdae650069..73f3adc301d9 100644 --- a/arch/x86/kvm/vmenter.h +++ b/arch/x86/kvm/vmenter.h @@ -6,4 +6,70 @@ #define KVM_ENTER_SAVE_SPEC_CTRL BIT(1) #define KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(2) +#ifdef __ASSEMBLER__ +.macro RESTORE_GUEST_SPEC_CTRL_BODY guest_spec_ctrl:req, label:req + /* + * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the + * host's, write the MSR. This is kept out-of-line so that the common + * case does not have to jump. + * + * IMPORTANT: To avoid RSB underflow attacks and any other nastiness, + * there must not be any returns or indirect branches between this code + * and vmentry. + */ +#ifdef CONFIG_X86_64 + mov \guest_spec_ctrl, %rdx + cmp PER_CPU_VAR(x86_spec_ctrl_current), %rdx + je \label + movl %edx, %eax + shr $32, %rdx +#else + mov \guest_spec_ctrl, %eax + mov PER_CPU_VAR(x86_spec_ctrl_current), %ecx + xor %eax, %ecx + mov 4 + \guest_spec_ctrl, %edx + mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %esi + xor %edx, %esi + or %esi, %ecx + je \label +#endif + mov $MSR_IA32_SPEC_CTRL, %ecx + wrmsr +.endm + +.macro RESTORE_HOST_SPEC_CTRL_BODY guest_spec_ctrl:req, enter_flags:req, label:req + /* Same for after vmexit. */ + mov $MSR_IA32_SPEC_CTRL, %ecx + + /* + * Load the value that the guest had written into MSR_IA32_SPEC_CTRL, + * if it was not intercepted during guest execution. + */ + testl $KVM_ENTER_SAVE_SPEC_CTRL, \enter_flags + jz 998f + rdmsr + movl %eax, \guest_spec_ctrl + movl %edx, 4 + \guest_spec_ctrl +998: + /* Now restore the host value of the MSR if different from the guest's. */ +#ifdef CONFIG_X86_64 + mov PER_CPU_VAR(x86_spec_ctrl_current), %rdx + cmp \guest_spec_ctrl, %rdx + je \label + movl %edx, %eax + shr $32, %rdx +#else + mov PER_CPU_VAR(x86_spec_ctrl_current), %eax + mov \guest_spec_ctrl, %esi + xor %eax, %esi + mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %edx + mov 4 + \guest_spec_ctrl, %edi + xor %edx, %edi + or %edi, %esi + je \label +#endif + wrmsr +.endm + +#endif /* __ASSEMBLER__ */ #endif /* __KVM_X86_VMENTER_H */ From 85c290509648e66939bdab21911351957675d631 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 09:54:35 -0400 Subject: [PATCH 0245/5214] KVM: VMX: switch to RESTORE_GUEST_SPEC_CTRL_BODY This has exactly the same expansion, so there is no change. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmenter.S | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index d776286fe738..2dd49080630d 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -101,35 +101,12 @@ SYM_FUNC_START(__vmx_vcpu_run) /* Reload @vmx, _ASM_ARG1 may be modified by vmx_update_host_rsp(). */ mov WORD_SIZE(%_ASM_SP), %_ASM_DI - ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL - /* - * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the - * host's, write the MSR. - * - * IMPORTANT: To avoid RSB underflow attacks and any other nastiness, - * there must not be any returns or indirect branches between this code - * and vmentry. + * Unlike AMD there's no V_SPEC_CTRL here, so do not leave the body + * out of line. Clobbers RAX, RCX, RDX, RSI. */ -#ifdef CONFIG_X86_64 - mov VMX_spec_ctrl(%rdi), %rdx - cmp PER_CPU_VAR(x86_spec_ctrl_current), %rdx - je .Lspec_ctrl_done - movl %edx, %eax - shr $32, %rdx -#else - mov VMX_spec_ctrl(%edi), %eax - mov PER_CPU_VAR(x86_spec_ctrl_current), %ecx - xor %eax, %ecx - mov VMX_spec_ctrl + 4(%edi), %edx - mov PER_CPU_VAR(x86_spec_ctrl_current + 4), %esi - xor %edx, %esi - or %esi, %ecx - je .Lspec_ctrl_done -#endif - mov $MSR_IA32_SPEC_CTRL, %ecx - wrmsr - + ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL + RESTORE_GUEST_SPEC_CTRL_BODY VMX_spec_ctrl(%_ASM_DI), .Lspec_ctrl_done .Lspec_ctrl_done: /* From 344ebd21f2c9d59fb6d7a409eedf8b31d8b0f17e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 04:39:20 -0400 Subject: [PATCH 0246/5214] KVM: VMX: replace vmx_spec_ctrl_restore_host with RESTORE_HOST_SPEC_CTRL_BODY Reuse the same assembly as SVM, just with alternatives instead of cpu_feature_enabled(X86_FEATURE_KERNEL_IBRS). Note that Intel does need an LFENCE with eIBRS, unlike AMD's AutoIBRS. However, it is not needed for X86_FEATURE_KERNEL_IBRS because there are no conditional branches between FILL_RETURN_BUFFER and ret. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmenter.h | 9 +++++++-- arch/x86/kvm/vmx/vmenter.S | 28 ++++++++++++++++++++++------ arch/x86/kvm/vmx/vmx.c | 25 ------------------------- arch/x86/kvm/vmx/vmx.h | 1 - 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/arch/x86/kvm/vmenter.h b/arch/x86/kvm/vmenter.h index 73f3adc301d9..ba3f71449c62 100644 --- a/arch/x86/kvm/vmenter.h +++ b/arch/x86/kvm/vmenter.h @@ -55,7 +55,12 @@ #ifdef CONFIG_X86_64 mov PER_CPU_VAR(x86_spec_ctrl_current), %rdx cmp \guest_spec_ctrl, %rdx - je \label + /* + * For legacy IBRS, the IBRS bit always needs to be written after + * transitioning from a less privileged predictor mode, regardless of + * whether the guest/host values differ. + */ + ALTERNATIVE __stringify(je \label), "", X86_FEATURE_KERNEL_IBRS movl %edx, %eax shr $32, %rdx #else @@ -66,7 +71,7 @@ mov 4 + \guest_spec_ctrl, %edi xor %edx, %edi or %edi, %esi - je \label + ALTERNATIVE __stringify(je \label), "", X86_FEATURE_KERNEL_IBRS #endif wrmsr .endm diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 2dd49080630d..7e4dc17fc0b8 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -105,9 +105,9 @@ SYM_FUNC_START(__vmx_vcpu_run) * Unlike AMD there's no V_SPEC_CTRL here, so do not leave the body * out of line. Clobbers RAX, RCX, RDX, RSI. */ - ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL - RESTORE_GUEST_SPEC_CTRL_BODY VMX_spec_ctrl(%_ASM_DI), .Lspec_ctrl_done -.Lspec_ctrl_done: + ALTERNATIVE "jmp .Lspec_ctrl_guest_done", "", X86_FEATURE_MSR_SPEC_CTRL + RESTORE_GUEST_SPEC_CTRL_BODY VMX_spec_ctrl(%_ASM_DI), .Lspec_ctrl_guest_done +.Lspec_ctrl_guest_done: /* * Since vmentry is serializing on affected CPUs, there's no need for @@ -252,16 +252,32 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) FILL_RETURN_BUFFER %_ASM_CX, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT,\ X86_FEATURE_RSB_VMEXIT_LITE - pop %_ASM_ARG2 /* @flags */ - pop %_ASM_ARG1 /* @vmx */ + /* Clobbers RAX, RCX, RDX, RSI. */ + ALTERNATIVE "jmp .Lspec_ctrl_host_done", "", X86_FEATURE_MSR_SPEC_CTRL + mov WORD_SIZE(%_ASM_SP), %_ASM_DI + RESTORE_HOST_SPEC_CTRL_BODY VMX_spec_ctrl(%_ASM_DI), (%_ASM_SP), .Lspec_ctrl_host_done +.Lspec_ctrl_host_done: - call vmx_spec_ctrl_restore_host + /* + * Halt speculation past a conditional wrmsr. Intel's eIBRS + * guarantees that the guest cannot control the RSB "once IBRS is + * set", but in the eIBRS case speculative execution past the 'je' + * can go all the way to the RET below while MSR_IA32_SPEC_CTRL + * still holds the guest value. + */ + ALTERNATIVE_2 "", "lfence", X86_FEATURE_MSR_SPEC_CTRL, \ + "", X86_FEATURE_KERNEL_IBRS CLEAR_BRANCH_HISTORY_VMEXIT /* Put return value in AX */ mov %_ASM_BX, %_ASM_AX + /* Pop our saved arguments from the stack */ + pop %_ASM_BX + pop %_ASM_BX + + /* ... and then the callee-save registers */ pop %_ASM_BX #ifdef CONFIG_X86_64 pop %r12 diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a8039b0f9392..b033f611fa04 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7388,31 +7388,6 @@ void noinstr vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp) } } -void noinstr vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, - unsigned int flags) -{ - u64 hostval = this_cpu_read(x86_spec_ctrl_current); - - if (!cpu_feature_enabled(X86_FEATURE_MSR_SPEC_CTRL)) - return; - - if (flags & KVM_ENTER_SAVE_SPEC_CTRL) - vmx->spec_ctrl = native_rdmsrq(MSR_IA32_SPEC_CTRL); - - /* - * If the guest/host SPEC_CTRL values differ, restore the host value. - * - * For legacy IBRS, the IBRS bit always needs to be written after - * transitioning from a less privileged predictor mode, regardless of - * whether the guest/host values differ. - */ - if (cpu_feature_enabled(X86_FEATURE_KERNEL_IBRS) || - vmx->spec_ctrl != hostval) - native_wrmsrq(MSR_IA32_SPEC_CTRL, hostval); - - barrier_nospec(); -} - static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu, bool force_immediate_exit) { diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index be6a1dc2f69f..f62007a5c2a7 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -367,7 +367,6 @@ void vmx_set_virtual_apic_mode(struct kvm_vcpu *vcpu); struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr); void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu); void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp); -void vmx_spec_ctrl_restore_host(struct vcpu_vmx *vmx, unsigned int flags); unsigned int __vmx_vcpu_enter_flags(struct vcpu_vmx *vmx); bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned int flags); void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu); From 9f82bbcac0a0d7727d7cff84ef517bf015752c9d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 10 Apr 2026 11:16:34 +0200 Subject: [PATCH 0247/5214] platform/x86: use u8 * for raw byte buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffer parameters in populate_{security,string}_buffer() and call_{biosattributes,password}_interface() are raw byte buffers, not character strings - use 'u8 *' instead of 'char *' to reflect this. Update the local buffer variables at the call sites and in populate_security_buffer() accordingly. Suggested-by: Ilpo Järvinen Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260410091633.2822-4-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/dell/dell-wmi-sysman/biosattr-interface.c | 8 ++++---- .../platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h | 4 ++-- .../x86/dell/dell-wmi-sysman/passwordattr-interface.c | 4 ++-- drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c index c2dd2de6bc20..db278ff4cc4d 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c @@ -13,8 +13,8 @@ #define SETBIOSDEFAULTS_METHOD_ID 0x03 #define SETATTRIBUTE_METHOD_ID 0x04 -static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, - int method_id) +static int call_biosattributes_interface(struct wmi_device *wdev, u8 *in_args, + size_t size, int method_id) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer input; @@ -51,7 +51,7 @@ int set_attribute(const char *a_name, const char *a_value) { size_t security_area_size, buffer_size; size_t a_name_size, a_value_size; - char *buffer = NULL, *start; + u8 *buffer = NULL, *start; int ret; mutex_lock(&wmi_priv.mutex); @@ -109,7 +109,7 @@ int set_bios_defaults(u8 deftype) { size_t security_area_size, buffer_size; size_t integer_area_size = sizeof(u8); - char *buffer = NULL; + u8 *buffer = NULL; u8 *defaultType; int ret; diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h index 5278a93fdaf7..3bddedad5eba 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h +++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h @@ -190,8 +190,8 @@ int init_bios_attr_set_interface(void); int map_wmi_error(int error_code); size_t calculate_string_buffer(const char *str); size_t calculate_security_buffer(const char *authentication); -void populate_security_buffer(char *buffer, const char *authentication); -ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str); +void populate_security_buffer(u8 *buffer, const char *authentication); +ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str); int set_new_password(const char *password_type, const char *new); int init_bios_attr_pass_interface(void); void exit_bios_attr_pass_interface(void); diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c index e586f7957946..026c134b3f97 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c @@ -8,7 +8,7 @@ #include #include "dell-wmi-sysman.h" -static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size) +static int call_password_interface(struct wmi_device *wdev, u8 *in_args, size_t size) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer input; @@ -42,7 +42,7 @@ int set_new_password(const char *password_type, const char *new) { size_t password_type_size, current_password_size, new_size; size_t security_area_size, buffer_size; - char *buffer = NULL, *start; + u8 *buffer = NULL, *start; char *current_password; int ret; diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 51d25fdc1389..ab46a023cc34 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -36,7 +36,7 @@ static int reset_option = -1; * @buffer_len: length of the destination buffer * @str: the string to insert into buffer */ -ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str) +ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str) { u16 *length = (u16 *)buffer; u16 *target = length + 1; @@ -87,10 +87,10 @@ size_t calculate_security_buffer(const char *authentication) * * Currently only supported type is PLAIN TEXT */ -void populate_security_buffer(char *buffer, const char *authentication) +void populate_security_buffer(u8 *buffer, const char *authentication) { size_t seclen = strlen(authentication); - char *auth = buffer + sizeof(u32) * 2; + u8 *auth = buffer + sizeof(u32) * 2; u32 *sectype = (u32 *) buffer; u32 *seclenp = sectype + 1; From 0b9f109c318a88d40e8a450d56f9bdcece94a327 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 10 Apr 2026 11:16:36 +0200 Subject: [PATCH 0248/5214] platform/x86: dell_rbu: use strscpy in image_type_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strcpy() has been deprecated [1] because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260410091633.2822-6-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell_rbu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 3fa9de9aa47b..768b15f406d3 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -562,9 +562,9 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj, buffer[count] = '\0'; if (strstr(buffer, "mono")) - strcpy(image_type, "mono"); + strscpy(image_type, "mono"); else if (strstr(buffer, "packet")) - strcpy(image_type, "packet"); + strscpy(image_type, "packet"); else if (strstr(buffer, "init")) { /* * If due to the user error the driver gets in a bad From 627ff616bc4622bea734fcc08630cc9c5a8370c5 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 27 Apr 2026 10:41:24 +0800 Subject: [PATCH 0249/5214] dt-bindings: remoteproc: imx-rproc: Support i.MX94 Add compatible string for: Cortex-M7 core[0,1] in i.MX94 Cortex-M33 Sync core in i.MX94 To i.MX94, Cortex-M7 core0 and core1 have different memory view from Cortex-A55 core, so different compatible string is used. Reviewed-by: Daniel Baluta Acked-by: Rob Herring (Arm) Reviewed-by: Frank Li Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20260427-imx943-rproc-v4-1-68d7c7253acd@nxp.com Signed-off-by: Mathieu Poirier --- .../devicetree/bindings/remoteproc/fsl,imx-rproc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml index ce8ec0119469..c18f71b64889 100644 --- a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml @@ -28,6 +28,9 @@ properties: - fsl,imx8qxp-cm4 - fsl,imx8ulp-cm33 - fsl,imx93-cm33 + - fsl,imx94-cm33s + - fsl,imx94-cm70 + - fsl,imx94-cm71 - fsl,imx95-cm7 clocks: From dd0dd1211324813ec17532ede17186c35127c637 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 27 Apr 2026 10:41:25 +0800 Subject: [PATCH 0250/5214] remoteproc: imx_rproc: Program non-zero SM CPU/LMM reset vector Cortex-M[7,33] processors use a fixed reset vector table format: 0x00 Initial SP value 0x04 Reset vector 0x08 NMI 0x0C ... ... IRQ[n] In ELF images, the corresponding layout is: reset_vectors: --> hardware reset address .word __stack_end__ .word Reset_Handler .word NMI_Handler .word HardFault_Handler ... .word UART_IRQHandler .word SPI_IRQHandler ... Reset_Handler: --> ELF entry point address ... The hardware fetches the first two words from reset_vectors and populates SP with __stack_end__ and PC with Reset_Handler. Execution proceeds from Reset_Handler. However, the ELF entry point does not always match the hardware reset address. For example, on i.MX94 CM33S: ELF entry point: 0x0ffc211d hardware reset base: 0x0ffc0000 (default reset value, sw programmable) Current driver always programs the reset vector as 0. But i.MX94 CM33S's default reset base is 0x0ffc0000, so the correct reset vector must be passed to the SM API; otherwise the M33 Sync core cannot boot successfully. rproc_elf_get_boot_addr() returns the ELF entry point, which is not the hardware reset vector address. Fix the issue by deriving the hardware reset vector locally using a SoC-specific mask: reset_vector = rproc->bootaddr & reset_vector_mask The ELF entry point semantics remain unchanged. The masking is applied only at the point where the SM reset vector is programmed. Add reset_vector_mask = GENMASK_U32(31, 16) to the i.MX95 M7 configuration so the hardware reset vector is derived correctly. Without this mask, the SM reset vector would be programmed with an unaligned ELF entry point and the M7 core would fail to boot. Reviewed-by: Daniel Baluta Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20260427-imx943-rproc-v4-2-68d7c7253acd@nxp.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/imx_rproc.c | 27 +++++++++++++++++++++++++-- drivers/remoteproc/imx_rproc.h | 2 ++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 0dd80e688b0e..c21782be4bb6 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -339,13 +339,32 @@ static int imx_rproc_scu_api_start(struct rproc *rproc) return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); } +static u64 imx_rproc_sm_get_reset_vector(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + u32 reset_vector_mask = priv->dcfg->reset_vector_mask ?: GENMASK(31, 0); + + /* + * The hardware fetches the first two words from reset_vectors + * (hardware reset address) and populates SP and PC using the first + * two words. Execution proceeds from PC. The ELF entry point does + * not always match the hardware reset address. + * To derive the correct hardware reset address, the lower address + * bits must be masked off before programming the reset vector. + */ + return rproc->bootaddr & reset_vector_mask; +} + static int imx_rproc_sm_cpu_start(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; const struct imx_rproc_dcfg *dcfg = priv->dcfg; + u64 reset_vector; int ret; - ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, 0, true, false, false); + reset_vector = imx_rproc_sm_get_reset_vector(rproc); + + ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, reset_vector, true, false, false); if (ret) { dev_err(priv->dev, "Failed to set reset vector cpuid(%u): %d\n", dcfg->cpuid, ret); return ret; @@ -359,13 +378,16 @@ static int imx_rproc_sm_lmm_start(struct rproc *rproc) struct imx_rproc *priv = rproc->priv; const struct imx_rproc_dcfg *dcfg = priv->dcfg; struct device *dev = priv->dev; + u64 reset_vector; int ret; + reset_vector = imx_rproc_sm_get_reset_vector(rproc); + /* * If the remoteproc core can't start the M7, it will already be * handled in imx_rproc_sm_lmm_prepare(). */ - ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid, 0, 0); + ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid, 0, reset_vector); if (ret) { dev_err(dev, "Failed to set reset vector lmid(%u), cpuid(%u): %d\n", dcfg->lmid, dcfg->cpuid, ret); @@ -1462,6 +1484,7 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = { /* Must align with System Manager Firmware */ .cpuid = 1, /* Use 1 as cpu id for M7 core */ .lmid = 1, /* Use 1 as Logical Machine ID where M7 resides */ + .reset_vector_mask = GENMASK_U32(31, 16), }; static const struct of_device_id imx_rproc_of_match[] = { diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h index d37e6f90548c..0d7d48352a10 100644 --- a/drivers/remoteproc/imx_rproc.h +++ b/drivers/remoteproc/imx_rproc.h @@ -41,6 +41,8 @@ struct imx_rproc_dcfg { /* For System Manager(SM) based SoCs */ u32 cpuid; /* ID of the remote core */ u32 lmid; /* ID of the Logcial Machine */ + /* reset_vector = elf_entry_addr & reset_vector_mask */ + u32 reset_vector_mask; }; #endif /* _IMX_RPROC_H */ From f4d97de947a1ba0d60b9b61f26ecd2e8fc173139 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 27 Apr 2026 10:41:26 +0800 Subject: [PATCH 0251/5214] remoteproc: imx_rproc: Add support for i.MX94 Add basic remoteproc support for the i.MX94 M-core processors, including address translation tables(dev addr is from view of remote processor, sys addr is from view of main processor) and device configuration data for the CM70, CM71, and CM33S cores. Reviewed-by: Daniel Baluta Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20260427-imx943-rproc-v4-3-68d7c7253acd@nxp.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/imx_rproc.c | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index c21782be4bb6..7f54322244ac 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -145,6 +145,41 @@ static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = { { 0x80000000, 0x80000000, 0x50000000, 0 }, }; +static const struct imx_rproc_att imx_rproc_att_imx94_m70[] = { + /* dev addr , sys addr , size , flags */ + /* TCM CODE NON-SECURE */ + { 0x00000000, 0x203C0000, 0x00040000, ATT_OWN | ATT_IOMEM }, + /* TCM SYS NON-SECURE*/ + { 0x20000000, 0x20400000, 0x00040000, ATT_OWN | ATT_IOMEM }, + + /* DDR */ + { 0x80000000, 0x80000000, 0x10000000, 0 }, +}; + +static const struct imx_rproc_att imx_rproc_att_imx94_m71[] = { + /* dev addr , sys addr , size , flags */ + /* TCM CODE NON-SECURE */ + { 0x00000000, 0x202C0000, 0x00040000, ATT_OWN | ATT_IOMEM }, + /* TCM SYS NON-SECURE*/ + { 0x20000000, 0x20300000, 0x00040000, ATT_OWN | ATT_IOMEM }, + + /* DDR */ + { 0x80000000, 0x80000000, 0x10000000, 0 }, +}; + +static const struct imx_rproc_att imx_rproc_att_imx94_m33s[] = { + /* dev addr , sys addr , size , flags */ + /* TCM CODE NON-SECURE */ + { 0x0FFC0000, 0x209C0000, 0x00040000, ATT_OWN | ATT_IOMEM }, + /* TCM SYS NON-SECURE */ + { 0x20000000, 0x20A00000, 0x00040000, ATT_OWN | ATT_IOMEM }, + /* M33S OCRAM NON-SECURE */ + { 0x20800000, 0x20800000, 0x180000, ATT_OWN | ATT_IOMEM }, + + /* DDR */ + { 0x80000000, 0x80000000, 0x10000000, 0 }, +}; + static const struct imx_rproc_att imx_rproc_att_imx93[] = { /* dev addr , sys addr , size , flags */ /* TCM CODE NON-SECURE */ @@ -1477,6 +1512,33 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { .flags = IMX_RPROC_NEED_CLKS, }; +static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m70 = { + .att = imx_rproc_att_imx94_m70, + .att_size = ARRAY_SIZE(imx_rproc_att_imx94_m70), + .ops = &imx_rproc_ops_sm_lmm, + .cpuid = 1, + .lmid = 2, + .reset_vector_mask = GENMASK_U32(31, 16), +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m71 = { + .att = imx_rproc_att_imx94_m71, + .att_size = ARRAY_SIZE(imx_rproc_att_imx94_m71), + .ops = &imx_rproc_ops_sm_lmm, + .cpuid = 7, + .lmid = 3, + .reset_vector_mask = GENMASK_U32(31, 16), +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m33s = { + .att = imx_rproc_att_imx94_m33s, + .att_size = ARRAY_SIZE(imx_rproc_att_imx94_m33s), + .ops = &imx_rproc_ops_sm_lmm, + .cpuid = 8, + .lmid = 1, + .reset_vector_mask = GENMASK_U32(31, 16), +}; + static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = { .att = imx_rproc_att_imx95_m7, .att_size = ARRAY_SIZE(imx_rproc_att_imx95_m7), @@ -1501,6 +1563,9 @@ static const struct of_device_id imx_rproc_of_match[] = { { .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm }, { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, { .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 }, + { .compatible = "fsl,imx94-cm70", .data = &imx_rproc_cfg_imx94_m70 }, + { .compatible = "fsl,imx94-cm71", .data = &imx_rproc_cfg_imx94_m71 }, + { .compatible = "fsl,imx94-cm33s", .data = &imx_rproc_cfg_imx94_m33s }, { .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 }, {}, }; From b54a4676b3069c9a8cc68e07122e6438491a3353 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 24 Apr 2026 09:57:57 +0200 Subject: [PATCH 0252/5214] sonypi: use strscpy() in sonypi_acpi_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strcpy() has been deprecated¹ because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. ¹ https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy Signed-off-by: Thorsten Blum Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260424075755.305770-3-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/char/sonypi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index ccda997a9098..959949f04f7d 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -1120,8 +1121,8 @@ static int sonypi_acpi_probe(struct platform_device *pdev) struct acpi_device *device = ACPI_COMPANION(&pdev->dev); sonypi_acpi_device = device; - strcpy(acpi_device_name(device), "Sony laptop hotkeys"); - strcpy(acpi_device_class(device), "sony/hotkey"); + strscpy(acpi_device_name(device), "Sony laptop hotkeys"); + strscpy(acpi_device_class(device), "sony/hotkey"); return 0; } From 0f4a265107902d133b70b1ced12aaea83e3cdeb1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Apr 2026 11:30:18 +0200 Subject: [PATCH 0253/5214] platform/x86: meraki-mx100: use real software node references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lpc_ich MFD driver now exposes the software node associated with the its GPIO controller cell. Remove the dummy software node from the meraki-mx100 driver and reference the real one instead. Acked-by: Andy Shevchenko Acked-by: Ilpo Järvinen Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260427-meraki-swnodes-v5-1-ad91cd306472@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/meraki-mx100.c | 41 +++++++++++++---------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2ffa4ecf65b0..2f3e554264a4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -345,6 +345,7 @@ config AYANEO_EC config MERAKI_MX100 tristate "Cisco Meraki MX100 Platform Driver" depends on GPIOLIB + depends on LPC_ICH depends on GPIO_ICH depends on LEDS_CLASS select LEDS_GPIO diff --git a/drivers/platform/x86/meraki-mx100.c b/drivers/platform/x86/meraki-mx100.c index 8c5276d98512..9f4caa1f3a92 100644 --- a/drivers/platform/x86/meraki-mx100.c +++ b/drivers/platform/x86/meraki-mx100.c @@ -20,16 +20,11 @@ #include #include #include +#include #include #include #include -#define TINK_GPIO_DRIVER_NAME "gpio_ich" - -static const struct software_node gpio_ich_node = { - .name = TINK_GPIO_DRIVER_NAME, -}; - /* LEDs */ static const struct software_node tink_gpio_leds_node = { .name = "meraki-mx100-leds", @@ -38,7 +33,7 @@ static const struct software_node tink_gpio_leds_node = { static const struct property_entry tink_internet_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:internet"), PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 11, GPIO_ACTIVE_LOW), { } }; @@ -50,7 +45,7 @@ static const struct software_node tink_internet_led_node = { static const struct property_entry tink_lan2_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 18, GPIO_ACTIVE_HIGH), { } }; @@ -62,7 +57,7 @@ static const struct software_node tink_lan2_led_node = { static const struct property_entry tink_lan3_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 20, GPIO_ACTIVE_HIGH), { } }; @@ -74,7 +69,7 @@ static const struct software_node tink_lan3_led_node = { static const struct property_entry tink_lan4_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 22, GPIO_ACTIVE_HIGH), { } }; @@ -86,7 +81,7 @@ static const struct software_node tink_lan4_led_node = { static const struct property_entry tink_lan5_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 23, GPIO_ACTIVE_HIGH), { } }; @@ -98,7 +93,7 @@ static const struct software_node tink_lan5_led_node = { static const struct property_entry tink_lan6_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 32, GPIO_ACTIVE_HIGH), { } }; @@ -110,7 +105,7 @@ static const struct software_node tink_lan6_led_node = { static const struct property_entry tink_lan7_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 34, GPIO_ACTIVE_HIGH), { } }; @@ -122,7 +117,7 @@ static const struct software_node tink_lan7_led_node = { static const struct property_entry tink_lan8_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 35, GPIO_ACTIVE_HIGH), { } }; @@ -134,7 +129,7 @@ static const struct software_node tink_lan8_led_node = { static const struct property_entry tink_lan9_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 36, GPIO_ACTIVE_HIGH), { } }; @@ -146,7 +141,7 @@ static const struct software_node tink_lan9_led_node = { static const struct property_entry tink_lan10_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 37, GPIO_ACTIVE_HIGH), { } }; @@ -158,7 +153,7 @@ static const struct software_node tink_lan10_led_node = { static const struct property_entry tink_lan11_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 48, GPIO_ACTIVE_HIGH), { } }; @@ -170,7 +165,7 @@ static const struct software_node tink_lan11_led_node = { static const struct property_entry tink_ha_green_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:ha"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 16, GPIO_ACTIVE_LOW), { } }; @@ -182,7 +177,7 @@ static const struct software_node tink_ha_green_led_node = { static const struct property_entry tink_ha_orange_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 7, GPIO_ACTIVE_LOW), { } }; @@ -194,7 +189,7 @@ static const struct software_node tink_ha_orange_led_node = { static const struct property_entry tink_usb_green_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:usb"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 21, GPIO_ACTIVE_LOW), { } }; @@ -206,7 +201,7 @@ static const struct software_node tink_usb_green_led_node = { static const struct property_entry tink_usb_orange_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 19, GPIO_ACTIVE_LOW), { } }; @@ -230,7 +225,7 @@ static const struct software_node tink_gpio_keys_node = { static const struct property_entry tink_reset_key_props[] = { PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), PROPERTY_ENTRY_STRING("label", "Reset"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 60, GPIO_ACTIVE_LOW), PROPERTY_ENTRY_U32("linux,input-type", EV_KEY), PROPERTY_ENTRY_U32("debounce-interval", 100), { } @@ -243,7 +238,6 @@ static const struct software_node tink_reset_key_node = { }; static const struct software_node *tink_swnodes[] = { - &gpio_ich_node, /* LEDs nodes */ &tink_gpio_leds_node, &tink_internet_led_node, @@ -348,3 +342,4 @@ MODULE_AUTHOR("Chris Blake "); MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:meraki-mx100"); +MODULE_IMPORT_NS("LPC_ICH"); From 74ab7c4d73d526bfebc6f3fd7382c6f672d8b912 Mon Sep 17 00:00:00 2001 From: Eduardo Vasconcelos Date: Sat, 25 Apr 2026 03:39:34 -0300 Subject: [PATCH 0254/5214] platform/x86: thinkpad_acpi: Remove unneeded goto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove an unneeded goto statement in hotkey_kthread(). Since the function has a single exit location with no cleanup code, the jump provides no benefit. Per the kernel coding style, returning directly is preferred over goto in such case [1]. [1] https://www.kernel.org/doc/html/latest/process/coding-style.html Signed-off-by: Eduardo Vasconcelos Tested-by: Eduardo Vasconcelos Reviewed-by: Mark Pearson Link: https://patch.msgid.link/20260425063936.9360-1-eduardo@eduardovasconcelos.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index e1cee42a1683..d4ed6f1216f2 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -2469,7 +2469,7 @@ static int hotkey_kthread(void *data) bool was_frozen; if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) - goto exit; + return 0; set_freezable(); @@ -2526,7 +2526,6 @@ static int hotkey_kthread(void *data) si ^= 1; } -exit: return 0; } From 10d65b7e1c37681e3eabe52b0ecb499538d32b41 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 3 Apr 2026 12:47:01 -0700 Subject: [PATCH 0255/5214] clk: mvebu: use kzalloc_flex Use a flexible array member to combine kzalloc and kcalloc in one allocation so they can be freed together. Add __counted_by for extra runtime analysis. Move counting variable assignment right after allocation as done by kzalloc_flex with GCC >= 15. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/mvebu/common.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 28f2e1b2a932..0f9ae5f5cefd 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -189,10 +189,10 @@ DEFINE_SPINLOCK(ctrl_gating_lock); struct clk_gating_ctrl { spinlock_t *lock; - struct clk **gates; int num_gates; void __iomem *base; u32 saved_reg; + struct clk *gates[] __counted_by(num_gates); }; static struct clk_gating_ctrl *ctrl; @@ -257,24 +257,21 @@ void __init mvebu_clk_gating_setup(struct device_node *np, clk_put(clk); } - ctrl = kzalloc_obj(*ctrl); + /* Count, allocate, and register clock gates */ + for (n = 0; desc[n].name;) + n++; + + ctrl = kzalloc_flex(*ctrl, gates, n); if (WARN_ON(!ctrl)) goto ctrl_out; + ctrl->num_gates = n; + /* lock must already be initialized */ ctrl->lock = &ctrl_gating_lock; ctrl->base = base; - /* Count, allocate, and register clock gates */ - for (n = 0; desc[n].name;) - n++; - - ctrl->num_gates = n; - ctrl->gates = kzalloc_objs(*ctrl->gates, ctrl->num_gates); - if (WARN_ON(!ctrl->gates)) - goto gates_out; - for (n = 0; n < ctrl->num_gates; n++) { const char *parent = (desc[n].parent) ? desc[n].parent : default_parent; @@ -289,8 +286,6 @@ void __init mvebu_clk_gating_setup(struct device_node *np, register_syscore(&clk_gate_syscore); return; -gates_out: - kfree(ctrl); ctrl_out: iounmap(base); } From 912b81234adc32b673d9c8f40e21005693bbd2d0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 31 Mar 2026 19:35:51 -0700 Subject: [PATCH 0256/5214] clk: hisilicon: clkdivider-hi6220: use kzalloc_flex Combine allocations using kzalloc_flex and a flexible array member. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/hisilicon/clkdivider-hi6220.c | 26 ++++++++--------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c index 1787ecefe601..20a337383a1e 100644 --- a/drivers/clk/hisilicon/clkdivider-hi6220.c +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -26,8 +26,8 @@ * @shift: shift to the divider bit field * @width: width of the divider bit field * @mask: mask for setting divider rate - * @table: the div table that the divider supports * @lock: register lock + * @table: the div table that the divider supports */ struct hi6220_clk_divider { struct clk_hw hw; @@ -35,8 +35,8 @@ struct hi6220_clk_divider { u8 shift; u8 width; u32 mask; - const struct clk_div_table *table; spinlock_t *lock; + struct clk_div_table table[]; }; #define to_hi6220_clk_divider(_hw) \ @@ -108,24 +108,19 @@ struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, u32 max_div, min_div; int i; - /* allocate the divider */ - div = kzalloc_obj(*div); - if (!div) - return ERR_PTR(-ENOMEM); - /* Init the divider table */ max_div = div_mask(width) + 1; min_div = 1; - table = kzalloc_objs(*table, max_div + 1); - if (!table) { - kfree(div); + /* allocate the divider */ + div = kzalloc_flex(*div, table, max_div + 1); + if (!div) return ERR_PTR(-ENOMEM); - } for (i = 0; i < max_div; i++) { - table[i].div = min_div + i; - table[i].val = table[i].div - 1; + table = &div->table[i]; + table->div = min_div + i; + table->val = table->div - 1; } init.name = name; @@ -141,14 +136,11 @@ struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, div->mask = mask_bit ? BIT(mask_bit) : 0; div->lock = lock; div->hw.init = &init; - div->table = table; /* register the clock */ clk = clk_register(dev, &div->hw); - if (IS_ERR(clk)) { - kfree(table); + if (IS_ERR(clk)) kfree(div); - } return clk; } From 4758f1354f00abf1d97d2f85a016123c78c1f548 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 25 Mar 2026 21:23:17 -0700 Subject: [PATCH 0257/5214] clk: visconti: pll: use kzalloc_flex Simplify allocation by using a flexible array member and kzalloc_flex. Add __counted_by for extra runtime analysis. Assign after allocation as required by __counted_by. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Acked-by: Nobuhiro Iwamatsu Signed-off-by: Stephen Boyd --- drivers/clk/visconti/pll.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/clk/visconti/pll.c b/drivers/clk/visconti/pll.c index 805b95481281..3ce1a906fb0c 100644 --- a/drivers/clk/visconti/pll.c +++ b/drivers/clk/visconti/pll.c @@ -21,9 +21,9 @@ struct visconti_pll { void __iomem *pll_base; spinlock_t *lock; unsigned long flags; - const struct visconti_pll_rate_table *rate_table; size_t rate_count; struct visconti_pll_provider *ctx; + struct visconti_pll_rate_table rate_table[] __counted_by(rate_count); }; #define PLL_CONF_REG 0x0000 @@ -255,10 +255,6 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, size_t len; int ret; - pll = kzalloc_obj(*pll); - if (!pll) - return ERR_PTR(-ENOMEM); - init.name = name; init.flags = CLK_IGNORE_UNUSED; init.parent_names = &parent_name; @@ -266,11 +262,13 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, for (len = 0; rate_table[len].rate != 0; ) len++; + + pll = kzalloc_flex(*pll, rate_table, len); + if (!pll) + return ERR_PTR(-ENOMEM); + pll->rate_count = len; - pll->rate_table = kmemdup_array(rate_table, - pll->rate_count, sizeof(*pll->rate_table), - GFP_KERNEL); - WARN(!pll->rate_table, "%s: could not allocate rate table for %s\n", __func__, name); + memcpy(pll->rate_table, rate_table, len * sizeof(*pll->rate_table)); init.ops = &visconti_pll_ops; pll->hw.init = &init; @@ -282,7 +280,6 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, ret = clk_hw_register(NULL, &pll->hw); if (ret) { pr_err("failed to register pll clock %s : %d\n", name, ret); - kfree(pll->rate_table); kfree(pll); pll_hw_clk = ERR_PTR(ret); } From 4266035a40b6f27f82b5f85dd4e8e23f9a0644a2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 25 Mar 2026 22:28:47 -0700 Subject: [PATCH 0258/5214] clk: clk-max77686: kzalloc + kcalloc to kzalloc Simplify allocation by using a flexible array member to combine allocations. Use struct_size to calculate size properly. Use __counted_by to get extra runtime analysis. Assign counting variable right after allocation as required by __counted_by. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/clk-max77686.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3727d5472450..9149ce4f702d 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -47,8 +47,8 @@ struct max77686_clk_init_data { struct max77686_clk_driver_data { enum max77686_chip_name chip; - struct max77686_clk_init_data *max_clk_data; size_t num_clks; + struct max77686_clk_init_data max_clk_data[] __counted_by(num_clks); }; static const struct @@ -168,19 +168,7 @@ static int max77686_clk_probe(struct platform_device *pdev) struct regmap *regmap; int i, ret, num_clks; - drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); - if (!drv_data) - return -ENOMEM; - - regmap = dev_get_regmap(parent, NULL); - if (!regmap) { - dev_err(dev, "Failed to get rtc regmap\n"); - return -ENODEV; - } - - drv_data->chip = id->driver_data; - - switch (drv_data->chip) { + switch (id->driver_data) { case CHIP_MAX77686: num_clks = MAX77686_CLKS_NUM; hw_clks = max77686_hw_clks_info; @@ -201,13 +189,19 @@ static int max77686_clk_probe(struct platform_device *pdev) return -EINVAL; } - drv_data->num_clks = num_clks; - drv_data->max_clk_data = devm_kcalloc(dev, num_clks, - sizeof(*drv_data->max_clk_data), - GFP_KERNEL); - if (!drv_data->max_clk_data) + drv_data = devm_kzalloc(dev, struct_size(drv_data, max_clk_data, num_clks), GFP_KERNEL); + if (!drv_data) return -ENOMEM; + drv_data->num_clks = num_clks; + drv_data->chip = id->driver_data; + + regmap = dev_get_regmap(parent, NULL); + if (!regmap) { + dev_err(dev, "Failed to get rtc regmap\n"); + return -ENODEV; + } + for (i = 0; i < num_clks; i++) { struct max77686_clk_init_data *max_clk_data; const char *clk_name; From 6f1791ca46f2d1e20f3b490b5358a2e6802c3834 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 25 Mar 2026 21:53:24 -0700 Subject: [PATCH 0259/5214] clk: bcm: iproc-asiu: simplify allocation Use kzalloc_flex and a flexible array member to combine allocations While at it, take clk_data out of the struct and move it into probe. It's not used anywhere else. Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-iproc-asiu.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c index 819ab1b55df3..56a813227981 100644 --- a/drivers/clk/bcm/clk-iproc-asiu.c +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -27,8 +27,7 @@ struct iproc_asiu { void __iomem *div_base; void __iomem *gate_base; - struct clk_hw_onecell_data *clk_data; - struct iproc_asiu_clk *clks; + struct iproc_asiu_clk clks[]; }; #define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw) @@ -184,22 +183,19 @@ void __init iproc_asiu_setup(struct device_node *node, { int i, ret; struct iproc_asiu *asiu; + struct clk_hw_onecell_data *clk_data; if (WARN_ON(!gate || !div)) return; - asiu = kzalloc_obj(*asiu); + asiu = kzalloc_flex(*asiu, clks, num_clks); if (WARN_ON(!asiu)) return; - asiu->clk_data = kzalloc_flex(*asiu->clk_data, hws, num_clks); - if (WARN_ON(!asiu->clk_data)) + clk_data = kzalloc_flex(*clk_data, hws, num_clks); + if (WARN_ON(!clk_data)) goto err_clks; - asiu->clk_data->num = num_clks; - - asiu->clks = kzalloc_objs(*asiu->clks, num_clks); - if (WARN_ON(!asiu->clks)) - goto err_asiu_clks; + clk_data->num = num_clks; asiu->div_base = of_iomap(node, 0); if (WARN_ON(!asiu->div_base)) @@ -236,11 +232,11 @@ void __init iproc_asiu_setup(struct device_node *node, ret = clk_hw_register(NULL, &asiu_clk->hw); if (WARN_ON(ret)) goto err_clk_register; - asiu->clk_data->hws[i] = &asiu_clk->hw; + clk_data->hws[i] = &asiu_clk->hw; } ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, - asiu->clk_data); + clk_data); if (WARN_ON(ret)) goto err_clk_register; @@ -248,17 +244,14 @@ void __init iproc_asiu_setup(struct device_node *node, err_clk_register: while (--i >= 0) - clk_hw_unregister(asiu->clk_data->hws[i]); + clk_hw_unregister(clk_data->hws[i]); iounmap(asiu->gate_base); err_iomap_gate: iounmap(asiu->div_base); err_iomap_div: - kfree(asiu->clks); - -err_asiu_clks: - kfree(asiu->clk_data); + kfree(clk_data); err_clks: kfree(asiu); From 2ceaa8b93b034eb4620fbbbbeea6dbf4c4281745 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 22 Apr 2026 14:53:12 +0200 Subject: [PATCH 0260/5214] clk: bulk: Use dev_err_probe() helper in of_clk_bulk_get() pr_err() can be replace with dev_err_probe() which will check if error code is -EPROBE_DEFER. For that the dev pointer has to be passed along. Signed-off-by: Alexander Stein Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/clk-bulk.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c index d85dae4bdf89..acd9dff30072 100644 --- a/drivers/clk/clk-bulk.c +++ b/drivers/clk/clk-bulk.c @@ -12,7 +12,8 @@ #include #include -static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks, +static int __must_check of_clk_bulk_get(struct device *dev, + struct device_node *np, int num_clks, struct clk_bulk_data *clks) { int ret; @@ -28,8 +29,8 @@ static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks, clks[i].clk = of_clk_get(np, i); if (IS_ERR(clks[i].clk)) { ret = PTR_ERR(clks[i].clk); - pr_err("%pOF: Failed to get clk index: %d ret: %d\n", - np, i, ret); + dev_err_probe(dev, ret, "%pOF: Failed to get clk index: %d (%s)\n", + np, i, clks[i].id); clks[i].clk = NULL; goto err; } @@ -43,7 +44,8 @@ static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks, return ret; } -static int __must_check of_clk_bulk_get_all(struct device_node *np, +static int __must_check of_clk_bulk_get_all(struct device *dev, + struct device_node *np, struct clk_bulk_data **clks) { struct clk_bulk_data *clk_bulk; @@ -58,7 +60,7 @@ static int __must_check of_clk_bulk_get_all(struct device_node *np, if (!clk_bulk) return -ENOMEM; - ret = of_clk_bulk_get(np, num_clks, clk_bulk); + ret = of_clk_bulk_get(dev, np, num_clks, clk_bulk); if (ret) { kfree(clk_bulk); return ret; @@ -144,7 +146,7 @@ int __must_check clk_bulk_get_all(struct device *dev, if (!np) return 0; - return of_clk_bulk_get_all(np, clks); + return of_clk_bulk_get_all(dev, np, clks); } EXPORT_SYMBOL(clk_bulk_get_all); From 820ea6936b2d64d2171747ab37780ec9458a9236 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 26 Mar 2026 05:09:35 +0000 Subject: [PATCH 0261/5214] clk: mediatek: add MUX_CLR_SET macro Some MediaTek SoCs (e.g. MT7988) define infra muxes that have neither a clock gate nor an update register. Add a MUX_CLR_SET convenience macro that takes only the mux register offsets, bit shift, and width, hardcoding upd_ofs = 0 and upd_shift = -1 so callers cannot accidentally pass bogus sentinel values to wrongly-typed fields. Signed-off-by: Daniel Golle Reviewed-by: Chen-Yu Tsai Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mux.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/mediatek/clk-mux.h b/drivers/clk/mediatek/clk-mux.h index 151e56dcf884..1a9baf306b4a 100644 --- a/drivers/clk/mediatek/clk-mux.h +++ b/drivers/clk/mediatek/clk-mux.h @@ -126,6 +126,11 @@ extern const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops; 0, _upd_ofs, _upd, CLK_SET_RATE_PARENT, \ mtk_mux_clr_set_upd_ops) +#define MUX_CLR_SET(_id, _name, _parents, _mux_ofs, \ + _mux_set_ofs, _mux_clr_ofs, _shift, _width) \ + MUX_CLR_SET_UPD(_id, _name, _parents, _mux_ofs, \ + _mux_set_ofs, _mux_clr_ofs, _shift, _width, 0, -1) + #define MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \ _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \ From aec2d3caae24216a8cd530aff7bc7528bd1531b2 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 26 Mar 2026 05:10:47 +0000 Subject: [PATCH 0262/5214] clk: mediatek: mt8192: use MUX_CLR_SET The mfg_pll_sel mux has neither a clock gate nor an update register, and upd_ofs is stored as u32, so the -1 truncates to 0xFFFFFFFF. While upd_shift being -1 (as s8) prevents the update path from executing at runtime, the bogus upd_ofs value is still stored in the struct. Use MUX_CLR_SET to avoid passing sentinel values to wrongly-typed fields. Fixes: 710573dee31b4 ("clk: mediatek: Add MT8192 basic clocks support") Signed-off-by: Daniel Golle Reviewed-by: Chen-Yu Tsai Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mt8192.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c index 50b43807c60c..12c8890d922f 100644 --- a/drivers/clk/mediatek/clk-mt8192.c +++ b/drivers/clk/mediatek/clk-mt8192.c @@ -579,8 +579,8 @@ static const struct mtk_mux top_mtk_muxes[] = { dsp7_parents, 0x050, 0x054, 0x058, 0, 3, 7, 0x004, 16), MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel", mfg_ref_parents, 0x050, 0x054, 0x058, 16, 2, 23, 0x004, 18), - MUX_CLR_SET_UPD(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel", - mfg_pll_parents, 0x050, 0x054, 0x058, 18, 1, -1, -1), + MUX_CLR_SET(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel", mfg_pll_parents, + 0x050, 0x054, 0x058, 18, 1), MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x050, 0x054, 0x058, 24, 3, 31, 0x004, 19), /* CLK_CFG_5 */ From 845a025a9436d40517b8fab0baea2d6157e0a552 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 26 Mar 2026 05:11:12 +0000 Subject: [PATCH 0263/5214] clk: mediatek: mt7988: use MUX_CLR_SET for gate-less muxes All 19 muxes in the infra_muxes[] array are pure mux selectors without a clock gate or update register, yet they were defined using MUX_GATE_CLR_SET_UPD with gate_shift = -1. This macro assigns mtk_mux_gate_clr_set_upd_ops, whose enable/disable/is_enabled callbacks perform BIT(gate_shift). Since gate_shift is stored as u8, the -1 truncates to 255, causing a shift-out-of-bounds at runtime: UBSAN: shift-out-of-bounds in drivers/clk/mediatek/clk-mux.c:76:8 shift exponent 255 is too large for 64-bit type 'long unsigned int' UBSAN: shift-out-of-bounds in drivers/clk/mediatek/clk-mux.c:102:4 shift exponent 255 is too large for 64-bit type 'long unsigned int' UBSAN: shift-out-of-bounds in drivers/clk/mediatek/clk-mux.c:122:16 shift exponent 255 is too large for 64-bit type 'long unsigned int' Switch these definitions to MUX_CLR_SET, which uses mtk_mux_clr_set_upd_ops (no gate callbacks) and does not require callers to pass sentinel values for unused update register fields. The actual clock gating for these peripherals is handled by the separate GATE_INFRA* definitions further down. Fixes: 4b4719437d85f ("clk: mediatek: add drivers for MT7988 SoC") Signed-off-by: Daniel Golle Reviewed-by: Chen-Yu Tsai Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mt7988-infracfg.c | 80 ++++++++++------------ 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/drivers/clk/mediatek/clk-mt7988-infracfg.c b/drivers/clk/mediatek/clk-mt7988-infracfg.c index ef8267319d91..13ffa9d88e24 100644 --- a/drivers/clk/mediatek/clk-mt7988-infracfg.c +++ b/drivers/clk/mediatek/clk-mt7988-infracfg.c @@ -56,49 +56,45 @@ static const char *const infra_pcie_gfmux_tl_ck_o_p3_parents[] __initconst = { static const struct mtk_mux infra_muxes[] = { /* MODULE_CLK_SEL_0 */ - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel", - infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel", - infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel", - infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", infra_mux_spi0_parents, - 0x0018, 0x0010, 0x0014, 4, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", infra_mux_spi1_parents, - 0x0018, 0x0010, 0x0014, 5, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", infra_mux_spi0_parents, - 0x0018, 0x0010, 0x0014, 6, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_SEL, "infra_pwm_sel", infra_pwm_bck_parents, 0x0018, - 0x0010, 0x0014, 14, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 16, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 18, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 20, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 22, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 24, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 26, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 28, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 30, 2, -1, -1, -1), + MUX_CLR_SET(CLK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel", + infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1), + MUX_CLR_SET(CLK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel", + infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, 1), + MUX_CLR_SET(CLK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel", + infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", + infra_mux_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 6, 1), + MUX_CLR_SET(CLK_INFRA_PWM_SEL, "infra_pwm_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 14, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 16, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 18, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 20, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 22, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 24, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 26, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 28, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 30, 2), /* MODULE_CLK_SEL_1 */ - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, "infra_pcie_gfmux_tl_o_p0_sel", - infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, 0x0020, 0x0024, 0, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, "infra_pcie_gfmux_tl_o_p1_sel", - infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, 0x0020, 0x0024, 2, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, "infra_pcie_gfmux_tl_o_p2_sel", - infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, 0x0020, 0x0024, 4, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, "infra_pcie_gfmux_tl_o_p3_sel", - infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, 0x0020, 0x0024, 6, 2, -1, - -1, -1), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, "infra_pcie_gfmux_tl_o_p0_sel", + infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, 0x0020, 0x0024, 0, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, "infra_pcie_gfmux_tl_o_p1_sel", + infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, 0x0020, 0x0024, 2, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, "infra_pcie_gfmux_tl_o_p2_sel", + infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, 0x0020, 0x0024, 4, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, "infra_pcie_gfmux_tl_o_p3_sel", + infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, 0x0020, 0x0024, 6, 2), }; static const struct mtk_gate_regs infra0_cg_regs = { From 85ad455f5ff742ece8e361aa1d9269194539f5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Tue, 14 Apr 2026 21:51:50 +0200 Subject: [PATCH 0264/5214] dt-bindings: clock: marvell,pxa1908: Add #reset-cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The APBC and APBCP controllers have reset lines exposed. Give them a #reset-cells so that they may be used as reset controllers. Signed-off-by: Duje Mihanović Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- .../bindings/clock/marvell,pxa1908.yaml | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml b/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml index 6f3a8578fe2a..0db5504013d5 100644 --- a/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml +++ b/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml @@ -37,6 +37,9 @@ properties: '#power-domain-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg @@ -44,16 +47,27 @@ required: additionalProperties: false -if: - not: - properties: - compatible: - contains: - const: marvell,pxa1908-apmu - -then: - properties: - '#power-domain-cells': false +allOf: + - if: + not: + properties: + compatible: + contains: + const: marvell,pxa1908-apmu + then: + properties: + '#power-domain-cells': false + - if: + not: + properties: + compatible: + contains: + enum: + - marvell,pxa1908-apbc + - marvell,pxa1908-apbcp + then: + properties: + '#reset-cells': false examples: # APMU block: From fcfae77b39cea1e129f86eb88bfd6ca34506effe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Tue, 14 Apr 2026 21:51:51 +0200 Subject: [PATCH 0265/5214] clk: mmp: pxa1908-apbc: Add reset cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been concluded by comparing the gate clock masks and vendor code between PXA1908/28 that PXA1908's APBC, similarly to PXA1928's APBC, has controllable reset lines. Describe these in the driver for correctness. Signed-off-by: Duje Mihanović Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-pxa1908-apbc.c | 58 ++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/drivers/clk/mmp/clk-pxa1908-apbc.c b/drivers/clk/mmp/clk-pxa1908-apbc.c index 3fd7b5e644f3..438ece4f047d 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbc.c +++ b/drivers/clk/mmp/clk-pxa1908-apbc.c @@ -7,6 +7,7 @@ #include #include "clk.h" +#include "reset.h" #define APBC_UART0 0x0 #define APBC_UART1 0x4 @@ -44,22 +45,25 @@ static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"}; static const char * const ssp_parent_names[] = {"pll1_d16", "pll1_d48", "pll1_d24", "pll1_d12"}; static struct mmp_param_gate_clk apbc_gate_clks[] = { - {PXA1908_CLK_TWSI0, "twsi0_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_TWSI1, "twsi1_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_TWSI3, "twsi3_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x7, 3, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, - {PXA1908_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x87, 0x83, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_TWSI0, "twsi0_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_TWSI1, "twsi1_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_TWSI3, "twsi3_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 3, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x83, 0x83, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_PWM1, "pwm1_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM1, 0x2, 2, 0, 0, NULL}, + {PXA1908_CLK_PWM3, "pwm3_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM3, 0x2, 2, 0, 0, NULL}, + {PXA1908_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x3, 3, 0, 0, &uart0_lock}, + {PXA1908_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 3, 0, 0, &uart1_lock}, + {PXA1908_CLK_THERMAL, "thermal_clk", NULL, 0, APBC_THERMAL, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_IPC_RST, "ipc_clk", NULL, 0, APBC_IPC_RST, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_SSP0, "ssp0_clk", "ssp0_mux", 0, APBC_SSP0, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_SSP2, "ssp2_clk", "ssp2_mux", 0, APBC_SSP2, 0x3, 3, 0, 0, NULL}, +}; + +static struct mmp_param_gate_clk apbc_gate_no_reset_clks[] = { {PXA1908_CLK_PWM0, "pwm0_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM0, 0x2, 2, 0, 0, &pwm0_lock}, - {PXA1908_CLK_PWM1, "pwm1_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM1, 0x6, 2, 0, 0, NULL}, {PXA1908_CLK_PWM2, "pwm2_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM2, 0x2, 2, 0, 0, NULL}, - {PXA1908_CLK_PWM3, "pwm3_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM3, 0x6, 2, 0, 0, NULL}, - {PXA1908_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x7, 3, 0, 0, &uart0_lock}, - {PXA1908_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x7, 3, 0, 0, &uart1_lock}, - {PXA1908_CLK_THERMAL, "thermal_clk", NULL, 0, APBC_THERMAL, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_IPC_RST, "ipc_clk", NULL, 0, APBC_IPC_RST, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_SSP0, "ssp0_clk", "ssp0_mux", 0, APBC_SSP0, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_SSP2, "ssp2_clk", "ssp2_mux", 0, APBC_SSP2, 0x7, 3, 0, 0, NULL}, }; static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -89,6 +93,30 @@ static void pxa1908_apb_periph_clk_init(struct pxa1908_clk_unit *pxa_unit) ARRAY_SIZE(apbc_mux_clks)); mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->base, ARRAY_SIZE(apbc_gate_clks)); + mmp_register_gate_clks(unit, apbc_gate_no_reset_clks, pxa_unit->base, + ARRAY_SIZE(apbc_gate_no_reset_clks)); +} + +/* Taken from clk-of-pxa1928.c */ +static void pxa1908_clk_reset_init(struct device_node *np, + struct pxa1908_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int nr_cells = ARRAY_SIZE(apbc_gate_clks); + + cells = kzalloc_objs(*cells, nr_cells); + if (!cells) + return; + + for (int i = 0; i < nr_cells; i++) { + cells[i].clk_id = apbc_gate_clks[i].id; + cells[i].reg = pxa_unit->base + apbc_gate_clks[i].offset; + cells[i].bits = BIT(2); + cells[i].flags = 0; + cells[i].lock = apbc_gate_clks[i].lock; + }; + + mmp_clk_reset_register(np, cells, nr_cells); } static int pxa1908_apbc_probe(struct platform_device *pdev) @@ -107,6 +135,8 @@ static int pxa1908_apbc_probe(struct platform_device *pdev) pxa1908_apb_periph_clk_init(pxa_unit); + pxa1908_clk_reset_init(pdev->dev.of_node, pxa_unit); + return 0; } From 8267609a380f3f0f47c1e0d13440cb87b3a65d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Tue, 14 Apr 2026 21:51:52 +0200 Subject: [PATCH 0266/5214] clk: mmp: pxa1908-apbcp: Add reset cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been concluded by comparing the gate clock masks and vendor code between PXA1908/28 that PXA1908's APBCP, similarly to PXA1928's APBC, has controllable reset lines. Describe these in the driver for correctness. Signed-off-by: Duje Mihanović Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-pxa1908-apbcp.c | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/clk/mmp/clk-pxa1908-apbcp.c b/drivers/clk/mmp/clk-pxa1908-apbcp.c index f638d7e89b47..1aa476103553 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbcp.c +++ b/drivers/clk/mmp/clk-pxa1908-apbcp.c @@ -7,6 +7,7 @@ #include #include "clk.h" +#include "reset.h" #define APBCP_UART2 0x1c #define APBCP_TWSI2 0x28 @@ -24,9 +25,9 @@ static DEFINE_SPINLOCK(uart2_lock); static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"}; static struct mmp_param_gate_clk apbcp_gate_clks[] = { - {PXA1908_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x7, 0x3, 0x0, 0, &uart2_lock}, - {PXA1908_CLK_TWSI2, "twsi2_clk", "pll1_32", CLK_SET_RATE_PARENT, APBCP_TWSI2, 0x7, 0x3, 0x0, 0, NULL}, - {PXA1908_CLK_AICER, "ripc_clk", NULL, 0, APBCP_AICER, 0x7, 0x2, 0x0, 0, NULL}, + {PXA1908_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA1908_CLK_TWSI2, "twsi2_clk", "pll1_32", CLK_SET_RATE_PARENT, APBCP_TWSI2, 0x3, 0x3, 0x0, 0, NULL}, + {PXA1908_CLK_AICER, "ripc_clk", NULL, 0, APBCP_AICER, 0x3, 0x2, 0x0, 0, NULL}, }; static struct mmp_param_mux_clk apbcp_mux_clks[] = { @@ -43,6 +44,28 @@ static void pxa1908_apb_p_periph_clk_init(struct pxa1908_clk_unit *pxa_unit) ARRAY_SIZE(apbcp_gate_clks)); } +/* Taken from clk-of-pxa1928.c */ +static void pxa1908_clk_reset_init(struct device_node *np, + struct pxa1908_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int nr_cells = ARRAY_SIZE(apbcp_gate_clks); + + cells = kzalloc_objs(*cells, nr_cells); + if (!cells) + return; + + for (int i = 0; i < nr_cells; i++) { + cells[i].clk_id = apbcp_gate_clks[i].id; + cells[i].reg = pxa_unit->base + apbcp_gate_clks[i].offset; + cells[i].bits = BIT(2); + cells[i].flags = 0; + cells[i].lock = apbcp_gate_clks[i].lock; + }; + + mmp_clk_reset_register(np, cells, nr_cells); +} + static int pxa1908_apbcp_probe(struct platform_device *pdev) { struct pxa1908_clk_unit *pxa_unit; @@ -59,6 +82,8 @@ static int pxa1908_apbcp_probe(struct platform_device *pdev) pxa1908_apb_p_periph_clk_init(pxa_unit); + pxa1908_clk_reset_init(pdev->dev.of_node, pxa_unit); + return 0; } From 0aef2f0db6db22c2a441e067d8e8458106fb0483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:29:04 +0100 Subject: [PATCH 0267/5214] clk: clk-axi-clkgen: Add support versal timings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add proper VCO and PFD limits for versal based platforms. For that we need to add new Technology and Speed grade defines. Signed-off-by: Nuno Sá Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/clk-axi-clkgen.c | 5 ++++- include/linux/adi-axi-common.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index fa5ccef73e60..26f76a6db820 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -521,7 +521,7 @@ static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, axi_clkgen->limits.fvco_max = 1200000; axi_clkgen->limits.fpfd_max = 450000; break; - case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2LV: + case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2MP: axi_clkgen->limits.fvco_max = 1440000; axi_clkgen->limits.fpfd_max = 500000; if (family == ADI_AXI_FPGA_FAMILY_KINTEX || family == ADI_AXI_FPGA_FAMILY_ARTIX) { @@ -546,6 +546,9 @@ static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, if (tech == ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS) { axi_clkgen->limits.fvco_max = 1600000; axi_clkgen->limits.fvco_min = 800000; + } else if (tech == ADI_AXI_FPGA_TECH_VERSAL) { + axi_clkgen->limits.fvco_max = 4320000; + axi_clkgen->limits.fvco_min = 2160000; } return 0; diff --git a/include/linux/adi-axi-common.h b/include/linux/adi-axi-common.h index 37962ba530df..e7ba393061ee 100644 --- a/include/linux/adi-axi-common.h +++ b/include/linux/adi-axi-common.h @@ -51,6 +51,7 @@ enum adi_axi_fpga_technology { ADI_AXI_FPGA_TECH_SERIES7, ADI_AXI_FPGA_TECH_ULTRASCALE, ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS, + ADI_AXI_FPGA_TECH_VERSAL, }; enum adi_axi_fpga_family { @@ -71,6 +72,7 @@ enum adi_axi_fpga_speed_grade { ADI_AXI_FPGA_SPEED_2 = 20, ADI_AXI_FPGA_SPEED_2L = 21, ADI_AXI_FPGA_SPEED_2LV = 22, + ADI_AXI_FPGA_SPEED_2MP = 23, ADI_AXI_FPGA_SPEED_3 = 30, }; From 9a51504d7c01b5279b2e787fa80bcb0b49c3f123 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 5 Mar 2026 19:25:40 -0800 Subject: [PATCH 0268/5214] clk-lpc18xx-ccu: kzalloc + kcalloc to kzalloc_flex Simplifies allocation by using a flexible array member. Also allows using __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Reviewed-by: Gustavo A. R. Silva Signed-off-by: Stephen Boyd --- drivers/clk/nxp/clk-lpc18xx-ccu.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c index dcb6d0c0b41a..3793e701835f 100644 --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -27,8 +27,8 @@ #define CCU_BRANCH_HAVE_DIV2 BIT(1) struct lpc18xx_branch_clk_data { - const char **name; int num; + const char *name[] __counted_by(num); }; struct lpc18xx_clk_branch { @@ -266,6 +266,7 @@ static void __init lpc18xx_ccu_init(struct device_node *np) { struct lpc18xx_branch_clk_data *clk_data; void __iomem *reg_base; + size_t size; int i, ret; reg_base = of_iomap(np, 0); @@ -274,19 +275,14 @@ static void __init lpc18xx_ccu_init(struct device_node *np) return; } - clk_data = kzalloc_obj(*clk_data); + size = of_property_count_strings(np, "clock-names"); + clk_data = kzalloc_flex(*clk_data, name, size); if (!clk_data) { iounmap(reg_base); return; } - clk_data->num = of_property_count_strings(np, "clock-names"); - clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); - if (!clk_data->name) { - iounmap(reg_base); - kfree(clk_data); - return; - } + clk_data->num = size; for (i = 0; i < clk_data->num; i++) { ret = of_property_read_string_index(np, "clock-names", i, From 7b03559044d20bb4cfa1a19df79dd1b52e27fb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Tue, 3 Mar 2026 16:25:18 +0100 Subject: [PATCH 0269/5214] clk: hisilicon: Improve deallocation in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unmap 'base' if an error occurs after it has been mapped. Reported-by: Tao Lan Closes: https://lore.kernel.org/lkml/ZNlSH+eWV8Sk3FYn@probook/ Signed-off-by: J. Neuschäfer Reviewed-by: Brian Masney Signed-off-by: Stephen Boyd --- drivers/clk/hisilicon/clk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index fae65127cd4a..08050ff1c8cf 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -70,7 +70,7 @@ struct hisi_clock_data *hisi_clk_init(struct device_node *np, clk_data = kzalloc_obj(*clk_data); if (!clk_data) - goto err; + goto err_base; clk_data->base = base; clk_table = kzalloc_objs(*clk_table, nr_clks); @@ -83,6 +83,8 @@ struct hisi_clock_data *hisi_clk_init(struct device_node *np, return clk_data; err_data: kfree(clk_data); +err_base: + iounmap(base); err: return NULL; } From e05a76ae1507d19d62eb0011592be9efb42e06cd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:42 +0900 Subject: [PATCH 0270/5214] firewire: core: code refactoring for early return at client resource allocation The add_client_resource() function returns zero at success or negative value at error. The critical section is already protected by scoped_guard() macro. In this case, the programming pattern of early return improves code readability. Link: https://lore.kernel.org/r/20260429093449.160545-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f791db4c8dff..144625c34be2 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -507,31 +507,30 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) static int add_client_resource(struct client *client, struct client_resource *resource, gfp_t gfp_mask) { - int ret; - scoped_guard(spinlock_irqsave, &client->lock) { u32 index; + int ret; - if (client->in_shutdown) { - ret = -ECANCELED; + if (client->in_shutdown) + return -ECANCELED; + + if (gfpflags_allow_blocking(gfp_mask)) { + ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b, + GFP_NOWAIT); } else { - if (gfpflags_allow_blocking(gfp_mask)) { - ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b, - GFP_NOWAIT); - } else { - ret = xa_alloc_bh(&client->resource_xa, &index, resource, - xa_limit_32b, GFP_NOWAIT); - } - } - if (ret >= 0) { - resource->handle = index; - client_get(client); - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); + ret = xa_alloc_bh(&client->resource_xa, &index, resource, + xa_limit_32b, GFP_NOWAIT); } + if (ret < 0) + return ret; + + resource->handle = index; + client_get(client); + if (is_iso_resource(resource)) + schedule_iso_resource(to_iso_resource(resource), 0); } - return ret < 0 ? ret : 0; + return 0; } static int release_client_resource(struct client *client, u32 handle, From 38fb1154185dfdd8ac2162da1da688f16ed66b9b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:43 +0900 Subject: [PATCH 0271/5214] firewire: core: code refactoring to queue work item for iso_resource The add_client_resource() function checks the type of client resource every time to be called. If the type is for iso_resource, it schedules work item. However, the iso_resource client resource is only added by the call of init_iso_resource(). There is no need to check the type every time adding any client resource. Link: https://lore.kernel.org/r/20260429093449.160545-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 144625c34be2..8391c7efab2c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -526,8 +526,6 @@ static int add_client_resource(struct client *client, struct client_resource *re resource->handle = index; client_get(client); - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); } return 0; @@ -1438,8 +1436,9 @@ static int init_iso_resource(struct client *client, } else { r->resource.release = NULL; r->resource.handle = -1; - schedule_iso_resource(r, 0); } + schedule_iso_resource(r, 0); + request->handle = r->resource.handle; return 0; From e698cec3117fb26fc2550cd0858174484dd90cc2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:44 +0900 Subject: [PATCH 0272/5214] firewire: core: code refactoring for helper function to fill iso_resource parameters This change is a preparation for future changes. The added helper function will be reused in the changes to fill iso_resource parameters according to the users' request. Link: https://lore.kernel.org/r/20260429093449.160545-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 45 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 8391c7efab2c..effa03739679 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -128,6 +128,12 @@ struct descriptor_resource { u32 data[]; }; +struct iso_resource_params { + int generation; + u64 channels; + s32 bandwidth; +}; + struct iso_resource { struct client_resource resource; struct client *client; @@ -135,9 +141,7 @@ struct iso_resource { struct delayed_work work; enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; - int generation; - u64 channels; - s32 bandwidth; + struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1290,6 +1294,20 @@ static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) return 0; } +static int fill_iso_resource_params(struct iso_resource_params *params, + struct fw_cdev_allocate_iso_resource *request) +{ + if ((request->channels == 0 && request->bandwidth == 0) || + request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) + return -EINVAL; + + params->generation = -1; + params->channels = request->channels; + params->bandwidth = request->bandwidth; + + return 0; +} + static void iso_resource_work(struct work_struct *work) { struct iso_resource_event *e; @@ -1310,21 +1328,21 @@ static void iso_resource_work(struct work_struct *work) } else { // We could be called twice within the same generation. skip = todo == ISO_RES_REALLOC && - r->generation == generation; + r->params.generation == generation; } free = todo == ISO_RES_DEALLOC || todo == ISO_RES_ALLOC_ONCE || todo == ISO_RES_DEALLOC_ONCE; - r->generation = generation; + r->params.generation = generation; } if (skip) goto out; - bandwidth = r->bandwidth; + bandwidth = r->params.bandwidth; fw_iso_resource_manage(client->device->card, generation, - r->channels, &channel, &bandwidth, + r->params.channels, &channel, &bandwidth, todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC || todo == ISO_RES_ALLOC_ONCE); @@ -1355,7 +1373,7 @@ static void iso_resource_work(struct work_struct *work) } if (todo == ISO_RES_ALLOC && channel >= 0) - r->channels = 1ULL << channel; + r->params.channels = 1ULL << channel; if (todo == ISO_RES_REALLOC && success) goto out; @@ -1402,10 +1420,6 @@ static int init_iso_resource(struct client *client, struct iso_resource *r; int ret; - if ((request->channels == 0 && request->bandwidth == 0) || - request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) - return -EINVAL; - r = kmalloc_obj(*r); e1 = kmalloc_obj(*e1); e2 = kmalloc_obj(*e2); @@ -1414,12 +1428,13 @@ static int init_iso_resource(struct client *client, goto fail; } + ret = fill_iso_resource_params(&r->params, request); + if (ret < 0) + goto fail; + INIT_DELAYED_WORK(&r->work, iso_resource_work); r->client = client; r->todo = todo; - r->generation = -1; - r->channels = request->channels; - r->bandwidth = request->bandwidth; r->e_alloc = e1; r->e_dealloc = e2; From b3ac3b453b23423e2c713d9ac497ddb0aec9aa7c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:45 +0900 Subject: [PATCH 0273/5214] firewire: core: split functions for iso_resource once operation Unlike FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE operation, the operations of FW_CDEV_IOC_[DE]ALLOCATE_ISO_RESOURCE_ONCE require no client resource, thus they keeps no handle value. This commit adds the series of functions to separate these operations, according to divide-and-conquer methodology. Link: https://lore.kernel.org/r/20260429093449.160545-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 83 ++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index effa03739679..478e8f6400f0 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -145,6 +145,18 @@ struct iso_resource { struct iso_resource_event *e_alloc, *e_dealloc; }; +struct iso_resource_once { + struct client *client; + // Schedule work and access todo only with client->lock held. + struct delayed_work work; + enum { + ISO_RES_ONCE_ALLOC, + ISO_RES_ONCE_DEALLOC, + } todo; + struct iso_resource_params params; + struct iso_resource_event *event; +}; + static struct address_handler_resource *to_address_handler_resource(struct client_resource *resource) { return container_of(resource, struct address_handler_resource, resource); @@ -1479,18 +1491,81 @@ static int ioctl_deallocate_iso_resource(struct client *client, arg->deallocate.handle, release_iso_resource, NULL); } +#define UNAVAILABLE_HANDLE -1 + +static void iso_resource_once_work(struct work_struct *work) +{ + struct iso_resource_once *r = from_work(r, work, work.work); + struct client *client = r->client; + struct iso_resource_event *e = r->event; + int generation, channel, bandwidth; + + scoped_guard(spinlock_irq, &client->lock) + generation = client->device->generation; + + r->params.generation = generation; + bandwidth = r->params.bandwidth; + + fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, + &bandwidth, r->todo == ISO_RES_ONCE_ALLOC); + + e->iso_resource.handle = UNAVAILABLE_HANDLE; + e->iso_resource.channel = channel; + e->iso_resource.bandwidth = bandwidth; + + queue_event(client, &e->event, &e->iso_resource, sizeof(e->iso_resource), NULL, 0); + + cancel_delayed_work(&r->work); + kfree(r); + + client_put(client); +} + +static int init_iso_resource_once(struct client *client, + struct fw_cdev_allocate_iso_resource *request, int todo) +{ + struct iso_resource_event *e __free(kfree) = kmalloc_obj(*e); + struct iso_resource_once *r __free(kfree) = kmalloc_obj(*r); + int err; + + if (!r || !e) + return -ENOMEM; + + err = fill_iso_resource_params(&r->params, request); + if (err < 0) + return err; + + INIT_DELAYED_WORK(&r->work, iso_resource_once_work); + r->client = client; + r->todo = todo; + + if (todo == ISO_RES_ONCE_ALLOC) + e->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; + else + e->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; + e->iso_resource.closure = request->closure; + r->event = no_free_ptr(e); + + // Keep the client until work item finishing. + client_get(r->client); + + queue_delayed_work(fw_workqueue, &no_free_ptr(r)->work, 0); + + request->handle = UNAVAILABLE_HANDLE; + + return 0; +} + static int ioctl_allocate_iso_resource_once(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); + return init_iso_resource_once(client, &arg->allocate_iso_resource, ISO_RES_ONCE_ALLOC); } static int ioctl_deallocate_iso_resource_once(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); + return init_iso_resource_once(client, &arg->allocate_iso_resource, ISO_RES_ONCE_DEALLOC); } /* From cd5f1a1126eeb2f6bd53d45684233e95dff41d82 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:46 +0900 Subject: [PATCH 0274/5214] firewire: core: code cleanup to remove old implementations for once operation The helper functions for iso_resource allocation and work item still include codes for once operation. This commit refactors them to remove the old implementations. Link: https://lore.kernel.org/r/20260429093449.160545-6-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 478e8f6400f0..f81a8aa4bcbc 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -139,8 +139,11 @@ struct iso_resource { struct client *client; /* Schedule work and access todo only with client->lock held. */ struct delayed_work work; - enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, - ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; + enum { + ISO_RES_ALLOC, + ISO_RES_REALLOC, + ISO_RES_DEALLOC, + } todo; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1342,9 +1345,7 @@ static void iso_resource_work(struct work_struct *work) skip = todo == ISO_RES_REALLOC && r->params.generation == generation; } - free = todo == ISO_RES_DEALLOC || - todo == ISO_RES_ALLOC_ONCE || - todo == ISO_RES_DEALLOC_ONCE; + free = todo == ISO_RES_DEALLOC; r->params.generation = generation; } @@ -1356,8 +1357,7 @@ static void iso_resource_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, &bandwidth, todo == ISO_RES_ALLOC || - todo == ISO_RES_REALLOC || - todo == ISO_RES_ALLOC_ONCE); + todo == ISO_RES_REALLOC); /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at @@ -1390,7 +1390,7 @@ static void iso_resource_work(struct work_struct *work) if (todo == ISO_RES_REALLOC && success) goto out; - if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { + if (todo == ISO_RES_ALLOC) { e = r->e_alloc; r->e_alloc = NULL; } else { @@ -1425,8 +1425,7 @@ static void release_iso_resource(struct client *client, schedule_iso_resource(r, 0); } -static int init_iso_resource(struct client *client, - struct fw_cdev_allocate_iso_resource *request, int todo) +static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) { struct iso_resource_event *e1, *e2; struct iso_resource *r; @@ -1446,7 +1445,7 @@ static int init_iso_resource(struct client *client, INIT_DELAYED_WORK(&r->work, iso_resource_work); r->client = client; - r->todo = todo; + r->todo = ISO_RES_ALLOC; r->e_alloc = e1; r->e_dealloc = e2; @@ -1455,15 +1454,10 @@ static int init_iso_resource(struct client *client, e2->iso_resource.closure = request->closure; e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; - if (todo == ISO_RES_ALLOC) { - r->resource.release = release_iso_resource; - ret = add_client_resource(client, &r->resource, GFP_KERNEL); - if (ret < 0) - goto fail; - } else { - r->resource.release = NULL; - r->resource.handle = -1; - } + r->resource.release = release_iso_resource; + ret = add_client_resource(client, &r->resource, GFP_KERNEL); + if (ret < 0) + goto fail; schedule_iso_resource(r, 0); request->handle = r->resource.handle; @@ -1480,8 +1474,7 @@ static int init_iso_resource(struct client *client, static int ioctl_allocate_iso_resource(struct client *client, union ioctl_arg *arg) { - return init_iso_resource(client, - &arg->allocate_iso_resource, ISO_RES_ALLOC); + return init_iso_resource(client, &arg->allocate_iso_resource); } static int ioctl_deallocate_iso_resource(struct client *client, From 48b68337bf6523f16b2afbb0d3e059eb211e3c85 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:47 +0900 Subject: [PATCH 0275/5214] firewire: core: append _auto suffix for non-once iso resource operations The functions for iso_resource once operations are carefully split from another type of operation. This commit adds _auto suffix to functions for the another type so that it is easily to distinguish them. Link: https://lore.kernel.org/r/20260429093449.160545-7-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 75 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f81a8aa4bcbc..b3ce34d777c3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -134,15 +134,15 @@ struct iso_resource_params { s32 bandwidth; }; -struct iso_resource { +struct iso_resource_auto { struct client_resource resource; struct client *client; /* Schedule work and access todo only with client->lock held. */ struct delayed_work work; enum { - ISO_RES_ALLOC, - ISO_RES_REALLOC, - ISO_RES_DEALLOC, + ISO_RES_AUTO_ALLOC, + ISO_RES_AUTO_REALLOC, + ISO_RES_AUTO_DEALLOC, } todo; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; @@ -175,16 +175,16 @@ static struct descriptor_resource *to_descriptor_resource(struct client_resource return container_of(resource, struct descriptor_resource, resource); } -static struct iso_resource *to_iso_resource(struct client_resource *resource) +static struct iso_resource_auto *to_iso_resource_auto(struct client_resource *resource) { - return container_of(resource, struct iso_resource, resource); + return container_of(resource, struct iso_resource_auto, resource); } -static void release_iso_resource(struct client *, struct client_resource *); +static void release_iso_resource_auto(struct client *, struct client_resource *); -static int is_iso_resource(const struct client_resource *resource) +static int is_iso_resource_auto(const struct client_resource *resource) { - return resource->release == release_iso_resource; + return resource->release == release_iso_resource_auto; } static void release_transaction(struct client *client, @@ -195,7 +195,7 @@ static int is_outbound_transaction_resource(const struct client_resource *resour return resource->release == release_transaction; } -static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) +static void schedule_iso_resource_auto(struct iso_resource_auto *r, unsigned long delay) { client_get(r->client); if (!queue_delayed_work(fw_workqueue, &r->work, delay)) @@ -443,8 +443,8 @@ static void queue_bus_reset_event(struct client *client) guard(spinlock_irq)(&client->lock); xa_for_each(&client->resource_xa, index, resource) { - if (is_iso_resource(resource)) - schedule_iso_resource(to_iso_resource(resource), 0); + if (is_iso_resource_auto(resource)) + schedule_iso_resource_auto(to_iso_resource_auto(resource), 0); } } @@ -1323,10 +1323,10 @@ static int fill_iso_resource_params(struct iso_resource_params *params, return 0; } -static void iso_resource_work(struct work_struct *work) +static void iso_resource_auto_work(struct work_struct *work) { struct iso_resource_event *e; - struct iso_resource *r = from_work(r, work, work.work); + struct iso_resource_auto *r = from_work(r, work, work.work); struct client *client = r->client; unsigned long index = r->resource.handle; int generation, channel, bandwidth, todo; @@ -1336,16 +1336,16 @@ static void iso_resource_work(struct work_struct *work) generation = client->device->generation; todo = r->todo; // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_ALLOC && + if (todo == ISO_RES_AUTO_ALLOC && time_is_after_jiffies64(client->device->card->reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource(r, msecs_to_jiffies(333)); + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); skip = true; } else { // We could be called twice within the same generation. - skip = todo == ISO_RES_REALLOC && + skip = todo == ISO_RES_AUTO_REALLOC && r->params.generation == generation; } - free = todo == ISO_RES_DEALLOC; + free = todo == ISO_RES_AUTO_DEALLOC; r->params.generation = generation; } @@ -1356,15 +1356,15 @@ static void iso_resource_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, &bandwidth, - todo == ISO_RES_ALLOC || - todo == ISO_RES_REALLOC); + todo == ISO_RES_AUTO_ALLOC || + todo == ISO_RES_AUTO_REALLOC); /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at * shutdown. */ if (channel == -EAGAIN && - (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) + (todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC)) goto out; success = channel >= 0 || bandwidth > 0; @@ -1372,11 +1372,11 @@ static void iso_resource_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) { // Transit from allocation to reallocation, except if the client // requested deallocation in the meantime. - if (r->todo == ISO_RES_ALLOC) - r->todo = ISO_RES_REALLOC; + if (r->todo == ISO_RES_AUTO_ALLOC) + r->todo = ISO_RES_AUTO_REALLOC; // Allocation or reallocation failure? Pull this resource out of the // xarray and prepare for deletion, unless the client is shutting down. - if (r->todo == ISO_RES_REALLOC && !success && + if (r->todo == ISO_RES_AUTO_REALLOC && !success && !client->in_shutdown && xa_erase(&client->resource_xa, index)) { client_put(client); @@ -1384,13 +1384,13 @@ static void iso_resource_work(struct work_struct *work) } } - if (todo == ISO_RES_ALLOC && channel >= 0) + if (todo == ISO_RES_AUTO_ALLOC && channel >= 0) r->params.channels = 1ULL << channel; - if (todo == ISO_RES_REALLOC && success) + if (todo == ISO_RES_AUTO_REALLOC && success) goto out; - if (todo == ISO_RES_ALLOC) { + if (todo == ISO_RES_AUTO_ALLOC) { e = r->e_alloc; r->e_alloc = NULL; } else { @@ -1414,21 +1414,20 @@ static void iso_resource_work(struct work_struct *work) client_put(client); } -static void release_iso_resource(struct client *client, - struct client_resource *resource) +static void release_iso_resource_auto(struct client *client, struct client_resource *resource) { - struct iso_resource *r = to_iso_resource(resource); + struct iso_resource_auto *r = to_iso_resource_auto(resource); guard(spinlock_irq)(&client->lock); - r->todo = ISO_RES_DEALLOC; - schedule_iso_resource(r, 0); + r->todo = ISO_RES_AUTO_DEALLOC; + schedule_iso_resource_auto(r, 0); } static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) { struct iso_resource_event *e1, *e2; - struct iso_resource *r; + struct iso_resource_auto *r; int ret; r = kmalloc_obj(*r); @@ -1443,9 +1442,9 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ if (ret < 0) goto fail; - INIT_DELAYED_WORK(&r->work, iso_resource_work); + INIT_DELAYED_WORK(&r->work, iso_resource_auto_work); r->client = client; - r->todo = ISO_RES_ALLOC; + r->todo = ISO_RES_AUTO_ALLOC; r->e_alloc = e1; r->e_dealloc = e2; @@ -1454,11 +1453,11 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ e2->iso_resource.closure = request->closure; e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; - r->resource.release = release_iso_resource; + r->resource.release = release_iso_resource_auto; ret = add_client_resource(client, &r->resource, GFP_KERNEL); if (ret < 0) goto fail; - schedule_iso_resource(r, 0); + schedule_iso_resource_auto(r, 0); request->handle = r->resource.handle; @@ -1481,7 +1480,7 @@ static int ioctl_deallocate_iso_resource(struct client *client, union ioctl_arg *arg) { return release_client_resource(client, - arg->deallocate.handle, release_iso_resource, NULL); + arg->deallocate.handle, release_iso_resource_auto, NULL); } #define UNAVAILABLE_HANDLE -1 From 6dbe7653fa01edeefc77b4d7c063562eb3debd48 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 29 Apr 2026 18:34:48 +0900 Subject: [PATCH 0276/5214] firewire: core: code cleanup for iso resource auto creation The init_iso_resource function is only called by ioctl_allocate_iso_resource(), thus no need to be unique. This commit unifies them with minor code refactoring. Link: https://lore.kernel.org/r/20260429093449.160545-8-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 53 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index b3ce34d777c3..bcfb20b770df 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1424,23 +1424,20 @@ static void release_iso_resource_auto(struct client *client, struct client_resou schedule_iso_resource_auto(r, 0); } -static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request) +static int ioctl_allocate_iso_resource(struct client *client, union ioctl_arg *arg) { - struct iso_resource_event *e1, *e2; - struct iso_resource_auto *r; - int ret; + struct fw_cdev_allocate_iso_resource *request = &arg->allocate_iso_resource; + struct iso_resource_event *e1 __free(kfree) = kmalloc_obj(*e1); + struct iso_resource_event *e2 __free(kfree) = kmalloc_obj(*e2); + struct iso_resource_auto *r __free(kfree) = kmalloc_obj(*r); + int err; - r = kmalloc_obj(*r); - e1 = kmalloc_obj(*e1); - e2 = kmalloc_obj(*e2); - if (r == NULL || e1 == NULL || e2 == NULL) { - ret = -ENOMEM; - goto fail; - } + if (!r || !e1 || !e2) + return -ENOMEM; - ret = fill_iso_resource_params(&r->params, request); - if (ret < 0) - goto fail; + err = fill_iso_resource_params(&r->params, request); + if (err < 0) + return err; INIT_DELAYED_WORK(&r->work, iso_resource_auto_work); r->client = client; @@ -1449,31 +1446,21 @@ static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_ r->e_dealloc = e2; e1->iso_resource.closure = request->closure; - e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; + e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; e2->iso_resource.closure = request->closure; - e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; + e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; r->resource.release = release_iso_resource_auto; - ret = add_client_resource(client, &r->resource, GFP_KERNEL); - if (ret < 0) - goto fail; - schedule_iso_resource_auto(r, 0); - + err = add_client_resource(client, &r->resource, GFP_KERNEL); + if (err < 0) + return err; request->handle = r->resource.handle; + retain_and_null_ptr(e1); + retain_and_null_ptr(e2); + schedule_iso_resource_auto(no_free_ptr(r), 0); + return 0; - fail: - kfree(r); - kfree(e1); - kfree(e2); - - return ret; -} - -static int ioctl_allocate_iso_resource(struct client *client, - union ioctl_arg *arg) -{ - return init_iso_resource(client, &arg->allocate_iso_resource); } static int ioctl_deallocate_iso_resource(struct client *client, From 3bb62e3f99a557d257e5f5a803200051b7de3afa Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 28 Apr 2026 21:52:39 -0500 Subject: [PATCH 0277/5214] gpiolib: acpi: Only trigger ActiveBoth interrupts on boot Commit ca876c7483b6 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") introduced logic to trigger edge-based GPIO interrupts during initialization to ensure proper initial state setup when firmware doesn't initialize it. However, according to the Microsoft GPIO documentation, triggering GPIO interrupts during initialization should only happen for interrupts marked as ActiveBoth (both IRQF_TRIGGER_RISING and IRQF_TRIGGER_FALLING) and only when the associated GPIO line is already asserted (logic level low). The current implementation incorrectly triggers: 1. Any edge-triggered interrupt (RISING-only or FALLING-only) 2. RISING interrupts when value is high and FALLING when value is low This causes problems at bootup for single-edge interrupts that don't follow the ActiveBoth pattern. Fix this by: - Only triggering when BOTH rising and falling edges are configured - Only triggering when the GPIO line is asserted (value == 0) Reported-by: Francesco Lauritano Closes: https://lore.kernel.org/all/6iFCwGH2vssb7NRUTWGpkubGMNbgIlBHSz40z8ZsezjxngXpoiiRiJaijviNvhiDAGIr43bfUmdxLmxYoHDjyft4DgwFc3Pnu5hzPguTa0s=@protonmail.com/ Tested-by: Marco Scardovi Fixes: ca876c7483b69 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") Link: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- Suggested-by: Armin Wolf Signed-off-by: Mario Limonciello Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi-core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 09f860200a05..eb8a40cfb7a9 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -233,12 +233,23 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; - /* Make sure we trigger the initial state of edge-triggered IRQs */ + /* + * Make sure we trigger the initial state of ActiveBoth IRQs. + * + * According to the Microsoft GPIO documentation, triggering GPIO + * interrupts marked as ActiveBoth during initialization is correct + * as long as the associated GPIO line is already "asserted" + * (logic level low). We should not trigger edge-based GPIO + * interrupts not marked as ActiveBoth. + * + * See: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- + * Section: "GPIO controllers and ActiveBoth interrupts" + */ if (acpi_gpio_need_run_edge_events_on_boot() && - (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + ((event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) == + (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + if (value == 0) event->handler(event->irq, event); } } From fa1d4fa118f4229168e9ca88cea260c5e5a94652 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Mon, 13 Apr 2026 08:50:39 +0800 Subject: [PATCH 0278/5214] i3c: master: svc: Fix missed IBI after false SLVSTART on NPCM845 The NPCM845 I3C controller may raise a false SLVSTART interrupt. The handler first latches MSTATUS and then clears SLVSTART. If a real IBI request arrives after the handler latches MSTATUS but before it clears the SLVSTART interrupt status, HW sets the SLVREQ state. However, the handler still relies on the stale MSTATUS snapshot, returns early, and misses the real IBI. No further interrupt is generated for this pending IBI. Re-read MSTATUS to obtain the latest state and avoid missing a real IBI due to this race condition. Fixes: 4dd12e944f07 ("i3c: master: svc: Fix npcm845 invalid slvstart event") Signed-off-by: Stanley Chu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260413005040.1211107-2-yschu@nuvoton.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/svc-i3c-master.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index e2d99a3ac07d..63063741fbd1 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -672,10 +672,18 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id) /* Clear the interrupt status */ writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS); - /* Ignore the false event */ - if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) && - !SVC_I3C_MSTATUS_STATE_SLVREQ(active)) - return IRQ_HANDLED; + if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART)) { + /* + * Re-read MSTATUS to obtain the latest state and avoid + * missing an IBI that arrives after MSTATUS is latched + * but before SLVSTART is cleared. + */ + active = readl(master->regs + SVC_I3C_MSTATUS); + + /* Ignore the false event */ + if (!SVC_I3C_MSTATUS_STATE_SLVREQ(active)) + return IRQ_HANDLED; + } /* * The SDA line remains low until the request is processed. From 1effa3adfe53cb2bb28bf5640a676b791d5ab405 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Mon, 13 Apr 2026 08:50:40 +0800 Subject: [PATCH 0279/5214] i3c: master: svc: Prevent IRQ storm from false SLVSTART on NPCM845 On NPCM845, when a target on the I3C bus gets stuck holding SDA low, the controller reports a false Master Request (MR) in-band interrupt event. The driver handles this by emitting a STOP condition to restore the bus. However, the hardware quirk SVC_I3C_QUIRK_FALSE_SLVSTART indicates that emitting a STOP condition may spuriously set the SLVSTART interrupt status bit. In the Master Request case, this creates a feedback loop: the STOP triggers a new SLVSTART event, the IRQ handler fires again, the controller still reports an MR type, another STOP is emitted, and the cycle repeats indefinitely, resulting in an IRQ storm that can lock up the CPU. Clear the SLVSTART status bit explicitly after emitting the STOP in the Master Request IBI handler when the SVC_I3C_QUIRK_FALSE_SLVSTART quirk is set. This breaks the feedback loop without affecting normal SLVSTART processing, which is already guarded in the top-level IRQ handler by checking that MSTATUS is in SLVREQ state. Signed-off-by: Stanley Chu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260413005040.1211107-3-yschu@nuvoton.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/svc-i3c-master.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 63063741fbd1..57d63d299e1e 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -655,6 +655,15 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_emit_stop(master); + + /* + * If a target gets stuck holding SDA low, the controller reports a MR. + * On NPCM845, emitting STOP may spuriously set SLVSTART, retriggering + * the interrupt and re-entering MR handling, leading to an IRQ storm. + * Clear SLVSTART after STOP to break the loop. + */ + if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART)) + writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS); break; default: break; From 14236919b0e68ac9be6b425d5149a3ad4fd85914 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Tue, 21 Apr 2026 16:48:37 -0500 Subject: [PATCH 0280/5214] i3c: master: Replace sprintf() with sysfs_emit() family Replace sprintf() function calls with sysfs_emit() and sysfs_emit_at() function calls where appropriate. This will help harden the driver and help modernize it. While at it, add missing newlines at the end of some sysfs_emit() (formerly sprintf()) calls. Signed-off-by: Maxwell Doose Reviewed-by: Frank Li Link: https://patch.msgid.link/20260421214837.20939-1-m32285159@gmail.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5cd4e5da2233..6b8df8089a35 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -174,7 +174,7 @@ static ssize_t bcr_show(struct device *dev, i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); - ret = sprintf(buf, "0x%02x\n", desc->info.bcr); + ret = sysfs_emit(buf, "0x%02x\n", desc->info.bcr); i3c_bus_normaluse_unlock(bus); return ret; @@ -191,7 +191,7 @@ static ssize_t dcr_show(struct device *dev, i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); - ret = sprintf(buf, "0x%02x\n", desc->info.dcr); + ret = sysfs_emit(buf, "0x%02x\n", desc->info.dcr); i3c_bus_normaluse_unlock(bus); return ret; @@ -208,7 +208,7 @@ static ssize_t pid_show(struct device *dev, i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); - ret = sprintf(buf, "%llx\n", desc->info.pid); + ret = sysfs_emit(buf, "%llx\n", desc->info.pid); i3c_bus_normaluse_unlock(bus); return ret; @@ -225,7 +225,7 @@ static ssize_t dynamic_address_show(struct device *dev, i3c_bus_normaluse_lock(bus); desc = dev_to_i3cdesc(dev); - ret = sprintf(buf, "%02x\n", desc->info.dyn_addr); + ret = sysfs_emit(buf, "%02x\n", desc->info.dyn_addr); i3c_bus_normaluse_unlock(bus); return ret; @@ -256,7 +256,7 @@ static ssize_t hdrcap_show(struct device *dev, if (!hdrcap_strings[mode]) continue; - ret = sprintf(buf + offset, offset ? " %s" : "%s", + ret = sysfs_emit_at(buf, offset, offset ? " %s" : "%s", hdrcap_strings[mode]); if (ret < 0) goto out; @@ -264,7 +264,7 @@ static ssize_t hdrcap_show(struct device *dev, offset += ret; } - ret = sprintf(buf + offset, "\n"); + ret = sysfs_emit_at(buf, offset, "\n"); if (ret < 0) goto out; @@ -290,10 +290,10 @@ static ssize_t modalias_show(struct device *dev, ext = I3C_PID_EXTRA_INFO(devinfo.pid); if (I3C_PID_RND_LOWER_32BITS(devinfo.pid)) - return sprintf(buf, "i3c:dcr%02Xmanuf%04X", devinfo.dcr, + return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04X\n", devinfo.dcr, manuf); - return sprintf(buf, "i3c:dcr%02Xmanuf%04Xpart%04Xext%04X", + return sysfs_emit(buf, "i3c:dcr%02Xmanuf%04Xpart%04Xext%04X\n", devinfo.dcr, manuf, part, ext); } static DEVICE_ATTR_RO(modalias); @@ -578,9 +578,9 @@ static ssize_t mode_show(struct device *dev, if (i3cbus->mode < 0 || i3cbus->mode >= ARRAY_SIZE(i3c_bus_mode_strings) || !i3c_bus_mode_strings[i3cbus->mode]) - ret = sprintf(buf, "unknown\n"); + ret = sysfs_emit(buf, "unknown\n"); else - ret = sprintf(buf, "%s\n", i3c_bus_mode_strings[i3cbus->mode]); + ret = sysfs_emit(buf, "%s\n", i3c_bus_mode_strings[i3cbus->mode]); i3c_bus_normaluse_unlock(i3cbus); return ret; @@ -595,7 +595,7 @@ static ssize_t current_master_show(struct device *dev, ssize_t ret; i3c_bus_normaluse_lock(i3cbus); - ret = sprintf(buf, "%d-%llx\n", i3cbus->id, + ret = sysfs_emit(buf, "%d-%llx\n", i3cbus->id, i3cbus->cur_master->info.pid); i3c_bus_normaluse_unlock(i3cbus); @@ -611,7 +611,7 @@ static ssize_t i3c_scl_frequency_show(struct device *dev, ssize_t ret; i3c_bus_normaluse_lock(i3cbus); - ret = sprintf(buf, "%ld\n", i3cbus->scl_rate.i3c); + ret = sysfs_emit(buf, "%ld\n", i3cbus->scl_rate.i3c); i3c_bus_normaluse_unlock(i3cbus); return ret; @@ -626,7 +626,7 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, ssize_t ret; i3c_bus_normaluse_lock(i3cbus); - ret = sprintf(buf, "%ld\n", i3cbus->scl_rate.i2c); + ret = sysfs_emit(buf, "%ld\n", i3cbus->scl_rate.i2c); i3c_bus_normaluse_unlock(i3cbus); return ret; From 1d78a8fc97c133b8aee54993a83f86b68ed2fdb8 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 1 Apr 2026 14:14:30 +0530 Subject: [PATCH 0281/5214] i3c: dw-i3c-master: Fix IBI count register selection for versalnet On DesignWare I3C controllers where IC_HAS_IBI_DATA=0 (such as versalnet), the IBI_STS_CNT field (bits [28:24] of QUEUE_STATUS_LEVEL) is hardwired to 0. The IBI status entry count is instead reported via IBI_BUF_BLR (bits [23:16] of the same register). irq_handle_ibis() was unconditionally reading IBI_STS_CNT, causing it to always see 0 pending IBIs on versalnet and return early without draining the IBI buffer. Since INTR_IBI_THLD_STAT is level-triggered against the buffer fill level, this left the interrupt permanently asserted. Detect IBI data capability at probe time by writing the IBI data threshold field in QUEUE_THLD_CTRL and reading it back. Use the result to select the correct register field in irq_handle_ibis(). Signed-off-by: Shubhrajyoti Datta Link: https://patch.msgid.link/20260401084430.436059-1-shubhrajyoti.datta@amd.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/dw-i3c-master.c | 21 ++++++++++++++++++++- drivers/i3c/master/dw-i3c-master.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 655693a2187e..a7593d6efac5 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1435,7 +1435,11 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) u32 reg; reg = readl(master->regs + QUEUE_STATUS_LEVEL); - n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg); + if (master->has_ibi_data) + n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg); + else + n_ibis = QUEUE_STATUS_IBI_BUF_BLR(reg); + if (!n_ibis) return; @@ -1566,6 +1570,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { int ret, irq; + u32 thld_ctrl; const struct dw_i3c_drvdata *drvdata; unsigned long quirks = 0; @@ -1623,6 +1628,20 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->maxdevs = ret >> 16; master->free_pos = GENMASK(master->maxdevs - 1, 0); + /* + * Detect IBI data capability (IC_HAS_IBI_DATA): write a non-zero value + * to IBI_DATA_THLD and read back. On controllers like Versalnet + * the field is hardwired to 0 and the write is ignored. Restore the + * original register value after detection. + */ + thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL); + ret = thld_ctrl | QUEUE_THLD_CTRL_IBI_DATA(2); + writel(ret, master->regs + QUEUE_THLD_CTRL); + ret = readl(master->regs + QUEUE_THLD_CTRL); + if (ret & QUEUE_THLD_CTRL_IBI_DATA_MASK) + master->has_ibi_data = true; + writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL); + if (has_acpi_companion(&pdev->dev)) { quirks = (unsigned long)device_get_match_data(&pdev->dev); } else if (pdev->dev.of_node) { diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index c5cb695c16ab..306e25a08937 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -51,6 +51,7 @@ struct dw_i3c_master { u32 i2c_fm_timing; u32 i2c_fmp_timing; u32 quirks; + bool has_ibi_data; /* * Per-device hardware data, used to manage the device address table * (DAT) From 3ca99eed042620d12315e9272ed3ef260ca29877 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 27 Apr 2026 10:11:46 +0800 Subject: [PATCH 0282/5214] pinctrl: mediatek: eint: Drop base from mtk_eint_chip_write_mask() When support for multiple EINT base addresses was added in commit 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses"), mtk_eint_chip_write_mask() was changed to write interrupt masks for all base addresses in one call. However the "base" parameter was left around and now causes sparse warnings: mtk-eint.c:428:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:428:44: expected void [noderef] __iomem *base mtk-eint.c:428:44: got void [noderef] __iomem **base mtk-eint.c:436:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:436:44: expected void [noderef] __iomem *base mtk-eint.c:436:44: got void [noderef] __iomem **base Since the "base" parameter is no longer needed, just drop it. Fixes: 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses") Cc: Hao Chang Cc: Qingliang Li Signed-off-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/mtk-eint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c index 2a3c04eedc5f..47ac92ea98c2 100644 --- a/drivers/pinctrl/mediatek/mtk-eint.c +++ b/drivers/pinctrl/mediatek/mtk-eint.c @@ -246,7 +246,7 @@ static int mtk_eint_irq_set_wake(struct irq_data *d, unsigned int on) } static void mtk_eint_chip_write_mask(const struct mtk_eint *eint, - void __iomem *base, unsigned int **buf) + unsigned int **buf) { int inst, port, port_num; void __iomem *reg; @@ -425,7 +425,7 @@ static void mtk_eint_irq_handler(struct irq_desc *desc) int mtk_eint_do_suspend(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->wake_mask); + mtk_eint_chip_write_mask(eint, eint->wake_mask); return 0; } @@ -433,7 +433,7 @@ EXPORT_SYMBOL_GPL(mtk_eint_do_suspend); int mtk_eint_do_resume(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->cur_mask); + mtk_eint_chip_write_mask(eint, eint->cur_mask); return 0; } From 0bdf7d7ee75da076eabcfa9b5fadd0ed0524df43 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:00:49 +0200 Subject: [PATCH 0283/5214] scsi: ufs: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260427070048.18017-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index 964ae70e7390..ff170c0b6da0 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -55,7 +55,7 @@ config SCSI_UFS_DWC_TC_PLATFORM If unsure, say N. config SCSI_UFS_QCOM - tristate "QCOM specific hooks to UFS controller platform driver" + tristate "Qualcomm specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM depends on GENERIC_MSI_IRQ depends on RESET_CONTROLLER From 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 13 Apr 2026 09:31:17 -0400 Subject: [PATCH 0284/5214] fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow indx_find_buffer() recursively descends the B+ tree index with no depth limit. A crafted NTFS image with circular index node references causes unbounded recursion, overflowing the kernel stack and panicking the system. This is reachable by mounting a malicious NTFS filesystem (e.g. from a USB drive via desktop automount) and deleting a file whose index entry triggers the rebalancing fallback path in indx_delete_entry(). Add a depth parameter and bail out with -EINVAL when it reaches the fnd->nodes array bound, matching the constraint already enforced by fnd_push() in indx_find(). The related function indx_find() was previously patched for a similar infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was missed. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 5344b29b0577..dbf5ec00b237 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -2022,13 +2022,21 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, static struct indx_node *indx_find_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, const struct INDEX_ROOT *root, - __le64 vbn, struct indx_node *n) + __le64 vbn, struct indx_node *n, + int depth) { int err; const struct NTFS_DE *e; struct indx_node *r; const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; + /* + * Limit recursion depth to prevent stack overflow from crafted + * images. Use the same bound as the fnd->nodes array (20). + */ + if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes)) + return ERR_PTR(-EINVAL); + /* Step 1: Scan one level. */ for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { if (!e) @@ -2049,7 +2057,8 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, if (err) return ERR_PTR(err); - r = indx_find_buffer(indx, ni, root, vbn, n); + r = indx_find_buffer(indx, ni, root, vbn, n, + depth + 1); if (r) return r; } @@ -2462,7 +2471,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, fnd_clear(fnd); - in = indx_find_buffer(indx, ni, root, sub_vbn, NULL); + in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0); if (IS_ERR(in)) { err = PTR_ERR(in); goto out; From 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 19:33:05 -0400 Subject: [PATCH 0285/5214] ntfs3: bound to_move in indx_insert_into_root before hdr_insert_head indx_insert_into_root() promotes a full resident $INDEX_ROOT into $INDEX_ALLOCATION and copies all non-last resident root entries into a newly allocated INDEX_BUFFER via hdr_insert_head(). The source byte count 'to_move' is summed from the on-disk resident entry sizes and is independent of the destination buffer size, which comes from root->index_block_size (via indx->index_bits). A crafted NTFS image that keeps a valid, full resident root but shrinks root->index_block_size down to 512 after the root has been populated makes hdr_insert_head() memcpy attacker-controlled resident entry bytes past the end of the kmalloc(1u << indx->index_bits) allocation returned by indx_new(). For a 512-byte destination and a resident root whose non-last entries total 560 bytes, the memcpy overruns by 120 bytes and a following memmove extends the highest written offset to 136 bytes past the allocation. The overflow bytes are a direct copy of on-disk entries (via kmemdup), so they are fully attacker-controlled. The write is reachable from unprivileged open(O_CREAT) on a mounted crafted NTFS image: a single sufficiently long create in a directory whose resident root is already full forces root promotion and triggers the copy. This is a controlled out-of-bounds write of 120-136 bytes past a kmalloc(index_block_size) allocation, with attacker-controlled content. It is a bounded adjacent-heap corruption primitive; it is not an arbitrary-address write. Successful exploitation into a named victim object depends on the surrounding slab layout. Reject the copy at the sink. The destination's INDEX_HDR already reports hdr_total (the payload capacity of the new buffer) and hdr_used (the bytes already consumed by the terminal END entry installed by indx_new()); require that to_move fits in the remaining payload before calling hdr_insert_head(). On mismatch, fail with -EINVAL and mark the filesystem as having a detected on-disk inconsistency, which is the same behaviour as the surrounding validation in this function. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index dbf5ec00b237..5868964e129c 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1742,6 +1742,22 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, hdr_used = le32_to_cpu(hdr->used); hdr_total = le32_to_cpu(hdr->total); + /* + * The destination INDEX_BUFFER has 'hdr_total' bytes of payload + * available after the header, of which 'hdr_used' are already + * consumed by the single terminal END entry installed by + * indx_new(). A crafted image can present a resident root whose + * non-last entries (summing to 'to_move') exceed what fits in + * this buffer; copying them unchecked would overrun the + * kmalloc(1u << indx->index_bits) allocation backing the new + * buffer. Reject the copy in that case. + */ + if (to_move > hdr_total - hdr_used) { + err = -EINVAL; + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + goto out_put_n; + } + /* Copy root entries into new buffer. */ hdr_insert_head(hdr, re, to_move); From 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:11:18 +0000 Subject: [PATCH 0286/5214] ntfs3: fix out-of-bounds read in decompress_lznt decompress_lznt() does not validate array index bounds before accessing the decompression table. A corrupted NTFS3 image with invalid compressed data can trigger an out-of-bounds read. Add index bounds checking to prevent the OOB access. Reported-by: syzbot+39b2fb0f2638669008ec@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Konstantin Komarov --- fs/ntfs3/lznt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index fdc9b2ebf341..f818d9785004 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, if (up - unc > LZNT_CHUNK_SIZE) return -EINVAL; /* Correct index */ - while (unc + s_max_off[index] < up) + while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up) index += 1; /* Check the current flag for zero. */ From f1df9d771df47aa40de6d70949c28720ae1e430d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 18:57:12 -0400 Subject: [PATCH 0287/5214] ntfs3: validate split-point offset in indx_insert_into_buffer indx_insert_into_buffer() computes used = used1 - to_copy - sp_size; memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off)); where sp and sp_size come from hdr_find_split(). hdr_find_split() walks entries by le16_to_cpu(e->size) without validating that each step stays within hdr->used or that the size field is at least sizeof(struct NTFS_DE). index_hdr_check(), the on-load gatekeeper, only validates header-level fields (used, total, de_off) and does not walk per-entry sizes. A crafted NTFS image whose leaf INDEX_HDR reports used == total but contains one interior NTFS_DE with size = 0xFFF0 therefore passes validation, descends to indx_insert_into_buffer() through the ntfs_create() -> indx_insert_entry() path, and makes hdr_find_split() return an sp whose sp_size (0xFFF0) greatly exceeds the remaining bytes in the buffer. The u32 subtraction underflows and the memmove count becomes a near-4-GiB value, producing an out-of-bounds kernel write that corrupts adjacent allocations and panics the kernel. Reproduced on 7.0.0-rc7 with UML + KASAN via a crafted image and a single 'touch' inside the mounted directory; crash site resolves to fs/ntfs3/index.c at the memmove. Trigger requires only local mount of an attacker-supplied filesystem image (USB, loopback, or removable media auto-mount). Reject the split whenever the chosen sp plus its declared size already extends past hdr1->used. This is the minimal fix; it preserves the existing hdr_find_split() contract and relies on the same out: cleanup path as the pre-existing error returns. A prior OOB read in the very same indx_insert_into_buffer() memmove was fixed in commit b8c44949044e ("fs/ntfs3: Fix OOB read in indx_insert_into_buffer") by tightening hdr_find_e(), but that fix does not cover the split-point size field path addressed here: sp is returned by hdr_find_split(), not hdr_find_e(), and the underflow is driven by sp->size rather than hdr->used exceeding hdr->total. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Reported-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 5868964e129c..ade276225999 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1862,6 +1862,20 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, memcpy(up_e, sp, sp_size); used1 = le32_to_cpu(hdr1->used); + + /* + * hdr_find_split does not validate per-entry sizes, so a crafted + * NTFS_DE whose le16 size field is out of range can place sp such + * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this + * guard the u32 'used = used1 - to_copy - sp_size' underflows and + * the subsequent memmove count becomes a near-4-GiB value, + * triggering an out-of-bounds kernel write. + */ + if (PtrOffset(hdr1, sp) + sp_size > used1) { + err = -EINVAL; + goto out; + } + hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS); if (!hdr1_saved) { err = -ENOMEM; From f37f5a2ac9d3737617c08f0dc7270b42e9cad907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duje=20Mihanovi=C4=87?= Date: Sat, 28 Mar 2026 21:42:16 +0100 Subject: [PATCH 0288/5214] backlight: ktd2801: Enable BL_CORE_SUSPENDRESUME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Boards using this backlight chip do not power the backlight off on suspend. Enable BL_CORE_SUSPENDRESUME so the chip gets powered off by the backlight core on suspend. Tested on samsung,coreprimevelte. Cc: stable@vger.kernel.org # v6.19 Signed-off-by: Duje Mihanović Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260328-ktd2801-pm-fix-v1-1-007cb103faeb@dujemihanovic.xyz Signed-off-by: Lee Jones --- drivers/video/backlight/ktd2801-backlight.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/backlight/ktd2801-backlight.c b/drivers/video/backlight/ktd2801-backlight.c index 17eac1b3bce4..1b1307e03b20 100644 --- a/drivers/video/backlight/ktd2801-backlight.c +++ b/drivers/video/backlight/ktd2801-backlight.c @@ -53,6 +53,7 @@ static int ktd2801_update_status(struct backlight_device *bd) } static const struct backlight_ops ktd2801_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, .update_status = ktd2801_update_status, }; From 5fcbbedec9dfce78044eee922bf2030e1bd03faa Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Tue, 7 Apr 2026 16:41:42 +0200 Subject: [PATCH 0289/5214] dt-bindings: backlight: Add max25014 support The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC with integrated boost controller. Signed-off-by: Maud Spierings Reviewed-by: Rob Herring (Arm) Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260407-max25014-v8-1-14eac7ed673a@gocontroll.com Signed-off-by: Lee Jones --- .../leds/backlight/maxim,max25014.yaml | 83 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml diff --git a/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml new file mode 100644 index 000000000000..d00be2e08193 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/backlight/maxim,max25014.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim max25014 backlight controller + +maintainers: + - Maud Spierings + +properties: + compatible: + enum: + - maxim,max25014 + + reg: + maxItems: 1 + + default-brightness: + minimum: 0 + maximum: 100 + default: 50 + + enable-gpios: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-supply: + description: Regulator which controls the boost converter input rail. + + pwms: + maxItems: 1 + + maxim,iset: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 15 + default: 11 + description: + Value of the ISET field in the ISET register. This controls the current + scale of the outputs, a higher number means more current. + + maxim,strings: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + A 4-bit bitfield that describes which led strings to turn on. + minItems: 4 + maxItems: 4 + items: + maximum: 1 + default: + [1 1 1 1] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + backlight@6f { + compatible = "maxim,max25014"; + reg = <0x6f>; + default-brightness = <50>; + enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + power-supply = <®_backlight>; + pwms = <&pwm1>; + maxim,iset = <7>; + maxim,strings = <1 1 1 0>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..50569fd26960 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15673,6 +15673,11 @@ F: Documentation/userspace-api/media/drivers/max2175.rst F: drivers/media/i2c/max2175* F: include/uapi/linux/max2175.h +MAX25014 BACKLIGHT DRIVER +M: Maud Spierings +S: Maintained +F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml + MAX31335 RTC DRIVER M: Antoniu Miclaus L: linux-rtc@vger.kernel.org From 3014ad47cfaf454cb0bbee353272beacd1e7c4bc Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Tue, 7 Apr 2026 16:41:43 +0200 Subject: [PATCH 0290/5214] backlight: Add max25014atg backlight The Maxim MAX25014 is a 4-channel automotive grade backlight driver IC with integrated boost controller. Signed-off-by: Maud Spierings Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260407-max25014-v8-2-14eac7ed673a@gocontroll.com Signed-off-by: Lee Jones --- MAINTAINERS | 1 + drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/max25014.c | 377 +++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 drivers/video/backlight/max25014.c diff --git a/MAINTAINERS b/MAINTAINERS index 50569fd26960..1c0d739a88d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15677,6 +15677,7 @@ MAX25014 BACKLIGHT DRIVER M: Maud Spierings S: Maintained F: Documentation/devicetree/bindings/leds/backlight/maxim,max25014.yaml +F: drivers/video/backlight/max25014.c MAX31335 RTC DRIVER M: Antoniu Miclaus diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a7a3fbaf7c29..f4e99542ffe8 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -282,6 +282,13 @@ config BACKLIGHT_DA9052 help Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs. +config BACKLIGHT_MAX25014 + tristate "Backlight driver for Maxim MAX25014" + depends on I2C + select REGMAP_I2C + help + If you are using a MAX25014 chip as a backlight driver say Y to enable it. + config BACKLIGHT_MAX8925 tristate "Backlight driver for MAX8925" depends on MFD_MAX8925 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 794820a98ed4..21c8313cfb12 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o +obj-$(CONFIG_BACKLIGHT_MAX25014) += max25014.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o diff --git a/drivers/video/backlight/max25014.c b/drivers/video/backlight/max25014.c new file mode 100644 index 000000000000..3ee45617261f --- /dev/null +++ b/drivers/video/backlight/max25014.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Backlight driver for Maxim MAX25014 + * + * Copyright (C) 2025 GOcontroll B.V. + * Author: Maud Spierings + */ + +#include +#include +#include +#include +#include + +#define MAX25014_ISET_DEFAULT_100 11 +#define MAX_BRIGHTNESS 100 +#define MIN_BRIGHTNESS 0 +#define TON_MAX 130720 /* @153Hz */ +#define TON_STEP 1307 /* @153Hz */ +#define TON_MIN 0 + +#define MAX25014_DEV_ID 0x00 +#define MAX25014_REV_ID 0x01 +#define MAX25014_ISET 0x02 +#define MAX25014_IMODE 0x03 +#define MAX25014_TON1H 0x04 +#define MAX25014_TON1L 0x05 +#define MAX25014_TON2H 0x06 +#define MAX25014_TON2L 0x07 +#define MAX25014_TON3H 0x08 +#define MAX25014_TON3L 0x09 +#define MAX25014_TON4H 0x0A +#define MAX25014_TON4L 0x0B +#define MAX25014_TON_1_4_LSB 0x0C +#define MAX25014_SETTING 0x12 +#define MAX25014_DISABLE 0x13 +#define MAX25014_BSTMON 0x14 +#define MAX25014_IOUT1 0x15 +#define MAX25014_IOUT2 0x16 +#define MAX25014_IOUT3 0x17 +#define MAX25014_IOUT4 0x18 +#define MAX25014_OPEN 0x1B +#define MAX25014_SHORTGND 0x1C +#define MAX25014_SHORTED_LED 0x1D +#define MAX25014_MASK 0x1E +#define MAX25014_DIAG 0x1F + +#define MAX25014_ISET_ENA BIT(5) +#define MAX25014_ISET_PSEN BIT(4) +#define MAX25014_IMODE_HDIM BIT(2) +#define MAX25014_SETTING_FPWM GENMASK(6, 4) +#define MAX25014_DISABLE_DIS_MASK GENMASK(3, 0) +#define MAX25014_DIAG_OT BIT(0) +#define MAX25014_DIAG_OTW BIT(1) +#define MAX25014_DIAG_HW_RST BIT(2) +#define MAX25014_DIAG_BSTOV BIT(3) +#define MAX25014_DIAG_BSTUV BIT(4) +#define MAX25014_DIAG_IREFOOR BIT(5) + +struct max25014 { + struct i2c_client *client; + struct backlight_device *bl; + struct regmap *regmap; + struct gpio_desc *enable; + uint32_t iset; + uint8_t strings_mask; +}; + +static const struct regmap_config max25014_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX25014_DIAG, +}; + +static int max25014_initial_power_state(struct max25014 *maxim) +{ + uint32_t val; + int ret; + + ret = regmap_read(maxim->regmap, MAX25014_ISET, &val); + if (ret) + return ret; + + return val & MAX25014_ISET_ENA ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; +} + +static int max25014_check_errors(struct max25014 *maxim) +{ + uint32_t val; + uint8_t i; + int ret; + + ret = regmap_read(maxim->regmap, MAX25014_OPEN, &val); + if (ret) + return ret; + if (val) { + dev_err(&maxim->client->dev, "Open led strings detected on:\n"); + for (i = 0; i < 4; i++) { + if (val & 1 << i) + dev_err(&maxim->client->dev, "string %d\n", i + 1); + } + return -EIO; + } + + ret = regmap_read(maxim->regmap, MAX25014_SHORTGND, &val); + if (ret) + return ret; + if (val) { + dev_err(&maxim->client->dev, "Short to ground detected on:\n"); + for (i = 0; i < 4; i++) { + if (val & 1 << i) + dev_err(&maxim->client->dev, "string %d\n", i + 1); + } + return -EIO; + } + + ret = regmap_read(maxim->regmap, MAX25014_SHORTED_LED, &val); + if (ret) + return ret; + if (val) { + dev_err(&maxim->client->dev, "Shorted led detected on:\n"); + for (i = 0; i < 4; i++) { + if (val & 1 << i) + dev_err(&maxim->client->dev, "string %d\n", i + 1); + } + return -EIO; + } + + ret = regmap_read(maxim->regmap, MAX25014_DIAG, &val); + if (ret) + return ret; + /* + * The HW_RST bit always starts at 1 after power up. + * It is reset on first read, does not indicate an error. + */ + if (val && val != MAX25014_DIAG_HW_RST) { + if (val & MAX25014_DIAG_OT) + dev_err(&maxim->client->dev, + "Overtemperature shutdown\n"); + if (val & MAX25014_DIAG_OTW) + dev_err(&maxim->client->dev, + "Chip is getting too hot (>125C)\n"); + if (val & MAX25014_DIAG_BSTOV) + dev_err(&maxim->client->dev, + "Boost converter overvoltage\n"); + if (val & MAX25014_DIAG_BSTUV) + dev_err(&maxim->client->dev, + "Boost converter undervoltage\n"); + if (val & MAX25014_DIAG_IREFOOR) + dev_err(&maxim->client->dev, "IREF out of range\n"); + return -EIO; + } + return 0; +} + +/* + * 1. disable unused strings + * 2. set dim mode + * 3. set setting register + * 4. enable the backlight + */ +static int max25014_configure(struct max25014 *maxim, int initial_state) +{ + uint32_t val; + int ret; + + ret = regmap_read(maxim->regmap, MAX25014_DISABLE, &val); + if (ret) + return ret; + + /* + * Strings can only be disabled when MAX25014_ISET_ENA == 0, check if + * it needs to be changed at all to prevent the backlight flashing when + * it is configured correctly by the bootloader + */ + if (!((val & MAX25014_DISABLE_DIS_MASK) == maxim->strings_mask)) { + if (initial_state == BACKLIGHT_POWER_ON) { + ret = regmap_write(maxim->regmap, MAX25014_ISET, 0); + if (ret) + return ret; + } + ret = regmap_write(maxim->regmap, MAX25014_DISABLE, maxim->strings_mask); + if (ret) + return ret; + } + + ret = regmap_write(maxim->regmap, MAX25014_IMODE, MAX25014_IMODE_HDIM); + if (ret) + return ret; + + ret = regmap_read(maxim->regmap, MAX25014_SETTING, &val); + if (ret) + return ret; + + ret = regmap_write(maxim->regmap, MAX25014_SETTING, + val & ~MAX25014_SETTING_FPWM); + if (ret) + return ret; + + return regmap_write(maxim->regmap, MAX25014_ISET, + maxim->iset | MAX25014_ISET_ENA | + MAX25014_ISET_PSEN); +} + +static int max25014_update_status(struct backlight_device *bl_dev) +{ + struct max25014 *maxim = bl_get_data(bl_dev); + uint32_t reg; + int ret; + + reg = TON_STEP * backlight_get_brightness(bl_dev); + + /* + * 18 bit number lowest, 2 bits in first register, + * next lowest 8 in the L register, next 8 in the H register + * Seemingly setting the strength of only one string controls all of + * them, individual settings don't affect the outcome. + */ + ret = regmap_write(maxim->regmap, MAX25014_TON_1_4_LSB, reg & 0b00000011); + if (ret != 0) + return ret; + ret = regmap_write(maxim->regmap, MAX25014_TON1L, (reg >> 2) & 0b11111111); + if (ret != 0) + return ret; + return regmap_write(maxim->regmap, MAX25014_TON1H, (reg >> 10) & 0b11111111); +} + +static const struct backlight_ops max25014_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = max25014_update_status, +}; + +static int max25014_parse_dt(struct max25014 *maxim, + uint32_t *initial_brightness) +{ + struct device *dev = &maxim->client->dev; + struct device_node *node = dev->of_node; + uint32_t strings[4]; + int res, i; + + res = of_property_count_u32_elems(node, "maxim,strings"); + if (res == 4) { + of_property_read_u32_array(node, "maxim,strings", strings, 4); + for (i = 0; i < 4; i++) { + if (strings[i] == 0) + maxim->strings_mask |= 1 << i; + } + } else { + maxim->strings_mask = 0; + } + + *initial_brightness = 50U; + of_property_read_u32(node, "default-brightness", initial_brightness); + + maxim->iset = MAX25014_ISET_DEFAULT_100; + of_property_read_u32(node, "maxim,iset", &maxim->iset); + + if (maxim->iset > 15) + return dev_err_probe(dev, -EINVAL, + "Invalid iset, should be a value from 0-15, entered was %d\n", + maxim->iset); + + if (*initial_brightness > 100) + return dev_err_probe(dev, -EINVAL, + "Invalid initial brightness, should be a value from 0-100, entered was %d\n", + *initial_brightness); + + return 0; +} + +static int max25014_probe(struct i2c_client *cl) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(cl); + struct backlight_properties props; + uint32_t initial_brightness = 50; + struct backlight_device *bl; + struct max25014 *maxim; + int ret; + + maxim = devm_kzalloc(&cl->dev, sizeof(struct max25014), GFP_KERNEL); + if (!maxim) + return -ENOMEM; + + maxim->client = cl; + + ret = max25014_parse_dt(maxim, &initial_brightness); + if (ret) + return ret; + + ret = devm_regulator_get_enable(&maxim->client->dev, "power"); + if (ret) + return dev_err_probe(&maxim->client->dev, ret, + "failed to get power-supply"); + + maxim->enable = devm_gpiod_get_optional(&maxim->client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(maxim->enable)) + return dev_err_probe(&maxim->client->dev, PTR_ERR(maxim->enable), + "failed to get enable gpio\n"); + + /* Datasheet Electrical Characteristics tSTARTUP 2ms */ + fsleep(2000); + + maxim->regmap = devm_regmap_init_i2c(cl, &max25014_regmap_config); + if (IS_ERR(maxim->regmap)) + return dev_err_probe(&maxim->client->dev, PTR_ERR(maxim->regmap), + "failed to initialize the i2c regmap\n"); + + i2c_set_clientdata(cl, maxim); + + ret = max25014_check_errors(maxim); + if (ret) /* error is already reported in the above function */ + return ret; + + ret = max25014_initial_power_state(maxim); + if (ret < 0) + return dev_err_probe(&maxim->client->dev, ret, "Could not get enabled state\n"); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + props.brightness = initial_brightness; + props.scale = BACKLIGHT_SCALE_LINEAR; + props.power = ret; + + ret = max25014_configure(maxim, ret); + if (ret) + return dev_err_probe(&maxim->client->dev, ret, "device config error"); + + bl = devm_backlight_device_register(&maxim->client->dev, id->name, + &maxim->client->dev, maxim, + &max25014_bl_ops, &props); + if (IS_ERR(bl)) + return dev_err_probe(&maxim->client->dev, PTR_ERR(bl), + "failed to register backlight\n"); + + maxim->bl = bl; + + backlight_update_status(maxim->bl); + + return 0; +} + +static void max25014_remove(struct i2c_client *cl) +{ + struct max25014 *maxim = i2c_get_clientdata(cl); + + backlight_device_set_brightness(maxim->bl, 0); + gpiod_set_value_cansleep(maxim->enable, 0); +} + +static const struct of_device_id max25014_dt_ids[] = { + { .compatible = "maxim,max25014", }, + { } +}; +MODULE_DEVICE_TABLE(of, max25014_dt_ids); + +static const struct i2c_device_id max25014_ids[] = { + { "max25014" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max25014_ids); + +static struct i2c_driver max25014_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(max25014_dt_ids), + }, + .probe = max25014_probe, + .remove = max25014_remove, + .id_table = max25014_ids, +}; +module_i2c_driver(max25014_driver); + +MODULE_DESCRIPTION("Maxim MAX25014 backlight driver"); +MODULE_AUTHOR("Maud Spierings "); +MODULE_LICENSE("GPL"); From c7233b3d99db9760daf07c4e95daa9675c6c0cba Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 29 Apr 2026 17:15:37 +0200 Subject: [PATCH 0291/5214] scsi: advansys: Drop ISA_DMA_API remnants Support for ISA bus mastering was removed a few years ago, and the VLB mode does not use the ISA DMA API, so drop the dependency and the header inclusion. Fixes: 9b4c8eaa68d0 ("advansys: remove ISA support") Signed-off-by: Arnd Bergmann Reviewed-by: Johannes Thumshirn Link: https://patch.msgid.link/20260429151623.3899875-1-arnd@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 1 - drivers/scsi/advansys.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index fc8e8b0bfa39..c3042393af23 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -474,7 +474,6 @@ config SCSI_ADVANSYS tristate "AdvanSys SCSI support" depends on SCSI depends on (ISA || EISA || PCI) && HAS_IOPORT - depends on ISA_DMA_API || !ISA help This is a driver for all SCSI host adapters manufactured by AdvanSys. It is documented in the kernel source in diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index fcf059bf41e8..5cdbf2bdb13d 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -36,7 +36,6 @@ #include #include -#include #include #include From 7787588db949a6caa7ca40bd6b67ecb75b68c932 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:30 -0400 Subject: [PATCH 0292/5214] scsi: ncr53c8xx: Drop CONFIG_ prefix from Zalon-specific compiler defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kconfiglint reports: X001: CONFIG_NCR53C8XX_PREFETCH referenced in Makefile but not defined in any Kconfig X001: CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS referenced in Makefile but not defined in any Kconfig The ncr53c8xx SCSI driver uses two preprocessor defines that carry the CONFIG_ prefix but are not defined in any Kconfig file: -DCONFIG_NCR53C8XX_PREFETCH -DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS These are hardcoded compiler flags in drivers/scsi/Makefile, passed only when CONFIG_SCSI_ZALON is enabled: ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \ -DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS The source files ncr53c8xx.c and ncr53c8xx.h check these defines with #ifdef to enable script prefetching and disable 16-bit word transfers respectively — both specific to the PA-RISC Zalon SCSI controller's big-endian bus requirements. These defines have been present since the initial git import in commit 1da177e4c3f4 ("Linux-2.6.12-rc2"). They predate the modern Kconfig convention that CONFIG_ prefixed symbols should always originate from Kconfig. The third define on the same line, SCSI_NCR_BIG_ENDIAN, already correctly omits the CONFIG_ prefix. The CONFIG_ prefix is misleading: these are not user-configurable options and do not appear in any Kconfig menu. They are unconditionally enabled for all Zalon builds. Remove the CONFIG_ prefix from both symbols — renaming them to NCR53C8XX_PREFETCH and SCSI_NCR53C8XX_NO_WORD_TRANSFERS — to match the convention used by SCSI_NCR_BIG_ENDIAN on the same line and to avoid confusion with actual Kconfig-managed symbols. No functional change. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Link: https://patch.msgid.link/20260426000330.56137-1-sashal@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/Makefile | 4 ++-- drivers/scsi/ncr53c8xx.c | 2 +- drivers/scsi/ncr53c8xx.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 16de3e41f94c..842c254bb226 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -177,8 +177,8 @@ sd_mod-$(CONFIG_BLK_DEV_ZONED) += sd_zbc.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ - := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \ - -DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS + := -DNCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \ + -DSCSI_NCR53C8XX_NO_WORD_TRANSFERS CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m) zalon7xx-objs := zalon.o ncr53c8xx.o diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 4a255aafed80..5369ca3fe4fd 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -1776,7 +1776,7 @@ struct ncb { ** return from the subroutine. */ -#ifdef CONFIG_NCR53C8XX_PREFETCH +#ifdef NCR53C8XX_PREFETCH #define PREFETCH_FLUSH_CNT 2 #define PREFETCH_FLUSH SCR_CALL, PADDRH (wait_dma), #else diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h index be38c902859e..2f6865ca1b87 100644 --- a/drivers/scsi/ncr53c8xx.h +++ b/drivers/scsi/ncr53c8xx.h @@ -397,7 +397,7 @@ #else -#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS +#ifdef SCSI_NCR53C8XX_NO_WORD_TRANSFERS /* Only 8 or 32 bit transfers allowed */ #define INW_OFF(o) (readb((char __iomem *)np->reg + ncr_offw(o)) << 8 | readb((char __iomem *)np->reg + ncr_offw(o) + 1)) #else @@ -405,7 +405,7 @@ #endif #define INL_OFF(o) readl_raw((char __iomem *)np->reg + (o)) -#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS +#ifdef SCSI_NCR53C8XX_NO_WORD_TRANSFERS /* Only 8 or 32 bit transfers allowed */ #define OUTW_OFF(o, val) do { writeb((char)((val) >> 8), (char __iomem *)np->reg + ncr_offw(o)); writeb((char)(val), (char __iomem *)np->reg + ncr_offw(o) + 1); } while (0) #else From c9ee94c7e2fb65a433b505d7bcf4c2b6ee81b86c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Apr 2026 09:31:14 +0800 Subject: [PATCH 0293/5214] scsi: ufs: dt-bindings: Add compatible for Nord UFS Host Controller Document UFS Host Controller on Qualcomm Nord SoC. Like the Eliza SoC, Nord has a multi-queue command (MCQ) register range in addition to the standard one, making both reg entries required. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Manivannan Sadhasivam Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260427013115.231731-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml index f28641c6e68f..900d93b675cd 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml @@ -17,6 +17,7 @@ select: enum: - qcom,eliza-ufshc - qcom,kaanapali-ufshc + - qcom,nord-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc required: @@ -28,6 +29,7 @@ properties: - enum: - qcom,eliza-ufshc - qcom,kaanapali-ufshc + - qcom,nord-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc - const: qcom,ufshc @@ -74,6 +76,7 @@ allOf: contains: enum: - qcom,eliza-ufshc + - qcom,nord-ufshc then: properties: reg: From 7030e16247dc9fb044371141c513581067c8e574 Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 09:31:15 +0800 Subject: [PATCH 0294/5214] scsi: ufs: dt-bindings: Add compatible for SA8797P UFS Host Controller SA8797P is the automotive variant of the Nord SoC. Like SA8255P, its platform firmware implements an SCMI server that manages UFS resources such as the PHY, clocks, regulators and resets via the SCMI power protocol. As a result, the OS-visible DT only describes the controller's MMIO, interrupt, IOMMU and power-domain interfaces, making SA8255P the appropriate fallback compatible. Signed-off-by: Deepti Jaggi Reviewed-by: Manivannan Sadhasivam Signed-off-by: Shawn Guo Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427013115.231731-3-shengchao.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- .../devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml index 75fae9f1eba7..db165a235cb6 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml @@ -11,7 +11,11 @@ maintainers: properties: compatible: - const: qcom,sa8255p-ufshc + oneOf: + - const: qcom,sa8255p-ufshc + - items: + - const: qcom,sa8797p-ufshc + - const: qcom,sa8255p-ufshc reg: maxItems: 1 From 91a4e313cbc284c8c75bd80c44617a4cfcf19e2d Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Apr 2026 11:41:51 -0400 Subject: [PATCH 0295/5214] KVM: TDX/VMX: rework EPT_VIOLATION_EXEC_FOR_RING3_LIN into PROT_MASK EPT exit qualification bit 6 is used when mode-based execute control is enabled, and reflects user executable addresses. Rework name to reflect the intention and add to EPT_VIOLATION_PROT_MASK, which allows simplifying the return evaluation in tdx_is_sept_violation_unexpected_pending a pinch. Rework handling in __vmx_handle_ept_violation to unconditionally clear EPT_VIOLATION_PROT_USER_EXEC until MBEC is implemented, as suggested by Sean [1]. Note: Intel SDM Table 29-7 defines bit 6 as: If the "mode-based execute control" VM-execution control is 0, the value of this bit is undefined. If that control is 1, this bit is the logical-AND of bit 10 in the EPT paging-structure entries used to translate the guest-physical address of the access causing the EPT violation. In this case, it indicates whether the guest-physical address was executable for user-mode linear addresses. [1] https://lore.kernel.org/all/aCJDzU1p_SFNRIJd@google.com/ Suggested-by: Sean Christopherson Signed-off-by: Jon Kohler Message-ID: <20251223054806.1611168-2-jon@nutanix.com> Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/vmx.h | 5 +++-- arch/x86/kvm/vmx/common.h | 9 +++++++-- arch/x86/kvm/vmx/tdx.c | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 37080382df54..b2291a766e3f 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -608,10 +608,11 @@ enum vm_entry_failure_code { #define EPT_VIOLATION_PROT_READ BIT(3) #define EPT_VIOLATION_PROT_WRITE BIT(4) #define EPT_VIOLATION_PROT_EXEC BIT(5) -#define EPT_VIOLATION_EXEC_FOR_RING3_LIN BIT(6) +#define EPT_VIOLATION_PROT_USER_EXEC BIT(6) #define EPT_VIOLATION_PROT_MASK (EPT_VIOLATION_PROT_READ | \ EPT_VIOLATION_PROT_WRITE | \ - EPT_VIOLATION_PROT_EXEC) + EPT_VIOLATION_PROT_EXEC | \ + EPT_VIOLATION_PROT_USER_EXEC) #define EPT_VIOLATION_GVA_IS_VALID BIT(7) #define EPT_VIOLATION_GVA_TRANSLATED BIT(8) diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h index 412d0829d7a2..adf925500b9e 100644 --- a/arch/x86/kvm/vmx/common.h +++ b/arch/x86/kvm/vmx/common.h @@ -94,8 +94,13 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, /* Is it a fetch fault? */ error_code |= (exit_qualification & EPT_VIOLATION_ACC_INSTR) ? PFERR_FETCH_MASK : 0; - /* ept page table entry is present? */ - error_code |= (exit_qualification & EPT_VIOLATION_PROT_MASK) + /* + * ept page table entry is present? + * note: unconditionally clear USER_EXEC until mode-based + * execute control is implemented + */ + error_code |= (exit_qualification & + (EPT_VIOLATION_PROT_MASK & ~EPT_VIOLATION_PROT_USER_EXEC)) ? PFERR_PRESENT_MASK : 0; if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 1e47c194af53..89f9fe30435d 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1845,7 +1845,7 @@ static inline bool tdx_is_sept_violation_unexpected_pending(struct kvm_vcpu *vcp if (eeq_type != TDX_EXT_EXIT_QUAL_TYPE_PENDING_EPT_VIOLATION) return false; - return !(eq & EPT_VIOLATION_PROT_MASK) && !(eq & EPT_VIOLATION_EXEC_FOR_RING3_LIN); + return !(eq & EPT_VIOLATION_PROT_MASK); } static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) From 2f4d934df00f63e736edf9437022db4bfae6f70f Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Apr 2026 11:41:52 -0400 Subject: [PATCH 0296/5214] KVM: x86/mmu: remove SPTE_PERM_MASK SPTE_PERM_MASK is no longer referenced by anything in the kernel. Signed-off-by: Jon Kohler Message-ID: <20251223054806.1611168-3-jon@nutanix.com> Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/spte.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 91ce29fd6f1b..28086fa86fe0 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -42,9 +42,6 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); #define SPTE_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) #endif -#define SPTE_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | shadow_user_mask \ - | shadow_x_mask | shadow_nx_mask | shadow_me_mask) - #define ACC_EXEC_MASK 1 #define ACC_WRITE_MASK PT_WRITABLE_MASK #define ACC_USER_MASK PT_USER_MASK From 9a67ab74021b4bfb2ad3c9f30af9dbab1127eb99 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Apr 2026 11:41:53 -0400 Subject: [PATCH 0297/5214] KVM: x86/mmu: free up bit 10 of PTEs in preparation for MBEC Update SPTE_MMIO_ALLOWED_MASK to allow EPT user executable (bit 10) to be treated like EPT RWX bit2:0, as when mode-based execute control is enabled, bit 10 can act like a "present" bit. Likewise do not include it in FROZEN_SPTE. No functional changes intended, other than the reduction of the maximum MMIO generation that is stored in page tables. Cc: Kai Huang Signed-off-by: Jon Kohler Message-ID: <20251223054806.1611168-4-jon@nutanix.com> Reviewed-by: Kai Huang Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/vmx.h | 2 ++ arch/x86/kvm/mmu/spte.h | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index b2291a766e3f..2b30b921b375 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -560,10 +560,12 @@ enum vmcs_field { #define VMX_EPT_ACCESS_BIT (1ull << 8) #define VMX_EPT_DIRTY_BIT (1ull << 9) #define VMX_EPT_SUPPRESS_VE_BIT (1ull << 63) + #define VMX_EPT_RWX_MASK (VMX_EPT_READABLE_MASK | \ VMX_EPT_WRITABLE_MASK | \ VMX_EPT_EXECUTABLE_MASK) #define VMX_EPT_MT_MASK (7ull << VMX_EPT_MT_EPTE_SHIFT) +#define VMX_EPT_USER_EXECUTABLE_MASK (1ull << 10) static inline u8 vmx_eptp_page_walk_level(u64 eptp) { diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 28086fa86fe0..4283cea3e66c 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -96,11 +96,11 @@ static_assert(!(EPT_SPTE_MMU_WRITABLE & SHADOW_ACC_TRACK_SAVED_MASK)); #undef SHADOW_ACC_TRACK_SAVED_MASK /* - * Due to limited space in PTEs, the MMIO generation is a 19 bit subset of + * Due to limited space in PTEs, the MMIO generation is an 18 bit subset of * the memslots generation and is derived as follows: * - * Bits 0-7 of the MMIO generation are propagated to spte bits 3-10 - * Bits 8-18 of the MMIO generation are propagated to spte bits 52-62 + * Bits 0-6 of the MMIO generation are propagated to spte bits 3-9 + * Bits 7-17 of the MMIO generation are propagated to spte bits 52-62 * * The KVM_MEMSLOT_GEN_UPDATE_IN_PROGRESS flag is intentionally not included in * the MMIO generation number, as doing so would require stealing a bit from @@ -111,7 +111,7 @@ static_assert(!(EPT_SPTE_MMU_WRITABLE & SHADOW_ACC_TRACK_SAVED_MASK)); */ #define MMIO_SPTE_GEN_LOW_START 3 -#define MMIO_SPTE_GEN_LOW_END 10 +#define MMIO_SPTE_GEN_LOW_END 9 #define MMIO_SPTE_GEN_HIGH_START 52 #define MMIO_SPTE_GEN_HIGH_END 62 @@ -133,7 +133,8 @@ static_assert(!(SPTE_MMU_PRESENT_MASK & * and so they're off-limits for generation; additional checks ensure the mask * doesn't overlap legal PA bits), and bit 63 (carved out for future usage). */ -#define SPTE_MMIO_ALLOWED_MASK (BIT_ULL(63) | GENMASK_ULL(51, 12) | GENMASK_ULL(2, 0)) +#define SPTE_MMIO_ALLOWED_MASK (BIT_ULL(63) | GENMASK_ULL(51, 12) | \ + BIT_ULL(10) | GENMASK_ULL(2, 0)) static_assert(!(SPTE_MMIO_ALLOWED_MASK & (SPTE_MMU_PRESENT_MASK | MMIO_SPTE_GEN_LOW_MASK | MMIO_SPTE_GEN_HIGH_MASK))); @@ -141,7 +142,7 @@ static_assert(!(SPTE_MMIO_ALLOWED_MASK & #define MMIO_SPTE_GEN_HIGH_BITS (MMIO_SPTE_GEN_HIGH_END - MMIO_SPTE_GEN_HIGH_START + 1) /* remember to adjust the comment above as well if you change these */ -static_assert(MMIO_SPTE_GEN_LOW_BITS == 8 && MMIO_SPTE_GEN_HIGH_BITS == 11); +static_assert(MMIO_SPTE_GEN_LOW_BITS == 7 && MMIO_SPTE_GEN_HIGH_BITS == 11); #define MMIO_SPTE_GEN_LOW_SHIFT (MMIO_SPTE_GEN_LOW_START - 0) #define MMIO_SPTE_GEN_HIGH_SHIFT (MMIO_SPTE_GEN_HIGH_START - MMIO_SPTE_GEN_LOW_BITS) @@ -217,10 +218,11 @@ extern u64 __read_mostly shadow_nonpresent_or_rsvd_mask; * * Only used by the TDP MMU. */ -#define FROZEN_SPTE (SHADOW_NONPRESENT_VALUE | 0x5a0ULL) +#define FROZEN_SPTE (SHADOW_NONPRESENT_VALUE | 0x1a0ULL) -/* Frozen SPTEs must not be misconstrued as shadow present PTEs. */ -static_assert(!(FROZEN_SPTE & SPTE_MMU_PRESENT_MASK)); +/* Frozen SPTEs must not be misconstrued as shadow or MMU present PTEs. */ +static_assert(!(FROZEN_SPTE & (SPTE_MMU_PRESENT_MASK | + VMX_EPT_RWX_MASK | VMX_EPT_USER_EXECUTABLE_MASK))); static inline bool is_frozen_spte(u64 spte) { From be4e45f360a2963ff4a1e17312ccbb273fa79bab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:41:54 -0400 Subject: [PATCH 0298/5214] KVM: x86/mmu: shuffle high bits of SPTEs in preparation for MBEC Access tracking will need to save bit 10 when MBEC is enabled. Right now it is simply shifting the R and X bits into bits 54 and 56, but bit 10 would not fit with the same scheme. Reorganize the high bits so that access tracking will use bits 52, 54 and 62. As a side effect, the free bits are compacted slightly, with 56-59 still unused. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/spte.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 4283cea3e66c..317b9cd1537c 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -17,10 +17,20 @@ */ #define SPTE_MMU_PRESENT_MASK BIT_ULL(11) +/* + * The ignored high bits are allocated as follows: + * - bits 52, 54: saved X-R bits for access tracking when EPT does not have A/D + * - bits 53 (EPT only): host writable + * - bits 55 (EPT only): MMU-writable + * - bits 56-59: unused + * - bits 60-61: type of A/D tracking + * - bits 62: unused + */ + /* * TDP SPTES (more specifically, EPT SPTEs) may not have A/D bits, and may also * be restricted to using write-protection (for L2 when CPU dirty logging, i.e. - * PML, is enabled). Use bits 52 and 53 to hold the type of A/D tracking that + * PML, is enabled). Use bits 60 and 61 to hold the type of A/D tracking that * is must be employed for a given TDP SPTE. * * Note, the "enabled" mask must be '0', as bits 62:52 are _reserved_ for PAE @@ -29,7 +39,7 @@ * TDP with CPU dirty logging (PML). If NPT ever gains PML-like support, it * must be restricted to 64-bit KVM. */ -#define SPTE_TDP_AD_SHIFT 52 +#define SPTE_TDP_AD_SHIFT 60 #define SPTE_TDP_AD_MASK (3ULL << SPTE_TDP_AD_SHIFT) #define SPTE_TDP_AD_ENABLED (0ULL << SPTE_TDP_AD_SHIFT) #define SPTE_TDP_AD_DISABLED (1ULL << SPTE_TDP_AD_SHIFT) @@ -65,7 +75,7 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); */ #define SHADOW_ACC_TRACK_SAVED_BITS_MASK (SPTE_EPT_READABLE_MASK | \ SPTE_EPT_EXECUTABLE_MASK) -#define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 54 +#define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 52 #define SHADOW_ACC_TRACK_SAVED_MASK (SHADOW_ACC_TRACK_SAVED_BITS_MASK << \ SHADOW_ACC_TRACK_SAVED_BITS_SHIFT) static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK)); @@ -84,8 +94,8 @@ static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK)); * to not overlap the A/D type mask or the saved access bits of access-tracked * SPTEs when A/D bits are disabled. */ -#define EPT_SPTE_HOST_WRITABLE BIT_ULL(57) -#define EPT_SPTE_MMU_WRITABLE BIT_ULL(58) +#define EPT_SPTE_HOST_WRITABLE BIT_ULL(53) +#define EPT_SPTE_MMU_WRITABLE BIT_ULL(55) static_assert(!(EPT_SPTE_HOST_WRITABLE & SPTE_TDP_AD_MASK)); static_assert(!(EPT_SPTE_MMU_WRITABLE & SPTE_TDP_AD_MASK)); From 6896bc190ed247ab8613362aa218f682bdab31ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:41:55 -0400 Subject: [PATCH 0299/5214] KVM: x86/mmu: remove SPTE_EPT_* spte.h is already including vmx.h, use the constants it defines. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/spte.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 317b9cd1537c..bc02a2e89a31 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -57,10 +57,6 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); #define ACC_USER_MASK PT_USER_MASK #define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK) -/* The mask for the R/X bits in EPT PTEs */ -#define SPTE_EPT_READABLE_MASK 0x1ull -#define SPTE_EPT_EXECUTABLE_MASK 0x4ull - #define SPTE_LEVEL_BITS 9 #define SPTE_LEVEL_SHIFT(level) __PT_LEVEL_SHIFT(level, SPTE_LEVEL_BITS) #define SPTE_INDEX(address, level) __PT_INDEX(address, level, SPTE_LEVEL_BITS) @@ -73,8 +69,8 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); * restored only when a write is attempted to the page. This mask obviously * must not overlap the A/D type mask. */ -#define SHADOW_ACC_TRACK_SAVED_BITS_MASK (SPTE_EPT_READABLE_MASK | \ - SPTE_EPT_EXECUTABLE_MASK) +#define SHADOW_ACC_TRACK_SAVED_BITS_MASK (VMX_EPT_READABLE_MASK | \ + VMX_EPT_EXECUTABLE_MASK) #define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 52 #define SHADOW_ACC_TRACK_SAVED_MASK (SHADOW_ACC_TRACK_SAVED_BITS_MASK << \ SHADOW_ACC_TRACK_SAVED_BITS_SHIFT) From 45c9dee6d6531bf1d0e0dbf577fb59850e34f6d0 Mon Sep 17 00:00:00 2001 From: Sowon Na Date: Fri, 17 Apr 2026 17:44:50 +0530 Subject: [PATCH 0300/5214] scsi: ufs: exynos: dt-bindings: Add ExynosAutov920 compatible string Add samsung,exynosautov920-ufs compatible for ExynosAutov920 SoC. Acked-by: Krzysztof Kozlowski Signed-off-by: Sowon Na Signed-off-by: Alim Akhtar Reviewed-by: Alim Akhtar Link: https://patch.msgid.link/20260417121452.827054-3-alim.akhtar@samsung.com Signed-off-by: Martin K. Petersen --- Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml b/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml index a7eb7ad85a94..710ce493f3b6 100644 --- a/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml @@ -19,6 +19,7 @@ properties: - samsung,exynos7-ufs - samsung,exynosautov9-ufs - samsung,exynosautov9-ufs-vh + - samsung,exynosautov920-ufs - tesla,fsd-ufs reg: From 50349bd5d0ab6d6f7e106a6cb1cdbf2bcfb75e08 Mon Sep 17 00:00:00 2001 From: Sowon Na Date: Fri, 17 Apr 2026 17:44:51 +0530 Subject: [PATCH 0301/5214] scsi: ufs: exynos: Add support for ExynosAutov920 SoC Add a dedicated compatible and drv_data with associated hooks for ExynosAutov920 SoC. ExynosAutov920 has a different mask of UFS sharability from ExynosAutov9, so add related changes for the same. Signed-off-by: Sowon Na Signed-off-by: Alim Akhtar [Alim: fixed unintended changes, other fixes] Link: https://patch.msgid.link/20260417121452.827054-4-alim.akhtar@samsung.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-exynos.c | 110 ++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c index 77a6c8e44485..b2f65c465525 100644 --- a/drivers/ufs/host/ufs-exynos.c +++ b/drivers/ufs/host/ufs-exynos.c @@ -97,6 +97,10 @@ #define UFS_EXYNOSAUTO_RD_SHARABLE BIT(1) #define UFS_EXYNOSAUTO_SHARABLE (UFS_EXYNOSAUTO_WR_SHARABLE | \ UFS_EXYNOSAUTO_RD_SHARABLE) +#define UFS_EXYNOSAUTOV920_WR_SHARABLE BIT(3) +#define UFS_EXYNOSAUTOV920_RD_SHARABLE BIT(2) +#define UFS_EXYNOSAUTOV920_SHARABLE (UFS_EXYNOSAUTOV920_WR_SHARABLE |\ + UFS_EXYNOSAUTOV920_RD_SHARABLE) #define UFS_GS101_WR_SHARABLE BIT(1) #define UFS_GS101_RD_SHARABLE BIT(0) #define UFS_GS101_SHARABLE (UFS_GS101_WR_SHARABLE | \ @@ -417,6 +421,95 @@ static int exynos7_ufs_post_pwr_change(struct exynos_ufs *ufs, return 0; } +static int exynosautov920_ufs_pre_link(struct exynos_ufs *ufs) +{ + struct ufs_hba *hba = ufs->hba; + int i; + u32 tx_line_reset_period, rx_line_reset_period; + + rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate) + / NSEC_PER_MSEC; + tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate) + / NSEC_PER_MSEC; + + unipro_writel(ufs, 0x5f, 0x44); + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40); + ufshcd_dme_set(hba, UIC_ARG_MIB(0x202), 0x02); + + for_each_ufs_rx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i), + (rx_line_reset_period >> 16) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i), + (rx_line_reset_period >> 8) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i), + (rx_line_reset_period) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x69); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6); + } + + for_each_ufs_tx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i), + 0x02); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i), + (tx_line_reset_period >> 16) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i), + (tx_line_reset_period >> 8) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i), + (tx_line_reset_period) & 0xFF); + + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x7f, i), 0x0); + } + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xa011), 0x8000); + + return 0; +} + +static int exynosautov920_ufs_post_link(struct exynos_ufs *ufs) +{ + struct ufs_hba *hba = ufs->hba; + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x9529), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15a4), 0x3e8); + ufshcd_dme_set(hba, UIC_ARG_MIB(0x9529), 0x0); + + return 0; +} + +static int exynosautov920_ufs_pre_pwr_change(struct exynos_ufs *ufs, + struct ufs_pa_layer_attr *pwr) +{ + struct ufs_hba *hba = ufs->hba; + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x15d4), 0x1); + + ufshcd_dme_set(hba, UIC_ARG_MIB(DL_FC0PROTTIMEOUTVAL), 8064); + ufshcd_dme_set(hba, UIC_ARG_MIB(DL_TC0REPLAYTIMEOUTVAL), 28224); + ufshcd_dme_set(hba, UIC_ARG_MIB(DL_AFC0REQTIMEOUTVAL), 20160); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000); + + unipro_writel(ufs, 8064, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0); + unipro_writel(ufs, 28224, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1); + unipro_writel(ufs, 20160, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2); + unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0); + unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1); + unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2); + + return 0; +} + /* * exynos_ufs_auto_ctrl_hcc - HCI core clock control by h/w * Control should be disabled in the below cases @@ -2201,6 +2294,21 @@ static const struct exynos_ufs_drv_data gs101_ufs_drvs = { .suspend = gs101_ufs_suspend, }; +static const struct exynos_ufs_drv_data exynosautov920_ufs_drvs = { + .uic_attr = &exynos7_uic_attr, + .quirks = UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING, + .opts = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL | + EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | + EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX | + EXYNOS_UFS_OPT_TIMER_TICK_SELECT, + .iocc_mask = UFS_EXYNOSAUTOV920_SHARABLE, + .drv_init = exynosauto_ufs_drv_init, + .post_hce_enable = exynosauto_ufs_post_hce_enable, + .pre_link = exynosautov920_ufs_pre_link, + .post_link = exynosautov920_ufs_post_link, + .pre_pwr_change = exynosautov920_ufs_pre_pwr_change, +}; + static const struct of_device_id exynos_ufs_of_match[] = { { .compatible = "google,gs101-ufs", .data = &gs101_ufs_drvs }, @@ -2210,6 +2318,8 @@ static const struct of_device_id exynos_ufs_of_match[] = { .data = &exynosauto_ufs_drvs }, { .compatible = "samsung,exynosautov9-ufs-vh", .data = &exynosauto_ufs_vh_drvs }, + { .compatible = "samsung,exynosautov920-ufs", + .data = &exynosautov920_ufs_drvs }, { .compatible = "tesla,fsd-ufs", .data = &fsd_ufs_drvs }, {}, From cc07b903a646bf6592182b27c63457d92f128125 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:08 +0900 Subject: [PATCH 0302/5214] PCI: endpoint: Add auxiliary resource query API Endpoint controller drivers may integrate auxiliary blocks (e.g. DMA engines) whose register windows and descriptor memories metadata need to be exposed to a remote peer. Endpoint function drivers need a generic way to discover such resources without hard-coding controller-specific helpers. Add pci_epc_get_aux_resources_count() / pci_epc_get_aux_resources() and the corresponding pci_epc_ops callbacks. The count helper returns the number of available resources, while the get helper fills a caller-provided array of resources described by type, physical address and size, plus type-specific metadata. Suggested-by: Manivannan Sadhasivam Suggested-by: Frank Li Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414141514.1341429-2-den@valinux.co.jp --- drivers/pci/endpoint/pci-epc-core.c | 80 +++++++++++++++++++++++++++++ include/linux/pci-epc.h | 54 +++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 6c3c58185fc5..831b40458dcd 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -156,6 +156,86 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, } EXPORT_SYMBOL_GPL(pci_epc_get_features); +/** + * pci_epc_get_aux_resources_count() - get the number of EPC-provided auxiliary resources + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * + * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control + * registers and/or descriptor memories can be exposed to the host by mapping + * them into BAR space. This helper queries how many such resources the backend + * provides. + * + * Return: the number of available resources on success, -EOPNOTSUPP if the + * backend does not support auxiliary resource queries, or another -errno on + * failure. + */ +int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no) +{ + int count; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!epc->ops->get_aux_resources_count) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + count = epc->ops->get_aux_resources_count(epc, func_no, + vfunc_no); + mutex_unlock(&epc->lock); + + return count; +} +EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources_count); + +/** + * pci_epc_get_aux_resources() - query EPC-provided auxiliary resources + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * @resources: output array + * @num_resources: size of @resources array in entries + * + * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control + * registers and/or descriptor memories can be exposed to the host by mapping + * them into BAR space. This helper queries the backend for such resources. + * + * Return: 0 on success, -EOPNOTSUPP if the backend does not support auxiliary + * resource queries, or another -errno on failure. + */ +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources) +{ + int ret; + + if (!resources || num_resources <= 0) + return -EINVAL; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!epc->ops->get_aux_resources) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + ret = epc->ops->get_aux_resources(epc, func_no, vfunc_no, resources, + num_resources); + mutex_unlock(&epc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources); + /** * pci_epc_stop() - stop the PCI link * @epc: the link of the EPC device that has to be stopped diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 1eca1264815b..f247cf9bcf1a 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -61,6 +61,47 @@ struct pci_epc_map { void __iomem *virt_addr; }; +/** + * enum pci_epc_aux_resource_type - auxiliary resource type identifiers + * @PCI_EPC_AUX_DOORBELL_MMIO: Doorbell MMIO, that might be outside the DMA + * controller register window + * + * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their + * register windows and descriptor memories into BAR space. This enum + * identifies the type of each exposable resource. + */ +enum pci_epc_aux_resource_type { + PCI_EPC_AUX_DOORBELL_MMIO, +}; + +/** + * struct pci_epc_aux_resource - a physical auxiliary resource that may be + * exposed for peer use + * @type: resource type, see enum pci_epc_aux_resource_type + * @phys_addr: physical base address of the resource + * @size: size of the resource in bytes + * @bar: BAR number where this resource is already exposed to the RC + * (NO_BAR if not) + * @bar_offset: offset within @bar where the resource starts (valid iff + * @bar != NO_BAR) + * @u: type-specific metadata + */ +struct pci_epc_aux_resource { + enum pci_epc_aux_resource_type type; + phys_addr_t phys_addr; + resource_size_t size; + enum pci_barno bar; + resource_size_t bar_offset; + + union { + /* PCI_EPC_AUX_DOORBELL_MMIO */ + struct { + int irq; /* IRQ number for the doorbell handler */ + u32 data; /* write value to ring the doorbell */ + } db_mmio; + } u; +}; + /** * struct pci_epc_ops - set of function pointers for performing EPC operations * @write_header: ops to populate configuration space header @@ -84,6 +125,9 @@ struct pci_epc_map { * @start: ops to start the PCI link * @stop: ops to stop the PCI link * @get_features: ops to get the features supported by the EPC + * @get_aux_resources_count: ops to get the number of controller-owned + * auxiliary resources + * @get_aux_resources: ops to retrieve controller-owned auxiliary resources * @owner: the module owner containing the ops */ struct pci_epc_ops { @@ -115,6 +159,11 @@ struct pci_epc_ops { void (*stop)(struct pci_epc *epc); const struct pci_epc_features* (*get_features)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); + int (*get_aux_resources_count)(struct pci_epc *epc, u8 func_no, + u8 vfunc_no); + int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources); struct module *owner; }; @@ -343,6 +392,11 @@ int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no); +int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no); +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources); enum pci_barno pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features); enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features From bfb9502651f689c338ba3e8aeb07d31c46e0cdf6 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:09 +0900 Subject: [PATCH 0303/5214] PCI: dwc: Record integrated eDMA register window Some DesignWare PCIe controllers integrate an eDMA block whose registers are located in a dedicated register window. The EP-side aux-resource code exposes an interrupt-emulation doorbell register (DOORBELL_MMIO) from that window. Its location is derived from the start of the eDMA register window plus the doorbell offset already provided by dw-edma, and the window size is used to validate the computed register location. Record the physical base and size of the integrated eDMA register window in struct dw_pcie so the EP-side DesignWare aux-resource provider can construct that doorbell resource. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Tested-by: Niklas Cassel Reviewed-by: Frank Li Link: https://patch.msgid.link/20260414141514.1341429-3-den@valinux.co.jp --- drivers/pci/controller/dwc/pcie-designware.c | 4 ++++ drivers/pci/controller/dwc/pcie-designware.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..22164e0068a9 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -162,8 +162,12 @@ int dw_pcie_get_resources(struct dw_pcie *pci) pci->edma.reg_base = devm_ioremap_resource(pci->dev, res); if (IS_ERR(pci->edma.reg_base)) return PTR_ERR(pci->edma.reg_base); + pci->edma_reg_phys = res->start; + pci->edma_reg_size = resource_size(res); } else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) { pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET; + pci->edma_reg_phys = pci->atu_phys_addr + DEFAULT_DBI_DMA_OFFSET; + pci->edma_reg_size = pci->atu_size - DEFAULT_DBI_DMA_OFFSET; } } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..f3314ceff8d7 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -544,6 +544,8 @@ struct dw_pcie { int max_link_speed; u8 n_fts[2]; struct dw_edma_chip edma; + phys_addr_t edma_reg_phys; + resource_size_t edma_reg_size; bool l1ss_support; /* L1 PM Substates support */ struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS]; struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS]; From ec2075cfc629eed09d8865621be76ff2197781d6 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:10 +0900 Subject: [PATCH 0304/5214] PCI: dwc: ep: Expose integrated eDMA resources via EPC aux-resource API Implement the EPC aux-resource API for DesignWare endpoint controllers with integrated eDMA. Currently, only report an interrupt-emulation doorbell register (PCI_EPC_AUX_DOORBELL_MMIO), including its Linux IRQ and the write data needed to trigger the interrupt. If the DMA controller MMIO window is already exposed via a platform-owned fixed BAR subregion, also provide the BAR number and offset so EPF drivers can reuse it without reprogramming the BAR. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://patch.msgid.link/20260414141514.1341429-4-den@valinux.co.jp --- .../pci/controller/dwc/pcie-designware-ep.c | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index d4dc3b24da60..e4c6f7193495 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "pcie-designware.h" @@ -817,6 +818,122 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) return ep->ops->get_features(ep); } +static const struct pci_epc_bar_rsvd_region * +dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep, + enum pci_epc_bar_rsvd_region_type type, + enum pci_barno *bar, + resource_size_t *bar_offset) +{ + const struct pci_epc_features *features; + const struct pci_epc_bar_desc *bar_desc; + const struct pci_epc_bar_rsvd_region *r; + int i, j; + + if (!ep->ops->get_features) + return NULL; + + features = ep->ops->get_features(ep); + if (!features) + return NULL; + + for (i = BAR_0; i <= BAR_5; i++) { + bar_desc = &features->bar[i]; + + if (!bar_desc->nr_rsvd_regions || !bar_desc->rsvd_regions) + continue; + + for (j = 0; j < bar_desc->nr_rsvd_regions; j++) { + r = &bar_desc->rsvd_regions[j]; + + if (r->type != type) + continue; + + if (bar) + *bar = i; + if (bar_offset) + *bar_offset = r->offset; + return r; + } + } + + return NULL; +} + +static int +dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_edma_chip *edma = &pci->edma; + + if (!pci->edma_reg_size) + return 0; + + if (edma->db_offset == ~0) + return 0; + + return 1; +} + +static int +dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + const struct pci_epc_bar_rsvd_region *rsvd; + struct dw_edma_chip *edma = &pci->edma; + enum pci_barno dma_ctrl_bar = NO_BAR; + resource_size_t db_offset = edma->db_offset; + resource_size_t dma_ctrl_bar_offset = 0; + resource_size_t dma_reg_size; + int count; + + count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no); + if (count < 0) + return count; + + if (num_resources < count) + return -ENOSPC; + + if (!count) + return 0; + + dma_reg_size = pci->edma_reg_size; + + rsvd = dw_pcie_ep_find_bar_rsvd_region(ep, + PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO, + &dma_ctrl_bar, + &dma_ctrl_bar_offset); + if (rsvd && rsvd->size < dma_reg_size) + dma_reg_size = rsvd->size; + + /* + * For interrupt-emulation doorbells, report a standalone resource + * instead of bundling it into the DMA controller MMIO resource. + */ + if (range_end_overflows_t(resource_size_t, db_offset, + sizeof(u32), dma_reg_size)) + return -EINVAL; + + resources[0] = (struct pci_epc_aux_resource) { + .type = PCI_EPC_AUX_DOORBELL_MMIO, + .phys_addr = pci->edma_reg_phys + db_offset, + .size = sizeof(u32), + .bar = dma_ctrl_bar, + .bar_offset = dma_ctrl_bar != NO_BAR ? + dma_ctrl_bar_offset + db_offset : 0, + .u.db_mmio = { + .irq = edma->db_irq, + .data = 0, /* write 0 to assert */ + }, + }; + + return 0; +} + static const struct pci_epc_ops epc_ops = { .write_header = dw_pcie_ep_write_header, .set_bar = dw_pcie_ep_set_bar, @@ -832,6 +949,8 @@ static const struct pci_epc_ops epc_ops = { .start = dw_pcie_ep_start, .stop = dw_pcie_ep_stop, .get_features = dw_pcie_ep_get_features, + .get_aux_resources_count = dw_pcie_ep_get_aux_resources_count, + .get_aux_resources = dw_pcie_ep_get_aux_resources, }; /** From a3a079e5c5b7748f206c4baeb92593f9eca3184a Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:11 +0900 Subject: [PATCH 0305/5214] PCI: endpoint: pci-ep-msi: Refactor doorbell allocation for new backends Prepare pci-ep-msi for non-MSI doorbell backends. Factor MSI doorbell allocation into a helper and extend struct pci_epf_doorbell_msg with: - irq_flags: required IRQ request flags (e.g. IRQF_SHARED for some backends) - type: doorbell backend type - bar/offset: pre-exposed doorbell target location, if any Initialize these fields for the existing MSI-backed doorbell implementation. Also add PCI_EPF_DOORBELL_EMBEDDED type, which is to be implemented in a follow-up patch. No functional changes. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Tested-by: Niklas Cassel Reviewed-by: Frank Li Link: https://patch.msgid.link/20260414141514.1341429-5-den@valinux.co.jp --- drivers/pci/endpoint/pci-ep-msi.c | 54 ++++++++++++++++++++++--------- include/linux/pci-epf.h | 23 +++++++++++-- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 1395919571f8..85fe46103220 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -35,23 +36,13 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) pci_epc_put(epc); } -int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) +static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db) { - struct pci_epc *epc = epf->epc; + struct pci_epf_doorbell_msg *msg; struct device *dev = &epf->dev; + struct pci_epc *epc = epf->epc; struct irq_domain *domain; - void *msg; - int ret; - int i; - - /* TODO: Multi-EPF support */ - if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { - dev_err(dev, "MSI doorbell doesn't support multiple EPF\n"); - return -EINVAL; - } - - if (epf->db_msg) - return -EBUSY; + int ret, i; domain = of_msi_map_get_device_domain(epc->dev.parent, 0, DOMAIN_BUS_PLATFORM_MSI); @@ -74,6 +65,12 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) if (!msg) return -ENOMEM; + for (i = 0; i < num_db; i++) + msg[i] = (struct pci_epf_doorbell_msg) { + .type = PCI_EPF_DOORBELL_MSI, + .bar = NO_BAR, + }; + epf->num_db = num_db; epf->db_msg = msg; @@ -90,13 +87,40 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) for (i = 0; i < num_db; i++) epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); + return 0; +} + +int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) +{ + struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; + int ret; + + /* TODO: Multi-EPF support */ + if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { + dev_err(dev, "Doorbell doesn't support multiple EPF\n"); + return -EINVAL; + } + + if (epf->db_msg) + return -EBUSY; + + ret = pci_epf_alloc_doorbell_msi(epf, num_db); + if (!ret) + return 0; + + dev_err(dev, "Failed to allocate doorbell: %d\n", ret); return ret; } EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); void pci_epf_free_doorbell(struct pci_epf *epf) { - platform_device_msi_free_irqs_all(epf->epc->dev.parent); + if (!epf->db_msg) + return; + + if (epf->db_msg[0].type == PCI_EPF_DOORBELL_MSI) + platform_device_msi_free_irqs_all(epf->epc->dev.parent); kfree(epf->db_msg); epf->db_msg = NULL; diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 7737a7c03260..cd747447a1ea 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -152,14 +152,33 @@ struct pci_epf_bar { struct pci_epf_bar_submap *submap; }; +enum pci_epf_doorbell_type { + PCI_EPF_DOORBELL_MSI = 0, + PCI_EPF_DOORBELL_EMBEDDED, +}; + /** * struct pci_epf_doorbell_msg - represents doorbell message - * @msg: MSI message - * @virq: IRQ number of this doorbell MSI message + * @msg: Doorbell address/data pair to be mapped into BAR space. + * For MSI-backed doorbells this is the MSI message, while for + * "embedded" doorbells this represents an MMIO write that asserts + * an interrupt on the EP side. + * @virq: IRQ number of this doorbell message + * @irq_flags: Required flags for request_irq()/request_threaded_irq(). + * Callers may OR-in additional flags (e.g. IRQF_ONESHOT). + * @type: Doorbell type. + * @bar: BAR number where the doorbell target is already exposed to the RC + * (NO_BAR if not) + * @offset: offset within @bar for the doorbell target (valid iff + * @bar != NO_BAR) */ struct pci_epf_doorbell_msg { struct msi_msg msg; int virq; + unsigned long irq_flags; + enum pci_epf_doorbell_type type; + enum pci_barno bar; + resource_size_t offset; }; /** From a48df51d23138388900995add2854cda4aa68e55 Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Tue, 28 Apr 2026 15:18:56 -0700 Subject: [PATCH 0306/5214] remoteproc: xlnx: Check remote core state The remote state is set to RPROC_DETACHED if the resource table is found in the memory. However, this can be wrong if the remote is not started, but firmware is still loaded in the memory. Use PM_GET_NODE_STATUS call to the firmware to request the state of the RPU node. If the RPU is actually out of reset and running, only then move the remote state to RPROC_DETACHED, otherwise keep the remote state to RPROC_OFFLINE. Signed-off-by: Tanmay Shah Fixes: bca4b02ef92e ("remoteproc: xlnx: Add attach detach support") Reviewed-by: Beleswar Padhi Acked-by: Michal Simek Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260428221855.313752-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- drivers/firmware/xilinx/zynqmp.c | 28 +++++++++++++++ drivers/remoteproc/xlnx_r5_remoteproc.c | 46 +++++++++++++++++++------ include/linux/firmware/xlnx-zynqmp.h | 21 +++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index fbe8510f4927..af838b2dc327 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1450,6 +1450,34 @@ int zynqmp_pm_get_node_status(const u32 node, u32 *const status, } EXPORT_SYMBOL_GPL(zynqmp_pm_get_node_status); +/** + * zynqmp_pm_get_rpu_node_status - PM call to request a RPU node's current power state + * @node: ID of the RPU component or sub-system in question + * @status: Current operating state of the requested RPU node. + * @requirements: Current requirements asserted on the RPU node. + * @usage: Usage information, used for RPU slave nodes only: + * PM_USAGE_NO_MASTER - No master is currently using + * the node + * PM_USAGE_CURRENT_MASTER - Only requesting master is + * currently using the node + * PM_USAGE_OTHER_MASTER - Only other masters are + * currently using the node + * PM_USAGE_BOTH_MASTERS - Both the current and at least + * one other master is currently + * using the node + * + * Return: Returns status, either success or error+reason + */ +int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, u32 *const usage) +{ + if (zynqmp_pm_feature(PM_GET_NODE_STATUS) < PM_API_VERSION_2) + return -EOPNOTSUPP; + + return zynqmp_pm_get_node_status(node, status, requirements, usage); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_node_status); + /** * zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to * be powered down forcefully diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 50a9974f3202..45a62cb98072 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -948,16 +948,6 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } - /* - * If firmware is already available in the memory then move rproc state - * to DETACHED. Firmware can be preloaded via debugger or by any other - * agent (processors) in the system. - * If firmware isn't available in the memory and resource table isn't - * found, then rproc state remains OFFLINE. - */ - if (!zynqmp_r5_get_rsc_table_va(r5_core)) - r5_rproc->state = RPROC_DETACHED; - r5_core->rproc = r5_rproc; return r5_core; @@ -1210,6 +1200,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, { struct device *dev = cluster->dev; struct zynqmp_r5_core *r5_core; + u32 req, usage, status; int ret = -EINVAL, i; r5_core = cluster->r5_cores[0]; @@ -1255,6 +1246,41 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, ret = zynqmp_r5_get_sram_banks(r5_core); if (ret) return ret; + + /* + * It is possible that firmware is loaded into the memory, but + * RPU (remote) is not running. In such case, RPU state will be + * moved to RPROC_DETACHED wrongfully. To avoid it first make + * sure RPU is power-on and out of reset before parsing for the + * resource table. + */ + ret = zynqmp_pm_get_rpu_node_status(r5_core->pm_domain_id, + &status, &req, &usage); + if (ret) { + dev_warn(r5_core->dev, + "failed to get rpu node status, err %d\n", ret); + continue; + } + + /* + * If RPU state is power on and out of reset i.e. running, then + * assign RPROC_DETACHED state. If the RPU is not out of reset + * then do not attempt to attach to the remote processor. + */ + if (status == PM_NODE_RUNNING) { + /* + * Not all the firmware that is running on the remote + * core is expected to have the resource table. The + * firmware might not use RPMsg at all, and in that case + * resource table becomes irrelevant. However, we still + * need to make sure that running core is not reported + * as offline. so do not decide remote core state based + * on the resource table availability + */ + if (zynqmp_r5_get_rsc_table_va(r5_core)) + dev_dbg(r5_core->dev, "rsc tbl not found\n"); + r5_core->rproc->state = RPROC_DETACHED; + } } return 0; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index d70dcd462b44..7e27b0f7bf7e 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -542,6 +542,18 @@ enum pm_gem_config_type { GEM_CONFIG_FIXED = 2, }; +/** + * enum pm_node_status - Device node status provided by xilpm fw + * @PM_NODE_UNUSED: Device is not used + * @PM_NODE_RUNNING: Device is power-on and out of reset + * @PM_NODE_HALT: Device is power-on but in the reset state + */ +enum pm_node_status { + PM_NODE_UNUSED = 0, + PM_NODE_RUNNING = 1, + PM_NODE_HALT = 12, +}; + /** * struct zynqmp_pm_query_data - PM query data * @qid: query ID @@ -630,6 +642,8 @@ int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode); int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode); int zynqmp_pm_get_node_status(const u32 node, u32 *const status, u32 *const requirements, u32 *const usage); +int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, u32 *const usage); int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); @@ -939,6 +953,13 @@ static inline int zynqmp_pm_get_node_status(const u32 node, u32 *const status, return -ENODEV; } +static inline int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status, + u32 *const requirements, + u32 *const usage) +{ + return -ENODEV; +} + static inline int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) From 3f7fd751d8a0bd47165d515d6479ec81944fb5d2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 20 Mar 2026 20:52:17 -0700 Subject: [PATCH 0307/5214] firmware: stratix10-svc: kmalloc_array + kzalloc to flex Use a flexible array member to combine allocations. Simplifies memory management. Add __counted_by for extra runtime analysis. Also move counting variable assignment up as required by __counted_by. Signed-off-by: Rosen Penev Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 90 +++++++++++++++----------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index e9e35d67ef96..5a76cf3fc83a 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -240,37 +240,6 @@ struct stratix10_async_ctrl { DECLARE_HASHTABLE(trx_list, ASYNC_TRX_HASH_BITS); }; -/** - * struct stratix10_svc_controller - service controller - * @dev: device - * @chans: array of service channels - * @num_chans: number of channels in 'chans' array - * @num_active_client: number of active service client - * @node: list management - * @genpool: memory pool pointing to the memory region - * @complete_status: state for completion - * @invoke_fn: function to issue secure monitor call or hypervisor call - * @svc: manages the list of client svc drivers - * @sdm_lock: only allows a single command single response to SDM - * @actrl: async control structure - * - * This struct is used to create communication channels for service clients, to - * handle secure monitor or hypervisor call. - */ -struct stratix10_svc_controller { - struct device *dev; - struct stratix10_svc_chan *chans; - int num_chans; - int num_active_client; - struct list_head node; - struct gen_pool *genpool; - struct completion complete_status; - svc_invoke_fn *invoke_fn; - struct stratix10_svc *svc; - struct mutex sdm_lock; - struct stratix10_async_ctrl actrl; -}; - /** * struct stratix10_svc_chan - service communication channel * @ctrl: pointer to service controller which is the provider of this channel @@ -296,6 +265,37 @@ struct stratix10_svc_chan { struct stratix10_async_chan *async_chan; }; +/** + * struct stratix10_svc_controller - service controller + * @dev: device + * @num_chans: number of channels in 'chans' array + * @num_active_client: number of active service client + * @node: list management + * @genpool: memory pool pointing to the memory region + * @complete_status: state for completion + * @invoke_fn: function to issue secure monitor call or hypervisor call + * @svc: manages the list of client svc drivers + * @sdm_lock: only allows a single command single response to SDM + * @actrl: async control structure + * @chans: array of service channels + * + * This struct is used to create communication channels for service clients, to + * handle secure monitor or hypervisor call. + */ +struct stratix10_svc_controller { + struct device *dev; + int num_chans; + int num_active_client; + struct list_head node; + struct gen_pool *genpool; + struct completion complete_status; + svc_invoke_fn *invoke_fn; + struct stratix10_svc *svc; + struct mutex sdm_lock; + struct stratix10_async_ctrl actrl; + struct stratix10_svc_chan chans[] __counted_by(num_chans); +}; + static LIST_HEAD(svc_ctrl); static LIST_HEAD(svc_data_mem); @@ -1901,7 +1901,6 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stratix10_svc_controller *controller; - struct stratix10_svc_chan *chans; struct gen_pool *genpool; struct stratix10_svc_sh_memory *sh_memory; struct stratix10_svc *svc = NULL; @@ -1929,23 +1928,16 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) return PTR_ERR(genpool); /* allocate service controller and supporting channel */ - controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); + controller = devm_kzalloc(dev, struct_size(controller, chans, SVC_NUM_CHANNEL), + GFP_KERNEL); if (!controller) { ret = -ENOMEM; goto err_destroy_pool; } - chans = devm_kmalloc_array(dev, SVC_NUM_CHANNEL, - sizeof(*chans), GFP_KERNEL | __GFP_ZERO); - if (!chans) { - ret = -ENOMEM; - goto err_destroy_pool; - } - - controller->dev = dev; controller->num_chans = SVC_NUM_CHANNEL; + controller->dev = dev; controller->num_active_client = 0; - controller->chans = chans; controller->genpool = genpool; controller->invoke_fn = invoke_fn; INIT_LIST_HEAD(&controller->node); @@ -1962,16 +1954,16 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) mutex_init(&controller->sdm_lock); for (i = 0; i < SVC_NUM_CHANNEL; i++) { - chans[i].scl = NULL; - chans[i].ctrl = controller; - chans[i].name = (char *)chan_names[i]; - spin_lock_init(&chans[i].lock); - ret = kfifo_alloc(&chans[i].svc_fifo, fifo_size, GFP_KERNEL); + controller->chans[i].scl = NULL; + controller->chans[i].ctrl = controller; + controller->chans[i].name = (char *)chan_names[i]; + spin_lock_init(&controller->chans[i].lock); + ret = kfifo_alloc(&controller->chans[i].svc_fifo, fifo_size, GFP_KERNEL); if (ret) { dev_err(dev, "failed to allocate FIFO %d\n", i); goto err_free_fifos; } - spin_lock_init(&chans[i].svc_fifo_lock); + spin_lock_init(&controller->chans[i].svc_fifo_lock); } list_add_tail(&controller->node, &svc_ctrl); @@ -2015,7 +2007,7 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) list_del(&controller->node); /* free only the FIFOs that were successfully allocated */ while (i--) - kfifo_free(&chans[i].svc_fifo); + kfifo_free(&controller->chans[i].svc_fifo); stratix10_svc_async_exit(controller); err_destroy_pool: gen_pool_destroy(genpool); From 4b0a32016347bfd6ae9849f21b1b767905f68d14 Mon Sep 17 00:00:00 2001 From: Siew Chin Lim Date: Mon, 6 Sep 2021 09:46:21 +0800 Subject: [PATCH 0308/5214] firmware: stratix10-svc: change get provision data to async SMC call Change INTEL_SIP_SMC_FCS_GET_PROVISION_DATA's SMC call to async from sync to avoid long runtime which may cause the watchdog timeout issue. Signed-off-by: Richard Gong Signed-off-by: Siew Chin Lim Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 4 ++-- include/linux/firmware/intel/stratix10-smc.h | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 5a76cf3fc83a..282211d2612b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -463,6 +463,7 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, case COMMAND_FCS_SEND_CERTIFICATE: case COMMAND_FCS_DATA_ENCRYPTION: case COMMAND_FCS_DATA_DECRYPTION: + case COMMAND_FCS_GET_PROVISION_DATA: cb_data->status = BIT(SVC_STATUS_OK); break; case COMMAND_RECONFIG_DATA_SUBMIT: @@ -491,7 +492,6 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, cb_data->kaddr2 = &res.a2; break; case COMMAND_FCS_RANDOM_NUMBER_GEN: - case COMMAND_FCS_GET_PROVISION_DATA: case COMMAND_POLL_SERVICE_STATUS: cb_data->status = BIT(SVC_STATUS_OK); cb_data->kaddr1 = &res.a1; @@ -674,7 +674,7 @@ static int svc_normal_to_secure_thread(void *data) break; case COMMAND_FCS_GET_PROVISION_DATA: a0 = INTEL_SIP_SMC_FCS_GET_PROVISION_DATA; - a1 = (unsigned long)pdata->paddr; + a1 = 0; a2 = 0; break; /* for HWMON */ diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 935dba3633b5..36ea619ea2ad 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -605,25 +605,21 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) /** * Request INTEL_SIP_SMC_FCS_GET_PROVISION_DATA - * Sync call to dump all the fuses and key hashes + * Async call to dump all the fuses and key hashes * * Call register usage: * a0 INTEL_SIP_SMC_FCS_GET_PROVISION_DATA - * a1 the physical address for firmware to write structure of fuse and - * key hashes - * a2-a7 not used + * a1-a7 not used * * Return status: * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_FCS_ERROR or * INTEL_SIP_SMC_FCS_REJECTED - * a1 mailbox error - * a2 physical address for the structure of fuse and key hashes - * a3 the size of structure + * a1-a3 not used * */ #define INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA 94 #define INTEL_SIP_SMC_FCS_GET_PROVISION_DATA \ - INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA) + INTEL_SIP_SMC_STD_CALL_VAL(INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA) /** * Request INTEL_SIP_SMC_HWMON_READTEMP From 0c768fa0aeb882fb5fbe75f4dcf0eb6ed23b9e8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:41:56 -0400 Subject: [PATCH 0309/5214] KVM: x86/mmu: merge make_spte_{non,}executable As the logic will become more complicated with the introduction of MBEC, at least write it only once. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/spte.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 85a0473809b0..849a1c1c92b5 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -317,14 +317,16 @@ static u64 modify_spte_protections(u64 spte, u64 set, u64 clear) return spte; } -static u64 make_spte_executable(u64 spte) +static u64 change_spte_executable(u64 spte, u8 access) { - return modify_spte_protections(spte, shadow_x_mask, shadow_nx_mask); -} + u64 set, clear; -static u64 make_spte_nonexecutable(u64 spte) -{ - return modify_spte_protections(spte, shadow_nx_mask, shadow_x_mask); + if (access & ACC_EXEC_MASK) + set = shadow_x_mask; + else + set = shadow_nx_mask; + clear = set ^ (shadow_nx_mask | shadow_x_mask); + return modify_spte_protections(spte, set, clear); } /* @@ -356,8 +358,8 @@ u64 make_small_spte(struct kvm *kvm, u64 huge_spte, * the page executable as the NX hugepage mitigation no longer * applies. */ - if ((role.access & ACC_EXEC_MASK) && is_nx_huge_page_enabled(kvm)) - child_spte = make_spte_executable(child_spte); + if (is_nx_huge_page_enabled(kvm)) + child_spte = change_spte_executable(child_spte, role.access); } return child_spte; @@ -379,7 +381,7 @@ u64 make_huge_spte(struct kvm *kvm, u64 small_spte, int level) huge_spte &= KVM_HPAGE_MASK(level) | ~PAGE_MASK; if (is_nx_huge_page_enabled(kvm)) - huge_spte = make_spte_nonexecutable(huge_spte); + huge_spte = change_spte_executable(huge_spte, 0); return huge_spte; } From 296b29597db1cf96b955da0f1bcb34d2759dd9d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:41:57 -0400 Subject: [PATCH 0310/5214] KVM: x86/mmu: rename and clarify BYTE_MASK The BYTE_MASK macro is the central point of the black magic in update_permission_bitmask(). Rename it to something that relates to how it is used, and add a comment explaining how it works. Using shifts instead of powers of two was actually suggested by David Hildenbrand back in 2017 for clarity[1] but I evidently forgot his suggestion when applying to kvm.git. [1] https://lore.kernel.org/kvm/e4b5df86-31ae-2f4e-0666-393753e256df@redhat.com/ Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 63 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 24fbc9ea502a..d94a488db79d 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5529,31 +5529,55 @@ reset_ept_shadow_zero_bits_mask(struct kvm_mmu *context, bool execonly) max_huge_page_level); } -#define BYTE_MASK(access) \ - ((1 & (access) ? 2 : 0) | \ - (2 & (access) ? 4 : 0) | \ - (3 & (access) ? 8 : 0) | \ - (4 & (access) ? 16 : 0) | \ - (5 & (access) ? 32 : 0) | \ - (6 & (access) ? 64 : 0) | \ - (7 & (access) ? 128 : 0)) - +/* + * Build a mask with all combinations of PTE access rights that + * include the given access bit. The mask can be queried with + * "mask & (1 << access)", where access is a combination of + * ACC_* bits. + * + * By mixing and matching multiple masks returned by ACC_BITS_MASK, + * update_permission_bitmask() builds what is effectively a + * two-dimensional array of bools. The second dimension is + * provided by individual bits of permissions[pfec >> 1], and + * logical &, | and ~ operations operate on all the 8 possible + * combinations of ACC_* bits. + */ +#define ACC_BITS_MASK(access) \ + ((1 & (access) ? 1 << 1 : 0) | \ + (2 & (access) ? 1 << 2 : 0) | \ + (3 & (access) ? 1 << 3 : 0) | \ + (4 & (access) ? 1 << 4 : 0) | \ + (5 & (access) ? 1 << 5 : 0) | \ + (6 & (access) ? 1 << 6 : 0) | \ + (7 & (access) ? 1 << 7 : 0)) static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) { - unsigned byte; + unsigned index; - const u8 x = BYTE_MASK(ACC_EXEC_MASK); - const u8 w = BYTE_MASK(ACC_WRITE_MASK); - const u8 u = BYTE_MASK(ACC_USER_MASK); + const u8 x = ACC_BITS_MASK(ACC_EXEC_MASK); + const u8 w = ACC_BITS_MASK(ACC_WRITE_MASK); + const u8 u = ACC_BITS_MASK(ACC_USER_MASK); bool cr4_smep = is_cr4_smep(mmu); bool cr4_smap = is_cr4_smap(mmu); bool cr0_wp = is_cr0_wp(mmu); bool efer_nx = is_efer_nx(mmu); - for (byte = 0; byte < ARRAY_SIZE(mmu->permissions); ++byte) { - unsigned pfec = byte << 1; + /* + * In hardware, page fault error codes are generated (as the name + * suggests) on any kind of page fault. permission_fault() and + * paging_tmpl.h already use the same bits after a successful page + * table walk, to indicate the kind of access being performed. + * + * However, PFERR_PRESENT_MASK and PFERR_RSVD_MASK are never set here, + * exactly because the page walk is successful. PFERR_PRESENT_MASK is + * removed by the shift, while PFERR_RSVD_MASK is repurposed in + * permission_fault() to indicate accesses that are *not* subject to + * SMAP restrictions. + */ + for (index = 0; index < ARRAY_SIZE(mmu->permissions); ++index) { + unsigned pfec = index << 1; /* * Each "*f" variable has a 1 bit for each UWX value @@ -5598,16 +5622,15 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) * - The access is supervisor mode * - If implicit supervisor access or X86_EFLAGS_AC is clear * - * Here, we cover the first four conditions. - * The fifth is computed dynamically in permission_fault(); - * PFERR_RSVD_MASK bit will be set in PFEC if the access is - * *not* subject to SMAP restrictions. + * Here, we cover the first four conditions. The fifth + * is computed dynamically in permission_fault() and + * communicated by setting PFERR_RSVD_MASK. */ if (cr4_smap) smapf = (pfec & (PFERR_RSVD_MASK|PFERR_FETCH_MASK)) ? 0 : kf; } - mmu->permissions[byte] = ff | uf | wf | smepf | smapf; + mmu->permissions[index] = ff | uf | wf | smepf | smapf; } } From 1622fc51fd27388c46a251af8372b4a3fa3a7c77 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 30 Apr 2026 18:42:47 +0200 Subject: [PATCH 0311/5214] Input: pcap_keys - remove unused driver Support for the ezx series of phones was removed in 2022, this driver is just dead code. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260430164326.2766500-1-arnd@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 10 --- drivers/input/misc/Makefile | 1 - drivers/input/misc/pcap_keys.c | 125 --------------------------------- 3 files changed, 136 deletions(-) delete mode 100644 drivers/input/misc/pcap_keys.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 94a753fcb64f..1f6c57dba030 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -745,16 +745,6 @@ config INPUT_WM831X_ON To compile this driver as a module, choose M here: the module will be called wm831x_on. -config INPUT_PCAP - tristate "Motorola EZX PCAP misc input events" - depends on EZX_PCAP - help - Say Y here if you want to use Power key and Headphone button - on Motorola EZX phones. - - To compile this driver as a module, choose M here: the - module will be called pcap_keys. - config INPUT_ADXL34X tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer" default n diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 415fc4e2918b..2281d6803fce 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o -obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PF1550_ONKEY) += pf1550-onkey.o diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c deleted file mode 100644 index b19899b50d0b..000000000000 --- a/drivers/input/misc/pcap_keys.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Input driver for PCAP events: - * * Power key - * * Headphone button - * - * Copyright (c) 2008,2009 Ilya Petrov - */ - -#include -#include -#include -#include -#include -#include - -struct pcap_keys { - struct pcap_chip *pcap; - struct input_dev *input; -}; - -/* PCAP2 interrupts us on keypress */ -static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys) -{ - struct pcap_keys *pcap_keys = _pcap_keys; - int pirq = irq_to_pcap(pcap_keys->pcap, irq); - u32 pstat; - - ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat); - pstat &= 1 << pirq; - - switch (pirq) { - case PCAP_IRQ_ONOFF: - input_report_key(pcap_keys->input, KEY_POWER, !pstat); - break; - case PCAP_IRQ_MIC: - input_report_key(pcap_keys->input, KEY_HP, !pstat); - break; - } - - input_sync(pcap_keys->input); - - return IRQ_HANDLED; -} - -static int pcap_keys_probe(struct platform_device *pdev) -{ - int err = -ENOMEM; - struct pcap_keys *pcap_keys; - struct input_dev *input_dev; - - pcap_keys = kmalloc_obj(*pcap_keys); - if (!pcap_keys) - return err; - - pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent); - - input_dev = input_allocate_device(); - if (!input_dev) - goto fail; - - pcap_keys->input = input_dev; - - platform_set_drvdata(pdev, pcap_keys); - input_dev->name = pdev->name; - input_dev->phys = "pcap-keys/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->dev.parent = &pdev->dev; - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(KEY_POWER, input_dev->keybit); - __set_bit(KEY_HP, input_dev->keybit); - - err = input_register_device(input_dev); - if (err) - goto fail_allocate; - - err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), - pcap_keys_handler, 0, "Power key", pcap_keys); - if (err) - goto fail_register; - - err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), - pcap_keys_handler, 0, "Headphone button", pcap_keys); - if (err) - goto fail_pwrkey; - - return 0; - -fail_pwrkey: - free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); -fail_register: - input_unregister_device(input_dev); - goto fail; -fail_allocate: - input_free_device(input_dev); -fail: - kfree(pcap_keys); - return err; -} - -static void pcap_keys_remove(struct platform_device *pdev) -{ - struct pcap_keys *pcap_keys = platform_get_drvdata(pdev); - - free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); - free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys); - - input_unregister_device(pcap_keys->input); - kfree(pcap_keys); -} - -static struct platform_driver pcap_keys_device_driver = { - .probe = pcap_keys_probe, - .remove = pcap_keys_remove, - .driver = { - .name = "pcap-keys", - } -}; -module_platform_driver(pcap_keys_device_driver); - -MODULE_DESCRIPTION("Motorola PCAP2 input events driver"); -MODULE_AUTHOR("Ilya Petrov "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcap_keys"); From cde5e7777f2e0b625d576b8725c65ec1f3b12b79 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 30 Apr 2026 18:42:48 +0200 Subject: [PATCH 0312/5214] Input: pcap_ts - remove unused driver Support for the ezx series of phones was removed in 2022, this driver is just dead code. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260430164326.2766500-2-arnd@kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 10 -- drivers/input/touchscreen/Makefile | 1 - drivers/input/touchscreen/pcap_ts.c | 252 ---------------------------- 3 files changed, 263 deletions(-) delete mode 100644 drivers/input/touchscreen/pcap_ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index aeaf9a9cbb41..484522d8d675 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1178,16 +1178,6 @@ config TOUCHSCREEN_TSC2007_IIO or ambient light monitoring), temperature and raw input values. -config TOUCHSCREEN_PCAP - tristate "Motorola PCAP touchscreen" - depends on EZX_PCAP - help - Say Y here if you have a Motorola EZX telephone and - want to enable support for the built-in touchscreen. - - To compile this driver as a module, choose M here: the - module will be called pcap_ts. - config TOUCHSCREEN_RM_TS tristate "Raydium I2C Touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f2b002abebe8..6d397268d2e3 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -74,7 +74,6 @@ obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o -obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c deleted file mode 100644 index 7b89eb74b9de..000000000000 --- a/drivers/input/touchscreen/pcap_ts.c +++ /dev/null @@ -1,252 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform. - * - * Copyright (C) 2006 Harald Welte - * Copyright (C) 2009 Daniel Ribeiro - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct pcap_ts { - struct pcap_chip *pcap; - struct input_dev *input; - struct delayed_work work; - u16 x, y; - u16 pressure; - u8 read_state; -}; - -#define SAMPLE_DELAY 20 /* msecs */ - -#define X_AXIS_MIN 0 -#define X_AXIS_MAX 1023 -#define Y_AXIS_MAX X_AXIS_MAX -#define Y_AXIS_MIN X_AXIS_MIN -#define PRESSURE_MAX X_AXIS_MAX -#define PRESSURE_MIN X_AXIS_MIN - -static void pcap_ts_read_xy(void *data, u16 res[2]) -{ - struct pcap_ts *pcap_ts = data; - - switch (pcap_ts->read_state) { - case PCAP_ADC_TS_M_PRESSURE: - /* pressure reading is unreliable */ - if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX) - pcap_ts->pressure = res[0]; - pcap_ts->read_state = PCAP_ADC_TS_M_XY; - schedule_delayed_work(&pcap_ts->work, 0); - break; - case PCAP_ADC_TS_M_XY: - pcap_ts->y = res[0]; - pcap_ts->x = res[1]; - if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX || - pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) { - /* pen has been released */ - input_report_abs(pcap_ts->input, ABS_PRESSURE, 0); - input_report_key(pcap_ts->input, BTN_TOUCH, 0); - - pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; - schedule_delayed_work(&pcap_ts->work, 0); - } else { - /* pen is touching the screen */ - input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); - input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); - input_report_key(pcap_ts->input, BTN_TOUCH, 1); - input_report_abs(pcap_ts->input, ABS_PRESSURE, - pcap_ts->pressure); - - /* switch back to pressure read mode */ - pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; - schedule_delayed_work(&pcap_ts->work, - msecs_to_jiffies(SAMPLE_DELAY)); - } - input_sync(pcap_ts->input); - break; - default: - dev_warn(&pcap_ts->input->dev, - "pcap_ts: Warning, unhandled read_state %d\n", - pcap_ts->read_state); - break; - } -} - -static void pcap_ts_work(struct work_struct *work) -{ - struct delayed_work *dw = to_delayed_work(work); - struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); - u8 ch[2]; - - pcap_set_ts_bits(pcap_ts->pcap, - pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); - - if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) - return; - - /* start adc conversion */ - ch[0] = PCAP_ADC_CH_TS_X1; - ch[1] = PCAP_ADC_CH_TS_Y1; - pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch, - pcap_ts_read_xy, pcap_ts); -} - -static irqreturn_t pcap_ts_event_touch(int pirq, void *data) -{ - struct pcap_ts *pcap_ts = data; - - if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) { - pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; - schedule_delayed_work(&pcap_ts->work, 0); - } - return IRQ_HANDLED; -} - -static int pcap_ts_open(struct input_dev *dev) -{ - struct pcap_ts *pcap_ts = input_get_drvdata(dev); - - pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; - schedule_delayed_work(&pcap_ts->work, 0); - - return 0; -} - -static void pcap_ts_close(struct input_dev *dev) -{ - struct pcap_ts *pcap_ts = input_get_drvdata(dev); - - cancel_delayed_work_sync(&pcap_ts->work); - - pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; - pcap_set_ts_bits(pcap_ts->pcap, - pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); -} - -static int pcap_ts_probe(struct platform_device *pdev) -{ - struct input_dev *input_dev; - struct pcap_ts *pcap_ts; - int err = -ENOMEM; - - pcap_ts = kzalloc_obj(*pcap_ts); - if (!pcap_ts) - return err; - - pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent); - platform_set_drvdata(pdev, pcap_ts); - - input_dev = input_allocate_device(); - if (!input_dev) - goto fail; - - INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work); - - pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; - pcap_set_ts_bits(pcap_ts->pcap, - pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); - - pcap_ts->input = input_dev; - input_set_drvdata(input_dev, pcap_ts); - - input_dev->name = "pcap-touchscreen"; - input_dev->phys = "pcap_ts/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0002; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &pdev->dev; - input_dev->open = pcap_ts_open; - input_dev->close = pcap_ts_close; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0); - input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, - PRESSURE_MAX, 0, 0); - - err = input_register_device(pcap_ts->input); - if (err) - goto fail_allocate; - - err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), - pcap_ts_event_touch, 0, "Touch Screen", pcap_ts); - if (err) - goto fail_register; - - return 0; - -fail_register: - input_unregister_device(input_dev); - goto fail; -fail_allocate: - input_free_device(input_dev); -fail: - kfree(pcap_ts); - - return err; -} - -static void pcap_ts_remove(struct platform_device *pdev) -{ - struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); - - free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts); - cancel_delayed_work_sync(&pcap_ts->work); - - input_unregister_device(pcap_ts->input); - - kfree(pcap_ts); -} - -#ifdef CONFIG_PM -static int pcap_ts_suspend(struct device *dev) -{ - struct pcap_ts *pcap_ts = dev_get_drvdata(dev); - - pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR); - return 0; -} - -static int pcap_ts_resume(struct device *dev) -{ - struct pcap_ts *pcap_ts = dev_get_drvdata(dev); - - pcap_set_ts_bits(pcap_ts->pcap, - pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); - return 0; -} - -static const struct dev_pm_ops pcap_ts_pm_ops = { - .suspend = pcap_ts_suspend, - .resume = pcap_ts_resume, -}; -#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops) -#else -#define PCAP_TS_PM_OPS NULL -#endif - -static struct platform_driver pcap_ts_driver = { - .probe = pcap_ts_probe, - .remove = pcap_ts_remove, - .driver = { - .name = "pcap-ts", - .pm = PCAP_TS_PM_OPS, - }, -}; -module_platform_driver(pcap_ts_driver); - -MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver"); -MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcap_ts"); From 834b33f9c95174606b25848b43b32432a3e98713 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:20 +0900 Subject: [PATCH 0313/5214] firewire: core: reduce critical section duration in pre-processing of isoc resource management in cdev It is preferable for the critical section to be as small as possible. Current implementation of iso_resource_auto_work() function uses a spinlock to control concurrent access to members of fw_card, fw_device, iso_resource_auto structures, however the locking duration could be reduced. This commit refactors to shorten that duration. Link: https://lore.kernel.org/r/20260501135823.241940-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index bcfb20b770df..887783e4bd52 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1329,32 +1329,36 @@ static void iso_resource_auto_work(struct work_struct *work) struct iso_resource_auto *r = from_work(r, work, work.work); struct client *client = r->client; unsigned long index = r->resource.handle; - int generation, channel, bandwidth, todo; + int current_generation, resource_generation, channel, bandwidth, todo; + u64 reset_jiffies; bool skip, free, success; scoped_guard(spinlock_irq, &client->lock) { - generation = client->device->generation; + reset_jiffies = client->device->card->reset_jiffies; + current_generation = client->device->generation; + resource_generation = r->params.generation; + r->params.generation = current_generation; todo = r->todo; - // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_AUTO_ALLOC && - time_is_after_jiffies64(client->device->card->reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource_auto(r, msecs_to_jiffies(333)); - skip = true; - } else { - // We could be called twice within the same generation. - skip = todo == ISO_RES_AUTO_REALLOC && - r->params.generation == generation; - } - free = todo == ISO_RES_AUTO_DEALLOC; - r->params.generation = generation; } + // Allow 1000ms grace period for other reallocations. + if (todo == ISO_RES_AUTO_ALLOC && + time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); + skip = true; + } else { + // We could be called twice within the same generation. + skip = todo == ISO_RES_AUTO_REALLOC && + resource_generation == current_generation; + } + free = todo == ISO_RES_AUTO_DEALLOC; + if (skip) goto out; bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, generation, + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, &channel, &bandwidth, todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC); From 92750bccf01f2e65976429acee06984a95803354 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:21 +0900 Subject: [PATCH 0314/5214] firewire: core: use switch statement for post-processing of isoc resource management in cdev The iso_resource_auto structure object has three states. The current implementation of state evaluation before managing the actual isochronous resources can be improved. This commit refactors the evaluation logic using a switch statement. Link: https://lore.kernel.org/r/20260501135823.241940-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 37 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 887783e4bd52..0d57b61ade12 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1331,7 +1331,7 @@ static void iso_resource_auto_work(struct work_struct *work) unsigned long index = r->resource.handle; int current_generation, resource_generation, channel, bandwidth, todo; u64 reset_jiffies; - bool skip, free, success; + bool free = false, success; scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; @@ -1341,27 +1341,29 @@ static void iso_resource_auto_work(struct work_struct *work) todo = r->todo; } - // Allow 1000ms grace period for other reallocations. - if (todo == ISO_RES_AUTO_ALLOC && - time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { - schedule_iso_resource_auto(r, msecs_to_jiffies(333)); - skip = true; - } else { + switch (todo) { + case ISO_RES_AUTO_ALLOC: + // Allow 1000ms grace period for other reallocations. + if (time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) { + schedule_iso_resource_auto(r, msecs_to_jiffies(333)); + goto out; + } + break; + case ISO_RES_AUTO_REALLOC: // We could be called twice within the same generation. - skip = todo == ISO_RES_AUTO_REALLOC && - resource_generation == current_generation; + if (resource_generation == current_generation) + goto out; + break; + case ISO_RES_AUTO_DEALLOC: + default: + break; } - free = todo == ISO_RES_AUTO_DEALLOC; - - if (skip) - goto out; bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, current_generation, - r->params.channels, &channel, &bandwidth, - todo == ISO_RES_AUTO_ALLOC || - todo == ISO_RES_AUTO_REALLOC); + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, + &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); + /* * Is this generation outdated already? As long as this resource sticks * in the xarray, it will be scheduled again for a newer generation or at @@ -1398,6 +1400,7 @@ static void iso_resource_auto_work(struct work_struct *work) e = r->e_alloc; r->e_alloc = NULL; } else { + free = true; e = r->e_dealloc; r->e_dealloc = NULL; } From afa66eeb1899087b205f49cdfb8465880d61cdd2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:22 +0900 Subject: [PATCH 0315/5214] firewire: core: refactor notification type determination after isoc resource management in cdev After managing the actual isochronous resources, there is post-processing logic to determine what type of event should be notified. However, there is room for improvement. This commit refactors the logic. Link: https://lore.kernel.org/r/20260501135823.241940-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 0d57b61ade12..4ce8754da93f 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1390,20 +1390,27 @@ static void iso_resource_auto_work(struct work_struct *work) } } - if (todo == ISO_RES_AUTO_ALLOC && channel >= 0) - r->params.channels = 1ULL << channel; - - if (todo == ISO_RES_AUTO_REALLOC && success) - goto out; - - if (todo == ISO_RES_AUTO_ALLOC) { - e = r->e_alloc; - r->e_alloc = NULL; - } else { + if (todo == ISO_RES_AUTO_DEALLOC) { free = true; e = r->e_dealloc; r->e_dealloc = NULL; + } else { + if (todo == ISO_RES_AUTO_REALLOC) { + if (success) + goto out; + + // Notify the userspace client of the failure through a deallocation event. + e = r->e_dealloc; + r->e_dealloc = NULL; + } else { + if (channel >= 0) + r->params.channels = 1ULL << channel; + + e = r->e_alloc; + r->e_alloc = NULL; + } } + e->iso_resource.handle = r->resource.handle; e->iso_resource.channel = channel; e->iso_resource.bandwidth = bandwidth; From fcabbf40fae501379b4f3a057febe92d4b0ebdd8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 1 May 2026 22:58:23 +0900 Subject: [PATCH 0316/5214] firewire: core: move allocation/reallocation paths into specific branch after isoc resource management in cdev After managing the actual isochronous resources, there is post-processing logic to determine what type of event should be notified. However, there is room for improvement. This commit refactors the logic. Link: https://lore.kernel.org/r/20260501135823.241940-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 53 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4ce8754da93f..c166e7617d2a 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1331,7 +1331,7 @@ static void iso_resource_auto_work(struct work_struct *work) unsigned long index = r->resource.handle; int current_generation, resource_generation, channel, bandwidth, todo; u64 reset_jiffies; - bool free = false, success; + bool free; scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; @@ -1364,37 +1364,31 @@ static void iso_resource_auto_work(struct work_struct *work) fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); - /* - * Is this generation outdated already? As long as this resource sticks - * in the xarray, it will be scheduled again for a newer generation or at - * shutdown. - */ - if (channel == -EAGAIN && - (todo == ISO_RES_AUTO_ALLOC || todo == ISO_RES_AUTO_REALLOC)) - goto out; - - success = channel >= 0 || bandwidth > 0; - - scoped_guard(spinlock_irq, &client->lock) { - // Transit from allocation to reallocation, except if the client - // requested deallocation in the meantime. - if (r->todo == ISO_RES_AUTO_ALLOC) - r->todo = ISO_RES_AUTO_REALLOC; - // Allocation or reallocation failure? Pull this resource out of the - // xarray and prepare for deletion, unless the client is shutting down. - if (r->todo == ISO_RES_AUTO_REALLOC && !success && - !client->in_shutdown && - xa_erase(&client->resource_xa, index)) { - client_put(client); - free = true; - } - } - if (todo == ISO_RES_AUTO_DEALLOC) { free = true; e = r->e_dealloc; r->e_dealloc = NULL; } else { + free = false; + + // Is this generation outdated already? As long as this resource sticks in the + // xarray, it will be scheduled again for a newer generation or at shutdown. + if (channel == -EAGAIN) + goto out; + + bool success = channel >= 0 || bandwidth > 0; + + if (!success) { + // Allocation or reallocation failure? Pull this resource out of the + // xarray and prepare for deletion, unless the client is shutting down. + scoped_guard(spinlock_irq, &client->lock) { + if (!client->in_shutdown && xa_erase(&client->resource_xa, index)) { + client_put(client); + free = true; + } + } + } + if (todo == ISO_RES_AUTO_REALLOC) { if (success) goto out; @@ -1403,6 +1397,11 @@ static void iso_resource_auto_work(struct work_struct *work) e = r->e_dealloc; r->e_dealloc = NULL; } else { + // Transit from allocation to reallocation, except if the client requested + // deallocation in the meantime. + scoped_guard(spinlock_irq, &client->lock) + r->todo = ISO_RES_AUTO_REALLOC; + if (channel >= 0) r->params.channels = 1ULL << channel; From 9a0afdd19a01c6edddb92eb6a464f9e99d946b90 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 30 Apr 2026 15:33:53 +0100 Subject: [PATCH 0317/5214] soundwire: stream: sdw_stream_remove_slave(): Check stream is valid In sdw_stream_remove_slave() check that stream is a valid pointer before passing it to functions that dereference it. Return 0 if the pointer is invalid. This is a convenience for callers. They can safely call this function during cleanup code without needing a pointer validity check duplicated at every call point. Signed-off-by: Richard Fitzgerald Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260430143353.2702714-1-rf@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 4ed8fb7663ad..c1ef177a0021 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -2229,11 +2229,15 @@ EXPORT_SYMBOL(sdw_stream_add_slave); * @slave: SDW Slave instance * @stream: SoundWire stream * - * This removes and frees port_rt and slave_rt from a stream + * This removes and frees port_rt and slave_rt from a stream. + * If stream is NULL or an ERR_PTR, do nothing and return 0. */ int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream) { + if (IS_ERR_OR_NULL(stream)) + return 0; + mutex_lock(&slave->bus->bus_lock); sdw_slave_port_free(slave, stream); From be6d8daaab654e9b0a8508757534d556d399d0cd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 29 Apr 2026 16:36:14 +0100 Subject: [PATCH 0318/5214] soundwire: intel_auxdevice: Add cs42l43b to wake_capable_list Add cs42l43b (both packaging options) to the wake_capable_list because it can generate jack events whilst the bus is stopped. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260429153614.741899-1-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 913e95207ee1..ee951be15465 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -51,6 +51,8 @@ struct wake_capable_part { }; static struct wake_capable_part wake_capable_list[] = { + {0x01fa, 0x2A30}, + {0x01fa, 0x2A3B}, {0x01fa, 0x4243}, {0x01fa, 0x4245}, {0x01fa, 0x4249}, From 45c7bda7b7440183850012153988e40b300f40d0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Apr 2026 14:55:12 +0800 Subject: [PATCH 0319/5214] soundwire: validate DT compatible before parsing it `sdw_of_find_slaves()` fetches raw `"compatible"` bytes with `of_get_property()` and then immediately parses them with `sscanf("sdw%01x%04hx%04hx%02hhx", ...)`. Live-tree OF properties are stored as raw bytes plus a separate length; they are not globally guaranteed to be NUL-terminated. Validate the first compatible string before parsing it. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260403183504.4-soundwire-compatible-pengpeng@iscas.ac.cn Signed-off-by: Vinod Koul --- drivers/soundwire/slave.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index ff763b692078..e0c49cbcb1ba 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -244,8 +244,8 @@ int sdw_of_find_slaves(struct sdw_bus *bus) struct sdw_slave_id id; const __be32 *addr; - compat = of_get_property(node, "compatible", NULL); - if (!compat) + ret = of_property_read_string(node, "compatible", &compat); + if (ret) continue; ret = sscanf(compat, "sdw%01x%04hx%04hx%02hhx", &sdw_version, From 0bd448b5467b7ead268bc3a946c1deabbda70fe0 Mon Sep 17 00:00:00 2001 From: Aditya Dabhade Date: Wed, 29 Apr 2026 01:04:59 +0530 Subject: [PATCH 0320/5214] phy: airoha: use C-style SPDX comment for header file checkpatch reports an improper SPDX comment style for this header: WARNING: Improper SPDX comment style for 'drivers/phy/phy-airoha-pcie-regs.h', please use '/*' instead Per Documentation/process/license-rules.rst, C header files must use the '/* SPDX-License-Identifier: ... */' form instead of the '// ....' form used in C source files. Fix it. Signed-off-by: Aditya Dabhade Link: https://patch.msgid.link/20260428193459.5865-1-aditya.dabhade066@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/phy-airoha-pcie-regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-airoha-pcie-regs.h b/drivers/phy/phy-airoha-pcie-regs.h index b938a7b468fe..58572c793722 100644 --- a/drivers/phy/phy-airoha-pcie-regs.h +++ b/drivers/phy/phy-airoha-pcie-regs.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2024 AIROHA Inc * Author: Lorenzo Bianconi From f67ab4706ab72af29c331b21f431c463b00d447a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 25 Mar 2026 11:20:39 +0000 Subject: [PATCH 0321/5214] phy: renesas: rcar-gen3-usb2: Simplify ID/VBUS detection logic Read USB2_ADPCTRL once in rcar_gen3_check_id() instead of issuing multiple MMIO reads, and derive both IDDIG and VBUSVALID from the same value. Drop the redundant !! operator, as assigning a masked u32 value to a bool already performs the required normalization. Simplify the logic by comparing the ID and VBUS status directly, which is equivalent to the previous conditional but easier to follow. Reported-by: Pavel Machek Closes: https://lore.kernel.org/all/acJVCOdlchLiSe5n@duo.ucw.cz/ Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260325112039.464992-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 79e820e2fe55..9a45d840efeb 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -314,13 +314,11 @@ static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) { if (ch->phy_data->vblvl_ctrl) { - bool vbus_valid; - bool device; + u32 val = readl(ch->base + USB2_ADPCTRL); + bool vbus_valid = val & USB2_ADPCTRL_VBUSVALID; + bool device = val & USB2_ADPCTRL_IDDIG; - device = !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); - vbus_valid = !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_VBUSVALID); - - return vbus_valid ? device : !device; + return device == vbus_valid; } if (!ch->uses_otg_pins) From 05ec592de0dd984b3c639f8e7dec9441b5bcf83b Mon Sep 17 00:00:00 2001 From: Fritz Koenig Date: Tue, 24 Mar 2026 14:00:06 -0700 Subject: [PATCH 0322/5214] Documentation: media: Fix v4l2_vp9_segmentation feature_data is defined as __s16 in the header. Signed-off-by: Fritz Koenig Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/ext-ctrls-codec-stateless.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst index 3b1e05c6eb13..d7a3b8ef03a7 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -1877,7 +1877,7 @@ params syntax' of the :ref:`vp9` specification for more details. :stub-columns: 0 :widths: 1 1 2 - * - __u8 + * - __s16 - ``feature_data[8][4]`` - Data attached to each feature. Data entry is only valid if the feature is enabled. The array shall be indexed with segment number as the first dimension From afbe4bc252d90a6f8fad869b06d5430f615f22f9 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 11:13:26 +0800 Subject: [PATCH 0323/5214] media: v4l2-ctrls: validate HEVC active reference counts HEVC slice parameters are shared stateless V4L2 controls, but the common validation path does not verify the active L0/L1 reference counts before driver-specific code consumes them. The original report came from Cedrus, but the active count bounds are not Cedrus-specific. Validate them in the common HEVC slice control path so stateless HEVC drivers get the same basic guarantees as soon as the control is queued. Do not reject ref_idx_l0/ref_idx_l1 entries here. Existing userspace may use out-of-range sentinel values such as 0xff for missing references, and some hardware can use that information for concealment. Keep this common check limited to the active reference counts. Fixes: d395a78db9eab ("media: hevc: Add decode params control") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ctrls-core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 6b375720e395..ba047d7d8601 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -971,6 +971,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; struct v4l2_area *area; @@ -1260,6 +1261,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + p_hevc_slice_params = p; + + if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; + + if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B) + break; + + if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: From 10358ea986c3c85516d1c8206486464f79d36e76 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 16:08:56 +0800 Subject: [PATCH 0324/5214] media: cedrus: skip invalid H.264 reference list entries Cedrus consumes H.264 ref_pic_list0/ref_pic_list1 entries from the stateless slice control and later uses their indices to look up decode->dpb[] in _cedrus_write_ref_list(). Rejecting such controls in cedrus_try_ctrl() would break existing userspace, since stateless H.264 reference lists may legitimately carry out-of-range indices for missing references. Instead, guard the actual DPB lookup in Cedrus and skip entries whose indices do not fit the fixed V4L2_H264_NUM_DPB_ENTRIES array. This keeps the fix local to the driver use site and avoids out-of-bounds reads from malformed or unsupported reference list entries. Fixes: e000e1fa4bdbd ("media: uapi: h264: Update reference lists") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Acked-by: Jernej Skrabec Tested-by: Chen-Yu Tsai Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index 3e2843ef6cce..fc54d993b11f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, u8 dpb_idx; dpb_idx = ref_list[i].index; + if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES) + continue; + dpb = &decode->dpb[dpb_idx]; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) From 018f2dc3a42098d3b04edd1480adc51d268adb14 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 25 Dec 2025 18:36:12 +0800 Subject: [PATCH 0325/5214] dt-bindings: media: sun4i-a10-video-engine: Add interconnect properties The Allwinner video engine sits behind the MBUS that is represented as an interconnect. Make sure that the interconnect properties are valid in the binding. Fixes: d41662e52a03 ("media: dt-bindings: media: allwinner,sun4i-a10-video-engine: Add R40 compatible") Cc: stable@vger.kernel.org Signed-off-by: Chen-Yu Tsai Acked-by: Rob Herring (Arm) Acked-by: Jernej Skrabec Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/allwinner,sun4i-a10-video-engine.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 541325f900a1..01f2afa023f0 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -63,6 +63,16 @@ properties: CMA pool to use for buffers allocation instead of the default CMA pool. + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnects: + maxItems: 1 + + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnect-names: + const: dma-mem + required: - compatible - reg From b1845a227fda37b2fe5327df3ca0015d7e290235 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Apr 2026 11:44:15 +0200 Subject: [PATCH 0326/5214] media: mtk-jpeg: cancel workqueue on release for supported platforms only Since a recent fix the mtk_jpeg_release function cancels any pending or running work present in the driver workqueue using cancel_work_sync function. Currently, only the multicore based variants use this workqueue and they have the jpeg_worker platform data field initialized with a workqueue callback function. For the others, this field value remain NULL by default. The cancel_work_sync function is unconditionally called in mtk_jpeg_release function, even for the variants that do not use the workqueue. This call generates a WARN_ON print in __flush_work because the workqueue callback function presence check fails in __flush_work function (used by cancel_work_sync). So, to avoid these warnings, call cancel_work_sync only if a workqueue callback is defined in platform data. Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work") Cc: stable@vger.kernel.org Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 8c684756d5fc..d147ec483081 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); - cancel_work_sync(&ctx->jpeg_work); + if (jpeg->variant->jpeg_worker) + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); From b20157147089a9c16a38c7810e2fe6f2df8e3277 Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Fri, 20 Mar 2026 13:05:26 -0500 Subject: [PATCH 0327/5214] media: chips-media: wave5: Move src_buf Removal to finish_encode During encoder processing, there is a case where the IRQ response could return the buffer back to userspace via v4l2_m2m_buf_done call. In this time, userspace could queue up this same buffer before start_encode removes the index from the ready queue. This would then lead to a case where the buffer in the ready queue could be a self loop due to the WRITE_ONCE(prev->next, new) call in __list_add. When __list_del is finally called, the loop is already made so nothing points back to ready queue list head and pointers are poisoned. A buffer should not be marked as DONE before the buffer is removed from m2m ready queue. Move removal entirely to finish_encode. Fixes: 9707a6254a8a6 ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Brandon Brnich Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../chips-media/wave5/wave5-vpu-enc.c | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 7613fcdbafed..c605a91718d8 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -226,13 +226,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res) } else { dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", __func__); - /* - * Remove the source buffer from the ready-queue now and finish - * it in the videobuf2 framework once the index is returned by the - * firmware in finish_encode - */ - if (src_buf) - v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); } return 0; @@ -259,27 +252,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); - /* - * The source buffer will not be found in the ready-queue as it has been - * dropped after sending of the encode firmware command, locate it in - * the videobuf2 queue directly - */ if (enc_output_info.enc_src_idx >= 0) { - struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), - enc_output_info.enc_src_idx); - if (vb->state != VB2_BUF_STATE_ACTIVE) - dev_warn(inst->dev->dev, - "%s: encoded buffer (%d) was not in ready queue %i.", - __func__, enc_output_info.enc_src_idx, vb->state); - else - src_buf = to_vb2_v4l2_buffer(vb); - - if (src_buf) { + src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx); + if (!src_buf) { + dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__); + } else { inst->timestamp = src_buf->vb2_buf.timestamp; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } else { - dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", - __func__, enc_output_info.enc_src_idx); } } From 5d801b59633f6af60bb0e18d3bbb18b7b040a6d9 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:57 +0900 Subject: [PATCH 0328/5214] media: v4l2-controls: Add control for background detection Add a generic V4L2 boolean control V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION that allows encoders to detect background regions in a frame and use fewer bits or skip mode to encode them, potentially reducing bitrate for streams with stationary scenes. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst | 6 ++++++ drivers/media/v4l2-core/v4l2-ctrls-defs.c | 2 ++ include/uapi/linux/v4l2-controls.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index c8890cb5e00a..ab865a1a6ba9 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -737,6 +737,12 @@ enum v4l2_mpeg_video_frame_skip_mode - Enable writing sample aspect ratio in the Video Usability Information. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (boolean)`` + If enabled, the encoder detect a background region in frame and + use low bits or skip mode to encode the background region. + If a lot of scenes are stationary or background, It may help to + reduce the video bitrate. Applicable to the encoder. + .. _v4l2-mpeg-video-h264-vui-sar-idc: ``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 551426c4cd01..e062f2088490 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -889,6 +889,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: return "Display Delay"; case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: return "Display Delay Enable"; case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: return "Generate Access Unit Delimiters"; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: return "Background Detection"; case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; @@ -1296,6 +1297,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: case V4L2_CID_WIDE_DYNAMIC_RANGE: case V4L2_CID_IMAGE_STABILIZATION: case V4L2_CID_RDS_RECEPTION: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 68dd0c4e47b2..affec0ab4781 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -464,6 +464,8 @@ enum v4l2_mpeg_video_intra_refresh_period_type { V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC = 1, }; +#define V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (V4L2_CID_CODEC_BASE + 238) + /* CIDs for the MPEG-2 Part 2 (H.262) codec */ #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270) enum v4l2_mpeg_video_mpeg2_level { From ffa7cb083c134042e2125cf37f488b407573607f Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:58 +0900 Subject: [PATCH 0329/5214] media: chips-media: wave5: Add support for background detection Implement V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION in the Wave5 encoder driver. When enabled, the hardware detects background regions in a frame and uses fewer bits or skip mode to encode them, reducing bitrate for streams with stationary scenes. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-hw.c | 4 +++- drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 7 +++++++ drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 687ce6ccf3ae..c516d125f553 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -49,6 +49,7 @@ #define FASTIO_ADDRESS_MASK GENMASK(15, 0) #define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24) +#define SEQ_BG_PARAM_REG_DATA 0x3800410 static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, const char *func); @@ -1838,7 +1839,8 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode); - vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, + SEQ_BG_PARAM_REG_DATA | p_param->bg_detection); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT, p_param->conf_win_bot << 16 | p_param->conf_win_top); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index c605a91718d8..e806973305ce 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -759,6 +759,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_BITRATE: inst->bit_rate = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: + inst->enc_param.bg_detection = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: inst->enc_param.avc_idr_period = ctrl->val; break; @@ -1184,6 +1187,7 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.beta_offset_div2 = input.beta_offset_div2; open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type; open_param->wave_param.intra_period = input.intra_period; + open_param->wave_param.bg_detection = input.bg_detection; if (inst->std == W_HEVC_ENC) { if (input.intra_period == 0) { open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR; @@ -1679,6 +1683,9 @@ static int wave5_vpu_open_enc(struct file *filp) v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_AU_DELIMITER, 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION, + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index c64135769869..dc31689e0d27 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,7 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 bg_detection: 1; /* enable background detection */ }; struct enc_open_param { From f8505f9d6b223a26854003bfdba1a0772457886d Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:59 +0900 Subject: [PATCH 0330/5214] media: chips-media: wave5: Support CBP profile Constrained Baseline Profile (CBP) and Baseline Profile (BP) have been treated as the same. Introduce the ability to differentiate between the two. Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Tested-by: Brandon Brnich Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-hw.c | 3 +++ drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 5 ++++- drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index c516d125f553..2392bce8d840 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -1763,6 +1763,9 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) (p_param->skip_intra_trans << 25) | (p_param->strong_intra_smooth_enable << 27) | (p_param->en_still_picture << 30); + else if (inst->std == W_AVC_ENC) + reg_val |= (p_param->constraint_set1_flag << 29); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); reg_val = (p_param->lossless_enable) | diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e806973305ce..e6ac76635178 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -918,6 +918,8 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: inst->enc_param.profile = H264_PROFILE_BP; inst->bit_depth = 8; + if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) + inst->enc_param.constraint_set1_flag = 1; break; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: inst->enc_param.profile = H264_PROFILE_MP; @@ -1194,6 +1196,7 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.intra_period = input.avc_idr_period; } } else { + open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag; open_param->wave_param.avc_idr_period = input.avc_idr_period; } open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; @@ -1666,7 +1669,7 @@ static int wave5_vpu_open_enc(struct file *filp) -6, 6, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, - 0, 1, 1, 1); + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index dc31689e0d27..7b08fef58217 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,7 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 constraint_set1_flag: 1; /* enable CBP */ u32 bg_detection: 1; /* enable background detection */ }; From 37340f0744442273facd9a785ecab10af985e4db Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:04:00 +0900 Subject: [PATCH 0331/5214] media: chips-media: wave5: Add Support for Packed YUV422 Formats Wave5 encoder is capable of reading in numerous raw pixel formats. Expose these formats and properly configure encoder if selected. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Tested-by: Brandon Brnich Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/chips-media/wave5/wave5-helper.h | 2 +- .../chips-media/wave5/wave5-vpu-enc.c | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h index d61fdbda359d..e6f241012c3b 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.h +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -11,7 +11,7 @@ #include "wave5-vpu.h" #define FMT_TYPES 2 -#define MAX_FMTS 12 +#define MAX_FMTS 16 const char *state_to_str(enum vpu_instance_state state); void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e6ac76635178..e6c94b6f2671 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -90,6 +90,22 @@ static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = { .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YVYU, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_VYUY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, } }; @@ -1140,6 +1156,22 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, else open_param->src_format = FORMAT_420; + switch (info->format) { + case V4L2_PIX_FMT_YUYV: + open_param->packed_format = PACKED_YUYV; + break; + case V4L2_PIX_FMT_YVYU: + open_param->packed_format = PACKED_YVYU; + break; + case V4L2_PIX_FMT_UYVY: + open_param->packed_format = PACKED_UYVY; + break; + case V4L2_PIX_FMT_VYUY: + open_param->packed_format = PACKED_VYUY; + break; + default: + break; + } open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE; open_param->wave_param.hvs_qp_scale = 2; open_param->wave_param.hvs_max_delta_qp = 10; From e0f5d6ae76423ec5b6f97c7c3e1f02187b988afd Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 24 Jun 2025 14:29:38 +0200 Subject: [PATCH 0332/5214] media: verisilicon: Export only needed pixels formats Some pixel formats can only be produced if the decoder outputs reference pictures directly. In some cases, such as AV1 film-grain, the use of the post-processor is strictly required. In this case, only enumerate the post-processor supported formats. The exception is when V4L2_FMTDESC_FLAG_ENUM_ALL is set, in this case, we enumerate everything regardless of the state. Signed-off-by: Benjamin Gaignard Fixes: bcd4f091cf1e ("media: verisilicon: Use V4L2_FMTDESC_FLAG_ENUM_ALL flag") Cc: stable@vger.kernel.org Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_v4l2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index fcf3bd9bcda2..83af9fa1ce94 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, unsigned int num_fmts, i, j = 0; bool skip_mode_none, enum_all_formats; u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + bool need_postproc = ctx->need_postproc; /* * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all @@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL); f->index = index; + if (enum_all_formats) + need_postproc = HANTRO_AUTO_POSTPROC; + /* * When dealing with an encoder: * - on the capture side we want to filter out all MODE_NONE formats. @@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, */ skip_mode_none = capture == ctx->is_encoder; - formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC); + formats = hantro_get_formats(ctx, &num_fmts, need_postproc); for (i = 0; i < num_fmts; i++) { bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; fmt = &formats[i]; From 166ea048d9a3ae29248ed1ce0761b975bced76e0 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:36 -0400 Subject: [PATCH 0333/5214] media: rkvdec: Introduce a global bitwriter helper The use of structures with bitfields is good when the values are somewhat aligned. More mis-alignement means that compilers need to do more gymnastics to edit the fields values. Some cases have been reported with CLang on specific architectures like armhf and hexagon, where the compiler would allocate a bigger local stack than needed or even completely freeze during compilation. Some fixes have been provided to ease the issues, but the real fix here is to use a bitwriter instead of heavily unaligned bitfields. This is a preparation commit to provide a global bitwriter interface for the whole driver. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-bitwriter.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h new file mode 100644 index 000000000000..288467941abf --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder bit writer + * + * Copyright (C) 2026 Collabora, Ltd. + * Detlev Casanova + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon + */ + +#ifndef RKVDEC_BIT_WRITER_H_ +#define RKVDEC_BIT_WRITER_H_ + +#include +#include + +struct rkvdec_bw_field { + u16 offset; + u8 len; +}; + +#define BW_FIELD(_offset, _len) ((struct rkvdec_bw_field){ _offset, _len }) + +static inline void rkvdec_set_bw_field(u32 *buf, struct rkvdec_bw_field field, u32 value) +{ + u8 bit = field.offset % 32; + u16 word = field.offset / 32; + u64 mask = GENMASK_ULL(bit + field.len - 1, bit); + u64 val = ((u64)value << bit) & mask; + + buf[word] &= ~mask; + buf[word] |= val; + if (bit + field.len > 32) { + buf[word + 1] &= ~(mask >> 32); + buf[word + 1] |= val >> 32; + } +} + +#endif /* RKVDEC_BIT_WRITER_H_ */ From adfc4fa79b62a76cd5509b0339fab971404c46d2 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:37 -0400 Subject: [PATCH 0334/5214] media: rkvdec: Use the global bitwriter instead of local one Both rkvdec-h264.c and rkvdec-hevc.c use their own bitwriter function and macros. Move to using the global one introduced before. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/rockchip/rkvdec/rkvdec-h264.c | 109 +++++------ .../platform/rockchip/rkvdec/rkvdec-hevc.c | 171 ++++++++---------- 2 files changed, 119 insertions(+), 161 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c index d3202cecb988..ffa606038192 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c @@ -16,6 +16,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-h264-common.h" +#include "rkvdec-bitwriter.h" /* Size with u32 units. */ #define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) @@ -25,56 +26,48 @@ struct rkvdec_sps_pps_packet { u32 info[8]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - -#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4) -#define PROFILE_IDC PS_FIELD(4, 8) -#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1) -#define CHROMA_FORMAT_IDC PS_FIELD(13, 2) -#define BIT_DEPTH_LUMA PS_FIELD(15, 3) -#define BIT_DEPTH_CHROMA PS_FIELD(18, 3) -#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1) -#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4) -#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5) -#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2) -#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4) -#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1) -#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9) -#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9) -#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1) -#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1) -#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1) -#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1) -#define NUM_VIEWS PS_FIELD(60, 2) -#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10) -#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1) -#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10) -#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1) -#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10) -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5) -#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1) -#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1) -#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5) -#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1) -#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2) -#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7) -#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6) -#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5) -#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1) -#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1) -#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1) -#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5) -#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1) -#define SCALING_LIST_ADDRESS PS_FIELD(184, 32) -#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 9) +#define PIC_HEIGHT_IN_MBS BW_FIELD(47, 9) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(56, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(57, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(58, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(59, 1) +#define NUM_VIEWS BW_FIELD(60, 2) +#define VIEW_ID(i) BW_FIELD(62 + ((i) * 10), 10) +#define NUM_ANCHOR_REFS_L(i) BW_FIELD(82 + ((i) * 11), 1) +#define ANCHOR_REF_L(i) BW_FIELD(83 + ((i) * 11), 10) +#define NUM_NON_ANCHOR_REFS_L(i) BW_FIELD(104 + ((i) * 11), 1) +#define NON_ANCHOR_REFS_L(i) BW_FIELD(105 + ((i) * 11), 10) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(136, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(141, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(142, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(143 + ((i) * 5), 5) +#define WEIGHTED_PRED_FLAG BW_FIELD(153, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(154, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(156, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(163, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(169, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(174, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(175, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(176, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(177, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(178, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(183, 1) +#define SCALING_LIST_ADDRESS BW_FIELD(184, 32) +#define IS_LONG_TERM(i) BW_FIELD(216 + (i), 1) /* Data structure describing auxiliary buffer format. */ struct rkvdec_h264_priv_tbl { @@ -91,20 +84,6 @@ struct rkvdec_h264_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -128,7 +107,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); WRITE_PPS(sps->profile_idc, PROFILE_IDC); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index ac8b825d080a..87abf93dfd5e 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -18,6 +18,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-hevc-common.h" +#include "rkvdec-bitwriter.h" /* Size in u8/u32 units. */ #define RKV_SCALING_LIST_SIZE 1360 @@ -34,80 +35,72 @@ struct rkvdec_rps_packet { u32 info[RKV_RPS_SIZE]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - /* SPS */ -#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4) -#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4) -#define CHROMA_FORMAT_IDC PS_FIELD(8, 2) -#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13) -#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13) -#define BIT_DEPTH_LUMA PS_FIELD(36, 4) -#define BIT_DEPTH_CHROMA PS_FIELD(40, 4) -#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5) -#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2) -#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3) -#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3) -#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3) -#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1) -#define AMP_ENABLED_FLAG PS_FIELD(66, 1) -#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1) -#define PCM_ENABLED_FLAG PS_FIELD(68, 1) -#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4) -#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4) -#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1) -#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3) -#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3) -#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7) -#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1) -#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6) -#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1) -#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1) +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 13) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(23, 13) +#define BIT_DEPTH_LUMA BW_FIELD(36, 4) +#define BIT_DEPTH_CHROMA BW_FIELD(40, 4) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(44, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(49, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(51, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(54, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(57, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(59, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(62, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(65, 1) +#define AMP_ENABLED_FLAG BW_FIELD(66, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(67, 1) +#define PCM_ENABLED_FLAG BW_FIELD(68, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(69, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(73, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(77, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(78, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(81, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(84, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(91, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(92, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(98, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(99, 1) /* PPS */ -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4) -#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1) -#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1) -#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13) -#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1) -#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1) -#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4) -#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4) -#define INIT_QP_MINUS26 PS_FIELD(163, 7) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1) -#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1) -#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1) -#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3) -#define PPS_CB_QP_OFFSET PS_FIELD(176, 5) -#define PPS_CR_QP_OFFSET PS_FIELD(181, 5) -#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1) -#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1) -#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1) -#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1) -#define TILES_ENABLED_FLAG PS_FIELD(190, 1) -#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1) -#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1) -#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1) -#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1) -#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1) -#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4) -#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4) -#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1) -#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3) -#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1) -#define NUM_TILE_COLUMNS PS_FIELD(212, 5) -#define NUM_TILE_ROWS PS_FIELD(217, 5) -#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8) -#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8) -#define SCALING_LIST_ADDRESS PS_FIELD(592, 32) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(134, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(138, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(139, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(140, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(153, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(154, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(155, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(159, 4) +#define INIT_QP_MINUS26 BW_FIELD(163, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(170, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(171, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(172, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(173, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(176, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(181, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(186, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(187, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(188, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(189, 1) +#define TILES_ENABLED_FLAG BW_FIELD(190, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(191, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(192, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(193, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(194, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(195, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(196, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(200, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(204, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(205, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(208, 1) +#define NUM_TILE_COLUMNS BW_FIELD(212, 5) +#define NUM_TILE_ROWS BW_FIELD(217, 5) +#define COLUMN_WIDTH(i) BW_FIELD(256 + ((i) * 8), 8) +#define ROW_HEIGHT(i) BW_FIELD(416 + ((i) * 8), 8) +#define SCALING_LIST_ADDRESS BW_FIELD(592, 32) /* Data structure describing auxiliary buffer format. */ struct rkvdec_hevc_priv_tbl { @@ -123,20 +116,6 @@ struct rkvdec_hevc_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -159,7 +138,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID); WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); @@ -321,17 +300,17 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx, int i, j; unsigned int lowdelay; -#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_RPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) -#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1) -#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4) -#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1) -#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4) +#define REF_PIC_LONG_TERM_L0(n) BW_FIELD((n) * 5, 1) +#define REF_PIC_IDX_L0(n) BW_FIELD(1 + ((n) * 5), 4) +#define REF_PIC_LONG_TERM_L1(n) BW_FIELD(((n) < 5 ? 75 : 132) + ((n) * 5), 1) +#define REF_PIC_IDX_L1(n) BW_FIELD(((n) < 4 ? 76 : 128) + ((n) * 5), 4) -#define LOWDELAY PS_FIELD(182, 1) -#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10) -#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9) -#define NUM_RPS_POC PS_FIELD(202, 4) +#define LOWDELAY BW_FIELD(182, 1) +#define LONG_TERM_RPS_BIT_OFFSET BW_FIELD(183, 10) +#define SHORT_TERM_RPS_BIT_OFFSET BW_FIELD(193, 9) +#define NUM_RPS_POC BW_FIELD(202, 4) for (j = 0; j < run->num_slices; j++) { uint st_bit_offset = 0; From 9b77225be5972a1e13a1b535caddba997e976035 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:38 -0400 Subject: [PATCH 0335/5214] media: rkvdec: common: Drop bitfields for the bitwriter Currently, the common code files for hevc and h264 use structs with bitfields to represent the HW RPS buffer. Because the bitfields are mostly unaligned and numerous, it brings compiler issues, especially with clang. To prevent that, switch to using the global bitwriter previously introduced instead. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-h264-common.c | 51 +--------- .../rockchip/rkvdec/rkvdec-h264-common.h | 40 ++------ .../rockchip/rkvdec/rkvdec-hevc-common.c | 93 ++++--------------- .../rockchip/rkvdec/rkvdec-hevc-common.h | 57 +++--------- 4 files changed, 44 insertions(+), 197 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c index e28f06394470..54639512e456 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c @@ -21,51 +21,6 @@ #define RKVDEC_NUM_REFLIST 3 -static void set_dpb_info(struct rkvdec_rps_entry *entries, - u8 reflist, - u8 refnum, - u8 info, - bool bottom) -{ - struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8]; - u8 idx = refnum % 8; - - switch (idx) { - case 0: - entry->dpb_info0 = info; - entry->bottom_flag0 = bottom; - break; - case 1: - entry->dpb_info1 = info; - entry->bottom_flag1 = bottom; - break; - case 2: - entry->dpb_info2 = info; - entry->bottom_flag2 = bottom; - break; - case 3: - entry->dpb_info3 = info; - entry->bottom_flag3 = bottom; - break; - case 4: - entry->dpb_info4 = info; - entry->bottom_flag4 = bottom; - break; - case 5: - entry->dpb_info5 = info; - entry->bottom_flag5 = bottom; - break; - case 6: - entry->dpb_info6 = info; - entry->bottom_flag6 = bottom; - break; - case 7: - entry->dpb_info7 = info; - entry->bottom_flag7 = bottom; - break; - } -} - void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -111,7 +66,7 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) continue; - hw_rps->frame_num[i] = builder->refs[i].frame_num; + rkvdec_set_bw_field(hw_rps->info, RPS_FRAME_NUM(i), builder->refs[i].frame_num); } for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { @@ -138,7 +93,9 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, dpb_valid = !!(run->ref_buf[ref->index]); bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; - set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_DPB_INFO(j, i), + ref->index | (dpb_valid << 4)); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_BOTTOM_FLAG(j, i), bottom); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h index 5336370507d6..f04b700b863c 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h @@ -16,6 +16,7 @@ #include #include "rkvdec.h" +#include "rkvdec-bitwriter.h" struct rkvdec_h264_scaling_list { u8 scaling_list_4x4[6][16]; @@ -38,39 +39,16 @@ struct rkvdec_h264_run { struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; }; -struct rkvdec_rps_entry { - u32 dpb_info0: 5; - u32 bottom_flag0: 1; - u32 view_index_off0: 1; - u32 dpb_info1: 5; - u32 bottom_flag1: 1; - u32 view_index_off1: 1; - u32 dpb_info2: 5; - u32 bottom_flag2: 1; - u32 view_index_off2: 1; - u32 dpb_info3: 5; - u32 bottom_flag3: 1; - u32 view_index_off3: 1; - u32 dpb_info4: 5; - u32 bottom_flag4: 1; - u32 view_index_off4: 1; - u32 dpb_info5: 5; - u32 bottom_flag5: 1; - u32 view_index_off5: 1; - u32 dpb_info6: 5; - u32 bottom_flag6: 1; - u32 view_index_off6: 1; - u32 dpb_info7: 5; - u32 bottom_flag7: 1; - u32 view_index_off7: 1; -} __packed; +#define RPS_FRAME_NUM(i) BW_FIELD((i) * 16, 16) +#define RPS_ENTRY_DPB_INFO(l, e) BW_FIELD(288 + (l) * 7 * 32 + (e) * 7, 5) //l: 0-2, e: 0-31 +#define RPS_ENTRY_BOTTOM_FLAG(l, e) BW_FIELD(293 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 +#define RPS_ENTRY_VIEW_INDEX_OFF(l, e) BW_FIELD(294 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 + +#define RKVDEC_H264_RPS_SIZE ALIGN(288 + 3 * 7 * 32, 128) struct rkvdec_rps { - u16 frame_num[16]; - u32 reserved0; - struct rkvdec_rps_entry entries[12]; - u32 reserved1[66]; -} __packed; + u32 info[RKVDEC_H264_RPS_SIZE / 8 / 4]; +}; void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run); void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index 3119f3bc9f98..f89602075121 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -74,72 +74,6 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size row_height[i] = pic_in_cts_height - sum; } -static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag) -{ - switch (poc) { - case 0: - set->delta_poc0 = value; - set->used_flag0 = flag; - break; - case 1: - set->delta_poc1 = value; - set->used_flag1 = flag; - break; - case 2: - set->delta_poc2 = value; - set->used_flag2 = flag; - break; - case 3: - set->delta_poc3 = value; - set->used_flag3 = flag; - break; - case 4: - set->delta_poc4 = value; - set->used_flag4 = flag; - break; - case 5: - set->delta_poc5 = value; - set->used_flag5 = flag; - break; - case 6: - set->delta_poc6 = value; - set->used_flag6 = flag; - break; - case 7: - set->delta_poc7 = value; - set->used_flag7 = flag; - break; - case 8: - set->delta_poc8 = value; - set->used_flag8 = flag; - break; - case 9: - set->delta_poc9 = value; - set->used_flag9 = flag; - break; - case 10: - set->delta_poc10 = value; - set->used_flag10 = flag; - break; - case 11: - set->delta_poc11 = value; - set->used_flag11 = flag; - break; - case 12: - set->delta_poc12 = value; - set->used_flag12 = flag; - break; - case 13: - set->delta_poc13 = value; - set->used_flag13 = flag; - break; - case 14: - set->delta_poc14 = value; - set->used_flag14 = flag; - break; - } -} - static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) { @@ -218,10 +152,11 @@ static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct r return; for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { - rps->refs[i].lt_ref_pic_poc_lsb = - run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps; - rps->refs[i].used_by_curr_pic_lt_flag = - !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_PIC_POC_LSB(i), + run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_USED_BY_CURR_PIC(i), + !!(run->ext_sps_lt_rps[i].flags & + V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT)); } } @@ -235,18 +170,24 @@ static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct r int j = 0; const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i]; - rps->short_term_ref_sets[i].num_negative = set->num_negative_pics; - rps->short_term_ref_sets[i].num_positive = set->num_positive_pics; + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_NEGATIVE(i), + set->num_negative_pics); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_POSITIVE(i), + set->num_positive_pics); for (; j < set->num_negative_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], j, - set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, j), + set->delta_poc_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, j), + set->used_by_curr_pic_s0[j]); } poc = j; for (j = 0; j < set->num_positive_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], poc + j, - set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, poc + j), + set->delta_poc_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, poc + j), + set->used_by_curr_pic_s1[j]); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h index 6f4faca4c091..2a9b7719ab2d 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h @@ -19,53 +19,24 @@ #include #include "rkvdec.h" +#include "rkvdec-bitwriter.h" -struct rkvdec_rps_refs { - u16 lt_ref_pic_poc_lsb; - u16 used_by_curr_pic_lt_flag : 1; - u16 reserved : 15; -} __packed; +#define RPS_LT_REF_PIC_POC_LSB(i) BW_FIELD(0 + (i) * 32, 16) // i: 0-31 +#define RPS_LT_REF_USED_BY_CURR_PIC(i) BW_FIELD(16 + (i) * 32, 1) // i: 0-31 -struct rkvdec_rps_short_term_ref_set { - u32 num_negative : 4; - u32 num_positive : 4; - u32 delta_poc0 : 16; - u32 used_flag0 : 1; - u32 delta_poc1 : 16; - u32 used_flag1 : 1; - u32 delta_poc2 : 16; - u32 used_flag2 : 1; - u32 delta_poc3 : 16; - u32 used_flag3 : 1; - u32 delta_poc4 : 16; - u32 used_flag4 : 1; - u32 delta_poc5 : 16; - u32 used_flag5 : 1; - u32 delta_poc6 : 16; - u32 used_flag6 : 1; - u32 delta_poc7 : 16; - u32 used_flag7 : 1; - u32 delta_poc8 : 16; - u32 used_flag8 : 1; - u32 delta_poc9 : 16; - u32 used_flag9 : 1; - u32 delta_poc10 : 16; - u32 used_flag10 : 1; - u32 delta_poc11 : 16; - u32 used_flag11 : 1; - u32 delta_poc12 : 16; - u32 used_flag12 : 1; - u32 delta_poc13 : 16; - u32 used_flag13 : 1; - u32 delta_poc14 : 16; - u32 used_flag14 : 1; - u32 reserved_bits : 25; - u32 reserved[3]; -} __packed; +#define RPS_ST_REF_SET_NUM_NEGATIVE(i) BW_FIELD(1024 + ((i) * 384), 4) // i: 0-63 +#define RPS_ST_REF_SET_NUM_POSITIVE(i) BW_FIELD(1028 + ((i) * 384), 4) // i: 0-63 + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_DELTA_POC(i, j) BW_FIELD(1032 + ((i) * 384) + ((j) * 17), 16) + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_USED(i, j) BW_FIELD(1048 + ((i) * 384) + ((j) * 17), 1) + +#define RKVDEC_RPS_HEVC_SIZE ALIGN(1032 + 64 * 384, 128) struct rkvdec_rps { - struct rkvdec_rps_refs refs[32]; - struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64]; + u32 info[RKVDEC_RPS_HEVC_SIZE / 8 / 4]; } __packed; struct rkvdec_hevc_run { From 4ae45bf4663ed93c61e9f716e81455122fb66ee2 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:39 -0400 Subject: [PATCH 0336/5214] media: rkvdec: vdpu383: Drop bitfields for the bitwriter The VDPU383 support for hevc and h264 use structs with bitfields to represent the SPS and PPS. Because the fields are mostly unaligned and numerous, it brings compiler issues, especially with clang. To prevent that, switch to using the global bitwriter previously introduced instead. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-vdpu383-h264.c | 337 +++++------- .../rockchip/rkvdec/rkvdec-vdpu383-hevc.c | 502 ++++++++---------- 2 files changed, 353 insertions(+), 486 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c index fb4f849d7366..5ec755733916 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c @@ -15,105 +15,64 @@ #include "rkvdec-cabac.h" #include "rkvdec-vdpu383-regs.h" #include "rkvdec-h264-common.h" +#include "rkvdec-bitwriter.h" -struct rkvdec_sps { - u16 seq_parameter_set_id: 4; - u16 profile_idc: 8; - u16 constraint_set3_flag: 1; - u16 chroma_format_idc: 2; - u16 bit_depth_luma: 3; - u16 bit_depth_chroma: 3; - u16 qpprime_y_zero_transform_bypass_flag: 1; - u16 log2_max_frame_num_minus4: 4; - u16 max_num_ref_frames: 5; - u16 pic_order_cnt_type: 2; - u16 log2_max_pic_order_cnt_lsb_minus4: 4; - u16 delta_pic_order_always_zero_flag: 1; +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 16) +#define PIC_HEIGHT_IN_MBS BW_FIELD(54, 16) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(70, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(71, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(72, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(73, 1) +#define NUM_VIEWS BW_FIELD(74, 2) +#define VIEW_ID(i) BW_FIELD(76 + ((i) * 10), 10) // i: 0-1 - u16 pic_width_in_mbs: 16; - u16 pic_height_in_mbs: 16; +#define PIC_PARAMETER_SET_ID BW_FIELD(96, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(104, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(109, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(110, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(111 + ((i) * 5), 5) // i: 0-1 +#define WEIGHTED_PRED_FLAG BW_FIELD(121, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(122, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(124, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(131, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(137, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(142, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(143, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(144, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(145, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(146, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(151, 1) +#define IS_LONG_TERM(i) BW_FIELD(152 + (i), 1) // i: 0-15 - u16 frame_mbs_only_flag: 1; - u16 mb_adaptive_frame_field_flag: 1; - u16 direct_8x8_inference_flag: 1; - u16 mvc_extension_enable: 1; - u16 num_views: 2; - u16 view_id0: 10; - u16 view_id1: 10; -} __packed; +#define PIC_FIELD_FLAG BW_FIELD(184, 1) +#define PIC_ASSOCIATED_FLAG BW_FIELD(185, 1) +#define CUR_TOP_FIELD BW_FIELD(186, 32) +#define CUR_BOT_FIELD BW_FIELD(218, 32) -struct rkvdec_pps { - u32 pic_parameter_set_id: 8; - u32 pps_seq_parameter_set_id: 5; - u32 entropy_coding_mode_flag: 1; - u32 bottom_field_pic_order_in_frame_present_flag: 1; - u32 num_ref_idx_l0_default_active_minus1: 5; - u32 num_ref_idx_l1_default_active_minus1: 5; - u32 weighted_pred_flag: 1; - u32 weighted_bipred_idc: 2; - u32 pic_init_qp_minus26: 7; - u32 pic_init_qs_minus26: 6; - u32 chroma_qp_index_offset: 5; - u32 deblocking_filter_control_present_flag: 1; - u32 constrained_intra_pred_flag: 1; - u32 redundant_pic_cnt_present: 1; - u32 transform_8x8_mode_flag: 1; - u32 second_chroma_qp_index_offset: 5; - u32 scaling_list_enable_flag: 1; - u32 is_longterm: 16; - u32 voidx: 16; +#define TOP_FIELD_ORDER_CNT(i) BW_FIELD(250 + (i) * 64, 32) // i: 0-15 +#define BOT_FIELD_ORDER_CNT(i) BW_FIELD(282 + (i) * 64, 32) // i: 0-15 - // dpb - u32 pic_field_flag: 1; - u32 pic_associated_flag: 1; - u32 cur_top_field: 32; - u32 cur_bot_field: 32; +#define REF_FIELD_FLAGS(i) BW_FIELD(1274 + (i), 1) // i: 0-15 +#define REF_TOPFIELD_USED(i) BW_FIELD(1290 + (i), 1) // i: 0-15 +#define REF_BOTFIELD_USED(i) BW_FIELD(1306 + (i), 1) // i: 0-15 +#define REF_COLMV_USE_FLAG(i) BW_FIELD(1322 + (i), 1) // i: 0-15 - u32 top_field_order_cnt0: 32; - u32 bot_field_order_cnt0: 32; - u32 top_field_order_cnt1: 32; - u32 bot_field_order_cnt1: 32; - u32 top_field_order_cnt2: 32; - u32 bot_field_order_cnt2: 32; - u32 top_field_order_cnt3: 32; - u32 bot_field_order_cnt3: 32; - u32 top_field_order_cnt4: 32; - u32 bot_field_order_cnt4: 32; - u32 top_field_order_cnt5: 32; - u32 bot_field_order_cnt5: 32; - u32 top_field_order_cnt6: 32; - u32 bot_field_order_cnt6: 32; - u32 top_field_order_cnt7: 32; - u32 bot_field_order_cnt7: 32; - u32 top_field_order_cnt8: 32; - u32 bot_field_order_cnt8: 32; - u32 top_field_order_cnt9: 32; - u32 bot_field_order_cnt9: 32; - u32 top_field_order_cnt10: 32; - u32 bot_field_order_cnt10: 32; - u32 top_field_order_cnt11: 32; - u32 bot_field_order_cnt11: 32; - u32 top_field_order_cnt12: 32; - u32 bot_field_order_cnt12: 32; - u32 top_field_order_cnt13: 32; - u32 bot_field_order_cnt13: 32; - u32 top_field_order_cnt14: 32; - u32 bot_field_order_cnt14: 32; - u32 top_field_order_cnt15: 32; - u32 bot_field_order_cnt15: 32; - - u32 ref_field_flags: 16; - u32 ref_topfield_used: 16; - u32 ref_botfield_used: 16; - u32 ref_colmv_use_flag: 16; - - u32 reserved0: 30; - u32 reserved[3]; -} __packed; +#define SPS_SIZE ALIGN(1322 + 16, 128) struct rkvdec_sps_pps { - struct rkvdec_sps sps; - struct rkvdec_pps pps; + u32 info[SPS_SIZE / 8 / 4]; } __packed; /* Data structure describing auxiliary buffer format. */ @@ -130,67 +89,6 @@ struct rkvdec_h264_ctx { struct vdpu383_regs_h26x regs; }; -static noinline_for_stack void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb) -{ - pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt; - pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt; - pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt; - pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt; - pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt; - pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt; - pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt; - pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt; - pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt; - pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt; - pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt; - pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt; - pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt; - pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt; - pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt; - pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt; - pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt; - pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt; - pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt; - pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt; - pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt; - pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt; - pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt; - pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt; - pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt; - pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt; - pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt; - pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt; - pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt; - pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt; - pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt; - pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt; -} - -static noinline_for_stack void set_dec_params(struct rkvdec_pps *pps, const struct v4l2_ctrl_h264_decode_params *dec_params) -{ - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - - for (int i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - pps->is_longterm |= (1 << i); - pps->ref_field_flags |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i; - pps->ref_colmv_use_flag |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i; - pps->ref_topfield_used |= - (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i; - pps->ref_botfield_used |= - (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i; - } - pps->pic_field_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC); - pps->pic_associated_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD); - - pps->cur_top_field = dec_params->top_field_order_cnt; - pps->cur_bot_field = dec_params->bottom_field_order_cnt; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -202,6 +100,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; struct rkvdec_sps_pps *hw_ps; u32 pic_width, pic_height; + int i; /* * HW read the SPS/PPS information from PPS packet index by PPS id. @@ -213,23 +112,25 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id; - hw_ps->sps.profile_idc = sps->profile_idc; - hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3)); - hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; - hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8; - hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8; - hw_ps->sps.qpprime_y_zero_transform_bypass_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); - hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4; - hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames; - hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type; - hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 = - sps->log2_max_pic_order_cnt_lsb_minus4; - hw_ps->sps.delta_pic_order_always_zero_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); - hw_ps->sps.mvc_extension_enable = 0; - hw_ps->sps.num_views = 0; + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PROFILE_IDC, sps->profile_idc); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINT_SET3_FLAG, + !!(sps->constraint_set_flags & (1 << 3))); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8); + rkvdec_set_bw_field(hw_ps->info, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_FRAME_NUM_MINUS4, + sps->log2_max_frame_num_minus4); + rkvdec_set_bw_field(hw_ps->info, MAX_NUM_REF_FRAMES, sps->max_num_ref_frames); + rkvdec_set_bw_field(hw_ps->info, PIC_ORDER_CNT_TYPE, sps->pic_order_cnt_type); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4, + sps->log2_max_pic_order_cnt_lsb_minus4); + rkvdec_set_bw_field(hw_ps->info, DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO)); + rkvdec_set_bw_field(hw_ps->info, MVC_EXTENSION_ENABLE, 0); + rkvdec_set_bw_field(hw_ps->info, NUM_VIEWS, 0); /* * Use the SPS values since they are already in macroblocks @@ -245,48 +146,72 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) pic_height /= 2; - hw_ps->sps.pic_width_in_mbs = pic_width; - hw_ps->sps.pic_height_in_mbs = pic_height; + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_MBS, pic_width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_MBS, pic_height); - hw_ps->sps.frame_mbs_only_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); - hw_ps->sps.mb_adaptive_frame_field_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); - hw_ps->sps.direct_8x8_inference_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); + rkvdec_set_bw_field(hw_ps->info, FRAME_MBS_ONLY_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); + rkvdec_set_bw_field(hw_ps->info, MB_ADAPTIVE_FRAME_FIELD_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)); + rkvdec_set_bw_field(hw_ps->info, DIRECT_8X8_INFERENCE_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)); /* write pps */ - hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id; - hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id; - hw_ps->pps.entropy_coding_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); - hw_ps->pps.bottom_field_pic_order_in_frame_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); - hw_ps->pps.num_ref_idx_l0_default_active_minus1 = - pps->num_ref_idx_l0_default_active_minus1; - hw_ps->pps.num_ref_idx_l1_default_active_minus1 = - pps->num_ref_idx_l1_default_active_minus1; - hw_ps->pps.weighted_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED); - hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc; - hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26; - hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26; - hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset; - hw_ps->pps.deblocking_filter_control_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); - hw_ps->pps.constrained_intra_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->pps.redundant_pic_cnt_present = - !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); - hw_ps->pps.transform_8x8_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); - hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset; - hw_ps->pps.scaling_list_enable_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PPS_SEQ_PARAMETER_SET_ID, pps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)); + rkvdec_set_bw_field(hw_ps->info, BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG, + !!(pps->flags & + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0), + pps->num_ref_idx_l0_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1), + pps->num_ref_idx_l1_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_IDC, pps->weighted_bipred_idc); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QP_MINUS26, pps->pic_init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QS_MINUS26, pps->pic_init_qs_minus26); + rkvdec_set_bw_field(hw_ps->info, CHROMA_QP_INDEX_OFFSET, pps->chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, REDUNDANT_PIC_CNT_PRESENT, + !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_8X8_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE)); + rkvdec_set_bw_field(hw_ps->info, SECOND_CHROMA_QP_INDEX_OFFSET, + pps->second_chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)); - set_field_order_cnt(&hw_ps->pps, dpb); - set_dec_params(&hw_ps->pps, dec_params); + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + rkvdec_set_bw_field(hw_ps->info, TOP_FIELD_ORDER_CNT(i), + dpb[i].top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, BOT_FIELD_ORDER_CNT(i), + dpb[i].bottom_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, IS_LONG_TERM(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)); + rkvdec_set_bw_field(hw_ps->info, REF_FIELD_FLAGS(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)); + rkvdec_set_bw_field(hw_ps->info, REF_COLMV_USE_FLAG(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)); + rkvdec_set_bw_field(hw_ps->info, REF_TOPFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)); + rkvdec_set_bw_field(hw_ps->info, REF_BOTFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)); + } + + rkvdec_set_bw_field(hw_ps->info, PIC_FIELD_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)); + rkvdec_set_bw_field(hw_ps->info, PIC_ASSOCIATED_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)); + + rkvdec_set_bw_field(hw_ps->info, CUR_TOP_FIELD, dec_params->top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, CUR_BOT_FIELD, dec_params->bottom_field_order_cnt); } static void rkvdec_write_regs(struct rkvdec_ctx *ctx) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c index 96d938ee70b0..3575338a531a 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c @@ -13,149 +13,106 @@ #include "rkvdec-rcb.h" #include "rkvdec-hevc-common.h" #include "rkvdec-vdpu383-regs.h" +#include "rkvdec-bitwriter.h" + +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 16) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(26, 16) +#define BIT_DEPTH_LUMA BW_FIELD(42, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(45, 3) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(48, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(53, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(55, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(58, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(61, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(63, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(66, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(69, 1) +#define AMP_ENABLED_FLAG BW_FIELD(70, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(71, 1) +#define PCM_ENABLED_FLAG BW_FIELD(72, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(73, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(77, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(81, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(82, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(85, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(88, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(95, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(96, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(102, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(103, 1) +#define SPS_MAX_DEC_PIC_BUFFERING_MINUS1 BW_FIELD(111, 4) +#define SEPARATE_COLOUR_PLANE_FLAG BW_FIELD(115, 1) +#define HIGH_PRECISION_OFFSETS_ENABLED_FLAG BW_FIELD(116, 1) +#define PERSISTENT_RICE_ADAPTATION_ENABLED_FLAG BW_FIELD(117, 1) + +/* PPS */ +#define PIC_PARAMETER_SET_ID BW_FIELD(118, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(124, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(128, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(129, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(130, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(143, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(144, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(145, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(149, 4) +#define INIT_QP_MINUS26 BW_FIELD(153, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(160, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(161, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(162, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(163, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(166, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(171, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(176, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(177, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(178, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(179, 1) +#define TILES_ENABLED_FLAG BW_FIELD(180, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(181, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(182, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(183, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(184, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(185, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(186, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(190, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(194, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(195, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(198, 1) + +/* pps extensions */ +#define LOG2_MAX_TRANSFORM_SKIP_BLOCK_SIZE BW_FIELD(202, 2) +#define CROSS_COMPONENT_PREDICTION_ENABLED_FLAG BW_FIELD(204, 1) +#define CHROMA_QP_OFFSET_LIST_ENABLED_FLAG BW_FIELD(205, 1) +#define LOG2_MIN_CU_CHROMA_QP_DELTA_SIZE BW_FIELD(206, 3) +#define CB_QP_OFFSET_LIST(i) BW_FIELD(209 + (i) * 5, 5) // i: 0-5 +#define CB_CR_OFFSET_LIST(i) BW_FIELD(239 + (i) * 5, 5) // i: 0-5 +#define CHROMA_QP_OFFSET_LIST_LEN_MINUS1 BW_FIELD(269, 3) + +/* mvc0 && mvc1 */ +#define MVC_FF BW_FIELD(272, 16) +#define MVC_00 BW_FIELD(288, 9) + +/* poc info */ +#define RESERVED2 BW_FIELD(297, 3) +#define CURRENT_POC BW_FIELD(300, 32) +#define REF_PIC_POC(i) BW_FIELD(332 + (i) * 32, 32) // i: 0-14 +#define RESERVED3 BW_FIELD(812, 32) +#define REF_IS_VALID(i) BW_FIELD(844 + (i), 1) // i: 0-14 +#define RESERVED4 BW_FIELD(859, 1) + +/* tile info*/ +#define NUM_TILE_COLUMNS BW_FIELD(860, 5) +#define NUM_TILE_ROWS BW_FIELD(865, 5) +#define COLUMN_WIDTH(i) BW_FIELD(870 + (i) * 12, 12) // i: 0-19 +#define ROW_HEIGHT(i) BW_FIELD(1110 + (i) * 12, 12) // i: 0-21 + +#define HEVC_SPS_SIZE ALIGN(1110 + 22 * 12, 256) struct rkvdec_hevc_sps_pps { - // SPS - u16 video_parameters_set_id : 4; - u16 seq_parameters_set_id_sps : 4; - u16 chroma_format_idc : 2; - u16 width : 16; - u16 height : 16; - u16 bit_depth_luma : 3; - u16 bit_depth_chroma : 3; - u16 max_pic_order_count_lsb : 5; - u16 diff_max_min_luma_coding_block_size : 2; - u16 min_luma_coding_block_size : 3; - u16 min_transform_block_size : 3; - u16 diff_max_min_transform_block_size : 2; - u16 max_transform_hierarchy_depth_inter : 3; - u16 max_transform_hierarchy_depth_intra : 3; - u16 scaling_list_enabled_flag : 1; - u16 amp_enabled_flag : 1; - u16 sample_adaptive_offset_enabled_flag : 1; - u16 pcm_enabled_flag : 1; - u16 pcm_sample_bit_depth_luma : 4; - u16 pcm_sample_bit_depth_chroma : 4; - u16 pcm_loop_filter_disabled_flag : 1; - u16 diff_max_min_pcm_luma_coding_block_size : 3; - u16 min_pcm_luma_coding_block_size : 3; - u16 num_short_term_ref_pic_sets : 7; - u16 long_term_ref_pics_present_flag : 1; - u16 num_long_term_ref_pics_sps : 6; - u16 sps_temporal_mvp_enabled_flag : 1; - u16 strong_intra_smoothing_enabled_flag : 1; - u16 reserved0 : 7; - u16 sps_max_dec_pic_buffering_minus1 : 4; - u16 separate_colour_plane_flag : 1; - u16 high_precision_offsets_enabled_flag : 1; - u16 persistent_rice_adaptation_enabled_flag : 1; - - // PPS - u16 picture_parameters_set_id : 6; - u16 seq_parameters_set_id_pps : 4; - u16 dependent_slice_segments_enabled_flag : 1; - u16 output_flag_present_flag : 1; - u16 num_extra_slice_header_bits : 13; - u16 sign_data_hiding_enabled_flag : 1; - u16 cabac_init_present_flag : 1; - u16 num_ref_idx_l0_default_active : 4; - u16 num_ref_idx_l1_default_active : 4; - u16 init_qp_minus26 : 7; - u16 constrained_intra_pred_flag : 1; - u16 transform_skip_enabled_flag : 1; - u16 cu_qp_delta_enabled_flag : 1; - u16 log2_min_cb_size : 3; - u16 pps_cb_qp_offset : 5; - u16 pps_cr_qp_offset : 5; - u16 pps_slice_chroma_qp_offsets_present_flag : 1; - u16 weighted_pred_flag : 1; - u16 weighted_bipred_flag : 1; - u16 transquant_bypass_enabled_flag : 1; - u16 tiles_enabled_flag : 1; - u16 entropy_coding_sync_enabled_flag : 1; - u16 pps_loop_filter_across_slices_enabled_flag : 1; - u16 loop_filter_across_tiles_enabled_flag : 1; - u16 deblocking_filter_override_enabled_flag : 1; - u16 pps_deblocking_filter_disabled_flag : 1; - u16 pps_beta_offset_div2 : 4; - u16 pps_tc_offset_div2 : 4; - u16 lists_modification_present_flag : 1; - u16 log2_parallel_merge_level : 3; - u16 slice_segment_header_extension_present_flag : 1; - u16 reserved1 : 3; - - // pps extensions - u16 log2_max_transform_skip_block_size : 2; - u16 cross_component_prediction_enabled_flag : 1; - u16 chroma_qp_offset_list_enabled_flag : 1; - u16 log2_min_cu_chroma_qp_delta_size : 3; - u16 cb_qp_offset_list0 : 5; - u16 cb_qp_offset_list1 : 5; - u16 cb_qp_offset_list2 : 5; - u16 cb_qp_offset_list3 : 5; - u16 cb_qp_offset_list4 : 5; - u16 cb_qp_offset_list5 : 5; - u16 cb_cr_offset_list0 : 5; - u16 cb_cr_offset_list1 : 5; - u16 cb_cr_offset_list2 : 5; - u16 cb_cr_offset_list3 : 5; - u16 cb_cr_offset_list4 : 5; - u16 cb_cr_offset_list5 : 5; - u16 chroma_qp_offset_list_len_minus1 : 3; - - /* mvc0 && mvc1 */ - u16 mvc_ff : 16; - u16 mvc_00 : 9; - - /* poc info */ - u16 reserved2 : 3; - u32 current_poc : 32; - u32 ref_pic_poc0 : 32; - u32 ref_pic_poc1 : 32; - u32 ref_pic_poc2 : 32; - u32 ref_pic_poc3 : 32; - u32 ref_pic_poc4 : 32; - u32 ref_pic_poc5 : 32; - u32 ref_pic_poc6 : 32; - u32 ref_pic_poc7 : 32; - u32 ref_pic_poc8 : 32; - u32 ref_pic_poc9 : 32; - u32 ref_pic_poc10 : 32; - u32 ref_pic_poc11 : 32; - u32 ref_pic_poc12 : 32; - u32 ref_pic_poc13 : 32; - u32 ref_pic_poc14 : 32; - u32 reserved3 : 32; - u32 ref_is_valid : 15; - u32 reserved4 : 1; - - /* tile info*/ - u16 num_tile_columns : 5; - u16 num_tile_rows : 5; - u32 column_width0 : 24; - u32 column_width1 : 24; - u32 column_width2 : 24; - u32 column_width3 : 24; - u32 column_width4 : 24; - u32 column_width5 : 24; - u32 column_width6 : 24; - u32 column_width7 : 24; - u32 column_width8 : 24; - u32 column_width9 : 24; - u32 row_height0 : 24; - u32 row_height1 : 24; - u32 row_height2 : 24; - u32 row_height3 : 24; - u32 row_height4 : 24; - u32 row_height5 : 24; - u32 row_height6 : 24; - u32 row_height7 : 24; - u32 row_height8 : 24; - u32 row_height9 : 24; - u32 row_height10 : 24; - u32 reserved5 : 2; - u32 padding; -} __packed; + u32 info[HEVC_SPS_SIZE / 8 / 4]; +}; struct rkvdec_hevc_priv_tbl { struct rkvdec_hevc_sps_pps param_set; @@ -171,51 +128,6 @@ struct rkvdec_hevc_ctx { struct vdpu383_regs_h26x regs; }; -static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row) -{ - hw_ps->column_width0 = column[0] | (column[1] << 12); - hw_ps->row_height0 = row[0] | (row[1] << 12); - hw_ps->column_width1 = column[2] | (column[3] << 12); - hw_ps->row_height1 = row[2] | (row[3] << 12); - hw_ps->column_width2 = column[4] | (column[5] << 12); - hw_ps->row_height2 = row[4] | (row[5] << 12); - hw_ps->column_width3 = column[6] | (column[7] << 12); - hw_ps->row_height3 = row[6] | (row[7] << 12); - hw_ps->column_width4 = column[8] | (column[9] << 12); - hw_ps->row_height4 = row[8] | (row[9] << 12); - hw_ps->column_width5 = column[10] | (column[11] << 12); - hw_ps->row_height5 = row[10] | (row[11] << 12); - hw_ps->column_width6 = column[12] | (column[13] << 12); - hw_ps->row_height6 = row[12] | (row[13] << 12); - hw_ps->column_width7 = column[14] | (column[15] << 12); - hw_ps->row_height7 = row[14] | (row[15] << 12); - hw_ps->column_width8 = column[16] | (column[17] << 12); - hw_ps->row_height8 = row[16] | (row[17] << 12); - hw_ps->column_width9 = column[18] | (column[19] << 12); - hw_ps->row_height9 = row[18] | (row[19] << 12); - - hw_ps->row_height10 = row[20] | (row[21] << 12); -} - -static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb) -{ - hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val; - hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val; - hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val; - hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val; - hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val; - hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val; - hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val; - hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val; - hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val; - hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val; - hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val; - hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val; - hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val; - hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val; - hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -245,104 +157,130 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->video_parameters_set_id = sps->video_parameter_set_id; - hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id; - hw_ps->chroma_format_idc = sps->chroma_format_idc; + rkvdec_set_bw_field(hw_ps->info, VIDEO_PARAMETER_SET_ID, sps->video_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; width = sps->pic_width_in_luma_samples; height = sps->pic_height_in_luma_samples; - hw_ps->width = width; - hw_ps->height = height; - hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8; - hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; - hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; - hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size; - hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3; - hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2; - hw_ps->diff_max_min_transform_block_size = - sps->log2_diff_max_min_luma_transform_block_size; - hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter; - hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra; - hw_ps->scaling_list_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); - hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED); - hw_ps->sample_adaptive_offset_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_LUMA_SAMPLES, width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_LUMA_SAMPLES, height); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB, + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_min_luma_coding_block_size_minus3 + 3); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_TRANSFORM_BLOCK_SIZE, + sps->log2_min_luma_transform_block_size_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE, + sps->log2_diff_max_min_luma_transform_block_size); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTER, + sps->max_transform_hierarchy_depth_inter); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA, + sps->max_transform_hierarchy_depth_intra); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, AMP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED); - hw_ps->pcm_enabled_flag = pcm_enabled; - hw_ps->pcm_sample_bit_depth_luma = - pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0; - hw_ps->pcm_sample_bit_depth_chroma = - pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0; - hw_ps->pcm_loop_filter_disabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); - hw_ps->diff_max_min_pcm_luma_coding_block_size = - sps->log2_diff_max_min_pcm_luma_coding_block_size; - hw_ps->min_pcm_luma_coding_block_size = - pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0; - hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; - hw_ps->long_term_ref_pics_present_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); - hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; - hw_ps->sps_temporal_mvp_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); - hw_ps->strong_intra_smoothing_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); - hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1; + rkvdec_set_bw_field(hw_ps->info, PCM_ENABLED_FLAG, pcm_enabled); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_LUMA, + pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_CHROMA, + pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_LOOP_FILTER_DISABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_pcm_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0); + rkvdec_set_bw_field(hw_ps->info, NUM_SHORT_TERM_REF_PIC_SETS, + sps->num_short_term_ref_pic_sets); + rkvdec_set_bw_field(hw_ps->info, LONG_TERM_REF_PICS_PRESENT_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_LONG_TERM_REF_PICS_SPS, + sps->num_long_term_ref_pics_sps); + rkvdec_set_bw_field(hw_ps->info, SPS_TEMPORAL_MVP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, STRONG_INTRA_SMOOTHING_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SPS_MAX_DEC_PIC_BUFFERING_MINUS1, + sps->sps_max_dec_pic_buffering_minus1); /* write pps */ - hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id; - hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id; - hw_ps->dependent_slice_segments_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); - hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); - hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits; - hw_ps->sign_data_hiding_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); - hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); - hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1; - hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1; - hw_ps->init_qp_minus26 = pps->init_qp_minus26; - hw_ps->constrained_intra_pred_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->transform_skip_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); - hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); - hw_ps->log2_min_cb_size = log2_min_cb_size + - sps->log2_diff_max_min_luma_coding_block_size - - pps->diff_cu_qp_delta_depth; - hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset; - hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset; - hw_ps->pps_slice_chroma_qp_offsets_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); - hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); - hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); - hw_ps->transquant_bypass_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, OUTPUT_FLAG_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_EXTRA_SLICE_HEADER_BITS, + pps->num_extra_slice_header_bits); + rkvdec_set_bw_field(hw_ps->info, SIGN_DATA_HIDING_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CABAC_INIT_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L0_DEFAULT_ACTIVE, + pps->num_ref_idx_l0_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L1_DEFAULT_ACTIVE, + pps->num_ref_idx_l1_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, INIT_QP_MINUS26, pps->init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_SKIP_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CU_QP_DELTA_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_CU_QP_DELTA_SIZE, log2_min_cb_size + + sps->log2_diff_max_min_luma_coding_block_size - + pps->diff_cu_qp_delta_depth); + rkvdec_set_bw_field(hw_ps->info, PPS_CB_QP_OFFSET, pps->pps_cb_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_CR_QP_OFFSET, pps->pps_cr_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSQUANT_BYPASS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); - hw_ps->tiles_enabled_flag = tiles_enabled; - hw_ps->entropy_coding_sync_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); - hw_ps->pps_loop_filter_across_slices_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); - hw_ps->loop_filter_across_tiles_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); - hw_ps->deblocking_filter_override_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); - hw_ps->pps_deblocking_filter_disabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); - hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2; - hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2; - hw_ps->lists_modification_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); - hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2; - hw_ps->slice_segment_header_extension_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); - hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1; - hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1; - hw_ps->mvc_ff = 0xffff; + rkvdec_set_bw_field(hw_ps->info, TILES_ENABLED_FLAG, tiles_enabled); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_SYNC_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_DEBLOCKING_FILTER_DISABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); + rkvdec_set_bw_field(hw_ps->info, PPS_BETA_OFFSET_DIV2, pps->pps_beta_offset_div2); + rkvdec_set_bw_field(hw_ps->info, PPS_TC_OFFSET_DIV2, pps->pps_tc_offset_div2); + rkvdec_set_bw_field(hw_ps->info, LISTS_MODIFICATION_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, LOG2_PARALLEL_MERGE_LEVEL, + pps->log2_parallel_merge_level_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_COLUMNS, + tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_ROWS, + tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, MVC_FF, 0xffff); // Setup tiles information memset(column_width, 0, sizeof(column_width)); @@ -367,15 +305,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, row_height[0] = (height + max_cu_width - 1) / max_cu_width; } - set_column_row(hw_ps, column_width, row_height); + for (i = 0; i < 20; i++) + rkvdec_set_bw_field(hw_ps->info, COLUMN_WIDTH(i), column_width[i]); + for (i = 0; i < 22; i++) + rkvdec_set_bw_field(hw_ps->info, ROW_HEIGHT(i), row_height[i]); // Setup POC information - hw_ps->current_poc = dec_params->pic_order_cnt_val; + rkvdec_set_bw_field(hw_ps->info, CURRENT_POC, dec_params->pic_order_cnt_val); - set_pps_ref_pic_poc(hw_ps, dec_params->dpb); for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - u32 valid = !!(dec_params->num_active_dpb_entries > i); - hw_ps->ref_is_valid |= valid << i; + rkvdec_set_bw_field(hw_ps->info, REF_IS_VALID(i), + !!(dec_params->num_active_dpb_entries > i)); + rkvdec_set_bw_field(hw_ps->info, REF_PIC_POC(i), + dec_params->dpb[i].pic_order_cnt_val); } } From 7cdbd7bb21949a8fda10c7104a2b12ee363cbf5c Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Thu, 2 Apr 2026 13:45:53 -0500 Subject: [PATCH 0337/5214] media: chips-media: wave5: Release m2m_ctx after Instance Removed from List Possible use after free if IRQ thread manages to obtain spinlock between m2m_ctx release and wave5_release function removing stream instance from list of active instances. The IRQ thread looks for the m2m_ctx which is freed so null pointer dereference occurs. Signed-off-by: Brandon Brnich Reviewed-by: Nicolas Dufresne Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 53a0ac068c2e..c3d34be833ff 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -68,7 +68,6 @@ int wave5_vpu_release_device(struct file *filp, int ret = 0; unsigned long flags; - v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); /* * To prevent Null reference exception, the existing irq handler were * separated to two modules. @@ -89,6 +88,9 @@ int wave5_vpu_release_device(struct file *filp, list_del_init(&inst->list); spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags); mutex_unlock(&inst->dev->irq_lock); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + if (inst->state != VPU_INST_STATE_NONE) { u32 fail_res; From 7d5d364f8b2dcc9b6b92456fb55632fde4a4d96f Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Thu, 2 Apr 2026 13:45:54 -0500 Subject: [PATCH 0338/5214] media: chips-media: wave5: Fix Reports from Kernel Lock Validator handle_dynamic_resolution change requires that the state_lock be acquired based on the lockdep_assert_held. However, the handle_dynamic_resolution_change call in initialize_sequence does not properly obtain the lock before calling. Since the v4l2_ctrl_find and s_ctrl can sleep, they should not be called while a lock is already held. Store off the fbc_buf_count then properly update control once lock has been freed. Signed-off-by: Brandon Brnich Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../chips-media/wave5/wave5-vpu-dec.c | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index d419076d7052..bb2ba9204a83 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -283,10 +283,23 @@ static void send_eos_event(struct vpu_instance *inst) inst->sent_eos = true; } +static void wave5_update_min_bufs_ctrl(struct vpu_instance *inst, u32 fbc_buf_count) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_ctrl *ctrl; + + if (!fbc_buf_count || fbc_buf_count == v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) + return; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, fbc_buf_count); +} + static int handle_dynamic_resolution_change(struct vpu_instance *inst) { struct v4l2_fh *fh = &inst->v4l2_fh; - struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; static const struct v4l2_event vpu_event_src_ch = { .type = V4L2_EVENT_SOURCE_CHANGE, @@ -305,14 +318,6 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst) inst->needs_reallocation = true; inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1; - if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) { - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); - if (ctrl) - v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count); - } if (p_dec_info->initial_info_obtained) { const struct vpu_format *vpu_fmt; @@ -439,19 +444,24 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END || dec_info.sequence_changed)) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (!v4l2_m2m_has_stopped(m2m_ctx)) { switch_state(inst, VPU_INST_STATE_STOP); - if (dec_info.sequence_changed) + if (dec_info.sequence_changed) { handle_dynamic_resolution_change(inst); - else + fbc_buf_count = inst->fbc_buf_count; + } else { send_eos_event(inst); + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } if (inst->sent_eos && @@ -1592,8 +1602,9 @@ static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = { static int initialize_sequence(struct vpu_instance *inst) { struct dec_initial_info initial_info; - int ret = 0; unsigned long flags; + u32 fbc_buf_count; + int ret = 0; memset(&initial_info, 0, sizeof(struct dec_initial_info)); @@ -1617,8 +1628,11 @@ static int initialize_sequence(struct vpu_instance *inst) spin_lock_irqsave(&inst->state_spinlock, flags); handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; spin_unlock_irqrestore(&inst->state_spinlock, flags); + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); + return 0; } @@ -1659,6 +1673,7 @@ static void wave5_vpu_dec_device_run(void *priv) ret = initialize_sequence(inst); if (ret) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (wave5_is_draining_or_eos(inst) && @@ -1667,14 +1682,18 @@ static void wave5_vpu_dec_device_run(void *priv) switch_state(inst, VPU_INST_STATE_STOP); - if (vb2_is_streaming(dst_vq)) + if (vb2_is_streaming(dst_vq)) { send_eos_event(inst); - else + } else { handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } else { set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); } From d99732334aaf33b9f93926b70b6a11c2cef3de39 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:02 +0300 Subject: [PATCH 0339/5214] media: cedrus: Fix missing cleanup in error path According to the documentation struct v4l2_fh has to be cleaned up with v4l2_fh_exit() before being freed. [1] Currently there is no actual bug here, when v4l2_fh_exit() isn't called. v4l2_fh_exit() in this case only destroys internal mutex. But it may change in the future, when v4l2_fh_init/v4l2_fh_exit will be enhanced. 1. https://docs.kernel.org/driver-api/media/v4l2-fh.html Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 6600245dff0e..1d2130f35fff 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -391,6 +391,7 @@ static int cedrus_open(struct file *file) err_m2m_release: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free: + v4l2_fh_exit(&ctx->fh); kfree(ctx); mutex_unlock(&dev->dev_mutex); From f0a22f1d602ed499a192284de5e811a73421f0c7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:40 +0300 Subject: [PATCH 0340/5214] media: cedrus: Fix failure to clean up hardware on probe failure If V4L2 device fails to register, then SRAM still be claimed and as a result driver will not be able to probe again. cedrus 1c0e000.video-codec: Failed to claim SRAM cedrus 1c0e000.video-codec: Failed to probe hardware cedrus 1c0e000.video-codec: probe with driver cedrus failed with error -16 cedrus_hw_remove undoes everything that was previously done by cedrus_hw_probe, such as disabling runtime power management and releasing the claimed SRAM and reserved memory region. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 1d2130f35fff..ee0e286add67 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -477,7 +477,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } vfd = &dev->vfd; @@ -538,6 +538,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + cedrus_hw_remove(dev); return ret; } From 94826832483708d4386226e6d966dd946f37a2bf Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Fri, 10 Apr 2026 08:15:42 +0200 Subject: [PATCH 0341/5214] media: verisilicon: remove hantro_run declaration The function hantro_run() is declared but never defined nor used, remove the dangling declaration. Signed-off-by: Rouven Czerwinski Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_hw.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 5f2011529f02..13e573f1f19d 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -427,7 +427,6 @@ extern const struct hantro_postproc_ops rockchip_vpu981_postproc_ops; extern const u32 hantro_vp8_dec_mc_filter[8][6]; void hantro_watchdog(struct work_struct *work); -void hantro_run(struct hantro_ctx *ctx); void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); From 968b741872914a15363af0daf24ed2e82ec355f3 Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Wed, 15 Apr 2026 18:25:21 +0900 Subject: [PATCH 0342/5214] media: v4l2-common: Add YUV24 format info The YUV24 format is missing an entry in the v4l2_format_info(). The YUV24 format is the packed YUV 4:4:4 formats with 8 bits per component. Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format") Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 554c591e1113..55bcd5975d9f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -281,6 +281,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, From d3dcde2dc1f182b93261f1a14651356cc2d12a7b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 12:00:10 +0100 Subject: [PATCH 0343/5214] media: dt-bindings: media: renesas,fcp: Document RZ/G3L FCPVD IP The FCPVD block on the RZ/G3L SoC is identical to the one found on the RZ/G2L SoC. Document RZ/G3L FCPVD IP. Signed-off-by: Biju Das Acked-by: Rob Herring (Arm) Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260330110012.131273-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index b5eff6fec8a9..86b176a634e1 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -30,6 +30,7 @@ properties: - renesas,r9a07g043u-fcpvd # RZ/G2UL - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L + - renesas,r9a08g046-fcpvd # RZ/G3L - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback @@ -77,6 +78,7 @@ allOf: - renesas,r9a07g043u-fcpvd - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd + - renesas,r9a08g046-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: From b01725760314f9bb592b15cc0c692eefdb20633d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 11:56:29 +0100 Subject: [PATCH 0344/5214] media: dt-bindings: media: renesas,vsp1: Document RZ/G3L VSPD The VSPD block on the RZ/G3L SoC is identical to the one found on the RZ/G2L SoC. Document RZ/G3L VSPD. Signed-off-by: Biju Das Acked-by: Rob Herring (Arm) Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260330105637.130189-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,vsp1.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 07a97dd87a5b..5447b9b78930 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -25,6 +25,7 @@ properties: - enum: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L + - renesas,r9a08g046-vsp2 # RZ/G3L - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback From acefeeee00c427b28f77a7217dad7216582e341f Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:37:02 +0200 Subject: [PATCH 0345/5214] media: dt-bindings: media: renesas,fcp: Document RZ/G3E SoC The FCPVD block on the RZ/G3E SoC is identical to the one found on the RZ/G2L SoC. No driver changes are required, as `renesas,fcpv` will be used as a fallback compatible string on the RZ/G3E SoC. Reviewed-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Tommaso Merciai Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/5aaf561a66c24639477a99d3861ca969a81673f0.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index 86b176a634e1..5e11ae0ee456 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -31,6 +31,7 @@ properties: - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L - renesas,r9a08g046-fcpvd # RZ/G3L + - renesas,r9a09g047-fcpvd # RZ/G3E - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback @@ -79,6 +80,7 @@ allOf: - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd - renesas,r9a08g046-fcpvd + - renesas,r9a09g047-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: From d9c8c4adf23d17549c0ec9c85b99d85a0ee6cf18 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:37:01 +0200 Subject: [PATCH 0346/5214] media: dt-bindings: media: renesas,vsp1: Document RZ/G3E The VSPD block on the RZ/G3E SoC is identical to the one found on the RZ/G2L SoC. No driver changes are required, as `renesas,r9a07g044-vsp2` will be used as a fallback compatible string on the RZ/G3E SoC. Reviewed-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Tommaso Merciai Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/c1e2eb36af01e0cddd8050ba70847c3a0821c91e.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,vsp1.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 5447b9b78930..803358780f01 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -26,6 +26,7 @@ properties: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L - renesas,r9a08g046-vsp2 # RZ/G3L + - renesas,r9a09g047-vsp2 # RZ/G3E - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback From 54f3c5643ec523a04b6ec0e7c19eb10f5ebebdd3 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 8 Apr 2026 15:45:34 +0000 Subject: [PATCH 0347/5214] fpga: region: fix use-after-free in child_regions_with_firmware() Move of_node_put(child_region) after the error print to avoid accessing freed memory when pr_err() references child_region. Fixes: 0fa20cdfcc1f ("fpga: fpga-region: device tree control for FPGA") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang [ Yilun: Fix the Fixes tag ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260408154534.404327-1-vulab@iscas.ac.cn Signed-off-by: Xu Yilun --- drivers/fpga/of-fpga-region.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index caa091224dc5..9107a5b461d3 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -168,11 +168,10 @@ static int child_regions_with_firmware(struct device_node *overlay) fpga_region_of_match); } - of_node_put(child_region); - if (ret) pr_err("firmware-name not allowed in child FPGA region: %pOF", child_region); + of_node_put(child_region); return ret; } From 49d580fc54f2d9c6cc3529361cafd44b14e87644 Mon Sep 17 00:00:00 2001 From: Phil Pemberton Date: Thu, 9 Apr 2026 13:20:15 +0100 Subject: [PATCH 0348/5214] dt-bindings: fpga: Add Technologic Systems TS-7300 FPGA Manager Add device tree binding documentation for the Altera Cyclone II FPGA found on Technologic Systems (now EmbeddedTS) TS-7300 boards, programmed via the memory-mapped interface in the CPLD. Signed-off-by: Phil Pemberton Reviewed-by: Florian Fainelli Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260409122016.3940462-2-philpem@philpem.me.uk Signed-off-by: Xu Yilun --- .../fpga/technologic,ts7300-fpga.yaml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/fpga/technologic,ts7300-fpga.yaml diff --git a/Documentation/devicetree/bindings/fpga/technologic,ts7300-fpga.yaml b/Documentation/devicetree/bindings/fpga/technologic,ts7300-fpga.yaml new file mode 100644 index 000000000000..c93e3a1a135b --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/technologic,ts7300-fpga.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/fpga/technologic,ts7300-fpga.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Technologic Systems TS-7300 FPGA Manager + +maintainers: + - Florian Fainelli + +description: + FPGA manager for the Altera Cyclone II FPGA on Technologic Systems + TS-7300 board. The FPGA is programmed via the memory-mapped interface + implemented in the CPLD. + +properties: + compatible: + const: technologic,ts7300-fpga + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + fpga-mgr@13c00000 { + compatible = "technologic,ts7300-fpga"; + reg = <0x13c00000 0x2>; + }; +... From bd3ce2c63dc8a78c6a383ed53cffe2bc6e5f3378 Mon Sep 17 00:00:00 2001 From: Phil Pemberton Date: Thu, 9 Apr 2026 13:20:16 +0100 Subject: [PATCH 0349/5214] fpga: ts73xx-fpga: add OF match table for device tree probing The ts73xx-fpga driver currently only matches by platform device name, which prevents it from being probed when the device is described in a device tree. Add an of_device_id table so the driver can match against the "technologic,ts7300-fpga" compatible string. The TS-7350 and TS-7390 use different FPGAs with a different programming interface, so while the driver is named "ts73xx-fpga", it doesn't apply to them. Signed-off-by: Phil Pemberton Reviewed-by: Florian Fainelli Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260409122016.3940462-3-philpem@philpem.me.uk Signed-off-by: Xu Yilun --- drivers/fpga/ts73xx-fpga.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c index 4e1d2a4d3df4..3460e4809f86 100644 --- a/drivers/fpga/ts73xx-fpga.c +++ b/drivers/fpga/ts73xx-fpga.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -119,9 +120,17 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(mgr); } +static const struct of_device_id ts73xx_fpga_of_match[] = { + { .compatible = "technologic,ts7300-fpga" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, ts73xx_fpga_of_match); + static struct platform_driver ts73xx_fpga_driver = { .driver = { .name = "ts73xx-fpga-mgr", + .of_match_table = ts73xx_fpga_of_match, }, .probe = ts73xx_fpga_probe, }; From 39696ecb8dfd0f18f0f651c001bd669eff91920f Mon Sep 17 00:00:00 2001 From: Ian Dannapel Date: Thu, 16 Apr 2026 16:42:34 +0200 Subject: [PATCH 0350/5214] dt-bindings: vendor-prefix: Add prefix for Efinix, Inc. Add entry for Efinix, Inc. (https://www.efinixinc.com/) Signed-off-by: Ian Dannapel Acked-by: Conor Dooley Acked-by: Alexander Dahl Link: https://lore.kernel.org/r/20260416144237.373852-2-iansdannapel@gmail.com Signed-off-by: Xu Yilun --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 28784d66ae7b..f42f2d9dad4d 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -489,6 +489,8 @@ patternProperties: description: Emtop Embedded Solutions "^eeti,.*": description: eGalax_eMPIA Technology Inc + "^efinix,.*": + description: Efinix, Inc. "^egnite,.*": description: egnite GmbH "^einfochips,.*": From 7fd0138b1a6a455cd498fe0f471fdf103dd0ca64 Mon Sep 17 00:00:00 2001 From: Ian Dannapel Date: Thu, 16 Apr 2026 16:42:35 +0200 Subject: [PATCH 0351/5214] dt-bindings: fpga: Add Efinix SPI programming bindings Add device tree bindings documentation for configuring Efinix FPGA using serial SPI passive programming mode. Signed-off-by: Ian Dannapel Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260416144237.373852-3-iansdannapel@gmail.com Signed-off-by: Xu Yilun --- .../bindings/fpga/efinix,trion-config.yaml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/fpga/efinix,trion-config.yaml diff --git a/Documentation/devicetree/bindings/fpga/efinix,trion-config.yaml b/Documentation/devicetree/bindings/fpga/efinix,trion-config.yaml new file mode 100644 index 000000000000..7c7444ff9c3a --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/efinix,trion-config.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/fpga/efinix,trion-config.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Efinix SPI FPGA Manager + +maintainers: + - Ian Dannapel + +description: | + Efinix FPGAs (Trion, Topaz, and Titanium families) support loading bitstreams + through "SPI Passive Mode". + Additional pin hogs for bus width configuration should be set + elsewhere, if necessary. + + References: + - https://www.efinixinc.com/docs/an006-configuring-trion-fpgas-v6.3.pdf + - https://www.efinixinc.com/docs/an033-configuring-titanium-fpgas-v2.8.pdf + - https://www.efinixinc.com/docs/an061-configuring-topaz-fpgas-v1.1.pdf + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - efinix,titanium-config + - efinix,topaz-config + - const: efinix,trion-config + - const: efinix,trion-config + + spi-cpha: true + + spi-cpol: true + + spi-max-frequency: + maximum: 25000000 + + reg: + maxItems: 1 + + reset-gpios: + description: + reset and re-configuration trigger pin (low active) + maxItems: 1 + + cdone-gpios: + description: + optional configuration done status pin (high active) + maxItems: 1 + +required: + - compatible + - reg + - reset-gpios + - spi-cpha + - spi-cpol + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>; + fpga-mgr@0 { + compatible = "efinix,trion-config"; + reg = <0>; + spi-max-frequency = <25000000>; + spi-cpha; + spi-cpol; + reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; + cdone-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + }; + }; + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>; + fpga-mgr@0 { + compatible = "efinix,titanium-config", "efinix,trion-config"; + reg = <0>; + spi-max-frequency = <25000000>; + spi-cpha; + spi-cpol; + reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; + cdone-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + }; + }; +... From d4d5ad6c6039a4921bd9f708bd0539258d60e55e Mon Sep 17 00:00:00 2001 From: Ian Dannapel Date: Thu, 16 Apr 2026 16:42:36 +0200 Subject: [PATCH 0352/5214] fpga-mgr: Add Efinix SPI programming driver Add a new driver for loading binary firmware to configuration RAM using "SPI passive mode" on Efinix FPGAs. Efinix passive SPI configuration requires chip select to remain asserted from reset until the complete bitstream and trailing idle clocks have been transferred, so the driver keeps CS active with cs_change and locks the SPI bus for the duration of configuration. Signed-off-by: Ian Dannapel Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260416144237.373852-4-iansdannapel@gmail.com Signed-off-by: Xu Yilun --- drivers/fpga/Kconfig | 7 + drivers/fpga/Makefile | 1 + drivers/fpga/efinix-spi.c | 260 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/fpga/efinix-spi.c diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 37b35f58f0df..748fc210c135 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -288,6 +288,13 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI FPGA manager driver support for Lattice FPGAs programming over slave SPI sysCONFIG interface. +config FPGA_MGR_EFINIX_SPI + tristate "Efinix FPGA configuration over SPI" + depends on SPI + help + FPGA manager driver support for Efinix FPGAs configuration over SPI + (passive mode only). + source "drivers/fpga/tests/Kconfig" endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index aeb89bb13517..6f5798b27e0d 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_FPGA_MGR_LATTICE_SYSCONFIG) += lattice-sysconfig.o obj-$(CONFIG_FPGA_MGR_LATTICE_SYSCONFIG_SPI) += lattice-sysconfig-spi.o obj-$(CONFIG_ALTERA_PR_IP_CORE) += altera-pr-ip-core.o obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT) += altera-pr-ip-core-plat.o +obj-$(CONFIG_FPGA_MGR_EFINIX_SPI) += efinix-spi.o # FPGA Secure Update Drivers obj-$(CONFIG_FPGA_M10_BMC_SEC_UPDATE) += intel-m10-bmc-sec-update.o diff --git a/drivers/fpga/efinix-spi.c b/drivers/fpga/efinix-spi.c new file mode 100644 index 000000000000..ed9a41232a32 --- /dev/null +++ b/drivers/fpga/efinix-spi.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FPGA Manager Driver for Efinix + * + * Copyright (C) 2025 iris-GmbH infrared & intelligent sensors + * + * Ian Dannapel + * + * Load Efinix FPGA firmware over SPI using the serial configuration interface. + * + * Note: Only passive mode (host initiates transfer) is currently supported. + */ + +#include +#include +#include +#include +#include +#include + +/* + * 13 dummy bytes generate 104 SPI clock cycles (8 bits each). + * Used to meet the requirement for >100 clock cycles idle sequence. + */ +#define EFINIX_SPI_IDLE_CYCLES_BYTES 13 + +/* + * tDMIN: Minimum time between deassertion of CRESET_N to first + * valid configuration data. (32 µs) + */ +#define EFINIX_TDMIN_US_MIN 35 +#define EFINIX_TDMIN_US_MAX 40 + +/* + * tCRESET_N: Minimum CRESET_N low pulse width required to + * trigger re-configuration. (320 ns) + */ +#define EFINIX_TCRESETN_DELAY_MIN_US 1 +#define EFINIX_TCRESETN_DELAY_MAX_US 2 + +/* + * tUSER: Minimum configuration duration after CDONE goes high + * before entering user mode. (25 µs) + */ +#define EFINIX_TUSER_US_MIN 30 +#define EFINIX_TUSER_US_MAX 35 + +struct efinix_spi_conf { + struct spi_device *spi; + struct gpio_desc *cdone; + struct gpio_desc *reset; +}; + +static void efinix_spi_reset(struct efinix_spi_conf *conf) +{ + gpiod_set_value(conf->reset, 1); + usleep_range(EFINIX_TCRESETN_DELAY_MIN_US, EFINIX_TCRESETN_DELAY_MAX_US); + gpiod_set_value(conf->reset, 0); + usleep_range(EFINIX_TDMIN_US_MIN, EFINIX_TDMIN_US_MAX); +} + +static enum fpga_mgr_states efinix_spi_state(struct fpga_manager *mgr) +{ + struct efinix_spi_conf *conf = mgr->priv; + + if (conf->cdone && gpiod_get_value(conf->cdone) == 1) + return FPGA_MGR_STATE_OPERATING; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static int efinix_spi_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct efinix_spi_conf *conf = mgr->priv; + struct spi_transfer assert_cs = { + /* Keep CS asserted across configuration. */ + .cs_change = 1, + }; + struct spi_message message; + int ret; + + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported\n"); + return -EOPNOTSUPP; + } + + /* + * Efinix passive SPI configuration requires chip select to stay + * asserted from reset until the bitstream is fully clocked in. + * Lock the SPI bus so no other device can toggle CS between the + * reset pulse and the write/complete transfers. + */ + spi_bus_lock(conf->spi->controller); + spi_message_init_with_transfers(&message, &assert_cs, 1); + ret = spi_sync_locked(conf->spi, &message); + if (ret) { + spi_bus_unlock(conf->spi->controller); + return ret; + } + + /* Reset with CS asserted */ + efinix_spi_reset(conf); + + return 0; +} + +static int efinix_spi_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct spi_transfer write_xfer = { + .tx_buf = buf, + .len = count, + .cs_change = 1, + }; + struct efinix_spi_conf *conf = mgr->priv; + struct spi_message message; + int ret; + + spi_message_init_with_transfers(&message, &write_xfer, 1); + ret = spi_sync_locked(conf->spi, &message); + if (ret) { + dev_err(&mgr->dev, "SPI error in firmware write: %d\n", ret); + spi_bus_unlock(conf->spi->controller); + } + + return ret; +} + +static int efinix_spi_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + unsigned long timeout = + jiffies + usecs_to_jiffies(info->config_complete_timeout_us); + struct spi_transfer clk_cycles = { + .len = EFINIX_SPI_IDLE_CYCLES_BYTES, + /* Release CS after the trailing idle clocks are sent. */ + .cs_change = 0, + }; + struct efinix_spi_conf *conf = mgr->priv; + struct spi_message message; + int done, ret; + bool expired = false; + u8 *dummy_buf; + + dummy_buf = kzalloc(EFINIX_SPI_IDLE_CYCLES_BYTES, GFP_KERNEL); + if (!dummy_buf) { + ret = -ENOMEM; + goto unlock_spi; + } + + /* + * Keep the bus locked while sending the trailing idle clocks, then + * let this final transfer deassert CS to terminate configuration. + */ + clk_cycles.tx_buf = dummy_buf; + spi_message_init_with_transfers(&message, &clk_cycles, 1); + ret = spi_sync_locked(conf->spi, &message); + if (ret) { + dev_err(&mgr->dev, "SPI error in write complete: %d\n", ret); + goto free_buf; + } + + if (conf->cdone) { + while (!expired) { + done = gpiod_get_value(conf->cdone); + if (done < 0) { + ret = done; + goto free_buf; + } + if (done) + break; + + usleep_range(10, 20); + expired = time_after(jiffies, timeout); + } + + if (expired) { + dev_err(&mgr->dev, "Timeout waiting for CDONE\n"); + ret = -ETIMEDOUT; + goto free_buf; + } + } + + usleep_range(EFINIX_TUSER_US_MIN, EFINIX_TUSER_US_MAX); + +free_buf: + kfree(dummy_buf); +unlock_spi: + spi_bus_unlock(conf->spi->controller); + + return ret; +} + +static const struct fpga_manager_ops efinix_spi_ops = { + .state = efinix_spi_state, + .write_init = efinix_spi_write_init, + .write = efinix_spi_write, + .write_complete = efinix_spi_write_complete, +}; + +static int efinix_spi_probe(struct spi_device *spi) +{ + struct efinix_spi_conf *conf; + struct fpga_manager *mgr; + + if (!(spi->mode & SPI_CPHA) || !(spi->mode & SPI_CPOL)) + return dev_err_probe(&spi->dev, -EINVAL, + "Unsupported SPI mode, set CPHA and CPOL\n"); + + conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) + return -ENOMEM; + + conf->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(conf->reset)) + return dev_err_probe(&spi->dev, PTR_ERR(conf->reset), + "Failed to get RESET gpio\n"); + + conf->cdone = devm_gpiod_get_optional(&spi->dev, "cdone", GPIOD_IN); + if (IS_ERR(conf->cdone)) + return dev_err_probe(&spi->dev, PTR_ERR(conf->cdone), + "Failed to get CDONE gpio\n"); + + conf->spi = spi; + + mgr = devm_fpga_mgr_register(&spi->dev, + "Efinix FPGA Manager", + &efinix_spi_ops, conf); + + return PTR_ERR_OR_ZERO(mgr); +} + +static const struct of_device_id efinix_spi_of_match[] = { + { .compatible = "efinix,trion-config", }, + {} +}; +MODULE_DEVICE_TABLE(of, efinix_spi_of_match); + +static const struct spi_device_id efinix_ids[] = { + { "trion-config", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(spi, efinix_ids); + +static struct spi_driver efinix_spi_driver = { + .driver = { + .name = "efinix-spi", + .of_match_table = efinix_spi_of_match, + }, + .probe = efinix_spi_probe, + .id_table = efinix_ids, +}; + +module_spi_driver(efinix_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Dannapel "); +MODULE_DESCRIPTION("Efinix FPGA SPI Programming Driver"); From f9c9ec2c319f843b70ecdf939d48b52d189bc081 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Fri, 1 May 2026 11:02:03 +0000 Subject: [PATCH 0353/5214] gfs2: fix use-after-free in gfs2_qd_dealloc gfs2_qd_dealloc(), called as an RCU callback from gfs2_qd_dispose(), accesses the superblock object sdp through qd->qd_sbd after freeing qd. It does so to decrement sd_quota_count and wake up sd_kill_wait. However, by the time the RCU callback runs, gfs2_put_super() may have already freed sdp via free_sbd(). This can happen when gfs2_quota_cleanup() is called during unmount: it disposes of quota objects via call_rcu() and then waits on sd_kill_wait with a 60-second timeout. If the timeout expires, or if gfs2_gl_hash_clear() triggers additional qd_put() calls that schedule more RCU callbacks after the wait completes, gfs2_put_super() will proceed to free the superblock while RCU callbacks referencing it are still pending. Add an rcu_barrier() before free_sbd() in gfs2_put_super() to ensure all pending RCU callbacks (including gfs2_qd_dealloc) have completed before the superblock is freed. Fixes: a475c5dd16e5 ("gfs2: Free quota data objects synchronously") Reported-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=42a37bf8045847d8f9d2 Tested-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Andreas Gruenbacher --- fs/gfs2/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index a2ea121331f1..4d854556b529 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -643,6 +643,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); + rcu_barrier(); free_sbd(sdp); } From 3afccee4a3b88b4ba370916bbb8ab7027221b810 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:46 +0200 Subject: [PATCH 0354/5214] clk: renesas: rzv2h: Add PLLDSI clk mux support Add PLLDSI clk mux support to select PLLDSI clock from different clock sources. Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register them in the clock driver. Extend the determine_rate callback to calculate and propagate PLL parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected, using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate(). The CLK_SMUX2_DSI{0,1}_CLK clock multiplexers select between two paths with different duty cycles: - CDIV7_DSIx_CLK (LVDS path, parent index 0): asymmetric H/L=4/3 duty (4/7) - CSDIV_DSIx (DSI/RGB path, parent index 1): symmetric 50% duty (1/2) Implement rzv2h_cpg_plldsi_smux_{get,set}_duty_cycle clock operations to allow the DRM driver to query and configure the appropriate clock path based on the required output duty cycle. Signed-off-by: Tommaso Merciai Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/24d3853ca2522df21e6a071a23e23ba4ca4b7276.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzv2h-cpg.c | 181 ++++++++++++++++++++++++++++++++ drivers/clk/renesas/rzv2h-cpg.h | 8 ++ 2 files changed, 189 insertions(+) diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index f6c47fb89bca..e271c04cee34 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -76,6 +76,11 @@ /* On RZ/G3E SoC we have two DSI PLLs */ #define MAX_CPG_DSI_PLL 2 +#define CPG_PLLDSI_SMUX_LVDS_DUTY_NUM 4 +#define CPG_PLLDSI_SMUX_LVDS_DUTY_DEN 7 +#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM 1 +#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN 2 + /** * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters * @@ -418,6 +423,20 @@ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); +/** + * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock + * + * @priv: CPG private data + * @mux: mux clk + */ +struct rzv2h_plldsi_mux_clk { + struct rzv2h_cpg_priv *priv; + struct clk_mux mux; +}; + +#define to_plldsi_clk_mux(_mux) \ + container_of(_mux, struct rzv2h_plldsi_mux_clk, mux) + static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -649,6 +668,165 @@ static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate, return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true); } +static u8 rzv2h_cpg_plldsi_smux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int rzv2h_cpg_plldsi_smux_set_parent(struct clk_hw *hw, u8 index) +{ + return clk_mux_ops.set_parent(hw, index); +} + +static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv *priv, + struct pll_clk *pll_clk, + struct clk_rate_request *req) +{ + struct rzv2h_pll_div_pars *dsi_params; + struct rzv2h_pll_dsi_info *dsi_info; + u8 lvds_table[] = { 7 }; + u64 rate_millihz; + + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; + dsi_params = &dsi_info->pll_dsi_parameters; + + rate_millihz = mul_u32_u32(req->rate, MILLI); + if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, + lvds_table, ARRAY_SIZE(lvds_table), rate_millihz)) { + dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", + req->rate); + return -EINVAL; + } + + req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); + req->best_parent_rate = req->rate; + dsi_info->req_pll_dsi_rate = req->best_parent_rate * dsi_params->div.divider_value; + + return 0; +} + +static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_mux *mux = to_clk_mux(hw); + struct rzv2h_plldsi_mux_clk *dsi_mux = to_plldsi_clk_mux(mux); + struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); + struct rzv2h_cpg_priv *priv = dsi_mux->priv; + + /* + * For LVDS output (parent index 0), calculate PLL parameters with + * fixed divider value of 7. For DSI/RGB output (parent index 1) skip + * PLL calculation here as it's handled by determine_rate of the + * divider (up one level). + */ + if (!clk_mux_ops.get_parent(hw)) + return rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, req); + + req->best_parent_rate = req->rate; + return 0; +} + +static int rzv2h_cpg_plldsi_smux_get_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + u8 parent = clk_mux_ops.get_parent(hw); + + /* + * CDIV7_DSIx_CLK - LVDS path (div7) - duty 4/7. + * CSDIV_DSIx - DSI/RGB path (csdiv) - duty 1/2. + */ + if (parent == 0) { + duty->num = CPG_PLLDSI_SMUX_LVDS_DUTY_NUM; + duty->den = CPG_PLLDSI_SMUX_LVDS_DUTY_DEN; + } else { + duty->num = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM; + duty->den = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN; + } + + return 0; +} + +static int rzv2h_cpg_plldsi_smux_set_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + struct clk_hw *parent_hw; + u8 parent_idx; + + /* + * Select parent based on requested duty cycle: + * - If duty > 50% (num/den > 1/2), select LVDS path (parent 0) + * - Otherwise, select DSI/RGB path (parent 1) + */ + if (duty->num * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN > + duty->den * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM) + parent_idx = 0; + else + parent_idx = 1; + + if (parent_idx >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + parent_hw = clk_hw_get_parent_by_index(hw, parent_idx); + if (!parent_hw) + return -EINVAL; + + return clk_hw_set_parent(hw, parent_hw); +} + +static const struct clk_ops rzv2h_cpg_plldsi_smux_ops = { + .determine_rate = rzv2h_cpg_plldsi_smux_determine_rate, + .get_parent = rzv2h_cpg_plldsi_smux_get_parent, + .set_parent = rzv2h_cpg_plldsi_smux_set_parent, + .get_duty_cycle = rzv2h_cpg_plldsi_smux_get_duty_cycle, + .set_duty_cycle = rzv2h_cpg_plldsi_smux_set_duty_cycle, +}; + +static struct clk * __init +rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core, + struct rzv2h_cpg_priv *priv) +{ + struct rzv2h_plldsi_mux_clk *clk_hw_data; + struct clk_init_data init; + struct clk_hw *clk_hw; + struct smuxed smux; + int ret; + + smux = core->cfg.smux; + + if (smux.shift + smux.width > 16) { + dev_err(priv->dev, "mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + + clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); + if (!clk_hw_data) + return ERR_PTR(-ENOMEM); + + clk_hw_data->priv = priv; + + init.name = core->name; + init.ops = &rzv2h_cpg_plldsi_smux_ops; + init.flags = core->flag; + init.parent_names = core->parent_names; + init.num_parents = core->num_parents; + + clk_hw_data->mux.reg = priv->base + smux.offset; + + clk_hw_data->mux.shift = smux.shift; + clk_hw_data->mux.mask = clk_div_mask(smux.width); + clk_hw_data->mux.flags = core->mux_flags; + clk_hw_data->mux.lock = &priv->rmw_lock; + + clk_hw = &clk_hw_data->mux.hw; + clk_hw->init = &init; + + ret = devm_clk_hw_register(priv->dev, clk_hw); + if (ret) + return ERR_PTR(ret); + + return clk_hw->clk; +} + static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); @@ -1085,6 +1263,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_PLLDSI_DIV: clk = rzv2h_cpg_plldsi_div_clk_register(core, priv); break; + case CLK_TYPE_PLLDSI_SMUX: + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv); + break; default: goto fail; } diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index dc957bdaf5e9..74a3824d605e 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -203,6 +203,7 @@ enum clk_types { CLK_TYPE_SMUX, /* Static Mux */ CLK_TYPE_PLLDSI, /* PLLDSI */ CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */ }; #define DEF_TYPE(_name, _id, _type...) \ @@ -241,6 +242,13 @@ enum clk_types { .dtable = _dtable, \ .parent = _parent, \ .flag = CLK_SET_RATE_PARENT) +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \ + .cfg.smux = _smux_packed, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .flag = CLK_SET_RATE_PARENT, \ + .mux_flags = CLK_MUX_HIWORD_MASK) /** * struct rzv2h_mod_clk - Module Clocks definitions From d394d8342d61a22dca00ce87045926d8e046f974 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:47 +0200 Subject: [PATCH 0355/5214] clk: renesas: r9a09g047: Add CLK_PLLETH_LPCLK support Add CLK_PLLETH_LPCLK clock support. Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/dcb0cab96e2ff3e23eafac061b2952c74622d1f8.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index e59ac4a05a7f..41464a6e9b5d 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -64,6 +64,8 @@ enum clk_ids { CLK_PLLDTY_DIV16, CLK_PLLVDO_CRU0, CLK_PLLVDO_GPU, + CLK_PLLETH_DIV4_LPCLK, + CLK_PLLETH_LPCLK, /* Module Clocks */ MOD_CLK_BASE, @@ -107,6 +109,14 @@ static const struct clk_div_table dtable_2_100[] = { {0, 0}, }; +static const struct clk_div_table dtable_16_128[] = { + {0, 16}, + {1, 32}, + {2, 64}, + {3, 128}, + {0, 0}, +}; + /* Mux clock tables */ static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; @@ -171,6 +181,10 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_DDIV(".pllvdo_cru0", CLK_PLLVDO_CRU0, CLK_PLLVDO, CDDIV3_DIVCTL3, dtable_2_4), DEF_DDIV(".pllvdo_gpu", CLK_PLLVDO_GPU, CLK_PLLVDO, CDDIV3_DIVCTL1, dtable_2_64), + DEF_FIXED(".plleth_div4_lpclk", CLK_PLLETH_DIV4_LPCLK, CLK_PLLETH, 1, 4), + DEF_CSDIV(".plleth_lpclk", CLK_PLLETH_LPCLK, CLK_PLLETH_DIV4_LPCLK, + CSDIV0_DIVCTL2, dtable_16_128), + /* Core Clocks */ DEF_FIXED("sys_0_pclk", R9A09G047_SYS_0_PCLK, CLK_QEXTAL, 1, 1), DEF_DDIV("ca55_0_coreclk0", R9A09G047_CA55_0_CORECLK0, CLK_PLLCA55, From 6913a6159688edee07185a6ed0f1c4ad57c881e5 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:48 +0200 Subject: [PATCH 0356/5214] clk: renesas: r9a09g047: Add CLK_PLLDSI{0,1} clocks Add support for the PLLDSI{0,1} clocks in the r9a09g047 CPG driver. Introduce CLK_PLLDSI{0,1} also, introduce the rzg3e_cpg_pll_dsi{0,1}_limits structures to describe the frequency constraints specific to the RZ/G3E SoC. On Renesas RZ/G3E: - PLLDSI0 maximum output frequency: 1218 MHz - PLLDSI1 maximum output frequency: 609 MHz These limits are enforced through the newly added RZG3E_CPG_PLL_DSI{0,1}_LIMITS(). Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/d26ec5349b0eb7ddb7d244fc53d1111a8530328f.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 11 +++++++++++ include/linux/clk/renesas.h | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 41464a6e9b5d..87d5924f7e79 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -30,6 +31,8 @@ enum clk_ids { CLK_PLLCA55, CLK_PLLVDO, CLK_PLLETH, + CLK_PLLDSI0, + CLK_PLLDSI1, /* Internal Core Clocks */ CLK_PLLCM33_DIV3, @@ -117,6 +120,12 @@ static const struct clk_div_table dtable_16_128[] = { {0, 0}, }; +RZG3E_CPG_PLL_DSI0_LIMITS(rzg3e_cpg_pll_dsi0_limits); +RZG3E_CPG_PLL_DSI1_LIMITS(rzg3e_cpg_pll_dsi1_limits); + +#define PLLDSI0 PLL_PACK_LIMITS(0xc0, 1, 0, &rzg3e_cpg_pll_dsi0_limits) +#define PLLDSI1 PLL_PACK_LIMITS(0x160, 1, 1, &rzg3e_cpg_pll_dsi1_limits) + /* Mux clock tables */ static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; @@ -138,6 +147,8 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_PLL(".pllca55", CLK_PLLCA55, CLK_QEXTAL, PLLCA55), DEF_FIXED(".plleth", CLK_PLLETH, CLK_QEXTAL, 125, 3), DEF_FIXED(".pllvdo", CLK_PLLVDO, CLK_QEXTAL, 105, 2), + DEF_PLLDSI(".plldsi0", CLK_PLLDSI0, CLK_QEXTAL, PLLDSI0), + DEF_PLLDSI(".plldsi1", CLK_PLLDSI1, CLK_QEXTAL, PLLDSI1), /* Internal Core Clocks */ DEF_FIXED(".pllcm33_div3", CLK_PLLCM33_DIV3, CLK_PLLCM33, 1, 3), diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index c360df9fa735..0949400f44de 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -164,6 +164,26 @@ struct rzv2h_pll_div_pars { .k = { .min = -32768, .max = 32767 }, \ } \ +#define RZG3E_CPG_PLL_DSI0_LIMITS(name) \ + static const struct rzv2h_pll_limits (name) = { \ + .fout = { .min = 25 * MEGA, .max = 1218 * MEGA }, \ + .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ + .m = { .min = 64, .max = 533 }, \ + .p = { .min = 1, .max = 4 }, \ + .s = { .min = 0, .max = 6 }, \ + .k = { .min = -32768, .max = 32767 }, \ + } \ + +#define RZG3E_CPG_PLL_DSI1_LIMITS(name) \ + static const struct rzv2h_pll_limits (name) = { \ + .fout = { .min = 25 * MEGA, .max = 609 * MEGA }, \ + .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ + .m = { .min = 64, .max = 533 }, \ + .p = { .min = 1, .max = 4 }, \ + .s = { .min = 0, .max = 6 }, \ + .k = { .min = -32768, .max = 32767 }, \ + } \ + #ifdef CONFIG_CLK_RZV2H bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz); From 0e1597c688880c2b916401c88afcd476a4e912a2 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:49 +0200 Subject: [PATCH 0357/5214] clk: renesas: r9a09g047: Add CLK_PLLDSI{0,1}_DIV7 clocks Add the CLK_PLLDSI0_DIV7 and CLK_PLLDSI1_DIV7 fixed-factor clocks to the r9a09g047 SoC clock driver. These clocks are required to enable LVDS0 and LVDS1 output support. Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/e50c1721e1dc160e8b4518e8c5172f10cba4b58b.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 87d5924f7e79..fd9b49c39dac 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -69,6 +69,8 @@ enum clk_ids { CLK_PLLVDO_GPU, CLK_PLLETH_DIV4_LPCLK, CLK_PLLETH_LPCLK, + CLK_PLLDSI0_DIV7, + CLK_PLLDSI1_DIV7, /* Module Clocks */ MOD_CLK_BASE, @@ -196,6 +198,9 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_CSDIV(".plleth_lpclk", CLK_PLLETH_LPCLK, CLK_PLLETH_DIV4_LPCLK, CSDIV0_DIVCTL2, dtable_16_128), + DEF_FIXED(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 1, 7), + DEF_FIXED(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 1, 7), + /* Core Clocks */ DEF_FIXED("sys_0_pclk", R9A09G047_SYS_0_PCLK, CLK_QEXTAL, 1, 1), DEF_DDIV("ca55_0_coreclk0", R9A09G047_CA55_0_CORECLK0, CLK_PLLCA55, From 7a9e1c485ce8040a6fe1ac8712e57860fe486cb3 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:50 +0200 Subject: [PATCH 0358/5214] clk: renesas: r9a09g047: Add CLK_PLLDSI{0,1}_CSDIV clocks Add the CLK_PLLDSI0_CSDIV and CLK_PLLDSI1_CSDIV fixed-factor clocks to the r9a09g047 SoC clock driver. These clocks are required to enable DSI and RGB output support. Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/4d5b4ddad89770447b3818381d5353f5065b72b5.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 18 ++++++++++++++++++ drivers/clk/renesas/rzv2h-cpg.h | 1 + 2 files changed, 19 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index fd9b49c39dac..82aae32d50e1 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -71,6 +71,8 @@ enum clk_ids { CLK_PLLETH_LPCLK, CLK_PLLDSI0_DIV7, CLK_PLLDSI1_DIV7, + CLK_PLLDSI0_CSDIV, + CLK_PLLDSI1_CSDIV, /* Module Clocks */ MOD_CLK_BASE, @@ -98,6 +100,18 @@ static const struct clk_div_table dtable_2_16[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_16_plldsi[] = { + {0, 2}, + {1, 4}, + {2, 6}, + {3, 8}, + {4, 10}, + {5, 12}, + {6, 14}, + {7, 16}, + {0, 0}, +}; + static const struct clk_div_table dtable_2_64[] = { {0, 2}, {1, 4}, @@ -198,6 +212,10 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_CSDIV(".plleth_lpclk", CLK_PLLETH_LPCLK, CLK_PLLETH_DIV4_LPCLK, CSDIV0_DIVCTL2, dtable_16_128), + DEF_PLLDSI_DIV(".plldsi0_csdiv", CLK_PLLDSI0_CSDIV, CLK_PLLDSI0, + CSDIV1_DIVCTL2, dtable_2_16_plldsi), + DEF_PLLDSI_DIV(".plldsi1_csdiv", CLK_PLLDSI1_CSDIV, CLK_PLLDSI1, + CSDIV1_DIVCTL3, dtable_2_16_plldsi), DEF_FIXED(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 1, 7), DEF_FIXED(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 1, 7), diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index 74a3824d605e..33bc3c27291c 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -148,6 +148,7 @@ struct fixed_mod_conf { #define CSDIV0_DIVCTL2 DDIV_PACK(CPG_CSDIV0, 8, 2, CSDIV_NO_MON) #define CSDIV0_DIVCTL3 DDIV_PACK_NO_RMW(CPG_CSDIV0, 12, 2, CSDIV_NO_MON) #define CSDIV1_DIVCTL2 DDIV_PACK(CPG_CSDIV1, 8, 4, CSDIV_NO_MON) +#define CSDIV1_DIVCTL3 DDIV_PACK(CPG_CSDIV1, 12, 4, CSDIV_NO_MON) #define SSEL0_SELCTL2 SMUX_PACK(CPG_SSEL0, 8, 1) #define SSEL0_SELCTL3 SMUX_PACK(CPG_SSEL0, 12, 1) From 8a6ccb522858e196e0e89a602f4dd4624b7f6e7a Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:51 +0200 Subject: [PATCH 0359/5214] clk: renesas: r9a09g047: Add support for SMUX2_DSI{0,1}_CLK Add support for the SMUX2_DSI0_CLK and SMUX2_DSI1_CLK clock muxes present on the r9a09g047 SoC. These muxes select between CDIV7_DSI{0,1}_CLK and CSDIV_2to16_PLLDSI{0,1} using the CPG_SSEL3 register (SELCTL0 and SELCTL1 bits). According to the hardware manual, when LVDS0 or LVDS1 outputs are used, SELCTL0 or SELCTL1 must be set accordingly. Signed-off-by: Tommaso Merciai Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/9595f56ce8ab120477bfc11eaafb0f2b655d049a.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 8 ++++++++ drivers/clk/renesas/rzv2h-cpg.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 82aae32d50e1..de0b9c071e0e 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -60,6 +60,8 @@ enum clk_ids { CLK_PLLETH_DIV_125_FIX, CLK_CSDIV_PLLETH_GBE0, CLK_CSDIV_PLLETH_GBE1, + CLK_SMUX2_DSI0_CLK, + CLK_SMUX2_DSI1_CLK, CLK_SMUX2_GBE0_TXCLK, CLK_SMUX2_GBE0_RXCLK, CLK_SMUX2_GBE1_TXCLK, @@ -143,6 +145,8 @@ RZG3E_CPG_PLL_DSI1_LIMITS(rzg3e_cpg_pll_dsi1_limits); #define PLLDSI1 PLL_PACK_LIMITS(0x160, 1, 1, &rzg3e_cpg_pll_dsi1_limits) /* Mux clock tables */ +static const char * const smux2_dsi0_clk[] = { ".plldsi0_div7", ".plldsi0_csdiv" }; +static const char * const smux2_dsi1_clk[] = { ".plldsi1_div7", ".plldsi1_csdiv" }; static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; static const char * const smux2_gbe1_rxclk[] = { ".plleth_gbe1", "et1_rxclk" }; @@ -218,6 +222,10 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { CSDIV1_DIVCTL3, dtable_2_16_plldsi), DEF_FIXED(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 1, 7), DEF_FIXED(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 1, 7), + DEF_PLLDSI_SMUX(".smux2_dsi0_clk", CLK_SMUX2_DSI0_CLK, + SSEL3_SELCTL0, smux2_dsi0_clk), + DEF_PLLDSI_SMUX(".smux2_dsi1_clk", CLK_SMUX2_DSI1_CLK, + SSEL3_SELCTL1, smux2_dsi1_clk), /* Core Clocks */ DEF_FIXED("sys_0_pclk", R9A09G047_SYS_0_PCLK, CLK_QEXTAL, 1, 1), diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index 33bc3c27291c..dec0f7b621d6 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -121,6 +121,7 @@ struct fixed_mod_conf { #define CPG_SSEL0 (0x300) #define CPG_SSEL1 (0x304) +#define CPG_SSEL3 (0x30C) #define CPG_CDDIV0 (0x400) #define CPG_CDDIV1 (0x404) #define CPG_CDDIV2 (0x408) @@ -156,6 +157,8 @@ struct fixed_mod_conf { #define SSEL1_SELCTL1 SMUX_PACK(CPG_SSEL1, 4, 1) #define SSEL1_SELCTL2 SMUX_PACK(CPG_SSEL1, 8, 1) #define SSEL1_SELCTL3 SMUX_PACK(CPG_SSEL1, 12, 1) +#define SSEL3_SELCTL0 SMUX_PACK(CPG_SSEL3, 0, 1) +#define SSEL3_SELCTL1 SMUX_PACK(CPG_SSEL3, 4, 1) #define BUS_MSTOP_IDX_MASK GENMASK(31, 16) #define BUS_MSTOP_BITS_MASK GENMASK(15, 0) From fe9a5541f096da191a153e92e04a1887307e4186 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:52 +0200 Subject: [PATCH 0360/5214] clk: renesas: r9a09g047: Add support for DSI clocks and resets Add definitions for DSI clocks and resets on the R9A09G047 cpg driver to enable proper initialization and control of the DSI hardware. Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/21ac6da825e8fad0b0a9d37d6daa955b0d23ce07.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index de0b9c071e0e..9e7bb65acea6 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -508,6 +508,16 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(9, BIT(4))), DEF_MOD("cru_0_pclk", CLK_PLLDTY_DIV16, 13, 4, 6, 20, BUS_MSTOP(9, BIT(4))), + DEF_MOD("dsi_0_pclk", CLK_PLLDTY_DIV16, 14, 8, 7, 8, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_aclk", CLK_PLLDTY_ACPU_DIV2, 14, 9, 7, 9, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_vclk1", CLK_SMUX2_DSI0_CLK, 14, 10, 7, 10, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_lpclk", CLK_PLLETH_LPCLK, 14, 11, 7, 11, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_pllref_clk", CLK_QEXTAL, 14, 12, 7, 12, + BUS_MSTOP(9, BIT(15) | BIT(14))), DEF_MOD("ge3d_clk", CLK_PLLVDO_GPU, 15, 0, 7, 16, BUS_MSTOP(3, BIT(4))), DEF_MOD("ge3d_axi_clk", CLK_PLLDTY_ACPU_DIV2, 15, 1, 7, 17, @@ -516,6 +526,8 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(3, BIT(4))), DEF_MOD("tsu_1_pclk", CLK_QEXTAL, 16, 10, 8, 10, BUS_MSTOP(2, BIT(15))), + DEF_MOD("dsi_0_vclk2", CLK_SMUX2_DSI1_CLK, 25, 0, 10, 21, + BUS_MSTOP(9, BIT(15) | BIT(14))), }; static const struct rzv2h_reset r9a09g047_resets[] __initconst = { @@ -591,6 +603,8 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(12, 5, 5, 22), /* CRU_0_PRESETN */ DEF_RST(12, 6, 5, 23), /* CRU_0_ARESETN */ DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ + DEF_RST(13, 7, 6, 8), /* DSI_0_PRESETN */ + DEF_RST(13, 8, 6, 9), /* DSI_0_ARESETN */ DEF_RST(13, 13, 6, 14), /* GE3D_RESETN */ DEF_RST(13, 14, 6, 15), /* GE3D_AXI_RESETN */ DEF_RST(13, 15, 6, 16), /* GE3D_ACE_RESETN */ From 272a6e2ad164094045af520299b5df3ce1763061 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:36:53 +0200 Subject: [PATCH 0361/5214] clk: renesas: r9a09g047: Add support for LCDC{0,1} clocks and resets Add LCDC{0,1} clocks and resets entries to the r9a09g047 CPG driver. Reviewed-by: Geert Uytterhoeven Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/c1b5afcef8068d4d074aff97e30b4d64b7c38eaf.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a09g047-cpg.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 9e7bb65acea6..94158b6834e6 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -518,6 +518,12 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(9, BIT(15) | BIT(14))), DEF_MOD("dsi_0_pllref_clk", CLK_QEXTAL, 14, 12, 7, 12, BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("lcdc_0_clk_a", CLK_PLLDTY_ACPU_DIV2, 14, 13, 7, 13, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), + DEF_MOD("lcdc_0_clk_p", CLK_PLLDTY_DIV16, 14, 14, 7, 14, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), + DEF_MOD("lcdc_0_clk_d", CLK_SMUX2_DSI0_CLK, 14, 15, 7, 15, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), DEF_MOD("ge3d_clk", CLK_PLLVDO_GPU, 15, 0, 7, 16, BUS_MSTOP(3, BIT(4))), DEF_MOD("ge3d_axi_clk", CLK_PLLDTY_ACPU_DIV2, 15, 1, 7, 17, @@ -528,6 +534,12 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(2, BIT(15))), DEF_MOD("dsi_0_vclk2", CLK_SMUX2_DSI1_CLK, 25, 0, 10, 21, BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("lcdc_1_clk_a", CLK_PLLDTY_ACPU_DIV2, 26, 8, 10, 30, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), + DEF_MOD("lcdc_1_clk_p", CLK_PLLDTY_DIV16, 26, 9, 10, 31, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), + DEF_MOD("lcdc_1_clk_d", CLK_SMUX2_DSI1_CLK, 26, 10, 11, 0, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), }; static const struct rzv2h_reset r9a09g047_resets[] __initconst = { @@ -605,10 +617,12 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ DEF_RST(13, 7, 6, 8), /* DSI_0_PRESETN */ DEF_RST(13, 8, 6, 9), /* DSI_0_ARESETN */ + DEF_RST(13, 12, 6, 13), /* LCDC_0_RESET_N */ DEF_RST(13, 13, 6, 14), /* GE3D_RESETN */ DEF_RST(13, 14, 6, 15), /* GE3D_AXI_RESETN */ DEF_RST(13, 15, 6, 16), /* GE3D_ACE_RESETN */ DEF_RST(15, 8, 7, 9), /* TSU_1_PRESETN */ + DEF_RST(17, 14, 8, 15), /* LCDC_1_RESET_N */ }; const struct rzv2h_cpg_info r9a09g047_cpg_info __initconst = { From 6eb7c193e751f057b2d75af9b174cfe5dd060696 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:43 +0200 Subject: [PATCH 0362/5214] mtd: spinand: Use secondary ops for continuous reads In case a chip supports continuous reads, but uses a slightly different cache operation for these, it may provide a secondary operation template which will be used only during continuous cache read operations. From a vendor driver point of view, enabling this feature implies providing a new set of templates for these continuous read operations. The core will automatically pick the fastest variant, depending on the hardware capabilities. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 61 ++++++++++++++++++++++++++++++++++++- include/linux/mtd/spinand.h | 12 ++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 786e3fb4874d..99a2494554ef 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -503,6 +503,11 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, rdesc = spinand->dirmaps[req->pos.plane].rdesc; + if (spinand->op_templates->cont_read_cache && req->continuous) + rdesc->info.op_tmpl = &rdesc->info.secondary_op_tmpl; + else + rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl; + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && req->mode != MTD_OPS_RAW) rdesc->info.op_tmpl->data.ecc = true; @@ -1235,6 +1240,7 @@ static struct spi_mem_dirmap_desc *spinand_create_rdesc( * its spi controller, use regular reading */ spinand->cont_read_possible = false; + memset(&info->secondary_op_tmpl, 0, sizeof(info->secondary_op_tmpl)); info->length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); @@ -1251,11 +1257,24 @@ static int spinand_create_dirmap(struct spinand_device *spinand, struct nand_device *nand = spinand_to_nand(spinand); struct spi_mem_dirmap_info info = { 0 }; struct spi_mem_dirmap_desc *desc; - bool enable_ecc = false; + bool enable_ecc = false, secondary_op = false; if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED) enable_ecc = true; + if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache) + secondary_op = true; + + /* + * Continuous read implies that only the main data is retrieved, backed + * by an on-die ECC engine. It is not possible to use a pipelind ECC + * engine with continuous read. + */ + if (enable_ecc && secondary_op) { + secondary_op = false; + spinand->cont_read_possible = false; + } + /* The plane number is passed in MSB just above the column address */ info.offset = plane << fls(nand->memorg.pagesize); @@ -1273,6 +1292,10 @@ static int spinand_create_dirmap(struct spinand_device *spinand, /* Read descriptor */ info.primary_op_tmpl = *spinand->op_templates->read_cache; info.primary_op_tmpl.data.ecc = enable_ecc; + if (secondary_op) { + info.secondary_op_tmpl = *spinand->op_templates->cont_read_cache; + info.secondary_op_tmpl.data.ecc = enable_ecc; + } desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -1622,6 +1645,33 @@ int spinand_match_and_init(struct spinand_device *spinand, if (ret) return ret; + if (info->op_variants.cont_read_cache) { + op = spinand_select_op_variant(spinand, SSDR, + info->op_variants.cont_read_cache); + if (op) { + const struct spi_mem_op *read_op; + + read_op = spinand->ssdr_op_templates.read_cache; + + /* + * Sometimes the fastest continuous read variant may not + * be supported. In this case, prefer to use the fastest + * read from cache variant and disable continuous reads. + */ + if (read_op->cmd.buswidth > op->cmd.buswidth || + (read_op->cmd.dtr && !op->cmd.dtr) || + read_op->addr.buswidth > op->addr.buswidth || + (read_op->addr.dtr && !op->addr.dtr) || + read_op->data.buswidth > op->data.buswidth || + (read_op->data.dtr && !op->data.dtr)) + spinand->cont_read_possible = false; + else + spinand->ssdr_op_templates.cont_read_cache = op; + } else { + spinand->cont_read_possible = false; + } + } + /* I/O variants selection with octo-spi DDR commands (optional) */ ret = spinand_init_odtr_instruction_set(spinand); @@ -1644,6 +1694,15 @@ int spinand_match_and_init(struct spinand_device *spinand, info->op_variants.update_cache); spinand->odtr_op_templates.update_cache = op; + if (info->op_variants.cont_read_cache) { + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.cont_read_cache); + if (op) + spinand->odtr_op_templates.cont_read_cache = op; + else + spinand->cont_read_possible = false; + } + return 0; } diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index f53f5f0499b4..44f4347104d6 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -583,6 +583,7 @@ enum spinand_bus_interface { * @op_variants.read_cache: variants of the read-cache operation * @op_variants.write_cache: variants of the write-cache operation * @op_variants.update_cache: variants of the update-cache operation + * @op_variants.cont_read_cache: variants of the continuous read-cache operation * @vendor_ops: vendor specific operations * @select_target: function used to select a target/die. Required only for * multi-die chips @@ -607,6 +608,7 @@ struct spinand_info { const struct spinand_op_variants *read_cache; const struct spinand_op_variants *write_cache; const struct spinand_op_variants *update_cache; + const struct spinand_op_variants *cont_read_cache; } op_variants; const struct spinand_op_variants *vendor_ops; int (*select_target)(struct spinand_device *spinand, @@ -636,6 +638,14 @@ struct spinand_info { .update_cache = __update, \ } +#define SPINAND_INFO_OP_VARIANTS_WITH_CONT(__read, __write, __update, __cont_read) \ + { \ + .read_cache = __read, \ + .write_cache = __write, \ + .update_cache = __update, \ + .cont_read_cache = __cont_read, \ + } + #define SPINAND_INFO_VENDOR_OPS(__ops) \ .vendor_ops = __ops @@ -707,6 +717,7 @@ struct spinand_dirmap { * @read_cache: read cache op template * @write_cache: write cache op template * @update_cache: update cache op template + * @cont_read_cache: continuous read cache op template (optional) */ struct spinand_mem_ops { struct spi_mem_op reset; @@ -721,6 +732,7 @@ struct spinand_mem_ops { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; + const struct spi_mem_op *cont_read_cache; }; /** From e1c172bbe1853e69e5fcf4692ea3c8b6fe9176a1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:48 +0200 Subject: [PATCH 0363/5214] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW As for the W35NxxJW family, add support for W25N{01,02}JW continuous read support. Similar operations require to be done, such as setting a specific bit in a configuration register, and providing a set of read variants without the address cycles. As read from cache variants are badly supported by SPI memory controllers, we create a new set of read from cache templates with a fake address cycle and just enough dummy cycles. There are two unsupported configurations (which would require 4.5 dummy bytes), so we just do not provide them. The same extra value in the ECC is possible as with the W35NxxJW family, so we reference the same helper to retrieve the ECC status. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/winbond.c | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 7cc0f0091430..f538cf2228f8 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -511,28 +511,6 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), - SPINAND_INFO("W35N02JW", /* 1.8V */ - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22), - NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1), - NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, - &write_cache_octal_variants, - &update_cache_octal_variants), - SPINAND_ODTR_PACKED_PAGE_READ, - SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), - SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), - SPINAND_INFO("W35N04JW", /* 1.8V */ - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), - NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), - NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, - &write_cache_octal_variants, - &update_cache_octal_variants), - SPINAND_ODTR_PACKED_PAGE_READ, - SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), - SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), /* 2G-bit densities */ SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), @@ -573,6 +551,17 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + SPINAND_INFO("W35N02JW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22), + NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants), + SPINAND_ODTR_PACKED_PAGE_READ, + SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), + SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), /* 4G-bit densities */ SPINAND_INFO("W25N04KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23), @@ -592,6 +581,17 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + SPINAND_INFO("W35N04JW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), + NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants), + SPINAND_ODTR_PACKED_PAGE_READ, + SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), + SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), }; static int winbond_spinand_init(struct spinand_device *spinand) From 5e85bff62bccf74bbce17d29c9818f83d8652b18 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 28 Nov 2025 18:53:46 +0100 Subject: [PATCH 0364/5214] mtd: spinand: winbond: Add support for continuous reads on W35NxxJW W35N{01,02,04}JW support being read continuously under certain circumstances. A bit must be set in their configuration register, and a specific read from cache operation, a bit shorter than usual because it no longer requires the address cycles, must be used for the occasion. Setting the "enable" bit is already supported by the core, aside from the subtlety of making sure the HFREQ bit is also set in octal DTR mode above 90MHz. However, handling two different read from cache templates involves creating a list of read from cache variants adapted the continuous reads, ie. without address cycles. Unfortunately, these operations, despite being very close to their original read from cache cousins, are often unsupported by smart SPI controller drivers because reading from cache historically allowed changing the offset at which the host would start by providing a 2-byte column address. In order to prevent issues with this, it has been decided to implement these variants with a single "ignored" address byte (respectively two in the octal DTR case), further reducing the amount of dummy cycles needed before the first bit of data. Enabling continuous reads has a side effect: the ECC status register now may also return the value b11, which means that more than 1 uncorrectable error happened during the read. This non standard behaviour requires to re-implement, almost identically the "get ECC" helper from the core, with just an extra case for this value (it is prefixed "w25w35nxxjw" because all these chips have the same behaviour). Speed gain is substantial, see below. The flash_speed -C benchmark has been run on a TI AM62A7 LP SK with CPU power management disabled, mounted with a W35N01JW chip. 1S-8S-8S: 1 page read speed is 15058 KiB/s 2 page read speed is 15058 KiB/s 3 page read speed is 16800 KiB/s 4 page read speed is 17066 KiB/s 5 page read speed is 18461 KiB/s 6 page read speed is 18461 KiB/s 7 page read speed is 19384 KiB/s 8 page read speed is 19692 KiB/s 9 page read speed is 19384 KiB/s 10 page read speed is 20000 KiB/s 11 page read speed is 20000 KiB/s 12 page read speed is 20000 KiB/s 13 page read speed is 20800 KiB/s 14 page read speed is 20363 KiB/s 15 page read speed is 20000 KiB/s 16 page read speed is 19692 KiB/s 32 page read speed is 19692 KiB/s 64 page read speed is 19692 KiB/s 8D-8D-8D: 1 page read speed is 23272 KiB/s 2 page read speed is 23272 KiB/s 3 page read speed is 28000 KiB/s 4 page read speed is 32000 KiB/s 5 page read speed is 34285 KiB/s 6 page read speed is 34285 KiB/s 7 page read speed is 36000 KiB/s 8 page read speed is 36571 KiB/s 9 page read speed is 36000 KiB/s 10 page read speed is 34285 KiB/s 11 page read speed is 36666 KiB/s 12 page read speed is 40000 KiB/s 13 page read speed is 41600 KiB/s 14 page read speed is 37333 KiB/s 15 page read speed is 40000 KiB/s 16 page read speed is 36571 KiB/s 32 page read speed is 42666 KiB/s 64 page read speed is 42666 KiB/s Signed-off-by: Miquel Raynal --- Not all configurations have been tested/validated yet. --- drivers/mtd/nand/spi/winbond.c | 126 +++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index f538cf2228f8..82383c8839f0 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -15,9 +15,11 @@ #define SPINAND_MFR_WINBOND 0xEF +#define WINBOND_CFG_HFREQ BIT(0) #define WINBOND_CFG_BUF_READ BIT(3) #define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4) +#define W25W35NXXJW_STATUS_ECC_MULT_UNCOR (3 << 4) #define W25N0XJW_SR4 0xD0 #define W25N0XJW_SR4_HS BIT(2) @@ -29,6 +31,49 @@ #define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7 #define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01 +/* + * Winbond chips ignore the address bytes during continuous reads, and + * because the dummy cycles are enough they indicate dropping the + * address cycles from the continuous read from cache variants. This is + * very poorly supported by SPI controller drivers which are "wired" to + * always at least provide the column. Keep using address cycles, but + * reduce the number of dummy cycles accordingly. + */ +#define WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \ + SPI_MEM_OP_ADDR(1, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \ + SPI_MEM_OP_ADDR(1, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 8), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1), \ + SPI_MEM_DTR_OP_ADDR(1, 0, 1), \ + SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1), \ + SPI_MEM_OP_ADDR(1, 0, 8), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 8), \ + SPI_MEM_OP_DATA_IN(len, buf, 8), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \ + SPI_MEM_DTR_OP_ADDR(2, 0, 8), \ + SPI_MEM_DTR_OP_DUMMY(ndummy - 2, 8), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \ + SPI_MEM_OP_MAX_FREQ(freq)) + /* * "X2" in the core is equivalent to "dual output" in the datasheets, * "X4" in the core is equivalent to "quad output" in the datasheets. @@ -49,6 +94,19 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants, SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); +static SPINAND_OP_VARIANTS(cont_read_cache_octal_variants, + WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(24, NULL, 0, 120 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(16, NULL, 0, 86 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(3, NULL, 0, 120 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(2, NULL, 0, 105 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(20, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(16, NULL, 0, 162 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(12, NULL, 0, 124 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(8, NULL, 0, 86 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(2, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(1, NULL, 0, 133 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(1, NULL, 0, 0)); + static SPINAND_OP_VARIANTS(write_cache_octal_variants, SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0), SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0), @@ -326,6 +384,26 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } +static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_HAS_BITFLIPS: + return 1; + + case STATUS_ECC_UNCOR_ERROR: + case W25W35NXXJW_STATUS_ECC_MULT_UNCOR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + static int w25n0xjw_hs_cfg(struct spinand_device *spinand, enum spinand_bus_interface iface) { @@ -451,6 +529,18 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand, return 0; } +static int w35n0xjw_set_cont_read(struct spinand_device *spinand, bool enable) +{ + const struct spi_mem_op *cont_op = spinand->op_templates->cont_read_cache; + u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ; + + if (cont_op && enable && spinand_op_is_odtr(cont_op) && + cont_op->max_freq >= 90 * HZ_PER_MHZ) + mask |= WINBOND_CFG_HFREQ; + + return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | WINBOND_CFG_HFREQ, mask); +} + static const struct spinand_info winbond_spinand_table[] = { /* 512M-bit densities */ SPINAND_INFO("W25N512GW", /* 1.8V */ @@ -504,13 +594,15 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21), NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, - &write_cache_octal_variants, - &update_cache_octal_variants), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants, + &cont_read_cache_octal_variants), 0, SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), - SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), + SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg), + SPINAND_CONT_READ(w35n0xjw_set_cont_read)), /* 2G-bit densities */ SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), @@ -555,13 +647,15 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22), NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, - &write_cache_octal_variants, - &update_cache_octal_variants), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants, + &cont_read_cache_octal_variants), SPINAND_ODTR_PACKED_PAGE_READ, SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), - SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), + SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg), + SPINAND_CONT_READ(w35n0xjw_set_cont_read)), /* 4G-bit densities */ SPINAND_INFO("W25N04KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23), @@ -585,13 +679,15 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, - &write_cache_octal_variants, - &update_cache_octal_variants), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants, + &cont_read_cache_octal_variants), SPINAND_ODTR_PACKED_PAGE_READ, SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), - SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), + SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg), + SPINAND_CONT_READ(w35n0xjw_set_cont_read)), }; static int winbond_spinand_init(struct spinand_device *spinand) From 240e65cb4402d62238667d71c0f9298607200b8b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Mar 2026 15:01:40 +0100 Subject: [PATCH 0365/5214] mtd: spinand: winbond: Create a helper to write the HS bit Updating the HS bit is not complex but implies reading, setting/clearing a bit and writing. Clean a bit this section by moving this logic in its own helper. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/winbond.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 82383c8839f0..97758f495c73 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -404,13 +404,28 @@ static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 status) return -EINVAL; } +static int w25n0xjw_set_sr4_hs(struct spinand_device *spinand, bool enable) +{ + int ret; + u8 sr4; + + ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4); + if (ret) + return ret; + + if (enable) + sr4 |= W25N0XJW_SR4_HS; + else + sr4 &= ~W25N0XJW_SR4_HS; + + return spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4); +} + static int w25n0xjw_hs_cfg(struct spinand_device *spinand, enum spinand_bus_interface iface) { const struct spi_mem_op *op; bool hs; - u8 sr4; - int ret; if (iface != SSDR) return -EOPNOTSUPP; @@ -429,20 +444,7 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand, else hs = true; - ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4); - if (ret) - return ret; - - if (hs) - sr4 |= W25N0XJW_SR4_HS; - else - sr4 &= ~W25N0XJW_SR4_HS; - - ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4); - if (ret) - return ret; - - return 0; + return w25n0xjw_set_sr4_hs(spinand, hs); } static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val) From ca842a62fe993019255b380ab53bed64ef31fe6f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 26 Mar 2026 17:02:29 +0100 Subject: [PATCH 0366/5214] mtd: spinand: winbond: Create a helper to detect the need for the HS bit The logic is not complex but might be reused to cleanup a bit the section by moving it to a dedicated helper. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/winbond.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 97758f495c73..a73a35adbf08 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -421,30 +421,33 @@ static int w25n0xjw_set_sr4_hs(struct spinand_device *spinand, bool enable) return spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4); } +/* + * SDR dual and quad I/O operations over 104MHz require the HS bit to + * enable a few more dummy cycles. + */ +static bool w25n0xjw_op_needs_hs(const struct spi_mem_op *op) +{ + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1) + return false; + else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ) + return false; + + return true; +} + static int w25n0xjw_hs_cfg(struct spinand_device *spinand, enum spinand_bus_interface iface) { const struct spi_mem_op *op; - bool hs; if (iface != SSDR) return -EOPNOTSUPP; - /* - * SDR dual and quad I/O operations over 104MHz require the HS bit to - * enable a few more dummy cycles. - */ op = spinand->op_templates->read_cache; - if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) - hs = false; - else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1) - hs = false; - else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ) - hs = false; - else - hs = true; - return w25n0xjw_set_sr4_hs(spinand, hs); + return w25n0xjw_set_sr4_hs(spinand, w25n0xjw_op_needs_hs(op)); } static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val) From e1ff8802ac57a917bca50dcabaf5ea78d8cd407f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Mar 2026 15:37:36 +0100 Subject: [PATCH 0367/5214] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW As for the W35NxxJW family, add support for W25N{01,02}JW continuous read support. Similar operations require to be done, such as setting a specific bit in a configuration register, and providing a set of read variants without the address cycles. As read from cache variants are badly supported by SPI memory controllers, we create a new set of read from cache templates with a fake address cycle and just enough dummy cycles. There are two unsupported configurations (which would require 4.5 dummy bytes), so we just do not provide them. The same extra value in the ECC is possible as with the W35NxxJW family, so we reference the same helper to retrieve the ECC status. Signed-off-by: Miquel Raynal --- All variants have been validated on a Nuvoton MA35D1 platform. --- drivers/mtd/nand/spi/winbond.c | 108 ++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index a73a35adbf08..9b78c1e6cbc9 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -46,6 +46,62 @@ SPI_MEM_OP_DATA_IN(len, buf, 1), \ SPI_MEM_OP_MAX_FREQ(freq)) +#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_1D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x0d, 1), \ + SPI_MEM_DTR_OP_ADDR(2, 0, 1), \ + SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 1), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(1, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(1, 0, 2), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbd, 1), \ + SPI_MEM_DTR_OP_ADDR(1, 0, 2), \ + SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 2), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 2), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(1, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6d, 1), \ + SPI_MEM_DTR_OP_ADDR(1, 0, 1), \ + SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(1, 0, 4), \ + SPI_MEM_OP_DUMMY(ndummy - 1, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xed, 1), \ + SPI_MEM_DTR_OP_ADDR(1, 0, 4), \ + SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 4), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \ + SPI_MEM_OP_MAX_FREQ(freq)) + #define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \ SPI_MEM_OP_ADDR(1, 0, 1), \ @@ -133,6 +189,20 @@ static SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_variants, SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 54 * HZ_PER_MHZ)); +static SPINAND_OP_VARIANTS(cont_read_cache_dual_quad_dtr_variants, + WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(11, NULL, 0, 80 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(5, NULL, 0, 80 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(7, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 104 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(6, NULL, 0, 80 * HZ_PER_MHZ), + /* The 1S_1D_2D variant would require 4.5 dummy bytes, this is not possible */ + WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(5, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 104 * HZ_PER_MHZ), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0), + /* The 1S_1D_1D variant would require 4.5 dummy bytes, this is not possible */ + WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0)); + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), @@ -445,11 +515,25 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand, if (iface != SSDR) return -EOPNOTSUPP; + /* + * At this stage, we do not yet know the continuous read template, nor + * if there is going to be one. Let's assume the continuous read + * template will be selected with the same heuristics as the buffered + * read variant, as there cannot be a HS configuration mismatch between + * them. + */ op = spinand->op_templates->read_cache; return w25n0xjw_set_sr4_hs(spinand, w25n0xjw_op_needs_hs(op)); } +static int w25n0xjw_set_cont_read(struct spinand_device *spinand, bool enable) +{ + u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ; + + return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, mask); +} + static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val) { struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr, @@ -580,12 +664,14 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants, + &write_cache_variants, + &update_cache_variants, + &cont_read_cache_dual_quad_dtr_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), + SPINAND_ECCINFO(&w25n01jw_ooblayout, w25w35nxxjw_ecc_get_status), + SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg), + SPINAND_CONT_READ(w25n0xjw_set_cont_read)), SPINAND_INFO("W25N01KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21), NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1), @@ -624,12 +710,14 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants, + &write_cache_variants, + &update_cache_variants, + &cont_read_cache_dual_quad_dtr_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), - SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), + SPINAND_ECCINFO(&w25m02gv_ooblayout, w25w35nxxjw_ecc_get_status), + SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg), + SPINAND_CONT_READ(w25n0xjw_set_cont_read)), SPINAND_INFO("W25N02KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), From 7845161edef8008710230efea3839757d0b03d25 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 26 Mar 2026 17:47:16 +0100 Subject: [PATCH 0368/5214] mtd: spinand: Make sure continuous read is always disabled during probe Recent changes made sure whenever we were using continuous reads, we would first start by disabling the feature to ensure a well proven and stable probe sequence. For development purposes, it might also matter to make sure we always disable continuous reads at first, in case the ECC configuration would change. Doing this "automatically" will become even more relevant when we add extra controller flags to prevent continuous reads at all. Ensure we disable continuous reads if the feature is available on the chip, regardless of whether it will be used or not. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 99a2494554ef..1d631054bb24 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -967,11 +967,7 @@ static void spinand_cont_read_init(struct spinand_device *spinand) enum nand_ecc_engine_type engine_type = nand->ecc.ctx.conf.engine_type; /* OOBs cannot be retrieved so external/on-host ECC engine won't work */ - if (spinand->set_cont_read && - (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || - engine_type == NAND_ECC_ENGINE_TYPE_NONE)) { - spinand->cont_read_possible = true; - + if (spinand->set_cont_read) { /* * Ensure continuous read is disabled on probe. * Some devices retain this state across soft reset, @@ -979,6 +975,10 @@ static void spinand_cont_read_init(struct spinand_device *spinand) * in false positive returns from spinand_isbad(). */ spinand_cont_read_enable(spinand, false); + + if (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || + engine_type == NAND_ECC_ENGINE_TYPE_NONE) + spinand->cont_read_possible = true; } } From 9e3997d3ab661f99828fb86487be56c25e166410 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 26 Mar 2026 17:47:17 +0100 Subject: [PATCH 0369/5214] mtd: spinand: Prevent continuous reads on some controllers Some controllers do not have full control over the CS line state and may deassert it under certain conditions in the middle of a (long) transfer. Continuous reads are stopped with a CS deassert, hence both features cannot live together. Whenever a controller flags that it cannot maintain the CS state reliably, disable continuous reads entirely. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 1d631054bb24..f1084d5e04b9 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -965,6 +965,7 @@ static void spinand_cont_read_init(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); enum nand_ecc_engine_type engine_type = nand->ecc.ctx.conf.engine_type; + struct spi_controller *ctlr = spinand->spimem->spi->controller; /* OOBs cannot be retrieved so external/on-host ECC engine won't work */ if (spinand->set_cont_read) { @@ -976,8 +977,9 @@ static void spinand_cont_read_init(struct spinand_device *spinand) */ spinand_cont_read_enable(spinand, false); - if (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || - engine_type == NAND_ECC_ENGINE_TYPE_NONE) + if ((engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || + engine_type == NAND_ECC_ENGINE_TYPE_NONE) && + !spi_mem_controller_is_capable(ctlr, no_cs_assertion)) spinand->cont_read_possible = true; } } From 87eee18f2974c0b1d854b6228408b93c6bc91744 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:07:19 -0700 Subject: [PATCH 0370/5214] mtd: sm_ftl: allocate cis_buffer with main struct Use a flexible array member and kzalloc_flex() to do so. Simplifies memory allocation slightly. Signed-off-by: Rosen Penev Signed-off-by: Miquel Raynal --- drivers/mtd/sm_ftl.c | 30 +++++++++++------------------- drivers/mtd/sm_ftl.h | 2 +- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index c8032755f9a4..fb0f97fb94f0 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -1133,7 +1133,7 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct sm_ftl *ftl; /* Allocate & initialize our private structure */ - ftl = kzalloc_obj(struct sm_ftl); + ftl = kzalloc_flex(*ftl, cis_buffer, SM_SECTOR_SIZE); if (!ftl) goto error1; @@ -1145,25 +1145,20 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) /* Read media information */ if (sm_get_media_info(ftl, mtd)) { dbg("found unsupported mtd device, aborting"); - goto error2; + goto error1; } - /* Allocate temporary CIS buffer for read retry support */ - ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); - if (!ftl->cis_buffer) - goto error2; - /* Allocate zone array, it will be initialized on demand */ ftl->zones = kzalloc_objs(struct ftl_zone, ftl->zone_count); if (!ftl->zones) - goto error3; + goto error2; /* Allocate the cache*/ ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); if (!ftl->cache_data) - goto error4; + goto error3; sm_cache_init(ftl); @@ -1171,7 +1166,7 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) /* Allocate upper layer structure and initialize it */ trans = kzalloc_obj(struct mtd_blktrans_dev); if (!trans) - goto error5; + goto error4; ftl->trans = trans; trans->priv = ftl; @@ -1184,12 +1179,12 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) if (sm_find_cis(ftl)) { dbg("CIS not found on mtd device, aborting"); - goto error6; + goto error5; } ftl->disk_attributes = sm_create_sysfs_attributes(ftl); if (!ftl->disk_attributes) - goto error6; + goto error5; trans->disk_attributes = ftl->disk_attributes; sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", @@ -1206,17 +1201,15 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) /* Register device*/ if (add_mtd_blktrans_dev(trans)) { dbg("error in mtdblktrans layer"); - goto error6; + goto error5; } return; -error6: - kfree(trans); error5: - kfree(ftl->cache_data); + kfree(trans); error4: - kfree(ftl->zones); + kfree(ftl->cache_data); error3: - kfree(ftl->cis_buffer); + kfree(ftl->zones); error2: kfree(ftl); error1: @@ -1242,7 +1235,6 @@ static void sm_remove_dev(struct mtd_blktrans_dev *dev) } sm_delete_sysfs_attributes(ftl); - kfree(ftl->cis_buffer); kfree(ftl->zones); kfree(ftl->cache_data); kfree(ftl); diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h index 6aed8b60de16..1a1a54ce836a 100644 --- a/drivers/mtd/sm_ftl.h +++ b/drivers/mtd/sm_ftl.h @@ -39,7 +39,6 @@ struct sm_ftl { int cis_block; /* CIS block location */ int cis_boffset; /* CIS offset in the block */ int cis_page_offset; /* CIS offset in the page */ - void *cis_buffer; /* tmp buffer for cis reads */ /* Cache */ int cache_block; /* block number of cached block */ @@ -56,6 +55,7 @@ struct sm_ftl { int cylinders; struct attribute_group *disk_attributes; + u8 cis_buffer[]; /* tmp buffer for cis reads */ }; struct chs_entry { From 9a3f9b3c47d8f071b0eb9e63906ac0448058278d Mon Sep 17 00:00:00 2001 From: Maksym Pikhotskyi Date: Fri, 17 Apr 2026 13:54:51 +0400 Subject: [PATCH 0371/5214] staging: rtl8723bs: fix stainfo check in rtw_aes_decrypt The null-pointer-guard was incorrect, returning _FAIL on valid pointer. Invert the guard, so it returns _FAIL on invalid pointer. Fixes: e23ad1570028 ("staging: rtl8723bs: use guard clause for stainfo check") Reported-by: Luka Gejak Closes: https://lore.kernel.org/linux-staging/E4BF62EF-C6F6-431F-8EDC-77C1E613E66B@linux.dev/ Reviewed-by: Dan Carpenter Signed-off-by: Maksym Pikhotskyi Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260417095452.23440-1-mpikhotskyi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_security.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index a00504ff2910..f467cb5b1dca 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -1212,7 +1212,7 @@ u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe) if (prxattrib->encrypt != _AES_) return _SUCCESS; stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); - if (stainfo) + if (!stainfo) return _FAIL; if (is_multicast_ether_addr(prxattrib->ra)) { static unsigned long start; From 13d883fa23289979f9fe28f28c6505bea5e0393c Mon Sep 17 00:00:00 2001 From: Maksym Pikhotskyi Date: Fri, 17 Apr 2026 13:54:52 +0400 Subject: [PATCH 0372/5214] staging: rtl8723bs: reduce nesting in rtw_security.c Improve readability of functions: rtw_tkip_encrypt, rtw_tkip_decrypt, rtw_aes_encrypt in rtw_security.c by adding early returns for the encryption type check and the stainfo null check. Signed-off-by: Maksym Pikhotskyi Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260417095452.23440-2-mpikhotskyi@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_security.c | 209 +++++++++--------- 1 file changed, 104 insertions(+), 105 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index f467cb5b1dca..2a5a4c0de9b1 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -449,47 +449,45 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe) pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + hw_hdr_offset; /* 4 start to encrypt each fragment */ - if (pattrib->encrypt == _TKIP_) { + if (pattrib->encrypt != _TKIP_) + return _SUCCESS; - { - if (is_multicast_ether_addr(pattrib->ra)) - prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey; - else - prwskey = pattrib->dot118021x_UncstKey.skey; + if (is_multicast_ether_addr(pattrib->ra)) + prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey; + else + prwskey = pattrib->dot118021x_UncstKey.skey; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { - iv = pframe + pattrib->hdrlen; - payload = pframe + pattrib->iv_len + pattrib->hdrlen; + for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { + iv = pframe + pattrib->hdrlen; + payload = pframe + pattrib->iv_len + pattrib->hdrlen; - GET_TKIP_PN(iv, dot11txpn); + GET_TKIP_PN(iv, dot11txpn); - pnl = (u16)(dot11txpn.val); - pnh = (u32)(dot11txpn.val >> 16); + pnl = (u16)(dot11txpn.val); + pnh = (u32)(dot11txpn.val >> 16); - phase1((u16 *)&ttkey[0], prwskey, &pattrib->ta[0], pnh); + phase1((u16 *)&ttkey[0], prwskey, &pattrib->ta[0], pnh); - phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0], pnl); + phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0], pnl); - if ((curfragnum + 1) == pattrib->nr_frags) { /* 4 the last fragment */ - length = pattrib->last_txcmdsz - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; - crc.f0 = cpu_to_le32(~crc32_le(~0, payload, length)); + if ((curfragnum + 1) == pattrib->nr_frags) { /* 4 the last fragment */ + length = pattrib->last_txcmdsz - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; + crc.f0 = cpu_to_le32(~crc32_le(~0, payload, length)); - arc4_setkey(ctx, rc4key, 16); - arc4_crypt(ctx, payload, payload, length); - arc4_crypt(ctx, payload + length, crc.f1, 4); + arc4_setkey(ctx, rc4key, 16); + arc4_crypt(ctx, payload, payload, length); + arc4_crypt(ctx, payload + length, crc.f1, 4); - } else { - length = pxmitpriv->frag_len - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; - crc.f0 = cpu_to_le32(~crc32_le(~0, payload, length)); + } else { + length = pxmitpriv->frag_len - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; + crc.f0 = cpu_to_le32(~crc32_le(~0, payload, length)); - arc4_setkey(ctx, rc4key, 16); - arc4_crypt(ctx, payload, payload, length); - arc4_crypt(ctx, payload + length, crc.f1, 4); + arc4_setkey(ctx, rc4key, 16); + arc4_crypt(ctx, payload, payload, length); + arc4_crypt(ctx, payload + length, crc.f1, 4); - pframe += pxmitpriv->frag_len; - pframe = (u8 *)round_up((SIZE_PTR)(pframe), 4); - } - } + pframe += pxmitpriv->frag_len; + pframe = (u8 *)round_up((SIZE_PTR)(pframe), 4); } } return res; @@ -517,82 +515,82 @@ u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe) pframe = (unsigned char *)((union recv_frame *)precvframe)->u.hdr.rx_data; /* 4 start to decrypt recvframe */ - if (prxattrib->encrypt == _TKIP_) { - stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); - if (stainfo) { - if (is_multicast_ether_addr(prxattrib->ra)) { - static unsigned long start; - static u32 no_gkey_bc_cnt; - static u32 no_gkey_mc_cnt; + if (prxattrib->encrypt != _TKIP_) + return _SUCCESS; - if (!psecuritypriv->binstallGrpkey) { - res = _FAIL; + stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); + if (!stainfo) + return _FAIL; - if (start == 0) - start = jiffies; + if (is_multicast_ether_addr(prxattrib->ra)) { + static unsigned long start; + static u32 no_gkey_bc_cnt; + static u32 no_gkey_mc_cnt; - if (is_broadcast_ether_addr(prxattrib->ra)) - no_gkey_bc_cnt++; - else - no_gkey_mc_cnt++; + if (!psecuritypriv->binstallGrpkey) { + res = _FAIL; - if (jiffies_to_msecs(jiffies - start) > 1000) { - if (no_gkey_bc_cnt || no_gkey_mc_cnt) { - netdev_dbg(padapter->pnetdev, - FUNC_ADPT_FMT " no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", - FUNC_ADPT_ARG(padapter), - no_gkey_bc_cnt, - no_gkey_mc_cnt); - } - start = jiffies; - no_gkey_bc_cnt = 0; - no_gkey_mc_cnt = 0; - } - goto exit; - } + if (start == 0) + start = jiffies; + if (is_broadcast_ether_addr(prxattrib->ra)) + no_gkey_bc_cnt++; + else + no_gkey_mc_cnt++; + + if (jiffies_to_msecs(jiffies - start) > 1000) { if (no_gkey_bc_cnt || no_gkey_mc_cnt) { netdev_dbg(padapter->pnetdev, - FUNC_ADPT_FMT " gkey installed. no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", + FUNC_ADPT_FMT " no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", FUNC_ADPT_ARG(padapter), no_gkey_bc_cnt, no_gkey_mc_cnt); } - start = 0; + start = jiffies; no_gkey_bc_cnt = 0; no_gkey_mc_cnt = 0; - - prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey; - } else { - prwskey = &stainfo->dot118021x_UncstKey.skey[0]; } - - iv = pframe + prxattrib->hdrlen; - payload = pframe + prxattrib->iv_len + prxattrib->hdrlen; - length = ((union recv_frame *)precvframe)->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len; - - GET_TKIP_PN(iv, dot11txpn); - - pnl = (u16)(dot11txpn.val); - pnh = (u32)(dot11txpn.val >> 16); - - phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], pnh); - phase2(&rc4key[0], prwskey, (unsigned short *)&ttkey[0], pnl); - - /* 4 decrypt payload include icv */ - - arc4_setkey(ctx, rc4key, 16); - arc4_crypt(ctx, payload, payload, length); - - *((u32 *)crc) = ~crc32_le(~0, payload, length - 4); - - if (crc[3] != payload[length - 1] || crc[2] != payload[length - 2] || - crc[1] != payload[length - 3] || crc[0] != payload[length - 4]) - res = _FAIL; - } else { - res = _FAIL; + goto exit; } + + if (no_gkey_bc_cnt || no_gkey_mc_cnt) { + netdev_dbg(padapter->pnetdev, + FUNC_ADPT_FMT " gkey installed. no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", + FUNC_ADPT_ARG(padapter), + no_gkey_bc_cnt, + no_gkey_mc_cnt); + } + start = 0; + no_gkey_bc_cnt = 0; + no_gkey_mc_cnt = 0; + + prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey; + } else { + prwskey = &stainfo->dot118021x_UncstKey.skey[0]; } + + iv = pframe + prxattrib->hdrlen; + payload = pframe + prxattrib->iv_len + prxattrib->hdrlen; + length = ((union recv_frame *)precvframe)->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len; + + GET_TKIP_PN(iv, dot11txpn); + + pnl = (u16)(dot11txpn.val); + pnh = (u32)(dot11txpn.val >> 16); + + phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], pnh); + phase2(&rc4key[0], prwskey, (unsigned short *)&ttkey[0], pnl); + + /* 4 decrypt payload include icv */ + + arc4_setkey(ctx, rc4key, 16); + arc4_crypt(ctx, payload, payload, length); + + *((u32 *)crc) = ~crc32_le(~0, payload, length - 4); + + if (crc[3] != payload[length - 1] || crc[2] != payload[length - 2] || + crc[1] != payload[length - 3] || crc[0] != payload[length - 4]) + res = _FAIL; exit: return res; } @@ -965,24 +963,25 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe) pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + hw_hdr_offset; /* 4 start to encrypt each fragment */ - if (pattrib->encrypt == _AES_) { - if (is_multicast_ether_addr(pattrib->ra)) - prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey; - else - prwskey = pattrib->dot118021x_UncstKey.skey; + if (pattrib->encrypt != _AES_) + return _SUCCESS; - for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { - if ((curfragnum + 1) == pattrib->nr_frags) { /* 4 the last fragment */ - length = pattrib->last_txcmdsz - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; + if (is_multicast_ether_addr(pattrib->ra)) + prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey; + else + prwskey = pattrib->dot118021x_UncstKey.skey; - aes_cipher(prwskey, pattrib->hdrlen, pframe, length); - } else { - length = pxmitpriv->frag_len - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; + for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) { + if ((curfragnum + 1) == pattrib->nr_frags) { /* 4 the last fragment */ + length = pattrib->last_txcmdsz - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; - aes_cipher(prwskey, pattrib->hdrlen, pframe, length); - pframe += pxmitpriv->frag_len; - pframe = (u8 *)round_up((SIZE_PTR)(pframe), 4); - } + aes_cipher(prwskey, pattrib->hdrlen, pframe, length); + } else { + length = pxmitpriv->frag_len - pattrib->hdrlen - pattrib->iv_len - pattrib->icv_len; + + aes_cipher(prwskey, pattrib->hdrlen, pframe, length); + pframe += pxmitpriv->frag_len; + pframe = (u8 *)round_up((SIZE_PTR)(pframe), 4); } } return res; From 2381baa4a9a0fe96007933084bc05fa4e40258f4 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 27 Apr 2026 05:46:56 +0000 Subject: [PATCH 0373/5214] staging: sm750fb: return -ETIMEDOUT on timeout in de_wait functions The hw_sm750le_de_wait() and hw_sm750_de_wait() functions return -1 when a timeout occurs. Replace these with -ETIMEDOUT to use a proper errno value and better describe the error condition. All callers check the return value as non-zero, so this change does not alter existing behavior. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260427054657.758-2-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_hw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index 79e6344a729f..beef05ded98d 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -501,8 +501,8 @@ int hw_sm750le_de_wait(void) (DE_STATE2_DE_FIFO_EMPTY | DE_STATE2_DE_MEM_FIFO_EMPTY)) return 0; } - /* timeout error */ - return -1; + + return -ETIMEDOUT; } int hw_sm750_de_wait(void) @@ -519,8 +519,8 @@ int hw_sm750_de_wait(void) (SYSTEM_CTRL_DE_FIFO_EMPTY | SYSTEM_CTRL_DE_MEM_FIFO_EMPTY)) return 0; } - /* timeout error */ - return -1; + + return -ETIMEDOUT; } int hw_sm750_pan_display(struct lynxfb_crtc *crtc, From 785ad91332236e968ce63444baa460be2ea99ef9 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 27 Apr 2026 05:46:57 +0000 Subject: [PATCH 0374/5214] staging: sm750fb: propagate error codes from de_wait() The sm750 acceleration functions currently return -1 when de_wait() fails, discarding the original error code. Since de_wait() now returns proper errno values, propagate the error code instead of returning -1. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260427054657.758-3-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_accel.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 0f94d859e91c..d8e91f7a7778 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -90,14 +90,16 @@ int sm750_hw_fillrect(struct lynx_accel *accel, u32 color, u32 rop) { u32 de_ctrl; + int ret; - if (accel->de_wait() != 0) { + ret = accel->de_wait(); + if (ret) { /* * int time wait and always busy,seems hardware * got something error */ pr_debug("De engine always busy\n"); - return -1; + return ret; } write_dpr(accel, DE_WINDOW_DESTINATION_BASE, base); /* dpr40 */ @@ -154,6 +156,7 @@ int sm750_hw_copyarea(struct lynx_accel *accel, unsigned int rop2) { unsigned int direction, de_ctrl; + int ret; direction = LEFT_TO_RIGHT; /* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */ @@ -263,8 +266,9 @@ int sm750_hw_copyarea(struct lynx_accel *accel, DE_WINDOW_WIDTH_DST_MASK) | (source_pitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr3c */ - if (accel->de_wait() != 0) - return -1; + ret = accel->de_wait(); + if (ret) + return ret; write_dpr(accel, DE_SOURCE, ((sx << DE_SOURCE_X_K1_SHIFT) & DE_SOURCE_X_K1_MASK) | @@ -326,14 +330,16 @@ int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf, unsigned int de_ctrl = 0; unsigned char remain[4]; int i, j; + int ret; start_bit &= 7; /* Just make sure the start bit is within legal range */ bytes_per_scan = (width + start_bit + 7) / 8; words_per_scan = bytes_per_scan & ~3; bytes_remain = bytes_per_scan & 3; - if (accel->de_wait() != 0) - return -1; + ret = accel->de_wait(); + if (ret) + return ret; /* * 2D Source Base. From 070e099a403bc848461088764de87cccbfe33787 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 27 Apr 2026 06:04:20 +0000 Subject: [PATCH 0375/5214] staging: sm750fb: fix typo in comment Fix typo in a comment. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260427060420.1076-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_accel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index d8e91f7a7778..ec2f0a6aa57d 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -95,8 +95,7 @@ int sm750_hw_fillrect(struct lynx_accel *accel, ret = accel->de_wait(); if (ret) { /* - * int time wait and always busy,seems hardware - * got something error + * Timeout waiting and engine always busy, seems like a hardware issue */ pr_debug("De engine always busy\n"); return ret; From 26813881181deb3a32fbb59eadb2599cbe8423f6 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 27 Apr 2026 10:17:12 +0200 Subject: [PATCH 0376/5214] staging: nvec: fix use-after-free in nvec_rx_completed() In nvec_rx_completed(), when an incomplete RX transfer is detected, nvec_msg_free() is called to return the message back to the pool by clearing its 'used' atomic flag. Immediately after this, the code accesses nvec->rx->data[0] to check the message type. Since nvec_msg_free() marks the pool slot as available via atomic_set(), any concurrent or subsequent call to nvec_msg_alloc() could claim that same slot and overwrite its data[] array. Reading nvec->rx->data[0] after freeing the message is therefore a use-after-free. Fix this by saving the message type byte before calling nvec_msg_free(), then using the saved value for the battery quirk check. Fixes: d6bdcf2e1019 ("staging: nvec: Add battery quirk to ignore incomplete responses") Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260427081713.3401874-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/nvec/nvec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 952c5a849a56..2a3499dd4d63 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -494,6 +494,8 @@ static void nvec_tx_completed(struct nvec_chip *nvec) static void nvec_rx_completed(struct nvec_chip *nvec) { if (nvec->rx->pos != nvec_msg_size(nvec->rx)) { + unsigned char msg_type = nvec->rx->data[0]; + dev_err(nvec->dev, "RX incomplete: Expected %u bytes, got %u\n", (uint)nvec_msg_size(nvec->rx), (uint)nvec->rx->pos); @@ -502,7 +504,7 @@ static void nvec_rx_completed(struct nvec_chip *nvec) nvec->state = 0; /* Battery quirk - Often incomplete, and likes to crash */ - if (nvec->rx->data[0] == NVEC_BAT) + if (msg_type == NVEC_BAT) complete(&nvec->ec_transfer); return; From fb2ae75b1ae5cffa11309b9ebf27aa4a2ceff9bd Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 27 Apr 2026 10:17:13 +0200 Subject: [PATCH 0377/5214] staging: nvec: fix unconditional pm_power_off teardown tegra_nvec_remove() unconditionally sets pm_power_off = NULL, even if nvec was not the one that registered it. This breaks any other driver that may have set pm_power_off to its own handler. Replace the unconditional assignment with a guarded check so that pm_power_off is only cleared if nvec was the one that set it. Also remove the stale FIXME comment, as the guard addresses exactly what it was asking for. Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260427081713.3401874-3-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/nvec/nvec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 2a3499dd4d63..88c416ee0381 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -906,8 +906,8 @@ static void tegra_nvec_remove(struct platform_device *pdev) nvec_unregister_notifier(nvec, &nvec->nvec_status_notifier); cancel_work_sync(&nvec->rx_work); cancel_work_sync(&nvec->tx_work); - /* FIXME: needs check whether nvec is responsible for power off */ - pm_power_off = NULL; + if (pm_power_off == nvec_power_off) + pm_power_off = NULL; } #ifdef CONFIG_PM_SLEEP From b6c1f5d3ff6bbfc32cfff2e8792e98f0b57ac9e1 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Mon, 27 Apr 2026 23:26:35 +0530 Subject: [PATCH 0378/5214] staging: most: video: remove filename from the top-of-file comment Remove the filename reference from top-of-file comment, to resolve checkpatch.pl warning. The filename can become outdated if the file is renamed. No functional change. Signed-off-by: Shyam Sunder Reddy Padira Link: https://patch.msgid.link/20260427175636.4605-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/video/video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 98988b0d9330..f846bb343b2e 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * video.c - V4L2 component for Mostcore + * V4L2 component for Mostcore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ From 593df953f6f50e82f8d653619b69288450fb6898 Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Tue, 28 Apr 2026 20:44:16 +0000 Subject: [PATCH 0379/5214] staging: sm750fb: remove double space in fb_ops entries Remove extra space after '=' in .fb_check_var assignments. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260428204416.55374-1-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 8f43eea2868b..fadba95850be 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -670,7 +670,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par) static const struct fb_ops lynxfb_ops = { .owner = THIS_MODULE, FB_DEFAULT_IOMEM_OPS, - .fb_check_var = lynxfb_ops_check_var, + .fb_check_var = lynxfb_ops_check_var, .fb_set_par = lynxfb_ops_set_par, .fb_setcolreg = lynxfb_ops_setcolreg, .fb_blank = lynxfb_ops_blank, @@ -680,7 +680,7 @@ static const struct fb_ops lynxfb_ops = { static const struct fb_ops lynxfb_ops_with_cursor = { .owner = THIS_MODULE, FB_DEFAULT_IOMEM_OPS, - .fb_check_var = lynxfb_ops_check_var, + .fb_check_var = lynxfb_ops_check_var, .fb_set_par = lynxfb_ops_set_par, .fb_setcolreg = lynxfb_ops_setcolreg, .fb_blank = lynxfb_ops_blank, @@ -691,7 +691,7 @@ static const struct fb_ops lynxfb_ops_with_cursor = { static const struct fb_ops lynxfb_ops_accel = { .owner = THIS_MODULE, __FB_DEFAULT_IOMEM_OPS_RDWR, - .fb_check_var = lynxfb_ops_check_var, + .fb_check_var = lynxfb_ops_check_var, .fb_set_par = lynxfb_ops_set_par, .fb_setcolreg = lynxfb_ops_setcolreg, .fb_blank = lynxfb_ops_blank, @@ -705,7 +705,7 @@ static const struct fb_ops lynxfb_ops_accel = { static const struct fb_ops lynxfb_ops_accel_with_cursor = { .owner = THIS_MODULE, __FB_DEFAULT_IOMEM_OPS_RDWR, - .fb_check_var = lynxfb_ops_check_var, + .fb_check_var = lynxfb_ops_check_var, .fb_set_par = lynxfb_ops_set_par, .fb_setcolreg = lynxfb_ops_setcolreg, .fb_blank = lynxfb_ops_blank, From 24b42bcd064b02a88f73e6163c2db2c8e4ef7100 Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Tue, 28 Apr 2026 23:38:16 -0400 Subject: [PATCH 0380/5214] staging: greybus: fix typo in sysfs-bus-greybus Fix spelling mistake: attibute -> attribute Signed-off-by: Maha Maryam Javaid Link: https://patch.msgid.link/20260429033816.11282-1-mahamaryamjavaid@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 2e998966cbe1..feeffe54a59b 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -26,7 +26,7 @@ Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - Writing a non-zero argument to this attibute disables the + Writing a non-zero argument to this attribute disables the module's interfaces before physically ejecting it. What: /sys/bus/greybus/devices/N-M/module_id From 4776aefb5aa5c2c5250597b95ab8d490e6b783cf Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sun, 3 May 2026 16:14:46 +0530 Subject: [PATCH 0381/5214] staging: most: dim2: remove filename from comment blocks Remove redundant filename references from top-of-file comment blocks across dim2 source files to resolve checkpatch.pl warnings. The filename is already implied by the file path and including it in comments can become outdated. No functional changes. Signed-off-by: Shyam Sunder Reddy Padira Link: https://patch.msgid.link/20260503104447.64657-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/dim2.c | 2 +- drivers/staging/most/dim2/errors.h | 2 +- drivers/staging/most/dim2/hal.c | 2 +- drivers/staging/most/dim2/hal.h | 2 +- drivers/staging/most/dim2/reg.h | 2 +- drivers/staging/most/dim2/sysfs.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 327790436653..ec0e568ec12d 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * dim2.c - MediaLB DIM2 Hardware Dependent Module + * MediaLB DIM2 Hardware Dependent Module * * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG */ diff --git a/drivers/staging/most/dim2/errors.h b/drivers/staging/most/dim2/errors.h index 268332e5735e..d707daaac3b6 100644 --- a/drivers/staging/most/dim2/errors.h +++ b/drivers/staging/most/dim2/errors.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * errors.h - Definitions of errors for DIM2 HAL API + * Definitions of errors for DIM2 HAL API * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG diff --git a/drivers/staging/most/dim2/hal.c b/drivers/staging/most/dim2/hal.c index 38376d3e3f0d..4f137ef846a3 100644 --- a/drivers/staging/most/dim2/hal.c +++ b/drivers/staging/most/dim2/hal.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * hal.c - DIM2 HAL implementation + * DIM2 HAL implementation * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG diff --git a/drivers/staging/most/dim2/hal.h b/drivers/staging/most/dim2/hal.h index ef10a8741c10..5d69a5e7d299 100644 --- a/drivers/staging/most/dim2/hal.h +++ b/drivers/staging/most/dim2/hal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * hal.h - DIM2 HAL interface + * DIM2 HAL interface * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG diff --git a/drivers/staging/most/dim2/reg.h b/drivers/staging/most/dim2/reg.h index b0f36c208a57..51ae158802b6 100644 --- a/drivers/staging/most/dim2/reg.h +++ b/drivers/staging/most/dim2/reg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * reg.h - Definitions for registers of DIM2 + * Definitions for registers of DIM2 * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG diff --git a/drivers/staging/most/dim2/sysfs.h b/drivers/staging/most/dim2/sysfs.h index 09115cf4ed00..60405cd473e7 100644 --- a/drivers/staging/most/dim2/sysfs.h +++ b/drivers/staging/most/dim2/sysfs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * sysfs.h - MediaLB sysfs information + * MediaLB sysfs information * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ From 4f44225a9b7aa99325fc0f4ea399b3f2f9595737 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:21 +0800 Subject: [PATCH 0382/5214] staging: octeon: convert cvmx_spi_mode_t from typedef to plain enum The Linux kernel coding style discourages the use of typedefs for enums. Convert cvmx_spi_mode_t to a plain 'enum cvmx_spi_mode' and update all users across the MIPS Octeon architecture code and the staging driver stubs. This is part of a series converting all remaining enum typedefs in the octeon subsystem to plain enums, improving compliance with the kernel coding style. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-2-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/executive/cvmx-spi.c | 16 ++++----- arch/mips/include/asm/octeon/cvmx-spi.h | 38 ++++++++++---------- drivers/staging/octeon/octeon-stubs.h | 6 ++-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c index eb9333e84a6b..b6c0b3fa73ad 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-spi.c +++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c @@ -102,7 +102,7 @@ void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks) * * Returns Zero on success, negative of failure. */ -int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, +int cvmx_spi_start_interface(int interface, enum cvmx_spi_mode mode, int timeout, int num_ports) { int res = -1; @@ -147,7 +147,7 @@ int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, * * Returns Zero on success, negative of failure. */ -int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout) +int cvmx_spi_restart_interface(int interface, enum cvmx_spi_mode mode, int timeout) { int res = -1; @@ -192,7 +192,7 @@ EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface); * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) +int cvmx_spi_reset_cb(int interface, enum cvmx_spi_mode mode) { union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl; union cvmx_spxx_clk_ctl spxx_clk_ctl; @@ -308,7 +308,7 @@ int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, +int cvmx_spi_calendar_setup_cb(int interface, enum cvmx_spi_mode mode, int num_ports) { int port; @@ -427,7 +427,7 @@ int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) +int cvmx_spi_clock_detect_cb(int interface, enum cvmx_spi_mode mode, int timeout) { int clock_transitions; union cvmx_spxx_clk_stat stat; @@ -505,7 +505,7 @@ int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) +int cvmx_spi_training_cb(int interface, enum cvmx_spi_mode mode, int timeout) { union cvmx_spxx_trn4_ctl spxx_trn4_ctl; union cvmx_spxx_clk_stat stat; @@ -574,7 +574,7 @@ int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) +int cvmx_spi_calendar_sync_cb(int interface, enum cvmx_spi_mode mode, int timeout) { uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { @@ -630,7 +630,7 @@ int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode) +int cvmx_spi_interface_up_cb(int interface, enum cvmx_spi_mode mode) { union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min; union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max; diff --git a/arch/mips/include/asm/octeon/cvmx-spi.h b/arch/mips/include/asm/octeon/cvmx-spi.h index d5038cc4b475..88f7e59a396b 100644 --- a/arch/mips/include/asm/octeon/cvmx-spi.h +++ b/arch/mips/include/asm/octeon/cvmx-spi.h @@ -36,35 +36,35 @@ /* CSR typedefs have been moved to cvmx-csr-*.h */ -typedef enum { +enum cvmx_spi_mode { CVMX_SPI_MODE_UNKNOWN = 0, CVMX_SPI_MODE_TX_HALFPLEX = 1, CVMX_SPI_MODE_RX_HALFPLEX = 2, CVMX_SPI_MODE_DUPLEX = 3 -} cvmx_spi_mode_t; +}; /** Callbacks structure to customize SPI4 initialization sequence */ typedef struct { /** Called to reset SPI4 DLL */ - int (*reset_cb) (int interface, cvmx_spi_mode_t mode); + int (*reset_cb)(int interface, enum cvmx_spi_mode mode); /** Called to setup calendar */ - int (*calendar_setup_cb) (int interface, cvmx_spi_mode_t mode, - int num_ports); + int (*calendar_setup_cb)(int interface, enum cvmx_spi_mode mode, + int num_ports); /** Called for Tx and Rx clock detection */ - int (*clock_detect_cb) (int interface, cvmx_spi_mode_t mode, - int timeout); + int (*clock_detect_cb)(int interface, enum cvmx_spi_mode mode, + int timeout); /** Called to perform link training */ - int (*training_cb) (int interface, cvmx_spi_mode_t mode, int timeout); + int (*training_cb)(int interface, enum cvmx_spi_mode mode, int timeout); /** Called for calendar data synchronization */ - int (*calendar_sync_cb) (int interface, cvmx_spi_mode_t mode, - int timeout); + int (*calendar_sync_cb)(int interface, enum cvmx_spi_mode mode, + int timeout); /** Called when interface is up */ - int (*interface_up_cb) (int interface, cvmx_spi_mode_t mode); + int (*interface_up_cb)(int interface, enum cvmx_spi_mode mode); } cvmx_spi_callbacks_t; @@ -94,7 +94,7 @@ static inline int cvmx_spi_is_spi_interface(int interface) * * Returns Zero on success, negative of failure. */ -extern int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_start_interface(int interface, enum cvmx_spi_mode mode, int timeout, int num_ports); /** @@ -110,7 +110,7 @@ extern int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, * @timeout: Timeout to wait for clock synchronization in seconds * Returns Zero on success, negative of failure. */ -extern int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_restart_interface(int interface, enum cvmx_spi_mode mode, int timeout); /** @@ -180,7 +180,7 @@ extern void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks); * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode); +extern int cvmx_spi_reset_cb(int interface, enum cvmx_spi_mode mode); /** * Callback to setup calendar and miscellaneous settings before clock @@ -197,7 +197,7 @@ extern int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode); * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_calendar_setup_cb(int interface, enum cvmx_spi_mode mode, int num_ports); /** @@ -214,7 +214,7 @@ extern int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_clock_detect_cb(int interface, enum cvmx_spi_mode mode, int timeout); /** @@ -231,7 +231,7 @@ extern int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_training_cb(int interface, enum cvmx_spi_mode mode, int timeout); /** @@ -248,7 +248,7 @@ extern int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, +extern int cvmx_spi_calendar_sync_cb(int interface, enum cvmx_spi_mode mode, int timeout); /** @@ -264,6 +264,6 @@ extern int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, * Returns Zero on success, non-zero error code on failure (will cause * SPI initialization to abort) */ -extern int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode); +extern int cvmx_spi_interface_up_cb(int interface, enum cvmx_spi_mode mode); #endif /* __CVMX_SPI_H__ */ diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 291eaffd2543..289a2d41fdc5 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -215,12 +215,12 @@ enum cvmx_fau_op_size { CVMX_FAU_OP_SIZE_64 = 3 }; -typedef enum { +enum cvmx_spi_mode { CVMX_SPI_MODE_UNKNOWN = 0, CVMX_SPI_MODE_TX_HALFPLEX = 1, CVMX_SPI_MODE_RX_HALFPLEX = 2, CVMX_SPI_MODE_DUPLEX = 3 -} cvmx_spi_mode_t; +}; typedef enum { CVMX_HELPER_INTERFACE_MODE_DISABLED, @@ -1364,7 +1364,7 @@ static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) } static inline int cvmx_spi_restart_interface(int interface, - cvmx_spi_mode_t mode, int timeout) + enum cvmx_spi_mode mode, int timeout) { return 0; } From 1397bb27f46ede6b03942b506d3e777b89634841 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:22 +0800 Subject: [PATCH 0383/5214] staging: octeon: convert cvmx_helper_interface_mode_t from typedef to plain enum The Linux kernel coding style discourages the use of typedefs for enums. Convert cvmx_helper_interface_mode_t to a plain 'enum cvmx_helper_interface_mode' and update all users across the MIPS Octeon architecture code and the staging driver stubs. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-3-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/executive/cvmx-helper-util.c | 2 +- arch/mips/cavium-octeon/executive/cvmx-helper.c | 8 ++++---- arch/mips/cavium-octeon/executive/cvmx-pko.c | 2 +- arch/mips/include/asm/octeon/cvmx-helper-util.h | 2 +- arch/mips/include/asm/octeon/cvmx-helper.h | 6 +++--- drivers/staging/octeon/ethernet.c | 2 +- drivers/staging/octeon/octeon-stubs.h | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c index 53b912745dbd..abaf91f6ae7c 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c @@ -52,7 +52,7 @@ * * Returns String */ -const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t +const char *cvmx_helper_interface_mode_to_string(enum cvmx_helper_interface_mode mode) { switch (mode) { diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c index 9abfc4bf9bd8..1985cd66806a 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -95,7 +95,7 @@ EXPORT_SYMBOL_GPL(cvmx_helper_ports_on_interface); * @INTERNAL * Return interface mode for CN68xx. */ -static cvmx_helper_interface_mode_t __cvmx_get_mode_cn68xx(int interface) +static enum cvmx_helper_interface_mode __cvmx_get_mode_cn68xx(int interface) { union cvmx_mio_qlmx_cfg qlm_cfg; switch (interface) { @@ -147,7 +147,7 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn68xx(int interface) * @INTERNAL * Return interface mode for an Octeon II */ -static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface) +static enum cvmx_helper_interface_mode __cvmx_get_mode_octeon2(int interface) { union cvmx_gmxx_inf_mode mode; @@ -247,7 +247,7 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface) * @INTERNAL * Return interface mode for CN7XXX. */ -static cvmx_helper_interface_mode_t __cvmx_get_mode_cn7xxx(int interface) +static enum cvmx_helper_interface_mode __cvmx_get_mode_cn7xxx(int interface) { union cvmx_gmxx_inf_mode mode; @@ -289,7 +289,7 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn7xxx(int interface) * Returns Mode of the interface. Unknown or unsupported interfaces return * DISABLED. */ -cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface) +enum cvmx_helper_interface_mode cvmx_helper_interface_get_mode(int interface) { union cvmx_gmxx_inf_mode mode; diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c index 6e70b859a0ac..760abbe12479 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-pko.c +++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c @@ -120,7 +120,7 @@ static void __cvmx_pko_port_map_o68(void) { int port; int interface, index; - cvmx_helper_interface_mode_t mode; + enum cvmx_helper_interface_mode mode; union cvmx_pko_mem_iport_ptrs config; /* diff --git a/arch/mips/include/asm/octeon/cvmx-helper-util.h b/arch/mips/include/asm/octeon/cvmx-helper-util.h index 97b27a07cfb0..103bb5b3142b 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-util.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-util.h @@ -42,7 +42,7 @@ * Returns String */ extern const char - *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t mode); + *cvmx_helper_interface_mode_to_string(enum cvmx_helper_interface_mode mode); /** * Setup Random Early Drop to automatically begin dropping packets. diff --git a/arch/mips/include/asm/octeon/cvmx-helper.h b/arch/mips/include/asm/octeon/cvmx-helper.h index 0cddce35291b..98824ff6314c 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper.h +++ b/arch/mips/include/asm/octeon/cvmx-helper.h @@ -38,7 +38,7 @@ #include #include -typedef enum { +enum cvmx_helper_interface_mode { CVMX_HELPER_INTERFACE_MODE_DISABLED, CVMX_HELPER_INTERFACE_MODE_RGMII, CVMX_HELPER_INTERFACE_MODE_GMII, @@ -49,7 +49,7 @@ typedef enum { CVMX_HELPER_INTERFACE_MODE_PICMG, CVMX_HELPER_INTERFACE_MODE_NPI, CVMX_HELPER_INTERFACE_MODE_LOOP, -} cvmx_helper_interface_mode_t; +}; union cvmx_helper_link_info { uint64_t u64; @@ -125,7 +125,7 @@ extern int cvmx_helper_get_number_of_interfaces(void); * Returns Mode of the interface. Unknown or unsupported interfaces return * DISABLED. */ -extern cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int +extern enum cvmx_helper_interface_mode cvmx_helper_interface_get_mode(int interface); /** diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index eadb74fc14c8..5f9c29071fab 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -798,7 +798,7 @@ static int cvm_oct_probe(struct platform_device *pdev) num_interfaces = cvmx_helper_get_number_of_interfaces(); for (interface = 0; interface < num_interfaces; interface++) { - cvmx_helper_interface_mode_t imode = + enum cvmx_helper_interface_mode imode = cvmx_helper_interface_get_mode(interface); int num_ports = cvmx_helper_ports_on_interface(interface); int port; diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 289a2d41fdc5..6c0329270464 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -222,7 +222,7 @@ enum cvmx_spi_mode { CVMX_SPI_MODE_DUPLEX = 3 }; -typedef enum { +enum cvmx_helper_interface_mode { CVMX_HELPER_INTERFACE_MODE_DISABLED, CVMX_HELPER_INTERFACE_MODE_RGMII, CVMX_HELPER_INTERFACE_MODE_GMII, @@ -233,7 +233,7 @@ typedef enum { CVMX_HELPER_INTERFACE_MODE_PICMG, CVMX_HELPER_INTERFACE_MODE_NPI, CVMX_HELPER_INTERFACE_MODE_LOOP, -} cvmx_helper_interface_mode_t; +}; typedef enum { CVMX_POW_WAIT = 1, @@ -1267,7 +1267,7 @@ static inline void cvmx_pko_get_port_status(u64 port_num, u64 clear, cvmx_pko_port_status_t *status) { } -static inline cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int +static inline enum cvmx_helper_interface_mode cvmx_helper_interface_get_mode(int interface) { return 0; From 3711d19ae08b455d9f5e3613685bb41554d79a27 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:23 +0800 Subject: [PATCH 0384/5214] staging: octeon: convert cvmx_pow_wait_t from typedef to plain enum The Linux kernel coding style discourages the use of typedefs for enums. Convert cvmx_pow_wait_t to a plain 'enum cvmx_pow_wait' and update all users across the MIPS Octeon architecture code and the staging driver stubs. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-4-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/octeon/cvmx-pow.h | 12 ++++++------ drivers/staging/octeon/octeon-stubs.h | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/mips/include/asm/octeon/cvmx-pow.h b/arch/mips/include/asm/octeon/cvmx-pow.h index 21b4378244fa..409029809639 100644 --- a/arch/mips/include/asm/octeon/cvmx-pow.h +++ b/arch/mips/include/asm/octeon/cvmx-pow.h @@ -84,10 +84,10 @@ enum cvmx_pow_tag_type { /** * Wait flag values for pow functions. */ -typedef enum { +enum cvmx_pow_wait { CVMX_POW_WAIT = 1, CVMX_POW_NO_WAIT = 0, -} cvmx_pow_wait_t; +}; /** * POW tag operations. These are used in the data stored to the POW. @@ -1348,7 +1348,7 @@ static inline void cvmx_pow_tag_sw_wait(void) * Returns: the WQE pointer from POW. Returns NULL if no work * was available. */ -static inline struct cvmx_wqe *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t +static inline struct cvmx_wqe *cvmx_pow_work_request_sync_nocheck(enum cvmx_pow_wait wait) { cvmx_pow_load_addr_t ptr; @@ -1382,7 +1382,7 @@ static inline struct cvmx_wqe *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_ * Returns: the WQE pointer from POW. Returns NULL if no work * was available. */ -static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +static inline struct cvmx_wqe *cvmx_pow_work_request_sync(enum cvmx_pow_wait wait) { if (CVMX_ENABLE_POW_CHECKS) __cvmx_pow_warn_if_pending_switch(__func__); @@ -1436,7 +1436,7 @@ static inline enum cvmx_pow_tag_type cvmx_pow_work_request_null_rd(void) * timeout), 0 to cause response to return immediately */ static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, - cvmx_pow_wait_t wait) + enum cvmx_pow_wait wait) { cvmx_pow_iobdma_store_t data; @@ -1465,7 +1465,7 @@ static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, * timeout), 0 to cause response to return immediately */ static inline void cvmx_pow_work_request_async(int scr_addr, - cvmx_pow_wait_t wait) + enum cvmx_pow_wait wait) { if (CVMX_ENABLE_POW_CHECKS) __cvmx_pow_warn_if_pending_switch(__func__); diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 6c0329270464..df0456417f15 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -235,10 +235,10 @@ enum cvmx_helper_interface_mode { CVMX_HELPER_INTERFACE_MODE_LOOP, }; -typedef enum { +enum cvmx_pow_wait { CVMX_POW_WAIT = 1, CVMX_POW_NO_WAIT = 0, -} cvmx_pow_wait_t; +}; typedef enum { CVMX_PKO_LOCK_NONE = 0, @@ -1344,11 +1344,11 @@ static inline unsigned int cvmx_get_core_num(void) } static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, - cvmx_pow_wait_t wait) + enum cvmx_pow_wait wait) { } static inline void cvmx_pow_work_request_async(int scr_addr, - cvmx_pow_wait_t wait) + enum cvmx_pow_wait wait) { } static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) @@ -1358,7 +1358,7 @@ static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) return wqe; } -static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +static inline struct cvmx_wqe *cvmx_pow_work_request_sync(enum cvmx_pow_wait wait) { return (void *)(unsigned long)wait; } From 8b804dad84c2df382f1fa45484487dfd75100a4a Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:24 +0800 Subject: [PATCH 0385/5214] staging: octeon: convert cvmx_pko_lock_t from typedef to plain enum The Linux kernel coding style discourages the use of typedefs for enums. Convert cvmx_pko_lock_t to a plain 'enum cvmx_pko_lock' and update all users across the MIPS Octeon architecture code and the staging driver stubs. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-5-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/octeon/cvmx-pko.h | 10 +++++----- drivers/staging/octeon/octeon-stubs.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h index f18a7f24daf8..d8e74a305646 100644 --- a/arch/mips/include/asm/octeon/cvmx-pko.h +++ b/arch/mips/include/asm/octeon/cvmx-pko.h @@ -93,7 +93,7 @@ typedef enum { /** * This enumeration represents the different locking modes supported by PKO. */ -typedef enum { +enum cvmx_pko_lock { /* * PKO doesn't do any locking. It is the responsibility of the * application to make sure that no other core is accessing @@ -112,7 +112,7 @@ typedef enum { * ll/sc. This is the most portable locking mechanism. */ CVMX_PKO_LOCK_CMD_QUEUE = 2, -} cvmx_pko_lock_t; +}; typedef struct { uint32_t packets; @@ -374,7 +374,7 @@ static inline void cvmx_pko_doorbell(uint64_t port, uint64_t queue, */ static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, - cvmx_pko_lock_t use_locking) + enum cvmx_pko_lock use_locking) { if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) { /* @@ -419,7 +419,7 @@ static inline cvmx_pko_status_t cvmx_pko_send_packet_finish( uint64_t queue, union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, - cvmx_pko_lock_t use_locking) + enum cvmx_pko_lock use_locking) { cvmx_cmd_queue_result_t result; if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) @@ -463,7 +463,7 @@ static inline cvmx_pko_status_t cvmx_pko_send_packet_finish3( union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, uint64_t addr, - cvmx_pko_lock_t use_locking) + enum cvmx_pko_lock use_locking) { cvmx_cmd_queue_result_t result; if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index df0456417f15..06cb4f15d9d5 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -240,11 +240,11 @@ enum cvmx_pow_wait { CVMX_POW_NO_WAIT = 0, }; -typedef enum { +enum cvmx_pko_lock { CVMX_PKO_LOCK_NONE = 0, CVMX_PKO_LOCK_ATOMIC_TAG = 1, CVMX_PKO_LOCK_CMD_QUEUE = 2, -} cvmx_pko_lock_t; +}; typedef enum { CVMX_PKO_SUCCESS, @@ -1383,12 +1383,12 @@ static inline union cvmx_gmxx_rxx_rx_inbnd cvmx_spi4000_check_speed(int interfac } static inline void cvmx_pko_send_packet_prepare(u64 port, u64 queue, - cvmx_pko_lock_t use_locking) + enum cvmx_pko_lock use_locking) { } static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(u64 port, u64 queue, union cvmx_pko_command_word0 pko_command, - union cvmx_buf_ptr packet, cvmx_pko_lock_t use_locking) + union cvmx_buf_ptr packet, enum cvmx_pko_lock use_locking) { return 0; } From 95147c0f62dcdec92ca8955f783d477a73b1f2be Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:25 +0800 Subject: [PATCH 0386/5214] staging: octeon: convert cvmx_pko_status_t from typedef to plain enum The Linux kernel coding style discourages the use of typedefs for enums. Convert cvmx_pko_status_t to a plain 'enum cvmx_pko_status' and update all users across the MIPS Octeon architecture code and the staging driver stubs. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-6-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/executive/cvmx-pko.c | 4 ++-- arch/mips/include/asm/octeon/cvmx-pko.h | 10 +++++----- drivers/staging/octeon/octeon-stubs.h | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c index 760abbe12479..b0199d5cb551 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-pko.c +++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c @@ -323,11 +323,11 @@ EXPORT_SYMBOL_GPL(cvmx_pko_shutdown); * queues have higher priority than higher numbered queues. * There must be num_queues elements in the array. */ -cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, uint64_t base_queue, +enum cvmx_pko_status cvmx_pko_config_port(uint64_t port, uint64_t base_queue, uint64_t num_queues, const uint64_t priority[]) { - cvmx_pko_status_t result_code; + enum cvmx_pko_status result_code; uint64_t queue; union cvmx_pko_mem_queue_ptrs config; union cvmx_pko_reg_queue_ptrs1 config1; diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h index d8e74a305646..a742c1d61d8f 100644 --- a/arch/mips/include/asm/octeon/cvmx-pko.h +++ b/arch/mips/include/asm/octeon/cvmx-pko.h @@ -80,7 +80,7 @@ #define CVMX_PKO_ILLEGAL_QUEUE 0xFFFF #define CVMX_PKO_MAX_QUEUE_DEPTH 0 -typedef enum { +enum cvmx_pko_status { CVMX_PKO_SUCCESS, CVMX_PKO_INVALID_PORT, CVMX_PKO_INVALID_QUEUE, @@ -88,7 +88,7 @@ typedef enum { CVMX_PKO_NO_MEMORY, CVMX_PKO_PORT_ALREADY_SETUP, CVMX_PKO_CMD_QUEUE_INIT_ERROR -} cvmx_pko_status_t; +}; /** * This enumeration represents the different locking modes supported by PKO. @@ -306,7 +306,7 @@ extern void cvmx_pko_shutdown(void); * of a value of 1. There must be num_queues elements in the * array. */ -extern cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, +extern enum cvmx_pko_status cvmx_pko_config_port(uint64_t port, uint64_t base_queue, uint64_t num_queues, const uint64_t priority[]); @@ -414,7 +414,7 @@ static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, * Returns: CVMX_PKO_SUCCESS on success, or error code on * failure of output */ -static inline cvmx_pko_status_t cvmx_pko_send_packet_finish( +static inline enum cvmx_pko_status cvmx_pko_send_packet_finish( uint64_t port, uint64_t queue, union cvmx_pko_command_word0 pko_command, @@ -457,7 +457,7 @@ static inline cvmx_pko_status_t cvmx_pko_send_packet_finish( * Returns: CVMX_PKO_SUCCESS on success, or error code on * failure of output */ -static inline cvmx_pko_status_t cvmx_pko_send_packet_finish3( +static inline enum cvmx_pko_status cvmx_pko_send_packet_finish3( uint64_t port, uint64_t queue, union cvmx_pko_command_word0 pko_command, diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 06cb4f15d9d5..8496c60d647e 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -246,7 +246,7 @@ enum cvmx_pko_lock { CVMX_PKO_LOCK_CMD_QUEUE = 2, }; -typedef enum { +enum cvmx_pko_status { CVMX_PKO_SUCCESS, CVMX_PKO_INVALID_PORT, CVMX_PKO_INVALID_QUEUE, @@ -254,7 +254,7 @@ typedef enum { CVMX_PKO_NO_MEMORY, CVMX_PKO_PORT_ALREADY_SETUP, CVMX_PKO_CMD_QUEUE_INIT_ERROR -} cvmx_pko_status_t; +}; enum cvmx_pow_tag_type { CVMX_POW_TAG_TYPE_ORDERED = 0L, @@ -1386,7 +1386,7 @@ static inline void cvmx_pko_send_packet_prepare(u64 port, u64 queue, enum cvmx_pko_lock use_locking) { } -static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(u64 port, +static inline enum cvmx_pko_status cvmx_pko_send_packet_finish(u64 port, u64 queue, union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, enum cvmx_pko_lock use_locking) { From 32c681e669fd5002ea2b5f5aed2509d6f9ed4739 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:26 +0800 Subject: [PATCH 0387/5214] staging: octeon: convert cvmx_pko_port_status_t from typedef to plain struct The Linux kernel coding style discourages the use of typedefs for structs. Convert cvmx_pko_port_status_t to a plain 'struct cvmx_pko_port_status' and update all users across the MIPS Octeon architecture code and the staging driver. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-7-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/octeon/cvmx-pko.h | 6 +++--- drivers/staging/octeon/ethernet.c | 2 +- drivers/staging/octeon/octeon-stubs.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h index a742c1d61d8f..26cb26a7ff2b 100644 --- a/arch/mips/include/asm/octeon/cvmx-pko.h +++ b/arch/mips/include/asm/octeon/cvmx-pko.h @@ -114,11 +114,11 @@ enum cvmx_pko_lock { CVMX_PKO_LOCK_CMD_QUEUE = 2, }; -typedef struct { +struct cvmx_pko_port_status { uint32_t packets; uint64_t octets; uint64_t doorbell; -} cvmx_pko_port_status_t; +}; /** * This structure defines the address to use on a packet enqueue @@ -574,7 +574,7 @@ static inline int cvmx_pko_get_num_queues(int port) * @status: Where to put the results. */ static inline void cvmx_pko_get_port_status(uint64_t port_num, uint64_t clear, - cvmx_pko_port_status_t *status) + struct cvmx_pko_port_status *status) { union cvmx_pko_reg_read_idx pko_reg_read_idx; union cvmx_pko_mem_count0 pko_mem_count0; diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 5f9c29071fab..448a4ec42d0b 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -202,7 +202,7 @@ EXPORT_SYMBOL(cvm_oct_free_work); static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev) { cvmx_pip_port_status_t rx_status; - cvmx_pko_port_status_t tx_status; + struct cvmx_pko_port_status tx_status; struct octeon_ethernet *priv = netdev_priv(dev); if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) { diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 8496c60d647e..7bb72e152f08 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -411,11 +411,11 @@ typedef struct { u16 inb_errors; } cvmx_pip_port_status_t; -typedef struct { +struct cvmx_pko_port_status { u32 packets; u64 octets; u64 doorbell; -} cvmx_pko_port_status_t; +}; union cvmx_pip_frm_len_chkx { u64 u64; @@ -1264,7 +1264,7 @@ static inline void cvmx_pip_get_port_status(u64 port_num, u64 clear, { } static inline void cvmx_pko_get_port_status(u64 port_num, u64 clear, - cvmx_pko_port_status_t *status) + struct cvmx_pko_port_status *status) { } static inline enum cvmx_helper_interface_mode cvmx_helper_interface_get_mode(int From 90a7073e8af3f779d43d85e34e6ff383c9a6cbd8 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 27 Apr 2026 23:54:27 +0800 Subject: [PATCH 0388/5214] staging: octeon: convert cvmx_pip_port_status_t from typedef to plain struct The Linux kernel coding style discourages the use of typedefs for structs. Convert cvmx_pip_port_status_t to a plain 'struct cvmx_pip_port_status' and update all users across the MIPS Octeon architecture code and the staging driver. No functional change. Signed-off-by: Eric Wu Link: https://patch.msgid.link/20260427155427.668540-8-kunjinkao.jp@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/octeon/cvmx-pip.h | 6 +++--- drivers/staging/octeon/ethernet.c | 2 +- drivers/staging/octeon/octeon-stubs.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/include/asm/octeon/cvmx-pip.h b/arch/mips/include/asm/octeon/cvmx-pip.h index 01ca7267a2ba..911276ee90c2 100644 --- a/arch/mips/include/asm/octeon/cvmx-pip.h +++ b/arch/mips/include/asm/octeon/cvmx-pip.h @@ -180,7 +180,7 @@ typedef union { /** * Status statistics for a port */ -typedef struct { +struct cvmx_pip_port_status { /* Inbound octets marked to be dropped by the IPD */ uint32_t dropped_octets; /* Inbound packets marked to be dropped by the IPD */ @@ -236,7 +236,7 @@ typedef struct { uint64_t inb_octets; /* Number of packets with GMX/SPX/PCI errors received by PIP */ uint16_t inb_errors; -} cvmx_pip_port_status_t; +}; /** * Definition of the PIP custom header that can be prepended @@ -365,7 +365,7 @@ static inline void cvmx_pip_config_diffserv_qos(uint64_t diffserv, uint64_t qos) * @status: Where to put the results. */ static inline void cvmx_pip_get_port_status(uint64_t port_num, uint64_t clear, - cvmx_pip_port_status_t *status) + struct cvmx_pip_port_status *status) { union cvmx_pip_stat_ctl pip_stat_ctl; union cvmx_pip_stat0_prtx stat0; diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 448a4ec42d0b..d85a9991faf6 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -201,7 +201,7 @@ EXPORT_SYMBOL(cvm_oct_free_work); */ static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev) { - cvmx_pip_port_status_t rx_status; + struct cvmx_pip_port_status rx_status; struct cvmx_pko_port_status tx_status; struct octeon_ethernet *priv = netdev_priv(dev); diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 7bb72e152f08..9c1968b7e2d1 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -386,7 +386,7 @@ union cvmx_ipd_sub_port_qos_cnt { } s; }; -typedef struct { +struct cvmx_pip_port_status { u32 dropped_octets; u32 dropped_packets; u32 pci_raw_packets; @@ -409,7 +409,7 @@ typedef struct { u32 inb_packets; u64 inb_octets; u16 inb_errors; -} cvmx_pip_port_status_t; +}; struct cvmx_pko_port_status { u32 packets; @@ -1260,7 +1260,7 @@ static inline int octeon_is_simulation(void) } static inline void cvmx_pip_get_port_status(u64 port_num, u64 clear, - cvmx_pip_port_status_t *status) + struct cvmx_pip_port_status *status) { } static inline void cvmx_pko_get_port_status(u64 port_num, u64 clear, From 7b07c48f8e944c1fdc1630a88f07e285018e562f Mon Sep 17 00:00:00 2001 From: Miguel Cyrineu Vale Date: Mon, 27 Apr 2026 16:37:59 -0300 Subject: [PATCH 0389/5214] staging: rtl8723bs: replace NULL comparison with NOT operator Fix checkpatch error "Comparison to NULL could be written "!psta"" in ioctl_cfg80211.c:2432 Signed-off-by: Miguel Cyrineu Vale Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427193759.71642-1-miguelcvalealt@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index a55c6808755e..1a2cce218f40 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -2429,7 +2429,7 @@ static int cfg80211_rtw_dump_station(struct wiphy *wiphy, spin_lock_bh(&pstapriv->asoc_list_lock); psta = rtw_sta_info_get_by_idx(idx, pstapriv); spin_unlock_bh(&pstapriv->asoc_list_lock); - if (psta == NULL) { + if (!psta) { ret = -ENOENT; goto exit; } From fa093d85bd7f66c31edd97af0c98f37356f57cad Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Tue, 28 Apr 2026 23:41:56 -0400 Subject: [PATCH 0390/5214] staging: rtl8723bs: fix typo in rtw_pwrctrl.c Fix spelling mistake: willl -> will Signed-off-by: Maha Maryam Javaid Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260429034156.11523-1-mahamaryamjavaid@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_pwrctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c index f074ea6e0f38..ca1cb58fc801 100644 --- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c @@ -576,7 +576,7 @@ void LPS_Leave_check(struct adapter *padapter) * * This will be called when CPWM interrupt is up. * - * using to update cpwn of drv; and drv willl make a decision to up or down pwr level + * using to update cpwn of drv; and drv will make a decision to up or down pwr level */ void cpwm_int_hdl(struct adapter *padapter, struct reportpwrstate_parm *preportpwrstate) { From 0752f1f4dbee06a3b13f25bdb068840ad110d02f Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:50 +0200 Subject: [PATCH 0391/5214] staging: rtl8723bs: remove blank line in rtw_btcoex.h Removes redundant blank line in rtw_btcoex.h. Found using checkpatch.pl. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-2-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 19764c80b8ba..5554118bbe36 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -9,7 +9,6 @@ #include - #define PACKET_NORMAL 0 #define PACKET_DHCP 1 #define PACKET_ARP 2 From e045a83f980a9c07461590041a65a28e784014c0 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:51 +0200 Subject: [PATCH 0392/5214] staging: rtl8723bs: add function definition arg names to rtw_btcoex.h Adds function definition argument names to rtw_btcoex.h in order to conform to kernel code style. This eliminates warnings generated by checkpatch.pl. In all cases it's the 'struct adapter *' argument which has been given the name 'padapter'. This matches the names used in the implementations of these functions. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-3-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 5554118bbe36..df939b20867b 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -14,14 +14,14 @@ #define PACKET_ARP 2 #define PACKET_EAPOL 3 -void rtw_btcoex_MediaStatusNotify(struct adapter *, u8 mediaStatus); -void rtw_btcoex_HaltNotify(struct adapter *); +void rtw_btcoex_MediaStatusNotify(struct adapter *padapter, u8 mediaStatus); +void rtw_btcoex_HaltNotify(struct adapter *padapter); /* ================================================== */ /* Below Functions are called by BT-Coex */ /* ================================================== */ -void rtw_btcoex_RejectApAggregatedPacket(struct adapter *, u8 enable); -void rtw_btcoex_LPS_Enter(struct adapter *); -void rtw_btcoex_LPS_Leave(struct adapter *); +void rtw_btcoex_RejectApAggregatedPacket(struct adapter *padapter, u8 enable); +void rtw_btcoex_LPS_Enter(struct adapter *padapter); +void rtw_btcoex_LPS_Leave(struct adapter *padapter); #endif /* __RTW_BTCOEX_H__ */ From b55686cb84a1733200c7647711f79edfe8f9486f Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:52 +0200 Subject: [PATCH 0393/5214] staging: rtl8723bs: rename rtw_btcoex_MediaStatusNotify() Renames function rtw_btcoex_MediaStatusNotify to rtw_btcoex_media_status_notify which removes CamelCase in order to conform to kernel code style. Issue found using checkpatch.pl Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-4-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ap.c | 2 +- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/core/rtw_cmd.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index f213bb8e173c..0fc41ae47fb0 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -2039,5 +2039,5 @@ void stop_ap_mode(struct adapter *padapter) rtw_free_mlme_priv_ie_data(pmlmepriv); - rtw_btcoex_MediaStatusNotify(padapter, 0); /* disconnect */ + rtw_btcoex_media_status_notify(padapter, 0); /* disconnect */ } diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 055ca95ff5a7..38d6b66a24d8 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -8,7 +8,7 @@ #include #include -void rtw_btcoex_MediaStatusNotify(struct adapter *padapter, u8 media_status) +void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 media_status) { if ((media_status == RT_MEDIA_CONNECT) && (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) { diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 72d5905a48e9..3fc9406e421c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -1291,11 +1291,11 @@ void lps_ctrl_wk_hdl(struct adapter *padapter, u8 lps_ctrl_type) /* Reset LPS Setting */ pwrpriv->LpsIdleCount = 0; rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus)); - rtw_btcoex_MediaStatusNotify(padapter, mstatus); + rtw_btcoex_media_status_notify(padapter, mstatus); break; case LPS_CTRL_DISCONNECT: mstatus = 0;/* disconnect */ - rtw_btcoex_MediaStatusNotify(padapter, mstatus); + rtw_btcoex_media_status_notify(padapter, mstatus); LPS_Leave(padapter, "LPS_CTRL_DISCONNECT"); rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus)); break; diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index cfd3eb253350..351f66e1ecbc 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -5126,7 +5126,7 @@ u8 setopmode_hdl(struct adapter *padapter, u8 *pbuf) if (psetop->mode == Ndis802_11APMode) { /* Do this after port switch to */ /* prevent from downloading rsvd page to wrong port */ - rtw_btcoex_MediaStatusNotify(padapter, 1); /* connect */ + rtw_btcoex_media_status_notify(padapter, 1); /* connect */ } return H2C_SUCCESS; diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index df939b20867b..524f0fba294d 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -14,7 +14,7 @@ #define PACKET_ARP 2 #define PACKET_EAPOL 3 -void rtw_btcoex_MediaStatusNotify(struct adapter *padapter, u8 mediaStatus); +void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 mediaStatus); void rtw_btcoex_HaltNotify(struct adapter *padapter); /* ================================================== */ From 5b7921d3859d0234240887f498be30709b1e1be6 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:53 +0200 Subject: [PATCH 0394/5214] staging: rtl8723bs: rename rtw_btcoex_media_status_notify definition arg Renames rtw_btcoex_media_status_notify_definition argument mediaStatus to media_status in order to conform to kernel code style. This was found using checkpatch.pl Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-5-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 524f0fba294d..6eced86d6623 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -14,7 +14,7 @@ #define PACKET_ARP 2 #define PACKET_EAPOL 3 -void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 mediaStatus); +void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 media_status); void rtw_btcoex_HaltNotify(struct adapter *padapter); /* ================================================== */ From c8e50e93c019904c950135da55d494a169fbf088 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:54 +0200 Subject: [PATCH 0395/5214] staging: rtl8723bs: rtw_btcoex_HaltNotify() -> rtw_btcoex_halt_notify() Renames function rtw_btcoex_HaltNotify to rtw_btcoex_halt_notify in order to conform to kernel code style. Found using checkpatch.pl. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-6-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 38d6b66a24d8..197bc4e7f73b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -18,7 +18,7 @@ void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 media_status) hal_btcoex_MediaStatusNotify(padapter, media_status); } -void rtw_btcoex_HaltNotify(struct adapter *padapter) +void rtw_btcoex_halt_notify(struct adapter *padapter) { if (!padapter->bup) return; diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 6eced86d6623..d9ed8f2d8d14 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -15,7 +15,7 @@ #define PACKET_EAPOL 3 void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 media_status); -void rtw_btcoex_HaltNotify(struct adapter *padapter); +void rtw_btcoex_halt_notify(struct adapter *padapter); /* ================================================== */ /* Below Functions are called by BT-Coex */ diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index 97a1e0da3034..cdb0b64cfaeb 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -400,7 +400,7 @@ static void rtw_dev_remove(struct sdio_func *func) LeaveAllPowerSaveMode(padapter); - rtw_btcoex_HaltNotify(padapter); + rtw_btcoex_halt_notify(padapter); rtw_sdio_if1_deinit(padapter); From 3018c9102689c957c686f0b3b7ba988dfe8a6693 Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:55 +0200 Subject: [PATCH 0396/5214] staging: rtl8723bs: rename rtw_btcoex_RejectApAggregatedPacket() Renames rtw_btcoex_RejectApAggregatedPacket to rtw_btcoex_reject_ap_aggregated_packet in order to conform to kernel code style. Found using checkpatch.pl. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-7-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 6 +++--- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 197bc4e7f73b..c2926322ebd2 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -32,7 +32,7 @@ void rtw_btcoex_halt_notify(struct adapter *padapter) /* ================================================== */ /* Below Functions are called by BT-Coex */ /* ================================================== */ -void rtw_btcoex_RejectApAggregatedPacket(struct adapter *padapter, u8 enable) +void rtw_btcoex_reject_ap_aggregated_packet(struct adapter *padapter, u8 enable) { struct mlme_ext_info *pmlmeinfo; struct sta_info *psta; diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 2a2dd60be8bb..77c474919ffc 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -134,7 +134,7 @@ static void halbtcoutsrc_AggregationCheck(struct btc_coexist *pBtCoexist) bNeedToAct = false; if (pBtCoexist->btInfo.bRejectAggPkt) { - rtw_btcoex_RejectApAggregatedPacket(padapter, true); + rtw_btcoex_reject_ap_aggregated_packet(padapter, true); } else { if (pBtCoexist->btInfo.bPreBtCtrlAggBufSize != pBtCoexist->btInfo.bBtCtrlAggBufSize) { @@ -151,8 +151,8 @@ static void halbtcoutsrc_AggregationCheck(struct btc_coexist *pBtCoexist) } if (bNeedToAct) { - rtw_btcoex_RejectApAggregatedPacket(padapter, true); - rtw_btcoex_RejectApAggregatedPacket(padapter, false); + rtw_btcoex_reject_ap_aggregated_packet(padapter, true); + rtw_btcoex_reject_ap_aggregated_packet(padapter, false); } } } diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index d9ed8f2d8d14..61a67360af00 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -20,7 +20,7 @@ void rtw_btcoex_halt_notify(struct adapter *padapter); /* ================================================== */ /* Below Functions are called by BT-Coex */ /* ================================================== */ -void rtw_btcoex_RejectApAggregatedPacket(struct adapter *padapter, u8 enable); +void rtw_btcoex_reject_ap_aggregated_packet(struct adapter *padapter, u8 enable); void rtw_btcoex_LPS_Enter(struct adapter *padapter); void rtw_btcoex_LPS_Leave(struct adapter *padapter); From 45b3b8bffa5a4f3d0331aa8c35671aeb7d323b2c Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:56 +0200 Subject: [PATCH 0397/5214] staging: rtl8723bs: rtw_btcoex_LPS_Enter() -> rtw_btcoex_lps_enter() Renames rtw_btcoex_LPS_Enter to rtw_btcoex_lps_enter in order to conform to kernel code style. Found using checkpatch.pl. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-8-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 2 +- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index c2926322ebd2..c9dd1a3cfabb 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -49,7 +49,7 @@ void rtw_btcoex_reject_ap_aggregated_packet(struct adapter *padapter, u8 enable) } } -void rtw_btcoex_LPS_Enter(struct adapter *padapter) +void rtw_btcoex_lps_enter(struct adapter *padapter) { struct pwrctrl_priv *pwrpriv; u8 lps_val; diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 77c474919ffc..fc977d5497c4 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -49,7 +49,7 @@ static void halbtcoutsrc_EnterLps(struct btc_coexist *pBtCoexist) pBtCoexist->btInfo.bBtCtrlLps = true; pBtCoexist->btInfo.bBtLpsOn = true; - rtw_btcoex_LPS_Enter(padapter); + rtw_btcoex_lps_enter(padapter); } static void halbtcoutsrc_NormalLps(struct btc_coexist *pBtCoexist) diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 61a67360af00..22010239515a 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -21,7 +21,7 @@ void rtw_btcoex_halt_notify(struct adapter *padapter); /* Below Functions are called by BT-Coex */ /* ================================================== */ void rtw_btcoex_reject_ap_aggregated_packet(struct adapter *padapter, u8 enable); -void rtw_btcoex_LPS_Enter(struct adapter *padapter); +void rtw_btcoex_lps_enter(struct adapter *padapter); void rtw_btcoex_LPS_Leave(struct adapter *padapter); #endif /* __RTW_BTCOEX_H__ */ From d845a187463e579eb0631cc927c73f72e41fe62b Mon Sep 17 00:00:00 2001 From: Linus Probert Date: Mon, 27 Apr 2026 19:38:57 +0200 Subject: [PATCH 0398/5214] staging: rtl8723bs: rename rtw_btcoex_LPS_Leave to rtw_btcoex_lps_leave Renames rtw_btcoex_LPS_Leave to rtw_btcoex_lps_leave in order to conform to kernel code style. Found using checkpatch.pl. Signed-off-by: Linus Probert Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260427173857.585742-9-linus.probert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 4 ++-- drivers/staging/rtl8723bs/include/rtw_btcoex.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index c9dd1a3cfabb..38937ba837f2 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -61,7 +61,7 @@ void rtw_btcoex_lps_enter(struct adapter *padapter) rtw_set_ps_mode(padapter, PS_MODE_MIN, 0, lps_val, "BTCOEX"); } -void rtw_btcoex_LPS_Leave(struct adapter *padapter) +void rtw_btcoex_lps_leave(struct adapter *padapter) { struct pwrctrl_priv *pwrpriv; diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index fc977d5497c4..81d25a4404c1 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -36,7 +36,7 @@ static void halbtcoutsrc_LeaveLps(struct btc_coexist *pBtCoexist) pBtCoexist->btInfo.bBtCtrlLps = true; pBtCoexist->btInfo.bBtLpsOn = false; - rtw_btcoex_LPS_Leave(padapter); + rtw_btcoex_lps_leave(padapter); } static void halbtcoutsrc_EnterLps(struct btc_coexist *pBtCoexist) @@ -60,7 +60,7 @@ static void halbtcoutsrc_NormalLps(struct btc_coexist *pBtCoexist) if (pBtCoexist->btInfo.bBtCtrlLps) { pBtCoexist->btInfo.bBtLpsOn = false; - rtw_btcoex_LPS_Leave(padapter); + rtw_btcoex_lps_leave(padapter); pBtCoexist->btInfo.bBtCtrlLps = false; /* recover the LPS state to the original */ diff --git a/drivers/staging/rtl8723bs/include/rtw_btcoex.h b/drivers/staging/rtl8723bs/include/rtw_btcoex.h index 22010239515a..765103498789 100644 --- a/drivers/staging/rtl8723bs/include/rtw_btcoex.h +++ b/drivers/staging/rtl8723bs/include/rtw_btcoex.h @@ -22,6 +22,6 @@ void rtw_btcoex_halt_notify(struct adapter *padapter); /* ================================================== */ void rtw_btcoex_reject_ap_aggregated_packet(struct adapter *padapter, u8 enable); void rtw_btcoex_lps_enter(struct adapter *padapter); -void rtw_btcoex_LPS_Leave(struct adapter *padapter); +void rtw_btcoex_lps_leave(struct adapter *padapter); #endif /* __RTW_BTCOEX_H__ */ From 617a446d0e80ffe41189cc90ae462b4e9e956b8b Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 30 Apr 2026 10:25:29 +0000 Subject: [PATCH 0399/5214] staging: fbtft: remove unused function fbtft_write_gpio16_wr_latched The function fbtft_write_gpio16_wr_latched is not referenced anywhere in the driver and only contains a stub implementation. Remove it from the driver. Signed-off-by: Hungyu Lin Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260430102529.25019-1-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fbtft/fbtft-io.c | 7 ------- drivers/staging/fbtft/fbtft.h | 1 - 2 files changed, 8 deletions(-) diff --git a/drivers/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c index de1904a443c2..7d331eba947a 100644 --- a/drivers/staging/fbtft/fbtft-io.c +++ b/drivers/staging/fbtft/fbtft-io.c @@ -227,10 +227,3 @@ int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) return 0; } EXPORT_SYMBOL(fbtft_write_gpio16_wr); - -int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) -{ - dev_err(par->info->device, "%s: function not implemented\n", __func__); - return -1; -} -EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h index 317be17b95c1..c7afb0fd3943 100644 --- a/drivers/staging/fbtft/fbtft.h +++ b/drivers/staging/fbtft/fbtft.h @@ -262,7 +262,6 @@ int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len); int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); -int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len); /* fbtft-bus.c */ int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); From 1dd3d7febbba0c7c9162859fc5af63f982ec1fe2 Mon Sep 17 00:00:00 2001 From: Abhai Kollara Date: Thu, 30 Apr 2026 20:12:06 +0000 Subject: [PATCH 0400/5214] staging: rtl8723bs: simplify NULL pointer comparisons in rtw_recv.h Fix checkpatch.pl warnings regarding explicit comparisons to NULL. The kernel coding style prefers the shorter if (!ptr) idiom over if (ptr == NULL). Clean up the inline functions in rtw_recv.h to match this standard. Signed-off-by: Abhai Kollara Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260430201158.2807823-1-abhai@protonmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_recv.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_recv.h b/drivers/staging/rtl8723bs/include/rtw_recv.h index 8e45871f07f0..9c32d480a20a 100644 --- a/drivers/staging/rtl8723bs/include/rtw_recv.h +++ b/drivers/staging/rtl8723bs/include/rtw_recv.h @@ -349,7 +349,7 @@ s32 rtw_recv_entry(union recv_frame *precv_frame); static inline u8 *get_rxmem(union recv_frame *precvframe) { /* always return rx_head... */ - if (precvframe == NULL) + if (!precvframe) return NULL; return precvframe->u.hdr.rx_head; @@ -362,7 +362,7 @@ static inline u8 *recvframe_pull(union recv_frame *precvframe, signed int sz) /* used for extract sz bytes from rx_data, update rx_data and return the updated rx_data to the caller */ - if (precvframe == NULL) + if (!precvframe) return NULL; @@ -387,7 +387,7 @@ static inline u8 *recvframe_put(union recv_frame *precvframe, signed int sz) /* after putting, rx_tail must be still larger than rx_end. */ unsigned char *prev_rx_tail; - if (precvframe == NULL) + if (!precvframe) return NULL; prev_rx_tail = precvframe->u.hdr.rx_tail; @@ -414,7 +414,7 @@ static inline u8 *recvframe_pull_tail(union recv_frame *precvframe, signed int s /* used for extract sz bytes from rx_end, update rx_end and return the updated rx_end to the caller */ /* after pulling, rx_end must be still larger than rx_data. */ - if (precvframe == NULL) + if (!precvframe) return NULL; precvframe->u.hdr.rx_tail -= sz; From e5855f8a4728038bd3732092e2758653252213e3 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Thu, 30 Apr 2026 20:29:05 +0300 Subject: [PATCH 0401/5214] staging: rtl8723bs: remove wrapper rtw_hal_disable_interrupt() Remove unnecessary wrapper and call DisableInterrupt8723BSdio() function directly to simplify the code. Signed-off-by: Nikolay Kulikov Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260430172954.12828-2-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 5 ----- drivers/staging/rtl8723bs/include/hal_intf.h | 2 -- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 2 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 5 ++--- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 529648036f58..f75b8a8192b8 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -94,11 +94,6 @@ void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariab SetHalODMVar(padapter, eVariable, pValue1, bSet); } -void rtw_hal_disable_interrupt(struct adapter *padapter) -{ - DisableInterrupt8723BSdio(padapter); -} - u8 rtw_hal_check_ips_status(struct adapter *padapter) { return CheckIPSStatus(padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 6af2337822ff..1d62ea71a890 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -193,8 +193,6 @@ u8 rtw_hal_get_def_var(struct adapter *padapter, enum hal_def_variable eVariable void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet); -void rtw_hal_disable_interrupt(struct adapter *padapter); - u8 rtw_hal_check_ips_status(struct adapter *padapter); s32 rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index b80f1d03ed5a..9270daa018b9 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -1141,7 +1141,7 @@ static int rtw_resume_process_normal(struct adapter *padapter) if (ret) goto exit; } - rtw_hal_disable_interrupt(padapter); + DisableInterrupt8723BSdio(padapter); /* if (sdio_alloc_irq(adapter_to_dvobj(padapter)) != _SUCCESS) */ if ((padapter->intf_alloc_irq) && (padapter->intf_alloc_irq(adapter_to_dvobj(padapter)) != _SUCCESS)) { ret = -1; diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index cdb0b64cfaeb..d047481816eb 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -211,8 +211,7 @@ static void sd_intf_stop(struct adapter *padapter) if (!padapter) return; - /* hal dep */ - rtw_hal_disable_interrupt(padapter); + DisableInterrupt8723BSdio(padapter); } @@ -281,7 +280,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct /* set mac addr */ rtw_macaddr_cfg(&psdio->func->dev, padapter->eeprompriv.mac_addr); - rtw_hal_disable_interrupt(padapter); + DisableInterrupt8723BSdio(padapter); status = _SUCCESS; From 52a6ca2c869304bd898e5147b3e68c858b31282f Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Thu, 30 Apr 2026 20:29:06 +0300 Subject: [PATCH 0402/5214] staging: rtl8723bs: rename DisableInterrupt8723BSdio() to snake_case Rename function DisableInterrupt8723BSdio() to rtw_sdio_disable_interrupt() and format its description to comply with Linux kernel coding style. Declare this function without 'extern' prototype in the .h file to fix checkpatch.pl warning. Signed-off-by: Nikolay Kulikov Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260430172954.12828-3-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_ops.c | 17 +++++++---------- drivers/staging/rtl8723bs/include/sdio_ops.h | 2 +- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 2 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 4 ++-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 77a6a5c7ec7e..5d74913be573 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -626,16 +626,13 @@ void rtw_sdio_enable_interrupt(struct adapter *adapter) rtw_write8(adapter, REG_C2HEVT_CLEAR, C2H_EVT_HOST_CLOSE); } -/* */ -/* Description: */ -/* Disable SDIO Host IMR configuration to mask unnecessary interrupt service. */ -/* */ -/* Assumption: */ -/* Using SDIO Local register ONLY for configuration. */ -/* */ -/* Created by Roger, 2011.02.11. */ -/* */ -void DisableInterrupt8723BSdio(struct adapter *adapter) +/* + * Disable SDIO Host IMR configuration to mask unnecessary interrupt service. + * + * Assumption: + * Using SDIO Local register ONLY for configuration. + */ +void rtw_sdio_disable_interrupt(struct adapter *adapter) { __le32 himr; diff --git a/drivers/staging/rtl8723bs/include/sdio_ops.h b/drivers/staging/rtl8723bs/include/sdio_ops.h index 7bb4b2047cc1..13f13076bc16 100644 --- a/drivers/staging/rtl8723bs/include/sdio_ops.h +++ b/drivers/staging/rtl8723bs/include/sdio_ops.h @@ -28,7 +28,7 @@ extern u8 CheckIPSStatus(struct adapter *padapter); extern void InitInterrupt8723BSdio(struct adapter *padapter); extern void InitSysInterrupt8723BSdio(struct adapter *padapter); void rtw_sdio_enable_interrupt(struct adapter *padapter); -extern void DisableInterrupt8723BSdio(struct adapter *padapter); +void rtw_sdio_disable_interrupt(struct adapter *padapter); extern u8 HalQueryTxBufferStatus8723BSdio(struct adapter *padapter); extern void HalQueryTxOQTBufferStatus8723BSdio(struct adapter *padapter); #endif /* !__SDIO_OPS_H__ */ diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index 9270daa018b9..f31196f54b3e 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -1141,7 +1141,7 @@ static int rtw_resume_process_normal(struct adapter *padapter) if (ret) goto exit; } - DisableInterrupt8723BSdio(padapter); + rtw_sdio_disable_interrupt(padapter); /* if (sdio_alloc_irq(adapter_to_dvobj(padapter)) != _SUCCESS) */ if ((padapter->intf_alloc_irq) && (padapter->intf_alloc_irq(adapter_to_dvobj(padapter)) != _SUCCESS)) { ret = -1; diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d047481816eb..c43a0391a5ca 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -211,7 +211,7 @@ static void sd_intf_stop(struct adapter *padapter) if (!padapter) return; - DisableInterrupt8723BSdio(padapter); + rtw_sdio_disable_interrupt(padapter); } @@ -280,7 +280,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct /* set mac addr */ rtw_macaddr_cfg(&psdio->func->dev, padapter->eeprompriv.mac_addr); - DisableInterrupt8723BSdio(padapter); + rtw_sdio_disable_interrupt(padapter); status = _SUCCESS; From 1376b1880465a4b2dcc30d446dc3ec0ed47b5212 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 2 May 2026 13:49:30 +0500 Subject: [PATCH 0403/5214] staging: rtl8723bs: remove unnecessary blank lines in rtw_ioctl_set.c Drop blank lines flagged by checkpatch.pl in rtw_ioctl_set.c: CHECK: Blank lines aren't necessary before a close brace '}' CHECK: Blank lines aren't necessary after an open brace '{' No functional change. Signed-off-by: Stepan Ionichev Reviewed-by: Luka Gejak Reviewed-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502084930.857-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ioctl_set.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index 69cbf89f0745..3cc2a7c9d27f 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -120,9 +120,7 @@ u8 rtw_do_join(struct adapter *padapter) pmlmepriv->to_join = false; } } - } - } exit: @@ -416,7 +414,6 @@ u8 rtw_set_802_11_authentication_mode(struct adapter *padapter, enum ndis_802_11 u8 rtw_set_802_11_add_wep(struct adapter *padapter, struct ndis_802_11_wep *wep) { - signed int keyid, res; struct security_priv *psecuritypriv = &(padapter->securitypriv); u8 ret = _SUCCESS; From c5a1b68c804ae1719c36b0a3b9cb91561d45c93d Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 2 May 2026 13:50:18 +0500 Subject: [PATCH 0404/5214] staging: rtl8723bs: drop blank line before close brace in rtw_ieee80211.c Drop a blank line before a close brace in rtw_get_wapi_ie(), flagged by checkpatch.pl: CHECK: Blank lines aren't necessary before a close brace '}' No functional change. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260502085018.1440-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 72b7f731dd47..5fb1fe5b72b3 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -593,7 +593,6 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) if (wapi_len) *wapi_len = in_ie[cnt + 1] + 2; - } cnt += in_ie[cnt + 1] + 2; /* get next */ From 2e078c0a5c6a6d9892bef62a36387cbb683ba032 Mon Sep 17 00:00:00 2001 From: Francisco Maestre Date: Sat, 2 May 2026 19:58:02 -0500 Subject: [PATCH 0405/5214] staging: rtl8723bs: fix block comment alignment in hal_pwr_seq.c Fix the multi-line block comment at the top of the file to follow the kernel coding style: each continuation line should start with ' * ' and the closing '*/' should be on its own line without a preceding blank line. This fixes the following checkpatch.pl warning: WARNING: Block comments should align the * on each line Signed-off-by: Francisco Maestre Link: https://patch.msgid.link/20260503005802.69046-1-francisco@maestretorreblanca.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_pwr_seq.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_pwr_seq.c b/drivers/staging/rtl8723bs/hal/hal_pwr_seq.c index 2438931ca51b..0b60eba128af 100644 --- a/drivers/staging/rtl8723bs/hal/hal_pwr_seq.c +++ b/drivers/staging/rtl8723bs/hal/hal_pwr_seq.c @@ -6,16 +6,14 @@ ******************************************************************************/ /* -* -This file includes all kinds of Power Action event for RTL8723B -and corresponding hardware configurations which are released from HW SD. - -Major Change History: - When Who What - ---------- --------------- ------------------------------- - 2011-08-08 Roger Create. - -*/ + * This file includes all kinds of Power Action event for RTL8723B + * and corresponding hardware configurations which are released from HW SD. + * + * Major Change History: + * When Who What + * ---------- --------------- ------------------------------- + * 2011-08-08 Roger Create. + */ #include "hal_pwr_seq.h" From 1d14738fa178c023bd9877b119c4a8ec6dfb6c5f Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:39 +0300 Subject: [PATCH 0406/5214] staging: rtl8723bs: core: simplify boolean comparisons Simplify boolean comparisons to improve readability and conform to the Linux kernel coding style. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-2-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ap.c | 2 +- drivers/staging/rtl8723bs/core/rtw_btcoex.c | 2 +- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- .../staging/rtl8723bs/core/rtw_ieee80211.c | 2 +- .../staging/rtl8723bs/core/rtw_ioctl_set.c | 54 +++++++------- drivers/staging/rtl8723bs/core/rtw_mlme.c | 12 ++-- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 16 ++--- drivers/staging/rtl8723bs/core/rtw_recv.c | 71 +++++++++---------- drivers/staging/rtl8723bs/core/rtw_sta_mgt.c | 2 +- .../staging/rtl8723bs/core/rtw_wlan_util.c | 18 ++--- drivers/staging/rtl8723bs/core/rtw_xmit.c | 34 ++++----- 11 files changed, 107 insertions(+), 108 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 0fc41ae47fb0..75d790e1b5c5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -297,7 +297,7 @@ void expire_timeout_chk(struct adapter *padapter) psta->keep_alive_trycnt = 0; spin_lock_bh(&pstapriv->asoc_list_lock); - if (list_empty(&psta->asoc_list) == false) { + if (!list_empty(&psta->asoc_list)) { list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, false, diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index 38937ba837f2..9e00424d00bd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -11,7 +11,7 @@ void rtw_btcoex_media_status_notify(struct adapter *padapter, u8 media_status) { if ((media_status == RT_MEDIA_CONNECT) && - (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) { + check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE)) { rtw_hal_set_hwreg(padapter, HW_VAR_DL_RSVD_PAGE, NULL); } diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 3fc9406e421c..f6e618675299 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -696,7 +696,7 @@ u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) t_len = sizeof(struct wlan_bssid_ex); /* for hidden ap to set fw_state here */ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) != true) { + if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE)) { switch (ndis_network_mode) { case Ndis802_11IBSS: set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 5fb1fe5b72b3..d0bbe1bb979c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -347,7 +347,7 @@ int rtw_generate_ie(struct registry_priv *pregistrypriv) /* HT Cap. */ if ((pregistrypriv->wireless_mode & WIRELESS_11_24N) && - (pregistrypriv->ht_enable == true)) { + pregistrypriv->ht_enable) { /* todo: */ } diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index 3cc2a7c9d27f..ebf0808f4104 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -80,7 +80,7 @@ u8 rtw_do_join(struct adapter *padapter) pmlmepriv->to_join = false; _set_timer(&pmlmepriv->assoc_timer, MAX_JOIN_TIMEOUT); } else { - if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { /* submit createbss_cmd to change to a ADHOC_MASTER */ /* pmlmepriv->lock has been acquired by caller... */ @@ -137,32 +137,32 @@ u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) netdev_dbg(padapter->pnetdev, "set ssid [%s] fw_state = 0x%08x\n", ssid->ssid, get_fwstate(pmlmepriv)); - if (padapter->hw_init_completed == false) { + if (!padapter->hw_init_completed) { status = _FAIL; goto exit; } spin_lock_bh(&pmlmepriv->lock); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) goto handle_tkip_countermeasure; - else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) + else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) goto release_mlme_lock; - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE) == true) { + if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE)) { if ((pmlmepriv->assoc_ssid.ssid_length == ssid->ssid_length) && (!memcmp(&pmlmepriv->assoc_ssid.ssid, ssid->ssid, ssid->ssid_length))) { - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == false) { - if (rtw_is_same_ibss(padapter, pnetwork) == false) { + if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + if (!rtw_is_same_ibss(padapter, pnetwork)) { /* if in WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE, create bss or rejoin again */ rtw_disassoc_cmd(padapter, 0, true); - if (check_fwstate(pmlmepriv, _FW_LINKED) == true) + if (check_fwstate(pmlmepriv, _FW_LINKED)) rtw_indicate_disconnect(padapter); rtw_free_assoc_resources(padapter, 1); - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE); set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); } @@ -175,12 +175,12 @@ u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) } else { rtw_disassoc_cmd(padapter, 0, true); - if (check_fwstate(pmlmepriv, _FW_LINKED) == true) + if (check_fwstate(pmlmepriv, _FW_LINKED)) rtw_indicate_disconnect(padapter); rtw_free_assoc_resources(padapter, 1); - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE); set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); } @@ -193,7 +193,7 @@ u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) goto release_mlme_lock; } - if (rtw_validate_ssid(ssid) == false) { + if (!rtw_validate_ssid(ssid)) { status = _FAIL; goto release_mlme_lock; } @@ -201,7 +201,7 @@ u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid)); pmlmepriv->assoc_by_bssid = false; - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) pmlmepriv->to_join = true; else status = rtw_do_join(padapter); @@ -221,10 +221,10 @@ u8 rtw_set_802_11_connect(struct adapter *padapter, u8 *bssid, struct ndis_802_1 bool ssid_valid = true; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - if (!ssid || rtw_validate_ssid(ssid) == false) + if (!ssid || !rtw_validate_ssid(ssid)) ssid_valid = false; - if (!bssid || rtw_validate_bssid(bssid) == false) + if (!bssid || !rtw_validate_bssid(bssid)) bssid_valid = false; if (!ssid_valid && !bssid_valid) { @@ -232,7 +232,7 @@ u8 rtw_set_802_11_connect(struct adapter *padapter, u8 *bssid, struct ndis_802_1 goto exit; } - if (padapter->hw_init_completed == false) { + if (!padapter->hw_init_completed) { status = _FAIL; goto exit; } @@ -242,9 +242,9 @@ u8 rtw_set_802_11_connect(struct adapter *padapter, u8 *bssid, struct ndis_802_1 netdev_dbg(padapter->pnetdev, FUNC_ADPT_FMT " fw_state = 0x%08x\n", FUNC_ADPT_ARG(padapter), get_fwstate(pmlmepriv)); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) goto handle_tkip_countermeasure; - else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) + else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) goto release_mlme_lock; handle_tkip_countermeasure: @@ -265,7 +265,7 @@ u8 rtw_set_802_11_connect(struct adapter *padapter, u8 *bssid, struct ndis_802_1 pmlmepriv->assoc_by_bssid = false; } - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) pmlmepriv->to_join = true; else status = rtw_do_join(padapter); @@ -294,15 +294,15 @@ u8 rtw_set_802_11_infrastructure_mode(struct adapter *padapter, spin_lock_bh(&pmlmepriv->lock); - if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) || (*pold_state == Ndis802_11IBSS)) + if (check_fwstate(pmlmepriv, _FW_LINKED) || (*pold_state == Ndis802_11IBSS)) rtw_disassoc_cmd(padapter, 0, true); - if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) || - (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) + if (check_fwstate(pmlmepriv, _FW_LINKED) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) rtw_free_assoc_resources(padapter, 1); if ((*pold_state == Ndis802_11Infrastructure) || (*pold_state == Ndis802_11IBSS)) { - if (check_fwstate(pmlmepriv, _FW_LINKED) == true) + if (check_fwstate(pmlmepriv, _FW_LINKED)) rtw_indicate_disconnect(padapter); /* will clr Linked_state; before this function, we must have checked whether issue dis-assoc_cmd or not */ } @@ -344,7 +344,7 @@ u8 rtw_set_802_11_disassociate(struct adapter *padapter) spin_lock_bh(&pmlmepriv->lock); - if (check_fwstate(pmlmepriv, _FW_LINKED) == true) { + if (check_fwstate(pmlmepriv, _FW_LINKED)) { rtw_disassoc_cmd(padapter, 0, true); rtw_indicate_disconnect(padapter); /* modify for CONFIG_IEEE80211W, none 11w can use it */ @@ -366,7 +366,7 @@ u8 rtw_set_802_11_bssid_list_scan(struct adapter *padapter, struct ndis_802_11_s res = false; goto exit; } - if (padapter->hw_init_completed == false) { + if (!padapter->hw_init_completed) { res = false; goto exit; } @@ -467,8 +467,8 @@ u16 rtw_get_cur_max_rate(struct adapter *adapter) struct sta_info *psta = NULL; u8 short_GI = 0; - if ((check_fwstate(pmlmepriv, _FW_LINKED) != true) - && (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) != true)) + if (!check_fwstate(pmlmepriv, _FW_LINKED) + && !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) return 0; psta = rtw_get_stainfo(&adapter->stapriv, get_bssid(pmlmepriv)); diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 3ecf4f979c03..14a6d0e2a3c1 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1272,7 +1272,7 @@ void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) struct wlan_network *cur_network = &pmlmepriv->cur_network; struct wlan_network *ptarget_wlan = NULL; - if (rtw_access_ctrl(adapter, pstassoc->macaddr) == false) + if (!rtw_access_ctrl(adapter, pstassoc->macaddr)) return; if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { @@ -1556,7 +1556,7 @@ void rtw_mlme_reset_auto_scan_int(struct adapter *adapter) if (pmlmeinfo->VHT_enable) /* disable auto scan when connect to 11AC AP */ mlme->auto_scan_int_ms = 0; - else if (adapter->registrypriv.wifi_spec && is_client_associated_to_ap(adapter) == true) + else if (adapter->registrypriv.wifi_spec && is_client_associated_to_ap(adapter)) mlme->auto_scan_int_ms = 60 * 1000; else if (rtw_chk_roam_flags(adapter, RTW_ROAM_ACTIVE)) { if (check_fwstate(mlme, WIFI_STATION_STATE) && check_fwstate(mlme, _FW_LINKED)) @@ -1658,10 +1658,10 @@ static int rtw_check_roaming_candidate(struct mlme_priv *mlme int updated = false; struct adapter *adapter = container_of(mlme, struct adapter, mlmepriv); - if (is_same_ess(&competitor->network, &mlme->cur_network.network) == false) + if (!is_same_ess(&competitor->network, &mlme->cur_network.network)) goto exit; - if (rtw_is_desired_network(adapter, competitor) == false) + if (!rtw_is_desired_network(adapter, competitor)) goto exit; /* got specific addr to roam */ @@ -1753,12 +1753,12 @@ static int rtw_check_join_candidate(struct mlme_priv *mlme goto exit; } - if (rtw_is_desired_network(adapter, competitor) == false) + if (!rtw_is_desired_network(adapter, competitor)) goto exit; if (rtw_to_roam(adapter) > 0) { if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= mlme->roam_scanr_exp_ms - || is_same_ess(&competitor->network, &mlme->cur_network.network) == false + || !is_same_ess(&competitor->network, &mlme->cur_network.network) ) goto exit; } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 351f66e1ecbc..e47ede86836c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -512,8 +512,8 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) return _SUCCESS; - if (check_fwstate(pmlmepriv, _FW_LINKED) == false && - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE) == false) { + if (!check_fwstate(pmlmepriv, _FW_LINKED) && + !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) { return _SUCCESS; } @@ -724,7 +724,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) goto auth_fail; } - if (rtw_access_ctrl(padapter, sa) == false) { + if (!rtw_access_ctrl(padapter, sa)) { status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto auth_fail; } @@ -747,7 +747,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) } else { spin_lock_bh(&pstapriv->asoc_list_lock); - if (list_empty(&pstat->asoc_list) == false) { + if (!list_empty(&pstat->asoc_list)) { list_del_init(&pstat->asoc_list); pstapriv->asoc_list_cnt--; if (pstat->expire_to > 0) { @@ -1229,7 +1229,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) } else pstat->flags &= ~WLAN_STA_HT; - if ((pmlmepriv->htpriv.ht_option == false) && (pstat->flags&WLAN_STA_HT)) { + if (!pmlmepriv->htpriv.ht_option && (pstat->flags&WLAN_STA_HT)) { status = WLAN_STATUS_CHALLENGE_FAIL; goto OnAssocReqFail; } @@ -1461,7 +1461,7 @@ unsigned int OnDeAuth(struct adapter *padapter, union recv_frame *precv_frame) u8 updated = false; spin_lock_bh(&pstapriv->asoc_list_lock); - if (list_empty(&psta->asoc_list) == false) { + if (!list_empty(&psta->asoc_list)) { list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, false, reason); @@ -1532,7 +1532,7 @@ unsigned int OnDisassoc(struct adapter *padapter, union recv_frame *precv_frame) u8 updated = false; spin_lock_bh(&pstapriv->asoc_list_lock); - if (list_empty(&psta->asoc_list) == false) { + if (!list_empty(&psta->asoc_list)) { list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, false, reason); @@ -4909,7 +4909,7 @@ void linked_status_chk(struct adapter *padapter) /* todo: To check why we under miracast session, rx_chk would be false */ psta = rtw_get_stainfo(pstapriv, pmlmeinfo->network.mac_address); if (psta) { - if (chk_ap_is_alive(padapter, psta) == false) + if (!chk_ap_is_alive(padapter, psta)) rx_chk = _FAIL; if (pxmitpriv->last_tx_pkts == pxmitpriv->tx_pkts) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index e11127992cfd..098a08c11fe5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -373,7 +373,7 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p /* psecuritypriv->dot118021XGrpKeyid, pmlmeinfo->key_index, rxdata_key_idx); */ - if (psecuritypriv->binstallGrpkey == false) { + if (!psecuritypriv->binstallGrpkey) { res = _FAIL; goto exit; } @@ -396,10 +396,10 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p bmic_err = true; } - if (bmic_err == true) { + if (bmic_err) { /* double check key_index for some timing issue , */ /* cannot compare with psecuritypriv->dot118021XGrpKeyid also cause timing issue */ - if ((is_multicast_ether_addr(prxattrib->ra) == true) && (prxattrib->key_index != pmlmeinfo->key_index)) + if (is_multicast_ether_addr(prxattrib->ra) && (prxattrib->key_index != pmlmeinfo->key_index)) brpt_micerror = false; if (prxattrib->bdecrypted && brpt_micerror) @@ -453,7 +453,7 @@ static union recv_frame *decryptor(struct adapter *padapter, union recv_frame *p } } - if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0) || (psecuritypriv->sw_decrypt == true))) { + if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0) || psecuritypriv->sw_decrypt)) { psecuritypriv->hw_decrypted = false; switch (prxattrib->encrypt) { @@ -703,8 +703,8 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * u8 *sta_addr = NULL; signed int bmcast = is_multicast_ether_addr(pattrib->dst); - if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) || - (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { /* filter packets that SA is myself or multicast or broadcast */ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) { @@ -726,7 +726,7 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * sta_addr = pattrib->src; - } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { /* For Station mode, sa and bssid should always be BSSID, and DA is my mac-address */ if (memcmp(pattrib->bssid, pattrib->src, ETH_ALEN)) { ret = _FAIL; @@ -734,7 +734,7 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * } sta_addr = pattrib->bssid; - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { if (bmcast) { /* For AP mode, if DA == MCAST, then BSSID should be also MCAST */ if (!is_multicast_ether_addr(pattrib->bssid)) { @@ -751,7 +751,7 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * sta_addr = pattrib->src; } - } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); @@ -788,10 +788,9 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p u8 *myhwaddr = myid(&adapter->eeprompriv); signed int bmcast = is_multicast_ether_addr(pattrib->dst); - if ((check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) && - (check_fwstate(pmlmepriv, _FW_LINKED) == true || - check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) - ) { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) && + (check_fwstate(pmlmepriv, _FW_LINKED) || + check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) { /* filter packets that SA is myself or multicast or broadcast */ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) { @@ -834,8 +833,8 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p goto exit; } - } else if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) && - (check_fwstate(pmlmepriv, _FW_LINKED) == true)) { + } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) && + check_fwstate(pmlmepriv, _FW_LINKED)) { memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); @@ -851,7 +850,7 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p goto exit; } - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* Special case */ ret = RTW_RX_HANDLED; goto exit; @@ -888,7 +887,7 @@ static signed int sta2ap_data_frame(struct adapter *adapter, union recv_frame *p unsigned char *mybssid = get_bssid(pmlmepriv); signed int ret = _SUCCESS; - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* For AP mode, RA =BSSID, TX =STA(SRC_ADDR), A3 =DST_ADDR */ if (memcmp(pattrib->bssid, mybssid, ETH_ALEN)) { ret = _FAIL; @@ -1392,7 +1391,7 @@ static signed int validate_80211w_mgmt(struct adapter *adapter, union recv_frame /* only support station mode */ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) && check_fwstate(pmlmepriv, _FW_LINKED) && - adapter->securitypriv.binstallBIPkey == true) { + adapter->securitypriv.binstallBIPkey) { /* unicast management frame decrypt */ if (pattrib->privacy && !(is_multicast_ether_addr(GetAddr1Ptr(ptr))) && (subtype == WIFI_DEAUTH || subtype == WIFI_DISASSOC || subtype == WIFI_ACTION)) { @@ -1576,7 +1575,7 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe) eth_type = ntohs(be_tmp); /* pattrib->ether_type */ pattrib->eth_type = eth_type; - if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)) { + if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { ptr += rmv_len; *ptr = 0x87; *(ptr + 1) = 0x12; @@ -1651,7 +1650,7 @@ static void rtw_recv_indicate_pkt(struct adapter *padapter, struct sk_buff *pkt, /* Indicate the packets to upper layer */ if (pkt) { - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { struct sk_buff *pskb2 = NULL; struct sta_info *psta = NULL; struct sta_priv *pstapriv = &padapter->stapriv; @@ -1892,7 +1891,7 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor plist = get_next(phead); /* Handling some condition for forced indicate case. */ - if (bforced == true) { + if (bforced) { if (list_empty(phead)) { /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); */ /* spin_unlock(&ppending_recvframe_queue->lock); */ @@ -1927,8 +1926,8 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor /* indicate this recv_frame */ if (!pattrib->amsdu) { - if ((padapter->bDriverStopped == false) && - (padapter->bSurpriseRemoved == false)) + if (!padapter->bDriverStopped && + !padapter->bSurpriseRemoved) rtw_recv_indicatepkt(padapter, prframe);/* indicate this recv_frame */ } else if (pattrib->amsdu == 1) { @@ -1967,8 +1966,8 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * wlanhdr_to_ethhdr(prframe); if (pattrib->qos != 1) { - if ((padapter->bDriverStopped == false) && - (padapter->bSurpriseRemoved == false)) { + if (!padapter->bDriverStopped && + !padapter->bSurpriseRemoved) { rtw_recv_indicatepkt(padapter, prframe); return _SUCCESS; @@ -1978,7 +1977,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * } - if (preorder_ctrl->enable == false) { + if (!preorder_ctrl->enable) { /* indicate this recv_frame */ preorder_ctrl->indicate_seq = pattrib->seq_num; @@ -1989,7 +1988,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * return _SUCCESS; } } else if (pattrib->amsdu == 1) { /* temp filter -> means didn't support A-MSDUs in a A-MPDU */ - if (preorder_ctrl->enable == false) { + if (!preorder_ctrl->enable) { preorder_ctrl->indicate_seq = pattrib->seq_num; retval = amsdu_to_msdu(padapter, prframe); @@ -2024,7 +2023,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * /* */ /* recv_indicatepkts_in_order(padapter, preorder_ctrl, true); */ - if (recv_indicatepkts_in_order(padapter, preorder_ctrl, false) == true) { + if (recv_indicatepkts_in_order(padapter, preorder_ctrl, false)) { _set_timer(&preorder_ctrl->reordering_ctrl_timer, REORDER_WAIT_TIME); spin_unlock_bh(&ppending_recvframe_queue->lock); } else { @@ -2052,7 +2051,7 @@ void rtw_reordering_ctrl_timeout_handler(struct timer_list *t) spin_lock_bh(&ppending_recvframe_queue->lock); - if (recv_indicatepkts_in_order(padapter, preorder_ctrl, true) == true) + if (recv_indicatepkts_in_order(padapter, preorder_ctrl, true)) _set_timer(&preorder_ctrl->reordering_ctrl_timer, REORDER_WAIT_TIME); spin_unlock_bh(&ppending_recvframe_queue->lock); @@ -2067,13 +2066,13 @@ static int process_recv_indicatepkts(struct adapter *padapter, union recv_frame struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct ht_priv *phtpriv = &pmlmepriv->htpriv; - if (phtpriv->ht_option == true) { /* B/G/N Mode */ + if (phtpriv->ht_option) { /* B/G/N Mode */ /* prframe->u.hdr.preorder_ctrl = &precvpriv->recvreorder_ctrl[pattrib->priority]; */ if (recv_indicatepkt_reorder(padapter, prframe) != _SUCCESS) { /* including perform A-MPDU Rx Ordering Buffer Control */ - if ((padapter->bDriverStopped == false) && - (padapter->bSurpriseRemoved == false)) { + if (!padapter->bDriverStopped && + !padapter->bSurpriseRemoved) { retval = _FAIL; return retval; } @@ -2083,7 +2082,7 @@ static int process_recv_indicatepkts(struct adapter *padapter, union recv_frame if (retval != _SUCCESS) return retval; - if ((padapter->bDriverStopped == false) && (padapter->bSurpriseRemoved == false)) { + if (!padapter->bDriverStopped && !padapter->bSurpriseRemoved) { /* indicate this recv_frame */ rtw_recv_indicatepkt(padapter, prframe); } else { @@ -2172,7 +2171,7 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) /* check if need to enqueue into uc_swdec_pending_queue*/ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) && !is_multicast_ether_addr(prxattrib->ra) && prxattrib->encrypt > 0 && - (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt == true) && + (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt) && psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPAPSK && !psecuritypriv->busetkipkey) { rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue); @@ -2258,8 +2257,8 @@ static void rtw_signal_stat_timer_hdl(struct timer_list *t) } } - if (check_fwstate(&adapter->mlmepriv, _FW_UNDER_SURVEY) == true || - check_fwstate(&adapter->mlmepriv, _FW_LINKED) == false + if (check_fwstate(&adapter->mlmepriv, _FW_UNDER_SURVEY) || + !check_fwstate(&adapter->mlmepriv, _FW_LINKED) ) { goto set_timer; } diff --git a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c index 1e5bc9f4cd4b..a1b7fe843979 100644 --- a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c +++ b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c @@ -530,7 +530,7 @@ u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr) paclnode = list_entry(plist, struct rtw_wlan_acl_node, list); if (!memcmp(paclnode->addr, mac_addr, ETH_ALEN)) - if (paclnode->valid == true) { + if (paclnode->valid) { match = true; break; } diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index cc1a7497764c..4f41f88908d7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -147,7 +147,7 @@ unsigned int ratetbl2rateset(struct adapter *padapter, unsigned char *rateset) default: rate = ratetbl_val_2wifirate(rate); - if (is_basicrate(padapter, rate) == true) + if (is_basicrate(padapter, rate)) rate |= IEEE80211_BASIC_RATE_MASK; rateset[len] = rate; @@ -236,7 +236,7 @@ void Restore_DM_Func_Flag(struct adapter *padapter) void Switch_DM_Func(struct adapter *padapter, u32 mode, u8 enable) { - if (enable == true) + if (enable) rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_SET, (u8 *)(&mode)); else rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_CLR, (u8 *)(&mode)); @@ -846,7 +846,7 @@ static void bwmode_update_check(struct adapter *padapter, struct ndis_80211_var_ if (!pIE) return; - if (phtpriv->ht_option == false) + if (!phtpriv->ht_option) return; if (pIE->length > sizeof(struct HT_info_element)) @@ -930,7 +930,7 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) if (!pIE) return; - if (phtpriv->ht_option == false) + if (!phtpriv->ht_option) return; pmlmeinfo->HT_caps_enable = 1; @@ -993,7 +993,7 @@ void HT_info_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) if (!pIE) return; - if (phtpriv->ht_option == false) + if (!phtpriv->ht_option) return; if (pIE->length > sizeof(struct HT_info_element)) @@ -1121,7 +1121,7 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len) struct mlme_priv *pmlmepriv = &Adapter->mlmepriv; int ssid_len; - if (is_client_associated_to_ap(Adapter) == false) + if (!is_client_associated_to_ap(Adapter)) return true; len = packet_len - sizeof(struct ieee80211_hdr_3addr); @@ -1688,7 +1688,7 @@ void adaptive_early_32k(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len) pmlmeext->bcn_delay_cnt[delay_ms]++; /* pmlmeext->bcn_delay_ratio[delay_ms] = (pmlmeext->bcn_delay_cnt[delay_ms] * 100) /pmlmeext->bcn_cnt; */ /* dump for adaptive_early_32k */ - if (pmlmeext->bcn_cnt > 100 && (pmlmeext->adaptive_tsf_done == true)) { + if (pmlmeext->bcn_cnt > 100 && pmlmeext->adaptive_tsf_done) { u8 ratio_20_delay, ratio_80_delay; u8 DrvBcnEarly, DrvBcnTimeOut; @@ -1736,7 +1736,7 @@ void rtw_alloc_macid(struct adapter *padapter, struct sta_info *psta) spin_lock_bh(&pdvobj->lock); for (i = 0; i < NUM_STA; i++) { - if (pdvobj->macid[i] == false) { + if (!pdvobj->macid[i]) { pdvobj->macid[i] = true; break; } @@ -1761,7 +1761,7 @@ void rtw_release_macid(struct adapter *padapter, struct sta_info *psta) spin_lock_bh(&pdvobj->lock); if (psta->mac_id < NUM_STA && psta->mac_id != 1) { - if (pdvobj->macid[psta->mac_id] == true) { + if (pdvobj->macid[psta->mac_id]) { pdvobj->macid[psta->mac_id] = false; psta->mac_id = NUM_STA; } diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index dba146b696e0..9ac8a8f74397 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -424,7 +424,7 @@ static void update_attrib_vcs_info(struct adapter *padapter, struct xmit_frame * } else { while (true) { /* IOT action */ - if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS) && (pattrib->ampdu_en == true) && + if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS) && pattrib->ampdu_en && (padapter->securitypriv.dot11PrivacyAlgrthm == _AES_)) { pattrib->vcs_mode = CTS_TO_SELF; break; @@ -460,7 +460,7 @@ static void update_attrib_vcs_info(struct adapter *padapter, struct xmit_frame * /* to do list: check MIMO power save condition. */ /* check AMPDU aggregation for TXOP */ - if (pattrib->ampdu_en == true) { + if (pattrib->ampdu_en) { pattrib->vcs_mode = RTS_CTS; break; } @@ -522,10 +522,10 @@ static s32 update_attrib_sec_info(struct adapter *padapter, struct pkt_attrib *p memset(pattrib->dot11tkiptxmickey.skey, 0, 16); pattrib->mac_id = psta->mac_id; - if (psta->ieee8021x_blocked == true) { + if (psta->ieee8021x_blocked) { pattrib->encrypt = 0; - if ((pattrib->ether_type != 0x888e) && (check_fwstate(pmlmepriv, WIFI_MP_STATE) == false)) { + if ((pattrib->ether_type != 0x888e) && !check_fwstate(pmlmepriv, WIFI_MP_STATE)) { res = _FAIL; goto exit; } @@ -690,8 +690,8 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p memcpy(pattrib->dst, ðerhdr.h_dest, ETH_ALEN); memcpy(pattrib->src, ðerhdr.h_source, ETH_ALEN); - if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) || - (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); memcpy(pattrib->ta, pattrib->src, ETH_ALEN); } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { @@ -757,7 +757,7 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p if (!psta) { /* if we cannot get psta => drop the pkt */ res = _FAIL; goto exit; - } else if ((check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) && (!(psta->state & _FW_LINKED))) { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) && !(psta->state & _FW_LINKED)) { res = _FAIL; goto exit; } @@ -939,7 +939,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr SetFrameSubType(fctrl, pattrib->subtype); if (pattrib->subtype & WIFI_DATA_TYPE) { - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { /* to_ds = 1, fr_ds = 0; */ { @@ -953,7 +953,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr if (pqospriv->qos_option) qos_option = true; - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* to_ds = 0, fr_ds = 1; */ SetFrDs(fctrl); memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); @@ -962,8 +962,8 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr if (pattrib->qos_en) qos_option = true; - } else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) || - (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) { + } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), ETH_ALEN); @@ -1021,7 +1021,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr pattrib->ampdu_en = true; /* re-check if enable ampdu by BA_starting_seqctrl */ - if (pattrib->ampdu_en == true) { + if (pattrib->ampdu_en) { u16 tx_seq; tx_seq = psta->BA_starting_seqctrl[pattrib->priority & 0x0f]; @@ -1178,7 +1178,7 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct frg_inx++; - if (bmcst || (rtw_endofpktfile(&pktfile) == true)) { + if (bmcst || rtw_endofpktfile(&pktfile)) { pattrib->nr_frags = frg_inx; pattrib->last_txcmdsz = pattrib->hdrlen + pattrib->iv_len + @@ -1204,7 +1204,7 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct xmitframe_swencrypt(padapter, pxmitframe); - if (bmcst == false) + if (!bmcst) update_attrib_vcs_info(padapter, pxmitframe); else pattrib->vcs_mode = NONE_VCS; @@ -2001,14 +2001,14 @@ s32 rtw_xmit(struct adapter *padapter, struct sk_buff **ppkt) do_queue_select(padapter, &pxmitframe->attrib); spin_lock_bh(&pxmitpriv->lock); - if (xmitframe_enqueue_for_sleeping_sta(padapter, pxmitframe) == true) { + if (xmitframe_enqueue_for_sleeping_sta(padapter, pxmitframe)) { spin_unlock_bh(&pxmitpriv->lock); return 1; } spin_unlock_bh(&pxmitpriv->lock); /* pre_xmitframe */ - if (rtw_hal_xmit(padapter, pxmitframe) == false) + if (!rtw_hal_xmit(padapter, pxmitframe)) return 1; return 0; @@ -2052,7 +2052,7 @@ signed int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct x signed int bmcst = is_multicast_ether_addr(pattrib->ra); bool update_tim = false; - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == false) + if (!check_fwstate(pmlmepriv, WIFI_AP_STATE)) return ret; psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra); if (pattrib->psta != psta) From 577ae917d4afc1c934d2f7c29a886dc8cb19aceb Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:40 +0300 Subject: [PATCH 0407/5214] staging: rtl8723bs: hal: simplify boolean comparisons Simplify boolean comparisons to improve readability and conform to the Linux kernel coding style. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-3-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 10 ++++---- drivers/staging/rtl8723bs/hal/hal_com.c | 4 ++-- .../staging/rtl8723bs/hal/hal_com_phycfg.c | 4 ++-- drivers/staging/rtl8723bs/hal/hal_intf.c | 4 ++-- drivers/staging/rtl8723bs/hal/odm.c | 8 +++---- .../staging/rtl8723bs/hal/odm_CfoTracking.c | 2 +- drivers/staging/rtl8723bs/hal/odm_DIG.c | 24 +++++++++---------- drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723b_dm.c | 12 +++++----- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 12 +++++----- .../staging/rtl8723bs/hal/rtl8723bs_recv.c | 2 +- .../staging/rtl8723bs/hal/rtl8723bs_xmit.c | 8 +++---- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 2 +- 13 files changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 81d25a4404c1..735321624e1b 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -164,8 +164,8 @@ static u8 halbtcoutsrc_IsWifiBusy(struct adapter *padapter) pmlmepriv = &padapter->mlmepriv; - if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == true) { - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE)) { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) return true; if (pmlmepriv->link_detect_info.busy_traffic) return true; @@ -185,8 +185,8 @@ static u32 _halbtcoutsrc_GetWifiLinkStatus(struct adapter *padapter) bp2p = false; portConnectedStatus = 0; - if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == true) { - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE)) { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { if (bp2p) portConnectedStatus |= WIFI_P2P_GO_CONNECTED; else @@ -528,7 +528,7 @@ static u8 halbtcoutsrc_Set(void *pBtcContext, u8 setType, void *pInBuf) case BTC_SET_ACT_UPDATE_RAMASK: pBtCoexist->btInfo.raMask = *pU4Tmp; - if (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE) == true) { + if (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE)) { struct sta_info *psta; struct wlan_bssid_ex *cur_network; diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index b4b135a9ca22..7fd6e0249390 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -94,7 +94,7 @@ bool HAL_IsLegalChannel(struct adapter *adapter, u32 Channel) bool bLegalChannel = true; if ((Channel <= 14) && (Channel >= 1)) { - if (is_supported_24g(adapter->registrypriv.wireless_mode) == false) + if (!is_supported_24g(adapter->registrypriv.wireless_mode)) bLegalChannel = false; } else { bLegalChannel = false; @@ -563,7 +563,7 @@ void SetHwReg(struct adapter *adapter, u8 variable, u8 *val) odm->SupportAbility = *((u32 *)val); break; case HW_VAR_DM_FUNC_OP: - if (*((u8 *)val) == true) { + if (*((u8 *)val)) { /* save dm flag */ odm->BK_SupportAbility = odm->SupportAbility; } else { diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index 45dbe1782bae..1e5669b17d0f 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -461,7 +461,7 @@ u8 PHY_GetTxPowerIndexBase( u8 txPower = 0; u8 chnlIdx = (Channel-1); - if (HAL_IsLegalChannel(padapter, Channel) == false) + if (!HAL_IsLegalChannel(padapter, Channel)) chnlIdx = 0; if (IS_CCK_RATE(Rate)) @@ -489,7 +489,7 @@ s8 PHY_GetTxPowerTrackingOffset(struct adapter *padapter, u8 RFPath, u8 Rate) struct dm_odm_t *pDM_Odm = &pHalData->odmpriv; s8 offset = 0; - if (pDM_Odm->RFCalibrateInfo.TxPowerTrackControl == false) + if (!pDM_Odm->RFCalibrateInfo.TxPowerTrackControl) return offset; if ((Rate == MGN_1M) || (Rate == MGN_2M) || (Rate == MGN_5_5M) || (Rate == MGN_11M)) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index f75b8a8192b8..03f23b75d532 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -119,7 +119,7 @@ s32 rtw_hal_mgnt_xmit(struct adapter *padapter, struct xmit_frame *pmgntframe) /* pwlanhdr = (struct rtw_ieee80211_hdr *)pframe; */ /* memcpy(pmgntframe->attrib.ra, pwlanhdr->addr1, ETH_ALEN); */ - if (padapter->securitypriv.binstallBIPkey == true) { + if (padapter->securitypriv.binstallBIPkey) { if (is_multicast_ether_addr(pmgntframe->attrib.ra)) { pmgntframe->attrib.encrypt = _BIP_; /* pmgntframe->attrib.bswenc = true; */ @@ -165,7 +165,7 @@ void rtw_hal_update_ra_mask(struct sta_info *psta, u8 rssi_level) pmlmepriv = &(padapter->mlmepriv); - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) add_ratid(padapter, psta, rssi_level); else { UpdateHalRAMask8723B(padapter, psta->mac_id, rssi_level); diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index 3dffa7620768..b01e9016e30c 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -381,7 +381,7 @@ static void FindMinimumRSSI(struct adapter *padapter) /* 1 1.Determine the minimum RSSI */ if ( - (pDM_Odm->bLinked != true) && + !pDM_Odm->bLinked && (pdmpriv->EntryMinUndecoratedSmoothedPWDB == 0) ) { pdmpriv->MinUndecoratedPWDBForDM = 0; @@ -400,7 +400,7 @@ static void odm_RSSIMonitorCheckCE(struct dm_odm_t *pDM_Odm) u32 PWDB_rssi[NUM_STA] = {0};/* 0~15]:MACID, [16~31]:PWDB_rssi */ struct ra_t *pRA_Table = &pDM_Odm->DM_RA_Table; - if (pDM_Odm->bLinked != true) + if (!pDM_Odm->bLinked) return; pRA_Table->firstconnect = pDM_Odm->bLinked; @@ -431,7 +431,7 @@ static void odm_RSSIMonitorCheckCE(struct dm_odm_t *pDM_Odm) for (i = 0; i < sta_cnt; i++) { if (PWDB_rssi[i] != (0)) { - if (pHalData->fw_ractrl == true)/* Report every sta's RSSI to FW */ + if (pHalData->fw_ractrl)/* Report every sta's RSSI to FW */ rtl8723b_set_rssi_cmd(Adapter, (u8 *)(&PWDB_rssi[i])); } } @@ -623,7 +623,7 @@ void ODM_DMWatchdog(struct dm_odm_t *pDM_Odm) } odm_CCKPacketDetectionThresh(pDM_Odm); - if (*(pDM_Odm->pbPowerSaving) == true) + if (*pDM_Odm->pbPowerSaving) return; diff --git a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c index 0eaedd8f6469..7e6b49399abb 100644 --- a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c +++ b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c @@ -138,7 +138,7 @@ void ODM_CfoTracking(void *pDM_VOID) pCfoTrack->CFO_ave_pre = CFO_ave; /* 4 1.4 Dynamic Xtal threshold */ - if (pCfoTrack->bAdjust == false) { + if (!pCfoTrack->bAdjust) { if (CFO_ave > CFO_TH_XTAL_HIGH || CFO_ave < (-CFO_TH_XTAL_HIGH)) pCfoTrack->bAdjust = true; } else { diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index a31c8368f9d9..426989986cc3 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -82,7 +82,7 @@ void odm_NHMBB(void *pDM_VOID) if ((pDM_Odm->NHMCurTxOkcnt) + 1 > (u64)(pDM_Odm->NHMCurRxOkcnt<<2) + 1) { /* Tx > 4*Rx possible for adaptivity test */ - if (pDM_Odm->NHM_cnt_0 >= 190 || pDM_Odm->adaptivity_flag == true) { + if (pDM_Odm->NHM_cnt_0 >= 190 || pDM_Odm->adaptivity_flag) { /* Enable EDCCA since it is possible running Adaptivity testing */ /* test_status = 1; */ pDM_Odm->adaptivity_flag = true; @@ -99,7 +99,7 @@ void odm_NHMBB(void *pDM_VOID) } } } else { /* TXadaptivity_flag == true && pDM_Odm->NHM_cnt_0 <= 200) { + if (pDM_Odm->adaptivity_flag && pDM_Odm->NHM_cnt_0 <= 200) { /* test_status = 2; */ pDM_Odm->tolerance_cnt = 0; } else { @@ -189,7 +189,7 @@ void odm_AdaptivityInit(void *pDM_VOID) { struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; - if (pDM_Odm->Carrier_Sense_enable == false) + if (!pDM_Odm->Carrier_Sense_enable) pDM_Odm->TH_L2H_ini = 0xf7; /* -7 */ else pDM_Odm->TH_L2H_ini = 0xa; @@ -232,7 +232,7 @@ void odm_Adaptivity(void *pDM_VOID, u8 IGI) pDM_Odm->IGI_target = (u8) IGI_target; /* Search pwdB lower bound */ - if (pDM_Odm->TxHangFlg == true) { + if (pDM_Odm->TxHangFlg) { PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_DBG_RPT_11N, bMaskDWord, 0x208); odm_SearchPwdBLowerBound(pDM_Odm, pDM_Odm->IGI_target); } @@ -253,9 +253,9 @@ void odm_Adaptivity(void *pDM_VOID, u8 IGI) if ( pDM_Odm->bLinked && - pDM_Odm->Carrier_Sense_enable == false && - pDM_Odm->NHM_disable == false && - pDM_Odm->TxHangFlg == false + !pDM_Odm->Carrier_Sense_enable && + !pDM_Odm->NHM_disable && + !pDM_Odm->TxHangFlg ) odm_NHMBB(pDM_Odm); @@ -323,7 +323,7 @@ bool odm_DigAbort(void *pDM_VOID) return true; /* add by Neil Chen to avoid PSD is processing */ - if (pDM_Odm->bDMInitialGainEnable == false) + if (!pDM_Odm->bDMInitialGainEnable) return true; return false; @@ -387,14 +387,14 @@ void odm_DIG(void *pDM_VOID) if (odm_DigAbort(pDM_Odm)) return; - if (pDM_Odm->adaptivity_flag == true) + if (pDM_Odm->adaptivity_flag) Adap_IGI_Upper = pDM_Odm->Adaptivity_IGI_upper; /* 1 Update status */ DIG_Dynamic_MIN = pDM_DigTable->DIG_Dynamic_MIN_0; - FirstConnect = (pDM_Odm->bLinked) && (pDM_DigTable->bMediaConnect_0 == false); - FirstDisConnect = (!pDM_Odm->bLinked) && (pDM_DigTable->bMediaConnect_0 == true); + FirstConnect = (pDM_Odm->bLinked) && !pDM_DigTable->bMediaConnect_0; + FirstDisConnect = (!pDM_Odm->bLinked) && pDM_DigTable->bMediaConnect_0; /* 1 Boundary Decision */ /* 2 For WIN\CE */ @@ -529,7 +529,7 @@ void odm_DIG(void *pDM_VOID) /* 1 Force upper bound and lower bound for adaptivity */ if ( pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY && - pDM_Odm->adaptivity_flag == true + pDM_Odm->adaptivity_flag ) { if (CurrentIGI > Adap_IGI_Upper) CurrentIGI = Adap_IGI_Upper; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index 4bdc8e314015..6c9337179645 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -359,7 +359,7 @@ void rtl8723b_set_FwPwrMode_cmd(struct adapter *padapter, u8 psmode) } if (psmode > 0) { - if (hal_btcoex_IsBtControlLps(padapter) == true) { + if (hal_btcoex_IsBtControlLps(padapter)) { PowerState = hal_btcoex_RpwmVal(padapter); byte5 = hal_btcoex_LpsVal(padapter); diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c index 928226679ab4..5cb13a10efd6 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c @@ -131,21 +131,21 @@ void rtl8723b_HalDmWatchDog(struct adapter *Adapter) hw_init_completed = Adapter->hw_init_completed; - if (hw_init_completed == false) + if (!hw_init_completed) goto skip_dm; fw_current_in_ps_mode = adapter_to_pwrctl(Adapter)->fw_current_in_ps_mode; rtw_hal_get_hwreg(Adapter, HW_VAR_FWLPS_RF_ON, (u8 *)(&bFwPSAwake)); if ( - (hw_init_completed == true) && - ((!fw_current_in_ps_mode) && bFwPSAwake) + hw_init_completed && + (!fw_current_in_ps_mode && bFwPSAwake) ) { rtw_hal_check_rxfifo_full(Adapter); } /* ODM */ - if (hw_init_completed == true) { + if (hw_init_completed) { u8 bLinked = false; u8 bsta_state = false; bool bBtDisabled = true; @@ -207,7 +207,7 @@ void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *Adapter) struct sta_priv *pstapriv = &Adapter->stapriv; struct sta_info *psta = NULL; - if (Adapter->hw_init_completed == false) + if (!Adapter->hw_init_completed) goto skip_lps_dm; @@ -216,7 +216,7 @@ void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *Adapter) ODM_CmnInfoUpdate(&pHalData->odmpriv, ODM_CMNINFO_LINK, bLinked); - if (bLinked == false) + if (!bLinked) goto skip_lps_dm; if (!(pDM_Odm->SupportAbility & ODM_BB_RSSI_MONITOR)) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 9a91fd0a96eb..aa469fe57ba8 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -738,7 +738,7 @@ static void hal_ReadEFuse_BT( Hal_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_AVAILABLE_EFUSE_BYTES_BANK, &total); for (bank = 1; bank < 3; bank++) { /* 8723b Max bake 0~2 */ - if (hal_EfuseSwitchToBank(padapter, bank) == false) + if (!hal_EfuseSwitchToBank(padapter, bank)) goto exit; eFuse_Addr = 0; @@ -882,7 +882,7 @@ void rtl8723b_InitBeaconParameters(struct adapter *padapter) rtw_write16(padapter, REG_TBTT_PROHIBIT, 0x6404);/* ms */ /* Firmware will control REG_DRVERLYINT when power saving is enable, */ /* so don't set this register on STA mode. */ - if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) == false) + if (!check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE)) rtw_write8(padapter, REG_DRVERLYINT, DRIVER_EARLY_INT_TIME_8723B); /* 5ms */ rtw_write8(padapter, REG_BCNDMATIM, BCN_DMA_ATIME_INT_TIME_8723B); /* 2ms */ @@ -998,7 +998,7 @@ void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) rtw_write32(padapter, REG_TCR, value32); /* NOTE: Fix test chip's bug (about contention windows's randomness) */ - if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE) == true) { + if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) { rtw_write8(padapter, REG_RXTSF_OFFSET_CCK, 0x50); rtw_write8(padapter, REG_RXTSF_OFFSET_OFDM, 0x50); } @@ -2119,7 +2119,7 @@ static void hw_var_set_mlme_sitesurvey(struct adapter *padapter, u8 variable, u8 /* config RCR to receive different BSSID & not to receive data frame */ value_rxfltmap2 = 0; - if ((check_fwstate(pmlmepriv, WIFI_AP_STATE) == true)) + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) rcr_clear_bit = RCR_CBSSID_BCN; value_rcr = rtw_read32(padapter, REG_RCR); @@ -2186,7 +2186,7 @@ static void hw_var_set_mlme_join(struct adapter *padapter, u8 variable, u8 *val) val32 |= RCR_CBSSID_DATA|RCR_CBSSID_BCN; rtw_write32(padapter, REG_RCR, val32); - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) RetryLimit = (pEEPROM->CustomerID == RT_CID_CCX) ? 7 : 48; else /* Ad-hoc Mode */ RetryLimit = 0x7; @@ -2724,7 +2724,7 @@ void SetHwReg8723B(struct adapter *padapter, u8 variable, u8 *val) break; case HW_VAR_DL_RSVD_PAGE: - if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE)) rtl8723b_download_BTCoex_AP_mode_rsvd_page(padapter); else rtl8723b_download_rsvd_page(padapter, RT_MEDIA_CONNECT); diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c index cab4707091e2..888029e21dcb 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c @@ -135,7 +135,7 @@ static void update_recvframe_phyinfo(union recv_frame *precvframe, precvframe->u.hdr.psta = NULL; if ( pkt_info.bssid_match && - (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true) + (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE)) ) { if (psta) { precvframe->u.hdr.psta = psta; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index ff39077deb69..705becfe6043 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -93,7 +93,7 @@ static s32 rtl8723_dequeue_writeport(struct adapter *padapter) ) goto free_xmitbuf; - if (rtw_sdio_wait_enough_TxOQT_space(padapter, pxmitbuf->agg_num) == false) + if (!rtw_sdio_wait_enough_TxOQT_space(padapter, pxmitbuf->agg_num)) goto free_xmitbuf; traffic_check_for_leave_lps(padapter, true, pxmitbuf->agg_num); @@ -225,7 +225,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv frame_phead = get_list_head(pframe_queue); - while (list_empty(frame_phead) == false) { + while (!list_empty(frame_phead)) { frame_plist = get_next(frame_phead); pxmitframe = container_of(frame_plist, struct xmit_frame, list); @@ -269,7 +269,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv } /* ok to send, remove frame from queue */ - if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE)) if ( (pxmitframe->attrib.psta->state & WIFI_SLEEP_STATE) && (pxmitframe->attrib.triggered == 0) @@ -566,7 +566,7 @@ void rtl8723bs_free_xmit_priv(struct adapter *padapter) spin_unlock_bh(&pqueue->lock); phead = &tmplist; - while (list_empty(phead) == false) { + while (!list_empty(phead)) { plist = get_next(phead); list_del_init(plist); diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index b3366830edbb..f27970ef54c8 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -584,7 +584,7 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) pwrctrlpriv = adapter_to_pwrctl(padapter); if ( - adapter_to_pwrctl(padapter)->bips_processing == true && + adapter_to_pwrctl(padapter)->bips_processing && adapter_to_pwrctl(padapter)->pre_ips_type == 0 ) { unsigned long start_time; From fa74eafdceb722c7987fde7f9d2e74105987c73a Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:41 +0300 Subject: [PATCH 0408/5214] staging: rtl8723bs: os_dep: simplify boolean comparisons Simplify boolean comparisons to improve readability and conform to the Linux kernel coding style. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-4-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 52 +++++++++---------- .../staging/rtl8723bs/os_dep/sdio_ops_linux.c | 4 +- drivers/staging/rtl8723bs/os_dep/xmit_linux.c | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 1a2cce218f40..633a42b4a157 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -275,7 +275,7 @@ struct cfg80211_bss *rtw_cfg80211_inform_bss(struct adapter *padapter, struct wl notify_timestamp = ktime_to_us(ktime_get_boottime()); /* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */ - if (check_fwstate(pmlmepriv, _FW_LINKED) == true && + if (check_fwstate(pmlmepriv, _FW_LINKED) && is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network, 0)) { notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength);/* dbm */ } else { @@ -372,7 +372,7 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) struct wlan_bssid_ex *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network); struct wlan_network *scanned = pmlmepriv->cur_network_scanned; - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { memcpy(&cur_network->network, pnetwork, sizeof(struct wlan_bssid_ex)); rtw_cfg80211_inform_bss(padapter, cur_network); } else { @@ -410,7 +410,7 @@ void rtw_cfg80211_indicate_connect(struct adapter *padapter) return; } - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) return; { @@ -479,7 +479,7 @@ void rtw_cfg80211_indicate_disconnect(struct adapter *padapter) return; } - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) return; if (!padapter->mlmepriv.not_indic_disco) { @@ -768,7 +768,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param struct sta_info *psta, *pbcmc_sta; struct sta_priv *pstapriv = &padapter->stapriv; - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE) == true) { /* sta mode */ + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE)) { /* sta mode */ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); if (psta) { /* Jeff: don't disable ieee8021x_blocked while clearing key */ @@ -892,15 +892,15 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, memcpy(param->u.crypt.key, (u8 *)params->key, params->key_len); } - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { ret = rtw_cfg80211_set_encryption(ndev, param, param_len); - } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { if (mac_addr) memcpy(param->sta_addr, mac_addr, ETH_ALEN); ret = rtw_cfg80211_ap_set_encryption(ndev, param, param_len); - } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true - || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { + } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) + || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { ret = rtw_cfg80211_set_encryption(ndev, param, param_len); } @@ -1033,7 +1033,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); int ret = 0; - if (adapter_to_dvobj(padapter)->processing_dev_remove == true) { + if (adapter_to_dvobj(padapter)->processing_dev_remove) { ret = -EPERM; goto exit; } @@ -1074,7 +1074,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, rtw_wdev->iftype = type; - if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) { + if (!rtw_set_802_11_infrastructure_mode(padapter, networkType)) { rtw_wdev->iftype = old_type; ret = -EPERM; goto exit; @@ -1209,8 +1209,8 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy pwdev_priv->scan_request = request; spin_unlock_bh(&pwdev_priv->scan_req_lock); - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { - if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING) == true) { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { + if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; } @@ -1225,10 +1225,10 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy if (request->ie && request->ie_len > 0) rtw_cfg80211_set_probe_req_wpsp2pie(padapter, (u8 *)request->ie, request->ie_len); - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) { + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; - } else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) { + } else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { ret = -EBUSY; goto check_need_indicate_scan_done; } @@ -1284,7 +1284,7 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy } spin_unlock_bh(&pmlmepriv->lock); - if (_status == false) + if (!_status) ret = -1; check_need_indicate_scan_done: @@ -1584,7 +1584,7 @@ static int cfg80211_rtw_join_ibss(struct wiphy *wiphy, struct net_device *ndev, ret = rtw_cfg80211_set_auth_type(psecuritypriv, NL80211_AUTHTYPE_OPEN_SYSTEM); rtw_set_802_11_authentication_mode(padapter, psecuritypriv->ndisauthtype); - if (rtw_set_802_11_ssid(padapter, &ndis_ssid) == false) { + if (!rtw_set_802_11_ssid(padapter, &ndis_ssid)) { ret = -1; goto exit; } @@ -1610,7 +1610,7 @@ static int cfg80211_rtw_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) rtw_wdev->iftype = NL80211_IFTYPE_STATION; - if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) == false) { + if (!rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure)) { rtw_wdev->iftype = old_type; ret = -EPERM; goto leave_ibss; @@ -1634,7 +1634,7 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, padapter->mlmepriv.not_indic_disco = true; - if (adapter_wdev_data(padapter)->block == true) { + if (adapter_wdev_data(padapter)->block) { ret = -EBUSY; goto exit; } @@ -1664,11 +1664,11 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, ndis_ssid.ssid_length = sme->ssid_len; memcpy(ndis_ssid.ssid, (u8 *)sme->ssid, sme->ssid_len); - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) { + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { ret = -EBUSY; goto exit; } - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) rtw_scan_abort(padapter); psecuritypriv->ndisencryptstatus = Ndis802_11EncryptionDisabled; @@ -1761,7 +1761,7 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, /* rtw_set_802_11_encryption_mode(padapter, padapter->securitypriv.ndisencryptstatus); */ - if (rtw_set_802_11_connect(padapter, (u8 *)sme->bssid, &ndis_ssid) == false) { + if (!rtw_set_802_11_connect(padapter, (u8 *)sme->bssid, &ndis_ssid)) { ret = -1; goto exit; } @@ -2067,7 +2067,7 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc u32 len = skb->len; u8 category, action; - if (rtw_action_frame_parse(buf, len, &category, &action) == false) + if (!rtw_action_frame_parse(buf, len, &category, &action)) goto fail; /* starting alloc mgmt frame to dump it */ @@ -2249,7 +2249,7 @@ static int rtw_add_beacon(struct adapter *adapter, const u8 *head, size_t head_l uint len, wps_ielen = 0; struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) + if (!check_fwstate(pmlmepriv, WIFI_AP_STATE)) return -EINVAL; if (head_len < 24) @@ -2343,7 +2343,7 @@ static int cfg80211_rtw_del_station(struct wiphy *wiphy, struct sta_priv *pstapriv = &padapter->stapriv; const u8 *mac = params->mac; - if (check_fwstate(pmlmepriv, (_FW_LINKED | WIFI_AP_STATE)) != true) + if (!check_fwstate(pmlmepriv, _FW_LINKED | WIFI_AP_STATE)) return -EINVAL; if (!mac) { @@ -2549,7 +2549,7 @@ static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* indicate ack before issue frame to avoid racing with rsp frame */ rtw_cfg80211_mgmt_tx_status(padapter, *cookie, buf, len, ack, GFP_KERNEL); - if (rtw_action_frame_parse(buf, len, &category, &action) == false) + if (!rtw_action_frame_parse(buf, len, &category, &action)) goto exit; rtw_ps_deny(padapter, PS_DENY_MGNT_TX); diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c b/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c index 3ea9fdfa14f8..e9a2f3f7ec74 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c @@ -224,7 +224,7 @@ u32 sd_read32(struct intf_hdl *pintfhdl, u32 addr, s32 *err) if ((-ESHUTDOWN == *err) || (-ENODEV == *err)) padapter->bSurpriseRemoved = true; - if (rtw_inc_and_chk_continual_io_error(psdiodev) == true) { + if (rtw_inc_and_chk_continual_io_error(psdiodev)) { padapter->bSurpriseRemoved = true; break; } @@ -300,7 +300,7 @@ void sd_write32(struct intf_hdl *pintfhdl, u32 addr, u32 v, s32 *err) if ((-ESHUTDOWN == *err) || (-ENODEV == *err)) padapter->bSurpriseRemoved = true; - if (rtw_inc_and_chk_continual_io_error(psdiodev) == true) { + if (rtw_inc_and_chk_continual_io_error(psdiodev)) { padapter->bSurpriseRemoved = true; break; } diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index 54a6d54400e2..d5bb1c7932fc 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -175,7 +175,7 @@ void _rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev) struct mlme_priv *pmlmepriv = &padapter->mlmepriv; s32 res = 0; - if (rtw_if_up(padapter) == false) + if (!rtw_if_up(padapter)) goto drop_packet; rtw_check_xmit_resource(padapter, pkt); From 367966fd02fc8e231b5a097ad2fb0ff49c4be6a2 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:42 +0300 Subject: [PATCH 0409/5214] staging: rtl8723bs: add spaces around bitwise OR operators Add spaces around bitwise OR operators to improve code readability and conform to the Linux kernel coding style. This fixes the SPACING issues identified by checkpatch.pl. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-5-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index e47ede86836c..b3b9976b15b2 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -513,7 +513,7 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) return _SUCCESS; if (!check_fwstate(pmlmepriv, _FW_LINKED) && - !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) { + !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { return _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index aa469fe57ba8..f8ba55e22638 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -998,7 +998,7 @@ void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) rtw_write32(padapter, REG_TCR, value32); /* NOTE: Fix test chip's bug (about contention windows's randomness) */ - if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) { + if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { rtw_write8(padapter, REG_RXTSF_OFFSET_CCK, 0x50); rtw_write8(padapter, REG_RXTSF_OFFSET_OFDM, 0x50); } From 866fb5a3ac41fbcc8e250981c0a28a293c850d2b Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:43 +0300 Subject: [PATCH 0410/5214] staging: rtl8723bs: remove redundant braces for single-statement block Remove redundant curly braces for single-statement block to improve code readability and conform to the Linux kernel coding style. This fixes the BRACES issue identified by checkpatch.pl. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-6-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index b01e9016e30c..b4f872f4d6c9 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -380,12 +380,9 @@ static void FindMinimumRSSI(struct adapter *padapter) /* 1 1.Determine the minimum RSSI */ - if ( - !pDM_Odm->bLinked && - (pdmpriv->EntryMinUndecoratedSmoothedPWDB == 0) - ) { + if (!pDM_Odm->bLinked && (pdmpriv->EntryMinUndecoratedSmoothedPWDB == 0)) pdmpriv->MinUndecoratedPWDBForDM = 0; - } else + else pdmpriv->MinUndecoratedPWDBForDM = pdmpriv->EntryMinUndecoratedSmoothedPWDB; } From d893a67a64250e4b6c8451ce69b129db3a2747b6 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:44 +0300 Subject: [PATCH 0411/5214] staging: rtl8723bs: move logical operators to previous line Move logical operators to the previous line to improve code readability and conform to the Linux kernel coding style. This fixes the LOGICAL_CONTINUATIONS issues identified by checkpatch.pl. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-7-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ioctl_set.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_mlme.c | 4 ++-- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index ebf0808f4104..e5de66d00bec 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -467,8 +467,8 @@ u16 rtw_get_cur_max_rate(struct adapter *adapter) struct sta_info *psta = NULL; u8 short_GI = 0; - if (!check_fwstate(pmlmepriv, _FW_LINKED) - && !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) + if (!check_fwstate(pmlmepriv, _FW_LINKED) && + !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) return 0; psta = rtw_get_stainfo(&adapter->stapriv, get_bssid(pmlmepriv)); diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 14a6d0e2a3c1..3c4c7d902295 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1757,8 +1757,8 @@ static int rtw_check_join_candidate(struct mlme_priv *mlme goto exit; if (rtw_to_roam(adapter) > 0) { - if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= mlme->roam_scanr_exp_ms - || !is_same_ess(&competitor->network, &mlme->cur_network.network) + if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= mlme->roam_scanr_exp_ms || + !is_same_ess(&competitor->network, &mlme->cur_network.network) ) goto exit; } diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 633a42b4a157..9cc5199753c2 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -899,8 +899,8 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, memcpy(param->sta_addr, mac_addr, ETH_ALEN); ret = rtw_cfg80211_ap_set_encryption(ndev, param, param_len); - } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) - || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { + } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { ret = rtw_cfg80211_set_encryption(ndev, param, param_len); } From 95f63d435590e1abeddedaf1c5a9b0768a0a1d99 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:45 +0300 Subject: [PATCH 0412/5214] staging: rtl8723bs: fix alignment of continued conditions Fix alignment of continued conditions to improve code readability and conform to the Linux kernel coding style. This fixes the PARENTHESIS_ALIGNMENT issues identified by checkpatch.pl. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-8-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ioctl_set.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_mlme.c | 2 +- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- drivers/staging/rtl8723bs/core/rtw_recv.c | 12 ++++++------ drivers/staging/rtl8723bs/core/rtw_xmit.c | 4 ++-- drivers/staging/rtl8723bs/hal/odm_DIG.c | 16 ++++++---------- drivers/staging/rtl8723bs/hal/rtl8723b_dm.c | 6 ++---- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 6 ++---- 8 files changed, 22 insertions(+), 30 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index e5de66d00bec..12bf7780bea5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -298,7 +298,7 @@ u8 rtw_set_802_11_infrastructure_mode(struct adapter *padapter, rtw_disassoc_cmd(padapter, 0, true); if (check_fwstate(pmlmepriv, _FW_LINKED) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) rtw_free_assoc_resources(padapter, 1); if ((*pold_state == Ndis802_11Infrastructure) || (*pold_state == Ndis802_11IBSS)) { @@ -468,7 +468,7 @@ u16 rtw_get_cur_max_rate(struct adapter *adapter) u8 short_GI = 0; if (!check_fwstate(pmlmepriv, _FW_LINKED) && - !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) + !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) return 0; psta = rtw_get_stainfo(&adapter->stapriv, get_bssid(pmlmepriv)); diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 3c4c7d902295..4381df2b89d6 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1758,7 +1758,7 @@ static int rtw_check_join_candidate(struct mlme_priv *mlme if (rtw_to_roam(adapter) > 0) { if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= mlme->roam_scanr_exp_ms || - !is_same_ess(&competitor->network, &mlme->cur_network.network) + !is_same_ess(&competitor->network, &mlme->cur_network.network) ) goto exit; } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index b3b9976b15b2..67570b81034f 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -513,7 +513,7 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) return _SUCCESS; if (!check_fwstate(pmlmepriv, _FW_LINKED) && - !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { + !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { return _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index 098a08c11fe5..d61b0fd4bfcd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -704,7 +704,7 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * signed int bmcast = is_multicast_ether_addr(pattrib->dst); if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { /* filter packets that SA is myself or multicast or broadcast */ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) { @@ -834,7 +834,7 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p } } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) && - check_fwstate(pmlmepriv, _FW_LINKED)) { + check_fwstate(pmlmepriv, _FW_LINKED)) { memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); @@ -2170,10 +2170,10 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) /* check if need to enqueue into uc_swdec_pending_queue*/ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) && - !is_multicast_ether_addr(prxattrib->ra) && prxattrib->encrypt > 0 && - (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt) && - psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPAPSK && - !psecuritypriv->busetkipkey) { + !is_multicast_ether_addr(prxattrib->ra) && prxattrib->encrypt > 0 && + (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt) && + psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPAPSK && + !psecuritypriv->busetkipkey) { rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue); if (recvpriv->free_recvframe_cnt < NR_RECVFRAME / 4) { diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 9ac8a8f74397..037a426ed15e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -691,7 +691,7 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p memcpy(pattrib->src, ðerhdr.h_source, ETH_ALEN); if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); memcpy(pattrib->ta, pattrib->src, ETH_ALEN); } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { @@ -963,7 +963,7 @@ s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattr if (pattrib->qos_en) qos_option = true; } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), ETH_ALEN); diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index 426989986cc3..fba85cc0e7bf 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -251,12 +251,10 @@ void odm_Adaptivity(void *pDM_VOID, u8 IGI) } else EDCCA_State = true; - if ( - pDM_Odm->bLinked && - !pDM_Odm->Carrier_Sense_enable && - !pDM_Odm->NHM_disable && - !pDM_Odm->TxHangFlg - ) + if (pDM_Odm->bLinked && + !pDM_Odm->Carrier_Sense_enable && + !pDM_Odm->NHM_disable && + !pDM_Odm->TxHangFlg) odm_NHMBB(pDM_Odm); if (EDCCA_State) { @@ -527,10 +525,8 @@ void odm_DIG(void *pDM_VOID) CurrentIGI = pDM_DigTable->rx_gain_range_max; /* 1 Force upper bound and lower bound for adaptivity */ - if ( - pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY && - pDM_Odm->adaptivity_flag - ) { + if (pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY && + pDM_Odm->adaptivity_flag) { if (CurrentIGI > Adap_IGI_Upper) CurrentIGI = Adap_IGI_Upper; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c index 5cb13a10efd6..612351010359 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c @@ -137,10 +137,8 @@ void rtl8723b_HalDmWatchDog(struct adapter *Adapter) fw_current_in_ps_mode = adapter_to_pwrctl(Adapter)->fw_current_in_ps_mode; rtw_hal_get_hwreg(Adapter, HW_VAR_FWLPS_RF_ON, (u8 *)(&bFwPSAwake)); - if ( - hw_init_completed && - (!fw_current_in_ps_mode && bFwPSAwake) - ) { + if (hw_init_completed && + (!fw_current_in_ps_mode && bFwPSAwake)) { rtw_hal_check_rxfifo_full(Adapter); } diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index f27970ef54c8..b801a78f69e0 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -583,10 +583,8 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) pHalData = GET_HAL_DATA(padapter); pwrctrlpriv = adapter_to_pwrctl(padapter); - if ( - adapter_to_pwrctl(padapter)->bips_processing && - adapter_to_pwrctl(padapter)->pre_ips_type == 0 - ) { + if (adapter_to_pwrctl(padapter)->bips_processing && + adapter_to_pwrctl(padapter)->pre_ips_type == 0) { unsigned long start_time; u8 cpwm_orig, cpwm_now; u8 val8, bMacPwrCtrlOn = true; From b684a14b7198e79fcc8e432d5847b10f64952e74 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Mon, 27 Apr 2026 20:58:46 +0300 Subject: [PATCH 0413/5214] staging: rtl8723bs: wrap lines exceeding 100 characters Wrap lines exceeding 100 characters to improve code readability and conform to the Linux kernel coding style. This fixes the LONG_LINE issues identified by checkpatch.pl. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260427175846.23470-9-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme.c | 6 +++--- drivers/staging/rtl8723bs/core/rtw_recv.c | 6 ++++-- drivers/staging/rtl8723bs/core/rtw_xmit.c | 3 ++- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 3 ++- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 4381df2b89d6..c07609f63e5d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1757,9 +1757,9 @@ static int rtw_check_join_candidate(struct mlme_priv *mlme goto exit; if (rtw_to_roam(adapter) > 0) { - if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= mlme->roam_scanr_exp_ms || - !is_same_ess(&competitor->network, &mlme->cur_network.network) - ) + if (jiffies_to_msecs(jiffies - competitor->last_scanned) >= + mlme->roam_scanr_exp_ms || + !is_same_ess(&competitor->network, &mlme->cur_network.network)) goto exit; } diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index d61b0fd4bfcd..b3a38ffa6ef7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -399,7 +399,8 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p if (bmic_err) { /* double check key_index for some timing issue , */ /* cannot compare with psecuritypriv->dot118021XGrpKeyid also cause timing issue */ - if (is_multicast_ether_addr(prxattrib->ra) && (prxattrib->key_index != pmlmeinfo->key_index)) + if (is_multicast_ether_addr(prxattrib->ra) && + (prxattrib->key_index != pmlmeinfo->key_index)) brpt_micerror = false; if (prxattrib->bdecrypted && brpt_micerror) @@ -2170,7 +2171,8 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) /* check if need to enqueue into uc_swdec_pending_queue*/ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) && - !is_multicast_ether_addr(prxattrib->ra) && prxattrib->encrypt > 0 && + !is_multicast_ether_addr(prxattrib->ra) && + prxattrib->encrypt > 0 && (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt) && psecuritypriv->ndisauthtype == Ndis802_11AuthModeWPAPSK && !psecuritypriv->busetkipkey) { diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 037a426ed15e..5251b12991ac 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -424,7 +424,8 @@ static void update_attrib_vcs_info(struct adapter *padapter, struct xmit_frame * } else { while (true) { /* IOT action */ - if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS) && pattrib->ampdu_en && + if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS) && + pattrib->ampdu_en && (padapter->securitypriv.dot11PrivacyAlgrthm == _AES_)) { pattrib->vcs_mode = CTS_TO_SELF; break; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index f8ba55e22638..b4dd9e9d41be 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -998,7 +998,8 @@ void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) rtw_write32(padapter, REG_TCR, value32); /* NOTE: Fix test chip's bug (about contention windows's randomness) */ - if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { + if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE | + WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) { rtw_write8(padapter, REG_RXTSF_OFFSET_CCK, 0x50); rtw_write8(padapter, REG_RXTSF_OFFSET_OFDM, 0x50); } diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 9cc5199753c2..1484336d7551 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1210,7 +1210,8 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy spin_unlock_bh(&pwdev_priv->scan_req_lock); if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { - if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) { + if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | + _FW_UNDER_LINKING)) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; } From 062d449caebb026bb608890e9febac1d83d0f230 Mon Sep 17 00:00:00 2001 From: Siwanan Bungtong Date: Sat, 2 May 2026 03:31:21 +0700 Subject: [PATCH 0414/5214] staging: rtl8723bs: add braces to if/else arms in HalBtc8723b1Ant.c Add missing braces to if/else statements to comply with kernel coding style and improve readability. No functional changes. Signed-off-by: Siwanan Bungtong Link: https://patch.msgid.link/20260501203121.227992-3-horstaufmental@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index b3e34f97cfc6..9bef096164fa 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -1485,12 +1485,13 @@ static void halbtc8723b1ant_PsTdmaCheckForPowerSaveState( if (lpsMode) { /* already under LPS state */ if (bNewPsState) { /* keep state under LPS, do nothing. */ - } else /* will leave LPS state, turn off psTdma first */ + } else { /* will leave LPS state, turn off psTdma first */ halbtc8723b1ant_PsTdma(pBtCoexist, NORMAL_EXEC, false, 0); + } } else { /* NO PS state */ - if (bNewPsState) /* will enter LPS state, turn off psTdma first */ + if (bNewPsState) { /* will enter LPS state, turn off psTdma first */ halbtc8723b1ant_PsTdma(pBtCoexist, NORMAL_EXEC, false, 0); - else { + } else { /* keep state under NO PS state, do nothing. */ } } From fd8b39b77613dcf2c5cd953be87ba397dc58e6f6 Mon Sep 17 00:00:00 2001 From: Siwanan Bungtong Date: Sat, 2 May 2026 03:53:36 +0700 Subject: [PATCH 0415/5214] staging: rtl8723bs: fix unbalanced braces in if/else statements Fix inconsistent brace usage in if/else blocks to improve readability and avoid misleading indentation. No functional changes. Signed-off-by: Siwanan Bungtong Reviewed-by: Nikolay Kulikov Link: https://patch.msgid.link/20260501205336.230955-1-horstaufmental@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index 9bef096164fa..622970c7fcfa 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -793,8 +793,9 @@ static void halbtc8723b1ant_SetAntPath( /* Use H2C to set GNT_BT to HIGH */ H2C_Parameter[0] = 1; pBtCoexist->fBtcFillH2c(pBtCoexist, 0x6E, 1, H2C_Parameter); - } else /* set grant_bt to high */ + } else { /* set grant_bt to high */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x765, 0x18); + } /* set wlan_act control by PTA */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x76e, 0x4); @@ -810,8 +811,9 @@ static void halbtc8723b1ant_SetAntPath( /* Use H2C to set GNT_BT to HIGH */ H2C_Parameter[0] = 1; pBtCoexist->fBtcFillH2c(pBtCoexist, 0x6E, 1, H2C_Parameter); - } else /* set grant_bt to high */ + } else { /* set grant_bt to high */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x765, 0x18); + } /* set wlan_act to always low */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x76e, 0x4); @@ -1856,8 +1858,9 @@ static void halbtc8723b1ant_ActionWifiConnected(struct btc_coexist *pBtCoexist) halbtc8723b1ant_PowerSaveState(pBtCoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); else halbtc8723b1ant_PowerSaveState(pBtCoexist, BTC_PS_LPS_ON, 0x50, 0x4); - } else + } else { halbtc8723b1ant_PowerSaveState(pBtCoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + } /* tdma and coex table */ if (!bWifiBusy) { @@ -2053,8 +2056,9 @@ static void halbtc8723b1ant_RunCoexistMechanism(struct btc_coexist *pBtCoexist) halbtc8723b1ant_ActionWifiNotConnectedAssoAuth(pBtCoexist); } else halbtc8723b1ant_ActionWifiNotConnected(pBtCoexist); - } else /* wifi LPS/Busy */ + } else { /* wifi LPS/Busy */ halbtc8723b1ant_ActionWifiConnected(pBtCoexist); + } } static void halbtc8723b1ant_InitCoexDm(struct btc_coexist *pBtCoexist) @@ -2089,8 +2093,9 @@ static void halbtc8723b1ant_InitHwConfig( if (bWifiOnly) { halbtc8723b1ant_SetAntPath(pBtCoexist, BTC_ANT_PATH_WIFI, true, false); halbtc8723b1ant_PsTdma(pBtCoexist, FORCE_EXEC, false, 9); - } else + } else { halbtc8723b1ant_SetAntPath(pBtCoexist, BTC_ANT_PATH_BT, true, false); + } /* PTA parameter */ halbtc8723b1ant_CoexTableWithType(pBtCoexist, FORCE_EXEC, 0); From d4be72aad7dcd0a9ba96799e92171ca035d89d79 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:40 +0300 Subject: [PATCH 0416/5214] staging: rtl8723bs: rename ChipType of struct hal_version to snake_case Rename 'ChipType' field to 'chip_type' to comply with Linux kernel coding style and fix checkpatch.pl warning. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-2-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 2 +- drivers/staging/rtl8723bs/include/HalVerDef.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index b4dd9e9d41be..a4e7bf0e8a97 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -836,7 +836,7 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) value32 = rtw_read32(padapter, REG_SYS_CFG); ChipVersion.ICType = CHIP_8723B; - ChipVersion.ChipType = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP); + ChipVersion.chip_type = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP); ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC); ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */ diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index d0ce21ccc1cc..bf4716ec18e8 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -43,7 +43,7 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ struct hal_version { /* tag_HAL_VERSION */ enum hal_ic_type_e ICType; - enum hal_chip_type_e ChipType; + enum hal_chip_type_e chip_type; enum hal_cut_version_e CUTVersion; enum hal_vendor_e VendorType; u8 ROMVer; @@ -53,7 +53,7 @@ struct hal_version { /* tag_HAL_VERSION */ /* Get element */ #define GET_CVID_IC_TYPE(version) ((enum hal_ic_type_e)((version).ICType)) -#define GET_CVID_CHIP_TYPE(version) ((enum hal_chip_type_e)((version).ChipType)) +#define GET_CVID_CHIP_TYPE(version) ((enum hal_chip_type_e)((version).chip_type)) #define GET_CVID_MANUFACTUER(version) ((enum hal_vendor_e)((version).VendorType)) #define GET_CVID_CUT_VERSION(version) ((enum hal_cut_version_e)((version).CUTVersion)) #define GET_CVID_ROM_VERSION(version) (((version).ROMVer) & ROM_VERSION_MASK) From 6d168099f1b10d8046681040ccf2ad6fa9b8685d Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:41 +0300 Subject: [PATCH 0417/5214] staging: rtl8723bs: replace type and rename the chip_type field The 'chip_type' field always accepts one value from the hal_chip_type_e enumeration: TEST_CHIP or NORMAL_CHIP (FPGA is never used). Changing this field's type to bool will allow it to be used directly in conditions without the need for wrapped macros. The new type requires a corresponding variable name, so rename it to 'chip_normal' to improve code readability. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-3-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 4 ++-- drivers/staging/rtl8723bs/include/HalVerDef.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index a4e7bf0e8a97..87da5f908e1a 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -836,7 +836,7 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) value32 = rtw_read32(padapter, REG_SYS_CFG); ChipVersion.ICType = CHIP_8723B; - ChipVersion.chip_type = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP); + ChipVersion.chip_normal = ((value32 & RTL_ID) ? false : true); ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC); ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */ @@ -912,7 +912,7 @@ void _InitBurstPktLen_8723BS(struct adapter *Adapter) /* ARFB table 9 for 11ac 5G 2SS */ rtw_write32(Adapter, REG_ARFR0_8723B, 0x00000010); - if (IS_NORMAL_CHIP(pHalData->VersionID)) + if (pHalData->VersionID.chip_normal) rtw_write32(Adapter, REG_ARFR0_8723B+4, 0xfffff000); else rtw_write32(Adapter, REG_ARFR0_8723B+4, 0x3e0ff000); diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index bf4716ec18e8..fbdfb690b73b 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -43,7 +43,7 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ struct hal_version { /* tag_HAL_VERSION */ enum hal_ic_type_e ICType; - enum hal_chip_type_e chip_type; + bool chip_normal; /* true - normal chip, false - test chip */ enum hal_cut_version_e CUTVersion; enum hal_vendor_e VendorType; u8 ROMVer; From 4874c05c4732abb9ea70a371eac7dced1f0022f7 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:42 +0300 Subject: [PATCH 0418/5214] staging: rtl8723bs: remove unused macros from include/HalVerDef.h Remove macros that are wrappers over the fields of 'struct hal_version' and are never used in driver code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-4-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/HalVerDef.h | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index fbdfb690b73b..cb970b1d37d7 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -51,35 +51,5 @@ struct hal_version { /* tag_HAL_VERSION */ /* hal_version VersionID; */ -/* Get element */ -#define GET_CVID_IC_TYPE(version) ((enum hal_ic_type_e)((version).ICType)) -#define GET_CVID_CHIP_TYPE(version) ((enum hal_chip_type_e)((version).chip_type)) -#define GET_CVID_MANUFACTUER(version) ((enum hal_vendor_e)((version).VendorType)) -#define GET_CVID_CUT_VERSION(version) ((enum hal_cut_version_e)((version).CUTVersion)) -#define GET_CVID_ROM_VERSION(version) (((version).ROMVer) & ROM_VERSION_MASK) - -/* */ -/* Common Macro. -- */ -/* */ -/* hal_version VersionID */ - -/* hal_chip_type_e */ -#define IS_TEST_CHIP(version) ((GET_CVID_CHIP_TYPE(version) == TEST_CHIP) ? true : false) -#define IS_NORMAL_CHIP(version) ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false) - -/* hal_cut_version_e */ -#define IS_A_CUT(version) ((GET_CVID_CUT_VERSION(version) == A_CUT_VERSION) ? true : false) -#define IS_B_CUT(version) ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? true : false) -#define IS_C_CUT(version) ((GET_CVID_CUT_VERSION(version) == C_CUT_VERSION) ? true : false) -#define IS_D_CUT(version) ((GET_CVID_CUT_VERSION(version) == D_CUT_VERSION) ? true : false) -#define IS_E_CUT(version) ((GET_CVID_CUT_VERSION(version) == E_CUT_VERSION) ? true : false) -#define IS_I_CUT(version) ((GET_CVID_CUT_VERSION(version) == I_CUT_VERSION) ? true : false) -#define IS_J_CUT(version) ((GET_CVID_CUT_VERSION(version) == J_CUT_VERSION) ? true : false) -#define IS_K_CUT(version) ((GET_CVID_CUT_VERSION(version) == K_CUT_VERSION) ? true : false) - -/* hal_vendor_e */ -#define IS_CHIP_VENDOR_TSMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC) ? true : false) -#define IS_CHIP_VENDOR_UMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC) ? true : false) -#define IS_CHIP_VENDOR_SMIC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_SMIC) ? true : false) #endif From acca1dcd9f567562c143316012e3f018b5b9ed9b Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:43 +0300 Subject: [PATCH 0419/5214] staging: rtl8723bs: remove unused ICType from struct hal_version Remove the unused 'ICType' field from the 'struct hal_version', as it always takes a single value (CHIP_8723B) upon initialization and is never used in driver. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-5-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 1 - drivers/staging/rtl8723bs/include/HalVerDef.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 87da5f908e1a..055ce57848f2 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -835,7 +835,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) pHalData = GET_HAL_DATA(padapter); value32 = rtw_read32(padapter, REG_SYS_CFG); - ChipVersion.ICType = CHIP_8723B; ChipVersion.chip_normal = ((value32 & RTL_ID) ? false : true); ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC); ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */ diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index cb970b1d37d7..c9c7bc369fa9 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -42,7 +42,6 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ }; struct hal_version { /* tag_HAL_VERSION */ - enum hal_ic_type_e ICType; bool chip_normal; /* true - normal chip, false - test chip */ enum hal_cut_version_e CUTVersion; enum hal_vendor_e VendorType; From 6d9e3c5ac7279d82814a6b6ac0c6aeab489a74fa Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:44 +0300 Subject: [PATCH 0420/5214] staging: rtl8723bs: remove unused VendorType from struct hal_version The 'VendorType' field of the 'struct hal_version' is set once when the chip is initialized, but no one ever reads this data, so remove it to eliminate unused code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-6-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 1 - drivers/staging/rtl8723bs/include/HalVerDef.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 055ce57848f2..c19d33b101c4 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -836,7 +836,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) value32 = rtw_read32(padapter, REG_SYS_CFG); ChipVersion.chip_normal = ((value32 & RTL_ID) ? false : true); - ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC); ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */ /* For regulator mode. by tynli. 2011.01.14 */ diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index c9c7bc369fa9..9428f82c248c 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -44,7 +44,6 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ struct hal_version { /* tag_HAL_VERSION */ bool chip_normal; /* true - normal chip, false - test chip */ enum hal_cut_version_e CUTVersion; - enum hal_vendor_e VendorType; u8 ROMVer; }; From 12a431a95d65c9d2ff3b38f3e1fcc1821a597446 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:45 +0300 Subject: [PATCH 0421/5214] staging: rtl8723bs: remove unused CUTVersion from struct hal_version Remove the 'CUTVersion' field from 'struct hal_version', which is read during initialization but is never used anywhere in the driver. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-7-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 1 - drivers/staging/rtl8723bs/include/HalVerDef.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index c19d33b101c4..30cae6e0439b 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -836,7 +836,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) value32 = rtw_read32(padapter, REG_SYS_CFG); ChipVersion.chip_normal = ((value32 & RTL_ID) ? false : true); - ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */ /* For regulator mode. by tynli. 2011.01.14 */ pHalData->RegulatorMode = ((value32 & SPS_SEL) ? RT_LDO_REGULATOR : RT_SWITCHING_REGULATOR); diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index 9428f82c248c..249acd83c89f 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -43,7 +43,6 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ struct hal_version { /* tag_HAL_VERSION */ bool chip_normal; /* true - normal chip, false - test chip */ - enum hal_cut_version_e CUTVersion; u8 ROMVer; }; From faf9f114a82001fe3a4b38e4650f3bc203622ae0 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:46 +0300 Subject: [PATCH 0422/5214] staging: rtl8723bs: remove unused ROMVer from struct hal_version 'ROMVer' field of 'sturct hal_version' never used anywhere in the driver, so remove it to eliminate unused code. Remove the related call to rtw_read32(), the result of which is only used for 'ROMVer' and is no longer needed. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-8-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 3 --- drivers/staging/rtl8723bs/include/HalVerDef.h | 1 - 2 files changed, 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 30cae6e0439b..4cdef45ae15e 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -840,9 +840,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) /* For regulator mode. by tynli. 2011.01.14 */ pHalData->RegulatorMode = ((value32 & SPS_SEL) ? RT_LDO_REGULATOR : RT_SWITCHING_REGULATOR); - value32 = rtw_read32(padapter, REG_GPIO_OUTSTS); - ChipVersion.ROMVer = ((value32 & RF_RL_ID) >> 20); /* ROM code version. */ - /* For multi-function consideration. Added by Roger, 2010.10.06. */ pHalData->MultiFunc = RT_MULTI_FUNC_NONE; value32 = rtw_read32(padapter, REG_MULTI_FUNC_CTRL); diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index 249acd83c89f..90ef37b1ddae 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -43,7 +43,6 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ struct hal_version { /* tag_HAL_VERSION */ bool chip_normal; /* true - normal chip, false - test chip */ - u8 ROMVer; }; /* hal_version VersionID; */ From 3778b29eaa1b5587b540b86daeb84d42c72d1697 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:47 +0300 Subject: [PATCH 0423/5214] staging: rtl8723bs: move normal_chip field to struct hal_com_data The 'chip_normal' field is the only remaining member of the 'struct hal_version' that is used. Move this field to the hal_com_data struct, where other chip information is already stored. The order of assignments and reads to this variable remains unchanged. This is purely a data relocation patch. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-9-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 4 ++-- drivers/staging/rtl8723bs/include/HalVerDef.h | 1 - drivers/staging/rtl8723bs/include/hal_data.h | 3 +++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 4cdef45ae15e..5c3b92f473aa 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -835,7 +835,7 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) pHalData = GET_HAL_DATA(padapter); value32 = rtw_read32(padapter, REG_SYS_CFG); - ChipVersion.chip_normal = ((value32 & RTL_ID) ? false : true); + pHalData->chip_normal = ((value32 & RTL_ID) ? false : true); /* For regulator mode. by tynli. 2011.01.14 */ pHalData->RegulatorMode = ((value32 & SPS_SEL) ? RT_LDO_REGULATOR : RT_SWITCHING_REGULATOR); @@ -906,7 +906,7 @@ void _InitBurstPktLen_8723BS(struct adapter *Adapter) /* ARFB table 9 for 11ac 5G 2SS */ rtw_write32(Adapter, REG_ARFR0_8723B, 0x00000010); - if (pHalData->VersionID.chip_normal) + if (pHalData->chip_normal) rtw_write32(Adapter, REG_ARFR0_8723B+4, 0xfffff000); else rtw_write32(Adapter, REG_ARFR0_8723B+4, 0x3e0ff000); diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index 90ef37b1ddae..ed6925872d56 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -42,7 +42,6 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ }; struct hal_version { /* tag_HAL_VERSION */ - bool chip_normal; /* true - normal chip, false - test chip */ }; /* hal_version VersionID; */ diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index ff4383e30322..20b14e665d99 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -391,6 +391,9 @@ struct hal_com_data { /* Interrupt related register information. */ u32 SysIntrStatus; u32 SysIntrMask; + + /* Chip version information */ + bool chip_normal; /* true - normal chip, false - test chip */ }; #define GET_HAL_DATA(__padapter) ((struct hal_com_data *)((__padapter)->HalData)) From 2e0ebca1c0103d2f8f54771e3caaa3caf9487dbd Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:48 +0300 Subject: [PATCH 0424/5214] staging: rtl8723bs: remove struct hal_version from include/HalVerDef.h Remove the empty 'struct hal_version' as all its fields were removed earlier and it is no longer needed. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-10-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c | 7 +------ drivers/staging/rtl8723bs/include/HalVerDef.h | 5 ----- drivers/staging/rtl8723bs/include/hal_data.h | 1 - 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 5c3b92f473aa..ebcd174336dc 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -825,10 +825,9 @@ void Hal_ReadEFuse( hal_ReadEFuse_BT(padapter, _offset, _size_byte, pbuf); } -static struct hal_version ReadChipVersion8723B(struct adapter *padapter) +static void ReadChipVersion8723B(struct adapter *padapter) { u32 value32; - struct hal_version ChipVersion; struct hal_com_data *pHalData; /* YJ, TODO, move read chip type here */ @@ -847,10 +846,6 @@ static struct hal_version ReadChipVersion8723B(struct adapter *padapter) pHalData->MultiFunc |= ((value32 & BT_FUNC_EN) ? RT_MULTI_FUNC_BT : 0); pHalData->MultiFunc |= ((value32 & GPS_FUNC_EN) ? RT_MULTI_FUNC_GPS : 0); pHalData->PolarityCtl = ((value32 & WL_HWPDN_SL) ? RT_POLARITY_HIGH_ACT : RT_POLARITY_LOW_ACT); - - pHalData->VersionID = ChipVersion; - - return ChipVersion; } void rtl8723b_read_chip_version(struct adapter *padapter) diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index ed6925872d56..9b607543133f 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -41,10 +41,5 @@ enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ CHIP_VENDOR_SMIC = 2, }; -struct hal_version { /* tag_HAL_VERSION */ -}; - -/* hal_version VersionID; */ - #endif diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index 20b14e665d99..876b3666aa1c 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -162,7 +162,6 @@ struct dm_priv { struct hal_com_data { - struct hal_version VersionID; enum rt_multi_func MultiFunc; /* For multi-function consideration. */ enum rt_polarity_ctl PolarityCtl; /* For Wifi PDn Polarity control. */ enum rt_regulator_mode RegulatorMode; /* switching regulator or LDO */ From 4917266aa30aba1eb28d7edc77449cacbf813487 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Sun, 3 May 2026 00:55:49 +0300 Subject: [PATCH 0425/5214] staging: rtl8723bs: remove include/HalVerDef.h file Remove this file and several enumerations from it, as they are not used anywhere in the driver code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260502220056.59815-11-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/HalVerDef.h | 45 ------------------- drivers/staging/rtl8723bs/include/hal_com.h | 1 - .../staging/rtl8723bs/include/rtl8723b_hal.h | 1 - 3 files changed, 47 deletions(-) delete mode 100644 drivers/staging/rtl8723bs/include/HalVerDef.h diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h deleted file mode 100644 index 9b607543133f..000000000000 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * - * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. - * - ******************************************************************************/ -#ifndef __HAL_VERSION_DEF_H__ -#define __HAL_VERSION_DEF_H__ - -/* hal_ic_type_e */ -enum hal_ic_type_e { /* tag_HAL_IC_Type_Definition */ - CHIP_8723B = 8, -}; - -/* hal_chip_type_e */ -enum hal_chip_type_e { /* tag_HAL_CHIP_Type_Definition */ - TEST_CHIP = 0, - NORMAL_CHIP = 1, - FPGA = 2, -}; - -/* hal_cut_version_e */ -enum hal_cut_version_e { /* tag_HAL_Cut_Version_Definition */ - A_CUT_VERSION = 0, - B_CUT_VERSION = 1, - C_CUT_VERSION = 2, - D_CUT_VERSION = 3, - E_CUT_VERSION = 4, - F_CUT_VERSION = 5, - G_CUT_VERSION = 6, - H_CUT_VERSION = 7, - I_CUT_VERSION = 8, - J_CUT_VERSION = 9, - K_CUT_VERSION = 10, -}; - -/* HAL_Manufacturer */ -enum hal_vendor_e { /* tag_HAL_Manufacturer_Version_Definition */ - CHIP_VENDOR_TSMC = 0, - CHIP_VENDOR_UMC = 1, - CHIP_VENDOR_SMIC = 2, -}; - - -#endif diff --git a/drivers/staging/rtl8723bs/include/hal_com.h b/drivers/staging/rtl8723bs/include/hal_com.h index cf775585c8e2..7bbf62cfd825 100644 --- a/drivers/staging/rtl8723bs/include/hal_com.h +++ b/drivers/staging/rtl8723bs/include/hal_com.h @@ -7,7 +7,6 @@ #ifndef __HAL_COMMON_H__ #define __HAL_COMMON_H__ -#include "HalVerDef.h" #include "hal_pg.h" #include "hal_phy.h" #include "hal_phy_reg.h" diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h index ffd03927841c..8e04e6a4cb01 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h @@ -118,7 +118,6 @@ struct rt_firmware_hdr { #define WMM_NORMAL_PAGE_NUM_LPQ_8723B 0x20 #define WMM_NORMAL_PAGE_NUM_NPQ_8723B 0x20 -#include "HalVerDef.h" #include "hal_com.h" #define EFUSE_OOB_PROTECT_BYTES 15 From e7a88f7e7aa1065c97d85bb151397d4b89671ee6 Mon Sep 17 00:00:00 2001 From: Aidan Russell Date: Sun, 3 May 2026 08:50:43 -0700 Subject: [PATCH 0426/5214] staging: rtl8723bs: Fix spelling mistakes in comments Fix several spelling mistakes in comments in the rtl8723bs staging driver. No functional changes. Signed-off-by: Aidan Russell Reviewed-by: Nikolay Kulikov Link: https://patch.msgid.link/20260503155043.15936-1-aidanlrussell@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_mlme.c | 2 +- drivers/staging/rtl8723bs/core/rtw_xmit.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index f6e618675299..2624d8472b3a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -1650,7 +1650,7 @@ u8 rtw_c2h_packet_wk_cmd(struct adapter *padapter, u8 *pbuf, u16 length) return res; } -/* dont call R/W in this function, beucase SDIO interrupt have claim host */ +/* do not call R/W in this function, because SDIO interrupt have claim host */ /* or deadlock will happen and cause special-systemserver-died in android */ u8 rtw_c2h_wk_cmd(struct adapter *padapter, u8 *c2h_evt) { diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index c07609f63e5d..9cf1c95dd924 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1378,7 +1378,7 @@ void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf) u16 media_status; media_status = (mac_id << 8) | 0; /* MACID|OPMODE:0 means disconnect */ - /* for STA, AP, ADHOC mode, report disconnect stauts to FW */ + /* for STA, AP, ADHOC mode, report disconnect status to FW */ rtw_hal_set_hwreg(adapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status); } diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 5251b12991ac..458e471535ad 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -727,7 +727,7 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p } } - /* for parsing ICMP pakcets */ + /* for parsing ICMP packets */ { struct iphdr *piphdr = (struct iphdr *)tmp; @@ -1359,7 +1359,7 @@ s32 rtw_mgmt_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, s /* set final tx command size */ pattrib->last_txcmdsz = pattrib->pktlen; - /* set protected bit must be beofre SW encrypt */ + /* set protected bit must be before SW encrypt */ SetPrivacy(mem_start); /* software encrypt */ xmitframe_swencrypt(padapter, pxmitframe); From abd79fe84da72eb9e132f6d1ad1987b3f800930f Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Mon, 4 May 2026 14:28:10 +0000 Subject: [PATCH 0427/5214] staging: rtl8723bs: Remove unnecessary braces in rtl8723bs_xmit.c Braces are not necessary for single statement blocks. This fixes the following checkpatch.pl warning: WARNING: braces {} are not necessary for single statement blocks Signed-off-by: Moksh Panicker Link: https://patch.msgid.link/20260504142812.21964-2-mokshpanicker.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index 705becfe6043..a7f23335cdd9 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -395,9 +395,9 @@ static s32 rtl8723bs_xmit_handler(struct adapter *padapter) spin_lock_bh(&pxmitpriv->lock); ret = rtw_txframes_pending(padapter); spin_unlock_bh(&pxmitpriv->lock); - if (ret == 1) { + if (ret == 1) goto next; - } + return _SUCCESS; } From 9669767409c66ff434a74c20bc1cbdf162779dd0 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Mon, 4 May 2026 14:28:11 +0000 Subject: [PATCH 0428/5214] staging: rtl8723bs: Remove commented-out dead code in hal_btcoex.c Remove three commented-out lines of dead code that are never executed. Keeping commented-out code is discouraged as it clutters the codebase and serves no purpose. Signed-off-by: Moksh Panicker Link: https://patch.msgid.link/20260504142812.21964-3-mokshpanicker.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 735321624e1b..e508bf8dd7ff 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -402,9 +402,6 @@ static u8 halbtcoutsrc_Get(void *pBtcContext, u8 getType, void *pOutBuf) case BTC_GET_U1_MAC_PHY_MODE: *pu8 = BTC_SMSP; -/* *pU1Tmp = BTC_DMSP; */ -/* *pU1Tmp = BTC_DMDP; */ -/* *pU1Tmp = BTC_MP_UNKNOWN; */ break; case BTC_GET_U1_AP_NUM: From 81f55766523e5293604cb96c5e98d10da345ff33 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Mon, 4 May 2026 14:28:12 +0000 Subject: [PATCH 0429/5214] staging: rtl8723bs: remove blank line after open brace in hal_com.c Remove unnecessary blank line after the opening brace of a for loop. This fixes the following checkpatch.pl warning: CHECK: Blank lines aren't necessary after an open brace '{' Signed-off-by: Moksh Panicker Link: https://patch.msgid.link/20260504142812.21964-4-mokshpanicker.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_com.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 7fd6e0249390..25686aeb073b 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -252,7 +252,6 @@ void HalSetBrateCfg(struct adapter *Adapter, u8 *mBratesOS, u16 *pBrateCfg) u8 i, is_brate, brate; for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) { - is_brate = mBratesOS[i] & IEEE80211_BASIC_RATE_MASK; brate = mBratesOS[i] & 0x7f; From b72386864481cf7fb6153842d22561ac3032302f Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Sun, 3 May 2026 15:12:36 -0500 Subject: [PATCH 0430/5214] rtc: ab8500: replace sprintf() with sysfs_emit() This patch replaces sprintf() with sysfs_emit() to ensure proper bounds checking. It also simplifies the return logic by directly returning the error after logging, instead of logging, calling sprintf(), then returning. Reviewed-by: Linus Walleij Signed-off-by: Maxwell Doose Link: https://patch.msgid.link/20260503201236.29685-1-m32285159@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ab8500.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index ed2b6b8bb3bf..c6147837f957 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -284,11 +284,10 @@ static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, retval = ab8500_rtc_get_calibration(dev, &calibration); if (retval < 0) { dev_err(dev, "Failed to read RTC calibration attribute\n"); - sprintf(buf, "0\n"); return retval; } - return sprintf(buf, "%d\n", calibration); + return sysfs_emit(buf, "%d\n", calibration); } static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, From 0692c2602e4cd410aa045f8991bd1c142b2e56f9 Mon Sep 17 00:00:00 2001 From: Riccardo Boninsegna Date: Sun, 29 Mar 2026 12:37:09 +0200 Subject: [PATCH 0431/5214] media: rc: mceusb: Add support for 04eb:e033 This is a Sonix SN8P2202XG microcontroller with firmware compatible with the already supported Northstar 04eb:e004, implementing an MCE IR receiver (PCB seems to be tracked for a transmitter too but missing related parts). Found in a Skintek SK-CR-IN+IR ( http://www.skintek.it/SK-CR-IN+IR.php ) internal 3.5 inch USB card reader and MCE receiver combo (implemented by, and wired as, separate USB devices) PCB marking: AU6475 966816 STIR REV:A02 MCE Signed-off-by: Riccardo Boninsegna Signed-off-by: Sean Young --- drivers/media/rc/mceusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 6a9e4382a224..39ba7f6a2549 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -397,6 +397,8 @@ static const struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, /* Northstar Systems, Inc. eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, + /* Northstar Systems, Inc. eHome Infrared Transceiver - variant */ + { USB_DEVICE(VENDOR_NORTHSTAR, 0xe033) }, /* TiVo PC IR Receiver */ { USB_DEVICE(VENDOR_TIVO, 0x2000), .driver_info = TIVO_KIT }, From 94637cc807da2443f34c6368ff64b2c85c6c6d13 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:37 +0200 Subject: [PATCH 0432/5214] media: imon_raw: Refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Sean Young --- drivers/media/rc/imon_raw.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index 3a526dea6532..295acd6ba9e1 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -105,26 +105,16 @@ static void imon_ir_rx(struct urb *urb) static int imon_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_endpoint_descriptor *ir_ep = NULL; - struct usb_host_interface *idesc; + struct usb_endpoint_descriptor *ir_ep; struct usb_device *udev; struct rc_dev *rcdev; struct imon *imon; - int i, ret; + int ret; udev = interface_to_usbdev(intf); - idesc = intf->cur_altsetting; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(ep)) { - ir_ep = ep; - break; - } - } - - if (!ir_ep) { + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ir_ep); + if (ret) { dev_err(&intf->dev, "IR endpoint missing"); return -ENODEV; } From 5a6ec95b0b471105ac601a21db87a03f7f1af967 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:38 +0200 Subject: [PATCH 0433/5214] media: irtoy: Refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining max packet size) instead of open coding. Note that the device has two bulk endpoints so there is no functional change here. Signed-off-by: Johan Hovold Signed-off-by: Sean Young --- drivers/media/rc/ir_toy.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 089833e41178..ee645882dd5a 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -393,27 +393,15 @@ static int irtoy_probe(struct usb_interface *intf, { struct usb_host_interface *idesc = intf->cur_altsetting; struct usb_device *usbdev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *ep_in = NULL; - struct usb_endpoint_descriptor *ep_out = NULL; - struct usb_endpoint_descriptor *ep = NULL; + struct usb_endpoint_descriptor *ep_in, *ep_out; struct irtoy *irtoy; struct rc_dev *rc; struct urb *urb; - int i, pipe, err = -ENOMEM; + int pipe, err; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - ep = &idesc->endpoint[i].desc; - - if (!ep_in && usb_endpoint_is_bulk_in(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_in = ep; - - if (!ep_out && usb_endpoint_is_bulk_out(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_out = ep; - } - - if (!ep_in || !ep_out) { + err = usb_find_common_endpoints(idesc, &ep_in, &ep_out, NULL, NULL); + if (err || usb_endpoint_maxp(ep_in) != MAX_PACKET || + usb_endpoint_maxp(ep_out) != MAX_PACKET) { dev_err(&intf->dev, "required endpoints not found\n"); return -ENODEV; } @@ -422,6 +410,7 @@ static int irtoy_probe(struct usb_interface *intf, if (!irtoy) return -ENOMEM; + err = -ENOMEM; irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); if (!irtoy->in) goto free_irtoy; From e7b91b21755924c2b26bd4f925c1b538a48b2370 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 4 May 2026 10:55:03 +0000 Subject: [PATCH 0434/5214] Input: atmel_mxt_ts - set byte_offset as signed The calculations done to obtain byte_offset can result into a negative number, fix its type. This patch fixes the following sparse error: drivers/input/touchscreen/atmel_mxt_ts.c:1481:44: warning: unsigned value that used to be signed checked against zero? drivers/input/touchscreen/atmel_mxt_ts.c:1479:49: signed value source Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260504-fix-sparse-v1-1-1071137cd280@chromium.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 87c6a10381f2..26ba82fb60b6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1397,7 +1397,8 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) { struct device *dev = &data->client->dev; struct mxt_object *object; - unsigned int type, instance, size, byte_offset; + unsigned int type, instance, size; + int byte_offset; int offset; int ret; int i; From 432b936fa6b7a8ad5d7ea55be9522c6dc24b9554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:07 +0300 Subject: [PATCH 0435/5214] PCI: Log all resource claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two ways to graft resource into resource tree in PCI, pci_assign_resource() and pci_claim_resource(). Only the former logs the action, which complicated troubleshooting the cases where resources are assigned by pci_claim_resource(), which mostly assigns the addresses inherited from the FW. Add logging into pci_claim_resource() to make troubleshooting easier. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-2-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-res.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index fbc05cda96ee..0d203325562b 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -167,6 +167,8 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return -EBUSY; } + pci_dbg(dev, "%s %pR: claiming\n", res_name, res); + return 0; } EXPORT_SYMBOL(pci_claim_resource); From 0c3f82a584082a0d2e09b65e0a2e9cdcf9046d0d Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:12 +0900 Subject: [PATCH 0436/5214] PCI: endpoint: pci-epf-vntb: Reuse pre-exposed doorbells and IRQ flags Support doorbell backends where the doorbell target is already exposed via a platform-owned fixed BAR mapping and/or where the doorbell IRQ must be requested with specific flags. When pci_epf_alloc_doorbell() provides db_msg[].bar/offset, reuse the pre-exposed BAR window and skip programming a new inbound mapping. Also honor db_msg[].irq_flags when requesting the doorbell IRQ. Multiple doorbells may share the same Linux IRQ. Avoid duplicate request_irq() calls by requesting each unique virq once. Make pci-epf-vntb work with platform-defined or embedded doorbell backends without exposing backend-specific details to the consumer layer. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Niklas Cassel Reviewed-by: Frank Li Link: https://patch.msgid.link/20260414141514.1341429-6-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 2256c3062b1a..b493a300da4d 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -134,6 +134,11 @@ struct epf_ntb { u16 vntb_vid; bool linkup; + + /* + * True when doorbells are interrupt-driven (MSI or embedded), false + * when polled. + */ bool msi_doorbell; u32 spad_size; @@ -517,6 +522,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return 0; } +static bool epf_ntb_db_irq_is_duplicated(const struct pci_epf *epf, unsigned int idx) +{ + unsigned int i; + + for (i = 0; i < idx; i++) + if (epf->db_msg[i].virq == epf->db_msg[idx].virq) + return true; + + return false; +} + static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, struct pci_epf_bar *db_bar, const struct pci_epc_features *epc_features, @@ -533,9 +549,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, if (ret) return ret; + /* + * The doorbell target may already be exposed by a platform-owned fixed + * BAR. In that case, we must reuse it and the requested db_bar must + * match. + */ + if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) { + ret = -EINVAL; + goto err_free_doorbell; + } + for (req = 0; req < ntb->db_count; req++) { + /* Avoid requesting duplicate handlers */ + if (epf_ntb_db_irq_is_duplicated(epf, req)) + continue; + ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler, - 0, "pci_epf_vntb_db", ntb); + epf->db_msg[req].irq_flags, "pci_epf_vntb_db", + ntb); if (ret) { dev_err(&epf->dev, @@ -545,6 +576,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, } } + if (epf->db_msg[0].bar != NO_BAR) { + for (i = 0; i < ntb->db_count; i++) { + msg = &epf->db_msg[i].msg; + + if (epf->db_msg[i].bar != barno) { + ret = -EINVAL; + goto err_free_irq; + } + + ntb->reg->db_data[i] = msg->data; + ntb->reg->db_offset[i] = epf->db_msg[i].offset; + } + goto out; + } + + /* Program inbound mapping for the doorbell */ msg = &epf->db_msg[0].msg; high = 0; @@ -591,6 +638,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, ntb->reg->db_offset[i] = offset; } +out: ntb->reg->db_entry_size = 0; ntb->msi_doorbell = true; @@ -598,9 +646,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, return 0; err_free_irq: - for (req--; req >= 0; req--) + for (req--; req >= 0; req--) { + if (epf_ntb_db_irq_is_duplicated(epf, req)) + continue; free_irq(epf->db_msg[req].virq, ntb); + } +err_free_doorbell: pci_epf_free_doorbell(ntb->epf); return ret; } @@ -666,8 +718,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb) if (ntb->msi_doorbell) { int i; - for (i = 0; i < ntb->db_count; i++) + for (i = 0; i < ntb->db_count; i++) { + if (epf_ntb_db_irq_is_duplicated(ntb->epf, i)) + continue; free_irq(ntb->epf->db_msg[i].virq, ntb); + } } if (ntb->epf->db_msg) From 8fda2dd209d34396cf49504e2c8dd55d182b14bc Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:13 +0900 Subject: [PATCH 0437/5214] PCI: endpoint: pci-epf-test: Reuse pre-exposed doorbell targets pci-epf-test advertises the doorbell target to the RC as a BAR number and an offset, and the RC rings the doorbell with a single DWORD MMIO write. Some doorbell backends may report that the doorbell target is already exposed via a platform-owned fixed BAR (db_msg[0].bar/offset). In that case, reuse the pre-exposed window and do not reprogram the BAR with pci_epc_set_bar(). Also honor db_msg[0].irq_flags when requesting the doorbell IRQ, and only restore the original BAR mapping on disable if pci-epf-test programmed it. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam [bhelgaas: wrap comment] Signed-off-by: Bjorn Helgaas Tested-by: Niklas Cassel Reviewed-by: Frank Li Link: https://patch.msgid.link/20260414141514.1341429-7-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-test.c | 86 +++++++++++++------ 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 591d301fa89d..4802d4f80f78 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -94,6 +94,7 @@ struct pci_epf_test { bool dma_private; const struct pci_epc_features *epc_features; struct pci_epf_bar db_bar; + bool db_bar_programmed; size_t bar_size[PCI_STD_NUM_BARS]; }; @@ -733,7 +734,9 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, { u32 status = le32_to_cpu(reg->status); struct pci_epf *epf = epf_test->epf; + struct pci_epf_doorbell_msg *db; struct pci_epc *epc = epf->epc; + unsigned long irq_flags; struct msi_msg *msg; enum pci_barno bar; size_t offset; @@ -743,13 +746,28 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, if (ret) goto set_status_err; - msg = &epf->db_msg[0].msg; - bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1); - if (bar < BAR_0) - goto err_doorbell_cleanup; + db = &epf->db_msg[0]; + msg = &db->msg; + epf_test->db_bar_programmed = false; + + if (db->bar != NO_BAR) { + /* + * The doorbell target is already exposed via a platform-owned + * fixed BAR + */ + bar = db->bar; + offset = db->offset; + } else { + bar = pci_epc_get_next_free_bar(epf_test->epc_features, + epf_test->test_reg_bar + 1); + if (bar < BAR_0) + goto err_doorbell_cleanup; + } + + irq_flags = epf->db_msg[0].irq_flags | IRQF_ONESHOT; ret = request_threaded_irq(epf->db_msg[0].virq, NULL, - pci_epf_test_doorbell_handler, IRQF_ONESHOT, + pci_epf_test_doorbell_handler, irq_flags, "pci-ep-test-doorbell", epf_test); if (ret) { dev_err(&epf->dev, @@ -761,22 +779,30 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, reg->doorbell_data = cpu_to_le32(msg->data); reg->doorbell_bar = cpu_to_le32(bar); - msg = &epf->db_msg[0].msg; - ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo, - &epf_test->db_bar.phys_addr, &offset); + if (db->bar == NO_BAR) { + ret = pci_epf_align_inbound_addr(epf, bar, + ((u64)msg->address_hi << 32) | + msg->address_lo, + &epf_test->db_bar.phys_addr, + &offset); - if (ret) - goto err_free_irq; + if (ret) + goto err_free_irq; + } reg->doorbell_offset = cpu_to_le32(offset); - epf_test->db_bar.barno = bar; - epf_test->db_bar.size = epf->bar[bar].size; - epf_test->db_bar.flags = epf->bar[bar].flags; + if (db->bar == NO_BAR) { + epf_test->db_bar.barno = bar; + epf_test->db_bar.size = epf->bar[bar].size; + epf_test->db_bar.flags = epf->bar[bar].flags; - ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); - if (ret) - goto err_free_irq; + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); + if (ret) + goto err_free_irq; + + epf_test->db_bar_programmed = true; + } status |= STATUS_DOORBELL_ENABLE_SUCCESS; reg->status = cpu_to_le32(status); @@ -806,17 +832,23 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test, free_irq(epf->db_msg[0].virq, epf_test); pci_epf_test_doorbell_cleanup(epf_test); - /* - * The doorbell feature temporarily overrides the inbound translation - * to point to the address stored in epf_test->db_bar.phys_addr, i.e., - * it calls set_bar() twice without ever calling clear_bar(), as - * calling clear_bar() would clear the BAR's PCI address assigned by - * the host. Thus, when disabling the doorbell, restore the inbound - * translation to point to the memory allocated for the BAR. - */ - ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]); - if (ret) - goto set_status_err; + if (epf_test->db_bar_programmed) { + /* + * The doorbell feature temporarily overrides the inbound + * translation to point to the address stored in + * epf_test->db_bar.phys_addr, i.e., it calls set_bar() + * twice without ever calling clear_bar(), as calling + * clear_bar() would clear the BAR's PCI address assigned + * by the host. Thus, when disabling the doorbell, restore + * the inbound translation to point to the memory allocated + * for the BAR. + */ + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]); + if (ret) + goto set_status_err; + + epf_test->db_bar_programmed = false; + } status |= STATUS_DOORBELL_DISABLE_SUCCESS; reg->status = cpu_to_le32(status); From e4f26243953fd3e87df93786b40293ca3a6a465e Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Tue, 14 Apr 2026 23:15:14 +0900 Subject: [PATCH 0438/5214] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback Some endpoint platforms cannot use platform MSI / GIC ITS to implement EP-side doorbells. In those cases, EPF drivers cannot provide an interrupt-driven doorbell and often fall back to polling. Add an "embedded" doorbell backend that uses a controller-integrated doorbell target (e.g. DesignWare integrated eDMA interrupt-emulation doorbell). The backend locates the doorbell register and a corresponding Linux IRQ via the EPC aux-resource API. If the doorbell register is already exposed via a fixed BAR mapping, provide BAR+offset. Otherwise provide the DMA address returned by dma_map_resource() (which may be an IOVA when an IOMMU is enabled) so EPF drivers can map it into BAR space. When MSI doorbell allocation fails with -ENODEV, pci_epf_alloc_doorbell() falls back to this embedded backend. Suggested-by: Manivannan Sadhasivam Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260414141514.1341429-8-den@valinux.co.jp --- drivers/pci/endpoint/pci-ep-msi.c | 131 +++++++++++++++++++++++++++++- include/linux/pci-epf.h | 8 ++ 2 files changed, 136 insertions(+), 3 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 85fe46103220..0855c7930abb 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -6,6 +6,8 @@ * Author: Frank Li */ +#include +#include #include #include #include @@ -36,6 +38,109 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) pci_epc_put(epc); } +static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) +{ + const struct pci_epc_aux_resource *doorbell = NULL; + struct pci_epf_doorbell_msg *msg; + struct pci_epc *epc = epf->epc; + size_t map_size = 0, off = 0; + dma_addr_t iova_base = 0; + phys_addr_t phys_base; + int count, ret, i; + u64 addr; + + count = pci_epc_get_aux_resources_count(epc, epf->func_no, + epf->vfunc_no); + if (count < 0) + return count; + if (!count) + return -ENODEV; + + struct pci_epc_aux_resource *res __free(kfree) = + kcalloc(count, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + ret = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no, + res, count); + if (ret) + return ret; + + /* TODO: Support multiple DOORBELL_MMIO resources per EPC. */ + for (i = 0; i < count; i++) { + if (res[i].type != PCI_EPC_AUX_DOORBELL_MMIO) + continue; + + doorbell = &res[i]; + break; + } + if (!doorbell) + return -ENODEV; + addr = doorbell->phys_addr; + if (!IS_ALIGNED(addr, sizeof(u32))) + return -EINVAL; + + /* + * Reuse the pre-exposed BAR window if available. Otherwise map the MMIO + * doorbell resource here. Any required IOMMU mapping is handled + * internally, matching the MSI doorbell semantics. + */ + if (doorbell->bar == NO_BAR) { + phys_base = addr & PAGE_MASK; + off = addr - phys_base; + map_size = PAGE_ALIGN(off + sizeof(u32)); + + iova_base = dma_map_resource(epc->dev.parent, phys_base, + map_size, DMA_FROM_DEVICE, 0); + if (dma_mapping_error(epc->dev.parent, iova_base)) + return -EIO; + + addr = iova_base + off; + } + + msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto err_unmap; + } + + /* + * Embedded doorbell backends (e.g. DesignWare eDMA interrupt emulation) + * typically provide a single IRQ and do not offer per-doorbell + * distinguishable address/data pairs. The EPC aux resource therefore + * exposes one DOORBELL_MMIO entry (u.db_mmio.irq). + * + * Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells. + * For such backends we replicate the same address/data for each entry + * and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them + * as equivalent "kick" doorbells. + */ + for (i = 0; i < num_db; i++) + msg[i] = (struct pci_epf_doorbell_msg) { + .msg.address_lo = (u32)addr, + .msg.address_hi = (u32)(addr >> 32), + .msg.data = doorbell->u.db_mmio.data, + .virq = doorbell->u.db_mmio.irq, + .irq_flags = IRQF_SHARED, + .type = PCI_EPF_DOORBELL_EMBEDDED, + .bar = doorbell->bar, + .offset = (doorbell->bar == NO_BAR) ? 0 : + doorbell->bar_offset, + .iova_base = iova_base, + .iova_size = map_size, + }; + + epf->num_db = num_db; + epf->db_msg = msg; + return 0; + +err_unmap: + if (map_size) + dma_unmap_resource(epc->dev.parent, iova_base, map_size, + DMA_FROM_DEVICE, 0); + return ret; +} + static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db) { struct pci_epf_doorbell_msg *msg; @@ -109,18 +214,38 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) if (!ret) return 0; - dev_err(dev, "Failed to allocate doorbell: %d\n", ret); - return ret; + /* + * Fall back to embedded doorbell only when platform MSI is unavailable + * for this EPC. + */ + if (ret != -ENODEV) + return ret; + + ret = pci_epf_alloc_doorbell_embedded(epf, num_db); + if (ret) { + dev_err(dev, "Failed to allocate doorbell: %d\n", ret); + return ret; + } + + dev_info(dev, "Using embedded (DMA) doorbell fallback\n"); + return 0; } EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); void pci_epf_free_doorbell(struct pci_epf *epf) { + struct pci_epf_doorbell_msg *msg0; + struct pci_epc *epc = epf->epc; + if (!epf->db_msg) return; - if (epf->db_msg[0].type == PCI_EPF_DOORBELL_MSI) + msg0 = &epf->db_msg[0]; + if (msg0->type == PCI_EPF_DOORBELL_MSI) platform_device_msi_free_irqs_all(epf->epc->dev.parent); + else if (msg0->type == PCI_EPF_DOORBELL_EMBEDDED && msg0->iova_size) + dma_unmap_resource(epc->dev.parent, msg0->iova_base, + msg0->iova_size, DMA_FROM_DEVICE, 0); kfree(epf->db_msg); epf->db_msg = NULL; diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index cd747447a1ea..8a6c64a35890 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -171,6 +171,12 @@ enum pci_epf_doorbell_type { * (NO_BAR if not) * @offset: offset within @bar for the doorbell target (valid iff * @bar != NO_BAR) + * @iova_base: Internal: base DMA address returned by dma_map_resource() for the + * embedded doorbell MMIO window (used only for unmapping). Valid + * when @type is PCI_EPF_DOORBELL_EMBEDDED and @iova_size is + * non-zero. + * @iova_size: Internal: size of the dma_map_resource() mapping at @iova_base. + * Zero when no mapping was created (e.g. pre-exposed fixed BAR). */ struct pci_epf_doorbell_msg { struct msi_msg msg; @@ -179,6 +185,8 @@ struct pci_epf_doorbell_msg { enum pci_epf_doorbell_type type; enum pci_barno bar; resource_size_t offset; + dma_addr_t iova_base; + size_t iova_size; }; /** From 7b9b6b34a6474e6f0e4e7c48e33e824131ed46d5 Mon Sep 17 00:00:00 2001 From: Yuki Horii Date: Fri, 10 Apr 2026 16:41:00 +0900 Subject: [PATCH 0439/5214] Input: tsc2007 - reduce I2C transactions for Z2 read The current implementation sends a separate power-down command after reading the Z2 value, resulting in an extra I2C transaction per measurement cycle. The TSC2007 command byte contains a 2-bit power-down mode selection field. By selecting the power-down state in the Z2 measurement command, the device powers down after the Z2 A/D conversion completes, eliminating the subsequent power-down transaction. This reduces the number of I2C transactions by one per touch measurement cycle, decreasing I2C bus overhead and improving touch sampling performance. Signed-off-by: Yuki Horii Tested-by: Andreas Kemnade # GTA04 Link: https://patch.msgid.link/20260410074100.1660-1-horiiyuk@ishida.co.jp Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2007_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index 524f14eb3da2..f9b3d2598933 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -61,10 +61,8 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) /* turn y+ off, x- on; we'll use formula #1 */ tc->z1 = tsc2007_xfer(tsc, READ_Z1); - tc->z2 = tsc2007_xfer(tsc, READ_Z2); - - /* Prepare for next touch reading - power down ADC, enable PENIRQ */ - tsc2007_xfer(tsc, PWRDOWN); + /* Read Z2 and power down ADC after A/D conversion, enable PENIRQ */ + tc->z2 = tsc2007_xfer(tsc, (TSC2007_POWER_OFF_IRQ_EN | TSC2007_MEASURE_Z2)); } u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc) From 0bcbfd1c1142d85faef8df5cb679d37f71394c5f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Mar 2026 23:41:50 +0200 Subject: [PATCH 0440/5214] media: v4l2-subdev: Fail {enable,disable}_streams and s_streaming nicely If a sub-device does not set enable_streams() and disable_streams() pad ops while it sets the s_stream() video op to v4l2_subdev_s_stream_helper(), enabling or disabling streaming either way on the sub-device will result calling v4l2_subdev_s_stream_helper() and v4l2_subdev_{enable,disable}_streams() recursively, exhausting the stack. Return -ENOIOCTLCMD in this case to handle the situation gracefully. Fixes: b62949ddaa52 ("media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 831c69c958b8..d00d27d49060 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2504,6 +2504,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) u64 source_mask = 0; int pad_index = -1; + if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) || + !v4l2_subdev_has_op(sd, pad, disable_streams))) + return -ENOIOCTLCMD; + /* * Find the source pad. This helper is meant for subdevs that have a * single source pad, so failures shouldn't happen, but catch them From f2cb7c01f48caffb38e12481949dea4f9beb65dc Mon Sep 17 00:00:00 2001 From: Can Guo Date: Fri, 24 Apr 2026 08:14:19 -0700 Subject: [PATCH 0441/5214] scsi: ufs: core: Introduce function ufshcd_query_attr_qword() Introduce a new generic function ufshcd_query_attr_qword() to handle quad-word (64-bit) UFS attribute operations. This consolidates the handling of 64-bit attributes which was previously scattered across multiple specialized functions. Reviewed-by: Peter Wang Signed-off-by: Can Guo Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260424151420.111675-2-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-sysfs.c | 30 ++++++-- drivers/ufs/core/ufshcd-priv.h | 3 +- drivers/ufs/core/ufshcd.c | 126 +++++++++++++++++---------------- 3 files changed, 94 insertions(+), 65 deletions(-) diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index 99af3c73f1af..d9dc4cc3452e 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -594,8 +594,13 @@ static ssize_t device_lvl_exception_id_show(struct device *dev, u64 exception_id; int err; + if (hba->dev_info.wspecversion < 0x410) + return -EOPNOTSUPP; + ufshcd_rpm_get_sync(hba); - err = ufshcd_read_device_lvl_exception_id(hba, &exception_id); + err = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID, + 0, 0, &exception_id); ufshcd_rpm_put_sync(hba); if (err) @@ -1670,6 +1675,12 @@ static inline bool ufshcd_is_wb_attrs(enum attr_idn idn) idn <= QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE; } +static inline bool ufshcd_is_qword_attr(enum attr_idn idn) +{ + return idn == QUERY_ATTR_IDN_TIMESTAMP || + idn == QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID; +} + static int wb_read_resize_attrs(struct ufs_hba *hba, enum attr_idn idn, u32 *attr_val) { @@ -1736,6 +1747,7 @@ static ssize_t _name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct ufs_hba *hba = dev_get_drvdata(dev); \ + u64 qword_value; \ u32 value; \ int ret; \ u8 index = 0; \ @@ -1748,14 +1760,24 @@ static ssize_t _name##_show(struct device *dev, \ if (ufshcd_is_wb_attrs(QUERY_ATTR_IDN##_uname)) \ index = ufshcd_wb_get_query_index(hba); \ ufshcd_rpm_get_sync(hba); \ - ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, \ - QUERY_ATTR_IDN##_uname, index, 0, &value); \ + if (ufshcd_is_qword_attr(QUERY_ATTR_IDN##_uname)) \ + ret = ufshcd_query_attr_qword(hba, \ + UPIU_QUERY_OPCODE_READ_ATTR, \ + QUERY_ATTR_IDN##_uname, \ + index, 0, &qword_value); \ + else \ + ret = ufshcd_query_attr(hba, \ + UPIU_QUERY_OPCODE_READ_ATTR, \ + QUERY_ATTR_IDN##_uname, index, 0, &value); \ ufshcd_rpm_put_sync(hba); \ if (ret) { \ ret = -EINVAL; \ goto out; \ } \ - ret = sysfs_emit(buf, "0x%08X\n", value); \ + if (ufshcd_is_qword_attr(QUERY_ATTR_IDN##_uname)) \ + ret = sysfs_emit(buf, "0x%016llX\n", qword_value); \ + else \ + ret = sysfs_emit(buf, "0x%08X\n", value); \ out: \ up(&hba->host_sem); \ return ret; \ diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 0a72148cb053..ed1adeb22ec6 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -60,6 +60,8 @@ int ufshcd_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode, u32 *attr_val); int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val); +int ufshcd_query_attr_qword(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 sel, u64 *attr_val); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res); void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit); @@ -106,7 +108,6 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, enum query_opcode desc_op); int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable); -int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba, u64 *exception_id); int ufshcd_uic_tx_eqtr(struct ufs_hba *hba, int gear); void ufshcd_apply_valid_tx_eq_settings(struct ufs_hba *hba); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 4805e40ed4d7..c92e0409c793 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -3611,6 +3611,67 @@ int ufshcd_query_attr_retry(struct ufs_hba *hba, return ret; } +/** + * ufshcd_query_attr_qword - Function of sending query requests for quad-word attributes + * @hba: per-adapter instance + * @opcode: attribute opcode + * @idn: attribute idn to access + * @index: index field + * @sel: selector field + * @attr_val: the attribute value after the query request completes + * + * Return: 0 for success, non-zero in case of failure. + */ +int ufshcd_query_attr_qword(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 sel, u64 *attr_val) +{ + struct utp_upiu_query_v4_0 *upiu_req; + struct utp_upiu_query_v4_0 *upiu_resp; + struct ufs_query_req *request = NULL; + struct ufs_query_res *response = NULL; + int err; + + if (!attr_val) { + dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n", + __func__, opcode); + return -EINVAL; + } + + ufshcd_dev_man_lock(hba); + + ufshcd_init_query(hba, &request, &response, opcode, idn, index, sel); + + switch (opcode) { + case UPIU_QUERY_OPCODE_WRITE_ATTR: + request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST; + upiu_req = (struct utp_upiu_query_v4_0 *)&request->upiu_req; + put_unaligned_be64(*attr_val, &upiu_req->osf3); + break; + case UPIU_QUERY_OPCODE_READ_ATTR: + request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST; + break; + default: + dev_err(hba->dev, "%s: Expected query attr opcode but got = 0x%.2x\n", + __func__, opcode); + err = -EINVAL; + goto out_unlock; + } + + err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout); + if (err) { + dev_err(hba->dev, "%s: opcode 0x%.2x for idn %d failed, index %d, selector %d, err = %d\n", + __func__, opcode, idn, index, sel, err); + goto out_unlock; + } + + upiu_resp = (struct utp_upiu_query_v4_0 *)response; + *attr_val = get_unaligned_be64(&upiu_resp->osf3); + +out_unlock: + ufshcd_dev_man_unlock(hba); + return err; +} + /* * Return: 0 upon success; > 0 in case the UFS device reported an OCS error; * < 0 if another error occurred. @@ -6224,46 +6285,6 @@ static void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba) __func__, err); } -/* - * Return: 0 upon success; > 0 in case the UFS device reported an OCS error; - * < 0 if another error occurred. - */ -int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba, u64 *exception_id) -{ - struct utp_upiu_query_v4_0 *upiu_resp; - struct ufs_query_req *request = NULL; - struct ufs_query_res *response = NULL; - int err; - - if (hba->dev_info.wspecversion < 0x410) - return -EOPNOTSUPP; - - ufshcd_hold(hba); - mutex_lock(&hba->dev_cmd.lock); - - ufshcd_init_query(hba, &request, &response, - UPIU_QUERY_OPCODE_READ_ATTR, - QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID, 0, 0); - - request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST; - - err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout); - - if (err) { - dev_err(hba->dev, "%s: failed to read device level exception %d\n", - __func__, err); - goto out; - } - - upiu_resp = (struct utp_upiu_query_v4_0 *)response; - *exception_id = get_unaligned_be64(&upiu_resp->osf3); -out: - mutex_unlock(&hba->dev_cmd.lock); - ufshcd_release(hba); - - return err; -} - static int __ufshcd_wb_toggle(struct ufs_hba *hba, bool set, enum flag_idn idn) { u8 index; @@ -9113,35 +9134,20 @@ static int ufshcd_device_params_init(struct ufs_hba *hba) static void ufshcd_set_timestamp_attr(struct ufs_hba *hba) { - int err; - struct ufs_query_req *request = NULL; - struct ufs_query_res *response = NULL; struct ufs_dev_info *dev_info = &hba->dev_info; - struct utp_upiu_query_v4_0 *upiu_data; + u64 ts_ns; + int err; if (dev_info->wspecversion < 0x400 || hba->dev_quirks & UFS_DEVICE_QUIRK_NO_TIMESTAMP_SUPPORT) return; - ufshcd_dev_man_lock(hba); - - ufshcd_init_query(hba, &request, &response, - UPIU_QUERY_OPCODE_WRITE_ATTR, - QUERY_ATTR_IDN_TIMESTAMP, 0, 0); - - request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST; - - upiu_data = (struct utp_upiu_query_v4_0 *)&request->upiu_req; - - put_unaligned_be64(ktime_get_real_ns(), &upiu_data->osf3); - - err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout); - + ts_ns = ktime_get_real_ns(); + err = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_TIMESTAMP, 0, 0, &ts_ns); if (err) dev_err(hba->dev, "%s: failed to set timestamp %d\n", __func__, err); - - ufshcd_dev_man_unlock(hba); } /** From 949af038b6d2a41c54502179c5a8ddfb3d57dd17 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Fri, 24 Apr 2026 08:14:20 -0700 Subject: [PATCH 0442/5214] scsi: ufs: core: Add support to retrieve and store TX Equalization settings Add support for UFS v5.0 JEDEC attributes qTxEQGnSettings and wTxEQGnSettingsExt to enable persistent storage and retrieval of optimal TX Equalization settings. This provides a fast-path for TX Equalization by reusing previously stored optimal settings, avoiding TX Equalization Training (EQTR) procedures during subsequent Power Mode changes. When no valid TX Equalization settings are found, fall back to full TX EQTR procedures and optionally save the results for future use. The validity of one set of TX Equalization settings is indicated by Bit[15] in wTxEQGnSettingsExt. Signed-off-by: Can Guo Reviewed-by: Peter Wang Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260424151420.111675-3-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-txeq.c | 287 +++++++++++++++++++++++++++++++++ drivers/ufs/core/ufshcd-priv.h | 2 + drivers/ufs/core/ufshcd.c | 5 + include/ufs/ufs.h | 2 + include/ufs/ufshcd.h | 2 + 5 files changed, 298 insertions(+) diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index b2dc89124353..4b264adfdf49 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -14,6 +14,9 @@ #include #include "ufshcd-priv.h" +#define TX_EQ_SETTING_MASK 0x7 +#define TX_EQ_SETTINGS_VALID_BIT BIT(15) + static bool use_adaptive_txeq; module_param(use_adaptive_txeq, bool, 0644); MODULE_PARM_DESC(use_adaptive_txeq, "Find and apply optimal TX Equalization settings before changing Power Mode (default: false)"); @@ -40,6 +43,28 @@ static bool txeq_presets_selected[UFS_TX_EQ_PRESET_MAX] = {[0 ... (UFS_TX_EQ_PRE module_param_array(txeq_presets_selected, bool, NULL, 0644); MODULE_PARM_DESC(txeq_presets_selected, "Use only the selected Presets out of the 8 TX Equalization Presets for TX EQTR"); +static int txeq_setting_sel_set(const char *val, const struct kernel_param *kp) +{ + return param_set_uint_minmax(val, kp, 0, 1); +} + +static const struct kernel_param_ops txeq_setting_sel_ops = { + .set = txeq_setting_sel_set, + .get = param_get_uint, +}; + +static unsigned int txeq_setting_sel; +module_param_cb(txeq_setting_sel, &txeq_setting_sel_ops, &txeq_setting_sel, 0644); +MODULE_PARM_DESC(txeq_setting_sel, "The qTxEQGnSettings and wTxEQGnSettingsExt Attributes selector used to retrieve and store TX Equalization settings"); + +static bool retrieve_txeq_setting = true; +module_param(retrieve_txeq_setting, bool, 0644); +MODULE_PARM_DESC(retrieve_txeq_setting, "Retrieve TX Equalization settings from qTxEQGnSettings and wTxEQGnSettingsExt Attributes (default: true)"); + +static bool store_txeq_setting = true; +module_param(store_txeq_setting, bool, 0644); +MODULE_PARM_DESC(store_txeq_setting, "Store the optimal TX Equalization settings to qTxEQGnSettings and wTxEQGnSettingsExt Attributes (default: true)"); + /* * ufs_tx_eq_preset - Table of minimum required list of presets. * @@ -117,6 +142,126 @@ static const u32 pa_tx_eq_setting[UFS_HS_GEAR_MAX] = { PA_TXEQG6SETTING }; +/* + * Decode Device TX Equalization PreShoot value based on qTxEQGnSettings bit assignment: + * bit[3:0]: Device TX Logical LANE 0 PreShoot + * bit[7:4]: Device TX Logical LANE 1 PreShoot + */ +static inline u8 tx_eq_device_preshoot_decode(u64 eq, u8 lane) +{ + return (u8)((eq >> (lane * TX_HS_PRESHOOT_SHIFT)) & TX_EQ_SETTING_MASK); +} + +/* + * Decode Device TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment: + * bit[19:16]: Device TX Logical LANE 0 DeEmphasis + * bit[23:20]: Device TX Logical LANE 1 DeEmphasis + */ +static inline u8 tx_eq_device_deemphasis_decode(u64 eq, u8 lane) +{ + return (u8)((eq >> (lane * TX_HS_DEEMPHASIS_SHIFT + 16)) & TX_EQ_SETTING_MASK); +} + +/* + * Decode Host TX Equalization PreShoot value based on qTxEQGnSettings bit assignment: + * bit[35:32]: Host TX Logical LANE 0 PreShoot + * bit[39:36]: Host TX Logical LANE 1 PreShoot + */ +static inline u8 tx_eq_host_preshoot_decode(u64 eq, u8 lane) +{ + return (u8)((eq >> (lane * TX_HS_PRESHOOT_SHIFT + 32)) & TX_EQ_SETTING_MASK); +} + +/* + * Decode Host TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment: + * bit[51:48]: Host TX Logical LANE 0 DeEmphasis + * bit[55:52]: Host TX Logical LANE 1 DeEmphasis + */ +static inline u8 tx_eq_host_deemphasis_decode(u64 eq, u8 lane) +{ + return (u8)((eq >> (lane * TX_HS_DEEMPHASIS_SHIFT + 48)) & TX_EQ_SETTING_MASK); +} + +/* + * Decode Device TX precode_en indication based on wTxEQGnSettingsExt bit assignment: + * bit[0]: PreCodeEn for Device TX Logical LANE 0 + * bit[1]: PreCodeEn for Device TX Logical LANE 1 + */ +static inline bool tx_eq_device_precode_en_decode(u16 eq_ext, u8 lane) +{ + return eq_ext & BIT(lane); +} + +/* + * Decode Host TX precode_en indication based on wTxEQGnSettingsExt bit assignment: + * bit[4]: PreCodeEn for Device RX Logical LANE 0 + * bit[5]: PreCodeEn for Device RX Logical LANE 1 + */ +static inline bool tx_eq_host_precode_en_decode(u16 eq_ext, u8 lane) +{ + return eq_ext & BIT(lane + 4); +} + +/* + * Encode Device TX Equalization PreShoot value based on qTxEQGnSettings bit assignment: + * bit[3:0]: Device TX Logical LANE 0 PreShoot + * bit[7:4]: Device TX Logical LANE 1 PreShoot + */ +static inline u64 tx_eq_device_preshoot_encode(u64 val, u8 lane) +{ + return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_PRESHOOT_SHIFT); +} + +/* + * Encode Device TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment: + * bit[19:16]: Device TX Logical LANE 0 DeEmphasis + * bit[23:20]: Device TX Logical LANE 1 DeEmphasis + */ +static inline u64 tx_eq_device_deemphasis_encode(u64 val, u8 lane) +{ + return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_DEEMPHASIS_SHIFT + 16); +} + +/* + * Encode Host TX Equalization PreShoot value based on qTxEQGnSettings bit assignment: + * bit[35:32]: Host TX Logical LANE 0 PreShoot + * bit[39:36]: Host TX Logical LANE 1 PreShoot + */ +static inline u64 tx_eq_host_preshoot_encode(u64 val, u8 lane) +{ + return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_PRESHOOT_SHIFT + 32); +} + +/* + * Encode Host TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment: + * bit[51:48]: Host TX Logical LANE 0 DeEmphasis + * bit[55:52]: Host TX Logical LANE 1 DeEmphasis + */ +static inline u64 tx_eq_host_deemphasis_encode(u64 val, u8 lane) +{ + return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_DEEMPHASIS_SHIFT + 48); +} + +/* + * Encode Device precode_en based on wTxEQGnSettingsExt bit assignment: + * bit[0]: PreCodeEn for Device TX Logical LANE 0 + * bit[1]: PreCodeEn for Device TX Logical LANE 1 + */ +static inline u16 tx_eq_device_precode_en_encode(bool en, u8 lane) +{ + return (u16)en << lane; +} + +/* + * Encode Host precode_en based on wTxEQGnSettingsExt bit assignment: + * bit[4]: PreCodeEn for Device RX Logical LANE 0 + * bit[5]: PreCodeEn for Device RX Logical LANE 1 + */ +static inline u16 tx_eq_host_precode_en_encode(bool en, u8 lane) +{ + return (u16)en << (lane + 4); +} + /** * ufshcd_configure_precoding - Configure Pre-Coding for all active lanes * @hba: per adapter instance @@ -1164,6 +1309,7 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, /* Mark TX Equalization settings as valid */ params->is_valid = true; + params->is_trained = true; params->is_applied = false; } @@ -1291,3 +1437,144 @@ int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear) return ret; } + +/** + * ufshcd_extract_tx_eq_settings_attrs - Extract TX Equalization settings from UFS attributes + * @hba: per adapter instance + * @gear: target gear + * + * This function extracts previously stored TX Equalization settings from UFS + * attributes qTxEQGnSettings and wTxEQGnSettingsExt. These attributes contain + * the optimal TX Equalization parameters (PreShoot, DeEmphasis, and PreCoding + * enable) that were determined during a previous EQTR procedure. + * + * The function reads: + * 1. qTxEQGnSettings (64-bit): Main attribute containing PreShoot and + * DeEmphasis values for both host and device TX lanes + * 2. wTxEQGnSettingsExt (16-bit): Extended attribute containing PreCoding + * enable flags and validity indicator + */ +static void ufshcd_extract_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear) +{ + struct ufshcd_tx_eq_params *params; + u32 lane, eq_ext; + int ret; + u64 eq; + + ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS_EXT, gear - 1, + (u8)txeq_setting_sel, &eq_ext); + if (ret) + return; + + dev_dbg(hba->dev, "%s: HS-G%u wTxEQGnSettingsExt (Selector %u) = 0x%08x\n", + __func__, gear, txeq_setting_sel, eq_ext); + + if (!(eq_ext & TX_EQ_SETTINGS_VALID_BIT)) + return; + + ret = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS, + gear - 1, (u8)txeq_setting_sel, &eq); + if (ret) + return; + + dev_dbg(hba->dev, "%s: HS-G%u qTxEQGnSettings (Selector %u) = 0x%016llx\n", + __func__, gear, txeq_setting_sel, eq); + + params = &hba->tx_eq_params[gear - 1]; + + for (lane = 0; lane < UFS_MAX_LANES; lane++) { + params->host[lane].preshoot = tx_eq_host_preshoot_decode(eq, lane); + params->host[lane].deemphasis = tx_eq_host_deemphasis_decode(eq, lane); + params->host[lane].precode_en = tx_eq_host_precode_en_decode(eq_ext, lane); + + params->device[lane].preshoot = tx_eq_device_preshoot_decode(eq, lane); + params->device[lane].deemphasis = tx_eq_device_deemphasis_decode(eq, lane); + params->device[lane].precode_en = tx_eq_device_precode_en_decode(eq_ext, lane); + } + + params->is_valid = true; +} + +void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba) +{ + u8 gear = (u8)adaptive_txeq_gear; + + if (!hba->max_pwr_info.is_valid || !ufshcd_is_tx_eq_supported(hba) || + !use_adaptive_txeq || !retrieve_txeq_setting) + return; + + for (; gear <= UFS_HS_GEAR_MAX; gear++) + ufshcd_extract_tx_eq_settings_attrs(hba, gear); +} + +/** + * ufshcd_update_tx_eq_settings_attrs - Update TX EQ settings in UFS attributes + * @hba: per adapter instance + * @gear: target gear + * + * This function stores the optimal TX Equalization settings obtained from + * TX EQTR procedure into UFS device attributes for future fast-path retrieval. + * The settings are stored in two complementary attributes: + * + * 1. qTxEQGnSettings (64-bit): Main attribute containing PreShoot and + * DeEmphasis values for both host and device TX lanes + * 2. wTxEQGnSettingsExt (16-bit): Extended attribute containing PreCoding + * enable flags and validity indicator + */ +static void ufshcd_update_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear) +{ + struct ufshcd_tx_eq_params *params; + u32 lane, eq_ext = 0; + u64 eq = 0; + int ret; + + params = &hba->tx_eq_params[gear - 1]; + if (!params->is_valid || !params->is_trained) + return; + + for (lane = 0; lane < UFS_MAX_LANES; lane++) { + eq |= tx_eq_host_preshoot_encode((u64)params->host[lane].preshoot, lane); + eq |= tx_eq_host_deemphasis_encode((u64)params->host[lane].deemphasis, lane); + eq_ext |= tx_eq_host_precode_en_encode(params->host[lane].precode_en, lane); + + eq |= tx_eq_device_preshoot_encode((u64)params->device[lane].preshoot, lane); + eq |= tx_eq_device_deemphasis_encode((u64)params->device[lane].deemphasis, lane); + eq_ext |= tx_eq_device_precode_en_encode(params->device[lane].precode_en, lane); + } + + /* Set validity flag to indicate valid settings are stored */ + eq_ext |= TX_EQ_SETTINGS_VALID_BIT; + + /* Write qTxEQGnSettings */ + ret = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS, + gear - 1, (u8)txeq_setting_sel, &eq); + if (ret) + return; + + /* Write wTxEQGnSettingsExt */ + ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS_EXT, gear - 1, + (u8)txeq_setting_sel, &eq_ext); + if (ret) + return; + + dev_dbg(hba->dev, "%s: Saved HS-G%u qTxEQGnSettings (Selector %u) = 0x%016llx\n", + __func__, gear, txeq_setting_sel, eq); + dev_dbg(hba->dev, "%s: Saved HS-G%u wTxEQGnSettingsExt (Selector %u) = 0x%08x\n", + __func__, gear, txeq_setting_sel, eq_ext); +} + +void ufshcd_store_tx_eq_settings(struct ufs_hba *hba) +{ + u8 gear = (u8)adaptive_txeq_gear; + + if (!hba->max_pwr_info.is_valid || !ufshcd_is_tx_eq_supported(hba) || + !use_adaptive_txeq || !store_txeq_setting) + return; + + for (; gear <= UFS_HS_GEAR_MAX; gear++) + ufshcd_update_tx_eq_settings_attrs(hba, gear); +} diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index ed1adeb22ec6..70f90d97f217 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -118,6 +118,8 @@ void ufshcd_print_tx_eq_params(struct ufs_hba *hba); bool ufshcd_is_txeq_presets_used(struct ufs_hba *hba); bool ufshcd_is_txeq_preset_selected(u8 preshoot, u8 deemphasis); int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear); +void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba); +void ufshcd_store_tx_eq_settings(struct ufs_hba *hba); /* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index c92e0409c793..a6026cc4b2f4 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9128,6 +9128,8 @@ static int ufshcd_device_params_init(struct ufs_hba *hba) dev_err(hba->dev, "%s: Failed getting max supported power mode\n", __func__); + + ufshcd_retrieve_tx_eq_settings(hba); out: return ret; } @@ -10748,6 +10750,9 @@ static void ufshcd_wl_shutdown(struct scsi_device *sdev) /* Turn on everything while shutting down */ ufshcd_rpm_get_sync(hba); + + ufshcd_store_tx_eq_settings(hba); + scsi_device_quiesce(sdev); shost_for_each_device(sdev, hba->host) { if (sdev == hba->ufs_device_wlun) diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index 602aa34c9822..0d48e137d66d 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -191,6 +191,8 @@ enum attr_idn { QUERY_ATTR_IDN_WB_BUF_RESIZE_HINT = 0x3C, QUERY_ATTR_IDN_WB_BUF_RESIZE_EN = 0x3D, QUERY_ATTR_IDN_WB_BUF_RESIZE_STATUS = 0x3E, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS = 0x47, + QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS_EXT = 0x48, }; /* Descriptor idn for Query requests */ diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index cfbc75d8df83..f48d6416e299 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -358,6 +358,7 @@ struct ufshcd_tx_eqtr_record { * @eqtr_record: Pointer to TX EQTR record * @is_valid: True if parameter contains valid TX Equalization settings * @is_applied: True if settings have been applied to UniPro of both sides + * @is_trained: True if parameters obtained from TX EQTR procedure */ struct ufshcd_tx_eq_params { struct ufshcd_tx_eq_settings host[UFS_MAX_LANES]; @@ -365,6 +366,7 @@ struct ufshcd_tx_eq_params { struct ufshcd_tx_eqtr_record *eqtr_record; bool is_valid; bool is_applied; + bool is_trained; }; /** From 334f7bcbdc79dc8e5b938ac3372453a5368221cf Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 4 May 2026 22:53:26 +0800 Subject: [PATCH 0443/5214] pinctrl: sunxi: fix regulator leak in sunxi_pmx_request() error path In the error path of sunxi_pmx_request(), the code calls regulator_put(s_reg->regulator) to release the regulator. However, s_reg->regulator is only assigned after a successful regulator_enable(). This causes a memory leak: the regulator obtained via regulator_get() is never properly released when regulator_enable() fails. Fixes: dc1445584177 ("pinctrl: sunxi: Fix and simplify pin bank regulator handling") Signed-off-by: Felix Gu Reviewed-by: Andre Przywara Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index d3042e0c9712..25489beeb312 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -925,7 +925,7 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) return 0; out: - regulator_put(s_reg->regulator); + regulator_put(reg); return ret; } From 1ea146943de776d0528facf031129420be64f07a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:07:25 +0200 Subject: [PATCH 0444/5214] dt-bindings: pinctrl: describe the Qualcomm nord-tlmm Add a DT binding document describing the TLMM pin controller available on the Nord platforms from Qualcomm. Co-developed-by: Shawn Guo Signed-off-by: Shawn Guo Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- .../bindings/pinctrl/qcom,nord-tlmm.yaml | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,nord-tlmm.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,nord-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,nord-tlmm.yaml new file mode 100644 index 000000000000..4bb511719f31 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,nord-tlmm.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,nord-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SA8797P TLMM block + +maintainers: + - Bartosz Golaszewski + +description: + Top Level Mode Multiplexer pin controller in Qualcomm SA8797P SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,nord-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 90 + + gpio-line-names: + maxItems: 181 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-nord-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-nord-tlmm-state" + additionalProperties: false + +$defs: + qcom-nord-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-7][0-9]|180)$" + - enum: [ ufs_reset ] + minItems: 1 + maxItems: 16 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ aoss_cti, atest_char, atest_usb20, atest_usb21, + aud_intfc0_clk, aud_intfc0_data, aud_intfc0_ws, + aud_intfc10_clk, aud_intfc10_data, aud_intfc10_ws, + aud_intfc1_clk, aud_intfc1_data, aud_intfc1_ws, + aud_intfc2_clk, aud_intfc2_data, aud_intfc2_ws, + aud_intfc3_clk, aud_intfc3_data, aud_intfc3_ws, + aud_intfc4_clk, aud_intfc4_data, aud_intfc4_ws, + aud_intfc5_clk, aud_intfc5_data, aud_intfc5_ws, + aud_intfc6_clk, aud_intfc6_data, aud_intfc6_ws, + aud_intfc7_clk, aud_intfc7_data, aud_intfc7_ws, + aud_intfc8_clk, aud_intfc8_data, aud_intfc8_ws, + aud_intfc9_clk, aud_intfc9_data, aud_intfc9_ws, + aud_mclk0_mira, aud_mclk0_mirb, aud_mclk1_mira, aud_mclk1_mirb, + aud_mclk2_mira, aud_mclk2_mirb, aud_refclk0, aud_refclk1, + bist_done, ccu_async_in, ccu_i2c_scl, ccu_i2c_sda, ccu_timer, + clink_debug, dbg_out, dbg_out_clk, + ddr_bist_complete, ddr_bist_fail, ddr_bist_start, ddr_bist_stop, + ddr_pxi, dp_rx0, dp_rx00, dp_rx01, dp_rx0_mute, dp_rx1, dp_rx10, + dp_rx11, dp_rx1_mute, + edp0_hot, edp0_lcd, edp1_hot, edp1_lcd, edp2_hot, edp2_lcd, + edp3_hot, edp3_lcd, + emac0_mcg, emac0_mdc, emac0_mdio, emac0_ptp, emac1_mcg, + emac1_mdc, emac1_mdio, emac1_ptp, + gcc_gp1_clk, gcc_gp2_clk, gcc_gp3_clk, gcc_gp4_clk, gcc_gp5_clk, + gcc_gp6_clk, gcc_gp7_clk, gcc_gp8_clk, jitter_bist, lbist_pass, + mbist_pass, mdp0_vsync_out, mdp1_vsync_out, mdp_vsync_e, + mdp_vsync_p, mdp_vsync_s, + pcie0_clk_req_n, pcie1_clk_req_n, pcie2_clk_req_n, + pcie3_clk_req_n, phase_flag, pll_bist_sync, pll_clk_aux, + prng_rosc0, prng_rosc1, pwrbrk_i_n, qdss, qdss_cti, qspi, + qup0_se0, qup0_se1, qup0_se2, qup0_se3, qup0_se4, qup0_se5, + qup1_se0, qup1_se1, qup1_se3, qup1_se2, qup1_se4, qup1_se5, + qup1_se6, qup2_se0, qup2_se1, qup2_se2, qup2_se3, qup2_se4, + qup2_se5, qup2_se6, + sailss_ospi, sdc4_clk, sdc4_cmd, sdc4_data, smb_alert, + smb_alert_n, smb_clk, smb_dat, tb_trig_sdc4, tmess_prng0, + tmess_prng1, tsc_timer, tsense_pwm, usb0_hs, + usb0_phy_ps, usb1_hs, usb1_phy_ps, usb2_hs, usxgmii0_phy, + usxgmii1_phy, vsense_trigger_mirnat, wcn_sw, wcn_sw_ctrl] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@f100000 { + compatible = "qcom,nord-tlmm"; + reg = <0x0f100000 0xc0000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&tlmm 0 0 181>; + wakeup-parent = <&pdc>; + + qup_uart15_default: qup-uart15-default-state { + pins = "gpio147", "gpio148"; + function = "qup2_se2"; + drive-strength = <2>; + bias-disable; + }; + }; +... From c24dd0826f064d6b99a74ca0e004a4cda6677b9f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:07:26 +0200 Subject: [PATCH 0445/5214] pinctrl: qcom: add the TLMM driver for the Nord platforms Add support for the TLMM controller on the Qualcomm Nord platform. Co-developed-by: Shawn Guo Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Reviewed-by: Maulik Shah Reviewed-by: Pankaj Patil Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig.msm | 8 + drivers/pinctrl/qcom/Makefile | 1 + drivers/pinctrl/qcom/pinctrl-nord.c | 1771 +++++++++++++++++++++++++++ 3 files changed, 1780 insertions(+) create mode 100644 drivers/pinctrl/qcom/pinctrl-nord.c diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index b78d6410ed2e..8b4d3bce585f 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -238,6 +238,14 @@ config PINCTRL_MSM8998 This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm MSM8998 platform. +config PINCTRL_NORD + tristate "Qualcomm Nord pin controller driver" + depends on ARM64 || COMPILE_TEST + default ARCH_QCOM + help + This is the pinctrl, pinmux and pinconf driver for the Qualcomm + TLMM block found on the Qualcomm NORD platforms. + config PINCTRL_QCM2290 tristate "Qualcomm QCM2290 pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index f0bb1920b27b..4262988c7d96 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_MDM9607) += pinctrl-mdm9607.o obj-$(CONFIG_PINCTRL_MDM9615) += pinctrl-mdm9615.o obj-$(CONFIG_PINCTRL_MILOS) += pinctrl-milos.o obj-$(CONFIG_PINCTRL_MILOS_LPASS_LPI) += pinctrl-milos-lpass-lpi.o +obj-$(CONFIG_PINCTRL_NORD) += pinctrl-nord.o obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o diff --git a/drivers/pinctrl/qcom/pinctrl-nord.c b/drivers/pinctrl/qcom/pinctrl-nord.c new file mode 100644 index 000000000000..f14361101bf4 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-nord.c @@ -0,0 +1,1771 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_wakeup_present_bit = 6, \ + .intr_wakeup_enable_bit = 7, \ + .intr_target_bit = 8, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 /* egpio mode */ \ + }, \ + .nfuncs = 12, \ + } + +#define UFS_RESET(pg_name, ctl, io) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = io, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc nord_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "UFS_RESET"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); + +static const unsigned int ufs_reset_pins[] = { 181 }; + +enum nord_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb20, + msm_mux_atest_usb21, + msm_mux_aud_intfc0_clk, + msm_mux_aud_intfc0_data, + msm_mux_aud_intfc0_ws, + msm_mux_aud_intfc10_clk, + msm_mux_aud_intfc10_data, + msm_mux_aud_intfc10_ws, + msm_mux_aud_intfc1_clk, + msm_mux_aud_intfc1_data, + msm_mux_aud_intfc1_ws, + msm_mux_aud_intfc2_clk, + msm_mux_aud_intfc2_data, + msm_mux_aud_intfc2_ws, + msm_mux_aud_intfc3_clk, + msm_mux_aud_intfc3_data, + msm_mux_aud_intfc3_ws, + msm_mux_aud_intfc4_clk, + msm_mux_aud_intfc4_data, + msm_mux_aud_intfc4_ws, + msm_mux_aud_intfc5_clk, + msm_mux_aud_intfc5_data, + msm_mux_aud_intfc5_ws, + msm_mux_aud_intfc6_clk, + msm_mux_aud_intfc6_data, + msm_mux_aud_intfc6_ws, + msm_mux_aud_intfc7_clk, + msm_mux_aud_intfc7_data, + msm_mux_aud_intfc7_ws, + msm_mux_aud_intfc8_clk, + msm_mux_aud_intfc8_data, + msm_mux_aud_intfc8_ws, + msm_mux_aud_intfc9_clk, + msm_mux_aud_intfc9_data, + msm_mux_aud_intfc9_ws, + msm_mux_aud_mclk0_mira, + msm_mux_aud_mclk0_mirb, + msm_mux_aud_mclk1_mira, + msm_mux_aud_mclk1_mirb, + msm_mux_aud_mclk2_mira, + msm_mux_aud_mclk2_mirb, + msm_mux_aud_refclk0, + msm_mux_aud_refclk1, + msm_mux_bist_done, + msm_mux_ccu_async_in, + msm_mux_ccu_i2c_scl, + msm_mux_ccu_i2c_sda, + msm_mux_ccu_timer, + msm_mux_clink_debug, + msm_mux_dbg_out, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist_complete, + msm_mux_ddr_bist_fail, + msm_mux_ddr_bist_start, + msm_mux_ddr_bist_stop, + msm_mux_ddr_pxi, + msm_mux_dp_rx0, + msm_mux_dp_rx00, + msm_mux_dp_rx01, + msm_mux_dp_rx0_mute, + msm_mux_dp_rx1, + msm_mux_dp_rx10, + msm_mux_dp_rx11, + msm_mux_dp_rx1_mute, + msm_mux_edp0_hot, + msm_mux_edp0_lcd, + msm_mux_edp1_hot, + msm_mux_edp1_lcd, + msm_mux_edp2_hot, + msm_mux_edp2_lcd, + msm_mux_edp3_hot, + msm_mux_edp3_lcd, + msm_mux_emac0_mcg, + msm_mux_emac0_mdc, + msm_mux_emac0_mdio, + msm_mux_emac0_ptp, + msm_mux_emac1_mcg, + msm_mux_emac1_mdc, + msm_mux_emac1_mdio, + msm_mux_emac1_ptp, + msm_mux_gcc_gp1_clk, + msm_mux_gcc_gp2_clk, + msm_mux_gcc_gp3_clk, + msm_mux_gcc_gp4_clk, + msm_mux_gcc_gp5_clk, + msm_mux_gcc_gp6_clk, + msm_mux_gcc_gp7_clk, + msm_mux_gcc_gp8_clk, + msm_mux_jitter_bist, + msm_mux_lbist_pass, + msm_mux_mbist_pass, + msm_mux_mdp0_vsync_out, + msm_mux_mdp1_vsync_out, + msm_mux_mdp_vsync_e, + msm_mux_mdp_vsync_p, + msm_mux_mdp_vsync_s, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie1_clk_req_n, + msm_mux_pcie2_clk_req_n, + msm_mux_pcie3_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_pwrbrk_i_n, + msm_mux_qdss, + msm_mux_qdss_cti, + msm_mux_qspi, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup0_se5, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup3_se0_mira, + msm_mux_qup3_se0_mirb, + msm_mux_sailss_ospi, + msm_mux_sdc4_clk, + msm_mux_sdc4_cmd, + msm_mux_sdc4_data, + msm_mux_smb_alert, + msm_mux_smb_alert_n, + msm_mux_smb_clk, + msm_mux_smb_dat, + msm_mux_tb_trig_sdc4, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tsc_timer, + msm_mux_tsense_pwm, + msm_mux_usb0_hs, + msm_mux_usb0_phy_ps, + msm_mux_usb1_hs, + msm_mux_usb1_phy_ps, + msm_mux_usb2_hs, + msm_mux_usxgmii0_phy, + msm_mux_usxgmii1_phy, + msm_mux_vsense_trigger_mirnat, + msm_mux_wcn_sw, + msm_mux_wcn_sw_ctrl, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", + "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", + "gpio126", "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", + "gpio132", "gpio133", "gpio134", "gpio135", "gpio136", "gpio137", + "gpio138", "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", + "gpio144", "gpio145", "gpio146", "gpio147", "gpio148", "gpio149", + "gpio150", "gpio151", "gpio152", "gpio153", "gpio154", "gpio155", + "gpio156", "gpio157", "gpio158", "gpio159", "gpio160", "gpio161", + "gpio162", "gpio163", "gpio164", "gpio165", "gpio166", "gpio167", + "gpio168", "gpio169", "gpio170", "gpio171", "gpio172", "gpio173", + "gpio174", "gpio175", "gpio176", "gpio177", "gpio178", "gpio179", + "gpio180", +}; + +static const char *const aoss_cti_groups[] = { + "gpio83", + "gpio84", + "gpio85", + "gpio86", +}; + +static const char *const atest_char_groups[] = { + "gpio176", "gpio177", "gpio178", "gpio179", "gpio180", +}; + +static const char *const atest_usb20_groups[] = { + "gpio126", + "gpio128", + "gpio130", +}; + +static const char *const atest_usb21_groups[] = { + "gpio127", + "gpio129", + "gpio131", +}; + +static const char *const aud_intfc0_clk_groups[] = { + "gpio57", +}; + +static const char *const aud_intfc0_data_groups[] = { + "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", "gpio66", +}; + +static const char *const aud_intfc0_ws_groups[] = { + "gpio58", +}; + +static const char *const aud_intfc10_clk_groups[] = { + "gpio61", +}; + +static const char *const aud_intfc10_data_groups[] = { + "gpio81", "gpio82", +}; + +static const char *const aud_intfc10_ws_groups[] = { + "gpio62", +}; + +static const char *const aud_intfc1_clk_groups[] = { + "gpio67", +}; + +static const char *const aud_intfc1_data_groups[] = { + "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", +}; + +static const char *const aud_intfc1_ws_groups[] = { + "gpio68", +}; + +static const char *const aud_intfc2_clk_groups[] = { + "gpio77", +}; + +static const char *const aud_intfc2_data_groups[] = { + "gpio79", "gpio80", "gpio81", "gpio82", +}; + +static const char *const aud_intfc2_ws_groups[] = { + "gpio78", +}; + +static const char *const aud_intfc3_clk_groups[] = { + "gpio83", +}; + +static const char *const aud_intfc3_data_groups[] = { + "gpio85", "gpio86", +}; + +static const char *const aud_intfc3_ws_groups[] = { + "gpio84", +}; + +static const char *const aud_intfc4_clk_groups[] = { + "gpio87", +}; + +static const char *const aud_intfc4_data_groups[] = { + "gpio89", "gpio90", +}; + +static const char *const aud_intfc4_ws_groups[] = { + "gpio88", +}; + +static const char *const aud_intfc5_clk_groups[] = { + "gpio91", +}; + +static const char *const aud_intfc5_data_groups[] = { + "gpio93", "gpio94", +}; + +static const char *const aud_intfc5_ws_groups[] = { + "gpio92", +}; + +static const char *const aud_intfc6_clk_groups[] = { + "gpio95", +}; + +static const char *const aud_intfc6_data_groups[] = { + "gpio97", "gpio98", +}; + +static const char *const aud_intfc6_ws_groups[] = { + "gpio96", +}; + +static const char *const aud_intfc7_clk_groups[] = { + "gpio63", +}; + +static const char *const aud_intfc7_data_groups[] = { + "gpio65", "gpio66", +}; + +static const char *const aud_intfc7_ws_groups[] = { + "gpio64", +}; + +static const char *const aud_intfc8_clk_groups[] = { + "gpio73", +}; + +static const char *const aud_intfc8_data_groups[] = { + "gpio75", "gpio76", +}; + +static const char *const aud_intfc8_ws_groups[] = { + "gpio74", +}; + +static const char *const aud_intfc9_clk_groups[] = { + "gpio70", +}; + +static const char *const aud_intfc9_data_groups[] = { + "gpio72", +}; + +static const char *const aud_intfc9_ws_groups[] = { + "gpio71", +}; + +static const char *const aud_mclk0_mira_groups[] = { + "gpio99", +}; + +static const char *const aud_mclk0_mirb_groups[] = { + "gpio86", +}; + +static const char *const aud_mclk1_mira_groups[] = { + "gpio100", +}; + +static const char *const aud_mclk1_mirb_groups[] = { + "gpio90", +}; + +static const char *const aud_mclk2_mira_groups[] = { + "gpio101", +}; + +static const char *const aud_mclk2_mirb_groups[] = { + "gpio94", +}; + +static const char *const aud_refclk0_groups[] = { + "gpio100", +}; + +static const char *const aud_refclk1_groups[] = { + "gpio101", +}; + +static const char *const bist_done_groups[] = { + "gpio168", +}; + +static const char *const ccu_async_in_groups[] = { + "gpio45", "gpio176", "gpio177", "gpio178", "gpio179", "gpio180", +}; + +static const char *const ccu_i2c_scl_groups[] = { + "gpio16", "gpio18", "gpio20", "gpio22", "gpio24", + "gpio114", "gpio116", "gpio126", "gpio130", "gpio132", +}; + +static const char *const ccu_i2c_sda_groups[] = { + "gpio15", "gpio17", "gpio19", "gpio21", "gpio23", + "gpio113", "gpio115", "gpio125", "gpio129", "gpio131", +}; + +static const char *const ccu_timer_groups[] = { + "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30", + "gpio31", "gpio32", "gpio33", "gpio34", "gpio143", "gpio144", + "gpio150", "gpio151", "gpio152", "gpio153", +}; + +static const char *const clink_debug_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio51", + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char *const dbg_out_groups[] = { + "gpio113", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio165", +}; + +static const char *const ddr_bist_complete_groups[] = { + "gpio37", +}; + +static const char *const ddr_bist_fail_groups[] = { + "gpio39", +}; + +static const char *const ddr_bist_start_groups[] = { + "gpio36", +}; + +static const char *const ddr_bist_stop_groups[] = { + "gpio38", +}; + +static const char *const ddr_pxi_groups[] = { + "gpio99", "gpio100", "gpio109", "gpio110", "gpio113", "gpio114", + "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", "gpio120", + "gpio121", "gpio122", "gpio126", "gpio127", "gpio128", "gpio129", + "gpio130", "gpio131", "gpio132", "gpio133", "gpio134", "gpio135", + "gpio136", "gpio137", "gpio138", "gpio139", "gpio162", "gpio163", + "gpio164", "gpio165", +}; + +static const char *const dp_rx0_groups[] = { + "gpio55", "gpio83", "gpio84", "gpio85", "gpio86", + "gpio88", "gpio89", "gpio137", "gpio138", +}; + +static const char *const dp_rx00_groups[] = { + "gpio99", +}; + +static const char *const dp_rx01_groups[] = { + "gpio100", +}; + +static const char *const dp_rx0_mute_groups[] = { + "gpio35", +}; + +static const char *const dp_rx1_groups[] = { + "gpio56", "gpio92", "gpio93", "gpio95", "gpio96", + "gpio97", "gpio98", "gpio158", "gpio159", +}; + +static const char *const dp_rx10_groups[] = { + "gpio121", +}; + +static const char *const dp_rx11_groups[] = { + "gpio122", +}; + +static const char *const dp_rx1_mute_groups[] = { + "gpio36", +}; + +static const char *const edp0_hot_groups[] = { + "gpio51", +}; + +static const char *const edp0_lcd_groups[] = { + "gpio47", +}; + +static const char *const edp1_hot_groups[] = { + "gpio52", +}; + +static const char *const edp1_lcd_groups[] = { + "gpio48", +}; + +static const char *const edp2_hot_groups[] = { + "gpio53", +}; + +static const char *const edp2_lcd_groups[] = { + "gpio49", +}; + +static const char *const edp3_hot_groups[] = { + "gpio54", +}; + +static const char *const edp3_lcd_groups[] = { + "gpio50", +}; + +static const char *const emac0_mcg_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char *const emac0_mdc_groups[] = { + "gpio47", +}; + +static const char *const emac0_mdio_groups[] = { + "gpio48", +}; + +static const char *const emac0_ptp_groups[] = { + "gpio133", "gpio134", "gpio135", "gpio136", + "gpio139", "gpio140", "gpio141", "gpio142", +}; + +static const char *const emac1_mcg_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char *const emac1_mdc_groups[] = { + "gpio49", +}; + +static const char *const emac1_mdio_groups[] = { + "gpio50", +}; + +static const char *const emac1_ptp_groups[] = { + "gpio37", "gpio38", "gpio39", "gpio40", + "gpio41", "gpio42", "gpio43", "gpio44", +}; + +static const char *const gcc_gp1_clk_groups[] = { + "gpio51", +}; + +static const char *const gcc_gp2_clk_groups[] = { + "gpio52", +}; + +static const char *const gcc_gp3_clk_groups[] = { + "gpio42", +}; + +static const char *const gcc_gp4_clk_groups[] = { + "gpio43", +}; + +static const char *const gcc_gp5_clk_groups[] = { + "gpio105", +}; + +static const char *const gcc_gp6_clk_groups[] = { + "gpio106", +}; + +static const char *const gcc_gp7_clk_groups[] = { + "gpio13", +}; + +static const char *const gcc_gp8_clk_groups[] = { + "gpio14", +}; + +static const char *const jitter_bist_groups[] = { + "gpio123", + "gpio138", +}; + +static const char *const lbist_pass_groups[] = { + "gpio121", +}; + +static const char *const mbist_pass_groups[] = { + "gpio122", +}; + +static const char *const mdp0_vsync_out_groups[] = { + "gpio113", "gpio114", "gpio115", "gpio116", "gpio121", "gpio122", + "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", +}; + +static const char *const mdp1_vsync_out_groups[] = { + "gpio123", "gpio124", "gpio125", "gpio126", "gpio129", "gpio130", + "gpio131", "gpio132", "gpio133", "gpio134", "gpio135", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio109", +}; + +static const char *const mdp_vsync_p_groups[] = { + "gpio110", +}; + +static const char *const mdp_vsync_s_groups[] = { + "gpio144", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio1", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio4", +}; + +static const char *const pcie2_clk_req_n_groups[] = { + "gpio7", +}; + +static const char *const pcie3_clk_req_n_groups[] = { + "gpio10", +}; + +static const char *const phase_flag_groups[] = { + "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", + "gpio75", "gpio76", "gpio77", "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", + "gpio83", "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", + "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio98", "gpio101", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio176", +}; + +static const char *const pll_clk_aux_groups[] = { + "gpio100", +}; + +static const char *const prng_rosc0_groups[] = { + "gpio117", +}; + +static const char *const prng_rosc1_groups[] = { + "gpio118", +}; + +static const char *const pwrbrk_i_n_groups[] = { + "gpio167", +}; + +static const char *const qdss_cti_groups[] = { + "gpio41", "gpio42", "gpio110", "gpio138", + "gpio142", "gpio144", "gpio162", "gpio163", +}; + +static const char *const qdss_groups[] = { + "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", + "gpio75", "gpio76", "gpio77", "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", + "gpio83", "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", + "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio108", +}; + +static const char *const qspi_groups[] = { + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", "gpio108", +}; + +static const char *const qup0_se0_groups[] = { + "gpio109", "gpio110", "gpio111", "gpio112", +}; + +static const char *const qup0_se1_groups[] = { + "gpio109", "gpio110", "gpio111", "gpio112", +}; + +static const char *const qup0_se2_groups[] = { + "gpio113", "gpio114", "gpio115", "gpio116", +}; + +static const char *const qup0_se3_groups[] = { + "gpio113", "gpio114", "gpio115", "gpio116", +}; + +static const char *const qup0_se4_groups[] = { + "gpio117", "gpio118", "gpio119", "gpio120", +}; + +static const char *const qup0_se5_groups[] = { + "gpio109", "gpio110", "gpio121", "gpio122", +}; + +static const char *const qup1_se0_groups[] = { + "gpio123", "gpio124", "gpio125", "gpio126", +}; + +static const char *const qup1_se1_groups[] = { + "gpio123", "gpio124", "gpio125", "gpio126", +}; + +static const char *const qup1_se2_groups[] = { + "gpio127", "gpio128", "gpio129", "gpio130", +}; + +static const char *const qup1_se3_groups[] = { + "gpio129", "gpio130", +}; + +static const char *const qup1_se4_groups[] = { + "gpio131", "gpio132", "gpio137", "gpio138", +}; + +static const char *const qup1_se5_groups[] = { + "gpio133", "gpio134", "gpio135", "gpio136", +}; + +static const char *const qup1_se6_groups[] = { + "gpio131", "gpio132", "gpio137", "gpio138", +}; + +static const char *const qup2_se0_groups[] = { + "gpio139", "gpio140", "gpio141", "gpio142", +}; + +static const char *const qup2_se1_groups[] = { + "gpio143", "gpio144", "gpio154", "gpio155", +}; + +static const char *const qup2_se2_groups[] = { + "gpio145", "gpio146", "gpio147", "gpio148", "gpio149", +}; + +static const char *const qup2_se3_groups[] = { + "gpio150", "gpio151", "gpio152", "gpio153", +}; + +static const char *const qup2_se4_groups[] = { + "gpio143", "gpio144", "gpio150", "gpio151", + "gpio152", "gpio154", "gpio155", +}; + +static const char *const qup2_se5_groups[] = { + "gpio156", "gpio157", "gpio158", "gpio159", +}; + +static const char *const qup2_se6_groups[] = { + "gpio156", "gpio157", "gpio158", "gpio159", +}; + +static const char *const qup3_se0_mira_groups[] = { + "gpio102", "gpio103", "gpio104", "gpio105", + "gpio106", "gpio107", "gpio108", +}; + +static const char *const qup3_se0_mirb_groups[] = { + "gpio102", "gpio103", +}; + +static const char *const sailss_ospi_groups[] = { + "gpio164", + "gpio165", +}; + +static const char *const sdc4_clk_groups[] = { + "gpio175", +}; + +static const char *const sdc4_cmd_groups[] = { + "gpio174", +}; + +static const char *const sdc4_data_groups[] = { + "gpio170", + "gpio171", + "gpio172", + "gpio173", +}; + +static const char *const smb_alert_groups[] = { + "gpio110", +}; + +static const char *const smb_alert_n_groups[] = { + "gpio109", +}; + +static const char *const smb_clk_groups[] = { + "gpio112", +}; + +static const char *const smb_dat_groups[] = { + "gpio111", +}; + +static const char *const tb_trig_sdc4_groups[] = { + "gpio169", +}; + +static const char *const tmess_prng0_groups[] = { + "gpio94", +}; + +static const char *const tmess_prng1_groups[] = { + "gpio95", +}; + +static const char *const tsc_timer_groups[] = { + "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", +}; + +static const char *const tsense_pwm_groups[] = { + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", "gpio50", +}; + +static const char *const usb0_hs_groups[] = { + "gpio12", +}; + +static const char *const usb0_phy_ps_groups[] = { + "gpio164", +}; + +static const char *const usb1_hs_groups[] = { + "gpio13", +}; + +static const char *const usb1_phy_ps_groups[] = { + "gpio165", +}; + +static const char *const usb2_hs_groups[] = { + "gpio14", +}; + +static const char *const usxgmii0_phy_groups[] = { + "gpio45", +}; + +static const char *const usxgmii1_phy_groups[] = { + "gpio46", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio132", +}; + +static const char *const wcn_sw_groups[] = { + "gpio161", +}; + +static const char *const wcn_sw_ctrl_groups[] = { + "gpio160", +}; + +static const struct pinfunction nord_functions[] = { + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb20), + MSM_PIN_FUNCTION(atest_usb21), + MSM_PIN_FUNCTION(aud_intfc0_clk), + MSM_PIN_FUNCTION(aud_intfc0_data), + MSM_PIN_FUNCTION(aud_intfc0_ws), + MSM_PIN_FUNCTION(aud_intfc10_clk), + MSM_PIN_FUNCTION(aud_intfc10_data), + MSM_PIN_FUNCTION(aud_intfc10_ws), + MSM_PIN_FUNCTION(aud_intfc1_clk), + MSM_PIN_FUNCTION(aud_intfc1_data), + MSM_PIN_FUNCTION(aud_intfc1_ws), + MSM_PIN_FUNCTION(aud_intfc2_clk), + MSM_PIN_FUNCTION(aud_intfc2_data), + MSM_PIN_FUNCTION(aud_intfc2_ws), + MSM_PIN_FUNCTION(aud_intfc3_clk), + MSM_PIN_FUNCTION(aud_intfc3_data), + MSM_PIN_FUNCTION(aud_intfc3_ws), + MSM_PIN_FUNCTION(aud_intfc4_clk), + MSM_PIN_FUNCTION(aud_intfc4_data), + MSM_PIN_FUNCTION(aud_intfc4_ws), + MSM_PIN_FUNCTION(aud_intfc5_clk), + MSM_PIN_FUNCTION(aud_intfc5_data), + MSM_PIN_FUNCTION(aud_intfc5_ws), + MSM_PIN_FUNCTION(aud_intfc6_clk), + MSM_PIN_FUNCTION(aud_intfc6_data), + MSM_PIN_FUNCTION(aud_intfc6_ws), + MSM_PIN_FUNCTION(aud_intfc7_clk), + MSM_PIN_FUNCTION(aud_intfc7_data), + MSM_PIN_FUNCTION(aud_intfc7_ws), + MSM_PIN_FUNCTION(aud_intfc8_clk), + MSM_PIN_FUNCTION(aud_intfc8_data), + MSM_PIN_FUNCTION(aud_intfc8_ws), + MSM_PIN_FUNCTION(aud_intfc9_clk), + MSM_PIN_FUNCTION(aud_intfc9_data), + MSM_PIN_FUNCTION(aud_intfc9_ws), + MSM_PIN_FUNCTION(aud_mclk0_mira), + MSM_PIN_FUNCTION(aud_mclk0_mirb), + MSM_PIN_FUNCTION(aud_mclk1_mira), + MSM_PIN_FUNCTION(aud_mclk1_mirb), + MSM_PIN_FUNCTION(aud_mclk2_mira), + MSM_PIN_FUNCTION(aud_mclk2_mirb), + MSM_PIN_FUNCTION(aud_refclk0), + MSM_PIN_FUNCTION(aud_refclk1), + MSM_PIN_FUNCTION(bist_done), + MSM_PIN_FUNCTION(ccu_async_in), + MSM_PIN_FUNCTION(ccu_i2c_scl), + MSM_PIN_FUNCTION(ccu_i2c_sda), + MSM_PIN_FUNCTION(ccu_timer), + MSM_PIN_FUNCTION(clink_debug), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist_complete), + MSM_PIN_FUNCTION(ddr_bist_fail), + MSM_PIN_FUNCTION(ddr_bist_start), + MSM_PIN_FUNCTION(ddr_bist_stop), + MSM_PIN_FUNCTION(ddr_pxi), + MSM_PIN_FUNCTION(dp_rx0), + MSM_PIN_FUNCTION(dp_rx00), + MSM_PIN_FUNCTION(dp_rx01), + MSM_PIN_FUNCTION(dp_rx0_mute), + MSM_PIN_FUNCTION(dp_rx1), + MSM_PIN_FUNCTION(dp_rx10), + MSM_PIN_FUNCTION(dp_rx11), + MSM_PIN_FUNCTION(dp_rx1_mute), + MSM_PIN_FUNCTION(edp0_hot), + MSM_PIN_FUNCTION(edp0_lcd), + MSM_PIN_FUNCTION(edp1_hot), + MSM_PIN_FUNCTION(edp1_lcd), + MSM_PIN_FUNCTION(edp2_hot), + MSM_PIN_FUNCTION(edp2_lcd), + MSM_PIN_FUNCTION(edp3_hot), + MSM_PIN_FUNCTION(edp3_lcd), + MSM_PIN_FUNCTION(emac0_mcg), + MSM_PIN_FUNCTION(emac0_mdc), + MSM_PIN_FUNCTION(emac0_mdio), + MSM_PIN_FUNCTION(emac0_ptp), + MSM_PIN_FUNCTION(emac1_mcg), + MSM_PIN_FUNCTION(emac1_mdc), + MSM_PIN_FUNCTION(emac1_mdio), + MSM_PIN_FUNCTION(emac1_ptp), + MSM_PIN_FUNCTION(gcc_gp1_clk), + MSM_PIN_FUNCTION(gcc_gp2_clk), + MSM_PIN_FUNCTION(gcc_gp3_clk), + MSM_PIN_FUNCTION(gcc_gp4_clk), + MSM_PIN_FUNCTION(gcc_gp5_clk), + MSM_PIN_FUNCTION(gcc_gp6_clk), + MSM_PIN_FUNCTION(gcc_gp7_clk), + MSM_PIN_FUNCTION(gcc_gp8_clk), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(lbist_pass), + MSM_PIN_FUNCTION(mbist_pass), + MSM_PIN_FUNCTION(mdp0_vsync_out), + MSM_PIN_FUNCTION(mdp1_vsync_out), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(mdp_vsync_p), + MSM_PIN_FUNCTION(mdp_vsync_s), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(pcie2_clk_req_n), + MSM_PIN_FUNCTION(pcie3_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(pwrbrk_i_n), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qspi), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup3_se0_mira), + MSM_PIN_FUNCTION(qup3_se0_mirb), + MSM_PIN_FUNCTION(sailss_ospi), + MSM_PIN_FUNCTION(sdc4_clk), + MSM_PIN_FUNCTION(sdc4_cmd), + MSM_PIN_FUNCTION(sdc4_data), + MSM_PIN_FUNCTION(smb_alert), + MSM_PIN_FUNCTION(smb_alert_n), + MSM_PIN_FUNCTION(smb_clk), + MSM_PIN_FUNCTION(smb_dat), + MSM_PIN_FUNCTION(tb_trig_sdc4), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(tsc_timer), + MSM_PIN_FUNCTION(tsense_pwm), + MSM_PIN_FUNCTION(usb0_hs), + MSM_PIN_FUNCTION(usb0_phy_ps), + MSM_PIN_FUNCTION(usb1_hs), + MSM_PIN_FUNCTION(usb1_phy_ps), + MSM_PIN_FUNCTION(usb2_hs), + MSM_PIN_FUNCTION(usxgmii0_phy), + MSM_PIN_FUNCTION(usxgmii1_phy), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wcn_sw), + MSM_PIN_FUNCTION(wcn_sw_ctrl), +}; + +/* Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup nord_groups[] = { + [0] = PINGROUP(0, _, _, _, _, _, _, _, _, _, _, _), + [1] = PINGROUP(1, pcie0_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [2] = PINGROUP(2, _, _, _, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, _, _, _, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, pcie1_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, _, _, _, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, _, _, _, _, _, _, _, _, _, _, _), + [7] = PINGROUP(7, pcie2_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, _, _, _, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, _, _, _, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, pcie3_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, _, _, _, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, usb0_hs, clink_debug, _, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, usb1_hs, clink_debug, gcc_gp7_clk, _, _, _, _, _, _, _, _), + [14] = PINGROUP(14, usb2_hs, clink_debug, gcc_gp8_clk, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, ccu_i2c_sda, _, _, _, _, _, _, _, _, _, _), + [16] = PINGROUP(16, ccu_i2c_scl, emac0_mcg, _, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, ccu_i2c_sda, emac0_mcg, _, _, _, _, _, _, _, _, _), + [18] = PINGROUP(18, ccu_i2c_scl, emac0_mcg, _, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, ccu_i2c_sda, emac0_mcg, _, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, ccu_i2c_scl, emac1_mcg, _, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, ccu_i2c_sda, emac1_mcg, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, ccu_i2c_scl, emac1_mcg, _, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, ccu_i2c_sda, emac1_mcg, _, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, ccu_i2c_scl, _, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, ccu_timer, tsc_timer, _, _, _, _, _, _, _, _, _), + [35] = PINGROUP(35, dp_rx0_mute, _, _, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, dp_rx1_mute, ddr_bist_start, _, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, emac1_ptp, ddr_bist_complete, _, _, _, _, _, _, _, _, _), + [38] = PINGROUP(38, emac1_ptp, ddr_bist_stop, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, emac1_ptp, ddr_bist_fail, _, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, emac1_ptp, _, _, _, _, _, _, _, _, _, _), + [41] = PINGROUP(41, emac1_ptp, qdss_cti, _, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, emac1_ptp, qdss_cti, gcc_gp3_clk, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, emac1_ptp, gcc_gp4_clk, tsense_pwm, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, emac1_ptp, tsense_pwm, _, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, usxgmii0_phy, ccu_async_in, tsense_pwm, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, usxgmii1_phy, tsense_pwm, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, emac0_mdc, edp0_lcd, tsense_pwm, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, emac0_mdio, edp1_lcd, tsense_pwm, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, emac1_mdc, edp2_lcd, tsense_pwm, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, emac1_mdio, edp3_lcd, tsense_pwm, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, edp0_hot, clink_debug, gcc_gp1_clk, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, edp1_hot, clink_debug, gcc_gp2_clk, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, edp2_hot, clink_debug, _, _, _, _, _, _, _, _, _), + [54] = PINGROUP(54, edp3_hot, clink_debug, _, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, dp_rx0, clink_debug, _, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, dp_rx1, _, _, _, _, _, _, _, _, _, _), + [57] = PINGROUP(57, aud_intfc0_clk, _, _, _, _, _, _, _, _, _, _), + [58] = PINGROUP(58, aud_intfc0_ws, _, _, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, aud_intfc0_data, _, _, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, aud_intfc0_data, _, _, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, aud_intfc0_data, aud_intfc10_clk, _, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, aud_intfc0_data, aud_intfc10_ws, _, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, aud_intfc0_data, aud_intfc7_clk, _, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, aud_intfc0_data, aud_intfc7_ws, _, _, _, _, _, _, _, _, _), + [65] = PINGROUP(65, aud_intfc0_data, aud_intfc7_data, _, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, aud_intfc0_data, aud_intfc7_data, _, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, aud_intfc1_clk, phase_flag, _, qdss, _, _, _, _, _, _, _), + [68] = PINGROUP(68, aud_intfc1_ws, phase_flag, _, qdss, _, _, _, _, _, _, _), + [69] = PINGROUP(69, aud_intfc1_data, phase_flag, _, qdss, _, _, _, _, _, _, _), + [70] = PINGROUP(70, aud_intfc1_data, aud_intfc9_clk, phase_flag, + _, qdss, _, _, _, _, _, _), + [71] = PINGROUP(71, aud_intfc1_data, aud_intfc9_ws, phase_flag, + _, qdss, _, _, _, _, _, _), + [72] = PINGROUP(72, aud_intfc1_data, aud_intfc9_data, phase_flag, + _, qdss, _, _, _, _, _, _), + [73] = PINGROUP(73, aud_intfc1_data, aud_intfc8_clk, phase_flag, + _, qdss, _, _, _, _, _, _), + [74] = PINGROUP(74, aud_intfc1_data, aud_intfc8_ws, phase_flag, + _, qdss, _, _, _, _, _, _), + [75] = PINGROUP(75, aud_intfc1_data, aud_intfc8_data, phase_flag, + _, qdss, _, _, _, _, _, _), + [76] = PINGROUP(76, aud_intfc1_data, aud_intfc8_data, phase_flag, + _, qdss, _, _, _, _, _, _), + [77] = PINGROUP(77, aud_intfc2_clk, phase_flag, _, qdss, _, _, _, _, _, _, _), + [78] = PINGROUP(78, aud_intfc2_ws, phase_flag, _, qdss, _, _, _, _, _, _, _), + [79] = PINGROUP(79, aud_intfc2_data, phase_flag, _, qdss, _, _, _, _, _, _, _), + [80] = PINGROUP(80, aud_intfc2_data, phase_flag, _, _, qdss, _, _, _, _, _, _), + [81] = PINGROUP(81, aud_intfc2_data, aud_intfc10_data, phase_flag, + _, _, qdss, _, _, _, _, _), + [82] = PINGROUP(82, aud_intfc2_data, aud_intfc10_data, phase_flag, + _, qdss, _, _, _, _, _, _), + [83] = PINGROUP(83, aud_intfc3_clk, dp_rx0, aoss_cti, phase_flag, _, qdss, + _, _, _, _, _), + [84] = PINGROUP(84, aud_intfc3_ws, dp_rx0, aoss_cti, phase_flag, _, qdss, + _, _, _, _, _), + [85] = PINGROUP(85, aud_intfc3_data, dp_rx0, aoss_cti, phase_flag, + _, qdss, _, _, _, _, _), + [86] = PINGROUP(86, aud_intfc3_data, aud_mclk0_mirb, dp_rx0, aoss_cti, phase_flag, + _, qdss, _, _, _, _), + [87] = PINGROUP(87, aud_intfc4_clk, phase_flag, _, qdss, _, _, _, _, _, _, _), + [88] = PINGROUP(88, aud_intfc4_ws, dp_rx0, phase_flag, _, qdss, _, _, _, _, _, _), + [89] = PINGROUP(89, aud_intfc4_data, dp_rx0, phase_flag, _, qdss, + _, _, _, _, _, _), + [90] = PINGROUP(90, aud_intfc4_data, aud_mclk1_mirb, phase_flag, + _, qdss, _, _, _, _, _, _), + [91] = PINGROUP(91, aud_intfc5_clk, phase_flag, _, qdss, _, _, _, _, _, _, _), + [92] = PINGROUP(92, aud_intfc5_ws, dp_rx1, phase_flag, _, qdss, _, _, _, _, _, _), + [93] = PINGROUP(93, aud_intfc5_data, dp_rx1, phase_flag, _, qdss, + _, _, _, _, _, _), + [94] = PINGROUP(94, aud_intfc5_data, aud_mclk2_mirb, phase_flag, tmess_prng0, + _, qdss, _, _, _, _, _), + [95] = PINGROUP(95, aud_intfc6_clk, dp_rx1, phase_flag, tmess_prng1, + _, qdss, _, _, _, _, _), + [96] = PINGROUP(96, aud_intfc6_ws, dp_rx1, phase_flag, _, qdss, + _, _, _, _, _, _), + [97] = PINGROUP(97, aud_intfc6_data, dp_rx1, qdss, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, aud_intfc6_data, dp_rx1, phase_flag, _, qdss, + _, _, _, _, _, _), + [99] = PINGROUP(99, aud_mclk0_mira, qdss, dp_rx00, ddr_pxi, _, _, _, _, _, _, _), + [100] = PINGROUP(100, aud_mclk1_mira, aud_refclk0, pll_clk_aux, + qdss, dp_rx01, ddr_pxi, _, _, _, _, _), + [101] = PINGROUP(101, aud_mclk2_mira, aud_refclk1, phase_flag, _, qdss, + _, _, _, _, _, _), + [102] = PINGROUP(102, qspi, qup3_se0_mira, qup3_se0_mirb, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, qspi, qup3_se0_mira, qup3_se0_mirb, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, qspi, qup3_se0_mira, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, qspi, qup3_se0_mira, gcc_gp5_clk, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, qspi, qup3_se0_mira, gcc_gp6_clk, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, qspi, qup3_se0_mira, _, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, qspi, qup3_se0_mira, qdss, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, qup0_se0, qup0_se1, qup0_se5, mdp_vsync_e, + smb_alert_n, _, ddr_pxi, _, _, _, _), + [110] = PINGROUP(110, qup0_se0, qup0_se1, qup0_se5, qdss_cti, + mdp_vsync_p, smb_alert, _, ddr_pxi, _, _, _), + [111] = PINGROUP(111, qup0_se1, qup0_se0, smb_dat, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, qup0_se1, qup0_se0, smb_clk, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, qup0_se2, qup0_se3, ccu_i2c_sda, mdp0_vsync_out, + dbg_out, ddr_pxi, _, _, _, _, _), + [114] = PINGROUP(114, qup0_se2, qup0_se3, ccu_i2c_scl, mdp0_vsync_out, + _, ddr_pxi, _, _, _, _, _), + [115] = PINGROUP(115, qup0_se3, qup0_se2, ccu_i2c_sda, mdp0_vsync_out, + _, ddr_pxi, _, _, _, _, _), + [116] = PINGROUP(116, qup0_se3, qup0_se2, ccu_i2c_scl, mdp0_vsync_out, + _, ddr_pxi, _, _, _, _, _), + [117] = PINGROUP(117, qup0_se4, prng_rosc0, _, ddr_pxi, _, _, _, _, _, _, _), + [118] = PINGROUP(118, qup0_se4, prng_rosc1, _, ddr_pxi, _, _, _, _, _, _, _), + [119] = PINGROUP(119, qup0_se4, _, ddr_pxi, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, qup0_se4, _, ddr_pxi, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, qup0_se5, lbist_pass, mdp0_vsync_out, _, dp_rx10, ddr_pxi, + _, _, _, _, _), + [122] = PINGROUP(122, qup0_se5, mbist_pass, mdp0_vsync_out, _, dp_rx11, ddr_pxi, + _, _, _, _, _), + [123] = PINGROUP(123, qup1_se0, qup1_se1, mdp1_vsync_out, jitter_bist, + _, _, _, _, _, _, _), + [124] = PINGROUP(124, qup1_se0, qup1_se1, mdp1_vsync_out, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, qup1_se1, qup1_se0, ccu_i2c_sda, mdp1_vsync_out, + _, _, _, _, _, _, _), + [126] = PINGROUP(126, qup1_se1, qup1_se0, ccu_i2c_scl, mdp1_vsync_out, + _, atest_usb20, ddr_pxi, _, _, _, _), + [127] = PINGROUP(127, qup1_se2, qup1_se2, _, atest_usb21, ddr_pxi, + _, _, _, _, _, _), + [128] = PINGROUP(128, qup1_se2, qup1_se2, _, atest_usb20, ddr_pxi, + _, _, _, _, _, _), + [129] = PINGROUP(129, qup1_se3, qup1_se3, ccu_i2c_sda, mdp1_vsync_out, + _, atest_usb21, ddr_pxi, _, _, _, _), + [130] = PINGROUP(130, qup1_se3, qup1_se3, ccu_i2c_scl, mdp1_vsync_out, + _, atest_usb20, ddr_pxi, _, _, _, _), + [131] = PINGROUP(131, qup1_se4, qup1_se6, ccu_i2c_sda, mdp1_vsync_out, + _, atest_usb21, ddr_pxi, _, _, _, _), + [132] = PINGROUP(132, qup1_se4, qup1_se6, ccu_i2c_scl, mdp1_vsync_out, + _, vsense_trigger_mirnat, ddr_pxi, _, _, _, _), + [133] = PINGROUP(133, qup1_se5, emac0_ptp, mdp1_vsync_out, _, ddr_pxi, + _, _, _, _, _, _), + [134] = PINGROUP(134, qup1_se5, emac0_ptp, mdp1_vsync_out, _, ddr_pxi, + _, _, _, _, _, _), + [135] = PINGROUP(135, qup1_se5, emac0_ptp, mdp1_vsync_out, _, ddr_pxi, + _, _, _, _, _, _), + [136] = PINGROUP(136, qup1_se5, emac0_ptp, _, ddr_pxi, _, _, _, _, _, _, _), + [137] = PINGROUP(137, qup1_se6, qup1_se4, dp_rx0, _, ddr_pxi, _, _, _, _, _, _), + [138] = PINGROUP(138, qup1_se6, qup1_se4, dp_rx0, qdss_cti, jitter_bist, ddr_pxi, + _, _, _, _, _), + [139] = PINGROUP(139, qup2_se0, emac0_ptp, mdp0_vsync_out, ddr_pxi, + _, _, _, _, _, _, _), + [140] = PINGROUP(140, qup2_se0, emac0_ptp, mdp0_vsync_out, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, qup2_se0, emac0_ptp, mdp0_vsync_out, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, qup2_se0, emac0_ptp, qdss_cti, mdp0_vsync_out, _, _, _, _, _, _, _), + [143] = PINGROUP(143, qup2_se1, qup2_se4, ccu_timer, mdp0_vsync_out, + _, _, _, _, _, _, _), + [144] = PINGROUP(144, qup2_se1, qup2_se4, ccu_timer, qdss_cti, mdp_vsync_s, + _, _, _, _, _, _), + [145] = PINGROUP(145, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, qup2_se3, qup2_se4, ccu_timer, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, qup2_se3, qup2_se4, ccu_timer, _, _, _, _, _, _, _, _), + [152] = PINGROUP(152, qup2_se3, qup2_se4, ccu_timer, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, qup2_se3, ccu_timer, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, qup2_se4, qup2_se1, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, qup2_se4, qup2_se1, _, _, _, _, _, _, _, _, _), + [156] = PINGROUP(156, qup2_se5, qup2_se6, _, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, qup2_se5, qup2_se6, _, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, qup2_se6, qup2_se5, dp_rx1, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, qup2_se6, qup2_se5, dp_rx1, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, wcn_sw_ctrl, _, _, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, wcn_sw, _, _, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, qdss_cti, _, ddr_pxi, _, _, _, _, _, _, _, _), + [163] = PINGROUP(163, qdss_cti, _, ddr_pxi, _, _, _, _, _, _, _, _), + [164] = PINGROUP(164, usb0_phy_ps, _, sailss_ospi, ddr_pxi, _, _, _, _, _, _, _), + [165] = PINGROUP(165, usb1_phy_ps, dbg_out_clk, sailss_ospi, ddr_pxi, + _, _, _, _, _, _, _), + [166] = PINGROUP(166, _, _, _, _, _, _, _, _, _, _, _), + [167] = PINGROUP(167, pwrbrk_i_n, _, _, _, _, _, _, _, _, _, _), + [168] = PINGROUP(168, bist_done, _, _, _, _, _, _, _, _, _, _), + [169] = PINGROUP(169, tb_trig_sdc4, _, _, _, _, _, _, _, _, _, _), + [170] = PINGROUP(170, sdc4_data, _, _, _, _, _, _, _, _, _, _), + [171] = PINGROUP(171, sdc4_data, _, _, _, _, _, _, _, _, _, _), + [172] = PINGROUP(172, sdc4_data, _, _, _, _, _, _, _, _, _, _), + [173] = PINGROUP(173, sdc4_data, _, _, _, _, _, _, _, _, _, _), + [174] = PINGROUP(174, sdc4_cmd, _, _, _, _, _, _, _, _, _, _), + [175] = PINGROUP(175, sdc4_clk, _, _, _, _, _, _, _, _, _, _), + [176] = PINGROUP(176, ccu_async_in, pll_bist_sync, atest_char, + _, _, _, _, _, _, _, _), + [177] = PINGROUP(177, ccu_async_in, atest_char, _, _, _, _, _, _, _, _, _), + [178] = PINGROUP(178, ccu_async_in, atest_char, _, _, _, _, _, _, _, _, _), + [179] = PINGROUP(179, ccu_async_in, atest_char, _, _, _, _, _, _, _, _, _), + [180] = PINGROUP(180, ccu_async_in, atest_char, _, _, _, _, _, _, _, _, _), + [181] = UFS_RESET(ufs_reset, 0xbd004, 0xbe000), +}; + +static const struct msm_gpio_wakeirq_map nord_pdc_map[] = { + { 0, 67 }, { 1, 68 }, { 2, 82 }, { 3, 69 }, { 4, 70 }, + { 5, 83 }, { 6, 71 }, { 7, 72 }, { 8, 84 }, { 9, 73 }, + { 10, 119 }, { 11, 85 }, { 45, 107 }, { 46, 98 }, { 102, 77 }, + { 108, 78 }, { 110, 120 }, { 114, 80 }, { 116, 81 }, { 120, 117 }, + { 124, 108 }, { 126, 99 }, { 128, 100 }, { 132, 101 }, { 138, 87 }, + { 142, 88 }, { 144, 89 }, { 153, 90 }, { 157, 91 }, { 159, 118 }, + { 160, 110 }, { 161, 79 }, { 166, 109 }, { 168, 111 }, +}; + +static const struct msm_pinctrl_soc_data nord_tlmm = { + .pins = nord_pins, + .npins = ARRAY_SIZE(nord_pins), + .functions = nord_functions, + .nfunctions = ARRAY_SIZE(nord_functions), + .groups = nord_groups, + .ngroups = ARRAY_SIZE(nord_groups), + .ngpios = 182, + .wakeirq_map = nord_pdc_map, + .nwakeirq_map = ARRAY_SIZE(nord_pdc_map), + .egpio_func = 11, +}; + +static const struct of_device_id nord_tlmm_of_match[] = { + { .compatible = "qcom,nord-tlmm", .data = &nord_tlmm }, + {}, +}; + +static int nord_tlmm_probe(struct platform_device *pdev) +{ + const struct msm_pinctrl_soc_data *pinctrl_data; + struct device *dev = &pdev->dev; + + pinctrl_data = device_get_match_data(dev); + if (!pinctrl_data) + return -EINVAL; + + return msm_pinctrl_probe(pdev, &nord_tlmm); +} + +static struct platform_driver nord_tlmm_driver = { + .driver = { + .name = "nord-tlmm", + .of_match_table = nord_tlmm_of_match, + }, + .probe = nord_tlmm_probe, +}; + +static int __init nord_tlmm_init(void) +{ + return platform_driver_register(&nord_tlmm_driver); +} +arch_initcall(nord_tlmm_init); + +static void __exit nord_tlmm_exit(void) +{ + platform_driver_unregister(&nord_tlmm_driver); +} +module_exit(nord_tlmm_exit); + +MODULE_DESCRIPTION("QTI Nord TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, nord_tlmm_of_match); From 65e0242c375284bd5b884106cd722591291d0e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Wed, 25 Mar 2026 13:01:18 +0100 Subject: [PATCH 0446/5214] media: mgb4: Fix DV timings limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide the real DV timings limits in VIDIOC_DV_TIMINGS_CAP. For the outputs the pixelclock is limited by the CMT table <25000kHz, 2*94642kHz>, for the inputs a slightly broader range is possible. The minimal supported/tested resolution is 64px. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_vin.c | 8 ++++---- drivers/media/pci/mgb4/mgb4_vout.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 74fb00c4a408..b24785455691 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -85,12 +85,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 20000000, + .max_pixelclock = 200000000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 7725bcd55e4c..22fbc9e53819 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -53,12 +53,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 25000000, + .max_pixelclock = 189284000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | From e024767f90f9f50bfcce4b20bb74237ad72450f3 Mon Sep 17 00:00:00 2001 From: Ken Lin Date: Thu, 2 Apr 2026 15:50:08 +0800 Subject: [PATCH 0447/5214] media: platform: cros-ec: Add Kulnex and Moxoe to the match table The Google Kulnex and Moxoe device uses the same approach as Google Brask which enables the HDMI CEC via the cros-ec-cec driver. Signed-off-by: Ken Lin Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 419b9a7abcce..4b3f9bff6067 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -334,6 +334,10 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dirks", "0000:00:02.0", port_ab_conns }, /* Google Moxie */ { "Google", "Moxie", "0000:00:02.0", port_b_conns }, + /* Google Kulnex */ + { "Google", "Kulnex", "0000:00:02.0", port_b_conns }, + /* Google Moxoe */ + { "Google", "Moxoe", "0000:00:02.0", port_b_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, From 560d29f4eef9fdac49fd5373502c42df4fc92752 Mon Sep 17 00:00:00 2001 From: Kells Ping Date: Mon, 20 Apr 2026 10:37:14 +0800 Subject: [PATCH 0448/5214] media: platform: cros-ec: Add Dirkson to the match table The Google Dirkson device uses the same approach as the Google Brask which enables the HDMI CEC via the cros-ec-cec driver. Signed-off-by: Kells Ping Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 4b3f9bff6067..ec1da9cd3674 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -338,6 +338,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Kulnex", "0000:00:02.0", port_b_conns }, /* Google Moxoe */ { "Google", "Moxoe", "0000:00:02.0", port_b_conns }, + /* Google Dirkson */ + { "Google", "Dirkson", "0000:00:02.0", port_ab_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, From c3a78691be8245e52ced489f268e413f18061ac2 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Fri, 24 Apr 2026 23:36:01 +0900 Subject: [PATCH 0449/5214] media: cec: seco: unregister adapter on IR probe failure If secocec_ir_probe() fails after cec_register_adapter() succeeds, probe returns an error and the driver remove callback is not called. The current unwind path unregisters the notifier and then falls through to cec_delete_adapter(), which violates the CEC adapter lifetime rules after a successful registration. Add a registered-adapter unwind path that unregisters the notifier and the adapter instead. Fixes: daef95769b3a ("media: seco-cec: add Consumer-IR support") Cc: stable@vger.kernel.org Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/seco/seco-cec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index b7bb49f02395..97ed9654c78a 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev) ret = secocec_ir_probe(secocec); if (ret) - goto err_notifier; + goto err_unregister_adapter; platform_set_drvdata(pdev, secocec); @@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev) return ret; +err_unregister_adapter: + cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); + cec_unregister_adapter(secocec->cec_adap); + goto err; err_notifier: cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); err_delete_adapter: From 7e49bb89df860bb7352974100cd5dd48752bb646 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Sun, 2 Nov 2025 23:58:34 +0200 Subject: [PATCH 0450/5214] thunderbolt: Avoid reserved fields in path config space for USB4 routers According to USB4 spec, USB4 Connection Manager shall not change value of any fields that are defined as "RsvdZ" or "VD". Specifically fields: Path Credits Allocated, IFC, ISE fields in path config space shall not be written by CM. To handle this, CM shall first read current path config space from the hardware, change only the fields that can be changed, and then write back the path config space. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/path.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 8713ea0f47c1..0092b2ec7873 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -412,7 +412,8 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, * in the USB4 spec so we clear them * only for pre-USB4 adapters. */ - if (!tb_switch_is_usb4(port->sw)) { + if (tb_port_is_null(port) || + !tb_switch_is_usb4(port->sw)) { hop.ingress_fc = 0; hop.ingress_shared_buffer = 0; } @@ -532,15 +533,18 @@ int tb_path_activate(struct tb_path *path) __tb_path_deactivate_hop(path->hops[i].in_port, path->hops[i].in_hop_index, path->clear_fc); - /* dword 0 */ + /* Needed for USB4 routers, read path config space before write */ + res = tb_port_read(path->hops[i].in_port, &hop, TB_CFG_HOPS, + 2 * path->hops[i].in_hop_index, 2); + if (res) + goto err; + hop.next_hop = path->hops[i].next_hop_index; hop.out_port = path->hops[i].out_port->port; - hop.initial_credits = path->hops[i].initial_credits; hop.pmps = path->hops[i].pm_support; hop.unknown1 = 0; hop.enable = 1; - /* dword 1 */ out_mask = (i == path->path_length - 1) ? TB_PATH_DESTINATION : TB_PATH_INTERNAL; in_mask = (i == 0) ? TB_PATH_SOURCE : TB_PATH_INTERNAL; @@ -550,12 +554,21 @@ int tb_path_activate(struct tb_path *path) hop.drop_packages = path->drop_packages; hop.counter = path->hops[i].in_counter_index; hop.counter_enable = path->hops[i].in_counter_index != -1; - hop.ingress_fc = path->ingress_fc_enable & in_mask; hop.egress_fc = path->egress_fc_enable & out_mask; - hop.ingress_shared_buffer = path->ingress_shared_buffer - & in_mask; - hop.egress_shared_buffer = path->egress_shared_buffer - & out_mask; + hop.egress_shared_buffer = path->egress_shared_buffer & out_mask; + /* + * Protocol adapters IFC and ISE bits, and Path Credits + * Allocated are vendor defined in the USB4 spec so we + * program them only for pre-USB4 and lane adapters. + */ + if (tb_port_is_null(path->hops[i].in_port) || + !tb_switch_is_usb4(path->hops[i].in_port->sw)) { + hop.initial_credits = path->hops[i].initial_credits; + hop.ingress_fc = path->ingress_fc_enable & in_mask; + hop.ingress_shared_buffer = + path->ingress_shared_buffer & in_mask; + } + hop.unknown3 = 0; tb_port_dbg(path->hops[i].in_port, "Writing hop %d\n", i); From 7c7345bcde6c611fa8fa13e624207c6005798aa8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 11 Nov 2025 17:52:01 +0200 Subject: [PATCH 0451/5214] thunderbolt: Don't disable lane adapter if XDomain lane bonding isn't possible This happens when firmware connection manager is being used. It will deal with disabling the lane 1 adapter after the tunnel has been established and re-enabling it afterwards. For this reason only do this when we know that lane bonding is possible (e.g running software connection manager). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 754808c43f00..57367e18733a 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1931,7 +1931,13 @@ static void tb_xdomain_link_exit(struct tb_xdomain *xd) if (tb_port_get_link_generation(down) >= 4) { down->bonded = false; down->dual_link_port->bonded = false; - } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) { + return; + } + + if (!xd->bonding_possible) + return; + + if (xd->link_width > TB_LINK_WIDTH_SINGLE) { /* * Just return port structures back to way they were and * update credits. No need to update userspace because From 2fb199dc64056ada4aa820a68931fed60333c289 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 11 Aug 2023 12:59:09 +0300 Subject: [PATCH 0452/5214] thunderbolt: Make XDomain lane bonding comply with the USB4 v2 spec The USB4 v2 Inter-Domain spec "unified" the lane bonding flow so that when the other end (with higher UUID) is not yet set the target link width accordingly it is expected to reply with ERROR_NOT_READY. Implement this for Linux. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 65 +++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 57367e18733a..680b2204875a 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -535,29 +535,19 @@ static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, } static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, - struct tb_xdomain *xd, u8 sequence) + struct tb_xdomain *xd, u8 sequence, + u8 slw, u8 sls, u8 tls, u8 tlw) { struct tb_xdp_link_state_status_response res; - struct tb_port *port = tb_xdomain_downstream_port(xd); - u32 val[2]; - int ret; memset(&res, 0, sizeof(res)); tb_xdp_fill_header(&res.hdr, xd->route, sequence, LINK_STATE_STATUS_RESPONSE, sizeof(res)); - ret = tb_port_read(port, val, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val)); - if (ret) - return ret; - - res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> - LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; - res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> - LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; - res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; - res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> - LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; + res.slw = slw; + res.sls = sls; + res.tls = tls; + res.tlw = tlw; return __tb_xdomain_response(ctl, &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP); @@ -804,8 +794,47 @@ static void tb_xdp_handle_request(struct work_struct *work) route); if (xd) { - ret = tb_xdp_link_state_status_response(tb, ctl, xd, - sequence); + struct tb_port *port = tb_xdomain_downstream_port(xd); + u8 slw, sls, tls, tlw; + u32 val[2]; + + /* + * Read the adapter supported and target widths + * and speeds. + */ + ret = tb_port_read(port, val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_0, + ARRAY_SIZE(val)); + if (ret) + break; + + slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> + LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; + sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> + LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; + tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; + tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> + LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; + + /* + * When we have higher UUID, we are supposed to + * return ERROR_NOT_READY if the tlw is not yet + * set according to the Inter-Domain spec for + * USB4 v2. + */ + if (xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH && + xd->target_link_width && + xd->target_link_width != tlw) { + tb_dbg(tb, "%llx: target link width not yet set %#x != %#x\n", + route, tlw, xd->target_link_width); + tb_xdp_error_response(ctl, route, sequence, + ERROR_NOT_READY); + } else { + tb_dbg(tb, "%llx: replying with target link width set to %#x\n", + route, tlw); + ret = tb_xdp_link_state_status_response(tb, ctl, + xd, sequence, slw, sls, tls, tlw); + } } else { tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY); From 138ec65b2c761f065b19d115aed2b8246fc272f5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 21 Nov 2025 08:47:23 +0200 Subject: [PATCH 0453/5214] thunderbolt: Keep the domain reference while processing hotplug We process hotplug events in a workqueue that may run after the domain has been removed by tb_domain_remove(). For example if user unloads the driver while at the same time plugging a device router we may have scheduled tb_handle_hotplug() to run. Avoid possible UAF in this case by taking the domain reference before scheduling the hotplug handler in tb_queue_hotplug(). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c69c323e6952..34b7d18cce56 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -98,7 +98,7 @@ static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) if (!ev) return; - ev->tb = tb; + ev->tb = tb_domain_get(tb); ev->route = route; ev->port = port; ev->unplug = unplug; @@ -2527,6 +2527,9 @@ static void tb_handle_hotplug(struct work_struct *work) pm_runtime_mark_last_busy(&tb->dev); pm_runtime_put_autosuspend(&tb->dev); + /* Undo the refcount increased in tb_queue_hotplug() */ + tb_domain_put(tb); + kfree(ev); } From 4c63f29872cb444b33665348bbd2f45cab06afcd Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 19 Nov 2025 13:15:58 +0200 Subject: [PATCH 0454/5214] thunderbolt: Release request if tb_cfg_request() fails in __tb_xdomain_response() If tb_cfg_request() fails setting up the request (for example the control channel is shut down already) it returns an error without calling the callback. To avoid leaking that memory, call tb_cfg_request_put() if tb_cfg_request() fails. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 680b2204875a..4fe19cf6387d 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -136,6 +136,7 @@ static int __tb_xdomain_response(struct tb_ctl *ctl, const void *response, size_t size, enum tb_cfg_pkg_type type) { struct tb_cfg_request *req; + int ret; req = tb_cfg_request_alloc(); if (!req) @@ -147,7 +148,11 @@ static int __tb_xdomain_response(struct tb_ctl *ctl, const void *response, req->request_size = size; req->request_type = type; - return tb_cfg_request(ctl, req, response_ready, req); + ret = tb_cfg_request(ctl, req, response_ready, req); + if (ret) + tb_cfg_request_put(req); + + return ret; } /** From e56249d8a68e712f3b60e1f3fdbb5b4fea146468 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 19 Nov 2025 12:49:30 +0200 Subject: [PATCH 0455/5214] thunderbolt: Set tb->root_switch to NULL when domain is stopped Similarly what we do with the firmware connection manager. This makes tb_xdp_handle_request() return error to the remote host. However, we need to make sure we keep the uuid alive so that we can reply until the whole domain is released. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 1 + drivers/thunderbolt/xdomain.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 34b7d18cce56..677877baae63 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -2952,6 +2952,7 @@ static void tb_stop(struct tb *tb) tb_tunnel_put(tunnel); } tb_switch_remove(tb->root_switch); + tb->root_switch = NULL; tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ } diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 4fe19cf6387d..a1887a15a284 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -740,7 +740,7 @@ static void tb_xdp_handle_request(struct work_struct *work) mutex_lock(&tb->lock); if (tb->root_switch) - uuid = tb->root_switch->uuid; + uuid = kmemdup(tb->root_switch->uuid, sizeof(*uuid), GFP_KERNEL); else uuid = NULL; mutex_unlock(&tb->lock); @@ -880,6 +880,7 @@ static void tb_xdp_handle_request(struct work_struct *work) } out: + kfree(uuid); kfree(xw->pkg); kfree(xw); @@ -2348,6 +2349,9 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, { struct tb_port *port; + if (!sw) + return NULL; + tb_switch_for_each_port(sw, port) { struct tb_xdomain *xd; From f5cc545f59699549adbaa4084149f8247865a51d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 19 Nov 2025 12:53:58 +0200 Subject: [PATCH 0456/5214] thunderbolt: Wait for tb_domain_release() to complete when driver is removed We should not call nhi_shutdown() before the domain structure and the control channel rings are completely released. Otherwise we might release resources like the nhi->msix_ida that are still referenced in tb_domain_release(). For this reason wait for the tb_domain_release() to be completed before continuing to nhi_shutdown() and eventually releasing of the rest of the data structures. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/domain.c | 3 +++ drivers/thunderbolt/nhi.c | 4 ++++ include/linux/thunderbolt.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 317780b99992..df4d7dd45adf 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -319,12 +319,15 @@ const struct bus_type tb_bus_type = { static void tb_domain_release(struct device *dev) { struct tb *tb = container_of(dev, struct tb, dev); + struct tb_nhi *nhi = tb->nhi; tb_ctl_free(tb->ctl); destroy_workqueue(tb->wq); ida_free(&tb_domain_ida, tb->index); mutex_destroy(&tb->lock); kfree(tb); + + complete(&nhi->domain_released); } const struct device_type tb_domain_type = { diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 2bb2e79ca3cb..1a2051673067 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1401,6 +1401,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_dbg(dev, "NHI initialized, starting thunderbolt\n"); + init_completion(&nhi->domain_released); + res = tb_domain_add(tb, host_reset); if (res) { /* @@ -1408,6 +1410,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) * activated. Do a proper shutdown. */ tb_domain_put(tb); + wait_for_completion(&nhi->domain_released); nhi_shutdown(nhi); return res; } @@ -1433,6 +1436,7 @@ static void nhi_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); tb_domain_remove(tb); + wait_for_completion(&nhi->domain_released); nhi_shutdown(nhi); } diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 0ba112175bb3..a5ef7100a6d3 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -493,6 +493,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) * MSI-X is used. * @hop_count: Number of rings (end point hops) supported by NHI. * @quirks: NHI specific quirks if any + * @domain_released: Completed when domain has been fully released */ struct tb_nhi { spinlock_t lock; @@ -507,6 +508,7 @@ struct tb_nhi { struct work_struct interrupt_work; u32 hop_count; unsigned long quirks; + struct completion domain_released; }; /** From 8b4060998637f06975fceee9b73845d8672d411e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 8 Sep 2025 13:31:29 +0300 Subject: [PATCH 0457/5214] thunderbolt: Keep XDomain reference during the lifetime of a service This is needed because we release the service ID in tb_service_release() and the ID array is owned by the parent XDomain. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index a1887a15a284..0e97d0ad7733 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1039,6 +1039,7 @@ static void tb_service_release(struct device *dev) ida_free(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); + tb_xdomain_put(xd); } const struct device_type tb_service_type = { @@ -1147,7 +1148,7 @@ static void enumerate_services(struct tb_xdomain *xd) svc->id = id; svc->dev.bus = &tb_bus_type; svc->dev.type = &tb_service_type; - svc->dev.parent = &xd->dev; + svc->dev.parent = get_device(&xd->dev); dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); tb_service_debugfs_init(svc); From 7f35f42395fc58f75f51c9b3aeba00787f10371a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 24 Apr 2026 13:04:26 +0300 Subject: [PATCH 0458/5214] thunderbolt: dma_test: No need to store debugfs directory pointer We don't actually need to store the debugfs directory pointer inside struct dma_test. Instead we can use the debugfs_lookup_and_remove() which also handles the case if the debugfs directory is already removed by the core driver (for example when cable is disconnected). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/dma_test.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index b4aa79d482a0..af1e6bc9c7cd 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -87,7 +87,6 @@ static const char * const dma_test_result_names[] = { * @error_code: Error code of the last run * @complete: Used to wait for the Rx to complete * @lock: Lock serializing access to this structure - * @debugfs_dir: dentry of this dma_test */ struct dma_test { const struct tb_service *svc; @@ -108,7 +107,6 @@ struct dma_test { enum dma_test_test_error error_code; struct completion complete; struct mutex lock; - struct dentry *debugfs_dir; }; /* DMA test property directory UUID: 3188cd10-6523-4a5a-a682-fdca07a248d8 */ @@ -619,18 +617,18 @@ DEFINE_SHOW_ATTRIBUTE(status); static void dma_test_debugfs_init(struct tb_service *svc) { - struct dma_test *dt = tb_service_get_drvdata(svc); + struct dentry *debugfs_dir; - dt->debugfs_dir = debugfs_create_dir("dma_test", svc->debugfs_dir); + debugfs_dir = debugfs_create_dir("dma_test", svc->debugfs_dir); - debugfs_create_file("lanes", 0600, dt->debugfs_dir, svc, &lanes_fops); - debugfs_create_file("speed", 0600, dt->debugfs_dir, svc, &speed_fops); - debugfs_create_file("packets_to_receive", 0600, dt->debugfs_dir, svc, + debugfs_create_file("lanes", 0600, debugfs_dir, svc, &lanes_fops); + debugfs_create_file("speed", 0600, debugfs_dir, svc, &speed_fops); + debugfs_create_file("packets_to_receive", 0600, debugfs_dir, svc, &packets_to_receive_fops); - debugfs_create_file("packets_to_send", 0600, dt->debugfs_dir, svc, + debugfs_create_file("packets_to_send", 0600, debugfs_dir, svc, &packets_to_send_fops); - debugfs_create_file("status", 0400, dt->debugfs_dir, svc, &status_fops); - debugfs_create_file("test", 0200, dt->debugfs_dir, svc, &test_fops); + debugfs_create_file("status", 0400, debugfs_dir, svc, &status_fops); + debugfs_create_file("test", 0200, debugfs_dir, svc, &test_fops); } static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) @@ -658,7 +656,7 @@ static void dma_test_remove(struct tb_service *svc) struct dma_test *dt = tb_service_get_drvdata(svc); mutex_lock(&dt->lock); - debugfs_remove_recursive(dt->debugfs_dir); + debugfs_lookup_and_remove("dma_test", svc->debugfs_dir); mutex_unlock(&dt->lock); } From 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 8 Sep 2025 13:37:36 +0300 Subject: [PATCH 0459/5214] thunderbolt: Remove service debugfs entries during unregister We add them as part of the register path so to keep it symmetric remove them as part of the unregister path. This also removes them even if the service itself is not yet released (but is unregistered), thus allowing new register with the same service name to happen. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 0e97d0ad7733..76e1902d18f3 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1035,7 +1035,6 @@ static void tb_service_release(struct device *dev) struct tb_service *svc = container_of(dev, struct tb_service, dev); struct tb_xdomain *xd = tb_service_parent(svc); - tb_service_debugfs_remove(svc); ida_free(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); @@ -1050,6 +1049,14 @@ const struct device_type tb_service_type = { }; EXPORT_SYMBOL_GPL(tb_service_type); +static void __unregister_service(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + + tb_service_debugfs_remove(svc); + device_unregister(&svc->dev); +} + static int remove_missing_service(struct device *dev, void *data) { struct tb_xdomain *xd = data; @@ -1061,7 +1068,7 @@ static int remove_missing_service(struct device *dev, void *data) if (!tb_property_find(xd->remote_properties, svc->key, TB_PROPERTY_TYPE_DIRECTORY)) - device_unregister(dev); + __unregister_service(dev); return 0; } @@ -1154,6 +1161,7 @@ static void enumerate_services(struct tb_xdomain *xd) tb_service_debugfs_init(svc); if (device_register(&svc->dev)) { + tb_service_debugfs_remove(svc); put_device(&svc->dev); break; } @@ -2092,7 +2100,7 @@ void tb_xdomain_add(struct tb_xdomain *xd) static int unregister_service(struct device *dev, void *data) { - device_unregister(dev); + __unregister_service(dev); return 0; } From a8937f35cf39c39c64325aa84d0463d866850857 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 6 Nov 2025 17:59:52 +0200 Subject: [PATCH 0460/5214] thunderbolt: Remove XDomain from the bus without holding tb->lock Currently we call device_unregister() for services and the XDomain itself with tb->lock held. This prevents the service drivers from calling any functions that may take it. For this reason separate removing the XDomain from the topology data structures (where we need the lock) from unregistering the device from the bus (where remove callbacks of the drivers are being called). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 2 ++ drivers/thunderbolt/domain.c | 30 ++++++++++++++++++ drivers/thunderbolt/icm.c | 5 +++ drivers/thunderbolt/switch.c | 14 +++++++++ drivers/thunderbolt/tb.c | 59 +++++++++++++++++------------------ drivers/thunderbolt/tb.h | 2 ++ drivers/thunderbolt/xdomain.c | 57 ++++++++++++++++++++------------- 7 files changed, 117 insertions(+), 52 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index d089d2decedd..569163a26afa 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -1780,6 +1780,8 @@ static void margining_port_remove(struct tb_port *port) if (!port->usb4) return; + if (!port->usb4->margining) + return; snprintf(dir_name, sizeof(dir_name), "port%d", port->port); parent = debugfs_lookup(dir_name, port->sw->debugfs_dir); diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index df4d7dd45adf..d83719a37b4c 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -853,6 +853,36 @@ int tb_domain_disconnect_all_paths(struct tb *tb) return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain); } +struct unregister_context { + const struct tb *tb; + int n; +}; + +static int unregister_unplugged_xdomain(struct device *dev, void *data) +{ + struct unregister_context *ctx = data; + struct tb_xdomain *xd; + + xd = tb_to_xdomain(dev); + if (xd && xd->tb == ctx->tb && xd->is_unplugged) { + tb_xdomain_unregister(xd); + ctx->n++; + } + return 0; +} + +int tb_domain_unregister_unplugged_xdomains(struct tb *tb) +{ + struct unregister_context ctx; + + ctx.tb = tb_domain_get(tb); + ctx.n = 0; + bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain); + tb_domain_put(tb); + + return ctx.n; +} + int tb_domain_init(void) { int ret; diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 9d95bf3ab44c..2f93a7bccad5 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -738,6 +738,7 @@ static void remove_xdomain(struct tb_xdomain *xd) sw = tb_to_switch(xd->dev.parent); tb_port_at(xd->route, sw)->xdomain = NULL; + xd->is_unplugged = true; tb_xdomain_remove(xd); } @@ -1762,6 +1763,8 @@ static void icm_handle_notification(struct work_struct *work) kfree(n->pkg); kfree(n); + + tb_domain_unregister_unplugged_xdomains(tb); } static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, @@ -2112,6 +2115,8 @@ static void icm_rescan_work(struct work_struct *work) if (tb->root_switch) icm_free_unplugged_children(tb->root_switch); mutex_unlock(&tb->lock); + + tb_domain_unregister_unplugged_xdomains(tb); } static void icm_complete(struct tb *tb) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c2ad58b19e7b..bfcab98faf4b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3625,6 +3625,20 @@ int tb_switch_resume(struct tb_switch *sw, bool runtime) tb_port_warn(port, "lost during suspend, disconnecting\n"); tb_sw_set_unplugged(port->remote->sw); + } else if (port->xdomain) { + /* + * If the user replaced the XDomain with + * another router, this will succeed in + * which case we must remove the XDomain + * before adding the new router. + */ + err = tb_cfg_get_upstream_port(sw->tb->ctl, + port->xdomain->route); + if (err > 0) { + tb_port_warn(port, + "XDomain was disconnected\n"); + port->xdomain->is_unplugged = true; + } } } } diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 677877baae63..a9d26a2ec259 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -2524,6 +2524,8 @@ static void tb_handle_hotplug(struct work_struct *work) out: mutex_unlock(&tb->lock); + tb_domain_unregister_unplugged_xdomains(tb); + pm_runtime_mark_last_busy(&tb->dev); pm_runtime_put_autosuspend(&tb->dev); @@ -3114,6 +3116,24 @@ static void tb_restore_children(struct tb_switch *sw) } } +static void tb_free_unplugged_xdomains(struct tb_switch *sw) +{ + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (tb_is_upstream_port(port)) + continue; + if (port->xdomain && port->xdomain->is_unplugged) { + tb_retimer_remove_all(port); + tb_xdomain_remove(port->xdomain); + tb_port_unconfigure_xdomain(port); + port->xdomain = NULL; + } else if (port->remote) { + tb_free_unplugged_xdomains(port->remote->sw); + } + } +} + static int tb_resume_noirq(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); @@ -3133,6 +3153,7 @@ static int tb_resume_noirq(struct tb *tb) tb_switch_resume(tb->root_switch, false); tb_free_invalid_tunnels(tb); tb_free_unplugged_children(tb->root_switch); + tb_free_unplugged_xdomains(tb->root_switch); tb_restore_children(tb->root_switch); /* @@ -3175,28 +3196,6 @@ static int tb_resume_noirq(struct tb *tb) return 0; } -static int tb_free_unplugged_xdomains(struct tb_switch *sw) -{ - struct tb_port *port; - int ret = 0; - - tb_switch_for_each_port(sw, port) { - if (tb_is_upstream_port(port)) - continue; - if (port->xdomain && port->xdomain->is_unplugged) { - tb_retimer_remove_all(port); - tb_xdomain_remove(port->xdomain); - tb_port_unconfigure_xdomain(port); - port->xdomain = NULL; - ret++; - } else if (port->remote) { - ret += tb_free_unplugged_xdomains(port->remote->sw); - } - } - - return ret; -} - static int tb_freeze_noirq(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); @@ -3216,14 +3215,14 @@ static int tb_thaw_noirq(struct tb *tb) static void tb_complete(struct tb *tb) { /* - * Release any unplugged XDomains and if there is a case where + * Unregister unplugged XDomains and if there is a case where * another domain is swapped in place of unplugged XDomain we * need to run another rescan. */ - mutex_lock(&tb->lock); - if (tb_free_unplugged_xdomains(tb->root_switch)) - tb_scan_switch(tb->root_switch); - mutex_unlock(&tb->lock); + if (tb_domain_unregister_unplugged_xdomains(tb)) { + scoped_guard(mutex, &tb->lock) + tb_scan_switch(tb->root_switch); + } } static int tb_runtime_suspend(struct tb *tb) @@ -3250,11 +3249,11 @@ static void tb_remove_work(struct work_struct *work) struct tb *tb = tcm_to_tb(tcm); mutex_lock(&tb->lock); - if (tb->root_switch) { + if (tb->root_switch) tb_free_unplugged_children(tb->root_switch); - tb_free_unplugged_xdomains(tb->root_switch); - } mutex_unlock(&tb->lock); + + tb_free_unplugged_xdomains(tb->root_switch); } static int tb_runtime_resume(struct tb *tb) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 217c3114bec8..229b9e7961fb 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -793,6 +793,7 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, int transmit_path, int transmit_ring, int receive_path, int receive_ring); int tb_domain_disconnect_all_paths(struct tb *tb); +int tb_domain_unregister_unplugged_xdomains(struct tb *tb); static inline struct tb *tb_domain_get(struct tb *tb) { @@ -1263,6 +1264,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, const uuid_t *remote_uuid); void tb_xdomain_add(struct tb_xdomain *xd); void tb_xdomain_remove(struct tb_xdomain *xd); +void tb_xdomain_unregister(struct tb_xdomain *xd); struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, u8 depth); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 76e1902d18f3..9a30fe36c4be 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -2105,38 +2105,51 @@ static int unregister_service(struct device *dev, void *data) } /** - * tb_xdomain_remove() - Remove XDomain from the bus + * tb_xdomain_remove() - Remove XDomain * @xd: XDomain to remove * - * This will stop all ongoing configuration work and remove the XDomain - * along with any services from the bus. When the last reference to @xd - * is released the object will be released as well. + * This will stop all ongoing configuration work. XDomain is not removed + * from the bus if it was added. That needs to be done separately by + * calling tb_xdomain_unregister(). + * + * Called with @tb->lock held. */ void tb_xdomain_remove(struct tb_xdomain *xd) { tb_xdomain_debugfs_remove(xd); - stop_handshake(xd); + tb_xdomain_link_exit(xd); + + if (!device_is_registered(&xd->dev)) { + /* + * Undo runtime PM here explicitly because it is + * possible that the XDomain was never added to the bus + * and thus device_del() is not called for it + * (device_del() would handle this otherwise). + */ + pm_runtime_disable(&xd->dev); + pm_runtime_put_noidle(&xd->dev); + pm_runtime_set_suspended(&xd->dev); + put_device(&xd->dev); + } +} + +/** + * tb_xdomain_unregister() - Unregister XDomain + * @xd: XDomain to unregister + * + * This will unregister the XDomain along with any services from the + * bus. When the last reference to @xd is released the object will be + * released as well. + */ +void tb_xdomain_unregister(struct tb_xdomain *xd) +{ + lockdep_assert_not_held(&xd->tb->lock); device_for_each_child_reverse(&xd->dev, xd, unregister_service); - tb_xdomain_link_exit(xd); - - /* - * Undo runtime PM here explicitly because it is possible that - * the XDomain was never added to the bus and thus device_del() - * is not called for it (device_del() would handle this otherwise). - */ - pm_runtime_disable(&xd->dev); - pm_runtime_put_noidle(&xd->dev); - pm_runtime_set_suspended(&xd->dev); - - if (!device_is_registered(&xd->dev)) { - put_device(&xd->dev); - } else { - dev_info(&xd->dev, "host disconnected\n"); - device_unregister(&xd->dev); - } + dev_info(&xd->dev, "host disconnected\n"); + device_unregister(&xd->dev); } /** From cf0c38ee554c3e9062408cc3a38325483d52ecd0 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Thu, 2 Oct 2025 15:37:22 +0300 Subject: [PATCH 0461/5214] thunderbolt: Don't create multiple DMA tunnels on firmware connection manager Firmware connection manager supports only one DMA tunnel per XDomain connection. Firmware prior Intel Titan Ridge failed the operation directly but the same does not happen anymore on Titan Ridge and forward. For this reason add an explicit check, and fail the operation accordingly in the driver. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 10 ++++++++++ drivers/thunderbolt/xdomain.c | 25 +++++++++++++++++++------ include/linux/thunderbolt.h | 2 ++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 2f93a7bccad5..c492995166f7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -587,6 +587,11 @@ static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, struct icm_fr_pkg_approve_xdomain request; int ret; + if (atomic_read(&xd->ntunnels) >= 1) { + tb_warn(tb, "only one tunnel is supported by the firmware\n"); + return -EOPNOTSUPP; + } + memset(&request, 0, sizeof(request)); request.hdr.code = ICM_APPROVE_XDOMAIN; request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; @@ -1158,6 +1163,11 @@ static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, struct icm_tr_pkg_approve_xdomain request; int ret; + if (atomic_read(&xd->ntunnels) >= 1) { + tb_warn(tb, "only one tunnel is supported by the firmware\n"); + return -EOPNOTSUPP; + } + memset(&request, 0, sizeof(request)); request.hdr.code = ICM_APPROVE_XDOMAIN; request.route_hi = upper_32_bits(xd->route); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 9a30fe36c4be..6e83f93eee83 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -2038,6 +2038,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, INIT_DELAYED_WORK(&xd->state_work, tb_xdomain_state_work); INIT_DELAYED_WORK(&xd->properties_changed_work, tb_xdomain_properties_changed); + atomic_set(&xd->ntunnels, 0); xd->local_uuid = kmemdup(local_uuid, sizeof(uuid_t), GFP_KERNEL); if (!xd->local_uuid) @@ -2328,9 +2329,15 @@ int tb_xdomain_enable_paths(struct tb_xdomain *xd, int transmit_path, int transmit_ring, int receive_path, int receive_ring) { - return tb_domain_approve_xdomain_paths(xd->tb, xd, transmit_path, - transmit_ring, receive_path, - receive_ring); + int ret; + + ret = tb_domain_approve_xdomain_paths(xd->tb, xd, transmit_path, + transmit_ring, receive_path, + receive_ring); + if (ret) + return ret; + atomic_inc(&xd->ntunnels); + return 0; } EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths); @@ -2353,9 +2360,15 @@ int tb_xdomain_disable_paths(struct tb_xdomain *xd, int transmit_path, int transmit_ring, int receive_path, int receive_ring) { - return tb_domain_disconnect_xdomain_paths(xd->tb, xd, transmit_path, - transmit_ring, receive_path, - receive_ring); + int ret; + + ret = tb_domain_disconnect_xdomain_paths(xd->tb, xd, transmit_path, + transmit_ring, receive_path, + receive_ring); + if (ret) + return ret; + atomic_dec(&xd->ntunnels); + return 0; } EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index a5ef7100a6d3..bbdbbc84c999 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -228,6 +228,7 @@ enum tb_link_width { * changed notification * @bonding_possible: True if lane bonding is possible on local side * @target_link_width: Target link width from the remote host + * @ntunnels: Keeps track of how many tunnels go through this XDomain * @link: Root switch link the remote domain is connected (ICM only) * @depth: Depth in the chain the remote domain is connected (ICM only) * @@ -273,6 +274,7 @@ struct tb_xdomain { int properties_changed_retries; bool bonding_possible; u8 target_link_width; + atomic_t ntunnels; u8 link; u8 depth; }; From d7fe0d53b2a8b08f6042cc89315118dee49e072e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Mar 2026 15:15:23 +0200 Subject: [PATCH 0462/5214] =?UTF-8?q?media:=20dw9719:=20Add=20back=20the?= =?UTF-8?q?=20I=C2=B2C=20device=20id=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The I²C device id table is necessary as the device may be, besides through system firmware, also instantiated in the IPU bridge so matching takes place using the I²C device id table. Add back the table, with ids for all supported devices. Reported-by: Michael Anthony Closes: https://lore.kernel.org/linux-media/AMBP190MB2678E7DC048409068260DCE8ED4AA@AMBP190MB2678.EURP190.PROD.OUTLOOK.COM/ Fixes: 15faf0fa1472 ("media: i2c: dw9719: Remove unused i2c device id table") Cc: stable@vger.kernel.org # for v6.19 and later Signed-off-by: Sakari Ailus --- drivers/media/i2c/dw9719.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index 59558335989e..3b7ba88fd67c 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -439,6 +439,15 @@ static void dw9719_remove(struct i2c_client *client) pm_runtime_set_suspended(&client->dev); } +static const struct i2c_device_id dw9719_id_table[] = { + { .name = "dw9718s", .driver_data = (kernel_ulong_t)DW9718S }, + { .name = "dw9719", .driver_data = (kernel_ulong_t)DW9719 }, + { .name = "dw9761", .driver_data = (kernel_ulong_t)DW9761 }, + { .name = "dw9800k", .driver_data = (kernel_ulong_t)DW9800K }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9719_id_table); + static const struct of_device_id dw9719_of_table[] = { { .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S }, { .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 }, @@ -459,6 +468,7 @@ static struct i2c_driver dw9719_i2c_driver = { }, .probe = dw9719_probe, .remove = dw9719_remove, + .id_table = dw9719_id_table, }; module_i2c_driver(dw9719_i2c_driver); From d3a9a8cf2d7fd61a2f63df61f6cbc0a9bb007cc0 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 13 Apr 2026 17:12:44 +0200 Subject: [PATCH 0463/5214] staging: media: ipu7: fix double-free and use-after-free in error paths In both ipu7_isys_init() and ipu7_psys_init(), pdata is allocated and then passed to ipu7_bus_initialize_device(), which stores it in adev->pdata. The ipu7_bus_release() function frees adev->pdata when the device's reference count drops to zero. Two error paths incorrectly call kfree(pdata) after the device teardown has already freed it: 1. When ipu7_mmu_init() fails: put_device() is called, which drops the reference count to zero and triggers ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is a double-free. 2. When ipu7_bus_add_device() fails: it calls auxiliary_device_uninit() internally, which calls put_device() -> ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is again a double-free. Note that the kfree(pdata) when ipu7_bus_initialize_device() itself fails is correct, because in that case auxiliary_device_init() failed and the release function was never set up, so pdata must be freed manually. Additionally, the error code was not saved before calling put_device(), causing ERR_CAST() to dereference the already-freed adev pointer when constructing the return value. Fix this by saving the error from dev_err_probe() before put_device() and returning ERR_PTR() instead. Remove the redundant kfree(pdata) calls and fix the use-after-free in the return values of the two affected error paths. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Cc: stable@vger.kernel.org Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index c771e763f8c5..310e3f24e571 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu7_mmu_init(isys_adev->mmu) failed\n"); + ret = dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return ERR_PTR(ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys = IPU_IS; ret = ipu7_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu7_mmu_init(psys_adev->mmu) failed\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return ERR_PTR(ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys = IPU_PS; ret = ipu7_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } From 062ba06f971c0c9f9b4cf37b3b7e8ee8a759e11c Mon Sep 17 00:00:00 2001 From: Lian Xiangyu Date: Tue, 24 Mar 2026 01:31:40 +0800 Subject: [PATCH 0464/5214] staging: media: ipu7: remove 'U' suffix from hexadecimal literals The ipu7 driver's TODO specifies that the 'U' suffix should be removed from hexadecimal values in register definitions. This patch cleans up the definitions in the header files within the ipu7 directory to comply with the requirements and improve consistency. The modification was verified by comparing the disassembly of the built-in.a archive before and after the change. The MD5 hashes of the disassembly output remained identical, confirming that this is a purely cosmetic cleanup with no functional impact on the binary. Signed-off-by: Lian Xiangyu Signed-off-by: Sakari Ailus --- .../staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 78 +++++++++---------- .../media/ipu7/abi/ipu7_fw_common_abi.h | 4 +- .../staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 12 +-- .../staging/media/ipu7/ipu7-buttress-regs.h | 10 +-- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h index a1519c4fe661..56b90aab83ea 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h @@ -42,7 +42,7 @@ struct ia_gofo_logger_config { ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) #define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) -#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) +#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000 / 4U) #define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) #define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ @@ -75,7 +75,7 @@ enum ia_gofo_boot_uc_tile_frequency_units { }; #define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ - (0xdead0000U == ((boot_state) & 0xffff0000U)) + (0xdead0000 == ((boot_state) & 0xffff0000)) struct ia_gofo_boot_config { u32 length; @@ -104,35 +104,35 @@ struct ia_gofo_secondary_boot_config { #pragma pack(pop) -#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U -#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U -#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U -#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U -#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U -#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U -#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U -#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U -#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U +#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401 +#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801 +#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802 +#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803 +#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804 +#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805 +#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806 +#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000 +#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010 enum ia_gofo_boot_state { - IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, - IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, - IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, - IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, - IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, - IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, - IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, - IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, - IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, - IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, - IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, - IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, + IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000, + IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000, + IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000, + IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010, + IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020, + IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030, + IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100, + IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200, + IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300, + IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100, + IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201, + IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301, IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, - IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, - IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, - IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, + IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501, + IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601, + IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, @@ -146,18 +146,18 @@ enum ia_gofo_boot_state { IA_GOFO_DOUBLE_EXCEPTION_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, - IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, - IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, + IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010, + IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011, IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = - 0xdead1012U, - IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, - IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, - IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, - IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, - IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, - IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U + 0xdead1012, + IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013, + IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014, + IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200, + IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300, + IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400, + IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500 }; #endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h index 7bb6fac585a3..398a13350480 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h @@ -60,7 +60,7 @@ struct ia_gofo_tlv_list { #define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) #define IA_GOFO_MSG_ERR_OK (0U) -#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) +#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffff) #define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) #define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) @@ -145,7 +145,7 @@ struct ia_gofo_msg_indirect { #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) -#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) +#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffff) struct ia_gofo_msg_log_info { u16 log_counter; diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h index 8a78dd0936df..311248385993 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h @@ -69,11 +69,11 @@ struct ipu7_msg_cb_profile { #define IPU_MSG_NODE_MAX_PROFILES (2U) #define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) -#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) +#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xff) -#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) -#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) -#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) +#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffff) +#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffff) +#define IPU_MSG_NODE_RSRC_ID_IS (0xfe) struct ipu7_msg_node { struct ia_gofo_tlv_header tlv_header; @@ -160,7 +160,7 @@ struct ipu7_msg_link_ep_pair { #define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) #define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) #define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) -#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) +#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xff) struct ipu7_msg_link { struct ia_gofo_tlv_header tlv_header; @@ -333,7 +333,7 @@ enum ipu7_msg_err_device { #pragma pack(pop) #pragma pack(push, 1) -#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) +#define IPU_MSG_GRAPH_ID_UNKNOWN (0xff) #define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U #define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h index 3eafd6a3813d..7b646aa538cf 100644 --- a/drivers/staging/media/ipu7/ipu7-buttress-regs.h +++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h @@ -287,7 +287,7 @@ #define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) #define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 -#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) +#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3 << 11) #define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) /* new for PTL */ #define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) @@ -326,8 +326,8 @@ #define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff /* IS/PS freq control */ -#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU -#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU +#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xff +#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xff #define IPU7_IS_FREQ_MAX 450 #define IPU7_IS_FREQ_MIN 50 @@ -350,11 +350,11 @@ /* buttree power status */ #define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 #define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 #define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 #define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 From 477620dccf3e9481ed6ae67cb5c747f25751c531 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 18:25:47 +0200 Subject: [PATCH 0465/5214] media: intel/ipu6: Improve DWC PHY HSFREQRANGE band selection for overlapping ranges The get_hsfreq_by_mbps() function searches the freqranges[] table backward (from highest to lowest index). Because adjacent frequency bands overlap, a data rate that falls in the overlap region always lands on the higher-indexed band. For data rates up to 1500 Mbps (index 42) every band uses osc_freq_target 335. Starting at index 43 (1461-1640 Mbps) the osc_freq_target drops to 208. A sensor running at 1498 Mbps sits in the overlap between index 42 (1414-1588, osc 335) and index 43 (1461-1640, osc 208). The backward search picks index 43, programming the lower osc_freq_target of 208 instead of the optimal 335. This causes DDL lock instability and CSI-2 CRC errors on affected configurations, such as the OmniVision OV08X40 sensor on Intel Arrow Lake platforms (Dell Pro Max 16). Rewrite get_hsfreq_by_mbps() to select the optimal band: 1. Among bands whose min/max range covers the data rate, prefer the one with the higher osc_freq_target. 2. If osc_freq_target is equal, prefer the band whose default_mbps is closest to the requested rate. Since the frequency ranges are monotonically increasing, the loop exits early once min exceeds the requested rate. For 1498 Mbps this now correctly selects index 42 (osc_freq_target 335, range 1414-1588) instead of index 43 (osc_freq_target 208, range 1461-1640). Fixes: 1e7eeb301696 ("media: intel/ipu6: add the CSI2 DPHY implementation") Cc: stable@vger.kernel.org Signed-off-by: Marco Nenciarini Signed-off-by: Sakari Ailus --- .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c index db2874843453..237906cb131a 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c @@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { static u16 get_hsfreq_by_mbps(u32 mbps) { - unsigned int i = DPHY_FREQ_RANGE_NUM; + u16 best = DPHY_FREQ_RANGE_INVALID_INDEX; + unsigned int i; - while (i--) { - if (freqranges[i].default_mbps == mbps || - (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) - return i; + for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) { + if (mbps > freqranges[i].max) + continue; + + if (mbps < freqranges[i].min) + break; + + if (best == DPHY_FREQ_RANGE_INVALID_INDEX || + freqranges[i].osc_freq_target > + freqranges[best].osc_freq_target || + (freqranges[i].osc_freq_target == + freqranges[best].osc_freq_target && + abs((int)mbps - (int)freqranges[i].default_mbps) < + abs((int)mbps - (int)freqranges[best].default_mbps))) + best = i; } - return DPHY_FREQ_RANGE_INVALID_INDEX; + return best; } static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, From 658810422076140f9a003a174f73b0eeaffa0611 Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 27 Mar 2026 00:10:00 +0100 Subject: [PATCH 0466/5214] media: dt-bindings: rockchip,rk3568-mipi-csi2: add rk3588 compatible The RK3588 MIPI CSI-2 receivers are compatible to the ones found in the RK3568. Introduce a list of compatible variants and add the RK3588 variant to it. Acked-by: Rob Herring (Arm) Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../bindings/media/rockchip,rk3568-mipi-csi2.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml index 4ac4a3b6f406..fbcf28e9e1da 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -16,9 +16,14 @@ description: properties: compatible: - enum: - - fsl,imx93-mipi-csi2 - - rockchip,rk3568-mipi-csi2 + oneOf: + - enum: + - fsl,imx93-mipi-csi2 + - rockchip,rk3568-mipi-csi2 + - items: + - enum: + - rockchip,rk3588-mipi-csi2 + - const: rockchip,rk3568-mipi-csi2 reg: maxItems: 1 From 05e58da46d8e8f8b29bc9b47053bb0637891c06f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 4 May 2026 19:54:35 -0400 Subject: [PATCH 0467/5214] mux: add devm_mux_state_get_from_np() to get mux from child node Add new API devm_mux_state_get_from_np() to retrieve a mux control from a specified child device node. Make devm_mux_state_get() call devm_mux_state_get_from_np() with a NULL node parameter, which defaults to using the device's own of_node. Support the following DT schema: pinctrl@0 { uart-func { mux-state = <&mux_chip 0>; }; spi-func { mux-state = <&mux_chip 1>; }; }; Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/mux/core.c | 42 ++++++++++++++++++++++-------------- include/linux/mux/consumer.h | 8 ++++++- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 23538de2c91b..2f01acfccf47 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -533,14 +533,16 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) * @state: Pointer to where the requested state is returned, or NULL when * the required multiplexer states are handled by other means. * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @node: the device nodes, use dev->of_node if it is NULL. * * Return: Pointer to the mux-control on success, an ERR_PTR with a negative * errno on error, or NULL if optional is true and mux doesn't exist. */ static struct mux_control *mux_get(struct device *dev, const char *mux_name, - unsigned int *state, bool optional) + unsigned int *state, bool optional, + struct device_node *node) { - struct device_node *np = dev->of_node; + struct device_node *np = node ? node : dev->of_node; struct of_phandle_args args; struct mux_chip *mux_chip; unsigned int controller; @@ -635,7 +637,7 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, */ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) { - struct mux_control *mux = mux_get(dev, mux_name, NULL, false); + struct mux_control *mux = mux_get(dev, mux_name, NULL, false, NULL); if (!mux) return ERR_PTR(-ENOENT); @@ -654,7 +656,7 @@ EXPORT_SYMBOL_GPL(mux_control_get); */ struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name) { - return mux_get(dev, mux_name, NULL, true); + return mux_get(dev, mux_name, NULL, true, NULL); } EXPORT_SYMBOL_GPL(mux_control_get_optional); @@ -712,11 +714,14 @@ EXPORT_SYMBOL_GPL(devm_mux_control_get); * @dev: The device that needs a mux-state. * @mux_name: The name identifying the mux-state. * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @np: the device nodes, use dev->of_node if it is NULL. * * Return: Pointer to the mux-state on success, an ERR_PTR with a negative * errno on error, or NULL if optional is true and mux doesn't exist. */ -static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional) +static struct mux_state * +mux_state_get(struct device *dev, const char *mux_name, bool optional, + struct device_node *np) { struct mux_state *mstate; @@ -724,7 +729,7 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, if (!mstate) return ERR_PTR(-ENOMEM); - mstate->mux = mux_get(dev, mux_name, &mstate->state, optional); + mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, np); if (IS_ERR(mstate->mux)) { int err = PTR_ERR(mstate->mux); @@ -773,7 +778,7 @@ static void devm_mux_state_release(struct device *dev, void *res) * errno on error, or NULL if optional is true and mux doesn't exist. */ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name, - bool optional, + bool optional, struct device_node *np, int (*init)(struct mux_state *mstate), int (*exit)(struct mux_state *mstate)) { @@ -781,7 +786,7 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu struct mux_state *mstate; int ret; - mstate = mux_state_get(dev, mux_name, optional); + mstate = mux_state_get(dev, mux_name, optional, np); if (IS_ERR(mstate)) return ERR_CAST(mstate); else if (optional && !mstate) @@ -815,20 +820,23 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu } /** - * devm_mux_state_get() - Get the mux-state for a device, with resource - * management. + * devm_mux_state_get_from_np() - Get the mux-state for a device, with resource + * management. * @dev: The device that needs a mux-control. * @mux_name: The name identifying the mux-control. + * @np: the device nodes, use dev->of_node if it is NULL. * * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. * * The mux-state will automatically be freed on release. */ -struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) +struct mux_state * +devm_mux_state_get_from_np(struct device *dev, const char *mux_name, + struct device_node *np) { - return __devm_mux_state_get(dev, mux_name, false, NULL, NULL); + return __devm_mux_state_get(dev, mux_name, false, np, NULL, NULL); } -EXPORT_SYMBOL_GPL(devm_mux_state_get); +EXPORT_SYMBOL_GPL(devm_mux_state_get_from_np); /** * devm_mux_state_get_optional() - Get the optional mux-state for a device, @@ -843,7 +851,7 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get); */ struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name) { - return __devm_mux_state_get(dev, mux_name, true, NULL, NULL); + return __devm_mux_state_get(dev, mux_name, true, NULL, NULL, NULL); } EXPORT_SYMBOL_GPL(devm_mux_state_get_optional); @@ -861,7 +869,8 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get_optional); */ struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name) { - return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect); + return __devm_mux_state_get(dev, mux_name, false, NULL, + mux_state_select, mux_state_deselect); } EXPORT_SYMBOL_GPL(devm_mux_state_get_selected); @@ -881,7 +890,8 @@ EXPORT_SYMBOL_GPL(devm_mux_state_get_selected); struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name) { - return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect); + return __devm_mux_state_get(dev, mux_name, true, NULL, + mux_state_select, mux_state_deselect); } EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected); diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index a961861a503b..449e38e6e2c5 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -60,7 +60,10 @@ struct mux_control *mux_control_get_optional(struct device *dev, const char *mux void mux_control_put(struct mux_control *mux); struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name); -struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name); + +struct mux_state * +devm_mux_state_get_from_np(struct device *dev, const char *mux_name, struct device_node *np); + struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name); struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name); struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name); @@ -161,4 +164,7 @@ static inline struct mux_state *devm_mux_state_get_optional_selected(struct devi #endif /* CONFIG_MULTIPLEXER */ +#define devm_mux_state_get(dev, mux_name) \ + devm_mux_state_get_from_np(dev, mux_name, NULL) + #endif /* _LINUX_MUX_CONSUMER_H */ From 9e5e1de61db29277d107bd6985c57629df449610 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 4 May 2026 19:54:36 -0400 Subject: [PATCH 0468/5214] dt-bindings: pinctrl: Add generic pinctrl for board-level mux chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic pinctrl binding for board-level pinmux chips that are controlled through the multiplexer subsystem. On some boards, especially development boards, external mux chips are used to switch SoC signals between different peripherals (e.g. MMC and UART). The mux select lines are often driven by a GPIO expander over I2C, as illustrated below: ┌──────┐ ┌─────┐ │ SOC │ │ │ ┌───────┐ │ │ │ │───►│ MMC │ │ │ │ MUX │ └───────┘ │ ├─────►│ │ ┌───────┐ │ │ │ │───►│ UART │ │ │ └─────┘ └───────┘ │ │ ▲ │ │ ┌────┴──────────────┐ │ I2C ├───►│ GPIO Expander │ └──────┘ └───────────────────┘ Traditionally, gpio-hog is used to configure the onboard mux at boot. However, the GPIO expander may probe later than consumer devices such as MMC. As a result, the MUX might not be configured when the peripheral driver probes, leading to initialization failures or data transfer errors. Introduce a generic pinctrl binding that models the board-level MUX as a pin control provider and builds proper device links between the MUX, its GPIO controller, and peripheral devices. This ensures correct probe ordering and reliable mux configuration. The implementation leverages the standard multiplexer subsystem, which provides broad support for onboard mux controllers and avoids the need for per-driver custom MUX handling. Allow pinctrl-* pattern as node name because this pinctrl device have not reg property. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring (Arm) Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- .../bindings/pinctrl/pinctrl-multiplexer.yaml | 57 +++++++++++++++++++ .../devicetree/bindings/pinctrl/pinctrl.yaml | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-multiplexer.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-multiplexer.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-multiplexer.yaml new file mode 100644 index 000000000000..2b0385ed879b --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-multiplexer.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/pinctrl-multiplexer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic pinctrl device for on-board MUX Chips + +maintainers: + - Frank Li + +description: + Generic pinctrl device for on-board MUX Chips, which switch SoC signals + between different peripherals (e.g. MMC and UART). + + The MUX select lines are often driven by a I2C GPIO expander. + +properties: + compatible: + const: pinctrl-multiplexer + +patternProperties: + '-grp$': + type: object + additionalProperties: false + properties: + mux-states: + maxItems: 1 + + required: + - mux-states + +required: + - compatible + +allOf: + - $ref: pinctrl.yaml# + +unevaluatedProperties: false + +examples: + - | + pinctrl-mux { + compatible = "pinctrl-multiplexer"; + + uart-grp { + mux-states = <&mux 0>; + }; + + spi-grp { + mux-states = <&mux 1>; + }; + + i2c-grp { + mux-states = <&mux 2>; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl.yaml index 290438826c50..20176bf30747 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl.yaml @@ -27,7 +27,7 @@ description: | properties: $nodename: - pattern: "^(pinctrl|pinmux)(@[0-9a-f]+)?$" + pattern: "^(pinctrl|pinmux)(@[0-9a-f]+|-[a-z0-9]+)?$" "#pinctrl-cells": description: > From aaaf31be04260316036f50a05b7d015c0d36b55a Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 4 May 2026 19:54:37 -0400 Subject: [PATCH 0469/5214] pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map() Refactor pinctrl_generic_pins_function_dt_subnode_to_map() by separating DT parsing logic from map creation. Introduce a new helper pinctrl_generic_to_map() to handle mapping to kernel data structures, while keeping DT property parsing in the subnode function. Improve code structure and enables easier reuse for platforms using different DT properties (e.g. pinmux) without modifying the dt_node_to_map-style callback API. Avoid unnecessary coupling to pinctrl_generic_pins_function_dt_node_to_map(), which provides functionality not needed when the phandle target is unambiguous. Maximize code reuse and provide a cleaner extension point for future pinctrl drivers. Suggested-by: Conor Dooley Acked-by: Conor Dooley Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.h | 18 ++++++ drivers/pinctrl/pinctrl-generic.c | 95 ++++++++++++++++++------------- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 659a781e2091..fa8fb0d290d1 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -172,6 +172,13 @@ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps); + +int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, + struct device_node *np, struct pinctrl_map **maps, + unsigned int *num_maps, unsigned int *num_reserved_maps, + const char **group_name, unsigned int ngroups, + const char **functions, unsigned int *pins, + unsigned int npins); #else static inline int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, @@ -181,4 +188,15 @@ pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, { return -ENOTSUPP; } + +static inline int +pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, + struct device_node *np, struct pinctrl_map **maps, + unsigned int *num_maps, unsigned int *num_reserved_maps, + const char **group_name, unsigned int ngroups, + const char **functions, unsigned int *pins, + void *function_data) +{ + return -ENOTSUPP; +} #endif diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c index efb39c6a6703..e4cd16ce2bda 100644 --- a/drivers/pinctrl/pinctrl-generic.c +++ b/drivers/pinctrl/pinctrl-generic.c @@ -17,29 +17,18 @@ #include "pinctrl-utils.h" #include "pinmux.h" -static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev, - struct device_node *parent, - struct device_node *np, - struct pinctrl_map **maps, - unsigned int *num_maps, - unsigned int *num_reserved_maps, - const char **group_names, - unsigned int ngroups) +int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, + struct device_node *np, struct pinctrl_map **maps, + unsigned int *num_maps, unsigned int *num_reserved_maps, + const char **group_names, unsigned int ngroups, + const char **functions, unsigned int *pins, + unsigned int npins) { struct device *dev = pctldev->dev; - const char **functions; + unsigned int num_configs; const char *group_name; unsigned long *configs; - unsigned int num_configs, pin, *pins; - int npins, ret, reserve = 1; - - npins = of_property_count_u32_elems(np, "pins"); - - if (npins < 1) { - dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", - parent, np, npins); - return npins; - } + int ret, reserve = 1; group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np); if (!group_name) @@ -47,26 +36,6 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p group_names[ngroups] = group_name; - pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); - if (!pins) - return -ENOMEM; - - functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL); - if (!functions) - return -ENOMEM; - - for (int i = 0; i < npins; i++) { - ret = of_property_read_u32_index(np, "pins", i, &pin); - if (ret) - return ret; - - pins[i] = pin; - - ret = of_property_read_string(np, "function", &functions[i]); - if (ret) - return ret; - } - ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve); if (ret) return ret; @@ -102,6 +71,54 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p return 0; }; +EXPORT_SYMBOL_GPL(pinctrl_generic_to_map); + +static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *parent, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps, + unsigned int *num_reserved_maps, + const char **group_names, + unsigned int ngroups) +{ + struct device *dev = pctldev->dev; + unsigned int pin, *pins; + const char **functions; + int npins, ret; + + npins = of_property_count_u32_elems(np, "pins"); + + if (npins < 1) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", + parent, np, npins); + return npins; + } + + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL); + if (!functions) + return -ENOMEM; + + for (int i = 0; i < npins; i++) { + ret = of_property_read_u32_index(np, "pins", i, &pin); + if (ret) + return ret; + + pins[i] = pin; + + ret = of_property_read_string(np, "function", &functions[i]); + if (ret) + return ret; + } + + return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps, + num_reserved_maps, group_names, ngroups, + functions, pins, npins); +} /* * For platforms that do not define groups or functions in the driver, but From 418a2bbdee2927d543db6913073d0e7ec8b540ee Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 4 May 2026 19:54:38 -0400 Subject: [PATCH 0470/5214] pinctrl: add optional .release_mux() callback Add an optional .release_mux() callback to struct pinmux_ops. Some drivers acquire additional resources in .set_mux(), such as software locks. These resources may need to be released when the mux function is no longer active. Introducing a dedicated .release_mux() callback allows drivers to clean up such resources. The callback is optional and does not affect existing drivers. Commit 2243a87d90b42 ("pinctrl: avoid duplicated calling enable_pinmux_setting for a pin") removed the .disable() callback to resolve two issues: 1. desc->mux_usecount increasing monotonically 2. Hardware glitches caused by repeated .disable()/.enable() calls Adding .release_mux() does not reintroduce those problems. The callback is intended only for releasing driver-side resources (e.g. locks) and must not modify hardware registers. Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/pinctrl/pinmux.c | 5 +++++ include/linux/pinctrl/pinmux.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 3a8dd184ba3d..c705bc182266 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -517,6 +517,7 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting) { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + const struct pinmux_ops *ops = pctldev->desc->pmxops; int ret = 0; const unsigned int *pins = NULL; unsigned int num_pins = 0; @@ -563,6 +564,10 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting) pins[i], desc->name, gname); } } + + if (ops->release_mux) + ops->release_mux(pctldev, setting->data.mux.func, + setting->data.mux.group); } #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h index 094bbe2fd6fd..77664937eeb2 100644 --- a/include/linux/pinctrl/pinmux.h +++ b/include/linux/pinctrl/pinmux.h @@ -51,6 +51,8 @@ struct pinctrl_gpio_range; * are handled by the pinmux subsystem. The @func_selector selects a * certain function whereas @group_selector selects a certain set of pins * to be used. On simple controllers the latter argument may be ignored + * @release_mux: Release software resources acquired by @set_mux. This callback + * must not change hardware state to avoid glitches when switching mux. * @gpio_request_enable: requests and enables GPIO on a certain pin. * Implement this only if you can mux every pin individually as GPIO. The * affected GPIO range is passed along with an offset(pin number) into that @@ -80,6 +82,9 @@ struct pinmux_ops { unsigned int selector); int (*set_mux) (struct pinctrl_dev *pctldev, unsigned int func_selector, unsigned int group_selector); + void (*release_mux) (struct pinctrl_dev *pctldev, + unsigned int func_selector, + unsigned int group_selector); int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned int offset); From 34acc5a8adfb76f2de63c8b8317397fb72b0aec8 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 4 May 2026 19:54:39 -0400 Subject: [PATCH 0471/5214] pinctrl: add generic board-level pinctrl driver using mux framework Many boards use on-board mux chips (often controlled by GPIOs from an I2C expander) to switch shared signals between peripherals. Add a generic pinctrl driver built on top of the mux framework to centralize mux handling and avoid probe ordering issues. Keep board-level routing out of individual drivers and supports boot-time only mux selection. Ensure correct probe ordering, especially when the GPIO expander is probed later. Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 9 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-generic-mux.c | 184 ++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-generic-mux.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 03f2e3ee065f..31d698fbaa01 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -272,6 +272,15 @@ config PINCTRL_GEMINI select GENERIC_PINCONF select MFD_SYSCON +config PINCTRL_GENERIC_MUX + tristate "Generic Pinctrl driver by using multiplexer" + depends on MULTIPLEXER + select PINMUX + select GENERIC_PINCTRL + help + Generic pinctrl driver by MULTIPLEXER framework to control on + board pin selection. + config PINCTRL_INGENIC bool "Pinctrl driver for the Ingenic JZ47xx SoCs" default MACH_INGENIC diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f7d5d5f76d0c..fcd1703440d2 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PINCTRL_EQUILIBRIUM) += pinctrl-equilibrium.o obj-$(CONFIG_PINCTRL_EP93XX) += pinctrl-ep93xx.o obj-$(CONFIG_PINCTRL_EYEQ5) += pinctrl-eyeq5.o obj-$(CONFIG_PINCTRL_GEMINI) += pinctrl-gemini.o +obj-$(CONFIG_PINCTRL_GENERIC_MUX) += pinctrl-generic-mux.o obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o obj-$(CONFIG_PINCTRL_K230) += pinctrl-k230.o diff --git a/drivers/pinctrl/pinctrl-generic-mux.c b/drivers/pinctrl/pinctrl-generic-mux.c new file mode 100644 index 000000000000..da5a5ec01583 --- /dev/null +++ b/drivers/pinctrl/pinctrl-generic-mux.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic Pin Control Driver for Board-Level Mux Chips + * Copyright 2026 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinmux.h" +#include "pinctrl-utils.h" + +struct mux_pin_function { + struct mux_state *mux_state; +}; + +struct mux_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + + /* mutex protect [pinctrl|pinmux]_generic functions */ + struct mutex lock; +}; + +static int +mux_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **maps, unsigned int *num_maps) +{ + unsigned int num_reserved_maps = 0; + struct mux_pin_function *function; + const char **group_names; + int ret; + + function = devm_kzalloc(pctldev->dev, sizeof(*function), GFP_KERNEL); + if (!function) + return -ENOMEM; + + group_names = devm_kcalloc(pctldev->dev, 1, sizeof(*group_names), GFP_KERNEL); + if (!group_names) + return -ENOMEM; + + function->mux_state = devm_mux_state_get_from_np(pctldev->dev, NULL, np_config); + if (IS_ERR(function->mux_state)) + return PTR_ERR(function->mux_state); + + ret = pinctrl_generic_to_map(pctldev, np_config, np_config, maps, + num_maps, &num_reserved_maps, group_names, + 0, &np_config->name, NULL, 0); + + if (ret) + return ret; + + ret = pinmux_generic_add_function(pctldev, np_config->name, group_names, + 1, function); + if (ret < 0) { + pinctrl_utils_free_map(pctldev, *maps, *num_maps); + return ret; + } + + return 0; +} + +static const struct pinctrl_ops mux_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .dt_node_to_map = mux_pinmux_dt_node_to_map, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int mux_pinmux_set_mux(struct pinctrl_dev *pctldev, + unsigned int func_selector, + unsigned int group_selector) +{ + struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev); + const struct function_desc *function; + struct mux_pin_function *func; + int ret; + + guard(mutex)(&mpctl->lock); + + function = pinmux_generic_get_function(pctldev, func_selector); + func = function->data; + + ret = mux_state_select(func->mux_state); + if (ret) + return ret; + + return 0; +} + +static void mux_pinmux_release_mux(struct pinctrl_dev *pctldev, + unsigned int func_selector, + unsigned int group_selector) +{ + struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev); + const struct function_desc *function; + struct mux_pin_function *func; + + guard(mutex)(&mpctl->lock); + + function = pinmux_generic_get_function(pctldev, func_selector); + func = function->data; + + mux_state_deselect(func->mux_state); +} + +static const struct pinmux_ops mux_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = mux_pinmux_set_mux, + .release_mux = mux_pinmux_release_mux, +}; + +static int mux_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mux_pinctrl *mpctl; + struct pinctrl_desc *pctl_desc; + int ret; + + mpctl = devm_kzalloc(dev, sizeof(*mpctl), GFP_KERNEL); + if (!mpctl) + return -ENOMEM; + + mpctl->dev = dev; + + platform_set_drvdata(pdev, mpctl); + + pctl_desc = devm_kzalloc(dev, sizeof(*pctl_desc), GFP_KERNEL); + if (!pctl_desc) + return -ENOMEM; + + ret = devm_mutex_init(dev, &mpctl->lock); + if (ret) + return ret; + + pctl_desc->name = dev_name(dev); + pctl_desc->owner = THIS_MODULE; + pctl_desc->pctlops = &mux_pinctrl_ops; + pctl_desc->pmxops = &mux_pinmux_ops; + + ret = devm_pinctrl_register_and_init(dev, pctl_desc, mpctl, + &mpctl->pctl); + if (ret) + return dev_err_probe(dev, ret, "Failed to register pinctrl.\n"); + + ret = pinctrl_enable(mpctl->pctl); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pinctrl.\n"); + + return 0; +} + +static const struct of_device_id mux_pinctrl_of_match[] = { + { .compatible = "pinctrl-multiplexer" }, + { } +}; +MODULE_DEVICE_TABLE(of, mux_pinctrl_of_match); + +static struct platform_driver mux_pinctrl_driver = { + .driver = { + .name = "generic-pinctrl-mux", + .of_match_table = mux_pinctrl_of_match, + }, + .probe = mux_pinctrl_probe, +}; +module_platform_driver(mux_pinctrl_driver); + +MODULE_AUTHOR("Frank Li "); +MODULE_DESCRIPTION("Generic Pin Control Driver for Board-Level Mux Chips"); +MODULE_LICENSE("GPL"); From b82e98bf02cad5923e1306211edb143701222fa2 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 25 Feb 2026 17:44:52 +0000 Subject: [PATCH 0472/5214] clk: microchip: rename clk-core to clk-pic32 clk-core is a confusingly generic name, since it is only used by a single platform and it uses very similar naming to the "soft" IP cores for use in FPGA fabric (CoreClock or similar is what that would be called, although nothing like that exists right now) that the FPGA business unit produces. Rename it to clk-pic32, matching the prefix used by most functions in the driver. As far as I can tell, impact on whatever users may (or may not...) exist for the platform is minimal as it's built-in only and the functions are called directly from clk-pic32mzda.c Reviewed-by: Brian Masney Signed-off-by: Conor Dooley --- drivers/clk/microchip/Makefile | 2 +- drivers/clk/microchip/{clk-core.c => clk-pic32.c} | 2 +- drivers/clk/microchip/{clk-core.h => clk-pic32.h} | 0 drivers/clk/microchip/clk-pic32mzda.c | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename drivers/clk/microchip/{clk-core.c => clk-pic32.c} (99%) rename drivers/clk/microchip/{clk-core.h => clk-pic32.h} (100%) diff --git a/drivers/clk/microchip/Makefile b/drivers/clk/microchip/Makefile index 13250e04e46c..8e60bc1a03ae 100644 --- a/drivers/clk/microchip/Makefile +++ b/drivers/clk/microchip/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_COMMON_CLK_PIC32) += clk-core.o +obj-$(CONFIG_COMMON_CLK_PIC32) += clk-pic32.o obj-$(CONFIG_PIC32MZDA) += clk-pic32mzda.o obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs.o obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs-ccc.o diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-pic32.c similarity index 99% rename from drivers/clk/microchip/clk-core.c rename to drivers/clk/microchip/clk-pic32.c index 692152b5094e..9d128fba2cde 100644 --- a/drivers/clk/microchip/clk-core.c +++ b/drivers/clk/microchip/clk-pic32.c @@ -11,7 +11,7 @@ #include #include -#include "clk-core.h" +#include "clk-pic32.h" /* OSCCON Reg fields */ #define OSC_CUR_MASK 0x07 diff --git a/drivers/clk/microchip/clk-core.h b/drivers/clk/microchip/clk-pic32.h similarity index 100% rename from drivers/clk/microchip/clk-core.h rename to drivers/clk/microchip/clk-pic32.h diff --git a/drivers/clk/microchip/clk-pic32mzda.c b/drivers/clk/microchip/clk-pic32mzda.c index 27599829ea40..e11cbdd982a6 100644 --- a/drivers/clk/microchip/clk-pic32mzda.c +++ b/drivers/clk/microchip/clk-pic32mzda.c @@ -14,7 +14,7 @@ #include #include -#include "clk-core.h" +#include "clk-pic32.h" /* FRC Postscaler */ #define OSC_FRCDIV_MASK 0x07 From 7d8bf3d8f91073f4db347ed3aa6302b56107499c Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 17 Mar 2026 17:05:44 +0000 Subject: [PATCH 0473/5214] media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si syzbot reported a general protection fault in vidtv_psi_ts_psi_write_into [1]. vidtv_mux_get_pid_ctx() can return NULL, but vidtv_mux_push_si() does not check for this before dereferencing the returned pointer to access the continuity counter. This leads to a general protection fault when accessing a near-NULL address. The root cause is that vidtv_mux_pid_ctx_init() does not check the return value of vidtv_mux_create_pid_ctx_once() for PMT section PIDs. If the allocation fails, the PID context is never created, but init returns success. The subsequent vidtv_mux_push_si() call then gets NULL from vidtv_mux_get_pid_ctx() and crashes. Fix both the root cause (add error check in vidtv_mux_pid_ctx_init for PMT PIDs) and add defensive NULL checks in vidtv_mux_push_si for all vidtv_mux_get_pid_ctx() calls. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: events vidtv_mux_tick RIP: 0010:vidtv_psi_ts_psi_write_into+0x54a/0xbc0 drivers/media/test-drivers/vidtv/vidtv_psi.c:197 Call Trace: vidtv_psi_table_header_write_into drivers/media/test-drivers/vidtv/vidtv_psi.c:799 [inline] vidtv_psi_pmt_write_into+0x3b2/0xa70 drivers/media/test-drivers/vidtv/vidtv_psi.c:1231 vidtv_mux_push_si+0x932/0xe80 drivers/media/test-drivers/vidtv/vidtv_mux.c:196 vidtv_mux_tick+0xe9b/0x1480 drivers/media/test-drivers/vidtv/vidtv_mux.c:408 Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Reported-by: syzbot+814c351d094f4f1a1b86@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=814c351d094f4f1a1b86 Signed-off-by: Ruslan Valiyev Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_mux.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c index f0134e38a1fb..ea2ea53102f9 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_mux.c +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m) /* add a ctx for all PMT sections */ while (p) { pid = vidtv_psi_get_pat_program_pid(p); - vidtv_mux_create_pid_ctx_once(m, pid); + if (!vidtv_mux_create_pid_ctx_once(m, pid)) + goto free; p = p->next; } @@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID); eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID); + if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx) + return 0; + pat_args.offset = m->mux_buf_offset; pat_args.continuity_counter = &pat_ctx->cc; @@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) } pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); + if (!pmt_ctx) + continue; pmt_args.offset = m->mux_buf_offset; pmt_args.pmt = m->si.pmt_secs[i]; From 34ca1065a4b2cf776c28d7cece6f6c9d90f7e3a3 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:53 +0800 Subject: [PATCH 0474/5214] media: ti: vpe: Fix fwnode_handle leak in vip_probe_complete() In vip_probe_complete(), the fwnode_handle reference is not released if the loop continues via the default switch case or if alloc_port() fails. This results in a reference count leak. Switch to using the __free(fwnode_handle) cleanup attribute to ensure the reference is automatically released when the handle goes out of scope. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index 0e91e87bda9b..a3c5e966f64c 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -3389,7 +3390,6 @@ static int vip_probe_complete(struct platform_device *pdev) struct vip_port *port; struct vip_dev *dev; struct device_node *parent = pdev->dev.of_node; - struct fwnode_handle *ep = NULL; unsigned int syscon_args[5]; int ret, i, slice_id, port_id, p; @@ -3411,8 +3411,9 @@ static int vip_probe_complete(struct platform_device *pdev) ctrl->syscon_bit_field[i] = syscon_args[i + 1]; for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { - ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent), - p, 0); + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_next_endpoint_by_regs( + of_fwnode_handle(parent), p, 0); if (!ep) continue; @@ -3447,7 +3448,6 @@ static int vip_probe_complete(struct platform_device *pdev) port = dev->ports[port_id]; vip_register_subdev_notify(port, ep); - fwnode_handle_put(ep); } return 0; } From 5e78a431a3f74c632351791d7902c60199d1ce83 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:54 +0800 Subject: [PATCH 0475/5214] media: ti: vpe: Fix the error code of devm_request_irq() Return the actual error code from devm_request_irq() instead of incorrectly returning -ENOMEM. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index a3c5e966f64c..bd34c982b427 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -3472,7 +3472,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq, 0, VIP_MODULE_NAME, dev); if (ret < 0) - return -ENOMEM; + return ret; spin_lock_init(&dev->slock); mutex_init(&dev->mutex); From e8f319eae96a3d718e810d52432020a2b77f5f60 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:55 +0800 Subject: [PATCH 0476/5214] media: ti: vpe: Fix the error code of devm_kzalloc() in vip_probe_slice() In vip_probe_slice(), the error check for devm_kzalloc() incorrectly uses PTR_ERR_OR_ZERO() which returns 0 for NULL pointer. Return -ENOMEM for devm_kzalloc() failure. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index bd34c982b427..cb0a5a07a3d4 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -3490,7 +3490,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL); if (!parser) - return PTR_ERR_OR_ZERO(parser); + return -ENOMEM; parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER); if (IS_ERR(parser->base)) @@ -3502,7 +3502,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->sc_assigned = VIP_NOT_ASSIGNED; sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL); if (!sc) - return PTR_ERR_OR_ZERO(sc); + return -ENOMEM; sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC); if (IS_ERR(sc->base)) @@ -3514,7 +3514,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->csc_assigned = VIP_NOT_ASSIGNED; csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL); if (!csc) - return PTR_ERR_OR_ZERO(csc); + return -ENOMEM; csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC); if (IS_ERR(csc->base)) From 165a7c73123eabd0f4f496601c811a23930d5671 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Fri, 20 Mar 2026 08:31:54 +0100 Subject: [PATCH 0477/5214] staging: media: av7110: remove dead code in av7110.c Remove commented-out line of dead code that serves no purpose. Signed-off-by: Tomasz Unger Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 014d0c6f0a8b..094207093b62 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -762,7 +762,6 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) u16 buf[20]; int ret, i; u16 handle; -// u16 mode = 0x0320; u16 mode = 0xb96a; dprintk(4, "%p\n", av7110); From b3c33615e56b830397965c20d4994b274edcd9df Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Fri, 20 Mar 2026 11:07:37 +0100 Subject: [PATCH 0478/5214] staging: media: av7110: remove print_time() dead code The DEBUG_TIMING macro is commented out and can never be defined, making the print_time() function body always empty. Remove the commented-out macro, the unused function definition and all its call sites as they serve no purpose. Signed-off-by: Tomasz Unger Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 094207093b62..edf1ef937354 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -314,17 +314,6 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } } -//#define DEBUG_TIMING -static inline void print_time(char *s) -{ -#ifdef DEBUG_TIMING - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - pr_info("%s(): %ptSp\n", s, &ts); -#endif -} - #define DEBI_READ 0 #define DEBI_WRITE 1 static inline void start_debi_dma(struct av7110 *av7110, int dir, @@ -353,7 +342,6 @@ static void debiirq(struct tasklet_struct *t) int handle = (type >> 8) & 0x1f; unsigned int xfer = 0; - print_time("debi"); dprintk(4, "type 0x%04x\n", type); if (type == -1) { @@ -473,7 +461,6 @@ static void gpioirq(struct tasklet_struct *t) txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); len = (av7110->debilen + 3) & ~3; - print_time("gpio"); dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); switch (av7110->debitype & 0xff) { @@ -2784,8 +2771,6 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr) { struct av7110 *av7110 = dev->ext_priv; - //print_time("av7110_irq"); - /* Note: Don't try to handle the DEBI error irq (MASK_18), in * intel mode the timeout is asserted all the time... */ From d1162a5adbb5e95953d460b5bde3a04cd4473fe9 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 25 Mar 2026 13:57:42 +0300 Subject: [PATCH 0479/5214] media: synopsys: hdmirx: Fix HPD lane hold time Increase time of holding HPD lane low by 50ms. This fixes EDID change not detected by source/display side. Fixes: 7b59b132ad43 ("media: platform: synopsys: Add support for HDMI input driver") Cc: stable@vger.kernel.org Reported-by: Ross Cawston Closes: https://lore.kernel.org/linux-rockchip/20260209061654.54757-1-ross@r-sc.ca/ Signed-off-by: Dmitry Osipenko Signed-off-by: Hans Verkuil --- drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 61ad20b18b8d..4c8957505a50 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -506,9 +506,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en) hdmirx_writel(hdmirx_dev, CORE_CONFIG, hdmirx_dev->hpd_trigger_level_high ? en : !en); - /* 100ms delay as per HDMI spec */ + /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */ if (!en) - msleep(100); + msleep(100 + 50); } static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev, From 253c8ef7d57da0c74db251f385324faaa5ae2257 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 11:23:30 +0000 Subject: [PATCH 0480/5214] media: aspeed: fix missing of_reserved_mem_device_release() on probe failure aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list. The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it. Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/aspeed/aspeed-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 41cb96f60110..a292275f6b7b 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2343,6 +2343,7 @@ static int aspeed_video_probe(struct platform_device *pdev) rc = aspeed_video_setup_video(video); if (rc) { aspeed_video_free_buf(video, &video->jpeg); + of_reserved_mem_device_release(&pdev->dev); clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; From 54e8f9888547eb916cabcc0bc0bc39730324c9ee Mon Sep 17 00:00:00 2001 From: Chethan C Date: Sat, 28 Mar 2026 22:20:34 +0530 Subject: [PATCH 0481/5214] staging: media: av7110: fix coding style Fix indentation and alignment issues reported by checkpatch.pl. Rename enums av7110_rec_play_state, av7110_type_rec_play_format, and av7110_encoder_command to follow kernel naming style. Rename wssData and wssMode to wss_data and wss_mode to avoid camelCase identifiers. Signed-off-by: Chethan C Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 31 +++++--- drivers/staging/media/av7110/av7110.h | 4 +- drivers/staging/media/av7110/av7110_av.c | 89 ++++++++++++++++------- drivers/staging/media/av7110/av7110_ca.c | 3 +- drivers/staging/media/av7110/av7110_hw.h | 71 +++++++++--------- drivers/staging/media/av7110/av7110_ir.c | 4 +- drivers/staging/media/av7110/av7110_v4l.c | 22 +++--- 7 files changed, 134 insertions(+), 90 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index edf1ef937354..656bffd8c3d6 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -121,19 +121,22 @@ static void init_av7110_av(struct av7110 *av7110) if (ret < 0) pr_err("cannot set internal volume to maximum:%d\n", ret); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)av7110->display_ar); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, av7110->display_ar); if (ret < 0) pr_err("unable to set aspect ratio\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); if (ret < 0) pr_err("unable to set pan scan\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 2, wss_cfg_4_3); if (ret < 0) pr_err("unable to configure 4:3 wss\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 3, wss_cfg_16_9); if (ret < 0) pr_err("unable to configure 16:9 wss\n"); @@ -704,7 +707,8 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, if (av7110->audiostate.bypass_mode) aflags |= 0x8000; - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_MULTI_PID, 6, pcrpid, vpid, apid, ttpid, subpid, aflags); } @@ -771,7 +775,7 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); } - buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_ADD_PID_FILTER; buf[1] = 16; buf[2] = dvbdmxfeed->pid; buf[3] = mode; @@ -814,7 +818,7 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110->handle2filter[handle] = NULL; - buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_DEL_PID_FILTER; buf[1] = 1; buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); @@ -859,7 +863,8 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) if (dvbdmxfeed->pes_type < 2 && npids[0]) if (av7110->fe_synced) { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); if (ret) return ret; } @@ -1897,11 +1902,13 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } else { ret = SetPIDs(av7110, 0, 0, 0, 0, 0); if (!ret) { - ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, + AV7110_FLUSH_TS_QUEUE, 0); if (!ret) ret = av7110_wait_msgstate(av7110, GPMQBusy); } diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index b584754f4be0..797dda437cbe 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -243,8 +243,8 @@ struct av7110 { struct dvb_video_events video_events; video_size_t video_size; - u16 wssMode; - u16 wssData; + u16 wss_mode; + u16 wss_data; struct infrared ir; diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 2993ac43c49c..22f1335d371b 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -111,7 +111,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, if (av7110->playing || (av7110->rec_mode & av)) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); dvbdmx->recording = 1; av7110->rec_mode |= av; @@ -121,7 +121,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[0]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[0]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: @@ -129,7 +131,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_AV: @@ -141,7 +145,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -158,7 +164,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av) if (av7110->playing & av) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing == RP_NONE) { av7110_ipack_reset(&av7110->ipack[0]); @@ -168,15 +174,21 @@ int av7110_av_start_play(struct av7110 *av7110, int av) av7110->playing |= av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); av7110->sinfo = 0; break; case RP_AV: av7110->sinfo = 0; - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -190,15 +202,19 @@ int av7110_av_stop(struct av7110 *av7110, int av) if (!(av7110->playing & av) && !(av7110->rec_mode & av)) return 0; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing) { av7110->playing &= ~av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: ret = av7110_set_vidmode(av7110, av7110->vidmode); @@ -208,10 +224,14 @@ int av7110_av_stop(struct av7110 *av7110, int av) av7110->rec_mode &= ~av; switch (av7110->rec_mode) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: break; @@ -325,7 +345,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) dprintk(2, "av7110:%p\n", av7110); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_LOAD_VID_CODE, 1, mode); if (!ret && !av7110->playing) { ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], @@ -333,7 +354,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } return ret; } @@ -1168,7 +1190,8 @@ static int dvb_video_ioctl(struct file *file, } if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_STOP, 0); if (ret) break; av7110->playing &= ~RP_VIDEO; @@ -1184,7 +1207,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_FREEZE: av7110->videostate.play_state = VIDEO_FREEZED; if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PAUSE, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); if (!ret) @@ -1193,7 +1217,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_CONTINUE: if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_CONTINUE, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) { @@ -1248,8 +1273,9 @@ static int dvb_video_ioctl(struct file *file, if (ret < 0) break; av7110->videostate.display_format = format; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); break; } @@ -1259,8 +1285,8 @@ static int dvb_video_ioctl(struct file *file, break; } av7110->display_ar = arg; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)arg); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, arg); break; #ifdef CONFIG_COMPAT @@ -1291,7 +1317,8 @@ static int dvb_video_ioctl(struct file *file, //note: arg is ignored by firmware if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); if (!ret) { @@ -1303,7 +1330,9 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_SLOWMOTION: if (av7110->playing & RP_VIDEO) { if (av7110->trickmode != TRICK_SLOW) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } else { @@ -1329,15 +1358,18 @@ static int dvb_video_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[1]); if (av7110->playing == RP_AV) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); if (ret) break; if (av7110->trickmode == TRICK_FAST) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); if (av7110->trickmode == TRICK_SLOW) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Slow, 2, 0, 0); + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } @@ -1483,7 +1515,8 @@ static int dvb_audio_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[0]); if (av7110->playing == RP_AV) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; case AUDIO_SET_ID: diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 63d9c97a5190..24876c8fdc8c 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -307,7 +307,8 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; } - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_DESCR, 5, (descr->index << 8) | descr->parity, (descr->cw[0] << 8) | descr->cw[1], (descr->cw[2] << 8) | descr->cw[3], diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index d4579f411c56..3afe474da871 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -21,12 +21,12 @@ enum av7110_bootstate { }; enum av7110_type_rec_play_format { - RP_None, - AudioPES, - AudioMp2, - AudioPCM, - VideoPES, - AV_PES + AV7110_RP_NONE, + AV7110_AUDIO_PES, + AV7110_AUDIO_MP2, + AV7110_AUDIO_PCM, + AV7110_VIDEO_PES, + AV7110_AV_PES, }; enum av7110_osd_palette_type { @@ -112,19 +112,19 @@ enum av7110_osd_command { }; enum av7110_pid_command { - MultiPID, - VideoPID, - AudioPID, - InitFilt, - FiltError, - NewVersion, - CacheError, - AddPIDFilter, - DelPIDFilter, - Scan, - SetDescr, - SetIR, - FlushTSQueue + AV7110_MULTI_PID, + AV7110_VIDEO_PID, + AV7110_AUDIO_PID, + AV7110_INIT_FILT, + AV7110_FILT_ERROR, + AV7110_NEW_VERSION, + AV7110_CACHE_ERROR, + AV7110_ADD_PID_FILTER, + AV7110_DEL_PID_FILTER, + AV7110_SCAN, + AV7110_SET_DESCR, + AV7110_SET_IR, + AV7110_FLUSH_TS_QUEUE, }; enum av7110_mpeg_command { @@ -158,24 +158,24 @@ enum av7110_request_command { }; enum av7110_encoder_command { - SetVidMode, - SetTestMode, - LoadVidCode, - SetMonitorType, - SetPanScanType, - SetFreezeMode, - SetWSSConfig + AV7110_SET_VID_MODE, + AV7110_SET_TEST_MODE, + AV7110_LOAD_VID_CODE, + AV7110_SET_MONITOR_TYPE, + AV7110_SET_PANSCAN_TYPE, + AV7110_SET_FREEZE_MODE, + AV7110_SET_WSS_CONFIG, }; enum av7110_rec_play_state { - __Record, - __Stop, - __Play, - __Pause, - __Slow, - __FF_IP, - __Scan_I, - __Continue + AV7110_REC_PLAY_RECORD, + AV7110_REC_PLAY_STOP, + AV7110_REC_PLAY_PLAY, + AV7110_REC_PLAY_PAUSE, + AV7110_REC_PLAY_SLOW, + AV7110_REC_PLAY_FF_IP, + AV7110_REC_PLAY_SCAN_I, + AV7110_REC_PLAY_CONTINUE, }; enum av7110_fw_cmd_misc { @@ -452,7 +452,8 @@ static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) { - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_VID_MODE, 1, mode); } static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index fdae467fd7ab..9b7bf1857868 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -71,8 +71,8 @@ int av7110_set_ir_config(struct av7110 *av7110) { dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_IR, 1, av7110->ir.ir_config); } static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 200a7a29ea31..aed7581fa8a5 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -556,7 +556,7 @@ static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, if (FW_VERSION(av7110->arm_app) < 0x2623) return -EINVAL; memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - if (av7110->wssMode) { + if (av7110->wss_mode) { f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; } @@ -596,14 +596,14 @@ static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, return -EINVAL; if (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) { /* WSS controlled by userspace */ - av7110->wssMode = 1; - av7110->wssData = 0; + av7110->wss_mode = 1; + av7110->wss_data = 0; } else { /* WSS controlled by firmware */ - av7110->wssMode = 0; - av7110->wssData = 0; + av7110->wss_mode = 0; + av7110->wss_data = 0; return av7110_fw_cmd(av7110, COMTYPE_ENCODER, - SetWSSConfig, 1, 0); + AV7110_SET_WSS_CONFIG, 1, 0); } return 0; } @@ -616,17 +616,19 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size int rc; dprintk(2, "\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d)) + if (FW_VERSION(av7110->arm_app) < 0x2623 || + !av7110->wss_mode || count != sizeof(d)) return -EINVAL; if (copy_from_user(&d, data, count)) return -EFAULT; if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) return -EINVAL; if (d.id) - av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; + av7110->wss_data = ((d.data[1] << 8) & 0x3f00) | d.data[0]; else - av7110->wssData = 0x8000; - rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); + av7110->wss_data = 0x8000; + rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 1, av7110->wss_data); return (rc < 0) ? rc : count; } From 60ca00792bce46ec170c7ed101f376186d4cf8a9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:17:49 +0000 Subject: [PATCH 0482/5214] media: nuvoton: npcm-video: fix error handling in npcm_video_init() npcm_video_init() has two error handling issues after of_reserved_mem_device_init() is called: When dma_set_mask_and_coherent() fails, the function releases the reserved memory but does not return, allowing execution to fall through into npcm_video_ece_init() with a failed DMA configuration. When npcm_video_ece_init() fails, the function returns an error without calling of_reserved_mem_device_release(), leaking the reserved memory association. Fix both by adding the missing return after the DMA mask failure and adding the missing of_reserved_mem_device_release() call on the ECE init error path. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index b2a562e1ee1c..5c6bddfe8073 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video) if (rc) { dev_err(dev, "Failed to set DMA mask\n"); of_reserved_mem_device_release(dev); + return rc; } rc = npcm_video_ece_init(video); if (rc) { + of_reserved_mem_device_release(dev); dev_err(dev, "Failed to initialize ECE\n"); return rc; } From 50cc0e547da50b887e63dfa1ad203cd5b735d01e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:18:09 +0000 Subject: [PATCH 0483/5214] media: nuvoton: npcm-video: fix memory leaks in probe and remove npcm_video_probe() allocates the npcm_video structure with kzalloc_obj() but never frees it on any probe error path or in npcm_video_remove(), leaking the allocation on every failed probe and every normal unbind. Additionally, when npcm_video_setup_video() fails, the reserved memory association established by of_reserved_mem_device_init() in npcm_video_init() is not released, leaking the rmem_assigned_device entry on the global list. Fix both by adding kfree(video) to all probe error paths and to npcm_video_remove(), and adding the missing of_reserved_mem_device_release() call when npcm_video_setup_video() fails. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 5c6bddfe8073..52505af35c08 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1750,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev) regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n"); - return PTR_ERR(regs); + rc = PTR_ERR(regs); + goto err_free; } video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs, &npcm_video_regmap_cfg); if (IS_ERR(video->vcd_regmap)) { dev_err(&pdev->dev, "Failed to initialize VCD regmap\n"); - return PTR_ERR(video->vcd_regmap); + rc = PTR_ERR(video->vcd_regmap); + goto err_free; } video->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(video->reset)) { dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n"); - return PTR_ERR(video->reset); + rc = PTR_ERR(video->reset); + goto err_free; } video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgcr"); - if (IS_ERR(video->gcr_regmap)) - return PTR_ERR(video->gcr_regmap); + if (IS_ERR(video->gcr_regmap)) { + rc = PTR_ERR(video->gcr_regmap); + goto err_free; + } video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgfxi"); - if (IS_ERR(video->gfx_regmap)) - return PTR_ERR(video->gfx_regmap); + if (IS_ERR(video->gfx_regmap)) { + rc = PTR_ERR(video->gfx_regmap); + goto err_free; + } rc = npcm_video_init(video); if (rc) - return rc; + goto err_free; rc = npcm_video_setup_video(video); if (rc) - return rc; + goto err_release_mem; dev_info(video->dev, "NPCM video driver probed\n"); return 0; + +err_release_mem: + of_reserved_mem_device_release(&pdev->dev); +err_free: + kfree(video); + return rc; } static void npcm_video_remove(struct platform_device *pdev) @@ -1800,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev) v4l2_device_unregister(v4l2_dev); if (video->ece.enable) npcm_video_ece_stop(video); + kfree(video); of_reserved_mem_device_release(dev); } From 237b133817b99af32f5d698f1e45c3f029e41792 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 29 Mar 2026 19:39:42 +0100 Subject: [PATCH 0484/5214] media: dead code cleanup in kconfig for VIDEO_SOLO6X10 The same kconfig 'select FONT_8x16' appears twice for VIDEO_SOLO6X10. I propose removing the second instance, as it is effectively dead code. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Signed-off-by: Hans Verkuil --- drivers/media/pci/solo6x10/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index adb247847e00..cc4a7b088370 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -8,7 +8,6 @@ config VIDEO_SOLO6X10 select VIDEOBUF2_DMA_SG select VIDEOBUF2_DMA_CONTIG select SND_PCM - select FONT_8x16 help This driver supports the Bluecherry H.264 and MPEG-4 hardware compression capture cards and other Softlogic-based ones. From 99feb05904bf6ee58077f179fc3aa7c19ed891e2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:36 +0200 Subject: [PATCH 0485/5214] media: si470x-usb: refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/radio/si470x/radio-si470x-usb.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 318b5f6d4202..869b1e7e34b9 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -565,8 +565,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, { struct si470x_device *radio; struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, int_end_size, retval; + int int_end_size, retval; unsigned char version_warning = 0; /* private data allocation and initialization */ @@ -595,12 +594,8 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, iface_desc = intf->cur_altsetting; /* Set up interrupt endpoint information. */ - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) - radio->int_in_endpoint = endpoint; - } - if (!radio->int_in_endpoint) { + retval = usb_find_int_in_endpoint(iface_desc, &radio->int_in_endpoint); + if (retval) { dev_info(&intf->dev, "could not find interrupt in endpoint\n"); retval = -EIO; goto err_usbbuf; From 84d87d51940044da8ed8ae838874d7edc8e979d7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:39 +0200 Subject: [PATCH 0486/5214] media: gspca: refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/gspca.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index f3d3f441c851..594d73e50b9f 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -208,22 +208,17 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, static void gspca_input_create_urb(struct gspca_dev *gspca_dev) { struct usb_interface *intf; - struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep; - int i; + int ret; if (gspca_dev->sd_desc->int_pkt_scan) { intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - intf_desc = intf->cur_altsetting; - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep = &intf_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(ep) && - usb_endpoint_xfer_int(ep)) { - alloc_and_submit_int_urb(gspca_dev, ep); - break; - } - } + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ep); + if (ret) + return; + + alloc_and_submit_int_urb(gspca_dev, ep); } } From b8b6b2bf0ee0c9d175e80effac6698e82739b7dc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:40 +0200 Subject: [PATCH 0487/5214] media: hdpvr: refactor endpoint lookup Use the common USB helper for looking up bulk-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/hdpvr/hdpvr-core.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index d42336836b18..849a2be416bd 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -265,13 +265,10 @@ static int hdpvr_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct hdpvr_device *dev; - struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client; #endif - size_t buffer_size; - int i; int dev_num; int retval = -ENOMEM; @@ -321,25 +318,18 @@ static int hdpvr_probe(struct usb_interface *interface, /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->bulk_in_endpointAddr && - usb_endpoint_is_bulk_in(endpoint)) { - /* USB interface description is buggy, reported max - * packet size is 512 bytes, windows driver uses 8192 */ - buffer_size = 8192; - dev->bulk_in_size = buffer_size; - dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - } - - } - if (!dev->bulk_in_endpointAddr) { + if (usb_find_bulk_in_endpoint(interface->cur_altsetting, &endpoint)) { v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); goto error_put_usb; } + /* + * USB interface description is buggy, reported max packet size is 512 + * bytes, windows driver uses 8192 + */ + dev->bulk_in_size = 8192; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + /* init the device */ if (hdpvr_device_init(dev)) { v4l2_err(&dev->v4l2_dev, "device init failed\n"); From fe90217607fc82010713aa5eb4d2518300fefe6a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:41 +0200 Subject: [PATCH 0488/5214] media: s2255: refactor endpoint lookup Use the common USB helper for looking up bulk-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 2c02873d09b5..0b8182edf8e4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2240,18 +2240,14 @@ static int s2255_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev_dbg(&interface->dev, "num EP: %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - dev->read_endpoint = endpoint->bEndpointAddress; - } - } - if (!dev->read_endpoint) { + if (usb_find_bulk_in_endpoint(iface_desc, &endpoint)) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto errorEP; } + + dev->read_endpoint = endpoint->bEndpointAddress; + timer_setup(&dev->timer, s2255_timer, 0); init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { From 2282f979560af6bbc8ee2c1ee8663197312cee5b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 12:08:31 +0200 Subject: [PATCH 0489/5214] media: vpif_capture: fix OF node reference imbalance The driver reuses the OF node of the parent device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the parent and releasing the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 4a5f8ae50b66 ("[media] davinci: vpif_capture: get subdevs from DT when available") Cc: stable@vger.kernel.org # 4.13 Cc: Kevin Hilman Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/davinci/vpif_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 15df3ea2f77e..91cb6223561a 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1498,7 +1498,7 @@ vpif_capture_get_pdata(struct platform_device *pdev, * video ports & endpoints data. */ if (pdev->dev.parent && pdev->dev.parent->of_node) - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) return pdev->dev.platform_data; From e42469304440d6b351b34eddb9284f198ceaf969 Mon Sep 17 00:00:00 2001 From: Josh Hesketh Date: Fri, 10 Apr 2026 16:25:02 +0100 Subject: [PATCH 0490/5214] staging: media: av7110: remove dead code from av7110_hw.c Remove functions av7110_reset_arm() and av7110_send_ci_cmd() which have both been disabled behind #if 0 since the introduction to staging. Code can be recovered from git history. Signed-off-by: Josh Hesketh Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110_hw.c | 46 ------------------------ 1 file changed, 46 deletions(-) diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 49ce295771e4..b0bd7666d48b 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -95,29 +95,6 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co return result; } -/* av7110 ARM core boot stuff */ -#if 0 -void av7110_reset_arm(struct av7110 *av7110) -{ - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - ARM_ResetMailBox(av7110); - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_ready = 1; - dprintk(1, "reset ARM\n"); -} -#endif /* 0 */ - static int waitdebi(struct av7110 *av7110, int adr, int state) { int k; @@ -498,29 +475,6 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) return ret; } -#if 0 -int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) -{ - int i, ret; - u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - for (i = 0; i < len && i < 32; i++) { - if (i % 2 == 0) - cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; - else - cmd[(i / 2) + 2] |= buf[i]; - } - - ret = av7110_send_fw_cmd(av7110, cmd, 18); - if (ret && ret != -ERESTARTSYS) - pr_err("%s(): error %d\n", __func__, ret); - return ret; -} -#endif /* 0 */ - int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, int request_buf_len, u16 *reply_buf, int reply_buf_len) { From 3eaac9e02d8591d3c790db572ef1c8fa5a841fdb Mon Sep 17 00:00:00 2001 From: Zhaoyang Yu <2426767509@qq.com> Date: Wed, 15 Apr 2026 14:49:09 +0000 Subject: [PATCH 0491/5214] media: dm1105: fix missing error check for dma_alloc_coherent The return value of dm1105_dma_map(), which handles DMA memory allocation, is ignored in dm1105_hw_init(). If dma_alloc_coherent() fails, the driver will proceed using a NULL pointer for DMA transfers, leading to a kernel oops or invalid hardware access. Fix this by checking the return value and propagating -ENOMEM on failure. Signed-off-by: Zhaoyang Yu <2426767509@qq.com> Signed-off-by: Hans Verkuil --- drivers/media/pci/dm1105/dm1105.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index bbd24769ae56..c33d811cbafa 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -768,6 +768,8 @@ static void dm1105_ir_exit(struct dm1105_dev *dm1105) static int dm1105_hw_init(struct dm1105_dev *dev) { + int ret; + dm1105_disable_irqs(dev); dm_writeb(DM1105_HOST_CTR, 0); @@ -778,7 +780,10 @@ static int dm1105_hw_init(struct dm1105_dev *dev) dm_writew(DM1105_TSCTR, 0xc10a); /* map DMA and set address */ - dm1105_dma_map(dev); + ret = dm1105_dma_map(dev); + if (ret) + return -ENOMEM; + dm1105_set_dma_addr(dev); /* big buffer */ dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); From 9aa21e1549db8882ff77b691e7714153df21dff0 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:28:26 +0800 Subject: [PATCH 0492/5214] media: vidtv: fix reference leak on failed device registration When platform_device_register() fails in vidtv_bridge_init(), the embedded struct device in vidtv_bridge_dev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vidtv_bridge_init() -> platform_device_register(&vidtv_bridge_dev) -> device_initialize(&vidtv_bridge_dev.dev) -> setup_pdev_dma_masks(&vidtv_bridge_dev) -> platform_device_add(&vidtv_bridge_dev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index a8a76434989c..fd69b4ee16f4 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void) int ret; ret = platform_device_register(&vidtv_bridge_dev); - if (ret) + if (ret) { + platform_device_put(&vidtv_bridge_dev); return ret; + } ret = platform_driver_register(&vidtv_bridge_driver); if (ret) From 33e2b833c66b890a0d71c4fa82d4c97143f7f75f Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:45:37 +0800 Subject: [PATCH 0493/5214] media: vimc: fix reference leak on failed device registration When platform_device_register() fails in vimc_init(), the embedded struct device in vimc_pdev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vimc_init() -> platform_device_register(&vimc_pdev) -> device_initialize(&vimc_pdev.dev) -> setup_pdev_dma_masks(&vimc_pdev) -> platform_device_add(&vimc_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: 4babf057c143f ("media: vimc: allocate vimc_device dynamically") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 15167e127461..fee0c7a09c4f 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -421,6 +421,7 @@ static int __init vimc_init(void) if (ret) { dev_err(&vimc_pdev.dev, "platform device registration failed (err=%d)\n", ret); + platform_device_put(&vimc_pdev); return ret; } From a07c179a92e949172ca52f6d4a13202ea88cd4b7 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:20:58 +0800 Subject: [PATCH 0494/5214] media: vivid: fix cleanup bugs in vivid_init() When platform_device_register() fails in vivid_init(), the embedded struct device in vivid_pdev has already been initialized by device_initialize(), but the failure path jumps to free_output_strings without dropping the device reference for the current platform device: vivid_init() -> platform_device_register(&vivid_pdev) -> device_initialize(&vivid_pdev.dev) -> setup_pdev_dma_masks(&vivid_pdev) -> platform_device_add(&vivid_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before jumping to the common cleanup path. Also, the unreg_driver label incorrectly calls platform_driver_register() instead of platform_driver_unregister(), which breaks cleanup when workqueue creation fails after successful driver registration. Fix that as well. The reference leak was identified by a static analysis tool I developed and confirmed by manual review. The incorrect cleanup call was found during code inspection. Fixes: f46d740fb0258 ("[media] vivid: turn this into a platform_device") Fixes: d7c969f37515d ("media: vivid: Add 'Is Connected To' menu controls") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index c8bf9b4d406c..62cfb5feb2cf 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -2289,8 +2289,10 @@ static int __init vivid_init(void) } } ret = platform_device_register(&vivid_pdev); - if (ret) + if (ret) { + platform_device_put(&vivid_pdev); goto free_output_strings; + } ret = platform_driver_register(&vivid_pdrv); if (ret) goto unreg_device; @@ -2311,7 +2313,7 @@ static int __init vivid_init(void) destroy_hdmi_wq: destroy_workqueue(update_hdmi_ctrls_workqueue); unreg_driver: - platform_driver_register(&vivid_pdrv); + platform_driver_unregister(&vivid_pdrv); unreg_device: platform_device_unregister(&vivid_pdev); free_output_strings: From 033ff0420e4c9c240ae5523fff39770298efa964 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 17 Apr 2026 14:53:30 +0800 Subject: [PATCH 0495/5214] media: marvell-cam: fix missing pci_disable_device() on remove During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure. However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup. Add the missing pci_disable_device() call to cafe_pci_remove(). Fixes: abfa3df36c01 ("[media] marvell-cam: Separate out the Marvell camera core") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/platform/marvell/cafe-driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index 632c15572aa8..22034df6cba9 100644 --- a/drivers/media/platform/marvell/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c @@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev) return; } cafe_shutdown(cam); + pci_disable_device(pdev); kfree(cam); } From 62a08bc8916ef36fddd5e4069df381f6379bbecb Mon Sep 17 00:00:00 2001 From: Philipp Matthias Hahn Date: Mon, 20 Apr 2026 15:54:43 +0200 Subject: [PATCH 0496/5214] media: gspca: Fix comment in sd_init() Fix spelling mistake of{ -> f}. Signed-off-by: Philipp Matthias Hahn Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/sonixb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 4d655e2da9cb..f251a886024f 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -943,7 +943,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - const __u8 stop = 0x09; /* Disable stream turn of LED */ + const __u8 stop = 0x09; /* Disable stream, turn off LED */ reg_w(gspca_dev, 0x01, &stop, 1); From e0f1c9a90ef665f2587c274a8fed59f2dfc575a6 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 22:16:31 +0900 Subject: [PATCH 0497/5214] media: ti: vpe: unwind v4l2 device registration on probe error If the vpe_top resource is missing, vpe_probe() returns -ENODEV after v4l2_device_register() has succeeded. Probe failures do not call the driver's remove callback, so the v4l2 device remains registered on that error path. Route that failure through the existing v4l2_device_unregister() unwind label, matching the other errors after v4l2_device_register(). Fixes: 4d59c7d45585 ("media: ti-vpe: vpe: Add missing null pointer checks") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vpe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index a7e5a85e72a1..81bd1f9cee30 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2539,7 +2539,8 @@ static int vpe_probe(struct platform_device *pdev) "vpe_top"); if (!dev->res) { dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); - return -ENODEV; + ret = -ENODEV; + goto v4l2_dev_unreg; } /* From 1a65db225b25bb8c8febf16974c060e0cc242eb9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 16:50:08 +0200 Subject: [PATCH 0498/5214] media: pci: dm1105: Free allocated workqueue Destroy allocated workqueue in remove() callback to free its resources, thus fixing memory leak. Fixes: 519a4bdcf822 ("V4L/DVB (11984): Add support for yet another SDMC DM1105 based DVB-S card.") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil --- drivers/media/pci/dm1105/dm1105.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index c33d811cbafa..80d2e143384b 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -1199,6 +1199,7 @@ static void dm1105_remove(struct pci_dev *pdev) dm1105_hw_exit(dev); free_irq(pdev->irq, dev); + destroy_workqueue(dev->wq); pci_iounmap(pdev, dev->io_mem); pci_release_regions(pdev); pci_disable_device(pdev); From fb00ee47e565e9928b6516cd92878200f47e0d3b Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Wed, 29 Apr 2026 13:37:51 -0400 Subject: [PATCH 0499/5214] staging: media: av7110: fix typo in av7110.c Fix spelling mistake: connectd -> connected Signed-off-by: Maha Maryam Javaid Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 656bffd8c3d6..862aee993889 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -2265,7 +2265,7 @@ static int frontend_init(struct av7110 *av7110) * original Roberto Deza's hardware: * * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. - * GPIO3 is in budget-patch hardware connectd to port B VSYNC + * GPIO3 is in budget-patch hardware connected to port B VSYNC * HS is an internal event of 7146, accessible with RPS * and temporarily raised high every n lines * (n in defined in the RPS_THRESH1 counter threshold) From 2fc030f9ff287efc4510f4ae5efee0f23e64b608 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 15:03:36 +0200 Subject: [PATCH 0500/5214] media: i2c: drop unneeded dependencies on OF_GPIO OF_GPIO is selected automatically on all OF systems. Any symbols it controls also provide stubs so there's really no reason to select it explicitly. For Kconfig entries that have no other dependencies: convert it to requiring OF to avoid new symbols popping up for everyone in make config, for others just drop it altogether. Signed-off-by: Bartosz Golaszewski Signed-off-by: Hans Verkuil --- drivers/media/i2c/Kconfig | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8f2ba4121586..a3ab48607dcf 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -237,7 +237,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -248,7 +248,7 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -268,7 +268,7 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" - depends on OF_GPIO + depends on OF help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. @@ -278,7 +278,7 @@ config VIDEO_IMX412 config VIDEO_IMX415 tristate "Sony IMX415 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -703,7 +703,7 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision @@ -1271,7 +1271,6 @@ config VIDEO_BT866 config VIDEO_ISL7998X tristate "Intersil ISL7998x video decoder" depends on VIDEO_DEV && I2C - depends on OF_GPIO select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1309,7 +1308,6 @@ config VIDEO_MAX9286 tristate "Maxim MAX9286 GMSL deserializer support" depends on I2C && I2C_MUX depends on VIDEO_DEV - depends on OF_GPIO select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER From 084973ebd67b28f0945c5d45408f86c58b540110 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 21:43:49 +0900 Subject: [PATCH 0501/5214] media: stm32: dcmi: unregister notifier on probe failure dcmi_graph_init() registers the async notifier before dcmi_probe() toggles the reset line. If reset_control_assert() or reset_control_deassert() fails afterwards, probe returns through err_cleanup and the driver core will not call dcmi_remove(). Unregister the notifier before cleaning it up on that error path, matching the successful remove path and the V4L2 async notifier lifetime rules. Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil Fixes: d079f94c9046 ("media: platform: Switch to v4l2_async_notifier_add_subdev") Cc: stable@vger.kernel.org [hverkuil: added Fixes tag] --- drivers/media/platform/st/stm32/stm32-dcmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index e5663fbe6422..eeb0199864dd 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2195,6 +2195,7 @@ static int dcmi_probe(struct platform_device *pdev) return 0; err_cleanup: + v4l2_async_nf_unregister(&dcmi->notifier); v4l2_async_nf_cleanup(&dcmi->notifier); err_media_entity_cleanup: media_entity_cleanup(&dcmi->vdev->entity); From 42ec65b46a4fc7565d48daa42bf025fdc67800eb Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:24:05 +0800 Subject: [PATCH 0502/5214] PCI: Use FIELD_MODIFY() instead of open-coding it Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> [bhelgaas: squash together] Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li # pcie-nxp-s32g.c Link: https://patch.msgid.link/20260430162420.42839-2-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-3-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-4-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-5-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-6-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-7-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-8-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-9-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-10-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-11-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-12-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-13-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-14-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-15-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-16-18255117159@163.com Link: https://patch.msgid.link/20260430162420.42839-17-18255117159@163.com --- drivers/pci/controller/dwc/pcie-al.c | 12 ++---- .../controller/dwc/pcie-designware-debugfs.c | 23 ++++------- .../pci/controller/dwc/pcie-designware-ep.c | 3 +- drivers/pci/controller/dwc/pcie-designware.c | 3 +- drivers/pci/controller/dwc/pcie-eswin.c | 3 +- drivers/pci/controller/dwc/pcie-nxp-s32g.c | 3 +- drivers/pci/controller/dwc/pcie-qcom-common.c | 40 +++++++------------ drivers/pci/controller/dwc/pcie-qcom-ep.c | 6 +-- drivers/pci/controller/dwc/pcie-tegra194.c | 8 ++-- drivers/pci/controller/pci-mvebu.c | 3 +- drivers/pci/controller/pcie-mediatek-gen3.c | 3 +- drivers/pci/ide.c | 6 +-- drivers/pci/iov.c | 3 +- drivers/pci/msi/msi.c | 11 ++--- drivers/pci/pci.c | 3 +- drivers/pci/pcie/ptm.c | 3 +- drivers/pci/rebar.c | 6 +-- drivers/pci/setup-cardbus.c | 3 +- drivers/pci/tph.c | 10 ++--- 19 files changed, 51 insertions(+), 101 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 345c281c74fe..ef8c8ce7c78c 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -253,7 +253,6 @@ static int al_pcie_config_prepare(struct al_pcie *pcie) u8 subordinate_bus; u8 secondary_bus; u32 cfg_control; - u32 reg; ft = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); if (!ft) @@ -285,14 +284,9 @@ static int al_pcie_config_prepare(struct al_pcie *pcie) CFG_CONTROL; cfg_control = al_pcie_controller_readl(pcie, cfg_control_offset); - - reg = cfg_control & - ~(CFG_CONTROL_SEC_BUS_MASK | CFG_CONTROL_SUBBUS_MASK); - - reg |= FIELD_PREP(CFG_CONTROL_SUBBUS_MASK, subordinate_bus) | - FIELD_PREP(CFG_CONTROL_SEC_BUS_MASK, secondary_bus); - - al_pcie_controller_writel(pcie, cfg_control_offset, reg); + FIELD_MODIFY(CFG_CONTROL_SUBBUS_MASK, &cfg_control, subordinate_bus); + FIELD_MODIFY(CFG_CONTROL_SEC_BUS_MASK, &cfg_control, secondary_bus); + al_pcie_controller_writel(pcie, cfg_control_offset, cfg_control); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index d0884253be97..945f8f9b6d0e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -265,8 +265,7 @@ static ssize_t lane_detect_write(struct file *file, const char __user *buf, return ret; val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); - val &= ~(LANE_SELECT); - val |= FIELD_PREP(LANE_SELECT, lane); + FIELD_MODIFY(LANE_SELECT, &val, lane); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG, val); return count; @@ -339,14 +338,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, val |= ((err_inj_list[pdata->idx].err_inj_type << EINJ_TYPE_SHIFT) & type_mask); val |= FIELD_PREP(EINJ_COUNT, counter); - if (err_group == 1 || err_group == 4) { - val &= ~(EINJ_VAL_DIFF); - val |= FIELD_PREP(EINJ_VAL_DIFF, val_diff); - } - if (err_group == 4) { - val &= ~(EINJ_VC_NUM); - val |= FIELD_PREP(EINJ_VC_NUM, vc_num); - } + if (err_group == 1 || err_group == 4) + FIELD_MODIFY(EINJ_VAL_DIFF, &val, val_diff); + if (err_group == 4) + FIELD_MODIFY(EINJ_VC_NUM, &val, vc_num); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group), val); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ_ENABLE_REG, (0x1 << err_group)); @@ -362,9 +357,8 @@ static void set_event_number(struct dwc_pcie_rasdes_priv *pdata, val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); val &= ~EVENT_COUNTER_ENABLE; - val &= ~(EVENT_COUNTER_GROUP_SELECT | EVENT_COUNTER_EVENT_SELECT); - val |= FIELD_PREP(EVENT_COUNTER_GROUP_SELECT, event_list[pdata->idx].group_no); - val |= FIELD_PREP(EVENT_COUNTER_EVENT_SELECT, event_list[pdata->idx].event_no); + FIELD_MODIFY(EVENT_COUNTER_GROUP_SELECT, &val, event_list[pdata->idx].group_no); + FIELD_MODIFY(EVENT_COUNTER_EVENT_SELECT, &val, event_list[pdata->idx].event_no); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); } @@ -469,8 +463,7 @@ static ssize_t counter_lane_write(struct file *file, const char __user *buf, mutex_lock(&rinfo->reg_event_lock); set_event_number(pdata, pci, rinfo); val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); - val &= ~(EVENT_COUNTER_LANE_SELECT); - val |= FIELD_PREP(EVENT_COUNTER_LANE_SELECT, lane); + FIELD_MODIFY(EVENT_COUNTER_LANE_SELECT, &val, lane); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); mutex_unlock(&rinfo->reg_event_lock); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index d4dc3b24da60..88e7fc3d5e9d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -707,8 +707,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, reg = ep_func->msi_cap + PCI_MSI_FLAGS; val = dw_pcie_ep_readw_dbi(ep, func_no, reg); - val &= ~PCI_MSI_FLAGS_QMASK; - val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, mmc); + FIELD_MODIFY(PCI_MSI_FLAGS_QMASK, &val, mmc); dw_pcie_dbi_ro_wr_en(pci); dw_pcie_ep_writew_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..bcfc7bfcf232 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -938,8 +938,7 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes) cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP); - lnkcap &= ~PCI_EXP_LNKCAP_MLW; - lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes); + FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, num_lanes); dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap); } diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c index 2845832b3824..ce8d64f8a395 100644 --- a/drivers/pci/controller/dwc/pcie-eswin.c +++ b/drivers/pci/controller/dwc/pcie-eswin.c @@ -211,8 +211,7 @@ static int eswin_pcie_host_init(struct dw_pcie_rp *pp) /* Configure Root Port type */ val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET); - val &= ~PCIEELBI_CTRL0_DEV_TYPE; - val |= FIELD_PREP(PCIEELBI_CTRL0_DEV_TYPE, PCI_EXP_TYPE_ROOT_PORT); + FIELD_MODIFY(PCIEELBI_CTRL0_DEV_TYPE, &val, PCI_EXP_TYPE_ROOT_PORT); writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET); list_for_each_entry(port, &pcie->ports, list) { diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c index b3ec38099fa3..31e1169b8ab6 100644 --- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c @@ -139,8 +139,7 @@ static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) /* Set RP mode */ val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); - val &= ~DEVICE_TYPE_MASK; - val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); + FIELD_MODIFY(DEVICE_TYPE_MASK, &val, PCI_EXP_TYPE_ROOT_PORT); /* Use default CRNS */ val &= ~SRIS_MODE; diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 5aa73c628737..0da73caf2011 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -30,20 +30,15 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci) reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; - reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; - reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, - speed - PCIE_SPEED_8_0GT); + FIELD_MODIFY(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, ®, + speed - PCIE_SPEED_8_0GT); dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); - reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | - GEN3_EQ_FMDC_N_EVALS | - GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA | - GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA); - reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | - FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5); + FIELD_MODIFY(GEN3_EQ_FMDC_T_MIN_PHASE23, ®, 0x1); + FIELD_MODIFY(GEN3_EQ_FMDC_N_EVALS, ®, 0xd); + FIELD_MODIFY(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, ®, 0x5); + FIELD_MODIFY(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, ®, 0x5); dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); @@ -61,14 +56,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci) u32 reg; reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_1_OFF); - reg &= ~(MARGINING_MAX_VOLTAGE_OFFSET | - MARGINING_NUM_VOLTAGE_STEPS | - MARGINING_MAX_TIMING_OFFSET | - MARGINING_NUM_TIMING_STEPS); - reg |= FIELD_PREP(MARGINING_MAX_VOLTAGE_OFFSET, 0x24) | - FIELD_PREP(MARGINING_NUM_VOLTAGE_STEPS, 0x78) | - FIELD_PREP(MARGINING_MAX_TIMING_OFFSET, 0x32) | - FIELD_PREP(MARGINING_NUM_TIMING_STEPS, 0x10); + FIELD_MODIFY(MARGINING_MAX_VOLTAGE_OFFSET, ®, 0x24); + FIELD_MODIFY(MARGINING_NUM_VOLTAGE_STEPS, ®, 0x78); + FIELD_MODIFY(MARGINING_MAX_TIMING_OFFSET, ®, 0x32); + FIELD_MODIFY(MARGINING_NUM_TIMING_STEPS, ®, 0x10); dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_1_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_2_OFF); @@ -76,13 +67,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci) MARGINING_SAMPLE_REPORTING_METHOD | MARGINING_IND_LEFT_RIGHT_TIMING | MARGINING_VOLTAGE_SUPPORTED; - reg &= ~(MARGINING_IND_UP_DOWN_VOLTAGE | - MARGINING_MAXLANES | - MARGINING_SAMPLE_RATE_TIMING | - MARGINING_SAMPLE_RATE_VOLTAGE); - reg |= FIELD_PREP(MARGINING_MAXLANES, pci->num_lanes) | - FIELD_PREP(MARGINING_SAMPLE_RATE_TIMING, 0x3f) | - FIELD_PREP(MARGINING_SAMPLE_RATE_VOLTAGE, 0x3f); + reg &= ~MARGINING_IND_UP_DOWN_VOLTAGE; + FIELD_MODIFY(MARGINING_MAXLANES, ®, pci->num_lanes); + FIELD_MODIFY(MARGINING_SAMPLE_RATE_TIMING, ®, 0x3f); + FIELD_MODIFY(MARGINING_SAMPLE_RATE_VOLTAGE, ®, 0x3f); dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_2_OFF, reg); } EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_lane_margining); diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 257c2bcb5f76..56184e6ca6e6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -494,15 +494,13 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) /* Set the L0s Exit Latency to 2us-4us = 0x6 */ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); - val &= ~PCI_EXP_LNKCAP_L0SEL; - val |= FIELD_PREP(PCI_EXP_LNKCAP_L0SEL, 0x6); + FIELD_MODIFY(PCI_EXP_LNKCAP_L0SEL, &val, 0x6); dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val); /* Set the L1 Exit Latency to be 32us-64 us = 0x6 */ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); - val &= ~PCI_EXP_LNKCAP_L1EL; - val |= FIELD_PREP(PCI_EXP_LNKCAP_L1EL, 0x6); + FIELD_MODIFY(PCI_EXP_LNKCAP_L1EL, &val, 0x6); dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val); dw_pcie_dbi_ro_wr_dis(pci); diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 9dcfa194050e..3c831331338e 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -872,8 +872,7 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); - val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC; - val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, 0x3ff); + FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, 0x3ff); val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE; dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); @@ -883,9 +882,8 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); - val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC; - val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, - pcie->of_data->gen4_preset_vec); + FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, + pcie->of_data->gen4_preset_vec); val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE; dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a72aa57591c0..d6eb65b8cd7f 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -263,8 +263,7 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) * not set correctly then link with endpoint card is not established. */ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); - lnkcap &= ~PCI_EXP_LNKCAP_MLW; - lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, port->is_x4 ? 4 : 1); + FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, port->is_x4 ? 4 : 1); mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); /* Disable Root Bridge I/O space, memory space and bus mastering. */ diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b0accd828589..f36a616a8b52 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -494,8 +494,7 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) /* Set Link Control 2 (LNKCTL2) speed restriction, if any */ if (pcie->max_link_speed) { val = readl_relaxed(pcie->base + PCIE_CONF_LINK2_CTL_STS); - val &= ~PCIE_CONF_LINK2_LCR2_LINK_SPEED; - val |= FIELD_PREP(PCIE_CONF_LINK2_LCR2_LINK_SPEED, pcie->max_link_speed); + FIELD_MODIFY(PCIE_CONF_LINK2_LCR2_LINK_SPEED, &val, pcie->max_link_speed); writel_relaxed(val, pcie->base + PCIE_CONF_LINK2_CTL_STS); } diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c index be74e8f0ae21..beb67b8fb5c5 100644 --- a/drivers/pci/ide.c +++ b/drivers/pci/ide.c @@ -170,8 +170,7 @@ void pci_ide_init(struct pci_dev *pdev) pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val); if (val & PCI_IDE_SEL_CTL_EN) continue; - val &= ~PCI_IDE_SEL_CTL_ID; - val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); + FIELD_MODIFY(PCI_IDE_SEL_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID); pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val); } @@ -182,8 +181,7 @@ void pci_ide_init(struct pci_dev *pdev) pci_read_config_dword(pdev, pos, &val); if (val & PCI_IDE_LINK_CTL_EN) continue; - val &= ~PCI_IDE_LINK_CTL_ID; - val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); + FIELD_MODIFY(PCI_IDE_LINK_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID); pci_write_config_dword(pdev, pos, val); } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 91ac4e37ecb9..fdae70abe804 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -946,8 +946,7 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev) pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl); size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]); - ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_VF_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_VF_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(dev, pos + PCI_VF_REBAR_CTRL, ctrl); } } diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 81d24a270a79..33c8b5b98684 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -201,8 +201,7 @@ static inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc, u16 msgctl; pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); - msgctl &= ~PCI_MSI_FLAGS_QSIZE; - msgctl |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, desc->pci.msi_attrib.multiple); + FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &msgctl, desc->pci.msi_attrib.multiple); pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo); @@ -532,9 +531,8 @@ void __pci_restore_msi_state(struct pci_dev *dev) pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); pci_msi_update_mask(entry, 0, 0); - control &= ~PCI_MSI_FLAGS_QSIZE; - control |= PCI_MSI_FLAGS_ENABLE | - FIELD_PREP(PCI_MSI_FLAGS_QSIZE, entry->pci.msi_attrib.multiple); + FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &control, entry->pci.msi_attrib.multiple); + control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); } @@ -970,8 +968,7 @@ int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag) if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual) return -ENXIO; - msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST; - msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag); + FIELD_MODIFY(PCI_MSIX_ENTRY_CTRL_ST, &msi_desc->pci.msix_ctrl, tag); pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl); /* Flush the write */ readl(pci_msix_desc_addr(msi_desc)); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..942f70f6a441 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5771,8 +5771,7 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc) if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) return -EIO; - cmd &= ~PCI_X_CMD_MAX_READ; - cmd |= FIELD_PREP(PCI_X_CMD_MAX_READ, v); + FIELD_MODIFY(PCI_X_CMD_MAX_READ, &cmd, v); if (pci_write_config_word(dev, cap + PCI_X_CMD, cmd)) return -EIO; } diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index a41ffd1914de..bd3bd39f6372 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -152,8 +152,7 @@ static int __pci_enable_ptm(struct pci_dev *dev) pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl); ctrl |= PCI_PTM_CTRL_ENABLE; - ctrl &= ~PCI_PTM_GRANULARITY_MASK; - ctrl |= FIELD_PREP(PCI_PTM_GRANULARITY_MASK, dev->ptm_granularity); + FIELD_MODIFY(PCI_PTM_GRANULARITY_MASK, &ctrl, dev->ptm_granularity); if (dev->ptm_root) ctrl |= PCI_PTM_CTRL_ROOT; diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index 39f8cf3b70d5..e3e0415fc29a 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -211,8 +211,7 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) return pos; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); if (pci_resource_is_iov(bar)) @@ -241,8 +240,7 @@ void pci_restore_rebar_state(struct pci_dev *pdev) bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); } } diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c index 1ebd13a1f730..f7c62054f227 100644 --- a/drivers/pci/setup-cardbus.c +++ b/drivers/pci/setup-cardbus.c @@ -253,8 +253,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */ - buses &= ~PCI_SEC_LATENCY_TIMER_MASK; - buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER); + FIELD_MODIFY(PCI_SEC_LATENCY_TIMER_MASK, &buses, CARDBUS_LATENCY_TIMER); /* We need to blast all three values with a single write */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c index 91145e8d9d95..655ffd60e62f 100644 --- a/drivers/pci/tph.c +++ b/drivers/pci/tph.c @@ -139,8 +139,7 @@ static void set_ctrl_reg_req_en(struct pci_dev *pdev, u8 req_type) pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); - reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, req_type); + FIELD_MODIFY(PCI_TPH_CTRL_REQ_EN_MASK, ®, req_type); pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); } @@ -427,11 +426,8 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode) /* Write them into TPH control register */ pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); - reg &= ~PCI_TPH_CTRL_MODE_SEL_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev->tph_mode); - - reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev->tph_req_type); + FIELD_MODIFY(PCI_TPH_CTRL_MODE_SEL_MASK, ®, pdev->tph_mode); + FIELD_MODIFY(PCI_TPH_CTRL_REQ_EN_MASK, ®, pdev->tph_req_type); pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); From 5e6c21c56998e1e58d2f314e70779989ea0fee5d Mon Sep 17 00:00:00 2001 From: Ben Reed Date: Tue, 5 May 2026 10:16:33 -0600 Subject: [PATCH 0503/5214] PCI: switchtec: Add Gen6 Device IDs Add device IDs for the next generation of switchtec products. No changes to the driver were required with the new version of the hardware. [logang: rewrote commit message] Signed-off-by: Ben Reed Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260505161633.67454-1-logang@deltatee.com --- drivers/pci/switch/switchtec.c | 16 ++++++++++++++++ include/linux/switchtec.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 93ebec94b763..41fc4b512708 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1852,6 +1852,22 @@ static const struct pci_device_id switchtec_pci_tbl[] = { SWITCHTEC_PCI_DEVICE(0x5552, SWITCHTEC_GEN5), /* PAXA 52XG5 */ SWITCHTEC_PCI_DEVICE(0x5536, SWITCHTEC_GEN5), /* PAXA 36XG5 */ SWITCHTEC_PCI_DEVICE(0x5528, SWITCHTEC_GEN5), /* PAXA 28XG5 */ + SWITCHTEC_PCI_DEVICE(0x6048, SWITCHTEC_GEN6), /* PFXs 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6064, SWITCHTEC_GEN6), /* PFXs 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6044, SWITCHTEC_GEN6), /* PFXs 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6060, SWITCHTEC_GEN6), /* PFXs 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6148, SWITCHTEC_GEN6), /* PSXs 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6164, SWITCHTEC_GEN6), /* PSXs 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6144, SWITCHTEC_GEN6), /* PSXs 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6160, SWITCHTEC_GEN6), /* PSXs 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6248, SWITCHTEC_GEN6), /* PFX 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6264, SWITCHTEC_GEN6), /* PFX 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6244, SWITCHTEC_GEN6), /* PFX 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6260, SWITCHTEC_GEN6), /* PFX 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6348, SWITCHTEC_GEN6), /* PSX 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6364, SWITCHTEC_GEN6), /* PSX 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6344, SWITCHTEC_GEN6), /* PSX 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6360, SWITCHTEC_GEN6), /* PSX 160XG6 */ SWITCHTEC_PCI100X_DEVICE(0x1001, SWITCHTEC_GEN4), /* PCI1001 16XG4 */ SWITCHTEC_PCI100X_DEVICE(0x1002, SWITCHTEC_GEN4), /* PCI1002 12XG4 */ SWITCHTEC_PCI100X_DEVICE(0x1003, SWITCHTEC_GEN4), /* PCI1003 16XG4 */ diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index cdb58d61c152..724da6c08bf7 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -42,6 +42,7 @@ enum switchtec_gen { SWITCHTEC_GEN3, SWITCHTEC_GEN4, SWITCHTEC_GEN5, + SWITCHTEC_GEN6, }; struct mrpc_regs { From d37a5467709e627370124d7346ef71ce605ababa Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:19 -0700 Subject: [PATCH 0504/5214] perf dwarf-aux: Fix libdw segmentation fault in cu_walk_functions_at A segmentation fault was observed in `libdw` when running `perf kmem` with `--page stat` on some workloads. The crash occurred deep inside `libdw` (specifically in `dwarf_child` and `dwarf_diename`) when processing DWARF information. The root cause was improper error handling of `dwarf_getfuncs` in `die_find_realfunc` and `die_find_tailfunc`. `dwarf_getfuncs` returns: - `0` on success (when all functions have been processed). - A positive offset if the callback aborts early (e.g., via `DWARF_CB_ABORT` when a match is found). - `-1` on error. The original code used `if (!dwarf_getfuncs(...)) return NULL;`. On error (`-1`), `!-1` evaluates to `0` (false), bypassing the error check. Execution then proceeded as if a match was found, returning uninitialized stack memory (`die_mem`) to the caller (`cu_walk_functions_at`). When `cu_walk_functions_at` passed this uninitialized memory to `libdw` via `dwarf_diename`, it caused a segmentation fault. Fix this by correcting the error check to `if (dwarf_getfuncs(...) <= 0)`. Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: d4c537e6bf86 ("perf probe: Ignore tail calls to probed functions") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 92db2fccc788..109a166a6d19 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -171,7 +171,6 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } return ret; - } /** @@ -620,7 +619,7 @@ Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, ad.addr = addr; ad.die_mem = die_mem; /* dwarf_getscopes can't find subprogram. */ - if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0)) + if (dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0) <= 0) return NULL; else return die_mem; @@ -659,7 +658,7 @@ Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, ad.addr = addr; ad.die_mem = die_mem; /* dwarf_getscopes can't find subprogram. */ - if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) + if (dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0) <= 0) return NULL; else return die_mem; From 17e9b4243e76f0a0fe951d30ce990d6081a8b426 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:20 -0700 Subject: [PATCH 0505/5214] perf dwarf-aux: Fix libdw API contract violations Check return values of `dwarf_decl_line` (where non-optional), `dwarf_getfuncs`, and `dwarf_lineaddr` to prevent using uninitialized stack variables or incorrectly reporting success on failure. For the root DIE in `die_walk_lines()`, `dwarf_decl_line` and `die_get_decl_file` are optional and their failures are handled gracefully to avoid breaking line walking on valid functions. Specifically, remove the strict `!decf` (declared file) check that would prematurely abort line walking on generated or artificial functions lacking this optional attribute. Additionally: - Add NULL pointer protection for `strcmp()` in `die_walk_lines()` when `inf` or `decf` are NULL to prevent crashes on generated code. - Use `dwarf_attr_integrate` in `die_get_data_member_location` to correctly resolve inherited member locations (e.g. via abstract origin or specification). Fixes: 57f95bf5f882 ("perf probe: Show correct statement line number by perf probe -l") Fixes: 3f4460a28fb2 ("perf probe: Filter out redundant inline-instances") Fixes: 75186a9b09e4 ("perf probe: Fix to show lines of sys_ functions correctly") Fixes: e0d153c69040 ("perf-probe: Move dwarf library routines to dwarf-aux.{c, h}") Fixes: 6243b9dc4c99 ("perf probe: Move dwarf specific functions to dwarf-aux.c") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 34 +++++++++++++++++----------------- tools/perf/util/dwarf-aux.h | 5 +++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 109a166a6d19..d7160f87ac7d 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -125,7 +125,8 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, Dwarf_Addr addr, && die_entrypc(&die_mem, &faddr) == 0 && faddr == addr) { *fname = die_get_decl_file(&die_mem); - dwarf_decl_line(&die_mem, lineno); + if (dwarf_decl_line(&die_mem, lineno) != 0) + return -ENOENT; goto out; } @@ -459,7 +460,7 @@ int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) size_t nexpr; int ret; - if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + if (dwarf_attr_integrate(mb_die, DW_AT_data_member_location, &attr) == NULL) return -ENOENT; if (dwarf_formudata(&attr, offs) != 0) { @@ -795,8 +796,7 @@ static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) /* Ignore redundant instances */ if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { - dwarf_decl_line(origin, &tmp); - if (die_get_call_lineno(inst) == tmp) { + if (dwarf_decl_line(origin, &tmp) == 0 && die_get_call_lineno(inst) == tmp) { tmp = die_get_decl_fileno(origin); if (die_get_call_fileno(inst) == tmp) return DIE_FIND_CB_CONTINUE; @@ -950,11 +950,6 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); dwarf_decl_line(rt_die, &decl); decf = die_get_decl_file(rt_die); - if (!decf) { - pr_debug2("Failed to get the declared file name of %s\n", - dwarf_diename(rt_die)); - return -EINVAL; - } } else cu_die = rt_die; if (!cu_die) { @@ -998,11 +993,12 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) if (die_find_inlinefunc(rt_die, addr, &die_mem)) { /* Call-site check */ inf = die_get_call_file(&die_mem); - if ((inf && !strcmp(inf, decf)) && + if ((inf == decf || (inf && decf && !strcmp(inf, decf))) && die_get_call_lineno(&die_mem) == lineno) goto found; - dwarf_decl_line(&die_mem, &inl); + if (dwarf_decl_line(&die_mem, &inl) != 0) + inl = 0; if (inl != decl || decf != die_get_decl_file(&die_mem)) continue; @@ -1034,8 +1030,10 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) .data = data, .retval = 0, }; - dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); - ret = param.retval; + if (dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0) < 0) + ret = -EINVAL; + else + ret = param.retval; } return ret; @@ -1939,10 +1937,12 @@ static bool die_get_postprologue_addr(unsigned long entrypc_idx, break; } - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); + if (dwarf_lineaddr(line, postprologue_addr) != 0) + return false; + if (*postprologue_addr >= highpc) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), postprologue_addr) != 0) + return false; + } return true; } diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index a79968a2e573..161f0bf980b6 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -10,6 +10,11 @@ #include #include +static inline const char *die_name(Dwarf_Die *die) +{ + return dwarf_diename(die) ?: ""; +} + struct strbuf; /* Find the realpath of the target file */ From 674dea094ef6594ea1aa0efb074a4646a81350b2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:21 -0700 Subject: [PATCH 0506/5214] perf srcline: Introduce inline_node__clear_frames() Introduce inline_node__clear_frames() to clean up partial allocations. This is a prerequisite for error handling in libdw inline unwinding. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/srcline.c | 9 ++++++++- tools/perf/util/srcline.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index db164d258163..62884428fb5a 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -429,10 +429,13 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, return addr2inlines(dso_name, addr, dso, sym); } -void inline_node__delete(struct inline_node *node) +void inline_node__clear_frames(struct inline_node *node) { struct inline_list *ilist, *tmp; + if (node == NULL) + return; + list_for_each_entry_safe(ilist, tmp, &node->val, list) { list_del_init(&ilist->list); zfree_srcline(&ilist->srcline); @@ -441,7 +444,11 @@ void inline_node__delete(struct inline_node *node) symbol__delete(ilist->symbol); free(ilist); } +} +void inline_node__delete(struct inline_node *node) +{ + inline_node__clear_frames(node); free(node); } diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h index 7c37b3bf9ce7..1018cbc886d6 100644 --- a/tools/perf/util/srcline.h +++ b/tools/perf/util/srcline.h @@ -47,6 +47,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, struct symbol *sym); /* free resources associated to the inline node list */ void inline_node__delete(struct inline_node *node); +void inline_node__clear_frames(struct inline_node *node); /* insert the inline node list into the DSO, which will take ownership */ void inlines__tree_insert(struct rb_root_cached *tree, From 0e18b5bb7c9d2b4c6295c5307658759fdc280e39 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:22 -0700 Subject: [PATCH 0507/5214] perf libdw: Fix callchain parent update in ORDER_CALLER mode Fix the parent srcline lookup in `libdw_a2l_cb()` to target the correct parent node depending on the callchain order (ORDER_CALLER/ORDER_CALLEE). This ensures inline callchains are not corrupted when nest depth > 2. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/libdw.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 216977884103..301642084c69 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -4,6 +4,7 @@ #include "srcline.h" #include "symbol.h" #include "dwarf-aux.h" +#include "callchain.h" #include #include #include @@ -80,7 +81,6 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); const char *call_fname = die_get_call_file(die); char *call_srcline = srcline__unknown; - struct inline_list *ilist; if (!inline_sym) return -ENOMEM; @@ -89,14 +89,20 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) if (call_fname) call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); - list_for_each_entry(ilist, &args->node->val, list) { - if (args->leaf_srcline == ilist->srcline) + if (!list_empty(&args->node->val)) { + struct inline_list *parent; + + if (callchain_param.order == ORDER_CALLEE) + parent = list_first_entry(&args->node->val, struct inline_list, list); + else + parent = list_last_entry(&args->node->val, struct inline_list, list); + + if (args->leaf_srcline == parent->srcline) args->leaf_srcline_used = false; - else if (ilist->srcline != srcline__unknown) - free(ilist->srcline); - ilist->srcline = call_srcline; + else if (parent->srcline != srcline__unknown) + free(parent->srcline); + parent->srcline = call_srcline; call_srcline = NULL; - break; } if (call_srcline && call_srcline != srcline__unknown) free(call_srcline); From 15f8c22c462b7670d3b047d46419a92f19819fc2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:23 -0700 Subject: [PATCH 0508/5214] perf libdw: Support DWARF line 0 in inline list Allow DWARF line 0 in `libdw_a2l_cb()`, as it is a valid reference for compiler-generated code. Filter `die_get_call_lineno` error codes (negative values), but fallback to line 0 if `call_fname` is present to preserve the caller's filename instead of discarding it entirely. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/libdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 301642084c69..196b9cdf51b2 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -80,6 +80,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) struct libdw_a2l_cb_args *args = _args; struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); const char *call_fname = die_get_call_file(die); + int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; if (!inline_sym) @@ -87,7 +88,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) /* Assign caller information to the parent. */ if (call_fname) - call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); + call_srcline = srcline_from_fileline(call_fname, call_lineno >= 0 ? call_lineno : 0); if (!list_empty(&args->node->val)) { struct inline_list *parent; From 9c6d535ddb794fb540c3b142ab1d7416d469c1de Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:24 -0700 Subject: [PATCH 0509/5214] perf libdw: Fix libdw API contract violations and memory leaks Check return values of `dwfl_report_end` and `dwfl_module_addrdie` to prevent using uninitialized stack variables or reporting success on failure. Additionally: - Ensure `*file` is freed and inline frames are cleared on error in `libdw__addr2line()` to prevent memory leaks and duplicated callchains when falling back to other unwinders. - Use `die_name()` safe wrapper inside the inline function unwinding callback (`libdw_a2l_cb`). - Refactor `libdw_a2l_cb`'s repeated memory error handling/cleanup paths using a cleaner goto control flow. Fixes: b7a2b011e9627ff3 ("perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line") Fixes: 88c51002d06f9a68 ("perf addr2line: Add a libdw implementation") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/libdw.c | 49 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 196b9cdf51b2..84713b2a7ad5 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -61,7 +61,10 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) return NULL; } - dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); + if (dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL) != 0) { + dwfl_end(dwfl); + return NULL; + } dso__set_libdw(dso, dwfl); return dwfl; @@ -73,18 +76,19 @@ struct libdw_a2l_cb_args { struct inline_node *node; char *leaf_srcline; bool leaf_srcline_used; + int err; }; static int libdw_a2l_cb(Dwarf_Die *die, void *_args) { struct libdw_a2l_cb_args *args = _args; - struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, die_name(die)); const char *call_fname = die_get_call_file(die); int call_lineno = die_get_call_lineno(die); char *call_srcline = srcline__unknown; if (!inline_sym) - return -ENOMEM; + goto abort_enomem; /* Assign caller information to the parent. */ if (call_fname) @@ -110,12 +114,27 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) /* Add this symbol to the chain as the leaf. */ if (!args->leaf_srcline_used) { - inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); + if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0) + goto abort_delete_sym; args->leaf_srcline_used = true; } else { - inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); + char *srcline = strdup(args->leaf_srcline); + + if (!srcline) + goto abort_delete_sym; + if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) { + free(srcline); + goto abort_delete_sym; + } } return 0; + +abort_delete_sym: + if (inline_sym->inlined) + symbol__delete(inline_sym); +abort_enomem: + args->err = -ENOMEM; + return DWARF_CB_ABORT; } int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, @@ -169,11 +188,29 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, .leaf_srcline = srcline_from_fileline(src ?: "", lineno), }; + if (!args.leaf_srcline) { + if (file && *file) { + free(*file); + *file = NULL; + } + return 0; + } + /* Walk from the parent down to the leaf. */ - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); + if (cudie) + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); if (!args.leaf_srcline_used) free(args.leaf_srcline); + + if (args.err) { + if (file && *file) { + free(*file); + *file = NULL; + } + inline_node__clear_frames(node); + return 0; + } } return 1; } From 9a2ef19b5f5218d35c161f272a901f6c070faf79 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:25 -0700 Subject: [PATCH 0510/5214] perf probe-finder: Fix libdw API contract violations Check return values of `dwarf_formsdata`, `dwarf_entrypc`, `dwarf_highpc`, `dwarf_bytesize`, `dwarf_attr`, `dwarf_decl_line`, `dwarf_getfuncs`, and `dwarf_formref_die`. Validate `dwarf_diename` and `dwarf_diecu` results to prevent potential crashes. Fix C90 mixed declarations. Additionally: - Avoid vfprintf undefined behavior with NULL strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls, including when warning about tail calls. - Prevent NULL pointer dereference in `convert_variable_fields()` when processing array elements for variables in registers. - Fallback to offset 0 in `line_range_search_cb()` instead of skipping functions without `DW_AT_decl_line`. - Relax `dwarf_getfuncs` error checking in `find_probe_point_by_func()` and `find_line_range_by_func()` to prevent premature CU search aborts, ensuring robustness against corrupted CUs. Fixes: 66f69b2197167cb9 ("perf probe: Support DW_AT_const_value constant value") Fixes: 3d918a12a1b3088a ("perf probe: Find fentry mcount fuzzed parameter location") Fixes: bcfc082150c6b1e9 ("perf probe: Remove redundant dwarf functions") Fixes: 221d061182b8ff55 ("perf probe: Fix to search local variables in appropriate scope") Fixes: b55a87ade3839c33 ("perf probe: Remove die() from probe-finder code") Fixes: 4c859351226c920b ("perf probe: Support glob wildcards for function name") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 102 +++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 64328abeef8b..f3f9a1573502 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -79,7 +79,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, unsigned int regn; Dwarf_Word offs = 0; bool ref = false; - const char *regs; + const char *regs, *name; int ret, ret2 = 0; if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) @@ -93,7 +93,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (!tvar) return 0; - dwarf_formsdata(&attr, &snum); + if (dwarf_formsdata(&attr, &snum) != 0) + return -ENOENT; ret = asprintf(&tvar->value, "\\%ld", (long)snum); return ret < 0 ? -ENOMEM : 0; @@ -103,8 +104,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) return -EINVAL; /* Broken DIE ? */ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { - ret = dwarf_entrypc(sp_die, &tmp); - if (ret) + if (dwarf_entrypc(sp_die, &tmp) != 0) return -ENOENT; if (probe_conf.show_location_range && @@ -115,8 +115,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, return -ENOENT; } - ret = dwarf_highpc(sp_die, &tmp); - if (ret) + if (dwarf_highpc(sp_die, &tmp) != 0) return -ENOENT; /* * This is fuzzed by fentry mcount. We try to find the @@ -138,12 +137,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, static_var: if (!tvar) return ret2; + /* Static variables on memory (not stack), make @varname */ - ret = strlen(dwarf_diename(vr_die)); + name = dwarf_diename(vr_die); + if (!name) + return -ENOENT; + ret = strlen(name); tvar->value = zalloc(ret + 2); if (tvar->value == NULL) return -ENOMEM; - snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); + snprintf(tvar->value, ret + 2, "@%s", name); tvar->ref = alloc_trace_arg_ref((long)offs); if (tvar->ref == NULL) return -ENOMEM; @@ -234,13 +237,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, } if (die_get_real_type(vr_die, &type) == NULL) { - pr_warning("Failed to get a type information of %s.\n", - dwarf_diename(vr_die)); + const char *name = dwarf_diename(vr_die); + + pr_warning("Failed to get a type information of %s.\n", name ?: ""); return -ENOENT; } pr_debug("%s type is %s.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) { /* String type */ @@ -249,7 +253,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, ret != DW_TAG_array_type) { pr_warning("Failed to cast into string: " "%s(%s) is not a pointer nor array.\n", - dwarf_diename(vr_die), dwarf_diename(&type)); + die_name(vr_die), die_name(&type)); return -EINVAL; } if (die_get_real_type(&type, &type) == NULL) { @@ -272,7 +276,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, !die_compare_name(&type, "unsigned char")) { pr_warning("Failed to cast into string: " "%s is not (unsigned) char *.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); return -EINVAL; } tvar->type = strdup(cast); @@ -299,7 +303,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, /* Check the bitwidth */ if (ret > MAX_BASIC_TYPE_BITS) { pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + die_name(&type), MAX_BASIC_TYPE_BITS); ret = MAX_BASIC_TYPE_BITS; } ret = snprintf(buf, 16, "%c%d", prefix, ret); @@ -333,12 +337,14 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Var real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); tag = dwarf_tag(&type); if (field->name[0] == '[' && (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { + int bsize; + /* Save original type for next field or type */ memcpy(die_mem, &type, sizeof(*die_mem)); /* Get the type of this array */ @@ -346,7 +352,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } - pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type), + pr_debug2("Array real type: %s (%x)\n", die_name(&type), (unsigned)dwarf_dieoffset(&type)); if (tag == DW_TAG_pointer_type) { ref = zalloc(sizeof(struct probe_trace_arg_ref)); @@ -357,7 +363,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, else *ref_ptr = ref; } - ref->offset += dwarf_bytesize(&type) * field->index; + bsize = dwarf_bytesize(&type); + + if (bsize < 0) + return -EINVAL; + if (!ref) { + pr_warning("Array indexing not supported for variables in registers.\n"); + return -ENOTSUP; + } + ref->offset += bsize * field->index; ref->user_access = user_access; goto next; } else if (tag == DW_TAG_pointer_type) { @@ -414,7 +428,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(type:%s) has no member %s.\n", varname, - dwarf_diename(&type), field->name); + die_name(&type), field->name); return -EINVAL; } @@ -461,7 +475,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) int ret; pr_debug("Converting variable %s into trace event.\n", - dwarf_diename(vr_die)); + die_name(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf, pf->tvar); @@ -542,7 +556,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, /* Verify the address is correct */ if (!dwarf_haspc(sp_die, paddr)) { pr_warning("Specified offset is out of %s\n", - dwarf_diename(sp_die)); + die_name(sp_die)); return -EINVAL; } @@ -599,7 +613,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { pr_warning("Ignoring tail call from %s\n", - dwarf_diename(&pf->sp_die)); + die_name(&pf->sp_die)); return 0; } else { pr_warning("Failed to find probe point in any " @@ -611,10 +625,16 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); /* Get the frame base attribute/ops from subprogram */ - dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); - ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) { + if (dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr) == NULL) { pf->fb_ops = NULL; + } else { + ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + } + + if (pf->fb_ops == NULL) { + /* Not supported */ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && @@ -667,8 +687,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) } } else { /* With the line number, find the nearest declared DIE */ - dwarf_decl_line(fn_die, &lno); - if (lno < fsp->line && fsp->diff > fsp->line - lno) { + if (dwarf_decl_line(fn_die, &lno) == 0 && lno < fsp->line && + fsp->diff > fsp->line - lno) { /* Keep a candidate and continue */ fsp->diff = fsp->line - lno; memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); @@ -924,12 +944,12 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Get probe address */ if (die_entrypc(in_die, &addr) != 0) { pr_warning("Failed to get entry address of %s.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } if (addr == 0) { pr_debug("%s has no valid entry address. skipped.\n", - dwarf_diename(in_die)); + die_name(in_die)); return -ENOENT; } pf->addr = addr; @@ -971,12 +991,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->file && fname && strtailcmp(pp->file, fname)) return DWARF_CB_OK; - pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), + pr_debug("Matched function: %s [%lx]\n", die_name(sp_die), (unsigned long)dwarf_dieoffset(sp_die)); pf->fname = fname; pf->abstrace_dieoffset = dwarf_dieoffset(sp_die); if (pp->line) { /* Function relative line */ - dwarf_decl_line(sp_die, &pf->lno); + if (dwarf_decl_line(sp_die, &pf->lno) != 0) + return DWARF_CB_OK; pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (die_is_func_instance(sp_die)) { @@ -985,7 +1006,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) /* But in some case the entry address is 0 */ if (pf->addr == 0) { pr_debug("%s has no entry PC. Skipped\n", - dwarf_diename(sp_die)); + die_name(sp_die)); param->retval = 0; /* Real function */ } else if (pp->lazy_line) @@ -1018,7 +1039,8 @@ static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + if (dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return _param.retval; } @@ -1207,7 +1229,8 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) * points to correct die. */ if (dwarf_attr(die_mem, DW_AT_abstract_origin, &attr)) { - dwarf_formref_die(&attr, &var_die); + if (dwarf_formref_die(&attr, &var_die) == NULL) + goto out; if (pf->abstrace_dieoffset != dwarf_dieoffset(&var_die)) goto out; } @@ -1293,13 +1316,16 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) if (ret < 0) goto end; - tev->point.realname = strdup(dwarf_diename(sc_die)); + tev->point.realname = strdup(die_name(sc_die)); if (!tev->point.realname) { ret = -ENOMEM; goto end; } - tev->lang = dwarf_srclang(dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL)); + if (dwarf_diecu(sc_die, &pf->cu_die, NULL, NULL) != NULL) + tev->lang = dwarf_srclang(&pf->cu_die); + else + tev->lang = DW_LANG_C; /* Fallback */ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); @@ -1794,7 +1820,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { lf->fname = die_get_decl_file(sp_die); - dwarf_decl_line(sp_die, &lr->offset); + if (dwarf_decl_line(sp_die, &lr->offset) != 0) + lr->offset = 0; /* Fallback if no line info */ pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ @@ -1818,7 +1845,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + if (dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0) < 0) + pr_debug("Failed to get functions from CU\n"); return param.retval; } From 5aa1941900050a2c80d29bc7ee0dfbddbad8f294 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:26 -0700 Subject: [PATCH 0511/5214] perf annotate-data: Fix libdw API contract violations Check return values of `dwarf_aggregate_size` and `dwarf_formudata`. Additionally: - Avoid `vfprintf` undefined behavior with `NULL` strings by using the `die_name()` helper for `dwarf_diename()` in `pr_*` calls. - Use `die_get_data_member_location()` (updated to use `dwarf_attr_integrate`) to correctly parse location expressions for inherited member locations in the fallback path when `dwarf_formudata()` fails. Fixes: 2bc3cf575a162a2c ("perf annotate-data: Improve debug message with location info") Fixes: 4a111cadac85362e ("perf annotate-data: Add member field in the data type") Fixes: 8b1042c425f6a5a9 ("perf annotate-data: Set bitfield member offset and size properly") Fixes: fc044c53b99fad03 ("perf annotate-data: Add dso->data_types tree") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1eff0a27237d..63e3c54fab42 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -74,7 +74,8 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) break; } - dwarf_aggregate_size(die, &size); + if (dwarf_aggregate_size(die, &size) != 0) + size = 0; strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); @@ -146,9 +147,9 @@ static void pr_debug_scope(Dwarf_Die *scope_die) tag = dwarf_tag(scope_die); if (tag == DW_TAG_subprogram) - pr_info("[function] %s\n", dwarf_diename(scope_die)); + pr_info("[function] %s\n", die_name(scope_die)); else if (tag == DW_TAG_inlined_subroutine) - pr_info("[inlined] %s\n", dwarf_diename(scope_die)); + pr_info("[inlined] %s\n", die_name(scope_die)); else if (tag == DW_TAG_lexical_block) pr_info("[block]\n"); else @@ -250,9 +251,12 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - dwarf_formudata(&attr, &loc); - else { + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) { + if (dwarf_formudata(&attr, &loc) != 0) { + if (die_get_data_member_location(die, &loc) != 0) + loc = 0; + } + } else { /* bitfield member */ if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && dwarf_formudata(&attr, &loc) == 0) @@ -273,7 +277,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) dwarf_diename(die), (long)bit_size) < 0) member->var_name = NULL; } else { - member->var_name = strdup(dwarf_diename(die)); + const char *name = dwarf_diename(die); + + member->var_name = name ? strdup(name) : NULL; } if (member->var_name == NULL) { @@ -370,7 +376,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, type_die); - dwarf_aggregate_size(type_die, &size); + if (dwarf_aggregate_size(type_die, &size) != 0) + size = 0; /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; @@ -1569,7 +1576,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) offset = loc->offset; pr_debug_dtp("CU for %s (die:%#lx)\n", - dwarf_diename(&cu_die), (long)dwarf_dieoffset(&cu_die)); + die_name(&cu_die), (long)dwarf_dieoffset(&cu_die)); if (reg == DWARF_REG_PC) { if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr, @@ -1636,7 +1643,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) } pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", - dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), + die_name(&var_die), (long)dwarf_dieoffset(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); if (reg == DWARF_REG_PC) { From 31088ccf0312b1a547046f1f69890ede07834a30 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:27 -0700 Subject: [PATCH 0512/5214] perf debuginfo: Fix libdw API contract violations Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Additionally: - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when `dwfl_report_offline()` or subsequent setup calls fail. Fixes: 6f1b6291cf73cb32 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debuginfo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 0e35c13abd04..84a78b30ceac 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -167,7 +170,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) From ae15db3e9b639491007cc1e9e99638e4b6091781 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 14 Apr 2026 14:42:41 +0200 Subject: [PATCH 0513/5214] perf callchain: Handle multiple address spaces perf test 'perf inject to convert DWARF callchains to regular ones' fails on s390. It was introduced with commit 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") The failure comes the difference in output. Without the inject script to convert DWARF the callchains is: # perf record -F 999 --call-graph dwarf -- perf test -w noploop # perf report -i perf.data --stdio --no-children -q \ --percent-limit=1 > /tmp/111 # cat /tmp/111 99.30% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main __libc_start_call_main __libc_start_main_impl (inlined) _start # With the inject script step the output is: # perf inject -i perf.data --convert-callchain -o /tmp/perf-inject-1.out # perf report -i /tmp/perf-inject-1.out --stdio --no-children -q \ --percent-limit=1 > /tmp/222 # cat /tmp/222 99.40% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main _start # diff /tmp/111 /tmp/222 1c1 < 99.30% perf-noploop perf [.] noploop --- > 99.40% perf-noploop perf [.] noploop 10,11d9 < __libc_start_call_main < __libc_start_main_impl (inlined) # The difference are the symbols __libc_start_call_main and __libc_start_main_impl. On x86_64, kernel and user space share a single virtual address space, with the kernel mapped to the upper end of memory. The instruction pointer value alone is sufficient to distinguish between user space and kernel space addresses. This is not true for s390, which uses separate address spaces for user and kernel. The same virtual address can be valid in both address spaces, so the instruction pointer value alone cannot determine whether an address belongs to the kernel or user space. Instead, perf must rely on the cpumode metadata derived from the processor status word (PSW) at sample time. In function perf_event__convert_sample_callchain() the first part copies a kernel callchain and context entries, if any. It then appends additional entries ignoring the address space architecture. Taking that into account, the symbols at addresses 0x3ff970348cb __libc_start_call_main 0x3ff970349c5 __libc_start_main_impl (located after the kernel address space on s390) are now included. Output before: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : FAILED! Output after: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : Ok Question to Namhyung: In function perf_event__convert_sample_callchain() just before the for() loop this patch modifies, the kernel callchain is copied, see this comment and the next 5 lines: /* copy kernel callchain and context entries */ Then why is machine__kernel_ip() needed in the for() loop, when the kernel entries have been copied just before the loop? Note: This patch was tested on x86_64 virtual machine and succeeded. Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") Signed-off-by: Thomas Richter Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Jan Polensky Cc: linux-s390@vger.kernel.org Cc: Sumanth Korikkar Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/common.c | 4 +++- tools/perf/builtin-inject.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 21836f70f231..ad0cab830a4d 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -237,5 +237,7 @@ int perf_env__lookup_objdump(struct perf_env *env, char **path) */ bool perf_env__single_address_space(struct perf_env *env) { - return strcmp(perf_env__arch(env), "sparc"); + const char *arch = perf_env__arch(env); + + return strcmp(arch, "s390") && strcmp(arch, "sparc"); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index f174bc69cec4..6ab20df358c4 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -438,7 +438,8 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, node = cursor->first; for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { - if (machine__kernel_ip(machine, node->ip)) + if (machine->single_address_space && + machine__kernel_ip(machine, node->ip)) /* kernel IPs were added already */; else if (node->ms.sym && node->ms.sym->inlined) /* we can't handle inlined callchains */; From 8e32bd775cc882ea591c75cac9e7d9c4ad68f7c6 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:47:59 +0100 Subject: [PATCH 0514/5214] perf arm_spe: Make a function to get the MIDR We'll need the MIDR to dump IMPDEF events in the next commits so extract a function for it. No functional changes intended. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/arm-spe.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index e5835042acdf..39046033e143 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -972,14 +972,9 @@ static void arm_spe__synth_memory_level(struct arm_spe_queue *speq, } } -static void arm_spe__synth_ds(struct arm_spe_queue *speq, - const struct arm_spe_record *record, - union perf_mem_data_src *data_src) +static int arm_spe__get_midr(struct arm_spe *spe, int cpu, u64 *midr) { - struct arm_spe *spe = speq->spe; - u64 *metadata = NULL; - u64 midr; - unsigned int i; + u64 *metadata; /* Metadata version 1 assumes all CPUs are the same (old behavior) */ if (spe->metadata_ver == 1) { @@ -987,15 +982,28 @@ static void arm_spe__synth_ds(struct arm_spe_queue *speq, pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n"); cpuid = perf_env__cpuid(perf_session__env(spe->session)); - midr = strtol(cpuid, NULL, 16); - } else { - metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu); - if (!metadata) - return; - - midr = metadata[ARM_SPE_CPU_MIDR]; + *midr = strtol(cpuid, NULL, 16); + return 0; } + metadata = arm_spe__get_metadata_by_cpu(spe, cpu); + if (!metadata) + return -EINVAL; + + *midr = metadata[ARM_SPE_CPU_MIDR]; + return 0; +} + +static void arm_spe__synth_ds(struct arm_spe_queue *speq, + const struct arm_spe_record *record, + union perf_mem_data_src *data_src) +{ + u64 midr; + unsigned int i; + + if (arm_spe__get_midr(speq->spe, speq->cpu, &midr)) + return; + for (i = 0; i < ARRAY_SIZE(data_source_handles); i++) { if (is_midr_in_range_list(midr, data_source_handles[i].midr_ranges)) { return data_source_handles[i].ds_synth(record, data_src); From 0c7e4d11fb8d490c8f64ea98d05e441c9e26c6ee Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:48:00 +0100 Subject: [PATCH 0515/5214] perf arm_spe: Handle missing CPU IDs Don't call strtol() with a null pointer to avoid undefined behavior. I'm not sure of the exact scenario for missing CPU IDs but I don't think it happens in practice. SPE decoding can continue without them with reduced functionality, but print an error message anyway. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/arm-spe.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 39046033e143..037c7aa150c1 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -982,16 +982,23 @@ static int arm_spe__get_midr(struct arm_spe *spe, int cpu, u64 *midr) pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n"); cpuid = perf_env__cpuid(perf_session__env(spe->session)); + if (!cpuid) + goto err; + *midr = strtol(cpuid, NULL, 16); return 0; } metadata = arm_spe__get_metadata_by_cpu(spe, cpu); if (!metadata) - return -EINVAL; + goto err; *midr = metadata[ARM_SPE_CPU_MIDR]; return 0; + +err: + pr_warning_once("Failed to get MIDR for CPU %d\n", cpu); + return -EINVAL; } static void arm_spe__synth_ds(struct arm_spe_queue *speq, From 8cf4d17ec7f0bebff8f3de3131e0c7e422a36da6 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:48:01 +0100 Subject: [PATCH 0516/5214] perf arm_spe: Store MIDR in arm_spe_pkt The MIDR will affect printing of arm_spe_pkts, so store a copy of it there. Technically it's constant for each decoder, but there is no decoder when doing a raw dump, so it has to be stored in every packet. It will only be used in raw dump mode and not in normal decoding for now, but to avoid any surprises, set MIDR properly on the decoder too. Having both the MIDR and the arm_spe_pkt (which has a copy of it) in the decoder seemed a bit weird, so remove arm_spe_pkt from the decoder. The packet is only short lived anyway so probably shouldn't have been there in the first place. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo --- .../util/arm-spe-decoder/arm-spe-decoder.c | 17 ++++++++------- .../util/arm-spe-decoder/arm-spe-decoder.h | 3 +-- .../arm-spe-decoder/arm-spe-pkt-decoder.c | 3 ++- .../arm-spe-decoder/arm-spe-pkt-decoder.h | 3 ++- tools/perf/util/arm-spe.c | 21 +++++++++++++------ 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c index 9e02b2bdd117..7a3a4815fd37 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c +++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c @@ -120,7 +120,8 @@ static int arm_spe_get_data(struct arm_spe_decoder *decoder) return decoder->len; } -static int arm_spe_get_next_packet(struct arm_spe_decoder *decoder) +static int arm_spe_get_next_packet(struct arm_spe_decoder *decoder, + struct arm_spe_pkt *packet) { int ret; @@ -134,7 +135,8 @@ static int arm_spe_get_next_packet(struct arm_spe_decoder *decoder) } ret = arm_spe_get_packet(decoder->buf, decoder->len, - &decoder->packet); + packet, decoder->midr); + if (ret <= 0) { /* Move forward for 1 byte */ decoder->buf += 1; @@ -144,7 +146,7 @@ static int arm_spe_get_next_packet(struct arm_spe_decoder *decoder) decoder->buf += ret; decoder->len -= ret; - } while (decoder->packet.type == ARM_SPE_PAD); + } while (packet->type == ARM_SPE_PAD); return 1; } @@ -154,19 +156,20 @@ static int arm_spe_read_record(struct arm_spe_decoder *decoder) int err; int idx; u64 payload, ip; + struct arm_spe_pkt packet; memset(&decoder->record, 0x0, sizeof(decoder->record)); decoder->record.context_id = (u64)-1; while (1) { - err = arm_spe_get_next_packet(decoder); + err = arm_spe_get_next_packet(decoder, &packet); if (err <= 0) return err; - idx = decoder->packet.index; - payload = decoder->packet.payload; + idx = packet.index; + payload = packet.payload; - switch (decoder->packet.type) { + switch (packet.type) { case ARM_SPE_TIMESTAMP: decoder->record.timestamp = payload; return 1; diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h index 3310e05122f0..0cbcb501edc9 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h +++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h @@ -147,8 +147,7 @@ struct arm_spe_decoder { const unsigned char *buf; size_t len; - - struct arm_spe_pkt packet; + u64 midr; }; struct arm_spe_decoder *arm_spe_decoder_new(struct arm_spe_params *params); diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c index 5769ba2f4140..718022aecec3 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c @@ -222,11 +222,12 @@ static int arm_spe_do_get_packet(const unsigned char *buf, size_t len, } int arm_spe_get_packet(const unsigned char *buf, size_t len, - struct arm_spe_pkt *packet) + struct arm_spe_pkt *packet, u64 midr) { int ret; ret = arm_spe_do_get_packet(buf, len, packet); + packet->midr = midr; /* put multiple consecutive PADs on the same line, up to * the fixed-width output format of 16 bytes per line. */ diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h index adf4cde320aa..a457821f3bcc 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h @@ -35,6 +35,7 @@ struct arm_spe_pkt { enum arm_spe_pkt_type type; unsigned char index; uint64_t payload; + uint64_t midr; }; /* Short header (HEADER0) and extended header (HEADER1) */ @@ -184,7 +185,7 @@ enum arm_spe_events { const char *arm_spe_pkt_name(enum arm_spe_pkt_type); int arm_spe_get_packet(const unsigned char *buf, size_t len, - struct arm_spe_pkt *packet); + struct arm_spe_pkt *packet, u64 midr); int arm_spe_pkt_desc(const struct arm_spe_pkt *packet, char *buf, size_t len); #endif diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 037c7aa150c1..2b31da231ef3 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -134,8 +134,10 @@ struct data_source_handle { .ds_synth = arm_spe__synth_##func, \ } +static int arm_spe__get_midr(struct arm_spe *spe, int cpu, u64 *midr); + static void arm_spe_dump(struct arm_spe *spe __maybe_unused, - unsigned char *buf, size_t len) + unsigned char *buf, size_t len, u64 midr) { struct arm_spe_pkt packet; size_t pos = 0; @@ -148,7 +150,8 @@ static void arm_spe_dump(struct arm_spe *spe __maybe_unused, len); while (len) { - ret = arm_spe_get_packet(buf, len, &packet); + ret = arm_spe_get_packet(buf, len, &packet, midr); + if (ret > 0) pkt_len = ret; else @@ -174,10 +177,10 @@ static void arm_spe_dump(struct arm_spe *spe __maybe_unused, } static void arm_spe_dump_event(struct arm_spe *spe, unsigned char *buf, - size_t len) + size_t len, u64 midr) { printf(".\n"); - arm_spe_dump(spe, buf, len); + arm_spe_dump(spe, buf, len, midr); } static int arm_spe_get_trace(struct arm_spe_buffer *b, void *data) @@ -302,8 +305,10 @@ static void arm_spe_set_pid_tid_cpu(struct arm_spe *spe, if (speq->thread) { speq->pid = thread__pid(speq->thread); - if (queue->cpu == -1) + if (queue->cpu == -1) { speq->cpu = thread__cpu(speq->thread); + arm_spe__get_midr(spe, speq->cpu, &speq->decoder->midr); + } } } @@ -1248,6 +1253,7 @@ static int arm_spe__setup_queue(struct arm_spe *spe, if (queue->cpu != -1) speq->cpu = queue->cpu; + arm_spe__get_midr(spe, queue->cpu, &speq->decoder->midr); if (!speq->on_heap) { int ret; @@ -1490,8 +1496,11 @@ static int arm_spe_process_auxtrace_event(struct perf_session *session, /* Dump here now we have copied a piped trace out of the pipe */ if (dump_trace) { if (auxtrace_buffer__get_data(buffer, fd)) { + u64 midr = 0; + + arm_spe__get_midr(spe, buffer->cpu.cpu, &midr); arm_spe_dump_event(spe, buffer->data, - buffer->size); + buffer->size, midr); auxtrace_buffer__put_data(buffer); } } From 96b4910c3c6c6e9cad9a90f2e01486faef108ca1 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:48:02 +0100 Subject: [PATCH 0517/5214] perf arm_spe: Turn event name mappings into an array This is so we can have a single function that prints events and can be used with multiple mappings from different CPUs. Remove any bit that was printed so that later we can print out the remaining unknown impdef bits. No functional changes intended. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo --- .../arm-spe-decoder/arm-spe-pkt-decoder.c | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c index 718022aecec3..67ca356100e5 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c @@ -277,6 +277,48 @@ static int arm_spe_pkt_out_string(int *err, char **buf_p, size_t *blen, return ret; } +struct ev_string { + u8 event; + const char *desc; +}; + +static const struct ev_string common_ev_strings[] = { + { .event = EV_EXCEPTION_GEN, .desc = "EXCEPTION-GEN" }, + { .event = EV_RETIRED, .desc = "RETIRED" }, + { .event = EV_L1D_ACCESS, .desc = "L1D-ACCESS" }, + { .event = EV_L1D_REFILL, .desc = "L1D-REFILL" }, + { .event = EV_TLB_ACCESS, .desc = "TLB-ACCESS" }, + { .event = EV_TLB_WALK, .desc = "TLB-REFILL" }, + { .event = EV_NOT_TAKEN, .desc = "NOT-TAKEN" }, + { .event = EV_MISPRED, .desc = "MISPRED" }, + { .event = EV_LLC_ACCESS, .desc = "LLC-ACCESS" }, + { .event = EV_LLC_MISS, .desc = "LLC-REFILL" }, + { .event = EV_REMOTE_ACCESS, .desc = "REMOTE-ACCESS" }, + { .event = EV_ALIGNMENT, .desc = "ALIGNMENT" }, + { .event = EV_TRANSACTIONAL, .desc = "TXN" }, + { .event = EV_PARTIAL_PREDICATE, .desc = "SVE-PARTIAL-PRED" }, + { .event = EV_EMPTY_PREDICATE, .desc = "SVE-EMPTY-PRED" }, + { .event = EV_L2D_ACCESS, .desc = "L2D-ACCESS" }, + { .event = EV_L2D_MISS, .desc = "L2D-MISS" }, + { .event = EV_CACHE_DATA_MODIFIED, .desc = "HITM" }, + { .event = EV_RECENTLY_FETCHED, .desc = "LFB" }, + { .event = EV_DATA_SNOOPED, .desc = "SNOOPED" }, + { .event = EV_STREAMING_SVE_MODE, .desc = "STREAMING-SVE" }, + { .event = EV_SMCU, .desc = "SMCU" }, + { .event = 0, .desc = NULL }, +}; + +static u64 print_event_list(int *err, char **buf, size_t *buf_len, + const struct ev_string *ev_strings, u64 payload) +{ + for (const struct ev_string *ev = ev_strings; ev->desc != NULL; ev++) { + if (payload & BIT_ULL(ev->event)) + arm_spe_pkt_out_string(err, buf, buf_len, " %s", ev->desc); + payload &= ~BIT_ULL(ev->event); + } + return payload; +} + static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet, char *buf, size_t buf_len) { @@ -284,51 +326,7 @@ static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet, int err = 0; arm_spe_pkt_out_string(&err, &buf, &buf_len, "EV"); - - if (payload & BIT(EV_EXCEPTION_GEN)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " EXCEPTION-GEN"); - if (payload & BIT(EV_RETIRED)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " RETIRED"); - if (payload & BIT(EV_L1D_ACCESS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-ACCESS"); - if (payload & BIT(EV_L1D_REFILL)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-REFILL"); - if (payload & BIT(EV_TLB_ACCESS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-ACCESS"); - if (payload & BIT(EV_TLB_WALK)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-REFILL"); - if (payload & BIT(EV_NOT_TAKEN)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " NOT-TAKEN"); - if (payload & BIT(EV_MISPRED)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " MISPRED"); - if (payload & BIT(EV_LLC_ACCESS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-ACCESS"); - if (payload & BIT(EV_LLC_MISS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-REFILL"); - if (payload & BIT(EV_REMOTE_ACCESS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " REMOTE-ACCESS"); - if (payload & BIT(EV_ALIGNMENT)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " ALIGNMENT"); - if (payload & BIT(EV_TRANSACTIONAL)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TXN"); - if (payload & BIT(EV_PARTIAL_PREDICATE)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-PARTIAL-PRED"); - if (payload & BIT(EV_EMPTY_PREDICATE)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-EMPTY-PRED"); - if (payload & BIT(EV_L2D_ACCESS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L2D-ACCESS"); - if (payload & BIT(EV_L2D_MISS)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L2D-MISS"); - if (payload & BIT(EV_CACHE_DATA_MODIFIED)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " HITM"); - if (payload & BIT(EV_RECENTLY_FETCHED)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LFB"); - if (payload & BIT(EV_DATA_SNOOPED)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SNOOPED"); - if (payload & BIT(EV_STREAMING_SVE_MODE)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " STREAMING-SVE"); - if (payload & BIT(EV_SMCU)) - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SMCU"); + print_event_list(&err, &buf, &buf_len, common_ev_strings, payload); return err; } From b566a7404161b680edd259e67375e1d73887f4b3 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:48:03 +0100 Subject: [PATCH 0518/5214] perf arm_spe: Decode Arm N1 IMPDEF events >From the TRM [1], N1 has one IMPDEF event which isn't covered by the common list. Add a framework so that more cores can be added in the future and that the N1 IMPDEF event can be decoded. Also increase the size of the buffer because we're adding more strings and if it gets truncated it falls back to a hex dump only. [1]: https://developer.arm.com/documentation/100616/0401/Statistical-Profiling-Extension/implementation-defined-features-of-SPE Suggested-by: Al Grant Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Link: https://developer.arm.com/documentation/100616/0401/Statistical-Profiling-Extension/implementation-defined-features-of-SPE Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/arm-spe-decoder/Build | 2 + .../arm-spe-decoder/arm-spe-pkt-decoder.c | 39 ++++++++++++++++++- .../arm-spe-decoder/arm-spe-pkt-decoder.h | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/arm-spe-decoder/Build b/tools/perf/util/arm-spe-decoder/Build index ab500e0efe24..97a298d1e279 100644 --- a/tools/perf/util/arm-spe-decoder/Build +++ b/tools/perf/util/arm-spe-decoder/Build @@ -1 +1,3 @@ perf-util-y += arm-spe-pkt-decoder.o arm-spe-decoder.o + +CFLAGS_arm-spe-pkt-decoder.o += -I$(srctree)/tools/arch/arm64/include/ -I$(OUTPUT)arch/arm64/include/generated/ diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c index 67ca356100e5..b74f887a48f2 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c @@ -15,6 +15,8 @@ #include "arm-spe-pkt-decoder.h" +#include "../../arm64/include/asm/cputype.h" + static const char * const arm_spe_packet_name[] = { [ARM_SPE_PAD] = "PAD", [ARM_SPE_END] = "END", @@ -308,6 +310,11 @@ static const struct ev_string common_ev_strings[] = { { .event = 0, .desc = NULL }, }; +static const struct ev_string n1_event_strings[] = { + { .event = 12, .desc = "LATE-PREFETCH" }, + { .event = 0, .desc = NULL }, +}; + static u64 print_event_list(int *err, char **buf, size_t *buf_len, const struct ev_string *ev_strings, u64 payload) { @@ -319,6 +326,26 @@ static u64 print_event_list(int *err, char **buf, size_t *buf_len, return payload; } +struct event_print_handle { + const struct midr_range *midr_ranges; + const struct ev_string *ev_strings; +}; + +#define EV_PRINT(range, strings) \ + { \ + .midr_ranges = range, \ + .ev_strings = strings, \ + } + +static const struct midr_range n1_event_encoding_cpus[] = { + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), + {}, +}; + +static const struct event_print_handle event_print_handles[] = { + EV_PRINT(n1_event_encoding_cpus, n1_event_strings), +}; + static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet, char *buf, size_t buf_len) { @@ -326,7 +353,17 @@ static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet, int err = 0; arm_spe_pkt_out_string(&err, &buf, &buf_len, "EV"); - print_event_list(&err, &buf, &buf_len, common_ev_strings, payload); + payload = print_event_list(&err, &buf, &buf_len, common_ev_strings, + payload); + + /* Try to decode IMPDEF bits for known CPUs */ + for (unsigned int i = 0; i < ARRAY_SIZE(event_print_handles); i++) { + if (is_midr_in_range_list(packet->midr, + event_print_handles[i].midr_ranges)) + payload = print_event_list(&err, &buf, &buf_len, + event_print_handles[i].ev_strings, + payload); + } return err; } diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h index a457821f3bcc..a3300bec4990 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h @@ -11,7 +11,7 @@ #include #include -#define ARM_SPE_PKT_DESC_MAX 256 +#define ARM_SPE_PKT_DESC_MAX 512 #define ARM_SPE_NEED_MORE_BYTES -1 #define ARM_SPE_BAD_PACKET -2 From e7ef461784945e95bb5f137383298cd2d4122a9e Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Apr 2026 13:48:04 +0100 Subject: [PATCH 0519/5214] perf arm_spe: Print remaining IMPDEF event numbers Any IMPDEF events not printed out from a known core's IMPDEF list or for a completely unknown core will still not be shown to the user. Fix this by printing the remaining bits as comma separated raw numbers, e.g. "IMPDEF:1,2,3,4". Suggested-by: Al Grant Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Arnaldo Carvalho de Melo --- .../util/arm-spe-decoder/arm-spe-pkt-decoder.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c index b74f887a48f2..600677318f84 100644 --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -365,6 +366,23 @@ static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet, payload); } + /* + * Print remaining IMPDEF bits that weren't printed above as raw + * "IMPDEF:1,2,3,4" etc. + */ + if (payload) { + arm_spe_pkt_out_string(&err, &buf, &buf_len, " IMPDEF:"); + for (int i = 0; i < 64; i++) { + const char *sep = payload & (payload - 1) ? "," : ""; + + if (payload & BIT_ULL(i)) { + arm_spe_pkt_out_string(&err, &buf, &buf_len, "%d%s", i, + sep); + payload &= ~BIT_ULL(i); + } + } + } + return err; } From e41cac8617a9b399bf013cda968c53927a9a0f91 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 3 May 2026 23:27:56 -0700 Subject: [PATCH 0520/5214] perf build: Update error message for BUILD_NONDISTRO=1 It should say binutils-dev(el) instead of plain binutils package as it's mostly installed already and can confuse people like me. :) Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 333ddd0e4bd8..06d7a3f9990c 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -923,7 +923,7 @@ ifdef BUILD_NONDISTRO $(call feature_check,libbfd-liberty-z) ifneq ($(feature-libbfd-threadsafe), 1) - $(error binutils 2.42 or later is required for non-distro builds) + $(error binutils-dev(el) 2.42 or later is required for non-distro builds) endif # we may be on a system that requires -liberty and (maybe) -lz From 53bc032892782e54f33dfe18b86c461492a55fa4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 3 May 2026 23:27:57 -0700 Subject: [PATCH 0521/5214] perf build: Add -fms-extensions for GEN_VMLINUX_H=1 On my system, `make GEN_VMLINUX_H=1` fails with a lot of error messages like below: ./util/bpf_skel/vmlinux.h:134488:4: error: declaration does not declare anything [-Werror,-Wmissing-declarations] 134488 | struct freelist_counters; | ^~~~~~~~~~~~~~~~~~~~~~~~ make[2]: *** [Makefile.perf:1249: linux/tools/perf/util/bpf_skel/.tmp/lock_contention.bpf.o] Error 1 I saw commit 835a50753579a ("selftests/bpf: Add -fms-extensions to bpf build flags") also added the same flags to bpf programs. Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index cee19c923c06..0aba14f22a06 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1249,6 +1249,7 @@ $(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) $(SKEL_OUT)/vmlinux.h $(QUIET_CLANG)$(CLANG) -g -O2 -fno-stack-protector --target=bpf \ $(CLANG_OPTIONS) $(EXTRA_BPF_FLAGS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \ -include $(OUTPUT)PERF-VERSION-FILE -include util/bpf_skel/perf_version.h \ + -fms-extensions -Wno-microsoft-anon-tag \ -c $(filter util/bpf_skel/%.bpf.c,$^) -o $@ $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o | $(BPFTOOL) From 8c8f2093614373ea8179b562320212a25cf937c0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 3 May 2026 23:27:58 -0700 Subject: [PATCH 0522/5214] perf build: Remove NO_GTK2 build test 4751bddd3f983af2 ("perf tools: Make GTK2 support opt-in") changed GTK2 build to be opt-in. So NO_GTK2 is meaningless and we need to pass GTK2=1 to enable it. Let's update the build-test configuration for that. Also make_no_ui is the same as make_no_slang since NO_GTK2 is no-op. Let's get rid of it as well. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/make | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 6587dc326d1b..dbd7c86a2dcc 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -78,8 +78,6 @@ make_libperl := LIBPERL=1 make_no_libpython := NO_LIBPYTHON=1 make_no_scripts := NO_LIBPYTHON=1 make_no_slang := NO_SLANG=1 -make_no_gtk2 := NO_GTK2=1 -make_no_ui := NO_SLANG=1 NO_GTK2=1 make_no_demangle := NO_DEMANGLE=1 make_no_libelf := NO_LIBELF=1 make_no_libdw := NO_LIBDW=1 @@ -118,7 +116,7 @@ make_install_prefix_slash := install prefix=/tmp/krava/ make_static := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX32=1 NO_JVMTI=1 NO_LIBTRACEEVENT=1 NO_LIBELF=1 # all the NO_* variable combined -make_minimal := NO_LIBPYTHON=1 NO_GTK2=1 +make_minimal := NO_LIBPYTHON=1 make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1 make_minimal += NO_LIBNUMA=1 NO_LIBBIONIC=1 NO_LIBDW=1 make_minimal += NO_LIBBPF=1 @@ -153,8 +151,6 @@ run += make_libperl run += make_no_libpython run += make_no_scripts run += make_no_slang -run += make_no_gtk2 -run += make_no_ui run += make_no_demangle run += make_no_libelf run += make_no_libdw From a562d6dc86bdfdd299e1b4734977a8d63e803583 Mon Sep 17 00:00:00 2001 From: Zile Xiong Date: Fri, 20 Mar 2026 14:54:45 +0800 Subject: [PATCH 0523/5214] media: vb2: use ssize_t for vb2_read/vb2_write vb2_read() and vb2_write() return size_t, but propagate negative errno values from __vb2_perform_fileio(). This relies on implicit signed/unsigned conversions in callers (e.g. vb2_fop_read()) to recover error codes: __vb2_perform_fileio() -> -EINVAL vb2_read() -> (size_t)-EINVAL vb2_fop_read() -> -EINVAL This relies on implicit conversions that are not obvious. These helpers are exported (EXPORT_SYMBOL_GPL) and part of the vb2 API, so changing their return type may affect existing users. However, they conceptually follow read/write semantics, where ssize_t is typically used to return either a byte count or a negative error code. Switch vb2_read() and vb2_write() to ssize_t, and update __vb2_perform_fileio() accordingly. Signed-off-by: Zile Xiong Acked-by: Marek Szyprowski Fixes: b25748fe6126 ("[media] v4l: videobuf2: add read() and write() emulator") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index adf668b213c2..b0a6084f1757 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2990,8 +2990,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) * @read: access mode selector (1 means read, 0 means write) */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) +static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) { struct vb2_fileio_data *fileio; struct vb2_fileio_buf *buf; @@ -3154,15 +3154,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return ret; } -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); } EXPORT_SYMBOL_GPL(vb2_read); -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, (char __user *) data, count, ppos, nonblocking, 0); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4424d481d7f7..4b4f4c15c53a 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -1093,8 +1093,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue. @@ -1103,8 +1103,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * typedef vb2_thread_fnc - callback function for use with vb2_thread. From a0701e387b46e2481c05b47f1235b954bfc2af3e Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 20 Mar 2026 15:04:53 +0800 Subject: [PATCH 0524/5214] media: cx23885: add ioremap return check and cleanup Add a check for the return value of pci_ioremap_bar() in cx23885_dev_setup(). If ioremap for BAR0 fails, release the already allocated PCI memory region, decrement the device count, and return -ENODEV. This prevents a potential null pointer dereference and ensures proper cleanup on memory mapping failure. Fixes: d19770e5178a ("V4L/DVB (6150): Add CX23885/CX23887 PCIe bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Signed-off-by: Hans Verkuil --- drivers/media/pci/cx23885/cx23885-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 4a8af8b88d84..9b92e8db494c 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1002,8 +1002,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } /* PCIe stuff */ - dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); + dev->lmmio = pci_ioremap_bar(dev->pci, 0); + if (!dev->lmmio) { + dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n", + dev->name); + goto err_release_region; + } dev->bmmio = (u8 __iomem *)dev->lmmio; @@ -1109,6 +1113,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } return 0; + +err_release_region: + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + cx23885_devcount--; + return -ENODEV; } static void cx23885_dev_unregister(struct cx23885_dev *dev) From 7d6358ab02866e5b7ed8d3a00805297617bbb0ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:37:27 +0200 Subject: [PATCH 0525/5214] media: cx231xx: fix devres lifetime USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes). Fix the driver state lifetime so that it is released on driver unbind. Fixes: 184a82784d50 ("[media] cx231xx: use devm_ functions to allocate memory") Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/cx231xx/cx231xx-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index b75535d6abaf..69b24205bc56 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1573,7 +1573,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->video_mode.num_alt, GFP_KERNEL); if (dev->video_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1614,7 +1615,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->vbi_mode.num_alt); /* compute alternate max packet sizes for vbi */ - dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->vbi_mode.num_alt, GFP_KERNEL); if (dev->vbi_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1656,7 +1658,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n", dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->sliced_cc_mode.num_alt, + GFP_KERNEL); if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1720,7 +1724,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, udev = interface_to_usbdev(interface); /* allocate memory for our device state and initialize it */ - dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { retval = -ENOMEM; goto err_if; @@ -1850,7 +1854,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL); + dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->ts1_mode.num_alt, + GFP_KERNEL); if (dev->ts1_mode.alt_max_pkt_size == NULL) { retval = -ENOMEM; goto err_video_alt; From f86ed548386e3050e5f8f25b450d09dc009d9a88 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Thu, 2 Apr 2026 15:35:29 +0800 Subject: [PATCH 0526/5214] media: saa7134: Fix a possible memory leak in saa7134_video_init1 In saa7134_video_init1(), the return value of the first saa7134_pgtable_alloc() is not checked. If it fails, the function continues as if successful, leaving the driver with an invalid page table. Additionally, if vb2_queue_init() for the VBI queue fails after the video queue page table has been allocated, the allocated memory is not freed before returning. The second saa7134_pgtable_alloc() also lacks a return value check. Errors occur during device probing before the device is fully registered, the normal cleanup path in saa7134_finidev() is not executed, leading to memory leaks and potential use of uninitialized DMA resources. Check the return value of both saa7134_pgtable_alloc() calls and propagate errors. On failure of any later step, free allocated page tables to avoid memory leaks. Ensure control handlers are also released on error to prevent further resource leakage. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: a00e68888d5d ("[media] saa7134: move saa7134_pgtable to saa7134_dmaqueue") Signed-off-by: Hans Verkuil --- drivers/media/pci/saa7134/saa7134-video.c | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 4a51b873e47a..2b1672737d84 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + if (ret) + goto err_free_ctrl; q = &dev->vbi_vbq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->lock = &dev->lock; q->dev = &dev->pci->dev; ret = vb2_queue_init(q); - if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } + + ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } return 0; + +err_free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + if (card_has_radio(dev)) + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } void saa7134_video_fini(struct saa7134_dev *dev) From c8e13b0dcb7084152cf8274f24e4c5a4ffec6439 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sun, 12 Apr 2026 17:24:16 +0000 Subject: [PATCH 0527/5214] media: tegra-video: tegra210: remove redundant NULL check in dequeue_buf_done list_first_entry() does not returns NULL when the list is known to be non-empty. The NULL check before list_del_init() is therefore redundant. Remove the unnecessary check. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra210.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index da99f19a39e7..a6606768fa03 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -362,8 +362,7 @@ dequeue_buf_done(struct tegra_vi_channel *chan) buf = list_first_entry(&chan->done, struct tegra_channel_buffer, queue); - if (buf) - list_del_init(&buf->queue); + list_del_init(&buf->queue); spin_unlock(&chan->done_lock); return buf; From cc20e81da6d99926f94fad7af21f75c07e865769 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 14 Apr 2026 16:32:39 +0800 Subject: [PATCH 0528/5214] media: em28xx-video: fix missing res_free() on init_usb_xfer failure res_get() is called before em28xx_init_usb_xfer(), but the error path of em28xx_init_usb_xfer() does not release the resource, leading to a persistent busy state. Signed-off-by: Haoxiang Li Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4a0ce9c5ee4b..da0422c65e5f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1248,8 +1248,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) dev->max_pkt_size, dev->packet_multiplier, em28xx_urb_data_copy); - if (rc < 0) + if (rc < 0) { + res_free(dev, vq->type); return rc; + } /* * djh: it's not clear whether this code is still needed. I'm From 680daf40a82d483949f87f0d8f98639dc47e610c Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Wed, 22 Apr 2026 20:17:34 +0530 Subject: [PATCH 0529/5214] media: rtl2832: fix use-after-free in rtl2832_remove() cancel_delayed_work_sync() is called before i2c_mux_del_adapters() in rtl2832_remove(). While the cancel waits for any running instance of i2c_gate_work to finish, it does not prevent the timer from being rescheduled by a concurrent thread. During probe, the r820t_attach() call attempts I2C transfers through the mux adapter. These transfers go through i2c_mux_master_xfer(), which calls rtl2832_deselect() after the transfer completes, rescheduling i2c_gate_work via schedule_delayed_work(). If this transfer is still in flight when rtl2832_remove() runs, rtl2832_deselect() can reschedule i2c_gate_work after it has been cancelled, causing a use-after-free when kfree(dev) is called. Fix this by calling i2c_mux_del_adapters() before cancel_delayed_work_sync(). Once the mux adapter is unregistered, no new I2C transfers can go through it, so rtl2832_deselect() can no longer reschedule i2c_gate_work. The subsequent cancel_delayed_work_sync() is then guaranteed to be final. Fixes: cddcc40b1b15 ("[media] rtl2832: convert to use an explicit i2c mux core") Cc: stable@vger.kernel.org Reported-by: syzbot+019ced393ab913002b75@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=019ced393ab913002b75 Signed-off-by: Deepanshu Kartikey Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index d8e1546aea5e..9898f729304a 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1115,10 +1115,10 @@ static void rtl2832_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_mux_del_adapters(dev->muxc); + cancel_delayed_work_sync(&dev->i2c_gate_work); + regmap_exit(dev->regmap); kfree(dev); From e4107c6b301d1afa4e3ae471f964da83d15ead4a Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:16 -0400 Subject: [PATCH 0530/5214] media: tegra-vde: Add HAS_IOMEM dependency to match SRAM select kconfiglint reports: K002: config VIDEO_TEGRA_VDE selects visible symbol SRAM which has dependencies VIDEO_TEGRA_VDE selects SRAM, which is defined in drivers/misc/Kconfig as: config SRAM bool "Generic on-chip SRAM driver" depends on HAS_IOMEM The NVIDIA Tegra video decoder driver was originally introduced in commit cd6c56feb591 ("media: staging: media: Introduce NVIDIA Tegra video decoder driver") as a staging driver with `depends on ARCH_TEGRA || COMPILE_TEST` and `select SRAM`. Since all Tegra SoCs have HAS_IOMEM, the SRAM dependency was implicitly satisfied for real hardware configurations. The driver was later de-staged in commit 8bd4aaf438e3 ("media: staging: tegra-vde: De-stage driver") and relocated to drivers/media/platform/nvidia/tegra-vde/ in commit 9b18ef7c9ff4 ("media: platform: rename tegra/vde/ to nvidia/tegra-vde/"). Throughout these moves, the `select SRAM` remained without a corresponding HAS_IOMEM dependency. Under COMPILE_TEST on a hypothetical architecture without HAS_IOMEM (such as UML in some configurations), the select would force SRAM on without its HAS_IOMEM dependency being met. Add an explicit `depends on HAS_IOMEM` to make the dependency chain complete and prevent this misconfiguration under COMPILE_TEST. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Signed-off-by: Hans Verkuil --- drivers/media/platform/nvidia/tegra-vde/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig index 2fe13f39c95b..f05fcc94deca 100644 --- a/drivers/media/platform/nvidia/tegra-vde/Kconfig +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -2,6 +2,7 @@ config VIDEO_TEGRA_VDE tristate "NVIDIA Tegra Video Decoder Engine driver" depends on V4L_MEM2MEM_DRIVERS depends on ARCH_TEGRA || COMPILE_TEST + depends on HAS_IOMEM depends on VIDEO_DEV select DMA_SHARED_BUFFER select IOMMU_IOVA From caced3578bf9f104a4aaad8f46c4c719e705d9a6 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Fri, 1 May 2026 23:28:31 +0300 Subject: [PATCH 0531/5214] media: v4l2-ctrls-request: add NULL check in v4l2_ctrl_request_complete() If CONFIG_MEDIA_CONTROLLER is undefined, media_request_object_find() will always return NULL, so its 2nd call in v4l2_ctrl_request_complete() would fail as well as the 1st one and thus cause hdl to have a wrong value (at the top of memory) and list_for_each_entry() to iterate over the garbage data located there. Add NULL check for the 2nd call and place the error cleanup at the end of v4l2_ctrl_request_complete()... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: c3bf5129f339 ("media: v4l2-ctrls: always copy the controls on completion") Cc: stable@vger.kernel.org Signed-off-by: Sergey Shtylyov Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ctrls-request.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index 4b7e6981b8d6..5c2fc767c6a6 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-request.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c @@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req, ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8); if (!ret) ret = v4l2_ctrl_request_bind(req, hdl, main_hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(hdl); - return; - } + if (ret) + goto error; hdl->request_is_queued = true; obj = media_request_object_find(req, &req_ops, main_hdl); + if (!obj) + goto error; } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); @@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, mutex_unlock(main_hdl->lock); media_request_object_complete(obj); media_request_object_put(obj); + return; + +error: + v4l2_ctrl_handler_free(hdl); + kfree(hdl); } EXPORT_SYMBOL(v4l2_ctrl_request_complete); From 0b42657bea6ba635226e8ef551076d024ceacdc9 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 31 Mar 2026 10:43:40 +0200 Subject: [PATCH 0532/5214] media: v4l2-common: Always register clock with device-specific name If we need to register a dummy fixed-frequency clock, always register it using a device-specific name. This supports the use case where a system has two of the same sensor, meaning two instances of the same driver, which previously both tried (and failed) to create a clock with the same name. Signed-off-by: Paul Cercueil Reviewed-by: Mehdi Djait Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 55bcd5975d9f..bceafc4e92c8 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -793,14 +793,15 @@ struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id, if (ret) return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret); - if (!id) { + if (id) + clk_id = kasprintf(GFP_KERNEL, "clk-%s-%s", dev_name(dev), id); + else clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev)); - if (!clk_id) - return ERR_PTR(-ENOMEM); - id = clk_id; - } - clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate); + if (!clk_id) + return ERR_PTR(-ENOMEM); + + clk_hw = devm_clk_hw_register_fixed_rate(dev, clk_id, NULL, 0, rate); if (IS_ERR(clk_hw)) return ERR_CAST(clk_hw); From 551bb2fd5e4ed63d33aa11f07102cce5179b7595 Mon Sep 17 00:00:00 2001 From: Yingchao Deng Date: Sun, 26 Apr 2026 17:59:34 +0800 Subject: [PATCH 0533/5214] coresight: cti: Fix DT filter signals silently ignored In cti_plat_process_filter_sigs(), after allocating a temporary cti_trig_grp struct via kzalloc_obj(), the code never assigns tg->nr_sigs = nr_filter_sigs. Since kzalloc zero-initialises the struct, tg->nr_sigs remains 0. cti_plat_read_trig_group() guards with: if (!tgrp->nr_sigs) return 0; so it returns immediately without reading any signal indices from DT. Fix by assigning tg->nr_sigs before calling cti_plat_read_trig_group(). Fixes: a5614770ab97 ("coresight: cti: Add device tree support for custom CTI") Signed-off-by: Yingchao Deng Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260426-nr_sigs-v1-1-3b9df99dab97@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-cti-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 4eff96f48594..d6d5388705c3 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -329,6 +329,7 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata, if (!tg) return -ENOMEM; + tg->nr_sigs = nr_filter_sigs; err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS); if (!err) drvdata->config.trig_out_filter |= tg->used_mask; From d9f8489c4325607d8a7c4a2860ce8c72a9874ff1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Mar 2026 12:42:49 +0200 Subject: [PATCH 0534/5214] media: Documentation: Use right function to test device power state Tell driver authors to use pm_runtime_get_if_active() instead of pm_runtime_get_if_in_use() to check the device's power state in the s_ctrl callback. pm_runtime_get_if_active() is the right function to use here since it returns non-zero if the device is powered on rather than its PM runtime usage_count is non-zero. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart --- Documentation/driver-api/media/camera-sensor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 94bd1dae82d5..c8552f70f496 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -114,7 +114,7 @@ of the device. This is because the power state of the device is only changed after the power state transition has taken place. The ``s_ctrl`` callback can be used to obtain device's power state after the power state transition: -.. c:function:: int pm_runtime_get_if_in_use(struct device *dev); +.. c:function:: int pm_runtime_get_if_active(struct device *dev); The function returns a non-zero value if it succeeded getting the power count or runtime PM was disabled, in either of which cases the driver may proceed to From d8e484a452ca195b7c099373f3c7901bd405b623 Mon Sep 17 00:00:00 2001 From: Maciej Wieczor-Retman Date: Wed, 8 Apr 2026 16:27:47 +0000 Subject: [PATCH 0535/5214] platform/x86/intel-uncore-freq: Rename instance_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "instance" word has a specific meaning in TPMI. It is a physical index related to compute dies and IO dies present on a single TPMI partition (which is also a single TPMI device). It's used for mapping MMIO blocks for direct TPMI register access. The currently used "instance_id" uncore_data struct field is a sequentially generated value that's used for appending to uncore directories inside the /sys/devices/system/cpu/intel_uncore_frequency directory. It has no relation to the physical TPMI elements. Signed-off-by: Maciej Wieczor-Retman Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/4d983157199cf0e163597df254e2dc629878b818.1775665057.git.m.wieczorretman@pm.me Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-common.c | 6 +++--- .../x86/intel/uncore-frequency/uncore-frequency-common.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 7070c94324e0..25ab511ed8d2 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -268,7 +268,7 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) if (ret < 0) goto uncore_unlock; - data->instance_id = ret; + data->seqnum_id = ret; scnprintf(data->name, sizeof(data->name), "uncore%02d", ret); } else { scnprintf(data->name, sizeof(data->name), "package_%02d_die_%02d", @@ -281,7 +281,7 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) ret = create_attr_group(data, data->name); if (ret) { if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) - ida_free(&intel_uncore_ida, data->instance_id); + ida_free(&intel_uncore_ida, data->seqnum_id); } else { data->control_cpu = cpu; data->valid = true; @@ -301,7 +301,7 @@ void uncore_freq_remove_die_entry(struct uncore_data *data) data->control_cpu = -1; data->valid = false; if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) - ida_free(&intel_uncore_ida, data->instance_id); + ida_free(&intel_uncore_ida, data->seqnum_id); mutex_unlock(&uncore_lock); } diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 0abe850ef54e..0d5fd91ee0aa 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -35,7 +35,7 @@ * @die_id: Die id for this instance * @domain_id: Power domain id for this instance * @cluster_id: cluster id in a domain - * @instance_id: Unique instance id to append to directory name + * @seqnum_id: Unique sequential id to append to directory name * @name: Sysfs entry name for this instance * @agent_type_mask: Bit mask of all hardware agents for this domain * @uncore_attr_group: Attribute group storage @@ -71,7 +71,7 @@ struct uncore_data { int die_id; int domain_id; int cluster_id; - int instance_id; + int seqnum_id; char name[32]; u16 agent_type_mask; From 6cf1c1e9f21ba2e44e05e691d5241290c7d6c41a Mon Sep 17 00:00:00 2001 From: Maciej Wieczor-Retman Date: Wed, 8 Apr 2026 16:27:51 +0000 Subject: [PATCH 0536/5214] platform/x86/intel-uncore-freq: Expose instance ID in the sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Insufficient data is exported to allow direct access to TPMI registers through MMIO. On non-partitioned systems domain_id can be used both for mapping CPUs to their compute die IDs and for mapping die indices to their MMIO memory blocks presented to userspace via TPMI debugfs. However on partitioned systems the debugfs association doesn't work anymore. This is due to how TPMI partitioning influences domain_id calculation. The previous association is lost on partitioned systems in order to keep using domain_id for mapping CPUs to compute dies. Expose the instance ID in sysfs that's unique in the scope of one TPMI partition (and hence one TPMI device). It's a physical index into mapped MMIO blocks and can be used by userspace to figure out how to directly access TPMI registers. Signed-off-by: Maciej Wieczor-Retman Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/b9ae8d5f1ab86bcdb1a8636fa48865a9e49e2e21.1775665057.git.m.wieczorretman@pm.me Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../pm/intel_uncore_frequency_scaling.rst | 7 +++++++ .../uncore-frequency/uncore-frequency-common.c | 10 ++++++++++ .../uncore-frequency/uncore-frequency-common.h | 6 +++++- .../uncore-frequency/uncore-frequency-tpmi.c | 15 ++++++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst index d367ba4d744a..b43ad4d5e333 100644 --- a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst +++ b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst @@ -88,8 +88,15 @@ and "fabric_cluster_id" in the directory. Attributes in each directory: +``instance_id`` + This attribute is used to get die indices in userspace mapped MMIO + blocks. Indices are local to a single TPMI partition. Needed for direct + TPMI register access. + ``domain_id`` This attribute is used to get the power domain id of this instance. + Indices are unique in all TPMI partitions on a given CPU package. Can be + used to map compute dies to corresponding CPUs. ``die_id`` This attribute is used to get the Linux die id of this instance. diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 25ab511ed8d2..3b554418a7a3 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -29,6 +29,13 @@ static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, return sysfs_emit(buf, "%u\n", data->domain_id); } +static ssize_t show_instance_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct uncore_data *data = container_of(attr, struct uncore_data, instance_id_kobj_attr); + + return sysfs_emit(buf, "%u\n", data->instance_id); +} + static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr); @@ -200,6 +207,9 @@ static int create_attr_group(struct uncore_data *data, char *name) if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) { init_attribute_root_ro(domain_id); data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr; + init_attribute_root_ro(instance_id); + data->uncore_attrs[index++] = &data->instance_id_kobj_attr.attr; + init_attribute_root_ro(fabric_cluster_id); data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr; init_attribute_root_ro(package_id); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 0d5fd91ee0aa..e319448dc1a4 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -36,6 +36,7 @@ * @domain_id: Power domain id for this instance * @cluster_id: cluster id in a domain * @seqnum_id: Unique sequential id to append to directory name + * @instance_id: Die indices or feature instances for a single TPMI device * @name: Sysfs entry name for this instance * @agent_type_mask: Bit mask of all hardware agents for this domain * @uncore_attr_group: Attribute group storage @@ -56,6 +57,7 @@ * @elc_floor_freq_khz_kobj_attr: Storage for kobject attribute elc_floor_freq_khz * @agent_types_kobj_attr: Storage for kobject attribute agent_type * @die_id_kobj_attr: Attribute storage for die_id information + * @instance_id_kobj_attr: Attribute storage for instance_id value * @uncore_attrs: Attribute storage for group creation * * This structure is used to encapsulate all data related to uncore sysfs @@ -72,6 +74,7 @@ struct uncore_data { int domain_id; int cluster_id; int seqnum_id; + int instance_id; char name[32]; u16 agent_type_mask; @@ -90,7 +93,8 @@ struct uncore_data { struct kobj_attribute elc_floor_freq_khz_kobj_attr; struct kobj_attribute agent_types_kobj_attr; struct kobj_attribute die_id_kobj_attr; - struct attribute *uncore_attrs[15]; + struct kobj_attribute instance_id_kobj_attr; + struct attribute *uncore_attrs[16]; }; #define UNCORE_DOMAIN_ID_INVALID -1 diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 88015a2c6a0d..44e2a90004df 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -385,7 +385,19 @@ static u8 io_die_index_next; /* Lock to protect io_die_start, io_die_index_next */ static DEFINE_MUTEX(domain_lock); -static void set_domain_id(int id, int num_resources, +static inline void set_instance_id(int id, struct tpmi_uncore_cluster_info *cluster_info) +{ + /* + * On non-partitioned systems domain_id can be used for mapping both + * CPUs to compute die IDs and physical die indexes to MMIO mapped + * memory. However on partitioned systems domain_id loses the second + * association. Therefore instance_id should be used for that instead, + * while domain_id should still be used to match CPUs to compute dies. + */ + cluster_info->uncore_data.instance_id = id; +} + +static void set_domain_id(int id, int num_resources, struct oobmsm_plat_info *plat_info, struct tpmi_uncore_cluster_info *cluster_info) { @@ -690,6 +702,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ set_cdie_id(i, cluster_info, plat_info); set_domain_id(i, num_resources, plat_info, cluster_info); + set_instance_id(i, cluster_info); cluster_info->uncore_root = tpmi_uncore; From 9a54c285630c0072daef5d526c3f0b697e901065 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:43 +0800 Subject: [PATCH 0537/5214] media: platform: amd: Introduce amd isp4 capture driver AMD isp4 capture is a v4l2 media device which implements media controller interface. It has one sub-device (AMD ISP4 sub-device) endpoint which can be connected to a remote CSI2 TX endpoint. It supports only one physical interface for now. Also add ISP4 driver related entry info into the MAINTAINERS file Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 13 +++ drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/amd/Kconfig | 3 + drivers/media/platform/amd/Makefile | 3 + drivers/media/platform/amd/isp4/Kconfig | 15 +++ drivers/media/platform/amd/isp4/Makefile | 6 ++ drivers/media/platform/amd/isp4/isp4.c | 127 +++++++++++++++++++++++ drivers/media/platform/amd/isp4/isp4.h | 17 +++ 9 files changed, 186 insertions(+) create mode 100644 drivers/media/platform/amd/Kconfig create mode 100644 drivers/media/platform/amd/Makefile create mode 100644 drivers/media/platform/amd/isp4/Kconfig create mode 100644 drivers/media/platform/amd/isp4/Makefile create mode 100644 drivers/media/platform/amd/isp4/isp4.c create mode 100644 drivers/media/platform/amd/isp4/isp4.h diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..3b495a4dc786 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1152,6 +1152,19 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git F: drivers/iommu/amd/ F: include/linux/amd-iommu.h +AMD ISP4 DRIVER +M: Bin Du +M: Nirujogi Pratap +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/platform/amd/Kconfig +F: drivers/media/platform/amd/Makefile +F: drivers/media/platform/amd/isp4/Kconfig +F: drivers/media/platform/amd/isp4/Makefile +F: drivers/media/platform/amd/isp4/isp4.c +F: drivers/media/platform/amd/isp4/isp4.h + AMD KFD M: Felix Kuehling L: amd-gfx@lists.freedesktop.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3f0b7bb68cc9..0b33e927bd59 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -63,6 +63,7 @@ config VIDEO_MUX # Platform drivers - Please keep it alphabetically sorted source "drivers/media/platform/allegro-dvt/Kconfig" +source "drivers/media/platform/amd/Kconfig" source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/arm/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6d5f79ddfcc3..16c185752474 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -6,6 +6,7 @@ # Place here, alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) obj-y += allegro-dvt/ +obj-y += amd/ obj-y += amlogic/ obj-y += amphion/ obj-y += arm/ diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig new file mode 100644 index 000000000000..25af49f246b2 --- /dev/null +++ b/drivers/media/platform/amd/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +source "drivers/media/platform/amd/isp4/Kconfig" diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile new file mode 100644 index 000000000000..8bfc1955f22e --- /dev/null +++ b/drivers/media/platform/amd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += isp4/ diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig new file mode 100644 index 000000000000..55dd2dc453a2 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config VIDEO_AMD_ISP4_CAPTURE + tristate "AMD ISP4 and camera driver" + depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_V4L2 + select VIDEOBUF2_VMALLOC + select VIDEO_V4L2_SUBDEV_API + help + This is support for AMD ISP4 and camera subsystem driver. + Say Y here to enable the ISP4 and camera device for video capture. + To compile this driver as a module, choose M here. The module will + be called amd_isp4_capture. diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile new file mode 100644 index 000000000000..500b81ce5d14 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 Advanced Micro Devices, Inc. + +obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o +amd_isp4_capture-objs := isp4.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c new file mode 100644 index 000000000000..58b21258b6d3 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4.h" + +#define ISP4_DRV_NAME "amd_isp_capture" + +static const struct { + const char *name; + u32 status_mask; + u32 en_mask; + u32 ack_mask; + u32 rb_int_num; +} isp4_irq[] = { + /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ + { + .name = "isp_irq_global", + .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ + }, + { + .name = "isp_irq_stream1", + .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ + }, +}; + +static irqreturn_t isp4_irq_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +static int isp4_capture_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_device *isp_dev; + int ret; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + dev->init_name = ISP4_DRV_NAME; + + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); + if (irq[i] < 0) + return dev_err_probe(dev, irq[i], + "fail to get irq %d\n", + isp4_irq[i].rb_int_num); + + ret = devm_request_irq(dev, irq[i], isp4_irq_handler, + IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + if (ret) + return dev_err_probe(dev, ret, "fail to req irq %d\n", + irq[i]); + } + + isp_dev->v4l2_dev.mdev = &isp_dev->mdev; + + strscpy(isp_dev->mdev.model, "amd_isp41_mdev", + sizeof(isp_dev->mdev.model)); + isp_dev->mdev.dev = dev; + media_device_init(&isp_dev->mdev); + + snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name), + "AMD-V4L2-ROOT"); + ret = v4l2_device_register(dev, &isp_dev->v4l2_dev); + if (ret) { + dev_err_probe(dev, ret, "fail register v4l2 device\n"); + goto err_clean_media; + } + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + ret = media_device_register(&isp_dev->mdev); + if (ret) { + dev_err_probe(dev, ret, "fail to register media device\n"); + goto err_isp4_deinit; + } + + platform_set_drvdata(pdev, isp_dev); + + return 0; + +err_isp4_deinit: + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); +err_clean_media: + media_device_cleanup(&isp_dev->mdev); + + return ret; +} + +static void isp4_capture_remove(struct platform_device *pdev) +{ + struct isp4_device *isp_dev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + media_device_unregister(&isp_dev->mdev); + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); + media_device_cleanup(&isp_dev->mdev); +} + +static struct platform_driver isp4_capture_drv = { + .probe = isp4_capture_probe, + .remove = isp4_capture_remove, + .driver = { + .name = ISP4_DRV_NAME, + } +}; + +module_platform_driver(isp4_capture_drv); + +MODULE_ALIAS("platform:" ISP4_DRV_NAME); +MODULE_IMPORT_NS("DMA_BUF"); + +MODULE_DESCRIPTION("AMD ISP4 Driver"); +MODULE_AUTHOR("Bin Du "); +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h new file mode 100644 index 000000000000..7f2db0dfa2d9 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_H_ +#define _ISP4_H_ + +#include +#include + +struct isp4_device { + struct v4l2_device v4l2_dev; + struct media_device mdev; +}; + +#endif /* _ISP4_H_ */ From f2f2c3547d6283e79de424c3c6e759899608fa1e Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:44 +0800 Subject: [PATCH 0538/5214] media: platform: amd: low level support for isp4 firmware Low level functions for accessing the registers and mapping to their ranges. This change also includes register definitions for ring buffer used to communicate with ISP Firmware. Ring buffer is the communication interface between driver and ISP Firmware. Command and responses are exchanged through the ring buffer. Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 1 + drivers/media/platform/amd/isp4/isp4_hw_reg.h | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 drivers/media/platform/amd/isp4/isp4_hw_reg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3b495a4dc786..e2561d95d251 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,6 +1164,7 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_hw_reg.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h new file mode 100644 index 000000000000..09c76f75c5ee --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_HW_REG_H_ +#define _ISP4_HW_REG_H_ + +#include + +#define ISP_SOFT_RESET 0x62000 +#define ISP_SYS_INT0_EN 0x62010 +#define ISP_SYS_INT0_STATUS 0x62014 +#define ISP_SYS_INT0_ACK 0x62018 +#define ISP_CCPU_CNTL 0x62054 +#define ISP_STATUS 0x62058 +#define ISP_LOG_RB_BASE_LO0 0x62148 +#define ISP_LOG_RB_BASE_HI0 0x6214c +#define ISP_LOG_RB_SIZE0 0x62150 +#define ISP_LOG_RB_RPTR0 0x62154 +#define ISP_LOG_RB_WPTR0 0x62158 +#define ISP_RB_BASE_LO1 0x62170 +#define ISP_RB_BASE_HI1 0x62174 +#define ISP_RB_SIZE1 0x62178 +#define ISP_RB_RPTR1 0x6217c +#define ISP_RB_WPTR1 0x62180 +#define ISP_RB_BASE_LO2 0x62184 +#define ISP_RB_BASE_HI2 0x62188 +#define ISP_RB_SIZE2 0x6218c +#define ISP_RB_RPTR2 0x62190 +#define ISP_RB_WPTR2 0x62194 +#define ISP_RB_BASE_LO3 0x62198 +#define ISP_RB_BASE_HI3 0x6219c +#define ISP_RB_SIZE3 0x621a0 +#define ISP_RB_RPTR3 0x621a4 +#define ISP_RB_WPTR3 0x621a8 +#define ISP_RB_BASE_LO4 0x621ac +#define ISP_RB_BASE_HI4 0x621b0 +#define ISP_RB_SIZE4 0x621b4 +#define ISP_RB_RPTR4 0x621b8 +#define ISP_RB_WPTR4 0x621bc +#define ISP_RB_BASE_LO5 0x621c0 +#define ISP_RB_BASE_HI5 0x621c4 +#define ISP_RB_SIZE5 0x621c8 +#define ISP_RB_RPTR5 0x621cc +#define ISP_RB_WPTR5 0x621d0 +#define ISP_RB_BASE_LO6 0x621d4 +#define ISP_RB_BASE_HI6 0x621d8 +#define ISP_RB_SIZE6 0x621dc +#define ISP_RB_RPTR6 0x621e0 +#define ISP_RB_WPTR6 0x621e4 +#define ISP_RB_BASE_LO7 0x621e8 +#define ISP_RB_BASE_HI7 0x621ec +#define ISP_RB_SIZE7 0x621f0 +#define ISP_RB_RPTR7 0x621f4 +#define ISP_RB_WPTR7 0x621f8 +#define ISP_RB_BASE_LO8 0x621fc +#define ISP_RB_BASE_HI8 0x62200 +#define ISP_RB_SIZE8 0x62204 +#define ISP_RB_RPTR8 0x62208 +#define ISP_RB_WPTR8 0x6220c +#define ISP_RB_BASE_LO9 0x62210 +#define ISP_RB_BASE_HI9 0x62214 +#define ISP_RB_SIZE9 0x62218 +#define ISP_RB_RPTR9 0x6221c +#define ISP_RB_WPTR9 0x62220 +#define ISP_RB_BASE_LO10 0x62224 +#define ISP_RB_BASE_HI10 0x62228 +#define ISP_RB_SIZE10 0x6222c +#define ISP_RB_RPTR10 0x62230 +#define ISP_RB_WPTR10 0x62234 +#define ISP_RB_BASE_LO11 0x62238 +#define ISP_RB_BASE_HI11 0x6223c +#define ISP_RB_SIZE11 0x62240 +#define ISP_RB_RPTR11 0x62244 +#define ISP_RB_WPTR11 0x62248 +#define ISP_RB_BASE_LO12 0x6224c +#define ISP_RB_BASE_HI12 0x62250 +#define ISP_RB_SIZE12 0x62254 +#define ISP_RB_RPTR12 0x62258 +#define ISP_RB_WPTR12 0x6225c + +#define ISP_POWER_STATUS 0x60000 + +/* ISP_SOFT_RESET */ +#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK 0x00000001UL + +/* ISP_CCPU_CNTL */ +#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK 0x00040000UL + +/* ISP_STATUS */ +#define ISP_STATUS__CCPU_REPORT_MASK 0x000000feUL + +/* ISP_SYS_INT0_STATUS */ +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK 0x00010000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK 0x00040000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK 0x00100000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK 0x00400000UL + +/* ISP_SYS_INT0_EN */ +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK 0x00010000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK 0x00040000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK 0x00100000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK 0x00400000UL + +/* ISP_SYS_INT0_ACK */ +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK 0x00010000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK 0x00040000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK 0x00100000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK 0x00400000UL + +/* Helper functions for reading isp registers */ +static inline u32 isp4hw_rreg(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +/* Helper functions for writing isp registers */ +static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val) +{ + return writel(val, base + reg); +} + +#endif /* _ISP4_HW_REG_H_ */ From 4c5feef6a62c22b578344891232872056415a3dd Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:45 +0800 Subject: [PATCH 0539/5214] media: platform: amd: Add isp4 fw and hw interface ISP firmware controls ISP HW pipeline using dedicated embedded processor called ccpu. The communication between ISP FW and driver is using commands and response messages sent through the ring buffer. Command buffers support either global setting that is not specific to the stream and support stream specific parameters. Response buffers contain ISP FW notification information such as frame buffer done and command done. IRQ is used for receiving response buffer from ISP firmware, which is handled in the main isp4 media device. ISP ccpu is booted up through the firmware loading helper function prior to stream start. Memory used for command buffer and response buffer needs to be allocated from amdgpu buffer manager because isp4 is a child device of amdgpu. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 3 + drivers/media/platform/amd/isp4/Makefile | 3 +- .../platform/amd/isp4/isp4_fw_cmd_resp.h | 318 +++++++ .../media/platform/amd/isp4/isp4_interface.c | 833 ++++++++++++++++++ .../media/platform/amd/isp4/isp4_interface.h | 144 +++ 5 files changed, 1300 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.c create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.h diff --git a/MAINTAINERS b/MAINTAINERS index e2561d95d251..1d19168cecaa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,7 +1164,10 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h +F: drivers/media/platform/amd/isp4/isp4_interface.c +F: drivers/media/platform/amd/isp4/isp4_interface.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 500b81ce5d14..c7eadd33fc97 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -3,4 +3,5 @@ # Copyright (C) 2025 Advanced Micro Devices, Inc. obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o -amd_isp4_capture-objs := isp4.o +amd_isp4_capture-objs := isp4.o \ + isp4_interface.o diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h new file mode 100644 index 000000000000..88bacb00355c --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_FW_CMD_RESP_H_ +#define _ISP4_FW_CMD_RESP_H_ + +/* + * Two types of command/response channel. + * Type Global Command has one command/response channel. + * Type Stream Command has one command/response channel. + *----------- ------------ + *| | --------------------------- | | + *| | ---->| Global Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | --------------------------- | | + *| | ---->| Stream Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | | | + *| HOST | | Firmware | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Global Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Stream Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *----------- ------------ + */ + +/* + * cmd_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_CMD_TYPE_SHIFT 24 +#define ISP4FW_CMD_GROUP_SHIFT 16 +#define ISP4FW_CMD_TYPE_STREAM_CTRL (0x2U << ISP4FW_CMD_TYPE_SHIFT) + +#define ISP4FW_CMD_GROUP_STREAM_CTRL (0x1U << ISP4FW_CMD_GROUP_SHIFT) +#define ISP4FW_CMD_GROUP_STREAM_BUFFER (0x4U << ISP4FW_CMD_GROUP_SHIFT) + +/* Stream Command */ +#define ISP4FW_CMD_ID_SET_STREAM_CONFIG (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1) +#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3) +#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5) +#define ISP4FW_CMD_ID_START_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7) +#define ISP4FW_CMD_ID_STOP_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8) + +/* Stream Buffer Command */ +#define ISP4FW_CMD_ID_SEND_BUFFER (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1) + +/* + * resp_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_RESP_GROUP_SHIFT 16 + +#define ISP4FW_RESP_GROUP_GENERAL (0x1 << ISP4FW_RESP_GROUP_SHIFT) +#define ISP4FW_RESP_GROUP_NOTIFICATION (0x3 << ISP4FW_RESP_GROUP_SHIFT) + +/* General Response */ +#define ISP4FW_RESP_ID_CMD_DONE (ISP4FW_RESP_GROUP_GENERAL | 0x1) + +/* Notification */ +#define ISP4FW_RESP_ID_NOTI_FRAME_DONE (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1) + +#define ISP4FW_CMD_STATUS_SUCCESS 0 +#define ISP4FW_CMD_STATUS_FAIL 1 +#define ISP4FW_CMD_STATUS_SKIPPED 2 + +#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA 4 + +#define ISP4FW_MEMORY_POOL_SIZE (100 * 1024 * 1024) + +/* + * standard ISP pipeline: mipicsi=>isp + */ +#define ISP4FW_MIPI0_ISP_PIPELINE_ID 0x5f91 + +enum isp4fw_sensor_id { + /* Sensor id for ISP input from MIPI port 0 */ + ISP4FW_SENSOR_ID_ON_MIPI0 = 0, +}; + +enum isp4fw_stream_id { + ISP4FW_STREAM_ID_INVALID = -1, + ISP4FW_STREAM_ID_1 = 0, + ISP4FW_STREAM_ID_2 = 1, + ISP4FW_STREAM_ID_3 = 2, + ISP4FW_STREAM_ID_MAXIMUM +}; + +enum isp4fw_image_format { + /* 4:2:0,semi-planar, 8-bit */ + ISP4FW_IMAGE_FORMAT_NV12 = 1, + /* interleave, 4:2:2, 8-bit */ + ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7, +}; + +enum isp4fw_pipe_out_ch { + ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0, +}; + +enum isp4fw_yuv_range { + ISP4FW_ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */ + ISP4FW_ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */ + ISP4FW_ISP_YUV_RANGE_MAX +}; + +enum isp4fw_buffer_type { + ISP4FW_BUFFER_TYPE_PREVIEW = 8, + ISP4FW_BUFFER_TYPE_META_INFO = 10, + ISP4FW_BUFFER_TYPE_MEM_POOL = 15, +}; + +enum isp4fw_buffer_status { + /* The buffer is INVALID */ + ISP4FW_BUFFER_STATUS_INVALID, + /* The buffer is not filled with image data */ + ISP4FW_BUFFER_STATUS_SKIPPED, + /* The buffer is available and awaiting to be filled */ + ISP4FW_BUFFER_STATUS_EXIST, + /* The buffer is filled with image data */ + ISP4FW_BUFFER_STATUS_DONE, + /* The buffer is unavailable */ + ISP4FW_BUFFER_STATUS_LACK, + /* The buffer is dirty, probably caused by LMI leakage */ + ISP4FW_BUFFER_STATUS_DIRTY, + ISP4FW_BUFFER_STATUS_MAX +}; + +enum isp4fw_buffer_source { + /* The buffer is from the stream buffer queue */ + ISP4FW_BUFFER_SOURCE_STREAM, +}; + +struct isp4fw_error_code { + u32 code1; + u32 code2; + u32 code3; + u32 code4; + u32 code5; +}; + +/* Command Structure for FW */ + +struct isp4fw_cmd { + u32 cmd_seq_num; + u32 cmd_id; + u32 cmd_param[12]; + u16 cmd_stream_id; + u8 cmd_silent_resp; + u8 reserved; + u32 cmd_check_sum; +}; + +struct isp4fw_resp_cmd_done { + /* + * The host2fw command seqNum. + * To indicate which command this response refers to. + */ + u32 cmd_seq_num; + /* The host2fw command id for host double check. */ + u32 cmd_id; + /* + * Indicate the command process status. + * 0 means success. 1 means fail. 2 means skipped + */ + u16 cmd_status; + /* + * If cmd_status is 1, the command failed. The host can check + * isp4fw_error_code for details. + */ + u16 isp4fw_error_code; + /* The response payload type varies by cmd. */ + u8 payload[36]; +}; + +struct isp4fw_resp_param_package { + u32 package_addr_lo; /* The low 32 bit of the pkg address. */ + u32 package_addr_hi; /* The high 32 bit of the pkg address. */ + u32 package_size; /* The total pkg size in bytes. */ + u32 package_check_sum; /* The byte sum of the pkg. */ +}; + +struct isp4fw_resp { + u32 resp_seq_num; + u32 resp_id; + union { + struct isp4fw_resp_cmd_done cmd_done; + struct isp4fw_resp_param_package frame_done; + u32 resp_param[12]; + } param; + u8 reserved[4]; + u32 resp_check_sum; +}; + +struct isp4fw_mipi_pipe_path_cfg { + u32 b_enable; + enum isp4fw_sensor_id isp4fw_sensor_id; +}; + +struct isp4fw_isp_pipe_path_cfg { + u32 isp_pipe_id; /* pipe ids for pipeline construction */ +}; + +struct isp4fw_isp_stream_cfg { + /* Isp mipi path */ + struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg; + /* Isp pipe path */ + struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg; + /* enable TNR */ + u32 b_enable_tnr; + /* + * Number of frames for RTA processing. + * Set to 0 to use the firmware's default value. + */ + u32 rta_frames_per_proc; +}; + +struct isp4fw_image_prop { + enum isp4fw_image_format image_format; + u32 width; + u32 height; + u32 luma_pitch; + u32 chroma_pitch; + enum isp4fw_yuv_range yuv_range; +}; + +struct isp4fw_buffer { + /* + * A check num for debug usage, host can set the buf_tags + * to different numbers + */ + u32 buf_tags; + union { + u32 value; + struct { + u32 space : 16; + u32 vmid : 16; + } bit; + } vmid_space; + u32 buf_base_a_lo; /* Low address of buffer A */ + u32 buf_base_a_hi; /* High address of buffer A */ + u32 buf_size_a; /* Buffer size of buffer A */ + + u32 buf_base_b_lo; /* Low address of buffer B */ + u32 buf_base_b_hi; /* High address of buffer B */ + u32 buf_size_b; /* Buffer size of buffer B */ + + u32 buf_base_c_lo; /* Low address of buffer C */ + u32 buf_base_c_hi; /* High address of buffer C */ + u32 buf_size_c; /* Buffer size of buffer C */ +}; + +struct isp4fw_buffer_meta_info { + u32 enabled; /* enabled flag */ + enum isp4fw_buffer_status status; /* BufferStatus */ + struct isp4fw_error_code err; /* err code */ + enum isp4fw_buffer_source source; /* BufferSource */ + struct isp4fw_image_prop image_prop; /* image_prop */ + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_meta_info { + u32 poc; /* frame id */ + u32 fc_id; /* frame ctl id */ + u32 time_stamp_lo; /* timestamp low 32 bits */ + u32 time_stamp_hi; /* timestamp_high 32 bits */ + struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */ +}; + +struct isp4fw_cmd_send_buffer { + enum isp4fw_buffer_type buffer_type; + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_cmd_set_out_ch_prop { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + struct isp4fw_image_prop image_prop; /* image property */ +}; + +struct isp4fw_cmd_enable_out_ch { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + u32 is_enable; /* If channel is enabled or not */ +}; + +struct isp4fw_cmd_set_stream_cfg { + struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */ +}; + +#endif /* _ISP4_FW_CMD_RESP_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c new file mode 100644 index 000000000000..fc9ad7d91a7e --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ + (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\ + | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) + +#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2) + +struct isp4if_rb_config { + const char *name; + u32 index; + u32 reg_rptr; + u32 reg_wptr; + u32 reg_base_lo; + u32 reg_base_hi; + u32 reg_size; + u32 val_size; + u64 base_mc_addr; + void *base_sys_addr; +}; + +/* FW cmd ring buffer configuration */ +static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "CMD_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR4, + .reg_wptr = ISP_RB_WPTR4, + .reg_base_lo = ISP_RB_BASE_LO4, + .reg_base_hi = ISP_RB_BASE_HI4, + .reg_size = ISP_RB_SIZE4, + }, + { + .name = "CMD_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR1, + .reg_wptr = ISP_RB_WPTR1, + .reg_base_lo = ISP_RB_BASE_LO1, + .reg_base_hi = ISP_RB_BASE_HI1, + .reg_size = ISP_RB_SIZE1, + }, + { + .name = "CMD_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR2, + .reg_wptr = ISP_RB_WPTR2, + .reg_base_lo = ISP_RB_BASE_LO2, + .reg_base_hi = ISP_RB_BASE_HI2, + .reg_size = ISP_RB_SIZE2, + }, + { + .name = "CMD_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR3, + .reg_wptr = ISP_RB_WPTR3, + .reg_base_lo = ISP_RB_BASE_LO3, + .reg_base_hi = ISP_RB_BASE_HI3, + .reg_size = ISP_RB_SIZE3, + }, +}; + +/* FW resp ring buffer configuration */ +static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "RES_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR12, + .reg_wptr = ISP_RB_WPTR12, + .reg_base_lo = ISP_RB_BASE_LO12, + .reg_base_hi = ISP_RB_BASE_HI12, + .reg_size = ISP_RB_SIZE12, + }, + { + .name = "RES_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR9, + .reg_wptr = ISP_RB_WPTR9, + .reg_base_lo = ISP_RB_BASE_LO9, + .reg_base_hi = ISP_RB_BASE_HI9, + .reg_size = ISP_RB_SIZE9, + }, + { + .name = "RES_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR10, + .reg_wptr = ISP_RB_WPTR10, + .reg_base_lo = ISP_RB_BASE_LO10, + .reg_base_hi = ISP_RB_BASE_HI10, + .reg_size = ISP_RB_SIZE10, + }, + { + .name = "RES_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR11, + .reg_wptr = ISP_RB_WPTR11, + .reg_base_lo = ISP_RB_BASE_LO11, + .reg_base_hi = ISP_RB_BASE_HI11, + .reg_size = ISP_RB_SIZE11, + }, +}; + +/* FW log ring buffer configuration */ +static struct isp4if_rb_config isp4if_log_rb_config = { + .name = "LOG_RB", + .index = 0, + .reg_rptr = ISP_LOG_RB_RPTR0, + .reg_wptr = ISP_LOG_RB_WPTR0, + .reg_base_lo = ISP_LOG_RB_BASE_LO0, + .reg_base_hi = ISP_LOG_RB_BASE_HI0, + .reg_size = ISP_LOG_RB_SIZE0, +}; + +static struct isp4if_gpu_mem_info * +isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) +{ + struct isp4if_gpu_mem_info *mem_info; + struct device *dev = ispif->dev; + int ret; + + mem_info = kmalloc_obj(*mem_info, GFP_KERNEL); + if (!mem_info) + return NULL; + + mem_info->mem_size = mem_size; + ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size, + &mem_info->mem_handle, + &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + if (ret) { + kfree(mem_info); + return NULL; + } + + return mem_info; +} + +static void isp4if_gpu_mem_free(struct isp4_interface *ispif, + struct isp4if_gpu_mem_info **mem_info_ptr) +{ + struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; + struct device *dev = ispif->dev; + + if (!mem_info) { + dev_err(dev, "invalid mem_info\n"); + return; + } + + *mem_info_ptr = NULL; + isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + kfree(mem_info); +} + +static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) +{ + isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool); + isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf); + isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf); + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) + isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]); +} + +static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, + ISP4FW_MEMORY_POOL_SIZE); + if (!ispif->fw_mem_pool) + goto error_no_memory; + + ispif->fw_cmd_resp_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); + if (!ispif->fw_cmd_resp_buf) + goto error_no_memory; + + ispif->fw_log_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); + if (!ispif->fw_log_buf) + goto error_no_memory; + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + ispif->meta_info_buf[i] = + isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE); + if (!ispif->meta_info_buf[i]) + goto error_no_memory; + } + + return 0; + +error_no_memory: + dev_err(dev, "failed to allocate gpu memory\n"); + return -ENOMEM; +} + +static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size) +{ + const u8 *surplus_ptr; + const u32 *buffer; + u32 checksum = 0; + size_t i; + + buffer = (const u32 *)buf; + for (i = 0; i < buf_size / sizeof(u32); i++) + checksum += buffer[i]; + + surplus_ptr = (const u8 *)&buffer[i]; + /* add surplus data crc checksum */ + for (i = 0; i < buf_size % sizeof(u32); i++) + checksum += surplus_ptr[i]; + + return checksum; +} + +void isp4if_clear_cmdq(struct isp4_interface *ispif) +{ + struct isp4if_cmd_element *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->cmdq_lock) + list_splice_init(&ispif->cmdq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, list) + kfree(buf_node); +} + +static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, + enum isp4if_stream_id stream) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_free; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + + /* + * Read and write pointers are equal, indicating the ring buffer + * is empty + */ + if (wr_ptr == rd_ptr) + return false; + + if (wr_ptr > rd_ptr) + bytes_free = len - (wr_ptr - rd_ptr); + else + bytes_free = rd_ptr - wr_ptr; + + /* + * Ignore one byte from the bytes free to prevent rd_ptr from equaling + * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is + * supposed to indicate that the ring buffer is empty. + */ + return bytes_free <= sizeof(struct isp4fw_cmd); +} + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id) +{ + struct isp4if_cmd_element *ele; + + guard(spinlock)(&ispif->cmdq_lock); + + list_for_each_entry(ele, &ispif->cmdq, list) { + if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) { + list_del(&ele->list); + return ele; + } + } + + return NULL; +} + +/* Must check that isp4if_is_cmdq_rb_full() == false before calling */ +static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + const struct isp4fw_cmd *cmd) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 cmd_sz = sizeof(*cmd); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + const void *src = cmd; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) { + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, rd_ptr, wr_ptr, len, cmd_sz); + return -EINVAL; + } + + bytes_to_end = len - wr_ptr; + if (bytes_to_end >= cmd_sz) { + /* FW cmd is just a straight copy to the write pointer */ + memcpy(mem_sys + wr_ptr, src, cmd_sz); + isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len); + } else { + /* + * FW cmd is split because the ring buffer needs to wrap + * around + */ + memcpy(mem_sys + wr_ptr, src, bytes_to_end); + memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end); + } + + return 0; +} + +static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) +{ + return ISP4IF_STREAM_ID_1; +} + +static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, + const void *package, + u32 package_size, bool sync) +{ + enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); + struct isp4if_cmd_element *ele = NULL; + struct device *dev = ispif->dev; + struct isp4fw_cmd cmd; + u32 seq_num; + int ret; + + if (package_size > sizeof(cmd.cmd_param)) { + dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n", + package_size, sizeof(cmd.cmd_param), cmd_id, stream); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = cmd_id; + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID; + break; + case ISP4IF_STREAM_ID_1: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_1; + break; + default: + dev_err(dev, "fail bad stream id %d\n", stream); + return -EINVAL; + } + + /* Allocate the sync command object early and outside of the lock */ + if (sync) { + ele = kmalloc_obj(*ele, GFP_KERNEL); + if (!ele) + return -ENOMEM; + + /* Get two references: one for the resp thread, one for us */ + atomic_set(&ele->refcnt, 2); + init_completion(&ele->cmd_done); + } + + if (package && package_size) + memcpy(cmd.cmd_param, package, package_size); + + scoped_guard(mutex, &ispif->isp4if_mutex) { + ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret, + ISP4IF_RB_FULL_SLEEP_US, + ISP4IF_RB_FULL_TIMEOUT_US, false, ispif, + stream); + if (ret) { + struct isp4if_rb_config *rb_config = + &isp4if_resp_rb_config[stream]; + u32 rd_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_rptr); + u32 wr_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_wptr); + + dev_err(dev, + "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", + stream, rd_ptr, wr_ptr); + ret = -ETIMEDOUT; + goto free_ele; + } + + seq_num = ispif->host2fw_seq_num++; + cmd.cmd_seq_num = seq_num; + cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd) + - sizeof(u32)); + + /* + * only append the fw cmd to queue when its response needs to + * be waited for, currently there are only two such commands, + * disable channel and stop stream which are only sent after + * close camera + */ + if (ele) { + ele->seq_num = seq_num; + ele->cmd_id = cmd_id; + scoped_guard(spinlock, &ispif->cmdq_lock) + list_add_tail(&ele->list, &ispif->cmdq); + } + + ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); + if (ret) { + dev_err(dev, + "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", + cmd_id); + goto err_dequeue_ele; + } + } + + if (ele) { + ret = wait_for_completion_timeout(&ele->cmd_done, + ISP4IF_FW_CMD_TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto err_dequeue_ele; + } + + ret = 0; + goto put_ele_ref; + } + + return 0; + +err_dequeue_ele: + /* + * Try to remove the command from the queue. If that fails, then it + * means the response thread is currently using the object, and we need + * to use the refcount to avoid a use-after-free by either side. + */ + if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id)) + goto free_ele; + +put_ele_ref: + /* Don't free the command if we didn't put the last reference */ + if (ele && atomic_dec_return(&ele->refcnt)) + ele = NULL; + +free_ele: + /* + * The response handler or the timeout/error path must dequeue the + * command element before we own the final reference. + */ + if (ele) + INIT_LIST_HEAD(&ele->list); + kfree(ele); + return ret; +} + +static int isp4if_send_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4fw_cmd_send_buffer cmd; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW; + cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(buf_info->planes[0].mc_addr, + &cmd.buffer.buf_base_a_lo, + &cmd.buffer.buf_base_a_hi); + cmd.buffer.buf_size_a = buf_info->planes[0].len; + + isp4if_split_addr64(buf_info->planes[1].mc_addr, + &cmd.buffer.buf_base_b_lo, + &cmd.buffer.buf_base_b_hi); + cmd.buffer.buf_size_b = buf_info->planes[1].len; + + isp4if_split_addr64(buf_info->planes[2].mc_addr, + &cmd.buffer.buf_base_c_lo, + &cmd.buffer.buf_base_c_hi); + cmd.buffer.buf_size_c = buf_info->planes[2].len; + + return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd, + sizeof(cmd), false); +} + +static void isp4if_init_rb_config(struct isp4_interface *ispif, + struct isp4if_rb_config *rb_config) +{ + isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo, + rb_config->base_mc_addr); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi, + rb_config->base_mc_addr >> 32); + isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size); +} + +static int isp4if_fw_init(struct isp4_interface *ispif) +{ + u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; + struct isp4if_rb_config *rb_config; + u32 offset; + unsigned int i; + + /* initialize CMD_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_cmd_rb_config + i); + offset = aligned_rb_chunk_size * rb_config->index; + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize RESP_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_resp_rb_config + i); + offset = aligned_rb_chunk_size * + (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize LOG_RB stream */ + rb_config = &isp4if_log_rb_config; + rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; + rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; + rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; + + isp4if_init_rb_config(ispif, rb_config); + + return 0; +} + +static int isp4if_wait_fw_ready(struct isp4_interface *ispif, + u32 isp_status_addr) +{ + struct device *dev = ispif->dev; + u32 timeout_ms = 100; + u32 interval_ms = 1; + u32 reg_val; + + /* wait for FW initialize done! */ + if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val + & ISP_STATUS__CCPU_REPORT_MASK, + interval_ms * 1000, timeout_ms * 1000, false, + ispif->mmio, isp_status_addr)) + return 0; + + dev_err(dev, "ISP CCPU FW boot failed\n"); + + return -ETIME; +} + +static void isp4if_enable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); +} + +static void isp4if_disable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); +} + +static int isp4if_fw_boot(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + if (ispif->status != ISP4IF_STATUS_PWR_ON) { + dev_err(dev, "invalid isp power status %d\n", ispif->status); + return -EINVAL; + } + + isp4if_disable_ccpu(ispif); + + isp4if_fw_init(ispif); + + /* clear ccpu status */ + isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0); + + isp4if_enable_ccpu(ispif); + + if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { + isp4if_disable_ccpu(ispif); + return -EINVAL; + } + + /* enable interrupts */ + isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN, + ISP4IF_FW_RESP_RB_IRQ_EN_MASK); + + ispif->status = ISP4IF_STATUS_FW_RUNNING; + + dev_dbg(dev, "ISP CCPU FW boot success\n"); + + return 0; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp) +{ + struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 resp_sz = sizeof(*resp); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + void *dst = resp; + u32 checksum; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) + goto err_rb_invalid; + + /* + * Read and write pointers are equal, indicating the ring buffer is + * empty + */ + if (rd_ptr == wr_ptr) + return -ENODATA; + + bytes_to_end = len - rd_ptr; + if (bytes_to_end >= resp_sz) { + /* FW response is just a straight copy from the read pointer */ + if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, resp_sz); + isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len); + } else { + /* + * FW response is split because the ring buffer wrapped + * around + */ + if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, bytes_to_end); + memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end); + } + + checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32)); + if (checksum != resp->resp_check_sum) { + dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", + checksum, resp->resp_check_sum, rd_ptr, wr_ptr); + dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", + stream, resp->resp_seq_num, + resp->resp_id); + return -EINVAL; + } + + return 0; + +err_rb_invalid: + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, rd_ptr, wr_ptr, len, resp_sz); + return -EINVAL; +} + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false); +} + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true); +} + +void isp4if_clear_bufq(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->bufq_lock) + list_splice_init(&ispif->bufq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, node) + kfree(buf_node); +} + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) +{ + kfree(buf_node); +} + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) +{ + struct isp4if_img_buf_node *node; + + node = kmalloc_obj(*node, GFP_KERNEL); + if (node) + node->buf_info = *buf_info; + + return node; +} + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node; + + guard(spinlock)(&ispif->bufq_lock); + + buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node), + node); + if (buf_node) + list_del(&buf_node->node); + + return buf_node; +} + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node) +{ + int ret; + + ret = isp4if_send_buffer(ispif, &buf_node->buf_info); + if (ret) + return ret; + + scoped_guard(spinlock, &ispif->bufq_lock) + list_add_tail(&buf_node->node, &ispif->bufq); + + return 0; +} + +int isp4if_stop(struct isp4_interface *ispif) +{ + isp4if_disable_ccpu(ispif); + + isp4if_dealloc_fw_gpumem(ispif); + + return 0; +} + +int isp4if_start(struct isp4_interface *ispif) +{ + int ret; + + ret = isp4if_alloc_fw_gpumem(ispif); + if (ret) + return ret; + + ret = isp4if_fw_boot(ispif); + if (ret) + goto failed_fw_boot; + + return 0; + +failed_fw_boot: + isp4if_dealloc_fw_gpumem(ispif); + return ret; +} + +int isp4if_deinit(struct isp4_interface *ispif) +{ + isp4if_clear_cmdq(ispif); + + isp4if_clear_bufq(ispif); + + mutex_destroy(&ispif->isp4if_mutex); + + return 0; +} + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio) +{ + ispif->dev = dev; + ispif->mmio = isp_mmio; + + spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */ + spin_lock_init(&ispif->bufq_lock); /* used for bufq access */ + mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ + + INIT_LIST_HEAD(&ispif->cmdq); + INIT_LIST_HEAD(&ispif->bufq); + + return 0; +} diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h new file mode 100644 index 000000000000..ce3ac9b9e5cd --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_INTERFACE_H_ +#define _ISP4_INTERFACE_H_ + +#include +#include +#include +#include + +struct isp4fw_resp; + +#define ISP4IF_RB_MAX 25 +#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9 +#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1) +#define ISP4IF_RB_PMBMAP_MEM_CHUNK \ + (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1)) +#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd) +#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40 +#define ISP4IF_FW_CMD_BUF_SIZE \ + (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE) +#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC) +#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US) + +#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) +#define ISP4IF_MAX_STREAM_BUF_COUNT 8 + +#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M + +enum isp4if_stream_id { + ISP4IF_STREAM_ID_GLOBAL = 0, + ISP4IF_STREAM_ID_1 = 1, + ISP4IF_STREAM_ID_MAX = 4 +}; + +enum isp4if_status { + ISP4IF_STATUS_PWR_OFF, + ISP4IF_STATUS_PWR_ON, + ISP4IF_STATUS_FW_RUNNING, + ISP4IF_FSM_STATUS_MAX +}; + +struct isp4if_gpu_mem_info { + u64 mem_size; + u64 gpu_mc_addr; + void *sys_addr; + void *mem_handle; +}; + +struct isp4if_img_buf_info { + struct { + void *sys_addr; + u64 mc_addr; + u32 len; + } planes[3]; +}; + +struct isp4if_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +struct isp4if_cmd_element { + struct list_head list; + u32 seq_num; + u32 cmd_id; + struct completion cmd_done; + atomic_t refcnt; +}; + +struct isp4_interface { + struct device *dev; + void __iomem *mmio; + + spinlock_t cmdq_lock; /* used for cmdq access */ + spinlock_t bufq_lock; /* used for bufq access */ + struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */ + + struct list_head cmdq; /* commands sent to fw */ + struct list_head bufq; /* buffers sent to fw */ + + enum isp4if_status status; + u32 host2fw_seq_num; + + /* ISP fw buffers */ + struct isp4if_gpu_mem_info *fw_log_buf; + struct isp4if_gpu_mem_info *fw_cmd_resp_buf; + struct isp4if_gpu_mem_info *fw_mem_pool; + struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT]; +}; + +static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi) +{ + if (lo) + *lo = addr & 0xffffffff; + + if (hi) + *hi = addr >> 32; +} + +static inline u64 isp4if_join_addr64(u32 lo, u32 hi) +{ + return (((u64)hi) << 32) | (u64)lo; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp); + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id); + +void isp4if_clear_cmdq(struct isp4_interface *ispif); + +void isp4if_clear_bufq(struct isp4_interface *ispif); + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node); + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info); + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif); + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node); + +int isp4if_stop(struct isp4_interface *ispif); + +int isp4if_start(struct isp4_interface *ispif); + +int isp4if_deinit(struct isp4_interface *ispif); + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio); + +#endif /* _ISP4_INTERFACE_H_ */ From 4e5e7a7ddb4ab9ac35928d7dc72efc8797639dc3 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:46 +0800 Subject: [PATCH 0540/5214] media: platform: amd: isp4 subdev and firmware loading handling added Isp4 sub-device is implementing v4l2 sub-device interface. It has one capture video node, and supports only preview stream. It manages firmware states, stream configuration. Add interrupt handling and notification for isp firmware to isp-subdevice. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 104 +- drivers/media/platform/amd/isp4/isp4.h | 7 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 1029 +++++++++++++++++ drivers/media/platform/amd/isp4/isp4_subdev.h | 120 ++ 6 files changed, 1259 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.c create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.h diff --git a/MAINTAINERS b/MAINTAINERS index 1d19168cecaa..1e02d9967f60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1168,6 +1168,8 @@ F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h +F: drivers/media/platform/amd/isp4/isp4_subdev.c +F: drivers/media/platform/amd/isp4/isp4_subdev.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index c7eadd33fc97..7e4854b6a4cd 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ - isp4_interface.o + isp4_interface.o \ + isp4_subdev.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 58b21258b6d3..e62f5d652d81 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -3,13 +3,18 @@ * Copyright (C) 2025 Advanced Micro Devices, Inc. */ +#include #include #include #include #include "isp4.h" +#include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \ + (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \ + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) static const struct { const char *name; @@ -17,27 +22,103 @@ static const struct { u32 en_mask; u32 ack_mask; u32 rb_int_num; -} isp4_irq[] = { +} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ { .name = "isp_irq_global", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK, .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ }, { .name = "isp_irq_stream1", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK, .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ }, }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable) +{ + u32 intr_en; + + /* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */ + spin_lock_irq(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN); + if (enable) + intr_en |= isp4_irq[index].en_mask; + else + intr_en &= ~isp4_irq[index].en_mask; + + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock_irq(&isp_subdev->irq_lock); +} + +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index) +{ + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[index]; + + thread_ctx->resp_ready = true; + wake_up_interruptible(&thread_ctx->waitq); +} + static irqreturn_t isp4_irq_handler(int irq, void *arg) { + struct isp4_subdev *isp_subdev = arg; + u32 intr_ack = 0, intr_en = 0, intr_status; + int seen = 0; + + /* Get the ISP_SYS interrupt status */ + intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS); + intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK; + + /* Find which ISP_SYS interrupts fired */ + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + if (intr_status & isp4_irq[i].status_mask) { + intr_ack |= isp4_irq[i].ack_mask; + intr_en |= isp4_irq[i].en_mask; + seen |= BIT(i); + } + } + + /* + * Disable the ISP_SYS interrupts that fired. Must be done before waking + * the response threads, since they re-enable interrupts when finished. + * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt(). + */ + spin_lock(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en; + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock(&isp_subdev->irq_lock); + + /* + * Clear the ISP_SYS interrupts. This must be done after the interrupts + * are disabled, so that ISP FW won't flag any new interrupts on these + * streams, and thus we don't need to clear interrupts again before + * re-enabling them in the response thread. + */ + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack); + + /* + * The operation `(seen >> i) << i` is logically equivalent to + * `seen &= ~BIT(i)`, with fewer instructions after compilation. + */ + for (int i; (i = ffs(seen)); seen = (seen >> i) << i) + isp4_wake_up_resp_thread(isp_subdev, i - 1); + return IRQ_HANDLED; } static int isp4_capture_probe(struct platform_device *pdev) { + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; struct device *dev = &pdev->dev; - int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_subdev *isp_subdev; struct isp4_device *isp_dev; int ret; @@ -47,6 +128,12 @@ static int isp4_capture_probe(struct platform_device *pdev) dev->init_name = ISP4_DRV_NAME; + isp_subdev = &isp_dev->isp_subdev; + isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(isp_subdev->mmio)) + return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio), + "isp ioremap fail\n"); + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); if (irq[i] < 0) @@ -55,7 +142,8 @@ static int isp4_capture_probe(struct platform_device *pdev) isp4_irq[i].rb_int_num); ret = devm_request_irq(dev, irq[i], isp4_irq_handler, - IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + IRQF_NO_AUTOEN, isp4_irq[i].name, + isp_subdev); if (ret) return dev_err_probe(dev, ret, "fail to req irq %d\n", irq[i]); @@ -78,6 +166,13 @@ static int isp4_capture_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + spin_lock_init(&isp_subdev->irq_lock); + ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq); + if (ret) { + dev_err_probe(dev, ret, "fail init isp4 sub dev\n"); + goto err_pm_disable; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); @@ -89,6 +184,8 @@ static int isp4_capture_probe(struct platform_device *pdev) return 0; err_isp4_deinit: + isp4sd_deinit(&isp_dev->isp_subdev); +err_pm_disable: pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); err_clean_media: @@ -103,6 +200,7 @@ static void isp4_capture_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; media_device_unregister(&isp_dev->mdev); + isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); media_device_cleanup(&isp_dev->mdev); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h index 7f2db0dfa2d9..2db6683d6d8b 100644 --- a/drivers/media/platform/amd/isp4/isp4.h +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -6,12 +6,15 @@ #ifndef _ISP4_H_ #define _ISP4_H_ -#include -#include +#include +#include "isp4_subdev.h" struct isp4_device { struct v4l2_device v4l2_dev; + struct isp4_subdev isp_subdev; struct media_device mdev; }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable); + #endif /* _ISP4_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c new file mode 100644 index 000000000000..6d571b7f8840 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include + +#include "isp4.h" +#include "isp4_fw_cmd_resp.h" +#include "isp4_interface.h" + +#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 + +#define ISP4SD_PERFORMANCE_STATE_LOW 0 +#define ISP4SD_PERFORMANCE_STATE_HIGH 1 + +/* align 32KB */ +#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) + +#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) + +static const char *isp4sd_entity_name = "amd isp4"; + +static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { + "amd_isp4_thread_global", + "amd_isp4_thread_stream1", +}; + +static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable) +{ + if (isp_subdev->enable_gpio) { + gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); + dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", + enable ? "enable" : "disable"); + } +} + +static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + int ret; + + if (!ispif->fw_mem_pool) { + dev_err(dev, "fail to alloc mem pool\n"); + return -ENOMEM; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", + ispif->fw_mem_pool->gpu_mc_addr, + buf_type.buffer.buf_size_a, ret); + return ret; + } + + dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", + ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); + + return 0; +} + +static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_stream_cfg cmd; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = + ISP4FW_SENSOR_ID_ON_MIPI0; + cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = + ISP4FW_MIPI0_ISP_PIPELINE_ID; + + cmd.stream_cfg.b_enable_tnr = true; + dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, + cmd.stream_cfg.b_enable_tnr); + + return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, + &cmd, sizeof(cmd)); +} + +static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + int ret; + + if (!meta_info_buf) { + dev_err(dev, "fail for no meta info buf(%u)\n", i); + return -ENOMEM; + } + + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = + ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_buf->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = meta_info_buf->mem_size; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send meta info(%u) fail\n", i); + return ret; + } + } + + dev_dbg(dev, "send meta info suc\n"); + return 0; +} + +static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, + struct isp4fw_image_prop *out_prop, + struct v4l2_subdev_state *state, u32 pad) +{ + struct device *dev = isp_subdev->dev; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(dev, "fail get subdev state format\n"); + return false; + } + + switch (format->code) { + case MEDIA_BUS_FMT_YUYV8_1_5X8: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width; + out_prop->chroma_pitch = out_prop->width; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width * 2; + out_prop->chroma_pitch = 0; + break; + default: + dev_err(dev, "fail for bad image format:0x%x\n", + format->code); + return false; + } + + if (!out_prop->width || !out_prop->height) + return false; + + return true; +} + +static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + dev_dbg(dev, "w:%u,h:%u\n", w, h); + + if (isp4sd_send_meta_buf(isp_subdev)) { + dev_err(dev, "fail to send meta buf\n"); + sensor_info->status = ISP4SD_START_STATUS_START_FAIL; + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (!sensor_info->start_stream_cmd_sent && + sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to start stream\n"); + return ret; + } + + sensor_info->start_stream_cmd_sent = true; + } else { + dev_dbg(dev, + "no send START_STREAM, start_sent %u, buf_sent %u\n", + sensor_info->start_stream_cmd_sent, + sensor_info->buf_sent_cnt); + } + + return 0; +} + +static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; + struct isp4fw_cmd_enable_out_ch cmd_ch_en; + struct device *dev = isp_subdev->dev; + int ret; + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); + cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + + if (!isp4sd_get_str_out_prop(isp_subdev, + &cmd_ch_prop.image_prop, state, pad)) { + dev_err(dev, "fail to get out prop\n"); + return -EINVAL; + } + + dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", + cmd_ch_prop.ch, + cmd_ch_prop.image_prop.image_format, + cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, + cmd_ch_prop.image_prop.luma_pitch, + cmd_ch_prop.image_prop.chroma_pitch); + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, + &cmd_ch_prop, sizeof(cmd_ch_prop)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to set out prop\n"); + return ret; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); + cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + cmd_ch_en.is_enable = true; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_en, sizeof(cmd_ch_en)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to enable channel\n"); + return ret; + } + + dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + + if (!sensor_info->start_stream_cmd_sent) { + ret = isp4sd_kickoff_stream(isp_subdev, + cmd_ch_prop.image_prop.width, + cmd_ch_prop.image_prop.height); + if (ret) { + dev_err(dev, "kickoff stream fail %d\n", ret); + return ret; + } + /* + * sensor_info->start_stream_cmd_sent will be set to true + * 1. in isp4sd_kickoff_stream, if app first send buffer then + * start stream + * 2. in isp_set_stream_buf, if app first start stream, then + * send buffer because ISP FW has the requirement, host needs + * to send buffer before send start stream cmd + */ + if (sensor_info->start_stream_cmd_sent) { + sensor_info->status = ISP4SD_START_STATUS_STARTED; + output_info->start_status = ISP4SD_START_STATUS_STARTED; + dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); + } + } else { + dev_dbg(dev, "stream running, no need kickoff\n"); + output_info->start_status = ISP4SD_START_STATUS_STARTED; + } + + dev_dbg(dev, "setup output suc\n"); + return 0; +} + +static int isp4sd_init_stream(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + int ret; + + ret = isp4sd_setup_fw_mem_pool(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup fw mem pool\n"); + return ret; + } + + ret = isp4sd_set_stream_path(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup stream path\n"); + return ret; + } + + return 0; +} + +static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); + } else { + memset(format, 0, sizeof(*format)); + format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; + } + + isp4if_clear_bufq(ispif); + isp4if_clear_cmdq(ispif); + + sensor_info->start_stream_cmd_sent = false; + sensor_info->buf_sent_cnt = 0; + + sensor_info->status = ISP4SD_START_STATUS_OFF; + output_info->start_status = ISP4SD_START_STATUS_OFF; +} + +static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_cmd_done *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_cmd_element *ele = + isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + stream_id, + para->cmd_id, para->cmd_status, para->cmd_seq_num, + ele); + + if (ele) { + complete(&ele->cmd_done); + if (atomic_dec_and_test(&ele->refcnt)) + kfree(ele); + } +} + +static struct isp4fw_meta_info * +isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc) +{ + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + + if (meta_info_buf->gpu_mc_addr == mc) + return meta_info_buf->sys_addr; + } + + return NULL; +} + +static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, + u64 meta_info_mc) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { + dev_warn(dev, "not working status %i, meta_info 0x%llx\n", + isp_subdev->sensor_info.status, meta_info_mc); + return; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_mc, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; + + if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type))) + dev_err(dev, "fail send meta_info 0x%llx\n", + meta_info_mc); + else + dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc); +} + +static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_param_package *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4if_img_buf_node *prev; + struct isp4fw_meta_info *meta; + u64 mc; + + mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); + meta = isp4sd_get_meta_by_mc(isp_subdev, mc); + if (!meta) { + dev_err(dev, "fail to get meta from mc %llx\n", mc); + return; + } + + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + meta->preview.status); + + if (meta->preview.enabled && + (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || + meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || + meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { + prev = isp4if_dequeue_buffer(ispif); + if (prev) + isp4if_dealloc_buffer_node(prev); + else + dev_err(dev, "fail null prev buf\n"); + + } else if (meta->preview.enabled) { + dev_err(dev, "fail bad preview status %u\n", + meta->preview.status); + } + + if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) + isp4sd_send_meta_info(isp_subdev, mc); + + dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, + isp_subdev->sensor_info.status); +} + +static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4fw_resp resp; + + while (true) { + if (isp4if_f2h_resp(ispif, stream_id, &resp)) { + /* Re-enable the interrupt */ + isp4_intr_enable(isp_subdev, stream_id, true); + /* + * Recheck to see if there is a new response. + * To ensure that an in-flight interrupt is not lost, + * enabling the interrupt must occur _before_ checking + * for a new response, hence a memory barrier is needed. + * Disable the interrupt again if there was a new + * response. + */ + mb(); + if (likely(isp4if_f2h_resp(ispif, stream_id, &resp))) + break; + + isp4_intr_enable(isp_subdev, stream_id, false); + } + + switch (resp.resp_id) { + case ISP4FW_RESP_ID_CMD_DONE: + isp4sd_fw_resp_cmd_done(isp_subdev, stream_id, + &resp.param.cmd_done); + break; + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + isp4sd_fw_resp_frame_done(isp_subdev, stream_id, + &resp.param.frame_done); + break; + default: + dev_err(dev, "-><- fail respid (0x%x)\n", + resp.resp_id); + break; + } + } +} + +static s32 isp4sd_fw_resp_thread(void *context) +{ + struct isp4_subdev_thread_param *para = context; + struct isp4_subdev *isp_subdev = para->isp_subdev; + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[para->idx]; + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "[%u] fw resp thread started\n", para->idx); + while (true) { + wait_event_interruptible(thread_ctx->waitq, + thread_ctx->resp_ready); + thread_ctx->resp_ready = false; + + if (kthread_should_stop()) { + dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx); + break; + } + + isp4sd_fw_resp_func(isp_subdev, para->idx); + } + + return 0; +} + +static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + if (thread_ctx->thread) { + kthread_stop(thread_ctx->thread); + thread_ctx->thread = NULL; + } + } + + return 0; +} + +static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + isp_subdev->isp_resp_para[i].idx = i; + isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev; + init_waitqueue_head(&thread_ctx->waitq); + thread_ctx->resp_ready = false; + + thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread, + &isp_subdev->isp_resp_para[i], + isp4sd_thread_name[i]); + if (IS_ERR(thread_ctx->thread)) { + dev_err(dev, "create thread [%d] fail\n", i); + thread_ctx->thread = NULL; + isp4sd_stop_resp_proc_threads(isp_subdev); + return -EINVAL; + } + } + + return 0; +} + +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) { + dev_err(dev, "fail for stream still running\n"); + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (isp_subdev->irq_enabled) { + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + disable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = false; + } + + isp4sd_stop_resp_proc_threads(isp_subdev); + dev_dbg(dev, "isp_subdev stop resp proc threads suc\n"); + + isp4if_stop(ispif); + + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) + dev_err(dev, + "fail to set isp_subdev performance state %u,ret %d\n", + perf_state, ret); + + /* hold ccpu reset */ + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0); + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0); + ret = pm_runtime_put_sync(dev); + if (ret) + dev_err(dev, "power off isp_subdev fail %d\n", ret); + else + dev_dbg(dev, "power off isp_subdev suc\n"); + + ispif->status = ISP4IF_STATUS_PWR_OFF; + isp4if_clear_cmdq(ispif); + isp4sd_module_enable(isp_subdev, false); + + /* + * When opening the camera, isp4sd_module_enable(isp_subdev, true) is + * called. Hardware requires at least a 20ms delay between disabling + * and enabling the module, so a sleep is added to ensure ISP stability + * during quick reopen scenarios. + */ + msleep(20); + + return 0; +} + +int isp4sd_pwron_and_init(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (ispif->status == ISP4IF_STATUS_FW_RUNNING) { + dev_dbg(dev, "camera already opened, do nothing\n"); + return 0; + } + + isp4sd_module_enable(isp_subdev, true); + + if (ispif->status < ISP4IF_STATUS_PWR_ON) { + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH; + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "fail to power on isp_subdev ret %d\n", + ret); + goto err_deinit; + } + + /* ISPPG ISP Power Status */ + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF); + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) { + dev_err(dev, + "fail to set performance state %u, ret %d\n", + perf_state, ret); + goto err_deinit; + } + + ispif->status = ISP4IF_STATUS_PWR_ON; + } + + isp_subdev->sensor_info.start_stream_cmd_sent = false; + isp_subdev->sensor_info.buf_sent_cnt = 0; + + ret = isp4if_start(ispif); + if (ret) { + dev_err(dev, "fail to start isp_subdev interface\n"); + goto err_deinit; + } + + if (isp4sd_start_resp_proc_threads(isp_subdev)) { + dev_err(dev, "isp_start_resp_proc_threads fail\n"); + goto err_deinit; + } + + dev_dbg(dev, "create resp threads ok\n"); + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + enable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = true; + + return 0; +err_deinit: + isp4sd_pwroff_and_deinit(sd); + return -EINVAL; +} + +static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + guard(mutex)(&isp_subdev->ops_mutex); + dev_dbg(dev, "status %i\n", output_info->start_status); + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) { + struct isp4fw_cmd_enable_out_ch cmd_ch_disable; + int ret; + + /* + * The struct will be shared with ISP FW, use memset() to + * guarantee padding bits are zeroed, since this is not + * guaranteed on all compilers. + */ + memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable)); + cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + /* `cmd_ch_disable.is_enable` is already false */ + ret = isp4if_send_command_sync(ispif, + ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_disable, + sizeof(cmd_ch_disable)); + if (ret) + dev_err(dev, "fail to disable stream\n"); + else + dev_dbg(dev, "wait disable stream suc\n"); + + ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM, + NULL, 0); + if (ret) + dev_err(dev, "fail to stop stream\n"); + else + dev_dbg(dev, "wait stop stream suc\n"); + } + + isp4sd_uninit_stream(isp_subdev, state, pad); + + /* + * Return success to ensure the stop process proceeds, + * and disregard any errors since they are not fatal. + */ + return 0; +} + +static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail, bad fsm %d\n", ispif->status); + return -EINVAL; + } + + switch (output_info->start_status) { + case ISP4SD_START_STATUS_OFF: + break; + case ISP4SD_START_STATUS_STARTED: + dev_dbg(dev, "stream already started, do nothing\n"); + return 0; + case ISP4SD_START_STATUS_START_FAIL: + dev_err(dev, "stream previously failed to start\n"); + return -EINVAL; + } + + ret = isp4sd_init_stream(isp_subdev); + if (ret) { + dev_err(dev, "fail to init isp_subdev stream\n"); + goto err_stop_stream; + } + + ret = isp4sd_setup_output(isp_subdev, state, pad); + if (ret) { + dev_err(dev, "fail to setup output\n"); + goto err_stop_stream; + } + + return 0; + +err_stop_stream: + isp4sd_stop_stream(isp_subdev, state, pad); + return ret; +} + +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_img_buf_node *buf_node; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail send img buf for bad fsm %d\n", + ispif->status); + return -EINVAL; + } + + buf_node = isp4if_alloc_buffer_node(buf_info); + if (!buf_node) { + dev_err(dev, "fail alloc sys img buf info node\n"); + return -ENOMEM; + } + + ret = isp4if_queue_buffer(ispif, buf_node); + if (ret) { + dev_err(dev, "fail to queue image buf, %d\n", ret); + goto error_release_buf_node; + } + + if (!isp_subdev->sensor_info.start_stream_cmd_sent) { + isp_subdev->sensor_info.buf_sent_cnt++; + + if (isp_subdev->sensor_info.buf_sent_cnt >= + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + ret = isp4if_send_command(ispif, + ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to START_STREAM"); + goto error_release_buf_node; + } + isp_subdev->sensor_info.start_stream_cmd_sent = true; + isp_subdev->sensor_info.output_info.start_status = + ISP4SD_START_STATUS_STARTED; + isp_subdev->sensor_info.status = + ISP4SD_START_STATUS_STARTED; + } else { + dev_dbg(dev, + "no send start, required %u, buf sent %u\n", + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM, + isp_subdev->sensor_info.buf_sent_cnt); + } + } + + return 0; + +error_release_buf_node: + isp4if_dealloc_buffer_node(buf_node); + return ret; +} + +static const struct v4l2_subdev_video_ops isp4sd_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static int isp4sd_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isp4sd_output_info *stream_info = + &(to_isp4_subdev(sd)->sensor_info.output_info); + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); + + if (!fmt) { + dev_err(sd->dev, "fail to get state format\n"); + return -EINVAL; + } + + *fmt = format->format; + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + stream_info->image_size = fmt->width * fmt->height * 2; + break; + case MEDIA_BUS_FMT_YUYV8_1_5X8: + default: + stream_info->image_size = fmt->width * fmt->height * 3 / 2; + break; + } + + if (!stream_info->image_size) { + dev_err(sd->dev, + "fail set pad format,code 0x%x,width %u, height %u\n", + fmt->code, fmt->width, fmt->height); + return -EINVAL; + } + + dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n", + fmt->code, fmt->width, fmt->height, + stream_info->image_size); + + return 0; +} + +static int isp4sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_start_stream(isp_subdev, state, pad); +} + +static int isp4sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_stop_stream(isp_subdev, state, pad); +} + +static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp4sd_set_pad_format, + .enable_streams = isp4sd_enable_streams, + .disable_streams = isp4sd_disable_streams, +}; + +static const struct v4l2_subdev_ops isp4sd_subdev_ops = { + .video = &isp4sd_video_ops, + .pad = &isp4sd_pad_ops, +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = v4l2_dev->dev; + int ret; + + isp_subdev->dev = dev; + v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops); + isp_subdev->sdev.owner = THIS_MODULE; + isp_subdev->sdev.dev = dev; + snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s", + dev_name(dev)); + + isp_subdev->sdev.entity.name = isp4sd_entity_name; + isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1, + &isp_subdev->sdev_pad); + if (ret) { + dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(&isp_subdev->sdev); + if (ret < 0) { + dev_err(dev, "fail to init finalize isp4 subdev %d\n", + ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev); + if (ret) { + dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n", + ret); + goto err_media_clean_up; + } + + isp4if_init(ispif, dev, isp_subdev->mmio); + + mutex_init(&isp_subdev->ops_mutex); + sensor_info->status = ISP4SD_START_STATUS_OFF; + + /* create ISP enable gpio control */ + isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev, + "enable_isp", + GPIOD_OUT_LOW); + if (IS_ERR(isp_subdev->enable_gpio)) { + ret = PTR_ERR(isp_subdev->enable_gpio); + dev_err(dev, "fail to get gpiod %d\n", ret); + goto err_subdev_unreg; + } + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + isp_subdev->irq[i] = irq[i]; + + isp_subdev->host2fw_seq_num = 1; + ispif->status = ISP4IF_STATUS_PWR_OFF; + + return 0; + +err_subdev_unreg: + v4l2_device_unregister_subdev(&isp_subdev->sdev); +err_media_clean_up: + v4l2_subdev_cleanup(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + return ret; +} + +void isp4sd_deinit(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + + v4l2_device_unregister_subdev(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + isp4if_deinit(ispif); + isp4sd_module_enable(isp_subdev, false); + + ispif->status = ISP4IF_STATUS_PWR_OFF; +} diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h new file mode 100644 index 000000000000..ceade9fec5d9 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_SUBDEV_H_ +#define _ISP4_SUBDEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +/* + * One is for none sensor specific response which is not used now. + * Another is for sensor specific response + */ +#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2 + +/* Indicates the ISP status */ +enum isp4sd_status { + ISP4SD_STATUS_PWR_OFF, + ISP4SD_STATUS_PWR_ON, + ISP4SD_STATUS_FW_RUNNING, + ISP4SD_STATUS_MAX +}; + +/* Indicates sensor and output stream status */ +enum isp4sd_start_status { + ISP4SD_START_STATUS_OFF, + ISP4SD_START_STATUS_STARTED, + ISP4SD_START_STATUS_START_FAIL, +}; + +struct isp4sd_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +/* This is ISP output after processing Bayer raw sensor input */ +struct isp4sd_output_info { + enum isp4sd_start_status start_status; + u32 image_size; +}; + +/* + * Struct for sensor info used as ISP input or source. + * status: sensor status. + * output_info: ISP output after processing the sensor input. + * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent + * to firmware. + * buf_sent_cnt: number of buffers sent to receive images. + */ +struct isp4sd_sensor_info { + struct isp4sd_output_info output_info; + enum isp4sd_start_status status; + bool start_stream_cmd_sent; + u32 buf_sent_cnt; +}; + +/* + * The thread is created by the driver to handle firmware responses which will + * be waken up when a firmware-to-driver response interrupt occurs. + */ +struct isp4sd_thread_handler { + struct task_struct *thread; + wait_queue_head_t waitq; + bool resp_ready; +}; + +struct isp4_subdev_thread_param { + u32 idx; + struct isp4_subdev *isp_subdev; +}; + +struct isp4_subdev { + struct v4l2_subdev sdev; + struct isp4_interface ispif; + + struct media_pad sdev_pad; + + enum isp4sd_status isp_status; + /* mutex used to synchronize the operation with firmware */ + struct mutex ops_mutex; + + struct isp4sd_thread_handler + fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + + u32 host2fw_seq_num; + + struct isp4sd_sensor_info sensor_info; + + /* gpio descriptor */ + struct gpio_desc *enable_gpio; + struct device *dev; + void __iomem *mmio; + struct isp4_subdev_thread_param + isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + bool irq_enabled; + /* spin lock to access ISP_SYS_INT0_EN exclusively */ + spinlock_t irq_lock; +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]); +void isp4sd_deinit(struct isp4_subdev *isp_subdev); +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info); +int isp4sd_pwron_and_init(struct v4l2_subdev *sd); +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd); + +#endif /* _ISP4_SUBDEV_H_ */ From 2ccf48af22709b90ea9419cd828c92652d7709ac Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:47 +0800 Subject: [PATCH 0541/5214] media: platform: amd: isp4 video node and buffers handling added Isp video implements v4l2 video interface and supports NV12 and YUYV. It manages buffers, pipeline power and state. Cherry-picked Sultan's DMA buffer related fix from branch v6.16-drm-tip-isp4-for-amd on https://github.com/kerneltoast/kernel_x86_laptop.git Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 11 + drivers/media/platform/amd/isp4/isp4_subdev.c | 13 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 2 + drivers/media/platform/amd/isp4/isp4_video.c | 797 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_video.h | 57 ++ 7 files changed, 881 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_video.c create mode 100644 drivers/media/platform/amd/isp4/isp4_video.h diff --git a/MAINTAINERS b/MAINTAINERS index 1e02d9967f60..2dc09b84a188 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1170,6 +1170,8 @@ F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h F: drivers/media/platform/amd/isp4/isp4_subdev.c F: drivers/media/platform/amd/isp4/isp4_subdev.h +F: drivers/media/platform/amd/isp4/isp4_video.c +F: drivers/media/platform/amd/isp4/isp4_video.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 7e4854b6a4cd..3fa0ee6d8a96 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ isp4_interface.o \ - isp4_subdev.o + isp4_subdev.o \ + isp4_video.o \ No newline at end of file diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index e62f5d652d81..9480dfffbcb2 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -173,6 +173,17 @@ static int isp4_capture_probe(struct platform_device *pdev) goto err_pm_disable; } + ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity, + 0, + &isp_dev->isp_subdev.isp_vdev.vdev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err_probe(dev, ret, "fail to create pad link\n"); + goto err_isp4_deinit; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 6d571b7f8840..5202232d50c5 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -467,11 +467,13 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); - if (prev) + if (prev) { + isp4vid_handle_frame_done(&isp_subdev->isp_vdev, + &prev->buf_info); isp4if_dealloc_buffer_node(prev); - else + } else { dev_err(dev, "fail null prev buf\n"); - + } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u\n", meta->preview.status); @@ -1006,6 +1008,10 @@ int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, isp_subdev->host2fw_seq_num = 1; ispif->status = ISP4IF_STATUS_PWR_OFF; + ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev); + if (ret) + goto err_subdev_unreg; + return 0; err_subdev_unreg: @@ -1020,6 +1026,7 @@ void isp4sd_deinit(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; + isp4vid_dev_deinit(&isp_subdev->isp_vdev); v4l2_device_unregister_subdev(&isp_subdev->sdev); media_entity_cleanup(&isp_subdev->sdev.entity); isp4if_deinit(ispif); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ceade9fec5d9..ddf6bdf4a62a 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -17,6 +17,7 @@ #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" +#include "isp4_video.h" /* * One is for none sensor specific response which is not used now. @@ -83,6 +84,7 @@ struct isp4_subdev_thread_param { struct isp4_subdev { struct v4l2_subdev sdev; struct isp4_interface ispif; + struct isp4vid_dev isp_vdev; struct media_pad sdev_pad; diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c new file mode 100644 index 000000000000..0cebb39f98e1 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4_interface.h" +#include "isp4_subdev.h" +#include "isp4_video.h" + +#define ISP4VID_ISP_DRV_NAME "amd_isp_capture" +#define ISP4VID_MAX_PREVIEW_FPS 30 +#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12 + +#define ISP4VID_PAD_VIDEO_OUTPUT 0 + +/* time perframe default */ +#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0] + +static const char *const isp4vid_video_dev_name = "Preview"; + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = { + {640, 360}, + {640, 480}, + {1280, 720}, + {1280, 960}, + {1920, 1080}, + {1920, 1440}, + {2560, 1440}, + {2880, 1620}, + {2880, 1624}, + {2888, 1808}, +}; + +static const u32 isp4vid_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUYV +}; + +/* time perframe list */ +static const struct v4l2_fract isp4vid_tpfs[] = { + { 1, ISP4VID_MAX_PREVIEW_FPS } +}; + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf) +{ + struct isp4vid_capture_buffer *isp4vid_buf; + void *vbuf; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list, + typeof(*isp4vid_buf), + list); + if (!isp4vid_buf) + return; + + vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0); + + if (vbuf != img_buf->planes[0].sys_addr) { + dev_err(isp_vdev->dev, "Invalid vbuf\n"); + return; + } + + list_del(&isp4vid_buf->list); + } + + /* Fill the buffer */ + isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); + isp4vid_buf->vb2.sequence = isp_vdev->sequence++; + isp4vid_buf->vb2.field = V4L2_FIELD_ANY; + + vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf, + 0, isp_vdev->format.sizeimage); + + vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n", + isp_vdev->format.sizeimage); +} + +static const struct v4l2_pix_format isp4vid_fmt_default = { + .width = 1920, + .height = 1080, + .pixelformat = ISP4VID_DEFAULT_FMT, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev, + enum vb2_buffer_state state) +{ + struct isp4vid_capture_buffer *vbuf, *node; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list) + vb2_buffer_done(&vbuf->vb2.vb2_buf, state); + INIT_LIST_HEAD(&isp_vdev->buf_list); + } + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state); +} + +static int isp4vid_vdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4vid_vdev_ent_ops = { + .link_validate = isp4vid_vdev_link_validate, +}; + +static const struct v4l2_file_operations isp4vid_vdev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int isp4vid_ioctl_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME); + cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + + dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name, + cap->capabilities); + + return 0; +} + +static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + f->fmt.pix = isp_vdev->format; + + return 0; +} + +static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt) +{ + int ret = 0; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt->bytesperline = fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + fmt->bytesperline = fmt->width * 2; + fmt->sizeimage = fmt->bytesperline * fmt->height; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct v4l2_frmsize_discrete *fsz; + size_t i; + + /* + * Check if the hardware supports the requested format, use the default + * format otherwise. + */ + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == format->pixelformat) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + format->pixelformat = ISP4VID_DEFAULT_FMT; + + switch (format->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: + fsz = v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, format->width, + format->height); + format->width = fsz->width; + format->height = fsz->height; + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, + format->pixelformat); + return -EINVAL; + } + + /* + * There is no need to check the return value, as failure will never + * happen here + */ + isp4vid_fill_buffer_size(format); + + if (format->field == V4L2_FIELD_ANY) + format->field = isp4vid_fmt_default.field; + + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = isp4vid_fmt_default.colorspace; + + return 0; +} + +static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev, + struct v4l2_pix_format *pix_fmt) +{ + struct v4l2_subdev_format fmt = {}; + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8; + break; + case V4L2_PIX_FMT_YUYV: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16; + break; + default: + return -EINVAL; + } + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT; + fmt.format.width = pix_fmt->width; + fmt.format.height = pix_fmt->height; + return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt); +} + +static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&isp_vdev->vbq)) + return -EBUSY; + + ret = isp4vid_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n", + isp_vdev->vdev.name, + isp_vdev->format.width, isp_vdev->format.height, + f->fmt.pix.width, f->fmt.pix.height); + dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat, + f->fmt.pix.pixelformat); + dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.bytesperline, + f->fmt.pix.bytesperline); + dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.sizeimage, + f->fmt.pix.sizeimage); + + isp_vdev->format = f->fmt.pix; + ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format); + + return ret; +} + +static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n", + isp_vdev->vdev.name, f->index, f->pixelformat); + + return 0; +} + +static int isp4vid_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) { + if (isp4vid_formats[i] == fsize->pixel_format) + break; + } + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = isp4vid_frmsize[fsize->index]; + dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n", + isp_vdev->vdev.name, fsize->index, + fsize->discrete.width, fsize->discrete.height); + } else { + return -EINVAL; + } + + return 0; +} + +static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + size_t i; + + if (fival->index >= ARRAY_SIZE(isp4vid_tpfs)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == fival->pixel_format) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++) + if (isp4vid_frmsize[i].width == fival->width && + isp4vid_frmsize[i].height == fival->height) + break; + + if (i == ARRAY_SIZE(isp4vid_frmsize)) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = isp4vid_tpfs[fival->index]; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n", + isp_vdev->vdev.name, fival->index, + fival->discrete.numerator, + fival->discrete.denominator); + + return 0; +} + +static int isp4vid_ioctl_g_param(struct file *file, void *priv, + struct v4l2_streamparm *param) +{ + struct v4l2_captureparm *capture = ¶m->parm.capture; + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + capture->capability = V4L2_CAP_TIMEPERFRAME; + capture->timeperframe = isp_vdev->timeperframe; + capture->readbuffers = 0; + + dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name, + capture->timeperframe.numerator, + capture->timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = { + .vidioc_querycap = isp4vid_ioctl_querycap, + .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_parm = isp4vid_ioctl_g_param, + .vidioc_s_parm = isp4vid_ioctl_g_param, + .vidioc_enum_framesizes = isp4vid_enum_framesizes, + .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals, +}; + +static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt) +{ + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + return fmt->width * fmt->height * 3 / 2; + case V4L2_PIX_FMT_YUYV: + return fmt->width * fmt->height * 2; + default: + return 0; + } +} + +static int isp4vid_qops_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); + + if (*nplanes > 1) { + dev_err(isp_vdev->dev, + "fail to setup queue, no mplane supported %u\n", + *nplanes); + return -EINVAL; + } + + if (*nplanes == 1) { + unsigned int size; + + size = isp4vid_get_image_size(&isp_vdev->format); + if (sizes[0] < size) { + dev_err(isp_vdev->dev, + "fail for small plane size %u, %u expected\n", + sizes[0], size); + return -EINVAL; + } + } + + if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT) + *nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: { + *nplanes = 1; + sizes[0] = max(sizes[0], isp_vdev->format.sizeimage); + isp_vdev->format.sizeimage = sizes[0]; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n", + isp_vdev->vdev.name, + *nbuffers, *nplanes, sizes[0]); + + return 0; +} + +static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4if_img_buf_info *img_buf = &buf->img_buf; + void *vaddr = vb2_plane_vaddr(vb, 0); + + dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n", + vaddr, buf->gpu_addr, vb->planes[0].length); + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: { + u32 y_size = isp_vdev->format.sizeimage / 3 * 2; + u32 uv_size = isp_vdev->format.sizeimage / 3; + + img_buf->planes[0].len = y_size; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = uv_size; + img_buf->planes[1].sys_addr = vaddr + y_size; + img_buf->planes[1].mc_addr = buf->gpu_addr + y_size; + + dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n", + img_buf->planes[1].mc_addr, + img_buf->planes[1].len); + + img_buf->planes[2].len = 0; + } + break; + case V4L2_PIX_FMT_YUYV: { + img_buf->planes[0].len = isp_vdev->format.sizeimage; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = 0; + img_buf->planes[2].len = 0; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return; + } + + if (isp_vdev->stream_started) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf); + + scoped_guard(mutex, &isp_vdev->buf_list_lock) + list_add_tail(&buf->list, &isp_vdev->buf_list); +} + +static int isp4vid_qops_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct isp4vid_capture_buffer *isp4vid_buf; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret = 0; + + isp_vdev->sequence = 0; + + ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev); + if (ret) { + dev_err(isp_vdev->dev, "power up isp fail %d\n", ret); + goto release_buffers; + } + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + goto release_buffers; + } + } + + list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, + &isp4vid_buf->img_buf); + + isp_vdev->stream_started = true; + + return 0; + +release_buffers: + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void isp4vid_qops_stop_streaming(struct vb2_queue *vq) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + entity = &isp_vdev->vdev.entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n", + subdev->name, ret); + } + + isp_vdev->stream_started = false; + isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev); + + /* Release all active buffers */ + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR); +} + +static int isp4vid_qops_buf_init(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + void *mem_priv = vb->planes[0].mem_priv; + struct device *dev = isp_vdev->dev; + u64 gpu_addr; + void *bo; + int ret; + + if (vb->planes[0].dbuf) { + buf->dbuf = vb->planes[0].dbuf; + } else { + /* + * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always + * defined when this driver is compiled. The #else branch is + * kept as a safeguard in case the dependency is ever removed. + */ +#ifdef CONFIG_HAS_DMA + buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0); + if (IS_ERR_OR_NULL(buf->dbuf)) { + dev_err(dev, "fail to get dma buf\n"); + return -EINVAL; + } +#else + dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n"); + buf->dbuf = NULL; + return -EINVAL; +#endif + } + + /* create isp user BO and obtain gpu_addr */ + ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr); + if (ret) { + dev_err(dev, "fail to create isp user BO\n"); + if (!vb->planes[0].dbuf) { + dma_buf_put(buf->dbuf); + buf->dbuf = NULL; + } + + return ret; + } + + buf->bo = bo; + buf->gpu_addr = gpu_addr; + return 0; +} + +static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + + if (buf->bo) { + isp_user_buffer_free(buf->bo); + buf->bo = NULL; + } + + /* + * Only put dmabufs we obtained ourselves via get_dmabuf, not ones + * provided by the framework for DMABUF import + */ + if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf) + dma_buf_put(buf->dbuf); + + buf->dbuf = NULL; +} + +static const struct vb2_ops isp4vid_qops = { + .queue_setup = isp4vid_qops_queue_setup, + .buf_init = isp4vid_qops_buf_init, + .buf_cleanup = isp4vid_qops_buf_cleanup, + .start_streaming = isp4vid_qops_start_streaming, + .stop_streaming = isp4vid_qops_stop_streaming, + .buf_queue = isp4vid_qops_buffer_queue, +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd) +{ + const char *vdev_name = isp4vid_video_dev_name; + struct v4l2_device *v4l2_dev; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev) + return -EINVAL; + + v4l2_dev = isp_sd->v4l2_dev; + vdev = &isp_vdev->vdev; + + isp_vdev->isp_sdev = isp_sd; + isp_vdev->dev = v4l2_dev->dev; + + /* Initialize the vb2_queue struct */ + mutex_init(&isp_vdev->vbq_lock); + q = &isp_vdev->vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp4vid_capture_buffer); + q->min_queued_buffers = 2; + q->ops = &isp4vid_qops; + q->drv_priv = isp_vdev; + q->mem_ops = &vb2_vmalloc_memops; + q->lock = &isp_vdev->vbq_lock; + q->dev = v4l2_dev->dev; + ret = vb2_queue_init(q); + if (ret) { + dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret); + return ret; + } + + /* Initialize buffer list and its lock */ + mutex_init(&isp_vdev->buf_list_lock); + INIT_LIST_HEAD(&isp_vdev->buf_list); + + /* Set default frame format */ + isp_vdev->format = isp4vid_fmt_default; + isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT; + v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator, + &isp_vdev->timeperframe.denominator, 8, 333); + + ret = isp4vid_fill_buffer_size(&isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret); + goto err_release_vb2_queue; + } + + ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail init format :%d\n", ret); + goto err_release_vb2_queue; + } + + /* Initialize the video_device struct */ + isp_vdev->vdev.entity.name = vdev_name; + isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1, + &isp_vdev->vdev_pad); + + if (ret) { + dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret); + goto err_release_vb2_queue; + } + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &isp4vid_vdev_ent_ops; + vdev->release = video_device_release_empty; + vdev->fops = &isp4vid_vdev_fops; + vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops; + vdev->lock = NULL; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + video_set_drvdata(vdev, isp_vdev); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&isp_vdev->vdev.entity); +err_release_vb2_queue: + vb2_queue_release(q); + return ret; +} + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev) +{ + vb2_video_unregister_device(&isp_vdev->vdev); +} diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h new file mode 100644 index 000000000000..c66451e26166 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_VIDEO_H_ +#define _ISP4_VIDEO_H_ + +#include +#include + +#include "isp4_interface.h" + +struct isp4vid_capture_buffer { + /* + * struct vb2_v4l2_buffer must be the first element + * the videobuf2 framework will allocate this struct based on + * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of + * memory as a vb2_buffer + */ + struct vb2_v4l2_buffer vb2; + struct isp4if_img_buf_info img_buf; + struct list_head list; + struct dma_buf *dbuf; + void *bo; + u64 gpu_addr; +}; + +struct isp4vid_dev { + struct video_device vdev; + struct media_pad vdev_pad; + struct v4l2_pix_format format; + + /* mutex that protects vbq */ + struct mutex vbq_lock; + struct vb2_queue vbq; + + /* mutex that protects buf_list */ + struct mutex buf_list_lock; + struct list_head buf_list; + + u32 sequence; + bool stream_started; + + struct device *dev; + struct v4l2_subdev *isp_sdev; + struct v4l2_fract timeperframe; +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd); + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev); + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf); + +#endif /* _ISP4_VIDEO_H_ */ From ec4bec227b9d3796cb78af0d3d9bcdd26f0769c5 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:48 +0800 Subject: [PATCH 0542/5214] media: platform: amd: isp4 debug fs logging and more descriptive errors Add debug fs for isp4 driver and add more detailed descriptive error info to some of the log message Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 4 + drivers/media/platform/amd/isp4/isp4_debug.c | 271 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_debug.h | 41 +++ .../media/platform/amd/isp4/isp4_interface.c | 25 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 29 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 5 + 8 files changed, 360 insertions(+), 20 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.c create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.h diff --git a/MAINTAINERS b/MAINTAINERS index 2dc09b84a188..b92e9625d84c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,6 +1164,8 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_debug.c +F: drivers/media/platform/amd/isp4/isp4_debug.h F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 3fa0ee6d8a96..3849062e17f3 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ + isp4_debug.o \ isp4_interface.o \ isp4_subdev.o \ - isp4_video.o \ No newline at end of file + isp4_video.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 9480dfffbcb2..bf6b8e26c2c0 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -9,6 +9,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" @@ -191,6 +192,7 @@ static int isp4_capture_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, isp_dev); + isp_debugfs_create(isp_dev); return 0; @@ -210,6 +212,8 @@ static void isp4_capture_remove(struct platform_device *pdev) struct isp4_device *isp_dev = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + isp_debugfs_remove(isp_dev); + media_device_unregister(&isp_dev->mdev); isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c new file mode 100644 index 000000000000..2fc00fc9a194 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) +#define ISP4DBG_MACRO_2_STR(X) #X +#define ISP4DBG_ONE_TIME_LOG_LEN 510 + +#ifdef CONFIG_DEBUG_FS + +void isp_debugfs_create(struct isp4_device *isp_dev) +{ + isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL); + debugfs_create_bool("fw_log_enable", 0644, + isp_dev->isp_subdev.debugfs_dir, + &isp_dev->isp_subdev.enable_fw_log); + isp_dev->isp_subdev.fw_log_output = + devm_kzalloc(isp_dev->isp_subdev.dev, + ISP4DBG_FW_LOG_RINGBUF_SIZE + 32, + GFP_KERNEL); +} + +void isp_debugfs_remove(struct isp4_device *isp_dev) +{ + debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir); + isp_dev->isp_subdev.debugfs_dir = NULL; +} + +static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size) +{ + struct isp4_interface *ispif = &isp->ispif; + char *buf = isp->fw_log_output; + struct device *dev = isp->dev; + u32 rd_ptr, wr_ptr; + u32 total_cnt = 0; + u32 offset = 0; + u32 cnt; + + if (!sys || !rb_size) + return 0; + + guard(mutex)(&ispif->isp4if_mutex); + + rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0); + wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0); + + do { + if (wr_ptr > rd_ptr) + cnt = wr_ptr - rd_ptr; + else if (wr_ptr < rd_ptr) + cnt = rb_size - rd_ptr; + else + goto quit; + + if (cnt > rb_size) { + dev_err(dev, "fail bad fw log size %u\n", cnt); + goto quit; + } + + memcpy(buf + offset, sys + rd_ptr, cnt); + + offset += cnt; + total_cnt += cnt; + rd_ptr = (rd_ptr + cnt) % rb_size; + } while (rd_ptr < wr_ptr); + + isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr); + +quit: + return total_cnt; +} + +void isp_fw_log_print(struct isp4_subdev *isp) +{ + struct isp4_interface *ispif = &isp->ispif; + char *fw_log_buf = isp->fw_log_output; + u32 cnt; + + if (!isp->enable_fw_log || !fw_log_buf) + return; + + cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr, + ispif->fw_log_buf->mem_size); + + if (cnt) { + char temp_ch; + char *str; + char *end; + /* line end */ + char *le; + + str = (char *)fw_log_buf; + end = ((char *)fw_log_buf + cnt); + fw_log_buf[cnt] = 0; + + while (str < end) { + le = strchr(str, 0x0A); + if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) || + (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) { + if (le) + *le = 0; + + if (*str != '\0') + dev_dbg(isp->dev, "%s", str); + + if (le) { + *le = 0x0A; + str = le + 1; + } else { + break; + } + } else { + u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN; + + temp_ch = str[tmp_len]; + str[tmp_len] = 0; + dev_dbg(isp->dev, "%s", str); + str[tmp_len] = temp_ch; + str = &str[tmp_len]; + } + } + } +} +#endif + +char *isp4dbg_get_buf_src_str(u32 src) +{ + switch (src) { + case ISP4FW_BUFFER_SOURCE_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM); + default: + return "Unknown buf source"; + } +} + +char *isp4dbg_get_buf_done_str(u32 status) +{ + switch (status) { + case ISP4FW_BUFFER_STATUS_INVALID: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID); + case ISP4FW_BUFFER_STATUS_SKIPPED: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED); + case ISP4FW_BUFFER_STATUS_EXIST: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST); + case ISP4FW_BUFFER_STATUS_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE); + case ISP4FW_BUFFER_STATUS_LACK: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK); + case ISP4FW_BUFFER_STATUS_DIRTY: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY); + case ISP4FW_BUFFER_STATUS_MAX: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX); + default: + return "Unknown Buf Done Status"; + } +} + +char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */) +{ + switch (fmt) { + case ISP4FW_IMAGE_FORMAT_NV12: + return "NV12"; + case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED: + return "YUV422INTERLEAVED"; + default: + return "unknown fmt"; + } +} + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, + void *in, void *orig_buf) +{ + struct isp4fw_buffer_meta_info *p; + struct isp4if_img_buf_info *orig; + + if (!in) + return; + + if (!pre) + pre = ""; + + p = in; + orig = orig_buf; + + dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre, + isp4dbg_get_img_fmt_str(p->image_prop.image_format), + p->enabled, isp4dbg_get_buf_done_str(p->status), p->status, + isp4dbg_get_buf_src_str(p->source)); + + dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n", + orig->planes[0].sys_addr, orig->planes[0].mc_addr, + orig->planes[0].len, orig->planes[1].sys_addr, + orig->planes[1].mc_addr, orig->planes[1].len, + orig->planes[2].sys_addr, orig->planes[2].mc_addr, + orig->planes[2].len); +} + +char *isp4dbg_get_buf_type(u32 type) +{ + /* enum isp4fw_buffer_type */ + switch (type) { + case ISP4FW_BUFFER_TYPE_PREVIEW: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW); + case ISP4FW_BUFFER_TYPE_META_INFO: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO); + case ISP4FW_BUFFER_TYPE_MEM_POOL: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL); + default: + return "unknown type"; + } +} + +char *isp4dbg_get_cmd_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_CMD_ID_START_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM); + case ISP4FW_CMD_ID_STOP_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM); + case ISP4FW_CMD_ID_SEND_BUFFER: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER); + case ISP4FW_CMD_ID_SET_STREAM_CONFIG: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG); + case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP); + case ISP4FW_CMD_ID_ENABLE_OUT_CHAN: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN); + default: + return "unknown cmd"; + } +} + +char *isp4dbg_get_resp_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_RESP_ID_CMD_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE); + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE); + default: + return "unknown respid"; + } +} + +char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */) +{ + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + return "STREAM_GLOBAL"; + case ISP4IF_STREAM_ID_1: + return "STREAM1"; + default: + return "unknown streamID"; + } +} + +char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */) +{ + switch ((enum isp4fw_pipe_out_ch)ch) { + case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW: + return "prev"; + default: + return "unknown channel"; + } +} diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h new file mode 100644 index 000000000000..d1262e03ae64 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_DEBUG_H_ +#define _ISP4_DEBUG_H_ + +#include +#include + +#include "isp4_subdev.h" + +#ifdef CONFIG_DEBUG_FS +struct isp4_device; + +void isp_debugfs_create(struct isp4_device *isp_dev); +void isp_debugfs_remove(struct isp4_device *isp_dev); +void isp_fw_log_print(struct isp4_subdev *isp); + +#else + +/* to avoid checkpatch warning */ +#define isp_debugfs_create(cam) ((void)(cam)) +#define isp_debugfs_remove(cam) ((void)(cam)) +#define isp_fw_log_print(isp) ((void)(isp)) + +#endif /* CONFIG_DEBUG_FS */ + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p, + void *orig_buf /* struct sys_img_buf_handle */); +char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */); +char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */); +char *isp4dbg_get_cmd_str(u32 cmd); +char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */ +char *isp4dbg_get_resp_str(u32 resp); +char *isp4dbg_get_buf_src_str(u32 src); +char *isp4dbg_get_buf_done_str(u32 status); +char *isp4dbg_get_if_stream_str(u32 stream); + +#endif /* _ISP4_DEBUG_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index fc9ad7d91a7e..15f14eddd683 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -5,6 +5,7 @@ #include +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" @@ -302,8 +303,9 @@ static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, wr_ptr = isp4hw_rreg(ispif->mmio, wreg); if (rd_ptr >= len || wr_ptr >= len) { dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", - stream, rd_ptr, wr_ptr, len, cmd_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, + wr_ptr, len, cmd_sz); return -EINVAL; } @@ -394,8 +396,9 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, rb_config->reg_wptr); dev_err(dev, - "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", - stream, rd_ptr, wr_ptr); + "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, + rd_ptr, wr_ptr); ret = -ETIMEDOUT; goto free_ele; } @@ -421,8 +424,8 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); if (ret) { dev_err(dev, - "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", - cmd_id); + "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n", + isp4dbg_get_cmd_str(cmd_id), cmd_id); goto err_dequeue_ele; } } @@ -692,8 +695,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, if (checksum != resp->resp_check_sum) { dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", checksum, resp->resp_check_sum, rd_ptr, wr_ptr); - dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", - stream, resp->resp_seq_num, + dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", + isp4dbg_get_if_stream_str(stream), stream, + resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id), resp->resp_id); return -EINVAL; } @@ -702,8 +706,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, err_rb_invalid: dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", - stream, rd_ptr, wr_ptr, len, resp_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len, + resp_sz); return -EINVAL; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 5202232d50c5..48deea79ce6c 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -7,6 +7,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_interface.h" @@ -263,9 +264,9 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return -EINVAL; } - dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", - cmd_ch_prop.ch, - cmd_ch_prop.image_prop.image_format, + dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", + isp4dbg_get_out_ch_str(cmd_ch_prop.ch), + isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); @@ -294,7 +295,8 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return ret; } - dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + dev_dbg(dev, "enable channel %s\n", + isp4dbg_get_out_ch_str(cmd_ch_en.ch)); if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, @@ -381,8 +383,9 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; - dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, + isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); @@ -458,8 +461,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, return; } - dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); if (meta->preview.enabled && @@ -468,6 +472,8 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); if (prev) { + isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, + &prev->buf_info); isp4vid_handle_frame_done(&isp_subdev->isp_vdev, &prev->buf_info); isp4if_dealloc_buffer_node(prev); @@ -475,8 +481,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, dev_err(dev, "fail null prev buf\n"); } } else if (meta->preview.enabled) { - dev_err(dev, "fail bad preview status %u\n", - meta->preview.status); + dev_err(dev, "fail bad preview status %u(%s)\n", + meta->preview.status, + isp4dbg_get_buf_done_str(meta->preview.status)); } if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) @@ -493,6 +500,9 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, struct device *dev = isp_subdev->dev; struct isp4fw_resp resp; + if (stream_id == ISP4IF_STREAM_ID_1) + isp_fw_log_print(isp_subdev); + while (true) { if (isp4if_f2h_resp(ispif, stream_id, &resp)) { /* Re-enable the interrupt */ @@ -522,7 +532,8 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, &resp.param.frame_done); break; default: - dev_err(dev, "-><- fail respid (0x%x)\n", + dev_err(dev, "-><- fail respid %s(0x%x)\n", + isp4dbg_get_resp_str(resp.resp_id), resp.resp_id); break; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ddf6bdf4a62a..20ea08a830af 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -109,6 +109,11 @@ struct isp4_subdev { bool irq_enabled; /* spin lock to access ISP_SYS_INT0_EN exclusively */ spinlock_t irq_lock; +#ifdef CONFIG_DEBUG_FS + bool enable_fw_log; + struct dentry *debugfs_dir; + char *fw_log_output; +#endif }; int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, From 3cd9b7011519c3fffffb7b6752fc7603be52dc1d Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:49 +0800 Subject: [PATCH 0543/5214] Documentation: add documentation of AMD isp 4 driver Add documentation for AMD ISP 4 and describe the main components Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- Documentation/admin-guide/media/amdisp4-1.rst | 63 +++++++++++++++++++ Documentation/admin-guide/media/amdisp4.dot | 6 ++ .../admin-guide/media/v4l-drivers.rst | 1 + MAINTAINERS | 2 + 4 files changed, 72 insertions(+) create mode 100644 Documentation/admin-guide/media/amdisp4-1.rst create mode 100644 Documentation/admin-guide/media/amdisp4.dot diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst new file mode 100644 index 000000000000..878141154f96 --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4-1.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +==================================== +AMD Image Signal Processor (amdisp4) +==================================== + +Introduction +============ + +This file documents the driver for the AMD ISP4 that is part of +AMD Ryzen AI Max 300 Series. + +The driver is located under drivers/media/platform/amd/isp4 and uses +the Media-Controller API. + +The driver exposes one video capture device to userspace and provide +web camera like interface. Internally the video device is connected +to the isp4 sub-device responsible for communication with the CCPU FW. + +Topology +======== + +.. _amdisp4_topology_graph: + +.. kernel-figure:: amdisp4.dot + :alt: Diagram of the media pipeline topology + :align: center + + + +The driver has 1 sub-device: Representing isp4 image signal processor. +The driver has 1 video device: Capture device for retrieving images. + +- ISP4 Image Signal Processing Subdevice Node + +--------------------------------------------- + +The isp4 is represented as a single V4L2 subdev, the sub-device does not +provide interface to the user space. The sub-device is connected to one video node +(isp4_capture) with immutable active link. The sub-device represents ISP with +connected sensor similar to smart cameras (sensors with integrated ISP). +sub-device has only one link to the video device for capturing the frames. +The sub-device communicates with CCPU FW for streaming configuration and +buffer management. + + +- isp4_capture - Frames Capture Video Node + +------------------------------------------ + +Isp4_capture is a capture device to capture frames to memory. +The entity is connected to isp4 sub-device. The video device +provides web camera like interface to userspace. It supports +mmap and dma buf types of memory. + +Capturing Video Frames Example +============================== + +.. code-block:: bash + + v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10" diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot new file mode 100644 index 000000000000..978f30c1a31a --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4.dot @@ -0,0 +1,6 @@ +digraph board { + rankdir=TB + n00000001 [label="{{} | amd isp4\n | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port0 -> n00000003 [style=bold] + n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index d31da8e0a54f..4621eae9fa1e 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation .. toctree:: :maxdepth: 2 + amdisp4-1 bttv c3-isp cafe_ccic diff --git a/MAINTAINERS b/MAINTAINERS index b92e9625d84c..81d53481d3f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1158,6 +1158,8 @@ M: Nirujogi Pratap L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git +F: Documentation/admin-guide/media/amdisp4-1.rst +F: Documentation/admin-guide/media/amdisp4.dot F: drivers/media/platform/amd/Kconfig F: drivers/media/platform/amd/Makefile F: drivers/media/platform/amd/isp4/Kconfig From 8d846a0a6f95cd717df892b077a1c990a86b29fa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:47:24 +0200 Subject: [PATCH 0544/5214] PCI: amd-mdb: Use the right GPIO header The driver includes the legacy GPIO header but does not use any symbols from it and actually wants , so fix this up. Signed-off-by: Andy Shevchenko Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260506084858.867884-2-andriy.shevchenko@linux.intel.com --- drivers/pci/controller/dwc/pcie-amd-mdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 7e50e11fbffd..88208e967201 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include From 800c861cfcbedd033bdffa65c00188fefdcf0767 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:47:25 +0200 Subject: [PATCH 0545/5214] PCI: designware-plat: Drop unused include This file does not use the symbols from the legacy header, so drop it. Signed-off-by: Andy Shevchenko Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260506084858.867884-3-andriy.shevchenko@linux.intel.com --- drivers/pci/controller/dwc/pcie-designware-plat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index d103ab759c4e..7d896752d43c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -8,7 +8,6 @@ */ #include #include -#include #include #include #include From 71d007f6fb17f519c2fe51d35e0bee5f602169e6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:47:26 +0200 Subject: [PATCH 0546/5214] PCI: fu740: Drop unused include This file does not use the symbols from the legacy header, so drop it. Signed-off-by: Andy Shevchenko Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260506084858.867884-4-andriy.shevchenko@linux.intel.com --- drivers/pci/controller/dwc/pcie-fu740.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c index 66367252032b..d0a34f680397 100644 --- a/drivers/pci/controller/dwc/pcie-fu740.c +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include From 7cc21269ef74269e423d64defc940808ccbf9774 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:47:27 +0200 Subject: [PATCH 0547/5214] PCI: visconti: Drop unused include This file does not use the symbols from the legacy header, so drop it. Signed-off-by: Andy Shevchenko Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260506084858.867884-5-andriy.shevchenko@linux.intel.com --- drivers/pci/controller/dwc/pcie-visconti.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-visconti.c b/drivers/pci/controller/dwc/pcie-visconti.c index cdeac6177143..f21775c3b3a1 100644 --- a/drivers/pci/controller/dwc/pcie-visconti.c +++ b/drivers/pci/controller/dwc/pcie-visconti.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include From 9c353c31b813c0d19b969d28545fd5bc008a9ed3 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Wed, 29 Apr 2026 11:45:45 +0530 Subject: [PATCH 0548/5214] pinctrl: qcom: Remove unused macro definitions Remove SDC_QDSD_PINGROUP, QUP_I3C and UFS_RESET macros as on some platforms they are unused. No functional impact. Reviewed-by: Dmitry Baryshkov Signed-off-by: Maulik Shah Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-eliza.c | 24 ------------------------ drivers/pinctrl/qcom/pinctrl-qcm2290.c | 23 ----------------------- drivers/pinctrl/qcom/pinctrl-qdu1000.c | 6 ------ drivers/pinctrl/qcom/pinctrl-sm4450.c | 7 ------- 4 files changed, 60 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index 40e263e35b45..de9783ab509f 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -54,30 +54,6 @@ .intr_detection_width = 2, \ } -#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ - { \ - .grp = PINCTRL_PINGROUP(#pg_name, \ - pg_name##_pins, \ - ARRAY_SIZE(pg_name##_pins)), \ - .ctl_reg = ctl, \ - .io_reg = 0, \ - .intr_cfg_reg = 0, \ - .intr_status_reg = 0, \ - .mux_bit = -1, \ - .pull_bit = pull, \ - .drv_bit = drv, \ - .oe_bit = -1, \ - .in_bit = -1, \ - .out_bit = -1, \ - .intr_enable_bit = -1, \ - .intr_status_bit = -1, \ - .intr_target_bit = -1, \ - .intr_raw_status_bit = -1, \ - .intr_polarity_bit = -1, \ - .intr_detection_bit = -1, \ - .intr_detection_width = -1, \ - } - #define UFS_RESET(pg_name, ctl, io) \ { \ .grp = PINCTRL_PINGROUP(#pg_name, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index 3b28ac498885..844d3dc9e72c 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -75,29 +75,6 @@ .intr_detection_width = -1, \ } -#define UFS_RESET(pg_name, offset) \ - { \ - .grp = PINCTRL_PINGROUP(#pg_name, \ - pg_name##_pins, \ - ARRAY_SIZE(pg_name##_pins)), \ - .ctl_reg = offset, \ - .io_reg = offset + 0x4, \ - .intr_cfg_reg = 0, \ - .intr_status_reg = 0, \ - .mux_bit = -1, \ - .pull_bit = 3, \ - .drv_bit = 0, \ - .oe_bit = -1, \ - .in_bit = -1, \ - .out_bit = 0, \ - .intr_enable_bit = -1, \ - .intr_status_bit = -1, \ - .intr_target_bit = -1, \ - .intr_raw_status_bit = -1, \ - .intr_polarity_bit = -1, \ - .intr_detection_bit = -1, \ - .intr_detection_width = -1, \ - } static const struct pinctrl_pin_desc qcm2290_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), diff --git a/drivers/pinctrl/qcom/pinctrl-qdu1000.c b/drivers/pinctrl/qcom/pinctrl-qdu1000.c index 5125df7eb127..77da87aa72aa 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdu1000.c +++ b/drivers/pinctrl/qcom/pinctrl-qdu1000.c @@ -99,12 +99,6 @@ .intr_detection_width = -1, \ } -#define QUP_I3C(qup_mode, qup_offset) \ - { \ - .mode = qup_mode, \ - .offset = qup_offset, \ - } - static const struct pinctrl_pin_desc qdu1000_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm4450.c b/drivers/pinctrl/qcom/pinctrl-sm4450.c index 83650f173b01..51a66a20dc66 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm4450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm4450.c @@ -99,13 +99,6 @@ .intr_detection_width = -1, \ } -#define QUP_I3C(qup_mode, qup_offset) \ - { \ - .mode = qup_mode, \ - .offset = qup_offset, \ - } - - static const struct pinctrl_pin_desc sm4450_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), From 8e9121cc761db8837352e9f5651402d04795bf59 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Thu, 30 Apr 2026 09:10:41 +0200 Subject: [PATCH 0549/5214] dt-bindings: pinctrl: qcom: Add SM6350 LPI pinctrl Add bindings for pin controller in Low Power Audio SubSystem (LPASS). Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- .../qcom,sm6350-lpass-lpi-pinctrl.yaml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,sm6350-lpass-lpi-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm6350-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm6350-lpass-lpi-pinctrl.yaml new file mode 100644 index 000000000000..4903b2d37d89 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm6350-lpass-lpi-pinctrl.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,sm6350-lpass-lpi-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM6350 SoC LPASS LPI TLMM + +maintainers: + - Luca Weiss + +description: + Top Level Mode Multiplexer pin controller in the Low Power Audio SubSystem + (LPASS) Low Power Island (LPI) of Qualcomm SM6350 SoC. + +properties: + compatible: + const: qcom,sm6350-lpass-lpi-pinctrl + + reg: + items: + - description: LPASS LPI TLMM Control and Status registers + - description: LPASS LPI MCC registers + + clocks: + items: + - description: LPASS Core voting clock + - description: LPASS Audio voting clock + + clock-names: + items: + - const: core + - const: audio + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-sm6350-lpass-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-sm6350-lpass-state" + additionalProperties: false + +$defs: + qcom-sm6350-lpass-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,lpass-lpi-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|1[0-4])$" + + function: + enum: [ dmic1_clk, dmic1_data, dmic2_clk, dmic2_data, dmic3_clk, + dmic3_data, gpio, i2s1_clk, i2s1_data, i2s1_ws, i2s2_clk, + i2s2_data, i2s2_ws, qua_mi2s_data, qua_mi2s_sclk, qua_mi2s_ws, + swr_rx_clk, swr_rx_data, swr_tx_clk, swr_tx_data, wsa_swr_clk, + wsa_swr_data ] + description: + Specify the alternative function to be configured for the specified + pins. + +allOf: + - $ref: qcom,lpass-lpi-common.yaml# + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + + lpass_tlmm: pinctrl@33c0000 { + compatible = "qcom,sm6350-lpass-lpi-pinctrl"; + reg = <0x033c0000 0x20000>, + <0x03550000 0x10000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpass_tlmm 0 0 15>; + + clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", + "audio"; + + i2s1_active: i2s1-active-state { + clk-pins { + pins = "gpio6"; + function = "i2s1_clk"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + ws-pins { + pins = "gpio7"; + function = "i2s1_ws"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + data-pins { + pins = "gpio8", "gpio9"; + function = "i2s1_data"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + }; From 8e60ee5f94f0437af418c696a543c184d3ffc11d Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Thu, 30 Apr 2026 09:10:42 +0200 Subject: [PATCH 0550/5214] pinctrl: qcom: lpass-lpi: Add ability to use SPARE_1 for slew control On some platforms like SM6350 (Bitra), some pins have their slew controlled with the SPARE_1 register. Add support for that. Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 2 ++ drivers/pinctrl/qcom/pinctrl-lpass-lpi.h | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 76aed3296279..15ced5027579 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -220,6 +220,8 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, if (pctrl->data->flags & LPI_FLAG_SLEW_RATE_SAME_REG) reg = pctrl->tlmm_base + LPI_TLMM_REG_OFFSET * group + LPI_GPIO_CFG_REG; + else if (g->slew_base_spare_1) + reg = pctrl->slew_base + LPI_SPARE_1_REG; else reg = pctrl->slew_base + LPI_SLEW_RATE_CTL_REG; diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h index f48368492861..6ba0c4eba984 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h @@ -16,6 +16,7 @@ struct platform_device; struct pinctrl_pin_desc; #define LPI_SLEW_RATE_CTL_REG 0xa000 +#define LPI_SPARE_1_REG 0xc000 #define LPI_TLMM_REG_OFFSET 0x1000 #define LPI_SLEW_RATE_MAX 0x03 #define LPI_SLEW_BITS_SIZE 0x02 @@ -47,6 +48,7 @@ struct pinctrl_pin_desc; { \ .pin = id, \ .slew_offset = soff, \ + .slew_base_spare_1 = false, \ .funcs = (int[]){ \ LPI_MUX_gpio, \ LPI_MUX_##f1, \ @@ -62,6 +64,7 @@ struct pinctrl_pin_desc; { \ .pin = id, \ .slew_offset = soff, \ + .slew_base_spare_1 = false, \ .funcs = (int[]){ \ LPI_MUX_gpio, \ LPI_MUX_##f1, \ @@ -73,6 +76,22 @@ struct pinctrl_pin_desc; .pin_offset = poff, \ } +#define LPI_PINGROUP_SLEW_SPARE_1(id, soff, f1, f2, f3, f4) \ + { \ + .pin = id, \ + .slew_offset = soff, \ + .slew_base_spare_1 = true, \ + .funcs = (int[]){ \ + LPI_MUX_gpio, \ + LPI_MUX_##f1, \ + LPI_MUX_##f2, \ + LPI_MUX_##f3, \ + LPI_MUX_##f4, \ + }, \ + .nfuncs = 5, \ + .pin_offset = 0, \ + } + /* * Slew rate control is done in the same register as rest of the * pin configuration. @@ -87,6 +106,7 @@ struct lpi_pingroup { unsigned int *funcs; unsigned int nfuncs; unsigned int pin_offset; + bool slew_base_spare_1; }; struct lpi_function { From 6ec81ace110ca2584d28863ffafe93a8ad6b7628 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Thu, 30 Apr 2026 09:10:43 +0200 Subject: [PATCH 0551/5214] pinctrl: qcom: Add SM6350 LPASS LPI TLMM Add support for the pin controller block on SM6350 Low Power Island. Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss [linusw@kernel.org: fixed up Kconfig entry] Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 9 ++ drivers/pinctrl/qcom/Makefile | 1 + .../pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c | 149 ++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 3accf0b489bb..5452b128c2e5 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -118,6 +118,15 @@ config PINCTRL_SM6115_LPASS_LPI Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI (Low Power Island) found on the Qualcomm Technologies Inc SM6115 platform. +config PINCTRL_SM6350_LPASS_LPI + tristate "Qualcomm SM6350 LPASS LPI pin controller driver" + depends on ARM64 || COMPILE_TEST + depends on PINCTRL_LPASS_LPI + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI + (Low Power Island) found on the Qualcomm Technologies Inc SM6350 platform. + config PINCTRL_SM8250_LPASS_LPI tristate "Qualcomm SM8250 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 4262988c7d96..a1e9e2e292a0 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_PINCTRL_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_SM6115_LPASS_LPI) += pinctrl-sm6115-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM6125) += pinctrl-sm6125.o obj-$(CONFIG_PINCTRL_SM6350) += pinctrl-sm6350.o +obj-$(CONFIG_PINCTRL_SM6350_LPASS_LPI) += pinctrl-sm6350-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM6375) += pinctrl-sm6375.o obj-$(CONFIG_PINCTRL_SM7150) += pinctrl-sm7150.o obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o diff --git a/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c new file mode 100644 index 000000000000..4d06abcfedfd --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, Luca Weiss + */ + +#include +#include +#include + +#include "pinctrl-lpass-lpi.h" + +enum lpass_lpi_functions { + LPI_MUX_dmic1_clk, + LPI_MUX_dmic1_data, + LPI_MUX_dmic2_clk, + LPI_MUX_dmic2_data, + LPI_MUX_dmic3_clk, + LPI_MUX_dmic3_data, + LPI_MUX_i2s1_clk, + LPI_MUX_i2s1_data, + LPI_MUX_i2s1_ws, + LPI_MUX_i2s2_clk, + LPI_MUX_i2s2_data, + LPI_MUX_i2s2_ws, + LPI_MUX_qua_mi2s_data, + LPI_MUX_qua_mi2s_sclk, + LPI_MUX_qua_mi2s_ws, + LPI_MUX_swr_rx_clk, + LPI_MUX_swr_rx_data, + LPI_MUX_swr_tx_clk, + LPI_MUX_swr_tx_data, + LPI_MUX_wsa_swr_clk, + LPI_MUX_wsa_swr_data, + LPI_MUX_gpio, + LPI_MUX__, +}; + +static const struct pinctrl_pin_desc sm6350_lpi_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), +}; + +static const char * const swr_tx_clk_groups[] = { "gpio0" }; +static const char * const swr_tx_data_groups[] = { "gpio1", "gpio2", "gpio14" }; +static const char * const swr_rx_clk_groups[] = { "gpio3" }; +static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5" }; +static const char * const dmic1_clk_groups[] = { "gpio6" }; +static const char * const dmic1_data_groups[] = { "gpio7" }; +static const char * const dmic2_clk_groups[] = { "gpio8" }; +static const char * const dmic2_data_groups[] = { "gpio9" }; +static const char * const i2s2_clk_groups[] = { "gpio10" }; +static const char * const i2s2_ws_groups[] = { "gpio11" }; +static const char * const dmic3_clk_groups[] = { "gpio12" }; +static const char * const dmic3_data_groups[] = { "gpio13" }; +static const char * const qua_mi2s_sclk_groups[] = { "gpio0" }; +static const char * const qua_mi2s_ws_groups[] = { "gpio1" }; +static const char * const qua_mi2s_data_groups[] = { "gpio2", "gpio3", "gpio4", "gpio5" }; +static const char * const i2s1_clk_groups[] = { "gpio6" }; +static const char * const i2s1_ws_groups[] = { "gpio7" }; +static const char * const i2s1_data_groups[] = { "gpio8", "gpio9" }; +static const char * const wsa_swr_clk_groups[] = { "gpio10" }; +static const char * const wsa_swr_data_groups[] = { "gpio11" }; +static const char * const i2s2_data_groups[] = { "gpio12", "gpio13" }; + +static const struct lpi_pingroup sm6350_groups[] = { + LPI_PINGROUP(0, 0, swr_tx_clk, qua_mi2s_sclk, _, _), + LPI_PINGROUP(1, 2, swr_tx_data, qua_mi2s_ws, _, _), + LPI_PINGROUP(2, 4, swr_tx_data, qua_mi2s_data, _, _), + LPI_PINGROUP(3, 8, swr_rx_clk, qua_mi2s_data, _, _), + LPI_PINGROUP(4, 10, swr_rx_data, qua_mi2s_data, _, _), + LPI_PINGROUP(5, 12, swr_rx_data, _, qua_mi2s_data, _), + LPI_PINGROUP(6, LPI_NO_SLEW, dmic1_clk, i2s1_clk, _, _), + LPI_PINGROUP(7, LPI_NO_SLEW, dmic1_data, i2s1_ws, _, _), + LPI_PINGROUP(8, LPI_NO_SLEW, dmic2_clk, i2s1_data, _, _), + LPI_PINGROUP(9, LPI_NO_SLEW, dmic2_data, i2s1_data, _, _), + LPI_PINGROUP(10, 16, i2s2_clk, wsa_swr_clk, _, _), + LPI_PINGROUP(11, 18, i2s2_ws, wsa_swr_data, _, _), + LPI_PINGROUP(12, LPI_NO_SLEW, dmic3_clk, i2s2_data, _, _), + LPI_PINGROUP(13, LPI_NO_SLEW, dmic3_data, i2s2_data, _, _), + LPI_PINGROUP_SLEW_SPARE_1(14, 0, swr_tx_data, _, _, _), +}; + +static const struct lpi_function sm6350_functions[] = { + LPI_FUNCTION(dmic1_clk), + LPI_FUNCTION(dmic1_data), + LPI_FUNCTION(dmic2_clk), + LPI_FUNCTION(dmic2_data), + LPI_FUNCTION(dmic3_clk), + LPI_FUNCTION(dmic3_data), + LPI_FUNCTION(i2s1_clk), + LPI_FUNCTION(i2s1_data), + LPI_FUNCTION(i2s1_ws), + LPI_FUNCTION(i2s2_clk), + LPI_FUNCTION(i2s2_data), + LPI_FUNCTION(i2s2_ws), + LPI_FUNCTION(qua_mi2s_data), + LPI_FUNCTION(qua_mi2s_sclk), + LPI_FUNCTION(qua_mi2s_ws), + LPI_FUNCTION(swr_rx_clk), + LPI_FUNCTION(swr_rx_data), + LPI_FUNCTION(swr_tx_clk), + LPI_FUNCTION(swr_tx_data), + LPI_FUNCTION(wsa_swr_clk), + LPI_FUNCTION(wsa_swr_data), +}; + +static const struct lpi_pinctrl_variant_data sm6350_lpi_data = { + .pins = sm6350_lpi_pins, + .npins = ARRAY_SIZE(sm6350_lpi_pins), + .groups = sm6350_groups, + .ngroups = ARRAY_SIZE(sm6350_groups), + .functions = sm6350_functions, + .nfunctions = ARRAY_SIZE(sm6350_functions), +}; + +static const struct of_device_id lpi_pinctrl_of_match[] = { + { + .compatible = "qcom,sm6350-lpass-lpi-pinctrl", + .data = &sm6350_lpi_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); + +static struct platform_driver lpi_pinctrl_driver = { + .driver = { + .name = "qcom-sm6350-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove = lpi_pinctrl_remove, +}; + +module_platform_driver(lpi_pinctrl_driver); +MODULE_DESCRIPTION("Qualcomm SM6350 LPI GPIO pin control driver"); +MODULE_LICENSE("GPL"); From 39b059c18892f527f67b250a0a666391d8bc54ee Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:12 +0200 Subject: [PATCH 0552/5214] pinctrl: airoha: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 995ba6175c95..e5a6a60fb3c6 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -2811,7 +2811,7 @@ static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, unsigned int group, unsigned long *config) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); - u32 cur_config = 0; + unsigned long cur_config = 0; int i; for (i = 0; i < pinctrl->grps[group].npins; i++) { From 34460ff07b182586b5c465041e1f7cb3387a3de2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:13 +0200 Subject: [PATCH 0553/5214] pinctrl: equilibrium: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-equilibrium.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-equilibrium.c b/drivers/pinctrl/pinctrl-equilibrium.c index ba1c867b7b89..349eb944b4ac 100644 --- a/drivers/pinctrl/pinctrl-equilibrium.c +++ b/drivers/pinctrl/pinctrl-equilibrium.c @@ -532,8 +532,9 @@ static int eqbr_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, static int eqbr_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { - unsigned int i, npins, old = 0; const unsigned int *pins; + unsigned int i, npins; + unsigned long old = 0; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From 2892054decdf01be2be2e45e5b9570124d8418c6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:14 +0200 Subject: [PATCH 0554/5214] pinctrl: ingenic: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. While Ingenic is a 32-bit platform, it is still better to use the correct type, to serve as an example. Signed-off-by: Geert Uytterhoeven Acked-by: Paul Cercueil Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-ingenic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index c7f14546de05..29d7f4e54bc7 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -4334,7 +4334,8 @@ static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { const unsigned int *pins; - unsigned int i, npins, old = 0; + unsigned int i, npins; + unsigned long old = 0; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From 3ca083fb4a85e66bf2e6503d7667c7e90bb7137b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:15 +0200 Subject: [PATCH 0555/5214] pinctrl: mediatek: moore: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-moore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-moore.c b/drivers/pinctrl/mediatek/pinctrl-moore.c index 70f608347a5f..8039b6dc39cd 100644 --- a/drivers/pinctrl/mediatek/pinctrl-moore.c +++ b/drivers/pinctrl/mediatek/pinctrl-moore.c @@ -402,7 +402,8 @@ static int mtk_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { const unsigned int *pins; - unsigned int i, npins, old = 0; + unsigned int i, npins; + unsigned long old = 0; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From 13fffbc21e3019ff69bed077dc68bd6a614cf3c5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:18 +0200 Subject: [PATCH 0556/5214] pinctrl: single: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-single.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 7e022a46ee42..4d5f85b7e6bb 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -619,8 +619,9 @@ static int pcs_pinconf_set(struct pinctrl_dev *pctldev, static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config) { + unsigned long old = 0; const unsigned *pins; - unsigned npins, old = 0; + unsigned npins; int i, ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From 041e5ea2032d4e1b456e4df55e6e95587692f892 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:04:09 -0700 Subject: [PATCH 0557/5214] pinctrl: sophgo: allocate power_cfg with priv Use a flexible array member to combine allocations and simplify code slightly. Signed-off-by: Rosen Penev [linusw@kernel.org: add a counter and __counted_by() annotation] Signed-off-by: Linus Walleij --- drivers/pinctrl/sophgo/pinctrl-cv18xx.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/pinctrl/sophgo/pinctrl-cv18xx.c b/drivers/pinctrl/sophgo/pinctrl-cv18xx.c index c3a2dcf71f2a..59318a42690f 100644 --- a/drivers/pinctrl/sophgo/pinctrl-cv18xx.c +++ b/drivers/pinctrl/sophgo/pinctrl-cv18xx.c @@ -27,8 +27,9 @@ #include "pinctrl-cv18xx.h" struct cv1800_priv { - u32 *power_cfg; - void __iomem *regs[2]; + void __iomem *regs[2]; + unsigned int num_power_cfg; + u32 power_cfg[] __counted_by(num_power_cfg); }; static unsigned int cv1800_dt_get_pin_mux(u32 value) @@ -417,14 +418,11 @@ static int cv1800_pinctrl_init(struct platform_device *pdev, const struct sophgo_pinctrl_data *pctrl_data = pctrl->data; struct cv1800_priv *priv; - priv = devm_kzalloc(&pdev->dev, sizeof(struct cv1800_priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, power_cfg, pctrl_data->npds), + GFP_KERNEL); if (!priv) return -ENOMEM; - - priv->power_cfg = devm_kcalloc(&pdev->dev, pctrl_data->npds, - sizeof(u32), GFP_KERNEL); - if (!priv->power_cfg) - return -ENOMEM; + priv->num_power_cfg = pctrl_data->npds; priv->regs[0] = devm_platform_ioremap_resource_byname(pdev, "sys"); if (IS_ERR(priv->regs[0])) From 02622db8683bd3e5581ebb8991a6ed53a459e9f1 Mon Sep 17 00:00:00 2001 From: Swati Agarwal Date: Mon, 4 May 2026 12:19:36 +0530 Subject: [PATCH 0558/5214] dt-bindings: pinctrl: qcom: move gpio-hog schema to tlmm-common Qualcomm TLMM-based pin controllers share the same gpio-hog binding semantics across multiple SoCs. The gpio-hog pattern currently defined in qcom,ipq4019-pinctrl.yaml and qcom,sdm845-pinctrl.yaml are not SOC specific and applies to all TLMM controllers. Move the gpio-hog patternProperties definition to qcom,tlmm-common.yaml so that it can be reused by other Qualcomm TLMM pinctrl bindings and avoid schema duplication. Signed-off-by: Swati Agarwal Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/qcom,ipq4019-pinctrl.yaml | 5 ----- .../devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml | 5 ----- .../devicetree/bindings/pinctrl/qcom,tlmm-common.yaml | 6 ++++++ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq4019-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq4019-pinctrl.yaml index cc5de9f77680..de9a3e67e1bb 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq4019-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq4019-pinctrl.yaml @@ -36,11 +36,6 @@ patternProperties: $ref: "#/$defs/qcom-ipq4019-tlmm-state" additionalProperties: false - "-hog(-[0-9]+)?$": - type: object - required: - - gpio-hog - $defs: qcom-ipq4019-tlmm-state: type: object diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml index 4fcac2e55b55..3b33daedc018 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml @@ -42,11 +42,6 @@ patternProperties: $ref: "#/$defs/qcom-sdm845-tlmm-state" additionalProperties: false - "-hog(-[0-9]+)?$": - type: object - required: - - gpio-hog - $defs: qcom-sdm845-tlmm-state: type: object diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,tlmm-common.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,tlmm-common.yaml index aae3dcf6cac8..aec72e8c0621 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,tlmm-common.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,tlmm-common.yaml @@ -51,6 +51,12 @@ properties: should not be accessed by the OS. Please see the ../gpio/gpio.txt for more information. +patternProperties: + "-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + allOf: - $ref: pinctrl.yaml# From 382488554128d21ab0b57d416dcf02287c0eed64 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 11:34:45 +0200 Subject: [PATCH 0559/5214] pinctrl: qcom: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Some Qualcomm pin controller drivers already have this correctly placed, so adjust the other drivers. No functional impact. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-apq8064.c | 2 +- drivers/pinctrl/qcom/pinctrl-apq8084.c | 2 +- drivers/pinctrl/qcom/pinctrl-eliza.c | 2 +- drivers/pinctrl/qcom/pinctrl-glymur.c | 2 +- drivers/pinctrl/qcom/pinctrl-hawi.c | 2 +- drivers/pinctrl/qcom/pinctrl-ipq4019.c | 2 +- drivers/pinctrl/qcom/pinctrl-ipq6018.c | 2 +- drivers/pinctrl/qcom/pinctrl-ipq8064.c | 2 +- drivers/pinctrl/qcom/pinctrl-ipq8074.c | 2 +- drivers/pinctrl/qcom/pinctrl-kaanapali.c | 2 +- drivers/pinctrl/qcom/pinctrl-mdm9607.c | 2 +- drivers/pinctrl/qcom/pinctrl-mdm9615.c | 2 +- drivers/pinctrl/qcom/pinctrl-milos.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8226.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8660.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8916.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8953.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8960.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8976.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8994.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8996.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8998.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm8x74.c | 2 +- drivers/pinctrl/qcom/pinctrl-qcm2290.c | 2 +- drivers/pinctrl/qcom/pinctrl-qcs404.c | 2 +- drivers/pinctrl/qcom/pinctrl-qcs615.c | 2 +- drivers/pinctrl/qcom/pinctrl-sc7180.c | 2 +- drivers/pinctrl/qcom/pinctrl-sc7280.c | 2 +- drivers/pinctrl/qcom/pinctrl-sdm660.c | 2 +- drivers/pinctrl/qcom/pinctrl-sdm845.c | 2 +- drivers/pinctrl/qcom/pinctrl-sdx55.c | 2 +- drivers/pinctrl/qcom/pinctrl-sdx65.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm6115.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm6125.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm6350.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm6375.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8150.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8250.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8350.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8450.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8550.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8650.c | 2 +- drivers/pinctrl/qcom/pinctrl-sm8750.c | 2 +- drivers/pinctrl/qcom/pinctrl-x1e80100.c | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-apq8064.c b/drivers/pinctrl/qcom/pinctrl-apq8064.c index 3654913f1ae5..a47f3d4f1ea5 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8064.c @@ -622,6 +622,7 @@ static const struct of_device_id apq8064_pinctrl_of_match[] = { { .compatible = "qcom,apq8064-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, apq8064_pinctrl_of_match); static struct platform_driver apq8064_pinctrl_driver = { .driver = { @@ -646,4 +647,3 @@ module_exit(apq8064_pinctrl_exit); MODULE_AUTHOR("Bjorn Andersson "); MODULE_DESCRIPTION("Qualcomm APQ8064 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, apq8064_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-apq8084.c b/drivers/pinctrl/qcom/pinctrl-apq8084.c index 9fdbe6743512..e3c1f86aba7d 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8084.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8084.c @@ -1198,6 +1198,7 @@ static const struct of_device_id apq8084_pinctrl_of_match[] = { { .compatible = "qcom,apq8084-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, apq8084_pinctrl_of_match); static struct platform_driver apq8084_pinctrl_driver = { .driver = { @@ -1221,4 +1222,3 @@ module_exit(apq8084_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm APQ8084 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, apq8084_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index de9783ab509f..bcd69c79a628 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -1609,6 +1609,7 @@ static const struct of_device_id eliza_tlmm_of_match[] = { { .compatible = "qcom,eliza-tlmm", }, {}, }; +MODULE_DEVICE_TABLE(of, eliza_tlmm_of_match); static struct platform_driver eliza_tlmm_driver = { .driver = { @@ -1632,4 +1633,3 @@ module_exit(eliza_tlmm_exit); MODULE_DESCRIPTION("QTI Eliza TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, eliza_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-glymur.c b/drivers/pinctrl/qcom/pinctrl-glymur.c index 9838c7839923..20f076e59375 100644 --- a/drivers/pinctrl/qcom/pinctrl-glymur.c +++ b/drivers/pinctrl/qcom/pinctrl-glymur.c @@ -1777,6 +1777,7 @@ static const struct of_device_id glymur_tlmm_of_match[] = { { .compatible = "qcom,mahua-tlmm", .data = &mahua_tlmm }, { }, }; +MODULE_DEVICE_TABLE(of, glymur_tlmm_of_match); static int glymur_tlmm_probe(struct platform_device *pdev) { @@ -1811,4 +1812,3 @@ module_exit(glymur_tlmm_exit); MODULE_DESCRIPTION("QTI Glymur TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, glymur_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-hawi.c b/drivers/pinctrl/qcom/pinctrl-hawi.c index 5c7894f3b9cb..cdde69e954a7 100644 --- a/drivers/pinctrl/qcom/pinctrl-hawi.c +++ b/drivers/pinctrl/qcom/pinctrl-hawi.c @@ -1584,6 +1584,7 @@ static const struct of_device_id hawi_tlmm_of_match[] = { { .compatible = "qcom,hawi-tlmm", }, {}, }; +MODULE_DEVICE_TABLE(of, hawi_tlmm_of_match); static struct platform_driver hawi_tlmm_driver = { .driver = { @@ -1607,4 +1608,3 @@ module_exit(hawi_tlmm_exit); MODULE_DESCRIPTION("QTI Hawi TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, hawi_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index c5f0decc3eb3..705097244c0d 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -702,6 +702,7 @@ static const struct of_device_id ipq4019_pinctrl_of_match[] = { { .compatible = "qcom,ipq4019-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, ipq4019_pinctrl_of_match); static struct platform_driver ipq4019_pinctrl_driver = { .driver = { @@ -725,4 +726,3 @@ module_exit(ipq4019_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm ipq4019 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, ipq4019_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq6018.c b/drivers/pinctrl/qcom/pinctrl-ipq6018.c index cc83f9362a85..9a42e343ff17 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq6018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq6018.c @@ -1072,6 +1072,7 @@ static const struct of_device_id ipq6018_pinctrl_of_match[] = { { .compatible = "qcom,ipq6018-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, ipq6018_pinctrl_of_match); static struct platform_driver ipq6018_pinctrl_driver = { .driver = { @@ -1095,4 +1096,3 @@ module_exit(ipq6018_pinctrl_exit); MODULE_DESCRIPTION("QTI ipq6018 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, ipq6018_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c index 0a9e357e64c6..78d320d56be6 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c @@ -624,6 +624,7 @@ static const struct of_device_id ipq8064_pinctrl_of_match[] = { { .compatible = "qcom,ipq8064-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, ipq8064_pinctrl_of_match); static struct platform_driver ipq8064_pinctrl_driver = { .driver = { @@ -648,4 +649,3 @@ module_exit(ipq8064_pinctrl_exit); MODULE_AUTHOR("Andy Gross "); MODULE_DESCRIPTION("Qualcomm IPQ8064 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, ipq8064_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8074.c b/drivers/pinctrl/qcom/pinctrl-ipq8074.c index 64ce8ea8f544..0b95f52adcc3 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8074.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8074.c @@ -1033,6 +1033,7 @@ static const struct of_device_id ipq8074_pinctrl_of_match[] = { { .compatible = "qcom,ipq8074-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, ipq8074_pinctrl_of_match); static struct platform_driver ipq8074_pinctrl_driver = { .driver = { @@ -1056,4 +1057,3 @@ module_exit(ipq8074_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm ipq8074 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, ipq8074_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-kaanapali.c b/drivers/pinctrl/qcom/pinctrl-kaanapali.c index 5cc45b9c55ab..e95ac064b84c 100644 --- a/drivers/pinctrl/qcom/pinctrl-kaanapali.c +++ b/drivers/pinctrl/qcom/pinctrl-kaanapali.c @@ -1774,6 +1774,7 @@ static const struct of_device_id kaanapali_tlmm_of_match[] = { { .compatible = "qcom,kaanapali-tlmm",}, {}, }; +MODULE_DEVICE_TABLE(of, kaanapali_tlmm_of_match); static struct platform_driver kaanapali_tlmm_driver = { .driver = { @@ -1797,4 +1798,3 @@ module_exit(kaanapali_tlmm_exit); MODULE_DESCRIPTION("QTI Kaanapali TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, kaanapali_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-mdm9607.c b/drivers/pinctrl/qcom/pinctrl-mdm9607.c index 5794b0a11010..ce61eb415755 100644 --- a/drivers/pinctrl/qcom/pinctrl-mdm9607.c +++ b/drivers/pinctrl/qcom/pinctrl-mdm9607.c @@ -1050,6 +1050,7 @@ static const struct of_device_id mdm9607_pinctrl_of_match[] = { { .compatible = "qcom,mdm9607-tlmm", }, { } }; +MODULE_DEVICE_TABLE(of, mdm9607_pinctrl_of_match); static struct platform_driver mdm9607_pinctrl_driver = { .driver = { @@ -1073,4 +1074,3 @@ module_exit(mdm9607_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm mdm9607 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, mdm9607_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-mdm9615.c b/drivers/pinctrl/qcom/pinctrl-mdm9615.c index 729fe3d7e14e..f87e4d9a8f4f 100644 --- a/drivers/pinctrl/qcom/pinctrl-mdm9615.c +++ b/drivers/pinctrl/qcom/pinctrl-mdm9615.c @@ -439,6 +439,7 @@ static const struct of_device_id mdm9615_pinctrl_of_match[] = { { .compatible = "qcom,mdm9615-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, mdm9615_pinctrl_of_match); static struct platform_driver mdm9615_pinctrl_driver = { .driver = { @@ -463,4 +464,3 @@ module_exit(mdm9615_pinctrl_exit); MODULE_AUTHOR("Neil Armstrong "); MODULE_DESCRIPTION("Qualcomm MDM9615 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, mdm9615_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-milos.c b/drivers/pinctrl/qcom/pinctrl-milos.c index 74b5253257af..f5079a0ce0a3 100644 --- a/drivers/pinctrl/qcom/pinctrl-milos.c +++ b/drivers/pinctrl/qcom/pinctrl-milos.c @@ -1310,6 +1310,7 @@ static const struct of_device_id milos_tlmm_of_match[] = { { .compatible = "qcom,milos-tlmm" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, milos_tlmm_of_match); static struct platform_driver milos_tlmm_driver = { .driver = { @@ -1333,4 +1334,3 @@ module_exit(milos_tlmm_exit); MODULE_DESCRIPTION("QTI Milos TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, milos_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8226.c b/drivers/pinctrl/qcom/pinctrl-msm8226.c index d27b7599ea83..6f02e7c2499e 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8226.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8226.c @@ -645,6 +645,7 @@ static const struct of_device_id msm8226_pinctrl_of_match[] = { { .compatible = "qcom,msm8226-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8226_pinctrl_of_match); static struct platform_driver msm8226_pinctrl_driver = { .driver = { @@ -669,4 +670,3 @@ module_exit(msm8226_pinctrl_exit); MODULE_AUTHOR("Bartosz Dudziak "); MODULE_DESCRIPTION("Qualcomm MSM8226 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8226_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8660.c b/drivers/pinctrl/qcom/pinctrl-msm8660.c index 5ded00396cd9..5b28a1c21a88 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8660.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8660.c @@ -974,6 +974,7 @@ static const struct of_device_id msm8660_pinctrl_of_match[] = { { .compatible = "qcom,msm8660-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8660_pinctrl_of_match); static struct platform_driver msm8660_pinctrl_driver = { .driver = { @@ -998,4 +999,3 @@ module_exit(msm8660_pinctrl_exit); MODULE_AUTHOR("Bjorn Andersson "); MODULE_DESCRIPTION("Qualcomm MSM8660 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8660_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8916.c b/drivers/pinctrl/qcom/pinctrl-msm8916.c index 709c5d1d4d0a..d115035ff96a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8916.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8916.c @@ -960,6 +960,7 @@ static const struct of_device_id msm8916_pinctrl_of_match[] = { { .compatible = "qcom,msm8916-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8916_pinctrl_of_match); static struct platform_driver msm8916_pinctrl_driver = { .driver = { @@ -983,4 +984,3 @@ module_exit(msm8916_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm msm8916 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8916_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8953.c b/drivers/pinctrl/qcom/pinctrl-msm8953.c index 02ea89f5feaa..d537fdaae148 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8953.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8953.c @@ -1807,6 +1807,7 @@ static const struct of_device_id msm8953_pinctrl_of_match[] = { { .compatible = "qcom,msm8953-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8953_pinctrl_of_match); static struct platform_driver msm8953_pinctrl_driver = { .driver = { @@ -1830,4 +1831,3 @@ module_exit(msm8953_pinctrl_exit); MODULE_DESCRIPTION("QTI msm8953 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8953_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8960.c b/drivers/pinctrl/qcom/pinctrl-msm8960.c index 2fb15208aba0..a373150468ca 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8960.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8960.c @@ -1239,6 +1239,7 @@ static const struct of_device_id msm8960_pinctrl_of_match[] = { { .compatible = "qcom,msm8960-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8960_pinctrl_of_match); static struct platform_driver msm8960_pinctrl_driver = { .driver = { @@ -1263,4 +1264,3 @@ module_exit(msm8960_pinctrl_exit); MODULE_AUTHOR("Bjorn Andersson "); MODULE_DESCRIPTION("Qualcomm MSM8960 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8960_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8976.c b/drivers/pinctrl/qcom/pinctrl-msm8976.c index 906a90778b97..bba3c87d8144 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8976.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8976.c @@ -1087,6 +1087,7 @@ static const struct of_device_id msm8976_pinctrl_of_match[] = { { .compatible = "qcom,msm8976-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8976_pinctrl_of_match); static struct platform_driver msm8976_pinctrl_driver = { .driver = { @@ -1110,4 +1111,3 @@ module_exit(msm8976_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm msm8976 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8976_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8994.c b/drivers/pinctrl/qcom/pinctrl-msm8994.c index ecbe6b91d1da..fdaa67c5869f 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8994.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8994.c @@ -1334,6 +1334,7 @@ static const struct of_device_id msm8994_pinctrl_of_match[] = { { .compatible = "qcom,msm8994-pinctrl", }, { } }; +MODULE_DEVICE_TABLE(of, msm8994_pinctrl_of_match); static struct platform_driver msm8994_pinctrl_driver = { .driver = { @@ -1357,4 +1358,3 @@ module_exit(msm8994_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm MSM8994 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8994_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8996.c b/drivers/pinctrl/qcom/pinctrl-msm8996.c index 73b07a10a957..332b18a8fa9c 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8996.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8996.c @@ -1911,6 +1911,7 @@ static const struct of_device_id msm8996_pinctrl_of_match[] = { { .compatible = "qcom,msm8996-pinctrl", }, { } }; +MODULE_DEVICE_TABLE(of, msm8996_pinctrl_of_match); static struct platform_driver msm8996_pinctrl_driver = { .driver = { @@ -1934,4 +1935,3 @@ module_exit(msm8996_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm msm8996 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8996_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8998.c b/drivers/pinctrl/qcom/pinctrl-msm8998.c index dcf11b79e562..0552c8212b29 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8998.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8998.c @@ -1525,6 +1525,7 @@ static const struct of_device_id msm8998_pinctrl_of_match[] = { { .compatible = "qcom,msm8998-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8998_pinctrl_of_match); static struct platform_driver msm8998_pinctrl_driver = { .driver = { @@ -1548,4 +1549,3 @@ module_exit(msm8998_pinctrl_exit); MODULE_DESCRIPTION("QTI msm8998 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8998_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-msm8x74.c b/drivers/pinctrl/qcom/pinctrl-msm8x74.c index ff432ec5815a..9422629ec6ca 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8x74.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8x74.c @@ -1073,6 +1073,7 @@ static const struct of_device_id msm8x74_pinctrl_of_match[] = { { .compatible = "qcom,msm8974-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, msm8x74_pinctrl_of_match); static struct platform_driver msm8x74_pinctrl_driver = { .driver = { @@ -1097,5 +1098,4 @@ module_exit(msm8x74_pinctrl_exit); MODULE_AUTHOR("Bjorn Andersson "); MODULE_DESCRIPTION("Qualcomm MSM8x74 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, msm8x74_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index 844d3dc9e72c..ffec2fd6412c 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -1101,6 +1101,7 @@ static const struct of_device_id qcm2290_pinctrl_of_match[] = { { .compatible = "qcom,qcm2290-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, qcm2290_pinctrl_of_match); static struct platform_driver qcm2290_pinctrl_driver = { .driver = { @@ -1124,4 +1125,3 @@ module_exit(qcm2290_pinctrl_exit); MODULE_DESCRIPTION("QTI QCM2290 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, qcm2290_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index 1048a7093b2e..01c0f0b2839a 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -1635,6 +1635,7 @@ static const struct of_device_id qcs404_pinctrl_of_match[] = { { .compatible = "qcom,qcs404-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, qcs404_pinctrl_of_match); static struct platform_driver qcs404_pinctrl_driver = { .driver = { @@ -1658,4 +1659,3 @@ module_exit(qcs404_pinctrl_exit); MODULE_DESCRIPTION("Qualcomm QCS404 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, qcs404_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c index 0ed4332d989e..9b4b95771d0b 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs615.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c @@ -1073,6 +1073,7 @@ static const struct of_device_id qcs615_tlmm_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, qcs615_tlmm_of_match); static int qcs615_tlmm_probe(struct platform_device *pdev) { @@ -1101,4 +1102,3 @@ module_exit(qcs615_tlmm_exit); MODULE_DESCRIPTION("QTI QCS615 TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, qcs615_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sc7180.c b/drivers/pinctrl/qcom/pinctrl-sc7180.c index 01cfcb416f33..a02a9a78a557 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7180.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7180.c @@ -1148,6 +1148,7 @@ static const struct of_device_id sc7180_pinctrl_of_match[] = { { .compatible = "qcom,sc7180-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sc7180_pinctrl_of_match); static struct platform_driver sc7180_pinctrl_driver = { .driver = { @@ -1172,4 +1173,3 @@ module_exit(sc7180_pinctrl_exit); MODULE_DESCRIPTION("QTI sc7180 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sc7180_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c index f22fd56efd89..bb32a56649df 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -1494,6 +1494,7 @@ static const struct of_device_id sc7280_pinctrl_of_match[] = { { .compatible = "qcom,sc7280-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sc7280_pinctrl_of_match); static struct platform_driver sc7280_pinctrl_driver = { .driver = { @@ -1518,4 +1519,3 @@ module_exit(sc7280_pinctrl_exit); MODULE_DESCRIPTION("QTI sc7280 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sc7280_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660.c b/drivers/pinctrl/qcom/pinctrl-sdm660.c index ab0368653d30..c4a1ec90a341 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm660.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm660.c @@ -1433,6 +1433,7 @@ static const struct of_device_id sdm660_pinctrl_of_match[] = { { .compatible = "qcom,sdm630-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sdm660_pinctrl_of_match); static struct platform_driver sdm660_pinctrl_driver = { .driver = { @@ -1456,4 +1457,3 @@ module_exit(sdm660_pinctrl_exit); MODULE_DESCRIPTION("QTI sdm660 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sdm660_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index b5ed2311b70e..48fbe3623e17 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -1339,6 +1339,7 @@ static const struct of_device_id sdm845_pinctrl_of_match[] = { { .compatible = "qcom,sdm845-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match); static struct platform_driver sdm845_pinctrl_driver = { .driver = { @@ -1364,4 +1365,3 @@ module_exit(sdm845_pinctrl_exit); MODULE_DESCRIPTION("QTI sdm845 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sdx55.c b/drivers/pinctrl/qcom/pinctrl-sdx55.c index 3e87f5927924..656a0726db92 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx55.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx55.c @@ -981,6 +981,7 @@ static const struct of_device_id sdx55_pinctrl_of_match[] = { { .compatible = "qcom,sdx55-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sdx55_pinctrl_of_match); static struct platform_driver sdx55_pinctrl_driver = { .driver = { @@ -1004,4 +1005,3 @@ module_exit(sdx55_pinctrl_exit); MODULE_DESCRIPTION("QTI sdx55 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sdx55_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sdx65.c b/drivers/pinctrl/qcom/pinctrl-sdx65.c index 4e787341b2a2..dde8993ca9b1 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx65.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx65.c @@ -929,6 +929,7 @@ static const struct of_device_id sdx65_pinctrl_of_match[] = { { .compatible = "qcom,sdx65-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, sdx65_pinctrl_of_match); static struct platform_driver sdx65_pinctrl_driver = { .driver = { @@ -952,4 +953,3 @@ module_exit(sdx65_pinctrl_exit); MODULE_DESCRIPTION("QTI sdx65 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sdx65_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index 234451fbf47b..e36716514f1f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -897,6 +897,7 @@ static const struct of_device_id sm6115_tlmm_of_match[] = { { .compatible = "qcom,sm6115-tlmm", }, { } }; +MODULE_DEVICE_TABLE(of, sm6115_tlmm_of_match); static struct platform_driver sm6115_tlmm_driver = { .driver = { @@ -920,4 +921,3 @@ module_exit(sm6115_tlmm_exit); MODULE_DESCRIPTION("QTI sm6115 tlmm driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm6115_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6125.c b/drivers/pinctrl/qcom/pinctrl-sm6125.c index 2cf9136860fc..7447ef9a4931 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6125.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6125.c @@ -1256,6 +1256,7 @@ static const struct of_device_id sm6125_tlmm_of_match[] = { { .compatible = "qcom,sm6125-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, sm6125_tlmm_of_match); static struct platform_driver sm6125_tlmm_driver = { .driver = { @@ -1279,4 +1280,3 @@ module_exit(sm6125_tlmm_exit); MODULE_DESCRIPTION("QTI sm6125 TLMM driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm6125_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6350.c b/drivers/pinctrl/qcom/pinctrl-sm6350.c index eb8cd4aa8a97..4089c96ff736 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6350.c @@ -1363,6 +1363,7 @@ static const struct of_device_id sm6350_tlmm_of_match[] = { { .compatible = "qcom,sm6350-tlmm" }, { }, }; +MODULE_DEVICE_TABLE(of, sm6350_tlmm_of_match); static struct platform_driver sm6350_tlmm_driver = { .driver = { @@ -1386,4 +1387,3 @@ module_exit(sm6350_tlmm_exit); MODULE_DESCRIPTION("QTI SM6350 TLMM driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm6350_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6375.c b/drivers/pinctrl/qcom/pinctrl-sm6375.c index d4547dd9f21f..8da71d940b90 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6375.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6375.c @@ -1506,6 +1506,7 @@ static const struct of_device_id sm6375_tlmm_of_match[] = { { .compatible = "qcom,sm6375-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, sm6375_tlmm_of_match); static struct platform_driver sm6375_tlmm_driver = { .driver = { @@ -1529,4 +1530,3 @@ module_exit(sm6375_tlmm_exit); MODULE_DESCRIPTION("QTI SM6375 TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, sm6375_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index 0767261f5149..3c29de948034 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -1532,6 +1532,7 @@ static const struct of_device_id sm8150_pinctrl_of_match[] = { { .compatible = "qcom,sm8150-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sm8150_pinctrl_of_match); static struct platform_driver sm8150_pinctrl_driver = { .driver = { @@ -1555,4 +1556,3 @@ module_exit(sm8150_pinctrl_exit); MODULE_DESCRIPTION("QTI sm8150 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm8150_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index f73f3b052de4..abf5f68ef62f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -1354,6 +1354,7 @@ static const struct of_device_id sm8250_pinctrl_of_match[] = { { .compatible = "qcom,sm8250-pinctrl", }, { }, }; +MODULE_DEVICE_TABLE(of, sm8250_pinctrl_of_match); static struct platform_driver sm8250_pinctrl_driver = { .driver = { @@ -1377,4 +1378,3 @@ module_exit(sm8250_pinctrl_exit); MODULE_DESCRIPTION("QTI sm8250 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm8250_pinctrl_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350.c b/drivers/pinctrl/qcom/pinctrl-sm8350.c index 377ddfc77e4f..8bd278c97171 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8350.c @@ -1632,6 +1632,7 @@ static const struct of_device_id sm8350_tlmm_of_match[] = { { .compatible = "qcom,sm8350-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, sm8350_tlmm_of_match); static struct platform_driver sm8350_tlmm_driver = { .driver = { @@ -1655,4 +1656,3 @@ module_exit(sm8350_tlmm_exit); MODULE_DESCRIPTION("QTI SM8350 TLMM driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm8350_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450.c b/drivers/pinctrl/qcom/pinctrl-sm8450.c index a1d84074ea49..8ca5b6e15e6c 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450.c @@ -1667,6 +1667,7 @@ static const struct of_device_id sm8450_tlmm_of_match[] = { { .compatible = "qcom,sm8450-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, sm8450_tlmm_of_match); static struct platform_driver sm8450_tlmm_driver = { .driver = { @@ -1690,4 +1691,3 @@ module_exit(sm8450_tlmm_exit); MODULE_DESCRIPTION("QTI SM8450 TLMM driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, sm8450_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c index cc8fbf4d5e84..d12256d970a0 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c @@ -1752,6 +1752,7 @@ static const struct of_device_id sm8550_tlmm_of_match[] = { { .compatible = "qcom,sm8550-tlmm", }, {}, }; +MODULE_DEVICE_TABLE(of, sm8550_tlmm_of_match); static struct platform_driver sm8550_tlmm_driver = { .driver = { @@ -1775,4 +1776,3 @@ module_exit(sm8550_tlmm_exit); MODULE_DESCRIPTION("QTI SM8550 TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, sm8550_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c index ab41292e3b4e..cf57d226c47f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -1732,6 +1732,7 @@ static const struct of_device_id sm8650_tlmm_of_match[] = { { .compatible = "qcom,sm8650-tlmm", }, {}, }; +MODULE_DEVICE_TABLE(of, sm8650_tlmm_of_match); static struct platform_driver sm8650_tlmm_driver = { .driver = { @@ -1755,4 +1756,3 @@ module_exit(sm8650_tlmm_exit); MODULE_DESCRIPTION("QTI SM8650 TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, sm8650_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8750.c b/drivers/pinctrl/qcom/pinctrl-sm8750.c index 4cfe73f30fac..f27e55c088d5 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8750.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8750.c @@ -1701,6 +1701,7 @@ static const struct of_device_id sm8750_tlmm_of_match[] = { { .compatible = "qcom,sm8750-tlmm", }, {}, }; +MODULE_DEVICE_TABLE(of, sm8750_tlmm_of_match); static struct platform_driver sm8750_tlmm_driver = { .driver = { @@ -1724,4 +1725,3 @@ module_exit(sm8750_tlmm_exit); MODULE_DESCRIPTION("QTI SM8750 TLMM driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, sm8750_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index a9fe75fc45e5..8d2b8246170b 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -1851,6 +1851,7 @@ static const struct of_device_id x1e80100_pinctrl_of_match[] = { { .compatible = "qcom,x1e80100-tlmm", }, { }, }; +MODULE_DEVICE_TABLE(of, x1e80100_pinctrl_of_match); static struct platform_driver x1e80100_pinctrl_driver = { .driver = { @@ -1874,4 +1875,3 @@ module_exit(x1e80100_pinctrl_exit); MODULE_DESCRIPTION("QTI X1E80100 TLMM pinctrl driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, x1e80100_pinctrl_of_match); From a11c0a37a994cacc2605b33ebab7cb6f4fa3d2a7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 11:43:17 +0200 Subject: [PATCH 0560/5214] pinctrl: bcm: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the pin controller drivers already have this correctly placed, so adjust the other drivers. No functional impact. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/bcm/pinctrl-bcm4908.c | 2 +- drivers/pinctrl/bcm/pinctrl-ns.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm4908.c b/drivers/pinctrl/bcm/pinctrl-bcm4908.c index 12f7a253ea4d..57969cdbc635 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm4908.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm4908.c @@ -466,6 +466,7 @@ static const struct of_device_id bcm4908_pinctrl_of_match_table[] = { { .compatible = "brcm,bcm4908-pinctrl", }, { } }; +MODULE_DEVICE_TABLE(of, bcm4908_pinctrl_of_match_table); static int bcm4908_pinctrl_probe(struct platform_device *pdev) { @@ -561,4 +562,3 @@ module_platform_driver(bcm4908_pinctrl_driver); MODULE_AUTHOR("Rafał Miłecki"); MODULE_DESCRIPTION("Broadcom BCM4908 pinmux driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, bcm4908_pinctrl_of_match_table); diff --git a/drivers/pinctrl/bcm/pinctrl-ns.c b/drivers/pinctrl/bcm/pinctrl-ns.c index 03bd01b4a945..e134c9c73450 100644 --- a/drivers/pinctrl/bcm/pinctrl-ns.c +++ b/drivers/pinctrl/bcm/pinctrl-ns.c @@ -204,6 +204,7 @@ static const struct of_device_id ns_pinctrl_of_match_table[] = { { .compatible = "brcm,bcm53012-pinmux", .data = (void *)FLAG_BCM53012, }, { } }; +MODULE_DEVICE_TABLE(of, ns_pinctrl_of_match_table); static int ns_pinctrl_probe(struct platform_device *pdev) { @@ -295,4 +296,3 @@ static struct platform_driver ns_pinctrl_driver = { module_platform_driver(ns_pinctrl_driver); MODULE_AUTHOR("Rafał Miłecki"); -MODULE_DEVICE_TABLE(of, ns_pinctrl_of_match_table); From accae072742eb4cbc0f0955660380a04b9f7dc1a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 11:43:18 +0200 Subject: [PATCH 0561/5214] pinctrl: rockchip: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the pin controller drivers already have this correctly placed, so adjust the other drivers. No functional impact. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-rockchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 2f14c7f9c95a..7e0fcd45fd26 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -5303,6 +5303,7 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = { .data = &rk3588_pin_ctrl }, {}, }; +MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); static struct platform_driver rockchip_pinctrl_driver = { .probe = rockchip_pinctrl_probe, @@ -5329,4 +5330,3 @@ module_exit(rockchip_pinctrl_drv_unregister); MODULE_DESCRIPTION("ROCKCHIP Pin Controller Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pinctrl-rockchip"); -MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); From 1fa3d15c5f1915ac0c939861d26871ef3376cfe0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 18:35:49 +0200 Subject: [PATCH 0562/5214] pinctrl: qcom: Make important drivers default (1) The main SoC TLMM (Top-Level Multiplexer) pin controller drivers are essential for booting up SoCs and are not really optional for a given platform. Kernel should not ask users choice of drivers when that choice is obvious and known to the developers that answer should be 'yes' or 'module'. Switch all Qualcomm TLMM pin controller drivers to a default 'yes' for ARCH_QCOM. This has impact: 1. arm64 defconfig: enable PINCTRL_SM7150, PINCTRL_IPQ9650 and PINCTRL_HAWI, which were not selected before but should be, because these platforms need them for proper boot. 2. arm qcom_defconfig: no changes. 3. arm multi_v7 defconfig: enable drivers necessary to boot ARM 32-bit platforms, which are already enabled on qcom_defconfig. 4. COMPILE_TEST builds: enable by default all drivers for arm or arm64 builds, whenever ARCH_QCOM is selected. This has impact on build time and feels logical, because if one selects ARCH_QCOM then probably by default wants to build test it entirely. Kernels with COMPILE_TEST are not supposed to be used for booting. Reviewed-by: Konrad Dybcio Reviewed-by: Linus Walleij Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov [linusw@kernel.org: Split off the defconfig changes to a separate patch] Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + drivers/pinctrl/qcom/Kconfig.msm | 61 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 5452b128c2e5..a09e840a01c6 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -6,6 +6,7 @@ config PINCTRL_MSM depends on GPIOLIB # OF for pinconf_generic_dt_node_to_map_group() from GENERIC_PINCONF depends on OF + default ARCH_QCOM select QCOM_SCM select PINMUX select GENERIC_PINMUX_FUNCTIONS diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 8b4d3bce585f..65bc7424065a 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -4,6 +4,7 @@ if PINCTRL_MSM config PINCTRL_APQ8064 tristate "Qualcomm APQ8064 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm APQ8064 platform. @@ -11,6 +12,7 @@ config PINCTRL_APQ8064 config PINCTRL_APQ8084 tristate "Qualcomm APQ8084 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm APQ8084 platform. @@ -18,6 +20,7 @@ config PINCTRL_APQ8084 config PINCTRL_ELIZA tristate "Qualcomm Eliza pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) @@ -28,6 +31,7 @@ config PINCTRL_ELIZA config PINCTRL_GLYMUR tristate "Qualcomm Glymur pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) @@ -38,6 +42,7 @@ config PINCTRL_GLYMUR config PINCTRL_HAWI tristate "Qualcomm Hawi pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) @@ -48,6 +53,7 @@ config PINCTRL_HAWI config PINCTRL_IPQ4019 tristate "Qualcomm IPQ4019 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm IPQ4019 platform. @@ -55,6 +61,7 @@ config PINCTRL_IPQ4019 config PINCTRL_IPQ5018 tristate "Qualcomm IPQ5018 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -64,6 +71,7 @@ config PINCTRL_IPQ5018 config PINCTRL_IPQ8064 tristate "Qualcomm IPQ8064 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm IPQ8064 platform. @@ -71,6 +79,7 @@ config PINCTRL_IPQ8064 config PINCTRL_IPQ5210 tristate "Qualcomm IPQ5210 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -79,6 +88,7 @@ config PINCTRL_IPQ5210 config PINCTRL_IPQ5332 tristate "Qualcomm IPQ5332 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -87,6 +97,7 @@ config PINCTRL_IPQ5332 config PINCTRL_IPQ5424 tristate "Qualcomm IPQ5424 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -96,6 +107,7 @@ config PINCTRL_IPQ5424 config PINCTRL_IPQ8074 tristate "Qualcomm IPQ8074 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -105,6 +117,7 @@ config PINCTRL_IPQ8074 config PINCTRL_IPQ6018 tristate "Qualcomm IPQ6018 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -114,6 +127,7 @@ config PINCTRL_IPQ6018 config PINCTRL_IPQ9574 tristate "Qualcomm IPQ9574 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -123,6 +137,7 @@ config PINCTRL_IPQ9574 config PINCTRL_IPQ9650 tristate "Qualcomm IPQ9650 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc. TLMM block found on the @@ -132,6 +147,7 @@ config PINCTRL_IPQ9650 config PINCTRL_KAANAPALI tristate "Qualcomm Kaanapali pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -140,6 +156,7 @@ config PINCTRL_KAANAPALI config PINCTRL_MSM8226 tristate "Qualcomm 8226 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -148,6 +165,7 @@ config PINCTRL_MSM8226 config PINCTRL_MSM8660 tristate "Qualcomm 8660 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 8660 platform. @@ -155,12 +173,14 @@ config PINCTRL_MSM8660 config PINCTRL_MSM8960 tristate "Qualcomm 8960 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 8960 platform. config PINCTRL_MDM9607 tristate "Qualcomm 9607 pin controller driver" + default ARCH_QCOM if ARM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 9607 platform. @@ -168,6 +188,7 @@ config PINCTRL_MDM9607 config PINCTRL_MDM9615 tristate "Qualcomm 9615 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 9615 platform. @@ -175,6 +196,7 @@ config PINCTRL_MDM9615 config PINCTRL_MSM8X74 tristate "Qualcomm 8x74 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 8974 platform. @@ -182,12 +204,14 @@ config PINCTRL_MSM8X74 config PINCTRL_MSM8909 tristate "Qualcomm 8909 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found on the Qualcomm MSM8909 platform. config PINCTRL_MSM8916 tristate "Qualcomm 8916 pin controller driver" + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found on the Qualcomm 8916 platform. @@ -201,6 +225,7 @@ config PINCTRL_MSM8917 config PINCTRL_MSM8953 tristate "Qualcomm 8953 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found on the Qualcomm MSM8953 platform. @@ -210,6 +235,7 @@ config PINCTRL_MSM8953 config PINCTRL_MSM8976 tristate "Qualcomm 8976 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found on the Qualcomm MSM8976 platform. @@ -219,6 +245,7 @@ config PINCTRL_MSM8976 config PINCTRL_MSM8994 tristate "Qualcomm 8994 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm 8994 platform. The @@ -227,6 +254,7 @@ config PINCTRL_MSM8994 config PINCTRL_MSM8996 tristate "Qualcomm MSM8996 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm MSM8996 platform. @@ -234,6 +262,7 @@ config PINCTRL_MSM8996 config PINCTRL_MSM8998 tristate "Qualcomm MSM8998 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm MSM8998 platform. @@ -249,6 +278,7 @@ config PINCTRL_NORD config PINCTRL_QCM2290 tristate "Qualcomm QCM2290 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the TLMM block found in the Qualcomm QCM2290 platform. @@ -256,6 +286,7 @@ config PINCTRL_QCM2290 config PINCTRL_QCS404 tristate "Qualcomm QCS404 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the TLMM block found in the Qualcomm QCS404 platform. @@ -263,6 +294,7 @@ config PINCTRL_QCS404 config PINCTRL_QCS615 tristate "Qualcomm QCS615 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the TLMM block found on the Qualcomm QCS615 platform. @@ -270,6 +302,7 @@ config PINCTRL_QCS615 config PINCTRL_QCS8300 tristate "Qualcomm QCS8300 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux and pinconf driver for the Qualcomm TLMM block found on the Qualcomm QCS8300 platform. @@ -277,6 +310,7 @@ config PINCTRL_QCS8300 config PINCTRL_QDF2XXX tristate "Qualcomm QDF2xxx pin controller driver" depends on ACPI + default ARCH_QCOM if ARM64 help This is the GPIO driver for the TLMM block found on the Qualcomm Technologies QDF2xxx SOCs. @@ -284,6 +318,7 @@ config PINCTRL_QDF2XXX config PINCTRL_QDU1000 tristate "Qualcomm QDU1000/QRU1000 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf, and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -292,6 +327,7 @@ config PINCTRL_QDU1000 config PINCTRL_SA8775P tristate "Qualcomm SA8775P pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux and pinconf driver for the Qualcomm TLMM block found on the Qualcomm SA8775P platforms. @@ -307,6 +343,7 @@ config PINCTRL_SAR2130P config PINCTRL_SC7180 tristate "Qualcomm SC7180 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -315,6 +352,7 @@ config PINCTRL_SC7180 config PINCTRL_SC7280 tristate "Qualcomm SC7280 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -324,6 +362,7 @@ config PINCTRL_SC8180X tristate "Qualcomm SC8180x pin controller driver" depends on (OF || ACPI) depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -332,6 +371,7 @@ config PINCTRL_SC8180X config PINCTRL_SC8280XP tristate "Qualcomm SC8280xp pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -340,6 +380,7 @@ config PINCTRL_SC8280XP config PINCTRL_SDM660 tristate "Qualcomm SDM660 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -348,6 +389,7 @@ config PINCTRL_SDM660 config PINCTRL_SDM670 tristate "Qualcomm SDM670 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -357,6 +399,7 @@ config PINCTRL_SDM845 tristate "Qualcomm SDM845 pin controller driver" depends on (OF || ACPI) depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -365,6 +408,7 @@ config PINCTRL_SDM845 config PINCTRL_SDX55 tristate "Qualcomm SDX55 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -373,6 +417,7 @@ config PINCTRL_SDX55 config PINCTRL_SDX65 tristate "Qualcomm SDX65 pin controller driver" depends on ARM || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -381,6 +426,7 @@ config PINCTRL_SDX65 config PINCTRL_SDX75 tristate "Qualcomm SDX75 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -389,6 +435,7 @@ config PINCTRL_SDX75 config PINCTRL_SM4450 tristate "Qualcomm SM4450 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -397,6 +444,7 @@ config PINCTRL_SM4450 config PINCTRL_SM6115 tristate "Qualcomm SM6115,SM4250 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -405,6 +453,7 @@ config PINCTRL_SM6115 config PINCTRL_SM6125 tristate "Qualcomm SM6125 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -413,6 +462,7 @@ config PINCTRL_SM6125 config PINCTRL_SM6350 tristate "Qualcomm SM6350 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -421,6 +471,7 @@ config PINCTRL_SM6350 config PINCTRL_SM6375 tristate "Qualcomm SM6375 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -429,6 +480,7 @@ config PINCTRL_SM6375 config PINCTRL_SM7150 tristate "Qualcomm SM7150 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -437,6 +489,7 @@ config PINCTRL_SM7150 config PINCTRL_MILOS tristate "Qualcomm Milos pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -445,6 +498,7 @@ config PINCTRL_MILOS config PINCTRL_SM8150 tristate "Qualcomm SM8150 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -453,6 +507,7 @@ config PINCTRL_SM8150 config PINCTRL_SM8250 tristate "Qualcomm SM8250 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -461,6 +516,7 @@ config PINCTRL_SM8250 config PINCTRL_SM8350 tristate "Qualcomm SM8350 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -469,6 +525,7 @@ config PINCTRL_SM8350 config PINCTRL_SM8450 tristate "Qualcomm SM8450 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -477,6 +534,7 @@ config PINCTRL_SM8450 config PINCTRL_SM8550 tristate "Qualcomm SM8550 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -485,6 +543,7 @@ config PINCTRL_SM8550 config PINCTRL_SM8650 tristate "Qualcomm SM8650 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -493,6 +552,7 @@ config PINCTRL_SM8650 config PINCTRL_SM8750 tristate "Qualcomm SM8750 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc TLMM block found on the Qualcomm @@ -501,6 +561,7 @@ config PINCTRL_SM8750 config PINCTRL_X1E80100 tristate "Qualcomm X1E80100 pin controller driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) From 72780f7964684939d7d2f69c348876213b184484 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 8 Dec 2025 19:24:29 +0000 Subject: [PATCH 0563/5214] PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining Discard Vendor:Device ID matching in the PCIe failed link retraining quirk and ignore the link status for the removal of the 2.5GT/s speed clamp, whether applied by the quirk itself or the firmware earlier on. Revert to the original target link speed if this final link retraining has failed. This is so that link training noise in hot-plug scenarios does not make a link remain clamped to the 2.5GT/s speed where an event race has led the quirk to apply the speed clamp for one device, only to leave it in place for a subsequent device to be plugged in. Refer to the Link Capabilities register directly for the maximum link speed determination so as to streamline backporting. Fixes: a89c82249c37 ("PCI: Work around PCIe link training failures") Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Tested-by: Alok Tiwari Cc: stable@vger.kernel.org # v6.5+ Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk --- drivers/pci/quirks.c | 51 ++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index caaed1a01dc0..dd0025d3914e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -80,11 +80,10 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) * Restrict the speed to 2.5GT/s then with the Target Link Speed field, * request a retrain and check the result. * - * If this turns out successful and we know by the Vendor:Device ID it is - * safe to do so, then lift the restriction, letting the devices negotiate - * a higher speed. Also check for a similar 2.5GT/s speed restriction the - * firmware may have already arranged and lift it with ports that already - * report their data link being up. + * If this turns out successful, or where a 2.5GT/s speed restriction has + * been previously arranged by the firmware and the port reports its link + * already being up, lift the restriction, in a hope it is safe to do so, + * letting the devices negotiate a higher speed. * * Otherwise revert the speed to the original setting and request a retrain * again to remove any residual state, ignoring the result as it's supposed @@ -95,51 +94,37 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) */ int pcie_failed_link_retrain(struct pci_dev *dev) { - static const struct pci_device_id ids[] = { - { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ - {} - }; - u16 lnksta, lnkctl2; + u16 lnksta, lnkctl2, oldlnkctl2; int ret = -ENOTTY; + u32 lnkcap; if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) return ret; pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { - u16 oldlnkctl2; - pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); - - pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false); - if (ret) { - pci_info(dev, "retraining failed\n"); - pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), - true); - return ret; - } - - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + goto err; } pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - - if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && - (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - pci_match_id(ids, dev)) { - u32 lnkcap; - + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && + (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); - pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); - if (ret) { - pci_info(dev, "retraining failed\n"); - return ret; - } + if (ret) + goto err; } + return ret; +err: + pci_info(dev, "retraining failed\n"); + pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), true); return ret; } From 30a361308085ebf9a848be2d5828ed913d3572f5 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 8 Dec 2025 19:24:34 +0000 Subject: [PATCH 0564/5214] PCI: Use pcie_get_speed_cap() in PCIe failed link retraining Rewrite a check for the maximum link speed in the Link Capabilities register in terms of pcie_get_speed_cap(). No functional change. Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Tested-by: Alok Tiwari Link: https://patch.msgid.link/alpine.DEB.2.21.2512080348310.49654@angie.orcam.me.uk --- drivers/pci/quirks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dd0025d3914e..81ee3f69b918 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -95,8 +95,8 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) int pcie_failed_link_retrain(struct pci_dev *dev) { u16 lnksta, lnkctl2, oldlnkctl2; + enum pci_bus_speed speed_cap; int ret = -ENOTTY; - u32 lnkcap; if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) @@ -111,12 +111,12 @@ int pcie_failed_link_retrain(struct pci_dev *dev) goto err; } + speed_cap = pcie_get_speed_cap(dev); pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + speed_cap > PCIE_SPEED_2_5GT) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); - ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); + ret = pcie_set_target_speed(dev, speed_cap, false); if (ret) goto err; } From 64d63fd28177abd1bbc3133610771aa15c22c223 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 8 Dec 2025 19:24:38 +0000 Subject: [PATCH 0565/5214] PCI: Bail out early for 2.5GT/s devices in PCIe failed link retraining There's no point in retraining a failed 2.5GT/s device at 2.5GT/s, so just don't and return early. While such devices might be unlikely to implement Link Active reporting, we need to retrieve the maximum link speed and use it in a conditional later on anyway, so the early check comes for free. Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Tested-by: Alok Tiwari Link: https://patch.msgid.link/alpine.DEB.2.21.2512080356070.49654@angie.orcam.me.uk --- drivers/pci/quirks.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 81ee3f69b918..1b4ae046dd69 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -102,6 +102,10 @@ int pcie_failed_link_retrain(struct pci_dev *dev) !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) return ret; + speed_cap = pcie_get_speed_cap(dev); + if (speed_cap <= PCIE_SPEED_2_5GT) + return ret; + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { @@ -111,10 +115,8 @@ int pcie_failed_link_retrain(struct pci_dev *dev) goto err; } - speed_cap = pcie_get_speed_cap(dev); pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - speed_cap > PCIE_SPEED_2_5GT) { + if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); ret = pcie_set_target_speed(dev, speed_cap, false); if (ret) From c368dd5cbd61ffab2b6f8a89b0d5775e2e16cde6 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 28 Apr 2026 16:46:12 +0800 Subject: [PATCH 0566/5214] soundwire: don't program SDW_SCP_BUSCLOCK_SCALE on a unattached Peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDW_SCP_BUSCLOCK_SCALE register will be programmed when the Peripheral is attached. We can and should skip programming the SDW_SCP_BUSCLOCK_SCALE register when the Peripheral is unattached. Fixes: 645291cfe5e5 ("Soundwire: stream: program BUSCLOCK_SCALE") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260428084612.322701-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index c1ef177a0021..cdac009b1a75 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -697,6 +697,13 @@ static int sdw_program_params(struct sdw_bus *bus, bool prepare) if (scale_index < 0) return scale_index; + /* Skip the unattached Peripherals */ + if (!completion_done(&slave->enumeration_complete)) { + dev_warn(&slave->dev, + "Not enumerated, skip programming BUSCLOCK_SCALE\n"); + continue; + } + ret = sdw_write_no_pm(slave, addr1, scale_index); if (ret < 0) { dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n"); From f772ff5a0e6758fd412803c09e03ba3bca5f5878 Mon Sep 17 00:00:00 2001 From: "Baoli.Zhang" Date: Wed, 6 May 2026 13:50:35 +0800 Subject: [PATCH 0567/5214] soundwire: fix bug in sdw_add_element_group_count found by syzkaller The original implementation caused an out-of-bounds memory access in the sdw_add_element_group_count for-loop when i == num. for (i = 0; i <= num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) ... To fix this error, the function now checks for existing rate/lane entries in the group(a function parameter) using a for-loop before adding them. No functional changes apart from this fix. Fixes: 9026118f20e2 ("soundwire: Add generic bandwidth allocation algorithm") Reviewed-by: Bard Liao Reviewed-by: Andy Shevchenko Signed-off-by: Baoli.Zhang Link: https://patch.msgid.link/20260506055039.3751028-2-baoli.zhang@linux.intel.com Signed-off-by: Vinod Koul --- .../soundwire/generic_bandwidth_allocation.c | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index fb3970e12dac..f016ad088a1d 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -299,39 +299,36 @@ static int sdw_add_element_group_count(struct sdw_group *group, int num = group->count; int i; - for (i = 0; i <= num; i++) { + for (i = 0; i < num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) - break; - - if (i != num) - continue; - - if (group->count >= group->max_size) { - unsigned int *rates; - unsigned int *lanes; - - group->max_size += 1; - rates = krealloc(group->rates, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!rates) - return -ENOMEM; - - group->rates = rates; - - lanes = krealloc(group->lanes, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!lanes) - return -ENOMEM; - - group->lanes = lanes; - } - - group->rates[group->count] = rate; - group->lanes[group->count++] = lane; + return 0; } + if (group->count >= group->max_size) { + unsigned int *rates; + unsigned int *lanes; + + group->max_size += 1; + rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!rates) + return -ENOMEM; + + group->rates = rates; + + lanes = krealloc(group->lanes, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!lanes) + return -ENOMEM; + + group->lanes = lanes; + } + + group->rates[group->count] = rate; + group->lanes[group->count++] = lane; + return 0; } From 654a7ae10b2ee6b07d5d9193c1c5465410781908 Mon Sep 17 00:00:00 2001 From: "Baoli.Zhang" Date: Wed, 6 May 2026 13:50:36 +0800 Subject: [PATCH 0568/5214] soundwire: increase group->max_size after allocation Only update `group->max_size` after both allocations succeed to avoid leaving the group's state inconsistent if one allocation fails. Signed-off-by: Baoli.Zhang Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260506055039.3751028-3-baoli.zhang@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index f016ad088a1d..cd9ccbaf0e46 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -308,9 +308,8 @@ static int sdw_add_element_group_count(struct sdw_group *group, unsigned int *rates; unsigned int *lanes; - group->max_size += 1; rates = krealloc(group->rates, - (sizeof(int) * group->max_size), + sizeof(int) * (group->max_size + 1), GFP_KERNEL); if (!rates) return -ENOMEM; @@ -318,12 +317,14 @@ static int sdw_add_element_group_count(struct sdw_group *group, group->rates = rates; lanes = krealloc(group->lanes, - (sizeof(int) * group->max_size), + sizeof(int) * (group->max_size + 1), GFP_KERNEL); if (!lanes) return -ENOMEM; group->lanes = lanes; + + group->max_size += 1; } group->rates[group->count] = rate; From 35a5ab8ef7f0f00b30eab9d917f3f0f4a2bec5d6 Mon Sep 17 00:00:00 2001 From: "Baoli.Zhang" Date: Wed, 6 May 2026 13:50:37 +0800 Subject: [PATCH 0569/5214] soundwire: use krealloc_array to prevent integer overflow Replace the use of krealloc() with krealloc_array() in sdw_add_element_group_count to mitigate the risk of integer overflow during memory allocation size calculation. Suggested-by: Andy Shevchenko Signed-off-by: Baoli.Zhang Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260506055039.3751028-4-baoli.zhang@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/generic_bandwidth_allocation.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index cd9ccbaf0e46..3575d69ce1c5 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -308,17 +308,15 @@ static int sdw_add_element_group_count(struct sdw_group *group, unsigned int *rates; unsigned int *lanes; - rates = krealloc(group->rates, - sizeof(int) * (group->max_size + 1), - GFP_KERNEL); + rates = krealloc_array(group->rates, group->max_size + 1, + sizeof(*group->rates), GFP_KERNEL); if (!rates) return -ENOMEM; group->rates = rates; - lanes = krealloc(group->lanes, - sizeof(int) * (group->max_size + 1), - GFP_KERNEL); + lanes = krealloc_array(group->lanes, group->max_size + 1, + sizeof(*group->lanes), GFP_KERNEL); if (!lanes) return -ENOMEM; From 01a20f1c46ed8fdbc9c4c97de60fae1a49f81b48 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 11:08:16 +0100 Subject: [PATCH 0570/5214] clk: renesas: r9a08g046: Add IA55_PCLK to critical module clocks Add R9A08G046_IA55_PCLK to the critical module clocks list to prevent the clock from being gated during suspend, as it is required for the interrupt controller (IA55) to function correctly. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430100838.157306-1-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index ce9503c3cfd1..0004b9516fdf 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -312,6 +312,7 @@ static const struct rzg2l_reset r9a08g046_resets[] = { static const unsigned int r9a08g046_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A08G046_GIC600_GICCLK, + MOD_CLK_BASE + R9A08G046_IA55_PCLK, MOD_CLK_BASE + R9A08G046_IA55_CLK, MOD_CLK_BASE + R9A08G046_DMAC_ACLK, }; From 44c1733331eb691493c29839520ed31edda34d8d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:20:17 +0200 Subject: [PATCH 0571/5214] clk: renesas: rzg2l: Consolidate DEF_MUX() and DEF_MUX_FLAGS() Define DEF_MUX() using DEF_MUX_FLAGS(), to reduce duplication. Signed-off-by: Geert Uytterhoeven Reviewed-by: Biju Das Link: https://patch.msgid.link/46e2713f39cc5efc4b05a65723e0781a9dd8291c.1777562043.git.geert+renesas@glider.be --- drivers/clk/renesas/rzg2l-cpg.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 0e63b62e8435..33f54ba0e64e 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -178,22 +178,19 @@ enum clk_types { .invalid_rate = _invalid_rate, \ .max_rate = _max_rate, .flag = (_clk_flags), \ .notifier = _notif) -#define DEF_MUX(_name, _id, _conf, _parent_names) \ - DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ - .parent_names = _parent_names, \ - .num_parents = ARRAY_SIZE(_parent_names), \ - .mux_flags = CLK_MUX_HIWORD_MASK) -#define DEF_MUX_RO(_name, _id, _conf, _parent_names) \ - DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ - .parent_names = _parent_names, \ - .num_parents = ARRAY_SIZE(_parent_names), \ - .mux_flags = CLK_MUX_READ_ONLY) #define DEF_MUX_FLAGS(_name, _id, _conf, _parent_names, _flag) \ DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ .mux_flags = CLK_MUX_HIWORD_MASK, \ .flag = _flag) +#define DEF_MUX(_name, _id, _conf, _parent_names) \ + DEF_MUX_FLAGS(_name, _id, _conf, _parent_names, 0) +#define DEF_MUX_RO(_name, _id, _conf, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .mux_flags = CLK_MUX_READ_ONLY) #define DEF_SD_MUX(_name, _id, _conf, _sconf, _parent_names, _mtable, _clk_flags, _notifier) \ DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, .sconf = _sconf, \ .parent_names = _parent_names, \ From 33cd08ac6200a8f96ff26183433affc10bead0ed Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:20:18 +0200 Subject: [PATCH 0572/5214] clk: renesas: rzg2l: Refactor rzg3l_cpg_pll_clk_endisable() Reduce duplication by introducing mon_mask. Eliminate an else branch by moving common parts into variable pre-initializations. Signed-off-by: Geert Uytterhoeven Reviewed-by: Biju Das Link: https://patch.msgid.link/9cda94b9b37c562a305f4dd6091fd71246764fd2.1777562043.git.geert+renesas@glider.be --- drivers/clk/renesas/rzg2l-cpg.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index f98b6eb4f501..426e93dc7a98 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1197,27 +1197,25 @@ static int rzg3l_cpg_pll_clk_endisable(struct clk_hw *hw, bool enable) { struct pll_clk *pll_clk = to_pll(hw); struct rzg2l_cpg_priv *priv = pll_clk->priv; + u32 mon_mask = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; + u32 val = RZG3L_PLL_STBY_RESETB_WEN; u32 stby_offset, mon_offset; - u32 val, mon_val; + u32 mon_val = 0; int ret; stby_offset = RZG3L_PLL_STBY_OFFSET(pll_clk->conf); mon_offset = RZG3L_PLL_MON_OFFSET(pll_clk->conf); if (enable) { - val = RZG3L_PLL_STBY_RESETB_WEN | RZG3L_PLL_STBY_RESETB; - mon_val = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; - } else { - val = RZG3L_PLL_STBY_RESETB_WEN; - mon_val = 0; + val |= RZG3L_PLL_STBY_RESETB; + mon_val = mon_mask; } writel(val, priv->base + stby_offset); /* ensure PLL is in normal/standby mode */ - ret = readl_poll_timeout_atomic(priv->base + mon_offset, val, mon_val == - (val & (RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK)), - 10, 100); + ret = readl_poll_timeout_atomic(priv->base + mon_offset, val, + mon_val == (val & mon_mask), 10, 100); if (ret) dev_err(priv->dev, "Failed to %s PLL 0x%x/%pC\n", enable ? "enable" : "disable", stby_offset, hw->clk); From 7f0c422c7fbfd9294ff9321ada0c63561e5c6ea0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:20:16 +0200 Subject: [PATCH 0573/5214] clk: renesas: cpg-mssr: Add number of clock cells check The number of clock cells is not validated in the clock provider's clk_src_get() callback. Add the missing check. Signed-off-by: Geert Uytterhoeven Reviewed-by: Biju Das Link: https://patch.msgid.link/46e010659ffdffd5e3541369f3b65d43ebe236ec.1777562043.git.geert+renesas@glider.be --- drivers/clk/renesas/renesas-cpg-mssr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 26ea85cfaa02..5b84cbee030b 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -370,6 +370,9 @@ struct clk *cpg_mssr_clk_src_twocell_get(struct of_phandle_args *clkspec, struct clk *clk; int range_check; + if (clkspec->args_count != 2) + return ERR_PTR(-EINVAL); + switch (clkspec->args[0]) { case CPG_CORE: type = "core"; From 0913cbfaa912f539f1275099ff75303948afad88 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 26 Jan 2026 11:53:33 +0200 Subject: [PATCH 0574/5214] media: imx219: Rename "PIXEL_ARRAY" as "ACTIVE_AREA" The imx219 driver uses macros for denoting the size of the pixel array. The values reflect the area of manufacturer-designated visible pixels, reflect this in the naming by calling it "ACTIVE_AREA" instead of "PIXEL_ARRAY". Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx219.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7da02ce5da15..a09699299b4e 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -142,10 +142,10 @@ /* IMX219 native and active pixel array size. */ #define IMX219_NATIVE_WIDTH 3296U #define IMX219_NATIVE_HEIGHT 2480U -#define IMX219_PIXEL_ARRAY_LEFT 8U -#define IMX219_PIXEL_ARRAY_TOP 8U -#define IMX219_PIXEL_ARRAY_WIDTH 3280U -#define IMX219_PIXEL_ARRAY_HEIGHT 2464U +#define IMX219_ACTIVE_AREA_LEFT 8U +#define IMX219_ACTIVE_AREA_TOP 8U +#define IMX219_ACTIVE_AREA_WIDTH 3280U +#define IMX219_ACTIVE_AREA_HEIGHT 2464U /* Mode : resolution and related config&values */ struct imx219_mode { @@ -675,13 +675,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT, &ret); cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT + crop->width - 1, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, - crop->top - IMX219_PIXEL_ARRAY_TOP, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, - crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP + crop->height - 1, &ret); imx219_get_binning(state, &bin_h, &bin_v); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); @@ -867,8 +867,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, * Use binning to maximize the crop rectangle size, and centre it in the * sensor. */ - bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); - bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + bin_h = min(IMX219_ACTIVE_AREA_WIDTH / format->width, 2U); + bin_v = min(IMX219_ACTIVE_AREA_HEIGHT / format->height, 2U); /* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */ binning = min(bin_h, bin_v); @@ -967,10 +967,10 @@ static int imx219_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + sel->r.top = IMX219_ACTIVE_AREA_TOP; + sel->r.left = IMX219_ACTIVE_AREA_LEFT; + sel->r.width = IMX219_ACTIVE_AREA_WIDTH; + sel->r.height = IMX219_ACTIVE_AREA_HEIGHT; return 0; } From 2c4f1ba7354312ad2d6e34e70a518a51a9344715 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Feb 2026 01:38:15 +0200 Subject: [PATCH 0575/5214] media: imx219: Fix maximum frame length in lines The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it. Fixes: 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Dave Stevenson Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index a09699299b4e..0b9ff639e9f7 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -72,7 +72,7 @@ /* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe #define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_LLP_MIN 0x0d78 From 224a4333fab19b7ccb321accdbbd969de0cc247f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 Mar 2026 15:27:30 +0200 Subject: [PATCH 0576/5214] media: imx219: Set horizontal blanking on mode change The driver UAPI is mode-based, allowing the user to choose a mode from a small list based on the output size. The vertical blanking is set based on the mode, do the same for horizontal blanking so the frame rate obtained is constant. Additionally, it's best to use a known-good horizontal blanking value as choosing the value freely may affect image quality. While the minimum value may not be the best value for horizontal blanking, at least it is constant rather than a minimum value of a different configuration. Signed-off-by: Sakari Ailus Reviewed-by: Dave Stevenson Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0b9ff639e9f7..223d3753cc93 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -837,11 +837,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; u8 bin_h, bin_v, binning; - u32 prev_line_len; int ret; format = v4l2_subdev_state_get_format(state, 0); - prev_line_len = format->width + imx219->hblank->val; /* * Adjust the requested format to match the closest mode. The Bayer @@ -882,7 +880,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; - int hblank, llp_min; + int llp_min; int pixel_rate; /* Update limits and set FPS to default */ @@ -924,15 +922,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, llp_min - mode->width); if (ret) return ret; - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_line_len - mode->width; - ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + ret = __v4l2_ctrl_s_ctrl(imx219->hblank, llp_min - mode->width); if (ret) return ret; From d07c23216d8bd4dfadc33911259033984309e8a7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:41:34 +0300 Subject: [PATCH 0577/5214] media: imx274: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx274.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 8ec78b60bea6..241821572e03 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -897,14 +897,6 @@ static int imx274_regulators_get(struct device *dev, struct stimx274 *imx274) imx274->supplies); } -/** - * imx274_s_ctrl - This is used to set the imx274 V4L2 controls - * @ctrl: V4L2 control to be set - * - * This function is used to set the V4L2 controls for the imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -1059,16 +1051,6 @@ static int __imx274_change_compose(struct stimx274 *imx274, return 0; } -/** - * imx274_get_fmt - Get the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to get the pad format information. - * - * Return: 0 on success - */ static int imx274_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1081,16 +1063,6 @@ static int imx274_get_fmt(struct v4l2_subdev *sd, return 0; } -/** - * imx274_set_fmt - This is used to set the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state information structure - * @format: Pointer to pad level media bus format - * - * This function is used to set the pad format. - * - * Return: 0 on success - */ static int imx274_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -1423,16 +1395,6 @@ static void imx274_load_default(struct stimx274 *priv) priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED; } -/** - * imx274_s_stream - It is used to start/stop the streaming. - * @sd: V4L2 Sub device - * @on: Flag (True / False) - * - * This function controls the start or stop of streaming for the - * imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_stream(struct v4l2_subdev *sd, int on) { struct stimx274 *imx274 = to_imx274(sd); From bf4430a0c9309faf5dd0aab86eb449fe01b8d534 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:43:33 +0300 Subject: [PATCH 0578/5214] media: imx334: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx334.c | 93 -------------------------------------- 1 file changed, 93 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 9654f9268056..553a16b84f4d 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -566,18 +566,6 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) return ret; } -/** - * imx334_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx334 *imx334 = @@ -678,14 +666,6 @@ static int imx334_get_format_code(struct imx334 *imx334, u32 code) return imx334_mbus_codes[0]; } -/** - * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -698,14 +678,6 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx334_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -749,14 +721,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx334_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -776,14 +740,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx334_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -815,13 +771,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx334_init_state() - Initialize sub-device state - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -856,15 +805,6 @@ static int imx334_set_framefmt(struct imx334 *imx334) return -EINVAL; } -/** - * imx334_enable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are enabled - * @streams_mask: bitmask specifying the streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -929,15 +869,6 @@ static int imx334_enable_streams(struct v4l2_subdev *sd, return ret; } -/** - * imx334_disable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are disabled - * @streams_mask: bitmask specifying the streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1067,12 +998,6 @@ static const struct v4l2_subdev_internal_ops imx334_internal_ops = { .init_state = imx334_init_state, }; -/** - * imx334_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1101,12 +1026,6 @@ static int imx334_power_on(struct device *dev) return ret; } -/** - * imx334_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1125,6 @@ static int imx334_init_controls(struct imx334 *imx334) return 0; } -/** - * imx334_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_probe(struct i2c_client *client) { struct imx334 *imx334; @@ -1311,12 +1224,6 @@ static int imx334_probe(struct i2c_client *client) return ret; } -/** - * imx334_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 28aeed7586637b860201bad419e01f800d7cb7f0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:09 +0300 Subject: [PATCH 0579/5214] media: imx335: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx335.c | 87 -------------------------------------- 1 file changed, 87 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 5790aa4fabeb..1f777a1a8192 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -698,18 +698,6 @@ static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) return ret; } -/** - * imx335_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx335 *imx335 = @@ -800,14 +788,6 @@ static int imx335_get_format_code(struct imx335 *imx335, u32 code) return imx335_mbus_codes[0]; } -/** - * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -820,14 +800,6 @@ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -871,14 +843,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx335_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -923,13 +887,6 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx335_init_state() - Initialize sub-device state - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -947,14 +904,6 @@ static int imx335_init_state(struct v4l2_subdev *sd, return imx335_set_pad_format(sd, sd_state, &fmt); } -/** - * imx335_get_selection() - Selection API - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @sel: V4L2 selection info - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -1011,15 +960,6 @@ static int imx335_set_framefmt(struct imx335 *imx335) return ret; } -/** - * imx335_enable_streams() - Enable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to enable - * @streams_mask: Bitmask of streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1097,15 +1037,6 @@ static int imx335_enable_streams(struct v4l2_subdev *sd, return ret; } -/** - * imx335_disable_streams() - Disable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to disable - * @streams_mask: Bitmask of streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1299,12 +1230,6 @@ static int imx335_power_on(struct device *dev) return ret; } -/** - * imx335_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1430,12 +1355,6 @@ static int imx335_init_controls(struct imx335 *imx335) return 0; } -/** - * imx335_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_probe(struct i2c_client *client) { struct imx335 *imx335; @@ -1530,12 +1449,6 @@ static int imx335_probe(struct i2c_client *client) return ret; } -/** - * imx335_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx335_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 63652fab67450ff3e334975fae509fd464be224b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:28 +0300 Subject: [PATCH 0580/5214] media: imx412: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx412.c | 82 -------------------------------------- 1 file changed, 82 deletions(-) diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index e25e0a9ff65c..2705af2f16c0 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -570,18 +570,6 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) return ret; } -/** - * imx412_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx412 *imx412 = @@ -634,14 +622,6 @@ static const struct v4l2_ctrl_ops imx412_ctrl_ops = { .s_ctrl = imx412_set_ctrl, }; -/** - * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -654,14 +634,6 @@ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -701,14 +673,6 @@ static void imx412_fill_pad_format(struct imx412 *imx412, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx412_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -731,14 +695,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx412_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -768,13 +724,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx412_init_state() - Initialize sub-device state - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -840,13 +789,6 @@ static int imx412_stop_streaming(struct imx412 *imx412) 1, IMX412_MODE_STANDBY); } -/** - * imx412_set_stream() - Enable sensor streaming - * @sd: pointer to imx412 subdevice - * @enable: set to enable sensor streaming - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_stream(struct v4l2_subdev *sd, int enable) { struct imx412 *imx412 = to_imx412(sd); @@ -1010,12 +952,6 @@ static const struct v4l2_subdev_internal_ops imx412_internal_ops = { .init_state = imx412_init_state, }; -/** - * imx412_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1053,12 +989,6 @@ static int imx412_power_on(struct device *dev) return ret; } -/** - * imx412_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1159,12 +1089,6 @@ static int imx412_init_controls(struct imx412 *imx412) return 0; } -/** - * imx412_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_probe(struct i2c_client *client) { struct imx412 *imx412; @@ -1254,12 +1178,6 @@ static int imx412_probe(struct i2c_client *client) return ret; } -/** - * imx412_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx412_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 5fba5f916c158ffeb82627dabbe52f48fb79dcbe Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:42 +0300 Subject: [PATCH 0581/5214] media: ov9282: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/ov9282.c | 67 -------------------------------------- 1 file changed, 67 deletions(-) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 2167fb73ea41..5b6f897a74fc 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -586,18 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value) return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR); } -/** - * ov9282_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov9282 *ov9282 = @@ -704,14 +692,6 @@ static const struct v4l2_ctrl_ops ov9282_ctrl_ops = { .try_ctrl = ov9282_try_ctrl, }; -/** - * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -780,14 +760,6 @@ static void ov9282_fill_pad_format(struct ov9282 *ov9282, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * ov9282_get_pad_format() - Get subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -807,14 +779,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * ov9282_set_pad_format() - Set subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -852,13 +816,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * ov9282_init_state() - Initialize sub-device state - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -1157,12 +1114,6 @@ static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { .init_state = ov9282_init_state, }; -/** - * ov9282_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1157,6 @@ static int ov9282_power_on(struct device *dev) return ret; } -/** - * ov9282_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1333,12 +1278,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282) return 0; } -/** - * ov9282_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_probe(struct i2c_client *client) { struct ov9282 *ov9282; @@ -1435,12 +1374,6 @@ static int ov9282_probe(struct i2c_client *client) return ret; } -/** - * ov9282_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void ov9282_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From e11fb2d3a4dfd32b5f50e89892799729ee1a8049 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:54 +0300 Subject: [PATCH 0582/5214] media: tvp514x: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/tvp514x.c | 55 +------------------------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index f9c9c80c33ac..7af8f37646d6 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -686,13 +686,6 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @ctrl: pointer to v4l2_ctrl structure - * - * If the requested control is supported, sets the control's current - * value in HW. Otherwise, returns -EINVAL if the control is not supported. - */ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -789,13 +782,6 @@ tvp514x_set_frame_interval(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { int err = 0; @@ -850,14 +836,6 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { .s_ctrl = tvp514x_s_ctrl, }; -/** - * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * Enumertaes mbus codes supported - */ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -877,14 +855,6 @@ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @format: pointer to v4l2_subdev_format structure - * - * Retrieves pad format which is active or tried based on requirement - */ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -909,14 +879,6 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @fmt: pointer to v4l2_subdev_format structure - * - * Set pad format for the output pad - */ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1014,15 +976,7 @@ tvp514x_get_pdata(struct i2c_client *client) return pdata; } -/** - * tvp514x_probe() - decoder driver i2c probe handler - * @client: i2c driver client device structure - * - * Register decoder as an i2c client device and V4L2 - * device. - */ -static int -tvp514x_probe(struct i2c_client *client) +static int tvp514x_probe(struct i2c_client *client) { struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client); struct tvp514x_decoder *decoder; @@ -1113,13 +1067,6 @@ tvp514x_probe(struct i2c_client *client) return ret; } -/** - * tvp514x_remove() - decoder driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister decoder as an i2c client device and V4L2 - * device. Complement of tvp514x_probe(). - */ static void tvp514x_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From ef56c563c4b756c6ab7ee02ea5d3b7cb7b70748c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 6 Feb 2026 11:38:18 +0200 Subject: [PATCH 0583/5214] media: Documentation: Improve LINK_FREQ documentation Add a reference to the LINK_FREQ control and clarify the meaning of the control as for C-PHY the matter is less obvious. Signed-off-by: Sakari Ailus Reviewed-by: Mirela Rabulea Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- Documentation/driver-api/media/tx-rx.rst | 3 ++- .../userspace-api/media/v4l/ext-ctrls-image-process.rst | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/media/tx-rx.rst b/Documentation/driver-api/media/tx-rx.rst index 22e1b13ecde9..7df2407817b3 100644 --- a/Documentation/driver-api/media/tx-rx.rst +++ b/Documentation/driver-api/media/tx-rx.rst @@ -93,7 +93,8 @@ where * - variable or constant - description * - link_freq - - The value of the ``V4L2_CID_LINK_FREQ`` integer64 menu item. + - The value of the :ref:`V4L2_CID_LINK_FREQ ` integer64 + menu item. * - nr_of_lanes - Number of data lanes used on the CSI-2 link. * - 2 diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst index 6d516f041ca2..57c2baf34acf 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst @@ -24,7 +24,10 @@ Image Process Control IDs .. _v4l2-cid-link-freq: ``V4L2_CID_LINK_FREQ (integer menu)`` - The frequency of the data bus (e.g. parallel or CSI-2). + The fundamental frequency of the operating symbol rate (serial interfaces + such as CSI-2) or the sampling rate (parallel interfaces such as DVP or + Bt.565) of the data interface. For CSI-2, the frequency is equal to + _1 / (2 * UI)_. .. _v4l2-cid-pixel-rate: From 1155f8e0ccb36570caa2aabe5f00e617deed3a59 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 25 Aug 2025 13:07:50 +0300 Subject: [PATCH 0584/5214] media: v4l2-subdev: Refactor returning routes Refactor returning the routes by adding a new function that essentially does a memcopy and sets the number of the routes in the routing table. Signed-off-by: Sakari Ailus Reviewed-by: Michael Riesch Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 33 ++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d00d27d49060..2e927ec336e5 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -629,6 +629,18 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, v4l2_subdev_get_unlocked_active_state(sd); } +static void v4l2_subdev_copy_routes(struct v4l2_subdev_routing *routing, + const struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *routes = + (struct v4l2_subdev_route *)(uintptr_t)routing->routes; + u32 copy_routes = min(routing->len_routes, state->routing.num_routes); + + memcpy(routes, state->routing.routes, sizeof(*routes) * copy_routes); + + routing->num_routes = state->routing.num_routes; +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_state *state) { @@ -1000,7 +1012,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_G_ROUTING: { struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting *krouting; if (!v4l2_subdev_enable_streams_api) return -ENOIOCTLCMD; @@ -1010,13 +1021,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, memset(routing->reserved, 0, sizeof(routing->reserved)); - krouting = &state->routing; - - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - krouting->routes, - min(krouting->num_routes, routing->len_routes) * - sizeof(*krouting->routes)); - routing->num_routes = krouting->num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1084,11 +1089,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, * the routing table. */ if (!v4l2_subdev_has_op(sd, pad, set_routing)) { - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1102,11 +1103,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (rval < 0) return rval; - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } From bc1ba628e37c93cf2abeb2c79716f49087f8a024 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 5 Feb 2026 10:19:35 +0200 Subject: [PATCH 0585/5214] media: v4l2-subdev: Allow accessing routes with STREAMS client capability Disable access to routes when the STREAMS client capability bit isn't set. Routes aren't relevant otherwise anyway. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Mirela Rabulea Reviewed-by: Michael Riesch Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2e927ec336e5..e9f81b9be9e2 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1019,6 +1019,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; + if (!client_supports_streams) + return -EINVAL; + memset(routing->reserved, 0, sizeof(routing->reserved)); v4l2_subdev_copy_routes(routing, state); @@ -1040,6 +1043,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; + if (!client_supports_streams) + return -EINVAL; + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; From d280b7cd7131fdfd1ecf09d1fb30d250a84ca16a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 May 2026 11:00:06 +0200 Subject: [PATCH 0586/5214] pinctrl: qcom: nord: remove duplicated pin function The qdss_cti function is initialized twice in the nord_functions array. Remove the duplicate entry. Fixes: c24dd0826f06 ("pinctrl: qcom: add the TLMM driver for the Nord platforms") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605061633.BJLI5voT-lkp@intel.com/ Signed-off-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-nord.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-nord.c b/drivers/pinctrl/qcom/pinctrl-nord.c index f14361101bf4..7c21306e77ff 100644 --- a/drivers/pinctrl/qcom/pinctrl-nord.c +++ b/drivers/pinctrl/qcom/pinctrl-nord.c @@ -1417,7 +1417,6 @@ static const struct pinfunction nord_functions[] = { MSM_PIN_FUNCTION(prng_rosc0), MSM_PIN_FUNCTION(prng_rosc1), MSM_PIN_FUNCTION(pwrbrk_i_n), - MSM_PIN_FUNCTION(qdss_cti), MSM_PIN_FUNCTION(qdss), MSM_PIN_FUNCTION(qdss_cti), MSM_PIN_FUNCTION(qspi), From db8e26c435698222b333cafcbc043dfdf083d591 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Mon, 27 Apr 2026 11:40:33 +0200 Subject: [PATCH 0587/5214] backlight: cgbc: Remove redundant X86 dependency The backlight driver depends on the MFD cgbc-core driver, which already depends on X86. The explicit X86 dependency for the backlight driver is redundant and can be safely removed. Signed-off-by: Thomas Richard Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260427-backlight-cgbc-remove-x86-dependency-v2-1-da9f2375a34a@bootlin.com Signed-off-by: Lee Jones --- drivers/video/backlight/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index f4e99542ffe8..7aa1c4b21111 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -260,7 +260,7 @@ config BACKLIGHT_PWM config BACKLIGHT_CGBC tristate "Congatec Board Controller (CGBC) backlight support" - depends on MFD_CGBC && X86 + depends on MFD_CGBC help Say Y here to enable support for LCD backlight control on Congatec x86-based boards via the CGBC (Congatec Board Controller). From acea35b567d0f610d533ca661f4106ac12583f78 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Mon, 27 Apr 2026 11:40:34 +0200 Subject: [PATCH 0588/5214] MAINTAINERS: Add cgbc backlight driver Add missing backlight driver in CONGATEC BOARD CONTROLLER entry. Signed-off-by: Thomas Richard Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260427-backlight-cgbc-remove-x86-dependency-v2-2-da9f2375a34a@bootlin.com Signed-off-by: Lee Jones --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1c0d739a88d8..a7ce6bedfcca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6474,6 +6474,7 @@ F: drivers/gpio/gpio-cgbc.c F: drivers/hwmon/cgbc-hwmon.c F: drivers/i2c/busses/i2c-cgbc.c F: drivers/mfd/cgbc-core.c +F: drivers/video/backlight/cgbc_bl.c F: drivers/watchdog/cgbc_wdt.c F: include/linux/mfd/cgbc.h From f195d54deef1bc6dd3326394975baff02c7ae487 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 17 Feb 2026 13:19:43 +0000 Subject: [PATCH 0589/5214] coresight: tmc: Fix overflow when calculating is bigger than 2GiB When specifying a 2GB AUX buffer, the ETR driver ends up allocating only a 1MB buffer instead: # echo 'file coresight-tmc-etr.c +p' > \ /sys/kernel/debug/dynamic_debug/control # perf record -e cs_etm/@tmc_etr0,timestamp=0/u -C 0 -m ,2G -- test coresight tmc_etr0: allocated buffer of size 1024KB in mode 0 The page index is an 'int' type, and shifting it by PAGE_SHIFT overflows when the resulting value exceeds 2GB. This produces a negative value, causing the driver to fall back to the minimum buffer size (1MB). Cast the page index to a wider type to accommodate large buffer sizes. Also fix a similar issue in the buffer offset calculation. Reported-by: Michiel van Tol Fixes: 99443ea19e8b ("coresight: Add generic TMC sg table framework") Fixes: eebe8dbd8630 ("coresight: tmc: Decouple the perf buffer allocation from sysfs mode") Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260217-arm_coresight_fix_big_buffer_size-v1-1-774e893d8e3f@arm.com --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 4dc1defe27a5..361a433e6f0c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -154,7 +154,7 @@ tmc_pages_get_offset(struct tmc_pages *tmc_pages, dma_addr_t addr) for (i = 0; i < tmc_pages->nr_pages; i++) { page_start = tmc_pages->daddrs[i]; if (addr >= page_start && addr < (page_start + PAGE_SIZE)) - return i * PAGE_SIZE + (addr - page_start); + return (long)i * PAGE_SIZE + (addr - page_start); } return -EINVAL; @@ -1379,7 +1379,7 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event, node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); /* Use the minimum limit if the required size is smaller */ - size = nr_pages << PAGE_SHIFT; + size = (ssize_t)nr_pages << PAGE_SHIFT; size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE); /* From 2ab4645fe4206c142a5f1491e191c906279686cf Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 5 May 2026 17:51:25 +0100 Subject: [PATCH 0590/5214] coresight: ete: Always save state on power down System register ETMs and ETE are unlikely to be preserved on CPU power down. The ETE DT binding also never documented "arm,coresight-loses-context-with-cpu" so nobody would have legitimately been able to use that binding to fix it and ACPI has no such binding at all. Fix it by hard coding the setting for sysreg ETMs (ETE is always sysreg) or ACPI boots. Use a local variable when setting up save_state so that it's immune to concurrent probing when devices have different configurations which is an issue with modifying the global. This fixes the following error when using Coresight with ACPI on the FVP which supports CPU PM: coresight ete0: External agent took claim tag WARNING: drivers/hwtracing/coresight/coresight-core.c:248 at coresight_disclaim_device_unlocked+0xe0/0xe8, CPU#0: perf/117 Fixes: 35e1c9163e02 ("coresight: ete: Add support for ETE tracing") Signed-off-by: James Clark Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260505-james-cs-ete-pm_save_enable-v3-1-485d21dd79b8@linaro.org --- .../coresight/coresight-etm4x-core.c | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d565a73f0042..591dfe0bc635 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -56,10 +56,14 @@ MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); #define PARAM_PM_SAVE_NEVER 1 /* never save any state */ #define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */ +/* + * Save option for ETM4. ETE, sysreg ETM4s and ACPI boots ignore this option and + * will always save. + */ static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE; module_param(pm_save_enable, int, 0444); MODULE_PARM_DESC(pm_save_enable, - "Save/restore state on power down: 1 = never, 2 = self-hosted"); + "Save/restore state on power down: 1 = never, 2 = self-hosted. MMIO and DT only."); static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); @@ -2012,7 +2016,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) { int ret = 0; - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return 0; /* @@ -2127,7 +2131,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) { - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return; if (coresight_get_mode(drvdata->csdev)) @@ -2212,6 +2216,17 @@ static void etm4_pm_clear(void) } } +static bool etm4x_always_pm_save(struct device *dev, struct csdev_access *csa) +{ + /* + * Only IO mem ETM devices will benefit from skipping PM save and only + * DT has the option to control it, not ACPI. Otherwise system register + * based ETMs and ETEs will always lose context on CPU power down, so + * always save. + */ + return !csa->io_mem || is_acpi_device_node(dev_fwnode(dev)); +} + static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) { int ret; @@ -2221,6 +2236,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) struct coresight_desc desc = { 0 }; u8 major, minor; char *type_name; + bool pm_save; if (!drvdata) return -EINVAL; @@ -2248,6 +2264,21 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) etm4_set_default(&drvdata->config); + if (etm4x_always_pm_save(dev, init_arg->csa)) + pm_save = true; + else if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) + pm_save = coresight_loses_context_with_cpu(dev); + else + pm_save = pm_save_enable != PARAM_PM_SAVE_NEVER; + + if (pm_save) { + drvdata->save_state = devm_kmalloc(dev, + sizeof(struct etmv4_save_state), + GFP_KERNEL); + if (!drvdata->save_state) + return -ENOMEM; + } + pdata = coresight_get_platform_data(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); @@ -2305,17 +2336,6 @@ static int etm4_probe(struct device *dev) if (ret) return ret; - if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) - pm_save_enable = coresight_loses_context_with_cpu(dev) ? - PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER; - - if (pm_save_enable != PARAM_PM_SAVE_NEVER) { - drvdata->save_state = devm_kmalloc(dev, - sizeof(struct etmv4_save_state), GFP_KERNEL); - if (!drvdata->save_state) - return -ENOMEM; - } - raw_spin_lock_init(&drvdata->spinlock); drvdata->cpu = coresight_get_cpu(dev); From 0ec0a8785d21f63db520bd9d2a67c55e855d36a8 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Wed, 8 Apr 2026 13:31:43 +0100 Subject: [PATCH 0591/5214] coresight: etm4x: Correct TRCVMIDCCTLR1 save and restore It is a typo to use trcvmidcctlr0 to save and restore TRCVMIDCCTLR1. Use trcvmidcctlr1 instead. Fixes: f5bd523690d2 ("coresight: etm4x: Convert all register accesses") Signed-off-by: Leo Yan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260408-arm_cs_fix_trcvmidcctlr1_typo-v1-1-6a5695363b46@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 591dfe0bc635..a251375db24b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1983,7 +1983,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR1); + state->trcvmidcctlr1 = etm4x_read32(csa, TRCVMIDCCTLR1); state->trcclaimset = etm4x_read32(csa, TRCCLAIMCLR); @@ -2106,7 +2106,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR1); + etm4x_relaxed_write32(csa, state->trcvmidcctlr1, TRCVMIDCCTLR1); etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET); From b7710233c16c1f42e0b58a1f0485658fc4fa61c1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 11:54:47 -0700 Subject: [PATCH 0592/5214] Input: atmel_mxt_ts - use __free() for obuf in mxt_object_show Use the __free(kfree) macro for the obuf allocation in mxt_object_show() to simplify the code. Assisted-by: Gemini:gemini-3.1-pro Reviewed-by: Ricardo Ribalda Link: https://patch.msgid.link/20260504185448.4055973-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 26ba82fb60b6..a9e86ad7ed5e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2834,14 +2834,12 @@ static ssize_t mxt_object_show(struct device *dev, int count = 0; int i, j; int error; - u8 *obuf; /* Pre-allocate buffer large enough to hold max sized object. */ - obuf = kmalloc(256, GFP_KERNEL); + u8 *obuf __free(kfree) = kmalloc(256, GFP_KERNEL); if (!obuf) return -ENOMEM; - error = 0; for (i = 0; i < data->info->object_num; i++) { object = data->object_table + i; @@ -2856,15 +2854,13 @@ static ssize_t mxt_object_show(struct device *dev, error = __mxt_read_reg(data->client, addr, size, obuf); if (error) - goto done; + return error; count = mxt_show_instance(buf, count, object, j, obuf); } } -done: - kfree(obuf); - return error ?: count; + return count; } static int mxt_check_firmware_format(struct device *dev, From c855c9921da72e535c24737c748f603a52d03f7e Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Mon, 27 Apr 2026 21:01:04 -0700 Subject: [PATCH 0593/5214] PCI/ASPM: Don't reconfigure ASPM entering low-power state Reconfiguring ASPM when a device transitions to low-power state can enable L1.1/L1.2 substates on the PCIe link at a time when the device is sleeping and may be unable to exit them. ASPM should be reconfigured on D0 entry (resume), not on the way down. pci_set_low_power_state() calls pcie_aspm_pm_state_change() after writing D3hot to PCI_PM_CTRL. pcie_aspm_pm_state_change() resets link->aspm_capable to link->aspm_support and then calls pcie_config_aspm_path(), which can enable ASPM L1.1/L1.2 substates on the PCIe link. If the device cannot recover the link from L1.2 while in D3hot, subsequent config space reads return 0xFFFF ("device inaccessible") and pci_power_up() fails with messages like: vfio-pci 0000:5d:00.0: Unable to change power state from D3hot to D0, device inaccessible This was observed on NVIDIA H100 SXM5 GPUs bound to vfio-pci when Linux runtime PM suspends them to D3hot: the GPU becomes permanently inaccessible and disappears from the PCIe bus. The call to pcie_aspm_pm_state_change() in pci_set_low_power_state() was restored by f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()""), which reverted 08d0cc5f3426 ("PCI/ASPM: Remove pcie_aspm_pm_state_change()"). The revert was necessary because the removal broke suspend/resume on certain platforms that required ASPM to be reconfigured on D0 entry. However, the revert restored the call in both pci_set_full_power_state() (D0 entry) and pci_set_low_power_state() (low-power entry). Only the D0-entry call is needed to fix the suspend/resume regression. The low-power-entry call is harmful: reconfiguring ASPM immediately after putting a device into D3hot can enable link substates that the device or platform cannot exit while the device is sleeping. Remove the pcie_aspm_pm_state_change() call from pci_set_low_power_state(). ASPM will still be reconfigured correctly when the device returns to D0 via pci_set_full_power_state(). Fixes: f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()"") Signed-off-by: Carlos Bilbao (Lambda) Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260428040104.78524-1-carlos.bilbao@kernel.org --- drivers/pci/pci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..f97a300058ef 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1514,9 +1514,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool pci_power_name(dev->current_state), pci_power_name(state)); - if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self, locked); - return 0; } From 49133d4ae0c9ceb63fa042c0b46b34e26e1c6676 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Apr 2026 12:24:58 +0200 Subject: [PATCH 0594/5214] platform/x86: barco-p50-gpio: attach software node to its target GPIO device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The software node representing the GPIO controller to consumers is "dangling": it's not really attached to the device. The GPIO lookup relies on matching the name of the node to the chip's label. Switch to using platform_device_register_full() and use the swnode field of struct platform_device_info to attach the software node to the GPIO device. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260427102459.110332-1-bartosz.golaszewski@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/barco-p50-gpio.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 2a6d8607c402..e1400382465d 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -124,7 +124,6 @@ static const struct software_node vendor_key_node = { }; static const struct software_node *p50_swnodes[] = { - &gpiochip_node, &gpio_leds_node, &identify_led_node, &gpio_keys_node, @@ -424,6 +423,13 @@ MODULE_DEVICE_TABLE(dmi, dmi_ids); static int __init p50_module_init(void) { struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1); + struct platform_device_info pdevinfo = { + .name = DRIVER_NAME, + .id = PLATFORM_DEVID_NONE, + .res = &res, + .num_res = 1, + .swnode = &gpiochip_node, + }; int ret; if (!dmi_first_match(dmi_ids)) @@ -433,7 +439,7 @@ static int __init p50_module_init(void) if (ret) return ret; - gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1); + gpio_pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(gpio_pdev)) { pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev)); platform_driver_unregister(&p50_gpio_driver); From fa84425e53c43c8a17ac2af2c8f99202f4e1ee7e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Apr 2026 11:46:01 +0200 Subject: [PATCH 0595/5214] platform/x86: pcengines-apuv2: reduce indiraction in swnode assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct platform_device_info now has the 'swnode' field allowing passing software nodes directly to platform_device_register_full() without going through software_node_fwnode(). It supports both registered and unregistered software nodes. Use it to drop one layer of indirection. Signed-off-by: Bartosz Golaszewski Reviewed-by: Dmitry Torokhov Link: https://patch.msgid.link/20260429-pcengines-apuv2-swnodes-v1-1-cd1a6fad856d@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pcengines-apuv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 3f19589d1ba0..8ac5f719c5a3 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -262,7 +262,7 @@ static struct platform_device * __init apu_create_pdev(const char *name, .id = PLATFORM_DEVID_NONE, .data = data, .size_data = size, - .fwnode = software_node_fwnode(swnode), + .swnode = swnode, }; struct platform_device *pdev; int err; From 083a59e05db9203c346e87d822c0c12a0f7a7c85 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:58:56 +0200 Subject: [PATCH 0596/5214] ACPI: provide acpi_bus_find_device_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a helper allowing to locate an ACPI device by its name. Signed-off-by: Bartosz Golaszewski Acked-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260504-baytrail-real-swnode-v5-1-c7878b69e383@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/acpi/bus.c | 15 +++++++++++++++ include/linux/acpi.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ec095e2009e..357b39db345d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1181,6 +1181,21 @@ int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data) } EXPORT_SYMBOL_GPL(acpi_bus_for_each_dev); +/** + * acpi_bus_find_device_by_name() - Locate an ACPI device by its name + * @name: Name of the device to match + * + * The caller is responsible for calling put_device() on the returned object. + * + * Returns: + * New reference to the matched device or NULL if the device can't be found. + */ +struct device *acpi_bus_find_device_by_name(const char *name) +{ + return bus_find_device_by_name(&acpi_bus_type, NULL, name); +} +EXPORT_SYMBOL_GPL(acpi_bus_find_device_by_name); + struct acpi_dev_walk_context { int (*fn)(struct acpi_device *, void *); void *data; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 67effb91fa98..10d6c6c11bdf 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -798,6 +798,8 @@ int acpi_get_local_u64_address(acpi_handle handle, u64 *addr); int acpi_get_local_address(acpi_handle handle, u32 *addr); const char *acpi_get_subsystem_id(acpi_handle handle); +struct device *acpi_bus_find_device_by_name(const char *name); + #ifdef CONFIG_ACPI_MRRM int acpi_mrrm_max_mem_region(void); #endif @@ -1106,6 +1108,11 @@ static inline const char *acpi_get_subsystem_id(acpi_handle handle) return ERR_PTR(-ENODEV); } +static inline struct device *acpi_bus_find_device_by_name(const char *name) +{ + return NULL; +} + static inline int acpi_register_wakeup_handler(int wake_irq, bool (*wakeup)(void *context), void *context) { From 1448c2d2ca5cc7f4ea6694e6bc809946de0a751c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:58:57 +0200 Subject: [PATCH 0597/5214] platform/x86: x86-android-tablets: enable fwnode matching of GPIO chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to allow GPIOLIB to match cherryview and baytrail GPIO controllers by their firmware nodes instead of their names, we need to attach the - currently "dangling" - existing software nodes to their target devices dynamically. The driver uses platform_create_bundle() and expects all required providers to be present before it itself is probed. We know the name of the device we're waiting for so look them up and assign the appropriate software node as the secondary firmware node of the underlying ACPI node. Scheduling fine-grained devres actions allows for proper teardown and unsetting of the secondary firmware nodes. Signed-off-by: Bartosz Golaszewski Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260504-baytrail-real-swnode-v5-2-c7878b69e383@oss.qualcomm.com [ij: added linux/bug.h include] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/x86-android-tablets/core.c | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 021009e9085b..5db794d65eb5 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -11,8 +11,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include +#include #include #include #include @@ -360,6 +362,61 @@ static const struct software_node *cherryview_gpiochip_node_group[] = { NULL }; +static void gpio_secondary_unset(void *data) +{ + struct device *dev = data; + + set_secondary_fwnode(dev, NULL); +} + +static void gpio_secondary_unregister_node_group(void *data) +{ + const struct software_node **nodes = data; + + software_node_unregister_node_group(nodes); +} + +static int gpio_secondary_fwnode_init(struct device *parent) +{ + const struct software_node *const *swnode; + struct fwnode_handle *fwnode; + int ret; + + if (!gpiochip_node_group) + return 0; + + ret = software_node_register_node_group(gpiochip_node_group); + if (ret) + return ret; + + ret = devm_add_action_or_reset(parent, + gpio_secondary_unregister_node_group, + gpiochip_node_group); + if (ret) + return ret; + + for (swnode = gpiochip_node_group; *swnode; swnode++) { + struct device *dev __free(put_device) = + acpi_bus_find_device_by_name((*swnode)->name); + if (!dev) + return dev_err_probe(parent, + -ENODEV, "Failed to find the required GPIO controller: %s\n", + (*swnode)->name); + + fwnode = software_node_fwnode(*swnode); + if (WARN_ON(!fwnode)) + return -ENOENT; + + set_secondary_fwnode(dev, fwnode); + + ret = devm_add_action_or_reset(parent, gpio_secondary_unset, dev); + if (ret) + return ret; + } + + return 0; +} + static void x86_android_tablet_remove(struct platform_device *pdev) { int i; @@ -391,7 +448,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev) software_node_unregister_node_group(gpio_button_swnodes); software_node_unregister_node_group(swnode_group); - software_node_unregister_node_group(gpiochip_node_group); } static __init int x86_android_tablet_probe(struct platform_device *pdev) @@ -427,9 +483,11 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) break; } - ret = software_node_register_node_group(gpiochip_node_group); - if (ret) + ret = gpio_secondary_fwnode_init(&pdev->dev); + if (ret) { + x86_android_tablet_remove(pdev); return ret; + } ret = software_node_register_node_group(dev_info->swnode_group); if (ret) { From dfe614f82e445a65cb2afd01859b06b01fce8889 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:32 -0700 Subject: [PATCH 0598/5214] platform/x86/intel/pmc: Use __free() in pmc_core_punit_pmt_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use scope-based cleanup in pmc_core_punit_pmt_init() instead of manually freeing. This simplifies the code flow by removing the explicit put call, making it less error-prone. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-2-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index d91e1ab842d6..c8a92d623520 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1325,16 +1325,15 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) { struct telem_endpoint *ep; - struct pci_dev *pcidev; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0)); + struct pci_dev *pcidev __free(pci_dev_put) = pci_get_domain_bus_and_slot(0, 0, + PCI_DEVFN(10, 0)); if (!pcidev) { dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found."); return; } ep = pmc_core_register_endpoint(pcidev, guids); - pci_dev_put(pcidev); if (IS_ERR(ep)) { dev_err(&pmcdev->pdev->dev, "pmc_core: couldn't get DMU telem endpoint %ld", From 38c79dd63b72e36919ef097d4e5025ca0fa17f34 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:33 -0700 Subject: [PATCH 0599/5214] platform/x86/intel/pmc: Enable PkgC LTR blocking counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the Package C-state LTR blocking counter in the PMT telemetry region. This counter records how many times any Package C-state entry is blocked for the specified reasons. Add pmc_core_pkgc_counters_show() as a common helper to display package C-state blocking counters from the telemetry region. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-3-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 79 +++++++++++++++++++++++---- drivers/platform/x86/intel/pmc/core.h | 15 ++++- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index c8a92d623520..7681c444f4bd 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1071,6 +1071,34 @@ static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us); +static int pmc_core_pkgc_counters_show(struct seq_file *s, + struct telem_endpoint *ep, + u32 offset, const char **counters) +{ + unsigned int i; + u32 counter; + int ret; + + for (i = 0; counters[i]; i++) { + ret = pmt_telem_read32(ep, offset + i, &counter, 1); + if (ret) + return ret; + seq_printf(s, "%-30s %-30u\n", counters[i], counter); + } + + return 0; +} + +static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + + return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep, + pmcdev->pkgc_ltr_blocker_offset, + pmcdev->pkgc_ltr_blocker_counters); +} +DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker); + static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; @@ -1322,7 +1350,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, return ERR_PTR(-ENODEV); } -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) { struct telem_endpoint *ep; @@ -1333,16 +1361,32 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) return; } - ep = pmc_core_register_endpoint(pcidev, guids); - if (IS_ERR(ep)) { - dev_err(&pmcdev->pdev->dev, - "pmc_core: couldn't get DMU telem endpoint %ld", - PTR_ERR(ep)); - return; + if (pmc_dev_info->dmu_guids) { + ep = pmc_core_register_endpoint(pcidev, pmc_dev_info->dmu_guids); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get DMU telem endpoint %ld", + PTR_ERR(ep)); + return; + } + + pmcdev->punit_ep = ep; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; } - pmcdev->punit_ep = ep; - pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + if (pmc_dev_info->pc_guid) { + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc_dev_info->pc_guid, 0); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get Package C-state telem endpoint %ld", + PTR_ERR(ep)); + return; + } + + pmcdev->pc_ep = ep; + pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters; + pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset; + } } void pmc_core_set_device_d3(unsigned int device) @@ -1466,6 +1510,13 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info pmcdev->dbgfs_dir, pmcdev, &pmc_core_die_c6_us_fops); } + + if (pmcdev->pc_ep) { + debugfs_create_file("pkgc_ltr_blocker_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_pkgc_ltr_blocker_fops); + } + } /* @@ -1716,8 +1767,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) } pmc_core_get_low_power_modes(pmcdev); - if (pmc_dev_info->dmu_guids) - pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); + if (pmc_dev_info->dmu_guids || pmc_dev_info->pc_guid) + pmc_core_punit_pmt_init(pmcdev, pmc_dev_info); if (ssram) { ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); @@ -1738,6 +1789,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) if (pmcdev->punit_ep) pmt_telem_unregister_endpoint(pmcdev->punit_ep); + if (pmcdev->pc_ep) + pmt_telem_unregister_endpoint(pmcdev->pc_ep); + return ret; } @@ -1834,6 +1888,9 @@ static void pmc_core_clean_structure(struct platform_device *pdev) if (pmcdev->punit_ep) pmt_telem_unregister_endpoint(pmcdev->punit_ep); + if (pmcdev->pc_ep) + pmt_telem_unregister_endpoint(pmcdev->pc_ep); + platform_set_drvdata(pdev, NULL); } diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 118c8740ad3a..a20aab73c140 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -453,6 +453,9 @@ struct pmc { * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * + * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters + * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * * pmc_dev contains info about power management controller device. */ struct pmc_dev { @@ -471,8 +474,12 @@ struct pmc_dev { u8 num_of_pkgc; u32 die_c6_offset; + struct telem_endpoint *pc_ep; struct telem_endpoint *punit_ep; struct pmc_info *regmap_list; + + const char **pkgc_ltr_blocker_counters; + u32 pkgc_ltr_blocker_offset; }; enum pmc_index { @@ -486,12 +493,15 @@ enum pmc_index { * struct pmc_dev_info - Structure to keep PMC device info * @pci_func: Function number of the primary PMC * @dmu_guids: List of Die Management Unit GUID + * @pc_guid: GUID for telemetry region to read PKGC blocker info + * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. * @map: Pointer to a pmc_reg_map struct that contains platform * specific attributes of the primary PMC * @sub_req_show: File operations to show substate requirements + * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action @@ -500,9 +510,12 @@ enum pmc_index { struct pmc_dev_info { u8 pci_func; u32 *dmu_guids; + u32 pc_guid; + u32 pkgc_ltr_blocker_offset; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; + const char **pkgc_ltr_blocker_counters; void (*suspend)(struct pmc_dev *pmcdev); int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); @@ -535,7 +548,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); void pmc_core_set_device_d3(unsigned int device); int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); From d727eb1c3ede7c21f885ded1f1ad65b47434a9b9 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:34 -0700 Subject: [PATCH 0600/5214] platform/x86/intel/pmc: Enable Pkgc blocking residency counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the Package C-state blocking counter in the PMT telemetry region. This counter reports the number of 10 µs intervals during which a Package C-state 10.2/3 entry was blocked for the specified reasons. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-4-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 15 +++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 7681c444f4bd..94ae098a155a 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1099,6 +1099,16 @@ static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker); +static int pmc_core_pkgc_blocker_residency_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + + return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep, + pmcdev->pkgc_blocker_offset, + pmcdev->pkgc_blocker_counters); +} +DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_blocker_residency); + static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; @@ -1386,6 +1396,8 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de pmcdev->pc_ep = ep; pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters; pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset; + pmcdev->pkgc_blocker_counters = pmc_dev_info->pkgc_blocker_counters; + pmcdev->pkgc_blocker_offset = pmc_dev_info->pkgc_blocker_offset; } } @@ -1515,6 +1527,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info debugfs_create_file("pkgc_ltr_blocker_show", 0444, pmcdev->dbgfs_dir, pmcdev, &pmc_core_pkgc_ltr_blocker_fops); + debugfs_create_file("pkgc_blocker_residency_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_pkgc_blocker_residency_fops); } } diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index a20aab73c140..829b1dee3f63 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -455,6 +455,8 @@ struct pmc { * * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * @pkgc_blocker_counters: Array of PKGC blocker counters + * @pkgc_blocker_offset: Offset to PKGC blocker in telemetry region * * pmc_dev contains info about power management controller device. */ @@ -480,6 +482,8 @@ struct pmc_dev { const char **pkgc_ltr_blocker_counters; u32 pkgc_ltr_blocker_offset; + const char **pkgc_blocker_counters; + u32 pkgc_blocker_offset; }; enum pmc_index { @@ -495,6 +499,7 @@ enum pmc_index { * @dmu_guids: List of Die Management Unit GUID * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. @@ -502,6 +507,7 @@ enum pmc_index { * specific attributes of the primary PMC * @sub_req_show: File operations to show substate requirements * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters + * @pkgc_blocker_counters: Array of PKGC blocker counters * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action @@ -512,10 +518,12 @@ struct pmc_dev_info { u32 *dmu_guids; u32 pc_guid; u32 pkgc_ltr_blocker_offset; + u32 pkgc_blocker_offset; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; const char **pkgc_ltr_blocker_counters; + const char **pkgc_blocker_counters; void (*suspend)(struct pmc_dev *pmcdev); int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); From 11de0586ecf40aa747972f1b0bf88bac192d7b06 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:35 -0700 Subject: [PATCH 0601/5214] platform/x86/intel/pmc: Use PCI DID for PMC SSRAM device discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the PMC SSRAM discovery process to identify the device using its PCI Device ID rather than relying on a fixed PCI bus location. The enumeration of integrated devices on the PCI bus is no longer guaranteed to be consistent across CPUs. On earlier platforms, the IOE and PCH SSRAM devices were hidden from the BIOS, and the SOC SSRAM device is associated to telemetry regions from all available SSRAM devices. Starting with Nova Lake, the IOE and PCH SSRAM devices register their telemetry regions independently, meaning each telemetry region is now linked to its corresponding SSRAM device. A new ssram_hidden attribute has been added to the pmc_dev_info structure to reflect this distinction. Signed-off-by: David E. Box Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-5-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 4 ++-- drivers/platform/x86/intel/pmc/core.c | 17 ++++++++++++----- drivers/platform/x86/intel/pmc/core.h | 6 ++++-- drivers/platform/x86/intel/pmc/lnl.c | 2 +- drivers/platform/x86/intel/pmc/mtl.c | 2 +- drivers/platform/x86/intel/pmc/ptl.c | 2 +- drivers/platform/x86/intel/pmc/wcl.c | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index eb23bc68340a..95372a0807ac 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -720,7 +720,6 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_pmc_dev = { - .pci_func = 0, .dmu_guids = ARL_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &arl_socs_reg_map, @@ -729,11 +728,11 @@ struct pmc_dev_info arl_pmc_dev = { .resume = arl_resume, .init = arl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_h_pmc_dev = { - .pci_func = 2, .dmu_guids = ARL_H_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &mtl_socm_reg_map, @@ -742,4 +741,5 @@ struct pmc_dev_info arl_h_pmc_dev = { .resume = arl_h_resume, .init = arl_h_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 94ae098a155a..e0ac329e6723 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1646,17 +1646,13 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc, static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) { - struct pci_dev *pcidev __free(pci_dev_put) = NULL; struct telem_endpoint *ep; unsigned int pmc_idx; int ret; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); - if (!pcidev) - return -ENODEV; - for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { struct pmc *pmc; + u16 devid; pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) @@ -1665,6 +1661,16 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc->map->lpm_req_guid) return -ENXIO; + if (pmc_dev_info->ssram_hidden) + devid = pmcdev->pmcs[PMC_IDX_MAIN]->devid; + else + devid = pmc->devid; + + struct pci_dev *pcidev __free(pci_dev_put) = + pci_get_device(PCI_VENDOR_ID_INTEL, devid, NULL); + if (!pcidev) + return -ENODEV; + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); @@ -1715,6 +1721,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) pmc->map = map; pmc->base_addr = pmc_ssram_telemetry.base_addr; + pmc->devid = pmc_ssram_telemetry.devid; pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length); if (!pmc->regbase) { diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 829b1dee3f63..f385a0eccd2c 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -425,6 +425,7 @@ struct pmc_info { * @ltr_ign: Holds LTR ignore data while suspended * @num_lpm_modes: Count of enabled modes * @lpm_en_modes: Array of enabled modes from lowest to highest priority + * @devid: Device ID of the SSRAM device * * pmc contains info about one power management controller device. */ @@ -436,6 +437,7 @@ struct pmc { u32 ltr_ign; u8 num_lpm_modes; u8 lpm_en_modes[LPM_MAX_NUM_MODES]; + u16 devid; }; /** @@ -495,7 +497,6 @@ enum pmc_index { /** * struct pmc_dev_info - Structure to keep PMC device info - * @pci_func: Function number of the primary PMC * @dmu_guids: List of Die Management Unit GUID * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region @@ -512,9 +513,9 @@ enum pmc_index { * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action * @sub_req: Function to achieve low power mode substate requirements + * @ssram_hidden: Some SSRAM devices are hidden on this platform */ struct pmc_dev_info { - u8 pci_func; u32 *dmu_guids; u32 pc_guid; u32 pkgc_ltr_blocker_offset; @@ -528,6 +529,7 @@ struct pmc_dev_info { int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep); + bool ssram_hidden; }; extern const struct pmc_bit_map msr_map[]; diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 1cd81ee54dcf..18f303af328e 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -571,7 +571,6 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info lnl_pmc_dev = { - .pci_func = 2, .regmap_list = lnl_pmc_info_list, .map = &lnl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, @@ -579,4 +578,5 @@ struct pmc_dev_info lnl_pmc_dev = { .resume = lnl_resume, .init = lnl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 57508cbf9cd4..193ebbe58402 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -994,7 +994,6 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info mtl_pmc_dev = { - .pci_func = 2, .dmu_guids = MTL_PMT_DMU_GUIDS, .regmap_list = mtl_pmc_info_list, .map = &mtl_socm_reg_map, @@ -1003,4 +1002,5 @@ struct pmc_dev_info mtl_pmc_dev = { .resume = mtl_resume, .init = mtl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 1f48e2bbc699..6c68772e738c 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -569,7 +569,6 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info ptl_pmc_dev = { - .pci_func = 2, .regmap_list = ptl_pmc_info_list, .map = &ptl_pcdp_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, @@ -577,4 +576,5 @@ struct pmc_dev_info ptl_pmc_dev = { .resume = ptl_resume, .init = ptl_core_init, .sub_req = pmc_core_pmt_get_blk_sub_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c index a45707e6364f..b55069945e9e 100644 --- a/drivers/platform/x86/intel/pmc/wcl.c +++ b/drivers/platform/x86/intel/pmc/wcl.c @@ -493,7 +493,6 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info wcl_pmc_dev = { - .pci_func = 2, .regmap_list = wcl_pmc_info_list, .map = &wcl_pcdn_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, @@ -501,4 +500,5 @@ struct pmc_dev_info wcl_pmc_dev = { .resume = wcl_resume, .init = wcl_core_init, .sub_req = pmc_core_pmt_get_blk_sub_req, + .ssram_hidden = true, }; From ebbf33380896cc489e870d88004ad3750e908a6c Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:36 -0700 Subject: [PATCH 0602/5214] platform/x86/intel/pmc: Add support for variable DMU offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for handling different DMU Die C6 offsets across platforms. The previous implementation assumed a uniform DMU Die C6 offset for all platforms, which is no longer valid after an upcoming change. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-6-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 2 ++ drivers/platform/x86/intel/pmc/core.c | 2 +- drivers/platform/x86/intel/pmc/core.h | 2 ++ drivers/platform/x86/intel/pmc/mtl.c | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 95372a0807ac..4d91ee010f6d 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -729,6 +729,7 @@ struct pmc_dev_info arl_pmc_dev = { .init = arl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; @@ -742,4 +743,5 @@ struct pmc_dev_info arl_h_pmc_dev = { .init = arl_h_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index e0ac329e6723..5d2e2681b0eb 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1381,7 +1381,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de } pmcdev->punit_ep = ep; - pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + pmcdev->die_c6_offset = pmc_dev_info->die_c6_offset; } if (pmc_dev_info->pc_guid) { diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index f385a0eccd2c..ef69de160ffb 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -514,6 +514,7 @@ enum pmc_index { * @init: Function to perform platform specific init action * @sub_req: Function to achieve low power mode substate requirements * @ssram_hidden: Some SSRAM devices are hidden on this platform + * @die_c6_offset: Telemetry offset to read Die C6 residency */ struct pmc_dev_info { u32 *dmu_guids; @@ -530,6 +531,7 @@ struct pmc_dev_info { int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep); bool ssram_hidden; + u32 die_c6_offset; }; extern const struct pmc_bit_map msr_map[]; diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 193ebbe58402..b724dd8c34db 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -1003,4 +1003,5 @@ struct pmc_dev_info mtl_pmc_dev = { .init = mtl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; From a7d5916d132300b1ff6ac0c5f6d7a7cb7817a7fc Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:37 -0700 Subject: [PATCH 0603/5214] platform/x86/intel/pmc: Retrieve PMC info only for available PMCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the Intel PMC Core driver to fetch PMC information only for available PMCs. Previously, the driver attempted to retrieve PMC info even when the corresponding PMC was not present. This change aligns with recent updates to the Intel SSRAM Telemetry driver. Starting with NVL, the SSRAM Telemetry driver is probed for each individual SSRAM device. The prior implementation could not differentiate between an unavailable PMC and one that had not yet completed information retrieval. To resolve this, the PMC Core driver now skips obtaining PMC info for unavailable PMCs. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-7-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 7 +++++++ drivers/platform/x86/intel/pmc/core.c | 19 +++++++++++-------- drivers/platform/x86/intel/pmc/core.h | 4 ++++ drivers/platform/x86/intel/pmc/lnl.c | 4 ++++ drivers/platform/x86/intel/pmc/mtl.c | 4 ++++ drivers/platform/x86/intel/pmc/ptl.c | 4 ++++ drivers/platform/x86/intel/pmc/wcl.c | 4 ++++ 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 4d91ee010f6d..11609d593383 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -672,6 +672,9 @@ static struct pmc_info arl_pmc_info_list[] = { {} }; +static const u8 arl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE, PMC_IDX_PCH}; +static const u8 arl_h_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE}; + #define ARL_NPU_PCI_DEV 0xad1d #define ARL_GNA_PCI_DEV 0xae4c #define ARL_H_NPU_PCI_DEV 0x7d1d @@ -721,6 +724,8 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_pmc_dev = { .dmu_guids = ARL_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(arl_pmc_list), + .pmc_list = arl_pmc_list, .regmap_list = arl_pmc_info_list, .map = &arl_socs_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, @@ -735,6 +740,8 @@ struct pmc_dev_info arl_pmc_dev = { static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_h_pmc_dev = { .dmu_guids = ARL_H_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(arl_h_pmc_list), + .pmc_list = arl_h_pmc_list, .regmap_list = arl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 5d2e2681b0eb..9c13a7554af9 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1734,16 +1734,17 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) return 0; } -static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev) +static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev, u8 num_pmcs, const u8 *pmc_list) { + unsigned int i; int ret; - ret = pmc_core_pmc_add(pmcdev, PMC_IDX_MAIN); - if (ret) - return ret; - - pmc_core_pmc_add(pmcdev, PMC_IDX_IOE); - pmc_core_pmc_add(pmcdev, PMC_IDX_PCH); + for (i = 0; i < num_pmcs; ++i) { + /* Non-MAIN PMCs are allowed to fail */ + ret = pmc_core_pmc_add(pmcdev, pmc_list[i]); + if (ret && (pmc_list[i] == PMC_IDX_MAIN)) + return ret; + } return 0; } @@ -1765,7 +1766,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) ssram = pmc_dev_info->regmap_list != NULL; if (ssram) { pmcdev->regmap_list = pmc_dev_info->regmap_list; - ret = pmc_core_ssram_get_reg_base(pmcdev); + ret = pmc_core_ssram_get_reg_base(pmcdev, + pmc_dev_info->num_pmcs, + pmc_dev_info->pmc_list); /* * EAGAIN error code indicates Intel PMC SSRAM Telemetry driver * has not finished probe and PMC info is not available yet. Try diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index ef69de160ffb..428b2e55864a 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -501,6 +501,8 @@ enum pmc_index { * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region * @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region + * @num_pmcs: Number of entries in @pmc_list + * @pmc_list: Index list of available PMC * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. @@ -521,6 +523,8 @@ struct pmc_dev_info { u32 pc_guid; u32 pkgc_ltr_blocker_offset; u32 pkgc_blocker_offset; + u8 num_pmcs; + const u8 *pmc_list; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 18f303af328e..5ff4922825d9 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -544,6 +544,8 @@ static struct pmc_info lnl_pmc_info_list[] = { {} }; +static const u8 lnl_pmc_list[] = {PMC_IDX_MAIN}; + #define LNL_NPU_PCI_DEV 0x643e #define LNL_IPU_PCI_DEV 0x645d @@ -571,6 +573,8 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info lnl_pmc_dev = { + .num_pmcs = ARRAY_SIZE(lnl_pmc_list), + .pmc_list = lnl_pmc_list, .regmap_list = lnl_pmc_info_list, .map = &lnl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index b724dd8c34db..9abeabd3dbe2 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -965,6 +965,8 @@ static struct pmc_info mtl_pmc_info_list[] = { {} }; +static const u8 mtl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE}; + #define MTL_GNA_PCI_DEV 0x7e4c #define MTL_IPU_PCI_DEV 0x7d19 #define MTL_VPU_PCI_DEV 0x7d1d @@ -995,6 +997,8 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info mtl_pmc_dev = { .dmu_guids = MTL_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(mtl_pmc_list), + .pmc_list = mtl_pmc_list, .regmap_list = mtl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 6c68772e738c..90fcd7900d9f 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -543,6 +543,8 @@ static struct pmc_info ptl_pmc_info_list[] = { {} }; +static const u8 ptl_pmc_list[] = {PMC_IDX_MAIN}; + #define PTL_NPU_PCI_DEV 0xb03e #define PTL_IPU_PCI_DEV 0xb05d @@ -569,6 +571,8 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info ptl_pmc_dev = { + .num_pmcs = ARRAY_SIZE(ptl_pmc_list), + .pmc_list = ptl_pmc_list, .regmap_list = ptl_pmc_info_list, .map = &ptl_pcdp_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c index b55069945e9e..d34ac916907d 100644 --- a/drivers/platform/x86/intel/pmc/wcl.c +++ b/drivers/platform/x86/intel/pmc/wcl.c @@ -469,6 +469,8 @@ static struct pmc_info wcl_pmc_info_list[] = { {} }; +static const u8 wcl_pmc_list[] = {PMC_IDX_MAIN}; + #define WCL_NPU_PCI_DEV 0xfd3e /* @@ -494,6 +496,8 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in struct pmc_dev_info wcl_pmc_dev = { .regmap_list = wcl_pmc_info_list, + .num_pmcs = ARRAY_SIZE(wcl_pmc_list), + .pmc_list = wcl_pmc_list, .map = &wcl_pcdn_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, .suspend = cnl_suspend, From 41354f4c8a791d3059f4355945e550693ac87ce8 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:38 -0700 Subject: [PATCH 0604/5214] platform/x86/intel/pmc: Add Nova Lake support to intel_pmc_core driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Nova Lake support in intel_pmc_core driver Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-8-xi.pardee@linux.intel.com [ij: added missing statics to a few structs] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Makefile | 3 +- drivers/platform/x86/intel/pmc/core.c | 2 + drivers/platform/x86/intel/pmc/core.h | 33 +- drivers/platform/x86/intel/pmc/nvl.c | 1539 +++++++++++++++++++++++ drivers/platform/x86/intel/pmc/ptl.c | 2 +- 5 files changed, 1576 insertions(+), 3 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/nvl.c diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index bb960c8721d7..23853e867c91 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -4,7 +4,8 @@ # intel_pmc_core-y := core.o spt.o cnp.o icl.o \ - tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o + tgl.o adl.o mtl.o arl.o \ + lnl.o ptl.o wcl.o nvl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 9c13a7554af9..3adae8fe8b30 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1849,6 +1849,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev), X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev), X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev), + X86_MATCH_VFM(INTEL_NOVALAKE, &nvl_s_pmc_dev), + X86_MATCH_VFM(INTEL_NOVALAKE_L, &nvl_h_pmc_dev), {} }; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 428b2e55864a..55cf567febe4 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -46,7 +46,7 @@ struct telem_endpoint; #define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x68 #define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1) #define MTPMC_MASK 0xffff0000 -#define PPFEAR_MAX_NUM_ENTRIES 12 +#define PPFEAR_MAX_NUM_ENTRIES 13 #define SPT_PPFEAR_NUM_ENTRIES 5 #define SPT_PMC_READ_DISABLE_BIT 0x16 #define SPT_PMC_MSG_FULL_STS_BIT 0x18 @@ -307,6 +307,29 @@ enum ppfear_regs { #define WCL_NUM_S0IX_BLOCKER 94 #define WCL_BLK_REQ_OFFSET 50 +/* Nova Lake */ +#define NVL_PCDH_PPFEAR_NUM_ENTRIES 13 +#define NVL_PCDH_PMC_MMIO_REG_LEN 0x363c +#define NVL_PCDS_PMC_MMIO_REG_LEN 0x3118 +#define NVL_PCHS_PMC_MMIO_REG_LEN 0x30d8 +#define NVL_LPM_PRI_OFFSET 0x17a4 +#define NVL_LPM_EN_OFFSET 0x17a0 +#define NVL_LPM_RESIDENCY_OFFSET 0x17a8 +#define NVL_LPM_LIVE_STATUS_OFFSET 0x1760 +#define NVL_LPM_NUM_MAPS 15 +#define NVL_PCDH_NUM_S0IX_BLOCKER 107 +#define NVL_PCDS_NUM_S0IX_BLOCKER 71 +#define NVL_PCHS_NUM_S0IX_BLOCKER 54 +#define NVL_PCDS_PMC_LTR_RESERVED 0x1bac +#define NVL_PCDH_BLK_REQ_OFFSET 53 +#define NVL_PCDS_BLK_REQ_OFFSET 18 +#define NVL_PCHS_BLK_REQ_OFFSET 46 +#define NVL_PMT_PC_GUID 0x13000101 +#define NVL_PMT_DMU_GUID 0x1a000101 +#define NVL_LTR_BLK_OFFSET 64 +#define NVL_PKGC_BLK_OFFSET 4 +#define NVL_PMT_DMU_DIE_C6_OFFSET 25 + /* SSRAM PMC Device ID */ /* LNL */ #define PMC_DEVID_LNL_SOCM 0xa87f @@ -329,6 +352,11 @@ enum ppfear_regs { #define PMC_DEVID_MTL_IOEP 0x7ecf #define PMC_DEVID_MTL_IOEM 0x7ebf +/* NVL */ +#define PMC_DEVID_NVL_PCDH 0xd37e +#define PMC_DEVID_NVL_PCDS 0xd47e +#define PMC_DEVID_NVL_PCHS 0x6e27 + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -558,6 +586,7 @@ extern const struct pmc_reg_map mtl_ioep_reg_map; extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[]; extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[]; extern const struct pmc_bit_map ptl_pcdp_signal_status_map[]; +extern const struct pmc_bit_map ptl_pcdp_ltr_show_map[]; void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); @@ -581,6 +610,8 @@ extern struct pmc_dev_info arl_h_pmc_dev; extern struct pmc_dev_info lnl_pmc_dev; extern struct pmc_dev_info ptl_pmc_dev; extern struct pmc_dev_info wcl_pmc_dev; +extern struct pmc_dev_info nvl_s_pmc_dev; +extern struct pmc_dev_info nvl_h_pmc_dev; void cnl_suspend(struct pmc_dev *pmcdev); int cnl_resume(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/nvl.c b/drivers/platform/x86/intel/pmc/nvl.c new file mode 100644 index 000000000000..8dabf2511dd9 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/nvl.c @@ -0,0 +1,1539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Nova Lake PCH. + * + * Copyright (c) 2026, Intel Corporation. + */ + +#include +#include + +#include "core.h" + +/* PMC SSRAM PMT Telemetry GUIDS */ +#define PCDH_LPM_REQ_GUID 0x01093101 +#define PCHS_LPM_REQ_GUID 0x01092101 +#define PCDS_LPM_REQ_GUID 0x01091102 + +/* + * Die Mapping to Product. + * Product PCDDie PCHDie + * NVL-H PCD-H None + * NVL-S PCD-S PCH-S + */ + +static const struct pmc_bit_map nvl_pcdh_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FUSE_OSSE_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"MPFPW2_PGD0", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"SBR16B20_PGD0", BIT(0)}, + {"DBG_SBR_PGD0", BIT(1)}, + {"SBR16B7_PGD0", BIT(2)}, + {"STRC_PGD0", BIT(3)}, + {"SBR16B8_PGD0", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"SBR16B2_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"DBG_SBR16B_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"SBR16B0_PGD0", BIT(7)}, + + {"P2SB0_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"SBR16B21_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR16B22_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"MPFPW1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"ESE_PGD0", BIT(1)}, + {"SBR16B6_PGD0", BIT(2)}, + {"P2SB1_PGD0", BIT(3)}, + {"SBR16B3_PGD0", BIT(4)}, + {"OSSE_SMT1_PGD0", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"SNPS_USB2_A_PGD0", BIT(7)}, + + {"U3FPW1_PGD0", BIT(0)}, + {"FIA_X_PGD0", BIT(1)}, + {"PSF4_PGD0", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"UFSX2_PGD0", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"FIA_PG_PGD0", BIT(7)}, + + {"D2D_IPU_PGD0", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"FIACPCB_X_PGD0", BIT(2)}, + {"SBR8B4_PGD0", BIT(3)}, + {"DBG_PSF_PGD0", BIT(4)}, + {"PSF6_PGD0", BIT(5)}, + {"UFSPW1_PGD0", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"PSF8_PGD0", BIT(0)}, + {"SBR16B9_PGD0", BIT(1)}, + {"PSF0_PGD0", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"SBR8B2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"DISP_PGA1_PGD0", BIT(2)}, + {"TCSS_PGD0", BIT(3)}, + {"DISP_PGA_PGD0", BIT(4)}, + {"SBR16B1_PGD0", BIT(5)}, + {"SBRG_PGD0", BIT(6)}, + {"PSF5_PGD0", BIT(7)}, + + {"SBR8B3_PGD0", BIT(0)}, + {"ACE_PGD0", BIT(1)}, + {"ACE_PGD1", BIT(2)}, + {"ACE_PGD2", BIT(3)}, + {"ACE_PGD3", BIT(4)}, + {"ACE_PGD4", BIT(5)}, + {"ACE_PGD5", BIT(6)}, + {"ACE_PGD6", BIT(7)}, + + {"ACE_PGD7", BIT(0)}, + {"ACE_PGD8", BIT(1)}, + {"ACE_PGD9", BIT(2)}, + {"ACE_PGD10", BIT(3)}, + {"FIACPCB_PG_PGD0", BIT(4)}, + {"SNPS_USB2_B_PGD0", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"SBR8B0_PGD0", BIT(7)}, + + {"SBR16B4_PGD0", BIT(0)}, + {"CSME_PTIO_PGD0", BIT(1)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pcdh_pfear_map[] = { + nvl_pcdh_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcdh_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 1}, + {"AON5_OFF_STS", BIT(3), 1}, + {"AON1_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"MPFPW1_0_PLL_OFF_STS", BIT(6), 1}, + {"D2D_PLL_OFF_STS", BIT(7), 1}, + {"USB3_PLL_OFF_STS", BIT(8), 1}, + {"AON3_SPL_OFF_STS", BIT(9), 1}, + {"MPFPW2_0_PLL_OFF_STS", BIT(12), 1}, + {"XTAL_AGGR_OFF_STS", BIT(17), 1}, + {"USB2_PLL_OFF_STS", BIT(18), 0}, + {"DDI2_PLL_OFF_STS", BIT(19), 1}, + {"SE_TCSS_PLL_OFF_STS", BIT(20), 1}, + {"DDI_PLL_OFF_STS", BIT(21), 1}, + {"FILTER_PLL_OFF_STS", BIT(22), 1}, + {"ACE_PLL_OFF_STS", BIT(24), 0}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"SOC_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"IMG_PLL_OFF_STS", BIT(29), 1}, + {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1}, + {"RTC_PLL_OFF_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 1}, + {"SPA_PGD0_PG_STS", BIT(4), 1}, + {"SPB_PGD0_PG_STS", BIT(5), 1}, + {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 1}, + {"SBR16B20_PGD0_PG_STS", BIT(8), 0}, + {"DBG_PGD0_PG_STS", BIT(9), 0}, + {"SBR16B7_PGD0_PG_STS", BIT(10), 0}, + {"STRC_PGD0_PG_STS", BIT(11), 0}, + {"SBR16B8_PGD0_PG_STS", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 1}, + {"LPSS_PGD0_PG_STS", BIT(14), 1}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"SBR16B2_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 1}, + {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"SBR16B0_PGD0_PG_STS", BIT(23), 0}, + {"P2SB0_PGD0_PG_STS", BIT(24), 1}, + {"XDCI_PGD0_PG_STS", BIT(25), 1}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 1}, + {"PMT_PGD0_PG_STS", BIT(29), 1}, + {"CLINK_PGD0_PG_STS", BIT(30), 1}, + {"SBR16B21_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 1}, + {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 1}, + {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, + {"SMS2_PGD0_PG_STS", BIT(4), 1}, + {"SMS1_PGD0_PG_STS", BIT(5), 1}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"ESE_PGD0_PG_STS", BIT(9), 1}, + {"SBR16B6_PGD0_PG_STS", BIT(10), 0}, + {"P2SB1_PGD0_PG_STS", BIT(11), 1}, + {"SBR16B3_PGD0_PG_STS", BIT(12), 0}, + {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 1}, + {"SNPA_USB2_A_PGD0_PG_STS", BIT(15), 0}, + {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, + {"FIA_X_PGD0_PG_STS", BIT(17), 0}, + {"PSF4_PGD0_PG_STS", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"UFSX2_PGD0_PG_STS", BIT(20), 1}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"FIA_PG_PGD0_PG_STS", BIT(23), 0}, + {"D2D_IPU_PGD0_PG_STS", BIT(24), 1}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"FIACPCB_X_PGD0_PG_STS", BIT(26), 0}, + {"SBR8B4_PGD0_PG_STS", BIT(27), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(28), 0}, + {"PSF6_PGD0_PG_STS", BIT(29), 0}, + {"UFSPW1_PGD0_PG_STS", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0), 0}, + {"SBR16B9_PGD0_PG_STS", BIT(1), 0}, + {"PSF0_PGD0_PG_STS", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 1}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 1}, + {"SBR8B2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 1}, + {"THC1_PGD0_PG_STS", BIT(8), 1}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"DISP_PGA1_PGD0_PG_STS", BIT(10), 0}, + {"TCSS_PGD0_PG_STS", BIT(11), 0}, + {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, + {"SBR16B1_PGD0_PG_STS", BIT(13), 0}, + {"SBRG_PGD0_PG_STS", BIT(14), 0}, + {"PSF5_PGD0_PG_STS", BIT(15), 0}, + {"SBR8B3_PGD0_PG_STS", BIT(16), 0}, + {"ACE_PGD0_PG_STS", BIT(17), 0}, + {"ACE_PGD1_PG_STS", BIT(18), 0}, + {"ACE_PGD2_PG_STS", BIT(19), 0}, + {"ACE_PGD3_PG_STS", BIT(20), 0}, + {"ACE_PGD4_PG_STS", BIT(21), 0}, + {"ACE_PGD5_PG_STS", BIT(22), 0}, + {"ACE_PGD6_PG_STS", BIT(23), 0}, + {"ACE_PGD7_PG_STS", BIT(24), 0}, + {"ACE_PGD8_PG_STS", BIT(25), 0}, + {"ACE_PGD9_PG_STS", BIT(26), 0}, + {"ACE_PGD10_PG_STS", BIT(27), 0}, + {"FIACPCB_PG_PGD0_PG_STS", BIT(28), 0}, + {"SNPS_USB2_B_PGD0_PG_STS", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 1}, + {"SBR8B0_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_3_map[] = { + {"SBR16B4_PGD0_PG_STS", BIT(0), 0}, + {"PTIO_PGD0_PG_STS", BIT(1), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 1}, + {"OSSE_D3_STS", BIT(6), 0}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"PSTH_D3_STS", BIT(21), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_1_map[] = { + {"OSSE_SMT1_D3_STS", BIT(0), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {"UFSX2_D3_STS", BIT(28), 0}, + {"ESE_D3_STS", BIT(29), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"OSSE_SMT2_D3_STS", BIT(11), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14), 1}, + {"THC1_D3_STS", BIT(15), 1}, + {"OSSE_SMT3_D3_STS", BIT(16), 0}, + {"ACE_D3_STS", BIT(23), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_0_map[] = { + {"LPSS_VNN_REQ_STS", BIT(3), 1}, + {"OSSE_VNN_REQ_STS", BIT(6), 1}, + {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_1_map[] = { + {"OSSE_SMT1_VNN_REQ_STS", BIT(0), 1}, + {"NPK_VNN_REQ_STS", BIT(4), 1}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 1}, + {"P2D_VNN_REQ_STS", BIT(18), 1}, + {"GBE_VNN_REQ_STS", BIT(19), 1}, + {"SMB_VNN_REQ_STS", BIT(25), 1}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {"ESE_VNN_REQ_STS", BIT(29), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 1}, + {"CSE_VNN_REQ_STS", BIT(4), 1}, + {"ISH_VNN_REQ_STS", BIT(7), 1}, + {"SMT1_VNN_REQ_STS", BIT(8), 1}, + {"CLINK_VNN_REQ_STS", BIT(14), 1}, + {"SMS1_VNN_REQ_STS", BIT(18), 1}, + {"SMS2_VNN_REQ_STS", BIT(19), 1}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, + {"DISP_SHIM_VNN_REQ_STS", BIT(22), 1}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_3_map[] = { + {"DTS0_VNN_REQ_STS", BIT(7), 0}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"VNN_SOC_REQ_STS", BIT(6), 1}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, + {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, + {"D2D_IPU_QACTIVE_REQ_STS", BIT(10), 1}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_REQ_STS", BIT(22), 1}, + {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, + {"XDCI_ATTACHED_REQ_STS", BIT(24), 1}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_REQ_STS", BIT(29), 1}, + {"PG5_PMA2_GVNN", BIT(30), 1}, + {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_rsc_status_map[] = { + {"CORE", 0, 1}, + {"Memory", 0, 1}, + {"PRIM_D2D", 0, 1}, + {"PSF0", 0, 1}, + {"PSF4", 0, 1}, + {"PSF6", 0, 1}, + {"PSF8", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pcdh_lpm_maps[] = { + nvl_pcdh_clocksource_status_map, + nvl_pcdh_power_gating_status_0_map, + nvl_pcdh_power_gating_status_1_map, + nvl_pcdh_power_gating_status_2_map, + nvl_pcdh_power_gating_status_3_map, + nvl_pcdh_d3_status_0_map, + nvl_pcdh_d3_status_1_map, + nvl_pcdh_d3_status_2_map, + nvl_pcdh_d3_status_3_map, + nvl_pcdh_vnn_req_status_0_map, + nvl_pcdh_vnn_req_status_1_map, + nvl_pcdh_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pcdh_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pcdh_blk_maps[] = { + nvl_pcdh_power_gating_status_0_map, + nvl_pcdh_power_gating_status_1_map, + nvl_pcdh_power_gating_status_2_map, + nvl_pcdh_power_gating_status_3_map, + nvl_pcdh_rsc_status_map, + nvl_pcdh_vnn_req_status_0_map, + nvl_pcdh_vnn_req_status_1_map, + nvl_pcdh_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pcdh_d3_status_0_map, + nvl_pcdh_d3_status_1_map, + nvl_pcdh_d3_status_2_map, + nvl_pcdh_d3_status_3_map, + nvl_pcdh_clocksource_status_map, + nvl_pcdh_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcds_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FUSE_OSSE_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"RSVD6", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"RSVD8", BIT(0)}, + {"RSVD9", BIT(1)}, + {"SBR16B7_PGD0", BIT(2)}, + {"SBR16B21_PGD0", BIT(3)}, + {"RSVD12", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"SBR16B1_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"DBG_SBR16B_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"RSVD23", BIT(7)}, + + {"P2SB0_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"CSME_PTIO_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR16B22_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"P2SB1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"RSVD41", BIT(1)}, + {"RSVD42", BIT(2)}, + {"RSVD43", BIT(3)}, + {"SBR16B2_PGD0", BIT(4)}, + {"OSSE_SMT1_PGD0", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"RSVD47_PGD0", BIT(7)}, + + {"RSVD48", BIT(0)}, + {"DBG_PSF_PGD0", BIT(1)}, + {"RSVD50", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"UFSX2_PGD0", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"SBR16B4_PGD0", BIT(7)}, + + {"RSVD56", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"RSVD58", BIT(2)}, + {"SBR16B20_PGD0", BIT(3)}, + {"RSVD60", BIT(4)}, + {"SBR8B20_PGD0", BIT(5)}, + {"RSVD62", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"PSF8_PGD0", BIT(0)}, + {"RSVD65", BIT(1)}, + {"RSVD66", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"SBR8B2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"SBR16B3_PGD0", BIT(2)}, + {"TCSS_PGD0", BIT(3)}, + {"DISP_PGA_PGD0", BIT(4)}, + {"RSVD77", BIT(5)}, + {"RSVD78", BIT(6)}, + {"RSVD79", BIT(7)}, + + {"SBRG_PGD0", BIT(0)}, + {"RSVD81", BIT(1)}, + {"SBR16B0_PGD0", BIT(2)}, + {"SBR8B0_PGD0", BIT(3)}, + {"PSF7_PGD0", BIT(4)}, + {"RSVD85", BIT(5)}, + {"RSVD86", BIT(6)}, + {"RSVD87", BIT(7)}, + + {"SBR16B6_PGD0", BIT(0)}, + {"PSD0_PGD0", BIT(1)}, + {"STRC_PGD0", BIT(2)}, + {"RSVD91", BIT(3)}, + {"DBG_SBR_PGD0", BIT(4)}, + {"RSVD93", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"DISP_PGA1_PGD0", BIT(7)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pcds_pfear_map[] = { + nvl_pcds_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcds_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", PTL_PMC_LTR_SATA2}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, + {"RSVD", NVL_PCDS_PMC_LTR_RESERVED}, + {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, + {"DMI3", ARL_PMC_LTR_DMI3}, + {"OSSE", LNL_PMC_LTR_OSSE}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 1}, + {"AON5_OFF_STS", BIT(3), 1}, + {"AON1_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"D2D_OFF_STS", BIT(8), 1}, + {"AON3_SPL_OFF_STS", BIT(9), 1}, + {"XTAL_AGGR_OFF_STS", BIT(17), 1}, + {"BCLK_EXT_INJ_OFF_STS", BIT(18), 1}, + {"DDI2_PLL_OFF_STS", BIT(19), 1}, + {"SE_TCSS_PLL_OFF_STS", BIT(20), 1}, + {"DDI_PLL_OFF_STS", BIT(21), 1}, + {"FILTER_PLL_OFF_STS", BIT(22), 1}, + {"PHY_OC_EXT_INJ_OFF_STS", BIT(23), 1}, + {"ACE_PLL_OFF_STS", BIT(24), 0}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"SOC_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1}, + {"RTC_PLL_OFF_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 0}, + {"SPA_PGD0_PG_STS", BIT(4), 0}, + {"SPB_PGD0_PG_STS", BIT(5), 0}, + {"RSVD_6", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 0}, + {"RSVD_8", BIT(8), 0}, + {"RSVD_9", BIT(9), 0}, + {"SBR16B7_PGD0_PG_STS", BIT(10), 0}, + {"SBR16B21_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 1}, + {"LPSS_PGD0_PG_STS", BIT(14), 0}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"SBR16B1_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 1}, + {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"RSVD_23", BIT(23), 0}, + {"P2SB0_PGD0_PG_STS", BIT(24), 1}, + {"XDCI_PGD0_PG_STS", BIT(25), 0}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 0}, + {"PMT_PGD0_PG_STS", BIT(29), 0}, + {"CLINK_PGD0_PG_STS", BIT(30), 0}, + {"PTIO_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 0}, + {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 0}, + {"P2SB1_PGD0_PG_STS", BIT(3), 1}, + {"SMS2_PGD0_PG_STS", BIT(4), 0}, + {"SMS1_PGD0_PG_STS", BIT(5), 0}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"RSVD_9", BIT(9), 0}, + {"RSVD_10", BIT(10), 0}, + {"RSVD_11", BIT(11), 0}, + {"SBR16B2_PGD0_PG_STS", BIT(12), 0}, + {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 1}, + {"RSVD_15", BIT(15), 0}, + {"RSVD_16", BIT(16), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(17), 0}, + {"RSVD_18", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"UFSX2_PGD0_PG_STS", BIT(20), 0}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"SBR16B4_PGD0_PG_STS", BIT(23), 0}, + {"RSVD_24", BIT(24), 0}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"RSVD_26", BIT(26), 0}, + {"SBR16B20_PGD0_PG_STS", BIT(27), 0}, + {"RSVD_28", BIT(28), 0}, + {"SBR8B20_PGD0_PG_STS", BIT(29), 0}, + {"RSVD_30", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0), 0}, + {"RSVD_1", BIT(1), 0}, + {"RSVD_2", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 1}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 1}, + {"SBR8B2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 0}, + {"THC1_PGD0_PG_STS", BIT(8), 0}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"SBR16B3_PGD0_PG_STS", BIT(10), 0}, + {"TCSS_PGD0_PG_STS", BIT(11), 0}, + {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, + {"RSVD_13", BIT(13), 0}, + {"RSVD_14", BIT(14), 0}, + {"RSVD_15", BIT(15), 0}, + {"SBRG_PGD0_PG_STS", BIT(16), 0}, + {"RSVD_17", BIT(17), 0}, + {"SBR16B0_PGD0_PG_STS", BIT(18), 0}, + {"SBR8B0_PGD0_PG_STS", BIT(19), 0}, + {"PSF7_PGD0_PG_STS", BIT(20), 0}, + {"RSVD_21", BIT(21), 0}, + {"RSVD_22", BIT(22), 0}, + {"RSVD_23", BIT(23), 0}, + {"SBR16B6_PGD0_PG_STS", BIT(24), 0}, + {"PSF0_PGD0_PG_STS", BIT(25), 0}, + {"STRC_PGD0_PG_STS", BIT(26), 0}, + {"RSVD_27", BIT(27), 0}, + {"DBG_SBR_PGD0_PG_STS", BIT(28), 0}, + {"RSVD_29", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 1}, + {"DISP_PGA1_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 1}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"PSTH_D3_STS", BIT(21), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_1_map[] = { + {"OSSE_D3_STS", BIT(14), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {"UFSX2_D3_STS", BIT(28), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"OSSE_SMT1_D3_STS", BIT(12), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_3_map[] = { + {"OSSE_SMT2_D3_STS", BIT(0), 0}, + {"THC0_D3_STS", BIT(14), 1}, + {"THC1_D3_STS", BIT(15), 1}, + {"OSSE_SMT3_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_0_map[] = { + {"LPSS_VNN_REQ_STS", BIT(3), 0}, + {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4), 1}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 1}, + {"OSSE_VNN_REQ_STS", BIT(14), 1}, + {"P2D_VNN_REQ_STS", BIT(18), 1}, + {"GBE_VNN_REQ_STS", BIT(19), 0}, + {"SMB_VNN_REQ_STS", BIT(25), 1}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 0}, + {"CSE_VNN_REQ_STS", BIT(4), 1}, + {"ISH_VNN_REQ_STS", BIT(7), 0}, + {"SMT1_VNN_REQ_STS", BIT(8), 0}, + {"OSSE_SMT1_VNN_REQ_STS", BIT(12), 1}, + {"CLINK_VNN_REQ_STS", BIT(14), 0}, + {"SMS1_VNN_REQ_STS", BIT(18), 0}, + {"SMS2_VNN_REQ_STS", BIT(19), 0}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_3_map[] = { + {"DISP_SHIM_VNN_REQ_STS", BIT(4), 1}, + {"DTS0_VNN_REQ_STS", BIT(7), 0}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"VNN_SOC_REQ_STS", BIT(6), 1}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, + {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_REQ_STS", BIT(22), 1}, + {"DG5_PMA0_REQ_STS", BIT(23), 1}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_REQ_STS", BIT(29), 1}, + {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_rsc_status_map[] = { + {"CORE", 0, 1}, + {"Memory", 0, 1}, + {"PRIM_D2D", 0, 1}, + {"PSF0", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_signal_status_map[] = { + {"LSX_Wake0_STS", BIT(0), 0}, + {"LSX_Wake1_STS", BIT(1), 0}, + {"LSX_Wake2_STS", BIT(2), 0}, + {"LSX_Wake3_STS", BIT(3), 0}, + {"LSX_Wake4_STS", BIT(4), 0}, + {"LSX_Wake5_STS", BIT(5), 0}, + {"LSX_Wake6_STS", BIT(6), 0}, + {"LSX_Wake7_STS", BIT(7), 0}, + {"LPSS_Wake0_STS", BIT(8), 1}, + {"LPSS_Wake1_STS", BIT(9), 1}, + {"Int_Timer_SS_Wake0_STS", BIT(10), 1}, + {"Int_Timer_SS_Wake1_STS", BIT(11), 1}, + {"Int_Timer_SS_Wake2_STS", BIT(12), 1}, + {"Int_Timer_SS_Wake3_STS", BIT(13), 1}, + {"Int_Timer_SS_Wake4_STS", BIT(14), 1}, + {"Int_Timer_SS_Wake5_STS", BIT(15), 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pcds_lpm_maps[] = { + nvl_pcds_clocksource_status_map, + nvl_pcds_power_gating_status_0_map, + nvl_pcds_power_gating_status_1_map, + nvl_pcds_power_gating_status_2_map, + nvl_pcds_d3_status_0_map, + nvl_pcds_d3_status_1_map, + nvl_pcds_d3_status_2_map, + nvl_pcds_d3_status_3_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pcds_vnn_req_status_1_map, + nvl_pcds_vnn_req_status_2_map, + nvl_pcds_vnn_req_status_3_map, + nvl_pcds_vnn_misc_status_map, + nvl_pcds_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pcds_blk_maps[] = { + nvl_pcds_power_gating_status_0_map, + nvl_pcds_power_gating_status_1_map, + nvl_pcds_power_gating_status_2_map, + nvl_pcds_rsc_status_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pcds_vnn_req_status_1_map, + nvl_pcds_vnn_req_status_2_map, + nvl_pcds_vnn_req_status_3_map, + nvl_pcds_d3_status_0_map, + nvl_pcds_d3_status_1_map, + nvl_pcds_d3_status_2_map, + nvl_pcds_d3_status_3_map, + nvl_pcds_clocksource_status_map, + nvl_pcds_vnn_misc_status_map, + nvl_pcds_signal_status_map, + NULL +}; + +static const struct pmc_bit_map nvl_pchs_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FIA_D_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"MPFPW2_PGD0", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"RSVD8", BIT(0)}, + {"PSF3_PGD0", BIT(1)}, + {"SBR5_PGD0", BIT(2)}, + {"SBR0_PGD0", BIT(3)}, + {"RSVD12", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"P2SB_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"EAH_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"SBR8_PGD0", BIT(7)}, + + {"PSF7_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"CSME_PTIO_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR1_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"MPFPW1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"ESE_PGD0", BIT(1)}, + {"SBR2_PGD0", BIT(2)}, + {"SBR3_PGD0", BIT(3)}, + {"SBR4_PGD0", BIT(4)}, + {"RSVD45", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"PSF1_PGD0", BIT(7)}, + + {"U3FPW1_PGD0", BIT(0)}, + {"DMI3FPW_PGD0", BIT(1)}, + {"PSF4_PGD0", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"RSVD52", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"SMT4_PGD0", BIT(7)}, + + {"RSVD56", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"RSVD58", BIT(2)}, + {"DMI3_PGD0", BIT(3)}, + {"RSVD60", BIT(4)}, + {"FIACPCB_D_PGD0", BIT(5)}, + {"RSVD62", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"FIACPCB_PGS_PGD0", BIT(0)}, + {"FIA_PGS_PGD0", BIT(1)}, + {"RSVD66", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"PSF2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"SBR9_PGD0", BIT(2)}, + {"U3FPW2_PGD0", BIT(3)}, + {"RSVD76", BIT(4)}, + {"DBG_PSF_PGD0", BIT(5)}, + {"DBG_SBR_PGD0", BIT(6)}, + {"SBR6_PGD0", BIT(7)}, + + {"SPC_PGD0", BIT(0)}, + {"ACE_PGD0", BIT(1)}, + {"ACE_PGD1", BIT(2)}, + {"ACE_PGD2", BIT(3)}, + {"ACE_PGD3", BIT(4)}, + {"ACE_PGD4", BIT(5)}, + {"ACE_PGD5", BIT(6)}, + {"ACE_PGD6", BIT(7)}, + + {"ACE_PGD7", BIT(0)}, + {"ACE_PGD8", BIT(1)}, + {"ACE_PGD9", BIT(2)}, + {"ACE_PGD10", BIT(3)}, + {"U3FPW3_PGD0", BIT(4)}, + {"SBR7_PGD0", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"ST_PGD0", BIT(7)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pchs_pfear_map[] = { + nvl_pchs_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pchs_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 0}, + {"AON2_SPL_OFF_STS", BIT(3), 0}, + {"AONL_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"AON5_OFF_STS", BIT(6), 0}, + {"USB3_PLL_OFF_STS", BIT(8), 1}, + {"MAIN_CRO_OFF_STS", BIT(11), 0}, + {"MAIN_DIVIDER_OFF_STS", BIT(12), 1}, + {"REF_PLL_NON_OC_OFF_STS", BIT(13), 1}, + {"DMI_PLL_OFF_STS", BIT(14), 1}, + {"PHY_EXT_INJ_OFF_STS", BIT(15), 1}, + {"AON6_MCRO_OFF_STS", BIT(16), 0}, + {"XTAL_AGGR_OFF_STS", BIT(17), 0}, + {"USB2_PLL_OFF_STS", BIT(18), 1}, + {"GBE_PLL_OFF_STS", BIT(21), 1}, + {"SATA_PLL_OFF_STS", BIT(22), 1}, + {"PCIE0_PLL_OFF_STS", BIT(23), 1}, + {"PCIE1_PLL_OFF_STS", BIT(24), 1}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"PCIE2_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"REF38P4_PLL_OFF_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FIA_D_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 0}, + {"SPA_PGD0_PG_STS", BIT(4), 1}, + {"SPB_PGD0_PG_STS", BIT(5), 1}, + {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 1}, + {"RSVD_8", BIT(8), 0}, + {"PSF3_PGD0_PG_STS", BIT(9), 0}, + {"SBR5_PGD0_PG_STS", BIT(10), 0}, + {"SBR0_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 0}, + {"LPSS_PGD0_PG_STS", BIT(14), 1}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"P2S_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 0}, + {"EAH_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"SBR8_PGD0_PG_STS", BIT(23), 0}, + {"PSF7_PGD0_PG_STS", BIT(24), 0}, + {"XDCI_PGD0_PG_STS", BIT(25), 1}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 1}, + {"PMT_PGD0_PG_STS", BIT(29), 1}, + {"CLINK_PGD0_PG_STS", BIT(30), 1}, + {"PTIO_PGD0_PG_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 1}, + {"SBR1_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 1}, + {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, + {"SMS2_PGD0_PG_STS", BIT(4), 1}, + {"SMS1_PGD0_PG_STS", BIT(5), 1}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"ESE_PGD0_PG_STS", BIT(9), 1}, + {"SBR2_PGD0_PG_STS", BIT(10), 0}, + {"SBR3_PGD0_PG_STS", BIT(11), 0}, + {"SBR4_PGD0_PG_STS", BIT(12), 0}, + {"RSVD_13", BIT(13), 0}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 0}, + {"PSF1_PGD0_PG_STS", BIT(15), 0}, + {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, + {"DMI3FPW_PGD0_PG_STS", BIT(17), 0}, + {"PSF4_PGD0_PG_STS", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"RSVD_20", BIT(20), 0}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"SMT4_PGD0_PG_STS", BIT(23), 1}, + {"RSVD_24", BIT(24), 0}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"RSVD_26", BIT(26), 0}, + {"DMI3_PGD0_PG_STS", BIT(27), 1}, + {"RSVD_28", BIT(28), 0}, + {"FIACPCB_D_PGD0_PG_STS", BIT(29), 0}, + {"RSVD_30", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_2_map[] = { + {"FIACPCB_PGS_PGD0_PG_STS", BIT(0), 0}, + {"FIA_PGS_PGD0_PG_STS", BIT(1), 0}, + {"RSVD_2", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 0}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 0}, + {"PSF2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 1}, + {"THC1_PGD0_PG_STS", BIT(8), 1}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"SBR9_PGA0_PGD0_PG_STS", BIT(10), 0}, + {"U3FPW2_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(13), 0}, + {"DBG_SBR_PGD0_PG_STS", BIT(14), 0}, + {"SBR6_PGD0_PG_STS", BIT(15), 0}, + {"SPC_PGD0_PG_STS", BIT(16), 1}, + {"ACE_PGD0_PG_STS", BIT(17), 0}, + {"ACE_PGD1_PG_STS", BIT(18), 0}, + {"ACE_PGD2_PG_STS", BIT(19), 0}, + {"ACE_PGD3_PG_STS", BIT(20), 0}, + {"ACE_PGD4_PG_STS", BIT(21), 0}, + {"ACE_PGD5_PG_STS", BIT(22), 0}, + {"ACE_PGD6_PG_STS", BIT(23), 0}, + {"ACE_PGD7_PG_STS", BIT(24), 0}, + {"ACE_PGD8_PG_STS", BIT(25), 0}, + {"ACE_PGD9_PG_STS", BIT(26), 0}, + {"ACE_PGD10_PG_STS", BIT(27), 0}, + {"U3FPW3_PGD0_PG_STS", BIT(28), 0}, + {"SBR7_PGD0_PG_STS", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 0}, + {"SATA_PGD0_PG_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 0}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"SPC_D3_STS", BIT(14), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"SATA_D3_STS", BIT(20), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_1_map[] = { + {"OSSE_D3_STS", BIT(6), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"P2S_D3_STS", BIT(24), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"SMT4_D3_STS", BIT(11), 0}, + {"SMT5_D3_STS", BIT(12), 0}, + {"SMT6_D3_STS", BIT(13), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14), 0}, + {"THC1_D3_STS", BIT(15), 0}, + {"ACE_D3_STS", BIT(23), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4), 0}, + {"OSSE_VNN_REQ_STS", BIT(6), 0}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 0}, + {"GBE_VNN_REQ_STS", BIT(19), 0}, + {"SMB_VNN_REQ_STS", BIT(25), 0}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 0}, + {"CSE_VNN_REQ_STS", BIT(4), 0}, + {"ISH_VNN_REQ_STS", BIT(7), 0}, + {"SMT1_VNN_REQ_STS", BIT(8), 0}, + {"SMT4_VNN_REQ_STS", BIT(11), 0}, + {"CLINK_VNN_REQ_STS", BIT(14), 0}, + {"SMS1_VNN_REQ_STS", BIT(18), 0}, + {"SMS2_VNN_REQ_STS", BIT(19), 0}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 0}, + {"GPIOCOM2_VNN_REQ_STS", BIT(22), 0}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 0}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_GVNN_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"DMI_IN_L1_REQ_STS", BIT(6), 0}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"DMI_CLKREQ_B_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_GVNN_REQ_STS", BIT(22), 1}, + {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, + {"XDCI_ATTACHED_REQ_STS", BIT(24), 0}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_EN_REQ_STS", BIT(29), 0}, + {"PG5_PMA2_GVNN_REQ_STS", BIT(30), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_rsc_status_map[] = { + {"Memory", 0, 1}, + {"Memory_NS", 0, 1}, + {"PSF1", 0, 1}, + {"PSF2", 0, 1}, + {"PSF3", 0, 1}, + {"REF_PLL", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pchs_lpm_maps[] = { + nvl_pchs_clocksource_status_map, + nvl_pchs_power_gating_status_0_map, + nvl_pchs_power_gating_status_1_map, + nvl_pchs_power_gating_status_2_map, + nvl_pchs_d3_status_0_map, + nvl_pchs_d3_status_1_map, + nvl_pchs_d3_status_2_map, + nvl_pchs_d3_status_3_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pchs_vnn_req_status_1_map, + nvl_pchs_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pchs_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pchs_blk_maps[] = { + nvl_pchs_power_gating_status_0_map, + nvl_pchs_power_gating_status_1_map, + nvl_pchs_power_gating_status_2_map, + nvl_pchs_rsc_status_map, + nvl_pchs_d3_status_0_map, + nvl_pchs_clocksource_status_map, + nvl_pchs_vnn_misc_status_map, + NULL +}; + +static const struct pmc_reg_map nvl_pcdh_reg_map = { + .pfear_sts = ext_nvl_pcdh_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = ptl_pcdp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCDH_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = NVL_PCDH_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = NVL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = NVL_LPM_PRI_OFFSET, + .lpm_en_offset = NVL_LPM_EN_OFFSET, + .lpm_residency_offset = NVL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pcdh_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = NVL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pcdh_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCDH_NUM_S0IX_BLOCKER, + .blocker_req_offset = NVL_PCDH_BLK_REQ_OFFSET, + .lpm_req_guid = PCDH_LPM_REQ_GUID, +}; + +static const struct pmc_reg_map nvl_pcds_reg_map = { + .pfear_sts = ext_nvl_pcds_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = nvl_pcds_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCDS_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = PTL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pcds_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pcds_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCDS_NUM_S0IX_BLOCKER, + .lpm_req_guid = PCDS_LPM_REQ_GUID, + .blocker_req_offset = NVL_PCDS_BLK_REQ_OFFSET, +}; + +static const struct pmc_reg_map nvl_pchs_reg_map = { + .pfear_sts = ext_nvl_pchs_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = ptl_pcdp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCHS_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = PTL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pchs_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pchs_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCHS_NUM_S0IX_BLOCKER, + .blocker_req_offset = NVL_PCHS_BLK_REQ_OFFSET, + .lpm_req_guid = PCHS_LPM_REQ_GUID, +}; + +static struct pmc_info nvl_pmc_info_list[] = { + { + .devid = PMC_DEVID_NVL_PCDH, + .map = &nvl_pcdh_reg_map, + }, + { + .devid = PMC_DEVID_NVL_PCDS, + .map = &nvl_pcds_reg_map, + }, + { + .devid = PMC_DEVID_NVL_PCHS, + .map = &nvl_pchs_reg_map, + }, + {} +}; + +static const char *nvl_ltr_block_counter_arr[] = { + "PKGC_PREVENT_LTR_IADOMAIN", + "PKGC_PREVENT_LTR_GDIE", + "PKGC_PREVENT_LTR_PCH", + "PKGC_PREVENT_LTR_DISPLAY", + "PKGC_PREVENT_LTR_IPU", + NULL +}; + +static const char *nvl_pkgc_blocker_residency[] = { + "PKGC_BLOCK_RESIDENCY_INVALID", + "PKGC_BLOCK_RESIDENCY_MISC", + "PKGC_BLOCK_RESIDENCY_CDIE_MISC", + "PKGC_BLOCK_RESIDENCY_MEDIA_MISC", + "PKGC_BLOCK_RESIDENCY_GT_MISC", + "PKGC_BLOCK_RESIDENCY_HUBATOM_MISC", + "PKGC_BLOCK_RESIDENCY_IPU_BUSY", + "PKGC_BLOCK_RESIDENCY_IPU_LTR", + "PKGC_BLOCK_RESIDENCY_IPU_TIMER", + "PKGC_BLOCK_RESIDENCY_DISP_BUSY", + "PKGC_BLOCK_RESIDENCY_DISP_LTR", + "PKGC_BLOCK_RESIDENCY_DISP_TIMER", + "PKGC_BLOCK_RESIDENCY_VPU_BUSY", + "PKGC_BLOCK_RESIDENCY_VPU_TIMER", + "PKGC_BLOCK_RESIDENCY_PMC_BUSY", + "PKGC_BLOCK_RESIDENCY_PMC_LTR", + "PKGC_BLOCK_RESIDENCY_PMC_TIMER", + "PKGC_BLOCK_RESIDENCY_HUBATOM_ARAT", + "PKGC_BLOCK_RESIDENCY_CDIE0_ARAT", + "PKGC_BLOCK_RESIDENCY_CDIE1_ARAT", + "PKGC_BLOCK_RESIDENCY_GT_ARAT", + "PKGC_BLOCK_RESIDENCY_MEDIA_ARAT", + "PKGC_BLOCK_RESIDENCY_DEMOTION", + "PKGC_BLOCK_RESIDENCY_THERMALS", + "PKGC_BLOCK_RESIDENCY_SNCU", + "PKGC_BLOCK_RESIDENCY_SVTU", + "PKGC_BLOCK_RESIDENCY_IAA", + "PKGC_BLOCK_RESIDENCY_IOC", + NULL, +}; + +static const u8 nvl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_PCH}; + +#define NVL_NPU_PCI_DEV 0xd71d + +/* + * Set power state of select devices that do not have drivers to D3 + * so that they do not block Package C entry. + */ +static void nvl_d3_fixup(void) +{ + pmc_core_set_device_d3(NVL_NPU_PCI_DEV); +} + +static int nvl_resume(struct pmc_dev *pmcdev) +{ + nvl_d3_fixup(); + return cnl_resume(pmcdev); +} + +static int nvl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) +{ + nvl_d3_fixup(); + return generic_core_init(pmcdev, pmc_dev_info); +} + +static u32 nvl_pmt_dmu_guids[] = {NVL_PMT_DMU_GUID, 0x0}; +struct pmc_dev_info nvl_s_pmc_dev = { + .num_pmcs = ARRAY_SIZE(nvl_pmc_list), + .pmc_list = nvl_pmc_list, + .regmap_list = nvl_pmc_info_list, + .map = &nvl_pcds_reg_map, + .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = nvl_resume, + .init = nvl_core_init, + .sub_req = pmc_core_pmt_get_blk_sub_req, + .dmu_guids = nvl_pmt_dmu_guids, + .pc_guid = NVL_PMT_PC_GUID, + .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET, + .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr, + .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET, + .pkgc_blocker_counters = nvl_pkgc_blocker_residency, + .ssram_hidden = false, + .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET, +}; + +struct pmc_dev_info nvl_h_pmc_dev = { + .num_pmcs = ARRAY_SIZE(nvl_pmc_list), + .pmc_list = nvl_pmc_list, + .regmap_list = nvl_pmc_info_list, + .map = &nvl_pcdh_reg_map, + .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = nvl_resume, + .init = nvl_core_init, + .sub_req = pmc_core_pmt_get_blk_sub_req, + .dmu_guids = nvl_pmt_dmu_guids, + .pc_guid = NVL_PMT_PC_GUID, + .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET, + .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr, + .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET, + .pkgc_blocker_counters = nvl_pkgc_blocker_residency, + .ssram_hidden = false, + .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET, +}; diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 90fcd7900d9f..a7d5d4f77934 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -137,7 +137,7 @@ static const struct pmc_bit_map *ext_ptl_pcdp_pfear_map[] = { NULL }; -static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { +const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, {"SATA", CNP_PMC_LTR_SATA}, From 113e86bc58a918f85d250723436a4d541a873358 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:27 +0800 Subject: [PATCH 0605/5214] PCI: Introduce named defines for PCI ROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the magic numbers associated with PCI ROM into named definitions. Some of these definitions will be used in the second fix patch. Signed-off-by: Guixin Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260508082128.3344255-2-kanie@linux.alibaba.com --- drivers/pci/rom.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index e18d3a4383ba..d4a141bd148b 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -5,13 +5,28 @@ * (C) Copyright 2004 Jon Smirl * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ + +#include #include #include #include +#include #include #include "pci.h" +#define PCI_ROM_HEADER_SIZE 0x1A +#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18 +#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15 +#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7) +#define PCI_ROM_IMAGE_LEN 0x10 +#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512 +#define PCI_ROM_IMAGE_SIGNATURE 0xAA55 + +/* Data structure signature is "PCIR" in ASCII representation */ +#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 +#define PCI_ROM_DATA_STRUCT_LEN 0x0A + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -91,26 +106,27 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, do { void __iomem *pds; /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != 0xAA55) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", - readw(image)); + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, readw(image)); break; } - /* get the PCI data structure and check its "PCIR" signature */ - pds = image + readw(image + 24); - if (readl(pds) != 0x52494350) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", - readl(pds)); + /* Get the PCI data structure and check its "PCIR" signature */ + pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); + if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); break; } - last_image = readb(pds + 21) & 0x80; - length = readw(pds + 16); - image += length * 512; + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & + PCI_ROM_LAST_IMAGE_INDICATOR_BIT; + length = readw(pds + PCI_ROM_IMAGE_LEN); + image += length * PCI_ROM_IMAGE_SECTOR_SIZE; /* Avoid iterating through memory outside the resource window */ if (image >= rom + size) break; if (!last_image) { - if (readw(image) != 0xAA55) { + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { pci_info(pdev, "No more image in the PCI ROM\n"); break; } From 538796b807fcfb81b2ce40cc97a614fd8588feb5 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:28 +0800 Subject: [PATCH 0606/5214] PCI: Check ROM header and data structure addr before accessing We meet a crash when running stress-ng on x86_64 machine: BUG: unable to handle page fault for address: ffa0000007f40000 RIP: 0010:pci_get_rom_size+0x52/0x220 Call Trace: pci_map_rom+0x80/0x130 pci_read_rom+0x4b/0xe0 kernfs_file_read_iter+0x96/0x180 vfs_read+0x1b1/0x300 Our analysis reveals that the ROM space's start address is 0xffa0000007f30000, and size is 0x10000. Because of broken ROM space, before calling readl(pds), the pds's value is 0xffa0000007f3ffff, which is already pointed to the ROM space end, invoking readl() would read 4 bytes therefore cause an out-of-bounds access and trigger a crash. Fix this by adding image header and data structure checking. We also found another crash on arm64 machine: Unable to handle kernel paging request at virtual address ffff8000dd1393ff Mem abort info: ESR = 0x0000000096000021 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x21: alignment fault The call trace is the same with x86_64, but the crash reason is that the data structure addr is not aligned with 4, and arm64 machine report "alignment fault". Fix this by adding alignment checking. Fixes: 47b975d234ea ("PCI: Avoid iterating through memory outside the resource window") Suggested-by: Guanghui Feng Signed-off-by: Guixin Liu [bhelgaas: shorten function names, wrap comments] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260508082128.3344255-3-kanie@linux.alibaba.com --- drivers/pci/rom.c | 123 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index d4a141bd148b..f2105c6ceef5 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -6,9 +6,12 @@ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ +#include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,15 @@ #define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 #define PCI_ROM_DATA_STRUCT_LEN 0x0A +/* + * Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at + * least 24 bytes (0x18), large enough to cover every fixed field this + * driver reads (up to the Indicator byte at offset 0x15). Reject smaller + * device-claimed lengths so the follow-up readers in pci_get_rom_size() + * cannot escape the mapped ROM window. + */ +#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18 + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -84,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_rom); +static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image, + void __iomem *rom, size_t size, + bool expect_valid) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long header_end; + u16 signature; + + /* + * Per PCI Firmware r3.3, sec 5.1, each image must start on a + * 512-byte boundary and must contain the PCI Expansion ROM header. + * Because @rom is page-aligned (returned by ioremap()), checking + * 512-byte alignment of @image is equivalent to enforcing the + * spec's sector-aligned layout within the ROM. This also + * satisfies the natural-alignment requirement of readw() on archs + * such as arm64 that disallow unaligned IOMEM access. + */ + if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE)) + return false; + + if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1, + &header_end)) + return false; + + if (image < rom || header_end > rom_end) + return false; + + /* Standard PCI ROMs start out with these bytes 55 AA */ + signature = readw(image); + if (signature != PCI_ROM_IMAGE_SIGNATURE) { + if (expect_valid) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, signature); + } else { + pci_info(pdev, "No more images in PCI ROM\n"); + } + return false; + } + + return true; +} + +static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds, + void __iomem *rom, size_t size) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long end; + u32 signature; + u16 data_len; + + /* + * Some CPU architectures require IOMEM access addresses to be + * aligned, for example arm64, so since we're about to call + * readl(), check here for 4-byte alignment. + */ + if (!IS_ALIGNED((unsigned long)pds, 4)) + return false; + + if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1, + &end)) + return false; + + if (pds < rom || end > rom_end) + return false; + + signature = readl(pds); + if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, signature); + return false; + } + + data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN); + if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX) + return false; + + if (check_add_overflow((unsigned long)pds, data_len - 1, &end)) + return false; + + if (end > rom_end) + return false; + + return true; +} + /** * pci_get_rom_size - obtain the actual size of the ROM image * @pdev: target PCI device @@ -99,38 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; - int last_image; unsigned int length; + bool last_image; image = rom; do { void __iomem *pds; - /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", - PCI_ROM_IMAGE_SIGNATURE, readw(image)); + if (!pci_rom_header_valid(pdev, image, rom, size, true)) break; - } + /* Get the PCI data structure and check its "PCIR" signature */ pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); - if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", - PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); + if (!pci_rom_data_struct_valid(pdev, pds, rom, size)) break; - } + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & PCI_ROM_LAST_IMAGE_INDICATOR_BIT; length = readw(pds + PCI_ROM_IMAGE_LEN); image += length * PCI_ROM_IMAGE_SECTOR_SIZE; - /* Avoid iterating through memory outside the resource window */ - if (image >= rom + size) + + if (!last_image && + !pci_rom_header_valid(pdev, image, rom, size, false)) break; - if (!last_image) { - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "No more image in the PCI ROM\n"); - break; - } - } } while (length && !last_image); /* never return a size larger than the PCI resource window */ From d29eac59214327e61ff47a980cceeadb7f25d01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:20 +0000 Subject: [PATCH 0607/5214] PCI/sysfs: Use PCI resource accessor macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct pdev->resource[] accesses with pci_resource_n(), and pdev->resource[].flags accesses with pci_resource_flags(). No functional changes intended. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260508043543.217179-2-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d37860841260..1fbc3daf87cc 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -177,7 +177,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, max = PCI_BRIDGE_RESOURCES; for (i = 0; i < max; i++) { - struct resource *res = &pci_dev->resource[i]; + struct resource *res = pci_resource_n(pci_dev, i); struct resource zerores = {}; /* For backwards compatibility */ @@ -689,7 +689,7 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%u\n", (pdev == vga_dev)); return sysfs_emit(buf, "%u\n", - !!(pdev->resource[PCI_ROM_RESOURCE].flags & + !!(pci_resource_flags(pdev, PCI_ROM_RESOURCE) & IORESOURCE_ROM_SHADOW)); } static DEVICE_ATTR_RO(boot_vga); @@ -1082,7 +1082,7 @@ static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *a struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int bar = (unsigned long)attr->private; enum pci_mmap_state mmap_type; - struct resource *res = &pdev->resource[bar]; + struct resource *res = pci_resource_n(pdev, bar); int ret; ret = security_locked_down(LOCKDOWN_PCI_ACCESS); @@ -1286,7 +1286,7 @@ static int pci_create_resource_files(struct pci_dev *pdev) retval = pci_create_attr(pdev, i, 0); /* for prefetchable resources, create a WC mappable file */ if (!retval && arch_can_pci_mmap_wc() && - pdev->resource[i].flags & IORESOURCE_PREFETCH) + pci_resource_flags(pdev, i) & IORESOURCE_PREFETCH) retval = pci_create_attr(pdev, i, 1); if (retval) { pci_remove_resource_files(pdev); From 77c5f3def45937f3f90ea4018cfdf9342b78b78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:21 +0000 Subject: [PATCH 0608/5214] PCI: Add pci_resource_is_io() and pci_resource_is_mem() helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helpers to check whether a PCI resource is of I/O port or memory type. These replace the open-coded pci_resource_flags() with IORESOURCE_IO and IORESOURCE_MEM pattern used across the tree. Suggested-by: Ilpo Järvinen Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260508043543.217179-3-kwilczynski@kernel.org --- include/linux/pci.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..e93fbe6b57fe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2300,6 +2300,31 @@ int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma); CONCATENATE(__pci_dev_for_each_res, COUNT_ARGS(__VA_ARGS__)) \ (dev, res, __VA_ARGS__) +/** + * pci_resource_is_io - check if a PCI resource is of I/O port type. + * @dev: PCI device to check. + * @resno: The resource number (BAR index) to check. + * + * Returns true if the resource type is I/O port. + */ +static inline bool pci_resource_is_io(const struct pci_dev *dev, int resno) +{ + return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_IO; +} + +/** + * pci_resource_is_mem - check if a PCI resource is of memory type. + * @dev: PCI device to check. + * @resno: The resource number (BAR index) to check. + * + * Returns true if the resource type is memory, including + * prefetchable memory. + */ +static inline bool pci_resource_is_mem(const struct pci_dev *dev, int resno) +{ + return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_MEM; +} + /* * Similar to the helpers above, these manipulate per-pci_dev * driver-specific data. They are really just a wrapper around From 04fbecc947ec3a2149680d29b9cdc147fc012fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:22 +0000 Subject: [PATCH 0609/5214] PCI/sysfs: Only allow supported resource types in I/O and MMIO helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, when the sysfs attributes for PCI resources are added dynamically, the resource access callbacks are only set when the underlying BAR type matches, using .read and .write for IORESOURCE_IO, and .mmap for IORESOURCE_MEM or IORESOURCE_IO with arch_can_pci_mmap_io() support. As such, when the callback is not set, the operation inherently fails. After the conversion to static attributes, visibility callbacks will control which resource files appear for each BAR, but the callbacks themselves will always be set. Add a type check to pci_resource_io() and pci_mmap_resource() to return -EIO for an unsupported resource type. Use the new pci_resource_is_io() and pci_resource_is_mem() helpers for the type checks, replacing the open-coded bitwise flag tests and also drop the local struct resource pointer in pci_mmap_resource(). Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260508043543.217179-4-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 1fbc3daf87cc..2e4e226e78d4 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1082,20 +1082,24 @@ static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *a struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int bar = (unsigned long)attr->private; enum pci_mmap_state mmap_type; - struct resource *res = pci_resource_n(pdev, bar); int ret; ret = security_locked_down(LOCKDOWN_PCI_ACCESS); if (ret) return ret; - if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) + if (!pci_resource_is_mem(pdev, bar) && + !(pci_resource_is_io(pdev, bar) && arch_can_pci_mmap_io())) + return -EIO; + + if (pci_resource_is_mem(pdev, bar) && + iomem_is_exclusive(pci_resource_start(pdev, bar))) return -EINVAL; if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS)) return -EINVAL; - mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + mmap_type = pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io; return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); } @@ -1123,6 +1127,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, int bar = (unsigned long)attr->private; unsigned long port = off; + if (!pci_resource_is_io(pdev, bar)) + return -EIO; + port += pci_resource_start(pdev, bar); if (port > pci_resource_end(pdev, bar)) From 77723f417199f79b011336b3922586641fff29b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:23 +0000 Subject: [PATCH 0610/5214] PCI/sysfs: Split pci_llseek_resource() for device and legacy attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both legacy and resource attributes set .f_mapping = iomem_get_mapping, so the default generic_file_llseek() would consult iomem_inode for the file size, which knows nothing about the attribute. That is why custom llseek callbacks exist. Currently, the legacy and resource attributes have .size set at creation time, as such, using the attr->size is sufficient. However, the upcoming static resource attributes will have .size == 0 set, since they are const, and the .bin_size() callback will be used to provide the real size to kernfs instead. The legacy attributes operate on a struct pci_bus, not struct pci_dev, so calling to_pci_dev() on them would be invalid. Thus, split pci_llseek_resource() into two functions: - pci_llseek_resource(), which derives the file size from the BAR using pci_resource_len(). - pci_llseek_resource_legacy(), which uses attr->size directly. Update the dynamic legacy attribute creation to use the new pci_llseek_resource_legacy() callback. The original pci_llseek_resource() was added in commit 24de09c16f97 ("PCI: Implement custom llseek for sysfs resource entries"). Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-5-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 2e4e226e78d4..2280b7edb41f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -881,13 +881,26 @@ static const struct attribute_group pci_dev_config_attr_group = { * llseek operation for mmappable PCI resources. * May be left unused if the arch doesn't provide them. */ +static __maybe_unused loff_t +pci_llseek_resource_legacy(struct file *filep, + struct kobject *kobj __always_unused, + const struct bin_attribute *attr, + loff_t offset, int whence) +{ + return fixed_size_llseek(filep, offset, whence, attr->size); +} + static __maybe_unused loff_t pci_llseek_resource(struct file *filep, - struct kobject *kobj __always_unused, + struct kobject *kobj, const struct bin_attribute *attr, loff_t offset, int whence) { - return fixed_size_llseek(filep, offset, whence, attr->size); + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + int bar = (unsigned long)attr->private; + + return fixed_size_llseek(filep, offset, whence, + pci_resource_len(pdev, bar)); } #ifdef HAVE_PCI_LEGACY @@ -1022,7 +1035,7 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_io->read = pci_read_legacy_io; b->legacy_io->write = pci_write_legacy_io; /* See pci_create_attr() for motivation */ - b->legacy_io->llseek = pci_llseek_resource; + b->legacy_io->llseek = pci_llseek_resource_legacy; b->legacy_io->mmap = pci_mmap_legacy_io; b->legacy_io->f_mapping = iomem_get_mapping; pci_adjust_legacy_attr(b, pci_mmap_io); @@ -1038,7 +1051,7 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_mem->attr.mode = 0600; b->legacy_mem->mmap = pci_mmap_legacy_mem; /* See pci_create_attr() for motivation */ - b->legacy_mem->llseek = pci_llseek_resource; + b->legacy_mem->llseek = pci_llseek_resource_legacy; b->legacy_mem->f_mapping = iomem_get_mapping; pci_adjust_legacy_attr(b, pci_mmap_mem); error = device_create_bin_file(&b->dev, b->legacy_mem); From 25ec7f57deceec633f27bc93b615f951e2ade814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:24 +0000 Subject: [PATCH 0611/5214] PCI/sysfs: Add CAP_SYS_ADMIN check to __resource_resize_store() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the __resource_resize_store() allows writing to the resourceN_resize sysfs attribute to change a BAR's size without checking for capabilities, currently relying only on the file access check. Resizing a BAR modifies PCI device configuration and can disrupt active drivers. After the upcoming conversion to static attributes, it will also trigger resource file updates via sysfs_update_groups(). Add a CAP_SYS_ADMIN check to prevent unprivileged users from performing BAR resize operations. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260508043543.217179-6-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 2280b7edb41f..dac780597727 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1596,6 +1596,9 @@ static ssize_t __resource_resize_store(struct device *dev, int n, int ret; u16 cmd; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (kstrtoul(buf, 0, &size) < 0) return -EINVAL; From b15ac7e008bf0039f981da21e983b2df130152ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:25 +0000 Subject: [PATCH 0612/5214] PCI/sysfs: Add static PCI resource attribute macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add three macros for declaring static binary attributes for PCI resource files: - pci_dev_resource_io_attr(), for I/O BAR resources (read/write) - pci_dev_resource_uc_attr(), for memory BAR resources (mmap uncached) - pci_dev_resource_wc_attr(), for write-combine resources (mmap WC) Each macro only sets the callbacks its resource type needs. The I/O macro conditionally includes mmap support via __PCI_RESOURCE_IO_MMAP_ATTRS on architectures where arch_can_pci_mmap_io() is true at compile time (such as PowerPC, SPARC, and Xtensa). Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-7-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index dac780597727..a74eca96918b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1197,6 +1197,47 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, return pci_resource_io(filp, kobj, attr, buf, off, count, true); } +/* + * generic_file_llseek() consults f_mapping->host to determine + * the file size. As iomem_inode knows nothing about the + * attribute, it's not going to work, so override it as well. + */ +#if arch_can_pci_mmap_io() +# define __PCI_RESOURCE_IO_MMAP_ATTRS \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_uc, +#else +# define __PCI_RESOURCE_IO_MMAP_ATTRS +#endif + +#define pci_dev_resource_io_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_io_attr = { \ + .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .read = pci_read_resource_io, \ + .write = pci_write_resource_io, \ + __PCI_RESOURCE_IO_MMAP_ATTRS \ +} + +#define pci_dev_resource_uc_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_uc_attr = { \ + .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_uc, \ +} + +#define pci_dev_resource_wc_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_wc_attr = { \ + .attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_wc, \ +} + /** * pci_remove_resource_files - cleanup resource files * @pdev: dev to cleanup From f078966ca1fb1b3865d8e6bbe2705cfd277fc637 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:52 +0000 Subject: [PATCH 0613/5214] media: uvcvideo: Fix sequence number when no EOF If the driver could not detect the EOF, the sequence number is increased twice: 1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped 2) When we enter uvc_video_decode_start() with the new buffer. Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start(). This also has some nice side affects: - The error status from the new packet will no longer get propagated to the previous frame-buffer. - uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet. - uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Reported-by: Hans de Goede Closes: https://lore.kernel.org/linux-media/CANiDSCuj4cPuB5_v2xyvAagA5FjoN8V5scXiFFOeD3aKDMqkCg@mail.gmail.com/T/#me39fb134e8c2c085567a31548c3403eb639625e4 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 92 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f6c8e3223796..3182f5e9d9a0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1168,6 +1168,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0]; fid = data[1] & UVC_STREAM_FID; + /* + * Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, and buf->bytesused to 0, + * so the first isochronous frame will never trigger an end of frame + * detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf && buf->bytesused != 0) { + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); + buf->state = UVC_BUF_STATE_READY; + + return -EAGAIN; + } + + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) && + buf && buf->bytesused != 0) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + /* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. @@ -1224,51 +1271,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, buf->state = UVC_BUF_STATE_ACTIVE; } - /* - * Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_dbg(stream->dev, FRAME, - "Frame complete (FID bit toggled)\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - /* - * Some cameras, when running two parallel streams (one MJPEG alongside - * another non-MJPEG stream), are known to lose the EOF packet for a frame. - * We can detect the end of a frame by checking for a new SOI marker, as - * the SOI always lies on the packet boundary between two frames for - * these devices. - */ - if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { - const u8 *packet = data + header_len; - - if (len >= header_len + 2 && - packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && - buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_READY; - buf->error = 1; - stream->last_fid ^= UVC_STREAM_FID; - return -EAGAIN; - } - } - stream->last_fid = fid; return header_len; From 2f24ac8dd87983a55f0498898f34a5f2b735b802 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:53 +0000 Subject: [PATCH 0614/5214] media: uvcvideo: Fix buffer sequence in frame gaps In UVC, the FID flips with every frame. For every FID flip, we increase the stream sequence number. Now, if a FID flips multiple times and there is no data transferred between the flips, the buffer sequence number will be set to the value of the stream sequence number after the first flip. Userspace uses the buffer sequence number to determine if there have been missing frames. With the current behaviour, userspace will think that the gap is in the wrong location. This patch modifies uvc_video_decode_start() to provide the correct buffer sequence number and timestamp. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 3182f5e9d9a0..62db4db4e565 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1223,6 +1223,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, stream->sequence++; if (stream->sequence) uvc_video_stats_update(stream); + + /* + * On a FID flip initialize sequence number and timestamp. + * + * The driver already takes care of injecting FID flips for + * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF. + */ + if (buf) { + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.vb2_buf.timestamp = + ktime_to_ns(uvc_video_get_time()); + } } uvc_video_clock_decode(stream, buf, data, len); @@ -1263,10 +1276,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - buf->buf.field = V4L2_FIELD_NONE; - buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); - /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; } From a84391b77351bbca92ea9db2be1f44e17f2eda6d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:44 +0000 Subject: [PATCH 0615/5214] media: uvcvideo: Import standard controls from uvcdynctrl The uvcdynctrl tool from libwebcam: https://sourceforge.net/projects/libwebcam/ maps proprietary controls into v4l2 controls using the UVCIOC_CTRL_MAP ioctl. The tool has not been updated for 10+ years now, and there is no reason for the UVC driver to not do the mapping by itself. This patch adds the mappings from the uvcdynctrl into the driver. Hopefully this effort can help in deprecating the UVCIOC_CTRL_MAP ioctl. Some background about UVCIOC_CTRL_MAP (thanks Laurent for the context): ``` this was envisioned as the base of a vibrant ecosystem where a large number of vendors would submit XML files that describe their XU control mappings, at a pace faster than could be supported by adding XU mappings to the driver. This vision failed to materialize and the tool has not been updated for 10+ years now. There is no reason to believe the situation will change. ``` During the porting, the following mappings where NOT imported because they were not using standard v4l2 IDs. It is recommended that userspace moves to UVCIOC_CTRL_QUERY for non standard controls. { .id = V4L2_CID_FLASH_MODE, .entity = UVC_GUID_SIS_LED_HW_CONTROL, .selector = 4, .size = 4, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0x3, .menu_mapping = { 0x20, 0x22 }, .menu_names = { "Off", "On" }, }, { .id = V4L2_CID_FLASH_FREQUENCY, .entity = UVC_GUID_SIS_LED_HW_CONTROL, .selector = 4, .size = 8, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_LED1_MODE, .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, .selector = 1, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0xF, .menu_mapping = { 0, 1, 2, 3 }, .menu_names = { "Off", "On", "Blinking", "Auto" }, }, { .id = V4L2_CID_LED1_FREQUENCY, .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, .selector = 1, .size = 8, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_DISABLE_PROCESSING, .entity = UVC_GUID_LOGITECH_VIDEO_PIPE_V1, .selector = 5, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, { .id = V4L2_CID_RAW_BITS_PER_PIXEL, .entity = UVC_GUID_LOGITECH_VIDEO_PIPE_V1, .selector = 8, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_LED1_MODE, .entity = UVC_GUID_LOGITECH_PERIPHERAL, .selector = 0x09, .size = 2, .offset = 8, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0xF, .menu_mapping = { 0, 1, 2, 3 }, .menu_names = { "Off", "On", "Blink", "Auto" }, }, { .id = V4L2_CID_LED1_FREQUENCY, .entity = UVC_GUID_LOGITECH_PERIPHERAL, .selector = 0x09, .size = 8, .offset = 24, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, This script has been used to generate the mappings. They were then reformatted manually to follow the driver style. import sys import uuid import re import xml.etree.ElementTree as ET def get_namespace(root): return re.match(r"\{.*\}", root.tag).group(0) def get_single_guid(ns, constant): id = constant.find(ns + "id").text value = constant.find(ns + "value").text return (id, value) def get_constants(ns, root): out = dict() for constant in root.iter(ns + "constant"): attr = constant.attrib if attr["type"] == "integer": id, value = get_single_guid(ns, constant) if id in out: print(f"dupe constant {id}") out[id] = value return out def get_guids(ns, root): out = dict() for constant in root.iter(ns + "constant"): attr = constant.attrib if attr["type"] == "guid": id, value = get_single_guid(ns, constant) if id in out: print(f"dupe guid {id}") out[id] = value return out def get_single_control(ns, control): out = {} for id in "entity", "selector", "index", "size", "description": v = control.find(ns + id) if v is None and id == "description": continue out[id] = v.text reqs = set() for r in control.find(ns + "requests"): reqs.add(r.text) out["requests"] = reqs return (control.attrib["id"], out) def get_controls(ns, root): out = dict() for control in root.iter(ns + "control"): id, value = get_single_control(ns, control) if id in out: print(f"Dupe control id {id}") out[id] = value return out def get_single_mapping(ns, mapping): out = {} out["name"] = mapping.find(ns + "name").text uvc = mapping.find(ns + "uvc") for id in "size", "offset", "uvc_type": out[id] = uvc.find(ns + id).text out["control_ref"] = uvc.find(ns + "control_ref").attrib["idref"] v4l2 = mapping.find(ns + "v4l2") for id in "id", "v4l2_type": out[id] = v4l2.find(ns + id).text menu = {} for entry in v4l2.iter(ns + "menu_entry"): menu[entry.attrib["name"]] = entry.attrib["value"] if menu: out["menu"] = menu return out def get_mapping(ns, root): out = [] for control in root.iter(ns + "mapping"): mapping = get_single_mapping(ns, control) out += [mapping] return out def print_guids(guids): for g in guids: print(f"#define {g} \\") u_bytes = uuid.UUID(guids[g]).bytes_le u_bytes = [f"0x{b:02x}" for b in u_bytes] print("\t{ " + ", ".join(u_bytes) + " }") def print_flags(flags): get_range = {"GET_MIN", "GET_DEF", "GET_MAX", "GET_CUR", "GET_RES"} if get_range.issubset(flags): flags -= get_range flags.add("GET_RANGE") flags = list(flags) flags.sort() out = "" for f in flags[:-1]: out += f"UVC_CTRL_FLAG_{f}\n\t\t\t\t| " out += f"UVC_CTRL_FLAG_{flags[-1]}" return out def print_description(desc): print("/*") for line in desc.strip().splitlines(): print(f" * {line.strip()}") print("*/") def print_controls(controls, cons): for id in controls: c = controls[id] if "description" in c: print_description(c["description"]) print( f"""\t{{ \t\t.entity\t\t= {c["entity"]}, \t\t.selector\t= {cons[c["selector"]]}, \t\t.index\t\t= {c["index"]}, \t\t.size\t\t= {c["size"]}, \t\t.flags\t\t= {print_flags(c["requests"])}, \t}},""" ) def menu_mapping_txt(menu): out = f"\n\t\t.menu_mask\t= 0x{((1< Cc: Martin Rubli Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 174 +++++++++++++++++++++++++++++++ include/linux/usb/uvc.h | 6 ++ 2 files changed, 180 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b9a3d9257a11..b6e020b41671 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -385,6 +385,99 @@ static const struct uvc_control_info uvc_ctrls[] = { | UVC_CTRL_FLAG_GET_RANGE | UVC_CTRL_FLAG_RESTORE, }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + * The unit of the pan/tilt values is 1/64th of a degree and the + * resolution is 1 degree. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + * + * Both bits can be set at the same time to reset both, pan and tilt, + * at the same time. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of focus motor movements for camera models that + * support mechanical focus. + * + * Bits 0 to 7 allow selection of the desired lens position. + * There are no physical units, instead, the focus range is spread over + * 256 logical units with 0 representing infinity focus and 255 being + * macro focus. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .index = 2, + .size = 6, + .flags = UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, }; static const u32 uvc_control_classes[] = { @@ -1009,6 +1102,87 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .menu_mask = BIT(V4L2_COLORFX_VIVID) | BIT(V4L2_COLORFX_NONE), }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, }; /* ------------------------------------------------------------------------ diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 05bfebab42b6..84f35c6f4d6e 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -43,6 +43,12 @@ #define UVC_GUID_MSXU_1_5 \ {0xdc, 0x95, 0x3f, 0x0f, 0x32, 0x26, 0x4e, 0x4c, \ 0x92, 0xc9, 0xa0, 0x47, 0x82, 0xf4, 0x3b, 0xc8} +#define UVC_GUID_LOGITECH_MOTOR_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56 } +#define UVC_GUID_LOGITECH_PERIPHERAL \ + {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \ + 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd } /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */ #define UVC_MSXU_CONTROL_FOCUS 0x01 From 85355f7268af2e1cdb771842de59ac6be42863d5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:45 +0000 Subject: [PATCH 0616/5214] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP The UVCIOC_CTRL_MAP lets userspace create a mapping for a custom control. This mapping is usually created by the uvcdynctrl userspace utility. We would like to get the mappings into the driver instead. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/drivers/uvcvideo.rst | 2 ++ drivers/media/usb/uvc/uvc_v4l2.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst index dbb30ad389ae..b09d2f8ba66e 100644 --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst @@ -109,6 +109,8 @@ IOCTL reference UVCIOC_CTRL_MAP - Map a UVC control to a V4L2 control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**This IOCTL is deprecated and will be eventually removed** + Argument: struct uvc_xu_control_mapping **Description**: diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index cda1697204ea..fa852b789193 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -1049,6 +1049,8 @@ static long uvc_ioctl_default(struct file *file, void *priv, bool valid_prio, switch (cmd) { /* Dynamic controls. */ case UVCIOC_CTRL_MAP: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP ioctl will be eventually removed.\n"); return uvc_ioctl_xu_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: @@ -1163,6 +1165,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, switch (cmd) { case UVCIOC_CTRL_MAP32: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP32 ioctl will be eventually removed.\n"); ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); if (ret) break; From 02f93b4f940c66b99258cce954dcaeda7b3295a8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:46 +0000 Subject: [PATCH 0617/5214] media: uvcvideo: Introduce allow_privacy_override module parameter Some camera modules have XU controls that can configure the behaviour of the privacy LED. Block mapping of those controls, unless the module is configured with a new parameter: allow_privacy_override. Signed-off-by: Ricardo Ribalda [johannes.goede@oss.qualcomm.com: Remove deprecation warning from param] Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 38 ++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvc_driver.c | 4 ++++ drivers/media/usb/uvc/uvc_v4l2.c | 7 ++++++ drivers/media/usb/uvc/uvcvideo.h | 2 ++ include/linux/usb/uvc.h | 4 ++++ 5 files changed, 55 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b6e020b41671..3ca108b83f1d 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, return ret; } +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector) +{ + /* + * This list is not exhaustive, it is a best effort to block access to + * non documented controls that can affect user's privacy. + */ + struct privacy_control { + u8 entity[16]; + u8 selector; + } privacy_control[] = { + { + .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, + .selector = 1, + }, + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 9, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(privacy_control); i++) + if (!memcmp(entity, privacy_control[i].entity, 16) && + selector == privacy_control[i].selector) + return true; + + return false; +} + int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry) { @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, return -ENOENT; } + if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n"); + uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n", + entity->guid, xqry->selector); + return -EACCES; + } + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 31b4ac3b48c1..e289cc71ba98 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; +bool uvc_allow_privacy_override_param; static struct usb_driver uvc_driver; @@ -2504,6 +2505,9 @@ module_param_named(trace, uvc_dbg_param, uint, 0644); MODULE_PARM_DESC(trace, "Trace level bitmask"); module_param_named(timeout, uvc_timeout_param, uint, 0644); MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); +module_param_named(allow_privacy_override, uvc_allow_privacy_override_param, bool, 0644); +MODULE_PARM_DESC(allow_privacy_override, + "Allow access to privacy related controls"); /* ------------------------------------------------------------------------ * Driver initialization and cleanup diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index fa852b789193..644c2f1cd8e6 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain, return -EINVAL; } + if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n"); + return -EACCES; + } + map = kzalloc_obj(*map); if (map == NULL) return -ENOMEM; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 0a0c01b2420f..4ba35727e954 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -666,6 +666,7 @@ extern unsigned int uvc_no_drop_param; extern unsigned int uvc_dbg_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; +extern bool uvc_allow_privacy_override_param; #define uvc_dbg(_dev, flag, fmt, ...) \ do { \ @@ -791,6 +792,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); void uvc_ctrl_cleanup_fh(struct uvc_fh *handle); +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector); /* Utility functions */ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 84f35c6f4d6e..99b070ab860f 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -49,6 +49,10 @@ #define UVC_GUID_LOGITECH_PERIPHERAL \ {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \ 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd } +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f } + /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */ #define UVC_MSXU_CONTROL_FOCUS 0x01 From 817998f863981b8b3f9829798dc4ec9fa275bdaf Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:02 +0800 Subject: [PATCH 0618/5214] media: qcom: camss: Fix csid IRQ offset for sa8775p Fix BUF_DONE_IRQ_STATUS_RDI_OFFSET calculation for csid lite on sa8775p platform. The offset should be 0 for csid lite on sa8775p, Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Fixes: ed03e99de0fa ("media: qcom: camss: Add support for CSID 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-csid-gen3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index 664245cf6eb0..bd059243790e 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -48,9 +48,9 @@ #define IS_CSID_690(csid) ((csid->camss->res->version == CAMSS_8775P) \ || (csid->camss->res->version == CAMSS_8300)) #define CSID_BUF_DONE_IRQ_STATUS 0x8C -#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ?\ - 1 : (IS_CSID_690(csid) ?\ - 13 : 14)) +#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? \ + ((IS_CSID_690(csid) ? 0 : 1)) : \ + ((IS_CSID_690(csid) ? 13 : 14))) #define CSID_BUF_DONE_IRQ_MASK 0x90 #define CSID_BUF_DONE_IRQ_CLEAR 0x94 #define CSID_BUF_DONE_IRQ_SET 0x98 From 27c6817bbc0edfe11669d0185e7ae5dc7a113edb Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:03 +0800 Subject: [PATCH 0619/5214] media: qcom: camss: Fix csid clock configuration for sa8775p Fix the mismatch between clock list and clock rate table for CSID lite instances. The current implementation has 5 clocks defined but only 2 are actually needed (vfe_lite_csid and vfe_lite_cphy_rx), while the clock rate table doesn't match this configuration. Update both clock list and rate table to maintain consistency: - Remove unused clocks: cpas_vfe_lite, vfe_lite_ahb, vfe_lite - Update clock rate table to match the remaining two clocks Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Fixes: ed03e99de0fa ("media: qcom: camss: Add support for CSID 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss.c | 40 +++++++++-------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 00b87fd9afbd..cb0134718985 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3598,12 +3598,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite0" }, .interrupt = { "csid_lite0" }, @@ -3617,12 +3615,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite1" }, .interrupt = { "csid_lite1" }, @@ -3636,12 +3632,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite2" }, .interrupt = { "csid_lite2" }, @@ -3655,12 +3649,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite3" }, .interrupt = { "csid_lite3" }, @@ -3674,12 +3666,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite4" }, .interrupt = { "csid_lite4" }, From 924f45e58b1fefca0e6accd45a508029436e112e Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:04 +0800 Subject: [PATCH 0620/5214] media: qcom: camss: Add missing clocks for VFE lite on sa8775p Add missing required clocks (cpas_ahb and camnoc_axi) for VFE lite instances on sa8775p platform. These clocks are necessary for proper VFE lite operation: Reviewed-by: Bryan O'Donoghue Signed-off-by: Wenmeng Liu Fixes: e7b59e1d06fb ("media: qcom: camss: Add support for VFE 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss.c | 40 ++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index cb0134718985..9335636d7c4d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3742,15 +3742,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, @@ -3765,15 +3767,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, @@ -3788,15 +3792,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite2" }, .interrupt = { "vfe_lite2" }, @@ -3811,15 +3817,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite3" }, .interrupt = { "vfe_lite3" }, @@ -3834,15 +3842,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite4" }, .interrupt = { "vfe_lite4" }, From 797c1cbf672f372d6a464df0dcedf476fc715969 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Mar 2026 16:18:24 +0100 Subject: [PATCH 0621/5214] media: qcom: camss: avoid format string warning clang-22 warns about csiphy_match_clock_name() taking a variable format string that is not checked against the 'int index' argument: drivers/media/platform/qcom/camss/camss-csiphy.c:566:44: error: diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'csiphy_match_clock_name' [-Werror,-Wmissing-format-attribute] 561 | static bool csiphy_match_clock_name(const char *clock_name, const char *format, | __attribute__((format(printf, 2, 3))) 562 | int index) 563 | { 564 | char name[16]; /* csiphyXXX_timer\0 */ 565 | 566 | snprintf(name, sizeof(name), format, index); | ^ drivers/media/platform/qcom/camss/camss-csiphy.c:561:13: note: 'csiphy_match_clock_name' declared here 561 | static bool csiphy_match_clock_name(const char *clock_name, const char *format, | ^ Change the function to use a snprintf() style format string that allows this to be checked at the call site. Signed-off-by: Arnd Bergmann Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-csiphy.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 62623393f414..78a1b568dbae 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -558,12 +558,16 @@ static int csiphy_init_formats(struct v4l2_subdev *sd, return csiphy_set_format(sd, fh ? fh->state : NULL, &format); } -static bool csiphy_match_clock_name(const char *clock_name, const char *format, - int index) +static bool __printf(2, 3) +csiphy_match_clock_name(const char *clock_name, const char *format, ...) { char name[16]; /* csiphyXXX_timer\0 */ + va_list args; + + va_start(args, format); + vsnprintf(name, sizeof(name), format, args); + va_end(args); - snprintf(name, sizeof(name), format, index); return !strcmp(clock_name, name); } From 983f4496b95eab85997054511a6c59575d4120b1 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 16 Feb 2026 09:54:19 +0100 Subject: [PATCH 0622/5214] dt-bindings: media: camss: Add qcom,sm6350-camss Add bindings for the Camera Subsystem on the SM6350 SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Reviewed-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sm6350-camss.yaml | 471 ++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml new file mode 100644 index 000000000000..96974d90d8c4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml @@ -0,0 +1,471 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,sm6350-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM6350 Camera Subsystem (CAMSS) + +maintainers: + - Luca Weiss + +description: + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. + +properties: + compatible: + const: qcom,sm6350-camss + + reg: + maxItems: 24 + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5_csr + - const: a5_qgic + - const: a5_sierra + - const: bps + - const: camnoc + - const: core_top_csr_tcsr + - const: cpas_cdm + - const: cpas_top + - const: ipe + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + clocks: + maxItems: 39 + + clock-names: + items: + - const: cam_axi + - const: soc_ahb + - const: camnoc_axi + - const: core_ahb + - const: cpas_ahb + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: csiphy3 + - const: csiphy3_timer + - const: vfe0_axi + - const: vfe0 + - const: vfe0_cphy_rx + - const: vfe0_csid + - const: vfe1_axi + - const: vfe1 + - const: vfe1_cphy_rx + - const: vfe1_csid + - const: vfe2_axi + - const: vfe2 + - const: vfe2_cphy_rx + - const: vfe2_csid + - const: vfe_lite + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + - const: bps + - const: bps_ahb + - const: bps_areg + - const: bps_axi + - const: icp + - const: ipe0 + - const: ipe0_ahb + - const: ipe0_areg + - const: ipe0_axi + - const: jpeg + - const: lrme + + interrupts: + maxItems: 18 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5 + - const: cpas + - const: cpas_cdm + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + interconnects: + maxItems: 4 + + interconnect-names: + items: + - const: ahb + - const: hf_mnoc + - const: sf_mnoc + - const: sf_icp_mnoc + + iommus: + maxItems: 14 + + power-domains: + maxItems: 6 + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: ife2 + - const: top + - const: bps + - const: ipe + + vdd-csiphy0-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY0. + + vdd-csiphy0-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY0. + + vdd-csiphy1-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY1. + + vdd-csiphy1-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY1. + + vdd-csiphy2-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY2. + + vdd-csiphy2-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY2. + + vdd-csiphy3-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY3. + + vdd-csiphy3-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY3. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + description: + CSI input ports. + + patternProperties: + "^port@[0-3]$": + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + description: + Input port for receiving CSI data from a CSIPHY. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + + required: + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + - ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@acb3000 { + compatible = "qcom,sm6350-camss"; + + reg = <0x0 0x0acb3000 0x0 0x1000>, + <0x0 0x0acba000 0x0 0x1000>, + <0x0 0x0acc1000 0x0 0x1000>, + <0x0 0x0acc8000 0x0 0x1000>, + <0x0 0x0ac65000 0x0 0x1000>, + <0x0 0x0ac66000 0x0 0x1000>, + <0x0 0x0ac67000 0x0 0x1000>, + <0x0 0x0ac68000 0x0 0x1000>, + <0x0 0x0acaf000 0x0 0x4000>, + <0x0 0x0acb6000 0x0 0x4000>, + <0x0 0x0acbd000 0x0 0x4000>, + <0x0 0x0acc4000 0x0 0x4000>, + <0x0 0x0ac18000 0x0 0x3000>, + <0x0 0x0ac00000 0x0 0x6000>, + <0x0 0x0ac10000 0x0 0x8000>, + <0x0 0x0ac6f000 0x0 0x8000>, + <0x0 0x0ac42000 0x0 0x4600>, + <0x0 0x01fc0000 0x0 0x40000>, + <0x0 0x0ac48000 0x0 0x1000>, + <0x0 0x0ac40000 0x0 0x1000>, + <0x0 0x0ac87000 0x0 0xa000>, + <0x0 0x0ac52000 0x0 0x4000>, + <0x0 0x0ac4e000 0x0 0x4000>, + <0x0 0x0ac6b000 0x0 0xa00>; + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5_csr", + "a5_qgic", + "a5_sierra", + "bps", + "camnoc", + "core_top_csr_tcsr", + "cpas_cdm", + "cpas_top", + "ipe", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + clocks = <&gcc GCC_CAMERA_AXI_CLK>, + <&camcc CAMCC_SOC_AHB_CLK>, + <&camcc CAMCC_CAMNOC_AXI_CLK>, + <&camcc CAMCC_CORE_AHB_CLK>, + <&camcc CAMCC_CPAS_AHB_CLK>, + <&camcc CAMCC_CSIPHY0_CLK>, + <&camcc CAMCC_CSI0PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY1_CLK>, + <&camcc CAMCC_CSI1PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY2_CLK>, + <&camcc CAMCC_CSI2PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY3_CLK>, + <&camcc CAMCC_CSI3PHYTIMER_CLK>, + <&camcc CAMCC_IFE_0_AXI_CLK>, + <&camcc CAMCC_IFE_0_CLK>, + <&camcc CAMCC_IFE_0_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_0_CSID_CLK>, + <&camcc CAMCC_IFE_1_AXI_CLK>, + <&camcc CAMCC_IFE_1_CLK>, + <&camcc CAMCC_IFE_1_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_1_CSID_CLK>, + <&camcc CAMCC_IFE_2_AXI_CLK>, + <&camcc CAMCC_IFE_2_CLK>, + <&camcc CAMCC_IFE_2_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_2_CSID_CLK>, + <&camcc CAMCC_IFE_LITE_CLK>, + <&camcc CAMCC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_LITE_CSID_CLK>, + <&camcc CAMCC_BPS_CLK>, + <&camcc CAMCC_BPS_AHB_CLK>, + <&camcc CAMCC_BPS_AREG_CLK>, + <&camcc CAMCC_BPS_AXI_CLK>, + <&camcc CAMCC_ICP_CLK>, + <&camcc CAMCC_IPE_0_CLK>, + <&camcc CAMCC_IPE_0_AHB_CLK>, + <&camcc CAMCC_IPE_0_AREG_CLK>, + <&camcc CAMCC_IPE_0_AXI_CLK>, + <&camcc CAMCC_JPEG_CLK>, + <&camcc CAMCC_LRME_CLK>; + clock-names = "cam_axi", + "soc_ahb", + "camnoc_axi", + "core_ahb", + "cpas_ahb", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "vfe0_axi", + "vfe0", + "vfe0_cphy_rx", + "vfe0_csid", + "vfe1_axi", + "vfe1", + "vfe1_cphy_rx", + "vfe1_csid", + "vfe2_axi", + "vfe2", + "vfe2_cphy_rx", + "vfe2_csid", + "vfe_lite", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "bps", + "bps_ahb", + "bps_areg", + "bps_axi", + "icp", + "ipe0", + "ipe0_ahb", + "ipe0_areg", + "ipe0_axi", + "jpeg", + "lrme"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5", + "cpas", + "cpas_cdm", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc", + "sf_icp_mnoc"; + + iommus = <&apps_smmu 0x820 0xc0>, + <&apps_smmu 0x840 0x0>, + <&apps_smmu 0x860 0xc0>, + <&apps_smmu 0x880 0x0>, + <&apps_smmu 0xc40 0x20>, + <&apps_smmu 0xc60 0x20>, + <&apps_smmu 0xc80 0x0>, + <&apps_smmu 0xca2 0x0>, + <&apps_smmu 0xcc0 0x20>, + <&apps_smmu 0xce0 0x20>, + <&apps_smmu 0xd00 0x20>, + <&apps_smmu 0xd20 0x20>, + <&apps_smmu 0xd40 0x20>, + <&apps_smmu 0xd60 0x20>; + + power-domains = <&camcc IFE_0_GDSC>, + <&camcc IFE_1_GDSC>, + <&camcc IFE_2_GDSC>, + <&camcc TITAN_TOP_GDSC>, + <&camcc BPS_GDSC>, + <&camcc IPE_0_GDSC>; + power-domain-names = "ife0", + "ife1", + "ife2", + "top", + "bps", + "ipe"; + + vdd-csiphy0-0p9-supply = <&vreg_l18a>; + vdd-csiphy0-1p25-supply = <&vreg_l22a>; + vdd-csiphy1-0p9-supply = <&vreg_l18a>; + vdd-csiphy1-1p25-supply = <&vreg_l22a>; + vdd-csiphy2-0p9-supply = <&vreg_l18a>; + vdd-csiphy2-1p25-supply = <&vreg_l22a>; + vdd-csiphy3-0p9-supply = <&vreg_l18a>; + vdd-csiphy3-1p25-supply = <&vreg_l22a>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csiphy0_ep: endpoint { + data-lanes = <0 1 2 3>; + bus-type = ; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; From f1db6810902d70767180b550614a936f0613c0a2 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 16 Feb 2026 09:54:20 +0100 Subject: [PATCH 0623/5214] media: qcom: camss: Add SM6350 support Add the necessary support for CAMSS on the SM6350 SoC. Reviewed-by: Bryan O'Donoghue Signed-off-by: Luca Weiss Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue --- .../qcom/camss/camss-csiphy-3ph-1-0.c | 125 +++++++++ drivers/media/platform/qcom/camss/camss-vfe.c | 2 + drivers/media/platform/qcom/camss/camss.c | 261 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 4 files changed, 389 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index 415483274552..dac8d2ecf799 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -399,6 +399,126 @@ csiphy_lane_regs lane_regs_sm8250[] = { {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, }; +/* GEN2 1.2.3 2PH */ +static const struct +csiphy_lane_regs lane_regs_sm6350[] = { + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0910, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0900, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0908, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0010, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x005c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c90, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c80, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c88, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0704, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0720, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0210, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x023c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0204, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0220, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x025c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0260, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0410, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x045c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0610, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x063c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0604, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0620, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x065c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0660, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, +}; + /* 14nm 2PH v 2.0.1 2p5Gbps 4 lane DPHY mode */ static const struct csiphy_lane_regs lane_regs_qcm2290[] = { @@ -1011,6 +1131,7 @@ static bool csiphy_is_gen2(u32 version) switch (version) { case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: @@ -1105,6 +1226,10 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_regs = &lane_regs_qcm2290[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290); break; + case CAMSS_6350: + regs->lane_regs = &lane_regs_sm6350[0]; + regs->lane_array_size = ARRAY_SIZE(lane_regs_sm6350); + break; case CAMSS_7280: case CAMSS_8250: regs->lane_regs = &lane_regs_sm8250[0]; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 5baf0e3d4bc4..7dc937d018f6 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -343,6 +343,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_660: case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8x96: case CAMSS_8250: @@ -2003,6 +2004,7 @@ static int vfe_bpl_align(struct vfe_device *vfe) switch (vfe->camss->res->version) { case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9335636d7c4d..506d84dd3b97 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1703,6 +1703,253 @@ static const struct resources_icc icc_res_sm6150[] = { }, }; +static const struct camss_subdev_resources csiphy_res_sm6350[] = { + /* CSIPHY0 */ + { + .regulators = { + { .supply = "vdd-csiphy0-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy0-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = { + { .supply = "vdd-csiphy1-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy1-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = { + { .supply = "vdd-csiphy2-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy2-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY3 */ + { + .regulators = { + { .supply = "vdd-csiphy3-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy3-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .id = 3, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + } +}; + +static const struct camss_subdev_resources csid_res_sm6350[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 (lite) */ + { + .regulators = {}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 400000000, 480000000 } }, + .reg = { "csid_lite" }, + .interrupt = { "csid_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + } +}; + +static const struct camss_subdev_resources vfe_res_sm6350[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe0", + "vfe0_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe1", + "vfe1_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe2", + "vfe2_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 (lite) */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe_lite", + "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite" }, + .interrupt = { "vfe_lite" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sm6350[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 0, + .icc_bw_tbl.peak = 300000, + }, + { + .name = "hf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_icp_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_8250[] = { /* CSIPHY0 */ { @@ -5233,6 +5480,19 @@ static const struct camss_resources sm6150_resources = { .vfe_num = ARRAY_SIZE(vfe_res_sm6150), }; +static const struct camss_resources sm6350_resources = { + .version = CAMSS_6350, + .pd_name = "top", + .csiphy_res = csiphy_res_sm6350, + .csid_res = csid_res_sm6350, + .vfe_res = vfe_res_sm6350, + .icc_res = icc_res_sm6350, + .icc_path_num = ARRAY_SIZE(icc_res_sm6350), + .csiphy_num = ARRAY_SIZE(csiphy_res_sm6350), + .csid_num = ARRAY_SIZE(csid_res_sm6350), + .vfe_num = ARRAY_SIZE(vfe_res_sm6350), +}; + static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", @@ -5329,6 +5589,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm6150-camss", .data = &sm6150_resources }, + { .compatible = "qcom,sm6350-camss", .data = &sm6350_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources }, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 6d048414c919..d323c105d185 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -81,6 +81,7 @@ enum camss_version { CAMSS_660, CAMSS_2290, CAMSS_6150, + CAMSS_6350, CAMSS_7280, CAMSS_8x16, CAMSS_8x39, From 70b193987c8c025618b6dea7303e7972053e9313 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:45 +0800 Subject: [PATCH 0624/5214] media: qcom: camss: Add common TPG support Introduce a new common Test Pattern Generator (TPG) implementation for Qualcomm CAMSS. This module provides a generic interface for pattern generation that can be reused by multiple platforms. Unlike CSID-integrated TPG, this TPG acts as a standalone block that emulates both CSIPHY and sensor behavior, enabling flexible test patterns without external hardware. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/Makefile | 11 +- drivers/media/platform/qcom/camss/camss-tpg.c | 519 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss-tpg.h | 118 ++++ drivers/media/platform/qcom/camss/camss.h | 5 + 4 files changed, 648 insertions(+), 5 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-tpg.c create mode 100644 drivers/media/platform/qcom/camss/camss-tpg.h diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 5e349b491513..d747aa7db3c1 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -10,10 +10,13 @@ qcom-camss-objs += \ camss-csid-680.o \ camss-csid-gen2.o \ camss-csid-gen3.o \ + camss-csiphy.o \ camss-csiphy-2ph-1-0.o \ camss-csiphy-3ph-1-0.o \ - camss-csiphy.o \ + camss-format.o \ camss-ispif.o \ + camss-tpg.o \ + camss-vfe.o \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ camss-vfe-4-8.o \ @@ -21,11 +24,9 @@ qcom-camss-objs += \ camss-vfe-340.o \ camss-vfe-480.o \ camss-vfe-680.o \ - camss-vfe-gen3.o \ camss-vfe-gen1.o \ + camss-vfe-gen3.o \ camss-vfe-vbif.o \ - camss-vfe.o \ - camss-video.o \ - camss-format.o \ + camss-video.o obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-tpg.c b/drivers/media/platform/qcom/camss/camss-tpg.c new file mode 100644 index 000000000000..c5b75132add4 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-tpg.h" +#include "camss.h" + +static const struct tpg_format_info formats_gen1[] = { + { + MEDIA_BUS_FMT_SBGGR8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, +}; + +const struct tpg_formats tpg_formats_gen1 = { + .nformats = ARRAY_SIZE(formats_gen1), + .formats = formats_gen1 +}; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return &formats[i]; + + return ERR_PTR(-EINVAL); +} + +static int tpg_set_clock_rates(struct tpg_device *tpg) +{ + struct device *dev = tpg->camss->dev; + int i, ret; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + long round_rate; + + if (clock->freq) { + round_rate = clk_round_rate(clock->clk, clock->freq[0]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + ret = clk_set_rate(clock->clk, round_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +static int tpg_set_power(struct v4l2_subdev *sd, int on) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct device *dev = tpg->camss->dev; + + if (on) { + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = tpg_set_clock_rates(tpg); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + ret = camss_enable_clocks(tpg->nclocks, tpg->clock, dev); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + tpg->res->hw_ops->reset(tpg); + + tpg->res->hw_ops->hw_version(tpg); + } else { + camss_disable_clocks(tpg->nclocks, tpg->clock); + + pm_runtime_put_sync(dev); + } + + return 0; +} + +static int tpg_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = v4l2_ctrl_handler_setup(&tpg->ctrls); + if (ret < 0) { + dev_err(tpg->camss->dev, + "could not sync v4l2 controls: %d\n", ret); + return ret; + } + } + + return tpg->res->hw_ops->configure_stream(tpg, enable); +} + +static struct v4l2_mbus_framefmt * +__tpg_get_format(struct tpg_device *tpg, + struct v4l2_subdev_state *sd_state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_state_get_format(sd_state, + pad); + + return &tpg->fmt; +} + +static void tpg_try_format(struct tpg_device *tpg, + struct v4l2_mbus_framefmt *fmt) +{ + unsigned int i; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fmt->code) + break; + + if (i >= tpg->res->formats->nformats) + fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + fmt->width = clamp_t(u32, fmt->width, TPG_MIN_WIDTH, TPG_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, TPG_MIN_HEIGHT, TPG_MAX_HEIGHT); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int tpg_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + + if (code->index >= tpg->res->formats->nformats) + return -EINVAL; + + code->code = tpg->res->formats->formats[code->index].code; + + return 0; +} + +static int tpg_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + unsigned int i; + + if (fse->index != 0) + return -EINVAL; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fse->code) + break; + + if (i >= tpg->res->formats->nformats) + return -EINVAL; + + fse->min_width = TPG_MIN_WIDTH; + fse->min_height = TPG_MIN_HEIGHT; + fse->max_width = TPG_MAX_WIDTH; + fse->max_height = TPG_MAX_HEIGHT; + + return 0; +} + +static int tpg_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int tpg_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + tpg_try_format(tpg, &fmt->format); + *format = fmt->format; + + return 0; +} + +static int tpg_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_TPG_PAD_SRC, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .width = 1920, + .height = 1080, + } + }; + + return tpg_set_format(sd, fh ? fh->state : NULL, &format); +} + +static int tpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tpg_device *tpg = container_of(ctrl->handler, + struct tpg_device, ctrls); + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ret = tpg->res->hw_ops->configure_testgen_pattern(tpg, ctrl->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops tpg_ctrl_ops = { + .s_ctrl = tpg_s_ctrl, +}; + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id) +{ + struct platform_device *pdev; + struct device *dev; + int i, j; + + dev = camss->dev; + pdev = to_platform_device(dev); + + tpg->camss = camss; + tpg->id = id; + tpg->res = &res->tpg; + tpg->res->hw_ops->subdev_init(tpg); + + tpg->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); + if (IS_ERR(tpg->base)) + return PTR_ERR(tpg->base); + + tpg->nclocks = 0; + while (res->clock[tpg->nclocks]) + tpg->nclocks++; + + if (!tpg->nclocks) + return 0; + + tpg->clock = devm_kcalloc(dev, tpg->nclocks, + sizeof(*tpg->clock), GFP_KERNEL); + if (!tpg->clock) + return -ENOMEM; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, clock->nfreqs, + sizeof(*clock->freq), GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + return 0; +} + +static int tpg_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + + return 0; +} + +static const struct v4l2_subdev_core_ops tpg_core_ops = { + .s_power = tpg_set_power, +}; + +static const struct v4l2_subdev_video_ops tpg_video_ops = { + .s_stream = tpg_set_stream, +}; + +static const struct v4l2_subdev_pad_ops tpg_pad_ops = { + .enum_mbus_code = tpg_enum_mbus_code, + .enum_frame_size = tpg_enum_frame_size, + .get_fmt = tpg_get_format, + .set_fmt = tpg_set_format, +}; + +static const struct v4l2_subdev_ops tpg_v4l2_ops = { + .core = &tpg_core_ops, + .video = &tpg_video_ops, + .pad = &tpg_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops tpg_v4l2_internal_ops = { + .open = tpg_init_formats, +}; + +static const struct media_entity_operations tpg_media_ops = { + .link_setup = tpg_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &tpg->subdev; + struct device *dev = tpg->camss->dev; + int ret; + + v4l2_subdev_init(sd, &tpg_v4l2_ops); + sd->internal_ops = &tpg_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + "msm_tpg", tpg->id); + sd->grp_id = TPG_GRP_ID; + v4l2_set_subdevdata(sd, tpg); + + ret = v4l2_ctrl_handler_init(&tpg->ctrls, 1); + if (ret < 0) { + dev_err(dev, "Failed to init ctrl handler: %d\n", ret); + return ret; + } + + tpg->testgen_mode = v4l2_ctrl_new_std_menu_items(&tpg->ctrls, + &tpg_ctrl_ops, V4L2_CID_TEST_PATTERN, + tpg->testgen.nmodes, 0, 0, + tpg->testgen.modes); + if (tpg->ctrls.error) { + dev_err(dev, "Failed to init ctrl: %d\n", tpg->ctrls.error); + ret = tpg->ctrls.error; + goto free_ctrl; + } + + tpg->subdev.ctrl_handler = &tpg->ctrls; + + ret = tpg_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto free_ctrl; + } + + tpg->pad.flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.ops = &tpg_media_ops; + ret = media_entity_pads_init(&sd->entity, 1, &tpg->pad); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto free_ctrl; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + media_entity_cleanup(&sd->entity); + goto free_ctrl; + } + + return 0; + +free_ctrl: + v4l2_ctrl_handler_free(&tpg->ctrls); + + return ret; +} + +void msm_tpg_unregister_entity(struct tpg_device *tpg) +{ + v4l2_device_unregister_subdev(&tpg->subdev); + media_entity_cleanup(&tpg->subdev.entity); + v4l2_ctrl_handler_free(&tpg->ctrls); +} diff --git a/drivers/media/platform/qcom/camss/camss-tpg.h b/drivers/media/platform/qcom/camss/camss-tpg.h new file mode 100644 index 000000000000..7fb35a97dd06 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-tpg.h + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef QC_MSM_CAMSS_TPG_H +#define QC_MSM_CAMSS_TPG_H + +#include +#include +#include +#include +#include +#include +#include + +#define ENCODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 +#define ENCODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 +#define ENCODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 +#define ENCODE_FORMAT_UNCOMPRESSED_14_BIT 0x4 +#define ENCODE_FORMAT_UNCOMPRESSED_16_BIT 0x5 +#define ENCODE_FORMAT_UNCOMPRESSED_20_BIT 0x6 +#define ENCODE_FORMAT_UNCOMPRESSED_24_BIT 0x7 + +#define MSM_TPG_PAD_SRC 0 +#define MSM_TPG_ACTIVE_VC 0 +#define MSM_TPG_ACTIVE_DT 0 + +#define TPG_MIN_WIDTH 1 +#define TPG_MIN_HEIGHT 1 +#define TPG_MAX_WIDTH 8191 +#define TPG_MAX_HEIGHT 8191 + +#define TPG_GRP_ID 0 + +enum tpg_testgen_mode { + TPG_PAYLOAD_MODE_DISABLED = 0, + TPG_PAYLOAD_MODE_INCREMENTING = 1, + TPG_PAYLOAD_MODE_ALTERNATING_55_AA = 2, + TPG_PAYLOAD_MODE_RANDOM = 5, + TPG_PAYLOAD_MODE_USER_SPECIFIED = 6, + TPG_PAYLOAD_MODE_COLOR_BARS = 9, + TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 9, +}; + +struct tpg_testgen_config { + enum tpg_testgen_mode mode; + const char * const*modes; + u8 nmodes; +}; + +struct tpg_format_info { + u32 code; + u8 data_type; + u8 encode_format; + u8 bpp; +}; + +struct tpg_formats { + unsigned int nformats; + const struct tpg_format_info *formats; +}; + +struct tpg_device; + +struct tpg_hw_ops { + int (*configure_stream)(struct tpg_device *tpg, u8 enable); + int (*configure_testgen_pattern)(struct tpg_device *tpg, s32 val); + u32 (*hw_version)(struct tpg_device *tpg); + int (*reset)(struct tpg_device *tpg); + void (*subdev_init)(struct tpg_device *tpg); +}; + +struct tpg_subdev_resources { + u8 lane_cnt; + const struct tpg_formats *formats; + const struct tpg_hw_ops *hw_ops; +}; + +struct tpg_device { + struct camss *camss; + u8 id; + struct v4l2_subdev subdev; + struct media_pad pad; + void __iomem *base; + struct camss_clock *clock; + int nclocks; + struct tpg_testgen_config testgen; + struct v4l2_mbus_framefmt fmt; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *testgen_mode; + const struct tpg_subdev_resources *res; + u32 hw_version; +}; + +struct camss_subdev_resources; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code); + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id); + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev); + +void msm_tpg_unregister_entity(struct tpg_device *tpg); + +extern const struct tpg_formats tpg_formats_gen1; + +extern const struct tpg_hw_ops tpg_ops_gen1; + +#endif /* QC_MSM_CAMSS_TPG_H */ diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index d323c105d185..93d691c8ac63 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -21,6 +21,7 @@ #include "camss-csid.h" #include "camss-csiphy.h" #include "camss-ispif.h" +#include "camss-tpg.h" #include "camss-vfe.h" #include "camss-format.h" @@ -52,6 +53,7 @@ struct camss_subdev_resources { char *interrupt[CAMSS_RES_MAX]; union { struct csiphy_subdev_resources csiphy; + struct tpg_subdev_resources tpg; struct csid_subdev_resources csid; struct vfe_subdev_resources vfe; }; @@ -106,6 +108,7 @@ struct camss_resources { enum camss_version version; const char *pd_name; const struct camss_subdev_resources *csiphy_res; + const struct camss_subdev_resources *tpg_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; const struct camss_subdev_resources *vfe_res; @@ -113,6 +116,7 @@ struct camss_resources { const struct resources_icc *icc_res; const unsigned int icc_path_num; const unsigned int csiphy_num; + const unsigned int tpg_num; const unsigned int csid_num; const unsigned int vfe_num; }; @@ -123,6 +127,7 @@ struct camss { struct media_device media_dev; struct device *dev; struct csiphy_device *csiphy; + struct tpg_device *tpg; struct csid_device *csid; struct ispif_device *ispif; struct vfe_device *vfe; From 4b14db418b6e9fe4cb1f50120bb26f73d3a1573f Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:46 +0800 Subject: [PATCH 0625/5214] media: qcom: camss: Add link support for TPG TPG is connected to the csid as an entity, the link needs to be adapted. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-csid.c | 45 ++++++++++----- .../media/platform/qcom/camss/camss-csid.h | 1 + .../media/platform/qcom/camss/camss-csiphy.c | 1 + .../media/platform/qcom/camss/camss-csiphy.h | 2 + drivers/media/platform/qcom/camss/camss.c | 55 +++++++++++++++++++ 5 files changed, 90 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index ed1820488c98..48459b46a981 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -35,6 +35,8 @@ #define HW_VERSION_REVISION 16 #define HW_VERSION_GENERATION 28 +#define LANE_CFG_BITWIDTH 4 + #define MSM_CSID_NAME "msm_csid" const char * const csid_testgen_modes[] = { @@ -1215,18 +1217,22 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) } /* - * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter - * @lane_cfg - CSI2 lane configuration + * csid_get_lane_assign - Calculate lane assign by csiphy/tpg lane num + * @lane_cfg: CSI2 lane configuration + * @num_lanes: lane num * * Return lane assign */ -static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) +static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg, int num_lanes) { u32 lane_assign = 0; + int pos; int i; - for (i = 0; i < lane_cfg->num_data; i++) - lane_assign |= lane_cfg->data[i].pos << (i * 4); + for (i = 0; i < num_lanes; i++) { + pos = lane_cfg ? lane_cfg->data[i].pos : i; + lane_assign |= pos << (i * LANE_CFG_BITWIDTH); + } return lane_assign; } @@ -1251,6 +1257,7 @@ static int csid_link_setup(struct media_entity *entity, if ((local->flags & MEDIA_PAD_FL_SINK) && (flags & MEDIA_LNK_FL_ENABLED)) { struct v4l2_subdev *sd; + struct tpg_device *tpg; struct csid_device *csid; struct csiphy_device *csiphy; struct csiphy_lanes_cfg *lane_cfg; @@ -1265,18 +1272,28 @@ static int csid_link_setup(struct media_entity *entity, return -EBUSY; sd = media_entity_to_v4l2_subdev(remote->entity); - csiphy = v4l2_get_subdevdata(sd); + if (sd->grp_id == TPG_GRP_ID) { + tpg = v4l2_get_subdevdata(sd); - /* If a sensor is not linked to CSIPHY */ - /* do no allow a link from CSIPHY to CSID */ - if (!csiphy->cfg.csi2) - return -EPERM; + csid->phy.lane_cnt = tpg->res->lane_cnt; + csid->phy.csiphy_id = tpg->id; + csid->phy.lane_assign = csid_get_lane_assign(NULL, csid->phy.lane_cnt); + csid->tpg_linked = true; + } else { + csiphy = v4l2_get_subdevdata(sd); - csid->phy.csiphy_id = csiphy->id; + /* If a sensor is not linked to CSIPHY */ + /* do no allow a link from CSIPHY to CSID */ + if (!csiphy->cfg.csi2) + return -EPERM; - lane_cfg = &csiphy->cfg.csi2->lane_cfg; - csid->phy.lane_cnt = lane_cfg->num_data; - csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); + csid->phy.csiphy_id = csiphy->id; + + lane_cfg = &csiphy->cfg.csi2->lane_cfg; + csid->phy.lane_cnt = lane_cfg->num_data; + csid->phy.lane_assign = csid_get_lane_assign(lane_cfg, lane_cfg->num_data); + csid->tpg_linked = false; + } } /* Decide which virtual channels to enable based on which source pads are enabled */ if (local->flags & MEDIA_PAD_FL_SOURCE) { diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index aedc96ed84b2..5296b10f6bac 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -161,6 +161,7 @@ struct csid_device { int num_supplies; struct completion reset_complete; struct csid_testgen_config testgen; + bool tpg_linked; struct csid_phy_config phy; struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 78a1b568dbae..539ac4888b60 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -793,6 +793,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", MSM_CSIPHY_NAME, csiphy->id); + sd->grp_id = CSIPHY_GRP_ID; v4l2_set_subdevdata(sd, csiphy); ret = csiphy_init_formats(sd, NULL); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 2d5054819df7..9d9657b82f74 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -21,6 +21,8 @@ #define MSM_CSIPHY_PAD_SRC 1 #define MSM_CSIPHY_PADS_NUM 2 +#define CSIPHY_GRP_ID 1 + struct csiphy_lane { u8 pos; u8 pol; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 506d84dd3b97..bd351ceadda7 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -4748,6 +4748,19 @@ static int camss_init_subdevices(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_subdev_init(camss, &camss->tpg[i], + &res->tpg_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init tpg%d sub-device: %d\n", + i, ret); + return ret; + } + } + } + /* note: SM8250 requires VFE to be initialized before CSID */ for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_subdev_init(camss, &camss->vfe[i], @@ -4836,6 +4849,23 @@ static int camss_link_entities(struct camss *camss) } } + for (i = 0; i < camss->res->tpg_num; i++) { + for (j = 0; j < camss->res->csid_num; j++) { + ret = media_create_pad_link(&camss->tpg[i].subdev.entity, + MSM_TPG_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); + if (ret < 0) { + camss_link_err(camss, + camss->tpg[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); + return ret; + } + } + } + if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { @@ -4940,6 +4970,19 @@ static int camss_register_entities(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_register_entity(&camss->tpg[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register tpg%d entity: %d\n", + i, ret); + goto err_reg_tpg; + } + } + } + for (i = 0; i < camss->res->csid_num; i++) { ret = msm_csid_register_entity(&camss->csid[i], &camss->v4l2_dev); @@ -4983,6 +5026,13 @@ static int camss_register_entities(struct camss *camss) for (i--; i >= 0; i--) msm_csid_unregister_entity(&camss->csid[i]); + i = camss->res->tpg_num; +err_reg_tpg: + if (camss->tpg) { + for (i--; i >= 0; i--) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + i = camss->res->csiphy_num; err_reg_csiphy: for (i--; i >= 0; i--) @@ -5004,6 +5054,11 @@ static void camss_unregister_entities(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) msm_csiphy_unregister_entity(&camss->csiphy[i]); + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + for (i = 0; i < camss->res->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); From 51fe835c485beaebe041d441d2be9840882bb152 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:47 +0800 Subject: [PATCH 0626/5214] media: qcom: camss: tpg: Add TPG support for multiple targets Add support for TPG found on LeMans, Monaco, Hamoa. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csid-680.c | 14 +- .../platform/qcom/camss/camss-csid-gen3.c | 14 +- .../platform/qcom/camss/camss-tpg-gen1.c | 231 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.c | 117 ++++++++- 5 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-tpg-gen1.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index d747aa7db3c1..27898b3cc7d3 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -16,6 +16,7 @@ qcom-camss-objs += \ camss-format.o \ camss-ispif.o \ camss-tpg.o \ + camss-tpg-gen1.o \ camss-vfe.o \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 3ad3a174bcfb..2e4547455d22 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -103,6 +103,8 @@ #define CSI2_RX_CFG0_PHY_NUM_SEL 20 #define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1 #define CSI2_RX_CFG0_PHY_TYPE_SEL 24 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0) @@ -185,10 +187,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { u32 val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index bd059243790e..222765fe7914 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -66,6 +66,8 @@ #define CSI2_RX_CFG0_VC_MODE 3 #define CSI2_RX_CFG0_DL0_INPUT_SEL 4 #define CSI2_RX_CFG0_PHY_NUM_SEL 20 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0) @@ -109,10 +111,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { int val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c new file mode 100644 index 000000000000..d29de5f93c18 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG (Test Pattern Generator) Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include + +#include "camss-tpg.h" +#include "camss.h" + +/* TPG global registers */ +#define TPG_HW_VERSION 0x0 +# define HW_VERSION_STEPPING GENMASK(15, 0) +# define HW_VERSION_REVISION GENMASK(27, 16) +# define HW_VERSION_GENERATION GENMASK(31, 28) + +#define TPG_HW_VER(gen, rev, step) \ + (((u32)(gen) << 28) | ((u32)(rev) << 16) | (u32)(step)) + +#define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0) +#define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0) + +#define TPG_HW_STATUS 0x4 + +#define TPG_CTRL 0x64 +# define TPG_CTRL_TEST_EN BIT(0) +# define TPG_CTRL_PHY_SEL BIT(3) +# define TPG_CTRL_NUM_ACTIVE_LANES GENMASK(5, 4) +# define TPG_CTRL_VC_DT_PATTERN_ID GENMASK(8, 6) +# define TPG_CTRL_OVERLAP_SHDR_EN BIT(10) +# define TPG_CTRL_NUM_ACTIVE_VC GENMASK(31, 30) + +#define TPG_CLEAR 0x1F4 + +/* TPG VC-based registers */ +#define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60) + +#define TPG_VC_n_CFG0(n) (0x68 + (n) * 0x60) +# define TPG_VC_n_CFG0_VC_NUM GENMASK(4, 0) +# define TPG_VC_n_CFG0_NUM_ACTIVE_DT GENMASK(9, 8) +# define TPG_VC_n_CFG0_NUM_BATCH GENMASK(15, 12) +# define TPG_VC_n_CFG0_NUM_FRAMES GENMASK(31, 16) + +#define TPG_VC_n_LSFR_SEED(n) (0x6C + (n) * 0x60) +#define TPG_VC_n_HBI_CFG(n) (0x70 + (n) * 0x60) +#define TPG_VC_n_VBI_CFG(n) (0x74 + (n) * 0x60) + +#define TPG_VC_n_COLOR_BARS_CFG(n) (0x78 + (n) * 0x60) +# define TPG_VC_n_COLOR_BARS_CFG_PIX_PATTERN GENMASK(2, 0) +# define TPG_VC_n_COLOR_BARS_CFG_QCFA_EN BIT(3) +# define TPG_VC_n_COLOR_BARS_CFG_SPLIT_EN BIT(4) +# define TPG_VC_n_COLOR_BARS_CFG_NOISE_EN BIT(5) +# define TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD GENMASK(13, 8) +# define TPG_VC_n_COLOR_BARS_CFG_XCFA_EN BIT(16) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_X GENMASK(26, 24) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_Y GENMASK(30, 28) + +/* TPG DT-based registers */ +#define TPG_VC_m_DT_n_CFG_0(m, n) (0x7C + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT GENMASK(15, 0) +# define TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_1(m, n) (0x80 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_1_DATA_TYPE GENMASK(5, 0) +# define TPG_VC_m_DT_n_CFG_1_ECC_XOR_MASK GENMASK(13, 8) +# define TPG_VC_m_DT_n_CFG_1_CRC_XOR_MASK GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_2(m, n) (0x84 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE GENMASK(3, 0) +/* v2.0.0: USER[19:4], ENC[23:20] */ +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(19, 4) +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(23, 20) +/* v2.1.0: USER[27:4], ENC[31:28] */ +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(27, 4) +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(31, 28) + +#define TPG_HBI_PCT_DEFAULT 545 /* 545% */ +#define TPG_VBI_PCT_DEFAULT 10 /* 10% */ +#define PERCENT_BASE 100 + +/* Default user-specified payload for TPG test generator. + * Keep consistent with CSID TPG default: 0xBE. + */ +#define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE +#define TPG_LFSR_SEED_DEFAULT 0x12345678 +#define TPG_COLOR_BARS_CFG_STANDARD \ + FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA) + +static const char * const testgen_payload_modes[] = { + [TPG_PAYLOAD_MODE_DISABLED] = "Disabled", + [TPG_PAYLOAD_MODE_INCREMENTING] = "Incrementing", + [TPG_PAYLOAD_MODE_ALTERNATING_55_AA] = "Alternating 0x55/0xAA", + [TPG_PAYLOAD_MODE_RANDOM] = "Pseudo-random Data", + [TPG_PAYLOAD_MODE_USER_SPECIFIED] = "User Specified", + [TPG_PAYLOAD_MODE_COLOR_BARS] = "Color bars", +}; + +static int tpg_stream_on(struct tpg_device *tpg) +{ + struct tpg_testgen_config *tg = &tpg->testgen; + struct v4l2_mbus_framefmt *input_format; + const struct tpg_format_info *format; + u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ? + tg->mode - 1 : 0; + u8 lane_cnt = tpg->res->lane_cnt; + u8 vc, dt, last_vc = 0; + u32 val; + + for (vc = 0; vc <= MSM_TPG_ACTIVE_VC; vc++) { + last_vc = vc; + + input_format = &tpg->fmt; + format = tpg_get_fmt_entry(tpg->res->formats->formats, + tpg->res->formats->nformats, + input_format->code); + if (IS_ERR(format)) + return -EINVAL; + + /* VC configuration */ + val = FIELD_PREP(TPG_VC_n_CFG0_NUM_ACTIVE_DT, MSM_TPG_ACTIVE_DT) | + FIELD_PREP(TPG_VC_n_CFG0_NUM_FRAMES, 0); + writel(val, tpg->base + TPG_VC_n_CFG0(vc)); + + writel(TPG_LFSR_SEED_DEFAULT, tpg->base + TPG_VC_n_LSFR_SEED(vc)); + + val = DIV_ROUND_UP(input_format->width * format->bpp * TPG_HBI_PCT_DEFAULT, + BITS_PER_BYTE * lane_cnt * PERCENT_BASE); + writel(val, tpg->base + TPG_VC_n_HBI_CFG(vc)); + + val = input_format->height * TPG_VBI_PCT_DEFAULT / PERCENT_BASE; + writel(val, tpg->base + TPG_VC_n_VBI_CFG(vc)); + + writel(TPG_COLOR_BARS_CFG_STANDARD, tpg->base + TPG_VC_n_COLOR_BARS_CFG(vc)); + + /* DT configuration */ + for (dt = 0; dt <= MSM_TPG_ACTIVE_DT; dt++) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT, + input_format->height & 0xffff) | + FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH, + input_format->width & 0xffff); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_0(vc, dt)); + + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_1_DATA_TYPE, format->data_type); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_1(vc, dt)); + + if (tpg->hw_version == TPG_HW_VER_2_0_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } else if (tpg->hw_version >= TPG_HW_VER_2_1_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_2(vc, dt)); + } + } + + /* Global TPG control */ + val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc); + writel(val, tpg->base + TPG_CTRL); + + return 0; +} + +static int tpg_reset(struct tpg_device *tpg) +{ + writel(0, tpg->base + TPG_CTRL); + writel(1, tpg->base + TPG_CLEAR); + + return 0; +} + +static void tpg_stream_off(struct tpg_device *tpg) +{ + tpg_reset(tpg); +} + +static int tpg_configure_stream(struct tpg_device *tpg, u8 enable) +{ + if (enable) + return tpg_stream_on(tpg); + + tpg_stream_off(tpg); + + return 0; +} + +static int tpg_configure_testgen_pattern(struct tpg_device *tpg, s32 val) +{ + if (val >= 0 && val <= TPG_PAYLOAD_MODE_COLOR_BARS) + tpg->testgen.mode = val; + + return 0; +} + +static u32 tpg_hw_version(struct tpg_device *tpg) +{ + u32 hw_version = readl(tpg->base + TPG_HW_VERSION); + + tpg->hw_version = hw_version; + dev_dbg(tpg->camss->dev, "tpg HW Version = %u.%u.%u\n", + (u32)FIELD_GET(HW_VERSION_GENERATION, hw_version), + (u32)FIELD_GET(HW_VERSION_REVISION, hw_version), + (u32)FIELD_GET(HW_VERSION_STEPPING, hw_version)); + + return hw_version; +} + +static void tpg_subdev_init(struct tpg_device *tpg) +{ + tpg->testgen.modes = testgen_payload_modes; + tpg->testgen.nmodes = TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; +} + +const struct tpg_hw_ops tpg_ops_gen1 = { + .configure_stream = tpg_configure_stream, + .configure_testgen_pattern = tpg_configure_testgen_pattern, + .hw_version = tpg_hw_version, + .reset = tpg_reset, + .subdev_init = tpg_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index bd351ceadda7..2123f6388e3d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3806,6 +3806,54 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, }; +static const struct camss_subdev_resources tpg_res_8775p[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID0 */ { @@ -4210,6 +4258,54 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, }; +static const struct camss_subdev_resources tpg_res_x1e80100[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_x1e80100[] = { /* CSID0 */ { @@ -4323,7 +4419,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb", "vfe0" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4347,7 +4443,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb", "vfe1" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4371,7 +4467,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4394,7 +4490,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -5277,6 +5373,13 @@ static int camss_probe(struct platform_device *pdev) if (!camss->csiphy) return -ENOMEM; + if (camss->res->tpg_num > 0) { + camss->tpg = devm_kcalloc(dev, camss->res->tpg_num, + sizeof(*camss->tpg), GFP_KERNEL); + if (!camss->tpg) + return -ENOMEM; + } + camss->csid = devm_kcalloc(dev, camss->res->csid_num, sizeof(*camss->csid), GFP_KERNEL); if (!camss->csid) @@ -5466,11 +5569,13 @@ static const struct camss_resources qcs8300_resources = { .version = CAMSS_8300, .pd_name = "top", .csiphy_res = csiphy_res_8300, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_qcs8300, .csiphy_num = ARRAY_SIZE(csiphy_res_8300), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_qcs8300), @@ -5480,11 +5585,13 @@ static const struct camss_resources sa8775p_resources = { .version = CAMSS_8775P, .pd_name = "top", .csiphy_res = csiphy_res_8775p, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_sa8775p, .csiphy_num = ARRAY_SIZE(csiphy_res_8775p), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_sa8775p), @@ -5620,12 +5727,14 @@ static const struct camss_resources x1e80100_resources = { .version = CAMSS_X1E80100, .pd_name = "top", .csiphy_res = csiphy_res_x1e80100, + .tpg_res = tpg_res_x1e80100, .csid_res = csid_res_x1e80100, .vfe_res = vfe_res_x1e80100, .csid_wrapper_res = &csid_wrapper_res_x1e80100, .icc_res = icc_res_x1e80100, .icc_path_num = ARRAY_SIZE(icc_res_x1e80100), .csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100), + .tpg_num = ARRAY_SIZE(tpg_res_x1e80100), .csid_num = ARRAY_SIZE(csid_res_x1e80100), .vfe_num = ARRAY_SIZE(vfe_res_x1e80100), }; From c97e797a64cdfe4acecff831b4285418d2815893 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 19 Mar 2026 17:09:02 +0800 Subject: [PATCH 0627/5214] media: qcom: camss: vfe: fix PIX subdev naming on VFE lite VFE lite hardware does not provide a functional PIX path, but after the per sub-device type resource changes the PIX subdev name is still assigned unconditionally. Only assign the PIX subdev name on non-lite VFE variants to avoid exposing a misleading device name. Fixes: ae44829a4a97 ("media: qcom: camss: Add per sub-device type resources") Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-vfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 7dc937d018f6..cc9a9685f6b5 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2055,7 +2055,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, v4l2_subdev_init(sd, &vfe_v4l2_ops); sd->internal_ops = &vfe_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) + if (i == VFE_LINE_PIX && vfe->res->is_lite == false) snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", MSM_VFE_NAME, vfe->id, "pix"); else From 49a2cc83540c6abc81bb137d0e8da3b59b1c2fcf Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 25 Feb 2026 16:14:46 +0100 Subject: [PATCH 0628/5214] MAINTAINERS: add myself as a CAMSS patch reviewer Add myself as a reviewer of Qualcomm CAMSS subsystem patches and delete inactive maintainers (Todor & Robert). Signed-off-by: Loic Poulain Reviewed-by: Vladimir Zapolskiy Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 81d53481d3f7..5c66788998e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21834,10 +21834,9 @@ F: drivers/bluetooth/btqcomsmd.c F: drivers/bluetooth/hci_qca.c QUALCOMM CAMERA SUBSYSTEM DRIVER -M: Robert Foss -M: Todor Tomov M: Bryan O'Donoghue R: Vladimir Zapolskiy +R: Loic Poulain L: linux-media@vger.kernel.org S: Maintained F: Documentation/admin-guide/media/qcom_camss.rst From 5837c36e012924709c0aaa997c5c8efc898f6a75 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 26 Feb 2026 10:08:50 +0100 Subject: [PATCH 0629/5214] media: qcom: camss: Add debug message to camss-video format check Add a debug trace to video_check_format() to log both the subdev-reported format and the format requested by the video node. This makes it easier to diagnose mismatches between subdev output and the negotiated V4L2 pixel format, as well as issues related to plane count, resolution, or field settings. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-video.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 831486e14754..f52d8e84f970 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -215,6 +215,12 @@ static int video_check_format(struct camss_video *video) if (ret < 0) return ret; + dev_dbg(video->camss->dev, + "%s: format is (%ux%u %p4cc/%up field:%u), trying (%ux%u %p4cc/%up field:%u)", + video->vdev.name, sd_pix->width, sd_pix->height, &sd_pix->pixelformat, + sd_pix->num_planes, sd_pix->field, pix->width, pix->height, &pix->pixelformat, + pix->num_planes, pix->field); + if (pix->pixelformat != sd_pix->pixelformat || pix->height != sd_pix->height || pix->width != sd_pix->width || From 91978c39ee90f0549a2a74e2e56a3e09c5ce6877 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:50 +0100 Subject: [PATCH 0630/5214] media: qcom: camss: Add per-format BPL alignment helper Add camss_format_get_bpl_alignment(), a helper that returns the bytes-per-line (BPL) alignment requirement for a given CAMSS format. Different RAW Bayer packing schemes impose different BPL alignment constraints (e.g. RAW10 requires multiples of 5 bytes, RAW12 multiples of 3 bytes, RAW14 multiples of 7 bytes, etc.). Centralizing this logic makes the alignment rules explicit and avoids duplicating them across the pipeline. This will allow PIX paths and buffer preparation code to correctly round up BPL values to hardware-required boundaries. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-format.c | 14 ++++++++++++++ drivers/media/platform/qcom/camss/camss-format.h | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c index 4a3d5549615c..52cb01306ee0 100644 --- a/drivers/media/platform/qcom/camss/camss-format.c +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -7,8 +7,10 @@ * Copyright (c) 2023, The Linux Foundation. All rights reserved. * Copyright (c) 2023 Qualcomm Technologies, Inc. */ +#include #include #include +#include #include "camss-format.h" @@ -33,6 +35,18 @@ u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nf return formats[0].mbus_bpp; } +/* + * camss_format_get_bpl_alignment - Retrieve required BPL alignment for a given format. + * @format: a pointer to the format + * + * Return the required alignment, in bytes. + */ +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *format) +{ + /* Minimal number of bytes required to keep the line length an integer number of pixels */ + return lcm_not_zero(format->mbus_bpp, BITS_PER_BYTE) / BITS_PER_BYTE; +} + /* * camss_format_find_code - Find a format code in an array * @code: a pointer to media bus format codes array diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h index 923a48c9c3fb..4f87ac8c4975 100644 --- a/drivers/media/platform/qcom/camss/camss-format.h +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -55,6 +55,7 @@ struct camss_formats { }; u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *f); u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, unsigned int nformats); From ac437a96b7e4ecf92c59f296f8eb678dda018fc8 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:51 +0100 Subject: [PATCH 0631/5214] media: qcom: camss: Use proper BPL alignment helper and non-power-of-two rounding Bytes-per-line (BPL) alignment in CAMSS currently uses ALIGN(), which only works correctly for power-of-two values. Some RAW Bayer packing formats (e.g. RAW10/12/14) require non-power-of-two alignment such as 3, 5, or 7-byte multiples, so ALIGN() produces incorrect results. Introduce the use of roundup() with the per-format alignment returned by camss_format_get_bpl_alignment() when no hardware alignment is enforced (video->bpl_alignment). Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-video.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index f52d8e84f970..0852eb6f1315 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -47,6 +47,9 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, unsigned int i; u32 bytesperline; + if (!alignment) + alignment = camss_format_get_bpl_alignment(f); + memset(pix, 0, sizeof(*pix)); v4l2_fill_pix_format_mplane(pix, mbus); pix->pixelformat = f->pixelformat; @@ -54,7 +57,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, for (i = 0; i < pix->num_planes; i++) { bytesperline = pix->width / f->hsub[i].numerator * f->hsub[i].denominator * f->bpp[i] / 8; - bytesperline = ALIGN(bytesperline, alignment); + bytesperline = roundup(bytesperline, alignment); pix->plane_fmt[i].bytesperline = bytesperline; pix->plane_fmt[i].sizeimage = pix->height / f->vsub[i].numerator * f->vsub[i].denominator * @@ -459,6 +462,7 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) { + unsigned int alignment = video->bpl_alignment; struct v4l2_pix_format_mplane *pix_mp; const struct camss_format_info *fi; struct v4l2_plane_pix_format *p; @@ -491,6 +495,9 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) width = pix_mp->width; height = pix_mp->height; + if (!alignment) + alignment = camss_format_get_bpl_alignment(fi); + memset(pix_mp, 0, sizeof(*pix_mp)); pix_mp->pixelformat = fi->pixelformat; @@ -500,7 +507,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) for (i = 0; i < pix_mp->num_planes; i++) { bpl = pix_mp->width / fi->hsub[i].numerator * fi->hsub[i].denominator * fi->bpp[i] / 8; - bpl = ALIGN(bpl, video->bpl_alignment); + bpl = roundup(bpl, alignment); pix_mp->plane_fmt[i].bytesperline = bpl; pix_mp->plane_fmt[i].sizeimage = pix_mp->height / fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; @@ -525,7 +532,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) lines = p->sizeimage / p->bytesperline; if (p->bytesperline < bytesperline[i]) - p->bytesperline = ALIGN(bytesperline[i], 8); + p->bytesperline = roundup(bytesperline[i], alignment); if (p->sizeimage < p->bytesperline * lines) p->sizeimage = p->bytesperline * lines; From e458c14072d95698637f1630b9d972509dccd8d5 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:52 +0100 Subject: [PATCH 0632/5214] media: qcom: camss: vfe: Make PIX BPL alignment format-based on CAMSS_2290 Split the VFE bytes-per-line (BPL) alignment logic into separate helpers for RDI and PIX paths. RDI is usually aligned on RDI write engine bus constraint such as 64-bit or 128-bit. But PIX engine is usually (at least on platform I looked at) based on pixel format. On CAMSS_2290, PIX BPL alignment is set to 0 to indicate that the alignment must be derived from the pixel format. This allows the pipeline to use camss_format_get_bpl_alignment(). For other platforms, retain the legacy PIX default (16 bytes), until PIX is properly tested/enabled. A future improvement would be to remove platform-specific conditionals from the VFE code and move the alignment requirements into the per-platform VFE resource data. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue [bod: Fixed straggling newlines] Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-vfe.c | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index cc9a9685f6b5..319d19158988 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1998,7 +1998,7 @@ static const struct media_entity_operations vfe_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -static int vfe_bpl_align(struct vfe_device *vfe) +static int vfe_bpl_align_rdi(struct vfe_device *vfe) { int ret = 8; @@ -2023,6 +2023,24 @@ static int vfe_bpl_align(struct vfe_device *vfe) return ret; } +static int vfe_bpl_align_pix(struct vfe_device *vfe) +{ + int ret = 16; + + switch (vfe->camss->res->version) { + case CAMSS_2290: + /* The alignment/bpl depends solely on the pixel format and is + * computed dynamically in camss_format_get_bpl_alignment(). + */ + ret = 0; + break; + default: + break; + } + + return ret; +} + /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device @@ -2089,11 +2107,12 @@ int msm_vfe_register_entities(struct vfe_device *vfe, } video_out->ops = &vfe->video_ops; - video_out->bpl_alignment = vfe_bpl_align(vfe); - video_out->line_based = 0; if (i == VFE_LINE_PIX) { - video_out->bpl_alignment = 16; + video_out->bpl_alignment = vfe_bpl_align_pix(vfe); video_out->line_based = 1; + } else { + video_out->bpl_alignment = vfe_bpl_align_rdi(vfe); + video_out->line_based = 0; } video_out->nformats = vfe->line[i].nformats; From 93ea81d16570442dbca04d2e2563ae8c3e65fa1b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:51 +0100 Subject: [PATCH 0633/5214] media: qcom: camss: Fix RDI streaming for CSID 680 Fix streaming to RDI1 and RDI2. csid->phy.en_vc contains a bitmask of enabled CSID ports not virtual channels. We cycle through the number of available CSID ports and test this value against the vc_en bitmask. We then use the passed value both as an index to the port configuration macros and as a virtual channel index. This is a very broken pattern. Reviewing the initial introduction of VC support it states that you can only map one CSID to one VFE. This is true however each CSID has multiple sources which can sink inside of the VFE - for example there is a "pixel" path for bayer stats which sources @ CSID(x):3 and sinks on VFE(x):pix. That is CSID port # 3 should drive VFE port #3. With our current setup only a sensor which drives virtual channel number #3 could possibly enable that setup. This is deeply wrong the virtual channel has no relevance to hooking CSID to VFE, a fact that is proven after this patch is applied allowing RDI0,RDI1 and RDI2 to function with VC0 whereas before only RDI1 worked. Another way the current model breaks is the DT field. A sensor driving different data-types on the same VC would not be able to separate the VC:DT pair to separate RDI outputs, thus breaking another feature of VCs in the MIPI data-stream. Default the VC back to zero. A follow on series will implement subdev streams to actually enable VCs without breaking CSID source to VFE sink. Fixes: 253314b20408 ("media: qcom: camss: Add CSID 680 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-680.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 2e4547455d22..345a67c8fb94 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -231,9 +231,9 @@ static void __csid_configure_top(struct csid_device *csid) CSID_TOP_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -245,28 +245,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 lane_cnt = 4; val = 0; - writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; /* note: for non-RDI path, this should be format->decode_format */ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_TIMESTAMP_STB_FRAME; val |= RDI_CFG1_BYTE_CNTR_EN; @@ -277,23 +277,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_V_EN; val |= RDI_CFG1_PACKING_MIPI; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_ENABLE; else val &= ~RDI_CFG0_ENABLE; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -302,11 +302,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_top(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From cc1c35619c9895f918f9e388e3caa6ab2ff2fbe7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:52 +0100 Subject: [PATCH 0634/5214] media: qcom: camss: Fix RDI streaming for CSID 340 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID 340 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: f0fc808a466a ("media: qcom: camss: Add CSID 340 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-csid-340.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 2b50f9b96a34..0231985746ed 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -74,9 +74,9 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -88,14 +88,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); @@ -105,10 +105,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (enable) val |= CSID_RDI_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n", - csid->id, enable ? "enable" : "disable", format->data_type, vc); + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", + csid->id, enable ? "enable" : "disable", format->data_type, + port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -117,9 +118,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_rx(csid, &csid->phy); + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rdi_stream(csid, enable, i, 0); __csid_ctrl_rdi(csid, enable, i); } } From 618765634cefbdddafa84f07f82e9dd05b86cb9c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:53 +0100 Subject: [PATCH 0635/5214] media: qcom: camss: Fix RDI streaming for CSID GEN2 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen2 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: 729fc005c8e2 ("media: qcom: camss: Split testgen, RDI and RX for CSID 170") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-gen2.c | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 2a1746dcc1c5..eadcb2f7e3aa 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) writel_relaxed(val, csid->base + CSID_TPG_CTRL); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = 1 << RDI_CFG0_BYTE_CNTR_EN; val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; @@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); /* CSID_TIMESTAMP_STB_POST_IRQ */ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG1(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(port)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val = readl_relaxed(csid->base + CSID_RDI_CFG0(port)); val |= enable << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u8 i; - /* Loop through all enabled VCs and configure stream for each */ + + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { if (tg->enabled) - __csid_configure_testgen(csid, enable, i); + __csid_configure_testgen(csid, enable, i, 0); - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From ad136e52634d5a393a9dc93383f8ea1e898faf37 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:54 +0100 Subject: [PATCH 0636/5214] media: qcom: camss: Fix RDI streaming for CSID GEN3 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen3 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: d96fe1808dcc ("media: qcom: camss: Add CSID 780 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-gen3.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index 222765fe7914..0fdbf75fb27d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -157,12 +157,12 @@ static void __csid_configure_wrapper(struct csid_device *csid) writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { u32 val; u8 lane_cnt = csid->phy.lane_cnt; /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -175,14 +175,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = RDI_CFG0_TIMESTAMP_EN; val |= RDI_CFG0_TIMESTAMP_STB_SEL; @@ -192,7 +192,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DT; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_PACKING_FORMAT_MIPI; val |= RDI_CFG1_PIX_STORE; @@ -201,22 +201,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_H_EN; val |= RDI_CFG1_CROP_V_EN; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_EN; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -225,11 +225,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_wrapper(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From c41f66ea9dae235cc1a5c3108a4420483d730328 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 17:29:09 +0200 Subject: [PATCH 0637/5214] clk: qcom: dispcc-x1e80100: Fix (possibly) dumping regmap Reading few registers at the end of the block (e.g. 0x10000, 0x10004) might result in synchronous external abort, so limit the regmap to the last readable register which allows dumping the regs for debugging. Reported-by: Daniel J Blueman Closes: https://lore.kernel.org/r/CAMVG2su+V5fcZ9LOC0Qm3bpfnhpbmQdJackc7-RvfztDL_dajw@mail.gmail.com/ Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Tested-by: Daniel J Blueman Link: https://lore.kernel.org/r/20260505152908.302097-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/dispcc-x1e80100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/dispcc-x1e80100.c b/drivers/clk/qcom/dispcc-x1e80100.c index aa7fd43969f9..575ba90d2070 100644 --- a/drivers/clk/qcom/dispcc-x1e80100.c +++ b/drivers/clk/qcom/dispcc-x1e80100.c @@ -1634,7 +1634,7 @@ static const struct regmap_config disp_cc_x1e80100_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x11008, + .max_register = 0xf004, /* 0x10000, 0x10004 and maybe others are for TZ */ .fast_io = true, }; From 402b68cdc8f923ef3985d1061bf324c8ba3965a8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Apr 2026 19:09:00 +0200 Subject: [PATCH 0638/5214] clk: qcom: Constify qcom_cc_driver_data and list of critical CBCR registers The static 'struct qcom_cc_driver_data' and array 'xxx_critical_cbcrs' are already treated by common.c code as pointers to const, so constify few remaining pieces. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Reviewed-by: Vladimir Zapolskiy Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20260429170859.247165-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/gcc-nord.c | 2 +- drivers/clk/qcom/gpucc-sm8750.c | 4 ++-- drivers/clk/qcom/negcc-nord.c | 2 +- drivers/clk/qcom/nwgcc-nord.c | 4 ++-- drivers/clk/qcom/segcc-nord.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/qcom/gcc-nord.c b/drivers/clk/qcom/gcc-nord.c index 3098d8fac0fb..8a6e429f2640 100644 --- a/drivers/clk/qcom/gcc-nord.c +++ b/drivers/clk/qcom/gcc-nord.c @@ -1850,7 +1850,7 @@ static const struct regmap_config gcc_nord_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data gcc_nord_driver_data = { +static const struct qcom_cc_driver_data gcc_nord_driver_data = { .dfs_rcgs = gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(gcc_nord_dfs_clocks), }; diff --git a/drivers/clk/qcom/gpucc-sm8750.c b/drivers/clk/qcom/gpucc-sm8750.c index 5d52c6d8b5e5..1466bd36403f 100644 --- a/drivers/clk/qcom/gpucc-sm8750.c +++ b/drivers/clk/qcom/gpucc-sm8750.c @@ -421,7 +421,7 @@ static struct clk_alpha_pll *gpu_cc_alpha_plls[] = { &gpu_cc_pll0, }; -static u32 gpu_cc_sm8750_critical_cbcrs[] = { +static const u32 gpu_cc_sm8750_critical_cbcrs[] = { 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ 0x9008, /* GPU_CC_CXO_AON_CLK */ 0x9064, /* GPU_CC_GX_AHB_FF_CLK */ @@ -430,7 +430,7 @@ static u32 gpu_cc_sm8750_critical_cbcrs[] = { 0x93a8, /* GPU_CC_RSCC_HUB_AON_CLK */ }; -static struct qcom_cc_driver_data gpu_cc_sm8750_driver_data = { +static const struct qcom_cc_driver_data gpu_cc_sm8750_driver_data = { .alpha_plls = gpu_cc_alpha_plls, .num_alpha_plls = ARRAY_SIZE(gpu_cc_alpha_plls), .clk_cbcrs = gpu_cc_sm8750_critical_cbcrs, diff --git a/drivers/clk/qcom/negcc-nord.c b/drivers/clk/qcom/negcc-nord.c index 1aa24e2784e5..2cb66b0691a6 100644 --- a/drivers/clk/qcom/negcc-nord.c +++ b/drivers/clk/qcom/negcc-nord.c @@ -1945,7 +1945,7 @@ static void clk_nord_regs_configure(struct device *dev, struct regmap *regmap) qcom_branch_set_force_mem_core(regmap, ne_gcc_ufs_phy_axi_clk, true); } -static struct qcom_cc_driver_data ne_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data ne_gcc_nord_driver_data = { .dfs_rcgs = ne_gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(ne_gcc_nord_dfs_clocks), .clk_regs_configure = clk_nord_regs_configure, diff --git a/drivers/clk/qcom/nwgcc-nord.c b/drivers/clk/qcom/nwgcc-nord.c index 163ab63c872b..961cae47ff7c 100644 --- a/drivers/clk/qcom/nwgcc-nord.c +++ b/drivers/clk/qcom/nwgcc-nord.c @@ -626,7 +626,7 @@ static const struct qcom_reset_map nw_gcc_nord_resets[] = { [NW_GCC_VIDEO_BCR] = { 0x1a000 }, }; -static u32 nw_gcc_nord_critical_cbcrs[] = { +static const u32 nw_gcc_nord_critical_cbcrs[] = { 0x16004, /* NW_GCC_CAMERA_AHB_CLK */ 0x16030, /* NW_GCC_CAMERA_XO_CLK */ 0x18004, /* NW_GCC_DISP_0_AHB_CLK */ @@ -641,7 +641,7 @@ static u32 nw_gcc_nord_critical_cbcrs[] = { 0x1a044, /* NW_GCC_VIDEO_XO_CLK */ }; -static struct qcom_cc_driver_data nw_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data nw_gcc_nord_driver_data = { .clk_cbcrs = nw_gcc_nord_critical_cbcrs, .num_clk_cbcrs = ARRAY_SIZE(nw_gcc_nord_critical_cbcrs), }; diff --git a/drivers/clk/qcom/segcc-nord.c b/drivers/clk/qcom/segcc-nord.c index 1aab0999de4d..c82a56d97154 100644 --- a/drivers/clk/qcom/segcc-nord.c +++ b/drivers/clk/qcom/segcc-nord.c @@ -1568,7 +1568,7 @@ static const struct regmap_config se_gcc_nord_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data se_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data se_gcc_nord_driver_data = { .dfs_rcgs = se_gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(se_gcc_nord_dfs_clocks), }; From 0f90f883b340402e44ee2a9fa9f75ecb7c075a4f Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Tue, 5 May 2026 16:41:11 +0000 Subject: [PATCH 0639/5214] dt-bindings: interconnect: document the RPM Network-On-Chip interconnect in Shikra SoC Document the RPM Network-On-Chip Interconnect for the Qualcomm Shikra platform. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Reviewed-by: Krzysztof Kozlowski Signed-off-by: Raviteja Laggyshetty Link: https://patch.msgid.link/20260505-shikra_icc-v3-1-8e03ff27c007@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,shikra.yaml | 134 ++++++++++++++++++ .../dt-bindings/interconnect/qcom,shikra.h | 121 ++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,shikra.yaml create mode 100644 include/dt-bindings/interconnect/qcom,shikra.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,shikra.yaml b/Documentation/devicetree/bindings/interconnect/qcom,shikra.yaml new file mode 100644 index 000000000000..a0c26de94ccf --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,shikra.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,shikra.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shikra Network-On-Chip interconnect + +maintainers: + - Raviteja Laggyshetty + +description: + The Qualcomm Shikra interconnect providers support adjusting the + bandwidth requirements between the various NoC fabrics. + +properties: + compatible: + enum: + - qcom,shikra-config-noc + - qcom,shikra-mem-noc-core + - qcom,shikra-sys-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 + +# Child node's properties +patternProperties: + '^interconnect-[a-z0-9]+$': + type: object + description: + The interconnect providers do not have a separate QoS register space, + but share parent's space. + + $ref: qcom,rpm-common.yaml# + + properties: + compatible: + enum: + - qcom,shikra-clk-virt + - qcom,shikra-mc-virt + - qcom,shikra-mmrt-virt + - qcom,shikra-mmnrt-virt + + required: + - compatible + + unevaluatedProperties: false + +required: + - compatible + - reg + +allOf: + - $ref: qcom,rpm-common.yaml# + - if: + properties: + compatible: + const: qcom,shikra-mem-noc-core + + then: + properties: + clocks: + items: + - description: GPU-NoC AXI clock + + clock-names: + items: + - const: gpu_axi + patternProperties: + '^interconnect-[a-z0-9]+$': false + + - if: + properties: + compatible: + const: qcom,shikra-sys-noc + + then: + properties: + clocks: + items: + - description: EMAC0-NoC AXI clock. + - description: EMAC1-NoC AXI clock. + - description: USB2-NoC AXI clock. + - description: USB3-NoC AXI clock. + + clock-names: + items: + - const: emac0_axi + - const: emac1_axi + - const: usb2_axi + - const: usb3_axi + + - if: + properties: + compatible: + const: qcom,shikra-config-noc + + then: + properties: + clocks: false + clock-names: false + patternProperties: + '^interconnect-[a-z0-9]+$': false + +unevaluatedProperties: false + +examples: + - | + interconnect@1880000 { + compatible = "qcom,shikra-sys-noc"; + reg = <0x01880000 0x6a080>; + #interconnect-cells = <2>; + clocks = <&gcc_emac0_axi_sys_noc_clk>, + <&gcc_emac1_axi_sys_noc_clk>, + <&gcc_sys_noc_usb2_prim_axi_clk>, + <&gcc_sys_noc_usb3_prim_axi_clk>; + clock-names = "emac0_axi", + "emac1_axi", + "usb2_axi", + "usb3_axi"; + + interconnect-clk { + compatible = "qcom,shikra-clk-virt"; + #interconnect-cells = <2>; + }; + }; diff --git a/include/dt-bindings/interconnect/qcom,shikra.h b/include/dt-bindings/interconnect/qcom,shikra.h new file mode 100644 index 000000000000..a42ea22ee162 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,shikra.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_SHIKRA_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_SHIKRA_H + +#define MASTER_QUP_CORE_0 0 +#define SLAVE_QUP_CORE_0 1 + +#define SNOC_CNOC_MAS 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_AHB2PHY_USB 2 +#define SLAVE_APSS_THROTTLE_CFG 3 +#define SLAVE_AUDIO 4 +#define SLAVE_BOOT_ROM 5 +#define SLAVE_CAMERA_NRT_THROTTLE_CFG 6 +#define SLAVE_CAMERA_CFG 7 +#define SLAVE_CDSP_THROTTLE_CFG 8 +#define SLAVE_CLK_CTL 9 +#define SLAVE_DSP_CFG 10 +#define SLAVE_RBCPR_CX_CFG 11 +#define SLAVE_RBCPR_MX_CFG 12 +#define SLAVE_CRYPTO_0_CFG 13 +#define SLAVE_DDR_SS_CFG 14 +#define SLAVE_DISPLAY_CFG 15 +#define SLAVE_EMAC0_CFG 16 +#define SLAVE_EMAC1_CFG 17 +#define SLAVE_GPU_CFG 18 +#define SLAVE_GPU_THROTTLE_CFG 19 +#define SLAVE_HWKM 20 +#define SLAVE_IMEM_CFG 21 +#define SLAVE_MAPSS 22 +#define SLAVE_MDSP_MPU_CFG 23 +#define SLAVE_MESSAGE_RAM 24 +#define SLAVE_MSS 25 +#define SLAVE_PCIE_CFG 26 +#define SLAVE_PDM 27 +#define SLAVE_PIMEM_CFG 28 +#define SLAVE_PKA_WRAPPER_CFG 29 +#define SLAVE_PMIC_ARB 30 +#define SLAVE_QDSS_CFG 31 +#define SLAVE_QM_CFG 32 +#define SLAVE_QM_MPU_CFG 33 +#define SLAVE_QPIC 34 +#define SLAVE_QUP_0 35 +#define SLAVE_RPM 36 +#define SLAVE_SDCC_1 37 +#define SLAVE_SDCC_2 38 +#define SLAVE_SECURITY 39 +#define SLAVE_SNOC_CFG 40 +#define SNOC_SF_THROTTLE_CFG 41 +#define SLAVE_TLMM 42 +#define SLAVE_TSCSS 43 +#define SLAVE_USB2 44 +#define SLAVE_USB3 45 +#define SLAVE_VENUS_CFG 46 +#define SLAVE_VENUS_THROTTLE_CFG 47 +#define SLAVE_VSENSE_CTRL_CFG 48 +#define SLAVE_SERVICE_CNOC 49 + +#define MASTER_LLCC 0 +#define SLAVE_EBI_CH0 1 + +#define MASTER_GRAPHICS_3D 0 +#define MASTER_MNOC_HF_MEM_NOC 1 +#define MASTER_ANOC_PCIE_MEM_NOC 2 +#define MASTER_SNOC_SF_MEM_NOC 3 +#define MASTER_AMPSS_M0 4 +#define MASTER_SYS_TCU 5 +#define SLAVE_LLCC 6 +#define SLAVE_MEMNOC_SNOC 7 +#define SLAVE_MEM_NOC_PCIE_SNOC 8 + +#define MASTER_CAMNOC_SF 0 +#define MASTER_VIDEO_P0 1 +#define MASTER_VIDEO_PROC 2 +#define SLAVE_MMNRT_VIRT 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_MDP_PORT0 1 +#define MASTER_MMRT_VIRT 2 +#define SLAVE_MM_MEMNOC 3 + +#define MASTER_SNOC_CFG 0 +#define MASTER_TIC 1 +#define MASTER_ANOC_SNOC 2 +#define MASTER_MEMNOC_PCIE 3 +#define MASTER_MEMNOC_SNOC 4 +#define MASTER_PIMEM 5 +#define MASTER_PCIE2_0 6 +#define MASTER_QDSS_BAM 7 +#define MASTER_QPIC 8 +#define MASTER_QUP_0 9 +#define CNOC_SNOC_MAS 10 +#define MASTER_AUDIO 11 +#define MASTER_EMAC_0 12 +#define MASTER_EMAC_1 13 +#define MASTER_QDSS_ETR 14 +#define MASTER_SDCC_1 15 +#define MASTER_SDCC_2 16 +#define MASTER_USB2_0 17 +#define MASTER_USB3 18 +#define MASTER_CRYPTO_CORE0 19 +#define SLAVE_APPSS 20 +#define SLAVE_MCUSS 21 +#define SLAVE_WCSS 22 +#define SLAVE_MEMNOC_SF 23 +#define SNOC_CNOC_SLV 24 +#define SLAVE_BOOTIMEM 25 +#define SLAVE_OCIMEM 26 +#define SLAVE_PIMEM 27 +#define SLAVE_SERVICE_SNOC 28 +#define SLAVE_PCIE2_0 29 +#define SLAVE_QDSS_STM 30 +#define SLAVE_TCU 31 +#define SLAVE_PCIE_MEMNOC 32 +#define SLAVE_ANOC_SNOC 33 + +#endif From 24761ddafd5c045fb3ba825cffa39e132af4ff55 Mon Sep 17 00:00:00 2001 From: Raviteja Laggyshetty Date: Tue, 5 May 2026 16:41:12 +0000 Subject: [PATCH 0640/5214] interconnect: qcom: add Shikra interconnect provider driver Add driver for the Qualcomm interconnect buses found in Shikra based platforms. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. Co-developed-by: Odelu Kukatla Signed-off-by: Odelu Kukatla Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Raviteja Laggyshetty Link: https://patch.msgid.link/20260505-shikra_icc-v3-2-8e03ff27c007@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/shikra.c | 1837 ++++++++++++++++++++++++++++ 3 files changed, 1848 insertions(+) create mode 100644 drivers/interconnect/qcom/shikra.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 786b4eda44b4..c7c7df2a6ddb 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -283,6 +283,15 @@ config INTERCONNECT_QCOM_SDX75 This is a driver for the Qualcomm Network-on-Chip on sdx75-based platforms. +config INTERCONNECT_QCOM_SHIKRA + tristate "Qualcomm SHIKRA interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on shikra-based + platforms. + config INTERCONNECT_QCOM_SM6115 tristate "Qualcomm SM6115 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index cdf2c6c9fbf3..7c1834d383d2 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -35,6 +35,7 @@ qnoc-sdm845-objs := sdm845.o qnoc-sdx55-objs := sdx55.o qnoc-sdx65-objs := sdx65.o qnoc-sdx75-objs := sdx75.o +qnoc-shikra-objs := shikra.o qnoc-sm6115-objs := sm6115.o qnoc-sm6350-objs := sm6350.o qnoc-sm7150-objs := sm7150.o @@ -80,6 +81,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX55) += qnoc-sdx55.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX65) += qnoc-sdx65.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX75) += qnoc-sdx75.o +obj-$(CONFIG_INTERCONNECT_QCOM_SHIKRA) += qnoc-shikra.o obj-$(CONFIG_INTERCONNECT_QCOM_SM6115) += qnoc-sm6115.o obj-$(CONFIG_INTERCONNECT_QCOM_SM6350) += qnoc-sm6350.o obj-$(CONFIG_INTERCONNECT_QCOM_SM7150) += qnoc-sm7150.o diff --git a/drivers/interconnect/qcom/shikra.c b/drivers/interconnect/qcom/shikra.c new file mode 100644 index 000000000000..bc40d1592fb3 --- /dev/null +++ b/drivers/interconnect/qcom/shikra.c @@ -0,0 +1,1837 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icc-rpm.h" + +static const char * const sys_noc_intf_clocks[] = { + "emac0_axi", + "emac1_axi", + "usb2_axi", + "usb3_axi", +}; + +static const char * const memnoc_intf_clocks[] = { + "gpu_axi", +}; + +enum { + SHIKRA_MASTER_QUP_CORE_0 = 1, + SHIKRA_SNOC_CNOC_MAS, + SHIKRA_MASTER_QDSS_DAP, + SHIKRA_MASTER_LLCC, + SHIKRA_MASTER_GRAPHICS_3D, + SHIKRA_MASTER_MNOC_HF_MEM_NOC, + SHIKRA_MASTER_ANOC_PCIE_MEM_NOC, + SHIKRA_MASTER_SNOC_SF_MEM_NOC, + SHIKRA_MASTER_AMPSS_M0, + SHIKRA_MASTER_SYS_TCU, + SHIKRA_MASTER_CAMNOC_SF, + SHIKRA_MASTER_VIDEO_P0, + SHIKRA_MASTER_VIDEO_PROC, + SHIKRA_MASTER_CAMNOC_HF, + SHIKRA_MASTER_MDP_PORT0, + SHIKRA_MASTER_MMRT_VIRT, + SHIKRA_MASTER_SNOC_CFG, + SHIKRA_MASTER_TIC, + SHIKRA_MASTER_ANOC_SNOC, + SHIKRA_MASTER_MEMNOC_PCIE, + SHIKRA_MASTER_MEMNOC_SNOC, + SHIKRA_MASTER_PIMEM, + SHIKRA_MASTER_PCIE2_0, + SHIKRA_MASTER_QDSS_BAM, + SHIKRA_MASTER_QPIC, + SHIKRA_MASTER_QUP_0, + SHIKRA_CNOC_SNOC_MAS, + SHIKRA_MASTER_AUDIO, + SHIKRA_MASTER_EMAC_0, + SHIKRA_MASTER_EMAC_1, + SHIKRA_MASTER_QDSS_ETR, + SHIKRA_MASTER_SDCC_1, + SHIKRA_MASTER_SDCC_2, + SHIKRA_MASTER_USB2_0, + SHIKRA_MASTER_USB3, + SHIKRA_MASTER_CRYPTO_CORE0, + + SHIKRA_SLAVE_QUP_CORE_0, + SHIKRA_SLAVE_AHB2PHY_USB, + SHIKRA_SLAVE_APSS_THROTTLE_CFG, + SHIKRA_SLAVE_AUDIO, + SHIKRA_SLAVE_BOOT_ROM, + SHIKRA_SLAVE_CAMERA_NRT_THROTTLE_CFG, + SHIKRA_SLAVE_CAMERA_CFG, + SHIKRA_SLAVE_CDSP_THROTTLE_CFG, + SHIKRA_SLAVE_CLK_CTL, + SHIKRA_SLAVE_DSP_CFG, + SHIKRA_SLAVE_RBCPR_CX_CFG, + SHIKRA_SLAVE_RBCPR_MX_CFG, + SHIKRA_SLAVE_CRYPTO_0_CFG, + SHIKRA_SLAVE_DDR_SS_CFG, + SHIKRA_SLAVE_DISPLAY_CFG, + SHIKRA_SLAVE_EMAC0_CFG, + SHIKRA_SLAVE_EMAC1_CFG, + SHIKRA_SLAVE_GPU_CFG, + SHIKRA_SLAVE_GPU_THROTTLE_CFG, + SHIKRA_SLAVE_HWKM, + SHIKRA_SLAVE_IMEM_CFG, + SHIKRA_SLAVE_MAPSS, + SHIKRA_SLAVE_MDSP_MPU_CFG, + SHIKRA_SLAVE_MESSAGE_RAM, + SHIKRA_SLAVE_MSS, + SHIKRA_SLAVE_PCIE_CFG, + SHIKRA_SLAVE_PDM, + SHIKRA_SLAVE_PIMEM_CFG, + SHIKRA_SLAVE_PKA_WRAPPER_CFG, + SHIKRA_SLAVE_PMIC_ARB, + SHIKRA_SLAVE_QDSS_CFG, + SHIKRA_SLAVE_QM_CFG, + SHIKRA_SLAVE_QM_MPU_CFG, + SHIKRA_SLAVE_QPIC, + SHIKRA_SLAVE_QUP_0, + SHIKRA_SLAVE_RPM, + SHIKRA_SLAVE_SDCC_1, + SHIKRA_SLAVE_SDCC_2, + SHIKRA_SLAVE_SECURITY, + SHIKRA_SLAVE_SNOC_CFG, + SHIKRA_SNOC_SF_THROTTLE_CFG, + SHIKRA_SLAVE_TLMM, + SHIKRA_SLAVE_TSCSS, + SHIKRA_SLAVE_USB2, + SHIKRA_SLAVE_USB3, + SHIKRA_SLAVE_VENUS_CFG, + SHIKRA_SLAVE_VENUS_THROTTLE_CFG, + SHIKRA_SLAVE_VSENSE_CTRL_CFG, + SHIKRA_SLAVE_SERVICE_CNOC, + SHIKRA_SLAVE_EBI_CH0, + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, + SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, + SHIKRA_SLAVE_MMNRT_VIRT, + SHIKRA_SLAVE_MM_MEMNOC, + SHIKRA_SLAVE_APPSS, + SHIKRA_SLAVE_MCUSS, + SHIKRA_SLAVE_WCSS, + SHIKRA_SLAVE_MEMNOC_SF, + SHIKRA_SNOC_CNOC_SLV, + SHIKRA_SLAVE_BOOTIMEM, + SHIKRA_SLAVE_OCIMEM, + SHIKRA_SLAVE_PIMEM, + SHIKRA_SLAVE_SERVICE_SNOC, + SHIKRA_SLAVE_PCIE2_0, + SHIKRA_SLAVE_QDSS_STM, + SHIKRA_SLAVE_TCU, + SHIKRA_SLAVE_PCIE_MEMNOC, + SHIKRA_SLAVE_ANOC_SNOC, +}; + +/* Master nodes */ +static const u16 qup0_core_master_links[] = { + SHIKRA_SLAVE_QUP_CORE_0, +}; + +static struct qcom_icc_node qup0_core_master = { + .id = SHIKRA_MASTER_QUP_CORE_0, + .name = "qup0_core_master", + .buswidth = 4, + .mas_rpm_id = 170, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qup0_core_master_links), + .links = qup0_core_master_links, +}; + +static const u16 qnm_snoc_cnoc_links[] = { + SHIKRA_SLAVE_AHB2PHY_USB, + SHIKRA_SLAVE_APSS_THROTTLE_CFG, + SHIKRA_SLAVE_AUDIO, + SHIKRA_SLAVE_BOOT_ROM, + SHIKRA_SLAVE_CAMERA_NRT_THROTTLE_CFG, + SHIKRA_SLAVE_CAMERA_CFG, + SHIKRA_SLAVE_CDSP_THROTTLE_CFG, + SHIKRA_SLAVE_CLK_CTL, + SHIKRA_SLAVE_DSP_CFG, + SHIKRA_SLAVE_RBCPR_CX_CFG, + SHIKRA_SLAVE_RBCPR_MX_CFG, + SHIKRA_SLAVE_CRYPTO_0_CFG, + SHIKRA_SLAVE_DDR_SS_CFG, + SHIKRA_SLAVE_DISPLAY_CFG, + SHIKRA_SLAVE_EMAC0_CFG, + SHIKRA_SLAVE_EMAC1_CFG, + SHIKRA_SLAVE_GPU_CFG, + SHIKRA_SLAVE_GPU_THROTTLE_CFG, + SHIKRA_SLAVE_HWKM, + SHIKRA_SLAVE_IMEM_CFG, + SHIKRA_SLAVE_MAPSS, + SHIKRA_SLAVE_MDSP_MPU_CFG, + SHIKRA_SLAVE_MESSAGE_RAM, + SHIKRA_SLAVE_MSS, + SHIKRA_SLAVE_PCIE_CFG, + SHIKRA_SLAVE_PDM, + SHIKRA_SLAVE_PIMEM_CFG, + SHIKRA_SLAVE_PKA_WRAPPER_CFG, + SHIKRA_SLAVE_PMIC_ARB, + SHIKRA_SLAVE_QDSS_CFG, + SHIKRA_SLAVE_QM_CFG, + SHIKRA_SLAVE_QM_MPU_CFG, + SHIKRA_SLAVE_QPIC, + SHIKRA_SLAVE_QUP_0, + SHIKRA_SLAVE_RPM, + SHIKRA_SLAVE_SDCC_1, + SHIKRA_SLAVE_SDCC_2, + SHIKRA_SLAVE_SECURITY, + SHIKRA_SLAVE_SNOC_CFG, + SHIKRA_SNOC_SF_THROTTLE_CFG, + SHIKRA_SLAVE_TLMM, + SHIKRA_SLAVE_TSCSS, + SHIKRA_SLAVE_USB2, + SHIKRA_SLAVE_USB3, + SHIKRA_SLAVE_VENUS_CFG, + SHIKRA_SLAVE_VENUS_THROTTLE_CFG, + SHIKRA_SLAVE_VSENSE_CTRL_CFG, + SHIKRA_SLAVE_SERVICE_CNOC, +}; + +static struct qcom_icc_node qnm_snoc_cnoc = { + .id = SHIKRA_SNOC_CNOC_MAS, + .name = "qnm_snoc_cnoc", + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_snoc_cnoc_links), + .links = qnm_snoc_cnoc_links, +}; + +static const u16 xm_dap_links[] = { + SHIKRA_SLAVE_AHB2PHY_USB, + SHIKRA_SLAVE_APSS_THROTTLE_CFG, + SHIKRA_SLAVE_AUDIO, + SHIKRA_SLAVE_BOOT_ROM, + SHIKRA_SLAVE_CAMERA_NRT_THROTTLE_CFG, + SHIKRA_SLAVE_CAMERA_CFG, + SHIKRA_SLAVE_CDSP_THROTTLE_CFG, + SHIKRA_SLAVE_CLK_CTL, + SHIKRA_SLAVE_DSP_CFG, + SHIKRA_SLAVE_RBCPR_CX_CFG, + SHIKRA_SLAVE_RBCPR_MX_CFG, + SHIKRA_SLAVE_CRYPTO_0_CFG, + SHIKRA_SLAVE_DDR_SS_CFG, + SHIKRA_SLAVE_DISPLAY_CFG, + SHIKRA_SLAVE_EMAC0_CFG, + SHIKRA_SLAVE_EMAC1_CFG, + SHIKRA_SLAVE_GPU_CFG, + SHIKRA_SLAVE_GPU_THROTTLE_CFG, + SHIKRA_SLAVE_HWKM, + SHIKRA_SLAVE_IMEM_CFG, + SHIKRA_SLAVE_MAPSS, + SHIKRA_SLAVE_MDSP_MPU_CFG, + SHIKRA_SLAVE_MESSAGE_RAM, + SHIKRA_SLAVE_MSS, + SHIKRA_SLAVE_PCIE_CFG, + SHIKRA_SLAVE_PDM, + SHIKRA_SLAVE_PIMEM_CFG, + SHIKRA_SLAVE_PKA_WRAPPER_CFG, + SHIKRA_SLAVE_PMIC_ARB, + SHIKRA_SLAVE_QDSS_CFG, + SHIKRA_SLAVE_QM_CFG, + SHIKRA_SLAVE_QM_MPU_CFG, + SHIKRA_SLAVE_QPIC, + SHIKRA_SLAVE_QUP_0, + SHIKRA_SLAVE_RPM, + SHIKRA_SLAVE_SDCC_1, + SHIKRA_SLAVE_SDCC_2, + SHIKRA_SLAVE_SECURITY, + SHIKRA_SLAVE_SNOC_CFG, + SHIKRA_SNOC_SF_THROTTLE_CFG, + SHIKRA_SLAVE_TLMM, + SHIKRA_SLAVE_TSCSS, + SHIKRA_SLAVE_USB2, + SHIKRA_SLAVE_USB3, + SHIKRA_SLAVE_VENUS_CFG, + SHIKRA_SLAVE_VENUS_THROTTLE_CFG, + SHIKRA_SLAVE_VSENSE_CTRL_CFG, + SHIKRA_SLAVE_SERVICE_CNOC, +}; + +static struct qcom_icc_node xm_dap = { + .id = SHIKRA_MASTER_QDSS_DAP, + .name = "xm_dap", + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_dap_links), + .links = xm_dap_links, +}; + +static const u16 llcc_mc_links[] = { + SHIKRA_SLAVE_EBI_CH0, +}; + +static struct qcom_icc_node llcc_mc = { + .id = SHIKRA_MASTER_LLCC, + .name = "llcc_mc", + .buswidth = 4, + .channels = 2, + .mas_rpm_id = 190, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(llcc_mc_links), + .links = llcc_mc_links, +}; + +static const u16 qnm_gpu_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, + SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, +}; + +static struct qcom_icc_node qnm_gpu = { + .id = SHIKRA_MASTER_GRAPHICS_3D, + .name = "qnm_gpu", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 6, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_gpu_links), + .links = qnm_gpu_links, +}; + +static const u16 qnm_mnoc_hf_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, + SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .id = SHIKRA_MASTER_MNOC_HF_MEM_NOC, + .name = "qnm_mnoc_hf", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 7, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_mnoc_hf_links), + .links = qnm_mnoc_hf_links, +}; + +static const u16 qnm_pcie_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, +}; + +static struct qcom_icc_node qnm_pcie = { + .id = SHIKRA_MASTER_ANOC_PCIE_MEM_NOC, + .name = "qnm_pcie", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 4, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = 185, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_pcie_links), + .links = qnm_pcie_links, +}; + +static const u16 qnm_snoc_sf_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, + SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .id = SHIKRA_MASTER_SNOC_SF_MEM_NOC, + .name = "qnm_snoc_sf", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 3, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = 76, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_snoc_sf_links), + .links = qnm_snoc_sf_links, +}; + +static const u16 xm_apps_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, + SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, +}; + +static struct qcom_icc_node xm_apps = { + .id = SHIKRA_MASTER_AMPSS_M0, + .name = "xm_apps", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 5, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_apps_links), + .links = xm_apps_links, +}; + +static const u16 xm_tcu_links[] = { + SHIKRA_SLAVE_LLCC, + SHIKRA_SLAVE_MEMNOC_SNOC, +}; + +static struct qcom_icc_node xm_tcu = { + .id = SHIKRA_MASTER_SYS_TCU, + .name = "xm_tcu", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 6, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_tcu_links), + .links = xm_tcu_links, +}; + +static const u16 qnm_camera_nrt_links[] = { + SHIKRA_SLAVE_MMNRT_VIRT, +}; + +static struct qcom_icc_node qnm_camera_nrt = { + .id = SHIKRA_MASTER_CAMNOC_SF, + .name = "qnm_camera_nrt", + .buswidth = 32, + .qos.ap_owned = true, + .qos.qos_port = 3, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_camera_nrt_links), + .links = qnm_camera_nrt_links, +}; + +static const u16 qxm_venus0_links[] = { + SHIKRA_SLAVE_MMNRT_VIRT, +}; + +static struct qcom_icc_node qxm_venus0 = { + .id = SHIKRA_MASTER_VIDEO_P0, + .name = "qxm_venus0", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 8, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qxm_venus0_links), + .links = qxm_venus0_links, +}; + +static const u16 qxm_venus_cpu_links[] = { + SHIKRA_SLAVE_MMNRT_VIRT, +}; + +static struct qcom_icc_node qxm_venus_cpu = { + .id = SHIKRA_MASTER_VIDEO_PROC, + .name = "qxm_venus_cpu", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 12, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qxm_venus_cpu_links), + .links = qxm_venus_cpu_links, +}; + +static const u16 qnm_camera_rt_links[] = { + SHIKRA_SLAVE_MM_MEMNOC, +}; + +static struct qcom_icc_node qnm_camera_rt = { + .id = SHIKRA_MASTER_CAMNOC_HF, + .name = "qnm_camera_rt", + .buswidth = 32, + .qos.ap_owned = true, + .qos.qos_port = 9, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_camera_rt_links), + .links = qnm_camera_rt_links, +}; + +static const u16 qxm_mdp0_links[] = { + SHIKRA_SLAVE_MM_MEMNOC, +}; + +static struct qcom_icc_node qxm_mdp0 = { + .id = SHIKRA_MASTER_MDP_PORT0, + .name = "qxm_mdp0", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 4, + .qos.urg_fwd_en = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qxm_mdp0_links), + .links = qxm_mdp0_links, +}; + +static const u16 mmrt_virt_master_links[] = { + SHIKRA_SLAVE_MM_MEMNOC, +}; + +static struct qcom_icc_node mmrt_virt_master = { + .id = SHIKRA_MASTER_MMRT_VIRT, + .name = "mmrt_virt_master", + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mmrt_virt_master_links), + .links = mmrt_virt_master_links, +}; + +static const u16 qhm_snoc_cfg_links[] = { + SHIKRA_SLAVE_SERVICE_SNOC, +}; + +static struct qcom_icc_node qhm_snoc_cfg = { + .id = SHIKRA_MASTER_SNOC_CFG, + .name = "qhm_snoc_cfg", + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qhm_snoc_cfg_links), + .links = qhm_snoc_cfg_links, +}; + +static const u16 qhm_tic_links[] = { + SHIKRA_SLAVE_APPSS, + SHIKRA_SLAVE_MCUSS, + SHIKRA_SLAVE_WCSS, + SHIKRA_SLAVE_MEMNOC_SF, + SHIKRA_SNOC_CNOC_SLV, + SHIKRA_SLAVE_BOOTIMEM, + SHIKRA_SLAVE_OCIMEM, + SHIKRA_SLAVE_PIMEM, + SHIKRA_SLAVE_QDSS_STM, + SHIKRA_SLAVE_TCU, +}; + +static struct qcom_icc_node qhm_tic = { + .id = SHIKRA_MASTER_TIC, + .name = "qhm_tic", + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qhm_tic_links), + .links = qhm_tic_links, +}; + +static const u16 qnm_anoc_snoc_links[] = { + SHIKRA_SLAVE_MEMNOC_SF, +}; + +static struct qcom_icc_node qnm_anoc_snoc = { + .id = SHIKRA_MASTER_ANOC_SNOC, + .name = "qnm_anoc_snoc", + .buswidth = 16, + .mas_rpm_id = 110, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_anoc_snoc_links), + .links = qnm_anoc_snoc_links, +}; + +static const u16 qnm_memnoc_pcie_links[] = { + SHIKRA_SLAVE_PCIE2_0, +}; + +static struct qcom_icc_node qnm_memnoc_pcie = { + .id = SHIKRA_MASTER_MEMNOC_PCIE, + .name = "qnm_memnoc_pcie", + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_memnoc_pcie_links), + .links = qnm_memnoc_pcie_links, +}; + +static const u16 qnm_memnoc_snoc_links[] = { + SHIKRA_SLAVE_APPSS, + SHIKRA_SLAVE_MCUSS, + SHIKRA_SLAVE_WCSS, + SHIKRA_SNOC_CNOC_SLV, + SHIKRA_SLAVE_BOOTIMEM, + SHIKRA_SLAVE_OCIMEM, + SHIKRA_SLAVE_PIMEM, + SHIKRA_SLAVE_QDSS_STM, + SHIKRA_SLAVE_TCU, +}; + +static struct qcom_icc_node qnm_memnoc_snoc = { + .id = SHIKRA_MASTER_MEMNOC_SNOC, + .name = "qnm_memnoc_snoc", + .buswidth = 8, + .mas_rpm_id = 184, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_memnoc_snoc_links), + .links = qnm_memnoc_snoc_links, +}; + +static const u16 qxm_pimem_links[] = { + SHIKRA_SLAVE_MEMNOC_SF, + SHIKRA_SLAVE_OCIMEM, +}; + +static struct qcom_icc_node qxm_pimem = { + .id = SHIKRA_MASTER_PIMEM, + .name = "qxm_pimem", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 14, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qxm_pimem_links), + .links = qxm_pimem_links, +}; + +static const u16 xm_pcie2_0_links[] = { + SHIKRA_SLAVE_PCIE_MEMNOC, +}; + +static struct qcom_icc_node xm_pcie2_0 = { + .id = SHIKRA_MASTER_PCIE2_0, + .name = "xm_pcie2_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 21, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 186, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_pcie2_0_links), + .links = xm_pcie2_0_links, +}; + +static const u16 qhm_qdss_bam_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .id = SHIKRA_MASTER_QDSS_BAM, + .name = "qhm_qdss_bam", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qhm_qdss_bam_links), + .links = qhm_qdss_bam_links, +}; + +static const u16 qhm_qpic_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node qhm_qpic = { + .id = SHIKRA_MASTER_QPIC, + .name = "qhm_qpic", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 1, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qhm_qpic_links), + .links = qhm_qpic_links, +}; + +static const u16 qhm_qup0_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node qhm_qup0 = { + .id = SHIKRA_MASTER_QUP_0, + .name = "qhm_qup0", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 0, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 166, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qhm_qup0_links), + .links = qhm_qup0_links, +}; + +static const u16 qnm_cnoc_snoc_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node qnm_cnoc_snoc = { + .id = SHIKRA_CNOC_SNOC_MAS, + .name = "qnm_cnoc_snoc", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 7, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qnm_cnoc_snoc_links), + .links = qnm_cnoc_snoc_links, +}; + +static const u16 qxm_audio_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node qxm_audio = { + .id = SHIKRA_MASTER_AUDIO, + .name = "qxm_audio", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 22, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .mas_rpm_id = 78, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qxm_audio_links), + .links = qxm_audio_links, +}; + +static const u16 xm_emac_0_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_emac_0 = { + .id = SHIKRA_MASTER_EMAC_0, + .name = "xm_emac_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 19, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_emac_0_links), + .links = xm_emac_0_links, +}; + +static const u16 xm_emac_1_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_emac_1 = { + .id = SHIKRA_MASTER_EMAC_1, + .name = "xm_emac_1", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 20, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_emac_1_links), + .links = xm_emac_1_links, +}; + +static const u16 xm_qdss_etr_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_qdss_etr = { + .id = SHIKRA_MASTER_QDSS_ETR, + .name = "xm_qdss_etr", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 11, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_qdss_etr_links), + .links = xm_qdss_etr_links, +}; + +static const u16 xm_sdc1_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_sdc1 = { + .id = SHIKRA_MASTER_SDCC_1, + .name = "xm_sdc1", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 13, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_sdc1_links), + .links = xm_sdc1_links, +}; + +static const u16 xm_sdc2_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_sdc2 = { + .id = SHIKRA_MASTER_SDCC_2, + .name = "xm_sdc2", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 17, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_sdc2_links), + .links = xm_sdc2_links, +}; + +static const u16 xm_usb2_0_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_usb2_0 = { + .id = SHIKRA_MASTER_USB2_0, + .name = "xm_usb2_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 24, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_usb2_0_links), + .links = xm_usb2_0_links, +}; + +static const u16 xm_usb3_0_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .id = SHIKRA_MASTER_USB3, + .name = "xm_usb3_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 18, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(xm_usb3_0_links), + .links = xm_usb3_0_links, +}; + +static const u16 crypto_c0_links[] = { + SHIKRA_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node crypto_c0 = { + .id = SHIKRA_MASTER_CRYPTO_CORE0, + .name = "crypto_c0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 16, + .qos.urg_fwd_en = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(crypto_c0_links), + .links = crypto_c0_links, +}; + +/* Slave nodes */ +static struct qcom_icc_node qup0_core_slave = { + .id = SHIKRA_SLAVE_QUP_CORE_0, + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 264, +}; + +static struct qcom_icc_node qhs_ahb2phy_usb = { + .id = SHIKRA_SLAVE_AHB2PHY_USB, + .name = "qhs_ahb2phy_usb", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_apss_throttle_cfg = { + .id = SHIKRA_SLAVE_APSS_THROTTLE_CFG, + .name = "qhs_apss_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_audio = { + .id = SHIKRA_SLAVE_AUDIO, + .name = "qhs_audio", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .id = SHIKRA_SLAVE_BOOT_ROM, + .name = "qhs_boot_rom", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_camera_nrt_throttle_cfg = { + .id = SHIKRA_SLAVE_CAMERA_NRT_THROTTLE_CFG, + .name = "qhs_camera_nrt_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_camera_ss_cfg = { + .id = SHIKRA_SLAVE_CAMERA_CFG, + .name = "qhs_camera_ss_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_cdsp_throttle_cfg = { + .id = SHIKRA_SLAVE_CDSP_THROTTLE_CFG, + .name = "qhs_cdsp_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .id = SHIKRA_SLAVE_CLK_CTL, + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_compute_dsp_cfg = { + .id = SHIKRA_SLAVE_DSP_CFG, + .name = "qhs_compute_dsp_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .id = SHIKRA_SLAVE_RBCPR_CX_CFG, + .name = "qhs_cpr_cx", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_cpr_mx = { + .id = SHIKRA_SLAVE_RBCPR_MX_CFG, + .name = "qhs_cpr_mx", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .id = SHIKRA_SLAVE_CRYPTO_0_CFG, + .name = "qhs_crypto0_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_ddr_ss_cfg = { + .id = SHIKRA_SLAVE_DDR_SS_CFG, + .name = "qhs_ddr_ss_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_disp_ss_cfg = { + .id = SHIKRA_SLAVE_DISPLAY_CFG, + .name = "qhs_disp_ss_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_emac0_cfg = { + .id = SHIKRA_SLAVE_EMAC0_CFG, + .name = "qhs_emac0_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_emac1_cfg = { + .id = SHIKRA_SLAVE_EMAC1_CFG, + .name = "qhs_emac1_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_gpu_cfg = { + .id = SHIKRA_SLAVE_GPU_CFG, + .name = "qhs_gpu_cfg", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_gpu_throttle_cfg = { + .id = SHIKRA_SLAVE_GPU_THROTTLE_CFG, + .name = "qhs_gpu_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_hwkm = { + .id = SHIKRA_SLAVE_HWKM, + .name = "qhs_hwkm", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .id = SHIKRA_SLAVE_IMEM_CFG, + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mapss = { + .id = SHIKRA_SLAVE_MAPSS, + .name = "qhs_mapss", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mdsp_mpu_cfg = { + .id = SHIKRA_SLAVE_MDSP_MPU_CFG, + .name = "qhs_mdsp_mpu_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mesg_ram = { + .id = SHIKRA_SLAVE_MESSAGE_RAM, + .name = "qhs_mesg_ram", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mss = { + .id = SHIKRA_SLAVE_MSS, + .name = "qhs_mss", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pcie_cfg = { + .id = SHIKRA_SLAVE_PCIE_CFG, + .name = "qhs_pcie_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pdm = { + .id = SHIKRA_SLAVE_PDM, + .name = "qhs_pdm", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .id = SHIKRA_SLAVE_PIMEM_CFG, + .name = "qhs_pimem_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pka_wrapper = { + .id = SHIKRA_SLAVE_PKA_WRAPPER_CFG, + .name = "qhs_pka_wrapper", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pmic_arb = { + .id = SHIKRA_SLAVE_PMIC_ARB, + .name = "qhs_pmic_arb", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .id = SHIKRA_SLAVE_QDSS_CFG, + .name = "qhs_qdss_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qm_cfg = { + .id = SHIKRA_SLAVE_QM_CFG, + .name = "qhs_qm_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qm_mpu_cfg = { + .id = SHIKRA_SLAVE_QM_MPU_CFG, + .name = "qhs_qm_mpu_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qpic = { + .id = SHIKRA_SLAVE_QPIC, + .name = "qhs_qpic", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qup0 = { + .id = SHIKRA_SLAVE_QUP_0, + .name = "qhs_qup0", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_rpm = { + .id = SHIKRA_SLAVE_RPM, + .name = "qhs_rpm", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .id = SHIKRA_SLAVE_SDCC_1, + .name = "qhs_sdc1", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .id = SHIKRA_SLAVE_SDCC_2, + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_security = { + .id = SHIKRA_SLAVE_SECURITY, + .name = "qhs_security", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 qhs_snoc_cfg_links[] = { + SHIKRA_MASTER_SNOC_CFG, +}; + +static struct qcom_icc_node qhs_snoc_cfg = { + .id = SHIKRA_SLAVE_SNOC_CFG, + .name = "qhs_snoc_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = 1, + .links = qhs_snoc_cfg_links, +}; + +static struct qcom_icc_node qhs_snoc_sf_throttle_cfg = { + .id = SHIKRA_SNOC_SF_THROTTLE_CFG, + .name = "qhs_snoc_sf_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_tlmm = { + .id = SHIKRA_SLAVE_TLMM, + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_tscss = { + .id = SHIKRA_SLAVE_TSCSS, + .name = "qhs_tscss", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_usb2 = { + .id = SHIKRA_SLAVE_USB2, + .name = "qhs_usb2", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_usb3 = { + .id = SHIKRA_SLAVE_USB3, + .name = "qhs_usb3", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .id = SHIKRA_SLAVE_VENUS_CFG, + .name = "qhs_venus_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_venus_throttle_cfg = { + .id = SHIKRA_SLAVE_VENUS_THROTTLE_CFG, + .name = "qhs_venus_throttle_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .id = SHIKRA_SLAVE_VSENSE_CTRL_CFG, + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node srvc_cnoc = { + .id = SHIKRA_SLAVE_SERVICE_CNOC, + .name = "srvc_cnoc", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node ebi = { + .id = SHIKRA_SLAVE_EBI_CH0, + .name = "ebi", + .channels = 2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 qns_llcc_links[] = { + SHIKRA_MASTER_LLCC, +}; + +static struct qcom_icc_node qns_llcc = { + .id = SHIKRA_SLAVE_LLCC, + .name = "qns_llcc", + .channels = 2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 312, + .num_links = 1, + .links = qns_llcc_links, +}; + +static const u16 qns_memnoc_snoc_links[] = { + SHIKRA_MASTER_MEMNOC_SNOC, +}; + +static struct qcom_icc_node qns_memnoc_snoc = { + .id = SHIKRA_SLAVE_MEMNOC_SNOC, + .name = "qns_memnoc_snoc", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 314, + .num_links = 1, + .links = qns_memnoc_snoc_links, +}; + +static const u16 qns_pcie_links[] = { + SHIKRA_MASTER_MEMNOC_PCIE, +}; + +static struct qcom_icc_node qns_pcie = { + .id = SHIKRA_SLAVE_MEM_NOC_PCIE_SNOC, + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = 1, + .links = qns_pcie_links, +}; + +static const u16 mmnrt_virt_slave_links[] = { + SHIKRA_MASTER_MMRT_VIRT, +}; + +static struct qcom_icc_node mmnrt_virt_slave = { + .id = SHIKRA_SLAVE_MMNRT_VIRT, + .name = "mmnrt_virt_slave", + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = 1, + .links = mmnrt_virt_slave_links, +}; + +static const u16 qns_mm_memnoc_links[] = { + SHIKRA_MASTER_MNOC_HF_MEM_NOC, +}; + +static struct qcom_icc_node qns_mm_memnoc = { + .id = SHIKRA_SLAVE_MM_MEMNOC, + .name = "qns_mm_memnoc", + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = 1, + .links = qns_mm_memnoc_links, +}; + +static struct qcom_icc_node qhs_apss = { + .id = SHIKRA_SLAVE_APPSS, + .name = "qhs_apss", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mcuss = { + .id = SHIKRA_SLAVE_MCUSS, + .name = "qhs_mcuss", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 319, +}; + +static struct qcom_icc_node qhs_wcss = { + .id = SHIKRA_SLAVE_WCSS, + .name = "qhs_wcss", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 23, +}; + +static const u16 qns_memnoc_sf_links[] = { + SHIKRA_MASTER_SNOC_SF_MEM_NOC, +}; + +static struct qcom_icc_node qns_memnoc_sf = { + .id = SHIKRA_SLAVE_MEMNOC_SF, + .name = "qns_memnoc_sf", + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 313, + .num_links = 1, + .links = qns_memnoc_sf_links, +}; + +static const u16 qns_snoc_cnoc_links[] = { + SHIKRA_SNOC_CNOC_MAS, +}; + +static struct qcom_icc_node qns_snoc_cnoc = { + .id = SHIKRA_SNOC_CNOC_SLV, + .name = "qns_snoc_cnoc", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 25, + .num_links = 1, + .links = qns_snoc_cnoc_links, +}; + +static struct qcom_icc_node qxs_bootimem = { + .id = SHIKRA_SLAVE_BOOTIMEM, + .name = "qxs_bootimem", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qxs_imem = { + .id = SHIKRA_SLAVE_OCIMEM, + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node qxs_pimem = { + .id = SHIKRA_SLAVE_PIMEM, + .name = "qxs_pimem", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node srvc_snoc = { + .id = SHIKRA_SLAVE_SERVICE_SNOC, + .name = "srvc_snoc", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node xs_pcie2_0 = { + .id = SHIKRA_SLAVE_PCIE2_0, + .name = "xs_pcie2_0", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .id = SHIKRA_SLAVE_QDSS_STM, + .name = "xs_qdss_stm", + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .id = SHIKRA_SLAVE_TCU, + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 qns_pcie_memnoc_links[] = { + SHIKRA_MASTER_ANOC_PCIE_MEM_NOC, +}; + +static struct qcom_icc_node qns_pcie_memnoc = { + .id = SHIKRA_SLAVE_PCIE_MEMNOC, + .name = "qns_pcie_memnoc", + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 317, + .num_links = 1, + .links = qns_pcie_memnoc_links, +}; + +static const u16 qns_anoc_snoc_links[] = { + SHIKRA_MASTER_ANOC_SNOC, +}; + +static struct qcom_icc_node qns_anoc_snoc = { + .id = SHIKRA_SLAVE_ANOC_SNOC, + .name = "qns_anoc_snoc", + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 141, + .num_links = 1, + .links = qns_anoc_snoc_links, +}; + +/* NoC descriptors */ +static struct qcom_icc_node * const shikra_clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, +}; + +static const struct qcom_icc_desc shikra_clk_virt = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_clk_virt_nodes, + .num_nodes = ARRAY_SIZE(shikra_clk_virt_nodes), + .bus_clk_desc = &qup_clk, + .keep_alive = true, +}; + +static struct qcom_icc_node * const shikra_config_noc_nodes[] = { + [SNOC_CNOC_MAS] = &qnm_snoc_cnoc, + [MASTER_QDSS_DAP] = &xm_dap, + [SLAVE_AHB2PHY_USB] = &qhs_ahb2phy_usb, + [SLAVE_APSS_THROTTLE_CFG] = &qhs_apss_throttle_cfg, + [SLAVE_AUDIO] = &qhs_audio, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_NRT_THROTTLE_CFG] = &qhs_camera_nrt_throttle_cfg, + [SLAVE_CAMERA_CFG] = &qhs_camera_ss_cfg, + [SLAVE_CDSP_THROTTLE_CFG] = &qhs_cdsp_throttle_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_DSP_CFG] = &qhs_compute_dsp_cfg, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MX_CFG] = &qhs_cpr_mx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_DDR_SS_CFG] = &qhs_ddr_ss_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_disp_ss_cfg, + [SLAVE_EMAC0_CFG] = &qhs_emac0_cfg, + [SLAVE_EMAC1_CFG] = &qhs_emac1_cfg, + [SLAVE_GPU_CFG] = &qhs_gpu_cfg, + [SLAVE_GPU_THROTTLE_CFG] = &qhs_gpu_throttle_cfg, + [SLAVE_HWKM] = &qhs_hwkm, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_MAPSS] = &qhs_mapss, + [SLAVE_MDSP_MPU_CFG] = &qhs_mdsp_mpu_cfg, + [SLAVE_MESSAGE_RAM] = &qhs_mesg_ram, + [SLAVE_MSS] = &qhs_mss, + [SLAVE_PCIE_CFG] = &qhs_pcie_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PKA_WRAPPER_CFG] = &qhs_pka_wrapper, + [SLAVE_PMIC_ARB] = &qhs_pmic_arb, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QM_CFG] = &qhs_qm_cfg, + [SLAVE_QM_MPU_CFG] = &qhs_qm_mpu_cfg, + [SLAVE_QPIC] = &qhs_qpic, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_RPM] = &qhs_rpm, + [SLAVE_SDCC_1] = &qhs_sdc1, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SECURITY] = &qhs_security, + [SLAVE_SNOC_CFG] = &qhs_snoc_cfg, + [SNOC_SF_THROTTLE_CFG] = &qhs_snoc_sf_throttle_cfg, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_TSCSS] = &qhs_tscss, + [SLAVE_USB2] = &qhs_usb2, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &qhs_venus_throttle_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, +}; + +static const struct regmap_config shikra_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8080, + .fast_io = true, +}; + +static const struct qcom_icc_desc shikra_config_noc = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_config_noc_nodes, + .num_nodes = ARRAY_SIZE(shikra_config_noc_nodes), + .bus_clk_desc = &bus_1_clk, + .regmap_cfg = &shikra_config_noc_regmap_config, + .keep_alive = true, +}; + +static struct qcom_icc_node * const shikra_mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI_CH0] = &ebi, +}; + +static const struct qcom_icc_desc shikra_mc_virt = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_mc_virt_nodes, + .num_nodes = ARRAY_SIZE(shikra_mc_virt_nodes), + .bus_clk_desc = &bimc_clk, + .keep_alive = true, + .ab_coeff = 152, +}; + +static struct qcom_icc_node * const shikra_mem_noc_core_nodes[] = { + [MASTER_GRAPHICS_3D] = &qnm_gpu, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_ANOC_PCIE_MEM_NOC] = &qnm_pcie, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_AMPSS_M0] = &xm_apps, + [MASTER_SYS_TCU] = &xm_tcu, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEMNOC_SNOC] = &qns_memnoc_snoc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config shikra_mem_noc_core_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x43080, + .fast_io = true, +}; + +static const struct qcom_icc_desc shikra_mem_noc_core = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_mem_noc_core_nodes, + .num_nodes = ARRAY_SIZE(shikra_mem_noc_core_nodes), + .bus_clk_desc = &mem_1_clk, + .regmap_cfg = &shikra_mem_noc_core_regmap_config, + .intf_clocks = memnoc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(memnoc_intf_clocks), + .qos_offset = 0x28000, + .keep_alive = true, + .ab_coeff = 142, +}; + +static struct qcom_icc_node * const shikra_mmnrt_virt_nodes[] = { + [MASTER_CAMNOC_SF] = &qnm_camera_nrt, + [MASTER_VIDEO_P0] = &qxm_venus0, + [MASTER_VIDEO_PROC] = &qxm_venus_cpu, + [SLAVE_MMNRT_VIRT] = &mmnrt_virt_slave, +}; + +static const struct regmap_config shikra_sys_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6a080, + .fast_io = true, +}; + +static const struct qcom_icc_desc shikra_mmnrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_mmnrt_virt_nodes, + .num_nodes = ARRAY_SIZE(shikra_mmnrt_virt_nodes), + .bus_clk_desc = &mmaxi_0_clk, + .regmap_cfg = &shikra_sys_noc_regmap_config, + .qos_offset = 0x51000, + .keep_alive = true, + .ab_coeff = 142, +}; + +static struct qcom_icc_node * const shikra_mmrt_virt_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camera_rt, + [MASTER_MDP_PORT0] = &qxm_mdp0, + [MASTER_MMRT_VIRT] = &mmrt_virt_master, + [SLAVE_MM_MEMNOC] = &qns_mm_memnoc, +}; + +static const struct qcom_icc_desc shikra_mmrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_mmrt_virt_nodes, + .num_nodes = ARRAY_SIZE(shikra_mmrt_virt_nodes), + .bus_clk_desc = &mmaxi_1_clk, + .regmap_cfg = &shikra_sys_noc_regmap_config, + .qos_offset = 0x51000, + .keep_alive = true, + .ab_coeff = 142, +}; + +static struct qcom_icc_node * const shikra_sys_noc_nodes[] = { + [MASTER_SNOC_CFG] = &qhm_snoc_cfg, + [MASTER_TIC] = &qhm_tic, + [MASTER_ANOC_SNOC] = &qnm_anoc_snoc, + [MASTER_MEMNOC_PCIE] = &qnm_memnoc_pcie, + [MASTER_MEMNOC_SNOC] = &qnm_memnoc_snoc, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_PCIE2_0] = &xm_pcie2_0, + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QPIC] = &qhm_qpic, + [MASTER_QUP_0] = &qhm_qup0, + [CNOC_SNOC_MAS] = &qnm_cnoc_snoc, + [MASTER_AUDIO] = &qxm_audio, + [MASTER_EMAC_0] = &xm_emac_0, + [MASTER_EMAC_1] = &xm_emac_1, + [MASTER_QDSS_ETR] = &xm_qdss_etr, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_USB2_0] = &xm_usb2_0, + [MASTER_USB3] = &xm_usb3_0, + [MASTER_CRYPTO_CORE0] = &crypto_c0, + [SLAVE_APPSS] = &qhs_apss, + [SLAVE_MCUSS] = &qhs_mcuss, + [SLAVE_WCSS] = &qhs_wcss, + [SLAVE_MEMNOC_SF] = &qns_memnoc_sf, + [SNOC_CNOC_SLV] = &qns_snoc_cnoc, + [SLAVE_BOOTIMEM] = &qxs_bootimem, + [SLAVE_OCIMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, + [SLAVE_PCIE2_0] = &xs_pcie2_0, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, + [SLAVE_PCIE_MEMNOC] = &qns_pcie_memnoc, + [SLAVE_ANOC_SNOC] = &qns_anoc_snoc, +}; + +static const struct qcom_icc_desc shikra_sys_noc = { + .type = QCOM_ICC_QNOC, + .nodes = shikra_sys_noc_nodes, + .num_nodes = ARRAY_SIZE(shikra_sys_noc_nodes), + .bus_clk_desc = &bus_2_clk, + .regmap_cfg = &shikra_sys_noc_regmap_config, + .intf_clocks = sys_noc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(sys_noc_intf_clocks), + .qos_offset = 0x51000, + .keep_alive = true, +}; + +static const struct of_device_id shikra_qnoc_of_match[] = { + { .compatible = "qcom,shikra-clk-virt", .data = &shikra_clk_virt }, + { .compatible = "qcom,shikra-config-noc", .data = &shikra_config_noc }, + { .compatible = "qcom,shikra-mc-virt", .data = &shikra_mc_virt }, + { .compatible = "qcom,shikra-mem-noc-core", .data = &shikra_mem_noc_core }, + { .compatible = "qcom,shikra-mmnrt-virt", .data = &shikra_mmnrt_virt }, + { .compatible = "qcom,shikra-mmrt-virt", .data = &shikra_mmrt_virt }, + { .compatible = "qcom,shikra-sys-noc", .data = &shikra_sys_noc }, + { }, +}; +MODULE_DEVICE_TABLE(of, shikra_qnoc_of_match); + +static struct platform_driver shikra_qnoc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-shikra", + .of_match_table = shikra_qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&shikra_qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&shikra_qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Shikra NoC driver"); +MODULE_LICENSE("GPL"); From 8d315860b5cce17559151d04b1d85022f0bb15a2 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Sun, 10 May 2026 10:06:06 +0800 Subject: [PATCH 0641/5214] dt-bindings: interconnect: Document RPMh Network-On-Chip for Qualcomm Nord SoC Add RPMh Network-On-Chip interconnect bindings for Qualcomm Nord SoC. Signed-off-by: Odelu Kukatla Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260510020607.1129773-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,nord-rpmh.yaml | 131 +++++++++++ .../dt-bindings/interconnect/qcom,nord-rpmh.h | 217 ++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,nord-rpmh.yaml create mode 100644 include/dt-bindings/interconnect/qcom,nord-rpmh.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,nord-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,nord-rpmh.yaml new file mode 100644 index 000000000000..3650d3d5b918 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,nord-rpmh.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,nord-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Nord + +maintainers: + - Odelu Kukatla + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,nord-rpmh.h + +properties: + compatible: + enum: + - qcom,nord-aggre1-noc + - qcom,nord-aggre1-noc-tile + - qcom,nord-aggre2-noc + - qcom,nord-aggre2-noc-tile + - qcom,nord-clk-virt + - qcom,nord-cnoc-cfg + - qcom,nord-cnoc-main + - qcom,nord-hpass-ag-noc + - qcom,nord-hscnoc + - qcom,nord-mc-virt + - qcom,nord-mmss-noc + - qcom,nord-nsp-data-noc-0 + - qcom,nord-nsp-data-noc-1 + - qcom,nord-nsp-data-noc-2 + - qcom,nord-nsp-data-noc-3 + - qcom,nord-pcie-cfg + - qcom,nord-pcie-data-inbound + - qcom,nord-pcie-data-outbound + - qcom,nord-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 4 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,nord-clk-virt + - qcom,nord-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,nord-aggre1-noc-tile + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB2 AXI clock + - description: aggre USB3 PRIM AXI clock + - description: aggre USB3 SEC AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,nord-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,nord-aggre1-noc-tile + - qcom,nord-aggre2-noc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + clk_virt: interconnect-clk-virt { + compatible = "qcom,nord-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre1_noc_tile: interconnect@1720000 { + compatible = "qcom,nord-aggre1-noc-tile"; + reg = <0x01720000 0x23400>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&ne_gcc_aggre_noc_ufs_phy_axi_clk>, + <&ne_gcc_aggre_noc_usb2_axi_clk>, + <&ne_gcc_aggre_noc_usb3_prim_axi_clk>, + <&ne_gcc_aggre_noc_usb3_sec_axi_clk>; + }; diff --git a/include/dt-bindings/interconnect/qcom,nord-rpmh.h b/include/dt-bindings/interconnect/qcom,nord-rpmh.h new file mode 100644 index 000000000000..5bdce6a9bab7 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,nord-rpmh.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_NORD_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_NORD_H + +#define MASTER_QSPI_0 0 +#define MASTER_SAILSS_MD1 1 +#define MASTER_QUP_3 2 +#define SLAVE_A1NOC_SNOC 3 + +#define MASTER_QUP_2 0 +#define MASTER_CRYPTO_CORE0 1 +#define MASTER_CRYPTO_CORE1 2 +#define MASTER_CRYPTO_CORE2 3 +#define MASTER_SDCC_4 4 +#define MASTER_UFS_MEM 5 +#define MASTER_USB2 6 +#define MASTER_USB3_0 7 +#define MASTER_USB3_1 8 +#define SLAVE_A1NOC_HSCNOC 9 + +#define MASTER_IPA 0 +#define MASTER_SOCCP_AGGR_NOC 1 +#define MASTER_QDSS_ETR 2 +#define MASTER_QDSS_ETR_1 3 +#define SLAVE_A2NOC_SNOC 4 + +#define MASTER_QUP_0 0 +#define MASTER_QUP_1 1 +#define MASTER_EMAC_0 2 +#define MASTER_EMAC_1 3 +#define SLAVE_A2NOC_HSCNOC 4 + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define MASTER_QUP_CORE_2 2 +#define MASTER_QUP_CORE_3 3 +#define SLAVE_QUP_CORE_0 4 +#define SLAVE_QUP_CORE_1 5 +#define SLAVE_QUP_CORE_2 6 +#define SLAVE_QUP_CORE_3 7 + +#define MASTER_CNOC_CFG 0 +#define SLAVE_PS_ETH_0 1 +#define SLAVE_PS_ETH_1 2 +#define SLAVE_SHS_SERVER 3 +#define SLAVE_AHB2PHY_0 4 +#define SLAVE_AHB2PHY_1 5 +#define SLAVE_AHB2PHY_2 6 +#define SLAVE_AHB2PHY_3 7 +#define SLAVE_AHB2PHY_ETH_0 8 +#define SLAVE_AHB2PHY_ETH_1 9 +#define SLAVE_CAMERA_CFG 10 +#define SLAVE_CLK_CTL 11 +#define SLAVE_CRYPTO_0_CFG 12 +#define SLAVE_CRYPTO_1_CFG 13 +#define SLAVE_CRYPTO_2_CFG 14 +#define SLAVE_DISPLAY_1_CFG 15 +#define SLAVE_DISPLAY_CFG 16 +#define SLAVE_DPRX0 17 +#define SLAVE_DPRX1 18 +#define SLAVE_EVA_CFG 19 +#define SLAVE_GFX3D_CFG 20 +#define SLAVE_GFX3D_1_CFG 21 +#define SLAVE_I2C 22 +#define SLAVE_IMEM_CFG 23 +#define SLAVE_MCW_PCIE 24 +#define SLAVE_MM_RSCC 25 +#define SLAVE_NE_CLK_CTL 26 +#define SLAVE_NSPSS0_CFG 27 +#define SLAVE_NSPSS1_CFG 28 +#define SLAVE_NSPSS2_CFG 29 +#define SLAVE_NSPSS3_CFG 30 +#define SLAVE_NW_CLK_CTL 31 +#define SLAVE_PRNG 32 +#define SLAVE_QDSS_CFG 33 +#define SLAVE_QSPI_0 34 +#define SLAVE_QUP_0 35 +#define SLAVE_QUP_3 36 +#define SLAVE_QUP_1 37 +#define SLAVE_QUP_2 38 +#define SLAVE_SAFEDMA_CFG 39 +#define SLAVE_SDCC_4 40 +#define SLAVE_SE_CLK_CTL 41 +#define SLAVE_TCSR 42 +#define SLAVE_TLMM 43 +#define SLAVE_TSC_CFG 44 +#define SLAVE_UFS_MEM_CFG 45 +#define SLAVE_USB2 46 +#define SLAVE_USB3_0 47 +#define SLAVE_USB3_1 48 +#define SLAVE_VENUS_CFG 49 +#define SLAVE_COMPUTENOC_CFG 50 +#define SLAVE_PCIE_NOC_CFG 51 +#define SLAVE_QTC_CFG 52 +#define SLAVE_QDSS_STM 53 +#define SLAVE_SYS_TCU0_CFG 54 +#define SLAVE_SYS_TCU1_CFG 55 +#define SLAVE_SYS_TCU2_CFG 56 + +#define MASTER_MM_RSCC 0 +#define MASTER_HSCNOC_CNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_HBCU 3 +#define SLAVE_IPA_CFG 4 +#define SLAVE_IPC_ROUTER_CFG 5 +#define SLAVE_SOCCP 6 +#define SLAVE_TME_CFG 7 +#define SLAVE_PCIE_DMA 8 +#define SLAVE_CNOC_CFG 9 +#define SLAVE_DDRSS_CFG 10 +#define SLAVE_IMEM 11 + +#define MASTER_HPASS_PROC_0 0 +#define MASTER_HPASS_PROC_1 1 +#define MASTER_HPASS_PROC_2 2 +#define SLAVE_HPASS_AGNOC_AUDIO 3 + +#define MASTER_GPU_TCU 0 +#define MASTER_QTC_TCU 1 +#define MASTER_SYS_TCU_0 2 +#define MASTER_SYS_TCU_1 3 +#define MASTER_SYS_TCU_2 4 +#define MASTER_APPSS_PROC 5 +#define MASTER_A1NOC_TILE_HSCNOC 6 +#define MASTER_A2NOC_TILE_HSCNOC 7 +#define MASTER_GFX3D 8 +#define MASTER_GFX3D_1 9 +#define MASTER_HPASS_ADAS_HSCNOC 10 +#define MASTER_HPASS_AUDIO_HSCNOC 11 +#define MASTER_MNOC_HF_MEM_NOC 12 +#define MASTER_MNOC_SF_MEM_NOC 13 +#define MASTER_NSP0_HSCNOC 14 +#define MASTER_NSP1_HSCNOC 15 +#define MASTER_NSP2_HSCNOC 16 +#define MASTER_NSP3_HSCNOC 17 +#define MASTER_ANOC_PCIE_GEM_NOC 18 +#define MASTER_SAILSS_MD0_HSCNOC 19 +#define MASTER_SNOC_SF_MEM_NOC 20 +#define MASTER_GIC 21 +#define SLAVE_HSCNOC_CNOC 22 +#define SLAVE_LLCC 23 +#define SLAVE_MEM_NOC_PCIE_SNOC 24 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_NRT_ICP_SF 1 +#define MASTER_CAMNOC_RT_CDM_SF 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_DPRX0 4 +#define MASTER_DPRX1 5 +#define MASTER_MDP0 6 +#define MASTER_MDP1 7 +#define MASTER_VIDEO_CV_PROC 8 +#define MASTER_VIDEO_EVA 9 +#define MASTER_VIDEO_MVP0 10 +#define MASTER_VIDEO_MVP1 11 +#define MASTER_VIDEO_V_PROC 12 +#define SLAVE_MNOC_HF_MEM_NOC 13 +#define SLAVE_MNOC_SF_MEM_NOC 14 + +#define MASTER_NSP0_PROC 0 +#define SLAVE_NSP0_HSC_NOC 1 + +#define MASTER_NSP1_PROC 0 +#define SLAVE_NSP1_HSC_NOC 1 + +#define MASTER_NSP2_PROC 0 +#define SLAVE_NSP2_HSC_NOC 1 + +#define MASTER_NSP3_PROC 0 +#define SLAVE_NSP3_HSC_NOC 1 + +#define MASTER_PCIE_NOC_CFG 0 +#define SLAVE_PCIE_AHB2PHY_CFG 1 +#define SLAVE_PCIE_CFG_0 2 +#define SLAVE_PCIE_CFG_1 3 +#define SLAVE_PCIE_CFG_2 4 +#define SLAVE_PCIE_CFG_3 5 +#define SLAVE_PCIE_DMA_0_CFG 6 +#define SLAVE_PCIE_DMA_1_CFG 7 +#define SLAVE_PCIE_DMA_2_CFG 8 + +#define MASTER_PCIE_DMA_0 0 +#define MASTER_PCIE_DMA_1 1 +#define MASTER_PCIE_DMA_2 2 +#define MASTER_PCIE_0 3 +#define MASTER_PCIE_1 4 +#define MASTER_PCIE_2 5 +#define MASTER_PCIE_3 6 +#define SLAVE_PCIE_HSCNOC 7 +#define SLAVE_PCIE_OBNOC_DMA 8 + +#define MASTER_CNOC_PCIE_DMA 0 +#define MASTER_ANOC_PCIE_HSCNOC 1 +#define MASTER_PCIE_IBNOC_DMA 2 +#define SLAVE_PCIE_DMA_0 3 +#define SLAVE_PCIE_DMA_1 4 +#define SLAVE_PCIE_DMA_2 5 +#define SLAVE_PCIE_0 6 +#define SLAVE_PCIE_1 7 +#define SLAVE_PCIE_2 8 +#define SLAVE_PCIE_3 9 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_A2NOC_SNOC 1 +#define MASTER_CNOC_SNOC 2 +#define MASTER_NSINOC_SNOC 3 +#define MASTER_SAFE_DMA 4 +#define SLAVE_SNOC_HSCNOC_SF 5 + +#endif From 010f4d24cc61fd03c61bbdcce15b7a2033956bc7 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Sun, 10 May 2026 10:06:07 +0800 Subject: [PATCH 0642/5214] interconnect: qcom: Add interconnect provider driver for Nord SoC Add driver for the Qualcomm interconnect buses found on Nord SoC. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pair. Signed-off-by: Odelu Kukatla Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260510020607.1129773-3-shengchao.guo@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 11 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/nord.c | 2682 ++++++++++++++++++++++++++++ 3 files changed, 2695 insertions(+) create mode 100644 drivers/interconnect/qcom/nord.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 786b4eda44b4..32808772c363 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -107,6 +107,17 @@ config INTERCONNECT_QCOM_MSM8996 This is a driver for the Qualcomm Network-on-Chip on msm8996-based platforms. +config INTERCONNECT_QCOM_NORD + tristate "Qualcomm Nord interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on Nord-based + platforms. The topology consists of several NoCs controlled by + the RPMh hardware and communicates via Bus Clock Manager (BCM) + through the Resource State Coordinator (RSC). + config INTERCONNECT_QCOM_OSM_L3 tristate "Qualcomm OSM L3 interconnect driver" depends on INTERCONNECT_QCOM || COMPILE_TEST diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index cdf2c6c9fbf3..988fa8b0f509 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -16,6 +16,7 @@ qnoc-msm8953-objs := msm8953.o qnoc-msm8974-objs := msm8974.o qnoc-msm8976-objs := msm8976.o qnoc-msm8996-objs := msm8996.o +qnoc-nord-objs := nord.o icc-osm-l3-objs := osm-l3.o qnoc-qcm2290-objs := qcm2290.o qnoc-qcs404-objs := qcs404.o @@ -61,6 +62,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8953) += qnoc-msm8953.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8976) += qnoc-msm8976.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o +obj-$(CONFIG_INTERCONNECT_QCOM_NORD) += qnoc-nord.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o diff --git a/drivers/interconnect/qcom/nord.c b/drivers/interconnect/qcom/nord.c new file mode 100644 index 000000000000..b3ca5675e7c8 --- /dev/null +++ b/drivers/interconnect/qcom/nord.c @@ -0,0 +1,2682 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup3_core_slave = { + .name = "qup3_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ps_eth_0 = { + .name = "ps_eth_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ps_eth_1 = { + .name = "ps_eth_1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ps_shs_server = { + .name = "ps_shs_server", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy2 = { + .name = "qhs_ahb2phy2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy3 = { + .name = "qhs_ahb2phy3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy_eth_0 = { + .name = "qhs_ahb2phy_eth_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy_eth_1 = { + .name = "qhs_ahb2phy_eth_1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto1_cfg = { + .name = "qhs_crypto1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto2_cfg = { + .name = "qhs_crypto2_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_1_cfg = { + .name = "qhs_display_1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_dprx0 = { + .name = "qhs_dprx0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_dprx1 = { + .name = "qhs_dprx1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_eva_cfg = { + .name = "qhs_eva_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_0_cfg = { + .name = "qhs_gpuss_0_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_gpuss_1_cfg = { + .name = "qhs_gpuss_1_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mcw_pcie = { + .name = "qhs_mcw_pcie", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mm_rscc = { + .name = "qhs_mm_rscc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ne_clk_ctl = { + .name = "qhs_ne_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_nspss0_cfg = { + .name = "qhs_nspss0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_nspss1_cfg = { + .name = "qhs_nspss1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_nspss2_cfg = { + .name = "qhs_nspss2_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_nspss3_cfg = { + .name = "qhs_nspss3_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_nw_clk_ctl = { + .name = "qhs_nw_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup3 = { + .name = "qhs_qup3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_safedma_cfg = { + .name = "qhs_safedma_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_se_clk_ctl = { + .name = "qhs_se_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tsc_cfg = { + .name = "qhs_tsc_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb2 = { + .name = "qhs_usb2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3_1 = { + .name = "qhs_usb3_1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_computenoc_cfg = { + .name = "qss_computenoc_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qtc_cfg = { + .name = "qss_qtc_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu0_cfg = { + .name = "xs_sys_tcu0_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_sys_tcu1_cfg = { + .name = "xs_sys_tcu1_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_sys_tcu2_cfg = { + .name = "xs_sys_tcu2_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_hbcu = { + .name = "qhs_hbcu", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 16, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_ahb2phy_cfg = { + .name = "qhs_pcie_ahb2phy_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg_0 = { + .name = "qhs_pcie_cfg_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg_1 = { + .name = "qhs_pcie_cfg_1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg_2 = { + .name = "qhs_pcie_cfg_2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg_3 = { + .name = "qhs_pcie_cfg_3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_dma_0_cfg = { + .name = "qhs_pcie_dma_0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_dma_1_cfg = { + .name = "qhs_pcie_dma_1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_dma_2_cfg = { + .name = "qhs_pcie_dma_2_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_pcie_dma_0 = { + .name = "qxs_pcie_dma_0", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qxs_pcie_dma_1 = { + .name = "qxs_pcie_dma_1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qxs_pcie_dma_2 = { + .name = "qxs_pcie_dma_2", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .channels = 1, + .buswidth = 32, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .channels = 1, + .buswidth = 32, +}; + +static struct qcom_icc_node xs_pcie_2 = { + .name = "xs_pcie_2", + .channels = 1, + .buswidth = 16, +}; + +static struct qcom_icc_node xs_pcie_3 = { + .name = "xs_pcie_3", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qup3_core_master = { + .name = "qup3_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 16, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node qsm_pcie_noc_cfg = { + .name = "qsm_pcie_noc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 8, + .link_nodes = { &qhs_pcie_ahb2phy_cfg, &qhs_pcie_cfg_0, + &qhs_pcie_cfg_1, &qhs_pcie_cfg_2, + &qhs_pcie_cfg_3, &qhs_pcie_dma_0_cfg, + &qhs_pcie_dma_1_cfg, &qhs_pcie_dma_2_cfg }, +}; + +static struct qcom_icc_node qnm_cnoc_pcie_dma = { + .name = "qnm_cnoc_pcie_dma", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qxs_pcie_dma_0, &qxs_pcie_dma_1, + &qxs_pcie_dma_2 }, +}; + +static struct qcom_icc_node qnm_hscnoc_pcie = { + .name = "qnm_hscnoc_pcie", + .channels = 1, + .buswidth = 32, + .num_links = 4, + .link_nodes = { &xs_pcie_0, &xs_pcie_1, + &xs_pcie_2, &xs_pcie_3 }, +}; + +static struct qcom_icc_node qnm_pcie_ibnoc_dma = { + .name = "qnm_pcie_ibnoc_dma", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &xs_pcie_0 }, +}; + +static struct qcom_icc_node qss_pcie_noc_cfg = { + .name = "qss_pcie_noc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_noc_cfg }, +}; + +static struct qcom_icc_node qns_pcie_dma = { + .name = "qns_pcie_dma", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_cnoc_pcie_dma }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 16, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_hscnoc_pcie }, +}; + +static struct qcom_icc_node qns_pcie_obnoc_dma = { + .name = "qns_pcie_obnoc_dma", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_pcie_ibnoc_dma }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 56, + .link_nodes = { &ps_eth_0, &ps_eth_1, + &ps_shs_server, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_ahb2phy2, + &qhs_ahb2phy3, &qhs_ahb2phy_eth_0, + &qhs_ahb2phy_eth_1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto0_cfg, + &qhs_crypto1_cfg, &qhs_crypto2_cfg, + &qhs_display_1_cfg, &qhs_display_cfg, + &qhs_dprx0, &qhs_dprx1, + &qhs_eva_cfg, &qhs_gpuss_0_cfg, + &qhs_gpuss_1_cfg, &qhs_i2c, + &qhs_imem_cfg, &qhs_mcw_pcie, + &qhs_mm_rscc, &qhs_ne_clk_ctl, + &qhs_nspss0_cfg, &qhs_nspss1_cfg, + &qhs_nspss2_cfg, &qhs_nspss3_cfg, + &qhs_nw_clk_ctl, &qhs_prng, + &qhs_qdss_cfg, &qhs_qspi, + &qhs_qup0, &qhs_qup3, + &qhs_qup1, &qhs_qup2, + &qhs_safedma_cfg, &qhs_sdc4, + &qhs_se_clk_ctl, &qhs_tcsr, + &qhs_tlmm, &qhs_tsc_cfg, + &qhs_ufs_mem_cfg, &qhs_usb2, + &qhs_usb3_0, &qhs_usb3_1, + &qhs_venus_cfg, &qss_computenoc_cfg, + &qss_pcie_noc_cfg, &qss_qtc_cfg, + &xs_qdss_stm, &xs_sys_tcu0_cfg, + &xs_sys_tcu1_cfg, &xs_sys_tcu2_cfg }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa44000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg }, +}; + +static struct qcom_icc_node qhm_mm_rscc = { + .name = "qhm_mm_rscc", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qss_cfg }, +}; + +static struct qcom_icc_node qnm_hscnoc = { + .name = "qnm_hscnoc", + .channels = 1, + .buswidth = 16, + .num_links = 10, + .link_nodes = { &qhs_aoss, &qhs_hbcu, + &qhs_ipa, &qhs_ipc_router, + &qhs_soccp, &qhs_tme_cfg, + &qns_pcie_dma, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem }, +}; + +static struct qcom_icc_node qns_hscnoc_cnoc = { + .name = "qns_hscnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_hscnoc }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 2, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x930000, 0xa45000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_qtc = { + .name = "alm_qtc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2c0000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu0 = { + .name = "alm_sys_tcu0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa42000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu1 = { + .name = "alm_sys_tcu1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x81f000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu2 = { + .name = "alm_sys_tcu2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x30000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 6, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_aggre_north = { + .name = "qnm_aggre_north", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x935000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_aggre_south = { + .name = "qnm_aggre_south", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x31000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_gpu0 = { + .name = "qnm_gpu0", + .channels = 4, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x931000, 0x932000, 0x933000, 0x934000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_gpu1 = { + .name = "qnm_gpu1", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xa40000, 0xa41000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_hpass_adas_hscnoc = { + .name = "qnm_hpass_adas_hscnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x240000, 0x245000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_hpass_audio_hscnoc = { + .name = "qnm_hpass_audio_hscnoc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x241000 }, + .prio = 3, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x81d000, 0x820000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x81e000, 0x821000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp0_hscnoc = { + .name = "qnm_nsp0_hscnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x32000, 0x33000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp1_hscnoc = { + .name = "qnm_nsp1_hscnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x34000, 0x35000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp2_hscnoc = { + .name = "qnm_nsp2_hscnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x36000, 0x37000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp3_hscnoc = { + .name = "qnm_nsp3_hscnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x38000, 0x39000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2c1000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_sailss_md0_hscnoc = { + .name = "qnm_sailss_md0_hscnoc", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x243000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa43000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_hscnoc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_a1noc_hscnoc = { + .name = "qns_a1noc_hscnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre_north }, +}; + +static struct qcom_icc_node qns_a2noc_hscnoc = { + .name = "qns_a2noc_hscnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre_south }, +}; + +static struct qcom_icc_node qns_hpass_agnoc_audio = { + .name = "qns_hpass_agnoc_audio", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_hpass_audio_hscnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_nsp0_hsc_noc = { + .name = "qns_nsp0_hsc_noc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp0_hscnoc }, +}; + +static struct qcom_icc_node qns_nsp1_hsc_noc = { + .name = "qns_nsp1_hsc_noc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp1_hscnoc }, +}; + +static struct qcom_icc_node qns_nsp2_hsc_noc = { + .name = "qns_nsp2_hsc_noc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp2_hscnoc }, +}; + +static struct qcom_icc_node qns_nsp3_hsc_noc = { + .name = "qns_nsp3_hsc_noc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp3_hscnoc }, +}; + +static struct qcom_icc_node qns_pcie_hscnoc = { + .name = "qns_pcie_hscnoc", + .channels = 1, + .buswidth = 64, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_hscnoc_sf = { + .name = "qns_hscnoc_sf", + .channels = 1, + .buswidth = 64, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1b000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node qxm_crypto_0 = { + .name = "qxm_crypto_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node qxm_crypto_1 = { + .name = "qxm_crypto_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1d000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node qxm_crypto_2 = { + .name = "qxm_crypto_2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node xm_usb2 = { + .name = "xm_usb2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node xm_usb3_1 = { + .name = "xm_usb3_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_hscnoc }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_hscnoc }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_hscnoc }, +}; + +static struct qcom_icc_node xm_emac_0 = { + .name = "xm_emac_0", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_hscnoc }, +}; + +static struct qcom_icc_node xm_emac_1 = { + .name = "xm_emac_1", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_hscnoc }, +}; + +static struct qcom_icc_node qnm_hpass_dsp0 = { + .name = "qnm_hpass_dsp0", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_hpass_agnoc_audio }, +}; + +static struct qcom_icc_node qnm_hpass_dsp1 = { + .name = "qnm_hpass_dsp1", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_hpass_agnoc_audio }, +}; + +static struct qcom_icc_node qnm_hpass_dsp2 = { + .name = "qnm_hpass_dsp2", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_hpass_agnoc_audio }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0x58000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1b000, 0x1c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_dprx0 = { + .name = "qnm_dprx0", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_dprx1 = { + .name = "qnm_dprx1", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5d000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp0 = { + .name = "qnm_mdp0", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x59000, 0x5a000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp1 = { + .name = "qnm_mdp1", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x5e000, 0x5f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x21000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x22000, 0x23000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp0 = { + .name = "qnm_video_mvp0", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1d000, 0x1e000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp1 = { + .name = "qnm_video_mvp1", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x1f000, 0x20000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x24000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_nsp_data00 = { + .name = "qnm_nsp_data00", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp0_hsc_noc }, +}; + +static struct qcom_icc_node qnm_nsp_data01 = { + .name = "qnm_nsp_data01", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp1_hsc_noc }, +}; + +static struct qcom_icc_node qnm_nsp_data02 = { + .name = "qnm_nsp_data02", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp2_hsc_noc }, +}; + +static struct qcom_icc_node qnm_nsp_data03 = { + .name = "qnm_nsp_data03", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp3_hsc_noc }, +}; + +static struct qcom_icc_node qxm_pcie_dma_0 = { + .name = "qxm_pcie_dma_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x49000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_pcie_hscnoc, &qns_pcie_obnoc_dma }, +}; + +static struct qcom_icc_node qxm_pcie_dma_1 = { + .name = "qxm_pcie_dma_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4a000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_pcie_hscnoc, &qns_pcie_obnoc_dma }, +}; + +static struct qcom_icc_node qxm_pcie_dma_2 = { + .name = "qxm_pcie_dma_2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4b000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_pcie_hscnoc, &qns_pcie_obnoc_dma }, +}; + +static struct qcom_icc_node xm_pcie_0 = { + .name = "xm_pcie_0", + .channels = 1, + .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_hscnoc }, +}; + +static struct qcom_icc_node xm_pcie_1 = { + .name = "xm_pcie_1", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_hscnoc }, +}; + +static struct qcom_icc_node xm_pcie_2 = { + .name = "xm_pcie_2", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_hscnoc }, +}; + +static struct qcom_icc_node xm_pcie_3 = { + .name = "xm_pcie_3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1b000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_hscnoc }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_node qnm_nsi_noc = { + .name = "qnm_nsi_noc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_node qnm_safe_dma = { + .name = "qnm_safe_dma", + .channels = 1, + .buswidth = 64, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1b000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qnm_sailss_md1 = { + .name = "qnm_sailss_md1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_qup3 = { + .name = "qxm_qup3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_bcm bcm_c0n0 = { + .name = "C0N0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp_data00, &qns_nsp0_hsc_noc }, +}; + +static struct qcom_icc_bcm bcm_c1n0 = { + .name = "C1N0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp_data01, &qns_nsp1_hsc_noc }, +}; + +static struct qcom_icc_bcm bcm_c2n0 = { + .name = "C2N0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp_data02, &qns_nsp2_hsc_noc }, +}; + +static struct qcom_icc_bcm bcm_c3n0 = { + .name = "C3N0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp_data03, &qns_nsp3_hsc_noc }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto_0 }, +}; + +static struct qcom_icc_bcm bcm_ce1 = { + .name = "CE1", + .num_nodes = 1, + .nodes = { &qxm_crypto_1 }, +}; + +static struct qcom_icc_bcm bcm_ce2 = { + .name = "CE2", + .num_nodes = 1, + .nodes = { &qxm_crypto_2 }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .enable_mask = BIT(0), + .num_nodes = 6, + .nodes = { &qsm_cfg, &qhm_mm_rscc, + &qnm_hscnoc, &qnm_cnoc_pcie_dma, + &qnm_hscnoc_pcie, &qnm_pcie_ibnoc_dma }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 2, + .nodes = { &qhs_display_1_cfg, &qhs_display_cfg }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 14, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_dprx0, &qnm_dprx1, + &qnm_mdp0, &qnm_mdp1, + &qnm_video_cv_cpu, &qnm_video_eva, + &qnm_video_mvp0, &qnm_video_mvp1, + &qnm_video_v_cpu, &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup3 = { + .name = "QUP3", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 24, + .nodes = { &alm_gpu_tcu, &alm_qtc, + &alm_sys_tcu0, &alm_sys_tcu1, + &alm_sys_tcu2, &chm_apps, + &qnm_aggre_north, &qnm_aggre_south, + &qnm_gpu0, &qnm_gpu1, + &qnm_hpass_adas_hscnoc, &qnm_hpass_audio_hscnoc, + &qnm_mnoc_hf, &qnm_mnoc_sf, + &qnm_nsp0_hscnoc, &qnm_nsp1_hscnoc, + &qnm_nsp2_hscnoc, &qnm_nsp3_hscnoc, + &qnm_pcie, &qnm_sailss_md0_hscnoc, + &qnm_snoc_sf, &xm_gic, + &qns_hscnoc_cnoc, &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_hscnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .enable_mask = BIT(0), + .num_nodes = 14, + .nodes = { &qns_a1noc_hscnoc, &qns_a2noc_hscnoc, + &qxm_pcie_dma_0, &qxm_pcie_dma_1, + &qxm_pcie_dma_2, &xm_pcie_0, + &xm_pcie_1, &xm_pcie_2, + &xm_pcie_3, &qns_pcie_hscnoc, + &qns_pcie_obnoc_dma, &qnm_cnoc_data, + &qnm_nsi_noc, &qnm_safe_dma }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_SAILSS_MD1] = &qnm_sailss_md1, + [MASTER_QUP_3] = &qxm_qup3, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config nord_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c400, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_aggre1_noc = { + .config = &nord_aggre1_noc_regmap_config, + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), +}; + +static struct qcom_icc_bcm * const aggre1_noc_tile_bcms[] = { + &bcm_ce0, + &bcm_ce1, + &bcm_ce2, + &bcm_sn1, +}; + +static struct qcom_icc_node * const aggre1_noc_tile_nodes[] = { + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_CRYPTO_CORE0] = &qxm_crypto_0, + [MASTER_CRYPTO_CORE1] = &qxm_crypto_1, + [MASTER_CRYPTO_CORE2] = &qxm_crypto_2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB2] = &xm_usb2, + [MASTER_USB3_0] = &xm_usb3_0, + [MASTER_USB3_1] = &xm_usb3_1, + [SLAVE_A1NOC_HSCNOC] = &qns_a1noc_hscnoc, +}; + +static const struct regmap_config nord_aggre1_noc_tile_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x23400, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_aggre1_noc_tile = { + .config = &nord_aggre1_noc_tile_regmap_config, + .nodes = aggre1_noc_tile_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_tile_nodes), + .bcms = aggre1_noc_tile_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_tile_bcms), +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_IPA] = &qxm_ipa, + [MASTER_SOCCP_AGGR_NOC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct regmap_config nord_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1b400, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_aggre2_noc = { + .config = &nord_aggre2_noc_regmap_config, + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), +}; + +static struct qcom_icc_bcm * const aggre2_noc_tile_bcms[] = { + &bcm_sn1, +}; + +static struct qcom_icc_node * const aggre2_noc_tile_nodes[] = { + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_EMAC_0] = &xm_emac_0, + [MASTER_EMAC_1] = &xm_emac_1, + [SLAVE_A2NOC_HSCNOC] = &qns_a2noc_hscnoc, +}; + +static const struct regmap_config nord_aggre2_noc_tile_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1b400, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_aggre2_noc_tile = { + .config = &nord_aggre2_noc_tile_regmap_config, + .nodes = aggre2_noc_tile_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_tile_nodes), + .bcms = aggre2_noc_tile_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_tile_bcms), +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, + &bcm_qup3, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, +}; + +static const struct qcom_icc_desc nord_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_cfg_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_PS_ETH_0] = &ps_eth_0, + [SLAVE_PS_ETH_1] = &ps_eth_1, + [SLAVE_SHS_SERVER] = &ps_shs_server, + [SLAVE_AHB2PHY_0] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_1] = &qhs_ahb2phy1, + [SLAVE_AHB2PHY_2] = &qhs_ahb2phy2, + [SLAVE_AHB2PHY_3] = &qhs_ahb2phy3, + [SLAVE_AHB2PHY_ETH_0] = &qhs_ahb2phy_eth_0, + [SLAVE_AHB2PHY_ETH_1] = &qhs_ahb2phy_eth_1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CRYPTO_1_CFG] = &qhs_crypto1_cfg, + [SLAVE_CRYPTO_2_CFG] = &qhs_crypto2_cfg, + [SLAVE_DISPLAY_1_CFG] = &qhs_display_1_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_DPRX0] = &qhs_dprx0, + [SLAVE_DPRX1] = &qhs_dprx1, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_0_cfg, + [SLAVE_GFX3D_1_CFG] = &qhs_gpuss_1_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_MCW_PCIE] = &qhs_mcw_pcie, + [SLAVE_MM_RSCC] = &qhs_mm_rscc, + [SLAVE_NE_CLK_CTL] = &qhs_ne_clk_ctl, + [SLAVE_NSPSS0_CFG] = &qhs_nspss0_cfg, + [SLAVE_NSPSS1_CFG] = &qhs_nspss1_cfg, + [SLAVE_NSPSS2_CFG] = &qhs_nspss2_cfg, + [SLAVE_NSPSS3_CFG] = &qhs_nspss3_cfg, + [SLAVE_NW_CLK_CTL] = &qhs_nw_clk_ctl, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SAFEDMA_CFG] = &qhs_safedma_cfg, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_SE_CLK_CTL] = &qhs_se_clk_ctl, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_TSC_CFG] = &qhs_tsc_cfg, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB2] = &qhs_usb2, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_USB3_1] = &qhs_usb3_1, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_COMPUTENOC_CFG] = &qss_computenoc_cfg, + [SLAVE_PCIE_NOC_CFG] = &qss_pcie_noc_cfg, + [SLAVE_QTC_CFG] = &qss_qtc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_SYS_TCU0_CFG] = &xs_sys_tcu0_cfg, + [SLAVE_SYS_TCU1_CFG] = &xs_sys_tcu1_cfg, + [SLAVE_SYS_TCU2_CFG] = &xs_sys_tcu2_cfg, +}; + +static const struct regmap_config nord_cnoc_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_cnoc_cfg = { + .config = &nord_cnoc_cfg_regmap_config, + .nodes = cnoc_cfg_nodes, + .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), + .bcms = cnoc_cfg_bcms, + .num_bcms = ARRAY_SIZE(cnoc_cfg_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_MM_RSCC] = &qhm_mm_rscc, + [MASTER_HSCNOC_CNOC] = &qnm_hscnoc, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_HBCU] = &qhs_hbcu, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_PCIE_DMA] = &qns_pcie_dma, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, +}; + +static const struct regmap_config nord_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1d200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_cnoc_main = { + .config = &nord_cnoc_main_regmap_config, + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_node * const hpass_ag_noc_nodes[] = { + [MASTER_HPASS_PROC_0] = &qnm_hpass_dsp0, + [MASTER_HPASS_PROC_1] = &qnm_hpass_dsp1, + [MASTER_HPASS_PROC_2] = &qnm_hpass_dsp2, + [SLAVE_HPASS_AGNOC_AUDIO] = &qns_hpass_agnoc_audio, +}; + +static const struct regmap_config nord_hpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x37080, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_hpass_ag_noc = { + .config = &nord_hpass_ag_noc_regmap_config, + .nodes = hpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(hpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const hscnoc_bcms[] = { + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const hscnoc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_QTC_TCU] = &alm_qtc, + [MASTER_SYS_TCU_0] = &alm_sys_tcu0, + [MASTER_SYS_TCU_1] = &alm_sys_tcu1, + [MASTER_SYS_TCU_2] = &alm_sys_tcu2, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_A1NOC_TILE_HSCNOC] = &qnm_aggre_north, + [MASTER_A2NOC_TILE_HSCNOC] = &qnm_aggre_south, + [MASTER_GFX3D] = &qnm_gpu0, + [MASTER_GFX3D_1] = &qnm_gpu1, + [MASTER_HPASS_ADAS_HSCNOC] = &qnm_hpass_adas_hscnoc, + [MASTER_HPASS_AUDIO_HSCNOC] = &qnm_hpass_audio_hscnoc, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_NSP0_HSCNOC] = &qnm_nsp0_hscnoc, + [MASTER_NSP1_HSCNOC] = &qnm_nsp1_hscnoc, + [MASTER_NSP2_HSCNOC] = &qnm_nsp2_hscnoc, + [MASTER_NSP3_HSCNOC] = &qnm_nsp3_hscnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SAILSS_MD0_HSCNOC] = &qnm_sailss_md0_hscnoc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_GIC] = &xm_gic, + [SLAVE_HSCNOC_CNOC] = &qns_hscnoc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config nord_hscnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xa45080, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_hscnoc = { + .config = &nord_hscnoc_regmap_config, + .nodes = hscnoc_nodes, + .num_nodes = ARRAY_SIZE(hscnoc_nodes), + .bcms = hscnoc_bcms, + .num_bcms = ARRAY_SIZE(hscnoc_bcms), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc nord_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_DPRX0] = &qnm_dprx0, + [MASTER_DPRX1] = &qnm_dprx1, + [MASTER_MDP0] = &qnm_mdp0, + [MASTER_MDP1] = &qnm_mdp1, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP0] = &qnm_video_mvp0, + [MASTER_VIDEO_MVP1] = &qnm_video_mvp1, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, +}; + +static const struct regmap_config nord_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x72800, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_mmss_noc = { + .config = &nord_mmss_noc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_data_noc_0_bcms[] = { + &bcm_c0n0, +}; + +static struct qcom_icc_node * const nsp_data_noc_0_nodes[] = { + [MASTER_NSP0_PROC] = &qnm_nsp_data00, + [SLAVE_NSP0_HSC_NOC] = &qns_nsp0_hsc_noc, +}; + +static const struct regmap_config nord_nsp_data_noc_0_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2a200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_nsp_data_noc_0 = { + .config = &nord_nsp_data_noc_0_regmap_config, + .nodes = nsp_data_noc_0_nodes, + .num_nodes = ARRAY_SIZE(nsp_data_noc_0_nodes), + .bcms = nsp_data_noc_0_bcms, + .num_bcms = ARRAY_SIZE(nsp_data_noc_0_bcms), +}; + +static struct qcom_icc_bcm * const nsp_data_noc_1_bcms[] = { + &bcm_c1n0, +}; + +static struct qcom_icc_node * const nsp_data_noc_1_nodes[] = { + [MASTER_NSP1_PROC] = &qnm_nsp_data01, + [SLAVE_NSP1_HSC_NOC] = &qns_nsp1_hsc_noc, +}; + +static const struct regmap_config nord_nsp_data_noc_1_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2a200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_nsp_data_noc_1 = { + .config = &nord_nsp_data_noc_1_regmap_config, + .nodes = nsp_data_noc_1_nodes, + .num_nodes = ARRAY_SIZE(nsp_data_noc_1_nodes), + .bcms = nsp_data_noc_1_bcms, + .num_bcms = ARRAY_SIZE(nsp_data_noc_1_bcms), +}; + +static struct qcom_icc_bcm * const nsp_data_noc_2_bcms[] = { + &bcm_c2n0, +}; + +static struct qcom_icc_node * const nsp_data_noc_2_nodes[] = { + [MASTER_NSP2_PROC] = &qnm_nsp_data02, + [SLAVE_NSP2_HSC_NOC] = &qns_nsp2_hsc_noc, +}; + +static const struct regmap_config nord_nsp_data_noc_2_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2a200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_nsp_data_noc_2 = { + .config = &nord_nsp_data_noc_2_regmap_config, + .nodes = nsp_data_noc_2_nodes, + .num_nodes = ARRAY_SIZE(nsp_data_noc_2_nodes), + .bcms = nsp_data_noc_2_bcms, + .num_bcms = ARRAY_SIZE(nsp_data_noc_2_bcms), +}; + +static struct qcom_icc_bcm * const nsp_data_noc_3_bcms[] = { + &bcm_c3n0, +}; + +static struct qcom_icc_node * const nsp_data_noc_3_nodes[] = { + [MASTER_NSP3_PROC] = &qnm_nsp_data03, + [SLAVE_NSP3_HSC_NOC] = &qns_nsp3_hsc_noc, +}; + +static const struct regmap_config nord_nsp_data_noc_3_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2a200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_nsp_data_noc_3 = { + .config = &nord_nsp_data_noc_3_regmap_config, + .nodes = nsp_data_noc_3_nodes, + .num_nodes = ARRAY_SIZE(nsp_data_noc_3_nodes), + .bcms = nsp_data_noc_3_bcms, + .num_bcms = ARRAY_SIZE(nsp_data_noc_3_bcms), +}; + +static struct qcom_icc_node * const pcie_cfg_nodes[] = { + [MASTER_PCIE_NOC_CFG] = &qsm_pcie_noc_cfg, + [SLAVE_PCIE_AHB2PHY_CFG] = &qhs_pcie_ahb2phy_cfg, + [SLAVE_PCIE_CFG_0] = &qhs_pcie_cfg_0, + [SLAVE_PCIE_CFG_1] = &qhs_pcie_cfg_1, + [SLAVE_PCIE_CFG_2] = &qhs_pcie_cfg_2, + [SLAVE_PCIE_CFG_3] = &qhs_pcie_cfg_3, + [SLAVE_PCIE_DMA_0_CFG] = &qhs_pcie_dma_0_cfg, + [SLAVE_PCIE_DMA_1_CFG] = &qhs_pcie_dma_1_cfg, + [SLAVE_PCIE_DMA_2_CFG] = &qhs_pcie_dma_2_cfg, +}; + +static const struct regmap_config nord_pcie_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7200, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_pcie_cfg = { + .config = &nord_pcie_cfg_regmap_config, + .nodes = pcie_cfg_nodes, + .num_nodes = ARRAY_SIZE(pcie_cfg_nodes), +}; + +static struct qcom_icc_bcm * const pcie_data_inbound_bcms[] = { + &bcm_sn1, +}; + +static struct qcom_icc_node * const pcie_data_inbound_nodes[] = { + [MASTER_PCIE_DMA_0] = &qxm_pcie_dma_0, + [MASTER_PCIE_DMA_1] = &qxm_pcie_dma_1, + [MASTER_PCIE_DMA_2] = &qxm_pcie_dma_2, + [MASTER_PCIE_0] = &xm_pcie_0, + [MASTER_PCIE_1] = &xm_pcie_1, + [MASTER_PCIE_2] = &xm_pcie_2, + [MASTER_PCIE_3] = &xm_pcie_3, + [SLAVE_PCIE_HSCNOC] = &qns_pcie_hscnoc, + [SLAVE_PCIE_OBNOC_DMA] = &qns_pcie_obnoc_dma, +}; + +static const struct regmap_config nord_pcie_data_inbound_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x4b080, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_pcie_data_inbound = { + .config = &nord_pcie_data_inbound_regmap_config, + .nodes = pcie_data_inbound_nodes, + .num_nodes = ARRAY_SIZE(pcie_data_inbound_nodes), + .bcms = pcie_data_inbound_bcms, + .num_bcms = ARRAY_SIZE(pcie_data_inbound_bcms), +}; + +static struct qcom_icc_bcm * const pcie_data_outbound_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const pcie_data_outbound_nodes[] = { + [MASTER_CNOC_PCIE_DMA] = &qnm_cnoc_pcie_dma, + [MASTER_ANOC_PCIE_HSCNOC] = &qnm_hscnoc_pcie, + [MASTER_PCIE_IBNOC_DMA] = &qnm_pcie_ibnoc_dma, + [SLAVE_PCIE_DMA_0] = &qxs_pcie_dma_0, + [SLAVE_PCIE_DMA_1] = &qxs_pcie_dma_1, + [SLAVE_PCIE_DMA_2] = &qxs_pcie_dma_2, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_PCIE_2] = &xs_pcie_2, + [SLAVE_PCIE_3] = &xs_pcie_3, +}; + +static const struct regmap_config nord_pcie_data_outbound_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17000, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_pcie_data_outbound = { + .config = &nord_pcie_data_outbound_regmap_config, + .nodes = pcie_data_outbound_nodes, + .num_nodes = ARRAY_SIZE(pcie_data_outbound_nodes), + .bcms = pcie_data_outbound_bcms, + .num_bcms = ARRAY_SIZE(pcie_data_outbound_bcms), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn1, + &bcm_sn2, + &bcm_sn3, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [MASTER_NSINOC_SNOC] = &qnm_nsi_noc, + [MASTER_SAFE_DMA] = &qnm_safe_dma, + [SLAVE_SNOC_HSCNOC_SF] = &qns_hscnoc_sf, +}; + +static const struct regmap_config nord_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c080, + .fast_io = true, +}; + +static const struct qcom_icc_desc nord_system_noc = { + .config = &nord_system_noc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,nord-aggre1-noc", .data = &nord_aggre1_noc }, + { .compatible = "qcom,nord-aggre1-noc-tile", .data = &nord_aggre1_noc_tile }, + { .compatible = "qcom,nord-aggre2-noc", .data = &nord_aggre2_noc }, + { .compatible = "qcom,nord-aggre2-noc-tile", .data = &nord_aggre2_noc_tile }, + { .compatible = "qcom,nord-clk-virt", .data = &nord_clk_virt }, + { .compatible = "qcom,nord-cnoc-cfg", .data = &nord_cnoc_cfg }, + { .compatible = "qcom,nord-cnoc-main", .data = &nord_cnoc_main }, + { .compatible = "qcom,nord-hpass-ag-noc", .data = &nord_hpass_ag_noc }, + { .compatible = "qcom,nord-hscnoc", .data = &nord_hscnoc }, + { .compatible = "qcom,nord-mc-virt", .data = &nord_mc_virt }, + { .compatible = "qcom,nord-mmss-noc", .data = &nord_mmss_noc }, + { .compatible = "qcom,nord-nsp-data-noc-0", .data = &nord_nsp_data_noc_0 }, + { .compatible = "qcom,nord-nsp-data-noc-1", .data = &nord_nsp_data_noc_1 }, + { .compatible = "qcom,nord-nsp-data-noc-2", .data = &nord_nsp_data_noc_2 }, + { .compatible = "qcom,nord-nsp-data-noc-3", .data = &nord_nsp_data_noc_3 }, + { .compatible = "qcom,nord-pcie-cfg", .data = &nord_pcie_cfg }, + { .compatible = "qcom,nord-pcie-data-inbound", .data = &nord_pcie_data_inbound }, + { .compatible = "qcom,nord-pcie-data-outbound", .data = &nord_pcie_data_outbound }, + { .compatible = "qcom,nord-system-noc", .data = &nord_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-nord", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Nord NoC driver"); +MODULE_LICENSE("GPL"); From a8f6774070428b9318acb65d4b9c02dc7aa4fffe Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 27 Apr 2026 23:52:55 +0530 Subject: [PATCH 0643/5214] dt-bindings: interconnect: qcom-bwmon: Add Hawi cpu-bwmon compatible Add the Qualcomm Hawi SoC compatible string for the CPU bandwidth monitor and there is single instance present globally to monitor the traffic from CPU to LLCC. Signed-off-by: Mukesh Ojha Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427182255.3649922-1-mukesh.ojha@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index ce79521bb1ef..82b1d94d3010 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -26,6 +26,7 @@ properties: - items: - enum: - qcom,glymur-cpu-bwmon + - qcom,hawi-cpu-bwmon - qcom,kaanapali-cpu-bwmon - qcom,qcm2290-cpu-bwmon - qcom,qcs615-cpu-bwmon From 7562778563ada536bf52123ff06aa749cba2d1b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 16 Apr 2026 15:09:13 +0200 Subject: [PATCH 0644/5214] interconnect: Do not create empty devres on missing interconnects of_icc_get() returns NULL on valid case - no interconnects - but no need to create devres for that, because it just wastes memory without any use/benefits. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Kuan-Wei Chiu Link: https://patch.msgid.link/20260416130912.375013-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 8569b78a1851..ce572c62f58f 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -432,7 +432,7 @@ struct icc_path *devm_of_icc_get(struct device *dev, const char *name) return ERR_PTR(-ENOMEM); path = of_icc_get(dev, name); - if (!IS_ERR(path)) { + if (!IS_ERR_OR_NULL(path)) { *ptr = path; devres_add(dev, ptr); } else { From 5f00d6e5bde009594eb3c462923b7897418a3d55 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:28:55 +0200 Subject: [PATCH 0645/5214] interconnect: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the privers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260505102854.186925-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8953.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/msm8953.c b/drivers/interconnect/qcom/msm8953.c index be2b1a606612..94a9773d2970 100644 --- a/drivers/interconnect/qcom/msm8953.c +++ b/drivers/interconnect/qcom/msm8953.c @@ -1307,6 +1307,7 @@ static const struct of_device_id msm8953_noc_of_match[] = { { .compatible = "qcom,msm8953-snoc-mm", .data = &msm8953_snoc_mm }, { } }; +MODULE_DEVICE_TABLE(of, msm8953_noc_of_match); static struct platform_driver msm8953_noc_driver = { .probe = qnoc_probe, @@ -1318,6 +1319,5 @@ static struct platform_driver msm8953_noc_driver = { }; module_platform_driver(msm8953_noc_driver); -MODULE_DEVICE_TABLE(of, msm8953_noc_of_match); MODULE_DESCRIPTION("Qualcomm MSM8953 NoC driver"); MODULE_LICENSE("GPL"); From 5fd68a012032c3ed8c002caa2e1d26a84116ad83 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 3 May 2026 18:16:54 +0200 Subject: [PATCH 0646/5214] dt-bindings: interconnect: qcom,sdm660: Disallow clocks when appropriate Only qcom,sdm660-mnoc and qcom,sdm660-a2noc devices from what is covered by this binding have clocks. Others do not, so restrict the schema to be more accurate. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260503161653.60785-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,sdm660.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml index 8f6bc6399626..51428a2b0ce0 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml @@ -79,6 +79,19 @@ allOf: - const: aggre2_usb3_axi - const: cfg_noc_usb2_axi + - if: + properties: + compatible: + enum: + - qcom,sdm660-bimc + - qcom,sdm660-cnoc + - qcom,sdm660-gnoc + - qcom,sdm660-snoc + then: + properties: + clocks: false + clock-names: false + examples: - | #include From 59eff26d62bf54a2f0751cb0c2b07f314888702f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 3 May 2026 18:16:55 +0200 Subject: [PATCH 0647/5214] dt-bindings: interconnect: qcom,sm6115: Drop incorrect children if:then: block This binding has children, so any if:then: block restricting them, cannot be defined in top-level allOf:if:then:properties:, because it simply does not match these children. The block, if it was useful, should be defined within patternProperties for the children, however since child nodes do not have clocks at all, there is little point in disallowing them in the first place. Remove completely redundant and ineffective piece of code. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260503161653.60785-5-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,sm6115.yaml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml index 14b1a0b08e73..67c1705af50f 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml @@ -95,20 +95,6 @@ allOf: - const: usb_axi - const: ipa - - if: - properties: - compatible: - enum: - - qcom,sm6115-bimc - - qcom,sm6115-clk-virt - - qcom,sm6115-mmrt-virt - - qcom,sm6115-mmnrt-virt - - then: - properties: - clocks: false - clock-names: false - unevaluatedProperties: false examples: From 401af0eb273f8188c2167923933bf92360e1f50e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 3 May 2026 18:16:56 +0200 Subject: [PATCH 0648/5214] dt-bindings: interconnect: qcom,sm6115: Restrict children and clocks Some interconnect devices described in the binding have children and some have clocks. The devices which do not have them, should have this restricted (disallowed). qcom,sm6115-cnoc has a clock, thus also extend the example to be complete for this device. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260503161653.60785-6-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,sm6115.yaml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml index 67c1705af50f..cdae0acf3a1d 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm6115.yaml @@ -62,23 +62,33 @@ allOf: - if: properties: compatible: - const: qcom,sm6115-cnoc + const: qcom,sm6115-bimc + then: + properties: + clocks: false + clock-names: false + patternProperties: + '^interconnect-[a-z0-9]+$': false + - if: + properties: + compatible: + const: qcom,sm6115-cnoc then: properties: clocks: items: - description: USB-NoC AXI clock - clock-names: items: - const: usb_axi + patternProperties: + '^interconnect-[a-z0-9]+$': false - if: properties: compatible: const: qcom,sm6115-snoc - then: properties: clocks: @@ -87,7 +97,6 @@ allOf: - description: UFS-NoC AXI clock. - description: USB-NoC AXI clock. - description: IPA clock. - clock-names: items: - const: cpu_axi @@ -135,4 +144,6 @@ examples: compatible = "qcom,sm6115-cnoc"; reg = <0x01900000 0x8200>; #interconnect-cells = <1>; + clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>; + clock-names = "usb_axi"; }; From 059f1a4c9e3aa44d888c0e7cf4559403eece0438 Mon Sep 17 00:00:00 2001 From: Nora Schiffer Date: Tue, 7 Apr 2026 13:42:33 +0200 Subject: [PATCH 0649/5214] dt-bindings: phy: ti: phy-j721e-wiz: Add ti,j722s-wiz-10g compatible The J722S WIZ is mostly identical to the AM64's, but additionally supports SGMII. The AM64 compatible ti,am64-wiz-10g is used as a fallback. Signed-off-by: Nora Schiffer Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/1ef8adf850f2fd41b6c4e3c89e4f4e6e0f469a0e.1775559102.git.nora.schiffer@ew.tq-group.com Signed-off-by: Vinod Koul --- .../bindings/phy/ti,phy-j721e-wiz.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml index 3f16ff14484d..0653252c18d8 100644 --- a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml +++ b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml @@ -12,13 +12,18 @@ maintainers: properties: compatible: - enum: - - ti,j721e-wiz-16g - - ti,j721e-wiz-10g - - ti,j721s2-wiz-10g - - ti,am64-wiz-10g - - ti,j7200-wiz-10g - - ti,j784s4-wiz-10g + oneOf: + - enum: + - ti,j721e-wiz-16g + - ti,j721e-wiz-10g + - ti,j721s2-wiz-10g + - ti,am64-wiz-10g + - ti,j7200-wiz-10g + - ti,j784s4-wiz-10g + - items: + - enum: + - ti,j722s-wiz-10g + - const: ti,am64-wiz-10g power-domains: maxItems: 1 From 567b3c62a7eb51db4cb562b416ec220132d524c9 Mon Sep 17 00:00:00 2001 From: Nora Schiffer Date: Tue, 7 Apr 2026 13:42:34 +0200 Subject: [PATCH 0650/5214] dt-bindings: phy: ti: phy-gmii-sel: Add ti,j722s-phy-gmii-sel compatible The J722S gmii-sel is mostly identical to the AM64's, but additionally supports SGMII. The AM64 compatible ti,am654-phy-gmii-sel is used as a fallback. Signed-off-by: Nora Schiffer Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/b67c8b0bc9cc918667e9329d79f617d033d025d5.1775559102.git.nora.schiffer@ew.tq-group.com Signed-off-by: Vinod Koul --- .../bindings/phy/ti,phy-gmii-sel.yaml | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/ti,phy-gmii-sel.yaml b/Documentation/devicetree/bindings/phy/ti,phy-gmii-sel.yaml index be41b4547ec6..60b644a4c639 100644 --- a/Documentation/devicetree/bindings/phy/ti,phy-gmii-sel.yaml +++ b/Documentation/devicetree/bindings/phy/ti,phy-gmii-sel.yaml @@ -47,15 +47,20 @@ description: | properties: compatible: - enum: - - ti,am3352-phy-gmii-sel - - ti,dra7xx-phy-gmii-sel - - ti,am43xx-phy-gmii-sel - - ti,dm814-phy-gmii-sel - - ti,am654-phy-gmii-sel - - ti,j7200-cpsw5g-phy-gmii-sel - - ti,j721e-cpsw9g-phy-gmii-sel - - ti,j784s4-cpsw9g-phy-gmii-sel + oneOf: + - enum: + - ti,am3352-phy-gmii-sel + - ti,dra7xx-phy-gmii-sel + - ti,am43xx-phy-gmii-sel + - ti,dm814-phy-gmii-sel + - ti,am654-phy-gmii-sel + - ti,j7200-cpsw5g-phy-gmii-sel + - ti,j721e-cpsw9g-phy-gmii-sel + - ti,j784s4-cpsw9g-phy-gmii-sel + - items: + - enum: + - ti,j722s-phy-gmii-sel + - const: ti,am654-phy-gmii-sel reg: maxItems: 1 From 61849b7afb579630fc45dbeaf5449b42b33cc70e Mon Sep 17 00:00:00 2001 From: Nora Schiffer Date: Tue, 7 Apr 2026 13:42:35 +0200 Subject: [PATCH 0651/5214] phy: ti: phy-j721e-wiz: add support for J722S SoC family The J722S WIZ is mostly identical to the AM64's, but additionally supports SGMII. Signed-off-by: Nora Schiffer Link: https://patch.msgid.link/85e9f67f8673dc7039ab87c34cfc9e4de1c7447c.1775559102.git.nora.schiffer@ew.tq-group.com Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-j721e-wiz.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index 6b584706b913..7531a8a04912 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -331,6 +331,7 @@ enum wiz_type { J721E_WIZ_16G, J721E_WIZ_10G, /* Also for J7200 SR1.0 */ AM64_WIZ_10G, + J722S_WIZ_10G, J7200_WIZ_10G, /* J7200 SR2.0 */ J784S4_WIZ_10G, J721S2_WIZ_10G, @@ -1020,6 +1021,7 @@ static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node) switch (wiz->type) { case AM64_WIZ_10G: + case J722S_WIZ_10G: case J7200_WIZ_10G: case J784S4_WIZ_10G: case J721S2_WIZ_10G: @@ -1089,6 +1091,7 @@ static void wiz_clock_init(struct wiz *wiz) switch (wiz->type) { case AM64_WIZ_10G: + case J722S_WIZ_10G: case J7200_WIZ_10G: switch (rate) { case REF_CLK_100MHZ: @@ -1158,6 +1161,7 @@ static int wiz_clock_probe(struct wiz *wiz, struct device_node *node) switch (wiz->type) { case AM64_WIZ_10G: + case J722S_WIZ_10G: case J7200_WIZ_10G: case J784S4_WIZ_10G: case J721S2_WIZ_10G: @@ -1246,6 +1250,14 @@ static int wiz_phy_fullrt_div(struct wiz *wiz, int lane) if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII) return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2); break; + + case J722S_WIZ_10G: + if (wiz->lane_phy_type[lane] == PHY_TYPE_PCIE) + return regmap_field_write(wiz->p0_fullrt_div[lane], 0x1); + if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII) + return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2); + break; + default: return 0; } @@ -1350,6 +1362,15 @@ static struct wiz_data am64_10g_data = { .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G, }; +static struct wiz_data j722s_10g_data = { + .type = J722S_WIZ_10G, + .pll0_refclk_mux_sel = &pll0_refclk_mux_sel, + .pll1_refclk_mux_sel = &pll1_refclk_mux_sel, + .refclk_dig_sel = &refclk_dig_sel_10g, + .clk_mux_sel = clk_mux_sel_10g, + .clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G, +}; + static struct wiz_data j7200_pg2_10g_data = { .type = J7200_WIZ_10G, .pll0_refclk_mux_sel = &sup_pll0_refclk_mux_sel, @@ -1389,6 +1410,9 @@ static const struct of_device_id wiz_id_table[] = { { .compatible = "ti,am64-wiz-10g", .data = &am64_10g_data, }, + { + .compatible = "ti,j722s-wiz-10g", .data = &j722s_10g_data, + }, { .compatible = "ti,j7200-wiz-10g", .data = &j7200_pg2_10g_data, }, From d39cf00e7daea64889dda9abb0b7e6da04a69d04 Mon Sep 17 00:00:00 2001 From: Nora Schiffer Date: Tue, 7 Apr 2026 13:42:36 +0200 Subject: [PATCH 0652/5214] phy: ti: gmii-sel: add support for J722S SoC family The J722S gmii-sel is mostly identical to the AM64's, but additionally supports SGMII. Signed-off-by: Nora Schiffer Link: https://patch.msgid.link/86488589fc055f61ee5341f9e268184f04febe71.1775559102.git.nora.schiffer@ew.tq-group.com Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-gmii-sel.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 6213c2b6005a..c2865a6b1d7f 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -251,6 +251,15 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = { .regfields = phy_gmii_sel_fields_am654, }; +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_j722s = { + .use_of_data = true, + .features = BIT(PHY_GMII_SEL_RGMII_ID_MODE) | + BIT(PHY_GMII_SEL_FIXED_TX_DELAY), + .regfields = phy_gmii_sel_fields_am654, + .extra_modes = BIT(PHY_INTERFACE_MODE_SGMII), +}; + static const struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = { .use_of_data = true, @@ -307,6 +316,10 @@ static const struct of_device_id phy_gmii_sel_id_table[] = { .compatible = "ti,am654-phy-gmii-sel", .data = &phy_gmii_sel_soc_am654, }, + { + .compatible = "ti,j722s-phy-gmii-sel", + .data = &phy_gmii_sel_soc_j722s, + }, { .compatible = "ti,j7200-cpsw5g-phy-gmii-sel", .data = &phy_gmii_sel_cpsw5g_soc_j7200, From ad8fdebd40fd25e86331886f4fc6951531691319 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 5 Mar 2026 01:00:51 +0000 Subject: [PATCH 0653/5214] dt-bindings: phy: spacemit: k3: add USB2 PHY support Introduce a compatible string for the USB2 PHY in SpacemiT K3 SoC. The IP of USB2 PHY mostly shares the same functionalities with K1 SoC, while has some register layout changes. Acked-by: Krzysztof Kozlowski Signed-off-by: Yixun Lan Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=c05cf9d274daf72dc7e433480cf2e0e888f6bd89 [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=00b4fe5be06aecd6426930de86b7cffc2330f4b8 [2] Link: https://patch.msgid.link/20260305-11-k3-usb2-phy-v4-1-15554fb933bc@kernel.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/spacemit,usb2-phy.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml index 43eaca90d88c..18025e5f60d6 100644 --- a/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/spacemit,usb2-phy.yaml @@ -4,14 +4,16 @@ $id: http://devicetree.org/schemas/phy/spacemit,usb2-phy.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: SpacemiT K1 SoC USB 2.0 PHY +title: SpacemiT K1/K3 SoC USB 2.0 PHY maintainers: - Ze Huang properties: compatible: - const: spacemit,k1-usb2-phy + enum: + - spacemit,k1-usb2-phy + - spacemit,k3-usb2-phy reg: maxItems: 1 From 056ee8b37bc91e3230afa11ec1018fa898b983b8 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 5 Mar 2026 01:00:52 +0000 Subject: [PATCH 0654/5214] phy: k1-usb: k3: add USB2 PHY support Add USB2 PHY support for SpacemiT K3 SoC. Register layout of handling USB disconnect operation has been changed, So introducing a platform data to distinguish the different SoCs. Reviewed-by: Yao Zi Signed-off-by: Yixun Lan Reviewed-by: Neil Armstrong Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=c05cf9d274daf72dc7e433480cf2e0e888f6bd89 [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=00b4fe5be06aecd6426930de86b7cffc2330f4b8 [2] Link: https://patch.msgid.link/20260305-11-k3-usb2-phy-v4-2-15554fb933bc@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/spacemit/phy-k1-usb2.c | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c index 9215d0b223b2..87b943d9111f 100644 --- a/drivers/phy/spacemit/phy-k1-usb2.c +++ b/drivers/phy/spacemit/phy-k1-usb2.c @@ -51,6 +51,9 @@ #define PHY_K1_HS_HOST_DISC 0x40 #define PHY_K1_HS_HOST_DISC_CLR BIT(0) +#define PHY_K3_HS_HOST_DISC 0x20 +#define PHY_K3_HS_HOST_DISC_CLR BIT(8) + #define PHY_PLL_DIV_CFG 0x98 #define PHY_FDIV_FRACT_8_15 GENMASK(7, 0) #define PHY_FDIV_FRACT_16_19 GENMASK(11, 8) @@ -145,7 +148,7 @@ static int spacemit_usb2phy_exit(struct phy *phy) return 0; } -static int spacemit_usb2phy_disconnect(struct phy *phy, int port) +static int spacemit_k1_usb2phy_disconnect(struct phy *phy, int port) { struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); @@ -155,10 +158,27 @@ static int spacemit_usb2phy_disconnect(struct phy *phy, int port) return 0; } -static const struct phy_ops spacemit_usb2phy_ops = { +static int spacemit_k3_usb2phy_disconnect(struct phy *phy, int port) +{ + struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); + + regmap_update_bits(sphy->regmap_base, PHY_K3_HS_HOST_DISC, + PHY_K3_HS_HOST_DISC_CLR, PHY_K3_HS_HOST_DISC_CLR); + + return 0; +} + +static const struct phy_ops spacemit_k1_usb2phy_ops = { .init = spacemit_usb2phy_init, .exit = spacemit_usb2phy_exit, - .disconnect = spacemit_usb2phy_disconnect, + .disconnect = spacemit_k1_usb2phy_disconnect, + .owner = THIS_MODULE, +}; + +static const struct phy_ops spacemit_k3_usb2phy_ops = { + .init = spacemit_usb2phy_init, + .exit = spacemit_usb2phy_exit, + .disconnect = spacemit_k3_usb2phy_disconnect, .owner = THIS_MODULE, }; @@ -167,12 +187,15 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct spacemit_usb2phy *sphy; + const struct phy_ops *ops; void __iomem *base; sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); if (!sphy) return -ENOMEM; + ops = device_get_match_data(dev); + sphy->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(sphy->clk)) return dev_err_probe(dev, PTR_ERR(sphy->clk), "Failed to get clock\n"); @@ -185,7 +208,7 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) if (IS_ERR(sphy->regmap_base)) return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n"); - sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops); + sphy->phy = devm_phy_create(dev, NULL, ops); if (IS_ERR(sphy->phy)) return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n"); @@ -196,7 +219,8 @@ static int spacemit_usb2phy_probe(struct platform_device *pdev) } static const struct of_device_id spacemit_usb2phy_dt_match[] = { - { .compatible = "spacemit,k1-usb2-phy", }, + { .compatible = "spacemit,k1-usb2-phy", .data = &spacemit_k1_usb2phy_ops }, + { .compatible = "spacemit,k3-usb2-phy", .data = &spacemit_k3_usb2phy_ops }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_usb2phy_dt_match); From c43207553867c150963308418a88dcd59bb2a3f0 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:05 +0200 Subject: [PATCH 0655/5214] media: iris: retrieve UBWC platform configuration Specifying UBWC data in each driver doesn't scale and is prone to errors. Request UBWC data from the central database in preparation to using it through the rest of the driver. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Kconfig | 1 + drivers/media/platform/qcom/iris/iris_core.h | 4 ++++ drivers/media/platform/qcom/iris/iris_probe.c | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index 3c803a05305a..39b06de6c3e6 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -5,6 +5,7 @@ config VIDEO_QCOM_IRIS select V4L2_MEM2MEM_DEV select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM + select QCOM_UBWC_CONFIG select VIDEOBUF2_DMA_CONTIG help This is a V4L2 driver for Qualcomm iris video accelerator diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index fb194c967ad4..d10a03aa5685 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -30,6 +30,8 @@ enum domain_type { DECODER = BIT(1), }; +struct qcom_ubwc_cfg_data; + /** * struct iris_core - holds core parameters valid for all instances * @@ -52,6 +54,7 @@ enum domain_type { * @resets: table of iris reset clocks * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data + * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory * @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory @@ -95,6 +98,7 @@ struct iris_core { struct reset_control_bulk_data *resets; struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; + const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; dma_addr_t sfr_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index ddaacda523ec..492f85f518eb 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "iris_core.h" #include "iris_ctrls.h" @@ -244,6 +245,10 @@ static int iris_probe(struct platform_device *pdev) core->iris_platform_data = of_device_get_match_data(core->dev); + core->ubwc_cfg = qcom_ubwc_config_get_data(); + if (IS_ERR(core->ubwc_cfg)) + return PTR_ERR(core->ubwc_cfg); + ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); if (ret) From 0c2bc74465c2298cbbcd0af0d0946b7b038c5e2d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:06 +0200 Subject: [PATCH 0656/5214] media: iris: don't specify min_acc_length in the source code The min_acc length can be calculated from the platform UBWC configuration. Use the freshly introduced helper and calculate min_acc length based on the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 6 +++++- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index d77fa29f44fc..aa4520b27739 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -3,6 +3,9 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include +#include + #include "iris_hfi_common.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_packet.h" @@ -120,6 +123,7 @@ static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_typ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr) { + const struct qcom_ubwc_cfg_data *ubwc = core->ubwc_cfg; u32 payload = 0; iris_hfi_gen2_create_header(hdr, 0, core->header_id++); @@ -146,7 +150,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->mal_length; + payload = qcom_ubwc_min_acc_length_64b(ubwc) ? 64 : 32; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAL_LENGTH, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5a489917580e..08a9529e599b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 mal_length; u32 highest_bank_bit; u32 bank_swzl_level; u32 bank_swz2_level; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 5da90d47f9c6..01c6ffa7e084 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .mal_length = 32, .highest_bank_bit = 16, .bank_swzl_level = 0, .bank_swz2_level = 1, From 68b41e1aaeb18b5e970a86f3c52264fdedbbc423 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:07 +0200 Subject: [PATCH 0657/5214] media: iris: don't specify highest_bank_bit in the source code The highest_bank_bit param is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index aa4520b27739..6dc0cbaa9c19 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -160,7 +160,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->highest_bank_bit; + payload = ubwc->highest_bank_bit; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_HBB, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 08a9529e599b..5639eb5a75b6 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 highest_bank_bit; u32 bank_swzl_level; u32 bank_swz2_level; u32 bank_swz3_level; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 01c6ffa7e084..bdeb92e0b7bc 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .highest_bank_bit = 16, .bank_swzl_level = 0, .bank_swz2_level = 1, .bank_swz3_level = 1, From 8f1a4226dfbdd665a5a9ee28f1e33343cc2b30d7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:08 +0200 Subject: [PATCH 0658/5214] media: iris: don't specify ubwc_swizzle in the source code The UBWC swizzle is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 6 +++--- drivers/media/platform/qcom/iris/iris_platform_common.h | 3 --- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 3 --- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index 6dc0cbaa9c19..a4d9efdbb43b 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -170,7 +170,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swzl_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL1); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL1, HFI_HOST_FLAGS_NONE, @@ -180,7 +180,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz2_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL2); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL2, HFI_HOST_FLAGS_NONE, @@ -190,7 +190,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz3_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL3); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL3, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5639eb5a75b6..e217f15ef028 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,9 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 bank_swzl_level; - u32 bank_swz2_level; - u32 bank_swz3_level; u32 bank_spreading; }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index bdeb92e0b7bc..8072f430bd26 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,9 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .bank_swzl_level = 0, - .bank_swz2_level = 1, - .bank_swz3_level = 1, .bank_spreading = 1, }; From 838dcd80db90b66e7d989b425c3c3574868f7ca4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:09 +0200 Subject: [PATCH 0659/5214] media: iris: don't specify bank_spreading in the source code The UBWC bank spreading is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index a4d9efdbb43b..a49394b92768 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -200,7 +200,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_spreading; + payload = qcom_ubwc_bank_spread(ubwc); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SPREADING, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e217f15ef028..07c58cf3a14a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 bank_spreading; }; struct platform_inst_caps { diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 8072f430bd26..4e617176dee4 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .bank_spreading = 1, }; static const struct tz_cp_config tz_cp_config_sm8550[] = { From 59d1b40ce3d76480cee6389a0015f1ca0f49c336 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:10 +0200 Subject: [PATCH 0660/5214] media: iris: don't specify max_channels in the source code The UBWC max_channels spreading is specified in the Iris driver, but it also can be calculated from the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index a49394b92768..0d05dd2afc07 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -140,7 +140,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->max_channels; + payload = qcom_ubwc_macrotile_mode(ubwc) ? 8 : 4; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAX_CHANNELS, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 07c58cf3a14a..e8b5446dce76 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -76,7 +76,6 @@ struct tz_cp_config { }; struct ubwc_config_data { - u32 max_channels; }; struct platform_inst_caps { diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 4e617176dee4..05b1dd11abce 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -791,7 +791,6 @@ static const char * const sm8550_opp_clk_table[] = { }; static struct ubwc_config_data ubwc_config_sm8550 = { - .max_channels = 8, }; static const struct tz_cp_config tz_cp_config_sm8550[] = { From 28d2ab064dfd7a349bbe14d211f3c95a4efde370 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:11 +0200 Subject: [PATCH 0661/5214] media: iris: drop remnants of UBWC configuration Now as all UBWC configuration bits were migrated to be used or derived from the global UBWC platform-specific data, drop the unused struct and field definitions. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_common.h | 4 ---- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 7 ------- 2 files changed, 11 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e8b5446dce76..f42e1798747c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -75,9 +75,6 @@ struct tz_cp_config { u32 cp_nonpixel_size; }; -struct ubwc_config_data { -}; - struct platform_inst_caps { u32 min_frame_width; u32 max_frame_width; @@ -241,7 +238,6 @@ struct iris_platform_data { u32 tz_cp_config_data_size; u32 core_arch; u32 hw_response_timeout; - struct ubwc_config_data *ubwc_config; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 05b1dd11abce..a526b50a1cd3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -790,9 +790,6 @@ static const char * const sm8550_opp_clk_table[] = { NULL, }; -static struct ubwc_config_data ubwc_config_sm8550 = { -}; - static const struct tz_cp_config tz_cp_config_sm8550[] = { { .cp_start = 0, @@ -949,7 +946,6 @@ const struct iris_platform_data sm8550_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1054,7 +1050,6 @@ const struct iris_platform_data sm8650_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1150,7 +1145,6 @@ const struct iris_platform_data sm8750_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1250,7 +1244,6 @@ const struct iris_platform_data qcs8300_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, From ab128251dace7f234f69a51bbe3d0b40ed9814c6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:16 +0200 Subject: [PATCH 0662/5214] media: dt-bindings: qcom,sm8250-venus: sort out power domains First of all, on SM8250 Iris (ex-Venus) core needs to scale clocks which are powered by the MMCX domain. Add MMCX domain to the list of the power domain to be used on this platform. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sm8250-venus.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml index da54493220c9..43a10d9f664e 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml @@ -22,7 +22,7 @@ properties: power-domains: minItems: 2 - maxItems: 3 + maxItems: 4 power-domain-names: minItems: 2 @@ -30,6 +30,7 @@ properties: - const: venus - const: vcodec0 - const: mx + - const: mmcx clocks: maxItems: 3 @@ -114,8 +115,12 @@ examples: interrupts = ; power-domains = <&videocc MVS0C_GDSC>, <&videocc MVS0_GDSC>, - <&rpmhpd RPMHPD_MX>; - power-domain-names = "venus", "vcodec0", "mx"; + <&rpmhpd RPMHPD_MX>, + <&rpmhpd RPMHPD_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mx", + "mmcx"; clocks = <&gcc GCC_VIDEO_AXI0_CLK>, <&videocc VIDEO_CC_MVS0C_CLK>, From 4170e2f25ce40b69f3827fcdabf395380095e136 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:17 +0200 Subject: [PATCH 0663/5214] media: iris: scale MMCX power domain on SM8250 On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 79865252acb6 ("media: iris: enable video driver probe of SM8250 SoC") Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 +- drivers/media/platform/qcom/iris/iris_probe.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index df8e6bf9430e..aa71f7f53ee3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -281,7 +281,7 @@ static const struct bw_info sm8250_bw_table_dec[] = { static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; -static const char * const sm8250_opp_pd_table[] = { "mx" }; +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; static const struct platform_clk_data sm8250_clk_table[] = { {IRIS_AXI_CLK, "iface" }, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 492f85f518eb..511c8b56cf5d 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -65,6 +65,13 @@ static int iris_init_power_domains(struct iris_core *core) return ret; ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) { + iris_opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, + &core->opp_pmdomain_tbl); + } if (ret < 0) return ret; From f45fd3dbb5f6d60a8b31d5cc4affaf1ef40163b3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:18 +0200 Subject: [PATCH 0664/5214] media: venus: scale MMCX power domain on SM8250 On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 0aeabfa29a9c ("media: venus: core: add sm8250 DT compatible and resource data") Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 7 ++++++- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/pm_helpers.c | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 7e639760c41d..00fb6806c129 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -882,6 +882,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -933,6 +934,7 @@ static const struct venus_resources sc7180_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -991,7 +993,8 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx" }, + .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, + .opp_pmdomain_num = 2, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -1053,6 +1056,7 @@ static const struct venus_resources sc7280_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -1100,6 +1104,7 @@ static const struct venus_resources qcm2290_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50_LITE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7506f5d0f609..70e7b40affa9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -83,6 +83,7 @@ struct venus_resources { const char **vcodec_pmdomains; unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; + unsigned int opp_pmdomain_num; unsigned int vcodec_num; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70..14a4e8311a64 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -887,7 +887,7 @@ static int vcodec_domains_get(struct venus_core *core) }; struct dev_pm_domain_attach_data opp_pd_data = { .pd_names = res->opp_pmdomain, - .num_pd_names = 1, + .num_pd_names = res->opp_pmdomain_num, .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; @@ -904,6 +904,12 @@ static int vcodec_domains_get(struct venus_core *core) /* Attach the power domain for setting performance state */ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) { + opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + } if (ret < 0) return ret; From ab4e6a16f97eb74c3ce6cef1b5acd07c08cf6629 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:53 +0200 Subject: [PATCH 0665/5214] media: dt-bindings: qcom,sc7280-venus: drop non-PAS support The only users of the non-PAS setup on SC7280 platform are the ChromeOS devices, which were cancelled before reaching end users. Iris, the alternative driver for the same hardware, does not support non-PAS setup. It is expected that in future both Venus and Iris devices will use different ABI for non-PAS (EL2) setup. In order to declare only the future-proof hardware description drop support for non-PAS setup from the SC7280 Venus schema (breaking almost non-existing SC7280 ChromeOS devices). The dropped iommus entry reflects the extra stream, which should not be treated in the same way as the main one (which doesn't match the usage described by the iommus definition). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sc7280-venus.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml index 413c5b4ee650..9725fcb761dc 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml @@ -43,8 +43,7 @@ properties: - const: vcodec_bus iommus: - minItems: 1 - maxItems: 2 + maxItems: 1 interconnects: maxItems: 2 @@ -120,12 +119,7 @@ examples: <&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>; interconnect-names = "cpu-cfg", "video-mem"; - iommus = <&apps_smmu 0x2180 0x20>, - <&apps_smmu 0x2184 0x20>; + iommus = <&apps_smmu 0x2180 0x20>; memory-region = <&video_mem>; - - video-firmware { - iommus = <&apps_smmu 0x21a2 0x0>; - }; }; From cc59dbb862ac3bb07f4b3f77a423569a9153fe7b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:54 +0200 Subject: [PATCH 0666/5214] media: dt-bindings: qcom-sc7180-venus: move video-firmware here As SC7180 is the only remaining user of the non-TZ / non-PAS setup which uses the video-firmware subnode, move its definition from the common schema to the SC7180-specific one. These properties do not accurately describe the hardware. Future platforms that are going to support non-TZ setup will use different semantics and different DT ABI (using the iommu-map property). Reviewed-by: Vikash Garodia Reviewed-by: Krzysztof Kozlowski Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sc7180-venus.yaml | 15 +++++++++++++++ .../bindings/media/qcom,venus-common.yaml | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml index bfd8b1ad4731..b21bed314848 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -91,6 +91,21 @@ properties: deprecated: true additionalProperties: false + video-firmware: + type: object + additionalProperties: false + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + required: - compatible - power-domain-names diff --git a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml index 3153d91f9d18..59a3fde846d2 100644 --- a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml +++ b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml @@ -47,21 +47,6 @@ properties: minItems: 1 maxItems: 4 - video-firmware: - type: object - additionalProperties: false - - description: | - Firmware subnode is needed when the platform does not - have TrustZone. - - properties: - iommus: - maxItems: 1 - - required: - - iommus - required: - reg - clocks From 8f100f5896e1ccec3802dafd0f719627733c8183 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:56 +0200 Subject: [PATCH 0667/5214] media: qcom: venus: flip the venus/iris switch With the Iris and Venus driver having more or less feature parity for "HFI 6xx" platforms and with Iris gaining support for SC7280, flip the switch. Use Iris by default for SM8250 and SC7280, the platforms which are supported by both drivers, and use Venus only if Iris is not compiled at all. Use IS_ENABLED to strip out the code and data structures which are used by the disabled platforms. Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov [bod: Moved two conditional compats inside of one ifdef for ci] [bod: Changed IS_V6(core) (0) to ((void)(core), 0) for ci] Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Makefile | 5 +---- drivers/media/platform/qcom/iris/iris_probe.c | 2 -- drivers/media/platform/qcom/venus/core.c | 6 +++++- drivers/media/platform/qcom/venus/core.h | 11 +++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 2abbd3aeb4af..2fde45f81727 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -10,6 +10,7 @@ qcom-iris-objs += iris_buffer.o \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ + iris_platform_gen1.o \ iris_platform_gen2.o \ iris_power.o \ iris_probe.o \ @@ -26,8 +27,4 @@ qcom-iris-objs += iris_buffer.o \ iris_vpu_buffer.o \ iris_vpu_common.o \ -ifeq ($(CONFIG_VIDEO_QCOM_VENUS),) -qcom-iris-objs += iris_platform_gen1.o -endif - obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 511c8b56cf5d..baa13cc5c209 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -364,7 +364,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,qcs8300-iris", .data = &qcs8300_data, }, -#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) { .compatible = "qcom,sc7280-venus", .data = &sc7280_data, @@ -373,7 +372,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8250-venus", .data = &sm8250_data, }, -#endif { .compatible = "qcom,sm8550-iris", .data = &sm8550_data, diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 00fb6806c129..a87e8afb23df 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -951,6 +951,7 @@ static const struct venus_resources sc7180_res = { .enc_nodename = "video-encoder", }; +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) static const struct freq_tbl sm8250_freq_table[] = { { 0, 444000000 }, { 0, 366000000 }, @@ -1073,6 +1074,7 @@ static const struct venus_resources sc7280_res = { .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; +#endif static const struct bw_tbl qcm2290_bw_table_dec[] = { { 352800, 597000, 0, 746000, 0 }, /* 1080p@30 + 720p@30 */ @@ -1130,11 +1132,13 @@ static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, - { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) + { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, }, +#endif { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 70e7b40affa9..03804c30808e 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -54,8 +54,10 @@ enum vpu_version { VPU_VERSION_AR50, VPU_VERSION_AR50_LITE, VPU_VERSION_IRIS1, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) VPU_VERSION_IRIS2, VPU_VERSION_IRIS2_1, +#endif }; struct firmware_version { @@ -526,13 +528,22 @@ struct venus_inst { #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) #define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX) #define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX) +#else +#define IS_V6(core) (((void)(core), 0)) +#endif #define IS_AR50(core) ((core)->res->vpu_version == VPU_VERSION_AR50) #define IS_AR50_LITE(core) ((core)->res->vpu_version == VPU_VERSION_AR50_LITE) #define IS_IRIS1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS1) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_IRIS2(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2) #define IS_IRIS2_1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2_1) +#else +#define IS_IRIS2(core) (((void)(core), 0)) +#define IS_IRIS2_1(core) (((void)(core), 0)) +#endif static inline bool is_lite(struct venus_core *core) { From 335cdc4c43a03c43b8a0073478e7dac4055aaa15 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:02 +0200 Subject: [PATCH 0668/5214] media: qcom: iris: drop pas_id from the iris_platform_data struct The PAS ID, the authentication service ID, used by the Iris is a constant and it is not expected to change anytime. Drop it from the platform data and use the constant instead. Reviewed-by: Dikshita Agarwal Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_firmware.c | 11 ++++++----- .../media/platform/qcom/iris/iris_platform_common.h | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 4 ---- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 5f408024e967..bc6c5c3e00c3 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -12,11 +12,12 @@ #include "iris_core.h" #include "iris_firmware.h" +#define IRIS_PAS_ID 9 + #define MAX_FIRMWARE_NAME_SIZE 128 static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) { - u32 pas_id = core->iris_platform_data->pas_id; const struct firmware *firmware = NULL; struct device *dev = core->dev; struct resource res; @@ -53,7 +54,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) } ret = qcom_mdt_load(dev, firmware, fw_name, - pas_id, mem_virt, mem_phys, res_size, NULL); + IRIS_PAS_ID, mem_virt, mem_phys, res_size, NULL); memunmap(mem_virt); err_release_fw: @@ -79,7 +80,7 @@ int iris_fw_load(struct iris_core *core) return -ENOMEM; } - ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id); + ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID); if (ret) { dev_err(core->dev, "auth and reset failed: %d\n", ret); return ret; @@ -93,7 +94,7 @@ int iris_fw_load(struct iris_core *core) cp_config->cp_nonpixel_size); if (ret) { dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret); - qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + qcom_scm_pas_shutdown(IRIS_PAS_ID); return ret; } } @@ -103,7 +104,7 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + return qcom_scm_pas_shutdown(IRIS_PAS_ID); } int iris_set_hw_state(struct iris_core *core, bool resume) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index f42e1798747c..e4eefc646c7f 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -12,7 +12,6 @@ struct iris_core; struct iris_inst; -#define IRIS_PAS_ID 9 #define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */ #define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */ @@ -226,7 +225,6 @@ struct iris_platform_data { unsigned int controller_rst_tbl_size; u64 dma_mask; const char *fwname; - u32 pas_id; struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index aa71f7f53ee3..07ed572e895b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -360,7 +360,6 @@ const struct iris_platform_data sm8250_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu-1.0/venus.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, @@ -413,7 +412,6 @@ const struct iris_platform_data sc7280_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu20_p1.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index a526b50a1cd3..1f23ddb972f0 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -934,7 +934,6 @@ const struct iris_platform_data sm8550_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1038,7 +1037,6 @@ const struct iris_platform_data sm8650_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu33_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1133,7 +1131,6 @@ const struct iris_platform_data sm8750_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu35_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1232,7 +1229,6 @@ const struct iris_platform_data qcs8300_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4_s6.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_qcs8300, From 8550eebfca38d3232fcb8c6641ea8543c78bee3c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:03 +0200 Subject: [PATCH 0669/5214] media: qcom: iris: use common set_preset_registers function The set_preset_registers is (currently) common to all supported devices. Extract it to a iris_vpu_common.c and call it directly from iris_vpu_power_on(). Later, if any of the devices requires special handling, it can be sorted out separately. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen1.c | 7 ------- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 9 --------- drivers/media/platform/qcom/iris/iris_vpu_common.c | 7 ++++++- drivers/media/platform/qcom/iris/iris_vpu_common.h | 2 ++ 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e4eefc646c7f..d7106902698c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -207,7 +207,6 @@ struct iris_platform_data { struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; - void (*set_preset_registers)(struct iris_core *core); const struct icc_info *icc_tbl; unsigned int icc_tbl_size; const struct bw_info *bw_tbl_dec; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 07ed572e895b..ed07d1b00e43 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -260,11 +260,6 @@ static struct platform_inst_caps platform_inst_cap_sm8250 = { .max_operating_rate = MAXIMUM_FPS, }; -static void iris_set_sm8250_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - static const struct icc_info sm8250_icc_table[] = { { "cpu-cfg", 1000, 1000 }, { "video-mem", 1000, 15000000 }, @@ -343,7 +338,6 @@ const struct iris_platform_data sm8250_data = { .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, .icc_tbl = sm8250_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), .clk_rst_tbl = sm8250_clk_reset_table, @@ -397,7 +391,6 @@ const struct iris_platform_data sc7280_data = { .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, .icc_tbl = sm8250_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), .bw_tbl_dec = sc7280_bw_table_dec, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 1f23ddb972f0..c84d4399f84d 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -756,11 +756,6 @@ static struct platform_inst_caps platform_inst_cap_sm8550 = { .max_operating_rate = MAXIMUM_FPS, }; -static void iris_set_sm8550_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - static const struct icc_info sm8550_icc_table[] = { { "cpu-cfg", 1000, 1000 }, { "video-mem", 1000, 15000000 }, @@ -917,7 +912,6 @@ const struct iris_platform_data sm8550_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8550_clk_reset_table, @@ -1018,7 +1012,6 @@ const struct iris_platform_data sm8650_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8650_clk_reset_table, @@ -1114,7 +1107,6 @@ const struct iris_platform_data sm8750_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8750_clk_reset_table, @@ -1212,7 +1204,6 @@ const struct iris_platform_data qcs8300_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8550_clk_reset_table, diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 548e5f1727fd..faabf53126f3 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -468,7 +468,7 @@ int iris_vpu_power_on(struct iris_core *core) iris_opp_set_rate(core->dev, freq); - core->iris_platform_data->set_preset_registers(core); + iris_vpu_set_preset_registers(core); iris_vpu_interrupt_init(core); core->intr_status = 0; @@ -485,3 +485,8 @@ int iris_vpu_power_on(struct iris_core *core) return ret; } + +void iris_vpu_set_preset_registers(struct iris_core *core) +{ + writel(0x0, core->reg_base + 0xb0088); +} diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index f6dffc613b82..07728c4c72b6 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -39,4 +39,6 @@ int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core); void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core); u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size); +void iris_vpu_set_preset_registers(struct iris_core *core); + #endif From c61331d2409b5501a4641adfee6597c1e79c34be Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:04 +0200 Subject: [PATCH 0670/5214] media: qcom: iris: don't use function indirection in gen2-specific code To note that iris_set_num_comv() is gen2-internal, rename it to iris_hfi_gen2_set_num_comv() and then stop using hfi_ops indirection to set session property (like other functions in this file do). Reviewed-by: Dikshita Agarwal Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_hfi_gen2_command.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 30bfd90d423b..e4f25b7f5d04 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1205,7 +1205,7 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type } } -static int iris_set_num_comv(struct iris_inst *inst) +static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst) { struct platform_inst_caps *caps; struct iris_core *core = inst->core; @@ -1220,12 +1220,12 @@ static int iris_set_num_comv(struct iris_inst *inst) num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? NUM_COMV_AV1 : caps->num_comv; - return core->hfi_ops->session_set_property(inst, - HFI_PROP_COMV_BUFFER_COUNT, - HFI_HOST_FLAGS_NONE, - HFI_PORT_BITSTREAM, - HFI_PAYLOAD_U32, - &num_comv, sizeof(u32)); + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_COMV_BUFFER_COUNT, + HFI_HOST_FLAGS_NONE, + HFI_PORT_BITSTREAM, + HFI_PAYLOAD_U32, + &num_comv, sizeof(u32)); } static void iris_hfi_gen2_get_buffer(u32 domain, struct iris_buffer *buffer, @@ -1257,7 +1257,7 @@ static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iri iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer); if (buffer->type == BUF_COMV) { - ret = iris_set_num_comv(inst); + ret = iris_hfi_gen2_set_num_comv(inst); if (ret) return ret; } From 35da0884068226ca3a53371dbf685db6e0d74658 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:05 +0200 Subject: [PATCH 0671/5214] media: qcom: iris: split HFI session ops from core ops Calling HFI instance-specific ops should not require double indirection through the core ops. Split instance-specific ops to a separate struct, keep a pointer to it in struct iris_inst and set it directly in the get_instance function. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 4 +- .../media/platform/qcom/iris/iris_common.c | 8 ++-- drivers/media/platform/qcom/iris/iris_ctrls.c | 46 +++++++++---------- .../platform/qcom/iris/iris_hfi_common.h | 3 ++ .../qcom/iris/iris_hfi_gen1_command.c | 23 +++++++--- .../qcom/iris/iris_hfi_gen2_command.c | 17 +++++-- .../media/platform/qcom/iris/iris_instance.h | 4 ++ drivers/media/platform/qcom/iris/iris_vb2.c | 2 +- drivers/media/platform/qcom/iris/iris_vdec.c | 6 +-- drivers/media/platform/qcom/iris/iris_venc.c | 4 +- drivers/media/platform/qcom/iris/iris_vidc.c | 2 +- 11 files changed, 72 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index 9151f43bc6b9..f55b7c608116 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -404,7 +404,7 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_queue_buf(inst, buf); @@ -572,7 +572,7 @@ int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane) static int iris_release_internal_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct iris_buffers *buffers = &inst->buffers[buffer_type]; struct iris_buffer *buffer, *next; int ret; diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c index 7f1c7fe144f7..25836561bcf3 100644 --- a/drivers/media/platform/qcom/iris/iris_common.c +++ b/drivers/media/platform/qcom/iris/iris_common.c @@ -48,7 +48,7 @@ void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_process_streamon_input(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state set_sub_state = 0; int ret; @@ -90,7 +90,7 @@ int iris_process_streamon_input(struct iris_inst *inst) int iris_process_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; bool drain_active, drc_active, first_ipsc; int ret = 0; @@ -189,7 +189,7 @@ static void iris_flush_deferred_buffers(struct iris_inst *inst, static void iris_kill_session(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; if (!inst->session_id) return; @@ -200,7 +200,7 @@ static void iris_kill_session(struct iris_inst *inst) int iris_session_streamoff(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_buffer_type buffer_type; int ret; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 3cec957580f5..5a24aa869b2d 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -399,7 +399,7 @@ static u32 iris_get_port_info(struct iris_inst *inst, int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -412,7 +412,7 @@ int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -425,7 +425,7 @@ int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct v4l2_format *inp_f = inst->fmt_src; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 height = inp_f->fmt.pix_mp.height; @@ -446,7 +446,7 @@ int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 work_route = inst->fw_caps[PIPE].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -459,7 +459,7 @@ int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -479,7 +479,7 @@ int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -499,7 +499,7 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; struct hfi_profile_level pl; @@ -520,7 +520,7 @@ int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_ca int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -539,7 +539,7 @@ int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 prepend_sps_pps = inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -561,7 +561,7 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; u32 bitrate = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -586,7 +586,7 @@ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 rc_mode = inst->fw_caps[BITRATE_MODE].value; u32 peak_bitrate = inst->fw_caps[cap_id].value; u32 bitrate = inst->fw_caps[BITRATE].value; @@ -613,7 +613,7 @@ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -640,7 +640,7 @@ int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -667,7 +667,7 @@ int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -687,7 +687,7 @@ int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 profile; @@ -712,7 +712,7 @@ int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 i_frame_qp = 0, p_frame_qp = 0, b_frame_qp = 0; u32 min_qp_enable = 0, client_qp_enable = 0; @@ -776,7 +776,7 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 max_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; @@ -841,7 +841,7 @@ int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -902,7 +902,7 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct hfi_quantization_range_v2 range; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -923,7 +923,7 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -953,7 +953,7 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val = HFI_DISABLE_FLIP; @@ -972,7 +972,7 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); u32 ir_period = inst->fw_caps[cap_id].value; u32 ir_type = 0; @@ -998,7 +998,7 @@ int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type ca int iris_set_properties(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct platform_inst_fw_cap *cap; int ret; u32 i; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 3edb5ae582b4..18684ada78b2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -110,6 +110,9 @@ struct iris_hfi_command_ops { int (*sys_image_version)(struct iris_core *core); int (*sys_interframe_powercollapse)(struct iris_core *core); int (*sys_pc_prep)(struct iris_core *core); +}; + +struct iris_hfi_session_ops { int (*session_set_config_params)(struct iris_inst *inst, u32 plane); int (*session_set_property)(struct iris_inst *inst, u32 packet_type, u32 flag, u32 plane, u32 payload_type, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index e42d17653c2c..a28b0c7ebbad 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1063,11 +1063,7 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p return 0; } -static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { - .sys_init = iris_hfi_gen1_sys_init, - .sys_image_version = iris_hfi_gen1_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_open = iris_hfi_gen1_session_open, .session_set_config_params = iris_hfi_gen1_session_set_config_params, .session_set_property = iris_hfi_gen1_session_set_property, @@ -1080,6 +1076,13 @@ static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { .session_close = iris_hfi_gen1_session_close, }; +static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { + .sys_init = iris_hfi_gen1_sys_init, + .sys_image_version = iris_hfi_gen1_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, +}; + void iris_hfi_gen1_command_ops_init(struct iris_core *core) { core->hfi_ops = &iris_hfi_gen1_command_ops; @@ -1087,5 +1090,13 @@ void iris_hfi_gen1_command_ops_init(struct iris_core *core) struct iris_inst *iris_hfi_gen1_get_instance(void) { - return kzalloc_obj(struct iris_inst); + struct iris_inst *out; + + out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->hfi_session_ops = &iris_hfi_gen1_session_ops; + + return out; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index e4f25b7f5d04..ffb70fd9499c 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1300,11 +1300,7 @@ static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct i inst_hfi_gen2->packet->size); } -static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { - .sys_init = iris_hfi_gen2_sys_init, - .sys_image_version = iris_hfi_gen2_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_open = iris_hfi_gen2_session_open, .session_set_config_params = iris_hfi_gen2_session_set_config_params, .session_set_property = iris_hfi_gen2_session_set_property, @@ -1319,6 +1315,13 @@ static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { .session_close = iris_hfi_gen2_session_close, }; +static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { + .sys_init = iris_hfi_gen2_sys_init, + .sys_image_version = iris_hfi_gen2_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, +}; + void iris_hfi_gen2_command_ops_init(struct iris_core *core) { core->hfi_ops = &iris_hfi_gen2_command_ops; @@ -1330,6 +1333,10 @@ struct iris_inst *iris_hfi_gen2_get_instance(void) /* The allocation is intentionally larger than struct iris_inst. */ out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->inst.hfi_session_ops = &iris_hfi_gen2_session_ops; return &out->inst; } diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 16965150f427..352af99699dd 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -15,6 +15,8 @@ #define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240 +struct iris_hfi_session_ops; + enum iris_fmt_type_out { IRIS_FMT_H264, IRIS_FMT_HEVC, @@ -38,6 +40,7 @@ struct iris_fmt { * @list: used for attach an instance to the core * @core: pointer to core structure * @session_id: id of current video session + * @hfi_session_ops: iris HFI session ops * @ctx_q_lock: lock to serialize queues related ioctls * @lock: lock to seralise forward and reverse threads * @fh: reference of v4l2 file handler @@ -80,6 +83,7 @@ struct iris_inst { struct list_head list; struct iris_core *core; u32 session_id; + const struct iris_hfi_session_ops *hfi_session_ops; struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */ struct mutex lock; /* lock to serialize forward and reverse threads */ struct v4l2_fh fh; diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index bf0b8400996e..a2ea2d67f60d 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -129,7 +129,7 @@ int iris_vb2_queue_setup(struct vb2_queue *q, if (!inst->once_per_session_set) { inst->once_per_session_set = true; - ret = core->hfi_ops->session_open(inst); + ret = inst->hfi_session_ops->session_open(inst); if (ret) { ret = -EINVAL; dev_err(core->dev, "session open failed\n"); diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 719217399a30..ccda3b9fb845 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -374,7 +374,7 @@ int iris_vdec_streamon_input(struct iris_inst *inst) int iris_vdec_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); @@ -434,7 +434,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_vdec_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -497,7 +497,7 @@ int iris_vdec_start_cmd(struct iris_inst *inst) int iris_vdec_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index aa27b22704eb..aeed756ee9ca 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -581,7 +581,7 @@ int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_venc_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -623,7 +623,7 @@ int iris_venc_start_cmd(struct iris_inst *inst) int iris_venc_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index bd38d84c9cc7..7e03d63578e1 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -224,7 +224,7 @@ int iris_open(struct file *filp) static void iris_session_close(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; bool wait_for_response = true; int ret; From d8a6a63372b86fa2152c633a384514585a9ce6ae Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:06 +0200 Subject: [PATCH 0672/5214] media: qcom: iris: merge hfi_response_ops and hfi_command_ops There is little point in having two different structures for HFI-related core ops. Merge both of them into the new iris_hfi_ops structure. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.h | 6 ++---- drivers/media/platform/qcom/iris/iris_hfi_common.c | 6 +++--- drivers/media/platform/qcom/iris/iris_hfi_common.h | 8 +++----- drivers/media/platform/qcom/iris/iris_hfi_gen1.h | 4 ++-- .../media/platform/qcom/iris/iris_hfi_gen1_command.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen1_response.c | 11 +---------- drivers/media/platform/qcom/iris/iris_hfi_gen2.h | 4 ++-- .../media/platform/qcom/iris/iris_hfi_gen2_command.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen2_response.c | 11 +---------- .../media/platform/qcom/iris/iris_platform_common.h | 3 +-- .../media/platform/qcom/iris/iris_platform_gen1.c | 6 ++---- .../media/platform/qcom/iris/iris_platform_gen2.c | 12 ++++-------- drivers/media/platform/qcom/iris/iris_probe.c | 3 +-- drivers/media/platform/qcom/iris/iris_vpu_common.c | 2 +- 14 files changed, 33 insertions(+), 59 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index d10a03aa5685..1592681640ab 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -68,8 +68,7 @@ struct qcom_ubwc_cfg_data; * @header_id: id of packet header * @packet_id: id of packet * @power: a structure for clock and bw information - * @hfi_ops: iris hfi command ops - * @hfi_response_ops: iris hfi response ops + * @hfi_sys_ops: iris HFI system ops * @core_init_done: structure of signal completion for system response * @intr_status: interrupt status * @sys_error_handler: a delayed work for handling system fatal error @@ -112,8 +111,7 @@ struct iris_core { u32 header_id; u32 packet_id; struct iris_core_power power; - const struct iris_hfi_command_ops *hfi_ops; - const struct iris_hfi_response_ops *hfi_response_ops; + const struct iris_hfi_sys_ops *hfi_sys_ops; struct completion core_init_done; u32 intr_status; struct delayed_work sys_error_handler; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c index 92112eb16c11..ad8e4ecb8605 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c @@ -76,7 +76,7 @@ u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients) int iris_hfi_core_init(struct iris_core *core) { - const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops; + const struct iris_hfi_sys_ops *hfi_ops = core->hfi_sys_ops; int ret; ret = hfi_ops->sys_init(core); @@ -109,7 +109,7 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data) iris_vpu_clear_interrupt(core); mutex_unlock(&core->lock); - core->hfi_response_ops->hfi_response_handler(core); + core->hfi_sys_ops->sys_hfi_response_handler(core); if (!iris_vpu_watchdog(core, core->intr_status)) enable_irq(irq); @@ -144,7 +144,7 @@ int iris_hfi_pm_suspend(struct iris_core *core) int iris_hfi_pm_resume(struct iris_core *core) { - const struct iris_hfi_command_ops *ops = core->hfi_ops; + const struct iris_hfi_sys_ops *ops = core->hfi_sys_ops; int ret; ret = iris_vpu_power_on(core); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 18684ada78b2..9aa84a1d8f95 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -105,11 +105,13 @@ struct iris_hfi_prop_type_handle { int (*handle)(struct iris_inst *inst, u32 plane); }; -struct iris_hfi_command_ops { +struct iris_hfi_sys_ops { int (*sys_init)(struct iris_core *core); int (*sys_image_version)(struct iris_core *core); int (*sys_interframe_powercollapse)(struct iris_core *core); int (*sys_pc_prep)(struct iris_core *core); + + void (*sys_hfi_response_handler)(struct iris_core *core); }; struct iris_hfi_session_ops { @@ -129,10 +131,6 @@ struct iris_hfi_session_ops { int (*session_close)(struct iris_inst *inst); }; -struct iris_hfi_response_ops { - void (*hfi_response_handler)(struct iris_core *core); -}; - struct hfi_subscription_params { u32 bitstream_resolution; u32 crop_offsets[2]; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h index 19b8e9054a75..38e9d262d7df 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h @@ -9,8 +9,8 @@ struct iris_core; struct iris_inst; -void iris_hfi_gen1_command_ops_init(struct iris_core *core); -void iris_hfi_gen1_response_ops_init(struct iris_core *core); +void iris_hfi_gen1_sys_ops_init(struct iris_core *core); +void iris_hfi_gen1_response_handler(struct iris_core *core); struct iris_inst *iris_hfi_gen1_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index a28b0c7ebbad..26b7feb05d15 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1076,16 +1076,18 @@ static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_close = iris_hfi_gen1_session_close, }; -static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { +static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { .sys_init = iris_hfi_gen1_sys_init, .sys_image_version = iris_hfi_gen1_sys_image_version, .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen1_response_handler, }; -void iris_hfi_gen1_command_ops_init(struct iris_core *core) +void iris_hfi_gen1_sys_ops_init(struct iris_core *core) { - core->hfi_ops = &iris_hfi_gen1_command_ops; + core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; } struct iris_inst *iris_hfi_gen1_get_instance(void) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c index 8e864c239e29..bfd7495bf44f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c @@ -688,7 +688,7 @@ static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen1_response_handler(struct iris_core *core) +void iris_hfi_gen1_response_handler(struct iris_core *core) { memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr)); while (!iris_hfi_queue_msg_read(core, core->response_packet)) { @@ -698,12 +698,3 @@ static void iris_hfi_gen1_response_handler(struct iris_core *core) iris_hfi_gen1_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = { - .hfi_response_handler = iris_hfi_gen1_response_handler, -}; - -void iris_hfi_gen1_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen1_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h index b9d3749a10ef..6cc6d9890c12 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h @@ -34,8 +34,8 @@ struct iris_inst_hfi_gen2 { struct hfi_subscription_params dst_subcr_params; }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core); -void iris_hfi_gen2_response_ops_init(struct iris_core *core); +void iris_hfi_gen2_sys_ops_init(struct iris_core *core); +void iris_hfi_gen2_response_handler(struct iris_core *core); struct iris_inst *iris_hfi_gen2_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index ffb70fd9499c..0c98d680bf09 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1315,16 +1315,18 @@ static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_close = iris_hfi_gen2_session_close, }; -static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { +static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { .sys_init = iris_hfi_gen2_sys_init, .sys_image_version = iris_hfi_gen2_sys_image_version, .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen2_response_handler, }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core) +void iris_hfi_gen2_sys_ops_init(struct iris_core *core) { - core->hfi_ops = &iris_hfi_gen2_command_ops; + core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; } struct iris_inst *iris_hfi_gen2_get_instance(void) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 8e19f61bbbf9..c350d231265e 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -977,7 +977,7 @@ static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen2_response_handler(struct iris_core *core) +void iris_hfi_gen2_response_handler(struct iris_core *core) { if (iris_vpu_watchdog(core, core->intr_status)) { struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT}; @@ -997,12 +997,3 @@ static void iris_hfi_gen2_response_handler(struct iris_core *core) iris_hfi_gen2_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = { - .hfi_response_handler = iris_hfi_gen2_response_handler, -}; - -void iris_hfi_gen2_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen2_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index d7106902698c..6b76a9046f9a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -202,8 +202,7 @@ enum platform_pm_domain_type { }; struct iris_platform_data { - void (*init_hfi_command_ops)(struct iris_core *core); - void (*init_hfi_response_ops)(struct iris_core *core); + void (*init_hfi_ops)(struct iris_core *core); struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index ed07d1b00e43..dc74da04771b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -334,8 +334,7 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { const struct iris_platform_data sm8250_data = { .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, + .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -387,8 +386,7 @@ const struct iris_platform_data sm8250_data = { const struct iris_platform_data sc7280_data = { .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, + .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index c84d4399f84d..19e99e1c2aff 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -908,8 +908,7 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { const struct iris_platform_data sm8550_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, @@ -1008,8 +1007,7 @@ const struct iris_platform_data sm8550_data = { */ const struct iris_platform_data sm8650_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, .icc_tbl = sm8550_icc_table, @@ -1103,8 +1101,7 @@ const struct iris_platform_data sm8650_data = { const struct iris_platform_data sm8750_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, .icc_tbl = sm8550_icc_table, @@ -1200,8 +1197,7 @@ const struct iris_platform_data sm8750_data = { */ const struct iris_platform_data qcs8300_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index baa13cc5c209..fa561f6a736c 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -264,8 +264,7 @@ static int iris_probe(struct platform_device *pdev) disable_irq_nosync(core->irq); iris_init_ops(core); - core->iris_platform_data->init_hfi_command_ops(core); - core->iris_platform_data->init_hfi_response_ops(core); + core->iris_platform_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index faabf53126f3..dbce5aeba06c 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -149,7 +149,7 @@ int iris_vpu_prepare_pc(struct iris_core *core) if (!wfi_status || !idle_status) goto skip_power_off; - ret = core->hfi_ops->sys_pc_prep(core); + ret = core->hfi_sys_ops->sys_pc_prep(core); if (ret) goto skip_power_off; From c8f12e754ee4dba209f86ba5e9156cfb69712711 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:07 +0200 Subject: [PATCH 0673/5214] media: qcom: iris: move get_instance to iris_hfi_sys_ops The get_instance() is a callback tightly connected to the HFI implementation. Move it into the new iris_hfi_sys_ops structure, merging all core callbacks into a single vtable. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_hfi_common.h | 2 ++ .../media/platform/qcom/iris/iris_hfi_gen1.h | 2 -- .../qcom/iris/iris_hfi_gen1_command.c | 32 ++++++++++--------- .../media/platform/qcom/iris/iris_hfi_gen2.h | 1 - .../qcom/iris/iris_hfi_gen2_command.c | 32 ++++++++++--------- .../platform/qcom/iris/iris_platform_common.h | 1 - .../platform/qcom/iris/iris_platform_gen1.c | 2 -- .../platform/qcom/iris/iris_platform_gen2.c | 4 --- drivers/media/platform/qcom/iris/iris_vidc.c | 2 +- 9 files changed, 37 insertions(+), 41 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 9aa84a1d8f95..a27447eb2519 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -112,6 +112,8 @@ struct iris_hfi_sys_ops { int (*sys_pc_prep)(struct iris_core *core); void (*sys_hfi_response_handler)(struct iris_core *core); + + struct iris_inst *(*sys_get_instance)(void); }; struct iris_hfi_session_ops { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h index 38e9d262d7df..c37adf65055a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h @@ -7,10 +7,8 @@ #define __IRIS_HFI_GEN1_H__ struct iris_core; -struct iris_inst; void iris_hfi_gen1_sys_ops_init(struct iris_core *core); void iris_hfi_gen1_response_handler(struct iris_core *core); -struct iris_inst *iris_hfi_gen1_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 26b7feb05d15..0017ade4adbd 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1076,21 +1076,7 @@ static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_close = iris_hfi_gen1_session_close, }; -static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { - .sys_init = iris_hfi_gen1_sys_init, - .sys_image_version = iris_hfi_gen1_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, - - .sys_hfi_response_handler = iris_hfi_gen1_response_handler, -}; - -void iris_hfi_gen1_sys_ops_init(struct iris_core *core) -{ - core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; -} - -struct iris_inst *iris_hfi_gen1_get_instance(void) +static struct iris_inst *iris_hfi_gen1_get_instance(void) { struct iris_inst *out; @@ -1102,3 +1088,19 @@ struct iris_inst *iris_hfi_gen1_get_instance(void) return out; } + +static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { + .sys_init = iris_hfi_gen1_sys_init, + .sys_image_version = iris_hfi_gen1_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen1_response_handler, + + .sys_get_instance = iris_hfi_gen1_get_instance, +}; + +void iris_hfi_gen1_sys_ops_init(struct iris_core *core) +{ + core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; +} diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h index 6cc6d9890c12..21ab58e0aa84 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h @@ -36,6 +36,5 @@ struct iris_inst_hfi_gen2 { void iris_hfi_gen2_sys_ops_init(struct iris_core *core); void iris_hfi_gen2_response_handler(struct iris_core *core); -struct iris_inst *iris_hfi_gen2_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 0c98d680bf09..639b75fca1ab 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1315,21 +1315,7 @@ static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_close = iris_hfi_gen2_session_close, }; -static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { - .sys_init = iris_hfi_gen2_sys_init, - .sys_image_version = iris_hfi_gen2_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, - - .sys_hfi_response_handler = iris_hfi_gen2_response_handler, -}; - -void iris_hfi_gen2_sys_ops_init(struct iris_core *core) -{ - core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; -} - -struct iris_inst *iris_hfi_gen2_get_instance(void) +static struct iris_inst *iris_hfi_gen2_get_instance(void) { struct iris_inst_hfi_gen2 *out; @@ -1342,3 +1328,19 @@ struct iris_inst *iris_hfi_gen2_get_instance(void) return &out->inst; } + +static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { + .sys_init = iris_hfi_gen2_sys_init, + .sys_image_version = iris_hfi_gen2_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen2_response_handler, + + .sys_get_instance = iris_hfi_gen2_get_instance, +}; + +void iris_hfi_gen2_sys_ops_init(struct iris_core *core) +{ + core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; +} diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6b76a9046f9a..d1daef2d874b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -203,7 +203,6 @@ enum platform_pm_domain_type { struct iris_platform_data { void (*init_hfi_ops)(struct iris_core *core); - struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index dc74da04771b..9925a893b404 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -333,7 +333,6 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { }; const struct iris_platform_data sm8250_data = { - .get_instance = iris_hfi_gen1_get_instance, .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, @@ -385,7 +384,6 @@ const struct iris_platform_data sm8250_data = { }; const struct iris_platform_data sc7280_data = { - .get_instance = iris_hfi_gen1_get_instance, .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 19e99e1c2aff..10a972f96cbe 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -907,7 +907,6 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { }; const struct iris_platform_data sm8550_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, @@ -1006,7 +1005,6 @@ const struct iris_platform_data sm8550_data = { * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, @@ -1100,7 +1098,6 @@ const struct iris_platform_data sm8650_data = { }; const struct iris_platform_data sm8750_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, @@ -1196,7 +1193,6 @@ const struct iris_platform_data sm8750_data = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 7e03d63578e1..ecd8a20fedbf 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -156,7 +156,7 @@ int iris_open(struct file *filp) pm_runtime_put_sync(core->dev); - inst = core->iris_platform_data->get_instance(); + inst = core->hfi_sys_ops->sys_get_instance(); if (!inst) return -ENOMEM; From 10175bca76e1497fd76fd662d33ab199f7160bb1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:08 +0200 Subject: [PATCH 0674/5214] media: qcom: iris: drop hw_response_timeout_val from platform data The HW response time is a constant between platforms. Remove it from the iris_platform_data structure and use it directly. Suggested-by: Vikash Garodia Reviewed-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.c | 3 +-- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 4 ---- drivers/media/platform/qcom/iris/iris_utils.c | 5 +---- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index 8406c48d635b..e6141012cd3d 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -28,14 +28,13 @@ void iris_core_deinit(struct iris_core *core) static int iris_wait_for_system_response(struct iris_core *core) { - u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; int ret; if (core->state == IRIS_CORE_ERROR) return -EIO; ret = wait_for_completion_timeout(&core->core_init_done, - msecs_to_jiffies(hw_response_timeout_val)); + msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); if (!ret) { core->state = IRIS_CORE_ERROR; return -ETIMEDOUT; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index d1daef2d874b..e8a219023aaa 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -232,7 +232,6 @@ struct iris_platform_data { const struct tz_cp_config *tz_cp_config_data; u32 tz_cp_config_data_size; u32 core_arch; - u32 hw_response_timeout; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 9925a893b404..6ed4c4ae4056 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -361,7 +361,6 @@ const struct iris_platform_data sm8250_data = { .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K, @@ -410,7 +409,6 @@ const struct iris_platform_data sc7280_data = { .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 1, .no_aon = true, .max_session_count = 16, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 10a972f96cbe..abe523db45c2 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -936,7 +936,6 @@ const struct iris_platform_data sm8550_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1036,7 +1035,6 @@ const struct iris_platform_data sm8650_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1127,7 +1125,6 @@ const struct iris_platform_data sm8750_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1222,7 +1219,6 @@ const struct iris_platform_data qcs8300_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index cfc5b576ec56..29b07d88507e 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -55,16 +55,13 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush) { - struct iris_core *core = inst->core; - u32 hw_response_timeout_val; struct completion *done; int ret; - hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; done = is_flush ? &inst->flush_completion : &inst->completion; mutex_unlock(&inst->lock); - ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val)); + ret = wait_for_completion_timeout(done, msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); mutex_lock(&inst->lock); if (!ret) { iris_inst_change_state(inst, IRIS_INST_ERROR); From 95faed0b9f516b693860865fc54c2a708946a96d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:09 +0200 Subject: [PATCH 0675/5214] media: qcom: iris: split firmware_data from raw platform data Having firmware-related fields in platform data results in the tying platform data to the HFI firmware data rather than the actual hardware. For example, SM8450 uses Gen2 firmware, so currently its platform data should be placed next to the other gen2 platforms, although it has the VPU2.0 core, similar to the one found on SM8250 and SC7280 and so the hardware-specific platform data is also close to those devices. Split firmware data to a separate struct, separating hardware-related data from the firmware interfaces. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 82 +++--- drivers/media/platform/qcom/iris/iris_core.h | 2 + drivers/media/platform/qcom/iris/iris_ctrls.c | 8 +- .../qcom/iris/iris_hfi_gen1_command.c | 8 +- .../qcom/iris/iris_hfi_gen2_command.c | 66 ++--- .../platform/qcom/iris/iris_platform_common.h | 91 ++++--- .../platform/qcom/iris/iris_platform_gen1.c | 67 ++--- .../platform/qcom/iris/iris_platform_gen2.c | 248 +++--------------- drivers/media/platform/qcom/iris/iris_probe.c | 3 +- drivers/media/platform/qcom/iris/iris_vidc.c | 10 +- .../platform/qcom/iris/iris_vpu_common.c | 2 +- 11 files changed, 211 insertions(+), 376 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index f55b7c608116..fbe136360aa1 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -301,31 +301,31 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, void iris_get_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } @@ -366,7 +366,7 @@ static int iris_create_internal_buffer(struct iris_inst *inst, int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; u32 internal_buffer_count, i, j; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -374,19 +374,19 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -442,7 +442,7 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buffer, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -451,19 +451,19 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -501,7 +501,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -510,19 +510,19 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - len = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + len = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - len = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + len = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - len = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + len = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - len = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + len = firmware_data->enc_op_int_buf_tbl_size; } } @@ -593,17 +593,17 @@ static int iris_release_internal_buffers(struct iris_inst *inst, static int iris_release_input_internal_buffers(struct iris_inst *inst) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; int ret; if (inst->domain == DECODER) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index 1592681640ab..e0ca245c8c63 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -54,6 +54,7 @@ struct qcom_ubwc_cfg_data; * @resets: table of iris reset clocks * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data + * @iris_firmware_data: a pointer to the firmware (or HFI) specific data * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory @@ -97,6 +98,7 @@ struct iris_core { struct reset_control_bulk_data *resets; struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; + const struct iris_firmware_data *iris_firmware_data; const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 5a24aa869b2d..ef7adac3764d 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -332,8 +332,8 @@ void iris_session_init_caps(struct iris_core *core) const struct platform_inst_fw_cap *caps; u32 i, num_cap, cap_id; - caps = core->iris_platform_data->inst_fw_caps_dec; - num_cap = core->iris_platform_data->inst_fw_caps_dec_size; + caps = core->iris_firmware_data->inst_fw_caps_dec; + num_cap = core->iris_firmware_data->inst_fw_caps_dec_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; @@ -360,8 +360,8 @@ void iris_session_init_caps(struct iris_core *core) } } - caps = core->iris_platform_data->inst_fw_caps_enc; - num_cap = core->iris_platform_data->inst_fw_caps_enc_size; + caps = core->iris_firmware_data->inst_fw_caps_enc; + num_cap = core->iris_firmware_data->inst_fw_caps_enc_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 0017ade4adbd..3fb90a466a64 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1033,8 +1033,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p }; if (inst->domain == DECODER) { - config_params = core->iris_platform_data->dec_input_config_params_default; - config_params_size = core->iris_platform_data->dec_input_config_params_default_size; + config_params = core->iris_firmware_data->dec_input_config_params_default; + config_params_size = core->iris_firmware_data->dec_input_config_params_default_size; if (V4L2_TYPE_IS_OUTPUT(plane)) { handler = vdec_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(vdec_prop_type_handle_inp_arr); @@ -1043,8 +1043,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p handler_size = ARRAY_SIZE(vdec_prop_type_handle_out_arr); } } else { - config_params = core->iris_platform_data->enc_input_config_params; - config_params_size = core->iris_platform_data->enc_input_config_params_size; + config_params = core->iris_firmware_data->enc_input_config_params; + config_params_size = core->iris_firmware_data->enc_input_config_params_size; handler = venc_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(venc_prop_type_handle_inp_arr); } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 639b75fca1ab..c90b22a75bc5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -601,7 +601,7 @@ static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane) static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *pdata = inst->core->iris_platform_data; + const struct iris_firmware_data *fdata = inst->core->iris_firmware_data; u32 config_params_size = 0, i, j; const u32 *config_params = NULL; int ret; @@ -630,31 +630,31 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { if (inst->codec == V4L2_PIX_FMT_H264) { - config_params = pdata->dec_input_config_params_default; - config_params_size = pdata->dec_input_config_params_default_size; + config_params = fdata->dec_input_config_params_default; + config_params_size = fdata->dec_input_config_params_default_size; } else if (inst->codec == V4L2_PIX_FMT_HEVC) { - config_params = pdata->dec_input_config_params_hevc; - config_params_size = pdata->dec_input_config_params_hevc_size; + config_params = fdata->dec_input_config_params_hevc; + config_params_size = fdata->dec_input_config_params_hevc_size; } else if (inst->codec == V4L2_PIX_FMT_VP9) { - config_params = pdata->dec_input_config_params_vp9; - config_params_size = pdata->dec_input_config_params_vp9_size; + config_params = fdata->dec_input_config_params_vp9; + config_params_size = fdata->dec_input_config_params_vp9_size; } else if (inst->codec == V4L2_PIX_FMT_AV1) { - config_params = pdata->dec_input_config_params_av1; - config_params_size = pdata->dec_input_config_params_av1_size; + config_params = fdata->dec_input_config_params_av1; + config_params_size = fdata->dec_input_config_params_av1_size; } else { return -EINVAL; } } else { - config_params = pdata->dec_output_config_params; - config_params_size = pdata->dec_output_config_params_size; + config_params = fdata->dec_output_config_params; + config_params_size = fdata->dec_output_config_params_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - config_params = pdata->enc_input_config_params; - config_params_size = pdata->enc_input_config_params_size; + config_params = fdata->enc_input_config_params; + config_params_size = fdata->enc_input_config_params_size; } else { - config_params = pdata->enc_output_config_params; - config_params_size = pdata->enc_output_config_params_size; + config_params = fdata->enc_output_config_params; + config_params_size = fdata->enc_output_config_params_size; } } @@ -849,24 +849,24 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan switch (inst->codec) { case V4L2_PIX_FMT_H264: - change_param = core->iris_platform_data->dec_input_config_params_default; + change_param = core->iris_firmware_data->dec_input_config_params_default; change_param_size = - core->iris_platform_data->dec_input_config_params_default_size; + core->iris_firmware_data->dec_input_config_params_default_size; break; case V4L2_PIX_FMT_HEVC: - change_param = core->iris_platform_data->dec_input_config_params_hevc; + change_param = core->iris_firmware_data->dec_input_config_params_hevc; change_param_size = - core->iris_platform_data->dec_input_config_params_hevc_size; + core->iris_firmware_data->dec_input_config_params_hevc_size; break; case V4L2_PIX_FMT_VP9: - change_param = core->iris_platform_data->dec_input_config_params_vp9; + change_param = core->iris_firmware_data->dec_input_config_params_vp9; change_param_size = - core->iris_platform_data->dec_input_config_params_vp9_size; + core->iris_firmware_data->dec_input_config_params_vp9_size; break; case V4L2_PIX_FMT_AV1: - change_param = core->iris_platform_data->dec_input_config_params_av1; + change_param = core->iris_firmware_data->dec_input_config_params_av1; change_param_size = - core->iris_platform_data->dec_input_config_params_av1_size; + core->iris_firmware_data->dec_input_config_params_av1_size; break; } @@ -996,29 +996,29 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) return 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - subscribe_prop_size = core->iris_platform_data->dec_input_prop_size; - subcribe_prop = core->iris_platform_data->dec_input_prop; + subscribe_prop_size = core->iris_firmware_data->dec_input_prop_size; + subcribe_prop = core->iris_firmware_data->dec_input_prop; } else { switch (inst->codec) { case V4L2_PIX_FMT_H264: - subcribe_prop = core->iris_platform_data->dec_output_prop_avc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_avc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_avc_size; + core->iris_firmware_data->dec_output_prop_avc_size; break; case V4L2_PIX_FMT_HEVC: - subcribe_prop = core->iris_platform_data->dec_output_prop_hevc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_hevc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_hevc_size; + core->iris_firmware_data->dec_output_prop_hevc_size; break; case V4L2_PIX_FMT_VP9: - subcribe_prop = core->iris_platform_data->dec_output_prop_vp9; + subcribe_prop = core->iris_firmware_data->dec_output_prop_vp9; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_vp9_size; + core->iris_firmware_data->dec_output_prop_vp9_size; break; case V4L2_PIX_FMT_AV1: - subcribe_prop = core->iris_platform_data->dec_output_prop_av1; + subcribe_prop = core->iris_firmware_data->dec_output_prop_av1; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_av1_size; + core->iris_firmware_data->dec_output_prop_av1_size; break; } } diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e8a219023aaa..5af6d9f49f01 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -201,9 +201,61 @@ enum platform_pm_domain_type { IRIS_APV_HW_POWER_DOMAIN, }; -struct iris_platform_data { +struct iris_firmware_data { void (*init_hfi_ops)(struct iris_core *core); + + u32 core_arch; + + const struct platform_inst_fw_cap *inst_fw_caps_dec; + u32 inst_fw_caps_dec_size; + const struct platform_inst_fw_cap *inst_fw_caps_enc; + u32 inst_fw_caps_enc_size; + + const u32 *dec_input_config_params_default; + unsigned int dec_input_config_params_default_size; + const u32 *dec_input_config_params_hevc; + unsigned int dec_input_config_params_hevc_size; + const u32 *dec_input_config_params_vp9; + unsigned int dec_input_config_params_vp9_size; + const u32 *dec_input_config_params_av1; + unsigned int dec_input_config_params_av1_size; + const u32 *dec_output_config_params; + unsigned int dec_output_config_params_size; + const u32 *enc_input_config_params; + unsigned int enc_input_config_params_size; + const u32 *enc_output_config_params; + unsigned int enc_output_config_params_size; + + const u32 *dec_input_prop; + unsigned int dec_input_prop_size; + const u32 *dec_output_prop_avc; + unsigned int dec_output_prop_avc_size; + const u32 *dec_output_prop_hevc; + unsigned int dec_output_prop_hevc_size; + const u32 *dec_output_prop_vp9; + unsigned int dec_output_prop_vp9_size; + const u32 *dec_output_prop_av1; + unsigned int dec_output_prop_av1_size; + + const u32 *dec_ip_int_buf_tbl; + unsigned int dec_ip_int_buf_tbl_size; + const u32 *dec_op_int_buf_tbl; + unsigned int dec_op_int_buf_tbl_size; + const u32 *enc_ip_int_buf_tbl; + unsigned int enc_ip_int_buf_tbl_size; + const u32 *enc_op_int_buf_tbl; + unsigned int enc_op_int_buf_tbl_size; +}; + +struct iris_platform_data { + /* + * XXX: remove firmware_data pointer and consider moving + * get_vpu_buffer_size pointer once we have platforms supporting both + * firmware kinds. + */ + const struct iris_firmware_data *firmware_data; u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); + const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; unsigned int icc_tbl_size; @@ -225,13 +277,8 @@ struct iris_platform_data { struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; - const struct platform_inst_fw_cap *inst_fw_caps_dec; - u32 inst_fw_caps_dec_size; - const struct platform_inst_fw_cap *inst_fw_caps_enc; - u32 inst_fw_caps_enc_size; const struct tz_cp_config *tz_cp_config_data; u32 tz_cp_config_data_size; - u32 core_arch; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; @@ -239,38 +286,6 @@ struct iris_platform_data { u32 max_core_mbpf; /* max number of macroblocks per second supported */ u32 max_core_mbps; - const u32 *dec_input_config_params_default; - unsigned int dec_input_config_params_default_size; - const u32 *dec_input_config_params_hevc; - unsigned int dec_input_config_params_hevc_size; - const u32 *dec_input_config_params_vp9; - unsigned int dec_input_config_params_vp9_size; - const u32 *dec_input_config_params_av1; - unsigned int dec_input_config_params_av1_size; - const u32 *dec_output_config_params; - unsigned int dec_output_config_params_size; - const u32 *enc_input_config_params; - unsigned int enc_input_config_params_size; - const u32 *enc_output_config_params; - unsigned int enc_output_config_params_size; - const u32 *dec_input_prop; - unsigned int dec_input_prop_size; - const u32 *dec_output_prop_avc; - unsigned int dec_output_prop_avc_size; - const u32 *dec_output_prop_hevc; - unsigned int dec_output_prop_hevc_size; - const u32 *dec_output_prop_vp9; - unsigned int dec_output_prop_vp9_size; - const u32 *dec_output_prop_av1; - unsigned int dec_output_prop_av1_size; - const u32 *dec_ip_int_buf_tbl; - unsigned int dec_ip_int_buf_tbl_size; - const u32 *dec_op_int_buf_tbl; - unsigned int dec_op_int_buf_tbl_size; - const u32 *enc_ip_int_buf_tbl; - unsigned int enc_ip_int_buf_tbl_size; - const u32 *enc_op_int_buf_tbl; - unsigned int enc_op_int_buf_tbl_size; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 6ed4c4ae4056..8875f90d487e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -332,8 +332,33 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8250_data = { +const struct iris_firmware_data iris_hfi_gen1_data = { .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, + + .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, + .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), + .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, + .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), + + .dec_input_config_params_default = + sm8250_vdec_input_config_param_default, + .dec_input_config_params_default_size = + ARRAY_SIZE(sm8250_vdec_input_config_param_default), + .enc_input_config_params = sm8250_venc_input_config_param, + .enc_input_config_params_size = + ARRAY_SIZE(sm8250_venc_input_config_param), + + .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, + .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), + .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, + .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), + + .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), +}; + +const struct iris_platform_data sm8250_data = { + .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -355,35 +380,16 @@ const struct iris_platform_data sm8250_data = { .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, - .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8250_vdec_input_config_param_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8250_vdec_input_config_param_default), - .enc_input_config_params = sm8250_venc_input_config_param, - .enc_input_config_params_size = - ARRAY_SIZE(sm8250_venc_input_config_param), - - .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; const struct iris_platform_data sc7280_data = { - .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, + .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -403,10 +409,6 @@ const struct iris_platform_data sc7280_data = { .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, - .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .num_vpp_pipe = 1, @@ -415,19 +417,4 @@ const struct iris_platform_data sc7280_data = { .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, /* max spec for SC7280 is 4096x2176@60fps */ .max_core_mbps = 4096 * 2176 / 256 * 60, - .dec_input_config_params_default = - sm8250_vdec_input_config_param_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8250_vdec_input_config_param_default), - .enc_input_config_params = sm8250_venc_input_config_param, - .enc_input_config_params_size = - ARRAY_SIZE(sm8250_venc_input_config_param), - - .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index abe523db45c2..05fbab276100 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -906,40 +906,16 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8550_data = { +const struct iris_firmware_data iris_hfi_gen2_data = { .init_hfi_ops = iris_hfi_gen2_sys_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, + + .core_arch = VIDEO_ARCH_LX, + .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, + .dec_input_config_params_default = sm8550_vdec_input_config_params_default, .dec_input_config_params_default_size = @@ -996,6 +972,37 @@ const struct iris_platform_data sm8550_data = { .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; +const struct iris_platform_data sm8550_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = sm8550_icc_table, + .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = sm8550_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), + .pmdomain_tbl = sm8550_pmdomain_table, + .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), + .opp_pd_tbl = sm8550_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4.mbn", + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + /* * Shares most of SM8550 data except: * - vpu_ops to iris_vpu33_ops @@ -1004,7 +1011,7 @@ const struct iris_platform_data sm8550_data = { * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, .icc_tbl = sm8550_icc_table, @@ -1028,75 +1035,16 @@ const struct iris_platform_data sm8650_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; const struct iris_platform_data sm8750_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, .icc_tbl = sm8550_icc_table, @@ -1118,71 +1066,12 @@ const struct iris_platform_data sm8750_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; /* @@ -1190,7 +1079,7 @@ const struct iris_platform_data sm8750_data = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, @@ -1212,69 +1101,10 @@ const struct iris_platform_data qcs8300_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_qcs8300, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, .max_core_mbps = (((3840 * 2176) / 256) * 120), - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index fa561f6a736c..dd87504c2e67 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -251,6 +251,7 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); + core->iris_firmware_data = core->iris_platform_data->firmware_data; core->ubwc_cfg = qcom_ubwc_config_get_data(); if (IS_ERR(core->ubwc_cfg)) @@ -264,7 +265,7 @@ static int iris_probe(struct platform_device *pdev) disable_irq_nosync(core->irq); iris_init_ops(core); - core->iris_platform_data->init_hfi_ops(core); + core->iris_firmware_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index ecd8a20fedbf..807c9a20b6ba 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -243,7 +243,7 @@ static void iris_session_close(struct iris_inst *inst) static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -251,11 +251,11 @@ static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 p u32 count = 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index dbce5aeba06c..c6cfc1d9fd9e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -63,7 +63,7 @@ static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core) writel(QTBL_ENABLE, core->reg_base + QTBL_INFO); if (core->sfr_daddr) { - value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch; + value = (u32)core->sfr_daddr + core->iris_firmware_data->core_arch; writel(value, core->reg_base + SFR_ADDR); } From 53a5e095636acbab817a7fb98a67ce76cac59fdf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:10 +0200 Subject: [PATCH 0676/5214] media: qcom: iris: split platform data from firmware data Finalize the logical separation of the software and hardware interface descriptions by moving hardware properties to the files specific to the particular VPU version. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Makefile | 6 +- .../{iris_platform_gen1.c => iris_hfi_gen1.c} | 134 ----------- .../{iris_platform_gen2.c => iris_hfi_gen2.c} | 214 ------------------ .../platform/qcom/iris/iris_platform_common.h | 3 + .../platform/qcom/iris/iris_platform_sm8250.h | 29 +++ .../platform/qcom/iris/iris_platform_sm8550.h | 31 +++ .../platform/qcom/iris/iris_platform_vpu2.c | 124 ++++++++++ .../platform/qcom/iris/iris_platform_vpu3x.c | 204 +++++++++++++++++ 8 files changed, 395 insertions(+), 350 deletions(-) rename drivers/media/platform/qcom/iris/{iris_platform_gen1.c => iris_hfi_gen1.c} (67%) rename drivers/media/platform/qcom/iris/{iris_platform_gen2.c => iris_hfi_gen2.c} (77%) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8250.h create mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8550.h create mode 100644 drivers/media/platform/qcom/iris/iris_platform_vpu2.c create mode 100644 drivers/media/platform/qcom/iris/iris_platform_vpu3x.c diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 2fde45f81727..48e415cbc439 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -4,14 +4,16 @@ qcom-iris-objs += iris_buffer.o \ iris_ctrls.o \ iris_firmware.o \ iris_hfi_common.o \ + iris_hfi_gen1.o \ iris_hfi_gen1_command.o \ iris_hfi_gen1_response.o \ + iris_hfi_gen2.o \ iris_hfi_gen2_command.o \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ - iris_platform_gen1.o \ - iris_platform_gen2.o \ + iris_platform_vpu2.o \ + iris_platform_vpu3x.o \ iris_power.o \ iris_probe.o \ iris_resources.o \ diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c similarity index 67% rename from drivers/media/platform/qcom/iris/iris_platform_gen1.c rename to drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 8875f90d487e..60f51a1ba941 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -3,38 +3,16 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_platform_common.h" -#include "iris_resources.h" #include "iris_hfi_gen1.h" #include "iris_hfi_gen1_defines.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" -#include "iris_instance.h" - -#include "iris_platform_sc7280.h" #define BITRATE_MIN 32000 #define BITRATE_MAX 160000000 -#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2) #define BITRATE_STEP 100 -static struct iris_fmt platform_fmts_sm8250_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { { .cap_id = PIPE, @@ -248,56 +226,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { }, }; -static struct platform_inst_caps platform_inst_cap_sm8250 = { - .min_frame_width = 128, - .max_frame_width = 8192, - .min_frame_height = 128, - .max_frame_height = 8192, - .max_mbpf = 138240, - .mb_cycles_vsp = 25, - .mb_cycles_vpp = 200, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static const struct icc_info sm8250_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8250_clk_reset_table[] = { "bus", "core" }; - -static const struct bw_info sm8250_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 2403000 }, - { ((4096 * 2160) / 256) * 30, 1224000 }, - { ((1920 * 1080) / 256) * 60, 812000 }, - { ((1920 * 1080) / 256) * 30, 416000 }, -}; - -static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; - -static const struct platform_clk_data sm8250_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8250_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static const struct tz_cp_config tz_cp_config_sm8250[] = { - { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, - }, -}; - static const u32 sm8250_vdec_input_config_param_default[] = { HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE, HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT, @@ -356,65 +284,3 @@ const struct iris_firmware_data iris_hfi_gen1_data = { .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; - -const struct iris_platform_data sm8250_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .clk_rst_tbl = sm8250_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table), - .bw_tbl_dec = sm8250_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sm8250_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), - .clk_tbl = sm8250_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), - .opp_clk_tbl = sm8250_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mbn", - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -const struct iris_platform_data sc7280_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .bw_tbl_dec = sc7280_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sc7280_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), - .clk_tbl = sc7280_clk_table, - .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), - .opp_clk_tbl = sc7280_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p1.mbn", - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .num_vpp_pipe = 1, - .no_aon = true, - .max_session_count = 16, - .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, - /* max spec for SC7280 is 4096x2176@60fps */ - .max_core_mbps = 4096 * 2176 / 256 * 60, -}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c similarity index 77% rename from drivers/media/platform/qcom/iris/iris_platform_gen2.c rename to drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 05fbab276100..ce8490d64854 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -4,40 +4,15 @@ * Copyright (c) 2025 Linaro Ltd */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_defines.h" #include "iris_platform_common.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" - -#include "iris_platform_qcs8300.h" -#include "iris_platform_sm8650.h" -#include "iris_platform_sm8750.h" #define VIDEO_ARCH_LX 1 #define BITRATE_MAX 245000000 -static struct iris_fmt platform_fmts_sm8550_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_AV1] = { - .pixfmt = V4L2_PIX_FMT_AV1, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, @@ -742,58 +717,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { }, }; -static struct platform_inst_caps platform_inst_cap_sm8550 = { - .min_frame_width = 96, - .max_frame_width = 8192, - .min_frame_height = 96, - .max_frame_height = 8192, - .max_mbpf = (8192 * 4352) / 256, - .mb_cycles_vpp = 200, - .mb_cycles_fw = 489583, - .mb_cycles_fw_vpp = 66234, - .num_comv = 0, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static const struct icc_info sm8550_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8550_clk_reset_table[] = { "bus" }; - -static const struct bw_info sm8550_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 1608000 }, - { ((4096 * 2160) / 256) * 30, 826000 }, - { ((1920 * 1080) / 256) * 60, 567000 }, - { ((1920 * 1080) / 256) * 30, 294000 }, -}; - -static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" }; - -static const struct platform_clk_data sm8550_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8550_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static const struct tz_cp_config tz_cp_config_sm8550[] = { - { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, - }, -}; - static const u32 sm8550_vdec_input_config_params_default[] = { HFI_PROP_BITSTREAM_RESOLUTION, HFI_PROP_CROP_OFFSETS, @@ -971,140 +894,3 @@ const struct iris_firmware_data iris_hfi_gen2_data = { .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; - -const struct iris_platform_data sm8550_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -/* - * Shares most of SM8550 data except: - * - vpu_ops to iris_vpu33_ops - * - clk_rst_tbl to sm8650_clk_reset_table - * - controller_rst_tbl to sm8650_controller_reset_table - * - fwname to "qcom/vpu/vpu33_p4.mbn" - */ -const struct iris_platform_data sm8650_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu33_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8650_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), - .controller_rst_tbl = sm8650_controller_reset_table, - .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu33_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -const struct iris_platform_data sm8750_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu35_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8750_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8750_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu35_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -/* - * Shares most of SM8550 data except: - * - inst_caps to platform_inst_cap_qcs8300 - */ -const struct iris_platform_data qcs8300_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4_s6.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_qcs8300, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 2, - .max_session_count = 16, - .max_core_mbpf = ((4096 * 2176) / 256) * 4, - .max_core_mbps = (((3840 * 2176) / 256) * 120), -}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5af6d9f49f01..6dfead673393 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -40,6 +40,9 @@ enum pipe_type { PIPE_4 = 4, }; +extern const struct iris_firmware_data iris_hfi_gen1_data; +extern const struct iris_firmware_data iris_hfi_gen2_data; + extern const struct iris_platform_data qcs8300_data; extern const struct iris_platform_data sc7280_data; extern const struct iris_platform_data sm8250_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.h b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h new file mode 100644 index 000000000000..50306043eb8e --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_SM8250_H__ +#define __IRIS_PLATFORM_SM8250_H__ + +static const struct bw_info sm8250_bw_table_dec[] = { + { ((4096 * 2160) / 256) * 60, 2403000 }, + { ((4096 * 2160) / 256) * 30, 1224000 }, + { ((1920 * 1080) / 256) * 60, 812000 }, + { ((1920 * 1080) / 256) * 30, 416000 }, +}; + +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; + +static const struct platform_clk_data sm8250_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static const char * const sm8250_opp_clk_table[] = { + "vcodec0_core", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h new file mode 100644 index 000000000000..a9d9709c2e35 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __IRIS_PLATFORM_SM8550_H__ +#define __IRIS_PLATFORM_SM8550_H__ + +static const char * const sm8550_clk_reset_table[] = { "bus" }; + +static const struct platform_clk_data sm8550_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static struct platform_inst_caps platform_inst_cap_sm8550 = { + .min_frame_width = 96, + .max_frame_width = 8192, + .min_frame_height = 96, + .max_frame_height = 8192, + .max_mbpf = (8192 * 4352) / 256, + .mb_cycles_vpp = 200, + .mb_cycles_fw = 489583, + .mb_cycles_fw_vpp = 66234, + .num_comv = 0, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c new file mode 100644 index 000000000000..ab2a19aa9c36 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_platform_common.h" +#include "iris_resources.h" +#include "iris_hfi_gen1.h" +#include "iris_hfi_gen1_defines.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" +#include "iris_instance.h" + +#include "iris_platform_sc7280.h" +#include "iris_platform_sm8250.h" + +static struct iris_fmt iris_fmts_vpu2_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static struct platform_inst_caps platform_inst_cap_vpu2 = { + .min_frame_width = 128, + .max_frame_width = 8192, + .min_frame_height = 128, + .max_frame_height = 8192, + .max_mbpf = 138240, + .mb_cycles_vsp = 25, + .mb_cycles_vpp = 200, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +static const struct icc_info iris_icc_info_vpu2[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const char * const iris_clk_reset_table_vpu2[] = { "bus", "core" }; + +static const char * const iris_pmdomain_table_vpu2[] = { "venus", "vcodec0" }; + +static const struct tz_cp_config tz_cp_config_vpu2[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +const struct iris_platform_data sc7280_data = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .bw_tbl_dec = sc7280_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sc7280_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), + .clk_tbl = sc7280_clk_table, + .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), + .opp_clk_tbl = sc7280_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu20_p1.mbn", + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 1, + .no_aon = true, + .max_session_count = 16, + .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, + /* max spec for SC7280 is 4096x2176@60fps */ + .max_core_mbps = 4096 * 2176 / 256 * 60, +}; + +const struct iris_platform_data sm8250_data = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .clk_rst_tbl = iris_clk_reset_table_vpu2, + .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sm8250_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), + .clk_tbl = sm8250_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), + .opp_clk_tbl = sm8250_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu-1.0/venus.mbn", + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c new file mode 100644 index 000000000000..c2496aa0f851 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Linaro Ltd + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_hfi_gen2.h" +#include "iris_hfi_gen2_defines.h" +#include "iris_platform_common.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" + +#include "iris_platform_qcs8300.h" +#include "iris_platform_sm8550.h" +#include "iris_platform_sm8650.h" +#include "iris_platform_sm8750.h" + +static struct iris_fmt iris_fmts_vpu3x_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_AV1] = { + .pixfmt = V4L2_PIX_FMT_AV1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static const struct icc_info iris_icc_info_vpu3x[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const struct bw_info iris_bw_table_dec_vpu3x[] = { + { ((4096 * 2160) / 256) * 60, 1608000 }, + { ((4096 * 2160) / 256) * 30, 826000 }, + { ((1920 * 1080) / 256) * 60, 567000 }, + { ((1920 * 1080) / 256) * 30, 294000 }, +}; + +static const char * const iris_pmdomain_table_vpu3x[] = { "venus", "vcodec0" }; + +static const char * const iris_opp_pd_table_vpu3x[] = { "mxc", "mmcx" }; + +static const char * const iris_opp_clk_table_vpu3x[] = { + "vcodec0_core", + NULL, +}; + +static const struct tz_cp_config tz_cp_config_vpu3[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +/* + * Shares most of SM8550 data except: + * - inst_caps to platform_inst_cap_qcs8300 + */ +const struct iris_platform_data qcs8300_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_qcs8300, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 2, + .max_session_count = 16, + .max_core_mbpf = ((4096 * 2176) / 256) * 4, + .max_core_mbps = (((3840 * 2176) / 256) * 120), +}; + +const struct iris_platform_data sm8550_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +/* + * Shares most of SM8550 data except: + * - vpu_ops to iris_vpu33_ops + * - clk_rst_tbl to sm8650_clk_reset_table + * - controller_rst_tbl to sm8650_controller_reset_table + * - fwname to "qcom/vpu/vpu33_p4.mbn" + */ +const struct iris_platform_data sm8650_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .vpu_ops = &iris_vpu33_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8650_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), + .controller_rst_tbl = sm8650_controller_reset_table, + .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu33_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +const struct iris_platform_data sm8750_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .vpu_ops = &iris_vpu35_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8750_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8750_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu35_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; From 96360a894197c54066b7e79de4b91c37dba39547 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:11 +0200 Subject: [PATCH 0677/5214] media: qcom: iris: use new firmware name for SM8250 The linux-firmware is providing the vpuNN_pM.mbn firmware for SM8250 since August of 2024. Stop using the legacy firmware name (vpu-1.0/venus.mbn) and switch to the standard firmware name schema (vpu/vpu20_p4.mbn). Reviewed-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_vpu2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index ab2a19aa9c36..692fbc2aab56 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -111,7 +111,7 @@ const struct iris_platform_data sm8250_data = { .opp_clk_tbl = sm8250_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mbn", + .fwname = "qcom/vpu/vpu20_p4.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, From 2c7fe9db118e29f541a6811594b4316a287b3512 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:12 +0200 Subject: [PATCH 0678/5214] media: qcom: iris: extract firmware description data In preparation to adding support for several firmware revisions to be used for a platform, extract the firmware description data. It incorporates firmware name, HFI ops and buffer requirements of the particular firmware build. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov [bod: Made struct iris_firmware_desc into static consts to pass media CI] Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 2 +- drivers/media/platform/qcom/iris/iris_core.h | 2 + .../media/platform/qcom/iris/iris_firmware.c | 2 +- .../qcom/iris/iris_hfi_gen1_command.c | 2 +- .../platform/qcom/iris/iris_platform_common.h | 17 ++++---- .../platform/qcom/iris/iris_platform_vpu2.c | 20 ++++++--- .../platform/qcom/iris/iris_platform_vpu3x.c | 41 +++++++++++++------ drivers/media/platform/qcom/iris/iris_probe.c | 3 +- 8 files changed, 59 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index fbe136360aa1..ef7f6f931557 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -295,7 +295,7 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, { struct iris_buffers *buffers = &inst->buffers[buffer_type]; - buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type); + buffers->size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, buffer_type); buffers->min_count = iris_vpu_buf_count(inst, buffer_type); } diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index e0ca245c8c63..24da60448cf2 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -55,6 +55,7 @@ struct qcom_ubwc_cfg_data; * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data * @iris_firmware_data: a pointer to the firmware (or HFI) specific data + * @iris_firmware_desc: a pointer to the firmware-specific descriptive data * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory @@ -99,6 +100,7 @@ struct iris_core { struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; const struct iris_firmware_data *iris_firmware_data; + const struct iris_firmware_desc *iris_firmware_desc; const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index bc6c5c3e00c3..1a476146d758 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -72,7 +72,7 @@ int iris_fw_load(struct iris_core *core) ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0, &fwpath); if (ret) - fwpath = core->iris_platform_data->fwname; + fwpath = core->iris_firmware_desc->fwname; ret = iris_load_fw_to_memory(core, fwpath); if (ret) { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 3fb90a466a64..83373862655f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -918,7 +918,7 @@ static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst, u32 plane) if (iris_split_mode_enabled(inst)) { bufsz.type = HFI_BUFFER_OUTPUT; - bufsz.size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, BUF_DPB); + bufsz.size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, BUF_DPB); ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz)); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6dfead673393..6a108173be35 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -250,14 +250,18 @@ struct iris_firmware_data { unsigned int enc_op_int_buf_tbl_size; }; -struct iris_platform_data { - /* - * XXX: remove firmware_data pointer and consider moving - * get_vpu_buffer_size pointer once we have platforms supporting both - * firmware kinds. - */ +struct iris_firmware_desc { const struct iris_firmware_data *firmware_data; u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); + const char *fwname; +}; + +struct iris_platform_data { + /* + * XXX: replace with gen1 / gen2 pointers once we have platforms + * supporting both firmware kinds. + */ + const struct iris_firmware_desc *firmware_desc; const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; @@ -276,7 +280,6 @@ struct iris_platform_data { const char * const *controller_rst_tbl; unsigned int controller_rst_tbl_size; u64 dma_mask; - const char *fwname; struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 692fbc2aab56..41986af8313b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -16,6 +16,18 @@ #include "iris_platform_sc7280.h" #include "iris_platform_sm8250.h" +static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p1.mbn", +}; + +static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p4.mbn", +}; + static struct iris_fmt iris_fmts_vpu2_dec[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, @@ -62,8 +74,7 @@ static const struct tz_cp_config tz_cp_config_vpu2[] = { }; const struct iris_platform_data sc7280_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu20_p1_gen1_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), @@ -78,7 +89,6 @@ const struct iris_platform_data sc7280_data = { .opp_clk_tbl = sc7280_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p1.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, @@ -93,8 +103,7 @@ const struct iris_platform_data sc7280_data = { }; const struct iris_platform_data sm8250_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu20_p4_gen1_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), @@ -111,7 +120,6 @@ const struct iris_platform_data sm8250_data = { .opp_clk_tbl = sm8250_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p4.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index c2496aa0f851..c249ff827541 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -17,6 +17,30 @@ #include "iris_platform_sm8650.h" #include "iris_platform_sm8750.h" +static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", +}; + +static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu33_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu35_p4.mbn", +}; + static struct iris_fmt iris_fmts_vpu3x_dec[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, @@ -71,8 +95,7 @@ static const struct tz_cp_config tz_cp_config_vpu3[] = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu30_p4_s6_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -89,7 +112,6 @@ const struct iris_platform_data qcs8300_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4_s6.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_qcs8300, @@ -102,8 +124,7 @@ const struct iris_platform_data qcs8300_data = { }; const struct iris_platform_data sm8550_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu30_p4_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -120,7 +141,6 @@ const struct iris_platform_data sm8550_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -137,11 +157,9 @@ const struct iris_platform_data sm8550_data = { * - vpu_ops to iris_vpu33_ops * - clk_rst_tbl to sm8650_clk_reset_table * - controller_rst_tbl to sm8650_controller_reset_table - * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, + .firmware_desc = &iris_vpu33_p4_gen2_desc, .vpu_ops = &iris_vpu33_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -160,7 +178,6 @@ const struct iris_platform_data sm8650_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu33_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -173,8 +190,7 @@ const struct iris_platform_data sm8650_data = { }; const struct iris_platform_data sm8750_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, + .firmware_desc = &iris_vpu35_p4_gen2_desc, .vpu_ops = &iris_vpu35_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -191,7 +207,6 @@ const struct iris_platform_data sm8750_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu35_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index dd87504c2e67..d36f0c0e785b 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -251,7 +251,8 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); - core->iris_firmware_data = core->iris_platform_data->firmware_data; + core->iris_firmware_desc = core->iris_platform_data->firmware_desc; + core->iris_firmware_data = core->iris_firmware_desc->firmware_data; core->ubwc_cfg = qcom_ubwc_config_get_data(); if (IS_ERR(core->ubwc_cfg)) From 271560880aaff108546c2d7a83b0ea93a96d266e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 19:32:37 +0200 Subject: [PATCH 0679/5214] interconnect: qcom: Fix indentation KConfig entries should be indented starting with one tab, so replace spaces with it. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260428-interconnect-qcom-clean-arm64-v1-1-e6bc3f7832db@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 786b4eda44b4..871663bfd094 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -9,22 +9,22 @@ config INTERCONNECT_QCOM_BCM_VOTER tristate config INTERCONNECT_QCOM_ELIZA - tristate "Qualcomm Eliza interconnect driver" - depends on INTERCONNECT_QCOM_RPMH_POSSIBLE - select INTERCONNECT_QCOM_RPMH - select INTERCONNECT_QCOM_BCM_VOTER - help - This is a driver for the Qualcomm Network-on-Chip on Eliza-based - platforms. + tristate "Qualcomm Eliza interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on Eliza-based + platforms. config INTERCONNECT_QCOM_GLYMUR - tristate "Qualcomm Glymur interconnect driver" - depends on INTERCONNECT_QCOM_RPMH_POSSIBLE - select INTERCONNECT_QCOM_RPMH - select INTERCONNECT_QCOM_BCM_VOTER - help - This is a driver for the Qualcomm Network-on-Chip on glymur-based - platforms. + tristate "Qualcomm Glymur interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on glymur-based + platforms. config INTERCONNECT_QCOM_KAANAPALI tristate "Qualcomm Kaanapali interconnect driver" From 5b696f065843e5553414d3040b3f8f6880468f51 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 19:32:38 +0200 Subject: [PATCH 0680/5214] interconnect: qcom: Restrict drivers per ARM/ARM64 There is no point to allow selecting core SoC drivers like interconnects for Qualcomm ARMv7 SoCs when building ARM64 kernel, and vice versa. This makes kernel configuration more difficult as many do not remember the Qualcomm SoCs model names/numbers and their properties like architecture. No features should be lost because: 1. There won't be a single image for ARMv7 and ARMv8/9 SoCs. 2. Newer ARMv8/9 SoCs won't be running in arm32 emulation mode. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260428-interconnect-qcom-clean-arm64-v1-2-e6bc3f7832db@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 871663bfd094..b2c4272ae48f 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -11,6 +11,7 @@ config INTERCONNECT_QCOM_BCM_VOTER config INTERCONNECT_QCOM_ELIZA tristate "Qualcomm Eliza interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -20,6 +21,7 @@ config INTERCONNECT_QCOM_ELIZA config INTERCONNECT_QCOM_GLYMUR tristate "Qualcomm Glymur interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -29,6 +31,7 @@ config INTERCONNECT_QCOM_GLYMUR config INTERCONNECT_QCOM_KAANAPALI tristate "Qualcomm Kaanapali interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -39,6 +42,7 @@ config INTERCONNECT_QCOM_MSM8909 tristate "Qualcomm MSM8909 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on msm8909-based @@ -75,6 +79,7 @@ config INTERCONNECT_QCOM_MSM8953 tristate "Qualcomm MSM8953 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on msm8953-based @@ -84,6 +89,7 @@ config INTERCONNECT_QCOM_MSM8974 tristate "Qualcomm MSM8974 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on msm8974-based @@ -93,6 +99,7 @@ config INTERCONNECT_QCOM_MSM8976 tristate "Qualcomm MSM8976 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on msm8976-based @@ -102,6 +109,7 @@ config INTERCONNECT_QCOM_MSM8996 tristate "Qualcomm MSM8996 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on msm8996-based @@ -110,6 +118,7 @@ config INTERCONNECT_QCOM_MSM8996 config INTERCONNECT_QCOM_OSM_L3 tristate "Qualcomm OSM L3 interconnect driver" depends on INTERCONNECT_QCOM || COMPILE_TEST + depends on ARM64 || COMPILE_TEST help Say y here to support the Operating State Manager (OSM) interconnect driver which controls the scaling of L3 caches on Qualcomm SoCs. @@ -118,6 +127,7 @@ config INTERCONNECT_QCOM_QCM2290 tristate "Qualcomm QCM2290 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on qcm2290-based @@ -127,6 +137,7 @@ config INTERCONNECT_QCOM_QCS404 tristate "Qualcomm QCS404 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on qcs404-based @@ -135,6 +146,7 @@ config INTERCONNECT_QCOM_QCS404 config INTERCONNECT_QCOM_QCS615 tristate "Qualcomm QCS615 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -144,6 +156,7 @@ config INTERCONNECT_QCOM_QCS615 config INTERCONNECT_QCOM_QCS8300 tristate "Qualcomm QCS8300 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -155,6 +168,7 @@ config INTERCONNECT_QCOM_QCS8300 config INTERCONNECT_QCOM_QDU1000 tristate "Qualcomm QDU1000/QRU1000 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -178,6 +192,7 @@ config INTERCONNECT_QCOM_RPMH config INTERCONNECT_QCOM_SA8775P tristate "Qualcomm SA8775P interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -187,6 +202,7 @@ config INTERCONNECT_QCOM_SA8775P config INTERCONNECT_QCOM_SAR2130P tristate "Qualcomm SAR2130P interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -196,6 +212,7 @@ config INTERCONNECT_QCOM_SAR2130P config INTERCONNECT_QCOM_SC7180 tristate "Qualcomm SC7180 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -205,6 +222,7 @@ config INTERCONNECT_QCOM_SC7180 config INTERCONNECT_QCOM_SC7280 tristate "Qualcomm SC7280 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -214,6 +232,7 @@ config INTERCONNECT_QCOM_SC7280 config INTERCONNECT_QCOM_SC8180X tristate "Qualcomm SC8180X interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -223,6 +242,7 @@ config INTERCONNECT_QCOM_SC8180X config INTERCONNECT_QCOM_SC8280XP tristate "Qualcomm SC8280XP interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -232,6 +252,7 @@ config INTERCONNECT_QCOM_SC8280XP config INTERCONNECT_QCOM_SDM660 tristate "Qualcomm SDM660 interconnect driver" depends on INTERCONNECT_QCOM + depends on ARM64 || COMPILE_TEST depends on QCOM_SMD_RPM select INTERCONNECT_QCOM_SMD_RPM help @@ -241,6 +262,7 @@ config INTERCONNECT_QCOM_SDM660 config INTERCONNECT_QCOM_SDM670 tristate "Qualcomm SDM670 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -250,6 +272,7 @@ config INTERCONNECT_QCOM_SDM670 config INTERCONNECT_QCOM_SDM845 tristate "Qualcomm SDM845 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -259,6 +282,7 @@ config INTERCONNECT_QCOM_SDM845 config INTERCONNECT_QCOM_SDX55 tristate "Qualcomm SDX55 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -268,6 +292,7 @@ config INTERCONNECT_QCOM_SDX55 config INTERCONNECT_QCOM_SDX65 tristate "Qualcomm SDX65 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -277,6 +302,7 @@ config INTERCONNECT_QCOM_SDX65 config INTERCONNECT_QCOM_SDX75 tristate "Qualcomm SDX75 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -287,6 +313,7 @@ config INTERCONNECT_QCOM_SM6115 tristate "Qualcomm SM6115 interconnect driver" depends on INTERCONNECT_QCOM depends on QCOM_SMD_RPM + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_SMD_RPM help This is a driver for the Qualcomm Network-on-Chip on sm6115-based @@ -295,6 +322,7 @@ config INTERCONNECT_QCOM_SM6115 config INTERCONNECT_QCOM_SM6350 tristate "Qualcomm SM6350 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -304,6 +332,7 @@ config INTERCONNECT_QCOM_SM6350 config INTERCONNECT_QCOM_SM7150 tristate "Qualcomm SM7150 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -313,6 +342,7 @@ config INTERCONNECT_QCOM_SM7150 config INTERCONNECT_QCOM_MILOS tristate "Qualcomm Milos interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -322,6 +352,7 @@ config INTERCONNECT_QCOM_MILOS config INTERCONNECT_QCOM_SM8150 tristate "Qualcomm SM8150 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -331,6 +362,7 @@ config INTERCONNECT_QCOM_SM8150 config INTERCONNECT_QCOM_SM8250 tristate "Qualcomm SM8250 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -340,6 +372,7 @@ config INTERCONNECT_QCOM_SM8250 config INTERCONNECT_QCOM_SM8350 tristate "Qualcomm SM8350 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -349,6 +382,7 @@ config INTERCONNECT_QCOM_SM8350 config INTERCONNECT_QCOM_SM8450 tristate "Qualcomm SM8450 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -358,6 +392,7 @@ config INTERCONNECT_QCOM_SM8450 config INTERCONNECT_QCOM_SM8550 tristate "Qualcomm SM8550 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -367,6 +402,7 @@ config INTERCONNECT_QCOM_SM8550 config INTERCONNECT_QCOM_SM8650 tristate "Qualcomm SM8650 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -376,6 +412,7 @@ config INTERCONNECT_QCOM_SM8650 config INTERCONNECT_QCOM_SM8750 tristate "Qualcomm SM8750 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help @@ -385,6 +422,7 @@ config INTERCONNECT_QCOM_SM8750 config INTERCONNECT_QCOM_X1E80100 tristate "Qualcomm X1E80100 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + depends on ARM64 || COMPILE_TEST select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER help From 6d6ff64e01ddeb579bf0078e5b6d50c04035541e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 4 May 2026 16:14:42 +0800 Subject: [PATCH 0681/5214] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Document Nord QMP UFS PHY Document QMP UFS PHY on Qualcomm Nord SoC. Signed-off-by: Shawn Guo Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260504081442.825908-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index 9616c736b6d4..b2c5c9a375a3 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -31,6 +31,7 @@ properties: - items: - enum: - qcom,eliza-qmp-ufs-phy + - qcom,nord-qmp-ufs-phy - const: qcom,sm8650-qmp-ufs-phy - items: - enum: From 78a6a90a5c4ac29d06fc8119885b80f919950d00 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 4 May 2026 19:06:46 +0300 Subject: [PATCH 0682/5214] dt-bindings: phy: qcom,snps-eusb2: Document the Eliza Synopsys eUSB2 PHY The Synopsys eUSB2 PHY found on the Eliza SoC is fully compatible with the one found the SM8550. So document it by adding the compatible to the list that has the SM8550 one as fallback. Acked-by: Rob Herring (Arm) Reviewed-by: Konrad Dybcio Signed-off-by: Abel Vesa Link: https://patch.msgid.link/20260504-eliza-bindings-phy-eusb2-v2-1-fa3a1fd65ab1@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml index 854f70af0a6c..096f6b546632 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-phy.yaml @@ -17,6 +17,7 @@ properties: oneOf: - items: - enum: + - qcom,eliza-snps-eusb2-phy - qcom,milos-snps-eusb2-phy - qcom,sar2130p-snps-eusb2-phy - qcom,sdx75-snps-eusb2-phy From d67a337d28a2d852ff539e983ad6790caf9c95f5 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 4 May 2026 19:03:41 +0300 Subject: [PATCH 0683/5214] dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp-phy: Add Eliza QMP PHY Document the compatible for the USB QMP PHY found on the Qualcomm Eliza SoC. It is fully compatible with the one found on Qualcomm SM8650, so add it with the SM8650 as fallback. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Abel Vesa Link: https://patch.msgid.link/20260504-eliza-bindings-qmp-phy-v2-1-849c4de8d75f@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index 3d537b7f9985..4eff92343ce4 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -16,6 +16,10 @@ description: properties: compatible: oneOf: + - items: + - enum: + - qcom,eliza-qmp-usb3-dp-phy + - const: qcom,sm8650-qmp-usb3-dp-phy - items: - enum: - qcom,kaanapali-qmp-usb3-dp-phy From 1a75ecefa4fbedefc1600e43445de4e1e7f03b55 Mon Sep 17 00:00:00 2001 From: SriNavmani A Date: Mon, 4 May 2026 09:38:32 +0800 Subject: [PATCH 0684/5214] dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY Axiado AX3000 SoC contains Arasan PHY which provides the interface to the HS200 eMMC host controller. Signed-off-by: SriNavmani A Reviewed-by: Rob Herring (Arm) Signed-off-by: Tzu-Hao Wei Link: https://patch.msgid.link/20260504-axiado-ax3000-add-emmc-phy-driver-support-v3-1-3ab7eb45b0c5@axiado.com Signed-off-by: Vinod Koul --- .../bindings/phy/axiado,ax3000-emmc-phy.yaml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml new file mode 100644 index 000000000000..61700b80e93f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/axiado,ax3000-emmc-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axiado AX3000 Arasan eMMC PHY + +maintainers: + - SriNavmani A + - Tzu-Hao Wei + - Prasad Bolisetty + +properties: + compatible: + const: axiado,ax3000-emmc-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@80801c00 { + compatible = "axiado,ax3000-emmc-phy"; + reg = <0x80801c00 0x1000>; + #phy-cells = <0>; + }; From 9e7dfa4bcd4e2c3541c4ee954ea5e66edab94d3f Mon Sep 17 00:00:00 2001 From: SriNavmani A Date: Mon, 4 May 2026 09:38:33 +0800 Subject: [PATCH 0685/5214] phy: axiado: add Axiado eMMC PHY driver It provides the required configurations for Axiado eMMC PHY driver for HS200 mode. Signed-off-by: SriNavmani A Co-developed-by: Prasad Bolisetty Signed-off-by: Prasad Bolisetty Signed-off-by: Tzu-Hao Wei Link: https://patch.msgid.link/20260504-axiado-ax3000-add-emmc-phy-driver-support-v3-2-3ab7eb45b0c5@axiado.com Signed-off-by: Vinod Koul --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/axiado/Kconfig | 11 ++ drivers/phy/axiado/Makefile | 1 + drivers/phy/axiado/phy-axiado-emmc.c | 217 +++++++++++++++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 drivers/phy/axiado/Kconfig create mode 100644 drivers/phy/axiado/Makefile create mode 100644 drivers/phy/axiado/phy-axiado-emmc.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 227b9a4c612e..872a7f02e1c4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -136,6 +136,7 @@ config PHY_XGENE source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/apple/Kconfig" +source "drivers/phy/axiado/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" source "drivers/phy/canaan/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f49d83f00a3d..ae756fea473b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_GENERIC_PHY) += allwinner/ \ amlogic/ \ apple/ \ + axiado/ \ broadcom/ \ cadence/ \ canaan/ \ diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig new file mode 100644 index 000000000000..d159e0345345 --- /dev/null +++ b/drivers/phy/axiado/Kconfig @@ -0,0 +1,11 @@ +# +# PHY drivers for Axiado platforms +# + +config PHY_AX3000_EMMC + tristate "Axiado eMMC PHY driver" + depends on OF && (ARCH_AXIADO || COMPILE_TEST) + select GENERIC_PHY + help + Enables this to support for the AX3000 EMMC PHY driver. + If unsure, say N. diff --git a/drivers/phy/axiado/Makefile b/drivers/phy/axiado/Makefile new file mode 100644 index 000000000000..1e2b1ba01609 --- /dev/null +++ b/drivers/phy/axiado/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_AX3000_EMMC) += phy-axiado-emmc.o diff --git a/drivers/phy/axiado/phy-axiado-emmc.c b/drivers/phy/axiado/phy-axiado-emmc.c new file mode 100644 index 000000000000..e0e2174776ad --- /dev/null +++ b/drivers/phy/axiado/phy-axiado-emmc.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Axiado eMMC PHY driver + * + * Copyright (C) 2017 Arasan Chip Systems Inc. + * Copyright (C) 2022-2026 Axiado Corporation (or its affiliates). + * + * Based on Arasan Driver (sdhci-pci-arasan.c) + * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with integrated phy. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Arasan eMMC 5.1 - PHY configuration registers */ +#define CAP_REG_IN_S1_MSB 0x04 +#define PHY_CTRL_1 0x38 +#define PHY_CTRL_2 0x3c +#define PHY_CTRL_3 0x40 +#define STATUS 0x50 + +#define DLL_ENBL BIT(26) +#define RTRIM_EN BIT(21) +#define PDB_ENBL BIT(23) +#define RETB_ENBL BIT(1) + +#define REN_STRB BIT(27) +#define REN_CMD_EN GENMASK(20, 12) + +/* Pull-UP Enable on CMD Line */ +#define PU_CMD_EN GENMASK(11, 3) + +/* Selection value for the optimum delay from 1-32 output tap lines */ +#define OTAP_DLY 0x02 +/* DLL charge pump current trim default [1000] */ +#define DLL_TRM_ICP 0x08 +/* Select the frequency range of DLL Operation */ +#define FRQ_SEL 0x01 + +#define OTAP_SEL_MASK GENMASK(10, 7) +#define DLL_TRM_MASK GENMASK(25, 22) +#define DLL_FRQSEL_MASK GENMASK(27, 25) + +#define OTAP_SEL(x) (FIELD_PREP(OTAP_SEL_MASK, x) | OTAPDLY_EN) +#define DLL_TRM(x) (FIELD_PREP(DLL_TRM_MASK, x) | DLL_ENBL) +#define DLL_FRQSEL(x) FIELD_PREP(DLL_FRQSEL_MASK, x) + +#define OTAPDLY_EN BIT(11) + +#define SEL_DLY_RXCLK BIT(18) +#define SEL_DLY_TXCLK BIT(19) + +#define CALDONE_MASK 0x40 +#define DLL_RDY_MASK 0x1 +#define MAX_CLK_BUF0 BIT(20) +#define MAX_CLK_BUF1 BIT(21) +#define MAX_CLK_BUF2 BIT(22) + +#define CLK_MULTIPLIER 0xc008e +#define POLL_TIMEOUT_MS 3000 +#define POLL_DELAY_US 100 + +struct axiado_emmc_phy { + void __iomem *reg_base; + struct device *dev; +}; + +static int axiado_emmc_phy_init(struct phy *phy) +{ + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + struct device *dev = ax_phy->dev; + u32 val; + int ret; + + val = readl(ax_phy->reg_base + PHY_CTRL_1); + writel(val | RETB_ENBL | RTRIM_EN, ax_phy->reg_base + PHY_CTRL_1); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3); + + ret = readl_poll_timeout(ax_phy->reg_base + STATUS, val, + val & CALDONE_MASK, POLL_DELAY_US, + POLL_TIMEOUT_MS * 1000); + if (ret) { + dev_err(dev, "PHY calibration timeout\n"); + return ret; + } + + val = readl(ax_phy->reg_base + PHY_CTRL_1); + writel(val | REN_CMD_EN | PU_CMD_EN, ax_phy->reg_base + PHY_CTRL_1); + + val = readl(ax_phy->reg_base + PHY_CTRL_2); + writel(val | REN_STRB, ax_phy->reg_base + PHY_CTRL_2); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + writel(val | MAX_CLK_BUF0 | MAX_CLK_BUF1 | MAX_CLK_BUF2, + ax_phy->reg_base + PHY_CTRL_3); + + writel(CLK_MULTIPLIER, ax_phy->reg_base + CAP_REG_IN_S1_MSB); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + writel(val | SEL_DLY_RXCLK | SEL_DLY_TXCLK, + ax_phy->reg_base + PHY_CTRL_3); + + return 0; +} + +static int axiado_emmc_phy_power_on(struct phy *phy) +{ + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + struct device *dev = ax_phy->dev; + u32 val; + int ret; + + val = readl(ax_phy->reg_base + PHY_CTRL_1); + writel(val | RETB_ENBL, ax_phy->reg_base + PHY_CTRL_1); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + writel(val | PDB_ENBL, ax_phy->reg_base + PHY_CTRL_3); + + val = readl(ax_phy->reg_base + PHY_CTRL_2); + writel(val | OTAP_SEL(OTAP_DLY), ax_phy->reg_base + PHY_CTRL_2); + + val = readl(ax_phy->reg_base + PHY_CTRL_1); + writel(val | DLL_TRM(DLL_TRM_ICP), ax_phy->reg_base + PHY_CTRL_1); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + writel(val | DLL_FRQSEL(FRQ_SEL), ax_phy->reg_base + PHY_CTRL_3); + + ret = read_poll_timeout(readl, val, val & DLL_RDY_MASK, POLL_DELAY_US, + POLL_TIMEOUT_MS * 1000, false, + ax_phy->reg_base + STATUS); + if (ret) { + dev_err(dev, "DLL ready timeout\n"); + return ret; + } + + return 0; +} + +static int axiado_emmc_phy_power_off(struct phy *phy) +{ + struct axiado_emmc_phy *ax_phy = phy_get_drvdata(phy); + u32 val; + + val = readl(ax_phy->reg_base + PHY_CTRL_1); + val &= ~(DLL_TRM_MASK | DLL_ENBL); + writel(val, ax_phy->reg_base + PHY_CTRL_1); + + val = readl(ax_phy->reg_base + PHY_CTRL_3); + val &= ~(DLL_FRQSEL_MASK | PDB_ENBL); + writel(val, ax_phy->reg_base + PHY_CTRL_3); + + return 0; +} + +static const struct phy_ops axiado_emmc_phy_ops = { + .init = axiado_emmc_phy_init, + .power_on = axiado_emmc_phy_power_on, + .power_off = axiado_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id axiado_emmc_phy_of_match[] = { + { .compatible = "axiado,ax3000-emmc-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, axiado_emmc_phy_of_match); + +static int axiado_emmc_phy_probe(struct platform_device *pdev) +{ + struct axiado_emmc_phy *ax_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct phy *generic_phy; + + if (!dev->of_node) + return -ENODEV; + + ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL); + if (!ax_phy) + return -ENOMEM; + + ax_phy->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ax_phy->reg_base)) + return PTR_ERR(ax_phy->reg_base); + + ax_phy->dev = dev; + + generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops); + if (IS_ERR(generic_phy)) + return dev_err_probe(dev, PTR_ERR(generic_phy), + "failed to create PHY\n"); + + phy_set_drvdata(generic_phy, ax_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver axiado_emmc_phy_driver = { + .probe = axiado_emmc_phy_probe, + .driver = { + .name = "axiado-emmc-phy", + .of_match_table = axiado_emmc_phy_of_match, + }, +}; +module_platform_driver(axiado_emmc_phy_driver); + +MODULE_DESCRIPTION("AX3000 eMMC PHY Driver"); +MODULE_AUTHOR("Axiado Corporation"); +MODULE_LICENSE("GPL"); From 13ee293a904b7b7b0507aaa8c71f7be7e683800e Mon Sep 17 00:00:00 2001 From: Tzu-Hao Wei Date: Mon, 4 May 2026 09:38:34 +0800 Subject: [PATCH 0686/5214] MAINTAINERS: Add Axiado AX3000 eMMC PHY driver Add SriNavmani, Prasad and me as maintainers for Axiado AX3000 eMMC PHY driver Acked-by: Prasad Bolisetty Signed-off-by: Tzu-Hao Wei Link: https://patch.msgid.link/20260504-axiado-ax3000-add-emmc-phy-driver-support-v3-3-3ab7eb45b0c5@axiado.com Signed-off-by: Vinod Koul --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..cf179372c7f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4334,6 +4334,16 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml F: drivers/hwmon/axi-fan-control.c +AXIADO EMMC PHY DRIVER +M: SriNavmani A +M: Tzu-Hao Wei +M: Prasad Bolisetty +L: linux-phy@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/phy/axiado,ax3000-emmc-phy.yaml +F: drivers/phy/axiado/Kconfig +F: drivers/phy/axiado/phy-axiado-emmc.c + AXI SPI ENGINE M: Michael Hennerich M: Nuno Sá From 18af47764d75bf2cd6297289255fd7f83967e7cf Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:09 -0400 Subject: [PATCH 0687/5214] phy: tegra: xusb: Make USB_CONN_GPIO select conditional on GPIOLIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kconfiglint reports: K006: config PHY_TEGRA_XUSB selects USB_CONN_GPIO which depends on GPIOLIB, but PHY_TEGRA_XUSB does not depend on GPIOLIB K002: config PHY_TEGRA_XUSB selects visible symbol USB_CONN_GPIO which has dependencies USB_CONN_GPIO was introduced in commit 4602f3bff266 ("usb: common: add USB GPIO based connection detection driver") with a hard dependency on GPIOLIB, since it needs GPIO pins to detect USB cable connection state. Commit f67213cee2b3 ("phy: tegra: xusb: Add usb-role-switch support") added `select USB_CONN_GPIO` to PHY_TEGRA_XUSB to support USB role switching via GPIO-based detection. At that time, PHY_TEGRA_XUSB depended only on `ARCH_TEGRA`, and both the ARM32 and ARM64 ARCH_TEGRA Kconfig definitions select GPIOLIB, so the missing explicit GPIOLIB guard was not a functional problem — GPIOLIB is always available when ARCH_TEGRA is enabled. Later, commit 0d5c9bc7c680 ("phy: tegra: Select USB_COMMON for usb_get_maximum_speed()") added `depends on USB_SUPPORT` but still did not address the GPIOLIB gap. The select can force USB_CONN_GPIO on without its GPIOLIB dependency being satisfied if the Kconfig dependencies were ever restructured. Make the select conditional with `select USB_CONN_GPIO if GPIOLIB` to make the dependency explicit. This mirrors how other drivers handle optional GPIO-dependent selects throughout the kernel. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Link: https://patch.msgid.link/20260426000309.54959-1-sashal@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/tegra/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig index 342fb736da4b..f0734415fc94 100644 --- a/drivers/phy/tegra/Kconfig +++ b/drivers/phy/tegra/Kconfig @@ -3,7 +3,7 @@ config PHY_TEGRA_XUSB tristate "NVIDIA Tegra XUSB pad controller driver" depends on ARCH_TEGRA && USB_SUPPORT select USB_COMMON - select USB_CONN_GPIO + select USB_CONN_GPIO if GPIOLIB select USB_PHY help Choose this option if you have an NVIDIA Tegra SoC. From 52595824b0027d075470f7f08afe805844c1b079 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:20 +0800 Subject: [PATCH 0688/5214] phy: qcom-qmp: Add missing QSERDES COM v2 registers A few registers that could be used by phy-qcom-qmp drivers are missing from qserdes-com-v2 header. Add them. Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260314051325.198137-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v2.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v2.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v2.h index 3ea1884f35dd..cb599c113189 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v2.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v2.h @@ -34,6 +34,7 @@ #define QSERDES_V2_COM_LOCK_CMP3_MODE1 0x060 #define QSERDES_V2_COM_EP_CLOCK_DETECT_CTR 0x068 #define QSERDES_V2_COM_SYSCLK_DET_COMP_STATUS 0x06c +#define QSERDES_V2_COM_BG_TRIM 0x070 #define QSERDES_V2_COM_CLK_EP_DIV 0x074 #define QSERDES_V2_COM_CP_CTRL_MODE0 0x078 #define QSERDES_V2_COM_CP_CTRL_MODE1 0x07c @@ -47,6 +48,7 @@ #define QSERDES_V2_COM_CML_SYSCLK_SEL 0x0b0 #define QSERDES_V2_COM_RESETSM_CNTRL 0x0b4 #define QSERDES_V2_COM_RESETSM_CNTRL2 0x0b8 +#define QSERDES_V2_COM_RESCODE_DIV_NUM 0x0c4 #define QSERDES_V2_COM_LOCK_CMP_EN 0x0c8 #define QSERDES_V2_COM_LOCK_CMP_CFG 0x0cc #define QSERDES_V2_COM_DEC_START_MODE0 0x0d0 @@ -83,6 +85,7 @@ #define QSERDES_V2_COM_RESTRIM_CODE_STATUS 0x164 #define QSERDES_V2_COM_PLLCAL_CODE1_STATUS 0x168 #define QSERDES_V2_COM_PLLCAL_CODE2_STATUS 0x16c +#define QSERDES_V2_COM_BG_CTRL 0x170 #define QSERDES_V2_COM_CLK_SELECT 0x174 #define QSERDES_V2_COM_HSCLK_SEL 0x178 #define QSERDES_V2_COM_INTEGLOOP_BINCODE_STATUS 0x17c From 764f409b840ab400253215e765a72b903feb6afd Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:21 +0800 Subject: [PATCH 0689/5214] phy: qcom-qmp: Use explicit QSERDES COM v2 register definitions As the code comments in the headers say, both qserdes-com and qserdes-com-v2 define QSERDES COM registers for QMP V2 PHY. Switch phy-qcom-qmp drivers to use register definitions in qserdes-com-v2 to make the QSERDES COM version explicit. Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260314051325.198137-3-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c | 86 ++++---- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 162 +++++++-------- drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 194 +++++++++--------- drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 188 ++++++++--------- drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 180 ++++++++-------- 5 files changed, 405 insertions(+), 405 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c index a7c65cfe31df..24b5d66e9ecf 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c @@ -59,49 +59,49 @@ static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { }; static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESCODE_DIV_NUM, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESCODE_DIV_NUM, 0x40), }; static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index fed2fc9bb311..aa2f8da93a02 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -309,46 +309,46 @@ static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_misc_tbl[] = { }; static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xD), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xD04), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0xb), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0xf), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x6), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0xf), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0xa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0xa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0xa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x3), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0xD), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0xD04), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0xb), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CTRL_BY_PSM, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_EP_DIV, 0x19), }; static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = { @@ -752,47 +752,47 @@ static const struct qmp_phy_init_tbl ipq9574_gen3x2_pcie_pcs_misc_tbl[] = { }; static const struct qmp_phy_init_tbl qcs615_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x6), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0xf), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x9), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x4), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x3), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0xd), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x04), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x35), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x4), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0xa), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x2), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x0), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0xf), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x6), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0xf), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0xa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x9), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x3), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0xd), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CTRL_BY_PSM, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0xa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_EP_DIV, 0x19), }; static const struct qmp_phy_init_tbl qcs615_pcie_rx_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 771bc7c2ab50..48252ab0379e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -147,52 +147,52 @@ static const struct qmp_phy_init_tbl milos_ufsphy_pcs[] = { }; static const struct qmp_phy_init_tbl msm8996_ufsphy_serdes[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x05), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x54), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0xd7), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_CTRL, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE1_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE1_MODE1, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE1, 0x00), }; static const struct qmp_phy_init_tbl msm8996_ufsphy_tx[] = { @@ -320,60 +320,60 @@ static const struct qmp_phy_init_tbl sc7280_ufsphy_hs_g4_rx[] = { }; static const struct qmp_phy_init_tbl sm6115_ufsphy_serdes[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x04), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE1_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE1, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE1_MODE1, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_INITVAL1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_INITVAL2, 0x00), }; static const struct qmp_phy_init_tbl sm6115_ufsphy_hs_b_serdes[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x44), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x44), }; static const struct qmp_phy_init_tbl sm6115_ufsphy_tx[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index b0ecd5ba2464..f43650f9a45c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -244,40 +244,40 @@ static const struct qmp_phy_init_tbl glymur_usb3_uniphy_pcs_usb_tbl[] = { }; static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x06), /* PLL and Loop filter settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x68), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0xaa), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x09), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xa0), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xaa), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x29), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x29), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), /* SSC settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x7d), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x05), }; static const struct qmp_phy_init_tbl ipq9574_usb3_tx_tbl[] = { @@ -326,40 +326,40 @@ static const struct qmp_phy_init_tbl ipq9574_usb3_pcs_tbl[] = { }; static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x06), /* PLL and Loop filter settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), /* SSC settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x07), }; static const struct qmp_phy_init_tbl ipq8074_usb3_rx_tbl[] = { @@ -401,40 +401,40 @@ static const struct qmp_phy_init_tbl ipq8074_usb3_pcs_tbl[] = { }; static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x04), /* PLL and Loop filter settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), /* SSC settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x07), }; static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index c342479a3798..67efe80a51a6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -177,44 +177,44 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { }; static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CTRL_BY_PSM, 0x01), }; static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = { @@ -291,63 +291,63 @@ static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { }; static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x37), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_CTRL, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x40), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x05), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_CTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN0_MODE0, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TIMER, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORECLK_DIV, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CORE_CLK_EN, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x02), }; static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_rbr[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x2c), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x69), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xbf), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x21), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x2c), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x21), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x24), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x69), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x38), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr2[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x20), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x8c), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x70), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x8c), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START3_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP1_MODE0, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP2_MODE0, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; static const struct qmp_phy_init_tbl qmp_v2_dp_tx_tbl[] = { @@ -906,9 +906,9 @@ static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - writel(0x20, qmp->dp_serdes + QSERDES_COM_RESETSM_CNTRL); + writel(0x20, qmp->dp_serdes + QSERDES_V2_COM_RESETSM_CNTRL); - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_COM_C_READY_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V2_COM_C_READY_STATUS, status, ((status & BIT(0)) > 0), 500, @@ -917,7 +917,7 @@ static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) return -ETIMEDOUT; } - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V2_COM_CMN_STATUS, status, ((status & BIT(0)) > 0), 500, @@ -926,7 +926,7 @@ static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) return -ETIMEDOUT; } - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V2_COM_CMN_STATUS, status, ((status & BIT(1)) > 0), 500, From 9dfdd6e7bebd63eeef0ba57493adee91c34ae338 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:22 +0800 Subject: [PATCH 0690/5214] phy: qcom-qmp-usbc: Use register definitions in qserdes-txrx-v3 The register definitions in header qserdes-txrx-v2 and qserdes-txrx-v3 are actually identical. Considering that QSERDES TX/RX v2 is already defined by header qserdes-txrx, qserdes-txrx-v2 is really just a duplication of qserdes-txrx-v3 for QSERDES TX/RX v3. Switch qcom-qmp-usbc driver to use v3 registers. Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260314051325.198137-4-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 64 ++++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index 67efe80a51a6..fb4be9531e7a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -351,20 +351,20 @@ static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr2[] = { }; static const struct qmp_phy_init_tbl qmp_v2_dp_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TRANSCEIVER_BIAS_EN, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_VMODE_CTRL1, 0x40), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_PRE_STALL_LDO_BOOST_EN, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_INTERFACE_SELECT, 0x3d), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_CLKBUF_ENABLE, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_RESET_TSYNC_EN, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TRAN_DRVR_EMP_EN, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TX_INTERFACE_MODE, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TX_EMP_POST1_LVL, 0x2b), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TX_DRV_LVL, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_TX_BAND, 0x4), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_RES_CODE_LANE_OFFSET_TX, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V2_TX_RES_CODE_LANE_OFFSET_RX, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_VMODE_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_INTERFACE_SELECT, 0x3d), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_CLKBUF_ENABLE, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RESET_TSYNC_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRAN_DRVR_EMP_EN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_INTERFACE_MODE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_EMP_POST1_LVL, 0x2b), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_DRV_LVL, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_BAND, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x12), }; struct qmp_usbc_offsets { @@ -809,10 +809,10 @@ static int qmp_v2_configure_dp_swing(struct qmp_usbc *qmp) if (voltage_swing_cfg == 0xff && pre_emphasis_cfg == 0xff) return -EINVAL; - writel(voltage_swing_cfg, tx + QSERDES_V2_TX_TX_DRV_LVL); - writel(pre_emphasis_cfg, tx + QSERDES_V2_TX_TX_EMP_POST1_LVL); - writel(voltage_swing_cfg, tx2 + QSERDES_V2_TX_TX_DRV_LVL); - writel(pre_emphasis_cfg, tx2 + QSERDES_V2_TX_TX_EMP_POST1_LVL); + writel(voltage_swing_cfg, tx + QSERDES_V3_TX_TX_DRV_LVL); + writel(pre_emphasis_cfg, tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); + writel(voltage_swing_cfg, tx2 + QSERDES_V3_TX_TX_DRV_LVL); + writel(pre_emphasis_cfg, tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); return 0; } @@ -871,17 +871,17 @@ static void qmp_v2_configure_dp_tx(struct qmp_usbc *qmp) void __iomem *tx2 = qmp->dp_tx2; /* program default setting first */ - writel(0x2a, tx + QSERDES_V2_TX_TX_DRV_LVL); - writel(0x20, tx + QSERDES_V2_TX_TX_EMP_POST1_LVL); - writel(0x2a, tx2 + QSERDES_V2_TX_TX_DRV_LVL); - writel(0x20, tx2 + QSERDES_V2_TX_TX_EMP_POST1_LVL); + writel(0x2a, tx + QSERDES_V3_TX_TX_DRV_LVL); + writel(0x20, tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); + writel(0x2a, tx2 + QSERDES_V3_TX_TX_DRV_LVL); + writel(0x20, tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); if (dp_opts->link_rate >= 2700) { - writel(0xc4, tx + QSERDES_V2_TX_LANE_MODE_1); - writel(0xc4, tx2 + QSERDES_V2_TX_LANE_MODE_1); + writel(0xc4, tx + QSERDES_V3_TX_LANE_MODE_1); + writel(0xc4, tx2 + QSERDES_V3_TX_LANE_MODE_1); } else { - writel(0xc6, tx + QSERDES_V2_TX_LANE_MODE_1); - writel(0xc6, tx2 + QSERDES_V2_TX_LANE_MODE_1); + writel(0xc6, tx + QSERDES_V3_TX_LANE_MODE_1); + writel(0xc6, tx2 + QSERDES_V3_TX_LANE_MODE_1); } qmp_v2_configure_dp_swing(qmp); @@ -955,12 +955,12 @@ static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) return -ETIMEDOUT; } - writel(0x3f, qmp->dp_tx + QSERDES_V2_TX_TRANSCEIVER_BIAS_EN); - writel(0x10, qmp->dp_tx + QSERDES_V2_TX_HIGHZ_DRVR_EN); - writel(0x0a, qmp->dp_tx + QSERDES_V2_TX_TX_POL_INV); - writel(0x3f, qmp->dp_tx2 + QSERDES_V2_TX_TRANSCEIVER_BIAS_EN); - writel(0x10, qmp->dp_tx2 + QSERDES_V2_TX_HIGHZ_DRVR_EN); - writel(0x0a, qmp->dp_tx2 + QSERDES_V2_TX_TX_POL_INV); + writel(0x3f, qmp->dp_tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); + writel(0x10, qmp->dp_tx + QSERDES_V3_TX_HIGHZ_DRVR_EN); + writel(0x0a, qmp->dp_tx + QSERDES_V3_TX_TX_POL_INV); + writel(0x3f, qmp->dp_tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); + writel(0x10, qmp->dp_tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN); + writel(0x0a, qmp->dp_tx2 + QSERDES_V3_TX_TX_POL_INV); writel(0x18, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); From c834f0a69051e5db52172262dadf8f7b5ff58bd0 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:23 +0800 Subject: [PATCH 0691/5214] phy: qcom-qmp-usbc: Rename QCS615 DP PHY variables and functions Commit 81791c45c8e0 ("phy: qcom: qmp-usbc: Add QCS615 USB/DP PHY config and DP mode support") chose to name QCS615 DP PHY variables/functions with qmp_v2 prefix, by assuming that QMP PHY registers are versioned as a whole. However, the reality is that the registers are versioned in sub-modules like QSERDES COM and QSERDES TXRX respectively, e.g. QCS615 DP PHY has registers of QSERDES COM v2 and QSERDES TXRX v3. Thus it may cause confusion that qmp_v2_xxx table and functions access QSERDES TXRX v3 registers. Rename QCS615 DP PHY variables and functions to be prefixed by qcs615 instead of qmp_v2. This better aligns with how the driver names USB3 PHY variables for QCM2290 etc. Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260314051325.198137-5-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 66 ++++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index fb4be9531e7a..d4b55e55dd77 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -290,7 +290,7 @@ static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), }; -static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl[] = { +static const struct qmp_phy_init_tbl qcs615_dp_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), QMP_PHY_INIT_CFG(QSERDES_V2_COM_SYSCLK_EN_SEL, 0x37), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x00), @@ -317,7 +317,7 @@ static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x02), }; -static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_rbr[] = { +static const struct qmp_phy_init_tbl qcs615_dp_serdes_tbl_rbr[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x2c), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x69), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), @@ -328,7 +328,7 @@ static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_rbr[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; -static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr[] = { +static const struct qmp_phy_init_tbl qcs615_dp_serdes_tbl_hbr[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x24), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x69), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), @@ -339,7 +339,7 @@ static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; -static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr2[] = { +static const struct qmp_phy_init_tbl qcs615_dp_serdes_tbl_hbr2[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x20), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DEC_START_MODE0, 0x8c), QMP_PHY_INIT_CFG(QSERDES_V2_COM_DIV_FRAC_START1_MODE0, 0x00), @@ -350,7 +350,7 @@ static const struct qmp_phy_init_tbl qmp_v2_dp_serdes_tbl_hbr2[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_LOCK_CMP3_MODE0, 0x00), }; -static const struct qmp_phy_init_tbl qmp_v2_dp_tx_tbl[] = { +static const struct qmp_phy_init_tbl qcs615_dp_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, 0x1a), QMP_PHY_INIT_CFG(QSERDES_V3_TX_VMODE_CTRL1, 0x40), QMP_PHY_INIT_CFG(QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN, 0x30), @@ -555,14 +555,14 @@ static const struct qmp_usbc_offsets qmp_usbc_usb3dp_offsets_qcs615 = { .dp_dp_phy = 0x1000, }; -static const u8 qmp_v2_dp_pre_emphasis_hbr2_rbr[4][4] = { +static const u8 qcs615_dp_pre_emphasis_hbr2_rbr[4][4] = { {0x00, 0x0b, 0x12, 0xff}, {0x00, 0x0a, 0x12, 0xff}, {0x00, 0x0c, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff} }; -static const u8 qmp_v2_dp_voltage_swing_hbr2_rbr[4][4] = { +static const u8 qcs615_dp_voltage_swing_hbr2_rbr[4][4] = { {0x07, 0x0f, 0x14, 0xff}, {0x11, 0x1d, 0x1f, 0xff}, {0x18, 0x1f, 0xff, 0xff}, @@ -641,10 +641,10 @@ static const struct qmp_phy_cfg qcs615_usb3phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout_qcm2290, }; -static void qmp_v2_dp_aux_init(struct qmp_usbc *qmp); -static void qmp_v2_configure_dp_tx(struct qmp_usbc *qmp); -static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp); -static int qmp_v2_calibrate_dp_phy(struct qmp_usbc *qmp); +static void qcs615_qmp_dp_aux_init(struct qmp_usbc *qmp); +static void qcs615_qmp_configure_dp_tx(struct qmp_usbc *qmp); +static int qcs615_qmp_configure_dp_phy(struct qmp_usbc *qmp); +static int qcs615_qmp_calibrate_dp_phy(struct qmp_usbc *qmp); static const struct qmp_phy_cfg qcs615_usb3dp_phy_cfg = { .offsets = &qmp_usbc_usb3dp_offsets_qcs615, @@ -660,25 +660,25 @@ static const struct qmp_phy_cfg qcs615_usb3dp_phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout_qcm2290, - .dp_serdes_tbl = qmp_v2_dp_serdes_tbl, - .dp_serdes_tbl_num = ARRAY_SIZE(qmp_v2_dp_serdes_tbl), - .dp_tx_tbl = qmp_v2_dp_tx_tbl, - .dp_tx_tbl_num = ARRAY_SIZE(qmp_v2_dp_tx_tbl), + .dp_serdes_tbl = qcs615_dp_serdes_tbl, + .dp_serdes_tbl_num = ARRAY_SIZE(qcs615_dp_serdes_tbl), + .dp_tx_tbl = qcs615_dp_tx_tbl, + .dp_tx_tbl_num = ARRAY_SIZE(qcs615_dp_tx_tbl), - .serdes_tbl_rbr = qmp_v2_dp_serdes_tbl_rbr, - .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v2_dp_serdes_tbl_rbr), - .serdes_tbl_hbr = qmp_v2_dp_serdes_tbl_hbr, - .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v2_dp_serdes_tbl_hbr), - .serdes_tbl_hbr2 = qmp_v2_dp_serdes_tbl_hbr2, - .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v2_dp_serdes_tbl_hbr2), + .serdes_tbl_rbr = qcs615_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qcs615_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qcs615_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_hbr2), - .swing_tbl = &qmp_v2_dp_voltage_swing_hbr2_rbr, - .pre_emphasis_tbl = &qmp_v2_dp_pre_emphasis_hbr2_rbr, + .swing_tbl = &qcs615_dp_voltage_swing_hbr2_rbr, + .pre_emphasis_tbl = &qcs615_dp_pre_emphasis_hbr2_rbr, - .dp_aux_init = qmp_v2_dp_aux_init, - .configure_dp_tx = qmp_v2_configure_dp_tx, - .configure_dp_phy = qmp_v2_configure_dp_phy, - .calibrate_dp_phy = qmp_v2_calibrate_dp_phy, + .dp_aux_init = qcs615_qmp_dp_aux_init, + .configure_dp_tx = qcs615_qmp_configure_dp_tx, + .configure_dp_phy = qcs615_qmp_configure_dp_phy, + .calibrate_dp_phy = qcs615_qmp_calibrate_dp_phy, .reset_list = usb3dpphy_reset_l, .num_resets = ARRAY_SIZE(usb3dpphy_reset_l), @@ -744,7 +744,7 @@ static int qmp_usbc_com_exit(struct phy *phy) return 0; } -static void qmp_v2_dp_aux_init(struct qmp_usbc *qmp) +static void qcs615_qmp_dp_aux_init(struct qmp_usbc *qmp) { writel(DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | @@ -774,7 +774,7 @@ static void qmp_v2_dp_aux_init(struct qmp_usbc *qmp) qmp->dp_dp_phy + QSERDES_V2_DP_PHY_AUX_INTERRUPT_MASK); } -static int qmp_v2_configure_dp_swing(struct qmp_usbc *qmp) +static int qcs615_qmp_configure_dp_swing(struct qmp_usbc *qmp) { const struct qmp_phy_cfg *cfg = qmp->cfg; const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; @@ -864,7 +864,7 @@ static int qmp_usbc_configure_dp_clocks(struct qmp_usbc *qmp) return 0; } -static void qmp_v2_configure_dp_tx(struct qmp_usbc *qmp) +static void qcs615_qmp_configure_dp_tx(struct qmp_usbc *qmp) { const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; void __iomem *tx = qmp->dp_tx; @@ -884,10 +884,10 @@ static void qmp_v2_configure_dp_tx(struct qmp_usbc *qmp) writel(0xc6, tx2 + QSERDES_V3_TX_LANE_MODE_1); } - qmp_v2_configure_dp_swing(qmp); + qcs615_qmp_configure_dp_swing(qmp); } -static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) +static int qcs615_qmp_configure_dp_phy(struct qmp_usbc *qmp) { u32 status; int ret; @@ -977,7 +977,7 @@ static int qmp_v2_configure_dp_phy(struct qmp_usbc *qmp) return 0; } -static int qmp_v2_calibrate_dp_phy(struct qmp_usbc *qmp) +static int qcs615_qmp_calibrate_dp_phy(struct qmp_usbc *qmp) { static const u8 cfg1_settings[] = {0x13, 0x23, 0x1d}; u8 val; From 9b1270d2b85bb7ce6bbc71232375b21d8be0b799 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:24 +0800 Subject: [PATCH 0692/5214] phy: qcom-qmp: Drop unused register headers None of qcom-qmp drivers uses header qserdes-com or qserdes-txrx-v2. Drop them. Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260314051325.198137-6-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-qserdes-com.h | 140 ------------------ .../qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h | 68 --------- drivers/phy/qualcomm/phy-qcom-qmp.h | 2 - 3 files changed, 210 deletions(-) delete mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h deleted file mode 100644 index 7fa5363feeb9..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com.h +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - -#ifndef QCOM_PHY_QMP_QSERDES_COM_H_ -#define QCOM_PHY_QMP_QSERDES_COM_H_ - -/* Only for QMP V2 PHY - QSERDES COM registers */ -#define QSERDES_COM_ATB_SEL1 0x000 -#define QSERDES_COM_ATB_SEL2 0x004 -#define QSERDES_COM_FREQ_UPDATE 0x008 -#define QSERDES_COM_BG_TIMER 0x00c -#define QSERDES_COM_SSC_EN_CENTER 0x010 -#define QSERDES_COM_SSC_ADJ_PER1 0x014 -#define QSERDES_COM_SSC_ADJ_PER2 0x018 -#define QSERDES_COM_SSC_PER1 0x01c -#define QSERDES_COM_SSC_PER2 0x020 -#define QSERDES_COM_SSC_STEP_SIZE1 0x024 -#define QSERDES_COM_SSC_STEP_SIZE2 0x028 -#define QSERDES_COM_POST_DIV 0x02c -#define QSERDES_COM_POST_DIV_MUX 0x030 -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 -#define QSERDES_COM_CLK_ENABLE1 0x038 -#define QSERDES_COM_SYS_CLK_CTRL 0x03c -#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 -#define QSERDES_COM_PLL_EN 0x044 -#define QSERDES_COM_PLL_IVCO 0x048 -#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c -#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 -#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 -#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 -#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c -#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 -#define QSERDES_COM_LOCK_CMP1_MODE2 0x064 -#define QSERDES_COM_CMN_RSVD0 0x064 -#define QSERDES_COM_LOCK_CMP2_MODE2 0x068 -#define QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x068 -#define QSERDES_COM_LOCK_CMP3_MODE2 0x06c -#define QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x06c -#define QSERDES_COM_BG_TRIM 0x070 -#define QSERDES_COM_CLK_EP_DIV 0x074 -#define QSERDES_COM_CP_CTRL_MODE0 0x078 -#define QSERDES_COM_CP_CTRL_MODE1 0x07c -#define QSERDES_COM_CP_CTRL_MODE2 0x080 -#define QSERDES_COM_CMN_RSVD1 0x080 -#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 -#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 -#define QSERDES_COM_PLL_RCTRL_MODE2 0x08c -#define QSERDES_COM_CMN_RSVD2 0x08c -#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 -#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 -#define QSERDES_COM_PLL_CCTRL_MODE2 0x098 -#define QSERDES_COM_CMN_RSVD3 0x098 -#define QSERDES_COM_PLL_CNTRL 0x09c -#define QSERDES_COM_PHASE_SEL_CTRL 0x0a0 -#define QSERDES_COM_PHASE_SEL_DC 0x0a4 -#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x0a8 -#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8 -#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac -#define QSERDES_COM_CML_SYSCLK_SEL 0x0b0 -#define QSERDES_COM_RESETSM_CNTRL 0x0b4 -#define QSERDES_COM_RESETSM_CNTRL2 0x0b8 -#define QSERDES_COM_RESTRIM_CTRL 0x0bc -#define QSERDES_COM_RESTRIM_CTRL2 0x0c0 -#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 -#define QSERDES_COM_LOCK_CMP_EN 0x0c8 -#define QSERDES_COM_LOCK_CMP_CFG 0x0cc -#define QSERDES_COM_DEC_START_MODE0 0x0d0 -#define QSERDES_COM_DEC_START_MODE1 0x0d4 -#define QSERDES_COM_DEC_START_MODE2 0x0d8 -#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x0d8 -#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc -#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 -#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 -#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 -#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec -#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 -#define QSERDES_COM_DIV_FRAC_START1_MODE2 0x0f4 -#define QSERDES_COM_VCO_TUNE_MINVAL1 0x0f4 -#define QSERDES_COM_DIV_FRAC_START2_MODE2 0x0f8 -#define QSERDES_COM_VCO_TUNE_MINVAL2 0x0f8 -#define QSERDES_COM_DIV_FRAC_START3_MODE2 0x0fc -#define QSERDES_COM_CMN_RSVD4 0x0fc -#define QSERDES_COM_INTEGLOOP_INITVAL 0x100 -#define QSERDES_COM_INTEGLOOP_EN 0x104 -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 0x118 -#define QSERDES_COM_VCO_TUNE_MAXVAL1 0x118 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 0x11c -#define QSERDES_COM_VCO_TUNE_MAXVAL2 0x11c -#define QSERDES_COM_RES_TRIM_CONTROL2 0x120 -#define QSERDES_COM_VCO_TUNE_CTRL 0x124 -#define QSERDES_COM_VCO_TUNE_MAP 0x128 -#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c -#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 -#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 -#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 -#define QSERDES_COM_VCO_TUNE1_MODE2 0x13c -#define QSERDES_COM_VCO_TUNE_INITVAL1 0x13c -#define QSERDES_COM_VCO_TUNE2_MODE2 0x140 -#define QSERDES_COM_VCO_TUNE_INITVAL2 0x140 -#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 -#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 -#define QSERDES_COM_SAR 0x14c -#define QSERDES_COM_SAR_CLK 0x150 -#define QSERDES_COM_SAR_CODE_OUT_STATUS 0x154 -#define QSERDES_COM_SAR_CODE_READY_STATUS 0x158 -#define QSERDES_COM_CMN_STATUS 0x15c -#define QSERDES_COM_RESET_SM_STATUS 0x160 -#define QSERDES_COM_RESTRIM_CODE_STATUS 0x164 -#define QSERDES_COM_PLLCAL_CODE1_STATUS 0x168 -#define QSERDES_COM_PLLCAL_CODE2_STATUS 0x16c -#define QSERDES_COM_BG_CTRL 0x170 -#define QSERDES_COM_CLK_SELECT 0x174 -#define QSERDES_COM_HSCLK_SEL 0x178 -#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS 0x17c -#define QSERDES_COM_PLL_ANALOG 0x180 -#define QSERDES_COM_CORECLK_DIV 0x184 -#define QSERDES_COM_SW_RESET 0x188 -#define QSERDES_COM_CORE_CLK_EN 0x18c -#define QSERDES_COM_C_READY_STATUS 0x190 -#define QSERDES_COM_CMN_CONFIG 0x194 -#define QSERDES_COM_CMN_RATE_OVERRIDE 0x198 -#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c -#define QSERDES_COM_DEBUG_BUS0 0x1a0 -#define QSERDES_COM_DEBUG_BUS1 0x1a4 -#define QSERDES_COM_DEBUG_BUS2 0x1a8 -#define QSERDES_COM_DEBUG_BUS3 0x1ac -#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 -#define QSERDES_COM_CMN_MISC1 0x1b4 -#define QSERDES_COM_CMN_MISC2 0x1b8 -#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc -#define QSERDES_COM_CORECLK_DIV_MODE2 0x1c0 -#define QSERDES_COM_CMN_RSVD5 0x1c4 - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h deleted file mode 100644 index 34919720b7bc..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - -#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V2_H_ -#define QCOM_PHY_QMP_QSERDES_TXRX_V2_H_ - -/* Only for QMP V2 PHY - TX registers */ -#define QSERDES_V2_TX_BIST_MODE_LANENO 0x000 -#define QSERDES_V2_TX_CLKBUF_ENABLE 0x008 -#define QSERDES_V2_TX_TX_EMP_POST1_LVL 0x00c -#define QSERDES_V2_TX_TX_DRV_LVL 0x01c -#define QSERDES_V2_TX_RESET_TSYNC_EN 0x024 -#define QSERDES_V2_TX_PRE_STALL_LDO_BOOST_EN 0x028 -#define QSERDES_V2_TX_TX_BAND 0x02c -#define QSERDES_V2_TX_SLEW_CNTL 0x030 -#define QSERDES_V2_TX_INTERFACE_SELECT 0x034 -#define QSERDES_V2_TX_RES_CODE_LANE_TX 0x03c -#define QSERDES_V2_TX_RES_CODE_LANE_RX 0x040 -#define QSERDES_V2_TX_RES_CODE_LANE_OFFSET_TX 0x044 -#define QSERDES_V2_TX_RES_CODE_LANE_OFFSET_RX 0x048 -#define QSERDES_V2_TX_DEBUG_BUS_SEL 0x058 -#define QSERDES_V2_TX_TRANSCEIVER_BIAS_EN 0x05c -#define QSERDES_V2_TX_HIGHZ_DRVR_EN 0x060 -#define QSERDES_V2_TX_TX_POL_INV 0x064 -#define QSERDES_V2_TX_PARRATE_REC_DETECT_IDLE_EN 0x068 -#define QSERDES_V2_TX_LANE_MODE_1 0x08c -#define QSERDES_V2_TX_LANE_MODE_2 0x090 -#define QSERDES_V2_TX_LANE_MODE_3 0x094 -#define QSERDES_V2_TX_RCV_DETECT_LVL_2 0x0a4 -#define QSERDES_V2_TX_TRAN_DRVR_EMP_EN 0x0c0 -#define QSERDES_V2_TX_TX_INTERFACE_MODE 0x0c4 -#define QSERDES_V2_TX_VMODE_CTRL1 0x0f0 - -/* Only for QMP V2 PHY - RX registers */ -#define QSERDES_V2_RX_UCDR_FO_GAIN 0x008 -#define QSERDES_V2_RX_UCDR_SO_GAIN_HALF 0x00c -#define QSERDES_V2_RX_UCDR_SO_GAIN 0x014 -#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN_HALF 0x024 -#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028 -#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN 0x02c -#define QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN 0x030 -#define QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 -#define QSERDES_V2_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c -#define QSERDES_V2_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 -#define QSERDES_V2_RX_UCDR_PI_CONTROLS 0x044 -#define QSERDES_V2_RX_RX_TERM_BW 0x07c -#define QSERDES_V2_RX_VGA_CAL_CNTRL1 0x0bc -#define QSERDES_V2_RX_VGA_CAL_CNTRL2 0x0c0 -#define QSERDES_V2_RX_RX_EQ_GAIN2_LSB 0x0c8 -#define QSERDES_V2_RX_RX_EQ_GAIN2_MSB 0x0cc -#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d0 -#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4 -#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8 -#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc -#define QSERDES_V2_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8 -#define QSERDES_V2_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc -#define QSERDES_V2_RX_SIGDET_ENABLES 0x100 -#define QSERDES_V2_RX_SIGDET_CNTRL 0x104 -#define QSERDES_V2_RX_SIGDET_LVL 0x108 -#define QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL 0x10c -#define QSERDES_V2_RX_RX_BAND 0x110 -#define QSERDES_V2_RX_RX_INTERFACE_MODE 0x11c -#define QSERDES_V2_RX_RX_MODE_00 0x164 -#define QSERDES_V2_RX_RX_MODE_01 0x168 - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index a873bdd7bffe..19e91f44e84e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -6,11 +6,9 @@ #ifndef QCOM_PHY_QMP_H_ #define QCOM_PHY_QMP_H_ -#include "phy-qcom-qmp-qserdes-com.h" #include "phy-qcom-qmp-qserdes-txrx.h" #include "phy-qcom-qmp-qserdes-com-v2.h" -#include "phy-qcom-qmp-qserdes-txrx-v2.h" #include "phy-qcom-qmp-qserdes-com-v3.h" #include "phy-qcom-qmp-qserdes-txrx-v3.h" From c7cd4798fafa84581502094d0be282072851c9b7 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sat, 14 Mar 2026 13:13:25 +0800 Subject: [PATCH 0693/5214] phy: qcom-qmp: Make QSERDES TXRX v2 registers explicit Rename QSERDES TXRX v2 registers and the header to make version explicit. Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260314051325.198137-7-shengchao.guo@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c | 24 +- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 50 ++--- .../qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h | 205 ++++++++++++++++++ .../phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h | 205 ------------------ drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 60 ++--- drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 74 +++---- drivers/phy/qualcomm/phy-qcom-qmp.h | 3 +- 7 files changed, 310 insertions(+), 311 deletions(-) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c index 24b5d66e9ecf..37e96493b722 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c @@ -105,21 +105,21 @@ static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x06), }; static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_LVL, 0x19), }; static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index aa2f8da93a02..75afbd15aaf4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -352,22 +352,22 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl ipq8074_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6), - QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_TX_TX_EMP_POST1_LVL, 0x36), - QMP_PHY_INIT_CFG(QSERDES_TX_SLEW_CNTL, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x6), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RES_CODE_LANE_OFFSET, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_TX_EMP_POST1_LVL, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_SLEW_CNTL, 0x0a), }; static const struct qmp_phy_init_tbl ipq8074_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x4), }; static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { @@ -796,21 +796,21 @@ static const struct qmp_phy_init_tbl qcs615_pcie_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl qcs615_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x4), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x4), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN_HALF, 0x4), }; static const struct qmp_phy_init_tbl qcs615_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x6), - QMP_PHY_INIT_CFG(QSERDES_TX_RES_CODE_LANE_OFFSET, 0x2), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x6), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RES_CODE_LANE_OFFSET, 0x2), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RCV_DETECT_LVL_2, 0x12), }; static const struct qmp_phy_init_tbl qcs615_pcie_pcs_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h new file mode 100644 index 000000000000..9ae0cf95e317 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v2.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V2_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V2_H_ + +/* Only for QMP V2 PHY - TX registers */ +#define QSERDES_V2_TX_BIST_MODE_LANENO 0x000 +#define QSERDES_V2_TX_BIST_INVERT 0x004 +#define QSERDES_V2_TX_CLKBUF_ENABLE 0x008 +#define QSERDES_V2_TX_CMN_CONTROL_ONE 0x00c +#define QSERDES_V2_TX_CMN_CONTROL_TWO 0x010 +#define QSERDES_V2_TX_CMN_CONTROL_THREE 0x014 +#define QSERDES_V2_TX_TX_EMP_POST1_LVL 0x018 +#define QSERDES_V2_TX_TX_POST2_EMPH 0x01c +#define QSERDES_V2_TX_TX_BOOST_LVL_UP_DN 0x020 +#define QSERDES_V2_TX_HP_PD_ENABLES 0x024 +#define QSERDES_V2_TX_TX_IDLE_LVL_LARGE_AMP 0x028 +#define QSERDES_V2_TX_TX_DRV_LVL 0x02c +#define QSERDES_V2_TX_TX_DRV_LVL_OFFSET 0x030 +#define QSERDES_V2_TX_RESET_TSYNC_EN 0x034 +#define QSERDES_V2_TX_PRE_STALL_LDO_BOOST_EN 0x038 +#define QSERDES_V2_TX_TX_BAND 0x03c +#define QSERDES_V2_TX_SLEW_CNTL 0x040 +#define QSERDES_V2_TX_INTERFACE_SELECT 0x044 +#define QSERDES_V2_TX_LPB_EN 0x048 +#define QSERDES_V2_TX_RES_CODE_LANE_TX 0x04c +#define QSERDES_V2_TX_RES_CODE_LANE_RX 0x050 +#define QSERDES_V2_TX_RES_CODE_LANE_OFFSET 0x054 +#define QSERDES_V2_TX_PERL_LENGTH1 0x058 +#define QSERDES_V2_TX_PERL_LENGTH2 0x05c +#define QSERDES_V2_TX_SERDES_BYP_EN_OUT 0x060 +#define QSERDES_V2_TX_DEBUG_BUS_SEL 0x064 +#define QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 +#define QSERDES_V2_TX_TX_POL_INV 0x06c +#define QSERDES_V2_TX_PARRATE_REC_DETECT_IDLE_EN 0x070 +#define QSERDES_V2_TX_BIST_PATTERN1 0x074 +#define QSERDES_V2_TX_BIST_PATTERN2 0x078 +#define QSERDES_V2_TX_BIST_PATTERN3 0x07c +#define QSERDES_V2_TX_BIST_PATTERN4 0x080 +#define QSERDES_V2_TX_BIST_PATTERN5 0x084 +#define QSERDES_V2_TX_BIST_PATTERN6 0x088 +#define QSERDES_V2_TX_BIST_PATTERN7 0x08c +#define QSERDES_V2_TX_BIST_PATTERN8 0x090 +#define QSERDES_V2_TX_LANE_MODE 0x094 +#define QSERDES_V2_TX_IDAC_CAL_LANE_MODE 0x098 +#define QSERDES_V2_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x09c +#define QSERDES_V2_TX_ATB_SEL1 0x0a0 +#define QSERDES_V2_TX_ATB_SEL2 0x0a4 +#define QSERDES_V2_TX_RCV_DETECT_LVL 0x0a8 +#define QSERDES_V2_TX_RCV_DETECT_LVL_2 0x0ac +#define QSERDES_V2_TX_PRBS_SEED1 0x0b0 +#define QSERDES_V2_TX_PRBS_SEED2 0x0b4 +#define QSERDES_V2_TX_PRBS_SEED3 0x0b8 +#define QSERDES_V2_TX_PRBS_SEED4 0x0bc +#define QSERDES_V2_TX_RESET_GEN 0x0c0 +#define QSERDES_V2_TX_RESET_GEN_MUXES 0x0c4 +#define QSERDES_V2_TX_TRAN_DRVR_EMP_EN 0x0c8 +#define QSERDES_V2_TX_TX_INTERFACE_MODE 0x0cc +#define QSERDES_V2_TX_PWM_CTRL 0x0d0 +#define QSERDES_V2_TX_PWM_ENCODED_OR_DATA 0x0d4 +#define QSERDES_V2_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0d8 +#define QSERDES_V2_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0dc +#define QSERDES_V2_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0e0 +#define QSERDES_V2_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0e4 +#define QSERDES_V2_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0e8 +#define QSERDES_V2_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0ec +#define QSERDES_V2_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0f0 +#define QSERDES_V2_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0f4 +#define QSERDES_V2_TX_VMODE_CTRL1 0x0f8 +#define QSERDES_V2_TX_VMODE_CTRL2 0x0fc +#define QSERDES_V2_TX_TX_ALOG_INTF_OBSV_CNTL 0x100 +#define QSERDES_V2_TX_BIST_STATUS 0x104 +#define QSERDES_V2_TX_BIST_ERROR_COUNT1 0x108 +#define QSERDES_V2_TX_BIST_ERROR_COUNT2 0x10c +#define QSERDES_V2_TX_TX_ALOG_INTF_OBSV 0x110 + +/* Only for QMP V2 PHY - RX registers */ +#define QSERDES_V2_RX_UCDR_FO_GAIN_HALF 0x000 +#define QSERDES_V2_RX_UCDR_FO_GAIN_QUARTER 0x004 +#define QSERDES_V2_RX_UCDR_FO_GAIN_EIGHTH 0x008 +#define QSERDES_V2_RX_UCDR_FO_GAIN 0x00c +#define QSERDES_V2_RX_UCDR_SO_GAIN_HALF 0x010 +#define QSERDES_V2_RX_UCDR_SO_GAIN_QUARTER 0x014 +#define QSERDES_V2_RX_UCDR_SO_GAIN_EIGHTH 0x018 +#define QSERDES_V2_RX_UCDR_SO_GAIN 0x01c +#define QSERDES_V2_RX_UCDR_SVS_FO_GAIN_HALF 0x020 +#define QSERDES_V2_RX_UCDR_SVS_FO_GAIN_QUARTER 0x024 +#define QSERDES_V2_RX_UCDR_SVS_FO_GAIN_EIGHTH 0x028 +#define QSERDES_V2_RX_UCDR_SVS_FO_GAIN 0x02c +#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN_HALF 0x030 +#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN_QUARTER 0x034 +#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN_EIGHTH 0x038 +#define QSERDES_V2_RX_UCDR_SVS_SO_GAIN 0x03c +#define QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN 0x040 +#define QSERDES_V2_RX_UCDR_FD_GAIN 0x044 +#define QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 +#define QSERDES_V2_RX_UCDR_FO_TO_SO_DELAY 0x04c +#define QSERDES_V2_RX_UCDR_FASTLOCK_COUNT_LOW 0x050 +#define QSERDES_V2_RX_UCDR_FASTLOCK_COUNT_HIGH 0x054 +#define QSERDES_V2_RX_UCDR_MODULATE 0x058 +#define QSERDES_V2_RX_UCDR_PI_CONTROLS 0x05c +#define QSERDES_V2_RX_RBIST_CONTROL 0x060 +#define QSERDES_V2_RX_AUX_CONTROL 0x064 +#define QSERDES_V2_RX_AUX_DATA_TCOARSE 0x068 +#define QSERDES_V2_RX_AUX_DATA_TFINE_LSB 0x06c +#define QSERDES_V2_RX_AUX_DATA_TFINE_MSB 0x070 +#define QSERDES_V2_RX_RCLK_AUXDATA_SEL 0x074 +#define QSERDES_V2_RX_AC_JTAG_ENABLE 0x078 +#define QSERDES_V2_RX_AC_JTAG_INITP 0x07c +#define QSERDES_V2_RX_AC_JTAG_INITN 0x080 +#define QSERDES_V2_RX_AC_JTAG_LVL 0x084 +#define QSERDES_V2_RX_AC_JTAG_MODE 0x088 +#define QSERDES_V2_RX_AC_JTAG_RESET 0x08c +#define QSERDES_V2_RX_RX_TERM_BW 0x090 +#define QSERDES_V2_RX_RX_RCVR_IQ_EN 0x094 +#define QSERDES_V2_RX_RX_IDAC_I_DC_OFFSETS 0x098 +#define QSERDES_V2_RX_RX_IDAC_IBAR_DC_OFFSETS 0x09c +#define QSERDES_V2_RX_RX_IDAC_Q_DC_OFFSETS 0x0a0 +#define QSERDES_V2_RX_RX_IDAC_QBAR_DC_OFFSETS 0x0a4 +#define QSERDES_V2_RX_RX_IDAC_A_DC_OFFSETS 0x0a8 +#define QSERDES_V2_RX_RX_IDAC_ABAR_DC_OFFSETS 0x0ac +#define QSERDES_V2_RX_RX_IDAC_EN 0x0b0 +#define QSERDES_V2_RX_RX_IDAC_ENABLES 0x0b4 +#define QSERDES_V2_RX_RX_IDAC_SIGN 0x0b8 +#define QSERDES_V2_RX_RX_HIGHZ_HIGHRATE 0x0bc +#define QSERDES_V2_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0c0 +#define QSERDES_V2_RX_RX_EQ_GAIN1_LSB 0x0c4 +#define QSERDES_V2_RX_RX_EQ_GAIN1_MSB 0x0c8 +#define QSERDES_V2_RX_RX_EQ_GAIN2_LSB 0x0cc +#define QSERDES_V2_RX_RX_EQ_GAIN2_MSB 0x0d0 +#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d4 +#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 +#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc +#define QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 +#define QSERDES_V2_RX_RX_IDAC_CAL_CONFIGURATION 0x0e4 +#define QSERDES_V2_RX_RX_IDAC_TSETTLE_LOW 0x0e8 +#define QSERDES_V2_RX_RX_IDAC_TSETTLE_HIGH 0x0ec +#define QSERDES_V2_RX_RX_IDAC_ENDSAMP_LOW 0x0f0 +#define QSERDES_V2_RX_RX_IDAC_ENDSAMP_HIGH 0x0f4 +#define QSERDES_V2_RX_RX_IDAC_MIDPOINT_LOW 0x0f8 +#define QSERDES_V2_RX_RX_IDAC_MIDPOINT_HIGH 0x0fc +#define QSERDES_V2_RX_RX_EQ_OFFSET_LSB 0x100 +#define QSERDES_V2_RX_RX_EQ_OFFSET_MSB 0x104 +#define QSERDES_V2_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 +#define QSERDES_V2_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c +#define QSERDES_V2_RX_SIGDET_ENABLES 0x110 +#define QSERDES_V2_RX_SIGDET_CNTRL 0x114 +#define QSERDES_V2_RX_SIGDET_LVL 0x118 +#define QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL 0x11c +#define QSERDES_V2_RX_RX_BAND 0x120 +#define QSERDES_V2_RX_CDR_FREEZE_UP_DN 0x124 +#define QSERDES_V2_RX_CDR_RESET_OVERRIDE 0x128 +#define QSERDES_V2_RX_RX_INTERFACE_MODE 0x12c +#define QSERDES_V2_RX_JITTER_GEN_MODE 0x130 +#define QSERDES_V2_RX_BUJ_AMP 0x134 +#define QSERDES_V2_RX_SJ_AMP1 0x138 +#define QSERDES_V2_RX_SJ_AMP2 0x13c +#define QSERDES_V2_RX_SJ_PER1 0x140 +#define QSERDES_V2_RX_SJ_PER2 0x144 +#define QSERDES_V2_RX_BUJ_STEP_FREQ1 0x148 +#define QSERDES_V2_RX_BUJ_STEP_FREQ2 0x14c +#define QSERDES_V2_RX_PPM_OFFSET1 0x150 +#define QSERDES_V2_RX_PPM_OFFSET2 0x154 +#define QSERDES_V2_RX_SIGN_PPM_PERIOD1 0x158 +#define QSERDES_V2_RX_SIGN_PPM_PERIOD2 0x15c +#define QSERDES_V2_RX_SSC_CTRL 0x160 +#define QSERDES_V2_RX_SSC_COUNT1 0x164 +#define QSERDES_V2_RX_SSC_COUNT2 0x168 +#define QSERDES_V2_RX_RX_ALOG_INTF_OBSV_CNTL 0x16c +#define QSERDES_V2_RX_RX_PWM_ENABLE_AND_DATA 0x170 +#define QSERDES_V2_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x174 +#define QSERDES_V2_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x178 +#define QSERDES_V2_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x17c +#define QSERDES_V2_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x180 +#define QSERDES_V2_RX_PI_CTRL1 0x184 +#define QSERDES_V2_RX_PI_CTRL2 0x188 +#define QSERDES_V2_RX_PI_QUAD 0x18c +#define QSERDES_V2_RX_IDATA1 0x190 +#define QSERDES_V2_RX_IDATA2 0x194 +#define QSERDES_V2_RX_AUX_DATA1 0x198 +#define QSERDES_V2_RX_AUX_DATA2 0x19c +#define QSERDES_V2_RX_AC_JTAG_OUTP 0x1a0 +#define QSERDES_V2_RX_AC_JTAG_OUTN 0x1a4 +#define QSERDES_V2_RX_RX_SIGDET 0x1a8 +#define QSERDES_V2_RX_RX_VDCOFF 0x1ac +#define QSERDES_V2_RX_IDAC_CAL_ON 0x1b0 +#define QSERDES_V2_RX_IDAC_STATUS_I 0x1b4 +#define QSERDES_V2_RX_IDAC_STATUS_IBAR 0x1b8 +#define QSERDES_V2_RX_IDAC_STATUS_Q 0x1bc +#define QSERDES_V2_RX_IDAC_STATUS_QBAR 0x1c0 +#define QSERDES_V2_RX_IDAC_STATUS_A 0x1c4 +#define QSERDES_V2_RX_IDAC_STATUS_ABAR 0x1c8 +#define QSERDES_V2_RX_CALST_STATUS_I 0x1cc +#define QSERDES_V2_RX_CALST_STATUS_Q 0x1d0 +#define QSERDES_V2_RX_CALST_STATUS_A 0x1d4 +#define QSERDES_V2_RX_RX_ALOG_INTF_OBSV 0x1d8 +#define QSERDES_V2_RX_READ_EQCODE 0x1dc +#define QSERDES_V2_RX_READ_OFFSETCODE 0x1e0 +#define QSERDES_V2_RX_IA_ERROR_COUNTER_LOW 0x1e4 +#define QSERDES_V2_RX_IA_ERROR_COUNTER_HIGH 0x1e8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h deleted file mode 100644 index d20694513eb4..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx.h +++ /dev/null @@ -1,205 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - -#ifndef QCOM_PHY_QMP_QSERDES_TXRX_H_ -#define QCOM_PHY_QMP_QSERDES_TXRX_H_ - -/* Only for QMP V2 PHY - TX registers */ -#define QSERDES_TX_BIST_MODE_LANENO 0x000 -#define QSERDES_TX_BIST_INVERT 0x004 -#define QSERDES_TX_CLKBUF_ENABLE 0x008 -#define QSERDES_TX_CMN_CONTROL_ONE 0x00c -#define QSERDES_TX_CMN_CONTROL_TWO 0x010 -#define QSERDES_TX_CMN_CONTROL_THREE 0x014 -#define QSERDES_TX_TX_EMP_POST1_LVL 0x018 -#define QSERDES_TX_TX_POST2_EMPH 0x01c -#define QSERDES_TX_TX_BOOST_LVL_UP_DN 0x020 -#define QSERDES_TX_HP_PD_ENABLES 0x024 -#define QSERDES_TX_TX_IDLE_LVL_LARGE_AMP 0x028 -#define QSERDES_TX_TX_DRV_LVL 0x02c -#define QSERDES_TX_TX_DRV_LVL_OFFSET 0x030 -#define QSERDES_TX_RESET_TSYNC_EN 0x034 -#define QSERDES_TX_PRE_STALL_LDO_BOOST_EN 0x038 -#define QSERDES_TX_TX_BAND 0x03c -#define QSERDES_TX_SLEW_CNTL 0x040 -#define QSERDES_TX_INTERFACE_SELECT 0x044 -#define QSERDES_TX_LPB_EN 0x048 -#define QSERDES_TX_RES_CODE_LANE_TX 0x04c -#define QSERDES_TX_RES_CODE_LANE_RX 0x050 -#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 -#define QSERDES_TX_PERL_LENGTH1 0x058 -#define QSERDES_TX_PERL_LENGTH2 0x05c -#define QSERDES_TX_SERDES_BYP_EN_OUT 0x060 -#define QSERDES_TX_DEBUG_BUS_SEL 0x064 -#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 -#define QSERDES_TX_TX_POL_INV 0x06c -#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x070 -#define QSERDES_TX_BIST_PATTERN1 0x074 -#define QSERDES_TX_BIST_PATTERN2 0x078 -#define QSERDES_TX_BIST_PATTERN3 0x07c -#define QSERDES_TX_BIST_PATTERN4 0x080 -#define QSERDES_TX_BIST_PATTERN5 0x084 -#define QSERDES_TX_BIST_PATTERN6 0x088 -#define QSERDES_TX_BIST_PATTERN7 0x08c -#define QSERDES_TX_BIST_PATTERN8 0x090 -#define QSERDES_TX_LANE_MODE 0x094 -#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x098 -#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x09c -#define QSERDES_TX_ATB_SEL1 0x0a0 -#define QSERDES_TX_ATB_SEL2 0x0a4 -#define QSERDES_TX_RCV_DETECT_LVL 0x0a8 -#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac -#define QSERDES_TX_PRBS_SEED1 0x0b0 -#define QSERDES_TX_PRBS_SEED2 0x0b4 -#define QSERDES_TX_PRBS_SEED3 0x0b8 -#define QSERDES_TX_PRBS_SEED4 0x0bc -#define QSERDES_TX_RESET_GEN 0x0c0 -#define QSERDES_TX_RESET_GEN_MUXES 0x0c4 -#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x0c8 -#define QSERDES_TX_TX_INTERFACE_MODE 0x0cc -#define QSERDES_TX_PWM_CTRL 0x0d0 -#define QSERDES_TX_PWM_ENCODED_OR_DATA 0x0d4 -#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND2 0x0d8 -#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND2 0x0dc -#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND2 0x0e0 -#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND2 0x0e4 -#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x0e8 -#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x0ec -#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x0f0 -#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x0f4 -#define QSERDES_TX_VMODE_CTRL1 0x0f8 -#define QSERDES_TX_VMODE_CTRL2 0x0fc -#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x100 -#define QSERDES_TX_BIST_STATUS 0x104 -#define QSERDES_TX_BIST_ERROR_COUNT1 0x108 -#define QSERDES_TX_BIST_ERROR_COUNT2 0x10c -#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x110 - -/* Only for QMP V2 PHY - RX registers */ -#define QSERDES_RX_UCDR_FO_GAIN_HALF 0x000 -#define QSERDES_RX_UCDR_FO_GAIN_QUARTER 0x004 -#define QSERDES_RX_UCDR_FO_GAIN_EIGHTH 0x008 -#define QSERDES_RX_UCDR_FO_GAIN 0x00c -#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 -#define QSERDES_RX_UCDR_SO_GAIN_QUARTER 0x014 -#define QSERDES_RX_UCDR_SO_GAIN_EIGHTH 0x018 -#define QSERDES_RX_UCDR_SO_GAIN 0x01c -#define QSERDES_RX_UCDR_SVS_FO_GAIN_HALF 0x020 -#define QSERDES_RX_UCDR_SVS_FO_GAIN_QUARTER 0x024 -#define QSERDES_RX_UCDR_SVS_FO_GAIN_EIGHTH 0x028 -#define QSERDES_RX_UCDR_SVS_FO_GAIN 0x02c -#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF 0x030 -#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER 0x034 -#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH 0x038 -#define QSERDES_RX_UCDR_SVS_SO_GAIN 0x03c -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 -#define QSERDES_RX_UCDR_FD_GAIN 0x044 -#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 -#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x04c -#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW 0x050 -#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH 0x054 -#define QSERDES_RX_UCDR_MODULATE 0x058 -#define QSERDES_RX_UCDR_PI_CONTROLS 0x05c -#define QSERDES_RX_RBIST_CONTROL 0x060 -#define QSERDES_RX_AUX_CONTROL 0x064 -#define QSERDES_RX_AUX_DATA_TCOARSE 0x068 -#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x06c -#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x070 -#define QSERDES_RX_RCLK_AUXDATA_SEL 0x074 -#define QSERDES_RX_AC_JTAG_ENABLE 0x078 -#define QSERDES_RX_AC_JTAG_INITP 0x07c -#define QSERDES_RX_AC_JTAG_INITN 0x080 -#define QSERDES_RX_AC_JTAG_LVL 0x084 -#define QSERDES_RX_AC_JTAG_MODE 0x088 -#define QSERDES_RX_AC_JTAG_RESET 0x08c -#define QSERDES_RX_RX_TERM_BW 0x090 -#define QSERDES_RX_RX_RCVR_IQ_EN 0x094 -#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x098 -#define QSERDES_RX_RX_IDAC_IBAR_DC_OFFSETS 0x09c -#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x0a0 -#define QSERDES_RX_RX_IDAC_QBAR_DC_OFFSETS 0x0a4 -#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x0a8 -#define QSERDES_RX_RX_IDAC_ABAR_DC_OFFSETS 0x0ac -#define QSERDES_RX_RX_IDAC_EN 0x0b0 -#define QSERDES_RX_RX_IDAC_ENABLES 0x0b4 -#define QSERDES_RX_RX_IDAC_SIGN 0x0b8 -#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x0bc -#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x0c0 -#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 -#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 -#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc -#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x0d4 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 -#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x0e4 -#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x0e8 -#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x0ec -#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x0f0 -#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x0f4 -#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x0f8 -#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x0fc -#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x100 -#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x104 -#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 -#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c -#define QSERDES_RX_SIGDET_ENABLES 0x110 -#define QSERDES_RX_SIGDET_CNTRL 0x114 -#define QSERDES_RX_SIGDET_LVL 0x118 -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c -#define QSERDES_RX_RX_BAND 0x120 -#define QSERDES_RX_CDR_FREEZE_UP_DN 0x124 -#define QSERDES_RX_CDR_RESET_OVERRIDE 0x128 -#define QSERDES_RX_RX_INTERFACE_MODE 0x12c -#define QSERDES_RX_JITTER_GEN_MODE 0x130 -#define QSERDES_RX_BUJ_AMP 0x134 -#define QSERDES_RX_SJ_AMP1 0x138 -#define QSERDES_RX_SJ_AMP2 0x13c -#define QSERDES_RX_SJ_PER1 0x140 -#define QSERDES_RX_SJ_PER2 0x144 -#define QSERDES_RX_BUJ_STEP_FREQ1 0x148 -#define QSERDES_RX_BUJ_STEP_FREQ2 0x14c -#define QSERDES_RX_PPM_OFFSET1 0x150 -#define QSERDES_RX_PPM_OFFSET2 0x154 -#define QSERDES_RX_SIGN_PPM_PERIOD1 0x158 -#define QSERDES_RX_SIGN_PPM_PERIOD2 0x15c -#define QSERDES_RX_SSC_CTRL 0x160 -#define QSERDES_RX_SSC_COUNT1 0x164 -#define QSERDES_RX_SSC_COUNT2 0x168 -#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x16c -#define QSERDES_RX_RX_PWM_ENABLE_AND_DATA 0x170 -#define QSERDES_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x174 -#define QSERDES_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x178 -#define QSERDES_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x17c -#define QSERDES_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x180 -#define QSERDES_RX_PI_CTRL1 0x184 -#define QSERDES_RX_PI_CTRL2 0x188 -#define QSERDES_RX_PI_QUAD 0x18c -#define QSERDES_RX_IDATA1 0x190 -#define QSERDES_RX_IDATA2 0x194 -#define QSERDES_RX_AUX_DATA1 0x198 -#define QSERDES_RX_AUX_DATA2 0x19c -#define QSERDES_RX_AC_JTAG_OUTP 0x1a0 -#define QSERDES_RX_AC_JTAG_OUTN 0x1a4 -#define QSERDES_RX_RX_SIGDET 0x1a8 -#define QSERDES_RX_RX_VDCOFF 0x1ac -#define QSERDES_RX_IDAC_CAL_ON 0x1b0 -#define QSERDES_RX_IDAC_STATUS_I 0x1b4 -#define QSERDES_RX_IDAC_STATUS_IBAR 0x1b8 -#define QSERDES_RX_IDAC_STATUS_Q 0x1bc -#define QSERDES_RX_IDAC_STATUS_QBAR 0x1c0 -#define QSERDES_RX_IDAC_STATUS_A 0x1c4 -#define QSERDES_RX_IDAC_STATUS_ABAR 0x1c8 -#define QSERDES_RX_CALST_STATUS_I 0x1cc -#define QSERDES_RX_CALST_STATUS_Q 0x1d0 -#define QSERDES_RX_CALST_STATUS_A 0x1d4 -#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x1d8 -#define QSERDES_RX_READ_EQCODE 0x1dc -#define QSERDES_RX_READ_OFFSETCODE 0x1e0 -#define QSERDES_RX_IA_ERROR_COUNTER_LOW 0x1e4 -#define QSERDES_RX_IA_ERROR_COUNTER_HIGH 0x1e8 - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 48252ab0379e..c7fc19a2aed1 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -196,22 +196,22 @@ static const struct qmp_phy_init_tbl msm8996_ufsphy_serdes[] = { }; static const struct qmp_phy_init_tbl msm8996_ufsphy_tx[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x02), }; static const struct qmp_phy_init_tbl msm8996_ufsphy_rx[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5b), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xff), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xff), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_INTERFACE_MODE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_TERM_BW, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN1_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN1_MSB, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN2_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN2_MSB, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), }; static const struct qmp_phy_init_tbl sc7280_ufsphy_tx[] = { @@ -377,26 +377,26 @@ static const struct qmp_phy_init_tbl sm6115_ufsphy_hs_b_serdes[] = { }; static const struct qmp_phy_init_tbl sm6115_ufsphy_tx[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x06), }; static const struct qmp_phy_init_tbl sm6115_ufsphy_rx[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x0F), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x40), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5B), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x3F), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SVS_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_CNTRL, 0x0F), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x1E), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_TERM_BW, 0x5B), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN1_LSB, 0xFF), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN1_MSB, 0x3F), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN2_LSB, 0xFF), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_GAIN2_MSB, 0x3F), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0D), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SVS_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SVS_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x5B), }; static const struct qmp_phy_init_tbl sm6115_ufsphy_pcs[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index f43650f9a45c..c5507168e135 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -248,7 +248,7 @@ static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), @@ -281,22 +281,22 @@ static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl ipq9574_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x06), }; static const struct qmp_phy_init_tbl ipq9574_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_ENABLES, 0x0c), }; static const struct qmp_phy_init_tbl ipq9574_usb3_pcs_tbl[] = { @@ -330,7 +330,7 @@ static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x08), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_SELECT, 0x30), QMP_PHY_INIT_CFG(QSERDES_V2_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), QMP_PHY_INIT_CFG(QSERDES_V2_COM_SVS_MODE_CLK_SEL, 0x01), QMP_PHY_INIT_CFG(QSERDES_V2_COM_HSCLK_SEL, 0x00), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CMN_CONFIG, 0x06), @@ -363,15 +363,15 @@ static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl ipq8074_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_ENABLES, 0x0), }; static const struct qmp_phy_init_tbl ipq8074_usb3_pcs_tbl[] = { @@ -438,22 +438,22 @@ static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { }; static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V2_TX_LANE_MODE, 0x06), }; static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_LVL, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V2_RX_SIGDET_DEGLITCH_CNTRL, 0x16), }; static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 19e91f44e84e..11b7e03b4fab 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -6,9 +6,8 @@ #ifndef QCOM_PHY_QMP_H_ #define QCOM_PHY_QMP_H_ -#include "phy-qcom-qmp-qserdes-txrx.h" - #include "phy-qcom-qmp-qserdes-com-v2.h" +#include "phy-qcom-qmp-qserdes-txrx-v2.h" #include "phy-qcom-qmp-qserdes-com-v3.h" #include "phy-qcom-qmp-qserdes-txrx-v3.h" From 0cc64561b03d755bba54cbd0cf05e9210ab40a13 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 28 Jan 2026 17:18:49 +0530 Subject: [PATCH 0694/5214] dt-bindings: phy: qcom-edp: Add reference clock for sa8775p eDP PHY The initial sa8775p eDP PHY binding contribution missed adding support for voting on the eDP reference clock. This went unnoticed because the UFS PHY driver happened to enable the same clock. After commit 77d2fa54a945 ("scsi: ufs: qcom : Refactor phy_power_on/off calls"), the eDP reference clock is no longer kept enabled, which results in the following PHY power-on failure: phy phy-aec2a00.phy.10: phy poweron failed --> -110 To fix this, explicit voting for the eDP reference clock is required. This patch adds the eDP reference clock for sa8775p eDP PHY and updates the corresponding example node. Signed-off-by: Ritesh Kumar Reviewed-by: Bjorn Andersson Acked-by: Dmitry Baryshkov Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260128114853.2543416-2-quic_riteshk@quicinc.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml | 6 ++++-- Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml index e2730a2f25cf..6c827cf9692b 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml @@ -200,9 +200,11 @@ examples: <0x0aec2000 0x1c8>; clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>, - <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_EDP_REF_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; #clock-cells = <1>; #phy-cells = <0>; diff --git a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml index 4a1daae3d8d4..0bf8bf4f66ac 100644 --- a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml @@ -74,6 +74,7 @@ allOf: compatible: enum: - qcom,glymur-dp-phy + - qcom,sa8775p-edp-phy - qcom,x1e80100-dp-phy then: properties: From a62bfbcf2db4ae6eb7a544a40b1075a81784ea41 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Mon, 2 Mar 2026 10:34:46 +0200 Subject: [PATCH 0695/5214] phy: qcom: m31-eusb2: Make USB repeater optional A repeater is not required for the PHY to function. On systems with multiple PHY instances connected to a multi-port controller, some PHYs may be unconnected. All PHYs must still probe successfully even without attached repeaters, otherwise the controller probe fails. So make it optional. Signed-off-by: Wesley Cheng [abel.vesa@oss.qualcomm.com: commit re-worded to reflect actual reason] Reviewed-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Signed-off-by: Abel Vesa Reviewed-by: Neil Armstrong Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260302-phy-qcom-m31-eusb2-make-repeater-optional-v2-1-dbf714c72056@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 68f1ba8fec4a..0bec8657149c 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -285,7 +285,7 @@ static int m31eusb2_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy->phy, phy); - phy->repeater = devm_of_phy_get_by_index(dev, dev->of_node, 0); + phy->repeater = devm_phy_optional_get(dev, NULL); if (IS_ERR(phy->repeater)) return dev_err_probe(dev, PTR_ERR(phy->repeater), "failed to get repeater\n"); From 905780855a320ab3dcf0e4eaebf544cb3e7b55f8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 21 Mar 2026 03:14:49 +0200 Subject: [PATCH 0696/5214] phy: lynx-28g: use timeouts when waiting for lane halt and reset There are various circumstances in which a lane halt, or a lane reset, will fail to complete. If this happens, it will hang the kernel, which only implements a busy loop with no timeout. The circumstances in which this will happen are all bugs in nature: - if we try to power off a powered off lane - if we try to power off a lane that uses a PLL locked onto the wrong refclk frequency (wrong RCW, but SoC boots anyway) Actually, unbounded loops in the kernel are a bad practice, so let's use read_poll_timeout() with a custom function that reads both LNaTRSTCTL (lane transmit control register) and LNaRRSTCTL (lane receive control register) and returns true when the request is done in both directions. The HLT_REQ bit has to clear, whereas the RST_DONE bit has to get set. Any time such an error happens, it is catastrophic and there is no point in trying to propagate it to our callers: - if lynx_28g_set_mode() -> lynx_28g_power_on() times out, we have already reconfigured the lane, but returning an error would tell the caller that we didn't - if lynx_28g_power_off() times out, again not much for the consumer to do to help get out of this situation - the phy_power_off() call is probably made from a context that the consumer can't cancel, or it is making it to return to a known state from a previous failure. So just print an error if timeouts happen and let the driver control flow continue. The entire point is just to not let the kernel freeze. Suggested-by: Josua Mayer Link: https://lore.kernel.org/lkml/d0c8bbf8-a0c5-469f-a148-de2235948c0f@solid-run.com/ Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260321011451.1557091-2-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 72 ++++++++++++++++++------ 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 63427fc34e26..a9ab92e3d1a9 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -249,6 +249,12 @@ #define CR(x) ((x) * 4) +#define LYNX_28G_LANE_HALT_SLEEP_US 100 +#define LYNX_28G_LANE_HALT_TIMEOUT_US 1000000 + +#define LYNX_28G_LANE_RESET_SLEEP_US 100 +#define LYNX_28G_LANE_RESET_TIMEOUT_US 1000000 + enum lynx_28g_eq_type { EQ_TYPE_NO_EQ = 0, EQ_TYPE_2TAP = 1, @@ -600,10 +606,29 @@ static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane, } } +static bool lynx_28g_lane_halt_done(struct lynx_28g_lane *lane) +{ + u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + u32 rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + + return !(trstctl & LNaTRSTCTL_HLT_REQ) && + !(rrstctl & LNaRRSTCTL_HLT_REQ); +} + +static bool lynx_28g_lane_reset_done(struct lynx_28g_lane *lane) +{ + u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + u32 rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + + return (trstctl & LNaTRSTCTL_RST_DONE) && + (rrstctl & LNaRRSTCTL_RST_DONE); +} + static int lynx_28g_power_off(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); - u32 trstctl, rrstctl; + bool done; + int err; if (!lane->powered_up) return 0; @@ -615,11 +640,14 @@ static int lynx_28g_power_off(struct phy *phy) LNaRRSTCTL_HLT_REQ); /* Wait until the halting process is complete */ - do { - trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while ((trstctl & LNaTRSTCTL_HLT_REQ) || - (rrstctl & LNaRRSTCTL_HLT_REQ)); + err = read_poll_timeout(lynx_28g_lane_halt_done, done, done, + LYNX_28G_LANE_HALT_SLEEP_US, + LYNX_28G_LANE_HALT_TIMEOUT_US, + false, lane); + if (err) { + dev_err(&phy->dev, "Lane %c halt failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } lane->powered_up = false; @@ -629,7 +657,8 @@ static int lynx_28g_power_off(struct phy *phy) static int lynx_28g_power_on(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); - u32 trstctl, rrstctl; + bool done; + int err; if (lane->powered_up) return 0; @@ -641,11 +670,14 @@ static int lynx_28g_power_on(struct phy *phy) LNaRRSTCTL_RST_REQ); /* Wait until the reset sequence is completed */ - do { - trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while (!(trstctl & LNaTRSTCTL_RST_DONE) || - !(rrstctl & LNaRRSTCTL_RST_DONE)); + err = read_poll_timeout(lynx_28g_lane_reset_done, done, done, + LYNX_28G_LANE_RESET_SLEEP_US, + LYNX_28G_LANE_RESET_TIMEOUT_US, + false, lane); + if (err) { + dev_err(&phy->dev, "Lane %c reset failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } lane->powered_up = true; @@ -1065,7 +1097,7 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) struct lynx_28g_priv *priv = work_to_lynx(work); struct lynx_28g_lane *lane; u32 rrstctl; - int i; + int err, i; for (i = 0; i < LYNX_28G_NUM_LANE; i++) { lane = &priv->lane[i]; @@ -1083,9 +1115,17 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) if (!(rrstctl & LNaRRSTCTL_CDR_LOCK)) { lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ, LNaRRSTCTL_RST_REQ); - do { - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while (!(rrstctl & LNaRRSTCTL_RST_DONE)); + + err = read_poll_timeout(lynx_28g_lane_read, rrstctl, + !!(rrstctl & LNaRRSTCTL_RST_DONE), + LYNX_28G_LANE_RESET_SLEEP_US, + LYNX_28G_LANE_RESET_TIMEOUT_US, + false, lane, LNaRRSTCTL); + if (err) { + dev_warn_once(&lane->phy->dev, + "Lane %c receiver reset failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } } mutex_unlock(&lane->phy->mutex); From 5d38f693f16a0e9470fda530e01994f35fed8644 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 21 Mar 2026 03:14:50 +0200 Subject: [PATCH 0697/5214] phy: lynx-28g: truly power the lanes up or down The current procedure for power_off() and power_on() is the same as the one used for major lane reconfiguration, aka halting. But this is incorrect. One would expect that a powered off lane causes the CDR (clock and data recovery) loop of the link partner to lose lock onto its RX stream (which suggests there are no longer any bit transitions => the channel is inactive). However, it can be observed that this does not take place (the CDR lock is still there), which means that a halted lane is not powered off. Implement the procedure mentioned in the block guide for powering down a lane, and then back on. This means: - lynx_28g_power_off() currently emits a HLT_REQ and waits for it to clear. Rename it to lynx_28g_lane_halt() and keep using it for lynx_28g_set_mode(). - lynx_28g_power_on() currently emits a RST_REQ and waits for it to clear. This is the reset procedure - rename it to lynx_28g_lane_reset(), and keep using it for lynx_28g_set_mode(). The "real" lynx_28g_power_off() should emit a STP_REQ and wait for it to clear. And the "real" lynx_28g_power_on() should clear the DIS bit and then perform a lane reset. Hook these methods to the phy_ops :: power_off() and power_on() instead. As opposed to lynx_28g_power_off() which is also directly hooked to the PHY API, lynx_28g_lane_halt() isn't; just to lynx_28g_set_mode(), and the call is being made in a state where we haven't yet made any change to the lane. So it does make some limited amount of sense to code this up such that if it fails, we make an attempt to reset the lane anyway and let the consumer know that phy_set_mode_ext() failed. Sort the field definitions in LNaTRSTCTL and LNaRRSTCTL in descending order, and add new definitions for STP_REQ and DIS, previously unused. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260321011451.1557091-3-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 103 +++++++++++++++++++---- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index a9ab92e3d1a9..1e2302799f8f 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -70,9 +70,11 @@ /* Lane a Tx Reset Control Register */ #define LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) -#define LNaTRSTCTL_HLT_REQ BIT(27) -#define LNaTRSTCTL_RST_DONE BIT(30) #define LNaTRSTCTL_RST_REQ BIT(31) +#define LNaTRSTCTL_RST_DONE BIT(30) +#define LNaTRSTCTL_HLT_REQ BIT(27) +#define LNaTRSTCTL_STP_REQ BIT(26) +#define LNaTRSTCTL_DIS BIT(24) /* Lane a Tx General Control Register */ #define LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) @@ -98,9 +100,11 @@ /* Lane a Rx Reset Control Register */ #define LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) -#define LNaRRSTCTL_HLT_REQ BIT(27) -#define LNaRRSTCTL_RST_DONE BIT(30) #define LNaRRSTCTL_RST_REQ BIT(31) +#define LNaRRSTCTL_RST_DONE BIT(30) +#define LNaRRSTCTL_HLT_REQ BIT(27) +#define LNaRRSTCTL_STP_REQ BIT(26) +#define LNaRRSTCTL_DIS BIT(24) #define LNaRRSTCTL_CDR_LOCK BIT(12) /* Lane a Rx General Control Register */ @@ -255,6 +259,9 @@ #define LYNX_28G_LANE_RESET_SLEEP_US 100 #define LYNX_28G_LANE_RESET_TIMEOUT_US 1000000 +#define LYNX_28G_LANE_STOP_SLEEP_US 100 +#define LYNX_28G_LANE_STOP_TIMEOUT_US 1000000 + enum lynx_28g_eq_type { EQ_TYPE_NO_EQ = 0, EQ_TYPE_2TAP = 1, @@ -615,6 +622,15 @@ static bool lynx_28g_lane_halt_done(struct lynx_28g_lane *lane) !(rrstctl & LNaRRSTCTL_HLT_REQ); } +static bool lynx_28g_lane_stop_done(struct lynx_28g_lane *lane) +{ + u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + u32 rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + + return !(trstctl & LNaTRSTCTL_STP_REQ) && + !(rrstctl & LNaRRSTCTL_STP_REQ); +} + static bool lynx_28g_lane_reset_done(struct lynx_28g_lane *lane) { u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); @@ -624,15 +640,13 @@ static bool lynx_28g_lane_reset_done(struct lynx_28g_lane *lane) (rrstctl & LNaRRSTCTL_RST_DONE); } -static int lynx_28g_power_off(struct phy *phy) +/* Halting puts the lane in a mode in which it can be reconfigured */ +static int lynx_28g_lane_halt(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); bool done; int err; - if (!lane->powered_up) - return 0; - /* Issue a halt request */ lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_HLT_REQ, LNaTRSTCTL_HLT_REQ); @@ -649,20 +663,15 @@ static int lynx_28g_power_off(struct phy *phy) 'A' + lane->id, ERR_PTR(err)); } - lane->powered_up = false; - - return 0; + return err; } -static int lynx_28g_power_on(struct phy *phy) +static int lynx_28g_lane_reset(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); bool done; int err; - if (lane->powered_up) - return 0; - /* Issue a reset request on the lane */ lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_RST_REQ, LNaTRSTCTL_RST_REQ); @@ -679,6 +688,61 @@ static int lynx_28g_power_on(struct phy *phy) 'A' + lane->id, ERR_PTR(err)); } + return err; +} + +static int lynx_28g_power_off(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + bool done; + int err; + + if (!lane->powered_up) + return 0; + + /* Issue a stop request */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_STP_REQ, + LNaTRSTCTL_STP_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_STP_REQ, + LNaRRSTCTL_STP_REQ); + + /* Wait until the stop process is complete */ + err = read_poll_timeout(lynx_28g_lane_stop_done, done, done, + LYNX_28G_LANE_STOP_SLEEP_US, + LYNX_28G_LANE_STOP_TIMEOUT_US, + false, lane); + if (err) { + dev_err(&phy->dev, "Lane %c stop failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } + + /* Power down the RX and TX portions of the lane */ + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_DIS, + LNaRRSTCTL_DIS); + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_DIS, + LNaTRSTCTL_DIS); + + lane->powered_up = false; + + return 0; +} + +static int lynx_28g_power_on(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + int err; + + if (lane->powered_up) + return 0; + + /* Power up the RX and TX portions of the lane */ + lynx_28g_lane_rmw(lane, LNaRRSTCTL, 0, LNaRRSTCTL_DIS); + lynx_28g_lane_rmw(lane, LNaTRSTCTL, 0, LNaTRSTCTL_DIS); + + err = lynx_28g_lane_reset(phy); + if (err) + return err; + lane->powered_up = true; return 0; @@ -992,8 +1056,11 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) /* If the lane is powered up, put the lane into the halt state while * the reconfiguration is being done. */ - if (powered_up) - lynx_28g_power_off(phy); + if (powered_up) { + err = lynx_28g_lane_halt(phy); + if (err) + goto out; + } err = lynx_28g_lane_disable_pcvt(lane, lane->mode); if (err) @@ -1007,7 +1074,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) out: if (powered_up) - lynx_28g_power_on(phy); + lynx_28g_lane_reset(phy); return err; } From 0ee5cc59c0ee679e1a3a749cfc47834041763494 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 21 Mar 2026 03:14:51 +0200 Subject: [PATCH 0698/5214] phy: lynx-28g: implement phy_exit() operation On Layerscape, some Lynx SerDes consumers go through the PHY framework (we call these 'managed' for the sake of this discussion; they are some Ethernet lanes), and some consumers don't go through the PHY framework (we call these 'unmanaged'; they are some unconverted Ethernet lanes, plus all SATA and PCIe controllers). A lane is initially unmanaged, and becomes managed when phy_init() is called on it. Similarly, it becomes unmanaged again when phy_exit() is called. Managed lanes are supposed to have power management through phy_power_on() and phy_power_off(). The lynx-28g SerDes driver, when it probes, probes on all lanes, but needs to be careful to keep the unmanaged lanes powered on, because those might be in use by consumers unaware that they need to call phy_init() and phy_power_on(). This also applies after phy_exit() is called - no guarantee is made about how the port may be used afterwards - may be DPDK, may be something else which is unaware of the PHY framework. Given this state table: State | Consumer calls phy_exit()? | Provider implements phy_exit()? -------+----------------------------+-------------------------------- (a) | no | no (b) | no | yes (c) | yes | no (d) | yes | yes we are currently in state (a). This has the problem that when the consumer fails to probe with -EPROBE_DEFER or is otherwise unbound, the phy->init_count remains elevated and the lane never returns to the unmanaged state (temporarily or not) as it should. Furthermore, the second and subsequent phy_init() consumer calls are never passed on to the provider driver. We solve the above problem by implementing phy_exit() in the consumer, and that moves us to state (c). But this creates the problem that a balanced set of phy_init() and phy_exit() calls from the consumer will effectively result in multiple lynx_28g_init() calls as seen by the SerDes and nothing else - as the optional phy_ops :: exit() is not implemented. But that actually doesn't work - the 28G Lynx SerDes can't power down a lane which is already powered down; that call sequence would time out and fail. So actually we want to be in state (d), where both the provider and the consumer implement phy_exit(). But we can only do that safely through intermediary state (b), where the provider implements it first. This effectively behaves just like (a), except it offers a safe migration path for the consumer to call phy_exit() as mandated by the Generic PHY API. Extra development notes: ignoring the lynx_28g_power_on() error in lynx_28g_exit() is a deliberate decision. The consumer can't deal with a teardown path that is not error-free. Ignoring the error is not silent: lynx_28g_power_on() -> lynx_28g_lane_reset() will print on failure. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260321011451.1557091-4-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 1e2302799f8f..4ec3fb7a0d69 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1113,8 +1113,25 @@ static int lynx_28g_init(struct phy *phy) return 0; } +static int lynx_28g_exit(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + + /* The lane returns to the state where it isn't managed by the + * consumer, so we must treat is as if it isn't initialized, and + * always powered on. + */ + lane->init = false; + lane->powered_up = false; + + lynx_28g_power_on(phy); + + return 0; +} + static const struct phy_ops lynx_28g_ops = { .init = lynx_28g_init, + .exit = lynx_28g_exit, .power_on = lynx_28g_power_on, .power_off = lynx_28g_power_off, .set_mode = lynx_28g_set_mode, From 53f60930e3d20883364fc01fd46b6099acb8127a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 19 Mar 2026 06:32:07 +0000 Subject: [PATCH 0699/5214] phy: renesas: phy-rzg3e-usb3: Fix malformed MODULE_AUTHOR string Fix a malformed MODULE_AUTHOR macro in the RZ/G3E USB3.0 PHY driver where the author's name and opening angle bracket were missing, leaving only the email address with a stray closing >. Correct it to the standard Name format. Reported-by: Pavel Machek Closes: https://lore.kernel.org/all/abp4KprlYyU+jMPu@duo.ucw.cz/ Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://patch.msgid.link/20260319063211.5056-1-biju.das.jz@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rzg3e-usb3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rzg3e-usb3.c b/drivers/phy/renesas/phy-rzg3e-usb3.c index 6b3453ea0004..030c600a53e6 100644 --- a/drivers/phy/renesas/phy-rzg3e-usb3.c +++ b/drivers/phy/renesas/phy-rzg3e-usb3.c @@ -256,4 +256,4 @@ module_platform_driver(rzg3e_phy_usb3_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Renesas RZ/G3E USB3.0 PHY Driver"); -MODULE_AUTHOR("biju.das.jz@bp.renesas.com>"); +MODULE_AUTHOR("Biju Das "); From b6e33443876d0ca7e93cf949455e3c1a1a0aae24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Mon, 9 Mar 2026 15:37:34 +0100 Subject: [PATCH 0700/5214] phy: Add driver for EyeQ5 Ethernet PHY wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EyeQ5 embeds a system-controller called OLB. It features many unrelated registers, and some of those are registers used to configure the integration of the RGMII/SGMII Cadence PHY used by MACB/GEM instances. Wrap in a neat generic PHY provider, exposing two PHYs with standard phy_init() / phy_set_mode() / phy_power_on() operations. Reviewed-by: Luca Ceresoli Signed-off-by: Théo Lebrun Reviewed-by: Vladimir Oltean Link: https://patch.msgid.link/20260309-macb-phy-v9-1-5afd87d9db43@bootlin.com Signed-off-by: Vinod Koul --- MAINTAINERS | 1 + drivers/phy/Kconfig | 13 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-eyeq5-eth.c | 280 ++++++++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 drivers/phy/phy-eyeq5-eth.c diff --git a/MAINTAINERS b/MAINTAINERS index cf179372c7f0..421dd786b399 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17958,6 +17958,7 @@ F: arch/mips/boot/dts/mobileye/ F: arch/mips/configs/eyeq*_defconfig F: arch/mips/mobileye/board-epm5.its.S F: drivers/clk/clk-eyeq.c +F: drivers/phy/phy-eyeq5-eth.c F: drivers/pinctrl/pinctrl-eyeq5.c F: drivers/reset/reset-eyeq.c F: include/dt-bindings/clock/mobileye,eyeq5-clk.h diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 872a7f02e1c4..ab96ee5858c1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -66,6 +66,19 @@ config PHY_CAN_TRANSCEIVER functional modes using gpios and sets the attribute max link rate, for CAN drivers. +config PHY_EYEQ5_ETH + tristate "Ethernet PHY Driver on EyeQ5" + depends on OF + depends on MACH_EYEQ5 || COMPILE_TEST + select AUXILIARY_BUS + select GENERIC_PHY + default MACH_EYEQ5 + help + Enable this to support the Ethernet PHY integrated on EyeQ5. + It supports both RGMII and SGMII. Registers are located in a + shared register region called OLB. If M is selected, the + module will be called phy-eyeq5-eth. + config PHY_GOOGLE_USB tristate "Google Tensor SoC USB PHY driver" select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ae756fea473b..f31767745123 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o +obj-$(CONFIG_PHY_EYEQ5_ETH) += phy-eyeq5-eth.o obj-$(CONFIG_PHY_GOOGLE_USB) += phy-google-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o diff --git a/drivers/phy/phy-eyeq5-eth.c b/drivers/phy/phy-eyeq5-eth.c new file mode 100644 index 000000000000..c03d77c360f7 --- /dev/null +++ b/drivers/phy/phy-eyeq5-eth.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EQ5_PHY_COUNT 2 + +#define EQ5_PHY0_GP 0x128 +#define EQ5_PHY1_GP 0x12c +#define EQ5_PHY0_SGMII 0x134 +#define EQ5_PHY1_SGMII 0x138 + +#define EQ5_GP_TX_SWRST_DIS BIT(0) // Tx SW reset +#define EQ5_GP_TX_M_CLKE BIT(1) // Tx M clock enable +#define EQ5_GP_SYS_SWRST_DIS BIT(2) // Sys SW reset +#define EQ5_GP_SYS_M_CLKE BIT(3) // Sys clock enable +#define EQ5_GP_SGMII_MODE BIT(4) // SGMII mode +#define EQ5_GP_RGMII_DRV GENMASK(8, 5) // RGMII drive strength + +#define EQ5_SGMII_PWR_EN BIT(0) +#define EQ5_SGMII_RST_DIS BIT(1) +#define EQ5_SGMII_PLL_EN BIT(2) +#define EQ5_SGMII_SIG_DET_SW BIT(3) +#define EQ5_SGMII_PWR_STATE BIT(4) +#define EQ5_SGMII_PLL_ACK BIT(18) +#define EQ5_SGMII_PWR_STATE_ACK GENMASK(24, 20) + +/* + * Instead of storing a phy_interface_t, we store this enum. + * + * We do not deal with RGMII timings in this generic PHY driver, + * it is all handled inside the net PHY. + */ +enum eq5_phy_submode { + EQ5_PHY_SUBMODE_SGMII, + EQ5_PHY_SUBMODE_RGMII, +}; + +struct eq5_phy_inst { + struct device *dev; + struct phy *phy; + void __iomem *gp, *sgmii; + enum eq5_phy_submode submode; + bool sgmii_support; +}; + +struct eq5_phy_private { + struct eq5_phy_inst phys[EQ5_PHY_COUNT]; +}; + +static int eq5_phy_exit(struct phy *phy) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + + writel(0, inst->gp); + writel(0, inst->sgmii); + udelay(5); /* settling time */ + return 0; +} + +static int eq5_phy_init(struct phy *phy) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + u32 reg; + + /* + * Hardware stops listening to our instructions once it is started. + * It must be reset to reconfigure it. + */ + eq5_phy_exit(phy); + + reg = EQ5_GP_TX_SWRST_DIS | EQ5_GP_TX_M_CLKE | + EQ5_GP_SYS_SWRST_DIS | EQ5_GP_SYS_M_CLKE | + FIELD_PREP(EQ5_GP_RGMII_DRV, 0x9); + writel(reg, inst->gp); + + return 0; +} + +static int eq5_phy_power_on(struct phy *phy) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + u32 reg; + + if (inst->submode == EQ5_PHY_SUBMODE_SGMII) { + writel(readl(inst->gp) | EQ5_GP_SGMII_MODE, inst->gp); + + reg = EQ5_SGMII_PWR_EN | EQ5_SGMII_RST_DIS | EQ5_SGMII_PLL_EN; + writel(reg, inst->sgmii); + + if (readl_poll_timeout(inst->sgmii, reg, + reg & EQ5_SGMII_PLL_ACK, 1, 100)) { + dev_err(inst->dev, "PLL timeout\n"); + return -ETIMEDOUT; + } + + reg = readl(inst->sgmii); + reg |= EQ5_SGMII_PWR_STATE | EQ5_SGMII_SIG_DET_SW; + writel(reg, inst->sgmii); + } else { + writel(readl(inst->gp) & ~EQ5_GP_SGMII_MODE, inst->gp); + writel(0, inst->sgmii); + } + + return 0; +} + +static int eq5_phy_power_off(struct phy *phy) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + + writel(readl(inst->gp) & ~EQ5_GP_SGMII_MODE, inst->gp); + writel(0, inst->sgmii); + + return 0; +} + +static int eq5_phy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + if (phy_interface_mode_is_rgmii(submode)) + return 0; + + if (inst->sgmii_support && submode == PHY_INTERFACE_MODE_SGMII) + return 0; + + return -EINVAL; +} + +static int eq5_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct eq5_phy_inst *inst = phy_get_drvdata(phy); + enum eq5_phy_submode target_submode; + int ret; + + ret = eq5_phy_validate(phy, mode, submode, NULL); + if (ret) + return ret; + + if (submode == PHY_INTERFACE_MODE_SGMII) + target_submode = EQ5_PHY_SUBMODE_SGMII; + else + target_submode = EQ5_PHY_SUBMODE_RGMII; + + if (target_submode == inst->submode) + return 0; + + inst->submode = target_submode; + + if (phy->power_count) { + eq5_phy_init(phy); + return eq5_phy_power_on(phy); + } + + return 0; +} + +static const struct phy_ops eq5_phy_ops = { + .init = eq5_phy_init, + .exit = eq5_phy_exit, + .power_on = eq5_phy_power_on, + .power_off = eq5_phy_power_off, + .set_mode = eq5_phy_set_mode, + .validate = eq5_phy_validate, +}; + +static struct phy *eq5_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct eq5_phy_private *priv = dev_get_drvdata(dev); + + if (args->args_count != 1 || args->args[0] >= EQ5_PHY_COUNT) + return ERR_PTR(-EINVAL); + + return priv->phys[args->args[0]].phy; +} + +static int eq5_phy_probe_phy(struct device *dev, struct eq5_phy_private *priv, + unsigned int index, void __iomem *base, + unsigned int gp, unsigned int sgmii, + bool sgmii_support) +{ + struct eq5_phy_inst *inst = &priv->phys[index]; + struct phy *phy; + + phy = devm_phy_create(dev, dev->of_node, &eq5_phy_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), + "failed to create PHY %u\n", index); + + inst->dev = dev; + inst->phy = phy; + inst->gp = base + gp; + inst->sgmii = base + sgmii; + inst->sgmii_support = sgmii_support; + phy_set_drvdata(phy, inst); + + /* + * Init inst->submode based on probe hardware state, allowing + * consumers to power us on without first setting the mode. + */ + if (sgmii_support && (readl(inst->gp) & EQ5_GP_SGMII_MODE)) + inst->submode = EQ5_PHY_SUBMODE_SGMII; + else + inst->submode = EQ5_PHY_SUBMODE_RGMII; + + return 0; +} + +static int eq5_phy_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct phy_provider *provider; + struct eq5_phy_private *priv; + void __iomem *base; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + base = (void __iomem *)dev_get_platdata(dev); + + ret = eq5_phy_probe_phy(dev, priv, 0, base, EQ5_PHY0_GP, + EQ5_PHY0_SGMII, true); + if (ret) + return ret; + + ret = eq5_phy_probe_phy(dev, priv, 1, base, EQ5_PHY1_GP, + EQ5_PHY1_SGMII, false); + if (ret) + return ret; + + provider = devm_of_phy_provider_register(dev, eq5_phy_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "registering provider failed\n"); + + return 0; +} + +static const struct auxiliary_device_id eq5_phy_id_table[] = { + { .name = "clk_eyeq.phy" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, eq5_phy_id_table); + +static struct auxiliary_driver eq5_phy_driver = { + .probe = eq5_phy_probe, + .id_table = eq5_phy_id_table, +}; +module_auxiliary_driver(eq5_phy_driver); + +MODULE_DESCRIPTION("EyeQ5 Ethernet PHY driver"); +MODULE_AUTHOR("Théo Lebrun "); +MODULE_LICENSE("GPL"); From cc68a1728abfcbde12d36015f244046ae74ddd44 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 6 Mar 2026 14:24:56 -0800 Subject: [PATCH 0701/5214] phy: miphy28lp: add COMPILE_TEST There's nothing special here to prevent compilation on non ARM hosts. Matches every other st phy driver. Signed-off-by: Rosen Penev Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260306222457.8400-2-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/st/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig index 304614b6dabf..49206185e563 100644 --- a/drivers/phy/st/Kconfig +++ b/drivers/phy/st/Kconfig @@ -4,7 +4,7 @@ # config PHY_MIPHY28LP tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" - depends on ARCH_STI + depends on ARCH_STI || COMPILE_TEST select GENERIC_PHY help Enable this to support the miphy transceiver (for SATA/PCIE/USB3) From f3508a61c892c592e4e893a3681e568a5c671027 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 6 Mar 2026 14:24:57 -0800 Subject: [PATCH 0702/5214] phy: miphy28lp: kzalloc + kcalloc to single kzalloc Use flex array to simplify allocation. Allows using __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260306222457.8400-3-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-miphy28lp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/phy/st/phy-miphy28lp.c b/drivers/phy/st/phy-miphy28lp.c index 43cef89af55e..c576fc5569fe 100644 --- a/drivers/phy/st/phy-miphy28lp.c +++ b/drivers/phy/st/phy-miphy28lp.c @@ -224,8 +224,8 @@ struct miphy28lp_dev { struct device *dev; struct regmap *regmap; struct mutex miphy_mutex; - struct miphy28lp_phy **phys; int nphys; + struct miphy28lp_phy *phys[] __counted_by(nphys); }; enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; @@ -1168,16 +1168,14 @@ static int miphy28lp_probe(struct platform_device *pdev) struct phy_provider *provider; struct phy *phy; int ret, port = 0; + size_t nphys; - miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + nphys = of_get_child_count(np); + miphy_dev = devm_kzalloc(&pdev->dev, struct_size(miphy_dev, phys, nphys), GFP_KERNEL); if (!miphy_dev) return -ENOMEM; - miphy_dev->nphys = of_get_child_count(np); - miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, - sizeof(*miphy_dev->phys), GFP_KERNEL); - if (!miphy_dev->phys) - return -ENOMEM; + miphy_dev->nphys = nphys; miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); if (IS_ERR(miphy_dev->regmap)) { From 057c81a17fffb17f66e5b4524d49b7caad3fe627 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 11 Feb 2026 15:49:48 +0100 Subject: [PATCH 0703/5214] phy: freescale: imx8qm-hsio: provide regmap names This driver uses multiple regmaps, which will causes name conflicts in debugfs like: debugfs: '5f1a0000.phy' already exists in 'regmap' Fix this by using a dedicated regmap config for each resource, each having a dedicated regmap name. Signed-off-by: Alexander Stein Reviewed-by: Neil Armstrong Reviewed-by: Frank Li Link: https://patch.msgid.link/20260211144949.1128122-1-alexander.stein@ew.tq-group.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8qm-hsio.c | 23 +++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c b/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c index 279b8ac7822d..4ab45c9f53df 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c @@ -107,7 +107,22 @@ static const char * const lan2_pcieb_clks[] = {"apb_pclk2", "pclk2", "ctl1_crr", static const char * const lan2_sata_clks[] = {"pclk2", "epcs_tx", "epcs_rx", "phy1_crr", "misc_crr"}; -static const struct regmap_config regmap_config = { +static const struct regmap_config regmap_phy_config = { + .name = "phy", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct regmap_config regmap_ctrl_config = { + .name = "ctrl", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct regmap_config regmap_misc_config = { + .name = "misc", .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -562,19 +577,19 @@ static int imx_hsio_probe(struct platform_device *pdev) return PTR_ERR(priv->base); off = devm_platform_ioremap_resource_byname(pdev, "phy"); - priv->phy = devm_regmap_init_mmio(dev, off, ®map_config); + priv->phy = devm_regmap_init_mmio(dev, off, ®map_phy_config); if (IS_ERR(priv->phy)) return dev_err_probe(dev, PTR_ERR(priv->phy), "unable to find phy csr registers\n"); off = devm_platform_ioremap_resource_byname(pdev, "ctrl"); - priv->ctrl = devm_regmap_init_mmio(dev, off, ®map_config); + priv->ctrl = devm_regmap_init_mmio(dev, off, ®map_ctrl_config); if (IS_ERR(priv->ctrl)) return dev_err_probe(dev, PTR_ERR(priv->ctrl), "unable to find ctrl csr registers\n"); off = devm_platform_ioremap_resource_byname(pdev, "misc"); - priv->misc = devm_regmap_init_mmio(dev, off, ®map_config); + priv->misc = devm_regmap_init_mmio(dev, off, ®map_misc_config); if (IS_ERR(priv->misc)) return dev_err_probe(dev, PTR_ERR(priv->misc), "unable to find misc csr registers\n"); From 9cfeef97f21be61372d718f7ee430ea65536bb08 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 5 Mar 2026 11:15:28 +0100 Subject: [PATCH 0704/5214] phy: renesas: rzg3e-usb3: Convert to FIELD_MODIFY() Use the FIELD_MODIFY() helper instead of open-coding the same operation. Signed-off-by: Geert Uytterhoeven Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/a52020ba597e2e213b161eee21239f10e6057d9d.1772705690.git.geert+renesas@glider.be Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rzg3e-usb3.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/phy/renesas/phy-rzg3e-usb3.c b/drivers/phy/renesas/phy-rzg3e-usb3.c index 030c600a53e6..060309547ea5 100644 --- a/drivers/phy/renesas/phy-rzg3e-usb3.c +++ b/drivers/phy/renesas/phy-rzg3e-usb3.c @@ -78,13 +78,11 @@ static void rzg3e_phy_usb2test_phy_init(void __iomem *base) writel(val, base + USB3_TEST_UTMICTRL2); val = readl(base + USB3_TEST_PRMCTRL5_R); - val &= ~USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK; - val |= FIELD_PREP(USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK, 2); + FIELD_MODIFY(USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK, &val, 2); writel(val, base + USB3_TEST_PRMCTRL5_R); val = readl(base + USB3_TEST_PRMCTRL6_R); - val &= ~USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK; - val |= FIELD_PREP(USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK, 7); + FIELD_MODIFY(USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK, &val, 7); writel(val, base + USB3_TEST_PRMCTRL6_R); val = readl(base + USB3_TEST_RESET); From 949aa12e030eac4424f4832eb93e96c20719ae7b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 May 2026 00:02:12 -0400 Subject: [PATCH 0705/5214] KVM: x86/mmu: separate more EPT/non-EPT permission_fault() Move more of EPT handling entirely in the existing "if (!ept)" conditional. Use a new "rf" variable instead of uf for read permissions for clarity. Merge smepf and ff into a single variable because EPT's "SMEP" (actually MBEC) is defined differently and does not need smepf. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index d94a488db79d..891b0cc5d38c 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5584,24 +5584,28 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) * that causes a fault with the given PFEC. */ + /* Faults from reads to non-readable pages */ + u8 rf = 0; /* Faults from writes to non-writable pages */ u8 wf = (pfec & PFERR_WRITE_MASK) ? (u8)~w : 0; /* Faults from user mode accesses to supervisor pages */ - u8 uf = (pfec & PFERR_USER_MASK) ? (u8)~u : 0; - /* Faults from fetches of non-executable pages*/ - u8 ff = (pfec & PFERR_FETCH_MASK) ? (u8)~x : 0; - /* Faults from kernel mode fetches of user pages */ - u8 smepf = 0; + u8 uf = 0; + /* Faults from fetches of non-executable pages */ + u8 ff = 0; /* Faults from kernel mode accesses of user pages */ u8 smapf = 0; - if (!ept) { + if (ept) { + rf = (pfec & PFERR_USER_MASK) ? (u8)~u : 0; + ff = (pfec & PFERR_FETCH_MASK) ? (u8)~x : 0; + } else { /* Faults from kernel mode accesses to user pages */ u8 kf = (pfec & PFERR_USER_MASK) ? 0 : u; - /* Not really needed: !nx will cause pte.nx to fault */ - if (!efer_nx) - ff = 0; + uf = (pfec & PFERR_USER_MASK) ? (u8)~u : 0; + + if (efer_nx) + ff |= (pfec & PFERR_FETCH_MASK) ? (u8)~x : 0; /* Allow supervisor writes if !cr0.wp */ if (!cr0_wp) @@ -5609,7 +5613,7 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) /* Disallow supervisor fetches of user code if cr4.smep */ if (cr4_smep) - smepf = (pfec & PFERR_FETCH_MASK) ? kf : 0; + ff |= (pfec & PFERR_FETCH_MASK) ? kf : 0; /* * SMAP:kernel-mode data accesses from user-mode @@ -5630,7 +5634,7 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) smapf = (pfec & (PFERR_RSVD_MASK|PFERR_FETCH_MASK)) ? 0 : kf; } - mmu->permissions[index] = ff | uf | wf | smepf | smapf; + mmu->permissions[index] = ff | uf | wf | rf | smapf; } } From a8827c19614629ee51f2355ceeea36b96d77eb60 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:41:58 -0400 Subject: [PATCH 0706/5214] KVM: x86/mmu: introduce ACC_READ_MASK Read permissions so far were only needed for EPT, which does not need ACC_USER_MASK. Therefore, for EPT page tables ACC_USER_MASK was repurposed as a read permission bit. In order to implement nested MBEC, EPT will genuinely have four kinds of accesses, and there will be no room for such hacks; bite the bullet at last, enlarging ACC_ALL to four bits and permissions[] to 2^4 bits (u16). The new code does not enforce that the XWR bits on non-execonly processors have their R bit set, even when running nested: none of the shadow_*_mask values have bit 0 set, and make_spte() genuinely relies on ACC_READ_MASK being requested! This works because, if execonly is not supported by the processor, shadow EPT will generate an EPT misconfig vmexit if the XWR bits represent a non-readable page, and therefore the pte_access argument to make_spte() will also always have ACC_READ_MASK set. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 12 ++++----- arch/x86/kvm/mmu.h | 2 +- arch/x86/kvm/mmu/mmu.c | 45 ++++++++++++++++++++------------- arch/x86/kvm/mmu/mmutrace.h | 3 ++- arch/x86/kvm/mmu/paging_tmpl.h | 35 +++++++++++++++---------- arch/x86/kvm/mmu/spte.c | 18 +++++-------- arch/x86/kvm/mmu/spte.h | 5 ++-- arch/x86/kvm/vmx/capabilities.h | 5 ---- arch/x86/kvm/vmx/common.h | 5 +--- arch/x86/kvm/vmx/vmx.c | 3 +-- 10 files changed, 69 insertions(+), 64 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c470e40a00aa..8f2a1b915df9 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -328,11 +328,11 @@ struct kvm_kernel_irq_routing_entry; * the number of unique SPs that can theoretically be created is 2^n, where n * is the number of bits that are used to compute the role. * - * But, even though there are 20 bits in the mask below, not all combinations + * But, even though there are 21 bits in the mask below, not all combinations * of modes and flags are possible: * * - invalid shadow pages are not accounted, mirror pages are not shadowed, - * so the bits are effectively 18. + * so the bits are effectively 19. * * - quadrant will only be used if has_4_byte_gpte=1 (non-PAE paging); * execonly and ad_disabled are only used for nested EPT which has @@ -347,7 +347,7 @@ struct kvm_kernel_irq_routing_entry; * cr0_wp=0, therefore these three bits only give rise to 5 possibilities. * * Therefore, the maximum number of possible upper-level shadow pages for a - * single gfn is a bit less than 2^13. + * single gfn is a bit less than 2^14. */ union kvm_mmu_page_role { u32 word; @@ -356,7 +356,7 @@ union kvm_mmu_page_role { unsigned has_4_byte_gpte:1; unsigned quadrant:2; unsigned direct:1; - unsigned access:3; + unsigned access:4; unsigned invalid:1; unsigned efer_nx:1; unsigned cr0_wp:1; @@ -366,7 +366,7 @@ union kvm_mmu_page_role { unsigned guest_mode:1; unsigned passthrough:1; unsigned is_mirror:1; - unsigned :4; + unsigned:3; /* * This is left at the top of the word so that @@ -492,7 +492,7 @@ struct kvm_mmu { * Byte index: page fault error code [4:1] * Bit index: pte permissions in ACC_* format */ - u8 permissions[16]; + u16 permissions[16]; u64 *pae_root; u64 *pml4_root; diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 830f46145692..23f37535c0ce 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -81,7 +81,7 @@ u8 kvm_mmu_get_max_tdp_level(void); void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 mmio_mask, u64 access_mask); void kvm_mmu_set_mmio_spte_value(struct kvm *kvm, u64 mmio_value); void kvm_mmu_set_me_spte_mask(u64 me_value, u64 me_mask); -void kvm_mmu_set_ept_masks(bool has_ad_bits, bool has_exec_only); +void kvm_mmu_set_ept_masks(bool has_ad_bits); void kvm_init_mmu(struct kvm_vcpu *vcpu); void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0, diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 891b0cc5d38c..c617837a5038 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2033,7 +2033,7 @@ static bool kvm_sync_page_check(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) */ const union kvm_mmu_page_role sync_role_ign = { .level = 0xf, - .access = 0x7, + .access = ACC_ALL, .quadrant = 0x3, .passthrough = 0x1, }; @@ -5539,7 +5539,7 @@ reset_ept_shadow_zero_bits_mask(struct kvm_mmu *context, bool execonly) * update_permission_bitmask() builds what is effectively a * two-dimensional array of bools. The second dimension is * provided by individual bits of permissions[pfec >> 1], and - * logical &, | and ~ operations operate on all the 8 possible + * logical &, | and ~ operations operate on all the 16 possible * combinations of ACC_* bits. */ #define ACC_BITS_MASK(access) \ @@ -5549,15 +5549,23 @@ reset_ept_shadow_zero_bits_mask(struct kvm_mmu *context, bool execonly) (4 & (access) ? 1 << 4 : 0) | \ (5 & (access) ? 1 << 5 : 0) | \ (6 & (access) ? 1 << 6 : 0) | \ - (7 & (access) ? 1 << 7 : 0)) + (7 & (access) ? 1 << 7 : 0) | \ + (8 & (access) ? 1 << 8 : 0) | \ + (9 & (access) ? 1 << 9 : 0) | \ + (10 & (access) ? 1 << 10 : 0) | \ + (11 & (access) ? 1 << 11 : 0) | \ + (12 & (access) ? 1 << 12 : 0) | \ + (13 & (access) ? 1 << 13 : 0) | \ + (14 & (access) ? 1 << 14 : 0) | \ + (15 & (access) ? 1 << 15 : 0)) static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) { unsigned index; - const u8 x = ACC_BITS_MASK(ACC_EXEC_MASK); - const u8 w = ACC_BITS_MASK(ACC_WRITE_MASK); - const u8 u = ACC_BITS_MASK(ACC_USER_MASK); + const u16 x = ACC_BITS_MASK(ACC_EXEC_MASK); + const u16 w = ACC_BITS_MASK(ACC_WRITE_MASK); + const u16 r = ACC_BITS_MASK(ACC_READ_MASK); bool cr4_smep = is_cr4_smep(mmu); bool cr4_smap = is_cr4_smap(mmu); @@ -5580,32 +5588,33 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) unsigned pfec = index << 1; /* - * Each "*f" variable has a 1 bit for each UWX value + * Each "*f" variable has a 1 bit for each ACC_* combo * that causes a fault with the given PFEC. */ /* Faults from reads to non-readable pages */ - u8 rf = 0; + u16 rf = (pfec & (PFERR_WRITE_MASK|PFERR_FETCH_MASK)) ? 0 : (u16)~r; /* Faults from writes to non-writable pages */ - u8 wf = (pfec & PFERR_WRITE_MASK) ? (u8)~w : 0; + u16 wf = (pfec & PFERR_WRITE_MASK) ? (u16)~w : 0; /* Faults from user mode accesses to supervisor pages */ - u8 uf = 0; + u16 uf = 0; /* Faults from fetches of non-executable pages */ - u8 ff = 0; + u16 ff = 0; /* Faults from kernel mode accesses of user pages */ - u8 smapf = 0; + u16 smapf = 0; if (ept) { - rf = (pfec & PFERR_USER_MASK) ? (u8)~u : 0; - ff = (pfec & PFERR_FETCH_MASK) ? (u8)~x : 0; + ff = (pfec & PFERR_FETCH_MASK) ? (u16)~x : 0; } else { - /* Faults from kernel mode accesses to user pages */ - u8 kf = (pfec & PFERR_USER_MASK) ? 0 : u; + const u16 u = ACC_BITS_MASK(ACC_USER_MASK); - uf = (pfec & PFERR_USER_MASK) ? (u8)~u : 0; + /* Faults from kernel mode accesses to user pages */ + u16 kf = (pfec & PFERR_USER_MASK) ? 0 : u; + + uf = (pfec & PFERR_USER_MASK) ? (u16)~u : 0; if (efer_nx) - ff |= (pfec & PFERR_FETCH_MASK) ? (u8)~x : 0; + ff |= (pfec & PFERR_FETCH_MASK) ? (u16)~x : 0; /* Allow supervisor writes if !cr0.wp */ if (!cr0_wp) diff --git a/arch/x86/kvm/mmu/mmutrace.h b/arch/x86/kvm/mmu/mmutrace.h index 764e3015d021..dcfdfedfc4e9 100644 --- a/arch/x86/kvm/mmu/mmutrace.h +++ b/arch/x86/kvm/mmu/mmutrace.h @@ -25,7 +25,8 @@ #define KVM_MMU_PAGE_PRINTK() ({ \ const char *saved_ptr = trace_seq_buffer_ptr(p); \ static const char *access_str[] = { \ - "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" \ + "----", "r---", "-w--", "rw--", "--u-", "r-u-", "-wu-", "rwu-", \ + "---x", "r--x", "-w-x", "rw-x", "--ux", "r-ux", "-wux", "rwux" \ }; \ union kvm_mmu_page_role role; \ \ diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 901cd2bd40b8..fb1b5d8b23e5 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -170,25 +170,24 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, return true; } -/* - * For PTTYPE_EPT, a page table can be executable but not readable - * on supported processors. Therefore, set_spte does not automatically - * set bit 0 if execute only is supported. Here, we repurpose ACC_USER_MASK - * to signify readability since it isn't used in the EPT case - */ static inline unsigned FNAME(gpte_access)(u64 gpte) { unsigned access; #if PTTYPE == PTTYPE_EPT access = ((gpte & VMX_EPT_WRITABLE_MASK) ? ACC_WRITE_MASK : 0) | ((gpte & VMX_EPT_EXECUTABLE_MASK) ? ACC_EXEC_MASK : 0) | - ((gpte & VMX_EPT_READABLE_MASK) ? ACC_USER_MASK : 0); + ((gpte & VMX_EPT_READABLE_MASK) ? ACC_READ_MASK : 0); #else - BUILD_BUG_ON(ACC_EXEC_MASK != PT_PRESENT_MASK); - BUILD_BUG_ON(ACC_EXEC_MASK != 1); + /* + * P is set here, so the page is always readable and W/U/!NX represent + * allowed accesses. + */ + BUILD_BUG_ON(ACC_READ_MASK != PT_PRESENT_MASK); + BUILD_BUG_ON(ACC_WRITE_MASK != PT_WRITABLE_MASK); + BUILD_BUG_ON(ACC_USER_MASK != PT_USER_MASK); + BUILD_BUG_ON(ACC_EXEC_MASK & (PT_WRITABLE_MASK | PT_USER_MASK | PT_PRESENT_MASK)); access = gpte & (PT_WRITABLE_MASK | PT_USER_MASK | PT_PRESENT_MASK); - /* Combine NX with P (which is set here) to get ACC_EXEC_MASK. */ - access ^= (gpte >> PT64_NX_SHIFT); + access |= gpte & PT64_NX_MASK ? 0 : ACC_EXEC_MASK; #endif return access; @@ -501,10 +500,18 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, if (write_fault) walker->fault.exit_qualification |= EPT_VIOLATION_ACC_WRITE; - if (user_fault) - walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ; - if (fetch_fault) + else if (fetch_fault) walker->fault.exit_qualification |= EPT_VIOLATION_ACC_INSTR; + else + walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ; + + /* + * Accesses to guest paging structures are either "reads" or + * "read+write" accesses, so consider them the latter if write_fault + * is true. + */ + if (access & PFERR_GUEST_PAGE_MASK) + walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ; /* * Note, pte_access holds the raw RWX bits from the EPTE, not diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 849a1c1c92b5..1b7fb508098b 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -194,12 +194,6 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, int is_host_mmio = -1; bool wrprot = false; - /* - * For the EPT case, shadow_present_mask has no RWX bits set if - * exec-only page table entries are supported. In that case, - * ACC_USER_MASK and shadow_user_mask are used to represent - * read access. See FNAME(gpte_access) in paging_tmpl.h. - */ WARN_ON_ONCE((pte_access | shadow_present_mask) == SHADOW_NONPRESENT_VALUE); if (sp->role.ad_disabled) @@ -228,6 +222,9 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, pte_access &= ~ACC_EXEC_MASK; } + if (pte_access & ACC_READ_MASK) + spte |= PT_PRESENT_MASK; /* or VMX_EPT_READABLE_MASK */ + if (pte_access & ACC_EXEC_MASK) spte |= shadow_x_mask; else @@ -391,6 +388,7 @@ u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled) u64 spte = SPTE_MMU_PRESENT_MASK; spte |= __pa(child_pt) | shadow_present_mask | PT_WRITABLE_MASK | + PT_PRESENT_MASK /* or VMX_EPT_READABLE_MASK */ | shadow_user_mask | shadow_x_mask | shadow_me_value; if (ad_disabled) @@ -491,18 +489,16 @@ void kvm_mmu_set_me_spte_mask(u64 me_value, u64 me_mask) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_mmu_set_me_spte_mask); -void kvm_mmu_set_ept_masks(bool has_ad_bits, bool has_exec_only) +void kvm_mmu_set_ept_masks(bool has_ad_bits) { kvm_ad_enabled = has_ad_bits; - shadow_user_mask = VMX_EPT_READABLE_MASK; + shadow_user_mask = 0; shadow_accessed_mask = VMX_EPT_ACCESS_BIT; shadow_dirty_mask = VMX_EPT_DIRTY_BIT; shadow_nx_mask = 0ull; shadow_x_mask = VMX_EPT_EXECUTABLE_MASK; - /* VMX_EPT_SUPPRESS_VE_BIT is needed for W or X violation. */ - shadow_present_mask = - (has_exec_only ? 0ull : VMX_EPT_READABLE_MASK) | VMX_EPT_SUPPRESS_VE_BIT; + shadow_present_mask = VMX_EPT_SUPPRESS_VE_BIT; shadow_acc_track_mask = VMX_EPT_RWX_MASK; shadow_host_writable_mask = EPT_SPTE_HOST_WRITABLE; diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index bc02a2e89a31..121bfb2217e8 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -52,10 +52,11 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); #define SPTE_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) #endif -#define ACC_EXEC_MASK 1 +#define ACC_READ_MASK PT_PRESENT_MASK #define ACC_WRITE_MASK PT_WRITABLE_MASK #define ACC_USER_MASK PT_USER_MASK -#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK) +#define ACC_EXEC_MASK 8 +#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK | ACC_READ_MASK) #define SPTE_LEVEL_BITS 9 #define SPTE_LEVEL_SHIFT(level) __PT_LEVEL_SHIFT(level, SPTE_LEVEL_BITS) diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 56cacc06225e..7e59eb0f41bb 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -300,11 +300,6 @@ static inline bool cpu_has_vmx_flexpriority(void) cpu_has_vmx_virtualize_apic_accesses(); } -static inline bool cpu_has_vmx_ept_execute_only(void) -{ - return vmx_capability.ept & VMX_EPT_EXECUTE_ONLY_BIT; -} - static inline bool cpu_has_vmx_ept_4levels(void) { return vmx_capability.ept & VMX_EPT_PAGE_WALK_4_BIT; diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h index adf925500b9e..1afbf272efae 100644 --- a/arch/x86/kvm/vmx/common.h +++ b/arch/x86/kvm/vmx/common.h @@ -85,11 +85,8 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, { u64 error_code; - /* Is it a read fault? */ - error_code = (exit_qualification & EPT_VIOLATION_ACC_READ) - ? PFERR_USER_MASK : 0; /* Is it a write fault? */ - error_code |= (exit_qualification & EPT_VIOLATION_ACC_WRITE) + error_code = (exit_qualification & EPT_VIOLATION_ACC_WRITE) ? PFERR_WRITE_MASK : 0; /* Is it a fetch fault? */ error_code |= (exit_qualification & EPT_VIOLATION_ACC_INSTR) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a29896a9ef14..337bbfecc021 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -8683,8 +8683,7 @@ __init int vmx_hardware_setup(void) set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */ if (enable_ept) - kvm_mmu_set_ept_masks(enable_ept_ad_bits, - cpu_has_vmx_ept_execute_only()); + kvm_mmu_set_ept_masks(enable_ept_ad_bits); else vt_x86_ops.get_mt_mask = NULL; From bd1c40114b02cf63ba33da8e7df71ed0fc2a3019 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:00 -0400 Subject: [PATCH 0707/5214] KVM: x86/mmu: pass PFERR_GUEST_PAGE/FINAL_MASK to kvm_translate_gpa The XS/XU bit for EPT are only applied to final accesses, and use the U bit from the page walk itself. While strictly speaking not necessary (any value of PFERR_USER_MASK would be the same for page table accesses, because they're reads and writes only), it is clearer and less hackish to only apply MBEC to PFERR_GUEST_FINAL_MASK. Allow kvm-intel.ko to distinguish the two cases. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/hyperv.c | 3 ++- arch/x86/kvm/mmu/mmu.c | 3 ++- arch/x86/kvm/mmu/paging_tmpl.h | 7 +++++-- arch/x86/kvm/x86.c | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 9b140bbdc1d8..cf9dd565b894 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -2041,7 +2041,8 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc) * read with kvm_read_guest(). */ if (!hc->fast && is_guest_mode(vcpu)) { - hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, 0, NULL); + hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, + PFERR_GUEST_FINAL_MASK, NULL); if (unlikely(hc->ingpa == INVALID_GPA)) return HV_STATUS_INVALID_HYPERCALL_INPUT; } diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c617837a5038..6ac9f760d28c 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4348,7 +4348,8 @@ static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, { if (exception) exception->error_code = 0; - return kvm_translate_gpa(vcpu, mmu, vaddr, access, exception); + return kvm_translate_gpa(vcpu, mmu, vaddr, access | PFERR_GUEST_FINAL_MASK, + exception); } static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index fb1b5d8b23e5..567f8b77ffe0 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -376,7 +376,8 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, walker->pte_gpa[walker->level - 1] = pte_gpa; real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(table_gfn), - nested_access, &walker->fault); + nested_access | PFERR_GUEST_PAGE_MASK, + &walker->fault); /* * FIXME: This can happen if emulation (for of an INS/OUTS @@ -444,7 +445,9 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, gfn += pse36_gfn_delta(pte); #endif - real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(gfn), access, &walker->fault); + real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(gfn), + access | PFERR_GUEST_FINAL_MASK, + &walker->fault); if (real_gpa == INVALID_GPA) return 0; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0a1b63c63d1a..ef1e3ae13887 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1072,7 +1072,8 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) * to an L1 GPA. */ real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(pdpt_gfn), - PFERR_USER_MASK | PFERR_WRITE_MASK, NULL); + PFERR_USER_MASK | PFERR_WRITE_MASK | + PFERR_GUEST_PAGE_MASK, NULL); if (real_gpa == INVALID_GPA) return 0; From 2c89d577548e5a74d417415014a14d8d7fb3518d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:01 -0400 Subject: [PATCH 0708/5214] KVM: x86/mmu: pass pte_access for final nGPA->GPA walk The XS/XU bit for EPT are only applied to final accesses, and use the U bit from the page walk itself. This is available in the page walker as pte_access & ACC_USER_MASK but not available to translate_nested_gpa, so pass it down. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/hyperv.c | 2 +- arch/x86/kvm/mmu.h | 15 ++++++++++++--- arch/x86/kvm/mmu/mmu.c | 8 +++++++- arch/x86/kvm/mmu/paging_tmpl.h | 4 ++-- arch/x86/kvm/mmu/spte.h | 6 ------ arch/x86/kvm/x86.c | 5 +++-- 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index cf9dd565b894..53688f7b76eb 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -2042,7 +2042,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc) */ if (!hc->fast && is_guest_mode(vcpu)) { hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, - PFERR_GUEST_FINAL_MASK, NULL); + PFERR_GUEST_FINAL_MASK, NULL, 0); if (unlikely(hc->ingpa == INVALID_GPA)) return HV_STATUS_INVALID_HYPERCALL_INPUT; } diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 23f37535c0ce..635c2e5d8513 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -37,6 +37,12 @@ extern bool __read_mostly enable_mmio_caching; #define PT32_ROOT_LEVEL 2 #define PT32E_ROOT_LEVEL 3 +#define ACC_READ_MASK PT_PRESENT_MASK +#define ACC_WRITE_MASK PT_WRITABLE_MASK +#define ACC_USER_MASK PT_USER_MASK +#define ACC_EXEC_MASK 8 +#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK | ACC_READ_MASK) + #define KVM_MMU_CR4_ROLE_BITS (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_LA57 | \ X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE) @@ -289,16 +295,19 @@ static inline void kvm_update_page_stats(struct kvm *kvm, int level, int count) } gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u64 access, - struct x86_exception *exception); + struct x86_exception *exception, + u64 pte_access); static inline gpa_t kvm_translate_gpa(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, gpa_t gpa, u64 access, - struct x86_exception *exception) + struct x86_exception *exception, + u64 pte_access) { if (mmu != &vcpu->arch.nested_mmu) return gpa; - return translate_nested_gpa(vcpu, gpa, access, exception); + return translate_nested_gpa(vcpu, gpa, access, exception, + pte_access); } static inline bool kvm_has_mirrored_tdp(const struct kvm *kvm) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 6ac9f760d28c..eb65f6c9c621 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4348,8 +4348,14 @@ static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, { if (exception) exception->error_code = 0; + /* + * EPT MBEC uses the effective access bits from the PTE to distinguish + * user and supervisor accesses, and treats every linear address as a + * user-mode address if CR0.PG=0. Therefore *include* ACC_USER_MASK in + * the last argument to kvm_translate_gpa (which NPT does not use). + */ return kvm_translate_gpa(vcpu, mmu, vaddr, access | PFERR_GUEST_FINAL_MASK, - exception); + exception, ACC_ALL); } static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 567f8b77ffe0..8dd9d510fc34 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -377,7 +377,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(table_gfn), nested_access | PFERR_GUEST_PAGE_MASK, - &walker->fault); + &walker->fault, 0); /* * FIXME: This can happen if emulation (for of an INS/OUTS @@ -447,7 +447,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(gfn), access | PFERR_GUEST_FINAL_MASK, - &walker->fault); + &walker->fault, walker->pte_access); if (real_gpa == INVALID_GPA) return 0; diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 121bfb2217e8..8a4c09c5cdbf 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -52,12 +52,6 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); #define SPTE_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) #endif -#define ACC_READ_MASK PT_PRESENT_MASK -#define ACC_WRITE_MASK PT_WRITABLE_MASK -#define ACC_USER_MASK PT_USER_MASK -#define ACC_EXEC_MASK 8 -#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK | ACC_READ_MASK) - #define SPTE_LEVEL_BITS 9 #define SPTE_LEVEL_SHIFT(level) __PT_LEVEL_SHIFT(level, SPTE_LEVEL_BITS) #define SPTE_INDEX(address, level) __PT_INDEX(address, level, SPTE_LEVEL_BITS) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ef1e3ae13887..67979b7de5d6 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1073,7 +1073,7 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) */ real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(pdpt_gfn), PFERR_USER_MASK | PFERR_WRITE_MASK | - PFERR_GUEST_PAGE_MASK, NULL); + PFERR_GUEST_PAGE_MASK, NULL, 0); if (real_gpa == INVALID_GPA) return 0; @@ -7849,7 +7849,8 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, } gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u64 access, - struct x86_exception *exception) + struct x86_exception *exception, + u64 pte_access) { struct kvm_mmu *mmu = vcpu->arch.mmu; gpa_t t_gpa; From b95b398d2e6d13ed47d4ab25bce499c7240923a1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:02 -0400 Subject: [PATCH 0709/5214] KVM: x86: make translate_nested_gpa vendor-specific EPT and NPT have different rules for passing PFERR_USER_MASK to the nested page table walk. In particular, for final addresses EPT uses the U bit of the guest (nGVA->nGPA) walk. While at it, remove PFERR_USER_MASK from the VMX version of the function, since it is actually ignored by the tables that update_permission_bitmask() generates for EPT. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 4 ++++ arch/x86/kvm/hyperv.c | 3 ++- arch/x86/kvm/mmu.h | 9 +++------ arch/x86/kvm/svm/nested.c | 15 +++++++++++++++ arch/x86/kvm/vmx/nested.c | 12 ++++++++++++ arch/x86/kvm/x86.c | 16 ---------------- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8f2a1b915df9..62dc782b2dd3 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2010,6 +2010,10 @@ struct kvm_x86_nested_ops { struct kvm_nested_state *kvm_state); bool (*get_nested_state_pages)(struct kvm_vcpu *vcpu); int (*write_log_dirty)(struct kvm_vcpu *vcpu, gpa_t l2_gpa); + gpa_t (*translate_nested_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, + u64 access, + struct x86_exception *exception, + u64 pte_access); int (*enable_evmcs)(struct kvm_vcpu *vcpu, uint16_t *vmcs_version); diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 53688f7b76eb..f35fae3a7b3d 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -2041,7 +2041,8 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc) * read with kvm_read_guest(). */ if (!hc->fast && is_guest_mode(vcpu)) { - hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, + hc->ingpa = kvm_x86_ops.nested_ops->translate_nested_gpa( + vcpu, hc->ingpa, PFERR_GUEST_FINAL_MASK, NULL, 0); if (unlikely(hc->ingpa == INVALID_GPA)) return HV_STATUS_INVALID_HYPERCALL_INPUT; diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 635c2e5d8513..63be5c5efed9 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -294,10 +294,6 @@ static inline void kvm_update_page_stats(struct kvm *kvm, int level, int count) atomic64_add(count, &kvm->stat.pages[level - 1]); } -gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u64 access, - struct x86_exception *exception, - u64 pte_access); - static inline gpa_t kvm_translate_gpa(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, gpa_t gpa, u64 access, @@ -306,8 +302,9 @@ static inline gpa_t kvm_translate_gpa(struct kvm_vcpu *vcpu, { if (mmu != &vcpu->arch.nested_mmu) return gpa; - return translate_nested_gpa(vcpu, gpa, access, exception, - pte_access); + return kvm_x86_ops.nested_ops->translate_nested_gpa(vcpu, gpa, access, + exception, + pte_access); } static inline bool kvm_has_mirrored_tdp(const struct kvm *kvm) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 961804df5f45..df232153eb24 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -2071,8 +2071,23 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) return true; } +static gpa_t svm_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, + u64 access, + struct x86_exception *exception, + u64 pte_access) +{ + struct kvm_mmu *mmu = vcpu->arch.mmu; + + BUG_ON(!mmu_is_nested(vcpu)); + + /* NPT walks are always user-walks */ + access |= PFERR_USER_MASK; + return mmu->gva_to_gpa(vcpu, mmu, gpa, access, exception); +} + struct kvm_x86_nested_ops svm_nested_ops = { .leave_nested = svm_leave_nested, + .translate_nested_gpa = svm_translate_nested_gpa, .is_exception_vmexit = nested_svm_is_exception_vmexit, .check_events = svm_check_nested_events, .triple_fault = nested_svm_triple_fault, diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 3fe88f29be7a..cd1924c6e075 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -7438,8 +7438,20 @@ __init int nested_vmx_hardware_setup(int (*exit_handlers[])(struct kvm_vcpu *)) return 0; } +static gpa_t vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, + u64 access, + struct x86_exception *exception, + u64 pte_access) +{ + struct kvm_mmu *mmu = vcpu->arch.mmu; + + BUG_ON(!mmu_is_nested(vcpu)); + return mmu->gva_to_gpa(vcpu, mmu, gpa, access, exception); +} + struct kvm_x86_nested_ops vmx_nested_ops = { .leave_nested = vmx_leave_nested, + .translate_nested_gpa = vmx_translate_nested_gpa, .is_exception_vmexit = nested_vmx_is_exception_vmexit, .check_events = vmx_check_nested_events, .has_events = vmx_has_nested_events, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 67979b7de5d6..7c6942afae81 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7848,22 +7848,6 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, kvm_x86_call(get_segment)(vcpu, var, seg); } -gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u64 access, - struct x86_exception *exception, - u64 pte_access) -{ - struct kvm_mmu *mmu = vcpu->arch.mmu; - gpa_t t_gpa; - - BUG_ON(!mmu_is_nested(vcpu)); - - /* NPT walks are always user-walks */ - access |= PFERR_USER_MASK; - t_gpa = mmu->gva_to_gpa(vcpu, mmu, gpa, access, exception); - - return t_gpa; -} - gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, struct x86_exception *exception) { From 2e111d932d1d7d5bbe2ecb5f0557a81281527cb7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:03 -0400 Subject: [PATCH 0710/5214] KVM: x86/mmu: split XS/XU bits for EPT When EPT is in use, replace ACC_USER_MASK with ACC_USER_EXEC_MASK, so that supervisor and user-mode execution can be controlled independently (ACC_USER_MASK would not allow a setting similar to XU=0 XS=1 W=1 R=1). Replace shadow_x_mask with shadow_xs_mask/shadow_xu_mask, to allow setting XS and XU bits separately in EPT entries. In fact, ACC_USER_EXEC_MASK is already set through ACC_ALL in the kvm_mmu_page roles and propagates to the XU bit of sPTEs even if MBEC is not (yet) enabled in the execution controls. This is fine, because the XU bit is ignored by the processor, and even once KVM supports MBEC this mode will remain for processors that lack the feature. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu.h | 3 +- arch/x86/kvm/mmu/mmu.c | 2 +- arch/x86/kvm/mmu/mmutrace.h | 6 ++-- arch/x86/kvm/mmu/spte.c | 62 ++++++++++++++++++++++++++----------- arch/x86/kvm/mmu/spte.h | 16 +++++++--- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 63be5c5efed9..d8c13e43c2d7 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -39,7 +39,8 @@ extern bool __read_mostly enable_mmio_caching; #define ACC_READ_MASK PT_PRESENT_MASK #define ACC_WRITE_MASK PT_WRITABLE_MASK -#define ACC_USER_MASK PT_USER_MASK +#define ACC_USER_MASK PT_USER_MASK /* non EPT */ +#define ACC_USER_EXEC_MASK ACC_USER_MASK /* EPT only */ #define ACC_EXEC_MASK 8 #define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK | ACC_READ_MASK) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index eb65f6c9c621..d8ed5e38b243 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5491,7 +5491,7 @@ static void reset_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, static inline bool boot_cpu_is_amd(void) { WARN_ON_ONCE(!tdp_enabled); - return shadow_x_mask == 0; + return shadow_xs_mask == 0; } /* diff --git a/arch/x86/kvm/mmu/mmutrace.h b/arch/x86/kvm/mmu/mmutrace.h index dcfdfedfc4e9..3429c1413f42 100644 --- a/arch/x86/kvm/mmu/mmutrace.h +++ b/arch/x86/kvm/mmu/mmutrace.h @@ -357,8 +357,8 @@ TRACE_EVENT( __entry->sptep = virt_to_phys(sptep); __entry->level = level; __entry->r = shadow_present_mask || (__entry->spte & PT_PRESENT_MASK); - __entry->x = is_executable_pte(__entry->spte); - __entry->u = shadow_user_mask ? !!(__entry->spte & shadow_user_mask) : -1; + __entry->x = (__entry->spte & (shadow_xs_mask | shadow_nx_mask)) == shadow_xs_mask; + __entry->u = !!(__entry->spte & (shadow_xu_mask | shadow_user_mask)); ), TP_printk("gfn %llx spte %llx (%s%s%s%s) level %d at %llx", @@ -366,7 +366,7 @@ TRACE_EVENT( __entry->r ? "r" : "-", __entry->spte & PT_WRITABLE_MASK ? "w" : "-", __entry->x ? "x" : "-", - __entry->u == -1 ? "" : (__entry->u ? "u" : "-"), + __entry->u ? "u" : "-", __entry->level, __entry->sptep ) ); diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 1b7fb508098b..f41573b0ccfa 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -29,8 +29,9 @@ bool __read_mostly kvm_ad_enabled; u64 __read_mostly shadow_host_writable_mask; u64 __read_mostly shadow_mmu_writable_mask; u64 __read_mostly shadow_nx_mask; -u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */ u64 __read_mostly shadow_user_mask; +u64 __read_mostly shadow_xs_mask; /* mutual exclusive with nx_mask and user_mask */ +u64 __read_mostly shadow_xu_mask; /* mutual exclusive with nx_mask and user_mask */ u64 __read_mostly shadow_accessed_mask; u64 __read_mostly shadow_dirty_mask; u64 __read_mostly shadow_mmio_value; @@ -217,21 +218,26 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, * would tie make_spte() further to vCPU/MMU state, and add complexity * just to optimize a mode that is anything but performance critical. */ - if (level > PG_LEVEL_4K && (pte_access & ACC_EXEC_MASK) && - is_nx_huge_page_enabled(vcpu->kvm)) { + if (level > PG_LEVEL_4K && is_nx_huge_page_enabled(vcpu->kvm)) { pte_access &= ~ACC_EXEC_MASK; + if (shadow_xu_mask) + pte_access &= ~ACC_USER_EXEC_MASK; } if (pte_access & ACC_READ_MASK) spte |= PT_PRESENT_MASK; /* or VMX_EPT_READABLE_MASK */ - if (pte_access & ACC_EXEC_MASK) - spte |= shadow_x_mask; - else - spte |= shadow_nx_mask; - - if (pte_access & ACC_USER_MASK) - spte |= shadow_user_mask; + if (shadow_nx_mask) { + if (!(pte_access & ACC_EXEC_MASK)) + spte |= shadow_nx_mask; + if (pte_access & ACC_USER_MASK) + spte |= shadow_user_mask; + } else { + if (pte_access & ACC_EXEC_MASK) + spte |= shadow_xs_mask; + if (pte_access & ACC_USER_EXEC_MASK) + spte |= shadow_xu_mask; + } if (level > PG_LEVEL_4K) spte |= PT_PAGE_SIZE_MASK; @@ -318,11 +324,13 @@ static u64 change_spte_executable(u64 spte, u8 access) { u64 set, clear; - if (access & ACC_EXEC_MASK) - set = shadow_x_mask; + if (shadow_nx_mask) + set = (access & ACC_EXEC_MASK) ? 0 : shadow_nx_mask; else - set = shadow_nx_mask; - clear = set ^ (shadow_nx_mask | shadow_x_mask); + set = + (access & ACC_EXEC_MASK ? shadow_xs_mask : 0) | + (access & ACC_USER_EXEC_MASK ? shadow_xu_mask : 0); + clear = set ^ (shadow_nx_mask | shadow_xs_mask | shadow_xu_mask); return modify_spte_protections(spte, set, clear); } @@ -389,7 +397,7 @@ u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled) spte |= __pa(child_pt) | shadow_present_mask | PT_WRITABLE_MASK | PT_PRESENT_MASK /* or VMX_EPT_READABLE_MASK */ | - shadow_user_mask | shadow_x_mask | shadow_me_value; + shadow_user_mask | shadow_xs_mask | shadow_xu_mask | shadow_me_value; if (ad_disabled) spte |= SPTE_TDP_AD_DISABLED; @@ -497,10 +505,27 @@ void kvm_mmu_set_ept_masks(bool has_ad_bits) shadow_accessed_mask = VMX_EPT_ACCESS_BIT; shadow_dirty_mask = VMX_EPT_DIRTY_BIT; shadow_nx_mask = 0ull; - shadow_x_mask = VMX_EPT_EXECUTABLE_MASK; + shadow_xs_mask = VMX_EPT_EXECUTABLE_MASK; + + /* + * The MMU always maps ACC_EXEC_MASK and ACC_USER_EXEC_MASK to the + * XS and XU bits of shadow EPT entries, regardless of whether MBEC + * is available on the host or enabled in the VMCS. + * + * For the non-nested case, pages are mapped with ACC_EXEC_MASK + * and ACC_USER_EXEC_MASK set in tandem, so XS == XU and the + * host's MBEC setting does not matter. On hardware without MBEC + * the XU bit is reserved-as-ignored, and setting it does no harm. + * + * For nested EPT MBEC is not supported, but bit 10 of the gPTE has + * no effect because (a) is_present_gpte() does not treat it as a + * present bit, and (b) permission_fault() uses an mmu->permissions[] + * array that effectively ignores ACC_USER_EXEC_MASK. + */ + shadow_xu_mask = VMX_EPT_USER_EXECUTABLE_MASK; shadow_present_mask = VMX_EPT_SUPPRESS_VE_BIT; - shadow_acc_track_mask = VMX_EPT_RWX_MASK; + shadow_acc_track_mask = VMX_EPT_RWX_MASK | VMX_EPT_USER_EXECUTABLE_MASK; shadow_host_writable_mask = EPT_SPTE_HOST_WRITABLE; shadow_mmu_writable_mask = EPT_SPTE_MMU_WRITABLE; @@ -548,7 +573,8 @@ void kvm_mmu_reset_all_pte_masks(void) shadow_accessed_mask = PT_ACCESSED_MASK; shadow_dirty_mask = PT_DIRTY_MASK; shadow_nx_mask = PT64_NX_MASK; - shadow_x_mask = 0; + shadow_xs_mask = 0; + shadow_xu_mask = 0; shadow_present_mask = PT_PRESENT_MASK; shadow_acc_track_mask = 0; diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 8a4c09c5cdbf..f5261d993eac 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -24,7 +24,7 @@ * - bits 55 (EPT only): MMU-writable * - bits 56-59: unused * - bits 60-61: type of A/D tracking - * - bits 62: unused + * - bits 62 (EPT only): saved XU bit for disabled AD */ /* @@ -65,7 +65,8 @@ static_assert(SPTE_TDP_AD_ENABLED == 0); * must not overlap the A/D type mask. */ #define SHADOW_ACC_TRACK_SAVED_BITS_MASK (VMX_EPT_READABLE_MASK | \ - VMX_EPT_EXECUTABLE_MASK) + VMX_EPT_EXECUTABLE_MASK | \ + VMX_EPT_USER_EXECUTABLE_MASK) #define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 52 #define SHADOW_ACC_TRACK_SAVED_MASK (SHADOW_ACC_TRACK_SAVED_BITS_MASK << \ SHADOW_ACC_TRACK_SAVED_BITS_SHIFT) @@ -178,8 +179,9 @@ extern bool __read_mostly kvm_ad_enabled; extern u64 __read_mostly shadow_host_writable_mask; extern u64 __read_mostly shadow_mmu_writable_mask; extern u64 __read_mostly shadow_nx_mask; -extern u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */ extern u64 __read_mostly shadow_user_mask; +extern u64 __read_mostly shadow_xs_mask; /* mutual exclusive with nx_mask and user_mask */ +extern u64 __read_mostly shadow_xu_mask; /* mutual exclusive with nx_mask and user_mask */ extern u64 __read_mostly shadow_accessed_mask; extern u64 __read_mostly shadow_dirty_mask; extern u64 __read_mostly shadow_mmio_value; @@ -357,7 +359,13 @@ static inline bool is_last_spte(u64 pte, int level) static inline bool is_executable_pte(u64 spte) { - return (spte & (shadow_x_mask | shadow_nx_mask)) == shadow_x_mask; + /* + * For now, return true if either the XS or XU bit is set + * This function is only used for fast_page_fault, + * which never processes shadow EPT, and regular page + * tables always have XS==XU. + */ + return (spte & (shadow_xs_mask | shadow_xu_mask | shadow_nx_mask)) != shadow_nx_mask; } static inline kvm_pfn_t spte_to_pfn(u64 pte) From f603854a1b42486ae6303675fcb47377c1396c80 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:04 -0400 Subject: [PATCH 0711/5214] KVM: x86/mmu: move cr4_smep to base role Guest page tables can be reused independent of the value of CR4.SMEP (at least if WP=1). However, this is not true of EPT MBEC pages, because presence of EPT entries is signaled by bits 0-2 when MBEC is off, and bits 0-2 + bit 10 when MBEC is on. In preparation for enabling MBEC, move cr4_smep to the base role. This makes the smep_andnot_wp bit redundant, so remove it. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- Documentation/virt/kvm/x86/mmu.rst | 10 ++++------ arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 23 +++++++++++++++-------- arch/x86/kvm/mmu/mmu.c | 6 +++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Documentation/virt/kvm/x86/mmu.rst b/Documentation/virt/kvm/x86/mmu.rst index 2b3b6d442302..666aa179601a 100644 --- a/Documentation/virt/kvm/x86/mmu.rst +++ b/Documentation/virt/kvm/x86/mmu.rst @@ -184,10 +184,8 @@ Shadow pages contain the following information: Contains the value of efer.nx for which the page is valid. role.cr0_wp: Contains the value of cr0.wp for which the page is valid. - role.smep_andnot_wp: - Contains the value of cr4.smep && !cr0.wp for which the page is valid - (pages for which this is true are different from other pages; see the - treatment of cr0.wp=0 below). + role.cr4_smep: + Contains the value of cr4.smep for which the page is valid. role.smap_andnot_wp: Contains the value of cr4.smap && !cr0.wp for which the page is valid (pages for which this is true are different from other pages; see the @@ -435,8 +433,8 @@ from being written by the kernel after cr0.wp has changed to 1, we make the value of cr0.wp part of the page role. This means that an spte created with one value of cr0.wp cannot be used when cr0.wp has a different value - it will simply be missed by the shadow page lookup code. A similar issue -exists when an spte created with cr0.wp=0 and cr4.smep=0 is used after -changing cr4.smep to 1. To avoid this, the value of !cr0.wp && cr4.smep +exists when an spte created with cr0.wp=0 and cr4.smap=0 is used after +changing cr4.smap to 1. To avoid this, the value of !cr0.wp && cr4.smap is also made a part of the page role. Large pages diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 3776cf5382a2..e4fca997ec79 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -94,6 +94,7 @@ KVM_X86_OP_OPTIONAL(sync_pir_to_irr) KVM_X86_OP_OPTIONAL_RET0(set_tss_addr) KVM_X86_OP_OPTIONAL_RET0(set_identity_map_addr) KVM_X86_OP_OPTIONAL_RET0(get_mt_mask) +KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep) KVM_X86_OP(load_mmu_pgd) KVM_X86_OP_OPTIONAL(link_external_spt) KVM_X86_OP_OPTIONAL(set_external_spte) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 62dc782b2dd3..23a7ac8d7fbe 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -343,8 +343,8 @@ struct kvm_kernel_irq_routing_entry; * paging has exactly one upper level, making level completely redundant * when has_4_byte_gpte=1. * - * - on top of this, smep_andnot_wp and smap_andnot_wp are only set if - * cr0_wp=0, therefore these three bits only give rise to 5 possibilities. + * - on top of this, smap_andnot_wp is only set if cr0_wp=0, + * therefore these two bits only give rise to 3 possibilities. * * Therefore, the maximum number of possible upper-level shadow pages for a * single gfn is a bit less than 2^14. @@ -360,12 +360,19 @@ union kvm_mmu_page_role { unsigned invalid:1; unsigned efer_nx:1; unsigned cr0_wp:1; - unsigned smep_andnot_wp:1; unsigned smap_andnot_wp:1; unsigned ad_disabled:1; unsigned guest_mode:1; unsigned passthrough:1; unsigned is_mirror:1; + + /* + * cr4_smep is also set for EPT MBEC. Because it affects + * which pages are considered non-present (bit 10 additionally + * must be zero if MBEC is on) it has to be in the base role. + */ + unsigned cr4_smep:1; + unsigned:3; /* @@ -392,10 +399,10 @@ union kvm_mmu_page_role { * tables (because KVM doesn't support Protection Keys with shadow paging), and * CR0.PG, CR4.PAE, and CR4.PSE are indirectly reflected in role.level. * - * Note, SMEP and SMAP are not redundant with sm*p_andnot_wp in the page role. - * If CR0.WP=1, KVM can reuse shadow pages for the guest regardless of SMEP and - * SMAP, but the MMU's permission checks for software walks need to be SMEP and - * SMAP aware regardless of CR0.WP. + * Note, SMAP is not redundant with smap_andnot_wp in the page role. If + * CR0.WP=1, KVM can reuse shadow pages for the guest regardless of SMAP, + * but the MMU's permission checks for software walks need to be SMAP + * aware regardless of CR0.WP. */ union kvm_mmu_extended_role { u32 word; @@ -405,7 +412,6 @@ union kvm_mmu_extended_role { unsigned int cr4_pse:1; unsigned int cr4_pke:1; unsigned int cr4_smap:1; - unsigned int cr4_smep:1; unsigned int cr4_la57:1; unsigned int efer_lma:1; }; @@ -1887,6 +1893,7 @@ struct kvm_x86_ops { int (*set_tss_addr)(struct kvm *kvm, unsigned int addr); int (*set_identity_map_addr)(struct kvm *kvm, u64 ident_addr); u8 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio); + bool (*tdp_has_smep)(struct kvm *kvm); void (*load_mmu_pgd)(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index d8ed5e38b243..097af8a1bd25 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -227,7 +227,7 @@ static inline bool __maybe_unused is_##reg##_##name(struct kvm_mmu *mmu) \ } BUILD_MMU_ROLE_ACCESSOR(base, cr0, wp); BUILD_MMU_ROLE_ACCESSOR(ext, cr4, pse); -BUILD_MMU_ROLE_ACCESSOR(ext, cr4, smep); +BUILD_MMU_ROLE_ACCESSOR(base, cr4, smep); BUILD_MMU_ROLE_ACCESSOR(ext, cr4, smap); BUILD_MMU_ROLE_ACCESSOR(ext, cr4, pke); BUILD_MMU_ROLE_ACCESSOR(ext, cr4, la57); @@ -5764,7 +5764,7 @@ static union kvm_cpu_role kvm_calc_cpu_role(struct kvm_vcpu *vcpu, role.base.efer_nx = ____is_efer_nx(regs); role.base.cr0_wp = ____is_cr0_wp(regs); - role.base.smep_andnot_wp = ____is_cr4_smep(regs) && !____is_cr0_wp(regs); + role.base.cr4_smep = ____is_cr4_smep(regs); role.base.smap_andnot_wp = ____is_cr4_smap(regs) && !____is_cr0_wp(regs); role.base.has_4_byte_gpte = !____is_cr4_pae(regs); @@ -5776,7 +5776,6 @@ static union kvm_cpu_role kvm_calc_cpu_role(struct kvm_vcpu *vcpu, else role.base.level = PT32_ROOT_LEVEL; - role.ext.cr4_smep = ____is_cr4_smep(regs); role.ext.cr4_smap = ____is_cr4_smap(regs); role.ext.cr4_pse = ____is_cr4_pse(regs); @@ -5835,6 +5834,7 @@ kvm_calc_tdp_mmu_root_page_role(struct kvm_vcpu *vcpu, role.access = ACC_ALL; role.cr0_wp = true; + role.cr4_smep = kvm_x86_call(tdp_has_smep)(vcpu->kvm); role.efer_nx = true; role.smm = cpu_role.base.smm; role.guest_mode = cpu_role.base.guest_mode; From a6b7b6b2050c7e9e350563775431d9c7c0a684b9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:05 -0400 Subject: [PATCH 0712/5214] KVM: VMX: enable use of MBEC If available, set SECONDARY_EXEC_MODE_BASED_EPT_EXEC in the secondary execution controls. The changes are limited because the MMU is designed to create the same sPTEs independent of the MBEC setting. On hosts lacking support for MBEC, and in nested guests which cannot enable it as of this commit, the XU bit is ignored by the processor. Note that, as of this patch, MBEC is not available to L1 hypervisors for their guests. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/vmx.h | 3 +++ arch/x86/kvm/mmu.h | 5 +++++ arch/x86/kvm/vmx/capabilities.h | 7 +++++++ arch/x86/kvm/vmx/common.h | 10 +++++----- arch/x86/kvm/vmx/main.c | 9 +++++++++ arch/x86/kvm/vmx/nested.c | 1 + arch/x86/kvm/vmx/vmx.c | 9 +++++++++ arch/x86/kvm/vmx/vmx.h | 1 + arch/x86/kvm/vmx/x86_ops.h | 6 ++++++ 9 files changed, 46 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 2b30b921b375..54aa5be50df9 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -619,9 +619,12 @@ enum vm_entry_failure_code { #define EPT_VIOLATION_GVA_TRANSLATED BIT(8) #define EPT_VIOLATION_RWX_TO_PROT(__epte) (((__epte) & VMX_EPT_RWX_MASK) << 3) +#define EPT_VIOLATION_USER_EXEC_TO_PROT(__epte) (((__epte) & VMX_EPT_USER_EXECUTABLE_MASK) >> 4) static_assert(EPT_VIOLATION_RWX_TO_PROT(VMX_EPT_RWX_MASK) == (EPT_VIOLATION_PROT_READ | EPT_VIOLATION_PROT_WRITE | EPT_VIOLATION_PROT_EXEC)); +static_assert(EPT_VIOLATION_USER_EXEC_TO_PROT(VMX_EPT_USER_EXECUTABLE_MASK) == + (EPT_VIOLATION_PROT_USER_EXEC)); /* * Exit Qualifications for NOTIFY VM EXIT diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index d8c13e43c2d7..23bc5b18efd0 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -83,6 +83,11 @@ static inline gfn_t kvm_mmu_max_gfn(void) return (1ULL << (max_gpa_bits - PAGE_SHIFT)) - 1; } +static inline bool mmu_has_mbec(struct kvm_mmu *mmu) +{ + return mmu->root_role.cr4_smep; +} + u8 kvm_mmu_get_max_tdp_level(void); void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 mmio_mask, u64 access_mask); diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 7e59eb0f41bb..07469d1cfe74 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -15,6 +15,7 @@ extern bool __read_mostly enable_ept; extern bool __read_mostly enable_unrestricted_guest; extern bool __read_mostly enable_ept_ad_bits; extern bool __read_mostly enable_pml; +extern bool __read_mostly enable_mbec; extern int __read_mostly pt_mode; #define PT_MODE_SYSTEM 0 @@ -406,4 +407,10 @@ static inline bool cpu_has_notify_vmexit(void) SECONDARY_EXEC_NOTIFY_VM_EXITING; } +static inline bool cpu_has_ept_mbec(void) +{ + return vmcs_config.cpu_based_2nd_exec_ctrl & + SECONDARY_EXEC_MODE_BASED_EPT_EXEC; +} + #endif /* __KVM_X86_VMX_CAPS_H */ diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h index 1afbf272efae..40fa72f31fc7 100644 --- a/arch/x86/kvm/vmx/common.h +++ b/arch/x86/kvm/vmx/common.h @@ -91,15 +91,15 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, /* Is it a fetch fault? */ error_code |= (exit_qualification & EPT_VIOLATION_ACC_INSTR) ? PFERR_FETCH_MASK : 0; - /* - * ept page table entry is present? - * note: unconditionally clear USER_EXEC until mode-based - * execute control is implemented - */ + /* ept page table entry is present? */ error_code |= (exit_qualification & (EPT_VIOLATION_PROT_MASK & ~EPT_VIOLATION_PROT_USER_EXEC)) ? PFERR_PRESENT_MASK : 0; + if (mmu_has_mbec(vcpu->arch.mmu)) + error_code |= (exit_qualification & EPT_VIOLATION_PROT_USER_EXEC) + ? PFERR_PRESENT_MASK : 0; + if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID) error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ? PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK; diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index dbebddf648be..83d9921277ea 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -755,6 +755,14 @@ static int vt_set_identity_map_addr(struct kvm *kvm, u64 ident_addr) return vmx_set_identity_map_addr(kvm, ident_addr); } +static bool vt_tdp_has_smep(struct kvm *kvm) +{ + if (is_td(kvm)) + return false; + + return vmx_tdp_has_smep(kvm); +} + static u64 vt_get_l2_tsc_offset(struct kvm_vcpu *vcpu) { /* TDX doesn't support L2 guest at the moment. */ @@ -966,6 +974,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .set_tss_addr = vt_op(set_tss_addr), .set_identity_map_addr = vt_op(set_identity_map_addr), .get_mt_mask = vmx_get_mt_mask, + .tdp_has_smep = vt_op(tdp_has_smep), .get_exit_info = vt_op(get_exit_info), .get_entry_info = vt_op(get_entry_info), diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index cd1924c6e075..299d4ca60fb3 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2440,6 +2440,7 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | SECONDARY_EXEC_APIC_REGISTER_VIRT | SECONDARY_EXEC_ENABLE_VMFUNC | + SECONDARY_EXEC_MODE_BASED_EPT_EXEC | SECONDARY_EXEC_DESC); if (nested_cpu_has(vmcs12, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 337bbfecc021..d9ba9f14ffd1 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -114,6 +114,9 @@ module_param(emulate_invalid_guest_state, bool, 0444); static bool __read_mostly fasteoi = 1; module_param(fasteoi, bool, 0444); +bool __read_mostly enable_mbec = 1; +module_param_named(mbec, enable_mbec, bool, 0444); + module_param(enable_apicv, bool, 0444); module_param(enable_ipiv, bool, 0444); @@ -2773,6 +2776,7 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, return -EIO; vmx_cap->ept = 0; + _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_MODE_BASED_EPT_EXEC; _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_EPT_VIOLATION_VE; } if (!(_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_VPID) && @@ -4735,6 +4739,9 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx) */ exec_control &= ~SECONDARY_EXEC_ENABLE_VMFUNC; + if (!enable_mbec) + exec_control &= ~SECONDARY_EXEC_MODE_BASED_EPT_EXEC; + /* SECONDARY_EXEC_DESC is enabled/disabled on writes to CR4.UMIP, * in vmx_set_cr4. */ exec_control &= ~SECONDARY_EXEC_DESC; @@ -8622,6 +8629,8 @@ __init int vmx_hardware_setup(void) if (!cpu_has_vmx_ept_ad_bits() || !enable_ept) enable_ept_ad_bits = 0; + if (!cpu_has_ept_mbec() || !enable_ept) + enable_mbec = 0; if (!cpu_has_vmx_unrestricted_guest() || !enable_ept) enable_unrestricted_guest = 0; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index db84e8001da5..0a4e263c4095 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -567,6 +567,7 @@ static inline u8 vmx_get_rvi(void) SECONDARY_EXEC_ENABLE_VMFUNC | \ SECONDARY_EXEC_BUS_LOCK_DETECTION | \ SECONDARY_EXEC_NOTIFY_VM_EXITING | \ + SECONDARY_EXEC_MODE_BASED_EPT_EXEC | \ SECONDARY_EXEC_ENCLS_EXITING | \ SECONDARY_EXEC_EPT_VIOLATION_VE) diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h index d09abeac2b56..409858074246 100644 --- a/arch/x86/kvm/vmx/x86_ops.h +++ b/arch/x86/kvm/vmx/x86_ops.h @@ -4,6 +4,7 @@ #include +#include "capabilities.h" #include "x86.h" __init int vmx_hardware_setup(void); @@ -104,6 +105,11 @@ int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr); int vmx_set_identity_map_addr(struct kvm *kvm, u64 ident_addr); u8 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio); +static inline bool vmx_tdp_has_smep(struct kvm *kvm) +{ + return enable_mbec; +} + void vmx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code); void vmx_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code); From a0efd8cb221fff20f511ecf759c42c076a513ebb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:06 -0400 Subject: [PATCH 0713/5214] KVM: nVMX: pass advanced EPT violation vmexit info to guest KVM will use advanced vmexit information for EPT violations to virtualize MBEC. Pass it to the guest since it is easy and allows testing nested nested. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/vmx.h | 4 ++++ arch/x86/kvm/mmu/paging_tmpl.h | 2 +- arch/x86/kvm/vmx/nested.c | 13 +++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 54aa5be50df9..ed2ded531e55 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -535,6 +535,7 @@ enum vmcs_field { #define VMX_EPT_1GB_PAGE_BIT (1ull << 17) #define VMX_EPT_INVEPT_BIT (1ull << 20) #define VMX_EPT_AD_BIT (1ull << 21) +#define VMX_EPT_ADVANCED_VMEXIT_INFO_BIT (1ull << 22) #define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25) #define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26) @@ -617,6 +618,9 @@ enum vm_entry_failure_code { EPT_VIOLATION_PROT_USER_EXEC) #define EPT_VIOLATION_GVA_IS_VALID BIT(7) #define EPT_VIOLATION_GVA_TRANSLATED BIT(8) +#define EPT_VIOLATION_GVA_USER BIT(9) +#define EPT_VIOLATION_GVA_WRITABLE BIT(10) +#define EPT_VIOLATION_GVA_NX BIT(11) #define EPT_VIOLATION_RWX_TO_PROT(__epte) (((__epte) & VMX_EPT_RWX_MASK) << 3) #define EPT_VIOLATION_USER_EXEC_TO_PROT(__epte) (((__epte) & VMX_EPT_USER_EXECUTABLE_MASK) >> 4) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 8dd9d510fc34..d4ce55195a7c 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -494,7 +494,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, * [2:0] - Derive from the access bits. The exit_qualification might be * out of date if it is serving an EPT misconfiguration. * [5:3] - Calculated by the page walk of the guest EPT page tables - * [7:8] - Derived from [7:8] of real exit_qualification + * [7:11] - Derived from [7:11] of real exit_qualification * * The other bits are set to 0. */ diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 299d4ca60fb3..46b65475765d 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -443,10 +443,14 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu, vm_exit_reason = EXIT_REASON_EPT_MISCONFIG; exit_qualification = 0; } else { + u64 mask = EPT_VIOLATION_GVA_IS_VALID | + EPT_VIOLATION_GVA_TRANSLATED; + if (vmx->nested.msrs.ept_caps & VMX_EPT_ADVANCED_VMEXIT_INFO_BIT) + mask |= EPT_VIOLATION_GVA_USER | + EPT_VIOLATION_GVA_WRITABLE | + EPT_VIOLATION_GVA_NX; exit_qualification = fault->exit_qualification; - exit_qualification |= vmx_get_exit_qual(vcpu) & - (EPT_VIOLATION_GVA_IS_VALID | - EPT_VIOLATION_GVA_TRANSLATED); + exit_qualification |= vmx_get_exit_qual(vcpu) & mask; vm_exit_reason = EXIT_REASON_EPT_VIOLATION; } @@ -7240,7 +7244,8 @@ static void nested_vmx_setup_secondary_ctls(u32 ept_caps, VMX_EPT_PAGE_WALK_5_BIT | VMX_EPTP_WB_BIT | VMX_EPT_INVEPT_BIT | - VMX_EPT_EXECUTE_ONLY_BIT; + VMX_EPT_EXECUTE_ONLY_BIT | + VMX_EPT_ADVANCED_VMEXIT_INFO_BIT; msrs->ept_caps &= ept_caps; msrs->ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT | From 9c167cb3c14c486e683fe4248a8309eb4dd3042b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:07 -0400 Subject: [PATCH 0714/5214] KVM: nVMX: pass PFERR_USER_MASK to MMU on EPT violations For EPT, PFERR_USER_MASK refers not to the CPL of the guest, but to the AND of the U bits encountered while walking guest page tables; this is consistent with how MBEC differentiates between XS and XU. This is available through the "advanced vmexit information for EPT violations" feature. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/common.h | 12 +++++++++--- arch/x86/kvm/vmx/vmx.c | 10 ++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h index 40fa72f31fc7..08005676702c 100644 --- a/arch/x86/kvm/vmx/common.h +++ b/arch/x86/kvm/vmx/common.h @@ -100,9 +100,15 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, error_code |= (exit_qualification & EPT_VIOLATION_PROT_USER_EXEC) ? PFERR_PRESENT_MASK : 0; - if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID) - error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ? - PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK; + if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID) { + if (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) { + error_code |= PFERR_GUEST_FINAL_MASK; + if (exit_qualification & EPT_VIOLATION_GVA_USER) + error_code |= PFERR_USER_MASK; + } else { + error_code |= PFERR_GUEST_PAGE_MASK; + } + } if (vt_is_tdx_private_gpa(vcpu->kvm, gpa)) error_code |= PFERR_PRIVATE_ACCESS; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d9ba9f14ffd1..3173b3a73836 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -2790,6 +2790,16 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, vmx_cap->vpid = 0; } + /* + * Virtualizing MBEC requires advanced vmexit information in order to + * distinguish supervisor and user accesses. For simplicity and clarity + * disable MBEC entirely if advanced vmexit information is not available, + * this way mbec=1 in the kvm_intel module parameters implies availability + * to nested guests as well. + */ + if (!(vmx_cap->ept & VMX_EPT_ADVANCED_VMEXIT_INFO_BIT)) + _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_MODE_BASED_EPT_EXEC; + if (!cpu_has_sgx()) _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_ENCLS_EXITING; From d220bcf026f0a4bc450744e427ad8fc08855f752 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:08 -0400 Subject: [PATCH 0715/5214] KVM: x86/mmu: add support for MBEC to EPT page table walks Extend the page walker to support moving bit 10 of the PTEs into ACC_USER_EXEC_MASK and bit 6 of the exit qualification of EPT violation VM exits. Note that while mmu_has_mbec()/cr4_smep affect the interpretation of ACC_USER_EXEC_MASK and add bit 10 as a "present bit" in guest EPT page table entries, they do not affect how KVM operates on SPTEs. That's because the MMU uses explicit ACC_USER_EXEC_MASK/shadow_xu_mask even for the non-nested EPT; the only difference is that ACC_USER_EXEC_MASK and ACC_EXEC_MASK will always be set in tandem outside the nested scenario. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 13 +++++++++++-- arch/x86/kvm/mmu/paging_tmpl.h | 27 +++++++++++++++++++++------ arch/x86/kvm/mmu/spte.h | 2 ++ arch/x86/kvm/vmx/nested.c | 9 +++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 097af8a1bd25..4c0c3d12ff15 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5570,7 +5570,6 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) { unsigned index; - const u16 x = ACC_BITS_MASK(ACC_EXEC_MASK); const u16 w = ACC_BITS_MASK(ACC_WRITE_MASK); const u16 r = ACC_BITS_MASK(ACC_READ_MASK); @@ -5611,8 +5610,18 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) u16 smapf = 0; if (ept) { - ff = (pfec & PFERR_FETCH_MASK) ? (u16)~x : 0; + const u16 xs = ACC_BITS_MASK(ACC_EXEC_MASK); + const u16 xu = ACC_BITS_MASK(ACC_USER_EXEC_MASK); + + if (pfec & PFERR_FETCH_MASK) { + /* Ignore XU unless MBEC is enabled. */ + if (cr4_smep) + ff = pfec & PFERR_USER_MASK ? (u16)~xu : (u16)~xs; + else + ff = (u16)~xs; + } } else { + const u16 x = ACC_BITS_MASK(ACC_EXEC_MASK); const u16 u = ACC_BITS_MASK(ACC_USER_MASK); /* Faults from kernel mode accesses to user pages */ diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index d4ce55195a7c..f741f7d4cc2d 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -124,12 +124,17 @@ static inline void FNAME(protect_clean_gpte)(struct kvm_mmu *mmu, unsigned *acce *access &= mask; } -static inline int FNAME(is_present_gpte)(unsigned long pte) +static inline int FNAME(is_present_gpte)(struct kvm_mmu *mmu, + unsigned long pte) { #if PTTYPE != PTTYPE_EPT return pte & PT_PRESENT_MASK; #else - return pte & 7; + /* + * For EPT, an entry is present if any of bits 2:0 are set. + * With mode-based execute control, bit 10 also indicates presence. + */ + return pte & (7 | (mmu_has_mbec(mmu) ? VMX_EPT_USER_EXECUTABLE_MASK : 0)); #endif } @@ -152,7 +157,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, u64 *spte, u64 gpte) { - if (!FNAME(is_present_gpte)(gpte)) + if (!FNAME(is_present_gpte)(vcpu->arch.mmu, gpte)) goto no_present; /* Prefetch only accessed entries (unless A/D bits are disabled). */ @@ -173,10 +178,17 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, static inline unsigned FNAME(gpte_access)(u64 gpte) { unsigned access; + /* + * Set bits in ACC_*_MASK even if they might not be used in the + * actual checks. For example, if EFER.NX is clear permission_fault() + * will ignore ACC_EXEC_MASK, and if MBEC is disabled it will + * ignore ACC_USER_EXEC_MASK. + */ #if PTTYPE == PTTYPE_EPT access = ((gpte & VMX_EPT_WRITABLE_MASK) ? ACC_WRITE_MASK : 0) | ((gpte & VMX_EPT_EXECUTABLE_MASK) ? ACC_EXEC_MASK : 0) | - ((gpte & VMX_EPT_READABLE_MASK) ? ACC_READ_MASK : 0); + ((gpte & VMX_EPT_READABLE_MASK) ? ACC_READ_MASK : 0) | + ((gpte & VMX_EPT_USER_EXECUTABLE_MASK) ? ACC_USER_EXEC_MASK : 0); #else /* * P is set here, so the page is always readable and W/U/!NX represent @@ -331,7 +343,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, if (walker->level == PT32E_ROOT_LEVEL) { pte = mmu->get_pdptr(vcpu, (addr >> 30) & 3); trace_kvm_mmu_paging_element(pte, walker->level); - if (!FNAME(is_present_gpte)(pte)) + if (!FNAME(is_present_gpte)(mmu, pte)) goto error; --walker->level; } @@ -414,7 +426,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, */ pte_access = pt_access & (pte ^ walk_nx_mask); - if (unlikely(!FNAME(is_present_gpte)(pte))) + if (unlikely(!FNAME(is_present_gpte)(mmu, pte))) goto error; if (unlikely(FNAME(is_rsvd_bits_set)(mmu, pte, walker->level))) { @@ -521,6 +533,9 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, * ACC_*_MASK flags! */ walker->fault.exit_qualification |= EPT_VIOLATION_RWX_TO_PROT(pte_access); + if (mmu_has_mbec(mmu)) + walker->fault.exit_qualification |= + EPT_VIOLATION_USER_EXEC_TO_PROT(pte_access); } #endif walker->fault.address = addr; diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index f5261d993eac..fe9571837fee 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -395,6 +395,8 @@ static inline bool __is_rsvd_bits_set(struct rsvd_bits_validate *rsvd_check, static inline bool __is_bad_mt_xwr(struct rsvd_bits_validate *rsvd_check, u64 pte) { + if (pte & VMX_EPT_USER_EXECUTABLE_MASK) + pte |= VMX_EPT_EXECUTABLE_MASK; return rsvd_check->bad_mt_xwr & BIT_ULL(pte & 0x3f); } diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 46b65475765d..84f5c25a1f12 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -7452,6 +7452,15 @@ static gpa_t vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_mmu *mmu = vcpu->arch.mmu; BUG_ON(!mmu_is_nested(vcpu)); + + /* + * MBEC differentiates based on the effective U/S bit of + * the guest page tables; not the processor CPL. + */ + access &= ~PFERR_USER_MASK; + if ((pte_access & ACC_USER_MASK) && (access & PFERR_GUEST_FINAL_MASK)) + access |= PFERR_USER_MASK; + return mmu->gva_to_gpa(vcpu, mmu, gpa, access, exception); } From 606ddbfb4c02219cd2499085b38d97a213d35479 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Apr 2026 11:42:09 -0400 Subject: [PATCH 0716/5214] KVM: nVMX: advertise MBEC to nested guests Advertise SECONDARY_EXEC_MODE_BASED_EPT_EXEC (MBEC) to userspace, which allows userspace to expose and advertise the feature to the guest. When MBEC is enabled by the guest, it is passed to the MMU via cr4_smep, and to the processor by the merging of vmcs12->secondary_vm_exec_control into the VMCS02's secondary VM execution controls. Signed-off-by: Jon Kohler Message-ID: <20251223054806.1611168-9-jon@nutanix.com> Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu.h | 2 +- arch/x86/kvm/mmu/mmu.c | 7 ++++--- arch/x86/kvm/mmu/spte.c | 10 ++++++---- arch/x86/kvm/vmx/nested.c | 11 +++++++++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 23bc5b18efd0..e1e3869f568b 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -100,7 +100,7 @@ void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0, unsigned long cr4, u64 efer, gpa_t nested_cr3); void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, int huge_page_level, bool accessed_dirty, - gpa_t new_eptp); + bool mbec, gpa_t new_eptp); bool kvm_can_do_async_pf(struct kvm_vcpu *vcpu); int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code, u64 fault_address, char *insn, int insn_len); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 4c0c3d12ff15..935c5f9b0d17 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5959,7 +5959,7 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_init_shadow_npt_mmu); static union kvm_cpu_role kvm_calc_shadow_ept_root_page_role(struct kvm_vcpu *vcpu, bool accessed_dirty, - bool execonly, u8 level) + bool execonly, u8 level, bool mbec) { union kvm_cpu_role role = {0}; @@ -5969,6 +5969,7 @@ kvm_calc_shadow_ept_root_page_role(struct kvm_vcpu *vcpu, bool accessed_dirty, */ WARN_ON_ONCE(is_smm(vcpu)); role.base.level = level; + role.base.cr4_smep = mbec; role.base.has_4_byte_gpte = false; role.base.direct = false; role.base.ad_disabled = !accessed_dirty; @@ -5984,13 +5985,13 @@ kvm_calc_shadow_ept_root_page_role(struct kvm_vcpu *vcpu, bool accessed_dirty, void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, int huge_page_level, bool accessed_dirty, - gpa_t new_eptp) + bool mbec, gpa_t new_eptp) { struct kvm_mmu *context = &vcpu->arch.guest_mmu; u8 level = vmx_eptp_page_walk_level(new_eptp); union kvm_cpu_role new_mode = kvm_calc_shadow_ept_root_page_role(vcpu, accessed_dirty, - execonly, level); + execonly, level, mbec); if (new_mode.as_u64 != context->cpu_role.as_u64) { /* EPT, and thus nested EPT, does not consume CR0, CR4, nor EFER. */ diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index f41573b0ccfa..d2f5f7dd8fe1 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -517,10 +517,12 @@ void kvm_mmu_set_ept_masks(bool has_ad_bits) * host's MBEC setting does not matter. On hardware without MBEC * the XU bit is reserved-as-ignored, and setting it does no harm. * - * For nested EPT MBEC is not supported, but bit 10 of the gPTE has - * no effect because (a) is_present_gpte() does not treat it as a - * present bit, and (b) permission_fault() uses an mmu->permissions[] - * array that effectively ignores ACC_USER_EXEC_MASK. + * For nested EPT, when MBEC is disabled by L1, correctness relies + * on (a) ignoring bit 10 of the gPTE in is_present_gpte(), rather + * than treating it as a present bit, and (b) permission_fault() + * using an mmu->permissions[] array that effectively ignores + * ACC_USER_EXEC_MASK. Bit 10 of the gPTE does end up mirrored + * in the sPTEs but is ignored because L2 runs with MBEC disabled. */ shadow_xu_mask = VMX_EPT_USER_EXECUTABLE_MASK; shadow_present_mask = VMX_EPT_SUPPRESS_VE_BIT; diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 84f5c25a1f12..bc1046f32ebc 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -469,6 +469,13 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu, vmcs12->guest_physical_address = fault->address; } +static inline bool nested_ept_mbec_enabled(struct kvm_vcpu *vcpu) +{ + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_MODE_BASED_EPT_EXEC); +} + static void nested_ept_new_eptp(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); @@ -477,6 +484,7 @@ static void nested_ept_new_eptp(struct kvm_vcpu *vcpu) kvm_init_shadow_ept_mmu(vcpu, execonly, ept_lpage_level, nested_ept_ad_enabled(vcpu), + nested_ept_mbec_enabled(vcpu), nested_ept_get_eptp(vcpu)); } @@ -7257,6 +7265,9 @@ static void nested_vmx_setup_secondary_ctls(u32 ept_caps, msrs->ept_caps |= VMX_EPT_AD_BIT; } + if (enable_mbec) + msrs->secondary_ctls_high |= + SECONDARY_EXEC_MODE_BASED_EPT_EXEC; /* * Advertise EPTP switching irrespective of hardware support, * KVM emulates it in software so long as VMFUNC is supported. From 55a71224bd90aa485c72cc55c446a26b485a8e36 Mon Sep 17 00:00:00 2001 From: Jon Kohler Date: Wed, 8 Apr 2026 11:42:10 -0400 Subject: [PATCH 0717/5214] KVM: nVMX: allow MBEC with EVMCS Extend EVMCS1_SUPPORTED_2NDEXEC to allow MBEC and EVMCS to coexist. Presenting both EVMCS and MBEC simultaneously causes KVM to filter out MBEC and not present it as a supported control to the guest, preventing performance gains from MBEC when Windows HVCI is enabled. The guest may choose not to use MBEC (e.g., if the admin does not enable Windows HVCI / Memory Integrity), but if they use traditional nested virt (Hyper-V, WSL2, etc.), having EVMCS exposed is important for improving nested guest performance. IOW allowing MBEC and EVMCS to coexist provides maximum optionality to Windows users without overcomplicating VM administration. Signed-off-by: Jon Kohler Message-ID: <20251223054806.1611168-8-jon@nutanix.com> Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/hyperv_evmcs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/vmx/hyperv_evmcs.h b/arch/x86/kvm/vmx/hyperv_evmcs.h index fc7c4e7bd1bf..bc08fe40590e 100644 --- a/arch/x86/kvm/vmx/hyperv_evmcs.h +++ b/arch/x86/kvm/vmx/hyperv_evmcs.h @@ -87,6 +87,7 @@ SECONDARY_EXEC_PT_CONCEAL_VMX | \ SECONDARY_EXEC_BUS_LOCK_DETECTION | \ SECONDARY_EXEC_NOTIFY_VM_EXITING | \ + SECONDARY_EXEC_MODE_BASED_EPT_EXEC | \ SECONDARY_EXEC_ENCLS_EXITING) #define EVMCS1_SUPPORTED_3RDEXEC (0ULL) From 4ae0a67231b424d86384621f370730edb8c94c0e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:11 -0400 Subject: [PATCH 0718/5214] KVM: x86/mmu: propagate access mask from root pages down Until now, all SPTEs have had all kinds of access allowed; however, for GMET to be enabled all the pages have to have ACC_USER_MASK disabled. By marking them as supervisor pages, the processor allows execution from either user or supervisor mode (unlike for normal paging, NPT ignores the U bit for reads and writes). That will mean that the root page's role has ACC_USER_MASK cleared and that has to be propagated down through the kvm_mmu_page tree. Do that, and pass the required access to the kvm_mmu_spte_requested tracepoint since it's not ACC_ALL anymore. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 9 +++++---- arch/x86/kvm/mmu/mmutrace.h | 10 ++++++---- arch/x86/kvm/mmu/paging_tmpl.h | 2 +- arch/x86/kvm/mmu/tdp_mmu.c | 6 +++--- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 935c5f9b0d17..f2b162abd7e5 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -3446,12 +3446,13 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { struct kvm_shadow_walk_iterator it; struct kvm_mmu_page *sp; - int ret; + int ret, access; gfn_t base_gfn = fault->gfn; kvm_mmu_hugepage_adjust(vcpu, fault); - trace_kvm_mmu_spte_requested(fault); + access = vcpu->arch.mmu->root_role.access; + trace_kvm_mmu_spte_requested(fault, access); for_each_shadow_entry(vcpu, fault->addr, it) { /* * We cannot overwrite existing page tables with an NX @@ -3464,7 +3465,7 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) if (it.level == fault->goal_level) break; - sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, true, ACC_ALL); + sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, true, access); if (sp == ERR_PTR(-EEXIST)) continue; @@ -3477,7 +3478,7 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) if (WARN_ON_ONCE(it.level != fault->goal_level)) return -EFAULT; - ret = mmu_set_spte(vcpu, fault->slot, it.sptep, ACC_ALL, + ret = mmu_set_spte(vcpu, fault->slot, it.sptep, access, base_gfn, fault->pfn, fault); if (ret == RET_PF_SPURIOUS) return ret; diff --git a/arch/x86/kvm/mmu/mmutrace.h b/arch/x86/kvm/mmu/mmutrace.h index 3429c1413f42..fa01719baf8d 100644 --- a/arch/x86/kvm/mmu/mmutrace.h +++ b/arch/x86/kvm/mmu/mmutrace.h @@ -373,23 +373,25 @@ TRACE_EVENT( TRACE_EVENT( kvm_mmu_spte_requested, - TP_PROTO(struct kvm_page_fault *fault), - TP_ARGS(fault), + TP_PROTO(struct kvm_page_fault *fault, u8 access), + TP_ARGS(fault, access), TP_STRUCT__entry( __field(u64, gfn) __field(u64, pfn) __field(u8, level) + __field(u8, access) ), TP_fast_assign( __entry->gfn = fault->gfn; __entry->pfn = fault->pfn | (fault->gfn & (KVM_PAGES_PER_HPAGE(fault->goal_level) - 1)); __entry->level = fault->goal_level; + __entry->access = access; ), - TP_printk("gfn %llx pfn %llx level %d", - __entry->gfn, __entry->pfn, __entry->level + TP_printk("gfn %llx pfn %llx level %d access %x", + __entry->gfn, __entry->pfn, __entry->level, __entry->access ) ); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index f741f7d4cc2d..047400af924d 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -734,7 +734,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, */ kvm_mmu_hugepage_adjust(vcpu, fault); - trace_kvm_mmu_spte_requested(fault); + trace_kvm_mmu_spte_requested(fault, gw->pte_access); for (; shadow_walk_okay(&it); shadow_walk_next(&it)) { /* diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 7b1102d26f9c..5a2f8ce9a32b 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -1185,9 +1185,9 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu, } if (unlikely(!fault->slot)) - new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL); + new_spte = make_mmio_spte(vcpu, iter->gfn, sp->role.access); else - wrprot = make_spte(vcpu, sp, fault->slot, ACC_ALL, iter->gfn, + wrprot = make_spte(vcpu, sp, fault->slot, sp->role.access, iter->gfn, fault->pfn, iter->old_spte, fault->prefetch, false, fault->map_writable, &new_spte); @@ -1272,7 +1272,7 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) kvm_mmu_hugepage_adjust(vcpu, fault); - trace_kvm_mmu_spte_requested(fault); + trace_kvm_mmu_spte_requested(fault, root->role.access); rcu_read_lock(); From 978598a230c16d40b7c8a262bd13c70d433c71bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:12 -0400 Subject: [PATCH 0719/5214] KVM: x86/mmu: introduce cpu_role bit for availability of PFEC.I/D While GMET looks a lot like SMEP, it has several annoying differences. The main one is that the availability of the I/D bit in the page fault error code still depends on the host CR4.SMEP and EFER.NXE bits. If the base.cr4_smep bit of the cpu_role is (ab)used to enable GMET, there needs to be another place where the host CR4.SMEP is read from; just merge it with EFER.NXE into a new cpu_role bit that tells paging_tmpl.h whether to set the I/D bit at all. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 7 +++++++ arch/x86/kvm/mmu/mmu.c | 8 ++++++++ arch/x86/kvm/mmu/paging_tmpl.h | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 23a7ac8d7fbe..7dde4ca87752 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -414,6 +414,13 @@ union kvm_mmu_extended_role { unsigned int cr4_smap:1; unsigned int cr4_la57:1; unsigned int efer_lma:1; + + /* + * True if either CR4.SMEP or EFER.NXE are set. For AMD NPT + * this is the "real" host CR4.SMEP whereas cr4_smep is + * actually GMET. + */ + unsigned int has_pferr_fetch:1; }; }; diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f2b162abd7e5..1860e4f3fb00 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -234,6 +234,11 @@ BUILD_MMU_ROLE_ACCESSOR(ext, cr4, la57); BUILD_MMU_ROLE_ACCESSOR(base, efer, nx); BUILD_MMU_ROLE_ACCESSOR(ext, efer, lma); +static inline bool has_pferr_fetch(struct kvm_mmu *mmu) +{ + return mmu->cpu_role.ext.has_pferr_fetch; +} + static inline bool is_cr0_pg(struct kvm_mmu *mmu) { return mmu->cpu_role.base.level > 0; @@ -5793,6 +5798,8 @@ static union kvm_cpu_role kvm_calc_cpu_role(struct kvm_vcpu *vcpu, role.ext.cr4_pke = ____is_efer_lma(regs) && ____is_cr4_pke(regs); role.ext.cr4_la57 = ____is_efer_lma(regs) && ____is_cr4_la57(regs); role.ext.efer_lma = ____is_efer_lma(regs); + + role.ext.has_pferr_fetch = role.base.efer_nx | role.base.cr4_smep; return role; } @@ -5946,6 +5953,7 @@ void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0, /* NPT requires CR0.PG=1. */ WARN_ON_ONCE(cpu_role.base.direct || !cpu_role.base.guest_mode); + cpu_role.base.cr4_smep = false; root_role = cpu_role.base; root_role.level = kvm_mmu_get_tdp_level(vcpu); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 047400af924d..07100bbfc270 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -489,7 +489,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, error: errcode |= write_fault | user_fault; - if (fetch_fault && (is_efer_nx(mmu) || is_cr4_smep(mmu))) + if (fetch_fault && has_pferr_fetch(mmu)) errcode |= PFERR_FETCH_MASK; walker->fault.vector = PF_VECTOR; From 7658b9343a8f65e5fe742301a083db1018320c1d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:13 -0400 Subject: [PATCH 0720/5214] KVM: SVM: add GMET bit definitions GMET (Guest Mode Execute Trap) is an AMD virtualization feature, essentially the nested paging version of SMEP. Hyper-V uses it; add it in preparation for making it available to hypervisors running under KVM. Acked-by: Borislav Petkov (AMD) Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/svm.h | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index de7bd88e539d..d58dbce83f45 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -379,6 +379,7 @@ #define X86_FEATURE_AVIC (15*32+13) /* "avic" Virtual Interrupt Controller */ #define X86_FEATURE_V_VMSAVE_VMLOAD (15*32+15) /* "v_vmsave_vmload" Virtual VMSAVE VMLOAD */ #define X86_FEATURE_VGIF (15*32+16) /* "vgif" Virtual GIF */ +#define X86_FEATURE_GMET (15*32+17) /* Guest Mode Execution Trap */ #define X86_FEATURE_X2AVIC (15*32+18) /* "x2avic" Virtual x2apic */ #define X86_FEATURE_V_SPEC_CTRL (15*32+20) /* "v_spec_ctrl" Virtual SPEC_CTRL */ #define X86_FEATURE_VNMI (15*32+25) /* "vnmi" Virtual NMI */ diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index bcfeb5e7c0ed..aa63431ba92c 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -243,6 +243,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define SVM_MISC_ENABLE_NP BIT(0) #define SVM_MISC_ENABLE_SEV BIT(1) #define SVM_MISC_ENABLE_SEV_ES BIT(2) +#define SVM_MISC_ENABLE_GMET BIT(3) #define SVM_MISC2_ENABLE_V_LBR BIT_ULL(0) #define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE BIT_ULL(1) From ec65797ce341e26563a9cb25943035bd2431a6c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 27 Apr 2026 11:30:56 -0400 Subject: [PATCH 0721/5214] KVM: x86/mmu: hard code more bits in kvm_init_shadow_npt_mmu The host CR0 does not really reflect onto the NPT format because hCR0.PG=1 must be set and hCR0.WP is ignored. Carve that in stone by removing the cr0 argument from kvm_init_shadow_npt_mmu. Pass in WP=1 as well; it does not matter for GMET disabled because PFERR_USER_MASK is always set, but a cleared W bit in the nested page tables cannot be overridden in supervisor mode when GMET is enabled, either. In fact, since CR0.WP=0 is the weird "extra accesses allowed" mode, it is acutally easier think about it being always set. Likewise, clear X86_CR4_SMAP to avoid that KVM erroneously faults on supervisor accesses to an U=1 page. Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu.h | 4 ++-- arch/x86/kvm/mmu/mmu.c | 8 ++++---- arch/x86/kvm/svm/nested.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index e1e3869f568b..1b354e1f2d81 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -96,8 +96,8 @@ void kvm_mmu_set_me_spte_mask(u64 me_value, u64 me_mask); void kvm_mmu_set_ept_masks(bool has_ad_bits); void kvm_init_mmu(struct kvm_vcpu *vcpu); -void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0, - unsigned long cr4, u64 efer, gpa_t nested_cr3); +void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr4, + u64 efer, gpa_t nested_cr3); void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, int huge_page_level, bool accessed_dirty, bool mbec, gpa_t new_eptp); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 1860e4f3fb00..b8ff834abf88 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5939,13 +5939,13 @@ static void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, shadow_mmu_init_context(vcpu, context, cpu_role, root_role); } -void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr0, - unsigned long cr4, u64 efer, gpa_t nested_cr3) +void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr4, + u64 efer, gpa_t nested_cr3) { struct kvm_mmu *context = &vcpu->arch.guest_mmu; struct kvm_mmu_role_regs regs = { - .cr0 = cr0, - .cr4 = cr4 & ~X86_CR4_PKE, + .cr0 = X86_CR0_PG | X86_CR0_WP, + .cr4 = cr4 & ~(X86_CR4_PKE | X86_CR4_SMAP), .efer = efer, }; union kvm_cpu_role cpu_role = kvm_calc_cpu_role(vcpu, ®s); diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index df232153eb24..a1cffd274000 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -93,7 +93,7 @@ static void nested_svm_init_mmu_context(struct kvm_vcpu *vcpu) * when called via KVM_SET_NESTED_STATE, that state may _not_ match current * vCPU state. CR0.WP is explicitly ignored, while CR0.PG is required. */ - kvm_init_shadow_npt_mmu(vcpu, X86_CR0_PG, svm->vmcb01.ptr->save.cr4, + kvm_init_shadow_npt_mmu(vcpu, svm->vmcb01.ptr->save.cr4, svm->vmcb01.ptr->save.efer, svm->nested.ctl.nested_cr3); vcpu->arch.mmu->get_guest_pgd = nested_svm_get_tdp_cr3; From 3129c6b81cabf18f597497213e3130a948fc1bb9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 27 Apr 2026 11:45:52 -0400 Subject: [PATCH 0722/5214] KVM: x86/mmu: add support for GMET to NPT page table walks GMET allows page table entries to be created with U=0 in NPT. However, when GMET=1 U=0 only affects execution, not reads or writes. Ignore user faults on non-fetch accesses for NPT GMET. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/mmu.h | 2 +- arch/x86/kvm/mmu/mmu.c | 18 ++++++++++++------ arch/x86/kvm/svm/nested.c | 10 +++++++--- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7dde4ca87752..1da3d5c59e15 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -370,6 +370,8 @@ union kvm_mmu_page_role { * cr4_smep is also set for EPT MBEC. Because it affects * which pages are considered non-present (bit 10 additionally * must be zero if MBEC is on) it has to be in the base role. + * It also has to be in the base role for AMD GMET because + * kernel-executable pages need to have U=0 with GMET enabled. */ unsigned cr4_smep:1; diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 1b354e1f2d81..ddf4e467c071 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -97,7 +97,7 @@ void kvm_mmu_set_ept_masks(bool has_ad_bits); void kvm_init_mmu(struct kvm_vcpu *vcpu); void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr4, - u64 efer, gpa_t nested_cr3); + u64 efer, gpa_t nested_cr3, u64 misc_ctl); void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, int huge_page_level, bool accessed_dirty, bool mbec, gpa_t new_eptp); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index b8ff834abf88..d6c595797542 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include "trace.h" @@ -5572,7 +5573,7 @@ reset_ept_shadow_zero_bits_mask(struct kvm_mmu *context, bool execonly) (14 & (access) ? 1 << 14 : 0) | \ (15 & (access) ? 1 << 15 : 0)) -static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) +static void update_permission_bitmask(struct kvm_mmu *mmu, bool tdp, bool ept) { unsigned index; @@ -5633,7 +5634,12 @@ static void update_permission_bitmask(struct kvm_mmu *mmu, bool ept) /* Faults from kernel mode accesses to user pages */ u16 kf = (pfec & PFERR_USER_MASK) ? 0 : u; - uf = (pfec & PFERR_USER_MASK) ? (u16)~u : 0; + /* + * For NPT GMET, U=0 does not affect reads and writes. Fetches + * are handled below via cr4_smep. + */ + if (!(tdp && cr4_smep)) + uf = (pfec & PFERR_USER_MASK) ? (u16)~u : 0; if (efer_nx) ff |= (pfec & PFERR_FETCH_MASK) ? (u16)~x : 0; @@ -5744,7 +5750,7 @@ static void reset_guest_paging_metadata(struct kvm_vcpu *vcpu, return; reset_guest_rsvds_bits_mask(vcpu, mmu); - update_permission_bitmask(mmu, false); + update_permission_bitmask(mmu, mmu == &vcpu->arch.guest_mmu, false); update_pkru_bitmask(mmu); } @@ -5940,7 +5946,7 @@ static void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, } void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr4, - u64 efer, gpa_t nested_cr3) + u64 efer, gpa_t nested_cr3, u64 misc_ctl) { struct kvm_mmu *context = &vcpu->arch.guest_mmu; struct kvm_mmu_role_regs regs = { @@ -5953,7 +5959,7 @@ void kvm_init_shadow_npt_mmu(struct kvm_vcpu *vcpu, unsigned long cr4, /* NPT requires CR0.PG=1. */ WARN_ON_ONCE(cpu_role.base.direct || !cpu_role.base.guest_mode); - cpu_role.base.cr4_smep = false; + cpu_role.base.cr4_smep = (misc_ctl & SVM_MISC_ENABLE_GMET) != 0; root_role = cpu_role.base; root_role.level = kvm_mmu_get_tdp_level(vcpu); @@ -6011,7 +6017,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, context->gva_to_gpa = ept_gva_to_gpa; context->sync_spte = ept_sync_spte; - update_permission_bitmask(context, true); + update_permission_bitmask(context, true, true); context->pkru_mask = 0; reset_rsvds_bits_mask_ept(vcpu, context, execonly, huge_page_level); reset_ept_shadow_zero_bits_mask(context, execonly); diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index a1cffd274000..7adfa7da210d 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -95,7 +95,8 @@ static void nested_svm_init_mmu_context(struct kvm_vcpu *vcpu) */ kvm_init_shadow_npt_mmu(vcpu, svm->vmcb01.ptr->save.cr4, svm->vmcb01.ptr->save.efer, - svm->nested.ctl.nested_cr3); + svm->nested.ctl.nested_cr3, + svm->nested.ctl.misc_ctl); vcpu->arch.mmu->get_guest_pgd = nested_svm_get_tdp_cr3; vcpu->arch.mmu->get_pdptr = nested_svm_get_tdp_pdptr; vcpu->arch.mmu->inject_page_fault = nested_svm_inject_npf_exit; @@ -2076,12 +2077,15 @@ static gpa_t svm_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, struct x86_exception *exception, u64 pte_access) { + struct vcpu_svm *svm = to_svm(vcpu); struct kvm_mmu *mmu = vcpu->arch.mmu; BUG_ON(!mmu_is_nested(vcpu)); - /* NPT walks are always user-walks */ - access |= PFERR_USER_MASK; + /* Non-GMET walks are always user-walks */ + if (!(svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_GMET)) + access |= PFERR_USER_MASK; + return mmu->gva_to_gpa(vcpu, mmu, gpa, access, exception); } From fe080f58dec24049b4e5ecfaab28ba0881c67abe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:15 -0400 Subject: [PATCH 0723/5214] KVM: SVM: enable GMET and set it in MMU role Set the GMET bit in the nested control field. This has effectively no impact as long as NPT page tables are changed to have U=0. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 6 +++++- arch/x86/kvm/svm/nested.c | 9 ++++++--- arch/x86/kvm/svm/svm.c | 16 ++++++++++++++++ arch/x86/kvm/svm/svm.h | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index d6c595797542..3c15244ff0d8 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5855,7 +5855,6 @@ kvm_calc_tdp_mmu_root_page_role(struct kvm_vcpu *vcpu, { union kvm_mmu_page_role role = {0}; - role.access = ACC_ALL; role.cr0_wp = true; role.cr4_smep = kvm_x86_call(tdp_has_smep)(vcpu->kvm); role.efer_nx = true; @@ -5866,6 +5865,11 @@ kvm_calc_tdp_mmu_root_page_role(struct kvm_vcpu *vcpu, role.direct = true; role.has_4_byte_gpte = false; + /* All TDP pages are supervisor-executable */ + role.access = ACC_ALL; + if (role.cr4_smep && shadow_user_mask) + role.access &= ~ACC_USER_MASK; + return role; } diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 7adfa7da210d..74a1df1cb84f 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -858,7 +858,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) * the latter, L1 runs L2 with shadow page tables that translate L2 GVAs * to L1 GPAs, so the same NPTs can be used for L1 and L2. */ - vmcb02->control.misc_ctl = vmcb01->control.misc_ctl & SVM_MISC_ENABLE_NP; + vmcb02->control.misc_ctl = vmcb01->control.misc_ctl & (SVM_MISC_ENABLE_NP | SVM_MISC_ENABLE_GMET); vmcb02->control.iopm_base_pa = vmcb01->control.iopm_base_pa; vmcb02->control.msrpm_base_pa = vmcb01->control.msrpm_base_pa; vmcb_mark_dirty(vmcb02, VMCB_PERM_MAP); @@ -895,9 +895,12 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) /* Also overwritten later if necessary. */ vmcb02->control.tlb_ctl = TLB_CONTROL_DO_NOTHING; - /* nested_cr3. */ - if (nested_npt_enabled(svm)) + /* Use vmcb01 MMU and format if guest does not use nNPT */ + if (nested_npt_enabled(svm)) { + vmcb02->control.misc_ctl &= ~SVM_MISC_ENABLE_GMET; + nested_svm_init_mmu_context(vcpu); + } vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset(vcpu->arch.l1_tsc_offset, vmcb12_ctrl->tsc_offset, diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index e7fdd7a9c280..3895d8794366 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -138,6 +138,9 @@ module_param(pause_filter_count_max, ushort, 0444); bool __ro_after_init npt_enabled = true; module_param_named(npt, npt_enabled, bool, 0444); +bool gmet_enabled = true; +module_param_named(gmet, gmet_enabled, bool, 0444); + /* allow nested virtualization in KVM/SVM */ static int __ro_after_init nested = true; module_param(nested, int, 0444); @@ -1209,6 +1212,10 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event) save->g_pat = vcpu->arch.pat; save->cr3 = 0; } + + if (gmet_enabled) + control->misc_ctl |= SVM_MISC_ENABLE_GMET; + svm->current_vmcb->asid_generation = 0; svm->asid = 0; @@ -4612,6 +4619,11 @@ svm_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall) hypercall[2] = 0xd9; } +static bool svm_tdp_has_smep(struct kvm *kvm) +{ + return gmet_enabled; +} + /* * The kvm parameter can be NULL (module initialization, or invocation before * VM creation). Be sure to check the kvm parameter before using it. @@ -5355,6 +5367,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = { .write_tsc_multiplier = svm_write_tsc_multiplier, .load_mmu_pgd = svm_load_mmu_pgd, + .tdp_has_smep = svm_tdp_has_smep, .check_intercept = svm_check_intercept, .handle_exit_irqoff = svm_handle_exit_irqoff, @@ -5588,6 +5601,9 @@ static __init int svm_hardware_setup(void) if (!boot_cpu_has(X86_FEATURE_NPT)) npt_enabled = false; + if (!npt_enabled || !boot_cpu_has(X86_FEATURE_GMET)) + gmet_enabled = false; + /* Force VM NPT level equal to the host's paging level */ kvm_configure_mmu(npt_enabled, get_npt_level(), get_npt_level(), PG_LEVEL_1G); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index a10668d17a16..dd93b3daefa9 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -44,6 +44,7 @@ static inline struct page *__sme_pa_to_page(unsigned long pa) #define IOPM_SIZE PAGE_SIZE * 3 #define MSRPM_SIZE PAGE_SIZE * 2 +extern bool gmet_enabled; extern bool npt_enabled; extern int nrips; extern int vgif; From 878fffd76c22b8afb53e4b9409f355a99b9f3ee1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:16 -0400 Subject: [PATCH 0724/5214] KVM: SVM: work around errata 1218 According to AMD, the hypervisor may not be able to determine whether a fault was a GMET fault or an NX fault based on EXITINFO1, and software "must read the relevant VMCB to determine whether a fault was a GMET fault or an NX fault". The APM further details that they meant the CPL field. KVM uses the page fault error code to distinguish the causes of a nested page fault, so recalculate the PFERR_USER_MASK bit of the vmexit information. Only do it for fetches and only if GMET is in use, because KVM does not differentiate based on PFERR_USER_MASK for other nested NPT page faults. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 3895d8794366..fd79874c5f4b 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1993,6 +1993,18 @@ static int npf_interception(struct kvm_vcpu *vcpu) } } + if (!is_sev_es_guest(vcpu) && + (svm->vmcb->control.misc_ctl & SVM_MISC_ENABLE_GMET) && + (error_code & PFERR_FETCH_MASK)) { + /* + * Work around errata 1218: EXITINFO1[2] May Be Incorrectly Set + * When GMET (Guest Mode Execute Trap extension) is Enabled + */ + error_code |= PFERR_USER_MASK; + if (svm_get_cpl(vcpu) != 3) + error_code &= ~PFERR_USER_MASK; + } + if (is_sev_snp_guest(vcpu) && (error_code & PFERR_GUEST_ENC_MASK)) error_code |= PFERR_PRIVATE_ACCESS; From 687ee95c1b6d7509a25bbc661e62801aa06cc2ae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2026 11:42:17 -0400 Subject: [PATCH 0725/5214] KVM: nSVM: enable GMET for guests All that needs to be done is moving the GMET bit from vmcb12 to vmcb02. The only new thing is that __nested_copy_vmcb_control_to_cache now ensures that ignored-if-unavailable bits are zero in svm->nested.ctl. Tested-by: David Riley Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/nested.c | 6 +++++- arch/x86/kvm/svm/svm.c | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 74a1df1cb84f..3d1fd1776e19 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -489,11 +489,14 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu, nested_svm_sanitize_intercept(vcpu, to, SKINIT); nested_svm_sanitize_intercept(vcpu, to, RDPRU); - /* Always clear SVM_MISC_ENABLE_NP if the guest cannot use NPTs */ + /* Always clear misc_ctl bits that the guest cannot use */ to->misc_ctl = from->misc_ctl; if (!guest_cpu_cap_has(vcpu, X86_FEATURE_NPT)) to->misc_ctl &= ~SVM_MISC_ENABLE_NP; + if (!gmet_enabled || !guest_cpu_cap_has(vcpu, X86_FEATURE_GMET)) + to->misc_ctl &= ~SVM_MISC_ENABLE_GMET; + to->iopm_base_pa = from->iopm_base_pa & PAGE_MASK; to->msrpm_base_pa = from->msrpm_base_pa & PAGE_MASK; to->tsc_offset = from->tsc_offset; @@ -898,6 +901,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) /* Use vmcb01 MMU and format if guest does not use nNPT */ if (nested_npt_enabled(svm)) { vmcb02->control.misc_ctl &= ~SVM_MISC_ENABLE_GMET; + vmcb02->control.misc_ctl |= (svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_GMET); nested_svm_init_mmu_context(vcpu); } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index fd79874c5f4b..a82471a6d3ea 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5504,6 +5504,9 @@ static __init void svm_set_cpu_caps(void) if (boot_cpu_has(X86_FEATURE_PFTHRESHOLD)) kvm_cpu_cap_set(X86_FEATURE_PFTHRESHOLD); + if (gmet_enabled) + kvm_cpu_cap_set(X86_FEATURE_GMET); + if (vgif) kvm_cpu_cap_set(X86_FEATURE_VGIF); From b9ea81ec1fd62a3371fe6f9b4c3340a6b2c3daea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 May 2026 16:11:14 +0200 Subject: [PATCH 0726/5214] USB: serial: mxuport: update number-of-ports encoding In preparation for adding support for 3, 5, 6 and 7 port devices, replace the current one-bit-per-type encoding of the number of ports with a more compact four bit encoding (2..16 ports or undefined). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mxuport.c | 38 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ad5fdf55a02e..591be1fe3737 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -146,11 +146,6 @@ #define MX_WAIT_FOR_LOW_WATER 0x0040 #define MX_WAIT_FOR_SEND_NEXT 0x0080 -#define MX_UPORT_2_PORT BIT(0) -#define MX_UPORT_4_PORT BIT(1) -#define MX_UPORT_8_PORT BIT(2) -#define MX_UPORT_16_PORT BIT(3) - /* This structure holds all of the local port information */ struct mxuport_port { u8 mcr_state; /* Last MCR state */ @@ -159,26 +154,31 @@ struct mxuport_port { spinlock_t spinlock; /* Protects msr_state */ }; +/* Encode number of ports (2..16 or undefined) */ +#define MX_PORTS_MASK GENMASK(3, 0) +#define MX_PORTS_OFFSET 1 +#define MX_PORTS(n) (((n) - MX_PORTS_OFFSET) & MX_PORTS_MASK) + /* Table of devices that work with this driver */ static const struct usb_device_id mxuport_idtable[] = { { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1250_PID), - .driver_info = MX_UPORT_2_PORT }, + .driver_info = MX_PORTS(2) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1251_PID), - .driver_info = MX_UPORT_2_PORT }, + .driver_info = MX_PORTS(2) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1410_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1450_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1451_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1618_PID), - .driver_info = MX_UPORT_8_PORT }, + .driver_info = MX_PORTS(8) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1658_PID), - .driver_info = MX_UPORT_8_PORT }, + .driver_info = MX_PORTS(8) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1613_PID), - .driver_info = MX_UPORT_16_PORT }, + .driver_info = MX_PORTS(16) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1653_PID), - .driver_info = MX_UPORT_16_PORT }, + .driver_info = MX_PORTS(16) }, {} /* Terminating entry */ }; @@ -942,14 +942,8 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, int num_ports; int i; - if (features & MX_UPORT_2_PORT) { - num_ports = 2; - } else if (features & MX_UPORT_4_PORT) { - num_ports = 4; - } else if (features & MX_UPORT_8_PORT) { - num_ports = 8; - } else if (features & MX_UPORT_16_PORT) { - num_ports = 16; + if (features & MX_PORTS_MASK) { + num_ports = (features & MX_PORTS_MASK) + MX_PORTS_OFFSET; } else { dev_warn(&serial->interface->dev, "unknown device, assuming two ports\n"); From 7a19b158f3c9bfd5cfbb3ed9ac99e2ec6895dfb3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:41 +0200 Subject: [PATCH 0727/5214] USB: serial: drop unused tty_driver includes USB serial drivers do not use anything from tty_driver.h directly (only core does) so drop the unnecessary include directives. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/belkin_sa.c | 1 - drivers/usb/serial/cyberjack.c | 1 - drivers/usb/serial/cypress_m8.c | 1 - drivers/usb/serial/digi_acceleport.c | 1 - drivers/usb/serial/empeg.c | 1 - drivers/usb/serial/f81232.c | 1 - drivers/usb/serial/ftdi_sio.c | 1 - drivers/usb/serial/garmin_gps.c | 1 - drivers/usb/serial/io_edgeport.c | 1 - drivers/usb/serial/io_ti.c | 1 - drivers/usb/serial/ipaq.c | 1 - drivers/usb/serial/ir-usb.c | 1 - drivers/usb/serial/iuu_phoenix.c | 1 - drivers/usb/serial/keyspan.c | 1 - drivers/usb/serial/keyspan_pda.c | 1 - drivers/usb/serial/kl5kusb105.c | 1 - drivers/usb/serial/kobil_sct.c | 1 - drivers/usb/serial/mct_u232.c | 1 - drivers/usb/serial/metro-usb.c | 1 - drivers/usb/serial/mos7720.c | 1 - drivers/usb/serial/mos7840.c | 1 - drivers/usb/serial/mxuport.c | 1 - drivers/usb/serial/omninet.c | 1 - drivers/usb/serial/opticon.c | 1 - drivers/usb/serial/oti6858.c | 1 - drivers/usb/serial/pl2303.c | 1 - drivers/usb/serial/quatech2.c | 1 - drivers/usb/serial/safe_serial.c | 1 - drivers/usb/serial/spcp8x5.c | 1 - drivers/usb/serial/ssu100.c | 1 - drivers/usb/serial/symbolserial.c | 1 - drivers/usb/serial/ti_usb_3410_5052.c | 1 - drivers/usb/serial/visor.c | 1 - drivers/usb/serial/whiteheat.c | 1 - 34 files changed, 34 deletions(-) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 38ac910b1082..adeb8e3441cf 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 4e8ceb23c27d..f457c8a65934 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index afff1a0f4298..5322f4b3cce6 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d515df045c4c..85dbf87aa361 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index aedcf7ebd269..a9f3d57236b2 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 9262a2ac97f5..74b55fd9fd3f 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index af14548fa03d..88dd32da82c2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 7205483a0115..6ab3ad7be7fc 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 3f5889145e51..4cf77ead9334 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb55370e036f..21a631d8e88f 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 3c6a9b9b9c2b..d9c9b78c41bd 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 12e928d25ba1..bda33a8976c7 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 0ca111b111c7..6f2fd3a39d8e 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 46448843541a..eb6e3d9d9ea7 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 5036600dd334..23a7683669eb 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index ed8531a64768..64b7539043b1 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 6126afd67a7b..2be31d7a7e1f 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 18844b92bd08..e009c2500a91 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index bc834cc48550..45e370593d4c 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 94459408e7fb..bc3b631041a9 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c0d0c24b074b..ef803e74ab4b 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 591be1fe3737..8f7ff187dbf5 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index aa1e9745f967..3e44250dd360 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index e2bed477ad57..b0c6e3578cf8 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 3ef5b5d8ce1a..bb55bbb7ff25 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 50dc838e8115..790c44c5d9b6 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index b05c655b3cc4..fc94d6026a62 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 238b54993446..fa4128a0c933 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -64,7 +64,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 2fbb48f19efd..5d5ea63da5df 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index da6316410b77..09fb698d3d89 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 7a5aa39172a8..c1f54248ecbb 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index b3591d6d7645..a68d91c548b9 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 062a38fe0c1c..8e1c7233ec45 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 6e2b52b2b5f8..2487a4d81b98 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include From 718f4416f0dae38d993996b527523fb5f91c76a0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:42 +0200 Subject: [PATCH 0728/5214] USB: serial: drop unused tty_flip includes These drivers (and usb-serial.c) no longer use anything from tty_flip.h directly so drop the unnecessary include directives. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/empeg.c | 1 - drivers/usb/serial/ipaq.c | 1 - drivers/usb/serial/ipw.c | 1 - drivers/usb/serial/option.c | 1 - drivers/usb/serial/qcserial.c | 1 - drivers/usb/serial/spcp8x5.c | 1 - drivers/usb/serial/usb-serial.c | 1 - drivers/usb/serial/visor.c | 1 - drivers/usb/serial/whiteheat.c | 1 - 9 files changed, 9 deletions(-) diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a9f3d57236b2..48e1a58ea3f5 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index d9c9b78c41bd..eb9b7a7b1d14 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 83709d678b3a..b900d0317a71 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42e4cecd28ac..fd5f1d9ab2f2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1a930dc668e4..105c0a0eae86 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5d5ea63da5df..c11d64bf08fb 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0e072fd87c3d..67e880955ce7 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 8e1c7233ec45..06e30be10ca6 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 2487a4d81b98..7ba05a1a3eae 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include From 02ea02a31e963bf3cb032badfd82d385a6921103 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:43 +0200 Subject: [PATCH 0729/5214] USB: serial: xr: add missing uaccess include Add the missing uaccess.h include, which is needed since TIOCSRS485 support was added, instead of relying on the header being included indirectly. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/xr_serial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 9fc4082de770..352c765d8803 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include From 1e578ec51cace9dcef961225b72b8da46ade13ae Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:44 +0200 Subject: [PATCH 0730/5214] USB: serial: drop unused uaccess includes These drivers (and usb-serial.c) no longer use anything from uaccess.h directly so drop the unnecessary include directives. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 1 - drivers/usb/serial/belkin_sa.c | 1 - drivers/usb/serial/cyberjack.c | 1 - drivers/usb/serial/cypress_m8.c | 1 - drivers/usb/serial/digi_acceleport.c | 1 - drivers/usb/serial/empeg.c | 1 - drivers/usb/serial/f81232.c | 1 - drivers/usb/serial/f81534.c | 1 - drivers/usb/serial/garmin_gps.c | 1 - drivers/usb/serial/generic.c | 1 - drivers/usb/serial/io_ti.c | 1 - drivers/usb/serial/ipaq.c | 1 - drivers/usb/serial/ipw.c | 1 - drivers/usb/serial/ir-usb.c | 1 - drivers/usb/serial/iuu_phoenix.c | 1 - drivers/usb/serial/keyspan.c | 1 - drivers/usb/serial/keyspan_pda.c | 1 - drivers/usb/serial/kl5kusb105.c | 1 - drivers/usb/serial/kobil_sct.c | 1 - drivers/usb/serial/mct_u232.c | 1 - drivers/usb/serial/metro-usb.c | 1 - drivers/usb/serial/mxuport.c | 1 - drivers/usb/serial/omninet.c | 1 - drivers/usb/serial/opticon.c | 1 - drivers/usb/serial/oti6858.c | 1 - drivers/usb/serial/pl2303.c | 1 - drivers/usb/serial/quatech2.c | 1 - drivers/usb/serial/safe_serial.c | 1 - drivers/usb/serial/ssu100.c | 1 - drivers/usb/serial/symbolserial.c | 1 - drivers/usb/serial/ti_usb_3410_5052.c | 1 - drivers/usb/serial/usb-serial.c | 1 - drivers/usb/serial/usb_wwan.c | 1 - drivers/usb/serial/visor.c | 1 - drivers/usb/serial/whiteheat.c | 1 - drivers/usb/serial/wishbone-serial.c | 1 - drivers/usb/serial/xsens_mt.c | 1 - 37 files changed, 37 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index d974da43fba3..3a574b6ee0c4 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index adeb8e3441cf..36dcdb15d6ab 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include "belkin_sa.h" diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index f457c8a65934..57e20eb02a46 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 5322f4b3cce6..1d61968ab69a 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include "cypress_m8.h" diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 85dbf87aa361..a75813a57fbe 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 48e1a58ea3f5..4937575caff1 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 74b55fd9fd3f..3a32f93a9738 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 685930ac8383..0037767e4bbf 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -28,7 +28,6 @@ #include #include #include -#include /* Serial Port register Address */ #define F81534_UART_BASE_ADDRESS 0x1200 diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 6ab3ad7be7fc..9f955962d546 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 757f0a586ddb..6eaf74930aa3 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 21a631d8e88f..28e16515c7f1 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index eb9b7a7b1d14..c01bdbcb3f55 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index b900d0317a71..5be153f2c68d 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -40,7 +40,6 @@ #include #include #include -#include #include "usb-wwan.h" #define DRIVER_AUTHOR "Roelf Diedericks" diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index bda33a8976c7..aac84539730d 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 6f2fd3a39d8e..3b1f7546fb5c 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include "iuu_phoenix.h" diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index eb6e3d9d9ea7..4eea5ced2199 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 23a7683669eb..3b99f9676c35 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 64b7539043b1..e7917871ddf1 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2be31d7a7e1f..4a622cf8e6ae 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index e009c2500a91..0e59289e7518 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 45e370593d4c..5fcd89f57ef3 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver" diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 8f7ff187dbf5..9bdc5e2fee96 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 3e44250dd360..1f35a7e7654f 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index b0c6e3578cf8..152dfc901cd8 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -16,7 +16,6 @@ #include #include #include -#include #define CONTROL_RTS 0x02 #define RESEND_CTS_STATE 0x03 diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index bb55bbb7ff25..77f34d7651d6 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include "oti6858.h" diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 790c44c5d9b6..128c80f11f49 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index fc94d6026a62..dc2b39810a26 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -19,7 +19,6 @@ #include #include #include -#include /* default urb timeout for usb operations */ #define QT2_USB_TIMEOUT USB_CTRL_SET_TIMEOUT diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index fa4128a0c933..a82b6453d79c 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -67,7 +67,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 09fb698d3d89..b0d51558b73c 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -15,7 +15,6 @@ #include #include #include -#include #define QT_OPEN_CLOSE_CHANNEL 0xca #define QT_SET_GET_DEVICE 0xc2 diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index c1f54248ecbb..8823671a9c7a 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -14,7 +14,6 @@ #include #include #include -#include static const struct usb_device_id id_table[] = { { USB_DEVICE(0x05e0, 0x0600) }, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a68d91c548b9..b3f00e315815 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 67e880955ce7..45e8a0b8d438 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index e752ffa4dc62..a183cc4515ed 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 06e30be10ca6..75f290a67cb4 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 7ba05a1a3eae..106e9b5443d3 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index 670d573f6b63..4a1fed4cb57b 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -11,7 +11,6 @@ #include #include #include -#include #define GSI_VENDOR_OPENCLOSE 0xB0 diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index 382b3698c1d5..151a6e893ba2 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -10,7 +10,6 @@ #include #include #include -#include #define XSENS_VID 0x2639 From 63c3a1a9be06f8f8c75d54ef9331e35adb2e7919 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:45 +0200 Subject: [PATCH 0731/5214] USB: serial: drop unused moduleparam includes These drivers (and usb-serial.c) no longer use anything from moduleparam.h so drop the unnecessary include directives. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 1 - drivers/usb/serial/metro-usb.c | 1 - drivers/usb/serial/oti6858.c | 1 - drivers/usb/serial/pl2303.c | 1 - drivers/usb/serial/usb-serial.c | 1 - drivers/usb/serial/visor.c | 1 - 6 files changed, 6 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 3a32f93a9738..dcceb95d7d99 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 5fcd89f57ef3..35473544f1c8 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 77f34d7651d6..c6364e8ff71f 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 128c80f11f49..0bcbdcea52af 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 45e8a0b8d438..17edc057a311 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 75f290a67cb4..5706c46c49c2 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include From 7f5d66d4b458c2141dfe49c6d5961b49079161ba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:46 +0200 Subject: [PATCH 0732/5214] USB: serial: garmin_gps: drop unused atomic include This driver no longer uses anything from atomic.h so drop the unused include directive. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/garmin_gps.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 9f955962d546..8020149f5658 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include From f0fc120c707bf6ccc8f6b8065b25fde4592f104f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:47 +0200 Subject: [PATCH 0733/5214] USB: serial: add missing atomic includes Add the missing atomic.h include to the two driver that use it but did not include it directly. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 1 + drivers/usb/serial/mos7720.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 4cf77ead9334..34ccf7820537 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -25,6 +25,7 @@ * */ +#include #include #include #include diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index bc3b631041a9..104f43a8f4d8 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -17,6 +17,8 @@ * Copyright (C) 2000 Inside Out Networks, All rights reserved. * Copyright (C) 2001-2002 Greg Kroah-Hartman */ + +#include #include #include #include From e690592f98c81416c5be5ce65828aa4665abc302 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 09:14:48 +0200 Subject: [PATCH 0734/5214] USB: serial: whiteheat: drop termbits include The termios definitions are provided by the termios.h header file which is included by tty.h so drop the redundant asm/termbits.h include directive. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/whiteheat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 106e9b5443d3..a2d83f69e00e 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include From 718fc81de0cfd3751465062deda01b8ec5560d00 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Wed, 6 May 2026 00:53:20 +0530 Subject: [PATCH 0735/5214] staging: vme_user: remove unnecessary NULL initialization before list iteration Remove redundant NULL initialization for list iterator because variables used as iterators in list_for_each_entry() do not require initialization, as the macro assigns them before first use. No functional change. Signed-off-by: Shyam Sunder Reddy Padira Link: https://patch.msgid.link/20260505192320.107936-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/vme_user/vme.c b/drivers/staging/vme_user/vme.c index 7220aba7b919..cb9796c87ee8 100644 --- a/drivers/staging/vme_user/vme.c +++ b/drivers/staging/vme_user/vme.c @@ -253,7 +253,7 @@ struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address, { struct vme_bridge *bridge; struct vme_slave_resource *allocated_image = NULL; - struct vme_slave_resource *slave_image = NULL; + struct vme_slave_resource *slave_image; struct vme_resource *resource = NULL; bridge = vdev->bridge; @@ -447,7 +447,7 @@ struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address, { struct vme_bridge *bridge; struct vme_master_resource *allocated_image = NULL; - struct vme_master_resource *master_image = NULL; + struct vme_master_resource *master_image; struct vme_resource *resource = NULL; bridge = vdev->bridge; @@ -818,7 +818,7 @@ struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route) { struct vme_bridge *bridge; struct vme_dma_resource *allocated_ctrlr = NULL; - struct vme_dma_resource *dma_ctrlr = NULL; + struct vme_dma_resource *dma_ctrlr; struct vme_resource *resource = NULL; /* XXX Not checking resource attributes */ @@ -1426,7 +1426,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev) { struct vme_bridge *bridge; struct vme_lm_resource *allocated_lm = NULL; - struct vme_lm_resource *lm = NULL; + struct vme_lm_resource *lm; struct vme_resource *resource = NULL; bridge = vdev->bridge; From 9bfa3674d9632648db96b5a883ee4209a9213333 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Wed, 6 May 2026 01:31:44 +0530 Subject: [PATCH 0736/5214] staging: vme_user: simplify boolean comparisons Replace explicit comparisons against zero with logical Not operator. This follows the standard kernel coding style for boolean logic. No functional changes. Signed-off-by: Shyam Sunder Reddy Padira Link: https://patch.msgid.link/20260505200145.160377-1-shyamsunderreddypadira@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/vme_user/vme.c b/drivers/staging/vme_user/vme.c index cb9796c87ee8..b5c66b66ce32 100644 --- a/drivers/staging/vme_user/vme.c +++ b/drivers/staging/vme_user/vme.c @@ -274,7 +274,7 @@ struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address, mutex_lock(&slave_image->mtx); if (((slave_image->address_attr & address) == address) && ((slave_image->cycle_attr & cycle) == cycle) && - (slave_image->locked == 0)) { + !slave_image->locked) { slave_image->locked = 1; mutex_unlock(&slave_image->mtx); allocated_image = slave_image; @@ -419,7 +419,7 @@ void vme_slave_free(struct vme_resource *resource) /* Unlock image */ mutex_lock(&slave_image->mtx); - if (slave_image->locked == 0) + if (!slave_image->locked) dev_err(bridge->parent, "Image is already free\n"); slave_image->locked = 0; @@ -469,7 +469,7 @@ struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address, if (((master_image->address_attr & address) == address) && ((master_image->cycle_attr & cycle) == cycle) && ((master_image->width_attr & dwidth) == dwidth) && - (master_image->locked == 0)) { + !master_image->locked) { master_image->locked = 1; spin_unlock(&master_image->lock); allocated_image = master_image; @@ -793,7 +793,7 @@ void vme_master_free(struct vme_resource *resource) /* Unlock image */ spin_lock(&master_image->lock); - if (master_image->locked == 0) + if (!master_image->locked) dev_err(bridge->parent, "Image is already free\n"); master_image->locked = 0; @@ -841,7 +841,7 @@ struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route) /* Find an unlocked and compatible controller */ mutex_lock(&dma_ctrlr->mtx); if (((dma_ctrlr->route_attr & route) == route) && - (dma_ctrlr->locked == 0)) { + !dma_ctrlr->locked) { dma_ctrlr->locked = 1; mutex_unlock(&dma_ctrlr->mtx); allocated_ctrlr = dma_ctrlr; @@ -1365,7 +1365,7 @@ void vme_irq_free(struct vme_dev *vdev, int level, int statid) bridge->irq[level - 1].count--; /* Disable IRQ level if no more interrupts attached at this level*/ - if (bridge->irq[level - 1].count == 0) + if (!bridge->irq[level - 1].count) bridge->irq_set(bridge, level, 0, 1); bridge->irq[level - 1].callback[statid].func = NULL; @@ -1445,7 +1445,7 @@ struct vme_resource *vme_lm_request(struct vme_dev *vdev) /* Find an unlocked controller */ mutex_lock(&lm->mtx); - if (lm->locked == 0) { + if (!lm->locked) { lm->locked = 1; mutex_unlock(&lm->mtx); allocated_lm = lm; @@ -1766,7 +1766,7 @@ int vme_register_bridge(struct vme_bridge *bridge) mutex_lock(&vme_buses_lock); for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) { - if ((vme_bus_numbers & (1 << i)) == 0) { + if (!(vme_bus_numbers & (1 << i))) { vme_bus_numbers |= (1 << i); bridge->num = i; INIT_LIST_HEAD(&bridge->devices); From bd2e75c827340bb91a5ce7056402779e26938424 Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Mon, 4 May 2026 23:46:12 +0300 Subject: [PATCH 0737/5214] staging: sm750fb: remove unnecessary initializations Remove two instances of `ret = 0` initializations since the variable is overridden unconditionally before being used. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260504204612.55657-1-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_hw.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index beef05ded98d..780fe6c391d3 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -29,8 +29,6 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) { int ret; - ret = 0; - sm750_dev->vidreg_start = pci_resource_start(pdev, 1); sm750_dev->vidreg_size = SZ_2M; @@ -243,7 +241,6 @@ int hw_sm750_crtc_set_mode(struct lynxfb_crtc *crtc, struct sm750_dev *sm750_dev; struct lynxfb_par *par; - ret = 0; par = container_of(crtc, struct lynxfb_par, crtc); sm750_dev = par->dev; From a5b28f0d8f7c323d3e2184599f438f01af3308a8 Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Fri, 8 May 2026 11:04:38 +0500 Subject: [PATCH 0738/5214] staging: most: net: replace pr_err with netdev_err Replace remaining pr_err() calls with netdev_err() where a net device is available to provide better context in error messages. Lines in skb_to_mep() are left as pr_err() since no net device context is available there. Signed-off-by: Maha Maryam Javaid Link: https://patch.msgid.link/20260508060438.20156-1-mahamaryamjavaid@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/net/net.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 15fa2e410e4c..e6e4498d33ab 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -329,13 +329,13 @@ static int comp_probe_channel(struct most_interface *iface, int channel_idx, } else { ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; if (ch->linked) { - pr_err("direction is allocated\n"); + netdev_err(nd->dev, "direction is allocated\n"); ret = -EINVAL; goto unlock; } if (register_netdev(nd->dev)) { - pr_err("register_netdev() failed\n"); + netdev_err(nd->dev, "register_netdev() failed\n"); ret = -EINVAL; goto unlock; } @@ -454,7 +454,7 @@ static int comp_rx_data(struct mbo *mbo) if (!skb) { dev->stats.rx_dropped++; - pr_err_once("drop packet: no memory for skb\n"); + netdev_err_once(dev, "drop packet: no memory for skb\n"); goto out; } From f50b4602fea62feb7757ad479041e9436f1de361 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Sat, 9 May 2026 23:29:07 -0700 Subject: [PATCH 0739/5214] staging: sm750fb: rename CamelCase variable and drop prefix Renames the CamelCase variable 'pvMem' in struct sm750_dev to 'vmem'. Dropping the pointer prefix 'p'. This fixes the following checkpatch.pl check: CHECK: Avoid CamelCase: Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260510062908.67848-2-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 18 +++++++++--------- drivers/staging/sm750fb/sm750.h | 2 +- drivers/staging/sm750fb/sm750_hw.c | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index fadba95850be..d8cb9fce268d 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -622,26 +622,26 @@ static int sm750fb_set_drv(struct lynxfb_par *par) output->paths = sm750_pnc; crtc->channel = sm750_primary; crtc->o_screen = 0; - crtc->v_screen = sm750_dev->pvMem; + crtc->v_screen = sm750_dev->vmem; break; case sm750_simul_sec: output->paths = sm750_pnc; crtc->channel = sm750_secondary; crtc->o_screen = 0; - crtc->v_screen = sm750_dev->pvMem; + crtc->v_screen = sm750_dev->vmem; break; case sm750_dual_normal: if (par->index == 0) { output->paths = sm750_panel; crtc->channel = sm750_primary; crtc->o_screen = 0; - crtc->v_screen = sm750_dev->pvMem; + crtc->v_screen = sm750_dev->vmem; } else { output->paths = sm750_crt; crtc->channel = sm750_secondary; /* not consider of padding stuffs for o_screen,need fix */ crtc->o_screen = sm750_dev->vidmem_size >> 1; - crtc->v_screen = sm750_dev->pvMem + crtc->o_screen; + crtc->v_screen = sm750_dev->vmem + crtc->o_screen; } break; case sm750_dual_swap: @@ -649,7 +649,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par) output->paths = sm750_panel; crtc->channel = sm750_secondary; crtc->o_screen = 0; - crtc->v_screen = sm750_dev->pvMem; + crtc->v_screen = sm750_dev->vmem; } else { output->paths = sm750_crt; crtc->channel = sm750_primary; @@ -657,7 +657,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par) * need fix */ crtc->o_screen = sm750_dev->vidmem_size >> 1; - crtc->v_screen = sm750_dev->pvMem + crtc->o_screen; + crtc->v_screen = sm750_dev->vmem + crtc->o_screen; } break; default: @@ -761,7 +761,7 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) crtc->cursor.max_h = 64; crtc->cursor.max_w = 64; crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8; - crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset; + crtc->cursor.vstart = sm750_dev->vmem + crtc->cursor.offset; memset_io(crtc->cursor.vstart, 0, crtc->cursor.size); if (!g_hwcursor) @@ -1028,7 +1028,7 @@ static int lynxfb_pci_probe(struct pci_dev *pdev, sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start, sm750_dev->vidmem_size); - memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size); + memset_io(sm750_dev->vmem, 0, sm750_dev->vidmem_size); pci_set_drvdata(pdev, sm750_dev); @@ -1060,7 +1060,7 @@ static void lynxfb_pci_remove(struct pci_dev *pdev) arch_phys_wc_del(sm750_dev->mtrr.vram); iounmap(sm750_dev->pvReg); - iounmap(sm750_dev->pvMem); + iounmap(sm750_dev->vmem); pci_release_region(pdev, 1); kfree(g_settings); } diff --git a/drivers/staging/sm750fb/sm750.h b/drivers/staging/sm750fb/sm750.h index 49a79d0a8a2e..2c9038d38b3c 100644 --- a/drivers/staging/sm750fb/sm750.h +++ b/drivers/staging/sm750fb/sm750.h @@ -98,7 +98,7 @@ struct sm750_dev { __u32 vidmem_size; __u32 vidreg_size; void __iomem *pvReg; - unsigned char __iomem *pvMem; + unsigned char __iomem *vmem; /* locks*/ spinlock_t slock; diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index 780fe6c391d3..6f7c354a34a1 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -64,9 +64,9 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) sm750_dev->vidmem_size = ddk750_get_vm_size(); /* reserve the vidmem space of smi adaptor */ - sm750_dev->pvMem = + sm750_dev->vmem = ioremap_wc(sm750_dev->vidmem_start, sm750_dev->vidmem_size); - if (!sm750_dev->pvMem) { + if (!sm750_dev->vmem) { dev_err(&pdev->dev, "Map video memory failed\n"); ret = -EFAULT; goto err_unmap_reg; From 45a337c12624fc0506a35beb713a6b8bce7a7c7b Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Sat, 9 May 2026 23:29:08 -0700 Subject: [PATCH 0740/5214] staging: sm750fb: change 2 CamelCase variables to snake_case Renames these variables inside struct init_status to adhere to kernel coding style: - powerMode -> power_mode - resetMemory -> reset_memory This fixes the following checkpatch.pl checks: - CHECK: Avoid CamelCase: - CHECK: Avoid CamelCase: Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260510062908.67848-3-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 4 ++-- drivers/staging/sm750fb/sm750.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index d8cb9fce268d..996a586a3727 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -859,9 +859,9 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src) sm750_dev->init_parm.chip_clk = 0; sm750_dev->init_parm.mem_clk = 0; sm750_dev->init_parm.master_clk = 0; - sm750_dev->init_parm.powerMode = 0; + sm750_dev->init_parm.power_mode = 0; sm750_dev->init_parm.setAllEngOff = 0; - sm750_dev->init_parm.resetMemory = 1; + sm750_dev->init_parm.reset_memory = 1; /* defaultly turn g_hwcursor on for both view */ g_hwcursor = 3; diff --git a/drivers/staging/sm750fb/sm750.h b/drivers/staging/sm750fb/sm750.h index 2c9038d38b3c..d2c522e67f26 100644 --- a/drivers/staging/sm750fb/sm750.h +++ b/drivers/staging/sm750fb/sm750.h @@ -39,13 +39,13 @@ enum sm750_path { }; struct init_status { - ushort powerMode; + ushort power_mode; /* below three clocks are in unit of MHZ*/ ushort chip_clk; ushort mem_clk; ushort master_clk; ushort setAllEngOff; - ushort resetMemory; + ushort reset_memory; }; struct lynx_accel { From 0551fd8a1ce2e2fdfb6c0f69b096555053cf3af9 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Tue, 5 May 2026 11:54:31 -0700 Subject: [PATCH 0741/5214] staging: rtl8723bs: remove unnecessary block comment Found a block comment formatting warning in odm_interface.h via checkpatch.pl. As far as I can tell, this block comment is no longer useful. Hence proposing to remove it. Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260505185431.27037-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm_interface.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm_interface.h b/drivers/staging/rtl8723bs/hal/odm_interface.h index 400473ca58ca..c3b13f5f37db 100644 --- a/drivers/staging/rtl8723bs/hal/odm_interface.h +++ b/drivers/staging/rtl8723bs/hal/odm_interface.h @@ -18,14 +18,6 @@ #define _bit_all(_name) BIT_##_name #define _bit_ic(_name, _ic) BIT_##_name##_ic -/*=================================== - -#define ODM_REG_DIG_11N 0xC50 -#define ODM_REG_DIG_11AC 0xDDD - -ODM_REG(DIG) -=====================================*/ - #define _reg_11N(_name) ODM_REG_##_name##_11N #define _bit_11N(_name) ODM_BIT_##_name##_11N From 99b168d35438e2b609a3486ca2fa3f099f9adf9e Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Wed, 6 May 2026 22:00:11 -0700 Subject: [PATCH 0742/5214] staging: rtl8723bs: remove commented out enum values from odm_types.h Found these commented out enum values, while checking for block comment issues with checkpatch.pl. These seem to be ok to remove for cleanup. I verified via: git grep 'RT_STATUS_' drivers/staging/rtl8723bs/ to check there are no other occurrences. Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260507050011.17066-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm_types.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm_types.h b/drivers/staging/rtl8723bs/hal/odm_types.h index 0da662e1493a..bc0c4d027492 100644 --- a/drivers/staging/rtl8723bs/hal/odm_types.h +++ b/drivers/staging/rtl8723bs/hal/odm_types.h @@ -18,12 +18,6 @@ enum hal_status { HAL_STATUS_SUCCESS, HAL_STATUS_FAILURE, - /*RT_STATUS_PENDING, - RT_STATUS_RESOURCE, - RT_STATUS_INVALID_CONTEXT, - RT_STATUS_INVALID_PARAMETER, - RT_STATUS_NOT_SUPPORT, - RT_STATUS_OS_API_FAILED,*/ }; From b325af6c49c7998f558255e1bfabc19c50ac1bd3 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Thu, 7 May 2026 10:42:56 -0700 Subject: [PATCH 0743/5214] staging: rtl8723bs: fix block comment alignment in hal/ header files Add leading whitespace to block comments in order to adhere to the kernel coding style. This fixes the following checkpatch.pl warning: WARNING: Block comments should align the * on each line. Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260507174256.5319-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/hal/HalHWImg8723B_MAC.h | 12 +++++------ .../staging/rtl8723bs/hal/HalHWImg8723B_RF.h | 20 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h index 50429c159fee..82de8aa0008b 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h @@ -1,17 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0 */ /****************************************************************************** -* -* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. -* -******************************************************************************/ + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + ******************************************************************************/ #ifndef __INC_MP_MAC_HW_IMG_8723B_H #define __INC_MP_MAC_HW_IMG_8723B_H /****************************************************************************** -* MAC_REG.TXT -******************************************************************************/ + * MAC_REG.TXT + ******************************************************************************/ void ODM_ReadAndConfig_MP_8723B_MAC_REG(/* TC: Test Chip, MP: MP Chip */ diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h index acf5679d188c..04441fdeaa26 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h @@ -1,17 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0 */ /****************************************************************************** -* -* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. -* -******************************************************************************/ + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + ******************************************************************************/ #ifndef __INC_MP_RF_HW_IMG_8723B_H #define __INC_MP_RF_HW_IMG_8723B_H /****************************************************************************** -* RadioA.TXT -******************************************************************************/ + * RadioA.TXT + ******************************************************************************/ void ODM_ReadAndConfig_MP_8723B_RadioA(/* TC: Test Chip, MP: MP Chip */ @@ -19,8 +19,8 @@ ODM_ReadAndConfig_MP_8723B_RadioA(/* TC: Test Chip, MP: MP Chip */ ); /****************************************************************************** -* TxPowerTrack_SDIO.TXT -******************************************************************************/ + * TxPowerTrack_SDIO.TXT + ******************************************************************************/ void ODM_ReadAndConfig_MP_8723B_TxPowerTrack_SDIO(/* TC: Test Chip, MP: MP Chip */ @@ -29,8 +29,8 @@ ODM_ReadAndConfig_MP_8723B_TxPowerTrack_SDIO(/* TC: Test Chip, MP: MP Chip */ u32 ODM_GetVersion_MP_8723B_TxPowerTrack_SDIO(void); /****************************************************************************** -* TXPWR_LMT.TXT -******************************************************************************/ + * TXPWR_LMT.TXT + ******************************************************************************/ void ODM_ReadAndConfig_MP_8723B_TXPWR_LMT(/* TC: Test Chip, MP: MP Chip */ From acfb6a467f63985ca1ac2d36355efb9d48e7ce52 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Fri, 8 May 2026 21:23:09 -0700 Subject: [PATCH 0744/5214] staging: rtl8723bs: fix block comment alignment in hal/ source files Add leading whitespace to block comments in order to adhere to the kernel coding style. This fixes the following checkpatch.pl warning: WARNING: Block comments should align the * on each line. Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260509042310.4745-2-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_com.c | 28 ++++++++-------- .../staging/rtl8723bs/hal/hal_com_phycfg.c | 6 ++-- drivers/staging/rtl8723bs/hal/odm.c | 32 +++++++++---------- drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c | 14 ++++---- .../staging/rtl8723bs/hal/rtl8723b_rf6052.c | 2 +- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 25686aeb073b..51dbb21345f8 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -435,10 +435,10 @@ void rtw_init_hal_com_default_value(struct adapter *Adapter) } /* -* C2H event format: -* Field TRIGGER CONTENT CMD_SEQ CMD_LEN CMD_ID -* BITS [127:120] [119:16] [15:8] [7:4] [3:0] -*/ + * C2H event format: + * Field TRIGGER CONTENT CMD_SEQ CMD_LEN CMD_ID + * BITS [127:120] [119:16] [15:8] [7:4] [3:0] + */ void c2h_evt_clear(struct adapter *adapter) { @@ -446,10 +446,10 @@ void c2h_evt_clear(struct adapter *adapter) } /* -* C2H event format: -* Field TRIGGER CMD_LEN CONTENT CMD_SEQ CMD_ID -* BITS [127:120] [119:112] [111:16] [15:8] [7:0] -*/ + * C2H event format: + * Field TRIGGER CMD_LEN CONTENT CMD_SEQ CMD_ID + * BITS [127:120] [119:112] [111:16] [15:8] [7:0] + */ s32 c2h_evt_read_88xx(struct adapter *adapter, u8 *buf) { s32 ret = _FAIL; @@ -483,9 +483,9 @@ s32 c2h_evt_read_88xx(struct adapter *adapter, u8 *buf) clear_evt: /* - * Clear event to notify FW we have read the command. - * If this field isn't clear, the FW won't update the next command message. - */ + * Clear event to notify FW we have read the command. + * If this field isn't clear, the FW won't update the next command message. + */ c2h_evt_clear(adapter); exit: return ret; @@ -582,9 +582,9 @@ void SetHwReg(struct adapter *adapter, u8 variable, u8 *val) break; case HW_VAR_DM_FUNC_CLR: /* - * input is already a mask to clear function - * don't invert it again! George, Lucas@20130513 - */ + * input is already a mask to clear function + * don't invert it again! George, Lucas@20130513 + */ odm->SupportAbility &= *((u32 *)val); break; case HW_VAR_AMPDU_MIN_SPACE: diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index 1e5669b17d0f..ea79e300f83b 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -407,9 +407,9 @@ struct adapter *padapter } /* - * This function must be called if the value in the PHY_REG_PG.txt(or header) - * is exact dBm values - */ + * This function must be called if the value in the PHY_REG_PG.txt(or header) + * is exact dBm values + */ void PHY_TxPowerByRateConfiguration(struct adapter *padapter) { phy_StoreTxPowerByRateBase(padapter); diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index b4f872f4d6c9..4f9e5edecaa1 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -279,22 +279,22 @@ static void odm_RefreshRateAdaptiveMaskCE(struct dm_odm_t *pDM_Odm) } /*----------------------------------------------------------------------------- -* Function: odm_RefreshRateAdaptiveMask() -* -* Overview: Update rate table mask according to rssi -* -* Input: NONE -* -* Output: NONE -* -* Return: NONE -* -* Revised History: -*When Who Remark -*05/27/2009 hpfan Create Version 0. -* -* -------------------------------------------------------------------------- -*/ + * Function: odm_RefreshRateAdaptiveMask() + * + * Overview: Update rate table mask according to rssi + * + * Input: NONE + * + * Output: NONE + * + * Return: NONE + * + * Revised History: + *When Who Remark + *05/27/2009 hpfan Create Version 0. + * + * -------------------------------------------------------------------------- + */ static void odm_RefreshRateAdaptiveMask(struct dm_odm_t *pDM_Odm) { diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index 6c9337179645..2485c415f14b 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -32,13 +32,13 @@ static u8 _is_fw_read_cmd_down(struct adapter *padapter, u8 msgbox_num) /***************************************** -* H2C Msg format : -*| 31 - 8 |7-5 | 4 - 0 | -*| h2c_msg |Class |CMD_ID | -*| 31-0 | -*| Ext msg | -* -******************************************/ + * H2C Msg format : + *| 31 - 8 |7-5 | 4 - 0 | + *| h2c_msg |Class |CMD_ID | + *| 31-0 | + *| Ext msg | + * + ******************************************/ s32 FillH2CCmd8723B(struct adapter *padapter, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer) { u8 h2c_box_num; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c index ffb35e1ace62..e56536dbbd96 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c @@ -24,7 +24,7 @@ * 11/05/2008 MHC Add API for tw power setting. * * -******************************************************************************/ + ******************************************************************************/ #include From 87480e2a97c0ecbb471b2fa8c1864d9b997c590f Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Fri, 8 May 2026 21:23:10 -0700 Subject: [PATCH 0745/5214] staging: rtl8723bs: move block comment terminator to new line Move block comment terminator '*/' to separate line in order to adhere to the kernel coding style. This fixes the following checkpatch.pl warning: WARNING: Block comments use a trailing */ on a separate line Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260509042310.4745-3-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c | 3 ++- drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 6ab65e9e8bee..005b0b724bdd 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -148,7 +148,8 @@ static void setCCKFilterCoefficient(struct dm_odm_t *pDM_Odm, u8 CCKSwingIndex) *When Who Remark *04/23/2012 MHC Create Version 0. * - *---------------------------------------------------------------------------*/ + *--------------------------------------------------------------------------- + */ void ODM_TxPwrTrackSetPwr_8723B( struct dm_odm_t *pDM_Odm, enum pwrtrack_method Method, diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c index e56536dbbd96..ad0b2d36312e 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c @@ -53,7 +53,8 @@ * Return: NONE * * Note: For RF type 0222D - *---------------------------------------------------------------------------*/ + *--------------------------------------------------------------------------- + */ void PHY_RF6052SetBandwidth8723B( struct adapter *Adapter, enum channel_width Bandwidth ) /* 20M or 40M */ From 23fe711a431ddc178549f0e931d01bee7554b4a7 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Fri, 8 May 2026 22:12:40 -0700 Subject: [PATCH 0746/5214] staging: rtl8723bs: remove commented out code from odm.c Remove commented out case statements for better readability. This also fixes the checkpatch.pl warning: WARNING: Block comments use * on subsequent lines. Signed-off-by: Jennifer Guo Reviewed-by: Nikolay Kulikov Link: https://patch.msgid.link/20260509051240.5807-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index 4f9e5edecaa1..dbd8183363a8 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -952,31 +952,6 @@ void ODM_CmnInfoUpdate(struct dm_odm_t *pDM_Odm, u32 CmnInfo, u64 Value) pDM_Odm->bBtDisableEdcaTurbo = (bool)Value; break; -/* - case ODM_CMNINFO_OP_MODE: - pDM_Odm->OPMode = (u8)Value; - break; - - case ODM_CMNINFO_WM_MODE: - pDM_Odm->WirelessMode = (u8)Value; - break; - - case ODM_CMNINFO_SEC_CHNL_OFFSET: - pDM_Odm->SecChOffset = (u8)Value; - break; - - case ODM_CMNINFO_SEC_MODE: - pDM_Odm->Security = (u8)Value; - break; - - case ODM_CMNINFO_BW: - pDM_Odm->BandWidth = (u8)Value; - break; - - case ODM_CMNINFO_CHNL: - pDM_Odm->Channel = (u8)Value; - break; -*/ default: /* do nothing */ break; From ec09be88d1a33afd3e4d1bbbe7963cc25dffe031 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Sat, 9 May 2026 23:58:29 -0700 Subject: [PATCH 0747/5214] staging: rtl8723bs: remove unnecessary blank lines in rtw_recv.c Remove unnecessary blank lines around braces {}. This fixes the following checkpatch.pl checks in rtw_recv.c: - CHECK: Blank lines aren't necessary after an open brace '{' - CHECK: Blank lines aren't necessary before a close brace '}' Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260510065829.68957-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_recv.c | 38 ----------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index b3a38ffa6ef7..fc0397ad4741 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -69,7 +69,6 @@ signed int _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *pada precvframe->u.hdr.adapter = padapter; precvframe++; - } res = rtw_hal_init_recv_priv(padapter); @@ -111,7 +110,6 @@ void _rtw_free_recv_priv(struct recv_priv *precvpriv) union recv_frame *_rtw_alloc_recvframe(struct __queue *pfree_recv_queue) { - union recv_frame *precvframe; struct list_head *plist, *phead; struct adapter *padapter; @@ -178,7 +176,6 @@ int rtw_free_recvframe(union recv_frame *precvframe, struct __queue *pfree_recv_ signed int _rtw_enqueue_recvframe(union recv_frame *precvframe, struct __queue *queue) { - struct adapter *padapter = precvframe->u.hdr.adapter; struct recv_priv *precvpriv = &padapter->recvpriv; @@ -270,7 +267,6 @@ signed int rtw_enqueue_recvbuf(struct recv_buf *precvbuf, struct __queue *queue) list_add_tail(&precvbuf->list, get_list_head(queue)); spin_unlock_bh(&queue->lock); return _SUCCESS; - } struct recv_buf *rtw_dequeue_recvbuf(struct __queue *queue) @@ -290,13 +286,11 @@ struct recv_buf *rtw_dequeue_recvbuf(struct __queue *queue) precvbuf = container_of(plist, struct recv_buf, list); list_del_init(&precvbuf->list); - } spin_unlock_bh(&queue->lock); return precvbuf; - } static void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) @@ -345,7 +339,6 @@ static void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *precvframe) { - signed int i, res = _SUCCESS; u32 datalen; u8 miccode[8]; @@ -417,18 +410,15 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p } recvframe_pull_tail(precvframe, 8); - } exit: return res; - } /* decrypt and set the ivlen, icvlen of the recv_frame */ static union recv_frame *decryptor(struct adapter *padapter, union recv_frame *precv_frame) { - struct rx_pkt_attrib *prxattrib = &precv_frame->u.hdr.attrib; struct security_priv *psecuritypriv = &padapter->securitypriv; union recv_frame *return_packet = precv_frame; @@ -573,7 +563,6 @@ static signed int recv_decache(union recv_frame *precv_frame, u8 bretry, struct prxcache->tid_rxseq[tid] = seq_ctrl; return _SUCCESS; - } static void process_pwrbit_data(struct adapter *padapter, union recv_frame *precv_frame) @@ -595,7 +584,6 @@ static void process_pwrbit_data(struct adapter *padapter, union recv_frame *prec /* pstapriv->sta_dz_bitmap |= BIT(psta->aid); */ stop_sta_xmit(padapter, psta); - } } else { if (psta->state & WIFI_SLEEP_STATE) { @@ -605,7 +593,6 @@ static void process_pwrbit_data(struct adapter *padapter, union recv_frame *prec wakeup_sta_to_xmit(padapter, psta); } } - } } @@ -706,7 +693,6 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { - /* filter packets that SA is myself or multicast or broadcast */ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) { ret = _FAIL; @@ -792,7 +778,6 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) && (check_fwstate(pmlmepriv, _FW_LINKED) || check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) { - /* filter packets that SA is myself or multicast or broadcast */ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) { ret = _FAIL; @@ -809,7 +794,6 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p if (is_zero_ether_addr(pattrib->bssid) || is_zero_ether_addr(mybssid) || (memcmp(pattrib->bssid, mybssid, ETH_ALEN))) { - if (!bmcast) issue_deauth(adapter, pattrib->bssid, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); @@ -859,7 +843,6 @@ static signed int ap2sta_data_frame(struct adapter *adapter, union recv_frame *p if (!memcmp(myhwaddr, pattrib->dst, ETH_ALEN) && (!bmcast)) { *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get sta_info */ if (!*psta) { - /* for AP multicast issue , modify by yiwei */ static unsigned long send_issue_deauth_time; @@ -1052,7 +1035,6 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_ } return _FAIL; - } /* perform defrag */ @@ -1123,7 +1105,6 @@ static union recv_frame *recvframe_defrag(struct adapter *adapter, pfhdr->attrib.icv_len = pnfhdr->attrib.icv_len; plist = get_next(plist); - } /* free the defrag_q queue and return the prframe */ @@ -1195,7 +1176,6 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re rtw_free_recvframe(precv_frame, pfree_recv_queue); prtnframe = NULL; } - } if ((ismfrag == 0) && (fragnum != 0)) { @@ -1216,7 +1196,6 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re rtw_free_recvframe(precv_frame, pfree_recv_queue); prtnframe = NULL; } - } if ((prtnframe) && (prtnframe->u.hdr.attrib.privacy)) { @@ -1269,7 +1248,6 @@ static signed int validate_recv_mgnt_frame(struct adapter *padapter, union recv_ mgt_dispatcher(padapter, precv_frame); return _SUCCESS; - } static signed int validate_recv_data_frame(struct adapter *adapter, union recv_frame *precv_frame) @@ -1325,7 +1303,6 @@ static signed int validate_recv_data_frame(struct adapter *adapter, union recv_f default: ret = _FAIL; break; - } if (ret == _FAIL || ret == RTW_RX_HANDLED) @@ -1454,7 +1431,6 @@ static signed int validate_80211w_mgmt(struct adapter *adapter, union recv_frame validate_80211w_fail: return _FAIL; - } static signed int validate_recv_frame(struct adapter *adapter, union recv_frame *precv_frame) @@ -1714,7 +1690,6 @@ static int amsdu_to_msdu(struct adapter *padapter, union recv_frame *prframe) pdata = prframe->u.hdr.rx_data; while (a_len > ETH_HLEN) { - /* Offset 12 denote 2 mac address */ nSubframe_Length = get_unaligned_be16(pdata + 12); @@ -1821,7 +1796,6 @@ static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl, un return false; else break; - } /* spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); */ @@ -1835,7 +1809,6 @@ static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl, un /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); */ return true; - } static int rtw_recv_indicatepkt(struct adapter *padapter, union recv_frame *precv_frame) @@ -1903,13 +1876,11 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor pattrib = &prframe->u.hdr.attrib; preorder_ctrl->indicate_seq = pattrib->seq_num; - } /* Prepare indication list and indication. */ /* Check if there is any packet need indicate. */ while (!list_empty(phead)) { - prframe = (union recv_frame *)plist; pattrib = &prframe->u.hdr.attrib; @@ -1946,7 +1917,6 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor bPktInBuf = true; break; } - } /* spin_unlock(&ppending_recvframe_queue->lock); */ @@ -1971,11 +1941,9 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * !padapter->bSurpriseRemoved) { rtw_recv_indicatepkt(padapter, prframe); return _SUCCESS; - } return _FAIL; - } if (!preorder_ctrl->enable) { @@ -2056,7 +2024,6 @@ void rtw_reordering_ctrl_timeout_handler(struct timer_list *t) _set_timer(&preorder_ctrl->reordering_ctrl_timer, REORDER_WAIT_TIME); spin_unlock_bh(&ppending_recvframe_queue->lock); - } static int process_recv_indicatepkts(struct adapter *padapter, union recv_frame *prframe) @@ -2090,11 +2057,9 @@ static int process_recv_indicatepkts(struct adapter *padapter, union recv_frame retval = _FAIL; return retval; } - } return retval; - } static int recv_func_prehandle(struct adapter *padapter, union recv_frame *rframe) @@ -2168,7 +2133,6 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) ret = recv_func_prehandle(padapter, rframe); if (ret == _SUCCESS) { - /* check if need to enqueue into uc_swdec_pending_queue*/ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) && !is_multicast_ether_addr(prxattrib->ra) && @@ -2236,7 +2200,6 @@ static void rtw_signal_stat_timer_hdl(struct timer_list *t) adapter->recvpriv.signal_strength = adapter->recvpriv.signal_strength_dbg; adapter->recvpriv.rssi = (s8)translate_percentage_to_dbm((u8)adapter->recvpriv.signal_strength_dbg); } else { - if (recvpriv->signal_strength_data.update_req == 0) {/* update_req is clear, means we got rx */ avg_signal_strength = recvpriv->signal_strength_data.avg_val; num_signal_strength = recvpriv->signal_strength_data.total_num; @@ -2289,5 +2252,4 @@ static void rtw_signal_stat_timer_hdl(struct timer_list *t) set_timer: rtw_set_signal_stat_timer(recvpriv); - } From d7d9af4cc305d6f5a59c05d167d56d4564be7c84 Mon Sep 17 00:00:00 2001 From: Aidan Russell Date: Mon, 4 May 2026 16:48:46 -0700 Subject: [PATCH 0748/5214] staging: rtl8723bs: Replace uint with unsigned int in core Replace the uint type with unsigned int in the rtl8723bs core code. This removes unnecessary typedef use and moves the driver closer to standards. No functional changes intended. Signed-off-by: Aidan Russell Link: https://patch.msgid.link/20260504234846.114912-1-aidanlrussell@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ap.c | 12 ++++++------ drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 75d790e1b5c5..065850a9e894 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -56,7 +56,7 @@ static void update_BCNTIM(struct adapter *padapter) /* update TIM IE */ u8 *p, *dst_ie, *premainder_ie = NULL, *pbackup_remainder_ie = NULL; __le16 tim_bitmap_le; - uint offset, tmp_len, tim_ielen, tim_ie_offset, remainder_ielen; + unsigned int offset, tmp_len, tim_ielen, tim_ie_offset, remainder_ielen; tim_bitmap_le = cpu_to_le16(pstapriv->tim_bitmap); @@ -152,7 +152,7 @@ static void update_BCNTIM(struct adapter *padapter) kfree(pbackup_remainder_ie); } - offset = (uint)(dst_ie - pie); + offset = (unsigned int)(dst_ie - pie); pnetwork_mlmeext->ie_length = offset + remainder_ielen; } @@ -757,7 +757,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) u8 *ht_info_ie = NULL; struct sta_info *psta = NULL; u16 cap, ht_cap = false; - uint ie_len = 0; + unsigned int ie_len = 0; int group_cipher, pairwise_cipher; u8 channel, network_type, support_rate[NDIS_802_11_LENGTH_RATES_EX]; int support_rate_num = 0; @@ -1351,7 +1351,7 @@ static void update_bcn_wps_ie(struct adapter *padapter) u8 *premainder_ie; u8 *pbackup_remainder_ie = NULL; - uint wps_ielen = 0, wps_offset, remainder_ielen; + unsigned int wps_ielen = 0, wps_offset, remainder_ielen; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; @@ -1371,7 +1371,7 @@ static void update_bcn_wps_ie(struct adapter *padapter) if (!pwps_ie_src) return; - wps_offset = (uint)(pwps_ie - ie); + wps_offset = (unsigned int)(pwps_ie - ie); premainder_ie = pwps_ie + wps_ielen; @@ -1380,7 +1380,7 @@ static void update_bcn_wps_ie(struct adapter *padapter) if (remainder_ielen) pbackup_remainder_ie = kmemdup(premainder_ie, remainder_ielen, GFP_ATOMIC); - wps_ielen = (uint)pwps_ie_src[1];/* to get ie data len */ + wps_ielen = (unsigned int)pwps_ie_src[1];/* to get ie data len */ if ((wps_offset + wps_ielen + 2 + remainder_ielen) <= MAX_IE_SZ) { memcpy(pwps_ie, pwps_ie_src, wps_ielen + 2); pwps_ie += (wps_ielen + 2); diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 2624d8472b3a..adc151326feb 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -672,7 +672,7 @@ int rtw_startbss_cmd(struct adapter *padapter, int flags) u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) { u8 res = _SUCCESS; - uint t_len = 0; + unsigned int t_len = 0; struct wlan_bssid_ex *psecnetwork; struct cmd_obj *pcmd; struct cmd_priv *pcmdpriv = &padapter->cmdpriv; From c87a4d3344094ddac866b2a02397d26817c3a647 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Thu, 7 May 2026 23:22:18 +0530 Subject: [PATCH 0749/5214] staging: rtl8723bs: remove unnecessary braces Remove unnecessary braces around a single statement block reported by checkpatch. Signed-off-by: Shivam Gupta Reviewed-by: Nikolay Kulikov Link: https://patch.msgid.link/20260507175218.16831-1-shivgupta751157@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c index 58f6cf063498..f3195f6f7e57 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c @@ -294,9 +294,8 @@ static u8 halbtc8723b2ant_ActionAlgorithm(struct btc_coexist *pBtCoexist) pBtCoexist->fBtcGet(pBtCoexist, BTC_GET_BL_HS_OPERATION, &bBtHsOn); - if (!pBtLinkInfo->bBtLinkExist) { + if (!pBtLinkInfo->bBtLinkExist) return algorithm; - } if (pBtLinkInfo->bScoExist) numOfDiffProfile++; From 3fb7c93716d36395ffec52d4c2d50f85fd5b451c Mon Sep 17 00:00:00 2001 From: Pramod Maurya Date: Sun, 10 May 2026 06:36:26 -0400 Subject: [PATCH 0750/5214] staging: rtl8723bs: Replace __attribute__((packed)) with __packed in wifi.h Replace all occurrences of __attribute__((packed)) with the preferred __packed macro in wifi.h to fix checkpatch.pl warnings. Signed-off-by: Pramod Maurya Link: https://patch.msgid.link/20260510103630.80917-1-pramod.nexgen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/wifi.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/wifi.h b/drivers/staging/rtl8723bs/include/wifi.h index 92b7882de15c..c8844e2bf7b4 100644 --- a/drivers/staging/rtl8723bs/include/wifi.h +++ b/drivers/staging/rtl8723bs/include/wifi.h @@ -333,7 +333,7 @@ struct ieee80211_ht_addt_info { __le16 operation_mode; __le16 stbc_param; unsigned char basic_set[16]; -} __attribute__ ((packed)); +} __packed; struct HT_caps_element { @@ -348,32 +348,32 @@ struct HT_caps_element { } HT_cap_element; unsigned char HT_cap[26]; } u; -} __attribute__ ((packed)); +} __packed; struct HT_info_element { unsigned char primary_channel; unsigned char infos[5]; unsigned char MCS_rate[16]; -} __attribute__ ((packed)); +} __packed; struct AC_param { unsigned char ACI_AIFSN; unsigned char CW; __le16 TXOP_limit; -} __attribute__ ((packed)); +} __packed; struct WMM_para_element { unsigned char QoS_info; unsigned char reserved; struct AC_param ac_param[4]; -} __attribute__ ((packed)); +} __packed; struct ADDBA_request { unsigned char dialog_token; __le16 BA_para_set; __le16 BA_timeout_value; __le16 BA_starting_seqctrl; -} __attribute__ ((packed)); +} __packed; /* 802.11n HT capabilities masks */ #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 From b36ddb456fe18696dca7aeb11e4936d55446f8a0 Mon Sep 17 00:00:00 2001 From: Pramod Maurya Date: Sun, 10 May 2026 07:03:34 -0400 Subject: [PATCH 0751/5214] staging: rtl8723bs: Replace __attribute__((__packed__)) with __packed in rtl8723b_hal.h Replace __attribute__((__packed__)) with the preferred __packed macro to fix a checkpatch.pl warning. Signed-off-by: Pramod Maurya Link: https://patch.msgid.link/20260510110338.98540-1-pramod.nexgen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtl8723b_hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h index 8e04e6a4cb01..b6006110ad6b 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h @@ -170,7 +170,7 @@ struct c2h_evt_hdr_t { u8 CmdID; u8 CmdLen; u8 CmdSeq; -} __attribute__((__packed__)); +} __packed; enum { /* tag_Package_Definition */ PACKAGE_DEFAULT, From 9bec6a9dc213aa0134d672f70d6afa363f4b3c88 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 5 May 2026 08:15:37 +0100 Subject: [PATCH 0752/5214] clk: renesas: r9a08g046: Add RSCI clocks and resets Add clock and reset entries for the Serial Communications Interfaces (RSCI) found on the RZ/G3L SoC. This includes various dividers and mux clocks needed for the four RSCI channels. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260505071544.8965-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 0004b9516fdf..0cb681358642 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -18,8 +18,10 @@ #define G3L_CPG_PL2_DDIV (0x204) #define G3L_CPG_PL3_DDIV (0x208) #define G3L_CPG_CA55CORE_DDIV (0x234) +#define G3L_CPG_RSCI_DDIV (0x238) #define G3L_CLKDIVSTATUS (0x280) #define G3L_CPG_ETH_SSEL (0x410) +#define G3L_CPG_RSCI_SSEL (0x414) #define G3L_CPG_ETH_SDIV (0x434) /* RZ/G3L Specific division configuration. */ @@ -30,6 +32,10 @@ #define G3L_DIV_CA55_CORE1 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 4, 3) #define G3L_DIV_CA55_CORE2 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 8, 3) #define G3L_DIV_CA55_CORE3 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 12, 3) +#define G3L_DIV_RSCI0 DDIV_PACK(G3L_CPG_RSCI_DDIV, 0, 2) +#define G3L_DIV_RSCI1 DDIV_PACK(G3L_CPG_RSCI_DDIV, 2, 2) +#define G3L_DIV_RSCI2 DDIV_PACK(G3L_CPG_RSCI_DDIV, 4, 2) +#define G3L_DIV_RSCI3 DDIV_PACK(G3L_CPG_RSCI_DDIV, 6, 2) #define G3L_SDIV_ETH_A DDIV_PACK(G3L_CPG_ETH_SDIV, 0, 2) #define G3L_SDIV_ETH_B DDIV_PACK(G3L_CPG_ETH_SDIV, 4, 1) #define G3L_SDIV_ETH_C DDIV_PACK(G3L_CPG_ETH_SDIV, 8, 2) @@ -43,6 +49,10 @@ #define G3L_DIV_CA55_CORE1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 13, 1) #define G3L_DIV_CA55_CORE2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 14, 1) #define G3L_DIV_CA55_CORE3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 15, 1) +#define G3L_DIV_RSCI0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 16, 1) +#define G3L_DIV_RSCI1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 17, 1) +#define G3L_DIV_RSCI2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 18, 1) +#define G3L_DIV_RSCI3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 19, 1) /* RZ/G3L Specific clocks select. */ #define G3L_SEL_ETH0_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 0, 1) @@ -55,6 +65,10 @@ #define G3L_SEL_ETH1_RM SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 10, 1) #define G3L_SEL_ETH1_CLK_TX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 11, 1) #define G3L_SEL_ETH1_CLK_RX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 12, 1) +#define G3L_SEL_RSCI0 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 0, 2) +#define G3L_SEL_RSCI1 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 2, 2) +#define G3L_SEL_RSCI2 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 4, 2) +#define G3L_SEL_RSCI3 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 6, 2) /* PLL 1/4/6/7 configuration registers macro. */ #define G3L_PLL1467_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) @@ -74,6 +88,10 @@ enum clk_ids { CLK_PLL1, CLK_PLL2, CLK_PLL2_DIV2, + CLK_PLL2_DIV2_4, + CLK_PLL2_DIV5, + CLK_PLL2_DIV6, + CLK_PLL2_DIV7, CLK_PLL3, CLK_PLL3_DIV2, CLK_PLL6, @@ -84,6 +102,10 @@ enum clk_ids { CLK_SEL_ETH1_TX, CLK_SEL_ETH1_RX, CLK_SEL_ETH1_RM, + CLK_SEL_RSCI0, + CLK_SEL_RSCI1, + CLK_SEL_RSCI2, + CLK_SEL_RSCI3, CLK_ETH0_TR, CLK_ETH0_RM, CLK_ETH1_TR, @@ -110,6 +132,14 @@ static const struct clk_div_table dtable_2_20[] = { { 0, 0 }, }; +static const struct clk_div_table dtable_2_16[] = { + { 0, 2 }, + { 1, 4 }, + { 2, 8 }, + { 3, 16 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_4_128[] = { { 0, 4 }, { 1, 8 }, @@ -140,6 +170,7 @@ static const char * const sel_eth0_rm[] = { ".pll6_div10", "eth0_rxc_rx_clk" }; static const char * const sel_eth1_tx[] = { ".div_eth1_tr", "eth1_txc_tx_clk" }; static const char * const sel_eth1_rx[] = { ".div_eth1_tr", "eth1_rxc_rx_clk" }; static const char * const sel_eth1_rm[] = { ".pll6_div10", "eth1_rxc_rx_clk" }; +static const char * const sel_rsci_rspi[] = { ".pll2_div5", ".pll2_div6", ".pll2_div7", ".pll2_div2_4" }; static const char * const sel_eth0_clk_tx_i[] = { ".sel_eth0_tx", ".div_eth0_rm" }; static const char * const sel_eth0_clk_rx_i[] = { ".sel_eth0_rx", ".div_eth0_rm" }; static const char * const sel_eth1_clk_tx_i[] = { ".sel_eth1_tx", ".div_eth1_rm" }; @@ -161,8 +192,16 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, G3L_PLL1467_CONF(0x54, 0x58, 0), 500000000UL), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), + DEF_FIXED(".pll2_div2_4", CLK_PLL2_DIV2_4, CLK_PLL2_DIV2, 1, 4), + DEF_FIXED(".pll2_div5", CLK_PLL2_DIV5, CLK_PLL2, 1, 5), + DEF_FIXED(".pll2_div6", CLK_PLL2_DIV6, CLK_PLL2, 1, 6), + DEF_FIXED(".pll2_div7", CLK_PLL2_DIV7, CLK_PLL2, 1, 7), DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), DEF_FIXED(".pll6_div10", CLK_PLL6_DIV10, CLK_PLL6, 1, 10), + DEF_MUX(".sel_rsci0", CLK_SEL_RSCI0, G3L_SEL_RSCI0, sel_rsci_rspi), + DEF_MUX(".sel_rsci1", CLK_SEL_RSCI1, G3L_SEL_RSCI1, sel_rsci_rspi), + DEF_MUX(".sel_rsci2", CLK_SEL_RSCI2, G3L_SEL_RSCI2, sel_rsci_rspi), + DEF_MUX(".sel_rsci3", CLK_SEL_RSCI3, G3L_SEL_RSCI3, sel_rsci_rspi), DEF_MUX(".sel_eth0_tx", CLK_SEL_ETH0_TX, G3L_SEL_ETH0_TX, sel_eth0_tx), DEF_MUX(".sel_eth0_rx", CLK_SEL_ETH0_RX, G3L_SEL_ETH0_RX, sel_eth0_rx), DEF_MUX(".sel_eth0_rm", CLK_SEL_ETH0_RM, G3L_SEL_ETH0_RM, sel_eth0_rm), @@ -189,6 +228,14 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { dtable_4_128, 0, 0, 0, NULL), DEF_G3S_DIV("P3", R9A08G046_CLK_P3, CLK_PLL2_DIV2, G3L_DIVPL2A, G3L_DIVPL2A_STS, dtable_4_128, 0, 0, 0, NULL), + DEF_G3S_DIV("P13", R9A08G046_CLK_P13, CLK_SEL_RSCI0, G3L_DIV_RSCI0, G3L_DIV_RSCI0_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P14", R9A08G046_CLK_P14, CLK_SEL_RSCI1, G3L_DIV_RSCI1, G3L_DIV_RSCI1_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P15", R9A08G046_CLK_P15, CLK_SEL_RSCI2, G3L_DIV_RSCI2, G3L_DIV_RSCI2_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P16", R9A08G046_CLK_P16, CLK_SEL_RSCI3, G3L_DIV_RSCI3, G3L_DIV_RSCI3_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), DEF_FIXED("HP", R9A08G046_CLK_HP, CLK_PLL6_DIV10, 1, 1), DEF_MUX_FLAGS("ETHTX01", R9A08G046_CLK_ETHTX01, G3L_SEL_ETH0_CLK_TX_I, sel_eth0_clk_tx_i, CLK_SET_RATE_PARENT), @@ -284,6 +331,22 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_MCPU3, BIT(4))), DEF_MOD("gpio_hclk", R9A08G046_GPIO_HCLK, R9A08G046_OSCCLK, 0x598, 0, MSTOP(BUS_PERI_CPU, BIT(6))), + DEF_MOD("rsci0_pclk", R9A08G046_RSCI0_PCLK, R9A08G046_CLK_P0, 0x618, 0, + MSTOP(BUS_MCPU2, BIT(7))), + DEF_MOD("rsci1_pclk", R9A08G046_RSCI1_PCLK, R9A08G046_CLK_P0, 0x618, 1, + MSTOP(BUS_MCPU2, BIT(8))), + DEF_MOD("rsci2_pclk", R9A08G046_RSCI2_PCLK, R9A08G046_CLK_P0, 0x618, 2, + MSTOP(BUS_MCPU3, BIT(11))), + DEF_MOD("rsci3_pclk", R9A08G046_RSCI3_PCLK, R9A08G046_CLK_P0, 0x618, 3, + MSTOP(BUS_MCPU3, BIT(12))), + DEF_MOD("rsci0_tclk", R9A08G046_RSCI0_TCLK, R9A08G046_CLK_P13, 0x618, 8, + MSTOP(BUS_MCPU2, BIT(7))), + DEF_MOD("rsci1_tclk", R9A08G046_RSCI1_TCLK, R9A08G046_CLK_P14, 0x618, 9, + MSTOP(BUS_MCPU2, BIT(8))), + DEF_MOD("rsci2_tclk", R9A08G046_RSCI2_TCLK, R9A08G046_CLK_P15, 0x618, 10, + MSTOP(BUS_MCPU3, BIT(11))), + DEF_MOD("rsci3_tclk", R9A08G046_RSCI3_TCLK, R9A08G046_CLK_P16, 0x618, 11, + MSTOP(BUS_MCPU3, BIT(12))), }; static const struct rzg2l_reset r9a08g046_resets[] = { @@ -308,6 +371,14 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_GPIO_RSTN, 0x898, 0), DEF_RST(R9A08G046_GPIO_PORT_RESETN, 0x898, 1), DEF_RST(R9A08G046_GPIO_SPARE_RESETN, 0x898, 2), + DEF_RST(R9A08G046_RSCI0_PRESETN, 0x918, 0), + DEF_RST(R9A08G046_RSCI1_PRESETN, 0x918, 1), + DEF_RST(R9A08G046_RSCI2_PRESETN, 0x918, 2), + DEF_RST(R9A08G046_RSCI3_PRESETN, 0x918, 3), + DEF_RST(R9A08G046_RSCI0_TRESETN, 0x918, 8), + DEF_RST(R9A08G046_RSCI1_TRESETN, 0x918, 9), + DEF_RST(R9A08G046_RSCI2_TRESETN, 0x918, 10), + DEF_RST(R9A08G046_RSCI3_TRESETN, 0x918, 11), }; static const unsigned int r9a08g046_crit_mod_clks[] __initconst = { From 29c46057d55648d88805e0412deb8729d806bf97 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 5 May 2026 08:15:38 +0100 Subject: [PATCH 0753/5214] clk: renesas: r9a08g046: Add SSIF-2 clocks and resets Add SSIF-2 clock and reset entries on the RZ/G3L SoC. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260505071544.8965-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 0cb681358642..fe3cb9a4f0ec 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -269,6 +269,22 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_REG0, BIT(0))), DEF_MOD("wdt0_clk", R9A08G046_WDT0_CLK, R9A08G046_OSCCLK, 0x548, 1, MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("ssi0_pclk2", R9A08G046_SSI0_PCLK2, R9A08G046_CLK_P0, 0x570, 0, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi0_pclk_sfr", R9A08G046_SSI0_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 1, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi1_pclk2", R9A08G046_SSI1_PCLK2, R9A08G046_CLK_P0, 0x570, 2, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi1_pclk_sfr", R9A08G046_SSI1_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 3, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi2_pclk2", R9A08G046_SSI2_PCLK2, R9A08G046_CLK_P0, 0x570, 4, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi2_pclk_sfr", R9A08G046_SSI2_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 5, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi3_pclk2", R9A08G046_SSI3_PCLK2, R9A08G046_CLK_P0, 0x570, 6, + MSTOP(BUS_MCPU1, BIT(13))), + DEF_MOD("ssi3_pclk_sfr", R9A08G046_SSI3_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 7, + MSTOP(BUS_MCPU1, BIT(13))), DEF_MOD("eth0_clk_axi", R9A08G046_ETH0_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 0, MSTOP(BUS_PERI_COM, BIT(2))), DEF_MOD("eth1_clk_axi", R9A08G046_ETH1_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 1, @@ -356,6 +372,10 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_DMAC_ARESETN, 0x82c, 0), DEF_RST(R9A08G046_DMAC_RST_ASYNC, 0x82c, 1), DEF_RST(R9A08G046_WDT0_PRESETN, 0x848, 0), + DEF_RST(R9A08G046_SSI0_RST_M2_REG, 0x870, 0), + DEF_RST(R9A08G046_SSI1_RST_M2_REG, 0x870, 1), + DEF_RST(R9A08G046_SSI2_RST_M2_REG, 0x870, 2), + DEF_RST(R9A08G046_SSI3_RST_M2_REG, 0x870, 3), DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), DEF_RST(R9A08G046_I2C0_MRST, 0x880, 0), From 5fcbbc1fcc4fa78bb5a184caa2c32db423676577 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 5 May 2026 08:15:39 +0100 Subject: [PATCH 0754/5214] clk: renesas: r9a08g046: Add RSPI clocks and resets Add clock and reset definitions for the three RSPI (Serial Peripheral Interface) channels on the RZ/G3L (R9A08G046) SoC. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260505071544.8965-4-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g046-cpg.c | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index fe3cb9a4f0ec..fc9db5a2f0ac 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -19,9 +19,11 @@ #define G3L_CPG_PL3_DDIV (0x208) #define G3L_CPG_CA55CORE_DDIV (0x234) #define G3L_CPG_RSCI_DDIV (0x238) +#define G3L_CPG_RSPI_DDIV (0x23c) #define G3L_CLKDIVSTATUS (0x280) #define G3L_CPG_ETH_SSEL (0x410) #define G3L_CPG_RSCI_SSEL (0x414) +#define G3L_CPG_RSPI_SSEL (0x418) #define G3L_CPG_ETH_SDIV (0x434) /* RZ/G3L Specific division configuration. */ @@ -36,6 +38,9 @@ #define G3L_DIV_RSCI1 DDIV_PACK(G3L_CPG_RSCI_DDIV, 2, 2) #define G3L_DIV_RSCI2 DDIV_PACK(G3L_CPG_RSCI_DDIV, 4, 2) #define G3L_DIV_RSCI3 DDIV_PACK(G3L_CPG_RSCI_DDIV, 6, 2) +#define G3L_DIV_RSPI0 DDIV_PACK(G3L_CPG_RSPI_DDIV, 0, 2) +#define G3L_DIV_RSPI1 DDIV_PACK(G3L_CPG_RSPI_DDIV, 2, 2) +#define G3L_DIV_RSPI2 DDIV_PACK(G3L_CPG_RSPI_DDIV, 4, 2) #define G3L_SDIV_ETH_A DDIV_PACK(G3L_CPG_ETH_SDIV, 0, 2) #define G3L_SDIV_ETH_B DDIV_PACK(G3L_CPG_ETH_SDIV, 4, 1) #define G3L_SDIV_ETH_C DDIV_PACK(G3L_CPG_ETH_SDIV, 8, 2) @@ -53,6 +58,9 @@ #define G3L_DIV_RSCI1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 17, 1) #define G3L_DIV_RSCI2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 18, 1) #define G3L_DIV_RSCI3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 19, 1) +#define G3L_DIV_RSPI0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 20, 1) +#define G3L_DIV_RSPI1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 21, 1) +#define G3L_DIV_RSPI2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 22, 1) /* RZ/G3L Specific clocks select. */ #define G3L_SEL_ETH0_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 0, 1) @@ -69,6 +77,9 @@ #define G3L_SEL_RSCI1 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 2, 2) #define G3L_SEL_RSCI2 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 4, 2) #define G3L_SEL_RSCI3 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 6, 2) +#define G3L_SEL_RSPI0 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 0, 2) +#define G3L_SEL_RSPI1 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 2, 2) +#define G3L_SEL_RSPI2 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 4, 2) /* PLL 1/4/6/7 configuration registers macro. */ #define G3L_PLL1467_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) @@ -106,6 +117,9 @@ enum clk_ids { CLK_SEL_RSCI1, CLK_SEL_RSCI2, CLK_SEL_RSCI3, + CLK_SEL_RSPI0, + CLK_SEL_RSPI1, + CLK_SEL_RSPI2, CLK_ETH0_TR, CLK_ETH0_RM, CLK_ETH1_TR, @@ -116,6 +130,14 @@ enum clk_ids { }; /* Divider tables */ +static const struct clk_div_table dtable_1_8[] = { + { 0, 1 }, + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_1_32[] = { { 0, 1 }, { 1, 2 }, @@ -202,6 +224,9 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_MUX(".sel_rsci1", CLK_SEL_RSCI1, G3L_SEL_RSCI1, sel_rsci_rspi), DEF_MUX(".sel_rsci2", CLK_SEL_RSCI2, G3L_SEL_RSCI2, sel_rsci_rspi), DEF_MUX(".sel_rsci3", CLK_SEL_RSCI3, G3L_SEL_RSCI3, sel_rsci_rspi), + DEF_MUX(".sel_rspi0", CLK_SEL_RSPI0, G3L_SEL_RSPI0, sel_rsci_rspi), + DEF_MUX(".sel_rspi1", CLK_SEL_RSPI1, G3L_SEL_RSPI1, sel_rsci_rspi), + DEF_MUX(".sel_rspi2", CLK_SEL_RSPI2, G3L_SEL_RSPI2, sel_rsci_rspi), DEF_MUX(".sel_eth0_tx", CLK_SEL_ETH0_TX, G3L_SEL_ETH0_TX, sel_eth0_tx), DEF_MUX(".sel_eth0_rx", CLK_SEL_ETH0_RX, G3L_SEL_ETH0_RX, sel_eth0_rx), DEF_MUX(".sel_eth0_rm", CLK_SEL_ETH0_RM, G3L_SEL_ETH0_RM, sel_eth0_rm), @@ -236,6 +261,12 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { dtable_2_16, 0, 100000000UL, 0, NULL), DEF_G3S_DIV("P16", R9A08G046_CLK_P16, CLK_SEL_RSCI3, G3L_DIV_RSCI3, G3L_DIV_RSCI3_STS, dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P17", R9A08G046_CLK_P17, CLK_SEL_RSPI0, G3L_DIV_RSPI0, G3L_DIV_RSPI0_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), + DEF_G3S_DIV("P18", R9A08G046_CLK_P18, CLK_SEL_RSPI1, G3L_DIV_RSPI1, G3L_DIV_RSPI1_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), + DEF_G3S_DIV("P19", R9A08G046_CLK_P19, CLK_SEL_RSPI2, G3L_DIV_RSPI2, G3L_DIV_RSPI2_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), DEF_FIXED("HP", R9A08G046_CLK_HP, CLK_PLL6_DIV10, 1, 1), DEF_MUX_FLAGS("ETHTX01", R9A08G046_CLK_ETHTX01, G3L_SEL_ETH0_CLK_TX_I, sel_eth0_clk_tx_i, CLK_SET_RATE_PARENT), @@ -345,6 +376,18 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_MCPU2, BIT(5))), DEF_MOD("scif5_clk_pck", R9A08G046_SCIF5_CLK_PCK, R9A08G046_CLK_P0, 0x584, 5, MSTOP(BUS_MCPU3, BIT(4))), + DEF_MOD("rspi0_pclk", R9A08G046_RSPI0_PCLK, R9A08G046_CLK_P3, 0x590, 0, + MSTOP(BUS_MCPU1, BIT(14))), + DEF_MOD("rspi1_pclk", R9A08G046_RSPI1_PCLK, R9A08G046_CLK_P3, 0x590, 1, + MSTOP(BUS_MCPU1, BIT(15))), + DEF_MOD("rspi2_pclk", R9A08G046_RSPI2_PCLK, R9A08G046_CLK_P3, 0x590, 2, + MSTOP(BUS_MCPU2, BIT(0))), + DEF_MOD("rspi0_tclk", R9A08G046_RSPI0_TCLK, R9A08G046_CLK_P17, 0x590, 8, + MSTOP(BUS_MCPU1, BIT(14))), + DEF_MOD("rspi1_tclk", R9A08G046_RSPI1_TCLK, R9A08G046_CLK_P18, 0x590, 9, + MSTOP(BUS_MCPU1, BIT(15))), + DEF_MOD("rspi2_tclk", R9A08G046_RSPI2_TCLK, R9A08G046_CLK_P19, 0x590, 10, + MSTOP(BUS_MCPU2, BIT(0))), DEF_MOD("gpio_hclk", R9A08G046_GPIO_HCLK, R9A08G046_OSCCLK, 0x598, 0, MSTOP(BUS_PERI_CPU, BIT(6))), DEF_MOD("rsci0_pclk", R9A08G046_RSCI0_PCLK, R9A08G046_CLK_P0, 0x618, 0, @@ -388,6 +431,12 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_SCIF3_RST_SYSTEM_N, 0x884, 3), DEF_RST(R9A08G046_SCIF4_RST_SYSTEM_N, 0x884, 4), DEF_RST(R9A08G046_SCIF5_RST_SYSTEM_N, 0x884, 5), + DEF_RST(R9A08G046_RSPI0_PRESETN, 0x890, 0), + DEF_RST(R9A08G046_RSPI1_PRESETN, 0x890, 1), + DEF_RST(R9A08G046_RSPI2_PRESETN, 0x890, 2), + DEF_RST(R9A08G046_RSPI0_TRESETN, 0x890, 8), + DEF_RST(R9A08G046_RSPI1_TRESETN, 0x890, 9), + DEF_RST(R9A08G046_RSPI2_TRESETN, 0x890, 10), DEF_RST(R9A08G046_GPIO_RSTN, 0x898, 0), DEF_RST(R9A08G046_GPIO_PORT_RESETN, 0x898, 1), DEF_RST(R9A08G046_GPIO_SPARE_RESETN, 0x898, 2), From cade7188314e922edb43bcd3e5b60f005ff5ea9b Mon Sep 17 00:00:00 2001 From: Pramod Maurya Date: Sun, 10 May 2026 13:51:02 -0400 Subject: [PATCH 0755/5214] staging: rtl8723bs: Remove multiple blank lines in include headers Remove superfluous consecutive blank lines from rtl8723bs include header files as flagged by checkpatch.pl. Signed-off-by: Pramod Maurya Link: https://patch.msgid.link/20260510175103.562518-2-pramod.nexgen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/cmd_osdep.h | 1 - drivers/staging/rtl8723bs/include/rtl8192c_recv.h | 1 - drivers/staging/rtl8723bs/include/rtl8723b_dm.h | 1 - drivers/staging/rtl8723bs/include/rtw_ioctl_set.h | 1 - drivers/staging/rtl8723bs/include/rtw_wifi_regd.h | 1 - drivers/staging/rtl8723bs/include/sdio_ops_linux.h | 1 - 6 files changed, 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/cmd_osdep.h b/drivers/staging/rtl8723bs/include/cmd_osdep.h index 5506f513dc01..41daa3c339a8 100644 --- a/drivers/staging/rtl8723bs/include/cmd_osdep.h +++ b/drivers/staging/rtl8723bs/include/cmd_osdep.h @@ -7,7 +7,6 @@ #ifndef __CMD_OSDEP_H_ #define __CMD_OSDEP_H_ - int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv); int rtw_init_evt_priv(struct evt_priv *pevtpriv); extern void _rtw_free_evt_priv(struct evt_priv *pevtpriv); diff --git a/drivers/staging/rtl8723bs/include/rtl8192c_recv.h b/drivers/staging/rtl8723bs/include/rtl8192c_recv.h index e2e9aa03ffdf..1f86654f0403 100644 --- a/drivers/staging/rtl8723bs/include/rtl8192c_recv.h +++ b/drivers/staging/rtl8723bs/include/rtl8192c_recv.h @@ -30,7 +30,6 @@ struct phy_stat { /* Rx smooth factor */ #define Rx_Smooth_Factor (20) - void rtl8192c_translate_rx_signal_stuff(union recv_frame *precvframe, struct phy_stat *pphy_status); void rtl8192c_query_rx_desc_status(union recv_frame *precvframe, struct recv_stat *pdesc); diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_dm.h b/drivers/staging/rtl8723bs/include/rtl8723b_dm.h index 1d2da5286e7c..50ab7aed079e 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_dm.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_dm.h @@ -29,5 +29,4 @@ void rtl8723b_HalDmWatchDog(struct adapter *padapter); void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *padapter); void rtl8723b_hal_dm_in_lps(struct adapter *padapter); - #endif diff --git a/drivers/staging/rtl8723bs/include/rtw_ioctl_set.h b/drivers/staging/rtl8723bs/include/rtw_ioctl_set.h index ab349de733c8..c3a1d1a84f78 100644 --- a/drivers/staging/rtl8723bs/include/rtw_ioctl_set.h +++ b/drivers/staging/rtl8723bs/include/rtw_ioctl_set.h @@ -7,7 +7,6 @@ #ifndef __RTW_IOCTL_SET_H_ #define __RTW_IOCTL_SET_H_ - typedef u8 NDIS_802_11_PMKID_VALUE[16]; u8 rtw_set_802_11_authentication_mode(struct adapter *pdapter, enum ndis_802_11_authentication_mode authmode); diff --git a/drivers/staging/rtl8723bs/include/rtw_wifi_regd.h b/drivers/staging/rtl8723bs/include/rtw_wifi_regd.h index e611651cb40b..e4d8fe9bf68d 100644 --- a/drivers/staging/rtl8723bs/include/rtw_wifi_regd.h +++ b/drivers/staging/rtl8723bs/include/rtw_wifi_regd.h @@ -13,5 +13,4 @@ void rtw_regd_init(struct wiphy *wiphy, struct regulatory_request *request)); void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); - #endif diff --git a/drivers/staging/rtl8723bs/include/sdio_ops_linux.h b/drivers/staging/rtl8723bs/include/sdio_ops_linux.h index 18830dd18372..cd67a7201065 100644 --- a/drivers/staging/rtl8723bs/include/sdio_ops_linux.h +++ b/drivers/staging/rtl8723bs/include/sdio_ops_linux.h @@ -25,6 +25,5 @@ void sd_write32(struct intf_hdl *pintfhdl, u32 addr, u32 v, s32 *err); s32 _sd_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata); s32 sd_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata); - void rtw_sdio_set_irq_thd(struct dvobj_priv *dvobj, void *thd_hdl); #endif From 9f9ed6afa6e45bea9906c87086b353877f6b5083 Mon Sep 17 00:00:00 2001 From: Pramod Maurya Date: Sun, 10 May 2026 13:52:04 -0400 Subject: [PATCH 0756/5214] staging: rtl8723bs: Replace __attribute__((packed)) with __packed in ieee80211.h Replace the verbose __attribute__ ((packed)) with the preferred kernel shorthand __packed in struct eapol and struct ieee80211_snap_hdr. Signed-off-by: Pramod Maurya Link: https://patch.msgid.link/20260510175207.563378-2-pramod.nexgen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/ieee80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h index a3f519e2f6d2..53707ea7bbc8 100644 --- a/drivers/staging/rtl8723bs/include/ieee80211.h +++ b/drivers/staging/rtl8723bs/include/ieee80211.h @@ -231,7 +231,7 @@ struct eapol { u8 version; u8 type; u16 length; -} __attribute__ ((packed)); +} __packed; #define IEEE80211_FCS_LEN 4 @@ -274,7 +274,7 @@ struct ieee80211_snap_hdr { u8 ssap; /* always 0xAA */ u8 ctrl; /* always 0x03 */ u8 oui[P80211_OUI_LEN]; /* organizational universal id */ -} __attribute__ ((packed)); +} __packed; #define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) From 8ba61dec20e56883bc782c8f062b2362432a5774 Mon Sep 17 00:00:00 2001 From: Pramod Maurya Date: Sun, 10 May 2026 13:52:06 -0400 Subject: [PATCH 0757/5214] staging: rtl8723bs: Fix block comment style in ieee80211.h Move trailing '*/' to a new line in multi-line block comments, add proper ' * ' prefix to comment continuation lines, and consolidate the OUI_MICROSOFT multi-line comment into a single line. Signed-off-by: Pramod Maurya Link: https://patch.msgid.link/20260510175207.563378-4-pramod.nexgen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/ieee80211.h | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h index 53707ea7bbc8..39ee139f1da9 100644 --- a/drivers/staging/rtl8723bs/include/ieee80211.h +++ b/drivers/staging/rtl8723bs/include/ieee80211.h @@ -400,12 +400,14 @@ enum { /* NOTE: This data is for statistical purposes; not all hardware provides this * information for frames received. Not setting these will not cause - * any adverse affects. */ + * any adverse affects. + */ /* IEEE 802.11 requires that STA supports concurrent reception of at least * three fragmented frames. This define can be increased to support more * concurrent frames, but it should be noted that each entry can consume about - * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ + * 2 kB of RAM and increasing cache size will slow down frame reassembly. + */ #define IEEE80211_FRAG_CACHE_LEN 4 #define SEC_KEY_1 (1<<0) @@ -431,19 +433,17 @@ enum { #define BIP_AAD_SIZE 20 /* - - 802.11 data frame from AP - - ,-------------------------------------------------------------------. -Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | - |------|------|---------|---------|---------|------|---------|------| -Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | - | | tion | (BSSID) | | | ence | data | | - `-------------------------------------------------------------------' - -Total: 28-2340 bytes - -*/ + * 802.11 data frame from AP + * + * ,-------------------------------------------------------------------. + * Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + * |------|------|---------|---------|---------|------|---------|------| + * Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + * | | tion | (BSSID) | | | ence | data | | + * `-------------------------------------------------------------------' + * + * Total: 28-2340 bytes + */ #define BEACON_PROBE_SSID_ID_POSITION 12 @@ -467,7 +467,8 @@ Total: 28-2340 bytes /* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs * only use 8, and then use extended rates for the remaining supported * rates. Other APs, however, stick all of their supported rates on the - * main rates information element... */ + * main rates information element... + */ #define MAX_RATES_LENGTH ((u8)12) #define MAX_RATES_EX_LENGTH ((u8)16) #define MAX_NETWORK_COUNT 128 @@ -497,11 +498,11 @@ Total: 28-2340 bytes #define IEEE80211_PS_MBCAST IEEE80211_DTIM_MBCAST #define IW_ESSID_MAX_SIZE 32 /* -join_res: --1: authentication fail --2: association fail -> 0: TID -*/ + * join_res: + * -1: authentication fail + * -2: association fail + * > 0: TID + */ #define DEFAULT_MAX_SCAN_AGE (15 * HZ) #define DEFAULT_FTS 2346 @@ -555,8 +556,7 @@ enum { ACT_PUBLIC_MAX }; -#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) - * 00:50:F2 */ +#define OUI_MICROSOFT 0x0050f2 /* Microsoft OUI (also used in Wi-Fi specs) */ #define WME_OUI_TYPE 2 #define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0 #define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1 From 6caa63431a2a2dcab3256148c88ab893eb7ccf0d Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:07 +0300 Subject: [PATCH 0758/5214] staging: rtl8723bs: remove unused DBG_FIXED_CHAN code Remove the DBG_FIXED_CHAN code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-2-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 14 -------------- drivers/staging/rtl8723bs/include/rtw_mlme_ext.h | 4 ---- 2 files changed, 18 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 67570b81034f..c966527c0ca5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -398,10 +398,6 @@ void init_mlme_ext_priv(struct adapter *padapter) pmlmeext->chan_scan_time = SURVEY_TO; pmlmeext->mlmeext_init = true; pmlmeext->active_keep_alive_check = true; - -#ifdef DBG_FIXED_CHAN - pmlmeext->fixed_chan = 0xFF; -#endif } void free_mlme_ext_priv(struct mlme_ext_priv *pmlmeext) @@ -3750,18 +3746,8 @@ void site_survey(struct adapter *padapter) /* val8 |= 0x0f; */ /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ if (pmlmeext->sitesurvey_res.channel_idx == 0) { -#ifdef DBG_FIXED_CHAN - if (pmlmeext->fixed_chan != 0xff) - set_channel_bwmode(padapter, pmlmeext->fixed_chan, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20); - else -#endif set_channel_bwmode(padapter, survey_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20); } else { -#ifdef DBG_FIXED_CHAN - if (pmlmeext->fixed_chan != 0xff) - r8723bs_select_channel(padapter, pmlmeext->fixed_chan); - else -#endif r8723bs_select_channel(padapter, survey_channel); } diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index 95769f90d196..b37d08bd5a59 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -416,10 +416,6 @@ struct mlme_ext_priv { u16 action_public_rxseq; u8 active_keep_alive_check; -#ifdef DBG_FIXED_CHAN - u8 fixed_chan; -#endif - }; void init_mlme_default_rate_set(struct adapter *padapter); From 7493412df4ec2eb38d78570aeceed30d9df07cb6 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:08 +0300 Subject: [PATCH 0759/5214] staging: rtl8723bs: remove unused DBG_RX_DUMP_EAP code Remove the DBG_RX_DUMP_EAP code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-3-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_recv.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index fc0397ad4741..e9ec6def7216 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -1491,17 +1491,6 @@ static signed int validate_recv_frame(struct adapter *adapter, union recv_frame struct recv_priv *precvpriv = &adapter->recvpriv; precvpriv->rx_drop++; - } else if (retval == _SUCCESS) { -#ifdef DBG_RX_DUMP_EAP - u8 bDumpRxPkt; - u16 eth_type; - - /* dump eapol */ - rtw_hal_get_def_var(adapter, HAL_DEF_DBG_DUMP_RXPKT, &(bDumpRxPkt)); - /* get ether_type */ - memcpy(ð_type, ptr + pattrib->hdrlen + pattrib->iv_len + LLC_HEADER_LENGTH, 2); - eth_type = ntohs((unsigned short) eth_type); -#endif } break; default: From d7d9eda56b1e6a5889199aab9fb10005fb38a477 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:09 +0300 Subject: [PATCH 0760/5214] staging: rtl8723bs: remove unused CONSISTENT_PN_ORDER code Remove the CONSISTENT_PN_ORDER code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-4-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_security.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index 2a5a4c0de9b1..50d8e30f7eda 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -649,13 +649,8 @@ static void construct_mic_iv(u8 *mic_iv, for (i = 2; i < 8; i++) mic_iv[i] = mpdu[i + 8]; /* mic_iv[2:7] = A2[0:5] = mpdu[10:15] */ - #ifdef CONSISTENT_PN_ORDER - for (i = 8; i < 14; i++) - mic_iv[i] = pn_vector[i - 8]; /* mic_iv[8:13] = PN[0:5] */ - #else for (i = 8; i < 14; i++) mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */ - #endif mic_iv[14] = (unsigned char)(payload_length / 256); mic_iv[15] = (unsigned char)(payload_length % 256); } @@ -772,13 +767,8 @@ static void construct_ctr_preload(u8 *ctr_preload, for (i = 2; i < 8; i++) ctr_preload[i] = mpdu[i + 8]; /* ctr_preload[2:7] = A2[0:5] = mpdu[10:15] */ -#ifdef CONSISTENT_PN_ORDER - for (i = 8; i < 14; i++) - ctr_preload[i] = pn_vector[i - 8]; /* ctr_preload[8:13] = PN[0:5] */ -#else for (i = 8; i < 14; i++) ctr_preload[i] = pn_vector[13 - i]; /* ctr_preload[8:13] = PN[5:0] */ -#endif ctr_preload[14] = (unsigned char)(c / 256); /* Ctr */ ctr_preload[15] = (unsigned char)(c % 256); } From a9142fdd5898dfc5ff949ba7a9825c92ee1c3764 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:10 +0300 Subject: [PATCH 0761/5214] staging: rtl8723bs: remove unused DBG_CH_SWITCH code Remove the DBG_CH_SWITCH code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-5-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/core/rtw_wlan_util.c | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 4f41f88908d7..917b80cbdc6a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -254,36 +254,10 @@ inline u8 rtw_get_oper_ch(struct adapter *adapter) inline void rtw_set_oper_ch(struct adapter *adapter, u8 ch) { -#ifdef DBG_CH_SWITCH - const int len = 128; - char msg[128] = {0}; - int cnt = 0; - int i = 0; -#endif /* DBG_CH_SWITCH */ struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); if (dvobj->oper_channel != ch) { dvobj->on_oper_ch_time = jiffies; - -#ifdef DBG_CH_SWITCH - cnt += scnprintf(msg + cnt, len - cnt, "switch to ch %3u", ch); - - for (i = 0; i < dvobj->iface_nums; i++) { - struct adapter *iface = dvobj->padapters[i]; - - cnt += scnprintf(msg + cnt, len - cnt, " [%s:", ADPT_ARG(iface)); - if (iface->mlmeextpriv.cur_channel == ch) - cnt += scnprintf(msg + cnt, len - cnt, "C"); - else - cnt += scnprintf(msg + cnt, len - cnt, "_"); - if (iface->wdinfo.listen_channel == ch && !rtw_p2p_chk_state(&iface->wdinfo, P2P_STATE_NONE)) - cnt += scnprintf(msg + cnt, len - cnt, "L"); - else - cnt += scnprintf(msg + cnt, len - cnt, "_"); - cnt += scnprintf(msg + cnt, len - cnt, "]"); - } - -#endif /* DBG_CH_SWITCH */ } dvobj->oper_channel = ch; From c91429207162d146d5e6bed5836f9c4f5ea625e6 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:11 +0300 Subject: [PATCH 0762/5214] staging: rtl8723bs: remove unused REMOVE_PACK code Remove the REMOVE_PACK code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-6-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h index aa4e36b6f2b4..3479eadde61f 100644 --- a/drivers/staging/rtl8723bs/hal/odm.h +++ b/drivers/staging/rtl8723bs/hal/odm.h @@ -975,10 +975,6 @@ enum ODM_FW_Config_Type { CONFIG_FW_BT, }; -#ifdef REMOVE_PACK -#pragma pack() -#endif - /* include "odm_function.h" */ /* 3 =========================================================== */ From 1ea2b0b62b336e287f4c893b2e404f0f0c56144a Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:12 +0300 Subject: [PATCH 0763/5214] staging: rtl8723bs: remove unused RTW_DVOBJ_CHIP_HW_TYPE code Remove the RTW_DVOBJ_CHIP_HW_TYPE code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-7-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/drv_types.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h index 7ed375ba18d8..30183d502b69 100644 --- a/drivers/staging/rtl8723bs/include/drv_types.h +++ b/drivers/staging/rtl8723bs/include/drv_types.h @@ -258,9 +258,6 @@ static inline struct dvobj_priv *pwrctl_to_dvobj(struct pwrctrl_priv *pwrctl_pri static inline struct device *dvobj_to_dev(struct dvobj_priv *dvobj) { /* todo: get interface type from dvobj and the return the dev accordingly */ -#ifdef RTW_DVOBJ_CHIP_HW_TYPE -#endif - return &dvobj->intf_data.func->dev; } From 4fc90c9f94984adbce00cc42d3746d00df51d103 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:13 +0300 Subject: [PATCH 0764/5214] staging: rtl8723bs: remove unused RTW_MLME_EXT_C_ code Remove the RTW_MLME_EXT_C_ code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-8-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_mlme_ext.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index b37d08bd5a59..d64e533a63c0 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -705,9 +705,4 @@ enum { MAX_C2HEVT }; - -#ifdef _RTW_MLME_EXT_C_ - -#endif/* _RTL8192C_CMD_C_ */ - #endif From acda662a0aa3c4f8bd6f1820b7c08ef0ebd501b9 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:14 +0300 Subject: [PATCH 0765/5214] staging: rtl8723bs: remove commented out code in rtw_mlme_ext.c Remove commented out code in the site_survey function as it is not used. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-9-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index c966527c0ca5..7ac23ccb39f7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -3741,10 +3741,6 @@ void site_survey(struct adapter *padapter) } if (survey_channel != 0) { - /* PAUSE 4-AC Queue when site_survey */ - /* rtw_hal_get_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ - /* val8 |= 0x0f; */ - /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ if (pmlmeext->sitesurvey_res.channel_idx == 0) { set_channel_bwmode(padapter, survey_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20); } else { From 3a7def187dcd8b004c99dc00b7b8e40072eefe7e Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Sun, 10 May 2026 16:43:15 +0300 Subject: [PATCH 0766/5214] staging: rtl8723bs: simplify if-else blocks in rtw_mlme_ext.c Fix indentation, remove unnecessary braces, and wrap long lines in if-else blocks inside the site_survey function. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260510134315.64295-10-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 7ac23ccb39f7..2667adc30b00 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -3741,11 +3741,11 @@ void site_survey(struct adapter *padapter) } if (survey_channel != 0) { - if (pmlmeext->sitesurvey_res.channel_idx == 0) { - set_channel_bwmode(padapter, survey_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20); - } else { - r8723bs_select_channel(padapter, survey_channel); - } + if (pmlmeext->sitesurvey_res.channel_idx == 0) + set_channel_bwmode(padapter, survey_channel, + HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20); + else + r8723bs_select_channel(padapter, survey_channel); if (ScanType == SCAN_ACTIVE) { /* obey the channel plan setting... */ { From 6a620bb2f4c1b40d9240b86bfc8e893eec876c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Steinm=C3=B6tzger?= Date: Sat, 9 May 2026 05:22:02 +0200 Subject: [PATCH 0767/5214] staging: rtl8723bs: replace non-standard BITn macros with BIT(n) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the local BIT0-BIT31 macro definitions from osdep_service.h and replace all usages with the kernel's BIT(n) macro from . NOTE: DYNAMIC_BB_DYNAMIC_TXPWR is defined as BIT2 and used in a bitwise NOT expression. Migrating to BIT(2) causes an -Werror=overflow warning due to the unsigned long result not fitting in u32. This instance has been left unconverted. Compile-tested only. Signed-off-by: Michael Steinmötzger Link: https://patch.msgid.link/20260509032202.146240-1-m.steinmoetzger@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 2 +- drivers/staging/rtl8723bs/core/rtw_mlme.c | 16 +- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- .../staging/rtl8723bs/core/rtw_wlan_util.c | 2 +- .../staging/rtl8723bs/hal/HalBtc8723b1Ant.c | 36 ++-- .../staging/rtl8723bs/hal/HalBtc8723b1Ant.h | 18 +- .../staging/rtl8723bs/hal/HalBtc8723b2Ant.c | 20 +- .../staging/rtl8723bs/hal/HalBtc8723b2Ant.h | 16 +- drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h | 10 +- .../staging/rtl8723bs/hal/HalHWImg8723B_BB.c | 30 +-- .../staging/rtl8723bs/hal/HalHWImg8723B_MAC.c | 24 +-- .../staging/rtl8723bs/hal/HalHWImg8723B_RF.c | 24 +-- .../staging/rtl8723bs/hal/HalPhyRf_8723B.c | 24 +-- drivers/staging/rtl8723bs/hal/hal_com.c | 4 +- drivers/staging/rtl8723bs/hal/odm.c | 2 +- drivers/staging/rtl8723bs/hal/odm.h | 50 ++--- drivers/staging/rtl8723bs/hal/odm_DIG.c | 22 +-- drivers/staging/rtl8723bs/hal/odm_DIG.h | 4 +- .../rtl8723bs/hal/odm_DynamicBBPowerSaving.c | 12 +- drivers/staging/rtl8723bs/hal/odm_HWConfig.c | 4 +- .../staging/rtl8723bs/hal/odm_RegDefine11N.h | 4 +- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 20 +- .../staging/rtl8723bs/hal/rtl8723b_phycfg.c | 12 +- .../staging/rtl8723bs/hal/rtl8723b_rf6052.c | 4 +- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 2 +- drivers/staging/rtl8723bs/include/drv_types.h | 6 +- .../staging/rtl8723bs/include/hal_com_reg.h | 104 +++++----- drivers/staging/rtl8723bs/include/hal_data.h | 2 +- drivers/staging/rtl8723bs/include/hal_intf.h | 16 +- drivers/staging/rtl8723bs/include/hal_phy.h | 4 +- .../staging/rtl8723bs/include/hal_pwr_seq.h | 182 +++++++++--------- .../staging/rtl8723bs/include/osdep_service.h | 32 --- .../staging/rtl8723bs/include/rtl8723b_spec.h | 88 ++++----- drivers/staging/rtl8723bs/include/rtw_cmd.h | 4 +- drivers/staging/rtl8723bs/include/rtw_ht.h | 16 +- drivers/staging/rtl8723bs/include/rtw_mlme.h | 6 +- .../staging/rtl8723bs/include/rtw_mlme_ext.h | 6 +- .../staging/rtl8723bs/include/rtw_pwrctrl.h | 4 +- 38 files changed, 401 insertions(+), 433 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index a7a636449813..803a608b74fa 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -95,7 +95,7 @@ u8 *data) /* <20130121, Kordan> For SMIC EFUSE specificatoin. */ /* 0x34[11]: SW force PGMEN input of efuse to high. (for the bank selected by 0x34[9:8]) */ /* PHY_SetMacReg(padapter, 0x34, BIT11, 0); */ - rtw_write16(padapter, 0x34, rtw_read16(padapter, 0x34) & (~BIT11)); + rtw_write16(padapter, 0x34, rtw_read16(padapter, 0x34) & (~BIT(11))); /* -----------------e-fuse reg ctrl --------------------------------- */ /* address */ diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 9cf1c95dd924..963b53027c68 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -2197,19 +2197,19 @@ void rtw_ht_use_default_setting(struct adapter *padapter) else phtpriv->bss_coexist = 0; - phtpriv->sgi_40m = TEST_FLAG(pregistrypriv->short_gi, BIT1) ? true : false; - phtpriv->sgi_20m = TEST_FLAG(pregistrypriv->short_gi, BIT0) ? true : false; + phtpriv->sgi_40m = TEST_FLAG(pregistrypriv->short_gi, BIT(1)) ? true : false; + phtpriv->sgi_20m = TEST_FLAG(pregistrypriv->short_gi, BIT(0)) ? true : false; /* LDPC support */ rtw_hal_get_def_var(padapter, HAL_DEF_RX_LDPC, (u8 *)&bHwLDPCSupport); CLEAR_FLAGS(phtpriv->ldpc_cap); if (bHwLDPCSupport) { - if (TEST_FLAG(pregistrypriv->ldpc_cap, BIT4)) + if (TEST_FLAG(pregistrypriv->ldpc_cap, BIT(4))) SET_FLAG(phtpriv->ldpc_cap, LDPC_HT_ENABLE_RX); } rtw_hal_get_def_var(padapter, HAL_DEF_TX_LDPC, (u8 *)&bHwLDPCSupport); if (bHwLDPCSupport) { - if (TEST_FLAG(pregistrypriv->ldpc_cap, BIT5)) + if (TEST_FLAG(pregistrypriv->ldpc_cap, BIT(5))) SET_FLAG(phtpriv->ldpc_cap, LDPC_HT_ENABLE_TX); } @@ -2217,12 +2217,12 @@ void rtw_ht_use_default_setting(struct adapter *padapter) rtw_hal_get_def_var(padapter, HAL_DEF_TX_STBC, (u8 *)&bHwSTBCSupport); CLEAR_FLAGS(phtpriv->stbc_cap); if (bHwSTBCSupport) { - if (TEST_FLAG(pregistrypriv->stbc_cap, BIT5)) + if (TEST_FLAG(pregistrypriv->stbc_cap, BIT(5))) SET_FLAG(phtpriv->stbc_cap, STBC_HT_ENABLE_TX); } rtw_hal_get_def_var(padapter, HAL_DEF_RX_STBC, (u8 *)&bHwSTBCSupport); if (bHwSTBCSupport) { - if (TEST_FLAG(pregistrypriv->stbc_cap, BIT4)) + if (TEST_FLAG(pregistrypriv->stbc_cap, BIT(4))) SET_FLAG(phtpriv->stbc_cap, STBC_HT_ENABLE_RX); } @@ -2230,10 +2230,10 @@ void rtw_ht_use_default_setting(struct adapter *padapter) rtw_hal_get_def_var(padapter, HAL_DEF_EXPLICIT_BEAMFORMER, (u8 *)&bHwSupportBeamformer); rtw_hal_get_def_var(padapter, HAL_DEF_EXPLICIT_BEAMFORMEE, (u8 *)&bHwSupportBeamformee); CLEAR_FLAGS(phtpriv->beamform_cap); - if (TEST_FLAG(pregistrypriv->beamform_cap, BIT4) && bHwSupportBeamformer) + if (TEST_FLAG(pregistrypriv->beamform_cap, BIT(4)) && bHwSupportBeamformer) SET_FLAG(phtpriv->beamform_cap, BEAMFORMING_HT_BEAMFORMER_ENABLE); - if (TEST_FLAG(pregistrypriv->beamform_cap, BIT5) && bHwSupportBeamformee) + if (TEST_FLAG(pregistrypriv->beamform_cap, BIT(5)) && bHwSupportBeamformee) SET_FLAG(phtpriv->beamform_cap, BEAMFORMING_HT_BEAMFORMEE_ENABLE); } diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 2667adc30b00..ba19a162896e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -5508,7 +5508,7 @@ u8 setkey_hdl(struct adapter *padapter, u8 *pbuf) else addr = null_addr; - ctrl = BIT(15) | BIT6 | ((pparm->algorithm) << 2) | pparm->keyid; + ctrl = BIT(15) | BIT(6) | ((pparm->algorithm) << 2) | pparm->keyid; write_cam(padapter, cam_id, ctrl, addr, pparm->key); netdev_dbg(padapter->pnetdev, "set group key camid:%d, addr:%pM, kid:%d, type:%s\n", diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 917b80cbdc6a..cd30e9103114 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -494,7 +494,7 @@ static bool _rtw_camid_is_gk(struct adapter *adapter, u8 cam_id) if (!(cam_ctl->bitmap & BIT(cam_id))) goto exit; - ret = (dvobj->cam_cache[cam_id].ctrl & BIT6) ? true : false; + ret = (dvobj->cam_cache[cam_id].ctrl & BIT(6)) ? true : false; exit: return ret; diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index 622970c7fcfa..f855df776a07 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -245,7 +245,7 @@ static void halbtc8723b1ant_QueryBtInfo(struct btc_coexist *pBtCoexist) pCoexSta->bC2hBtInfoReqSent = true; - H2C_Parameter[0] |= BIT0; /* trigger */ + H2C_Parameter[0] |= BIT(0); /* trigger */ pBtCoexist->fBtcFillH2c(pBtCoexist, 0x61, 1, H2C_Parameter); } @@ -602,7 +602,7 @@ static void halbtc8723b1ant_SetSwPenaltyTxRateAdaptive( H2C_Parameter[0] = 0x6; /* opCode, 0x6 = Retry_Penalty */ if (bLowPenaltyRa) { - H2C_Parameter[1] |= BIT0; + H2C_Parameter[1] |= BIT(0); H2C_Parameter[2] = 0x00; /* normal rate except MCS7/6/5, OFDM54/48/36 */ H2C_Parameter[3] = 0xf7; /* MCS7 or OFDM54 */ H2C_Parameter[4] = 0xf8; /* MCS6 or OFDM48 */ @@ -709,7 +709,7 @@ static void halbtc8723b1ant_SetFwIgnoreWlanAct( u8 H2C_Parameter[1] = {0}; if (bEnable) - H2C_Parameter[0] |= BIT0; /* function enable */ + H2C_Parameter[0] |= BIT(0); /* function enable */ pBtCoexist->fBtcFillH2c(pBtCoexist, 0x63, 1, H2C_Parameter); } @@ -826,8 +826,8 @@ static void halbtc8723b1ant_SetAntPath( /* 0x4c[24:23]= 00, Set Antenna control by BT_RFE_CTRL BT Vendor 0xac = 0xf002 */ u4Tmp = pBtCoexist->fBtcRead4Byte(pBtCoexist, 0x4c); - u4Tmp &= ~BIT23; - u4Tmp &= ~BIT24; + u4Tmp &= ~BIT(23); + u4Tmp &= ~BIT(24); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x4c, u4Tmp); } else { /* Use H2C to set GNT_BT to LOW */ @@ -842,7 +842,7 @@ static void halbtc8723b1ant_SetAntPath( u1Tmp = pBtCoexist->fBtcRead1Byte(pBtCoexist, 0x49d); cntBtCalChk++; - if (u1Tmp & BIT0) + if (u1Tmp & BIT(0)) mdelay(50); else break; @@ -861,8 +861,8 @@ static void halbtc8723b1ant_SetAntPath( if (bInitHwCfg) { /* 0x4c[23]= 0, 0x4c[24]= 1 Antenna control by WL/BT */ u4Tmp = pBtCoexist->fBtcRead4Byte(pBtCoexist, 0x4c); - u4Tmp &= ~BIT23; - u4Tmp |= BIT24; + u4Tmp &= ~BIT(23); + u4Tmp |= BIT(24); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x4c, u4Tmp); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x948, 0x0); /* fixed internal switch S1->WiFi, S0->BT */ @@ -908,8 +908,8 @@ static void halbtc8723b1ant_SetAntPath( if (bInitHwCfg) { /* 0x4c[23]= 1, 0x4c[24]= 0 Antenna control by 0x64 */ u4Tmp = pBtCoexist->fBtcRead4Byte(pBtCoexist, 0x4c); - u4Tmp |= BIT23; - u4Tmp &= ~BIT24; + u4Tmp |= BIT(23); + u4Tmp &= ~BIT(24); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x4c, u4Tmp); /* Fix Ext switch Main->S1, Aux->S0 */ @@ -967,12 +967,12 @@ static void halbtc8723b1ant_SetFwPstdma( pBtCoexist->fBtcGet(pBtCoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE, &bApEnable); if (bApEnable) { - if (byte1 & BIT4 && !(byte1 & BIT5)) { - realByte1 &= ~BIT4; - realByte1 |= BIT5; + if (byte1 & BIT(4) && !(byte1 & BIT(5))) { + realByte1 &= ~BIT(4); + realByte1 |= BIT(5); - realByte5 |= BIT5; - realByte5 &= ~BIT6; + realByte5 |= BIT(5); + realByte5 &= ~BIT(6); } } @@ -2121,7 +2121,7 @@ void EXhalbtc8723b1ant_PowerOnSetting(struct btc_coexist *pBtCoexist) /* enable BB, REG_SYS_FUNC_EN such that we can write 0x948 correctly. */ u2Tmp = pBtCoexist->fBtcRead2Byte(pBtCoexist, 0x2); - pBtCoexist->fBtcWrite2Byte(pBtCoexist, 0x2, u2Tmp | BIT0 | BIT1); + pBtCoexist->fBtcWrite2Byte(pBtCoexist, 0x2, u2Tmp | BIT(0) | BIT(1)); /* set GRAN_BT = 1 */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x765, 0x18); @@ -2500,7 +2500,7 @@ void EXhalbtc8723b1ant_BtInfoNotify( /* Here we need to resend some wifi info to BT */ /* because bt is reset and loss of the info. */ - if (pCoexSta->btInfoExt & BIT1) { + if (pCoexSta->btInfoExt & BIT(1)) { pBtCoexist->fBtcGet(pBtCoexist, BTC_GET_BL_WIFI_CONNECTED, &bWifiConnected); if (bWifiConnected) EXhalbtc8723b1ant_MediaStatusNotify(pBtCoexist, BTC_MEDIA_CONNECT); @@ -2508,7 +2508,7 @@ void EXhalbtc8723b1ant_BtInfoNotify( EXhalbtc8723b1ant_MediaStatusNotify(pBtCoexist, BTC_MEDIA_DISCONNECT); } - if (pCoexSta->btInfoExt & BIT3) { + if (pCoexSta->btInfoExt & BIT(3)) { if (!pBtCoexist->bManualControl && !pBtCoexist->bStopCoexDm) halbtc8723b1ant_IgnoreWlanAct(pBtCoexist, FORCE_EXEC, false); } else { diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.h b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.h index de471e27a185..7a12d92da0a9 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.h +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.h @@ -5,17 +5,17 @@ * ******************************************************************************/ /* The following is for 8723B 1ANT BT Co-exist definition */ -#define BT_INFO_8723B_1ANT_B_FTP BIT7 -#define BT_INFO_8723B_1ANT_B_A2DP BIT6 -#define BT_INFO_8723B_1ANT_B_HID BIT5 -#define BT_INFO_8723B_1ANT_B_SCO_BUSY BIT4 -#define BT_INFO_8723B_1ANT_B_ACL_BUSY BIT3 -#define BT_INFO_8723B_1ANT_B_INQ_PAGE BIT2 -#define BT_INFO_8723B_1ANT_B_SCO_ESCO BIT1 -#define BT_INFO_8723B_1ANT_B_CONNECTION BIT0 +#define BT_INFO_8723B_1ANT_B_FTP BIT(7) +#define BT_INFO_8723B_1ANT_B_A2DP BIT(6) +#define BT_INFO_8723B_1ANT_B_HID BIT(5) +#define BT_INFO_8723B_1ANT_B_SCO_BUSY BIT(4) +#define BT_INFO_8723B_1ANT_B_ACL_BUSY BIT(3) +#define BT_INFO_8723B_1ANT_B_INQ_PAGE BIT(2) +#define BT_INFO_8723B_1ANT_B_SCO_ESCO BIT(1) +#define BT_INFO_8723B_1ANT_B_CONNECTION BIT(0) #define BT_INFO_8723B_1ANT_A2DP_BASIC_RATE(_BT_INFO_EXT_) \ - (((_BT_INFO_EXT_ & BIT0)) ? true : false) + (((_BT_INFO_EXT_ & BIT(0))) ? true : false) #define BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT 2 diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c index f3195f6f7e57..e4b056873d73 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c @@ -185,7 +185,7 @@ static void halbtc8723b2ant_QueryBtInfo(struct btc_coexist *pBtCoexist) pCoexSta->bC2hBtInfoReqSent = true; - H2C_Parameter[0] |= BIT0; /* trigger */ + H2C_Parameter[0] |= BIT(0); /* trigger */ pBtCoexist->fBtcFillH2c(pBtCoexist, 0x61, 1, H2C_Parameter); } @@ -509,7 +509,7 @@ static void halbtc8723b2ant_SetSwPenaltyTxRateAdaptive( H2C_Parameter[0] = 0x6; /* opCode, 0x6 = Retry_Penalty */ if (bLowPenaltyRa) { - H2C_Parameter[1] |= BIT0; + H2C_Parameter[1] |= BIT(0); H2C_Parameter[2] = 0x00; /* normal rate except MCS7/6/5, OFDM54/48/36 */ H2C_Parameter[3] = 0xf7; /* MCS7 or OFDM54 */ H2C_Parameter[4] = 0xf8; /* MCS6 or OFDM48 */ @@ -748,7 +748,7 @@ static void halbtc8723b2ant_SetFwIgnoreWlanAct( u8 H2C_Parameter[1] = {0}; if (bEnable) - H2C_Parameter[0] |= BIT0; /* function enable */ + H2C_Parameter[0] |= BIT(0); /* function enable */ pBtCoexist->fBtcFillH2c(pBtCoexist, 0x63, 1, H2C_Parameter); } @@ -877,8 +877,8 @@ static void halbtc8723b2ant_SetAntPath( if (bInitHwCfg) { /* 0x4c[23]= 0, 0x4c[24]= 1 Antenna control by WL/BT */ u4Tmp = pBtCoexist->fBtcRead4Byte(pBtCoexist, 0x4c); - u4Tmp &= ~BIT23; - u4Tmp |= BIT24; + u4Tmp &= ~BIT(23); + u4Tmp |= BIT(24); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x4c, u4Tmp); } @@ -895,8 +895,8 @@ static void halbtc8723b2ant_SetAntPath( if (bInitHwCfg) { /* 0x4c[23]= 0, 0x4c[24]= 1 Antenna control by WL/BT */ u4Tmp = pBtCoexist->fBtcRead4Byte(pBtCoexist, 0x4c); - u4Tmp |= BIT23; - u4Tmp &= ~BIT24; + u4Tmp |= BIT(23); + u4Tmp &= ~BIT(24); pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0x4c, u4Tmp); } @@ -2336,7 +2336,7 @@ void EXhalbtc8723b2ant_PowerOnSetting(struct btc_coexist *pBtCoexist) /* enable BB, REG_SYS_FUNC_EN such that we can write 0x948 correctly. */ u2Tmp = pBtCoexist->fBtcRead2Byte(pBtCoexist, 0x2); - pBtCoexist->fBtcWrite2Byte(pBtCoexist, 0x2, u2Tmp | BIT0 | BIT1); + pBtCoexist->fBtcWrite2Byte(pBtCoexist, 0x2, u2Tmp | BIT(0) | BIT(1)); /* set GRAN_BT = 1 */ pBtCoexist->fBtcWrite1Byte(pBtCoexist, 0x765, 0x18); @@ -2505,7 +2505,7 @@ void EXhalbtc8723b2ant_BtInfoNotify( /* Here we need to resend some wifi info to BT */ /* because bt is reset and loss of the info. */ - if ((pCoexSta->btInfoExt & BIT1)) { + if ((pCoexSta->btInfoExt & BIT(1))) { pBtCoexist->fBtcGet(pBtCoexist, BTC_GET_BL_WIFI_CONNECTED, &bWifiConnected); if (bWifiConnected) @@ -2514,7 +2514,7 @@ void EXhalbtc8723b2ant_BtInfoNotify( EXhalbtc8723b2ant_MediaStatusNotify(pBtCoexist, BTC_MEDIA_DISCONNECT); } - if ((pCoexSta->btInfoExt & BIT3)) { + if ((pCoexSta->btInfoExt & BIT(3))) { halbtc8723b2ant_IgnoreWlanAct(pBtCoexist, FORCE_EXEC, false); } else { /* BT already NOT ignore Wlan active, do nothing here. */ diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.h b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.h index 1896ac54614c..ebc47f65e8d2 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.h +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.h @@ -5,14 +5,14 @@ * ******************************************************************************/ /* The following is for 8723B 2Ant BT Co-exist definition */ -#define BT_INFO_8723B_2ANT_B_FTP BIT7 -#define BT_INFO_8723B_2ANT_B_A2DP BIT6 -#define BT_INFO_8723B_2ANT_B_HID BIT5 -#define BT_INFO_8723B_2ANT_B_SCO_BUSY BIT4 -#define BT_INFO_8723B_2ANT_B_ACL_BUSY BIT3 -#define BT_INFO_8723B_2ANT_B_INQ_PAGE BIT2 -#define BT_INFO_8723B_2ANT_B_SCO_ESCO BIT1 -#define BT_INFO_8723B_2ANT_B_CONNECTION BIT0 +#define BT_INFO_8723B_2ANT_B_FTP BIT(7) +#define BT_INFO_8723B_2ANT_B_A2DP BIT(6) +#define BT_INFO_8723B_2ANT_B_HID BIT(5) +#define BT_INFO_8723B_2ANT_B_SCO_BUSY BIT(4) +#define BT_INFO_8723B_2ANT_B_ACL_BUSY BIT(3) +#define BT_INFO_8723B_2ANT_B_INQ_PAGE BIT(2) +#define BT_INFO_8723B_2ANT_B_SCO_ESCO BIT(1) +#define BT_INFO_8723B_2ANT_B_CONNECTION BIT(0) #define BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT 2 diff --git a/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h b/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h index 9091f2f75fe1..36b0f429dd4c 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h +++ b/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h @@ -69,11 +69,11 @@ enum btc_chip_interface { }; /* following is for wifi link status */ -#define WIFI_STA_CONNECTED BIT0 -#define WIFI_AP_CONNECTED BIT1 -#define WIFI_HS_CONNECTED BIT2 -#define WIFI_P2P_GO_CONNECTED BIT3 -#define WIFI_P2P_GC_CONNECTED BIT4 +#define WIFI_STA_CONNECTED BIT(0) +#define WIFI_AP_CONNECTED BIT(1) +#define WIFI_HS_CONNECTED BIT(2) +#define WIFI_P2P_GO_CONNECTED BIT(3) +#define WIFI_P2P_GC_CONNECTED BIT(4) struct btc_board_info { /* The following is some board information */ diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_BB.c b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_BB.c index 4666b2ff3157..afdde3980ec3 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_BB.c +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_BB.c @@ -11,11 +11,11 @@ static bool CheckPositive(struct dm_odm_t *pDM_Odm, const u32 Condition1, const u32 Condition2) { u8 _BoardType = - ((pDM_Odm->BoardType & BIT4) >> 4) << 0 | /* _GLNA */ - ((pDM_Odm->BoardType & BIT3) >> 3) << 1 | /* _GPA */ - ((pDM_Odm->BoardType & BIT7) >> 7) << 2 | /* _ALNA */ - ((pDM_Odm->BoardType & BIT6) >> 6) << 3 | /* _APA */ - ((pDM_Odm->BoardType & BIT2) >> 2) << 4; /* _BT */ + ((pDM_Odm->BoardType & BIT(4)) >> 4) << 0 | /* _GLNA */ + ((pDM_Odm->BoardType & BIT(3)) >> 3) << 1 | /* _GPA */ + ((pDM_Odm->BoardType & BIT(7)) >> 7) << 2 | /* _ALNA */ + ((pDM_Odm->BoardType & BIT(6)) >> 6) << 3 | /* _APA */ + ((pDM_Odm->BoardType & BIT(2)) >> 2) << 4; /* _BT */ u32 cond1 = Condition1, cond2 = Condition2; u32 driver1 = @@ -51,13 +51,13 @@ static bool CheckPositive(struct dm_odm_t *pDM_Odm, const u32 Condition1, const if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE */ return true; - if ((cond1 & BIT0) != 0) /* GLNA */ + if ((cond1 & BIT(0)) != 0) /* GLNA */ bitMask |= 0x000000FF; - if ((cond1 & BIT1) != 0) /* GPA */ + if ((cond1 & BIT(1)) != 0) /* GPA */ bitMask |= 0x0000FF00; - if ((cond1 & BIT2) != 0) /* ALNA */ + if ((cond1 & BIT(2)) != 0) /* ALNA */ bitMask |= 0x00FF0000; - if ((cond1 & BIT3) != 0) /* APA */ + if ((cond1 & BIT(3)) != 0) /* APA */ bitMask |= 0xFF000000; /* BoardType of each RF path is matched */ @@ -223,7 +223,7 @@ void ODM_ReadAndConfig_MP_8723B_AGC_TAB(struct dm_odm_t *pDM_Odm) } else { /* This line is the beginning of branch. */ bool bMatched = true; - u8 cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + u8 cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); if (cCond == COND_ELSE) { /* ELSE, ENDIF */ bMatched = true; @@ -255,10 +255,10 @@ void ODM_ReadAndConfig_MP_8723B_AGC_TAB(struct dm_odm_t *pDM_Odm) } /* Keeps reading until ENDIF. */ - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); while (cCond != COND_ENDIF && i < ArrayLen - 2) { READ_NEXT_PAIR(v1, v2, i); - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); } } } @@ -483,7 +483,7 @@ void ODM_ReadAndConfig_MP_8723B_PHY_REG(struct dm_odm_t *pDM_Odm) } else { /* This line is the beginning of branch. */ bool bMatched = true; - u8 cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + u8 cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); if (cCond == COND_ELSE) { /* ELSE, ENDIF */ bMatched = true; @@ -514,10 +514,10 @@ void ODM_ReadAndConfig_MP_8723B_PHY_REG(struct dm_odm_t *pDM_Odm) } /* Keeps reading until ENDIF. */ - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); while (cCond != COND_ENDIF && i < ArrayLen - 2) { READ_NEXT_PAIR(v1, v2, i); - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); } } } diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.c b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.c index 9a3393f5122b..0e098bd2d9f6 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.c +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.c @@ -11,11 +11,11 @@ static bool CheckPositive(struct dm_odm_t *pDM_Odm, const u32 Condition1, const u32 Condition2) { u8 _BoardType = - ((pDM_Odm->BoardType & BIT4) >> 4) << 0 | /* _GLNA */ - ((pDM_Odm->BoardType & BIT3) >> 3) << 1 | /* _GPA */ - ((pDM_Odm->BoardType & BIT7) >> 7) << 2 | /* _ALNA */ - ((pDM_Odm->BoardType & BIT6) >> 6) << 3 | /* _APA */ - ((pDM_Odm->BoardType & BIT2) >> 2) << 4; /* _BT */ + ((pDM_Odm->BoardType & BIT(4)) >> 4) << 0 | /* _GLNA */ + ((pDM_Odm->BoardType & BIT(3)) >> 3) << 1 | /* _GPA */ + ((pDM_Odm->BoardType & BIT(7)) >> 7) << 2 | /* _ALNA */ + ((pDM_Odm->BoardType & BIT(6)) >> 6) << 3 | /* _APA */ + ((pDM_Odm->BoardType & BIT(2)) >> 2) << 4; /* _BT */ u32 cond1 = Condition1, cond2 = Condition2; u32 driver1 = @@ -51,13 +51,13 @@ static bool CheckPositive(struct dm_odm_t *pDM_Odm, const u32 Condition1, const if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE */ return true; - if ((cond1 & BIT0) != 0) /* GLNA */ + if ((cond1 & BIT(0)) != 0) /* GLNA */ bitMask |= 0x000000FF; - if ((cond1 & BIT1) != 0) /* GPA */ + if ((cond1 & BIT(1)) != 0) /* GPA */ bitMask |= 0x0000FF00; - if ((cond1 & BIT2) != 0) /* ALNA */ + if ((cond1 & BIT(2)) != 0) /* ALNA */ bitMask |= 0x00FF0000; - if ((cond1 & BIT3) != 0) /* APA */ + if ((cond1 & BIT(3)) != 0) /* APA */ bitMask |= 0xFF000000; /* BoardType of each RF path is matched */ @@ -195,7 +195,7 @@ void ODM_ReadAndConfig_MP_8723B_MAC_REG(struct dm_odm_t *pDM_Odm) } else { /* This line is the beginning of branch. */ bool bMatched = true; - u8 cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + u8 cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); if (cCond == COND_ELSE) { /* ELSE, ENDIF */ bMatched = true; @@ -226,10 +226,10 @@ void ODM_ReadAndConfig_MP_8723B_MAC_REG(struct dm_odm_t *pDM_Odm) } /* Keeps reading until ENDIF. */ - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); while (cCond != COND_ENDIF && i < ArrayLen - 2) { READ_NEXT_PAIR(v1, v2, i); - cCond = (u8)((v1 & (BIT29 | BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); } } } diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c index 0c7d0307b822..bfdebc840730 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c @@ -13,11 +13,11 @@ static bool CheckPositive( ) { u8 _BoardType = - ((pDM_Odm->BoardType & BIT4) >> 4) << 0 | /* _GLNA */ - ((pDM_Odm->BoardType & BIT3) >> 3) << 1 | /* _GPA */ - ((pDM_Odm->BoardType & BIT7) >> 7) << 2 | /* _ALNA */ - ((pDM_Odm->BoardType & BIT6) >> 6) << 3 | /* _APA */ - ((pDM_Odm->BoardType & BIT2) >> 2) << 4; /* _BT */ + ((pDM_Odm->BoardType & BIT(4)) >> 4) << 0 | /* _GLNA */ + ((pDM_Odm->BoardType & BIT(3)) >> 3) << 1 | /* _GPA */ + ((pDM_Odm->BoardType & BIT(7)) >> 7) << 2 | /* _ALNA */ + ((pDM_Odm->BoardType & BIT(6)) >> 6) << 3 | /* _APA */ + ((pDM_Odm->BoardType & BIT(2)) >> 2) << 4; /* _BT */ u32 cond1 = Condition1, cond2 = Condition2; u32 driver1 = @@ -59,13 +59,13 @@ static bool CheckPositive( if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE */ return true; - if ((cond1 & BIT0) != 0) /* GLNA */ + if ((cond1 & BIT(0)) != 0) /* GLNA */ bitMask |= 0x000000FF; - if ((cond1 & BIT1) != 0) /* GPA */ + if ((cond1 & BIT(1)) != 0) /* GPA */ bitMask |= 0x0000FF00; - if ((cond1 & BIT2) != 0) /* ALNA */ + if ((cond1 & BIT(2)) != 0) /* ALNA */ bitMask |= 0x00FF0000; - if ((cond1 & BIT3) != 0) /* APA */ + if ((cond1 & BIT(3)) != 0) /* APA */ bitMask |= 0xFF000000; /* BoardType of each RF path is matched */ @@ -227,7 +227,7 @@ void ODM_ReadAndConfig_MP_8723B_RadioA(struct dm_odm_t *pDM_Odm) } else { /* This line is the beginning of branch. */ bool bMatched = true; - u8 cCond = (u8)((v1 & (BIT29|BIT28)) >> 28); + u8 cCond = (u8)((v1 & (BIT(29)|BIT(28))) >> 28); if (cCond == COND_ELSE) { /* ELSE, ENDIF */ bMatched = true; @@ -259,10 +259,10 @@ void ODM_ReadAndConfig_MP_8723B_RadioA(struct dm_odm_t *pDM_Odm) } /* Keeps reading until ENDIF. */ - cCond = (u8)((v1 & (BIT29|BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29)|BIT(28))) >> 28); while (cCond != COND_ENDIF && i < ArrayLen-2) { READ_NEXT_PAIR(v1, v2, i); - cCond = (u8)((v1 & (BIT29|BIT28)) >> 28); + cCond = (u8)((v1 & (BIT(29)|BIT(28))) >> 28); } } } diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 005b0b724bdd..de431bc75627 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -79,7 +79,7 @@ static void setIqkMatrix_8723B( PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XCTxAFE, bMaskH4Bits, value32); value32 = ((IqkResult_X * ele_D)>>7)&0x01; - PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT24, value32); + PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT(24), value32); break; case RF_PATH_B: /* write new elements A, C, D to regC88 and regC9C, @@ -92,7 +92,7 @@ static void setIqkMatrix_8723B( PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XDTxAFE, bMaskH4Bits, value32); value32 = ((IqkResult_X * ele_D)>>7)&0x01; - PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT28, value32); + PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT(28), value32); break; default: @@ -103,13 +103,13 @@ static void setIqkMatrix_8723B( case RF_PATH_A: PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XATxIQImbalance, bMaskDWord, OFDMSwingTable_New[OFDM_index]); PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XCTxAFE, bMaskH4Bits, 0x00); - PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT24, 0x00); + PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT(24), 0x00); break; case RF_PATH_B: PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XBTxIQImbalance, bMaskDWord, OFDMSwingTable_New[OFDM_index]); PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_XDTxAFE, bMaskH4Bits, 0x00); - PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT28, 0x00); + PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_ECCAThreshold, BIT(28), 0x00); break; default: @@ -427,7 +427,7 @@ static u8 phy_PathA_IQK_8723B( tmp = 0x400 - tmp; if ( - !(regEAC & BIT28) && + !(regEAC & BIT(28)) && (((regE94 & 0x03FF0000)>>16) != 0x142) && (((regE9C & 0x03FF0000)>>16) != 0x42) && (((regE94 & 0x03FF0000)>>16) < 0x110) && @@ -526,7 +526,7 @@ static u8 phy_PathA_RxIQK8723B( tmp = 0x400 - tmp; if ( - !(regEAC & BIT28) && + !(regEAC & BIT(28)) && (((regE94 & 0x03FF0000)>>16) != 0x142) && (((regE9C & 0x03FF0000)>>16) != 0x42) && (((regE94 & 0x03FF0000)>>16) < 0x110) && @@ -617,7 +617,7 @@ static u8 phy_PathA_RxIQK8723B( tmp = 0x400 - tmp; if ( - !(regEAC & BIT27) && /* if Tx is OK, check whether Rx is OK */ + !(regEAC & BIT(27)) && /* if Tx is OK, check whether Rx is OK */ (((regEA4 & 0x03FF0000)>>16) != 0x132) && (((regEAC & 0x03FF0000)>>16) != 0x36) && (((regEA4 & 0x03FF0000)>>16) < 0x110) && @@ -710,7 +710,7 @@ static u8 phy_PathB_IQK_8723B(struct adapter *padapter) tmp = 0x400 - tmp; if ( - !(regEAC & BIT28) && + !(regEAC & BIT(28)) && (((regE94 & 0x03FF0000)>>16) != 0x142) && (((regE9C & 0x03FF0000)>>16) != 0x42) && (((regE94 & 0x03FF0000)>>16) < 0x110) && @@ -805,7 +805,7 @@ static u8 phy_PathB_RxIQK8723B(struct adapter *padapter, bool configPathB) tmp = 0x400 - tmp; if ( - !(regEAC & BIT28) && + !(regEAC & BIT(28)) && (((regE94 & 0x03FF0000)>>16) != 0x142) && (((regE9C & 0x03FF0000)>>16) != 0x42) && (((regE94 & 0x03FF0000)>>16) < 0x110) && @@ -897,7 +897,7 @@ static u8 phy_PathB_RxIQK8723B(struct adapter *padapter, bool configPathB) tmp = 0x400 - tmp; if ( - !(regEAC & BIT27) && /* if Tx is OK, check whether Rx is OK */ + !(regEAC & BIT(27)) && /* if Tx is OK, check whether Rx is OK */ (((regEA4 & 0x03FF0000)>>16) != 0x132) && (((regEAC & 0x03FF0000)>>16) != 0x36) && (((regEA4 & 0x03FF0000)>>16) < 0x110) && @@ -1196,9 +1196,9 @@ static void _PHY_MACSettingCalibration8723B( rtw_write8(pDM_Odm->Adapter, MACReg[i], 0x3F); for (i = 1 ; i < (IQK_MAC_REG_NUM - 1); i++) { - rtw_write8(pDM_Odm->Adapter, MACReg[i], (u8)(MACBackup[i]&(~BIT3))); + rtw_write8(pDM_Odm->Adapter, MACReg[i], (u8)(MACBackup[i]&(~BIT(3)))); } - rtw_write8(pDM_Odm->Adapter, MACReg[i], (u8)(MACBackup[i]&(~BIT5))); + rtw_write8(pDM_Odm->Adapter, MACReg[i], (u8)(MACBackup[i]&(~BIT(5)))); } diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 51dbb21345f8..792cd071df22 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -756,7 +756,7 @@ void rtw_bb_rf_gain_offset(struct adapter *padapter) u32 v1 = 0, v2 = 0, target = 0; u32 i = 0; - if (value & BIT4) { + if (value & BIT(4)) { if (padapter->eeprompriv.EEPROMRFGainVal != 0xff) { rtw_hal_read_rfreg(padapter, RF_PATH_A, 0x7f, 0xffffffff); @@ -768,7 +768,7 @@ void rtw_bb_rf_gain_offset(struct adapter *padapter) break; } } - PHY_SetRFReg(padapter, RF_PATH_A, REG_RF_BB_GAIN_OFFSET, BIT18|BIT17|BIT16|BIT15, target); + PHY_SetRFReg(padapter, RF_PATH_A, REG_RF_BB_GAIN_OFFSET, BIT(18)|BIT(17)|BIT(16)|BIT(15), target); rtw_hal_read_rfreg(padapter, RF_PATH_A, 0x7f, 0xffffffff); } diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index dbd8183363a8..df394323d901 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -551,7 +551,7 @@ void ODM_TXPowerTrackingCheck(struct dm_odm_t *pDM_Odm) return; if (!pDM_Odm->RFCalibrateInfo.TM_Trigger) { /* at least delay 1 sec */ - PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, RF_T_METER_NEW, (BIT17 | BIT16), 0x03); + PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, RF_T_METER_NEW, (BIT(17) | BIT(16)), 0x03); pDM_Odm->RFCalibrateInfo.TM_Trigger = 1; return; diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h index 3479eadde61f..ef23a2088733 100644 --- a/drivers/staging/rtl8723bs/hal/odm.h +++ b/drivers/staging/rtl8723bs/hal/odm.h @@ -355,30 +355,30 @@ enum { /* _ODM_Support_Ability_Definition */ /* */ /* BB ODM section BIT 0-15 */ /* */ - ODM_BB_DIG = BIT0, - ODM_BB_RA_MASK = BIT1, - ODM_BB_DYNAMIC_TXPWR = BIT2, - ODM_BB_FA_CNT = BIT3, - ODM_BB_RSSI_MONITOR = BIT4, - ODM_BB_CCK_PD = BIT5, - ODM_BB_ANT_DIV = BIT6, - ODM_BB_PWR_SAVE = BIT7, - ODM_BB_PWR_TRAIN = BIT8, - ODM_BB_RATE_ADAPTIVE = BIT9, - ODM_BB_PATH_DIV = BIT10, - ODM_BB_PSD = BIT11, - ODM_BB_RXHP = BIT12, - ODM_BB_ADAPTIVITY = BIT13, - ODM_BB_CFO_TRACKING = BIT14, + ODM_BB_DIG = BIT(0), + ODM_BB_RA_MASK = BIT(1), + ODM_BB_DYNAMIC_TXPWR = BIT(2), + ODM_BB_FA_CNT = BIT(3), + ODM_BB_RSSI_MONITOR = BIT(4), + ODM_BB_CCK_PD = BIT(5), + ODM_BB_ANT_DIV = BIT(6), + ODM_BB_PWR_SAVE = BIT(7), + ODM_BB_PWR_TRAIN = BIT(8), + ODM_BB_RATE_ADAPTIVE = BIT(9), + ODM_BB_PATH_DIV = BIT(10), + ODM_BB_PSD = BIT(11), + ODM_BB_RXHP = BIT(12), + ODM_BB_ADAPTIVITY = BIT(13), + ODM_BB_CFO_TRACKING = BIT(14), /* MAC DM section BIT 16-23 */ - ODM_MAC_EDCA_TURBO = BIT16, - ODM_MAC_EARLY_MODE = BIT17, + ODM_MAC_EDCA_TURBO = BIT(16), + ODM_MAC_EARLY_MODE = BIT(17), /* RF ODM section BIT 24-31 */ - ODM_RF_TX_PWR_TRACK = BIT24, - ODM_RF_RX_GAIN_TRACK = BIT25, - ODM_RF_CALIBRATION = BIT26, + ODM_RF_TX_PWR_TRACK = BIT(24), + ODM_RF_RX_GAIN_TRACK = BIT(25), + ODM_RF_CALIBRATION = BIT(26), }; /* ODM_CMNINFO_INTERFACE */ @@ -389,7 +389,7 @@ enum { /* tag_ODM_Support_Interface_Definition */ /* ODM_CMNINFO_IC_TYPE */ enum { /* tag_ODM_Support_IC_Type_Definition */ - ODM_RTL8723B = BIT8, + ODM_RTL8723B = BIT(8), }; /* ODM_CMNINFO_CUT_VER */ @@ -434,10 +434,10 @@ enum { /* tag_ODM_RF_Type_Definition */ /* ODM_CMNINFO_WM_MODE */ enum { /* tag_Wireless_Mode_Definition */ ODM_WM_UNKNOWN = 0x0, - ODM_WM_B = BIT0, - ODM_WM_G = BIT1, - ODM_WM_N24G = BIT3, - ODM_WM_AUTO = BIT5, + ODM_WM_B = BIT(0), + ODM_WM_G = BIT(1), + ODM_WM_N24G = BIT(3), + ODM_WM_AUTO = BIT(5), }; /* ODM_CMNINFO_BW */ diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index fba85cc0e7bf..ef3c2db8553e 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -19,8 +19,8 @@ void odm_NHMCounterStatisticsInit(void *pDM_VOID) rtw_write32(pDM_Odm->Adapter, ODM_REG_NHM_TH3_TO_TH0_11N, 0xffffff52); /* 0x898 = 0xffffff52 th_3, th_2, th_1, th_0 */ rtw_write32(pDM_Odm->Adapter, ODM_REG_NHM_TH7_TO_TH4_11N, 0xffffffff); /* 0x89c = 0xffffffff th_7, th_6, th_5, th_4 */ PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_FPGA0_IQK_11N, bMaskByte0, 0xff); /* 0xe28[7:0]= 0xff th_8 */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT10|BIT9|BIT8, 0x7); /* 0x890[9:8]=3 enable CCX */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTC_11N, BIT7, 0x1); /* 0xc0c[7]= 1 max power among all RX ants */ + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT(10)|BIT(9)|BIT(8), 0x7); /* 0x890[9:8]=3 enable CCX */ + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTC_11N, BIT(7), 0x1); /* 0xc0c[7]= 1 max power among all RX ants */ } void odm_NHMCounterStatistics(void *pDM_VOID) @@ -48,8 +48,8 @@ void odm_NHMCounterStatisticsReset(void *pDM_VOID) { struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT1, 0); - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT1, 1); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT(1), 0); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_NHM_TH9_TH10_11N, BIT(1), 1); } void odm_NHMBBInit(void *pDM_VOID) @@ -143,9 +143,9 @@ void odm_SearchPwdBLowerBound(void *pDM_VOID, u8 IGI_target) for (cnt = 0; cnt < 20; cnt++) { value32 = PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG_RPT_11N, bMaskDWord); - if (value32 & BIT30) + if (value32 & BIT(30)) pDM_Odm->txEdcca1 = pDM_Odm->txEdcca1 + 1; - else if (value32 & BIT29) + else if (value32 & BIT(29)) pDM_Odm->txEdcca1 = pDM_Odm->txEdcca1 + 1; else pDM_Odm->txEdcca0 = pDM_Odm->txEdcca0 + 1; @@ -209,7 +209,7 @@ void odm_AdaptivityInit(void *pDM_VOID) pDM_Odm->Adaptivity_IGI_upper = 0; odm_NHMBBInit(pDM_Odm); - PHY_SetBBReg(pDM_Odm->Adapter, REG_RD_CTRL, BIT11, 1); /* stop counting if EDCCA is asserted */ + PHY_SetBBReg(pDM_Odm->Adapter, REG_RD_CTRL, BIT(11), 1); /* stop counting if EDCCA is asserted */ } @@ -612,9 +612,9 @@ void odm_FalseAlarmCounterStatistics(void *pDM_VOID) /* hold ofdm counter */ /* hold page C counter */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_HOLDC_11N, BIT31, 1); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_HOLDC_11N, BIT(31), 1); /* hold page D counter */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTD_11N, BIT31, 1); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_OFDM_FA_RSTD_11N, BIT(31), 1); ret_value = PHY_QueryBBReg( pDM_Odm->Adapter, ODM_REG_OFDM_FA_TYPE1_11N, bMaskDWord @@ -649,8 +649,8 @@ void odm_FalseAlarmCounterStatistics(void *pDM_VOID) { /* hold cck counter */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT12, 1); - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT14, 1); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT(12), 1); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG_CCK_FA_RST_11N, BIT(14), 1); ret_value = PHY_QueryBBReg( pDM_Odm->Adapter, ODM_REG_CCK_FA_LSB_11N, bMaskByte0 diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.h b/drivers/staging/rtl8723bs/hal/odm_DIG.h index a5b041101c89..032c4abae49b 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.h +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.h @@ -78,8 +78,8 @@ struct false_ALARM_STATISTICS { }; enum ODM_Pause_DIG_TYPE { - ODM_PAUSE_DIG = BIT0, - ODM_RESUME_DIG = BIT1 + ODM_PAUSE_DIG = BIT(0), + ODM_RESUME_DIG = BIT(1) }; #define DM_DIG_THRESH_HIGH 40 diff --git a/drivers/staging/rtl8723bs/hal/odm_DynamicBBPowerSaving.c b/drivers/staging/rtl8723bs/hal/odm_DynamicBBPowerSaving.c index 57c5736527d2..ba6357c6a7ed 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DynamicBBPowerSaving.c +++ b/drivers/staging/rtl8723bs/hal/odm_DynamicBBPowerSaving.c @@ -35,7 +35,7 @@ void ODM_RF_Saving(void *pDM_VOID, u8 bForceInNormal) if (pDM_PSTable->initialize == 0) { pDM_PSTable->Reg874 = (PHY_QueryBBReg(pDM_Odm->Adapter, 0x874, bMaskDWord)&0x1CC000)>>14; - pDM_PSTable->RegC70 = (PHY_QueryBBReg(pDM_Odm->Adapter, 0xc70, bMaskDWord)&BIT3)>>3; + pDM_PSTable->RegC70 = (PHY_QueryBBReg(pDM_Odm->Adapter, 0xc70, bMaskDWord)&BIT(3))>>3; pDM_PSTable->Reg85C = (PHY_QueryBBReg(pDM_Odm->Adapter, 0x85c, bMaskDWord)&0xFF000000)>>24; pDM_PSTable->RegA74 = (PHY_QueryBBReg(pDM_Odm->Adapter, 0xa74, bMaskDWord)&0xF000)>>12; /* Reg818 = PHY_QueryBBReg(padapter, 0x818, bMaskDWord); */ @@ -63,18 +63,18 @@ void ODM_RF_Saving(void *pDM_VOID, u8 bForceInNormal) if (pDM_PSTable->PreRFState != pDM_PSTable->CurRFState) { if (pDM_PSTable->CurRFState == RF_Save) { PHY_SetBBReg(pDM_Odm->Adapter, 0x874, 0x1C0000, 0x2); /* Reg874[20:18]=3'b010 */ - PHY_SetBBReg(pDM_Odm->Adapter, 0xc70, BIT3, 0); /* RegC70[3]= 1'b0 */ + PHY_SetBBReg(pDM_Odm->Adapter, 0xc70, BIT(3), 0); /* RegC70[3]= 1'b0 */ PHY_SetBBReg(pDM_Odm->Adapter, 0x85c, 0xFF000000, 0x63); /* Reg85C[31:24]= 0x63 */ PHY_SetBBReg(pDM_Odm->Adapter, 0x874, 0xC000, 0x2); /* Reg874[15:14]=2'b10 */ PHY_SetBBReg(pDM_Odm->Adapter, 0xa74, 0xF000, 0x3); /* RegA75[7:4]= 0x3 */ - PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT28, 0x0); /* Reg818[28]= 1'b0 */ - PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT28, 0x1); /* Reg818[28]= 1'b1 */ + PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT(28), 0x0); /* Reg818[28]= 1'b0 */ + PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT(28), 0x1); /* Reg818[28]= 1'b1 */ } else { PHY_SetBBReg(pDM_Odm->Adapter, 0x874, 0x1CC000, pDM_PSTable->Reg874); - PHY_SetBBReg(pDM_Odm->Adapter, 0xc70, BIT3, pDM_PSTable->RegC70); + PHY_SetBBReg(pDM_Odm->Adapter, 0xc70, BIT(3), pDM_PSTable->RegC70); PHY_SetBBReg(pDM_Odm->Adapter, 0x85c, 0xFF000000, pDM_PSTable->Reg85C); PHY_SetBBReg(pDM_Odm->Adapter, 0xa74, 0xF000, pDM_PSTable->RegA74); - PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT28, 0x0); + PHY_SetBBReg(pDM_Odm->Adapter, 0x818, BIT(28), 0x0); } pDM_PSTable->PreRFState = pDM_PSTable->CurRFState; } diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c index 994b8c578e7a..4784d33eab0e 100644 --- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c +++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c @@ -335,7 +335,7 @@ static void odm_Process_RSSIForDM( } } - pEntry->rssi_stat.PacketMap = (pEntry->rssi_stat.PacketMap<<1) | BIT0; + pEntry->rssi_stat.PacketMap = (pEntry->rssi_stat.PacketMap<<1) | BIT(0); } else { RSSI_Ave = pPhyInfo->rx_pwd_ba11; @@ -369,7 +369,7 @@ static void odm_Process_RSSIForDM( pEntry->rssi_stat.ValidBit++; for (i = 0; i < pEntry->rssi_stat.ValidBit; i++) - OFDM_pkt += (u8)(pEntry->rssi_stat.PacketMap>>i)&BIT0; + OFDM_pkt += (u8)(pEntry->rssi_stat.PacketMap>>i)&BIT(0); if (pEntry->rssi_stat.ValidBit == 64) { Weighting = ((OFDM_pkt<<4) > 64)?64:(OFDM_pkt<<4); diff --git a/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h b/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h index 3c71938899e0..6d7062a1cbbe 100644 --- a/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h +++ b/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h @@ -155,8 +155,8 @@ /* DIG Related */ #define ODM_BIT_IGI_11N 0x0000007F -#define ODM_BIT_CCK_RPT_FORMAT_11N BIT9 +#define ODM_BIT_CCK_RPT_FORMAT_11N BIT(9) #define ODM_BIT_BB_RX_PATH_11N 0xF -#define ODM_BIT_BB_ATC_11N BIT11 +#define ODM_BIT_BB_ATC_11N BIT(11) #endif diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index ebcd174336dc..2a748367fbba 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -267,7 +267,7 @@ void rtl8723b_FirmwareSelfReset(struct adapter *padapter) rtw_write8(padapter, REG_HMETFR+3, 0x20); val = rtw_read8(padapter, REG_SYS_FUNC_EN + 1); - while (val & BIT2) { + while (val & BIT(2)) { Delay--; if (Delay == 0) break; @@ -278,7 +278,7 @@ void rtl8723b_FirmwareSelfReset(struct adapter *padapter) if (Delay == 0) { /* force firmware reset */ val = rtw_read8(padapter, REG_SYS_FUNC_EN + 1); - rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val & (~BIT2)); + rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val & (~BIT(2))); } } } @@ -1004,9 +1004,9 @@ void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) void hal_notch_filter_8723b(struct adapter *adapter, bool enable) { if (enable) - rtw_write8(adapter, rOFDM0_RxDSP+1, rtw_read8(adapter, rOFDM0_RxDSP+1) | BIT1); + rtw_write8(adapter, rOFDM0_RxDSP+1, rtw_read8(adapter, rOFDM0_RxDSP+1) | BIT(1)); else - rtw_write8(adapter, rOFDM0_RxDSP+1, rtw_read8(adapter, rOFDM0_RxDSP+1) & ~BIT1); + rtw_write8(adapter, rOFDM0_RxDSP+1, rtw_read8(adapter, rOFDM0_RxDSP+1) & ~BIT(1)); } void UpdateHalRAMask8723B(struct adapter *padapter, u32 mac_id, u8 rssi_level) @@ -1261,7 +1261,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->BW20_Diff[rfPath][TxCount] = EEPROM_DEFAULT_24G_HT20_DIFF; else { pwrInfo24G->BW20_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4; - if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->BW20_Diff[rfPath][TxCount] |= 0xF0; } @@ -1269,7 +1269,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->OFDM_Diff[rfPath][TxCount] = EEPROM_DEFAULT_24G_OFDM_DIFF; else { pwrInfo24G->OFDM_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f); - if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->OFDM_Diff[rfPath][TxCount] |= 0xF0; } pwrInfo24G->CCK_Diff[rfPath][TxCount] = 0; @@ -1279,7 +1279,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->BW40_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF; else { pwrInfo24G->BW40_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4; - if (pwrInfo24G->BW40_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->BW40_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->BW40_Diff[rfPath][TxCount] |= 0xF0; } @@ -1287,7 +1287,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->BW20_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF; else { pwrInfo24G->BW20_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f); - if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->BW20_Diff[rfPath][TxCount] |= 0xF0; } eeAddr++; @@ -1296,7 +1296,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->OFDM_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF; else { pwrInfo24G->OFDM_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4; - if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->OFDM_Diff[rfPath][TxCount] |= 0xF0; } @@ -1304,7 +1304,7 @@ static void Hal_ReadPowerValueFromPROM_8723B( pwrInfo24G->CCK_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF; else { pwrInfo24G->CCK_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f); - if (pwrInfo24G->CCK_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */ + if (pwrInfo24G->CCK_Diff[rfPath][TxCount] & BIT(3)) /* 4bit sign number to 8 bit sign number */ pwrInfo24G->CCK_Diff[rfPath][TxCount] |= 0xF0; } eeAddr++; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 10eb96105f83..d6369eb7b570 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -124,9 +124,9 @@ static u32 phy_RFSerialRead_8723B( udelay(10); if (eRFPath == RF_PATH_A) - RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter1 | MaskforPhySet, BIT8); + RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XA_HSSIParameter1 | MaskforPhySet, BIT(8)); else if (eRFPath == RF_PATH_B) - RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter1 | MaskforPhySet, BIT8); + RfPiEnable = (u8)PHY_QueryBBReg(Adapter, rFPGA0_XB_HSSIParameter1 | MaskforPhySet, BIT(8)); if (RfPiEnable) { /* Read from BBreg8b8, 12 bits for 8190, 20bits for T65 RF */ @@ -374,7 +374,7 @@ int PHY_BBConfig8723B(struct adapter *Adapter) /* Enable BB and RF */ RegVal = rtw_read16(Adapter, REG_SYS_FUNC_EN); - rtw_write16(Adapter, REG_SYS_FUNC_EN, (u16)(RegVal | BIT13 | BIT0 | BIT1)); + rtw_write16(Adapter, REG_SYS_FUNC_EN, (u16)(RegVal | BIT(13) | BIT(0) | BIT(1))); rtw_write32(Adapter, 0x948, 0x280); /* Others use Antenna S1 */ @@ -574,7 +574,7 @@ static void phy_SetRegBW_8723B( break; case CHANNEL_WIDTH_40: - u2tmp = RegRfMod_BW | BIT7; + u2tmp = RegRfMod_BW | BIT(7); rtw_write16(Adapter, REG_TRXPTCL_CTL_8723B, (u2tmp & 0xFEFF)); /* BIT 7 = 1, BIT 8 = 0 */ break; @@ -620,7 +620,7 @@ static void phy_PostSetBwMode8723B(struct adapter *Adapter) PHY_SetBBReg(Adapter, rFPGA1_RFMOD, bRFMOD, 0x0); - PHY_SetBBReg(Adapter, rOFDM0_TxPseudoNoiseWgt, (BIT31 | BIT30), 0x0); + PHY_SetBBReg(Adapter, rOFDM0_TxPseudoNoiseWgt, (BIT(31) | BIT(30)), 0x0); break; /* 40 MHz channel*/ @@ -634,7 +634,7 @@ static void phy_PostSetBwMode8723B(struct adapter *Adapter) PHY_SetBBReg(Adapter, rOFDM1_LSTF, 0xC00, pHalData->nCur40MhzPrimeSC); - PHY_SetBBReg(Adapter, 0x818, (BIT26 | BIT27), (pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); + PHY_SetBBReg(Adapter, 0x818, (BIT(26) | BIT(27)), (pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); break; default: break; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c index ad0b2d36312e..5564f3f20ba5 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c @@ -63,13 +63,13 @@ void PHY_RF6052SetBandwidth8723B( switch (Bandwidth) { case CHANNEL_WIDTH_20: - pHalData->RfRegChnlVal[0] = ((pHalData->RfRegChnlVal[0] & 0xfffff3ff) | BIT10 | BIT11); + pHalData->RfRegChnlVal[0] = ((pHalData->RfRegChnlVal[0] & 0xfffff3ff) | BIT(10) | BIT(11)); PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); PHY_SetRFReg(Adapter, RF_PATH_B, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); break; case CHANNEL_WIDTH_40: - pHalData->RfRegChnlVal[0] = ((pHalData->RfRegChnlVal[0] & 0xfffff3ff) | BIT10); + pHalData->RfRegChnlVal[0] = ((pHalData->RfRegChnlVal[0] & 0xfffff3ff) | BIT(10)); PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); PHY_SetRFReg(Adapter, RF_PATH_B, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); break; diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index b801a78f69e0..1e0498268aee 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -564,7 +564,7 @@ static bool HalDetectPwrDownMode(struct adapter *Adapter) rtw_efuse_shadow_read(Adapter, 1, 0x7B/*EEPROM_RF_OPT3_92C*/, (u32 *)&tmpvalue); /* 2010/08/25 MH INF priority > PDN Efuse value. */ - if (tmpvalue & BIT4 && pwrctrlpriv->reg_pdnmode) + if (tmpvalue & BIT(4) && pwrctrlpriv->reg_pdnmode) pHalData->pwrdown = true; else pHalData->pwrdown = false; diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h index 30183d502b69..552d0c5fa47f 100644 --- a/drivers/staging/rtl8723bs/include/drv_types.h +++ b/drivers/staging/rtl8723bs/include/drv_types.h @@ -380,9 +380,9 @@ struct adapter { /* */ /* Function disabled. */ /* */ -#define DF_TX_BIT BIT0 -#define DF_RX_BIT BIT1 -#define DF_IO_BIT BIT2 +#define DF_TX_BIT BIT(0) +#define DF_RX_BIT BIT(1) +#define DF_IO_BIT BIT(2) /* define RTW_ENABLE_FUNC(padapter, func) (atomic_sub(&adapter_to_dvobj(padapter)->disable_func, (func))) */ diff --git a/drivers/staging/rtl8723bs/include/hal_com_reg.h b/drivers/staging/rtl8723bs/include/hal_com_reg.h index cf5c15dc2bfd..479748d85626 100644 --- a/drivers/staging/rtl8723bs/include/hal_com_reg.h +++ b/drivers/staging/rtl8723bs/include/hal_com_reg.h @@ -204,22 +204,22 @@ /* */ /* 8723/8188E Host System Interrupt Status Register (offset 0x5C, 32 byte) */ /* */ -#define HSISR_GPIO12_0_INT BIT0 -#define HSISR_SPS_OCP_INT BIT5 -#define HSISR_RON_INT BIT6 -#define HSISR_PDNINT BIT7 -#define HSISR_GPIO9_INT BIT25 +#define HSISR_GPIO12_0_INT BIT(0) +#define HSISR_SPS_OCP_INT BIT(5) +#define HSISR_RON_INT BIT(6) +#define HSISR_PDNINT BIT(7) +#define HSISR_GPIO9_INT BIT(25) /* */ /* Response Rate Set Register (offset 0x440, 24bits) */ /* */ -#define RRSR_1M BIT0 -#define RRSR_2M BIT1 -#define RRSR_5_5M BIT2 -#define RRSR_11M BIT3 -#define RRSR_6M BIT4 -#define RRSR_12M BIT6 -#define RRSR_24M BIT8 +#define RRSR_1M BIT(0) +#define RRSR_2M BIT(1) +#define RRSR_5_5M BIT(2) +#define RRSR_11M BIT(3) +#define RRSR_6M BIT(4) +#define RRSR_12M BIT(6) +#define RRSR_24M BIT(8) #define RRSR_CCK_RATES (RRSR_11M|RRSR_5_5M|RRSR_2M|RRSR_1M) @@ -250,12 +250,12 @@ /* */ /* BW_OPMODE bits (Offset 0x603, 8bit) */ /* */ -#define BW_OPMODE_20MHZ BIT2 +#define BW_OPMODE_20MHZ BIT(2) /* */ /* CAM Config Setting (offset 0x680, 1 byte) */ /* */ -#define CAM_VALID BIT15 +#define CAM_VALID BIT(15) #define CAM_CONTENT_COUNT 8 @@ -263,8 +263,8 @@ #define TOTAL_CAM_ENTRY 32 -#define CAM_WRITE BIT16 -#define CAM_POLLINIG BIT31 +#define CAM_WRITE BIT(16) +#define CAM_POLLINIG BIT(31) /* */ /* 12. Host Interrupt Status Registers */ @@ -273,20 +273,20 @@ /* */ /* 8192C (RCR) Receive Configuration Register (Offset 0x608, 32 bits) */ /* */ -#define RCR_APPFCS BIT31 /* WMAC append FCS after pauload */ -#define RCR_APP_MIC BIT30 /* MACRX will retain the MIC at the bottom of the packet. */ -#define RCR_APP_ICV BIT29 /* MACRX will retain the ICV at the bottom of the packet. */ -#define RCR_APP_PHYST_RXFF BIT28 /* PHY Status is appended before RX packet in RXFF */ -#define RCR_APP_BA_SSN BIT27 /* SSN of previous TXBA is appended as after original RXDESC as the 4-th DW of RXDESC. */ -#define RCR_HTC_LOC_CTRL BIT14 /* MFC<--HTC = 1 MFC-->HTC = 0 */ -#define RCR_AMF BIT13 /* Accept management type frame */ -#define RCR_ADF BIT11 /* Accept data type frame. This bit also regulates BA, BAR, and PS-Poll (AP mode only). */ -#define RCR_ACRC32 BIT8 /* Accept CRC32 error packet */ -#define RCR_CBSSID_BCN BIT7 /* Accept BSSID match packet (Rx beacon, probe rsp) */ -#define RCR_CBSSID_DATA BIT6 /* Accept BSSID match packet (Data) */ -#define RCR_AB BIT3 /* Accept broadcast packet */ -#define RCR_AM BIT2 /* Accept multicast packet */ -#define RCR_APM BIT1 /* Accept physical match packet */ +#define RCR_APPFCS BIT(31) /* WMAC append FCS after pauload */ +#define RCR_APP_MIC BIT(30) /* MACRX will retain the MIC at the bottom of the packet. */ +#define RCR_APP_ICV BIT(29) /* MACRX will retain the ICV at the bottom of the packet. */ +#define RCR_APP_PHYST_RXFF BIT(28) /* PHY Status is appended before RX packet in RXFF */ +#define RCR_APP_BA_SSN BIT(27) /* SSN of previous TXBA is appended as after original RXDESC as the 4-th DW of RXDESC. */ +#define RCR_HTC_LOC_CTRL BIT(14) /* MFC<--HTC = 1 MFC-->HTC = 0 */ +#define RCR_AMF BIT(13) /* Accept management type frame */ +#define RCR_ADF BIT(11) /* Accept data type frame. This bit also regulates BA, BAR, and PS-Poll (AP mode only). */ +#define RCR_ACRC32 BIT(8) /* Accept CRC32 error packet */ +#define RCR_CBSSID_BCN BIT(7) /* Accept BSSID match packet (Rx beacon, probe rsp) */ +#define RCR_CBSSID_DATA BIT(6) /* Accept BSSID match packet (Data) */ +#define RCR_AB BIT(3) /* Accept broadcast packet */ +#define RCR_AM BIT(2) /* Accept multicast packet */ +#define RCR_APM BIT(1) /* Accept physical match packet */ /* */ @@ -540,26 +540,26 @@ #define SDIO_HIMR_DISABLED 0 /* RTL8723/RTL8188E SDIO Host Interrupt Mask Register */ -#define SDIO_HIMR_RX_REQUEST_MSK BIT0 -#define SDIO_HIMR_AVAL_MSK BIT1 +#define SDIO_HIMR_RX_REQUEST_MSK BIT(0) +#define SDIO_HIMR_AVAL_MSK BIT(1) /* SDIO Host Interrupt Service Routine */ -#define SDIO_HISR_RX_REQUEST BIT0 -#define SDIO_HISR_AVAL BIT1 -#define SDIO_HISR_TXERR BIT2 -#define SDIO_HISR_RXERR BIT3 -#define SDIO_HISR_TXFOVW BIT4 -#define SDIO_HISR_RXFOVW BIT5 -#define SDIO_HISR_TXBCNOK BIT6 -#define SDIO_HISR_TXBCNERR BIT7 -#define SDIO_HISR_C2HCMD BIT17 -#define SDIO_HISR_CPWM1 BIT18 -#define SDIO_HISR_CPWM2 BIT19 -#define SDIO_HISR_HSISR_IND BIT20 -#define SDIO_HISR_GTINT3_IND BIT21 -#define SDIO_HISR_GTINT4_IND BIT22 -#define SDIO_HISR_PSTIMEOUT BIT23 -#define SDIO_HISR_OCPINT BIT24 +#define SDIO_HISR_RX_REQUEST BIT(0) +#define SDIO_HISR_AVAL BIT(1) +#define SDIO_HISR_TXERR BIT(2) +#define SDIO_HISR_RXERR BIT(3) +#define SDIO_HISR_TXFOVW BIT(4) +#define SDIO_HISR_RXFOVW BIT(5) +#define SDIO_HISR_TXBCNOK BIT(6) +#define SDIO_HISR_TXBCNERR BIT(7) +#define SDIO_HISR_C2HCMD BIT(17) +#define SDIO_HISR_CPWM1 BIT(18) +#define SDIO_HISR_CPWM2 BIT(19) +#define SDIO_HISR_HSISR_IND BIT(20) +#define SDIO_HISR_GTINT3_IND BIT(21) +#define SDIO_HISR_GTINT4_IND BIT(22) +#define SDIO_HISR_PSTIMEOUT BIT(23) +#define SDIO_HISR_OCPINT BIT(24) #define MASK_SDIO_HISR_CLEAR (SDIO_HISR_TXERR |\ SDIO_HISR_RXERR |\ @@ -590,9 +590,9 @@ #define C2H_EVT_FW_CLOSE 0xFF /* Set by FW indicating that FW had set the C2H command message and it's not yet read by driver. */ /* 2REG_MULTI_FUNC_CTRL(For RTL8723 Only) */ -#define WL_HWPDN_SL BIT1 /* WiFi HW PDn polarity control */ -#define WL_FUNC_EN BIT2 /* WiFi function enable */ -#define BT_FUNC_EN BIT18 /* BT function enable */ -#define GPS_FUNC_EN BIT22 /* GPS function enable */ +#define WL_HWPDN_SL BIT(1) /* WiFi HW PDn polarity control */ +#define WL_FUNC_EN BIT(2) /* WiFi function enable */ +#define BT_FUNC_EN BIT(18) /* BT function enable */ +#define GPS_FUNC_EN BIT(22) /* GPS function enable */ #endif /* __HAL_COMMON_H__ */ diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index 876b3666aa1c..bc9d8c945f38 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -77,7 +77,7 @@ enum { struct dm_priv { u8 DM_Type; -#define DYNAMIC_FUNC_BT BIT0 +#define DYNAMIC_FUNC_BT BIT(0) u8 DMFlag; u8 InitDMFlag; diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 1d62ea71a890..9b000681ff80 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -9,10 +9,10 @@ enum { - RTW_PCIE = BIT0, - RTW_USB = BIT1, - RTW_SDIO = BIT2, - RTW_GSPI = BIT3, + RTW_PCIE = BIT(0), + RTW_USB = BIT(1), + RTW_SDIO = BIT(2), + RTW_GSPI = BIT(3), }; enum { @@ -161,10 +161,10 @@ enum hal_intf_ps_func { typedef s32 (*c2h_id_filter)(u8 *c2h_evt); #define RF_CHANGE_BY_INIT 0 -#define RF_CHANGE_BY_IPS BIT28 -#define RF_CHANGE_BY_PS BIT29 -#define RF_CHANGE_BY_HW BIT30 -#define RF_CHANGE_BY_SW BIT31 +#define RF_CHANGE_BY_IPS BIT(28) +#define RF_CHANGE_BY_PS BIT(29) +#define RF_CHANGE_BY_HW BIT(30) +#define RF_CHANGE_BY_SW BIT(31) #define GET_EEPROM_EFUSE_PRIV(adapter) (&adapter->eeprompriv) #define is_boot_from_eeprom(adapter) (adapter->eeprompriv.EepromOrEfuse) diff --git a/drivers/staging/rtl8723bs/include/hal_phy.h b/drivers/staging/rtl8723bs/include/hal_phy.h index 3d71a4f41592..abc0f27fdaa4 100644 --- a/drivers/staging/rtl8723bs/include/hal_phy.h +++ b/drivers/staging/rtl8723bs/include/hal_phy.h @@ -10,8 +10,8 @@ /* Antenna detection method, i.e., using single tone detection or RSSI reported from each antenna detected. */ /* Added by Roger, 2013.05.22. */ /* */ -#define ANT_DETECT_BY_SINGLE_TONE BIT0 -#define ANT_DETECT_BY_RSSI BIT1 +#define ANT_DETECT_BY_SINGLE_TONE BIT(0) +#define ANT_DETECT_BY_RSSI BIT(1) #define IS_ANT_DETECT_SUPPORT_SINGLE_TONE(__Adapter) ((GET_HAL_DATA(__Adapter)->AntDetection) & ANT_DETECT_BY_SINGLE_TONE) #define IS_ANT_DETECT_SUPPORT_RSSI(__Adapter) ((GET_HAL_DATA(__Adapter)->AntDetection) & ANT_DETECT_BY_RSSI) diff --git a/drivers/staging/rtl8723bs/include/hal_pwr_seq.h b/drivers/staging/rtl8723bs/include/hal_pwr_seq.h index 48bf7f66a06e..9639eef8a51d 100644 --- a/drivers/staging/rtl8723bs/include/hal_pwr_seq.h +++ b/drivers/staging/rtl8723bs/include/hal_pwr_seq.h @@ -39,92 +39,92 @@ #define RTL8723B_TRANS_CARDEMU_TO_ACT \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0}, /*0x20[0] = 1b'1 enable LDOA12 MACRO block for all interface*/ \ - {0x0067, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, /*0x67[0] = 0 to disable BT_GPS_SEL pins*/ \ + {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*0x20[0] = 1b'1 enable LDOA12 MACRO block for all interface*/ \ + {0x0067, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*0x67[0] = 0 to disable BT_GPS_SEL pins*/ \ {0x0001, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 1, PWRSEQ_DELAY_MS},/*Delay 1ms*/ \ - {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, 0}, /*0x00[5] = 1b'0 release analog Ips to digital , 1:isolation*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT4|BIT3|BIT2), 0},/* disable SW LPS 0x04[10]= 0 and WLSUS_EN 0x04[11]= 0*/ \ - {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* Disable USB suspend */ \ - {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, BIT1},/* wait till 0x04[17] = 1 power ready*/ \ - {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/* Enable USB suspend */ \ - {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* release WLON reset 0x04[16]= 1*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, 0},/* disable HWPDN 0x04[15]= 0*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT4|BIT3), 0},/* disable WL suspend*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* polling until return 0*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT0, 0},/**/ \ - {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT6, BIT6},/* Enable WL control XTAL setting*/ \ - {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1},/*Enable falling edge triggering interrupt*/\ - {0x0063, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1},/*Enable GPIO9 interrupt mode*/\ - {0x0062, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*Enable GPIO9 input mode*/\ - {0x0058, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*Enable HSISR GPIO[C:0] interrupt*/\ - {0x005A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1},/*Enable HSISR GPIO9 interrupt*/\ - {0x0068, PWR_CUT_TESTCHIP_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3, BIT3},/*For GPIO9 internal pull high setting by test chip*/\ - {0x0069, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT6, BIT6},/*For GPIO9 internal pull high setting*/\ + {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), 0}, /*0x00[5] = 1b'0 release analog Ips to digital , 1:isolation*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)|BIT(2)), 0},/* disable SW LPS 0x04[10]= 0 and WLSUS_EN 0x04[11]= 0*/ \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* Disable USB suspend */ \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)},/* wait till 0x04[17] = 1 power ready*/ \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), (0)},/* Enable USB suspend */ \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* release WLON reset 0x04[16]= 1*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/* disable HWPDN 0x04[15]= 0*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)), 0},/* disable WL suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* polling until return 0*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0},/**/ \ + {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)},/* Enable WL control XTAL setting*/ \ + {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},/*Enable falling edge triggering interrupt*/\ + {0x0063, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},/*Enable GPIO9 interrupt mode*/\ + {0x0062, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*Enable GPIO9 input mode*/\ + {0x0058, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*Enable HSISR GPIO[C:0] interrupt*/\ + {0x005A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},/*Enable HSISR GPIO9 interrupt*/\ + {0x0068, PWR_CUT_TESTCHIP_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3), BIT(3)},/*For GPIO9 internal pull high setting by test chip*/\ + {0x0069, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)},/*For GPIO9 internal pull high setting*/\ #define RTL8723B_TRANS_ACT_TO_CARDEMU \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},/*0x1F[7:0] = 0 turn off RF*/ \ - {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*Enable rising edge triggering interrupt*/ \ - {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* release WLON reset 0x04[16]= 1*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1}, /*0x04[9] = 1 turn off MAC by HW state machine*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, 0}, /*wait till 0x04[9] = 0 polling until return 0 to disable*/ \ - {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT6, 0},/* Enable BT control XTAL setting*/\ - {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, BIT5}, /*0x00[5] = 1b'1 analog Ips to digital , 1:isolation*/ \ - {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0}, /*0x20[0] = 1b'0 disable LDOA12 MACRO block*/\ + {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*Enable rising edge triggering interrupt*/ \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* release WLON reset 0x04[16]= 1*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, /*0x04[9] = 1 turn off MAC by HW state machine*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0}, /*wait till 0x04[9] = 0 polling until return 0 to disable*/ \ + {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), 0},/* Enable BT control XTAL setting*/\ + {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)}, /*0x00[5] = 1b'1 analog Ips to digital , 1:isolation*/ \ + {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, /*0x20[0] = 1b'0 disable LDOA12 MACRO block*/\ #define RTL8723B_TRANS_CARDEMU_TO_SUS \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4|BIT3, (BIT4|BIT3)}, /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, BIT3}, /*0x04[12:11] = 2b'01 enable WL suspend*/ \ - {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4)|BIT(3), (BIT(4)|BIT(3))}, /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, /*0x04[12:11] = 2b'01 enable WL suspend*/ \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, /*0x07[7:0] = 0x20 SDIO SOP option to disable BG/MB/ACK/SWR*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, BIT3|BIT4}, /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, BIT0}, /*Set SDIO suspend local register*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, 0}, /*wait power state to suspend*/ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)|BIT(4)}, /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0}, /*wait power state to suspend*/ #define RTL8723B_TRANS_SUS_TO_CARDEMU \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3 | BIT7, 0}, /*clear suspend enable and power down enable*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, 0}, /*Set SDIO suspend local register*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, BIT1}, /*wait power state to suspend*/\ - {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, /*0x23[4] = 1b'0 12H LDO enter normal mode*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0}, /*0x04[12:11] = 2b'01enable WL suspend*/ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, /*clear suspend enable and power down enable*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, /*wait power state to suspend*/\ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*0x23[4] = 1b'0 12H LDO enter normal mode*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, /*0x04[12:11] = 2b'01enable WL suspend*/ #define RTL8723B_TRANS_CARDEMU_TO_CARDDIS \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, omments here*/ \ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, /*0x07 = 0x20 , SOP option to disable BG/MB*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, BIT3}, /*0x04[12:11] = 2b'01 enable WL suspend*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT2, BIT2}, /*0x04[10] = 1, enable SW LPS*/ \ - {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 1}, /*0x48[16] = 1 to enable GPIO9 as EXT WAKEUP*/ \ - {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, BIT0}, /*Set SDIO suspend local register*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, 0}, /*wait power state to suspend*/ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, /*0x04[12:11] = 2b'01 enable WL suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), BIT(2)}, /*0x04[10] = 1, enable SW LPS*/ \ + {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1}, /*0x48[16] = 1 to enable GPIO9 as EXT WAKEUP*/ \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0}, /*wait power state to suspend*/ #define RTL8723B_TRANS_CARDDIS_TO_CARDEMU \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3 | BIT7, 0}, /*clear suspend enable and power down enable*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, 0}, /*Set SDIO suspend local register*/ \ - {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, BIT1}, /*wait power state to suspend*/\ - {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0}, /*0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/ \ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0}, /*0x04[12:11] = 2b'01enable WL suspend*/\ - {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, /*0x23[4] = 1b'0 12H LDO enter normal mode*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, /*clear suspend enable and power down enable*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, /*wait power state to suspend*/\ + {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, /*0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, /*0x04[12:11] = 2b'01enable WL suspend*/\ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*0x23[4] = 1b'0 12H LDO enter normal mode*/ \ {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},/*PCIe DMA start*/ #define RTL8723B_TRANS_CARDEMU_TO_PDN \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, /*0x23[4] = 1b'1 12H LDO enter sleep mode*/ \ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK|PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, /*0x07[7:0] = 0x20 SOP option to disable BG/MB/ACK/SWR*/ \ - {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/* 0x04[16] = 0*/\ - {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, BIT7},/* 0x04[15] = 1*/ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/* 0x04[16] = 0*/\ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},/* 0x04[15] = 1*/ #define RTL8723B_TRANS_ACT_TO_LPS \ /* format */ \ @@ -135,13 +135,13 @@ {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \ {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \ {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*CCK and OFDM are disabled, and clock are gated*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*CCK and OFDM are disabled, and clock are gated*/ \ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US},/*Delay 1us*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*Whole BB is reset*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*Whole BB is reset*/ \ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x03},/*Reset MAC TRX*/ \ - {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*check if removed later*/ \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*check if removed later*/ \ {0x0093, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x00},/*When driver enter Sus/ Disable, enable LOP for BT*/ \ - {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, BIT5},/*Respond TxOK to scheduler*/ \ + {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)},/*Respond TxOK to scheduler*/ \ #define RTL8723B_TRANS_LPS_TO_ACT \ @@ -151,59 +151,59 @@ {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*USB RPWM*/\ {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*PCIe RPWM*/\ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, /*Delay*/\ - {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, /*. 0x08[4] = 0 switch TSF to 40M*/\ - {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT7, 0}, /*Polling 0x109[7]= 0 TSF in 40M*/\ - {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT6|BIT7, 0}, /*. 0x29[7:6] = 2b'00 enable BB clock*/\ - {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1}, /*. 0x101[1] = 1*/\ + {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*. 0x08[4] = 0 switch TSF to 40M*/\ + {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0}, /*Polling 0x109[7]= 0 TSF in 40M*/\ + {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0}, /*. 0x29[7:6] = 2b'00 enable BB clock*/\ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, /*. 0x101[1] = 1*/\ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, /*. 0x100[7:0] = 0xFF enable WMAC TRX*/\ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1|BIT0, BIT1|BIT0}, /*. 0x02[1:0] = 2b'11 enable BB macro*/\ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1)|BIT(0), BIT(1)|BIT(0)}, /*. 0x02[1:0] = 2b'11 enable BB macro*/\ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, /*. 0x522 = 0*/ #define RTL8723B_TRANS_ACT_TO_SWLPS \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0194, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*enable 32 K source*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*CCK and OFDM are disabled, and clock are gated*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 1},/*CCK and OFDM are enable*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*CCK and OFDM are disabled, and clock are gated*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 1},/*CCK and OFDM are enable*/ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*CCK and OFDM are disabled, and clock are gated*/ \ + {0x0194, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*enable 32 K source*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*CCK and OFDM are disabled, and clock are gated*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1},/*CCK and OFDM are enable*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*CCK and OFDM are disabled, and clock are gated*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1},/*CCK and OFDM are enable*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*CCK and OFDM are disabled, and clock are gated*/ \ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x3F},/*Reset MAC TRX*/ \ - {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*disable security engine*/ \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*disable security engine*/ \ {0x0093, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x40},/*When driver enter Sus/ Disable, enable LOP for BT*/ \ - {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, BIT5},/*reset dual TSF*/ \ - {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT2, 0},/*Reset CPU*/ \ + {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)},/*reset dual TSF*/ \ + {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), 0},/*Reset CPU*/ \ {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},/*Reset MCUFWDL register*/ \ - {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*Reset CPU IO Wrapper*/ \ - {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 1},/*Reset CPU IO Wrapper*/ \ + {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*Reset CPU IO Wrapper*/ \ + {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1},/*Reset CPU IO Wrapper*/ \ {0x0287, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*polling RXFF packet number = 0 */ \ - {0x0286, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, BIT1},/*polling RXDMA idle */ \ - {0x013D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*Clear FW RPWM interrupt */\ - {0x0139, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*Set FW RPWM interrupt source*/\ - {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4},/*switch TSF to 32K*/\ - {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, BIT7},/*polling TSF stable*/\ - {0x0090, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*Set FW LPS*/ \ - {0x0090, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT0, 0},/*polling FW LPS ready */ + {0x0286, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)},/*polling RXDMA idle */ \ + {0x013D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*Clear FW RPWM interrupt */\ + {0x0139, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*Set FW RPWM interrupt source*/\ + {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)},/*switch TSF to 32K*/\ + {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},/*polling TSF stable*/\ + {0x0090, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*Set FW LPS*/ \ + {0x0090, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0},/*polling FW LPS ready */ #define RTL8723B_TRANS_SWLPS_TO_ACT \ /* format */ \ /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here*/ \ - {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0},/*switch TSF to 32K*/\ - {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, 0},/*polling TSF stable*/\ - {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1}, /*. 0x101[1] = 1, enable security engine*/\ + {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0},/*switch TSF to 32K*/\ + {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/*polling TSF stable*/\ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, /*. 0x101[1] = 1, enable security engine*/\ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, /*. 0x100[7:0] = 0xFF enable WMAC TRX*/\ {0x06B7, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x09}, /*. reset MAC rx state machine*/\ {0x06B4, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x86}, /*. reset MAC rx state machine*/\ - {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1},/* set CPU RAM code ready*/ \ - {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/*Reset CPU IO Wrapper*/ \ - {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT2, 0},/* Enable CPU*/ \ - {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/*enable CPU IO Wrapper*/ \ - {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT2, BIT2},/* Enable CPU*/ \ - {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT7, BIT7},/*polling FW init ready */ \ - {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT6, BIT6},/*polling FW init ready */ \ - {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0}, /*. 0x02[1:0] = 2b'11 enable BB macro*/\ + {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)},/* set CPU RAM code ready*/ \ + {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*Reset CPU IO Wrapper*/ \ + {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), 0},/* Enable CPU*/ \ + {0x001D, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/*enable CPU IO Wrapper*/ \ + {0x0003, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), BIT(2)},/* Enable CPU*/ \ + {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), BIT(7)},/*polling FW init ready */ \ + {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(6), BIT(6)},/*polling FW init ready */ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*. 0x02[1:0] = 2b'11 enable BB macro*/\ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, /*. 0x522 = 0*/ #define RTL8723B_TRANS_END \ diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index 86b321e2081d..bd72edcb4259 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -14,39 +14,7 @@ #include -#define BIT0 0x00000001 -#define BIT1 0x00000002 #define BIT2 0x00000004 -#define BIT3 0x00000008 -#define BIT4 0x00000010 -#define BIT5 0x00000020 -#define BIT6 0x00000040 -#define BIT7 0x00000080 -#define BIT8 0x00000100 -#define BIT9 0x00000200 -#define BIT10 0x00000400 -#define BIT11 0x00000800 -#define BIT12 0x00001000 -#define BIT13 0x00002000 -#define BIT14 0x00004000 -#define BIT15 0x00008000 -#define BIT16 0x00010000 -#define BIT17 0x00020000 -#define BIT18 0x00040000 -#define BIT19 0x00080000 -#define BIT20 0x00100000 -#define BIT21 0x00200000 -#define BIT22 0x00400000 -#define BIT23 0x00800000 -#define BIT24 0x01000000 -#define BIT25 0x02000000 -#define BIT26 0x04000000 -#define BIT27 0x08000000 -#define BIT28 0x10000000 -#define BIT29 0x20000000 -#define BIT30 0x40000000 -#define BIT31 0x80000000 -#define BIT32 0x0100000000 extern int RTW_STATUS_CODE(int error_code); diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_spec.h b/drivers/staging/rtl8723bs/include/rtl8723b_spec.h index 6816040a6aff..7f749e32a5b9 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_spec.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_spec.h @@ -167,7 +167,7 @@ /* */ /* 8723B REG_CCK_CHECK (offset 0x454) */ /* */ -#define BIT_BCN_PORT_SEL BIT5 +#define BIT_BCN_PORT_SEL BIT(5) /* */ /* */ @@ -188,50 +188,50 @@ /* */ #define IMR_DISABLED_8723B 0 /* IMR DW0(0x00B0-00B3) Bit 0-31 */ -#define IMR_TIMER2_8723B BIT31 /* Timeout interrupt 2 */ -#define IMR_TIMER1_8723B BIT30 /* Timeout interrupt 1 */ -#define IMR_PSTIMEOUT_8723B BIT29 /* Power Save Time Out Interrupt */ -#define IMR_GTINT4_8723B BIT28 /* When GTIMER4 expires, this bit is set to 1 */ -#define IMR_GTINT3_8723B BIT27 /* When GTIMER3 expires, this bit is set to 1 */ -#define IMR_TXBCN0ERR_8723B BIT26 /* Transmit Beacon0 Error */ -#define IMR_TXBCN0OK_8723B BIT25 /* Transmit Beacon0 OK */ -#define IMR_TSF_BIT32_TOGGLE_8723B BIT24 /* TSF Timer BIT32 toggle indication interrupt */ -#define IMR_BCNDMAINT0_8723B BIT20 /* Beacon DMA Interrupt 0 */ -#define IMR_BCNDERR0_8723B BIT16 /* Beacon Queue DMA OK0 */ -#define IMR_HSISR_IND_ON_INT_8723B BIT15 /* HSISR Indicator (HSIMR & HSISR is true, this bit is set to 1) */ -#define IMR_BCNDMAINT_E_8723B BIT14 /* Beacon DMA Interrupt Extension for Win7 */ -#define IMR_ATIMEND_8723B BIT12 /* CTWidnow End or ATIM Window End */ -#define IMR_C2HCMD_8723B BIT10 /* CPU to Host Command INT Status, Write 1 clear */ -#define IMR_CPWM2_8723B BIT9 /* CPU power Mode exchange INT Status, Write 1 clear */ -#define IMR_CPWM_8723B BIT8 /* CPU power Mode exchange INT Status, Write 1 clear */ -#define IMR_HIGHDOK_8723B BIT7 /* High Queue DMA OK */ -#define IMR_MGNTDOK_8723B BIT6 /* Management Queue DMA OK */ -#define IMR_BKDOK_8723B BIT5 /* AC_BK DMA OK */ -#define IMR_BEDOK_8723B BIT4 /* AC_BE DMA OK */ -#define IMR_VIDOK_8723B BIT3 /* AC_VI DMA OK */ -#define IMR_VODOK_8723B BIT2 /* AC_VO DMA OK */ -#define IMR_RDU_8723B BIT1 /* Rx Descriptor Unavailable */ -#define IMR_ROK_8723B BIT0 /* Receive DMA OK */ +#define IMR_TIMER2_8723B BIT(31) /* Timeout interrupt 2 */ +#define IMR_TIMER1_8723B BIT(30) /* Timeout interrupt 1 */ +#define IMR_PSTIMEOUT_8723B BIT(29) /* Power Save Time Out Interrupt */ +#define IMR_GTINT4_8723B BIT(28) /* When GTIMER4 expires, this bit is set to 1 */ +#define IMR_GTINT3_8723B BIT(27) /* When GTIMER3 expires, this bit is set to 1 */ +#define IMR_TXBCN0ERR_8723B BIT(26) /* Transmit Beacon0 Error */ +#define IMR_TXBCN0OK_8723B BIT(25) /* Transmit Beacon0 OK */ +#define IMR_TSF_BIT32_TOGGLE_8723B BIT(24) /* TSF Timer BIT32 toggle indication interrupt */ +#define IMR_BCNDMAINT0_8723B BIT(20) /* Beacon DMA Interrupt 0 */ +#define IMR_BCNDERR0_8723B BIT(16) /* Beacon Queue DMA OK0 */ +#define IMR_HSISR_IND_ON_INT_8723B BIT(15) /* HSISR Indicator (HSIMR & HSISR is true, this bit is set to 1) */ +#define IMR_BCNDMAINT_E_8723B BIT(14) /* Beacon DMA Interrupt Extension for Win7 */ +#define IMR_ATIMEND_8723B BIT(12) /* CTWidnow End or ATIM Window End */ +#define IMR_C2HCMD_8723B BIT(10) /* CPU to Host Command INT Status, Write 1 clear */ +#define IMR_CPWM2_8723B BIT(9) /* CPU power Mode exchange INT Status, Write 1 clear */ +#define IMR_CPWM_8723B BIT(8) /* CPU power Mode exchange INT Status, Write 1 clear */ +#define IMR_HIGHDOK_8723B BIT(7) /* High Queue DMA OK */ +#define IMR_MGNTDOK_8723B BIT(6) /* Management Queue DMA OK */ +#define IMR_BKDOK_8723B BIT(5) /* AC_BK DMA OK */ +#define IMR_BEDOK_8723B BIT(4) /* AC_BE DMA OK */ +#define IMR_VIDOK_8723B BIT(3) /* AC_VI DMA OK */ +#define IMR_VODOK_8723B BIT(2) /* AC_VO DMA OK */ +#define IMR_RDU_8723B BIT(1) /* Rx Descriptor Unavailable */ +#define IMR_ROK_8723B BIT(0) /* Receive DMA OK */ /* IMR DW1(0x00B4-00B7) Bit 0-31 */ -#define IMR_BCNDMAINT7_8723B BIT27 /* Beacon DMA Interrupt 7 */ -#define IMR_BCNDMAINT6_8723B BIT26 /* Beacon DMA Interrupt 6 */ -#define IMR_BCNDMAINT5_8723B BIT25 /* Beacon DMA Interrupt 5 */ -#define IMR_BCNDMAINT4_8723B BIT24 /* Beacon DMA Interrupt 4 */ -#define IMR_BCNDMAINT3_8723B BIT23 /* Beacon DMA Interrupt 3 */ -#define IMR_BCNDMAINT2_8723B BIT22 /* Beacon DMA Interrupt 2 */ -#define IMR_BCNDMAINT1_8723B BIT21 /* Beacon DMA Interrupt 1 */ -#define IMR_BCNDOK7_8723B BIT20 /* Beacon Queue DMA OK Interrupt 7 */ -#define IMR_BCNDOK6_8723B BIT19 /* Beacon Queue DMA OK Interrupt 6 */ -#define IMR_BCNDOK5_8723B BIT18 /* Beacon Queue DMA OK Interrupt 5 */ -#define IMR_BCNDOK4_8723B BIT17 /* Beacon Queue DMA OK Interrupt 4 */ -#define IMR_BCNDOK3_8723B BIT16 /* Beacon Queue DMA OK Interrupt 3 */ -#define IMR_BCNDOK2_8723B BIT15 /* Beacon Queue DMA OK Interrupt 2 */ -#define IMR_BCNDOK1_8723B BIT14 /* Beacon Queue DMA OK Interrupt 1 */ -#define IMR_ATIMEND_E_8723B BIT13 /* ATIM Window End Extension for Win7 */ -#define IMR_TXERR_8723B BIT11 /* Tx Error Flag Interrupt Status, write 1 clear. */ -#define IMR_RXERR_8723B BIT10 /* Rx Error Flag INT Status, Write 1 clear */ -#define IMR_TXFOVW_8723B BIT9 /* Transmit FIFO Overflow */ -#define IMR_RXFOVW_8723B BIT8 /* Receive FIFO Overflow */ +#define IMR_BCNDMAINT7_8723B BIT(27) /* Beacon DMA Interrupt 7 */ +#define IMR_BCNDMAINT6_8723B BIT(26) /* Beacon DMA Interrupt 6 */ +#define IMR_BCNDMAINT5_8723B BIT(25) /* Beacon DMA Interrupt 5 */ +#define IMR_BCNDMAINT4_8723B BIT(24) /* Beacon DMA Interrupt 4 */ +#define IMR_BCNDMAINT3_8723B BIT(23) /* Beacon DMA Interrupt 3 */ +#define IMR_BCNDMAINT2_8723B BIT(22) /* Beacon DMA Interrupt 2 */ +#define IMR_BCNDMAINT1_8723B BIT(21) /* Beacon DMA Interrupt 1 */ +#define IMR_BCNDOK7_8723B BIT(20) /* Beacon Queue DMA OK Interrupt 7 */ +#define IMR_BCNDOK6_8723B BIT(19) /* Beacon Queue DMA OK Interrupt 6 */ +#define IMR_BCNDOK5_8723B BIT(18) /* Beacon Queue DMA OK Interrupt 5 */ +#define IMR_BCNDOK4_8723B BIT(17) /* Beacon Queue DMA OK Interrupt 4 */ +#define IMR_BCNDOK3_8723B BIT(16) /* Beacon Queue DMA OK Interrupt 3 */ +#define IMR_BCNDOK2_8723B BIT(15) /* Beacon Queue DMA OK Interrupt 2 */ +#define IMR_BCNDOK1_8723B BIT(14) /* Beacon Queue DMA OK Interrupt 1 */ +#define IMR_ATIMEND_E_8723B BIT(13) /* ATIM Window End Extension for Win7 */ +#define IMR_TXERR_8723B BIT(11) /* Tx Error Flag Interrupt Status, write 1 clear. */ +#define IMR_RXERR_8723B BIT(10) /* Rx Error Flag INT Status, Write 1 clear */ +#define IMR_TXFOVW_8723B BIT(9) /* Transmit FIFO Overflow */ +#define IMR_RXFOVW_8723B BIT(8) /* Receive FIFO Overflow */ #endif diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h index 3014fcf7bb85..f78aa0d7abc5 100644 --- a/drivers/staging/rtl8723bs/include/rtw_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h @@ -33,8 +33,8 @@ /* cmd flags */ enum { - RTW_CMDF_DIRECTLY = BIT0, - RTW_CMDF_WAIT_ACK = BIT1, + RTW_CMDF_DIRECTLY = BIT(0), + RTW_CMDF_WAIT_ACK = BIT(1), }; struct cmd_priv { diff --git a/drivers/staging/rtl8723bs/include/rtw_ht.h b/drivers/staging/rtl8723bs/include/rtw_ht.h index 54fadb92334c..da3efba7112a 100644 --- a/drivers/staging/rtl8723bs/include/rtw_ht.h +++ b/drivers/staging/rtl8723bs/include/rtw_ht.h @@ -61,16 +61,16 @@ enum { RT_HT_CAP_USE_JAGUAR_CCUT = 0x04, }; -#define LDPC_HT_ENABLE_RX BIT0 -#define LDPC_HT_ENABLE_TX BIT1 -#define LDPC_HT_CAP_TX BIT3 +#define LDPC_HT_ENABLE_RX BIT(0) +#define LDPC_HT_ENABLE_TX BIT(1) +#define LDPC_HT_CAP_TX BIT(3) -#define STBC_HT_ENABLE_RX BIT0 -#define STBC_HT_ENABLE_TX BIT1 -#define STBC_HT_CAP_TX BIT3 +#define STBC_HT_ENABLE_RX BIT(0) +#define STBC_HT_ENABLE_TX BIT(1) +#define STBC_HT_CAP_TX BIT(3) -#define BEAMFORMING_HT_BEAMFORMER_ENABLE BIT0 /* Declare our NIC supports beamformer */ -#define BEAMFORMING_HT_BEAMFORMEE_ENABLE BIT1 /* Declare our NIC supports beamformee */ +#define BEAMFORMING_HT_BEAMFORMER_ENABLE BIT(0) /* Declare our NIC supports beamformer */ +#define BEAMFORMING_HT_BEAMFORMEE_ENABLE BIT(1) /* Declare our NIC supports beamformee */ /* 20/40 BSS Coexist */ #define SET_EXT_CAPABILITY_ELE_BSS_COEXIST(_pEleStart, _val) SET_BITS_TO_LE_1BYTE((_pEleStart), 0, 1, _val) diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme.h b/drivers/staging/rtl8723bs/include/rtw_mlme.h index ac3ba746b64c..8cc96164e1cf 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme.h @@ -111,9 +111,9 @@ struct rt_link_detect_t { /* used for mlme_priv.roam_flags */ enum { - RTW_ROAM_ON_EXPIRED = BIT0, - RTW_ROAM_ON_RESUME = BIT1, - RTW_ROAM_ACTIVE = BIT2, + RTW_ROAM_ON_EXPIRED = BIT(0), + RTW_ROAM_ON_RESUME = BIT(1), + RTW_ROAM_ACTIVE = BIT(2), }; struct mlme_priv { diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index d64e533a63c0..a3c4501c2242 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -27,12 +27,12 @@ /* ====== ODM_ABILITY_E ======== */ /* BB ODM section BIT 0-15 */ -#define DYNAMIC_BB_DIG BIT0 /* ODM_BB_DIG */ +#define DYNAMIC_BB_DIG BIT(0) /* ODM_BB_DIG */ #define DYNAMIC_BB_DYNAMIC_TXPWR BIT2 /* ODM_BB_DYNAMIC_TXPWR */ -#define DYNAMIC_BB_ANT_DIV BIT6 /* ODM_BB_ANT_DIV */ +#define DYNAMIC_BB_ANT_DIV BIT(6) /* ODM_BB_ANT_DIV */ /* RF ODM section BIT 24-31 */ -#define DYNAMIC_RF_CALIBRATION BIT26/* ODM_RF_CALIBRATION */ +#define DYNAMIC_RF_CALIBRATION BIT(26)/* ODM_RF_CALIBRATION */ #define DYNAMIC_ALL_FUNC_ENABLE 0xFFFFFFF diff --git a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h index c27d07861b8c..67dc74512e8f 100644 --- a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h +++ b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h @@ -86,8 +86,8 @@ enum rt_rf_power_state { /* ASPM OSC Control bit, added by Roger, 2013.03.29. */ #define RT_PCI_ASPM_OSC_IGNORE 0 /* PCI ASPM ignore OSC control in default */ -#define RT_PCI_ASPM_OSC_ENABLE BIT0 /* PCI ASPM controlled by OS according to ACPI Spec 5.0 */ -#define RT_PCI_ASPM_OSC_DISABLE BIT1 /* PCI ASPM controlled by driver or BIOS, i.e., force enable ASPM */ +#define RT_PCI_ASPM_OSC_ENABLE BIT(0) /* PCI ASPM controlled by OS according to ACPI Spec 5.0 */ +#define RT_PCI_ASPM_OSC_DISABLE BIT(1) /* PCI ASPM controlled by driver or BIOS, i.e., force enable ASPM */ enum { PSBBREG_RF0 = 0, From 6abf0b2df0b1c2205a4c0591425e6461afa62edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Steinm=C3=B6tzger?= Date: Mon, 11 May 2026 06:40:29 +0200 Subject: [PATCH 0768/5214] staging: rtl8723bs: fix type issue in DYNAMIC_BB_DYNAMIC_TXPWR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit cast to fix -Werror=overflow warning for BIT(2) usage in DYNAMIC_BB_DYNAMIC_TXPWR Suggested-by: Nikolay Kulikov Signed-off-by: Michael Steinmötzger Link: https://patch.msgid.link/20260511044029.14839-1-m.steinmoetzger@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 4 ++-- drivers/staging/rtl8723bs/include/osdep_service.h | 1 - drivers/staging/rtl8723bs/include/rtw_mlme_ext.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index cd30e9103114..1d37c2d5b10d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1450,13 +1450,13 @@ void update_IOT_info(struct adapter *padapter) pmlmeinfo->turboMode_cts2self = 0; pmlmeinfo->turboMode_rtsen = 1; /* disable high power */ - Switch_DM_Func(padapter, (~DYNAMIC_BB_DYNAMIC_TXPWR), false); + Switch_DM_Func(padapter, ((u32)(~DYNAMIC_BB_DYNAMIC_TXPWR)), false); break; case HT_IOT_PEER_REALTEK: /* rtw_write16(padapter, 0x4cc, 0xffff); */ /* rtw_write16(padapter, 0x546, 0x01c0); */ /* disable high power */ - Switch_DM_Func(padapter, (~DYNAMIC_BB_DYNAMIC_TXPWR), false); + Switch_DM_Func(padapter, ((u32)(~DYNAMIC_BB_DYNAMIC_TXPWR)), false); break; default: pmlmeinfo->turboMode_cts2self = 0; diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index bd72edcb4259..2f5011a8210c 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -14,7 +14,6 @@ #include -#define BIT2 0x00000004 extern int RTW_STATUS_CODE(int error_code); diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index a3c4501c2242..cb23c6939bfc 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -28,7 +28,7 @@ /* ====== ODM_ABILITY_E ======== */ /* BB ODM section BIT 0-15 */ #define DYNAMIC_BB_DIG BIT(0) /* ODM_BB_DIG */ -#define DYNAMIC_BB_DYNAMIC_TXPWR BIT2 /* ODM_BB_DYNAMIC_TXPWR */ +#define DYNAMIC_BB_DYNAMIC_TXPWR BIT(2) /* ODM_BB_DYNAMIC_TXPWR */ #define DYNAMIC_BB_ANT_DIV BIT(6) /* ODM_BB_ANT_DIV */ /* RF ODM section BIT 24-31 */ From 55793d3ddf774e9bbeed3c940fb19d6c1124b6d3 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:07 +0100 Subject: [PATCH 0769/5214] pinctrl: renesas: rzg2l: Make QSPI register handling conditional The QSPI register at offset 0x3008 is not present on all SoCs supported by the RZ/G2L pinctrl driver. Unconditionally reading and writing this register during suspend/resume on hardware that lacks it can cause undefined behaviour. Add a qspi field to rzg2l_register_offsets to allow per-SoC declaration of the QSPI register offset, and guard the suspend/resume accesses with a check on that field. Populate the offset only for the RZ/{G2L,G2LC,G2UL, Five} hardware configuration, which is where the register is known to exist. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index bc2154b69514..ca9d4a3ec737 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -145,7 +145,7 @@ #define SMT(off) (0x3400 + (off) * 8) #define SD_CH(off, ch) ((off) + (ch) * 4) #define ETH_POC(off, ch) ((off) + (ch) * 4) -#define QSPI (0x3008) +#define QSPI (0x3008) /* known on RZ/{G2L,G2LC,G2UL,Five} only */ #define PVDD_2500 2 /* I/O domain voltage 2.5V */ #define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ @@ -220,12 +220,14 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = { * @sd_ch: SD_CH register offset * @eth_poc: ETH_POC register offset * @oen: OEN register offset + * @qspi: QSPI register offset */ struct rzg2l_register_offsets { u16 pwpr; u16 sd_ch; u16 eth_poc; u16 oen; + u16 qspi; }; /** @@ -3297,7 +3299,8 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev) cache->eth_poc[i] = readb(pctrl->base + ETH_POC(regs->eth_poc, i)); } - cache->qspi = readb(pctrl->base + QSPI); + if (regs->qspi) + cache->qspi = readb(pctrl->base + regs->qspi); cache->oen = readb(pctrl->base + pctrl->data->hwcfg->regs.oen); if (!atomic_read(&pctrl->wakeup_path)) @@ -3323,7 +3326,8 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev) return ret; } - writeb(cache->qspi, pctrl->base + QSPI); + if (regs->qspi) + writeb(cache->qspi, pctrl->base + regs->qspi); raw_spin_lock_irqsave(&pctrl->lock, flags); rzg2l_oen_write_with_pwpr(pctrl, cache->oen); @@ -3381,6 +3385,7 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = { .sd_ch = 0x3000, .eth_poc = 0x300c, .oen = 0x3018, + .qspi = QSPI, }, .iolh_groupa_ua = { /* 3v3 power source */ From bbe2277dedbeb54bb251ae3e0599007253739aad Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:08 +0100 Subject: [PATCH 0770/5214] pinctrl: renesas: rzg2l: Add support for selecting power source for {WDT,AWO,ISO} The RZ/G3L SoC has support for setting power source that are not controlled by the following voltage control registers: - SD_CH{0,1,2}_POC, XSPI_POC, ETH{0,1}_POC, I3C_SET.POC Add support for selecting voltages using OTHER_POC register for setting I/O domain voltage for WDT, ISO and AWO by extending rzg2l_caps_to_pwr_reg() with a mask output parameter so that callers callers can identify which bit(s) within OTHER_POC correspond to the requested domain. Update rzg2l_get_power_source() to extract the relevant bit field via field_get() when reading OTHER_POC, and update rzg2l_set_power_source() to perform a read-modify-write under the spinlock when writing to OTHER_POC, since multiple domains share the same register. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-4-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 53 ++++++++++++++++++++----- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index ca9d4a3ec737..7b1bb66d4ff6 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -63,10 +63,18 @@ #define PIN_CFG_SMT BIT(16) /* Schmitt-trigger input control */ #define PIN_CFG_ELC BIT(17) #define PIN_CFG_IOLH_RZV2H BIT(18) +#define PIN_CFG_PVDD1833_OTH_AWO_POC BIT(19) /* known on RZ/G3L only */ +#define PIN_CFG_PVDD1833_OTH_ISO_POC BIT(20) /* known on RZ/G3L only */ +#define PIN_CFG_WDTOVF_N_POC BIT(21) /* known on RZ/G3L only */ #define RZG2L_SINGLE_PIN BIT_ULL(63) /* Dedicated pin */ #define RZG2L_VARIABLE_CFG BIT_ULL(62) /* Variable cfg for port pins */ +#define PIN_CFG_OTHER_POC_MASK \ + (PIN_CFG_PVDD1833_OTH_AWO_POC | \ + PIN_CFG_PVDD1833_OTH_ISO_POC | \ + PIN_CFG_WDTOVF_N_POC) + #define RZG2L_MPXED_COMMON_PIN_FUNCS(group) \ (PIN_CFG_IOLH_##group | \ PIN_CFG_PUPD | \ @@ -146,6 +154,7 @@ #define SD_CH(off, ch) ((off) + (ch) * 4) #define ETH_POC(off, ch) ((off) + (ch) * 4) #define QSPI (0x3008) /* known on RZ/{G2L,G2LC,G2UL,Five} only */ +#define OTHER_POC (0x3028) /* known on RZ/G3L only */ #define PVDD_2500 2 /* I/O domain voltage 2.5V */ #define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ @@ -900,7 +909,8 @@ static void rzg2l_rmw_pin_config(struct rzg2l_pinctrl *pctrl, u32 offset, raw_spin_unlock_irqrestore(&pctrl->lock, flags); } -static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, u32 caps) +static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, + u32 caps, u8 *mask) { if (caps & PIN_CFG_IO_VMC_SD0) return SD_CH(regs->sd_ch, 0); @@ -912,6 +922,16 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, u32 return ETH_POC(regs->eth_poc, 1); if (caps & PIN_CFG_IO_VMC_QSPI) return QSPI; + if (caps & PIN_CFG_OTHER_POC_MASK) { + if (caps & PIN_CFG_PVDD1833_OTH_AWO_POC) + *mask = BIT(0); + else if (caps & PIN_CFG_PVDD1833_OTH_ISO_POC) + *mask = BIT(1); + else + *mask = BIT(2); + + return OTHER_POC; + } return -EINVAL; } @@ -920,17 +940,20 @@ static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps { const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; + u8 val, mask; int pwr_reg; - u8 val; if (caps & PIN_CFG_SOFT_PS) return pctrl->settings[pin].power_source; - pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps); + pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask); if (pwr_reg < 0) return pwr_reg; val = readb(pctrl->base + pwr_reg); + if (pwr_reg == OTHER_POC) + val = field_get(mask, val); + switch (val) { case PVDD_1800: return 1800; @@ -948,8 +971,8 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps { const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; + u8 poc_val, val, mask; int pwr_reg; - u8 val; if (caps & PIN_CFG_SOFT_PS) { pctrl->settings[pin].power_source = ps; @@ -958,25 +981,37 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps switch (ps) { case 1800: - val = PVDD_1800; + poc_val = PVDD_1800; break; case 2500: if (!(caps & (PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1))) return -EINVAL; - val = PVDD_2500; + poc_val = PVDD_2500; break; case 3300: - val = PVDD_3300; + poc_val = PVDD_3300; break; default: return -EINVAL; } - pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps); + pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask); if (pwr_reg < 0) return pwr_reg; - writeb(val, pctrl->base + pwr_reg); + if (pwr_reg == OTHER_POC) { + scoped_guard(raw_spinlock, &pctrl->lock) { + val = readb(pctrl->base + pwr_reg); + if (poc_val) + val |= mask; + else + val &= ~mask; + writeb(val, pctrl->base + pwr_reg); + } + } else { + writeb(poc_val, pctrl->base + pwr_reg); + } + pctrl->settings[pin].power_source = ps; return 0; From da4a37540b780c386eb2362bfcc638f769918cb1 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:09 +0100 Subject: [PATCH 0771/5214] pinctrl: renesas: rzg2l: Update OEN pin validation to use exact match The RZ/G2L SoC uses pin 0 from a port for OEN while RZ/G3L uses pin 1. The existing greater-than comparison against oen_max_pin in rzg2l_pin_to_oen_bit() would incorrectly accept any pin below that value rather than enforcing the single valid OEN pin for each SoC. Replace the range check with an exact equality test so that only the designated OEN pin is accepted. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-5-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 7b1bb66d4ff6..2a46ba7b3709 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1124,7 +1124,7 @@ static int rzg2l_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin) u64 caps = FIELD_GET(PIN_CFG_MASK, *pin_data); u8 pin = RZG2L_PIN_ID_TO_PIN(_pin); - if (pin > pctrl->data->hwcfg->oen_max_pin) + if (pin != pctrl->data->hwcfg->oen_max_pin) return -EINVAL; /* From a7d7aa8f5babac74ef780737e717bf9d90962eed Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:10 +0100 Subject: [PATCH 0772/5214] pinctrl: renesas: rzg2l: Add support for RZ/G3L SoC Add pinctrl driver support for RZ/G3L SoC. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-6-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 228 ++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 2a46ba7b3709..004096d5d1d1 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -93,6 +94,18 @@ #define RZG2L_MPXED_ETH_PIN_FUNCS(x) ((x) | PIN_CFG_NF) +#define RZG3L_MPXED_ETH_PIN_FUNCS(ether) \ + (PIN_CFG_IO_VMC_##ether | \ + PIN_CFG_IOLH_C | \ + PIN_CFG_PUPD | \ + PIN_CFG_NF) + +#define RZG3L_MPXED_PIN_FUNCS(group) (RZG2L_MPXED_COMMON_PIN_FUNCS(group) | \ + PIN_CFG_SOFT_PS) + +#define RZG3L_MPXED_PIN_FUNCS_POC(grp, poc) (RZG2L_MPXED_COMMON_PIN_FUNCS(grp) | \ + PIN_CFG_PVDD1833_OTH_##poc##_POC) + #define PIN_CFG_PIN_MAP_MASK GENMASK_ULL(61, 54) #define PIN_CFG_PIN_REG_MASK GENMASK_ULL(53, 46) #define PIN_CFG_MASK GENMASK_ULL(31, 0) @@ -230,6 +243,7 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = { * @eth_poc: ETH_POC register offset * @oen: OEN register offset * @qspi: QSPI register offset + * @other_poc: OTHER_POC register offset */ struct rzg2l_register_offsets { u16 pwpr; @@ -237,6 +251,7 @@ struct rzg2l_register_offsets { u16 eth_poc; u16 oen; u16 qspi; + u16 other_poc; }; /** @@ -337,6 +352,7 @@ struct rzg2l_pinctrl_pin_settings { * @nod: NOD registers cache * @sd_ch: SD_CH registers cache * @eth_poc: ET_POC registers cache + * @other_poc: OTHER_POC register cache * @oen: Output Enable register cache * @qspi: QSPI registers cache */ @@ -354,6 +370,7 @@ struct rzg2l_pinctrl_reg_cache { u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; + u8 other_poc; u8 qspi; }; @@ -403,6 +420,60 @@ static u64 rzg2l_pinctrl_get_variable_pin_cfg(struct rzg2l_pinctrl *pctrl, return 0; } +static const u64 r9a08g046_variable_pin_cfg[] = { + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 0, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 1, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 2, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 3, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 4, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 5, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 6, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PA, 7, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 0, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 1, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0) | PIN_CFG_OEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 2, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 3, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 4, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 5, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 6, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PB, 7, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 0, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 1, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 2, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 3, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 4, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 5, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 6, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PD, 7, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 0, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 1, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1) | PIN_CFG_OEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 2, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 3, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 4, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 5, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 6, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 7, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 0, RZG3L_MPXED_PIN_FUNCS(B)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 1, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 2, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 3, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 4, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 5, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 6, RZG3L_MPXED_PIN_FUNCS_POC(B, ISO)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 7, RZG3L_MPXED_PIN_FUNCS_POC(B, ISO)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 0, RZG3L_MPXED_PIN_FUNCS(B)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 1, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 2, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 3, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 4, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 5, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 0, RZG3L_MPXED_PIN_FUNCS(A) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 1, RZG3L_MPXED_PIN_FUNCS(A)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 2, RZG3L_MPXED_PIN_FUNCS(A)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 3, RZG3L_MPXED_PIN_FUNCS(A)), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 4, RZG3L_MPXED_PIN_FUNCS(A)), +}; + static const u64 r9a09g047_variable_pin_cfg[] = { RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PA, 0, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_IEN), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PA, 1, RZV2H_MPXED_PIN_FUNCS), @@ -2130,6 +2201,70 @@ static const u64 r9a09g047_gpio_configs[] = { RZG2L_GPIO_PORT_PACK(4, 0x3c, RZV2H_MPXED_PIN_FUNCS), /* PS */ }; +static const char * const rzg3l_gpio_names[] = { + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "P20", "P21", "P22", "P23", "P24", "P25", "P26", "P27", + "P30", "P31", "P32", "P33", "P34", "P35", "P36", "P37", + "", "", "", "", "", "", "", "", + "P50", "P51", "P52", "P53", "P54", "P55", "P56", "P57", + "P60", "P61", "P62", "P63", "P64", "P65", "P66", "P67", + "P70", "P71", "P72", "P73", "P74", "P75", "P76", "P77", + "P80", "P81", "P82", "P83", "P84", "P85", "P86", "P87", + "", "", "", "", "", "", "", "", + "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7", + "PB0", "PB1", "PB2", "PB3", "PB4", "PB5", "PB6", "PB7", + "PC0", "PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", + "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", + "PE0", "PE1", "PE2", "PE3", "PE4", "PE5", "PE6", "PE7", + "PF0", "PF1", "PF2", "PF3", "PF4", "PF5", "PF6", "PF7", + "PG0", "PG1", "PG2", "PG3", "PG4", "PG5", "PG6", "PG7", + "PH0", "PH1", "PH2", "PH3", "PH4", "PH5", "PH6", "PH7", + "", "", "", "", "", "", "", "", + "PJ0", "PJ1", "PJ2", "PJ3", "PJ4", "PJ5", "PJ6", "PJ7", + "PK0", "PK1", "PK2", "PK3", "PK4", "PK5", "PK6", "PK7", + "PL0", "PL1", "PL2", "PL3", "PL4", "PL5", "PL6", "PL7", + "PM0", "PM1", "PM2", "PM3", "PM4", "PM5", "PM6", "PM7", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "PS0", "PS1", "PS2", "PS3", "PS4", "PS5", "PS6", "PS7", +}; + +static const u64 r9a08g046_gpio_configs[] = { + 0x0, + 0x0, + RZG2L_GPIO_PORT_PACK(2, 0x22, PIN_CFG_NF | PIN_CFG_IEN), /* P2 */ + RZG2L_GPIO_PORT_PACK(7, 0x23, RZG3L_MPXED_PIN_FUNCS_POC(A, AWO)), /* P3 */ + 0x0, + RZG2L_GPIO_PORT_PACK(7, 0x25, RZG3L_MPXED_PIN_FUNCS_POC(A, ISO)), /* P5 */ + RZG2L_GPIO_PORT_PACK(7, 0x26, RZG3L_MPXED_PIN_FUNCS_POC(A, ISO)), /* P6 */ + RZG2L_GPIO_PORT_PACK(8, 0x27, RZG3L_MPXED_PIN_FUNCS_POC(A, ISO)), /* P7 */ + RZG2L_GPIO_PORT_PACK(6, 0x28, RZG3L_MPXED_PIN_FUNCS_POC(A, ISO)), /* P8 */ + 0x0, + RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2a), /* PA */ + RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2b), /* PB */ + RZG2L_GPIO_PORT_PACK(3, 0x2c, RZG3L_MPXED_ETH_PIN_FUNCS(ETH0)), /* PC */ + RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2d), /* PD */ + RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2e), /* PE */ + RZG2L_GPIO_PORT_PACK(3, 0x2f, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), /* PF */ + RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x30), /* PG */ + RZG2L_GPIO_PORT_PACK_VARIABLE(6, 0x31), /* PH */ + 0x0, + RZG2L_GPIO_PORT_PACK_VARIABLE(5, 0x33), /* PJ */ + RZG2L_GPIO_PORT_PACK(4, 0x34, RZG3L_MPXED_PIN_FUNCS_POC(B, ISO)), /* PK */ + RZG2L_GPIO_PORT_PACK(5, 0x35, RZG3L_MPXED_PIN_FUNCS(C)), /* PL */ + RZG2L_GPIO_PORT_PACK(8, 0x36, RZG3L_MPXED_PIN_FUNCS(C)), /* PM */ + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + RZG2L_GPIO_PORT_PACK(2, 0x3c, RZG3L_MPXED_PIN_FUNCS(A)), /* PS */ +}; + static const char * const rzv2h_gpio_names[] = { "P00", "P01", "P02", "P03", "P04", "P05", "P06", "P07", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", @@ -2468,6 +2603,37 @@ static struct rzg2l_dedicated_configs rzg3e_dedicated_pins[] = { (PIN_CFG_IOLH_RZV2H | PIN_CFG_SR | PIN_CFG_IEN | PIN_CFG_PUPD)) }, }; +static const struct rzg2l_dedicated_configs rzg3l_dedicated_pins[] = { + { "WDTOVF_N", RZG2L_SINGLE_PIN_PACK(0x5, 0, + (PIN_CFG_IOLH_A | PIN_CFG_WDTOVF_N_POC)) }, + { "SCIF0_RXD", RZG2L_SINGLE_PIN_PACK(0x6, 0, + (PIN_CFG_IOLH_A | PIN_CFG_PUPD | PIN_CFG_PVDD1833_OTH_AWO_POC)) }, + { "SCIF0_TXD", RZG2L_SINGLE_PIN_PACK(0x6, 1, + (PIN_CFG_IOLH_A | PIN_CFG_PUPD | PIN_CFG_PVDD1833_OTH_AWO_POC)) }, + { "SD0_CLK", RZG2L_SINGLE_PIN_PACK(0x9, 0, PIN_CFG_IOLH_B) }, + { "SD0_CMD", RZG2L_SINGLE_PIN_PACK(0x9, 1, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_RST#", RZG2L_SINGLE_PIN_PACK(0x9, 2, PIN_CFG_IOLH_B) }, + { "SD0_DS", RZG2L_SINGLE_PIN_PACK(0x9, 5, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT0", RZG2L_SINGLE_PIN_PACK(0x0a, 0, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT1", RZG2L_SINGLE_PIN_PACK(0x0a, 1, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT2", RZG2L_SINGLE_PIN_PACK(0x0a, 2, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT3", RZG2L_SINGLE_PIN_PACK(0x0a, 3, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT4", RZG2L_SINGLE_PIN_PACK(0x0a, 4, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT5", RZG2L_SINGLE_PIN_PACK(0x0a, 5, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT6", RZG2L_SINGLE_PIN_PACK(0x0a, 6, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + { "SD0_DAT7", RZG2L_SINGLE_PIN_PACK(0x0a, 7, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, +}; + static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl *pctrl) { const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[virq]; @@ -3025,6 +3191,9 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev) BUILD_BUG_ON(ARRAY_SIZE(r9a08g045_gpio_configs) * RZG2L_PINS_PER_PORT > ARRAY_SIZE(rzg2l_gpio_names)); + BUILD_BUG_ON(ARRAY_SIZE(r9a08g046_gpio_configs) * RZG2L_PINS_PER_PORT > + ARRAY_SIZE(rzg3l_gpio_names)); + BUILD_BUG_ON(ARRAY_SIZE(r9a09g047_gpio_configs) * RZG2L_PINS_PER_PORT > ARRAY_SIZE(rzg3e_gpio_names)); @@ -3337,6 +3506,8 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev) if (regs->qspi) cache->qspi = readb(pctrl->base + regs->qspi); cache->oen = readb(pctrl->base + pctrl->data->hwcfg->regs.oen); + if (regs->other_poc) + cache->other_poc = readb(pctrl->base + regs->other_poc); if (!atomic_read(&pctrl->wakeup_path)) clk_disable_unprepare(pctrl->clk); @@ -3363,6 +3534,8 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev) if (regs->qspi) writeb(cache->qspi, pctrl->base + regs->qspi); + if (regs->other_poc) + writeb(cache->other_poc, pctrl->base + regs->other_poc); raw_spin_lock_irqsave(&pctrl->lock, flags); rzg2l_oen_write_with_pwpr(pctrl, cache->oen); @@ -3431,6 +3604,40 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = { .oen_max_pin = 0, }; +static const struct rzg2l_hwcfg rzg3l_hwcfg = { + .regs = { + .pwpr = 0x3000, + .sd_ch = 0x3004, + .eth_poc = 0x3010, + .oen = 0x3018, + .other_poc = OTHER_POC, + }, + .iolh_groupa_ua = { + /* 1v8 power source */ + [RZG2L_IOLH_IDX_1V8] = 2200, 4400, 9000, 10000, + /* 3v3 power source */ + [RZG2L_IOLH_IDX_3V3] = 1900, 4000, 8000, 9000, + }, + .iolh_groupb_ua = { + /* 1v8 power source */ + [RZG2L_IOLH_IDX_1V8] = 7000, 8000, 9000, 10000, + /* 3v3 power source */ + [RZG2L_IOLH_IDX_3V3] = 4000, 6000, 8000, 9000, + }, + .iolh_groupc_ua = { + /* 1v8 power source */ + [RZG2L_IOLH_IDX_1V8] = 5200, 6000, 6550, 6800, + /* 2v5 source */ + [RZG2L_IOLH_IDX_2V5] = 4700, 5300, 5800, 6100, + /* 3v3 power source */ + [RZG2L_IOLH_IDX_3V3] = 4500, 5200, 5700, 6050, + }, + .tint_start_index = 17, + .drive_strength_ua = true, + .func_base = 0, + .oen_max_pin = 1, /* Pin 1 of P{B,E}1_ISO is the maximum OEN pin. */ +}; + static const struct rzg2l_hwcfg rzg3s_hwcfg = { .regs = { .pwpr = 0x3000, @@ -3524,6 +3731,23 @@ static struct rzg2l_pinctrl_data r9a08g045_data = { .bias_param_to_hw = &rzg2l_bias_param_to_hw, }; +static struct rzg2l_pinctrl_data r9a08g046_data = { + .port_pins = rzg3l_gpio_names, + .port_pin_configs = r9a08g046_gpio_configs, + .n_ports = ARRAY_SIZE(r9a08g046_gpio_configs), + .variable_pin_cfg = r9a08g046_variable_pin_cfg, + .n_variable_pin_cfg = ARRAY_SIZE(r9a08g046_variable_pin_cfg), + .dedicated_pins = rzg3l_dedicated_pins, + .n_port_pins = ARRAY_SIZE(r9a08g046_gpio_configs) * RZG2L_PINS_PER_PORT, + .n_dedicated_pins = ARRAY_SIZE(rzg3l_dedicated_pins), + .hwcfg = &rzg3l_hwcfg, + .pwpr_pfc_lock_unlock = &rzg2l_pwpr_pfc_lock_unlock, + .pmc_writeb = &rzg2l_pmc_writeb, + .pin_to_oen_bit = &rzg2l_pin_to_oen_bit, + .hw_to_bias_param = &rzg2l_hw_to_bias_param, + .bias_param_to_hw = &rzg2l_bias_param_to_hw, +}; + static struct rzg2l_pinctrl_data r9a09g047_data = { .port_pins = rzg3e_gpio_names, .port_pin_configs = r9a09g047_gpio_configs, @@ -3604,6 +3828,10 @@ static const struct of_device_id rzg2l_pinctrl_of_table[] = { .compatible = "renesas,r9a08g045-pinctrl", .data = &r9a08g045_data, }, + { + .compatible = "renesas,r9a08g046-pinctrl", + .data = &r9a08g046_data, + }, { .compatible = "renesas,r9a09g047-pinctrl", .data = &r9a09g047_data, From b2aff3c99facba8174eb594870b7eeb909aa9e1a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:11 +0100 Subject: [PATCH 0773/5214] pinctrl: renesas: rzg2l: Simplify rzg2l_pinctrl_set_mux() The port and function selectors are evaluated multiple times in rzg2l_pinctrl_set_mux(). Simplify the function by dropping dupicate evaluation storing them in local variables. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-7-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 004096d5d1d1..eff5fc081ec8 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -687,16 +687,18 @@ static int rzg2l_pinctrl_set_mux(struct pinctrl_dev *pctldev, for (i = 0; i < group->grp.npins; i++) { u64 *pin_data = pctrl->desc.pins[pins[i]].drv_data; u32 off = RZG2L_PIN_CFG_TO_PORT_OFFSET(*pin_data); + u32 port = RZG2L_PIN_ID_TO_PORT(pins[i]); u32 pin = RZG2L_PIN_ID_TO_PIN(pins[i]); + unsigned int func; - ret = rzg2l_validate_pin(pctrl, *pin_data, RZG2L_PIN_ID_TO_PORT(pins[i]), pin); + ret = rzg2l_validate_pin(pctrl, *pin_data, port, pin); if (ret) return ret; - dev_dbg(pctrl->dev, "port:%u pin: %u off:%x PSEL:%u\n", - RZG2L_PIN_ID_TO_PORT(pins[i]), pin, off, psel_val[i] - hwcfg->func_base); + func = psel_val[i] - hwcfg->func_base; + dev_dbg(pctrl->dev, "port:%u pin: %u off:%x PSEL:%u\n", port, pin, off, func); - rzg2l_pinctrl_set_pfc_mode(pctrl, pin, off, psel_val[i] - hwcfg->func_base); + rzg2l_pinctrl_set_pfc_mode(pctrl, pin, off, func); } return 0; From c590b377dcc0c382fb6d969483e551789a24540e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 30 Apr 2026 10:34:12 +0100 Subject: [PATCH 0774/5214] pinctrl: renesas: rzg2l: Add support for clone channel control The RZ/G3L SoC has some IP such as I2C ch{2,3},SCIF ch{3,4,5}, RSPI ch{1,2} and RSCI ch{1,2,3} need to control the clone channel for proper operation. As per the RZ/G3L hardware manual, the clone channel setting is to be done before the mux setting. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260430093422.74812-8-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 181 ++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index eff5fc081ec8..7a5fe801e283 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -152,6 +154,18 @@ FIELD_PREP_CONST(VARIABLE_PIN_CFG_PORT_MASK, (port)) | \ FIELD_PREP_CONST(PIN_CFG_MASK, (cfg))) +#define RZG3L_CLONE_CHANNEL_PIN_MASK GENMASK(31, 24) +#define RZG3L_CLONE_CHANNEL_PORT_MASK GENMASK(23, 19) +#define RZG3L_CLONE_CHANNEL_BIT_MASK GENMASK(12, 9) +#define RZG3L_CLONE_CHANNEL_VAL_MASK BIT(8) +#define RZG3L_CLONE_CHANNEL_PFC_MASK GENMASK(7, 0) +#define RZG3L_CLONE_CHANNEL_DATA(port, pins, bit, val, pfc) \ + (FIELD_PREP_CONST(RZG3L_CLONE_CHANNEL_PIN_MASK, (pins)) | \ + FIELD_PREP_CONST(RZG3L_CLONE_CHANNEL_PORT_MASK, (port)) | \ + FIELD_PREP_CONST(RZG3L_CLONE_CHANNEL_BIT_MASK, (bit)) | \ + FIELD_PREP_CONST(RZG3L_CLONE_CHANNEL_VAL_MASK, (val)) | \ + FIELD_PREP_CONST(RZG3L_CLONE_CHANNEL_PFC_MASK, (pfc))) + #define P(off) (0x0000 + (off)) #define PM(off) (0x0100 + (off) * 2) #define PMC(off) (0x0200 + (off)) @@ -313,6 +327,8 @@ struct rzg2l_pinctrl_data { const struct rzg2l_dedicated_configs *dedicated_pins; unsigned int n_port_pins; unsigned int n_dedicated_pins; + const u32 *clone_channel_data; + unsigned int n_clone_channel_data; const struct rzg2l_hwcfg *hwcfg; const u64 *variable_pin_cfg; unsigned int n_variable_pin_cfg; @@ -350,6 +366,7 @@ struct rzg2l_pinctrl_pin_settings { * @smt: SMT registers cache * @sr: SR registers cache * @nod: NOD registers cache + * @clone: Clone register cache * @sd_ch: SD_CH registers cache * @eth_poc: ET_POC registers cache * @other_poc: OTHER_POC register cache @@ -367,6 +384,7 @@ struct rzg2l_pinctrl_reg_cache { u32 *smt[2]; u32 *sr[2]; u32 *nod[2]; + u32 clone; u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; @@ -385,6 +403,8 @@ struct rzg2l_pinctrl { struct clk *clk; + struct regmap *syscon; + struct gpio_chip gpio_chip; struct pinctrl_gpio_range gpio_range; DECLARE_BITMAP(tint_slot, RZG2L_TINT_MAX_INTERRUPT); @@ -398,6 +418,7 @@ struct rzg2l_pinctrl { struct rzg2l_pinctrl_reg_cache *cache; struct rzg2l_pinctrl_reg_cache *dedicated_cache; atomic_t wakeup_path; + u32 clone_offset; }; static const u16 available_ps[] = { 1800, 2500, 3300 }; @@ -623,6 +644,45 @@ static int rzg2l_validate_pin(struct rzg2l_pinctrl *pctrl, return 0; } +static int rzg2l_pinctrl_set_clone_mode(struct rzg2l_pinctrl *pctrl, + u8 port, u8 pin, u8 func) +{ + unsigned int i; + + if (!pctrl->data->clone_channel_data) + return 0; + + switch (func) { + case 2: + case 4 ... 7: + break; + default: + return 0; + } + + for (i = 0; i < pctrl->data->n_clone_channel_data; i++) { + u32 pin_data = pctrl->data->clone_channel_data[i]; + unsigned int pin_func_mask = FIELD_GET(RZG3L_CLONE_CHANNEL_PFC_MASK, pin_data); + unsigned int pin_mask = FIELD_GET(RZG3L_CLONE_CHANNEL_PIN_MASK, pin_data); + u32 bit, val; + + if (!(pin_func_mask & BIT(func)) || + FIELD_GET(RZG3L_CLONE_CHANNEL_PORT_MASK, pin_data) != port) + continue; + + if (!(pin_mask & BIT(pin))) + continue; + + bit = FIELD_GET(RZG3L_CLONE_CHANNEL_BIT_MASK, pin_data); + val = FIELD_GET(RZG3L_CLONE_CHANNEL_VAL_MASK, pin_data); + + return regmap_update_bits(pctrl->syscon, pctrl->clone_offset, + BIT(bit), val << bit); + } + + return 0; +} + static void rzg2l_pinctrl_set_pfc_mode(struct rzg2l_pinctrl *pctrl, u8 pin, u8 off, u8 func) { @@ -698,6 +758,10 @@ static int rzg2l_pinctrl_set_mux(struct pinctrl_dev *pctldev, func = psel_val[i] - hwcfg->func_base; dev_dbg(pctrl->dev, "port:%u pin: %u off:%x PSEL:%u\n", port, pin, off, func); + ret = rzg2l_pinctrl_set_clone_mode(pctrl, port, pin, func); + if (ret) + return ret; + rzg2l_pinctrl_set_pfc_mode(pctrl, pin, off, func); } @@ -2636,6 +2700,97 @@ static const struct rzg2l_dedicated_configs rzg3l_dedicated_pins[] = { (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, }; +static const u32 r9a08g046_clone_channel_data[] = { + /* I2C ch2 Bit:0 Value:0 PFC:4 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PG, GENMASK(7, 6), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PH, GENMASK(3, 2), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PK, GENMASK(1, 0), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PA, GENMASK(5, 4) | GENMASK(1, 0), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PB, GENMASK(5, 4) | GENMASK(1, 0), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PC, GENMASK(1, 0), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(7, 6) | GENMASK(3, 2), 0, 0, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(7, 6) | GENMASK(3, 2), 0, 0, BIT(4)), + /* I2C ch2 Bit:0 Value:1 PFC:4 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(5, 4) | GENMASK(1, 0), 0, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(6, 5) | GENMASK(2, 1), 0, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(5, 4) | GENMASK(1, 0), 0, 1, BIT(4)), + /* I2C ch3 Bit:1 Value:0 PFC:4 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PF, GENMASK(1, 0), 1, 0, BIT(4)), + /* I2C ch3 Bit:1 Value:1 PFC:4 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P2, GENMASK(1, 0), 1, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, BIT(6) | GENMASK(3, 2), 1, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(4, 3) | BIT(0), 1, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, GENMASK(7, 6), 1, 1, BIT(4)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(3, 2), 1, 1, BIT(4)), + /* SCIF ch3 Bit:4 Value:0 PFC:{6,7} */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PG, GENMASK(6, 4), 4, 0, BIT(6)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PH, GENMASK(5, 3), 4, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PA, GENMASK(4, 2), 4, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PB, GENMASK(5, 3), 4, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(2, 0), 4, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(3, 1), 4, 0, BIT(7)), + /* SCIF ch3 Bit:4 Value:1 PFC:7 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(2, 0), 4, 1, BIT(7)), + /* SCIF ch4 Bit:5 Value:0 PFC:7 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PK, GENMASK(2, 0), 5, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PA, GENMASK(7, 5), 5, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PB, GENMASK(7, 6), 5, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PC, BIT(0), 5, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(5, 3), 5, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(6, 4), 5, 0, BIT(7)), + /* SCIF ch4 Bit:5 Value:1 PFC:7 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(5, 3), 5, 1, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(4, 2), 5, 1, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, GENMASK(7, 5), 5, 1, BIT(7)), + /* SCIF ch5 Bit:6 Value:0 PFC:7 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, BIT(7), 6, 0, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PF, GENMASK(1, 0), 6, 0, BIT(7)), + /* SCIF ch5 Bit:6 Value:1 PFC:7 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, BIT(6), 6, 1, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(6, 5) | GENMASK(1, 0), 6, 1, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, GENMASK(4, 2) | BIT(0), 6, 1, BIT(7)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(2, 0), 6, 1, BIT(7)), + /* RSPI ch1 Bit:8 Value:0 PFC:2 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PH, GENMASK(5, 0), 8, 0, BIT(2)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(7, 5), 8, 0, BIT(2)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(3, 0), 8, 0, BIT(2)), + /* RSPI ch1 Bit:8 Value:1 PFC:2 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(6, 0), 8, 1, BIT(2)), + /* RSPI ch2 Bit:9 Value:0 PFC:2 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(7, 4), 9, 0, BIT(2)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PF, GENMASK(2, 0), 9, 0, BIT(2)), + /* RSPI ch2 Bit:9 Value:1 PFC:2 */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(6, 0), 9, 1, BIT(2)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, BIT(7), 9, 1, BIT(2)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(5, 0), 9, 1, BIT(2)), + /* RSCI ch1 Bit:12 Value:0 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PG, GENMASK(3, 0), 12, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PA, GENMASK(3, 0), 12, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PB, GENMASK(7, 6), 12, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PC, GENMASK(1, 0), 12, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(7, 4), 12, 0, GENMASK(6, 5)), + /* RSCI ch1 Bit:12 Value:1 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(3, 0), 12, 1, GENMASK(6, 5)), + /* RSCI ch2 Bit:13 Value:0 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PH, GENMASK(3, 0), 13, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PK, GENMASK(3, 0), 13, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PA, GENMASK(7, 4), 13, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PD, GENMASK(3, 0), 13, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(3, 0), 13, 0, GENMASK(6, 5)), + /* RSCI ch2 Bit:13 Value:1 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P5, GENMASK(6, 4), 13, 1, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(6, 5) | BIT(0), 13, 1, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, GENMASK(7, 6) | GENMASK(1, 0), 13, 1, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(1, 0), 13, 1, GENMASK(6, 5)), + /* RSCI ch3 Bit:14 Value:0 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PE, GENMASK(7, 6), 14, 0, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_PF, GENMASK(1, 0), 14, 0, GENMASK(6, 5)), + /* RSCI ch3 Bit:14 Value:1 PFC:{5,6} shared pins based on RSCI mode */ + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P6, GENMASK(4, 1), 14, 1, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P7, GENMASK(5, 2), 14, 1, GENMASK(6, 5)), + RZG3L_CLONE_CHANNEL_DATA(RZG3L_P8, GENMASK(5, 2), 14, 1, GENMASK(6, 5)), +}; + static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl *pctrl) { const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[virq]; @@ -3222,6 +3377,16 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev) "failed to enable GPIO clk\n"); } + if (pctrl->data->clone_channel_data) { + struct device_node *np = pctrl->dev->of_node; + + pctrl->syscon = syscon_regmap_lookup_by_phandle_args(np, "renesas,clonech", + 1, &pctrl->clone_offset); + if (IS_ERR(pctrl->syscon)) + return dev_err_probe(pctrl->dev, PTR_ERR(pctrl->syscon), + "Failed to parse renesas,clonech\n"); + } + raw_spin_lock_init(&pctrl->lock); spin_lock_init(&pctrl->bitmap_lock); mutex_init(&pctrl->mutex); @@ -3511,6 +3676,14 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev) if (regs->other_poc) cache->other_poc = readb(pctrl->base + regs->other_poc); + if (pctrl->syscon) { + int ret; + + ret = regmap_read(pctrl->syscon, pctrl->clone_offset, &cache->clone); + if (ret) + return ret; + } + if (!atomic_read(&pctrl->wakeup_path)) clk_disable_unprepare(pctrl->clk); else @@ -3528,6 +3701,12 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev) unsigned long flags; int ret; + if (pctrl->syscon) { + ret = regmap_write(pctrl->syscon, pctrl->clone_offset, cache->clone); + if (ret) + return ret; + } + if (!atomic_read(&pctrl->wakeup_path)) { ret = clk_prepare_enable(pctrl->clk); if (ret) @@ -3742,6 +3921,8 @@ static struct rzg2l_pinctrl_data r9a08g046_data = { .dedicated_pins = rzg3l_dedicated_pins, .n_port_pins = ARRAY_SIZE(r9a08g046_gpio_configs) * RZG2L_PINS_PER_PORT, .n_dedicated_pins = ARRAY_SIZE(rzg3l_dedicated_pins), + .clone_channel_data = r9a08g046_clone_channel_data, + .n_clone_channel_data = ARRAY_SIZE(r9a08g046_clone_channel_data), .hwcfg = &rzg3l_hwcfg, .pwpr_pfc_lock_unlock = &rzg2l_pwpr_pfc_lock_unlock, .pmc_writeb = &rzg2l_pmc_writeb, From f246b6c2bdeca803364af9789309559e5274fa34 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:16 +0200 Subject: [PATCH 0775/5214] pinctrl: renesas: rzg2l: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Reviewed-by: Lad Prabhakar Link: https://patch.msgid.link/6befae30f129daffd94f7a9507d874443e444a21.1777562725.git.geert+renesas@glider.be --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 7a5fe801e283..a106e087c224 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1821,8 +1821,9 @@ static int rzg2l_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { + unsigned long prev_config = 0; const unsigned int *pins; - unsigned int i, npins, prev_config = 0; + unsigned int i, npins; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From ec642ab9b76f8f5907e19778f035c49c3b192bbb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:33:17 +0200 Subject: [PATCH 0776/5214] pinctrl: renesas: rzv2m: Fix type in .pin_config_group_get() callback On 64-bit platforms, "unsigned long" is 64-bit. Hence checking if all "unsigned long" configuration values are equal should be done using an "unsigned long" temporary. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/4bcb78b40d685b0aab8c3150d379240ffb765b37.1777562725.git.geert+renesas@glider.be --- drivers/pinctrl/renesas/pinctrl-rzv2m.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzv2m.c b/drivers/pinctrl/renesas/pinctrl-rzv2m.c index 495e7f5d4128..827b3e91a6cc 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzv2m.c +++ b/drivers/pinctrl/renesas/pinctrl-rzv2m.c @@ -695,8 +695,9 @@ static int rzv2m_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { + unsigned long prev_config = 0; const unsigned int *pins; - unsigned int i, npins, prev_config = 0; + unsigned int i, npins; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); From c12b5ee30fb665133b3119283cfe7ce9474e3589 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:59 -0400 Subject: [PATCH 0777/5214] thunderbolt: test: add KUnit regression tests for XDomain property parser Add three KUnit cases that exercise the defects fixed by the sibling commits in this series by feeding crafted XDomain property blocks to tb_property_parse_dir(): tb_test_property_parse_u32_wrap - entry->value = 0xffffff00 and entry->length = 0x100 so their u32 sum 0x100000000 wraps to 0 under the block_len guard; without the fix the subsequent parse_dwdata() reads attacker-directed OOB memory. tb_test_property_parse_recursion - two DIRECTORY entries pointing at each other, driving __tb_property_parse_dir() recursion; without the fix the kernel stack is exhausted. tb_test_property_parse_dir_len_underflow - a DIRECTORY entry with length < 4 placed near the end of the block so the non-root UUID kmemdup of 4 dwords from dir_offset reads OOB before the later content_len = dir_len - 4 underflow path is reached. Each test asserts tb_property_parse_dir() returns NULL on the crafted input. With CONFIG_KASAN=y, running these on the pre-fix kernel produces an oops inside __tb_property_parse_dir or its callees: u32_wrap takes a page fault on the KASAN shadow lookup for the wild ~16 GiB OOB offset; recursion trips a KASAN out-of-bounds report in __unwind_start as the per-task kernel stack is consumed; dir_len_underflow trips a KASAN slab-out-of-bounds report in kmemdup_noprof reading 16 bytes past the 28-byte block. Post-fix they pass cleanly. The crafted blocks are populated by writing u32 dwords directly, matching the existing root_directory[] style used elsewhere in this file rather than imposing a private struct overlay. Run with: ./tools/testing/kunit/kunit.py run --arch=x86_64 \ --kconfig_add CONFIG_PCI=y --kconfig_add CONFIG_NVMEM=y \ --kconfig_add CONFIG_USB4=y --kconfig_add CONFIG_USB4_KUNIT_TEST=y \ --kconfig_add CONFIG_KASAN=y 'thunderbolt.tb_test_property_parse_*' Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/test.c | 126 +++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 1f4318249c22..f41fabf15456 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -2852,7 +2852,133 @@ static void tb_test_property_copy(struct kunit *test) tb_property_free_dir(src); } +/* + * Reproducers for three memory-safety defects in + * drivers/thunderbolt/property.c reached from a crafted XDomain + * PROPERTIES_RESPONSE payload. Without the fix these trip KASAN or + * smash the kernel stack; with the fix each returns NULL cleanly. + * + * The on-wire entry layout matches struct tb_property_entry in + * property.c (private to that translation unit): u32 key_hi, u32 + * key_lo, then a packed u32 = (type << 24) | (reserved << 16) | + * length, then u32 value. Each entry is 4 dwords. + */ + +static void tb_test_property_parse_u32_wrap(struct kunit *test) +{ + /* + * 0x102 dwords: enough for the entry's length field (0x100) to + * pass the "entry->length > block_len" gate so the wrap check + * is actually exercised. parse_dwdata's downstream OOB read + * lands ~16 GiB past the allocation regardless. + */ + u32 *block = kunit_kzalloc(test, 0x102 * sizeof(u32), GFP_KERNEL); + struct tb_property_dir *dir; + + KUNIT_ASSERT_NOT_NULL(test, block); + + block[0] = 0x55584401; /* "UXD" v1 magic */ + block[1] = 0x00000004; /* Root directory length: one entry */ + + /* + * DATA entry whose value 0xffffff00 + length 0x100 wrap to 0 + * in u32, passing the sum <= block_len guard even though the + * real offset is far past the allocation. + */ + block[2] = 0x61616161; /* key_hi */ + block[3] = 0x61616161; /* key_lo */ + block[4] = 0x64000100; /* type=DATA, reserved=0, length=0x100 */ + block[5] = 0xffffff00; /* value */ + + dir = tb_property_parse_dir(block, 0x102); + KUNIT_EXPECT_NULL(test, dir); + tb_property_free_dir(dir); +} + +static void tb_test_property_parse_recursion(struct kunit *test) +{ + /* + * 10 dwords: rootdir header (2) + parent DIRECTORY entry (4) + + * the child entry that lives at dir_offset(2) + UUID(4) = 6, + * occupying block[6..9]. Each recursive level re-reads the + * same block[6..9] as its first child entry, which is itself + * a DIRECTORY pointing at offset 2. + */ + u32 *block = kunit_kzalloc(test, 10 * sizeof(u32), GFP_KERNEL); + struct tb_property_dir *dir; + + KUNIT_ASSERT_NOT_NULL(test, block); + + block[0] = 0x55584401; /* "UXD" v1 magic */ + block[1] = 0x00000004; /* Root directory length: one entry */ + + /* + * DIRECTORY entry pointing at dir_offset = 2 with length = 8. + * Non-root parse derives content_offset = 6, content_len = 4, + * nentries = 1. block[6..9] is read both as the parent's UUID + * (kmemdup'd into dir->uuid) and as the single child entry -- + * which is itself a DIRECTORY pointing at offset 2, so the + * recursion never terminates and the kernel stack is exhausted. + */ + block[2] = 0x61616161; /* key_hi */ + block[3] = 0x61616161; /* key_lo */ + block[4] = 0x44000008; /* type=DIRECTORY, reserved=0, length=8 */ + block[5] = 0x00000002; /* value = dir_offset */ + + block[6] = 0x62626262; /* doubles as UUID dword 0 / child key_hi */ + block[7] = 0x62626262; /* doubles as UUID dword 1 / child key_lo */ + block[8] = 0x44000008; /* type=DIRECTORY, reserved=0, length=8 */ + block[9] = 0x00000002; /* value = dir_offset (back at parent) */ + + dir = tb_property_parse_dir(block, 10); + KUNIT_EXPECT_NULL(test, dir); + tb_property_free_dir(dir); +} + +static void tb_test_property_parse_dir_len_underflow(struct kunit *test) +{ + /* + * Allocate exactly 7 dwords (28 bytes) so the kmalloc-32 chunk + * leaves a 4-byte slab redzone tail that KASAN-Generic can flag. + * With block_len = 7, dir_offset = 4, dir_len = 3, the non-root + * UUID kmemdup reads 16 bytes from byte 16, so bytes 28..31 fall + * in the redzone and trip a KASAN slab-out-of-bounds report on + * the pre-fix kernel. Sizing the buffer at a power of two (32, + * 64, ...) puts the over-read into the slab cache tail where + * KASAN's generic shadow does not flag it, and the test reduces + * to the downstream content_len = dir_len - 4 underflow path + * which also returns NULL. + */ + u32 *block = kunit_kzalloc(test, 7 * sizeof(u32), GFP_KERNEL); + struct tb_property_dir *dir; + + KUNIT_ASSERT_NOT_NULL(test, block); + + block[0] = 0x55584401; /* "UXD" v1 magic */ + block[1] = 0x00000004; /* Root directory length: one entry */ + + /* + * DIRECTORY entry with length = 3 pointing at dir_offset = 4. + * tb_property_entry_valid() permits value(4) + length(3) <= + * block_len(7). Non-root parse begins with a kmemdup of 4 + * dwords from dir_offset for the UUID; that read runs past the + * 28-byte allocation before the dir_len < 4 reject would fire. + */ + block[2] = 0x61616161; /* key_hi */ + block[3] = 0x61616161; /* key_lo */ + block[4] = 0x44000003; /* type=DIRECTORY, reserved=0, length=3 */ + block[5] = 0x00000004; /* value = dir_offset */ + /* block[6] is the start of the four UUID dwords; block[7..] is OOB. */ + + dir = tb_property_parse_dir(block, 7); + KUNIT_EXPECT_NULL(test, dir); + tb_property_free_dir(dir); +} + static struct kunit_case tb_test_cases[] = { + KUNIT_CASE(tb_test_property_parse_u32_wrap), + KUNIT_CASE(tb_test_property_parse_recursion), + KUNIT_CASE(tb_test_property_parse_dir_len_underflow), KUNIT_CASE(tb_test_path_basic), KUNIT_CASE(tb_test_path_not_connected_walk), KUNIT_CASE(tb_test_path_single_hop_walk), From 53df57b3ae880914b5b4ea51a5e8e52c2729258a Mon Sep 17 00:00:00 2001 From: "Kevin Hilman (TI)" Date: Mon, 20 Apr 2026 16:51:17 -0700 Subject: [PATCH 0778/5214] dt-bindings: power: Add power-domains-child-ids property Add binding documentation for the new power-domains-child-ids property, which works in conjunction with the existing power-domains property to establish parent-child relationships between a multi-domain power domain provider and external parent domains. Each element in the uint32 array identifies the child domain ID (index) within the provider that should be made a child domain of the corresponding phandle entry in power-domains. The two arrays must have the same number of elements. Signed-off-by: Kevin Hilman (TI) Reviewed-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- .../bindings/power/power-domain.yaml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml index b1147dbf2e73..163b0af158fd 100644 --- a/Documentation/devicetree/bindings/power/power-domain.yaml +++ b/Documentation/devicetree/bindings/power/power-domain.yaml @@ -68,6 +68,21 @@ properties: by the given provider should be subdomains of the domain specified by this binding. + power-domains-child-ids: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + An array of child domain IDs that correspond to the power-domains + property. This property is only applicable to power domain providers + with "#power-domain-cells" > 0 (i.e., providers that supply multiple + power domains). It specifies which of the provider's child domains + should be associated with each parent domain listed in the power-domains + property. The number of elements in this array must match the number of + phandles in the power-domains property. Each element specifies the child + domain ID (index) that should be made a child domain of the corresponding + parent domain. This enables hierarchical power domain structures where + different child domains from the same provider can have different + parent domains. + required: - "#power-domain-cells" @@ -133,3 +148,22 @@ examples: min-residency-us = <7000>; }; }; + + - | + // Example: SCMI domain 15 -> MAIN_PD, SCMI domain 19 -> WKUP_PD + MAIN_PD: power-controller-main { + compatible = "foo,power-controller"; + #power-domain-cells = <0>; + }; + + WKUP_PD: power-controller-wkup { + compatible = "foo,power-controller"; + #power-domain-cells = <0>; + }; + + scmi_pds: power-controller-scmi { + compatible = "foo,power-controller"; + #power-domain-cells = <1>; + power-domains = <&MAIN_PD>, <&WKUP_PD>; + power-domains-child-ids = <15>, <19>; + }; From 39d260d6bc7aab6777c0d14d1e27648ca8e9252e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 11 May 2026 12:45:02 +0200 Subject: [PATCH 0779/5214] firewire: Simplify storing pointers in device id struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Technically it is fine (on all current Linux architectures) to store a pointer in an unsigned long variable. However this needs explicit casting which is an easy source for type mismatches. By replacing the plain unsigned long .driver_data in struct ieee1394_device_id by an anonymous union, most of the casting can be dropped. There is still some implicit casting involved (between a void * and a driver specific pointer type), but that's better than the approach to store a pointer in an unsigned long variable as this doesn't lose the information that the data being pointed to is const. All users of struct ieee1394_device_id are initialized in a way that is compatible with the new definition, so no adaptions are needed there. (The comments addressing to CHERI extension are dropped by the maintainer.) Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/e5ba45a7e386461c0b1a5001635aa008b01c2164.1778494204.git.u.kleine-koenig@baylibre.com Signed-off-by: Takashi Sakamoto --- include/linux/mod_devicetable.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 23ff24080dfd..3b0c9a251a2e 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -61,7 +61,10 @@ struct ieee1394_device_id { __u32 model_id; __u32 specifier_id; __u32 version; - kernel_ulong_t driver_data; + union { + kernel_ulong_t driver_data; + const void *driver_data_ptr; + }; }; From 8208d94f149a53311ac7687c051cb3a6d58063f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 11 May 2026 12:45:03 +0200 Subject: [PATCH 0780/5214] ALSA: firewire: Make use of ieee1394's .driver_data_ptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently struct ieee1394_device_id gained a new member to store a pointer to driver data. Make use of that to get rid of a bunch of casts. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/6b7b7b3b8b54465ad5e7463412a202350fccbb62.1778494204.git.u.kleine-koenig@baylibre.com Signed-off-by: Takashi Sakamoto --- sound/firewire/dice/dice.c | 34 +++++++++++++++++----------------- sound/firewire/fireface/ff.c | 12 ++++++------ sound/firewire/motu/motu.c | 6 +++--- sound/firewire/oxfw/oxfw.c | 4 ++-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index f7a50bae4b55..58f14aadc73d 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -148,7 +148,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent snd_dice_detect_formats_t detect_formats; int err; - if (!entry->driver_data && entry->vendor_id != OUI_SSL) { + if (!entry->driver_data_ptr && entry->vendor_id != OUI_SSL) { err = check_dice_category(unit); if (err < 0) return -ENODEV; @@ -164,10 +164,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent dev_set_drvdata(&unit->device, dice); dice->card = card; - if (!entry->driver_data) + if (!entry->driver_data_ptr) detect_formats = snd_dice_stream_detect_current_formats; else - detect_formats = (snd_dice_detect_formats_t)entry->driver_data; + detect_formats = entry->driver_data_ptr; // Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer // frequency. @@ -255,7 +255,7 @@ static void dice_bus_reset(struct fw_unit *unit) .model_id = (model), \ .specifier_id = (vendor), \ .version = DICE_INTERFACE, \ - .driver_data = (kernel_ulong_t)(data), \ + .driver_data_ptr = (data), \ } static const struct ieee1394_device_id dice_id_table[] = { @@ -267,7 +267,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MAUDIO, .model_id = 0x000010, - .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + .driver_data_ptr = snd_dice_detect_extension_formats, }, /* M-Audio Profire 610 has a different value in version field. */ { @@ -275,7 +275,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MAUDIO, .model_id = 0x000011, - .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + .driver_data_ptr = snd_dice_detect_extension_formats, }, /* TC Electronic Konnekt 24D. */ { @@ -283,7 +283,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000020, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Konnekt 8. */ { @@ -291,7 +291,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000021, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Studio Konnekt 48. */ { @@ -299,7 +299,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000022, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Konnekt Live. */ { @@ -307,7 +307,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000023, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Desktop Konnekt 6. */ { @@ -315,7 +315,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000024, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Impact Twin. */ { @@ -323,7 +323,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000027, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* TC Electronic Digital Konnekt x32. */ { @@ -331,7 +331,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_TCELECTRONIC, .model_id = 0x000030, - .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + .driver_data_ptr = snd_dice_detect_tcelectronic_formats, }, /* Alesis iO14/iO26. */ { @@ -339,7 +339,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_ALESIS, .model_id = MODEL_ALESIS_IO_BOTH, - .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats, + .driver_data_ptr = snd_dice_detect_alesis_formats, }, // Alesis MasterControl. { @@ -347,7 +347,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_ALESIS, .model_id = 0x000002, - .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_mastercontrol_formats, + .driver_data_ptr = snd_dice_detect_alesis_mastercontrol_formats, }, /* Mytek Stereo 192 DSD-DAC. */ { @@ -355,7 +355,7 @@ static const struct ieee1394_device_id dice_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_MYTEK, .model_id = 0x000002, - .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats, + .driver_data_ptr = snd_dice_detect_mytek_formats, }, // Solid State Logic, Duende Classic and Mini. // NOTE: each field of GUID in config ROM is not compliant to standard @@ -469,7 +469,7 @@ static const struct ieee1394_device_id dice_id_table[] = { .model_id = OUI_TEAC, .specifier_id = OUI_TEAC, .version = 0x800006, - .driver_data = (kernel_ulong_t)snd_dice_detect_teac_formats, + .driver_data_ptr = snd_dice_detect_teac_formats, }, { } }; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 5d2c4fbf4434..13472822d2be 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -70,7 +70,7 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e init_waitqueue_head(&ff->hwdep_wait); ff->unit_version = entry->version; - ff->spec = (const struct snd_ff_spec *)entry->driver_data; + ff->spec = entry->driver_data_ptr; err = snd_ff_transaction_register(ff); if (err < 0) @@ -186,7 +186,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_FF800, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ff800, + .driver_data_ptr = &spec_ff800, }, /* Fireface 400 */ { @@ -198,7 +198,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_FF400, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ff400, + .driver_data_ptr = &spec_ff400, }, // Fireface UFX. { @@ -210,7 +210,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_UFX, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ufx_802, + .driver_data_ptr = &spec_ufx_802, }, // Fireface UCX. { @@ -222,7 +222,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_UCX, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ucx, + .driver_data_ptr = &spec_ucx, }, // Fireface 802. { @@ -234,7 +234,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .specifier_id = OUI_RME, .version = SND_FF_UNIT_VERSION_802, .model_id = 0x101800, - .driver_data = (kernel_ulong_t)&spec_ufx_802, + .driver_data_ptr = &spec_ufx_802, }, {} }; diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index fd2a9dddbfa6..1fec6c8cdf6c 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -78,7 +78,7 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent dev_set_drvdata(&unit->device, motu); motu->card = card; - motu->spec = (const struct snd_motu_spec *)entry->driver_data; + motu->spec = entry->driver_data_ptr; mutex_init(&motu->mutex); spin_lock_init(&motu->lock); init_waitqueue_head(&motu->hwdep_wait); @@ -148,7 +148,7 @@ static void motu_bus_update(struct fw_unit *unit) snd_motu_transaction_reregister(motu); } -#define SND_MOTU_DEV_ENTRY(model, data) \ +#define SND_MOTU_DEV_ENTRY(model, data_ptr) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ IEEE1394_MATCH_SPECIFIER_ID | \ @@ -156,7 +156,7 @@ static void motu_bus_update(struct fw_unit *unit) .vendor_id = OUI_MOTU, \ .specifier_id = OUI_MOTU, \ .version = model, \ - .driver_data = (kernel_ulong_t)data, \ + .driver_data_ptr = data_ptr, \ } static const struct ieee1394_device_id motu_id_table[] = { diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 5039bd79b18e..38a3c3b150df 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -95,7 +95,7 @@ static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *ent /* to apply card definitions */ if (entry->vendor_id == VENDOR_GRIFFIN || entry->vendor_id == VENDOR_LACIE) { - info = (const struct compat_info *)entry->driver_data; + info = entry->driver_data_ptr; d = info->driver_name; v = info->vendor_name; m = info->model_name; @@ -321,7 +321,7 @@ static const struct compat_info lacie_speakers = { .model_id = model, \ .specifier_id = SPECIFIER_1394TA, \ .version = VERSION_AVC, \ - .driver_data = (kernel_ulong_t)data, \ + .driver_data_ptr = data, \ } static const struct ieee1394_device_id oxfw_id_table[] = { From 29e34a882b09e9f01d0fff5e312ea0cf9499137d Mon Sep 17 00:00:00 2001 From: "Kevin Hilman (TI)" Date: Mon, 20 Apr 2026 16:51:18 -0700 Subject: [PATCH 0781/5214] pmdomain: core: add support for power-domains-child-ids Currently, PM domains can only support hierarchy for simple providers (e.g. ones with #power-domain-cells = 0). Add support for oncell providers as well by adding a new property `power-domains-child-ids` to describe the parent/child relationship. For example, an SCMI PM domain provider has multiple domains, each of which might be a child of diffeent parent domains. In this example, the parent domains are MAIN_PD and WKUP_PD: scmi_pds: protocol@11 { reg = <0x11>; #power-domain-cells = <1>; power-domains = <&MAIN_PD>, <&WKUP_PD>; power-domains-child-ids = <15>, <19>; }; With this example using the new property, SCMI PM domain 15 becomes a child domain of MAIN_PD, and SCMI domain 19 becomes a child domain of WKUP_PD. To support this feature, add two new core functions - of_genpd_add_child_ids() - of_genpd_remove_child_ids() which can be called by pmdomain providers to add/remove child domains if they support the new property power-domains-child-ids. The add function is "all or nothing". If it cannot add all of the child domains in the list, it will unwind any additions already made and report a failure. Signed-off-by: Kevin Hilman (TI) Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 167 ++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 16 ++++ 2 files changed, 183 insertions(+) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 71e930e80178..f7731270015d 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2924,6 +2924,173 @@ static struct generic_pm_domain *genpd_get_from_provider( return genpd; } +/** + * of_genpd_add_child_ids() - Parse power-domains-child-ids property + * @np: Device node pointer associated with the PM domain provider. + * @data: Pointer to the onecell data associated with the PM domain provider. + * + * Parse the power-domains and power-domains-child-ids properties to establish + * parent-child relationships for PM domains. The power-domains property lists + * parent domains, and power-domains-child-ids lists which child domain IDs + * should be associated with each parent. + * + * Uses "all or nothing" semantics: either all relationships are established + * successfully, or none are (any partially-added relationships are unwound + * on error). + * + * Returns the number of parent-child relationships established on success, + * 0 if the properties don't exist, or a negative error code on failure. + */ +int of_genpd_add_child_ids(struct device_node *np, + struct genpd_onecell_data *data) +{ + struct of_phandle_args parent_args; + struct generic_pm_domain *parent_genpd, *child_genpd; + struct generic_pm_domain **pairs; /* pairs[2*i]=parent, pairs[2*i+1]=child */ + u32 child_id; + int i, ret, count, child_count, added = 0; + + /* Check if both properties exist */ + count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells"); + if (count <= 0) + return 0; + + child_count = of_property_count_u32_elems(np, "power-domains-child-ids"); + if (child_count < 0) + return 0; + if (child_count != count) + return -EINVAL; + + /* Allocate tracking array for error unwind (parent/child pairs) */ + pairs = kmalloc_array(count * 2, sizeof(*pairs), GFP_KERNEL); + if (!pairs) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = of_property_read_u32_index(np, "power-domains-child-ids", + i, &child_id); + if (ret) + goto err_unwind; + + /* Validate child ID is within bounds */ + if (child_id >= data->num_domains) { + pr_err("Child ID %u out of bounds (max %u) for %pOF\n", + child_id, data->num_domains - 1, np); + ret = -EINVAL; + goto err_unwind; + } + + /* Get the child domain */ + child_genpd = data->domains[child_id]; + if (!child_genpd) { + pr_err("Child domain %u is NULL for %pOF\n", child_id, np); + ret = -EINVAL; + goto err_unwind; + } + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", i, + &parent_args); + if (ret) + goto err_unwind; + + /* Get the parent domain */ + parent_genpd = genpd_get_from_provider(&parent_args); + of_node_put(parent_args.np); + if (IS_ERR(parent_genpd)) { + pr_err("Failed to get parent domain for %pOF: %ld\n", + np, PTR_ERR(parent_genpd)); + ret = PTR_ERR(parent_genpd); + goto err_unwind; + } + + /* Establish parent-child relationship */ + ret = pm_genpd_add_subdomain(parent_genpd, child_genpd); + if (ret) { + pr_err("Failed to add child domain %u to parent in %pOF: %d\n", + child_id, np, ret); + goto err_unwind; + } + + /* Track for potential unwind */ + pairs[2 * added] = parent_genpd; + pairs[2 * added + 1] = child_genpd; + added++; + + pr_debug("Added child domain %u (%s) to parent %s for %pOF\n", + child_id, child_genpd->name, parent_genpd->name, np); + } + + kfree(pairs); + return count; + +err_unwind: + /* Reverse all previously established relationships */ + while (added-- > 0) + pm_genpd_remove_subdomain(pairs[2 * added], pairs[2 * added + 1]); + kfree(pairs); + return ret; +} +EXPORT_SYMBOL_GPL(of_genpd_add_child_ids); + +/** + * of_genpd_remove_child_ids() - Remove parent-child PM domain relationships + * @np: Device node pointer associated with the PM domain provider. + * @data: Pointer to the onecell data associated with the PM domain provider. + * + * Reverses the effect of of_genpd_add_child_ids() by parsing the same + * power-domains and power-domains-child-ids properties and calling + * pm_genpd_remove_subdomain() for each established relationship. + * + * Returns 0 on success, -ENOENT if properties don't exist, or negative error + * code on failure. + */ +int of_genpd_remove_child_ids(struct device_node *np, + struct genpd_onecell_data *data) +{ + struct of_phandle_args parent_args; + struct generic_pm_domain *parent_genpd, *child_genpd; + u32 child_id; + int i, ret, count, child_count; + + /* Check if both properties exist */ + count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells"); + if (count <= 0) + return -ENOENT; + + child_count = of_property_count_u32_elems(np, "power-domains-child-ids"); + if (child_count < 0) + return -ENOENT; + if (child_count != count) + return -EINVAL; + + for (i = 0; i < count; i++) { + if (of_property_read_u32_index(np, "power-domains-child-ids", + i, &child_id)) + continue; + + if (child_id >= data->num_domains || !data->domains[child_id]) + continue; + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", i, + &parent_args); + if (ret) + continue; + + parent_genpd = genpd_get_from_provider(&parent_args); + of_node_put(parent_args.np); + if (IS_ERR(parent_genpd)) + continue; + + child_genpd = data->domains[child_id]; + pm_genpd_remove_subdomain(parent_genpd, child_genpd); + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_genpd_remove_child_ids); + /** * of_genpd_add_device() - Add a device to an I/O PM domain * @genpdspec: OF phandle args to use for look-up PM domain diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index b299dc0128d6..f925614aebdb 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -467,6 +467,10 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); int of_genpd_parse_idle_states(struct device_node *dn, struct genpd_power_state **states, int *n); void of_genpd_sync_state(struct device_node *np); +int of_genpd_add_child_ids(struct device_node *np, + struct genpd_onecell_data *data); +int of_genpd_remove_child_ids(struct device_node *np, + struct genpd_onecell_data *data); int genpd_dev_pm_attach(struct device *dev); struct device *genpd_dev_pm_attach_by_id(struct device *dev, @@ -536,6 +540,18 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) { return ERR_PTR(-EOPNOTSUPP); } + +static inline int of_genpd_add_child_ids(struct device_node *np, + struct genpd_onecell_data *data) +{ + return -EOPNOTSUPP; +} + +static inline int of_genpd_remove_child_ids(struct device_node *np, + struct genpd_onecell_data *data) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ #ifdef CONFIG_PM From fba9c703c18459e936c365442667c08ca40dadcb Mon Sep 17 00:00:00 2001 From: "Kevin Hilman (TI)" Date: Mon, 20 Apr 2026 16:51:19 -0700 Subject: [PATCH 0782/5214] pmdomain: arm_scmi: add support for domain hierarchies After primary SCMI pmdomain is created, use new of_genpd helper which checks for child domain mappings defined in power-domains-child-ids. Also remove any child domain mappings when SCMI domain is removed. Signed-off-by: Kevin Hilman (TI) Signed-off-by: Ulf Hansson --- drivers/pmdomain/arm/scmi_pm_domain.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c index 5454faed7d5d..3d73aef21d2f 100644 --- a/drivers/pmdomain/arm/scmi_pm_domain.c +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -113,6 +113,15 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev) goto err_rm_genpds; dev_set_drvdata(dev, scmi_pd_data); + + /* + * Parse (optional) power-domains-child-ids property to establish + * parent-child relationships. + */ + ret = of_genpd_add_child_ids(np, scmi_pd_data); + if (ret < 0) + dev_err(dev, "Failed to add child domain hierarchy: %d\n", ret); + dev_info(dev, "Initialized %d power domains", num_domains); return 0; @@ -130,9 +139,13 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev) struct device *dev = &sdev->dev; struct device_node *np = dev->of_node; + scmi_pd_data = dev_get_drvdata(dev); + + /* Remove any parent-child relationships established at probe time */ + of_genpd_remove_child_ids(np, scmi_pd_data); + of_genpd_del_provider(np); - scmi_pd_data = dev_get_drvdata(dev); for (i = 0; i < scmi_pd_data->num_domains; i++) { if (!scmi_pd_data->domains[i]) continue; From cc966553e6ff0849978b5754531b768b0ff54985 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:00:27 +0200 Subject: [PATCH 0783/5214] x86/platform/olpc: xo15: Drop wakeup source on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent leaking a wakeup source object after removing the driver by adding appropriate cleanup code to its remove callback function. Fixes: a0f30f592d2d ("x86, olpc: Add XO-1.5 SCI driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2069931.usQuhbGJ8B@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/platform/olpc/olpc-xo15-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 82c51b6ec528..276caf756a9c 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -186,6 +186,7 @@ static int xo15_sci_add(struct acpi_device *device) static void xo15_sci_remove(struct acpi_device *device) { + device_init_wakeup(&device->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); From 75c7d3d76b78b568969316224c8ae25c0224cc9a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:01:41 +0200 Subject: [PATCH 0784/5214] x86/platform/olpc: xo15: Convert ACPI driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the olpc-xo15-sci ACPI driver to a platform one. After this change, the wakeup source added by the driver will appear under the platform device used for driver binding, but the sysfs attribute added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting it to be there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1970421.CQOukoFCf9@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/platform/olpc/olpc-xo15-sci.c | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 276caf756a9c..e486109e88c8 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -136,14 +137,16 @@ static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; } -static int xo15_sci_add(struct acpi_device *device) +static int xo15_sci_probe(struct platform_device *pdev) { + struct acpi_device *device; unsigned long long tmp; acpi_status status; int r; + device = ACPI_COMPANION(&pdev->dev); if (!device) - return -EINVAL; + return -ENODEV; strscpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME); strscpy(acpi_device_class(device), XO15_SCI_CLASS); @@ -160,7 +163,7 @@ static int xo15_sci_add(struct acpi_device *device) if (ACPI_FAILURE(status)) return -ENODEV; - dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); + dev_info(&pdev->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); r = sysfs_create_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); if (r) @@ -174,7 +177,7 @@ static int xo15_sci_add(struct acpi_device *device) /* Enable wake-on-EC */ if (device->wakeup.flags.valid) - device_init_wakeup(&device->dev, true); + device_init_wakeup(&pdev->dev, true); return 0; @@ -184,9 +187,11 @@ static int xo15_sci_add(struct acpi_device *device) return r; } -static void xo15_sci_remove(struct acpi_device *device) +static void xo15_sci_remove(struct platform_device *pdev) { - device_init_wakeup(&device->dev, false); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + + device_init_wakeup(&pdev->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); @@ -214,19 +219,18 @@ static const struct acpi_device_id xo15_sci_device_ids[] = { {"", 0}, }; -static struct acpi_driver xo15_sci_drv = { - .name = DRV_NAME, - .class = XO15_SCI_CLASS, - .ids = xo15_sci_device_ids, - .ops = { - .add = xo15_sci_add, - .remove = xo15_sci_remove, +static struct platform_driver xo15_sci_drv = { + .probe = xo15_sci_probe, + .remove = xo15_sci_remove, + .driver = { + .name = DRV_NAME, + .acpi_match_table = xo15_sci_device_ids, + .pm = &xo15_sci_pm, }, - .drv.pm = &xo15_sci_pm, }; static int __init xo15_sci_init(void) { - return acpi_bus_register_driver(&xo15_sci_drv); + return platform_driver_register(&xo15_sci_drv); } device_initcall(xo15_sci_init); From 5c44f48e91deefdd42e567a2779d331937c97cd0 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 11 May 2026 18:13:20 +0530 Subject: [PATCH 0785/5214] platform: arm64: Add driver for EC found on Qualcomm reference devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Embedded controller driver support for Hamoa/Purwa/Glymur qualcomm reference boards. It handles fan control, temperature sensors, access to EC state changes and supports reporting suspend entry/exit to the EC. Co-developed-by: Maya Matuszczyk Signed-off-by: Maya Matuszczyk Signed-off-by: Sibi Sankar Reviewed-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Tested-by: Akhil P Oommen Co-developed-by: Anvesh Jain P Signed-off-by: Anvesh Jain P Link: https://patch.msgid.link/20260511-add-driver-for-ec-v9-2-e5437c39b7f8@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 8 + drivers/platform/arm64/Kconfig | 13 + drivers/platform/arm64/Makefile | 1 + drivers/platform/arm64/qcom-hamoa-ec.c | 451 +++++++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 drivers/platform/arm64/qcom-hamoa-ec.c diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..839e39825455 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21896,6 +21896,14 @@ F: Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml F: drivers/misc/fastrpc.c F: include/uapi/misc/fastrpc.h +QUALCOMM HAMOA EMBEDDED CONTROLLER DRIVER +M: Anvesh Jain P +M: Sibi Sankar +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/embedded-controller/qcom,hamoa-crd-ec.yaml +F: drivers/platform/arm64/qcom-hamoa-ec.c + QUALCOMM HEXAGON ARCHITECTURE M: Brian Cain L: linux-hexagon@vger.kernel.org diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig index 10f905d7d6bf..e32e01b2a9bd 100644 --- a/drivers/platform/arm64/Kconfig +++ b/drivers/platform/arm64/Kconfig @@ -90,4 +90,17 @@ config EC_LENOVO_THINKPAD_T14S Say M or Y here to include this support. +config EC_QCOM_HAMOA + tristate "Embedded Controller driver for Qualcomm Hamoa/Glymur reference devices" + depends on ARCH_QCOM || COMPILE_TEST + depends on I2C + depends on THERMAL || THERMAL=n + help + Say M or Y here to enable the Embedded Controller driver for Qualcomm + Snapdragon-based Hamoa/Glymur reference devices. The driver handles fan + control, temperature sensors, access to EC state changes and supports + reporting suspend entry/exit to the EC. + + This driver currently supports Hamoa/Purwa/Glymur reference devices. + endif # ARM64_PLATFORM_DEVICES diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile index 60c131cff6a1..7681be4a46e9 100644 --- a/drivers/platform/arm64/Makefile +++ b/drivers/platform/arm64/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o obj-$(CONFIG_EC_LENOVO_THINKPAD_T14S) += lenovo-thinkpad-t14s.o +obj-$(CONFIG_EC_QCOM_HAMOA) += qcom-hamoa-ec.o diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c new file mode 100644 index 000000000000..a018f7bf35d2 --- /dev/null +++ b/drivers/platform/arm64/qcom-hamoa-ec.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Maya Matuszczyk + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EC_SCI_EVT_READ_CMD 0x05 +#define EC_FW_VERSION_CMD 0x0e +#define EC_MODERN_STANDBY_CMD 0x23 +#define EC_FAN_DBG_CONTROL_CMD 0x30 +#define EC_SCI_EVT_CONTROL_CMD 0x35 +#define EC_THERMAL_CAP_CMD 0x42 + +#define EC_FW_VERSION_RESP_LEN 4 +#define EC_THERMAL_CAP_RESP_LEN 3 +#define EC_FAN_DEBUG_CMD_LEN 6 +#define EC_FAN_SPEED_DATA_SIZE 4 + +#define EC_MODERN_STANDBY_ENTER 0x01 +#define EC_MODERN_STANDBY_EXIT 0x00 + +#define EC_FAN_DEBUG_MODE_OFF 0 +#define EC_FAN_DEBUG_MODE_ON BIT(0) +#define EC_FAN_ON BIT(1) +#define EC_FAN_DEBUG_TYPE_PWM BIT(2) +#define EC_MAX_FAN_CNT 2 +#define EC_FAN_NAME_SIZE 20 +#define EC_FAN_MAX_PWM 255 + +enum qcom_ec_sci_events { + EC_FAN1_STATUS_CHANGE_EVT = 0x30, + EC_FAN2_STATUS_CHANGE_EVT, + EC_FAN1_SPEED_CHANGE_EVT, + EC_FAN2_SPEED_CHANGE_EVT, + EC_NEW_LUT_SET_EVT, + EC_FAN_PROFILE_SWITCH_EVT, + EC_THERMISTOR_1_THRESHOLD_CROSS_EVT, + EC_THERMISTOR_2_THRESHOLD_CROSS_EVT, + EC_THERMISTOR_3_THRESHOLD_CROSS_EVT, + /* Reserved: 0x39 - 0x3c/0x3f */ + EC_RECOVERED_FROM_RESET_EVT = 0x3d, +}; + +struct qcom_ec_version { + u8 main_version; + u8 sub_version; + u8 test_version; +}; + +struct qcom_ec_thermal_cap { +#define EC_THERMAL_FAN_CNT(x) (FIELD_GET(GENMASK(1, 0), (x))) +#define EC_THERMAL_FAN_TYPE(x) (FIELD_GET(GENMASK(4, 2), (x))) +#define EC_THERMAL_THERMISTOR_MASK(x) (FIELD_GET(GENMASK(7, 0), (x))) + u8 fan_cnt; + u8 fan_type; + u8 thermistor_mask; +}; + +struct qcom_ec_cooling_dev { + struct thermal_cooling_device *cdev; + struct device *parent_dev; + u8 fan_id; + u8 state; +}; + +struct qcom_ec { + struct qcom_ec_cooling_dev *ec_cdev; + struct qcom_ec_thermal_cap thermal_cap; + struct qcom_ec_version version; + struct i2c_client *client; +}; + +static int qcom_ec_read(struct qcom_ec *ec, u8 cmd, u8 resp_len, u8 *resp) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(ec->client, cmd, resp_len, resp); + if (ret < 0) + return ret; + else if (ret == 0 || ret == 0xff) + return -EOPNOTSUPP; + + if (resp[0] >= resp_len) + return -EINVAL; + + return 0; +} + +/* + * EC Device Firmware Version: + * + * Read Response: + * ---------------------------------------------------------------------- + * | Offset | Name | Description | + * ---------------------------------------------------------------------- + * | 0x00 | Byte count | Number of bytes in response | + * | | | (excluding byte count) | + * ---------------------------------------------------------------------- + * | 0x01 | Test-version | Test-version of EC firmware | + * ---------------------------------------------------------------------- + * | 0x02 | Sub-version | Sub-version of EC firmware | + * ---------------------------------------------------------------------- + * | 0x03 | Main-version | Main-version of EC firmware | + * ---------------------------------------------------------------------- + * + */ +static int qcom_ec_read_fw_version(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qcom_ec *ec = i2c_get_clientdata(client); + struct qcom_ec_version *version = &ec->version; + u8 resp[EC_FW_VERSION_RESP_LEN]; + int ret; + + ret = qcom_ec_read(ec, EC_FW_VERSION_CMD, EC_FW_VERSION_RESP_LEN, resp); + if (ret < 0) + return ret; + + version->main_version = resp[3]; + version->sub_version = resp[2]; + version->test_version = resp[1]; + + dev_dbg(dev, "EC Version %d.%d.%d\n", + version->main_version, version->sub_version, version->test_version); + + return 0; +} + +/* + * EC Device Thermal Capabilities: + * + * Read Response: + * ------------------------------------------------------------------------------ + * | Offset | Name | Description | + * ------------------------------------------------------------------------------ + * | 0x00 | Byte count | Number of bytes in response | + * | | | (excluding byte count) | + * ------------------------------------------------------------------------------ + * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | + * | 0x03 | Capabilities | Bit 2-4: Type of fan | + * | | | Bit 5-6: Reserved | + * | | | Bit 7: Data Valid/Invalid | + * | | | (Valid - 1, Invalid - 0) | + * | | | Bit 8-15: Thermistor 0 - 7 presence | + * | | | (1 present, 0 absent) | + * ------------------------------------------------------------------------------ + * + */ +static int qcom_ec_thermal_capabilities(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qcom_ec *ec = i2c_get_clientdata(client); + struct qcom_ec_thermal_cap *cap = &ec->thermal_cap; + u8 resp[EC_THERMAL_CAP_RESP_LEN]; + int ret; + + ret = qcom_ec_read(ec, EC_THERMAL_CAP_CMD, EC_THERMAL_CAP_RESP_LEN, resp); + if (ret < 0) + return ret; + + cap->fan_cnt = min(EC_MAX_FAN_CNT, EC_THERMAL_FAN_CNT(resp[1])); + cap->fan_type = EC_THERMAL_FAN_TYPE(resp[1]); + cap->thermistor_mask = EC_THERMAL_THERMISTOR_MASK(resp[2]); + + dev_dbg(dev, "Fan count: %d Fan Type: %d Thermistor Mask: %x\n", + cap->fan_cnt, cap->fan_type, cap->thermistor_mask); + + return 0; +} + +static irqreturn_t qcom_ec_irq(int irq, void *data) +{ + struct qcom_ec *ec = data; + struct device *dev = &ec->client->dev; + int val; + + val = i2c_smbus_read_byte_data(ec->client, EC_SCI_EVT_READ_CMD); + if (val < 0) { + dev_err_ratelimited(dev, "Failed to read EC SCI Event: %d\n", val); + return IRQ_HANDLED; + } + + switch (val) { + case EC_FAN1_STATUS_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan1 status changed\n"); + break; + case EC_FAN2_STATUS_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan2 status changed\n"); + break; + case EC_FAN1_SPEED_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan1 speed crossed low/high trip point\n"); + break; + case EC_FAN2_SPEED_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan2 speed crossed low/high trip point\n"); + break; + case EC_NEW_LUT_SET_EVT: + dev_dbg_ratelimited(dev, "New LUT set\n"); + break; + case EC_FAN_PROFILE_SWITCH_EVT: + dev_dbg_ratelimited(dev, "FAN Profile switched\n"); + break; + case EC_THERMISTOR_1_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 1 threshold crossed\n"); + break; + case EC_THERMISTOR_2_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 2 threshold crossed\n"); + break; + case EC_THERMISTOR_3_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 3 threshold crossed\n"); + break; + case EC_RECOVERED_FROM_RESET_EVT: + dev_dbg_ratelimited(dev, "EC recovered from reset\n"); + break; + default: + dev_notice_ratelimited(dev, "Unknown EC event: %d\n", val); + break; + } + + return IRQ_HANDLED; +} + +static int qcom_ec_sci_evt_control(struct device *dev, bool enable) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_SCI_EVT_CONTROL_CMD, enable ? 1 : 0); +} + +static int qcom_ec_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + *state = EC_FAN_MAX_PWM; + + return 0; +} + +static int qcom_ec_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata; + + *state = ec_cdev->state; + + return 0; +} + +/* + * Fan Debug control command: + * + * Command Payload: + * -------------------------------------------------------------------------------------- + * | Offset | Name | Description | + * -------------------------------------------------------------------------------------- + * | 0x00 | Command | Fan control command | + * -------------------------------------------------------------------------------------- + * | 0x01 | Fan ID | 0x1 : Fan 1 | + * | | | 0x2 : Fan 2 | + * -------------------------------------------------------------------------------------- + * | 0x02 | Byte count = 4| Size of data to set fan speed | + * -------------------------------------------------------------------------------------- + * | 0x03 | Mode | Bit 0: Debug Mode On/Off (0 - OFF, 1 - ON ) | + * | | | Bit 1: Fan On/Off (0 - Off, 1 - ON) | + * | | | Bit 2: Debug Type (0 - RPM, 1 - PWM) | + * -------------------------------------------------------------------------------------- + * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | + * | 0x05 | | | + * -------------------------------------------------------------------------------------- + * | 0x06 | Speed in PWM | PWM value, if mode selected is PWM (0 - 255) | + * ______________________________________________________________________________________ + * + */ +static int qcom_ec_fan_debug_mode_off(struct qcom_ec_cooling_dev *ec_cdev) +{ + struct device *dev = ec_cdev->parent_dev; + struct i2c_client *client = to_i2c_client(dev); + u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE, + EC_FAN_DEBUG_MODE_OFF, 0, 0, 0 }; + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD, + sizeof(request), request); + if (ret) { + dev_err(dev, "Failed to turn off fan%d debug mode: %d\n", + ec_cdev->fan_id, ret); + } + + return ret; +} + +static int qcom_ec_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata; + struct device *dev = ec_cdev->parent_dev; + struct i2c_client *client = to_i2c_client(dev); + u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE, + EC_FAN_DEBUG_MODE_ON | EC_FAN_ON | EC_FAN_DEBUG_TYPE_PWM, + 0, 0, state }; + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD, + sizeof(request), request); + if (ret) { + dev_err(dev, "Failed to set fan pwm: %d\n", ret); + return ret; + } + + ec_cdev->state = state; + + return 0; +} + +static const struct thermal_cooling_device_ops qcom_ec_thermal_ops = { + .get_max_state = qcom_ec_fan_get_max_state, + .get_cur_state = qcom_ec_fan_get_cur_state, + .set_cur_state = qcom_ec_fan_set_cur_state, +}; + +static int qcom_ec_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD, + EC_MODERN_STANDBY_EXIT); +} + +static int qcom_ec_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD, + EC_MODERN_STANDBY_ENTER); +} + +static int qcom_ec_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qcom_ec *ec; + unsigned int i; + int ret; + + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); + if (!ec) + return -ENOMEM; + + ec->client = client; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, qcom_ec_irq, + IRQF_ONESHOT, "qcom_ec", ec); + if (ret < 0) + return ret; + + i2c_set_clientdata(client, ec); + + ret = qcom_ec_read_fw_version(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read EC firmware version\n"); + + ret = qcom_ec_sci_evt_control(dev, true); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable SCI events\n"); + + ret = qcom_ec_thermal_capabilities(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read thermal capabilities\n"); + + if (ec->thermal_cap.fan_cnt == 0) { + dev_warn(dev, FW_BUG "Failed to get fan count, firmware update required\n"); + return 0; + } + + ec->ec_cdev = devm_kcalloc(dev, ec->thermal_cap.fan_cnt, sizeof(*ec->ec_cdev), GFP_KERNEL); + if (!ec->ec_cdev) + return -ENOMEM; + + for (i = 0; i < ec->thermal_cap.fan_cnt; i++) { + struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i]; + char name[EC_FAN_NAME_SIZE]; + + scnprintf(name, sizeof(name), "qcom_ec_fan_%u", i); + ec_cdev->fan_id = i + 1; + ec_cdev->parent_dev = dev; + + ec_cdev->cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, ec_cdev, + &qcom_ec_thermal_ops); + if (IS_ERR(ec_cdev->cdev)) { + return dev_err_probe(dev, PTR_ERR(ec_cdev->cdev), + "Failed to register fan%d cooling device\n", i); + } + } + + return 0; +} + +static void qcom_ec_remove(struct i2c_client *client) +{ + struct qcom_ec *ec = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int ret; + + ret = qcom_ec_sci_evt_control(dev, false); + if (ret < 0) + dev_err(dev, "Failed to disable SCI events: %d\n", ret); + + for (int i = 0; i < ec->thermal_cap.fan_cnt; i++) { + struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i]; + + qcom_ec_fan_debug_mode_off(ec_cdev); + } +} + +static const struct of_device_id qcom_ec_of_match[] = { + { .compatible = "qcom,hamoa-crd-ec" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_ec_of_match); + +static const struct i2c_device_id qcom_ec_i2c_id_table[] = { + { "qcom-hamoa-ec", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, qcom_ec_i2c_id_table); + +static DEFINE_SIMPLE_DEV_PM_OPS(qcom_ec_pm_ops, + qcom_ec_suspend, + qcom_ec_resume); + +static struct i2c_driver qcom_ec_i2c_driver = { + .driver = { + .name = "qcom-hamoa-ec", + .of_match_table = qcom_ec_of_match, + .pm = &qcom_ec_pm_ops, + }, + .probe = qcom_ec_probe, + .remove = qcom_ec_remove, + .id_table = qcom_ec_i2c_id_table, +}; +module_i2c_driver(qcom_ec_i2c_driver); + +MODULE_DESCRIPTION("QCOM Hamoa Embedded Controller"); +MODULE_LICENSE("GPL"); From 3b95f36464ec161bcf14c5d1e5f9d5d5e9464582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 7 May 2026 17:50:25 +0200 Subject: [PATCH 0786/5214] platform/x86: pmc_atom: Use named initializer for pci_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being more verbose using a named initializer yields easier to understand code and doesn't rely on the two hidden zeros in the PCI_VDEVICE macro. This doesn't introduce any changes to the compiled result of the array, which was confirmed with an ARCH=x86 build. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260507155025.3186956-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pmc_atom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index 48c2a0e59d18..046933f6d1b6 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -570,8 +570,8 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) /* Data for PCI driver interface used by pci_match_id() call below */ static const struct pci_device_id pmc_pci_ids[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), .driver_data = (kernel_ulong_t)&byt_data }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), .driver_data = (kernel_ulong_t)&cht_data }, {} }; From 4d97a37e2cedb008eb39354b2a4e2af9ae8bdbe7 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Sun, 12 Apr 2026 16:50:08 +0300 Subject: [PATCH 0787/5214] ecryptfs: remove redundant variable found_auth_tok The found_auth_tok variable is no longer needed, as the fact of finding a token is determined directly by jumping to the found_matching_auth_tok label inside the loop. Remove found_auth_tok, simplifying the function logic. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Alexey Velichayshiy [tyhicks: Unsplit log message string] Signed-off-by: Tyler Hicks --- fs/ecryptfs/keystore.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 0be746493e56..ebebc9551f1f 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1718,7 +1718,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, struct dentry *ecryptfs_dentry) { size_t i = 0; - size_t found_auth_tok; size_t next_packet_is_auth_tok_packet; LIST_HEAD(auth_tok_list); struct ecryptfs_auth_tok *matching_auth_tok; @@ -1822,7 +1821,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, * the metadata. There may be several potential matches, but * just one will be sufficient to decrypt to get the FEK. */ find_next_matching_auth_tok: - found_auth_tok = 0; list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) { candidate_auth_tok = &auth_tok_list_item->auth_tok; if (unlikely(ecryptfs_verbosity > 0)) { @@ -1843,17 +1841,13 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, &matching_auth_tok, crypt_stat->mount_crypt_stat, candidate_auth_tok_sig); - if (!rc) { - found_auth_tok = 1; + if (!rc) goto found_matching_auth_tok; - } - } - if (!found_auth_tok) { - ecryptfs_printk(KERN_ERR, "Could not find a usable " - "authentication token\n"); - rc = -EIO; - goto out_wipe_list; } + ecryptfs_printk(KERN_ERR, + "Could not find a usable authentication token\n"); + rc = -EIO; + goto out_wipe_list; found_matching_auth_tok: if (candidate_auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) { memcpy(&(candidate_auth_tok->token.private_key), From f785606039fdc8c4de2908dfe9bd672de15a76ce Mon Sep 17 00:00:00 2001 From: Zhiyong Tao Date: Thu, 23 Apr 2026 14:22:45 +0800 Subject: [PATCH 0788/5214] serial: 8250_mtk: Add ACPI support Add ACPI support to 8250_mtk driver. This makes it possible to use UART on ARM-based desktops with EDK2 UEFI firmware. Signed-off-by: Yenchia Chen Signed-off-by: Zhiyong Tao Link: https://patch.msgid.link/20260423062345.2473300-2-zhiyong.tao@mediatek.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 5875a7b9b4b1..e6a56cf54ae0 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "8250.h" @@ -521,6 +522,7 @@ static int mtk8250_probe(struct platform_device *pdev) struct mtk8250_data *data; struct resource *regs; int irq, err; + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -543,12 +545,13 @@ static int mtk8250_probe(struct platform_device *pdev) data->clk_count = 0; - if (pdev->dev.of_node) { + if (is_of_node(fwnode)) { err = mtk8250_probe_of(pdev, &uart.port, data); if (err) return err; - } else + } else if (!fwnode) { return -ENODEV; + } spin_lock_init(&uart.port.lock); uart.port.mapbase = regs->start; @@ -564,14 +567,18 @@ static int mtk8250_probe(struct platform_device *pdev) uart.port.startup = mtk8250_startup; uart.port.set_termios = mtk8250_set_termios; uart.port.uartclk = clk_get_rate(data->uart_clk); + if (!uart.port.uartclk) + uart.port.uartclk = 26 * HZ_PER_MHZ; #ifdef CONFIG_SERIAL_8250_DMA if (data->dma) uart.dma = data->dma; #endif - /* Disable Rate Fix function */ - writel(0x0, uart.port.membase + + if (is_of_node(fwnode)) { + /* Disable Rate Fix function */ + writel(0x0, uart.port.membase + (MTK_UART_RATE_FIX << uart.port.regshift)); + } platform_set_drvdata(pdev, data); @@ -649,11 +656,19 @@ static const struct of_device_id mtk8250_of_match[] = { }; MODULE_DEVICE_TABLE(of, mtk8250_of_match); +static const struct acpi_device_id mtk8250_acpi_match[] = { + { "MTKI0511" }, + { "NVDA0240" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, mtk8250_acpi_match); + static struct platform_driver mtk8250_platform_driver = { .driver = { .name = "mt6577-uart", .pm = &mtk8250_pm_ops, .of_match_table = mtk8250_of_match, + .acpi_match_table = mtk8250_acpi_match, }, .probe = mtk8250_probe, .remove = mtk8250_remove, From 455d4ed5ea5dbdd690f96b5e0582d760cbfc9e8d Mon Sep 17 00:00:00 2001 From: "Nikola Z. Ivanov" Date: Mon, 27 Apr 2026 00:42:50 +0300 Subject: [PATCH 0789/5214] serial: mxs-auart: replace hardcoded 1 with predefined macro GPIO_LINE_DIRECTION_IN The GPIO_LINE_DIRECTION_* definitions have just recently been exposed to gpio consumers.h by breaking them out in a separate defs.h file. Use this to validate the gpio direction instead of the hard-coded literal. Signed-off-by: Nikola Z. Ivanov Link: https://patch.msgid.link/20260426214250.781151-1-zlatistiv@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 693b491f1e75..697318dbb146 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1520,7 +1520,7 @@ static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) for (i = 0; i < UART_GPIO_MAX; i++) { gpiod = mctrl_gpio_to_gpiod(s->gpios, i); - if (gpiod && (gpiod_get_direction(gpiod) == 1)) + if (gpiod && (gpiod_get_direction(gpiod) == GPIO_LINE_DIRECTION_IN)) s->gpio_irq[i] = gpiod_to_irq(gpiod); else s->gpio_irq[i] = -EINVAL; From a23907d034dbe92915b7af2de18c5d7faac0c0b1 Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 09:01:14 +0800 Subject: [PATCH 0790/5214] dt-bindings: serial: Add compatible for Qualcomm SA8797P SoC Document QUP GENI UART controller on Qualcomm Nord SA8797P SoC which is compatible with SA8255P controller. Signed-off-by: Deepti Jaggi Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260427010114.230341-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/qcom,sa8255p-geni-uart.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml b/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml index c8f01923cb25..8496f822dfa5 100644 --- a/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml +++ b/Documentation/devicetree/bindings/serial/qcom,sa8255p-geni-uart.yaml @@ -14,9 +14,16 @@ allOf: properties: compatible: - enum: - - qcom,sa8255p-geni-uart - - qcom,sa8255p-geni-debug-uart + oneOf: + - enum: + - qcom,sa8255p-geni-uart + - qcom,sa8255p-geni-debug-uart + - items: + - const: qcom,sa8797p-geni-uart + - const: qcom,sa8255p-geni-uart + - items: + - const: qcom,sa8797p-geni-debug-uart + - const: qcom,sa8255p-geni-debug-uart reg: maxItems: 1 From 49fa670efdfdd27e29145cbd2beef065c08717ef Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Thu, 30 Apr 2026 15:58:22 +0200 Subject: [PATCH 0791/5214] serial: 8250_port: recognize UPIO_AU My MIPS Alchemy systems generate the following warning during probe of the 8250 driver: WARNING: drivers/tty/serial/8250/8250_port.c:462 at set_io_from_upio+0xfc/0x124, CPU#0: swapper/0/1 Unsupported UART type 4 [...] [<80521d40>] set_io_from_upio+0xfc/0x124 [<80521dfc>] serial8250_set_defaults+0x94/0xe0 [<80520fb4>] serial8250_register_8250_port+0x288/0x51c [<805214ec>] serial8250_probe+0x160/0x1e8 [<8053b5f0>] platform_probe+0x64/0x90 The least invasive fix is to recognize UPIO_AU (type 4) in set_io_from_upio() and do nothing, since all parameters have already been set up in 8250_rt288x.c::au_platform_setup(). Run-tested on Alchemy Au1300 platform. Signed-off-by: Manuel Lauss Link: https://patch.msgid.link/20260430135822.905035-1-manuel.lauss@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index af78cc02f38e..02e6cdefdbac 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -458,6 +458,8 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = io_serial_out; break; #endif + case UPIO_AU: + break; default: WARN(p->iotype != UPIO_PORT || p->iobase, "Unsupported UART type %x\n", p->iotype); From 61848e9799f2543a3ea144e277b17aaec9707566 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sun, 3 May 2026 20:14:53 -0700 Subject: [PATCH 0792/5214] tty: synclink_gt: remove broken driver The synclink_gt driver was marked as broken in commit 426263d5fb40 ("tty: synclink_gt: mark as BROKEN") in July 2023 because it had severe structural problems and there had been no evidence of users since 2016. Since then, no meaningful improvements have been made to the driver, and it is unlikely that will ever happen due to the lack of interest. Drop the driver and references to it in comments and documentation. include/uapi/linux/synclink.h is also removed. The only use of this header I have found is the linux-raw-sys Rust crate. It generates bindings for all UAPI headers, but has a hardcoded list of headers and ioctls, including this one, so that does not indicate that anyone is using it. I have sent a pull request to remove the include and ioctl definitions for this header (see the link below). Link: https://github.com/sunfishcode/linux-raw-sys/pull/185 Signed-off-by: Ethan Nelson-Moore Acked-by: Jakub Kicinski Link: https://patch.msgid.link/20260504031519.18877-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../userspace-api/ioctl/ioctl-number.rst | 1 - arch/powerpc/configs/ppc6xx_defconfig | 1 - drivers/net/ppp/Kconfig | 4 +- drivers/tty/Kconfig | 11 +- drivers/tty/Makefile | 1 - drivers/tty/n_hdlc.c | 7 - drivers/tty/synclink_gt.c | 5038 ----------------- include/linux/synclink.h | 37 - include/uapi/linux/synclink.h | 301 - 9 files changed, 3 insertions(+), 5398 deletions(-) delete mode 100644 drivers/tty/synclink_gt.c delete mode 100644 include/linux/synclink.h delete mode 100644 include/uapi/linux/synclink.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 331223761fff..58716f4f4834 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -271,7 +271,6 @@ Code Seq# Include File Comments 'm' 00-09 linux/mmtimer.h conflict! 'm' all linux/mtio.h conflict! 'm' all linux/soundcard.h conflict! -'m' all linux/synclink.h conflict! 'm' 00-19 drivers/message/fusion/mptctl.h conflict! 'm' 00 drivers/scsi/megaraid/megaraid_ioctl.h conflict! 'n' 00-7F linux/ncp_fs.h and fs/ncpfs/ioctl.c diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index ccabc6e17168..4ddd2c01b8b7 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -551,7 +551,6 @@ CONFIG_GAMEPORT_EMU10K1=m CONFIG_GAMEPORT_FM801=m # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_NONSTANDARD=y -CONFIG_SYNCLINK_GT=m CONFIG_NOZOMI=m CONFIG_N_HDLC=m CONFIG_SERIAL_8250=y diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index 753354b4e36c..5324e2102383 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -191,8 +191,8 @@ config PPP_SYNC_TTY tristate "PPP support for sync tty ports" help Say Y (or M) here if you want to be able to use PPP over synchronous - (HDLC) tty devices, such as the SyncLink adapter. These devices - are often used for high-speed leased lines like T1/E1. + (HDLC) tty devices. These devices are often used for high-speed leased + lines like T1/E1. To compile this driver as a module, choose M here. diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 149f3d53b760..df6832a4c237 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -231,21 +231,12 @@ config MOXA_SMARTIO This driver can also be built as a module. The module will be called mxser. If you want to do that, say M here. -config SYNCLINK_GT - tristate "SyncLink GT/AC support" - depends on SERIAL_NONSTANDARD && PCI - depends on BROKEN - help - Support for SyncLink GT and SyncLink AC families of - synchronous and asynchronous serial adapters - manufactured by Microgate Systems, Ltd. (www.microgate.com) - config N_HDLC tristate "HDLC line discipline support" depends on SERIAL_NONSTANDARD help Allows synchronous HDLC communications with tty device drivers that - support synchronous HDLC such as the Microgate SyncLink adapter. + support synchronous HDLC. This driver can be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 07aca5184a55..8ca1a0a2229f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o obj-$(CONFIG_NOZOMI) += nozomi.o obj-$(CONFIG_NULL_TTY) += ttynull.o -obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 98eefa2cede4..895b850a5950 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -4,8 +4,6 @@ * Written by Paul Fulghum paulkf@microgate.com * for Microgate Corporation * - * Microgate and SyncLink are registered trademarks of Microgate Corporation - * * Adapted from ppp.c, written by Michael Callahan , * Al Longyear , * Paul Mackerras @@ -54,11 +52,6 @@ * this line discipline (or another line discipline that is frame * oriented such as N_PPP). * - * The SyncLink driver (synclink.c) implements both asynchronous - * (using standard line discipline N_TTY) and synchronous HDLC - * (using N_HDLC) communications, with the latter using the above - * conventions. - * * This implementation is very basic and does not maintain * any statistics. The main point is to enforce the raw data * and frame orientation of HDLC communications. diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c deleted file mode 100644 index bf4f50e0ce94..000000000000 --- a/drivers/tty/synclink_gt.c +++ /dev/null @@ -1,5038 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ -/* - * Device driver for Microgate SyncLink GT serial adapters. - * - * written by Paul Fulghum for Microgate Corporation - * paulkf@microgate.com - * - * Microgate and SyncLink are trademarks of Microgate Corporation - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * DEBUG OUTPUT DEFINITIONS - * - * uncomment lines below to enable specific types of debug output - * - * DBGINFO information - most verbose output - * DBGERR serious errors - * DBGBH bottom half service routine debugging - * DBGISR interrupt service routine debugging - * DBGDATA output receive and transmit data - * DBGTBUF output transmit DMA buffers and registers - * DBGRBUF output receive DMA buffers and registers - */ - -#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt -#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt -#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt -#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt -#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label)) -/*#define DBGTBUF(info) dump_tbufs(info)*/ -/*#define DBGRBUF(info) dump_rbufs(info)*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE)) -#define SYNCLINK_GENERIC_HDLC 1 -#else -#define SYNCLINK_GENERIC_HDLC 0 -#endif - -/* - * module identification - */ -static const char driver_name[] = "SyncLink GT"; -static const char tty_dev_prefix[] = "ttySLG"; -MODULE_DESCRIPTION("Device driver for Microgate SyncLink GT serial adapters"); -MODULE_LICENSE("GPL"); -#define MAX_DEVICES 32 - -static const struct pci_device_id pci_table[] = { - { PCI_VDEVICE(MICROGATE, SYNCLINK_GT_DEVICE_ID) }, - { PCI_VDEVICE(MICROGATE, SYNCLINK_GT2_DEVICE_ID) }, - { PCI_VDEVICE(MICROGATE, SYNCLINK_GT4_DEVICE_ID) }, - { PCI_VDEVICE(MICROGATE, SYNCLINK_AC_DEVICE_ID) }, - { 0 }, /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, pci_table); - -static int init_one(struct pci_dev *dev,const struct pci_device_id *ent); -static void remove_one(struct pci_dev *dev); -static struct pci_driver pci_driver = { - .name = "synclink_gt", - .id_table = pci_table, - .probe = init_one, - .remove = remove_one, -}; - -static bool pci_registered; - -/* - * module configuration and status - */ -static struct slgt_info *slgt_device_list; -static int slgt_device_count; - -static int ttymajor; -static int debug_level; -static int maxframe[MAX_DEVICES]; - -module_param(ttymajor, int, 0); -module_param(debug_level, int, 0); -module_param_array(maxframe, int, NULL, 0); - -MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned"); -MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail"); -MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)"); - -/* - * tty support and callbacks - */ -static struct tty_driver *serial_driver; - -static void wait_until_sent(struct tty_struct *tty, int timeout); -static void flush_buffer(struct tty_struct *tty); -static void tx_release(struct tty_struct *tty); - -/* - * generic HDLC support - */ -#define dev_to_port(D) (dev_to_hdlc(D)->priv) - - -/* - * device specific structures, macros and functions - */ - -#define SLGT_MAX_PORTS 4 -#define SLGT_REG_SIZE 256 - -/* - * conditional wait facility - */ -struct cond_wait { - struct cond_wait *next; - wait_queue_head_t q; - wait_queue_entry_t wait; - unsigned int data; -}; -static void flush_cond_wait(struct cond_wait **head); - -/* - * DMA buffer descriptor and access macros - */ -struct slgt_desc -{ - __le16 count; - __le16 status; - __le32 pbuf; /* physical address of data buffer */ - __le32 next; /* physical address of next descriptor */ - - /* driver book keeping */ - char *buf; /* virtual address of data buffer */ - unsigned int pdesc; /* physical address of this descriptor */ - dma_addr_t buf_dma_addr; - unsigned short buf_count; -}; - -#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) -#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b)) -#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b)) -#define set_desc_eof(a,b) (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0)) -#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b)) -#define desc_count(a) (le16_to_cpu((a).count)) -#define desc_status(a) (le16_to_cpu((a).status)) -#define desc_complete(a) (le16_to_cpu((a).status) & BIT15) -#define desc_eof(a) (le16_to_cpu((a).status) & BIT2) -#define desc_crc_error(a) (le16_to_cpu((a).status) & BIT1) -#define desc_abort(a) (le16_to_cpu((a).status) & BIT0) -#define desc_residue(a) ((le16_to_cpu((a).status) & 0x38) >> 3) - -struct _input_signal_events { - int ri_up; - int ri_down; - int dsr_up; - int dsr_down; - int dcd_up; - int dcd_down; - int cts_up; - int cts_down; -}; - -/* - * device instance data structure - */ -struct slgt_info { - void *if_ptr; /* General purpose pointer (used by SPPP) */ - struct tty_port port; - - struct slgt_info *next_device; /* device list link */ - - char device_name[25]; - struct pci_dev *pdev; - - int port_count; /* count of ports on adapter */ - int adapter_num; /* adapter instance number */ - int port_num; /* port instance number */ - - /* array of pointers to port contexts on this adapter */ - struct slgt_info *port_array[SLGT_MAX_PORTS]; - - int line; /* tty line instance number */ - - struct mgsl_icount icount; - - int timeout; - int x_char; /* xon/xoff character */ - unsigned int read_status_mask; - unsigned int ignore_status_mask; - - wait_queue_head_t status_event_wait_q; - wait_queue_head_t event_wait_q; - struct timer_list tx_timer; - struct timer_list rx_timer; - - unsigned int gpio_present; - struct cond_wait *gpio_wait_q; - - spinlock_t lock; /* spinlock for synchronizing with ISR */ - - struct work_struct task; - u32 pending_bh; - bool bh_requested; - bool bh_running; - - int isr_overflow; - bool irq_requested; /* true if IRQ requested */ - bool irq_occurred; /* for diagnostics use */ - - /* device configuration */ - - unsigned int bus_type; - unsigned int irq_level; - unsigned long irq_flags; - - unsigned char __iomem * reg_addr; /* memory mapped registers address */ - u32 phys_reg_addr; - bool reg_addr_requested; - - MGSL_PARAMS params; /* communications parameters */ - u32 idle_mode; - u32 max_frame_size; /* as set by device config */ - - unsigned int rbuf_fill_level; - unsigned int rx_pio; - unsigned int if_mode; - unsigned int base_clock; - unsigned int xsync; - unsigned int xctrl; - - /* device status */ - - bool rx_enabled; - bool rx_restart; - - bool tx_enabled; - bool tx_active; - - unsigned char signals; /* serial signal states */ - int init_error; /* initialization error */ - - unsigned char *tx_buf; - int tx_count; - - bool drop_rts_on_tx_done; - struct _input_signal_events input_signal_events; - - int dcd_chkcount; /* check counts to prevent */ - int cts_chkcount; /* too many IRQs if a signal */ - int dsr_chkcount; /* is floating */ - int ri_chkcount; - - char *bufs; /* virtual address of DMA buffer lists */ - dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */ - - unsigned int rbuf_count; - struct slgt_desc *rbufs; - unsigned int rbuf_current; - unsigned int rbuf_index; - unsigned int rbuf_fill_index; - unsigned short rbuf_fill_count; - - unsigned int tbuf_count; - struct slgt_desc *tbufs; - unsigned int tbuf_current; - unsigned int tbuf_start; - - unsigned char *tmp_rbuf; - unsigned int tmp_rbuf_count; - - /* SPPP/Cisco HDLC device parts */ - - int netcount; - spinlock_t netlock; -#if SYNCLINK_GENERIC_HDLC - struct net_device *netdev; -#endif - -}; - -static const MGSL_PARAMS default_params = { - .mode = MGSL_MODE_HDLC, - .loopback = 0, - .flags = HDLC_FLAG_UNDERRUN_ABORT15, - .encoding = HDLC_ENCODING_NRZI_SPACE, - .clock_speed = 0, - .addr_filter = 0xff, - .crc_type = HDLC_CRC_16_CCITT, - .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS, - .preamble = HDLC_PREAMBLE_PATTERN_NONE, - .data_rate = 9600, - .data_bits = 8, - .stop_bits = 1, - .parity = ASYNC_PARITY_NONE -}; - - -#define BH_RECEIVE 1 -#define BH_TRANSMIT 2 -#define BH_STATUS 4 -#define IO_PIN_SHUTDOWN_LIMIT 100 - -#define DMABUFSIZE 256 -#define DESC_LIST_SIZE 4096 - -#define MASK_PARITY BIT1 -#define MASK_FRAMING BIT0 -#define MASK_BREAK BIT14 -#define MASK_OVERRUN BIT4 - -#define GSR 0x00 /* global status */ -#define JCR 0x04 /* JTAG control */ -#define IODR 0x08 /* GPIO direction */ -#define IOER 0x0c /* GPIO interrupt enable */ -#define IOVR 0x10 /* GPIO value */ -#define IOSR 0x14 /* GPIO interrupt status */ -#define TDR 0x80 /* tx data */ -#define RDR 0x80 /* rx data */ -#define TCR 0x82 /* tx control */ -#define TIR 0x84 /* tx idle */ -#define TPR 0x85 /* tx preamble */ -#define RCR 0x86 /* rx control */ -#define VCR 0x88 /* V.24 control */ -#define CCR 0x89 /* clock control */ -#define BDR 0x8a /* baud divisor */ -#define SCR 0x8c /* serial control */ -#define SSR 0x8e /* serial status */ -#define RDCSR 0x90 /* rx DMA control/status */ -#define TDCSR 0x94 /* tx DMA control/status */ -#define RDDAR 0x98 /* rx DMA descriptor address */ -#define TDDAR 0x9c /* tx DMA descriptor address */ -#define XSR 0x40 /* extended sync pattern */ -#define XCR 0x44 /* extended control */ - -#define RXIDLE BIT14 -#define RXBREAK BIT14 -#define IRQ_TXDATA BIT13 -#define IRQ_TXIDLE BIT12 -#define IRQ_TXUNDER BIT11 /* HDLC */ -#define IRQ_RXDATA BIT10 -#define IRQ_RXIDLE BIT9 /* HDLC */ -#define IRQ_RXBREAK BIT9 /* async */ -#define IRQ_RXOVER BIT8 -#define IRQ_DSR BIT7 -#define IRQ_CTS BIT6 -#define IRQ_DCD BIT5 -#define IRQ_RI BIT4 -#define IRQ_ALL 0x3ff0 -#define IRQ_MASTER BIT0 - -#define slgt_irq_on(info, mask) \ - wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask))) -#define slgt_irq_off(info, mask) \ - wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask))) - -static __u8 rd_reg8(struct slgt_info *info, unsigned int addr); -static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value); -static __u16 rd_reg16(struct slgt_info *info, unsigned int addr); -static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value); -static __u32 rd_reg32(struct slgt_info *info, unsigned int addr); -static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value); - -static void msc_set_vcr(struct slgt_info *info); - -static int startup_hw(struct slgt_info *info); -static int block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info); -static void shutdown_hw(struct slgt_info *info); -static void program_hw(struct slgt_info *info); -static void change_params(struct slgt_info *info); - -static int adapter_test(struct slgt_info *info); - -static void reset_port(struct slgt_info *info); -static void async_mode(struct slgt_info *info); -static void sync_mode(struct slgt_info *info); - -static void rx_stop(struct slgt_info *info); -static void rx_start(struct slgt_info *info); -static void reset_rbufs(struct slgt_info *info); -static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last); -static bool rx_get_frame(struct slgt_info *info); -static bool rx_get_buf(struct slgt_info *info); - -static void tx_start(struct slgt_info *info); -static void tx_stop(struct slgt_info *info); -static void tx_set_idle(struct slgt_info *info); -static unsigned int tbuf_bytes(struct slgt_info *info); -static void reset_tbufs(struct slgt_info *info); -static void tdma_reset(struct slgt_info *info); -static bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int count); - -static void get_gtsignals(struct slgt_info *info); -static void set_gtsignals(struct slgt_info *info); -static void set_rate(struct slgt_info *info, u32 data_rate); - -static void bh_transmit(struct slgt_info *info); -static void isr_txeom(struct slgt_info *info, unsigned short status); - -static void tx_timeout(struct timer_list *t); -static void rx_timeout(struct timer_list *t); - -/* - * ioctl handlers - */ -static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount); -static int get_params(struct slgt_info *info, MGSL_PARAMS __user *params); -static int set_params(struct slgt_info *info, MGSL_PARAMS __user *params); -static int get_txidle(struct slgt_info *info, int __user *idle_mode); -static int set_txidle(struct slgt_info *info, int idle_mode); -static int tx_enable(struct slgt_info *info, int enable); -static int tx_abort(struct slgt_info *info); -static int rx_enable(struct slgt_info *info, int enable); -static int modem_input_wait(struct slgt_info *info,int arg); -static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); -static int get_interface(struct slgt_info *info, int __user *if_mode); -static int set_interface(struct slgt_info *info, int if_mode); -static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); -static int get_xsync(struct slgt_info *info, int __user *if_mode); -static int set_xsync(struct slgt_info *info, int if_mode); -static int get_xctrl(struct slgt_info *info, int __user *if_mode); -static int set_xctrl(struct slgt_info *info, int if_mode); - -/* - * driver functions - */ -static void release_resources(struct slgt_info *info); - -/* - * DEBUG OUTPUT CODE - */ -#ifndef DBGINFO -#define DBGINFO(fmt) -#endif -#ifndef DBGERR -#define DBGERR(fmt) -#endif -#ifndef DBGBH -#define DBGBH(fmt) -#endif -#ifndef DBGISR -#define DBGISR(fmt) -#endif - -#ifdef DBGDATA -static void trace_block(struct slgt_info *info, const char *data, int count, const char *label) -{ - int i; - int linecount; - printk("%s %s data:\n",info->device_name, label); - while(count) { - linecount = (count > 16) ? 16 : count; - for(i=0; i < linecount; i++) - printk("%02X ",(unsigned char)data[i]); - for(;i<17;i++) - printk(" "); - for(i=0;i=040 && data[i]<=0176) - printk("%c",data[i]); - else - printk("."); - } - printk("\n"); - data += linecount; - count -= linecount; - } -} -#else -#define DBGDATA(info, buf, size, label) -#endif - -#ifdef DBGTBUF -static void dump_tbufs(struct slgt_info *info) -{ - int i; - printk("tbuf_current=%d\n", info->tbuf_current); - for (i=0 ; i < info->tbuf_count ; i++) { - printk("%d: count=%04X status=%04X\n", - i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status)); - } -} -#else -#define DBGTBUF(info) -#endif - -#ifdef DBGRBUF -static void dump_rbufs(struct slgt_info *info) -{ - int i; - printk("rbuf_current=%d\n", info->rbuf_current); - for (i=0 ; i < info->rbuf_count ; i++) { - printk("%d: count=%04X status=%04X\n", - i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status)); - } -} -#else -#define DBGRBUF(info) -#endif - -static inline int sanity_check(struct slgt_info *info, char *devname, const char *name) -{ -#ifdef SANITY_CHECK - if (!info) { - printk("null struct slgt_info for (%s) in %s\n", devname, name); - return 1; - } -#else - if (!info) - return 1; -#endif - return 0; -} - -/* - * line discipline callback wrappers - * - * The wrappers maintain line discipline references - * while calling into the line discipline. - * - * ldisc_receive_buf - pass receive data to line discipline - */ -static void ldisc_receive_buf(struct tty_struct *tty, - const __u8 *data, char *flags, int count) -{ - struct tty_ldisc *ld; - if (!tty) - return; - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, data, flags, count); - tty_ldisc_deref(ld); - } -} - -/* tty callbacks */ - -static int open(struct tty_struct *tty, struct file *filp) -{ - struct slgt_info *info; - int retval, line; - unsigned long flags; - - line = tty->index; - if (line >= slgt_device_count) { - DBGERR(("%s: open with invalid line #%d.\n", driver_name, line)); - return -ENODEV; - } - - info = slgt_device_list; - while(info && info->line != line) - info = info->next_device; - if (sanity_check(info, tty->name, "open")) - return -ENODEV; - if (info->init_error) { - DBGERR(("%s init error=%d\n", info->device_name, info->init_error)); - return -ENODEV; - } - - tty->driver_data = info; - info->port.tty = tty; - - DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); - - mutex_lock(&info->port.mutex); - - spin_lock_irqsave(&info->netlock, flags); - if (info->netcount) { - retval = -EBUSY; - spin_unlock_irqrestore(&info->netlock, flags); - mutex_unlock(&info->port.mutex); - goto cleanup; - } - info->port.count++; - spin_unlock_irqrestore(&info->netlock, flags); - - if (info->port.count == 1) { - /* 1st open on this device, init hardware */ - retval = startup_hw(info); - if (retval < 0) { - mutex_unlock(&info->port.mutex); - goto cleanup; - } - } - mutex_unlock(&info->port.mutex); - retval = block_til_ready(tty, filp, info); - if (retval) { - DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval)); - goto cleanup; - } - - retval = 0; - -cleanup: - if (retval) { - if (tty->count == 1) - info->port.tty = NULL; /* tty layer will release tty struct */ - if(info->port.count) - info->port.count--; - } - - DBGINFO(("%s open rc=%d\n", info->device_name, retval)); - return retval; -} - -static void close(struct tty_struct *tty, struct file *filp) -{ - struct slgt_info *info = tty->driver_data; - - if (sanity_check(info, tty->name, "close")) - return; - DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count)); - - if (tty_port_close_start(&info->port, tty, filp) == 0) - goto cleanup; - - mutex_lock(&info->port.mutex); - if (tty_port_initialized(&info->port)) - wait_until_sent(tty, info->timeout); - flush_buffer(tty); - tty_ldisc_flush(tty); - - shutdown_hw(info); - mutex_unlock(&info->port.mutex); - - tty_port_close_end(&info->port, tty); - info->port.tty = NULL; -cleanup: - DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count)); -} - -static void hangup(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "hangup")) - return; - DBGINFO(("%s hangup\n", info->device_name)); - - flush_buffer(tty); - - mutex_lock(&info->port.mutex); - shutdown_hw(info); - - spin_lock_irqsave(&info->port.lock, flags); - info->port.count = 0; - info->port.tty = NULL; - spin_unlock_irqrestore(&info->port.lock, flags); - tty_port_set_active(&info->port, false); - mutex_unlock(&info->port.mutex); - - wake_up_interruptible(&info->port.open_wait); -} - -static void set_termios(struct tty_struct *tty, - const struct ktermios *old_termios) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - DBGINFO(("%s set_termios\n", tty->driver->name)); - - change_params(info); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { - info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - spin_lock_irqsave(&info->lock,flags); - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { - info->signals |= SerialSignal_DTR; - if (!C_CRTSCTS(tty) || !tty_throttled(tty)) - info->signals |= SerialSignal_RTS; - spin_lock_irqsave(&info->lock,flags); - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped = false; - tx_release(tty); - } -} - -static void update_tx_timer(struct slgt_info *info) -{ - /* - * use worst case speed of 1200bps to calculate transmit timeout - * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) - */ - if (info->params.mode == MGSL_MODE_HDLC) { - int timeout = (tbuf_bytes(info) * 7) + 1000; - mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); - } -} - -static ssize_t write(struct tty_struct *tty, const u8 *buf, size_t count) -{ - int ret = 0; - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "write")) - return -EIO; - - DBGINFO(("%s write count=%zu\n", info->device_name, count)); - - if (!info->tx_buf || (count > info->max_frame_size)) - return -EIO; - - if (!count || tty->flow.stopped || tty->hw_stopped) - return 0; - - spin_lock_irqsave(&info->lock, flags); - - if (info->tx_count) { - /* send accumulated data from send_char() */ - if (!tx_load(info, info->tx_buf, info->tx_count)) - goto cleanup; - info->tx_count = 0; - } - - if (tx_load(info, buf, count)) - ret = count; - -cleanup: - spin_unlock_irqrestore(&info->lock, flags); - DBGINFO(("%s write rc=%d\n", info->device_name, ret)); - return ret; -} - -static int put_char(struct tty_struct *tty, u8 ch) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - int ret = 0; - - if (sanity_check(info, tty->name, "put_char")) - return 0; - DBGINFO(("%s put_char(%u)\n", info->device_name, ch)); - if (!info->tx_buf) - return 0; - spin_lock_irqsave(&info->lock,flags); - if (info->tx_count < info->max_frame_size) { - info->tx_buf[info->tx_count++] = ch; - ret = 1; - } - spin_unlock_irqrestore(&info->lock,flags); - return ret; -} - -static void send_xchar(struct tty_struct *tty, char ch) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "send_xchar")) - return; - DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch)); - info->x_char = ch; - if (ch) { - spin_lock_irqsave(&info->lock,flags); - if (!info->tx_enabled) - tx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -static void wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct slgt_info *info = tty->driver_data; - unsigned long orig_jiffies, char_time; - - if (!info ) - return; - if (sanity_check(info, tty->name, "wait_until_sent")) - return; - DBGINFO(("%s wait_until_sent entry\n", info->device_name)); - if (!tty_port_initialized(&info->port)) - goto exit; - - orig_jiffies = jiffies; - - /* Set check interval to 1/5 of estimated time to - * send a character, and make it at least 1. The check - * interval should also be less than the timeout. - * Note: use tight timings here to satisfy the NIST-PCTS. - */ - - if (info->params.data_rate) { - char_time = info->timeout/(32 * 5); - if (!char_time) - char_time++; - } else - char_time = 1; - - if (timeout) - char_time = min_t(unsigned long, char_time, timeout); - - while (info->tx_active) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } -exit: - DBGINFO(("%s wait_until_sent exit\n", info->device_name)); -} - -static unsigned int write_room(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned int ret; - - if (sanity_check(info, tty->name, "write_room")) - return 0; - ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE; - DBGINFO(("%s write_room=%u\n", info->device_name, ret)); - return ret; -} - -static void flush_chars(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "flush_chars")) - return; - DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count)); - - if (info->tx_count <= 0 || tty->flow.stopped || - tty->hw_stopped || !info->tx_buf) - return; - - DBGINFO(("%s flush_chars start transmit\n", info->device_name)); - - spin_lock_irqsave(&info->lock,flags); - if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock,flags); -} - -static void flush_buffer(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "flush_buffer")) - return; - DBGINFO(("%s flush_buffer\n", info->device_name)); - - spin_lock_irqsave(&info->lock, flags); - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock, flags); - - tty_wakeup(tty); -} - -/* - * throttle (stop) transmitter - */ -static void tx_hold(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_hold")) - return; - DBGINFO(("%s tx_hold\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC) - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); -} - -/* - * release (start) transmitter - */ -static void tx_release(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "tx_release")) - return; - DBGINFO(("%s tx_release\n", info->device_name)); - spin_lock_irqsave(&info->lock, flags); - if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count)) - info->tx_count = 0; - spin_unlock_irqrestore(&info->lock, flags); -} - -/* - * Service an IOCTL request - * - * Arguments - * - * tty pointer to tty instance data - * cmd IOCTL command code - * arg command argument/context - * - * Return 0 if success, otherwise error code - */ -static int ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct slgt_info *info = tty->driver_data; - void __user *argp = (void __user *)arg; - int ret; - - if (sanity_check(info, tty->name, "ioctl")) - return -ENODEV; - DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); - - if (cmd != TIOCMIWAIT) { - if (tty_io_error(tty)) - return -EIO; - } - - switch (cmd) { - case MGSL_IOCWAITEVENT: - return wait_mgsl_event(info, argp); - case TIOCMIWAIT: - return modem_input_wait(info,(int)arg); - case MGSL_IOCSGPIO: - return set_gpio(info, argp); - case MGSL_IOCGGPIO: - return get_gpio(info, argp); - case MGSL_IOCWAITGPIO: - return wait_gpio(info, argp); - case MGSL_IOCGXSYNC: - return get_xsync(info, argp); - case MGSL_IOCSXSYNC: - return set_xsync(info, (int)arg); - case MGSL_IOCGXCTRL: - return get_xctrl(info, argp); - case MGSL_IOCSXCTRL: - return set_xctrl(info, (int)arg); - } - mutex_lock(&info->port.mutex); - switch (cmd) { - case MGSL_IOCGPARAMS: - ret = get_params(info, argp); - break; - case MGSL_IOCSPARAMS: - ret = set_params(info, argp); - break; - case MGSL_IOCGTXIDLE: - ret = get_txidle(info, argp); - break; - case MGSL_IOCSTXIDLE: - ret = set_txidle(info, (int)arg); - break; - case MGSL_IOCTXENABLE: - ret = tx_enable(info, (int)arg); - break; - case MGSL_IOCRXENABLE: - ret = rx_enable(info, (int)arg); - break; - case MGSL_IOCTXABORT: - ret = tx_abort(info); - break; - case MGSL_IOCGSTATS: - ret = get_stats(info, argp); - break; - case MGSL_IOCGIF: - ret = get_interface(info, argp); - break; - case MGSL_IOCSIF: - ret = set_interface(info,(int)arg); - break; - default: - ret = -ENOIOCTLCMD; - } - mutex_unlock(&info->port.mutex); - return ret; -} - -static int get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) - -{ - struct slgt_info *info = tty->driver_data; - struct mgsl_icount cnow; /* kernel counter temps */ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->lock,flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - -/* - * support for 32 bit ioctl calls on 64 bit systems - */ -#ifdef CONFIG_COMPAT -static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) -{ - struct MGSL_PARAMS32 tmp_params; - - DBGINFO(("%s get_params32\n", info->device_name)); - memset(&tmp_params, 0, sizeof(tmp_params)); - tmp_params.mode = (compat_ulong_t)info->params.mode; - tmp_params.loopback = info->params.loopback; - tmp_params.flags = info->params.flags; - tmp_params.encoding = info->params.encoding; - tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; - tmp_params.addr_filter = info->params.addr_filter; - tmp_params.crc_type = info->params.crc_type; - tmp_params.preamble_length = info->params.preamble_length; - tmp_params.preamble = info->params.preamble; - tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; - tmp_params.data_bits = info->params.data_bits; - tmp_params.stop_bits = info->params.stop_bits; - tmp_params.parity = info->params.parity; - if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) - return -EFAULT; - return 0; -} - -static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) -{ - struct MGSL_PARAMS32 tmp_params; - unsigned long flags; - - DBGINFO(("%s set_params32\n", info->device_name)); - if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) - return -EFAULT; - - spin_lock_irqsave(&info->lock, flags); - if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) { - info->base_clock = tmp_params.clock_speed; - } else { - info->params.mode = tmp_params.mode; - info->params.loopback = tmp_params.loopback; - info->params.flags = tmp_params.flags; - info->params.encoding = tmp_params.encoding; - info->params.clock_speed = tmp_params.clock_speed; - info->params.addr_filter = tmp_params.addr_filter; - info->params.crc_type = tmp_params.crc_type; - info->params.preamble_length = tmp_params.preamble_length; - info->params.preamble = tmp_params.preamble; - info->params.data_rate = tmp_params.data_rate; - info->params.data_bits = tmp_params.data_bits; - info->params.stop_bits = tmp_params.stop_bits; - info->params.parity = tmp_params.parity; - } - spin_unlock_irqrestore(&info->lock, flags); - - program_hw(info); - - return 0; -} - -static long slgt_compat_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct slgt_info *info = tty->driver_data; - int rc; - - if (sanity_check(info, tty->name, "compat_ioctl")) - return -ENODEV; - DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); - - switch (cmd) { - case MGSL_IOCSPARAMS32: - rc = set_params32(info, compat_ptr(arg)); - break; - - case MGSL_IOCGPARAMS32: - rc = get_params32(info, compat_ptr(arg)); - break; - - case MGSL_IOCGPARAMS: - case MGSL_IOCSPARAMS: - case MGSL_IOCGTXIDLE: - case MGSL_IOCGSTATS: - case MGSL_IOCWAITEVENT: - case MGSL_IOCGIF: - case MGSL_IOCSGPIO: - case MGSL_IOCGGPIO: - case MGSL_IOCWAITGPIO: - case MGSL_IOCGXSYNC: - case MGSL_IOCGXCTRL: - rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg)); - break; - default: - rc = ioctl(tty, cmd, arg); - } - DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); - return rc; -} -#else -#define slgt_compat_ioctl NULL -#endif /* ifdef CONFIG_COMPAT */ - -/* - * proc fs support - */ -static inline void line_info(struct seq_file *m, struct slgt_info *info) -{ - char stat_buf[30]; - unsigned long flags; - - seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n", - info->device_name, info->phys_reg_addr, - info->irq_level, info->max_frame_size); - - /* output current serial signal states */ - spin_lock_irqsave(&info->lock,flags); - get_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (info->signals & SerialSignal_RTS) - strcat(stat_buf, "|RTS"); - if (info->signals & SerialSignal_CTS) - strcat(stat_buf, "|CTS"); - if (info->signals & SerialSignal_DTR) - strcat(stat_buf, "|DTR"); - if (info->signals & SerialSignal_DSR) - strcat(stat_buf, "|DSR"); - if (info->signals & SerialSignal_DCD) - strcat(stat_buf, "|CD"); - if (info->signals & SerialSignal_RI) - strcat(stat_buf, "|RI"); - - if (info->params.mode != MGSL_MODE_ASYNC) { - seq_printf(m, "\tHDLC txok:%d rxok:%d", - info->icount.txok, info->icount.rxok); - if (info->icount.txunder) - seq_printf(m, " txunder:%d", info->icount.txunder); - if (info->icount.txabort) - seq_printf(m, " txabort:%d", info->icount.txabort); - if (info->icount.rxshort) - seq_printf(m, " rxshort:%d", info->icount.rxshort); - if (info->icount.rxlong) - seq_printf(m, " rxlong:%d", info->icount.rxlong); - if (info->icount.rxover) - seq_printf(m, " rxover:%d", info->icount.rxover); - if (info->icount.rxcrc) - seq_printf(m, " rxcrc:%d", info->icount.rxcrc); - } else { - seq_printf(m, "\tASYNC tx:%d rx:%d", - info->icount.tx, info->icount.rx); - if (info->icount.frame) - seq_printf(m, " fe:%d", info->icount.frame); - if (info->icount.parity) - seq_printf(m, " pe:%d", info->icount.parity); - if (info->icount.brk) - seq_printf(m, " brk:%d", info->icount.brk); - if (info->icount.overrun) - seq_printf(m, " oe:%d", info->icount.overrun); - } - - /* Append serial signal status to end */ - seq_printf(m, " %s\n", stat_buf+1); - - seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n", - info->tx_active,info->bh_requested,info->bh_running, - info->pending_bh); -} - -/* Called to print information about devices - */ -static int synclink_gt_proc_show(struct seq_file *m, void *v) -{ - struct slgt_info *info; - - seq_puts(m, "synclink_gt driver\n"); - - info = slgt_device_list; - while( info ) { - line_info(m, info); - info = info->next_device; - } - return 0; -} - -/* - * return count of bytes in transmit buffer - */ -static unsigned int chars_in_buffer(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned int count; - if (sanity_check(info, tty->name, "chars_in_buffer")) - return 0; - count = tbuf_bytes(info); - DBGINFO(("%s chars_in_buffer()=%u\n", info->device_name, count)); - return count; -} - -/* - * signal remote device to throttle send data (our receive data) - */ -static void throttle(struct tty_struct * tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "throttle")) - return; - DBGINFO(("%s throttle\n", info->device_name)); - if (I_IXOFF(tty)) - send_xchar(tty, STOP_CHAR(tty)); - if (C_CRTSCTS(tty)) { - spin_lock_irqsave(&info->lock,flags); - info->signals &= ~SerialSignal_RTS; - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* - * signal remote device to stop throttling send data (our receive data) - */ -static void unthrottle(struct tty_struct * tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - if (sanity_check(info, tty->name, "unthrottle")) - return; - DBGINFO(("%s unthrottle\n", info->device_name)); - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - send_xchar(tty, START_CHAR(tty)); - } - if (C_CRTSCTS(tty)) { - spin_lock_irqsave(&info->lock,flags); - info->signals |= SerialSignal_RTS; - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - } -} - -/* - * set or clear transmit break condition - * break_state -1=set break condition, 0=clear - */ -static int set_break(struct tty_struct *tty, int break_state) -{ - struct slgt_info *info = tty->driver_data; - unsigned short value; - unsigned long flags; - - if (sanity_check(info, tty->name, "set_break")) - return -EINVAL; - DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); - - spin_lock_irqsave(&info->lock,flags); - value = rd_reg16(info, TCR); - if (break_state == -1) - value |= BIT6; - else - value &= ~BIT6; - wr_reg16(info, TCR, value); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -#if SYNCLINK_GENERIC_HDLC - -/** - * hdlcdev_attach - called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) - * @dev: pointer to network device structure - * @encoding: serial encoding setting - * @parity: FCS setting - * - * Set encoding and frame check sequence (FCS) options. - * - * Return: 0 if success, otherwise error code - */ -static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, - unsigned short parity) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned char new_encoding; - unsigned short new_crctype; - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - DBGINFO(("%s hdlcdev_attach\n", info->device_name)); - - switch (encoding) - { - case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; - case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; - case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; - case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; - case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; - default: return -EINVAL; - } - - switch (parity) - { - case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; - case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; - case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; - default: return -EINVAL; - } - - info->params.encoding = new_encoding; - info->params.crc_type = new_crctype; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - - return 0; -} - -/** - * hdlcdev_xmit - called by generic HDLC layer to send a frame - * @skb: socket buffer containing HDLC frame - * @dev: pointer to network device structure - */ -static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlc_xmit\n", dev->name)); - - if (!skb->len) - return NETDEV_TX_OK; - - /* stop sending until this frame completes */ - netif_stop_queue(dev); - - /* update network statistics */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - /* save start time for transmit timeout detection */ - netif_trans_update(dev); - - spin_lock_irqsave(&info->lock, flags); - tx_load(info, skb->data, skb->len); - spin_unlock_irqrestore(&info->lock, flags); - - /* done with socket buffer, so free it */ - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/** - * hdlcdev_open - called by network layer when interface enabled - * @dev: pointer to network device structure - * - * Claim resources and initialize hardware. - * - * Return: 0 if success, otherwise error code - */ -static int hdlcdev_open(struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - int rc; - unsigned long flags; - - DBGINFO(("%s hdlcdev_open\n", dev->name)); - - /* arbitrate between network and tty opens */ - spin_lock_irqsave(&info->netlock, flags); - if (info->port.count != 0 || info->netcount != 0) { - DBGINFO(("%s hdlc_open busy\n", dev->name)); - spin_unlock_irqrestore(&info->netlock, flags); - return -EBUSY; - } - info->netcount=1; - spin_unlock_irqrestore(&info->netlock, flags); - - /* claim resources and init adapter */ - if ((rc = startup_hw(info)) != 0) { - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - - /* generic HDLC layer open processing */ - rc = hdlc_open(dev); - if (rc) { - shutdown_hw(info); - spin_lock_irqsave(&info->netlock, flags); - info->netcount = 0; - spin_unlock_irqrestore(&info->netlock, flags); - return rc; - } - - /* assert RTS and DTR, apply hardware settings */ - info->signals |= SerialSignal_RTS | SerialSignal_DTR; - program_hw(info); - - /* enable network layer transmit */ - netif_trans_update(dev); - netif_start_queue(dev); - - /* inform generic HDLC layer of current DCD status */ - spin_lock_irqsave(&info->lock, flags); - get_gtsignals(info); - spin_unlock_irqrestore(&info->lock, flags); - if (info->signals & SerialSignal_DCD) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - return 0; -} - -/** - * hdlcdev_close - called by network layer when interface is disabled - * @dev: pointer to network device structure - * - * Shutdown hardware and release resources. - * - * Return: 0 if success, otherwise error code - */ -static int hdlcdev_close(struct net_device *dev) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlcdev_close\n", dev->name)); - - netif_stop_queue(dev); - - /* shutdown adapter and release resources */ - shutdown_hw(info); - - hdlc_close(dev); - - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - - return 0; -} - -/** - * hdlcdev_ioctl - called by network layer to process IOCTL call to network device - * @dev: pointer to network device structure - * @ifr: pointer to network interface request structure - * @cmd: IOCTL command code - * - * Return: 0 if success, otherwise error code - */ -static int hdlcdev_ioctl(struct net_device *dev, struct if_settings *ifs) -{ - const size_t size = sizeof(sync_serial_settings); - sync_serial_settings new_line; - sync_serial_settings __user *line = ifs->ifs_ifsu.sync; - struct slgt_info *info = dev_to_port(dev); - unsigned int flags; - - DBGINFO(("%s hdlcdev_ioctl\n", dev->name)); - - /* return error if TTY interface open */ - if (info->port.count) - return -EBUSY; - - memset(&new_line, 0, sizeof(new_line)); - - switch (ifs->type) { - case IF_GET_IFACE: /* return current sync_serial_settings */ - - ifs->type = IF_IFACE_SYNC_SERIAL; - if (ifs->size < size) { - ifs->size = size; /* data size wanted */ - return -ENOBUFS; - } - - flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - - switch (flags){ - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; - case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; - case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; - default: new_line.clock_type = CLOCK_DEFAULT; - } - - new_line.clock_rate = info->params.clock_speed; - new_line.loopback = info->params.loopback ? 1:0; - - if (copy_to_user(line, &new_line, size)) - return -EFAULT; - return 0; - - case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&new_line, line, size)) - return -EFAULT; - - switch (new_line.clock_type) - { - case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; - case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; - case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; - case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; - case CLOCK_DEFAULT: flags = info->params.flags & - (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; - default: return -EINVAL; - } - - if (new_line.loopback != 0 && new_line.loopback != 1) - return -EINVAL; - - info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | - HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | - HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | - HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); - info->params.flags |= flags; - - info->params.loopback = new_line.loopback; - - if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) - info->params.clock_speed = new_line.clock_rate; - else - info->params.clock_speed = 0; - - /* if network interface up, reprogram hardware */ - if (info->netcount) - program_hw(info); - return 0; - - default: - return hdlc_ioctl(dev, ifs); - } -} - -/** - * hdlcdev_tx_timeout - called by network layer when transmit timeout is detected - * @dev: pointer to network device structure - * @txqueue: unused - */ -static void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct slgt_info *info = dev_to_port(dev); - unsigned long flags; - - DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); - - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - - spin_lock_irqsave(&info->lock,flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); - - netif_wake_queue(dev); -} - -/** - * hdlcdev_tx_done - called by device driver when transmit completes - * @info: pointer to device instance information - * - * Reenable network layer transmit if stopped. - */ -static void hdlcdev_tx_done(struct slgt_info *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); -} - -/** - * hdlcdev_rx - called by device driver when frame received - * @info: pointer to device instance information - * @buf: pointer to buffer contianing frame data - * @size: count of data bytes in buf - * - * Pass frame to network layer. - */ -static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - struct net_device *dev = info->netdev; - - DBGINFO(("%s hdlcdev_rx\n", dev->name)); - - if (skb == NULL) { - DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); - dev->stats.rx_dropped++; - return; - } - - skb_put_data(skb, buf, size); - - skb->protocol = hdlc_type_trans(skb, dev); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - - netif_rx(skb); -} - -static const struct net_device_ops hdlcdev_ops = { - .ndo_open = hdlcdev_open, - .ndo_stop = hdlcdev_close, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_siocwandev = hdlcdev_ioctl, - .ndo_tx_timeout = hdlcdev_tx_timeout, -}; - -/** - * hdlcdev_init - called by device driver when adding device instance - * @info: pointer to device instance information - * - * Do generic HDLC initialization. - * - * Return: 0 if success, otherwise error code - */ -static int hdlcdev_init(struct slgt_info *info) -{ - int rc; - struct net_device *dev; - hdlc_device *hdlc; - - /* allocate and initialize network and HDLC layer objects */ - - dev = alloc_hdlcdev(info); - if (!dev) { - printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name); - return -ENOMEM; - } - - /* for network layer reporting purposes only */ - dev->mem_start = info->phys_reg_addr; - dev->mem_end = info->phys_reg_addr + SLGT_REG_SIZE - 1; - dev->irq = info->irq_level; - - /* network layer callbacks and settings */ - dev->netdev_ops = &hdlcdev_ops; - dev->watchdog_timeo = 10 * HZ; - dev->tx_queue_len = 50; - - /* generic HDLC layer callbacks and settings */ - hdlc = dev_to_hdlc(dev); - hdlc->attach = hdlcdev_attach; - hdlc->xmit = hdlcdev_xmit; - - /* register objects with HDLC layer */ - rc = register_hdlc_device(dev); - if (rc) { - printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); - free_netdev(dev); - return rc; - } - - info->netdev = dev; - return 0; -} - -/** - * hdlcdev_exit - called by device driver when removing device instance - * @info: pointer to device instance information - * - * Do generic HDLC cleanup. - */ -static void hdlcdev_exit(struct slgt_info *info) -{ - if (!info->netdev) - return; - unregister_hdlc_device(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; -} - -#endif /* ifdef CONFIG_HDLC */ - -/* - * get async data from rx DMA buffers - */ -static void rx_async(struct slgt_info *info) -{ - struct mgsl_icount *icount = &info->icount; - unsigned int start, end; - unsigned char *p; - unsigned char status; - struct slgt_desc *bufs = info->rbufs; - int i, count; - int chars = 0; - int stat; - unsigned char ch; - - start = end = info->rbuf_current; - - while(desc_complete(bufs[end])) { - count = desc_count(bufs[end]) - info->rbuf_index; - p = bufs[end].buf + info->rbuf_index; - - DBGISR(("%s rx_async count=%d\n", info->device_name, count)); - DBGDATA(info, p, count, "rx"); - - for(i=0 ; i < count; i+=2, p+=2) { - ch = *p; - icount->rx++; - - stat = 0; - - status = *(p + 1) & (BIT1 + BIT0); - if (status) { - if (status & BIT1) - icount->parity++; - else if (status & BIT0) - icount->frame++; - /* discard char if tty control flags say so */ - if (status & info->ignore_status_mask) - continue; - if (status & BIT1) - stat = TTY_PARITY; - else if (status & BIT0) - stat = TTY_FRAME; - } - tty_insert_flip_char(&info->port, ch, stat); - chars++; - } - - if (i < count) { - /* receive buffer not completed */ - info->rbuf_index += i; - mod_timer(&info->rx_timer, jiffies + 1); - break; - } - - info->rbuf_index = 0; - free_rbufs(info, end, end); - - if (++end == info->rbuf_count) - end = 0; - - /* if entire list searched then no frame available */ - if (end == start) - break; - } - - if (chars) - tty_flip_buffer_push(&info->port); -} - -/* - * return next bottom half action to perform - */ -static int bh_action(struct slgt_info *info) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&info->lock,flags); - - if (info->pending_bh & BH_RECEIVE) { - info->pending_bh &= ~BH_RECEIVE; - rc = BH_RECEIVE; - } else if (info->pending_bh & BH_TRANSMIT) { - info->pending_bh &= ~BH_TRANSMIT; - rc = BH_TRANSMIT; - } else if (info->pending_bh & BH_STATUS) { - info->pending_bh &= ~BH_STATUS; - rc = BH_STATUS; - } else { - /* Mark BH routine as complete */ - info->bh_running = false; - info->bh_requested = false; - rc = 0; - } - - spin_unlock_irqrestore(&info->lock,flags); - - return rc; -} - -/* - * perform bottom half processing - */ -static void bh_handler(struct work_struct *work) -{ - struct slgt_info *info = container_of(work, struct slgt_info, task); - int action; - - info->bh_running = true; - - while((action = bh_action(info))) { - switch (action) { - case BH_RECEIVE: - DBGBH(("%s bh receive\n", info->device_name)); - switch(info->params.mode) { - case MGSL_MODE_ASYNC: - rx_async(info); - break; - case MGSL_MODE_HDLC: - while(rx_get_frame(info)); - break; - case MGSL_MODE_RAW: - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - case MGSL_MODE_XSYNC: - while(rx_get_buf(info)); - break; - } - /* restart receiver if rx DMA buffers exhausted */ - if (info->rx_restart) - rx_start(info); - break; - case BH_TRANSMIT: - bh_transmit(info); - break; - case BH_STATUS: - DBGBH(("%s bh status\n", info->device_name)); - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - break; - default: - DBGBH(("%s unknown action\n", info->device_name)); - break; - } - } - DBGBH(("%s bh_handler exit\n", info->device_name)); -} - -static void bh_transmit(struct slgt_info *info) -{ - struct tty_struct *tty = info->port.tty; - - DBGBH(("%s bh_transmit\n", info->device_name)); - if (tty) - tty_wakeup(tty); -} - -static void dsr_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT3) { - info->signals |= SerialSignal_DSR; - info->input_signal_events.dsr_up++; - } else { - info->signals &= ~SerialSignal_DSR; - info->input_signal_events.dsr_down++; - } - DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_DSR); - return; - } - info->icount.dsr++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -static void cts_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT2) { - info->signals |= SerialSignal_CTS; - info->input_signal_events.cts_up++; - } else { - info->signals &= ~SerialSignal_CTS; - info->input_signal_events.cts_down++; - } - DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_CTS); - return; - } - info->icount.cts++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; - - if (tty_port_cts_enabled(&info->port)) { - if (info->port.tty) { - if (info->port.tty->hw_stopped) { - if (info->signals & SerialSignal_CTS) { - info->port.tty->hw_stopped = false; - info->pending_bh |= BH_TRANSMIT; - return; - } - } else { - if (!(info->signals & SerialSignal_CTS)) - info->port.tty->hw_stopped = true; - } - } - } -} - -static void dcd_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT1) { - info->signals |= SerialSignal_DCD; - info->input_signal_events.dcd_up++; - } else { - info->signals &= ~SerialSignal_DCD; - info->input_signal_events.dcd_down++; - } - DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_DCD); - return; - } - info->icount.dcd++; -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) { - if (info->signals & SerialSignal_DCD) - netif_carrier_on(info->netdev); - else - netif_carrier_off(info->netdev); - } -#endif - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; - - if (tty_port_check_carrier(&info->port)) { - if (info->signals & SerialSignal_DCD) - wake_up_interruptible(&info->port.open_wait); - else { - if (info->port.tty) - tty_hangup(info->port.tty); - } - } -} - -static void ri_change(struct slgt_info *info, unsigned short status) -{ - if (status & BIT0) { - info->signals |= SerialSignal_RI; - info->input_signal_events.ri_up++; - } else { - info->signals &= ~SerialSignal_RI; - info->input_signal_events.ri_down++; - } - DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals)); - if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) { - slgt_irq_off(info, IRQ_RI); - return; - } - info->icount.rng++; - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - info->pending_bh |= BH_STATUS; -} - -static void isr_rxdata(struct slgt_info *info) -{ - unsigned int count = info->rbuf_fill_count; - unsigned int i = info->rbuf_fill_index; - unsigned short reg; - - while (rd_reg16(info, SSR) & IRQ_RXDATA) { - reg = rd_reg16(info, RDR); - DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg)); - if (desc_complete(info->rbufs[i])) { - /* all buffers full */ - rx_stop(info); - info->rx_restart = true; - continue; - } - info->rbufs[i].buf[count++] = (unsigned char)reg; - /* async mode saves status byte to buffer for each data byte */ - if (info->params.mode == MGSL_MODE_ASYNC) - info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8); - if (count == info->rbuf_fill_level || (reg & BIT10)) { - /* buffer full or end of frame */ - set_desc_count(info->rbufs[i], count); - set_desc_status(info->rbufs[i], BIT15 | (reg >> 8)); - info->rbuf_fill_count = count = 0; - if (++i == info->rbuf_count) - i = 0; - info->pending_bh |= BH_RECEIVE; - } - } - - info->rbuf_fill_index = i; - info->rbuf_fill_count = count; -} - -static void isr_serial(struct slgt_info *info) -{ - unsigned short status = rd_reg16(info, SSR); - - DBGISR(("%s isr_serial status=%04X\n", info->device_name, status)); - - wr_reg16(info, SSR, status); /* clear pending */ - - info->irq_occurred = true; - - if (info->params.mode == MGSL_MODE_ASYNC) { - if (status & IRQ_TXIDLE) { - if (info->tx_active) - isr_txeom(info, status); - } - if (info->rx_pio && (status & IRQ_RXDATA)) - isr_rxdata(info); - if ((status & IRQ_RXBREAK) && (status & RXBREAK)) { - info->icount.brk++; - /* process break detection if tty control allows */ - if (info->port.tty) { - if (!(status & info->ignore_status_mask)) { - if (info->read_status_mask & MASK_BREAK) { - tty_insert_flip_char(&info->port, 0, TTY_BREAK); - if (info->port.flags & ASYNC_SAK) - do_SAK(info->port.tty); - } - } - } - } - } else { - if (status & (IRQ_TXIDLE + IRQ_TXUNDER)) - isr_txeom(info, status); - if (info->rx_pio && (status & IRQ_RXDATA)) - isr_rxdata(info); - if (status & IRQ_RXIDLE) { - if (status & RXIDLE) - info->icount.rxidle++; - else - info->icount.exithunt++; - wake_up_interruptible(&info->event_wait_q); - } - - if (status & IRQ_RXOVER) - rx_start(info); - } - - if (status & IRQ_DSR) - dsr_change(info, status); - if (status & IRQ_CTS) - cts_change(info, status); - if (status & IRQ_DCD) - dcd_change(info, status); - if (status & IRQ_RI) - ri_change(info, status); -} - -static void isr_rdma(struct slgt_info *info) -{ - unsigned int status = rd_reg32(info, RDCSR); - - DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status)); - - /* RDCSR (rx DMA control/status) - * - * 31..07 reserved - * 06 save status byte to DMA buffer - * 05 error - * 04 eol (end of list) - * 03 eob (end of buffer) - * 02 IRQ enable - * 01 reset - * 00 enable - */ - wr_reg32(info, RDCSR, status); /* clear pending */ - - if (status & (BIT5 + BIT4)) { - DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name)); - info->rx_restart = true; - } - info->pending_bh |= BH_RECEIVE; -} - -static void isr_tdma(struct slgt_info *info) -{ - unsigned int status = rd_reg32(info, TDCSR); - - DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status)); - - /* TDCSR (tx DMA control/status) - * - * 31..06 reserved - * 05 error - * 04 eol (end of list) - * 03 eob (end of buffer) - * 02 IRQ enable - * 01 reset - * 00 enable - */ - wr_reg32(info, TDCSR, status); /* clear pending */ - - if (status & (BIT5 + BIT4 + BIT3)) { - // another transmit buffer has completed - // run bottom half to get more send data from user - info->pending_bh |= BH_TRANSMIT; - } -} - -/* - * return true if there are unsent tx DMA buffers, otherwise false - * - * if there are unsent buffers then info->tbuf_start - * is set to index of first unsent buffer - */ -static bool unsent_tbufs(struct slgt_info *info) -{ - unsigned int i = info->tbuf_current; - bool rc = false; - - /* - * search backwards from last loaded buffer (precedes tbuf_current) - * for first unsent buffer (desc_count > 0) - */ - - do { - if (i) - i--; - else - i = info->tbuf_count - 1; - if (!desc_count(info->tbufs[i])) - break; - info->tbuf_start = i; - rc = true; - } while (i != info->tbuf_current); - - return rc; -} - -static void isr_txeom(struct slgt_info *info, unsigned short status) -{ - DBGISR(("%s txeom status=%04x\n", info->device_name, status)); - - slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); - tdma_reset(info); - if (status & IRQ_TXUNDER) { - unsigned short val = rd_reg16(info, TCR); - wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, TCR, val); /* clear reset bit */ - } - - if (info->tx_active) { - if (info->params.mode != MGSL_MODE_ASYNC) { - if (status & IRQ_TXUNDER) - info->icount.txunder++; - else if (status & IRQ_TXIDLE) - info->icount.txok++; - } - - if (unsent_tbufs(info)) { - tx_start(info); - update_tx_timer(info); - return; - } - info->tx_active = false; - - timer_delete(&info->tx_timer); - - if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { - info->signals &= ~SerialSignal_RTS; - info->drop_rts_on_tx_done = false; - set_gtsignals(info); - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - { - if (info->port.tty && (info->port.tty->flow.stopped || info->port.tty->hw_stopped)) { - tx_stop(info); - return; - } - info->pending_bh |= BH_TRANSMIT; - } - } -} - -static void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state) -{ - struct cond_wait *w, *prev; - - /* wake processes waiting for specific transitions */ - for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) { - if (w->data & changed) { - w->data = state; - wake_up_interruptible(&w->q); - if (prev != NULL) - prev->next = w->next; - else - info->gpio_wait_q = w->next; - } else - prev = w; - } -} - -/* interrupt service routine - * - * irq interrupt number - * dev_id device ID supplied during interrupt registration - */ -static irqreturn_t slgt_interrupt(int dummy, void *dev_id) -{ - struct slgt_info *info = dev_id; - unsigned int gsr; - unsigned int i; - - DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); - - while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { - DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); - info->irq_occurred = true; - for(i=0; i < info->port_count ; i++) { - if (info->port_array[i] == NULL) - continue; - spin_lock(&info->port_array[i]->lock); - if (gsr & (BIT8 << i)) - isr_serial(info->port_array[i]); - if (gsr & (BIT16 << (i*2))) - isr_rdma(info->port_array[i]); - if (gsr & (BIT17 << (i*2))) - isr_tdma(info->port_array[i]); - spin_unlock(&info->port_array[i]->lock); - } - } - - if (info->gpio_present) { - unsigned int state; - unsigned int changed; - spin_lock(&info->lock); - while ((changed = rd_reg32(info, IOSR)) != 0) { - DBGISR(("%s iosr=%08x\n", info->device_name, changed)); - /* read latched state of GPIO signals */ - state = rd_reg32(info, IOVR); - /* clear pending GPIO interrupt bits */ - wr_reg32(info, IOSR, changed); - for (i=0 ; i < info->port_count ; i++) { - if (info->port_array[i] != NULL) - isr_gpio(info->port_array[i], changed, state); - } - } - spin_unlock(&info->lock); - } - - for(i=0; i < info->port_count ; i++) { - struct slgt_info *port = info->port_array[i]; - if (port == NULL) - continue; - spin_lock(&port->lock); - if ((port->port.count || port->netcount) && - port->pending_bh && !port->bh_running && - !port->bh_requested) { - DBGISR(("%s bh queued\n", port->device_name)); - schedule_work(&port->task); - port->bh_requested = true; - } - spin_unlock(&port->lock); - } - - DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); - return IRQ_HANDLED; -} - -static int startup_hw(struct slgt_info *info) -{ - DBGINFO(("%s startup\n", info->device_name)); - - if (tty_port_initialized(&info->port)) - return 0; - - if (!info->tx_buf) { - info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); - if (!info->tx_buf) { - DBGERR(("%s can't allocate tx buffer\n", info->device_name)); - return -ENOMEM; - } - } - - info->pending_bh = 0; - - memset(&info->icount, 0, sizeof(info->icount)); - - /* program hardware for current parameters */ - change_params(info); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - tty_port_set_initialized(&info->port, true); - - return 0; -} - -/* - * called by close() and hangup() to shutdown hardware - */ -static void shutdown_hw(struct slgt_info *info) -{ - unsigned long flags; - - if (!tty_port_initialized(&info->port)) - return; - - DBGINFO(("%s shutdown\n", info->device_name)); - - /* clear status wait queue because status changes */ - /* can't happen after shutting down the hardware */ - wake_up_interruptible(&info->status_event_wait_q); - wake_up_interruptible(&info->event_wait_q); - - timer_delete_sync(&info->tx_timer); - timer_delete_sync(&info->rx_timer); - - kfree(info->tx_buf); - info->tx_buf = NULL; - - spin_lock_irqsave(&info->lock,flags); - - tx_stop(info); - rx_stop(info); - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - - if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { - info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_gtsignals(info); - } - - flush_cond_wait(&info->gpio_wait_q); - - spin_unlock_irqrestore(&info->lock,flags); - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - tty_port_set_initialized(&info->port, false); -} - -static void program_hw(struct slgt_info *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - - rx_stop(info); - tx_stop(info); - - if (info->params.mode != MGSL_MODE_ASYNC || - info->netcount) - sync_mode(info); - else - async_mode(info); - - set_gtsignals(info); - - info->dcd_chkcount = 0; - info->cts_chkcount = 0; - info->ri_chkcount = 0; - info->dsr_chkcount = 0; - - slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI); - get_gtsignals(info); - - if (info->netcount || - (info->port.tty && info->port.tty->termios.c_cflag & CREAD)) - rx_start(info); - - spin_unlock_irqrestore(&info->lock,flags); -} - -/* - * reconfigure adapter based on new parameters - */ -static void change_params(struct slgt_info *info) -{ - unsigned cflag; - int bits_per_char; - - if (!info->port.tty) - return; - DBGINFO(("%s change_params\n", info->device_name)); - - cflag = info->port.tty->termios.c_cflag; - - /* if B0 rate (hangup) specified then negate RTS and DTR */ - /* otherwise assert RTS and DTR */ - if (cflag & CBAUD) - info->signals |= SerialSignal_RTS | SerialSignal_DTR; - else - info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - - /* byte size and parity */ - - info->params.data_bits = tty_get_char_size(cflag); - info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1; - - if (cflag & PARENB) - info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN; - else - info->params.parity = ASYNC_PARITY_NONE; - - /* calculate number of jiffies to transmit a full - * FIFO (32 bytes) at specified data rate - */ - bits_per_char = info->params.data_bits + - info->params.stop_bits + 1; - - info->params.data_rate = tty_get_baud_rate(info->port.tty); - - if (info->params.data_rate) { - info->timeout = (32*HZ*bits_per_char) / - info->params.data_rate; - } - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - tty_port_set_cts_flow(&info->port, cflag & CRTSCTS); - tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL); - - /* process tty input control flags */ - - info->read_status_mask = IRQ_RXOVER; - if (I_INPCK(info->port.tty)) - info->read_status_mask |= MASK_PARITY | MASK_FRAMING; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= MASK_BREAK; - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask |= MASK_BREAK; - /* If ignoring parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= MASK_OVERRUN; - } - - program_hw(info); -} - -static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount) -{ - DBGINFO(("%s get_stats\n", info->device_name)); - if (!user_icount) { - memset(&info->icount, 0, sizeof(info->icount)); - } else { - if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount))) - return -EFAULT; - } - return 0; -} - -static int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params) -{ - DBGINFO(("%s get_params\n", info->device_name)); - if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS))) - return -EFAULT; - return 0; -} - -static int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params) -{ - unsigned long flags; - MGSL_PARAMS tmp_params; - - DBGINFO(("%s set_params\n", info->device_name)); - if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS))) - return -EFAULT; - - spin_lock_irqsave(&info->lock, flags); - if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) - info->base_clock = tmp_params.clock_speed; - else - memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS)); - spin_unlock_irqrestore(&info->lock, flags); - - program_hw(info); - - return 0; -} - -static int get_txidle(struct slgt_info *info, int __user *idle_mode) -{ - DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode)); - if (put_user(info->idle_mode, idle_mode)) - return -EFAULT; - return 0; -} - -static int set_txidle(struct slgt_info *info, int idle_mode) -{ - unsigned long flags; - DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode)); - spin_lock_irqsave(&info->lock,flags); - info->idle_mode = idle_mode; - if (info->params.mode != MGSL_MODE_ASYNC) - tx_set_idle(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int tx_enable(struct slgt_info *info, int enable) -{ - unsigned long flags; - DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable)); - spin_lock_irqsave(&info->lock,flags); - if (enable) { - if (!info->tx_enabled) - tx_start(info); - } else { - if (info->tx_enabled) - tx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* - * abort transmit HDLC frame - */ -static int tx_abort(struct slgt_info *info) -{ - unsigned long flags; - DBGINFO(("%s tx_abort\n", info->device_name)); - spin_lock_irqsave(&info->lock,flags); - tdma_reset(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int rx_enable(struct slgt_info *info, int enable) -{ - unsigned long flags; - unsigned int rbuf_fill_level; - DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); - spin_lock_irqsave(&info->lock,flags); - /* - * enable[31..16] = receive DMA buffer fill level - * 0 = noop (leave fill level unchanged) - * fill level must be multiple of 4 and <= buffer size - */ - rbuf_fill_level = ((unsigned int)enable) >> 16; - if (rbuf_fill_level) { - if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { - spin_unlock_irqrestore(&info->lock, flags); - return -EINVAL; - } - info->rbuf_fill_level = rbuf_fill_level; - if (rbuf_fill_level < 128) - info->rx_pio = 1; /* PIO mode */ - else - info->rx_pio = 0; /* DMA mode */ - rx_stop(info); /* restart receiver to use new fill level */ - } - - /* - * enable[1..0] = receiver enable command - * 0 = disable - * 1 = enable - * 2 = enable or force hunt mode if already enabled - */ - enable &= 3; - if (enable) { - if (!info->rx_enabled) - rx_start(info); - else if (enable == 2) { - /* force hunt mode (write 1 to RCR[3]) */ - wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3); - } - } else { - if (info->rx_enabled) - rx_stop(info); - } - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -/* - * wait for specified event to occur - */ -static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr) -{ - unsigned long flags; - int s; - int rc=0; - struct mgsl_icount cprev, cnow; - int events; - int mask; - struct _input_signal_events oldsigs, newsigs; - DECLARE_WAITQUEUE(wait, current); - - if (get_user(mask, mask_ptr)) - return -EFAULT; - - DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask)); - - spin_lock_irqsave(&info->lock,flags); - - /* return immediately if state matches requested events */ - get_gtsignals(info); - s = info->signals; - - events = mask & - ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + - ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + - ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + - ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); - if (events) { - spin_unlock_irqrestore(&info->lock,flags); - goto exit; - } - - /* save current irq counts */ - cprev = info->icount; - oldsigs = info->input_signal_events; - - /* enable hunt and idle irqs if needed */ - if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) { - unsigned short val = rd_reg16(info, SCR); - if (!(val & IRQ_RXIDLE)) - wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE)); - } - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&info->event_wait_q, &wait); - - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - newsigs = info->input_signal_events; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (newsigs.dsr_up == oldsigs.dsr_up && - newsigs.dsr_down == oldsigs.dsr_down && - newsigs.dcd_up == oldsigs.dcd_up && - newsigs.dcd_down == oldsigs.dcd_down && - newsigs.cts_up == oldsigs.cts_up && - newsigs.cts_down == oldsigs.cts_down && - newsigs.ri_up == oldsigs.ri_up && - newsigs.ri_down == oldsigs.ri_down && - cnow.exithunt == cprev.exithunt && - cnow.rxidle == cprev.rxidle) { - rc = -EIO; - break; - } - - events = mask & - ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + - (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + - (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + - (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + - (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + - (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + - (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + - (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + - (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + - (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); - if (events) - break; - - cprev = cnow; - oldsigs = newsigs; - } - - remove_wait_queue(&info->event_wait_q, &wait); - set_current_state(TASK_RUNNING); - - - if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { - spin_lock_irqsave(&info->lock,flags); - if (!waitqueue_active(&info->event_wait_q)) { - /* disable enable exit hunt mode/idle rcvd IRQs */ - wr_reg16(info, SCR, - (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE)); - } - spin_unlock_irqrestore(&info->lock,flags); - } -exit: - if (rc == 0) - rc = put_user(events, mask_ptr); - return rc; -} - -static int get_interface(struct slgt_info *info, int __user *if_mode) -{ - DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode)); - if (put_user(info->if_mode, if_mode)) - return -EFAULT; - return 0; -} - -static int set_interface(struct slgt_info *info, int if_mode) -{ - unsigned long flags; - unsigned short val; - - DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode)); - spin_lock_irqsave(&info->lock,flags); - info->if_mode = if_mode; - - msc_set_vcr(info); - - /* TCR (tx control) 07 1=RTS driver control */ - val = rd_reg16(info, TCR); - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - else - val &= ~BIT7; - wr_reg16(info, TCR, val); - - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static int get_xsync(struct slgt_info *info, int __user *xsync) -{ - DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); - if (put_user(info->xsync, xsync)) - return -EFAULT; - return 0; -} - -/* - * set extended sync pattern (1 to 4 bytes) for extended sync mode - * - * sync pattern is contained in least significant bytes of value - * most significant byte of sync pattern is oldest (1st sent/detected) - */ -static int set_xsync(struct slgt_info *info, int xsync) -{ - unsigned long flags; - - DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); - spin_lock_irqsave(&info->lock, flags); - info->xsync = xsync; - wr_reg32(info, XSR, xsync); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int get_xctrl(struct slgt_info *info, int __user *xctrl) -{ - DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); - if (put_user(info->xctrl, xctrl)) - return -EFAULT; - return 0; -} - -/* - * set extended control options - * - * xctrl[31:19] reserved, must be zero - * xctrl[18:17] extended sync pattern length in bytes - * 00 = 1 byte in xsr[7:0] - * 01 = 2 bytes in xsr[15:0] - * 10 = 3 bytes in xsr[23:0] - * 11 = 4 bytes in xsr[31:0] - * xctrl[16] 1 = enable terminal count, 0=disabled - * xctrl[15:0] receive terminal count for fixed length packets - * value is count minus one (0 = 1 byte packet) - * when terminal count is reached, receiver - * automatically returns to hunt mode and receive - * FIFO contents are flushed to DMA buffers with - * end of frame (EOF) status - */ -static int set_xctrl(struct slgt_info *info, int xctrl) -{ - unsigned long flags; - - DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); - spin_lock_irqsave(&info->lock, flags); - info->xctrl = xctrl; - wr_reg32(info, XCR, xctrl); - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -/* - * set general purpose IO pin state and direction - * - * user_gpio fields: - * state each bit indicates a pin state - * smask set bit indicates pin state to set - * dir each bit indicates a pin direction (0=input, 1=output) - * dmask set bit indicates pin direction to set - */ -static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - unsigned long flags; - struct gpio_desc gpio; - __u32 data; - - if (!info->gpio_present) - return -EINVAL; - if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n", - info->device_name, gpio.state, gpio.smask, - gpio.dir, gpio.dmask)); - - spin_lock_irqsave(&info->port_array[0]->lock, flags); - if (gpio.dmask) { - data = rd_reg32(info, IODR); - data |= gpio.dmask & gpio.dir; - data &= ~(gpio.dmask & ~gpio.dir); - wr_reg32(info, IODR, data); - } - if (gpio.smask) { - data = rd_reg32(info, IOVR); - data |= gpio.smask & gpio.state; - data &= ~(gpio.smask & ~gpio.state); - wr_reg32(info, IOVR, data); - } - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - - return 0; -} - -/* - * get general purpose IO pin state and direction - */ -static int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - struct gpio_desc gpio; - if (!info->gpio_present) - return -EINVAL; - gpio.state = rd_reg32(info, IOVR); - gpio.smask = 0xffffffff; - gpio.dir = rd_reg32(info, IODR); - gpio.dmask = 0xffffffff; - if (copy_to_user(user_gpio, &gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s get_gpio state=%08x dir=%08x\n", - info->device_name, gpio.state, gpio.dir)); - return 0; -} - -/* - * conditional wait facility - */ -static void init_cond_wait(struct cond_wait *w, unsigned int data) -{ - init_waitqueue_head(&w->q); - init_waitqueue_entry(&w->wait, current); - w->data = data; -} - -static void add_cond_wait(struct cond_wait **head, struct cond_wait *w) -{ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&w->q, &w->wait); - w->next = *head; - *head = w; -} - -static void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw) -{ - struct cond_wait *w, *prev; - remove_wait_queue(&cw->q, &cw->wait); - set_current_state(TASK_RUNNING); - for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) { - if (w == cw) { - if (prev != NULL) - prev->next = w->next; - else - *head = w->next; - break; - } - } -} - -static void flush_cond_wait(struct cond_wait **head) -{ - while (*head != NULL) { - wake_up_interruptible(&(*head)->q); - *head = (*head)->next; - } -} - -/* - * wait for general purpose I/O pin(s) to enter specified state - * - * user_gpio fields: - * state - bit indicates target pin state - * smask - set bit indicates watched pin - * - * The wait ends when at least one watched pin enters the specified - * state. When 0 (no error) is returned, user_gpio->state is set to the - * state of all GPIO pins when the wait ends. - * - * Note: Each pin may be a dedicated input, dedicated output, or - * configurable input/output. The number and configuration of pins - * varies with the specific adapter model. Only input pins (dedicated - * or configured) can be monitored with this function. - */ -static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) -{ - unsigned long flags; - int rc = 0; - struct gpio_desc gpio; - struct cond_wait wait; - u32 state; - - if (!info->gpio_present) - return -EINVAL; - if (copy_from_user(&gpio, user_gpio, sizeof(gpio))) - return -EFAULT; - DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n", - info->device_name, gpio.state, gpio.smask)); - /* ignore output pins identified by set IODR bit */ - if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0) - return -EINVAL; - init_cond_wait(&wait, gpio.smask); - - spin_lock_irqsave(&info->port_array[0]->lock, flags); - /* enable interrupts for watched pins */ - wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); - /* get current pin states */ - state = rd_reg32(info, IOVR); - - if (gpio.smask & ~(state ^ gpio.state)) { - /* already in target state */ - gpio.state = state; - } else { - /* wait for target state */ - add_cond_wait(&info->gpio_wait_q, &wait); - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - schedule(); - if (signal_pending(current)) - rc = -ERESTARTSYS; - else - gpio.state = wait.data; - spin_lock_irqsave(&info->port_array[0]->lock, flags); - remove_cond_wait(&info->gpio_wait_q, &wait); - } - - /* disable all GPIO interrupts if no waiting processes */ - if (info->gpio_wait_q == NULL) - wr_reg32(info, IOER, 0); - spin_unlock_irqrestore(&info->port_array[0]->lock, flags); - - if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) - rc = -EFAULT; - return rc; -} - -static int modem_input_wait(struct slgt_info *info,int arg) -{ - unsigned long flags; - int rc; - struct mgsl_icount cprev, cnow; - DECLARE_WAITQUEUE(wait, current); - - /* save current irq counts */ - spin_lock_irqsave(&info->lock,flags); - cprev = info->icount; - add_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - for(;;) { - schedule(); - if (signal_pending(current)) { - rc = -ERESTARTSYS; - break; - } - - /* get new irq counts */ - spin_lock_irqsave(&info->lock,flags); - cnow = info->icount; - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&info->lock,flags); - - /* if no change, wait aborted for some reason */ - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - rc = -EIO; - break; - } - - /* check for change in caller specified modem input */ - if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) || - (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) || - (arg & TIOCM_CD && cnow.dcd != cprev.dcd) || - (arg & TIOCM_CTS && cnow.cts != cprev.cts)) { - rc = 0; - break; - } - - cprev = cnow; - } - remove_wait_queue(&info->status_event_wait_q, &wait); - set_current_state(TASK_RUNNING); - return rc; -} - -/* - * return state of serial control and status signals - */ -static int tiocmget(struct tty_struct *tty) -{ - struct slgt_info *info = tty->driver_data; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->lock,flags); - get_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - - result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0); - - DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result)); - return result; -} - -/* - * set modem control signals (DTR/RTS) - * - * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit - * TIOCMSET = set/clear signal values - * value bit mask for command - */ -static int tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct slgt_info *info = tty->driver_data; - unsigned long flags; - - DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear)); - - if (set & TIOCM_RTS) - info->signals |= SerialSignal_RTS; - if (set & TIOCM_DTR) - info->signals |= SerialSignal_DTR; - if (clear & TIOCM_RTS) - info->signals &= ~SerialSignal_RTS; - if (clear & TIOCM_DTR) - info->signals &= ~SerialSignal_DTR; - - spin_lock_irqsave(&info->lock,flags); - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - return 0; -} - -static bool carrier_raised(struct tty_port *port) -{ - unsigned long flags; - struct slgt_info *info = container_of(port, struct slgt_info, port); - - spin_lock_irqsave(&info->lock,flags); - get_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); - - return info->signals & SerialSignal_DCD; -} - -static void dtr_rts(struct tty_port *port, bool active) -{ - unsigned long flags; - struct slgt_info *info = container_of(port, struct slgt_info, port); - - spin_lock_irqsave(&info->lock,flags); - if (active) - info->signals |= SerialSignal_RTS | SerialSignal_DTR; - else - info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_gtsignals(info); - spin_unlock_irqrestore(&info->lock,flags); -} - - -/* - * block current process until the device is ready to open - */ -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct slgt_info *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - bool do_clocal = false; - unsigned long flags; - bool cd; - struct tty_port *port = &info->port; - - DBGINFO(("%s block_til_ready\n", tty->driver->name)); - - if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { - /* nonblock mode is set or port is not enabled */ - tty_port_set_active(port, true); - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = true; - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&info->lock, flags); - port->count--; - spin_unlock_irqrestore(&info->lock, flags); - port->blocked_open++; - - while (1) { - if (C_BAUD(tty) && tty_port_initialized(port)) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - cd = tty_port_carrier_raised(port); - if (do_clocal || cd) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(tty); - schedule(); - tty_lock(tty); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - - if (!retval) - tty_port_set_active(port, true); - - DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); - return retval; -} - -/* - * allocate buffers used for calling line discipline receive_buf - * directly in synchronous mode - * note: add 5 bytes to max frame size to allow appending - * 32-bit CRC and status byte when configured to do so - */ -static int alloc_tmp_rbuf(struct slgt_info *info) -{ - info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); - if (info->tmp_rbuf == NULL) - return -ENOMEM; - - return 0; -} - -static void free_tmp_rbuf(struct slgt_info *info) -{ - kfree(info->tmp_rbuf); - info->tmp_rbuf = NULL; -} - -/* - * allocate DMA descriptor lists. - */ -static int alloc_desc(struct slgt_info *info) -{ - unsigned int i; - unsigned int pbufs; - - /* allocate memory to hold descriptor lists */ - info->bufs = dma_alloc_coherent(&info->pdev->dev, DESC_LIST_SIZE, - &info->bufs_dma_addr, GFP_KERNEL); - if (info->bufs == NULL) - return -ENOMEM; - - info->rbufs = (struct slgt_desc*)info->bufs; - info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count; - - pbufs = (unsigned int)info->bufs_dma_addr; - - /* - * Build circular lists of descriptors - */ - - for (i=0; i < info->rbuf_count; i++) { - /* physical address of this descriptor */ - info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc)); - - /* physical address of next descriptor */ - if (i == info->rbuf_count - 1) - info->rbufs[i].next = cpu_to_le32(pbufs); - else - info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc))); - set_desc_count(info->rbufs[i], DMABUFSIZE); - } - - for (i=0; i < info->tbuf_count; i++) { - /* physical address of this descriptor */ - info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc)); - - /* physical address of next descriptor */ - if (i == info->tbuf_count - 1) - info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc)); - else - info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc))); - } - - return 0; -} - -static void free_desc(struct slgt_info *info) -{ - if (info->bufs != NULL) { - dma_free_coherent(&info->pdev->dev, DESC_LIST_SIZE, - info->bufs, info->bufs_dma_addr); - info->bufs = NULL; - info->rbufs = NULL; - info->tbufs = NULL; - } -} - -static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) -{ - int i; - for (i=0; i < count; i++) { - bufs[i].buf = dma_alloc_coherent(&info->pdev->dev, DMABUFSIZE, - &bufs[i].buf_dma_addr, GFP_KERNEL); - if (!bufs[i].buf) - return -ENOMEM; - bufs[i].pbuf = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr); - } - return 0; -} - -static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count) -{ - int i; - for (i=0; i < count; i++) { - if (bufs[i].buf == NULL) - continue; - dma_free_coherent(&info->pdev->dev, DMABUFSIZE, bufs[i].buf, - bufs[i].buf_dma_addr); - bufs[i].buf = NULL; - } -} - -static int alloc_dma_bufs(struct slgt_info *info) -{ - info->rbuf_count = 32; - info->tbuf_count = 32; - - if (alloc_desc(info) < 0 || - alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 || - alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 || - alloc_tmp_rbuf(info) < 0) { - DBGERR(("%s DMA buffer alloc fail\n", info->device_name)); - return -ENOMEM; - } - reset_rbufs(info); - return 0; -} - -static void free_dma_bufs(struct slgt_info *info) -{ - if (info->bufs) { - free_bufs(info, info->rbufs, info->rbuf_count); - free_bufs(info, info->tbufs, info->tbuf_count); - free_desc(info); - } - free_tmp_rbuf(info); -} - -static int claim_resources(struct slgt_info *info) -{ - if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) { - DBGERR(("%s reg addr conflict, addr=%08X\n", - info->device_name, info->phys_reg_addr)); - info->init_error = DiagStatus_AddressConflict; - goto errout; - } - else - info->reg_addr_requested = true; - - info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE); - if (!info->reg_addr) { - DBGERR(("%s can't map device registers, addr=%08X\n", - info->device_name, info->phys_reg_addr)); - info->init_error = DiagStatus_CantAssignPciResources; - goto errout; - } - return 0; - -errout: - release_resources(info); - return -ENODEV; -} - -static void release_resources(struct slgt_info *info) -{ - if (info->irq_requested) { - free_irq(info->irq_level, info); - info->irq_requested = false; - } - - if (info->reg_addr_requested) { - release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE); - info->reg_addr_requested = false; - } - - if (info->reg_addr) { - iounmap(info->reg_addr); - info->reg_addr = NULL; - } -} - -/* Add the specified device instance data structure to the - * global linked list of devices and increment the device count. - */ -static void add_device(struct slgt_info *info) -{ - char *devstr; - - info->next_device = NULL; - info->line = slgt_device_count; - sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line); - - if (info->line < MAX_DEVICES) { - if (maxframe[info->line]) - info->max_frame_size = maxframe[info->line]; - } - - slgt_device_count++; - - if (!slgt_device_list) - slgt_device_list = info; - else { - struct slgt_info *current_dev = slgt_device_list; - while(current_dev->next_device) - current_dev = current_dev->next_device; - current_dev->next_device = info; - } - - if (info->max_frame_size < 4096) - info->max_frame_size = 4096; - else if (info->max_frame_size > 65535) - info->max_frame_size = 65535; - - switch(info->pdev->device) { - case SYNCLINK_GT_DEVICE_ID: - devstr = "GT"; - break; - case SYNCLINK_GT2_DEVICE_ID: - devstr = "GT2"; - break; - case SYNCLINK_GT4_DEVICE_ID: - devstr = "GT4"; - break; - case SYNCLINK_AC_DEVICE_ID: - devstr = "AC"; - info->params.mode = MGSL_MODE_ASYNC; - break; - default: - devstr = "(unknown model)"; - } - printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n", - devstr, info->device_name, info->phys_reg_addr, - info->irq_level, info->max_frame_size); - -#if SYNCLINK_GENERIC_HDLC - hdlcdev_init(info); -#endif -} - -static const struct tty_port_operations slgt_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* - * allocate device instance structure, return NULL on failure - */ -static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) -{ - struct slgt_info *info; - - info = kzalloc_obj(struct slgt_info); - - if (!info) { - DBGERR(("%s device alloc failed adapter=%d port=%d\n", - driver_name, adapter_num, port_num)); - } else { - tty_port_init(&info->port); - info->port.ops = &slgt_port_ops; - INIT_WORK(&info->task, bh_handler); - info->max_frame_size = 4096; - info->base_clock = 14745600; - info->rbuf_fill_level = DMABUFSIZE; - init_waitqueue_head(&info->status_event_wait_q); - init_waitqueue_head(&info->event_wait_q); - spin_lock_init(&info->netlock); - memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; - info->adapter_num = adapter_num; - info->port_num = port_num; - - timer_setup(&info->tx_timer, tx_timeout, 0); - timer_setup(&info->rx_timer, rx_timeout, 0); - - /* Copy configuration info to device instance data */ - info->pdev = pdev; - info->irq_level = pdev->irq; - info->phys_reg_addr = pci_resource_start(pdev,0); - - info->bus_type = MGSL_BUS_TYPE_PCI; - info->irq_flags = IRQF_SHARED; - - info->init_error = -1; /* assume error, set to 0 on successful init */ - } - - return info; -} - -static void device_init(int adapter_num, struct pci_dev *pdev) -{ - struct slgt_info *port_array[SLGT_MAX_PORTS]; - int i; - int port_count = 1; - - if (pdev->device == SYNCLINK_GT2_DEVICE_ID) - port_count = 2; - else if (pdev->device == SYNCLINK_GT4_DEVICE_ID) - port_count = 4; - - /* allocate device instances for all ports */ - for (i=0; i < port_count; ++i) { - port_array[i] = alloc_dev(adapter_num, i, pdev); - if (port_array[i] == NULL) { - for (--i; i >= 0; --i) { - tty_port_destroy(&port_array[i]->port); - kfree(port_array[i]); - } - return; - } - } - - /* give copy of port_array to all ports and add to device list */ - for (i=0; i < port_count; ++i) { - memcpy(port_array[i]->port_array, port_array, sizeof(port_array)); - add_device(port_array[i]); - port_array[i]->port_count = port_count; - spin_lock_init(&port_array[i]->lock); - } - - /* Allocate and claim adapter resources */ - if (!claim_resources(port_array[0])) { - - alloc_dma_bufs(port_array[0]); - - /* copy resource information from first port to others */ - for (i = 1; i < port_count; ++i) { - port_array[i]->irq_level = port_array[0]->irq_level; - port_array[i]->reg_addr = port_array[0]->reg_addr; - alloc_dma_bufs(port_array[i]); - } - - if (request_irq(port_array[0]->irq_level, - slgt_interrupt, - port_array[0]->irq_flags, - port_array[0]->device_name, - port_array[0]) < 0) { - DBGERR(("%s request_irq failed IRQ=%d\n", - port_array[0]->device_name, - port_array[0]->irq_level)); - } else { - port_array[0]->irq_requested = true; - adapter_test(port_array[0]); - for (i=1 ; i < port_count ; i++) { - port_array[i]->init_error = port_array[0]->init_error; - port_array[i]->gpio_present = port_array[0]->gpio_present; - } - } - } - - for (i = 0; i < port_count; ++i) { - struct slgt_info *info = port_array[i]; - tty_port_register_device(&info->port, serial_driver, info->line, - &info->pdev->dev); - } -} - -static int init_one(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - if (pci_enable_device(dev)) { - printk("error enabling pci device %p\n", dev); - return -EIO; - } - pci_set_master(dev); - device_init(slgt_device_count, dev); - return 0; -} - -static void remove_one(struct pci_dev *dev) -{ -} - -static const struct tty_operations ops = { - .open = open, - .close = close, - .write = write, - .put_char = put_char, - .flush_chars = flush_chars, - .write_room = write_room, - .chars_in_buffer = chars_in_buffer, - .flush_buffer = flush_buffer, - .ioctl = ioctl, - .compat_ioctl = slgt_compat_ioctl, - .throttle = throttle, - .unthrottle = unthrottle, - .send_xchar = send_xchar, - .break_ctl = set_break, - .wait_until_sent = wait_until_sent, - .set_termios = set_termios, - .stop = tx_hold, - .start = tx_release, - .hangup = hangup, - .tiocmget = tiocmget, - .tiocmset = tiocmset, - .get_icount = get_icount, - .proc_show = synclink_gt_proc_show, -}; - -static void slgt_cleanup(void) -{ - struct slgt_info *info; - struct slgt_info *tmp; - - if (serial_driver) { - for (info=slgt_device_list ; info != NULL ; info=info->next_device) - tty_unregister_device(serial_driver, info->line); - tty_unregister_driver(serial_driver); - tty_driver_kref_put(serial_driver); - } - - /* reset devices */ - info = slgt_device_list; - while(info) { - reset_port(info); - info = info->next_device; - } - - /* release devices */ - info = slgt_device_list; - while(info) { -#if SYNCLINK_GENERIC_HDLC - hdlcdev_exit(info); -#endif - free_dma_bufs(info); - free_tmp_rbuf(info); - if (info->port_num == 0) - release_resources(info); - tmp = info; - info = info->next_device; - tty_port_destroy(&tmp->port); - kfree(tmp); - } - - if (pci_registered) - pci_unregister_driver(&pci_driver); -} - -/* - * Driver initialization entry point. - */ -static int __init slgt_init(void) -{ - int rc; - - serial_driver = tty_alloc_driver(MAX_DEVICES, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(serial_driver)) { - printk("%s can't allocate tty driver\n", driver_name); - return PTR_ERR(serial_driver); - } - - /* Initialize the tty_driver structure */ - - serial_driver->driver_name = "synclink_gt"; - serial_driver->name = tty_dev_prefix; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->init_termios.c_ispeed = 9600; - serial_driver->init_termios.c_ospeed = 9600; - tty_set_operations(serial_driver, &ops); - if ((rc = tty_register_driver(serial_driver)) < 0) { - DBGERR(("%s can't register serial driver\n", driver_name)); - tty_driver_kref_put(serial_driver); - serial_driver = NULL; - goto error; - } - - slgt_device_count = 0; - if ((rc = pci_register_driver(&pci_driver)) < 0) { - printk("%s pci_register_driver error=%d\n", driver_name, rc); - goto error; - } - pci_registered = true; - - return 0; - -error: - slgt_cleanup(); - return rc; -} - -static void __exit slgt_exit(void) -{ - slgt_cleanup(); -} - -module_init(slgt_init); -module_exit(slgt_exit); - -/* - * register access routines - */ - -static inline void __iomem *calc_regaddr(struct slgt_info *info, - unsigned int addr) -{ - void __iomem *reg_addr = info->reg_addr + addr; - - if (addr >= 0x80) - reg_addr += info->port_num * 32; - else if (addr >= 0x40) - reg_addr += info->port_num * 16; - - return reg_addr; -} - -static __u8 rd_reg8(struct slgt_info *info, unsigned int addr) -{ - return readb(calc_regaddr(info, addr)); -} - -static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value) -{ - writeb(value, calc_regaddr(info, addr)); -} - -static __u16 rd_reg16(struct slgt_info *info, unsigned int addr) -{ - return readw(calc_regaddr(info, addr)); -} - -static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value) -{ - writew(value, calc_regaddr(info, addr)); -} - -static __u32 rd_reg32(struct slgt_info *info, unsigned int addr) -{ - return readl(calc_regaddr(info, addr)); -} - -static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value) -{ - writel(value, calc_regaddr(info, addr)); -} - -static void rdma_reset(struct slgt_info *info) -{ - unsigned int i; - - /* set reset bit */ - wr_reg32(info, RDCSR, BIT1); - - /* wait for enable bit cleared */ - for(i=0 ; i < 1000 ; i++) - if (!(rd_reg32(info, RDCSR) & BIT0)) - break; -} - -static void tdma_reset(struct slgt_info *info) -{ - unsigned int i; - - /* set reset bit */ - wr_reg32(info, TDCSR, BIT1); - - /* wait for enable bit cleared */ - for(i=0 ; i < 1000 ; i++) - if (!(rd_reg32(info, TDCSR) & BIT0)) - break; -} - -/* - * enable internal loopback - * TxCLK and RxCLK are generated from BRG - * and TxD is looped back to RxD internally. - */ -static void enable_loopback(struct slgt_info *info) -{ - /* SCR (serial control) BIT2=loopback enable */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2)); - - if (info->params.mode != MGSL_MODE_ASYNC) { - /* CCR (clock control) - * 07..05 tx clock source (010 = BRG) - * 04..02 rx clock source (010 = BRG) - * 01 auxclk enable (0 = disable) - * 00 BRG enable (1 = enable) - * - * 0100 1001 - */ - wr_reg8(info, CCR, 0x49); - - /* set speed if available, otherwise use default */ - if (info->params.clock_speed) - set_rate(info, info->params.clock_speed); - else - set_rate(info, 3686400); - } -} - -/* - * set baud rate generator to specified rate - */ -static void set_rate(struct slgt_info *info, u32 rate) -{ - unsigned int div; - unsigned int osc = info->base_clock; - - /* div = osc/rate - 1 - * - * Round div up if osc/rate is not integer to - * force to next slowest rate. - */ - - if (rate) { - div = osc/rate; - if (!(osc % rate) && div) - div--; - wr_reg16(info, BDR, (unsigned short)div); - } -} - -static void rx_stop(struct slgt_info *info) -{ - unsigned short val; - - /* disable and reset receiver */ - val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, RCR, val); /* clear reset bit */ - - slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE); - - /* clear pending rx interrupts */ - wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER); - - rdma_reset(info); - - info->rx_enabled = false; - info->rx_restart = false; -} - -static void rx_start(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA); - - /* clear pending rx overrun IRQ */ - wr_reg16(info, SSR, IRQ_RXOVER); - - /* reset and disable receiver */ - val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */ - wr_reg16(info, RCR, val); /* clear reset bit */ - - rdma_reset(info); - reset_rbufs(info); - - if (info->rx_pio) { - /* rx request when rx FIFO not empty */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14)); - slgt_irq_on(info, IRQ_RXDATA); - if (info->params.mode == MGSL_MODE_ASYNC) { - /* enable saving of rx status */ - wr_reg32(info, RDCSR, BIT6); - } - } else { - /* rx request when rx FIFO half full */ - wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14)); - /* set 1st descriptor address */ - wr_reg32(info, RDDAR, info->rbufs[0].pdesc); - - if (info->params.mode != MGSL_MODE_ASYNC) { - /* enable rx DMA and DMA interrupt */ - wr_reg32(info, RDCSR, (BIT2 + BIT0)); - } else { - /* enable saving of rx status, rx DMA and DMA interrupt */ - wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0)); - } - } - - slgt_irq_on(info, IRQ_RXOVER); - - /* enable receiver */ - wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1)); - - info->rx_restart = false; - info->rx_enabled = true; -} - -static void tx_start(struct slgt_info *info) -{ - if (!info->tx_enabled) { - wr_reg16(info, TCR, - (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2)); - info->tx_enabled = true; - } - - if (desc_count(info->tbufs[info->tbuf_start])) { - info->drop_rts_on_tx_done = false; - - if (info->params.mode != MGSL_MODE_ASYNC) { - if (info->params.flags & HDLC_FLAG_AUTO_RTS) { - get_gtsignals(info); - if (!(info->signals & SerialSignal_RTS)) { - info->signals |= SerialSignal_RTS; - set_gtsignals(info); - info->drop_rts_on_tx_done = true; - } - } - - slgt_irq_off(info, IRQ_TXDATA); - slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); - /* clear tx idle and underrun status bits */ - wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); - } else { - slgt_irq_off(info, IRQ_TXDATA); - slgt_irq_on(info, IRQ_TXIDLE); - /* clear tx idle status bit */ - wr_reg16(info, SSR, IRQ_TXIDLE); - } - /* set 1st descriptor address and start DMA */ - wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); - wr_reg32(info, TDCSR, BIT2 + BIT0); - info->tx_active = true; - } -} - -static void tx_stop(struct slgt_info *info) -{ - unsigned short val; - - timer_delete(&info->tx_timer); - - tdma_reset(info); - - /* reset and disable transmitter */ - val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */ - wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */ - - slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER); - - /* clear tx idle and underrun status bit */ - wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); - - reset_tbufs(info); - - info->tx_enabled = false; - info->tx_active = false; -} - -static void reset_port(struct slgt_info *info) -{ - if (!info->reg_addr) - return; - - tx_stop(info); - rx_stop(info); - - info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_gtsignals(info); - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); -} - -static void reset_adapter(struct slgt_info *info) -{ - int i; - for (i=0; i < info->port_count; ++i) { - if (info->port_array[i]) - reset_port(info->port_array[i]); - } -} - -static void async_mode(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - tx_stop(info); - rx_stop(info); - - /* TCR (tx control) - * - * 15..13 mode, 010=async - * 12..10 encoding, 000=NRZ - * 09 parity enable - * 08 1=odd parity, 0=even parity - * 07 1=RTS driver control - * 06 1=break enable - * 05..04 character length - * 00=5 bits - * 01=6 bits - * 10=7 bits - * 11=8 bits - * 03 0=1 stop bit, 1=2 stop bits - * 02 reset - * 01 enable - * 00 auto-CTS enable - */ - val = 0x4000; - - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - - if (info->params.parity != ASYNC_PARITY_NONE) { - val |= BIT9; - if (info->params.parity == ASYNC_PARITY_ODD) - val |= BIT8; - } - - switch (info->params.data_bits) - { - case 6: val |= BIT4; break; - case 7: val |= BIT5; break; - case 8: val |= BIT5 + BIT4; break; - } - - if (info->params.stop_bits != 1) - val |= BIT3; - - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - val |= BIT0; - - wr_reg16(info, TCR, val); - - /* RCR (rx control) - * - * 15..13 mode, 010=async - * 12..10 encoding, 000=NRZ - * 09 parity enable - * 08 1=odd parity, 0=even parity - * 07..06 reserved, must be 0 - * 05..04 character length - * 00=5 bits - * 01=6 bits - * 10=7 bits - * 11=8 bits - * 03 reserved, must be zero - * 02 reset - * 01 enable - * 00 auto-DCD enable - */ - val = 0x4000; - - if (info->params.parity != ASYNC_PARITY_NONE) { - val |= BIT9; - if (info->params.parity == ASYNC_PARITY_ODD) - val |= BIT8; - } - - switch (info->params.data_bits) - { - case 6: val |= BIT4; break; - case 7: val |= BIT5; break; - case 8: val |= BIT5 + BIT4; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT0; - - wr_reg16(info, RCR, val); - - /* CCR (clock control) - * - * 07..05 011 = tx clock source is BRG/16 - * 04..02 010 = rx clock source is BRG - * 01 0 = auxclk disabled - * 00 1 = BRG enabled - * - * 0110 1001 - */ - wr_reg8(info, CCR, 0x69); - - msc_set_vcr(info); - - /* SCR (serial control) - * - * 15 1=tx req on FIFO half empty - * 14 1=rx req on FIFO half full - * 13 tx data IRQ enable - * 12 tx idle IRQ enable - * 11 rx break on IRQ enable - * 10 rx data IRQ enable - * 09 rx break off IRQ enable - * 08 overrun IRQ enable - * 07 DSR IRQ enable - * 06 CTS IRQ enable - * 05 DCD IRQ enable - * 04 RI IRQ enable - * 03 0=16x sampling, 1=8x sampling - * 02 1=txd->rxd internal loopback enable - * 01 reserved, must be zero - * 00 1=master IRQ enable - */ - val = BIT15 + BIT14 + BIT0; - /* JCR[8] : 1 = x8 async mode feature available */ - if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate && - ((info->base_clock < (info->params.data_rate * 16)) || - (info->base_clock % (info->params.data_rate * 16)))) { - /* use 8x sampling */ - val |= BIT3; - set_rate(info, info->params.data_rate * 8); - } else { - /* use 16x sampling */ - set_rate(info, info->params.data_rate * 16); - } - wr_reg16(info, SCR, val); - - slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER); - - if (info->params.loopback) - enable_loopback(info); -} - -static void sync_mode(struct slgt_info *info) -{ - unsigned short val; - - slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); - tx_stop(info); - rx_stop(info); - - /* TCR (tx control) - * - * 15..13 mode - * 000=HDLC/SDLC - * 001=raw bit synchronous - * 010=asynchronous/isochronous - * 011=monosync byte synchronous - * 100=bisync byte synchronous - * 101=xsync byte synchronous - * 12..10 encoding - * 09 CRC enable - * 08 CRC32 - * 07 1=RTS driver control - * 06 preamble enable - * 05..04 preamble length - * 03 share open/close flag - * 02 reset - * 01 enable - * 00 auto-CTS enable - */ - val = BIT2; - - switch(info->params.mode) { - case MGSL_MODE_XSYNC: - val |= BIT15 + BIT13; - break; - case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; - case MGSL_MODE_BISYNC: val |= BIT15; break; - case MGSL_MODE_RAW: val |= BIT13; break; - } - if (info->if_mode & MGSL_INTERFACE_RTS_EN) - val |= BIT7; - - switch(info->params.encoding) - { - case HDLC_ENCODING_NRZB: val |= BIT10; break; - case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; - case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; - case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; - case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; - case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; - } - - switch (info->params.crc_type & HDLC_CRC_MASK) - { - case HDLC_CRC_16_CCITT: val |= BIT9; break; - case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; - } - - if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) - val |= BIT6; - - switch (info->params.preamble_length) - { - case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break; - case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break; - case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_CTS) - val |= BIT0; - - wr_reg16(info, TCR, val); - - /* TPR (transmit preamble) */ - - switch (info->params.preamble) - { - case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break; - case HDLC_PREAMBLE_PATTERN_ONES: val = 0xff; break; - case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break; - case HDLC_PREAMBLE_PATTERN_10: val = 0x55; break; - case HDLC_PREAMBLE_PATTERN_01: val = 0xaa; break; - default: val = 0x7e; break; - } - wr_reg8(info, TPR, (unsigned char)val); - - /* RCR (rx control) - * - * 15..13 mode - * 000=HDLC/SDLC - * 001=raw bit synchronous - * 010=asynchronous/isochronous - * 011=monosync byte synchronous - * 100=bisync byte synchronous - * 101=xsync byte synchronous - * 12..10 encoding - * 09 CRC enable - * 08 CRC32 - * 07..03 reserved, must be 0 - * 02 reset - * 01 enable - * 00 auto-DCD enable - */ - val = 0; - - switch(info->params.mode) { - case MGSL_MODE_XSYNC: - val |= BIT15 + BIT13; - break; - case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; - case MGSL_MODE_BISYNC: val |= BIT15; break; - case MGSL_MODE_RAW: val |= BIT13; break; - } - - switch(info->params.encoding) - { - case HDLC_ENCODING_NRZB: val |= BIT10; break; - case HDLC_ENCODING_NRZI_MARK: val |= BIT11; break; - case HDLC_ENCODING_NRZI: val |= BIT11 + BIT10; break; - case HDLC_ENCODING_BIPHASE_MARK: val |= BIT12; break; - case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break; - case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break; - } - - switch (info->params.crc_type & HDLC_CRC_MASK) - { - case HDLC_CRC_16_CCITT: val |= BIT9; break; - case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break; - } - - if (info->params.flags & HDLC_FLAG_AUTO_DCD) - val |= BIT0; - - wr_reg16(info, RCR, val); - - /* CCR (clock control) - * - * 07..05 tx clock source - * 04..02 rx clock source - * 01 auxclk enable - * 00 BRG enable - */ - val = 0; - - if (info->params.flags & HDLC_FLAG_TXC_BRG) - { - // when RxC source is DPLL, BRG generates 16X DPLL - // reference clock, so take TxC from BRG/16 to get - // transmit clock at actual data rate - if (info->params.flags & HDLC_FLAG_RXC_DPLL) - val |= BIT6 + BIT5; /* 011, txclk = BRG/16 */ - else - val |= BIT6; /* 010, txclk = BRG */ - } - else if (info->params.flags & HDLC_FLAG_TXC_DPLL) - val |= BIT7; /* 100, txclk = DPLL Input */ - else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN) - val |= BIT5; /* 001, txclk = RXC Input */ - - if (info->params.flags & HDLC_FLAG_RXC_BRG) - val |= BIT3; /* 010, rxclk = BRG */ - else if (info->params.flags & HDLC_FLAG_RXC_DPLL) - val |= BIT4; /* 100, rxclk = DPLL */ - else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN) - val |= BIT2; /* 001, rxclk = TXC Input */ - - if (info->params.clock_speed) - val |= BIT1 + BIT0; - - wr_reg8(info, CCR, (unsigned char)val); - - if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL)) - { - // program DPLL mode - switch(info->params.encoding) - { - case HDLC_ENCODING_BIPHASE_MARK: - case HDLC_ENCODING_BIPHASE_SPACE: - val = BIT7; break; - case HDLC_ENCODING_BIPHASE_LEVEL: - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: - val = BIT7 + BIT6; break; - default: val = BIT6; // NRZ encodings - } - wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val)); - - // DPLL requires a 16X reference clock from BRG - set_rate(info, info->params.clock_speed * 16); - } - else - set_rate(info, info->params.clock_speed); - - tx_set_idle(info); - - msc_set_vcr(info); - - /* SCR (serial control) - * - * 15 1=tx req on FIFO half empty - * 14 1=rx req on FIFO half full - * 13 tx data IRQ enable - * 12 tx idle IRQ enable - * 11 underrun IRQ enable - * 10 rx data IRQ enable - * 09 rx idle IRQ enable - * 08 overrun IRQ enable - * 07 DSR IRQ enable - * 06 CTS IRQ enable - * 05 DCD IRQ enable - * 04 RI IRQ enable - * 03 reserved, must be zero - * 02 1=txd->rxd internal loopback enable - * 01 reserved, must be zero - * 00 1=master IRQ enable - */ - wr_reg16(info, SCR, BIT15 + BIT14 + BIT0); - - if (info->params.loopback) - enable_loopback(info); -} - -/* - * set transmit idle mode - */ -static void tx_set_idle(struct slgt_info *info) -{ - unsigned char val; - unsigned short tcr; - - /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits - * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits - */ - tcr = rd_reg16(info, TCR); - if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) { - /* disable preamble, set idle size to 16 bits */ - tcr = (tcr & ~(BIT6 + BIT5)) | BIT4; - /* MSB of 16 bit idle specified in tx preamble register (TPR) */ - wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff)); - } else if (!(tcr & BIT6)) { - /* preamble is disabled, set idle size to 8 bits */ - tcr &= ~(BIT5 + BIT4); - } - wr_reg16(info, TCR, tcr); - - if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) { - /* LSB of custom tx idle specified in tx idle register */ - val = (unsigned char)(info->idle_mode & 0xff); - } else { - /* standard 8 bit idle patterns */ - switch(info->idle_mode) - { - case HDLC_TXIDLE_FLAGS: val = 0x7e; break; - case HDLC_TXIDLE_ALT_ZEROS_ONES: - case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break; - case HDLC_TXIDLE_ZEROS: - case HDLC_TXIDLE_SPACE: val = 0x00; break; - default: val = 0xff; - } - } - - wr_reg8(info, TIR, val); -} - -/* - * get state of V24 status (input) signals - */ -static void get_gtsignals(struct slgt_info *info) -{ - unsigned short status = rd_reg16(info, SSR); - - /* clear all serial signals except RTS and DTR */ - info->signals &= SerialSignal_RTS | SerialSignal_DTR; - - if (status & BIT3) - info->signals |= SerialSignal_DSR; - if (status & BIT2) - info->signals |= SerialSignal_CTS; - if (status & BIT1) - info->signals |= SerialSignal_DCD; - if (status & BIT0) - info->signals |= SerialSignal_RI; -} - -/* - * set V.24 Control Register based on current configuration - */ -static void msc_set_vcr(struct slgt_info *info) -{ - unsigned char val = 0; - - /* VCR (V.24 control) - * - * 07..04 serial IF select - * 03 DTR - * 02 RTS - * 01 LL - * 00 RL - */ - - switch(info->if_mode & MGSL_INTERFACE_MASK) - { - case MGSL_INTERFACE_RS232: - val |= BIT5; /* 0010 */ - break; - case MGSL_INTERFACE_V35: - val |= BIT7 + BIT6 + BIT5; /* 1110 */ - break; - case MGSL_INTERFACE_RS422: - val |= BIT6; /* 0100 */ - break; - } - - if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) - val |= BIT4; - if (info->signals & SerialSignal_DTR) - val |= BIT3; - if (info->signals & SerialSignal_RTS) - val |= BIT2; - if (info->if_mode & MGSL_INTERFACE_LL) - val |= BIT1; - if (info->if_mode & MGSL_INTERFACE_RL) - val |= BIT0; - wr_reg8(info, VCR, val); -} - -/* - * set state of V24 control (output) signals - */ -static void set_gtsignals(struct slgt_info *info) -{ - unsigned char val = rd_reg8(info, VCR); - if (info->signals & SerialSignal_DTR) - val |= BIT3; - else - val &= ~BIT3; - if (info->signals & SerialSignal_RTS) - val |= BIT2; - else - val &= ~BIT2; - wr_reg8(info, VCR, val); -} - -/* - * free range of receive DMA buffers (i to last) - */ -static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last) -{ - int done = 0; - - while(!done) { - /* reset current buffer for reuse */ - info->rbufs[i].status = 0; - set_desc_count(info->rbufs[i], info->rbuf_fill_level); - if (i == last) - done = 1; - if (++i == info->rbuf_count) - i = 0; - } - info->rbuf_current = i; -} - -/* - * mark all receive DMA buffers as free - */ -static void reset_rbufs(struct slgt_info *info) -{ - free_rbufs(info, 0, info->rbuf_count - 1); - info->rbuf_fill_index = 0; - info->rbuf_fill_count = 0; -} - -/* - * pass receive HDLC frame to upper layer - * - * return true if frame available, otherwise false - */ -static bool rx_get_frame(struct slgt_info *info) -{ - unsigned int start, end; - unsigned short status; - unsigned int framesize = 0; - unsigned long flags; - struct tty_struct *tty = info->port.tty; - unsigned char addr_field = 0xff; - unsigned int crc_size = 0; - - switch (info->params.crc_type & HDLC_CRC_MASK) { - case HDLC_CRC_16_CCITT: crc_size = 2; break; - case HDLC_CRC_32_CCITT: crc_size = 4; break; - } - -check_again: - - framesize = 0; - addr_field = 0xff; - start = end = info->rbuf_current; - - for (;;) { - if (!desc_complete(info->rbufs[end])) - goto cleanup; - - if (framesize == 0 && info->params.addr_filter != 0xff) - addr_field = info->rbufs[end].buf[0]; - - framesize += desc_count(info->rbufs[end]); - - if (desc_eof(info->rbufs[end])) - break; - - if (++end == info->rbuf_count) - end = 0; - - if (end == info->rbuf_current) { - if (info->rx_enabled){ - spin_lock_irqsave(&info->lock,flags); - rx_start(info); - spin_unlock_irqrestore(&info->lock,flags); - } - goto cleanup; - } - } - - /* status - * - * 15 buffer complete - * 14..06 reserved - * 05..04 residue - * 02 eof (end of frame) - * 01 CRC error - * 00 abort - */ - status = desc_status(info->rbufs[end]); - - /* ignore CRC bit if not using CRC (bit is undefined) */ - if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE) - status &= ~BIT1; - - if (framesize == 0 || - (addr_field != 0xff && addr_field != info->params.addr_filter)) { - free_rbufs(info, start, end); - goto check_again; - } - - if (framesize < (2 + crc_size) || status & BIT0) { - info->icount.rxshort++; - framesize = 0; - } else if (status & BIT1) { - info->icount.rxcrc++; - if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) - framesize = 0; - } - -#if SYNCLINK_GENERIC_HDLC - if (framesize == 0) { - info->netdev->stats.rx_errors++; - info->netdev->stats.rx_frame_errors++; - } -#endif - - DBGBH(("%s rx frame status=%04X size=%d\n", - info->device_name, status, framesize)); - DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); - - if (framesize) { - if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { - framesize -= crc_size; - crc_size = 0; - } - - if (framesize > info->max_frame_size + crc_size) - info->icount.rxlong++; - else { - /* copy dma buffer(s) to contiguous temp buffer */ - int copy_count = framesize; - int i = start; - unsigned char *p = info->tmp_rbuf; - info->tmp_rbuf_count = framesize; - - info->icount.rxok++; - - while(copy_count) { - int partial_count = min_t(int, copy_count, info->rbuf_fill_level); - memcpy(p, info->rbufs[i].buf, partial_count); - p += partial_count; - copy_count -= partial_count; - if (++i == info->rbuf_count) - i = 0; - } - - if (info->params.crc_type & HDLC_CRC_RETURN_EX) { - *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK; - framesize++; - } - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_rx(info,info->tmp_rbuf, framesize); - else -#endif - ldisc_receive_buf(tty, info->tmp_rbuf, NULL, - framesize); - } - } - free_rbufs(info, start, end); - return true; - -cleanup: - return false; -} - -/* - * pass receive buffer (RAW synchronous mode) to tty layer - * return true if buffer available, otherwise false - */ -static bool rx_get_buf(struct slgt_info *info) -{ - unsigned int i = info->rbuf_current; - unsigned int count; - - if (!desc_complete(info->rbufs[i])) - return false; - count = desc_count(info->rbufs[i]); - switch(info->params.mode) { - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - case MGSL_MODE_XSYNC: - /* ignore residue in byte synchronous modes */ - if (desc_residue(info->rbufs[i])) - count--; - break; - } - DBGDATA(info, info->rbufs[i].buf, count, "rx"); - DBGINFO(("rx_get_buf size=%d\n", count)); - if (count) - ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, NULL, - count); - free_rbufs(info, i, i); - return true; -} - -static void reset_tbufs(struct slgt_info *info) -{ - unsigned int i; - info->tbuf_current = 0; - for (i=0 ; i < info->tbuf_count ; i++) { - info->tbufs[i].status = 0; - info->tbufs[i].count = 0; - } -} - -/* - * return number of free transmit DMA buffers - */ -static unsigned int free_tbuf_count(struct slgt_info *info) -{ - unsigned int count = 0; - unsigned int i = info->tbuf_current; - - do - { - if (desc_count(info->tbufs[i])) - break; /* buffer in use */ - ++count; - if (++i == info->tbuf_count) - i=0; - } while (i != info->tbuf_current); - - /* if tx DMA active, last zero count buffer is in use */ - if (count && (rd_reg32(info, TDCSR) & BIT0)) - --count; - - return count; -} - -/* - * return number of bytes in unsent transmit DMA buffers - * and the serial controller tx FIFO - */ -static unsigned int tbuf_bytes(struct slgt_info *info) -{ - unsigned int total_count = 0; - unsigned int i = info->tbuf_current; - unsigned int reg_value; - unsigned int count; - unsigned int active_buf_count = 0; - - /* - * Add descriptor counts for all tx DMA buffers. - * If count is zero (cleared by DMA controller after read), - * the buffer is complete or is actively being read from. - * - * Record buf_count of last buffer with zero count starting - * from current ring position. buf_count is mirror - * copy of count and is not cleared by serial controller. - * If DMA controller is active, that buffer is actively - * being read so add to total. - */ - do { - count = desc_count(info->tbufs[i]); - if (count) - total_count += count; - else if (!total_count) - active_buf_count = info->tbufs[i].buf_count; - if (++i == info->tbuf_count) - i = 0; - } while (i != info->tbuf_current); - - /* read tx DMA status register */ - reg_value = rd_reg32(info, TDCSR); - - /* if tx DMA active, last zero count buffer is in use */ - if (reg_value & BIT0) - total_count += active_buf_count; - - /* add tx FIFO count = reg_value[15..8] */ - total_count += (reg_value >> 8) & 0xff; - - /* if transmitter active add one byte for shift register */ - if (info->tx_active) - total_count++; - - return total_count; -} - -/* - * load data into transmit DMA buffer ring and start transmitter if needed - * return true if data accepted, otherwise false (buffers full) - */ -static bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int size) -{ - unsigned short count; - unsigned int i; - struct slgt_desc *d; - - /* check required buffer space */ - if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info)) - return false; - - DBGDATA(info, buf, size, "tx"); - - /* - * copy data to one or more DMA buffers in circular ring - * tbuf_start = first buffer for this data - * tbuf_current = next free buffer - * - * Copy all data before making data visible to DMA controller by - * setting descriptor count of the first buffer. - * This prevents an active DMA controller from reading the first DMA - * buffers of a frame and stopping before the final buffers are filled. - */ - - info->tbuf_start = i = info->tbuf_current; - - while (size) { - d = &info->tbufs[i]; - - count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size); - memcpy(d->buf, buf, count); - - size -= count; - buf += count; - - /* - * set EOF bit for last buffer of HDLC frame or - * for every buffer in raw mode - */ - if ((!size && info->params.mode == MGSL_MODE_HDLC) || - info->params.mode == MGSL_MODE_RAW) - set_desc_eof(*d, 1); - else - set_desc_eof(*d, 0); - - /* set descriptor count for all but first buffer */ - if (i != info->tbuf_start) - set_desc_count(*d, count); - d->buf_count = count; - - if (++i == info->tbuf_count) - i = 0; - } - - info->tbuf_current = i; - - /* set first buffer count to make new data visible to DMA controller */ - d = &info->tbufs[info->tbuf_start]; - set_desc_count(*d, d->buf_count); - - /* start transmitter if needed and update transmit timeout */ - if (!info->tx_active) - tx_start(info); - update_tx_timer(info); - - return true; -} - -static int register_test(struct slgt_info *info) -{ - static unsigned short patterns[] = - {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696}; - static unsigned int count = ARRAY_SIZE(patterns); - unsigned int i; - int rc = 0; - - for (i=0 ; i < count ; i++) { - wr_reg16(info, TIR, patterns[i]); - wr_reg16(info, BDR, patterns[(i+1)%count]); - if ((rd_reg16(info, TIR) != patterns[i]) || - (rd_reg16(info, BDR) != patterns[(i+1)%count])) { - rc = -ENODEV; - break; - } - } - info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0; - info->init_error = rc ? 0 : DiagStatus_AddressFailure; - return rc; -} - -static int irq_test(struct slgt_info *info) -{ - unsigned long timeout; - unsigned long flags; - struct tty_struct *oldtty = info->port.tty; - u32 speed = info->params.data_rate; - - info->params.data_rate = 921600; - info->port.tty = NULL; - - spin_lock_irqsave(&info->lock, flags); - async_mode(info); - slgt_irq_on(info, IRQ_TXIDLE); - - /* enable transmitter */ - wr_reg16(info, TCR, - (unsigned short)(rd_reg16(info, TCR) | BIT1)); - - /* write one byte and wait for tx idle */ - wr_reg16(info, TDR, 0); - - /* assume failure */ - info->init_error = DiagStatus_IrqFailure; - info->irq_occurred = false; - - spin_unlock_irqrestore(&info->lock, flags); - - timeout=100; - while(timeout-- && !info->irq_occurred) - msleep_interruptible(10); - - spin_lock_irqsave(&info->lock,flags); - reset_port(info); - spin_unlock_irqrestore(&info->lock,flags); - - info->params.data_rate = speed; - info->port.tty = oldtty; - - info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure; - return info->irq_occurred ? 0 : -ENODEV; -} - -static int loopback_test_rx(struct slgt_info *info) -{ - unsigned char *src, *dest; - int count; - - if (desc_complete(info->rbufs[0])) { - count = desc_count(info->rbufs[0]); - src = info->rbufs[0].buf; - dest = info->tmp_rbuf; - - for( ; count ; count-=2, src+=2) { - /* src=data byte (src+1)=status byte */ - if (!(*(src+1) & (BIT9 + BIT8))) { - *dest = *src; - dest++; - info->tmp_rbuf_count++; - } - } - DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx"); - return 1; - } - return 0; -} - -static int loopback_test(struct slgt_info *info) -{ -#define TESTFRAMESIZE 20 - - unsigned long timeout; - u16 count; - unsigned char buf[TESTFRAMESIZE]; - int rc = -ENODEV; - unsigned long flags; - - struct tty_struct *oldtty = info->port.tty; - MGSL_PARAMS params; - - memcpy(¶ms, &info->params, sizeof(params)); - - info->params.mode = MGSL_MODE_ASYNC; - info->params.data_rate = 921600; - info->params.loopback = 1; - info->port.tty = NULL; - - /* build and send transmit frame */ - for (count = 0; count < TESTFRAMESIZE; ++count) - buf[count] = (unsigned char)count; - - info->tmp_rbuf_count = 0; - memset(info->tmp_rbuf, 0, TESTFRAMESIZE); - - /* program hardware for HDLC and enabled receiver */ - spin_lock_irqsave(&info->lock,flags); - async_mode(info); - rx_start(info); - tx_load(info, buf, count); - spin_unlock_irqrestore(&info->lock, flags); - - /* wait for receive complete */ - for (timeout = 100; timeout; --timeout) { - msleep_interruptible(10); - if (loopback_test_rx(info)) { - rc = 0; - break; - } - } - - /* verify received frame length and contents */ - if (!rc && (info->tmp_rbuf_count != count || - memcmp(buf, info->tmp_rbuf, count))) { - rc = -ENODEV; - } - - spin_lock_irqsave(&info->lock,flags); - reset_adapter(info); - spin_unlock_irqrestore(&info->lock,flags); - - memcpy(&info->params, ¶ms, sizeof(info->params)); - info->port.tty = oldtty; - - info->init_error = rc ? DiagStatus_DmaFailure : 0; - return rc; -} - -static int adapter_test(struct slgt_info *info) -{ - DBGINFO(("testing %s\n", info->device_name)); - if (register_test(info) < 0) { - printk("register test failure %s addr=%08X\n", - info->device_name, info->phys_reg_addr); - } else if (irq_test(info) < 0) { - printk("IRQ test failure %s IRQ=%d\n", - info->device_name, info->irq_level); - } else if (loopback_test(info) < 0) { - printk("loopback test failure %s\n", info->device_name); - } - return info->init_error; -} - -/* - * transmit timeout handler - */ -static void tx_timeout(struct timer_list *t) -{ - struct slgt_info *info = timer_container_of(info, t, tx_timer); - unsigned long flags; - - DBGINFO(("%s tx_timeout\n", info->device_name)); - if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { - info->icount.txtimeout++; - } - spin_lock_irqsave(&info->lock,flags); - tx_stop(info); - spin_unlock_irqrestore(&info->lock,flags); - -#if SYNCLINK_GENERIC_HDLC - if (info->netcount) - hdlcdev_tx_done(info); - else -#endif - bh_transmit(info); -} - -/* - * receive buffer polling timer - */ -static void rx_timeout(struct timer_list *t) -{ - struct slgt_info *info = timer_container_of(info, t, rx_timer); - unsigned long flags; - - DBGINFO(("%s rx_timeout\n", info->device_name)); - spin_lock_irqsave(&info->lock, flags); - info->pending_bh |= BH_RECEIVE; - spin_unlock_irqrestore(&info->lock, flags); - bh_handler(&info->task); -} - diff --git a/include/linux/synclink.h b/include/linux/synclink.h deleted file mode 100644 index f1405b1c71ba..000000000000 --- a/include/linux/synclink.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SyncLink Multiprotocol Serial Adapter Driver - * - * $Id: synclink.h,v 3.14 2006/07/17 20:15:43 paulkf Exp $ - * - * Copyright (C) 1998-2000 by Microgate Corporation - * - * Redistribution of this file is permitted under - * the terms of the GNU Public License (GPL) - */ -#ifndef _SYNCLINK_H_ -#define _SYNCLINK_H_ - -#include - -/* provide 32 bit ioctl compatibility on 64 bit systems */ -#ifdef CONFIG_COMPAT -#include -struct MGSL_PARAMS32 { - compat_ulong_t mode; - unsigned char loopback; - unsigned short flags; - unsigned char encoding; - compat_ulong_t clock_speed; - unsigned char addr_filter; - unsigned short crc_type; - unsigned char preamble_length; - unsigned char preamble; - compat_ulong_t data_rate; - unsigned char data_bits; - unsigned char stop_bits; - unsigned char parity; -}; -#define MGSL_IOCSPARAMS32 _IOW(MGSL_MAGIC_IOC,0,struct MGSL_PARAMS32) -#define MGSL_IOCGPARAMS32 _IOR(MGSL_MAGIC_IOC,1,struct MGSL_PARAMS32) -#endif -#endif /* _SYNCLINK_H_ */ diff --git a/include/uapi/linux/synclink.h b/include/uapi/linux/synclink.h deleted file mode 100644 index 62f32d4e1021..000000000000 --- a/include/uapi/linux/synclink.h +++ /dev/null @@ -1,301 +0,0 @@ -/* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ -/* - * SyncLink Multiprotocol Serial Adapter Driver - * - * $Id: synclink.h,v 3.14 2006/07/17 20:15:43 paulkf Exp $ - * - * Copyright (C) 1998-2000 by Microgate Corporation - * - * Redistribution of this file is permitted under - * the terms of the GNU Public License (GPL) - */ - -#ifndef _UAPI_SYNCLINK_H_ -#define _UAPI_SYNCLINK_H_ -#define SYNCLINK_H_VERSION 3.6 - -#include - -#define BIT0 0x0001 -#define BIT1 0x0002 -#define BIT2 0x0004 -#define BIT3 0x0008 -#define BIT4 0x0010 -#define BIT5 0x0020 -#define BIT6 0x0040 -#define BIT7 0x0080 -#define BIT8 0x0100 -#define BIT9 0x0200 -#define BIT10 0x0400 -#define BIT11 0x0800 -#define BIT12 0x1000 -#define BIT13 0x2000 -#define BIT14 0x4000 -#define BIT15 0x8000 -#define BIT16 0x00010000 -#define BIT17 0x00020000 -#define BIT18 0x00040000 -#define BIT19 0x00080000 -#define BIT20 0x00100000 -#define BIT21 0x00200000 -#define BIT22 0x00400000 -#define BIT23 0x00800000 -#define BIT24 0x01000000 -#define BIT25 0x02000000 -#define BIT26 0x04000000 -#define BIT27 0x08000000 -#define BIT28 0x10000000 -#define BIT29 0x20000000 -#define BIT30 0x40000000 -#define BIT31 0x80000000 - - -#define HDLC_MAX_FRAME_SIZE 65535 -#define MAX_ASYNC_TRANSMIT 4096 -#define MAX_ASYNC_BUFFER_SIZE 4096 - -#define ASYNC_PARITY_NONE 0 -#define ASYNC_PARITY_EVEN 1 -#define ASYNC_PARITY_ODD 2 -#define ASYNC_PARITY_SPACE 3 - -#define HDLC_FLAG_UNDERRUN_ABORT7 0x0000 -#define HDLC_FLAG_UNDERRUN_ABORT15 0x0001 -#define HDLC_FLAG_UNDERRUN_FLAG 0x0002 -#define HDLC_FLAG_UNDERRUN_CRC 0x0004 -#define HDLC_FLAG_SHARE_ZERO 0x0010 -#define HDLC_FLAG_AUTO_CTS 0x0020 -#define HDLC_FLAG_AUTO_DCD 0x0040 -#define HDLC_FLAG_AUTO_RTS 0x0080 -#define HDLC_FLAG_RXC_DPLL 0x0100 -#define HDLC_FLAG_RXC_BRG 0x0200 -#define HDLC_FLAG_RXC_TXCPIN 0x8000 -#define HDLC_FLAG_RXC_RXCPIN 0x0000 -#define HDLC_FLAG_TXC_DPLL 0x0400 -#define HDLC_FLAG_TXC_BRG 0x0800 -#define HDLC_FLAG_TXC_TXCPIN 0x0000 -#define HDLC_FLAG_TXC_RXCPIN 0x0008 -#define HDLC_FLAG_DPLL_DIV8 0x1000 -#define HDLC_FLAG_DPLL_DIV16 0x2000 -#define HDLC_FLAG_DPLL_DIV32 0x0000 -#define HDLC_FLAG_HDLC_LOOPMODE 0x4000 - -#define HDLC_CRC_NONE 0 -#define HDLC_CRC_16_CCITT 1 -#define HDLC_CRC_32_CCITT 2 -#define HDLC_CRC_MASK 0x00ff -#define HDLC_CRC_RETURN_EX 0x8000 - -#define RX_OK 0 -#define RX_CRC_ERROR 1 - -#define HDLC_TXIDLE_FLAGS 0 -#define HDLC_TXIDLE_ALT_ZEROS_ONES 1 -#define HDLC_TXIDLE_ZEROS 2 -#define HDLC_TXIDLE_ONES 3 -#define HDLC_TXIDLE_ALT_MARK_SPACE 4 -#define HDLC_TXIDLE_SPACE 5 -#define HDLC_TXIDLE_MARK 6 -#define HDLC_TXIDLE_CUSTOM_8 0x10000000 -#define HDLC_TXIDLE_CUSTOM_16 0x20000000 - -#define HDLC_ENCODING_NRZ 0 -#define HDLC_ENCODING_NRZB 1 -#define HDLC_ENCODING_NRZI_MARK 2 -#define HDLC_ENCODING_NRZI_SPACE 3 -#define HDLC_ENCODING_NRZI HDLC_ENCODING_NRZI_SPACE -#define HDLC_ENCODING_BIPHASE_MARK 4 -#define HDLC_ENCODING_BIPHASE_SPACE 5 -#define HDLC_ENCODING_BIPHASE_LEVEL 6 -#define HDLC_ENCODING_DIFF_BIPHASE_LEVEL 7 - -#define HDLC_PREAMBLE_LENGTH_8BITS 0 -#define HDLC_PREAMBLE_LENGTH_16BITS 1 -#define HDLC_PREAMBLE_LENGTH_32BITS 2 -#define HDLC_PREAMBLE_LENGTH_64BITS 3 - -#define HDLC_PREAMBLE_PATTERN_NONE 0 -#define HDLC_PREAMBLE_PATTERN_ZEROS 1 -#define HDLC_PREAMBLE_PATTERN_FLAGS 2 -#define HDLC_PREAMBLE_PATTERN_10 3 -#define HDLC_PREAMBLE_PATTERN_01 4 -#define HDLC_PREAMBLE_PATTERN_ONES 5 - -#define MGSL_MODE_ASYNC 1 -#define MGSL_MODE_HDLC 2 -#define MGSL_MODE_MONOSYNC 3 -#define MGSL_MODE_BISYNC 4 -#define MGSL_MODE_RAW 6 -#define MGSL_MODE_BASE_CLOCK 7 -#define MGSL_MODE_XSYNC 8 - -#define MGSL_BUS_TYPE_ISA 1 -#define MGSL_BUS_TYPE_EISA 2 -#define MGSL_BUS_TYPE_PCI 5 - -#define MGSL_INTERFACE_MASK 0xf -#define MGSL_INTERFACE_DISABLE 0 -#define MGSL_INTERFACE_RS232 1 -#define MGSL_INTERFACE_V35 2 -#define MGSL_INTERFACE_RS422 3 -#define MGSL_INTERFACE_RTS_EN 0x10 -#define MGSL_INTERFACE_LL 0x20 -#define MGSL_INTERFACE_RL 0x40 -#define MGSL_INTERFACE_MSB_FIRST 0x80 - -typedef struct _MGSL_PARAMS -{ - /* Common */ - - unsigned long mode; /* Asynchronous or HDLC */ - unsigned char loopback; /* internal loopback mode */ - - /* HDLC Only */ - - unsigned short flags; - unsigned char encoding; /* NRZ, NRZI, etc. */ - unsigned long clock_speed; /* external clock speed in bits per second */ - unsigned char addr_filter; /* receive HDLC address filter, 0xFF = disable */ - unsigned short crc_type; /* None, CRC16-CCITT, or CRC32-CCITT */ - unsigned char preamble_length; - unsigned char preamble; - - /* Async Only */ - - unsigned long data_rate; /* bits per second */ - unsigned char data_bits; /* 7 or 8 data bits */ - unsigned char stop_bits; /* 1 or 2 stop bits */ - unsigned char parity; /* none, even, or odd */ - -} MGSL_PARAMS, *PMGSL_PARAMS; - -#define MICROGATE_VENDOR_ID 0x13c0 -#define SYNCLINK_DEVICE_ID 0x0010 -#define MGSCC_DEVICE_ID 0x0020 -#define SYNCLINK_SCA_DEVICE_ID 0x0030 -#define SYNCLINK_GT_DEVICE_ID 0x0070 -#define SYNCLINK_GT4_DEVICE_ID 0x0080 -#define SYNCLINK_AC_DEVICE_ID 0x0090 -#define SYNCLINK_GT2_DEVICE_ID 0x00A0 -#define MGSL_MAX_SERIAL_NUMBER 30 - -/* -** device diagnostics status -*/ - -#define DiagStatus_OK 0 -#define DiagStatus_AddressFailure 1 -#define DiagStatus_AddressConflict 2 -#define DiagStatus_IrqFailure 3 -#define DiagStatus_IrqConflict 4 -#define DiagStatus_DmaFailure 5 -#define DiagStatus_DmaConflict 6 -#define DiagStatus_PciAdapterNotFound 7 -#define DiagStatus_CantAssignPciResources 8 -#define DiagStatus_CantAssignPciMemAddr 9 -#define DiagStatus_CantAssignPciIoAddr 10 -#define DiagStatus_CantAssignPciIrq 11 -#define DiagStatus_MemoryError 12 - -#define SerialSignal_DCD 0x01 /* Data Carrier Detect */ -#define SerialSignal_TXD 0x02 /* Transmit Data */ -#define SerialSignal_RI 0x04 /* Ring Indicator */ -#define SerialSignal_RXD 0x08 /* Receive Data */ -#define SerialSignal_CTS 0x10 /* Clear to Send */ -#define SerialSignal_RTS 0x20 /* Request to Send */ -#define SerialSignal_DSR 0x40 /* Data Set Ready */ -#define SerialSignal_DTR 0x80 /* Data Terminal Ready */ - - -/* - * Counters of the input lines (CTS, DSR, RI, CD) interrupts - */ -struct mgsl_icount { - __u32 cts, dsr, rng, dcd, tx, rx; - __u32 frame, parity, overrun, brk; - __u32 buf_overrun; - __u32 txok; - __u32 txunder; - __u32 txabort; - __u32 txtimeout; - __u32 rxshort; - __u32 rxlong; - __u32 rxabort; - __u32 rxover; - __u32 rxcrc; - __u32 rxok; - __u32 exithunt; - __u32 rxidle; -}; - -struct gpio_desc { - __u32 state; - __u32 smask; - __u32 dir; - __u32 dmask; -}; - -#define DEBUG_LEVEL_DATA 1 -#define DEBUG_LEVEL_ERROR 2 -#define DEBUG_LEVEL_INFO 3 -#define DEBUG_LEVEL_BH 4 -#define DEBUG_LEVEL_ISR 5 - -/* -** Event bit flags for use with MgslWaitEvent -*/ - -#define MgslEvent_DsrActive 0x0001 -#define MgslEvent_DsrInactive 0x0002 -#define MgslEvent_Dsr 0x0003 -#define MgslEvent_CtsActive 0x0004 -#define MgslEvent_CtsInactive 0x0008 -#define MgslEvent_Cts 0x000c -#define MgslEvent_DcdActive 0x0010 -#define MgslEvent_DcdInactive 0x0020 -#define MgslEvent_Dcd 0x0030 -#define MgslEvent_RiActive 0x0040 -#define MgslEvent_RiInactive 0x0080 -#define MgslEvent_Ri 0x00c0 -#define MgslEvent_ExitHuntMode 0x0100 -#define MgslEvent_IdleReceived 0x0200 - -/* Private IOCTL codes: - * - * MGSL_IOCSPARAMS set MGSL_PARAMS structure values - * MGSL_IOCGPARAMS get current MGSL_PARAMS structure values - * MGSL_IOCSTXIDLE set current transmit idle mode - * MGSL_IOCGTXIDLE get current transmit idle mode - * MGSL_IOCTXENABLE enable or disable transmitter - * MGSL_IOCRXENABLE enable or disable receiver - * MGSL_IOCTXABORT abort transmitting frame (HDLC) - * MGSL_IOCGSTATS return current statistics - * MGSL_IOCWAITEVENT wait for specified event to occur - * MGSL_LOOPTXDONE transmit in HDLC LoopMode done - * MGSL_IOCSIF set the serial interface type - * MGSL_IOCGIF get the serial interface type - */ -#define MGSL_MAGIC_IOC 'm' -#define MGSL_IOCSPARAMS _IOW(MGSL_MAGIC_IOC,0,struct _MGSL_PARAMS) -#define MGSL_IOCGPARAMS _IOR(MGSL_MAGIC_IOC,1,struct _MGSL_PARAMS) -#define MGSL_IOCSTXIDLE _IO(MGSL_MAGIC_IOC,2) -#define MGSL_IOCGTXIDLE _IO(MGSL_MAGIC_IOC,3) -#define MGSL_IOCTXENABLE _IO(MGSL_MAGIC_IOC,4) -#define MGSL_IOCRXENABLE _IO(MGSL_MAGIC_IOC,5) -#define MGSL_IOCTXABORT _IO(MGSL_MAGIC_IOC,6) -#define MGSL_IOCGSTATS _IO(MGSL_MAGIC_IOC,7) -#define MGSL_IOCWAITEVENT _IOWR(MGSL_MAGIC_IOC,8,int) -#define MGSL_IOCCLRMODCOUNT _IO(MGSL_MAGIC_IOC,15) -#define MGSL_IOCLOOPTXDONE _IO(MGSL_MAGIC_IOC,9) -#define MGSL_IOCSIF _IO(MGSL_MAGIC_IOC,10) -#define MGSL_IOCGIF _IO(MGSL_MAGIC_IOC,11) -#define MGSL_IOCSGPIO _IOW(MGSL_MAGIC_IOC,16,struct gpio_desc) -#define MGSL_IOCGGPIO _IOR(MGSL_MAGIC_IOC,17,struct gpio_desc) -#define MGSL_IOCWAITGPIO _IOWR(MGSL_MAGIC_IOC,18,struct gpio_desc) -#define MGSL_IOCSXSYNC _IO(MGSL_MAGIC_IOC, 19) -#define MGSL_IOCGXSYNC _IO(MGSL_MAGIC_IOC, 20) -#define MGSL_IOCSXCTRL _IO(MGSL_MAGIC_IOC, 21) -#define MGSL_IOCGXCTRL _IO(MGSL_MAGIC_IOC, 22) - - -#endif /* _UAPI_SYNCLINK_H_ */ From 44e55f1f3088e4a471a943fbcf087ea7783a0199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 28 Apr 2026 16:40:33 +0200 Subject: [PATCH 0793/5214] serial: 8250_pci: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... and PCI device helpers. The various struct pci_device_id were defined using a mixture of initialization by position and by name. Some use the PCI device helpers (like PCI_DEVICE and PCI_DEVICE_SUB) and others don't. Consistently use named initializers, drop assignments of 0 by position for .class and .class_mask and use the PCI device helpers. Also use consistent line-breaks and positioning for opening and closing curly braces. The secret plan is to make struct pci_device_id::driver_data an anonymous union (similar to https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/) and that requires named initializers. But it's also a nice cleanup on its own. This patch doesn't change the compiled result; this was verified using an allmodconfig with several things disabled that make reproducible builds harder on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260428144033.1037617-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 3120 ++++++++++++++-------------- 1 file changed, 1563 insertions(+), 1557 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 2fbd8f2603b5..3e5bc9e8d269 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -143,16 +143,15 @@ struct serial_private { #define PCIE_DEVICE_ID_AX99100 0x9100 static const struct pci_device_id pci_use_msi[] = { - { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x1000) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x1000) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, - 0xA000, 0x1000) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100, - 0xA000, 0x1000) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL, - PCI_ANY_ID, PCI_ANY_ID) }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, + 0xA000, 0x1000) }, + { PCI_VDEVICE(HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL) }, { PCI_DEVICE_SUB(PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100, 0xA000, 0x1000) }, { } @@ -4040,42 +4039,42 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ /* WCH CH353 2S1P */ - { PCI_VDEVICE(WCHCN, 0x7053), REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHCN, 0x7053), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, /* WCH CH353 1S1P */ - { PCI_VDEVICE(WCHCN, 0x5053), REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHCN, 0x5053), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, /* WCH CH382 2S1P */ - { PCI_VDEVICE(WCHIC, 0x3250), REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHIC, 0x3250), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, /* Intel platforms with MID UART */ - { PCI_VDEVICE(INTEL, 0x081b), REPORT_8250_CONFIG(MID), }, - { PCI_VDEVICE(INTEL, 0x081c), REPORT_8250_CONFIG(MID), }, - { PCI_VDEVICE(INTEL, 0x081d), REPORT_8250_CONFIG(MID), }, - { PCI_VDEVICE(INTEL, 0x1191), REPORT_8250_CONFIG(MID), }, - { PCI_VDEVICE(INTEL, 0x18d8), REPORT_8250_CONFIG(MID), }, - { PCI_VDEVICE(INTEL, 0x19d8), REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081b), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081c), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081d), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x1191), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x18d8), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x19d8), .driver_data = REPORT_8250_CONFIG(MID), }, /* Intel platforms with DesignWare UART */ - { PCI_VDEVICE(INTEL, 0x0936), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x0f0a), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x0f0c), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x228a), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x228c), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b96), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b97), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b98), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b99), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b9a), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x4b9b), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x9ce3), REPORT_8250_CONFIG(LPSS), }, - { PCI_VDEVICE(INTEL, 0x9ce4), REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0936), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0c), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228c), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b96), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b97), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b98), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b99), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9b), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce3), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce4), .driver_data = REPORT_8250_CONFIG(LPSS), }, /* Exar devices */ - { PCI_VDEVICE(EXAR, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), }, - { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), }, + { PCI_VDEVICE(EXAR, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(EXAR), }, + { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(EXAR), }, /* Pericom devices */ - { PCI_VDEVICE(PERICOM, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), }, - { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), }, + { PCI_VDEVICE(PERICOM, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, + { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, /* End of the black list */ { } @@ -4448,713 +4447,753 @@ static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one, pciserial_resume_one); static const struct pci_device_id serial_pci_tbl[] = { - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600, - PCI_DEVICE_ID_ADVANTECH_PCI1600_1611, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, - PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, - pbn_b2_8_921600 }, - /* Advantech also use 0x3618 and 0xf618 */ - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, - PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, - PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, - pbn_b1_2_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, - pbn_b1_2_1250000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, - pbn_b0_2_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, - pbn_b0_4_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_AFAVLAB, - PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_1_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, + { + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600, + PCI_DEVICE_ID_ADVANTECH_PCI1600_1611, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001), + .driver_data = pbn_b2_8_921600, + }, { + /* Advantech also use 0x3618 and 0xf618 */ + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232), + .driver_data = pbn_b1_8_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232), + .driver_data = pbn_b1_4_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232), + .driver_data = pbn_b1_2_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232), + .driver_data = pbn_b1_8_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232), + .driver_data = pbn_b1_4_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232), + .driver_data = pbn_b1_2_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485), + .driver_data = pbn_b1_2_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ), + .driver_data = pbn_b1_2_1250000, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2), + .driver_data = pbn_b0_2_1843200, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4), + .driver_data = pbn_b0_4_1843200, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_AFAVLAB, PCI_SUBDEVICE_ID_AFAVLAB_P061), + .driver_data = pbn_b0_4_1152000, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530), + .driver_data = pbn_b2_bt_1_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422), + .driver_data = pbn_b2_bt_4_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4), + .driver_data = pbn_b2_bt_4_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_SPCOM200), + .driver_data = pbn_b2_bt_2_921600, + }, { + /* VScom SPCOM800, from sl@s.pl */ + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_SPCOM800), + .driver_data = pbn_b2_8_921600, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_1077), + .driver_data = pbn_b2_4_921600, + }, { + /* Unknown card - subdevice 0x1584 */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1584), + .driver_data = pbn_b2_4_115200, + }, { + /* Unknown card - subdevice 0x1588 */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1588), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, PCI_SUBDEVICE_ID_KEYSPAN_SX2), + .driver_data = pbn_panacom, + }, { + PCI_VDEVICE(PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM), + .driver_data = pbn_panacom4, + }, { + PCI_VDEVICE(PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM), + .driver_data = pbn_panacom2, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, PCI_DEVICE_ID_ESDGMBH_CPCIASIO4), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST4), + .driver_data = pbn_b2_4_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST8), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST16), + .driver_data = pbn_b2_16_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC), + .driver_data = pbn_b2_16_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS4), + .driver_data = pbn_b2_4_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4055), + .driver_data = pbn_b2_4_115200, + }, { + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a), + .driver_data = pbn_plx_romulus, + }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - /* - * VScom SPCOM800, from sl@s.pl - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_921600 }, - /* Unknown card - subdevice 0x1584 */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, - pbn_b2_4_115200 }, - /* Unknown card - subdevice 0x1588 */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0, - pbn_b2_8_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, - pbn_panacom }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom4 }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom2 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_VENDOR_ID_ESDGMBH, - PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, - PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, - pbn_b2_4_115200 }, - /* - * Megawolf Romulus PCI Serial Card, from Mike Hudson - * (Exoray@isys.ca) - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, 0, 0, - pbn_plx_romulus }, /* * Quatech cards. These actually have configurable clocks but for * now we just use the default. * * 100 series are RS232, 200 series RS422, */ - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_8_115200 }, - - { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, - 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, - 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_OXSEMI, 0x9505, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - + { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSC100), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC100), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC200), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSC200), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100), + .driver_data = pbn_b0_8_115200, + }, { + PCI_VDEVICE_SUB(SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL), + .driver_data = pbn_b0_4_1152000, + }, { + PCI_VDEVICE(OXSEMI, 0x9505), + .driver_data = pbn_b0_bt_2_921600, + }, { /* * The below card is a little controversial since it is the * subject of a PCI vendor/device ID clash. (See * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). * For now just used the hex ID 0x950a. */ - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00, - 0, 0, pbn_b0_2_115200 }, - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30, - 0, 0, pbn_b0_2_115200 }, - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_1130000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, - PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_1152000 }, + PCI_VDEVICE_SUB(OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE_SUB(OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(OXSEMI, 0x950a), + .driver_data = pbn_b0_2_1130000, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, + PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958), + .driver_data = pbn_b2_8_1152000, + }, /* * Oxford Semiconductor Inc. Tornado PCI express device range. */ - { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(OXSEMI, 0xc101), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc105), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc11b), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc11f), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc120), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc124), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc138), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc13d), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc140), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc141), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc144), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc145), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc158), /* OXPCIe952 2 Native UART */ + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc15d), /* OXPCIe952 2 Native UART */ + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc208), /* OXPCIe954 4 Native UART */ + .driver_data = pbn_oxsemi_4_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc20d), /* OXPCIe954 4 Native UART */ + .driver_data = pbn_oxsemi_4_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc308), /* OXPCIe958 8 Native UART */ + .driver_data = pbn_oxsemi_8_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc30d), /* OXPCIe958 8 Native UART */ + .driver_data = pbn_oxsemi_8_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc40b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc40f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc41b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc41f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc42b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc42f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc43b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc43f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc44b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc44f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc45b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc45f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc46b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc46f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc47b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc47f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc48b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc48f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc49b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc49f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4ab), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4af), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4bb), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4bf), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4cb), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4cf), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado */ - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, - pbn_oxsemi_1_15625000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, - pbn_oxsemi_2_15625000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, - pbn_oxsemi_4_15625000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, - pbn_oxsemi_8_15625000 }, + { + /* IQ Express 1 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4001), + .driver_data = pbn_oxsemi_1_15625000, + }, { + /* IQ Express 2 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4002), + .driver_data = pbn_oxsemi_2_15625000, + }, { + /* IQ Express 4 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4004), + .driver_data = pbn_oxsemi_4_15625000, + }, { + /* IQ Express 8 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4008), + .driver_data = pbn_oxsemi_8_15625000, + }, /* * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado */ - { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, - PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE_SUB(DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, + PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* * EndRun Technologies. PCI express device range. * EndRun PTP/1588 has 2 Native UARTs utilizing OxSemi 952. */ - { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(ENDRUN, PCI_DEVICE_ID_ENDRUN_1588), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, * from skokodyn@yahoo.com */ - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, - pbn_sbsxrsio }, + { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422), + .driver_data = pbn_sbsxrsio, + }, /* * Digitan DS560-558, from jimd@esoft.com */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, + { + PCI_VDEVICE(ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM), + .driver_data = pbn_b1_1_115200, + }, /* * Titan Electronic cards * The 400L and 800L have a custom setup quirk. */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_1_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_4_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_8_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_titan_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, + { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200), + .driver_data = pbn_b0_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800B), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100L), + .driver_data = pbn_b1_1_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200L), + .driver_data = pbn_b1_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400L), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800L), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200I), + .driver_data = pbn_b4_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400I), + .driver_data = pbn_b4_bt_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800I), + .driver_data = pbn_b4_bt_8_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400EH), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800EH), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800EHB), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100E), + .driver_data = pbn_titan_1_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200E), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400E), + .driver_data = pbn_titan_4_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800E), + .driver_data = pbn_titan_8_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200EI), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200EISI), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200V3), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_410V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800V3B), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850), + .driver_data = pbn_b0_bt_8_921600, + }, /* * Computone devices submitted by Doug McNash dmcnash@computone.com */ - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - 0, 0, pbn_computone_4 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - 0, 0, pbn_computone_8 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - 0, 0, pbn_computone_6 }, + { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4), + .driver_data = pbn_computone_4, + }, { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8), + .driver_data = pbn_computone_8, + }, { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6), + .driver_data = pbn_computone_6, + }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_921600 }, + { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N), + .driver_data = pbn_oxsemi, + }, { + PCI_VDEVICE_SUB(TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID), + .driver_data = pbn_b0_bt_1_921600, + }, /* * Sunix PCI serial boards */ - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0001, 0, 0, - pbn_sunix_pci_1s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0002, 0, 0, - pbn_sunix_pci_2s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0004, 0, 0, - pbn_sunix_pci_4s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0084, 0, 0, - pbn_sunix_pci_4s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0008, 0, 0, - pbn_sunix_pci_8s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0088, 0, 0, - pbn_sunix_pci_8s }, - { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, - PCI_VENDOR_ID_SUNIX, 0x0010, 0, 0, - pbn_sunix_pci_16s }, + { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0001), + .driver_data = pbn_sunix_pci_1s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0002), + .driver_data = pbn_sunix_pci_2s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0004), + .driver_data = pbn_sunix_pci_4s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0084), + .driver_data = pbn_sunix_pci_4s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0008), + .driver_data = pbn_sunix_pci_8s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0088), + .driver_data = pbn_sunix_pci_8s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0010), + .driver_data = pbn_sunix_pci_16s, + }, /* * AFAVLAB serial card, from Harald Welte */ - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_460800 }, + { + PCI_VDEVICE(AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028), + .driver_data = pbn_b0_bt_8_115200, + }, { + PCI_VDEVICE(AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030), + .driver_data = pbn_b0_bt_8_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DSERIAL), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_OCTO_A), + .driver_data = pbn_b0_bt_4_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_OCTO_B), + .driver_data = pbn_b0_bt_4_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUAD_A), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUAD_B), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_SSERIAL), + .driver_data = pbn_b0_bt_1_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PORT_650), + .driver_data = pbn_b0_bt_1_460800, + }, /* * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). @@ -5164,560 +5203,533 @@ static const struct pci_device_id serial_pci_tbl[] = { * Note that JC140x are RS422/485 cards which require ox950 * ACR = 0x10, and as such are not currently fully supported. */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1402, 0x0002, 0, 0, - pbn_b0_2_921600 }, */ -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1404, 0x0004, 0, 0, - pbn_b0_4_921600 }, */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, + { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1204, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, +/* { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1402, 0x0002), + .driver_data = pbn_b0_2_921600, + }, */ +/* { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1404, 0x0004), + .driver_data = pbn_b0_4_921600, + }, */ + { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, /* * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, + { + PCI_VDEVICE(DELL, PCI_DEVICE_ID_DELL_RAC4), + .driver_data = pbn_b1_1_1382400, + }, /* * Dell Remote Access Card III - Tim_T_Murphy@Dell.com */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, + { + PCI_VDEVICE(DELL, PCI_DEVICE_ID_DELL_RACIII), + .driver_data = pbn_b1_1_1382400, + }, /* * RAStel 2 port modem, gerg@moreton.com.au */ - { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, + { + PCI_VDEVICE(MORETON, PCI_DEVICE_ID_RASTEL_2PORT), + .driver_data = pbn_b2_bt_2_115200, + }, /* * EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, - 0xE4BF, PCI_ANY_ID, 0, 0, - pbn_intel_i960 }, + { + PCI_VDEVICE_SUB(INTEL, PCI_DEVICE_ID_INTEL_80960_RP, + 0xE4BF, PCI_ANY_ID), + .driver_data = pbn_intel_i960, + }, /* * Xircom Cardbus/Ethernet combos */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, + { + PCI_VDEVICE(XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM), + .driver_data = pbn_b0_1_115200, + }, /* * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, + { + PCI_VDEVICE(XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G), + .driver_data = pbn_b0_1_115200, + }, /* * Untested PCI modems, sent in from various folks... */ - /* - * Elsa Model 56K PCI Modem, from Andreas Rath - */ - { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, 0, 0, - pbn_sgi_ioc3 }, + { + /* Elsa Model 56K PCI Modem, from Andreas Rath */ + PCI_VDEVICE_SUB(ROCKWELL, 0x1004, 0x1048, 0x1500), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE_SUB(SGI, PCI_DEVICE_ID_SGI_IOC3, 0xFF00, 0), + .driver_data = pbn_sgi_ioc3, + }, /* * HP Diva card */ - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, - pbn_b1_1_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_5_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - /* HPE PCI serial device */ - { PCI_VENDOR_ID_HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_2_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_4_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_8_115200 }, - /* - * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) - */ - { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - /* - * ITE - */ - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b1_bt_1_115200 }, - - /* - * IntaShield IS-100 - */ - { PCI_VENDOR_ID_INTASHIELD, 0x0D60, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - /* - * IntaShield IS-200 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0d80 */ - pbn_b2_2_115200 }, - /* - * IntaShield IS-400 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ - pbn_b2_4_115200 }, - /* - * IntaShield IX-100 - */ - { PCI_VENDOR_ID_INTASHIELD, 0x4027, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, - /* - * IntaShield IX-200 - */ - { PCI_VENDOR_ID_INTASHIELD, 0x4028, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, - /* - * IntaShield IX-400 - */ - { PCI_VENDOR_ID_INTASHIELD, 0x4029, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE_SUB(HP, PCI_DEVICE_ID_HP_DIVA, + PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_DIVA), + .driver_data = pbn_b0_5_115200, + }, { + PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_DIVA_AUX), + .driver_data = pbn_b2_1_115200, + }, { + /* HPE PCI serial device */ + PCI_VDEVICE(HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM2), + .driver_data = pbn_b3_2_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM4), + .driver_data = pbn_b3_4_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM8), + .driver_data = pbn_b3_8_115200, + }, { + /* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) */ + PCI_VDEVICE(TOPIC, PCI_DEVICE_ID_TOPIC_TP560), + .driver_data = pbn_b0_1_115200, + }, { + /* ITE */ + PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872), + .driver_data = pbn_b1_bt_1_115200, + }, { + /* IntaShield IS-100 */ + PCI_VDEVICE(INTASHIELD, 0x0D60), + .driver_data = pbn_b2_1_115200, + }, { + /* IntaShield IS-200; 135a.0d80 */ + PCI_VDEVICE(INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200), + .driver_data = pbn_b2_2_115200, + }, { + /* IntaShield IS-400; 135a.0dc0 */ + PCI_VDEVICE(INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400), + .driver_data = pbn_b2_4_115200, + }, { + /* IntaShield IX-100 */ + PCI_VDEVICE(INTASHIELD, 0x4027), + .driver_data = pbn_oxsemi_1_15625000, + }, { + /* IntaShield IX-200 */ + PCI_VDEVICE(INTASHIELD, 0x4028), + .driver_data = pbn_oxsemi_2_15625000, + }, { + /* IntaShield IX-400 */ + PCI_VDEVICE(INTASHIELD, 0x4029), + .driver_data = pbn_oxsemi_4_15625000, + }, /* Brainboxes Devices */ /* * Brainboxes UC-101 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0BA1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0BA2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0BA3, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0BA1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0BA2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0BA3), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-235/246 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0AA1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_1_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0AA2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_1_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0AA1), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AA2), + .driver_data = pbn_b2_1_115200, + }, /* * Brainboxes UC-253/UC-734 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0CA1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0CA1), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-260/271/701/756 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0D21, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0E34, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0D21), + .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + .class_mask = 0xffff00, + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0E34), + .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + .class_mask = 0xffff00, + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UC-268 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0841, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0841), + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UC-275/279 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0881, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_8_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0881), + .driver_data = pbn_b2_8_115200, + }, /* * Brainboxes UC-302 */ - { PCI_VENDOR_ID_INTASHIELD, 0x08E1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x08E2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x08E3, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x08E1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08E2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08E3), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-310 */ - { PCI_VENDOR_ID_INTASHIELD, 0x08C1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x08C1), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-313 */ - { PCI_VENDOR_ID_INTASHIELD, 0x08A1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x08A2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x08A3, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x08A1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08A2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08A3), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-320/324 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0A61, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_1_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0A61), + .driver_data = pbn_b2_1_115200, + }, /* * Brainboxes UC-346 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0B01, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0B02, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0B01), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B02), + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UC-357 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0A81, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0A82, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0A83, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0A81), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0A82), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0A83), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-368 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0C41, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C42, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C43, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0C41), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C42), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C43), + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UC-420 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0921, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0921), + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UC-607 */ - { PCI_VENDOR_ID_INTASHIELD, 0x09A1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x09A2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x09A3, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x09A1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x09A2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x09A3), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UC-836 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0D41, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_4_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0D41), + .driver_data = pbn_b2_4_115200, + }, /* * Brainboxes UP-189 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0AC1, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0AC2, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0AC3, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0AC1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AC2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AC3), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UP-200 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0B21, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0B22, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0B23, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0B21), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B22), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B23), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UP-869 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0C01, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C02, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C03, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0C01), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C02), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C03), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes UP-880 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0C21, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C22, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x0C23, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_2_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0C21), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C22), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C23), + .driver_data = pbn_b2_2_115200, + }, /* * Brainboxes PX-101 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4005, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4019, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4005), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4019), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * Brainboxes PX-235/246 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4004, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_1_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4016, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4004), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4016), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Brainboxes PX-203/PX-257 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4006, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4015, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4006), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4015), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * Brainboxes PX-260/PX-701 */ - { PCI_VENDOR_ID_INTASHIELD, 0x400A, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x400A), + .driver_data = pbn_oxsemi_4_15625000, + }, /* * Brainboxes PX-275/279 */ - { PCI_VENDOR_ID_INTASHIELD, 0x0E41, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b2_8_115200 }, + { + PCI_VDEVICE(INTASHIELD, 0x0E41), + .driver_data = pbn_b2_8_115200, + }, /* * Brainboxes PX-310 */ - { PCI_VENDOR_ID_INTASHIELD, 0x400E, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x400E), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * Brainboxes PX-313 */ - { PCI_VENDOR_ID_INTASHIELD, 0x400C, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x400C), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * Brainboxes PX-320/324/PX-376/PX-387 */ - { PCI_VENDOR_ID_INTASHIELD, 0x400B, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x400B), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Brainboxes PX-335/346 */ - { PCI_VENDOR_ID_INTASHIELD, 0x400F, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x400F), + .driver_data = pbn_oxsemi_4_15625000, + }, /* * Brainboxes PX-368 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4010, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4010), + .driver_data = pbn_oxsemi_4_15625000, + }, /* * Brainboxes PX-420 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4000, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4011, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4000), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4011), + .driver_data = pbn_oxsemi_4_15625000, + }, /* * Brainboxes PX-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x401D, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x401D), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Brainboxes PX-803/PX-857 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4009, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_2_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4018, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, - { PCI_VENDOR_ID_INTASHIELD, 0x401E, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_2_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4009), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4018), + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(INTASHIELD, 0x401E), + .driver_data = pbn_oxsemi_2_15625000, + }, /* * Brainboxes PX-820 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4002, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4013, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_4_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4002), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4013), + .driver_data = pbn_oxsemi_4_15625000, + }, /* * Brainboxes PX-835/PX-846 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4008, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b0_1_115200 }, - { PCI_VENDOR_ID_INTASHIELD, 0x4017, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4008), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4017), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Brainboxes XC-235 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4026, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4026), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Brainboxes XC-475 */ - { PCI_VENDOR_ID_INTASHIELD, 0x4021, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_oxsemi_1_15625000 }, + { + PCI_VDEVICE(INTASHIELD, 0x4021), + .driver_data = pbn_oxsemi_1_15625000, + }, /* * Perle PCI-RAS cards */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, - 0, 0, pbn_b2_4_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, - 0, 0, pbn_b2_8_921600 }, + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4), + .driver_data = pbn_b2_4_921600, + }, + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8), + .driver_data = pbn_b2_8_921600, + }, /* * Mainpine series cards: Fairly standard layout but fools @@ -5725,375 +5737,343 @@ static const struct pci_device_id serial_pci_tbl[] = { * unmatched communications subclasses in the PCI Express case */ - { /* RockForceDUO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0300, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0400, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0600, - 0, 0, pbn_b0_2_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0700, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0800, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0C00, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUARTRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0D00, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x1D00, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceD1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2000, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceF1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2100, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceD2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceF2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2300, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceD4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2400, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceF4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceD8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2600, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceF8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2700, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express D1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3000, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express F1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3100, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express D2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3200, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express F2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3300, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express D4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3400, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express F4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3500, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express D8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3C00, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express F8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3D00, - 0, 0, pbn_b0_8_115200 }, + { + /* RockForceDUO */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0200), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUATRO */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0300), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceDUO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0400), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUATRO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0500), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForce+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0600), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForce+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0700), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceOCTO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0800), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceDUO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0C00), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUARTRO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0D00), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceOCTO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x1D00), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceD1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2000), + .driver_data = pbn_b0_1_115200, + }, { + /* RockForceF1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2100), + .driver_data = pbn_b0_1_115200, + }, { /* RockForceD2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2200), + .driver_data = pbn_b0_2_115200, + }, { /* RockForceF2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2300), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceD4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2400), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceF4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2500), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceD8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2600), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceF8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2700), + .driver_data = pbn_b0_8_115200, + }, { + /* IQ Express D1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3000), + .driver_data = pbn_b0_1_115200, + }, { + /* IQ Express F1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3100), + .driver_data = pbn_b0_1_115200, + }, { + /* IQ Express D2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3200), + .driver_data = pbn_b0_2_115200, + }, { + /* IQ Express F2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3300), + .driver_data = pbn_b0_2_115200, + }, { + /* IQ Express D4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3400), + .driver_data = pbn_b0_4_115200, + }, { + /* IQ Express F4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3500), + .driver_data = pbn_b0_4_115200, + }, { + /* IQ Express D8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3C00), + .driver_data = pbn_b0_8_115200, + }, { + /* IQ Express F8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3D00), + .driver_data = pbn_b0_8_115200, + }, - - /* - * PA Semi PA6T-1682M on-chip UART - */ - { PCI_VENDOR_ID_PASEMI, 0xa004, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pasemi_1682M }, + { + /* PA Semi PA6T-1682M on-chip UART */ + PCI_VDEVICE(PASEMI, 0xa004), + .driver_data = pbn_pasemi_1682M, + }, /* * National Instruments */ - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, + { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI23216), + .driver_data = pbn_b1_16_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2328), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2324I), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2322I), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_23216), + .driver_data = pbn_b1_16_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2328), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8422_2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8422_2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2328), + .driver_data = pbn_ni8430_8, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2328), + .driver_data = pbn_ni8430_8, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_23216), + .driver_data = pbn_ni8430_16, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_23216), + .driver_data = pbn_ni8430_16, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8432_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8432_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8432_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8432_2324), + .driver_data = pbn_ni8430_4, + }, /* * MOXA */ - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A), pbn_moxa_8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B), pbn_moxa_8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A), pbn_moxa_8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I), pbn_moxa_8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N), pbn_moxa_2 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N), pbn_moxa_4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A), pbn_moxa_8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A), pbn_moxa_8 }, + { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A), + .driver_data = pbn_moxa_8, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B), + .driver_data = pbn_moxa_8, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A), + .driver_data = pbn_moxa_8, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I), + .driver_data = pbn_moxa_8, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N), + .driver_data = pbn_moxa_2, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N), + .driver_data = pbn_moxa_4, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A), + .driver_data = pbn_moxa_8, + }, { + PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A), + .driver_data = pbn_moxa_8, + }, /* * ADDI-DATA GmbH communication cards */ - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_AMCC, - PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b1_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7800_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_4_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_2_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_1_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_8_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_CPCI7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_VENDOR_ID_IBM, 0x0299, - 0, 0, pbn_b0_bt_2_115200 }, + { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(AMCC, PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500_2), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420_2), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300_2), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500_3), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420_3), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300_3), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7800_3), + .driver_data = pbn_b0_8_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7500), + .driver_data = pbn_ADDIDATA_PCIe_4_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7420), + .driver_data = pbn_ADDIDATA_PCIe_2_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7300), + .driver_data = pbn_ADDIDATA_PCIe_1_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7800), + .driver_data = pbn_ADDIDATA_PCIe_8_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7500), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_VENDOR_ID_IBM, 0x0299), + .driver_data = pbn_b0_bt_2_115200, + }, /* * other NetMos 9835 devices are most likely handled by the @@ -6101,157 +6081,183 @@ static const struct pci_device_id serial_pci_tbl[] = { * before adding them here. */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - /* the 9901 is a rebranded 9912 */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3002, - 0, 0, pbn_NETMOS9900_2s_115200 }, - - { PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, + { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + /* the 9901 is a rebranded 9912 */ + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002), + .driver_data = pbn_NETMOS9900_2s_115200, + }, { + PCI_DEVICE_SUB(PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, /* * Best Connectivity and Rosewill PCI Multi I/O cards */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x3002, - 0, 0, pbn_b0_bt_2_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x3004, - 0, 0, pbn_b0_bt_4_115200 }, + { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3002), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004), + .driver_data = pbn_b0_bt_4_115200, + }, /* * ASIX AX99100 PCIe to Multi I/O Controller */ - { PCI_VENDOR_ID_ASIX, PCI_DEVICE_ID_ASIX_AX99100, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, + { + PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, /* Intel CE4100 */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ce4100_1_115200 }, + { + PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART), + .driver_data = pbn_ce4100_1_115200, + }, /* * Cronyx Omega PCI */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_omegapci }, + { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA), + .driver_data = pbn_omegapci, + }, /* * Broadcom TruManage */ - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_brcm_trumanage }, + { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE), + .driver_data = pbn_brcm_trumanage, + }, /* * AgeStar as-prs2-009 */ - { PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_bt_2_115200 }, + { + PCI_VDEVICE(AGESTAR, PCI_DEVICE_ID_AGESTAR_9375), + .driver_data = pbn_b0_bt_2_115200, + }, /* * WCH CH353 series devices: The 2S1P is handled by parport_serial * so not listed here. */ - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_4S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_bt_4_115200 }, + { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_4S), + .driver_data = pbn_b0_bt_4_115200, + }, { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1PF), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH355_4S), + .driver_data = pbn_b0_bt_4_115200, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S), + .driver_data = pbn_wch382_2, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH384_4S), + .driver_data = pbn_wch384_4, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH384_8S), + .driver_data = pbn_wch384_8, + }, - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1PF, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_bt_2_115200 }, - - { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH355_4S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_bt_4_115200 }, - - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_wch382_2 }, - - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH384_4S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_wch384_4 }, - - { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH384_8S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_wch384_8 }, /* * Realtek RealManage */ - { PCI_VENDOR_ID_REALTEK, 0x816a, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_REALTEK, 0x816b, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_1_115200 }, + { + PCI_VDEVICE(REALTEK, 0x816a), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(REALTEK, 0x816b), + .driver_data = pbn_b0_1_115200, + }, /* Systembase Multi I/O cards */ - { PCI_VDEVICE(SYSTEMBASE, 0x0008), pbn_b0_8_921600 }, + { + PCI_VDEVICE(SYSTEMBASE, 0x0008), + .driver_data = pbn_b0_8_921600, + }, /* Fintek PCI serial cards */ - { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, - { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, - { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, - { PCI_DEVICE(0x1c29, 0x1204), .driver_data = pbn_fintek_F81504A }, - { PCI_DEVICE(0x1c29, 0x1208), .driver_data = pbn_fintek_F81508A }, - { PCI_DEVICE(0x1c29, 0x1212), .driver_data = pbn_fintek_F81512A }, + { + PCI_DEVICE(0x1c29, 0x1104), + .driver_data = pbn_fintek_4, + }, { + PCI_DEVICE(0x1c29, 0x1108), + .driver_data = pbn_fintek_8, + }, { + PCI_DEVICE(0x1c29, 0x1112), + .driver_data = pbn_fintek_12, + }, { + PCI_DEVICE(0x1c29, 0x1204), + .driver_data = pbn_fintek_F81504A, + }, { + PCI_DEVICE(0x1c29, 0x1208), + .driver_data = pbn_fintek_F81508A, + }, { + PCI_DEVICE(0x1c29, 0x1212), + .driver_data = pbn_fintek_F81512A, + }, /* MKS Tenta SCOM-080x serial cards */ - { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, - { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + { + PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000, + }, { + PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000, + }, /* Amazon PCI serial device */ - { PCI_DEVICE(0x1d0f, 0x8250), .driver_data = pbn_b0_1_115200 }, + { + PCI_DEVICE(0x1d0f, 0x8250), .driver_data = pbn_b0_1_115200, + }, /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL */ - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MODEM << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, - 0xffff00, pbn_default }, - { 0, } + { + PCI_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00), + .driver_data = pbn_default, + }, { + PCI_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00), + .driver_data = pbn_default, + }, { + PCI_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00), + .driver_data = pbn_default, + }, + { } }; static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, From 88af183a135ccdd004bdeebf4e0c99383b530a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 5 May 2026 10:47:49 +0200 Subject: [PATCH 0794/5214] serial: jsm: Drop unused driver_data assigment and redundant zeros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't use the driver_data field, so drop the assigment together with the then redundant zero assigment for .class and .class_mask. Also drop the zero in the list terminator. While at it also use PCI_VDEVICE() to allow dropping "PCI_VENDOR_ID_". Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260505084749.2304121-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm_driver.c | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 4b73e51f83fb..8cd9b981bf20 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -296,25 +296,25 @@ static void jsm_remove_one(struct pci_dev *pdev) } static const struct pci_device_id jsm_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_4), 0, 0, 6 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422), 0, 0, 7 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_1_422_485), 0, 0, 8 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2_422_485), 0, 0, 9 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8), 0, 0, 10 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4), 0, 0, 11 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4RJ45), 0, 0, 12 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_8RJ45), 0, 0, 13 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_4), 0, 0, 14 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_4_422), 0, 0, 15 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_8), 0, 0, 16 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_CLASSIC_8_422), 0, 0, 17 }, - { 0, } + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_2DB9) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_2DB9PRI) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_2RJ45) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI) }, + { PCI_VDEVICE(DIGI, PCIE_DEVICE_ID_NEO_4_IBM) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_NEO_8) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_4) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_1_422) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_1_422_485) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_NEO_2_422_485) }, + { PCI_VDEVICE(DIGI, PCIE_DEVICE_ID_NEO_8) }, + { PCI_VDEVICE(DIGI, PCIE_DEVICE_ID_NEO_4) }, + { PCI_VDEVICE(DIGI, PCIE_DEVICE_ID_NEO_4RJ45) }, + { PCI_VDEVICE(DIGI, PCIE_DEVICE_ID_NEO_8RJ45) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_CLASSIC_4) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_CLASSIC_4_422) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_CLASSIC_8) }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_CLASSIC_8_422) }, + { } }; MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); From 430a1386df5c98e65f6b943c15366e4ca92e3328 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 6 May 2026 14:46:43 +0200 Subject: [PATCH 0795/5214] serial: sh-sci: Remove plat_sci_port.flags The last setter of p->flags was removed in commit 37744feebc086908 ("sh: remove sh5 support") in v5.8. Link: https://lore.kernel.org/CAMuHMdXs94k3-7YD-yO7p2=+u8waYGAz8mpP5LDbMf3szt4V-w@mail.gmail.com Signed-off-by: Geert Uytterhoeven Reviewed-by: John Ogness Reviewed-by: Lad Prabhakar Link: https://patch.msgid.link/20260506124643.128021-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- include/linux/serial_sci.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6c819b6b2425..a35230d57540 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3369,7 +3369,7 @@ static int sci_init_single(struct platform_device *dev, } port->type = SCI_PUBLIC_PORT_ID(p->type); - port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF | p->flags; + port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF; port->fifosize = sci_port->params->fifosize; if (p->type == PORT_SCI && !dev->dev.of_node) { diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 0f2f50b8d28e..36c795d61f7e 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -51,7 +51,6 @@ struct plat_sci_port_ops { */ struct plat_sci_port { unsigned int type; /* SCI / SCIF / IRDA / HSCIF */ - upf_t flags; /* UPF_* flags */ unsigned int sampling_rate; unsigned int scscr; /* SCSCR initialization */ From 9c7eb1c9c3e3bfecb556fc8fa1b68939385444de Mon Sep 17 00:00:00 2001 From: John Ogness Date: Wed, 6 May 2026 14:21:56 +0206 Subject: [PATCH 0796/5214] serial: core: Add dedicated uart_port field for console flow Currently the UPF_CONS_FLOW bit in the uart_port.flags field is used by serial console drivers to identify if a user has configured flow control on the console. Usually this policy is setup during early boot, but can be changed at runtime. The bits in uart_port.flags are either hardware and driver properties that are initialized before usage or are properties that can be changed via the tty layer. The UPF_CONS_FLOW bit is an exception because it is a console-only policy that can change at runtime and its setting and usage have nothing to do with the tty layer. This actually causes a problem for its usage because uart_port.flags is synchronized by a related tty_port.mutex, but a console has no relation to a tty (other than sharing the port). This is probably why console flow control is not properly available for most serial drivers. And it is hindering being able to provide a proper implementation. Commit d01f4d181c92 ("serial: core: Privatize tty->hw_stopped") addressed a similar issue to deal with software assisted CTS flow state tracking. Add a new uart_port boolean field "cons_flow" to store the user configuration for console flow control. Add getter/setter wrappers to allow for adding more policies later and/or locking constraint validation. Mark UPF_CONS_FLOW as deprecated. Signed-off-by: John Ogness Link: https://patch.msgid.link/20260506121606.5805-2-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 666430b47899..4f7bbdd90017 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -533,6 +533,7 @@ struct uart_port { #define UPF_HARD_FLOW ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS)) /* Port has hardware-assisted s/w flow control */ #define UPF_SOFT_FLOW ((__force upf_t) BIT_ULL(22)) +/* Deprecated: use uart_set_cons_flow_enabled()/uart_cons_flow_enabled() instead. */ #define UPF_CONS_FLOW ((__force upf_t) BIT_ULL(23)) #define UPF_SHARE_IRQ ((__force upf_t) BIT_ULL(24)) #define UPF_EXAR_EFR ((__force upf_t) BIT_ULL(25)) @@ -567,6 +568,7 @@ struct uart_port { #define UPSTAT_SYNC_FIFO ((__force upstat_t) (1 << 5)) bool hw_stopped; /* sw-assisted CTS flow state */ + bool cons_flow; /* user specified console flow control */ unsigned int mctrl; /* current modem ctrl settings */ unsigned int frame_time; /* frame timing in ns */ unsigned int type; /* port type */ @@ -1163,6 +1165,16 @@ static inline bool uart_softcts_mode(struct uart_port *uport) return ((uport->status & mask) == UPSTAT_CTS_ENABLE); } +static inline void uart_set_cons_flow_enabled(struct uart_port *uport, bool enabled) +{ + uport->cons_flow = enabled; +} + +static inline bool uart_cons_flow_enabled(const struct uart_port *uport) +{ + return uport->cons_flow; +} + /* * The following are helper functions for the low level drivers. */ From bf558715d91cfa28f283de7105a879a92da31fb7 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Wed, 6 May 2026 14:21:57 +0206 Subject: [PATCH 0797/5214] serial: Replace driver usage of UPF_CONS_FLOW Rather than using the UPF_CONS_FLOW bit of uart_port.flags to track the user configuration of console flow control, use the newly added uart_port.cons_flow (via its getter/setter functions). A coccinelle script was used to perform the search/replace. Note1: The sh-sci driver is blindly copying platform data configuration flags to uart_port.flags. Thus UPF_CONS_FLOW could get set for uart_port.flags. A follow-up commit will address this. Note2: The samsung_tty driver is using UPF_CONS_FLOW as a platform data configuration flag. However, the driver explicitly checks for this configuration flag and thus setting UPF_CONS_FLOW in uart_port.flags is avoided. Signed-off-by: John Ogness Link: https://patch.msgid.link/20260506121606.5805-3-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 4 ++-- drivers/tty/serial/bcm63xx_uart.c | 2 +- drivers/tty/serial/omap-serial.c | 2 +- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/pxa.c | 2 +- drivers/tty/serial/samsung_tty.c | 8 ++++---- drivers/tty/serial/serial_txx9.c | 4 ++-- drivers/tty/serial/sunsu.c | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 02e6cdefdbac..e4e6a53ebea3 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1990,7 +1990,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) wait_for_lsr(up, bits); /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(&up->port)) { for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; @@ -3353,7 +3353,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, * it regardless of the CTS state. Therefore, only use fifo * if we don't use control flow. */ - !(up->port.flags & UPF_CONS_FLOW); + !uart_cons_flow_enabled(&up->port); if (likely(use_fifo)) serial8250_console_fifo_write(up, s, count); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 51df9d2d8bfc..544695cb184c 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -675,7 +675,7 @@ static void wait_for_xmitr(struct uart_port *port) } /* Wait up to 1s for flow control if necessary */ - if (port->flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(port)) { tmout = 1000000; while (--tmout) { unsigned int val; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 0b85f47ff19e..a689d190940c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1092,7 +1092,7 @@ static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up) } while (!uart_lsr_tx_empty(status)); /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(&up->port)) { for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 6729d8e83c3c..2f72cd62e248 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1444,7 +1444,7 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(&up->port)) { unsigned int tmout; for (tmout = 1000000; tmout; tmout--) { unsigned int msr = ioread8(up->membase + UART_MSR); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index fea0255067cc..10fc8990579b 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -573,7 +573,7 @@ static void wait_for_xmitr(struct uart_pxa_port *up) } while (!uart_lsr_tx_empty(status)); /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(&up->port)) { tmout = 1000000; while (--tmout && ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index e27806bf2cf3..2f94fc798cff 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -319,7 +319,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) ourport->tx_enabled = 0; ourport->tx_in_progress = 0; - if (port->flags & UPF_CONS_FLOW) + if (uart_cons_flow_enabled(port)) s3c24xx_serial_rx_enable(port); ourport->tx_mode = 0; @@ -493,7 +493,7 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) struct tty_port *tport = &port->state->port; if (!ourport->tx_enabled) { - if (port->flags & UPF_CONS_FLOW) + if (uart_cons_flow_enabled(port)) s3c24xx_serial_rx_disable(port); ourport->tx_enabled = 1; @@ -781,7 +781,7 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) uerstat = rd_regl(port, S3C2410_UERSTAT); ch = rd_reg(port, S3C2410_URXH); - if (port->flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(port)) { bool txe = s3c24xx_serial_txempty_nofifo(port); if (ourport->rx_enabled) { @@ -1830,7 +1830,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, if (cfg->uart_flags & UPF_CONS_FLOW) { dev_dbg(port->dev, "enabling flow control\n"); - port->flags |= UPF_CONS_FLOW; + uart_set_cons_flow_enabled(port, true); } /* sort our the physical and virtual addresses for each UART */ diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 436a559234df..4ae9a45c8e3a 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -422,7 +422,7 @@ static void wait_for_xmitr(struct uart_port *up) udelay(1); /* Wait up to 1s for flow control if necessary */ - if (up->flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(up)) { tmout = 1000000; while (--tmout && (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) @@ -857,7 +857,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count) * Disable flow-control if enabled (and unnecessary) */ flcr = sio_in(up, TXX9_SIFLCR); - if (!(up->flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES)) + if (!uart_cons_flow_enabled(up) && (flcr & TXX9_SIFLCR_TES)) sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES); uart_console_write(up, s, count, serial_txx9_console_putchar); diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 6505a1930da9..4ff3e5c41dab 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1245,7 +1245,7 @@ static void wait_for_xmitr(struct uart_sunsu_port *up) } while (!uart_lsr_tx_empty(status)); /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { + if (uart_cons_flow_enabled(&up->port)) { tmout = 1000000; while (--tmout && ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) From 6d7f4890bbb6fc8b70f618aa0407043b32bc738a Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 29 Apr 2026 17:13:25 +0800 Subject: [PATCH 0798/5214] serial: 8250_dwlib: move DesignWare register definitions to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the DW_UART_* register offsets and CPR bit/field definitions from 8250_dwlib.c into 8250_dwlib.h so they can be shared by 8250_dw and 8250_dwlib users. Add an include guard for 8250_dwlib.h. Signed-off-by: Jia Wang Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260429-ultrarisc-serial-v7-1-e475cce9e274@ultrarisc.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 11 ----- drivers/tty/serial/8250/8250_dwlib.c | 49 -------------------- drivers/tty/serial/8250/8250_dwlib.h | 67 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 94beadb4024d..467755bf0092 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -34,22 +34,11 @@ #include "8250_dwlib.h" -/* Offsets for the DesignWare specific registers */ -#define DW_UART_USR 0x1f /* UART Status Register */ -#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ - #define OCTEON_UART_USR 0x27 /* UART Status Register */ #define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */ #define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */ -/* DesignWare specific register fields */ -#define DW_UART_IIR_IID GENMASK(3, 0) - -#define DW_UART_MCR_SIRE BIT(6) - -#define DW_UART_USR_BUSY BIT(0) - /* Renesas specific register fields */ #define RZN1_UART_xDMACR_DMA_EN BIT(0) #define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1) diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index b055d89cfb39..8859e66d2d71 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -13,55 +13,6 @@ #include "8250_dwlib.h" -/* Offsets for the DesignWare specific registers */ -#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */ -#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */ -#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */ -#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ -#define DW_UART_RAR 0xc4 /* Receive Address Register */ -#define DW_UART_TAR 0xc8 /* Transmit Address Register */ -#define DW_UART_LCR_EXT 0xcc /* Line Extended Control Register */ -#define DW_UART_CPR 0xf4 /* Component Parameter Register */ -#define DW_UART_UCV 0xf8 /* UART Component Version */ - -/* Receive / Transmit Address Register bits */ -#define DW_UART_ADDR_MASK GENMASK(7, 0) - -/* Line Status Register bits */ -#define DW_UART_LSR_ADDR_RCVD BIT(8) - -/* Transceiver Control Register bits */ -#define DW_UART_TCR_RS485_EN BIT(0) -#define DW_UART_TCR_RE_POL BIT(1) -#define DW_UART_TCR_DE_POL BIT(2) -#define DW_UART_TCR_XFER_MODE GENMASK(4, 3) -#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0) -#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1) -#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2) - -/* Line Extended Control Register bits */ -#define DW_UART_LCR_EXT_DLS_E BIT(0) -#define DW_UART_LCR_EXT_ADDR_MATCH BIT(1) -#define DW_UART_LCR_EXT_SEND_ADDR BIT(2) -#define DW_UART_LCR_EXT_TRANSMIT_MODE BIT(3) - -/* Component Parameter Register bits */ -#define DW_UART_CPR_ABP_DATA_WIDTH GENMASK(1, 0) -#define DW_UART_CPR_AFCE_MODE BIT(4) -#define DW_UART_CPR_THRE_MODE BIT(5) -#define DW_UART_CPR_SIR_MODE BIT(6) -#define DW_UART_CPR_SIR_LP_MODE BIT(7) -#define DW_UART_CPR_ADDITIONAL_FEATURES BIT(8) -#define DW_UART_CPR_FIFO_ACCESS BIT(9) -#define DW_UART_CPR_FIFO_STAT BIT(10) -#define DW_UART_CPR_SHADOW BIT(11) -#define DW_UART_CPR_ENCODED_PARMS BIT(12) -#define DW_UART_CPR_DMA_EXTRA BIT(13) -#define DW_UART_CPR_FIFO_MODE GENMASK(23, 16) - -/* Helper for FIFO size calculation */ -#define DW_UART_CPR_FIFO_SIZE(a) (FIELD_GET(DW_UART_CPR_FIFO_MODE, (a)) * 16) - /* * divisor = div(I) + div(F) * "I" means integer, "F" means fractional diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index 7dd2a8e7b780..2f26f9ecacbe 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -1,11 +1,76 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* Synopsys DesignWare 8250 library header file. */ +#ifndef _SERIAL_8250_DWLIB_H_ +#define _SERIAL_8250_DWLIB_H_ + +#include +#include #include #include #include "8250.h" +/* Offsets for the DesignWare specific registers */ +#define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ +#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */ +#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */ +#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */ +#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ +#define DW_UART_RAR 0xc4 /* Receive Address Register */ +#define DW_UART_TAR 0xc8 /* Transmit Address Register */ +#define DW_UART_LCR_EXT 0xcc /* Line Extended Control Register */ +#define DW_UART_CPR 0xf4 /* Component Parameter Register */ +#define DW_UART_UCV 0xf8 /* UART Component Version */ + +/* Interrupt ID Register bits */ +#define DW_UART_IIR_IID GENMASK(3, 0) + +/* Modem Control Register bits */ +#define DW_UART_MCR_SIRE BIT(6) + +/* Line Status Register bits */ +#define DW_UART_LSR_ADDR_RCVD BIT(8) + +/* UART Status Register bits */ +#define DW_UART_USR_BUSY BIT(0) + +/* Transceiver Control Register bits */ +#define DW_UART_TCR_RS485_EN BIT(0) +#define DW_UART_TCR_RE_POL BIT(1) +#define DW_UART_TCR_DE_POL BIT(2) +#define DW_UART_TCR_XFER_MODE GENMASK(4, 3) +#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0) +#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1) +#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2) + +/* Receive / Transmit Address Register bits */ +#define DW_UART_ADDR_MASK GENMASK(7, 0) + +/* Line Extended Control Register bits */ +#define DW_UART_LCR_EXT_DLS_E BIT(0) +#define DW_UART_LCR_EXT_ADDR_MATCH BIT(1) +#define DW_UART_LCR_EXT_SEND_ADDR BIT(2) +#define DW_UART_LCR_EXT_TRANSMIT_MODE BIT(3) + +/* Component Parameter Register bits */ +#define DW_UART_CPR_ABP_DATA_WIDTH GENMASK(1, 0) +#define DW_UART_CPR_AFCE_MODE BIT(4) +#define DW_UART_CPR_THRE_MODE BIT(5) +#define DW_UART_CPR_SIR_MODE BIT(6) +#define DW_UART_CPR_SIR_LP_MODE BIT(7) +#define DW_UART_CPR_ADDITIONAL_FEATURES BIT(8) +#define DW_UART_CPR_FIFO_ACCESS BIT(9) +#define DW_UART_CPR_FIFO_STAT BIT(10) +#define DW_UART_CPR_SHADOW BIT(11) +#define DW_UART_CPR_ENCODED_PARMS BIT(12) +#define DW_UART_CPR_DMA_EXTRA BIT(13) +#define DW_UART_CPR_FIFO_MODE GENMASK(23, 16) + +/* Helper for FIFO size calculation */ +#define DW_UART_CPR_FIFO_SIZE(a) (FIELD_GET(DW_UART_CPR_FIFO_MODE, (a)) * 16) + struct dw8250_port_data { /* Port properties */ int line; @@ -38,3 +103,5 @@ static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) else writel(reg, p->membase + offset); } + +#endif /* _SERIAL_8250_DWLIB_H_ */ From 07bb8459c443b840c55293cee1c726431dd749cf Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 29 Apr 2026 17:13:26 +0800 Subject: [PATCH 0799/5214] serial: 8250_dw: build Renesas RZN1 CPR value from DW_UART_CPR_* definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the magic CPR value for Renesas RZ/N1 with a composition using DW_UART_CPR_* bit/field definitions and FIELD_PREP_CONST(). Introduce a helper macro to convert a FIFO size (bytes) into the CPR FIFO_MODE field value, with BUILD_BUG_ON_ZERO() checks for alignment and bounds. Use it to replace the literal FIFO_MODE values in the RZN1. Signed-off-by: Jia Wang Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260429-ultrarisc-serial-v7-2-e475cce9e274@ultrarisc.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 10 +++++++++- drivers/tty/serial/8250/8250_dwlib.h | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 467755bf0092..480f82d89856 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -937,7 +937,15 @@ static const struct dw8250_platform_data dw8250_armada_38x_data = { static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { .usr_reg = DW_UART_USR, - .cpr_value = 0x00012f32, + .cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) | + DW_UART_CPR_AFCE_MODE | + DW_UART_CPR_THRE_MODE | + DW_UART_CPR_ADDITIONAL_FEATURES | + DW_UART_CPR_FIFO_ACCESS | + DW_UART_CPR_FIFO_STAT | + DW_UART_CPR_SHADOW | + DW_UART_CPR_DMA_EXTRA | + DW_UART_CPR_FIFO_MODE_FROM_SIZE(16), .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, }; diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index 2f26f9ecacbe..1fe52332e774 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -68,8 +69,13 @@ #define DW_UART_CPR_DMA_EXTRA BIT(13) #define DW_UART_CPR_FIFO_MODE GENMASK(23, 16) -/* Helper for FIFO size calculation */ +/* Helpers for FIFO size calculation */ #define DW_UART_CPR_FIFO_SIZE(a) (FIELD_GET(DW_UART_CPR_FIFO_MODE, (a)) * 16) +#define DW_UART_CPR_FIFO_MODE_FROM_SIZE(size) \ + (FIELD_PREP_CONST(DW_UART_CPR_FIFO_MODE, \ + BUILD_BUG_ON_ZERO((size) > 2048) + \ + BUILD_BUG_ON_ZERO((size) % 16) + \ + ((size) / 16))) struct dw8250_port_data { /* Port properties */ From d22d2e54f660eeb64e06f2cb6db068dac66a5733 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 29 Apr 2026 17:13:27 +0800 Subject: [PATCH 0800/5214] dt-bindings: serial: snps-dw-apb-uart: Add UltraRISC DP1000 UART UltraRISC DP1000 integrates a Synopsys DesignWare APB UART, but it does not provide the standard CPR and UCV registers. Signed-off-by: Jia Wang Acked-by: Conor Dooley Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260429-ultrarisc-serial-v7-3-e475cce9e274@ultrarisc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 685c1eceb782..49f51b002879 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -78,6 +78,7 @@ properties: - starfive,jh7100-hsuart - starfive,jh7100-uart - starfive,jh7110-uart + - ultrarisc,dp1000-uart - const: snps,dw-apb-uart - const: snps,dw-apb-uart From 4a9a0b1a82a8b23eb68032dd19b120e82cd67004 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 29 Apr 2026 17:13:28 +0800 Subject: [PATCH 0801/5214] serial: 8250_dw: Use a fixed CPR value for UltraRISC DP1000 UART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UltraRISC DP1000 UART does not provide the standard CPR register used by 8250_dw to discover port capabilities. Provide a fixed CPR value for the DP1000-specific compatible so the driver can configure the port correctly. Signed-off-by: Jia Wang Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20260429-ultrarisc-serial-v7-4-e475cce9e274@ultrarisc.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 480f82d89856..55e40c10f46a 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -959,6 +959,15 @@ static const struct dw8250_platform_data dw8250_intc10ee = { .quirks = DW_UART_QUIRK_IER_KICK, }; +static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = { + .usr_reg = DW_UART_USR, + .cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) | + DW_UART_CPR_THRE_MODE | + DW_UART_CPR_DMA_EXTRA | + DW_UART_CPR_FIFO_MODE_FROM_SIZE(32), + .quirks = DW_UART_QUIRK_CPR_VALUE, +}; + static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, @@ -966,6 +975,7 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, { .compatible = "sophgo,sg2044-uart", .data = &dw8250_skip_set_rate_data }, { .compatible = "starfive,jh7100-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "ultrarisc,dp1000-uart", .data = &dw8250_ultrarisc_dp1000_data }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); From ba48a6b91f54ad4ad1b9b8ed5878ee17a18e0f38 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 10 Apr 2026 11:20:09 -0400 Subject: [PATCH 0802/5214] serial: icom: remove check for zero baud rate from uart_get_baud_rate() The minimum baud rate supported by this driver is 300, so even for the B0 case, uart_get_baud_rate() will return 9600, not zero. This check predates commit 16ae2a877bf4 ("serial: Fix crash if the minimum rate of the device is > 9600 baud") and is no longer necessary so remove it. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260410152022.2146488-2-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/icom.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index bcdc07208486..b6399d86a0bc 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1396,8 +1396,6 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old_termios, icom_acfg_baud[0], icom_acfg_baud[BAUD_TABLE_LIMIT]); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ for (index = 0; index < BAUD_TABLE_LIMIT; index++) { if (icom_acfg_baud[index] == baud) { From 888b245964ad3617866d6d4b5bb8a154a410ef32 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 10 Apr 2026 11:20:10 -0400 Subject: [PATCH 0803/5214] serial: apbuart: remove check for zero baud rate from uart_get_baud_rate() The minimum baud rate supported by this driver is 0, so even for the B0 case, uart_get_baud_rate() will return 9600, not zero. This check predates commit 16ae2a877bf4 ("serial: Fix crash if the minimum rate of the device is > 9600 baud") and is no longer necessary so remove it. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260410152022.2146488-3-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/apbuart.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 3e46341cfff8..afb04d727203 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -210,8 +210,6 @@ static void apbuart_set_termios(struct uart_port *port, /* Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - if (baud == 0) - panic("invalid baudrate %i\n", port->uartclk / 16); /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */ quot = (uart_get_divisor(port, baud)) * 2; From 2f0c8853109bca54ac28e33659ce600ceb45ff12 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 10 Apr 2026 11:20:11 -0400 Subject: [PATCH 0804/5214] serial: core: update uart_get_baud_rate() obsolete comments Update obsolete comments to match the actual code. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260410152022.2146488-4-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 89cebdd27841..e6a8ab40442d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -465,7 +465,8 @@ EXPORT_SYMBOL(uart_update_timeout); * baud. * * If the new baud rate is invalid, try the @old termios setting. If it's still - * invalid, we try 9600 baud. If that is also invalid 0 is returned. + * invalid, clip to the nearest chip supported rate. + * If that is also invalid 0 is returned. * * The @termios structure is updated to reflect the baud rate we're actually * going to be using. Don't do this for the case where B0 is requested ("hang @@ -523,7 +524,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, return baud; /* - * Oops, the quotient was zero. Try again with + * If the range cannot be met then try again with * the old baud rate if possible. */ termios->c_cflag &= ~CBAUD; From 1097aedec77674fe4f08f40b3f0bac26ea437fc0 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 10 Apr 2026 11:20:12 -0400 Subject: [PATCH 0805/5214] serial: core: simplify clipping logic in uart_get_baud_rate() Simplify the clipping logic in uart_get_baud_rate() to improve code readability. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260410152022.2146488-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e6a8ab40442d..f89c0dc29516 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -543,11 +543,11 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, */ if (!hung_up) { if (baud <= min) - tty_termios_encode_baud_rate(termios, - min + 1, min + 1); + baud = min + 1; else - tty_termios_encode_baud_rate(termios, - max - 1, max - 1); + baud = max - 1; + + tty_termios_encode_baud_rate(termios, baud, baud); } } return 0; From 091ea8e5d34e1dca5d8f30127173b949e3f803ce Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 10 Apr 2026 11:20:13 -0400 Subject: [PATCH 0806/5214] serial: core: prevent division by zero by always returning non-zero baud rate If a device has a minimum baud rate > 9600 bauds, and a new termios baud rate of 0 (hang up) is requested, uart_get_baud_rate() will return 0. Most drivers do not check this return value and call uart_update_timeout() with this zero baud rate, which will trigger a "Division by zero in kernel" fault: stty -F /dev/ttySC0 0 Division by zero in kernel. ... Fix by returning the larger of 9600 or min for the B0 case. This now ensures that a non-zero baud rate is returned, and greatly simplifies the code. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260410152022.2146488-6-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f89c0dc29516..075a69164aa7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -462,11 +462,10 @@ EXPORT_SYMBOL(uart_update_timeout); * * Decode the termios structure into a numeric baud rate, taking account of the * magic 38400 baud rate (with spd_* flags), and mapping the %B0 rate to 9600 - * baud. + * baud or min argument, whichever is greater. * * If the new baud rate is invalid, try the @old termios setting. If it's still * invalid, clip to the nearest chip supported rate. - * If that is also invalid 0 is returned. * * The @termios structure is updated to reflect the baud rate we're actually * going to be using. Don't do this for the case where B0 is requested ("hang @@ -481,7 +480,6 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, unsigned int try; unsigned int baud; unsigned int altbaud; - int hung_up = 0; upf_t flags = port->flags & UPF_SPD_MASK; switch (flags) { @@ -515,10 +513,8 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, /* * Special case: B0 rate. */ - if (baud == 0) { - hung_up = 1; - baud = 9600; - } + if (baud == 0) + return max(min, 9600); if (baud >= min && baud <= max) return baud; @@ -530,9 +526,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, termios->c_cflag &= ~CBAUD; if (old) { baud = tty_termios_baud_rate(old); - if (!hung_up) - tty_termios_encode_baud_rate(termios, - baud, baud); + tty_termios_encode_baud_rate(termios, baud, baud); old = NULL; continue; } @@ -541,15 +535,16 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, * As a last resort, if the range cannot be met then clip to * the nearest chip supported rate. */ - if (!hung_up) { - if (baud <= min) - baud = min + 1; - else - baud = max - 1; + if (baud <= min) + baud = min + 1; + else + baud = max - 1; - tty_termios_encode_baud_rate(termios, baud, baud); - } + tty_termios_encode_baud_rate(termios, baud, baud); } + + /* Should never happen */ + WARN_ON(1); return 0; } EXPORT_SYMBOL(uart_get_baud_rate); From 0dcaf1c6b20546661ed14d9b1e3e9c252a0df9f7 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:28 -0400 Subject: [PATCH 0807/5214] serial: max310x: uniformize clock/freq types max310x_set_ref_clk() returns a 32-bits clock value, so there is no need to have parameters and intermediate values defined as long. Change clock and freq types to int. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-1-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index ac7d3f197c3a..7c1c3696f568 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -545,7 +545,7 @@ static int max310x_set_baud(struct uart_port *port, int baud) return (16*port->uartclk) / (c*(16*div + frac)); } -static int max310x_update_best_err(unsigned long f, long *besterr) +static int max310x_update_best_err(unsigned int f, long *besterr) { /* Use baudrate 115200 for calculate error */ long err = f % (460800 * 16); @@ -559,11 +559,11 @@ static int max310x_update_best_err(unsigned long f, long *besterr) } static s32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, - unsigned long freq, bool xtal) + unsigned int freq, bool xtal) { unsigned int div, clksrc, pllcfg = 0; long besterr = -1; - unsigned long fdiv, fmul, bestfreq = freq; + unsigned int fdiv, fmul, bestfreq = freq; /* First, update error without PLL */ max310x_update_best_err(freq, &besterr); @@ -1268,7 +1268,8 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty const struct max310x_if_cfg *if_cfg, struct regmap *regmaps[], int irq) { - int i, ret, fmin, fmax, freq; + unsigned int fmin, fmax, freq; + int i, ret; struct max310x_port *s; s32 uartclk = 0; bool xtal; From db3c618405d2a4ac870915b8438ff17be93b5f16 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:29 -0400 Subject: [PATCH 0808/5214] serial: max310x: simplify max310x_update_best_err() besterr was defined as a signed type was to make sure that the first call to max310x_update_best_err() would always set besterr. Also there is no need for it to be a long. By changing its type to unsigned int and initial value to UINT_MAX, max310x_update_best_err() can be simplified and be more efficient while achieving the same initial result. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-2-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 7c1c3696f568..b68a17ebd10b 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -545,12 +545,12 @@ static int max310x_set_baud(struct uart_port *port, int baud) return (16*port->uartclk) / (c*(16*div + frac)); } -static int max310x_update_best_err(unsigned int f, long *besterr) +static int max310x_update_best_err(unsigned int f, unsigned int *besterr) { /* Use baudrate 115200 for calculate error */ - long err = f % (460800 * 16); + unsigned int err = f % (460800 * 16); - if ((*besterr < 0) || (*besterr > err)) { + if (*besterr > err) { *besterr = err; return 0; } @@ -562,7 +562,7 @@ static s32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, unsigned int freq, bool xtal) { unsigned int div, clksrc, pllcfg = 0; - long besterr = -1; + unsigned int besterr = UINT_MAX; unsigned int fdiv, fmul, bestfreq = freq; /* First, update error without PLL */ From df1490942a41f26aaea9ca487299c26286327603 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:30 -0400 Subject: [PATCH 0809/5214] serial: max310x: change return type of max310x_set_ref_clk() Having max310x_set_ref_clk() return an s32 as both an error code and a frequency value is ambiguous. Fix by passing a pointer to the frequency value that will then be updated, and simply return a status. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-3-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index b68a17ebd10b..cf27e597ea41 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -558,8 +558,8 @@ static int max310x_update_best_err(unsigned int f, unsigned int *besterr) return 1; } -static s32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, - unsigned int freq, bool xtal) +static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, + unsigned int freq, unsigned int *fref, bool xtal) { unsigned int div, clksrc, pllcfg = 0; unsigned int besterr = UINT_MAX; @@ -632,7 +632,9 @@ static s32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, "clock is not stable\n"); } - return bestfreq; + *fref = bestfreq; + + return 0; } static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int len) @@ -1271,7 +1273,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty unsigned int fmin, fmax, freq; int i, ret; struct max310x_port *s; - s32 uartclk = 0; + unsigned int uartclk = 0; bool xtal; for (i = 0; i < devtype->nr; i++) @@ -1357,11 +1359,9 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty regmap_write(regmaps[i], MAX310X_MODE1_REG, devtype->mode1); } - uartclk = max310x_set_ref_clk(dev, s, freq, xtal); - if (uartclk < 0) { - ret = uartclk; + ret = max310x_set_ref_clk(dev, s, freq, &uartclk, xtal); + if (ret < 0) goto out_uart; - } dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); From ffc3414d15c615bc0a46536343f917d8a5f14655 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:31 -0400 Subject: [PATCH 0810/5214] serial: max310x: update baudrate comments for err calculation The baudrate used to compute the best error was changed from 115200 to 460800 in commit 35240ba26a93 ("tty: max310x: Fix invalid baudrate divisors calculator"), but the comment was not updated, so fix it. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-4-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index cf27e597ea41..f689958736f7 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -547,7 +547,7 @@ static int max310x_set_baud(struct uart_port *port, int baud) static int max310x_update_best_err(unsigned int f, unsigned int *besterr) { - /* Use baudrate 115200 for calculate error */ + /* Use high-enough baudrate to calculate error */ unsigned int err = f % (460800 * 16); if (*besterr > err) { From d0001adcd1f838d97bc23a20375f60bb3304aa5c Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:32 -0400 Subject: [PATCH 0811/5214] serial: max310x: improve max310x_set_ref_clk() efficiency The same basic code is duplicated for each of the four PLL multiplier values, so simplify and streamline it. Doing so allows us to avoid some unnecessary (fdiv * factor) multiplications for some fdiv values and replace the second "if" statement with an "else if" and avoid a useless comparison. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-5-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 91 ++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index f689958736f7..1cd7632831a2 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -258,6 +258,17 @@ #define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */ #define MAX14830_REV_ID (0xb0) +struct max310x_clk_config_t { + u8 prediv; /* Predivider */ + u8 pll_mult; /* PLL multiplier */ + unsigned int fref; /* + * Reference clock for fractional baud rate generator: + * PLL enabled: (freq / prediv) x pll_mult + * PLL disabled: freq + */ + unsigned int err; /* Computed error for selected parameters */ +}; + struct max310x_if_cfg { int (*extended_reg_enable)(struct device *dev, bool enable); u8 rev_id_offset; @@ -545,70 +556,72 @@ static int max310x_set_baud(struct uart_port *port, int baud) return (16*port->uartclk) / (c*(16*div + frac)); } -static int max310x_update_best_err(unsigned int f, unsigned int *besterr) +static void max310x_try_cfg(unsigned int fdiv, u8 div, u8 pll_mult, + struct max310x_clk_config_t *cfg) { + unsigned int fmul = fdiv * pll_mult; + unsigned int err; + /* Use high-enough baudrate to calculate error */ - unsigned int err = f % (460800 * 16); + err = fmul % (460800 * 16); - if (*besterr > err) { - *besterr = err; - return 0; + if (cfg->err > err) { + cfg->err = err; + cfg->pll_mult = pll_mult; + cfg->prediv = div; + cfg->fref = fmul; } +} - return 1; +static u8 max310x_pll_mult_to_id(u8 pll_mult) +{ + switch (pll_mult) { + case 144: return 3; + case 96: return 2; + case 48: return 1; + case 6: + default: return 0; + } } static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, unsigned int freq, unsigned int *fref, bool xtal) { - unsigned int div, clksrc, pllcfg = 0; - unsigned int besterr = UINT_MAX; - unsigned int fdiv, fmul, bestfreq = freq; + unsigned int div, fdiv, clksrc; + struct max310x_clk_config_t cfg; + + cfg.err = UINT_MAX; + cfg.prediv = 0; + cfg.fref = freq; /* First, update error without PLL */ - max310x_update_best_err(freq, &besterr); + max310x_try_cfg(freq, 1, 1, &cfg); /* Try all possible PLL dividers */ - for (div = 1; (div <= 63) && besterr; div++) { + for (div = 1; (div <= 63) && cfg.err; div++) { fdiv = DIV_ROUND_CLOSEST(freq, div); - /* Try multiplier 6 */ - fmul = fdiv * 6; if ((fdiv >= 500000) && (fdiv <= 800000)) - if (!max310x_update_best_err(fmul, &besterr)) { - pllcfg = (0 << 6) | div; - bestfreq = fmul; - } - /* Try multiplier 48 */ - fmul = fdiv * 48; - if ((fdiv >= 850000) && (fdiv <= 1200000)) - if (!max310x_update_best_err(fmul, &besterr)) { - pllcfg = (1 << 6) | div; - bestfreq = fmul; - } - /* Try multiplier 96 */ - fmul = fdiv * 96; + max310x_try_cfg(fdiv, div, 6, &cfg); /* PLL x6 */ + else if ((fdiv >= 850000) && (fdiv <= 1200000)) + max310x_try_cfg(fdiv, div, 48, &cfg); /* PLL x48 */ + if ((fdiv >= 425000) && (fdiv <= 1000000)) - if (!max310x_update_best_err(fmul, &besterr)) { - pllcfg = (2 << 6) | div; - bestfreq = fmul; - } - /* Try multiplier 144 */ - fmul = fdiv * 144; + max310x_try_cfg(fdiv, div, 96, &cfg); /* PLL x96 */ + if ((fdiv >= 390000) && (fdiv <= 667000)) - if (!max310x_update_best_err(fmul, &besterr)) { - pllcfg = (3 << 6) | div; - bestfreq = fmul; - } + max310x_try_cfg(fdiv, div, 144, &cfg); /* PLL x144 */ } /* Configure clock source */ clksrc = MAX310X_CLKSRC_EXTCLK_BIT | (xtal ? MAX310X_CLKSRC_CRYST_BIT : 0); /* Configure PLL */ - if (pllcfg) { + if (cfg.prediv) { + u8 pll_id = max310x_pll_mult_to_id(cfg.pll_mult); + clksrc |= MAX310X_CLKSRC_PLL_BIT; - regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg); + regmap_write(s->regmap, MAX310X_PLLCFG_REG, (pll_id << 6) | cfg.prediv); } else clksrc |= MAX310X_CLKSRC_PLLBYP_BIT; @@ -632,7 +645,7 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, "clock is not stable\n"); } - *fref = bestfreq; + *fref = cfg.fref; return 0; } From 9769046a06cc75e2af1d54c50238cccf95759cda Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:33 -0400 Subject: [PATCH 0812/5214] serial: max310x: use regmap_read_poll_timeout() for busy wait Simplify busy wait stages by using regmap_read_poll_timeout(). Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-6-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 44 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 1cd7632831a2..7ab5e4bd87ee 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -241,12 +241,12 @@ #define MAX310X_WRITE_BIT 0x80 /* Port startup definitions */ -#define MAX310X_PORT_STARTUP_WAIT_RETRIES 20 /* Number of retries */ -#define MAX310X_PORT_STARTUP_WAIT_DELAY_MS 10 /* Delay between retries */ +#define MAX310X_PORT_STARTUP_SLEEP_US 10000 /* Delay between retries */ +#define MAX310X_PORT_STARTUP_TIMEOUT_US (20 * MAX310X_PORT_STARTUP_SLEEP_US) /* Total timeout */ /* Crystal-related definitions */ -#define MAX310X_XTAL_WAIT_RETRIES 20 /* Number of retries */ -#define MAX310X_XTAL_WAIT_DELAY_MS 10 /* Delay between retries */ +#define MAX310X_XTAL_SLEEP_US 10000 /* Delay between retries */ +#define MAX310X_XTAL_TIMEOUT_US (20 * MAX310X_XTAL_SLEEP_US) /* Total timeout */ /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) @@ -587,7 +587,7 @@ static u8 max310x_pll_mult_to_id(u8 pll_mult) static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, unsigned int freq, unsigned int *fref, bool xtal) { - unsigned int div, fdiv, clksrc; + unsigned int div, fdiv, clksrc, val; struct max310x_clk_config_t cfg; cfg.err = UINT_MAX; @@ -629,18 +629,13 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, /* Wait for crystal */ if (xtal) { - bool stable = false; - unsigned int try = 0, val = 0; + int ret; - do { - msleep(MAX310X_XTAL_WAIT_DELAY_MS); - regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val); - - if (val & MAX310X_STS_CLKREADY_BIT) - stable = true; - } while (!stable && (++try < MAX310X_XTAL_WAIT_RETRIES)); - - if (!stable) + ret = regmap_read_poll_timeout(s->regmap, MAX310X_STS_IRQSTS_REG, + val, val & MAX310X_STS_CLKREADY_BIT, + MAX310X_XTAL_SLEEP_US, + MAX310X_XTAL_TIMEOUT_US); + if (ret) return dev_err_probe(dev, -EAGAIN, "clock is not stable\n"); } @@ -1346,8 +1341,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_clk; for (i = 0; i < devtype->nr; i++) { - bool started = false; - unsigned int try = 0, val = 0; + unsigned int val; /* Reset port */ regmap_write(regmaps[i], MAX310X_MODE2_REG, @@ -1356,15 +1350,11 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty regmap_write(regmaps[i], MAX310X_MODE2_REG, 0); /* Wait for port startup */ - do { - msleep(MAX310X_PORT_STARTUP_WAIT_DELAY_MS); - regmap_read(regmaps[i], MAX310X_BRGDIVLSB_REG, &val); - - if (val == 0x01) - started = true; - } while (!started && (++try < MAX310X_PORT_STARTUP_WAIT_RETRIES)); - - if (!started) { + ret = regmap_read_poll_timeout(regmaps[i], MAX310X_BRGDIVLSB_REG, + val, val == 0x01, + MAX310X_PORT_STARTUP_SLEEP_US, + MAX310X_PORT_STARTUP_TIMEOUT_US); + if (ret) { ret = dev_err_probe(dev, -EAGAIN, "port reset failed\n"); goto out_uart; } From 877c2556adb7758ada3af8a1e87a217ed21c6ef4 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:34 -0400 Subject: [PATCH 0813/5214] serial: max310x: use FIELD_PREP macro to set PLL bitfields Use FIELD_PREP macros to improve code readability. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-7-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 7ab5e4bd87ee..b4449b68cfee 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -9,6 +9,7 @@ * Based on max3107.c, by Aavamobile */ +#include #include #include #include @@ -621,7 +622,9 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, u8 pll_id = max310x_pll_mult_to_id(cfg.pll_mult); clksrc |= MAX310X_CLKSRC_PLL_BIT; - regmap_write(s->regmap, MAX310X_PLLCFG_REG, (pll_id << 6) | cfg.prediv); + val = FIELD_PREP(MAX310X_PLLCFG_PLLFACTOR_MASK, pll_id) | + FIELD_PREP(MAX310X_PLLCFG_PREDIV_MASK, cfg.prediv); + regmap_write(s->regmap, MAX310X_PLLCFG_REG, val); } else clksrc |= MAX310X_CLKSRC_PLLBYP_BIT; From 20ffe4b3330a8bde9e933e9ba2323d5e9386caa5 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:35 -0400 Subject: [PATCH 0814/5214] serial: max310x: allow driver to be built with SPI or I2C If SPI is disabled, the max310x driver cannot be selected. Allow driver to be selected if either I2C or SPI is set. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-8-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 3e519d001e02..f834e5d292fd 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -321,7 +321,7 @@ config SERIAL_MAX3100 config SERIAL_MAX310X tristate "MAX310X support" - depends on SPI_MASTER + depends on SPI_MASTER || I2C select SERIAL_CORE select REGMAP_SPI if SPI_MASTER select REGMAP_I2C if I2C From 922cc077b248a786a93339e7b76bc21ed8575ac7 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:36 -0400 Subject: [PATCH 0815/5214] serial: max310x: move variables to while() scope txlen, to_send and tail variables are only used within the while() loop. Move them to that scope to keep them closer to their usage and improve readability. Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-9-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index b4449b68cfee..748306d1a932 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -761,8 +761,6 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) static void max310x_handle_tx(struct uart_port *port) { struct tty_port *tport = &port->state->port; - unsigned int txlen, to_send; - unsigned char *tail; if (unlikely(port->x_char)) { max310x_port_write(port, MAX310X_THR_REG, port->x_char); @@ -779,6 +777,9 @@ static void max310x_handle_tx(struct uart_port *port) * We could do that in one SPI transaction, but meh. */ while (!kfifo_is_empty(&tport->xmit_fifo)) { + unsigned int txlen, to_send; + unsigned char *tail; + /* Limit to space available in TX FIFO */ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); txlen = port->fifosize - txlen; From ebd57bda9df6234d93e05dfe2fd250c6535ca3bb Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 10:53:37 -0400 Subject: [PATCH 0816/5214] serial: max310x: add comments for PLL limits Add comments to help clarify the provenance of the various hardcoded values used in computing the ref clk. Assisted-by: Gemini:Pro Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260417-max310x-2-v1-10-b424e105ecac@dimonoff.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 748306d1a932..9f423b3b4201 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -585,6 +585,23 @@ static u8 max310x_pll_mult_to_id(u8 pll_mult) } } +/* + * From table 7 in datasheet: PLLFactor Selector Guide + * + * +-----------+----------------+-------------------+-------------------+ + * | PLLFactor | MULTIPLICATION | fPLLIN | fREF | + * | (1 & 0) | FACTOR +---------+---------+---------+---------+ + * | | | MIN | MAX | MIN | MAX | + * +-----------+----------------+---------+---------+---------+---------+ + * | 0 | 6 | 500kHz | 800kHz | 3MHz | 4.8MHz | + * +-----------+----------------+---------+---------+---------+---------+ + * | 1 | 48 | 850kHz | 1.2MHz | 40.8MHz | 56MHz | + * +-----------+----------------+---------+---------+---------+---------+ + * | 2 | 96 | 425kHz | 1MHz | 40.8MHz | 96MHz | + * +-----------+----------------+---------+---------+---------+---------+ + * | 3 | 144 | 390kHz | 667kHz | 56MHz | 96MHz | + * +-----------+----------------+---------+---------+---------+---------+ + */ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, unsigned int freq, unsigned int *fref, bool xtal) { From 16e95bfb79b5d9d01dc7651d98caf3c2ace331cd Mon Sep 17 00:00:00 2001 From: Aniket Randive Date: Mon, 4 May 2026 15:40:45 +0530 Subject: [PATCH 0817/5214] serial: qcom-geni: Avoid probing debug console UART without console support When CONFIG_SERIAL_QCOM_GENI_CONSOLE is disabled, the driver still advertises the debug UART compatible strings ("qcom,geni-debug-uart" and "qcom,sa8255p-geni-debug-uart") in its of_match table. This lets the driver match and probe console UART DT nodes even though console support is not built. As a result, the console port is never registered with the UART core and uart_add_one_port() fails with -EINVAL. Fix this by only including the debug UART compatible entries in the match table when CONFIG_SERIAL_QCOM_GENI_CONSOLE is enabled, preventing the driver from probing console UART nodes when console support is absent. Reviewed-by: Praveen Talari Signed-off-by: Aniket Randive Link: https://patch.msgid.link/20260504101045.1084672-1-aniket.randive@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index b365dd5da3cb..58d142afa160 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1992,6 +1992,7 @@ static int qcom_geni_serial_resume(struct device *dev) return ret; } +#if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE) static const struct qcom_geni_device_data qcom_geni_console_data = { .console = true, .mode = GENI_SE_FIFO, @@ -2000,14 +2001,6 @@ static const struct qcom_geni_device_data qcom_geni_console_data = { .power_state = geni_serial_resource_state, }; -static const struct qcom_geni_device_data qcom_geni_uart_data = { - .console = false, - .mode = GENI_SE_DMA, - .resources_init = geni_serial_resource_init, - .set_rate = geni_serial_set_rate, - .power_state = geni_serial_resource_state, -}; - static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = { .console = true, .mode = GENI_SE_FIFO, @@ -2019,6 +2012,15 @@ static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = { .resources_init = geni_serial_pwr_init, .set_rate = geni_serial_set_level, }; +#endif + +static const struct qcom_geni_device_data qcom_geni_uart_data = { + .console = false, + .mode = GENI_SE_DMA, + .resources_init = geni_serial_resource_init, + .set_rate = geni_serial_set_rate, + .power_state = geni_serial_resource_state, +}; static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = { .console = false, @@ -2039,6 +2041,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { }; static const struct of_device_id qcom_geni_serial_match_table[] = { +#if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE) { .compatible = "qcom,geni-debug-uart", .data = &qcom_geni_console_data, @@ -2047,6 +2050,7 @@ static const struct of_device_id qcom_geni_serial_match_table[] = { .compatible = "qcom,sa8255p-geni-debug-uart", .data = &sa8255p_qcom_geni_console_data, }, +#endif { .compatible = "qcom,geni-uart", .data = &qcom_geni_uart_data, From b6615f762cfa4b68638886d3390369a8545336bc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 10:25:35 +0200 Subject: [PATCH 0818/5214] fpga: lattice-sysconfig-spi: Fix the terminator entries in ID tables The whole purpose of the terminator entry is to be the last one. The trailing comma makes this statement prone to failure. On top of that the style is used for the entries is unusual. Standardize this all by moving terminator entries to their own lines and drop trailing commas. Signed-off-by: Andy Shevchenko Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260508082716.1156192-2-andriy.shevchenko@linux.intel.com Signed-off-by: Xu Yilun --- drivers/fpga/lattice-sysconfig-spi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/fpga/lattice-sysconfig-spi.c b/drivers/fpga/lattice-sysconfig-spi.c index 44691cfcf50a..9f9f76af49fb 100644 --- a/drivers/fpga/lattice-sysconfig-spi.c +++ b/drivers/fpga/lattice-sysconfig-spi.c @@ -125,7 +125,8 @@ static const struct spi_device_id sysconfig_spi_ids[] = { { .name = "sysconfig-ecp5", .driver_data = (kernel_ulong_t)&ecp5_spi_max_speed_hz, - }, {}, + }, + {} }; MODULE_DEVICE_TABLE(spi, sysconfig_spi_ids); @@ -134,7 +135,8 @@ static const struct of_device_id sysconfig_of_ids[] = { { .compatible = "lattice,sysconfig-ecp5", .data = &ecp5_spi_max_speed_hz, - }, {}, + }, + {} }; MODULE_DEVICE_TABLE(of, sysconfig_of_ids); #endif /* IS_ENABLED(CONFIG_OF) */ From f396fe4ef1606f586cd284bf876782a064cb269d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 10:25:36 +0200 Subject: [PATCH 0819/5214] fpga: lattice-sysconfig-spi: Drop of_match_ptr() protection Limiting the scope of devicetree support to CONFIG_OF prevents use of this driver with ACPI via PRP0001. Drop the dependency. Signed-off-by: Andy Shevchenko Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260508082716.1156192-3-andriy.shevchenko@linux.intel.com Signed-off-by: Xu Yilun --- drivers/fpga/lattice-sysconfig-spi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/fpga/lattice-sysconfig-spi.c b/drivers/fpga/lattice-sysconfig-spi.c index 9f9f76af49fb..351e74c9dfe8 100644 --- a/drivers/fpga/lattice-sysconfig-spi.c +++ b/drivers/fpga/lattice-sysconfig-spi.c @@ -130,7 +130,6 @@ static const struct spi_device_id sysconfig_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, sysconfig_spi_ids); -#if IS_ENABLED(CONFIG_OF) static const struct of_device_id sysconfig_of_ids[] = { { .compatible = "lattice,sysconfig-ecp5", @@ -139,14 +138,13 @@ static const struct of_device_id sysconfig_of_ids[] = { {} }; MODULE_DEVICE_TABLE(of, sysconfig_of_ids); -#endif /* IS_ENABLED(CONFIG_OF) */ static struct spi_driver lattice_sysconfig_driver = { .probe = sysconfig_spi_probe, .id_table = sysconfig_spi_ids, .driver = { .name = "lattice_sysconfig_spi_fpga_mgr", - .of_match_table = of_match_ptr(sysconfig_of_ids), + .of_match_table = sysconfig_of_ids, }, }; module_spi_driver(lattice_sysconfig_driver); From e7bfabd68054d343230e39e904c244b9d8e3fb53 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 10:25:37 +0200 Subject: [PATCH 0820/5214] fpga: lattice-sysconfig-spi: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Signed-off-by: Andy Shevchenko Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260508082716.1156192-4-andriy.shevchenko@linux.intel.com Signed-off-by: Xu Yilun --- drivers/fpga/lattice-sysconfig-spi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/fpga/lattice-sysconfig-spi.c b/drivers/fpga/lattice-sysconfig-spi.c index 351e74c9dfe8..3cabf5c16070 100644 --- a/drivers/fpga/lattice-sysconfig-spi.c +++ b/drivers/fpga/lattice-sysconfig-spi.c @@ -3,8 +3,14 @@ * Lattice FPGA programming over slave SPI sysCONFIG interface. */ -#include +#include +#include +#include +#include +#include +#include #include +#include #include "lattice-sysconfig.h" From 24da2324f8d2679a1a6b2ad72910e6a43bf344fa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 10 May 2026 10:51:58 +0200 Subject: [PATCH 0821/5214] fpga: lattice-sysconfig-spi: simplify with spi_get_device_match_data() Use spi_get_device_match_data() helper to simplify a bit the driver. Signed-off-by: Andy Shevchenko Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260510090556.1582900-1-andriy.shevchenko@linux.intel.com Signed-off-by: Xu Yilun --- drivers/fpga/lattice-sysconfig-spi.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/fpga/lattice-sysconfig-spi.c b/drivers/fpga/lattice-sysconfig-spi.c index 3cabf5c16070..5d195602b261 100644 --- a/drivers/fpga/lattice-sysconfig-spi.c +++ b/drivers/fpga/lattice-sysconfig-spi.c @@ -91,7 +91,6 @@ static int sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv *priv) static int sysconfig_spi_probe(struct spi_device *spi) { - const struct spi_device_id *dev_id; struct device *dev = &spi->dev; struct sysconfig_priv *priv; const u32 *spi_max_speed; @@ -100,15 +99,7 @@ static int sysconfig_spi_probe(struct spi_device *spi) if (!priv) return -ENOMEM; - spi_max_speed = device_get_match_data(dev); - if (!spi_max_speed) { - dev_id = spi_get_device_id(spi); - if (!dev_id) - return -ENODEV; - - spi_max_speed = (const u32 *)dev_id->driver_data; - } - + spi_max_speed = spi_get_device_match_data(spi); if (!spi_max_speed) return -EINVAL; From b2fc2c6ebbd2d49935c8960755d8170faead2159 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:40:31 +0200 Subject: [PATCH 0822/5214] platform/x86: xo15-ebook: Fix wakeup source and GPE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device_set_wakeup_enable() call in ebook_switch_add() doesn't actually do anything because power.can_wakeup is not set for ACPI device objects. Moreover, had it done anything, it would have registered a wakeup source object that wouldn't have been used going forward and that wakeup source would have been leaked after driver removal because ebook_switch_remove() doesn't clean it up. Accordingly, remove that call from ebook_switch_add(). Also prevent leaking an enabled ACPI GPE after removing the driver by adding appropriate cleanup code to ebook_switch_remove(). Fixes: 89ca11771a4b ("OLPC XO-1.5 ebook switch driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1966125.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 4d1b1b310cc5..1568169b7872 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -38,6 +38,7 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids); struct ebook_switch { struct input_dev *input; char phys[32]; /* for input device */ + bool gpe_enabled; }; static int ebook_send_state(struct acpi_device *device) @@ -128,7 +129,7 @@ static int ebook_switch_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device_set_wakeup_enable(&device->dev, true); + button->gpe_enabled = true; } return 0; @@ -144,6 +145,10 @@ static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); + if (button->gpe_enabled) + acpi_disable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + input_unregister_device(button->input); kfree(button); } From 826264e0b02dc856979bdd230d96969e93fe41ed Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:41:47 +0200 Subject: [PATCH 0823/5214] platform/x86: xo15-ebook: Fix formatting of labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix formatting of two labels in ebook_switch_add() to make that function follow the current kernel coding style more closely. No functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/14016199.uLZWGnKmhe@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 1568169b7872..616f4bb3461a 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -134,9 +134,9 @@ static int ebook_switch_add(struct acpi_device *device) return 0; - err_free_input: +err_free_input: input_free_device(input); - err_free_button: +err_free_button: kfree(button); return error; } From 60e68011d8ecdb3071cc7713e05750e6b08aec12 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:43:13 +0200 Subject: [PATCH 0824/5214] platform/x86: xo15-ebook: Register ACPI notify handler directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to a platform one, make it install an ACPI notify handler directly instead of using a .notify() callback in struct acpi_driver. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3420768.aeNJFYEL58@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 616f4bb3461a..8af1b9078db8 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -57,16 +57,15 @@ static int ebook_send_state(struct acpi_device *device) return 0; } -static void ebook_switch_notify(struct acpi_device *device, u32 event) +static void ebook_switch_notify(acpi_handle handle, u32 event, void *data) { switch (event) { case ACPI_FIXED_HARDWARE_EVENT: case XO15_EBOOK_NOTIFY_STATUS: - ebook_send_state(device); + ebook_send_state(data); break; default: - acpi_handle_debug(device->handle, - "Unsupported event [0x%x]\n", event); + acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event); break; } } @@ -123,6 +122,11 @@ static int ebook_switch_add(struct acpi_device *device) if (error) goto err_free_input; + error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + ebook_switch_notify, device); + if (error) + goto err_unregister_input; + ebook_send_state(device); if (device->wakeup.flags.valid) { @@ -139,6 +143,10 @@ static int ebook_switch_add(struct acpi_device *device) err_free_button: kfree(button); return error; + +err_unregister_input: + input_unregister_device(input); + goto err_free_button; } static void ebook_switch_remove(struct acpi_device *device) @@ -149,6 +157,8 @@ static void ebook_switch_remove(struct acpi_device *device) acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + ebook_switch_notify); input_unregister_device(button->input); kfree(button); } @@ -160,7 +170,6 @@ static struct acpi_driver xo15_ebook_driver = { .ops = { .add = ebook_switch_add, .remove = ebook_switch_remove, - .notify = ebook_switch_notify, }, .drv.pm = &ebook_switch_pm, }; From b82c2e30cf8edca7052d020f278b461a0ef657c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:44:58 +0200 Subject: [PATCH 0825/5214] platform/x86: xo15-ebook: Convert ACPI driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the OLPC XO-1.5 ebook switch driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8688586.T7Z3S40VBb@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 8af1b9078db8..2ca15f04b2d9 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -15,6 +15,7 @@ #include #include #include +#include #define MODULE_NAME "xo15-ebook" @@ -41,13 +42,13 @@ struct ebook_switch { bool gpe_enabled; }; -static int ebook_send_state(struct acpi_device *device) +static int ebook_send_state(struct device *dev) { - struct ebook_switch *button = acpi_driver_data(device); + struct ebook_switch *button = dev_get_drvdata(dev); unsigned long long state; acpi_status status; - status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); + status = acpi_evaluate_integer(ACPI_HANDLE(dev), "EBK", NULL, &state); if (ACPI_FAILURE(status)) return -EIO; @@ -73,14 +74,15 @@ static void ebook_switch_notify(acpi_handle handle, u32 event, void *data) #ifdef CONFIG_PM_SLEEP static int ebook_switch_resume(struct device *dev) { - return ebook_send_state(to_acpi_device(dev)); + return ebook_send_state(dev); } #endif static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); -static int ebook_switch_add(struct acpi_device *device) +static int ebook_switch_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); const struct acpi_device_id *id; struct ebook_switch *button; struct input_dev *input; @@ -90,7 +92,7 @@ static int ebook_switch_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); button->input = input = input_allocate_device(); if (!input) { @@ -100,7 +102,7 @@ static int ebook_switch_add(struct acpi_device *device) id = acpi_match_acpi_device(ebook_device_ids, device); if (!id) { - dev_err(&device->dev, "Unsupported hid\n"); + dev_err(&pdev->dev, "Unsupported hid\n"); error = -ENODEV; goto err_free_input; } @@ -113,7 +115,7 @@ static int ebook_switch_add(struct acpi_device *device) input->name = acpi_device_name(device); input->phys = button->phys; input->id.bustype = BUS_HOST; - input->dev.parent = &device->dev; + input->dev.parent = &pdev->dev; input->evbit[0] = BIT_MASK(EV_SW); set_bit(SW_TABLET_MODE, input->swbit); @@ -123,11 +125,11 @@ static int ebook_switch_add(struct acpi_device *device) goto err_free_input; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - ebook_switch_notify, device); + ebook_switch_notify, &pdev->dev); if (error) goto err_unregister_input; - ebook_send_state(device); + ebook_send_state(&pdev->dev); if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ @@ -149,9 +151,10 @@ static int ebook_switch_add(struct acpi_device *device) goto err_free_button; } -static void ebook_switch_remove(struct acpi_device *device) +static void ebook_switch_remove(struct platform_device *pdev) { - struct ebook_switch *button = acpi_driver_data(device); + struct ebook_switch *button = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); if (button->gpe_enabled) acpi_disable_gpe(device->wakeup.gpe_device, @@ -163,14 +166,13 @@ static void ebook_switch_remove(struct acpi_device *device) kfree(button); } -static struct acpi_driver xo15_ebook_driver = { - .name = MODULE_NAME, - .class = XO15_EBOOK_CLASS, - .ids = ebook_device_ids, - .ops = { - .add = ebook_switch_add, - .remove = ebook_switch_remove, +static struct platform_driver xo15_ebook_driver = { + .probe = ebook_switch_probe, + .remove = ebook_switch_remove, + .driver = { + .name = MODULE_NAME, + .acpi_match_table = ebook_device_ids, + .pm = &ebook_switch_pm, }, - .drv.pm = &ebook_switch_pm, }; -module_acpi_driver(xo15_ebook_driver); +module_platform_driver(xo15_ebook_driver); From 18bc6ce6bb618e1dff4473d7dd528d22519abbd7 Mon Sep 17 00:00:00 2001 From: Brodie Abrew Date: Wed, 6 May 2026 17:49:16 -0700 Subject: [PATCH 0826/5214] platform/x86: sel3350-platform: Retain LED state on load and unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the platform driver is loaded or unloaded, it overwrites the existing LED states. This can cause a loss of early boot state when the driver loads, and it can cause the ALARM contact to change state or flicker. Explicitly retain the existing LED state to prevent overwriting on driver load and unload. Tested-By: Robert Joslyn Reviewed-by: Robert Joslyn Signed-off-by: Brodie Abrew Link: https://patch.msgid.link/20260507004916.6710-1-brodie_abrew@selinc.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sel3350-platform.c | 136 ++++++++++++++++++------ 1 file changed, 103 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/sel3350-platform.c b/drivers/platform/x86/sel3350-platform.c index 02e2081e2333..f3a314235632 100644 --- a/drivers/platform/x86/sel3350-platform.c +++ b/drivers/platform/x86/sel3350-platform.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include #include @@ -30,19 +32,82 @@ #define SEL_PS_B_DETECT "sel_ps_b_detect" #define SEL_PS_B_GOOD "sel_ps_b_good" +#define AUX_LED_GRN1 "sel_aux_led_grn1" +#define AUX_LED_GRN2 "sel_aux_led_grn2" +#define AUX_LED_GRN3 "sel_aux_led_grn3" +#define AUX_LED_GRN4 "sel_aux_led_grn4" +#define ALARM_STATE_USER "sel_alarm_state_user" +#define ENABLE_STATE_USER "sel_enable_state_user" +#define AUX_LED_RED1 "sel_aux_led_red1" +#define AUX_LED_RED2 "sel_aux_led_red2" +#define AUX_LED_RED3 "sel_aux_led_red3" +#define AUX_LED_RED4 "sel_aux_led_red4" + +static const char *const sel3350_leds_gpio_names[] = { + AUX_LED_GRN1, + AUX_LED_GRN2, + AUX_LED_GRN3, + AUX_LED_GRN4, + ALARM_STATE_USER, + ENABLE_STATE_USER, + AUX_LED_RED1, + AUX_LED_RED2, + AUX_LED_RED3, + AUX_LED_RED4, +}; + /* LEDs */ -static const struct gpio_led sel3350_leds[] = { - { .name = "sel:green:aux1" }, - { .name = "sel:green:aux2" }, - { .name = "sel:green:aux3" }, - { .name = "sel:green:aux4" }, - { .name = "sel:red:alarm" }, +static struct gpio_led sel3350_leds[] = { + { .name = "sel:green:aux1", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux2", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux3", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux4", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:alarm", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, { .name = "sel:green:enabled", - .default_state = LEDS_GPIO_DEFSTATE_ON }, - { .name = "sel:red:aux1" }, - { .name = "sel:red:aux2" }, - { .name = "sel:red:aux3" }, - { .name = "sel:red:aux4" }, + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux1", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux2", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux3", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux4", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, }; static const struct gpio_led_platform_data sel3350_leds_pdata = { @@ -50,25 +115,6 @@ static const struct gpio_led_platform_data sel3350_leds_pdata = { .leds = sel3350_leds, }; -/* Map GPIOs to LEDs */ -static struct gpiod_lookup_table sel3350_leds_table = { - .dev_id = "leds-gpio", - .table = { - GPIO_LOOKUP_IDX(BXT_NW, 49, NULL, 0, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 50, NULL, 1, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 51, NULL, 2, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 52, NULL, 3, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_W, 20, NULL, 4, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_W, 21, NULL, 5, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 37, NULL, 6, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 38, NULL, 7, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 39, NULL, 8, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 40, NULL, 9, GPIO_ACTIVE_HIGH), - {}, - } -}; - -/* Map GPIOs to power supplies */ static struct gpiod_lookup_table sel3350_gpios_table = { .dev_id = B2093_GPIO_ACPI_ID ":00", .table = { @@ -76,6 +122,16 @@ static struct gpiod_lookup_table sel3350_gpios_table = { GPIO_LOOKUP(BXT_NW, 45, SEL_PS_A_GOOD, GPIO_ACTIVE_LOW), GPIO_LOOKUP(BXT_NW, 46, SEL_PS_B_DETECT, GPIO_ACTIVE_LOW), GPIO_LOOKUP(BXT_NW, 47, SEL_PS_B_GOOD, GPIO_ACTIVE_LOW), + GPIO_LOOKUP(BXT_NW, 49, AUX_LED_GRN1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 50, AUX_LED_GRN2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 51, AUX_LED_GRN3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 52, AUX_LED_GRN4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_W, 20, ALARM_STATE_USER, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_W, 21, ENABLE_STATE_USER, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 37, AUX_LED_RED1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 38, AUX_LED_RED2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 39, AUX_LED_RED3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 40, AUX_LED_RED4, GPIO_ACTIVE_HIGH), {}, } }; @@ -149,6 +205,7 @@ struct sel3350_data { static int sel3350_probe(struct platform_device *pdev) { int rs; + int i; struct sel3350_data *sel3350; struct power_supply_config ps_cfg = {}; @@ -158,9 +215,19 @@ static int sel3350_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sel3350); - gpiod_add_lookup_table(&sel3350_leds_table); gpiod_add_lookup_table(&sel3350_gpios_table); + for (i = 0; i < ARRAY_SIZE(sel3350_leds); ++i) { + sel3350_leds[i].gpiod = devm_gpiod_get(&pdev->dev, + sel3350_leds_gpio_names[i], + GPIOD_ASIS); + if (IS_ERR_OR_NULL(sel3350_leds[i].gpiod)) { + rs = -EPROBE_DEFER; + goto err_gpio_loop; + } + gpiod_set_consumer_name(sel3350_leds[i].gpiod, sel3350_leds[i].name); + } + sel3350->leds_pdev = platform_device_register_data( NULL, "leds-gpio", @@ -209,11 +276,15 @@ static int sel3350_probe(struct platform_device *pdev) return 0; +err_gpio_loop: + while (i--) + devm_gpiod_put(&pdev->dev, sel3350_leds[i].gpiod); + goto err_platform; + err_ps: platform_device_unregister(sel3350->leds_pdev); err_platform: gpiod_remove_lookup_table(&sel3350_gpios_table); - gpiod_remove_lookup_table(&sel3350_leds_table); return rs; } @@ -224,7 +295,6 @@ static void sel3350_remove(struct platform_device *pdev) platform_device_unregister(sel3350->leds_pdev); gpiod_remove_lookup_table(&sel3350_gpios_table); - gpiod_remove_lookup_table(&sel3350_leds_table); } static const struct acpi_device_id sel3350_device_ids[] = { From a3d0dbd18ce908292607bb6cf37c978ece8a33d4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:15:55 -0700 Subject: [PATCH 0827/5214] platform/x86: panasonic-laptop: simplify allocation of sinf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change to a flexible array member to allocate once instead of twice. Use __counted_by for extra runtime analysis. Move the counting variable assignment to right after allocation as done by kzalloc_flex for GCC 15 and above. Remove + 1 to allocation. It's already done in the previous line. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260430221555.83351-1-rosenp@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 1337f7c49805..2fe8ff0d32a8 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -246,11 +246,11 @@ struct pcc_acpi { int ac_brightness; int dc_brightness; int current_brightness; - u32 *sinf; struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; struct platform_device *platform; + u32 sinf[] __counted_by(num_sifr); }; /* @@ -1003,21 +1003,15 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) */ num_sifr++; - pcc = kzalloc_obj(struct pcc_acpi); + pcc = kzalloc_flex(*pcc, sinf, num_sifr); if (!pcc) { pr_err("Couldn't allocate mem for pcc"); return -ENOMEM; } - pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL); - if (!pcc->sinf) { - result = -ENOMEM; - goto out_hotkey; - } - + pcc->num_sifr = num_sifr; pcc->device = device; pcc->handle = device->handle; - pcc->num_sifr = num_sifr; device->driver_data = pcc; strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_PCC_CLASS); @@ -1025,7 +1019,7 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) result = acpi_pcc_init_input(pcc); if (result) { pr_err("Error installing keyinput handler\n"); - goto out_sinf; + goto out_hotkey; } if (!acpi_pcc_retrieve_biosdata(pcc)) { @@ -1106,10 +1100,8 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) backlight_device_unregister(pcc->backlight); out_input: input_unregister_device(pcc->input_dev); -out_sinf: - device->driver_data = NULL; - kfree(pcc->sinf); out_hotkey: + device->driver_data = NULL; kfree(pcc); return result; @@ -1139,7 +1131,6 @@ static void acpi_pcc_hotkey_remove(struct platform_device *pdev) device->driver_data = NULL; - kfree(pcc->sinf); kfree(pcc); } From 8ef6b01cee44803691c0a0c95b36f8ec710e2afb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:43:07 -0700 Subject: [PATCH 0828/5214] platform/x86/intel/vsec: allocate res with intel_vsec_dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a flexible array member to combine allocations. Avoids having to free separately. Add __counted_by for extra runtime analysis. Move counting variable assignment to after allocations as is already done by kzalloc_flex for GCC 15 and above. Signed-off-by: Rosen Penev Tested-by: David E. Box Reviewed-by: David E. Box Link: https://patch.msgid.link/20260430224307.109311-1-rosenp@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 14 ++++---------- drivers/platform/x86/intel/vsec_tpmi.c | 13 ++++--------- include/linux/intel_vsec.h | 6 +++--- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 7d5dbc1c1d05..9ef0f043bbee 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -112,7 +112,6 @@ static void intel_vsec_dev_release(struct device *dev) ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); kfree(intel_vsec_dev->acpi_disc); - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); } @@ -225,7 +224,6 @@ int intel_vsec_add_aux(struct device *parent, ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); return ret; } @@ -233,7 +231,6 @@ int intel_vsec_add_aux(struct device *parent, id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (id < 0) { xa_erase(&auxdev_array, intel_vsec_dev->id); - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); return id; } @@ -282,7 +279,7 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; - struct resource __free(kfree) *res = NULL; + struct resource *res; struct resource *tmp; struct device *parent; unsigned long quirks = info->quirks; @@ -306,13 +303,12 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head return -EINVAL; } - intel_vsec_dev = kzalloc_obj(*intel_vsec_dev); + intel_vsec_dev = kzalloc_flex(*intel_vsec_dev, resource, header->num_entries); if (!intel_vsec_dev) return -ENOMEM; - res = kzalloc_objs(*res, header->num_entries); - if (!res) - return -ENOMEM; + intel_vsec_dev->num_resources = header->num_entries; + res = intel_vsec_dev->resource; if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; @@ -342,8 +338,6 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head } intel_vsec_dev->dev = dev; - intel_vsec_dev->resource = no_free_ptr(res); - intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->priv_data = info->priv_data; diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 7fc6ff8d1040..5e16bc219c5b 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -626,15 +626,12 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, if (!name) return -EOPNOTSUPP; - res = kzalloc_objs(*res, pfs->pfs_header.num_entries); - if (!res) + feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries); + if (!feature_vsec_dev) return -ENOMEM; - feature_vsec_dev = kzalloc_obj(*feature_vsec_dev); - if (!feature_vsec_dev) { - kfree(res); - return -ENOMEM; - } + feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; + res = feature_vsec_dev->resource; snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name); @@ -647,8 +644,6 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, } feature_vsec_dev->dev = vsec_dev->dev; - feature_vsec_dev->resource = res; - feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; feature_vsec_dev->priv_data = &tpmi_info->plat_info; feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info); feature_vsec_dev->ida = &intel_vsec_tpmi_ida; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 1fe5665a9d02..07ea563f524e 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -135,8 +135,6 @@ struct intel_vsec_platform_info { * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access * @dev: struct device associated with the device - * @resource: PCI discovery resources (BAR windows), one per discovery - * instance. Valid only when @src == INTEL_VSEC_DISC_PCI * @acpi_disc: ACPI discovery tables, each entry is two QWORDs * in little-endian format as defined by the PMT ACPI spec. * Valid only when @src == INTEL_VSEC_DISC_ACPI. @@ -149,11 +147,12 @@ struct intel_vsec_platform_info { * @quirks: specified quirks * @base_addr: base address of entries (if specified) * @cap_id: the enumerated id of the vsec feature + * @resource: PCI discovery resources (BAR windows), one per discovery + * instance. Valid only when @src == INTEL_VSEC_DISC_PCI */ struct intel_vsec_device { struct auxiliary_device auxdev; struct device *dev; - struct resource *resource; u32 (*acpi_disc)[4]; enum intel_vsec_disc_source src; struct ida *ida; @@ -164,6 +163,7 @@ struct intel_vsec_device { unsigned long quirks; u64 base_addr; unsigned long cap_id; + struct resource resource[] __counted_by(num_resources); }; /** From 165e81354eefd5551358112773f24027aac59d5a Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 28 Apr 2026 21:51:59 -0400 Subject: [PATCH 0829/5214] platform/x86: thinkpad_acpi: Add debugfs entry to display HWDD raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo diagnostics and support team have requested a mechanism to get the raw data from the HWDD commands to support customer debug situations. Add a debugfs entry to display the HWDD raw data. Signed-off-by: Mark Pearson Link: https://patch.msgid.link/20260429015210.1863316-1-mpearson-lenovo@squebb.ca Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index d4ed6f1216f2..445e1403308e 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -11216,6 +11217,26 @@ static ssize_t hwdd_status_show(struct device *dev, return sysfs_emit(buf, "0\n"); } + +/* sysfs type-c damage detection raw data - accessed via debugfs*/ +static int hwdd_raw_show(struct seq_file *m, void *v) +{ + unsigned int damage_status; + int err; + + if (!ucdd_supported) + return -ENODEV; + + /* Get USB TYPE-C damage status */ + err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status); + if (err) + return err; + + seq_printf(m, "HWDD: 0x%x\n", damage_status); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(hwdd_raw); + static DEVICE_ATTR_RO(hwdd_status); static DEVICE_ATTR_RO(hwdd_detail); @@ -11503,6 +11524,20 @@ static const char * __init str_supported(int is_supported) } #endif /* CONFIG_THINKPAD_ACPI_DEBUG */ +static struct dentry *tpacpi_dbg; +static void tpacpi_debugfs_init(void) +{ + tpacpi_dbg = debugfs_create_dir("tpacpi", NULL); + + /* HWDD raw data */ + debugfs_create_file("hwdd-raw", 0444, tpacpi_dbg, NULL, &hwdd_raw_fops); +} + +static void tpacpi_debugfs_remove(void) +{ + debugfs_remove_recursive(tpacpi_dbg); +} + static void ibm_exit(struct ibm_struct *ibm) { dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name); @@ -12067,6 +12102,8 @@ static void thinkpad_acpi_module_exit(void) remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); if (tpacpi_wq) destroy_workqueue(tpacpi_wq); + if (tpacpi_dbg) + tpacpi_debugfs_remove(); kfree(thinkpad_id.bios_version_str); kfree(thinkpad_id.ec_version_str); @@ -12197,6 +12234,8 @@ static int __init thinkpad_acpi_module_init(void) return -ENODEV; } + tpacpi_debugfs_init(); + dmi_id = dmi_first_match(fwbug_list); if (dmi_id) tp_features.quirks = dmi_id->driver_data; From a4cbd51862d449e487866c173f5dff46329da76c Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 28 Apr 2026 16:38:46 +0200 Subject: [PATCH 0830/5214] perf test: Fix nanosleep check in the ftrace test The perf ftrace test case runs: perf ftrace profile --graph-opts depth=5 sleep 0.1 and checks that the output contains a *clock_nanosleep function with a count of 1. This fails on a risc-v system that uses musl as its C library. musl's nanosleep syscall wrapper uses either the nanosleep or the clock_nanosleep syscall. Filter for sys_*nanosleep to allow both syscalls. Signed-off-by: Martin Kaiser Acked-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/ftrace.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh index 7f8aafcbb761..9f6e590f6437 100755 --- a/tools/perf/tests/shell/ftrace.sh +++ b/tools/perf/tests/shell/ftrace.sh @@ -71,9 +71,10 @@ test_ftrace_profile() { grep ^# "${output}" time_re="[[:space:]]+1[[:digit:]]{5}\.[[:digit:]]{3}" # 100283.000 100283.000 100283.000 1 __x64_sys_clock_nanosleep - # Check for one *clock_nanosleep line with a Count of just 1 that takes a bit more than 0.1 seconds - # Strip the _x64_sys part to work with other architectures - grep -E "^${time_re}${time_re}${time_re}[[:space:]]+1[[:space:]]+.*clock_nanosleep" "${output}" + # Check for one *sys_*nanosleep line with a Count of just 1 that takes a bit more than 0.1 seconds + # Strip the _x64_ part to work with other architectures, strip the clock part to support + # C libraries that use the nanosleep syscall instead of clock_nanosleep + grep -E "^${time_re}${time_re}${time_re}[[:space:]]+1[[:space:]]+.*sys_.*nanosleep" "${output}" echo "perf ftrace profile test [Success]" } From 9a4a67dddc3d88e2e9d3cff462b943f40ea439e4 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 28 Apr 2026 16:38:47 +0200 Subject: [PATCH 0831/5214] perf test: Fix sys_enter_openat event test for musl The "syscalls:sys_enter_openat event fields" test calls openat(AT_FDCWD, "/etc/passwd", O_RDONLY | O_DIRECTORY) and verifies that the flags of the captured event are matching. This fails for musl, where the openat syscall wrapper always adds O_LARGEFILE. Update the check to allow for additional flags, the access mode flags must be unchanged. Signed-off-by: Martin Kaiser Acked-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/openat-syscall-tp-fields.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 2a139d2781a8..5523cf4e9321 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -120,7 +120,10 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused tp_flags = evsel__intval(evsel, &sample, "flags"); perf_sample__exit(&sample); - if (flags != tp_flags) { + /* C library wrapper may set additional flags, + access mode must be unchanged */ + if ((tp_flags & O_ACCMODE) != (flags & O_ACCMODE) || + (tp_flags & flags) != flags) { pr_debug("%s: Expected flags=%#x, got %#x\n", __func__, flags, tp_flags); goto out_delete_evlist; From e1c19cd376a30cbc893c76eaaa31f0e2fe972ebd Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 28 Apr 2026 16:38:48 +0200 Subject: [PATCH 0832/5214] perf test: Fix "trace summary" test for musl-based systems The trace summary test calls /bin/true and filters for open, read and close events. These events are coming from shared library loads. On a musl system, the loader and libc may point to the same file. true needs only libc, no further shared libraries are loaded at startup. The test fails since no open, read and close events are captured. root@host:~# ldd /bin/true /lib/ld-musl-riscv64.so.1 (0x3fb8882000) libc.so => /lib/ld-musl-riscv64.so.1 (0x3fb8882000) root@host:~# file /lib/ld-musl-riscv64.so.1 /lib/ld-musl-riscv64.so.1: symbolic link to /usr/lib/libc.so root@host:~# strace -f /bin/true execve("/bin/true", ["/bin/true", ...], ... /* 18 vars */) = 1 set_tid_address(0x3fa1f7bf70) = 330 mprotect(0x2ad6b8e000, 12288, PROT_READ) = 0 exit_group(0) = ? +++ exited with 0 +++ Run "cat /dev/null" instead of "true". This creates the required events regardless of the C library and it works for cat from busybox or from coreutils. Signed-off-by: Martin Kaiser Acked-by: Ian Rogers Acked-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/trace_summary.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/trace_summary.sh b/tools/perf/tests/shell/trace_summary.sh index 22e2651d5919..b80dea77cec6 100755 --- a/tools/perf/tests/shell/trace_summary.sh +++ b/tools/perf/tests/shell/trace_summary.sh @@ -14,7 +14,7 @@ OUTPUT=$(mktemp /tmp/perf_trace_test.XXXXX) test_perf_trace() { args=$1 - workload="true" + workload="cat /dev/null" search="^\s*(open|read|close).*[0-9]+%$" echo "testing: perf trace ${args} -- ${workload}" From 111388a0c01e6c2f9822623a764b3dcf8bc1492c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 21:10:04 -0700 Subject: [PATCH 0833/5214] perf sched stats: Fix segmentation faults, memory leaks, and stale pointers in diff mode The patch addresses multiple segmentation fault vectors, out-of-bounds reads, and memory leaks in perf sched stats by adding bounds checks, NULL checks, proper error propagation, and robust memory cleanup. 1. In get_all_cpu_stats(), added assert(!list_empty(head)) to prevent unsafe list_first_entry() calls on empty lists, added a missing NULL check for summary_head->cpu_data allocation, and implemented a cleanup ladder using a temporary list to prevent memory leaks on error paths. 2. In free_schedstat(), fixed memory leaks by ensuring internal domain_data and cpu_data pointers are freed. 3. In show_schedstat_data(), fixed a stale pointer issue where ds2 retained its value from a previous iteration when dptr2 became NULL, and added proper propagation of errors from get_all_cpu_stats(). 4. Propagated show_schedstat_data() errors up to perf_sched__schedstat_diff() and perf_sched__schedstat_live() to prevent output corruption on failure. 5. In show_schedstat_data(), added NULL checks for cd_map1 and cd_map2 to gracefully handle invalid or empty data files. 6. Added parallel iteration termination checks using list_is_last() in show_schedstat_data() for both domain and CPU lists to safely terminate at the end of each list when files contain a different number of CPUs or domains. 7. Added CPU bounds checks (cs1->cpu >= nr1 and cs2->cpu >= nr2) in show_schedstat_data() to prevent out-of-bounds reads from cd_map1 and cd_map2 when comparing files from machines with different CPU counts. 8. Added NULL checks for cd_info1 and cd_info2 to prevent crashes when a CPU has data samples but no corresponding domain info in the header. 9. Added domain bounds checks (ds1->domain >= cd_info1->nr_domains and ds2->domain >= cd_info2->nr_domains) to prevent out-of-bounds array accesses in the domains array. 10. Added NULL checks for dinfo1 and dinfo2 in show_schedstat_data() to prevent crashes when a domain has no corresponding domain info. 11. Zero-initialized the perf_data array in perf_sched__schedstat_diff() to prevent stack garbage from causing perf_data_file__fd() to attempt to use a NULL fptr when use_stdio happened to be non-zero. Assisted-by: Gemini:gemini-3.1-pro-preview Reviewed-by: James Clark Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Venkat Rao Bagalkote Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 175 +++++++++++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 37 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 555247568e7a..fd679b106582 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3947,6 +3947,8 @@ static struct schedstat_domain *domain_second_pass; static bool after_workload_flag; static bool verbose_field; +static void free_schedstat(struct list_head *head); + static void store_schedstat_cpu_diff(struct schedstat_cpu *after_workload) { struct perf_record_schedstat_cpu *before = cpu_second_pass->cpu_data; @@ -4170,37 +4172,50 @@ static void summarize_schedstat_domain(struct schedstat_domain *summary_domain, */ static int get_all_cpu_stats(struct list_head *head) { - struct schedstat_cpu *cptr = list_first_entry(head, struct schedstat_cpu, cpu_list); - struct schedstat_cpu *summary_head = NULL; - struct perf_record_schedstat_domain *ds; - struct perf_record_schedstat_cpu *cs; + struct schedstat_cpu *cptr, *summary_head = NULL; struct schedstat_domain *dptr, *tdptr; bool is_last = false; int cnt = 1; int ret = 0; + struct list_head tmp_cleanup_list; - if (cptr) { - summary_head = zalloc(sizeof(*summary_head)); - if (!summary_head) - return -ENOMEM; + assert(!list_empty(head)); + cptr = list_first_entry(head, struct schedstat_cpu, cpu_list); - summary_head->cpu_data = zalloc(sizeof(*cs)); - memcpy(summary_head->cpu_data, cptr->cpu_data, sizeof(*cs)); + INIT_LIST_HEAD(&tmp_cleanup_list); - INIT_LIST_HEAD(&summary_head->domain_head); + summary_head = zalloc(sizeof(*summary_head)); + if (!summary_head) + return -ENOMEM; - list_for_each_entry(dptr, &cptr->domain_head, domain_list) { - tdptr = zalloc(sizeof(*tdptr)); - if (!tdptr) - return -ENOMEM; + INIT_LIST_HEAD(&summary_head->domain_head); + INIT_LIST_HEAD(&summary_head->cpu_list); + list_add(&summary_head->cpu_list, &tmp_cleanup_list); - tdptr->domain_data = zalloc(sizeof(*ds)); - if (!tdptr->domain_data) - return -ENOMEM; + summary_head->cpu_data = zalloc(sizeof(*summary_head->cpu_data)); + if (!summary_head->cpu_data) { + ret = -ENOMEM; + goto out_cleanup; + } + memcpy(summary_head->cpu_data, cptr->cpu_data, sizeof(*summary_head->cpu_data)); - memcpy(tdptr->domain_data, dptr->domain_data, sizeof(*ds)); - list_add_tail(&tdptr->domain_list, &summary_head->domain_head); + list_for_each_entry(dptr, &cptr->domain_head, domain_list) { + tdptr = zalloc(sizeof(*tdptr)); + if (!tdptr) { + ret = -ENOMEM; + goto out_cleanup; } + INIT_LIST_HEAD(&tdptr->domain_list); + + tdptr->domain_data = zalloc(sizeof(*tdptr->domain_data)); + if (!tdptr->domain_data) { + free(tdptr); + ret = -ENOMEM; + goto out_cleanup; + } + + memcpy(tdptr->domain_data, dptr->domain_data, sizeof(*tdptr->domain_data)); + list_add_tail(&tdptr->domain_list, &summary_head->domain_head); } list_for_each_entry(cptr, head, cpu_list) { @@ -4212,32 +4227,52 @@ static int get_all_cpu_stats(struct list_head *head) cnt++; summarize_schedstat_cpu(summary_head, cptr, cnt, is_last); + if (list_empty(&summary_head->domain_head)) + continue; + tdptr = list_first_entry(&summary_head->domain_head, struct schedstat_domain, domain_list); list_for_each_entry(dptr, &cptr->domain_head, domain_list) { summarize_schedstat_domain(tdptr, dptr, cnt, is_last); + if (list_is_last(&tdptr->domain_list, &summary_head->domain_head)) { + tdptr = NULL; + break; + } tdptr = list_next_entry(tdptr, domain_list); } } + list_del_init(&summary_head->cpu_list); list_add(&summary_head->cpu_list, head); + return 0; + +out_cleanup: + free_schedstat(&tmp_cleanup_list); return ret; } -static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map **cd_map1, - struct list_head *head2, struct cpu_domain_map **cd_map2, +static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map **cd_map1, int nr1, + struct list_head *head2, struct cpu_domain_map **cd_map2, int nr2, bool summary_only) { struct schedstat_cpu *cptr1 = list_first_entry(head1, struct schedstat_cpu, cpu_list); struct perf_record_schedstat_domain *ds1 = NULL, *ds2 = NULL; - struct perf_record_schedstat_cpu *cs1 = NULL, *cs2 = NULL; struct schedstat_domain *dptr1 = NULL, *dptr2 = NULL; struct schedstat_cpu *cptr2 = NULL; __u64 jiffies1 = 0, jiffies2 = 0; bool is_summary = true; int ret = 0; + if (!cd_map1) { + pr_err("Error: CPU domain map 1 is missing.\n"); + return -1; + } + if (head2 && !cd_map2) { + pr_err("Error: CPU domain map 2 is missing.\n"); + return -1; + } + printf("Description\n"); print_separator2(SEP_LEN, "", 0); printf("%-30s-> %s\n", "DESC", "Description of the field"); @@ -4260,21 +4295,47 @@ static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map ** printf("\n"); ret = get_all_cpu_stats(head1); + if (ret) + return ret; if (cptr2) { ret = get_all_cpu_stats(head2); + if (ret) + return ret; cptr2 = list_first_entry(head2, struct schedstat_cpu, cpu_list); } list_for_each_entry(cptr1, head1, cpu_list) { struct cpu_domain_map *cd_info1 = NULL, *cd_info2 = NULL; + struct perf_record_schedstat_cpu *cs1 = cptr1->cpu_data; + struct perf_record_schedstat_cpu *cs2 = NULL; - cs1 = cptr1->cpu_data; + dptr2 = NULL; + if (cs1->cpu >= (u32)nr1) { + pr_err("Error: CPU %d exceeds domain map size %d\n", cs1->cpu, nr1); + return -1; + } cd_info1 = cd_map1[cs1->cpu]; + if (!cd_info1) { + pr_err("Error: CPU %d domain info is missing in map 1.\n", + cs1->cpu); + return -1; + } if (cptr2) { cs2 = cptr2->cpu_data; + if (cs2->cpu >= (u32)nr2) { + pr_err("Error: CPU %d exceeds domain map size %d\n", cs2->cpu, nr2); + return -1; + } cd_info2 = cd_map2[cs2->cpu]; - dptr2 = list_first_entry(&cptr2->domain_head, struct schedstat_domain, - domain_list); + if (!cd_info2) { + pr_err("Error: CPU %d domain info is missing in map 2.\n", + cs2->cpu); + return -1; + } + if (!list_empty(&cptr2->domain_head)) + dptr2 = list_first_entry(&cptr2->domain_head, + struct schedstat_domain, + domain_list); } if (cs2 && cs1->cpu != cs2->cpu) { @@ -4302,10 +4363,31 @@ static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map ** struct domain_info *dinfo1 = NULL, *dinfo2 = NULL; ds1 = dptr1->domain_data; + ds2 = NULL; + if (ds1->domain >= cd_info1->nr_domains) { + pr_err("Error: Domain %d exceeds max domains %d for CPU %d in map 1.\n", + ds1->domain, cd_info1->nr_domains, cs1->cpu); + return -1; + } dinfo1 = cd_info1->domains[ds1->domain]; + if (!dinfo1) { + pr_err("Error: Domain %d info is missing for CPU %d in map 1.\n", + ds1->domain, cs1->cpu); + return -1; + } if (dptr2) { ds2 = dptr2->domain_data; + if (ds2->domain >= cd_info2->nr_domains) { + pr_err("Error: Domain %d exceeds max domains %d for CPU %d in map 2.\n", + ds2->domain, cd_info2->nr_domains, cs2->cpu); + return -1; + } dinfo2 = cd_info2->domains[ds2->domain]; + if (!dinfo2) { + pr_err("Error: Domain %d info is missing for CPU %d in map 2.\n", + ds2->domain, cs2->cpu); + return -1; + } } if (dinfo2 && dinfo1->domain != dinfo2->domain) { @@ -4334,14 +4416,22 @@ static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map ** print_domain_stats(ds1, ds2, jiffies1, jiffies2); print_separator2(SEP_LEN, "", 0); - if (dptr2) - dptr2 = list_next_entry(dptr2, domain_list); + if (dptr2) { + if (list_is_last(&dptr2->domain_list, &cptr2->domain_head)) + dptr2 = NULL; + else + dptr2 = list_next_entry(dptr2, domain_list); + } } if (summary_only) break; - if (cptr2) - cptr2 = list_next_entry(cptr2, cpu_list); + if (cptr2) { + if (list_is_last(&cptr2->cpu_list, head2)) + cptr2 = NULL; + else + cptr2 = list_next_entry(cptr2, cpu_list); + } is_summary = false; } @@ -4473,9 +4563,11 @@ static void free_schedstat(struct list_head *head) list_for_each_entry_safe(cptr, n2, head, cpu_list) { list_for_each_entry_safe(dptr, n1, &cptr->domain_head, domain_list) { list_del_init(&dptr->domain_list); + free(dptr->domain_data); free(dptr); } list_del_init(&cptr->cpu_list); + free(cptr->cpu_data); free(cptr); } } @@ -4523,7 +4615,9 @@ static int perf_sched__schedstat_report(struct perf_sched *sched) } cd_map = session->header.env.cpu_domain; - err = show_schedstat_data(&cpu_head, cd_map, NULL, NULL, false); + err = show_schedstat_data(&cpu_head, cd_map, + session->header.env.nr_cpus_avail, + NULL, NULL, 0, false); } out: @@ -4538,7 +4632,7 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched, struct cpu_domain_map **cd_map0 = NULL, **cd_map1 = NULL; struct list_head cpu_head_ses0, cpu_head_ses1; struct perf_session *session[2]; - struct perf_data data[2]; + struct perf_data data[2] = {0}; int ret = 0, err = 0; static const char *defaults[] = { "perf.data.old", @@ -4573,8 +4667,10 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched, } err = perf_session__process_events(session[0]); - if (err) + if (err) { + free_schedstat(&cpu_head); goto out_delete_ses0; + } cd_map0 = session[0]->header.env.cpu_domain; list_replace_init(&cpu_head, &cpu_head_ses0); @@ -4590,8 +4686,10 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched, } err = perf_session__process_events(session[1]); - if (err) + if (err) { + free_schedstat(&cpu_head); goto out_delete_ses1; + } cd_map1 = session[1]->header.env.cpu_domain; list_replace_init(&cpu_head, &cpu_head_ses1); @@ -4607,10 +4705,13 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched, if (list_empty(&cpu_head_ses0)) { pr_err("Data is not available\n"); ret = -1; - goto out_delete_ses0; + goto out_delete_ses1; } - show_schedstat_data(&cpu_head_ses0, cd_map0, &cpu_head_ses1, cd_map1, true); + ret = show_schedstat_data(&cpu_head_ses0, cd_map0, session[0]->header.env.nr_cpus_avail, + &cpu_head_ses1, cd_map1, session[1]->header.env.nr_cpus_avail, true); + if (ret) + goto out_delete_ses1; out_delete_ses1: free_schedstat(&cpu_head_ses1); @@ -4720,7 +4821,7 @@ static int perf_sched__schedstat_live(struct perf_sched *sched, goto out; } - show_schedstat_data(&cpu_head, cd_map, NULL, NULL, false); + err = show_schedstat_data(&cpu_head, cd_map, nr, NULL, NULL, 0, false); free_cpu_domain_info(cd_map, sv, nr); out: free_schedstat(&cpu_head); From 91182741369b261c441e63e6678893032a6d7e4c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 17:45:42 -0700 Subject: [PATCH 0834/5214] perf sched: Add missing mmap2 handler in timehist perf_sched__timehist() registers event handlers for options using the sched->tool struct. It registers handlers for MMAP, COMM, EXIT, FORK, etc. but completely omits registering a handler for MMAP2 events. Failing to register both MMAP and MMAP2 handlers causes modern systems (which primarily output MMAP2 records) to silently drop VMA map mappings. This results in uninitialized machine/thread mapping structures, making it impossible to resolve shared library instruction pointers (IPs) to dynamic symbols/DSOs during timehist callchain analysis. Fix this by correctly registering perf_event__process_mmap2 in sched->tool inside perf_sched__timehist(). Fixes: 49394a2a24c78ce0 ("perf sched timehist: Introduce timehist command") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: David Ahern Cc: Gabriel Marin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fd679b106582..53a93aa18853 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3299,6 +3299,7 @@ static int perf_sched__timehist(struct perf_sched *sched) */ sched->tool.sample = perf_timehist__process_sample; sched->tool.mmap = perf_event__process_mmap; + sched->tool.mmap2 = perf_event__process_mmap2; sched->tool.comm = perf_event__process_comm; sched->tool.exit = perf_event__process_exit; sched->tool.fork = perf_event__process_fork; From 09d355618f7ccc27ffc7fc668b2e232872962079 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 17:45:43 -0700 Subject: [PATCH 0835/5214] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delegate_tool was missing the delegate overrides for schedstat_cpu and schedstat_domain. As a result, when allocated with zalloc, these callbacks defaulted to NULL, causing a segmentation fault crash if any schedstat events were delivered during event processing. Fix this by adding delegate_schedstat_cpu and delegate_schedstat_domain via the CREATE_DELEGATE_OP2 macro, and ensuring delegate_tool__init correctly registers them. Additionally, delegate_tool__init completely omitted copying the dont_split_sample_group property from the delegate. This would cause wrapper tools to default the flag to false, which corrupts piped event processing (e.g., in perf inject) by triggering duplicate event deliveries on split sample values in deliver_sample_group(). Similarly, perf_tool__init() omitted the initialization of this boolean field. On stack-allocated tools that rely on this initializer (like intel-tpebs or __cmd_evlist), this could result in uninitialized stack garbage evaluating to true—silently dropping non-leader event members in deliver_sample_group(). Fix both issues by properly copying the field in delegate_tool__init and initializing it to false in perf_tool__init. Fixes: 6331b266935916bf ("perf tool: Add a delegate_tool that just delegates actions to another tool") Fixes: 79bcd34e0f3da39f ("perf inject: Fix leader sampling inserting additional samples") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Gabriel Marin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/tool.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 013c7839e2cf..ff2150517b75 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -285,6 +285,7 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->no_warn = false; tool->show_feat_hdr = SHOW_FEAT_NO_HEADER; tool->merge_deferred_callchains = true; + tool->dont_split_sample_group = false; tool->sample = process_event_sample_stub; tool->mmap = process_event_stub; @@ -433,6 +434,8 @@ CREATE_DELEGATE_OP2(stat_config); CREATE_DELEGATE_OP2(stat_round); CREATE_DELEGATE_OP2(thread_map); CREATE_DELEGATE_OP2(time_conv); +CREATE_DELEGATE_OP2(schedstat_cpu); +CREATE_DELEGATE_OP2(schedstat_domain); CREATE_DELEGATE_OP2(tracing_data); #define CREATE_DELEGATE_OP3(name) \ @@ -470,6 +473,7 @@ void delegate_tool__init(struct delegate_tool *tool, struct perf_tool *delegate) tool->tool.no_warn = delegate->no_warn; tool->tool.show_feat_hdr = delegate->show_feat_hdr; tool->tool.merge_deferred_callchains = delegate->merge_deferred_callchains; + tool->tool.dont_split_sample_group = delegate->dont_split_sample_group; tool->tool.sample = delegate_sample; tool->tool.read = delegate_read; @@ -516,4 +520,6 @@ void delegate_tool__init(struct delegate_tool *tool, struct perf_tool *delegate) tool->tool.bpf_metadata = delegate_bpf_metadata; tool->tool.compressed = delegate_compressed; tool->tool.auxtrace = delegate_auxtrace; + tool->tool.schedstat_cpu = delegate_schedstat_cpu; + tool->tool.schedstat_domain = delegate_schedstat_domain; } From c87c9046c4e00d599454e033a477176c4d73ac2a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 May 2026 18:39:57 +0800 Subject: [PATCH 0836/5214] pinctrl: mediatek: paris: bypass pinctrl GPIO layer in set GPIO direction pinctrl_gpio_direction_input() / pinctrl_gpio_direction_output() take the pinctrl mutex. This causes a gpiochip operations to need to sleep. Worse yet, the .can_sleep field in the gpiochip is not set. This causes the shared GPIO proxy to trip over, as it uses gpiod_cansleep() to check whether it can use a spinlock or needs a mutex. In this case, it ends up taking a spinlock, then calls pinctrl_gpio_direction_output(), which takes a mutex. This causes a huge warning. While this class of Mediatek hardware does not have separate clear/set registers, the pinctrl context has a spinlock that is taken whenever a register read-modify-write is done. Also, once the GPIO function is selected / muxed in, further GPIO operations do not involve pinctrl operations or state. The GPIO direction and level values do not require toggling the pinmux or any other pin config options. Switch to directly calling mtk_pinmux_gpio_set_direction() in the GPIO set direction callbacks to avoid taking the pinctrl mutex. Drop the .gpio_set_direction field in mtk_pmxops to signal we are no longer using the pinctrl GPIO layer for setting the direction. Signed-off-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-paris.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-paris.c b/drivers/pinctrl/mediatek/pinctrl-paris.c index 6bf37d8085fa..23f04b24fd65 100644 --- a/drivers/pinctrl/mediatek/pinctrl-paris.c +++ b/drivers/pinctrl/mediatek/pinctrl-paris.c @@ -771,7 +771,6 @@ static const struct pinmux_ops mtk_pmxops = { .get_function_name = mtk_pmx_get_func_name, .get_function_groups = mtk_pmx_get_func_groups, .set_mux = mtk_pmx_set_mux, - .gpio_set_direction = mtk_pinmux_gpio_set_direction, .gpio_request_enable = mtk_pinmux_gpio_request_enable, }; @@ -886,19 +885,22 @@ static int mtk_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) static int mtk_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio) { - return pinctrl_gpio_direction_input(chip, gpio); + struct mtk_pinctrl *hw = gpiochip_get_data(chip); + + return mtk_pinmux_gpio_set_direction(hw->pctrl, NULL, gpio, true); } static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, int value) { + struct mtk_pinctrl *hw = gpiochip_get_data(chip); int ret; ret = mtk_gpio_set(chip, gpio, value); if (ret) return ret; - return pinctrl_gpio_direction_output(chip, gpio); + return mtk_pinmux_gpio_set_direction(hw->pctrl, NULL, gpio, false); } static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) From 3982db2df3ed4c195e5f0a9a4513545a15901107 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 May 2026 18:40:55 +0800 Subject: [PATCH 0837/5214] pinctrl: mediatek: common-v1: bypass pinctrl GPIO layer in set GPIO direction pinctrl_gpio_direction_input() / pinctrl_gpio_direction_output() take the pinctrl mutex. This causes a gpiochip operations to need to sleep. Worse yet, the .can_sleep field in the gpiochip is not set. This causes the shared GPIO proxy to trip over, as it uses gpiod_cansleep() to check whether it can use a spinlock or needs a mutex. In this case, it ends up taking a spinlock, then calls pinctrl_gpio_direction_output(), which takes a mutex. This causes a huge warning. Since the Mediatek hardware has separate clear/set registers, there is no risk of clobbering other bits like with a read-modify-write pattern. Also, once the GPIO function is selected / muxed in, further GPIO operations do not involve pinctrl operations or state. The GPIO direction and level values do not require toggling the pinmux or any other pin config options. Switch to directly calling mtk_pmx_gpio_set_direction() in the GPIO set direction callbacks to avoid taking the pinctrl mutex. Drop the .gpio_set_direction field in mtk_pmx_ops to signal we are no longer using the pinctrl GPIO layer for setting the direction. Signed-off-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index 3f518dce6d23..dd2c8aa03938 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -802,20 +802,27 @@ static const struct pinmux_ops mtk_pmx_ops = { .get_function_name = mtk_pmx_get_func_name, .get_function_groups = mtk_pmx_get_func_groups, .set_mux = mtk_pmx_set_mux, - .gpio_set_direction = mtk_pmx_gpio_set_direction, .gpio_request_enable = mtk_pmx_gpio_request_enable, }; +static int mtk_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct mtk_pinctrl *pctl = gpiochip_get_data(chip); + + return mtk_pmx_gpio_set_direction(pctl->pctl_dev, NULL, offset, true); +} + static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { + struct mtk_pinctrl *pctl = gpiochip_get_data(chip); int ret; ret = mtk_gpio_set(chip, offset, value); if (ret) return ret; - return pinctrl_gpio_direction_output(chip, offset); + return mtk_pmx_gpio_set_direction(pctl->pctl_dev, NULL, offset, false); } static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -895,7 +902,7 @@ static const struct gpio_chip mtk_gpio_chip = { .request = gpiochip_generic_request, .free = gpiochip_generic_free, .get_direction = mtk_gpio_get_direction, - .direction_input = pinctrl_gpio_direction_input, + .direction_input = mtk_gpio_direction_input, .direction_output = mtk_gpio_direction_output, .get = mtk_gpio_get, .set = mtk_gpio_set, From 86dc1b92cdb343f8de51edf489bcdd8cb5517cf8 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 6 May 2026 16:06:18 +0800 Subject: [PATCH 0838/5214] dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl Add a device tree binding for the pin controller found in the ASPEED AST2700 SoC0. The controller manages various peripheral functions such as eMMC, USB, VGA DDC, JTAG, and PCIe root complex signals. Describe the AST2700 SoC0 pin controller using standard pin multiplexing and configuration properties. Signed-off-by: Billy Tsai Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../pinctrl/aspeed,ast2700-soc0-pinctrl.yaml | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml new file mode 100644 index 000000000000..407a64f28d49 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml @@ -0,0 +1,188 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED AST2700 SoC0 Pin Controller + +maintainers: + - Billy Tsai + +description: > + The AST2700 features a dual-SoC architecture with two interconnected SoCs, + each having its own System Control Unit (SCU) for independent pin control. + This pin controller manages the pin multiplexing for SoC0. + + The SoC0 pin controller manages pin functions including eMMC, VGA DDC, + dual USB3/USB2 ports (A and B), JTAG, and PCIe root complex interfaces. + +properties: + compatible: + const: aspeed,ast2700-soc0-pinctrl + + reg: + maxItems: 1 + +patternProperties: + '-state$': + description: | + Pin control state. + + If 'function' is present, the node describes a pinmux state and must + specify 'groups'. + + For pin configuration, exactly one of 'groups' or 'pins' must be + specified in each state node. Group-level configuration applies to all + pins in the group. Pin-level configuration may be supplied in a + separate state node for individual pins; when both group-level and + pin-level configuration apply to the same pin, the pin-level + configuration takes precedence. + + type: object + allOf: + - $ref: pinmux-node.yaml# + - $ref: pincfg-node.yaml# + - if: + required: + - function + then: + required: + - groups + - oneOf: + - required: + - groups + - required: + - pins + + additionalProperties: false + + properties: + function: + enum: + - EMMC + - JTAGDDR + - JTAGM0 + - JTAGPCIEA + - JTAGPCIEB + - JTAGPSP + - JTAGSSP + - JTAGTSP + - JTAGUSB3A + - JTAGUSB3B + - PCIERC0PERST + - PCIERC1PERST + - TSPRSTN + - UFSCLKI + - USB2AD0 + - USB2AD1 + - USB2AH + - USB2AHP + - USB2AHPD0 + - USB2AXH + - USB2AXH2B + - USB2AXHD1 + - USB2AXHP + - USB2AXHP2B + - USB2AXHPD1 + - USB2BD0 + - USB2BD1 + - USB2BH + - USB2BHP + - USB2BHPD0 + - USB2BXH + - USB2BXH2A + - USB2BXHD1 + - USB2BXHP + - USB2BXHP2A + - USB2BXHPD1 + - USB3AXH + - USB3AXH2B + - USB3AXHD + - USB3AXHP + - USB3AXHP2B + - USB3AXHPD + - USB3BXH + - USB3BXH2A + - USB3BXHD + - USB3BXHP + - USB3BXHP2A + - USB3BXHPD + - VB + - VGADDC + + groups: + enum: + - EMMCCDN + - EMMCG1 + - EMMCG4 + - EMMCG8 + - EMMCWPN + - JTAG0 + - PCIERC0PERST + - PCIERC1PERST + - TSPRSTN + - UFSCLKI + - USB2A + - USB2AAP + - USB2ABP + - USB2ADAP + - USB2AH + - USB2AHAP + - USB2B + - USB2BAP + - USB2BBP + - USB2BDBP + - USB2BH + - USB2BHBP + - USB3A + - USB3AAP + - USB3ABP + - USB3B + - USB3BAP + - USB3BBP + - VB0 + - VB1 + - VGADDC + + pins: + enum: + - AB13 + - AB14 + - AC13 + - AC14 + - AD13 + - AD14 + - AE13 + - AE14 + - AE15 + - AF13 + - AF14 + - AF15 + + drive-strength: + enum: [3, 6, 8, 11, 16, 18, 20, 23, 30, 32, 33, 35, 37, 38, 39, 41] + + bias-disable: true + bias-pull-up: true + bias-pull-down: true + +required: + - compatible + - reg + +allOf: + - $ref: pinctrl.yaml# + +additionalProperties: false + +examples: + - | + pinctrl@400 { + compatible = "aspeed,ast2700-soc0-pinctrl"; + reg = <0x400 0x318>; + emmc-state { + function = "EMMC"; + groups = "EMMCG1"; + }; + }; From 7f73aa118a6fd9d3a8a2030e1f6aa0dd5dc8cae1 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 6 May 2026 16:06:20 +0800 Subject: [PATCH 0839/5214] pinctrl: aspeed: Add AST2700 SoC0 support Add pinctrl support for the SoC0 instance of the ASPEED AST2700. AST2700 consists of two interconnected SoC instances, each with its own pinctrl register block. The SoC0 pinctrl hardware closely follows the design found in previous ASPEED BMC generations, allowing the driver to build upon the common ASPEED pinctrl infrastructure. Signed-off-by: Billy Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/aspeed/Kconfig | 9 + drivers/pinctrl/aspeed/Makefile | 1 + .../pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c | 749 ++++++++++++++++++ 3 files changed, 759 insertions(+) create mode 100644 drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig index 1a4e5b9ed471..f9672cca891e 100644 --- a/drivers/pinctrl/aspeed/Kconfig +++ b/drivers/pinctrl/aspeed/Kconfig @@ -31,3 +31,12 @@ config PINCTRL_ASPEED_G6 help Say Y here to enable pin controller support for Aspeed's 6th generation SoCs. GPIO is provided by a separate GPIO driver. + +config PINCTRL_ASPEED_G7_SOC0 + bool "Aspeed G7 SoC pin control" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF + select PINCTRL_ASPEED + help + Say Y here to enable pin controller support for the SoC0 instance + of Aspeed's 7th generation SoCs. GPIO is provided by a separate + GPIO driver. diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile index db2a7600ae2b..0de524ca2c72 100644 --- a/drivers/pinctrl/aspeed/Makefile +++ b/drivers/pinctrl/aspeed/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_ASPEED) += pinctrl-aspeed.o pinmux-aspeed.o obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o obj-$(CONFIG_PINCTRL_ASPEED_G6) += pinctrl-aspeed-g6.o +obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC0) += pinctrl-aspeed-g7-soc0.o diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c new file mode 100644 index 000000000000..94f216f55bdd --- /dev/null +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-aspeed.h" +#include "pinmux-aspeed.h" +#include "../pinctrl-utils.h" + +#define SCU200 0x200 /* System Reset Control #1 */ + +#define SCU010 0x010 /* Hardware Strap Register */ +#define SCU400 0x400 /* Multi-function Pin Control #1 */ +#define SCU404 0x404 /* Multi-function Pin Control #2 */ +#define SCU408 0x408 /* Multi-function Pin Control #3 */ +#define SCU40C 0x40C /* Multi-function Pin Control #3 */ +#define SCU410 0x410 /* USB Multi-function Control Register */ +#define SCU414 0x414 /* VGA Function Control Register */ + +#define SCU480 0x480 /* GPIO18A0 IO Control Register */ +#define SCU484 0x484 /* GPIO18A1 IO Control Register */ +#define SCU488 0x488 /* GPIO18A2 IO Control Register */ +#define SCU48C 0x48c /* GPIO18A3 IO Control Register */ +#define SCU490 0x490 /* GPIO18A4 IO Control Register */ +#define SCU494 0x494 /* GPIO18A5 IO Control Register */ +#define SCU498 0x498 /* GPIO18A6 IO Control Register */ +#define SCU49C 0x49c /* GPIO18A7 IO Control Register */ +#define SCU4A0 0x4A0 /* GPIO18B0 IO Control Register */ +#define SCU4A4 0x4A4 /* GPIO18B1 IO Control Register */ +#define SCU4A8 0x4A8 /* GPIO18B2 IO Control Register */ +#define SCU4AC 0x4AC /* GPIO18B3 IO Control Register */ + +enum { + AC14, + AE15, + AD14, + AE14, + AF14, + AB13, + AB14, + AF15, + AF13, + AC13, + AD13, + AE13, + JTAG_PORT, + PCIERC0_PERST, + PCIERC1_PERST, + PORTA_MODE, + PORTA_U2, + PORTB_MODE, + PORTB_U2, + PORTA_U2_PHY, + PORTB_U2_PHY, + PORTA_U3, + PORTB_U3, + PORTA_U3_PHY, + PORTB_U3_PHY, +}; + +SIG_EXPR_LIST_DECL_SEMG(AC14, EMMCCLK, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 0)); +SIG_EXPR_LIST_DECL_SEMG(AC14, VB1CS, VB1, VB, SIG_DESC_SET(SCU404, 0)); +PIN_DECL_2(AC14, GPIO18A0, EMMCCLK, VB1CS); + +SIG_EXPR_LIST_DECL_SEMG(AE15, EMMCCMD, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 1)); +SIG_EXPR_LIST_DECL_SEMG(AE15, VB1CK, VB1, VB, SIG_DESC_SET(SCU404, 1)); +PIN_DECL_2(AE15, GPIO18A1, EMMCCMD, VB1CK); + +SIG_EXPR_LIST_DECL_SEMG(AD14, EMMCDAT0, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 2)); +SIG_EXPR_LIST_DECL_SEMG(AD14, VB1MOSI, VB1, VB, SIG_DESC_SET(SCU404, 2)); +PIN_DECL_2(AD14, GPIO18A2, EMMCDAT0, VB1MOSI); + +SIG_EXPR_LIST_DECL_SEMG(AE14, EMMCDAT1, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 3)); +SIG_EXPR_LIST_DECL_SEMG(AE14, VB1MISO, VB1, VB, SIG_DESC_SET(SCU404, 3)); +PIN_DECL_2(AE14, GPIO18A3, EMMCDAT1, VB1MISO); + +SIG_EXPR_LIST_DECL_SEMG(AF14, EMMCDAT2, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 4)); +PIN_DECL_1(AF14, GPIO18A4, EMMCDAT2); + +SIG_EXPR_LIST_DECL_SEMG(AB13, EMMCDAT3, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 5)); +PIN_DECL_1(AB13, GPIO18A5, EMMCDAT3); + +SIG_EXPR_LIST_DECL_SEMG(AB14, EMMCCDN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 6)); +SIG_EXPR_LIST_DECL_SEMG(AB14, VB0CS, VB0, VB, SIG_DESC_SET(SCU010, 17)); +PIN_DECL_2(AB14, GPIO18A6, EMMCCDN, VB0CS); + +SIG_EXPR_LIST_DECL_SEMG(AF15, EMMCWPN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 7)); +SIG_EXPR_LIST_DECL_SEMG(AF15, VB0CK, VB0, VB, SIG_DESC_SET(SCU010, 17)); +PIN_DECL_2(AF15, GPIO18A7, EMMCWPN, VB0CK); + +SIG_EXPR_LIST_DECL_SESG(AF13, TSPRSTN, TSPRSTN, SIG_DESC_SET(SCU010, 9)); +SIG_EXPR_LIST_DECL_SEMG(AF13, EMMCDAT4, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 8)); +SIG_EXPR_LIST_DECL_SEMG(AF13, VB0MOSI, VB0, VB, SIG_DESC_SET(SCU010, 17)); +PIN_DECL_3(AF13, GPIO18B0, TSPRSTN, EMMCDAT4, VB0MOSI); + +SIG_EXPR_LIST_DECL_SESG(AC13, UFSCLKI, UFSCLKI, SIG_DESC_SET(SCU010, 19)); +SIG_EXPR_LIST_DECL_SEMG(AC13, EMMCDAT5, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 9)); +SIG_EXPR_LIST_DECL_SEMG(AC13, VB0MISO, VB0, VB, SIG_DESC_SET(SCU010, 17)); +PIN_DECL_3(AC13, GPIO18B1, UFSCLKI, EMMCDAT5, VB0MISO); + +SIG_EXPR_LIST_DECL_SEMG(AD13, EMMCDAT6, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 10)); +SIG_EXPR_LIST_DECL_SESG(AD13, DDCCLK, VGADDC, SIG_DESC_SET(SCU404, 10)); +PIN_DECL_2(AD13, GPIO18B2, EMMCDAT6, DDCCLK); + +SIG_EXPR_LIST_DECL_SEMG(AE13, EMMCDAT7, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 11)); +SIG_EXPR_LIST_DECL_SESG(AE13, DDCDAT, VGADDC, SIG_DESC_SET(SCU404, 11)); +PIN_DECL_2(AE13, GPIO18B3, EMMCDAT7, DDCDAT); + +GROUP_DECL(EMMCG1, AC14, AE15, AD14); +GROUP_DECL(EMMCG4, AC14, AE15, AD14, AE14, AF14, AB13); +GROUP_DECL(EMMCG8, AC14, AE15, AD14, AE14, AF14, AB13, AF13, AC13, AD13, AE13); +GROUP_DECL(EMMCWPN, AF15); +GROUP_DECL(EMMCCDN, AB14); +FUNC_DECL_(EMMC, "EMMCG1", "EMMCG4", "EMMCG8", "EMMCWPN", "EMMCCDN"); + +GROUP_DECL(VB1, AC14, AE15, AD14, AE14); +GROUP_DECL(VB0, AF15, AB14, AF13, AC13); +FUNC_DECL_2(VB, VB1, VB0); + +FUNC_GROUP_DECL(TSPRSTN, AF13); + +FUNC_GROUP_DECL(UFSCLKI, AC13); + +FUNC_GROUP_DECL(VGADDC, AD13, AE13); + +/* JTAG Port Selection */ +#define JTAG_PORT_PSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x0, 0 } +#define JTAG_PORT_SSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x41, 0 } +#define JTAG_PORT_TSP_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x42, 0 } +#define JTAG_PORT_DDR_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x43, 0 } +#define JTAG_PORT_USB3A_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x44, 0 } +#define JTAG_PORT_USB3B_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x45, 0 } +#define JTAG_PORT_PCIEA_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x46, 0 } +#define JTAG_PORT_PCIEB_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x47, 0 } +#define JTAG_PORT_JTAGM0_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x8, 0 } + +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPSP, JTAG0, JTAGPSP, JTAG_PORT_PSP_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGSSP, JTAG0, JTAGSSP, JTAG_PORT_SSP_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGTSP, JTAG0, JTAGTSP, JTAG_PORT_TSP_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGDDR, JTAG0, JTAGDDR, JTAG_PORT_DDR_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3A, JTAG0, JTAGUSB3A, JTAG_PORT_USB3A_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3B, JTAG0, JTAGUSB3B, JTAG_PORT_USB3B_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEA, JTAG0, JTAGPCIEA, JTAG_PORT_PCIEA_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEB, JTAG0, JTAGPCIEB, JTAG_PORT_PCIEB_DESC); +SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGM0, JTAG0, JTAGM0, JTAG_PORT_JTAGM0_DESC); +PIN_DECL_(JTAG_PORT, SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGSSP), + SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGTSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGDDR), + SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3A), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3B), + SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEA), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEB), + SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGM0)); + +GROUP_DECL(JTAG0, JTAG_PORT); + +FUNC_DECL_1(JTAGPSP, JTAG0); +FUNC_DECL_1(JTAGSSP, JTAG0); +FUNC_DECL_1(JTAGTSP, JTAG0); +FUNC_DECL_1(JTAGDDR, JTAG0); +FUNC_DECL_1(JTAGUSB3A, JTAG0); +FUNC_DECL_1(JTAGUSB3B, JTAG0); +FUNC_DECL_1(JTAGPCIEA, JTAG0); +FUNC_DECL_1(JTAGPCIEB, JTAG0); +FUNC_DECL_1(JTAGM0, JTAG0); + +/* PCIe Reset Control */ +SIG_EXPR_LIST_DECL_SESG(PCIERC0_PERST, PCIERC0PERST, PCIERC0PERST, SIG_DESC_SET(SCU200, 21)); +PIN_DECL_(PCIERC0_PERST, SIG_EXPR_LIST_PTR(PCIERC0_PERST, PCIERC0PERST)); +FUNC_GROUP_DECL(PCIERC0PERST, PCIERC0_PERST); + +SIG_EXPR_LIST_DECL_SESG(PCIERC1_PERST, PCIERC1PERST, PCIERC1PERST, SIG_DESC_SET(SCU200, 19)); +PIN_DECL_(PCIERC1_PERST, SIG_EXPR_LIST_PTR(PCIERC1_PERST, PCIERC1PERST)); +FUNC_GROUP_DECL(PCIERC1PERST, PCIERC1_PERST); + +#define PORTA_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 0, 0 } +#define PORTA_MODE_D0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 1, 0 } +#define PORTA_MODE_H_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 2, 0 } +#define PORTA_MODE_HP_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHPD0, USB2AH, USB2AHPD0, PORTA_MODE_HPD0_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AH, USB2AHAP, USB2AH, PORTA_MODE_H_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHP, USB2AHAP, USB2AHP, PORTA_MODE_HP_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AD0, USB2AHAP, USB2AD0, PORTA_MODE_D0_DESC); +PIN_DECL_(PORTA_MODE, SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHPD0), + SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AH), SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHP), + SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AD0)); + +#define PORTA_U2_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 0, 0 } +#define PORTA_U2_D1_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 1, 0 } +#define PORTA_U2_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 2, 0 } +#define PORTA_U2_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHD1, USB2A, USB2AXHD1, PORTA_U2_XHD_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHPD1, USB2A, USB2AXHPD1, PORTA_U2_XHD_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH, USB2AAP, USB2AXH, PORTA_U2_XH_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP, USB2AAP, USB2AXHP, PORTA_U2_XH_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH2B, USB2ABP, USB2AXH2B, PORTA_U2_XH2E_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP2B, USB2ABP, USB2AXHP2B, PORTA_U2_XH2E_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AD1, USB2ADAP, USB2AD1, PORTA_U2_D1_DESC); +PIN_DECL_(PORTA_U2, SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHD1), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHPD1), + SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP), + SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH2B), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP2B), + SIG_EXPR_LIST_PTR(PORTA_U2, USB2AD1)); + +#define PORTB_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 0, 0 } +#define PORTB_MODE_D0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 1, 0 } +#define PORTB_MODE_H_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 2, 0 } +#define PORTB_MODE_HP_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHPD0, USB2BH, USB2BHPD0, PORTB_MODE_HPD0_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BH, USB2BHBP, USB2BH, PORTB_MODE_H_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHP, USB2BHBP, USB2BHP, PORTB_MODE_HP_DESC); +SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BD0, USB2BHBP, USB2BD0, PORTB_MODE_D0_DESC); +PIN_DECL_(PORTB_MODE, SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHPD0), + SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BH), SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHP), + SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BD0)); + +#define PORTB_U2_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 0, 0 } +#define PORTB_U2_D1_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 1, 0 } +#define PORTB_U2_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 2, 0 } +#define PORTB_U2_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHD1, USB2B, USB2BXHD1, PORTB_U2_XHD_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHPD1, USB2B, USB2BXHPD1, PORTB_U2_XHD_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH, USB2BBP, USB2BXH, PORTB_U2_XH_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP, USB2BBP, USB2BXHP, PORTB_U2_XH_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH2A, USB2BAP, USB2BXH2A, PORTB_U2_XH2E_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP2A, USB2BAP, USB2BXHP2A, PORTB_U2_XH2E_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BD1, USB2BDBP, USB2BD1, PORTB_U2_D1_DESC); +PIN_DECL_(PORTB_U2, SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHD1), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHPD1), + SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP), + SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH2A), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP2A), + SIG_EXPR_LIST_PTR(PORTB_U2, USB2BD1)); +/* + * USB2 virtual PHY pins. + * + * PORTA_U2_PHY and PORTB_U2_PHY are logical endpoints, not package pins. + * They alias existing USB2 expressions so pin groups can model direct and + * cross-coupled routing for host and mode paths. + * + * - USB2AAP/USB2ADAP/USB2AHAP: use PORTA_U2_PHY + * - USB2ABP : use PORTB_U2_PHY + * - USB2BBP/USB2BDBP/USB2BHBP: use PORTB_U2_PHY + * - USB2BAP : use PORTA_U2_PHY + * + * They do not have any registers to configure this behaviour; the goal is + * simply for the driver to prevent conflicting selections. For example, + * selecting group USB2ABP and USB2BBP at the same time should not be + * allowed. + */ +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXH, USB2AAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXHP, USB2AAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXH2A, USB2BAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXHP2A, USB2BAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD1, USB2ADAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AH, USB2AHAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AHP, USB2AHAP); +SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD0, USB2AHAP); +PIN_DECL_(PORTA_U2_PHY, SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXH), + SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXHP), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXH2A), + SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXHP2A), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD1), + SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AH), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AHP), + SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD0)); + +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXH2B, USB2ABP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXHP2B, USB2ABP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXH, USB2BBP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXHP, USB2BBP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD1, USB2BDBP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BH, USB2BHBP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BHP, USB2BHBP); +SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD0, USB2BHBP); +PIN_DECL_(PORTB_U2_PHY, SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXH2B), + SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXH), + SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXHP), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD1), + SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BH), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BHP), + SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD0)); + +GROUP_DECL(USB2A, PORTA_U2); +GROUP_DECL(USB2AAP, PORTA_U2, PORTA_U2_PHY); +GROUP_DECL(USB2ABP, PORTA_U2, PORTB_U2_PHY); +GROUP_DECL(USB2ADAP, PORTA_U2, PORTA_U2_PHY); +GROUP_DECL(USB2AH, PORTA_MODE); +GROUP_DECL(USB2AHAP, PORTA_MODE, PORTA_U2_PHY); + +FUNC_DECL_1(USB2AXHD1, USB2A); +FUNC_DECL_1(USB2AXHPD1, USB2A); +FUNC_DECL_1(USB2AXH, USB2AAP); +FUNC_DECL_1(USB2AXHP, USB2AAP); +FUNC_DECL_1(USB2AXH2B, USB2ABP); +FUNC_DECL_1(USB2AXHP2B, USB2ABP); +FUNC_DECL_1(USB2AD1, USB2ADAP); +FUNC_DECL_1(USB2AHPD0, USB2AH); +FUNC_DECL_1(USB2AH, USB2AHAP); +FUNC_DECL_1(USB2AHP, USB2AHAP); +FUNC_DECL_1(USB2AD0, USB2AHAP); + +GROUP_DECL(USB2B, PORTB_U2); +GROUP_DECL(USB2BBP, PORTB_U2, PORTB_U2_PHY); +GROUP_DECL(USB2BAP, PORTB_U2, PORTA_U2_PHY); +GROUP_DECL(USB2BDBP, PORTB_U2, PORTB_U2_PHY); +GROUP_DECL(USB2BH, PORTB_MODE); +GROUP_DECL(USB2BHBP, PORTB_MODE, PORTB_U2_PHY); + +FUNC_DECL_1(USB2BXHD1, USB2B); +FUNC_DECL_1(USB2BXHPD1, USB2B); +FUNC_DECL_1(USB2BXH, USB2BBP); +FUNC_DECL_1(USB2BXHP, USB2BBP); +FUNC_DECL_1(USB2BXH2A, USB2BAP); +FUNC_DECL_1(USB2BXHP2A, USB2BAP); +FUNC_DECL_1(USB2BD1, USB2BDBP); +FUNC_DECL_1(USB2BHPD0, USB2BH); +FUNC_DECL_1(USB2BH, USB2BHBP); +FUNC_DECL_1(USB2BHP, USB2BHBP); +FUNC_DECL_1(USB2BD0, USB2BHBP); + +#define PORTA_U3_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 0, 0 } +#define PORTA_U3_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 2, 0 } +#define PORTA_U3_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHD, USB3A, USB3AXHD, PORTA_U3_XHD_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHPD, USB3A, USB3AXHPD, PORTA_U3_XHD_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH, USB3AAP, USB3AXH, PORTA_U3_XH_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP, USB3AAP, USB3AXHP, PORTA_U3_XH_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH2B, USB3ABP, USB3AXH2B, PORTA_U3_XH2E_DESC, + SIG_DESC_SET(SCU410, 9)); +SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP2B, USB3ABP, USB3AXHP2B, PORTA_U3_XH2E_DESC, + SIG_DESC_CLEAR(SCU410, 9)); +PIN_DECL_(PORTA_U3, SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHD), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHPD), + SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP), + SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH2B), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP2B)); + +#define PORTB_U3_XHD_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 0, 0 } +#define PORTB_U3_XH_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 2, 0 } +#define PORTB_U3_XH2E_DESC { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 3, 0 } + +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHD, USB3B, USB3BXHD, PORTB_U3_XHD_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHPD, USB3B, USB3BXHPD, PORTB_U3_XHD_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH, USB3BBP, USB3BXH, PORTB_U3_XH_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP, USB3BBP, USB3BXHP, PORTB_U3_XH_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH2A, USB3BAP, USB3BXH2A, PORTB_U3_XH2E_DESC, + SIG_DESC_SET(SCU410, 10)); +SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP2A, USB3BAP, USB3BXHP2A, PORTB_U3_XH2E_DESC, + SIG_DESC_CLEAR(SCU410, 10)); +PIN_DECL_(PORTB_U3, SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHD), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHPD), + SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP), + SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH2A), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP2A)); + +/* + * USB3 virtual PHY pins. + * + * PORTA_U3_PHY and PORTB_U3_PHY are logical endpoints, not package pins. + * They alias existing USB3 expressions so pin groups can model both direct and + * cross-coupled routing to PHY A/B. + * + * - USB3AAP: PORTA_U3 + PORTA_U3_PHY (A -> PHY A) + * - USB3ABP: PORTA_U3 + PORTB_U3_PHY (A -> PHY B) + * - USB3BBP: PORTB_U3 + PORTB_U3_PHY (B -> PHY B) + * - USB3BAP: PORTB_U3 + PORTA_U3_PHY (B -> PHY A) + * + * They do not have any registers to configure this behavior; the goal is + * simply for the driver to prevent conflicting selections. For example, + * selecting group USB3ABP and USB3BBP at the same time should not be + * allowed. + */ +SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXH, USB3AAP); +SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXHP, USB3AAP); +SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXH2A, USB3BAP); +SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXHP2A, USB3BAP); +PIN_DECL_(PORTA_U3_PHY, SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXH), + SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXHP), SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXH2A), + SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXHP2A)); + +SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXH2B, USB3ABP); +SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXHP2B, USB3ABP); +SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXH, USB3BBP); +SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXHP, USB3BBP); +PIN_DECL_(PORTB_U3_PHY, SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXH2B), + SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXH), + SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXHP)); + +/* USB3A xHCI to vHUB */ +GROUP_DECL(USB3A, PORTA_U3); +/* USB3A xHCI to USB3A PHY */ +GROUP_DECL(USB3AAP, PORTA_U3, PORTA_U3_PHY); +/* USB3A xHCI to USB3B PHY */ +GROUP_DECL(USB3ABP, PORTA_U3, PORTB_U3_PHY); + +FUNC_DECL_1(USB3AXHD, USB3A); +FUNC_DECL_1(USB3AXHPD, USB3A); +FUNC_DECL_1(USB3AXH, USB3AAP); +FUNC_DECL_1(USB3AXHP, USB3AAP); +FUNC_DECL_1(USB3AXH2B, USB3ABP); +FUNC_DECL_1(USB3AXHP2B, USB3ABP); + +/* USB3B xHCI to vHUB */ +GROUP_DECL(USB3B, PORTB_U3); +/* USB3B xHCI to USB3A PHY */ +GROUP_DECL(USB3BAP, PORTB_U3, PORTA_U3_PHY); +/* USB3B xHCI to USB3B PHY */ +GROUP_DECL(USB3BBP, PORTB_U3, PORTB_U3_PHY); + +FUNC_DECL_1(USB3BXHD, USB3B); +FUNC_DECL_1(USB3BXHPD, USB3B); +FUNC_DECL_1(USB3BXH, USB3BBP); +FUNC_DECL_1(USB3BXHP, USB3BBP); +FUNC_DECL_1(USB3BXH2A, USB3BAP); +FUNC_DECL_1(USB3BXHP2A, USB3BAP); + +static const struct pinctrl_pin_desc aspeed_g7_soc0_pins[] = { + ASPEED_PINCTRL_PIN(AC14), + ASPEED_PINCTRL_PIN(AE15), + ASPEED_PINCTRL_PIN(AD14), + ASPEED_PINCTRL_PIN(AE14), + ASPEED_PINCTRL_PIN(AF14), + ASPEED_PINCTRL_PIN(AB13), + ASPEED_PINCTRL_PIN(AB14), + ASPEED_PINCTRL_PIN(AF15), + ASPEED_PINCTRL_PIN(AF13), + ASPEED_PINCTRL_PIN(AC13), + ASPEED_PINCTRL_PIN(AD13), + ASPEED_PINCTRL_PIN(AE13), + ASPEED_PINCTRL_PIN(JTAG_PORT), + ASPEED_PINCTRL_PIN(PCIERC0_PERST), + ASPEED_PINCTRL_PIN(PCIERC1_PERST), + ASPEED_PINCTRL_PIN(PORTA_MODE), + ASPEED_PINCTRL_PIN(PORTA_U2), + ASPEED_PINCTRL_PIN(PORTA_U3), + ASPEED_PINCTRL_PIN(PORTA_U2_PHY), + ASPEED_PINCTRL_PIN(PORTA_U3_PHY), + ASPEED_PINCTRL_PIN(PORTB_MODE), + ASPEED_PINCTRL_PIN(PORTB_U2), + ASPEED_PINCTRL_PIN(PORTB_U3), + ASPEED_PINCTRL_PIN(PORTB_U2_PHY), + ASPEED_PINCTRL_PIN(PORTB_U3_PHY), +}; + +static const struct aspeed_pin_group aspeed_g7_soc0_groups[] = { + ASPEED_PINCTRL_GROUP(EMMCCDN), + ASPEED_PINCTRL_GROUP(EMMCG1), + ASPEED_PINCTRL_GROUP(EMMCG4), + ASPEED_PINCTRL_GROUP(EMMCG8), + ASPEED_PINCTRL_GROUP(EMMCWPN), + ASPEED_PINCTRL_GROUP(TSPRSTN), + ASPEED_PINCTRL_GROUP(UFSCLKI), + ASPEED_PINCTRL_GROUP(VB0), + ASPEED_PINCTRL_GROUP(VB1), + ASPEED_PINCTRL_GROUP(VGADDC), + /* JTAG groups */ + ASPEED_PINCTRL_GROUP(JTAG0), + /* PCIE RC groups */ + ASPEED_PINCTRL_GROUP(PCIERC0PERST), + ASPEED_PINCTRL_GROUP(PCIERC1PERST), + /* USB3A groups */ + ASPEED_PINCTRL_GROUP(USB3A), + ASPEED_PINCTRL_GROUP(USB3AAP), + ASPEED_PINCTRL_GROUP(USB3ABP), + /* USB3B groups */ + ASPEED_PINCTRL_GROUP(USB3B), + ASPEED_PINCTRL_GROUP(USB3BAP), + ASPEED_PINCTRL_GROUP(USB3BBP), + /* USB2A groups */ + ASPEED_PINCTRL_GROUP(USB2A), + ASPEED_PINCTRL_GROUP(USB2AAP), + ASPEED_PINCTRL_GROUP(USB2ABP), + ASPEED_PINCTRL_GROUP(USB2ADAP), + ASPEED_PINCTRL_GROUP(USB2AH), + ASPEED_PINCTRL_GROUP(USB2AHAP), + /* USB2B groups */ + ASPEED_PINCTRL_GROUP(USB2B), + ASPEED_PINCTRL_GROUP(USB2BAP), + ASPEED_PINCTRL_GROUP(USB2BBP), + ASPEED_PINCTRL_GROUP(USB2BDBP), + ASPEED_PINCTRL_GROUP(USB2BH), + ASPEED_PINCTRL_GROUP(USB2BHBP), +}; + +static const struct aspeed_pin_function aspeed_g7_soc0_functions[] = { + ASPEED_PINCTRL_FUNC(EMMC), + ASPEED_PINCTRL_FUNC(TSPRSTN), + ASPEED_PINCTRL_FUNC(UFSCLKI), + ASPEED_PINCTRL_FUNC(VB), + ASPEED_PINCTRL_FUNC(VGADDC), + /* JTAG functions */ + ASPEED_PINCTRL_FUNC(JTAGDDR), + ASPEED_PINCTRL_FUNC(JTAGM0), + ASPEED_PINCTRL_FUNC(JTAGPCIEA), + ASPEED_PINCTRL_FUNC(JTAGPCIEB), + ASPEED_PINCTRL_FUNC(JTAGPSP), + ASPEED_PINCTRL_FUNC(JTAGSSP), + ASPEED_PINCTRL_FUNC(JTAGTSP), + ASPEED_PINCTRL_FUNC(JTAGUSB3A), + ASPEED_PINCTRL_FUNC(JTAGUSB3B), + /* PCIE RC functions */ + ASPEED_PINCTRL_FUNC(PCIERC0PERST), + ASPEED_PINCTRL_FUNC(PCIERC1PERST), + /* USB3A functions */ + ASPEED_PINCTRL_FUNC(USB3AXH), + ASPEED_PINCTRL_FUNC(USB3AXH2B), + ASPEED_PINCTRL_FUNC(USB3AXHD), + ASPEED_PINCTRL_FUNC(USB3AXHP), + ASPEED_PINCTRL_FUNC(USB3AXHP2B), + ASPEED_PINCTRL_FUNC(USB3AXHPD), + /* USB3B functions */ + ASPEED_PINCTRL_FUNC(USB3BXH), + ASPEED_PINCTRL_FUNC(USB3BXH2A), + ASPEED_PINCTRL_FUNC(USB3BXHD), + ASPEED_PINCTRL_FUNC(USB3BXHP), + ASPEED_PINCTRL_FUNC(USB3BXHP2A), + ASPEED_PINCTRL_FUNC(USB3BXHPD), + /* USB2A functions */ + ASPEED_PINCTRL_FUNC(USB2AD0), + ASPEED_PINCTRL_FUNC(USB2AD1), + ASPEED_PINCTRL_FUNC(USB2AH), + ASPEED_PINCTRL_FUNC(USB2AHP), + ASPEED_PINCTRL_FUNC(USB2AHPD0), + ASPEED_PINCTRL_FUNC(USB2AXH), + ASPEED_PINCTRL_FUNC(USB2AXH2B), + ASPEED_PINCTRL_FUNC(USB2AXHD1), + ASPEED_PINCTRL_FUNC(USB2AXHP), + ASPEED_PINCTRL_FUNC(USB2AXHP2B), + ASPEED_PINCTRL_FUNC(USB2AXHPD1), + /* USB2B functions */ + ASPEED_PINCTRL_FUNC(USB2BD0), + ASPEED_PINCTRL_FUNC(USB2BD1), + ASPEED_PINCTRL_FUNC(USB2BH), + ASPEED_PINCTRL_FUNC(USB2BHP), + ASPEED_PINCTRL_FUNC(USB2BHPD0), + ASPEED_PINCTRL_FUNC(USB2BXH), + ASPEED_PINCTRL_FUNC(USB2BXH2A), + ASPEED_PINCTRL_FUNC(USB2BXHD1), + ASPEED_PINCTRL_FUNC(USB2BXHP), + ASPEED_PINCTRL_FUNC(USB2BXHP2A), + ASPEED_PINCTRL_FUNC(USB2BXHPD1), +}; + +static const struct pinmux_ops aspeed_g7_soc0_pinmux_ops = { + .get_functions_count = aspeed_pinmux_get_fn_count, + .get_function_name = aspeed_pinmux_get_fn_name, + .get_function_groups = aspeed_pinmux_get_fn_groups, + .set_mux = aspeed_pinmux_set_mux, + .gpio_request_enable = aspeed_gpio_request_enable, + .strict = true, +}; + +static const struct pinctrl_ops aspeed_g7_soc0_pinctrl_ops = { + .get_groups_count = aspeed_pinctrl_get_groups_count, + .get_group_name = aspeed_pinctrl_get_group_name, + .get_group_pins = aspeed_pinctrl_get_group_pins, + .pin_dbg_show = aspeed_pinctrl_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_free_map, +}; + +static const struct pinconf_ops aspeed_g7_soc0_pinconf_ops = { + .is_generic = true, + .pin_config_get = aspeed_pin_config_get, + .pin_config_set = aspeed_pin_config_set, + .pin_config_group_get = aspeed_pin_config_group_get, + .pin_config_group_set = aspeed_pin_config_group_set, +}; + +/* pinctrl_desc */ +static const struct pinctrl_desc aspeed_g7_soc0_pinctrl_desc = { + .name = "aspeed-g7-soc0-pinctrl", + .pins = aspeed_g7_soc0_pins, + .npins = ARRAY_SIZE(aspeed_g7_soc0_pins), + .pctlops = &aspeed_g7_soc0_pinctrl_ops, + .pmxops = &aspeed_g7_soc0_pinmux_ops, + .confops = &aspeed_g7_soc0_pinconf_ops, +}; + +static const struct aspeed_pin_config aspeed_g7_soc0_configs[] = { + /* GPIO18A */ + { PIN_CONFIG_DRIVE_STRENGTH, { AC14, AC14 }, SCU480, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AC14, AC14 }, SCU480, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AC14, AC14 }, SCU480, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AC14, AC14 }, SCU480, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AE15, AE15 }, SCU484, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AE15, AE15 }, SCU484, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AE15, AE15 }, SCU484, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AE15, AE15 }, SCU484, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AD14, AD14 }, SCU488, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AD14, AD14 }, SCU488, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AD14, AD14 }, SCU488, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AD14, AD14 }, SCU488, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AE14, AE14 }, SCU48C, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AE14, AE14 }, SCU48C, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AE14, AE14 }, SCU48C, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AE14, AE14 }, SCU48C, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AF14, AF14 }, SCU490, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AF14, AF14 }, SCU490, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AF14, AF14 }, SCU490, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AF14, AF14 }, SCU490, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AB13, AB13 }, SCU494, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AB13, AB13 }, SCU494, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AB13, AB13 }, SCU494, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AB13, AB13 }, SCU494, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AB14, AB14 }, SCU498, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AB14, AB14 }, SCU498, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AB14, AB14 }, SCU498, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AB14, AB14 }, SCU498, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AF15, AF15 }, SCU49C, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AF15, AF15 }, SCU49C, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AF15, AF15 }, SCU49C, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AF15, AF15 }, SCU49C, BIT(5) }, + /* GPIO18B */ + { PIN_CONFIG_DRIVE_STRENGTH, { AF13, AF13 }, SCU4A0, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AF13, AF13 }, SCU4A0, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AF13, AF13 }, SCU4A0, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AF13, AF13 }, SCU4A0, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AC13, AC13 }, SCU4A4, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AC13, AC13 }, SCU4A4, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AC13, AC13 }, SCU4A4, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AC13, AC13 }, SCU4A4, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AD13, AD13 }, SCU4A8, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AD13, AD13 }, SCU4A8, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AD13, AD13 }, SCU4A8, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AD13, AD13 }, SCU4A8, BIT(5) }, + { PIN_CONFIG_DRIVE_STRENGTH, { AE13, AE13 }, SCU4AC, GENMASK(3, 0) }, + { PIN_CONFIG_BIAS_PULL_DOWN, { AE13, AE13 }, SCU4AC, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_PULL_UP, { AE13, AE13 }, SCU4AC, GENMASK(5, 4) }, + { PIN_CONFIG_BIAS_DISABLE, { AE13, AE13 }, SCU4AC, BIT(5) }, +}; + +static const struct aspeed_pin_config_map aspeed_g7_soc0_pin_config_map[] = { + { PIN_CONFIG_BIAS_PULL_DOWN, -1, 2, GENMASK(1, 0) }, + { PIN_CONFIG_BIAS_PULL_UP, -1, 3, GENMASK(1, 0) }, + { PIN_CONFIG_BIAS_DISABLE, -1, 0, BIT_MASK(0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 3, 0, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 6, 1, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 8, 2, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 11, 3, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 16, 4, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 18, 5, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 20, 6, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 23, 7, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 30, 8, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 32, 9, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 33, 10, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 35, 11, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 37, 12, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 38, 13, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 39, 14, GENMASK(3, 0) }, + { PIN_CONFIG_DRIVE_STRENGTH, 41, 15, GENMASK(3, 0) }, + +}; + +static int aspeed_g7_soc0_sig_expr_set(struct aspeed_pinmux_data *ctx, + const struct aspeed_sig_expr *expr, bool enable) +{ + int ret; + int i; + + for (i = 0; i < expr->ndescs; i++) { + const struct aspeed_sig_desc *desc = &expr->descs[i]; + u32 pattern = enable ? desc->enable : desc->disable; + u32 val = (pattern << __ffs(desc->mask)); + + if (!ctx->maps[desc->ip]) + return -ENODEV; + + WARN_ON_ONCE(desc->ip != ASPEED_IP_SCU); + + ret = regmap_update_bits(ctx->maps[desc->ip], desc->reg, + desc->mask, val); + if (ret) + return ret; + } + + ret = aspeed_sig_expr_eval(ctx, expr, enable); + if (ret < 0) + return ret; + + return ret ? 0 : -EPERM; +} + +static const struct aspeed_pinmux_ops aspeed_g7_soc0_ops = { + .set = aspeed_g7_soc0_sig_expr_set, +}; + +static struct aspeed_pinctrl_data aspeed_g7_soc0_pinctrl_data = { + .pins = aspeed_g7_soc0_pins, + .npins = ARRAY_SIZE(aspeed_g7_soc0_pins), + .pinmux = { + .ops = &aspeed_g7_soc0_ops, + .groups = aspeed_g7_soc0_groups, + .ngroups = ARRAY_SIZE(aspeed_g7_soc0_groups), + .functions = aspeed_g7_soc0_functions, + .nfunctions = ARRAY_SIZE(aspeed_g7_soc0_functions), + }, + .configs = aspeed_g7_soc0_configs, + .nconfigs = ARRAY_SIZE(aspeed_g7_soc0_configs), + .confmaps = aspeed_g7_soc0_pin_config_map, + .nconfmaps = ARRAY_SIZE(aspeed_g7_soc0_pin_config_map), +}; + +static int aspeed_g7_soc0_pinctrl_probe(struct platform_device *pdev) +{ + return aspeed_pinctrl_probe(pdev, &aspeed_g7_soc0_pinctrl_desc, + &aspeed_g7_soc0_pinctrl_data); +} + +static const struct of_device_id aspeed_g7_soc0_pinctrl_match[] = { + { .compatible = "aspeed,ast2700-soc0-pinctrl" }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_g7_soc0_pinctrl_match); + +static struct platform_driver aspeed_g7_soc0_pinctrl_driver = { + .probe = aspeed_g7_soc0_pinctrl_probe, + .driver = { + .name = "aspeed-g7-soc0-pinctrl", + .of_match_table = aspeed_g7_soc0_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init aspeed_g7_soc0_pinctrl_init(void) +{ + return platform_driver_register(&aspeed_g7_soc0_pinctrl_driver); +} +arch_initcall(aspeed_g7_soc0_pinctrl_init); From 4bc69ca4478e6296e92cd0a37f1a7bdfdb8432d4 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Tue, 5 May 2026 14:24:12 +0200 Subject: [PATCH 0840/5214] pinctrl: qcom: Fix typo STRENGH -> STRENGTH Signed-off-by: Thomas Weber Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index 5c966d51eda7..c42dd7c736fe 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -53,7 +53,7 @@ #define PM8XXX_GPIO_PHYSICAL_OFFSET 1 /* custom pinconf parameters */ -#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) +#define PM8XXX_QCOM_DRIVE_STRENGTH (PIN_CONFIG_END + 1) #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) /** @@ -97,13 +97,13 @@ struct pm8xxx_gpio { }; static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { - {"qcom,drive-strength", PM8XXX_QCOM_DRIVE_STRENGH, 0}, + {"qcom,drive-strength", PM8XXX_QCOM_DRIVE_STRENGTH, 0}, {"qcom,pull-up-strength", PM8XXX_QCOM_PULL_UP_STRENGTH, 0}, }; #ifdef CONFIG_DEBUG_FS static const struct pin_config_item pm8xxx_conf_items[ARRAY_SIZE(pm8xxx_gpio_bindings)] = { - PCONFDUMP(PM8XXX_QCOM_DRIVE_STRENGH, "drive-strength", NULL, true), + PCONFDUMP(PM8XXX_QCOM_DRIVE_STRENGTH, "drive-strength", NULL, true), PCONFDUMP(PM8XXX_QCOM_PULL_UP_STRENGTH, "pull up strength", NULL, true), }; #endif @@ -291,7 +291,7 @@ static int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev, case PIN_CONFIG_POWER_SOURCE: arg = pin->power_source; break; - case PM8XXX_QCOM_DRIVE_STRENGH: + case PM8XXX_QCOM_DRIVE_STRENGTH: arg = pin->output_strength; break; case PIN_CONFIG_DRIVE_PUSH_PULL: @@ -373,7 +373,7 @@ static int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev, pin->power_source = arg; banks |= BIT(0); break; - case PM8XXX_QCOM_DRIVE_STRENGH: + case PM8XXX_QCOM_DRIVE_STRENGTH: if (arg > PMIC_GPIO_STRENGTH_LOW) { dev_err(pctrl->dev, "invalid drive strength\n"); return -EINVAL; From b66b2bd3a3e827c6ba0d074ef637f9f260604e2b Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Tue, 5 May 2026 14:24:43 +0200 Subject: [PATCH 0841/5214] pinctrl: realtek: Fix typo STRENGH -> STRENGTH Signed-off-by: Thomas Weber Signed-off-by: Linus Walleij --- drivers/pinctrl/realtek/pinctrl-rtd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/realtek/pinctrl-rtd.c b/drivers/pinctrl/realtek/pinctrl-rtd.c index a2c672508a4b..60222339726b 100644 --- a/drivers/pinctrl/realtek/pinctrl-rtd.c +++ b/drivers/pinctrl/realtek/pinctrl-rtd.c @@ -34,14 +34,14 @@ struct rtd_pinctrl { }; /* custom pinconf parameters */ -#define RTD_DRIVE_STRENGH_P (PIN_CONFIG_END + 1) -#define RTD_DRIVE_STRENGH_N (PIN_CONFIG_END + 2) +#define RTD_DRIVE_STRENGTH_P (PIN_CONFIG_END + 1) +#define RTD_DRIVE_STRENGTH_N (PIN_CONFIG_END + 2) #define RTD_DUTY_CYCLE (PIN_CONFIG_END + 3) #define RTD_HIGH_VIL (PIN_CONFIG_END + 4) static const struct pinconf_generic_params rtd_custom_bindings[] = { - {"realtek,drive-strength-p", RTD_DRIVE_STRENGH_P, 0}, - {"realtek,drive-strength-n", RTD_DRIVE_STRENGH_N, 0}, + {"realtek,drive-strength-p", RTD_DRIVE_STRENGTH_P, 0}, + {"realtek,drive-strength-n", RTD_DRIVE_STRENGTH_N, 0}, {"realtek,duty-cycle", RTD_DUTY_CYCLE, 0}, {"realtek,high-vil-microvolt", RTD_HIGH_VIL, 0}, }; @@ -473,7 +473,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, val = 1; break; - case RTD_DRIVE_STRENGH_P: + case RTD_DRIVE_STRENGTH_P: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc) { dev_err(data->dev, "P driving unsupported for pin: %s\n", name); @@ -490,7 +490,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, val = set_val << p_off; break; - case RTD_DRIVE_STRENGH_N: + case RTD_DRIVE_STRENGTH_N: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc) { dev_err(data->dev, "N driving unsupported for pin: %s\n", name); From 37193e5b112d0685d9caa562cd01837a7ba3d11d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 4 May 2026 15:27:47 +0800 Subject: [PATCH 0842/5214] dt-bindings: pinctrl: mediatek: mt8188: allow gpio hogs Add gpio hogs subnode rules to the MT8188 pinctrl binding. Signed-off-by: Icenowy Zheng Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml index e994b0c70dbf..1cf06e46f7bb 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml @@ -67,6 +67,11 @@ properties: # PIN CONFIGURATION NODES patternProperties: + "-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + '-pins$': type: object additionalProperties: false From 0942cdb8f4b14cbfdc1996c2c332c6402a203bf3 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Mon, 20 Apr 2026 22:30:03 +0100 Subject: [PATCH 0843/5214] dt-bindings: pinctrl: mediatek,mt65xx: Add MT6392 pinctrl Add a compatible for the pinctrl device of the MT6392 PMIC, a variant of the already supported MT6397. Signed-off-by: Luca Leonardo Scorcia Reviewed-by: AngeloGioacchino Del Regno Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij --- .../pinctrl/mediatek,mt65xx-pinctrl.yaml | 1 + .../pinctrl/mediatek,mt6392-pinfunc.h | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 include/dt-bindings/pinctrl/mediatek,mt6392-pinfunc.h diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt65xx-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt65xx-pinctrl.yaml index aa71398cf522..1468c6f87cfa 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt65xx-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt65xx-pinctrl.yaml @@ -17,6 +17,7 @@ properties: enum: - mediatek,mt2701-pinctrl - mediatek,mt2712-pinctrl + - mediatek,mt6392-pinctrl - mediatek,mt6397-pinctrl - mediatek,mt7623-pinctrl - mediatek,mt8127-pinctrl diff --git a/include/dt-bindings/pinctrl/mediatek,mt6392-pinfunc.h b/include/dt-bindings/pinctrl/mediatek,mt6392-pinfunc.h new file mode 100644 index 000000000000..c65278c8103d --- /dev/null +++ b/include/dt-bindings/pinctrl/mediatek,mt6392-pinfunc.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +#ifndef __DTS_MT6392_PINFUNC_H +#define __DTS_MT6392_PINFUNC_H + +#include + +#define MT6392_PIN_0_INT__FUNC_GPIO0 (MTK_PIN_NO(0) | 0) +#define MT6392_PIN_0_INT__FUNC_INT (MTK_PIN_NO(0) | 1) +#define MT6392_PIN_0_INT__FUNC_TEST_CK2 (MTK_PIN_NO(0) | 5) +#define MT6392_PIN_0_INT__FUNC_TEST_IN1 (MTK_PIN_NO(0) | 6) +#define MT6392_PIN_0_INT__FUNC_TEST_OUT1 (MTK_PIN_NO(0) | 7) + +#define MT6392_PIN_1_SRCLKEN__FUNC_GPIO1 (MTK_PIN_NO(1) | 0) +#define MT6392_PIN_1_SRCLKEN__FUNC_SRCLKEN (MTK_PIN_NO(1) | 1) +#define MT6392_PIN_1_SRCLKEN__FUNC_TEST_CK0 (MTK_PIN_NO(1) | 5) +#define MT6392_PIN_1_SRCLKEN__FUNC_TEST_IN2 (MTK_PIN_NO(1) | 6) +#define MT6392_PIN_1_SRCLKEN__FUNC_TEST_OUT2 (MTK_PIN_NO(1) | 7) + +#define MT6392_PIN_2_RTC_32K1V8__FUNC_GPIO2 (MTK_PIN_NO(2) | 0) +#define MT6392_PIN_2_RTC_32K1V8__FUNC_RTC_32K1V8 (MTK_PIN_NO(2) | 1) +#define MT6392_PIN_2_RTC_32K1V8__FUNC_TEST_CK1 (MTK_PIN_NO(2) | 5) +#define MT6392_PIN_2_RTC_32K1V8__FUNC_TEST_IN3 (MTK_PIN_NO(2) | 6) +#define MT6392_PIN_2_RTC_32K1V8__FUNC_TEST_OUT3 (MTK_PIN_NO(2) | 7) + +#define MT6392_PIN_3_SPI_CLK__FUNC_GPIO3 (MTK_PIN_NO(3) | 0) +#define MT6392_PIN_3_SPI_CLK__FUNC_SPI_CLK (MTK_PIN_NO(3) | 1) + +#define MT6392_PIN_4_SPI_CSN__FUNC_GPIO4 (MTK_PIN_NO(4) | 0) +#define MT6392_PIN_4_SPI_CSN__FUNC_SPI_CSN (MTK_PIN_NO(4) | 1) + +#define MT6392_PIN_5_SPI_MOSI__FUNC_GPIO5 (MTK_PIN_NO(5) | 0) +#define MT6392_PIN_5_SPI_MOSI__FUNC_SPI_MOSI (MTK_PIN_NO(5) | 1) + +#define MT6392_PIN_6_SPI_MISO__FUNC_GPIO6 (MTK_PIN_NO(6) | 0) +#define MT6392_PIN_6_SPI_MISO__FUNC_SPI_MISO (MTK_PIN_NO(6) | 1) +#define MT6392_PIN_6_SPI_MISO__FUNC_TEST_IN4 (MTK_PIN_NO(6) | 6) +#define MT6392_PIN_6_SPI_MISO__FUNC_TEST_OUT4 (MTK_PIN_NO(6) | 7) + +#endif /* __DTS_MT6392_PINFUNC_H */ From d8074fd57a0231457e580c1f6cd33f089f09fd12 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 7 May 2026 11:21:14 -0400 Subject: [PATCH 0844/5214] mux: describe np parameter in __devm_mux_state_get() Add a description for the 'np' parameter of __devm_mux_state_get() to fix build warning. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605061502.ullLjmtN-lkp@intel.com/ Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/mux/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 2f01acfccf47..5083e3d19606 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -771,6 +771,7 @@ static void devm_mux_state_release(struct device *dev, void *res) * @dev: The device that needs a mux-state. * @mux_name: The name identifying the mux-state. * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @np: The device nodes, use dev->of_node if it is NULL. * @init: Optional function pointer for mux-state object initialisation. * @exit: Optional function pointer for mux-state object cleanup on release. * From 76fc060df67c7d3c0ca9613ebcc1e5c257c325d9 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Thu, 7 May 2026 22:38:28 +0530 Subject: [PATCH 0845/5214] clk: qcom: add Global Clock controller (GCC) driver for IPQ9650 SoC Add support for the global clock controller found on IPQ9650 SoC. Signed-off-by: Kathiravan Thirumoorthy Link: https://lore.kernel.org/r/20260507-ipq9650_boot_to_shell-v3-2-62742b49c991@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Kconfig | 10 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-ipq9650.c | 3445 ++++++++++++++++++++++++++++++++ 3 files changed, 3456 insertions(+) create mode 100644 drivers/clk/qcom/gcc-ipq9650.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index df21ef5ffd68..9573e88d1f25 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -434,6 +434,16 @@ config IPQ_GCC_9574 i2c, USB, SD/eMMC, etc. Select this for the root clock of ipq9574. +config IPQ_GCC_9650 + tristate "IPQ9650 Global Clock Controller" + depends on ARM64 || COMPILE_TEST + default ARCH_QCOM + help + Support for global clock controller on ipq9650 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, etc. Select this for the root clock + of ipq9650. + config IPQ_NSSCC_5424 tristate "IPQ5424 NSS Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 462c7615a6d7..46c997fa59ca 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_GCC_8074) += gcc-ipq8074.o obj-$(CONFIG_IPQ_GCC_9574) += gcc-ipq9574.o +obj-$(CONFIG_IPQ_GCC_9650) += gcc-ipq9650.o obj-$(CONFIG_IPQ_NSSCC_5424) += nsscc-ipq5424.o obj-$(CONFIG_IPQ_NSSCC_9574) += nsscc-ipq9574.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o diff --git a/drivers/clk/qcom/gcc-ipq9650.c b/drivers/clk/qcom/gcc-ipq9650.c new file mode 100644 index 000000000000..c556c2bbfd96 --- /dev/null +++ b/drivers/clk/qcom/gcc-ipq9650.c @@ -0,0 +1,3445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "reset.h" + +enum { + DT_XO, + DT_SLEEP_CLK, + DT_PCIE30_PHY0_PIPE_CLK, + DT_PCIE30_PHY1_PIPE_CLK, + DT_PCIE30_PHY2_PIPE_CLK, + DT_PCIE30_PHY3_PIPE_CLK, + DT_PCIE30_PHY4_PIPE_CLK, + DT_USB3_PHY0_CC_PIPE_CLK, + DT_NSS_CMN_CLK, +}; + +enum { + P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_ODD, + P_GPLL2_OUT_AUX, + P_GPLL2_OUT_MAIN, + P_GPLL4_OUT_MAIN, + P_GPLL4_OUT_ODD, + P_NSS_CMN_CLK, + P_SLEEP_CLK, + P_XO, +}; + +static const struct clk_parent_data gcc_parent_data_xo = { .index = DT_XO }; + +static struct clk_alpha_pll gpll0_main = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_main", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ops, + }, + }, +}; + +static struct clk_fixed_factor gpll0_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_div2", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_alpha_pll_postdiv gpll0 = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll0", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll2 = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gpll2", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_zonda_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll2_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll2_out_main = { + .offset = 0x21000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll2_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll2_out_main), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll2_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll2.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_zonda_ops, + }, +}; + +static struct clk_alpha_pll gpll4 = { + .offset = 0x22000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gpll4", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + /* + * There are no consumers for this GPLL in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + .ops = &clk_alpha_pll_fixed_lucid_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_xo[] = { + { P_XO, 0 }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_XO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_XO }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_MAIN, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_XO }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_AUX, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_ODD, 1 }, + { P_GPLL0_OUT_MAIN, 3 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_XO }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_XO, 0 }, + { P_NSS_CMN_CLK, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_GPLL2_OUT_AUX, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_XO }, + { .index = DT_NSS_CMN_CLK }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_9[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_9[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2_out_main.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_10[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_11[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_11[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_12[] = { + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_12[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_13[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct freq_tbl ftbl_gcc_adss_pwm_clk_src[] = { + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_adss_pwm_clk_src = { + .cmd_rcgr = 0x1c004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_gemnoc_anoc_pcie_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_nss_ts_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_xo_clk_src = { + .cmd_rcgr = 0x34004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_xo, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_clk_src", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_xo_div4_clk_src = { + .mult = 1, + .div = 4, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_div4_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_xo_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_rcg2 gcc_nss_ts_clk_src = { + .cmd_rcgr = 0x17088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src[] = { + F(462000000, P_NSS_CMN_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_nssnoc_memnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x17004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_system_noc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_system_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x2e004, + .freq_tbl = ftbl_gcc_system_noc_bfdcd_clk_src, + .hid_width = 5, + .parent_map = gcc_parent_map_13, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_system_noc_bfdcd_clk_src", + .parent_data = gcc_parent_data_13, + .num_parents = ARRAY_SIZE(gcc_parent_data_13), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcnoc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x31004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + /* + * There are no consumers for this source in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + .ops = &clk_rcg2_ops, + }, +}; +static const struct freq_tbl ftbl_gcc_pcie0_axi_m_clk_src[] = { + F(200000000, P_GPLL4_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie0_axi_m_clk_src = { + .cmd_rcgr = 0x28018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_axi_s_clk_src = { + .cmd_rcgr = 0x28020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_rchng_clk_src = { + .cmd_rcgr = 0x28028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie1_axi_m_clk_src[] = { + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie1_axi_m_clk_src = { + .cmd_rcgr = 0x29018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_axi_s_clk_src = { + .cmd_rcgr = 0x29020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_rchng_clk_src = { + .cmd_rcgr = 0x29028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_axi_m_clk_src = { + .cmd_rcgr = 0x2a018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_axi_s_clk_src = { + .cmd_rcgr = 0x2a020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_rchng_clk_src = { + .cmd_rcgr = 0x2a028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_axi_m_clk_src = { + .cmd_rcgr = 0x2b018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_axi_s_clk_src = { + .cmd_rcgr = 0x2b020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_rchng_clk_src = { + .cmd_rcgr = 0x2b028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_axi_m_clk_src = { + .cmd_rcgr = 0x25004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_axi_s_clk_src = { + .cmd_rcgr = 0x2500c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_rchng_clk_src = { + .cmd_rcgr = 0x25014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_aux_clk_src[] = { + F(20000000, P_GPLL0_OUT_MAIN, 10, 1, 4), + { } +}; + +static struct clk_rcg2 gcc_pcie_aux_clk_src = { + .cmd_rcgr = 0x28004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_pcie_aux_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_at_clk_src[] = { + F(240000000, P_GPLL4_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_at_clk_src = { + .cmd_rcgr = 0x2d004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qdss_at_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_eud_at_div_clk_src = { + .mult = 1, + .div = 6, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eud_at_div_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_qdss_at_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_tsctr_clk_src[] = { + F(600000000, P_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_tsctr_clk_src = { + .cmd_rcgr = 0x2d01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qdss_tsctr_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_tsctr_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_qdss_dap_sync_clk_src = { + .mult = 1, + .div = 4, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_dap_sync_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_qdss_tsctr_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sleep_clk_src = { + .cmd_rcgr = 0x3400c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sleep_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qpic_io_macro_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(320000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qpic_clk_src = { + .cmd_rcgr = 0x32020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_qpic_io_macro_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qpic_io_macro_clk_src = { + .cmd_rcgr = 0x32004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_qpic_io_macro_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_2x_core_clk_src = { + .cmd_rcgr = 0x100c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_gemnoc_anoc_pcie_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_2x_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap_se0_clk_src[] = { + F(960000, P_XO, 10, 2, 5), + F(3686636, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 2, 217), + F(4800000, P_XO, 5, 0, 0), + F(7373272, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 4, 217), + F(9600000, P_XO, 2.5, 0, 0), + F(14746544, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 8, 217), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 2, 29, 250), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58986175, P_GPLL0_OUT_MAIN, 1, 16, 217), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se0_clk_src = { + .cmd_rcgr = 0x2018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se1_clk_src = { + .cmd_rcgr = 0x3018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se2_clk_src = { + .cmd_rcgr = 0x3034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se3_clk_src = { + .cmd_rcgr = 0x3050, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap_se4_clk_src[] = { + F(960000, P_XO, 10, 2, 5), + F(3686636, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 2, 217), + F(4800000, P_XO, 5, 0, 0), + F(7373272, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 4, 217), + F(9600000, P_XO, 2.5, 0, 0), + F(14746544, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 8, 217), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 2, 29, 250), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58986175, P_GPLL0_OUT_MAIN, 1, 16, 217), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se4_clk_src = { + .cmd_rcgr = 0x306c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se4_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se5_clk_src = { + .cmd_rcgr = 0x3090, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se4_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se6_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se6_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se7_clk_src = { + .cmd_rcgr = 0x4020, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_XO, 16, 12, 125), + F(400000, P_XO, 12, 1, 5), + F(24000000, P_GPLL2_OUT_MAIN, 12, 1, 2), + F(48000000, P_GPLL2_OUT_MAIN, 12, 0, 0), + F(96000000, P_GPLL2_OUT_MAIN, 6, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(192000000, P_GPLL2_OUT_MAIN, 3, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x33004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(300000000, P_GPLL4_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x33018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct clk_rcg2 gcc_uniphy_sys_clk_src = { + .cmd_rcgr = 0x17090, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy_sys_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb0_aux_clk_src = { + .cmd_rcgr = 0x2c018, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb0_master_clk_src = { + .cmd_rcgr = 0x2c004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_gemnoc_anoc_pcie_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb0_mock_utmi_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(60000000, P_GPLL4_OUT_ODD, 10, 1, 2), + { } +}; + +static struct clk_rcg2 gcc_usb0_mock_utmi_clk_src = { + .cmd_rcgr = 0x2c02c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_usb0_mock_utmi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb1_mock_utmi_clk_src = { + .cmd_rcgr = 0x3c004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_usb0_mock_utmi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div gcc_nssnoc_memnoc_div_clk_src = { + .reg = 0x1700c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb0_mock_utmi_div_clk_src = { + .reg = 0x2c040, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb1_mock_utmi_div_clk_src = { + .reg = 0x3c018, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb1_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_adss_pwm_clk = { + .halt_reg = 0x1c00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_adss_pwm_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie0_1lane_m_clk = { + .halt_reg = 0x2e07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie0_1lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie0_1lane_s_clk = { + .halt_reg = 0x2e0cc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0cc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie0_1lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie1_2lane_m_clk = { + .halt_reg = 0x2e084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie1_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie1_2lane_s_clk = { + .halt_reg = 0x2e0d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie1_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie2_2lane_m_clk = { + .halt_reg = 0x2e080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie2_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie2_2lane_s_clk = { + .halt_reg = 0x2e0d4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie2_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie3_2lane_m_clk = { + .halt_reg = 0x2e0bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie3_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie3_2lane_s_clk = { + .halt_reg = 0x2e0d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie3_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie4_1lane_m_clk = { + .halt_reg = 0x2e0c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie4_1lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie4_1lane_s_clk = { + .halt_reg = 0x2e0dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie4_1lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_ahb_clk = { + .halt_reg = 0x3a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_sys_clk = { + .halt_reg = 0x3a008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3a008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdio_ahb_clk = { + .halt_reg = 0x17040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mdio_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nss_ts_clk = { + .halt_reg = 0x17018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nss_ts_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscc_clk = { + .halt_reg = 0x17034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscfg_clk = { + .halt_reg = 0x1702c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscfg_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_atb_clk = { + .halt_reg = 0x17014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_1_clk = { + .halt_reg = 0x17084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_clk = { + .halt_reg = 0x17024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_nsscc_clk = { + .halt_reg = 0x17030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_pcnoc_1_clk = { + .halt_reg = 0x17080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_pcnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_qosgen_ref_clk = { + .halt_reg = 0x1701c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1701c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_qosgen_ref_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_xo_div4_clk_src.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_1_clk = { + .halt_reg = 0x1707c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_clk = { + .halt_reg = 0x17028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_timeout_ref_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_timeout_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_div4_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_xo_dcd_clk = { + .halt_reg = 0x17074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_xo_dcd_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_ahb_clk = { + .halt_reg = 0x28030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_aux_clk = { + .halt_reg = 0x28070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_m_clk = { + .halt_reg = 0x28038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + .halt_reg = 0x28048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_clk = { + .halt_reg = 0x28040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie0_pipe_clk_src = { + .reg = 0x28064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_pipe_clk = { + .halt_reg = 0x28068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x28068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_ahb_clk = { + .halt_reg = 0x29030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_aux_clk = { + .halt_reg = 0x29074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_m_clk = { + .halt_reg = 0x29038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_bridge_clk = { + .halt_reg = 0x29048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_clk = { + .halt_reg = 0x29040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie1_pipe_clk_src = { + .reg = 0x29064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_pipe_clk = { + .halt_reg = 0x29068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x29068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_ahb_clk = { + .halt_reg = 0x2a030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_aux_clk = { + .halt_reg = 0x2a078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_m_clk = { + .halt_reg = 0x2a038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_s_bridge_clk = { + .halt_reg = 0x2a048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_s_clk = { + .halt_reg = 0x2a040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie2_pipe_clk_src = { + .reg = 0x2a064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie2_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY2_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_pipe_clk = { + .halt_reg = 0x2a068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2a068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie2_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_ahb_clk = { + .halt_reg = 0x2b030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_aux_clk = { + .halt_reg = 0x2b07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_m_clk = { + .halt_reg = 0x2b038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_s_bridge_clk = { + .halt_reg = 0x2b048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_s_clk = { + .halt_reg = 0x2b040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie3_pipe_clk_src = { + .reg = 0x2b064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie3_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY3_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_pipe_clk = { + .halt_reg = 0x2b068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2b068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie3_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_ahb_clk = { + .halt_reg = 0x2501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_aux_clk = { + .halt_reg = 0x25020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_m_clk = { + .halt_reg = 0x25028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_s_bridge_clk = { + .halt_reg = 0x25038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_s_clk = { + .halt_reg = 0x25030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie4_pipe_clk_src = { + .reg = 0x25058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie4_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY4_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_pipe_clk = { + .halt_reg = 0x2503c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2503c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie4_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_rchng_clk = { + .halt_reg = 0x28028, + .clkr = { + .enable_reg = 0x28028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_rchng_clk = { + .halt_reg = 0x29028, + .clkr = { + .enable_reg = 0x29028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_rchng_clk = { + .halt_reg = 0x2a028, + .clkr = { + .enable_reg = 0x2a028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie2_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_rchng_clk = { + .halt_reg = 0x2b028, + .clkr = { + .enable_reg = 0x2b028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie3_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_rchng_clk = { + .halt_reg = 0x25014, + .clkr = { + .enable_reg = 0x25014, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie4_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_at_clk = { + .halt_reg = 0x2d034, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2d034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_dap_clk = { + .halt_reg = 0x2d058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_dap_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_dap_sync_clk_src.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_ahb_clk = { + .halt_reg = 0x32010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_clk = { + .halt_reg = 0x32028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_io_macro_clk = { + .halt_reg = 0x3200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3200c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_io_macro_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_sleep_clk = { + .halt_reg = 0x32018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_mst_clk = { + .halt_reg = 0x1014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_mst_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_slv_clk = { + .halt_reg = 0x102c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_slv_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se0_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se1_clk = { + .halt_reg = 0x302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se2_clk = { + .halt_reg = 0x3048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se3_clk = { + .halt_reg = 0x3064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se4_clk = { + .halt_reg = 0x3080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se5_clk = { + .halt_reg = 0x30a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x30a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se6_clk = { + .halt_reg = 0x4018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se7_clk = { + .halt_reg = 0x4034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x3303c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3303c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x3302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x33034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_snoc_usb_clk = { + .halt_reg = 0x2e0c4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0c4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_snoc_usb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_ahb_clk = { + .halt_reg = 0x1704c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1704c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_sys_clk = { + .halt_reg = 0x17048, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_ahb_clk = { + .halt_reg = 0x1705c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1705c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_sys_clk = { + .halt_reg = 0x17058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_ahb_clk = { + .halt_reg = 0x1706c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_sys_clk = { + .halt_reg = 0x17068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_aux_clk = { + .halt_reg = 0x2c04c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_eud_at_clk = { + .halt_reg = 0x30004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x30004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_eud_at_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_eud_at_div_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_master_clk = { + .halt_reg = 0x2c044, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_mock_utmi_clk = { + .halt_reg = 0x2c050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_phy_cfg_ahb_clk = { + .halt_reg = 0x2c05c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c05c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_phy_cfg_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb0_pipe_clk_src = { + .reg = 0x2c074, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_USB3_PHY0_CC_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_pipe_clk = { + .halt_reg = 0x2c054, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2c054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_usb0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_sleep_clk = { + .halt_reg = 0x2c058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_master_clk = { + .halt_reg = 0x3c028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_mock_utmi_clk = { + .halt_reg = 0x3c024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb1_mock_utmi_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_phy_cfg_ahb_clk = { + .halt_reg = 0x3c01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3c01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_phy_cfg_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_sleep_clk = { + .halt_reg = 0x3c020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gcc_ipq9650_clocks[] = { + [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr, + [GCC_ADSS_PWM_CLK_SRC] = &gcc_adss_pwm_clk_src.clkr, + [GCC_ANOC_PCIE0_1LANE_M_CLK] = &gcc_anoc_pcie0_1lane_m_clk.clkr, + [GCC_ANOC_PCIE0_1LANE_S_CLK] = &gcc_anoc_pcie0_1lane_s_clk.clkr, + [GCC_ANOC_PCIE1_2LANE_M_CLK] = &gcc_anoc_pcie1_2lane_m_clk.clkr, + [GCC_ANOC_PCIE1_2LANE_S_CLK] = &gcc_anoc_pcie1_2lane_s_clk.clkr, + [GCC_ANOC_PCIE2_2LANE_M_CLK] = &gcc_anoc_pcie2_2lane_m_clk.clkr, + [GCC_ANOC_PCIE2_2LANE_S_CLK] = &gcc_anoc_pcie2_2lane_s_clk.clkr, + [GCC_ANOC_PCIE3_2LANE_M_CLK] = &gcc_anoc_pcie3_2lane_m_clk.clkr, + [GCC_ANOC_PCIE3_2LANE_S_CLK] = &gcc_anoc_pcie3_2lane_s_clk.clkr, + [GCC_ANOC_PCIE4_1LANE_M_CLK] = &gcc_anoc_pcie4_1lane_m_clk.clkr, + [GCC_ANOC_PCIE4_1LANE_S_CLK] = &gcc_anoc_pcie4_1lane_s_clk.clkr, + [GCC_CMN_12GPLL_AHB_CLK] = &gcc_cmn_12gpll_ahb_clk.clkr, + [GCC_CMN_12GPLL_SYS_CLK] = &gcc_cmn_12gpll_sys_clk.clkr, + [GCC_MDIO_AHB_CLK] = &gcc_mdio_ahb_clk.clkr, + [GCC_NSS_TS_CLK] = &gcc_nss_ts_clk.clkr, + [GCC_NSS_TS_CLK_SRC] = &gcc_nss_ts_clk_src.clkr, + [GCC_NSSCC_CLK] = &gcc_nsscc_clk.clkr, + [GCC_NSSCFG_CLK] = &gcc_nsscfg_clk.clkr, + [GCC_NSSNOC_ATB_CLK] = &gcc_nssnoc_atb_clk.clkr, + [GCC_NSSNOC_MEMNOC_1_CLK] = &gcc_nssnoc_memnoc_1_clk.clkr, + [GCC_NSSNOC_MEMNOC_BFDCD_CLK_SRC] = &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr, + [GCC_NSSNOC_MEMNOC_CLK] = &gcc_nssnoc_memnoc_clk.clkr, + [GCC_NSSNOC_MEMNOC_DIV_CLK_SRC] = &gcc_nssnoc_memnoc_div_clk_src.clkr, + [GCC_NSSNOC_NSSCC_CLK] = &gcc_nssnoc_nsscc_clk.clkr, + [GCC_NSSNOC_PCNOC_1_CLK] = &gcc_nssnoc_pcnoc_1_clk.clkr, + [GCC_NSSNOC_QOSGEN_REF_CLK] = &gcc_nssnoc_qosgen_ref_clk.clkr, + [GCC_NSSNOC_SNOC_1_CLK] = &gcc_nssnoc_snoc_1_clk.clkr, + [GCC_NSSNOC_SNOC_CLK] = &gcc_nssnoc_snoc_clk.clkr, + [GCC_NSSNOC_TIMEOUT_REF_CLK] = &gcc_nssnoc_timeout_ref_clk.clkr, + [GCC_NSSNOC_XO_DCD_CLK] = &gcc_nssnoc_xo_dcd_clk.clkr, + [GCC_PCIE0_AHB_CLK] = &gcc_pcie0_ahb_clk.clkr, + [GCC_PCIE0_AUX_CLK] = &gcc_pcie0_aux_clk.clkr, + [GCC_PCIE0_AXI_M_CLK] = &gcc_pcie0_axi_m_clk.clkr, + [GCC_PCIE0_AXI_M_CLK_SRC] = &gcc_pcie0_axi_m_clk_src.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_PCIE0_AXI_S_CLK] = &gcc_pcie0_axi_s_clk.clkr, + [GCC_PCIE0_AXI_S_CLK_SRC] = &gcc_pcie0_axi_s_clk_src.clkr, + [GCC_PCIE0_PIPE_CLK] = &gcc_pcie0_pipe_clk.clkr, + [GCC_PCIE0_PIPE_CLK_SRC] = &gcc_pcie0_pipe_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK_SRC] = &gcc_pcie0_rchng_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE1_AHB_CLK] = &gcc_pcie1_ahb_clk.clkr, + [GCC_PCIE1_AUX_CLK] = &gcc_pcie1_aux_clk.clkr, + [GCC_PCIE1_AXI_M_CLK] = &gcc_pcie1_axi_m_clk.clkr, + [GCC_PCIE1_AXI_M_CLK_SRC] = &gcc_pcie1_axi_m_clk_src.clkr, + [GCC_PCIE1_AXI_S_BRIDGE_CLK] = &gcc_pcie1_axi_s_bridge_clk.clkr, + [GCC_PCIE1_AXI_S_CLK] = &gcc_pcie1_axi_s_clk.clkr, + [GCC_PCIE1_AXI_S_CLK_SRC] = &gcc_pcie1_axi_s_clk_src.clkr, + [GCC_PCIE1_PIPE_CLK] = &gcc_pcie1_pipe_clk.clkr, + [GCC_PCIE1_PIPE_CLK_SRC] = &gcc_pcie1_pipe_clk_src.clkr, + [GCC_PCIE1_RCHNG_CLK_SRC] = &gcc_pcie1_rchng_clk_src.clkr, + [GCC_PCIE1_RCHNG_CLK] = &gcc_pcie1_rchng_clk.clkr, + [GCC_PCIE2_AHB_CLK] = &gcc_pcie2_ahb_clk.clkr, + [GCC_PCIE2_AUX_CLK] = &gcc_pcie2_aux_clk.clkr, + [GCC_PCIE2_AXI_M_CLK] = &gcc_pcie2_axi_m_clk.clkr, + [GCC_PCIE2_AXI_M_CLK_SRC] = &gcc_pcie2_axi_m_clk_src.clkr, + [GCC_PCIE2_AXI_S_BRIDGE_CLK] = &gcc_pcie2_axi_s_bridge_clk.clkr, + [GCC_PCIE2_AXI_S_CLK] = &gcc_pcie2_axi_s_clk.clkr, + [GCC_PCIE2_AXI_S_CLK_SRC] = &gcc_pcie2_axi_s_clk_src.clkr, + [GCC_PCIE2_PIPE_CLK] = &gcc_pcie2_pipe_clk.clkr, + [GCC_PCIE2_PIPE_CLK_SRC] = &gcc_pcie2_pipe_clk_src.clkr, + [GCC_PCIE2_RCHNG_CLK_SRC] = &gcc_pcie2_rchng_clk_src.clkr, + [GCC_PCIE2_RCHNG_CLK] = &gcc_pcie2_rchng_clk.clkr, + [GCC_PCIE3_AHB_CLK] = &gcc_pcie3_ahb_clk.clkr, + [GCC_PCIE3_AUX_CLK] = &gcc_pcie3_aux_clk.clkr, + [GCC_PCIE3_AXI_M_CLK] = &gcc_pcie3_axi_m_clk.clkr, + [GCC_PCIE3_AXI_M_CLK_SRC] = &gcc_pcie3_axi_m_clk_src.clkr, + [GCC_PCIE3_AXI_S_BRIDGE_CLK] = &gcc_pcie3_axi_s_bridge_clk.clkr, + [GCC_PCIE3_AXI_S_CLK] = &gcc_pcie3_axi_s_clk.clkr, + [GCC_PCIE3_AXI_S_CLK_SRC] = &gcc_pcie3_axi_s_clk_src.clkr, + [GCC_PCIE3_PIPE_CLK] = &gcc_pcie3_pipe_clk.clkr, + [GCC_PCIE3_PIPE_CLK_SRC] = &gcc_pcie3_pipe_clk_src.clkr, + [GCC_PCIE3_RCHNG_CLK_SRC] = &gcc_pcie3_rchng_clk_src.clkr, + [GCC_PCIE3_RCHNG_CLK] = &gcc_pcie3_rchng_clk.clkr, + [GCC_PCIE4_AHB_CLK] = &gcc_pcie4_ahb_clk.clkr, + [GCC_PCIE4_AUX_CLK] = &gcc_pcie4_aux_clk.clkr, + [GCC_PCIE4_AXI_M_CLK] = &gcc_pcie4_axi_m_clk.clkr, + [GCC_PCIE4_AXI_M_CLK_SRC] = &gcc_pcie4_axi_m_clk_src.clkr, + [GCC_PCIE4_AXI_S_BRIDGE_CLK] = &gcc_pcie4_axi_s_bridge_clk.clkr, + [GCC_PCIE4_AXI_S_CLK] = &gcc_pcie4_axi_s_clk.clkr, + [GCC_PCIE4_AXI_S_CLK_SRC] = &gcc_pcie4_axi_s_clk_src.clkr, + [GCC_PCIE4_PIPE_CLK] = &gcc_pcie4_pipe_clk.clkr, + [GCC_PCIE4_PIPE_CLK_SRC] = &gcc_pcie4_pipe_clk_src.clkr, + [GCC_PCIE4_RCHNG_CLK_SRC] = &gcc_pcie4_rchng_clk_src.clkr, + [GCC_PCIE4_RCHNG_CLK] = &gcc_pcie4_rchng_clk.clkr, + [GCC_PCIE_AUX_CLK_SRC] = &gcc_pcie_aux_clk_src.clkr, + [GCC_PCNOC_BFDCD_CLK_SRC] = &gcc_pcnoc_bfdcd_clk_src.clkr, + [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr, + [GCC_QDSS_AT_CLK_SRC] = &gcc_qdss_at_clk_src.clkr, + [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr, + [GCC_QDSS_TSCTR_CLK_SRC] = &gcc_qdss_tsctr_clk_src.clkr, + [GCC_QPIC_AHB_CLK] = &gcc_qpic_ahb_clk.clkr, + [GCC_QPIC_CLK] = &gcc_qpic_clk.clkr, + [GCC_QPIC_CLK_SRC] = &gcc_qpic_clk_src.clkr, + [GCC_QPIC_IO_MACRO_CLK] = &gcc_qpic_io_macro_clk.clkr, + [GCC_QPIC_IO_MACRO_CLK_SRC] = &gcc_qpic_io_macro_clk_src.clkr, + [GCC_QPIC_SLEEP_CLK] = &gcc_qpic_sleep_clk.clkr, + [GCC_QUPV3_2X_CORE_CLK_SRC] = &gcc_qupv3_2x_core_clk_src.clkr, + [GCC_QUPV3_AHB_MST_CLK] = &gcc_qupv3_ahb_mst_clk.clkr, + [GCC_QUPV3_AHB_SLV_CLK] = &gcc_qupv3_ahb_slv_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK] = &gcc_qupv3_wrap_se0_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK_SRC] = &gcc_qupv3_wrap_se0_clk_src.clkr, + [GCC_QUPV3_WRAP_SE1_CLK] = &gcc_qupv3_wrap_se1_clk.clkr, + [GCC_QUPV3_WRAP_SE1_CLK_SRC] = &gcc_qupv3_wrap_se1_clk_src.clkr, + [GCC_QUPV3_WRAP_SE2_CLK] = &gcc_qupv3_wrap_se2_clk.clkr, + [GCC_QUPV3_WRAP_SE2_CLK_SRC] = &gcc_qupv3_wrap_se2_clk_src.clkr, + [GCC_QUPV3_WRAP_SE3_CLK] = &gcc_qupv3_wrap_se3_clk.clkr, + [GCC_QUPV3_WRAP_SE3_CLK_SRC] = &gcc_qupv3_wrap_se3_clk_src.clkr, + [GCC_QUPV3_WRAP_SE4_CLK] = &gcc_qupv3_wrap_se4_clk.clkr, + [GCC_QUPV3_WRAP_SE4_CLK_SRC] = &gcc_qupv3_wrap_se4_clk_src.clkr, + [GCC_QUPV3_WRAP_SE5_CLK] = &gcc_qupv3_wrap_se5_clk.clkr, + [GCC_QUPV3_WRAP_SE5_CLK_SRC] = &gcc_qupv3_wrap_se5_clk_src.clkr, + [GCC_QUPV3_WRAP_SE6_CLK] = &gcc_qupv3_wrap_se6_clk.clkr, + [GCC_QUPV3_WRAP_SE6_CLK_SRC] = &gcc_qupv3_wrap_se6_clk_src.clkr, + [GCC_QUPV3_WRAP_SE7_CLK] = &gcc_qupv3_wrap_se7_clk.clkr, + [GCC_QUPV3_WRAP_SE7_CLK_SRC] = &gcc_qupv3_wrap_se7_clk_src.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SLEEP_CLK_SRC] = &gcc_sleep_clk_src.clkr, + [GCC_SNOC_USB_CLK] = &gcc_snoc_usb_clk.clkr, + [GCC_SYSTEM_NOC_BFDCD_CLK_SRC] = &gcc_system_noc_bfdcd_clk_src.clkr, + [GCC_UNIPHY0_AHB_CLK] = &gcc_uniphy0_ahb_clk.clkr, + [GCC_UNIPHY0_SYS_CLK] = &gcc_uniphy0_sys_clk.clkr, + [GCC_UNIPHY1_AHB_CLK] = &gcc_uniphy1_ahb_clk.clkr, + [GCC_UNIPHY1_SYS_CLK] = &gcc_uniphy1_sys_clk.clkr, + [GCC_UNIPHY2_AHB_CLK] = &gcc_uniphy2_ahb_clk.clkr, + [GCC_UNIPHY2_SYS_CLK] = &gcc_uniphy2_sys_clk.clkr, + [GCC_UNIPHY_SYS_CLK_SRC] = &gcc_uniphy_sys_clk_src.clkr, + [GCC_USB0_AUX_CLK] = &gcc_usb0_aux_clk.clkr, + [GCC_USB0_AUX_CLK_SRC] = &gcc_usb0_aux_clk_src.clkr, + [GCC_USB0_EUD_AT_CLK] = &gcc_usb0_eud_at_clk.clkr, + [GCC_USB0_MASTER_CLK] = &gcc_usb0_master_clk.clkr, + [GCC_USB0_MASTER_CLK_SRC] = &gcc_usb0_master_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_CLK] = &gcc_usb0_mock_utmi_clk.clkr, + [GCC_USB0_MOCK_UTMI_CLK_SRC] = &gcc_usb0_mock_utmi_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_DIV_CLK_SRC] = &gcc_usb0_mock_utmi_div_clk_src.clkr, + [GCC_USB0_PHY_CFG_AHB_CLK] = &gcc_usb0_phy_cfg_ahb_clk.clkr, + [GCC_USB0_PIPE_CLK] = &gcc_usb0_pipe_clk.clkr, + [GCC_USB0_PIPE_CLK_SRC] = &gcc_usb0_pipe_clk_src.clkr, + [GCC_USB0_SLEEP_CLK] = &gcc_usb0_sleep_clk.clkr, + [GCC_USB1_MASTER_CLK] = &gcc_usb1_master_clk.clkr, + [GCC_USB1_MOCK_UTMI_CLK] = &gcc_usb1_mock_utmi_clk.clkr, + [GCC_USB1_MOCK_UTMI_CLK_SRC] = &gcc_usb1_mock_utmi_clk_src.clkr, + [GCC_USB1_MOCK_UTMI_DIV_CLK_SRC] = &gcc_usb1_mock_utmi_div_clk_src.clkr, + [GCC_USB1_PHY_CFG_AHB_CLK] = &gcc_usb1_phy_cfg_ahb_clk.clkr, + [GCC_USB1_SLEEP_CLK] = &gcc_usb1_sleep_clk.clkr, + [GCC_XO_CLK_SRC] = &gcc_xo_clk_src.clkr, + [GPLL0_MAIN] = &gpll0_main.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL2] = &gpll2.clkr, + [GPLL2_OUT_MAIN] = &gpll2_out_main.clkr, + [GPLL4] = &gpll4.clkr, +}; + +static const struct qcom_reset_map gcc_ipq9650_resets[] = { + [GCC_ADSS_BCR] = { 0x1c000 }, + [GCC_ADSS_PWM_CLK_ARES] = { 0x1c00c, 2 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_BCR] = { 0x38000 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_GPLL0_CLK_ARES] = { 0x3800c, 2 }, + [GCC_APSS_AHB_CLK_ARES] = { 0x24014, 2 }, + [GCC_APSS_ATB_CLK_ARES] = { 0x24034, 2 }, + [GCC_APSS_AXI_CLK_ARES] = { 0x24018, 2 }, + [GCC_APSS_TS_CLK_ARES] = { 0x24030, 2 }, + [GCC_BOOT_ROM_AHB_CLK_ARES] = { 0x1302c, 2 }, + [GCC_BOOT_ROM_BCR] = { 0x13028 }, + [GCC_CPUSS_TRIG_CLK_ARES] = { 0x2401c, 2 }, + [GCC_GP1_CLK_ARES] = { 0x8018, 2 }, + [GCC_GP2_CLK_ARES] = { 0x8030, 2 }, + [GCC_GP3_CLK_ARES] = { 0x8048, 2 }, + [GCC_MDIO_AHB_CLK_ARES] = { 0x17040, 2 }, + [GCC_MDIO_BCR] = { 0x1703c }, + [GCC_NSS_BCR] = { 0x17000 }, + [GCC_NSS_TS_CLK_ARES] = { 0x17018, 2 }, + [GCC_NSSCC_CLK_ARES] = { 0x17034, 2 }, + [GCC_NSSCFG_CLK_ARES] = { 0x1702c, 2 }, + [GCC_NSSNOC_ATB_CLK_ARES] = { 0x17014, 2 }, + [GCC_NSSNOC_MEMNOC_1_CLK_ARES] = { 0x17084, 2 }, + [GCC_NSSNOC_MEMNOC_CLK_ARES] = { 0x17024, 2 }, + [GCC_NSSNOC_NSSCC_CLK_ARES] = { 0x17030, 2 }, + [GCC_NSSNOC_PCNOC_1_CLK_ARES] = { 0x17080, 2 }, + [GCC_NSSNOC_QOSGEN_REF_CLK_ARES] = { 0x1701c, 2 }, + [GCC_NSSNOC_SNOC_1_CLK_ARES] = { 0x1707c, 2 }, + [GCC_NSSNOC_SNOC_CLK_ARES] = { 0x17028, 2 }, + [GCC_NSSNOC_TIMEOUT_REF_CLK_ARES] = { 0x17020, 2 }, + [GCC_NSSNOC_XO_DCD_CLK_ARES] = { 0x17074, 2 }, + [GCC_PCIE0_AHB_CLK_ARES] = { 0x28030, 2 }, + [GCC_PCIE0_AUX_CLK_ARES] = { 0x28070, 2 }, + [GCC_PCIE0_AXI_M_CLK_ARES] = { 0x28038, 2 }, + [GCC_PCIE0_AXI_S_BRIDGE_CLK_ARES] = { 0x28048, 2 }, + [GCC_PCIE0_AXI_S_CLK_ARES] = { 0x28040, 2 }, + [GCC_PCIE0_BCR] = { 0x28000 }, + [GCC_PCIE0_LINK_DOWN_BCR] = { 0x28054 }, + [GCC_PCIE0_PHY_BCR] = { 0x28060 }, + [GCC_PCIE0_PIPE_CLK_ARES] = { 0x28068, 2 }, + [GCC_PCIE0PHY_PHY_BCR] = { 0x2805c }, + [GCC_PCIE0_PIPE_RESET] = { 0x28058, 0 }, + [GCC_PCIE0_CORE_STICKY_RESET] = { 0x28058, 1 }, + [GCC_PCIE0_AXI_S_STICKY_RESET] = { 0x28058, 2 }, + [GCC_PCIE0_AXI_S_RESET] = { 0x28058, 3 }, + [GCC_PCIE0_AXI_M_STICKY_RESET] = { 0x28058, 4 }, + [GCC_PCIE0_AXI_M_RESET] = { 0x28058, 5 }, + [GCC_PCIE0_AUX_RESET] = { 0x28058, 6 }, + [GCC_PCIE0_AHB_RESET] = { 0x28058, 7 }, + [GCC_PCIE1_AHB_CLK_ARES] = { 0x29030, 2 }, + [GCC_PCIE1_AUX_CLK_ARES] = { 0x29074, 2 }, + [GCC_PCIE1_AXI_M_CLK_ARES] = { 0x29038, 2 }, + [GCC_PCIE1_AXI_S_BRIDGE_CLK_ARES] = { 0x29048, 2 }, + [GCC_PCIE1_AXI_S_CLK_ARES] = { 0x29040, 2 }, + [GCC_PCIE1_BCR] = { 0x29000 }, + [GCC_PCIE1_LINK_DOWN_BCR] = { 0x29054 }, + [GCC_PCIE1_PHY_BCR] = { 0x29060 }, + [GCC_PCIE1_PIPE_CLK_ARES] = { 0x29068, 2 }, + [GCC_PCIE1PHY_PHY_BCR] = { 0x2905c }, + [GCC_PCIE1_PIPE_RESET] = { 0x29058, 0 }, + [GCC_PCIE1_CORE_STICKY_RESET] = { 0x29058, 1 }, + [GCC_PCIE1_AXI_S_STICKY_RESET] = { 0x29058, 2 }, + [GCC_PCIE1_AXI_S_RESET] = { 0x29058, 3 }, + [GCC_PCIE1_AXI_M_STICKY_RESET] = { 0x29058, 4 }, + [GCC_PCIE1_AXI_M_RESET] = { 0x29058, 5 }, + [GCC_PCIE1_AUX_RESET] = { 0x29058, 6 }, + [GCC_PCIE1_AHB_RESET] = { 0x29058, 7 }, + [GCC_PCIE2_AHB_CLK_ARES] = { 0x2a030, 2 }, + [GCC_PCIE2_AUX_CLK_ARES] = { 0x2a078, 2 }, + [GCC_PCIE2_AXI_M_CLK_ARES] = { 0x2a038, 2 }, + [GCC_PCIE2_AXI_S_BRIDGE_CLK_ARES] = { 0x2a048, 2 }, + [GCC_PCIE2_AXI_S_CLK_ARES] = { 0x2a040, 2 }, + [GCC_PCIE2_BCR] = { 0x2a000 }, + [GCC_PCIE2_LINK_DOWN_BCR] = { 0x2a054 }, + [GCC_PCIE2_PHY_BCR] = { 0x2a060 }, + [GCC_PCIE2_PIPE_CLK_ARES] = { 0x2a068, 2 }, + [GCC_PCIE2PHY_PHY_BCR] = { 0x2a05c }, + [GCC_PCIE2_PIPE_RESET] = { 0x2a058, 0 }, + [GCC_PCIE2_CORE_STICKY_RESET] = { 0x2a058, 1 }, + [GCC_PCIE2_AXI_S_STICKY_RESET] = { 0x2a058, 2 }, + [GCC_PCIE2_AXI_S_RESET] = { 0x2a058, 3 }, + [GCC_PCIE2_AXI_M_STICKY_RESET] = { 0x2a058, 4 }, + [GCC_PCIE2_AXI_M_RESET] = { 0x2a058, 5 }, + [GCC_PCIE2_AUX_RESET] = { 0x2a058, 6 }, + [GCC_PCIE2_AHB_RESET] = { 0x2a058, 7 }, + [GCC_PCIE3_AHB_CLK_ARES] = { 0x2b030, 2 }, + [GCC_PCIE3_AUX_CLK_ARES] = { 0x2b07c, 2 }, + [GCC_PCIE3_AXI_M_CLK_ARES] = { 0x2b038, 2 }, + [GCC_PCIE3_AXI_S_BRIDGE_CLK_ARES] = { 0x2b048, 2 }, + [GCC_PCIE3_AXI_S_CLK_ARES] = { 0x2b040, 2 }, + [GCC_PCIE3_BCR] = { 0x2b000 }, + [GCC_PCIE3_LINK_DOWN_BCR] = { 0x2b054 }, + [GCC_PCIE3_PHY_BCR] = { 0x2b060 }, + [GCC_PCIE3_PIPE_CLK_ARES] = { 0x2b068, 2 }, + [GCC_PCIE3PHY_PHY_BCR] = { 0x2b05c }, + [GCC_PCIE3_PIPE_RESET] = { 0x2b058, 0 }, + [GCC_PCIE3_CORE_STICKY_RESET] = { 0x2b058, 1 }, + [GCC_PCIE3_AXI_S_STICKY_RESET] = { 0x2b058, 2 }, + [GCC_PCIE3_AXI_S_RESET] = { 0x2b058, 3 }, + [GCC_PCIE3_AXI_M_STICKY_RESET] = { 0x2b058, 4 }, + [GCC_PCIE3_AXI_M_RESET] = { 0x2b058, 5 }, + [GCC_PCIE3_AUX_RESET] = { 0x2b058, 6 }, + [GCC_PCIE3_AHB_RESET] = { 0x2b058, 7 }, + [GCC_PCIE4_AHB_CLK_ARES] = { 0x2501c, 2 }, + [GCC_PCIE4_AUX_CLK_ARES] = { 0x25020, 2 }, + [GCC_PCIE4_AXI_M_CLK_ARES] = { 0x25028, 2 }, + [GCC_PCIE4_AXI_S_BRIDGE_CLK_ARES] = { 0x25038, 2 }, + [GCC_PCIE4_AXI_S_CLK_ARES] = { 0x25030, 2 }, + [GCC_PCIE4_BCR] = { 0x25000 }, + [GCC_PCIE4_LINK_DOWN_BCR] = { 0x25044 }, + [GCC_PCIE4_PHY_BCR] = { 0x2504c }, + [GCC_PCIE4_PIPE_CLK_ARES] = { 0x2503c, 2 }, + [GCC_PCIE4_PIPE_RESET] = { 0x25054, 0 }, + [GCC_PCIE4_CORE_STICKY_RESET] = { 0x25054, 1 }, + [GCC_PCIE4_AXI_S_STICKY_RESET] = { 0x25054, 2 }, + [GCC_PCIE4_AXI_S_RESET] = { 0x25054, 3 }, + [GCC_PCIE4_AXI_M_STICKY_RESET] = { 0x25054, 4 }, + [GCC_PCIE4_AXI_M_RESET] = { 0x25054, 5 }, + [GCC_PCIE4_AUX_RESET] = { 0x25054, 6 }, + [GCC_PCIE4_AHB_RESET] = { 0x25054, 7 }, + [GCC_PCIE4PHY_PHY_BCR] = { 0x25048 }, + [GCC_QDSS_APB2JTAG_CLK_ARES] = { 0x2d05c, 2 }, + [GCC_QDSS_AT_CLK_ARES] = { 0x2d034, 2 }, + [GCC_QDSS_BCR] = { 0x2d000 }, + [GCC_QDSS_CFG_AHB_CLK_ARES] = { 0x2d068, 2 }, + [GCC_QDSS_DAP_AHB_CLK_ARES] = { 0x2d064, 2 }, + [GCC_QDSS_DAP_CLK_ARES] = { 0x2d058, 2 }, + [GCC_QDSS_ETR_USB_CLK_ARES] = { 0x2d060, 2 }, + [GCC_QDSS_EUD_AT_CLK_ARES] = { 0x2d06c, 2 }, + [GCC_QDSS_STM_CLK_ARES] = { 0x2d03c, 2 }, + [GCC_QDSS_TRACECLKIN_CLK_ARES] = { 0x2d040, 2 }, + [GCC_QDSS_TS_CLK_ARES] = { 0x2d078, 2 }, + [GCC_QDSS_TSCTR_DIV16_CLK_ARES] = { 0x2d054, 2 }, + [GCC_QDSS_TSCTR_DIV2_CLK_ARES] = { 0x2d044, 2 }, + [GCC_QDSS_TSCTR_DIV3_CLK_ARES] = { 0x2d048, 2 }, + [GCC_QDSS_TSCTR_DIV4_CLK_ARES] = { 0x2d04c, 2 }, + [GCC_QDSS_TSCTR_DIV8_CLK_ARES] = { 0x2d050, 2 }, + [GCC_QPIC_AHB_CLK_ARES] = { 0x32010, 2 }, + [GCC_QPIC_CLK_ARES] = { 0x32028, 2 }, + [GCC_QPIC_BCR] = { 0x32000 }, + [GCC_QPIC_IO_MACRO_CLK_ARES] = { 0x3200c, 2 }, + [GCC_QPIC_SLEEP_CLK_ARES] = { 0x32018, 2 }, + [GCC_QUPV3_2X_CORE_CLK_ARES] = { 0x1020, 2 }, + [GCC_QUPV3_AHB_MST_CLK_ARES] = { 0x1014, 2 }, + [GCC_QUPV3_AHB_SLV_CLK_ARES] = { 0x102c, 2 }, + [GCC_QUPV3_BCR] = { 0x1000 }, + [GCC_QUPV3_CORE_CLK_ARES] = { 0x1018, 2 }, + [GCC_QUPV3_WRAP_SE0_CLK_ARES] = { 0x202c, 2 }, + [GCC_QUPV3_WRAP_SE0_BCR] = { 0x2000 }, + [GCC_QUPV3_WRAP_SE1_CLK_ARES] = { 0x302c, 2 }, + [GCC_QUPV3_WRAP_SE1_BCR] = { 0x3000 }, + [GCC_QUPV3_WRAP_SE2_CLK_ARES] = { 0x3048, 2 }, + [GCC_QUPV3_WRAP_SE2_BCR] = { 0x3030 }, + [GCC_QUPV3_WRAP_SE3_CLK_ARES] = { 0x3064, 2 }, + [GCC_QUPV3_WRAP_SE3_BCR] = { 0x304c }, + [GCC_QUPV3_WRAP_SE4_CLK_ARES] = { 0x3080, 2 }, + [GCC_QUPV3_WRAP_SE4_BCR] = { 0x3068 }, + [GCC_QUPV3_WRAP_SE5_CLK_ARES] = { 0x30a4, 2 }, + [GCC_QUPV3_WRAP_SE5_BCR] = { 0x308c }, + [GCC_QUPV3_WRAP_SE6_CLK_ARES] = { 0x4018, 2 }, + [GCC_QUPV3_WRAP_SE6_BCR] = { 0x4000 }, + [GCC_QUPV3_WRAP_SE7_CLK_ARES] = { 0x4034, 2 }, + [GCC_QUPV3_WRAP_SE7_BCR] = { 0x401c }, + [GCC_QUSB2_0_PHY_BCR] = { 0x2c068 }, + [GCC_QUSB2_1_PHY_BCR] = { 0x3c030 }, + [GCC_SDCC1_APPS_CLK_ARES] = { 0x3302c, 2 }, + [GCC_SDCC1_ICE_CORE_CLK_ARES] = { 0x33034, 2 }, + [GCC_SDCC_BCR] = { 0x33000 }, + [GCC_TLMM_AHB_CLK_ARES] = { 0x3e004, 2 }, + [GCC_TLMM_CLK_ARES] = { 0x3e008, 2 }, + [GCC_TLMM_BCR] = { 0x3e000 }, + [GCC_UNIPHY0_AHB_CLK_ARES] = { 0x1704c, 2 }, + [GCC_UNIPHY0_BCR] = { 0x17044 }, + [GCC_UNIPHY0_PMA_BCR] = { 0x17098 }, + [GCC_UNIPHY0_SYS_CLK_ARES] = { 0x17048, 2 }, + [GCC_UNIPHY1_AHB_CLK_ARES] = { 0x1705c, 2 }, + [GCC_UNIPHY1_BCR] = { 0x17054 }, + [GCC_UNIPHY1_PMA_BCR] = { 0x1709c }, + [GCC_UNIPHY1_SYS_CLK_ARES] = { 0x17058, 2 }, + [GCC_UNIPHY2_AHB_CLK_ARES] = { 0x1706c, 2 }, + [GCC_UNIPHY2_BCR] = { 0x17064 }, + [GCC_UNIPHY2_PMA_BCR] = { 0x170a0 }, + [GCC_UNIPHY2_SYS_CLK_ARES] = { 0x17068, 2 }, + [GCC_UNIPHY0_XPCS_ARES] = { 0x17050, 2 }, + [GCC_UNIPHY1_XLGPCS_ARES] = { 0x17060, 1 }, + [GCC_UNIPHY1_XPCS_ARES] = { 0x17060, 2 }, + [GCC_UNIPHY2_XLGPCS_ARES] = { 0x17070, 1 }, + [GCC_UNIPHY2_XPCS_ARES] = { 0x17070, 2 }, + [GCC_USB0_AUX_CLK_ARES] = { 0x2c04c, 2 }, + [GCC_USB0_MASTER_CLK_ARES] = { 0x2c044, 2 }, + [GCC_USB0_MOCK_UTMI_CLK_ARES] = { 0x2c050, 2 }, + [GCC_USB0_PHY_BCR] = { 0x2c06c }, + [GCC_USB0_PHY_CFG_AHB_CLK_ARES] = { 0x2c05c, 2 }, + [GCC_USB0_PIPE_CLK_ARES] = { 0x2c054, 2 }, + [GCC_USB0_SLEEP_CLK_ARES] = { 0x2c058, 2 }, + [GCC_USB1_BCR] = { 0x3c000 }, + [GCC_USB1_MASTER_CLK_ARES] = { 0x3c028, 2 }, + [GCC_USB1_MOCK_UTMI_CLK_ARES] = { 0x3c024, 2 }, + [GCC_USB1_PHY_CFG_AHB_CLK_ARES] = { 0x3c01c, 2 }, + [GCC_USB1_SLEEP_CLK_ARES] = { 0x3c020, 2 }, + [GCC_USB3PHY_0_PHY_BCR] = { 0x2c070 }, + [GCC_USB_BCR] = { 0x2c000 }, +}; + +static const struct of_device_id gcc_ipq9650_match_table[] = { + { .compatible = "qcom,ipq9650-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_ipq9650_match_table); + +static const struct regmap_config gcc_ipq9650_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3f024, + .fast_io = true, +}; + +static struct clk_hw *gcc_ipq9650_hws[] = { + &gpll0_div2.hw, + &gcc_xo_div4_clk_src.hw, + &gcc_qdss_dap_sync_clk_src.hw, + &gcc_eud_at_div_clk_src.hw, +}; + +static const struct qcom_cc_desc gcc_ipq9650_desc = { + .config = &gcc_ipq9650_regmap_config, + .clks = gcc_ipq9650_clocks, + .num_clks = ARRAY_SIZE(gcc_ipq9650_clocks), + .resets = gcc_ipq9650_resets, + .num_resets = ARRAY_SIZE(gcc_ipq9650_resets), + .clk_hws = gcc_ipq9650_hws, + .num_clk_hws = ARRAY_SIZE(gcc_ipq9650_hws), +}; + +static int gcc_ipq9650_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_ipq9650_desc); +} + +static struct platform_driver gcc_ipq9650_driver = { + .probe = gcc_ipq9650_probe, + .driver = { + .name = "qcom,gcc-ipq9650", + .of_match_table = gcc_ipq9650_match_table, + }, +}; + +static int __init gcc_ipq9650_init(void) +{ + return platform_driver_register(&gcc_ipq9650_driver); +} +core_initcall(gcc_ipq9650_init); + +static void __exit gcc_ipq9650_exit(void) +{ + platform_driver_unregister(&gcc_ipq9650_driver); +} +module_exit(gcc_ipq9650_exit); + +MODULE_DESCRIPTION("QTI GCC IPQ9650 Driver"); +MODULE_LICENSE("GPL"); From 75479bed53b11f81e37fb38c0608a2af4ddc9b1e Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:46 +0200 Subject: [PATCH 0846/5214] PCI: intel-gw: Remove unused PCIE_APP_INTX_OFST definition The C preprocessor define 'PCIE_APP_INTX_OFST' is not used in the sources. Delete it. Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-2-0a2b933fe04f@dev.tdt.de --- drivers/pci/controller/dwc/pcie-intel-gw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index c21906eced61..80d1607c46cb 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -47,7 +47,6 @@ #define PCIE_APP_IRN_INTD BIT(16) #define PCIE_APP_IRN_MSG_LTR BIT(18) #define PCIE_APP_IRN_SYS_ERR_RC BIT(29) -#define PCIE_APP_INTX_OFST 12 #define PCIE_APP_IRN_INT \ (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ From abddc0539e5931f4ad2f589a03cbba5a6a64485f Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:47 +0200 Subject: [PATCH 0847/5214] PCI: intel-gw: Move interrupt enable to own function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve the readability of the code, move the interrupt enable instructions to a separate function. That is already done for the disable interrupt instruction. In addition, clear and disable all pending interrupts, as is done in intel_pcie_core_irq_disable(). After that, enable all relevant interrupts again. The 'PCIE_APP_IRNEN' definition contains all the relevant interrupts that are of interest. This change is also done in the MaxLinear SDK [1]. As I unfortunately don’t have any documentation for this IP core, I suspect that the intention is to set the IP core for interrupt handling to a specific state. Perhaps the problem is that the IP core did not reinitialize the interrupt register properly after a power cycle. In my view, it can’t do any harm to switch the interrupt off and then on again to set them to a specific state. The reason why the MaxLinear SDK is used as a reference here is, that this PCIe DWC IP is used in the URX851 and URX850 SoC. This SoC was originally developed by Intel when they acquired Lantiq’s home networking division in 2015 [2]. In 2020 the home network division was sold to MaxLinear [3]. Since then, this SoC belongs to MaxLinear. They use their own SDK, which runs on kernel version '5.15.x'. [1] https://github.com/maxlinear/linux/blob/updk_9.1.90/drivers/pci/controller/dwc/pcie-intel-gw.c#L431 [2] https://www.intc.com/news-events/press-releases/detail/364/intel-to-acquire-lantiq-advancing-the-connected-home [3] https://investors.maxlinear.com/press-releases/detail/395/maxlinear-to-acquire-intels-home-gateway-platform Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-3-0a2b933fe04f@dev.tdt.de --- drivers/pci/controller/dwc/pcie-intel-gw.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 80d1607c46cb..e88b8243cc41 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -195,6 +195,13 @@ static void intel_pcie_device_rst_deassert(struct intel_pcie *pcie) gpiod_set_value_cansleep(pcie->reset_gpio, 0); } +static void intel_pcie_core_irq_enable(struct intel_pcie *pcie) +{ + pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); + pcie_app_wr(pcie, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); + pcie_app_wr(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT); +} + static void intel_pcie_core_irq_disable(struct intel_pcie *pcie) { pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); @@ -316,9 +323,7 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) if (ret) goto app_init_err; - /* Enable integrated interrupts */ - pcie_app_wr_mask(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, - PCIE_APP_IRN_INT); + intel_pcie_core_irq_enable(pcie); return 0; From febf9ed3c35e5eec7ea384ebbd55a5296e3ca5e9 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:48 +0200 Subject: [PATCH 0848/5214] PCI: intel-gw: Enable clock before PHY init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To ensure that the boot sequence is correct, the DWC PCIe core clock must be switched on before PHY init call [1]. This changes are based on patched kernel sources of the MaxLinear SDK. The reason why the MaxLinear SDK is used as a reference here is, that this PCIe DWC IP is used in the URX851 and URX850 SoC. This SoC was originally developed by Intel when they acquired Lantiq’s home networking division in 2015 [2]. In 2020 the home network division was sold to MaxLinear [3]. Since then, this SoC belongs to MaxLinear. They use their own SDK, which runs on kernel version '5.15.x'. [1] https://github.com/maxlinear/linux/blob/updk_9.1.90/drivers/pci/controller/dwc/pcie-intel-gw.c#L544 [2] https://www.intc.com/news-events/press-releases/detail/364/intel-to-acquire-lantiq-advancing-the-connected-home [3] https://investors.maxlinear.com/press-releases/detail/395/maxlinear-to-acquire-intels-home-gateway-platform Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-4-0a2b933fe04f@dev.tdt.de --- drivers/pci/controller/dwc/pcie-intel-gw.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index e88b8243cc41..6d9499d95467 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -291,13 +291,9 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) intel_pcie_core_rst_assert(pcie); intel_pcie_device_rst_assert(pcie); - - ret = phy_init(pcie->phy); - if (ret) - return ret; - intel_pcie_core_rst_deassert(pcie); + /* Controller clock must be provided earlier than PHY */ ret = clk_prepare_enable(pcie->core_clk); if (ret) { dev_err(pcie->pci.dev, "Core clock enable failed: %d\n", ret); @@ -306,13 +302,17 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) pci->atu_base = pci->dbi_base + 0xC0000; + ret = phy_init(pcie->phy); + if (ret) + goto phy_err; + intel_pcie_ltssm_disable(pcie); intel_pcie_link_setup(pcie); intel_pcie_init_n_fts(pci); ret = dw_pcie_setup_rc(&pci->pp); if (ret) - goto app_init_err; + goto err; dw_pcie_upconfig_setup(pci); @@ -321,17 +321,18 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) ret = dw_pcie_wait_for_link(pci); if (ret) - goto app_init_err; + goto err; intel_pcie_core_irq_enable(pcie); return 0; -app_init_err: +err: + phy_exit(pcie->phy); +phy_err: clk_disable_unprepare(pcie->core_clk); clk_err: intel_pcie_core_rst_assert(pcie); - phy_exit(pcie->phy); return ret; } From 1eedabe7c6170b5c73c7d801f427c127be74916e Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:49 +0200 Subject: [PATCH 0849/5214] PCI: intel-gw: Add .start_link() callback The pcie-intel-gw driver had no .start_link() callback. Add one so the driver works again and does not abort with the following error messages during probing: intel-gw-pcie d1000000.pcie: host bridge /soc/pcie@d1000000 ranges: intel-gw-pcie d1000000.pcie: MEM 0x00dc000000..0x00ddffffff -> 0x00dc000000 intel-combo-phy d0c00000.combo-phy: Set combo mode: combophy[1]: mode: PCIe single lane mode intel-gw-pcie d1000000.pcie: No outbound iATU found intel-gw-pcie d1000000.pcie: Cannot initialize host intel-gw-pcie d1000000.pcie: probe with driver intel-gw-pcie failed with error -22 intel-gw-pcie c1100000.pcie: host bridge /soc/pcie@c1100000 ranges: intel-gw-pcie c1100000.pcie: MEM 0x00ce000000..0x00cfffffff -> 0x00ce000000 intel-combo-phy c0c00000.combo-phy: Set combo mode: combophy[3]: mode: PCIe single lane mode intel-gw-pcie c1100000.pcie: No outbound iATU found intel-gw-pcie c1100000.pcie: Cannot initialize host intel-gw-pcie c1100000.pcie: probe with driver intel-gw-pcie failed with error -22 Fixes: c5097b9869a1 ("Revert "PCI: dwc: Wait for link up only if link is started"") Fixes: da56a1bfbab5 ("PCI: dwc: Wait for link up only if link is started") Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam [bhelgaas: remove timestamps] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-5-0a2b933fe04f@dev.tdt.de --- drivers/pci/controller/dwc/pcie-intel-gw.c | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 6d9499d95467..afd933050c92 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -284,6 +284,16 @@ static void intel_pcie_turn_off(struct intel_pcie *pcie) pcie_rc_cfg_wr_mask(pcie, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); } +static int intel_pcie_start_link(struct dw_pcie *pci) +{ + struct intel_pcie *pcie = dev_get_drvdata(pci->dev); + + intel_pcie_device_rst_deassert(pcie); + intel_pcie_ltssm_enable(pcie); + + return 0; +} + static int intel_pcie_host_setup(struct intel_pcie *pcie) { int ret; @@ -310,25 +320,12 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) intel_pcie_link_setup(pcie); intel_pcie_init_n_fts(pci); - ret = dw_pcie_setup_rc(&pci->pp); - if (ret) - goto err; - dw_pcie_upconfig_setup(pci); - intel_pcie_device_rst_deassert(pcie); - intel_pcie_ltssm_enable(pcie); - - ret = dw_pcie_wait_for_link(pci); - if (ret) - goto err; - intel_pcie_core_irq_enable(pcie); return 0; -err: - phy_exit(pcie->phy); phy_err: clk_disable_unprepare(pcie->core_clk); clk_err: @@ -386,6 +383,7 @@ static int intel_pcie_rc_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_ops intel_pcie_ops = { + .start_link = intel_pcie_start_link, }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { From 19f90be49c117db2d70c49f5d73ec428c84dbb1e Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:50 +0200 Subject: [PATCH 0850/5214] PCI: intel-gw: Fix ATU base address setup and add optional DT 'atu' region The ATU base address was set in intel_pcie_host_setup(), which is called via pp->ops->init(). However, dw_pcie_get_resources() runs before this callback and sets a default atu_base of 0x300000, which then gets overwritten by the driver's value of 0xC0000. But this ordering is broken because atu_base must be set before dw_pcie_get_resources() runs, not after. So move the atu_base assignment from intel_pcie_host_setup() to intel_pcie_probe() to fix the initialization order. The call stack is: intel_pcie_probe dw_pcie_host_init dw_pcie_host_get_resources dw_pcie_get_resources <- sets atu_base = 0x300000 pp->ops->init intel_pcie_rc_init intel_pcie_host_setup <- was overwriting atu_base here Additionally, add support for parsing the ATU region from the device tree. If an 'atu' region is present in DT, the DWC core parses it via dw_pcie_get_resources() and the driver does not set atu_base explicitly. If 'atu' is absent, the driver falls back to the hardcoded offset (0xC0000 from DBI base) for backwards compatibility, with a warning to the user. Signed-off-by: Florian Eckert [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-6-0a2b933fe04f@dev.tdt.de --- drivers/pci/controller/dwc/pcie-intel-gw.c | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index afd933050c92..2674cd376f49 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -310,8 +310,6 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) goto clk_err; } - pci->atu_base = pci->dbi_base + 0xC0000; - ret = phy_init(pcie->phy); if (ret) goto phy_err; @@ -395,6 +393,7 @@ static int intel_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct intel_pcie *pcie; struct dw_pcie_rp *pp; + struct resource *res; struct dw_pcie *pci; int ret; @@ -419,6 +418,32 @@ static int intel_pcie_probe(struct platform_device *pdev) pci->ops = &intel_pcie_ops; pp->ops = &intel_pcie_dw_ops; + /* + * If the 'atu' region is not available in the devicetree, use the + * default offset from DBI region for backwards compatibility. The + * 'atu' region should always be specified in the devicetree, as + * this is a hardware-specific address that should not be defined + * in the driver. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); + if (!res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + pci->dbi_phys_addr = res->start; + pci->atu_base = devm_ioremap(dev, res->start + 0xC0000, SZ_4K); + if (!pci->atu_base) { + dev_err(dev, "failed to remap ATU space\n"); + return -ENOMEM; + } + + pci->atu_size = SZ_4K; + pci->atu_phys_addr = res->start + 0xC0000; + dev_warn(dev, "ATU region not specified in DT. Using default offset\n"); + } + ret = dw_pcie_host_init(pp); if (ret) { dev_err(dev, "Cannot initialize host\n"); From e220b668b17f329476d0fcea6783bb06cceeca6d Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 17 Apr 2026 10:35:51 +0200 Subject: [PATCH 0851/5214] dt-bindings: PCI: intel,lgm-pcie: Add 'atu' resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'atu' information is already set in the dwc core, if it is specified in the devicetree. The driver uses its own default, if not set in the devicetree. This information is hardware-specific and should therefore be maintained in the devicetree rather than in the source. To be backward compatible, this field is not mandatory. If 'atu' resource is not specified in the devicetree, the driver’s default value is used. Signed-off-by: Florian Eckert Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260417-pcie-intel-gw-v5-7-0a2b933fe04f@dev.tdt.de --- Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml index 54e2890ae631..394bb46b38e6 100644 --- a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml @@ -27,16 +27,20 @@ properties: - const: snps,dw-pcie reg: + minItems: 3 items: - description: Controller control and status registers. - description: PCIe configuration registers. - description: Controller application registers. + - description: Internal Address Translation Unit (iATU) registers. reg-names: + minItems: 3 items: - const: dbi - const: config - const: app + - const: atu ranges: maxItems: 1 @@ -95,8 +99,9 @@ examples: #size-cells = <2>; reg = <0xd0e00000 0x1000>, <0xd2000000 0x800000>, - <0xd0a41000 0x1000>; - reg-names = "dbi", "config", "app"; + <0xd0a41000 0x1000>, + <0xd0ec0000 0x1000>; + reg-names = "dbi", "config", "app", "atu"; linux,pci-domain = <0>; max-link-speed = <4>; bus-range = <0x00 0x08>; From ba07fd2f5742e8b0d965003b34c7a44eb4f67e53 Mon Sep 17 00:00:00 2001 From: Ben Hoff Date: Sun, 10 May 2026 19:50:36 -0400 Subject: [PATCH 0852/5214] media: pci: add AVMatrix HWS capture driver Add an in-tree AVMatrix HWS PCIe capture driver. The driver supports up to four HDMI inputs and exposes the video capture path through V4L2 with vb2-dma-contig streaming, DV timings, and per-input controls. Audio support is intentionally omitted from this submission. This patch also adds the MAINTAINERS entry for the new driver. This driver is derived from a GPL out-of-tree driver. Changes since v6: - v6 accidently contained legacy history, resubmitting with latest - remove an unused mode-change label reported by W=1 Changes since v5: - keep queue_setup() and alloc_sizeimage on the logical sizeimage value - drop the dead queue_setup() fallback that rebuilt pix.sizeimage on the fly - clarify that hws_calc_sizeimage() models the packed-YUYV-only path - add Assisted-by attribution for Codex Changes since v4: - replace plain 64-bit elapsed-time divisions in debug logging with div_u64() so i386 module builds do not emit __udivdi3 references Changes since v3: - fold the MAINTAINERS update into this patch so per-patch CI sees the new file pattern - wrap the validation text for checkpatch Changes since v2: - keep scratch DMA allocation on a single probe-owned path - avoid double-freeing V4L2 control handlers on register unwind - drop the extra per-node resolution sysfs ABI - turn live geometry changes into explicit SOURCE_CHANGE renegotiation - report live DV timings and reject attempts to retime a live source - stop advertising RESOLUTION source changes for fps-only updates - keep live fps state across harmless S_FMT restarts - stop exposing an unvalidated DV RX power-present signal - clean the imported sources for checkpatch and W=1 builds Validation: - build-tested with W=1 against a local kernel build tree - compiled the driver with ARCH=i386 allmodconfig and verified the resulting hws_pci.o, hws_video.o, and hws.o do not reference __udivdi3 - v4l2-compliance 1.33.0-5459 from v4l-utils commit 4a0d2c3b4f523406cb9a6f4c541ef14f72f19f3d on /dev/video2: 48 tests succeeded, 0 failed, 1 warning DV_RX_POWER_PRESENT is intentionally left unsupported in this revision because current hardware evidence does not expose a validated receiver-side power-detect signal distinct from active video presence. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604020522.z22eZuW8-lkp@intel.com/ Assisted-by: Codex:gpt-5.5 Signed-off-by: Ben Hoff Signed-off-by: Hans Verkuil --- MAINTAINERS | 6 + drivers/media/pci/Kconfig | 1 + drivers/media/pci/Makefile | 1 + drivers/media/pci/hws/Kconfig | 11 + drivers/media/pci/hws/Makefile | 4 + drivers/media/pci/hws/hws.h | 173 +++ drivers/media/pci/hws/hws_irq.c | 269 +++++ drivers/media/pci/hws/hws_irq.h | 10 + drivers/media/pci/hws/hws_pci.c | 865 ++++++++++++++ drivers/media/pci/hws/hws_reg.h | 136 +++ drivers/media/pci/hws/hws_v4l2_ioctl.c | 919 +++++++++++++++ drivers/media/pci/hws/hws_v4l2_ioctl.h | 36 + drivers/media/pci/hws/hws_video.c | 1490 ++++++++++++++++++++++++ drivers/media/pci/hws/hws_video.h | 29 + 14 files changed, 3950 insertions(+) create mode 100644 drivers/media/pci/hws/Kconfig create mode 100644 drivers/media/pci/hws/Makefile create mode 100644 drivers/media/pci/hws/hws.h create mode 100644 drivers/media/pci/hws/hws_irq.c create mode 100644 drivers/media/pci/hws/hws_irq.h create mode 100644 drivers/media/pci/hws/hws_pci.c create mode 100644 drivers/media/pci/hws/hws_reg.h create mode 100644 drivers/media/pci/hws/hws_v4l2_ioctl.c create mode 100644 drivers/media/pci/hws/hws_v4l2_ioctl.h create mode 100644 drivers/media/pci/hws/hws_video.c create mode 100644 drivers/media/pci/hws/hws_video.h diff --git a/MAINTAINERS b/MAINTAINERS index 5c66788998e8..4e054ce1cfaa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4317,6 +4317,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml F: drivers/iio/adc/hx711.c +AVMATRIX HWS CAPTURE DRIVER +M: Ben Hoff +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/pci/hws/ + AWINIC AW99706 WLED BACKLIGHT DRIVER M: Junjie Cao S: Maintained diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index eebb16c58f3d..bfdb200f85a3 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -13,6 +13,7 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" +source "drivers/media/pci/hws/Kconfig" source "drivers/media/pci/mgb4/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/tw5864/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 02763ad88511..c4508b6723a9 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ +obj-$(CONFIG_VIDEO_HWS) += hws/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_MGB4) += mgb4/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ diff --git a/drivers/media/pci/hws/Kconfig b/drivers/media/pci/hws/Kconfig new file mode 100644 index 000000000000..ab0bbf49ca71 --- /dev/null +++ b/drivers/media/pci/hws/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_HWS + tristate "AVMatrix HWS PCIe capture devices" + depends on PCI && VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + help + Enable support for AVMatrix HWS series multi-channel PCIe capture + devices that provide HDMI video capture. + + To compile this driver as a module, choose M here: the module + will be named hws. diff --git a/drivers/media/pci/hws/Makefile b/drivers/media/pci/hws/Makefile new file mode 100644 index 000000000000..f9c7dc4f2d8d --- /dev/null +++ b/drivers/media/pci/hws/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +hws-objs := hws_pci.o hws_irq.o hws_video.o hws_v4l2_ioctl.o + +obj-$(CONFIG_VIDEO_HWS) += hws.o diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h new file mode 100644 index 000000000000..8fbe1fe27844 --- /dev/null +++ b/drivers/media/pci/hws/hws.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_PCIE_H +#define HWS_PCIE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hws_reg.h" + +struct hwsmem_param { + u32 index; + u32 type; + u32 status; +}; + +struct hws_pix_state { + u32 width; + u32 height; + u32 fourcc; /* V4L2_PIX_FMT_* (YUYV only here) */ + u32 bytesperline; /* stride */ + u32 sizeimage; /* full frame */ + enum v4l2_field field; /* V4L2_FIELD_NONE or INTERLACED */ + enum v4l2_colorspace colorspace; /* e.g., REC709 */ + enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_DEFAULT */ + enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_LIM_RANGE */ + enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */ + bool interlaced; /* cached hardware state */ + u32 half_size; /* hardware half-frame size */ +}; + +#define UNSET (-1U) + +struct hws_pcie_dev; +struct hws_adapter; +struct hws_video; + +struct hwsvideo_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + int slot; +}; + +struct hws_video { + /* Linkage */ + struct hws_pcie_dev *parent; + struct video_device *video_device; + + struct vb2_queue buffer_queue; + struct list_head capture_queue; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + /* Locking */ + struct mutex state_lock; + spinlock_t irq_lock; /* Protects capture_queue and active buffers. */ + + /* Indices */ + int channel_index; + + /* Color controls */ + int current_brightness; + int current_contrast; + int current_saturation; + int current_hue; + + /* V4L2 controls */ + struct v4l2_ctrl_handler control_handler; + struct v4l2_ctrl *ctrl_brightness; + struct v4l2_ctrl *ctrl_contrast; + struct v4l2_ctrl *ctrl_saturation; + struct v4l2_ctrl *ctrl_hue; + + /* Capture queue status */ + struct hws_pix_state pix; + struct v4l2_dv_timings cur_dv_timings; /* last configured/notified DV timings */ + u32 current_fps; /* Hz, updated by mode changes, not by read-only queries */ + + /* Per-channel capture state */ + bool cap_active; + bool stop_requested; + u8 last_buf_half_toggle; + bool half_seen; + atomic_t sequence_number; + u32 queued_count; + + /* Timeout and error handling */ + u32 timeout_count; + u32 error_count; + + bool window_valid; + u32 last_dma_hi; + u32 last_dma_page; + u32 last_pci_addr; + u32 last_half16; + + /* Misc counters */ + int signal_loss_cnt; +}; + +static inline void hws_set_current_dv_timings(struct hws_video *vid, + u32 width, u32 height, + bool interlaced) +{ + if (!vid) + return; + + vid->cur_dv_timings = (struct v4l2_dv_timings) { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = width, + .height = height, + .interlaced = interlaced, + }, + }; +} + +struct hws_scratch_dma { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +struct hws_pcie_dev { + /* Core objects */ + struct pci_dev *pdev; + struct hws_video video[MAX_VID_CHANNELS]; + + /* BAR and workqueues */ + void __iomem *bar0_base; + + /* Device identity and capabilities */ + u16 vendor_id; + u16 device_id; + u16 device_ver; + u16 hw_ver; + u32 sub_ver; + u32 port_id; + /* Tri-state support flag used by set_video_format_size(). */ + u32 support_yv12; + u32 max_hw_video_buf_sz; + u8 max_channels; + u8 cur_max_video_ch; + bool start_run; + + bool buf_allocated; + + /* V4L2 framework objects */ + struct v4l2_device v4l2_device; + + /* Kernel thread */ + struct task_struct *main_task; + struct hws_scratch_dma scratch_vid[MAX_VID_CHANNELS]; + + bool suspended; + int irq; + + /* Error flags */ + int pci_lost; +}; + +#endif diff --git a/drivers/media/pci/hws/hws_irq.c b/drivers/media/pci/hws/hws_irq.c new file mode 100644 index 000000000000..eebb4b8a5cd5 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hws_irq.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws.h" + +#define MAX_INT_LOOPS 100 + +static bool hws_toggle_debug; +module_param_named(toggle_debug, hws_toggle_debug, bool, 0644); +MODULE_PARM_DESC(toggle_debug, + "Read toggle registers in IRQ handler for debug logging"); + +static int hws_arm_next(struct hws_pcie_dev *hws, u32 ch) +{ + struct hws_video *v = &hws->video[ch]; + unsigned long flags; + struct hwsvideo_buffer *buf; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d queued=%d\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + !list_empty(&v->capture_queue)); + + if (READ_ONCE(hws->suspended)) { + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): suspended\n", ch); + return -EBUSY; + } + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) { + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d -> cancel\n", ch, + v->stop_requested, v->cap_active); + return -ECANCELED; + } + + spin_lock_irqsave(&v->irq_lock, flags); + if (list_empty(&v->capture_queue)) { + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): queue empty\n", ch); + return -EAGAIN; + } + + buf = list_first_entry(&v->capture_queue, struct hwsvideo_buffer, list); + list_del_init(&buf->list); /* keep buffer safe for later cleanup */ + if (v->queued_count) + v->queued_count--; + v->active = buf; + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): picked buffer %p\n", ch, + buf); + + /* Publish descriptor(s) before doorbell/MMIO kicks. */ + wmb(); + + /* Avoid MMIO during suspend */ + if (READ_ONCE(hws->suspended)) { + unsigned long f; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): suspended after pick\n", ch); + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add(&buf->list, &v->capture_queue); + v->queued_count++; + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, f); + return -EBUSY; + } + + /* Also program the DMA address register directly */ + { + dma_addr_t dma_addr = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, ch, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + } + + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): programmed buffer %p\n", ch, + buf); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return 0; +} + +static void hws_video_handle_vdone(struct hws_video *v) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned int ch = v->channel_index; + struct hwsvideo_buffer *done; + unsigned long flags; + bool promoted = false; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): stop=%d cap=%d active=%p\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + v->active); + + int ret; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): entry stop=%d cap=%d\n", ch, + v->stop_requested, v->cap_active); + if (READ_ONCE(hws->suspended)) + return; + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return; + + spin_lock_irqsave(&v->irq_lock, flags); + done = v->active; + if (done && v->next_prepared) { + v->active = v->next_prepared; + v->next_prepared = NULL; + promoted = true; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 1) Complete the buffer the HW just finished (if any) */ + if (done) { + struct vb2_v4l2_buffer *vb2v = &done->vb; + size_t expected = v->pix.sizeimage; + size_t plane_size = vb2_plane_size(&vb2v->vb2_buf, 0); + + if (expected > plane_size) { + dev_warn_ratelimited(&hws->pdev->dev, + "bh_video(ch=%u): sizeimage %zu > plane %zu, dropping seq=%u\n", + ch, expected, plane_size, + (u32)atomic_read(&v->sequence_number) + 1); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_ERROR); + goto arm_next; + } + vb2_set_plane_payload(&vb2v->vb2_buf, 0, expected); + + dma_rmb(); /* device writes visible before userspace sees it */ + + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): DONE buf=%p seq=%u half_seen=%d toggle=%u\n", + ch, done, vb2v->sequence, v->half_seen, + v->last_buf_half_toggle); + + if (!promoted) + v->active = NULL; /* channel no longer owns this buffer */ + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + + if (READ_ONCE(hws->suspended)) + return; + + if (promoted) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): promoted pre-armed buffer active=%p\n", + ch, v->active); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return; + } + +arm_next: + /* 2) Immediately arm the next queued buffer (if present) */ + ret = hws_arm_next(hws, ch); + if (ret == -EAGAIN) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): no queued buffer to arm\n", ch); + return; + } + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): armed next buffer, active=%p\n", ch, + v->active); + /* On success the engine now points at v->active's DMA address */ +} + +irqreturn_t hws_irq_handler(int irq, void *info) +{ + struct hws_pcie_dev *pdx = info; + u32 int_state; + + dev_dbg(&pdx->pdev->dev, "irq: entry\n"); + if (pdx->bar0_base) { + dev_dbg(&pdx->pdev->dev, + "irq: INT_EN=0x%08x INT_STATUS=0x%08x\n", + readl(pdx->bar0_base + INT_EN_REG_BASE), + readl(pdx->bar0_base + HWS_REG_INT_STATUS)); + } + + /* Fast path: if suspended, quietly ack and exit */ + if (READ_ONCE(pdx->suspended)) { + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (int_state) { + writel(int_state, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + return int_state ? IRQ_HANDLED : IRQ_NONE; + } + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (!int_state || int_state == 0xFFFFFFFF) { + dev_dbg(&pdx->pdev->dev, + "irq: spurious or device-gone int_state=0x%08x\n", + int_state); + return IRQ_NONE; + } + dev_dbg(&pdx->pdev->dev, "irq: entry INT_STATUS=0x%08x\n", int_state); + + /* Loop until all pending bits are serviced (max 100 iterations) */ + for (u32 cnt = 0; int_state && cnt < MAX_INT_LOOPS; ++cnt) { + for (unsigned int ch = 0; ch < pdx->cur_max_video_ch; ++ch) { + u32 vbit = HWS_INT_VDONE_BIT(ch); + + if (!(int_state & vbit)) + continue; + + if (READ_ONCE(pdx->video[ch].cap_active) && + !READ_ONCE(pdx->video[ch].stop_requested)) { + if (hws_toggle_debug) { + u32 toggle = + readl_relaxed(pdx->bar0_base + + HWS_REG_VBUF_TOGGLE(ch)) & 0x01; + WRITE_ONCE(pdx->video[ch].last_buf_half_toggle, + toggle); + } + dma_rmb(); + WRITE_ONCE(pdx->video[ch].half_seen, true); + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u toggle=%u handling inline (cap=%d)\n", + ch, + READ_ONCE(pdx->video[ch].last_buf_half_toggle), + READ_ONCE(pdx->video[ch].cap_active)); + hws_video_handle_vdone(&pdx->video[ch]); + } else { + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u ignored (cap=%d stop=%d)\n", + ch, + READ_ONCE(pdx->video[ch].cap_active), + READ_ONCE(pdx->video[ch].stop_requested)); + } + + writel(vbit, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + + /* Re-read in case new interrupt bits popped while processing */ + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + dev_dbg(&pdx->pdev->dev, + "irq: loop cnt=%u new INT_STATUS=0x%08x\n", cnt, + int_state); + if (cnt + 1 == MAX_INT_LOOPS) + dev_warn_ratelimited(&pdx->pdev->dev, + "IRQ storm? status=0x%08x\n", + int_state); + } + + return IRQ_HANDLED; +} diff --git a/drivers/media/pci/hws/hws_irq.h b/drivers/media/pci/hws/hws_irq.h new file mode 100644 index 000000000000..a42867aa0c46 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_INTERRUPT_H +#define HWS_INTERRUPT_H + +#include +#include "hws.h" + +irqreturn_t hws_irq_handler(int irq, void *info); + +#endif /* HWS_INTERRUPT_H */ diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c new file mode 100644 index 000000000000..30bb7d34465b --- /dev/null +++ b/drivers/media/pci/hws/hws_pci.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define DRV_NAME "hws" +#define HWS_BUSY_POLL_DELAY_US 10 +#define HWS_BUSY_POLL_TIMEOUT_US 1000000 + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +/* register layout inside HWS_REG_DEVICE_INFO */ +#define DEVINFO_VER GENMASK(7, 0) +#define DEVINFO_SUBVER GENMASK(15, 8) +#define DEVINFO_YV12 GENMASK(31, 28) +#define DEVINFO_HWKEY GENMASK(27, 24) +#define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */ + +#define MAKE_ENTRY(__vend, __chip, __subven, __subdev, __configptr) \ + { .vendor = (__vend), \ + .device = (__chip), \ + .subvendor = (__subven), \ + .subdevice = (__subdev), \ + .driver_data = (unsigned long)(__configptr) } + +/* + * PCI IDs for HWS family cards. + * + * The subsystem IDs are fixed at 0x8888:0x0007 for this family. Some boards + * enumerate with vendor ID 0x8888 or 0x1f33. Exact SKU names are not fully + * pinned down yet; update these comments when vendor documentation or INF + * strings are available. + */ +static const struct pci_device_id hws_pci_table[] = { + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x9534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8554, 0x8888, 0x0007, NULL), + + /* HWS 2x2 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8524, 0x8888, 0x0007, NULL), + /* HWS 2x2 SDI family. */ + MAKE_ENTRY(0x1F33, 0x6524, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8504, 0x8888, 0x0007, NULL), + /* HWS X4 SDI family. */ + MAKE_ENTRY(0x8888, 0x6504, 0x8888, 0x0007, NULL), + + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x8532, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8512, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8501, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x6502, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8504, 0x8888, 0x0007, NULL), + /* HWS 2x2 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8524, 0x8888, 0x0007, NULL), + + {} +}; + +static void enable_pcie_relaxed_ordering(struct pci_dev *dev) +{ + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); +} + +static void hws_configure_hardware_capabilities(struct hws_pcie_dev *hdev) +{ + u16 id = hdev->device_id; + + /* select per-chip channel counts */ + switch (id) { + case 0x9534: + case 0x6524: + case 0x8524: + case 0x8504: + case 0x6504: + hdev->cur_max_video_ch = 4; + break; + case 0x8532: + hdev->cur_max_video_ch = 2; + break; + case 0x8512: + case 0x6502: + hdev->cur_max_video_ch = 2; + break; + case 0x8501: + hdev->cur_max_video_ch = 1; + break; + default: + hdev->cur_max_video_ch = 4; + break; + } + + /* universal buffer capacity */ + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + + /* decide hardware-version and program DMA max size if needed */ + if (hdev->device_ver > 121) { + if (id == 0x8501 && hdev->device_ver == 122) { + hdev->hw_ver = 0; + } else { + hdev->hw_ver = 1; + u32 dma_max = (u32)(MAX_VIDEO_SCALER_SIZE / 16); + + writel(dma_max, hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + /* readback to flush posted MMIO write */ + (void)readl(hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + } + } else { + hdev->hw_ver = 0; + } +} + +static void hws_stop_device(struct hws_pcie_dev *hws); + +static void hws_log_lifecycle_snapshot(struct hws_pcie_dev *hws, + const char *action, + const char *phase) +{ + struct device *dev; + u32 int_en, int_status, vcap, sys_status, dec_mode; + + if (!hws || !hws->pdev) + return; + + dev = &hws->pdev->dev; + if (!hws->bar0_base) { + dev_dbg(dev, + "lifecycle:%s:%s bar0-unmapped suspended=%d start_run=%d pci_lost=%d irq=%d\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq); + return; + } + + int_en = readl(hws->bar0_base + INT_EN_REG_BASE); + int_status = readl(hws->bar0_base + HWS_REG_INT_STATUS); + vcap = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + sys_status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + dec_mode = readl(hws->bar0_base + HWS_REG_DEC_MODE); + + dev_dbg(dev, + "lifecycle:%s:%s suspended=%d start_run=%d pci_lost=%d irq=%d INT_EN=0x%08x INT_STATUS=0x%08x VCAP=0x%08x SYS=0x%08x DEC=0x%08x\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq, int_en, int_status, vcap, + sys_status, dec_mode); +} + +static int read_chip_id(struct hws_pcie_dev *hdev) +{ + u32 reg; + /* mirror PCI IDs for later switches */ + hdev->device_id = hdev->pdev->device; + hdev->vendor_id = hdev->pdev->vendor; + + reg = readl(hdev->bar0_base + HWS_REG_DEVICE_INFO); + + hdev->device_ver = FIELD_GET(DEVINFO_VER, reg); + hdev->sub_ver = FIELD_GET(DEVINFO_SUBVER, reg); + hdev->support_yv12 = FIELD_GET(DEVINFO_YV12, reg); + hdev->port_id = FIELD_GET(DEVINFO_PORTID, reg); + + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + hdev->max_channels = 4; + hdev->buf_allocated = false; + hdev->main_task = NULL; + hdev->start_run = false; + hdev->pci_lost = 0; + + writel(0x00, hdev->bar0_base + HWS_REG_DEC_MODE); + writel(0x10, hdev->bar0_base + HWS_REG_DEC_MODE); + + hws_configure_hardware_capabilities(hdev); + + dev_info(&hdev->pdev->dev, + "chip detected: ver=%u subver=%u port=%u yv12=%u\n", + hdev->device_ver, hdev->sub_ver, hdev->port_id, + hdev->support_yv12); + + return 0; +} + +static int main_ks_thread_handle(void *data) +{ + struct hws_pcie_dev *pdx = data; + + set_freezable(); + + while (!kthread_should_stop()) { + /* If we're suspending, don't touch hardware; just sleep/freeze. */ + if (READ_ONCE(pdx->suspended)) { + try_to_freeze(); + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + continue; + } + + /* avoid MMIO when suspended (guarded above) */ + check_video_format(pdx); + + try_to_freeze(); /* cooperate with freezer each loop */ + + /* Sleep 1s or until signaled to wake/stop */ + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + } + + dev_dbg(&pdx->pdev->dev, "%s: exiting\n", __func__); + return 0; +} + +static void hws_stop_kthread_action(void *data) +{ + struct hws_pcie_dev *hws = data; + struct task_struct *t; + u64 start_ns; + + if (!hws) + return; + + t = READ_ONCE(hws->main_task); + if (!IS_ERR_OR_NULL(t)) { + start_ns = ktime_get_mono_fast_ns(); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:begin task=%s[%d]\n", + t->comm, t->pid); + WRITE_ONCE(hws->main_task, NULL); + kthread_stop(t); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); + } +} + +static int hws_alloc_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + /* 64 KiB is plenty for a safe dummy; hardware needs 64-byte alignment. */ + const size_t need = ALIGN(64 * 1024, 64); + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { +#if defined(CONFIG_HAS_DMA) /* normal on PCIe platforms */ + void *cpu = dma_alloc_coherent(&hws->pdev->dev, need, + &hws->scratch_vid[ch].dma, + GFP_KERNEL); +#else + void *cpu = NULL; +#endif + if (!cpu) { + dev_warn(&hws->pdev->dev, + "scratch: dma_alloc_coherent failed ch=%d\n", ch); + /* not fatal: free earlier ones and continue without seeding */ + while (--ch >= 0) { + if (hws->scratch_vid[ch].cpu) + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + return -ENOMEM; + } + hws->scratch_vid[ch].cpu = cpu; + hws->scratch_vid[ch].size = need; + } + return 0; +} + +static void hws_free_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) { + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + } +} + +static void hws_seed_channel(struct hws_pcie_dev *hws, int ch) +{ + dma_addr_t paddr = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(paddr); + u32 hi = upper_32_bits(paddr); + u32 pci_addr = lo & PCI_E_BAR_ADD_LOWMASK; + + lo &= PCI_E_BAR_ADD_MASK; + + /* Program 64-bit BAR remap entry for this channel (table @ 0x208 + ch * 8) */ + writel_relaxed(hi, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8); + writel_relaxed(lo, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8 + + PCIE_BARADDROFSIZE); + + /* Program capture engine per-channel base/half */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half size: use either the current format's half or half of scratch. */ + { + u32 half = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + + writel_relaxed(half / 16, + hws->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush posted writes */ +} + +static void hws_seed_all_channels(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) + hws_seed_channel(hws, ch); + } +} + +static void hws_irq_mask_gate(struct hws_pcie_dev *hws) +{ + writel(0x00000000, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_unmask_gate(struct hws_pcie_dev *hws) +{ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_clear_pending(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_block_hotpaths(struct hws_pcie_dev *hws) +{ + WRITE_ONCE(hws->suspended, true); + if (hws->irq >= 0) + disable_irq(hws->irq); + + if (!hws->bar0_base) + return; + + hws_irq_mask_gate(hws); + hws_irq_clear_pending(hws); +} + +static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct hws_pcie_dev *hws; + int i, ret, irq; + unsigned long irqf = 0; + bool v4l2_registered = false; + + /* devres-backed device object */ + hws = devm_kzalloc(&pdev->dev, sizeof(*hws), GFP_KERNEL); + if (!hws) + return -ENOMEM; + + hws->pdev = pdev; + hws->irq = -1; + hws->suspended = false; + pci_set_drvdata(pdev, hws); + + /* 1) Enable device + bus mastering (managed) */ + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_enable_device\n"); + pci_set_master(pdev); + + /* 2) Map BAR0 (managed) */ + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_iomap_regions BAR0\n"); + hws->bar0_base = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_warn(&pdev->dev, + "64-bit DMA mask unavailable, falling back to 32-bit (%d)\n", + ret); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "No suitable DMA configuration\n"); + } else { + dev_dbg(&pdev->dev, "Using 64-bit DMA mask\n"); + } + + /* 3) Apply optional PCIe tuning. */ + enable_pcie_relaxed_ordering(pdev); +#ifdef CONFIG_ARCH_TI816X + pcie_set_readrq(pdev, 128); +#endif + + /* 4) Identify chip & capabilities */ + read_chip_id(hws); + dev_info(&pdev->dev, "Device VID=0x%04x DID=0x%04x\n", + pdev->vendor, pdev->device); + hws_init_video_sys(hws, false); + + /* 5) Init channels (video state, locks, vb2, ctrls) */ + for (i = 0; i < hws->max_channels; i++) { + ret = hws_video_init_channel(hws, i); + if (ret) { + dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i); + goto err_unwind_channels; + } + } + + /* 6) Allocate scratch DMA and seed BAR table + channel base/half (legacy SetDMAAddress) */ + ret = hws_alloc_seed_buffers(hws); + if (!ret) + hws_seed_all_channels(hws); + + /* 7) Start-run sequence. */ + hws_init_video_sys(hws, false); + + /* A) Force legacy INTx; legacy used request_irq(pdev->irq, ..., IRQF_SHARED) */ + pci_intx(pdev, 1); + irqf = IRQF_SHARED; + irq = pdev->irq; + hws->irq = irq; + dev_info(&pdev->dev, "IRQ mode: legacy INTx (shared), irq=%d\n", irq); + + /* B) Mask the device's global/bridge gate (INT_EN_REG_BASE) */ + hws_irq_mask_gate(hws); + + /* C) Clear any sticky pending interrupt status (W1C) before we arm the line */ + hws_irq_clear_pending(hws); + + /* D) Request the legacy shared interrupt line (no vectors/MSI/MSI-X) */ + ret = devm_request_irq(&pdev->dev, irq, hws_irq_handler, irqf, + dev_name(&pdev->dev), hws); + if (ret) { + dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", irq, ret); + goto err_unwind_channels; + } + + /* E) Set the global interrupt enable bit in main control register */ + { + u32 ctl_reg = readl(hws->bar0_base + HWS_REG_CTL); + + ctl_reg |= HWS_CTL_IRQ_ENABLE_BIT; + writel(ctl_reg, hws->bar0_base + HWS_REG_CTL); + (void)readl(hws->bar0_base + HWS_REG_CTL); /* flush write */ + dev_info(&pdev->dev, "Global IRQ enable bit set in control register\n"); + } + + /* F) Open the global gate just like legacy did */ + hws_irq_unmask_gate(hws); + dev_info(&pdev->dev, "INT_EN_GATE readback=0x%08x\n", + readl(hws->bar0_base + INT_EN_REG_BASE)); + + /* 11) Register V4L2 */ + ret = hws_video_register(hws); + if (ret) { + dev_err(&pdev->dev, "video_register: %d\n", ret); + goto err_unwind_channels; + } + v4l2_registered = true; + + /* 12) Background monitor thread (managed) */ + hws->main_task = kthread_run(main_ks_thread_handle, hws, "hws-mon"); + if (IS_ERR(hws->main_task)) { + ret = PTR_ERR(hws->main_task); + hws->main_task = NULL; + dev_err(&pdev->dev, "kthread_run: %d\n", ret); + goto err_unregister_va; + } + ret = devm_add_action_or_reset(&pdev->dev, hws_stop_kthread_action, hws); + if (ret) { + dev_err(&pdev->dev, "devm_add_action kthread_stop: %d\n", ret); + goto err_unregister_va; /* reset already stopped the thread */ + } + + /* 13) Final: show the line is armed */ + dev_info(&pdev->dev, "irq handler installed on irq=%d\n", irq); + return 0; + +err_unregister_va: + hws_stop_device(hws); + hws_video_unregister(hws); + hws_free_seed_buffers(hws); + return ret; +err_unwind_channels: + hws_free_seed_buffers(hws); + if (!v4l2_registered) { + while (--i >= 0) + hws_video_cleanup_channel(hws, i); + } + return ret; +} + +static int hws_check_busy(struct hws_pcie_dev *pdx) +{ + void __iomem *reg = pdx->bar0_base + HWS_REG_SYS_STATUS; + u32 val; + int ret; + + /* poll until !(val & BUSY_BIT), sleeping HWS_BUSY_POLL_DELAY_US between reads */ + ret = readl_poll_timeout(reg, val, !(val & HWS_SYS_DMA_BUSY_BIT), + HWS_BUSY_POLL_DELAY_US, + HWS_BUSY_POLL_TIMEOUT_US); + if (ret) { + dev_err(&pdx->pdev->dev, + "SYS_STATUS busy bit never cleared (0x%08x)\n", val); + return -ETIMEDOUT; + } + + return 0; +} + +static void hws_stop_dsp(struct hws_pcie_dev *hws) +{ + u32 status; + + /* Read the decoder mode/status register */ + status = readl(hws->bar0_base + HWS_REG_DEC_MODE); + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + + /* If the device looks unplugged/stuck, bail out */ + if (status == 0xFFFFFFFF) + return; + + /* Tell the DSP to stop */ + writel(0x10, hws->bar0_base + HWS_REG_DEC_MODE); + + if (hws_check_busy(hws)) + dev_warn(&hws->pdev->dev, "DSP busy timeout on stop\n"); + /* Disable video capture engine in the DSP */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); +} + +/* Publish stop so ISR/BH will not touch video buffers anymore. */ +static void hws_publish_stop_flags(struct hws_pcie_dev *hws) +{ + unsigned int i; + + for (i = 0; i < hws->cur_max_video_ch; ++i) { + struct hws_video *v = &hws->video[i]; + + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + } + + smp_wmb(); /* make flags visible before we touch MMIO/queues */ +} + +/* Drain engines + ISR/BH after flags are published. */ +static void hws_drain_after_stop(struct hws_pcie_dev *hws) +{ + u32 ackmask = 0; + unsigned int i; + u64 start_ns = ktime_get_mono_fast_ns(); + + /* Mask device enables: no new DMA starts. */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush */ + + /* Let any in-flight DMAs finish (best-effort). */ + (void)hws_check_busy(hws); + + /* Ack any latched VDONE. */ + for (i = 0; i < hws->cur_max_video_ch; ++i) + ackmask |= HWS_INT_VDONE_BIT(i); + if (ackmask) { + writel(ackmask, hws->bar0_base + HWS_REG_INT_STATUS); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + /* Ensure no hard IRQ is still running. */ + if (hws->irq >= 0) + synchronize_irq(hws->irq); + + dev_dbg(&hws->pdev->dev, "lifecycle:drain-after-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +static void hws_stop_device(struct hws_pcie_dev *hws) +{ + u32 status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + u64 start_ns = ktime_get_mono_fast_ns(); + bool live = status != 0xFFFFFFFF; + + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + if (!live) { + hws->pci_lost = true; + goto out; + } + hws_log_lifecycle_snapshot(hws, "stop-device", "begin"); + + /* Make ISR/BH a no-op, then drain engines/IRQ. */ + hws_publish_stop_flags(hws); + hws_drain_after_stop(hws); + + /* 1) Stop the on-board DSP */ + hws_stop_dsp(hws); + +out: + hws->start_run = false; + if (live) + hws_log_lifecycle_snapshot(hws, "stop-device", "end"); + else + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:device-lost\n"); + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:done (%lluus)\n", + hws_elapsed_us(start_ns)); + dev_dbg(&hws->pdev->dev, "%s: complete\n", __func__); +} + +static int hws_quiesce_for_transition(struct hws_pcie_dev *hws, + const char *action, + bool stop_thread) +{ + struct device *dev = &hws->pdev->dev; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + int vret; + + hws_log_lifecycle_snapshot(hws, action, "begin"); + + step_ns = ktime_get_mono_fast_ns(); + hws_block_hotpaths(hws); + dev_dbg(dev, "lifecycle:%s:block-hotpaths (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "blocked"); + + if (stop_thread) { + step_ns = ktime_get_mono_fast_ns(); + hws_stop_kthread_action(hws); + dev_dbg(dev, "lifecycle:%s:stop-kthread (%lluus)\n", action, + hws_elapsed_us(step_ns)); + } + + step_ns = ktime_get_mono_fast_ns(); + vret = hws_video_quiesce(hws, action); + dev_dbg(dev, "lifecycle:%s:video-quiesce ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(step_ns)); + if (vret) + dev_warn(dev, "lifecycle:%s video quiesce returned %d\n", + action, vret); + + step_ns = ktime_get_mono_fast_ns(); + hws_stop_device(hws); + dev_dbg(dev, "lifecycle:%s:stop-device (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "end"); + dev_dbg(dev, "lifecycle:%s:quiesce-done ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(start_ns)); + + return vret; +} + +static void hws_remove(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + u64 start_ns; + + if (!hws) + return; + + start_ns = ktime_get_mono_fast_ns(); + dev_info(&pdev->dev, "lifecycle:remove begin\n"); + hws_log_lifecycle_snapshot(hws, "remove", "begin"); + + /* Stop the monitor thread before tearing down V4L2/vb2 objects. */ + hws_block_hotpaths(hws); + hws_stop_kthread_action(hws); + + /* Stop hardware and capture cleanly. */ + hws_stop_device(hws); + + /* Unregister V4L2 resources. */ + hws_video_unregister(hws); + + /* Release seeded DMA buffers */ + hws_free_seed_buffers(hws); + /* kthread is stopped by the devm action registered in probe. */ + hws_log_lifecycle_snapshot(hws, "remove", "end"); + dev_info(&pdev->dev, "lifecycle:remove done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +#ifdef CONFIG_PM_SLEEP +static int hws_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_suspend begin\n"); + vret = hws_quiesce_for_transition(hws, "pm_suspend", false); + + step_ns = ktime_get_mono_fast_ns(); + pci_save_state(pdev); + pci_clear_master(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + dev_dbg(dev, "lifecycle:pm_suspend:pci-d3hot (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(dev, "lifecycle:pm_suspend done ret=%d (%lluus)\n", vret, + hws_elapsed_us(start_ns)); + + return 0; +} + +static int hws_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int ret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_resume begin\n"); + + /* Back to D0 and re-enable the function */ + step_ns = ktime_get_mono_fast_ns(); + pci_set_power_state(pdev, PCI_D0); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "pci_enable_device: %d\n", ret); + return ret; + } + pci_restore_state(pdev); + pci_set_master(pdev); + dev_dbg(dev, "lifecycle:pm_resume:pci-enable (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* Reapply any PCIe tuning lost across D3 */ + enable_pcie_relaxed_ordering(pdev); + + /* Reinitialize chip-side capabilities / registers */ + step_ns = ktime_get_mono_fast_ns(); + read_chip_id(hws); + /* Re-seed BAR remaps/DMA windows and restart the capture core */ + hws_seed_all_channels(hws); + hws_init_video_sys(hws, true); + hws_irq_clear_pending(hws); + dev_dbg(dev, "lifecycle:pm_resume:chip-reinit (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* IRQs can be re-enabled now that MMIO is sane */ + step_ns = ktime_get_mono_fast_ns(); + if (hws->irq >= 0) + enable_irq(hws->irq); + + WRITE_ONCE(hws->suspended, false); + dev_dbg(dev, "lifecycle:pm_resume:irq-unsuspend (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* vb2: nothing mandatory; userspace will STREAMON again when ready */ + step_ns = ktime_get_mono_fast_ns(); + hws_video_pm_resume(hws); + dev_dbg(dev, "lifecycle:pm_resume:video-resume (%lluus)\n", + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, "pm_resume", "end"); + dev_info(dev, "lifecycle:pm_resume done (%lluus)\n", + hws_elapsed_us(start_ns)); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(hws_pm_ops, hws_pm_suspend, hws_pm_resume); +# define HWS_PM_OPS (&hws_pm_ops) +#else +# define HWS_PM_OPS NULL +#endif + +static void hws_shutdown(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + if (!hws) + return; + + dev_info(&pdev->dev, "lifecycle:pci_shutdown begin\n"); + vret = hws_quiesce_for_transition(hws, "pci_shutdown", true); + + step_ns = ktime_get_mono_fast_ns(); + pci_clear_master(pdev); + dev_dbg(&pdev->dev, "lifecycle:pci_shutdown:clear-master (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(&pdev->dev, "lifecycle:pci_shutdown done ret=%d (%lluus)\n", + vret, hws_elapsed_us(start_ns)); +} + +static struct pci_driver hws_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = hws_pci_table, + .probe = hws_probe, + .remove = hws_remove, + .shutdown = hws_shutdown, + .driver = { + .pm = HWS_PM_OPS, + }, +}; + +MODULE_DEVICE_TABLE(pci, hws_pci_table); + +static int __init pcie_hws_init(void) +{ + return pci_register_driver(&hws_pci_driver); +} + +static void __exit pcie_hws_exit(void) +{ + pci_unregister_driver(&hws_pci_driver); +} + +module_init(pcie_hws_init); +module_exit(pcie_hws_exit); + +MODULE_DESCRIPTION(DRV_NAME); +MODULE_AUTHOR("Ben Hoff "); +MODULE_AUTHOR("Sales "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h new file mode 100644 index 000000000000..e4fb4af44434 --- /dev/null +++ b/drivers/media/pci/hws/hws_reg.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _HWS_PCIE_REG_H +#define _HWS_PCIE_REG_H + +#include +#include + +#define XDMA_CHANNEL_NUM_MAX (1) +#define MAX_NUM_ENGINES (XDMA_CHANNEL_NUM_MAX * 2) + +#define PCIE_BARADDROFSIZE 4u + +#define PCI_BUS_ACCESS_BASE 0x00000000U +#define INT_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0134U) +#define PCIEBR_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0148U) +#define PCIE_INT_DEC_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0138U) + +#define HWS_INT_EN_MASK 0x0003FFFFU + +#define PCIEBAR_AXI_BASE 0x20000000U + +#define CTL_REG_ACC_BASE 0x0 +#define PCI_ADDR_TABLE_BASE CTL_REG_ACC_BASE + +#define CVBS_IN_BASE 0x00004000U +#define CVBS_IN_BUF_BASE (CVBS_IN_BASE + (16U * PCIE_BARADDROFSIZE)) +#define CVBS_IN_BUF_BASE2 (CVBS_IN_BASE + (50U * PCIE_BARADDROFSIZE)) + +/* 2 Mib */ +#define MAX_L_VIDEO_SIZE 0x200000U + +#define PCI_E_BAR_PAGE_SIZE 0x20000000 +#define PCI_E_BAR_ADD_MASK 0xE0000000 +#define PCI_E_BAR_ADD_LOWMASK 0x1FFFFFFF + +#define MAX_VID_CHANNELS 4 + +#define MAX_MM_VIDEO_SIZE SZ_4M + +#define MAX_VIDEO_HW_W 1920 +#define MAX_VIDEO_HW_H 1080 +#define MAX_VIDEO_SCALER_SIZE (1920U * 1080U * 2U) + +#define MIN_VAMP_BRIGHTNESS_UNITS 0 +#define MAX_VAMP_BRIGHTNESS_UNITS 0xff + +#define MIN_VAMP_CONTRAST_UNITS 0 +#define MAX_VAMP_CONTRAST_UNITS 0xff + +#define MIN_VAMP_SATURATION_UNITS 0 +#define MAX_VAMP_SATURATION_UNITS 0xff + +#define MIN_VAMP_HUE_UNITS 0 +#define MAX_VAMP_HUE_UNITS 0xff + +#define HWS_BRIGHTNESS_DEFAULT 0x80 +#define HWS_CONTRAST_DEFAULT 0x80 +#define HWS_SATURATION_DEFAULT 0x80 +#define HWS_HUE_DEFAULT 0x00 + +/* Core/global status. */ +#define HWS_REG_SYS_STATUS (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* bit3: DMA busy, bit2: int, ... */ + +#define HWS_SYS_DMA_BUSY_BIT BIT(3) /* 0x08 = DMA busy flag */ + +#define HWS_REG_DEC_MODE (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* Main control register */ +#define HWS_REG_CTL (CVBS_IN_BASE + 4 * PCIE_BARADDROFSIZE) +#define HWS_CTL_IRQ_ENABLE_BIT BIT(0) /* Global interrupt enable bit */ +/* Write 0x00 to fully reset decoder, + * set bit 31=1 to "start run", + * low byte=0x13 selects YUYV/BT.709/etc, + * in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing. + */ + +/* Per-channel done flags. */ +#define HWS_REG_INT_STATUS (CVBS_IN_BASE + 1 * PCIE_BARADDROFSIZE) +#define HWS_SYS_BUSY_BIT BIT(2) /* matches old 0x04 test */ + +/* Capture enable switches. */ +/* bit0-3: CH0-CH3 video enable */ +#define HWS_REG_VCAP_ENABLE (CVBS_IN_BASE + 2 * PCIE_BARADDROFSIZE) +/* bits0-3: signal present, bits8-11: interlace */ +#define HWS_REG_ACTIVE_STATUS (CVBS_IN_BASE + 5 * PCIE_BARADDROFSIZE) +/* bits0-3: HDCP detected */ +#define HWS_REG_HDCP_STATUS (CVBS_IN_BASE + 8 * PCIE_BARADDROFSIZE) +#define HWS_REG_DMA_MAX_SIZE (CVBS_IN_BASE + 9 * PCIE_BARADDROFSIZE) + +/* Buffer addresses (written once during init/reset). */ +/* Base of host-visible buffer. */ +#define HWS_REG_VBUF1_ADDR (CVBS_IN_BASE + 25 * PCIE_BARADDROFSIZE) +/* Per-channel DMA address. */ +#define HWS_REG_DMA_ADDR(ch) (CVBS_IN_BASE + (26 + (ch)) * PCIE_BARADDROFSIZE) + +/* Per-channel live buffer toggles (read-only). */ +#define HWS_REG_VBUF_TOGGLE(ch) (CVBS_IN_BASE + (32 + (ch)) * PCIE_BARADDROFSIZE) +/* + * Returns 0 or 1 = which half of the video ring the DMA engine is + * currently filling for channel *ch* (0-3). + */ + +/* Per-interrupt bits (video 0-3). */ +#define HWS_INT_VDONE_BIT(ch) BIT(ch) /* 0x01,0x02,0x04,0x08 */ + +#define HWS_REG_INT_ACK (CVBS_IN_BASE + 0x4000 + 1 * PCIE_BARADDROFSIZE) + +/* 16-bit W | 16-bit H. */ +#define HWS_REG_IN_RES(ch) (CVBS_IN_BASE + (90 + (ch) * 2) * PCIE_BARADDROFSIZE) +/* B|C|H|S packed bytes. */ +#define HWS_REG_BCHS(ch) (CVBS_IN_BASE + (91 + (ch) * 2) * PCIE_BARADDROFSIZE) + +/* Input fps. */ +#define HWS_REG_FRAME_RATE(ch) (CVBS_IN_BASE + (110 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out W|H. */ +#define HWS_REG_OUT_RES(ch) (CVBS_IN_BASE + (120 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out fps. */ +#define HWS_REG_OUT_FRAME_RATE(ch) (CVBS_IN_BASE + (130 + (ch)) * PCIE_BARADDROFSIZE) + +/* Device version/port ID/subversion register. */ +#define HWS_REG_DEVICE_INFO (CVBS_IN_BASE + 88 * PCIE_BARADDROFSIZE) +/* + * Reading this 32-bit word returns: + * bits 7:0 = "device version" + * bits 15:8 = "device sub-version" + * bits 23:24 = "HW key / port ID" etc. + * bits 31:28 = "support YV12" flags + */ + +/* Convenience aliases for individual channels. */ +#define HWS_REG_VBUF_TOGGLE_CH0 HWS_REG_VBUF_TOGGLE(0) +#define HWS_REG_VBUF_TOGGLE_CH1 HWS_REG_VBUF_TOGGLE(1) +#define HWS_REG_VBUF_TOGGLE_CH2 HWS_REG_VBUF_TOGGLE(2) +#define HWS_REG_VBUF_TOGGLE_CH3 HWS_REG_VBUF_TOGGLE(3) + +#endif /* _HWS_PCIE_REG_H */ diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c new file mode 100644 index 000000000000..0303e311ee4a --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_v4l2_ioctl.h" + +struct hws_dv_mode { + struct v4l2_dv_timings timings; + u32 refresh_hz; +}; + +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced); +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps); +static u32 hws_get_live_fps(struct hws_video *vid); +static u32 hws_input_status(struct hws_video *vid); +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings); + +static const struct hws_dv_mode hws_dv_modes[] = { + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 148500000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 30, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 720, + .hfrontporch = 110, + .hsync = 40, + .hbackporch = 220, + .vfrontporch = 5, + .vsync = 5, + .vbackporch = 20, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 576, + .interlaced = 0, + }, + }, + 50, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 800, + .height = 600, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 640, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1024, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 800, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 1024, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1360, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1440, + .height = 900, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1680, + .height = 1050, + .interlaced = 0, + }, + }, + 60, + }, + /* Portrait */ + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1080, + .height = 1920, + .interlaced = 0, + }, + }, + 60, + }, +}; + +static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes); + +/* YUYV: 16 bpp; align to 64 as you did elsewhere */ +static inline u32 hws_calc_bpl_yuyv(u32 w) { return ALIGN(w * 2, 64); } +static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; } +static inline u32 hws_calc_half_size(u32 sizeimage) +{ + return sizeimage / 2; +} + +static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch, + u8 br, u8 co, u8 hu, u8 sa) +{ + u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch)); + (void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */ +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_match_supported_dv(const struct v4l2_dv_timings *req) +{ + const struct v4l2_bt_timings *bt; + u32 fps; + + if (!req || req->type != V4L2_DV_BT_656_1120) + return NULL; + + bt = &req->bt; + fps = 0; + if (bt->pixelclock) { + u32 total_w = bt->width + bt->hfrontporch + bt->hsync + + bt->hbackporch; + u32 total_h = bt->height + bt->vfrontporch + bt->vsync + + bt->vbackporch; + + if (total_w && total_h) + fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock, + (u64)total_w * total_h); + } + if (fps) { + const struct hws_dv_mode *exact = + hws_find_dv_by_wh_fps(bt->width, bt->height, + !!bt->interlaced, fps); + if (exact) + return exact; + } + return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced); +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced) + return t; + } + return NULL; +} + +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced && + t->refresh_hz == fps) + return t; + } + return NULL; +} + +static bool hws_get_live_dv_geometry(struct hws_video *vid, + u32 *w, u32 *h, bool *interlaced) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return false; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index)); + if (!reg || reg == 0xFFFFFFFF) + return false; + + if (w) + *w = reg & 0xFFFF; + if (h) + *h = (reg >> 16) & 0xFFFF; + if (interlaced) { + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + *interlaced = !!(reg & BIT(8 + vid->channel_index)); + } + return true; +} + +static u32 hws_get_live_fps(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 fps; + + if (!vid) + return 0; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return 0; + + fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index)); + if (!fps || fps == 0xFFFFFFFF || fps > 240) + return 0; + + return fps; +} + +static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced) +{ + const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced); + + if (m && m->refresh_hz) + return m->refresh_hz; + /* Fallback to a sane default */ + return 60; +} + +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings) +{ + const struct hws_dv_mode *m; + + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (!m) + return -ENOLINK; + + *timings = m->timings; + return 0; +} + +static u32 hws_input_status(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return V4L2_IN_ST_NO_SIGNAL; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return V4L2_IN_ST_NO_SIGNAL; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + if (reg == 0xffffffff) + return V4L2_IN_ST_NO_SIGNAL; + + return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL; +} + +/* Query the *current detected* DV timings on the input. + * If you have a real hardware detector, call it here; otherwise we + * derive from the cached pix state and map to the closest supported DV mode. + */ +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + hws_get_live_dv_geometry(vid, &w, &h, &interlace); + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + + return hws_fill_dv_timings(w, h, interlace, fps, timings); +} + +/* Enumerate the Nth supported DV timings from our static table. */ +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + u32 w, h; + u32 fps; + bool interlace; + + if (!edv) + return -EINVAL; + + if (edv->pad) + return -EINVAL; + + w = 0; + h = 0; + interlace = false; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (m) { + if (edv->index) + return -EINVAL; + edv->timings = m->timings; + return 0; + } + } + + if (edv->index >= hws_dv_modes_cnt) + return -EINVAL; + + edv->timings = hws_dv_modes[edv->index].timings; + return 0; +} + +/* Get the *currently configured* DV timings. */ +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + return hws_fill_dv_timings(w, h, interlace, fps, timings); + } + + *timings = vid->cur_dv_timings; + return 0; +} + +static inline void hws_set_colorimetry_state(struct hws_pix_state *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +/* Set DV timings: must match one of our supported modes. + * If buffers are queued and this implies a size change, we reject with -EBUSY. + * Otherwise we update pix state and (optionally) reprogram the HW. + */ +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + const struct v4l2_bt_timings *bt; + u32 new_w, new_h; + bool interlaced; + int ret = 0; + unsigned long was_busy; + u32 live_w, live_h; + u32 live_fps; + bool live_interlaced; + bool live_present; + + if (!timings) + return -EINVAL; + + m = hws_match_supported_dv(timings); + if (!m) + return -EINVAL; + + bt = &m->timings.bt; + if (bt->interlaced) + return -EINVAL; /* only progressive modes are advertised */ + new_w = bt->width; + new_h = bt->height; + interlaced = false; + + lockdep_assert_held(&vid->state_lock); + live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h, + &live_interlaced); + + /* If vb2 has active buffers and size would change, reject. */ + was_busy = vb2_is_busy(&vid->buffer_queue); + if (was_busy && + (new_w != vid->pix.width || new_h != vid->pix.height || + interlaced != vid->pix.interlaced)) { + ret = -EBUSY; + return ret; + } + + /* When a live input signal is present, the receiver owns the timing. + * Allow setting the already-active timings so v4l2-compliance can + * round-trip them, but reject attempts to retime the live source. + */ + if (live_present) { + live_fps = hws_get_live_fps(vid); + if (!live_fps) + live_fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(live_w, live_h, + live_interlaced); + if (live_w == new_w && live_h == new_h && + live_interlaced == interlaced && + m->refresh_hz == live_fps) + return 0; + return -EBUSY; + } + + /* Update software pixel state (and recalc sizes) */ + vid->pix.width = new_w; + vid->pix.height = new_h; + vid->pix.field = interlaced ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_NONE; + vid->pix.interlaced = interlaced; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + + hws_set_colorimetry_state(&vid->pix); + + /* Recompute stride, sizeimage, and half_size. */ + vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w); + vid->pix.sizeimage = hws_calc_size_yuyv(new_w, new_h); + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->cur_dv_timings = m->timings; + vid->current_fps = m->refresh_hz; + return ret; +} + +/* Report DV timings capability: advertise BT.656/1120 with + * the min/max WxH derived from our table and basic progressive support. + */ +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + u32 min_w = ~0U, min_h = ~0U; + u32 max_w = 0, max_h = 0; + size_t i, n = 0; + + if (!cap) + return -EINVAL; + + memset(cap, 0, sizeof(*cap)); + cap->type = V4L2_DV_BT_656_1120; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt; + + if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120) + continue; + n++; + + if (bt->width < min_w) + min_w = bt->width; + if (bt->height < min_h) + min_h = bt->height; + if (bt->width > max_w) + max_w = bt->width; + if (bt->height > max_h) + max_h = bt->height; + } + + /* If the table was empty, fail gracefully. */ + if (!n || min_w == U32_MAX) + return -ENODATA; + + cap->bt.min_width = min_w; + cap->bt.max_width = max_w; + cap->bt.min_height = min_h; + cap->bt.max_height = max_h; + + /* We support both CEA-861- and VESA-style modes in the list. */ + cap->bt.standards = + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT; + + /* Only progressive modes are advertised. */ + cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE; + + /* Leave pixelclock/porch limits unconstrained (0) for now. */ + return 0; +} + +static int hws_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hws_video *vid = + container_of(ctrl->handler, struct hws_video, control_handler); + struct hws_pcie_dev *pdx = vid->parent; + bool program = false; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + vid->current_brightness = ctrl->val; + program = true; + break; + case V4L2_CID_CONTRAST: + vid->current_contrast = ctrl->val; + program = true; + break; + case V4L2_CID_SATURATION: + vid->current_saturation = ctrl->val; + program = true; + break; + case V4L2_CID_HUE: + vid->current_hue = ctrl->val; + program = true; + break; + default: + return -EINVAL; + } + + if (program) { + hws_hw_write_bchs(pdx, vid->channel_index, + (u8)vid->current_brightness, + (u8)vid->current_contrast, + (u8)vid->current_hue, + (u8)vid->current_saturation); + } + return 0; +} + +const struct v4l2_ctrl_ops hws_ctrl_ops = { + .s_ctrl = hws_s_ctrl, +}; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct hws_video *vid = video_drvdata(file); + int vi_index = vid->channel_index + 1; /* keep it simple */ + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), + "AVMatrix HWS Capture %d", vi_index); + return 0; +} + +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; /* only one format */ + + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; +} + +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct hws_video *vid = video_drvdata(file); + + fmt->fmt.pix.width = vid->pix.width; + fmt->fmt.pix.height = vid->pix.height; + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt->fmt.pix.field = vid->pix.field; + fmt->fmt.pix.bytesperline = vid->pix.bytesperline; + fmt->fmt.pix.sizeimage = vid->pix.sizeimage; + fmt->fmt.pix.colorspace = vid->pix.colorspace; + fmt->fmt.pix.ycbcr_enc = vid->pix.ycbcr_enc; + fmt->fmt.pix.quantization = vid->pix.quantization; + fmt->fmt.pix.xfer_func = vid->pix.xfer_func; + return 0; +} + +static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hws_video *vid = file ? video_drvdata(file) : NULL; + struct hws_pcie_dev *pdev = vid ? vid->parent : NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 req_w = pix->width, req_h = pix->height; + u32 w, h, min_bpl, bpl; + size_t size; /* wider than u32 for overflow check */ + size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE; + + /* Only YUYV */ + pix->pixelformat = V4L2_PIX_FMT_YUYV; + + /* Defaults then clamp */ + w = (req_w ? req_w : 640); + h = (req_h ? req_h : 480); + if (w > MAX_VIDEO_HW_W) + w = MAX_VIDEO_HW_W; + if (h > MAX_VIDEO_HW_H) + h = MAX_VIDEO_HW_H; + if (!w) + w = 640; /* hard fallback in case macros are odd */ + if (!h) + h = 480; + + /* Field policy */ + pix->field = V4L2_FIELD_NONE; + + /* Stride policy for packed 16bpp, 64B align */ + min_bpl = ALIGN(w * 2, 64); + + /* Bound requested bpl to something sane, then align */ + bpl = pix->bytesperline; + if (bpl < min_bpl) { + bpl = min_bpl; + } else { + /* Cap at 16x width to avoid silly values that overflow sizeimage */ + u32 max_bpl = ALIGN(w * 2 * 16, 64); + + if (bpl > max_bpl) + bpl = max_bpl; + bpl = ALIGN(bpl, 64); + } + if (h && max_frame) { + size_t max_bpl_hw = max_frame / h; + + if (max_bpl_hw < min_bpl) + return -ERANGE; + max_bpl_hw = rounddown(max_bpl_hw, 64); + if (!max_bpl_hw) + return -ERANGE; + if (bpl > max_bpl_hw) { + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n", + bpl, max_bpl_hw, max_frame); + bpl = (u32)max_bpl_hw; + } + } + size = (size_t)bpl * (size_t)h; + if (size > max_frame) + return -ERANGE; + + pix->width = w; + pix->height = h; + pix->bytesperline = bpl; + pix->sizeimage = (u32)size; /* logical size, not page-aligned */ + + hws_set_colorimetry_fmt(pix); + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n", + pix->width, pix->height, pix->bytesperline, + pix->sizeimage, pix->field); + return 0; +} + +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct hws_video *vid = video_drvdata(file); + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Normalize the request */ + ret = hws_vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + /* Don't allow buffer layout changes while buffers are queued. */ + if (vb2_is_busy(&vid->buffer_queue)) { + if (f->fmt.pix.width != vid->pix.width || + f->fmt.pix.height != vid->pix.height || + f->fmt.pix.bytesperline != vid->pix.bytesperline) + return -EBUSY; + } + + /* Apply to driver state */ + vid->pix.width = f->fmt.pix.width; + vid->pix.height = f->fmt.pix.height; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.field = f->fmt.pix.field; + vid->pix.colorspace = f->fmt.pix.colorspace; + vid->pix.ycbcr_enc = f->fmt.pix.ycbcr_enc; + vid->pix.quantization = f->fmt.pix.quantization; + vid->pix.xfer_func = f->fmt.pix.xfer_func; + + /* Update negotiated buffer sizes. */ + vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */ + vid->pix.sizeimage = f->fmt.pix.sizeimage; /* logical */ + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->pix.interlaced = false; + /* S_FMT negotiates buffer layout only. Keep detector-owned DV timing + * state unchanged so a harmless restart cannot clobber the live FPS. + */ + /* Or: + * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false); + */ + + dev_dbg(&vid->parent->pdev->dev, + "s_fmt: w=%u h=%u bpl=%u size=%u\n", + vid->pix.width, vid->pix.height, vid->pix.bytesperline, + vid->pix.sizeimage); + + return 0; +} + +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param) +{ + struct hws_video *vid = video_drvdata(file); + u32 fps; + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : 60; + + /* HDMI receivers report the detected frame period, they don't set it. */ + param->parm.capture.capability = 0; + param->parm.capture.capturemode = 0; + param->parm.capture.timeperframe.numerator = 1; + param->parm.capture.timeperframe.denominator = fps; + param->parm.capture.extendedmode = 0; + param->parm.capture.readbuffers = 0; + + return 0; +} + +int hws_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct hws_video *vid = video_drvdata(file); + + if (input->index) + return -EINVAL; + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, KBUILD_MODNAME, sizeof(input->name)); + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = hws_input_status(vid); + + return 0; +} + +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index) +{ + *index = 0; + return 0; +} + +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h new file mode 100644 index 000000000000..53044f78d6fa --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_V4L2_IOCTL_H +#define HWS_V4L2_IOCTL_H + +#include +#include + +extern const struct v4l2_ctrl_ops hws_ctrl_ops; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap); +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f); +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f); +int hws_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms); +int hws_vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms); +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfps); +int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i); +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i); +int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap); +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); + +int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv); +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); + +#endif diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c new file mode 100644 index 000000000000..18e4bc6901d3 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.c @@ -0,0 +1,1490 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define HWS_REMAP_SLOT_OFF(ch) (0x208 + (ch) * 8) /* one 64-bit slot per ch */ +#define HWS_BUF_BASE_OFF(ch) (CVBS_IN_BUF_BASE + (ch) * PCIE_BARADDROFSIZE) +#define HWS_HALF_SZ_OFF(ch) (CVBS_IN_BUF_BASE2 + (ch) * PCIE_BARADDROFSIZE) + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace); +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace); +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch); +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch); +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced); + +/* DMA helper functions */ +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma); +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid); + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +static inline bool list_node_unlinked(const struct list_head *n) +{ + return n->next == LIST_POISON1 || n->prev == LIST_POISON2; +} + +static bool dma_window_verify; +module_param_named(dma_window_verify, dma_window_verify, bool, 0644); +MODULE_PARM_DESC(dma_window_verify, + "Read back DMA window registers after programming (debug)"); + +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag) +{ + iowrite32(lower_32_bits(dma), hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + dev_dbg(&hws->pdev->dev, "dma_doorbell ch%u: dma=0x%llx tag=%s\n", ch, + (u64)dma, tag ? tag : ""); +} + +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + struct hws_pcie_dev *hws = vid->parent; + unsigned int ch = vid->channel_index; + u32 table_off = HWS_REMAP_SLOT_OFF(ch); + u32 lo = lower_32_bits(dma); + u32 hi = upper_32_bits(dma); + u32 pci_addr = lo & addr_low_mask; + u32 page_lo = lo & addr_mask; + + bool wrote = false; + + /* Remap entry only when DMA crosses into a new 512 MB page */ + if (!vid->window_valid || vid->last_dma_hi != hi || + vid->last_dma_page != page_lo) { + writel(hi, hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + writel(page_lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + vid->last_dma_hi = hi; + vid->last_dma_page = page_lo; + wrote = true; + } + + /* Base pointer only needs low 29 bits */ + if (!vid->window_valid || vid->last_pci_addr != pci_addr) { + writel((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + vid->last_pci_addr = pci_addr; + wrote = true; + } + + /* Half-size only changes when resolution changes */ + if (!vid->window_valid || vid->last_half16 != vid->pix.half_size / 16) { + writel(vid->pix.half_size / 16, + hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + vid->last_half16 = vid->pix.half_size / 16; + wrote = true; + } + + vid->window_valid = true; + + if (dma_window_verify && wrote) { + u32 r_hi = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + u32 r_lo = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + u32 r_base = readl(hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + u32 r_half = readl(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + + dev_dbg(&hws->pdev->dev, + "ch%u remap verify: hi=0x%08x page_lo=0x%08x exp_page=0x%08x base=0x%08x exp_base=0x%08x half16B=0x%08x exp_half=0x%08x\n", + ch, r_hi, r_lo, page_lo, r_base, + (ch + 1) * PCIEBAR_AXI_BASE + pci_addr, r_half, + vid->pix.half_size / 16); + } else if (wrote) { + /* Flush posted writes before arming DMA */ + readl_relaxed(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + } +} + +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid) +{ + struct hwsvideo_buffer *buf; + + if (!vid || list_empty(&vid->capture_queue)) + return NULL; + + buf = list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (vid->queued_count) + vid->queued_count--; + return buf; +} + +void hws_prime_next_locked(struct hws_video *vid) +{ + struct hws_pcie_dev *hws; + struct hwsvideo_buffer *next; + dma_addr_t dma; + + if (!vid) + return; + + hws = vid->parent; + if (!hws || !hws->bar0_base) + return; + + if (!READ_ONCE(vid->cap_active) || !vid->active || vid->next_prepared) + return; + + next = hws_take_queued_buffer_locked(vid); + if (!next) + return; + + vid->next_prepared = next; + dma = vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, vid->channel_index, dma); + iowrite32(lower_32_bits(dma), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + dev_dbg(&hws->pdev->dev, + "ch%u pre-armed next buffer %p dma=0x%llx\n", + vid->channel_index, next, (u64)dma); +} + +static bool hws_force_no_signal_frame(struct hws_video *v, const char *tag) +{ + struct hws_pcie_dev *hws; + unsigned long flags; + struct hwsvideo_buffer *buf = NULL, *next = NULL; + bool have_next = false; + bool doorbell = false; + + if (!v) + return false; + hws = v->parent; + if (!hws || READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return false; + spin_lock_irqsave(&v->irq_lock, flags); + if (v->active) { + buf = v->active; + v->active = NULL; + buf->slot = 0; + } else if (!list_empty(&v->capture_queue)) { + buf = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (v->queued_count) + v->queued_count--; + buf->slot = 0; + } + if (v->next_prepared) { + next = v->next_prepared; + v->next_prepared = NULL; + next->slot = 0; + v->active = next; + have_next = true; + } else if (!list_empty(&v->capture_queue)) { + next = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&next->list); + if (v->queued_count) + v->queued_count--; + next->slot = 0; + v->active = next; + have_next = true; + } else { + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + if (!buf) + return false; + /* Complete buffer with a neutral frame so dequeuers keep running. */ + { + struct vb2_v4l2_buffer *vb2v = &buf->vb; + void *dst = vb2_plane_vaddr(&vb2v->vb2_buf, 0); + + if (dst) + memset(dst, 0x10, v->pix.sizeimage); + vb2_set_plane_payload(&vb2v->vb2_buf, 0, v->pix.sizeimage); + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + if (have_next && next) { + dma_addr_t dma = + vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma); + hws_set_dma_doorbell(hws, v->channel_index, dma, + tag ? tag : "nosignal_zero"); + doorbell = true; + } + if (doorbell) { + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + } + return true; +} + +static int hws_ctrls_init(struct hws_video *vid) +{ + struct v4l2_ctrl_handler *hdl = &vid->control_handler; + + /* Create BCHS controls. */ + v4l2_ctrl_handler_init(hdl, 4); + + vid->ctrl_brightness = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_BRIGHTNESS, + MIN_VAMP_BRIGHTNESS_UNITS, + MAX_VAMP_BRIGHTNESS_UNITS, 1, + HWS_BRIGHTNESS_DEFAULT); + + vid->ctrl_contrast = + v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_CONTRAST, + MIN_VAMP_CONTRAST_UNITS, MAX_VAMP_CONTRAST_UNITS, + 1, HWS_CONTRAST_DEFAULT); + + vid->ctrl_saturation = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_SATURATION, + MIN_VAMP_SATURATION_UNITS, + MAX_VAMP_SATURATION_UNITS, 1, + HWS_SATURATION_DEFAULT); + + vid->ctrl_hue = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_HUE, + MIN_VAMP_HUE_UNITS, + MAX_VAMP_HUE_UNITS, 1, + HWS_HUE_DEFAULT); + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + return 0; +} + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + + /* basic sanity */ + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return -EINVAL; + + vid = &pdev->video[ch]; + + /* hard reset the per-channel struct (safe here since we init everything next) */ + memset(vid, 0, sizeof(*vid)); + + /* identity */ + vid->parent = pdev; + vid->channel_index = ch; + + /* locks & lists */ + mutex_init(&vid->state_lock); + spin_lock_init(&vid->irq_lock); + INIT_LIST_HEAD(&vid->capture_queue); + atomic_set(&vid->sequence_number, 0); + vid->active = NULL; + + /* DMA watchdog removed; retain counters for diagnostics */ + vid->timeout_count = 0; + vid->error_count = 0; + + vid->queued_count = 0; + vid->window_valid = false; + + /* Default format. */ + vid->pix.width = 1920; + vid->pix.height = 1080; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.bytesperline = ALIGN(vid->pix.width * 2, 64); + vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height; + vid->pix.field = V4L2_FIELD_NONE; + vid->pix.colorspace = V4L2_COLORSPACE_REC709; + vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE; + vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; + vid->pix.interlaced = false; + vid->pix.half_size = vid->pix.sizeimage / 2; + hws_set_current_dv_timings(vid, vid->pix.width, + vid->pix.height, vid->pix.interlaced); + vid->current_fps = 60; + + /* color controls default (mid-scale) */ + vid->current_brightness = 0x80; + vid->current_contrast = 0x80; + vid->current_saturation = 0x80; + vid->current_hue = 0x80; + + /* capture state */ + vid->cap_active = false; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; + + /* Create BCHS + DV power-present as modern controls */ + { + int err = hws_ctrls_init(vid); + + if (err) { + dev_err(&pdev->pdev->dev, + "v4l2 ctrl init failed on ch%d: %d\n", ch, err); + return err; + } + } + + return 0; +} + +static void hws_video_drain_queue_locked(struct hws_video *vid) +{ + /* Return in-flight first */ + if (vid->active) { + vb2_buffer_done(&vid->active->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vid->active = NULL; + } + + /* Then everything queued */ + while (!list_empty(&vid->capture_queue)) { + struct hwsvideo_buffer *b = + list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, + list); + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void hws_video_release_registration(struct hws_video *vid) +{ + if (vid->buffer_queue.ops) { + vb2_queue_release(&vid->buffer_queue); + vid->buffer_queue.ops = NULL; + } + + if (!vid->video_device) + return; + + if (video_is_registered(vid->video_device)) + vb2_video_unregister_device(vid->video_device); + else + video_device_release(vid->video_device); + vid->video_device = NULL; +} + +static void hws_video_collect_done_locked(struct hws_video *vid, + struct list_head *done) +{ + struct hwsvideo_buffer *b; + + if (vid->active) { + if (!list_node_unlinked(&vid->active->list)) { + list_move_tail(&vid->active->list, done); + } else { + INIT_LIST_HEAD(&vid->active->list); + list_add_tail(&vid->active->list, done); + } + vid->active = NULL; + } + + if (vid->next_prepared) { + if (!list_node_unlinked(&vid->next_prepared->list)) { + list_move_tail(&vid->next_prepared->list, done); + } else { + INIT_LIST_HEAD(&vid->next_prepared->list); + list_add_tail(&vid->next_prepared->list, done); + } + vid->next_prepared = NULL; + } + + while (!list_empty(&vid->capture_queue)) { + b = list_first_entry(&vid->capture_queue, struct hwsvideo_buffer, + list); + list_move_tail(&b->list, done); + } + + vid->queued_count = 0; +} + +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + unsigned long flags; + + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return; + + vid = &pdev->video[ch]; + + /* 1) Stop HW best-effort for this channel */ + hws_enable_video_capture(vid->parent, vid->channel_index, false); + + /* 2) Flip software state so IRQ/BH will be no-ops if they run */ + WRITE_ONCE(vid->stop_requested, true); + WRITE_ONCE(vid->cap_active, false); + + /* 3) Ensure the IRQ handler finished any in-flight completions */ + if (vid->parent && vid->parent->irq >= 0) + synchronize_irq(vid->parent->irq); + + /* 4) Drain SW capture queue & in-flight under lock */ + spin_lock_irqsave(&vid->irq_lock, flags); + hws_video_drain_queue_locked(vid); + spin_unlock_irqrestore(&vid->irq_lock, flags); + + /* 5) Release VB2 queue if initialized */ + hws_video_release_registration(vid); + + /* 6) Free V4L2 controls */ + v4l2_ctrl_handler_free(&vid->control_handler); + + /* 8) Reset simple state; do not memset the whole struct here. */ + mutex_destroy(&vid->state_lock); + INIT_LIST_HEAD(&vid->capture_queue); + vid->active = NULL; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; +} + +/* Convenience cast */ +static inline struct hwsvideo_buffer *to_hwsbuf(struct vb2_buffer *vb) +{ + return container_of(to_vb2_v4l2_buffer(vb), struct hwsvideo_buffer, vb); +} + +static int hws_buf_init(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + INIT_LIST_HEAD(&b->list); + return 0; +} + +static void hws_buf_finish(struct vb2_buffer *vb) +{ + /* vb2 core handles cache maintenance for dma-contig buffers */ + (void)vb; +} + +static void hws_buf_cleanup(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + if (!list_empty(&b->list)) + list_del_init(&b->list); +} + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma) +{ + struct hws_video *vid = &hws->video[ch]; + + hws_program_dma_window(vid, dma); +} + +void hws_enable_video_capture(struct hws_pcie_dev *hws, unsigned int chan, + bool on) +{ + u32 status; + + if (!hws || hws->pci_lost || chan >= hws->max_channels) + return; + + status = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + status = on ? (status | BIT(chan)) : (status & ~BIT(chan)); + writel(status, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + + WRITE_ONCE(hws->video[chan].cap_active, on); + + dev_dbg(&hws->pdev->dev, "vcap %s ch%u (reg=0x%08x)\n", + on ? "ON" : "OFF", chan, status); +} + +static void hws_seed_dma_windows(struct hws_pcie_dev *hws) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + u32 table = 0x208; /* one 64-bit entry per channel */ + unsigned int ch; + + if (!hws || !hws->bar0_base) + return; + + /* If cur_max_video_ch is not set yet, default to max_channels. */ + if (!hws->cur_max_video_ch || hws->cur_max_video_ch > hws->max_channels) + hws->cur_max_video_ch = hws->max_channels; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++, table += 8) { + if (!hws->scratch_vid[ch].cpu) + continue; + + /* Program 64-bit BAR remap entry for this channel */ + { + dma_addr_t p = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(p) & addr_mask; + u32 hi = upper_32_bits(p); + u32 pci_addr_low = lower_32_bits(p) & addr_low_mask; + + writel_relaxed(hi, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table); + writel_relaxed(lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table + PCIE_BARADDROFSIZE); + + /* Per-channel AXI base + PCI low */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + + pci_addr_low, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half-frame length in /16 units. + * Prefer the current channel's computed half_size if available. + * Fall back to half of the probe-owned scratch buffer. + */ + { + u32 half_bytes = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + writel_relaxed(half_bytes / 16, + hws->bar0_base + + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + } + } + + /* Post writes so device sees them before we move on */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); +} + +static void hws_ack_all_irqs(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_open_irq_fabric(struct hws_pcie_dev *hws) +{ + /* Route all sources to vector 0. */ + writel(0x00000000, hws->bar0_base + PCIE_INT_DEC_REG_BASE); + (void)readl(hws->bar0_base + PCIE_INT_DEC_REG_BASE); + + /* Enable the PCIe bridge. */ + writel(0x00000001, hws->bar0_base + PCIEBR_EN_REG_BASE); + (void)readl(hws->bar0_base + PCIEBR_EN_REG_BASE); + + /* Open the global/bridge gate (legacy 0x3FFFF) */ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable) +{ + int i; + + if (hws->start_run && !enable) + return; + + /* 1) reset the decoder mode register to 0 */ + writel(0x00000000, hws->bar0_base + HWS_REG_DEC_MODE); + hws_seed_dma_windows(hws); + + /* 3) on a full reset, clear all per-channel status and indices */ + if (!enable) { + for (i = 0; i < hws->max_channels; i++) { + /* helpers to arm/disable capture engines */ + hws_enable_video_capture(hws, i, false); + } + } + + /* 4) Start run: set bit31, wait a bit, then program low 24 bits. */ + writel(0x80000000, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x80FFFFFF, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x13, hws->bar0_base + HWS_REG_DEC_MODE); + hws_ack_all_irqs(hws); + hws_open_irq_fabric(hws); + /* 6) record that we're now running */ + hws->start_run = true; +} + +int hws_check_card_status(struct hws_pcie_dev *hws) +{ + u32 status; + + if (!hws || !hws->bar0_base) + return -ENODEV; + + status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + + /* Common device-missing pattern. */ + if (status == 0xFFFFFFFF) { + hws->pci_lost = true; + dev_err(&hws->pdev->dev, "PCIe device not responding\n"); + return -ENODEV; + } + + /* If RUN/READY bit (bit0) is not set, reinitialize the video core. */ + if (!(status & BIT(0))) { + dev_dbg(&hws->pdev->dev, + "SYS_STATUS not ready (0x%08x), reinitializing\n", + status); + hws_init_video_sys(hws, true); + } + + return 0; +} + +void check_video_format(struct hws_pcie_dev *pdx) +{ + int i; + + for (i = 0; i < pdx->cur_max_video_ch; i++) { + bool interlace = false; + + if (!hws_read_active_state(pdx, i, &interlace)) { + /* No active video; optionally feed neutral frames to keep streaming. */ + if (pdx->video[i].signal_loss_cnt == 0) + pdx->video[i].signal_loss_cnt = 1; + if (READ_ONCE(pdx->video[i].cap_active)) + hws_force_no_signal_frame(&pdx->video[i], + "monitor_nosignal"); + } else { + if (pdx->hw_ver > 0) + handle_hwv2_path(pdx, i); + else + /* Legacy path stub; see handle_legacy_path() comment. */ + handle_legacy_path(pdx, i); + + update_live_resolution(pdx, i, interlace); + pdx->video[i].signal_loss_cnt = 0; + } + } +} + +static inline void hws_write_if_diff(struct hws_pcie_dev *hws, u32 reg_off, + u32 new_val) +{ + void __iomem *addr; + u32 old; + + if (!hws || !hws->bar0_base) + return; + + addr = hws->bar0_base + reg_off; + + old = readl(addr); + /* Treat all-ones as device gone; avoid writing garbage. */ + if (old == 0xFFFFFFFF) { + hws->pci_lost = true; + return; + } + + if (old != new_val) { + writel(new_val, addr); + /* Post the write on some bridges / enforce ordering. */ + (void)readl(addr); + } +} + +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace) +{ + u32 reg; + bool active; + + if (ch >= pdx->cur_max_video_ch) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + active = !!(reg & BIT(ch)); + if (interlace) + *interlace = !!(reg & BIT(8 + ch)); + return active; +} + +/* Modern hardware path: keep HW registers in sync with current per-channel + * software state. + */ +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + struct hws_video *vid; + u32 reg, in_fps, cur_out_res, want_out_res; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + + vid = &hws->video[ch]; + + /* 1) Input frame rate (read-only; log or export via debugfs if wanted) */ + in_fps = readl(hws->bar0_base + HWS_REG_FRAME_RATE(ch)); + /* dev_dbg(&hws->pdev->dev, "ch%u input fps=%u\n", ch, in_fps); */ + (void)in_fps; + + /* 2) Output resolution programming. + * For now, mirror the current format to OUT_RES. + */ + want_out_res = (vid->pix.height << 16) | vid->pix.width; + cur_out_res = readl(hws->bar0_base + HWS_REG_OUT_RES(ch)); + if (cur_out_res != want_out_res) + hws_write_if_diff(hws, HWS_REG_OUT_RES(ch), want_out_res); + + /* 3) Output FPS: only program if you actually track a target. + * Example heuristic (disabled by default): + * + * u32 out_fps = (vid->fmt_curr.height >= 1080) ? 60 : 30; + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), out_fps); + */ + + /* 4) BCHS controls: pack from per-channel current_* fields */ + reg = readl(hws->bar0_base + HWS_REG_BCHS(ch)); + { + u8 br = reg & 0xFF; + u8 co = (reg >> 8) & 0xFF; + u8 hu = (reg >> 16) & 0xFF; + u8 sa = (reg >> 24) & 0xFF; + + if (br != vid->current_brightness || + co != vid->current_contrast || hu != vid->current_hue || + sa != vid->current_saturation) { + u32 packed = (vid->current_saturation << 24) | + (vid->current_hue << 16) | + (vid->current_contrast << 8) | + vid->current_brightness; + hws_write_if_diff(hws, HWS_REG_BCHS(ch), packed); + } + } + + /* 5) HDCP detect: read only (no cache field in your structs today) */ + reg = readl(hws->bar0_base + HWS_REG_HDCP_STATUS); + /* bool hdcp = !!(reg & BIT(ch)); // use if you later add a field/control */ +} + +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + /* + * Legacy (hw_ver == 0) expected behavior: + * - A per-channel SW FPS accumulator incremented on each VDONE. + * - A once-per-second poll mapped the count to discrete FPS: + * >55*2 => 60, >45*2 => 50, >25*2 => 30, >20*2 => 25, else 60, + * then reset the accumulator to 0. + * - The *2 factor assumed VDONE fired per-field; if legacy VDONE is + * per-frame, drop the factor. + * + * Current code keeps this path as a no-op; vid->current_fps stays at the + * default or mode-derived value. If accurate legacy FPS reporting is + * needed (V4L2 g_parm/timeperframe), reintroduce the accumulator in the + * IRQ path and perform the mapping/reset here. + * + * No-op by default. If you introduce a SW FPS accumulator, map it here. + * + * Example skeleton: + * + * u32 sw_rate = READ_ONCE(hws->sw_fps[ch]); // incremented elsewhere + * if (sw_rate > THRESHOLD) { + * u32 fps = pick_fps_from_rate(sw_rate); + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), fps); + * WRITE_ONCE(hws->sw_fps[ch], 0); + * } + */ + (void)hws; + (void)ch; +} + +static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx, + unsigned int ch, u16 w, u16 h, + bool interlaced, u32 fps) +{ + struct hws_video *v = &pdx->video[ch]; + unsigned long flags; + bool queue_busy; + bool geometry_changed; + struct list_head done; + struct hwsvideo_buffer *b, *tmp; + + if (!pdx || !pdx->bar0_base) + return; + if (ch >= pdx->max_channels) + return; + if (!w || !h || w > MAX_VIDEO_HW_W || + (!interlaced && h > MAX_VIDEO_HW_H) || + (interlaced && (h * 2) > MAX_VIDEO_HW_H)) + return; + if (!fps || fps == 0xFFFFFFFF || fps > 240) + fps = (h == 576) ? 50 : 60; + + geometry_changed = w != v->pix.width || h != v->pix.height || + interlaced != v->pix.interlaced; + if (!geometry_changed && fps == v->current_fps) + return; + + if (!geometry_changed) { + /* Refresh cached live timing state, but don't emit a resolution + * change event when only the frame rate changes. + */ + mutex_lock(&v->state_lock); + v->pix.interlaced = interlaced; + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + mutex_unlock(&v->state_lock); + return; + } + + if (!mutex_trylock(&v->state_lock)) + return; + + INIT_LIST_HEAD(&done); + queue_busy = vb2_is_busy(&v->buffer_queue); + + WRITE_ONCE(v->stop_requested, true); + WRITE_ONCE(v->cap_active, false); + /* Publish software stop first so the IRQ completion path sees the stop + * before we touch MMIO or the lists. Pairs with READ_ONCE() checks in the + * VDONE handler and hws_arm_next() to prevent completions while modes + * change. + */ + smp_wmb(); + + hws_enable_video_capture(pdx, ch, false); + readl(pdx->bar0_base + HWS_REG_INT_STATUS); + + if (v->parent && v->parent->irq >= 0) + synchronize_irq(v->parent->irq); + + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Update software pixel state */ + v->pix.width = w; + v->pix.height = h; + v->pix.interlaced = interlaced; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + + hws_calc_sizeimage(v, w, h, interlaced); + v->window_valid = false; + + /* Geometry changes require userspace renegotiation once buffers exist. + * Emit SOURCE_CHANGE, mark the queue in error, and let userspace + * STREAMOFF/REQBUFS/STREAMON rather than trying to restart capture + * with partially drained in-flight state. + */ + if (queue_busy) { + struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + }; + + ev.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION; + v4l2_event_queue(v->video_device, &ev); + vb2_queue_error(&v->buffer_queue); + } else { + WRITE_ONCE(v->stop_requested, false); + } + + /* Program HW with new resolution */ + hws_write_if_diff(pdx, HWS_REG_OUT_RES(ch), (h << 16) | w); + + /* Legacy half-buffer programming */ + writel(v->pix.half_size / 16, + pdx->bar0_base + CVBS_IN_BUF_BASE2 + ch * PCIE_BARADDROFSIZE); + (void)readl(pdx->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + + /* Reset per-channel toggles/counters */ + WRITE_ONCE(v->last_buf_half_toggle, 0); + atomic_set(&v->sequence_number, 0); + + mutex_unlock(&v->state_lock); + + list_for_each_entry_safe(b, tmp, &done, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace) +{ + u32 reg = readl(pdx->bar0_base + HWS_REG_IN_RES(ch)); + u32 fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(ch)); + u16 res_w = reg & 0xFFFF; + u16 res_h = (reg >> 16) & 0xFFFF; + struct hws_video *vid = &pdx->video[ch]; + bool geometry_changed; + bool fps_changed; + + bool within_hw = (res_w <= MAX_VIDEO_HW_W) && + ((!interlace && res_h <= MAX_VIDEO_HW_H) || + (interlace && (res_h * 2) <= MAX_VIDEO_HW_H)); + + if (!within_hw) + return; + + geometry_changed = res_w != vid->pix.width || + res_h != vid->pix.height || + interlace != vid->pix.interlaced; + fps_changed = fps && fps != 0xFFFFFFFF && fps <= 240 && + fps != vid->current_fps; + + if (geometry_changed || fps_changed) + hws_video_apply_mode_change(pdx, ch, res_w, res_h, interlace, + fps); +} + +static int hws_open(struct file *file) +{ + return v4l2_fh_open(file); +} + +static const struct v4l2_file_operations hws_fops = { + .owner = THIS_MODULE, + .open = hws_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int hws_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hws_ioctl_fops = { + /* Core caps/info */ + .vidioc_querycap = hws_vidioc_querycap, + + /* Pixel format: still needed to report YUYV etc. */ + .vidioc_enum_fmt_vid_cap = hws_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = hws_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = hws_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = hws_vidioc_try_fmt_vid_cap, + + /* Buffer queueing / streaming */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + /* Inputs */ + .vidioc_enum_input = hws_vidioc_enum_input, + .vidioc_g_input = hws_vidioc_g_input, + .vidioc_s_input = hws_vidioc_s_input, + + /* DV timings (HDMI/DVI/VESA modes) */ + .vidioc_query_dv_timings = hws_vidioc_query_dv_timings, + .vidioc_enum_dv_timings = hws_vidioc_enum_dv_timings, + .vidioc_g_dv_timings = hws_vidioc_g_dv_timings, + .vidioc_s_dv_timings = hws_vidioc_s_dv_timings, + .vidioc_dv_timings_cap = hws_vidioc_dv_timings_cap, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = hws_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_parm = hws_vidioc_g_parm, +}; + +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced) +{ + /* HWS captures packed YUYV only; stride is 16 bpp aligned to 64 bytes. */ + u32 lines = h; /* full frame lines for sizeimage */ + u32 bytesperline = ALIGN(w * 2, 64); + u32 sizeimage, half0; + + /* publish into pix, since we now carry these in-state */ + v->pix.bytesperline = bytesperline; + sizeimage = bytesperline * lines; + + half0 = sizeimage / 2; + + v->pix.sizeimage = sizeimage; + v->pix.half_size = half0; /* first half; second = sizeimage - half0 */ + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; + + return v->pix.sizeimage; +} + +static int hws_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct hws_video *vid = q->drv_priv; + + if (*nplanes) { + if (sizes[0] < vid->pix.sizeimage) + return -EINVAL; + } else { + *nplanes = 1; + sizes[0] = vid->pix.sizeimage; + } + + return 0; +} + +static int hws_buffer_prepare(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hws_pcie_dev *hws = vid->parent; + size_t need = vid->pix.sizeimage; + dma_addr_t dma_addr; + + if (vb2_plane_size(vb, 0) < need) + return -EINVAL; + + /* Validate DMA address alignment */ + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (dma_addr & 0x3F) { /* 64-byte alignment required */ + dev_err(&hws->pdev->dev, + "Buffer DMA address 0x%llx not 64-byte aligned\n", + (unsigned long long)dma_addr); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, need); + return 0; +} + +static void hws_buffer_queue(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hwsvideo_buffer *buf = to_hwsbuf(vb); + struct hws_pcie_dev *hws = vid->parent; + unsigned long flags; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): vb=%p sizeimage=%u q_active=%d\n", + vid->channel_index, vb, vid->pix.sizeimage, + READ_ONCE(vid->cap_active)); + + /* Initialize buffer slot */ + buf->slot = 0; + + spin_lock_irqsave(&vid->irq_lock, flags); + list_add_tail(&buf->list, &vid->capture_queue); + vid->queued_count++; + + /* If streaming and no in-flight buffer, prime HW immediately */ + if (READ_ONCE(vid->cap_active) && !vid->active) { + dma_addr_t dma_addr; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): priming first vb=%p\n", + vid->channel_index, &buf->vb.vb2_buf); + list_del_init(&buf->list); + vid->queued_count--; + vid->active = buf; + + dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(vid->parent, vid->channel_index, + dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, vid->channel_index, true); + hws_prime_next_locked(vid); + } else if (READ_ONCE(vid->cap_active) && vid->active) { + hws_prime_next_locked(vid); + } + spin_unlock_irqrestore(&vid->irq_lock, flags); +} + +static int hws_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + struct hwsvideo_buffer *to_program = NULL; /* local copy */ + struct vb2_buffer *prog_vb2 = NULL; + unsigned long flags; + int ret; + + dev_dbg(&hws->pdev->dev, "start_streaming: ch=%u count=%u\n", + v->channel_index, count); + + ret = hws_check_card_status(hws); + if (ret) { + struct hwsvideo_buffer *b, *tmp; + unsigned long f; + LIST_HEAD(queued); + + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add_tail(&v->active->list, &queued); + v->active = NULL; + } + if (v->next_prepared) { + list_add_tail(&v->next_prepared->list, &queued); + v->next_prepared = NULL; + } + while (!list_empty(&v->capture_queue)) { + b = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_move_tail(&b->list, &queued); + } + spin_unlock_irqrestore(&v->irq_lock, f); + + list_for_each_entry_safe(b, tmp, &queued, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + return ret; + } + (void)hws_read_active_state(hws, v->channel_index, + &v->pix.interlaced); + + lockdep_assert_held(&v->state_lock); + /* init per-stream state */ + WRITE_ONCE(v->stop_requested, false); + WRITE_ONCE(v->cap_active, true); + WRITE_ONCE(v->half_seen, false); + WRITE_ONCE(v->last_buf_half_toggle, 0); + + /* Try to prime a buffer, but it's OK if none are queued yet */ + spin_lock_irqsave(&v->irq_lock, flags); + if (!v->active && !list_empty(&v->capture_queue)) { + to_program = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&to_program->list); + v->queued_count--; + v->active = to_program; + prog_vb2 = &to_program->vb.vb2_buf; + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u took buffer %p\n", + v->channel_index, to_program); + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Only program/enable HW if we actually have a buffer */ + if (to_program) { + if (!prog_vb2) + prog_vb2 = &to_program->vb.vb2_buf; + { + dma_addr_t dma_addr; + + dma_addr = vb2_dma_contig_plane_dma_addr(prog_vb2, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + + HWS_REG_DMA_ADDR(v->channel_index)); + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u programmed buffer %p dma=0x%08x\n", + v->channel_index, to_program, + lower_32_bits(dma_addr)); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + { + unsigned long pf; + + spin_lock_irqsave(&v->irq_lock, pf); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, pf); + } + } else { + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u no buffer yet (will arm on QBUF)\n", + v->channel_index); + } + + return 0; +} + +static void hws_log_video_state(struct hws_video *v, const char *action, + const char *phase) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + unsigned int queued = 0; + unsigned int tracked = 0; + unsigned int seq = 0; + struct hwsvideo_buffer *b; + bool streaming = vb2_is_streaming(&v->buffer_queue); + bool cap_active; + bool stop_requested; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + spin_lock_irqsave(&v->irq_lock, flags); + list_for_each_entry(b, &v->capture_queue, list) + queued++; + cap_active = READ_ONCE(v->cap_active); + stop_requested = READ_ONCE(v->stop_requested); + active = v->active; + next_prepared = v->next_prepared; + tracked = v->queued_count; + seq = (u32)atomic_read(&v->sequence_number); + spin_unlock_irqrestore(&v->irq_lock, flags); + + dev_dbg(&hws->pdev->dev, + "video:%s:%s ch=%u streaming=%d cap=%d stop=%d active=%p next=%p queued=%u tracked=%u seq=%u\n", + action, phase, v->channel_index, streaming, cap_active, + stop_requested, active, next_prepared, queued, tracked, seq); +} + +static void hws_stop_streaming(struct vb2_queue *q) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + struct hwsvideo_buffer *b, *tmp; + LIST_HEAD(done); + unsigned int done_cnt = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + hws_log_video_state(v, "streamoff", "begin"); + + /* 1) Quiesce SW/HW first */ + lockdep_assert_held(&v->state_lock); + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + + hws_enable_video_capture(v->parent, v->channel_index, false); + + /* 2) Collect in-flight + queued under the IRQ lock */ + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 3) Complete outside the lock */ + list_for_each_entry_safe(b, tmp, &done, list) { + /* Unlink from 'done' before completing */ + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + done_cnt++; + } + dev_dbg(&hws->pdev->dev, + "video:streamoff:done ch=%u completed=%u (%lluus)\n", + v->channel_index, done_cnt, hws_elapsed_us(start_ns)); + hws_log_video_state(v, "streamoff", "end"); +} + +static const struct vb2_ops hwspcie_video_qops = { + .queue_setup = hws_queue_setup, + .buf_prepare = hws_buffer_prepare, + .buf_init = hws_buf_init, + .buf_finish = hws_buf_finish, + .buf_cleanup = hws_buf_cleanup, + .buf_queue = hws_buffer_queue, + .start_streaming = hws_start_streaming, + .stop_streaming = hws_stop_streaming, +}; + +int hws_video_register(struct hws_pcie_dev *dev) +{ + int i, ret; + + ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_device); + if (ret) { + dev_err(&dev->pdev->dev, "v4l2_device_register failed: %d\n", + ret); + return ret; + } + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + struct video_device *vdev; + struct vb2_queue *q; + + /* hws_video_init_channel() should have set: + * - ch->parent, ch->channel_index + * - locks (state_lock, irq_lock) + * - capture_queue (INIT_LIST_HEAD) + * - control_handler + controls + * - fmt_curr (width/height) + * Do not reinitialize any of those here. + */ + + vdev = video_device_alloc(); + if (!vdev) { + dev_err(&dev->pdev->dev, + "video_device_alloc ch%u failed\n", i); + ret = -ENOMEM; + goto err_unwind; + } + ch->video_device = vdev; + + /* Basic V4L2 node setup */ + snprintf(vdev->name, sizeof(vdev->name), "%s-hdmi%u", + KBUILD_MODNAME, i); + vdev->v4l2_dev = &dev->v4l2_device; + vdev->fops = &hws_fops; + vdev->ioctl_ops = &hws_ioctl_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + vdev->lock = &ch->state_lock; /* serialize file ops */ + vdev->ctrl_handler = &ch->control_handler; + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release; + if (ch->control_handler.error) { + ret = ch->control_handler.error; + goto err_unwind; + } + video_set_drvdata(vdev, ch); + + /* vb2 queue init (dma-contig) */ + q = &ch->buffer_queue; + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = ch; + q->buf_struct_size = sizeof(struct hwsvideo_buffer); + q->ops = &hwspcie_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ch->state_lock; + q->min_queued_buffers = 1; + q->dev = &dev->pdev->dev; + + ret = vb2_queue_init(q); + vdev->queue = q; + if (ret) { + dev_err(&dev->pdev->dev, + "vb2_queue_init ch%u failed: %d\n", i, ret); + goto err_unwind; + } + + /* Make controls live (no-op if none or already set up) */ + if (ch->control_handler.error) { + ret = ch->control_handler.error; + dev_err(&dev->pdev->dev, + "ctrl handler ch%u error: %d\n", i, ret); + goto err_unwind; + } + v4l2_ctrl_handler_setup(&ch->control_handler); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(&dev->pdev->dev, + "video_register_device ch%u failed: %d\n", i, + ret); + goto err_unwind; + } + } + + return 0; + +err_unwind: + for (; i >= 0; i--) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + } + v4l2_device_unregister(&dev->v4l2_device); + return ret; +} + +void hws_video_unregister(struct hws_pcie_dev *dev) +{ + int i; + + if (!dev) + return; + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + v4l2_ctrl_handler_free(&ch->control_handler); + } + v4l2_device_unregister(&dev->v4l2_device); +} + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason) +{ + int i, ret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + dev_dbg(&hws->pdev->dev, "video:%s:begin channels=%u\n", reason, + hws->cur_max_video_ch); + for (i = 0; i < hws->cur_max_video_ch; i++) { + struct hws_video *vid = &hws->video[i]; + struct vb2_queue *q = &vid->buffer_queue; + u64 ch_start_ns = ktime_get_mono_fast_ns(); + bool streaming; + + if (!q || !q->ops) { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d skipped queue-unavailable\n", + reason, i); + continue; + } + + streaming = vb2_is_streaming(q); + hws_log_video_state(vid, reason, "channel"); + if (streaming) { + /* Stop via vb2, which runs .stop_streaming. */ + int r = vb2_streamoff(q, q->type); + + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d streamoff ret=%d (%lluus)\n", + reason, i, r, hws_elapsed_us(ch_start_ns)); + if (r && !ret) + ret = r; + } else { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d idle (%lluus)\n", + reason, i, hws_elapsed_us(ch_start_ns)); + } + } + dev_dbg(&hws->pdev->dev, "video:%s:done ret=%d (%lluus)\n", reason, + ret, hws_elapsed_us(start_ns)); + return ret; +} + +void hws_video_pm_resume(struct hws_pcie_dev *hws) +{ + /* Nothing mandatory to do here for vb2; userspace will STREAMON + * again when ready. + */ +} diff --git a/drivers/media/pci/hws/hws_video.h b/drivers/media/pci/hws/hws_video.h new file mode 100644 index 000000000000..4feaf5b2f5a9 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_VIDEO_H +#define HWS_VIDEO_H + +struct hws_video; + +int hws_video_register(struct hws_pcie_dev *dev); +void hws_video_unregister(struct hws_pcie_dev *dev); +void hws_enable_video_capture(struct hws_pcie_dev *hws, + unsigned int chan, + bool on); +void hws_prime_next_locked(struct hws_video *vid); + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch); +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch); +void check_video_format(struct hws_pcie_dev *pdx); +int hws_check_card_status(struct hws_pcie_dev *hws); +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable); + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, + unsigned int ch, + dma_addr_t dma); +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag); + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason); +void hws_video_pm_resume(struct hws_pcie_dev *hws); + +#endif From 07f38a4942efc6937dfb412592705c1c54258e43 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 6 May 2026 18:53:24 -0700 Subject: [PATCH 0853/5214] media: gspca: use module_usb_driver() Nothing interesting happens in _init and _exit. Just use the macro to simplify the code slightly. Signed-off-by: Rosen Penev Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/touptek.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c index dde311c25d9b..734644d928ab 100644 --- a/drivers/media/usb/gspca/touptek.c +++ b/drivers/media/usb/gspca/touptek.c @@ -709,19 +709,4 @@ static struct usb_driver sd_driver = { #endif }; -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); From d5b50055338e131a1a99f923ebb0361974a00f36 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 7 May 2026 02:22:13 +0000 Subject: [PATCH 0854/5214] media: tegra-video: vi: fix invalid u32 return value in format lookup tegra_get_format_fourcc_by_idx() returns a u32 but uses -EINVAL to signal an out-of-bounds index. This results in a large unsigned value being returned, which may be interpreted as a valid fourcc. Returning 0 is not a valid fourcc either. This condition should never happen, so use WARN_ON_ONCE() to catch unexpected out-of-bounds access and return a valid fallback format instead. Suggested-by: Hans Verkuil Fixes: 3d8a97eabef0 ("media: tegra-video: Add Tegra210 Video input driver") Cc: stable@vger.kernel.org Reviewed-by: Luca Ceresoli Signed-off-by: Hungyu Lin Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index f14cdc7b5211..456134a9e8cf 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi, static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi, unsigned int index) { - if (index >= vi->soc->nformats) - return -EINVAL; + if (WARN_ON_ONCE(index >= vi->soc->nformats)) + return vi->soc->video_formats[0].fourcc; return vi->soc->video_formats[index].fourcc; } From 5328dd0ce2d5db943393c9222dbcd2bd41fcba35 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:06 +0000 Subject: [PATCH 0855/5214] media: v4l2-dev: Add range check for vdev->minor If the fixed minor ranges are not properly set we could end up in a situation where the calculated minor is invalid. Add a check for this in the code to make it more robust. This check also fixes the following false positive smatch warning: drivers/media/v4l2-core/v4l2-dev.c:1036 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 drivers/media/v4l2-core/v4l2-dev.c:1043 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 drivers/media/v4l2-core/v4l2-dev.c:1101 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 Reviewed-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-dev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6ce623a1245a..5516b2bbb08f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1032,6 +1032,11 @@ int __video_register_device(struct video_device *vdev, vdev->minor = i + minor_offset; vdev->num = nr; + if (WARN_ON(vdev->minor >= VIDEO_NUM_DEVICES)) { + mutex_unlock(&videodev_lock); + return -EINVAL; + } + /* Should not happen since we thought this minor was free */ if (WARN_ON(video_devices[vdev->minor])) { mutex_unlock(&videodev_lock); From 508afd8060e83182e5c2f88385b77f598f6a3d0d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:07 +0000 Subject: [PATCH 0856/5214] media: i2c: mt9p031: Rewrite assignment to make smatch happy The current code makes smatch a bit uncomfortable: drivers/media/i2c/mt9p031.c:799 mt9p031_s_ctrl() warn: assigning (-1952) to unsigned variable 'data' Probably because smatch is not clever enough (yet). Do a simple rewrite to make sure that smatch understands what we are doing here. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/i2c/mt9p031.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index ea5d43d925ff..8dc57eeba606 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -796,7 +796,8 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) data = (1 << 6) | (ctrl->val >> 1); } else { ctrl->val &= ~7; - data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + data = ((ctrl->val - 64) >> 3) & 0x7f; + data = (data << 8) | (1 << 6) | 32; } return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); From ddf9d68a368d8cae5510923a99bd2d9a7275a091 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:08 +0000 Subject: [PATCH 0857/5214] media: i2c: adv7604: Add range checks for chip info If the driver's chip information is invalid we can end up accessing an invalid memory region. This fixes the following false positive smatch errors: drivers/media/i2c/adv7604.c:3672 adv76xx_probe() error: buffer overflow 'state->pads' 7 <= 4294967294 drivers/media/i2c/adv7604.c:3673 adv76xx_probe() error: buffer overflow 'state->pads' 7 <= u32max Reviewed-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/i2c/adv7604.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 67116a4ef134..ae75982fb514 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3668,6 +3668,12 @@ static int adv76xx_probe(struct i2c_client *client) state->source_pad = state->info->num_dv_ports + (state->info->has_afe ? 2 : 0); + if (WARN_ON(state->source_pad >= ADV76XX_PAD_MAX)) { + err = -EINVAL; + v4l2_err(sd, "invalid chip info\n"); + goto err_i2c; + } + for (i = 0; i < state->source_pad; ++i) state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; From 79aef69bb0903616f4867f0168aea717a11c439c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:09 +0000 Subject: [PATCH 0858/5214] media: chips-media: wave5: Add range checks for dec_output_info If the driver's dec_output_info contains invalid data the driver can write in invalid memory. Add a range check for that. This fixes this smatch error: drivers/media/platform/chips-media/wave5/wave5-vpuapi.c:588 wave5_vpu_dec_get_output_info() error: buffer overflow 'inst->frame_buf' 64 <= 127 Signed-off-by: Ricardo Ribalda Reviewed-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/platform/chips-media/wave5/wave5-vpuapi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c index d26ffc942219..f77abd5e122a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -584,8 +584,15 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs; if (info->index_frame_display >= 0 && - info->index_frame_display < (int)max_dec_index) - info->disp_frame = inst->frame_buf[val + info->index_frame_display]; + info->index_frame_display < (int)max_dec_index) { + u32 idx = val + info->index_frame_display; + + if (WARN_ON(idx >= MAX_REG_FRAME)) { + ret = -EINVAL; + goto err_out; + } + info->disp_frame = inst->frame_buf[idx]; + } info->rd_ptr = p_dec_info->stream_rd_ptr; info->wr_ptr = p_dec_info->stream_wr_ptr; From c32fe4c4918c9aa49f61359e3b42619c4d8686de Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:10 +0000 Subject: [PATCH 0859/5214] media: staging: ipu3-imgu: Add range check for imgu_css_cfg_acc_stripe If the driver's stripe information is invalid it can result in an integer underflow. Add a range check to avoid this kind of error. This patch fixes the following smatch error: drivers/staging/media/ipu3/ipu3-css-params.c:1792 imgu_css_cfg_acc_stripe() warn: 'acc->stripe.bds_out_stripes[0]->width - 2 * f' 4294967168 can't fit into 65535 'acc->stripe.bds_out_stripes[1]->offset' Cc: stable@vger.kernel.org Fixes: e11110a5b744 ("media: staging/intel-ipu3: css: Compute and program ccs") Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu3/ipu3-css-params.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 2c48d57a3180..92cce31e35c5 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[0].width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); } else { + u32 offset; + /* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0].width = acc->stripe.bds_out_stripes[1].width = @@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[1].width += f; } /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1].offset = - acc->stripe.bds_out_stripes[0].width - 2 * f; + offset = acc->stripe.bds_out_stripes[0].width - 2 * f; + if (offset > 65535) + return -EINVAL; + acc->stripe.bds_out_stripes[1].offset = offset; } acc->stripe.effective_stripes[0].height = From 9724164f71974a2a44a5e026614fbcc05bab6d91 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:11 +0000 Subject: [PATCH 0860/5214] media: amlogic-c3: Add validations for ae and awb config Avoid invalid memory access if the zones_num is bigger than zone_weight. This patch fixes the following smatch errors: drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max Cc: stable@vger.kernel.org Fixes: fb2e135208f3 ("media: platform: Add C3 ISP driver") Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Keke Li Signed-off-by: Hans Verkuil --- drivers/media/platform/amlogic/c3/isp/c3-isp-params.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index 6f9ca7a7dd88..aec3eed0e443 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -104,6 +104,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AWB_MAX_ZONES) + zones_num = C3_ISP_AWB_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { @@ -220,6 +222,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AE_MAX_ZONES) + zones_num = C3_ISP_AE_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { From 56384b486b80ce4a2bc93689aae49995f908f90d Mon Sep 17 00:00:00 2001 From: Arash Golgol Date: Sat, 9 May 2026 19:40:13 +0330 Subject: [PATCH 0861/5214] media: video-i2c: use vb2_video_unregister_device on driver removal The driver uses vb2_fop_release() as its file release operation, so vb2_video_unregister_device() should be used instead of video_unregister_device() during driver removal. This ensures that the vb2 queue is properly disconnected before the video device is unregistered. Signed-off-by: Arash Golgol Signed-off-by: Hans Verkuil --- drivers/media/i2c/video-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index fef3993f4e2d..9c47f4aaa359 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -888,7 +888,7 @@ static void video_i2c_remove(struct i2c_client *client) if (data->chip->set_power) data->chip->set_power(data, false); - video_unregister_device(&data->vdev); + vb2_video_unregister_device(&data->vdev); } #ifdef CONFIG_PM From bc4574c265ed738849e46d942617100580fcedd2 Mon Sep 17 00:00:00 2001 From: Arash Golgol Date: Sun, 10 May 2026 07:04:23 +0330 Subject: [PATCH 0862/5214] media: video-i2c: fix buffer queue ordering Queued buffers are added to the tail of vid_cap_active in buffer_queue(), but the capture kthread also retrieves buffers from the tail of the list. This makes the queue behave as LIFO instead of FIFO when multiple buffers are queued. Fix this by retrieving buffers from the head of the list. Signed-off-by: Arash Golgol Signed-off-by: Hans Verkuil --- drivers/media/i2c/video-i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 9c47f4aaa359..2b23914717d1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -454,8 +454,9 @@ static int video_i2c_thread_vid_cap(void *priv) spin_lock(&data->slock); if (!list_empty(&data->vid_cap_active)) { - vid_cap_buf = list_last_entry(&data->vid_cap_active, - struct video_i2c_buffer, list); + vid_cap_buf = list_first_entry(&data->vid_cap_active, + struct video_i2c_buffer, + list); list_del(&vid_cap_buf->list); } From 5fec5c8f11fdb65cfa0276f3877e8b8c1c8c50f7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 11 May 2026 16:32:38 +0200 Subject: [PATCH 0863/5214] media: rzv2h-ivc: Add myself as co-maintainer Add myself as co-maintainer of the RZ/V2H(P) IVC block. Signed-off-by: Jacopo Mondi Acked-by: Daniel Scally Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4e054ce1cfaa..4b5b66d5114e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22756,6 +22756,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c RENESAS RZ/V2H(P) INPUT VIDEO CONTROL BLOCK DRIVER M: Daniel Scally +M: Jacopo Mondi L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml From 04344d0b4929caa94c0df72f767752aa0935ef5d Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:06 +0300 Subject: [PATCH 0864/5214] media: airspy: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. airspy_start_streaming() returned -ENODEV early when the USB device had been disconnected (s->udev == NULL) without returning any buffers that buf_queue() had already accepted. Take v4l2_lock first and jump to the existing err_clear_bit label, which already drains s->queued_bufs via vb2_buffer_done(..., VB2_BUF_STATE_QUEUED) before unlocking. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 634fe5033951 ("[media] airspy: AirSpy SDR driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/airspy/airspy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 8f6b721ba107..57edb42463e8 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(s->dev, "\n"); - if (!s->udev) - return -ENODEV; - mutex_lock(&s->v4l2_lock); + if (!s->udev) { + ret = -ENODEV; + goto err_clear_bit; + } + s->sequence = 0; set_bit(POWER_ON, &s->flags); From 7201c17786a498497bca57752883b90914d405ac Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:07 +0300 Subject: [PATCH 0865/5214] media: msi2500: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. msi2500_start_streaming() had five error paths that all hit this trap and were further tangled by ret-overwriting between calls: - -ENODEV when the USB device was already disconnected - -ERESTARTSYS when mutex_lock_interruptible() was interrupted - msi2500_set_usb_adc() failure: ret was silently overwritten by the next call (msi2500_isoc_init), so the error was lost entirely - msi2500_isoc_init() failure: cleanup_queued_bufs was called, but the function then fell through to msi2500_ctrl_msg() and again masked the original error by overwriting ret - msi2500_ctrl_msg(CMD_START_STREAMING) failure: no cleanup at all, leaving isoc URBs submitted with no way for the driver to consume them Consolidate the error paths into a small goto chain. Every failure now stops the function, drains the queued-buffer list, and returns the real error code. The ctrl_msg failure path also rolls back the preceding msi2500_isoc_init() via msi2500_isoc_cleanup() before unlocking and draining. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 977e444f59ad ("[media] Mirics MSi3101 SDR Dongle driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/msi2500/msi2500.c | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 1ff98956b680..0614087c3c3c 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) +static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev, + enum vb2_buffer_state state) { unsigned long flags; @@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) buf = list_entry(dev->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -830,25 +831,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dev->dev, "\n"); - if (!dev->udev) - return -ENODEV; + if (!dev->udev) { + ret = -ENODEV; + goto err_cleanup; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto err_cleanup; + } /* wake-up tuner */ v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); ret = msi2500_set_usb_adc(dev); + if (ret) + goto err_unlock_cleanup; ret = msi2500_isoc_init(dev); if (ret) - msi2500_cleanup_queued_bufs(dev); + goto err_unlock_cleanup; ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0); + if (ret) + goto err_isoc_cleanup; mutex_unlock(&dev->v4l2_lock); + return 0; +err_isoc_cleanup: + msi2500_isoc_cleanup(dev); +err_unlock_cleanup: + mutex_unlock(&dev->v4l2_lock); +err_cleanup: + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return ret; } @@ -863,7 +879,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) if (dev->udev) msi2500_isoc_cleanup(dev); - msi2500_cleanup_queued_bufs(dev); + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); /* according to tests, at least 700us delay is required */ msleep(20); From 975b2ee20e569d47821e4f6c9761b4664d48a6a4 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:08 +0300 Subject: [PATCH 0866/5214] media: pwc: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. pwc's start_streaming() had two early returns that hit this trap: -ENODEV when the USB device was already disconnected, and -ERESTARTSYS when mutex_lock_interruptible() was interrupted by a signal. Call the existing pwc_cleanup_queued_bufs() helper with VB2_BUF_STATE_QUEUED before returning (matching the state already used by the pwc_isoc_init() error path in the same function). This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: ceede9fa8939 ("[media] pwc: Fix locking") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/pwc/pwc-if.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index c416e2fc5754..59b99ac8fcb6 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - if (!pdev->udev) + if (!pdev->udev) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&pdev->v4l2_lock)) + if (mutex_lock_interruptible(&pdev->v4l2_lock)) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); pwc_set_leds(pdev, leds[0], leds[1]); From 33ca0aab6f4bd90921fc1395478f38f72c4d19af Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:09 +0300 Subject: [PATCH 0867/5214] media: rtl2832_sdr: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. rtl2832_sdr_start_streaming() had multiple error paths that hit this trap: two direct early returns (-ENODEV, -ERESTARTSYS), plus six `goto err` paths covering subdev s_power, tuner setup, ADC setup, stream-buffer allocation, urb allocation, and urb submission failures. None of them returned the queued buffers. The original function had no distinct success exit and fell straight through into the err label, which previously only did mutex_unlock and "return ret". Adding queued-buffer cleanup at err must therefore be paired with an explicit success return; otherwise every successful start would also drain the buffer queue and kill streaming. Add that success return, then add rtl2832_sdr_cleanup_queued_bufs() at the err label and before each early return. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). The err label still does not roll back power_ctrl(), frontend_ctrl(), the POWER_ON flag, or stream/URB allocations that may have happened before the failing step. Those are pre-existing leaks of a different class and are not addressed here. Fixes: 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832_sdr.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 422d1a7b5456..c564485e3bbb 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev, + enum vb2_buffer_state state) { struct platform_device *pdev = dev->pdev; unsigned long flags; @@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(&pdev->dev, "\n"); - if (!dev->udev) + if (!dev->udev) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } if (d->props->power_ctrl) d->props->power_ctrl(d, 1); @@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + mutex_unlock(&dev->v4l2_lock); + return 0; + err: + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); return ret; @@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) rtl2832_sdr_kill_urbs(dev); rtl2832_sdr_free_urbs(dev); rtl2832_sdr_free_stream_bufs(dev); - rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); rtl2832_sdr_unset_adc(dev); /* sleep tuner */ From ffc8eec06378a340d708c889184ab3e14b57d540 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:10 +0300 Subject: [PATCH 0868/5214] media: stm32-dcmipp: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. dcmipp_bytecap_start_streaming() returned -EINVAL when the source subdevice could not be resolved from the media graph, before pm_runtime_resume_and_get() and media_pipeline_start() had been called. The remaining error paths already converge on the err_buffer_done label, which calls dcmipp_bytecap_all_buffers_done(..., VB2_BUF_STATE_QUEUED). Jump to that label directly: the intermediate err_pm_put / err_media_pipeline_stop labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- .../media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index a42f43d19f9e..f0e809458489 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -401,8 +401,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, */ if (!vcap->s_subdev) { pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) { + ret = -EINVAL; + goto err_buffer_done; + } vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); vcap->s_subdev_pad_nb = pad->index; } From bbba3e260a62810a717b4442a3bb96d0ec0f6309 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:11 +0300 Subject: [PATCH 0869/5214] media: sun4i-csi: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. sun4i_csi_start_streaming() returned -EINVAL when no matching CSI format could be found, before any setup (scratch buffer allocation, pipeline start) had been performed. The remaining error paths already converge on the err_clear_dma_queue label, which calls return_all_buffers(..., VB2_BUF_STATE_QUEUED) under csi->qlock. Jump to that label directly: the intermediate err_disable_device / err_disable_pipeline / err_free_scratch_buffer labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 577bbf23b758 ("media: sunxi: Add A10 CSI driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index e911c7f7acc5..4781db21c205 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL); - if (!csi_fmt) - return -EINVAL; + if (!csi_fmt) { + ret = -EINVAL; + goto err_clear_dma_queue; + } dev_dbg(csi->dev, "Starting capture\n"); From 813dacdda8f2e05b1309a4942e91531333b296ed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 12 May 2026 08:46:15 +0200 Subject: [PATCH 0870/5214] media: visl: check if ctx->tpg_str_buf allocation failed The result of ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL); was never checked. Add this. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/visl/visl-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c index 127ab18bce99..5e9bdd425d4a 100644 --- a/drivers/media/test-drivers/visl/visl-core.c +++ b/drivers/media/test-drivers/visl/visl-core.c @@ -339,6 +339,10 @@ static int visl_open(struct file *file) } ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL); + if (!ctx->tpg_str_buf) { + rc = -ENOMEM; + goto free_ctx; + } v4l2_fh_init(&ctx->fh, video_devdata(file)); ctx->dev = dev; From 10f943b12e7cb338da00f10e129043ae27b33af4 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 May 2026 08:54:26 +0200 Subject: [PATCH 0871/5214] media: rzg2l-cru: Add MAINTAINERS entry The CRU was missing a maintainer entry. Add it. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4b5b66d5114e..63389fea5d15 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22688,6 +22688,15 @@ S: Supported F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml F: drivers/counter/rz-mtu3-cnt.c +RENESAS RZ/G2L / RZ/V2H(P) CRU +M: Lad Prabhakar +M: Jacopo Mondi +L: linux-renesas-soc@vger.kernel.org +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml +F: drivers/media/platform/renesas/rzg2l-cru/ + RENESAS RZ/T2H / RZ/N2H A/D DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org From 1389ab9bf9f627d4daed86f492091b00f110aa86 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Fri, 1 May 2026 02:45:23 +0800 Subject: [PATCH 0872/5214] PCI: loongson: Do not ignore downstream devices on external bridges Loongson PCI host controllers have a hardware quirk that requires software to ignore downstream devices with device number > 0 on the internal bridges. The current implementation applies the workaround to all non-root buses, which breaks external bridges (e.g., PCIe switches) with multiple downstream devices. Fix it by only applying the workaround to internal bridges. Tested on Loongson-LS3A4000-7A1000-NUC-SE, using AMD Promontory 21 chipset add-in card [1]. $ lspci -tnnnvvv -[0000:00]-+-00.0 Loongson Technology LLC 7A1000 Chipset Hyper Transport Bridge Controller [0014:7a00] +-00.1 Loongson Technology LLC 7A2000 Chipset Hyper Transport Bridge Controller [0014:7a10] +-03.0 Loongson Technology LLC 2K1000/2000 / 7A1000 Chipset Gigabit Ethernet Controller [0014:7a03] +-04.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-04.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-05.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-05.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-06.0 Loongson Technology LLC 7A1000 Chipset Vivante GC1000 GPU [0014:7a15] +-06.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset Display Controller [0014:7a06] +-07.0 Loongson Technology LLC 2K1000/2000/3000 / 3B6000M / 7A1000/2000 Chipset HD Audio Controller [0014:7a07] +-08.0 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.2 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-09.0-[01]----00.0 Qualcomm Technologies, Inc QCNFA765 Wireless Network Adapter [17cb:1103] +-0a.0-[02]----00.0 Etron Technology, Inc. EJ188/EJ198 USB 3.0 Host Controller [1b6f:7052] +-0f.0-[03-08]----00.0-[04-08]--+-00.0-[05]----00.0 Shenzhen Longsys Electronics Co., Ltd. FORESEE XP1000 / Lexar Professional CFexpress Type B Gold series, NM620 PCIe NVME SSD (DRAM-less) [1d97:5216] | +-08.0-[06]----00.0 MAXIO Technology (Hangzhou) Ltd. NVMe SSD Controller MAP1202 (DRAM-less) [1e4b:1202] | +-0c.0-[07]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset USB 3.2 Controller [1022:43f7] | \-0d.0-[08]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] \-16.0 Loongson Technology LLC 7A1000 Chipset SPI Controller [0014:7a0b] Fixes: 2410e3301fcc ("PCI: loongson: Don't access non-existent devices") Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Co-developed-by: Lain "Fearyncess" Yang Signed-off-by: Lain "Fearyncess" Yang Signed-off-by: Rong Zhang Signed-off-by: Manivannan Sadhasivam Link: https://oshwhub.com/wesd/b650 [1] Link: https://patch.msgid.link/20260501-ls7a-bridge-fixes-v2-1-69fa93683805@rong.moe --- drivers/pci/controller/pci-loongson.c | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index bc630ab8a283..de5e809a537d 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); +static const struct pci_device_id loongson_internal_bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, + { 0, }, +}; + /* * Some Loongson PCIe ports have hardware limitations on their Maximum Read * Request Size. They can't handle anything larger than this. Sane @@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev) { struct pci_bus *bus = pdev->bus; struct pci_dev *bridge; - static const struct pci_device_id bridge_devids[] = { - { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, - { 0, }, - }; /* look for the matching bridge */ while (!pci_is_root_bus(bus)) { bridge = bus->self; bus = bus->parent; - if (pci_match_id(bridge_devids, bridge)) { + if (pci_match_id(loongson_internal_bridge_devids, bridge)) { if (pcie_get_readrq(pdev) > 256) { pci_info(pdev, "limiting MRRS to 256\n"); pcie_set_readrq(pdev, 256); @@ -230,11 +231,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, struct loongson_pci *priv = pci_bus_to_loongson_pci(bus); /* - * Do not read more than one device on the bus other than - * the host bus. + * Do not read more than one device on the internal bridges. */ if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) { - if (!pci_is_root_bus(bus) && (device > 0)) + if (!pci_is_root_bus(bus) && (device > 0) && + pci_match_id(loongson_internal_bridge_devids, bus->self)) return NULL; } From 0492461a0508d88f2b63de4f7f4e71b38078de20 Mon Sep 17 00:00:00 2001 From: Sai Krishna Musham Date: Mon, 27 Apr 2026 17:42:27 +0530 Subject: [PATCH 0873/5214] PCI: amd-mdb: Assert PERST# on shutdown Add a shutdown handler for the AMD MDB PCIe host controller that asserts the PERST# signal via GPIO before the system powers off or reboots. This ensures the connected PCIe endpoint is held in reset during shutdown. Signed-off-by: Sai Krishna Musham [mani: removed conditional check since GPIO APIs return safely if desc is NULL] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260427121227.290604-1-sai.krishna.musham@amd.com --- drivers/pci/controller/dwc/pcie-amd-mdb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 7e50e11fbffd..fe568d871579 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -507,6 +507,13 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev) return amd_mdb_add_pcie_port(pcie, pdev); } +static void amd_mdb_pcie_shutdown(struct platform_device *pdev) +{ + struct amd_mdb_pcie *pcie = platform_get_drvdata(pdev); + + gpiod_set_value_cansleep(pcie->perst_gpio, 1); +} + static const struct of_device_id amd_mdb_pcie_of_match[] = { { .compatible = "amd,versal2-mdb-host", @@ -521,6 +528,7 @@ static struct platform_driver amd_mdb_pcie_driver = { .suppress_bind_attrs = true, }, .probe = amd_mdb_pcie_probe, + .shutdown = amd_mdb_pcie_shutdown, }; builtin_platform_driver(amd_mdb_pcie_driver); From 86f6dc05ea051fa03ebc03174bc00f734593465d Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 3 Apr 2026 12:30:03 +0200 Subject: [PATCH 0874/5214] bus: mhi: host: pci_generic: Round up nr_irqs to power of two When an MHI device uses standard MSI, the PCI core requires the allocated number of vectors to be a strict power of two. But devices will only ask for the irqs they need, so they might not be properly aligned. Make sure a power-of-2 number of vectors is requested. Signed-off-by: Javier Achirica Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/CACixm21q7b_diEx5COZxVZm9EhZ0hnakM_WBjEWcCsznfWeniw@mail.gmail.com --- drivers/bus/mhi/host/pci_generic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 750da3dbb4c6..7efcbcf5a59e 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1189,7 +1189,8 @@ static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl, */ mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events; - nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSIX | PCI_IRQ_MSI); + nr_vectors = pci_alloc_irq_vectors(pdev, 1, roundup_pow_of_two(mhi_cntrl->nr_irqs), + PCI_IRQ_MSIX | PCI_IRQ_MSI); if (nr_vectors < 0) { dev_err(&pdev->dev, "Error allocating MSI vectors %d\n", nr_vectors); From 9dece4435d396e9877e27483552b910ba8654169 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Apr 2026 11:59:40 +0530 Subject: [PATCH 0875/5214] bus: mhi: ep: Fix potential deadlock in mhi_ep_reset_worker() There is a potential deadlock scenario in mhi_ep_reset_worker() where the state_lock mutex is acquired twice in the same call chain: mhi_ep_reset_worker() mutex_lock(&mhi_cntrl->state_lock) mhi_ep_power_up() mhi_ep_set_ready_state() mutex_lock(&mhi_cntrl->state_lock) <- Deadlock Fix this by releasing the state_lock before calling mhi_ep_power_up(). The lock is only needed to protect current MHI state read operation. The lock can be safely released before proceeding with the power up sequence. Fixes: 7a97b6b47353 ("bus: mhi: ep: Add support for handling MHI_RESET") Signed-off-by: Sumit Kumar Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414-reset_worker_deadlock-v2-1-42fd682b45db@oss.qualcomm.com --- drivers/bus/mhi/ep/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 0277e1ab1198..425525e232f9 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1087,11 +1087,12 @@ static void mhi_ep_reset_worker(struct work_struct *work) mhi_ep_power_down(mhi_cntrl); - mutex_lock(&mhi_cntrl->state_lock); - /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */ mhi_ep_mmio_reset(mhi_cntrl); + + mutex_lock(&mhi_cntrl->state_lock); cur_state = mhi_cntrl->mhi_state; + mutex_unlock(&mhi_cntrl->state_lock); /* * Only proceed further if the reset is due to SYS_ERR. The host will @@ -1100,8 +1101,6 @@ static void mhi_ep_reset_worker(struct work_struct *work) */ if (cur_state == MHI_STATE_SYS_ERR) mhi_ep_power_up(mhi_cntrl); - - mutex_unlock(&mhi_cntrl->state_lock); } /* From ce3e534ee9c8d13a68c8a611c3b7bd0c2152d2ab Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Apr 2026 11:59:41 +0530 Subject: [PATCH 0876/5214] bus: mhi: ep: Add missing state_lock protection for mhi_state access The mhi_cntrl->mhi_state field should be protected by state_lock to ensure atomic state transitions. However, mhi_ep_power_up() access mhi_state without holding this lock, which can race with concurrent state transitions and lead to state corruption. Add proper state_lock protection around mhi_state access. Fixes: fb3a26b7e8af ("bus: mhi: ep: Add support for powering up the MHI endpoint stack") Fixes: f7d0806bdb1b3 ("bus: mhi: ep: Add support for handling SYS_ERR condition") Signed-off-by: Sumit Kumar Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414-reset_worker_deadlock-v2-2-42fd682b45db@oss.qualcomm.com --- drivers/bus/mhi/ep/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 425525e232f9..5330b03dc636 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1147,7 +1147,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl) for (i = 0; i < mhi_cntrl->event_rings; i++) mhi_ep_ring_init(&mhi_cntrl->mhi_event[i].ring, RING_TYPE_ER, i); + mutex_lock(&mhi_cntrl->state_lock); mhi_cntrl->mhi_state = MHI_STATE_RESET; + mutex_unlock(&mhi_cntrl->state_lock); /* Set AMSS EE before signaling ready state */ mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); From 5096977d0da4b4176410f12d79716568858ea3f9 Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Tue, 12 May 2026 13:24:58 +0200 Subject: [PATCH 0877/5214] bus: mhi: host: pci_generic: Add Telit FE910C04 modem support Add SDX35 based modem Telit FE910C04, reusing FN920C04 configuration. 01:00.0 Unassigned class [ff00]: Qualcomm Device 011a Subsystem: Device 1c5d:202a Signed-off-by: Daniele Palmas Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260512112458.1048999-1-dnlplm@gmail.com --- drivers/bus/mhi/host/pci_generic.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 7efcbcf5a59e..5836ecb0ea32 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -914,6 +914,16 @@ static const struct mhi_pci_dev_info mhi_telit_fe912c04_info = { .edl_trigger = true, }; +static const struct mhi_pci_dev_info mhi_telit_fe910c04_info = { + .name = "telit-fe910c04", + .config = &modem_telit_fn920c04_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, + .mru_default = 32768, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = { .name = "netprisma-lcur57", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -941,6 +951,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FN920C04 (sdx35) */ {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2020), .driver_data = (kernel_ulong_t) &mhi_telit_fn920c04_info }, + /* Telit FE910C04 (sdx35) */ + {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x202a), + .driver_data = (kernel_ulong_t) &mhi_telit_fe910c04_info }, /* Telit FE912C04 (sdx35) */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2045), .driver_data = (kernel_ulong_t) &mhi_telit_fe912c04_info }, From 519ddf194b158b91439319f6b977b8a465fda0fb Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 2 Mar 2026 14:26:12 +0530 Subject: [PATCH 0878/5214] bus: mhi: ep: Protect mhi_ep_handle_syserr() in the error path All the callers of mhi_ep_handle_syserr() except mhi_ep_process_cmd_ring() are holding the 'state_lock' to avoid the race in setting the MHI state. So do the same in mhi_ep_process_cmd_ring() for sanity. Fixes: e827569062a8 ("bus: mhi: ep: Add support for processing command rings") Cc: stable@vger.kernel.org # 5.18 Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260302085612.18725-1-manivannan.sadhasivam@oss.qualcomm.com --- drivers/bus/mhi/ep/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 5330b03dc636..9db2a2a2c913 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -232,7 +232,9 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele ret = mhi_ep_create_device(mhi_cntrl, ch_id); if (ret) { dev_err(dev, "Error creating device for channel (%u)\n", ch_id); + mutex_lock(&mhi_cntrl->state_lock); mhi_ep_handle_syserr(mhi_cntrl); + mutex_unlock(&mhi_cntrl->state_lock); return ret; } } From f4526ffee6ff9f5845b430957417149eded74bf3 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Tue, 12 May 2026 09:56:07 +0800 Subject: [PATCH 0879/5214] coresight: fix missing error code when trace ID is invalid When coresight_path_assign_trace_id() cannot assign a valid trace ID, coresight_enable_sysfs() takes the err_path goto with ret still 0, returning success to the caller despite no trace session being started. Change coresight_path_assign_trace_id() to return int, moving the IS_VALID_CS_TRACE_ID() check inside it so it returns -EINVAL on failure and 0 on success. Update both callers to propagate this return value directly instead of inspecting path->trace_id after the call. Fixes: d87d76d823d1 ("Coresight: Allocate trace ID after building the path") Reviewed-by: James Clark Reviewed-by: Richard Cheng Signed-off-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260512-fix-trace-id-error-v4-1-eb3de789767a@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-core.c | 23 +++++++++++-------- .../hwtracing/coresight/coresight-etm-perf.c | 5 ++-- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-sysfs.c | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf6..2105bb813940 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -739,8 +739,8 @@ static int coresight_get_trace_id(struct coresight_device *csdev, * Call this after creating the path and before enabling it. This leaves * the trace ID set on the path, or it remains 0 if it couldn't be assigned. */ -void coresight_path_assign_trace_id(struct coresight_path *path, - enum cs_mode mode) +int coresight_path_assign_trace_id(struct coresight_path *path, + enum cs_mode mode) { struct coresight_device *sink = coresight_get_sink(path); struct coresight_node *nd; @@ -750,15 +750,18 @@ void coresight_path_assign_trace_id(struct coresight_path *path, /* Assign a trace ID to the path for the first device that wants to do it */ trace_id = coresight_get_trace_id(nd->csdev, mode, sink); - /* - * 0 in this context is that it didn't want to assign so keep searching. - * Non 0 is either success or fail. - */ - if (trace_id != 0) { - path->trace_id = trace_id; - return; - } + /* 0 means the device has no ID assignment, so keep searching */ + if (trace_id == 0) + continue; + + if (!IS_VALID_CS_TRACE_ID(trace_id)) + return -EINVAL; + + path->trace_id = trace_id; + return 0; } + + return -EINVAL; } /** diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index f85dedf89a3f..89ba7c9a6613 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -324,6 +324,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, struct coresight_device *sink = NULL; struct coresight_device *user_sink = NULL, *last_sink = NULL; struct etm_event_data *event_data = NULL; + int ret; event_data = alloc_event_data(cpu); if (!event_data) @@ -420,8 +421,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, } /* ensure we can allocate a trace ID for this CPU */ - coresight_path_assign_trace_id(path, CS_MODE_PERF); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) { + ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); + if (ret) { cpumask_clear_cpu(cpu, mask); coresight_release_path(path); continue; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 1ea882dffd70..34c7e792adbd 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -153,7 +153,7 @@ int coresight_make_links(struct coresight_device *orig, void coresight_remove_links(struct coresight_device *orig, struct coresight_connection *conn); u32 coresight_get_sink_id(struct coresight_device *csdev); -void coresight_path_assign_trace_id(struct coresight_path *path, +int coresight_path_assign_trace_id(struct coresight_path *path, enum cs_mode mode); #if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index d2a6ed8bcc74..b6a870399e83 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -211,8 +211,8 @@ int coresight_enable_sysfs(struct coresight_device *csdev) goto out; } - coresight_path_assign_trace_id(path, CS_MODE_SYSFS); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) + ret = coresight_path_assign_trace_id(path, CS_MODE_SYSFS); + if (ret) goto err_path; ret = coresight_enable_path(path, CS_MODE_SYSFS); From 3357509593d2d7b1aa04bd2cf188477df7af447b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 4 Apr 2026 12:54:37 +0200 Subject: [PATCH 0880/5214] dt-bindings: clock: qcom,kaanapali-gxclkctl: Correctly use additionalProperties The binding does not reference any other schema, thus should use "additionalProperties: false" to disallow any undocumented properties. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20260404105436.138110-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml index 466c884aa2ba..e868963f659b 100644 --- a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml @@ -44,7 +44,7 @@ required: - power-domains - '#power-domain-cells' -unevaluatedProperties: false +additionalProperties: false examples: - | From d9ff07f459552eabd04f8831bcaa76d761dfe264 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 27 Apr 2026 09:32:11 +0800 Subject: [PATCH 0881/5214] dt-bindings: PCI: Add UltraRISC DP1000 PCIe controller Add UltraRISC DP1000 SoC PCIe controller devicetree bindings. Signed-off-by: Jia Wang Signed-off-by: Manivannan Sadhasivam [bhelgaas: squash MAINTAINERS to driver commit update to touch only once] Signed-off-by: Bjorn Helgaas Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427-ultrarisc-pcie-v4-2-98935f6cdfb5@ultrarisc.com --- .../bindings/pci/ultrarisc,dp1000-pcie.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml new file mode 100644 index 000000000000..512b935bf5d1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/ultrarisc,dp1000-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UltraRISC DP1000 PCIe Host Controller + +description: + UltraRISC DP1000 SoC PCIe host controller is based on the DesignWare PCIe IP. + +maintainers: + - Xincheng Zhang + - Jia Wang + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + const: ultrarisc,dp1000-pcie + + reg: + items: + - description: Data Bus Interface (DBI) registers. + - description: PCIe configuration space region. + + reg-names: + items: + - const: dbi + - const: config + + num-lanes: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [4, 16] + description: Number of lanes to use. + + interrupts: + items: + - description: MSI interrupt + - description: Legacy INTA interrupt + - description: Legacy INTB interrupt + - description: Legacy INTC interrupt + - description: Legacy INTD interrupt + + interrupt-names: + items: + - const: msi + - const: inta + - const: intb + - const: intc + - const: intd + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@21000000 { + compatible = "ultrarisc,dp1000-pcie"; + reg = <0x0 0x21000000 0x0 0x01000000>, + <0x0 0x4fff0000 0x0 0x00010000>; + reg-names = "dbi", "config"; + ranges = <0x81000000 0x0 0x4fbf0000 0x0 0x4fbf0000 0x0 0x00400000>, + <0x82000000 0x0 0x40000000 0x0 0x40000000 0x0 0x0fbf0000>, + <0xc3000000 0x40 0x00000000 0x40 0x00000000 0xd 0x00000000>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + device_type = "pci"; + dma-coherent; + bus-range = <0x0 0xff>; + num-lanes = <16>; + interrupt-parent = <&plic>; + interrupts = <43>, <44>, <45>, <46>, <47>; + interrupt-names = "msi", "inta", "intb", "intc", "intd"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &plic 44>, + <0x0 0x0 0x0 0x2 &plic 45>, + <0x0 0x0 0x0 0x3 &plic 46>, + <0x0 0x0 0x0 0x4 &plic 47>; + }; + }; From 35e3509b632b197f1842f5547c36138b29ac18ee Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Tue, 6 Jan 2026 21:35:11 -0800 Subject: [PATCH 0882/5214] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5332 SoC Add device tree bindings for the CMN PLL block in IPQ5332 SoC, which shares similarities with IPQ9574 but has different output clock frequencies. Add a new header file to export CMN PLL output clock specifiers for IPQ5332 SoC. Acked-by: Krzysztof Kozlowski Signed-off-by: Luo Jie Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-2-f9f7e4efbd79@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + .../dt-bindings/clock/qcom,ipq5332-cmn-pll.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml index de338c05190f..b9c3650e5c4c 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml @@ -25,6 +25,7 @@ properties: compatible: enum: - qcom,ipq5018-cmn-pll + - qcom,ipq5332-cmn-pll - qcom,ipq5424-cmn-pll - qcom,ipq6018-cmn-pll - qcom,ipq8074-cmn-pll diff --git a/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h b/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h new file mode 100644 index 000000000000..172330e43669 --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_IPQ5332_CMN_PLL_H +#define _DT_BINDINGS_CLK_QCOM_IPQ5332_CMN_PLL_H + +/* CMN PLL core clock. */ +#define IPQ5332_CMN_PLL_CLK 0 + +/* The output clocks from CMN PLL of IPQ5332. */ +#define IPQ5332_XO_24MHZ_CLK 1 +#define IPQ5332_SLEEP_32KHZ_CLK 2 +#define IPQ5332_PCS_31P25MHZ_CLK 3 +#define IPQ5332_NSS_300MHZ_CLK 4 +#define IPQ5332_PPE_200MHZ_CLK 5 +#define IPQ5332_ETH_50MHZ_CLK 6 +#endif From 5fc35740c3b32d2c820c97d041282e7bca4ad0bf Mon Sep 17 00:00:00 2001 From: Xincheng Zhang Date: Mon, 27 Apr 2026 09:32:12 +0800 Subject: [PATCH 0883/5214] PCI: ultrarisc: Add UltraRISC DP1000 PCIe Root Complex driver Add DP1000 SoC PCIe Root Complex driver. The controller only supports 32-bit aligned configuration space accesses. Signed-off-by: Xincheng Zhang Signed-off-by: Jia Wang [mani: changed to builtin_platform_driver() to prevent irqchip removal] Signed-off-by: Manivannan Sadhasivam [bhelgaas: squash MAINTAINERS update here] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260427-ultrarisc-pcie-v4-3-98935f6cdfb5@ultrarisc.com --- MAINTAINERS | 8 + drivers/pci/controller/dwc/Kconfig | 13 ++ drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-designware.h | 24 +++ drivers/pci/controller/dwc/pcie-ultrarisc.c | 175 +++++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-ultrarisc.c diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..b52ea4bfe7da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20759,6 +20759,14 @@ S: Maintained F: Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml F: drivers/pci/controller/plda/pcie-starfive.c +PCIE DRIVER FOR ULTRARISC DP1000 +M: Xincheng Zhang +M: Jia Wang +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml +F: drivers/pci/controller/dwc/pcie-ultrarisc.c + PCIE ENDPOINT DRIVER FOR QUALCOMM M: Manivannan Sadhasivam L: linux-pci@vger.kernel.org diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index f2fde13107f2..216ede0a867e 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -560,4 +560,17 @@ config PCIE_VISCONTI_HOST Say Y here if you want PCIe controller support on Toshiba Visconti SoC. This driver supports TMPV7708 SoC. +config PCIE_ULTRARISC + tristate "UltraRISC PCIe host controller" + depends on ARCH_ULTRARISC || COMPILE_TEST + select PCIE_DW_HOST + select PCI_MSI + default y if ARCH_ULTRARISC + help + Enables support for the PCIe controller in the UltraRISC SoC. + This driver supports UR-DP1000 SoC. + + By default, this symbol is enabled when ARCH_ULTRARISC is active, + requiring no further configuration on that platform. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 7177451db8aa..d4f88cfc6229 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o obj-$(CONFIG_PCIE_SPACEMIT_K1) += pcie-spacemit-k1.o obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o +obj-$(CONFIG_PCIE_ULTRARISC) += pcie-ultrarisc.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..865c69866c04 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -71,6 +71,8 @@ /* Synopsys-specific PCIe configuration registers */ #define PCIE_PORT_FORCE 0x708 +/* Bit[7:0] LINK_NUM: Link Number. Not used for endpoint */ +#define PORT_LINK_NUM_MASK GENMASK(7, 0) #define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23) #define PCIE_PORT_AFR 0x70C @@ -98,6 +100,28 @@ #define PCIE_PORT_LANE_SKEW 0x714 #define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0) +/* + * PCIE_TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number + * Register. + * + * This register holds the ack frequency, latency, replay, fast link + * scaling timers, and max function number values. + * + * Bit[30:29] FAST_LINK_SCALING_FACTOR: Fast Link Timer Scaling Factor. + * 0x0 (SF_1024): Scaling Factor is 1024 (1ms is 1us). + * When the LTSSM is in Config or L12 Entry State, 1ms + * timer is 2us, 2ms timer is 4us and 3ms timer is 6us. + * 0x1 (SF_256): Scaling Factor is 256 (1ms is 4us) + * 0x2 (SF_64): Scaling Factor is 64 (1ms is 16us) + * 0x3 (SF_16): Scaling Factor is 16 (1ms is 64us) + */ +#define PCIE_TIMER_CTRL_MAX_FUNC_NUM 0x718 +#define PORT_FLT_SF_MASK GENMASK(30, 29) +#define PORT_FLT_SF_VAL_1024 0x0 +#define PORT_FLT_SF_VAL_256 0x1 +#define PORT_FLT_SF_VAL_64 0x2 +#define PORT_FLT_SF_VAL_16 0x3 + #define PCIE_PORT_DEBUG0 0x728 #define PORT_LOGIC_LTSSM_STATE_MASK 0x3f #define PORT_LOGIC_LTSSM_STATE_L0 0x11 diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c new file mode 100644 index 000000000000..6ee661ceff67 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DWC PCIe RC driver for UltraRISC SoCs + * + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCIE_CUS_CORE 0x400000 + +#define LTSSM_ENABLE BIT(7) +#define FAST_LINK_MODE BIT(12) +#define HOLD_PHY_RST BIT(14) +#define L1SUB_DISABLE BIT(15) + +#define ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS 0x6 + +static struct pci_ops ultrarisc_pci_ops = { + .map_bus = dw_pcie_own_conf_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct pci_host_bridge *bridge = pp->bridge; + u8 cap_exp; + u32 val; + + bridge->ops = &ultrarisc_pci_ops; + + if (dw_pcie_link_up(pci)) + return 0; + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val &= ~FAST_LINK_MODE; + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + val = dw_pcie_readl_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM); + FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64); + dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val); + + cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2); + FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT); + dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val); + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); + FIELD_MODIFY(PORT_LINK_NUM_MASK, &val, 0); + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); + + val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_DEVCTL2); + FIELD_MODIFY(PCI_EXP_DEVCTL2_COMP_TIMEOUT, &val, + ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS); + dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_DEVCTL2, val); + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val &= ~(HOLD_PHY_RST | L1SUB_DISABLE); + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + return 0; +} + +static void ultrarisc_pcie_pme_turn_off(struct dw_pcie_rp *pp) +{ + /* + * DP1000 does not support sending PME_Turn_Off from the RC. + * Keep this callback empty to skip the generic MSG TLP path. + */ +} + +static const struct dw_pcie_host_ops ultrarisc_pcie_host_ops = { + .init = ultrarisc_pcie_host_init, + .pme_turn_off = ultrarisc_pcie_pme_turn_off, +}; + +static int ultrarisc_pcie_start_link(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val |= LTSSM_ENABLE; + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = ultrarisc_pcie_start_link, +}; + +static int ultrarisc_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie_rp *pp; + struct dw_pcie *pci; + int ret; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + /* Set a default value suitable for at most 16 in and 16 out windows */ + pci->atu_size = SZ_8K; + + pp = &pci->pp; + + platform_set_drvdata(pdev, pci); + + pp->num_vectors = MAX_MSI_IRQS; + /* No L2/L3 Ready indication is available on this platform */ + pp->skip_l23_ready = true; + pp->ops = &ultrarisc_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int ultrarisc_pcie_suspend_noirq(struct device *dev) +{ + struct dw_pcie *pci = dev_get_drvdata(dev); + + return dw_pcie_suspend_noirq(pci); +} + +static int ultrarisc_pcie_resume_noirq(struct device *dev) +{ + struct dw_pcie *pci = dev_get_drvdata(dev); + + return dw_pcie_resume_noirq(pci); +} + +static const struct dev_pm_ops ultrarisc_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(ultrarisc_pcie_suspend_noirq, + ultrarisc_pcie_resume_noirq) +}; + +static const struct of_device_id ultrarisc_pcie_of_match[] = { + { + .compatible = "ultrarisc,dp1000-pcie", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ultrarisc_pcie_of_match); + +static struct platform_driver ultrarisc_pcie_driver = { + .driver = { + .name = "ultrarisc-pcie", + .of_match_table = ultrarisc_pcie_of_match, + .suppress_bind_attrs = true, + .pm = &ultrarisc_pcie_pm_ops, + }, + .probe = ultrarisc_pcie_probe, +}; +builtin_platform_driver(ultrarisc_pcie_driver); + +MODULE_DESCRIPTION("UltraRISC DP1000 DWC PCIe host controller"); +MODULE_LICENSE("GPL"); From 88c543fff756450bcd04ec4560c4440be36c9e75 Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Tue, 6 Jan 2026 21:35:10 -0800 Subject: [PATCH 0884/5214] clk: qcom: cmnpll: Account for reference clock divider The clk_cmn_pll_recalc_rate() function must account for the reference clock divider programmed in CMN_PLL_REFCLK_CONFIG. Without this fix, platforms with a reference divider other than 1 calculate incorrect CMN PLL rates. For example, on IPQ5332 where the reference divider is 2, the computed rate becomes twice the actual output. Read CMN_PLL_REFCLK_DIV and divide the parent rate by this value before applying the 2 * FACTOR scaling. This yields the correct rate calculation: rate = (parent_rate / ref_div) * 2 * factor. Maintain backward compatibility with earlier platforms (e.g. IPQ9574, IPQ5424, IPQ5018) that use ref_div = 1. Fixes: f81715a4c87c ("clk: qcom: Add CMN PLL clock controller driver for IPQ SoC") Signed-off-by: Luo Jie Reviewed-by: Konrad Dybcio Tested-by: George Moussalem Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-1-f9f7e4efbd79@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/ipq-cmn-pll.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 5763e4df59a1..889c176089c2 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -199,7 +199,7 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); - u32 val, factor; + u32 val, factor, ref_div; /* * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted @@ -207,8 +207,15 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, */ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); + if (WARN_ON(factor == 0)) + factor = 1; - return parent_rate * 2 * factor; + regmap_read(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, &val); + ref_div = FIELD_GET(CMN_PLL_REFCLK_DIV, val); + if (WARN_ON(ref_div == 0)) + ref_div = 1; + + return div_u64((u64)parent_rate * 2 * factor, ref_div); } static int clk_cmn_pll_determine_rate(struct clk_hw *hw, From 1dcbf4195a262d57f4da812248cdbbcdc81bf8d8 Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Tue, 6 Jan 2026 21:35:12 -0800 Subject: [PATCH 0885/5214] clk: qcom: cmnpll: Add IPQ5332 SoC support The CMN PLL in IPQ5332 SoC produces different output clocks when compared to IPQ9574. While most clock outputs match IPQ9574, the ethernet PHY/switch (50 Mhz) and PPE clocks (200 Mhz) in IPQ5332 are different. Add IPQ5332-specific clock definitions and of_device_id entry. Signed-off-by: Luo Jie Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-3-f9f7e4efbd79@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/ipq-cmn-pll.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 889c176089c2..441e88101ea3 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ /* @@ -20,6 +20,11 @@ * and an output clock to NSS (network subsystem) at 300 MHZ. The other output * clocks from CMN PLL on IPQ5424 are the same as IPQ9574. * + * On the IPQ5332 SoC, the CMN PLL provides a single 50 MHZ clock output to + * the Ethernet PHY (or switch) via the UNIPHY (PCS). It also supplies a 200 + * MHZ clock to the PPE. The remaining fixed-rate clocks to the GCC and PCS + * are the same as those in the IPQ9574 SoC. + * * +---------+ * | GCC | * +--+---+--+ @@ -51,6 +56,7 @@ #include #include +#include #include #include #include @@ -131,6 +137,16 @@ static const struct cmn_pll_fixed_output_clk ipq8074_output_clks[] = { { /* Sentinel */ } }; +static const struct cmn_pll_fixed_output_clk ipq5332_output_clks[] = { + CLK_PLL_OUTPUT(IPQ5332_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), + CLK_PLL_OUTPUT(IPQ5332_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), + CLK_PLL_OUTPUT(IPQ5332_PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), + CLK_PLL_OUTPUT(IPQ5332_NSS_300MHZ_CLK, "nss-300mhz", 300000000UL), + CLK_PLL_OUTPUT(IPQ5332_PPE_200MHZ_CLK, "ppe-200mhz", 200000000UL), + CLK_PLL_OUTPUT(IPQ5332_ETH_50MHZ_CLK, "eth-50mhz", 50000000UL), + { /* Sentinel */ } +}; + static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), @@ -468,6 +484,7 @@ static const struct dev_pm_ops ipq_cmn_pll_pm_ops = { static const struct of_device_id ipq_cmn_pll_clk_ids[] = { { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, + { .compatible = "qcom,ipq5332-cmn-pll", .data = &ipq5332_output_clks }, { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, { .compatible = "qcom,ipq8074-cmn-pll", .data = &ipq8074_output_clks }, From dbabf6a32ffb69a604f966ec01a20a060836939d Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Mon, 30 Mar 2026 10:13:49 +0000 Subject: [PATCH 0886/5214] dt-bindings: clock: qcom,sm6125-dispcc: reference qcom,gcc.yaml Just like most of Qualcomm clock controllers, we can reference common qcom,gcc.yaml schema to unify the common parts of the binding. This also adds the '#reset-cells' property which is permitted for the SM6125 SoC clock controllers, but not listed as a valid property. Fixes: bb4d28e377cf ("arm64: dts: qcom: sm6125: Add missing MDSS core reset") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603150629.GYoouFwZ-lkp@intel.com/ Signed-off-by: Biswapriyo Nath Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260330-ginkgo-add-usb-ir-vib-v3-2-c4b778b0d7f8@gmail.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,dispcc-sm6125.yaml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml index ef2b1e204430..a177a1934b19 100644 --- a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml @@ -42,12 +42,6 @@ properties: - const: cfg_ahb_clk - const: gcc_disp_gpll0_div_clk_src - '#clock-cells': - const: 1 - - '#power-domain-cells': - const: 1 - power-domains: description: A phandle and PM domain specifier for the CX power domain. @@ -58,18 +52,16 @@ properties: A phandle to an OPP node describing the power domain's performance point. maxItems: 1 - reg: - maxItems: 1 - required: - compatible - - reg - clocks - clock-names - - '#clock-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | @@ -101,6 +93,7 @@ examples: power-domains = <&rpmpd SM6125_VDDCX>; #clock-cells = <1>; + #reset-cells = <1>; #power-domain-cells = <1>; }; ... From 1e1b554c2b910591aa3fffdb8077a2ca33bf3cb2 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Fri, 10 Apr 2026 11:55:07 +0530 Subject: [PATCH 0887/5214] PCI: dwc: Apply ECRC workaround for DesignWare cores prior to 5.10a The ECRC (TLP digest) workaround was originally applied only for DesignWare core version 4.90a. Per discussion in Synopsys case, the dependency of the iATU TD bit on ECRC generation was removed in 5.10a, so apply the workaround for all DWC versions below that release. Replace the misleading comment that referred to raw version constants with readable DesignWare release name to help readability. Fixes: b210b1595606 ("PCI: dwc: Apply ECRC workaround to DesignWare 5.00a as well") Signed-off-by: Manikanta Maddireddy [mani: corrected fixes tag format] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260410062507.657453-1-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-designware.c | 16 ++++++++-------- drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..76c9a0a10367 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -487,13 +487,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg static inline u32 dw_pcie_enable_ecrc(u32 val) { /* - * DWC versions 0x3530302a and 0x3536322a have a design issue where - * the 'TD' bit in the Control register-1 of the ATU outbound - * region acts like an override for the ECRC setting, i.e., the - * presence of TLP Digest (ECRC) in the outgoing TLPs is solely - * determined by this bit. This is contrary to the PCIe spec which - * says that the enablement of the ECRC is solely determined by the - * AER registers. + * DesignWare core versions prior to 5.10A have a design issue where the + * 'TD' bit in the Control register-1 of the ATU outbound region acts + * like an override for the ECRC setting, i.e., the presence of TLP + * Digest (ECRC) in the outgoing TLPs is solely determined by this + * bit. This is contrary to the PCIe spec which says that the + * enablement of the ECRC is solely determined by the AER + * registers. * * Because of this, even when the ECRC is enabled through AER * registers, the transactions going through ATU won't have TLP @@ -563,7 +563,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A)) + if (!dw_pcie_ver_is_ge(pci, 510A)) val = dw_pcie_enable_ecrc(val); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..a07b7abda41f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -35,6 +35,7 @@ #define DW_PCIE_VER_480A 0x3438302a #define DW_PCIE_VER_490A 0x3439302a #define DW_PCIE_VER_500A 0x3530302a +#define DW_PCIE_VER_510A 0x3531302a #define DW_PCIE_VER_520A 0x3532302a #define DW_PCIE_VER_540A 0x3534302a #define DW_PCIE_VER_562A 0x3536322a From 7edfb7fb58ee058298e18fde76a6077ef17d19d8 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 17:36:02 -0700 Subject: [PATCH 0888/5214] clk: rockchip: allow COMPILE_TEST builds COMMON_CLK_ROCKCHIP already gates the Rockchip clock objects inside the Rockchip clock Makefile. Allow selecting it for COMPILE_TEST and use it for the parent Makefile descent instead of ARCH_ROCKCHIP. The per-SoC Rockchip clock symbols already have COMPILE_TEST dependencies, so this exposes the existing build coverage to other architectures without selecting the Rockchip platform. Tested with: make LLVM=1 ARCH=loongarch drivers/clk/rockchip/ Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Reviewed-by: Brian Masney Link: https://patch.msgid.link/20260509003602.956186-1-rosenp@gmail.com Signed-off-by: Heiko Stuebner --- drivers/clk/Makefile | 2 +- drivers/clk/rockchip/Kconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a3e2862ebd7e..e83c60320bfe 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -141,7 +141,7 @@ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ obj-y += ralink/ obj-y += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_COMMON_CLK_ROCKCHIP) += rockchip/ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-y += socfpga/ diff --git a/drivers/clk/rockchip/Kconfig b/drivers/clk/rockchip/Kconfig index 7e1433502061..85133498f013 100644 --- a/drivers/clk/rockchip/Kconfig +++ b/drivers/clk/rockchip/Kconfig @@ -3,7 +3,7 @@ config COMMON_CLK_ROCKCHIP bool "Rockchip clock controller common support" - depends on ARCH_ROCKCHIP + depends on ARCH_ROCKCHIP || COMPILE_TEST default ARCH_ROCKCHIP help Say y here to enable common clock controller for Rockchip platforms. From ae04f36f752ad51343389d1ea8433dc8ff4ffa1e Mon Sep 17 00:00:00 2001 From: Antti Laakso Date: Thu, 19 Mar 2026 17:50:30 +0200 Subject: [PATCH 0889/5214] platform/x86: int3472: Match MSI laptop board name Ensure MSI system is correct by checking board name too. Signed-off-by: Antti Laakso Reviewed-by: Daniel Scally Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/tps68470_board_data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index c1ddbf9a82c0..e65067358301 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -404,6 +404,7 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"), }, .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data, }, From 621e4f762ed63830d162e7cc994ebab8758fa355 Mon Sep 17 00:00:00 2001 From: Antti Laakso Date: Thu, 19 Mar 2026 17:50:31 +0200 Subject: [PATCH 0890/5214] platform/x86: int3472: Add more MSI AI evo laptops The MSI prestige AI EVO 13 and 16 have the same camera configuration as model 14. Use the same platform data for all. Signed-off-by: Antti Laakso Reviewed-by: Daniel Scally [Sakari Ailus: Use user-reported board name for model 16.] Signed-off-by: Sakari Ailus --- .../x86/intel/int3472/tps68470_board_data.c | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index e65067358301..28c9e12c85bf 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -238,7 +238,7 @@ static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = { REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"), }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_core_reg_init_data = { .constraints = { .min_uV = 1200000, .max_uV = 1200000, @@ -249,7 +249,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_da .consumer_supplies = ovti5675_dvdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_ana_reg_init_data = { .constraints = { .min_uV = 2815200, .max_uV = 2815200, @@ -260,7 +260,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_dat .consumer_supplies = ovti5675_avdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -269,7 +269,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_dat }, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vsio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -280,12 +280,12 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_da .consumer_supplies = ovti5675_dovdd_consumer_supplies, }; -static const struct tps68470_regulator_platform_data msi_p14_ai_evo_tps68470_pdata = { +static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps68470_pdata = { .reg_init_data = { - [TPS68470_CORE] = &msi_p14_ai_evo_tps68470_core_reg_init_data, - [TPS68470_ANA] = &msi_p14_ai_evo_tps68470_ana_reg_init_data, - [TPS68470_VIO] = &msi_p14_ai_evo_tps68470_vio_reg_init_data, - [TPS68470_VSIO] = &msi_p14_ai_evo_tps68470_vsio_reg_init_data, + [TPS68470_CORE] = &msi_prestige_ai_evo_tps68470_core_reg_init_data, + [TPS68470_ANA] = &msi_prestige_ai_evo_tps68470_ana_reg_init_data, + [TPS68470_VIO] = &msi_prestige_ai_evo_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &msi_prestige_ai_evo_tps68470_vsio_reg_init_data, }, }; @@ -315,7 +315,7 @@ static struct gpiod_lookup_table dell_7212_int3479_gpios = { } }; -static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { +static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { .dev_id = "i2c-OVTI5675:00", .table = { GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), @@ -323,13 +323,13 @@ static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { } }; -static const struct property_entry msi_p14_ai_evo_gpio_props[] = { +static const struct property_entry msi_prestige_ai_evo_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } }; -static const struct software_node msi_p14_ai_evo_tps68470_gpio_swnode = { - .properties = msi_p14_ai_evo_gpio_props, +static const struct software_node msi_prestige_ai_evo_tps68470_gpio_swnode = { + .properties = msi_prestige_ai_evo_gpio_props, }; static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { @@ -361,13 +361,13 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = }, }; -static const struct int3472_tps68470_board_data msi_p14_ai_evo_tps68470_board_data = { +static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = { .dev_name = "i2c-INT3472:06", - .tps68470_regulator_pdata = &msi_p14_ai_evo_tps68470_pdata, - .tps68470_gpio_swnode = &msi_p14_ai_evo_tps68470_gpio_swnode, + .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata, + .tps68470_gpio_swnode = &msi_prestige_ai_evo_tps68470_gpio_swnode, .n_gpiod_lookups = 1, .tps68470_gpio_lookup_tables = { - &msi_p14_ai_evo_ovti5675_gpios, + &msi_prestige_ai_evo_ovti5675_gpios, }, }; @@ -400,13 +400,29 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { }, .driver_data = (void *)&dell_7212_tps68470_board_data, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 13 AI+ Evo A2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-13Q3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"), }, - .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 16 AI+ Evo B2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-15A3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, }, { } }; From c93a5f038ccc11ed8558ce642f62d5ede701a348 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 16 Apr 2026 11:40:36 -0400 Subject: [PATCH 0891/5214] integrity: Check for NULL returned by asymmetric_key_public_key Check for a NULL pointer returned by asymmetric_key_public_key and return -ENOKEY in this case. Signed-off-by: Stefan Berger Tested-by: Kamlesh Kumar Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 6e68ec3becbd..a791ad43b3fb 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -108,6 +108,10 @@ int asymmetric_verify(struct key *keyring, const char *sig, pks.hash_algo = hash_algo_name[hdr->hash_algo]; pk = asymmetric_key_public_key(key); + if (!pk) { + ret = -ENOKEY; + goto out; + } pks.pkey_algo = pk->pkey_algo; if (!strcmp(pk->pkey_algo, "rsa")) { pks.encoding = "pkcs1"; From 474c78c26744b6921549f0c679b7507a57cfcbb9 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 16 Apr 2026 11:40:37 -0400 Subject: [PATCH 0892/5214] integrity: Check that algo parameter is within valid range Check that the algo parameter passed to calc_file_id_hash is within valid range. Do this in asymmetric_verify_v3 since this value will also be passed to a hashless signature verification function from here. Signed-off-by: Stefan Berger Tested-by: Kamlesh Kumar Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index a791ad43b3fb..ed171a627d18 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -139,7 +139,7 @@ int asymmetric_verify(struct key *keyring, const char *sig, /* * calc_file_id_hash - calculate the hash of the ima_file_id struct data * @type: xattr type [enum evm_ima_xattr_type] - * @algo: hash algorithm [enum hash_algo] + * @algo: hash algorithm [enum hash_algo]; caller must ensure valid value * @digest: pointer to the digest to be hashed * @hash: (out) pointer to the hash * @@ -187,6 +187,9 @@ int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen, struct ima_max_digest_data hash; int rc; + if (algo >= HASH_ALGO__LAST) + return -ENOPKG; + rc = calc_file_id_hash(hdr->type, algo, data, &hash); if (rc) return -EINVAL; From 33aa0c8cf0657b9588f835645cbbcebe44a2a1ee Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 16 Apr 2026 11:40:38 -0400 Subject: [PATCH 0893/5214] integrity: Refactor asymmetric_verify for reusability Refactor asymmetric_verify for reusability. Have it call asymmetric_verify_common with the signature verification key and the public_key structure as parameters. sigv3 support for ML-DSA will need to check the public key type first to decide how to do the signature verification and therefore will have these parameters available for calling asymmetric_verify_common. Signed-off-by: Stefan Berger Tested-by: Kamlesh Kumar Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 62 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index ed171a627d18..a4eb73bba6d2 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -79,18 +79,25 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) return key; } -int asymmetric_verify(struct key *keyring, const char *sig, - int siglen, const char *data, int datalen) +/** + * asymmetric_verify_common -- sigv2 and sigv3 common verify function + * @key: The key to use for signature verification; caller must free it + * @pk: The associated public key; must not be NULL + * @sig: The xattr signature + * @siglen: The length of the xattr signature; must be at least + * sizeof(struct signature_v2_hdr) + * @data: The data to verify the signature on + * @datalen: Length of @data + */ +static int asymmetric_verify_common(const struct key *key, + const struct public_key *pk, + const char *sig, int siglen, + const char *data, int datalen) { - struct public_key_signature pks; struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; - const struct public_key *pk; - struct key *key; + struct public_key_signature pks; int ret; - if (siglen <= sizeof(*hdr)) - return -EBADMSG; - siglen -= sizeof(*hdr); if (siglen != be16_to_cpu(hdr->sig_size)) @@ -99,19 +106,9 @@ int asymmetric_verify(struct key *keyring, const char *sig, if (hdr->hash_algo >= HASH_ALGO__LAST) return -ENOPKG; - key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid)); - if (IS_ERR(key)) - return PTR_ERR(key); - memset(&pks, 0, sizeof(pks)); pks.hash_algo = hash_algo_name[hdr->hash_algo]; - - pk = asymmetric_key_public_key(key); - if (!pk) { - ret = -ENOKEY; - goto out; - } pks.pkey_algo = pk->pkey_algo; if (!strcmp(pk->pkey_algo, "rsa")) { pks.encoding = "pkcs1"; @@ -131,11 +128,38 @@ int asymmetric_verify(struct key *keyring, const char *sig, pks.s_size = siglen; ret = verify_signature(key, &pks); out: - key_put(key); pr_debug("%s() = %d\n", __func__, ret); return ret; } +int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen) +{ + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + const struct public_key *pk; + struct key *key; + int ret; + + if (siglen <= sizeof(*hdr)) + return -EBADMSG; + + key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid)); + if (IS_ERR(key)) + return PTR_ERR(key); + pk = asymmetric_key_public_key(key); + if (!pk) { + ret = -ENOKEY; + goto out; + } + + ret = asymmetric_verify_common(key, pk, sig, siglen, data, datalen); + +out: + key_put(key); + + return ret; +} + /* * calc_file_id_hash - calculate the hash of the ima_file_id struct data * @type: xattr type [enum evm_ima_xattr_type] From 489d7e2e7e9a31faa38ca25be0e6cbe3eea2960f Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 16 Apr 2026 11:40:39 -0400 Subject: [PATCH 0894/5214] integrity: Add support for sigv3 verification using ML-DSA keys Add support for sigv3 signature verification using ML-DSA in pure mode. When a sigv3 signature is verified, first check whether the key to use for verification is an ML-DSA key and therefore uses a hashless signature verification scheme. The hashless signature verification method uses the ima_file_id structure directly for signature verification rather than its digest. Suggested-by: Eric Biggers Signed-off-by: Stefan Berger Tested-by: Kamlesh Kumar Signed-off-by: Mimi Zohar --- security/integrity/digsig_asymmetric.c | 89 ++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index a4eb73bba6d2..b4c23a0ed68f 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -204,20 +204,99 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type, return rc; } +/** + * asymmetric_verify_v3_hashless - Use hashless signature verification on sigv3 + * @key: The key to use for signature verification; caller must free it + * @pk: The associated public key; must not be NULL + * @encoding: The encoding the key type uses + * @sig: The xattr signature + * @siglen: The length of the xattr signature; must be at least + * sizeof(struct signature_v2_hdr) + * @algo: hash algorithm [enum hash_algo]; caller must ensure valid value + * @digest: The file digest + * + * Create an ima_file_id structure and use it for signature verification + * directly. This can be used for ML-DSA in pure mode for example. + */ +static int asymmetric_verify_v3_hashless(struct key *key, + const struct public_key *pk, + const char *encoding, + const char *sig, int siglen, + u8 algo, + const u8 *digest) +{ + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + struct ima_file_id file_id = { + .hash_type = hdr->type, + .hash_algorithm = algo, + }; + size_t digest_size = hash_digest_size[algo]; + struct public_key_signature pks = { + .m = (u8 *)&file_id, + .m_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size), + .s = hdr->sig, + .s_size = siglen - sizeof(*hdr), + .pkey_algo = pk->pkey_algo, + .hash_algo = "none", + .encoding = encoding, + }; + int ret; + + if (hdr->type != IMA_VERITY_DIGSIG && + hdr->type != EVM_IMA_XATTR_DIGSIG && + hdr->type != EVM_XATTR_PORTABLE_DIGSIG) + return -EINVAL; + + if (pks.s_size != be16_to_cpu(hdr->sig_size)) + return -EBADMSG; + + memcpy(file_id.hash, digest, digest_size); + + ret = verify_signature(key, &pks); + pr_debug("%s() = %d\n", __func__, ret); + return ret; +} + int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen, const char *data, int datalen, u8 algo) { struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; struct ima_max_digest_data hash; + const struct public_key *pk; + struct key *key; int rc; if (algo >= HASH_ALGO__LAST) return -ENOPKG; - rc = calc_file_id_hash(hdr->type, algo, data, &hash); - if (rc) - return -EINVAL; + if (siglen <= sizeof(*hdr)) + return -EBADMSG; - return asymmetric_verify(keyring, sig, siglen, hash.digest, - hash.hdr.length); + key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid)); + if (IS_ERR(key)) + return PTR_ERR(key); + + pk = asymmetric_key_public_key(key); + if (!pk) { + rc = -ENOKEY; + goto out; + } + if (!strncmp(pk->pkey_algo, "mldsa", 5)) { + rc = asymmetric_verify_v3_hashless(key, pk, "raw", + sig, siglen, algo, data); + } else { + rc = calc_file_id_hash(hdr->type, algo, data, &hash); + if (rc) { + rc = -EINVAL; + goto out; + } + + rc = asymmetric_verify_common(key, pk, sig, siglen, hash.digest, + hash.hdr.length); + } + +out: + key_put(key); + + return rc; } From 11143a19f5b8dc8f414deab87571134f9f447313 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 23 Apr 2026 23:30:00 +0800 Subject: [PATCH 0895/5214] evm: terminate and bound the evm_xattrs read buffer evm_read_xattrs() allocates size + 1 bytes, fills them from the list of enabled xattrs, and then passes strlen(temp) to simple_read_from_buffer(). When no configured xattrs are enabled, the fill loop stores nothing and temp[0] remains uninitialized, so strlen() reads beyond initialized memory. Explicitly terminate the buffer after allocation, use snprintf() for each formatted line, and pass the accumulated length, without risk of truncation, to simple_read_from_buffer(). Fixes: fa516b66a1bf ("EVM: Allow runtime modification of the set of verified xattrs") Signed-off-by: Pengpeng Hou Reviewed-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/evm/evm_secfs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index acd840461902..4baf5e23bc97 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -127,8 +127,8 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { char *temp; - int offset = 0; - ssize_t rc, size = 0; + size_t offset = 0, size = 0; + ssize_t rc; struct xattr_list *xattr; if (*ppos != 0) @@ -151,16 +151,22 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, return -ENOMEM; } + temp[size] = '\0'; + + /* + * No truncation possible: size is computed over the same enabled + * xattrs under xattr_list_mutex, so offset never exceeds size. + */ list_for_each_entry(xattr, &evm_config_xattrnames, list) { if (!xattr->enabled) continue; - sprintf(temp + offset, "%s\n", xattr->name); - offset += strlen(xattr->name) + 1; + offset += snprintf(temp + offset, size + 1 - offset, "%s\n", + xattr->name); } mutex_unlock(&xattr_list_mutex); - rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + rc = simple_read_from_buffer(buf, count, ppos, temp, offset); kfree(temp); From 5dc31cd4a91a7f006d23efa97a5594a7e3ac7790 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 16 Apr 2026 21:16:25 -0700 Subject: [PATCH 0896/5214] PCI: qcom: Set max OPP before DBI access during resume During resume, qcom_pcie_icc_opp_update() may access DBI registers before the OPP votes are restored, triggering NoC errors. Set the PCIe controller to the maximum OPP first in resume_noirq(), then proceed with link/DBI accesses. The OPP is later updated again based on the actual link bandwidth requirements. Introduce a helper to reuse the max-OPP setup code and share it with probe(). Fixes: 5b6272e0efd5 ("PCI: qcom: Add OPP support to scale performance") Signed-off-by: Qiang Yu [mani: commit log and error log rewording] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260416-setmaxopp-v1-1-6a74e2d945a0@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 42 ++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index af6bf5cce65b..f21f3806fc1f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1613,6 +1613,22 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie) } } +static int qcom_pcie_set_max_opp(struct device *dev) +{ + unsigned long max_freq = ULONG_MAX; + struct dev_pm_opp *opp; + int ret; + + opp = dev_pm_opp_find_freq_floor(dev, &max_freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + ret = dev_pm_opp_set_opp(dev, opp); + dev_pm_opp_put(opp); + + return ret; +} + static int qcom_pcie_link_transition_count(struct seq_file *s, void *data) { struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private); @@ -1845,9 +1861,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) struct qcom_pcie_perst *perst, *tmp_perst; struct qcom_pcie_port *port, *tmp_port; const struct qcom_pcie_cfg *pcie_cfg; - unsigned long max_freq = ULONG_MAX; struct device *dev = &pdev->dev; - struct dev_pm_opp *opp; struct qcom_pcie *pcie; struct dw_pcie_rp *pp; struct resource *res; @@ -1951,21 +1965,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) * probe(), OPP will be updated using qcom_pcie_icc_opp_update(). */ if (!ret) { - opp = dev_pm_opp_find_freq_floor(dev, &max_freq); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - dev_err_probe(pci->dev, ret, - "Unable to find max freq OPP\n"); - goto err_pm_runtime_put; - } else { - ret = dev_pm_opp_set_opp(dev, opp); - } - - dev_pm_opp_put(opp); + ret = qcom_pcie_set_max_opp(dev); if (ret) { - dev_err_probe(pci->dev, ret, - "Failed to set OPP for freq %lu\n", - max_freq); + dev_err_probe(dev, ret, "Failed to set max OPP\n"); goto err_pm_runtime_put; } @@ -2100,6 +2102,14 @@ static int qcom_pcie_resume_noirq(struct device *dev) return 0; if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->use_pm_opp) { + ret = qcom_pcie_set_max_opp(dev); + if (ret) { + dev_err(dev, "Failed to set max OPP: %d\n", ret); + return ret; + } + } + ret = icc_enable(pcie->icc_cpu); if (ret) { dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); From 3b4ec7dcdf89d7a88d0d0ca2ca723955b1586eb3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:30 -0700 Subject: [PATCH 0897/5214] KVM: x86: Add dedicated storage for guest RIP Add kvm_vcpu_arch.rip to track guest RIP instead of including it in the generic regs[] array. Decoupling RIP from regs[] will allow using a *completely* arbitrary index for RIP, as opposed to the mostly-arbitrary index that is currently used. That in turn will allow using indices 16-31 to track R16-R31 that are coming with APX. Note, although RIP can used for addressing, it does NOT have an architecturally defined index, and so can't be reached via flows like get_vmx_mem_address() where KVM "blindly" reads a general purpose register given the SIB information reported by hardware. For RIP-relative addressing, hardware reports the full "offset" in vmcs.EXIT_QUALIFICATION. Note #2, keep the available/dirty tracking as RSP is context switched through the VMCS, i.e. needs to be cached for VMX. Opportunistically rename NR_VCPU_REGS to NR_VCPU_GENERAL_PURPOSE_REGS to better capture what it tracks, and so that KVM can slot in R16-R13 without running into weirdness where KVM's definition of "EXREG" doesn't line up with APX's definition of "extended reg". No functional change intended. Cc: Chang S. Bae Signed-off-by: Sean Christopherson Reviewed-by: Chang S. Bae Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-2-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 10 ++++++---- arch/x86/kvm/kvm_cache_regs.h | 12 ++++++++---- arch/x86/kvm/svm/sev.c | 2 +- arch/x86/kvm/svm/svm.c | 6 +++--- arch/x86/kvm/vmx/vmx.c | 8 ++++---- arch/x86/kvm/vmx/vmx.h | 2 +- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c470e40a00aa..68a11325e8bc 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -191,10 +191,11 @@ enum kvm_reg { VCPU_REGS_R14 = __VCPU_REGS_R14, VCPU_REGS_R15 = __VCPU_REGS_R15, #endif - VCPU_REGS_RIP, - NR_VCPU_REGS, + NR_VCPU_GENERAL_PURPOSE_REGS, - VCPU_EXREG_PDPTR = NR_VCPU_REGS, + VCPU_REG_RIP = NR_VCPU_GENERAL_PURPOSE_REGS, + + VCPU_EXREG_PDPTR, VCPU_EXREG_CR0, /* * Alias AMD's ERAPS (not a real register) to CR3 so that common code @@ -799,7 +800,8 @@ struct kvm_vcpu_arch { * rip and regs accesses must go through * kvm_{register,rip}_{read,write} functions. */ - unsigned long regs[NR_VCPU_REGS]; + unsigned long regs[NR_VCPU_GENERAL_PURPOSE_REGS]; + unsigned long rip; u32 regs_avail; u32 regs_dirty; diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 8ddb01191d6f..9b7df9de0e87 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -112,7 +112,7 @@ static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu */ static inline unsigned long kvm_register_read_raw(struct kvm_vcpu *vcpu, int reg) { - if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_REGS)) + if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_GENERAL_PURPOSE_REGS)) return 0; if (!kvm_register_is_available(vcpu, reg)) @@ -124,7 +124,7 @@ static inline unsigned long kvm_register_read_raw(struct kvm_vcpu *vcpu, int reg static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, unsigned long val) { - if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_REGS)) + if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_GENERAL_PURPOSE_REGS)) return; vcpu->arch.regs[reg] = val; @@ -133,12 +133,16 @@ static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, static inline unsigned long kvm_rip_read(struct kvm_vcpu *vcpu) { - return kvm_register_read_raw(vcpu, VCPU_REGS_RIP); + if (!kvm_register_is_available(vcpu, VCPU_REG_RIP)) + kvm_x86_call(cache_reg)(vcpu, VCPU_REG_RIP); + + return vcpu->arch.rip; } static inline void kvm_rip_write(struct kvm_vcpu *vcpu, unsigned long val) { - kvm_register_write_raw(vcpu, VCPU_REGS_RIP, val); + vcpu->arch.rip = val; + kvm_register_mark_dirty(vcpu, VCPU_REG_RIP); } static inline unsigned long kvm_rsp_read(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index c2126b3c3072..940b97d4a852 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -967,7 +967,7 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) save->r14 = svm->vcpu.arch.regs[VCPU_REGS_R14]; save->r15 = svm->vcpu.arch.regs[VCPU_REGS_R15]; #endif - save->rip = svm->vcpu.arch.regs[VCPU_REGS_RIP]; + save->rip = svm->vcpu.arch.rip; /* Sync some non-GPR registers before encrypting */ save->xcr0 = svm->vcpu.arch.xcr0; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index e7fdd7a9c280..85edaee27b03 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4420,7 +4420,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX]; svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP]; - svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP]; + svm->vmcb->save.rip = vcpu->arch.rip; /* * Disable singlestep if we're injecting an interrupt/exception. @@ -4506,7 +4506,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vcpu->arch.cr2 = svm->vmcb->save.cr2; vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax; vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp; - vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip; + vcpu->arch.rip = svm->vmcb->save.rip; } vcpu->arch.regs_dirty = 0; @@ -4946,7 +4946,7 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram) svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX]; svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP]; - svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP]; + svm->vmcb->save.rip = vcpu->arch.rip; nested_svm_simple_vmexit(svm, SVM_EXIT_SW); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a29896a9ef14..577b0c6286ad 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -2604,8 +2604,8 @@ void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) case VCPU_REGS_RSP: vcpu->arch.regs[VCPU_REGS_RSP] = vmcs_readl(GUEST_RSP); break; - case VCPU_REGS_RIP: - vcpu->arch.regs[VCPU_REGS_RIP] = vmcs_readl(GUEST_RIP); + case VCPU_REG_RIP: + vcpu->arch.rip = vmcs_readl(GUEST_RIP); break; case VCPU_EXREG_PDPTR: if (enable_ept) @@ -7536,8 +7536,8 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) if (kvm_register_is_dirty(vcpu, VCPU_REGS_RSP)) vmcs_writel(GUEST_RSP, vcpu->arch.regs[VCPU_REGS_RSP]); - if (kvm_register_is_dirty(vcpu, VCPU_REGS_RIP)) - vmcs_writel(GUEST_RIP, vcpu->arch.regs[VCPU_REGS_RIP]); + if (kvm_register_is_dirty(vcpu, VCPU_REG_RIP)) + vmcs_writel(GUEST_RIP, vcpu->arch.rip); vcpu->arch.regs_dirty = 0; if (run_flags & KVM_RUN_LOAD_GUEST_DR6) diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index db84e8001da5..d0cc5f6c6879 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -620,7 +620,7 @@ BUILD_CONTROLS_SHADOW(tertiary_exec, TERTIARY_VM_EXEC_CONTROL, 64) * cache on demand. Other registers not listed here are synced to * the cache immediately after VM-Exit. */ -#define VMX_REGS_LAZY_LOAD_SET ((1 << VCPU_REGS_RIP) | \ +#define VMX_REGS_LAZY_LOAD_SET ((1 << VCPU_REG_RIP) | \ (1 << VCPU_REGS_RSP) | \ (1 << VCPU_EXREG_RFLAGS) | \ (1 << VCPU_EXREG_PDPTR) | \ From e31de5791093df059aaae3eb5ef25709a022617a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:31 -0700 Subject: [PATCH 0898/5214] KVM: x86: Drop the "EX" part of "EXREG" to avoid collision with APX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that NR_VCPU_REGS is no longer a thing, and now that now that RIP is effectively an EXREG, drop the "EX" is for extended (or maybe extra?") prefix from non-GPR registers to avoid a collision with APX (Advanced Performance Extensions), which adds: 16 additional general-purpose registers (GPRs) R16–R31, also referred to as Extended GPRs (EGPRs) in this document; I.e. KVM's version of "extended" won't match with APX's definition. No functional change intended. Signed-off-by: Sean Christopherson Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-3-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 18 +++++++-------- arch/x86/kvm/kvm_cache_regs.h | 16 ++++++------- arch/x86/kvm/svm/svm.c | 6 ++--- arch/x86/kvm/svm/svm.h | 2 +- arch/x86/kvm/vmx/nested.c | 6 ++--- arch/x86/kvm/vmx/tdx.c | 4 ++-- arch/x86/kvm/vmx/vmx.c | 40 ++++++++++++++++----------------- arch/x86/kvm/vmx/vmx.h | 20 ++++++++--------- arch/x86/kvm/x86.c | 16 ++++++------- 9 files changed, 64 insertions(+), 64 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 68a11325e8bc..b1eae1e7b04f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -195,8 +195,8 @@ enum kvm_reg { VCPU_REG_RIP = NR_VCPU_GENERAL_PURPOSE_REGS, - VCPU_EXREG_PDPTR, - VCPU_EXREG_CR0, + VCPU_REG_PDPTR, + VCPU_REG_CR0, /* * Alias AMD's ERAPS (not a real register) to CR3 so that common code * can trigger emulation of the RAP (Return Address Predictor) with @@ -204,13 +204,13 @@ enum kvm_reg { * is cleared on writes to CR3, i.e. marking CR3 dirty will naturally * mark ERAPS dirty as well. */ - VCPU_EXREG_CR3, - VCPU_EXREG_ERAPS = VCPU_EXREG_CR3, - VCPU_EXREG_CR4, - VCPU_EXREG_RFLAGS, - VCPU_EXREG_SEGMENTS, - VCPU_EXREG_EXIT_INFO_1, - VCPU_EXREG_EXIT_INFO_2, + VCPU_REG_CR3, + VCPU_REG_ERAPS = VCPU_REG_CR3, + VCPU_REG_CR4, + VCPU_REG_RFLAGS, + VCPU_REG_SEGMENTS, + VCPU_REG_EXIT_INFO_1, + VCPU_REG_EXIT_INFO_2, }; enum { diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 9b7df9de0e87..ac1f9867a234 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -159,8 +159,8 @@ static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index) { might_sleep(); /* on svm */ - if (!kvm_register_is_available(vcpu, VCPU_EXREG_PDPTR)) - kvm_x86_call(cache_reg)(vcpu, VCPU_EXREG_PDPTR); + if (!kvm_register_is_available(vcpu, VCPU_REG_PDPTR)) + kvm_x86_call(cache_reg)(vcpu, VCPU_REG_PDPTR); return vcpu->arch.walk_mmu->pdptrs[index]; } @@ -174,8 +174,8 @@ static inline ulong kvm_read_cr0_bits(struct kvm_vcpu *vcpu, ulong mask) { ulong tmask = mask & KVM_POSSIBLE_CR0_GUEST_BITS; if ((tmask & vcpu->arch.cr0_guest_owned_bits) && - !kvm_register_is_available(vcpu, VCPU_EXREG_CR0)) - kvm_x86_call(cache_reg)(vcpu, VCPU_EXREG_CR0); + !kvm_register_is_available(vcpu, VCPU_REG_CR0)) + kvm_x86_call(cache_reg)(vcpu, VCPU_REG_CR0); return vcpu->arch.cr0 & mask; } @@ -196,8 +196,8 @@ static inline ulong kvm_read_cr4_bits(struct kvm_vcpu *vcpu, ulong mask) { ulong tmask = mask & KVM_POSSIBLE_CR4_GUEST_BITS; if ((tmask & vcpu->arch.cr4_guest_owned_bits) && - !kvm_register_is_available(vcpu, VCPU_EXREG_CR4)) - kvm_x86_call(cache_reg)(vcpu, VCPU_EXREG_CR4); + !kvm_register_is_available(vcpu, VCPU_REG_CR4)) + kvm_x86_call(cache_reg)(vcpu, VCPU_REG_CR4); return vcpu->arch.cr4 & mask; } @@ -211,8 +211,8 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu, static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu) { - if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) - kvm_x86_call(cache_reg)(vcpu, VCPU_EXREG_CR3); + if (!kvm_register_is_available(vcpu, VCPU_REG_CR3)) + kvm_x86_call(cache_reg)(vcpu, VCPU_REG_CR3); return vcpu->arch.cr3; } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 85edaee27b03..ee5749d8b3e8 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1517,7 +1517,7 @@ static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) kvm_register_mark_available(vcpu, reg); switch (reg) { - case VCPU_EXREG_PDPTR: + case VCPU_REG_PDPTR: /* * When !npt_enabled, mmu->pdptrs[] is already available since * it is always updated per SDM when moving to CRs. @@ -4179,7 +4179,7 @@ static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva) static void svm_flush_tlb_guest(struct kvm_vcpu *vcpu) { - kvm_register_mark_dirty(vcpu, VCPU_EXREG_ERAPS); + kvm_register_mark_dirty(vcpu, VCPU_REG_ERAPS); svm_flush_tlb_asid(vcpu); } @@ -4457,7 +4457,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) svm->vmcb->save.cr2 = vcpu->arch.cr2; if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS) && - kvm_register_is_dirty(vcpu, VCPU_EXREG_ERAPS)) + kvm_register_is_dirty(vcpu, VCPU_REG_ERAPS)) svm->vmcb->control.erap_ctl |= ERAP_CONTROL_CLEAR_RAP; svm_fixup_nested_rips(vcpu); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index a10668d17a16..0a4f7db7afbc 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -484,7 +484,7 @@ static inline bool svm_is_vmrun_failure(u64 exit_code) * KVM_REQ_LOAD_MMU_PGD is always requested when the cached vcpu->arch.cr3 * is changed. svm_load_mmu_pgd() then syncs the new CR3 value into the VMCB. */ -#define SVM_REGS_LAZY_LOAD_SET (1 << VCPU_EXREG_PDPTR) +#define SVM_REGS_LAZY_LOAD_SET (1 << VCPU_REG_PDPTR) static inline void __vmcb_set_intercept(unsigned long *intercepts, u32 bit) { diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 3fe88f29be7a..22b1f06a9d40 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -1189,7 +1189,7 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, } vcpu->arch.cr3 = cr3; - kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_dirty(vcpu, VCPU_REG_CR3); /* Re-initialize the MMU, e.g. to pick up CR4 MMU role changes. */ kvm_init_mmu(vcpu); @@ -4972,7 +4972,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu) nested_ept_uninit_mmu_context(vcpu); vcpu->arch.cr3 = vmcs_readl(GUEST_CR3); - kvm_register_mark_available(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_available(vcpu, VCPU_REG_CR3); /* * Use ept_save_pdptrs(vcpu) to load the MMU's cached PDPTRs @@ -5074,7 +5074,7 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, kvm_service_local_tlb_flush_requests(vcpu); /* - * VCPU_EXREG_PDPTR will be clobbered in arch/x86/kvm/vmx/vmx.h between + * VCPU_REG_PDPTR will be clobbered in arch/x86/kvm/vmx/vmx.h between * now and the new vmentry. Ensure that the VMCS02 PDPTR fields are * up-to-date before switching to L1. */ diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 1e47c194af53..c23ec4ac8bc8 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1013,8 +1013,8 @@ static fastpath_t tdx_exit_handlers_fastpath(struct kvm_vcpu *vcpu) return EXIT_FASTPATH_NONE; } -#define TDX_REGS_AVAIL_SET (BIT_ULL(VCPU_EXREG_EXIT_INFO_1) | \ - BIT_ULL(VCPU_EXREG_EXIT_INFO_2) | \ +#define TDX_REGS_AVAIL_SET (BIT_ULL(VCPU_REG_EXIT_INFO_1) | \ + BIT_ULL(VCPU_REG_EXIT_INFO_2) | \ BIT_ULL(VCPU_REGS_RAX) | \ BIT_ULL(VCPU_REGS_RBX) | \ BIT_ULL(VCPU_REGS_RCX) | \ diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 577b0c6286ad..aa1c26018439 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -843,8 +843,8 @@ static bool vmx_segment_cache_test_set(struct vcpu_vmx *vmx, unsigned seg, bool ret; u32 mask = 1 << (seg * SEG_FIELD_NR + field); - if (!kvm_register_is_available(&vmx->vcpu, VCPU_EXREG_SEGMENTS)) { - kvm_register_mark_available(&vmx->vcpu, VCPU_EXREG_SEGMENTS); + if (!kvm_register_is_available(&vmx->vcpu, VCPU_REG_SEGMENTS)) { + kvm_register_mark_available(&vmx->vcpu, VCPU_REG_SEGMENTS); vmx->segment_cache.bitmask = 0; } ret = vmx->segment_cache.bitmask & mask; @@ -1609,8 +1609,8 @@ unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long rflags, save_rflags; - if (!kvm_register_is_available(vcpu, VCPU_EXREG_RFLAGS)) { - kvm_register_mark_available(vcpu, VCPU_EXREG_RFLAGS); + if (!kvm_register_is_available(vcpu, VCPU_REG_RFLAGS)) { + kvm_register_mark_available(vcpu, VCPU_REG_RFLAGS); rflags = vmcs_readl(GUEST_RFLAGS); if (vmx->rmode.vm86_active) { rflags &= RMODE_GUEST_OWNED_EFLAGS_BITS; @@ -1633,7 +1633,7 @@ void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags) * if L1 runs L2 as a restricted guest. */ if (is_unrestricted_guest(vcpu)) { - kvm_register_mark_available(vcpu, VCPU_EXREG_RFLAGS); + kvm_register_mark_available(vcpu, VCPU_REG_RFLAGS); vmx->rflags = rflags; vmcs_writel(GUEST_RFLAGS, rflags); return; @@ -2607,17 +2607,17 @@ void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) case VCPU_REG_RIP: vcpu->arch.rip = vmcs_readl(GUEST_RIP); break; - case VCPU_EXREG_PDPTR: + case VCPU_REG_PDPTR: if (enable_ept) ept_save_pdptrs(vcpu); break; - case VCPU_EXREG_CR0: + case VCPU_REG_CR0: guest_owned_bits = vcpu->arch.cr0_guest_owned_bits; vcpu->arch.cr0 &= ~guest_owned_bits; vcpu->arch.cr0 |= vmcs_readl(GUEST_CR0) & guest_owned_bits; break; - case VCPU_EXREG_CR3: + case VCPU_REG_CR3: /* * When intercepting CR3 loads, e.g. for shadowing paging, KVM's * CR3 is loaded into hardware, not the guest's CR3. @@ -2625,7 +2625,7 @@ void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) if (!(exec_controls_get(to_vmx(vcpu)) & CPU_BASED_CR3_LOAD_EXITING)) vcpu->arch.cr3 = vmcs_readl(GUEST_CR3); break; - case VCPU_EXREG_CR4: + case VCPU_REG_CR4: guest_owned_bits = vcpu->arch.cr4_guest_owned_bits; vcpu->arch.cr4 &= ~guest_owned_bits; @@ -3350,7 +3350,7 @@ void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu) { struct kvm_mmu *mmu = vcpu->arch.walk_mmu; - if (!kvm_register_is_dirty(vcpu, VCPU_EXREG_PDPTR)) + if (!kvm_register_is_dirty(vcpu, VCPU_REG_PDPTR)) return; if (is_pae_paging(vcpu)) { @@ -3373,7 +3373,7 @@ void ept_save_pdptrs(struct kvm_vcpu *vcpu) mmu->pdptrs[2] = vmcs_read64(GUEST_PDPTR2); mmu->pdptrs[3] = vmcs_read64(GUEST_PDPTR3); - kvm_register_mark_available(vcpu, VCPU_EXREG_PDPTR); + kvm_register_mark_available(vcpu, VCPU_REG_PDPTR); } #define CR3_EXITING_BITS (CPU_BASED_CR3_LOAD_EXITING | \ @@ -3416,7 +3416,7 @@ void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) vmcs_writel(CR0_READ_SHADOW, cr0); vmcs_writel(GUEST_CR0, hw_cr0); vcpu->arch.cr0 = cr0; - kvm_register_mark_available(vcpu, VCPU_EXREG_CR0); + kvm_register_mark_available(vcpu, VCPU_REG_CR0); #ifdef CONFIG_X86_64 if (vcpu->arch.efer & EFER_LME) { @@ -3434,8 +3434,8 @@ void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) * (correctly) stop reading vmcs.GUEST_CR3 because it thinks * KVM's CR3 is installed. */ - if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) - vmx_cache_reg(vcpu, VCPU_EXREG_CR3); + if (!kvm_register_is_available(vcpu, VCPU_REG_CR3)) + vmx_cache_reg(vcpu, VCPU_REG_CR3); /* * When running with EPT but not unrestricted guest, KVM must @@ -3472,7 +3472,7 @@ void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) * GUEST_CR3 is still vmx->ept_identity_map_addr if EPT + !URG. */ if (!(old_cr0_pg & X86_CR0_PG) && (cr0 & X86_CR0_PG)) - kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_dirty(vcpu, VCPU_REG_CR3); } /* depends on vcpu->arch.cr0 to be set to a new value */ @@ -3501,7 +3501,7 @@ void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level) if (!enable_unrestricted_guest && !is_paging(vcpu)) guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr; - else if (kvm_register_is_dirty(vcpu, VCPU_EXREG_CR3)) + else if (kvm_register_is_dirty(vcpu, VCPU_REG_CR3)) guest_cr3 = vcpu->arch.cr3; else /* vmcs.GUEST_CR3 is already up-to-date. */ update_guest_cr3 = false; @@ -3561,7 +3561,7 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) } vcpu->arch.cr4 = cr4; - kvm_register_mark_available(vcpu, VCPU_EXREG_CR4); + kvm_register_mark_available(vcpu, VCPU_REG_CR4); if (!enable_unrestricted_guest) { if (enable_ept) { @@ -5021,7 +5021,7 @@ void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) vmcs_write32(GUEST_IDTR_LIMIT, 0xffff); vmx_segment_cache_clear(vmx); - kvm_register_mark_available(vcpu, VCPU_EXREG_SEGMENTS); + kvm_register_mark_available(vcpu, VCPU_REG_SEGMENTS); vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE); vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0); @@ -7514,9 +7514,9 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vmx->vt.exit_reason.full = EXIT_REASON_INVALID_STATE; vmx->vt.exit_reason.failed_vmentry = 1; - kvm_register_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_1); + kvm_register_mark_available(vcpu, VCPU_REG_EXIT_INFO_1); vmx->vt.exit_qualification = ENTRY_FAIL_DEFAULT; - kvm_register_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_2); + kvm_register_mark_available(vcpu, VCPU_REG_EXIT_INFO_2); vmx->vt.exit_intr_info = 0; return EXIT_FASTPATH_NONE; } diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index d0cc5f6c6879..9fb76ea48caf 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -317,7 +317,7 @@ static __always_inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu) { struct vcpu_vt *vt = to_vt(vcpu); - if (!kvm_register_test_and_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_1) && + if (!kvm_register_test_and_mark_available(vcpu, VCPU_REG_EXIT_INFO_1) && !WARN_ON_ONCE(is_td_vcpu(vcpu))) vt->exit_qualification = vmcs_readl(EXIT_QUALIFICATION); @@ -328,7 +328,7 @@ static __always_inline u32 vmx_get_intr_info(struct kvm_vcpu *vcpu) { struct vcpu_vt *vt = to_vt(vcpu); - if (!kvm_register_test_and_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_2) && + if (!kvm_register_test_and_mark_available(vcpu, VCPU_REG_EXIT_INFO_2) && !WARN_ON_ONCE(is_td_vcpu(vcpu))) vt->exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO); @@ -622,14 +622,14 @@ BUILD_CONTROLS_SHADOW(tertiary_exec, TERTIARY_VM_EXEC_CONTROL, 64) */ #define VMX_REGS_LAZY_LOAD_SET ((1 << VCPU_REG_RIP) | \ (1 << VCPU_REGS_RSP) | \ - (1 << VCPU_EXREG_RFLAGS) | \ - (1 << VCPU_EXREG_PDPTR) | \ - (1 << VCPU_EXREG_SEGMENTS) | \ - (1 << VCPU_EXREG_CR0) | \ - (1 << VCPU_EXREG_CR3) | \ - (1 << VCPU_EXREG_CR4) | \ - (1 << VCPU_EXREG_EXIT_INFO_1) | \ - (1 << VCPU_EXREG_EXIT_INFO_2)) + (1 << VCPU_REG_RFLAGS) | \ + (1 << VCPU_REG_PDPTR) | \ + (1 << VCPU_REG_SEGMENTS) | \ + (1 << VCPU_REG_CR0) | \ + (1 << VCPU_REG_CR3) | \ + (1 << VCPU_REG_CR4) | \ + (1 << VCPU_REG_EXIT_INFO_1) | \ + (1 << VCPU_REG_EXIT_INFO_2)) static inline unsigned long vmx_l1_guest_owned_cr0_bits(void) { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0a1b63c63d1a..ac05cc289b56 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1090,14 +1090,14 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) } /* - * Marking VCPU_EXREG_PDPTR dirty doesn't work for !tdp_enabled. + * Marking VCPU_REG_PDPTR dirty doesn't work for !tdp_enabled. * Shadow page roots need to be reconstructed instead. */ if (!tdp_enabled && memcmp(mmu->pdptrs, pdpte, sizeof(mmu->pdptrs))) kvm_mmu_free_roots(vcpu->kvm, mmu, KVM_MMU_ROOT_CURRENT); memcpy(mmu->pdptrs, pdpte, sizeof(mmu->pdptrs)); - kvm_register_mark_dirty(vcpu, VCPU_EXREG_PDPTR); + kvm_register_mark_dirty(vcpu, VCPU_REG_PDPTR); kvm_make_request(KVM_REQ_LOAD_MMU_PGD, vcpu); vcpu->arch.pdptrs_from_userspace = false; @@ -1478,7 +1478,7 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) kvm_mmu_new_pgd(vcpu, cr3); vcpu->arch.cr3 = cr3; - kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_dirty(vcpu, VCPU_REG_CR3); /* Do not call post_set_cr3, we do not get here for confidential guests. */ handle_tlb_flush: @@ -12473,7 +12473,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, vcpu->arch.cr2 = sregs->cr2; *mmu_reset_needed |= kvm_read_cr3(vcpu) != sregs->cr3; vcpu->arch.cr3 = sregs->cr3; - kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_dirty(vcpu, VCPU_REG_CR3); kvm_x86_call(post_set_cr3)(vcpu, sregs->cr3); kvm_set_cr8(vcpu, sregs->cr8); @@ -12566,7 +12566,7 @@ static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2) for (i = 0; i < 4 ; i++) kvm_pdptr_write(vcpu, i, sregs2->pdptrs[i]); - kvm_register_mark_dirty(vcpu, VCPU_EXREG_PDPTR); + kvm_register_mark_dirty(vcpu, VCPU_REG_PDPTR); mmu_reset_needed = 1; vcpu->arch.pdptrs_from_userspace = true; } @@ -13111,7 +13111,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) kvm_rip_write(vcpu, 0xfff0); vcpu->arch.cr3 = 0; - kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); + kvm_register_mark_dirty(vcpu, VCPU_REG_CR3); /* * CR0.CD/NW are set on RESET, preserved on INIT. Note, some versions @@ -14323,7 +14323,7 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva) * the RAP (Return Address Predicator). */ if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS)) - kvm_register_is_dirty(vcpu, VCPU_EXREG_ERAPS); + kvm_register_is_dirty(vcpu, VCPU_REG_ERAPS); kvm_invalidate_pcid(vcpu, operand.pcid); return kvm_skip_emulated_instruction(vcpu); @@ -14339,7 +14339,7 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva) fallthrough; case INVPCID_TYPE_ALL_INCL_GLOBAL: /* - * Don't bother marking VCPU_EXREG_ERAPS dirty, SVM will take + * Don't bother marking VCPU_REG_ERAPS dirty, SVM will take * care of doing so when emulating the full guest TLB flush * (the RAP is cleared on all implicit TLB flushes). */ From 3b628ef168dabfff8f413a791675c34beb8a16db Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:32 -0700 Subject: [PATCH 0899/5214] KVM: nVMX: Do a bitwise-AND of regs_avail when switching active VMCS When switching between vmcs01 and vmcs02, do a bitwise-AND of regs_avail to effectively reset the mask for the new VMCS, purely to be consistent with all other "full" writes of regs_avail. In practice, a straight write versus a bitwise-AND will yield the same result, as kvm_arch_vcpu_create() marks *all* registers available (and dirty), and KVM never marks registers unavailable unless they're lazily loaded. This will allow adding wrapper APIs to set regs_{avail,dirty} without having to add special handling for a nVMX use case that doesn't exist in practice. Signed-off-by: Sean Christopherson Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-4-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 22b1f06a9d40..63c4ca8c97d5 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -310,7 +310,7 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs) vmx_sync_vmcs_host_state(vmx, prev); put_cpu(); - vcpu->arch.regs_avail = ~VMX_REGS_LAZY_LOAD_SET; + vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; /* * All lazily updated registers will be reloaded from VMCS12 on both From 0af556e6f980de533c1c28b365db82236094637a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:33 -0700 Subject: [PATCH 0900/5214] KVM: x86: Add wrapper APIs to reset dirty/available register masks Add wrappers for setting regs_{avail,dirty} in anticipation of turning the fields into proper bitmaps, at which point direct writes won't work so well. Deliberately leave the initialization in kvm_arch_vcpu_create() as-is, because the regs_avail logic in particular is special in that it's the one and only place where KVM marks eagerly synchronized registers as available. No functional change intended. Signed-off-by: Sean Christopherson Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-5-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/kvm_cache_regs.h | 18 ++++++++++++++++++ arch/x86/kvm/svm/svm.c | 4 ++-- arch/x86/kvm/vmx/nested.c | 4 ++-- arch/x86/kvm/vmx/tdx.c | 2 +- arch/x86/kvm/vmx/vmx.c | 4 ++-- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index ac1f9867a234..7f71d468178c 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -105,6 +105,24 @@ static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu return arch___test_and_set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); } +static __always_inline void kvm_clear_available_registers(struct kvm_vcpu *vcpu, + u32 clear_mask) +{ + /* + * Note the bitwise-AND! In practice, a straight write would also work + * as KVM initializes the mask to all ones and never clears registers + * that are eagerly synchronized. Using a bitwise-AND adds a bit of + * sanity checking as incorrectly marking an eagerly sync'd register + * unavailable will generate a WARN due to an unexpected cache request. + */ + vcpu->arch.regs_avail &= ~clear_mask; +} + +static __always_inline void kvm_reset_dirty_registers(struct kvm_vcpu *vcpu) +{ + vcpu->arch.regs_dirty = 0; +} + /* * The "raw" register helpers are only for cases where the full 64 bits of a * register are read/written irrespective of current vCPU mode. In other words, diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ee5749d8b3e8..2b73d2650155 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4508,7 +4508,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp; vcpu->arch.rip = svm->vmcb->save.rip; } - vcpu->arch.regs_dirty = 0; + kvm_reset_dirty_registers(vcpu); if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI)) kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); @@ -4554,7 +4554,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vcpu->arch.apf.host_apf_flags = kvm_read_and_reset_apf_flags(); - vcpu->arch.regs_avail &= ~SVM_REGS_LAZY_LOAD_SET; + kvm_clear_available_registers(vcpu, SVM_REGS_LAZY_LOAD_SET); if (!msr_write_intercepted(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL)) rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, vcpu_to_pmu(vcpu)->global_ctrl); diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 63c4ca8c97d5..c4d2bc080add 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -310,13 +310,13 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs) vmx_sync_vmcs_host_state(vmx, prev); put_cpu(); - vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; + kvm_clear_available_registers(vcpu, VMX_REGS_LAZY_LOAD_SET); /* * All lazily updated registers will be reloaded from VMCS12 on both * vmentry and vmexit. */ - vcpu->arch.regs_dirty = 0; + kvm_reset_dirty_registers(vcpu); } static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index c23ec4ac8bc8..c9ab7902151f 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1098,7 +1098,7 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) tdx_load_host_xsave_state(vcpu); - vcpu->arch.regs_avail &= TDX_REGS_AVAIL_SET; + kvm_clear_available_registers(vcpu, ~(u32)TDX_REGS_AVAIL_SET); if (unlikely(tdx->vp_enter_ret == EXIT_REASON_EPT_MISCONFIG)) return EXIT_FASTPATH_NONE; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index aa1c26018439..61eeafcd70f1 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7472,7 +7472,7 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, flags); vcpu->arch.cr2 = native_read_cr2(); - vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; + kvm_clear_available_registers(vcpu, VMX_REGS_LAZY_LOAD_SET); vmx->idt_vectoring_info = 0; @@ -7538,7 +7538,7 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vmcs_writel(GUEST_RSP, vcpu->arch.regs[VCPU_REGS_RSP]); if (kvm_register_is_dirty(vcpu, VCPU_REG_RIP)) vmcs_writel(GUEST_RIP, vcpu->arch.rip); - vcpu->arch.regs_dirty = 0; + kvm_reset_dirty_registers(vcpu); if (run_flags & KVM_RUN_LOAD_GUEST_DR6) set_debugreg(vcpu->arch.dr6, 6); From 133ecccbfea3ba02c3fc4e8ff18ac238d6ea1524 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:34 -0700 Subject: [PATCH 0901/5214] KVM: x86: Track available/dirty register masks as "unsigned long" values Convert regs_{avail,dirty} and all related masks to "unsigned long" values as an intermediate step towards declaring the fields as actual bitmaps, and as a step toward support APX, which will push the total number of registers beyond 32 on 64-bit kernels. Opportunistically convert TDX's ULL bitmask to a UL to match everything else (TDX is 64-bit only, so it's a nop in the end). No functional change intended. Signed-off-by: Sean Christopherson Signed-off-by: Xiaoyao Li Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-6-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 4 ++-- arch/x86/kvm/kvm_cache_regs.h | 2 +- arch/x86/kvm/svm/svm.h | 2 +- arch/x86/kvm/vmx/tdx.c | 36 ++++++++++++++++----------------- arch/x86/kvm/vmx/vmx.h | 20 +++++++++--------- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b1eae1e7b04f..c47eb294c066 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -802,8 +802,8 @@ struct kvm_vcpu_arch { */ unsigned long regs[NR_VCPU_GENERAL_PURPOSE_REGS]; unsigned long rip; - u32 regs_avail; - u32 regs_dirty; + unsigned long regs_avail; + unsigned long regs_dirty; unsigned long cr0; unsigned long cr0_guest_owned_bits; diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 7f71d468178c..171e6bc2e169 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -106,7 +106,7 @@ static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu } static __always_inline void kvm_clear_available_registers(struct kvm_vcpu *vcpu, - u32 clear_mask) + unsigned long clear_mask) { /* * Note the bitwise-AND! In practice, a straight write would also work diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 0a4f7db7afbc..cf93d922758f 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -484,7 +484,7 @@ static inline bool svm_is_vmrun_failure(u64 exit_code) * KVM_REQ_LOAD_MMU_PGD is always requested when the cached vcpu->arch.cr3 * is changed. svm_load_mmu_pgd() then syncs the new CR3 value into the VMCB. */ -#define SVM_REGS_LAZY_LOAD_SET (1 << VCPU_REG_PDPTR) +#define SVM_REGS_LAZY_LOAD_SET (BIT(VCPU_REG_PDPTR)) static inline void __vmcb_set_intercept(unsigned long *intercepts, u32 bit) { diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index c9ab7902151f..85f28363e4cc 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1013,23 +1013,23 @@ static fastpath_t tdx_exit_handlers_fastpath(struct kvm_vcpu *vcpu) return EXIT_FASTPATH_NONE; } -#define TDX_REGS_AVAIL_SET (BIT_ULL(VCPU_REG_EXIT_INFO_1) | \ - BIT_ULL(VCPU_REG_EXIT_INFO_2) | \ - BIT_ULL(VCPU_REGS_RAX) | \ - BIT_ULL(VCPU_REGS_RBX) | \ - BIT_ULL(VCPU_REGS_RCX) | \ - BIT_ULL(VCPU_REGS_RDX) | \ - BIT_ULL(VCPU_REGS_RBP) | \ - BIT_ULL(VCPU_REGS_RSI) | \ - BIT_ULL(VCPU_REGS_RDI) | \ - BIT_ULL(VCPU_REGS_R8) | \ - BIT_ULL(VCPU_REGS_R9) | \ - BIT_ULL(VCPU_REGS_R10) | \ - BIT_ULL(VCPU_REGS_R11) | \ - BIT_ULL(VCPU_REGS_R12) | \ - BIT_ULL(VCPU_REGS_R13) | \ - BIT_ULL(VCPU_REGS_R14) | \ - BIT_ULL(VCPU_REGS_R15)) +#define TDX_REGS_AVAIL_SET (BIT(VCPU_REG_EXIT_INFO_1) | \ + BIT(VCPU_REG_EXIT_INFO_2) | \ + BIT(VCPU_REGS_RAX) | \ + BIT(VCPU_REGS_RBX) | \ + BIT(VCPU_REGS_RCX) | \ + BIT(VCPU_REGS_RDX) | \ + BIT(VCPU_REGS_RBP) | \ + BIT(VCPU_REGS_RSI) | \ + BIT(VCPU_REGS_RDI) | \ + BIT(VCPU_REGS_R8) | \ + BIT(VCPU_REGS_R9) | \ + BIT(VCPU_REGS_R10) | \ + BIT(VCPU_REGS_R11) | \ + BIT(VCPU_REGS_R12) | \ + BIT(VCPU_REGS_R13) | \ + BIT(VCPU_REGS_R14) | \ + BIT(VCPU_REGS_R15)) static void tdx_load_host_xsave_state(struct kvm_vcpu *vcpu) { @@ -1098,7 +1098,7 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) tdx_load_host_xsave_state(vcpu); - kvm_clear_available_registers(vcpu, ~(u32)TDX_REGS_AVAIL_SET); + kvm_clear_available_registers(vcpu, ~TDX_REGS_AVAIL_SET); if (unlikely(tdx->vp_enter_ret == EXIT_REASON_EPT_MISCONFIG)) return EXIT_FASTPATH_NONE; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 9fb76ea48caf..48447fa983f4 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -620,16 +620,16 @@ BUILD_CONTROLS_SHADOW(tertiary_exec, TERTIARY_VM_EXEC_CONTROL, 64) * cache on demand. Other registers not listed here are synced to * the cache immediately after VM-Exit. */ -#define VMX_REGS_LAZY_LOAD_SET ((1 << VCPU_REG_RIP) | \ - (1 << VCPU_REGS_RSP) | \ - (1 << VCPU_REG_RFLAGS) | \ - (1 << VCPU_REG_PDPTR) | \ - (1 << VCPU_REG_SEGMENTS) | \ - (1 << VCPU_REG_CR0) | \ - (1 << VCPU_REG_CR3) | \ - (1 << VCPU_REG_CR4) | \ - (1 << VCPU_REG_EXIT_INFO_1) | \ - (1 << VCPU_REG_EXIT_INFO_2)) +#define VMX_REGS_LAZY_LOAD_SET (BIT(VCPU_REGS_RSP) | \ + BIT(VCPU_REG_RIP) | \ + BIT(VCPU_REG_RFLAGS) | \ + BIT(VCPU_REG_PDPTR) | \ + BIT(VCPU_REG_SEGMENTS) | \ + BIT(VCPU_REG_CR0) | \ + BIT(VCPU_REG_CR3) | \ + BIT(VCPU_REG_CR4) | \ + BIT(VCPU_REG_EXIT_INFO_1) | \ + BIT(VCPU_REG_EXIT_INFO_2)) static inline unsigned long vmx_l1_guest_owned_cr0_bits(void) { From 39a5ee37be89d3c4ed65ad206c0a2b4aebd9e1cf Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Apr 2026 15:42:35 -0700 Subject: [PATCH 0902/5214] KVM: x86: Use a proper bitmap for tracking available/dirty registers Define regs_{avail,dirty} as bitmaps instead of U32s to harden against overflow, and to allow for dynamically sizing the bitmaps when APX comes along, which will add 16 more GPRs (R16-R31) and thus increase the total number of registers beyond 32. Open code writes in the "reset" APIs, as the writes are hot paths and bitmap_write() is complete overkill for what KVM needs. Even better, hardcoding writes to entry '0' in the array is a perfect excuse to assert that the array contains exactly one entry, e.g. to effectively add guard against defining R16-R31 in 32-bit kernels. For all intents and purposes, no functional change intended even though using bitmap_fill() will mean "undefined" registers are no longer marked available and dirty (KVM should never be querying those bits). Signed-off-by: Sean Christopherson Reviewed-by: Kai Huang Tested-by: Kai Huang Message-ID: <20260409224236.2021562-7-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 6 ++++-- arch/x86/kvm/kvm_cache_regs.h | 20 ++++++++++++-------- arch/x86/kvm/x86.c | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c47eb294c066..ef0c368676c5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -211,6 +211,8 @@ enum kvm_reg { VCPU_REG_SEGMENTS, VCPU_REG_EXIT_INFO_1, VCPU_REG_EXIT_INFO_2, + + NR_VCPU_TOTAL_REGS, }; enum { @@ -802,8 +804,8 @@ struct kvm_vcpu_arch { */ unsigned long regs[NR_VCPU_GENERAL_PURPOSE_REGS]; unsigned long rip; - unsigned long regs_avail; - unsigned long regs_dirty; + DECLARE_BITMAP(regs_avail, NR_VCPU_TOTAL_REGS); + DECLARE_BITMAP(regs_dirty, NR_VCPU_TOTAL_REGS); unsigned long cr0; unsigned long cr0_guest_owned_bits; diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 171e6bc2e169..2ae492ad6412 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -67,29 +67,29 @@ static inline bool kvm_register_is_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { kvm_assert_register_caching_allowed(vcpu); - return test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); + return test_bit(reg, vcpu->arch.regs_avail); } static inline bool kvm_register_is_dirty(struct kvm_vcpu *vcpu, enum kvm_reg reg) { kvm_assert_register_caching_allowed(vcpu); - return test_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty); + return test_bit(reg, vcpu->arch.regs_dirty); } static inline void kvm_register_mark_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { kvm_assert_register_caching_allowed(vcpu); - __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); + __set_bit(reg, vcpu->arch.regs_avail); } static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu, enum kvm_reg reg) { kvm_assert_register_caching_allowed(vcpu); - __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); - __set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty); + __set_bit(reg, vcpu->arch.regs_avail); + __set_bit(reg, vcpu->arch.regs_dirty); } /* @@ -102,12 +102,15 @@ static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu enum kvm_reg reg) { kvm_assert_register_caching_allowed(vcpu); - return arch___test_and_set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); + return arch___test_and_set_bit(reg, vcpu->arch.regs_avail); } static __always_inline void kvm_clear_available_registers(struct kvm_vcpu *vcpu, unsigned long clear_mask) { + BUILD_BUG_ON(sizeof(clear_mask) != sizeof(vcpu->arch.regs_avail[0])); + BUILD_BUG_ON(ARRAY_SIZE(vcpu->arch.regs_avail) != 1); + /* * Note the bitwise-AND! In practice, a straight write would also work * as KVM initializes the mask to all ones and never clears registers @@ -115,12 +118,13 @@ static __always_inline void kvm_clear_available_registers(struct kvm_vcpu *vcpu, * sanity checking as incorrectly marking an eagerly sync'd register * unavailable will generate a WARN due to an unexpected cache request. */ - vcpu->arch.regs_avail &= ~clear_mask; + vcpu->arch.regs_avail[0] &= ~clear_mask; } static __always_inline void kvm_reset_dirty_registers(struct kvm_vcpu *vcpu) { - vcpu->arch.regs_dirty = 0; + BUILD_BUG_ON(ARRAY_SIZE(vcpu->arch.regs_dirty) != 1); + vcpu->arch.regs_dirty[0] = 0; } /* diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ac05cc289b56..b8a91feec8e1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -12836,8 +12836,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) int r; vcpu->arch.last_vmentry_cpu = -1; - vcpu->arch.regs_avail = ~0; - vcpu->arch.regs_dirty = ~0; + bitmap_fill(vcpu->arch.regs_avail, NR_VCPU_TOTAL_REGS); + bitmap_fill(vcpu->arch.regs_dirty, NR_VCPU_TOTAL_REGS); kvm_gpc_init(&vcpu->arch.pv_time, vcpu->kvm); From bc984356520cea884229dc6ff044d9bf0049aa19 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:40 -0700 Subject: [PATCH 0903/5214] dt-bindings: clock: qcom-rpmhcc: Add RPMHCC bindings for Hawi Update documentation for the RPMH clock controller on the Qualcomm Hawi SoC. Acked-by: Rob Herring (Arm) Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-1-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml | 1 + include/dt-bindings/clock/qcom,rpmh.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml index a2c404a57981..d344b3386042 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml @@ -19,6 +19,7 @@ properties: enum: - qcom,eliza-rpmh-clk - qcom,glymur-rpmh-clk + - qcom,hawi-rpmh-clk - qcom,kaanapali-rpmh-clk - qcom,milos-rpmh-clk - qcom,nord-rpmh-clk diff --git a/include/dt-bindings/clock/qcom,rpmh.h b/include/dt-bindings/clock/qcom,rpmh.h index 0a7d1be0d124..2d62d5d0b08d 100644 --- a/include/dt-bindings/clock/qcom,rpmh.h +++ b/include/dt-bindings/clock/qcom,rpmh.h @@ -33,5 +33,7 @@ #define RPMH_HWKM_CLK 24 #define RPMH_QLINK_CLK 25 #define RPMH_QLINK_CLK_A 26 +#define RPMH_LN_BB_CLK4 27 +#define RPMH_LN_BB_CLK4_A 28 #endif From eb340b092d124d9e6592b2e58634e0ddb59dddbe Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:41 -0700 Subject: [PATCH 0904/5214] dt-bindings: clock: qcom: Add Hawi TCSR clock controller Add bindings documentation for TCSR clock controller on the Qualcomm Hawi SoC. Acked-by: Rob Herring (Arm) Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-2-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,sm8550-tcsr.yaml | 2 ++ include/dt-bindings/clock/qcom,hawi-tcsrcc.h | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,hawi-tcsrcc.h diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml index 1ccdf4b0f5dd..08824f848973 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml @@ -17,6 +17,7 @@ description: | See also: - include/dt-bindings/clock/qcom,eliza-tcsr.h - include/dt-bindings/clock/qcom,glymur-tcsr.h + - include/dt-bindings/clock/qcom,hawi-tcsrcc.h - include/dt-bindings/clock/qcom,nord-tcsrcc.h - include/dt-bindings/clock/qcom,sm8550-tcsr.h - include/dt-bindings/clock/qcom,sm8650-tcsr.h @@ -28,6 +29,7 @@ properties: - enum: - qcom,eliza-tcsr - qcom,glymur-tcsr + - qcom,hawi-tcsrcc - qcom,kaanapali-tcsr - qcom,milos-tcsr - qcom,nord-tcsrcc diff --git a/include/dt-bindings/clock/qcom,hawi-tcsrcc.h b/include/dt-bindings/clock/qcom,hawi-tcsrcc.h new file mode 100644 index 000000000000..957bc5f75bb7 --- /dev/null +++ b/include/dt-bindings/clock/qcom,hawi-tcsrcc.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_TCSR_CC_HAWI_H +#define _DT_BINDINGS_CLK_QCOM_TCSR_CC_HAWI_H + +/* TCSR_CC clocks */ +#define TCSR_PCIE_0_CLKREF_EN 0 +#define TCSR_PCIE_1_CLKREF_EN 1 +#define TCSR_UFS_CLKREF_EN 2 +#define TCSR_USB2_CLKREF_EN 3 +#define TCSR_USB3_CLKREF_EN 4 + +#endif From d6cd9d5692babcdc697cb55736cb9ab2df87805e Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:42 -0700 Subject: [PATCH 0905/5214] dt-bindings: clock: qcom: Add Hawi global clock controller Add device tree bindings for the global clock controller on the Qualcomm Hawi SoC. Acked-by: Rob Herring (Arm) Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-3-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,hawi-gcc.yaml | 63 +++++ include/dt-bindings/clock/qcom,hawi-gcc.h | 253 ++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml create mode 100644 include/dt-bindings/clock/qcom,hawi-gcc.h diff --git a/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml new file mode 100644 index 000000000000..4f428c0f7286 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,hawi-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller on Hawi + +maintainers: + - Vivek Aknurwar + +description: | + Qualcomm global clock control module provides the clocks, resets and power + domains on Hawi. + + See also: include/dt-bindings/clock/qcom,hawi-gcc.h + +properties: + compatible: + const: qcom,hawi-gcc + + clocks: + items: + - description: Board XO source + - description: Board Always On XO source + - description: Sleep clock source + - description: PCIE 0 Pipe clock source + - description: PCIE 1 Pipe clock source + - description: UFS PHY RX symbol 0 clock + - description: UFS PHY RX symbol 1 clock + - description: UFS PHY TX symbol 0 clock + - description: USB3 PHY wrapper pipe clock + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@100000 { + compatible = "qcom,hawi-gcc"; + reg = <0x00100000 0x1f4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&rpmhcc RPMH_CXO_CLK_A>, + <&sleep_clk>, + <&pcie0_phy>, + <&pcie1_phy>, + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>, + <&usb_1_qmpphy>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/include/dt-bindings/clock/qcom,hawi-gcc.h b/include/dt-bindings/clock/qcom,hawi-gcc.h new file mode 100644 index 000000000000..6cd7fa0884f5 --- /dev/null +++ b/include/dt-bindings/clock/qcom,hawi-gcc.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_HAWI_H +#define _DT_BINDINGS_CLK_QCOM_GCC_HAWI_H + +/* GCC clocks */ +#define GCC_AGGRE_NOC_PCIE_AXI_CLK 0 +#define GCC_AGGRE_STARDUSTNOC_USB3_PRIM_AXI_CLK 1 +#define GCC_AGGRE_UFS_PHY_AXI_CLK 2 +#define GCC_BOOT_ROM_AHB_CLK 3 +#define GCC_CAM_BIST_MCLK_AHB_CLK 4 +#define GCC_CAMERA_AHB_CLK 5 +#define GCC_CAMERA_HF_AXI_CLK 6 +#define GCC_CAMERA_RSC_CORE_CLK 7 +#define GCC_CAMERA_SF_AXI_CLK 8 +#define GCC_CAMERA_XO_CLK 9 +#define GCC_CFG_NOC_PCIE_ANOC_AHB_CLK 10 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 11 +#define GCC_CNOC_PCIE_SF_AXI_CLK 12 +#define GCC_EVA_AHB_CLK 13 +#define GCC_EVA_AXI0_CLK 14 +#define GCC_EVA_AXI0C_CLK 15 +#define GCC_EVA_XO_CLK 16 +#define GCC_GP1_CLK 17 +#define GCC_GP1_CLK_SRC 18 +#define GCC_GP2_CLK 19 +#define GCC_GP2_CLK_SRC 20 +#define GCC_GP3_CLK 21 +#define GCC_GP3_CLK_SRC 22 +#define GCC_GPLL0 23 +#define GCC_GPLL0_OUT_EVEN 24 +#define GCC_GPLL4 25 +#define GCC_GPLL5 26 +#define GCC_GPLL7 27 +#define GCC_GPLL9 28 +#define GCC_GPU_CFG_AHB_CLK 29 +#define GCC_GPU_GEMNOC_GFX_CLK 30 +#define GCC_GPU_GPLL0_CLK_SRC 31 +#define GCC_GPU_GPLL0_DIV_CLK_SRC 32 +#define GCC_GPU_RSC_CORE_CLK 33 +#define GCC_GPU_SMMU_VOTE_CLK 34 +#define GCC_MMU_TCU_VOTE_CLK 35 +#define GCC_PCIE_0_AUX_CLK 36 +#define GCC_PCIE_0_AUX_CLK_SRC 37 +#define GCC_PCIE_0_CFG_AHB_CLK 38 +#define GCC_PCIE_0_MSTR_AXI_CLK 39 +#define GCC_PCIE_0_PHY_AUX_CLK 40 +#define GCC_PCIE_0_PHY_AUX_CLK_SRC 41 +#define GCC_PCIE_0_PHY_RCHNG_CLK 42 +#define GCC_PCIE_0_PHY_RCHNG_CLK_SRC 43 +#define GCC_PCIE_0_PIPE_CLK 44 +#define GCC_PCIE_0_PIPE_CLK_SRC 45 +#define GCC_PCIE_0_PIPE_DIV2_CLK 46 +#define GCC_PCIE_0_PIPE_DIV_CLK_SRC 47 +#define GCC_PCIE_0_SLV_AXI_CLK 48 +#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 49 +#define GCC_PCIE_1_AUX_CLK 50 +#define GCC_PCIE_1_AUX_CLK_SRC 51 +#define GCC_PCIE_1_CFG_AHB_CLK 52 +#define GCC_PCIE_1_MSTR_AXI_CLK 53 +#define GCC_PCIE_1_PHY_AUX_CLK 54 +#define GCC_PCIE_1_PHY_AUX_CLK_SRC 55 +#define GCC_PCIE_1_PHY_RCHNG_CLK 56 +#define GCC_PCIE_1_PHY_RCHNG_CLK_SRC 57 +#define GCC_PCIE_1_PIPE_CLK 58 +#define GCC_PCIE_1_PIPE_CLK_SRC 59 +#define GCC_PCIE_1_PIPE_DIV2_CLK 60 +#define GCC_PCIE_1_PIPE_DIV_CLK_SRC 61 +#define GCC_PCIE_1_RSC_CORE_CLK 62 +#define GCC_PCIE_1_SLV_AXI_CLK 63 +#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 64 +#define GCC_PCIE_RSC_CORE_CLK 65 +#define GCC_PCIE_RSCC_CFG_AHB_CLK 66 +#define GCC_PCIE_RSCC_XO_CLK 67 +#define GCC_PDM2_CLK 68 +#define GCC_PDM2_CLK_SRC 69 +#define GCC_PDM_AHB_CLK 70 +#define GCC_PDM_XO4_CLK 71 +#define GCC_QUPV3_I2C_CORE_CLK 72 +#define GCC_QUPV3_I2C_S0_CLK 73 +#define GCC_QUPV3_I2C_S0_CLK_SRC 74 +#define GCC_QUPV3_I2C_S1_CLK 75 +#define GCC_QUPV3_I2C_S1_CLK_SRC 76 +#define GCC_QUPV3_I2C_S2_CLK 77 +#define GCC_QUPV3_I2C_S2_CLK_SRC 78 +#define GCC_QUPV3_I2C_S3_CLK 79 +#define GCC_QUPV3_I2C_S3_CLK_SRC 80 +#define GCC_QUPV3_I2C_S4_CLK 81 +#define GCC_QUPV3_I2C_S4_CLK_SRC 82 +#define GCC_QUPV3_I2C_S_AHB_CLK 83 +#define GCC_QUPV3_WRAP1_CORE_2X_CLK 84 +#define GCC_QUPV3_WRAP1_CORE_CLK 85 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK 86 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC 87 +#define GCC_QUPV3_WRAP1_S0_CLK 88 +#define GCC_QUPV3_WRAP1_S0_CLK_SRC 89 +#define GCC_QUPV3_WRAP1_S1_CLK 90 +#define GCC_QUPV3_WRAP1_S1_CLK_SRC 91 +#define GCC_QUPV3_WRAP1_S2_CLK 92 +#define GCC_QUPV3_WRAP1_S2_CLK_SRC 93 +#define GCC_QUPV3_WRAP1_S3_CLK 94 +#define GCC_QUPV3_WRAP1_S3_CLK_SRC 95 +#define GCC_QUPV3_WRAP1_S4_CLK 96 +#define GCC_QUPV3_WRAP1_S4_CLK_SRC 97 +#define GCC_QUPV3_WRAP1_S5_CLK 98 +#define GCC_QUPV3_WRAP1_S5_CLK_SRC 99 +#define GCC_QUPV3_WRAP1_S6_CLK 100 +#define GCC_QUPV3_WRAP1_S6_CLK_SRC 101 +#define GCC_QUPV3_WRAP1_S7_CLK 102 +#define GCC_QUPV3_WRAP1_S7_CLK_SRC 103 +#define GCC_QUPV3_WRAP2_CORE_2X_CLK 104 +#define GCC_QUPV3_WRAP2_CORE_CLK 105 +#define GCC_QUPV3_WRAP2_S0_CLK 106 +#define GCC_QUPV3_WRAP2_S0_CLK_SRC 107 +#define GCC_QUPV3_WRAP2_S1_CLK 108 +#define GCC_QUPV3_WRAP2_S1_CLK_SRC 109 +#define GCC_QUPV3_WRAP2_S2_CLK 110 +#define GCC_QUPV3_WRAP2_S2_CLK_SRC 111 +#define GCC_QUPV3_WRAP2_S3_CLK 112 +#define GCC_QUPV3_WRAP2_S3_CLK_SRC 113 +#define GCC_QUPV3_WRAP2_S4_CLK 114 +#define GCC_QUPV3_WRAP2_S4_CLK_SRC 115 +#define GCC_QUPV3_WRAP3_CORE_2X_CLK 116 +#define GCC_QUPV3_WRAP3_CORE_CLK 117 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK 118 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC 119 +#define GCC_QUPV3_WRAP3_S0_CLK 120 +#define GCC_QUPV3_WRAP3_S0_CLK_SRC 121 +#define GCC_QUPV3_WRAP3_S1_CLK 122 +#define GCC_QUPV3_WRAP3_S1_CLK_SRC 123 +#define GCC_QUPV3_WRAP3_S2_CLK 124 +#define GCC_QUPV3_WRAP3_S2_CLK_SRC 125 +#define GCC_QUPV3_WRAP3_S3_CLK 126 +#define GCC_QUPV3_WRAP3_S3_CLK_SRC 127 +#define GCC_QUPV3_WRAP3_S4_CLK 128 +#define GCC_QUPV3_WRAP3_S4_CLK_SRC 129 +#define GCC_QUPV3_WRAP3_S5_CLK 130 +#define GCC_QUPV3_WRAP3_S5_CLK_SRC 131 +#define GCC_QUPV3_WRAP4_CORE_2X_CLK 132 +#define GCC_QUPV3_WRAP4_CORE_CLK 133 +#define GCC_QUPV3_WRAP4_S0_CLK 134 +#define GCC_QUPV3_WRAP4_S0_CLK_SRC 135 +#define GCC_QUPV3_WRAP4_S1_CLK 136 +#define GCC_QUPV3_WRAP4_S1_CLK_SRC 137 +#define GCC_QUPV3_WRAP4_S2_CLK 138 +#define GCC_QUPV3_WRAP4_S2_CLK_SRC 139 +#define GCC_QUPV3_WRAP4_S3_CLK 140 +#define GCC_QUPV3_WRAP4_S3_CLK_SRC 141 +#define GCC_QUPV3_WRAP4_S4_CLK 142 +#define GCC_QUPV3_WRAP4_S4_CLK_SRC 143 +#define GCC_QUPV3_WRAP_1_M_AXI_CLK 144 +#define GCC_QUPV3_WRAP_1_S_AHB_CLK 145 +#define GCC_QUPV3_WRAP_2_M_AHB_CLK 146 +#define GCC_QUPV3_WRAP_2_S_AHB_CLK 147 +#define GCC_QUPV3_WRAP_3_M_AHB_CLK 148 +#define GCC_QUPV3_WRAP_3_S_AHB_CLK 149 +#define GCC_QUPV3_WRAP_4_M_AHB_CLK 150 +#define GCC_QUPV3_WRAP_4_S_AHB_CLK 151 +#define GCC_SDCC2_AHB_CLK 152 +#define GCC_SDCC2_APPS_CLK 153 +#define GCC_SDCC2_APPS_CLK_SRC 154 +#define GCC_SDCC4_AHB_CLK 155 +#define GCC_SDCC4_APPS_CLK 156 +#define GCC_SDCC4_APPS_CLK_SRC 157 +#define GCC_UFS_PHY_AHB_CLK 158 +#define GCC_UFS_PHY_AXI_CLK 159 +#define GCC_UFS_PHY_AXI_CLK_SRC 160 +#define GCC_UFS_PHY_ICE_CORE_CLK 161 +#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 162 +#define GCC_UFS_PHY_PHY_AUX_CLK 163 +#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 164 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 165 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC 166 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 167 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC 168 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 169 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC 170 +#define GCC_UFS_PHY_UNIPRO_5_CORE_CLK 171 +#define GCC_UFS_PHY_UNIPRO_5_CORE_CLK_SRC 172 +#define GCC_USB30_PRIM_MASTER_CLK 173 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 174 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 175 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 176 +#define GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 177 +#define GCC_USB30_PRIM_SLEEP_CLK 178 +#define GCC_USB3_PRIM_PHY_AUX_CLK 179 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 180 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 181 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 182 +#define GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 183 +#define GCC_VIDEO_AHB_CLK 184 +#define GCC_VIDEO_AXI0_CLK 185 +#define GCC_VIDEO_AXI0C_CLK 186 +#define GCC_VIDEO_XO_CLK 187 + +/* GCC power domains */ +#define GCC_PCIE_0_GDSC 0 +#define GCC_PCIE_0_PHY_GDSC 1 +#define GCC_PCIE_1_GDSC 2 +#define GCC_PCIE_1_PHY_GDSC 3 +#define GCC_UFS_MEM_PHY_GDSC 4 +#define GCC_UFS_PHY_GDSC 5 +#define GCC_USB30_PRIM_GDSC 6 +#define GCC_USB3_PHY_GDSC 7 + +/* GCC resets */ +#define GCC_CAMERA_BCR 0 +#define GCC_EVA_AXI0_CLK_ARES 1 +#define GCC_EVA_AXI0C_CLK_ARES 2 +#define GCC_EVA_BCR 3 +#define GCC_GPU_BCR 4 +#define GCC_PCIE_0_BCR 5 +#define GCC_PCIE_0_LINK_DOWN_BCR 6 +#define GCC_PCIE_0_NOCSR_COM_PHY_BCR 7 +#define GCC_PCIE_0_PHY_BCR 8 +#define GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR 9 +#define GCC_PCIE_1_BCR 10 +#define GCC_PCIE_1_LINK_DOWN_BCR 11 +#define GCC_PCIE_1_NOCSR_COM_PHY_BCR 12 +#define GCC_PCIE_1_PHY_BCR 13 +#define GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR 14 +#define GCC_PCIE_PHY_BCR 15 +#define GCC_PCIE_PHY_CFG_AHB_BCR 16 +#define GCC_PCIE_PHY_COM_BCR 17 +#define GCC_PCIE_RSCC_BCR 18 +#define GCC_PDM_BCR 19 +#define GCC_QUPV3_WRAPPER_1_BCR 20 +#define GCC_QUPV3_WRAPPER_2_BCR 21 +#define GCC_QUPV3_WRAPPER_3_BCR 22 +#define GCC_QUPV3_WRAPPER_4_BCR 23 +#define GCC_QUPV3_WRAPPER_I2C_BCR 24 +#define GCC_QUSB2PHY_PRIM_BCR 25 +#define GCC_QUSB2PHY_SEC_BCR 26 +#define GCC_SDCC2_BCR 27 +#define GCC_SDCC4_BCR 28 +#define GCC_TCSR_PCIE_BCR 29 +#define GCC_UFS_PHY_BCR 30 +#define GCC_USB30_PRIM_BCR 31 +#define GCC_USB3_DP_PHY_PRIM_BCR 32 +#define GCC_USB3_DP_PHY_SEC_BCR 33 +#define GCC_USB3_PHY_PRIM_BCR 34 +#define GCC_USB3_PHY_SEC_BCR 35 +#define GCC_USB3PHY_PHY_PRIM_BCR 36 +#define GCC_USB3PHY_PHY_SEC_BCR 37 +#define GCC_VIDEO_AXI0_CLK_ARES 38 +#define GCC_VIDEO_AXI0C_CLK_ARES 39 +#define GCC_VIDEO_BCR 40 +#define GCC_VIDEO_XO_CLK_ARES 41 + +#endif From 8ef9743f0b5e98ddfe217c2d58c2d37635ab6465 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:43 -0700 Subject: [PATCH 0906/5214] clk: qcom: rpmh: Add support for Hawi RPMH clocks Add RPMH clocks present in Qualcomm Hawi SoC. Reviewed-by: Konrad Dybcio Reviewed-by: Taniya Das Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-4-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-rpmh.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 339a6bbcdc4c..8eae6fccc127 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -409,7 +409,9 @@ DEFINE_CLK_RPMH_VRM(clk5, _a2_e0, "C5A_E0", 2); DEFINE_CLK_RPMH_VRM(clk6, _a2_e0, "C6A_E0", 2); DEFINE_CLK_RPMH_VRM(clk7, _a2_e0, "C7A_E0", 2); DEFINE_CLK_RPMH_VRM(clk8, _a2_e0, "C8A_E0", 2); +DEFINE_CLK_RPMH_VRM(clk9, _a2_e0, "C9A_E0", 2); +DEFINE_CLK_RPMH_VRM(clk7, _a4_e0, "C7A_E0", 4); DEFINE_CLK_RPMH_VRM(clk11, _a4_e0, "C11A_E0", 4); DEFINE_CLK_RPMH_BCM(ce, "CE0"); @@ -984,6 +986,36 @@ static const struct clk_rpmh_desc clk_rpmh_nord = { .num_clks = ARRAY_SIZE(nord_rpmh_clocks), }; +static struct clk_hw *hawi_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div2.hw, + [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div2_ao.hw, + [RPMH_DIV_CLK1] = &clk_rpmh_clk11_a4_e0.hw, + [RPMH_LN_BB_CLK1] = &clk_rpmh_clk6_a2_e0.hw, + [RPMH_LN_BB_CLK1_A] = &clk_rpmh_clk6_a2_e0_ao.hw, + [RPMH_LN_BB_CLK2] = &clk_rpmh_clk7_a4_e0.hw, + [RPMH_LN_BB_CLK2_A] = &clk_rpmh_clk7_a4_e0_ao.hw, + [RPMH_LN_BB_CLK3] = &clk_rpmh_clk8_a2_e0.hw, + [RPMH_LN_BB_CLK3_A] = &clk_rpmh_clk8_a2_e0_ao.hw, + [RPMH_LN_BB_CLK4] = &clk_rpmh_clk9_a2_e0.hw, + [RPMH_LN_BB_CLK4_A] = &clk_rpmh_clk9_a2_e0_ao.hw, + [RPMH_RF_CLK1] = &clk_rpmh_clk1_a1_e0.hw, + [RPMH_RF_CLK1_A] = &clk_rpmh_clk1_a1_e0_ao.hw, + [RPMH_RF_CLK2] = &clk_rpmh_clk2_a1_e0.hw, + [RPMH_RF_CLK2_A] = &clk_rpmh_clk2_a1_e0_ao.hw, + [RPMH_RF_CLK3] = &clk_rpmh_clk3_a2_e0.hw, + [RPMH_RF_CLK3_A] = &clk_rpmh_clk3_a2_e0_ao.hw, + [RPMH_RF_CLK4] = &clk_rpmh_clk4_a2_e0.hw, + [RPMH_RF_CLK4_A] = &clk_rpmh_clk4_a2_e0_ao.hw, + [RPMH_RF_CLK5] = &clk_rpmh_clk5_a2_e0.hw, + [RPMH_RF_CLK5_A] = &clk_rpmh_clk5_a2_e0_ao.hw, + [RPMH_IPA_CLK] = &clk_rpmh_ipa.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_hawi = { + .clks = hawi_rpmh_clocks, + .num_clks = ARRAY_SIZE(hawi_rpmh_clocks), +}; + static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -1075,6 +1107,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) static const struct of_device_id clk_rpmh_match_table[] = { { .compatible = "qcom,eliza-rpmh-clk", .data = &clk_rpmh_eliza}, { .compatible = "qcom,glymur-rpmh-clk", .data = &clk_rpmh_glymur}, + { .compatible = "qcom,hawi-rpmh-clk", .data = &clk_rpmh_hawi}, { .compatible = "qcom,kaanapali-rpmh-clk", .data = &clk_rpmh_kaanapali}, { .compatible = "qcom,milos-rpmh-clk", .data = &clk_rpmh_milos}, { .compatible = "qcom,nord-rpmh-clk", .data = &clk_rpmh_nord}, From 24ba8ce4c9867e4224bb22ab3a50838d073fe13a Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:44 -0700 Subject: [PATCH 0907/5214] clk: qcom: Add Hawi TCSR clock controller driver Add support for the TCSR clock controller found on the Qualcomm Hawi SoC. This controller provides reference clocks for various peripherals including PCIe, UFS, and USB. Reviewed-by: Taniya Das Reviewed-by: Konrad Dybcio Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-5-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Kconfig | 7 ++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/tcsrcc-hawi.c | 158 +++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 drivers/clk/qcom/tcsrcc-hawi.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 9573e88d1f25..c3e7cf53e799 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -296,6 +296,13 @@ config QCOM_CLK_RPMH Say Y if you want to support the clocks exposed by RPMh on platforms such as SDM845. +config CLK_HAWI_TCSRCC + tristate "Hawi TCSR Clock Controller" + depends on ARM64 || COMPILE_TEST + help + Support for the TCSR clock controller on Hawi devices. + Say Y if you want to use peripheral devices such as PCIe, USB, UFS. + config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" depends on ARM || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 46c997fa59ca..a46d3ac97d09 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_GPUCC) += gpucc-glymur.o gxclkctl-kaanapali.o obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_VIDEOCC) += videocc-glymur.o +obj-$(CONFIG_CLK_HAWI_TCSRCC) += tcsrcc-hawi.o obj-$(CONFIG_CLK_KAANAPALI_CAMCC) += cambistmclkcc-kaanapali.o camcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_DISPCC) += dispcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_GCC) += gcc-kaanapali.o diff --git a/drivers/clk/qcom/tcsrcc-hawi.c b/drivers/clk/qcom/tcsrcc-hawi.c new file mode 100644 index 000000000000..c942b0c8e09f --- /dev/null +++ b/drivers/clk/qcom/tcsrcc-hawi.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "reset.h" + +enum { + DT_BI_TCXO_PAD, +}; + +static struct clk_branch tcsr_pcie_0_clkref_en = { + .halt_reg = 0x4c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x4c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_pcie_1_clkref_en = { + .halt_reg = 0x0, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ufs_clkref_en = { + .halt_reg = 0x10, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ufs_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_clkref_en = { + .halt_reg = 0x18, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x18, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb3_clkref_en = { + .halt_reg = 0x8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb3_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *tcsr_cc_hawi_clocks[] = { + [TCSR_PCIE_0_CLKREF_EN] = &tcsr_pcie_0_clkref_en.clkr, + [TCSR_PCIE_1_CLKREF_EN] = &tcsr_pcie_1_clkref_en.clkr, + [TCSR_UFS_CLKREF_EN] = &tcsr_ufs_clkref_en.clkr, + [TCSR_USB2_CLKREF_EN] = &tcsr_usb2_clkref_en.clkr, + [TCSR_USB3_CLKREF_EN] = &tcsr_usb3_clkref_en.clkr, +}; + +static const struct regmap_config tcsr_cc_hawi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x4c, + .fast_io = true, +}; + +static const struct qcom_cc_desc tcsr_cc_hawi_desc = { + .config = &tcsr_cc_hawi_regmap_config, + .clks = tcsr_cc_hawi_clocks, + .num_clks = ARRAY_SIZE(tcsr_cc_hawi_clocks), +}; + +static const struct of_device_id tcsr_cc_hawi_match_table[] = { + { .compatible = "qcom,hawi-tcsrcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, tcsr_cc_hawi_match_table); + +static int tcsr_cc_hawi_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &tcsr_cc_hawi_desc); +} + +static struct platform_driver tcsr_cc_hawi_driver = { + .probe = tcsr_cc_hawi_probe, + .driver = { + .name = "tcsrcc-hawi", + .of_match_table = tcsr_cc_hawi_match_table, + }, +}; + +module_platform_driver(tcsr_cc_hawi_driver); + +MODULE_DESCRIPTION("QTI TCSRCC HAWI Driver"); +MODULE_LICENSE("GPL"); From e1668c6c237ef09c1fc096ba005dbe9c1d2127de Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:45 -0700 Subject: [PATCH 0908/5214] clk: qcom: clk-alpha-pll: Add support for Taycan EHA_T PLL Add clock operations and register offsets to enable control of the Taycan EHA_T PLL, allowing for proper configuration and management of the PLL. Reviewed-by: Taniya Das Reviewed-by: Konrad Dybcio Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-6-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-alpha-pll.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 42d334492145..3a2157bebc52 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -31,6 +31,7 @@ enum { CLK_ALPHA_PLL_TYPE_PONGO_EKO_T = CLK_ALPHA_PLL_TYPE_PONGO_ELU, CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T = CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, + CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T = CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, CLK_ALPHA_PLL_TYPE_RIVIAN_EVO, CLK_ALPHA_PLL_TYPE_RIVIAN_ELU, CLK_ALPHA_PLL_TYPE_RIVIAN_EKO_T = CLK_ALPHA_PLL_TYPE_RIVIAN_ELU, @@ -198,16 +199,19 @@ extern const struct clk_ops clk_alpha_pll_zonda_ops; extern const struct clk_ops clk_alpha_pll_lucid_evo_ops; #define clk_alpha_pll_taycan_elu_ops clk_alpha_pll_lucid_evo_ops #define clk_alpha_pll_taycan_eko_t_ops clk_alpha_pll_lucid_evo_ops +#define clk_alpha_pll_taycan_eha_t_ops clk_alpha_pll_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_reset_lucid_evo_ops; #define clk_alpha_pll_reset_lucid_ole_ops clk_alpha_pll_reset_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_fixed_lucid_evo_ops; #define clk_alpha_pll_fixed_lucid_ole_ops clk_alpha_pll_fixed_lucid_evo_ops #define clk_alpha_pll_fixed_taycan_elu_ops clk_alpha_pll_fixed_lucid_evo_ops #define clk_alpha_pll_fixed_taycan_eko_t_ops clk_alpha_pll_fixed_lucid_evo_ops +#define clk_alpha_pll_fixed_taycan_eha_t_ops clk_alpha_pll_fixed_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_postdiv_lucid_evo_ops; #define clk_alpha_pll_postdiv_lucid_ole_ops clk_alpha_pll_postdiv_lucid_evo_ops #define clk_alpha_pll_postdiv_taycan_elu_ops clk_alpha_pll_postdiv_lucid_evo_ops #define clk_alpha_pll_postdiv_taycan_eko_t_ops clk_alpha_pll_postdiv_lucid_evo_ops +#define clk_alpha_pll_postdiv_taycan_eha_t_ops clk_alpha_pll_postdiv_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_pongo_elu_ops; #define clk_alpha_pll_pongo_eko_t_ops clk_alpha_pll_pongo_elu_ops @@ -246,6 +250,8 @@ void clk_pongo_elu_pll_configure(struct clk_alpha_pll *pll, struct regmap *regma clk_lucid_evo_pll_configure(pll, regmap, config) #define clk_taycan_eko_t_pll_configure(pll, regmap, config) \ clk_lucid_evo_pll_configure(pll, regmap, config) +#define clk_taycan_eha_t_pll_configure(pll, regmap, config) \ + clk_lucid_evo_pll_configure(pll, regmap, config) void clk_rivian_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); From 67121dad6cba6df7f5d8c21fa432ff543964c53f Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 09:50:46 -0700 Subject: [PATCH 0909/5214] clk: qcom: Add support for global clock controller on Hawi Add support for the global clock controller (GCC) on the Qualcomm Hawi SoC. Reviewed-by: Taniya Das Reviewed-by: Konrad Dybcio Reviewed-by: Mike Tipton Signed-off-by: Vivek Aknurwar Link: https://lore.kernel.org/r/20260506-clk-hawi-v3-7-530b538679f1@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-hawi.c | 3657 +++++++++++++++++++++++++++++++++++ 3 files changed, 3667 insertions(+) create mode 100644 drivers/clk/qcom/gcc-hawi.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index c3e7cf53e799..d9cff5b0281d 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -296,6 +296,15 @@ config QCOM_CLK_RPMH Say Y if you want to support the clocks exposed by RPMh on platforms such as SDM845. +config CLK_HAWI_GCC + tristate "Hawi Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the global clock controller on Hawi devices. + Say Y if you want to use peripheral devices such as UART, SPI, + I2C, USB, UFS, SD/eMMC, PCIe, etc. + config CLK_HAWI_TCSRCC tristate "Hawi TCSR Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index a46d3ac97d09..e100cfd6a52d 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_GPUCC) += gpucc-glymur.o gxclkctl-kaanapali.o obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_VIDEOCC) += videocc-glymur.o +obj-$(CONFIG_CLK_HAWI_GCC) += gcc-hawi.o obj-$(CONFIG_CLK_HAWI_TCSRCC) += tcsrcc-hawi.o obj-$(CONFIG_CLK_KAANAPALI_CAMCC) += cambistmclkcc-kaanapali.o camcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_DISPCC) += dispcc-kaanapali.o diff --git a/drivers/clk/qcom/gcc-hawi.c b/drivers/clk/qcom/gcc-hawi.c new file mode 100644 index 000000000000..6dd07c772c29 --- /dev/null +++ b/drivers/clk/qcom/gcc-hawi.c @@ -0,0 +1,3657 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK, + DT_PCIE_0_PIPE_CLK, + DT_PCIE_1_PIPE_CLK, + DT_UFS_PHY_RX_SYMBOL_0_CLK, + DT_UFS_PHY_RX_SYMBOL_1_CLK, + DT_UFS_PHY_TX_SYMBOL_0_CLK, + DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_GCC_GPLL0_OUT_EVEN, + P_GCC_GPLL0_OUT_MAIN, + P_GCC_GPLL4_OUT_MAIN, + P_GCC_GPLL5_OUT_MAIN, + P_GCC_GPLL7_OUT_MAIN, + P_GCC_GPLL9_OUT_MAIN, + P_PCIE_0_PIPE_CLK, + P_PCIE_1_PIPE_CLK, + P_SLEEP_CLK, + P_UFS_PHY_RX_SYMBOL_0_CLK, + P_UFS_PHY_RX_SYMBOL_1_CLK, + P_UFS_PHY_TX_SYMBOL_0_CLK, + P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +static struct clk_alpha_pll gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_taycan_eha_t_ops, + }, +}; + +static struct clk_alpha_pll gcc_gpll4 = { + .offset = 0x4000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll5 = { + .offset = 0x5000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll7 = { + .offset = 0x7000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll7", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll9 = { + .offset = 0x9000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll9", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL7_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll7.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL5_OUT_MAIN, 3 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll5.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL9_OUT_MAIN, 2 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll9.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_0_pipe_clk_src = { + .reg = 0x6b0a8, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_1_pipe_clk_src = { + .reg = 0x670a4, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_0_clk_src = { + .reg = 0x77068, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_1_clk_src = { + .reg = 0x770ec, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_1_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_tx_symbol_0_clk_src = { + .reg = 0x77058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_TX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x39074, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x64004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x65004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_gp3_clk_src = { + .cmd_rcgr = 0x66004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { + .cmd_rcgr = 0x6b0ac, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_0_phy_aux_clk_src = { + .cmd_rcgr = 0x6b0c4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_aux_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_phy_rchng_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_phy_rchng_clk_src = { + .cmd_rcgr = 0x6b08c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { + .cmd_rcgr = 0x670a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_aux_clk_src = { + .cmd_rcgr = 0x670c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_aux_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_rchng_clk_src = { + .cmd_rcgr = 0x67088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(60000000, P_GCC_GPLL0_OUT_MAIN, 10, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x33010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s0_clk_src = { + .cmd_rcgr = 0x17008, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s1_clk_src = { + .cmd_rcgr = 0x17024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s2_clk_src = { + .cmd_rcgr = 0x17040, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s3_clk_src = { + .cmd_rcgr = 0x1705c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s4_clk_src = { + .cmd_rcgr = 0x17078, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_EVEN, 2.5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap1_qspi_ref_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_qspi_ref_clk_src = { + .cmd_rcgr = 0x188c0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_qspi_ref_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s0_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(60000000, P_GCC_GPLL0_OUT_EVEN, 5, 0, 0), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { + .name = "gcc_qupv3_wrap1_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x18014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { + .name = "gcc_qupv3_wrap1_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x18150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { + .name = "gcc_qupv3_wrap1_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x182a0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s4_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(60000000, P_GCC_GPLL0_OUT_EVEN, 5, 0, 0), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { + .name = "gcc_qupv3_wrap1_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x183dc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { + .name = "gcc_qupv3_wrap1_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x18518, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s6_clk_src_init = { + .name = "gcc_qupv3_wrap1_s6_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { + .cmd_rcgr = 0x18654, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s7_clk_src_init = { + .name = "gcc_qupv3_wrap1_s7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { + .cmd_rcgr = 0x18790, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s0_clk_src_init = { + .name = "gcc_qupv3_wrap2_s0_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s0_clk_src = { + .cmd_rcgr = 0x1e01c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s1_clk_src_init = { + .name = "gcc_qupv3_wrap2_s1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s1_clk_src = { + .cmd_rcgr = 0x1e160, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s2_clk_src_init = { + .name = "gcc_qupv3_wrap2_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s2_clk_src = { + .cmd_rcgr = 0x1e29c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s3_clk_src_init = { + .name = "gcc_qupv3_wrap2_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s3_clk_src = { + .cmd_rcgr = 0x1e3d8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s4_clk_src_init = { + .name = "gcc_qupv3_wrap2_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s4_clk_src = { + .cmd_rcgr = 0x1e514, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s4_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_EVEN, 2.5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap3_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap3_qspi_ref_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_qspi_ref_clk_src = { + .cmd_rcgr = 0xa8650, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_qspi_ref_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s0_clk_src_init = { + .name = "gcc_qupv3_wrap3_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s0_clk_src = { + .cmd_rcgr = 0xa8014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s2_clk_src_init = { + .name = "gcc_qupv3_wrap3_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s2_clk_src = { + .cmd_rcgr = 0xa8168, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s3_clk_src_init = { + .name = "gcc_qupv3_wrap3_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s3_clk_src = { + .cmd_rcgr = 0xa82a4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s4_clk_src_init = { + .name = "gcc_qupv3_wrap3_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s4_clk_src = { + .cmd_rcgr = 0xa83e0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s5_clk_src_init = { + .name = "gcc_qupv3_wrap3_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s5_clk_src = { + .cmd_rcgr = 0xa851c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s0_clk_src_init = { + .name = "gcc_qupv3_wrap4_s0_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s0_clk_src = { + .cmd_rcgr = 0xa9014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s1_clk_src_init = { + .name = "gcc_qupv3_wrap4_s1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s1_clk_src = { + .cmd_rcgr = 0xa9150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s2_clk_src_init = { + .name = "gcc_qupv3_wrap4_s2_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s2_clk_src = { + .cmd_rcgr = 0xa928c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s3_clk_src_init = { + .name = "gcc_qupv3_wrap4_s3_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s3_clk_src = { + .cmd_rcgr = 0xa93c8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s4_clk_src_init = { + .name = "gcc_qupv3_wrap4_s4_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s4_clk_src = { + .cmd_rcgr = 0xa9504, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s4_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(37500000, P_GCC_GPLL0_OUT_EVEN, 8, 0, 0), + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(202000000, P_GCC_GPLL9_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { + .cmd_rcgr = 0x1401c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc4_apps_clk_src = { + .cmd_rcgr = 0x1601c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_sdcc4_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_apps_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(225000000, P_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + F(450000000, P_GCC_GPLL5_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x77034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(225000000, P_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + F(450000000, P_GCC_GPLL5_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { + .cmd_rcgr = 0x7708c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { + .cmd_rcgr = 0x770c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_5_core_clk_src[] = { + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(201500000, P_GCC_GPLL4_OUT_MAIN, 4, 0, 0), + F(403000000, P_GCC_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_unipro_5_core_clk_src = { + .cmd_rcgr = 0x770a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_unipro_5_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_5_core_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, P_GCC_GPLL0_OUT_EVEN, 4.5, 0, 0), + F(133333333, P_GCC_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { + .cmd_rcgr = 0x39034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x3904c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x39078, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_0_pipe_div_clk_src = { + .reg = 0x6b0a4, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_1_pipe_div_clk_src = { + .reg = 0x670a0, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_qupv3_wrap1_s2_clk_src = { + .reg = 0x1828c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_qupv3_wrap3_s1_clk_src = { + .reg = 0xa8154, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s1_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb30_prim_mock_utmi_postdiv_clk_src = { + .reg = 0x39064, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_aggre_noc_pcie_axi_clk = { + .halt_reg = 0x10068, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_noc_pcie_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_stardustnoc_usb3_prim_axi_clk = { + .halt_reg = 0x39094, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x39094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_stardustnoc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { + .halt_reg = 0x770f0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770f0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_hf_axi_clk = { + .halt_reg = 0x26014, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x26014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_sf_axi_clk = { + .halt_reg = 0x2601c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x2601c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2601c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_pcie_anoc_ahb_clk = { + .halt_reg = 0x10050, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_pcie_anoc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb3_prim_axi_clk = { + .halt_reg = 0x39090, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x39090, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_pcie_sf_axi_clk = { + .halt_reg = 0x10058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_pcie_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eva_axi0_clk = { + .halt_reg = 0x9f008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x9f008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9f008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eva_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eva_axi0c_clk = { + .halt_reg = 0x9f010, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x9f010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9f010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eva_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x64000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x64000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x65000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x65000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x66000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x66000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gemnoc_gfx_clk = { + .halt_reg = 0x71010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x71010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gemnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_smmu_vote_clk = { + .halt_reg = 0x7d000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mmu_tcu_vote_clk = { + .halt_reg = 0x7d02c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mmu_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x6b044, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b044, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_cfg_ahb_clk = { + .halt_reg = 0x6b040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_mstr_axi_clk = { + .halt_reg = 0x6b030, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b030, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_phy_aux_clk = { + .halt_reg = 0x6b054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_phy_rchng_clk = { + .halt_reg = 0x6b084, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b084, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x6b074, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_div2_clk = { + .halt_reg = 0x6b064, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b064, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_axi_clk = { + .halt_reg = 0x6b020, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_q2a_axi_clk = { + .halt_reg = 0x6b01c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b01c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_aux_clk = { + .halt_reg = 0x67040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_cfg_ahb_clk = { + .halt_reg = 0x6703c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6703c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_mstr_axi_clk = { + .halt_reg = 0x6702c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6702c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_phy_aux_clk = { + .halt_reg = 0x67050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_phy_rchng_clk = { + .halt_reg = 0x67080, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_clk = { + .halt_reg = 0x67070, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_div2_clk = { + .halt_reg = 0x67060, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_axi_clk = { + .halt_reg = 0x6701c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6701c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_q2a_axi_clk = { + .halt_reg = 0x67018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x3300c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3300c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x33004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x33004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x33008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_core_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s0_clk = { + .halt_reg = 0x17004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s1_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s2_clk = { + .halt_reg = 0x1703c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s3_clk = { + .halt_reg = 0x17058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s4_clk = { + .halt_reg = 0x17074, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s_ahb_clk = { + .halt_reg = 0x23000, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23000, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x2315c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_clk = { + .halt_reg = 0x23148, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_qspi_ref_clk = { + .halt_reg = 0x188bc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s0_clk = { + .halt_reg = 0x18004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s1_clk = { + .halt_reg = 0x18140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s2_clk = { + .halt_reg = 0x1827c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s3_clk = { + .halt_reg = 0x18290, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s4_clk = { + .halt_reg = 0x183cc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s5_clk = { + .halt_reg = 0x18508, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s6_clk = { + .halt_reg = 0x18644, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s7_clk = { + .halt_reg = 0x18780, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_2x_clk = { + .halt_reg = 0x232b4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_clk = { + .halt_reg = 0x232a0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s0_clk = { + .halt_reg = 0x1e004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s1_clk = { + .halt_reg = 0x1e148, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s2_clk = { + .halt_reg = 0x1e28c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s3_clk = { + .halt_reg = 0x1e3c8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s4_clk = { + .halt_reg = 0x1e504, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_2x_clk = { + .halt_reg = 0x2340c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_clk = { + .halt_reg = 0x233f8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_qspi_ref_clk = { + .halt_reg = 0xa8648, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s0_clk = { + .halt_reg = 0xa8004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s1_clk = { + .halt_reg = 0xa8140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s2_clk = { + .halt_reg = 0xa8158, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s3_clk = { + .halt_reg = 0xa8294, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s4_clk = { + .halt_reg = 0xa83d0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s5_clk = { + .halt_reg = 0xa850c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_core_2x_clk = { + .halt_reg = 0x23564, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_core_clk = { + .halt_reg = 0x23550, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s0_clk = { + .halt_reg = 0xa9004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s1_clk = { + .halt_reg = 0xa9140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s2_clk = { + .halt_reg = 0xa927c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s3_clk = { + .halt_reg = 0xa93b8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s4_clk = { + .halt_reg = 0xa94f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_m_axi_clk = { + .halt_reg = 0x23140, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23140, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_m_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_s_ahb_clk = { + .halt_reg = 0x23144, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23144, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_m_ahb_clk = { + .halt_reg = 0x23298, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23298, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_s_ahb_clk = { + .halt_reg = 0x2329c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2329c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_3_m_ahb_clk = { + .halt_reg = 0x233f0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x233f0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_3_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_3_s_ahb_clk = { + .halt_reg = 0x233f4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x233f4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_3_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_4_m_ahb_clk = { + .halt_reg = 0x23548, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23548, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_4_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_4_s_ahb_clk = { + .halt_reg = 0x2354c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2354c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_4_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x14014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x14004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc2_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_ahb_clk = { + .halt_reg = 0x16014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x16014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_apps_clk = { + .halt_reg = 0x16004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x16004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc4_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ahb_clk = { + .halt_reg = 0x77028, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_axi_clk = { + .halt_reg = 0x77018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ice_core_clk = { + .halt_reg = 0x7707c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7707c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_phy_aux_clk = { + .halt_reg = 0x770bc, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770bc, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_0_clk = { + .halt_reg = 0x77030, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x77030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_1_clk = { + .halt_reg = 0x770d8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x770d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_tx_symbol_0_clk = { + .halt_reg = 0x7702c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_tx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_unipro_5_core_clk = { + .halt_reg = 0x7706c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7706c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_5_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_unipro_5_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_master_clk = { + .halt_reg = 0x39018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_mock_utmi_clk = { + .halt_reg = 0x3902c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3902c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_sleep_clk = { + .halt_reg = 0x39028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_aux_clk = { + .halt_reg = 0x39068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0x3906c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3906c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0x39070, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x39070, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0_clk = { + .halt_reg = 0x32018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0c_clk = { + .halt_reg = 0x32020, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gcc_pcie_0_gdsc = { + .gdscr = 0x6b004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x52154, + .collapse_mask = BIT(0), + .pd = { + .name = "gcc_pcie_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_0_phy_gdsc = { + .gdscr = 0x6c000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x52154, + .collapse_mask = BIT(1), + .pd = { + .name = "gcc_pcie_0_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_gdsc = { + .gdscr = 0x67004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(2), + .pd = { + .name = "gcc_pcie_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_phy_gdsc = { + .gdscr = 0x68000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(3), + .pd = { + .name = "gcc_pcie_1_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_ufs_mem_phy_gdsc = { + .gdscr = 0x9e000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_ufs_mem_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_ufs_phy_gdsc = { + .gdscr = 0x77004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb30_prim_gdsc = { + .gdscr = 0x39004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb3_phy_gdsc = { + .gdscr = 0x50018, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_usb3_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gcc_hawi_clocks[] = { + [GCC_AGGRE_NOC_PCIE_AXI_CLK] = &gcc_aggre_noc_pcie_axi_clk.clkr, + [GCC_AGGRE_STARDUSTNOC_USB3_PRIM_AXI_CLK] = &gcc_aggre_stardustnoc_usb3_prim_axi_clk.clkr, + [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CAMERA_HF_AXI_CLK] = &gcc_camera_hf_axi_clk.clkr, + [GCC_CAMERA_SF_AXI_CLK] = &gcc_camera_sf_axi_clk.clkr, + [GCC_CFG_NOC_PCIE_ANOC_AHB_CLK] = &gcc_cfg_noc_pcie_anoc_ahb_clk.clkr, + [GCC_CFG_NOC_USB3_PRIM_AXI_CLK] = &gcc_cfg_noc_usb3_prim_axi_clk.clkr, + [GCC_CNOC_PCIE_SF_AXI_CLK] = &gcc_cnoc_pcie_sf_axi_clk.clkr, + [GCC_EVA_AXI0_CLK] = &gcc_eva_axi0_clk.clkr, + [GCC_EVA_AXI0C_CLK] = &gcc_eva_axi0c_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr, + [GCC_GPLL0] = &gcc_gpll0.clkr, + [GCC_GPLL0_OUT_EVEN] = &gcc_gpll0_out_even.clkr, + [GCC_GPLL4] = &gcc_gpll4.clkr, + [GCC_GPLL5] = &gcc_gpll5.clkr, + [GCC_GPLL7] = &gcc_gpll7.clkr, + [GCC_GPLL9] = &gcc_gpll9.clkr, + [GCC_GPU_GEMNOC_GFX_CLK] = &gcc_gpu_gemnoc_gfx_clk.clkr, + [GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr, + [GCC_GPU_SMMU_VOTE_CLK] = &gcc_gpu_smmu_vote_clk.clkr, + [GCC_MMU_TCU_VOTE_CLK] = &gcc_mmu_tcu_vote_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_AUX_CLK_SRC] = &gcc_pcie_0_aux_clk_src.clkr, + [GCC_PCIE_0_CFG_AHB_CLK] = &gcc_pcie_0_cfg_ahb_clk.clkr, + [GCC_PCIE_0_MSTR_AXI_CLK] = &gcc_pcie_0_mstr_axi_clk.clkr, + [GCC_PCIE_0_PHY_AUX_CLK] = &gcc_pcie_0_phy_aux_clk.clkr, + [GCC_PCIE_0_PHY_AUX_CLK_SRC] = &gcc_pcie_0_phy_aux_clk_src.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK] = &gcc_pcie_0_phy_rchng_clk.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK_SRC] = &gcc_pcie_0_phy_rchng_clk_src.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_0_PIPE_CLK_SRC] = &gcc_pcie_0_pipe_clk_src.clkr, + [GCC_PCIE_0_PIPE_DIV2_CLK] = &gcc_pcie_0_pipe_div2_clk.clkr, + [GCC_PCIE_0_PIPE_DIV_CLK_SRC] = &gcc_pcie_0_pipe_div_clk_src.clkr, + [GCC_PCIE_0_SLV_AXI_CLK] = &gcc_pcie_0_slv_axi_clk.clkr, + [GCC_PCIE_0_SLV_Q2A_AXI_CLK] = &gcc_pcie_0_slv_q2a_axi_clk.clkr, + [GCC_PCIE_1_AUX_CLK] = &gcc_pcie_1_aux_clk.clkr, + [GCC_PCIE_1_AUX_CLK_SRC] = &gcc_pcie_1_aux_clk_src.clkr, + [GCC_PCIE_1_CFG_AHB_CLK] = &gcc_pcie_1_cfg_ahb_clk.clkr, + [GCC_PCIE_1_MSTR_AXI_CLK] = &gcc_pcie_1_mstr_axi_clk.clkr, + [GCC_PCIE_1_PHY_AUX_CLK] = &gcc_pcie_1_phy_aux_clk.clkr, + [GCC_PCIE_1_PHY_AUX_CLK_SRC] = &gcc_pcie_1_phy_aux_clk_src.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK] = &gcc_pcie_1_phy_rchng_clk.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK_SRC] = &gcc_pcie_1_phy_rchng_clk_src.clkr, + [GCC_PCIE_1_PIPE_CLK] = &gcc_pcie_1_pipe_clk.clkr, + [GCC_PCIE_1_PIPE_CLK_SRC] = &gcc_pcie_1_pipe_clk_src.clkr, + [GCC_PCIE_1_PIPE_DIV2_CLK] = &gcc_pcie_1_pipe_div2_clk.clkr, + [GCC_PCIE_1_PIPE_DIV_CLK_SRC] = &gcc_pcie_1_pipe_div_clk_src.clkr, + [GCC_PCIE_1_SLV_AXI_CLK] = &gcc_pcie_1_slv_axi_clk.clkr, + [GCC_PCIE_1_SLV_Q2A_AXI_CLK] = &gcc_pcie_1_slv_q2a_axi_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_QUPV3_I2C_CORE_CLK] = &gcc_qupv3_i2c_core_clk.clkr, + [GCC_QUPV3_I2C_S0_CLK] = &gcc_qupv3_i2c_s0_clk.clkr, + [GCC_QUPV3_I2C_S0_CLK_SRC] = &gcc_qupv3_i2c_s0_clk_src.clkr, + [GCC_QUPV3_I2C_S1_CLK] = &gcc_qupv3_i2c_s1_clk.clkr, + [GCC_QUPV3_I2C_S1_CLK_SRC] = &gcc_qupv3_i2c_s1_clk_src.clkr, + [GCC_QUPV3_I2C_S2_CLK] = &gcc_qupv3_i2c_s2_clk.clkr, + [GCC_QUPV3_I2C_S2_CLK_SRC] = &gcc_qupv3_i2c_s2_clk_src.clkr, + [GCC_QUPV3_I2C_S3_CLK] = &gcc_qupv3_i2c_s3_clk.clkr, + [GCC_QUPV3_I2C_S3_CLK_SRC] = &gcc_qupv3_i2c_s3_clk_src.clkr, + [GCC_QUPV3_I2C_S4_CLK] = &gcc_qupv3_i2c_s4_clk.clkr, + [GCC_QUPV3_I2C_S4_CLK_SRC] = &gcc_qupv3_i2c_s4_clk_src.clkr, + [GCC_QUPV3_I2C_S_AHB_CLK] = &gcc_qupv3_i2c_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_2X_CLK] = &gcc_qupv3_wrap1_core_2x_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_CLK] = &gcc_qupv3_wrap1_core_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK] = &gcc_qupv3_wrap1_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP1_S0_CLK] = &gcc_qupv3_wrap1_s0_clk.clkr, + [GCC_QUPV3_WRAP1_S0_CLK_SRC] = &gcc_qupv3_wrap1_s0_clk_src.clkr, + [GCC_QUPV3_WRAP1_S1_CLK] = &gcc_qupv3_wrap1_s1_clk.clkr, + [GCC_QUPV3_WRAP1_S1_CLK_SRC] = &gcc_qupv3_wrap1_s1_clk_src.clkr, + [GCC_QUPV3_WRAP1_S2_CLK] = &gcc_qupv3_wrap1_s2_clk.clkr, + [GCC_QUPV3_WRAP1_S2_CLK_SRC] = &gcc_qupv3_wrap1_s2_clk_src.clkr, + [GCC_QUPV3_WRAP1_S3_CLK] = &gcc_qupv3_wrap1_s3_clk.clkr, + [GCC_QUPV3_WRAP1_S3_CLK_SRC] = &gcc_qupv3_wrap1_s3_clk_src.clkr, + [GCC_QUPV3_WRAP1_S4_CLK] = &gcc_qupv3_wrap1_s4_clk.clkr, + [GCC_QUPV3_WRAP1_S4_CLK_SRC] = &gcc_qupv3_wrap1_s4_clk_src.clkr, + [GCC_QUPV3_WRAP1_S5_CLK] = &gcc_qupv3_wrap1_s5_clk.clkr, + [GCC_QUPV3_WRAP1_S5_CLK_SRC] = &gcc_qupv3_wrap1_s5_clk_src.clkr, + [GCC_QUPV3_WRAP1_S6_CLK] = &gcc_qupv3_wrap1_s6_clk.clkr, + [GCC_QUPV3_WRAP1_S6_CLK_SRC] = &gcc_qupv3_wrap1_s6_clk_src.clkr, + [GCC_QUPV3_WRAP1_S7_CLK] = &gcc_qupv3_wrap1_s7_clk.clkr, + [GCC_QUPV3_WRAP1_S7_CLK_SRC] = &gcc_qupv3_wrap1_s7_clk_src.clkr, + [GCC_QUPV3_WRAP2_CORE_2X_CLK] = &gcc_qupv3_wrap2_core_2x_clk.clkr, + [GCC_QUPV3_WRAP2_CORE_CLK] = &gcc_qupv3_wrap2_core_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK] = &gcc_qupv3_wrap2_s0_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK_SRC] = &gcc_qupv3_wrap2_s0_clk_src.clkr, + [GCC_QUPV3_WRAP2_S1_CLK] = &gcc_qupv3_wrap2_s1_clk.clkr, + [GCC_QUPV3_WRAP2_S1_CLK_SRC] = &gcc_qupv3_wrap2_s1_clk_src.clkr, + [GCC_QUPV3_WRAP2_S2_CLK] = &gcc_qupv3_wrap2_s2_clk.clkr, + [GCC_QUPV3_WRAP2_S2_CLK_SRC] = &gcc_qupv3_wrap2_s2_clk_src.clkr, + [GCC_QUPV3_WRAP2_S3_CLK] = &gcc_qupv3_wrap2_s3_clk.clkr, + [GCC_QUPV3_WRAP2_S3_CLK_SRC] = &gcc_qupv3_wrap2_s3_clk_src.clkr, + [GCC_QUPV3_WRAP2_S4_CLK] = &gcc_qupv3_wrap2_s4_clk.clkr, + [GCC_QUPV3_WRAP2_S4_CLK_SRC] = &gcc_qupv3_wrap2_s4_clk_src.clkr, + [GCC_QUPV3_WRAP3_CORE_2X_CLK] = &gcc_qupv3_wrap3_core_2x_clk.clkr, + [GCC_QUPV3_WRAP3_CORE_CLK] = &gcc_qupv3_wrap3_core_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK] = &gcc_qupv3_wrap3_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP3_S0_CLK] = &gcc_qupv3_wrap3_s0_clk.clkr, + [GCC_QUPV3_WRAP3_S0_CLK_SRC] = &gcc_qupv3_wrap3_s0_clk_src.clkr, + [GCC_QUPV3_WRAP3_S1_CLK] = &gcc_qupv3_wrap3_s1_clk.clkr, + [GCC_QUPV3_WRAP3_S1_CLK_SRC] = &gcc_qupv3_wrap3_s1_clk_src.clkr, + [GCC_QUPV3_WRAP3_S2_CLK] = &gcc_qupv3_wrap3_s2_clk.clkr, + [GCC_QUPV3_WRAP3_S2_CLK_SRC] = &gcc_qupv3_wrap3_s2_clk_src.clkr, + [GCC_QUPV3_WRAP3_S3_CLK] = &gcc_qupv3_wrap3_s3_clk.clkr, + [GCC_QUPV3_WRAP3_S3_CLK_SRC] = &gcc_qupv3_wrap3_s3_clk_src.clkr, + [GCC_QUPV3_WRAP3_S4_CLK] = &gcc_qupv3_wrap3_s4_clk.clkr, + [GCC_QUPV3_WRAP3_S4_CLK_SRC] = &gcc_qupv3_wrap3_s4_clk_src.clkr, + [GCC_QUPV3_WRAP3_S5_CLK] = &gcc_qupv3_wrap3_s5_clk.clkr, + [GCC_QUPV3_WRAP3_S5_CLK_SRC] = &gcc_qupv3_wrap3_s5_clk_src.clkr, + [GCC_QUPV3_WRAP4_CORE_2X_CLK] = &gcc_qupv3_wrap4_core_2x_clk.clkr, + [GCC_QUPV3_WRAP4_CORE_CLK] = &gcc_qupv3_wrap4_core_clk.clkr, + [GCC_QUPV3_WRAP4_S0_CLK] = &gcc_qupv3_wrap4_s0_clk.clkr, + [GCC_QUPV3_WRAP4_S0_CLK_SRC] = &gcc_qupv3_wrap4_s0_clk_src.clkr, + [GCC_QUPV3_WRAP4_S1_CLK] = &gcc_qupv3_wrap4_s1_clk.clkr, + [GCC_QUPV3_WRAP4_S1_CLK_SRC] = &gcc_qupv3_wrap4_s1_clk_src.clkr, + [GCC_QUPV3_WRAP4_S2_CLK] = &gcc_qupv3_wrap4_s2_clk.clkr, + [GCC_QUPV3_WRAP4_S2_CLK_SRC] = &gcc_qupv3_wrap4_s2_clk_src.clkr, + [GCC_QUPV3_WRAP4_S3_CLK] = &gcc_qupv3_wrap4_s3_clk.clkr, + [GCC_QUPV3_WRAP4_S3_CLK_SRC] = &gcc_qupv3_wrap4_s3_clk_src.clkr, + [GCC_QUPV3_WRAP4_S4_CLK] = &gcc_qupv3_wrap4_s4_clk.clkr, + [GCC_QUPV3_WRAP4_S4_CLK_SRC] = &gcc_qupv3_wrap4_s4_clk_src.clkr, + [GCC_QUPV3_WRAP_1_M_AXI_CLK] = &gcc_qupv3_wrap_1_m_axi_clk.clkr, + [GCC_QUPV3_WRAP_1_S_AHB_CLK] = &gcc_qupv3_wrap_1_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_M_AHB_CLK] = &gcc_qupv3_wrap_2_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_S_AHB_CLK] = &gcc_qupv3_wrap_2_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_3_M_AHB_CLK] = &gcc_qupv3_wrap_3_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_3_S_AHB_CLK] = &gcc_qupv3_wrap_3_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_4_M_AHB_CLK] = &gcc_qupv3_wrap_4_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_4_S_AHB_CLK] = &gcc_qupv3_wrap_4_s_ahb_clk.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr, + [GCC_SDCC4_AHB_CLK] = &gcc_sdcc4_ahb_clk.clkr, + [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SDCC4_APPS_CLK_SRC] = &gcc_sdcc4_apps_clk_src.clkr, + [GCC_UFS_PHY_AHB_CLK] = &gcc_ufs_phy_ahb_clk.clkr, + [GCC_UFS_PHY_AXI_CLK] = &gcc_ufs_phy_axi_clk.clkr, + [GCC_UFS_PHY_AXI_CLK_SRC] = &gcc_ufs_phy_axi_clk_src.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK] = &gcc_ufs_phy_ice_core_clk.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &gcc_ufs_phy_ice_core_clk_src.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK] = &gcc_ufs_phy_phy_aux_clk.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &gcc_ufs_phy_phy_aux_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &gcc_ufs_phy_rx_symbol_0_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_rx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK] = &gcc_ufs_phy_rx_symbol_1_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC] = &gcc_ufs_phy_rx_symbol_1_clk_src.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &gcc_ufs_phy_tx_symbol_0_clk.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_tx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_UNIPRO_5_CORE_CLK] = &gcc_ufs_phy_unipro_5_core_clk.clkr, + [GCC_UFS_PHY_UNIPRO_5_CORE_CLK_SRC] = &gcc_ufs_phy_unipro_5_core_clk_src.clkr, + [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK_SRC] = &gcc_usb30_prim_master_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK] = &gcc_usb30_prim_mock_utmi_clk.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC] = &gcc_usb30_prim_mock_utmi_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC] = &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr, + [GCC_USB30_PRIM_SLEEP_CLK] = &gcc_usb30_prim_sleep_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK] = &gcc_usb3_prim_phy_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &gcc_usb3_prim_phy_aux_clk_src.clkr, + [GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &gcc_usb3_prim_phy_com_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK] = &gcc_usb3_prim_phy_pipe_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK_SRC] = &gcc_usb3_prim_phy_pipe_clk_src.clkr, + [GCC_VIDEO_AXI0_CLK] = &gcc_video_axi0_clk.clkr, + [GCC_VIDEO_AXI0C_CLK] = &gcc_video_axi0c_clk.clkr, +}; + +static struct gdsc *gcc_hawi_gdscs[] = { + [GCC_PCIE_0_GDSC] = &gcc_pcie_0_gdsc, + [GCC_PCIE_0_PHY_GDSC] = &gcc_pcie_0_phy_gdsc, + [GCC_PCIE_1_GDSC] = &gcc_pcie_1_gdsc, + [GCC_PCIE_1_PHY_GDSC] = &gcc_pcie_1_phy_gdsc, + [GCC_UFS_MEM_PHY_GDSC] = &gcc_ufs_mem_phy_gdsc, + [GCC_UFS_PHY_GDSC] = &gcc_ufs_phy_gdsc, + [GCC_USB30_PRIM_GDSC] = &gcc_usb30_prim_gdsc, + [GCC_USB3_PHY_GDSC] = &gcc_usb3_phy_gdsc, +}; + +static const struct qcom_reset_map gcc_hawi_resets[] = { + [GCC_CAMERA_BCR] = { 0x26000 }, + [GCC_EVA_AXI0_CLK_ARES] = { 0x9f008, 2 }, + [GCC_EVA_AXI0C_CLK_ARES] = { 0x9f010, 2 }, + [GCC_EVA_BCR] = { 0x9f000 }, + [GCC_GPU_BCR] = { 0x71000 }, + [GCC_PCIE_0_BCR] = { 0x6b000 }, + [GCC_PCIE_0_LINK_DOWN_BCR] = { 0x6c014 }, + [GCC_PCIE_0_NOCSR_COM_PHY_BCR] = { 0x6c020 }, + [GCC_PCIE_0_PHY_BCR] = { 0x6c01c }, + [GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR] = { 0x6c028 }, + [GCC_PCIE_1_BCR] = { 0x67000 }, + [GCC_PCIE_1_LINK_DOWN_BCR] = { 0x8e014 }, + [GCC_PCIE_1_NOCSR_COM_PHY_BCR] = { 0x8e020 }, + [GCC_PCIE_1_PHY_BCR] = { 0x8e01c }, + [GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR] = { 0x8e024 }, + [GCC_PCIE_PHY_BCR] = { 0x6f000 }, + [GCC_PCIE_PHY_CFG_AHB_BCR] = { 0x6f00c }, + [GCC_PCIE_PHY_COM_BCR] = { 0x6f010 }, + [GCC_PCIE_RSCC_BCR] = { 0x11000 }, + [GCC_PDM_BCR] = { 0x33000 }, + [GCC_QUPV3_WRAPPER_1_BCR] = { 0x18000 }, + [GCC_QUPV3_WRAPPER_2_BCR] = { 0x1e000 }, + [GCC_QUPV3_WRAPPER_3_BCR] = { 0xa8000 }, + [GCC_QUPV3_WRAPPER_4_BCR] = { 0xa9000 }, + [GCC_QUPV3_WRAPPER_I2C_BCR] = { 0x17000 }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 }, + [GCC_SDCC2_BCR] = { 0x14000 }, + [GCC_SDCC4_BCR] = { 0x16000 }, + [GCC_TCSR_PCIE_BCR] = { 0x6f018 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0x39000 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3_DP_PHY_SEC_BCR] = { 0x50014 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3_PHY_SEC_BCR] = { 0x5000c }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_USB3PHY_PHY_SEC_BCR] = { 0x50010 }, + [GCC_VIDEO_AXI0_CLK_ARES] = { 0x32018, 2 }, + [GCC_VIDEO_AXI0C_CLK_ARES] = { 0x32020, 2 }, + [GCC_VIDEO_BCR] = { 0x32000 }, + [GCC_VIDEO_XO_CLK_ARES] = { 0x32028, 2 }, +}; + +static const u32 gcc_hawi_critical_cbcrs[] = { + 0xa0004, /* GCC_CAM_BIST_MCLK_AHB_CLK */ + 0x26004, /* GCC_CAMERA_AHB_CLK */ + 0x26028, /* GCC_CAMERA_RSC_CORE_CLK */ + 0x26024, /* GCC_CAMERA_XO_CLK */ + 0x9f004, /* GCC_EVA_AHB_CLK */ + 0x9f018, /* GCC_EVA_XO_CLK */ + 0x71004, /* GCC_GPU_CFG_AHB_CLK */ + 0x7101c, /* GCC_GPU_RSC_CORE_CLK */ + 0x67084, /* GCC_PCIE_1_RSC_CORE_CLK */ + 0x43014, /* GCC_PCIE_LINK_XO_CLK */ + 0x6b088, /* GCC_PCIE_RSC_CORE_CLK */ + 0x52010, /* GCC_PCIE_RSCC_CFG_AHB_CLK */ + 0x52010, /* GCC_PCIE_RSCC_XO_CLK */ + 0x32004, /* GCC_VIDEO_AHB_CLK */ + 0x32028, /* GCC_VIDEO_XO_CLK */ +}; + +static const struct clk_rcg_dfs_data gcc_hawi_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap1_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s4_clk_src), +}; + +static const struct regmap_config gcc_hawi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f41f4, + .fast_io = true, +}; + +static void clk_hawi_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* FORCE_MEM_CORE_ON for ufs phy ice core clocks */ + qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true); +} + +static const struct qcom_cc_driver_data gcc_hawi_driver_data = { + .clk_cbcrs = gcc_hawi_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gcc_hawi_critical_cbcrs), + .dfs_rcgs = gcc_hawi_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_hawi_dfs_clocks), + .clk_regs_configure = clk_hawi_regs_configure, +}; + +static const struct qcom_cc_desc gcc_hawi_desc = { + .config = &gcc_hawi_regmap_config, + .clks = gcc_hawi_clocks, + .num_clks = ARRAY_SIZE(gcc_hawi_clocks), + .resets = gcc_hawi_resets, + .num_resets = ARRAY_SIZE(gcc_hawi_resets), + .gdscs = gcc_hawi_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_hawi_gdscs), + .use_rpm = true, + .driver_data = &gcc_hawi_driver_data, +}; + +static const struct of_device_id gcc_hawi_match_table[] = { + { .compatible = "qcom,hawi-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_hawi_match_table); + +static int gcc_hawi_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_hawi_desc); +} + +static struct platform_driver gcc_hawi_driver = { + .probe = gcc_hawi_probe, + .driver = { + .name = "gcc-hawi", + .of_match_table = gcc_hawi_match_table, + }, +}; + +static int __init gcc_hawi_init(void) +{ + return platform_driver_register(&gcc_hawi_driver); +} +subsys_initcall(gcc_hawi_init); + +static void __exit gcc_hawi_exit(void) +{ + platform_driver_unregister(&gcc_hawi_driver); +} +module_exit(gcc_hawi_exit); + +MODULE_DESCRIPTION("QTI GCC HAWI Driver"); +MODULE_LICENSE("GPL"); From 190cc5370a8b6eb2894f1e958058b0c6589c582e Mon Sep 17 00:00:00 2001 From: Takahiro Itazuri Date: Mon, 20 Apr 2026 15:46:04 +0000 Subject: [PATCH 0910/5214] KVM: Rename invalidate_begin to invalidate_start for consistency Rename kvm_mmu_invalidate_begin() to kvm_mmu_invalidate_start() to align with mmu_notifier_ops.invalidate_range_start(), which is the callback that ultimately drives KVM's MMU invalidation. While the naming within KVM itself is a close split between "_begin" and "_start": $ git grep -E "invalidate(_range)?_begin" **/kvm* | wc -l 12 $ git grep -E "invalidate(_range)?_start" **/kvm* | wc -l 21 All two of the begin() uses are in KVM: $ git grep -E "invalidate(_range)?_begin" * | wc -l 14 And those two holdouts are bugs in invalidate_range_start()'s comment, i.e. will also be fixed sooner or later[*]. On the other hand, use of _start() is pervasive throughout the kernel: $ git grep -E "invalidate(_range)?_start" * | wc -l 117 Even if that weren't the case, conforming to the mmu_notifier_ops naming is the right call since invalidate_range_start() is the external API that KVM hooks into. No functional change intended. Link: https://lore.kernel.org/all/20260513163546.1176742-1-seanjc@google.com [*] Signed-off-by: Takahiro Itazuri Link: https://patch.msgid.link/20260420154720.29012-4-itazur@amazon.com [sean: massage changelog to provide more (accurate) numbers] Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/mmu.c | 2 +- include/linux/kvm_host.h | 2 +- virt/kvm/guest_memfd.c | 14 +++++++------- virt/kvm/kvm_main.c | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f8aa7eda661e..85fd8d9e70ce 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7002,7 +7002,7 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) write_lock(&kvm->mmu_lock); - kvm_mmu_invalidate_begin(kvm); + kvm_mmu_invalidate_start(kvm); kvm_mmu_invalidate_range_add(kvm, gfn_start, gfn_end); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 4c14aee1fb06..7b231a1e63ba 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1562,7 +1562,7 @@ void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc); void *kvm_mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc); #endif -void kvm_mmu_invalidate_begin(struct kvm *kvm); +void kvm_mmu_invalidate_start(struct kvm *kvm); void kvm_mmu_invalidate_range_add(struct kvm *kvm, gfn_t start, gfn_t end); void kvm_mmu_invalidate_end(struct kvm *kvm); bool kvm_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range); diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 69c9d6d546b2..a35a55571a2d 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -157,7 +157,7 @@ static enum kvm_gfn_range_filter kvm_gmem_get_invalidate_filter(struct inode *in return KVM_FILTER_PRIVATE; } -static void __kvm_gmem_invalidate_begin(struct gmem_file *f, pgoff_t start, +static void __kvm_gmem_invalidate_start(struct gmem_file *f, pgoff_t start, pgoff_t end, enum kvm_gfn_range_filter attr_filter) { @@ -181,7 +181,7 @@ static void __kvm_gmem_invalidate_begin(struct gmem_file *f, pgoff_t start, found_memslot = true; KVM_MMU_LOCK(kvm); - kvm_mmu_invalidate_begin(kvm); + kvm_mmu_invalidate_start(kvm); } flush |= kvm_mmu_unmap_gfn_range(kvm, &gfn_range); @@ -194,7 +194,7 @@ static void __kvm_gmem_invalidate_begin(struct gmem_file *f, pgoff_t start, KVM_MMU_UNLOCK(kvm); } -static void kvm_gmem_invalidate_begin(struct inode *inode, pgoff_t start, +static void kvm_gmem_invalidate_start(struct inode *inode, pgoff_t start, pgoff_t end) { enum kvm_gfn_range_filter attr_filter; @@ -203,7 +203,7 @@ static void kvm_gmem_invalidate_begin(struct inode *inode, pgoff_t start, attr_filter = kvm_gmem_get_invalidate_filter(inode); kvm_gmem_for_each_file(f, inode) - __kvm_gmem_invalidate_begin(f, start, end, attr_filter); + __kvm_gmem_invalidate_start(f, start, end, attr_filter); } static void __kvm_gmem_invalidate_end(struct gmem_file *f, pgoff_t start, @@ -238,7 +238,7 @@ static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len) */ filemap_invalidate_lock(inode->i_mapping); - kvm_gmem_invalidate_begin(inode, start, end); + kvm_gmem_invalidate_start(inode, start, end); truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1); @@ -352,7 +352,7 @@ static int kvm_gmem_release(struct inode *inode, struct file *file) * Zap all SPTEs pointed at by this file. Do not free the backing * memory, as its lifetime is associated with the inode, not the file. */ - __kvm_gmem_invalidate_begin(f, 0, -1ul, + __kvm_gmem_invalidate_start(f, 0, -1ul, kvm_gmem_get_invalidate_filter(inode)); __kvm_gmem_invalidate_end(f, 0, -1ul); @@ -504,7 +504,7 @@ static int kvm_gmem_error_folio(struct address_space *mapping, struct folio *fol start = folio->index; end = start + folio_nr_pages(folio); - kvm_gmem_invalidate_begin(mapping->host, start, end); + kvm_gmem_invalidate_start(mapping->host, start, end); /* * Do not truncate the range, what action is taken in response to the diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 89489996fbc1..7ba621d2c14c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -670,7 +670,7 @@ static __always_inline bool kvm_age_hva_range_no_flush(struct mmu_notifier *mn, return kvm_age_hva_range(mn, start, end, handler, false); } -void kvm_mmu_invalidate_begin(struct kvm *kvm) +void kvm_mmu_invalidate_start(struct kvm *kvm) { lockdep_assert_held_write(&kvm->mmu_lock); /* @@ -726,7 +726,7 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, .start = range->start, .end = range->end, .handler = kvm_mmu_unmap_gfn_range, - .on_lock = kvm_mmu_invalidate_begin, + .on_lock = kvm_mmu_invalidate_start, .flush_on_ret = true, .may_block = mmu_notifier_range_blockable(range), }; @@ -2542,7 +2542,7 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, .end = end, .arg.attributes = attributes, .handler = kvm_pre_set_memory_attributes, - .on_lock = kvm_mmu_invalidate_begin, + .on_lock = kvm_mmu_invalidate_start, .flush_on_ret = true, .may_block = true, }; From 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 23 Apr 2026 09:26:27 -0700 Subject: [PATCH 0911/5214] KVM: x86: Ensure vendor's exit handler runs before fastpath userspace exits Move the handling of fastpath userspace exits into vendor code to ensure KVM runs vendor specific operations that need to run before userspace gains control of the vCPU. E.g. for VMX (and soon to be for SVM as well), KVM needs to flush the PML buffer prior to exiting to userspace, otherwise any memory written by the final KVM_RUN might never be flagged as dirty. Note, waiting to snapshot CR0 and CR3 until svm_handle_exit() is flawed in general, as that risks consuming stale state in a fastpath handler. That will be addressed in a future change. Fixes: f7f39c50edb9 ("KVM: x86: Exit to userspace if fastpath triggers one on instruction skip") Cc: stable@vger.kernel.org Cc: Nikunj A. Dadhania Reviewed-by: Nikunj A. Dadhania Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260423162628.490962-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ arch/x86/kvm/x86.c | 3 --- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index b78dd8805ebb..fd0362874756 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3671,6 +3671,9 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) vcpu->arch.cr3 = svm->vmcb->save.cr3; } + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + if (is_guest_mode(vcpu)) { int vmexit; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 1701db1b2e18..d81b22359918 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6705,6 +6705,9 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) if (enable_pml && !is_guest_mode(vcpu)) vmx_flush_pml_buffer(vcpu); + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + /* * KVM should never reach this point with a pending nested VM-Enter. * More specifically, short-circuiting VM-Entry to emulate L2 due to diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 48f259015ce4..810ff08780d1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -11588,9 +11588,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (vcpu->arch.apic_attention) kvm_lapic_sync_from_vapic(vcpu); - if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) - return 0; - r = kvm_x86_call(handle_exit)(vcpu, exit_fastpath); return r; From b21525756e8288560939bc2055218f3e2961db04 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 23 Apr 2026 09:26:28 -0700 Subject: [PATCH 0912/5214] KVM: SVM: Refresh vcpu->arch.cr{0,3} prior to invoking fastpath handler Refresh KVM's copies of CR0 and CR3 from the VMCB prior to (potentially) invoking a fastpath handler to ensure that KVM doesn't consume stale state. While it's unlikely KVM will ever consume CR3 or CR0.{TS,MP} in the fastpath, grabbing the values from the VMCB is inexpensive, i.e. the risk of subtle bugs far outweighs the reward of deferring reads for a small subset of VM-Exits. Note, KVM doesn't currently consume CR3 or CR0.{TS,MP} in the fastpath, as KVM requires next_rip to be valid (i.e. KVM doesn't read CR3 to decode the instruction), CR0.MP is never consumed, and CR0.TS is only consumed by the full emulator. Reviewed-by: Nikunj A. Dadhania Link: https://patch.msgid.link/20260423162628.490962-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/svm.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index fd0362874756..95c411da6f2c 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3663,14 +3663,6 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) struct vcpu_svm *svm = to_svm(vcpu); struct kvm_run *kvm_run = vcpu->run; - /* SEV-ES guests must use the CR write traps to track CR registers. */ - if (!is_sev_es_guest(vcpu)) { - if (!svm_is_intercept(svm, INTERCEPT_CR0_WRITE)) - vcpu->arch.cr0 = svm->vmcb->save.cr0; - if (npt_enabled) - vcpu->arch.cr3 = svm->vmcb->save.cr3; - } - if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) return 0; @@ -4527,11 +4519,17 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL)) x86_spec_ctrl_restore_host(svm->virt_spec_ctrl); + /* SEV-ES guests must use the CR write traps to track CR registers. */ if (!is_sev_es_guest(vcpu)) { vcpu->arch.cr2 = svm->vmcb->save.cr2; vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax; vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp; vcpu->arch.rip = svm->vmcb->save.rip; + + if (!svm_is_intercept(svm, INTERCEPT_CR0_WRITE)) + vcpu->arch.cr0 = svm->vmcb->save.cr0; + if (npt_enabled) + vcpu->arch.cr3 = svm->vmcb->save.cr3; } kvm_reset_dirty_registers(vcpu); From 64f1fa859c1e6371e68286f81bedbc0b21c3d068 Mon Sep 17 00:00:00 2001 From: Piotr Zarycki Date: Tue, 12 May 2026 18:13:17 +0200 Subject: [PATCH 0913/5214] KVM: selftests: sync_regs_test: drop stale TODO comment The TODO asked for a build-time check to guard against missing new sync fields. Remove it, as code review is sufficient to catch such issues. Signed-off-by: Piotr Zarycki Link: https://patch.msgid.link/20260512161317.2580678-1-piotr.zarycki@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/sync_regs_test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/sync_regs_test.c b/tools/testing/selftests/kvm/x86/sync_regs_test.c index e0c52321f87c..5b0c2359bbb4 100644 --- a/tools/testing/selftests/kvm/x86/sync_regs_test.c +++ b/tools/testing/selftests/kvm/x86/sync_regs_test.c @@ -255,7 +255,6 @@ KVM_ONE_VCPU_TEST(sync_regs_test, req_and_verify_all_valid, guest_code) struct kvm_regs regs; /* Request and verify all valid register sets. */ - /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */ run->kvm_valid_regs = TEST_SYNC_FIELDS; vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); From b67c030f4f6589c574d98de4f3c4ea3efee9b2be Mon Sep 17 00:00:00 2001 From: Piotr Zarycki Date: Tue, 28 Apr 2026 10:30:37 +0200 Subject: [PATCH 0914/5214] KVM: selftests: Fix typo in comment in hyperv_features.c Fix a typo in a comment: 'vailable' -> 'available'. Signed-off-by: Piotr Zarycki Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260428083037.1926902-1-piotr.zarycki@gmail.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/hyperv_features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/hyperv_features.c b/tools/testing/selftests/kvm/x86/hyperv_features.c index 7347f1fe5157..5053a4454811 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_features.c +++ b/tools/testing/selftests/kvm/x86/hyperv_features.c @@ -457,7 +457,7 @@ static void guest_test_msrs_access(void) msr->fault_expected = true; break; case 45: - /* MSR is vailable when CPUID feature bit is set */ + /* MSR is available when CPUID feature bit is set */ if (!has_invtsc) goto next_stage; vcpu_set_cpuid_feature(vcpu, HV_ACCESS_TSC_INVARIANT); From 749fbf2bdcd71f6fdd2524f0f8102e1be38f3254 Mon Sep 17 00:00:00 2001 From: Piotr Zarycki Date: Wed, 22 Apr 2026 15:03:07 +0200 Subject: [PATCH 0915/5214] KVM: selftests: hyperv_tlb_flush: replace NOP loop with udelay() Replace the open-coded NOP loop with udelay() which was added to KVM selftests in commit 6b878cbb87bf ("KVM: selftests: Add guest udelay() utility for x86"). The NOP loop is CPU speed dependent while udelay() provides a deterministic delay regardless of host CPU frequency. Signed-off-by: Piotr Zarycki Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260422130307.1171808-1-piotr.zarycki@gmail.com Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/x86/hyperv_tlb_flush.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c b/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c index 15ee8b7bfc11..b4be9a175379 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c +++ b/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c @@ -141,17 +141,6 @@ static void swap_two_test_pages(gpa_t pte_gva1, gpa_t pte_gva2) *(u64 *)pte_gva2 = tmp; } -/* - * TODO: replace the silly NOP loop with a proper udelay() implementation. - */ -static inline void do_delay(void) -{ - int i; - - for (i = 0; i < 1000000; i++) - asm volatile("nop"); -} - /* * Prepare to test: 'disable' workers by setting the expectation to '0', * clear hypercall input page and then swap two test pages. @@ -169,7 +158,7 @@ static inline void prepare_to_test(struct test_data *data) wmb(); /* Make sure workers have enough time to notice */ - do_delay(); + udelay(100); /* Swap test page mappings */ swap_two_test_pages(data->test_pages_pte[0], data->test_pages_pte[1]); @@ -189,7 +178,7 @@ static inline void post_test(struct test_data *data, u64 exp1, u64 exp2) set_expected_val((void *)data->test_pages, exp2, WORKER_VCPU_ID_2); /* Make sure workers have enough time to test */ - do_delay(); + udelay(100); } #define TESTVAL1 0x0101010101010101 From acf4d11a35d8bb546e205fe05349f60cfefbff76 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Thu, 16 Apr 2026 16:23:23 -0700 Subject: [PATCH 0916/5214] crypto/ccp: hoist kernel part of SNP_PLATFORM_STATUS ...to its own function. This way it can be used when the kernel needs access to the platform status regardless of the INIT state of the firmware. No functional change intended. Cc: Herbert Xu Signed-off-by: Tycho Andersen (AMD) Acked-by: Herbert Xu Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-2-seanjc@google.com Signed-off-by: Sean Christopherson --- drivers/crypto/ccp/sev-dev.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index d1e9e0ac63b6..22bc4ef27a63 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2381,7 +2381,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) return ret; } -static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) +static int __sev_do_snp_platform_status(struct sev_user_data_snp_status *status, + int *error) { struct sev_device *sev = psp_master->sev_data; struct sev_data_snp_addr buf; @@ -2389,9 +2390,6 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) void *data; int ret; - if (!argp->data) - return -EINVAL; - status_page = alloc_page(GFP_KERNEL_ACCOUNT); if (!status_page) return -ENOMEM; @@ -2414,7 +2412,7 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) } buf.address = __psp_pa(data); - ret = __sev_do_cmd_locked(SEV_CMD_SNP_PLATFORM_STATUS, &buf, &argp->error); + ret = __sev_do_cmd_locked(SEV_CMD_SNP_PLATFORM_STATUS, &buf, error); if (sev->snp_initialized) { /* @@ -2429,15 +2427,32 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) if (ret) goto cleanup; - if (copy_to_user((void __user *)argp->data, data, - sizeof(struct sev_user_data_snp_status))) - ret = -EFAULT; + memcpy(status, data, sizeof(*status)); cleanup: __free_pages(status_page, 0); return ret; } +static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) +{ + struct sev_user_data_snp_status status; + int ret; + + if (!argp->data) + return -EINVAL; + + ret = __sev_do_snp_platform_status(&status, &argp->error); + if (ret < 0) + return ret; + + if (copy_to_user((void __user *)argp->data, &status, + sizeof(struct sev_user_data_snp_status))) + ret = -EFAULT; + + return ret; +} + static int sev_ioctl_do_snp_commit(struct sev_issue_cmd *argp) { struct sev_device *sev = psp_master->sev_data; From 4b28f0846ef6521b47ee95ea7d77c9d40a7baf29 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Thu, 16 Apr 2026 16:23:24 -0700 Subject: [PATCH 0917/5214] crypto/ccp: export firmware supported vm types In some configurations, the firmware does not support all VM types. The SEV firmware has an entry in the TCB_VERSION structure referred to as the Security Version Number in the SEV-SNP firmware specification and referred to as the "SPL" in SEV firmware release notes. The SEV firmware release notes say: On every SEV firmware release where a security mitigation has been added, the SNP SPL gets increased by 1. This is to let users know that it is important to update to this version. The SEV firmware release that fixed CVE-2025-48514 by disabling SEV-ES support on vulnerable platforms has this SVN increased to reflect the fix. The SVN is platform-specific, as is the structure of TCB_VERSION. Check CURRENT_TCB instead of REPORTED_TCB, since the firmware behaves with the CURRENT_TCB SVN level and will reject SEV-ES VMs accordingly. Parse the SVN, and mask off the SEV_ES supported VM type from the list of supported types if it is above the per-platform threshold for the relevant platforms. Signed-off-by: Tycho Andersen (AMD) Acked-by: Herbert Xu Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-3-seanjc@google.com Signed-off-by: Sean Christopherson --- drivers/crypto/ccp/sev-dev.c | 70 ++++++++++++++++++++++++++++++++++++ include/linux/psp-sev.h | 37 +++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 22bc4ef27a63..7cd6cd6fdb10 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2954,3 +2954,73 @@ void sev_pci_exit(void) sev_firmware_shutdown(sev); } + +static int get_v1_svn(struct sev_device *sev) +{ + struct sev_snp_tcb_version_genoa_milan *tcb; + struct sev_user_data_snp_status status; + int ret, error = 0; + + mutex_lock(&sev_cmd_mutex); + ret = __sev_do_snp_platform_status(&status, &error); + mutex_unlock(&sev_cmd_mutex); + if (ret < 0) + return ret; + + tcb = (struct sev_snp_tcb_version_genoa_milan *)&status + .current_tcb_version; + return tcb->snp; +} + +static int get_v2_svn(struct sev_device *sev) +{ + struct sev_user_data_snp_status status; + struct sev_snp_tcb_version_turin *tcb; + int ret, error = 0; + + mutex_lock(&sev_cmd_mutex); + ret = __sev_do_snp_platform_status(&status, &error); + mutex_unlock(&sev_cmd_mutex); + if (ret < 0) + return ret; + + tcb = (struct sev_snp_tcb_version_turin *)&status + .current_tcb_version; + return tcb->snp; +} + +static bool sev_firmware_allows_es(struct sev_device *sev) +{ + /* Documented in AMD-SB-3023 */ + if (boot_cpu_has(X86_FEATURE_ZEN4) || boot_cpu_has(X86_FEATURE_ZEN3)) + return get_v1_svn(sev) < 0x1b; + else if (boot_cpu_has(X86_FEATURE_ZEN5)) + return get_v2_svn(sev) < 0x4; + else + return true; +} + +int sev_firmware_supported_vm_types(void) +{ + int supported_vm_types = 0; + struct sev_device *sev; + + if (!psp_master || !psp_master->sev_data) + return supported_vm_types; + sev = psp_master->sev_data; + + supported_vm_types |= BIT(KVM_X86_SEV_VM); + supported_vm_types |= BIT(KVM_X86_SEV_ES_VM); + + if (!sev->snp_initialized) + return supported_vm_types; + + supported_vm_types |= BIT(KVM_X86_SNP_VM); + + if (!sev_firmware_allows_es(sev)) + supported_vm_types &= ~BIT(KVM_X86_SEV_ES_VM); + + return supported_vm_types; + +} +EXPORT_SYMBOL_FOR_MODULES(sev_firmware_supported_vm_types, "kvm-amd"); diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index d5099a2baca5..ce16bbc0b308 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -902,6 +902,42 @@ struct snp_feature_info { /* Feature bits in EBX */ #define SNP_SEV_TIO_SUPPORTED BIT(1) +/** + * struct sev_snp_tcb_version_genoa_milan + * + * @boot_loader: SVN of PSP bootloader + * @tee: SVN of PSP operating system + * @reserved: reserved + * @snp: SVN of SNP firmware + * @microcode: Lowest current patch level of all cores + */ +struct sev_snp_tcb_version_genoa_milan { + u8 boot_loader; + u8 tee; + u8 reserved[4]; + u8 snp; + u8 microcode; +}; + +/** + * struct sev_snp_tcb_version_turin + * + * @fmc: SVN of FMC firmware + * @boot_loader: SVN of PSP bootloader + * @tee: SVN of PSP operating system + * @snp: SVN of SNP firmware + * @reserved: reserved + * @microcode: Lowest current patch level of all cores + */ +struct sev_snp_tcb_version_turin { + u8 fmc; + u8 boot_loader; + u8 tee; + u8 snp; + u8 reserved[3]; + u8 microcode; +}; + #ifdef CONFIG_CRYPTO_DEV_SP_PSP /** @@ -1048,6 +1084,7 @@ void snp_free_firmware_page(void *addr); void sev_platform_shutdown(void); bool sev_is_snp_ciphertext_hiding_supported(void); u64 sev_get_snp_policy_bits(void); +int sev_firmware_supported_vm_types(void); #else /* !CONFIG_CRYPTO_DEV_SP_PSP */ From c2a02db765af244a9a221aac386245ed4513fb81 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 16 Apr 2026 16:23:25 -0700 Subject: [PATCH 0918/5214] KVM: SEV: Set supported SEV+ VM types during sev_hardware_setup() Set the supported SEV+ VM types during sev_hardware_setup() instead of waiting until sev_set_cpu_caps(). This will using the set of *fully* supported VM types to print the enabled/unusable/disabled messaged. For all intents and purposes, no functional change intended. Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 940b97d4a852..e5e7d6a99220 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3013,18 +3013,14 @@ void sev_vm_destroy(struct kvm *kvm) void __init sev_set_cpu_caps(void) { - if (sev_enabled) { + if (sev_enabled) kvm_cpu_cap_set(X86_FEATURE_SEV); - kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_VM); - } - if (sev_es_enabled) { + + if (sev_es_enabled) kvm_cpu_cap_set(X86_FEATURE_SEV_ES); - kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_ES_VM); - } - if (sev_snp_enabled) { + + if (sev_snp_enabled) kvm_cpu_cap_set(X86_FEATURE_SEV_SNP); - kvm_caps.supported_vm_types |= BIT(KVM_X86_SNP_VM); - } } static bool is_sev_snp_initialized(void) @@ -3194,6 +3190,13 @@ void __init sev_hardware_setup(void) } } + if (sev_supported) + kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_VM); + if (sev_es_supported) + kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_ES_VM); + if (sev_snp_supported) + kvm_caps.supported_vm_types |= BIT(KVM_X86_SNP_VM); + if (boot_cpu_has(X86_FEATURE_SEV)) pr_info("SEV %s (ASIDs %u - %u)\n", sev_supported ? min_sev_asid <= max_sev_asid ? "enabled" : From 82bf8282444c03a8a61b718b3ba3582b0481e970 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 16 Apr 2026 16:23:26 -0700 Subject: [PATCH 0919/5214] KVM: SEV: Consolidate logic for printing state of SEV{,-ES,-SNP} enabling Add a helper to print enabled/unusable/disabled for SEV+ VM types in anticipation of SNP also being subjecting to "unusable" logic. No functional change intended. Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index e5e7d6a99220..c4bbe49bb8c1 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3050,6 +3050,11 @@ static bool is_sev_snp_initialized(void) return initialized; } +static const char * __init sev_str_feature_state(bool is_supported, bool is_usable) +{ + return is_supported ? is_usable ? "enabled" : "unusable" : "disabled"; +} + void __init sev_hardware_setup(void) { unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count; @@ -3199,19 +3204,15 @@ void __init sev_hardware_setup(void) if (boot_cpu_has(X86_FEATURE_SEV)) pr_info("SEV %s (ASIDs %u - %u)\n", - sev_supported ? min_sev_asid <= max_sev_asid ? "enabled" : - "unusable" : - "disabled", + sev_str_feature_state(sev_supported, min_sev_asid <= max_sev_asid), min_sev_asid, max_sev_asid); if (boot_cpu_has(X86_FEATURE_SEV_ES)) pr_info("SEV-ES %s (ASIDs %u - %u)\n", - sev_es_supported ? min_sev_es_asid <= max_sev_es_asid ? "enabled" : - "unusable" : - "disabled", + sev_str_feature_state(sev_es_supported, min_sev_es_asid <= max_sev_es_asid), min_sev_es_asid, max_sev_es_asid); if (boot_cpu_has(X86_FEATURE_SEV_SNP)) pr_info("SEV-SNP %s (ASIDs %u - %u)\n", - str_enabled_disabled(sev_snp_supported), + sev_str_feature_state(sev_snp_supported, true), min_snp_asid, max_snp_asid); sev_enabled = sev_supported; From 93d1a486e1d4f05db65b36db5fe2bca3d0257bb0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 16 Apr 2026 16:23:27 -0700 Subject: [PATCH 0920/5214] KVM: SEV: Don't advertise support for unusable VM types Commit 0aa6b90ef9d7 ("KVM: SVM: Add support for allowing zero SEV ASIDs") made it possible to make it impossible to use SEV VMs by not allocating them any ASIDs. Commit 6c7c620585c6 ("KVM: SEV: Add SEV-SNP CipherTextHiding support") did the same thing for SEV-ES. Do not export KVM_X86_SEV(_ES)_VM as supported types if in either of these situations, so that userspace can use them to determine what is actually supported by the current kernel configuration. Also move the buildup to a local variable so it is easier to add additional masking in future patches. Link: https://lore.kernel.org/all/aZyLIWtffvEnmtYh@google.com/ Suggested-by: Sean Christopherson Signed-off-by: Tycho Andersen (AMD) [sean: land code in sev_hardware_setup()] Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-6-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index c4bbe49bb8c1..105d95034cae 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3062,6 +3062,7 @@ void __init sev_hardware_setup(void) bool sev_snp_supported = false; bool sev_es_supported = false; bool sev_supported = false; + u32 vm_types = 0; if (!sev_enabled || !npt_enabled || !nrips) goto out; @@ -3195,24 +3196,26 @@ void __init sev_hardware_setup(void) } } - if (sev_supported) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_VM); - if (sev_es_supported) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SEV_ES_VM); + if (sev_supported && min_sev_asid <= max_sev_asid) + vm_types |= BIT(KVM_X86_SEV_VM); + if (sev_es_supported && min_sev_es_asid <= max_sev_es_asid) + vm_types |= BIT(KVM_X86_SEV_ES_VM); if (sev_snp_supported) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SNP_VM); + vm_types |= BIT(KVM_X86_SNP_VM); + + kvm_caps.supported_vm_types |= vm_types; if (boot_cpu_has(X86_FEATURE_SEV)) pr_info("SEV %s (ASIDs %u - %u)\n", - sev_str_feature_state(sev_supported, min_sev_asid <= max_sev_asid), + sev_str_feature_state(sev_supported, vm_types & BIT(KVM_X86_SEV_VM)), min_sev_asid, max_sev_asid); if (boot_cpu_has(X86_FEATURE_SEV_ES)) pr_info("SEV-ES %s (ASIDs %u - %u)\n", - sev_str_feature_state(sev_es_supported, min_sev_es_asid <= max_sev_es_asid), + sev_str_feature_state(sev_es_supported, vm_types & BIT(KVM_X86_SEV_ES_VM)), min_sev_es_asid, max_sev_es_asid); if (boot_cpu_has(X86_FEATURE_SEV_SNP)) pr_info("SEV-SNP %s (ASIDs %u - %u)\n", - sev_str_feature_state(sev_snp_supported, true), + sev_str_feature_state(sev_snp_supported, vm_types & BIT(KVM_X86_SNP_VM)), min_snp_asid, max_snp_asid); sev_enabled = sev_supported; From d8355a92df1f016bcb2fdb0cc9fc7bd13b6588dc Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Thu, 16 Apr 2026 16:23:28 -0700 Subject: [PATCH 0921/5214] KVM: SEV: Don't advertise VM types that are disabled by firmware As called out in a footnote for a recent SNP vulnerability[1], it is possible for a specific flavor of SEV+ to be disabled by the firmware even when the flavor is fully supported by the CPU and platform: Applying mitigation CVE-2025-48514 will result in disabling SEV-ES when SEV-SNP is enabled. Restrict KVM's set of supported VM types based on the VM types that are fully supported by firmware to avoid over-reporting what KVM can actually support. Like KVM's handling of ASID space exhaustion, don't modify KVM's CPUID capabilities, as the CPU/platform still supports the underlying technology and clearing e.g. SEV_ES while advertising SEV_SNP would confuse KVM and userspace. Link: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-3023.html [1] Link: https://lore.kernel.org/all/aZyLIWtffvEnmtYh@google.com Suggested-by: Sean Christopherson Signed-off-by: Tycho Andersen (AMD) [sean: rewrite changelog to provide details on why/how this can happen] Reviewed-by: Tom Lendacky Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-7-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 105d95034cae..145d0c54d955 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3202,6 +3202,7 @@ void __init sev_hardware_setup(void) vm_types |= BIT(KVM_X86_SEV_ES_VM); if (sev_snp_supported) vm_types |= BIT(KVM_X86_SNP_VM); + vm_types &= sev_firmware_supported_vm_types(); kvm_caps.supported_vm_types |= vm_types; From accb7f3a63846e6685dc7cdce0307c645515db98 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Thu, 16 Apr 2026 16:23:29 -0700 Subject: [PATCH 0922/5214] KVM: selftests: Teach sev_*_test about revoking VM types Instead of using CPUID, use the VM type bit to determine support, since those now reflect the correct status of support by the kernel and firmware configurations. Suggested-by: Sean Christopherson Signed-off-by: Tycho Andersen (AMD) Tested-by: Tycho Andersen (AMD) Link: https://patch.msgid.link/20260416232329.3408497-8-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/sev_init2_tests.c | 14 ++++++-------- .../testing/selftests/kvm/x86/sev_migrate_tests.c | 2 +- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c index 8eeba2327c7c..8db88c355f16 100644 --- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c @@ -136,16 +136,14 @@ int main(int argc, char *argv[]) kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM); TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)); - have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES); + have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); - TEST_ASSERT(have_sev_es == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)), - "sev-es: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)", - kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_ES_VM); + TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES), + "sev-es: SEV_ES_VM supported without SEV_ES in CPUID"); - have_snp = kvm_cpu_has(X86_FEATURE_SEV_SNP); - TEST_ASSERT(have_snp == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM)), - "sev-snp: KVM_CAP_VM_TYPES (%x) indicates SNP support (bit %d), but CPUID does not", - kvm_check_cap(KVM_CAP_VM_TYPES), KVM_X86_SNP_VM); + have_snp = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM); + TEST_ASSERT(!have_snp || kvm_cpu_has(X86_FEATURE_SEV_SNP), + "sev-snp: SNP_VM supported without SEV_SNP in CPUID"); test_vm_types(); diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c index 6b0928e69051..42bc023d5193 100644 --- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c @@ -374,7 +374,7 @@ int main(int argc, char *argv[]) TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); - have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES); + have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); if (kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)) { test_sev_migrate_from(/* es= */ false); diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index 1a49ee391586..6b2cbe2a90b7 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -249,10 +249,10 @@ int main(int argc, char *argv[]) test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); - if (kvm_cpu_has(X86_FEATURE_SEV_ES)) + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)) test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES); - if (kvm_cpu_has(X86_FEATURE_SEV_SNP)) + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM)) test_sev_smoke(guest_snp_code, KVM_X86_SNP_VM, snp_default_policy()); return 0; From 78ee2d50185a037b3d2452a97f3dad69c3f7f389 Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Fri, 1 May 2026 13:35:32 -0700 Subject: [PATCH 0923/5214] KVM: SVM: Fix page overflow in sev_dbg_crypt() for ENCRYPT path In sev_dbg_crypt(), the per-iteration transfer length is bounded by the source page offset (PAGE_SIZE - s_off) but not by the destination page offset (PAGE_SIZE - d_off). When d_off > s_off, the encrypt path (__sev_dbg_encrypt_user) performs a read-modify-write using a single-page intermediate buffer (dst_tpage): 1. __sev_dbg_decrypt() expands the size to round_up(len + (d_off & 15), 16) before issuing the PSP command. If len + (d_off & 15) > PAGE_SIZE, the PSP writes beyond the end of the 4096-byte dst_tpage allocation. 2. The subsequent memcpy()/copy_from_user() into page_address(dst_tpage) + (d_off & 15) of 'len' bytes overflows by up to 15 bytes under the same condition. Trigger example: s_off = 0, d_off = 1, debug.len = PAGE_SIZE - the PSP is instructed to write round_up(4097, 16) = 4112 bytes to a 4096-byte buffer. Fix by also bounding len by (PAGE_SIZE - d_off), the same check that sev_send_update_data() already performs for its single-page guest region. ================================================================== BUG: KASAN: slab-use-after-free in sev_dbg_crypt+0x993/0xd10 [kvm_amd] Write of size 4095 at addr ff110062293bb009 by task sev_dbg_test/228214 CPU: 96 UID: 0 PID: 228214 Comm: sev_dbg_test Tainted: G U W 7.0.0-smp--5ce9b0c48211-dbg #156 PREEMPTLAZY Tainted: [U]=USER, [W]=WARN Hardware name: Google Astoria/astoria, BIOS 0.20250817.1-0 08/25/2025 Call Trace: dump_stack_lvl+0x54/0x70 print_report+0xbc/0x260 kasan_report+0xa2/0xd0 kasan_check_range+0x25f/0x2c0 __asan_memcpy+0x40/0x70 sev_dbg_crypt+0x993/0xd10 [kvm_amd] sev_mem_enc_ioctl+0x33c/0x450 [kvm_amd] kvm_vm_ioctl+0x65d/0x6d0 [kvm] __se_sys_ioctl+0xb2/0x100 do_syscall_64+0xe8/0x870 entry_SYSCALL_64_after_hwframe+0x4b/0x53 The buggy address belongs to the physical page: page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x7fe72b6a0 pfn:0x62293bb memcg:ff11000112827d82 flags: 0x1400000000000000(node=1|zone=1) raw: 1400000000000000 0000000000000000 dead000000000122 0000000000000000 raw: 00000007fe72b6a0 0000000000000000 00000001ffffffff ff11000112827d82 page dumped because: kasan: bad access detected Memory state around the buggy address: ff110062293bbf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff110062293bbf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ff110062293bc000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ ff110062293bc080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ff110062293bc100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ================================================================== Disabling lock debugging due to kernel taint Fixes: 24f41fb23a39 ("KVM: SVM: Add support for SEV DEBUG_DECRYPT command") Fixes: 7d1594f5d94b ("KVM: SVM: Add support for SEV DEBUG_ENCRYPT command") Cc: stable@vger.kernel.org Signed-off-by: Ashutosh Desai [sean: add sample KASAN splat, Fixes, and stable@] Link: https://patch.msgid.link/20260501203537.2120074-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 145d0c54d955..00d3bab091d2 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1396,6 +1396,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) s_off = vaddr & ~PAGE_MASK; d_off = dst_vaddr & ~PAGE_MASK; len = min_t(size_t, (PAGE_SIZE - s_off), size); + len = min_t(size_t, len, PAGE_SIZE - d_off); if (dec) ret = __sev_dbg_decrypt_user(kvm, From ccd6c77223bbdea352190956f5e00e3b07119fc3 Mon Sep 17 00:00:00 2001 From: Peter Fang Date: Tue, 7 Apr 2026 17:11:28 -0700 Subject: [PATCH 0924/5214] KVM: Fix kvm_vcpu_map[_readonly]() function prototypes kvm_vcpu_map() and kvm_vcpu_map_readonly() should take a gfn instead of a gpa. This appears to be a result of the original kvm_vcpu_map() being declared with the wrong function prototype in kvm_host.h, even though it was correct in the actual implementation in kvm_main.c. No actual harm has been done yet as all of the call sites are correctly passing in a gfn. Plus, both gfn_t and gpa_t are typedef'd to u64 so this change shouldn't have any functional impact. Compile-tested on x86 and ppc, which are the current users of these interfaces. Fixes: e45adf665a53 ("KVM: Introduce a new guest mapping API") Cc: KarimAllah Ahmed Cc: Konrad Rzeszutek Wilk Signed-off-by: Peter Fang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260408001137.3290444-2-peter.fang@intel.com Signed-off-by: Sean Christopherson --- include/linux/kvm_host.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 7b231a1e63ba..61a3430957f2 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1390,20 +1390,20 @@ void mark_page_dirty_in_slot(struct kvm *kvm, const struct kvm_memory_slot *mems void mark_page_dirty(struct kvm *kvm, gfn_t gfn); void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn); -int __kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_host_map *map, +int __kvm_vcpu_map(struct kvm_vcpu *vcpu, gfn_t gfn, struct kvm_host_map *map, bool writable); void kvm_vcpu_unmap(struct kvm_vcpu *vcpu, struct kvm_host_map *map); -static inline int kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, +static inline int kvm_vcpu_map(struct kvm_vcpu *vcpu, gfn_t gfn, struct kvm_host_map *map) { - return __kvm_vcpu_map(vcpu, gpa, map, true); + return __kvm_vcpu_map(vcpu, gfn, map, true); } -static inline int kvm_vcpu_map_readonly(struct kvm_vcpu *vcpu, gpa_t gpa, +static inline int kvm_vcpu_map_readonly(struct kvm_vcpu *vcpu, gfn_t gfn, struct kvm_host_map *map) { - return __kvm_vcpu_map(vcpu, gpa, map, false); + return __kvm_vcpu_map(vcpu, gfn, map, false); } static inline void kvm_vcpu_map_mark_dirty(struct kvm_vcpu *vcpu, From 923ca078f08cbdf526d1e8df9bbb824b14c8ed9d Mon Sep 17 00:00:00 2001 From: Ethan Yang Date: Mon, 6 Apr 2026 15:53:56 -0700 Subject: [PATCH 0925/5214] KVM: x86: Don't leave APF half-enabled on bad APF data GPA kvm_pv_enable_async_pf() updates vcpu->arch.apf.msr_en_val before initializing the APF data gfn_to_hva cache. If userspace provides an invalid GPA, kvm_gfn_to_hva_cache_init() fails, but msr_en_val stays enabled and leaves APF state half-initialized. Later APF paths can then try to use the empty cache and trigger WARN_ON() in kvm_read_guest_offset_cached(). Determine the new APF enabled state from the incoming MSR value, do cache initialization first on the enable path, and commit msr_en_val only after successful initialization. Keep the disable path behavior unchanged. Reported-by: syzbot+bc0e18379a290e5edfe4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=bc0e18379a290e5edfe4 Fixes: 344d9588a9df ("KVM: Add PV MSR to enable asynchronous page faults delivery.") Link: https://lore.kernel.org/r/aHfD3MczrDpzDX9O@google.com Suggested-by: Sean Christopherson Reviewed-by: Xiaoyao Li Signed-off-by: Ethan Yang [sean: don't bother with a local "enable" variable] Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260406225359.1245490-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 810ff08780d1..82dce54ac505 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1043,11 +1043,16 @@ bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_require_dr); -static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) +static bool __kvm_pv_async_pf_enabled(u64 data) { u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT; - return (vcpu->arch.apf.msr_en_val & mask) == mask; + return (data & mask) == mask; +} + +static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) +{ + return __kvm_pv_async_pf_enabled(vcpu->arch.apf.msr_en_val); } static inline u64 pdptr_rsvd_bits(struct kvm_vcpu *vcpu) @@ -3648,18 +3653,19 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) if (!lapic_in_kernel(vcpu)) return data ? 1 : 0; + if (__kvm_pv_async_pf_enabled(data) && + kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, + sizeof(u64))) + return 1; + vcpu->arch.apf.msr_en_val = data; - if (!kvm_pv_async_pf_enabled(vcpu)) { + if (!__kvm_pv_async_pf_enabled(data)) { kvm_clear_async_pf_completion_queue(vcpu); kvm_async_pf_hash_reset(vcpu); return 0; } - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.apf.data, gpa, - sizeof(u64))) - return 1; - vcpu->arch.apf.send_always = (data & KVM_ASYNC_PF_SEND_ALWAYS); vcpu->arch.apf.delivery_as_pf_vmexit = data & KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT; From 3c0bf11dd41ffd4206c8ba76d485379e307080e0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 6 Apr 2026 15:53:57 -0700 Subject: [PATCH 0926/5214] KVM: x86: Drop superfluous caching of KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT Drop kvm_vcpu_arch.apf.delivery_as_pf_vmexit and instead use msr_en_val as the source of truth to reduce the probability of operating on stale data. This fixes flaws where KVM fails to update delivery_as_pf_vmexit when APF is explicitly disabled by the guest or implicitly disabled by KVM on INIT. Absent other bugs, the flaws are benign as KVM *shouldn't* consume delivery_as_pf_vmexit when PV APF support is disabled. Simply delete the field, as there's zero benefit to maintaining a separate "cache" of the state. Fixes: 52a5c155cf79 ("KVM: async_pf: Let guest support delivery of async_pf from guest mode") Reviewed-by: Binbin Wu Reviewed-by: Xiaoyao Li Link: https://patch.msgid.link/20260406225359.1245490-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/x86.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8a53ca619570..5644dc9f08a4 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1058,7 +1058,6 @@ struct kvm_vcpu_arch { u32 id; u32 host_apf_flags; bool send_always; - bool delivery_as_pf_vmexit; bool pageready_pending; } apf; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 82dce54ac505..4bffcea3ede9 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3667,7 +3667,6 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) } vcpu->arch.apf.send_always = (data & KVM_ASYNC_PF_SEND_ALWAYS); - vcpu->arch.apf.delivery_as_pf_vmexit = data & KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT; kvm_async_pf_wakeup_all(vcpu); @@ -14018,7 +14017,7 @@ static bool kvm_can_deliver_async_pf(struct kvm_vcpu *vcpu) * L1 needs to opt into the special #PF vmexits that are * used to deliver async page faults. */ - return vcpu->arch.apf.delivery_as_pf_vmexit; + return vcpu->arch.apf.msr_en_val & KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT; } else { /* * Play it safe in case the guest temporarily disables paging. From 7e985021ef2f6f60bc2fe126978d81a7efa7aeff Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 6 Apr 2026 15:53:58 -0700 Subject: [PATCH 0927/5214] KVM: x86: Drop superfluous caching of KVM_ASYNC_PF_SEND_ALWAYS Drop kvm_vcpu_arch.apf.send_always and instead use msr_en_val as the source of truth to reduce the probability of operating on stale data. This fixes flaws where KVM fails to update send_always when APF is explicitly disabled by the guest or implicitly disabled by KVM on INIT. Absent other bugs, the flaws are benign as KVM *shouldn't* consume send_always when PV APF support is disabled. Simply delete the field, as there's zero benefit to maintaining a separate "cache" of the state. Opportunistically turn the enabled vs. disabled logic at the end of kvm_pv_enable_async_pf() into an if-else instead of using an early return, e.g. so that it's more obvious that both paths are "success" paths. Fixes: 6adba5274206 ("KVM: Let host know whether the guest can handle async PF in non-userspace context.") Reviewed-by: Binbin Wu Reviewed-by: Xiaoyao Li Link: https://patch.msgid.link/20260406225359.1245490-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/x86.c | 12 ++++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5644dc9f08a4..2b986a733cd6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1057,7 +1057,6 @@ struct kvm_vcpu_arch { u16 vec; u32 id; u32 host_apf_flags; - bool send_always; bool pageready_pending; } apf; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4bffcea3ede9..b01f9a4d3363 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3660,16 +3660,12 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) vcpu->arch.apf.msr_en_val = data; - if (!__kvm_pv_async_pf_enabled(data)) { + if (__kvm_pv_async_pf_enabled(data)) { + kvm_async_pf_wakeup_all(vcpu); + } else { kvm_clear_async_pf_completion_queue(vcpu); kvm_async_pf_hash_reset(vcpu); - return 0; } - - vcpu->arch.apf.send_always = (data & KVM_ASYNC_PF_SEND_ALWAYS); - - kvm_async_pf_wakeup_all(vcpu); - return 0; } @@ -14008,7 +14004,7 @@ static bool kvm_can_deliver_async_pf(struct kvm_vcpu *vcpu) if (!kvm_pv_async_pf_enabled(vcpu)) return false; - if (!vcpu->arch.apf.send_always && + if (!(vcpu->arch.apf.msr_en_val & KVM_ASYNC_PF_SEND_ALWAYS) && (vcpu->arch.guest_state_protected || !kvm_x86_call(get_cpl)(vcpu))) return false; From 41d37c42e0c637c88e02fc0bf6a7884e9719cc6a Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Tue, 7 Apr 2026 20:19:12 +0530 Subject: [PATCH 0928/5214] KVM: selftests: memslot_perf_test: make host wait timeout configurable When memslot_perf_test is run on the Qemu Risc-V Virt machine, sometimes the RW subtest fails due to sigalarm, indicating that the guest sync did not finish within the expected duration of 10 seconds. Since the current timeout value is itself a bump up from the original 2s, making the host timeout value configurable via a new command line parameter. The test can be invoked with '-t' option to set a suitable timeout value for the host. Signed-off-by: Mayuresh Chitale Link: https://patch.msgid.link/20260407144914.2621843-1-mayuresh.chitale@oss.qualcomm.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/memslot_perf_test.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index 3d02db371422..53f63487790f 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -111,6 +111,7 @@ struct sync_area { */ static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic bool is not lockless"); +static int wait_timeout = 10; static sem_t vcpu_ready; static bool map_unmap_verify; @@ -418,7 +419,7 @@ static bool _guest_should_exit(void) */ static noinline void host_perform_sync(struct sync_area *sync) { - alarm(10); + alarm(wait_timeout); atomic_store_explicit(&sync->sync_flag, true, memory_order_release); while (atomic_load_explicit(&sync->sync_flag, memory_order_acquire)) @@ -900,7 +901,7 @@ static void help(char *name, struct test_args *targs) { int ctr; - pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count]\n", + pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count] [-t wait_timeout]\n", name); pr_info(" -h: print this help screen.\n"); pr_info(" -v: enable verbose mode (not for benchmarking).\n"); @@ -916,6 +917,8 @@ static void help(char *name, struct test_args *targs) targs->seconds); pr_info(" -r: specify the number of runs per test (currently: %i)\n", targs->runs); + pr_info(" -t: specify the number of seconds for host wait timeout (currently: %i)\n", + wait_timeout); pr_info("\nAvailable tests:\n"); for (ctr = 0; ctr < NTESTS; ctr++) @@ -964,7 +967,7 @@ static bool parse_args(int argc, char *argv[], u32 max_mem_slots; int opt; - while ((opt = getopt(argc, argv, "hvdqs:f:e:l:r:")) != -1) { + while ((opt = getopt(argc, argv, "hvdqs:f:e:l:r:t:")) != -1) { switch (opt) { case 'h': default: @@ -1007,6 +1010,9 @@ static bool parse_args(int argc, char *argv[], case 'r': targs->runs = atoi_positive("Runs per test", optarg); break; + case 't': + wait_timeout = atoi_positive("Host wait timeout", optarg); + break; } } From 9a7ff263922e7163d0def5d35948e6b6545449f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 13 Mar 2026 13:20:39 +0100 Subject: [PATCH 0929/5214] KVM: VFIO: clean up control flow in kvm_vfio_file_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The struct file that this function fgets() is always passed to fput() before returning, so use automatic cleanup via __free() to avoid several jumps to the end of the function. Similarly, use a mutex guard to completely remove the need to use gotos. Signed-off-by: Carlos López Reviewed-by: Alex Williamson Link: https://patch.msgid.link/20260313122040.1413091-4-clopez@suse.de Signed-off-by: Sean Christopherson --- virt/kvm/vfio.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 9f9acb66cc1e..2c91bad3333b 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -144,33 +144,26 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd) { struct kvm_vfio *kv = dev->private; struct kvm_vfio_file *kvf; - struct file *filp; - int ret = 0; + struct file *filp __free(fput) = NULL; filp = fget(fd); if (!filp) return -EBADF; /* Ensure the FD is a vfio FD. */ - if (!kvm_vfio_file_is_valid(filp)) { - ret = -EINVAL; - goto out_fput; - } + if (!kvm_vfio_file_is_valid(filp)) + return -EINVAL; - mutex_lock(&kv->lock); + guard(mutex)(&kv->lock); list_for_each_entry(kvf, &kv->file_list, node) { - if (kvf->file == filp) { - ret = -EEXIST; - goto out_unlock; - } + if (kvf->file == filp) + return -EEXIST; } kvf = kzalloc_obj(*kvf, GFP_KERNEL_ACCOUNT); - if (!kvf) { - ret = -ENOMEM; - goto out_unlock; - } + if (!kvf) + return -ENOMEM; kvf->file = get_file(filp); list_add_tail(&kvf->node, &kv->file_list); @@ -178,11 +171,7 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd) kvm_vfio_file_set_kvm(kvf->file, dev->kvm); kvm_vfio_update_coherency(dev); -out_unlock: - mutex_unlock(&kv->lock); -out_fput: - fput(filp); - return ret; + return 0; } static int kvm_vfio_file_del(struct kvm_device *dev, unsigned int fd) From 40f0f4bb6041b1f520445a4c5f0c8777f7c98f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 13 Mar 2026 13:20:40 +0100 Subject: [PATCH 0930/5214] KVM: VFIO: use mutex guard in kvm_vfio_file_set_spapr_tce() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a mutex guard to hold a lock for the entirety of the function, which removes the need for a goto (whose label even has a misleading name since 8152f8201088 ("fdget(), more trivial conversions")) Signed-off-by: Carlos López Reviewed-by: Alex Williamson Link: https://patch.msgid.link/20260313122040.1413091-5-clopez@suse.de Signed-off-by: Sean Christopherson --- virt/kvm/vfio.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 2c91bad3333b..e0d621567dbf 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -216,7 +216,6 @@ static int kvm_vfio_file_set_spapr_tce(struct kvm_device *dev, struct kvm_vfio_spapr_tce param; struct kvm_vfio *kv = dev->private; struct kvm_vfio_file *kvf; - int ret; if (copy_from_user(¶m, arg, sizeof(struct kvm_vfio_spapr_tce))) return -EFAULT; @@ -225,9 +224,7 @@ static int kvm_vfio_file_set_spapr_tce(struct kvm_device *dev, if (fd_empty(f)) return -EBADF; - ret = -ENOENT; - - mutex_lock(&kv->lock); + guard(mutex)(&kv->lock); list_for_each_entry(kvf, &kv->file_list, node) { if (kvf->file != fd_file(f)) @@ -235,20 +232,15 @@ static int kvm_vfio_file_set_spapr_tce(struct kvm_device *dev, if (!kvf->iommu_group) { kvf->iommu_group = kvm_vfio_file_iommu_group(kvf->file); - if (WARN_ON_ONCE(!kvf->iommu_group)) { - ret = -EIO; - goto err_fdput; - } + if (WARN_ON_ONCE(!kvf->iommu_group)) + return -EIO; } - ret = kvm_spapr_tce_attach_iommu_group(dev->kvm, param.tablefd, - kvf->iommu_group); - break; + return kvm_spapr_tce_attach_iommu_group(dev->kvm, param.tablefd, + kvf->iommu_group); } -err_fdput: - mutex_unlock(&kv->lock); - return ret; + return -ENOENT; } #endif From 3b20a19c592d31d90594565de89f974135b22819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 13 Mar 2026 13:20:41 +0100 Subject: [PATCH 0931/5214] KVM: VFIO: deduplicate file release logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two callsites which destroy files in kv->file_list: the function servicing KVM_DEV_VFIO_FILE_DEL, and the relase of the whole KVM VFIO device. The process involves several steps, so move all those into a single function, removing duplicate code. Signed-off-by: Carlos López Reviewed-by: Alex Williamson Link: https://patch.msgid.link/20260313122040.1413091-6-clopez@suse.de Signed-off-by: Sean Christopherson --- virt/kvm/vfio.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index e0d621567dbf..2fee1e82f8f6 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -174,6 +174,17 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd) return 0; } +static void kvm_vfio_file_free(struct kvm_device *dev, struct kvm_vfio_file *kvf) +{ +#ifdef CONFIG_SPAPR_TCE_IOMMU + kvm_spapr_tce_release_vfio_group(dev->kvm, kvf); +#endif + kvm_vfio_file_set_kvm(kvf->file, NULL); + fput(kvf->file); + list_del(&kvf->node); + kfree(kvf); +} + static int kvm_vfio_file_del(struct kvm_device *dev, unsigned int fd) { struct kvm_vfio *kv = dev->private; @@ -189,18 +200,11 @@ static int kvm_vfio_file_del(struct kvm_device *dev, unsigned int fd) mutex_lock(&kv->lock); list_for_each_entry(kvf, &kv->file_list, node) { - if (kvf->file != fd_file(f)) - continue; - - list_del(&kvf->node); -#ifdef CONFIG_SPAPR_TCE_IOMMU - kvm_spapr_tce_release_vfio_group(dev->kvm, kvf); -#endif - kvm_vfio_file_set_kvm(kvf->file, NULL); - fput(kvf->file); - kfree(kvf); - ret = 0; - break; + if (kvf->file == fd_file(f)) { + kvm_vfio_file_free(dev, kvf); + ret = 0; + break; + } } kvm_vfio_update_coherency(dev); @@ -307,15 +311,8 @@ static void kvm_vfio_release(struct kvm_device *dev) struct kvm_vfio *kv = dev->private; struct kvm_vfio_file *kvf, *tmp; - list_for_each_entry_safe(kvf, tmp, &kv->file_list, node) { -#ifdef CONFIG_SPAPR_TCE_IOMMU - kvm_spapr_tce_release_vfio_group(dev->kvm, kvf); -#endif - kvm_vfio_file_set_kvm(kvf->file, NULL); - fput(kvf->file); - list_del(&kvf->node); - kfree(kvf); - } + list_for_each_entry_safe(kvf, tmp, &kv->file_list, node) + kvm_vfio_file_free(dev, kvf); kvm_vfio_update_coherency(dev); From 156615264fd3430e7f90b36f46076b2f6ec4a24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 13 Mar 2026 13:20:42 +0100 Subject: [PATCH 0932/5214] KVM: VFIO: update coherency only if file was deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When servicing a KVM_DEV_VFIO_FILE_DEL request, if a file is removed from kv->file_list, kv->noncoherent needs to be updated, in case we can revert to using coherent DMA. However, if we found no candidate to remove, there is no need to re-scan the list, so do it only if a matching file was found. To simplify the control flow, use a mutex guard so that we can return early from within the search loop if the maching file is found. Signed-off-by: Carlos López Reviewed-by: Alex Williamson Link: https://patch.msgid.link/20260313122040.1413091-7-clopez@suse.de Signed-off-by: Sean Christopherson --- virt/kvm/vfio.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index 2fee1e82f8f6..6cdc4e9a333a 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -190,27 +190,21 @@ static int kvm_vfio_file_del(struct kvm_device *dev, unsigned int fd) struct kvm_vfio *kv = dev->private; struct kvm_vfio_file *kvf; CLASS(fd, f)(fd); - int ret; if (fd_empty(f)) return -EBADF; - ret = -ENOENT; - - mutex_lock(&kv->lock); + guard(mutex)(&kv->lock); list_for_each_entry(kvf, &kv->file_list, node) { if (kvf->file == fd_file(f)) { kvm_vfio_file_free(dev, kvf); - ret = 0; - break; + kvm_vfio_update_coherency(dev); + return 0; } } - kvm_vfio_update_coherency(dev); - - mutex_unlock(&kv->lock); - return ret; + return -ENOENT; } #ifdef CONFIG_SPAPR_TCE_IOMMU From 4487492b92a4f3df4166ab27a277b3d34867e5f2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 28 Jan 2026 17:14:33 -0800 Subject: [PATCH 0933/5214] x86/tdx: Use pg_level in TDX APIs, not the TDX-Module's 0-based level Rework the TDX APIs to take the kernel's 1-based pg_level enum, not the TDX-Module's 0-based level. The APIs are _kernel_ APIs, not TDX-Module APIs, and the kernel (and KVM) uses "enum pg_level" literally everywhere. Using "enum pg_level" eliminates ambiguity when looking at the APIs (it's NOT clear that "int level" refers to the TDX-Module's level), and will allow for using existing helpers like page_level_size() when support for hugepages is added to the S-EPT APIs. No functional change intended. Cc: Kai Huang Cc: Dave Hansen Cc: Rick Edgecombe Cc: Yan Zhao Cc: Vishal Annapurve Cc: Ackerley Tng Acked-by: Kiryl Shutsemau Reviewed-by: Kai Huang Tested-by: Kai Huang Reviewed-by: Rick Edgecombe Tested-by: Rick Edgecombe Acked-by: Dave Hansen Link: https://patch.msgid.link/20260129011517.3545883-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/tdx.h | 14 ++++---------- arch/x86/kvm/vmx/tdx.c | 11 ++++------- arch/x86/virt/vmx/tdx/tdx.c | 26 ++++++++++++++++++-------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index a149740b24e8..c140ddde59ff 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -187,19 +187,13 @@ static inline u64 mk_keyed_paddr(u16 hkid, struct page *page) return ret; } -static inline int pg_level_to_tdx_sept_level(enum pg_level level) -{ - WARN_ON_ONCE(level == PG_LEVEL_NONE); - return level - 1; -} - u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page); u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, enum pg_level level, struct page *page, u64 *ext_err1, u64 *ext_err2); u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); -u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, enum pg_level level, struct page *page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, enum pg_level level, u64 *ext_err1, u64 *ext_err2); u64 tdh_mng_key_config(struct tdx_td *td); u64 tdh_mng_create(struct tdx_td *td, u16 hkid); u64 tdh_vp_create(struct tdx_td *td, struct tdx_vp *vp); @@ -215,7 +209,7 @@ u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data); u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask); u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size); u64 tdh_mem_track(struct tdx_td *tdr); -u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, enum pg_level level, u64 *ext_err1, u64 *ext_err2); u64 tdh_phymem_cache_wb(bool resume); u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b8c3d3d8bbfe..04aecdd3a6c0 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1638,14 +1638,13 @@ static int tdx_mem_page_add(struct kvm *kvm, gfn_t gfn, enum pg_level level, static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, enum pg_level level, kvm_pfn_t pfn) { - int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); struct page *page = pfn_to_page(pfn); gpa_t gpa = gfn_to_gpa(gfn); u64 entry, level_state; u64 err; - err = tdh_mem_page_aug(&kvm_tdx->td, gpa, tdx_level, page, &entry, &level_state); + err = tdh_mem_page_aug(&kvm_tdx->td, gpa, level, page, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1689,12 +1688,11 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, enum pg_level level, void *private_spt) { - int tdx_level = pg_level_to_tdx_sept_level(level); gpa_t gpa = gfn_to_gpa(gfn); struct page *page = virt_to_page(private_spt); u64 err, entry, level_state; - err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, page, &entry, + err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, level, page, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1778,7 +1776,6 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { struct page *page = pfn_to_page(spte_to_pfn(mirror_spte)); - int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); gpa_t gpa = gfn_to_gpa(gfn); u64 err, entry, level_state; @@ -1798,7 +1795,7 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, return; err = tdh_do_no_vcpus(tdh_mem_range_block, kvm, &kvm_tdx->td, gpa, - tdx_level, &entry, &level_state); + level, &entry, &level_state); if (TDX_BUG_ON_2(err, TDH_MEM_RANGE_BLOCK, entry, level_state, kvm)) return; @@ -1814,7 +1811,7 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, * Race with TDH.VP.ENTER due to (0-step mitigation) and Guest TDCALLs. */ err = tdh_do_no_vcpus(tdh_mem_page_remove, kvm, &kvm_tdx->td, gpa, - tdx_level, &entry, &level_state); + level, &entry, &level_state); if (TDX_BUG_ON_2(err, TDH_MEM_PAGE_REMOVE, entry, level_state, kvm)) return; diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index cb9b3210ab71..a6e77afafa79 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -1568,6 +1568,12 @@ static void tdx_clflush_page(struct page *page) clflush_cache_range(page_to_virt(page), PAGE_SIZE); } +static int pg_level_to_tdx_sept_level(enum pg_level level) +{ + WARN_ON_ONCE(level == PG_LEVEL_NONE); + return level - 1; +} + noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) { args->rcx = td->tdvpr_pa; @@ -1608,10 +1614,11 @@ u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page } EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_add); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, enum pg_level level, + struct page *page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { - .rcx = gpa | level, + .rcx = gpa | pg_level_to_tdx_sept_level(level), .rdx = tdx_tdr_pa(td), .r8 = page_to_phys(page), }; @@ -1639,10 +1646,11 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) } EXPORT_SYMBOL_FOR_KVM(tdh_vp_addcx); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, enum pg_level level, + struct page *page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { - .rcx = gpa | level, + .rcx = gpa | pg_level_to_tdx_sept_level(level), .rdx = tdx_tdr_pa(td), .r8 = page_to_phys(page), }; @@ -1658,10 +1666,11 @@ u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u } EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_aug); -u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, enum pg_level level, + u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { - .rcx = gpa | level, + .rcx = gpa | pg_level_to_tdx_sept_level(level), .rdx = tdx_tdr_pa(td), }; u64 ret; @@ -1874,10 +1883,11 @@ u64 tdh_mem_track(struct tdx_td *td) } EXPORT_SYMBOL_FOR_KVM(tdh_mem_track); -u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, enum pg_level level, + u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { - .rcx = gpa | level, + .rcx = gpa | pg_level_to_tdx_sept_level(level), .rdx = tdx_tdr_pa(td), }; u64 ret; From 02eaaffdd8656084f130e000a7c1a4ef27ac87a8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 28 Jan 2026 17:14:34 -0800 Subject: [PATCH 0934/5214] KVM: x86/mmu: Update iter->old_spte if cmpxchg64 on mirror SPTE "fails" Pass a pointer to iter->old_spte, not simply its value, when setting an external SPTE in __tdp_mmu_set_spte_atomic(), so that the iterator's value will be updated if the cmpxchg64 to freeze the mirror SPTE fails. The bug is currently benign as TDX is mutualy exclusive with all paths that do "local" retry", e.g. clear_dirty_gfn_range() and wrprot_gfn_range(). Fixes: 77ac7079e66d ("KVM: x86/tdp_mmu: Propagate building mirror page tables") Reviewed-by: Kai Huang Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260129011517.3545883-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 5a2f8ce9a32b..f98afc3422ce 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -509,10 +509,10 @@ static void *get_external_spt(gfn_t gfn, u64 new_spte, int level) } static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sptep, - gfn_t gfn, u64 old_spte, + gfn_t gfn, u64 *old_spte, u64 new_spte, int level) { - bool was_present = is_shadow_present_pte(old_spte); + bool was_present = is_shadow_present_pte(*old_spte); bool is_present = is_shadow_present_pte(new_spte); bool is_leaf = is_present && is_last_spte(new_spte, level); int ret = 0; @@ -525,7 +525,7 @@ static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sp * page table has been modified. Use FROZEN_SPTE similar to * the zapping case. */ - if (!try_cmpxchg64(rcu_dereference(sptep), &old_spte, FROZEN_SPTE)) + if (!try_cmpxchg64(rcu_dereference(sptep), old_spte, FROZEN_SPTE)) return -EBUSY; /* @@ -541,7 +541,7 @@ static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sp ret = kvm_x86_call(link_external_spt)(kvm, gfn, level, external_spt); } if (ret) - __kvm_tdp_mmu_write_spte(sptep, old_spte); + __kvm_tdp_mmu_write_spte(sptep, *old_spte); else __kvm_tdp_mmu_write_spte(sptep, new_spte); return ret; @@ -670,7 +670,7 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, return -EBUSY; ret = set_external_spte_present(kvm, iter->sptep, iter->gfn, - iter->old_spte, new_spte, iter->level); + &iter->old_spte, new_spte, iter->level); if (ret) return ret; } else { From a8b2924676ec42d76597f849baba31acc3ba1bfc Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 28 Jan 2026 17:14:35 -0800 Subject: [PATCH 0935/5214] KVM: TDX: Account all non-transient page allocations for per-TD structures Account all non-transient allocations associated with a single TD (or its vCPUs), as KVM's ABI is that allocations that are active for the lifetime of a VM are accounted. Leave temporary allocations, i.e. allocations that are freed within a single function/ioctl, unaccounted, to again align with KVM's existing behavior, e.g. see commit dd103407ca31 ("KVM: X86: Remove unnecessary GFP_KERNEL_ACCOUNT for temporary variables"). Fixes: 8d032b683c29 ("KVM: TDX: create/destroy VM structure") Fixes: a50f673f25e0 ("KVM: TDX: Do TDX specific vcpu initialization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260129011517.3545883-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 04aecdd3a6c0..72d916c957bc 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2384,20 +2384,20 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, ret = -ENOMEM; - tdr_page = alloc_page(GFP_KERNEL); + tdr_page = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdr_page) goto free_hkid; kvm_tdx->td.tdcs_nr_pages = tdx_sysinfo->td_ctrl.tdcs_base_size / PAGE_SIZE; /* TDVPS = TDVPR(4K page) + TDCX(multiple 4K pages), -1 for TDVPR. */ kvm_tdx->td.tdcx_nr_pages = tdx_sysinfo->td_ctrl.tdvps_base_size / PAGE_SIZE - 1; - tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, - kvm_tdx->td.tdcs_nr_pages); + tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, kvm_tdx->td.tdcs_nr_pages, + GFP_KERNEL_ACCOUNT); if (!tdcs_pages) goto free_tdr; for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] = alloc_page(GFP_KERNEL); + tdcs_pages[i] = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdcs_pages[i]) goto free_tdcs; } @@ -2872,7 +2872,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) int ret, i; u64 err; - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) return -ENOMEM; tdx->vp.tdvpr_page = page; @@ -2885,14 +2885,14 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); if (!tdx->vp.tdcx_pages) { ret = -ENOMEM; goto free_tdvpr; } for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) { ret = -ENOMEM; goto free_tdcx; From e1a31ca28c9d1c36dd4e40468436b9ebce14e4d2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 28 Jan 2026 17:14:36 -0800 Subject: [PATCH 0936/5214] KVM: x86: Make "external SPTE" ops that can fail RET0 static calls Define kvm_x86_ops .link_external_spt(), .set_external_spte(), and .free_external_spt() as RET0 static calls so that an unexpected call to a a default operation doesn't consume garbage. Fixes: 77ac7079e66d ("KVM: x86/tdp_mmu: Propagate building mirror page tables") Fixes: 94faba8999b9 ("KVM: x86/tdp_mmu: Propagate tearing down mirror page tables") Reviewed-by: Kai Huang Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260129011517.3545883-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-ops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index e4fca997ec79..b0269325646c 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -96,9 +96,9 @@ KVM_X86_OP_OPTIONAL_RET0(set_identity_map_addr) KVM_X86_OP_OPTIONAL_RET0(get_mt_mask) KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep) KVM_X86_OP(load_mmu_pgd) -KVM_X86_OP_OPTIONAL(link_external_spt) -KVM_X86_OP_OPTIONAL(set_external_spte) -KVM_X86_OP_OPTIONAL(free_external_spt) +KVM_X86_OP_OPTIONAL_RET0(link_external_spt) +KVM_X86_OP_OPTIONAL_RET0(set_external_spte) +KVM_X86_OP_OPTIONAL_RET0(free_external_spt) KVM_X86_OP_OPTIONAL(remove_external_spte) KVM_X86_OP(has_wbinvd_exit) KVM_X86_OP(get_l2_tsc_offset) From 1f3e69af5f938ff331bf5d6bf379a877e4b69315 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Fri, 10 Apr 2026 16:26:54 -0700 Subject: [PATCH 0937/5214] KVM: TDX: Fix x2APIC MSR handling in tdx_has_emulated_msr() Rework tdx_has_emulated_msr() to explicitly enumerate the x2APIC MSRs that KVM can emulate, instead of trying to enumerate the MSRs that KVM cannot emulate. Drop the inner switch and list the emulatable x2APIC registers directly in the outer switch's "return true" block. The old code had multiple bugs in the x2APIC range handling. X2APIC_MSR(APIC_ISR + APIC_ISR_NR) was incorrect because APIC_ISR_NR is 0x8, not 0x80, so the X2APIC_MSR() shift lost the lower bits, collapsing each range to a single MSR. IA32_X2APIC_SELF_IPI was also missing from the non-emulatable list. Note, these bugs are relatively benign, as they only affect a guest that is requesting "bogus" emulation. KVM has no visibility into whether or not a guest has enabled #VE reduction, which changes which MSRs the TDX-Module handles itself versus triggering a #VE for the guest to make a TDVMCALL. So maintaining a list of non-emulatable MSRs is fragile. Listing only the MSRs KVM can always emulate sidesteps the problem. Suggested-by: Sean Christopherson Reported-by: Dmytro Maluka Closes: https://lore.kernel.org/all/20260318190111.1041924-1-dmaluka@chromium.org Fixes: dd50294f3e3c ("KVM: TDX: Implement callbacks for MSR operations") Assisted-by: Claude:claude-opus-4-6 [based on a diff from Sean, but added missed LVTCMCI case, log] Signed-off-by: Rick Edgecombe Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260410232654.3864196-1-rick.p.edgecombe@intel.com [sean: call out the bugs are relatively benign, expand comment] Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b8c3d3d8bbfe..e58a64400186 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2116,23 +2116,29 @@ bool tdx_has_emulated_msr(u32 index) case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: /* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ case MSR_KVM_POLL_CONTROL: + /* + * Except for x2APIC registers that are virtualized by the CPU, which + * KVM can't emulate as KVM doesn't have access to the virtual APIC + * page, KVM emulates the same set of x2APIC registers for TDX versus + * non-TDX guests. + */ + case X2APIC_MSR(APIC_ID): + case X2APIC_MSR(APIC_LVR): + case X2APIC_MSR(APIC_LDR): + case X2APIC_MSR(APIC_SPIV): + case X2APIC_MSR(APIC_ESR): + case X2APIC_MSR(APIC_LVTCMCI): + case X2APIC_MSR(APIC_ICR): + case X2APIC_MSR(APIC_LVTT): + case X2APIC_MSR(APIC_LVTTHMR): + case X2APIC_MSR(APIC_LVTPC): + case X2APIC_MSR(APIC_LVT0): + case X2APIC_MSR(APIC_LVT1): + case X2APIC_MSR(APIC_LVTERR): + case X2APIC_MSR(APIC_TMICT): + case X2APIC_MSR(APIC_TMCCT): + case X2APIC_MSR(APIC_TDCR): return true; - case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: - /* - * x2APIC registers that are virtualized by the CPU can't be - * emulated, KVM doesn't have access to the virtual APIC page. - */ - switch (index) { - case X2APIC_MSR(APIC_TASKPRI): - case X2APIC_MSR(APIC_PROCPRI): - case X2APIC_MSR(APIC_EOI): - case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): - case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): - case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): - return false; - default: - return true; - } default: return false; } From 6edd35e77a42ee3e72405619049a45bf368f9ab2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:35:33 -0700 Subject: [PATCH 0938/5214] KVM: selftests: Add a test to verify SEV {en,de}crypt debug ioctls Add a selftest to verify KVM's handling of {de,en}crypt debug ioctls, specifically focusing on edge cases around the chunk (16 bytes) and page (4096) sizes, where KVM had multiple bugs. E.g. KVM would fail to handle small sizes that aren't naturally aligned and sized, would buffer overflow if the destination was unaligned but the source was not, etc. Attempt to strike a balance between an exhaustive test and a reasonable runtime. On a system with both SEV and SEV-ES support, the current runtime is under 45 seconds. Which isn't great, but it's tolerable, and it's not obvious which of the combinations are "better" than the others. Link: https://patch.msgid.link/20260501203537.2120074-3-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/include/x86/sev.h | 24 ++++ .../testing/selftests/kvm/x86/sev_dbg_test.c | 118 ++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/sev_dbg_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..82fa943b9503 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -140,6 +140,7 @@ TEST_GEN_PROGS_x86 += x86/tsc_msrs_test TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test TEST_GEN_PROGS_x86 += x86/xen_shinfo_test TEST_GEN_PROGS_x86 += x86/xen_vmcall_test +TEST_GEN_PROGS_x86 += x86/sev_dbg_test TEST_GEN_PROGS_x86 += x86/sev_init2_tests TEST_GEN_PROGS_x86 += x86/sev_migrate_tests TEST_GEN_PROGS_x86 += x86/sev_smoke_test diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h index 1af44c151d60..dec383e59a47 100644 --- a/tools/testing/selftests/kvm/include/x86/sev.h +++ b/tools/testing/selftests/kvm/include/x86/sev.h @@ -144,4 +144,28 @@ static inline void snp_launch_update_data(struct kvm_vm *vm, gpa_t gpa, vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data); } +static inline void sev_dbg_crypt_memory(struct kvm_vm *vm, unsigned int cmd, + void *dst, void *src, unsigned int len) +{ + struct kvm_sev_dbg dbg = { + .src_uaddr = (unsigned long)src, + .dst_uaddr = (unsigned long)dst, + .len = len, + }; + + vm_sev_ioctl(vm, cmd, &dbg); +} + +static inline void sev_decrypt_memory(struct kvm_vm *vm, void *dst, void *src, + unsigned int len) +{ + sev_dbg_crypt_memory(vm, KVM_SEV_DBG_DECRYPT, dst, src, len); +} + +static inline void sev_encrypt_memory(struct kvm_vm *vm, void *dst, void *src, + unsigned int len) +{ + sev_dbg_crypt_memory(vm, KVM_SEV_DBG_ENCRYPT, dst, src, len); +} + #endif /* SELFTEST_KVM_SEV_H */ diff --git a/tools/testing/selftests/kvm/x86/sev_dbg_test.c b/tools/testing/selftests/kvm/x86/sev_dbg_test.c new file mode 100644 index 000000000000..a9d8e4c059f9 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/sev_dbg_test.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "sev.h" + +#define BUFFER_SIZE (PAGE_SIZE * 2) + +static u8 *data; +static u8 src[BUFFER_SIZE] __aligned(PAGE_SIZE); +static u8 dst[BUFFER_SIZE] __aligned(PAGE_SIZE); + +static void validate_dst(int i, int nr_bytes, u8 pattern) +{ + for ( ; i < nr_bytes; i++) + TEST_ASSERT(dst[i] == pattern, + "Expected 0x%x at byte %u, got 0x%x", + pattern, i, dst[i]); +} + +static void validate_buffers(void) +{ + int i; + + for (i = 0; i < BUFFER_SIZE; i++) + TEST_ASSERT(src[i] == dst[i], + "Expected src[%u] (0x%x) == dst[%u] (0x%x)", + i, src[i], i, dst[i]); +} + +static void ____test_sev_dbg(struct kvm_vm *vm, int i, int j, int nr_bytes) +{ + u8 pattern = guest_random_u32(&guest_rng); + + if (i + nr_bytes > BUFFER_SIZE || j + nr_bytes > BUFFER_SIZE) + return; + + memset(&src[i], pattern, nr_bytes); + sev_encrypt_memory(vm, &data[j], &src[i], nr_bytes); + sev_decrypt_memory(vm, &dst[i], &data[j], nr_bytes); + validate_buffers(); + validate_dst(i, nr_bytes, pattern); +} + +static void __test_sev_dbg(struct kvm_vm *vm, int nr_bytes) +{ + /* + * In a perfect world, all sizes at all combinations within the buffers + * would be tested. In reality, even this much testing is quite slow. + * Target sizes and offsets around the chunk (16 bytes) and page (4096 + * bytes) sizes. + */ + int x[] = { 1, 8, 15, 16, 23 }; + int p = PAGE_SIZE - 24; + int i, j; + + ____test_sev_dbg(vm, 0, 0, nr_bytes); + + for (i = 0; i < ARRAY_SIZE(x); i++) { + for (j = 0; j < ARRAY_SIZE(x); j++) { + ____test_sev_dbg(vm, x[i], x[j], nr_bytes); + ____test_sev_dbg(vm, x[i], p + x[j], nr_bytes); + ____test_sev_dbg(vm, p + x[i], x[j], nr_bytes); + ____test_sev_dbg(vm, p + x[i], p + x[j], nr_bytes); + } + } +} + +static void test_sev_dbg(u32 type, u64 policy) +{ + int sizes[] = { 1, 8, 15, 16, 17, 32, 33 }; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int i; + + if (!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(type))) + return; + + vm = vm_sev_create_with_one_vcpu(type, NULL, &vcpu); + + data = addr_gva2hva(vm, vm_alloc(vm, BUFFER_SIZE, KVM_UTIL_MIN_VADDR)); + memset(data, 0xaa, BUFFER_SIZE); + + vm_sev_launch(vm, policy, NULL); + + sev_decrypt_memory(vm, dst, data, BUFFER_SIZE); + validate_dst(0, BUFFER_SIZE, 0xaa); + + memset(src, 0x55, BUFFER_SIZE); + sev_encrypt_memory(vm, data, src, BUFFER_SIZE); + sev_decrypt_memory(vm, dst, data, BUFFER_SIZE); + validate_dst(0, BUFFER_SIZE, 0x55); + + __test_sev_dbg(vm, PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(sizes); i++) { + __test_sev_dbg(vm, sizes[i]); + __test_sev_dbg(vm, PAGE_SIZE - sizes[i]); + __test_sev_dbg(vm, PAGE_SIZE + sizes[i]); + __test_sev_dbg(vm, BUFFER_SIZE - sizes[i]); + } + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); + + /* Note, KVM doesn't support {de,en}crypt commands for SNP. */ + test_sev_dbg(KVM_X86_SEV_VM, 0); + test_sev_dbg(KVM_X86_SEV_ES_VM, SEV_POLICY_ES); + return 0; +} From cb32e895546bf45ebdd0e76ae7a7d783eed2c304 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:35:34 -0700 Subject: [PATCH 0939/5214] KVM: SEV: Explicitly validate the dst buffer for debug operations When encrypting/decrypting guest memory, explicitly check that the destination is non-NULL and doesn't wrap instead of subtly relying on sev_pin_memory() to perform the check. This will allow adding and using a more focused single-page pinning helper. Link: https://patch.msgid.link/20260501203537.2120074-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 00d3bab091d2..9e9b8ca2e9f7 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1357,9 +1357,11 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) if (copy_from_user(&debug, u64_to_user_ptr(argp->data), sizeof(debug))) return -EFAULT; - if (!debug.len || debug.src_uaddr + debug.len < debug.src_uaddr) + if (!debug.len || !debug.src_uaddr || !debug.dst_uaddr) return -EINVAL; - if (!debug.dst_uaddr) + + if (debug.src_uaddr + debug.len < debug.src_uaddr || + debug.dst_uaddr + debug.len < debug.dst_uaddr) return -EINVAL; vaddr = debug.src_uaddr; From 99694add4af382a8fd594e3fc8d02342b1afde02 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:35:35 -0700 Subject: [PATCH 0940/5214] KVM: SEV: Add helper function to pin/unpin a single page Add helpers to pin/unpin a single page, and use it in all flows that pin exactly one page. None of the single-page users actually check that the correct number of pages was pinned, which is functionally ok, but visually jarring, especially in the decrypt/encrypt flow, which separately pins two pages, but uses a single variable to track how pages were pinned each time. Again, it's functionally ok since core mm guarantees exactly one page will be pinned on success, but it's ugly. Opportunistically use page_to_phys() instead of open coding an equivalent via page_to_pfn(). Note, all users of the single-page helper pre-validate the address and length, i.e. don't rely on the sanity check in sev_pin_memory(). No functional change intended. Link: https://patch.msgid.link/20260501203537.2120074-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 145 +++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 55 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 9e9b8ca2e9f7..2d0f5a802ae1 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -720,14 +720,50 @@ static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; } +static int sev_check_pin_count(struct kvm *kvm, unsigned long npages) +{ + unsigned long total_npages, lock_limit; + + total_npages = to_kvm_sev_info(kvm)->pages_locked + npages; + if (total_npages > totalram_pages()) + return -EINVAL; + + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + if (total_npages > lock_limit && !capable(CAP_IPC_LOCK)) { + pr_err_ratelimited("SEV: %lu total pages would exceed the lock limit of %lu.\n", + total_npages, lock_limit); + return -ENOMEM; + } + + return 0; +} + +static int sev_pin_user_pages(struct kvm *kvm, unsigned long addr, int npages, + unsigned int gup_flags, struct page **pages) +{ + int npinned; + + lockdep_assert_held(&kvm->lock); + + npinned = pin_user_pages_fast(addr, npages, gup_flags, pages); + if (npinned != npages) { + if (npinned > 0) + unpin_user_pages(pages, npinned); + pr_err_ratelimited("SEV: Failure locking %u pages.\n", npages); + return -ENOMEM; + } + + to_kvm_sev_info(kvm)->pages_locked += npages; + return 0; +} + static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, unsigned long ulen, unsigned long *n, unsigned int flags) { - struct kvm_sev_info *sev = to_kvm_sev_info(kvm); - unsigned long npages, total_npages, lock_limit; + unsigned long npages; struct page **pages; - int npinned, ret; + int ret; lockdep_assert_held(&kvm->lock); @@ -743,16 +779,9 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, if (npages > INT_MAX) return ERR_PTR(-EINVAL); - total_npages = sev->pages_locked + npages; - if (total_npages > totalram_pages()) - return ERR_PTR(-EINVAL); - - lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (total_npages > lock_limit && !capable(CAP_IPC_LOCK)) { - pr_err("SEV: %lu total pages would exceed the lock limit of %lu.\n", - total_npages, lock_limit); - return ERR_PTR(-ENOMEM); - } + ret = sev_check_pin_count(kvm, npages); + if (ret) + return ERR_PTR(ret); /* * Don't WARN if the kernel (rightly) thinks the total size is absurd, @@ -764,25 +793,14 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, if (!pages) return ERR_PTR(-ENOMEM); - /* Pin the user virtual address. */ - npinned = pin_user_pages_fast(uaddr, npages, flags, pages); - if (npinned != npages) { - pr_err("SEV: Failure locking %lu pages.\n", npages); - ret = -ENOMEM; - goto err; + ret = sev_pin_user_pages(kvm, uaddr, npages, flags, pages); + if (ret) { + kvfree(pages); + return ERR_PTR(ret); } *n = npages; - sev->pages_locked = total_npages; - return pages; - -err: - if (npinned > 0) - unpin_user_pages(pages, npinned); - - kvfree(pages); - return ERR_PTR(ret); } static void sev_unpin_memory(struct kvm *kvm, struct page **pages, @@ -793,6 +811,29 @@ static void sev_unpin_memory(struct kvm *kvm, struct page **pages, to_kvm_sev_info(kvm)->pages_locked -= npages; } +static struct page *sev_pin_page(struct kvm *kvm, unsigned long addr, + unsigned int flags) +{ + struct page *page; + int r; + + r = sev_check_pin_count(kvm, 1); + if (r) + return ERR_PTR(r); + + r = sev_pin_user_pages(kvm, addr, 1, flags, &page); + if (r) + return ERR_PTR(r); + + return page; +} + +static void sev_unpin_page(struct kvm *kvm, struct page *page) +{ + unpin_user_pages(&page, 1); + to_kvm_sev_info(kvm)->pages_locked -= 1; +} + static void sev_clflush_pages(struct page *pages[], unsigned long npages) { uint8_t *page_virtual; @@ -1345,9 +1386,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) { unsigned long vaddr, vaddr_end, next_vaddr; unsigned long dst_vaddr; - struct page **src_p, **dst_p; + struct page *src_p, *dst_p; struct kvm_sev_dbg debug; - unsigned long n; unsigned int size; int ret; @@ -1373,13 +1413,13 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) int len, s_off, d_off; /* lock userspace source and destination page */ - src_p = sev_pin_memory(kvm, vaddr & PAGE_MASK, PAGE_SIZE, &n, 0); + src_p = sev_pin_page(kvm, vaddr & PAGE_MASK, 0); if (IS_ERR(src_p)) return PTR_ERR(src_p); - dst_p = sev_pin_memory(kvm, dst_vaddr & PAGE_MASK, PAGE_SIZE, &n, FOLL_WRITE); + dst_p = sev_pin_page(kvm, dst_vaddr & PAGE_MASK, FOLL_WRITE); if (IS_ERR(dst_p)) { - sev_unpin_memory(kvm, src_p, n); + sev_unpin_page(kvm, src_p); return PTR_ERR(dst_p); } @@ -1388,8 +1428,8 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) * the pages; flush the destination too so that future accesses do not * see stale data. */ - sev_clflush_pages(src_p, 1); - sev_clflush_pages(dst_p, 1); + sev_clflush_pages(&src_p, 1); + sev_clflush_pages(&dst_p, 1); /* * Since user buffer may not be page aligned, calculate the @@ -1402,20 +1442,20 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) if (dec) ret = __sev_dbg_decrypt_user(kvm, - __sme_page_pa(src_p[0]) + s_off, + __sme_page_pa(src_p) + s_off, (void __user *)dst_vaddr, - __sme_page_pa(dst_p[0]) + d_off, + __sme_page_pa(dst_p) + d_off, len, &argp->error); else ret = __sev_dbg_encrypt_user(kvm, - __sme_page_pa(src_p[0]) + s_off, + __sme_page_pa(src_p) + s_off, (void __user *)vaddr, - __sme_page_pa(dst_p[0]) + d_off, + __sme_page_pa(dst_p) + d_off, (void __user *)dst_vaddr, len, &argp->error); - sev_unpin_memory(kvm, src_p, n); - sev_unpin_memory(kvm, dst_p, n); + sev_unpin_page(kvm, src_p); + sev_unpin_page(kvm, dst_p); if (ret) goto err; @@ -1698,8 +1738,7 @@ static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) struct sev_data_send_update_data data; struct kvm_sev_send_update_data params; void *hdr, *trans_data; - struct page **guest_page; - unsigned long n; + struct page *guest_page; int ret, offset; if (!sev_guest(kvm)) @@ -1723,8 +1762,7 @@ static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) return -EINVAL; /* Pin guest memory */ - guest_page = sev_pin_memory(kvm, params.guest_uaddr & PAGE_MASK, - PAGE_SIZE, &n, 0); + guest_page = sev_pin_page(kvm, params.guest_uaddr & PAGE_MASK, 0); if (IS_ERR(guest_page)) return PTR_ERR(guest_page); @@ -1745,7 +1783,7 @@ static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) data.trans_len = params.trans_len; /* The SEND_UPDATE_DATA command requires C-bit to be always set. */ - data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset; + data.guest_address = page_to_phys(guest_page) + offset; data.guest_address |= sev_me_mask; data.guest_len = params.guest_len; data.handle = to_kvm_sev_info(kvm)->handle; @@ -1772,8 +1810,7 @@ static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) e_free_hdr: kfree(hdr); e_unpin: - sev_unpin_memory(kvm, guest_page, n); - + sev_unpin_page(kvm, guest_page); return ret; } @@ -1878,8 +1915,7 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) struct kvm_sev_receive_update_data params; struct sev_data_receive_update_data data; void *hdr = NULL, *trans = NULL; - struct page **guest_page; - unsigned long n; + struct page *guest_page; int ret, offset; if (!sev_guest(kvm)) @@ -1916,8 +1952,7 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) data.trans_len = params.trans_len; /* Pin guest memory */ - guest_page = sev_pin_memory(kvm, params.guest_uaddr & PAGE_MASK, - PAGE_SIZE, &n, FOLL_WRITE); + guest_page = sev_pin_page(kvm, params.guest_uaddr & PAGE_MASK, FOLL_WRITE); if (IS_ERR(guest_page)) { ret = PTR_ERR(guest_page); goto e_free_trans; @@ -1928,10 +1963,10 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) * encrypts the written data with the guest's key, and the cache may * contain dirty, unencrypted data. */ - sev_clflush_pages(guest_page, n); + sev_clflush_pages(&guest_page, 1); /* The RECEIVE_UPDATE_DATA command requires C-bit to be always set. */ - data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset; + data.guest_address = page_to_phys(guest_page) + offset; data.guest_address |= sev_me_mask; data.guest_len = params.guest_len; data.handle = to_kvm_sev_info(kvm)->handle; @@ -1939,7 +1974,7 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) ret = sev_issue_cmd(kvm, SEV_CMD_RECEIVE_UPDATE_DATA, &data, &argp->error); - sev_unpin_memory(kvm, guest_page, n); + sev_unpin_page(kvm, guest_page); e_free_trans: kfree(trans); From 8d6297a3e73e08f86aebdeeafb968816175ce39a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:35:36 -0700 Subject: [PATCH 0941/5214] KVM: SEV: Rewrite logic to {de,en}crypt memory for debug Wholesale rewrite the guts of the debug {de,en}crypt flows, as the existing code is broken, e.g. doesn't handle cases where the access isn't naturally sized and aligned, and is so wildly flawed that attempting to salvage the current code in an iterative fashion would be more risky than a rewrite. E.g. when encrypting 9 bytes at offset 8, KVM needs to _decrypt_ destination[31:0] into a temporary buffer, buffer[31:0], then copy 9 bytes from source[8:0] to buffer[16:8], then encrypt buffer[31:0] back into destination[31:0]. The current code only ever copies 16 bytes, and bizarrely uses a temporary buffer for the source as well. To keep the code easier to read and maintain, send the unaligned cases down dedicated "slow" paths instead of trying to mix and match the possible combinations in one helper. For now, preserve the basic approach of the current code, e.g. allocate an entire page for the temporary buffer, to minimize unwanted changes in functionality. Link: https://patch.msgid.link/20260501203537.2120074-6-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 324 +++++++++++++++++++---------------------- 1 file changed, 148 insertions(+), 176 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 2d0f5a802ae1..756878a258a4 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1237,159 +1237,140 @@ static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; } -static int __sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src, - unsigned long dst, int size, - int *error, bool enc) +static int sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src_pa, + unsigned long dst_pa, unsigned int size, + unsigned int ioctl, int *error) { - struct sev_data_dbg data; + int cmd = ioctl == KVM_SEV_DBG_DECRYPT ? SEV_CMD_DBG_DECRYPT : + SEV_CMD_DBG_ENCRYPT; + struct sev_data_dbg data = { + .handle = to_kvm_sev_info(kvm)->handle, + .dst_addr = dst_pa, + .src_addr = src_pa, + .len = size, + }; - data.reserved = 0; - data.handle = to_kvm_sev_info(kvm)->handle; - data.dst_addr = dst; - data.src_addr = src; - data.len = size; - - return sev_issue_cmd(kvm, - enc ? SEV_CMD_DBG_ENCRYPT : SEV_CMD_DBG_DECRYPT, - &data, error); + return sev_issue_cmd(kvm, cmd, &data, error); } -static int __sev_dbg_decrypt(struct kvm *kvm, unsigned long src_paddr, - unsigned long dst_paddr, int sz, int *err) +static struct page *sev_alloc_dbg_buffer(void **buf) { - int offset; + struct page *buf_p; + + buf_p = alloc_page(GFP_KERNEL); + if (!buf_p) + return NULL; + + *buf = kmap_local_page(buf_p); + return buf_p; +} + +static void sev_free_dbg_buffer(struct page *buf_p, void *buf) +{ + kunmap_local(buf); + __free_page(buf_p); +} + +static unsigned int sev_dbg_crypt_slow_addr_and_size(struct page *page, + unsigned long __va, + unsigned int len, + unsigned long *pa) +{ + /* The number of bytes to {de,en}crypt must be 16-byte aligned. */ + unsigned int nr_bytes = round_up(len, 16); + unsigned long va = ALIGN_DOWN(__va, 16); /* - * Its safe to read more than we are asked, caller should ensure that - * destination has enough space. + * Increase the number of bytes to {de,en}crypt by one chunk (16 bytes) + * if the aligned address and length doesn't cover the unaligned range, + * e.g. if the address is unaligned _and_ the access will split a chunk + * at the tail. */ - offset = src_paddr & 15; - src_paddr = round_down(src_paddr, 16); - sz = round_up(sz + offset, 16); + if (va + nr_bytes < __va + len) + nr_bytes += 16; - return __sev_issue_dbg_cmd(kvm, src_paddr, dst_paddr, sz, err, false); -} - -static int __sev_dbg_decrypt_user(struct kvm *kvm, unsigned long paddr, - void __user *dst_uaddr, - unsigned long dst_paddr, - int size, int *err) -{ - struct page *tpage = NULL; - int ret, offset; - - /* if inputs are not 16-byte then use intermediate buffer */ - if (!IS_ALIGNED(dst_paddr, 16) || - !IS_ALIGNED(paddr, 16) || - !IS_ALIGNED(size, 16)) { - tpage = (void *)alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); - if (!tpage) - return -ENOMEM; - - dst_paddr = __sme_page_pa(tpage); - } - - ret = __sev_dbg_decrypt(kvm, paddr, dst_paddr, size, err); - if (ret) - goto e_free; - - if (tpage) { - offset = paddr & 15; - if (copy_to_user(dst_uaddr, page_address(tpage) + offset, size)) - ret = -EFAULT; - } - -e_free: - if (tpage) - __free_page(tpage); - - return ret; -} - -static int __sev_dbg_encrypt_user(struct kvm *kvm, unsigned long paddr, - void __user *vaddr, - unsigned long dst_paddr, - void __user *dst_vaddr, - int size, int *error) -{ - struct page *src_tpage = NULL; - struct page *dst_tpage = NULL; - int ret, len = size; - - /* If source buffer is not aligned then use an intermediate buffer */ - if (!IS_ALIGNED((unsigned long)vaddr, 16)) { - src_tpage = alloc_page(GFP_KERNEL_ACCOUNT); - if (!src_tpage) - return -ENOMEM; - - if (copy_from_user(page_address(src_tpage), vaddr, size)) { - __free_page(src_tpage); - return -EFAULT; - } - - paddr = __sme_page_pa(src_tpage); - } + *pa = __sme_page_pa(page) + (va & ~PAGE_MASK); /* - * If destination buffer or length is not aligned then do read-modify-write: - * - decrypt destination in an intermediate buffer - * - copy the source buffer in an intermediate buffer - * - use the intermediate buffer as source buffer + * Sanity check that the new access won't split a page. This should + * never happen; just squash the access and let the firmware command + * fail. */ - if (!IS_ALIGNED((unsigned long)dst_vaddr, 16) || !IS_ALIGNED(size, 16)) { - int dst_offset; + if (WARN_ON_ONCE((*pa & PAGE_MASK) != ((*pa + nr_bytes - 1) & PAGE_MASK))) + return 0; - dst_tpage = alloc_page(GFP_KERNEL_ACCOUNT); - if (!dst_tpage) { - ret = -ENOMEM; - goto e_free; - } - - ret = __sev_dbg_decrypt(kvm, dst_paddr, - __sme_page_pa(dst_tpage), size, error); - if (ret) - goto e_free; - - /* - * If source is kernel buffer then use memcpy() otherwise - * copy_from_user(). - */ - dst_offset = dst_paddr & 15; - - if (src_tpage) - memcpy(page_address(dst_tpage) + dst_offset, - page_address(src_tpage), size); - else { - if (copy_from_user(page_address(dst_tpage) + dst_offset, - vaddr, size)) { - ret = -EFAULT; - goto e_free; - } - } - - paddr = __sme_page_pa(dst_tpage); - dst_paddr = round_down(dst_paddr, 16); - len = round_up(size, 16); - } - - ret = __sev_issue_dbg_cmd(kvm, paddr, dst_paddr, len, error, true); - -e_free: - if (src_tpage) - __free_page(src_tpage); - if (dst_tpage) - __free_page(dst_tpage); - return ret; + return nr_bytes; } -static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) +static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, + struct page *src_p, unsigned long dst, + unsigned int len, int *err) +{ + unsigned int nr_bytes; + unsigned long src_pa; + struct page *buf_p; + void *buf; + int r; + + buf_p = sev_alloc_dbg_buffer(&buf); + if (!buf_p) + return -ENOMEM; + + nr_bytes = sev_dbg_crypt_slow_addr_and_size(src_p, src, len, &src_pa); + + r = sev_issue_dbg_cmd(kvm, src_pa, __sme_page_pa(buf_p), + nr_bytes, KVM_SEV_DBG_DECRYPT, err); + if (r) + goto out; + + if (copy_to_user((void __user *)dst, buf + (src & 15), len)) + r = -EFAULT; +out: + sev_free_dbg_buffer(buf_p, buf); + return r; +} + +static int sev_dbg_encrypt_slow(struct kvm *kvm, unsigned long src, + unsigned long dst, struct page *dst_p, + unsigned int len, int *err) +{ + unsigned int nr_bytes; + unsigned long dst_pa; + struct page *buf_p; + void *buf; + int r; + + buf_p = sev_alloc_dbg_buffer(&buf); + if (!buf_p) + return -ENOMEM; + + /* Decrypt the _destination_ to do a RMW on plaintext. */ + nr_bytes = sev_dbg_crypt_slow_addr_and_size(dst_p, dst, len, &dst_pa); + + r = sev_issue_dbg_cmd(kvm, dst_pa, __sme_page_pa(buf_p), + nr_bytes, KVM_SEV_DBG_DECRYPT, err); + if (r) + goto out; + + /* + * Copy from the source into the intermediate buffer, and then + * re-encrypt the buffer into the destination. + */ + if (copy_from_user(buf + (dst & 15), (void __user *)src, len)) + r = -EFAULT; + else + r = sev_issue_dbg_cmd(kvm, __sme_page_pa(buf_p), dst_pa, + nr_bytes, KVM_SEV_DBG_ENCRYPT, err); +out: + sev_free_dbg_buffer(buf_p, buf); + return r; +} + +static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, + unsigned int cmd) { - unsigned long vaddr, vaddr_end, next_vaddr; - unsigned long dst_vaddr; - struct page *src_p, *dst_p; struct kvm_sev_dbg debug; - unsigned int size; - int ret; + unsigned int i, len; if (!sev_guest(kvm)) return -ENOTTY; @@ -1404,20 +1385,29 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) debug.dst_uaddr + debug.len < debug.dst_uaddr) return -EINVAL; - vaddr = debug.src_uaddr; - size = debug.len; - vaddr_end = vaddr + size; - dst_vaddr = debug.dst_uaddr; + for (i = 0; i < debug.len; i += len) { + unsigned long src = debug.src_uaddr + i; + unsigned long dst = debug.dst_uaddr + i; + unsigned long s_off = src & ~PAGE_MASK; + unsigned long d_off = dst & ~PAGE_MASK; + struct page *src_p, *dst_p; + int ret; - for (; vaddr < vaddr_end; vaddr = next_vaddr) { - int len, s_off, d_off; + /* + * Copy as many remaining bytes as possible while staying in a + * single page for both the source and destination. + */ + len = min3(debug.len - i, PAGE_SIZE - s_off, PAGE_SIZE - d_off); - /* lock userspace source and destination page */ - src_p = sev_pin_page(kvm, vaddr & PAGE_MASK, 0); + /* + * Pin the source and destination pages; firmware operates on + * physical addresses. + */ + src_p = sev_pin_page(kvm, src & PAGE_MASK, 0); if (IS_ERR(src_p)) return PTR_ERR(src_p); - dst_p = sev_pin_page(kvm, dst_vaddr & PAGE_MASK, FOLL_WRITE); + dst_p = sev_pin_page(kvm, dst & PAGE_MASK, FOLL_WRITE); if (IS_ERR(dst_p)) { sev_unpin_page(kvm, src_p); return PTR_ERR(dst_p); @@ -1431,41 +1421,25 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) sev_clflush_pages(&src_p, 1); sev_clflush_pages(&dst_p, 1); - /* - * Since user buffer may not be page aligned, calculate the - * offset within the page. - */ - s_off = vaddr & ~PAGE_MASK; - d_off = dst_vaddr & ~PAGE_MASK; - len = min_t(size_t, (PAGE_SIZE - s_off), size); - len = min_t(size_t, len, PAGE_SIZE - d_off); - - if (dec) - ret = __sev_dbg_decrypt_user(kvm, - __sme_page_pa(src_p) + s_off, - (void __user *)dst_vaddr, - __sme_page_pa(dst_p) + d_off, - len, &argp->error); + if (IS_ALIGNED(src, 16) && IS_ALIGNED(dst, 16) && IS_ALIGNED(len, 16)) + ret = sev_issue_dbg_cmd(kvm, + __sme_page_pa(src_p) + s_off, + __sme_page_pa(dst_p) + d_off, + len, cmd, &argp->error); + else if (cmd == KVM_SEV_DBG_DECRYPT) + ret = sev_dbg_decrypt_slow(kvm, src, src_p, dst, + len, &argp->error); else - ret = __sev_dbg_encrypt_user(kvm, - __sme_page_pa(src_p) + s_off, - (void __user *)vaddr, - __sme_page_pa(dst_p) + d_off, - (void __user *)dst_vaddr, - len, &argp->error); + ret = sev_dbg_encrypt_slow(kvm, src, dst, dst_p, + len, &argp->error); sev_unpin_page(kvm, src_p); sev_unpin_page(kvm, dst_p); if (ret) - goto err; - - next_vaddr = vaddr + len; - dst_vaddr = dst_vaddr + len; - size -= len; + return ret; } -err: - return ret; + return 0; } static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) @@ -2727,10 +2701,8 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp) r = sev_guest_status(kvm, &sev_cmd); break; case KVM_SEV_DBG_DECRYPT: - r = sev_dbg_crypt(kvm, &sev_cmd, true); - break; case KVM_SEV_DBG_ENCRYPT: - r = sev_dbg_crypt(kvm, &sev_cmd, false); + r = sev_dbg_crypt(kvm, &sev_cmd, sev_cmd.id); break; case KVM_SEV_LAUNCH_SECRET: r = sev_launch_secret(kvm, &sev_cmd); From 4c735bf1bc22fd6ee66ab4bffe1d7599c3964781 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:35:37 -0700 Subject: [PATCH 0942/5214] KVM: SEV: Allocate only as many bytes as needed for temp crypt buffers When using a temporary buffer to {de,en}crypt unaligned memory for debug, allocate only the number of bytes that are needed instead of allocating an entire page. The most common case for unaligned accesses will be reading or writing less than 16 bytes. Link: https://patch.msgid.link/20260501203537.2120074-7-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 69 ++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 756878a258a4..37d4cfa5d980 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1253,53 +1253,34 @@ static int sev_issue_dbg_cmd(struct kvm *kvm, unsigned long src_pa, return sev_issue_cmd(kvm, cmd, &data, error); } -static struct page *sev_alloc_dbg_buffer(void **buf) +static void *sev_dbg_crypt_slow_alloc(struct page *page, unsigned long __va, + unsigned int len, unsigned long *pa, + unsigned int *nr_bytes) { - struct page *buf_p; - - buf_p = alloc_page(GFP_KERNEL); - if (!buf_p) - return NULL; - - *buf = kmap_local_page(buf_p); - return buf_p; -} - -static void sev_free_dbg_buffer(struct page *buf_p, void *buf) -{ - kunmap_local(buf); - __free_page(buf_p); -} - -static unsigned int sev_dbg_crypt_slow_addr_and_size(struct page *page, - unsigned long __va, - unsigned int len, - unsigned long *pa) -{ - /* The number of bytes to {de,en}crypt must be 16-byte aligned. */ - unsigned int nr_bytes = round_up(len, 16); unsigned long va = ALIGN_DOWN(__va, 16); + /* The number of bytes to {de,en}crypt must be 16-byte aligned. */ + *nr_bytes = round_up(len, 16); + /* * Increase the number of bytes to {de,en}crypt by one chunk (16 bytes) * if the aligned address and length doesn't cover the unaligned range, * e.g. if the address is unaligned _and_ the access will split a chunk * at the tail. */ - if (va + nr_bytes < __va + len) - nr_bytes += 16; + if (va + *nr_bytes < __va + len) + *nr_bytes += 16; *pa = __sme_page_pa(page) + (va & ~PAGE_MASK); /* * Sanity check that the new access won't split a page. This should - * never happen; just squash the access and let the firmware command - * fail. + * never happen; just pretend the allocation failed. */ - if (WARN_ON_ONCE((*pa & PAGE_MASK) != ((*pa + nr_bytes - 1) & PAGE_MASK))) - return 0; + if (WARN_ON_ONCE((*pa & PAGE_MASK) != ((*pa + *nr_bytes - 1) & PAGE_MASK))) + return NULL; - return nr_bytes; + return kmalloc(*nr_bytes, GFP_KERNEL); } static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, @@ -1308,17 +1289,14 @@ static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, { unsigned int nr_bytes; unsigned long src_pa; - struct page *buf_p; void *buf; int r; - buf_p = sev_alloc_dbg_buffer(&buf); - if (!buf_p) + buf = sev_dbg_crypt_slow_alloc(src_p, src, len, &src_pa, &nr_bytes); + if (!buf) return -ENOMEM; - nr_bytes = sev_dbg_crypt_slow_addr_and_size(src_p, src, len, &src_pa); - - r = sev_issue_dbg_cmd(kvm, src_pa, __sme_page_pa(buf_p), + r = sev_issue_dbg_cmd(kvm, src_pa, __sme_set(__pa(buf)), nr_bytes, KVM_SEV_DBG_DECRYPT, err); if (r) goto out; @@ -1326,7 +1304,7 @@ static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, if (copy_to_user((void __user *)dst, buf + (src & 15), len)) r = -EFAULT; out: - sev_free_dbg_buffer(buf_p, buf); + kfree(buf); return r; } @@ -1336,18 +1314,15 @@ static int sev_dbg_encrypt_slow(struct kvm *kvm, unsigned long src, { unsigned int nr_bytes; unsigned long dst_pa; - struct page *buf_p; void *buf; int r; - buf_p = sev_alloc_dbg_buffer(&buf); - if (!buf_p) + /* Decrypt the _destination_ to do a RMW on plaintext. */ + buf = sev_dbg_crypt_slow_alloc(dst_p, dst, len, &dst_pa, &nr_bytes); + if (!buf) return -ENOMEM; - /* Decrypt the _destination_ to do a RMW on plaintext. */ - nr_bytes = sev_dbg_crypt_slow_addr_and_size(dst_p, dst, len, &dst_pa); - - r = sev_issue_dbg_cmd(kvm, dst_pa, __sme_page_pa(buf_p), + r = sev_issue_dbg_cmd(kvm, dst_pa, __sme_set(__pa(buf)), nr_bytes, KVM_SEV_DBG_DECRYPT, err); if (r) goto out; @@ -1359,10 +1334,10 @@ static int sev_dbg_encrypt_slow(struct kvm *kvm, unsigned long src, if (copy_from_user(buf + (dst & 15), (void __user *)src, len)) r = -EFAULT; else - r = sev_issue_dbg_cmd(kvm, __sme_page_pa(buf_p), dst_pa, + r = sev_issue_dbg_cmd(kvm, __sme_set(__pa(buf)), dst_pa, nr_bytes, KVM_SEV_DBG_ENCRYPT, err); out: - sev_free_dbg_buffer(buf_p, buf); + kfree(buf); return r; } From 822790ab01495d67b14174063ba46fcc19ff0aa8 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:24 -0700 Subject: [PATCH 0943/5214] KVM: x86: Define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT Define a quirk to control whether nested SVM shares L1's PAT with L2 (legacy behavior) or gives L2 its own independent gPAT (correct behavior per the APM). When the quirk is enabled (default), L2 shares L1's PAT, preserving the legacy KVM behavior. When userspace disables the quirk, KVM correctly virtualizes the PAT for nested SVM guests, giving L2 a separate gPAT as specified in the AMD architecture. Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-2-jmattson@google.com Signed-off-by: Sean Christopherson --- Documentation/virt/kvm/api.rst | 14 ++++++++++++++ arch/x86/include/asm/kvm_host.h | 3 ++- arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/kvm/svm/svm.h | 11 +++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 52bbbb553ce1..59469495c8d3 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8553,6 +8553,20 @@ KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM By default, KVM relaxes the consisten bit to be cleared. Note that the vmcs02 bit is still completely controlled by the host, regardless of the quirk setting. + +KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT By default, KVM for nested SVM guests + shares the IA32_PAT MSR between L1 and + L2. This is legacy behavior and does + not match the AMD architecture + specification. When this quirk is + disabled and nested paging (NPT) is + enabled for L2, KVM correctly + virtualizes a separate guest PAT + register for L2, using the g_pat + field in the VMCB. When NPT is + disabled for L2, L1 and L2 continue + to share the IA32_PAT MSR regardless + of the quirk setting. ======================================== ================================================ 7.32 KVM_CAP_MAX_VCPU_ID diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8a53ca619570..ef353daeacc9 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2550,7 +2550,8 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages); KVM_X86_QUIRK_SLOT_ZAP_ALL | \ KVM_X86_QUIRK_STUFF_FEATURE_MSRS | \ KVM_X86_QUIRK_IGNORE_GUEST_PAT | \ - KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM) + KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM | \ + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT) #define KVM_X86_CONDITIONAL_QUIRKS \ (KVM_X86_QUIRK_CD_NW_CLEARED | \ diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 5f2b30d0405c..3ada2fa9ca86 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -477,6 +477,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) #define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9) #define KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM (1 << 10) +#define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT (1 << 11) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 19b80ef56e2b..9fd2232aa8d1 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -24,6 +24,7 @@ #include "cpuid.h" #include "kvm_cache_regs.h" +#include "x86.h" /* * Helpers to convert to/from physical addresses for pages whose address is @@ -641,6 +642,16 @@ static inline bool nested_npt_enabled(struct vcpu_svm *svm) return svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_NP; } +static inline bool l2_has_separate_pat(struct kvm_vcpu *vcpu) +{ + /* + * If KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled while a vCPU + * is running, the L2 IA32_PAT semantics for that vCPU are undefined. + */ + return nested_npt_enabled(to_svm(vcpu)) && + !kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); +} + static inline bool nested_vnmi_enabled(struct vcpu_svm *svm) { return guest_cpu_cap_has(&svm->vcpu, X86_FEATURE_VNMI) && From 0a8aeb15848ea1c873fd20e1ad3aeba689aeafa8 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:25 -0700 Subject: [PATCH 0944/5214] KVM: x86: nSVM: Clear VMCB_NPT clean bit when updating hPAT from guest mode When running an L2 guest and writing to MSR_IA32_CR_PAT, the host PAT value is stored in both vmcb01's g_pat field and vmcb02's g_pat field, but the clean bit was only being cleared for vmcb02. Introduce the helper vmcb_set_gpat() which sets vmcb->save.g_pat and marks the VMCB dirty for VMCB_NPT. Use this helper in both svm_set_msr() for updating vmcb01 and in nested_vmcb02_compute_g_pat() for updating vmcb02, ensuring both VMCBs' NPT fields are properly marked dirty. Fixes: 4995a3685f1b ("KVM: SVM: Use a separate vmcb for the nested L2 guest") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-3-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 2 +- arch/x86/kvm/svm/svm.c | 3 +-- arch/x86/kvm/svm/svm.h | 6 ++++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 3d1fd1776e19..cd8f5f3f5e33 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -701,7 +701,7 @@ void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm) return; /* FIXME: merge g_pat from vmcb01 and vmcb12. */ - svm->nested.vmcb02.ptr->save.g_pat = svm->vmcb01.ptr->save.g_pat; + vmcb_set_gpat(svm->nested.vmcb02.ptr, svm->vmcb01.ptr->save.g_pat); } static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index b78dd8805ebb..d032d0b3d4c0 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2989,10 +2989,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) if (ret) break; - svm->vmcb01.ptr->save.g_pat = data; + vmcb_set_gpat(svm->vmcb01.ptr, data); if (is_guest_mode(vcpu)) nested_vmcb02_compute_g_pat(svm); - vmcb_mark_dirty(svm->vmcb, VMCB_NPT); break; case MSR_IA32_SPEC_CTRL: if (!msr->host_initiated && diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 9fd2232aa8d1..5261c3f12424 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -465,6 +465,12 @@ static inline bool vmcb12_is_dirty(struct vmcb_ctrl_area_cached *control, int bi return !test_bit(bit, (unsigned long *)&control->clean); } +static inline void vmcb_set_gpat(struct vmcb *vmcb, u64 data) +{ + vmcb->save.g_pat = data; + vmcb_mark_dirty(vmcb, VMCB_NPT); +} + static __always_inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu) { return container_of(vcpu, struct vcpu_svm, vcpu); From 4b83e4ba836eadaf369e9af016e20cd74b7fa011 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:26 -0700 Subject: [PATCH 0945/5214] KVM: x86: nSVM: Cache and validate vmcb12 g_pat When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and nested paging is enabled in vmcb12, validate g_pat at emulated VMRUN and cause an immediate VMEXIT with exit code VMEXIT_INVALID if it is invalid, as specified in the APM, volume 2: "Nested Paging and VMRUN/VMEXIT." Fixes: 3d6368ef580a ("KVM: SVM: Add VMRUN handler") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-4-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 23 +++++++++++++++++++---- arch/x86/kvm/svm/svm.h | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index cd8f5f3f5e33..98701c23bc99 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -411,7 +411,8 @@ static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu, /* Common checks that apply to both L1 and L2 state. */ static bool nested_vmcb_check_save(struct kvm_vcpu *vcpu, - struct vmcb_save_area_cached *save) + struct vmcb_save_area_cached *save, + bool check_gpat) { if (CC(!(save->efer & EFER_SVME))) return false; @@ -446,6 +447,15 @@ static bool nested_vmcb_check_save(struct kvm_vcpu *vcpu, if (CC(!kvm_valid_efer(vcpu, save->efer))) return false; + /* + * If userspace contrives to get an invalid g_pat into vmcb02 by + * disabling KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT in a race with + * this check, it should be prepared for the KVM_EXIT_FAIL_ENTRY + * that will follow. + */ + if (check_gpat && CC(!kvm_pat_valid(save->g_pat))) + return false; + return true; } @@ -453,7 +463,8 @@ int nested_svm_check_cached_vmcb12(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - if (!nested_vmcb_check_save(vcpu, &svm->nested.save) || + if (!nested_vmcb_check_save(vcpu, &svm->nested.save, + l2_has_separate_pat(vcpu)) || !nested_vmcb_check_controls(vcpu, &svm->nested.ctl)) return -EINVAL; @@ -566,6 +577,7 @@ static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to, to->rax = from->rax; to->cr2 = from->cr2; + to->g_pat = from->g_pat; svm_copy_lbrs(to, from); } @@ -1982,13 +1994,16 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, /* * Validate host state saved from before VMRUN (see - * nested_svm_check_permissions). + * nested_svm_check_permissions). Note that the g_pat field is not + * validated, because (a) it may have been clobbered by SMM before + * KVM_GET_NESTED_STATE, and (b) it is not loaded at emulated + * #VMEXIT. */ __nested_copy_vmcb_save_to_cache(&save_cached, save); if (!(save->cr0 & X86_CR0_PG) || !(save->cr0 & X86_CR0_PE) || (save->rflags & X86_EFLAGS_VM) || - !nested_vmcb_check_save(vcpu, &save_cached)) + !nested_vmcb_check_save(vcpu, &save_cached, false)) goto out_free; diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 5261c3f12424..fb7360231d8b 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -167,6 +167,7 @@ struct vmcb_save_area_cached { u64 isst_addr; u64 rax; u64 cr2; + u64 g_pat; u64 dbgctl; u64 br_from; u64 br_to; From 02233c73f8ae275e80bde931173054b23082751c Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:27 -0700 Subject: [PATCH 0946/5214] KVM: x86: nSVM: Set vmcb02.g_pat correctly for nested NPT When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and nested NPT is enabled in vmcb12, copy the (cached and validated) vmcb12 g_pat field to vmcb02's g_pat, giving L2 its own independent guest PAT register. When the quirk is enabled (default), or when NPT is enabled but nested NPT is disabled, copy L1's IA32_PAT MSR to the vmcb02 g_pat field, since L2 shares the IA32_PAT MSR with L1. When NPT is disabled, the g_pat field is ignored by hardware. Fixes: 15038e147247 ("KVM: SVM: obey guest PAT") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-5-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 98701c23bc99..536252e2d1bc 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -731,9 +731,6 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; struct kvm_vcpu *vcpu = &svm->vcpu; - nested_vmcb02_compute_g_pat(svm); - vmcb_mark_dirty(vmcb02, VMCB_NPT); - /* Load the nested guest state */ if (svm->nested.vmcb12_gpa != svm->nested.last_vmcb12_gpa) { new_vmcb12 = true; @@ -764,6 +761,13 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) vmcb_mark_dirty(vmcb02, VMCB_CET); } + if (l2_has_separate_pat(vcpu)) { + if (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_NPT))) + vmcb_set_gpat(vmcb02, svm->nested.save.g_pat); + } else if (npt_enabled) { + vmcb_set_gpat(vmcb02, vcpu->arch.pat); + } + kvm_set_rflags(vcpu, save->rflags | X86_EFLAGS_FIXED); svm_set_efer(vcpu, svm->nested.save.efer); From 573321b945af85499ec4ea84d805af9a054d4629 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:28 -0700 Subject: [PATCH 0947/5214] KVM: x86: nSVM: Redirect IA32_PAT accesses to either hPAT or gPAT When handling PAT accesses from L2, route PAT accesses to either hPAT or gPAT based on whether or not L2 has a separate PAT, i.e. if KVM is actually emulating gPAT, instead of using L1's PAT for everything. Specifically, if KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled, the vCPU is in guest mode with nested NPT enabled, *and* the access if from the guest (i.e. is not from the host stuffing PAT as part of save/restore), then redirect guest PAT accesses to the gPAT "register" in vmcb02, i.e. emulate gPAT for L2. Always route non-guest accesses to hPAT, i.e. L1's PAT in vcpu->arch.pat, to ensures that KVM_{G,S}ET_MSRS and KVM_{G,S}ET_NESTED_STATE are independent of each other and can be ordered arbitrarily during save and restore. E.g. if KVM didn't exempt host accesses, then whether a write to PAT hit hPAT or gPAT would vary based on whether userspace restores PAT before or after nested state. Note, gPAT is saved and restored separately via KVM_{G,S}ET_NESTED_STATE. WARN if there's a host-initiated access to PAT from within KVM_RUN, i.e. if KVM itself initiated the access, as there are no such accesses today, and it's not clear what the "right" behavior would be. Fixes: 15038e147247 ("KVM: SVM: obey guest PAT") Signed-off-by: Jim Mattson Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 9 --------- arch/x86/kvm/svm/svm.c | 36 +++++++++++++++++++++++++++++++++--- arch/x86/kvm/svm/svm.h | 1 - 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 536252e2d1bc..e70839d4531c 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -707,15 +707,6 @@ static int nested_svm_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, return 0; } -void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm) -{ - if (!svm->nested.vmcb02.ptr) - return; - - /* FIXME: merge g_pat from vmcb01 and vmcb12. */ - vmcb_set_gpat(svm->nested.vmcb02.ptr, svm->vmcb01.ptr->save.g_pat); -} - static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) { return guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index d032d0b3d4c0..62d374ceffc1 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2786,6 +2786,20 @@ static bool sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, !msr_write_intercepted(to_svm(vcpu), msr_info->index); } +static bool svm_pat_accesses_gpat(struct kvm_vcpu *vcpu, bool from_host) +{ + /* + * When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and nested + * NPT is enabled, L2 has a separate PAT from L1. Guest accesses + * to IA32_PAT while running L2 target L2's gPAT; host-initiated + * accesses always target L1's hPAT so that KVM_GET/SET_MSRS and + * KVM_GET/SET_NESTED_STATE are independent of each other and can + * be ordered arbitrarily during save and restore. + */ + WARN_ON_ONCE(from_host && vcpu->wants_to_run); + return !from_host && is_guest_mode(vcpu) && l2_has_separate_pat(vcpu); +} + static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { struct vcpu_svm *svm = to_svm(vcpu); @@ -2902,6 +2916,12 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_AMD64_DE_CFG: msr_info->data = svm->msr_decfg; break; + case MSR_IA32_CR_PAT: + if (svm_pat_accesses_gpat(vcpu, msr_info->host_initiated)) { + msr_info->data = svm->vmcb->save.g_pat; + break; + } + return kvm_get_msr_common(vcpu, msr_info); default: return kvm_get_msr_common(vcpu, msr_info); } @@ -2985,13 +3005,23 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) break; case MSR_IA32_CR_PAT: + if (svm_pat_accesses_gpat(vcpu, msr->host_initiated)) { + if (!kvm_pat_valid(data)) + return 1; + + vmcb_set_gpat(svm->vmcb, data); + break; + } + ret = kvm_set_msr_common(vcpu, msr); if (ret) break; - vmcb_set_gpat(svm->vmcb01.ptr, data); - if (is_guest_mode(vcpu)) - nested_vmcb02_compute_g_pat(svm); + if (npt_enabled) { + vmcb_set_gpat(svm->vmcb01.ptr, data); + if (is_guest_mode(vcpu) && !l2_has_separate_pat(vcpu)) + vmcb_set_gpat(svm->vmcb, data); + } break; case MSR_IA32_SPEC_CTRL: if (!msr->host_initiated && diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index fb7360231d8b..2b6733dffd76 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -893,7 +893,6 @@ void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm, void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, struct vmcb_save_area *save); void nested_sync_control_from_vmcb02(struct vcpu_svm *svm); -void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm); void svm_switch_vmcb(struct vcpu_svm *svm, struct kvm_vmcb_info *target_vmcb); extern struct kvm_x86_nested_ops svm_nested_ops; From d65cf222b8994e2c82a8effa855f7357b1e18d1e Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:29 -0700 Subject: [PATCH 0948/5214] KVM: x86: nSVM: Save gPAT to vmcb12.g_pat on VMEXIT According to the APM volume 3 pseudo-code for "VMRUN," when nested paging is enabled in the vmcb, the guest PAT register (gPAT) is saved to the vmcb on emulated VMEXIT. When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and the vCPU is in guest mode with nested NPT enabled, save the vmcb02 g_pat field to the vmcb12 g_pat field on emulated VMEXIT. Fixes: 15038e147247 ("KVM: SVM: obey guest PAT") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-7-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index e70839d4531c..1db821181f43 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1258,6 +1258,9 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu) vmcb12->save.dr6 = svm->vcpu.arch.dr6; vmcb12->save.cpl = vmcb02->save.cpl; + if (l2_has_separate_pat(vcpu)) + vmcb12->save.g_pat = vmcb02->save.g_pat; + if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { vmcb12->save.s_cet = vmcb02->save.s_cet; vmcb12->save.isst_addr = vmcb02->save.isst_addr; From 32ebdbce3b2332f049cab5d0dc534471f2b0d7f6 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:30 -0700 Subject: [PATCH 0949/5214] KVM: Documentation: document KVM_{GET,SET}_NESTED_STATE for SVM Document the nested state constants and structures for SVM that were added by commit cc440cdad5b7 ("KVM: nSVM: implement KVM_GET_NESTED_STATE and KVM_SET_NESTED_STATE"). Fixes: cc440cdad5b7 ("KVM: nSVM: implement KVM_GET_NESTED_STATE and KVM_SET_NESTED_STATE") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-8-jmattson@google.com Signed-off-by: Sean Christopherson --- Documentation/virt/kvm/api.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 59469495c8d3..6fa731464aa2 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4944,10 +4944,13 @@ Errors: #define KVM_STATE_NESTED_FORMAT_SVM 1 #define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000 + #define KVM_STATE_NESTED_SVM_VMCB_SIZE 0x1000 #define KVM_STATE_NESTED_VMX_SMM_GUEST_MODE 0x00000001 #define KVM_STATE_NESTED_VMX_SMM_VMXON 0x00000002 + #define KVM_STATE_NESTED_GIF_SET 0x00000100 + #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 struct kvm_vmx_nested_state_hdr { @@ -4962,11 +4965,19 @@ Errors: __u64 preemption_timer_deadline; }; + struct kvm_svm_nested_state_hdr { + __u64 vmcb_pa; + }; + struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; }; + struct kvm_svm_nested_state_data { + __u8 vmcb12[KVM_STATE_NESTED_SVM_VMCB_SIZE]; + }; + This ioctl copies the vcpu's nested virtualization state from the kernel to userspace. From 4f256d5770febb9d61f9b57a4c79c491bf4987f1 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 7 Apr 2026 12:03:31 -0700 Subject: [PATCH 0950/5214] KVM: x86: nSVM: Save/restore gPAT with KVM_{GET,SET}_NESTED_STATE Add a 'gpat' field to kvm_svm_nested_state_hdr to carry L2's guest PAT value across save and restore. When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled and the vCPU is in guest mode with nested NPT enabled, save vmcb02's g_pat into the header on KVM_GET_NESTED_STATE, and restore it on KVM_SET_NESTED_STATE. Host-initiated accesses to IA32_PAT (via KVM_GET/SET_MSRS) always target L1's hPAT, so they cannot be used to save or restore gPAT. The separate header field ensures that KVM_GET/SET_MSRS and KVM_GET/SET_NESTED_STATE are independent and can be ordered arbitrarily during save and restore. Note that struct kvm_svm_nested_state_hdr is included in a union padded to 120 bytes, so there is room to add the gpat field without changing any offsets. Fixes: cc440cdad5b7 ("KVM: nSVM: implement KVM_GET_NESTED_STATE and KVM_SET_NESTED_STATE") Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260407190343.325299-9-jmattson@google.com Signed-off-by: Sean Christopherson --- Documentation/virt/kvm/api.rst | 1 + arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/kvm/svm/nested.c | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 6fa731464aa2..adfb04bbf3db 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4967,6 +4967,7 @@ Errors: struct kvm_svm_nested_state_hdr { __u64 vmcb_pa; + __u64 gpat; }; struct kvm_vmx_nested_state_data { diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 3ada2fa9ca86..1585ec804066 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -533,6 +533,7 @@ struct kvm_svm_nested_state_data { struct kvm_svm_nested_state_hdr { __u64 vmcb_pa; + __u64 gpat; }; /* for KVM_CAP_NESTED_STATE */ diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 1db821181f43..b326420ac883 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1876,6 +1876,9 @@ static int svm_get_nested_state(struct kvm_vcpu *vcpu, /* First fill in the header and copy it out. */ if (is_guest_mode(vcpu)) { kvm_state.hdr.svm.vmcb_pa = svm->nested.vmcb12_gpa; + kvm_state.hdr.svm.gpat = 0; + if (l2_has_separate_pat(vcpu)) + kvm_state.hdr.svm.gpat = svm->vmcb->save.g_pat; kvm_state.size += KVM_STATE_NESTED_SVM_VMCB_SIZE; kvm_state.flags |= KVM_STATE_NESTED_GUEST_MODE; @@ -1928,6 +1931,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, struct vmcb_save_area *save; struct vmcb_save_area_cached save_cached; struct vmcb_ctrl_area_cached ctl_cached; + bool use_separate_l2_pat; unsigned long cr0; int ret; @@ -2004,6 +2008,17 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, !nested_vmcb_check_save(vcpu, &save_cached, false)) goto out_free; + /* + * Validate gPAT when the shared PAT quirk is disabled (i.e. L2 + * has its own gPAT). This is done separately from the + * vmcb_save_area_cached validation above, because gPAT is L2 + * state, but the vmcb_save_area_cached is populated with L1 state. + */ + use_separate_l2_pat = (ctl_cached.misc_ctl & SVM_MISC_ENABLE_NP) && + !kvm_check_has_quirk(vcpu->kvm, + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); + if (use_separate_l2_pat && !kvm_pat_valid(kvm_state->hdr.svm.gpat)) + goto out_free; /* * All checks done, we can enter guest mode. Userspace provides @@ -2030,6 +2045,10 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, nested_copy_vmcb_control_to_cache(svm, ctl); svm_switch_vmcb(svm, &svm->nested.vmcb02); + + if (use_separate_l2_pat) + vmcb_set_gpat(svm->vmcb, kvm_state->hdr.svm.gpat); + nested_vmcb02_prepare_control(svm); /* From 4e90368e8680d9ddcb06f82f2e63cbbcf21cef2c Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Thu, 14 May 2026 15:52:06 +0800 Subject: [PATCH 0951/5214] soundwire: intel_auxdevice: Add es9356 to wake_capable_list Add es9356 to the wake_capable_list because it can generate jack events whilst the bus is stopped Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260514075206.3483-7-zhangyi@everest-semi.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_auxdevice.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index ee951be15465..0b8107bec9ab 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -72,6 +72,7 @@ static struct wake_capable_part wake_capable_list[] = { {0x025d, 0x717}, {0x025d, 0x721}, {0x025d, 0x722}, + {0x04b3, 0x9356}, }; static bool is_wake_capable(struct sdw_slave *slave) From acf676b9de0c86bc735a7f04962d3d688e156ffc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 8 May 2026 18:17:55 +0800 Subject: [PATCH 0952/5214] soundwire: intel: Move suspend tracking from trigger to pm suspend Mark all open DAI runtimes as suspended in the component .suspend callback instead of relying on SNDRV_PCM_TRIGGER_SUSPEND, which is not delivered during PAUSE or xrun states. If during system suspend a dai is open it means that it is in either in SUSPENDED, PAUSED or STOPPED (due to xrun) state and they will need to be re-initialized during resume (which is done in .prepare callback). Signed-off-by: Peter Ujfalusi Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260508101755.1247039-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 31 ++++++-------------------- drivers/soundwire/intel_ace2x.c | 39 ++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index dcd7440e78fa..af65214836b4 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -906,19 +906,6 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn } switch (cmd) { - case SNDRV_PCM_TRIGGER_SUSPEND: - - /* - * The .prepare callback is used to deal with xruns and resume operations. - * In the case of xruns, the DMAs and SHIM registers cannot be touched, - * but for resume operations the DMAs and SHIM registers need to be initialized. - * the .trigger callback is used to track the suspend case only. - */ - - dai_runtime->suspended = true; - - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dai_runtime->paused = true; break; @@ -955,10 +942,12 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) struct snd_soc_dai *dai; /* - * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core - * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. - * Since the component suspend is called last, we can trap this corner case - * and force the DAIs to release their resources. + * Mark all open streams as suspended. + * Open streams at this point can be in SUSPENDED, PAUSED or STOPPED + * state and during prepare the DMAs and SHIM registers need to be + * initialized for them. + * The STOPPED state is a special corner case which can happen if audio + * experiences xrun at suspend time. */ for_each_component_dais(component, dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); @@ -966,13 +955,7 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) dai_runtime = cdns->dai_runtime_array[dai->id]; - if (!dai_runtime) - continue; - - if (dai_runtime->suspended) - continue; - - if (dai_runtime->paused) + if (dai_runtime) dai_runtime->suspended = true; } diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 20422534baf1..0a97253fe0c2 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -894,19 +894,6 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn } switch (cmd) { - case SNDRV_PCM_TRIGGER_SUSPEND: - - /* - * The .prepare callback is used to deal with xruns and resume operations. - * In the case of xruns, the DMAs and SHIM registers cannot be touched, - * but for resume operations the DMAs and SHIM registers need to be initialized. - * the .trigger callback is used to track the suspend case only. - */ - - dai_runtime->suspended = true; - - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dai_runtime->paused = true; break; @@ -930,8 +917,34 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .get_stream = intel_get_sdw_stream, }; +static int intel_component_dais_suspend(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + + /* + * Mark all open streams as suspended. + * Open streams at this point can be in SUSPENDED, PAUSED or STOPPED + * state and during prepare the DMAs and SHIM registers need to be + * initialized for them. + * The STOPPED state is a special corner case which can happen if audio + * experiences xrun at suspend time. + */ + for_each_component_dais(component, dai) { + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_cdns_dai_runtime *dai_runtime; + + dai_runtime = cdns->dai_runtime_array[dai->id]; + + if (dai_runtime) + dai_runtime->suspended = true; + } + + return 0; +} + static const struct snd_soc_component_driver dai_component = { .name = "soundwire", + .suspend = intel_component_dais_suspend, }; /* From 1561f12db08b1da1fe47ce9a5beff11adfd90ac8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 11 May 2026 18:00:19 +0300 Subject: [PATCH 0953/5214] dt-bindings: phy: lynx-28g: add compatible strings per SerDes and instantiation The 28G Lynx SerDes is instantiated 3 times in the NXP LX2160A SoC and twice in the NXP LX2162A. All these instances share the same register map, but the number of lanes and the protocols supported by each lane differs in a way that isn't detectable by the programming model. For example, not all lanes of all SerDes block instantiations support 25GbE. So, using a generic "fsl,lynx-28g" compatible string and expecting all SerDes instantiations to use it was a mistake that needs to be fixed. The option chosen is to encode the SoC and the SerDes instance in the compatible string, with everything else being the responsibility of the driver to derive. An alternative considered but dismissed was to add sufficient device tree properties to describe the per-lane differences (implying: supported protocols), as well as the different lane count. Any decision made for the 28G Lynx should be consistent with the decisions taken for the yet-to-be-introduced 10G Lynx SerDes (older generation for older SoCs), because of how similar they are. I've seen the alternative at play in this unmerged patch set for the 10G Lynx here, and I didn't like it: https://lore.kernel.org/linux-phy/20230413160607.4128315-3-sean.anderson@seco.com/ This is because there, we have a higher degree of variability in the PCCR register values that need to be written per protocol. This makes that approach more drawn-out and more prone to errors, compared to the compatible strings which are more succinct and obviously correct. NXP SoC reference manuals clearly document the SerDes instantiations as not identical, and refers to them as such (SerDes 1, 2, etc). The per-SoC compatible string is prepended to the "fsl,lynx-28g" generic compatible, which is left there for compatibility with old kernels. An exception would be LX2160A SerDes #3, which at the time of writing is not described in fsl-lx2160a.dtsi. As "fsl,lx2160a-serdes3" implies it is a 28G Lynx SerDes, it makes "fsl,lynx-28g" redundant so we don't accept it. Signed-off-by: Vladimir Oltean Reviewed-by: Rob Herring (Arm) Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-2-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/fsl,lynx-28g.yaml | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml index e96229c2f8fb..8375bca810cc 100644 --- a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml @@ -9,10 +9,37 @@ title: Freescale Lynx 28G SerDes PHY maintainers: - Ioana Ciornei +description: + The Lynx 28G is a multi-lane, multi-protocol SerDes (PCIe, SATA, Ethernet) + present in multiple instances on NXP LX2160A and LX2162A SoCs. All instances + share a common register map and programming model, however they differ in + supported protocols per lane in a way that is not detectable by said + programming model without prior knowledge. The distinction is made through + the compatible string. + properties: compatible: - enum: - - fsl,lynx-28g + oneOf: + - const: fsl,lynx-28g + deprecated: true + description: + Legacy compatibility string for Lynx 28G SerDes. Any assumption + regarding whether a certain lane supports a certain protocol may + be incorrect. Deprecated except when used as a fallback. Use + device-specific strings instead. + - items: + - const: fsl,lx2160a-serdes1 + - const: fsl,lynx-28g + - items: + - const: fsl,lx2160a-serdes2 + - const: fsl,lynx-28g + - items: + - const: fsl,lx2162a-serdes1 + - const: fsl,lynx-28g + - items: + - const: fsl,lx2162a-serdes2 + - const: fsl,lynx-28g + - const: fsl,lx2160a-serdes3 reg: maxItems: 1 @@ -60,7 +87,7 @@ examples: #size-cells = <2>; serdes@1ea0000 { - compatible = "fsl,lynx-28g"; + compatible = "fsl,lx2160a-serdes1", "fsl,lynx-28g"; reg = <0x0 0x1ea0000 0x0 0x1e30>; #address-cells = <1>; #size-cells = <0>; From 37056ea140242681ccf706f63ad8c7366d89f5e3 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 11 May 2026 18:00:20 +0300 Subject: [PATCH 0954/5214] dt-bindings: phy: lynx-28g: add constraint on LX2162A lane indices The SerDes 1 of LX2162A has fewer lanes than all other instances, and strangely, their indices are not 0-3, but 4-7. This is a best-effort constraint, since we can only impose it when using per-SoC compatible string and per-lane OF nodes. Signed-off-by: Vladimir Oltean Acked-by: Conor Dooley Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-3-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/fsl,lynx-28g.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml index 8375bca810cc..d73591315d4b 100644 --- a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml @@ -78,6 +78,21 @@ required: - reg - "#phy-cells" +allOf: + # LX2162A SerDes 1 has fewer lanes than the others + - if: + properties: + compatible: + contains: + const: fsl,lx2162a-serdes1 + then: + patternProperties: + "^phy@[0-7]$": + properties: + reg: + minimum: 4 + maximum: 7 + additionalProperties: false examples: From c9d80e861034e2f3618065dc02b1de828aed24f6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 11 May 2026 18:00:21 +0300 Subject: [PATCH 0955/5214] phy: lynx-28g: require an OF node to probe The driver will gain support for variants in an upcoming change, and will use of_device_get_match_data() to deduce the running variant from the compatible string. Currently, the driver expects the schema at phy/fsl,lynx-28g.yaml, and OF-based consumers, but doesn't enforce this. And it is possible for user space to force-bind the driver to a device without OF node using the driver_override sysfs. To avoid future surprise crashes for an unsupported configuration, explicitly test for the presence of an OF node and fail probing if found. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-4-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 4ec3fb7a0d69..6d0c395d20e5 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1286,6 +1286,12 @@ static int lynx_28g_probe(struct platform_device *pdev) struct device_node *dn; int err; + dn = dev_of_node(dev); + if (!dn) { + dev_err(dev, "Device requires an OF node\n"); + return -EINVAL; + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1301,7 +1307,6 @@ static int lynx_28g_probe(struct platform_device *pdev) lynx_28g_pll_read_configuration(priv); - dn = dev_of_node(dev); if (of_get_child_count(dn)) { struct device_node *child; From 8c57247f5d9734d617862d320bd3894eec898298 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 11 May 2026 18:00:22 +0300 Subject: [PATCH 0956/5214] phy: lynx-28g: probe on per-SoC and per-instance compatible strings Add driver support for probing on the new, per-instance and per-SoC bindings, which provide the main benefit that they allow rejecting unsupported protocols per lane (10GbE on SerDes 2 lanes 0-5), but they also allow avoiding the creation of PHYs for lanes that don't exist (LX2162A lanes 0-3). For old device trees with just "fsl,lynx-28g", the only things that change are: - a probe time warning/encouragement to update the device tree. This is warranted by the fact that using "fsl,lynx-28g" may already provide incorrect behaviour (undetected absent 10GbE support on LX2160A SerDes 2 lanes 0-5). But we retain bug compatibility nonetheless. - the feature set is frozen in time (e.g. no 25GbE). Since we cannot guarantee that this protocol will work on a lane, just err on the safe side and don't offer it (and require a device tree update to get it). In terms of code, the lynx_28g_supports_lane_mode() function prototype changes. It was a SerDes-global function and now becomes per lane, to reflect the specific capabilities each instance may have. The implementation goes through priv->info->lane_supports_mode(). Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-5-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 126 +++++++++++++++++++++-- 1 file changed, 116 insertions(+), 10 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 6d0c395d20e5..5eddc2723e78 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -446,9 +446,15 @@ struct lynx_28g_lane { enum lynx_lane_mode mode; }; +struct lynx_info { + bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); + int first_lane; +}; + struct lynx_28g_priv { void __iomem *base; struct device *dev; + const struct lynx_info *info; /* Serialize concurrent access to registers shared between lanes, * like PCCn */ @@ -513,11 +519,18 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) } } -static bool lynx_28g_supports_lane_mode(struct lynx_28g_priv *priv, +/* A lane mode is supported if we have a PLL that can provide its required + * clock net, and if there is a protocol converter for that mode on that lane. + */ +static bool lynx_28g_supports_lane_mode(struct lynx_28g_lane *lane, enum lynx_lane_mode mode) { + struct lynx_28g_priv *priv = lane->priv; int i; + if (!priv->info->lane_supports_mode(lane->id, mode)) + return false; + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { if (PLLnRSTCTL_DIS(priv->pll[i].rstctl)) continue; @@ -783,6 +796,87 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) } } +static bool lx2160a_serdes1_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return true; +} + +static bool lx2160a_serdes2_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + return true; + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + return lane == 6 || lane == 7; + default: + return false; + } +} + +static bool lx2160a_serdes3_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + /* + * Non-networking SerDes, and this driver supports only + * networking protocols + */ + return false; +} + +static bool lx2162a_serdes1_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return true; +} + +static bool lx2162a_serdes2_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return lx2160a_serdes2_lane_supports_mode(lane, mode); +} + +/* Feature set is not expected to grow for the deprecated compatible string */ +static bool lynx_28g_compat_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + return true; + default: + return false; + } +} + +static const struct lynx_info lynx_info_compat = { + .lane_supports_mode = lynx_28g_compat_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes1 = { + .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes2 = { + .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes3 = { + .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes1 = { + .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, + .first_lane = 4, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes2 = { + .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, +}; + static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, u32 *val) { @@ -1035,7 +1129,6 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); - struct lynx_28g_priv *priv = lane->priv; int powered_up = lane->powered_up; enum lynx_lane_mode lane_mode; int err = 0; @@ -1047,7 +1140,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(priv, lane_mode)) + if (!lynx_28g_supports_lane_mode(lane, lane_mode)) return -EOPNOTSUPP; if (lane_mode == lane->mode) @@ -1083,14 +1176,13 @@ static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts __always_unused) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); - struct lynx_28g_priv *priv = lane->priv; enum lynx_lane_mode lane_mode; if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(priv, lane_mode)) + if (!lynx_28g_supports_lane_mode(lane, lane_mode)) return -EOPNOTSUPP; return 0; @@ -1183,7 +1275,7 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) u32 rrstctl; int err, i; - for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + for (i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { lane = &priv->lane[i]; if (!lane->phy) continue; @@ -1253,7 +1345,8 @@ static struct phy *lynx_28g_xlate(struct device *dev, idx = args->args[0]; - if (WARN_ON(idx >= LYNX_28G_NUM_LANE)) + if (WARN_ON(idx >= LYNX_28G_NUM_LANE || + idx < priv->info->first_lane)) return ERR_PTR(-EINVAL); return priv->lane[idx].phy; @@ -1297,10 +1390,18 @@ static int lynx_28g_probe(struct platform_device *pdev) return -ENOMEM; priv->dev = dev; + priv->info = of_device_get_match_data(dev); dev_set_drvdata(dev, priv); spin_lock_init(&priv->pcc_lock); INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + /* + * If we get here it means we probed on a device tree where + * "fsl,lynx-28g" wasn't the fallback, but the sole compatible string. + */ + if (priv->info == &lynx_info_compat) + dev_warn(dev, "Please update device tree to use per-device compatible strings\n"); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -1323,7 +1424,7 @@ static int lynx_28g_probe(struct platform_device *pdev) return -EINVAL; } - if (reg >= LYNX_28G_NUM_LANE) { + if (reg < priv->info->first_lane || reg >= LYNX_28G_NUM_LANE) { dev_err(dev, "\"reg\" property out of range for %pOF\n", child); of_node_put(child); return -EINVAL; @@ -1336,7 +1437,7 @@ static int lynx_28g_probe(struct platform_device *pdev) } } } else { - for (int i = 0; i < LYNX_28G_NUM_LANE; i++) { + for (int i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { err = lynx_28g_probe_lane(priv, i, NULL); if (err) return err; @@ -1362,7 +1463,12 @@ static void lynx_28g_remove(struct platform_device *pdev) } static const struct of_device_id lynx_28g_of_match_table[] = { - { .compatible = "fsl,lynx-28g" }, + { .compatible = "fsl,lx2160a-serdes1", .data = &lynx_info_lx2160a_serdes1 }, + { .compatible = "fsl,lx2160a-serdes2", .data = &lynx_info_lx2160a_serdes2 }, + { .compatible = "fsl,lx2160a-serdes3", .data = &lynx_info_lx2160a_serdes3 }, + { .compatible = "fsl,lx2162a-serdes1", .data = &lynx_info_lx2162a_serdes1 }, + { .compatible = "fsl,lx2162a-serdes2", .data = &lynx_info_lx2162a_serdes2 }, + { .compatible = "fsl,lynx-28g", .data = &lynx_info_compat }, /* fallback, keep last */ { }, }; MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table); From 1cab8fba5073962712fae3b763417af95ccf476a Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 11 May 2026 18:00:23 +0300 Subject: [PATCH 0957/5214] phy: lynx-28g: add support for 25GBASER Add support for 25GBASE-R in the Lynx 28G SerDes PHY driver. This will be used by the dpaa2-mac consumer on LX2160A with: - phy_validate(phy, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_25GBASER) to detect support. - phy_set_mode_ext(phy, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_25GBASER) to reconfigure the lane for this protocol. The intended use case for dynamic protocol switching to 25GBase-R is with SFP28 modules, and protocol switching is triggered by the SFP module insertion. There also exists a 25GBase-KR use case, where the protocol switching is covered by IEEE 802.3 clause 73 auto-negotiation. However, that is not handled here; it merely needs the support added here as basic ground work. The lane frequency for 25GbE is sourced from a clock net frequency of 12.890625 GHz, as produced by PLLF or PLLS, further multiplied by the lane by 2. The clock net frequencies produced by the PLLs are treated as read-only by the driver, so the absence of a PLL provisioned for the right clock net frequency implies absence of 25GbE support, even though a lane might have the appropriate protocol converter for it. In terms of implementation, the change consists of: - determining at probe time if any PLL was preconfigured for the required clock net frequency for 25GbE - adding the default lane parameters for reconfiguring a lane to 25GbE irrespective of the original protocol - allowing this operating mode only on supported lanes, i.e. all lanes of LX2162A SerDes #1, and LX2160A SerDes lanes 0-1, 4-7. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-6-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 90 +++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 5eddc2723e78..92bfc5f65e0b 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -57,6 +57,7 @@ #define PLLnCR1_FRATE_5G_10GVCO 0x0 #define PLLnCR1_FRATE_5G_25GVCO 0x10 #define PLLnCR1_FRATE_10G_20GVCO 0x6 +#define PLLnCR1_FRATE_12G_25GVCO 0x16 /* Per SerDes lane registers */ /* Lane a General Control Register */ @@ -64,9 +65,11 @@ #define LNaGCR0_PROTO_SEL GENMASK(7, 3) #define LNaGCR0_PROTO_SEL_SGMII 0x1 #define LNaGCR0_PROTO_SEL_XFI 0xa +#define LNaGCR0_PROTO_SEL_25G 0x1a #define LNaGCR0_IF_WIDTH GENMASK(2, 0) #define LNaGCR0_IF_WIDTH_10_BIT 0x0 #define LNaGCR0_IF_WIDTH_20_BIT 0x2 +#define LNaGCR0_IF_WIDTH_40_BIT 0x4 /* Lane a Tx Reset Control Register */ #define LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) @@ -85,6 +88,7 @@ #define LNaTGCR0_N_RATE_FULL 0x0 #define LNaTGCR0_N_RATE_HALF 0x1 #define LNaTGCR0_N_RATE_QUARTER 0x2 +#define LNaTGCR0_N_RATE_DOUBLE 0x3 #define LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30) #define LNaTECR0_EQ_TYPE GENMASK(30, 28) @@ -116,6 +120,7 @@ #define LNaRGCR0_N_RATE_FULL 0x0 #define LNaRGCR0_N_RATE_HALF 0x1 #define LNaRGCR0_N_RATE_QUARTER 0x2 +#define LNaRGCR0_N_RATE_DOUBLE 0x3 #define LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48) #define LNaRGCR1_RX_ORD_ELECIDLE BIT(31) @@ -282,6 +287,7 @@ enum lynx_lane_mode { LANE_MODE_1000BASEX_SGMII, LANE_MODE_10GBASER, LANE_MODE_USXGMII, + LANE_MODE_25GBASER, LANE_MODE_MAX, }; @@ -420,6 +426,41 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { .ttlcr0 = LNaTTLCR0_TTL_SLO_PM_BYP | LNaTTLCR0_DATA_IN_SSC, }, + [LANE_MODE_25GBASER] = { + .proto_sel = LNaGCR0_PROTO_SEL_25G, + .if_width = LNaGCR0_IF_WIDTH_40_BIT, + .teq_type = EQ_TYPE_3TAP, + .sgn_preq = 1, + .ratio_preq = 2, + .sgn_post1q = 1, + .ratio_post1q = 7, + .amp_red = 0, + .adpt_eq = 48, + .enter_idle_flt_sel = 0, + .exit_idle_flt_sel = 0, + .data_lost_th_sel = 0, + .gk2ovd = 0, + .gk3ovd = 0, + .gk4ovd = 5, + .gk2ovd_en = 0, + .gk3ovd_en = 0, + .gk4ovd_en = 1, + .eq_offset_ovd = 0x1f, + .eq_offset_ovd_en = 0, + .eq_offset_rng_dbl = 1, + .eq_blw_sel = 1, + .eq_boost = 2, + .spare_in = 3, + .smp_autoz_d1r = 2, + .smp_autoz_eg1r = 2, + .rccr0 = LNaRCCR0_CAL_EN | + LNaRCCR0_CAL_DC3_DIS | + LNaRCCR0_CAL_DC2_DIS | + LNaRCCR0_CAL_DC1_DIS | + LNaRCCR0_CAL_DC0_DIS, + .ttlcr0 = LNaTTLCR0_DATA_IN_SSC | + FIELD_PREP_CONST(LNaTTLCR0_CDR_MIN_SMP_ON, 1), + }, }; struct lynx_pccr { @@ -499,6 +540,8 @@ static const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) return "10GBase-R"; case LANE_MODE_USXGMII: return "USXGMII"; + case LANE_MODE_25GBASER: + return "25GBase-R"; default: return "unknown"; } @@ -514,6 +557,8 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) return LANE_MODE_10GBASER; case PHY_INTERFACE_MODE_USXGMII: return LANE_MODE_USXGMII; + case PHY_INTERFACE_MODE_25GBASER: + return LANE_MODE_25GBASER; default: return LANE_MODE_UNKNOWN; } @@ -601,6 +646,20 @@ static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, break; } break; + case PLLnCR1_FRATE_12G_25GVCO: + switch (lane_mode) { + case LANE_MODE_25GBASER: + lynx_28g_lane_rmw(lane, LNaTGCR0, + FIELD_PREP(LNaTGCR0_N_RATE, LNaTGCR0_N_RATE_DOUBLE), + LNaTGCR0_N_RATE); + lynx_28g_lane_rmw(lane, LNaRGCR0, + FIELD_PREP(LNaRGCR0_N_RATE, LNaRGCR0_N_RATE_DOUBLE), + LNaRGCR0_N_RATE); + break; + default: + break; + } + break; default: break; } @@ -761,6 +820,11 @@ static int lynx_28g_power_on(struct phy *phy) return 0; } +static int lynx_28g_e25g_pcvt(int lane) +{ + return 7 - lane; +} + static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane, struct lynx_pccr *pccr) { @@ -776,6 +840,11 @@ static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane, pccr->width = 4; pccr->shift = SXGMII_CFG(lane); break; + case LANE_MODE_25GBASER: + pccr->offset = PCCD; + pccr->width = 4; + pccr->shift = E25G_CFG(lynx_28g_e25g_pcvt(lane)); + break; default: return -EOPNOTSUPP; } @@ -791,6 +860,8 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) case LANE_MODE_USXGMII: case LANE_MODE_10GBASER: return SXGMIIaCR0(lane); + case LANE_MODE_25GBASER: + return E25GaCR0(lynx_28g_e25g_pcvt(lane)); default: return -EOPNOTSUPP; } @@ -799,7 +870,12 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) static bool lx2160a_serdes1_lane_supports_mode(int lane, enum lynx_lane_mode mode) { - return true; + switch (mode) { + case LANE_MODE_25GBASER: + return lane != 2 && lane != 3; + default: + return true; + } } static bool lx2160a_serdes2_lane_supports_mode(int lane, @@ -1115,6 +1191,9 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, case LANE_MODE_USXGMII: val |= PCCC_SXGMIIn_CFG; break; + case LANE_MODE_25GBASER: + val |= PCCD_E25Gn_CFG; + break; default: break; } @@ -1259,8 +1338,12 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) __set_bit(LANE_MODE_10GBASER, pll->supported); __set_bit(LANE_MODE_USXGMII, pll->supported); break; + case PLLnCR1_FRATE_12G_25GVCO: + /* 12.890625GHz clock net */ + __set_bit(LANE_MODE_25GBASER, pll->supported); + break; default: - /* 6GHz, 12.890625GHz, 8GHz */ + /* 6GHz, 8GHz */ break; } } @@ -1327,6 +1410,9 @@ static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) else lane->mode = LANE_MODE_USXGMII; break; + case LNaPSS_TYPE_25G: + lane->mode = LANE_MODE_25GBASER; + break; default: lane->mode = LANE_MODE_UNKNOWN; } From ebee9004cc0200b2b708ebf7ac625d35c71c049f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:26 +0200 Subject: [PATCH 0958/5214] phy: phy-can-transceiver: Check driver match and driver data against NULL Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override() so platform drivers that rely on the existence of a device's driver data need to verify its presence. Accordingly, add requisite match and driver data checks against NULL to the driver where they are missing. Fixes: a4a86d273ff1 ("phy: phy-can-transceiver: Add support for generic CAN transceiver driver") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 2b52e47f247a..1808f903c057 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -162,6 +162,9 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) int err, i, num_ch = 1; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + if (!match || !match->data) + return -ENODEV; + drvdata = match->data; if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) num_ch = 2; From 23db9fd578ca3b446ceaa5c9a0157f0838f4df4e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:27 +0200 Subject: [PATCH 0959/5214] phy: phy-can-transceiver: use device_get_match_data() Use the generic firmware node interface for retrieving device match data instead of the OF-specific one. While at it, drop unneeded argument to devm_phy_create() which extracts device node from the given device by default. Reviewed-by: Josua Mayer Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-3-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 1808f903c057..37b706d841ff 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -5,9 +5,9 @@ * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com * */ -#include #include #include +#include #include #include #include @@ -152,7 +152,6 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) struct can_transceiver_phy *can_transceiver_phy; struct can_transceiver_priv *priv; const struct can_transceiver_data *drvdata; - const struct of_device_id *match; struct phy *phy; struct gpio_desc *silent_gpio; struct gpio_desc *standby_gpio; @@ -161,11 +160,10 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) u32 max_bitrate = 0; int err, i, num_ch = 1; - match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); - if (!match || !match->data) + drvdata = device_get_match_data(dev); + if (!drvdata) return -ENODEV; - drvdata = match->data; if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) num_ch = 2; @@ -190,7 +188,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) can_transceiver_phy = &priv->can_transceiver_phy[i]; can_transceiver_phy->priv = priv; - phy = devm_phy_create(dev, dev->of_node, &can_transceiver_phy_ops); + phy = devm_phy_create(dev, NULL, &can_transceiver_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create can transceiver phy\n"); return PTR_ERR(phy); From 62455f6be1256084cfff8690df416f418b6f0dd2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:28 +0200 Subject: [PATCH 0960/5214] phy: phy-can-transceiver: Move OF ID table closer to their user There is no code that uses ID table directly, except the struct device_driver at the end of the file. Hence, move table closer to its user. It's always possible to access them via a pointer. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-4-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 37b706d841ff..5c9698f77c7d 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -97,35 +97,6 @@ static const struct can_transceiver_data tja1057_drvdata = { .flags = CAN_TRANSCEIVER_SILENT_PRESENT, }; -static const struct of_device_id can_transceiver_phy_ids[] = { - { - .compatible = "ti,tcan1042", - .data = &tcan1042_drvdata - }, - { - .compatible = "ti,tcan1043", - .data = &tcan1043_drvdata - }, - { - .compatible = "nxp,tja1048", - .data = &tja1048_drvdata - }, - { - .compatible = "nxp,tja1051", - .data = &tja1051_drvdata - }, - { - .compatible = "nxp,tja1057", - .data = &tja1057_drvdata - }, - { - .compatible = "nxp,tjr1443", - .data = &tcan1043_drvdata - }, - { } -}; -MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); - static struct phy *can_transceiver_phy_xlate(struct device *dev, const struct of_phandle_args *args) { @@ -232,6 +203,35 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static const struct of_device_id can_transceiver_phy_ids[] = { + { + .compatible = "ti,tcan1042", + .data = &tcan1042_drvdata + }, + { + .compatible = "ti,tcan1043", + .data = &tcan1043_drvdata + }, + { + .compatible = "nxp,tja1048", + .data = &tja1048_drvdata + }, + { + .compatible = "nxp,tja1051", + .data = &tja1051_drvdata + }, + { + .compatible = "nxp,tja1057", + .data = &tja1057_drvdata + }, + { + .compatible = "nxp,tjr1443", + .data = &tcan1043_drvdata + }, + { } +}; +MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); + static struct platform_driver can_transceiver_phy_driver = { .probe = can_transceiver_phy_probe, .driver = { From 79a5274fb39904f8a60bdd7bf7753ee1ba700210 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:29 +0200 Subject: [PATCH 0961/5214] phy: phy-can-transceiver: Don't check for specific errors when parsing properties Instead of checking for the specific error codes (that can be considered a layering violation to some extent) check for the property existence first and then either parse it, or apply a default value. With that, return an error when parsing of the existing property fails. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-5-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 5c9698f77c7d..3cebaa54f7db 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -128,8 +128,9 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) struct gpio_desc *standby_gpio; struct gpio_desc *enable_gpio; struct mux_state *mux_state; - u32 max_bitrate = 0; int err, i, num_ch = 1; + const char *propname; + u32 max_bitrate; drvdata = device_get_match_data(dev); if (!drvdata) @@ -151,9 +152,17 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->mux_state = mux_state; - err = device_property_read_u32(dev, "max-bitrate", &max_bitrate); - if ((err != -EINVAL) && !max_bitrate) - dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); + propname = "max-bitrate"; + if (device_property_present(dev, propname)) { + err = device_property_read_u32(dev, propname, &max_bitrate); + if (err) + return dev_err_probe(dev, err, "failed to parse %s\n", propname); + + if (max_bitrate == 0) + dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); + } else { + max_bitrate = 0; + } for (i = 0; i < num_ch; i++) { can_transceiver_phy = &priv->can_transceiver_phy[i]; From 05c72fbff4ac18e9bbb0e4b3884dad1f833807f4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:30 +0200 Subject: [PATCH 0962/5214] phy: phy-can-transceiver: Decouple assignment and definition in probe The code like int foo = X; ... if (bar) foo = Y; is prone to subtle mistakes and hence harder to maintain as the foo value may be changed inadvertently while code in '...' grown in lines. On top it's harder to navigate to understand the possible values of foo when branch is not taken (requires to look somewhere else in the code, far from the piece at hand). Besides that in case of taken branch the foo will be rewritten, which is not a problem per se, just an unneeded operation. Decouple assignment and definition to use if-else to address the inconveniences described above. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-6-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 3cebaa54f7db..30330499585b 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -128,8 +128,8 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) struct gpio_desc *standby_gpio; struct gpio_desc *enable_gpio; struct mux_state *mux_state; - int err, i, num_ch = 1; const char *propname; + int err, i, num_ch; u32 max_bitrate; drvdata = device_get_match_data(dev); @@ -138,6 +138,8 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) if (drvdata->flags & CAN_TRANSCEIVER_DUAL_CH) num_ch = 2; + else + num_ch = 1; priv = devm_kzalloc(dev, struct_size(priv, can_transceiver_phy, num_ch), GFP_KERNEL); if (!priv) From 52ae64602394bc9a8e7b67f5e4e70c56e31699a7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:31 +0200 Subject: [PATCH 0963/5214] phy: phy-can-transceiver: Drop unused include This file does not use the symbols from the legacy header, so let's drop it. Reviewed-by: Josua Mayer Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-7-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 30330499585b..75dc49e75ca0 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -5,12 +5,11 @@ * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com * */ +#include #include #include #include #include -#include -#include #include struct can_transceiver_data { From a896852613136e6babe6036567ab0989fb322a57 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 5 May 2026 19:04:06 +0200 Subject: [PATCH 0964/5214] dt-bindings: phy: rockchip,inno-usb2phy: Require GRF for RK3568/RV1108 Typically these Rockchip USB2 PHYs are fully contained within a single GRF. However, for RK3568 and RV1108 regs to control the USB2 PHY is located in a different GRF compared to the base address. Update this binding to require rockchip,usbgrf for RK3568 and RV1108 to properly reflect that the USB GRF is required to control the USB2 PHYs on these variants. Also disable use of rockchip,usbgrf for variants where it is not required. This should not introduce any breakage as the affected usb2phy nodes for RK3568 and RV1108 were added together with a rockchip,usbgrf phandle in their initial commit. Signed-off-by: Jonas Karlman Acked-by: Rob Herring (Arm) Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260505170410.3265305-2-heiko@sntech.de Signed-off-by: Vinod Koul --- .../bindings/phy/rockchip,inno-usb2phy.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml index 58e735b5dd05..b95c9e3e44fe 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml @@ -145,6 +145,20 @@ anyOf: - host-port allOf: + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3568-usb2phy + - rockchip,rv1108-usb2phy + then: + required: + - rockchip,usbgrf + else: + properties: + rockchip,usbgrf: false + - if: properties: compatible: From be29cd958f5393004a24cd7b5b1da88dd90a651b Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 5 May 2026 19:04:07 +0200 Subject: [PATCH 0965/5214] phy: rockchip: inno-usb2: Simplify rockchip,usbgrf handling The logic to decide if usbgrf or grf should be used is more complex than it needs to be. For RK3568, RV1108 and soon RK3528 we can assign the rockchip,usbgrf regmap directly to grf instead of doing a usbgrf and grf dance. Simplify the code to only use the grf regmap and handle the logic of what regmap should be used in driver probe instead. The only expected change from this is that RK3528 can be supported because of an addition of a of_property_present() check. Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260505170410.3265305-3-heiko@sntech.de Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 68 +++++-------------- 1 file changed, 18 insertions(+), 50 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 8f4c08e599aa..7cec45192393 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -228,7 +228,6 @@ struct rockchip_usb2phy_port { * struct rockchip_usb2phy - usb2.0 phy driver data. * @dev: pointer to device. * @grf: General Register Files regmap. - * @usbgrf: USB General Register Files regmap. * @clks: array of phy input clocks. * @clk480m: clock struct of phy output clk. * @clk480m_hw: clock struct of phy output clk management. @@ -246,7 +245,6 @@ struct rockchip_usb2phy_port { struct rockchip_usb2phy { struct device *dev; struct regmap *grf; - struct regmap *usbgrf; struct clk_bulk_data *clks; struct clk *clk480m; struct clk_hw clk480m_hw; @@ -261,11 +259,6 @@ struct rockchip_usb2phy { struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; }; -static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy) -{ - return rphy->usbgrf == NULL ? rphy->grf : rphy->usbgrf; -} - static inline int property_enable(struct regmap *base, const struct usb2phy_reg *reg, bool en) { @@ -323,12 +316,11 @@ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); - struct regmap *base = get_reg_base(rphy); int ret; /* turn on 480m clk output if it is off */ - if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) { - ret = property_enable(base, &rphy->phy_cfg->clkout_ctl, true); + if (!property_enabled(rphy->grf, &rphy->phy_cfg->clkout_ctl)) { + ret = property_enable(rphy->grf, &rphy->phy_cfg->clkout_ctl, true); if (ret) return ret; @@ -343,19 +335,17 @@ static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); - struct regmap *base = get_reg_base(rphy); /* turn off 480m clk output */ - property_enable(base, &rphy->phy_cfg->clkout_ctl, false); + property_enable(rphy->grf, &rphy->phy_cfg->clkout_ctl, false); } static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); - struct regmap *base = get_reg_base(rphy); - return property_enabled(base, &rphy->phy_cfg->clkout_ctl); + return property_enabled(rphy->grf, &rphy->phy_cfg->clkout_ctl); } static unsigned long @@ -574,7 +564,6 @@ static int rockchip_usb2phy_power_on(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - struct regmap *base = get_reg_base(rphy); int ret; dev_dbg(&rport->phy->dev, "port power on\n"); @@ -586,7 +575,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy) if (ret) return ret; - ret = property_enable(base, &rport->port_cfg->phy_sus, false); + ret = property_enable(rphy->grf, &rport->port_cfg->phy_sus, false); if (ret) { clk_disable_unprepare(rphy->clk480m); return ret; @@ -615,7 +604,6 @@ static int rockchip_usb2phy_power_off(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - struct regmap *base = get_reg_base(rphy); int ret; dev_dbg(&rport->phy->dev, "port power off\n"); @@ -623,7 +611,7 @@ static int rockchip_usb2phy_power_off(struct phy *phy) if (rport->suspended) return 0; - ret = property_enable(base, &rport->port_cfg->phy_sus, true); + ret = property_enable(rphy->grf, &rport->port_cfg->phy_sus, true); if (ret) return ret; @@ -787,28 +775,22 @@ static const char *chg_to_string(enum power_supply_type chg_type) static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, bool en) { - struct regmap *base = get_reg_base(rphy); - - property_enable(base, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); - property_enable(base, &rphy->phy_cfg->chg_det.idp_src_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.idp_src_en, en); } static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, bool en) { - struct regmap *base = get_reg_base(rphy); - - property_enable(base, &rphy->phy_cfg->chg_det.vdp_src_en, en); - property_enable(base, &rphy->phy_cfg->chg_det.idm_sink_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.vdp_src_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.idm_sink_en, en); } static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, bool en) { - struct regmap *base = get_reg_base(rphy); - - property_enable(base, &rphy->phy_cfg->chg_det.vdm_src_en, en); - property_enable(base, &rphy->phy_cfg->chg_det.idp_sink_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.vdm_src_en, en); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.idp_sink_en, en); } #define CHG_DCD_POLL_TIME (100 * HZ / 1000) @@ -820,7 +802,6 @@ static void rockchip_chg_detect_work(struct work_struct *work) struct rockchip_usb2phy_port *rport = container_of(work, struct rockchip_usb2phy_port, chg_work.work); struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - struct regmap *base = get_reg_base(rphy); bool is_dcd, tmout, vout, vbus_attach; unsigned long delay; @@ -834,7 +815,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) rockchip_usb2phy_power_off(rport->phy); /* put the controller in non-driving mode */ if (!vbus_attach) - property_enable(base, &rphy->phy_cfg->chg_det.opmode, false); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.opmode, false); /* Start DCD processing stage 1 */ rockchip_chg_enable_dcd(rphy, true); rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; @@ -898,7 +879,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) case USB_CHG_STATE_DETECTED: /* put the controller in normal mode */ if (!vbus_attach) - property_enable(base, &rphy->phy_cfg->chg_det.opmode, true); + property_enable(rphy->grf, &rphy->phy_cfg->chg_det.opmode, true); rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); dev_dbg(&rport->phy->dev, "charger = %s\n", chg_to_string(rphy->chg_type)); @@ -1353,27 +1334,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) if (!rphy) return -ENOMEM; - if (!dev->parent || !dev->parent->of_node) { + if (!dev->parent || !dev->parent->of_node || + of_property_present(np, "rockchip,usbgrf")) { rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf"); - if (IS_ERR(rphy->grf)) { - dev_err(dev, "failed to locate usbgrf\n"); - return PTR_ERR(rphy->grf); - } } else { rphy->grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(rphy->grf)) - return PTR_ERR(rphy->grf); - } - - if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) { - rphy->usbgrf = - syscon_regmap_lookup_by_phandle(dev->of_node, - "rockchip,usbgrf"); - if (IS_ERR(rphy->usbgrf)) - return PTR_ERR(rphy->usbgrf); - } else { - rphy->usbgrf = NULL; } + if (IS_ERR(rphy->grf)) + return PTR_ERR(rphy->grf); if (of_property_read_u32_index(np, "reg", 0, ®)) { dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); From c430f042f4ba2c5abd592b6924f028d3bb09d4cf Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 5 May 2026 19:04:08 +0200 Subject: [PATCH 0966/5214] dt-bindings: phy: rockchip,inno-usb2phy: Add compatible for RK3528 The embedded USB2 PHY on RK3528 is very similar to the one in RK3568, the main difference being that it only uses two clocks instead of three. Add compatible to support the USB2 PHY in RK3528. Signed-off-by: Jonas Karlman Reviewed-by: Rob Herring (Arm) Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260505170410.3265305-4-heiko@sntech.de Signed-off-by: Vinod Koul --- .../bindings/phy/rockchip,inno-usb2phy.yaml | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml index b95c9e3e44fe..f50fc69fbbe4 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml @@ -20,6 +20,7 @@ properties: - rockchip,rk3328-usb2phy - rockchip,rk3366-usb2phy - rockchip,rk3399-usb2phy + - rockchip,rk3528-usb2phy - rockchip,rk3562-usb2phy - rockchip,rk3568-usb2phy - rockchip,rk3576-usb2phy @@ -41,11 +42,15 @@ properties: maxItems: 3 clock-names: - minItems: 1 - items: + oneOf: - const: phyclk - - const: aclk - - const: aclk_slv + - items: + - const: phyclk + - const: pclk + - items: + - const: phyclk + - const: aclk + - const: aclk_slv assigned-clocks: description: @@ -65,6 +70,9 @@ properties: description: Muxed interrupt for both ports maxItems: 1 + power-domains: + maxItems: 1 + resets: maxItems: 2 @@ -150,6 +158,7 @@ allOf: compatible: contains: enum: + - rockchip,rk3528-usb2phy - rockchip,rk3568-usb2phy - rockchip,rv1108-usb2phy then: @@ -218,6 +227,19 @@ allOf: clock-names: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3528-usb2phy + then: + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + - if: properties: compatible: From 2775541de0580ab1cd077dfef710e6316563d567 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 5 May 2026 19:04:09 +0200 Subject: [PATCH 0967/5214] phy: rockchip: inno-usb2: Add clkout_ctl_phy support The 480m clk is controlled using regs in the PHY address space and not in the USB GRF address space on e.g. RK3528 and RK3506. Add a clkout_ctl_phy usb2phy_reg to handle enable/disable of the 480m clk on these SoCs. Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260505170410.3265305-5-heiko@sntech.de Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 7cec45192393..d8879fcd4291 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -179,6 +179,7 @@ struct rockchip_usb2phy_cfg { unsigned int num_ports; int (*phy_tuning)(struct rockchip_usb2phy *rphy); struct usb2phy_reg clkout_ctl; + struct usb2phy_reg clkout_ctl_phy; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; const struct rockchip_chg_det_reg chg_det; }; @@ -228,6 +229,7 @@ struct rockchip_usb2phy_port { * struct rockchip_usb2phy - usb2.0 phy driver data. * @dev: pointer to device. * @grf: General Register Files regmap. + * @phy_base: USB PHY regmap. * @clks: array of phy input clocks. * @clk480m: clock struct of phy output clk. * @clk480m_hw: clock struct of phy output clk management. @@ -245,6 +247,7 @@ struct rockchip_usb2phy_port { struct rockchip_usb2phy { struct device *dev; struct regmap *grf; + struct regmap *phy_base; struct clk_bulk_data *clks; struct clk *clk480m; struct clk_hw clk480m_hw; @@ -312,15 +315,33 @@ static void rockchip_usb2phy_clk_bulk_disable(void *data) clk_bulk_disable_unprepare(rphy->num_clks, rphy->clks); } -static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) +static void +rockchip_usb2phy_clk480m_clkout_ctl(struct clk_hw *hw, struct regmap **base, + const struct usb2phy_reg **clkout_ctl) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); + + if (rphy->phy_cfg->clkout_ctl_phy.enable) { + *base = rphy->phy_base; + *clkout_ctl = &rphy->phy_cfg->clkout_ctl_phy; + } else { + *base = rphy->grf; + *clkout_ctl = &rphy->phy_cfg->clkout_ctl; + } +} + +static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) +{ + const struct usb2phy_reg *clkout_ctl; + struct regmap *base; int ret; + rockchip_usb2phy_clk480m_clkout_ctl(hw, &base, &clkout_ctl); + /* turn on 480m clk output if it is off */ - if (!property_enabled(rphy->grf, &rphy->phy_cfg->clkout_ctl)) { - ret = property_enable(rphy->grf, &rphy->phy_cfg->clkout_ctl, true); + if (!property_enabled(base, clkout_ctl)) { + ret = property_enable(base, clkout_ctl, true); if (ret) return ret; @@ -333,19 +354,23 @@ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) { - struct rockchip_usb2phy *rphy = - container_of(hw, struct rockchip_usb2phy, clk480m_hw); + const struct usb2phy_reg *clkout_ctl; + struct regmap *base; + + rockchip_usb2phy_clk480m_clkout_ctl(hw, &base, &clkout_ctl); /* turn off 480m clk output */ - property_enable(rphy->grf, &rphy->phy_cfg->clkout_ctl, false); + property_enable(base, clkout_ctl, false); } static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) { - struct rockchip_usb2phy *rphy = - container_of(hw, struct rockchip_usb2phy, clk480m_hw); + const struct usb2phy_reg *clkout_ctl; + struct regmap *base; - return property_enabled(rphy->grf, &rphy->phy_cfg->clkout_ctl); + rockchip_usb2phy_clk480m_clkout_ctl(hw, &base, &clkout_ctl); + + return property_enabled(base, clkout_ctl); } static unsigned long @@ -1336,9 +1361,13 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) if (!dev->parent || !dev->parent->of_node || of_property_present(np, "rockchip,usbgrf")) { + rphy->phy_base = device_node_to_regmap(np); + if (IS_ERR(rphy->phy_base)) + return PTR_ERR(rphy->phy_base); rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf"); } else { rphy->grf = syscon_node_to_regmap(dev->parent->of_node); + rphy->phy_base = rphy->grf; } if (IS_ERR(rphy->grf)) return PTR_ERR(rphy->grf); From 864b3617df827865a95a06f06f09a8d57a795b91 Mon Sep 17 00:00:00 2001 From: Jianwei Zheng Date: Tue, 5 May 2026 19:04:10 +0200 Subject: [PATCH 0968/5214] phy: rockchip: inno-usb2: Add support for RK3528 The RK3528 has a single USB2PHY with a otg and host port. Add support for the RK3528 variant of USB2PHY. PHY tuning for RK3528: - Turn off differential receiver in suspend mode to save power consumption. - Set HS eye-height to 400mV instead of default 450mV. - Choose the Tx fs/ls data as linestate from TX driver for otg port which uses dwc3 controller to improve fs/ls devices compatibility with long cables. Undocumented magic-values are based on the linux-stan-6.1-rkr5 tag of the vendor-kernel. Signed-off-by: Jianwei Zheng Signed-off-by: Jonas Karlman Signed-off-by: Heiko Stuebner Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260505170410.3265305-6-heiko@sntech.de Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index d8879fcd4291..133cfd6624e8 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1511,6 +1511,38 @@ static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) BIT(2) << BIT_WRITEABLE_SHIFT | 0); } +static int rk3528_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + int ret; + + /* Turn off otg port differential receiver in suspend mode */ + ret = regmap_write(rphy->phy_base, 0x30, BIT(18) | 0x0000); + if (ret) + return ret; + + /* Turn off host port differential receiver in suspend mode */ + ret = regmap_write(rphy->phy_base, 0x430, BIT(18) | 0x0000); + if (ret) + return ret; + + /* Set otg port HS eye height to 400mv (default is 450mv) */ + ret = regmap_write(rphy->phy_base, 0x30, GENMASK(22, 20) | 0x0000); + if (ret) + return ret; + + /* Set host port HS eye height to 400mv (default is 450mv) */ + ret = regmap_write(rphy->phy_base, 0x430, GENMASK(22, 20) | 0x0000); + if (ret) + return ret; + + /* Choose the Tx fs/ls data as linestate from TX driver for otg port */ + ret = regmap_write(rphy->phy_base, 0x94, GENMASK(22, 19) | 0x0018); + if (ret) + return ret; + + return 0; +} + static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy) { int ret; @@ -1924,6 +1956,57 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = { + { + .reg = 0xffdf0000, + .num_ports = 2, + .phy_tuning = rk3528_usb2phy_tuning, + .clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x004c, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0074, 3, 2, 0, 3 }, + .bvalid_det_st = { 0x0078, 3, 2, 0, 3 }, + .bvalid_det_clr = { 0x007c, 3, 2, 0, 3 }, + .idfall_det_en = { 0x0074, 5, 5, 0, 1 }, + .idfall_det_st = { 0x0078, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x007c, 5, 5, 0, 1 }, + .idrise_det_en = { 0x0074, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0078, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x007c, 4, 4, 0, 1 }, + .ls_det_en = { 0x0074, 0, 0, 0, 1 }, + .ls_det_st = { 0x0078, 0, 0, 0, 1 }, + .ls_det_clr = { 0x007c, 0, 0, 0, 1 }, + .utmi_avalid = { 0x006c, 1, 1, 0, 1 }, + .utmi_bvalid = { 0x006c, 0, 0, 0, 1 }, + .utmi_id = { 0x006c, 6, 6, 0, 1 }, + .utmi_ls = { 0x006c, 5, 4, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x005c, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0090, 0, 0, 0, 1 }, + .ls_det_st = { 0x0094, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0098, 0, 0, 0, 1 }, + .utmi_ls = { 0x006c, 13, 12, 0, 1 }, + .utmi_hstdet = { 0x006c, 15, 15, 0, 1 }, + } + }, + .chg_det = { + .opmode = { 0x004c, 3, 0, 5, 1 }, + .cp_det = { 0x006c, 19, 19, 0, 1 }, + .dcp_det = { 0x006c, 18, 18, 0, 1 }, + .dp_det = { 0x006c, 20, 20, 0, 1 }, + .idm_sink_en = { 0x0058, 1, 1, 0, 1 }, + .idp_sink_en = { 0x0058, 0, 0, 0, 1 }, + .idp_src_en = { 0x0058, 2, 2, 0, 1 }, + .rdm_pdwn_en = { 0x0058, 3, 3, 0, 1 }, + .vdm_src_en = { 0x0058, 5, 5, 0, 1 }, + .vdp_src_en = { 0x0058, 4, 4, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3562_phy_cfgs[] = { { .reg = 0xff740000, @@ -2291,6 +2374,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, + { .compatible = "rockchip,rk3528-usb2phy", .data = &rk3528_phy_cfgs }, { .compatible = "rockchip,rk3562-usb2phy", .data = &rk3562_phy_cfgs }, { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, { .compatible = "rockchip,rk3576-usb2phy", .data = &rk3576_phy_cfgs }, From 2cc066dbd3a211628b40967949cff2790d0f62eb Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sat, 25 Apr 2026 17:36:41 +0000 Subject: [PATCH 0969/5214] dt-bindings: phy: Document PCIe PHY in EcoNet EN751221 and EN7528 EN751221 and EN7528 SoCs have two PCIe slots, and each one has a PHY which behaves slightly differently because one slot is Gen1/Gen2 while the other is Gen1 only. Signed-off-by: Caleb James DeLisle Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260425173642.406089-2-cjd@cjdns.fr Signed-off-by: Vinod Koul --- .../phy/econet,en751221-pcie-phy.yaml | 50 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml new file mode 100644 index 000000000000..987d396c1c64 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/econet,en751221-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: EcoNet PCI-Express PHY for EcoNet EN751221 and EN7528 + +maintainers: + - Caleb James DeLisle + +description: + The PCIe PHY supports physical layer functionality for PCIe Gen1 and + Gen1/Gen2 ports. On these SoCs, port 0 is a Gen1-only port while + port 1 is Gen1/Gen2 capable. + +properties: + compatible: + enum: + - econet,en751221-pcie-gen1 + - econet,en751221-pcie-gen2 + - econet,en7528-pcie-gen1 + - econet,en7528-pcie-gen2 + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <1>; + #size-cells = <1>; + + pcie-phy@1faf2000 { + compatible = "econet,en7528-pcie-gen1"; + reg = <0x1faf2000 0x1000>; + #phy-cells = <0>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 421dd786b399..e6e729b13de2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9153,6 +9153,12 @@ F: drivers/irqchip/irq-econet-en751221.c F: include/dt-bindings/clock/econet,en751221-scu.h F: include/dt-bindings/reset/econet,en751221-scu.h +ECONET PCIE PHY DRIVER +M: Caleb James DeLisle +L: linux-mips@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml + ECRYPT FILE SYSTEM M: Tyler Hicks L: ecryptfs@vger.kernel.org From da1af49257591190ad6521abb3198f1f40420407 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Sat, 25 Apr 2026 17:36:42 +0000 Subject: [PATCH 0970/5214] phy: econet: Add PCIe PHY driver for EcoNet EN751221 and EN7528 SoCs. Introduce support for EcoNet PCIe PHY controllers found in EN751221 and EN7528 SoCs, these SoCs are not identical but are similar, each having one Gen1 port, and one Gen1/Gen2 port. Co-developed-by: Ahmed Naseef Signed-off-by: Ahmed Naseef [cjd@cjdns.fr: add EN751221 support and refactor for clarity] Signed-off-by: Caleb James DeLisle Link: https://patch.msgid.link/20260425173642.406089-3-cjd@cjdns.fr Signed-off-by: Vinod Koul --- MAINTAINERS | 1 + drivers/phy/Kconfig | 12 +++ drivers/phy/Makefile | 1 + drivers/phy/phy-econet-pcie.c | 182 ++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 drivers/phy/phy-econet-pcie.c diff --git a/MAINTAINERS b/MAINTAINERS index e6e729b13de2..8ae7578a0532 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9158,6 +9158,7 @@ M: Caleb James DeLisle L: linux-mips@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/phy/econet,en751221-pcie-phy.yaml +F: drivers/phy/phy-econet-pcie.c ECRYPT FILE SYSTEM M: Tyler Hicks diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index ab96ee5858c1..7ef342f4bbb6 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -79,6 +79,18 @@ config PHY_EYEQ5_ETH shared register region called OLB. If M is selected, the module will be called phy-eyeq5-eth. +config PHY_ECONET_PCIE + tristate "EcoNet PCIe-PHY Driver" + depends on ECONET || COMPILE_TEST + depends on OF + select GENERIC_PHY + select REGMAP_MMIO + help + Say Y here to add support for EcoNet PCIe PHY driver. + This driver create the basic PHY instance and provides initialize + callback for PCIe GEN1 and GEN2 ports. This PHY is found on + EcoNet SoCs including EN751221 and EN7528. + config PHY_GOOGLE_USB tristate "Google Tensor SoC USB PHY driver" select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f31767745123..1e69ed50fb17 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o +obj-$(CONFIG_PHY_ECONET_PCIE) += phy-econet-pcie.o obj-$(CONFIG_PHY_EYEQ5_ETH) += phy-eyeq5-eth.o obj-$(CONFIG_PHY_GOOGLE_USB) += phy-google-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o diff --git a/drivers/phy/phy-econet-pcie.c b/drivers/phy/phy-econet-pcie.c new file mode 100644 index 000000000000..96be69d1ffeb --- /dev/null +++ b/drivers/phy/phy-econet-pcie.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Author: Caleb James DeLisle + * Ahmed Naseef + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Rx detection timing for EN751221: 16*8 clock cycles */ +#define EN751221_RXDET_VAL 16 + +/* Rx detection timing when in power mode 3 */ +#define EN75_RXDET_P3_REG 0xa28 +#define EN75_RXDET_P3_MASK GENMASK(17, 9) + +/* Rx detection timing when in power mode 2 */ +#define EN75_RXDET_P2_REG 0xa2c +#define EN75_RXDET_P2_MASK GENMASK(8, 0) + +/* Rx impedance */ +#define EN75_RX_IMPEDANCE_REG 0xb2c +#define EN75_RX_IMPEDANCE_MASK GENMASK(13, 12) +enum en75_rx_impedance { + EN75_RX_IMPEDANCE_100_OHM = 0, + EN75_RX_IMPEDANCE_95_OHM = 1, + EN75_RX_IMPEDANCE_90_OHM = 2, +}; + +/* PLL Invert clock */ +#define EN75_PLL_PH_INV_REG 0x4a0 +#define EN75_PLL_PH_INV_MASK BIT(5) + +struct en75_phy_op { + u32 reg; + u32 mask; + u32 val; +}; + +struct en7528_pcie_phy { + struct regmap *regmap; + const struct en75_phy_op *data; +}; + +/* Port 0 PHY: set LCDDS_CLK_PH_INV for PLL operation */ +static const struct en75_phy_op en7528_phy_gen1[] = { + { + .reg = EN75_PLL_PH_INV_REG, + .mask = EN75_PLL_PH_INV_MASK, + .val = 1, + }, + { /* sentinel */ } +}; + +/* EN7528 Port 1 PHY: Rx impedance tuning, target R -5 Ohm */ +static const struct en75_phy_op en7528_phy_gen2[] = { + { + .reg = EN75_RX_IMPEDANCE_REG, + .mask = EN75_RX_IMPEDANCE_MASK, + .val = EN75_RX_IMPEDANCE_95_OHM, + }, + { /* sentinel */ } +}; + +/* EN751221 Port 1 PHY, set RX detect to 16*8 clock cycles */ +static const struct en75_phy_op en751221_phy_gen2[] = { + { + .reg = EN75_RXDET_P3_REG, + .mask = EN75_RXDET_P3_MASK, + .val = EN751221_RXDET_VAL, + }, + { + .reg = EN75_RXDET_P2_REG, + .mask = EN75_RXDET_P2_MASK, + .val = EN751221_RXDET_VAL, + }, + { /* sentinel */ } +}; + +static int en75_pcie_phy_init(struct phy *phy) +{ + struct en7528_pcie_phy *ephy = phy_get_drvdata(phy); + const struct en75_phy_op *data = ephy->data; + int i, ret; + u32 val; + + for (i = 0; data[i].mask || data[i].val; i++) { + if (i) + usleep_range(1000, 2000); + + val = field_prep(data[i].mask, data[i].val); + + ret = regmap_update_bits(ephy->regmap, data[i].reg, + data[i].mask, val); + if (ret) + return ret; + } + + return 0; +} + +static const struct phy_ops en75_pcie_phy_ops = { + .init = en75_pcie_phy_init, + .owner = THIS_MODULE, +}; + +static int en75_pcie_phy_probe(struct platform_device *pdev) +{ + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + struct device *dev = &pdev->dev; + const struct en75_phy_op *data; + struct phy_provider *provider; + struct en7528_pcie_phy *ephy; + void __iomem *base; + struct phy *phy; + int i; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL); + if (!ephy) + return -ENOMEM; + + ephy->data = data; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* Set max_register to highest used register */ + for (i = 0; data[i].mask || data[i].val; i++) + if (data[i].reg > regmap_config.max_register) + regmap_config.max_register = data[i].reg; + + ephy->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(ephy->regmap)) + return PTR_ERR(ephy->regmap); + + phy = devm_phy_create(dev, dev->of_node, &en75_pcie_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, ephy); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id en75_pcie_phy_ids[] = { + { .compatible = "econet,en7528-pcie-gen1", .data = en7528_phy_gen1 }, + { .compatible = "econet,en7528-pcie-gen2", .data = en7528_phy_gen2 }, + { .compatible = "econet,en751221-pcie-gen1", .data = en7528_phy_gen1 }, + { .compatible = "econet,en751221-pcie-gen2", .data = en751221_phy_gen2 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, en75_pcie_phy_ids); + +static struct platform_driver en75_pcie_phy_driver = { + .probe = en75_pcie_phy_probe, + .driver = { + .name = "econet-pcie-phy", + .of_match_table = en75_pcie_phy_ids, + }, +}; +module_platform_driver(en75_pcie_phy_driver); + +MODULE_AUTHOR("Caleb James DeLisle "); +MODULE_DESCRIPTION("EcoNet PCIe PHY driver"); +MODULE_LICENSE("GPL"); From deb281cec3cbef5f02c6bc29fa5cd51f6d5be10f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 15 Mar 2026 14:49:30 -0700 Subject: [PATCH 0971/5214] phy: qcom-usb-hs: use flexible array member Simplify allocation by removing kmalloc_array and just doing kzalloc. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260315214930.4621-1-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-usb-hs.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c index 98a18987f1be..928a982a8a76 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -34,9 +34,9 @@ struct qcom_usb_hs_phy { struct regulator *v1p8; struct regulator *v3p3; struct reset_control *reset; - struct ulpi_seq *init_seq; struct extcon_dev *vbus_edev; struct notifier_block vbus_notify; + struct ulpi_seq init_seq[]; }; static int qcom_usb_hs_phy_set_mode(struct phy *phy, @@ -209,19 +209,16 @@ static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) int size; int ret; - uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); + if (size < 0) + size = 0; + + uphy = devm_kzalloc(&ulpi->dev, struct_size(uphy, init_seq, (size / 2) + 1), GFP_KERNEL); if (!uphy) return -ENOMEM; ulpi_set_drvdata(ulpi, uphy); uphy->ulpi = ulpi; - size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); - if (size < 0) - size = 0; - uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, - sizeof(*uphy->init_seq), GFP_KERNEL); - if (!uphy->init_seq) - return -ENOMEM; ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", (u8 *)uphy->init_seq, size); if (ret && size) From fd6cd05ceabdf67635a4cef5145f79d1217bf11b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 3 Mar 2026 20:34:20 -0800 Subject: [PATCH 0972/5214] phy: mediatek: xsphy: reduce main allocation Instead of kzalloc and kcalloc, we can use a flex array to reduce to a single allocation. Also added __counted_by() for extra possible analysis. Signed-off-by: Rosen Penev Reviewed-by: Gustavo A. R. Silva Link: https://patch.msgid.link/20260304043420.14151-1-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-xsphy.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c index c0ddb9273cc3..cc1d66954212 100644 --- a/drivers/phy/mediatek/phy-mtk-xsphy.c +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -112,10 +112,10 @@ struct xsphy_instance { struct mtk_xsphy { struct device *dev; void __iomem *glb_base; /* only shared u3 sif */ - struct xsphy_instance **phys; - int nphys; int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */ int src_coef; /* coefficient for slew rate calibrate */ + int nphys; + struct xsphy_instance *phys[] __counted_by(nphys); }; static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, @@ -515,18 +515,15 @@ static int mtk_xsphy_probe(struct platform_device *pdev) struct resource *glb_res; struct mtk_xsphy *xsphy; struct resource res; + size_t nphys; int port; - xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); + nphys = of_get_child_count(np); + xsphy = devm_kzalloc(dev, struct_size(xsphy, phys, nphys), GFP_KERNEL); if (!xsphy) return -ENOMEM; - xsphy->nphys = of_get_child_count(np); - xsphy->phys = devm_kcalloc(dev, xsphy->nphys, - sizeof(*xsphy->phys), GFP_KERNEL); - if (!xsphy->phys) - return -ENOMEM; - + xsphy->nphys = nphys; xsphy->dev = dev; platform_set_drvdata(pdev, xsphy); From 44984aaf1aa727ff944dd4b72fcf069d08b0056d Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Thu, 30 Apr 2026 12:53:27 +0100 Subject: [PATCH 0973/5214] clk: samsung: exynos850: mark APM I3C clocks as critical The Exynos850 APM co-processor relies on the I3C bus to communicate with the PMIC. Currently, there is no dedicated PMIC consumer driver managing these clocks, so the clock subsystem automatically gates them during the initialisation. Once gated, any subsequent ACPM communication with APM results in timeouts. As a temporary workaround (and let's hope it doesn't become permanent), mark both `gout_i3c_pclk` and `gout_i3c_sclk` as CLK_IS_CRITICAL ones to prevent the clock subsystem from disabling them. This makes the ACPM communication functional. This workaround should be reverted once a proper ACPM PMIC driver is implemented to manage these clocks. Cc: Sam Protsenko Cc: Tudor Ambarus Signed-off-by: Alexey Klimov Reviewed-by: Sam Protsenko Reviewed-by: Tudor Ambarus Link: https://patch.msgid.link/20260430-exynos850-i3c-criticalclocks-v1-1-6e1fd8dfa21b@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynos850.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos850.c b/drivers/clk/samsung/clk-exynos850.c index eb9c80b60225..b143a42293f5 100644 --- a/drivers/clk/samsung/clk-exynos850.c +++ b/drivers/clk/samsung/clk-exynos850.c @@ -686,10 +686,11 @@ static const struct samsung_gate_clock apm_gate_clks[] __initconst = { CLK_CON_GAT_GOUT_APM_APBIF_RTC_PCLK, 21, 0, 0), GATE(CLK_GOUT_TOP_RTC_PCLK, "gout_top_rtc_pclk", "dout_apm_bus", CLK_CON_GAT_GOUT_APM_APBIF_TOP_RTC_PCLK, 21, 0, 0), + /* TODO: Should be dealt with or enabled in PMIC ACPM driver */ GATE(CLK_GOUT_I3C_PCLK, "gout_i3c_pclk", "dout_apm_bus", - CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_PCLK, 21, 0, 0), + CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_PCLK, 21, CLK_IS_CRITICAL, 0), GATE(CLK_GOUT_I3C_SCLK, "gout_i3c_sclk", "mout_apm_i3c", - CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK, 21, 0, 0), + CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK, 21, CLK_IS_CRITICAL, 0), GATE(CLK_GOUT_SPEEDY_PCLK, "gout_speedy_pclk", "dout_apm_bus", CLK_CON_GAT_GOUT_APM_SPEEDY_APM_PCLK, 21, 0, 0), /* TODO: Should be enabled in GPIO driver (or made CLK_IS_CRITICAL) */ From 165daffc7c33c94b0b7ef7f5719531f1f8bc0a3a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 11 May 2026 22:41:40 -0700 Subject: [PATCH 0974/5214] perf record: Refactor ARM64 leaf caller setup out of arch Code in tools/perf/arch causes portability issues/opaqueness and LTO issues due to the use of weak symbols. Move the adding of LR to the sample_user_regs into arm64-frame-pointer-unwind-support.c conditional on EM_HOST == EM_AARCH64 (false on all non-ARM64 builds). This also better encapsulates the use of the sampled registers by get_leaf_frame_caller_aarch64 and the set up by the new add_leaf_frame_caller_opts_aarch64, exposing opportunities for possibly sampling PC and SP to help the unwinder. Reviewed-by: James Clark Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Dapeng Mi Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Shimin Guo Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/util/Build | 1 - tools/perf/arch/arm64/util/machine.c | 12 ------------ tools/perf/builtin-record.c | 11 +++++------ tools/perf/util/arm64-frame-pointer-unwind-support.c | 6 ++++++ tools/perf/util/arm64-frame-pointer-unwind-support.h | 2 ++ tools/perf/util/callchain.h | 2 -- 6 files changed, 13 insertions(+), 21 deletions(-) delete mode 100644 tools/perf/arch/arm64/util/machine.c diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index 4e06a08d281a..638aa6948ab5 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -5,7 +5,6 @@ perf-util-y += ../../arm/util/pmu.o perf-util-y += arm-spe.o perf-util-y += header.o perf-util-y += hisi-ptt.o -perf-util-y += machine.o perf-util-y += mem-events.o perf-util-y += pmu.o perf-util-y += tsc.o diff --git a/tools/perf/arch/arm64/util/machine.c b/tools/perf/arch/arm64/util/machine.c deleted file mode 100644 index 80fb13c958d9..000000000000 --- a/tools/perf/arch/arm64/util/machine.c +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "callchain.h" // prototype of arch__add_leaf_frame_record_opts -#include "perf_regs.h" -#include "record.h" - -#define SMPL_REG_MASK(b) (1ULL << (b)) - -void arch__add_leaf_frame_record_opts(struct record_opts *opts) -{ - opts->sample_user_regs |= SMPL_REG_MASK(PERF_REG_ARM64_LR); -} diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4a5eba498c02..272bba7f4b9e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -14,6 +14,7 @@ #include "util/parse-events.h" #include "util/config.h" +#include "util/arm64-frame-pointer-unwind-support.h" #include "util/callchain.h" #include "util/cgroup.h" #include "util/header.h" @@ -3230,10 +3231,6 @@ static int record__parse_off_cpu_thresh(const struct option *opt, return 0; } -void __weak arch__add_leaf_frame_record_opts(struct record_opts *opts __maybe_unused) -{ -} - static int parse_control_option(const struct option *opt, const char *str, int unset __maybe_unused) @@ -4319,8 +4316,10 @@ int cmd_record(int argc, const char **argv) evlist__warn_user_requested_cpus(rec->evlist, rec->opts.target.cpu_list); - if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP) - arch__add_leaf_frame_record_opts(&rec->opts); + if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP) { + if (EM_HOST == EM_AARCH64) + add_leaf_frame_caller_opts_aarch64(&rec->opts); + } err = -ENOMEM; if (evlist__create_maps(rec->evlist, &rec->opts.target) < 0) { diff --git a/tools/perf/util/arm64-frame-pointer-unwind-support.c b/tools/perf/util/arm64-frame-pointer-unwind-support.c index 858ce2b01812..3af8c7a466e0 100644 --- a/tools/perf/util/arm64-frame-pointer-unwind-support.c +++ b/tools/perf/util/arm64-frame-pointer-unwind-support.c @@ -2,6 +2,7 @@ #include "arm64-frame-pointer-unwind-support.h" #include "callchain.h" #include "event.h" +#include "record.h" #include "unwind.h" #include @@ -16,6 +17,11 @@ struct entries { #define SMPL_REG_MASK(b) (1ULL << (b)) +void add_leaf_frame_caller_opts_aarch64(struct record_opts *opts) +{ + opts->sample_user_regs |= SMPL_REG_MASK(PERF_REG_ARM64_LR); +} + static bool get_leaf_frame_caller_enabled(struct perf_sample *sample) { struct regs_dump *regs; diff --git a/tools/perf/util/arm64-frame-pointer-unwind-support.h b/tools/perf/util/arm64-frame-pointer-unwind-support.h index 42d3a45490f5..ba35b295bfcd 100644 --- a/tools/perf/util/arm64-frame-pointer-unwind-support.h +++ b/tools/perf/util/arm64-frame-pointer-unwind-support.h @@ -5,8 +5,10 @@ #include struct perf_sample; +struct record_opts; struct thread; +void add_leaf_frame_caller_opts_aarch64(struct record_opts *opts); u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thread, int user_idx); #endif /* __PERF_ARM_FRAME_POINTER_UNWIND_SUPPORT_H */ diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 06d463ccc7a0..b7702d65ad60 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -277,8 +277,6 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, } #endif -void arch__add_leaf_frame_record_opts(struct record_opts *opts); - char *callchain_list__sym_name(struct callchain_list *cl, char *bf, size_t bfsize, bool show_dso); char *callchain_node__scnprintf_value(struct callchain_node *node, From 41a543c86d110073275e5294852d692e5faf6b3b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 14 Apr 2026 10:58:55 -0700 Subject: [PATCH 0975/5214] perf pmu-events AMD: Switch l2_itlb_misses to bp_l1_tlb_miss_l2_tlb_miss.all l2_itlb_misses is a valid legacy cache event name, hence allowing it in all_events in metric.py. l2_itlb_misses was also a json event for AMD zen1, zen2 and zen3. For zen4, zen5 and zen6 the checking that metric events are within the json was skipping l2_itlb_misses as it is a valid legacy event, however, the PMU driver lacks the event mapping causing it to be a bad event when used in the metric. Add bp_l1_tlb_miss_l2_tlb_miss.all as the l2 itlb miss event (bp = branch predictor, the AMD way to say itlb), so that is used in preference to l2_itlb_misses when the event exists. Remove l2_itlb_misses from metric.py as the legacy event isn't used by any metrics and having it is error prone for newer AMD zen models. Fixes: e596f329668ec2b5 ("perf jevents: Add itlb metric group for AMD") Reviewed-by: Sandipan Das Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/amd_metrics.py | 2 +- tools/perf/pmu-events/metric.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index e2defaffde3e..971f6e7af1f8 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -268,7 +268,7 @@ def AmdDtlb() -> Optional[MetricGroup]: def AmdItlb(): global _zen_model l2h = Event("bp_l1_tlb_miss_l2_tlb_hit", "bp_l1_tlb_miss_l2_hit") - l2m = Event("l2_itlb_misses") + l2m = Event("bp_l1_tlb_miss_l2_tlb_miss.all", "l2_itlb_misses",) l2r = l2h + l2m itlb_l1_mg = None diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 585454828c2f..ac582db785fc 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -25,7 +25,6 @@ def LoadEvents(directory: str) -> None: "cycles", "duration_time", "instructions", - "l2_itlb_misses", } for file in os.listdir(os.fsencode(directory)): filename = os.fsdecode(file) From e356a67cc4b6af9440a0d86eb6b9bbaa92d42ef4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 30 Apr 2026 09:17:25 -0700 Subject: [PATCH 0976/5214] perf metricgroup: Avoid scanning unnecessary PMUs for identifier match Only uncore PMUs can have an identifier, so add an optimized perf_pmus__scan routine for that case to avoid all PMU types being created. Reviewed-by: James Clark Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: Leo Yan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/metricgroup.c | 11 +++-------- tools/perf/util/pmus.c | 18 +++++++++++++++++- tools/perf/util/pmus.h | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 4db9578efd81..5a489e97c413 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -415,14 +415,9 @@ static int metricgroup__sys_event_iter(const struct pmu_metric *pm, if (!pm->metric_expr || !pm->compat) return 0; - while ((pmu = perf_pmus__scan(pmu))) { - - if (!pmu->id || !pmu_uncore_identifier_match(pm->compat, pmu->id)) - continue; - - return d->fn(pm, table, d->data); - } - return 0; + /* Only process with the iterator if there is a a PMU that matches the ID. */ + pmu = perf_pmus__scan_for_uncore_id(pmu, pm->compat); + return pmu ? d->fn(pm, table, d->data) : 0; } int metricgroup__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 9a2023ceeefd..5e3f571450fe 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -409,7 +409,7 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const c if (!pmu) { /* * Core PMUs, other sysfs PMUs and tool PMU can have any name or - * aren't wother optimizing for. + * aren't worth optimizing for. */ unsigned int to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | @@ -486,6 +486,22 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) return NULL; } +struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmu *pmu, const char *compat) +{ + if (!pmu) { + /* Only uncore PMUs can have identifiers. */ + unsigned int to_read_pmus = PERF_TOOL_PMU_TYPE_PE_OTHER_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu = list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) { + if (pmu->id && pmu_uncore_identifier_match(compat, pmu->id)) + return pmu; + } + return NULL; +} + const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) { struct perf_pmu *pmu = NULL; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 7cb36863711a..0d55edb3f2fc 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -23,6 +23,7 @@ struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event); struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard); +struct perf_pmu *perf_pmus__scan_for_uncore_id(struct perf_pmu *pmu, const char *compat); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); From 195254adeddc30c5a892a1cc9528a6ed5e841224 Mon Sep 17 00:00:00 2001 From: Evgenii Burenchev Date: Wed, 29 Apr 2026 12:52:12 +0300 Subject: [PATCH 0977/5214] scsi: snic: vnic_dev: Remove dead store in vnic_dev_discover_res() The assignment 'len = count' for RES_TYPE_INTR_PBA_LEGACY, RES_TYPE_DEVCMD, and RES_TYPE_DEVCMD2 cases is never used. Drop the unused assignments to fix the following static analyzer warning. No functional change. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Evgenii Burenchev Acked-by: Narsimhulu Musini Link: https://patch.msgid.link/20260429095212.11251-1-evg28bur@yandex.ru Signed-off-by: Martin K. Petersen --- drivers/scsi/snic/vnic_dev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/snic/vnic_dev.c b/drivers/scsi/snic/vnic_dev.c index ed7771e62854..22303f827583 100644 --- a/drivers/scsi/snic/vnic_dev.c +++ b/drivers/scsi/snic/vnic_dev.c @@ -132,7 +132,6 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev, case RES_TYPE_INTR_PBA_LEGACY: case RES_TYPE_DEVCMD: case RES_TYPE_DEVCMD2: - len = count; break; default: From 2a18c57560f454e2e63373ecf00e4a6fb0265600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 5 May 2026 10:28:52 +0200 Subject: [PATCH 0978/5214] scsi: ufs: tc-dwc-g210-pci: Simplify initialization of pci_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A list initializer is hard to parse for a human if they don't see or know the order of the members of struct pci_device_id. So use the PCI_VDEVICE macro which is much more idiomatic and skip assigning explicit zeros. There are no changes to the compiled result of the array; verified with builds for x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/ff015bf46ad395702f40c85c8359fd24957e7224.1777968942.git.u.kleine-koenig@baylibre.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/tc-dwc-g210-pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c index 0167d8bef71a..c6d89f9c44ae 100644 --- a/drivers/ufs/host/tc-dwc-g210-pci.c +++ b/drivers/ufs/host/tc-dwc-g210-pci.c @@ -114,8 +114,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = { }; static const struct pci_device_id tc_dwc_g210_pci_tbl[] = { - { PCI_VENDOR_ID_SYNOPSYS, 0xB101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_SYNOPSYS, 0xB102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VDEVICE(SYNOPSYS, 0xB101) }, + { PCI_VDEVICE(SYNOPSYS, 0xB102) }, { } /* terminate list */ }; From 8ef4c72dbbfda41b8f83a9b5a275feaf4a30ea21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 5 May 2026 10:28:53 +0200 Subject: [PATCH 0979/5214] scsi: ufs: ufshcd-pci: Use PCI_VDEVICE and named initializers for pci array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pci_device_id array uses a mixture of ways to initialize ufshcd_pci_tbl[]. List initializers are hard to read unless you memoized the order of the struct members. Use the PCI_VDEVICE for all entries and a named initializer for .driver_data. This allows to idiomatically assign the members without using zeros to fill the fields before .driver_data (either explicitly or hidding in PCI_VDEVICE()). There are no changes to the compiled result of the array; verified with builds for x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Adrian Hunter Link: https://patch.msgid.link/6cac1c22381f7026edad9854d70833381d14929a.1777968942.git.u.kleine-koenig@baylibre.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufshcd-pci.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index effa3c7a01c5..13293e83064c 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -680,21 +680,20 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = { }; static const struct pci_device_id ufshcd_pci_tbl[] = { - { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - (kernel_ulong_t)&ufs_qemu_hba_vops }, - { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x4B43), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x98FA), (kernel_ulong_t)&ufs_intel_lkf_hba_vops }, - { PCI_VDEVICE(INTEL, 0x51FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x54FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x7E47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, - { PCI_VDEVICE(INTEL, 0xA847), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x7747), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, - { PCI_VDEVICE(INTEL, 0xE447), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, - { PCI_VDEVICE(INTEL, 0x4D47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, - { PCI_VDEVICE(INTEL, 0xD335), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(REDHAT, 0x0013), .driver_data = (kernel_ulong_t)&ufs_qemu_hba_vops }, + { PCI_VDEVICE(SAMSUNG, 0xC00C), .driver_data = 0 }, + { PCI_VDEVICE(INTEL, 0x9DFA), .driver_data = (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x4B41), .driver_data = (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x4B43), .driver_data = (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x98FA), .driver_data = (kernel_ulong_t)&ufs_intel_lkf_hba_vops }, + { PCI_VDEVICE(INTEL, 0x51FF), .driver_data = (kernel_ulong_t)&ufs_intel_adl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x54FF), .driver_data = (kernel_ulong_t)&ufs_intel_adl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x7E47), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xA847), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x7747), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xE447), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x4D47), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xD335), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { } /* terminate list */ }; From 036218473a8467493860df84602a7825b71385af Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Wed, 6 May 2026 12:45:04 +0300 Subject: [PATCH 0980/5214] scsi: core: scsi_scan: Fix typo in comment Fix spelling mistake in comment: - initialze -> initialize Signed-off-by: Md Shofiqul Islam Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260506094504.2235-1-shofiqtest@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index ef22a4228b85..a35a5f777d16 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -858,7 +858,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, } /** - * scsi_add_lun - allocate and fully initialze a scsi_device + * scsi_add_lun - allocate and fully initialize a scsi_device * @sdev: holds information to be stored in the new scsi_device * @inq_result: holds the result of a previous INQUIRY to the LUN * @bflags: black/white list flag From 73322071418ec3ad5e4d9cdf783890d7f2ae9777 Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Wed, 6 May 2026 03:49:48 +0300 Subject: [PATCH 0981/5214] scsi: storvsc: Replace symbolic permissions with octal Symbolic permissions like S_IRUGO and S_IWUSR are not preferred by checkpatch. Replace with their octal equivalents: - S_IRUGO|S_IWUSR -> 0644 - S_IRUGO -> 0444 Signed-off-by: Md Shofiqul Islam Reviewed-by: Long Li Link: https://patch.msgid.link/20260506004948.2172-1-shofiqtest@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 6977ca8a0658..571ea549152b 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -156,7 +156,7 @@ static bool hv_dev_is_fc(struct hv_device *hv_dev); #define STORVSC_LOGGING_WARN 2 static int logging_level = STORVSC_LOGGING_ERROR; -module_param(logging_level, int, S_IRUGO|S_IWUSR); +module_param(logging_level, int, 0644); MODULE_PARM_DESC(logging_level, "Logging level, 0 - None, 1 - Error (default), 2 - Warning."); @@ -345,17 +345,17 @@ static int storvsc_change_queue_depth(struct scsi_device *sdev, int queue_depth) static int storvsc_vcpus_per_sub_channel = 4; static unsigned int storvsc_max_hw_queues; -module_param(storvsc_ringbuffer_size, int, S_IRUGO); +module_param(storvsc_ringbuffer_size, int, 0444); MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); module_param(storvsc_max_hw_queues, uint, 0644); MODULE_PARM_DESC(storvsc_max_hw_queues, "Maximum number of hardware queues"); -module_param(storvsc_vcpus_per_sub_channel, int, S_IRUGO); +module_param(storvsc_vcpus_per_sub_channel, int, 0444); MODULE_PARM_DESC(storvsc_vcpus_per_sub_channel, "Ratio of VCPUs to subchannels"); static int ring_avail_percent_lowater = 10; -module_param(ring_avail_percent_lowater, int, S_IRUGO); +module_param(ring_avail_percent_lowater, int, 0444); MODULE_PARM_DESC(ring_avail_percent_lowater, "Select a channel if available ring size > this in percent"); From 1039939c52f27667c819537ce5ca231805ca40b8 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 7 May 2026 16:34:10 +0200 Subject: [PATCH 0982/5214] scsi: scsi_transport_srp: Move long delayed work to system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Signed-off-by: Marco Crivellari Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260507143410.337267-1-marco.crivellari@suse.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_srp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index d71ab5fdb758..a61cbb079ab4 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -234,7 +234,7 @@ static ssize_t store_reconnect_delay(struct device *dev, if (rport->reconnect_delay <= 0 && delay > 0 && rport->state != SRP_RPORT_RUNNING) { - queue_delayed_work(system_long_wq, &rport->reconnect_work, + queue_delayed_work(system_dfl_long_wq, &rport->reconnect_work, delay * HZ); } else if (delay <= 0) { cancel_delayed_work(&rport->reconnect_work); @@ -390,7 +390,7 @@ static void srp_reconnect_work(struct work_struct *work) delay = rport->reconnect_delay * clamp(rport->failed_reconnects - 10, 1, 100); if (delay > 0) - queue_delayed_work(system_long_wq, + queue_delayed_work(system_dfl_long_wq, &rport->reconnect_work, delay * HZ); } } @@ -474,7 +474,7 @@ static void __srp_start_tl_fail_timers(struct srp_rport *rport) if (rport->state == SRP_RPORT_LOST) return; if (delay > 0) - queue_delayed_work(system_long_wq, &rport->reconnect_work, + queue_delayed_work(system_dfl_long_wq, &rport->reconnect_work, 1UL * delay * HZ); if ((fast_io_fail_tmo >= 0 || dev_loss_tmo >= 0) && srp_rport_set_state(rport, SRP_RPORT_BLOCKED) == 0) { @@ -482,11 +482,11 @@ static void __srp_start_tl_fail_timers(struct srp_rport *rport) rport->state); scsi_block_targets(shost, &shost->shost_gendev); if (fast_io_fail_tmo >= 0) - queue_delayed_work(system_long_wq, + queue_delayed_work(system_dfl_long_wq, &rport->fast_io_fail_work, 1UL * fast_io_fail_tmo * HZ); if (dev_loss_tmo >= 0) - queue_delayed_work(system_long_wq, + queue_delayed_work(system_dfl_long_wq, &rport->dev_loss_work, 1UL * dev_loss_tmo * HZ); } From 53f5cce2efc7af85a15ca224c660c397332f19e1 Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Sat, 2 May 2026 14:07:03 +0800 Subject: [PATCH 0983/5214] scsi: st: Fix typo in documentation Correct "form" to "from" in drive buffers description. Signed-off-by: Wang Zihan Link: https://patch.msgid.link/tencent_818C822F215676B9B14011B88848609BD309@qq.com Signed-off-by: Martin K. Petersen --- Documentation/scsi/st.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/scsi/st.rst b/Documentation/scsi/st.rst index b4a092faa9c8..539ff06daf5e 100644 --- a/Documentation/scsi/st.rst +++ b/Documentation/scsi/st.rst @@ -93,7 +93,7 @@ optionally written. In both cases end of data is signified by returning zero bytes for two consecutive reads. Writing filemarks without the immediate bit set in the SCSI command block acts -as a synchronization point, i.e., all remaining data form the drive buffers is +as a synchronization point, i.e., all remaining data from the drive buffers is written to tape before the command returns. This makes sure that write errors are caught at that point, but this takes time. In some applications, several consecutive files must be written fast. The MTWEOFI operation can be used to From 250ba648f42d571e08e0bd95fa32953e7577001d Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Mon, 11 May 2026 17:30:30 +0800 Subject: [PATCH 0984/5214] scsi: libiscsi: Fix spelling and format errors Fix two issues in libiscsi.c: - Correct typo "numer" to "number" in iscsi_session_setup() comment - Fix format string "seconds\n." to "seconds.\n" in recv timeout warning Signed-off-by: Wang Yan Reviewed-by: Mike Christie Reviewed-by: Chris Leech Link: https://patch.msgid.link/20260511093030.63542-1-wangyan01@kylinos.cn Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 25857d6ed6e8..160f02f2f51d 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -3012,7 +3012,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) * This can be used by software iscsi_transports that allocate * a session per scsi host. * - * Callers should set cmds_max to the largest total numer (mgmt + scsi) of + * Callers should set cmds_max to the largest total number (mgmt + scsi) of * tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks * for nop handling and login/logout requests. */ @@ -3307,7 +3307,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) if (conn->ping_timeout && !conn->recv_timeout) { iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of " - "zero. Using 5 seconds\n."); + "zero. Using 5 seconds.\n"); conn->recv_timeout = 5; } From 2cc8a6cf8a801065b68550d5af33f62999ce15f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 27 Apr 2026 19:45:46 +0200 Subject: [PATCH 0985/5214] scsi: mvsas: Don't emit __LINE__ in debug messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __LINE__ changes quite easily for cleanup commits. So when checking if a cleanup patch introduces changes to the resulting binary each usage of __LINE__ is source of annoyance. So instead of __FILE__ and __LINE__ emit __func__ to give at least some more indication about where the messages originates from than __FILE__ alone; with that and the actual message the situation should be clear enough. While at it reduce duplication by implementing mv_dprintk() using mv_printk(). Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260427174545.2014499-2-u.kleine-koenig@baylibre.com Signed-off-by: Martin K. Petersen --- drivers/scsi/mvsas/mv_sas.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 09ce3f2241f2..7521f969aa87 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -35,10 +35,10 @@ #define MVS_ID_NOT_MAPPED 0x7f #define WIDE_PORT_MAX_PHY 4 #define mv_printk(fmt, arg ...) \ - printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg) + printk(KERN_DEBUG "%s: " fmt, __func__, ## arg) #ifdef MV_DEBUG -#define mv_dprintk(format, arg...) \ - printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg) +#define mv_dprintk(fmt, arg...) \ + mv_printk(fmt, ## arg) #else #define mv_dprintk(format, arg...) no_printk(format, ## arg) #endif From 67b85a88265df19f049241d8c00571a5408f4eeb Mon Sep 17 00:00:00 2001 From: Yihang Li Date: Sat, 25 Apr 2026 16:20:56 +0800 Subject: [PATCH 0986/5214] scsi: hisi_sas: Add slave_destroy interface for v3 hw WARNING is triggered when executing link reset of remote PHY and rmmod SAS driver simultaneously. Following is the WARNING log: WARNING: CPU: 61 PID: 21818 at drivers/base/core.c:1347 __device_links_no_driver+0xb4/0xc0 Call trace: __device_links_no_driver+0xb4/0xc0 device_links_driver_cleanup+0xb0/0xfc __device_release_driver+0x198/0x23c device_release_driver+0x38/0x50 bus_remove_device+0x130/0x140 device_del+0x184/0x434 __scsi_remove_device+0x118/0x150 scsi_remove_target+0x1bc/0x240 sas_rphy_remove+0x90/0x94 sas_rphy_delete+0x24/0x3c sas_destruct_devices+0x64/0xa0 [libsas] sas_revalidate_domain+0xe4/0x150 [libsas] process_one_work+0x1e0/0x46c worker_thread+0x15c/0x464 kthread+0x160/0x170 ret_from_fork+0x10/0x20 ---[ end trace 71e059eb58f85d4a ]--- During SAS phy up, link->status is set to DL_STATE_AVAILABLE in device_links_driver_bound, then this setting influences __device_links_no_driver() before driver rmmod and caused WARNING. Add the slave_destroy interface to make sure link is removed after flush workque. Fixes: 16fd4a7c5917 ("scsi: hisi_sas: Add device link between SCSI devices and hisi_hba") Signed-off-by: Yihang Li Link: https://patch.msgid.link/20260425082056.2749910-1-liyihang9@huawei.com Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index fda07b193137..c7430f7c4048 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2977,7 +2977,7 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; if (!device_link_add(&sdev->sdev_gendev, dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { if (pm_runtime_enabled(dev)) { dev_info(dev, "add device link failed, disable runtime PM for the host\n"); pm_runtime_disable(dev); @@ -2987,6 +2987,15 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; } +static void hisi_sas_sdev_destroy(struct scsi_device *sdev) +{ + struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev); + struct hisi_hba *hisi_hba = shost_priv(shost); + struct device *dev = hisi_hba->dev; + + device_link_remove(&sdev->sdev_gendev, dev); +} + static struct attribute *host_v3_hw_attrs[] = { &dev_attr_phy_event_threshold.attr, &dev_attr_intr_conv_v3_hw.attr, @@ -3401,6 +3410,7 @@ static const struct scsi_host_template sht_v3_hw = { .sg_tablesize = HISI_SAS_SGE_PAGE_CNT, .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT, .sdev_init = hisi_sas_sdev_init, + .sdev_destroy = hisi_sas_sdev_destroy, .shost_groups = host_v3_hw_groups, .sdev_groups = sdev_groups_v3_hw, .tag_alloc_policy_rr = true, From 2a8fbcfb04aa9db189bfa3842d4f586aecd0e631 Mon Sep 17 00:00:00 2001 From: Kumar Meiyappan Date: Thu, 16 Apr 2026 15:37:57 +0000 Subject: [PATCH 0987/5214] scsi: pm8001: Reject firmware update in fatal error state pm8001_store_update_fw() allows a firmware update request even when the controller has already entered a fatal error state. Firmware update is not valid once the controller is in that state, and attempting it can lead to a call trace. Reject the request early by checking controller_fatal_error, set the firmware status to FAIL_PARAMETERS, and return -EINVAL. Signed-off-by: Kumar Meiyappan Signed-off-by: Sagar Biradar Link: https://patch.msgid.link/20260416153757.414896-1-sagar.biradar@microchip.com Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_ctl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index cbfda8c04e95..bb38b2d63acb 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -826,6 +826,14 @@ static ssize_t pm8001_store_update_fw(struct device *cdev, goto out; } + if (pm8001_ha->controller_fatal_error) { + pm8001_dbg(pm8001_ha, FAIL, + "controller in fatal error state, firmware update rejected\n"); + pm8001_ha->fw_status = FAIL_PARAMETERS; + ret = -EINVAL; + goto out; + } + for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { if (!memcmp(flash_command_table[i].command, cmd_ptr, strlen(cmd_ptr))) { From aa3b8f56ef27ed72394a752820abdec4608b731c Mon Sep 17 00:00:00 2001 From: Kumar Meiyappan Date: Thu, 16 Apr 2026 15:46:50 +0000 Subject: [PATCH 0988/5214] scsi: pm8001: Reject non-fatal dump when controller is crashed pm80xx_get_non_fatal_dump() can be called even after the controller has entered a fatal error state. In that case the forensic memory contents are not safe to access for a non-fatal dump request, and attempting to do so can trigger a call trace. Check controller_fatal_error before reading the non-fatal dump buffer and return -EINVAL when the controller is already in a crashed state. This prevents non-fatal dump collection from running in an invalid controller state. Signed-off-by: Kumar Meiyappan Signed-off-by: Sagar Biradar Link: https://patch.msgid.link/20260416154650.415624-1-sagar.biradar@microchip.com Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm80xx_hwi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 954f307352e6..2c0fa7ab33d2 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -401,6 +401,13 @@ ssize_t pm80xx_get_non_fatal_dump(struct device *cdev, char *buf_copy = buf; temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr; + + if (pm8001_ha->controller_fatal_error) { + pm8001_dbg(pm8001_ha, FAIL, + "non-fatal dump not available in fatal error state\n"); + return -EINVAL; + } + if (++pm8001_ha->non_fatal_count == 1) { if (pm8001_ha->chip_id == chip_8001) { snprintf(pm8001_ha->forensic_info.data_buf.direct_data, From e72323f3b09f9c890fa93a74197bbc290d39d981 Mon Sep 17 00:00:00 2001 From: Palash Kambar Date: Thu, 23 Apr 2026 15:50:22 +0530 Subject: [PATCH 0989/5214] scsi: ufs: core: Configure only active lanes during link The number of connected lanes detected during UFS link startup can be fewer than the lanes specified in the device tree. The current driver logic attempts to configure all lanes defined in the device tree, regardless of their actual availability. This mismatch may cause failures during power mode changes. Hence, Add a check during link startup to ensure that only the lanes actually discovered are considered valid. If a mismatch is detected, fail the initialization early, preventing the driver from entering an unsupported configuration that could cause power mode transition failures. Reviewed-by: Bart Van Assche Reviewed-by: Shawn Lin Reviewed-by: Manivannan Sadhasivam Signed-off-by: Palash Kambar Link: https://patch.msgid.link/20260423102023.3779489-2-palash.kambar@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index a6026cc4b2f4..1aad1c03c3fc 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -5218,6 +5218,35 @@ void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val) } EXPORT_SYMBOL_GPL(ufshcd_update_evt_hist); +static int ufshcd_validate_link_params(struct ufs_hba *hba) +{ + int ret, val; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), + &val); + if (ret) + return ret; + + if (val != hba->lanes_per_direction) { + dev_err(hba->dev, "Tx lane mismatch [config,reported] [%d,%d]\n", + hba->lanes_per_direction, val); + return -ENOLINK; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), + &val); + if (ret) + return ret; + + if (val != hba->lanes_per_direction) { + dev_err(hba->dev, "Rx lane mismatch [config,reported] [%d,%d]\n", + hba->lanes_per_direction, val); + return -ENOLINK; + } + + return 0; +} + /** * ufshcd_link_startup - Initialize unipro link startup * @hba: per adapter instance @@ -5291,6 +5320,10 @@ static int ufshcd_link_startup(struct ufs_hba *hba) goto out; } + ret = ufshcd_validate_link_params(hba); + if (ret) + goto out; + /* Include any host controller configuration via UIC commands */ ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE); if (ret) From 76417038c4d61fc3d407625c0b9332942f13e142 Mon Sep 17 00:00:00 2001 From: Palash Kambar Date: Thu, 23 Apr 2026 15:50:23 +0530 Subject: [PATCH 0990/5214] scsi: ufs: ufs-qcom: Enable Auto Hibern8 clock request support On platforms that support Auto Hibern8 (AH8), the UFS controller can autonomously de-assert clk_req signals to the Global Clock Controller when entering the Hibern8 state. This allows Global Clock Controller (GCC) to gate unused clocks, improving power efficiency. Enable the Clock Request feature by setting the UFS_HW_CLK_CTRL_EN bit in the UFS_AH8_CFG register, as recommended in the Hardware Programming Guidelines. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Palash Kambar Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260423102023.3779489-3-palash.kambar@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-qcom.c | 10 ++++++++++ drivers/ufs/host/ufs-qcom.h | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index bc037db46624..c084ccc72523 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -705,6 +705,13 @@ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, bool is_pre_scale_up, unsign return 0; } +static void ufs_qcom_link_startup_post_change(struct ufs_hba *hba) +{ + if (ufshcd_is_auto_hibern8_supported(hba)) + ufshcd_rmwl(hba, UFS_HW_CLK_CTRL_EN, UFS_HW_CLK_CTRL_EN, + UFS_AH8_CFG); +} + static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) { @@ -730,6 +737,9 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, */ err = ufshcd_disable_host_tx_lcc(hba); + break; + case POST_CHANGE: + ufs_qcom_link_startup_post_change(hba); break; default: break; diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 5d083331a7f4..e20b3ca50577 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -268,6 +268,17 @@ enum { */ #define NUM_TX_R1W1 13 +/* bit definitions for UFS_AH8_CFG register */ +#define CC_UFS_SYS_CLK_REQ_EN BIT(2) +#define CC_UFS_ICE_CORE_CLK_REQ_EN BIT(3) +#define CC_UFS_UNIPRO_CORE_CLK_REQ_EN BIT(4) +#define CC_UFS_AUXCLK_REQ_EN BIT(5) + +#define UFS_HW_CLK_CTRL_EN (CC_UFS_SYS_CLK_REQ_EN |\ + CC_UFS_ICE_CORE_CLK_REQ_EN |\ + CC_UFS_UNIPRO_CORE_CLK_REQ_EN |\ + CC_UFS_AUXCLK_REQ_EN) + static inline void ufs_qcom_get_controller_revision(struct ufs_hba *hba, u8 *major, u16 *minor, u16 *step) From 016d484531e3169cd7bcb26e0ac2c5523080809f Mon Sep 17 00:00:00 2001 From: Piotr Zarycki Date: Thu, 23 Apr 2026 10:13:43 +0200 Subject: [PATCH 0991/5214] scsi: isci: Remove unused macro scu_get_command_request_logical_port() The macro scu_get_command_request_logical_port() has never been used since it was introduced. Signed-off-by: Piotr Zarycki Link: https://patch.msgid.link/20260423081343.1813002-1-piotr.zarycki@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/isci/scu_task_context.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h index 582d22d54689..e56cd0fdc35f 100644 --- a/drivers/scsi/isci/scu_task_context.h +++ b/drivers/scsi/isci/scu_task_context.h @@ -211,8 +211,6 @@ typedef enum { #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT 12 #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK 0x00007000 -#define scu_get_command_reqeust_logical_port(x) \ - ((x) & SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK) #define MAKE_SCU_CONTEXT_COMMAND_TYPE(type) \ From ee4133b921a17b239ca617e9b0c16cad1277e9fd Mon Sep 17 00:00:00 2001 From: Joao Paulo Menezes Linaris Date: Tue, 12 May 2026 14:30:57 -0300 Subject: [PATCH 0992/5214] counter: intel-qep: Replace manual mutex logic with lock guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use guard() for handling mutex lock instead of locking and unlocking mutex explicitly. This improves readability by eliminating the need for gotos and by clearly indicating mutex will be locked only when execution is in guard scope. Signed-off-by: Joao Paulo Menezes Linaris Co-developed-by: Guilherme Dias Signed-off-by: Guilherme Dias Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20260512173058.14858-1-jplinaris@usp.br Signed-off-by: William Breathitt Gray --- drivers/counter/intel-qep.c | 50 +++++++++++++------------------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index c49c178056f4..9c6536f75afd 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -188,25 +188,21 @@ static int intel_qep_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 max) { struct intel_qep *qep = counter_priv(counter); - int ret = 0; /* Intel QEP ceiling configuration only supports 32-bit values */ if (max != (u32)max) return -ERANGE; - mutex_lock(&qep->lock); - if (qep->enabled) { - ret = -EBUSY; - goto out; - } + guard(mutex)(&qep->lock); + + if (qep->enabled) + return -EBUSY; pm_runtime_get_sync(qep->dev); intel_qep_writel(qep, INTEL_QEPMAX, max); pm_runtime_put(qep->dev); -out: - mutex_unlock(&qep->lock); - return ret; + return 0; } static int intel_qep_enable_read(struct counter_device *counter, @@ -226,10 +222,11 @@ static int intel_qep_enable_write(struct counter_device *counter, u32 reg; bool changed; - mutex_lock(&qep->lock); + guard(mutex)(&qep->lock); + changed = val ^ qep->enabled; if (!changed) - goto out; + return 0; pm_runtime_get_sync(qep->dev); reg = intel_qep_readl(qep, INTEL_QEPCON); @@ -246,8 +243,6 @@ static int intel_qep_enable_write(struct counter_device *counter, pm_runtime_put(qep->dev); qep->enabled = val; -out: - mutex_unlock(&qep->lock); return 0; } @@ -279,7 +274,6 @@ static int intel_qep_spike_filter_ns_write(struct counter_device *counter, struct intel_qep *qep = counter_priv(counter); u32 reg; bool enable; - int ret = 0; /* * Spike filter length is (MAX_COUNT + 2) clock periods. @@ -300,11 +294,10 @@ static int intel_qep_spike_filter_ns_write(struct counter_device *counter, if (length > INTEL_QEPFLT_MAX_COUNT(length)) return -ERANGE; - mutex_lock(&qep->lock); - if (qep->enabled) { - ret = -EBUSY; - goto out; - } + guard(mutex)(&qep->lock); + + if (qep->enabled) + return -EBUSY; pm_runtime_get_sync(qep->dev); reg = intel_qep_readl(qep, INTEL_QEPCON); @@ -316,9 +309,7 @@ static int intel_qep_spike_filter_ns_write(struct counter_device *counter, intel_qep_writel(qep, INTEL_QEPCON, reg); pm_runtime_put(qep->dev); -out: - mutex_unlock(&qep->lock); - return ret; + return 0; } static int intel_qep_preset_enable_read(struct counter_device *counter, @@ -342,13 +333,11 @@ static int intel_qep_preset_enable_write(struct counter_device *counter, { struct intel_qep *qep = counter_priv(counter); u32 reg; - int ret = 0; - mutex_lock(&qep->lock); - if (qep->enabled) { - ret = -EBUSY; - goto out; - } + guard(mutex)(&qep->lock); + + if (qep->enabled) + return -EBUSY; pm_runtime_get_sync(qep->dev); reg = intel_qep_readl(qep, INTEL_QEPCON); @@ -360,10 +349,7 @@ static int intel_qep_preset_enable_write(struct counter_device *counter, intel_qep_writel(qep, INTEL_QEPCON, reg); pm_runtime_put(qep->dev); -out: - mutex_unlock(&qep->lock); - - return ret; + return 0; } static struct counter_comp intel_qep_count_ext[] = { From 8549642259cdb405e9815c4d55a7bb32114d42e6 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Thu, 14 May 2026 16:36:23 +0300 Subject: [PATCH 0993/5214] dt-bindings: interconnect: qcom,eliza-rpmh: Add SDCC1 slave The Eliza RPMh interconnect binding is missing the SDCC1 CNOC CFG slave ID. Add it so SDCC1 consumer can describe the corresponding interconnect path. Append the new ID to preserve the existing ABI values. Signed-off-by: Abel Vesa Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260514-eliza-interconnect-add-missing-sdcc1-slave-node-v2-1-13c03bc890cb@oss.qualcomm.com Signed-off-by: Georgi Djakov --- include/dt-bindings/interconnect/qcom,eliza-rpmh.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/interconnect/qcom,eliza-rpmh.h b/include/dt-bindings/interconnect/qcom,eliza-rpmh.h index 95db2fe647de..dfe99feefb27 100644 --- a/include/dt-bindings/interconnect/qcom,eliza-rpmh.h +++ b/include/dt-bindings/interconnect/qcom,eliza-rpmh.h @@ -57,6 +57,7 @@ #define SLAVE_PCIE_ANOC_CFG 27 #define SLAVE_QDSS_STM 28 #define SLAVE_TCU 29 +#define SLAVE_SDCC_1 30 #define MASTER_GEM_NOC_CNOC 0 #define MASTER_GEM_NOC_PCIE_SNOC 1 From caeab82fbe533711715242fc0c3f381c0e22ed21 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Thu, 14 May 2026 16:36:24 +0300 Subject: [PATCH 0994/5214] interconnect: qcom: eliza: Add SDCC1 slave node The Eliza interconnect provider is missing the SDCC1 CNOC CFG slave node. Add qhs_sdc1 to the provider node table so SDCC1 interconnect paths can resolve to a provider node. Hook qhs_sdc1 up to qsm_cfg and CN0, and bump the corresponding qsm_cfg.num_links and bcm_cn0.num_nodes counts. Reviewed-by: Dmitry Baryshkov Signed-off-by: Abel Vesa Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260514-eliza-interconnect-add-missing-sdcc1-slave-node-v2-2-13c03bc890cb@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/eliza.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/interconnect/qcom/eliza.c b/drivers/interconnect/qcom/eliza.c index a4f7903f0524..891e4e6e8ba8 100644 --- a/drivers/interconnect/qcom/eliza.c +++ b/drivers/interconnect/qcom/eliza.c @@ -127,6 +127,12 @@ static struct qcom_icc_node qhs_qup2 = { .buswidth = 4, }; +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .channels = 1, + .buswidth = 4, +}; + static struct qcom_icc_node qhs_sdc2 = { .name = "qhs_sdc2", .channels = 1, @@ -383,7 +389,7 @@ static struct qcom_icc_node qsm_cfg = { .name = "qsm_cfg", .channels = 1, .buswidth = 4, - .num_links = 29, + .num_links = 30, .link_nodes = { &qhs_ahb2phy0, &qhs_ahb2phy1, &qhs_camera_cfg, &qhs_clk_ctl, &qhs_crypto0_cfg, &qhs_display_cfg, @@ -392,7 +398,7 @@ static struct qcom_icc_node qsm_cfg = { &qhs_mss_cfg, &qhs_pcie_0_cfg, &qhs_prng, &qhs_qdss_cfg, &qhs_qspi, &qhs_qup1, - &qhs_qup2, &qhs_sdc2, + &qhs_qup2, &qhs_sdc1, &qhs_sdc2, &qhs_tcsr, &qhs_tlmm, &qhs_ufs_mem_cfg, &qhs_usb3_0, &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, @@ -1111,7 +1117,7 @@ static struct qcom_icc_bcm bcm_cn0 = { .name = "CN0", .enable_mask = BIT(0), .keepalive = true, - .num_nodes = 43, + .num_nodes = 44, .nodes = { &qsm_cfg, &qhs_ahb2phy0, &qhs_ahb2phy1, &qhs_camera_cfg, &qhs_clk_ctl, &qhs_crypto0_cfg, @@ -1119,7 +1125,7 @@ static struct qcom_icc_bcm bcm_cn0 = { &qhs_i3c_ibi1_cfg, &qhs_imem_cfg, &qhs_mss_cfg, &qhs_pcie_0_cfg, &qhs_prng, &qhs_qdss_cfg, - &qhs_qspi, &qhs_sdc2, + &qhs_qspi, &qhs_sdc1, &qhs_sdc2, &qhs_tcsr, &qhs_tlmm, &qhs_ufs_mem_cfg, &qhs_usb3_0, &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, @@ -1321,6 +1327,7 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = { [SLAVE_QSPI_0] = &qhs_qspi, [SLAVE_QUP_1] = &qhs_qup1, [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_1] = &qhs_sdc1, [SLAVE_SDCC_2] = &qhs_sdc2, [SLAVE_TCSR] = &qhs_tcsr, [SLAVE_TLMM] = &qhs_tlmm, From c7c8352fe569d17e3d379a83075a8ea12168526f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Apr 2026 17:24:42 +0200 Subject: [PATCH 0995/5214] pinctrl: renesas: sh-pfc: Implement .pin_config_group_get() callback When reading /sys/kernel/debug/pinctrl/*.pinctrl-sh-pfc/pinconf-groups while CONFIG_DEBUG_PINCTRL is enabled, the user is confronted with a seemlingly endless stream of identical messages on the console: sh-pfc e6060000.pinctrl: cannot get configuration for pin group, missing group config get function in driver Fix this by implementing the sh_pfc_pinconf_ops.pin_config_group_get() callback. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/130ce567f23fd6eef8f5fa7273480a0e3ff2d1d9.1777562482.git.geert+renesas@glider.be --- drivers/pinctrl/renesas/pinctrl.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/pinctrl/renesas/pinctrl.c b/drivers/pinctrl/renesas/pinctrl.c index 3a742f74ecd1..8585ed4bcfe0 100644 --- a/drivers/pinctrl/renesas/pinctrl.c +++ b/drivers/pinctrl/renesas/pinctrl.c @@ -719,6 +719,30 @@ static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, return 0; } +static int sh_pfc_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, unsigned long *config) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = pmx->pfc->info->groups[group].pins; + unsigned int num_pins = pmx->pfc->info->groups[group].nr_pins; + unsigned long prev_config = 0; + int ret; + + for (unsigned int i = 0; i < num_pins; ++i) { + ret = sh_pfc_pinconf_get(pctldev, pins[i], config); + if (ret) + return ret; + + /* configs should match for all pins in the group */ + if (i && prev_config != *config) + return -ENOTSUPP; + + prev_config = *config; + } + + return 0; +} + static int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, unsigned long *configs, unsigned num_configs) @@ -745,6 +769,7 @@ static const struct pinconf_ops sh_pfc_pinconf_ops = { .is_generic = true, .pin_config_get = sh_pfc_pinconf_get, .pin_config_set = sh_pfc_pinconf_set, + .pin_config_group_get = sh_pfc_pinconf_group_get, .pin_config_group_set = sh_pfc_pinconf_group_set, .pin_config_config_dbg_show = pinconf_generic_dump_config, }; From 4f42053949324867dc40d67829f18a01539e6322 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 2 May 2026 20:55:43 +0200 Subject: [PATCH 0996/5214] clk: renesas: r8a73a4: Add ZT/ZTR trace clocks Implement support for the ZT trace bus and ZTR trace clocks on R-Mobile APE6. Signed-off-by: Marek Vasut Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260502185557.93061-3-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/clk-r8a73a4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/renesas/clk-r8a73a4.c b/drivers/clk/renesas/clk-r8a73a4.c index 7f47644396e6..1a2bbc5e9299 100644 --- a/drivers/clk/renesas/clk-r8a73a4.c +++ b/drivers/clk/renesas/clk-r8a73a4.c @@ -42,6 +42,8 @@ static struct div4_clk div4_clks[] = { { "b", CPG_FRQCRA, 8 }, { "m1", CPG_FRQCRA, 4 }, { "m2", CPG_FRQCRA, 0 }, + { "ztr", CPG_FRQCRB, 20 }, + { "zt", CPG_FRQCRB, 16 }, { "zx", CPG_FRQCRB, 12 }, { "zs", CPG_FRQCRB, 8 }, { "hp", CPG_FRQCRB, 4 }, From 4248ae6e799605b3e62855be6085935d89de50d1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:45 -0700 Subject: [PATCH 0997/5214] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection Currently, both libdw and libunwind define 'unwind__get_entries'. This causes a duplicate symbol build failure when both are compiled into perf. This commit refactors the DWARF unwind post-processing to be configurable at runtime via the .perfconfig file option 'unwind.style', or using the argument '--unwind-style' in the commands 'perf report', 'perf script' and 'perf inject', in a similar manner to the addr2line or the disassembler style. The file 'tools/perf/util/unwind.c' adds the top-level dispatch function 'unwind__get_entries'. The backend implementations are renamed to 'libdw__get_entries' and 'libunwind__get_entries'. Both are attempted as fallbacks if not configured, or if the primary backend fails. Fixes: 2e9191573a69ff96 ("perf build: Remove NO_LIBDW_DWARF_UNWIND option") Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: libunwind-devel@nongnu.org Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon [ Don't mix declarations and code, move 'entries' variable to the start of scope ] [ Use pr_warning_once() instead of pr_err() in stubs for get_entries(), suggested by a local sashiko instance ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 4 + tools/perf/builtin-report.c | 4 + tools/perf/builtin-script.c | 4 + tools/perf/util/Build | 1 + tools/perf/util/config.c | 4 + tools/perf/util/symbol_conf.h | 10 +++ tools/perf/util/unwind-libdw.c | 20 ++++- tools/perf/util/unwind-libunwind-local.c | 27 ++++-- tools/perf/util/unwind-libunwind.c | 2 +- tools/perf/util/unwind.c | 104 +++++++++++++++++++++++ tools/perf/util/unwind.h | 65 ++++++++------ 11 files changed, 210 insertions(+), 35 deletions(-) create mode 100644 tools/perf/util/unwind.c diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6ab20df358c4..a2493f1097df 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -26,6 +26,7 @@ #include "util/synthetic-events.h" #include "util/thread.h" #include "util/namespaces.h" +#include "util/unwind.h" #include "util/util.h" #include "util/tsc.h" @@ -2563,6 +2564,9 @@ int cmd_inject(int argc, const char **argv) OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", "guest mount directory under which every guest os" " instance has a subdir"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, "Generate callchains using DWARF and drop register/stack data"), OPT_END() diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 95c0bdba6b11..0b0966d94128 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -48,6 +48,7 @@ #include "util/time-utils.h" #include "util/auxtrace.h" #include "util/units.h" +#include "util/unwind.h" #include "util/util.h" // perf_tip() #include "ui/ui.h" #include "ui/progress.h" @@ -1449,6 +1450,9 @@ int cmd_report(int argc, const char **argv) OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style", "addr2line styles (libdw,llvm,libbfd,addr2line)", report_parse_addr2line_config), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, "Symbol demangling. Enabled by default, use --no-demangle to disable."), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c8ac9f01a36b..fd0b4609516b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -63,6 +63,7 @@ #include #include "util/dlfilter.h" #include "util/record.h" +#include "util/unwind.h" #include "util/util.h" #include "util/cgroup.h" #include "util/annotate.h" @@ -4159,6 +4160,9 @@ int cmd_script(int argc, const char **argv) "Enable symbol demangling"), OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), + OPT_CALLBACK(0, "unwind-style", NULL, "unwind style", + "unwind styles (libdw,libunwind)", + unwind__option), OPT_STRING(0, "addr2line", &symbol_conf.addr2line_path, "path", "addr2line binary to use for line numbers"), OPT_STRING(0, "time", &script.time_str, "str", diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 70cc91d00804..01edfccebb88 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -216,6 +216,7 @@ ifndef CONFIG_SETNS perf-util-y += setns.o endif +perf-util-y += unwind.o perf-util-$(CONFIG_LIBDW) += probe-finder.o perf-util-$(CONFIG_LIBDW) += dwarf-aux.o perf-util-$(CONFIG_LIBDW) += dwarf-regs.o diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 087002fb1b9b..7988149dc7ed 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -23,6 +23,7 @@ #include "build-id.h" #include "debug.h" #include "config.h" +#include "unwind.h" #include #include #include @@ -525,6 +526,9 @@ int perf_default_config(const char *var, const char *value, if (strstarts(var, "addr2line.")) return addr2line_configure(var, value, dummy); + if (strstarts(var, "unwind.")) + return unwind__configure(var, value, dummy); + /* Add other config variables here. */ return 0; } diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index 6cd454d7c98e..0dee5aa6a534 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -9,6 +9,15 @@ struct strlist; struct intlist; +enum unwind_style { + UNWIND_STYLE_UNKNOWN = 0, + UNWIND_STYLE_LIBDW, + UNWIND_STYLE_LIBUNWIND, +}; + +#define MAX_UNWIND_STYLE (UNWIND_STYLE_LIBUNWIND + 1) + + enum a2l_style { A2L_STYLE_UNKNOWN = 0, A2L_STYLE_LIBDW, @@ -81,6 +90,7 @@ struct symbol_conf { const char *addr2line_path; enum a2l_style addr2line_style[MAX_A2L_STYLE]; int addr2line_timeout_ms; + enum unwind_style unwind_style[MAX_UNWIND_STYLE]; unsigned long time_quantum; struct strlist *dso_list, *comm_list, diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 05e8e68bd49c..7f35042be567 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -339,7 +339,7 @@ frame_callback(Dwfl_Frame *state, void *arg) DWARF_CB_ABORT : DWARF_CB_OK; } -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, @@ -353,10 +353,10 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, static struct unwind_info *ui; Dwfl *dwfl; Dwarf_Word ip; - int err = -EINVAL, i; + int err = -EINVAL, i, entries; if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; + return 0; ui = zalloc(sizeof(*ui) + sizeof(ui->entries[0]) * max_stack); if (!ui) @@ -430,6 +430,18 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, map_symbol__exit(&ui->entries[i].ms); dwfl_ui_ti->ui = NULL; + entries = (int)ui->idx; free(ui); - return 0; + /* + * Unwinder return contract: + * > 0 : unwinding succeeded (stops fallback). If we found frames but hit an error + * (e.g. truncated stack), report success to preserve existing frames. + * 0 : unwinding failed without yielding frames. Ignore non-fatal errors + * (e.g. missing debug info, DWARF corruption) to allow fallback unwinder or + * kernel callchain resolution to proceed. + * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. + */ + if (err) + return (err == -ENOMEM) ? -ENOMEM : (entries > 0 ? 1 : 0); + return entries; } diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 87d496e9dfa6..27e2f7b31789 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -744,7 +744,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), perf_arch_reg_ip(e_machine)); if (ret) - return ret; + return 0; ips[i++] = (unw_word_t) val; @@ -757,7 +757,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, addr_space = maps__addr_space(thread__maps(ui->thread)); if (addr_space == NULL) - return -1; + return 0; ret = unw_init_remote(&c, addr_space, ui); if (ret && !ui->best_effort) @@ -785,15 +785,30 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, /* * Display what we got based on the order setup. */ + int entries = 0; for (i = 0; i < max_stack && !ret; i++) { int j = i; if (callchain_param.order == ORDER_CALLER) j = max_stack - i - 1; - ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; + if (ips[j]) { + ret = entry(ips[j], ui->thread, cb, arg); + if (ret) + break; + entries++; + } } - return ret; + /* + * Unwinder return contract: + * > 0 : unwinding succeeded (stops fallback). + * 0 : unwinding failed without yielding frames. Ignore non-fatal errors + * (e.g. stepping failure) to allow fallback unwinder or kernel callchains. + * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. + */ + if (ret == -ENOMEM) + return -ENOMEM; + return (entries > 0 || ret == 0) ? entries : 0; } static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, @@ -809,10 +824,10 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, }; if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; + return 0; if (max_stack <= 0) - return -EINVAL; + return 0; return get_entries(&ui, cb, arg, max_stack); } diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index cb8be6acfb6f..a0016b897dae 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -79,7 +79,7 @@ void unwind__finish_access(struct maps *maps) ops->finish_access(maps); } -int unwind__get_entries(unwind_entry_cb_t cb, void *arg, +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort) diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c new file mode 100644 index 000000000000..4ed4b1d55c69 --- /dev/null +++ b/tools/perf/util/unwind.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "debug.h" +#include "symbol_conf.h" +#include "unwind.h" +#include +#include +#include + +int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + int ret = 0; + +#if defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + if (symbol_conf.unwind_style[0] == UNWIND_STYLE_UNKNOWN) { + int i = 0; +#ifdef HAVE_LIBDW_SUPPORT + symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBDW; +#endif +#ifdef HAVE_LIBUNWIND_SUPPORT + symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBUNWIND; +#endif + } +#endif //defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT) + + for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.unwind_style); i++) { + switch (symbol_conf.unwind_style[i]) { + case UNWIND_STYLE_LIBDW: + ret = libdw__get_entries(cb, arg, thread, data, max_stack, best_effort); + break; + case UNWIND_STYLE_LIBUNWIND: + ret = libunwind__get_entries(cb, arg, thread, data, max_stack, best_effort); + break; + case UNWIND_STYLE_UNKNOWN: + default: +#if !defined(HAVE_LIBDW_SUPPORT) && !defined(HAVE_LIBUNWIND_SUPPORT) + pr_warning_once( + "Error: dwarf unwinding not supported, build perf with libdw or libunwind.\n"); +#endif + ret = 0; + break; + } + if (ret > 0) { + ret = 0; + break; + } + if (ret < 0) + break; + } + return ret; +} + +int unwind__configure(const char *var, const char *value, void *cb __maybe_unused) +{ + static const char * const unwind_style_names[] = { + [UNWIND_STYLE_LIBDW] = "libdw", + [UNWIND_STYLE_LIBUNWIND] = "libunwind", + NULL + }; + char *s, *p, *saveptr; + size_t i = 0; + + if (strcmp(var, "unwind.style")) + return 0; + + if (!value) + return -1; + + s = strdup(value); + if (!s) + return -1; + + memset(symbol_conf.unwind_style, 0, sizeof(symbol_conf.unwind_style)); + + p = strtok_r(s, ",", &saveptr); + while (p && i < ARRAY_SIZE(symbol_conf.unwind_style)) { + bool found = false; + char *q = strim(p); + + for (size_t j = UNWIND_STYLE_LIBDW; j < MAX_UNWIND_STYLE; j++) { + if (!strcasecmp(q, unwind_style_names[j])) { + symbol_conf.unwind_style[i++] = j; + found = true; + break; + } + } + if (!found) + pr_warning("Unknown unwind style: %s\n", q); + p = strtok_r(NULL, ",", &saveptr); + } + + free(s); + return 0; +} + +int unwind__option(const struct option *opt __maybe_unused, + const char *arg, + int unset __maybe_unused) +{ + return unwind__configure("unwind.style", arg, NULL); +} diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 9f7164c6d9aa..69ba08afda79 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -4,9 +4,10 @@ #include #include -#include "util/map_symbol.h" +#include "map_symbol.h" struct maps; +struct option; struct perf_sample; struct thread; @@ -26,7 +27,9 @@ struct unwind_libunwind_ops { struct perf_sample *data, int max_stack, bool best_effort); }; -#ifdef HAVE_DWARF_UNWIND_SUPPORT +int unwind__configure(const char *var, const char *value, void *cb); +int unwind__option(const struct option *opt, const char *arg, int unset); + /* * When best_effort is set, don't report errors and fail silently. This could * be expanded in the future to be more permissive about things other than @@ -36,8 +39,31 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -/* libunwind specific */ + +#ifdef HAVE_LIBDW_SUPPORT +int libdw__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); +#else +#include "debug.h" +static inline int libdw__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + pr_warning_once("Error: libdw dwarf unwinding not built into perf\n"); + return 0; +} +#endif + #ifdef HAVE_LIBUNWIND_SUPPORT +/* libunwind specific */ +int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, + struct thread *thread, + struct perf_sample *data, int max_stack, + bool best_effort); #ifndef LIBUNWIND__ARCH_REG_ID #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) #endif @@ -47,6 +73,18 @@ int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); #else +#include "debug.h" +static inline int libunwind__get_entries(unwind_entry_cb_t cb __maybe_unused, + void *arg __maybe_unused, + struct thread *thread __maybe_unused, + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused) +{ + pr_warning_once("Error: libunwind dwarf unwinding not built into perf\n"); + return 0; +} + static inline int unwind__prepare_access(struct maps *maps __maybe_unused, struct map *map __maybe_unused, bool *initialized __maybe_unused) @@ -57,26 +95,5 @@ static inline int unwind__prepare_access(struct maps *maps __maybe_unused, static inline void unwind__flush_access(struct maps *maps __maybe_unused) {} static inline void unwind__finish_access(struct maps *maps __maybe_unused) {} #endif -#else -static inline int -unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, - void *arg __maybe_unused, - struct thread *thread __maybe_unused, - struct perf_sample *data __maybe_unused, - int max_stack __maybe_unused, - bool best_effort __maybe_unused) -{ - return 0; -} -static inline int unwind__prepare_access(struct maps *maps __maybe_unused, - struct map *map __maybe_unused, - bool *initialized __maybe_unused) -{ - return 0; -} - -static inline void unwind__flush_access(struct maps *maps __maybe_unused) {} -static inline void unwind__finish_access(struct maps *maps __maybe_unused) {} -#endif /* HAVE_DWARF_UNWIND_SUPPORT */ #endif /* __UNWIND_H */ From 6d13d53be9ddab1425e0c2789788adc4d6f05d74 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:46 -0700 Subject: [PATCH 0998/5214] tools build: Deduplicate test-libunwind for different architectures The separate test files only exist to pass a different #include, instead have a single source file and pass -include to $(CC) to include the relevant header file for the architecture being tested. Generate the rules using a foreach loop. Include tests for all current libunwind architectures. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 12 +++++- tools/build/feature/Makefile | 40 +++++++++---------- tools/build/feature/test-libunwind-aarch64.c | 27 ------------- tools/build/feature/test-libunwind-arm.c | 28 ------------- .../test-libunwind-debug-frame-aarch64.c | 17 -------- .../feature/test-libunwind-debug-frame-arm.c | 17 -------- .../feature/test-libunwind-debug-frame.c | 1 - tools/build/feature/test-libunwind-x86.c | 28 ------------- tools/build/feature/test-libunwind-x86_64.c | 28 ------------- tools/build/feature/test-libunwind.c | 1 - 10 files changed, 29 insertions(+), 170 deletions(-) delete mode 100644 tools/build/feature/test-libunwind-aarch64.c delete mode 100644 tools/build/feature/test-libunwind-arm.c delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c delete mode 100644 tools/build/feature/test-libunwind-x86.c delete mode 100644 tools/build/feature/test-libunwind-x86_64.c diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 2f192d3bf61b..af79c6cf0229 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -104,6 +104,9 @@ FEATURE_TESTS_BASIC := \ # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list # of all feature tests + +LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64 + FEATURE_TESTS_EXTRA := \ bionic \ compile-32 \ @@ -127,7 +130,10 @@ FEATURE_TESTS_EXTRA := \ libpfm4 \ libdebuginfod \ clang-bpf-co-re \ - bpftool-skeletons + bpftool-skeletons \ + libunwind \ + libunwind-debug-frame \ + $(foreach arch,$(LIBUNWIND_ARCHS),libunwind-$(arch) libunwind-debug-frame-$(arch)) FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC) @@ -212,6 +218,10 @@ ifeq ($(feature-all), 1) $(call feature_check,compile-x32) $(call feature_check,bionic) $(call feature_check,libbabeltrace) + $(call feature_check,libunwind) + $(call feature_check,libunwind-debug-frame) + $(foreach arch,$(LIBUNWIND_ARCHS),$(call feature_check,libunwind-$(arch))) + $(foreach arch,$(LIBUNWIND_ARCHS),$(call feature_check,libunwind-debug-frame-$(arch))) else $(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat))) endif diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 704c687ed3ad..4e4a92ef5e1e 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 include ../../scripts/Makefile.include +LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64 + FILES= \ test-all.bin \ test-backtrace.bin \ @@ -38,12 +40,7 @@ FILES= \ test-libtracefs.bin \ test-libunwind.bin \ test-libunwind-debug-frame.bin \ - test-libunwind-x86.bin \ - test-libunwind-x86_64.bin \ - test-libunwind-arm.bin \ - test-libunwind-aarch64.bin \ - test-libunwind-debug-frame-arm.bin \ - test-libunwind-debug-frame-aarch64.bin \ + $(foreach arch,$(LIBUNWIND_ARCHS),test-libunwind-$(arch).bin test-libunwind-debug-frame-$(arch).bin) \ test-pthread-attr-setaffinity-np.bin \ test-pthread-barrier.bin \ test-stackprotector-all.bin \ @@ -211,27 +208,26 @@ $(OUTPUT)test-numa_num_possible_cpus.bin: $(BUILD) -lnuma $(OUTPUT)test-libunwind.bin: - $(BUILD) -lelf -llzma + $(BUILD) -include libunwind.h -lelf -llzma -lunwind $(OUTPUT)test-libunwind-debug-frame.bin: - $(BUILD) -lelf -llzma -$(OUTPUT)test-libunwind-x86.bin: - $(BUILD) -lelf -llzma -lunwind-x86 + $(BUILD) -include libunwind.h -lelf -llzma -lunwind -$(OUTPUT)test-libunwind-x86_64.bin: - $(BUILD) -lelf -llzma -lunwind-x86_64 +define LIBUNWIND_RULE +$$(OUTPUT)test-libunwind-$(1).bin: + $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \ + test-libunwind.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \ + > $$(@:.bin=.make.output) 2>&1 -$(OUTPUT)test-libunwind-arm.bin: - $(BUILD) -lelf -llzma -lunwind-arm +$$(OUTPUT)test-libunwind-debug-frame-$(1).bin: + $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \ + test-libunwind-debug-frame.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \ + > $$(@:.bin=.make.output) 2>&1 -$(OUTPUT)test-libunwind-aarch64.bin: - $(BUILD) -lelf -llzma -lunwind-aarch64 - -$(OUTPUT)test-libunwind-debug-frame-arm.bin: - $(BUILD) -lelf -llzma -lunwind-arm - -$(OUTPUT)test-libunwind-debug-frame-aarch64.bin: - $(BUILD) -lelf -llzma -lunwind-aarch64 +endef +$(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call LIBUNWIND_RULE,$(arch))) \ +) $(OUTPUT)test-libslang.bin: $(BUILD) -lslang diff --git a/tools/build/feature/test-libunwind-aarch64.c b/tools/build/feature/test-libunwind-aarch64.c deleted file mode 100644 index 323803f49212..000000000000 --- a/tools/build/feature/test-libunwind-aarch64.c +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space = unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-arm.c b/tools/build/feature/test-libunwind-arm.c deleted file mode 100644 index cb378b7d6866..000000000000 --- a/tools/build/feature/test-libunwind-arm.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space = unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame-aarch64.c b/tools/build/feature/test-libunwind-debug-frame-aarch64.c deleted file mode 100644 index 36d6646c185e..000000000000 --- a/tools/build/feature/test-libunwind-debug-frame-aarch64.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -int main(void) -{ - dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame-arm.c b/tools/build/feature/test-libunwind-debug-frame-arm.c deleted file mode 100644 index 8696e48e1268..000000000000 --- a/tools/build/feature/test-libunwind-debug-frame-arm.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -int main(void) -{ - dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0); - return 0; -} diff --git a/tools/build/feature/test-libunwind-debug-frame.c b/tools/build/feature/test-libunwind-debug-frame.c index efb55cdd8d01..4c57e37004b3 100644 --- a/tools/build/feature/test-libunwind-debug-frame.c +++ b/tools/build/feature/test-libunwind-debug-frame.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include extern int diff --git a/tools/build/feature/test-libunwind-x86.c b/tools/build/feature/test-libunwind-x86.c deleted file mode 100644 index e5e0f6c89637..000000000000 --- a/tools/build/feature/test-libunwind-x86.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space = unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind-x86_64.c b/tools/build/feature/test-libunwind-x86_64.c deleted file mode 100644 index 62ae4db597dc..000000000000 --- a/tools/build/feature/test-libunwind-x86_64.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -static unw_accessors_t accessors; - -int main(void) -{ - unw_addr_space_t addr_space; - - addr_space = unw_create_addr_space(&accessors, 0); - if (addr_space) - return 0; - - unw_init_remote(NULL, addr_space, NULL); - dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL); - - return 0; -} diff --git a/tools/build/feature/test-libunwind.c b/tools/build/feature/test-libunwind.c index 53fd26614ff0..5af5dc3a73d4 100644 --- a/tools/build/feature/test-libunwind.c +++ b/tools/build/feature/test-libunwind.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, From 444508cd7c7b4f052553af204cc73d5ac4d19901 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:47 -0700 Subject: [PATCH 0999/5214] perf build: Be more programmatic when setting up libunwind variables Iterate LIBUNWIND_ARCHS when setting up CONFIG_ and HAVE_ definitions rather than treating each architecture individually. This sets up the libunwind build variables and C definitions beyond x86 and arm/aarch64. The existing naming convention is followed for compatibility. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 228 ++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 127 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 06d7a3f9990c..7a0e372824c6 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -65,45 +65,13 @@ $(call detected_var,SRCARCH) CFLAGS += -I$(OUTPUT)arch/$(SRCARCH)/include/generated -# Additional ARCH settings for ppc -ifeq ($(SRCARCH),powerpc) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS := -lunwind -lunwind-ppc64 - endif -endif - # Additional ARCH settings for x86 ifeq ($(SRCARCH),x86) $(call detected,CONFIG_X86) ifeq (${IS_64_BIT}, 1) CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma - endif $(call detected,CONFIG_X86_64) - else - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind - endif - endif -endif - -ifeq ($(SRCARCH),arm) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind -lunwind-arm - endif -endif - -ifeq ($(SRCARCH),arm64) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 - endif -endif - -ifeq ($(SRCARCH),loongarch) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64 endif endif @@ -111,49 +79,69 @@ ifeq ($(ARCH),s390) CFLAGS += -fPIC endif -ifeq ($(ARCH),mips) - ifndef NO_LIBUNWIND - LIBUNWIND_LIBS = -lunwind -lunwind-mips - endif -endif - ifneq ($(LIBUNWIND),1) NO_LIBUNWIND := 1 endif -ifeq ($(LIBUNWIND_LIBS),) - NO_LIBUNWIND := 1 +ifndef NO_LIBUNWIND + ifeq ($(SRCARCH),arm) + LIBUNWIND_LIBS = -lunwind -lunwind-arm + endif + ifeq ($(SRCARCH),arm64) + LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 + endif + ifeq ($(SRCARCH),loongarch) + LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64 + endif + ifeq ($(ARCH),mips) + LIBUNWIND_LIBS = -lunwind -lunwind-mips + endif + ifeq ($(SRCARCH),powerpc) + LIBUNWIND_LIBS := -lunwind -lunwind-ppc64 + endif + ifeq ($(SRCARCH),riscv) + LIBUNWIND_LIBS := -lunwind -lunwind-riscv + endif + ifeq ($(SRCARCH),s390) + LIBUNWIND_LIBS := -lunwind -lunwind-s390x + endif + ifeq ($(SRCARCH),x86) + ifeq (${IS_64_BIT}, 1) + LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma + else + LIBUNWIND_LIBS = -lunwind-x86 -lunwind -llzma + endif + endif + ifeq ($(LIBUNWIND_LIBS),) + NO_LIBUNWIND := 1 + endif endif + # # For linking with debug library, run like: # # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ # - -libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code)) -define libunwind_arch_set_flags_code - FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include - FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib -endef - -ifdef LIBUNWIND_DIR - LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include - LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib - LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 loongarch - $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch))) -endif +LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64 ifndef NO_LIBUNWIND - # Set per-feature check compilation flags FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) - - FEATURE_CHECK_LDFLAGS-libunwind-arm += -lunwind -lunwind-arm - FEATURE_CHECK_LDFLAGS-libunwind-aarch64 += -lunwind -lunwind-aarch64 - FEATURE_CHECK_LDFLAGS-libunwind-x86 += -lunwind -llzma -lunwind-x86 - FEATURE_CHECK_LDFLAGS-libunwind-x86_64 += -lunwind -llzma -lunwind-x86_64 + + ifdef LIBUNWIND_DIR + LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include + LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib + + define libunwind_arch_set_flags + FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include + FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib -lunwind -lunwind-$(1) + endef + $(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call libunwind_arch_set_flags,$(arch))) \ + ) + endif endif ifdef CSINCLUDES @@ -638,49 +626,6 @@ ifeq ($(SRCARCH),powerpc) endif endif -ifndef NO_LIBUNWIND - have_libunwind := - - $(call feature_check,libunwind) - - $(call feature_check,libunwind-x86) - ifeq ($(feature-libunwind-x86), 1) - $(call detected,CONFIG_LIBUNWIND_X86) - CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT - LDFLAGS += -lunwind-x86 - EXTLIBS_LIBUNWIND += -lunwind-x86 - have_libunwind = 1 - endif - - $(call feature_check,libunwind-aarch64) - ifeq ($(feature-libunwind-aarch64), 1) - $(call detected,CONFIG_LIBUNWIND_AARCH64) - CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT - LDFLAGS += -lunwind-aarch64 - EXTLIBS_LIBUNWIND += -lunwind-aarch64 - have_libunwind = 1 - $(call feature_check,libunwind-debug-frame-aarch64) - ifneq ($(feature-libunwind-debug-frame-aarch64), 1) - $(warning No debug_frame support found in libunwind-aarch64) - CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64 - endif - endif - - ifneq ($(feature-libunwind), 1) - $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now) - NO_LOCAL_LIBUNWIND := 1 - else - have_libunwind := 1 - $(call detected,CONFIG_LOCAL_LIBUNWIND) - endif - - ifneq ($(have_libunwind), 1) - NO_LIBUNWIND := 1 - endif -else - NO_LOCAL_LIBUNWIND := 1 -endif - ifndef NO_LIBBPF ifneq ($(feature-bpf), 1) $(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.) @@ -739,6 +684,60 @@ ifndef GEN_VMLINUX_H VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h endif +ifndef NO_LIBUNWIND + have_libunwind := + + $(call feature_check,libunwind) + ifneq ($(feature-libunwind), 1) + $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now) + NO_LOCAL_LIBUNWIND := 1 + else + have_libunwind := 1 + $(call detected,CONFIG_LOCAL_LIBUNWIND) + CFLAGS += -DHAVE_LIBUNWIND_SUPPORT + CFLAGS += $(LIBUNWIND_CFLAGS) + LDFLAGS += $(LIBUNWIND_LDFLAGS) + EXTLIBS_LIBUNWIND := $(LIBUNWIND_LIBS) + $(call feature_check,libunwind-debug-frame) + ifneq ($(feature-libunwind-debug-frame), 1) + CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME + endif + endif + + define PROCESS_REMOTE_LIBUNWIND_ARCH + $(call feature_check,libunwind-$(1)) + + ifeq ($$(feature-libunwind-$(1)), 1) + upper_arch := $$(shell echo $(1) | tr '[:lower:]' '[:upper:]') + $$(call detected,CONFIG_LIBUNWIND_$$(upper_arch)) + + CFLAGS += -DHAVE_LIBUNWIND_$$(upper_arch)_SUPPORT + LDFLAGS += -lunwind-$(1) + EXTLIBS_LIBUNWIND += -lunwind-$(1) + have_libunwind := 1 + + $$(call feature_check,libunwind-debug-frame-$(1)) + ifneq ($$(feature-libunwind-debug-frame-$(1)), 1) + CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_$$(upper_arch) + endif + endif + endef + $(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call PROCESS_REMOTE_LIBUNWIND_ARCH,$(arch))) \ + ) + + EXTLIBS += $(EXTLIBS_LIBUNWIND) + + ifeq ($(findstring -static,${LDFLAGS}),-static) + # gcc -static links libgcc_eh which contains piece of libunwind + LIBUNWIND_LDFLAGS += -Wl,--allow-multiple-definition + endif + + ifneq ($(have_libunwind), 1) + NO_LIBUNWIND := 1 + endif +endif + dwarf-post-unwind := 1 dwarf-post-unwind-text := BUG @@ -761,31 +760,6 @@ ifeq ($(dwarf-post-unwind),1) $(call detected,CONFIG_DWARF_UNWIND) endif -ifndef NO_LIBUNWIND - ifndef NO_LOCAL_LIBUNWIND - ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64)) - $(call feature_check,libunwind-debug-frame) - ifneq ($(feature-libunwind-debug-frame), 1) - $(warning No debug_frame support found in libunwind) - CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME - endif - else - # non-ARM has no dwarf_find_debug_frame() function: - CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME - endif - EXTLIBS += $(LIBUNWIND_LIBS) - LDFLAGS += $(LIBUNWIND_LIBS) - endif - ifeq ($(findstring -static,${LDFLAGS}),-static) - # gcc -static links libgcc_eh which contans piece of libunwind - LIBUNWIND_LDFLAGS += -Wl,--allow-multiple-definition - endif - CFLAGS += -DHAVE_LIBUNWIND_SUPPORT - CFLAGS += $(LIBUNWIND_CFLAGS) - LDFLAGS += $(LIBUNWIND_LDFLAGS) - EXTLIBS += $(EXTLIBS_LIBUNWIND) -endif - ifneq ($(NO_LIBTRACEEVENT),1) $(call detected,CONFIG_TRACE) endif From 38547c3d871c46f46e98cc6d10c496984e112978 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:43 +0300 Subject: [PATCH 1000/5214] media: i2c: lm3560: Fix v4l2 subdev registration The existing driver does not call media subdev registration, making it invisible to the media framework. Since the LM3560 supports two independent LEDs, register each LED as a separate media entity. Because registering LEDs before device initialization may cause access attempts before the hardware is ready, lm3560_init_device has been moved before the subdevice initializations. An additional helper, lm3560_subdev_cleanup, was added to release LED0 if the initialization of LED1 fails, and to deregister both LEDs in the remove function. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index f4cc844f4e3c..edfb07587cab 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -364,8 +364,15 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, goto err_out; flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; - return rval; + rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]); + if (rval < 0) { + dev_err(flash->dev, "failed to register V4L2 subdev"); + goto error_out_media; + } + return rval; +error_out_media: + media_entity_cleanup(&flash->subdev_led[led_no].entity); err_out: v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); return rval; @@ -391,6 +398,14 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } +static void lm3560_subdev_cleanup(struct lm3560_flash *flash, + enum lm3560_led_id led_no) +{ + v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); + v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); + media_entity_cleanup(&flash->subdev_led[led_no].entity); +} + static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; @@ -425,17 +440,19 @@ static int lm3560_probe(struct i2c_client *client) flash->dev = &client->dev; mutex_init(&flash->lock); + rval = lm3560_init_device(flash); + if (rval < 0) + return rval; + rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); if (rval < 0) return rval; rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); - if (rval < 0) - return rval; - - rval = lm3560_init_device(flash); - if (rval < 0) + if (rval < 0) { + lm3560_subdev_cleanup(flash, LM3560_LED0); return rval; + } i2c_set_clientdata(client, flash); @@ -447,11 +464,8 @@ static void lm3560_remove(struct i2c_client *client) struct lm3560_flash *flash = i2c_get_clientdata(client); unsigned int i; - for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) { - v4l2_device_unregister_subdev(&flash->subdev_led[i]); - v4l2_ctrl_handler_free(&flash->ctrls_led[i]); - media_entity_cleanup(&flash->subdev_led[i].entity); - } + for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) + lm3560_subdev_cleanup(flash, i); } static const struct i2c_device_id lm3560_id_table[] = { From ace46508bac913f9d0f4b84b49392445ae911fb3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:44 +0300 Subject: [PATCH 1001/5214] media: i2c: lm3560: Optimize mutex lock usage Pass the device's own mutex lock to the control handler so that the media framework can handle control access instead of managing it manually. The lock must be common to both sub-devices since they share same hardware, so the individual sub-device locks will not work here. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index edfb07587cab..5b568ed9536b 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -162,14 +162,12 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); int rval = -EINVAL; - mutex_lock(&flash->lock); - if (ctrl->id == V4L2_CID_FLASH_FAULT) { s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); if (rval < 0) - goto out; + return rval; if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; if (reg_val & FAULT_OVERTEMP) @@ -179,8 +177,6 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) ctrl->cur.val = fault; } -out: - mutex_unlock(&flash->lock); return rval; } @@ -190,8 +186,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) u8 tout_bits; int rval = -EINVAL; - mutex_lock(&flash->lock); - switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: flash->led_mode = ctrl->val; @@ -202,14 +196,12 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_SOURCE: rval = regmap_update_bits(flash->regmap, REG_CONFIG1, 0x04, (ctrl->val) << 2); - if (rval < 0) - goto err_out; break; case V4L2_CID_FLASH_STROBE: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_FLASH; rval = lm3560_mode_ctrl(flash); @@ -218,7 +210,7 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_STOP: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_NONE; rval = lm3560_mode_ctrl(flash); @@ -239,8 +231,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } -err_out: - mutex_unlock(&flash->lock); return rval; } @@ -328,6 +318,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash, if (fault != NULL) fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + hdl->lock = &flash->lock; + if (hdl->error) return hdl->error; @@ -363,6 +355,7 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, if (rval < 0) goto err_out; flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; + flash->subdev_led[led_no].state_lock = &flash->lock; rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]); if (rval < 0) { From 34fc7eedc3a0e150086024c450218aba6efaf4cd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:45 +0300 Subject: [PATCH 1002/5214] media: i2c: lm3560: Convert to use OF bindings Since there are no users of this driver via platform data, remove the platform data support and switch to using Device Tree bindings. Converting to Device Tree assumes dynamic and independent registration of LEDs. To monitor the configured LEDs, a bitmap has been added. This makes LED cleanup more robust and less context dependent. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 141 ++++++++++++++++++++++++++----------- include/media/i2c/lm3560.h | 15 ---- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 5b568ed9536b..609fd0339345 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -9,11 +9,14 @@ * Ldd-Mlp */ +#include #include #include #include #include +#include #include +#include #include #include #include @@ -43,22 +46,33 @@ enum led_enable { * struct lm3560_flash * * @dev: pointer to &struct device - * @pdata: platform data * @regmap: reg. map for i2c * @lock: muxtex for serial access. * @led_mode: V4L2 LED mode * @ctrls_led: V4L2 controls * @subdev_led: V4L2 subdev + * @led_id: LED status holder + * @peak: peak current + * @max_flash_timeout: flash timeout + * @max_flash_brt: flash mode led brightness + * @max_torch_brt: torch mode led brightness */ struct lm3560_flash { struct device *dev; - struct lm3560_platform_data *pdata; struct regmap *regmap; struct mutex lock; enum v4l2_flash_led_mode led_mode; struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; struct v4l2_subdev subdev_led[LM3560_LED_MAX]; + + DECLARE_BITMAP(led_id, LM3560_LED_MAX); + + enum lm3560_peak_current peak; + u32 max_flash_timeout; + + u32 max_flash_brt[LM3560_LED_MAX]; + u32 max_torch_brt[LM3560_LED_MAX]; }; #define to_lm3560_flash(_ctrl, _no) \ @@ -269,8 +283,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash, enum lm3560_led_id led_no) { struct v4l2_ctrl *fault; - u32 max_flash_brt = flash->pdata->max_flash_brt[led_no]; - u32 max_torch_brt = flash->pdata->max_torch_brt[led_no]; + u32 max_flash_brt = flash->max_flash_brt[led_no]; + u32 max_torch_brt = flash->max_torch_brt[led_no]; struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; @@ -295,9 +309,9 @@ static int lm3560_init_controls(struct lm3560_flash *flash, /* flash strobe timeout */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, LM3560_FLASH_TOUT_MIN, - flash->pdata->max_flash_timeout, + flash->max_flash_timeout, LM3560_FLASH_TOUT_STEP, - flash->pdata->max_flash_timeout); + flash->max_flash_timeout); /* flash brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, @@ -339,15 +353,18 @@ static const struct regmap_config lm3560_regmap = { }; static int lm3560_subdev_init(struct lm3560_flash *flash, - enum lm3560_led_id led_no, char *led_name) + enum lm3560_led_id led_no, + struct fwnode_handle *fwnode) { struct i2c_client *client = to_i2c_client(flash->dev); int rval; v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops); flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - strscpy(flash->subdev_led[led_no].name, led_name, - sizeof(flash->subdev_led[led_no].name)); + snprintf(flash->subdev_led[led_no].name, + sizeof(flash->subdev_led[led_no].name), + "lm3560-led%d", led_no); + flash->subdev_led[led_no].fwnode = fwnode_handle_get(fwnode); rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; @@ -378,7 +395,7 @@ static int lm3560_init_device(struct lm3560_flash *flash) /* set peak current */ rval = regmap_update_bits(flash->regmap, - REG_FLASH_TOUT, 0x60, flash->pdata->peak); + REG_FLASH_TOUT, 0x60, flash->peak); if (rval < 0) return rval; /* output disable */ @@ -391,18 +408,21 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } -static void lm3560_subdev_cleanup(struct lm3560_flash *flash, - enum lm3560_led_id led_no) +static void lm3560_subdev_cleanup(struct lm3560_flash *flash) { - v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); - v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); - media_entity_cleanup(&flash->subdev_led[led_no].entity); + int led_no; + + for_each_set_bit(led_no, flash->led_id, LM3560_LED_MAX) { + v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); + v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); + media_entity_cleanup(&flash->subdev_led[led_no].entity); + } } static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; - struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev); + u32 peak_ua; int rval; flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); @@ -415,36 +435,69 @@ static int lm3560_probe(struct i2c_client *client) return rval; } - /* if there is no platform data, use chip default value */ - if (pdata == NULL) { - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENODEV; - pdata->peak = LM3560_PEAK_3600mA; - pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX; - /* led 1 */ - pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX; - /* led 2 */ - pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX; - } - flash->pdata = pdata; flash->dev = &client->dev; mutex_init(&flash->lock); + bitmap_zero(flash->led_id, LM3560_LED_MAX); + + flash->peak = LM3560_PEAK_1600mA; + rval = device_property_read_u32(flash->dev, + "ti,peak-current-microamp", &peak_ua); + if (!rval) { + switch (peak_ua) { + case 1600000: + flash->peak = LM3560_PEAK_1600mA; + break; + case 2300000: + flash->peak = LM3560_PEAK_2300mA; + break; + case 3000000: + flash->peak = LM3560_PEAK_3000mA; + break; + case 3600000: + flash->peak = LM3560_PEAK_3600mA; + break; + default: + return -EINVAL; + } + } + + flash->max_flash_timeout = LM3560_FLASH_TOUT_MIN * 1000; + device_property_read_u32(flash->dev, "flash-max-timeout-us", + &flash->max_flash_timeout); + flash->max_flash_timeout /= 1000; + rval = lm3560_init_device(flash); if (rval < 0) return rval; - rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); - if (rval < 0) - return rval; + device_for_each_child_node_scoped(flash->dev, node) { + u32 reg; - rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); - if (rval < 0) { - lm3560_subdev_cleanup(flash, LM3560_LED0); - return rval; + rval = fwnode_property_read_u32(node, "reg", ®); + if (rval < 0) + /* We care only about nodes with reg property */ + continue; + + if (reg == LM3560_LED0 || reg == LM3560_LED1) { + flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN; + fwnode_property_read_u32(node, "flash-max-microamp", + &flash->max_flash_brt[reg]); + + flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN; + fwnode_property_read_u32(node, "led-max-microamp", + &flash->max_torch_brt[reg]); + + rval = lm3560_subdev_init(flash, reg, node); + if (rval < 0) { + lm3560_subdev_cleanup(flash); + return dev_err_probe(flash->dev, rval, + "failed to register led%d\n", + reg); + } + + set_bit(reg, flash->led_id); + } } i2c_set_clientdata(client, flash); @@ -455,12 +508,17 @@ static int lm3560_probe(struct i2c_client *client) static void lm3560_remove(struct i2c_client *client) { struct lm3560_flash *flash = i2c_get_clientdata(client); - unsigned int i; - for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) - lm3560_subdev_cleanup(flash, i); + lm3560_subdev_cleanup(flash); } +static const struct of_device_id lm3560_of_match[] = { + { .compatible = "ti,lm3559" }, + { .compatible = "ti,lm3560" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3560_of_match); + static const struct i2c_device_id lm3560_id_table[] = { { LM3559_NAME }, { LM3560_NAME }, @@ -473,6 +531,7 @@ static struct i2c_driver lm3560_i2c_driver = { .driver = { .name = LM3560_NAME, .pm = NULL, + .of_match_table = lm3560_of_match, }, .probe = lm3560_probe, .remove = lm3560_remove, diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h index 770d8c72c94a..b56c1ff8fd49 100644 --- a/include/media/i2c/lm3560.h +++ b/include/media/i2c/lm3560.h @@ -66,19 +66,4 @@ enum lm3560_peak_current { LM3560_PEAK_3600mA = 0x60 }; -/* struct lm3560_platform_data - * - * @peak : peak current - * @max_flash_timeout: flash timeout - * @max_flash_brt: flash mode led brightness - * @max_torch_brt: torch mode led brightness - */ -struct lm3560_platform_data { - enum lm3560_peak_current peak; - - u32 max_flash_timeout; - u32 max_flash_brt[LM3560_LED_MAX]; - u32 max_torch_brt[LM3560_LED_MAX]; -}; - #endif /* __LM3560_H__ */ From af5fa4896e8528fa664ef2735910e8c73daccae1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:46 +0300 Subject: [PATCH 1003/5214] media: i2c: lm3560: Add support for PM features Add support for power management features to better control the LM3560 within the media framework. To achieve the desired PM support, the HWEN GPIO and VIN power supply were added and configured into power on/off sequences. Added PM operations along with the PM configuration setup. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 117 ++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 609fd0339345..49030bc0f441 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -12,12 +12,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -48,6 +51,8 @@ enum led_enable { * @dev: pointer to &struct device * @regmap: reg. map for i2c * @lock: muxtex for serial access. + * @hwen_gpio: line connected to HWEN pin + * @vin_supply: line connected to IN supply (2.5V - 5.5V) * @led_mode: V4L2 LED mode * @ctrls_led: V4L2 controls * @subdev_led: V4L2 subdev @@ -62,6 +67,9 @@ struct lm3560_flash { struct regmap *regmap; struct mutex lock; + struct gpio_desc *hwen_gpio; + struct regulator *vin_supply; + enum v4l2_flash_led_mode led_mode; struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; struct v4l2_subdev subdev_led[LM3560_LED_MAX]; @@ -176,12 +184,17 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); int rval = -EINVAL; + if (!pm_runtime_get_if_active(flash->dev)) + return 0; + if (ctrl->id == V4L2_CID_FLASH_FAULT) { s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); - if (rval < 0) + if (rval < 0) { + pm_runtime_put(flash->dev); return rval; + } if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; if (reg_val & FAULT_OVERTEMP) @@ -191,6 +204,8 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) ctrl->cur.val = fault; } + pm_runtime_put(flash->dev); + return rval; } @@ -200,6 +215,9 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) u8 tout_bits; int rval = -EINVAL; + if (!pm_runtime_get_if_active(flash->dev)) + return 0; + switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: flash->led_mode = ctrl->val; @@ -245,6 +263,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } + pm_runtime_put(flash->dev); + return rval; } @@ -408,6 +428,38 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } +static int __maybe_unused lm3560_power_off(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return 0; +} + +static int __maybe_unused lm3560_power_on(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + int rval; + + rval = regulator_enable(flash->vin_supply); + if (rval < 0) { + dev_err(flash->dev, "failed to enable vin power supply\n"); + return rval; + } + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); + + rval = lm3560_init_device(flash); + if (rval < 0) { + lm3560_power_off(dev); + return rval; + } + + return 0; +} + static void lm3560_subdev_cleanup(struct lm3560_flash *flash) { int led_no; @@ -440,6 +492,17 @@ static int lm3560_probe(struct i2c_client *client) bitmap_zero(flash->led_id, LM3560_LED_MAX); + flash->hwen_gpio = devm_gpiod_get_optional(flash->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(flash->hwen_gpio)) + return dev_err_probe(flash->dev, PTR_ERR(flash->hwen_gpio), + "failed to get hwen gpio\n"); + + flash->vin_supply = devm_regulator_get(flash->dev, "vin"); + if (IS_ERR(flash->vin_supply)) + return dev_err_probe(flash->dev, PTR_ERR(flash->vin_supply), + "failed to get vin-supply\n"); + flash->peak = LM3560_PEAK_1600mA; rval = device_property_read_u32(flash->dev, "ti,peak-current-microamp", &peak_ua); @@ -467,9 +530,19 @@ static int lm3560_probe(struct i2c_client *client) &flash->max_flash_timeout); flash->max_flash_timeout /= 1000; + rval = regulator_enable(flash->vin_supply); + if (rval < 0) + return dev_err_probe(flash->dev, rval, + "failed to enable vin power supply\n"); + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); + rval = lm3560_init_device(flash); if (rval < 0) - return rval; + goto error_disable; + + pm_runtime_set_active(flash->dev); + pm_runtime_enable(flash->dev); device_for_each_child_node_scoped(flash->dev, node) { u32 reg; @@ -490,10 +563,10 @@ static int lm3560_probe(struct i2c_client *client) rval = lm3560_subdev_init(flash, reg, node); if (rval < 0) { - lm3560_subdev_cleanup(flash); - return dev_err_probe(flash->dev, rval, - "failed to register led%d\n", - reg); + dev_err(flash->dev, + "failed to register led%d: %d\n", + reg, rval); + goto error_clean; } set_bit(reg, flash->led_id); @@ -502,7 +575,23 @@ static int lm3560_probe(struct i2c_client *client) i2c_set_clientdata(client, flash); + pm_runtime_set_autosuspend_delay(flash->dev, 1000); + pm_runtime_use_autosuspend(flash->dev); + pm_runtime_idle(flash->dev); + return 0; + +error_clean: + pm_runtime_disable(flash->dev); + pm_runtime_set_suspended(flash->dev); + + lm3560_subdev_cleanup(flash); + +error_disable: + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return rval; } static void lm3560_remove(struct i2c_client *client) @@ -510,8 +599,22 @@ static void lm3560_remove(struct i2c_client *client) struct lm3560_flash *flash = i2c_get_clientdata(client); lm3560_subdev_cleanup(flash); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + lm3560_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } +static const struct dev_pm_ops lm3560_pm_ops = { + SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL) +}; + static const struct of_device_id lm3560_of_match[] = { { .compatible = "ti,lm3559" }, { .compatible = "ti,lm3560" }, @@ -530,7 +633,7 @@ MODULE_DEVICE_TABLE(i2c, lm3560_id_table); static struct i2c_driver lm3560_i2c_driver = { .driver = { .name = LM3560_NAME, - .pm = NULL, + .pm = pm_ptr(&lm3560_pm_ops), .of_match_table = lm3560_of_match, }, .probe = lm3560_probe, From dd9a02fc75cefc84024eb658c9e528cc97ca4eda Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:47 +0300 Subject: [PATCH 1004/5214] media: i2c: lm3560: Add proper support for LM3559 The LM3559 is very similar to the LM3560, but it operates at much lower currents. This may result in incorrect current selection if LM3560 ranges are applied to the LM3559. Implement driver data matching to use device-specific current configurations. Since the driver no longer supports platform data and device configuration is performed more granularly, move the remaining enums from the header into the driver file and remove the header. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 120 ++++++++++++++++++++++++++++++------- include/media/i2c/lm3560.h | 69 --------------------- 2 files changed, 100 insertions(+), 89 deletions(-) delete mode 100644 include/media/i2c/lm3560.h diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 49030bc0f441..46d316a26e50 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -22,9 +22,9 @@ #include #include #include -#include #include #include +#include /* registers definitions */ #define REG_ENABLE 0x10 @@ -39,12 +39,44 @@ #define FAULT_OVERTEMP (1<<1) #define FAULT_SHORT_CIRCUIT (1<<2) +#define LM3560_FLASH_TOUT_MIN 32 +#define LM3560_FLASH_TOUT_STEP 32 +#define LM3560_FLASH_TOUT_MAX 1024 +#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ + ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ + (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) +#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ + ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) + +enum lm3560_led_id { + LM3560_LED0 = 0, + LM3560_LED1, + LM3560_LED_MAX +}; + +enum lm3560_peak_current { + LM3560_PEAK_1600mA = 0x00, + LM3560_PEAK_2300mA = 0x20, + LM3560_PEAK_3000mA = 0x40, + LM3560_PEAK_3600mA = 0x60 +}; + enum led_enable { MODE_SHDN = 0x0, MODE_TORCH = 0x2, MODE_FLASH = 0x3, }; +struct lm3560_flash_config { + u32 flash_brt_min_ua; + u32 flash_brt_max_ua; + u32 flash_brt_step_ua; + + u32 torch_brt_min_ua; + u32 torch_brt_max_ua; + u32 torch_brt_step_ua; +}; + /** * struct lm3560_flash * @@ -61,6 +93,7 @@ enum led_enable { * @max_flash_timeout: flash timeout * @max_flash_brt: flash mode led brightness * @max_torch_brt: torch mode led brightness + * @config: device specific current configuration */ struct lm3560_flash { struct device *dev; @@ -81,6 +114,8 @@ struct lm3560_flash { u32 max_flash_brt[LM3560_LED_MAX]; u32 max_torch_brt[LM3560_LED_MAX]; + + const struct lm3560_flash_config *config; }; #define to_lm3560_flash(_ctrl, _no) \ @@ -136,15 +171,20 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash, static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_TORCH_BRT_MIN) + if (brt < config->torch_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->torch_brt_min_ua, + config->torch_brt_max_ua); + br_bits = (br_bits - config->torch_brt_min_ua) / + config->torch_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_TORCH_BR, 0x07, br_bits); @@ -159,15 +199,20 @@ static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_FLASH_BRT_MIN) + if (brt < config->flash_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->flash_brt_min_ua, + config->flash_brt_max_ua); + br_bits = (br_bits - config->flash_brt_min_ua) / + config->flash_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_FLASH_BR, 0x0f, br_bits); @@ -307,6 +352,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash, u32 max_torch_brt = flash->max_torch_brt[led_no]; struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; + const struct lm3560_flash_config *config = flash->config; v4l2_ctrl_handler_init(hdl, 8); @@ -335,13 +381,13 @@ static int lm3560_init_controls(struct lm3560_flash *flash, /* flash brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, - LM3560_FLASH_BRT_MIN, max_flash_brt, - LM3560_FLASH_BRT_STEP, max_flash_brt); + config->flash_brt_min_ua, max_flash_brt, + config->flash_brt_step_ua, max_flash_brt); /* torch brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, - LM3560_TORCH_BRT_MIN, max_torch_brt, - LM3560_TORCH_BRT_STEP, max_torch_brt); + config->torch_brt_min_ua, max_torch_brt, + config->torch_brt_step_ua, max_torch_brt); /* fault */ fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0, @@ -481,6 +527,10 @@ static int lm3560_probe(struct i2c_client *client) if (flash == NULL) return -ENOMEM; + flash->config = device_get_match_data(&client->dev); + if (!flash->config) + return -ENODEV; + flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap); if (IS_ERR(flash->regmap)) { rval = PTR_ERR(flash->regmap); @@ -507,16 +557,26 @@ static int lm3560_probe(struct i2c_client *client) rval = device_property_read_u32(flash->dev, "ti,peak-current-microamp", &peak_ua); if (!rval) { + /* + * LM3559 has lower peak current limit, but + * bit configuration matches LM3560. + * Correct current restrictions are enforced + * by the LM3560 schema. + */ switch (peak_ua) { + case 1400000: case 1600000: flash->peak = LM3560_PEAK_1600mA; break; + case 2100000: case 2300000: flash->peak = LM3560_PEAK_2300mA; break; + case 2700000: case 3000000: flash->peak = LM3560_PEAK_3000mA; break; + case 3200000: case 3600000: flash->peak = LM3560_PEAK_3600mA; break; @@ -545,6 +605,7 @@ static int lm3560_probe(struct i2c_client *client) pm_runtime_enable(flash->dev); device_for_each_child_node_scoped(flash->dev, node) { + const struct lm3560_flash_config *config = flash->config; u32 reg; rval = fwnode_property_read_u32(node, "reg", ®); @@ -553,11 +614,11 @@ static int lm3560_probe(struct i2c_client *client) continue; if (reg == LM3560_LED0 || reg == LM3560_LED1) { - flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN; + flash->max_flash_brt[reg] = config->flash_brt_min_ua; fwnode_property_read_u32(node, "flash-max-microamp", &flash->max_flash_brt[reg]); - flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN; + flash->max_torch_brt[reg] = config->torch_brt_min_ua; fwnode_property_read_u32(node, "led-max-microamp", &flash->max_torch_brt[reg]); @@ -615,24 +676,43 @@ static const struct dev_pm_ops lm3560_pm_ops = { SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL) }; +static const struct lm3560_flash_config lm3559_config = { + .flash_brt_min_ua = 56250, + .flash_brt_max_ua = 900000, + .flash_brt_step_ua = 56250, + + .torch_brt_min_ua = 28125, + .torch_brt_max_ua = 225000, + .torch_brt_step_ua = 28125, +}; + +static const struct lm3560_flash_config lm3560_config = { + .flash_brt_min_ua = 62500, + .flash_brt_max_ua = 1000000, + .flash_brt_step_ua = 62500, + + .torch_brt_min_ua = 31250, + .torch_brt_max_ua = 250000, + .torch_brt_step_ua = 31250, +}; + static const struct of_device_id lm3560_of_match[] = { - { .compatible = "ti,lm3559" }, - { .compatible = "ti,lm3560" }, + { .compatible = "ti,lm3559", .data = &lm3559_config }, + { .compatible = "ti,lm3560", .data = &lm3560_config }, { } }; MODULE_DEVICE_TABLE(of, lm3560_of_match); static const struct i2c_device_id lm3560_id_table[] = { - { LM3559_NAME }, - { LM3560_NAME }, - {} + { "lm3559", (kernel_ulong_t)&lm3559_config }, + { "lm3560", (kernel_ulong_t)&lm3560_config }, + { } }; - MODULE_DEVICE_TABLE(i2c, lm3560_id_table); static struct i2c_driver lm3560_i2c_driver = { .driver = { - .name = LM3560_NAME, + .name = "lm3560", .pm = pm_ptr(&lm3560_pm_ops), .of_match_table = lm3560_of_match, }, diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h deleted file mode 100644 index b56c1ff8fd49..000000000000 --- a/include/media/i2c/lm3560.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/i2c/lm3560.h - * - * Copyright (C) 2013 Texas Instruments - * - * Contact: Daniel Jeong - * Ldd-Mlp - */ - -#ifndef __LM3560_H__ -#define __LM3560_H__ - -#include - -#define LM3559_NAME "lm3559" -#define LM3560_NAME "lm3560" -#define LM3560_I2C_ADDR (0x53) - -/* FLASH Brightness - * min 62500uA, step 62500uA, max 1000000uA - */ -#define LM3560_FLASH_BRT_MIN 62500 -#define LM3560_FLASH_BRT_STEP 62500 -#define LM3560_FLASH_BRT_MAX 1000000 -#define LM3560_FLASH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_FLASH_BRT_MIN ? 0 : \ - (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP)) -#define LM3560_FLASH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN) - -/* FLASH TIMEOUT DURATION - * min 32ms, step 32ms, max 1024ms - */ -#define LM3560_FLASH_TOUT_MIN 32 -#define LM3560_FLASH_TOUT_STEP 32 -#define LM3560_FLASH_TOUT_MAX 1024 -#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ - ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ - (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) -#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ - ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) - -/* TORCH BRT - * min 31250uA, step 31250uA, max 250000uA - */ -#define LM3560_TORCH_BRT_MIN 31250 -#define LM3560_TORCH_BRT_STEP 31250 -#define LM3560_TORCH_BRT_MAX 250000 -#define LM3560_TORCH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_TORCH_BRT_MIN ? 0 : \ - (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP)) -#define LM3560_TORCH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN) - -enum lm3560_led_id { - LM3560_LED0 = 0, - LM3560_LED1, - LM3560_LED_MAX -}; - -enum lm3560_peak_current { - LM3560_PEAK_1600mA = 0x00, - LM3560_PEAK_2300mA = 0x20, - LM3560_PEAK_3000mA = 0x40, - LM3560_PEAK_3600mA = 0x60 -}; - -#endif /* __LM3560_H__ */ From a5b7991d5a737df71a5e4230554481255af64ed4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:07:53 +0200 Subject: [PATCH 1005/5214] iio: adc: ad4062: add GPIOLIB dependency The ad4062 driver gained support for the gpiochip and now fails to build when GPIOLIB is disabled: 390-linux-ld: drivers/iio/adc/ad4062.o: in function `ad4062_gpio_get': drivers/iio/adc/ad4062.c:1383:(.text+0x3dc): undefined reference to `gpiochip_get_data` Add a Kconfig dependency for this. Fixes: da1d3596b1e4 ("iio: adc: ad4062: Add GPIO Controller support") Signed-off-by: Arnd Bergmann Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a9dedbb8eb46..2dcb9980d7c8 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -78,6 +78,7 @@ config AD4030 config AD4062 tristate "Analog Devices AD4062 Driver" depends on I3C + depends on GPIOLIB select REGMAP_I3C select IIO_BUFFER select IIO_TRIGGERED_BUFFER From 307dc4240bd41852d9e0912921e298160db1c109 Mon Sep 17 00:00:00 2001 From: Sam Daly Date: Thu, 14 May 2026 18:23:21 +0200 Subject: [PATCH 1006/5214] iio: light: veml6075: add bounds check to veml6075_it_ms index veml6075_it_ms has 5 elements but VEML6075_CONF_IT can yield values 0-7. If it returns a value >= 5, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range. The problem values are reserved so should never be read from the register. Hence this is hardening against fault device, missprogramming or bus corruption. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Sam Daly Signed-off-by: Greg Kroah-Hartman Reviewed-by: Javier Carrasco Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6075.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c index edbb43407054..f7eb159e5cb4 100644 --- a/drivers/iio/light/veml6075.c +++ b/drivers/iio/light/veml6075.c @@ -100,7 +100,7 @@ static const struct iio_chan_spec veml6075_channels[] = { static int veml6075_request_measurement(struct veml6075_data *data) { - int ret, conf, int_time; + int ret, conf, int_time, int_index; ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); if (ret < 0) @@ -117,7 +117,11 @@ static int veml6075_request_measurement(struct veml6075_data *data) * time for all possible configurations. Using a 1.50 factor simplifies * operations and ensures reliability under all circumstances. */ - int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)]; + int_index = FIELD_GET(VEML6075_CONF_IT, conf); + if (int_index >= ARRAY_SIZE(veml6075_it_ms)) + return -EINVAL; + + int_time = veml6075_it_ms[int_index]; msleep(int_time + (int_time / 2)); /* shutdown again, data registers are still accessible */ From 95e8a48d7a85d4226934020e57815a3316d3a14b Mon Sep 17 00:00:00 2001 From: Sam Daly Date: Thu, 14 May 2026 18:23:20 +0200 Subject: [PATCH 1007/5214] iio: adc: ti-ads1298: add bounds check to pga_settings index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ads1298_pga_settings has 7 elements but ADS1298_MASK_CH_PGA can yield values 0-7. If it yields a value >= 7, this causes an out-of-bounds array access. Add a bounds check and return -EINVAL if the index is out of range. Note that the remaining value b111 is reserved so should not be seen in a correctly functioning system. Assisted-by: gkh_clanker_2000 Cc: stable Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Signed-off-by: Sam Daly Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1298.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index ae30b47e4514..731792f06993 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -279,6 +279,7 @@ static const u8 ads1298_pga_settings[] = { 6, 1, 2, 3, 4, 8, 12 }; static int ads1298_get_scale(struct ads1298_private *priv, int channel, int *val, int *val2) { + unsigned int pga_idx; int ret; unsigned int regval; u8 gain; @@ -302,7 +303,11 @@ static int ads1298_get_scale(struct ads1298_private *priv, if (ret) return ret; - gain = ads1298_pga_settings[FIELD_GET(ADS1298_MASK_CH_PGA, regval)]; + pga_idx = FIELD_GET(ADS1298_MASK_CH_PGA, regval); + if (pga_idx >= ARRAY_SIZE(ads1298_pga_settings)) + return -EINVAL; + + gain = ads1298_pga_settings[pga_idx]; *val /= gain; /* Full scale is VREF / gain */ *val2 = ADS1298_BITS_PER_SAMPLE - 1; /* Signed, hence the -1 */ From 3718315f58f8ad663cc9c0225e3a9f3fc365dc8c Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 2 May 2026 00:10:07 +0800 Subject: [PATCH 1008/5214] PCI: dra7xx: Use common mode field in struct dw_pcie Remove the redundant mode field from struct dra7xx_pcie and use the existing mode field in struct dw_pcie instead. This avoids duplication and prevents potential inconsistencies between the two mode fields. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bjorn Helgaas Link: https://patch.msgid.link/20260501161010.71688-2-18255117159@163.com --- drivers/pci/controller/dwc/pci-dra7xx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index cd904659c321..3fc889944f02 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -92,7 +92,6 @@ struct dra7xx_pcie { struct phy **phy; struct irq_domain *irq_domain; struct clk *clk; - enum dw_pcie_device_mode mode; }; struct dra7xx_pcie_of_data { @@ -328,7 +327,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) dev_dbg(dev, "Link Request Reset\n"); if (reg & LINK_UP_EVT) { - if (dra7xx->mode == DW_PCIE_EP_TYPE) + if (dra7xx->pci->mode == DW_PCIE_EP_TYPE) dw_pcie_ep_linkup(ep); dev_dbg(dev, "Link-up state change\n"); } @@ -828,7 +827,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev) default: dev_err(dev, "INVALID device type %d\n", mode); } - dra7xx->mode = mode; + dra7xx->pci->mode = mode; ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler, IRQF_SHARED | IRQF_ONESHOT, @@ -841,7 +840,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev) return 0; err_deinit: - if (dra7xx->mode == DW_PCIE_RC_TYPE) + if (dra7xx->pci->mode == DW_PCIE_RC_TYPE) dw_pcie_host_deinit(&dra7xx->pci->pp); else dw_pcie_ep_deinit(&dra7xx->pci->ep); @@ -865,7 +864,7 @@ static int dra7xx_pcie_suspend(struct device *dev) struct dw_pcie *pci = dra7xx->pci; u32 val; - if (dra7xx->mode != DW_PCIE_RC_TYPE) + if (pci->mode != DW_PCIE_RC_TYPE) return 0; /* clear MSE */ @@ -882,7 +881,7 @@ static int dra7xx_pcie_resume(struct device *dev) struct dw_pcie *pci = dra7xx->pci; u32 val; - if (dra7xx->mode != DW_PCIE_RC_TYPE) + if (pci->mode != DW_PCIE_RC_TYPE) return 0; /* set MSE */ From 5d47f6aeffdf55524fb1130666057266a741e97e Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 2 May 2026 00:10:08 +0800 Subject: [PATCH 1009/5214] PCI: artpec6: Use common mode field in struct dw_pcie Remove the redundant mode field from struct artpec6_pcie and use the existing mode field in struct dw_pcie instead. This avoids duplication and prevents potential inconsistencies between the two mode fields. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bjorn Helgaas Link: https://patch.msgid.link/20260501161010.71688-3-18255117159@163.com --- drivers/pci/controller/dwc/pcie-artpec6.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 55cb957ae1f3..5cd227dda9a1 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -34,7 +34,6 @@ struct artpec6_pcie { struct regmap *regmap; /* DT axis,syscon-pcie */ void __iomem *phy_base; /* DT phy */ enum artpec_pcie_variants variant; - enum dw_pcie_device_mode mode; }; struct artpec_pcie_of_data { @@ -100,7 +99,7 @@ static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr) struct dw_pcie_rp *pp = &pci->pp; struct dw_pcie_ep *ep = &pci->ep; - switch (artpec6_pcie->mode) { + switch (artpec6_pcie->pci->mode) { case DW_PCIE_RC_TYPE: return cpu_addr - pp->cfg0_base; case DW_PCIE_EP_TYPE: @@ -413,7 +412,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) artpec6_pcie->pci = pci; artpec6_pcie->variant = variant; - artpec6_pcie->mode = mode; + artpec6_pcie->pci->mode = mode; artpec6_pcie->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy"); @@ -428,7 +427,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, artpec6_pcie); - switch (artpec6_pcie->mode) { + switch (artpec6_pcie->pci->mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST)) return -ENODEV; @@ -464,7 +463,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); + dev_err(dev, "INVALID device type %d\n", artpec6_pcie->pci->mode); } return 0; From a8bf471f87f4c5c180641a2c6f7d82b1aa0f61fd Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 2 May 2026 00:10:09 +0800 Subject: [PATCH 1010/5214] PCI: dwc: Use common mode field in struct dw_pcie Remove the redundant mode field from struct dw_plat_pcie and use the existing mode field in struct dw_pcie instead. This avoids duplication and prevents potential inconsistencies between the two mode fields. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bjorn Helgaas Link: https://patch.msgid.link/20260501161010.71688-4-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware-plat.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index d103ab759c4e..d02286678a0a 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -22,7 +22,6 @@ struct dw_plat_pcie { struct dw_pcie *pci; - enum dw_pcie_device_mode mode; }; struct dw_plat_pcie_of_data { @@ -118,11 +117,11 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) pci->dev = dev; dw_plat_pcie->pci = pci; - dw_plat_pcie->mode = mode; + dw_plat_pcie->pci->mode = mode; platform_set_drvdata(pdev, dw_plat_pcie); - switch (dw_plat_pcie->mode) { + switch (dw_plat_pcie->pci->mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST)) return -ENODEV; @@ -148,7 +147,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); + dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->pci->mode); ret = -EINVAL; break; } From f02459d0342f94fbbc6296fca0f221783d3a37de Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 2 May 2026 00:10:10 +0800 Subject: [PATCH 1011/5214] PCI: keembay: Use common mode field in struct dw_pcie Remove the redundant mode field from struct keembay_pcie and use the existing mode field in struct dw_pcie instead. This avoids duplication and prevents potential inconsistencies between the two mode fields. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bjorn Helgaas Link: https://patch.msgid.link/20260501161010.71688-5-18255117159@163.com --- drivers/pci/controller/dwc/pcie-keembay.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 7cf2c312ecec..2459c4d66b88 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -58,7 +58,6 @@ struct keembay_pcie { struct dw_pcie pci; void __iomem *apb_base; - enum dw_pcie_device_mode mode; struct clk *clk_master; struct clk *clk_aux; @@ -117,7 +116,7 @@ static int keembay_pcie_start_link(struct dw_pcie *pci) u32 val; int ret; - if (pcie->mode == DW_PCIE_EP_TYPE) + if (pcie->pci.mode == DW_PCIE_EP_TYPE) return 0; keembay_pcie_ltssm_set(pcie, false); @@ -409,7 +408,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = &keembay_pcie_ops; - pcie->mode = mode; + pcie->pci.mode = mode; pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb"); if (IS_ERR(pcie->apb_base)) @@ -417,7 +416,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); - switch (pcie->mode) { + switch (pcie->pci.mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_KEEMBAY_HOST)) return -ENODEV; @@ -443,7 +442,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "Invalid device type %d\n", pcie->mode); + dev_err(dev, "Invalid device type %d\n", pcie->pci.mode); return -ENODEV; } From 5ef4bac02189bee0b7c170e352d7a38e13fe9678 Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:29 -0700 Subject: [PATCH 1012/5214] PCI: altera: Do not dispose parent IRQ mapping altera_pcie_irq_teardown() calls irq_dispose_mapping() on pcie->irq. However, pcie->irq is the parent IRQ returned by platform_get_irq(), not the mapping created by Altera INTx irq_domain. The Altera driver only sets the chained handler on the parent IRQ. It should detach that handler during teardown, but it should not dispose the parent IRQ mapping, which belongs to the parent interrupt controller's irq_domain. Drop irq_dispose_mapping(pcie->irq) from the teardown path. Note that during irqchip remove(), the child IRQs should've disposed. But since the chained handler itself is removed, there is no way the stale child IRQs (if exists) could fire. So it is safe here. Fixes: ec15c4d0d5d2 ("PCI: altera: Allow building as module") Signed-off-by: Mahesh Vaidya [mani: added a note about IRQ disposal] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-2-mahesh.vaidya@altera.com --- drivers/pci/controller/pcie-altera.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3dbb7adc421c..3d3519b8d88f 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -868,7 +868,6 @@ static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); - irq_dispose_mapping(pcie->irq); } static int altera_pcie_parse_dt(struct altera_pcie *pcie) From 7a94138caeb27f3c49c1dbd93bf422098925bb28 Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:30 -0700 Subject: [PATCH 1013/5214] PCI: altera: Fix resource leaks on probe failure The chained IRQ handler is set during probe, but is only removed during the driver remove(). If pci_host_probe() fails, the handler and INTx IRQ domain remain set even though the devm-managed host bridge storage containing struct altera_pcie will be released, leaving the handler with a stale data pointer. Interrupts are also enabled before pci_host_probe() is called. If probe fails after that point, the controller interrupt source should be disabled before the chained handler and INTx domain are removed. So set the chained handler only after the INTx domain has been created. Disable controller interrupts during IRQ teardown, and tear the IRQ setup down if pci_host_probe() fails. Fixes: c63aed7334c2 ("PCI: altera: Use pci_host_probe() to register host") Signed-off-by: Mahesh Vaidya [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-3-mahesh.vaidya@altera.com --- drivers/pci/controller/pcie-altera.c | 35 ++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3d3519b8d88f..76f3823d9613 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -864,8 +864,23 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) return 0; } +static void altera_pcie_disable_irq(struct altera_pcie *pcie) +{ + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || + pcie->pcie_data->version == ALTERA_PCIE_V2) { + /* Disable all P2A interrupts */ + cra_writel(pcie, 0, P2A_INT_ENABLE); + } else if (pcie->pcie_data->version == ALTERA_PCIE_V3) { + /* Disable port-level interrupts (CFG_AER, etc.) */ + writel(0, pcie->hip_base + + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } +} + static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { + altera_pcie_disable_irq(pcie); irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); } @@ -890,7 +905,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (pcie->irq < 0) return pcie->irq; - irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie); return 0; } @@ -1019,6 +1033,14 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } + /* + * The chained handler uses pcie->irq_domain, so set it only after the + * INTx domain has been created. + */ + irq_set_chained_handler_and_data(pcie->irq, + pcie->pcie_data->ops->rp_isr, + pcie); + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || pcie->pcie_data->version == ALTERA_PCIE_V2) { /* clear all interrupts */ @@ -1036,7 +1058,16 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; - return pci_host_probe(bridge); + ret = pci_host_probe(bridge); + if (ret) + goto err_teardown_irq; + + return 0; + +err_teardown_irq: + altera_pcie_irq_teardown(pcie); + + return ret; } static void altera_pcie_remove(struct platform_device *pdev) From fdf08e4b5a33e840b6c2a988cd4392b4443ca51b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:48 -0700 Subject: [PATCH 1014/5214] perf unwind-libunwind: Make libunwind register reading cross platform Move the libunwind register to perf register mapping functions in arch/../util/unwind-libunwind.c into a new libunwind-arch directory. Rename the functions to __get_perf_regnum_for_unw_regnum_. Add untested ppc32 and s390 functions. Add a get_perf_regnum_for_unw_regnum function that takes an ELF machine as well as a register number and chooses the appropriate architecture implementation. Split the x86 and powerpc 32 and 64-bit implementations apart so that a single libunwind-.h header is included. Move the e_machine into the unwind_info struct to make it easier to pass. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon [ Map UNW_PPC32_NIP to PERF_REG_POWERPC_NIP like done for 64-bit, pointed out by a local sashiko ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/Build | 2 - tools/perf/arch/arm/util/unwind-libunwind.c | 50 -------- tools/perf/arch/arm64/util/Build | 1 - tools/perf/arch/arm64/util/unwind-libunwind.c | 17 --- tools/perf/arch/loongarch/util/Build | 2 - .../arch/loongarch/util/unwind-libunwind.c | 82 ------------- tools/perf/arch/mips/Build | 1 - tools/perf/arch/mips/util/Build | 1 - tools/perf/arch/mips/util/unwind-libunwind.c | 22 ---- tools/perf/arch/powerpc/util/Build | 1 - .../perf/arch/powerpc/util/unwind-libunwind.c | 92 -------------- tools/perf/arch/x86/util/Build | 3 - tools/perf/arch/x86/util/unwind-libunwind.c | 115 ------------------ tools/perf/util/Build | 1 + tools/perf/util/libunwind-arch/Build | 10 ++ .../perf/util/libunwind-arch/libunwind-arch.c | 32 +++++ .../perf/util/libunwind-arch/libunwind-arch.h | 16 +++ .../perf/util/libunwind-arch/libunwind-arm.c | 15 +++ .../util/libunwind-arch/libunwind-arm64.c | 14 +++ .../perf/util/libunwind-arch/libunwind-i386.c | 43 +++++++ .../util/libunwind-arch/libunwind-loongarch.c | 27 ++++ .../perf/util/libunwind-arch/libunwind-mips.c | 29 +++++ .../util/libunwind-arch/libunwind-ppc32.c | 33 +++++ .../util/libunwind-arch/libunwind-ppc64.c | 33 +++++ .../perf/util/libunwind-arch/libunwind-s390.c | 29 +++++ .../util/libunwind-arch/libunwind-x86_64.c | 52 ++++++++ tools/perf/util/libunwind/arm64.c | 5 - tools/perf/util/libunwind/x86_32.c | 12 -- tools/perf/util/unwind-libunwind-local.c | 12 +- tools/perf/util/unwind.h | 5 - 30 files changed, 340 insertions(+), 417 deletions(-) delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/mips/Build delete mode 100644 tools/perf/arch/mips/util/Build delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c create mode 100644 tools/perf/util/libunwind-arch/Build create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build index b94bf3c5279a..768ae5d16553 100644 --- a/tools/perf/arch/arm/util/Build +++ b/tools/perf/arch/arm/util/Build @@ -1,3 +1 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o - perf-util-y += pmu.o auxtrace.o cs-etm.o diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c deleted file mode 100644 index 438906bf0014..000000000000 --- a/tools/perf/arch/arm/util/unwind-libunwind.c +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../../util/unwind.h" -#include "../../../util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_ARM_R0: - return PERF_REG_ARM_R0; - case UNW_ARM_R1: - return PERF_REG_ARM_R1; - case UNW_ARM_R2: - return PERF_REG_ARM_R2; - case UNW_ARM_R3: - return PERF_REG_ARM_R3; - case UNW_ARM_R4: - return PERF_REG_ARM_R4; - case UNW_ARM_R5: - return PERF_REG_ARM_R5; - case UNW_ARM_R6: - return PERF_REG_ARM_R6; - case UNW_ARM_R7: - return PERF_REG_ARM_R7; - case UNW_ARM_R8: - return PERF_REG_ARM_R8; - case UNW_ARM_R9: - return PERF_REG_ARM_R9; - case UNW_ARM_R10: - return PERF_REG_ARM_R10; - case UNW_ARM_R11: - return PERF_REG_ARM_FP; - case UNW_ARM_R12: - return PERF_REG_ARM_IP; - case UNW_ARM_R13: - return PERF_REG_ARM_SP; - case UNW_ARM_R14: - return PERF_REG_ARM_LR; - case UNW_ARM_R15: - return PERF_REG_ARM_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return -EINVAL; -} diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index 638aa6948ab5..15b7340b70f7 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -1,4 +1,3 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-util-y += ../../arm/util/auxtrace.o perf-util-y += ../../arm/util/cs-etm.o perf-util-y += ../../arm/util/pmu.o diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c deleted file mode 100644 index 871af5992298..000000000000 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include - -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include "perf_regs.h" -#include "../../../util/unwind.h" -#endif -#include "../../../util/debug.h" - -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - if (regnum < 0 || regnum >= PERF_REG_ARM64_EXTENDED_MAX) - return -EINVAL; - - return regnum; -} diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build index 8d91e78d31c9..2328fb9a30a3 100644 --- a/tools/perf/arch/loongarch/util/Build +++ b/tools/perf/arch/loongarch/util/Build @@ -1,3 +1 @@ perf-util-y += header.o - -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c deleted file mode 100644 index f693167b86ef..000000000000 --- a/tools/perf/arch/loongarch/util/unwind-libunwind.c +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#include "util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_LOONGARCH64_R1: - return PERF_REG_LOONGARCH_R1; - case UNW_LOONGARCH64_R2: - return PERF_REG_LOONGARCH_R2; - case UNW_LOONGARCH64_R3: - return PERF_REG_LOONGARCH_R3; - case UNW_LOONGARCH64_R4: - return PERF_REG_LOONGARCH_R4; - case UNW_LOONGARCH64_R5: - return PERF_REG_LOONGARCH_R5; - case UNW_LOONGARCH64_R6: - return PERF_REG_LOONGARCH_R6; - case UNW_LOONGARCH64_R7: - return PERF_REG_LOONGARCH_R7; - case UNW_LOONGARCH64_R8: - return PERF_REG_LOONGARCH_R8; - case UNW_LOONGARCH64_R9: - return PERF_REG_LOONGARCH_R9; - case UNW_LOONGARCH64_R10: - return PERF_REG_LOONGARCH_R10; - case UNW_LOONGARCH64_R11: - return PERF_REG_LOONGARCH_R11; - case UNW_LOONGARCH64_R12: - return PERF_REG_LOONGARCH_R12; - case UNW_LOONGARCH64_R13: - return PERF_REG_LOONGARCH_R13; - case UNW_LOONGARCH64_R14: - return PERF_REG_LOONGARCH_R14; - case UNW_LOONGARCH64_R15: - return PERF_REG_LOONGARCH_R15; - case UNW_LOONGARCH64_R16: - return PERF_REG_LOONGARCH_R16; - case UNW_LOONGARCH64_R17: - return PERF_REG_LOONGARCH_R17; - case UNW_LOONGARCH64_R18: - return PERF_REG_LOONGARCH_R18; - case UNW_LOONGARCH64_R19: - return PERF_REG_LOONGARCH_R19; - case UNW_LOONGARCH64_R20: - return PERF_REG_LOONGARCH_R20; - case UNW_LOONGARCH64_R21: - return PERF_REG_LOONGARCH_R21; - case UNW_LOONGARCH64_R22: - return PERF_REG_LOONGARCH_R22; - case UNW_LOONGARCH64_R23: - return PERF_REG_LOONGARCH_R23; - case UNW_LOONGARCH64_R24: - return PERF_REG_LOONGARCH_R24; - case UNW_LOONGARCH64_R25: - return PERF_REG_LOONGARCH_R25; - case UNW_LOONGARCH64_R26: - return PERF_REG_LOONGARCH_R26; - case UNW_LOONGARCH64_R27: - return PERF_REG_LOONGARCH_R27; - case UNW_LOONGARCH64_R28: - return PERF_REG_LOONGARCH_R28; - case UNW_LOONGARCH64_R29: - return PERF_REG_LOONGARCH_R29; - case UNW_LOONGARCH64_R30: - return PERF_REG_LOONGARCH_R30; - case UNW_LOONGARCH64_R31: - return PERF_REG_LOONGARCH_R31; - case UNW_LOONGARCH64_PC: - return PERF_REG_LOONGARCH_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return -EINVAL; -} diff --git a/tools/perf/arch/mips/Build b/tools/perf/arch/mips/Build deleted file mode 100644 index e63eabc2c8f4..000000000000 --- a/tools/perf/arch/mips/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-y += util/ diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build deleted file mode 100644 index 818b808a8247..000000000000 --- a/tools/perf/arch/mips/util/Build +++ /dev/null @@ -1 +0,0 @@ -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o diff --git a/tools/perf/arch/mips/util/unwind-libunwind.c b/tools/perf/arch/mips/util/unwind-libunwind.c deleted file mode 100644 index 0d8c99c29da6..000000000000 --- a/tools/perf/arch/mips/util/unwind-libunwind.c +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#include "util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_MIPS_R1 ... UNW_MIPS_R25: - return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; - case UNW_MIPS_R28 ... UNW_MIPS_R31: - return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; - case UNW_MIPS_PC: - return PERF_REG_MIPS_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } -} diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index d66574cbb9a9..ae928050e07a 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -6,5 +6,4 @@ perf-util-y += evsel.o perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o -perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o perf-util-y += auxtrace.o diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/arch/powerpc/util/unwind-libunwind.c deleted file mode 100644 index 90a6beda20de..000000000000 --- a/tools/perf/arch/powerpc/util/unwind-libunwind.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright 2016 Chandan Kumar, IBM Corporation. - */ - -#include -#include -#include -#include "../../util/unwind.h" -#include "../../util/debug.h" - -int libunwind__arch_reg_id(int regnum) -{ - switch (regnum) { - case UNW_PPC64_R0: - return PERF_REG_POWERPC_R0; - case UNW_PPC64_R1: - return PERF_REG_POWERPC_R1; - case UNW_PPC64_R2: - return PERF_REG_POWERPC_R2; - case UNW_PPC64_R3: - return PERF_REG_POWERPC_R3; - case UNW_PPC64_R4: - return PERF_REG_POWERPC_R4; - case UNW_PPC64_R5: - return PERF_REG_POWERPC_R5; - case UNW_PPC64_R6: - return PERF_REG_POWERPC_R6; - case UNW_PPC64_R7: - return PERF_REG_POWERPC_R7; - case UNW_PPC64_R8: - return PERF_REG_POWERPC_R8; - case UNW_PPC64_R9: - return PERF_REG_POWERPC_R9; - case UNW_PPC64_R10: - return PERF_REG_POWERPC_R10; - case UNW_PPC64_R11: - return PERF_REG_POWERPC_R11; - case UNW_PPC64_R12: - return PERF_REG_POWERPC_R12; - case UNW_PPC64_R13: - return PERF_REG_POWERPC_R13; - case UNW_PPC64_R14: - return PERF_REG_POWERPC_R14; - case UNW_PPC64_R15: - return PERF_REG_POWERPC_R15; - case UNW_PPC64_R16: - return PERF_REG_POWERPC_R16; - case UNW_PPC64_R17: - return PERF_REG_POWERPC_R17; - case UNW_PPC64_R18: - return PERF_REG_POWERPC_R18; - case UNW_PPC64_R19: - return PERF_REG_POWERPC_R19; - case UNW_PPC64_R20: - return PERF_REG_POWERPC_R20; - case UNW_PPC64_R21: - return PERF_REG_POWERPC_R21; - case UNW_PPC64_R22: - return PERF_REG_POWERPC_R22; - case UNW_PPC64_R23: - return PERF_REG_POWERPC_R23; - case UNW_PPC64_R24: - return PERF_REG_POWERPC_R24; - case UNW_PPC64_R25: - return PERF_REG_POWERPC_R25; - case UNW_PPC64_R26: - return PERF_REG_POWERPC_R26; - case UNW_PPC64_R27: - return PERF_REG_POWERPC_R27; - case UNW_PPC64_R28: - return PERF_REG_POWERPC_R28; - case UNW_PPC64_R29: - return PERF_REG_POWERPC_R29; - case UNW_PPC64_R30: - return PERF_REG_POWERPC_R30; - case UNW_PPC64_R31: - return PERF_REG_POWERPC_R31; - case UNW_PPC64_LR: - return PERF_REG_POWERPC_LINK; - case UNW_PPC64_CTR: - return PERF_REG_POWERPC_CTR; - case UNW_PPC64_XER: - return PERF_REG_POWERPC_XER; - case UNW_PPC64_NIP: - return PERF_REG_POWERPC_NIP; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - return -EINVAL; -} diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index b94c91984c66..7f89fffe4615 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build @@ -8,9 +8,6 @@ perf-util-y += evlist.o perf-util-y += mem-events.o perf-util-y += evsel.o perf-util-y += iostat.o - -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o - perf-util-y += auxtrace.o perf-util-y += intel-pt.o perf-util-y += intel-bts.o diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c deleted file mode 100644 index 47357973b55b..000000000000 --- a/tools/perf/arch/x86/util/unwind-libunwind.c +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include "../../util/debug.h" -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include "perf_regs.h" -#include "../../util/unwind.h" -#endif - -#ifdef HAVE_ARCH_X86_64_SUPPORT -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - int id; - - switch (regnum) { - case UNW_X86_64_RAX: - id = PERF_REG_X86_AX; - break; - case UNW_X86_64_RDX: - id = PERF_REG_X86_DX; - break; - case UNW_X86_64_RCX: - id = PERF_REG_X86_CX; - break; - case UNW_X86_64_RBX: - id = PERF_REG_X86_BX; - break; - case UNW_X86_64_RSI: - id = PERF_REG_X86_SI; - break; - case UNW_X86_64_RDI: - id = PERF_REG_X86_DI; - break; - case UNW_X86_64_RBP: - id = PERF_REG_X86_BP; - break; - case UNW_X86_64_RSP: - id = PERF_REG_X86_SP; - break; - case UNW_X86_64_R8: - id = PERF_REG_X86_R8; - break; - case UNW_X86_64_R9: - id = PERF_REG_X86_R9; - break; - case UNW_X86_64_R10: - id = PERF_REG_X86_R10; - break; - case UNW_X86_64_R11: - id = PERF_REG_X86_R11; - break; - case UNW_X86_64_R12: - id = PERF_REG_X86_R12; - break; - case UNW_X86_64_R13: - id = PERF_REG_X86_R13; - break; - case UNW_X86_64_R14: - id = PERF_REG_X86_R14; - break; - case UNW_X86_64_R15: - id = PERF_REG_X86_R15; - break; - case UNW_X86_64_RIP: - id = PERF_REG_X86_IP; - break; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return id; -} -#else -int LIBUNWIND__ARCH_REG_ID(int regnum) -{ - int id; - - switch (regnum) { - case UNW_X86_EAX: - id = PERF_REG_X86_AX; - break; - case UNW_X86_EDX: - id = PERF_REG_X86_DX; - break; - case UNW_X86_ECX: - id = PERF_REG_X86_CX; - break; - case UNW_X86_EBX: - id = PERF_REG_X86_BX; - break; - case UNW_X86_ESI: - id = PERF_REG_X86_SI; - break; - case UNW_X86_EDI: - id = PERF_REG_X86_DI; - break; - case UNW_X86_EBP: - id = PERF_REG_X86_BP; - break; - case UNW_X86_ESP: - id = PERF_REG_X86_SP; - break; - case UNW_X86_EIP: - id = PERF_REG_X86_IP; - break; - default: - pr_err("unwind: invalid reg id %d\n", regnum); - return -EINVAL; - } - - return id; -} -#endif /* HAVE_ARCH_X86_64_SUPPORT */ diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 01edfccebb88..bf4204135ccb 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -228,6 +228,7 @@ perf-util-$(CONFIG_LIBDW) += unwind-libdw.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/ perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build new file mode 100644 index 000000000000..87fd657a3248 --- /dev/null +++ b/tools/perf/util/libunwind-arch/Build @@ -0,0 +1,10 @@ +perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm64.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c new file mode 100644 index 000000000000..5439bf90d161 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include +#include + +int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum) +{ + switch (e_machine) { + case EM_ARM: + return __get_perf_regnum_for_unw_regnum_arm(unw_regnum); + case EM_AARCH64: + return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum); + case EM_LOONGARCH: + return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum); + case EM_MIPS: + return __get_perf_regnum_for_unw_regnum_mips(unw_regnum); + case EM_PPC: + return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum); + case EM_PPC64: + return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum); + case EM_S390: + return __get_perf_regnum_for_unw_regnum_s390(unw_regnum); + case EM_386: + return __get_perf_regnum_for_unw_regnum_i386(unw_regnum); + case EM_X86_64: + return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h new file mode 100644 index 000000000000..e1009c6cb965 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBUNWIND_ARCH_H +#define __LIBUNWIND_ARCH_H + +int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); +int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum); + +#endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c new file mode 100644 index 000000000000..6740ee55b043 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/arm/include/uapi/asm/perf_regs.h" +#include +#include + +int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) +{ + if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return unw_regnum; +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c new file mode 100644 index 000000000000..53b1877dfa04 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/arm64/include/uapi/asm/perf_regs.h" +#include + +int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum) +{ + if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return unw_regnum; +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c new file mode 100644 index 000000000000..a41f7c3c2fa5 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_X86_SUPPORT + return -EINVAL; +#else + static const int perf_i386_regnums[] = { +#define REGNUM(reg) [UNW_X86_E ## reg] = PERF_REG_X86_ ## reg + REGNUM(AX), + REGNUM(DX), + REGNUM(CX), + REGNUM(BX), + REGNUM(SI), + REGNUM(DI), + REGNUM(BP), + REGNUM(SP), + REGNUM(IP), +#undef REGNUM + }; + + if (unw_regnum == UNW_X86_EAX) + return PERF_REG_X86_AX; + + if (unw_regnum < 0 || unw_regnum >= (int)ARRAY_SIZE(perf_i386_regnums) || + perf_i386_regnums[unw_regnum] == 0) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + + return perf_i386_regnums[unw_regnum]; +#endif // HAVE_LIBUNWIND_X86_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c new file mode 100644 index 000000000000..0431757505f9 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_LOONGARCH64_R1 ... UNW_LOONGARCH64_R31: + return unw_regnum - UNW_LOONGARCH64_R1 + PERF_REG_LOONGARCH_R1; + case UNW_LOONGARCH64_PC: + return PERF_REG_LOONGARCH_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c new file mode 100644 index 000000000000..01a506c8079c --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/mips/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_MIPS_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_MIPS_R1 ... UNW_MIPS_R25: + return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; + case UNW_MIPS_R28 ... UNW_MIPS_R31: + return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; + case UNW_MIPS_PC: + return PERF_REG_MIPS_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_MIPS_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c new file mode 100644 index 000000000000..976a16030407 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_PPC32_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_PPC32_R0 ... UNW_PPC32_R31: + return unw_regnum - UNW_PPC32_R0 + PERF_REG_POWERPC_R0; + case UNW_PPC32_LR: + return PERF_REG_POWERPC_LINK; + case UNW_PPC32_CTR: + return PERF_REG_POWERPC_CTR; + case UNW_PPC32_XER: + return PERF_REG_POWERPC_XER; + case UNW_PPC32_NIP: + return PERF_REG_POWERPC_NIP; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_PPC32_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c new file mode 100644 index 000000000000..c508dc89615b --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_PPC64_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_PPC64_R0 ... UNW_PPC64_R31: + return unw_regnum - UNW_PPC64_R0 + PERF_REG_POWERPC_R0; + case UNW_PPC64_LR: + return PERF_REG_POWERPC_LINK; + case UNW_PPC64_CTR: + return PERF_REG_POWERPC_CTR; + case UNW_PPC64_XER: + return PERF_REG_POWERPC_XER; + case UNW_PPC64_NIP: + return PERF_REG_POWERPC_NIP; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_PPC64_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c new file mode 100644 index 000000000000..7088991015e6 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/s390/include/uapi/asm/perf_regs.h" +#include +#include + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_S390X_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_S390X_R0 ... UNW_S390X_R15: + return unw_regnum - UNW_S390X_R0 + PERF_REG_S390_R0; + case UNW_S390X_F0 ... UNW_S390X_F15: + return unw_regnum - UNW_S390X_F0 + PERF_REG_S390_FP0; + case UNW_S390X_IP: + return PERF_REG_S390_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_S390X_SUPPORT +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c new file mode 100644 index 000000000000..82dfb2c7152a --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_X86_64_SUPPORT + return -EINVAL; +#else + static const int perf_x86_64_regnums[] = { +#define REGNUM(reg) [UNW_X86_64_R ## reg] = PERF_REG_X86_ ## reg + REGNUM(AX), + REGNUM(DX), + REGNUM(CX), + REGNUM(BX), + REGNUM(SI), + REGNUM(DI), + REGNUM(BP), + REGNUM(SP), + REGNUM(IP), +#undef REGNUM +#define REGNUM(reg) [UNW_X86_64_ ## reg] = PERF_REG_X86_ ## reg + REGNUM(R8), + REGNUM(R9), + REGNUM(R10), + REGNUM(R11), + REGNUM(R12), + REGNUM(R13), + REGNUM(R14), + REGNUM(R15), +#undef REGNUM + }; + + if (unw_regnum == UNW_X86_64_RAX) + return PERF_REG_X86_AX; + + if (unw_regnum < 0 || unw_regnum >= (int)ARRAY_SIZE(perf_x86_64_regnums) || + perf_x86_64_regnums[unw_regnum] == 0) { + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } + return perf_x86_64_regnums[unw_regnum]; +#endif // HAVE_LIBUNWIND_X86_64_SUPPORT +} diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c index 37ecef0c53b9..15670a964495 100644 --- a/tools/perf/util/libunwind/arm64.c +++ b/tools/perf/util/libunwind/arm64.c @@ -14,11 +14,6 @@ #define REMOTE_UNWIND_LIBUNWIND -/* Define arch specific functions & regs for libunwind, should be - * defined before including "unwind.h" - */ -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum) - #include "unwind.h" #include "libunwind-aarch64.h" #define perf_event_arm_regs perf_event_arm64_regs diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c index 1697dece1b74..1e9fb8bfec44 100644 --- a/tools/perf/util/libunwind/x86_32.c +++ b/tools/perf/util/libunwind/x86_32.c @@ -14,20 +14,8 @@ #define REMOTE_UNWIND_LIBUNWIND -/* Define arch specific functions & regs for libunwind, should be - * defined before including "unwind.h" - */ -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum) - #include "unwind.h" #include "libunwind-x86.h" -#include <../../../../arch/x86/include/uapi/asm/perf_regs.h> - -/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c' - * for x86_32, we undef it to compile code for x86_32 only. - */ -#undef HAVE_ARCH_X86_64_SUPPORT -#include "../../arch/x86/util/unwind-libunwind.c" /* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no * dwarf_find_debug_frame() function. diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 27e2f7b31789..adc6e82b5f27 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -39,6 +39,7 @@ #include "debug.h" #include "asm/bug.h" #include "dso.h" +#include "libunwind-arch/libunwind-arch.h" extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, @@ -95,6 +96,7 @@ struct unwind_info { struct perf_sample *sample; struct machine *machine; struct thread *thread; + uint16_t e_machine; bool best_effort; }; @@ -583,9 +585,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as, } ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample), - perf_arch_reg_sp(thread__e_machine(ui->thread, - ui->machine, - /*e_flags=*/NULL))); + perf_arch_reg_sp(ui->e_machine)); if (ret) return ret; @@ -633,7 +633,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as, return 0; } - id = LIBUNWIND__ARCH_REG_ID(regnum); + id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); if (id < 0) return -EINVAL; @@ -734,7 +734,6 @@ static void _unwind__finish_access(struct maps *maps) static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { - uint16_t e_machine = thread__e_machine(ui->thread, ui->machine, /*e_flags=*/NULL); u64 val; unw_word_t ips[max_stack]; unw_addr_space_t addr_space; @@ -742,7 +741,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, int ret, i = 0; ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), - perf_arch_reg_ip(e_machine)); + perf_arch_reg_ip(ui->e_machine)); if (ret) return 0; @@ -820,6 +819,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, .sample = data, .thread = thread, .machine = maps__machine(thread__maps(thread)), + .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL), .best_effort = best_effort }; diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 69ba08afda79..ce256b00b6d2 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -64,11 +64,6 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -#ifndef LIBUNWIND__ARCH_REG_ID -#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) -#endif - -int LIBUNWIND__ARCH_REG_ID(int regnum); int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized); void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); From 2723e9f8b5cf36b8c49b9798fdbe326cf2b70785 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:49 -0700 Subject: [PATCH 1015/5214] perf unwind-libunwind: Move flush/finish access out of local Flush and finish access are relatively simple calls into libunwind, move them out struct unwind_libunwind_ops. So that the correct version can be called, add an e_machine variable to maps. This size regression will go away when the unwind_libunwind_ops no longer need stashing in the maps. To set the e_machine up pass it into unwind__prepare_access, which no longer needs to determine the unwind operations based on a map dso because of this. This also means the maps copying code can call unwind__prepare_access once for the e_machine rather than once per map. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/libunwind-arch/libunwind-arch.c | 82 +++++++++++++++++++ .../perf/util/libunwind-arch/libunwind-arch.h | 24 ++++++ .../perf/util/libunwind-arch/libunwind-arm.c | 19 +++++ .../util/libunwind-arch/libunwind-arm64.c | 20 +++++ .../perf/util/libunwind-arch/libunwind-i386.c | 15 ++++ .../util/libunwind-arch/libunwind-loongarch.c | 15 ++++ .../perf/util/libunwind-arch/libunwind-mips.c | 15 ++++ .../util/libunwind-arch/libunwind-ppc32.c | 15 ++++ .../util/libunwind-arch/libunwind-ppc64.c | 15 ++++ .../perf/util/libunwind-arch/libunwind-s390.c | 15 ++++ .../util/libunwind-arch/libunwind-x86_64.c | 15 ++++ tools/perf/util/maps.c | 36 +++++--- tools/perf/util/maps.h | 2 + tools/perf/util/thread.c | 32 ++------ tools/perf/util/unwind-libunwind-local.c | 12 --- tools/perf/util/unwind-libunwind.c | 59 ++++++------- tools/perf/util/unwind.h | 8 +- 17 files changed, 309 insertions(+), 90 deletions(-) diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c index 5439bf90d161..9692e6c81492 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include #include @@ -30,3 +31,84 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum) return -EINVAL; } } + + +void libunwind_arch__flush_access(struct maps *maps) +{ + unsigned int e_machine = maps__e_machine(maps); + + switch (e_machine) { + case EM_NONE: + break; // No libunwind info on the maps. + case EM_ARM: + __libunwind_arch__flush_access_arm(maps); + break; + case EM_AARCH64: + __libunwind_arch__flush_access_arm64(maps); + break; + case EM_LOONGARCH: + __libunwind_arch__flush_access_loongarch(maps); + break; + case EM_MIPS: + __libunwind_arch__flush_access_mips(maps); + break; + case EM_PPC: + __libunwind_arch__flush_access_ppc32(maps); + break; + case EM_PPC64: + __libunwind_arch__flush_access_ppc64(maps); + break; + case EM_S390: + __libunwind_arch__flush_access_s390(maps); + break; + case EM_386: + __libunwind_arch__flush_access_i386(maps); + break; + case EM_X86_64: + __libunwind_arch__flush_access_x86_64(maps); + break; + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + break; + } +} + +void libunwind_arch__finish_access(struct maps *maps) +{ + unsigned int e_machine = maps__e_machine(maps); + + switch (e_machine) { + case EM_NONE: + break; // No libunwind info on the maps. + case EM_ARM: + __libunwind_arch__finish_access_arm(maps); + break; + case EM_AARCH64: + __libunwind_arch__finish_access_arm64(maps); + break; + case EM_LOONGARCH: + __libunwind_arch__finish_access_loongarch(maps); + break; + case EM_MIPS: + __libunwind_arch__finish_access_mips(maps); + break; + case EM_PPC: + __libunwind_arch__finish_access_ppc32(maps); + break; + case EM_PPC64: + __libunwind_arch__finish_access_ppc64(maps); + break; + case EM_S390: + __libunwind_arch__finish_access_s390(maps); + break; + case EM_386: + __libunwind_arch__finish_access_i386(maps); + break; + case EM_X86_64: + __libunwind_arch__finish_access_x86_64(maps); + break; + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + break; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h index e1009c6cb965..c00277a5e914 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -2,6 +2,8 @@ #ifndef __LIBUNWIND_ARCH_H #define __LIBUNWIND_ARCH_H +struct maps; + int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum); @@ -13,4 +15,26 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum); int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum); +void __libunwind_arch__flush_access_arm(struct maps *maps); +void __libunwind_arch__flush_access_arm64(struct maps *maps); +void __libunwind_arch__flush_access_loongarch(struct maps *maps); +void __libunwind_arch__flush_access_mips(struct maps *maps); +void __libunwind_arch__flush_access_ppc32(struct maps *maps); +void __libunwind_arch__flush_access_ppc64(struct maps *maps); +void __libunwind_arch__flush_access_s390(struct maps *maps); +void __libunwind_arch__flush_access_i386(struct maps *maps); +void __libunwind_arch__flush_access_x86_64(struct maps *maps); +void libunwind_arch__flush_access(struct maps *maps); + +void __libunwind_arch__finish_access_arm(struct maps *maps); +void __libunwind_arch__finish_access_arm64(struct maps *maps); +void __libunwind_arch__finish_access_loongarch(struct maps *maps); +void __libunwind_arch__finish_access_mips(struct maps *maps); +void __libunwind_arch__finish_access_ppc32(struct maps *maps); +void __libunwind_arch__finish_access_ppc64(struct maps *maps); +void __libunwind_arch__finish_access_s390(struct maps *maps); +void __libunwind_arch__finish_access_i386(struct maps *maps); +void __libunwind_arch__finish_access_x86_64(struct maps *maps); +void libunwind_arch__finish_access(struct maps *maps); + #endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c index 6740ee55b043..bbaf01406c52 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -1,10 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/arm/include/uapi/asm/perf_regs.h" #include #include +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +#include +#endif + int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) { if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) { @@ -13,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum) } return unw_regnum; } + +void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c index 53b1877dfa04..8ba510089736 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm64.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -1,9 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/arm64/include/uapi/asm/perf_regs.h" +#include #include +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT +#include +#endif + int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum) { if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) { @@ -12,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum) } return unw_regnum; } + +void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c index a41f7c3c2fa5..383f0a44d290 100644 --- a/tools/perf/util/libunwind-arch/libunwind-i386.c +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include @@ -41,3 +42,17 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused) return perf_i386_regnums[unw_regnum]; #endif // HAVE_LIBUNWIND_X86_SUPPORT } + +void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c index 0431757505f9..7ff459849930 100644 --- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" #include #include @@ -25,3 +26,17 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused) } #endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT } + +void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c index 01a506c8079c..1fa81742ff4a 100644 --- a/tools/perf/util/libunwind-arch/libunwind-mips.c +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/mips/include/uapi/asm/perf_regs.h" #include #include @@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused) } #endif // HAVE_LIBUNWIND_MIPS_SUPPORT } + +void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c index 976a16030407..deb49a44c257 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include #include @@ -31,3 +32,17 @@ int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused) } #endif // HAVE_LIBUNWIND_PPC32_SUPPORT } + +void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c index c508dc89615b..9d7321214fac 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include #include @@ -31,3 +32,17 @@ int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused) } #endif // HAVE_LIBUNWIND_PPC64_SUPPORT } + +void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c index 7088991015e6..ab296931dbbc 100644 --- a/tools/perf/util/libunwind-arch/libunwind-s390.c +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/s390/include/uapi/asm/perf_regs.h" #include #include @@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused) } #endif // HAVE_LIBUNWIND_S390X_SUPPORT } + +void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c index 82dfb2c7152a..2caca0b7b384 100644 --- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "libunwind-arch.h" #include "../debug.h" +#include "../maps.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include @@ -50,3 +51,17 @@ int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused) return perf_x86_64_regnums[unw_regnum]; #endif // HAVE_LIBUNWIND_X86_64_SUPPORT } + +void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 81a97ac34077..27924c70e0e1 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -40,6 +40,7 @@ DECLARE_RC_STRUCT(maps) { #ifdef HAVE_LIBUNWIND_SUPPORT void *addr_space; const struct unwind_libunwind_ops *unwind_libunwind_ops; + uint16_t e_machine; #endif #ifdef HAVE_LIBDW_SUPPORT void *libdw_addr_space_dwfl; @@ -206,6 +207,16 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun { RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops; } + +uint16_t maps__e_machine(const struct maps *maps) +{ + return RC_CHK_ACCESS(maps)->e_machine; +} + +void maps__set_e_machine(struct maps *maps, uint16_t e_machine) +{ + RC_CHK_ACCESS(maps)->e_machine = e_machine; +} #endif #ifdef HAVE_LIBDW_SUPPORT void *maps__libdw_addr_space_dwfl(const struct maps *maps) @@ -1039,6 +1050,14 @@ int maps__copy_from(struct maps *dest, struct maps *parent) down_write(maps__lock(dest)); down_read(maps__lock(parent)); +#ifdef HAVE_LIBUNWIND_SUPPORT + err = unwind__prepare_access(dest, maps__e_machine(parent)); + if (err) { + up_read(maps__lock(parent)); + up_write(maps__lock(dest)); + return err; + } +#endif parent_maps_by_address = maps__maps_by_address(parent); n = maps__nr_maps(parent); if (maps__nr_maps(dest) == 0) { @@ -1068,14 +1087,11 @@ int maps__copy_from(struct maps *dest, struct maps *parent) if (!new) err = -ENOMEM; else { - err = unwind__prepare_access(dest, new, NULL); - if (!err) { - dest_maps_by_address[i] = new; - map__set_kmap_maps(new, dest); - if (dest_maps_by_name) - dest_maps_by_name[i] = map__get(new); - RC_CHK_ACCESS(dest)->nr_maps = i + 1; - } + dest_maps_by_address[i] = new; + map__set_kmap_maps(new, dest); + if (dest_maps_by_name) + dest_maps_by_name[i] = map__get(new); + RC_CHK_ACCESS(dest)->nr_maps = i + 1; } if (err) map__put(new); @@ -1093,9 +1109,7 @@ int maps__copy_from(struct maps *dest, struct maps *parent) if (!new) err = -ENOMEM; else { - err = unwind__prepare_access(dest, new, NULL); - if (!err) - err = __maps__insert(dest, new); + err = __maps__insert(dest, new); } map__put(new); } diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 20c52084ba9e..6469f62c41a8 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -51,6 +51,8 @@ void *maps__addr_space(const struct maps *maps); void maps__set_addr_space(struct maps *maps, void *addr_space); const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps); void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops); +uint16_t maps__e_machine(const struct maps *maps); +void maps__set_e_machine(struct maps *maps, uint16_t e_machine); #endif #ifdef HAVE_LIBDW_SUPPORT void *maps__libdw_addr_space_dwfl(const struct maps *maps); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 22be77225bb0..aac9cb75dcf4 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -358,41 +358,21 @@ size_t thread__fprintf(struct thread *thread, FILE *fp) int thread__insert_map(struct thread *thread, struct map *map) { int ret; + uint16_t e_machine; - ret = unwind__prepare_access(thread__maps(thread), map, NULL); + ret = maps__fixup_overlap_and_insert(thread__maps(thread), map); if (ret) return ret; - return maps__fixup_overlap_and_insert(thread__maps(thread), map); -} - -struct thread__prepare_access_maps_cb_args { - int err; - struct maps *maps; -}; - -static int thread__prepare_access_maps_cb(struct map *map, void *data) -{ - bool initialized = false; - struct thread__prepare_access_maps_cb_args *args = data; - - args->err = unwind__prepare_access(args->maps, map, &initialized); - - return (args->err || initialized) ? 1 : 0; + e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL); + return unwind__prepare_access(thread__maps(thread), e_machine); } static int thread__prepare_access(struct thread *thread) { - struct thread__prepare_access_maps_cb_args args = { - .err = 0, - }; + uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL); - if (dwarf_callchain_users) { - args.maps = thread__maps(thread); - maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb, &args); - } - - return args.err; + return unwind__prepare_access(thread__maps(thread), e_machine); } static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone) diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index adc6e82b5f27..a9af2839e828 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -721,16 +721,6 @@ static int _unwind__prepare_access(struct maps *maps) return 0; } -static void _unwind__flush_access(struct maps *maps) -{ - unw_flush_cache(maps__addr_space(maps), 0, 0); -} - -static void _unwind__finish_access(struct maps *maps) -{ - unw_destroy_addr_space(maps__addr_space(maps)); -} - static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { @@ -835,8 +825,6 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, static struct unwind_libunwind_ops _unwind_libunwind_ops = { .prepare_access = _unwind__prepare_access, - .flush_access = _unwind__flush_access, - .finish_access = _unwind__finish_access, .get_entries = _unwind__get_entries, }; diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index a0016b897dae..00be1286b799 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -7,76 +7,63 @@ #include "debug.h" #include "env.h" #include "callchain.h" +#include "libunwind-arch/libunwind-arch.h" +#include +#include struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops; struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops; struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops; -int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized) +int unwind__prepare_access(struct maps *maps, uint16_t e_machine) { - const char *arch; - enum dso_type dso_type; struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops; - struct dso *dso = map__dso(map); - struct machine *machine; - int err; if (!dwarf_callchain_users) return 0; if (maps__addr_space(maps)) { - pr_debug("unwind: thread map already set, dso=%s\n", dso__name(dso)); - if (initialized) - *initialized = true; + pr_debug3("unwind: thread map already set\n"); return 0; } - machine = maps__machine(maps); - /* env->arch is NULL for live-mode (i.e. perf top) */ - if (!machine->env || !machine->env->arch) - goto out_register; - - dso_type = dso__type(dso, machine); - if (dso_type == DSO__TYPE_UNKNOWN) + if (e_machine == EM_NONE) return 0; - arch = perf_env__arch(machine->env); - - if (!strcmp(arch, "x86")) { - if (dso_type != DSO__TYPE_64BIT) + if (e_machine != EM_HOST) { + /* If not live/local mode. */ + switch (e_machine) { + case EM_386: ops = x86_32_unwind_libunwind_ops; - } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) { - if (dso_type == DSO__TYPE_64BIT) + break; + case EM_AARCH64: ops = arm64_unwind_libunwind_ops; + break; + default: + pr_warning_once("unwind: ELF machine type %d is not supported\n", + e_machine); + return 0; + } } if (!ops) { - pr_warning_once("unwind: target platform=%s is not supported\n", arch); + pr_warning_once("unwind: target platform is not supported\n"); return 0; } -out_register: maps__set_unwind_libunwind_ops(maps, ops); + maps__set_e_machine(maps, e_machine); - err = maps__unwind_libunwind_ops(maps)->prepare_access(maps); - if (initialized) - *initialized = err ? false : true; - return err; + return ops->prepare_access(maps); } void unwind__flush_access(struct maps *maps) { - const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps); - - if (ops) - ops->flush_access(maps); + libunwind_arch__flush_access(maps); } void unwind__finish_access(struct maps *maps) { - const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps); - - if (ops) - ops->finish_access(maps); + libunwind_arch__finish_access(maps); } int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ce256b00b6d2..6a0f117898b2 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -2,6 +2,7 @@ #ifndef __UNWIND_H #define __UNWIND_H +#include #include #include #include "map_symbol.h" @@ -20,8 +21,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); struct unwind_libunwind_ops { int (*prepare_access)(struct maps *maps); - void (*flush_access)(struct maps *maps); - void (*finish_access)(struct maps *maps); int (*get_entries)(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); @@ -64,7 +63,7 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack, bool best_effort); -int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized); +int unwind__prepare_access(struct maps *maps, uint16_t e_machine); void unwind__flush_access(struct maps *maps); void unwind__finish_access(struct maps *maps); #else @@ -81,8 +80,7 @@ static inline int libunwind__get_entries(unwind_entry_cb_t cb __maybe_unused, } static inline int unwind__prepare_access(struct maps *maps __maybe_unused, - struct map *map __maybe_unused, - bool *initialized __maybe_unused) + uint16_t e_machine __maybe_unused) { return 0; } From 88b4275ff542510fa80c32c863571c71d60f8766 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:50 -0700 Subject: [PATCH 1016/5214] perf unwind-libunwind: Remove libunwind-local Local unwinding only works on the machine libunwind is built for, rather than cross platform, the APIs for remote and local unwinding are similar but types like unw_word_t depend on the included header. Place the architecture specific code into the appropriate libunwind-.c file. Put generic code in unwind-libunwind.c and use libunwind-arch to choose the correct implementation based on the thread's e_machine. Structuring the code this way avoids including the unwind-libunwind-local.c for each architecture of remote unwinding. Data is moved into the struct unwind_info to simplify the architecture and generic code, trying to keep as much code as possible generic. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 3 - .../perf/util/libunwind-arch/libunwind-arch.c | 184 ++++ .../perf/util/libunwind-arch/libunwind-arch.h | 234 +++++ .../perf/util/libunwind-arch/libunwind-arm.c | 256 ++++++ .../util/libunwind-arch/libunwind-arm64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-i386.c | 254 ++++++ .../util/libunwind-arch/libunwind-loongarch.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-mips.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc32.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-s390.c | 255 ++++++ .../util/libunwind-arch/libunwind-x86_64.c | 253 ++++++ tools/perf/util/libunwind/arm64.c | 35 - tools/perf/util/libunwind/x86_32.c | 29 - tools/perf/util/maps.c | 10 - tools/perf/util/maps.h | 2 - tools/perf/util/unwind-libunwind-local.c | 834 ------------------ tools/perf/util/unwind-libunwind.c | 680 +++++++++++++- tools/perf/util/unwind.h | 7 - 19 files changed, 3353 insertions(+), 958 deletions(-) delete mode 100644 tools/perf/util/libunwind/arm64.c delete mode 100644 tools/perf/util/libunwind/x86_32.c delete mode 100644 tools/perf/util/unwind-libunwind-local.c diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bf4204135ccb..2bb60f50f62d 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -226,11 +226,8 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o perf-util-$(CONFIG_LIBDW) += libdw.o perf-util-$(CONFIG_LIBDW) += unwind-libdw.o -perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/ -perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o -perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o ifeq ($(CONFIG_LIBTRACEEVENT),y) perf-util-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c index 9692e6c81492..8539b4233df4 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -112,3 +112,187 @@ void libunwind_arch__finish_access(struct maps *maps) break; } } + +void *libunwind_arch__create_addr_space(unsigned int e_machine) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__create_addr_space_arm(); + case EM_AARCH64: + return __libunwind_arch__create_addr_space_arm64(); + case EM_LOONGARCH: + return __libunwind_arch__create_addr_space_loongarch(); + case EM_MIPS: + return __libunwind_arch__create_addr_space_mips(); + case EM_PPC: + return __libunwind_arch__create_addr_space_ppc32(); + case EM_PPC64: + return __libunwind_arch__create_addr_space_ppc64(); + case EM_S390: + return __libunwind_arch__create_addr_space_s390(); + case EM_386: + return __libunwind_arch__create_addr_space_i386(); + case EM_X86_64: + return __libunwind_arch__create_addr_space_x86_64(); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi, + need_unwind_info, arg); + case EM_AARCH64: + return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi, + need_unwind_info, arg); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di, pi, + need_unwind_info, arg); + case EM_MIPS: + return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC: + return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC64: + return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi, + need_unwind_info, arg); + case EM_S390: + return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi, + need_unwind_info, arg); + case EM_386: + return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi, + need_unwind_info, arg); + case EM_X86_64: + return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi, + need_unwind_info, arg); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_AARCH64: + return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debug, ip, + segbase, obj_name, + start, end); + case EM_MIPS: + return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_PPC: + return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_PPC64: + return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_S390: + return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_386: + return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip, segbase, + obj_name, start, end); + case EM_X86_64: + return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, ip, segbase, + obj_name, start, end); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, int max_stack, + bool best_effort, uint16_t e_machine, + uint64_t first_ip) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack, + best_effort, first_ip); + case EM_AARCH64: + return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack, + best_effort, first_ip); + case EM_LOONGARCH: + return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_stack, + best_effort, first_ip); + case EM_MIPS: + return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC: + return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC64: + return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack, + best_effort, first_ip); + case EM_S390: + return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack, + best_effort, first_ip); + case EM_386: + return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack, + best_effort, first_ip); + case EM_X86_64: + return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stack, + best_effort, first_ip); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui) +{ + free(ui); +} + +int libunwind_arch__unwind_step(struct unwind_info *ui) +{ + switch (ui->e_machine) { + case EM_ARM: + return __libunwind_arch__unwind_step_arm(ui); + case EM_AARCH64: + return __libunwind_arch__unwind_step_arm64(ui); + case EM_LOONGARCH: + return __libunwind_arch__unwind_step_loongarch(ui); + case EM_MIPS: + return __libunwind_arch__unwind_step_mips(ui); + case EM_PPC: + return __libunwind_arch__unwind_step_ppc32(ui); + case EM_PPC64: + return __libunwind_arch__unwind_step_ppc64(ui); + case EM_S390: + return __libunwind_arch__unwind_step_s390(ui); + case EM_386: + return __libunwind_arch__unwind_step_i386(ui); + case EM_X86_64: + return __libunwind_arch__unwind_step_x86_64(ui); + default: + pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine); + return -EINVAL; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h index c00277a5e914..2bf7fc33313b 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -2,7 +2,36 @@ #ifndef __LIBUNWIND_ARCH_H #define __LIBUNWIND_ARCH_H +#include +#include +#include + +struct machine; struct maps; +struct perf_sample; +struct thread; + +struct unwind_info { + struct machine *machine; + struct thread *thread; + struct perf_sample *sample; + void *cursor; + uint64_t *ips; + int cur_ip; + int max_ips; + unsigned int unw_word_t_size; + uint16_t e_machine; + bool best_effort; +}; + +struct libarch_unwind__dyn_info { + uint64_t start_ip; + uint64_t end_ip; + uint64_t segbase; + uint64_t table_data; + uint64_t table_len; +}; +struct libarch_unwind__proc_info; int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); @@ -37,4 +66,209 @@ void __libunwind_arch__finish_access_i386(struct maps *maps); void __libunwind_arch__finish_access_x86_64(struct maps *maps); void libunwind_arch__finish_access(struct maps *maps); +void *__libunwind_arch__create_addr_space_arm(void); +void *__libunwind_arch__create_addr_space_arm64(void); +void *__libunwind_arch__create_addr_space_loongarch(void); +void *__libunwind_arch__create_addr_space_mips(void); +void *__libunwind_arch__create_addr_space_ppc32(void); +void *__libunwind_arch__create_addr_space_ppc64(void); +void *__libunwind_arch__create_addr_space_s390(void); +void *__libunwind_arch__create_addr_space_i386(void); +void *__libunwind_arch__create_addr_space_x86_64(void); +void *libunwind_arch__create_addr_space(unsigned int e_machine); + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg); +int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int __write, void *arg); +int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __write, void *arg); + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_mips(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_s390(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_i386(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint16_t e_machine, + uint64_t first_ip); + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui); + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui); +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui); +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui); +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui); +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui); +int libunwind_arch__unwind_step(struct unwind_info *ui); + #endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c index bbaf01406c52..61b1c5a70b7f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_ARM_SUPPORT @@ -32,3 +36,255 @@ void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm(void) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_ARM; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c index 8ba510089736..4d467dd440a1 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm64.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm64/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT @@ -32,3 +36,254 @@ void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm64(void) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_AARCH64; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c index 383f0a44d290..3423be45096a 100644 --- a/tools/perf/util/libunwind-arch/libunwind-i386.c +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include #ifdef HAVE_LIBUNWIND_X86_SUPPORT @@ -56,3 +59,254 @@ void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_i386(void) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_I386; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c index 7ff459849930..ff225a540b43 100644 --- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT @@ -40,3 +44,254 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_loongarch(void) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_LOONGARCH; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c index 1fa81742ff4a..ed14a8f94fa7 100644 --- a/tools/perf/util/libunwind-arch/libunwind-mips.c +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/mips/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_mips(void) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_MIPS; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c index deb49a44c257..289a3b8cde75 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_PPC32_SUPPORT @@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc32(void) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_PPC; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c index 9d7321214fac..a4c9b6cc3c00 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_PPC64_SUPPORT @@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc64(void) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_PPC64; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c index ab296931dbbc..3e57cfc451c6 100644 --- a/tools/perf/util/libunwind-arch/libunwind-s390.c +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/s390/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include #ifdef HAVE_LIBUNWIND_S390X_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_s390(void) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_S390; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c index 2caca0b7b384..b13e9b1cc7a3 100644 --- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT @@ -65,3 +68,253 @@ void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_x86_64(void) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_X86_64; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c deleted file mode 100644 index 15670a964495..000000000000 --- a/tools/perf/util/libunwind/arm64.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each arm64 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-aarch64.h" -#define perf_event_arm_regs perf_event_arm64_regs -#include <../../../arch/arm64/include/uapi/asm/perf_regs.h> -#undef perf_event_arm_regs -#include "../../arch/arm64/util/unwind-libunwind.c" - -/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind, - * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64 - * unwind methods. - */ -#undef NO_LIBUNWIND_DEBUG_FRAME -#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64 -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -arm64_unwind_libunwind_ops = &_unwind_libunwind_ops; diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c deleted file mode 100644 index 1e9fb8bfec44..000000000000 --- a/tools/perf/util/libunwind/x86_32.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/x86/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each x86 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-x86.h" - -/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no - * dwarf_find_debug_frame() function. - */ -#ifndef NO_LIBUNWIND_DEBUG_FRAME -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops; diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 27924c70e0e1..923935ee21b6 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -198,16 +198,6 @@ void maps__set_addr_space(struct maps *maps, void *addr_space) RC_CHK_ACCESS(maps)->addr_space = addr_space; } -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps) -{ - return RC_CHK_ACCESS(maps)->unwind_libunwind_ops; -} - -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops) -{ - RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops; -} - uint16_t maps__e_machine(const struct maps *maps) { return RC_CHK_ACCESS(maps)->e_machine; diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 6469f62c41a8..5b80b199685e 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -49,8 +49,6 @@ refcount_t *maps__refcnt(struct maps *maps); /* Test only. */ #ifdef HAVE_LIBUNWIND_SUPPORT void *maps__addr_space(const struct maps *maps); void maps__set_addr_space(struct maps *maps, void *addr_space); -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps); -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops); uint16_t maps__e_machine(const struct maps *maps); void maps__set_e_machine(struct maps *maps, uint16_t e_machine); #endif diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c deleted file mode 100644 index a9af2839e828..000000000000 --- a/tools/perf/util/unwind-libunwind-local.c +++ /dev/null @@ -1,834 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. - * - * Lots of this code have been borrowed or heavily inspired from parts of - * the libunwind 0.99 code which are (amongst other contributors I may have - * forgotten): - * - * Copyright (C) 2002-2007 Hewlett-Packard Co - * Contributed by David Mosberger-Tang - * - * And the bugs have been added by: - * - * Copyright (C) 2010, Frederic Weisbecker - * Copyright (C) 2012, Jiri Olsa - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include -#endif -#include "callchain.h" -#include "thread.h" -#include "session.h" -#include "perf_regs.h" -#include "unwind.h" -#include "map.h" -#include "symbol.h" -#include "debug.h" -#include "asm/bug.h" -#include "dso.h" -#include "libunwind-arch/libunwind-arch.h" - -extern int -UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, - unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ -#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ - -/* Pointer-encoding formats: */ -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ -#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ -#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ -#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ -#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ - -/* Pointer-encoding application: */ -#define DW_EH_PE_absptr 0x00 /* absolute value */ -#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ - -/* - * The following are not documented by LSB v1.3, yet they are used by - * GCC, presumably they aren't documented by LSB since they aren't - * used on Linux: - */ -#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ -#define DW_EH_PE_aligned 0x50 /* aligned pointer */ - -/* Flags intentionally not handled, since they're not needed: - * #define DW_EH_PE_indirect 0x80 - * #define DW_EH_PE_uleb128 0x01 - * #define DW_EH_PE_udata2 0x02 - * #define DW_EH_PE_sleb128 0x09 - * #define DW_EH_PE_sdata2 0x0a - * #define DW_EH_PE_textrel 0x20 - * #define DW_EH_PE_datarel 0x30 - */ - -struct unwind_info { - struct perf_sample *sample; - struct machine *machine; - struct thread *thread; - uint16_t e_machine; - bool best_effort; -}; - -#define dw_read(ptr, type, end) ({ \ - type *__p = (type *) ptr; \ - type __v; \ - if ((__p + 1) > (type *) end) \ - return -EINVAL; \ - __v = *__p++; \ - ptr = (typeof(ptr)) __p; \ - __v; \ - }) - -static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, - u8 encoding) -{ - u8 *cur = *p; - *val = 0; - - switch (encoding) { - case DW_EH_PE_omit: - *val = 0; - goto out; - case DW_EH_PE_ptr: - *val = dw_read(cur, unsigned long, end); - goto out; - default: - break; - } - - switch (encoding & DW_EH_PE_APPL_MASK) { - case DW_EH_PE_absptr: - break; - case DW_EH_PE_pcrel: - *val = (unsigned long) cur; - break; - default: - return -EINVAL; - } - - if ((encoding & 0x07) == 0x00) - encoding |= DW_EH_PE_udata4; - - switch (encoding & DW_EH_PE_FORMAT_MASK) { - case DW_EH_PE_sdata4: - *val += dw_read(cur, s32, end); - break; - case DW_EH_PE_udata4: - *val += dw_read(cur, u32, end); - break; - case DW_EH_PE_sdata8: - *val += dw_read(cur, s64, end); - break; - case DW_EH_PE_udata8: - *val += dw_read(cur, u64, end); - break; - default: - return -EINVAL; - } - - out: - *p = cur; - return 0; -} - -#define dw_read_encoded_value(ptr, end, enc) ({ \ - u64 __v; \ - if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ - return -EINVAL; \ - } \ - __v; \ - }) - -static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset) -{ - Elf *elf; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - int ret = -1; - - elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf == NULL) - return -1; - - if (gelf_getehdr(elf, &ehdr) == NULL) - goto out_err; - - if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) - goto out_err; - - *address = shdr.sh_addr; - *offset = shdr.sh_offset; - ret = 0; -out_err: - elf_end(elf); - return ret; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static u64 elf_section_offset(int fd, const char *name) -{ - u64 address, offset = 0; - - if (elf_section_address_and_offset(fd, name, &address, &offset)) - return 0; - - return offset; -} -#endif - -static u64 elf_base_address(int fd) -{ - Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - GElf_Phdr phdr; - u64 retval = 0; - size_t i, phdrnum = 0; - - if (elf == NULL) - return 0; - (void)elf_getphdrnum(elf, &phdrnum); - /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */ - for (i = 0; i < phdrnum; i++) { - if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) { - retval = phdr.p_vaddr & -getpagesize(); - break; - } - } - - elf_end(elf); - return retval; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int elf_is_exec(int fd, const char *name) -{ - Elf *elf; - GElf_Ehdr ehdr; - int retval = 0; - - elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf == NULL) - return 0; - if (gelf_getehdr(elf, &ehdr) == NULL) - goto out; - - retval = (ehdr.e_type == ET_EXEC); - -out: - elf_end(elf); - pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval); - return retval; -} -#endif - -struct table_entry { - u32 start_ip_offset; - u32 fde_offset; -}; - -struct eh_frame_hdr { - unsigned char version; - unsigned char eh_frame_ptr_enc; - unsigned char fde_count_enc; - unsigned char table_enc; - - /* - * The rest of the header is variable-length and consists of the - * following members: - * - * encoded_t eh_frame_ptr; - * encoded_t fde_count; - */ - - /* A single encoded pointer should not be more than 8 bytes. */ - u64 enc[2]; - - /* - * struct { - * encoded_t start_ip; - * encoded_t fde_addr; - * } binary_search_table[fde_count]; - */ - char data[]; -} __packed; - -static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, - u64 offset, u64 *table_data_offset, u64 *fde_count) -{ - struct eh_frame_hdr hdr; - u8 *enc = (u8 *) &hdr.enc; - u8 *end = (u8 *) &hdr.data; - ssize_t r; - - r = dso__data_read_offset(dso, machine, offset, - (u8 *) &hdr, sizeof(hdr)); - if (r != sizeof(hdr)) - return -EINVAL; - - /* We dont need eh_frame_ptr, just skip it. */ - dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); - - *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); - *table_data_offset = enc - (u8 *) &hdr; - return 0; -} - -struct read_unwind_spec_eh_frame_maps_cb_args { - struct dso *dso; - u64 base_addr; -}; - -static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) -{ - - struct read_unwind_spec_eh_frame_maps_cb_args *args = data; - - if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr) - args->base_addr = map__start(map) - map__pgoff(map); - - return 0; -} - - -static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui, - u64 *table_data, u64 *segbase, - u64 *fde_count) -{ - struct read_unwind_spec_eh_frame_maps_cb_args args = { - .dso = dso, - .base_addr = UINT64_MAX, - }; - int ret, fd; - - if (dso__data(dso)->eh_frame_hdr_offset == 0) { - if (!dso__data_get_fd(dso, ui->machine, &fd)) - return -EINVAL; - - /* Check the .eh_frame section for unwinding info */ - ret = elf_section_address_and_offset(fd, ".eh_frame_hdr", - &dso__data(dso)->eh_frame_hdr_addr, - &dso__data(dso)->eh_frame_hdr_offset); - dso__data(dso)->elf_base_addr = elf_base_address(fd); - dso__data_put_fd(dso); - if (ret || dso__data(dso)->eh_frame_hdr_offset == 0) - return -EINVAL; - } - - maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args); - - args.base_addr -= dso__data(dso)->elf_base_addr; - /* Address of .eh_frame_hdr */ - *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr; - ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset, - table_data, fde_count); - if (ret) - return ret; - /* binary_search_table offset plus .eh_frame_hdr address */ - *table_data += *segbase; - return 0; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int read_unwind_spec_debug_frame(struct dso *dso, - struct machine *machine, u64 *offset) -{ - int fd; - u64 ofs = dso__data(dso)->debug_frame_offset; - - /* debug_frame can reside in: - * - dso - * - debug pointed by symsrc_filename - * - gnu_debuglink, which doesn't necessary - * has to be pointed by symsrc_filename - */ - if (ofs == 0) { - if (dso__data_get_fd(dso, machine, &fd)) { - ofs = elf_section_offset(fd, ".debug_frame"); - dso__data_put_fd(dso); - } - - if (ofs <= 0) { - fd = open(dso__symsrc_filename(dso), O_RDONLY); - if (fd >= 0) { - ofs = elf_section_offset(fd, ".debug_frame"); - close(fd); - } - } - - if (ofs <= 0) { - char *debuglink = malloc(PATH_MAX); - int ret = 0; - - if (debuglink == NULL) { - pr_err("unwind: Can't read unwind spec debug frame.\n"); - return -ENOMEM; - } - - ret = dso__read_binary_type_filename( - dso, DSO_BINARY_TYPE__DEBUGLINK, - machine->root_dir, debuglink, PATH_MAX); - if (!ret) { - fd = open(debuglink, O_RDONLY); - if (fd >= 0) { - ofs = elf_section_offset(fd, - ".debug_frame"); - close(fd); - } - } - if (ofs > 0) { - if (dso__symsrc_filename(dso) != NULL) { - pr_warning( - "%s: overwrite symsrc(%s,%s)\n", - __func__, - dso__symsrc_filename(dso), - debuglink); - dso__free_symsrc_filename(dso); - } - dso__set_symsrc_filename(dso, debuglink); - } else { - free(debuglink); - } - } - - dso__data(dso)->debug_frame_offset = ofs; - } - - *offset = ofs; - if (*offset) - return 0; - - return -EINVAL; -} -#endif - -static struct map *find_map(unw_word_t ip, struct unwind_info *ui) -{ - struct addr_location al; - struct map *ret; - - addr_location__init(&al); - thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); - ret = map__get(al.map); - addr_location__exit(&al); - return ret; -} - -static int -find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, - int need_unwind_info, void *arg) -{ - struct unwind_info *ui = arg; - struct map *map; - struct dso *dso; - unw_dyn_info_t di; - u64 table_data, segbase, fde_count; - int ret = -EINVAL; - - map = find_map(ip, ui); - if (!map) - return -EINVAL; - - dso = map__dso(map); - if (!dso) { - map__put(map); - return -EINVAL; - } - - pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso)); - - /* Check the .eh_frame section for unwinding info */ - if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) { - memset(&di, 0, sizeof(di)); - di.format = UNW_INFO_FORMAT_REMOTE_TABLE; - di.start_ip = map__start(map); - di.end_ip = map__end(map); - di.u.rti.segbase = segbase; - di.u.rti.table_data = table_data; - di.u.rti.table_len = fde_count * sizeof(struct table_entry) - / sizeof(unw_word_t); - ret = dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } - -#ifndef NO_LIBUNWIND_DEBUG_FRAME - /* Check the .debug_frame section for unwinding info */ - if (ret < 0 && - !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { - int fd; - u64 start = map__start(map); - unw_word_t base = start; - const char *symfile; - - if (dso__data_get_fd(dso, ui->machine, &fd)) { - if (elf_is_exec(fd, dso__name(dso))) - base = 0; - dso__data_put_fd(dso); - } - - symfile = dso__symsrc_filename(dso) ?: dso__name(dso); - - memset(&di, 0, sizeof(di)); - if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map))) - ret = dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } -#endif - map__put(map); - return ret; -} - -static int access_fpreg(unw_addr_space_t __maybe_unused as, - unw_regnum_t __maybe_unused num, - unw_fpreg_t __maybe_unused *val, - int __maybe_unused __write, - void __maybe_unused *arg) -{ - pr_err("unwind: access_fpreg unsupported\n"); - return -UNW_EINVAL; -} - -static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused *dil_addr, - void __maybe_unused *arg) -{ - return -UNW_ENOINFO; -} - -static int resume(unw_addr_space_t __maybe_unused as, - unw_cursor_t __maybe_unused *cu, - void __maybe_unused *arg) -{ - pr_err("unwind: resume unsupported\n"); - return -UNW_EINVAL; -} - -static int -get_proc_name(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused addr, - char __maybe_unused *bufp, size_t __maybe_unused buf_len, - unw_word_t __maybe_unused *offp, void __maybe_unused *arg) -{ - pr_err("unwind: get_proc_name unsupported\n"); - return -UNW_EINVAL; -} - -static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, - unw_word_t *data) -{ - struct map *map; - struct dso *dso; - ssize_t size; - - map = find_map(addr, ui); - if (!map) { - pr_debug("unwind: no map for %lx\n", (unsigned long)addr); - return -1; - } - - dso = map__dso(map); - - if (!dso) { - map__put(map); - return -1; - } - - size = dso__data_read_addr(dso, map, ui->machine, - addr, (u8 *) data, sizeof(*data)); - map__put(map); - return !(size == sizeof(*data)); -} - -static int access_mem(unw_addr_space_t __maybe_unused as, - unw_word_t addr, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui = arg; - struct stack_dump *stack = &ui->sample->user_stack; - u64 start, end; - int offset; - int ret; - - /* Don't support write, probably not needed. */ - if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) { - *valp = 0; - return 0; - } - - ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample), - perf_arch_reg_sp(ui->e_machine)); - if (ret) - return ret; - - end = start + stack->size; - - /* Check overflow. */ - if (addr + sizeof(unw_word_t) < addr) - return -EINVAL; - - if (addr < start || addr + sizeof(unw_word_t) >= end) { - ret = access_dso_mem(ui, addr, valp); - if (ret) { - pr_debug("unwind: access_mem %p not inside range" - " 0x%" PRIx64 "-0x%" PRIx64 "\n", - (void *) (uintptr_t) addr, start, end); - *valp = 0; - return ret; - } - return 0; - } - - offset = addr - start; - *valp = *(unw_word_t *)&stack->data[offset]; - pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", - (void *) (uintptr_t) addr, (unsigned long)*valp, offset); - return 0; -} - -static int access_reg(unw_addr_space_t __maybe_unused as, - unw_regnum_t regnum, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui = arg; - int id, ret; - u64 val; - - /* Don't support write, I suspect we don't need it. */ - if (__write) { - pr_err("unwind: access_reg w %d\n", regnum); - return 0; - } - - if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { - *valp = 0; - return 0; - } - - id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); - if (id < 0) - return -EINVAL; - - ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); - if (ret) { - if (!ui->best_effort) - pr_err("unwind: can't read reg %d\n", regnum); - return ret; - } - - *valp = (unw_word_t) val; - pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); - return 0; -} - -static void put_unwind_info(unw_addr_space_t __maybe_unused as, - unw_proc_info_t *pi __maybe_unused, - void *arg __maybe_unused) -{ - pr_debug("unwind: put_unwind_info called\n"); -} - -static int entry(u64 ip, struct thread *thread, - unwind_entry_cb_t cb, void *arg) -{ - struct unwind_entry e; - struct addr_location al; - int ret; - - addr_location__init(&al); - e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); - e.ip = ip; - e.ms.map = al.map; - e.ms.thread = thread__get(al.thread); - - pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", - al.sym ? al.sym->name : "''", - ip, - al.map ? map__map_ip(al.map, ip) : (u64) 0); - - ret = cb(&e, arg); - addr_location__exit(&al); - return ret; -} - -static void display_error(int err) -{ - switch (err) { - case UNW_EINVAL: - pr_err("unwind: Only supports local.\n"); - break; - case UNW_EUNSPEC: - pr_err("unwind: Unspecified error.\n"); - break; - case UNW_EBADREG: - pr_err("unwind: Register unavailable.\n"); - break; - default: - break; - } -} - -static unw_accessors_t accessors = { - .find_proc_info = find_proc_info, - .put_unwind_info = put_unwind_info, - .get_dyn_info_list_addr = get_dyn_info_list_addr, - .access_mem = access_mem, - .access_reg = access_reg, - .access_fpreg = access_fpreg, - .resume = resume, - .get_proc_name = get_proc_name, -}; - -static int _unwind__prepare_access(struct maps *maps) -{ - void *addr_space = unw_create_addr_space(&accessors, 0); - - maps__set_addr_space(maps, addr_space); - if (!addr_space) { - pr_err("unwind: Can't create unwind address space.\n"); - return -ENOMEM; - } - - unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); - return 0; -} - -static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, - void *arg, int max_stack) -{ - u64 val; - unw_word_t ips[max_stack]; - unw_addr_space_t addr_space; - unw_cursor_t c; - int ret, i = 0; - - ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), - perf_arch_reg_ip(ui->e_machine)); - if (ret) - return 0; - - ips[i++] = (unw_word_t) val; - - /* - * If we need more than one entry, do the DWARF - * unwind itself. - */ - if (max_stack - 1 > 0) { - WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); - addr_space = maps__addr_space(thread__maps(ui->thread)); - - if (addr_space == NULL) - return 0; - - ret = unw_init_remote(&c, addr_space, ui); - if (ret && !ui->best_effort) - display_error(ret); - - while (!ret && (unw_step(&c) > 0) && i < max_stack) { - unw_get_reg(&c, UNW_REG_IP, &ips[i]); - - /* - * Decrement the IP for any non-activation frames. - * this is required to properly find the srcline - * for caller frames. - * See also the documentation for dwfl_frame_pc(), - * which this code tries to replicate. - */ - if (unw_is_signal_frame(&c) <= 0) - --ips[i]; - - ++i; - } - - max_stack = i; - } - - /* - * Display what we got based on the order setup. - */ - int entries = 0; - for (i = 0; i < max_stack && !ret; i++) { - int j = i; - - if (callchain_param.order == ORDER_CALLER) - j = max_stack - i - 1; - if (ips[j]) { - ret = entry(ips[j], ui->thread, cb, arg); - if (ret) - break; - entries++; - } - } - - /* - * Unwinder return contract: - * > 0 : unwinding succeeded (stops fallback). - * 0 : unwinding failed without yielding frames. Ignore non-fatal errors - * (e.g. stepping failure) to allow fallback unwinder or kernel callchains. - * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. - */ - if (ret == -ENOMEM) - return -ENOMEM; - return (entries > 0 || ret == 0) ? entries : 0; -} - -static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, - struct thread *thread, - struct perf_sample *data, int max_stack, - bool best_effort) -{ - struct unwind_info ui = { - .sample = data, - .thread = thread, - .machine = maps__machine(thread__maps(thread)), - .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL), - .best_effort = best_effort - }; - - if (!data->user_regs || !data->user_regs->regs) - return 0; - - if (max_stack <= 0) - return 0; - - return get_entries(&ui, cb, arg, max_stack); -} - -static struct unwind_libunwind_ops -_unwind_libunwind_ops = { - .prepare_access = _unwind__prepare_access, - .get_entries = _unwind__get_entries, -}; - -#ifndef REMOTE_UNWIND_LIBUNWIND -struct unwind_libunwind_ops * -local_unwind_libunwind_ops = &_unwind_libunwind_ops; -#endif diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 00be1286b799..73d191ce51a5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -1,23 +1,560 @@ // SPDX-License-Identifier: GPL-2.0 -#include "unwind.h" -#include "dso.h" -#include "map.h" -#include "thread.h" -#include "session.h" -#include "debug.h" -#include "env.h" #include "callchain.h" +#include "debug.h" +#include "dso.h" +#include "env.h" +#include "map.h" +#include "perf_regs.h" +#include "session.h" +#include "symbol.h" +#include "thread.h" +#include "unwind.h" #include "libunwind-arch/libunwind-arch.h" #include #include +#include +#include +#include -struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops; -struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops; -struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops; +#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ +#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ + +/* Pointer-encoding formats: */ +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ +#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ +#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ +#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ +#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ + +/* Pointer-encoding application: */ +#define DW_EH_PE_absptr 0x00 /* absolute value */ +#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ + +/* + * The following are not documented by LSB v1.3, yet they are used by + * GCC, presumably they aren't documented by LSB since they aren't + * used on Linux: + */ +#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ +#define DW_EH_PE_aligned 0x50 /* aligned pointer */ + +/* Flags intentionally not handled, since they're not needed: + * #define DW_EH_PE_indirect 0x80 + * #define DW_EH_PE_uleb128 0x01 + * #define DW_EH_PE_udata2 0x02 + * #define DW_EH_PE_sleb128 0x09 + * #define DW_EH_PE_sdata2 0x0a + * #define DW_EH_PE_textrel 0x20 + * #define DW_EH_PE_datarel 0x30 + */ + +#define dw_read(ptr, type, end) ({ \ + type *__p = (type *) ptr; \ + type __v; \ + if ((__p + 1) > (type *) end) \ + return -EINVAL; \ + __v = *__p++; \ + ptr = (typeof(ptr)) __p; \ + __v; \ + }) + +static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, + u8 encoding) +{ + u8 *cur = *p; + *val = 0; + + switch (encoding) { + case DW_EH_PE_omit: + *val = 0; + goto out; + case DW_EH_PE_ptr: + *val = dw_read(cur, unsigned long, end); + goto out; + default: + break; + } + + switch (encoding & DW_EH_PE_APPL_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + *val = (unsigned long) cur; + break; + default: + return -EINVAL; + } + + if ((encoding & 0x07) == 0x00) + encoding |= DW_EH_PE_udata4; + + switch (encoding & DW_EH_PE_FORMAT_MASK) { + case DW_EH_PE_sdata4: + *val += dw_read(cur, s32, end); + break; + case DW_EH_PE_udata4: + *val += dw_read(cur, u32, end); + break; + case DW_EH_PE_sdata8: + *val += dw_read(cur, s64, end); + break; + case DW_EH_PE_udata8: + *val += dw_read(cur, u64, end); + break; + default: + return -EINVAL; + } + + out: + *p = cur; + return 0; +} + +#define dw_read_encoded_value(ptr, end, enc) ({ \ + u64 __v; \ + if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ + return -EINVAL; \ + } \ + __v; \ + }) + +static u64 elf_base_address(int fd) +{ + Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + GElf_Phdr phdr; + u64 retval = 0; + size_t i, phdrnum = 0; + + if (elf == NULL) + return 0; + (void)elf_getphdrnum(elf, &phdrnum); + /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */ + for (i = 0; i < phdrnum; i++) { + if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) { + retval = phdr.p_vaddr & -getpagesize(); + break; + } + } + + elf_end(elf); + return retval; +} + +static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, + u64 offset, u64 *table_data_offset, u64 *fde_count) +{ + struct eh_frame_hdr { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + + /* + * The rest of the header is variable-length and consists of the + * following members: + * + * encoded_t eh_frame_ptr; + * encoded_t fde_count; + */ + + /* A single encoded pointer should not be more than 8 bytes. */ + u64 enc[2]; + + /* + * struct { + * encoded_t start_ip; + * encoded_t fde_addr; + * } binary_search_table[fde_count]; + */ + char data[]; + } __packed hdr; + u8 *enc = (u8 *) &hdr.enc; + u8 *end = (u8 *) &hdr.data; + ssize_t r; + + r = dso__data_read_offset(dso, machine, offset, (u8 *) &hdr, sizeof(hdr)); + if (r != sizeof(hdr)) + return -EINVAL; + + /* We dont need eh_frame_ptr, just skip it. */ + dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); + + *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); + *table_data_offset = enc - (u8 *) &hdr; + return 0; +} + +struct read_unwind_spec_eh_frame_maps_cb_args { + struct dso *dso; + u64 base_addr; +}; + +static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) +{ + + struct read_unwind_spec_eh_frame_maps_cb_args *args = data; + + if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr) + args->base_addr = map__start(map) - map__pgoff(map); + + return 0; +} + +static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + int ret = -1; + + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf == NULL) + return -1; + + if (gelf_getehdr(elf, &ehdr) == NULL) + goto out_err; + + if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) + goto out_err; + + *address = shdr.sh_addr; + *offset = shdr.sh_offset; + ret = 0; +out_err: + elf_end(elf); + return ret; +} + +static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui, + u64 *table_data, u64 *segbase, + u64 *fde_count) +{ + struct read_unwind_spec_eh_frame_maps_cb_args args = { + .dso = dso, + .base_addr = UINT64_MAX, + }; + int ret, fd; + + if (dso__data(dso)->eh_frame_hdr_offset == 0) { + if (!dso__data_get_fd(dso, ui->machine, &fd)) + return -EINVAL; + + /* Check the .eh_frame section for unwinding info */ + ret = elf_section_address_and_offset(fd, ".eh_frame_hdr", + &dso__data(dso)->eh_frame_hdr_addr, + &dso__data(dso)->eh_frame_hdr_offset); + dso__data(dso)->elf_base_addr = elf_base_address(fd); + dso__data_put_fd(dso); + if (ret || dso__data(dso)->eh_frame_hdr_offset == 0) + return -EINVAL; + } + + maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args); + + args.base_addr -= dso__data(dso)->elf_base_addr; + /* Address of .eh_frame_hdr */ + *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr; + ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset, + table_data, fde_count); + if (ret) + return ret; + /* binary_search_table offset plus .eh_frame_hdr address */ + *table_data += *segbase; + return 0; +} + +static u64 elf_section_offset(int fd, const char *name) +{ + u64 address, offset = 0; + + if (elf_section_address_and_offset(fd, name, &address, &offset)) + return 0; + + return offset; +} + +static int read_unwind_spec_debug_frame(struct dso *dso, + struct machine *machine, u64 *offset) +{ + int fd; + u64 ofs = dso__data(dso)->debug_frame_offset; + + /* debug_frame can reside in: + * - dso + * - debug pointed by symsrc_filename + * - gnu_debuglink, which doesn't necessary + * has to be pointed by symsrc_filename + */ + if (ofs == 0) { + if (dso__data_get_fd(dso, machine, &fd)) { + ofs = elf_section_offset(fd, ".debug_frame"); + dso__data_put_fd(dso); + } + + if (ofs <= 0) { + fd = open(dso__symsrc_filename(dso), O_RDONLY); + if (fd >= 0) { + ofs = elf_section_offset(fd, ".debug_frame"); + close(fd); + } + } + + if (ofs <= 0) { + char *debuglink = malloc(PATH_MAX); + int ret = 0; + + if (debuglink == NULL) { + pr_err("unwind: Can't read unwind spec debug frame.\n"); + return -ENOMEM; + } + + ret = dso__read_binary_type_filename( + dso, DSO_BINARY_TYPE__DEBUGLINK, + machine->root_dir, debuglink, PATH_MAX); + if (!ret) { + fd = open(debuglink, O_RDONLY); + if (fd >= 0) { + ofs = elf_section_offset(fd, + ".debug_frame"); + close(fd); + } + } + if (ofs > 0) { + if (dso__symsrc_filename(dso) != NULL) { + pr_warning( + "%s: overwrite symsrc(%s,%s)\n", + __func__, + dso__symsrc_filename(dso), + debuglink); + dso__free_symsrc_filename(dso); + } + dso__set_symsrc_filename(dso, debuglink); + } else { + free(debuglink); + } + } + + dso__data(dso)->debug_frame_offset = ofs; + } + + *offset = ofs; + if (*offset) + return 0; + + return -EINVAL; +} + +static struct map *find_map(uint64_t ip, struct unwind_info *ui) +{ + struct addr_location al; + struct map *ret; + + addr_location__init(&al); + thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + ret = map__get(al.map); + addr_location__exit(&al); + return ret; +} + +static int elf_is_exec(int fd, const char *name) +{ + Elf *elf; + GElf_Ehdr ehdr; + int retval = 0; + + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf == NULL) + return 0; + if (gelf_getehdr(elf, &ehdr) == NULL) + goto out; + + retval = (ehdr.e_type == ET_EXEC); + +out: + elf_end(elf); + pr_debug3("unwind: elf_is_exec(%s): %d\n", name, retval); + return retval; +} + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg) +{ + struct unwind_info *ui = arg; + struct map *map; + struct dso *dso; + u64 table_data, segbase, fde_count; + int ret = -EINVAL; + + map = find_map(ip, ui); + if (!map) + return -EINVAL; + + dso = map__dso(map); + if (!dso) { + map__put(map); + return -EINVAL; + } + + pr_debug3("unwind: find_proc_info dso %s\n", dso__name(dso)); + + /* Check the .eh_frame section for unwinding info */ + if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) { + struct table_entry { + u32 start_ip_offset; + u32 fde_offset; + }; + struct libarch_unwind__dyn_info di = { + .start_ip = map__start(map), + .end_ip = map__end(map), + .segbase = segbase, + .table_data = table_data, + .table_len = fde_count * sizeof(struct table_entry) / ui->unw_word_t_size, + }; + + ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi, + need_unwind_info, arg); + } + + /* Check the .debug_frame section for unwinding info */ + if (ret < 0 && !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { + int fd; + u64 start = map__start(map); + u64 base = start; + const char *symfile; + struct libarch_unwind__dyn_info di = {}; + + if (dso__data_get_fd(dso, ui->machine, &fd)) { + if (elf_is_exec(fd, dso__name(dso))) + base = 0; + dso__data_put_fd(dso); + } + + symfile = dso__symsrc_filename(dso) ?: dso__name(dso); + + if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=*/0, &di, ip, + base, symfile, start, map__end(map))) { + ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi, + need_unwind_info, arg); + } + } + map__put(map); + return ret; +} + +static int access_dso_mem(struct unwind_info *ui, uint64_t addr, void *data_word) +{ + struct map *map; + struct dso *dso; + ssize_t size; + + map = find_map(addr, ui); + if (!map) { + pr_debug("unwind: no map for %lx\n", (unsigned long)addr); + return -1; + } + + dso = map__dso(map); + + if (!dso) { + map__put(map); + return -1; + } + + size = dso__data_read_addr(dso, map, ui->machine, + addr, + (u8 *) data_word, + ui->unw_word_t_size); + map__put(map); + return !((size_t)size == ui->unw_word_t_size); +} + +int __libunwind__access_mem(void *as __maybe_unused, uint64_t addr, void *valp_word, + int __write, void *arg) +{ + struct unwind_info *ui = arg; + struct stack_dump *stack = &ui->sample->user_stack; + u64 start, end; + int offset; + int ret; + + /* Don't support write, probably not needed. */ + if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) { + uint64_t zero = 0; + + memcpy(valp_word, &zero, ui->unw_word_t_size); + return 0; + } + + ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample), + perf_arch_reg_sp(ui->e_machine)); + if (ret) + return ret; + + end = start + stack->size; + + /* Check overflow. */ + if (addr + ui->unw_word_t_size < addr) + return -EINVAL; + + if (addr < start || addr + ui->unw_word_t_size >= end) { + ret = access_dso_mem(ui, addr, valp_word); + if (ret) { + pr_debug3("unwind: access_mem %p not inside range" + " 0x%" PRIx64 "-0x%" PRIx64 "\n", + (void *) (uintptr_t) addr, start, end); + memset(valp_word, 0, ui->unw_word_t_size); + return ret; + } + return 0; + } + + offset = addr - start; + memcpy(valp_word, &stack->data[offset], ui->unw_word_t_size); + pr_debug3("unwind: access_mem addr %p val %lx, offset %d\n", + (void *) (uintptr_t) addr, *((unsigned long *)valp_word), offset); + return 0; +} + +int __libunwind__access_reg(void *as __maybe_unused, int regnum, void *valp_word, int __write, + void *arg) +{ + struct unwind_info *ui = arg; + int id, ret; + u64 val; + + /* Don't support write, I suspect we don't need it. */ + if (__write) { + pr_err("unwind: access_reg w %d\n", regnum); + return 0; + } + + if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { + memset(valp_word, 0, ui->unw_word_t_size); + return 0; + } + + id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); + if (id < 0) + return -EINVAL; + + ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); + if (ret) { + if (!ui->best_effort) + pr_err("unwind: can't read reg %d\n", regnum); + return ret; + } + + if (ui->unw_word_t_size == 8) + *(uint64_t *)valp_word = val; + else + *(uint32_t *)valp_word = (uint32_t)val; + pr_debug3("unwind: reg %d, val %lx\n", regnum, val); + return 0; +} int unwind__prepare_access(struct maps *maps, uint16_t e_machine) { - struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops; + void *addr_space; if (!dwarf_callchain_users) return 0; @@ -30,30 +567,16 @@ int unwind__prepare_access(struct maps *maps, uint16_t e_machine) if (e_machine == EM_NONE) return 0; - if (e_machine != EM_HOST) { - /* If not live/local mode. */ - switch (e_machine) { - case EM_386: - ops = x86_32_unwind_libunwind_ops; - break; - case EM_AARCH64: - ops = arm64_unwind_libunwind_ops; - break; - default: - pr_warning_once("unwind: ELF machine type %d is not supported\n", - e_machine); - return 0; - } - } - - if (!ops) { - pr_warning_once("unwind: target platform is not supported\n"); - return 0; - } - maps__set_unwind_libunwind_ops(maps, ops); maps__set_e_machine(maps, e_machine); + addr_space = libunwind_arch__create_addr_space(e_machine); - return ops->prepare_access(maps); + maps__set_addr_space(maps, addr_space); + if (!addr_space) { + pr_err("unwind: Can't create unwind address space.\n"); + return -ENOMEM; + } + + return 0; } void unwind__flush_access(struct maps *maps) @@ -66,14 +589,95 @@ void unwind__finish_access(struct maps *maps) libunwind_arch__finish_access(maps); } +static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void *arg) +{ + struct unwind_entry e; + struct addr_location al; + int ret; + + addr_location__init(&al); + e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); + e.ip = ip; + e.ms.map = al.map; + e.ms.thread = thread__get(al.thread); + + pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", + al.sym ? al.sym->name : "''", + ip, + al.map ? map__map_ip(al.map, ip) : (u64) 0); + + ret = cb(&e, arg); + addr_location__exit(&al); + return ret; +} + int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, - struct perf_sample *data, int max_stack, + struct perf_sample *sample, int max_stack, bool best_effort) { - const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread)); + struct unwind_info *ui; + uint64_t first_ip; + int ret, i = 0, entries = 0; + uint16_t e_machine; - if (ops) - return ops->get_entries(cb, arg, thread, data, max_stack, best_effort); - return 0; + if (!sample->user_regs || !sample->user_regs->regs) + return 0; + + if (max_stack <= 0) + return 0; + + if (!thread) { + pr_warning_once("WARNING: thread is NULL"); + return 0; + } + + e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL); + ret = perf_reg_value(&first_ip, perf_sample__user_regs(sample), + perf_arch_reg_ip(e_machine)); + if (ret) + return 0; + + if (max_stack == 1) { + /* Special case for a single entry. */ + ret = entry(first_ip, thread, cb, arg); + return ret ? (ret == -ENOMEM ? -ENOMEM : 0) : 1; + } + + ui = libunwind_arch_unwind_info__new(thread, sample, max_stack, best_effort, e_machine, first_ip); + if (!ui) + return -ENOMEM; + + do { + ret = libunwind_arch__unwind_step(ui); + if (ret < 0) + goto out; + + } while (ret); + + /* + * Display what we got based on the order setup. + */ + for (i = 0; i < ui->cur_ip; i++) { + int j = callchain_param.order == ORDER_CALLEE ? i : ui->cur_ip - i - 1; + + if (ui->ips[j]) { + ret = entry(ui->ips[j], thread, cb, arg); + if (ret) + break; + entries++; + } + } +out: + libunwind_arch_unwind_info__delete(ui); + /* + * Unwinder return contract: + * > 0 : unwinding succeeded (stops fallback). + * 0 : unwinding failed without yielding frames. Ignore non-fatal errors + * (e.g. stepping failure) to allow fallback unwinder or kernel callchains. + * < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely. + */ + if (ret == -ENOMEM) + return -ENOMEM; + return (entries > 0 || ret == 0) ? entries : 0; } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index 6a0f117898b2..a2d4af694fde 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -19,13 +19,6 @@ struct unwind_entry { typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -struct unwind_libunwind_ops { - int (*prepare_access)(struct maps *maps); - int (*get_entries)(unwind_entry_cb_t cb, void *arg, - struct thread *thread, - struct perf_sample *data, int max_stack, bool best_effort); -}; - int unwind__configure(const char *var, const char *value, void *cb); int unwind__option(const struct option *opt, const char *arg, int unset); From 834c557167a9236d8d8fe858b7b2d71ce6df7545 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 May 2026 16:31:51 -0700 Subject: [PATCH 1017/5214] perf unwind-libunwind: Add RISC-V libunwind support Add a RISC-V implementation for unwinding. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andrew Jones Cc: Athira Rajeev Cc: Dapeng Mi Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Florian Fainelli Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Li Guan Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Shimin Guo Cc: Thomas Richter Cc: Tomas Glozar Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/libunwind-arch/Build | 1 + .../perf/util/libunwind-arch/libunwind-arch.c | 21 ++ .../perf/util/libunwind-arch/libunwind-arch.h | 22 ++ .../util/libunwind-arch/libunwind-riscv.c | 297 ++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build index 87fd657a3248..80d3571918b1 100644 --- a/tools/perf/util/libunwind-arch/Build +++ b/tools/perf/util/libunwind-arch/Build @@ -5,6 +5,7 @@ perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o +perf-util-$(CONFIG_LIBUNWIND) += libunwind-riscv.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c index 8539b4233df4..9a74cf3c8729 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -20,6 +20,8 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum) return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum); case EM_PPC64: return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum); + case EM_RISCV: + return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum); case EM_S390: return __get_perf_regnum_for_unw_regnum_s390(unw_regnum); case EM_386: @@ -58,6 +60,9 @@ void libunwind_arch__flush_access(struct maps *maps) case EM_PPC64: __libunwind_arch__flush_access_ppc64(maps); break; + case EM_RISCV: + __libunwind_arch__flush_access_riscv(maps); + break; case EM_S390: __libunwind_arch__flush_access_s390(maps); break; @@ -98,6 +103,9 @@ void libunwind_arch__finish_access(struct maps *maps) case EM_PPC64: __libunwind_arch__finish_access_ppc64(maps); break; + case EM_RISCV: + __libunwind_arch__finish_access_riscv(maps); + break; case EM_S390: __libunwind_arch__finish_access_s390(maps); break; @@ -128,6 +136,8 @@ void *libunwind_arch__create_addr_space(unsigned int e_machine) return __libunwind_arch__create_addr_space_ppc32(); case EM_PPC64: return __libunwind_arch__create_addr_space_ppc64(); + case EM_RISCV: + return __libunwind_arch__create_addr_space_riscv(); case EM_S390: return __libunwind_arch__create_addr_space_s390(); case EM_386: @@ -167,6 +177,9 @@ int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, case EM_PPC64: return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi, need_unwind_info, arg); + case EM_RISCV: + return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi, + need_unwind_info, arg); case EM_S390: return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi, need_unwind_info, arg); @@ -211,6 +224,9 @@ int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, case EM_PPC64: return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase, obj_name, start, end); + case EM_RISCV: + return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, ip, segbase, + obj_name, start, end); case EM_S390: return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase, obj_name, start, end); @@ -250,6 +266,9 @@ struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, case EM_PPC64: return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack, best_effort, first_ip); + case EM_RISCV: + return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack, + best_effort, first_ip); case EM_S390: return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack, best_effort, first_ip); @@ -285,6 +304,8 @@ int libunwind_arch__unwind_step(struct unwind_info *ui) return __libunwind_arch__unwind_step_ppc32(ui); case EM_PPC64: return __libunwind_arch__unwind_step_ppc64(ui); + case EM_RISCV: + return __libunwind_arch__unwind_step_riscv(ui); case EM_S390: return __libunwind_arch__unwind_step_s390(ui); case EM_386: diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h index 2bf7fc33313b..74a09cd58f38 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -39,6 +39,7 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum); int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum); int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum); int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum); +int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum); int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum); int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum); int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum); @@ -50,6 +51,7 @@ void __libunwind_arch__flush_access_loongarch(struct maps *maps); void __libunwind_arch__flush_access_mips(struct maps *maps); void __libunwind_arch__flush_access_ppc32(struct maps *maps); void __libunwind_arch__flush_access_ppc64(struct maps *maps); +void __libunwind_arch__flush_access_riscv(struct maps *maps); void __libunwind_arch__flush_access_s390(struct maps *maps); void __libunwind_arch__flush_access_i386(struct maps *maps); void __libunwind_arch__flush_access_x86_64(struct maps *maps); @@ -61,6 +63,7 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps); void __libunwind_arch__finish_access_mips(struct maps *maps); void __libunwind_arch__finish_access_ppc32(struct maps *maps); void __libunwind_arch__finish_access_ppc64(struct maps *maps); +void __libunwind_arch__finish_access_riscv(struct maps *maps); void __libunwind_arch__finish_access_s390(struct maps *maps); void __libunwind_arch__finish_access_i386(struct maps *maps); void __libunwind_arch__finish_access_x86_64(struct maps *maps); @@ -72,6 +75,7 @@ void *__libunwind_arch__create_addr_space_loongarch(void); void *__libunwind_arch__create_addr_space_mips(void); void *__libunwind_arch__create_addr_space_ppc32(void); void *__libunwind_arch__create_addr_space_ppc64(void); +void *__libunwind_arch__create_addr_space_riscv(void); void *__libunwind_arch__create_addr_space_s390(void); void *__libunwind_arch__create_addr_space_i386(void); void *__libunwind_arch__create_addr_space_x86_64(void); @@ -111,6 +115,11 @@ int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg); +int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip, struct libarch_unwind__dyn_info *di, void *pi, @@ -176,6 +185,13 @@ int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found, const char *obj_name, uint64_t start, uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_riscv(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); int __libunwind_arch__dwarf_find_debug_frame_s390(int found, struct libarch_unwind__dyn_info *di_debug, uint64_t ip, @@ -236,6 +252,11 @@ struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *threa int max_stack, bool best_effort, uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread, struct perf_sample *sample, int max_stack, @@ -266,6 +287,7 @@ int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui); int __libunwind_arch__unwind_step_mips(struct unwind_info *ui); int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui); int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui); int __libunwind_arch__unwind_step_s390(struct unwind_info *ui); int __libunwind_arch__unwind_step_i386(struct unwind_info *ui); int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui); diff --git a/tools/perf/util/libunwind-arch/libunwind-riscv.c b/tools/perf/util/libunwind-arch/libunwind-riscv.c new file mode 100644 index 000000000000..3220690cd7d1 --- /dev/null +++ b/tools/perf/util/libunwind-arch/libunwind-riscv.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "libunwind-arch.h" +#include "../debug.h" +#include "../maps.h" +#include "../thread.h" +#include "../../../arch/riscv/include/uapi/asm/perf_regs.h" +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +#include +#endif + +int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum __maybe_unused) +{ +#ifndef HAVE_LIBUNWIND_RISCV_SUPPORT + return -EINVAL; +#else + switch (unw_regnum) { + case UNW_RISCV_X1 ... UNW_RISCV_X31: + return unw_regnum - UNW_RISCV_X1 + PERF_REG_RISCV_RA; + case UNW_RISCV_PC: + return PERF_REG_RISCV_PC; + default: + pr_err("unwind: invalid reg id %d\n", unw_regnum); + return -EINVAL; + } +#endif // HAVE_LIBUNWIND_RISCV_SUPPORT +} + +void __libunwind_arch__flush_access_riscv(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_flush_cache(maps__addr_space(maps), 0, 0); +#endif +} + +void __libunwind_arch__finish_access_riscv(struct maps *maps __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_destroy_addr_space(maps__addr_space(maps)); +#endif +} + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_riscv(void) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as __maybe_unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.rti.segbase; + _di->table_data = di.u.rti.table_data; + _di->table_len = di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_riscv(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV) + unw_dyn_info_t di = { + .format = UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip = _di->start_ip, + .end_ip = _di->end_ip, + .u = { + .rti = { + .segbase = _di->segbase, + .table_data = _di->table_data, + .table_len = _di->table_len, + }, + }, + }; + int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end); + + _di->start_ip = di.start_ip; + _di->end_ip = di.end_ip; + _di->segbase = di.u.ti.segbase; + _di->table_data = di.u.ti.table_data; + _di->table_len = di.u.ti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps = thread__maps(thread); + void *addr_space = maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space == NULL) + return NULL; + + ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine = maps__machine(maps); + ui->ui.thread = thread; + ui->ui.sample = sample; + ui->ui.cursor = &ui->_cursor; + ui->ui.ips = &ui->_ips[0]; + ui->ui.ips[0] = first_ip; + ui->ui.cur_ip = 1; + ui->ui.max_ips = max_stack; + ui->ui.unw_word_t_size = sizeof(unw_word_t); + ui->ui.e_machine = EM_RISCV; + ui->ui.best_effort = best_effort; + + ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT + int ret; + + if (ui->cur_ip >= ui->max_ips) + return 0; + + ret = unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <= 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] = ip; + } + return ret; +#else + return -EINVAL; +#endif +} From 7ee7f48413c42b90230de4a8e40898b757bc8e82 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 13 May 2026 12:23:46 -0700 Subject: [PATCH 1018/5214] perf trace beauty fcntl: Fix build with older kernel headers Toolchains with older kernel headers that do not include upstream commit c75b1d9421f80f41 ("fs: add fcntl() interface for setting/getting write life time hints") will now fail to build perf due to missing definitions for F_GET_RW_HINT/F_SET_RW_HINT/F_GET_FILE_RW_HINT/F_SET_FILE_RW_HINT. Provide a fallback definition for these when they are not already defined. Fixes: 9c47f66748381ecb ("perf trace beauty fcntl: Basic 'arg' beautifier") Reviewed-by: Ian Rogers Signed-off-by: Florian Fainelli Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Markus Mayer Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/fcntl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/trace/beauty/fcntl.c b/tools/perf/trace/beauty/fcntl.c index d075904dccce..e1b99b8f55eb 100644 --- a/tools/perf/trace/beauty/fcntl.c +++ b/tools/perf/trace/beauty/fcntl.c @@ -9,6 +9,22 @@ #include #include +#ifndef F_GET_RW_HINT +#define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11) +#endif + +#ifndef F_SET_RW_HINT +#define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12) +#endif + +#ifndef F_GET_FILE_RW_HINT +#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13) +#endif + +#ifndef F_SET_FILE_RW_HINT +#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14) +#endif + static size_t fcntl__scnprintf_getfd(unsigned long val, char *bf, size_t size, bool show_prefix) { return val ? scnprintf(bf, size, "%s", "0") : From 2e2ba7d1ea554ee6e9e751a53eebf3e9270b0670 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 10 Apr 2026 12:13:44 +0100 Subject: [PATCH 1019/5214] perf pmu: Skip test on Arm64 when #slots is zero Some Arm64 PMUs expose 'caps/slots' as 0 when the slot count is not implemented, tool_pmu__read_event() currently returns false for this, so metrics that reference #slots are reported as syntax error. Since the commit 3a61fd866ef9 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()"), these syntax errors are populated as failures and make the PMU metric test fail: 9.3: Parsing of PMU event table metrics: --- start --- ... Found metric 'backend_bound' metric expr 100 * (stall_slot_backend / (#slots * cpu_cycles)) for backend_bound parsing metric: 100 * (stall_slot_backend / (#slots * cpu_cycles)) Failure to read '#slots' literal: #slots = nan syntax error Fail to parse metric or group `backend_bound' ... ---- end(-1) ---- 9.3: Parsing of PMU event table metrics : FAILED! This commit introduces a new function is_expected_broken_metric() to identify broken metrics, and treats metrics containing "#slots" as expected broken when #slots == 0 on Arm64 platforms. Fixes: 3a61fd866ef9aaa1 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()") Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu-events.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index a99716862168..b1609a7e1d8c 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -15,6 +15,7 @@ #include "util/expr.h" #include "util/hashmap.h" #include "util/parse-events.h" +#include "util/tool_pmu.h" #include "metricgroup.h" #include "stat.h" @@ -817,6 +818,26 @@ struct metric { struct metric_ref metric_ref; }; +static bool is_expected_broken_metric(const struct pmu_metric *pm) +{ + if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || + !strcmp(pm->metric_name, "M3")) + return true; + +#if defined(__aarch64__) + /* + * Arm64 platforms may return "#slots == 0", which is treated as a + * syntax error by the parser. Don't test these metrics when running + * on such platforms. + */ + if (strstr(pm->metric_expr, "#slots") && + !tool_pmu__cpu_slots_per_cycle()) + return true; +#endif + + return false; +} + static int test__parsing_callback(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data) @@ -852,8 +873,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, err = metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { - if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || - !strcmp(pm->metric_name, "M3")) { + if (is_expected_broken_metric(pm)) { (*failures)--; pr_debug("Expected broken metric %s skipping\n", pm->metric_name); err = 0; From 1172160f2a2de7bade3bec64b8c5ecf945cde5ed Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 5 May 2026 18:34:55 +0100 Subject: [PATCH 1020/5214] iio: pressure: bmp280: zero-init bmp580 trigger handler buffer bmp580_trigger_handler() builds an on-stack scan buffer containing two __le32 fields and an aligned_s64 timestamp, and pushes it to userspace via iio_push_to_buffers_with_ts(). However, only the low 3 bytes of each __le32 field are populated by the device data: memcpy(&buffer.comp_press, &data->buf[3], 3); memcpy(&buffer.comp_temp, &data->buf[0], 3); The high byte of each field is left uninitialised on the stack. The bmp580 channels declare storagebits = 32, so the IIO core transports all four bytes per sample to userspace as part of the scan element, leaking two bytes of kernel stack per scan. Zero-initialise the buffer before populating it, mirroring the fix applied to bme280_trigger_handler() in commit 018f50909e66 ("iio: bmp280: zero-init buffer"). Fixes: 872c8014e05e ("iio: pressure: bmp280: drop sensor_data array") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 9b489766e457..0ebe0b682caa 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1910,7 +1910,7 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) u32 comp_press; s32 comp_temp; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); From 8320c77e67382d5d55d77043a5f60a867d408a2b Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 10 May 2026 07:35:00 +0500 Subject: [PATCH 1021/5214] iio: gyro: bmg160: bail out when bandwidth/filter is not in table bmg160_get_filter() walks bmg160_samp_freq_table[] looking for the entry matching the bw_bits value read from the chip: for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { if (bmg160_samp_freq_table[i].bw_bits == bw_bits) break; } *val = bmg160_samp_freq_table[i].filter; If no entry matches, i ends up equal to the array size and the next line reads one slot past the end. bmg160_set_filter() has the same shape, driven by 'val' instead of bw_bits. smatch flags both: drivers/iio/gyro/bmg160_core.c:204 bmg160_get_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 drivers/iio/gyro/bmg160_core.c:222 bmg160_set_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 Return -EINVAL when no entry matches. The set_filter() path is reachable from userspace via the sysfs in_anglvel_filter_low_pass_3db_frequency interface, so userspace can trivially trigger the out-of-bounds read with a value that is not in bmg160_samp_freq_table[].filter. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/bmg160_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 38394b5f3275..58963f3ea186 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -201,6 +201,9 @@ static int bmg160_get_filter(struct bmg160_data *data, int *val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + *val = bmg160_samp_freq_table[i].filter; return ret ? ret : IIO_VAL_INT; @@ -218,6 +221,9 @@ static int bmg160_set_filter(struct bmg160_data *data, int val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bmg160_samp_freq_table[i].bw_bits); if (ret < 0) { From 088fcb9b567f8723074ad9eb1bf5cb46f8a0096b Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 11:40:20 +0500 Subject: [PATCH 1022/5214] iio: gyro: bmg160: wait full startup time after mode change at probe bmg160_chip_init() calls bmg160_set_mode(BMG160_MODE_NORMAL) and then waits only 500-1000 us. Per the BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013), the start-up and wake-up times (tsu, twusm) are 30 ms. The same file already waits BMG160_MAX_STARTUP_TIME_MS (80 ms) in bmg160_runtime_resume() after the same set_mode(NORMAL) operation. The 500 us value at probe was likely a unit mix-up; the old comment said "500 ms" while the code used microseconds. Reuse the same constant via msleep() and add a code comment explaining the datasheet basis for the wait. Without this, register writes that follow the mode change can hit the chip before it is ready. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/bmg160_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 58963f3ea186..d611341a0e2a 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -264,8 +264,14 @@ static int bmg160_chip_init(struct bmg160_data *data) if (ret < 0) return ret; - /* Wait upto 500 ms to be ready after changing mode */ - usleep_range(500, 1000); + /* + * Wait for the chip to be ready after switching to normal mode. + * The BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013) + * specifies a start-up / wake-up time (tsu, twusm) of 30 ms; use + * BMG160_MAX_STARTUP_TIME_MS (80 ms) as a safety margin, matching + * what bmg160_runtime_resume() already does. + */ + msleep(BMG160_MAX_STARTUP_TIME_MS); /* Set Bandwidth */ ret = bmg160_set_bw(data, BMG160_DEF_BW); From be843b0579f872ec7590d825e2c9a656d4790c4b Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:10 +0500 Subject: [PATCH 1023/5214] iio: proximity: vl53l0x: notify trigger and clear IRQ on error paths vl53l0x_trigger_handler() returns directly on the I2C read failure paths without calling iio_trigger_notify_done() or vl53l0x_clear_irq(). A single transient i2c_smbus_read_i2c_block_data() failure (negative errno or a short read) therefore leaves two pieces of state behind: - iio_trigger_notify_done() never decrements the trigger's use_count, so iio_trigger_poll_nested() silently drops further dispatches (see industrialio-trigger.c, the !atomic_read(&trig->use_count) guard); - vl53l0x_clear_irq() never writes SYSTEM_INTERRUPT_CLEAR, so the chip keeps the DRDY interrupt asserted. The sensor's buffer mode stays wedged from then on, recoverable only by re-binding the driver. The sibling driver vl53l1x-i2c.c handles exactly the same case correctly by jumping to a "notify_and_clear_irq" label that always calls both helpers; mirror that here. The bogus negative-int return value cast to irqreturn_t also goes away as a side effect. Fixes: 762186c6e7b1 ("iio: proximity: vl53l0x-i2c: Added continuous mode support") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/vl53l0x-i2c.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index ad3e46d47fa8..6c6e6dab045f 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -87,15 +87,14 @@ static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) ret = i2c_smbus_read_i2c_block_data(data->client, VL_REG_RESULT_RANGE_STATUS, sizeof(buffer), buffer); - if (ret < 0) - return ret; - else if (ret != 12) - return -EREMOTEIO; + if (ret != 12) + goto done; scan.chan = get_unaligned_be16(&buffer[10]); iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), iio_get_time_ns(indio_dev)); +done: iio_trigger_notify_done(indio_dev->trig); vl53l0x_clear_irq(data); From a4b25906fc0130bc9cec45472ed03301d9e1da22 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:26 +0200 Subject: [PATCH 1024/5214] clk: at91: sam9x7: Remove gmac peripheral clock with ID 67 According with datasheet (see link section) table 12.1 the instance ID 67 is reserved. This change drops the gmactsu_clk entry from the SAM9X7 clock description table to align with the datasheet. Link: https://ww1.microchip.com/downloads/aemDocuments/documents/MPU32/ProductDocuments/DataSheets/SAM9X7-Series-Data-Sheet-DS60001813.pdf Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-2-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea --- drivers/clk/at91/sam9x7.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 89868a0aeaba..66aadebc51a4 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -420,7 +420,6 @@ static const struct { { .n = "lvdsc_clk", .id = 56, }, { .n = "pit64b1_clk", .id = 58, }, { .n = "puf_clk", .id = 59, }, - { .n = "gmactsu_clk", .id = 67, }, }; /* From 24881a7ec7f63cd238c39effb5805c39737a1872 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:27 +0200 Subject: [PATCH 1025/5214] clk: at91: sam9x7: Rename macb0_clk to gmac_clk Update the peripheral clock name for ID 24 from macb0_clk to gmac_clk to match the actual GMAC hardware block present on SAM9X7 SoCs and the datasheet description. Signed-off-by: Mihai Sain Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20260309075329.1528-3-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea --- drivers/clk/at91/sam9x7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 66aadebc51a4..32c082b4ca4b 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -387,7 +387,7 @@ static const struct { { .n = "dma0_clk", .id = 20, }, { .n = "uhphs_clk", .id = 22, }, { .n = "udphs_clk", .id = 23, }, - { .n = "macb0_clk", .id = 24, }, + { .n = "gmac_clk", .id = 24, }, { .n = "lcd_clk", .id = 25, }, { .n = "sdmmc1_clk", .id = 26, }, { .n = "ssc_clk", .id = 28, }, From b6f6ebb0fb57ae6da622fb8fd4ebdc9ba1ae5756 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:28 +0200 Subject: [PATCH 1026/5214] clk: at91: sam9x7: Fix gmac_gclk clock definition According to the datasheet (see link section), table 12.1, instance ID 24 is used for the GMAC generic clock, while instance ID 67 is reserved. Add the correct gmac_gclk entry at ID 24, aligned with the SoC clock layout, and remove the old misplaced entry at ID 67. Link: https://ww1.microchip.com/downloads/aemDocuments/documents/MPU32/ProductDocuments/DataSheets/SAM9X75-SIP-Series-Data-Sheet-DS60001827.pdf Fixes: 33013b43e271 ("clk: at91: sam9x7: add sam9x7 pmc driver") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-4-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea --- drivers/clk/at91/sam9x7.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 89868a0aeaba..6b330c3e6bca 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -569,6 +569,15 @@ static const struct { .pp_chg_id = INT_MIN, }, + { + .n = "gmac_gclk", + .id = 24, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + { .n = "lcd_gclk", .id = 25, @@ -702,15 +711,6 @@ static const struct { .pp_count = 1, .pp_chg_id = INT_MIN, }, - - { - .n = "gmac_gclk", - .id = 67, - .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, - .pp_mux_table = { 6, 8, }, - .pp_count = 2, - .pp_chg_id = INT_MIN, - }, }; static void __init sam9x7_pmc_setup(struct device_node *np) From e445b78ffb8d43440b9f417d6701826746114a0d Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 24 Apr 2026 17:29:33 -0400 Subject: [PATCH 1027/5214] perf trace: Introduce --show-cpu option to display cpu id When tracing system-wide workloads or specific events, it is highly valuable to know exactly which CPU executed a specific event. Currently, perf trace output defaults to omitting CPU information. Introduce a new "--show-cpu" command-line option. When provided, this flag extracts the CPU from the perf sample and prints it in a "[000]" format immediately following the timestamp. This mirrors the behaviour of other tracing tools like ftrace and perf script. For example: # perf trace -e sched:sched_switch --max-events 5 --show-cpu 0.000 [002] :0/0 sched:sched_switch(prev_comm: "swapper/2", prev_prio: 120, next_comm: "rcu_preempt", next_pid: 16 (rcu_preempt), next_prio: 120) 0.009 [002] rcu_preempt/16 sched:sched_switch(prev_comm: "rcu_preempt", prev_pid: 16 (rcu_preempt), prev_prio: 120, prev_state: 128, next_comm: "swapper/2", next_prio: 120) 0.033 [002] :0/0 sched:sched_switch(prev_comm: "swapper/2", prev_prio: 120, next_comm: "kworker/u32:48", next_pid: 35840 (kworker/u32:48-), next_prio: 120) 0.041 [002] kworker/u32:48/35840 sched:sched_switch(prev_comm: "kworker/u32:48", prev_pid: 35840 (kworker/u32:48-), prev_prio: 120, prev_state: 128, next_comm: "swapper/2", next_prio: 120) 0.045 [002] :0/0 sched:sched_switch(prev_comm: "swapper/2", prev_prio: 120, next_comm: "kworker/u32:48", next_pid: 35840 (kworker/u32:48-), next_prio: 120) The feature is implemented strictly as an opt-in toggle to prevent cluttering the standard output and to preserve backwards compatibility for scripts parsing the default output format. Signed-off-by: Aaron Tomlin Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Daniel Vacek Cc: Howard Chu Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sean Ashe Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-trace.txt | 3 ++ tools/perf/builtin-trace.c | 51 ++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 892c82a9bf40..d0b6c771a1b9 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -199,6 +199,9 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --show-on-off-events:: Show the --switch-on/off events too. +--show-cpu:: + Show cpu id. + --max-stack:: Set the stack depth limit when parsing the callchain, anything beyond the specified depth will be ignored. Note that at this point diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e58c49d047a2..e9cc870a5acd 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -217,6 +217,7 @@ struct trace { bool kernel_syscallchains; s16 args_alignment; bool show_tstamp; + bool show_cpu; bool show_duration; bool show_zeros; bool show_arg_names; @@ -1531,6 +1532,7 @@ static size_t fprintf_duration(unsigned long t, bool calculated, FILE *fp) */ struct thread_trace { u64 entry_time; + u32 entry_cpu; bool entry_pending; unsigned long nr_events; unsigned long pfmaj, pfmin; @@ -1893,6 +1895,27 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) return fprintf(fp, " ? "); } +/** + * trace__fprintf_cpu - Print the CPU ID to a given file stream + * @cpu: The CPU ID to print + * @fp: The file stream to write to + * + * Formats and prints the specified CPU ID enclosed in brackets + * (e.g., "[003] ") to the provided file pointer. It is used to + * align and display the CPU ID consistently within the trace output. + * + * Return: The number of characters printed. + */ +static size_t trace__fprintf_cpu(u32 cpu, FILE *fp) +{ + size_t printed = 0; + + if (cpu != (u32)-1) + printed += fprintf(fp, "[%03u] ", cpu); + + return printed; +} + static pid_t workload_pid = -1; static volatile sig_atomic_t done = false; static volatile sig_atomic_t interrupted = false; @@ -1923,12 +1946,15 @@ static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread } static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, - u64 duration, bool duration_calculated, u64 tstamp, FILE *fp) + u64 duration, bool duration_calculated, + u64 tstamp, u32 cpu, FILE *fp) { size_t printed = 0; if (trace->show_tstamp) printed = trace__fprintf_tstamp(trace, tstamp, fp); + if (trace->show_cpu && cpu != (u32)-1) + printed += trace__fprintf_cpu(cpu, fp); if (trace->show_duration) printed += fprintf_duration(duration, duration_calculated, fp); return printed + trace__fprintf_comm_tid(trace, thread, fp); @@ -2707,7 +2733,9 @@ static int trace__printf_interrupted_entry(struct trace *trace) if (!ttrace->entry_pending) return 0; - printed = trace__fprintf_entry_head(trace, trace->current, 0, false, ttrace->entry_time, trace->output); + printed = trace__fprintf_entry_head(trace, trace->current, 0, false, + ttrace->entry_time, ttrace->entry_cpu, + trace->output); printed += len = fprintf(trace->output, "%s)", ttrace->entry_str); if (len < trace->args_alignment - 4) @@ -2825,6 +2853,7 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel, if (evsel != trace->syscalls.events.sys_enter) augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); ttrace->entry_time = sample->time; + ttrace->entry_cpu = sample->cpu; msg = ttrace->entry_str; printed += scnprintf(msg + printed, trace__entry_str_size - printed, "%s(", sc->name); @@ -2835,7 +2864,9 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel, if (!(trace->duration_filter || trace->summary_only || trace->failure_only || trace->min_stack)) { int alignment = 0; - trace__fprintf_entry_head(trace, thread, 0, false, ttrace->entry_time, trace->output); + trace__fprintf_entry_head(trace, thread, 0, false, + ttrace->entry_time, + sample->cpu, trace->output); printed = fprintf(trace->output, "%s)", ttrace->entry_str); if (trace->args_alignment > printed) alignment = trace->args_alignment - printed; @@ -2980,7 +3011,9 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, if (trace->summary_only || (ret >= 0 && trace->failure_only)) goto out; - trace__fprintf_entry_head(trace, thread, duration, duration_calculated, ttrace->entry_time, trace->output); + trace__fprintf_entry_head(trace, thread, duration, + duration_calculated, ttrace->entry_time, + sample->cpu, trace->output); if (ttrace->entry_pending) { printed = fprintf(trace->output, "%s", ttrace->entry_str); @@ -3280,6 +3313,9 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, trace__printf_interrupted_entry(trace); trace__fprintf_tstamp(trace, sample->time, trace->output); + if (trace->show_cpu) + trace__fprintf_cpu(sample->cpu, trace->output); + if (trace->trace_syscalls && trace->show_duration) fprintf(trace->output, "( ): "); @@ -3405,7 +3441,8 @@ static int trace__pgfault(struct trace *trace, thread__find_symbol(thread, sample->cpumode, sample->ip, &al); - trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output); + trace__fprintf_entry_head(trace, thread, 0, true, sample->time, + sample->cpu, trace->output); fprintf(trace->output, "%sfault [", evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? @@ -5432,6 +5469,7 @@ int cmd_trace(int argc, const char **argv) OPT_CALLBACK('m', "mmap-pages", &trace.opts.mmap_pages, "pages", "number of mmap data pages", evlist__parse_mmap_pages), OPT_STRING('u', "uid", &trace.uid_str, "user", "user to profile"), + OPT_BOOLEAN(0, "show-cpu", &trace.show_cpu, "show cpu id"), OPT_CALLBACK(0, "duration", &trace, "float", "show only events with duration > N.M ms", trace__set_duration), @@ -5566,6 +5604,9 @@ int cmd_trace(int argc, const char **argv) goto out; } + if (trace.show_cpu) + trace.opts.sample_cpu = true; + if ((nr_cgroups || trace.cgroup) && !trace.opts.target.system_wide) { usage_with_options_msg(trace_usage, trace_options, "cgroup monitoring only available in system-wide mode"); From 70247658d0e783c93b48fcbc3b81d99e992ff478 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 15 May 2026 18:31:38 +0500 Subject: [PATCH 1028/5214] iio: resolver: ad2s1210: notify trigger and clear state on fault read error ad2s1210_trigger_handler() walks several scan-mask branches and uses "goto error_ret" to land on the iio_trigger_notify_done() teardown at the bottom of the function for every I/O error -- except the MOD_CONFIG fault-register read, which uses a bare "return ret": if (st->fixed_mode == MOD_CONFIG) { unsigned int reg_val; ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) return ret; ... } Two problems on that path: - the handler returns a negative errno where the prototype expects an irqreturn_t (IRQ_HANDLED / IRQ_NONE), so the caller in the IIO core sees a value outside the enum; - iio_trigger_notify_done() is skipped, leaving the trigger busy-flag set. A single transient SPI/regmap error on the fault read then wedges the trigger so subsequent samples are dropped until the consumer is detached. Convert the error path to "goto error_ret" so the failure path goes through the same notify_done() teardown as every other error in the handler. Fixes: f9b9ff95be8c ("iio: resolver: ad2s1210: add support for adi,fixed-mode") Signed-off-by: Stepan Ionichev Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/resolver/ad2s1210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 774728c804c0..1be19fe8aa3f 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1334,7 +1334,7 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - return ret; + goto error_ret; st->sample.fault = reg_val; } From ff29241030eb6f4505d903d87c29f51c1866a95d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:28:26 +0200 Subject: [PATCH 1029/5214] iio: light: acpi-als: Check ACPI_COMPANION() against NULL Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the acpi-als IIO driver. Fixes: d4243cb08a27 ("iio: light: acpi-als: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index ab229318dce9..1983a7f17aa9 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -179,11 +179,15 @@ static irqreturn_t acpi_als_trigger_handler(int irq, void *p) static int acpi_als_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acpi_device *device = ACPI_COMPANION(dev); + struct acpi_device *device; struct iio_dev *indio_dev; struct acpi_als *als; int ret; + device = ACPI_COMPANION(dev); + if (!device) + return -ENODEV; + indio_dev = devm_iio_device_alloc(dev, sizeof(*als)); if (!indio_dev) return -ENOMEM; From c52bb33b641ebaae3e209f97714cb1758206f7d9 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 May 2026 14:01:11 +1300 Subject: [PATCH 1030/5214] iio: light: veml6030: fix channel type when pushing events The events are registered for IIO_LIGHT and not for IIO_INTENSITY. Use the correct channel type. When at it, fix minor checkpatch code style warning (alignment). Cc: stable@vger.kernel.org Fixes: 7b779f573c48 ("iio: light: add driver for veml6030 ambient light sensor") Signed-off-by: Javier Carrasco Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6030.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 6bcacae3863c..da8c32cabfd6 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -875,9 +875,11 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) else evtdir = IIO_EV_DIR_FALLING; - iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, - 0, IIO_EV_TYPE_THRESH, evtdir), - iio_get_time_ns(indio_dev)); + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + evtdir), + iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } From 25b8f50b0622689cd1f7233e452407ce777a479e Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Tue, 14 Apr 2026 17:34:28 +0000 Subject: [PATCH 1031/5214] clk: qcom: clk-rpmh: Make all VRMs optional Some VRMs aren't present on all boards, so mark them as optional. This prevents probe failures on boards where not all VRMs are present. This resolves an issue seen on the Nothing Phone (4a) Pro (Eliza) where probe fails due to RPMH_RF_CLK5 not being present on the board, this is due to this device having a slightly different PMIC configuration from the Eliza MTP. This matches the downstream approach of marking all VRMs as optional and makes the previous clka_optional handling redundant. Signed-off-by: Alexander Koskovich Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260414-clk-rpmh-vrm-opt-v3-1-8ca21469ffbc@pm.me Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-rpmh.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 8eae6fccc127..6367b2a5a4e0 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -66,8 +66,6 @@ struct clk_rpmh { struct clk_rpmh_desc { struct clk_hw **clks; size_t num_clks; - /* RPMh clock clkaN are optional for this platform */ - bool clka_optional; }; static DEFINE_MUTEX(rpmh_clk_lock); @@ -699,7 +697,6 @@ static struct clk_hw *sm8550_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8550 = { .clks = sm8550_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8550_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *sm8650_rpmh_clocks[] = { @@ -731,7 +728,6 @@ static struct clk_hw *sm8650_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8650 = { .clks = sm8650_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8650_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *sc7280_rpmh_clocks[] = { @@ -901,7 +897,6 @@ static struct clk_hw *sm8750_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8750 = { .clks = sm8750_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8750_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *glymur_rpmh_clocks[] = { @@ -1059,8 +1054,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) if (!res_addr) { hw_clks[i] = NULL; - if (desc->clka_optional && - !strncmp(rpmh_clk->res_name, "clka", sizeof("clka") - 1)) + if (rpmh_clk->res_addr == CLK_RPMH_VRM_EN_OFFSET) continue; dev_err(&pdev->dev, "missing RPMh resource address for %s\n", From 8a7fe10eec64bfb7cf4091bca540de4c55d56bfa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 14 May 2026 22:16:25 +0800 Subject: [PATCH 1032/5214] soundwire: intel_ace2x: release bpt_stream when close it The BPT stream was allocated in intel_ace2x_bpt_open_stream(), we need to free it in intel_ace2x_bpt_close_stream(). Fixes: 4c1ce9f37d8a8 ("soundwire: intel_ace2x: add BPT send_async/wait callbacks") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260514141625.1834216-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_ace2x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 0a97253fe0c2..b37933efac5d 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -317,6 +317,7 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave dev_err(cdns->dev, "%s: remove slave failed: %d\n", __func__, ret); + sdw_release_stream(cdns->bus.bpt_stream); cdns->bus.bpt_stream = NULL; } From d97d13c24d7893abcfb80d38630ce74daaa1434c Mon Sep 17 00:00:00 2001 From: Alessandro Baldi Date: Sun, 17 May 2026 11:14:15 +0200 Subject: [PATCH 1033/5214] media: imon: Add iMON VFD HID OEM v1.2 key mappings Add Vol+/Vol-/Mute panel button mappings for iMON VFD HID OEM v1.2. This version differs in the codes that generate the KEY_VOLUMEUP, KEY_VOLUMEDOWN and KEY_MUTE events. Signed-off-by: Alessandro Baldi Signed-off-by: Sean Young --- drivers/media/rc/imon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 9bb27ba8240f..049a73b5f882 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -290,6 +290,10 @@ static const struct imon_usb_dev_descr imon_OEM_VFD = { { 0x000100000000ffeell, KEY_VOLUMEUP }, { 0x010000000000ffeell, KEY_VOLUMEDOWN }, { 0x000000000100ffeell, KEY_MUTE }, + /* iMON VFD HID OEM v1.2 */ + { 0x000000000a00ffeell, KEY_VOLUMEUP }, + { 0x000000000b00ffeell, KEY_VOLUMEDOWN }, + { 0x000000000c00ffeell, KEY_MUTE }, /* 0xffdc iMON MCE VFD */ { 0x00010000ffffffeell, KEY_VOLUMEUP }, { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, From 2b9ef70d3abe1eaa9b37253fd7765cf40ff2a5ad Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 17 May 2026 21:15:30 +0500 Subject: [PATCH 1034/5214] pinctrl: intel: move PWM base computation past feature check Compute base inside intel_pinctrl_probe_pwm() only after the PINCTRL_FEATURE_PWM and CONFIG_PWM_LPSS checks have passed. Tidy up; no functional change. Suggested-by: Andy Shevchenko Link: https://lore.kernel.org/linux-gpio/aglu5jy5SbW9Wjwj@ashevche-desk.local/ Signed-off-by: Stepan Ionichev Acked-by: Mika Westerberg Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-intel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 97bf5ec78db4..2e2526e01d58 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -1556,13 +1556,13 @@ static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl, struct intel_community *community, unsigned short capability_offset) { - void __iomem *base = community->regs + capability_offset + 4; static const struct pwm_lpss_boardinfo info = { .clk_rate = 19200000, .npwm = 1, .base_unit_bits = 22, }; struct pwm_chip *chip; + void __iomem *base; if (!(community->features & PINCTRL_FEATURE_PWM)) return 0; @@ -1570,6 +1570,7 @@ static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl, if (!IS_REACHABLE(CONFIG_PWM_LPSS)) return 0; + base = community->regs + capability_offset + 4; chip = devm_pwm_lpss_probe(pctrl->dev, base, &info); return PTR_ERR_OR_ZERO(chip); } From ea2c2b9e2a66e2b4aa0455b2d70058e2f0ea4d23 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 15 May 2026 21:08:08 +0100 Subject: [PATCH 1035/5214] coresight: Fix source not disabled on idr_alloc_u32 failure In coresight_enable_sysfs(), for non-CPU sources (SOFTWARE, TPDM, OTHERS), the source device is enabled via coresight_enable_source_sysfs() before idr_alloc_u32() maps the path. If idr_alloc_u32() fails, the original code jumped directly to err_source, which only calls coresight_disable_path() and coresight_release_path(). The source device was left enabled with an incremented refcnt but no path tracked for it, leaving the device in an inconsistent state. Disable the source before jumping to err_source so the enable and path operations are fully unwound. Fixes: 5c0016d7b343 ("coresight: core: Use IDR for non-cpu bound sources' paths.") Signed-off-by: Jie Gan Reviewed-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-1-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index b6a870399e83..da6f22b512c9 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -244,8 +244,10 @@ int coresight_enable_sysfs(struct coresight_device *csdev) */ hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) + if (ret) { + coresight_disable_source_sysfs(csdev, NULL); goto err_source; + } break; default: /* We can't be here */ From 864754d0a084141085f154db044401fb2dce6a34 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:09 +0100 Subject: [PATCH 1036/5214] coresight: Handle helper enable failure properly If a helper fails to be enabled, unwind any helpers that were already enabled earlier in the loop. This avoids leaving partially enabled helpers behind. Fixes: 6148652807ba ("coresight: Enable and disable helper devices adjacent to the path") Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-2-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 2105bb813940..256f6a32621b 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -499,10 +499,19 @@ static int coresight_enable_helpers(struct coresight_device *csdev, ret = coresight_enable_helper(helper, mode, path); if (ret) - return ret; + goto err; } return 0; + +err: + while (i--) { + helper = csdev->pdata->out_conns[i]->dest_dev; + if (helper && coresight_is_helper(helper)) + coresight_disable_helper(helper, path); + } + + return ret; } int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) From 10be00dd737545a5ed20a7a6ff908a0bc42f80b7 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:10 +0100 Subject: [PATCH 1037/5214] coresight: Extract device init into coresight_init_device() This commit extracts the allocation and initialization of the coresight device structure into a separate function to make future extensions easier. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-3-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 27 ++++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 256f6a32621b..d5d18168b481 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1334,20 +1334,16 @@ void coresight_release_platform_data(struct device *dev, devm_kfree(dev, pdata); } -struct coresight_device *coresight_register(struct coresight_desc *desc) +static struct coresight_device * +coresight_init_device(struct coresight_desc *desc) { - int ret; struct coresight_device *csdev; - bool registered = false; csdev = kzalloc_obj(*csdev); - if (!csdev) { - ret = -ENOMEM; - goto err_out; - } + if (!csdev) + return ERR_PTR(-ENOMEM); csdev->pdata = desc->pdata; - csdev->type = desc->type; csdev->subtype = desc->subtype; csdev->ops = desc->ops; @@ -1360,6 +1356,21 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->dev.release = coresight_device_release; csdev->dev.bus = &coresight_bustype; + return csdev; +} + +struct coresight_device *coresight_register(struct coresight_desc *desc) +{ + int ret; + struct coresight_device *csdev; + bool registered = false; + + csdev = coresight_init_device(desc); + if (IS_ERR(csdev)) { + ret = PTR_ERR(csdev); + goto err_out; + } + if (csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { raw_spin_lock_init(&csdev->perf_sink_id_map.lock); From da2bfe3377b598b297400e7c6c3bf5d493b408c8 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:11 +0100 Subject: [PATCH 1038/5214] coresight: Populate CPU ID into coresight_device Add a new flag CORESIGHT_DESC_CPU_BOUND to indicate components that are CPU bound. Populate CPU ID into the coresight_device structure; otherwise, set CPU ID to -1 for non CPU bound devices. Use the {0} initializer to clear coresight_desc structures to avoid uninitialized values. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-4-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-catu.c | 2 +- drivers/hwtracing/coresight/coresight-core.c | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-cti-core.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-etm3x-core.c | 2 ++ drivers/hwtracing/coresight/coresight-etm4x-core.c | 2 ++ drivers/hwtracing/coresight/coresight-trbe.c | 2 ++ include/linux/coresight.h | 8 ++++++++ 7 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ce71dcddfca2..43abe13995cf 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -514,7 +514,7 @@ static int __catu_probe(struct device *dev, struct resource *res) int ret = 0; u32 dma_mask; struct catu_drvdata *drvdata; - struct coresight_desc catu_desc; + struct coresight_desc catu_desc = { 0 }; struct coresight_platform_data *pdata = NULL; void __iomem *base; diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index d5d18168b481..ffed314d1313 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1350,6 +1350,19 @@ coresight_init_device(struct coresight_desc *desc) csdev->access = desc->access; csdev->orphan = true; + if (desc->flags & CORESIGHT_DESC_CPU_BOUND) { + csdev->cpu = desc->cpu; + } else { + /* A per-CPU source or sink must set CPU_BOUND flag */ + if (coresight_is_percpu_source(csdev) || + coresight_is_percpu_sink(csdev)) { + kfree(csdev); + return ERR_PTR(-EINVAL); + } + + csdev->cpu = -1; + } + csdev->dev.type = &coresight_dev_type[desc->type]; csdev->dev.groups = desc->groups; csdev->dev.parent = desc->dev; diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 2f4c9362709a..b2c9a4db13b4 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -659,7 +659,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) void __iomem *base; struct device *dev = &adev->dev; struct cti_drvdata *drvdata = NULL; - struct coresight_desc cti_desc; + struct coresight_desc cti_desc = { 0 }; struct coresight_platform_data *pdata = NULL; struct resource *res = &adev->res; @@ -702,11 +702,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) * eCPU ID. System CTIs will have the name cti_sys where I is an * index allocated by order of discovery. */ - if (drvdata->ctidev.cpu >= 0) + if (drvdata->ctidev.cpu >= 0) { + cti_desc.cpu = drvdata->ctidev.cpu; + cti_desc.flags = CORESIGHT_DESC_CPU_BOUND; cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d", drvdata->ctidev.cpu); - else + } else { cti_desc.name = coresight_alloc_device_name("cti_sys", dev); + } if (!cti_desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index a547a6d2e0bd..eb665db1a37d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -891,6 +891,8 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etm_groups; + desc.cpu = drvdata->cpu; + desc.flags = CORESIGHT_DESC_CPU_BOUND; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index a251375db24b..8f270bfc7472 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2291,6 +2291,8 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etmv4_groups; + desc.cpu = drvdata->cpu; + desc.flags = CORESIGHT_DESC_CPU_BOUND; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 1511f8eb95af..14e35b9660d7 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -1289,6 +1289,8 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp desc.ops = &arm_trbe_cs_ops; desc.groups = arm_trbe_groups; desc.dev = dev; + desc.cpu = cpu; + desc.flags = CORESIGHT_DESC_CPU_BOUND; trbe_csdev = coresight_register(&desc); if (IS_ERR(trbe_csdev)) goto cpu_clear; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2131febebee9..687190ca11dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -141,6 +141,8 @@ struct csdev_access { .base = (_addr), \ }) +#define CORESIGHT_DESC_CPU_BOUND BIT(0) + /** * struct coresight_desc - description of a component required from drivers * @type: as defined by @coresight_dev_type. @@ -153,6 +155,8 @@ struct csdev_access { * in the component's sysfs sub-directory. * @name: name for the coresight device, also shown under sysfs. * @access: Describe access to the device + * @flags: The descritpion flags. + * @cpu: The CPU this component is affined to. */ struct coresight_desc { enum coresight_dev_type type; @@ -163,6 +167,8 @@ struct coresight_desc { const struct attribute_group **groups; const char *name; struct csdev_access access; + u32 flags; + int cpu; }; /** @@ -260,6 +266,7 @@ struct coresight_trace_id_map { * device's spinlock when the coresight_mutex held and mode == * CS_MODE_SYSFS. Otherwise it must be accessed from inside the * spinlock. + * @cpu: The CPU this component is affined to (-1 for not CPU bound). * @orphan: true if the component has connections that haven't been linked. * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs * by writing a 1 to the 'enable_sink' file. A sink can be @@ -286,6 +293,7 @@ struct coresight_device { struct device dev; atomic_t mode; int refcnt; + int cpu; bool orphan; /* sink specific fields */ bool sysfs_sink_activated; From 9d5eb760e304d5c1e5deb80a185c6a04deaab7fe Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:12 +0100 Subject: [PATCH 1039/5214] coresight: Remove .cpu_id() callback from source ops The CPU ID can be fetched directly from the coresight_device structure, so the .cpu_id() callback is no longer needed. Remove the .cpu_id() callback from source ops and update callers accordingly. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-5-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 8 ++++---- drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-etm3x-core.c | 8 -------- drivers/hwtracing/coresight/coresight-etm4x-core.c | 8 -------- drivers/hwtracing/coresight/coresight-sysfs.c | 4 ++-- include/linux/coresight.h | 3 --- 6 files changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index ffed314d1313..5c711e517501 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -799,7 +799,7 @@ static int _coresight_build_path(struct coresight_device *csdev, goto out; if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) && - sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) { + sink == per_cpu(csdev_sink, csdev->cpu)) { if (_coresight_build_path(sink, source, sink, path) == 0) { found = true; goto out; @@ -1026,7 +1026,7 @@ coresight_find_default_sink(struct coresight_device *csdev) /* look for a default sink if we have not found for this device */ if (!csdev->def_sink) { if (coresight_is_percpu_source(csdev)) - csdev->def_sink = per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev)); + csdev->def_sink = per_cpu(csdev_sink, csdev->cpu); if (!csdev->def_sink) csdev->def_sink = coresight_find_sink(csdev, &depth); } @@ -1764,10 +1764,10 @@ int coresight_etm_get_trace_id(struct coresight_device *csdev, enum cs_mode mode { int cpu, trace_id; - if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE || !source_ops(csdev)->cpu_id) + if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) return -EINVAL; - cpu = source_ops(csdev)->cpu_id(csdev); + cpu = csdev->cpu; switch (mode) { case CS_MODE_SYSFS: trace_id = coresight_trace_id_get_cpu_id(cpu); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 89ba7c9a6613..7434f68d4482 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -825,7 +825,7 @@ static void etm_addr_filters_sync(struct perf_event *event) int etm_perf_symlink(struct coresight_device *csdev, bool link) { char entry[sizeof("cpu9999999")]; - int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev); + int ret = 0, cpu = csdev->cpu; struct device *pmu_dev = etm_pmu.dev; struct device *cs_dev = &csdev->dev; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index eb665db1a37d..ab47f69e923f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -466,13 +466,6 @@ static void etm_enable_sysfs_smp_call(void *info) coresight_set_mode(csdev, CS_MODE_DISABLED); } -static int etm_cpu_id(struct coresight_device *csdev) -{ - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return drvdata->cpu; -} - void etm_release_trace_id(struct etm_drvdata *drvdata) { coresight_trace_id_put_cpu_id(drvdata->cpu); @@ -684,7 +677,6 @@ static void etm_disable(struct coresight_device *csdev, } static const struct coresight_ops_source etm_source_ops = { - .cpu_id = etm_cpu_id, .enable = etm_enable, .disable = etm_disable, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 8f270bfc7472..b7312570a7ae 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -231,13 +231,6 @@ static void etm4_cs_unlock(struct etmv4_drvdata *drvdata, CS_UNLOCK(csa->base); } -static int etm4_cpu_id(struct coresight_device *csdev) -{ - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return drvdata->cpu; -} - void etm4_release_trace_id(struct etmv4_drvdata *drvdata) { coresight_trace_id_put_cpu_id(drvdata->cpu); @@ -1205,7 +1198,6 @@ static void etm4_pause_perf(struct coresight_device *csdev) } static const struct coresight_ops_source etm4_source_ops = { - .cpu_id = etm4_cpu_id, .enable = etm4_enable, .disable = etm4_disable, .resume_perf = etm4_resume_perf, diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index da6f22b512c9..905c973e99ce 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -232,7 +232,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) * be a single session per tracer (when working from sysFS) * a per-cpu variable will do just fine. */ - cpu = source_ops(csdev)->cpu_id(csdev); + cpu = csdev->cpu; per_cpu(tracer_path, cpu) = path; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: @@ -284,7 +284,7 @@ void coresight_disable_sysfs(struct coresight_device *csdev) switch (csdev->subtype.source_subtype) { case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - cpu = source_ops(csdev)->cpu_id(csdev); + cpu = csdev->cpu; path = per_cpu(tracer_path, cpu); per_cpu(tracer_path, cpu) = NULL; break; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 687190ca11dd..e9c20ceb9016 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -395,15 +395,12 @@ struct coresight_ops_link { /** * struct coresight_ops_source - basic operations for a source * Operations available for sources. - * @cpu_id: returns the value of the CPU number this component - * is associated to. * @enable: enables tracing for a source. * @disable: disables tracing for a source. * @resume_perf: resumes tracing for a source in perf session. * @pause_perf: pauses tracing for a source in perf session. */ struct coresight_ops_source { - int (*cpu_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, struct coresight_path *path); void (*disable)(struct coresight_device *csdev, From 2c7f786928c4319ee9c68207a28529e5226b9266 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:13 +0100 Subject: [PATCH 1040/5214] coresight: Take hotplug lock in enable_source_store() for Sysfs mode The hotplug lock is acquired and released in etm{3|4}_disable_sysfs(), which are low-level functions. This prevents us from a new solution for hotplug. Firstly, hotplug callbacks cannot invoke etm{3|4}_disable_sysfs() to disable the source; otherwise, a deadlock issue occurs. The reason is that, in the hotplug flow, the kernel acquires the hotplug lock before calling callbacks. Subsequently, if coresight_disable_source() is invoked and it calls etm{3|4}_disable_sysfs(), the hotplug lock will be acquired twice, leading to a double lock issue. Secondly, when hotplugging a CPU on or off, if we want to manipulate all components on a path attached to the CPU, we need to maintain atomicity for the entire path. Otherwise, a race condition may occur with users setting the same path via the Sysfs knobs, ultimately causing mess states in CoreSight components. This patch moves the hotplug locking from etm{3|4}_disable_sysfs() into enable_source_store(). As a result, when users control the Sysfs knobs, the whole flow is protected by hotplug locking, ensuring it is mutual exclusive with hotplug callbacks. Note, the paired function etm{3|4}_enable_sysfs() does not use hotplug locking, which is why this patch does not modify it. Reviewed-by: Mike Leach Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-6-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-etm3x-core.c | 8 -------- drivers/hwtracing/coresight/coresight-etm4x-core.c | 9 --------- drivers/hwtracing/coresight/coresight-sysfs.c | 7 +++++++ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index ab47f69e923f..aeeb284abdbe 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -620,13 +620,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* - * Taking hotplug lock here protects from clocks getting disabled - * with tracing being left on (crash scenario) if user disable occurs - * after cpu online mask indicates the cpu is offline but before the - * DYING hotplug callback is serviced by the ETM driver. - */ - cpus_read_lock(); spin_lock(&drvdata->spinlock); /* @@ -637,7 +630,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev) drvdata, 1); spin_unlock(&drvdata->spinlock); - cpus_read_unlock(); /* * we only release trace IDs when resetting sysfs. diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index b7312570a7ae..7011a20d2004 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1110,13 +1110,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* - * Taking hotplug lock here protects from clocks getting disabled - * with tracing being left on (crash scenario) if user disable occurs - * after cpu online mask indicates the cpu is offline but before the - * DYING hotplug callback is serviced by the ETM driver. - */ - cpus_read_lock(); raw_spin_lock(&drvdata->spinlock); /* @@ -1130,8 +1123,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) cscfg_csdev_disable_active_config(csdev); - cpus_read_unlock(); - /* * we only release trace IDs when resetting sysfs. * This permits sysfs users to read the trace ID after the trace diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 905c973e99ce..682500b7296c 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -362,6 +362,13 @@ static ssize_t enable_source_store(struct device *dev, if (ret) return ret; + /* + * CoreSight hotplug callbacks in core layer control a activated path + * from its source to sink. Taking hotplug lock here protects a race + * condition with hotplug callbacks. + */ + guard(cpus_read_lock)(); + if (val) { ret = coresight_enable_sysfs(csdev); if (ret) From f37bc31447c0ddafedb25e3c4a4f4e2284034247 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:14 +0100 Subject: [PATCH 1041/5214] coresight: perf: Retrieve path and source from event data ETM perf callbacks currently use the per-CPU csdev_src pointer, which can race with updates during device registration and unregistration. The AUX setup already builds and stores the path in the event data. Use this path to retrieve the source instead of csdev_src to avoid the race. Export coresight_get_source() and add etm_event_get_ctxt_path() to retrieve the context's path and its source with READ_ONCE() / WRITE_ONCE() accessors. Give the comments to explain why this approach is safe when pause or resume callbacks preempt the disable callback (e.g. via NMI). Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-7-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 2 +- .../hwtracing/coresight/coresight-etm-perf.c | 116 +++++++++++------- drivers/hwtracing/coresight/coresight-priv.h | 1 + 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 5c711e517501..3fc6a5db778c 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -82,7 +82,7 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); -static struct coresight_device *coresight_get_source(struct coresight_path *path) +struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 7434f68d4482..bab85f7dac47 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -315,6 +315,35 @@ static bool sinks_compatible(struct coresight_device *a, (sink_ops(a) == sink_ops(b)); } +/* + * This helper is used for fetching the path pointer via the ctxt. + * + * Perf event callbacks run on the same CPU in atomic context, but AUX pause + * and resume may run in NMI context and preempt other callbacks. Since the + * event stop callback clears ctxt->event_data before the data is released, + * AUX pause/resume will either observe a NULL pointer and stop fetching the + * path pointer, or safely access event_data and the path, as the data has + * not yet been freed. + */ +static struct coresight_path *etm_event_get_ctxt_path(struct etm_ctxt *ctxt) +{ + struct etm_event_data *event_data; + struct coresight_path *path; + + if (!ctxt) + return NULL; + + event_data = READ_ONCE(ctxt->event_data); + if (!event_data) + return NULL; + + path = etm_event_cpu_path(event_data, smp_processor_id()); + if (!path) + return NULL; + + return path; +} + static void *etm_setup_aux(struct perf_event *event, void **pages, int nr_pages, bool overwrite) { @@ -465,13 +494,23 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, goto out; } -static int etm_event_resume(struct coresight_device *csdev, - struct etm_ctxt *ctxt) +static int etm_event_resume(struct coresight_path *path) { - if (!ctxt->event_data) + struct coresight_device *source; + int ret; + + if (!path) return 0; - return coresight_resume_source(csdev); + source = coresight_get_source(path); + if (!source) + return 0; + + ret = coresight_resume_source(source); + if (ret < 0) + dev_err(&source->dev, "Failed to resume ETM event.\n"); + + return ret; } static void etm_event_start(struct perf_event *event, int flags) @@ -480,23 +519,19 @@ static void etm_event_start(struct perf_event *event, int flags) struct etm_event_data *event_data; struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt); struct perf_output_handle *handle = &ctxt->handle; - struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct coresight_device *source, *sink; struct coresight_path *path; u64 hw_id; - if (!csdev) - goto fail; - if (flags & PERF_EF_RESUME) { - if (etm_event_resume(csdev, ctxt) < 0) { - dev_err(&csdev->dev, "Failed to resume ETM event.\n"); + path = etm_event_get_ctxt_path(ctxt); + if (etm_event_resume(path) < 0) goto fail; - } return; } /* Have we messed up our tracking ? */ - if (WARN_ON(ctxt->event_data)) + if (WARN_ON(READ_ONCE(ctxt->event_data))) goto fail; /* @@ -524,9 +559,10 @@ static void etm_event_start(struct perf_event *event, int flags) path = etm_event_cpu_path(event_data, cpu); path->handle = handle; - /* We need a sink, no need to continue without one */ + /* We need source and sink, no need to continue if any is not set */ + source = coresight_get_source(path); sink = coresight_get_sink(path); - if (WARN_ON_ONCE(!sink)) + if (WARN_ON_ONCE(!source || !sink)) goto fail_end_stop; /* Nothing will happen without a path */ @@ -534,7 +570,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop; /* Finally enable the tracer */ - if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path)) + if (source_ops(source)->enable(source, event, CS_MODE_PERF, path)) goto fail_disable_path; /* @@ -558,7 +594,7 @@ static void etm_event_start(struct perf_event *event, int flags) /* Tell the perf core the event is alive */ event->hw.state = 0; /* Save the event_data for this ETM */ - ctxt->event_data = event_data; + WRITE_ONCE(ctxt->event_data, event_data); return; fail_disable_path: @@ -578,26 +614,25 @@ static void etm_event_start(struct perf_event *event, int flags) return; } -static void etm_event_pause(struct perf_event *event, - struct coresight_device *csdev, +static void etm_event_pause(struct coresight_path *path, + struct perf_event *event, struct etm_ctxt *ctxt) { - int cpu = smp_processor_id(); - struct coresight_device *sink; struct perf_output_handle *handle = &ctxt->handle; - struct coresight_path *path; + struct coresight_device *source, *sink; + struct etm_event_data *event_data; unsigned long size; - if (!ctxt->event_data) + if (!path) + return; + + source = coresight_get_source(path); + sink = coresight_get_sink(path); + if (WARN_ON_ONCE(!source || !sink)) return; /* Stop tracer */ - coresight_pause_source(csdev); - - path = etm_event_cpu_path(ctxt->event_data, cpu); - sink = coresight_get_sink(path); - if (WARN_ON_ONCE(!sink)) - return; + coresight_pause_source(source); /* * The per CPU sink has own interrupt handling, it might have @@ -614,8 +649,9 @@ static void etm_event_pause(struct perf_event *event, if (!sink_ops(sink)->update_buffer) return; + event_data = READ_ONCE(ctxt->event_data); size = sink_ops(sink)->update_buffer(sink, handle, - ctxt->event_data->snk_config); + event_data->snk_config); if (READ_ONCE(handle->event)) { if (!size) return; @@ -631,14 +667,14 @@ static void etm_event_stop(struct perf_event *event, int mode) { int cpu = smp_processor_id(); unsigned long size; - struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct coresight_device *source, *sink; struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt); struct perf_output_handle *handle = &ctxt->handle; + struct coresight_path *path = etm_event_get_ctxt_path(ctxt); struct etm_event_data *event_data; - struct coresight_path *path; if (mode & PERF_EF_PAUSE) - return etm_event_pause(event, csdev, ctxt); + return etm_event_pause(path, event, ctxt); /* * If we still have access to the event_data via handle, @@ -648,9 +684,9 @@ static void etm_event_stop(struct perf_event *event, int mode) WARN_ON(perf_get_aux(handle) != ctxt->event_data)) return; - event_data = ctxt->event_data; + event_data = READ_ONCE(ctxt->event_data); /* Clear the event_data as this ETM is stopping the trace. */ - ctxt->event_data = NULL; + WRITE_ONCE(ctxt->event_data, NULL); if (event->hw.state == PERF_HES_STOPPED) return; @@ -672,19 +708,13 @@ static void etm_event_stop(struct perf_event *event, int mode) return; } - if (!csdev) - return; - - path = etm_event_cpu_path(event_data, cpu); - if (!path) - return; - + source = coresight_get_source(path); sink = coresight_get_sink(path); - if (!sink) + if (!source || !sink) return; /* stop tracer */ - coresight_disable_source(csdev, event); + coresight_disable_source(source, event); /* tell the core */ event->hw.state = PERF_HES_STOPPED; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 34c7e792adbd..75029744561f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -248,6 +248,7 @@ void coresight_add_helper(struct coresight_device *csdev, void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); +struct coresight_device *coresight_get_source(struct coresight_path *path); void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev); From 6317302ae25268f53fc292226592a7776b39dffd Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:15 +0100 Subject: [PATCH 1042/5214] coresight: Take a reference on csdev coresight_get_ref() currently pins the provider module and takes a reference on the parent device, but it does not pin &csdev->dev. Take a reference on &csdev->dev when grabbing a CoreSight device and drop it in coresight_put_ref(). Reorder the sequence to follow child-to-parent dependencies: first take a reference on csdev, then on the parent device and grab the driver module. Once the data and module are pinned, take a PM runtime reference to power on the hardware. Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-8-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 36 +++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 3fc6a5db778c..6b098c3cab0b 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -651,15 +651,29 @@ struct coresight_device *coresight_get_sink_by_id(u32 id) */ static bool coresight_get_ref(struct coresight_device *csdev) { - struct device *dev = csdev->dev.parent; + struct device *dev = &csdev->dev; + struct device *parent = csdev->dev.parent; + struct device_driver *drv; + + /* Make sure csdev can't go away */ + get_device(dev); + + /* Make sure parent device can't go away */ + get_device(parent); /* Make sure the driver can't be removed */ - if (!try_module_get(dev->driver->owner)) - return false; - /* Make sure the device can't go away */ - get_device(dev); - pm_runtime_get_sync(dev); + drv = parent->driver; + if (!drv || !try_module_get(drv->owner)) + goto err_module; + + /* Make sure the device is powered on */ + pm_runtime_get_sync(parent); return true; + +err_module: + put_device(parent); + put_device(dev); + return false; } /** @@ -670,11 +684,15 @@ static bool coresight_get_ref(struct coresight_device *csdev) */ static void coresight_put_ref(struct coresight_device *csdev) { - struct device *dev = csdev->dev.parent; + struct device *dev = &csdev->dev; + struct device *parent = csdev->dev.parent; + struct device_driver *drv = parent->driver; - pm_runtime_put(dev); + pm_runtime_put(parent); + if (drv) + module_put(drv->owner); + put_device(parent); put_device(dev); - module_put(dev->driver->owner); } /* From 3a4a1c4dd977b5141ad28958d940d2b1aaecf70e Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:16 +0100 Subject: [PATCH 1043/5214] coresight: Move per-CPU source pointer to core layer Move the per-CPU source pointer from ETM perf to the core layer, as this will be used for not only perf session and also for CPU PM notifiers. Provides coresight_{set|clear|get}_percpu_source() helpers to access the per-CPU source pointer. Add a raw spinlock to protect exclusive access. Device registration and unregistration call the set and clear helpers for init and teardown the pointers. Update callers to invoke coresight_get_percpu_source() for retrieving the pointer. Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-9-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 38 +++++++++++++++++++ .../hwtracing/coresight/coresight-etm-perf.c | 14 ++----- drivers/hwtracing/coresight/coresight-priv.h | 1 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6b098c3cab0b..995a4a1bab3d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -35,6 +35,9 @@ DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); +static DEFINE_RAW_SPINLOCK(coresight_dev_lock); +static DEFINE_PER_CPU(struct coresight_device *, csdev_source); + /** * struct coresight_node - elements of a path, from source to sink * @csdev: Address of an element. @@ -82,6 +85,39 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static void coresight_set_percpu_source(struct coresight_device *csdev) +{ + if (!csdev || !coresight_is_percpu_source(csdev)) + return; + + guard(raw_spinlock_irqsave)(&coresight_dev_lock); + + /* Expect no device to be set yet */ + WARN_ON(per_cpu(csdev_source, csdev->cpu)); + per_cpu(csdev_source, csdev->cpu) = csdev; +} + +static void coresight_clear_percpu_source(struct coresight_device *csdev) +{ + if (!csdev || !coresight_is_percpu_source(csdev)) + return; + + guard(raw_spinlock_irqsave)(&coresight_dev_lock); + + /* The per-CPU pointer should contain the same csdev */ + WARN_ON(per_cpu(csdev_source, csdev->cpu) != csdev); + per_cpu(csdev_source, csdev->cpu) = NULL; +} + +struct coresight_device *coresight_get_percpu_source(int cpu) +{ + if (WARN_ON(cpu < 0)) + return NULL; + + guard(raw_spinlock_irqsave)(&coresight_dev_lock); + return per_cpu(csdev_source, cpu); +} + struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1452,6 +1488,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) if (ret) goto out_unlock; + coresight_set_percpu_source(csdev); mutex_unlock(&coresight_mutex); if (cti_assoc_ops && cti_assoc_ops->add) @@ -1481,6 +1518,7 @@ void coresight_unregister(struct coresight_device *csdev) cti_assoc_ops->remove(csdev); mutex_lock(&coresight_mutex); + coresight_clear_percpu_source(csdev); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev); coresight_clear_default_sink(csdev); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index bab85f7dac47..b9e556818c3c 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -49,7 +49,6 @@ struct etm_ctxt { }; static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt); -static DEFINE_PER_CPU(struct coresight_device *, csdev_src); GEN_PMU_FORMAT_ATTR(cycacc); GEN_PMU_FORMAT_ATTR(timestamp); @@ -386,7 +385,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, struct coresight_path *path; struct coresight_device *csdev; - csdev = per_cpu(csdev_src, cpu); + csdev = coresight_get_percpu_source(cpu); /* * If there is no ETM associated with this CPU clear it from * the mask and continue with the rest. If ever we try to trace @@ -864,17 +863,12 @@ int etm_perf_symlink(struct coresight_device *csdev, bool link) if (!etm_perf_up) return -EPROBE_DEFER; - if (link) { + if (link) ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry); - if (ret) - return ret; - per_cpu(csdev_src, cpu) = csdev; - } else { + else sysfs_remove_link(&pmu_dev->kobj, entry); - per_cpu(csdev_src, cpu) = NULL; - } - return 0; + return ret; } EXPORT_SYMBOL_GPL(etm_perf_symlink); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 75029744561f..4d6f5bc1df9c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -249,6 +249,7 @@ void coresight_add_helper(struct coresight_device *csdev, void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); struct coresight_device *coresight_get_source(struct coresight_path *path); +struct coresight_device *coresight_get_percpu_source(int cpu); void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev); From d79125cc3622680c9f2880acacae3981b4bcf08c Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:17 +0100 Subject: [PATCH 1044/5214] coresight: Take per-CPU source reference during AUX setup etm_setup_aux() fetches the per-CPU source pointer while preparing perf AUX trace paths. This can race with CoreSight device unregistration, the ETM device may has been released while the AUX setup still use it, leading to use-after-free. Move per-CPU path construction into etm_event_build_path() and use the coresight_{get|put}_percpu_source_ref() pairs to take and drop the device references, this ensures the device is safe to access during path construction. Update comments accordingly. Document a PREEMPT_RT corner case: the put_device() may release resources while coresight_dev_lock (a raw spinlock) is held, and the release may attempt to acquire a spinlock that becomes sleepable under PREEMPT_RT. This will be fixed in the future. Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-10-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 38 ++++- .../hwtracing/coresight/coresight-etm-perf.c | 160 ++++++++++-------- drivers/hwtracing/coresight/coresight-priv.h | 3 +- 3 files changed, 130 insertions(+), 71 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 995a4a1bab3d..fc4855c3dfaa 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -109,13 +109,47 @@ static void coresight_clear_percpu_source(struct coresight_device *csdev) per_cpu(csdev_source, csdev->cpu) = NULL; } -struct coresight_device *coresight_get_percpu_source(int cpu) +struct coresight_device *coresight_get_percpu_source_ref(int cpu) { + struct coresight_device *csdev; + if (WARN_ON(cpu < 0)) return NULL; guard(raw_spinlock_irqsave)(&coresight_dev_lock); - return per_cpu(csdev_source, cpu); + + csdev = per_cpu(csdev_source, cpu); + if (!csdev) + return NULL; + + /* + * Holding a reference to the csdev->dev ensures that the + * coresight_device is live for the caller. The path building + * logic can safely either build a path to the sink or fail + * if the device is being unregistered (if there was a race). + * The caller can skip the "source" device, if no path could + * be built. + */ + get_device(&csdev->dev); + + return csdev; +} + +void coresight_put_percpu_source_ref(struct coresight_device *csdev) +{ + if (!csdev || !coresight_is_percpu_source(csdev)) + return; + + guard(raw_spinlock_irqsave)(&coresight_dev_lock); + + /* + * TODO: coresight_device_release() is invoked to release resources when + * the device's refcount reaches zero. It then calls free_percpu(), + * which acquires pcpu_lock — a sleepable lock when PREEMPT_RT is + * enabled. Since the raw spinlock coresight_dev_lock is held, this can + * lead to a potential "scheduling while atomic" issue. + */ + put_device(&csdev->dev); } struct coresight_device *coresight_get_source(struct coresight_path *path) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index b9e556818c3c..89b99c3caedb 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -343,6 +343,86 @@ static struct coresight_path *etm_event_get_ctxt_path(struct etm_ctxt *ctxt) return path; } +static struct coresight_path * +etm_event_build_path(struct perf_event *event, int cpu, + struct coresight_device *user_sink, + struct coresight_device *match_sink) +{ + struct coresight_path *path = NULL; + struct coresight_device *source, *sink; + int ret; + + source = coresight_get_percpu_source_ref(cpu); + + /* + * If there is no ETM associated with this CPU or ever we try to trace + * on this CPU, we handle it accordingly. + */ + if (!source) + return NULL; + + /* + * If AUX pause feature is enabled but the ETM driver does not + * support the operations, skip for this source. + */ + if (event->attr.aux_start_paused && + (!source_ops(source)->pause_perf || + !source_ops(source)->resume_perf)) { + dev_err_once(&source->dev, "AUX pause is not supported.\n"); + goto out; + } + + /* If sink has been specified by user, directly use it */ + if (user_sink) { + sink = user_sink; + } else { + /* + * No sink provided - look for a default sink for all the ETMs, + * where this event can be scheduled. + * + * We allocate the sink specific buffers only once for this + * event. If the ETMs have different default sink devices, we + * can only use a single "type" of sink as the event can carry + * only one sink specific buffer. Thus we have to make sure + * that the sinks are of the same type and driven by the same + * driver, as the one we allocate the buffer for. We don't + * trace on a CPU if the sink is not compatible. + */ + + /* Find the default sink for this ETM */ + sink = coresight_find_default_sink(source); + if (!sink) + goto out; + + /* Check if this sink compatible with the last sink */ + if (match_sink && !sinks_compatible(match_sink, sink)) + goto out; + } + + /* + * Building a path doesn't enable it, it simply builds a + * list of devices from source to sink that can be + * referenced later when the path is actually needed. + */ + path = coresight_build_path(source, sink); + if (IS_ERR(path)) + goto out; + + /* ensure we can allocate a trace ID for this CPU */ + ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); + if (ret) { + coresight_release_path(path); + path = NULL; + goto out; + } + + coresight_trace_id_perf_start(&sink->perf_sink_id_map); + +out: + coresight_put_percpu_source_ref(source); + return IS_ERR_OR_NULL(path) ? NULL : path; +} + static void *etm_setup_aux(struct perf_event *event, void **pages, int nr_pages, bool overwrite) { @@ -350,9 +430,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, int cpu = event->cpu; cpumask_t *mask; struct coresight_device *sink = NULL; - struct coresight_device *user_sink = NULL, *last_sink = NULL; + struct coresight_device *user_sink = NULL; struct etm_event_data *event_data = NULL; - int ret; event_data = alloc_event_data(cpu); if (!event_data) @@ -383,80 +462,25 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, */ for_each_cpu(cpu, mask) { struct coresight_path *path; - struct coresight_device *csdev; - csdev = coresight_get_percpu_source(cpu); - /* - * If there is no ETM associated with this CPU clear it from - * the mask and continue with the rest. If ever we try to trace - * on this CPU, we handle it accordingly. - */ - if (!csdev) { + path = etm_event_build_path(event, cpu, user_sink, sink); + if (!path) { + /* + * Failed to create a path for the CPU, clear it from + * the mask and continue to next one. + */ cpumask_clear_cpu(cpu, mask); continue; } /* - * If AUX pause feature is enabled but the ETM driver does not - * support the operations, clear this CPU from the mask and - * continue to next one. + * The first found sink is saved here and passed to + * etm_event_build_path() to check whether the remaining ETMs + * have a compatible default sink. */ - if (event->attr.aux_start_paused && - (!source_ops(csdev)->pause_perf || !source_ops(csdev)->resume_perf)) { - dev_err_once(&csdev->dev, "AUX pause is not supported.\n"); - cpumask_clear_cpu(cpu, mask); - continue; - } + if (!user_sink && !sink) + sink = coresight_get_sink(path); - /* - * No sink provided - look for a default sink for all the ETMs, - * where this event can be scheduled. - * We allocate the sink specific buffers only once for this - * event. If the ETMs have different default sink devices, we - * can only use a single "type" of sink as the event can carry - * only one sink specific buffer. Thus we have to make sure - * that the sinks are of the same type and driven by the same - * driver, as the one we allocate the buffer for. As such - * we choose the first sink and check if the remaining ETMs - * have a compatible default sink. We don't trace on a CPU - * if the sink is not compatible. - */ - if (!user_sink) { - /* Find the default sink for this ETM */ - sink = coresight_find_default_sink(csdev); - if (!sink) { - cpumask_clear_cpu(cpu, mask); - continue; - } - - /* Check if this sink compatible with the last sink */ - if (last_sink && !sinks_compatible(last_sink, sink)) { - cpumask_clear_cpu(cpu, mask); - continue; - } - last_sink = sink; - } - - /* - * Building a path doesn't enable it, it simply builds a - * list of devices from source to sink that can be - * referenced later when the path is actually needed. - */ - path = coresight_build_path(csdev, sink); - if (IS_ERR(path)) { - cpumask_clear_cpu(cpu, mask); - continue; - } - - /* ensure we can allocate a trace ID for this CPU */ - ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); - if (ret) { - cpumask_clear_cpu(cpu, mask); - coresight_release_path(path); - continue; - } - - coresight_trace_id_perf_start(&sink->perf_sink_id_map); *etm_event_cpu_path_ptr(event_data, cpu) = path; } diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 4d6f5bc1df9c..808d1546f568 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -249,7 +249,8 @@ void coresight_add_helper(struct coresight_device *csdev, void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); struct coresight_device *coresight_get_source(struct coresight_path *path); -struct coresight_device *coresight_get_percpu_source(int cpu); +struct coresight_device *coresight_get_percpu_source_ref(int cpu); +void coresight_put_percpu_source_ref(struct coresight_device *csdev); void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev); From 4d8dd98ee5bf9f77862cce039adfcdccd02daa08 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:18 +0100 Subject: [PATCH 1045/5214] coresight: Register CPU PM notifier in core layer The current implementation only saves and restores the context for ETM sources while ignoring the context of links. However, if funnels or replicators on a linked path resides in a CPU or cluster power domain, the hardware context for the link will be lost after resuming from low power states. To support context management for links during CPU low power modes, a better way is to implement CPU PM callbacks in the Arm CoreSight core layer. As the core layer has sufficient information for linked paths, from tracers to links, which can be used for power management. As a first step, this patch registers CPU PM notifier in the core layer. If a source device provides callbacks for saving and restoring context, these callbacks will be invoked in CPU suspend and resume. Reviewed-by: James Clark Tested-by: James Clark Reviewed-by: Yeoreum Yun Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-11-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 93 ++++++++++++++++++++ include/linux/coresight.h | 2 + 2 files changed, 95 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index fc4855c3dfaa..309f3a844e8f 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1741,6 +1742,91 @@ static void coresight_release_device_list(void) } } +static struct coresight_device *coresight_cpu_get_active_source(void) +{ + struct coresight_device *source; + bool is_active = false; + + source = coresight_get_percpu_source_ref(smp_processor_id()); + if (!source) + return NULL; + + if (coresight_get_mode(source) != CS_MODE_DISABLED) + is_active = true; + + coresight_put_percpu_source_ref(source); + + /* + * It is expected to run in atomic context, so it cannot be preempted + * to disable the source. Here returns the active source pointer + * without concern that its state may change. Since the build path has + * taken a reference on the component, the source can be safely used + * by the caller. + */ + return is_active ? source : NULL; +} + +static int coresight_pm_is_needed(struct coresight_device *csdev) +{ + if (!csdev) + return 0; + + /* pm_save_disable() and pm_restore_enable() must be paired */ + if (coresight_ops(csdev)->pm_save_disable && + coresight_ops(csdev)->pm_restore_enable) + return 1; + + return 0; +} + +static int coresight_pm_device_save(struct coresight_device *csdev) +{ + return coresight_ops(csdev)->pm_save_disable(csdev); +} + +static void coresight_pm_device_restore(struct coresight_device *csdev) +{ + coresight_ops(csdev)->pm_restore_enable(csdev); +} + +static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, + void *v) +{ + struct coresight_device *csdev = coresight_cpu_get_active_source(); + + if (!coresight_pm_is_needed(csdev)) + return NOTIFY_DONE; + + switch (cmd) { + case CPU_PM_ENTER: + if (coresight_pm_device_save(csdev)) + return NOTIFY_BAD; + break; + case CPU_PM_EXIT: + case CPU_PM_ENTER_FAILED: + coresight_pm_device_restore(csdev); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block coresight_cpu_pm_nb = { + .notifier_call = coresight_cpu_pm_notify, +}; + +static int __init coresight_pm_setup(void) +{ + return cpu_pm_register_notifier(&coresight_cpu_pm_nb); +} + +static void coresight_pm_cleanup(void) +{ + cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); +} + const struct bus_type coresight_bustype = { .name = "coresight", }; @@ -1795,9 +1881,15 @@ static int __init coresight_init(void) /* initialise the coresight syscfg API */ ret = cscfg_init(); + if (ret) + goto exit_notifier; + + ret = coresight_pm_setup(); if (!ret) return 0; + cscfg_exit(); +exit_notifier: atomic_notifier_chain_unregister(&panic_notifier_list, &coresight_notifier); exit_perf: @@ -1809,6 +1901,7 @@ static int __init coresight_init(void) static void __exit coresight_exit(void) { + coresight_pm_cleanup(); cscfg_exit(); atomic_notifier_chain_unregister(&panic_notifier_list, &coresight_notifier); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e9c20ceb9016..5f9d7ea9f594 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -438,6 +438,8 @@ struct coresight_ops_panic { struct coresight_ops { int (*trace_id)(struct coresight_device *csdev, enum cs_mode mode, struct coresight_device *sink); + int (*pm_save_disable)(struct coresight_device *csdev); + void (*pm_restore_enable)(struct coresight_device *csdev); const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; From 0f5e588c70a868c475cef22193f12bed678e1461 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:19 +0100 Subject: [PATCH 1046/5214] coresight: etm4x: Hook CPU PM callbacks Add a helper etm4_pm_save_needed() to detect if need save context for self-hosted PM mode and hook pm_save_disable() and pm_restore_enable() callbacks through etm4_cs_pm_ops structure. With this, ETMv4 PM save/restore is integrated into the core layer's CPU PM. Organize etm4_cs_ops and etm4_cs_pm_ops together for more readable. The CPU PM notifier in the ETMv4 driver is no longer needed, remove it along with its registration and unregistration code. Reviewed-by: James Clark Tested-by: James Clark Reviewed-by: Yeoreum Yun Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-12-f88c4a3ecfe9@arm.com --- .../coresight/coresight-etm4x-core.c | 73 ++++++------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 7011a20d2004..5cd70f0f3710 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1195,11 +1195,6 @@ static const struct coresight_ops_source etm4_source_ops = { .pause_perf = etm4_pause_perf, }; -static const struct coresight_ops etm4_cs_ops = { - .trace_id = coresight_etm_get_trace_id, - .source_ops = &etm4_source_ops, -}; - static bool cpu_supports_sysreg_trace(void) { u64 dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1); @@ -1853,6 +1848,11 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; } +static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata) +{ + return !!drvdata->save_state; +} + static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) { int i, ret = 0; @@ -1995,11 +1995,12 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) return ret; } -static int etm4_cpu_save(struct etmv4_drvdata *drvdata) +static int etm4_cpu_save(struct coresight_device *csdev) { + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret = 0; - if (!drvdata->save_state) + if (!etm4_pm_save_needed(drvdata)) return 0; /* @@ -2112,47 +2113,27 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4_cs_lock(drvdata, csa); } -static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) +static void etm4_cpu_restore(struct coresight_device *csdev) { - if (!drvdata->save_state) + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!etm4_pm_save_needed(drvdata)) return; if (coresight_get_mode(drvdata->csdev)) __etm4_cpu_restore(drvdata); } -static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, - void *v) -{ - struct etmv4_drvdata *drvdata; - unsigned int cpu = smp_processor_id(); +static const struct coresight_ops etm4_cs_ops = { + .trace_id = coresight_etm_get_trace_id, + .source_ops = &etm4_source_ops, +}; - if (!etmdrvdata[cpu]) - return NOTIFY_OK; - - drvdata = etmdrvdata[cpu]; - - if (WARN_ON_ONCE(drvdata->cpu != cpu)) - return NOTIFY_BAD; - - switch (cmd) { - case CPU_PM_ENTER: - if (etm4_cpu_save(drvdata)) - return NOTIFY_BAD; - break; - case CPU_PM_EXIT: - case CPU_PM_ENTER_FAILED: - etm4_cpu_restore(drvdata); - break; - default: - return NOTIFY_DONE; - } - - return NOTIFY_OK; -} - -static struct notifier_block etm4_cpu_pm_nb = { - .notifier_call = etm4_cpu_pm_notify, +static const struct coresight_ops etm4_cs_pm_ops = { + .trace_id = coresight_etm_get_trace_id, + .source_ops = &etm4_source_ops, + .pm_save_disable = etm4_cpu_save, + .pm_restore_enable = etm4_cpu_restore, }; /* Setup PM. Deals with error conditions and counts */ @@ -2160,16 +2141,12 @@ static int __init etm4_pm_setup(void) { int ret; - ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb); - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, "arm/coresight4:starting", etm4_starting_cpu, etm4_dying_cpu); if (ret) - goto unregister_notifier; + return ret; ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight4:online", @@ -2183,15 +2160,11 @@ static int __init etm4_pm_setup(void) /* failed dyn state - remove others */ cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); - -unregister_notifier: - cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); return ret; } static void etm4_pm_clear(void) { - cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online); @@ -2270,7 +2243,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) desc.type = CORESIGHT_DEV_TYPE_SOURCE; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; - desc.ops = &etm4_cs_ops; + desc.ops = etm4_pm_save_needed(drvdata) ? &etm4_cs_pm_ops : &etm4_cs_ops; desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etmv4_groups; From 81bf1c33f6a7b2be8523e8b3655b85ca959975cf Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:20 +0100 Subject: [PATCH 1047/5214] coresight: etm4x: Remove redundant checks in PM save and restore ETMv4 driver save/restore callbacks still re-check conditions that are already validated by the core layer before the callbacks are invoked. Remove the duplicated checks and fold the two-level functions into direct callback implementations. The obsolete WARN_ON() checks are removed as well. Reviewed-by: Mike Leach Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-13-f88c4a3ecfe9@arm.com --- .../coresight/coresight-etm4x-core.c | 41 ++----------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 5cd70f0f3710..fef1270439e9 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1853,17 +1853,14 @@ static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata) return !!drvdata->save_state; } -static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) +static int etm4_cpu_save(struct coresight_device *csdev) { int i, ret = 0; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_save_state *state; - struct coresight_device *csdev = drvdata->csdev; struct csdev_access *csa; struct device *etm_dev; - if (WARN_ON(!csdev)) - return -ENODEV; - etm_dev = &csdev->dev; csa = &csdev->access; @@ -1995,32 +1992,13 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) return ret; } -static int etm4_cpu_save(struct coresight_device *csdev) -{ - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret = 0; - - if (!etm4_pm_save_needed(drvdata)) - return 0; - - /* - * Save and restore the ETM Trace registers only if - * the ETM is active. - */ - if (coresight_get_mode(drvdata->csdev)) - ret = __etm4_cpu_save(drvdata); - return ret; -} - -static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) +static void etm4_cpu_restore(struct coresight_device *csdev) { int i; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_save_state *state = drvdata->save_state; struct csdev_access *csa = &drvdata->csdev->access; - if (WARN_ON(!drvdata->csdev)) - return; - etm4_cs_unlock(drvdata, csa); etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET); @@ -2113,17 +2091,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4_cs_lock(drvdata, csa); } -static void etm4_cpu_restore(struct coresight_device *csdev) -{ - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - if (!etm4_pm_save_needed(drvdata)) - return; - - if (coresight_get_mode(drvdata->csdev)) - __etm4_cpu_restore(drvdata); -} - static const struct coresight_ops etm4_cs_ops = { .trace_id = coresight_etm_get_trace_id, .source_ops = &etm4_source_ops, From 3b6e3e04659a7e32cfbf71d6958eb6318bae50c0 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:21 +0100 Subject: [PATCH 1048/5214] coresight: syscfg: Use IRQ-safe spinlock to protect active variables cscfg_config_sysfs_get_active_cfg() will be used from idle flows, while sleeping locks are not allowed. Introduce sysfs_store_lock to replace the mutex to protect sysfs_active_config and sysfs_active_preset accesses, with IRQ-safe locking to avoid lockdep complaint. Refactor cscfg_config_sysfs_activate() to use spinlock for sysfs_active_config and activate/deactivate config. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-14-f88c4a3ecfe9@arm.com --- .../hwtracing/coresight/coresight-syscfg.c | 38 ++++++++++--------- .../hwtracing/coresight/coresight-syscfg.h | 2 + 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index d7f5037953d6..2bfdd7b45e49 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -953,39 +953,41 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti unsigned long cfg_hash; int err = 0; - mutex_lock(&cscfg_mutex); + guard(mutex)(&cscfg_mutex); cfg_hash = (unsigned long)config_desc->event_ea->var; if (activate) { /* cannot be a current active value to activate this */ - if (cscfg_mgr->sysfs_active_config) { - err = -EBUSY; - goto exit_unlock; - } - err = _cscfg_activate_config(cfg_hash); - if (!err) + if (cscfg_mgr->sysfs_active_config) + return -EBUSY; + + scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) { + err = _cscfg_activate_config(cfg_hash); + if (err) + return err; + cscfg_mgr->sysfs_active_config = cfg_hash; + } } else { - /* disable if matching current value */ - if (cscfg_mgr->sysfs_active_config == cfg_hash) { + if (cscfg_mgr->sysfs_active_config != cfg_hash) + return -EINVAL; + + scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) { + /* disable if matching current value */ _cscfg_deactivate_config(cfg_hash); cscfg_mgr->sysfs_active_config = 0; - } else - err = -EINVAL; + } } -exit_unlock: - mutex_unlock(&cscfg_mutex); - return err; + return 0; } /* set the sysfs preset value */ void cscfg_config_sysfs_set_preset(int preset) { - mutex_lock(&cscfg_mutex); + guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock); cscfg_mgr->sysfs_active_preset = preset; - mutex_unlock(&cscfg_mutex); } /* @@ -994,10 +996,9 @@ void cscfg_config_sysfs_set_preset(int preset) */ void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset) { - mutex_lock(&cscfg_mutex); + guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock); *preset = cscfg_mgr->sysfs_active_preset; *cfg_hash = cscfg_mgr->sysfs_active_config; - mutex_unlock(&cscfg_mutex); } EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg); @@ -1201,6 +1202,7 @@ static int cscfg_create_device(void) INIT_LIST_HEAD(&cscfg_mgr->load_order_list); atomic_set(&cscfg_mgr->sys_active_cnt, 0); cscfg_mgr->load_state = CSCFG_NONE; + raw_spin_lock_init(&cscfg_mgr->sysfs_store_lock); /* setup the device */ dev = cscfg_device(); diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 66e2db890d82..658e93c3705f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -42,6 +42,7 @@ enum cscfg_load_ops { * @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs. * @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs. * @load_state: A multi-stage load/unload operation is in progress. + * @sysfs_store_lock: Exclusive access sysfs stored variables. */ struct cscfg_manager { struct device dev; @@ -54,6 +55,7 @@ struct cscfg_manager { u32 sysfs_active_config; int sysfs_active_preset; enum cscfg_load_ops load_state; + raw_spinlock_t sysfs_store_lock; }; /* get reference to dev in cscfg_manager */ From 5cae719943399929b4f9e612d9400017d3e2c1e1 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:22 +0100 Subject: [PATCH 1049/5214] coresight: Disable source helpers in coresight_disable_path() coresight_enable_path() enables helpers attached to every device in the path, including those bound to the source. However, coresight_disable_path() skips the source node, so source helpers had to be disabled separately in coresight_disable_source(). Move source helper disabling into coresight_disable_path() instead. Make coresight_disable_path_from() start from the passed node nd, so it can also disable helpers on the source. Update the comments accordingly. As coresight_disable_path_from() now changes its semantics from "start beyond nd" to "start from nd", update the failure handling in coresight_enable_path(). If enabling a node fails, iterate to the previous node (the last successfully enabled one) and pass it to coresight_disable_path_from() for rollback. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-15-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 27 ++++++-------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 309f3a844e8f..d65cdce1dfef 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -456,19 +456,12 @@ static void coresight_disable_helpers(struct coresight_device *csdev, } /* - * Helper function to call source_ops(csdev)->disable and also disable the - * helpers. - * - * There is an imbalance between coresight_enable_path() and - * coresight_disable_path(). Enabling also enables the source's helpers as part - * of the path, but disabling always skips the first item in the path (which is - * the source), so sources and their helpers don't get disabled as part of that - * function and we need the extra step here. + * coresight_disable_source() only disables the source, but do nothing for + * the associated helpers, which are controlled as part of the path. */ void coresight_disable_source(struct coresight_device *csdev, void *data) { source_ops(csdev)->disable(csdev, data); - coresight_disable_helpers(csdev, NULL); } EXPORT_SYMBOL_GPL(coresight_disable_source); @@ -495,9 +488,9 @@ int coresight_resume_source(struct coresight_device *csdev) EXPORT_SYMBOL_GPL(coresight_resume_source); /* - * coresight_disable_path_from : Disable components in the given path beyond - * @nd in the list. If @nd is NULL, all the components, except the SOURCE are - * disabled. + * coresight_disable_path_from : Disable components in the given path starting + * from @nd in the list. If @nd is NULL, all the components, except the SOURCE + * are disabled. */ static void coresight_disable_path_from(struct coresight_path *path, struct coresight_node *nd) @@ -508,7 +501,7 @@ static void coresight_disable_path_from(struct coresight_path *path, if (!nd) nd = list_first_entry(&path->path_list, struct coresight_node, link); - list_for_each_entry_continue(nd, &path->path_list, link) { + list_for_each_entry_from(nd, &path->path_list, link) { csdev = nd->csdev; type = csdev->type; @@ -528,12 +521,6 @@ static void coresight_disable_path_from(struct coresight_path *path, coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: - /* - * We skip the first node in the path assuming that it - * is the source. So we don't expect a source device in - * the middle of a path. - */ - WARN_ON(1); break; case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; @@ -648,6 +635,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) err_disable_helpers: coresight_disable_helpers(csdev, path); err_disable_path: + /* Fetch the previous node, the last successfully enabled one */ + nd = list_next_entry(nd, link); coresight_disable_path_from(path, nd); goto out; } From 3d3289c0d850b186e07351b77e89e0ee755d1f62 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:23 +0100 Subject: [PATCH 1050/5214] coresight: Control path with range CPU PM notifiers need to control only part of a path instead of always operating on the full path. Add internal enable and disable helpers that take an inclusive node range [from, to], validate that the requested nodes are ordered before using them. Update the existed coresight_{enable|disable}_path() interfaces as full-path wrappers by passing the first and last path nodes. The helpers coresight_path_{first|last}_node() are provided for conveniently fetching the first and last nodes on the path. In coresight_enable_path_from_to(), if a failure occurs at the last node in the range, no device is actually enabled in this case, bail out directly. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-16-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 95 +++++++++++++++++--- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index d65cdce1dfef..62a12a909889 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -62,6 +62,24 @@ static LIST_HEAD(coresight_dev_idx_list); static const struct cti_assoc_op *cti_assoc_ops; +static struct coresight_node * +coresight_path_first_node(struct coresight_path *path) +{ + if (list_empty(&path->path_list)) + return NULL; + + return list_first_entry(&path->path_list, struct coresight_node, link); +} + +static struct coresight_node * +coresight_path_last_node(struct coresight_path *path) +{ + if (list_empty(&path->path_list)) + return NULL; + + return list_last_entry(&path->path_list, struct coresight_node, link); +} + void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) { cti_assoc_ops = cti_op; @@ -488,19 +506,41 @@ int coresight_resume_source(struct coresight_device *csdev) EXPORT_SYMBOL_GPL(coresight_resume_source); /* - * coresight_disable_path_from : Disable components in the given path starting - * from @nd in the list. If @nd is NULL, all the components, except the SOURCE - * are disabled. + * Callers must fetch nodes from the path and pass @from and @to to the path + * enable/disable functions. Walk the path from @from to locate @to. If @to + * is found, it indicates @from and @to are in order. Otherwise, they are out + * of order. */ -static void coresight_disable_path_from(struct coresight_path *path, - struct coresight_node *nd) +static bool coresight_path_nodes_in_order(struct coresight_path *path, + struct coresight_node *from, + struct coresight_node *to) +{ + struct coresight_node *nd; + + if (WARN_ON_ONCE(!from || !to)) + return false; + + nd = from; + list_for_each_entry_from(nd, &path->path_list, link) { + if (nd == to) + return true; + } + + return false; +} + +static void coresight_disable_path_from_to(struct coresight_path *path, + struct coresight_node *from, + struct coresight_node *to) { u32 type; struct coresight_device *csdev, *parent, *child; + struct coresight_node *nd; - if (!nd) - nd = list_first_entry(&path->path_list, struct coresight_node, link); + if (!coresight_path_nodes_in_order(path, from, to)) + return; + nd = from; list_for_each_entry_from(nd, &path->path_list, link) { csdev = nd->csdev; type = csdev->type; @@ -534,12 +574,18 @@ static void coresight_disable_path_from(struct coresight_path *path, /* Disable all helpers adjacent along the path last */ coresight_disable_helpers(csdev, path); + + /* Iterate up to and including @to */ + if (nd == to) + break; } } void coresight_disable_path(struct coresight_path *path) { - coresight_disable_path_from(path, NULL); + coresight_disable_path_from_to(path, + coresight_path_first_node(path), + coresight_path_last_node(path)); } EXPORT_SYMBOL_GPL(coresight_disable_path); @@ -572,16 +618,21 @@ static int coresight_enable_helpers(struct coresight_device *csdev, return ret; } -int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) +static int coresight_enable_path_from_to(struct coresight_path *path, + enum cs_mode mode, + struct coresight_node *from, + struct coresight_node *to) { int ret = 0; u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; - struct coresight_device *source; - source = coresight_get_source(path); - list_for_each_entry_reverse(nd, &path->path_list, link) { + if (!coresight_path_nodes_in_order(path, from, to)) + return -EINVAL; + + nd = to; + list_for_each_entry_from_reverse(nd, &path->path_list, link) { csdev = nd->csdev; type = csdev->type; @@ -620,7 +671,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; child = list_next_entry(nd, link)->csdev; - ret = coresight_enable_link(csdev, parent, child, source); + ret = coresight_enable_link(csdev, parent, child, + coresight_get_source(path)); if (ret) goto err_disable_helpers; break; @@ -628,6 +680,10 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) ret = -EINVAL; goto err_disable_helpers; } + + /* Iterate down to and including @from */ + if (nd == from) + break; } out: @@ -635,12 +691,23 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) err_disable_helpers: coresight_disable_helpers(csdev, path); err_disable_path: + /* No device is actually enabled */ + if (nd == to) + goto out; + /* Fetch the previous node, the last successfully enabled one */ nd = list_next_entry(nd, link); - coresight_disable_path_from(path, nd); + coresight_disable_path_from_to(path, nd, to); goto out; } +int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) +{ + return coresight_enable_path_from_to(path, mode, + coresight_path_first_node(path), + coresight_path_last_node(path)); +} + struct coresight_device *coresight_get_sink(struct coresight_path *path) { struct coresight_device *csdev; From 0724585ace19028a6cea8534820c7092f8a7c53c Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:24 +0100 Subject: [PATCH 1051/5214] coresight: Use helpers to fetch first and last nodes Replace open code with coresight_path_first_node() and coresight_path_last_node() for fetching the nodes. Check that the node is not NULL before accessing csdev field. Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-17-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 62a12a909889..61ae7b6c7a83 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -174,11 +174,16 @@ void coresight_put_percpu_source_ref(struct coresight_device *csdev) struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; + struct coresight_node *nd; if (!path) return NULL; - csdev = list_first_entry(&path->path_list, struct coresight_node, link)->csdev; + nd = coresight_path_first_node(path); + if (!nd) + return NULL; + + csdev = nd->csdev; if (!coresight_is_device_source(csdev)) return NULL; @@ -711,11 +716,16 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) struct coresight_device *coresight_get_sink(struct coresight_path *path) { struct coresight_device *csdev; + struct coresight_node *nd; if (!path) return NULL; - csdev = list_last_entry(&path->path_list, struct coresight_node, link)->csdev; + nd = coresight_path_last_node(path); + if (!nd) + return NULL; + + csdev = nd->csdev; if (csdev->type != CORESIGHT_DEV_TYPE_SINK && csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) return NULL; From a18e877b0491ad21b24d6f1fe2afb854990e031d Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:25 +0100 Subject: [PATCH 1052/5214] coresight: Introduce coresight_enable_source() helper Introduce the coresight_enable_source() helper for enabling source device. Add validation to ensure the device is a source before proceeding with further operations. Reviewed-by: James Clark Tested-by: James Clark Reviewed-by: Yeoreum Yun Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-18-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 18 ++++++++++++++++-- .../hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 3 +++ drivers/hwtracing/coresight/coresight-sysfs.c | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 61ae7b6c7a83..f833b73ebc16 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -479,11 +479,25 @@ static void coresight_disable_helpers(struct coresight_device *csdev, } /* - * coresight_disable_source() only disables the source, but do nothing for - * the associated helpers, which are controlled as part of the path. + * coresight_enable_source() and coresight_disable_source() only enable and + * disable the source, but do nothing for the associated helpers, which are + * controlled as part of the path. */ +int coresight_enable_source(struct coresight_device *csdev, + struct perf_event *event, enum cs_mode mode, + struct coresight_path *path) +{ + if (!coresight_is_device_source(csdev)) + return -EINVAL; + + return source_ops(csdev)->enable(csdev, event, mode, path); +} + void coresight_disable_source(struct coresight_device *csdev, void *data) { + if (!coresight_is_device_source(csdev)) + return; + source_ops(csdev)->disable(csdev, data); } EXPORT_SYMBOL_GPL(coresight_disable_source); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 89b99c3caedb..09b21a711a87 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -593,7 +593,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop; /* Finally enable the tracer */ - if (source_ops(source)->enable(source, event, CS_MODE_PERF, path)) + if (coresight_enable_source(source, event, CS_MODE_PERF, path)) goto fail_disable_path; /* diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 808d1546f568..dddac946659f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -251,6 +251,9 @@ struct coresight_device *coresight_get_percpu_sink(int cpu); struct coresight_device *coresight_get_source(struct coresight_path *path); struct coresight_device *coresight_get_percpu_source_ref(int cpu); void coresight_put_percpu_source_ref(struct coresight_device *csdev); +int coresight_enable_source(struct coresight_device *csdev, + struct perf_event *event, enum cs_mode mode, + struct coresight_path *path); void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 682500b7296c..9ec112b457bb 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -66,7 +66,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, */ lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) { - ret = source_ops(csdev)->enable(csdev, NULL, mode, path); + ret = coresight_enable_source(csdev, NULL, mode, path); if (ret) return ret; } From ac8eac9062eefc33f5e97e526eef69bc98ecbdbc Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:26 +0100 Subject: [PATCH 1053/5214] coresight: Save active path for system tracers This commit only set the path pointer for system tracers (e.g. STM) in coresight_{enable|disable}_source(). Later changes will set the path pointer locally for per-CPU sources. This is because the mode and path pointer must be set together, so that they are observed atomically by the CPU PM notifier. Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-19-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 23 +++++++++++++++++++- include/linux/coresight.h | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f833b73ebc16..7f6febd20faa 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -487,10 +487,28 @@ int coresight_enable_source(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, struct coresight_path *path) { + int ret; + if (!coresight_is_device_source(csdev)) return -EINVAL; - return source_ops(csdev)->enable(csdev, event, mode, path); + ret = source_ops(csdev)->enable(csdev, event, mode, path); + if (ret) + return ret; + + /* + * Update the path pointer until after the source is enabled to avoid + * races where multiple paths attempt to enable the same source. + * + * Do not set the path pointer here for per-CPU sources; set it locally + * on the CPU instead. Otherwise, there is a window where the path is + * enabled but the pointer is not yet set, causing CPU PM notifiers to + * miss PM operations due to reading a NULL pointer. + */ + if (!coresight_is_percpu_source(csdev)) + csdev->path = path; + + return 0; } void coresight_disable_source(struct coresight_device *csdev, void *data) @@ -498,6 +516,9 @@ void coresight_disable_source(struct coresight_device *csdev, void *data) if (!coresight_is_device_source(csdev)) return; + if (!coresight_is_percpu_source(csdev)) + csdev->path = NULL; + source_ops(csdev)->disable(csdev, data); } EXPORT_SYMBOL_GPL(coresight_disable_source); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 5f9d7ea9f594..58d474b26980 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -257,6 +257,7 @@ struct coresight_trace_id_map { * by @coresight_ops. * @access: Device i/o access abstraction for this device. * @dev: The device entity associated to this component. + * @path: Activated path pointer (only used for source device). * @mode: The device mode, i.e sysFS, Perf or disabled. This is actually * an 'enum cs_mode' but stored in an atomic type. Access is always * through atomic APIs, ensuring SMP-safe synchronisation between @@ -291,6 +292,7 @@ struct coresight_device { const struct coresight_ops *ops; struct csdev_access access; struct device dev; + struct coresight_path *path; atomic_t mode; int refcnt; int cpu; From b6d54a94dcf42d405ed6de502c42572b2a462445 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:27 +0100 Subject: [PATCH 1054/5214] coresight: etm4x: Set active path on target CPU Set the path pointer on the target CPU during ETM enable and disable. This ensures the device mode and path pointer are updated together and observed atomically by the CPU PM notifier. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-20-f88c4a3ecfe9@arm.com --- .../hwtracing/coresight/coresight-etm4x-core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index fef1270439e9..343ba9ce946a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -238,6 +238,7 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata) struct etm4_enable_arg { struct etmv4_drvdata *drvdata; + struct coresight_path *path; int rc; }; @@ -625,8 +626,12 @@ static void etm4_enable_sysfs_smp_call(void *info) arg->rc = etm4_enable_hw(arg->drvdata); /* The tracer didn't start */ - if (arg->rc) + if (arg->rc) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return; + } + + csdev->path = arg->path; } /* @@ -894,9 +899,13 @@ static int etm4_enable_perf(struct coresight_device *csdev, out: /* Failed to start tracer; roll back to DISABLED mode */ - if (ret) + if (ret) { coresight_set_mode(csdev, CS_MODE_DISABLED); - return ret; + return ret; + } + + csdev->path = path; + return 0; } static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -926,6 +935,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa * ensures that register writes occur when cpu is powered. */ arg.drvdata = drvdata; + arg.path = path; ret = smp_call_function_single(drvdata->cpu, etm4_enable_sysfs_smp_call, &arg, 1); if (!ret) @@ -1067,6 +1077,7 @@ static void etm4_disable_sysfs_smp_call(void *info) etm4_disable_hw(drvdata); + drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); } @@ -1096,6 +1107,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, /* TRCVICTLR::SSSTATUS, bit[9] */ filters->ssstatus = (control & BIT(9)); + drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); /* From bc9907750718cdaad85870c93f2c72db5c85e345 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:28 +0100 Subject: [PATCH 1055/5214] coresight: etm3x: Set active path on target CPU Set the path pointer on the target CPU during ETM enable and disable. This ensures the device mode and path pointer are updated together and observed atomically by the CPU PM notifier. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-21-f88c4a3ecfe9@arm.com --- .../hwtracing/coresight/coresight-etm3x-core.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index aeeb284abdbe..c6fe8b25b855 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -441,6 +441,7 @@ static int etm_enable_hw(struct etm_drvdata *drvdata) struct etm_enable_arg { struct etm_drvdata *drvdata; + struct coresight_path *path; int rc; }; @@ -462,8 +463,12 @@ static void etm_enable_sysfs_smp_call(void *info) arg->rc = etm_enable_hw(arg->drvdata); /* The tracer didn't start */ - if (arg->rc) + if (arg->rc) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return; + } + + csdev->path = arg->path; } void etm_release_trace_id(struct etm_drvdata *drvdata) @@ -492,10 +497,13 @@ static int etm_enable_perf(struct coresight_device *csdev, ret = etm_enable_hw(drvdata); /* Failed to start tracer; roll back to DISABLED mode */ - if (ret) + if (ret) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return ret; + } - return ret; + csdev->path = path; + return 0; } static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -514,6 +522,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_pat */ if (cpu_online(drvdata->cpu)) { arg.drvdata = drvdata; + arg.path = path; ret = smp_call_function_single(drvdata->cpu, etm_enable_sysfs_smp_call, &arg, 1); if (!ret) @@ -583,6 +592,7 @@ static void etm_disable_sysfs_smp_call(void *info) etm_disable_hw(drvdata); + drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); } @@ -607,6 +617,7 @@ static void etm_disable_perf(struct coresight_device *csdev) CS_LOCK(drvdata->csa.base); + drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); /* From a2e91258e8647f729eed80e13dc2423aa5fb7417 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:29 +0100 Subject: [PATCH 1056/5214] coresight: sysfs: Use source's path pointer for path control Since the path pointer is stored in the source's structure, retrieve it directly when disabling the path. As a result, the global variables used for caching path pointers are no longer needed. Remove them to simplify the code. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-22-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 82 ++----------------- 1 file changed, 9 insertions(+), 73 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 9ec112b457bb..21196ee1d2bd 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -5,26 +5,12 @@ */ #include -#include #include #include #include "coresight-priv.h" #include "coresight-trace-id.h" -/* - * Use IDR to map the hash of the source's device name - * to the pointer of path for the source. The idr is for - * the sources which aren't associated with CPU. - */ -static DEFINE_IDR(path_idr); - -/* - * When operating Coresight drivers from the sysFS interface, only a single - * path can exist from a tracer (associated to a CPU) to a sink. - */ -static DEFINE_PER_CPU(struct coresight_path *, tracer_path); - ssize_t coresight_simple_show_pair(struct device *_dev, struct device_attribute *attr, char *buf) { @@ -167,11 +153,10 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev, int coresight_enable_sysfs(struct coresight_device *csdev) { - int cpu, ret = 0; + int ret = 0; struct coresight_device *sink; struct coresight_path *path; enum coresight_dev_subtype_source subtype; - u32 hash; subtype = csdev->subtype.source_subtype; @@ -223,37 +208,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev) if (ret) goto err_source; - switch (subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - /* - * When working from sysFS it is important to keep track - * of the paths that were created so that they can be - * undone in 'coresight_disable()'. Since there can only - * be a single session per tracer (when working from sysFS) - * a per-cpu variable will do just fine. - */ - cpu = csdev->cpu; - per_cpu(tracer_path, cpu) = path; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - /* - * Use the hash of source's device name as ID - * and map the ID to the pointer of the path. - */ - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) { - coresight_disable_source_sysfs(csdev, NULL); - goto err_source; - } - break; - default: - /* We can't be here */ - break; - } - out: mutex_unlock(&coresight_mutex); return ret; @@ -269,9 +223,8 @@ EXPORT_SYMBOL_GPL(coresight_enable_sysfs); void coresight_disable_sysfs(struct coresight_device *csdev) { - int cpu, ret; - struct coresight_path *path = NULL; - u32 hash; + struct coresight_path *path; + int ret; mutex_lock(&coresight_mutex); @@ -279,32 +232,15 @@ void coresight_disable_sysfs(struct coresight_device *csdev) if (ret) goto out; + /* + * coresight_disable_source_sysfs() clears the 'csdev->path' pointer + * when disabling the source. Retrieve the path pointer here. + */ + path = csdev->path; + if (!coresight_disable_source_sysfs(csdev, NULL)) goto out; - switch (csdev->subtype.source_subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - cpu = csdev->cpu; - path = per_cpu(tracer_path, cpu); - per_cpu(tracer_path, cpu) = NULL; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - /* Find the path by the hash. */ - path = idr_find(&path_idr, hash); - if (path == NULL) { - pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); - goto out; - } - idr_remove(&path_idr, hash); - break; - default: - /* We can't be here */ - break; - } - coresight_disable_path(path); coresight_release_path(path); From 39c40892f1a45908fbc57c334ac503da0c80d77c Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:30 +0100 Subject: [PATCH 1057/5214] coresight: Control path during CPU idle Extend the CPU PM flow to control the path: disable from source up to the node before the sink, then re-enable the same range on restore. To avoid latency, control it up to the node before the sink. Track per-CPU PM restore failures using percpu_pm_failed. Once a CPU hits a restore failure, set the percpu_pm_failed and return NOTIFY_BAD on subsequent notifications to avoid repeating half-completed transitions. Setting percpu_pm_failed permanently blocks CPU PM on that CPU. Such failures are typically seen during development; disabling PM operations simplifies the implementation, and a warning highlights the issue. Clear this flag when the source device is unregistered. Reviewed-by: James Clark Tested-by: Jie Gan Reviewed-by: Yeoreum Yun Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-23-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 93 ++++++++++++++++---- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 7f6febd20faa..fc60b72a4abf 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -38,6 +38,7 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); static DEFINE_RAW_SPINLOCK(coresight_dev_lock); static DEFINE_PER_CPU(struct coresight_device *, csdev_source); +static DEFINE_PER_CPU(bool, percpu_pm_failed); /** * struct coresight_node - elements of a path, from source to sink @@ -121,6 +122,9 @@ static void coresight_clear_percpu_source(struct coresight_device *csdev) if (!csdev || !coresight_is_percpu_source(csdev)) return; + /* Clear percpu_pm_failed */ + per_cpu(percpu_pm_failed, csdev->cpu) = false; + guard(raw_spinlock_irqsave)(&coresight_dev_lock); /* The per-CPU pointer should contain the same csdev */ @@ -1843,7 +1847,7 @@ static void coresight_release_device_list(void) } } -static struct coresight_device *coresight_cpu_get_active_source(void) +static struct coresight_path *coresight_cpu_get_active_path(void) { struct coresight_device *source; bool is_active = false; @@ -1859,22 +1863,32 @@ static struct coresight_device *coresight_cpu_get_active_source(void) /* * It is expected to run in atomic context, so it cannot be preempted - * to disable the source. Here returns the active source pointer - * without concern that its state may change. Since the build path has - * taken a reference on the component, the source can be safely used - * by the caller. + * to disable the path. Here returns the active path pointer without + * concern that its state may change. Since the build path has taken + * a reference on the component, the path can be safely used by the + * caller. */ - return is_active ? source : NULL; + return is_active ? source->path : NULL; } -static int coresight_pm_is_needed(struct coresight_device *csdev) +/* Return: 1 if PM is required, 0 if skip, or a negative error */ +static int coresight_pm_is_needed(struct coresight_path *path) { - if (!csdev) + struct coresight_device *source; + + if (this_cpu_read(percpu_pm_failed)) + return -EIO; + + if (!path) + return 0; + + source = coresight_get_source(path); + if (!source) return 0; /* pm_save_disable() and pm_restore_enable() must be paired */ - if (coresight_ops(csdev)->pm_save_disable && - coresight_ops(csdev)->pm_restore_enable) + if (coresight_ops(source)->pm_save_disable && + coresight_ops(source)->pm_restore_enable) return 1; return 0; @@ -1890,22 +1904,71 @@ static void coresight_pm_device_restore(struct coresight_device *csdev) coresight_ops(csdev)->pm_restore_enable(csdev); } +static int coresight_pm_save(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + struct coresight_node *from, *to; + int ret; + + ret = coresight_pm_device_save(source); + if (ret) + return ret; + + from = coresight_path_first_node(path); + /* Disable up to the node before sink */ + to = list_prev_entry(coresight_path_last_node(path), link); + coresight_disable_path_from_to(path, from, to); + + return 0; +} + +static void coresight_pm_restore(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + struct coresight_node *from, *to; + int ret; + + from = coresight_path_first_node(path); + /* Enable up to the node before sink */ + to = list_prev_entry(coresight_path_last_node(path), link); + ret = coresight_enable_path_from_to(path, coresight_get_mode(source), + from, to); + if (ret) + goto path_failed; + + coresight_pm_device_restore(source); + return; + +path_failed: + pr_err("Failed in coresight PM restore on CPU%d: %d\n", + smp_processor_id(), ret); + + /* + * Once PM fails on a CPU, set percpu_pm_failed and leave it set until + * reboot. This prevents repeated partial transitions during idle + * entry and exit. + */ + this_cpu_write(percpu_pm_failed, true); +} + static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { - struct coresight_device *csdev = coresight_cpu_get_active_source(); + struct coresight_path *path = coresight_cpu_get_active_path(); + int ret; - if (!coresight_pm_is_needed(csdev)) - return NOTIFY_DONE; + ret = coresight_pm_is_needed(path); + if (ret <= 0) + return ret ? NOTIFY_BAD : NOTIFY_DONE; switch (cmd) { case CPU_PM_ENTER: - if (coresight_pm_device_save(csdev)) + if (coresight_pm_save(path)) return NOTIFY_BAD; break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED: - coresight_pm_device_restore(csdev); + coresight_pm_restore(path); break; default: return NOTIFY_DONE; From 7bbe5a172376d1bbf5da4a68fee6d77ae5a03e55 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:31 +0100 Subject: [PATCH 1058/5214] coresight: Add PM callbacks for sink device Unlike system level sinks, per-CPU sinks may lose power during CPU idle states. Currently, this applies specifically to TRBE. This commit invokes save and restore callbacks for the sink in the CPU PM notifier. If the sink provides PM callbacks but the source does not, this is unsafe because the sink cannot be disabled safely unless the source can also be controlled, so veto low power entry to avoid lockups. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-24-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 35 ++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index fc60b72a4abf..e3de4b388372 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1874,7 +1874,7 @@ static struct coresight_path *coresight_cpu_get_active_path(void) /* Return: 1 if PM is required, 0 if skip, or a negative error */ static int coresight_pm_is_needed(struct coresight_path *path) { - struct coresight_device *source; + struct coresight_device *source, *sink; if (this_cpu_read(percpu_pm_failed)) return -EIO; @@ -1883,7 +1883,8 @@ static int coresight_pm_is_needed(struct coresight_path *path) return 0; source = coresight_get_source(path); - if (!source) + sink = coresight_get_sink(path); + if (!source || !sink) return 0; /* pm_save_disable() and pm_restore_enable() must be paired */ @@ -1891,16 +1892,35 @@ static int coresight_pm_is_needed(struct coresight_path *path) coresight_ops(source)->pm_restore_enable) return 1; + /* + * It is not permitted that the source has no callbacks while the sink + * does, as the sink cannot be disabled without disabling the source, + * which may lead to lockups. Fix this by enabling self-hosted PM + * mode for ETM (see etm4_probe()). + */ + if (coresight_ops(sink)->pm_save_disable && + coresight_ops(sink)->pm_restore_enable) { + pr_warn_once("coresight PM failed: source has no PM callbacks; " + "cannot safely control sink\n"); + return -EINVAL; + } + return 0; } static int coresight_pm_device_save(struct coresight_device *csdev) { + if (!csdev || !coresight_ops(csdev)->pm_save_disable) + return 0; + return coresight_ops(csdev)->pm_save_disable(csdev); } static void coresight_pm_device_restore(struct coresight_device *csdev) { + if (!csdev || !coresight_ops(csdev)->pm_restore_enable) + return; + coresight_ops(csdev)->pm_restore_enable(csdev); } @@ -1919,15 +1939,24 @@ static int coresight_pm_save(struct coresight_path *path) to = list_prev_entry(coresight_path_last_node(path), link); coresight_disable_path_from_to(path, from, to); + /* + * Save the sink. Most sinks do not implement a save callback to avoid + * latency from memory copying. We assume the sink's save and restore + * always succeed. + */ + coresight_pm_device_save(coresight_get_sink(path)); return 0; } static void coresight_pm_restore(struct coresight_path *path) { struct coresight_device *source = coresight_get_source(path); + struct coresight_device *sink = coresight_get_sink(path); struct coresight_node *from, *to; int ret; + coresight_pm_device_restore(sink); + from = coresight_path_first_node(path); /* Enable up to the node before sink */ to = list_prev_entry(coresight_path_last_node(path), link); @@ -1940,6 +1969,8 @@ static void coresight_pm_restore(struct coresight_path *path) return; path_failed: + coresight_pm_device_save(sink); + pr_err("Failed in coresight PM restore on CPU%d: %d\n", smp_processor_id(), ret); From da06d6eb523bdd20d063395d6cf7f4c873d338e8 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Fri, 15 May 2026 21:08:32 +0100 Subject: [PATCH 1059/5214] coresight: trbe: Save and restore state across CPU low power state TRBE context can be lost when a CPU enters low power states. If a trace source is restored while TRBE is not, tracing may run without an active sink, which can lead to hangs on some devices (e.g., Pixel 9). The save and restore flows are described in the section K5.5 "Context switching" of Arm ARM (ARM DDI 0487 L.a). This commit adds save and restore callbacks with following the software usages defined in the architecture manual. During the restore flow, since TRBLIMITR_EL1.E resets to 0 on a warm reset, the trace buffer unit is disabled when idle resume, it is safe to restore base/pointer/status registers first and program TRBLIMITR_EL1 last. Signed-off-by: Yabin Cui Co-developed-by: Leo Yan Signed-off-by: Leo Yan Reviewed-by: James Clark Tested-by: James Clark Reviewed-by: Yeoreum Yun Tested-by: Jie Gan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-25-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-trbe.c | 59 +++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 14e35b9660d7..c7cbca45f2de 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = { */ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256 +/* + * struct trbe_save_state: Register values representing TRBE state + * @trblimitr - Trace Buffer Limit Address Register value + * @trbbaser - Trace Buffer Base Register value + * @trbptr - Trace Buffer Write Pointer Register value + * @trbsr - Trace Buffer Status Register value + */ +struct trbe_save_state { + u64 trblimitr; + u64 trbbaser; + u64 trbptr; + u64 trbsr; +}; + /* * struct trbe_cpudata: TRBE instance specific data * @trbe_flag - TRBE dirty/access flag support @@ -134,6 +148,7 @@ struct trbe_cpudata { enum cs_mode mode; struct trbe_buf *buf; struct trbe_drvdata *drvdata; + struct trbe_save_state save_state; DECLARE_BITMAP(errata, TRBE_ERRATA_MAX); }; @@ -1189,6 +1204,46 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) return IRQ_HANDLED; } +static int arm_trbe_save(struct coresight_device *csdev) +{ + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev); + struct trbe_save_state *state = &cpudata->save_state; + + state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); + + /* Disable the unit, ensure the writes to memory are complete */ + if (state->trblimitr & TRBLIMITR_EL1_E) + trbe_drain_and_disable_local(cpudata); + + state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1); + state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1); + state->trbsr = read_sysreg_s(SYS_TRBSR_EL1); + return 0; +} + +static void arm_trbe_restore(struct coresight_device *csdev) +{ + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev); + struct trbe_save_state *state = &cpudata->save_state; + + write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1); + write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1); + write_sysreg_s(state->trbsr, SYS_TRBSR_EL1); + + if (!(state->trblimitr & TRBLIMITR_EL1_E)) { + write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1); + } else { + /* + * The section K5.5 Context switching, Arm ARM (ARM DDI 0487 + * L.a), S_PKLXF requires a Context synchronization event to + * guarantee the Trace Buffer Unit will observe the new values + * of the system registers. + */ + isb(); + set_trbe_enabled(cpudata, state->trblimitr); + } +} + static const struct coresight_ops_sink arm_trbe_sink_ops = { .enable = arm_trbe_enable, .disable = arm_trbe_disable, @@ -1198,7 +1253,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = { }; static const struct coresight_ops arm_trbe_cs_ops = { - .sink_ops = &arm_trbe_sink_ops, + .pm_save_disable = arm_trbe_save, + .pm_restore_enable = arm_trbe_restore, + .sink_ops = &arm_trbe_sink_ops, }; static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf) From bf64b06ede93a8b56bf4dce0809e4edd111ccf36 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:33 +0100 Subject: [PATCH 1060/5214] coresight: sysfs: Increment refcount only for software source Except for software sources (e.g. STM), other sources treat multiple enables as equivalent to a single enable. The device mode already tracks the binary state, so it is redundant to operate refcount. Introduce a helper coresight_is_software_source() for check software source. Refactor to maintain the refcount only for software sources. This simplifies future CPU PM handling without refcount logic. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-26-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 39 ++++++++++++------- include/linux/coresight.h | 6 +++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 21196ee1d2bd..80b6b634a70d 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -39,6 +39,26 @@ ssize_t coresight_simple_show32(struct device *_dev, } EXPORT_SYMBOL_GPL(coresight_simple_show32); +static void coresight_source_get_refcnt(struct coresight_device *csdev) +{ + /* + * There could be multiple applications driving the software + * source. So keep the refcount for each such user when the + * source is already enabled. + * + * No need to increment the reference counter for other source + * types, as multiple enables are the same as a single enable. + */ + if (coresight_is_software_source(csdev)) + csdev->refcnt++; +} + +static void coresight_source_put_refcnt(struct coresight_device *csdev) +{ + if (coresight_is_software_source(csdev)) + csdev->refcnt--; +} + static int coresight_enable_source_sysfs(struct coresight_device *csdev, enum cs_mode mode, struct coresight_path *path) @@ -57,14 +77,14 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, return ret; } - csdev->refcnt++; + coresight_source_get_refcnt(csdev); return 0; } /** - * coresight_disable_source_sysfs - Drop the reference count by 1 and disable - * the device if there are no users left. + * coresight_disable_source_sysfs - Drop the reference count by 1 for software + * sources. Disable the device if there are no users left. * * @csdev: The coresight device to disable * @data: Opaque data to pass on to the disable function of the source device. @@ -79,7 +99,7 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev, if (coresight_get_mode(csdev) != CS_MODE_SYSFS) return false; - csdev->refcnt--; + coresight_source_put_refcnt(csdev); if (csdev->refcnt == 0) { coresight_disable_source(csdev, data); return true; @@ -156,9 +176,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev) int ret = 0; struct coresight_device *sink; struct coresight_path *path; - enum coresight_dev_subtype_source subtype; - - subtype = csdev->subtype.source_subtype; mutex_lock(&coresight_mutex); @@ -173,13 +190,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) * doesn't hold coresight_mutex. */ if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { - /* - * There could be multiple applications driving the software - * source. So keep the refcount for each such user when the - * source is already enabled. - */ - if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) - csdev->refcnt++; + coresight_source_get_refcnt(csdev); goto out; } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 58d474b26980..76ef4c096512 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -611,6 +611,12 @@ static inline bool coresight_is_percpu_source(struct coresight_device *csdev) (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_PROC); } +static inline bool coresight_is_software_source(struct coresight_device *csdev) +{ + return csdev && coresight_is_device_source(csdev) && + (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE); +} + static inline bool coresight_is_percpu_sink(struct coresight_device *csdev) { return csdev && (csdev->type == CORESIGHT_DEV_TYPE_SINK) && From 7105d2aa76d81d5133b2819a32c62c6dfbfbe12f Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:34 +0100 Subject: [PATCH 1061/5214] coresight: Move CPU hotplug callbacks to core layer This commit moves CPU hotplug callbacks from ETMv4 driver to core layer. The motivation is the core layer can control all components on an activated path rather but not only managing tracer in ETMv4 driver. The perf event layer will disable CoreSight PMU event 'cs_etm' when hotplug off a CPU. That means a perf mode will be always converted to disabled mode in CPU hotplug. Arm CoreSight CPU hotplug callbacks only need to handle the Sysfs mode and ignore the perf mode. Add a 'mode' argument to coresight_pm_get_active_path() so it only returns active paths for the relevant mode. Define the enum with bit flags so it is safe for bitwise operations. Change CPUHP_AP_ARM_CORESIGHT_STARTING to CPUHP_AP_ARM_CORESIGHT_ONLINE so that the CPU hotplug callback runs in the online state and thread context, allowing coresight_disable_sysfs() to be called directly to disable the path. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-27-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 48 +++++++++++++++---- .../coresight/coresight-etm3x-core.c | 40 ---------------- .../coresight/coresight-etm4x-core.c | 37 -------------- include/linux/coresight.h | 6 +-- include/linux/cpuhotplug.h | 2 +- 5 files changed, 43 insertions(+), 90 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index e3de4b388372..f7b1308a759c 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1847,7 +1847,7 @@ static void coresight_release_device_list(void) } } -static struct coresight_path *coresight_cpu_get_active_path(void) +static struct coresight_path *coresight_cpu_get_active_path(enum cs_mode mode) { struct coresight_device *source; bool is_active = false; @@ -1856,17 +1856,17 @@ static struct coresight_path *coresight_cpu_get_active_path(void) if (!source) return NULL; - if (coresight_get_mode(source) != CS_MODE_DISABLED) + if (coresight_get_mode(source) & mode) is_active = true; coresight_put_percpu_source_ref(source); /* - * It is expected to run in atomic context, so it cannot be preempted - * to disable the path. Here returns the active path pointer without - * concern that its state may change. Since the build path has taken - * a reference on the component, the path can be safely used by the - * caller. + * It is expected to run in atomic context or with the CPU lock held for + * sysfs mode, so it cannot be preempted to disable the path. Here + * returns the active path pointer without concern that its state may + * change. Since the build path has taken a reference on the component, + * the path can be safely used by the caller. */ return is_active ? source->path : NULL; } @@ -1985,7 +1985,8 @@ static void coresight_pm_restore(struct coresight_path *path) static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { - struct coresight_path *path = coresight_cpu_get_active_path(); + struct coresight_path *path = + coresight_cpu_get_active_path(CS_MODE_SYSFS | CS_MODE_PERF); int ret; ret = coresight_pm_is_needed(path); @@ -2012,13 +2013,42 @@ static struct notifier_block coresight_cpu_pm_nb = { .notifier_call = coresight_cpu_pm_notify, }; +static int coresight_dying_cpu(unsigned int cpu) +{ + struct coresight_path *path; + + /* + * The perf event layer will disable PMU events in the CPU + * hotplug. Here only handles SYSFS case. + */ + path = coresight_cpu_get_active_path(CS_MODE_SYSFS); + if (!path) + return 0; + + coresight_disable_sysfs(coresight_get_source(path)); + return 0; +} + static int __init coresight_pm_setup(void) { - return cpu_pm_register_notifier(&coresight_cpu_pm_nb); + int ret; + + ret = cpu_pm_register_notifier(&coresight_cpu_pm_nb); + if (ret) + return ret; + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE, + "arm/coresight-core:dying", + NULL, coresight_dying_cpu); + if (ret) + cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); + + return ret; } static void coresight_pm_cleanup(void) { + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE); cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); } diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index c6fe8b25b855..862ad0786699 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -699,35 +699,6 @@ static int etm_online_cpu(unsigned int cpu) return 0; } -static int etm_starting_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) { - etm_os_unlock(etmdrvdata[cpu]); - etmdrvdata[cpu]->os_unlock = true; - } - - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm_enable_hw(etmdrvdata[cpu]); - spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - -static int etm_dying_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - spin_lock(&etmdrvdata[cpu]->spinlock); - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm_disable_hw(etmdrvdata[cpu]); - spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - static bool etm_arch_supported(u8 arch) { switch (arch) { @@ -795,13 +766,6 @@ static int __init etm_hp_setup(void) { int ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, - "arm/coresight:starting", - etm_starting_cpu, etm_dying_cpu); - - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight:online", etm_online_cpu, NULL); @@ -812,15 +776,11 @@ static int __init etm_hp_setup(void) return 0; } - /* failed dyn state - remove others */ - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); - return ret; } static void etm_hp_clear(void) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online); hp_online = 0; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 343ba9ce946a..14bb31bd6a0b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1833,33 +1833,6 @@ static int etm4_online_cpu(unsigned int cpu) return 0; } -static int etm4_starting_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - raw_spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) - etm4_os_unlock(etmdrvdata[cpu]); - - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm4_enable_hw(etmdrvdata[cpu]); - raw_spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - -static int etm4_dying_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - raw_spin_lock(&etmdrvdata[cpu]->spinlock); - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm4_disable_hw(etmdrvdata[cpu]); - raw_spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata) { return !!drvdata->save_state; @@ -2120,13 +2093,6 @@ static int __init etm4_pm_setup(void) { int ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, - "arm/coresight4:starting", - etm4_starting_cpu, etm4_dying_cpu); - - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight4:online", etm4_online_cpu, NULL); @@ -2137,14 +2103,11 @@ static int __init etm4_pm_setup(void) return 0; } - /* failed dyn state - remove others */ - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); return ret; } static void etm4_pm_clear(void) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online); hp_online = 0; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 76ef4c096512..add0579cad88 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -344,9 +344,9 @@ struct coresight_path { }; enum cs_mode { - CS_MODE_DISABLED, - CS_MODE_SYSFS, - CS_MODE_PERF, + CS_MODE_DISABLED = 0, + CS_MODE_SYSFS = BIT(0), + CS_MODE_PERF = BIT(1), }; #define coresight_ops(csdev) csdev->ops diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 22ba327ec227..0fb3a2a62eb0 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -180,7 +180,6 @@ enum cpuhp_state { CPUHP_AP_DUMMY_TIMER_STARTING, CPUHP_AP_ARM_XEN_STARTING, CPUHP_AP_ARM_XEN_RUNSTATE_STARTING, - CPUHP_AP_ARM_CORESIGHT_STARTING, CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, CPUHP_AP_ARM64_ISNDEP_STARTING, CPUHP_AP_SMPCFD_DYING, @@ -200,6 +199,7 @@ enum cpuhp_state { CPUHP_AP_IRQ_AFFINITY_ONLINE, CPUHP_AP_BLK_MQ_ONLINE, CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS, + CPUHP_AP_ARM_CORESIGHT_ONLINE, CPUHP_AP_X86_INTEL_EPB_ONLINE, CPUHP_AP_PERF_ONLINE, CPUHP_AP_PERF_X86_ONLINE, From a5dd853fb7774c9543aed272a8614c15ebce3173 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 15 May 2026 21:08:35 +0100 Subject: [PATCH 1062/5214] coresight: sysfs: Validate CPU online status for per-CPU sources The current SysFS flow first enables the links and sink, then rolls back to disable them if the source fails to enable. This failure can occur if the associated CPU is offline, which causes the SMP call to fail. Validate whether the associated CPU is online for a per-CPU tracer. If the CPU is offline, return -ENODEV and bail out. Tested-by: James Clark Reviewed-by: Yeoreum Yun Reviewed-by: James Clark Tested-by: Jie Gan Reviewed-by: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-28-f88c4a3ecfe9@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 80b6b634a70d..4b010f8bc4c0 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -168,6 +168,9 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev, return -EINVAL; } + if (coresight_is_percpu_source(csdev) && !cpu_online(csdev->cpu)) + return -ENODEV; + return 0; } From 61800a80288b5ec108660ebec74ba52a68a2c9f3 Mon Sep 17 00:00:00 2001 From: Manish Baing Date: Tue, 12 May 2026 18:02:25 +0000 Subject: [PATCH 1063/5214] dt-bindings: fpga: altr,a10-pr-ip: convert to DT schema Convert the Altera Arria 10 Partial Reconfiguration IP bindings from text format to YAML schema. Signed-off-by: Manish Baing Reviewed-by: Krzysztof Kozlowski Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20260512180225.65902-1-manishbaing2789@gmail.com Signed-off-by: Xu Yilun --- .../devicetree/bindings/fpga/altera-pr-ip.txt | 12 ------- .../bindings/fpga/altr,a10-pr-ip.yaml | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) delete mode 100644 Documentation/devicetree/bindings/fpga/altera-pr-ip.txt create mode 100644 Documentation/devicetree/bindings/fpga/altr,a10-pr-ip.yaml diff --git a/Documentation/devicetree/bindings/fpga/altera-pr-ip.txt b/Documentation/devicetree/bindings/fpga/altera-pr-ip.txt deleted file mode 100644 index 52a294cf2730..000000000000 --- a/Documentation/devicetree/bindings/fpga/altera-pr-ip.txt +++ /dev/null @@ -1,12 +0,0 @@ -Altera Arria10 Partial Reconfiguration IP - -Required properties: -- compatible : should contain "altr,a10-pr-ip" -- reg : base address and size for memory mapped io. - -Example: - - fpga_mgr: fpga-mgr@ff20c000 { - compatible = "altr,a10-pr-ip"; - reg = <0xff20c000 0x10>; - }; diff --git a/Documentation/devicetree/bindings/fpga/altr,a10-pr-ip.yaml b/Documentation/devicetree/bindings/fpga/altr,a10-pr-ip.yaml new file mode 100644 index 000000000000..1f4df40308bd --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altr,a10-pr-ip.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/fpga/altr,a10-pr-ip.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera Arria10 Partial Reconfiguration IP + +maintainers: + - Matthew Gerlach + +description: + The Altera Arria 10 Partial Reconfiguration IP core allows the host + processor to perform partial reconfiguration of the FPGA fabric. + +properties: + compatible: + const: altr,a10-pr-ip + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + fpga-mgr@ff20c000 { + compatible = "altr,a10-pr-ip"; + reg = <0xff20c000 0x10>; + }; From 3c310d945b9490b3417eba7d58d7c7fbb3156081 Mon Sep 17 00:00:00 2001 From: Manish Baing Date: Tue, 12 May 2026 18:20:33 +0000 Subject: [PATCH 1064/5214] dt-bindings: fpga: altr,socfpga-fpga-mgr: convert to DT schema Convert the Altera SoCFPGA FPGA Manager bindings from text format to YAML schema. Signed-off-by: Manish Baing Reviewed-by: Rob Herring (Arm) Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20260512182033.66222-1-manishbaing2789@gmail.com Signed-off-by: Xu Yilun --- .../bindings/fpga/altera-socfpga-fpga-mgr.txt | 17 --------- .../bindings/fpga/altr,socfpga-fpga-mgr.yaml | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+), 17 deletions(-) delete mode 100644 Documentation/devicetree/bindings/fpga/altera-socfpga-fpga-mgr.txt create mode 100644 Documentation/devicetree/bindings/fpga/altr,socfpga-fpga-mgr.yaml diff --git a/Documentation/devicetree/bindings/fpga/altera-socfpga-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/altera-socfpga-fpga-mgr.txt deleted file mode 100644 index d52f3340414d..000000000000 --- a/Documentation/devicetree/bindings/fpga/altera-socfpga-fpga-mgr.txt +++ /dev/null @@ -1,17 +0,0 @@ -Altera SOCFPGA FPGA Manager - -Required properties: -- compatible : should contain "altr,socfpga-fpga-mgr" -- reg : base address and size for memory mapped io. - - The first index is for FPGA manager register access. - - The second index is for writing FPGA configuration data. -- interrupts : interrupt for the FPGA Manager device. - -Example: - - hps_0_fpgamgr: fpgamgr@ff706000 { - compatible = "altr,socfpga-fpga-mgr"; - reg = <0xFF706000 0x1000 - 0xFFB90000 0x1000>; - interrupts = <0 175 4>; - }; diff --git a/Documentation/devicetree/bindings/fpga/altr,socfpga-fpga-mgr.yaml b/Documentation/devicetree/bindings/fpga/altr,socfpga-fpga-mgr.yaml new file mode 100644 index 000000000000..9bcc1200d61d --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altr,socfpga-fpga-mgr.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/fpga/altr,socfpga-fpga-mgr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera SOCFPGA FPGA Manager + +maintainers: + - Steffen Trumtrar + +properties: + compatible: + const: altr,socfpga-fpga-mgr + + reg: + items: + - description: FPGA manager register access + - description: Writing FPGA configuration data + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + fpgamgr@ff706000 { + compatible = "altr,socfpga-fpga-mgr"; + reg = <0xff706000 0x1000>, + <0xffb90000 0x1000>; + interrupts = <0 175 4>; + }; From 22fc8822d14663cad66f2852e7a14f442e1d3ab5 Mon Sep 17 00:00:00 2001 From: Jian Yang Date: Mon, 13 Apr 2026 15:13:55 +0800 Subject: [PATCH 1065/5214] PCI: mediatek-gen3: Fix PERST# control timing during system startup Some MediaTek chips stop generating REFCLK if the PCIE_PHY_RSTB signal of PCIe controller is asserted at the start of mtk_pcie_devices_power_up(). But the driver deasserts PCIE_PHY_RSTB together with PCIE_PE_RSTB signal that is used to deassert PERST#. This violates PCIe CEM r6.0, sec 2.11.2, which mandates waiting for 100ms (PCIE_T_PVPERL_MS) after power becomes stable. Move the MAC, PHY and BRG reset deassert code above the PCIE_T_PVPERL_MS delay and leave the PCIE_PE_RSTB deassertion after the delay. Add the 10ms delay mentioned in the MediaTek datasheet after asserting PCIE_BRG_RSTB and before accessing the PCIE_RST_CTRL_REG register. Signed-off-by: Jian Yang [mani: commit log and comments rewording] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260413071401.1151-2-jian.yang@mediatek.com --- drivers/pci/controller/pcie-mediatek-gen3.c | 26 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b0accd828589..01badc4f9311 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -63,6 +63,12 @@ #define PCIE_BRG_RSTB BIT(2) #define PCIE_PE_RSTB BIT(3) +/* + * As described in the datasheet of MediaTek PCIe Gen3 controller, wait 10ms + * after setting PCIE_BRG_RSTB, and before accessing PCIe internal registers. + */ +#define PCIE_BRG_RST_RDY_MS 10 + #define PCIE_LTSSM_STATUS_REG 0x150 #define PCIE_LTSSM_STATE_MASK GENMASK(28, 24) #define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24) @@ -430,6 +436,21 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) return err; } + /* + * Some of MediaTek's chips won't output REFCLK when PCIE_PHY_RSTB is + * asserted, we have to de-assert MAC & PHY & BRG reset signals first + * to allow the REFCLK to be stable. While PCIE_BRG_RSTB is asserted, + * there is a short period during which the PCIe internal register + * cannot be accessed, so we need to wait 10ms here. + */ + msleep(PCIE_BRG_RST_RDY_MS); + + if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { + /* De-assert MAC, PHY and BRG reset signals */ + val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB); + writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + } + /* * Described in PCIe CEM specification revision 6.0. * @@ -439,9 +460,8 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) msleep(PCIE_T_PVPERL_MS); if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { - /* De-assert reset signals */ - val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | - PCIE_PE_RSTB); + /* De-assert PERST# signal */ + val &= ~PCIE_PE_RSTB; writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); } From 7a0e17e7a0816d532c0f02e8d64f603a4dc283ee Mon Sep 17 00:00:00 2001 From: Jian Yang Date: Mon, 13 Apr 2026 15:13:56 +0800 Subject: [PATCH 1066/5214] PCI: mediatek-gen3: Add a .shutdown() callback to control PERST# signal Add a .shutdown() callback to control the timing of PERST# and power during system shutdown to ensure that PERST# is asserted before power to the connector is removed, as required by PCIe CEM r6.0, sec 2.2. Signed-off-by: Jian Yang Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260413071401.1151-3-jian.yang@mediatek.com --- drivers/pci/controller/pcie-mediatek-gen3.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 01badc4f9311..8aec57626f5f 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1286,6 +1286,14 @@ static void mtk_pcie_remove(struct platform_device *pdev) mtk_pcie_irq_teardown(pcie); } +static void mtk_pcie_shutdown(struct platform_device *pdev) +{ + struct mtk_gen3_pcie *pcie = platform_get_drvdata(pdev); + + mtk_pcie_devices_power_down(pcie); + mtk_pcie_power_down(pcie); +} + static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie) { int i; @@ -1424,6 +1432,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, .remove = mtk_pcie_remove, + .shutdown = mtk_pcie_shutdown, .driver = { .name = "mtk-pcie-gen3", .of_match_table = mtk_pcie_of_match, From d39d55d7411c18ca6aeb63aafa8035f4ad8b317f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 5 May 2026 18:59:16 +0800 Subject: [PATCH 1067/5214] PCI: mediatek-gen3: Do full device power down on removal When power control for downstream devices was introduced in the mediatek-gen3 PCIe controller driver, only the power to the downstream devices was cut when the controller driver is removed. This matched existing behavior, but in hindsight a proper power down sequence should have been followed. Call mtk_pcie_devices_power_down() on driver removal so that in addition to removing power from the downstream devices, PERST# is asserted. Fixes: 1a152e21940a ("PCI: mediatek-gen3: Integrate new pwrctrl API") Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260505105918.1823170-1-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 8aec57626f5f..1da2166d1017 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1280,7 +1280,7 @@ static void mtk_pcie_remove(struct platform_device *pdev) pci_remove_root_bus(host->bus); pci_unlock_rescan_remove(); - pci_pwrctrl_power_off_devices(pcie->dev); + mtk_pcie_devices_power_down(pcie); mtk_pcie_power_down(pcie); pci_pwrctrl_destroy_devices(pcie->dev); mtk_pcie_irq_teardown(pcie); From e30d8b2730a33e5e8789371e947c3529789a6070 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 14 May 2026 17:39:51 +0100 Subject: [PATCH 1068/5214] mailbox: mpfs: fix check for syscon presence in mpfs_mbox_inbox_isr() mpfs_mbox_inbox_isr() writes to the sysreg scb syscon, not the control scb syscon, but checks for the presence of the latter. Ultimately this makes little difference because if one syscon is present, both will be. Fixes: a4123ffab9ece ("mailbox: mpfs: support new, syscon based, devicetree configuration") Signed-off-by: Conor Dooley Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index d5d9effece97..ef40fe2be30d 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -201,7 +201,7 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - if (mbox->control_scb) + if (mbox->sysreg_scb) regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); else writel_relaxed(0, mbox->int_reg); From 388f16c9372d15585b594998f34542ed00fddebf Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 08:52:35 +0800 Subject: [PATCH 1069/5214] dt-bindings: mailbox: qcom: Document Nord CPUCP mailbox controller Document CPUSS Control Processor (CPUCP) mailbox controller for Qualcomm Nord SoC, which is compatible with X1E80100 CPUCP, even though it supports more IPC channels. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml index 90bfde66cc4a..c8107d58f3d5 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml @@ -20,6 +20,7 @@ properties: - enum: - qcom,glymur-cpucp-mbox - qcom,kaanapali-cpucp-mbox + - qcom,nord-cpucp-mbox - qcom,sm8750-cpucp-mbox - const: qcom,x1e80100-cpucp-mbox - enum: From 0edda639ba13d9da37447586b5480582a2581e2b Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 08:52:36 +0800 Subject: [PATCH 1070/5214] mailbox: qcom-cpucp: Add support for Nord CPUCP mailbox controller The Nord SoC CPUCP mailbox supports 16 IPC channels, compared to 3 on x1e80100. The existing driver hardcodes the channel count via a compile-time constant (APSS_CPUCP_IPC_CHAN_SUPPORTED), making it impossible to support hardware with a different number of channels. Introduce a qcom_cpucp_mbox_data per-hardware configuration struct that carries the channel count, and retrieve it via of_device_get_match_data() at probe time. Switch the channel array from a fixed-size member to a dynamically allocated buffer sized from the hardware data. Update the x1e80100 entry to supply its own data struct, and add a new Nord entry with num_chans = 16. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Reviewed-by: Konrad Dybcio Signed-off-by: Jassi Brar --- drivers/mailbox/qcom-cpucp-mbox.c | 35 ++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/mailbox/qcom-cpucp-mbox.c b/drivers/mailbox/qcom-cpucp-mbox.c index 44f4ed15f818..862e45e8fbd5 100644 --- a/drivers/mailbox/qcom-cpucp-mbox.c +++ b/drivers/mailbox/qcom-cpucp-mbox.c @@ -12,7 +12,6 @@ #include #include -#define APSS_CPUCP_IPC_CHAN_SUPPORTED 3 #define APSS_CPUCP_MBOX_CMD_OFF 0x4 /* Tx Registers */ @@ -26,6 +25,14 @@ #define APSS_CPUCP_RX_MBOX_EN 0x4c00 #define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0) +/** + * struct qcom_cpucp_mbox_data - Per-hardware mailbox configuration data + * @num_chans: Number of IPC channels supported by this hardware + */ +struct qcom_cpucp_mbox_data { + int num_chans; +}; + /** * struct qcom_cpucp_mbox - Holder for the mailbox driver * @chans: The mailbox channel @@ -34,7 +41,7 @@ * @rx_base: Base address of the CPUCP rx registers */ struct qcom_cpucp_mbox { - struct mbox_chan chans[APSS_CPUCP_IPC_CHAN_SUPPORTED]; + struct mbox_chan *chans; struct mbox_controller mbox; void __iomem *tx_base; void __iomem *rx_base; @@ -53,7 +60,7 @@ static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data) status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT); - for_each_set_bit(i, (unsigned long *)&status, APSS_CPUCP_IPC_CHAN_SUPPORTED) { + for_each_set_bit(i, (unsigned long *)&status, cpucp->mbox.num_chans) { u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF); struct mbox_chan *chan = &cpucp->chans[i]; unsigned long flags; @@ -112,15 +119,24 @@ static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = { static int qcom_cpucp_mbox_probe(struct platform_device *pdev) { + const struct qcom_cpucp_mbox_data *data; struct device *dev = &pdev->dev; struct qcom_cpucp_mbox *cpucp; struct mbox_controller *mbox; int irq, ret; + data = of_device_get_match_data(dev); + if (!data) + return dev_err_probe(dev, -EINVAL, "No match data found\n"); + cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL); if (!cpucp) return -ENOMEM; + cpucp->chans = devm_kcalloc(dev, data->num_chans, sizeof(*cpucp->chans), GFP_KERNEL); + if (!cpucp->chans) + return -ENOMEM; + cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL); if (IS_ERR(cpucp->rx_base)) return PTR_ERR(cpucp->rx_base); @@ -146,7 +162,7 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev) mbox = &cpucp->mbox; mbox->dev = dev; - mbox->num_chans = APSS_CPUCP_IPC_CHAN_SUPPORTED; + mbox->num_chans = data->num_chans; mbox->chans = cpucp->chans; mbox->ops = &qcom_cpucp_mbox_chan_ops; @@ -157,8 +173,17 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev) return 0; } +static const struct qcom_cpucp_mbox_data qcom_x1e80100_mbox_data = { + .num_chans = 3, +}; + +static const struct qcom_cpucp_mbox_data qcom_nord_mbox_data = { + .num_chans = 16, +}; + static const struct of_device_id qcom_cpucp_mbox_of_match[] = { - { .compatible = "qcom,x1e80100-cpucp-mbox" }, + { .compatible = "qcom,nord-cpucp-mbox", .data = &qcom_nord_mbox_data }, + { .compatible = "qcom,x1e80100-cpucp-mbox", .data = &qcom_x1e80100_mbox_data }, {} }; MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match); From 3931aaac040921931e6d97c3c1a836e864386642 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Fri, 8 May 2026 16:10:48 +0530 Subject: [PATCH 1071/5214] dt-bindings: mailbox: qcom: Add Shikra APCS compatible Add compatible for the Qualcomm Shikra APCS block. Signed-off-by: Komal Bajaj Signed-off-by: Sneh Mankad Acked-by: Rob Herring (Arm) Signed-off-by: Jassi Brar --- .../devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index f40dc9048327..1b4ef0688ca7 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -49,6 +49,7 @@ properties: - qcom,qcs615-apss-shared - qcom,sc7180-apss-shared - qcom,sc8180x-apss-shared + - qcom,shikra-apss-shared - qcom,sm7150-apss-shared - qcom,sm8150-apss-shared - const: qcom,sdm845-apss-shared From c19bb923b5bcb077aebcb06ce164f6f4c6b3072c Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 27 Apr 2026 23:54:12 +0530 Subject: [PATCH 1072/5214] dt-bindings: mailbox: qcom,cpucp-mbox: Add Hawi compatible Document CPU Control Processor (CPUCP) mailbox controller for Qualcomm Hawi SoCs. It is software compatible with X1E80100 CPUCP mailbox controller hence fallback to it. Signed-off-by: Mukesh Ojha Acked-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml index c8107d58f3d5..03359479d926 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml @@ -19,6 +19,7 @@ properties: - items: - enum: - qcom,glymur-cpucp-mbox + - qcom,hawi-cpucp-mbox - qcom,kaanapali-cpucp-mbox - qcom,nord-cpucp-mbox - qcom,sm8750-cpucp-mbox From cfba87b3875dd0d78ec6ca75d2446412030133fa Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 1 Apr 2026 18:21:26 +0530 Subject: [PATCH 1073/5214] dt-bindings: mailbox: qcom: Add IPCC support for Hawi Platform Document the Inter-Processor Communication Controller on the Qualcomm Hawi Platform, which will be used to route interrupts across various subsystems found on the SoC. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Reviewed-by: Manivannan Sadhasivam Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index f5c584cf2146..fae20a6615f6 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -26,6 +26,7 @@ properties: - enum: - qcom,eliza-ipcc - qcom,glymur-ipcc + - qcom,hawi-ipcc - qcom,kaanapali-ipcc - qcom,milos-ipcc - qcom,qcs8300-ipcc From 7caa16023b14dee4aa791f1a298474db02a791fd Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Thu, 30 Apr 2026 11:12:59 +0000 Subject: [PATCH 1074/5214] mailbox: exynos: Drop unused register definitions Leaving these dead definitions in place hides which registers are actually being used by the hardware, making the driver harder to read and maintain. Remove them to clean up the file. Signed-off-by: Tudor Ambarus Reviewed-by: Alexey Klimov Signed-off-by: Jassi Brar --- drivers/mailbox/exynos-mailbox.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c index d2355b128ba4..fa02f18948cf 100644 --- a/drivers/mailbox/exynos-mailbox.c +++ b/drivers/mailbox/exynos-mailbox.c @@ -16,15 +16,8 @@ #include #include -#define EXYNOS_MBOX_MCUCTRL 0x0 /* Mailbox Control Register */ -#define EXYNOS_MBOX_INTCR0 0x24 /* Interrupt Clear Register 0 */ #define EXYNOS_MBOX_INTMR0 0x28 /* Interrupt Mask Register 0 */ -#define EXYNOS_MBOX_INTSR0 0x2c /* Interrupt Status Register 0 */ -#define EXYNOS_MBOX_INTMSR0 0x30 /* Interrupt Mask Status Register 0 */ #define EXYNOS_MBOX_INTGR1 0x40 /* Interrupt Generation Register 1 */ -#define EXYNOS_MBOX_INTMR1 0x48 /* Interrupt Mask Register 1 */ -#define EXYNOS_MBOX_INTSR1 0x4c /* Interrupt Status Register 1 */ -#define EXYNOS_MBOX_INTMSR1 0x50 /* Interrupt Mask Status Register 1 */ #define EXYNOS_MBOX_INTMR0_MASK GENMASK(15, 0) #define EXYNOS_MBOX_INTGR1_MASK GENMASK(15, 0) From 7bcfb7e65457f784b9495b10743f2c9db409b5b7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:01:14 +0200 Subject: [PATCH 1075/5214] mailbox: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 5bf4155b090a..3062ee352f78 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -341,7 +341,7 @@ config SPRD_MBOX you want to build the Spreatrum mailbox controller driver. config QCOM_CPUCP_MBOX - tristate "Qualcomm Technologies, Inc. CPUCP mailbox driver" + tristate "Qualcomm CPUCP mailbox driver" depends on (ARCH_QCOM || COMPILE_TEST) && 64BIT help Qualcomm Technologies, Inc. CPUSS Control Processor (CPUCP) mailbox @@ -349,7 +349,7 @@ config QCOM_CPUCP_MBOX Y here if you want to build this driver. config QCOM_IPCC - tristate "Qualcomm Technologies, Inc. IPCC driver" + tristate "Qualcomm IPCC driver" depends on ARCH_QCOM || COMPILE_TEST help Qualcomm Technologies, Inc. Inter-Processor Communication Controller From b57d1a40bc43258372fa1f4d39305e093947a262 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 28 Apr 2026 11:55:44 +0900 Subject: [PATCH 1076/5214] mailbox: mtk-adsp: fix UAF during device teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the SOF audio driver fails to initialize (e.g. firmware boot timeout), its devres unwind frees the snd_sof_dev object that the mailbox client (mtk-adsp-ipc) reaches via chan->cl->rx_callback. The mtk-adsp-mailbox shutdown clears the mailbox command registers but leaves the IRQ line unmasked, so a late interrupt can still queue a threaded handler after mbox_free_channel() had cleared chan->cl, and mbox_chan_received_data() would then trigger UAF: BUG: KASAN: slab-use-after-free in sof_ipc3_validate_fw_version sof_ipc3_validate_fw_version sof_ipc3_do_rx_work sof_ipc3_rx_msg mt8196_dsp_handle_request mtk_adsp_ipc_recv mbox_chan_received_data mtk_adsp_mbox_isr irq_thread_fn Freed by task ...: kfree devres_release_all really_probe ... (sof-audio-of-mt8196 probe failure) The crash was observed roughly three seconds after the failed probe. disable_irq() in shutdown and enable_irq() in startup. disable_irq() also waits for any in-flight interrupts, so by the time mbox_free_channel() proceeds to clear chan->cl no rx_callback can run. In addition, request the IRQ with IRQF_NO_AUTOEN so it stays masked between probe and the first client bind — otherwise an early interrupt can crash on chan->cl == NULL in mbox_chan_received_data(). Fixes: af2dfa96c52d ("mailbox: mediatek: add support for adsp mailbox controller") Signed-off-by: Sergey Senozhatsky Reviewed-by: Tzung-Bi Shih Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-adsp-mailbox.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c index 91487aa4d7da..8bcecddee0eb 100644 --- a/drivers/mailbox/mtk-adsp-mailbox.c +++ b/drivers/mailbox/mtk-adsp-mailbox.c @@ -19,6 +19,7 @@ struct mtk_adsp_mbox_priv { struct mbox_controller mbox; void __iomem *va_mboxreg; const struct mtk_adsp_mbox_cfg *cfg; + int irq; }; struct mtk_adsp_mbox_cfg { @@ -67,6 +68,8 @@ static int mtk_adsp_mbox_startup(struct mbox_chan *chan) writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); + enable_irq(priv->irq); + return 0; } @@ -74,6 +77,8 @@ static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan) { struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox); + disable_irq(priv->irq); + /* Clear ADSP mbox command */ writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); @@ -139,8 +144,10 @@ static int mtk_adsp_mbox_probe(struct platform_device *pdev) if (irq < 0) return irq; + priv->irq = irq; ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq, - mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE, + mtk_adsp_mbox_isr, + IRQF_TRIGGER_NONE | IRQF_NO_AUTOEN, dev_name(dev), mbox->chans); if (ret < 0) return ret; From c96c8a7404ef8ce434ffd0f07b00e1a493fff42d Mon Sep 17 00:00:00 2001 From: Joonwon Kang Date: Tue, 21 Apr 2026 10:46:51 +0000 Subject: [PATCH 1077/5214] mailbox: Clarify multi-thread is not supported in blocking mode Unlike in non-blocking mode, multi-thread has not been supported in blocking mode. This commit is to prevent clients from having wrong assumption by explicitly specifying this fact to the API doc. Signed-off-by: Joonwon Kang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index bbc9fd75a95f..b00f7a32e866 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -258,6 +258,10 @@ EXPORT_SYMBOL_GPL(mbox_chan_tx_slots_available); * over the chan, i.e, tx_done() is made. * This function could be called from atomic context as it simply * queues the data and returns a token against the request. + * In blocking mode, it is caller's responsibility to serialize threads' + * access to a channel if multi-threads are to send messages through the + * same channel, i.e. caller should not call this function until any + * previous call returns. * * Return: Non-negative integer for successful submission (non-blocking mode) * or transmission over chan (blocking mode). From 96a3d2f3167f5644b30e60171898e67123c3c2c6 Mon Sep 17 00:00:00 2001 From: Joonwon Kang Date: Sun, 10 May 2026 05:41:11 +0000 Subject: [PATCH 1078/5214] mailbox: Make mbox_send_message() return error code when tx fails When the mailbox controller failed transmitting message, the error code was only passed to the client's tx done handler and not to mbox_send_message() in blocking mode. For this reason, the function could return a false success. This commit resolves the issue by introducing the tx status and checking it before mbox_send_message() returns. This commit works with the premise that the multi-threads' access to a channel in blocking mode is serialized by clients, not by the mailbox APIs, since the current mbox_send_message() in blocking mode does not support multi-threads. Signed-off-by: Joonwon Kang Reviewed-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 6 +++++- include/linux/mailbox_controller.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index b00f7a32e866..066702e5a46f 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -98,8 +98,10 @@ static void tx_tick(struct mbox_chan *chan, int r) if (chan->cl->tx_done) chan->cl->tx_done(chan->cl, mssg, r); - if (r != -ETIME && chan->cl->tx_block) + if (r != -ETIME && chan->cl->tx_block) { + chan->tx_status = r; complete(&chan->tx_complete); + } } static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) @@ -295,6 +297,8 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) if (ret == 0) { t = -ETIME; tx_tick(chan, t); + } else if (chan->tx_status < 0) { + t = chan->tx_status; } } diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index dc93287a2a01..26a238a6f941 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -120,6 +120,7 @@ struct mbox_controller { * @txdone_method: Way to detect TXDone chosen by the API * @cl: Pointer to the current owner of this channel * @tx_complete: Transmission completion + * @tx_status: Transmission status * @active_req: Currently active request hook * @msg_count: No. of mssg currently queued * @msg_free: Index of next available mssg slot @@ -132,6 +133,7 @@ struct mbox_chan { unsigned txdone_method; struct mbox_client *cl; struct completion tx_complete; + int tx_status; void *active_req; unsigned msg_count, msg_free; void *msg_data[MBOX_TX_QUEUE_LEN]; From 4f176444dcc977d1888fd9220c357a4d32338ee0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 May 2026 09:09:47 +0200 Subject: [PATCH 1079/5214] mailbox: don't free the channel if the startup callback failed If the optional startup() callbacks fails, we need to clear some states. Currently, this is done by freeing the channel. This does, however, more than needed which creates problems. Namely, it is calling the shutdown() callback. This is totally not intuitive. No user expects that shutdown() is called when startup() fails, similar to remove() not being called when probe() fails. Currently, quite some mailbox users register the IRQ in startup() and free them in shutdown(). These drivers will get a WARN about freeing an already free IRQ. Other subtle issues could arise from this unexpected behaviour. To solve this problem, introduce a helper which does the minimal cleanup and use it in both, in free_channel() and after startup() failed. Link: https://sashiko.dev/#/patchset/20260402112709.13002-1-wsa%2Brenesas%40sang-engineering.com # second issue Fixes: 2b6d83e2b8b7 ("mailbox: Introduce framework for mailbox") Signed-off-by: Wolfram Sang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 066702e5a46f..ef88474501b9 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -335,6 +335,19 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout) } EXPORT_SYMBOL_GPL(mbox_flush); +static void mbox_clean_and_put_channel(struct mbox_chan *chan) +{ + /* The queued TX requests are simply aborted, no callbacks are made */ + scoped_guard(spinlock_irqsave, &chan->lock) { + chan->cl = NULL; + chan->active_req = MBOX_NO_MSG; + if (chan->txdone_method == MBOX_TXDONE_BY_ACK) + chan->txdone_method = MBOX_TXDONE_BY_POLL; + } + + module_put(chan->mbox->dev->driver->owner); +} + static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) { struct device *dev = cl->dev; @@ -358,10 +371,9 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) if (chan->mbox->ops->startup) { ret = chan->mbox->ops->startup(chan); - if (ret) { dev_err(dev, "Unable to startup the chan (%d)\n", ret); - mbox_free_channel(chan); + mbox_clean_and_put_channel(chan); return ret; } } @@ -503,15 +515,7 @@ void mbox_free_channel(struct mbox_chan *chan) if (chan->mbox->ops->shutdown) chan->mbox->ops->shutdown(chan); - /* The queued TX requests are simply aborted, no callbacks are made */ - scoped_guard(spinlock_irqsave, &chan->lock) { - chan->cl = NULL; - chan->active_req = MBOX_NO_MSG; - if (chan->txdone_method == MBOX_TXDONE_BY_ACK) - chan->txdone_method = MBOX_TXDONE_BY_POLL; - } - - module_put(chan->mbox->dev->driver->owner); + mbox_clean_and_put_channel(chan); } EXPORT_SYMBOL_GPL(mbox_free_channel); From 5e4907c4908bff6570f48aff86fef424e74c051f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 May 2026 09:16:06 +0200 Subject: [PATCH 1080/5214] mailbox: add list of used channels to debugfs During development, it is useful to see which mailboxes are currently obtained. Use a seq-file in debugfs to list the currently registered controllers and their used channels. Example output from a Renesas R-Car X5H based system: # cat /sys/kernel/debug/mailbox/mailbox_summary 189e0000.system-controller: 0: c1000000.mailbox_test_send_to_recv 1: c1000100.mailbox_test_recv_to_send 128: c1000100.mailbox_test_recv_to_send 129: c1000000.mailbox_test_send_to_recv 189e1000.system-controller: 4: scmi_dev.1 5: scmi_dev.2 Signed-off-by: Wolfram Sang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index ef88474501b9..efacd24a085d 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include static LIST_HEAD(mbox_cons); @@ -644,3 +646,66 @@ int devm_mbox_controller_register(struct device *dev, return 0; } EXPORT_SYMBOL_GPL(devm_mbox_controller_register); + +#ifdef CONFIG_DEBUG_FS +static void *mbox_seq_start(struct seq_file *s, loff_t *pos) +{ + mutex_lock(&con_mutex); + return seq_list_start(&mbox_cons, *pos); +} + +static void *mbox_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return seq_list_next(v, &mbox_cons, pos); +} + +static void mbox_seq_stop(struct seq_file *s, void *v) +{ + mutex_unlock(&con_mutex); +} + +static int mbox_seq_show(struct seq_file *seq, void *v) +{ + const struct mbox_controller *mbox = list_entry(v, struct mbox_controller, node); + + seq_printf(seq, "%s:\n", dev_name(mbox->dev)); + + for (unsigned int i = 0; i < mbox->num_chans; i++) { + struct mbox_chan *chan = &mbox->chans[i]; + + scoped_guard(spinlock_irqsave, &chan->lock) { + if (chan->cl) { + struct device *cl_dev = chan->cl->dev; + + seq_printf(seq, " %3u: %s\n", i, + cl_dev ? dev_name(cl_dev) : "NULL device"); + } + } + } + + return 0; +} + +static const struct seq_operations mbox_sops = { + .start = mbox_seq_start, + .next = mbox_seq_next, + .stop = mbox_seq_stop, + .show = mbox_seq_show, +}; +DEFINE_SEQ_ATTRIBUTE(mbox); + +/* + * subsys_initcall() is used here but controllers may already have been + * registered earlier or will be later. The rationale is that debugfs is + * accessed only late, i.e. from userspace. So, files created here must make no + * assumptions about initcall ordering. + */ +static int __init mbox_init(void) +{ + struct dentry *mbox_debugfs = debugfs_create_dir("mailbox", NULL); + + debugfs_create_file("mailbox_summary", 0444, mbox_debugfs, NULL, &mbox_fops); + return 0; +} +subsys_initcall(mbox_init); +#endif /* DEBUG_FS */ From 6113cea475959372e8e07144fa3824642440f388 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 18 May 2026 14:12:18 -0500 Subject: [PATCH 1081/5214] PCI: Log device readiness timeouts as errors pci_dev_wait() waits for a device to be Configuration-Ready after a reset, such as a Function-Level Reset (FLR), a soft reset during a D3hot-> D0uninitialized transition when No_Soft_Reset == 0), or a power-up sequence from D3cold->D0uninitialized. If pci_dev_wait() returns success, the device is guaranteed to respond to configuration requests with Successful Completion status. If it times out, device is completely non-responsive. Upgrade the log level from pci_warn() to pci_err() to reflect this failure state. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260518191220.636213-2-bhelgaas@google.com --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..5a9af0bb2c71 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1253,8 +1253,8 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) } if (delay > timeout) { - pci_warn(dev, "not ready %dms after %s; giving up\n", - delay - 1, reset_type); + pci_err(dev, "not ready %dms after %s; giving up\n", + delay - 1, reset_type); return -ENOTTY; } From 41167a1e98536b4baf0846fd259c8124bd1c4e1b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 18 May 2026 14:12:19 -0500 Subject: [PATCH 1082/5214] PCI: Wait for device readiness after D3hot -> D0uninitialized transition For a device that advertises No_Soft_Reset == 0, a transition from D3hot to D0uninitialized is a soft reset, and the resulting internal device state is undefined. Per PCIe r7.0, sec 2.3.1, a transition from D3hot to D0uninitialized mandates a minimum 10 ms delay before accessing the device. Following this delay, the device is permitted to respond to initial configuration requests with a Request Retry Status (RRS) completion status if it needs more time to initialize. Call pci_dev_wait() after pci_power_up() performs a D3hot->D0uninitialized transition to ensure the device is ready to accept config accesses, as is done after the similar transition in pci_pm_reset(). If the device is already ready, this is essentially a no-op except for one additional config read. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260518191220.636213-3-bhelgaas@google.com --- drivers/pci/pci.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5a9af0bb2c71..8228d2782f95 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1300,7 +1300,18 @@ int pci_power_up(struct pci_dev *dev) bool need_restore; pci_power_t state; u16 pmcsr; + int ret; + /* + * When setting power state to D0, platform_pci_set_power_state() + * ensures main power is on. If it puts the device in D0, it also + * completes any required delays after the transition; if it leaves + * the device in D1, D2, or D3hot, we use the PM Capability to + * transition to D0. + * + * In all cases, the device is either Configuration-Ready or + * inaccessible upon return. + */ platform_pci_set_power_state(dev, PCI_D0); if (!dev->pm_cap) { @@ -1341,10 +1352,19 @@ int pci_power_up(struct pci_dev *dev) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0); /* Mandatory transition delays; see PCI PM 1.2. */ - if (state == PCI_D3hot) + if (state == PCI_D3hot) { pci_dev_d3_sleep(dev); - else if (state == PCI_D2) + if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) { + ret = pci_dev_wait(dev, "power up D3hot->D0uninitialized", + PCIE_RESET_READY_POLL_MS); + if (ret) { + dev->current_state = PCI_D3cold; + return -EIO; + } + } + } else if (state == PCI_D2) { udelay(PCI_PM_D2_DELAY); + } end: dev->current_state = PCI_D0; From 10baa9b4df4005ca53c59c30c2d4b774469e10b6 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 3 May 2026 15:34:46 +0200 Subject: [PATCH 1083/5214] PCI: Drop unnecessary retries when restoring BARs In 2012, commit 26f41062f28d ("PCI: check for pci bar restore completion and retry") amended pci_restore_state() to attempt BAR restoration up to 10 times. This was necessary because back in the day, only a 100 msec delay was observed after pcie_flr() carried out a Function Level Reset. The retries ensured that BARs were restored even if devices needed more time to come out of reset. In 2016, commit 5adecf817dd6 ("PCI: Wait for up to 1000ms after FLR reset") extended the delay to 1 sec. Commit a2758b6b8fdb ("PCI: Rename pci_flr_wait() to pci_dev_wait() and make it generic") subsequently extended it further to 60 sec. The lengthened delay makes it unnecessary to retry BAR restoration, so drop it. Reported-by: Bjorn Helgaas Closes: https://lore.kernel.org/r/20260416225745.GA41850@bhelgaas/ Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/785c98b50a7a00d0698848c75d51b8f5669ad18f.1777814679.git.lukas@wunner.de --- drivers/pci/pci.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8228d2782f95..a21b0075e1de 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1784,7 +1784,7 @@ int pci_save_state(struct pci_dev *dev) EXPORT_SYMBOL(pci_save_state); static void pci_restore_config_dword(struct pci_dev *pdev, int offset, - u32 saved_val, int retry, bool force) + u32 saved_val, bool force) { u32 val; @@ -1792,52 +1792,42 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset, if (!force && val == saved_val) return; - for (;;) { - pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", - offset, val, saved_val); - pci_write_config_dword(pdev, offset, saved_val); - if (retry-- <= 0) - return; + pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", offset, val, + saved_val); - pci_read_config_dword(pdev, offset, &val); - if (val == saved_val) - return; - - mdelay(1); - } + pci_write_config_dword(pdev, offset, saved_val); } static void pci_restore_config_space_range(struct pci_dev *pdev, - int start, int end, int retry, - bool force) + int start, int end, bool force) { int index; for (index = end; index >= start; index--) pci_restore_config_dword(pdev, 4 * index, pdev->saved_config_space[index], - retry, force); + force); } static void pci_restore_config_space(struct pci_dev *pdev) { if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) { - pci_restore_config_space_range(pdev, 10, 15, 0, false); + pci_restore_config_space_range(pdev, 10, 15, false); /* Restore BARs before the command register. */ - pci_restore_config_space_range(pdev, 4, 9, 10, false); - pci_restore_config_space_range(pdev, 0, 3, 0, false); + pci_restore_config_space_range(pdev, 4, 9, false); + pci_restore_config_space_range(pdev, 0, 3, false); } else if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - pci_restore_config_space_range(pdev, 12, 15, 0, false); + pci_restore_config_space_range(pdev, 12, 15, false); /* * Force rewriting of prefetch registers to avoid S3 resume * issues on Intel PCI bridges that occur when these * registers are not explicitly written. */ - pci_restore_config_space_range(pdev, 9, 11, 0, true); - pci_restore_config_space_range(pdev, 0, 8, 0, false); + pci_restore_config_space_range(pdev, 9, 11, true); + pci_restore_config_space_range(pdev, 0, 8, false); } else { - pci_restore_config_space_range(pdev, 0, 15, 0, false); + pci_restore_config_space_range(pdev, 0, 15, false); } } From 548f3d287d92bcbc908b02db57fb889f8a8276a0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 12:07:00 +0200 Subject: [PATCH 1084/5214] PCI/pwrctrl: Lock device when calling device_is_bound() The kerneldoc for device_is_bound() states that it must be called with the device lock taken. Synchronize the two calls in pwrctrl core. Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Signed-off-by: Bartosz Golaszewski Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260518100700.47581-1-bartosz.golaszewski@oss.qualcomm.com --- drivers/pci/pwrctrl/core.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index b5a0a14d316e..de3b78fa4b5b 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -206,10 +206,12 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) if (!pdev) return; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_off_device(&pdev->dev); - if (ret) - dev_err(&pdev->dev, "Failed to power off device: %d", ret); + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_off_device(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to power off device: %d", ret); + } } platform_device_put(pdev); @@ -250,7 +252,7 @@ static int __pci_pwrctrl_power_on_device(struct device *dev) static int pci_pwrctrl_power_on_device(struct device_node *np) { struct platform_device *pdev; - int ret; + int ret = 0; for_each_available_child_of_node_scoped(np, child) { ret = pci_pwrctrl_power_on_device(child); @@ -265,12 +267,14 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) if (!pdev) return 0; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_on_device(&pdev->dev); - } else { - /* FIXME: Use blocking wait instead of probe deferral */ - dev_dbg(&pdev->dev, "driver is not bound\n"); - ret = -EPROBE_DEFER; + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_on_device(&pdev->dev); + } else { + /* FIXME: Use blocking wait instead of probe deferral */ + dev_dbg(&pdev->dev, "driver is not bound\n"); + ret = -EPROBE_DEFER; + } } platform_device_put(pdev); From aad953fb4eed0df5486cd54ccad80ac197678e01 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 19 Mar 2026 17:08:44 +0800 Subject: [PATCH 1085/5214] PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core reset sequence. Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into the core reset functions to properly reflect its purpose. Remove the .enable_ref_clk() callback for i.MX6SX since it was incorrectly manipulating this bit. Fixes: e3c06cd063d6 ("PCI: imx6: Add initial imx6sx support") Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260319090844.444987-1-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index e35044cc5218..1034ac5c5f5c 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -665,14 +665,6 @@ static int imx_pcie_attach_pd(struct device *dev) return 0; } -static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) -{ - regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, - enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN); - return 0; -} - static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { if (enable) { @@ -786,6 +778,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) if (assert) regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN); /* Force PCIe PHY reset */ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET, @@ -1877,7 +1872,6 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .init_phy = imx6sx_pcie_init_phy, - .enable_ref_clk = imx6sx_pcie_enable_ref_clk, .core_reset = imx6sx_pcie_core_reset, .ops = &imx_pcie_host_ops, }, From e3d334156b593c465153700b3f9fbdcac5af9cbd Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Wed, 22 Apr 2026 17:35:38 +0800 Subject: [PATCH 1086/5214] dt-bindings: PCI: fsl,imx6q-pcie: Add reset GPIO in Root Port node Update fsl,imx6q-pcie.yaml to include the standard reset-gpios property for the Root Port node. The reset-gpios property is already defined in pci-bus-common.yaml for PERST#, so use it instead of the local reset-gpio property. Keep the existing reset-gpio property in the bridge node for backward compatibility, but mark it as deprecated. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260422093549.407022-2-sherry.sun@nxp.com --- .../bindings/pci/fsl,imx6q-pcie.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index 9d1349855b42..e8b8131f5f23 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -66,16 +66,34 @@ properties: - const: dma reset-gpio: + deprecated: true description: Should specify the GPIO for controlling the PCI bus device reset signal. It's not polarity aware and defaults to active-low reset sequence (L=reset state, H=operation state) (optional required). + This property is deprecated, instead of referencing this property from the + host bridge node, use the reset-gpios property from the root port node. reset-gpio-active-high: + deprecated: true description: If present then the reset sequence using the GPIO specified in the "reset-gpio" property is reversed (H=reset state, L=operation state) (optional required). + This property is deprecated along with the reset-gpio property above, use + the reset-gpios property from the root port node. type: boolean + pcie@0: + description: + Describe the i.MX6 PCIe Root Port. + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + unevaluatedProperties: false + required: - compatible - reg @@ -236,6 +254,7 @@ unevaluatedProperties: false examples: - | #include + #include #include pcie: pcie@1ffc000 { @@ -262,5 +281,18 @@ examples: <&clks IMX6QDL_CLK_LVDS1_GATE>, <&clks IMX6QDL_CLK_PCIE_REF_125M>; clock-names = "pcie", "pcie_bus", "pcie_phy"; + + pcie_port0: pcie@0 { + compatible = "pciclass,0604"; + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + }; }; ... From b269bb5e4cc3cf04a592d516a3dc260d9d893f24 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Wed, 22 Apr 2026 17:35:39 +0800 Subject: [PATCH 1087/5214] PCI: host-generic: Add common helpers for parsing Root Port properties Introduce generic helper functions to parse Root Port device tree nodes and extract common properties like reset GPIOs. This allows multiple PCI host controller drivers to share the same parsing logic. Define struct pci_host_port to hold common Root Port properties (currently only list of PERST# GPIO descriptors) and add pci_host_common_parse_ports() to parse Root Port nodes from device tree. Also add the 'ports' list to struct pci_host_bridge to better maintain parsed Root Port information. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260422093549.407022-3-sherry.sun@nxp.com --- drivers/pci/controller/pci-host-common.c | 169 +++++++++++++++++++++++ drivers/pci/controller/pci-host-common.h | 28 ++++ drivers/pci/probe.c | 1 + include/linux/pci.h | 1 + 4 files changed, 199 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5..9bf66c256f81 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,174 @@ #include "pci-host-common.h" +/** + * pci_host_common_delete_ports - Cleanup function for port list + * @data: Pointer to the port list head + */ +void pci_host_common_delete_ports(void *data) +{ + struct list_head *ports = data; + struct pci_host_perst *perst, *tmp_perst; + struct pci_host_port *port, *tmp_port; + + list_for_each_entry_safe(port, tmp_port, ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); + list_del(&port->list); + } +} +EXPORT_SYMBOL_GPL(pci_host_common_delete_ports); + +/** + * pci_host_common_parse_perst - Parse PERST# from all nodes, depth first + * @dev: Device pointer + * @port: PCI host port + * @np: Device tree node to start parsing from + * + * Recursively parse PERST# GPIO from all PCIe bridge nodes starting from + * @np in a depth-first manner. + * + * Return: 0 on success, negative error code on failure. + */ +static int pci_host_common_parse_perst(struct device *dev, + struct pci_host_port *port, + struct device_node *np) +{ + struct pci_host_perst *perst; + struct gpio_desc *reset; + int ret; + + if (!of_property_present(np, "reset-gpios")) + goto parse_child_node; + + reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset", + GPIOD_ASIS, "PERST#"); + if (IS_ERR(reset)) { + /* + * FIXME: GPIOLIB currently supports exclusive GPIO access only. + * Non exclusive access is broken. But shared PERST# requires + * non-exclusive access. So once GPIOLIB properly supports it, + * implement it here. + */ + if (PTR_ERR(reset) == -EBUSY) + dev_err(dev, "Shared PERST# is not supported\n"); + + return PTR_ERR(reset); + } + + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + + INIT_LIST_HEAD(&perst->list); + perst->desc = reset; + list_add_tail(&perst->list, &port->perst); + +parse_child_node: + for_each_available_child_of_node_scoped(np, child) { + ret = pci_host_common_parse_perst(dev, port, child); + if (ret) + return ret; + } + + return 0; +} + +/** + * pci_host_common_parse_port - Parse a single Root Port node + * @dev: Device pointer + * @bridge: PCI host bridge + * @node: Device tree node of the Root Port + * + * Parse Root Port properties from the device tree. Currently it only + * handles the PERST# GPIO (including PERST# GPIOs from all PCIe bridge + * nodes under this Root Port), which is optional. + * + * NOTE: This helper fetches resources (like PERST# GPIO) optionally. If a + * controller driver has a hard dependency on certain resources (PHY, + * clocks, regulators, etc.), those resources MUST be modeled correctly in + * the DT binding and validated in DTS. This helper cannot enforce such + * dependencies and the driver may fail to operate if required resources + * are missing. + * + * Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding + * should be used), Other negative error codes on failure. + */ +static int pci_host_common_parse_port(struct device *dev, + struct pci_host_bridge *bridge, + struct device_node *node) +{ + struct pci_host_port *port; + int ret; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + INIT_LIST_HEAD(&port->perst); + + ret = pci_host_common_parse_perst(dev, port, node); + if (ret) + return ret; + + /* + * 1. PERST# found in RP or its child nodes - list is not empty, + * continue + * + * 2. PERST# not found in RP/children, but found in RC node - + * return -ENODEV to fallback legacy binding + * + * 3. PERST# not found anywhere - list is empty, continue (optional + * PERST#) + */ + if (list_empty(&port->perst)) { + if (of_property_present(dev->of_node, "reset-gpios") || + of_property_present(dev->of_node, "reset-gpio")) + return -ENODEV; + } + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &bridge->ports); + + return 0; +} + +/** + * pci_host_common_parse_ports - Parse Root Port nodes from device tree + * @dev: Device pointer + * @bridge: PCI host bridge + * + * Iterate through child nodes of the host bridge and parse Root Port + * properties (currently only reset GPIOs). + * + * Return: 0 on success, -ENODEV if no ports found or PERST# found in RC + * node (legacy binding should be used), Other negative error codes on + * failure. + */ +int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge) +{ + int ret = -ENODEV; + + for_each_available_child_of_node_scoped(dev->of_node, of_port) { + if (!of_node_is_type(of_port, "pci")) + continue; + ret = pci_host_common_parse_port(dev, bridge, of_port); + if (ret) + goto err_cleanup; + } + + if (ret) + return ret; + + return devm_add_action_or_reset(dev, pci_host_common_delete_ports, + &bridge->ports); + +err_cleanup: + pci_host_common_delete_ports(&bridge->ports); + return ret; +} +EXPORT_SYMBOL_GPL(pci_host_common_parse_ports); + static void gen_pci_unmap_cfg(void *ptr) { pci_ecam_free((struct pci_config_window *)ptr); diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index b5075d4bd7eb..0f5fcc02e778 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -12,6 +12,34 @@ struct pci_ecam_ops; +/** + * struct pci_host_perst - PERST# GPIO descriptor + * @list: List node for linking multiple PERST# GPIOs + * @desc: GPIO descriptor for PERST# signal + * + * This structure holds a single PERST# GPIO descriptor. + */ +struct pci_host_perst { + struct list_head list; + struct gpio_desc *desc; +}; + +/** + * struct pci_host_port - Generic Root Port properties + * @list: List node for linking multiple ports + * @perst: List of PERST# GPIO descriptors for this port and its children + * + * This structure contains common properties that can be parsed from + * Root Port device tree nodes. + */ +struct pci_host_port { + struct list_head list; + struct list_head perst; +}; + +void pci_host_common_delete_ports(void *data); +int pci_host_common_parse_ports(struct device *dev, + struct pci_host_bridge *bridge); int pci_host_common_probe(struct platform_device *pdev); int pci_host_common_init(struct platform_device *pdev, struct pci_host_bridge *bridge, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc..6094b6c1fc90 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -660,6 +660,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge) { INIT_LIST_HEAD(&bridge->windows); INIT_LIST_HEAD(&bridge->dma_ranges); + INIT_LIST_HEAD(&bridge->ports); /* * We assume we can manage these PCIe features. Some systems may diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..cb5f3e7e8e48 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -636,6 +636,7 @@ struct pci_host_bridge { int domain_nr; struct list_head windows; /* resource_entry */ struct list_head dma_ranges; /* dma ranges resource list */ + struct list_head ports; /* Root Port list (pci_host_port) */ #ifdef CONFIG_PCI_IDE u16 nr_ide_streams; /* Max streams possibly active in @ide_stream_ida */ struct ida ide_stream_ida; From 610fa91d9863d9bc0ce496c5129328459c75174b Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Wed, 22 Apr 2026 17:35:40 +0800 Subject: [PATCH 1088/5214] PCI: imx6: Assert PERST# before enabling regulators The PCIe endpoint may start responding or driving signals as soon as its supply is enabled, even before the reference clock is stable. Asserting PERST# before enabling the regulator ensures that the endpoint remains in reset throughout the entire power-up sequence, until both power and refclk are known to be stable and link initialization can safely begin. Currently, the driver enables the vpcie3v3aux regulator in imx_pcie_probe() before PERST# is asserted in imx_pcie_host_init(), which may cause PCIe endpoint undefined behavior during early power-up. However, there is no issue so far because PERST# is requested as GPIOD_OUT_HIGH in imx_pcie_probe(), which guarantees that PERST# is asserted before enabling the vpcie3v3aux regulator. This prepares for an upcoming changes that will parse the reset property using the new Root Port binding, which will use GPIOD_ASIS when requesting the reset GPIO. With GPIOD_ASIS, the GPIO state is not guaranteed, so explicit sequencing is required. Fix the power sequencing by: 1. Moving vpcie3v3aux regulator enable from probe to imx_pcie_host_init(), where it can be properly sequenced with PERST#. 2. Moving imx_pcie_assert_perst() before regulator and clock enable to ensure correct ordering. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Richard Zhu Link: https://patch.msgid.link/20260422093549.407022-4-sherry.sun@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 50 +++++++++++++++++++++------ 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 1034ac5c5f5c..5335c2b140fb 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -168,6 +168,8 @@ struct imx_pcie { u32 tx_swing_full; u32 tx_swing_low; struct regulator *vpcie; + struct regulator *vpcie_aux; + bool vpcie_aux_enabled; struct regulator *vph; void __iomem *phy_base; @@ -1217,6 +1219,13 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge, imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); } +static void imx_pcie_vpcie_aux_disable(void *data) +{ + struct regulator *vpcie_aux = data; + + regulator_disable(vpcie_aux); +} + static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert) { if (assert) { @@ -1237,6 +1246,24 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) struct imx_pcie *imx_pcie = to_imx_pcie(pci); int ret; + imx_pcie_assert_perst(imx_pcie, true); + + /* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */ + if (imx_pcie->vpcie_aux && !imx_pcie->vpcie_aux_enabled) { + ret = regulator_enable(imx_pcie->vpcie_aux); + if (ret) { + dev_err(dev, "failed to enable vpcie_aux regulator: %d\n", + ret); + return ret; + } + imx_pcie->vpcie_aux_enabled = true; + + ret = devm_add_action_or_reset(dev, imx_pcie_vpcie_aux_disable, + imx_pcie->vpcie_aux); + if (ret) + return ret; + } + if (imx_pcie->vpcie) { ret = regulator_enable(imx_pcie->vpcie); if (ret) { @@ -1246,25 +1273,24 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } } + ret = imx_pcie_clk_enable(imx_pcie); + if (ret) { + dev_err(dev, "unable to enable pcie clocks: %d\n", ret); + goto err_reg_disable; + } + if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) { pp->bridge->enable_device = imx_pcie_enable_device; pp->bridge->disable_device = imx_pcie_disable_device; } imx_pcie_assert_core_reset(imx_pcie); - imx_pcie_assert_perst(imx_pcie, true); if (imx_pcie->drvdata->init_phy) imx_pcie->drvdata->init_phy(imx_pcie); imx_pcie_configure_type(imx_pcie); - ret = imx_pcie_clk_enable(imx_pcie); - if (ret) { - dev_err(dev, "unable to enable pcie clocks: %d\n", ret); - goto err_reg_disable; - } - if (imx_pcie->phy) { ret = phy_init(imx_pcie->phy); if (ret) { @@ -1777,9 +1803,13 @@ static int imx_pcie_probe(struct platform_device *pdev) of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq"); - ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); - if (ret < 0 && ret != -ENODEV) - return dev_err_probe(dev, ret, "failed to enable Vaux supply\n"); + imx_pcie->vpcie_aux = devm_regulator_get_optional(&pdev->dev, + "vpcie3v3aux"); + if (IS_ERR(imx_pcie->vpcie_aux)) { + if (PTR_ERR(imx_pcie->vpcie_aux) != -ENODEV) + return PTR_ERR(imx_pcie->vpcie_aux); + imx_pcie->vpcie_aux = NULL; + } imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); if (IS_ERR(imx_pcie->vpcie)) { From 250eea5c06f5291603612cdf0c60326db4e19141 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Wed, 22 Apr 2026 17:35:41 +0800 Subject: [PATCH 1089/5214] PCI: imx6: Parse 'reset-gpios' in Root Port nodes The current DT binding for pci-imx6 specifies the 'reset-gpios' property in the host bridge node. However, the PERST# signal logically belongs to individual Root Ports rather than the host bridge itself. This becomes important when supporting PCIe Key E connector and the PCI power control framework for pci-imx6 driver, which requires properties to be specified in Root Port nodes. Parse 'reset-gpios' from Root Port nodes and the PCIe bridge nodes under the Root Port using the common helper pci_host_common_parse_ports(), and update the reset GPIO handling to use the parsed port list from bridge->ports. To maintain DT backwards compatibility, fall back to the legacy method of parsing the host bridge node if the reset property is not present in the Root Port nodes. Since now the reset GPIO is obtained with GPIOD_ASIS flag, it may be in input mode, so use gpiod_direction_output() instead of gpiod_set_value_cansleep() to ensure the reset GPIO is properly configured as output before setting its value. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Richard Zhu Link: https://patch.msgid.link/20260422093549.407022-5-sherry.sun@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 88 ++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 5335c2b140fb..773ab65b2afa 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -34,6 +34,7 @@ #include #include "../../pci.h" +#include "../pci-host-common.h" #include "pcie-designware.h" #define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) @@ -152,7 +153,6 @@ struct imx_lut_data { struct imx_pcie { struct dw_pcie *pci; - struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; bool supports_clkreq; @@ -1219,6 +1219,41 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge, imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); } +static int imx_pcie_parse_legacy_binding(struct imx_pcie *pcie) +{ + struct device *dev = pcie->pci->dev; + struct pci_host_bridge *bridge = pcie->pci->pp.bridge; + struct pci_host_port *port; + struct pci_host_perst *perst; + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(reset)) + return PTR_ERR(reset); + + if (!reset) + return 0; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + + INIT_LIST_HEAD(&port->perst); + perst->desc = reset; + INIT_LIST_HEAD(&perst->list); + list_add_tail(&perst->list, &port->perst); + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &bridge->ports); + + return devm_add_action_or_reset(dev, pci_host_common_delete_ports, + &bridge->ports); +} + static void imx_pcie_vpcie_aux_disable(void *data) { struct regulator *vpcie_aux = data; @@ -1228,14 +1263,26 @@ static void imx_pcie_vpcie_aux_disable(void *data) static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert) { + struct dw_pcie *pci = imx_pcie->pci; + struct pci_host_bridge *bridge = pci->pp.bridge; + struct pci_host_perst *perst; + struct pci_host_port *port; + + if (!bridge || list_empty(&bridge->ports)) + return; + if (assert) { - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); - } else { - if (imx_pcie->reset_gpiod) { - msleep(PCIE_T_PVPERL_MS); - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); - msleep(PCIE_RESET_CONFIG_WAIT_MS); + list_for_each_entry(port, &bridge->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_direction_output(perst->desc, 1); } + } else { + mdelay(PCIE_T_PVPERL_MS); + list_for_each_entry(port, &bridge->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_direction_output(perst->desc, 0); + } + mdelay(PCIE_RESET_CONFIG_WAIT_MS); } } @@ -1244,8 +1291,28 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; struct imx_pcie *imx_pcie = to_imx_pcie(pci); + struct pci_host_bridge *bridge = pp->bridge; int ret; + if (bridge && list_empty(&bridge->ports)) { + /* Parse Root Port nodes if present */ + ret = pci_host_common_parse_ports(dev, bridge); + if (ret) { + if (ret != -ENODEV) { + dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret); + return ret; + } + + /* + * Fall back to legacy binding for DT backwards + * compatibility + */ + ret = imx_pcie_parse_legacy_binding(imx_pcie); + if (ret) + return ret; + } + } + imx_pcie_assert_perst(imx_pcie, true); /* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */ @@ -1699,13 +1766,6 @@ static int imx_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx_pcie->phy_base); } - /* Fetch GPIOs */ - imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(imx_pcie->reset_gpiod)) - return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod), - "unable to get reset gpio\n"); - gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset"); - /* Fetch clocks */ imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks); if (imx_pcie->num_clks < 0) From cd4ce68875f406b0cf3f5a94c0fd22989689222f Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 30 Dec 2025 18:09:15 +0100 Subject: [PATCH 1090/5214] media: rzg2l-cru: Skip ICnMC configuration when ICnSVC is used When the CRU is configured to use ICnSVC for virtual channel mapping, as on the RZ/{G3E, V2H/P} SoC, the ICnMC register must not be programmed. Return early after setting up ICnSVC to avoid overriding the ICnMC register, which is not applicable in this mode. This prevents unintended register programming when ICnSVC is enabled. Cc: stable@vger.kernel.org Fixes: 3c5ca0a48bb0 ("media: rzg2l-cru: Drop function pointer to configure CSI") Signed-off-by: Tommaso Merciai [Rework to not break image format programming] Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 1 + .../platform/renesas/rzg2l-cru/rzg2l-video.c | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index a5a57369ef0e..10e62f2646d0 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -60,6 +60,7 @@ #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_VCSEL_MASK GENMASK(23, 22) #define ICnMC_INF_MASK GENMASK(21, 16) #define ICnMS_IA BIT(2) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 162e2ace6931..6aea7c244df1 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -262,19 +262,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, u8 csi_vc) { const struct rzg2l_cru_info *info = cru->info; - u32 icnmc = ICnMC_INF(ip_fmt->datatype); + u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK | + ICnMC_VCSEL_MASK); + icnmc |= ICnMC_INF(ip_fmt->datatype); + /* + * VC filtering goes through SVC register on G3E/V2H. + * + * FIXME: virtual channel filtering is likely broken and only VC=0 + * works. + */ if (cru->info->regs[ICnSVC]) { rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + } else { + icnmc |= ICnMC_VCSEL(csi_vc); } - icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; - - /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, info->image_conv, icnmc); } From 8f383bbded25a8fa9f5344eeaa1e197a5cb8fe65 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 30 Dec 2025 18:09:16 +0100 Subject: [PATCH 1091/5214] media: rzg2l-cru: Use only frame end interrupts On RZ/G3E the CRU driver relies on the frame end interrupt to detect the completion of an active frame transfer when stopping DMA. Update the driver to enable only frame end interrupts (CRUnIE2_FExE), dropping the usage of the frame start interrupts, which is not required for this operations flow. Fix the interrupt status handling in the DMA stopping state by checking the correct frame end status bits (FExS) instead of the frame start one (FSxS). Add a dedicated CRUnINTS2_FExS() macro to reflect the actual register bit layout and drop the now unused CRUnIE2_FSxE() and CRUnINTS2_FSxS() macros. This ensures that DMA stopping is triggered by the intended frame end events and avoids incorrect interrupt handling. Signed-off-by: Tommaso Merciai Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 3 +-- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index 10e62f2646d0..23cb50ee8e57 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -14,12 +14,11 @@ #define CRUnIE_EFE BIT(17) -#define CRUnIE2_FSxE(x) BIT(((x) * 3)) #define CRUnIE2_FExE(x) BIT(((x) * 3) + 1) #define CRUnINTS_SFS BIT(16) -#define CRUnINTS2_FSxS(x) BIT(((x) * 3)) +#define CRUnINTS2_FExS(x) BIT(((x) * 3) + 1) #define CRUnRST_VRESETN BIT(0) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 6aea7c244df1..98b6afbc708d 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -440,7 +440,6 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru) { - rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel)); rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel)); } @@ -700,10 +699,10 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) } if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FSxS(0) || - irq_status & CRUnINTS2_FSxS(1) || - irq_status & CRUnINTS2_FSxS(2) || - irq_status & CRUnINTS2_FSxS(3)) + if (irq_status & CRUnINTS2_FExS(0) || + irq_status & CRUnINTS2_FExS(1) || + irq_status & CRUnINTS2_FExS(2) || + irq_status & CRUnINTS2_FExS(3)) dev_dbg(cru->dev, "IRQ while state stopping\n"); return IRQ_HANDLED; } From 66b3f340e8cc447c4d769680f2c9b0a4cdea0a2c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:24:06 +0100 Subject: [PATCH 1092/5214] media: rzg2l-cru: Modernize locking usage with guards Use more modern lock guard constructs to express the locking sequences in the rzg2l driver. Also include spinlock.h and mutex.h that were previously missing. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 2 ++ .../platform/renesas/rzg2l-cru/rzg2l-video.c | 31 ++++++------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 3a200db15730..be4a9a4953d1 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -9,7 +9,9 @@ #define __RZG2L_CRU__ #include +#include #include +#include #include #include diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 98b6afbc708d..0739169f4439 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -110,10 +110,10 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, enum vb2_buffer_state state) { struct rzg2l_cru_buffer *buf, *node; - unsigned long flags; unsigned int i; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); + for (i = 0; i < cru->num_buf; i++) { if (cru->queue_buf[i]) { vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, @@ -126,7 +126,6 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, @@ -165,13 +164,9 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); list_add_tail(to_buf_list(vbuf), &cru->buf_list); - - spin_unlock_irqrestore(&cru->qlock, flags); } static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, @@ -465,7 +460,6 @@ void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); - unsigned long flags; u8 csi_vc; int ret; @@ -475,7 +469,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) csi_vc = ret; cru->svc_channel = csi_vc; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); @@ -492,7 +486,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Initialize image convert */ ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); if (ret) { - spin_unlock_irqrestore(&cru->qlock, flags); return ret; } @@ -502,8 +495,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Enable image processing reception */ rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); - spin_unlock_irqrestore(&cru->qlock, flags); - return 0; } @@ -573,16 +564,15 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; unsigned int handled = 0; - unsigned long flags; u32 irq_status; u32 amnmbs; int slot; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) - goto done; + return IRQ_RETVAL(handled); handled = 1; @@ -591,14 +581,14 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ if (cru->state == RZG2L_CRU_DMA_STOPPED) { dev_dbg(cru->dev, "IRQ while state stopped\n"); - goto done; + return IRQ_RETVAL(handled); } /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ if (cru->state == RZG2L_CRU_DMA_STOPPING) { if (irq_status & CRUnINTS_SFS) dev_dbg(cru->dev, "IRQ while state stopping\n"); - goto done; + return IRQ_RETVAL(handled); } /* Prepare for capture and update state */ @@ -621,7 +611,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) if (cru->state == RZG2L_CRU_DMA_STARTING) { if (slot != 0) { dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - goto done; + return IRQ_RETVAL(handled); } dev_dbg(cru->dev, "Capture start synced!\n"); @@ -646,9 +636,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for next frame */ rzg2l_cru_fill_hw_slot(cru, slot); -done: - spin_unlock_irqrestore(&cru->qlock, flags); - return IRQ_RETVAL(handled); } From 4acf167fa3696a1f3abfeb7ddcafb2a3e084a638 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:33:27 +0100 Subject: [PATCH 1093/5214] media: rzg2l-cru: Use proper guard() in irq handler The irq handler uses a scoped_guard() that covers the whole function body. Replace it with a more appropriate guard() and reduce the indentation. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 0739169f4439..75928b0f48be 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -670,70 +670,70 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - scoped_guard(spinlock, &cru->qlock) { - irq_status = rzg2l_cru_read(cru, CRUnINTS2); - if (!irq_status) - return IRQ_NONE; + guard(spinlock)(&cru->qlock); - dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); + irq_status = rzg2l_cru_read(cru, CRUnINTS2); + if (!irq_status) + return IRQ_NONE; - rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); + dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_HANDLED; - } + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FExS(0) || - irq_status & CRUnINTS2_FExS(1) || - irq_status & CRUnINTS2_FExS(2) || - irq_status & CRUnINTS2_FExS(3)) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_HANDLED; - } - - slot = rzg3e_cru_get_current_slot(cru); - if (slot < 0) - return IRQ_HANDLED; - - dev_dbg(cru->dev, "Current written slot: %d\n", slot); - cru->buf_addr[slot] = 0; - - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_HANDLED; - } - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - - /* Capture frame */ - if (cru->queue_buf[slot]) { - struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; - - buf->field = cru->format.field; - buf->sequence = cru->sequence; - buf->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); - cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); - } - - cru->sequence++; - - /* Prepare for next frame */ - rzg2l_cru_fill_hw_slot(cru, slot); + /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ + if (cru->state == RZG2L_CRU_DMA_STOPPED) { + dev_dbg(cru->dev, "IRQ while state stopped\n"); + return IRQ_HANDLED; } + if (cru->state == RZG2L_CRU_DMA_STOPPING) { + if (irq_status & CRUnINTS2_FExS(0) || + irq_status & CRUnINTS2_FExS(1) || + irq_status & CRUnINTS2_FExS(2) || + irq_status & CRUnINTS2_FExS(3)) + dev_dbg(cru->dev, "IRQ while state stopping\n"); + return IRQ_HANDLED; + } + + slot = rzg3e_cru_get_current_slot(cru); + if (slot < 0) + return IRQ_HANDLED; + + dev_dbg(cru->dev, "Current written slot: %d\n", slot); + cru->buf_addr[slot] = 0; + + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (cru->state == RZG2L_CRU_DMA_STARTING) { + if (slot != 0) { + dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); + return IRQ_HANDLED; + } + dev_dbg(cru->dev, "Capture start synced!\n"); + cru->state = RZG2L_CRU_DMA_RUNNING; + } + + /* Capture frame */ + if (cru->queue_buf[slot]) { + struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; + + buf->field = cru->format.field; + buf->sequence = cru->sequence; + buf->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); + cru->queue_buf[slot] = NULL; + } else { + /* Scratch buffer was used, dropping frame. */ + dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); + } + + cru->sequence++; + + /* Prepare for next frame */ + rzg2l_cru_fill_hw_slot(cru, slot); + return IRQ_HANDLED; } From d921f21e27626b6684d8c8222925abafae4cfd74 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 12 Feb 2026 15:38:11 +0100 Subject: [PATCH 1094/5214] media: rzg2l-cru: Remove locking from start/stop routines The start/stop streaming routines do not need to lock the whole function body against possible concurrent accesses to the CRU buffers or hardware registers. The stop function starts by disabling interrupts, and only this portion needs to be protected not to race against a possible IRQ. Once interrupts are disabled, nothing in the video device driver can race and once the peripheral has been disabled we can release all pending buffers. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Reviewed-by: Daniel Scally Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 75928b0f48be..96c71f1357f8 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -340,23 +340,19 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) { unsigned int retries = 0; - unsigned long flags; u32 icnms; - spin_lock_irqsave(&cru->qlock, flags); - - /* Disable and clear the interrupt */ - cru->info->disable_interrupts(cru); + scoped_guard(spinlock_irq, &cru->qlock) { + /* Disable and clear the interrupt */ + cru->info->disable_interrupts(cru); + } /* Stop the operation of image conversion */ rzg2l_cru_write(cru, ICnEN, 0); /* Wait for streaming to stop */ - while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { - spin_unlock_irqrestore(&cru->qlock, flags); + while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) msleep(RZG2L_TIMEOUT_MS); - spin_lock_irqsave(&cru->qlock, flags); - } icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; if (icnms) @@ -400,8 +396,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) /* Resets the image processing module */ rzg2l_cru_write(cru, CRUnRST, 0); - - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) @@ -469,8 +463,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) csi_vc = ret; cru->svc_channel = csi_vc; - guard(spinlock_irqsave)(&cru->qlock); - /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); From 84a4664d8c57318757f4cb86998a7e592b0dc376 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 12 Feb 2026 15:38:38 +0100 Subject: [PATCH 1095/5214] media: rzg2l-cru: Do not use irqsave when not needed The return_unused_buffers() and rzg2l_cru_buffer_queue() functions are never called from an interrupt context, hence they do not need to use the irqsave version of the spinlock primitives. Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 96c71f1357f8..b2d1b6a4aaaa 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -112,7 +112,7 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, struct rzg2l_cru_buffer *buf, *node; unsigned int i; - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irq)(&cru->qlock); for (i = 0; i < cru->num_buf; i++) { if (cru->queue_buf[i]) { @@ -165,7 +165,7 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irq)(&cru->qlock); list_add_tail(to_buf_list(vbuf), &cru->buf_list); } From a8de479fabce2d0a63d958e3ff30a053c1b3c4bb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:35:35 +0100 Subject: [PATCH 1096/5214] media: rzg2l-cru: Remove wrong locking comment A function documented as "need to hold qlock before calling" actually takes the lock itself. Drop the comment and prepare to replace it with proper annotations where appropriate. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b2d1b6a4aaaa..c49131587679 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -105,7 +105,6 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) __rzg2l_cru_read_constant(cru, offset) : \ __rzg2l_cru_read(cru, offset)) -/* Need to hold qlock before calling */ static void return_unused_buffers(struct rzg2l_cru_dev *cru, enum vb2_buffer_state state) { From c3e0b2e34ef1f177a459ead54d1f66123149ec46 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:17:23 +0100 Subject: [PATCH 1097/5214] media: rz2gl-cru: Introduce a spinlock for hw operations The CRU driver uses a single spinlock to protect the buffers queue and the hardware operations. This single spinlock is held for the whole duration of the interrupt handler, causing all other driver's operations to freeze. Under heavy system stress conditions with userspace not providing buffers fast enough, this causes loss of frames. Prepare to re-work the driver locking by introducing (but not using yet) a new spinlock to protect the hardware registers programming. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 10 +++++++--- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index be4a9a4953d1..12d574182eb8 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -111,7 +111,6 @@ struct rzg2l_cru_info { * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled * @svc_channel: SVC0/1/2/3 to use for RZ/G3E - * @buf_addr: Memory addresses where current video data is written. * @notifier: V4L2 asynchronous subdevs notifier * * @ip: Image processing subdev info @@ -120,6 +119,10 @@ struct rzg2l_cru_info { * @mdev_lock: protects the count, notifier and csi members * @pad: media pad for the video device entity * + * @hw_lock: protects the slot counter, hardware programming of + * slot addresses and the @buf_addr[] list + * @buf_addr: Memory addresses where current video data is written + * * @lock: protects @queue * @queue: vb2 buffers queue * @scratch: cpu address for scratch buffer @@ -149,8 +152,6 @@ struct rzg2l_cru_dev { u8 num_buf; u8 svc_channel; - dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; - struct v4l2_async_notifier notifier; struct rzg2l_cru_ip ip; @@ -159,6 +160,9 @@ struct rzg2l_cru_dev { struct mutex mdev_lock; struct media_pad pad; + spinlock_t hw_lock; + dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + struct mutex lock; struct vb2_queue queue; void *scratch; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index c49131587679..b6616d54f8a3 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -843,6 +843,7 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) mutex_init(&cru->lock); INIT_LIST_HEAD(&cru->buf_list); + spin_lock_init(&cru->hw_lock); spin_lock_init(&cru->qlock); cru->state = RZG2L_CRU_DMA_STOPPED; From ffd977914e162fa73aa1200c998fb555221ece20 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:50:10 +0100 Subject: [PATCH 1098/5214] media: rzg2l-cru: Split hw locking from buffers Split the locking between a spinlock dedicated to protect the hardware slots programming (hw_lock) and one lock (qlock) to protect the queue of buffers submitted by userspace. Do not rework the locking strategy yet but start simply by splitting the locking in two. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b6616d54f8a3..5769dbcbd084 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -111,19 +111,21 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, struct rzg2l_cru_buffer *buf, *node; unsigned int i; - guard(spinlock_irq)(&cru->qlock); - - for (i = 0; i < cru->num_buf; i++) { - if (cru->queue_buf[i]) { - vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, - state); - cru->queue_buf[i] = NULL; + scoped_guard(spinlock_irq, &cru->hw_lock) { + for (i = 0; i < cru->num_buf; i++) { + if (cru->queue_buf[i]) { + vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, + state); + cru->queue_buf[i] = NULL; + } } } - list_for_each_entry_safe(buf, node, &cru->buf_list, list) { - vb2_buffer_done(&buf->vb.vb2_buf, state); - list_del(&buf->list); + scoped_guard(spinlock_irq, &cru->qlock) { + list_for_each_entry_safe(buf, node, &cru->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } } } @@ -203,6 +205,8 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); + guard(spinlock)(&cru->qlock); + if (list_empty(&cru->buf_list)) { cru->queue_buf[slot] = NULL; phys_addr = cru->scratch_phys; @@ -341,7 +345,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) unsigned int retries = 0; u32 icnms; - scoped_guard(spinlock_irq, &cru->qlock) { + scoped_guard(spinlock_irq, &cru->hw_lock) { /* Disable and clear the interrupt */ cru->info->disable_interrupts(cru); } @@ -559,7 +563,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) u32 amnmbs; int slot; - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irqsave)(&cru->hw_lock); irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) @@ -661,7 +665,7 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - guard(spinlock)(&cru->qlock); + guard(spinlock)(&cru->hw_lock); irq_status = rzg2l_cru_read(cru, CRUnINTS2); if (!irq_status) From f067b1c9c25079c182aebf50d33b8bc8c49e3a1b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:55:00 +0100 Subject: [PATCH 1099/5214] media: rzg2l-cru: Manually track active slot number The CRU cycles over the hardware slots where the destination address for the next frame has to be programmed. The RZ/G2L version of the IP has a register that tells which is the last used slot by the hardware but, unfortunately, such register is not available on RZ/G3E and RZ/V2H(P). The driver currently compares the value of the AMnMADRSL/H register which report "the memory address which the current video data was written to" and compares it with the address programmed in the slots. This heuristic requires a bit of book keeping and proper locking. As the driver handles the FrameEnd interrupt, it's way easier to keep track of the slot that has been used by ourselves with a driver variable. Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 6 ++-- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 31 ++----------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 12d574182eb8..25f17069585c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -119,9 +119,10 @@ struct rzg2l_cru_info { * @mdev_lock: protects the count, notifier and csi members * @pad: media pad for the video device entity * - * @hw_lock: protects the slot counter, hardware programming of - * slot addresses and the @buf_addr[] list + * @hw_lock: protects the @active_slot counter, hardware programming + * of slot addresses and the @buf_addr[] list * @buf_addr: Memory addresses where current video data is written + * @active_slot: The slot in use * * @lock: protects @queue * @queue: vb2 buffers queue @@ -162,6 +163,7 @@ struct rzg2l_cru_dev { spinlock_t hw_lock; dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + unsigned int active_slot; struct mutex lock; struct vb2_queue queue; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 5769dbcbd084..b02940369a18 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -634,31 +634,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) return IRQ_RETVAL(handled); } -static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru) -{ - u64 amnmadrs; - int slot; - - /* - * When AMnMADRSL is read, AMnMADRSH of the higher-order - * address also latches the address. - * - * AMnMADRSH must be read after AMnMADRSL has been read. - */ - amnmadrs = rzg2l_cru_read(cru, AMnMADRSL); - amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32; - - /* Ensure amnmadrs is within this buffer range */ - for (slot = 0; slot < cru->num_buf; slot++) { - if (amnmadrs >= cru->buf_addr[slot] && - amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) - return slot; - } - - dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs); - return -EINVAL; -} - irqreturn_t rzg3e_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; @@ -690,9 +665,8 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) return IRQ_HANDLED; } - slot = rzg3e_cru_get_current_slot(cru); - if (slot < 0) - return IRQ_HANDLED; + slot = cru->active_slot; + cru->active_slot = (cru->active_slot + 1) % cru->num_buf; dev_dbg(cru->dev, "Current written slot: %d\n", slot); cru->buf_addr[slot] = 0; @@ -769,6 +743,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto assert_presetn; } + cru->active_slot = 0; cru->sequence = 0; ret = rzg2l_cru_set_stream(cru, 1); From 1b0e65a1bd7e2673ae40f0adc07349936a17d461 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 10:57:11 +0100 Subject: [PATCH 1100/5214] media: rz2gl-cru: Return pending buffers in order Buffers are programmed into slots in queueing order. When returning pending buffers we can't simply start from the first slot but we should actually iterate slots starting from the one is use. The rzg3e_cru_irq() handler already uses 'active_slot', make rzg2l_cru_irq() use it as well to know where to start iterating from. As the pattern of iterating over slots in order will be used for slots programming in the next patches, provide an helper macro to do that. While at it, rename return_unused_buffers() to rzg2l_cru_return_buffers(). Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b02940369a18..715628878b90 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -42,6 +42,24 @@ struct rzg2l_cru_buffer { #define to_buf_list(vb2_buffer) \ (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) +/* + * The CRU hardware cycles over its slots when transferring frames. All drivers + * structure that contains programming data for the slots, such as the memory + * destination addresses have to be iterated as they were circular buffers. + * + * Provide here utilities to iterate over slots and the associated data. + */ +static inline unsigned int rzg2l_cru_slot_next(struct rzg2l_cru_dev *cru, + unsigned int slot) +{ + return (slot + 1) % cru->num_buf; +} + +/* Start cycling on cru slots from the one after 'start'. */ +#define for_each_cru_slot_from(cru, slot, start) \ + for ((slot) = rzg2l_cru_slot_next((cru), (start)); \ + (slot) != (start); (slot) = rzg2l_cru_slot_next((cru), (slot))) + /* ----------------------------------------------------------------------------- * DMA operations */ @@ -105,27 +123,35 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) __rzg2l_cru_read_constant(cru, offset) : \ __rzg2l_cru_read(cru, offset)) -static void return_unused_buffers(struct rzg2l_cru_dev *cru, - enum vb2_buffer_state state) +static void rzg2l_cru_return_buffers(struct rzg2l_cru_dev *cru, + enum vb2_buffer_state state) { struct rzg2l_cru_buffer *buf, *node; - unsigned int i; scoped_guard(spinlock_irq, &cru->hw_lock) { - for (i = 0; i < cru->num_buf; i++) { - if (cru->queue_buf[i]) { - vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, - state); - cru->queue_buf[i] = NULL; - } + /* Return the buffer in progress first, if not completed yet. */ + unsigned int slot = cru->active_slot; + + if (cru->queue_buf[slot]) { + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; + } + + /* Return all the pending buffers after the active one. */ + for_each_cru_slot_from(cru, slot, cru->active_slot) { + if (!cru->queue_buf[slot]) + continue; + + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; } } - scoped_guard(spinlock_irq, &cru->qlock) { - list_for_each_entry_safe(buf, node, &cru->buf_list, list) { - vb2_buffer_done(&buf->vb.vb2_buf, state); - list_del(&buf->list); - } + guard(spinlock_irq)(&cru->qlock); + + list_for_each_entry_safe(buf, node, &cru->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); } } @@ -588,16 +614,16 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for capture and update state */ amnmbs = rzg2l_cru_read(cru, AMnMBS); - slot = amnmbs & AMnMBS_MBSTS; + cru->active_slot = amnmbs & AMnMBS_MBSTS; /* * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). * Recalculate to get the current transfer complete MB. */ - if (slot == 0) + if (cru->active_slot == 0) slot = cru->num_buf - 1; else - slot--; + slot = cru->active_slot - 1; /* * To hand buffers back in a known order to userspace start @@ -666,7 +692,7 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) } slot = cru->active_slot; - cru->active_slot = (cru->active_slot + 1) % cru->num_buf; + cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); cru->buf_addr[slot] = 0; @@ -737,7 +763,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); dev_err(cru->dev, "Failed to allocate scratch buffer\n"); ret = -ENOMEM; goto assert_presetn; @@ -748,7 +774,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count ret = rzg2l_cru_set_stream(cru, 1); if (ret) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); goto out; } @@ -785,7 +811,7 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); - return_unused_buffers(cru, VB2_BUF_STATE_ERROR); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_ERROR); reset_control_assert(cru->presetn); clk_disable_unprepare(cru->vclk); From 145b8d16cc4a450d2f890935dc7617b05caf566c Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 18 Sep 2025 12:02:06 +0100 Subject: [PATCH 1101/5214] media: rzg2l-cru: Rework rzg2l_cru_fill_hw_slot() The current implementation of rzg2l_cru_fill_hw_slot() results in the artificial loss of frames. At present whenever a frame-complete IRQ is received the driver fills the hardware slot that was just written to with the address of the next buffer in the driver's queue. If the queue is empty, that hardware slot's address is set to the address of the scratch buffer to enable the capture loop to keep running. There is a minimum of a two-frame delay before that slot will be written to however, and in the intervening period userspace may queue more buffers which could be used. To resolve the issue rework rzg2l_cru_fill_hw_slot() so that it iteratively fills all slots from the queue which currently do not have a buffer assigned, until the queue is empty. The scratch buffer is only resorted to in the event that the queue is empty and the next slot that will be written to does not already have a buffer assigned. Signed-off-by: Daniel Scally Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 715628878b90..c375cf9e261e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -214,47 +214,52 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, } /* - * Moves a buffer from the queue to the HW slot. If no buffer is - * available use the scratch buffer. The scratch buffer is never - * returned to userspace, its only function is to enable the capture - * loop to keep running. + * Move as many buffers as possible from the queue to HW slots If no buffer is + * available use the scratch buffer. The scratch buffer is never returned to + * userspace, its only function is to enable the capture loop to keep running. + * + * @cru: the CRU device + * @slot: the slot that has just completed */ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) { - struct vb2_v4l2_buffer *vbuf; struct rzg2l_cru_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + unsigned int next_slot; dma_addr_t phys_addr; - /* A already populated slot shall never be overwritten. */ - if (WARN_ON(cru->queue_buf[slot])) - return; + lockdep_assert_held(&cru->hw_lock); - dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); + /* Find the next slot which hasn't a valid address programmed. */ + for_each_cru_slot_from(cru, next_slot, slot) { + if (cru->queue_buf[next_slot]) + continue; - guard(spinlock)(&cru->qlock); + scoped_guard(spinlock_irqsave, &cru->qlock) { + buf = list_first_entry_or_null(&cru->buf_list, + struct rzg2l_cru_buffer, list); + if (buf) + list_del_init(&buf->list); + } + + if (!buf) { + /* Direct frames to the scratch buffer. */ + phys_addr = cru->scratch_phys; + cru->queue_buf[next_slot] = NULL; + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); + return; + } - if (list_empty(&cru->buf_list)) { - cru->queue_buf[slot] = NULL; - phys_addr = cru->scratch_phys; - } else { - /* Keep track of buffer we give to HW */ - buf = list_entry(cru->buf_list.next, - struct rzg2l_cru_buffer, list); vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - cru->queue_buf[slot] = vbuf; - - /* Setup DMA */ + cru->queue_buf[next_slot] = vbuf; phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); } - - rzg2l_cru_set_slot_addr(cru, slot, phys_addr); } static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { const struct rzg2l_cru_info *info = cru->info; - unsigned int slot; u32 amnaxiattr; /* @@ -263,8 +268,14 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) */ rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); - for (slot = 0; slot < cru->num_buf; slot++) - rzg2l_cru_fill_hw_slot(cru, slot); + /* + * Program slot#0 with the first available buffer, if any. Pass to the + * function 'num_buf - 1' as rzg2l_cru_fill_hw_slot() calculates which + * is the next slot to program. + */ + scoped_guard(spinlock_irq, &cru->hw_lock) { + rzg2l_cru_fill_hw_slot(cru, cru->num_buf - 1); + } if (info->has_stride) { u32 stride = cru->format.bytesperline; @@ -695,7 +706,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); - cru->buf_addr[slot] = 0; /* * To hand buffers back in a known order to userspace start From 85d8820d486939a5b3398de4ed34388c7d36f251 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 15:27:37 +0100 Subject: [PATCH 1102/5214] media: rzg2l-cru: Remove the 'state' variable The cru driver uses a 'state' variable for debugging purpose in the interrupt handler. The state is used to detect invalid usage conditions that are not meant to happen unless the driver has a bug in handling the stop and start conditions. Remove the state variable which seems to be a debugging leftover. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Tested-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 15 ---- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 71 +------------------ 2 files changed, 3 insertions(+), 83 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 25f17069585c..5bf334e173d2 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -38,20 +38,6 @@ enum rzg2l_csi2_pads { struct rzg2l_cru_dev; -/** - * enum rzg2l_cru_dma_state - DMA states - * @RZG2L_CRU_DMA_STOPPED: No operation in progress - * @RZG2L_CRU_DMA_STARTING: Capture starting up - * @RZG2L_CRU_DMA_RUNNING: Operation in progress have buffers - * @RZG2L_CRU_DMA_STOPPING: Stopping operation - */ -enum rzg2l_cru_dma_state { - RZG2L_CRU_DMA_STOPPED = 0, - RZG2L_CRU_DMA_STARTING, - RZG2L_CRU_DMA_RUNNING, - RZG2L_CRU_DMA_STOPPING, -}; - struct rzg2l_cru_csi { struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; @@ -174,7 +160,6 @@ struct rzg2l_cru_dev { struct vb2_v4l2_buffer *queue_buf[RZG2L_CRU_HW_BUFFER_MAX]; struct list_head buf_list; unsigned int sequence; - enum rzg2l_cru_dma_state state; struct v4l2_pix_format format; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index c375cf9e261e..fe8ab1ffea81 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -398,8 +398,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) if (icnms) dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); - cru->state = RZG2L_CRU_DMA_STOPPED; - /* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { if (cru->info->fifo_empty(cru)) @@ -587,8 +585,6 @@ static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) { - cru->state = RZG2L_CRU_DMA_STOPPING; - rzg2l_cru_set_stream(cru, 0); } @@ -600,8 +596,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) u32 amnmbs; int slot; - guard(spinlock_irqsave)(&cru->hw_lock); - irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) return IRQ_RETVAL(handled); @@ -610,20 +604,9 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_RETVAL(handled); - } + /* Calculate slot and prepare for new capture. */ + guard(spinlock_irqsave)(&cru->hw_lock); - /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS_SFS) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_RETVAL(handled); - } - - /* Prepare for capture and update state */ amnmbs = rzg2l_cru_read(cru, AMnMBS); cru->active_slot = amnmbs & AMnMBS_MBSTS; @@ -636,20 +619,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) else slot = cru->active_slot - 1; - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_RETVAL(handled); - } - - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - /* Capture frame */ if (cru->queue_buf[slot]) { cru->queue_buf[slot]->field = cru->format.field; @@ -677,49 +646,18 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - guard(spinlock)(&cru->hw_lock); - irq_status = rzg2l_cru_read(cru, CRUnINTS2); if (!irq_status) return IRQ_NONE; - dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); - rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_HANDLED; - } - - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FExS(0) || - irq_status & CRUnINTS2_FExS(1) || - irq_status & CRUnINTS2_FExS(2) || - irq_status & CRUnINTS2_FExS(3)) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_HANDLED; - } - + guard(spinlock)(&cru->hw_lock); slot = cru->active_slot; cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_HANDLED; - } - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - /* Capture frame */ if (cru->queue_buf[slot]) { struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; @@ -788,7 +726,6 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto out; } - cru->state = RZG2L_CRU_DMA_STARTING; dev_dbg(cru->dev, "Starting to capture\n"); return 0; @@ -861,8 +798,6 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) spin_lock_init(&cru->hw_lock); spin_lock_init(&cru->qlock); - cru->state = RZG2L_CRU_DMA_STOPPED; - for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) cru->queue_buf[i] = NULL; From acb6a00aa3055f13f817829b68661e8d490babe3 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 31 Mar 2026 10:03:47 +0200 Subject: [PATCH 1103/5214] media: rzg2l-cru: Remove debug printouts from irq Using dev_dbg() in irq handlers to debug per-frame events is marginally useful and possibly not the best idea, as using printk-based helpers introduce latencies that impact the drivers operations. If any tracing/debugging has to be performed around frame events in interrupt handlers, the tracing subsystem offers better alternatives. Drop dev_dgb() calls from the CRU interrupt handlers. Reviewed-by: Daniel Scally Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index fe8ab1ffea81..b6990466165e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -627,9 +627,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); } cru->sequence++; @@ -656,8 +653,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) slot = cru->active_slot; cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); - dev_dbg(cru->dev, "Current written slot: %d\n", slot); - /* Capture frame */ if (cru->queue_buf[slot]) { struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; @@ -667,9 +662,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) buf->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); } cru->sequence++; From 8cb2d88fa5526898a48881956b62c6618635d157 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 26 Mar 2026 16:07:41 +0100 Subject: [PATCH 1104/5214] media: rzg2l-cru: Simplify irq return value handling The rzg2l_cru_irq() irq handler uses a local variable to store the handler return value. Simplify it by using IRQ_NONE and IRQ_HANDLED. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b6990466165e..5185a547461d 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -591,16 +591,13 @@ static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; - unsigned int handled = 0; u32 irq_status; u32 amnmbs; int slot; irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) - return IRQ_RETVAL(handled); - - handled = 1; + return IRQ_NONE; rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); @@ -634,7 +631,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for next frame */ rzg2l_cru_fill_hw_slot(cru, slot); - return IRQ_RETVAL(handled); + return IRQ_HANDLED; } irqreturn_t rzg3e_cru_irq(int irq, void *data) From b4e6ddce16b8ae0ddbec7c96c675779260ad4697 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 28 Mar 2026 17:34:51 +0100 Subject: [PATCH 1105/5214] media: rzv2h-ivc: Wait for frame end in stop_streaming The rzv2h-ivc driver fails to handle back-2-back streaming sessions that do not go through a peripheral reset. As the driver uses an autosuspend delay of 2 seconds, it is quite possible that two consecutive streaming sessions won't go through a suspend/resume sequence. If the peripheral is not reset the second streaming session hangs and no frames are delivered to the ISP. This is because the stop_streaming() procedure implemented in the driver doesn't match what's prescribed by the chip datasheet: 1) The chip manual suggests to poll the RZV2H_IVC_FM_INT_STAT_STPEND bit of RZV2H_IVC_REG_FM_INT_STA instead of polling on RZV2H_IVC_REG_FM_STOP and prescribes to clear the bit after polling has completed 2) More importantly: the RZV2H_IVC_REG_FM_STOP_FSTOP bit has to be set on RZV2H_IVC_REG_FM_STOP -only- if a frame transfer to the ISP is in progress. Setting the RZV2H_IVC_REG_FM_STOP_FSTOP bit when no frame is being transferred causes the polling routine to timeout and the next streaming session fails to start As a frame transfer of an image in 1920x1080@10bi takes 5 milliseconds at most, it is quite possible that the frame transfer completion interrupt races with the stop procedure. Instead of forcing a frame transfer abort, simply wait for the in-progress transfer to complete by polling the ivc->vvalid_ifp status variable in an hand-rolled loop that allows to inspect the variable while holding the spinlock, to allow the irq handler to complete the current buffer. With this change, streaming back-2-back without suspending the peripheral works successfully. Cc: stable@vger.kernel.org Fixes: f0b3984d821b ("media: platform: Add Renesas Input Video Control block driver") Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../renesas/rzv2h-ivc/rzv2h-ivc-video.c | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c index b167f1bab7ef..932fed38cf3f 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c @@ -297,12 +297,33 @@ static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count) static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) { struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); - u32 val = 0; + unsigned int loop = 5; - rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP); - readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, - val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP), - 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + /* + * If no frame transfer is in progress, we're done, otherwise, wait for + * the transfer to complete. + * + * Transferring a 1920x1080@10bit frame to the ISP takes less than 5 + * msec so sleep for 2.5 msec (+- 25%) and give up after 5 attempts. + */ + for (; loop > 0; loop--) { + unsigned int vvalid_ifp; + + /* + * Inspect the ivc->vvalid_ifp variable holding the spinlock not + * to the race with the rzv2h_ivc_buffer_done() call in the irq + * handler. + */ + scoped_guard(spinlock_irq, &ivc->spinlock) { + vvalid_ifp = ivc->vvalid_ifp; + } + if (vvalid_ifp < 2) + break; + + fsleep(2500); + } + if (!loop) + dev_err(ivc->dev, "Failed to stop streaming\n"); rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); video_device_pipeline_stop(&ivc->vdev.dev); From e398a06907913d11096348fd220b986110ce300a Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 18 Nov 2025 10:04:30 +0800 Subject: [PATCH 1106/5214] media: mali-c55: Remove unneeded semicolon Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index c1a562cd214e..25dc3226d081 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -458,7 +458,7 @@ static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55) if (ret) { dev_err(mali_c55->dev, "failed to register V4L2 device\n"); goto err_unregister_media_device; - }; + } mali_c55->notifier.ops = &mali_c55_notifier_ops; v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev); From 5a11410b3de199d4c1d23f453982777e9c0396f8 Mon Sep 17 00:00:00 2001 From: "jempty.liang" Date: Tue, 13 Jan 2026 07:57:22 +0000 Subject: [PATCH 1107/5214] media: mali-c55: Initialise dev for tpg/rsz/isp subdevs The subdevices registered by the Mali-C55 driver do not have their 'struct device *dev' member initialized. This is visibile when looking at debug message, as in example: "(NULL device *): collect_streams: sub-device 'mali-c55 tpg' does not support streams" Fix this by initializing the *dev field for each subdevice registered by the Mali-C55 driver. Signed-off-by: jempty.liang Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-isp.c | 1 + drivers/media/platform/arm/mali-c55/mali-c55-resizer.c | 1 + drivers/media/platform/arm/mali-c55/mali-c55-tpg.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index 4c0fd1ec741c..dfa5dbe68c46 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -583,6 +583,7 @@ int mali_c55_register_isp(struct mali_c55 *mali_c55) sd->entity.ops = &mali_c55_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; sd->internal_ops = &mali_c55_isp_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name)); isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK | diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c index a8d739af74b6..c4f46651dcee 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -1070,6 +1070,7 @@ static int mali_c55_register_resizer(struct mali_c55 *mali_c55, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->internal_ops = &mali_c55_resizer_internal_ops; + sd->dev = mali_c55->dev; rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK; rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c index 1af5d2759a83..894f4cf377af 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c @@ -370,6 +370,7 @@ int mali_c55_register_tpg(struct mali_c55 *mali_c55) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; sd->internal_ops = &mali_c55_tpg_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name)); pad->flags = MEDIA_PAD_FL_SOURCE; From ff57ee85854280c6c0662559ed287591b19fc1d5 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 15 Jan 2026 10:35:16 +0800 Subject: [PATCH 1108/5214] media: mali-c55: core: Remove redundant dev_err() The platform_get_irq_byname() function already prints an error message internally upon failure using dev_err_probe(). Therefore, the explicit dev_err() is redundant and results in duplicate error logs. Remove the redundant dev_err() call to clean up the error path. Signed-off-by: Chen Ni Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 25dc3226d081..6505d3be9807 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -833,7 +833,6 @@ static int mali_c55_probe(struct platform_device *pdev) mali_c55->irqnum = platform_get_irq(pdev, 0); if (mali_c55->irqnum < 0) { ret = mali_c55->irqnum; - dev_err(dev, "failed to get interrupt\n"); goto err_deinit_media_frameworks; } From 94c6402e423d36a2bd6f62055a65a0d439d84da7 Mon Sep 17 00:00:00 2001 From: Alper Ak Date: Sat, 7 Feb 2026 12:18:22 +0300 Subject: [PATCH 1109/5214] media: mali-c55: Fix possible ERR_PTR in enable_streams The media_pad_remote_pad_unique() function returns either a valid pointer or an ERR_PTR() on failure (-ENOTUNIQ if multiple links are enabled, -ENOLINK if no connected pad is found). The return value was assigned directly to isp->remote_src and dereferenced in the next line without checking for errors, which could lead to an ERR_PTR dereference. Add proper error checking with IS_ERR() before dereferencing the pointer. Also set isp->remote_src to NULL on error to maintain consistency with other error paths in the function. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: Alper Ak Reviewed-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-isp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index dfa5dbe68c46..e128adf6ee37 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -333,6 +333,13 @@ static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd, sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO]; isp->remote_src = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(isp->remote_src)) { + ret = PTR_ERR(isp->remote_src); + dev_err(mali_c55->dev, "Failed to get remote source pad: %d\n", ret); + isp->remote_src = NULL; + return ret; + } + src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity); isp->frame_sequence = 0; From 38e3509dd98d7e970db87e13f8ec7412852a6967 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:50 +0000 Subject: [PATCH 1110/5214] media: mali-c55: Add missing of_reserved_mem_device_release() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mali_c55_probe() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. This function allocates a struct rmem_assigned_device and adds it to a global linked list, which must be explicitly released via of_reserved_mem_device_release() — there is no devm variant of this API. However, neither the probe error paths nor mali_c55_remove() called of_reserved_mem_device_release(). Any probe failure after the of_reserved_mem_device_init() call, as well as every normal device removal, leaked the reserved memory association on the global list. Fix this by adding an err_release_mem label at the end of the probe error chain and calling of_reserved_mem_device_release() in mali_c55_remove(). The remove teardown order is also corrected to call mali_c55_media_frameworks_deinit() before kfree(), mirroring the probe init order in reverse. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 6505d3be9807..305b8cbb9d1c 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -806,8 +806,10 @@ static int mali_c55_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(dev, UINT_MAX); ret = __mali_c55_power_on(mali_c55); - if (ret) - return dev_err_probe(dev, ret, "failed to power on\n"); + if (ret) { + dev_err_probe(dev, ret, "failed to power on\n"); + goto err_release_mem; + } ret = mali_c55_check_hwcfg(mali_c55); if (ret) @@ -845,6 +847,8 @@ static int mali_c55_probe(struct platform_device *pdev) kfree(mali_c55->context.registers); err_power_off: __mali_c55_power_off(mali_c55); +err_release_mem: + of_reserved_mem_device_release(dev); return ret; } @@ -853,8 +857,9 @@ static void mali_c55_remove(struct platform_device *pdev) { struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); - kfree(mali_c55->context.registers); mali_c55_media_frameworks_deinit(mali_c55); + kfree(mali_c55->context.registers); + of_reserved_mem_device_release(&pdev->dev); } static const struct of_device_id mali_c55_of_match[] = { From 2c9b9bcc2569f52e366ec71ca012542e161f1f8d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:51 +0000 Subject: [PATCH 1111/5214] media: mali-c55: Power-off the peripheral in remove() The Mali C55 driver doesn't depend on PM. For this reason, if pm_runtime is not compiled in it is required to manually power-off the peripheral during the driver's remove() handler. Also pm_runtime_enable() is called during probe but mali_c55_remove() never calls pm_runtime_disable(), leaving the device's runtime PM state enabled after the driver is unbound. Manually power-off the peripheral in remove() if the peripheral has not been suspended using runtime_pm and disable runtime pm. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 305b8cbb9d1c..00aed62c63d9 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -858,6 +858,11 @@ static void mali_c55_remove(struct platform_device *pdev) struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); mali_c55_media_frameworks_deinit(mali_c55); + if (!pm_runtime_suspended(&pdev->dev)) { + __mali_c55_power_off(mali_c55); + pm_runtime_set_suspended(&pdev->dev); + } + pm_runtime_disable(&pdev->dev); kfree(mali_c55->context.registers); of_reserved_mem_device_release(&pdev->dev); } From a1db83cc6f7e88a166c77d9060507ec01d617784 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:52 +0000 Subject: [PATCH 1112/5214] media: mali-c55: Disable pm_runtime on probe error When mali_c55_media_frameworks_init() fails, the goto target jumps to err_free_context_registers, skipping pm_runtime_disable() despite pm_runtime having already been enabled earlier in the function. Fix this by adding an err_pm_runtime_disable label and redirecting the frameworks init failure to it, so pm_runtime is properly unwound on that error path. The runtime PM status is also set back to suspended before disabling, to undo the pm_runtime_set_active() from probe. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 00aed62c63d9..ee4a4267415e 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -828,7 +828,7 @@ static int mali_c55_probe(struct platform_device *pdev) ret = mali_c55_media_frameworks_init(mali_c55); if (ret) - goto err_free_context_registers; + goto err_pm_runtime_disable; pm_runtime_idle(&pdev->dev); @@ -842,8 +842,9 @@ static int mali_c55_probe(struct platform_device *pdev) err_deinit_media_frameworks: mali_c55_media_frameworks_deinit(mali_c55); +err_pm_runtime_disable: + pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); -err_free_context_registers: kfree(mali_c55->context.registers); err_power_off: __mali_c55_power_off(mali_c55); From f546912bcac6463a22c5825e27a7952f8b48c887 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Wed, 4 Mar 2026 16:06:23 -0300 Subject: [PATCH 1113/5214] phy: qcom: qmp-combo: Move pipe_clk on/off to common Keep the USB pipe clock working when the phy is in DP-only mode, because the dwc controller still needs it for USB 2.0 over the same Type-C port. Tested with the BenQ RD280UA monitor which has a downstream-facing port for data passthrough that's manually switchable between USB 2 and 3, corresponding to 4-lane and 2-lane DP respectively. Note: the suspend/resume callbacks were already gating the enable/disable of this clock only on init_count and not usb_init_count! Reviewed-by: Neil Armstrong Reviewed-by: Konrad Dybcio Signed-off-by: Val Packett Link: https://patch.msgid.link/20260304190827.176988-1-val@packett.cool Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 93f1aa10d400..cdcfad2e86b1 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -3691,6 +3691,13 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) if (ret) goto err_assert_reset; + /* In DP-only mode, the pipe clk is still required for USB2 */ + ret = clk_prepare_enable(qmp->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + goto err_disable_clocks; + } + qphy_setbits(com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, SW_PWRDN); /* override hardware control for reset of qmp phy */ @@ -3749,6 +3756,8 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) return 0; +err_disable_clocks: + clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); err_assert_reset: reset_control_bulk_assert(cfg->num_resets, qmp->resets); err_disable_regulators: @@ -3768,6 +3777,7 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force) reset_control_bulk_assert(cfg->num_resets, qmp->resets); + clk_disable_unprepare(qmp->pipe_clk); clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); regulator_bulk_disable(cfg->num_vregs, qmp->vregs); @@ -3871,12 +3881,6 @@ static int qmp_combo_usb_power_on(struct phy *phy) qmp_configure(qmp->dev, serdes, cfg->serdes_tbl, cfg->serdes_tbl_num); - ret = clk_prepare_enable(qmp->pipe_clk); - if (ret) { - dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); - return ret; - } - /* Tx, Rx, and PCS configurations */ qmp_configure_lane(qmp->dev, tx, cfg->tx_tbl, cfg->tx_tbl_num, 1); qmp_configure_lane(qmp->dev, tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2); @@ -3922,8 +3926,6 @@ static int qmp_combo_usb_power_off(struct phy *phy) struct qmp_combo *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; - clk_disable_unprepare(qmp->pipe_clk); - /* PHY reset */ qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); From 5809de26ccab13c18d591e923acbab5edebbf283 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Wed, 29 Apr 2026 08:27:02 -0500 Subject: [PATCH 1114/5214] platform/x86: alienware-wmi-base: Transition to new WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transition to the new wmi_buffer based WMI API. Signed-off-by: Kurt Borja Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260429-aw-new-api-v5-1-7702668d04c6@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/dell/alienware-wmi-base.c | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c index 64562b92314f..19cadf21203a 100644 --- a/drivers/platform/x86/dell/alienware-wmi-base.c +++ b/drivers/platform/x86/dell/alienware-wmi-base.c @@ -12,8 +12,13 @@ #include #include #include +#include +#include #include #include + +#include + #include "alienware-wmi.h" MODULE_AUTHOR("Mario Limonciello "); @@ -150,22 +155,22 @@ u8 alienware_interface; int alienware_wmi_command(struct wmi_device *wdev, u32 method_id, void *in_args, size_t in_size, u32 *out_data) { - struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; - struct acpi_buffer in = {in_size, in_args}; - acpi_status ret; + struct wmi_buffer out, in = { + .data = in_args, + .length = in_size, + }; + int ret; - ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL); - if (ACPI_FAILURE(ret)) - return -EIO; + if (!out_data) + return wmidev_invoke_procedure(wdev, 0, method_id, &in); - union acpi_object *obj __free(kfree) = out.pointer; + ret = wmidev_invoke_method(wdev, 0, method_id, &in, &out, + sizeof(*out_data)); + if (ret) + return ret; - if (out_data) { - if (obj && obj->type == ACPI_TYPE_INTEGER) - *out_data = (u32)obj->integer.value; - else - return -ENOMSG; - } + __le32 *data __free(kfree) = out.data; + *out_data = le32_to_cpu(*data); return 0; } From 0b31f297557fff0941769e8257198151a0fbe8bf Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 27 Apr 2026 08:57:13 +0800 Subject: [PATCH 1115/5214] phy: rockchip: naneng-combphy: Consolidate SSC configuration The PCIe SSC configuration for the RK3588 and RK3576 SoCs required additional tuning which is missing. When adding these same SSC configurations for both of these two SoCs, as well as upcoming platforms, it's obvious the SSC setup code was largely duplicated across the platform-specific configuration functions. This becomes harder to maintain as more platforms are added. So extract the common SSC logic into a shared helper function, rk_combphy_common_cfg_ssc(). This cleans up the per-platform drivers and centralizes the standard configuration as possible. Reviewed-by: Neil Armstrong Reviewed-by: Heiko Stuebner Signed-off-by: Shawn Lin Link: https://patch.msgid.link/1777251433-110466-1-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Vinod Koul --- .../rockchip/phy-rockchip-naneng-combphy.c | 173 ++++++++---------- 1 file changed, 73 insertions(+), 100 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index b60d6bf3f33c..2b0f152f5470 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -121,6 +121,7 @@ #define RK3568_PHYREG32_SSC_OFFSET_500PPM 1 #define RK3568_PHYREG33 0x80 +#define RK3568_PHYREG33_PLL_SSC_CTRL BIT(5) #define RK3568_PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) #define RK3568_PHYREG33_PLL_KVCO_SHIFT 2 #define RK3568_PHYREG33_PLL_KVCO_VALUE 2 @@ -446,6 +447,74 @@ static int rockchip_combphy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsigned long rate) +{ + struct device_node *np = priv->dev->of_node; + u32 val; + + if (!priv->enable_ssc) + return; + + /* Set SSC downward spread spectrum for PCIe and USB3 */ + if (priv->type == PHY_TYPE_PCIE || priv->type == PHY_TYPE_USB3) { + val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); + } + + /* Set SSC downward spread spectrum +500ppm for SATA in 100MHz */ + if (priv->type == PHY_TYPE_SATA && rate == REF_CLOCK_100MHz) { + val = FIELD_PREP(RK3568_PHYREG32_SSC_DIR_MASK, + RK3568_PHYREG32_SSC_DOWNWARD); + val |= FIELD_PREP(RK3568_PHYREG32_SSC_OFFSET_MASK, + RK3568_PHYREG32_SSC_OFFSET_500PPM); + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, + RK3568_PHYREG32); + } + + /* Enable SSC */ + val = readl(priv->mmio + RK3568_PHYREG8); + val |= RK3568_PHYREG8_SSC_EN; + writel(val, priv->mmio + RK3568_PHYREG8); + + /* Some SoCs need tuning PCIe SSC instead of default configuration in 24MHz */ + if (!of_device_is_compatible(np, "rockchip,rk3588-naneng-combphy") && + !of_device_is_compatible(np, "rockchip,rk3576-naneng-combphy")) + return; + + /* PLL control SSC module period should be set if need tuning */ + val = readl(priv->mmio + RK3568_PHYREG33); + val |= RK3568_PHYREG33_PLL_SSC_CTRL; + writel(val, priv->mmio + RK3568_PHYREG33); + + if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_24MHz) { + /* Set PLL loop divider */ + writel(0x00, priv->mmio + RK3576_PHYREG17); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); + + /* Set up rx_pck invert and rx msb to disable */ + writel(0x00, priv->mmio + RK3588_PHYREG27); + + /* + * Set up SU adjust signal: + * su_trim[7:0], PLL KVCO adjust bits[2:0] to min + * su_trim[15:8], PLL LPF R1 adujst bits[9:7]=3'b101 + * su_trim[23:16], CKRCV adjust + * su_trim[31:24], CKDRV adjust + */ + writel(0x90, priv->mmio + RK3568_PHYREG11); + writel(0x02, priv->mmio + RK3568_PHYREG12); + writel(0x08, priv->mmio + RK3568_PHYREG13); + writel(0x57, priv->mmio + RK3568_PHYREG14); + writel(0x40, priv->mmio + RK3568_PHYREG15); + + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); + + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3576_PHYREG33_PLL_KVCO_VALUE); + writel(val, priv->mmio + RK3568_PHYREG33); + } +} + static int rk3528_combphy_cfg(struct rockchip_combphy_priv *priv) { const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; @@ -600,21 +669,12 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: - /* Set SSC downward spread spectrum */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true); break; case PHY_TYPE_USB3: - /* Set SSC downward spread spectrum */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, - RK3568_PHYREG32); - /* Enable adaptive CTLE for USB3.0 Rx */ rockchip_combphy_updatel(priv, RK3568_PHYREG15_CTLE_EN, RK3568_PHYREG15_CTLE_EN, RK3568_PHYREG15); @@ -706,11 +766,7 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) } } - if (priv->enable_ssc) { - val = readl(priv->mmio + RK3568_PHYREG8); - val |= RK3568_PHYREG8_SSC_EN; - writel(val, priv->mmio + RK3568_PHYREG8); - } + rk_combphy_common_cfg_ssc(priv, rate); return 0; } @@ -755,11 +811,6 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: - /* Set SSC downward spread spectrum. */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); @@ -767,10 +818,6 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) break; case PHY_TYPE_USB3: - /* Set SSC downward spread spectrum. */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT, - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - /* Enable adaptive CTLE for USB3.0 Rx. */ val = readl(priv->mmio + RK3568_PHYREG15); val |= RK3568_PHYREG15_CTLE_EN; @@ -880,13 +927,6 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); - } else if (priv->type == PHY_TYPE_SATA) { - /* downward spread spectrum +500ppm */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - val |= RK3568_PHYREG32_SSC_OFFSET_500PPM << - RK3568_PHYREG32_SSC_OFFSET_SHIFT; - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, - RK3568_PHYREG32); } break; @@ -909,11 +949,7 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) } } - if (priv->enable_ssc) { - val = readl(priv->mmio + RK3568_PHYREG8); - val |= RK3568_PHYREG8_SSC_EN; - writel(val, priv->mmio + RK3568_PHYREG8); - } + rk_combphy_common_cfg_ssc(priv, rate); return 0; } @@ -972,10 +1008,6 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: - /* Set SSC downward spread spectrum */ - val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); @@ -983,10 +1015,6 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) break; case PHY_TYPE_USB3: - /* Set SSC downward spread spectrum */ - val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - /* Enable adaptive CTLE for USB3.0 Rx */ val = readl(priv->mmio + RK3568_PHYREG15); val |= RK3568_PHYREG15_CTLE_EN; @@ -1110,14 +1138,6 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) writel(0x88, priv->mmio + RK3568_PHYREG13); writel(0x56, priv->mmio + RK3568_PHYREG14); } else if (priv->type == PHY_TYPE_SATA) { - /* downward spread spectrum +500ppm */ - val = FIELD_PREP(RK3568_PHYREG32_SSC_DIR_MASK, - RK3568_PHYREG32_SSC_DOWNWARD); - val |= FIELD_PREP(RK3568_PHYREG32_SSC_OFFSET_MASK, - RK3568_PHYREG32_SSC_OFFSET_500PPM); - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, - RK3568_PHYREG32); - /* ssc ppm adjust to 3500ppm */ rockchip_combphy_updatel(priv, RK3576_PHYREG10_SSC_PCM_MASK, RK3576_PHYREG10_SSC_PCM_3500PPM, @@ -1156,39 +1176,7 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) } } - if (priv->enable_ssc) { - val = readl(priv->mmio + RK3568_PHYREG8); - val |= RK3568_PHYREG8_SSC_EN; - writel(val, priv->mmio + RK3568_PHYREG8); - - if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_24MHz) { - /* Set PLL loop divider */ - writel(0x00, priv->mmio + RK3576_PHYREG17); - writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); - - /* Set up rx_pck invert and rx msb to disable */ - writel(0x00, priv->mmio + RK3588_PHYREG27); - - /* - * Set up SU adjust signal: - * su_trim[7:0], PLL KVCO adjust bits[2:0] to min - * su_trim[15:8], PLL LPF R1 adujst bits[9:7]=3'b101 - * su_trim[23:16], CKRCV adjust - * su_trim[31:24], CKDRV adjust - */ - writel(0x90, priv->mmio + RK3568_PHYREG11); - writel(0x02, priv->mmio + RK3568_PHYREG12); - writel(0x08, priv->mmio + RK3568_PHYREG13); - writel(0x57, priv->mmio + RK3568_PHYREG14); - writel(0x40, priv->mmio + RK3568_PHYREG15); - - writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); - - val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, - RK3576_PHYREG33_PLL_KVCO_VALUE); - writel(val, priv->mmio + RK3568_PHYREG33); - } - } + rk_combphy_common_cfg_ssc(priv, rate); return 0; } @@ -1255,10 +1243,6 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) } break; case PHY_TYPE_USB3: - /* Set SSC downward spread spectrum */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); - /* Enable adaptive CTLE for USB3.0 Rx. */ val = readl(priv->mmio + RK3568_PHYREG15); val |= RK3568_PHYREG15_CTLE_EN; @@ -1343,13 +1327,6 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) /* Set up su_trim: */ writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); - } else if (priv->type == PHY_TYPE_SATA) { - /* downward spread spectrum +500ppm */ - val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; - val |= RK3568_PHYREG32_SSC_OFFSET_500PPM << - RK3568_PHYREG32_SSC_OFFSET_SHIFT; - rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, - RK3568_PHYREG32); } break; default: @@ -1371,11 +1348,7 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) } } - if (priv->enable_ssc) { - val = readl(priv->mmio + RK3568_PHYREG8); - val |= RK3568_PHYREG8_SSC_EN; - writel(val, priv->mmio + RK3568_PHYREG8); - } + rk_combphy_common_cfg_ssc(priv, rate); return 0; } From a62d9440ebbce3d9f0bd6c346a7fda2a53726850 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 18 May 2026 17:20:24 +0300 Subject: [PATCH 1116/5214] dt-bindings: phy: add PHY bindings for the TI DS125DF111 Retimer PHY Add device tree binding for the TI DS125DF111 Retimer PHY. Signed-off-by: Ioana Ciornei Acked-by: Conor Dooley Link: https://patch.msgid.link/20260518142026.3098496-2-ioana.ciornei@nxp.com Signed-off-by: Vinod Koul --- .../bindings/phy/ti,ds125df111.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/ti,ds125df111.yaml diff --git a/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml b/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml new file mode 100644 index 000000000000..ca4605f1d664 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/ti,ds125df111.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI DS125DF111 Retimer PHY + +description: + This binding describes the TI DS125DF111 multi-protocol Retimer PHY. + +maintainers: + - Ioana Ciornei + +properties: + compatible: + const: ti,ds125df111 + + reg: + maxItems: 1 + + "#phy-cells": + const: 1 + description: | + The phandle's argument in the PHY specifier selects one of the two + channels of the retimer + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + phy@18 { + compatible = "ti,ds125df111"; + reg = <0x18>; + #phy-cells = <1>; + }; + }; From 711f64979e500799b27b33a8f030e2fd939fd07f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 18 May 2026 17:20:25 +0300 Subject: [PATCH 1117/5214] phy: ti: alphabetically sort Kconfig and Makefile Sort alphabetically the entries in the Kconfig and Makefile files. Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260518142026.3098496-3-ioana.ciornei@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/ti/Kconfig | 124 ++++++++++++++++++++-------------------- drivers/phy/ti/Makefile | 12 ++-- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index b40f28019131..dbe65500f20c 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -2,53 +2,6 @@ # # Phy drivers for TI platforms # -config PHY_DA8XX_USB - tristate "TI DA8xx USB PHY Driver" - depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST - select GENERIC_PHY - select MFD_SYSCON - help - Enable this to support the USB PHY on DA8xx SoCs. - - This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. - -config PHY_DM816X_USB - tristate "TI dm816x USB PHY driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - depends on USB_SUPPORT - select GENERIC_PHY - select USB_PHY - help - Enable this for dm816x USB to work. - -config PHY_AM654_SERDES - tristate "TI AM654 SERDES support" - depends on OF && (ARCH_K3 || COMPILE_TEST) - depends on COMMON_CLK - select GENERIC_PHY - select MULTIPLEXER - select REGMAP_MMIO - select MUX_MMIO - help - This option enables support for TI AM654 SerDes PHY used for - PCIe. - -config PHY_J721E_WIZ - tristate "TI J721E WIZ (SERDES Wrapper) support" - depends on OF && (ARCH_K3 || COMPILE_TEST) - depends on HAS_IOMEM && OF_ADDRESS - depends on COMMON_CLK - select GENERIC_PHY - select MULTIPLEXER - select REGMAP_MMIO - select MUX_MMIO - help - This option enables support for WIZ module present in TI's J721E - SoC. WIZ is a serdes wrapper used to configure some of the input - signals to the SERDES (Sierra/Torrent). This driver configures - three clock selects (pll0, pll1, dig) and resets for each of the - lanes. - config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST @@ -73,6 +26,68 @@ config OMAP_USB2 The USB OTG controller communicates with the comparator using this driver. +config PHY_AM654_SERDES + tristate "TI AM654 SERDES support" + depends on OF && (ARCH_K3 || COMPILE_TEST) + depends on COMMON_CLK + select GENERIC_PHY + select MULTIPLEXER + select REGMAP_MMIO + select MUX_MMIO + help + This option enables support for TI AM654 SerDes PHY used for + PCIe. + +config PHY_DA8XX_USB + tristate "TI DA8xx USB PHY Driver" + depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the USB PHY on DA8xx SoCs. + + This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. + +config PHY_DM816X_USB + tristate "TI dm816x USB PHY driver" + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this for dm816x USB to work. + +config PHY_J721E_WIZ + tristate "TI J721E WIZ (SERDES Wrapper) support" + depends on OF && (ARCH_K3 || COMPILE_TEST) + depends on HAS_IOMEM && OF_ADDRESS + depends on COMMON_CLK + select GENERIC_PHY + select MULTIPLEXER + select REGMAP_MMIO + select MUX_MMIO + help + This option enables support for WIZ module present in TI's J721E + SoC. WIZ is a serdes wrapper used to configure some of the input + signals to the SERDES (Sierra/Torrent). This driver configures + three clock selects (pll0, pll1, dig) and resets for each of the + lanes. + +config PHY_TI_GMII_SEL + tristate + select GENERIC_PHY + select REGMAP + help + This driver supports configuring of the TI CPSW Port mode depending on + the Ethernet PHY connected to the CPSW Port. + +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + config TI_PIPE3 tristate "TI PIPE3 PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST @@ -84,13 +99,6 @@ config TI_PIPE3 This driver interacts with the "OMAP Control PHY Driver" to power on/off the PHY. -config PHY_TUSB1210 - tristate "TI TUSB1210 ULPI PHY module" - depends on USB_ULPI_BUS - select GENERIC_PHY - help - Support for TI TUSB1210 USB ULPI PHY. - config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS @@ -103,11 +111,3 @@ config TWL4030_USB family chips (including the TWL5030 and TPS659x0 devices). This transceiver supports high and full speed devices plus, in host mode, low speed. - -config PHY_TI_GMII_SEL - tristate - select GENERIC_PHY - select REGMAP - help - This driver supports configuring of the TI CPSW Port mode depending on - the Ethernet PHY connected to the CPSW Port. diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index dcba2571c9bd..975fb8448ba5 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o -obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o -obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o -obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o -obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_AM654_SERDES) += phy-am654-serdes.o -obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o +obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o obj-$(CONFIG_PHY_J721E_WIZ) += phy-j721e-wiz.o +obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o +obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o +obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o +obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o From 9bf5c16a6e63ebf2803deb32ba984a7fcf2c5ed7 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 18 May 2026 17:20:26 +0300 Subject: [PATCH 1118/5214] phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer Add a generic PHY driver for the TI DS125DF111 Multi-Protocol Dual-Channel Retimer. The driver currently supports only 10G and 1G link speeds but it can easily extended to also cover other usecases. Since the available datasheet (https://www.ti.com/lit/gpn/DS125DF111) does not name the registers, the name for the macros were determined by their usage pattern. A PHY device is created for each of the two channels present on the retimer. This allows for independent configuration of the two channels. This capability is especially important on retimers which have more than 2 channels that can be, depending on the board design, connected in multiple different ways to the SerDes lanes. Signed-off-by: Ioana Ciornei Reviewed-by: Vladimir Oltean Link: https://patch.msgid.link/20260518142026.3098496-4-ioana.ciornei@nxp.com Signed-off-by: Vinod Koul --- MAINTAINERS | 7 + drivers/phy/ti/Kconfig | 10 ++ drivers/phy/ti/Makefile | 1 + drivers/phy/ti/phy-ds125df111.c | 294 ++++++++++++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 drivers/phy/ti/phy-ds125df111.c diff --git a/MAINTAINERS b/MAINTAINERS index 8ae7578a0532..e00b43bd3444 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26684,6 +26684,13 @@ T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git F: drivers/media/platform/ti/davinci/ F: include/media/davinci/ +TI DS125DF111 RETIMER PHY DRIVER +M: Ioana Ciornei +L: linux-phy@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/phy/ti,ds125df111.yaml +F: drivers/phy/ti/phy-ds125df111.c + TI ENHANCED CAPTURE (eCAP) DRIVER M: Vignesh Raghavendra R: Julien Panis diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index dbe65500f20c..b52c7eabe24c 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -73,6 +73,16 @@ config PHY_J721E_WIZ three clock selects (pll0, pll1, dig) and resets for each of the lanes. +config PHY_TI_DS125DF111 + tristate "TI DS125DF111 2-Channel Retimer Driver" + depends on OF && I2C + select GENERIC_PHY + help + Enable this to add support for configuration and runtime management + of the TI DS125DF111 Multi-Protocol 2-Channel Retimer. + The retimer is modeled as a Generic PHY and supports both 10G and 1G + link speeds. + config PHY_TI_GMII_SEL tristate select GENERIC_PHY diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index 975fb8448ba5..a002ef8764a2 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PHY_AM654_SERDES) += phy-am654-serdes.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o obj-$(CONFIG_PHY_J721E_WIZ) += phy-j721e-wiz.o +obj-$(CONFIG_PHY_TI_DS125DF111) += phy-ds125df111.o obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o diff --git a/drivers/phy/ti/phy-ds125df111.c b/drivers/phy/ti/phy-ds125df111.c new file mode 100644 index 000000000000..84ff96d7d589 --- /dev/null +++ b/drivers/phy/ti/phy-ds125df111.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2026 NXP */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DS125DF111_NUM_CH 2 +#define DS125DF111_NUM_VCO_GROUP_REG 5 + +#define DS125DF111_CH_SELECT 0xff +#define DS125DF111_CH_SELECT_TARGET_MASK GENMASK(3, 0) +#define DS125DF111_CH_SELECT_EN BIT(2) + +#define DS125DF111_CH_CTRL 0x00 +#define DS125DF111_CH_CTRL_RESET BIT(2) /* self clearing */ + +#define DS125DF111_CH_RST_SLEEP_US 10 +#define DS125DF111_CH_RST_TIMEOUT_US 10000 + +#define DS125DF111_VCO_GROUP_BASE 0x60 + +#define DS125DF111_RATIOS 0x2f +#define DS125DF111_RATIOS_RATE_MASK GENMASK(7, 6) +#define DS125DF111_RATIOS_SUBRATE_MASK GENMASK(5, 4) +#define DS125DF111_RATIOS_MASK GENMASK(7, 4) + +struct ds125df111_ch { + struct phy *phy; + struct ds125df111_priv *priv; + int idx; +}; + +struct ds125df111_priv { + struct ds125df111_ch ch[DS125DF111_NUM_CH]; + struct i2c_client *client; + struct mutex mutex; /* protects access to shared registers */ +}; + +enum ds125df111_mode { + FREQ_1G, + FREQ_10G, +}; + +static const struct ds125df111_config { + u8 vco_group[DS125DF111_NUM_VCO_GROUP_REG]; + u8 rate; + u8 subrate; +} ds125df111_cfg[] = { + [FREQ_1G] = { + /* VCO group #0 = 10GHz, VCO group #1 = 10GHz */ + .vco_group = {0x00, 0xB2, 0x00, 0xB2, 0xCC}, + /* By using the following combination of rate and subrate we + * select divide ratios of 1, 2, 4, 8 on both groups + */ + .rate = 0x1, + .subrate = 0x2, + }, + + [FREQ_10G] = { + /* VCO group #0 = 10.3125GHz, VCO group #1 = 10.3125GHz */ + .vco_group = {0x90, 0xB3, 0x90, 0xB3, 0xCD}, + /* By using the following combination of rate and subrate we + * select divide ratios of 1 on both groups + */ + .rate = 0x1, + .subrate = 0x3, + }, +}; + +static int ds125df111_rmw(struct ds125df111_priv *priv, u8 reg, u8 clr, u8 set) +{ + struct i2c_client *i2c = priv->client; + int err; + u8 val; + + err = i2c_smbus_read_byte_data(i2c, reg); + if (err < 0) + return err; + + val = (u8)err; + val &= ~clr; + val |= set; + + err = i2c_smbus_write_byte_data(i2c, reg, val); + if (err < 0) + return err; + + return 0; +} + +static int ds125df111_configure(struct phy *phy, + const struct ds125df111_config *cfg) +{ + struct ds125df111_ch *ch = phy_get_drvdata(phy); + struct ds125df111_priv *priv = ch->priv; + struct i2c_client *i2c = priv->client; + struct device *dev = &phy->dev; + u8 ratios_val; + int err, i; + int val; + + mutex_lock(&priv->mutex); + + /* Make sure that any subsequent read/write operation will be directed + * only to the registers of the selected channel + */ + err = ds125df111_rmw(priv, DS125DF111_CH_SELECT, + DS125DF111_CH_SELECT_TARGET_MASK, + DS125DF111_CH_SELECT_EN | ch->idx); + if (err < 0) { + dev_err(dev, "Unable to select channel: %pe\n", ERR_PTR(err)); + goto out; + } + + /* Reset channel registers and wait until the bit was cleared */ + err = ds125df111_rmw(priv, DS125DF111_CH_CTRL, 0, + DS125DF111_CH_CTRL_RESET); + if (err < 0) { + dev_err(dev, "Error resetting channel configuration: %pe\n", + ERR_PTR(err)); + goto out; + } + + err = read_poll_timeout(i2c_smbus_read_byte_data, val, + val < 0 || !(val & DS125DF111_CH_CTRL_RESET), + DS125DF111_CH_RST_SLEEP_US, + DS125DF111_CH_RST_TIMEOUT_US, false, i2c, + DS125DF111_CH_CTRL); + if (err) { + dev_err(dev, "Timed out waiting for channel reset: %pe\n", + ERR_PTR(err)); + goto out; + } + + if (val < 0) { + dev_err(dev, "Error reading reset status: %pe\n", ERR_PTR(val)); + err = val; + goto out; + } + + /* Program the VCO group frequencies */ + for (i = 0; i < DS125DF111_NUM_VCO_GROUP_REG; i++) { + err = i2c_smbus_write_byte_data(i2c, + DS125DF111_VCO_GROUP_BASE + i, + cfg->vco_group[i]); + if (err < 0) { + dev_err(dev, "Error programming VCO group: %pe\n", + ERR_PTR(err)); + goto out; + } + } + + /* Set the divide ratios for the VCO groups */ + ratios_val = FIELD_PREP(DS125DF111_RATIOS_RATE_MASK, cfg->rate) | + FIELD_PREP(DS125DF111_RATIOS_SUBRATE_MASK, cfg->subrate); + err = ds125df111_rmw(priv, DS125DF111_RATIOS, DS125DF111_RATIOS_MASK, + ratios_val); + if (err < 0) { + dev_err(dev, "Error programming the divide ratios: %pe\n", + ERR_PTR(err)); + goto out; + } + +out: + mutex_unlock(&priv->mutex); + + return err; +} + +static int ds125df111_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + const struct ds125df111_config *cfg; + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_10GBASER: + cfg = &ds125df111_cfg[FREQ_10G]; + break; + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + cfg = &ds125df111_cfg[FREQ_1G]; + break; + default: + return -EINVAL; + } + + return ds125df111_configure(phy, cfg); +} + +static int ds125df111_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts __always_unused) +{ + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + switch (submode) { + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + return 0; + default: + return -EINVAL; + } +} + +static const struct phy_ops ds125df111_ops = { + .validate = ds125df111_validate, + .set_mode = ds125df111_set_mode, + .owner = THIS_MODULE, +}; + +static struct phy *ds125df111_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct ds125df111_priv *priv = dev_get_drvdata(dev); + u32 idx; + + if (args->args_count != 1) + return ERR_PTR(-EINVAL); + + idx = args->args[0]; + if (idx >= DS125DF111_NUM_CH) { + dev_err(dev, "Maximum number of channels is %d\n", + DS125DF111_NUM_CH); + return ERR_PTR(-EINVAL); + } + + return priv->ch[idx].phy; +} + +static int ds125df111_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct phy_provider *provider; + struct ds125df111_priv *priv; + int i, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->client = client; + err = devm_mutex_init(dev, &priv->mutex); + if (err) + return err; + + i2c_set_clientdata(client, priv); + + for (i = 0; i < DS125DF111_NUM_CH; i++) { + struct ds125df111_ch *ch = &priv->ch[i]; + struct phy *phy; + + phy = devm_phy_create(dev, NULL, &ds125df111_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + ch->idx = i; + ch->priv = priv; + ch->phy = phy; + + phy_set_drvdata(phy, ch); + } + + provider = devm_of_phy_provider_register(dev, ds125df111_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id ds125df111_dt_ids[] = { + { .compatible = "ti,ds125df111", }, + {} +}; +MODULE_DEVICE_TABLE(of, ds125df111_dt_ids); + +static struct i2c_driver ds125df111_driver = { + .driver = { + .name = "ds125df111", + .of_match_table = ds125df111_dt_ids, + }, + .probe = ds125df111_probe, +}; +module_i2c_driver(ds125df111_driver); + +MODULE_AUTHOR("Ioana Ciornei "); +MODULE_DESCRIPTION("TI DS125DF111 Retimer driver"); +MODULE_LICENSE("GPL"); From 4339fa4fae057c99cbcfd8127769219feb14964b Mon Sep 17 00:00:00 2001 From: Krishnamoorthi M Date: Fri, 8 May 2026 00:39:26 +0530 Subject: [PATCH 1119/5214] platform/x86/amd: hfi: Support for ranking table versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add changes to support new ranking table version. Version 2 of the heterogeneous ranking table provides static CPU rankings. Version 3 adds dynamic ranking table support on newer AMD platforms. These changes ensure that platforms still reporting version 2 continue to function with the existing static ranking path, avoiding regressions on older hardware that does not supply a dynamic ranking table. Signed-off-by: Krishnamoorthi M Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Shyam Sundar S K Link: https://patch.msgid.link/20260507190926.1211726-1-krishnamoorthi.m@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hfi/hfi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/hfi/hfi.c b/drivers/platform/x86/amd/hfi/hfi.c index 83863a5e0fbc..e0ebcb0c4acd 100644 --- a/drivers/platform/x86/amd/hfi/hfi.c +++ b/drivers/platform/x86/amd/hfi/hfi.c @@ -33,7 +33,8 @@ #define AMD_HFI_DRIVER "amd_hfi" #define AMD_HFI_MAILBOX_COUNT 1 -#define AMD_HETERO_RANKING_TABLE_VER 2 +#define AMD_HETERO_RANKING_TABLE_MIN_VER 2 +#define AMD_HETERO_RANKING_TABLE_MAX_VER 3 #define AMD_HETERO_CPUID_27 0x80000027 @@ -158,7 +159,8 @@ static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data) dev_err(amd_hfi_data->dev, "invalid signature in shared memory\n"); return -EINVAL; } - if (amd_hfi_data->shmem->version_number != AMD_HETERO_RANKING_TABLE_VER) { + if (amd_hfi_data->shmem->version_number < AMD_HETERO_RANKING_TABLE_MIN_VER || + amd_hfi_data->shmem->version_number > AMD_HETERO_RANKING_TABLE_MAX_VER) { dev_err(amd_hfi_data->dev, "invalid version %d\n", amd_hfi_data->shmem->version_number); return -EINVAL; From 293e19f416fa3f233a2fb013258f7abcb39ad6ed Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 19 May 2026 10:22:53 +0200 Subject: [PATCH 1120/5214] MAINTAINERS: Hand over phy-zynqmp to Tomi Valkeinen I volunteered to maintain the phy-zynqmp driver as part of my work on the ZynqMP DPSUB driver. Now that Tomi has taken over the DPSUB, it makes sense for him to handle the phy-zynqmp driver as well. Signed-off-by: Laurent Pinchart Acked-by: Tomi Valkeinen Acked-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260519082253.40142-1-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e00b43bd3444..06563ce3f459 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29278,7 +29278,7 @@ F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yam F: drivers/edac/zynqmp_edac.c XILINX ZYNQMP PSGTR PHY DRIVER -M: Laurent Pinchart +M: Tomi Valkeinen L: linux-kernel@vger.kernel.org S: Supported T: git https://github.com/Xilinx/linux-xlnx.git From d08048d4e03b68aeadce6100ba32a525b6e5ab3d Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 21 Apr 2026 08:40:21 -0500 Subject: [PATCH 1121/5214] ipmi:kcs: Reduce the number of retries The retry count was initially set to 10, which with the 5 second timeouts give 55 seconds to fail a message. The IPMI spec specifies the 5 second timeout, and it specifies retries for KCS but does not specify a number. 55 seconds is a long time, so reduce retries to 2. It matches the default in the BT state machine. This is 15 seconds, then, which is still a long time, but more reasonable. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_kcs_sm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index efda90dcf5b3..c89055db39e5 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -102,7 +102,7 @@ enum kcs_states { /* Timeouts in microseconds. */ #define IBF_RETRY_TIMEOUT (5*USEC_PER_SEC) #define OBF_RETRY_TIMEOUT (5*USEC_PER_SEC) -#define MAX_ERROR_RETRIES 10 +#define MAX_ERROR_RETRIES 2 #define ERROR0_OBF_WAIT_JIFFIES (2*HZ) struct si_sm_data { From cc3f0c66183022444c1ff35ad6885127200eb921 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 19 May 2026 13:57:22 +0800 Subject: [PATCH 1122/5214] ipmi: Use LIST_HEAD() to initialize on stack list head Use LIST_HEAD to initialize on stack list head. No intentional functional impact. Change generated with below coccinelle script: @@ identifier name; @@ - struct list_head name; + LIST_HEAD(name); ... when != name - INIT_LIST_HEAD(&name); Signed-off-by: Jisheng Zhang Message-ID: <20260519055722.13161-1-jszhang@kernel.org> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 869ac87a4b6a..7a4566046b68 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1610,14 +1610,12 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val) { struct ipmi_smi *intf = user->intf; struct ipmi_recv_msg *msg, *msg2; - struct list_head msgs; + LIST_HEAD(msgs); user = acquire_ipmi_user(user); if (!user) return -ENODEV; - INIT_LIST_HEAD(&msgs); - mutex_lock(&intf->events_mutex); if (user->gets_events == val) goto out; @@ -3785,10 +3783,9 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf) struct seq_table *ent; struct ipmi_smi_msg *msg; struct list_head *entry; - struct list_head tmplist; + LIST_HEAD(tmplist); /* Clear out our transmit queues and hold the messages. */ - INIT_LIST_HEAD(&tmplist); list_splice_tail(&intf->hp_xmit_msgs, &tmplist); list_splice_tail(&intf->xmit_msgs, &tmplist); @@ -4442,7 +4439,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg, *recv_msg2; - struct list_head msgs; + LIST_HEAD(msgs); struct ipmi_user *user; int rv = 0, deliver_count = 0; @@ -4457,8 +4454,6 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, return 0; } - INIT_LIST_HEAD(&msgs); - mutex_lock(&intf->events_mutex); ipmi_inc_stat(intf, events); @@ -5101,7 +5096,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, static bool ipmi_timeout_handler(struct ipmi_smi *intf, unsigned long timeout_period) { - struct list_head timeouts; + LIST_HEAD(timeouts); struct ipmi_recv_msg *msg, *msg2; unsigned long flags; int i; @@ -5120,7 +5115,6 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, * have timed out, putting them in the timeouts * list. */ - INIT_LIST_HEAD(&timeouts); mutex_lock(&intf->seq_lock); if (intf->ipmb_maintenance_mode_timeout) { if (intf->ipmb_maintenance_mode_timeout <= timeout_period) From 65efd1314dbcb37e4ab47974095ae97f90751f30 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:32 +0200 Subject: [PATCH 1123/5214] media: include/uapi/linux/cec*.h: add CEC LIP support Add support for the new Latency Indication Protocol feature. This adds the opcodes and the wrapper functions. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/8dac7c99b7eab4fcc11d76d50dd08fb4448672f4.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <8dac7c99b7eab4fcc11d76d50dd08fb4448672f4.1779115235.git.hverkuil+cisco@kernel.org> --- include/uapi/linux/cec-funcs.h | 182 +++++++++++++++++++++++++++++++++ include/uapi/linux/cec.h | 24 +++++ 2 files changed, 206 insertions(+) diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index 189ecf0e13cd..ba4b47de9bf0 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -1701,6 +1701,188 @@ static inline void cec_ops_request_current_latency(const struct cec_msg *msg, } +/* Latency Indication Protocol Feature */ +/* Only for CEC 2.0 and up */ +static inline void cec_msg_request_lip_support(struct cec_msg *msg, + int reply, __u16 phys_addr) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REQUEST_LIP_SUPPORT; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_REPORT_LIP_SUPPORT : 0; +} + +static inline void cec_ops_request_lip_support(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_report_lip_support(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_LIP_SUPPORT; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_report_lip_support(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_and_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, __u8 vrr_format, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->msg[5] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[6] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_and_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; + *audio_format = msg->msg[5]; + *audio_format_extension = msg->len > 6 ? msg->msg[6] : 0; +} + +static inline void cec_msg_report_audio_and_video_latency(struct cec_msg *msg, + __u16 video_latency, + __u16 audio_latency) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; + msg->msg[4] = audio_latency >> 8; + msg->msg[5] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_and_video_latency(const struct cec_msg *msg, + __u16 *video_latency, + __u16 *audio_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; + *audio_latency = (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_latency(struct cec_msg *msg, + int reply, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_LATENCY; + msg->msg[2] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[3] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_latency(const struct cec_msg *msg, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *audio_format = msg->msg[2]; + *audio_format_extension = msg->len > 3 ? msg->msg[3] : 0; +} + +static inline void cec_msg_report_audio_latency(struct cec_msg *msg, + __u16 audio_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_LATENCY; + msg->msg[2] = audio_latency >> 8; + msg->msg[3] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_latency(const struct cec_msg *msg, + __u16 *audio_latency) +{ + *audio_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_request_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, + __u8 vrr_format) +{ + msg->len = 5; + msg->msg[1] = CEC_MSG_REQUEST_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->reply = reply ? CEC_MSG_REPORT_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; +} + +static inline void cec_msg_report_video_latency(struct cec_msg *msg, + __u16 video_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; +} + +static inline void cec_ops_report_video_latency(const struct cec_msg *msg, + __u16 *video_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_update_sqid(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_UPDATE_SQID; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_update_sqid(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + + /* Capability Discovery and Control Feature */ static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, __u16 phys_addr1, diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index b2af1dddd4d7..75bc9e4e0350 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -1104,6 +1104,30 @@ struct cec_event { #define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3 +/* Latency Indication Protocol Feature */ +#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 +#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 +#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 +/* HDR Format Operand (hdr_format) */ +#define CEC_OP_HDR_FORMAT_GAMMA_SDR 0 +#define CEC_OP_HDR_FORMAT_GAMMA_HDR 1 +#define CEC_OP_HDR_FORMAT_PQ 2 +#define CEC_OP_HDR_FORMAT_HLG 3 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 8 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 9 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 11 +#define CEC_OP_HDR_FORMAT_DV_SINK_LED 16 +#define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17 +#define CEC_OP_HDR_FORMAT_HDR10PLUS 24 +#define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32 +#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 +#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 +#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 +#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 +#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 +#define CEC_MSG_UPDATE_SQID 0x58 + + /* Capability Discovery and Control Feature */ #define CEC_MSG_CDC_MESSAGE 0xf8 /* Ethernet-over-HDMI: nobody ever does this... */ From 300b8963c2f68a70588b086ac9137c2fae9caed5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:33 +0200 Subject: [PATCH 1124/5214] Documentation: media: add CEC opcodes Add the new opcodes to cec.h.rst.exceptions to avoid documentation build failures. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/afe5d8f6cdf2dc030011eaf9acd8f68847d1b8cf.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: --- .../media/cec/cec.h.rst.exceptions | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions index 65e8be062bdb..5d6f7747c023 100644 --- a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions +++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions @@ -524,6 +524,29 @@ ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY +ignore define CEC_MSG_REQUEST_LIP_SUPPORT +ignore define CEC_MSG_REPORT_LIP_SUPPORT +ignore define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY + +ignore define CEC_OP_HDR_FORMAT_GAMMA_SDR +ignore define CEC_OP_HDR_FORMAT_GAMMA_HDR +ignore define CEC_OP_HDR_FORMAT_PQ +ignore define CEC_OP_HDR_FORMAT_HLG +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 +ignore define CEC_OP_HDR_FORMAT_DV_SINK_LED +ignore define CEC_OP_HDR_FORMAT_DV_SOURCE_LED +ignore define CEC_OP_HDR_FORMAT_HDR10PLUS +ignore define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 + +ignore define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY +ignore define CEC_MSG_REQUEST_AUDIO_LATENCY +ignore define CEC_MSG_REPORT_AUDIO_LATENCY +ignore define CEC_MSG_REQUEST_VIDEO_LATENCY +ignore define CEC_MSG_REPORT_VIDEO_LATENCY +ignore define CEC_MSG_UPDATE_SQID + ignore define CEC_MSG_CDC_MESSAGE ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE From b300f96cb8cbda56c645e0816948803ea9674433 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:34 +0200 Subject: [PATCH 1125/5214] media: cec: core: add LIP support Add support for the new CEC LIP opcodes. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/9fb27f4a2133cf46b8e0fc126c2d1ef8b1bc30e1.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <9fb27f4a2133cf46b8e0fc126c2d1ef8b1bc30e1.1779115235.git.hverkuil+cisco@kernel.org> --- drivers/media/cec/core/cec-adap.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 8f7244ac1d43..a90cb84a4b4d 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1098,6 +1098,15 @@ static const u8 cec_msg_size[256] = { [CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST, [CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST, [CEC_MSG_CDC_MESSAGE] = 2 | BCAST, + [CEC_MSG_REQUEST_LIP_SUPPORT] = 4 | DIRECTED, + [CEC_MSG_REPORT_LIP_SUPPORT] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_LATENCY] = 3 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_REQUEST_VIDEO_LATENCY] = 5 | DIRECTED, + [CEC_MSG_REPORT_VIDEO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_UPDATE_SQID] = 6 | DIRECTED, }; /* Called by the CEC adapter if a message is received */ From 79500c929de313550554e604d22f3202955cea20 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:35 +0200 Subject: [PATCH 1126/5214] media: include/uapi/linux/cec*: clarify which msgs are CEC 2.0 Drop comments about CEC 2.0 from cec-funcs.h. In cec.h clearly comment messages that are CEC 2.0 specific as such. Also rename references to HDMI 2.0 to CEC 2.0. The messages were marked as CEC 2.0 only. That is wrong, these messages are explicitly allowed for any CEC version. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/098789ee233f777d5ad87a72f09a997373c98d25.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <098789ee233f777d5ad87a72f09a997373c98d25.1779115235.git.hverkuil+cisco@kernel.org> --- include/uapi/linux/cec.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 75bc9e4e0350..81a05c9c0706 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -742,7 +742,7 @@ struct cec_event { #define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7 #define CEC_MSG_SET_MENU_LANGUAGE 0x32 -#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */ +#define CEC_MSG_REPORT_FEATURES 0xa6 /* CEC 2.0 */ /* All Device Types Operand (all_device_types) */ #define CEC_OP_ALL_DEVTYPE_TV 0x80 #define CEC_OP_ALL_DEVTYPE_RECORD 0x40 @@ -777,7 +777,7 @@ struct cec_event { #define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02 #define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL 0x01 -#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */ +#define CEC_MSG_GIVE_FEATURES 0xa5 /* CEC 2.0 */ /* Deck Control Feature */ @@ -1067,7 +1067,7 @@ struct cec_event { #define CEC_OP_AUD_FMT_ID_CEA861 0 #define CEC_OP_AUD_FMT_ID_CEA861_CXT 1 -#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 +#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 /* CEC 2.0 */ /* Audio Rate Control Feature */ #define CEC_MSG_SET_AUDIO_RATE 0x9a @@ -1091,7 +1091,6 @@ struct cec_event { /* Dynamic Audio Lipsync Feature */ -/* Only for CEC 2.0 and up */ #define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7 #define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8 /* Low Latency Mode Operand (low_latency_mode) */ @@ -1105,9 +1104,9 @@ struct cec_event { /* Latency Indication Protocol Feature */ -#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 -#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 -#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 +#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 /* CEC 2.0 */ +#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 /* CEC 2.0 */ /* HDR Format Operand (hdr_format) */ #define CEC_OP_HDR_FORMAT_GAMMA_SDR 0 #define CEC_OP_HDR_FORMAT_GAMMA_HDR 1 @@ -1120,12 +1119,12 @@ struct cec_event { #define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17 #define CEC_OP_HDR_FORMAT_HDR10PLUS 24 #define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32 -#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 -#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 -#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 -#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 -#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 -#define CEC_MSG_UPDATE_SQID 0x58 +#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 /* CEC 2.0 */ +#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 /* CEC 2.0 */ +#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 /* CEC 2.0 */ +#define CEC_MSG_UPDATE_SQID 0x58 /* CEC 2.0 */ /* Capability Discovery and Control Feature */ From d5a047e957a754462706fc10c0e648bcc4dcc8e6 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:10 +0530 Subject: [PATCH 1127/5214] platform/x86: int3472: Rename daisy-chain GPIO props to generic Rename the MSI-specific daisy-chain GPIO properties and software node to generic names so they can be reused by other platforms that also require daisy-chain GPIO configuration for TPS68470. Signed-off-by: Arun T Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/tps68470_board_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index 28c9e12c85bf..4358dc601923 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -323,13 +323,13 @@ static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { } }; -static const struct property_entry msi_prestige_ai_evo_gpio_props[] = { +static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } }; -static const struct software_node msi_prestige_ai_evo_tps68470_gpio_swnode = { - .properties = msi_prestige_ai_evo_gpio_props, +static const struct software_node int3472_tps68470_daisy_chain_gpio_swnode = { + .properties = int3472_tps68470_daisy_chain_gpio_props, }; static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { @@ -364,7 +364,7 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = { .dev_name = "i2c-INT3472:06", .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata, - .tps68470_gpio_swnode = &msi_prestige_ai_evo_tps68470_gpio_swnode, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, .n_gpiod_lookups = 1, .tps68470_gpio_lookup_tables = { &msi_prestige_ai_evo_ovti5675_gpios, From cbc6cd55ae5fb6e15bd761f588d7a3057b0c54f8 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:11 +0530 Subject: [PATCH 1128/5214] platform/x86: int3472: Add TPS68470 board data for intel nvl The Intel NVL platform uses IPU8 powered by a TPS68470 PMIC, requiring board data to configure the GPIOs and regulators for proper camera sensor operation. Signed-off-by: Arun T Reviewed-by: Daniel Scally Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- .../x86/intel/int3472/tps68470_board_data.c | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index 4358dc601923..c53542465fb4 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -289,6 +289,86 @@ static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps6847 }, }; +/* Settings for Intel NVL platform */ + +static struct regulator_consumer_supply ovti13b1_core_consumer_supplies[] = { + REGULATOR_SUPPLY("dvdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_ana_consumer_supplies[] = { + REGULATOR_SUPPLY("avdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_vcm_consumer_supplies[] = { + REGULATOR_SUPPLY("vcc", "i2c-OVTI13B1:01-VCM"), +}; + +static struct regulator_consumer_supply ovti13b1_vsio_consumer_supplies[] = { + REGULATOR_SUPPLY("dovdd", "i2c-OVTI13B1:01"), +}; + +static const struct regulator_init_data intel_nvl_tps68470_core_reg_init_data = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_core_consumer_supplies), + .consumer_supplies = ovti13b1_core_consumer_supplies, +}; + +static const struct regulator_init_data intel_nvl_tps68470_ana_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_ana_consumer_supplies), + .consumer_supplies = ovti13b1_ana_consumer_supplies, +}; +static const struct regulator_init_data intel_nvl_tps68470_vcm_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vcm_consumer_supplies), + .consumer_supplies = ovti13b1_vcm_consumer_supplies, +}; + +/* Ensure the always-on VIO regulator has the same voltage as VSIO */ +static const struct regulator_init_data intel_nvl_tps68470_vio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .always_on = true, + }, +}; +static const struct regulator_init_data intel_nvl_tps68470_vsio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vsio_consumer_supplies), + .consumer_supplies = ovti13b1_vsio_consumer_supplies, +}; + +static const struct tps68470_regulator_platform_data intel_nvl_tps68470_pdata = { + .reg_init_data = { + [TPS68470_CORE] = &intel_nvl_tps68470_core_reg_init_data, + [TPS68470_ANA] = &intel_nvl_tps68470_ana_reg_init_data, + [TPS68470_VCM] = &intel_nvl_tps68470_vcm_reg_init_data, + [TPS68470_VIO] = &intel_nvl_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &intel_nvl_tps68470_vsio_reg_init_data, + }, +}; + static struct gpiod_lookup_table surface_go_int347a_gpios = { .dev_id = "i2c-INT347A:00", .table = { @@ -323,6 +403,14 @@ static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { } }; +static struct gpiod_lookup_table intel_nvl_tps68470_gpios = { + .dev_id = "i2c-OVTI13B1:01", + .table = { + GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), + { } + } +}; + static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } @@ -371,6 +459,16 @@ static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_boa }, }; +static const struct int3472_tps68470_board_data intel_nvl_tps68470_board_data = { + .dev_name = "i2c-INT3472:04", + .tps68470_regulator_pdata = &intel_nvl_tps68470_pdata, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, + .n_gpiod_lookups = 1, + .tps68470_gpio_lookup_tables = { + &intel_nvl_tps68470_gpios, + }, +}; + static const struct dmi_system_id int3472_tps68470_board_data_table[] = { { .matches = { @@ -424,6 +522,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { }, .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Nova Lake Client Platform"), + }, + .driver_data = (void *)&intel_nvl_tps68470_board_data, + }, { } }; From 03529c85f4e94950b8de3df7336c253b587febe9 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:12 +0530 Subject: [PATCH 1129/5214] media: ov13b10: Support multiple regulators The OV13B10 sensor driver currently handles a single regulator called avdd, however the sensor can be supplied by up to three regulators. Update the driver to handle all of them together using the regulator bulk API. Signed-off-by: Arun T Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov13b10.c | 47 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 5421874732bc..b0d34141a13a 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -699,6 +700,12 @@ static const struct ov13b10_mode supported_2_lanes_modes[] = { }, }; +static const char * const ov13b10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + struct ov13b10 { struct device *dev; @@ -708,7 +715,7 @@ struct ov13b10 { struct v4l2_ctrl_handler ctrl_handler; struct clk *img_clk; - struct regulator *avdd; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov13b10_supply_names)]; struct gpio_desc *reset; /* V4L2 Controls */ @@ -1194,9 +1201,8 @@ static int ov13b10_power_off(struct device *dev) struct ov13b10 *ov13b10 = to_ov13b10(sd); gpiod_set_value_cansleep(ov13b10->reset, 1); - - if (ov13b10->avdd) - regulator_disable(ov13b10->avdd); + regulator_bulk_disable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); clk_disable_unprepare(ov13b10->img_clk); @@ -1214,14 +1220,12 @@ static int ov13b10_power_on(struct device *dev) dev_err(dev, "failed to enable imaging clock: %d", ret); return ret; } - - if (ov13b10->avdd) { - ret = regulator_enable(ov13b10->avdd); - if (ret < 0) { - dev_err(dev, "failed to enable avdd: %d", ret); - clk_disable_unprepare(ov13b10->img_clk); - return ret; - } + ret = regulator_bulk_enable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + clk_disable_unprepare(ov13b10->img_clk); + return ret; } gpiod_set_value_cansleep(ov13b10->reset, 0); @@ -1475,7 +1479,8 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) unsigned long freq; int ret; - ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", GPIOD_OUT_LOW); + ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", + GPIOD_OUT_LOW); if (IS_ERR(ov13b->reset)) return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->reset), "failed to get reset gpio\n"); @@ -1491,15 +1496,15 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) "external clock %lu is not supported\n", freq); - ov13b->avdd = devm_regulator_get_optional(ov13b->dev, "avdd"); - if (IS_ERR(ov13b->avdd)) { - ret = PTR_ERR(ov13b->avdd); - ov13b->avdd = NULL; - if (ret != -ENODEV) - return dev_err_probe(ov13b->dev, ret, - "failed to get avdd regulator\n"); - } + for (unsigned int i = 0; i < ARRAY_SIZE(ov13b10_supply_names); i++) + ov13b->supplies[i].supply = ov13b10_supply_names[i]; + ret = devm_regulator_bulk_get(ov13b->dev, + ARRAY_SIZE(ov13b10_supply_names), + ov13b->supplies); + if (ret) + return dev_err_probe(ov13b->dev, ret, + "failed to get regulators\n"); return 0; } From 5d8004bd279579452d7fe9f38396414fc87e1109 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Tue, 12 May 2026 11:21:14 +0800 Subject: [PATCH 1130/5214] media: platform: amd: isp4: drop stale list reinit before free Newer Smatch snapshots no longer report the false positive around isp4if_send_fw_cmd(), so the extra list reinitialization before kfree() is no longer needed. Drop the stale list reinit and keep the cleanup path simpler. Signed-off-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/isp4_interface.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index 15f14eddd683..8d73f66bb42c 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -459,12 +459,6 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, ele = NULL; free_ele: - /* - * The response handler or the timeout/error path must dequeue the - * command element before we own the final reference. - */ - if (ele) - INIT_LIST_HEAD(&ele->list); kfree(ele); return ret; } From e62bb5abdcc0dc57ff09c4db961784582e61cd9b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:12:06 +0200 Subject: [PATCH 1131/5214] media: platform: amd: add DRM_AMDGPU dependency With DRM_AMDGPU=m and DRM_AMD_ISP=y, it is possible to configura VIDEO_AMD_ISP4_CAPTURE as built-in, but that fails to link: aarch64-linux-ld: drivers/media/platform/amd/isp4/isp4_interface.o: in function `isp4if_gpu_mem_alloc.isra.0': isp4_interface.c:(.text+0x1d0): undefined reference to `isp_kernel_buffer_alloc' aarch64-linux-ld: drivers/media/platform/amd/isp4/isp4_interface.o: in function `isp4if_dealloc_fw_gpumem': isp4_interface.c:(.text+0x26c): undefined reference to `isp_kernel_buffer_free' Add a dependency on the tristate DRM_AMDGPU symbol in addition to the boolean DRM_AMD_ISP=y, so this can only be built-in if the ISP driver is also linked into the kernel itself. Fixes: 9a54c285630c ("media: platform: amd: Introduce amd isp4 capture driver") Signed-off-by: Arnd Bergmann Reviewed-by: Bin Du Tested-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig index 55dd2dc453a2..9d1927af1cb8 100644 --- a/drivers/media/platform/amd/isp4/Kconfig +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -2,7 +2,9 @@ config VIDEO_AMD_ISP4_CAPTURE tristate "AMD ISP4 and camera driver" - depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA + depends on DRM_AMDGPU && DRM_AMD_ISP + depends on HAS_DMA + depends on VIDEO_DEV select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS select VIDEOBUF2_V4L2 From 9061075b4f0a897e25ce46c396698dd24f92f5cb Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 16 May 2026 11:08:28 +0200 Subject: [PATCH 1132/5214] net/9p/usbg: Constify struct configfs_item_operations 'struct configfs_item_operations' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 25167 9336 256 34759 87c7 net/9p/trans_usbg.o After: ===== text data bss dec hex filename 25231 9272 256 34759 87c7 net/9p/trans_usbg.o Signed-off-by: Christophe JAILLET Message-ID: <2478bdabd7d169a686879c049f11dc307b5debbd.1778922467.git.christophe.jaillet@wanadoo.fr> Signed-off-by: Dominique Martinet --- net/9p/trans_usbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/9p/trans_usbg.c b/net/9p/trans_usbg.c index 1ce70338999c..419cda13a7b5 100644 --- a/net/9p/trans_usbg.c +++ b/net/9p/trans_usbg.c @@ -804,7 +804,7 @@ static void usb9pfs_attr_release(struct config_item *item) usb_put_function_instance(&usb9pfs_opts->func_inst); } -static struct configfs_item_operations usb9pfs_item_ops = { +static const struct configfs_item_operations usb9pfs_item_ops = { .release = usb9pfs_attr_release, }; From b4d71bea144550ff4a0917f8c4b06d4063eb27a6 Mon Sep 17 00:00:00 2001 From: Pierre Barre Date: Tue, 12 May 2026 13:20:31 +0000 Subject: [PATCH 1133/5214] 9p: use kvzalloc for readdir buffer The readdir buffer is sized to msize, so kzalloc() can fail under fragmentation with a page allocation failure in v9fs_alloc_rdir_buf() / v9fs_dir_readdir_dotl(). The buffer is only a response sink and is never pack_sg_list()'d, so kvzalloc() is safe for all transports, unlike the fcall buffers fixed in e21d451a82f3 ("9p: Use kvmalloc for message buffers on supported transports"). Signed-off-by: Pierre Barre Message-ID: <20260512132032.369281-1-pierre@barre.sh> Signed-off-by: Dominique Martinet --- fs/9p/vfs_dir.c | 2 +- net/9p/client.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index e0d34e4e9076..e82f60c1c854 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -70,7 +70,7 @@ static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen) struct p9_fid *fid = filp->private_data; if (!fid->rdir) - fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); + fid->rdir = kvzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); return fid->rdir; } diff --git a/net/9p/client.c b/net/9p/client.c index f0dcf252af7e..b7d947910037 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -765,7 +765,7 @@ static void p9_fid_destroy(struct p9_fid *fid) spin_lock_irqsave(&clnt->lock, flags); idr_remove(&clnt->fids, fid->fid); spin_unlock_irqrestore(&clnt->lock, flags); - kfree(fid->rdir); + kvfree(fid->rdir); kfree(fid); } From e661e17ddbed524b5fbda789a091b48b6b677067 Mon Sep 17 00:00:00 2001 From: Pierre Barre Date: Tue, 12 May 2026 13:20:32 +0000 Subject: [PATCH 1134/5214] 9p: invalidate readdir buffer on seek The per-fid readdir buffer (fid->rdir) is populated lazily and only refilled when fully drained (rdir->head == rdir->tail). userspace lseek() on a directory fd updates file->f_pos via generic_file_llseek() but does not touch the cached buffer, so the next getdents() iterates the stale cache and emits entries from the previous position instead of the one the caller asked for. Track the file position the cached data corresponds to in struct p9_rdir, and drop the cache on entry to iterate_shared when it no longer matches ctx->pos. The 9p protocol's Tread/Treaddir already take an arbitrary offset on every request, so a refill at the new position is always legal; no .llseek override or seek restriction is needed. Reported-by: Pierre Barre Link: https://lore.kernel.org/v9fs/496d10b9-40fe-4f81-8014-37497c37ff63@app.fastmail.com/ Signed-off-by: Pierre Barre Message-ID: <20260512132032.369281-2-pierre@barre.sh> Signed-off-by: Dominique Martinet --- fs/9p/vfs_dir.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index e82f60c1c854..323f85352f6a 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -27,6 +27,7 @@ * struct p9_rdir - readdir accounting * @head: start offset of current dirread buffer * @tail: end offset of current dirread buffer + * @offset: file position the data at @head corresponds to * @buf: dirread buffer * * private structure for keeping track of readdir @@ -36,6 +37,7 @@ struct p9_rdir { int head; int tail; + loff_t offset; uint8_t buf[]; }; @@ -102,6 +104,9 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) kvec.iov_base = rdir->buf; kvec.iov_len = buflen; + if (rdir->head < rdir->tail && rdir->offset != ctx->pos) + rdir->head = rdir->tail = 0; + while (1) { if (rdir->tail == rdir->head) { struct iov_iter to; @@ -117,6 +122,7 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) rdir->head = 0; rdir->tail = n; + rdir->offset = ctx->pos; } while (rdir->head < rdir->tail) { err = p9stat_read(fid->clnt, rdir->buf + rdir->head, @@ -134,6 +140,7 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) rdir->head += err; ctx->pos += err; + rdir->offset = ctx->pos; } } } @@ -161,6 +168,9 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) if (!rdir) return -ENOMEM; + if (rdir->head < rdir->tail && rdir->offset != ctx->pos) + rdir->head = rdir->tail = 0; + while (1) { if (rdir->tail == rdir->head) { err = p9_client_readdir(fid, rdir->buf, buflen, @@ -170,6 +180,7 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) rdir->head = 0; rdir->tail = err; + rdir->offset = ctx->pos; } while (rdir->head < rdir->tail) { @@ -190,6 +201,7 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) ctx->pos = curdirent.d_off; rdir->head += err; + rdir->offset = ctx->pos; } } } From 7e6445d9d6f7dfc5635fc216488d6f62a5594fe9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 23 Sep 2025 12:43:51 +0300 Subject: [PATCH 1135/5214] thunderbolt: Add tb_property_merge_dir() This allows merging one XDomain property directory into another. We are going to use this in the subsequent patch. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/property.c | 164 +++++++++++++++++++++++---------- include/linux/thunderbolt.h | 3 + 2 files changed, 119 insertions(+), 48 deletions(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 50cbfc92fe65..6b9666b61181 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -38,6 +38,7 @@ struct tb_property_dir_entry { static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root); +static struct tb_property *tb_property_copy(const struct tb_property *property); static inline void parse_dwdata(void *dst, const void *src, size_t dwords) { @@ -507,17 +508,9 @@ ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, return ret < 0 ? ret : 0; } -/** - * tb_property_copy_dir() - Take a deep copy of directory - * @dir: Directory to copy - * - * The resulting directory needs to be released by calling tb_property_free_dir(). - * - * Return: Pointer to &struct tb_property_dir, %NULL in case of failure. - */ -struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) +static struct tb_property_dir *copy_dir(const struct tb_property_dir *dir) { - struct tb_property *property, *p = NULL; + struct tb_property *property, *p; struct tb_property_dir *d; if (!dir) @@ -528,56 +521,131 @@ struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) return NULL; list_for_each_entry(property, &dir->properties, list) { - struct tb_property *p; - - p = tb_property_alloc(property->key, property->type); + p = tb_property_copy(property); if (!p) goto err_free; - - p->length = property->length; - - switch (property->type) { - case TB_PROPERTY_TYPE_DIRECTORY: - p->value.dir = tb_property_copy_dir(property->value.dir); - if (!p->value.dir) - goto err_free; - break; - - case TB_PROPERTY_TYPE_DATA: - p->value.data = kmemdup(property->value.data, - property->length * 4, - GFP_KERNEL); - if (!p->value.data) - goto err_free; - break; - - case TB_PROPERTY_TYPE_TEXT: - p->value.text = kzalloc(p->length * 4, GFP_KERNEL); - if (!p->value.text) - goto err_free; - strcpy(p->value.text, property->value.text); - break; - - case TB_PROPERTY_TYPE_VALUE: - p->value.immediate = property->value.immediate; - break; - - default: - break; - } - list_add_tail(&p->list, &d->properties); } return d; err_free: - kfree(p); tb_property_free_dir(d); - return NULL; } +static struct tb_property *tb_property_copy(const struct tb_property *property) +{ + struct tb_property *p; + + p = tb_property_alloc(property->key, property->type); + if (!p) + return NULL; + + p->length = property->length; + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + p->value.dir = copy_dir(property->value.dir); + if (!p->value.dir) + goto err_free; + break; + + case TB_PROPERTY_TYPE_DATA: + p->value.data = kmemdup(property->value.data, + property->length * 4, + GFP_KERNEL); + if (!p->value.data) + goto err_free; + break; + + case TB_PROPERTY_TYPE_TEXT: + p->value.text = kzalloc(p->length * 4, GFP_KERNEL); + if (!p->value.text) + goto err_free; + strcpy(p->value.text, property->value.text); + break; + + case TB_PROPERTY_TYPE_VALUE: + p->value.immediate = property->value.immediate; + break; + + default: + break; + } + + return p; + +err_free: + kfree(p); + return NULL; +} + +/** + * tb_property_copy_dir() - Take a deep copy of directory + * @dir: Directory to copy + * + * The resulting directory needs to be released by calling tb_property_free_dir(). + * + * Return: Pointer to &struct tb_property_dir, %NULL in case of failure. + */ +struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) +{ + return copy_dir(dir); +} +EXPORT_SYMBOL_GPL(tb_property_copy_dir); + +/** + * tb_property_merge_dir() - Merges directory into parent + * @parent: Directory to merge @dir + * @dir: Directory that is merged + * @replace: Replace existing entries + * + * This will merge @dir into @parent. Both must have same UUID. The + * properties in @dir will overwrite overlapping properties in @parent + * if @replace is %true. Contents of @dir is copied (so if it is not + * needed afterwards it needs to relesed by calling tb_property_free_dir()). + */ +int tb_property_merge_dir(struct tb_property_dir *parent, + const struct tb_property_dir *dir, + bool replace) +{ + const struct tb_property *property; + + if (WARN_ON(parent == dir)) + return -EINVAL; + + if (!uuid_equal(parent->uuid, dir->uuid)) + return -EINVAL; + + list_for_each_entry(property, &dir->properties, list) { + struct tb_property *p, *tmp; + + tmp = tb_property_copy(property); + if (!tmp) + return -ENOMEM; + + p = tb_property_find(parent, property->key, property->type); + if (p) { + if (replace) { + /* + * Found existing property in parent so + * replace with the new one. + */ + list_replace(&p->list, &tmp->list); + tb_property_free(p); + } else { + tb_property_free(tmp); + continue; + } + } else { + list_add_tail(&tmp->list, &parent->properties); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(tb_property_merge_dir); + /** * tb_property_add_immediate() - Add immediate property to directory * @parent: Directory to add the property diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index bbdbbc84c999..e98d569779f9 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -153,6 +153,9 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, size_t block_len); struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir); +int tb_property_merge_dir(struct tb_property_dir *parent, + const struct tb_property_dir *dir, + bool replace); struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid); void tb_property_free_dir(struct tb_property_dir *dir); int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, From aa4999c0297ae56143f67ecb3ef008a473afcc00 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 23 Sep 2025 12:46:11 +0300 Subject: [PATCH 1136/5214] thunderbolt: Add KUnit test for tb_property_merge_dir() This makes sure it keeps working if we ever need to change it. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/test.c | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index f41fabf15456..fa8bee0ccf8b 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -2975,6 +2975,87 @@ static void tb_test_property_parse_dir_len_underflow(struct kunit *test) tb_property_free_dir(dir); } +static void tb_test_property_merge(struct kunit *test) +{ + struct tb_property_dir *dir1, *dir2, *dir3; + struct tb_property *p; + uuid_t uuid; + int ret; + + dir1 = tb_property_create_dir(&network_dir_uuid); + KUNIT_ASSERT_NOT_NULL(test, dir1); + ret = tb_property_add_immediate(dir1, "prtcid", 1); + KUNIT_EXPECT_EQ(test, ret, 0); + ret = tb_property_add_immediate(dir1, "prtcvers", 1); + KUNIT_EXPECT_EQ(test, ret, 0); + ret = tb_property_add_immediate(dir1, "prtcrevs", 0); + KUNIT_EXPECT_EQ(test, ret, 0); + ret = tb_property_add_immediate(dir1, "prtcstns", 0); + KUNIT_EXPECT_EQ(test, ret, 0); + + dir2 = tb_property_create_dir(&network_dir_uuid); + KUNIT_ASSERT_NOT_NULL(test, dir2); + ret = tb_property_add_text(dir2, "descr", "This is text"); + KUNIT_EXPECT_EQ(test, ret, 0); + /* This replaces the value in dir1 */ + ret = tb_property_add_immediate(dir2, "prtcvers", 0x1234); + KUNIT_EXPECT_EQ(test, ret, 0); + + uuid_gen(&uuid); + dir3 = tb_property_create_dir(&uuid); + KUNIT_ASSERT_NOT_NULL(test, dir3); + ret = tb_property_add_immediate(dir3, "value0", 0); + KUNIT_EXPECT_EQ(test, ret, 0); + ret = tb_property_add_text(dir3, "value1", "Text value"); + KUNIT_EXPECT_EQ(test, ret, 0); + ret = tb_property_add_dir(dir2, "my", dir3); + KUNIT_EXPECT_EQ(test, ret, 0); + + ret = tb_property_merge_dir(dir1, dir2, true); + KUNIT_EXPECT_EQ(test, ret, 0); + + p = tb_property_get_next(dir1, NULL); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcid"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_EQ(test, p->length, 1); + KUNIT_ASSERT_EQ(test, p->value.immediate, 1); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcvers"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_EQ(test, p->length, 1); + KUNIT_ASSERT_EQ(test, p->value.immediate, 0x1234); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcrevs"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_EQ(test, p->length, 1); + KUNIT_ASSERT_EQ(test, p->value.immediate, 0); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcstns"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_EQ(test, p->length, 1); + KUNIT_ASSERT_EQ(test, p->value.immediate, 0); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "descr"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_TEXT); + KUNIT_ASSERT_EQ(test, p->length, 4); + KUNIT_ASSERT_STREQ(test, p->value.text, "This is text"); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NOT_NULL(test, p); + KUNIT_ASSERT_STREQ(test, &p->key[0], "my"); + KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_DIRECTORY); + compare_dirs(test, p->value.dir, dir3); + p = tb_property_get_next(dir1, p); + KUNIT_ASSERT_NULL(test, p); + + tb_property_free_dir(dir2); + tb_property_free_dir(dir1); +} + static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_property_parse_u32_wrap), KUNIT_CASE(tb_test_property_parse_recursion), @@ -3018,6 +3099,7 @@ static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_property_parse), KUNIT_CASE(tb_test_property_format), KUNIT_CASE(tb_test_property_copy), + KUNIT_CASE(tb_test_property_merge), { } }; From abc27e5bfed2fa281ccb23419ef4d1c9ded86398 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 23 Sep 2025 12:51:27 +0300 Subject: [PATCH 1137/5214] thunderbolt: Allow service drivers to specify their own properties The XDomain properties can be useful for service drivers, for example to implement a registry for the services they expose. So far there has been no need for service drivers to specify these but with the USB4STREAM driver that we are going to use them. This adds remote and local side properties that the service drivers have access to. Remote side is read-only but the local side can be changed by a service driver. Also provide a mechanism to notify the remote side that there are changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 95 ++++++++++++++++++++++++++++++----- include/linux/thunderbolt.h | 12 +++++ 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 6e83f93eee83..781d88d06b93 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -640,6 +640,32 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler) } EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler); +static int update_service_properties(struct device *dev, void *data) +{ + struct tb_property_dir *root = data; + struct tb_service *svc; + struct tb_property *p; + + svc = tb_to_service(dev); + if (!svc) + return 0; + + guard(mutex)(&svc->lock); + + /* + * Replace the static service properties with the dynamic one. + * Typically this is the same but service drivers can add their + * own dynamic properties here too. + */ + p = tb_property_find(root, svc->key, TB_PROPERTY_TYPE_DIRECTORY); + if (!p) + return 0; + if (svc->local_properties) + return tb_property_merge_dir(p->value.dir, + svc->local_properties, false); + return 0; +} + static void update_property_block(struct tb_xdomain *xd) { mutex_lock(&xdomain_lock); @@ -664,6 +690,9 @@ static void update_property_block(struct tb_xdomain *xd) tb_property_add_text(dir, "deviceid", utsname()->nodename); tb_property_add_immediate(dir, "maxhopid", xd->local_max_hopid); + /* Add service specific dynamic properties */ + device_for_each_child(&xd->dev, dir, update_service_properties); + ret = tb_property_format_dir(dir, NULL, 0); if (ret < 0) { dev_warn(&xd->dev, "local property block creation failed\n"); @@ -936,6 +965,40 @@ void tb_unregister_service_driver(struct tb_service_driver *drv) } EXPORT_SYMBOL_GPL(tb_unregister_service_driver); +static int update_xdomain(struct device *dev, void *data) +{ + struct tb_xdomain *xd; + + xd = tb_to_xdomain(dev); + if (xd) { + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(50)); + } + + return 0; +} + +/** + * tb_service_properties_changed() - Notify the other host about changes + * @svc: Service whose properties changed + * + * Notifies the other host that service properties may have been + * changed. This should be called whenever @svc->local_properties is + * updated. + */ +void tb_service_properties_changed(struct tb_service *svc) +{ + struct tb_xdomain *xd = tb_service_parent(svc); + + if (xd->is_unplugged) + return; + + scoped_guard(mutex, &xdomain_lock) + xdomain_property_block_gen++; + update_xdomain(&xd->dev, NULL); +} +EXPORT_SYMBOL_GPL(tb_service_properties_changed); + static ssize_t key_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1035,6 +1098,7 @@ static void tb_service_release(struct device *dev) struct tb_service *svc = container_of(dev, struct tb_service, dev); struct tb_xdomain *xd = tb_service_parent(svc); + tb_property_free_dir(svc->remote_properties); ida_free(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); @@ -1049,6 +1113,16 @@ const struct device_type tb_service_type = { }; EXPORT_SYMBOL_GPL(tb_service_type); +static void update_service(struct tb_service *svc, struct tb_property *property) +{ + struct tb_property_dir *dir = property->value.dir; + + guard(mutex)(&svc->lock); + tb_property_free_dir(svc->remote_properties); + svc->remote_properties = tb_property_copy_dir(dir); + kobject_uevent(&svc->dev.kobj, KOBJ_CHANGE); +} + static void __unregister_service(struct device *dev) { struct tb_service *svc = tb_to_service(dev); @@ -1109,6 +1183,12 @@ static int populate_service(struct tb_service *svc, if (!svc->key) return -ENOMEM; + svc->remote_properties = tb_property_copy_dir(dir); + if (!svc->remote_properties) { + kfree(svc->key); + return -ENOMEM; + } + return 0; } @@ -1133,6 +1213,7 @@ static void enumerate_services(struct tb_xdomain *xd) /* If the service exists already we are fine */ dev = device_find_child(&xd->dev, p, find_service); if (dev) { + update_service(tb_to_service(dev), p); put_device(dev); continue; } @@ -1156,6 +1237,7 @@ static void enumerate_services(struct tb_xdomain *xd) svc->dev.bus = &tb_bus_type; svc->dev.type = &tb_service_type; svc->dev.parent = get_device(&xd->dev); + mutex_init(&svc->lock); dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); tb_service_debugfs_init(svc); @@ -2549,19 +2631,6 @@ bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, return ret > 0; } -static int update_xdomain(struct device *dev, void *data) -{ - struct tb_xdomain *xd; - - xd = tb_to_xdomain(dev); - if (xd) { - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(50)); - } - - return 0; -} - static void update_all_xdomains(void) { bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index e98d569779f9..f60e3a1aecae 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -397,6 +397,10 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); * @prtcvers: Protocol version from the properties directory * @prtcrevs: Protocol software revision from the properties directory * @prtcstns: Protocol settings mask from the properties directory + * @lock: Protects this structure + * @local_properties: Properties owned by the service driver + * @remote_properties: Properties read from the remote service. These + * are read-only. * @debugfs_dir: Pointer to the service debugfs directory. Always created * when debugfs is enabled. Can be used by service drivers to * add their own entries under the service. @@ -404,6 +408,9 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); * Each domain exposes set of services it supports as collection of * properties. For each service there will be one corresponding * &struct tb_service. Service drivers are bound to these. + * + * Service drivers can add their own dynamic properties to + * @local_properties but whenever they do so @lock must be held. */ struct tb_service { struct device dev; @@ -413,6 +420,9 @@ struct tb_service { u32 prtcvers; u32 prtcrevs; u32 prtcstns; + struct mutex lock; + struct tb_property_dir *local_properties; + struct tb_property_dir *remote_properties; struct dentry *debugfs_dir; }; @@ -481,6 +491,8 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) return tb_to_xdomain(svc->dev.parent); } +void tb_service_properties_changed(struct tb_service *svc); + /** * struct tb_nhi - thunderbolt native host interface * @lock: Must be held during ring creation/destruction. Is acquired by From 5140737c592d23b4de0f98c47dd347684a0c8de3 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Nov 2025 13:16:53 +0200 Subject: [PATCH 1138/5214] thunderbolt / net: Move ring_frame_size() to thunderbolt.h This function can be used outside of thunderbolt networking driver so move it to the common header. No functional changes. Signed-off-by: Mika Westerberg --- drivers/net/thunderbolt/main.c | 16 ++++++---------- include/linux/thunderbolt.h | 10 +++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 7aae5d915a1e..495f7fe366e6 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -38,7 +38,7 @@ #define TBNET_MATCH_FRAGS_ID BIT(1) #define TBNET_64K_FRAMES BIT(2) #define TBNET_MAX_MTU SZ_64K -#define TBNET_FRAME_SIZE SZ_4K +#define TBNET_FRAME_SIZE TB_MAX_FRAME_SIZE #define TBNET_MAX_PAYLOAD_SIZE \ (TBNET_FRAME_SIZE - sizeof(struct thunderbolt_ip_frame_header)) /* Rx packets need to hold space for skb_shared_info */ @@ -327,11 +327,6 @@ static void stop_login(struct tbnet *net) netdev_dbg(net->dev, "login stopped\n"); } -static inline unsigned int tbnet_frame_size(const struct tbnet_frame *tf) -{ - return tf->frame.size ? : TBNET_FRAME_SIZE; -} - static void tbnet_free_buffers(struct tbnet_ring *ring) { unsigned int i; @@ -562,7 +557,7 @@ static struct tbnet_frame *tbnet_get_tx_buffer(struct tbnet *net) tf->frame.size = 0; dma_sync_single_for_cpu(dma_dev, tf->frame.buffer_phy, - tbnet_frame_size(tf), DMA_TO_DEVICE); + tb_ring_frame_size(&tf->frame), DMA_TO_DEVICE); return tf; } @@ -744,7 +739,7 @@ static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf, } /* Should be greater than just header i.e. contains data */ - size = tbnet_frame_size(tf); + size = tb_ring_frame_size(&tf->frame); if (size <= sizeof(*hdr)) { net->stats.rx_length_errors++; return false; @@ -1011,7 +1006,8 @@ static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb, hdr->frame_index, hdr->frame_count); dma_sync_single_for_device(dma_dev, frames[i]->frame.buffer_phy, - tbnet_frame_size(frames[i]), DMA_TO_DEVICE); + tb_ring_frame_size(&frames[i]->frame), + DMA_TO_DEVICE); } return true; @@ -1085,7 +1081,7 @@ static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb, */ for (i = 0; i < frame_count; i++) { dma_sync_single_for_device(dma_dev, frames[i]->frame.buffer_phy, - tbnet_frame_size(frames[i]), DMA_TO_DEVICE); + tb_ring_frame_size(&frames[i]->frame), DMA_TO_DEVICE); } return true; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index f60e3a1aecae..1d1bd458b5af 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -628,7 +628,15 @@ struct ring_frame { }; /* Minimum size for ring_rx */ -#define TB_FRAME_SIZE 0x100 +#define TB_FRAME_SIZE 256 +#define TB_MAX_FRAME_SIZE 4096 + +static inline size_t tb_ring_frame_size(const struct ring_frame *frame) +{ + if (frame->size) + return frame->size; + return TB_MAX_FRAME_SIZE; +} struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags); From c51777370ac2ef435401340e205ef1d0c778df28 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 27 Feb 2026 19:51:45 +0200 Subject: [PATCH 1139/5214] thunderbolt / net: Let the service drivers configure interrupt throttling Instead of the core driver programming fixed value for throttling let the service drivers to specify the interval if they need this. Signed-off-by: Mika Westerberg --- drivers/net/thunderbolt/main.c | 4 +++ drivers/thunderbolt/dma_test.c | 5 +++ drivers/thunderbolt/nhi.c | 58 ++++++++++++++++++---------------- drivers/thunderbolt/nhi_regs.h | 3 +- include/linux/thunderbolt.h | 5 +++ 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 495f7fe366e6..f8f97e8e2226 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -34,6 +34,7 @@ #define TBNET_RING_SIZE 256 #define TBNET_LOGIN_RETRIES 60 #define TBNET_LOGOUT_RETRIES 10 +#define TBNET_THROTTLING 128000 #define TBNET_E2E BIT(0) #define TBNET_MATCH_FRAGS_ID BIT(1) #define TBNET_64K_FRAMES BIT(2) @@ -956,6 +957,9 @@ static int tbnet_open(struct net_device *dev) } net->rx_ring.ring = ring; + tb_ring_throttling(net->tx_ring.ring, TBNET_THROTTLING); + tb_ring_throttling(net->rx_ring.ring, TBNET_THROTTLING); + napi_enable(&net->napi); start_login(net); diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index af1e6bc9c7cd..7877319b1b03 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -155,6 +155,8 @@ static int dma_test_start_rings(struct dma_test *dt) dt->tx_ring = ring; e2e_tx_hop = ring->hop; + tb_ring_throttling(ring, 128000); + ret = tb_xdomain_alloc_out_hopid(xd, -1); if (ret < 0) { dma_test_free_rings(dt); @@ -162,6 +164,7 @@ static int dma_test_start_rings(struct dma_test *dt) } dt->tx_hopid = ret; + } if (dt->packets_to_receive) { @@ -180,6 +183,8 @@ static int dma_test_start_rings(struct dma_test *dt) dt->rx_ring = ring; + tb_ring_throttling(ring, 128000); + ret = tb_xdomain_alloc_in_hopid(xd, -1); if (ret < 0) { dma_test_free_rings(dt); diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 1a2051673067..13009246e617 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -93,7 +93,7 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) u32 old, new; if (ring->irq > 0) { - u32 step, shift, ivr, misc; + u32 step, shift, ivr, misc, itr; void __iomem *ivr_base; int auto_clear_bit; int index; @@ -131,6 +131,12 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) if (active) ivr |= ring->vector << shift; iowrite32(ivr, ivr_base + step); + + /* Throttling is specified in 256ns increments */ + itr = DIV_ROUND_UP(ring->interval_nsec, 256); + itr &= REG_INT_THROTTLING_RATE_INTERVAL_MASK; + iowrite32(itr, ring->nhi->iobase + REG_INT_THROTTLING_RATE + + ring->vector * 4); } old = ioread32(ring->nhi->iobase + reg); @@ -854,6 +860,26 @@ void tb_ring_free(struct tb_ring *ring) } EXPORT_SYMBOL_GPL(tb_ring_free); +/** + * tb_ring_throttling() - Configure throttling for ring interrupt + * @ring: Ring to configure + * @interval_nsec: Interval counter for moderation (in ns), %0 disables + * + * Enables or disables ring interrupt throttling. The ring must be + * stopped for this to be called. Granularity is 256 ns. + * + * Return: %0 on success, negative errno otherwise. + */ +int tb_ring_throttling(struct tb_ring *ring, unsigned int interval_nsec) +{ + guard(spinlock_irqsave)(&ring->lock); + if (WARN_ON_ONCE(ring->running)) + return -EBUSY; + ring->interval_nsec = interval_nsec; + return 0; +} +EXPORT_SYMBOL_GPL(tb_ring_throttling); + /** * nhi_mailbox_cmd() - Send a command through NHI mailbox * @nhi: Pointer to the NHI structure @@ -1035,22 +1061,6 @@ static int nhi_poweroff_noirq(struct device *dev) return __nhi_suspend_noirq(dev, wakeup); } -static void nhi_enable_int_throttling(struct tb_nhi *nhi) -{ - /* Throttling is specified in 256ns increments */ - u32 throttle = DIV_ROUND_UP(128 * NSEC_PER_USEC, 256); - unsigned int i; - - /* - * Configure interrupt throttling for all vectors even if we - * only use few. - */ - for (i = 0; i < MSIX_MAX_VECS; i++) { - u32 reg = REG_INT_THROTTLING_RATE + i * 4; - iowrite32(throttle, nhi->iobase + reg); - } -} - static int nhi_resume_noirq(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -1065,13 +1075,10 @@ static int nhi_resume_noirq(struct device *dev) */ if (!pci_device_is_present(pdev)) { nhi->going_away = true; - } else { - if (nhi->ops && nhi->ops->resume_noirq) { - ret = nhi->ops->resume_noirq(nhi); - if (ret) - return ret; - } - nhi_enable_int_throttling(tb->nhi); + } else if (nhi->ops && nhi->ops->resume_noirq) { + ret = nhi->ops->resume_noirq(nhi); + if (ret) + return ret; } return tb_domain_resume_noirq(tb); @@ -1133,7 +1140,6 @@ static int nhi_runtime_resume(struct device *dev) return ret; } - nhi_enable_int_throttling(nhi); return tb_domain_runtime_resume(tb); } @@ -1271,8 +1277,6 @@ static int nhi_init_msi(struct tb_nhi *nhi) /* In case someone left them on. */ nhi_disable_interrupts(nhi); - nhi_enable_int_throttling(nhi); - ida_init(&nhi->msix_ida); /* diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index cf5222bee971..d6a197fabc74 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -101,7 +101,8 @@ struct ring_desc { #define REG_RING_INTERRUPT_MASK_CLEAR_BASE 0x38208 -#define REG_INT_THROTTLING_RATE 0x38c00 +#define REG_INT_THROTTLING_RATE 0x38c00 +#define REG_INT_THROTTLING_RATE_INTERVAL_MASK GENMASK(15, 0) /* Interrupt Vector Allocation */ #define REG_INT_VEC_ALLOC_BASE 0x38c40 diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 1d1bd458b5af..1160e0bf5c5b 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -554,6 +554,8 @@ struct tb_nhi { * @start_poll: Called when ring interrupt is triggered to start * polling. Passing %NULL keeps the ring in interrupt mode. * @poll_data: Data passed to @start_poll + * @interval_nsec: Interval counter if interrupt throttling is to be + * used with this ring (in ns) */ struct tb_ring { spinlock_t lock; @@ -577,6 +579,7 @@ struct tb_ring { u16 eof_mask; void (*start_poll)(void *data); void *poll_data; + unsigned int interval_nsec; }; /* Leave ring interrupt enabled on suspend */ @@ -697,6 +700,8 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame) struct ring_frame *tb_ring_poll(struct tb_ring *ring); void tb_ring_poll_complete(struct tb_ring *ring); +int tb_ring_throttling(struct tb_ring *ring, unsigned int interval_nsec); + /** * tb_ring_dma_device() - Return device used for DMA mapping * @ring: Ring whose DMA device is retrieved From d614113c10ae6e35e74cdfc4fea280ce1a93ca0b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Nov 2025 13:18:06 +0200 Subject: [PATCH 1140/5214] thunderbolt: Add helper to figure size of the ring Add to common header a function that returns size of the ring. This can be used in the drivers instead of rolling own version. Signed-off-by: Mika Westerberg --- include/linux/thunderbolt.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 1160e0bf5c5b..9df8a356396f 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -641,6 +641,11 @@ static inline size_t tb_ring_frame_size(const struct ring_frame *frame) return TB_MAX_FRAME_SIZE; } +static inline size_t tb_ring_size(const struct tb_ring *ring) +{ + return ring->size; +} + struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags); struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, From 94a11cd5ddb1d7c206f81df17a5fcb5d3ec2d13f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 27 Jun 2025 20:25:42 +0300 Subject: [PATCH 1141/5214] thunderbolt: Add tb_ring_flush() This allows the caller to wait for the ring to be empty. We are going to need this in the upcoming userspace tunneling support. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 28 ++++++++++++++++++++++++++++ include/linux/thunderbolt.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 13009246e617..a0a789bfb680 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -325,6 +325,8 @@ static void ring_work(struct work_struct *work) if (frame->callback) frame->callback(ring, frame, canceled); } + + wake_up(&ring->wait); } int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) @@ -601,6 +603,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, INIT_LIST_HEAD(&ring->queue); INIT_LIST_HEAD(&ring->in_flight); INIT_WORK(&ring->work, ring_work); + init_waitqueue_head(&ring->wait); ring->nhi = nhi; ring->hop = hop; @@ -760,6 +763,31 @@ void tb_ring_start(struct tb_ring *ring) } EXPORT_SYMBOL_GPL(tb_ring_start); +static bool tb_ring_empty(struct tb_ring *ring) +{ + guard(spinlock_irqsave)(&ring->lock); + return list_empty(&ring->in_flight); +} + +/** + * tb_ring_flush() - Waits for a ring to be empty + * @ring: Ring to wait + * @timeout_msec: Timeout in ms how long to wait. + * + * This can be called before stopping a ring to make sure all the frames + * submitted prior have been completed. + * + * Return: %true if the ring is empty now, %false otherwise. + */ +bool tb_ring_flush(struct tb_ring *ring, unsigned int timeout_msec) +{ + if (!wait_event_timeout(ring->wait, tb_ring_empty(ring), + msecs_to_jiffies(timeout_msec))) + return false; + return tb_ring_empty(ring); +} +EXPORT_SYMBOL_GPL(tb_ring_flush); + /** * tb_ring_stop() - shutdown a ring * @ring: Ring to stop diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 9df8a356396f..9c5cb5e4f23d 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -556,6 +556,7 @@ struct tb_nhi { * @poll_data: Data passed to @start_poll * @interval_nsec: Interval counter if interrupt throttling is to be * used with this ring (in ns) + * @wait: Used to signal that the ring may be empty now */ struct tb_ring { spinlock_t lock; @@ -580,6 +581,7 @@ struct tb_ring { void (*start_poll)(void *data); void *poll_data; unsigned int interval_nsec; + wait_queue_head_t wait; }; /* Leave ring interrupt enabled on suspend */ @@ -653,6 +655,7 @@ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data); void tb_ring_start(struct tb_ring *ring); +bool tb_ring_flush(struct tb_ring *ring, unsigned int timeout_msec); void tb_ring_stop(struct tb_ring *ring); void tb_ring_free(struct tb_ring *ring); From cba57ed6f1e7529498cebbbe135c5132667fa923 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 12 Aug 2025 13:53:49 +0300 Subject: [PATCH 1142/5214] thunderbolt: Add support for ConfigFS This adds ConfigFS support to USB4/Thunderbolt bus. By itself this just creates the subsystem but it exposes functions that can be used to register groups under it. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/Kconfig | 4 +++ drivers/thunderbolt/Makefile | 1 + drivers/thunderbolt/configfs.c | 61 ++++++++++++++++++++++++++++++++++ drivers/thunderbolt/domain.c | 2 ++ drivers/thunderbolt/tb.h | 8 +++++ include/linux/thunderbolt.h | 6 ++++ 6 files changed, 82 insertions(+) create mode 100644 drivers/thunderbolt/configfs.c diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig index db3b0bef48f4..9b4aaa456e32 100644 --- a/drivers/thunderbolt/Kconfig +++ b/drivers/thunderbolt/Kconfig @@ -18,6 +18,10 @@ menuconfig USB4 if USB4 +config USB4_CONFIGFS + def_tristate USB4 + depends on CONFIGFS_FS && !(USB4=y && CONFIGFS_FS=m) + config USB4_DEBUGFS_WRITE bool "Enable write by debugfs to configuration spaces (DANGEROUS)" help diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index b44b32dcb832..d5b367dfda1e 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -7,6 +7,7 @@ thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o clx.o thunderbolt-${CONFIG_ACPI} += acpi.o thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o +thunderbolt-$(CONFIG_USB4_CONFIGFS) += configfs.o thunderbolt-${CONFIG_USB4_KUNIT_TEST} += test.o CFLAGS_test.o += $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/thunderbolt/configfs.c b/drivers/thunderbolt/configfs.c new file mode 100644 index 000000000000..dc6bc363dfe8 --- /dev/null +++ b/drivers/thunderbolt/configfs.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ConfigFS support + * + * Copyright (C) 2026, Intel Corporation + * Author: Mika Westerberg + */ + +#include +#include + +#include "tb.h" + +static const struct config_item_type tb_root_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem tb_configfs = { + .su_group = { + .cg_item = { + .ci_namebuf = "thunderbolt", + .ci_type = &tb_root_group_type, + }, + }, +}; + +/** + * tb_configfs_register_group() - Register Thunderbolt ConfigFS group + * @group: Group to register. + * + * Registers the new @group under Thunderbolt subsystem ConfigFS. + * + * Return: 0% in case of success, negative errno otherwise. + */ +int tb_configfs_register_group(struct config_group *group) +{ + return configfs_register_group(&tb_configfs.su_group, group); +} +EXPORT_SYMBOL_GPL(tb_configfs_register_group); + +/** + * tb_configfs_unregister_group() - Unregister previously registered group + * @group: Group to unregister. + */ +void tb_configfs_unregister_group(struct config_group *group) +{ + configfs_unregister_group(group); +} +EXPORT_SYMBOL_GPL(tb_configfs_unregister_group); + +int tb_configfs_init(void) +{ + config_group_init(&tb_configfs.su_group); + mutex_init(&tb_configfs.su_mutex); + return configfs_register_subsystem(&tb_configfs); +} + +void tb_configfs_exit(void) +{ + configfs_unregister_subsystem(&tb_configfs); +} diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index d83719a37b4c..b381f184340e 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -887,6 +887,7 @@ int tb_domain_init(void) { int ret; + tb_configfs_init(); tb_debugfs_init(); tb_acpi_init(); @@ -916,4 +917,5 @@ void tb_domain_exit(void) tb_xdomain_exit(); tb_acpi_exit(); tb_debugfs_exit(); + tb_configfs_exit(); } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 229b9e7961fb..e60f1bc3764e 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1558,4 +1558,12 @@ static inline void tb_retimer_debugfs_init(struct tb_retimer *rt) { } static inline void tb_retimer_debugfs_remove(struct tb_retimer *rt) { } #endif +#if IS_REACHABLE(CONFIG_CONFIGFS_FS) +int tb_configfs_init(void); +void tb_configfs_exit(void); +#else +static inline int tb_configfs_init(void) { return 0; } +static inline void tb_configfs_exit(void) { } +#endif + #endif diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 9c5cb5e4f23d..0be9b298e692 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -13,6 +13,7 @@ #include +struct config_group; struct fwnode_handle; struct device; @@ -727,6 +728,11 @@ static inline struct device *tb_ring_dma_device(struct tb_ring *ring) bool usb4_usb3_port_match(struct device *usb4_port_dev, const struct fwnode_handle *usb3_port_fwnode); +#if IS_REACHABLE(CONFIG_CONFIGFS_FS) +int tb_configfs_register_group(struct config_group *group); +void tb_configfs_unregister_group(struct config_group *group); +#endif + #else /* CONFIG_USB4 */ static inline bool usb4_usb3_port_match(struct device *usb4_port_dev, const struct fwnode_handle *usb3_port_fwnode) From 6db21d817b43f8ce5654ccc7aff80d40e4dba4ac Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 12 Aug 2025 13:56:30 +0300 Subject: [PATCH 1143/5214] thunderbolt: Add support for USB4STREAM Introduce USB4STREAM protocol and Linux implementation. This allows two (or more) hosts to transfer data directly over Thunderbolt/USB4 cable through a character device without need to go through the network stack. Any application that supports read(2) and write(2) in some form should be able to use the device without changes. The data is sent out to the other side over a tunnel inside Thunderbolt/USB4 fabric. The character device is called /dev/tbstreamX where X is the minor number starting from 0. All stream devices need to be configured first. This is done through ConfigFS interface. There can be multiple streams at the same time (this depends on number of DMA rings and available HopIDs) and a single stream supports traffic in both directions. For example there could be an application that uses one stream as control channel and another one as bi-directional data channel. A real use-case for this is to take a backup as a part of recovery initramfs tooling (no need to setup networking or have ssh or similar tooling as part of the initramfs). Say we want to backup the disk of host1 to host2. First Thunderbolt/USB4 cable is connected between the hosts (there can be devices in the middle too) then the receiving side configures the stream: host2 # mkdir /sys/kernel/config/thunderbolt/stream/0-1.0 host2 # mkdir /sys/kernel/config/thunderbolt/stream/0-1.0/backup host2 # echo -1 > /sys/kernel/config/thunderbolt/stream/0-1.0/backup/in_hopid host2 # echo -1 > /sys/kernel/config/thunderbolt/stream/0-1.0/backup/out_hopid We use automatic HopID allocation (writing -1 to HopIDs) for simplicity. From this point forward the /dev/tbstream0 can be used pretty much as regular file: host2 # dd if=/dev/tbstream0 of=/tmp/host1.nvme0n1.backup-$(date +%F) bs=256k The host that is being backed up then configures the stream accordingly: host1 # mkdir /sys/kernel/config/thunderbolt/stream/0-503.0 host1 # mkdir /sys/kernel/config/thunderbolt/stream/0-503.0/backup Here we take advantage of the fact that host2 also announces the active streams through XDomain properties so the name "backup" gives us the HopIDs. It is also possible to configure them manually in the same way we did for host2. Then it is just a matter of copying the data over: host1 # dd if=/dev/nvme0n1 of=/dev/tbstream0 bs=256k Similarly it is possible to transfer parts of the filesystem. For example copy contents of mydir over to the host2: host2 # gunzip < /dev/tbstream0 | tar xf - host1 # tar cf - mydir | gzip > /dev/tbstream0 Other end of the spectrum use-case is "borrowing" laptop (host1) camera to desktop (host2): host2 # gst-launch-1.0 filesrc location=/dev/tbstream0 ! jpegdec ! videoconvert ! \ autovideosink host1 # gst-launch-1.0 v4l2src device=/dev/video0 ! video/x-raw,width=1920,height=1080 ! \ jpegenc quality=90 ! filesink location=/dev/tbstream0 Once the streams are no longer needed they can be removed: host1 # cd /sys/kernel/config/thunderbolt/stream/ host1 # rmdir -p 0-503.0/backup host2 # cd /sys/kernel/config/thunderbolt/stream host2 # rmdir -p 0-1.0/backup Co-developed-by: Alan Borzeszkowski Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- .../ABI/testing/configfs-thunderbolt_stream | 83 + drivers/thunderbolt/Kconfig | 11 + drivers/thunderbolt/Makefile | 3 + drivers/thunderbolt/stream.c | 1698 +++++++++++++++++ 4 files changed, 1795 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-thunderbolt_stream create mode 100644 drivers/thunderbolt/stream.c diff --git a/Documentation/ABI/testing/configfs-thunderbolt_stream b/Documentation/ABI/testing/configfs-thunderbolt_stream new file mode 100644 index 000000000000..7abc6b73a1e4 --- /dev/null +++ b/Documentation/ABI/testing/configfs-thunderbolt_stream @@ -0,0 +1,83 @@ +What: /sys/kernel/config/thunderbolt/stream/. +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + Configuration group for a stream Thunderbolt/USB4 + service. It is possible to create groups even if there + is no connection yet to the other host. Once a + connection established and there is stream service on + the remote side that matches, this configuration is + applied to it. + + To find the service name you can run tblist from tbtools [1]: + + # tblist -A + ... + Domain 0 Route 3 Index 0: stream + + [1] https://github.com/intel/tbtools + +What: /sys/kernel/config/thunderbolt/stream/./$name +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + Creates new stream with $name and fills it with the + default values. If there is an advertised remote stream + with the same name, uses its values as the default. + +What: /sys/kernel/config/thunderbolt/stream/./$name/index +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + This matches the X in /dev/tbstreamX and allows userspace + to map the configfs directory to the corresponding character + device. + +What: /sys/kernel/config/thunderbolt/stream/./$name/in_hopid +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + In HopID used with the read path of the tunnel. Available HopIDs + for tunneling start from 8. You can pass also -1 for automatic + allocation. The allocated value can be read here. Writing 0 will + de-allocate if the stream is not in use. + + To figure out the maximum HopID you can run tbget from + tbtools for the lane adapter. For example below we check + for lane adapter number 1 (first USB4 port): + + # tbget -r 0 -a 1 -D ADP_CS_5.Max\ Input\ HopID + 19 + + This allows to use anything between 8 and 19 inclusive. + +What: /sys/kernel/config/thunderbolt/stream/./$name/out_hopid +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + Out HopID used with the write path of the tunnel. Available HopIDs + for tunneling start from 8. You can pass also -1 for automatic + allocation. The allocated value can be read here. Writing 0 will + de-allocate if the stream is not in use. See @in_hopid + for how to figure out the maximum HopID. + +What: /sys/kernel/config/thunderbolt/stream/./$name/ring_size +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + Size of the TX/RX rings. Can be adjusted between 32 and + 4096. The default is 256. + +What: /sys/kernel/config/thunderbolt/stream/./$name/throttling +Date: Sep 2026 +KernelVersion: v7.2 +Contact: Mika Westerberg +Description: + Interrupt throttling rate in ns. Lower values can give + better latency. The default is 8192 ns. diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig index 9b4aaa456e32..294b3227a545 100644 --- a/drivers/thunderbolt/Kconfig +++ b/drivers/thunderbolt/Kconfig @@ -64,4 +64,15 @@ config USB4_DMA_TEST To compile this driver a module, choose M here. The module will be called thunderbolt_dma_test. +config USB4_STREAM + tristate "Stream data over Thunderbolt/USB4 cable" + depends on USB4_CONFIGFS + help + This adds support for USB4STREAM protocol that allows two + hosts to stream data directly over Thunderbolt/USB4 cable + through /dev/tbstreamX devices. + + To compile this driver a module, choose M here. The module will be + called thunderbolt_stream. + endif # USB4 diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index d5b367dfda1e..c792b8084ffa 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -13,3 +13,6 @@ CFLAGS_test.o += $(DISABLE_STRUCTLEAK_PLUGIN) thunderbolt_dma_test-${CONFIG_USB4_DMA_TEST} += dma_test.o obj-$(CONFIG_USB4_DMA_TEST) += thunderbolt_dma_test.o + +thunderbolt_stream-${CONFIG_USB4_STREAM} += stream.o +obj-$(CONFIG_USB4_STREAM) += thunderbolt_stream.o diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c new file mode 100644 index 000000000000..c1f5c55583d0 --- /dev/null +++ b/drivers/thunderbolt/stream.c @@ -0,0 +1,1698 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Stream data over Thunderbolt/USB4 cable + * + * Copyright (C) 2026, Intel Corporation + * Authors: Alan Borzeszkowski + * Mika Westerberg + */ + +#define pr_fmt(fmt) "tbstream: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * USB4STREAM - Stream data directly over Thunderbolt/USB4 cable + * + * HopIDs are configured by the user. In Linux this is done through + * ConfigFS. Once that is done paths are be established the first time + * the stream is opened. Typically the read side is opened first to make + * sure all the data will be received. + * + * End-to-end flow control is mandatory on both sides. + * + * Data is sent to the other side as tunneled DATA packets. All the data + * is owned by the user and passed as-is from the writer to the reader. + * + * Once the stream device is closed, a CLOSE packet is sent to the peer + * so it can take the necessary action. On Linux this typically results + * in EOF being returned to the reader. + * + * Tunneled packet types: + * + * +-------+---------+------------------+ + * | PDF | Type | Payload size | + * +-------+---------+------------------+ + * | 2 | DATA | up to 4 KiB | + * | 3 | CLOSE | up to 256 bytes | + * +-------+---------+------------------+ + * + * Each stream can optionally publish configuration values under its own + * XDomain property directory. The name of the directory is the name of + * the stream in question and the UUID is up to the stream. For example + * if the stream exposes video output then the directory name could be + * "video". + * + * Below values are reserved and can be used by the stream: + * + * +----------+-----------+-------------------------+ + * | Key | Type | Contents | + * +----------+-----------+-------------------------+ + * | inhopid | IMMEDIATE | Configured input HopID | + * | outhopid | IMMEDIATE | Configured output HopID | + * +----------+-----------+-------------------------+ + * + * It is allowed to add more stream specific properties as well if the + * above are not enough. + */ + +#define TBSTREAM_DEV_RING_SIZE 256 +#define TBSTREAM_DEV_MIN_RING_SIZE 32 +#define TBSTREAM_DEV_MAX_RING_SIZE 4096 +#define TBSTREAM_DEV_THROTTLING 8192 +#define TBSTREAM_DEV_MAX_THROTTLING 16776960 + +/** + * enum tbstream_frame_pdf - PDF numbers for tunneled frames + * @TBSTREAM_FRAME_START: PDF of the start of the frame + * @TBSTREAM_DATA: PDF of the DATA frame + * @TBSTREAM_CLOSE: PDF of the CLOSE frame + */ +enum tbstream_frame_pdf { + TBSTREAM_FRAME_START = 1, + TBSTREAM_DATA, + TBSTREAM_CLOSE, +}; + +/** + * struct tbstream_frame - Frame submitted to/from the rings + * @sdev: Pointer to the stream device + * @page: Page holding the packet + * @offset: Offset inside @page if partial read is done + * @completed: %true if the RX frame is completed + * @frame: Underlying frame structure + */ +struct tbstream_frame { + struct tbstream_dev *sdev; + struct page *page; + unsigned int offset; + bool completed; + struct ring_frame frame; +}; + +/** + * struct tbstream_ring - Stream RX/TX ring structure + * @ring: Pointer to the API ring + * @prod: Current value of producer + * @cons: Current value of consumer + * @frames: Holds the ring frames + */ +struct tbstream_ring { + struct tb_ring *ring; + unsigned long prod; + unsigned long cons; + struct tbstream_frame *frames; +}; + +/** + * struct tbstream_dev - Stream character device + * @group: ConfigFS group for this device + * @stream: Pointer to the stream if it is attached (%NULL otherwise) + * @misc: Character device used for tunneling + * @kref: Reference count + * @index: Unique identifier for the character device + * @in_hopid: In HopID + * @out_hopid: Out HopID + * @ring_size: Size of the rings + * @throttling: Interrupt throttling rate in ns + * @users: Number of times @cdev has been opened + * @closed: CLOSE packet was received + * @removed: Userspace removed the ConfigFS group underneath. + * @wait: Waitqueue for open, read and write + * @lock: Lock protecting this structure + * @tx_ring: Transmit ring + * @rx_ring: Receive ring + * @list: Stream devices are linked through this + */ +struct tbstream_dev { + struct config_group group; + struct tbstream *stream; + struct miscdevice misc; + struct kref kref; + int index; + int in_hopid; + int out_hopid; + unsigned int ring_size; + unsigned int throttling; + int users; + bool closed; + bool removed; + wait_queue_head_t wait; + struct mutex lock; + struct tbstream_ring tx_ring; + struct tbstream_ring rx_ring; + struct list_head list; +}; + +/** + * struct tbstream_group - Config group for stream + * @group: ConfigFS group for @stream + * @stream: Stream the ConfigFS group is attached to. %NULL if there is + * no stream attached. + * @lock: Lock protecting this structure + * @dev_list: List of stream devices + * + * This is the ConfigFS directory for one connection to another host. + * There can be several &struct stream_dev linked through @dev_list of + * this structure. Reference count managed through @group. + */ +struct tbstream_group { + struct config_group group; + struct tbstream *stream; + struct mutex lock; + struct list_head dev_list; +}; + +/** + * struct tbstream - Stream service private data + * @kref: Reference count + * @svc: Pointer to the service device + * @list: Streams are linked through this in @stream_list + * + * This represents the actual physical connection between two hosts. + */ +struct tbstream { + struct kref kref; + struct tb_service *svc; + struct list_head list; +}; + +static DEFINE_IDA(tbstream_indices); + +/* Protects tbstream_list */ +static DEFINE_MUTEX(tbstream_lock); +static LIST_HEAD(tbstream_list); + +/* Serializes tbstream_get()/put() */ +static DEFINE_MUTEX(tbstream_kref_lock); + +/* Serializes tbstream_dev_get()/put() */ +static DEFINE_MUTEX(tbstream_dev_kref_lock); + +/* Stream property directory UUID: 3a1cb984-c4d9-4469-a277-ce2fdfd11f0d */ +static const uuid_t tbstream_dir_uuid = + UUID_INIT(0x3a1cb984, 0xc4d9, 0x4469, + 0xa2, 0x77, 0xce, 0x2f, 0xdf, 0xd1, 0x1f, 0x0d); + +static struct tb_property_dir *tbstream_dir; + +static void tbstream_release(struct kref *kref) +{ + struct tbstream *stream = container_of(kref, typeof(*stream), kref); + + tb_service_put(stream->svc); + kfree(stream); +} + +static void tbstream_put(struct tbstream *stream) +{ + if (stream) { + guard(mutex)(&tbstream_kref_lock); + kref_put(&stream->kref, tbstream_release); + } +} + +static struct tbstream *tbstream_get(struct tbstream *stream) +{ + if (stream) { + guard(mutex)(&tbstream_kref_lock); + kref_get(&stream->kref); + } + return stream; +} + +static inline bool tbstream_valid(const struct tbstream *stream) +{ + if (stream) + return !tb_service_parent(stream->svc)->is_unplugged; + return false; +} + +static void tbstream_ring_free(struct tbstream_ring *ring) +{ + struct device *dma_dev = tb_ring_dma_device(ring->ring); + enum dma_data_direction dir; + int i; + + if (ring->ring->is_tx) + dir = DMA_TO_DEVICE; + else + dir = DMA_FROM_DEVICE; + + for (i = 0; i < tb_ring_size(ring->ring); i++) { + struct tbstream_frame *sf = &ring->frames[i]; + + if (sf->frame.buffer_phy) + dma_unmap_page(dma_dev, sf->frame.buffer_phy, + tb_ring_frame_size(&sf->frame), dir); + sf->frame.buffer_phy = 0; + if (sf->page) + __free_page(sf->page); + sf->page = NULL; + } + + ring->prod = 0; + ring->cons = 0; + kfree(ring->frames); +} + +static inline bool tbstream_ring_available(const struct tbstream_ring *ring) +{ + return ring->prod > ring->cons; +} + +static inline struct tb_xdomain *tbstream_dev_xdomain(struct tbstream_dev *sdev) +{ + if (sdev->stream) + return tb_service_parent(sdev->stream->svc); + return NULL; +} + +static void tbstream_dev_release(struct kref *kref) +{ + struct tbstream_dev *sdev = container_of(kref, struct tbstream_dev, kref); + + if (sdev->stream) { + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); + + if (sdev->out_hopid > 0) + tb_xdomain_release_out_hopid(xd, sdev->out_hopid); + if (sdev->in_hopid > 0) + tb_xdomain_release_in_hopid(xd, sdev->in_hopid); + + tbstream_put(sdev->stream); + } + ida_free(&tbstream_indices, sdev->index); + kfree(sdev->misc.name); + kfree(sdev); +} + +static inline void tbstream_dev_put(struct tbstream_dev *sdev) +{ + guard(mutex)(&tbstream_dev_kref_lock); + kref_put(&sdev->kref, tbstream_dev_release); +} + +static inline struct tbstream_dev *tbstream_dev_get(struct tbstream_dev *sdev) +{ + guard(mutex)(&tbstream_dev_kref_lock); + kref_get(&sdev->kref); + return sdev; +} + +static inline struct tbstream_dev *to_tbstream_dev(struct miscdevice *misc) +{ + return container_of(misc, struct tbstream_dev, misc); +} + +static inline int tbstream_dev_valid(const struct tbstream_dev *sdev) +{ + const struct tbstream *stream = sdev->stream; + + if (!tbstream_valid(stream)) + return -ENXIO; + if (sdev->in_hopid <= 0 || sdev->out_hopid <= 0) + return -EINVAL; + return 0; +} + +static inline bool tbstream_dev_removed(const struct tbstream_dev *sdev) +{ + return sdev->removed; +} + +static inline bool tbstream_dev_closed(const struct tbstream_dev *sdev) +{ + return sdev->closed; +} + +static void +tbstream_dev_rx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct tbstream_frame *sf = container_of(frame, typeof(*sf), frame); + struct tbstream_dev *sdev = sf->sdev; + + if (canceled) + return; + + sf->completed = true; + sdev->rx_ring.prod++; + + if (sf->frame.flags & RING_DESC_CRC_ERROR) + pr_warn("RX CRC error\n"); + else if (sf->frame.flags & RING_DESC_BUFFER_OVERRUN) + pr_warn("RX buffer overrun\n"); + else + wake_up_interruptible_poll(&sdev->wait, EPOLLIN | EPOLLRDNORM); +} + +static struct tbstream_frame * +tbstream_dev_completed_rx(struct tbstream_dev *sdev) +{ + struct device *dma_dev = tb_ring_dma_device(sdev->rx_ring.ring); + struct tbstream_frame *sf; + int index; + + index = sdev->rx_ring.cons % tb_ring_size(sdev->rx_ring.ring); + sf = &sdev->rx_ring.frames[index]; + if (!sf->completed) + return NULL; + + dma_sync_single_for_cpu(dma_dev, sf->frame.buffer_phy, + tb_ring_frame_size(&sf->frame), + DMA_FROM_DEVICE); + return sf; +} + +static int tbstream_dev_consume_rx(struct tbstream_dev *sdev) +{ + struct device *dma_dev = tb_ring_dma_device(sdev->rx_ring.ring); + struct tbstream_frame *sf; + int index; + + index = sdev->rx_ring.cons % tb_ring_size(sdev->rx_ring.ring); + sdev->rx_ring.cons++; + + sf = &sdev->rx_ring.frames[index]; + sf->completed = false; + sf->offset = 0; + sf->frame.size = 0; + + dma_sync_single_for_device(dma_dev, sf->frame.buffer_phy, + tb_ring_frame_size(&sf->frame), + DMA_FROM_DEVICE); + + return tb_ring_rx(sdev->rx_ring.ring, &sf->frame); +} + +static int tbstream_dev_alloc_rx_buffers(struct tbstream_dev *sdev) +{ + size_t ring_size = tb_ring_size(sdev->rx_ring.ring); + int i; + + sdev->rx_ring.frames = kcalloc(ring_size, sizeof(struct tbstream_frame), + GFP_KERNEL); + if (!sdev->rx_ring.frames) + return -ENOMEM; + + for (i = 0; i < ring_size; i++) { + struct device *dma_dev = tb_ring_dma_device(sdev->rx_ring.ring); + struct tbstream_frame *sf = &sdev->rx_ring.frames[i]; + dma_addr_t dma_addr; + + sf->page = alloc_page(GFP_KERNEL); + if (!sf->page) + return -ENOMEM; + + dma_addr = dma_map_page(dma_dev, sf->page, 0, TB_MAX_FRAME_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + __free_page(sf->page); + sf->page = NULL; + return -ENOMEM; + } + + sf->sdev = sdev; + sf->frame.callback = tbstream_dev_rx_callback; + sf->frame.buffer_phy = dma_addr; + + tb_ring_rx(sdev->rx_ring.ring, &sf->frame); + } + + sdev->rx_ring.cons = 0; + sdev->rx_ring.prod = 0; + return 0; +} + +static void +tbstream_dev_tx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct tbstream_frame *sf = container_of(frame, typeof(*sf), frame); + struct tbstream_dev *sdev = sf->sdev; + + if (canceled) + return; + + sdev->tx_ring.prod++; + if (sf->frame.eof == TBSTREAM_DATA) + wake_up_interruptible_poll(&sdev->wait, EPOLLOUT | EPOLLWRNORM); +} + +static int tbstream_dev_alloc_tx_buffers(struct tbstream_dev *sdev) +{ + struct device *dma_dev = tb_ring_dma_device(sdev->tx_ring.ring); + size_t ring_size = tb_ring_size(sdev->tx_ring.ring); + int i; + + sdev->tx_ring.frames = kcalloc(ring_size, sizeof(struct tbstream_frame), + GFP_KERNEL); + if (!sdev->tx_ring.frames) + return -ENOMEM; + + for (i = 0; i < ring_size; i++) { + struct tbstream_frame *sf = &sdev->tx_ring.frames[i]; + dma_addr_t dma_addr; + + sf->page = alloc_page(GFP_KERNEL); + if (!sf->page) + return -ENOMEM; + + dma_addr = dma_map_page(dma_dev, sf->page, 0, TB_MAX_FRAME_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + __free_page(sf->page); + sf->page = NULL; + return -ENOMEM; + } + + sf->sdev = sdev; + sf->frame.callback = tbstream_dev_tx_callback; + sf->frame.buffer_phy = dma_addr; + sf->frame.sof = TBSTREAM_FRAME_START; + } + + sdev->tx_ring.cons = 0; + sdev->tx_ring.prod = ring_size - 1; + return 0; +} + +static struct tbstream_frame * +tbstream_dev_alloc_tx(struct tbstream_dev *sdev, enum tbstream_frame_pdf pdf, + struct iov_iter *from, size_t size) +{ + struct device *dma_dev = tb_ring_dma_device(sdev->tx_ring.ring); + struct tbstream_frame *sf; + int index; + + if (!tbstream_ring_available(&sdev->tx_ring)) + return ERR_PTR(-ENOBUFS); + + index = sdev->tx_ring.cons % tb_ring_size(sdev->tx_ring.ring); + sdev->tx_ring.cons++; + + sf = &sdev->tx_ring.frames[index]; + sf->frame.size = size < TB_MAX_FRAME_SIZE ? size : 0; + sf->frame.eof = pdf; + + dma_sync_single_for_cpu(dma_dev, sf->frame.buffer_phy, size, + DMA_TO_DEVICE); + if (pdf == TBSTREAM_DATA) { + if (copy_page_from_iter(sf->page, 0, size, from) != size) + return ERR_PTR(-EFAULT); + } else { + memset(page_address(sf->page), 0, size); + } + dma_sync_single_for_device(dma_dev, sf->frame.buffer_phy, size, + DMA_TO_DEVICE); + return sf; +} + +static int +tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from, + size_t size) +{ + struct tbstream_frame *sf; + + sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_DATA, from, size); + if (IS_ERR(sf)) + return PTR_ERR(sf); + return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); +} + +static int tbstream_dev_send_close(struct tbstream_dev *sdev) +{ + struct tbstream_frame *sf; + + sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256); + if (IS_ERR(sf)) + return PTR_ERR(sf); + return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); +} + +static int tbstream_dev_start(struct tbstream_dev *sdev) +{ + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); + u16 sof_mask, eof_mask; + struct tb_ring *ring; + int ret, e2e_tx_hop; + + ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, + RING_FLAG_FRAME | RING_FLAG_E2E); + if (!ring) + return -ENOMEM; + sdev->tx_ring.ring = ring; + + ret = tbstream_dev_alloc_tx_buffers(sdev); + if (ret) + goto err_free_tx; + + e2e_tx_hop = ring->hop; + sof_mask = BIT(TBSTREAM_FRAME_START); + eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE); + + ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, + RING_FLAG_FRAME | RING_FLAG_E2E, e2e_tx_hop, + sof_mask, eof_mask, NULL, NULL); + if (!ring) { + ret = -ENOMEM; + goto err_free_tx_buffers; + } + sdev->rx_ring.ring = ring; + + ret = tb_xdomain_enable_paths(xd, sdev->out_hopid, + sdev->tx_ring.ring->hop, + sdev->in_hopid, + sdev->rx_ring.ring->hop); + if (ret) + goto err_free_rx; + + tb_ring_throttling(sdev->tx_ring.ring, sdev->throttling); + tb_ring_throttling(sdev->rx_ring.ring, sdev->throttling); + + tb_ring_start(sdev->tx_ring.ring); + tb_ring_start(sdev->rx_ring.ring); + + ret = tbstream_dev_alloc_rx_buffers(sdev); + if (ret) + goto err_stop; + return 0; + +err_stop: + tb_ring_stop(sdev->rx_ring.ring); + tb_ring_stop(sdev->tx_ring.ring); +err_free_rx: + tb_ring_free(sdev->rx_ring.ring); +err_free_tx_buffers: + tbstream_ring_free(&sdev->tx_ring); +err_free_tx: + tb_ring_free(sdev->tx_ring.ring); + + return ret; +} + +static void tbstream_dev_stop(struct tbstream_dev *sdev) +{ + struct tb_xdomain *xd; + + /* Wait for the ring to complete any outstanding frames */ + tb_ring_flush(sdev->tx_ring.ring, 500); + tb_ring_stop(sdev->tx_ring.ring); + tb_ring_flush(sdev->rx_ring.ring, 500); + tb_ring_stop(sdev->rx_ring.ring); + + xd = tbstream_dev_xdomain(sdev); + if (xd) { + tb_xdomain_disable_paths(xd, sdev->out_hopid, + sdev->tx_ring.ring->hop, + sdev->in_hopid, + sdev->rx_ring.ring->hop); + } + + tbstream_ring_free(&sdev->rx_ring); + tb_ring_free(sdev->rx_ring.ring); + sdev->rx_ring.ring = NULL; + tbstream_ring_free(&sdev->tx_ring); + tb_ring_free(sdev->tx_ring.ring); + sdev->tx_ring.ring = NULL; +} + +static ssize_t +tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) +{ + struct file *file = kiocb->ki_filp; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; + + ret = tbstream_dev_valid(sdev); + if (ret) + return ret; + + if (mutex_lock_interruptible(&sdev->lock)) + return -ERESTARTSYS; + + while (!tbstream_ring_available(&sdev->rx_ring)) { + mutex_unlock(&sdev->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(sdev->wait, + tbstream_ring_available(&sdev->rx_ring) || + tbstream_dev_valid(sdev) != 0 || + tbstream_dev_closed(sdev) || + tbstream_dev_removed(sdev)); + if (ret) + return ret; + + ret = tbstream_dev_valid(sdev); + if (ret) + return ret; + + if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) + return 0; + + if (mutex_lock_interruptible(&sdev->lock)) + return -ERESTARTSYS; + } + + nbytes = 0; + while (nbytes < iov_iter_count(to)) { + struct tbstream_frame *sf; + size_t size, sf_size; + + sf = tbstream_dev_completed_rx(sdev); + if (!sf) + break; + /* + * CLOSE tunneled packet. If userspace already read + * something then we stop processing now and return + * those bytes. Next time the first frame will be CLOSE + * in which case we return EOF to the user. + */ + if (sf->frame.eof == TBSTREAM_CLOSE) { + if (!nbytes) { + tbstream_dev_consume_rx(sdev); + sdev->closed = true; + } + break; + } + + sf_size = tb_ring_frame_size(&sf->frame); + size = min(iov_iter_count(to) - nbytes, sf_size); + + if (copy_page_to_iter(sf->page, sf->offset, size, to) != size) { + ret = -EFAULT; + break; + } + + /* + * If not all data from the frame is read so leave it in + * place and update the offset accordingly so next read + * gets the rest. + */ + if (size < sf_size) { + sf->offset += size; + sf->frame.size = sf_size - size; + } else { + ret = tbstream_dev_consume_rx(sdev); + if (ret) + break; + } + + nbytes += size; + } + + mutex_unlock(&sdev->lock); + if (ret) + return ret; + return nbytes; +} + +static ssize_t +tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) +{ + struct file *file = kiocb->ki_filp; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; + + ret = tbstream_dev_valid(sdev); + if (ret) + return ret; + + if (mutex_lock_interruptible(&sdev->lock)) + return -ERESTARTSYS; + + while (!tbstream_ring_available(&sdev->tx_ring)) { + mutex_unlock(&sdev->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(sdev->wait, + tbstream_ring_available(&sdev->tx_ring) || + tbstream_dev_valid(sdev) != 0 || + tbstream_dev_closed(sdev) || + tbstream_dev_removed(sdev)); + if (ret) + return ret; + + ret = tbstream_dev_valid(sdev); + if (ret) + return ret; + + if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) + return -ENXIO; + + if (mutex_lock_interruptible(&sdev->lock)) + return -ERESTARTSYS; + } + + nbytes = 0; + while (nbytes < iov_iter_count(from)) { + size_t size; + + size = min(iov_iter_count(from) - nbytes, TB_MAX_FRAME_SIZE); + ret = tbstream_dev_send_data(sdev, from, size); + if (ret) { + /* + * If there are no more buffers we are done for + * this write. + */ + if (ret == -ENOBUFS) + ret = 0; + break; + } + + nbytes += size; + } + + mutex_unlock(&sdev->lock); + if (ret) + return ret; + return nbytes; +} + +static __poll_t +tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + __poll_t mask = 0; + + poll_wait(file, &sdev->wait, wait); + guard(mutex)(&sdev->lock); + if (tbstream_dev_valid(sdev) != 0) { + mask |= EPOLLHUP | EPOLLERR; + } else { + if (tbstream_ring_available(&sdev->tx_ring)) + mask |= EPOLLOUT | EPOLLWRNORM; + if (tbstream_ring_available(&sdev->rx_ring)) + mask |= EPOLLIN | EPOLLRDNORM; + } + return mask; +} + +static int tbstream_dev_fops_open(struct inode *inode, struct file *file) +{ + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + int ret; + + tbstream_dev_get(sdev); + + if (mutex_lock_interruptible(&sdev->lock)) { + tbstream_dev_put(sdev); + return -ERESTARTSYS; + } + + /* + * If there is no stream attached yet, block until it appears + * unless this is opened in non-blocking mode. + */ + while ((ret = tbstream_dev_valid(sdev))) { + mutex_unlock(&sdev->lock); + + if (ret != -ENXIO || (file->f_flags & O_NONBLOCK)) + goto err_put; + + ret = wait_event_interruptible(sdev->wait, + tbstream_dev_valid(sdev) == 0 || + tbstream_dev_removed(sdev)); + if (ret) + goto err_put; + + if (tbstream_dev_removed(sdev)) { + ret = -ENXIO; + goto err_put; + } + + if (mutex_lock_interruptible(&sdev->lock)) { + ret = -ERESTARTSYS; + goto err_put; + } + } + + /* Only on first open we allocate rings and enable paths */ + if (!sdev->users++) { + ret = tbstream_dev_start(sdev); + if (ret) { + sdev->users--; + goto err_unlock; + } + sdev->closed = false; + } + + mutex_unlock(&sdev->lock); + return 0; + +err_unlock: + mutex_unlock(&sdev->lock); +err_put: + tbstream_dev_put(sdev); + + return ret; +} + +static int tbstream_dev_fops_release(struct inode *inode, struct file *file) +{ + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + + mutex_lock(&sdev->lock); + if (--sdev->users == 0) { + /* + * Send CLOSE tunneled packet to notify the other end + * that we are closing the file. We do this twice if the + * first one fails. + */ + tbstream_dev_send_close(sdev); + tbstream_dev_stop(sdev); + } + mutex_unlock(&sdev->lock); + + tbstream_dev_put(sdev); + return 0; +} + +static const struct file_operations tbstream_dev_fops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .read_iter = tbstream_dev_fops_read_iter, + .write_iter = tbstream_dev_fops_write_iter, + .poll = tbstream_dev_fops_poll, + .open = tbstream_dev_fops_open, + .release = tbstream_dev_fops_release, +}; + +static inline struct tbstream_dev * +tbstream_dev_from_group(struct config_group *group) +{ + return container_of(group, struct tbstream_dev, group); +} + +static ssize_t tbstream_dev_index_show(struct config_item *item, char *buf) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + return sysfs_emit(buf, "%d\n", sdev->index); +} +CONFIGFS_ATTR_RO(tbstream_dev_, index); + +static ssize_t tbstream_dev_in_hopid_show(struct config_item *item, char *buf) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + return sysfs_emit(buf, "%d\n", sdev->in_hopid); +} + +/* svc->lock must be held */ +static void service_remove_properties(struct tb_service *svc, const char *name) +{ + struct tb_property *p; + + if (!svc->local_properties) + return; + + p = tb_property_find(svc->local_properties, name, + TB_PROPERTY_TYPE_DIRECTORY); + if (p) { + tb_property_free_dir(p->value.dir); + tb_property_remove(p); + + dev_dbg(&svc->dev, "removed local directory %s\n", name); + + /* + * Is the service directory empty already? If it is then + * we can release it as well. + */ + tb_property_for_each(svc->local_properties, p) { + if (p->type == TB_PROPERTY_TYPE_DIRECTORY) + return; + } + + tb_property_free_dir(svc->local_properties); + svc->local_properties = NULL; + } +} + +static int service_update_properties(struct tb_service *svc, const char *name, + int in_hopid, int out_hopid) +{ + struct tb_property_dir *dir; + struct tb_property *p; + + guard(mutex)(&svc->lock); + + if (in_hopid < 8 || out_hopid < 8) { + service_remove_properties(svc, name); + return 0; + } + + if (!svc->local_properties) { + /* + * Add the service directory first time we + * populate the entries. + */ + svc->local_properties = tb_property_copy_dir(tbstream_dir); + if (!svc->local_properties) + return -ENOMEM; + } + + p = tb_property_find(svc->local_properties, name, + TB_PROPERTY_TYPE_DIRECTORY); + if (p) { + dir = p->value.dir; + + p = tb_property_find(dir, "inhopid", TB_PROPERTY_TYPE_VALUE); + if (p && p->value.immediate != in_hopid) + p->value.immediate = in_hopid; + p = tb_property_find(dir, "outhopid", TB_PROPERTY_TYPE_VALUE); + if (p && p->value.immediate != out_hopid) + p->value.immediate = out_hopid; + + dev_dbg(&svc->dev, + "updated local directory %s: in HopID %d, out HopID %d\n", + name, in_hopid, out_hopid); + } else { + uuid_t uuid; + int ret; + + uuid_gen(&uuid); + dir = tb_property_create_dir(&uuid); + if (!dir) + return -ENOMEM; + + tb_property_add_immediate(dir, "inhopid", in_hopid); + tb_property_add_immediate(dir, "outhopid", out_hopid); + + ret = tb_property_add_dir(svc->local_properties, name, dir); + if (ret) { + tb_property_free_dir(dir); + return ret; + } + + dev_dbg(&svc->dev, + "added local directory %s: in HopID %d, out HopID %d\n", + name, in_hopid, out_hopid); + } + + return 0; +} + +static int tbstream_dev_update_properties(struct tbstream_dev *sdev) +{ + struct tbstream *stream; + int ret; + + stream = tbstream_get(sdev->stream); + if (!stream) + return 0; + + ret = service_update_properties(stream->svc, + config_item_name(&sdev->group.cg_item), + sdev->in_hopid, sdev->out_hopid); + if (!ret) + tb_service_properties_changed(stream->svc); + + tbstream_put(stream); + return ret; +} + +static int tbstream_dev_alloc_in_hopid(struct tbstream_dev *sdev, int hopid) +{ + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); + int ret; + + if (sdev->in_hopid > 0 && sdev->in_hopid != hopid) + tb_xdomain_release_in_hopid(xd, sdev->in_hopid); + if (!hopid) { + sdev->in_hopid = hopid; + return 0; + } + ret = tb_xdomain_alloc_in_hopid(xd, hopid); + if (ret < 0) + return ret; + /* + * If specific HopID was asked by the user and we did not get + * that one then release and return error instead. + */ + if (hopid > 0 && hopid != ret) { + tb_xdomain_release_in_hopid(xd, ret); + return -EBUSY; + } + sdev->in_hopid = ret; + return 0; +} + +static int tbstream_dev_alloc_out_hopid(struct tbstream_dev *sdev, int hopid) +{ + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); + int ret; + + if (sdev->out_hopid > 0 && sdev->out_hopid != hopid) + tb_xdomain_release_out_hopid(xd, sdev->out_hopid); + if (!hopid) { + sdev->out_hopid = hopid; + return 0; + } + ret = tb_xdomain_alloc_out_hopid(xd, hopid); + if (ret < 0) + return ret; + if (hopid > 0 && hopid != ret) { + tb_xdomain_release_out_hopid(xd, ret); + return -EBUSY; + } + sdev->out_hopid = ret; + return 0; +} + +static ssize_t +tbstream_dev_in_hopid_store(struct config_item *item, const char *buf, + size_t count) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + int ret, in_hopid; + + ret = kstrtoint(buf, 0, &in_hopid); + if (ret) + return ret; + + guard(mutex)(&sdev->lock); + if (sdev->users) + return -EBUSY; + if (sdev->stream) { + ret = tbstream_dev_alloc_in_hopid(sdev, in_hopid); + if (ret) + return ret; + ret = tbstream_dev_update_properties(sdev); + } else { + sdev->in_hopid = in_hopid; + } + return ret ? ret : count; +} +CONFIGFS_ATTR(tbstream_dev_, in_hopid); + +static ssize_t tbstream_dev_out_hopid_show(struct config_item *item, char *buf) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + return sysfs_emit(buf, "%d\n", sdev->out_hopid); +} + +static ssize_t +tbstream_dev_out_hopid_store(struct config_item *item, const char *buf, + size_t count) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + int ret, out_hopid; + + ret = kstrtoint(buf, 0, &out_hopid); + if (ret) + return ret; + + guard(mutex)(&sdev->lock); + if (sdev->users) + return -EBUSY; + if (sdev->stream) { + ret = tbstream_dev_alloc_out_hopid(sdev, out_hopid); + if (ret) + return ret; + ret = tbstream_dev_update_properties(sdev); + } else { + sdev->out_hopid = out_hopid; + } + return ret ? ret : count; +} +CONFIGFS_ATTR(tbstream_dev_, out_hopid); + +static ssize_t tbstream_dev_ring_size_show(struct config_item *item, char *buf) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + return sysfs_emit(buf, "%u\n", sdev->ring_size); +} + +static ssize_t +tbstream_dev_ring_size_store(struct config_item *item, const char *buf, + size_t count) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + unsigned int ring_size; + int ret; + + ret = kstrtouint(buf, 0, &ring_size); + if (ret) + return ret; + + if (ring_size < TBSTREAM_DEV_MIN_RING_SIZE || + ring_size > TBSTREAM_DEV_MAX_RING_SIZE) + return -EINVAL; + + guard(mutex)(&sdev->lock); + if (sdev->users) + return -EBUSY; + sdev->ring_size = ring_size; + return count; +} +CONFIGFS_ATTR(tbstream_dev_, ring_size); + +static ssize_t tbstream_dev_throttling_show(struct config_item *item, char *buf) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + return sysfs_emit(buf, "%u\n", sdev->throttling); +} + +static ssize_t +tbstream_dev_throttling_store(struct config_item *item, const char *buf, + size_t count) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + unsigned int throttling; + int ret; + + ret = kstrtouint(buf, 0, &throttling); + if (ret) + return ret; + + if (throttling > TBSTREAM_DEV_MAX_THROTTLING) + return -EINVAL; + + guard(mutex)(&sdev->lock); + if (sdev->users) + return -EBUSY; + sdev->throttling = throttling; + return count; +} +CONFIGFS_ATTR(tbstream_dev_, throttling); + +static struct configfs_attribute *tbstream_dev_attrs[] = { + &tbstream_dev_attr_index, + &tbstream_dev_attr_in_hopid, + &tbstream_dev_attr_out_hopid, + &tbstream_dev_attr_ring_size, + &tbstream_dev_attr_throttling, + NULL, +}; + +static void tbstream_dev_item_release(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(group); + + misc_deregister(&sdev->misc); + tbstream_dev_put(sdev); +} + +static struct configfs_item_operations tbstream_dev_item_ops = { + .release = tbstream_dev_item_release, +}; + +static const struct config_item_type tbstream_dev_type = { + .ct_owner = THIS_MODULE, + .ct_item_ops = &tbstream_dev_item_ops, + .ct_attrs = tbstream_dev_attrs, +}; + +static void service_get_hopids(struct tb_service *svc, const char *name, + int *in_hopid, int *out_hopid) +{ + struct tb_property_dir *dir; + struct tb_property *p; + + guard(mutex)(&svc->lock); + + /* See if we have directory entry with the matching name */ + p = tb_property_find(svc->remote_properties, name, + TB_PROPERTY_TYPE_DIRECTORY); + if (!p) + return; + + dir = p->value.dir; + + /* + * We need to reverse the HopIDs on our end so that in becomes + * out and vice versa. + */ + p = tb_property_find(dir, "inhopid", TB_PROPERTY_TYPE_VALUE); + if (p && p->value.immediate >= 8) + *out_hopid = p->value.immediate; + p = tb_property_find(dir, "outhopid", TB_PROPERTY_TYPE_VALUE); + if (p && p->value.immediate >= 8) + *in_hopid = p->value.immediate; +} + +static void +tbstream_dev_attach_stream(struct tbstream_dev *sdev, struct tbstream_group *sg) +{ + const char *name = config_item_name(&sdev->group.cg_item); + struct tbstream *stream; + + stream = tbstream_get(sg->stream); + if (!stream) + return; + + scoped_guard(mutex, &sdev->lock) { + sdev->stream = stream; + /* + * If there is no existing configuration (or automatic + * configuration is being used) check if the other side + * has configuration for this and use it. + */ + if (sdev->in_hopid <= 0 && sdev->out_hopid <= 0) + service_get_hopids(stream->svc, name, &sdev->in_hopid, + &sdev->out_hopid); + if (sdev->in_hopid) + tbstream_dev_alloc_in_hopid(sdev, sdev->in_hopid); + if (sdev->out_hopid) + tbstream_dev_alloc_out_hopid(sdev, sdev->out_hopid); + } + + service_update_properties(stream->svc, name, sdev->in_hopid, + sdev->out_hopid); + tb_service_properties_changed(stream->svc); + + /* Notify any openerers that the stream is now attached */ + wake_up_interruptible(&sdev->wait); +} + +static void tbstream_dev_detach_stream(struct tbstream_dev *sdev) +{ + const char *name = config_item_name(&sdev->group.cg_item); + struct tbstream *stream; + struct tb_xdomain *xd; + + scoped_guard(mutex, &sdev->lock) { + stream = sdev->stream; + if (!stream) + return; + sdev->stream = NULL; + xd = tb_service_parent(stream->svc); + if (sdev->out_hopid > 0) + tb_xdomain_release_out_hopid(xd, sdev->out_hopid); + if (sdev->in_hopid > 0) + tb_xdomain_release_in_hopid(xd, sdev->in_hopid); + } + + service_update_properties(stream->svc, name, 0, 0); + tb_service_properties_changed(stream->svc); + + tbstream_put(stream); + + /* Notify any task that the stream is not valid anymore */ + wake_up_interruptible_poll(&sdev->wait, EPOLLHUP | EPOLLERR); +} + +static inline struct tbstream_group * +to_tbstream_group(struct config_group *group) +{ + return container_of(group, struct tbstream_group, group); +} + +static struct config_group * +tbstream_dev_make_group(struct config_group *group, const char *name) +{ + struct tbstream_group *sg = to_tbstream_group(group); + struct tbstream_dev *sdev; + int ret, index; + + /* + * We want the names to be suitable for passing as property + * directory names. + */ + if (strlen(name) > TB_PROPERTY_KEY_SIZE) + return ERR_PTR(-ENAMETOOLONG); + + sdev = kzalloc_obj(*sdev, GFP_KERNEL); + if (!sdev) + return ERR_PTR(-ENOMEM); + + index = ida_alloc(&tbstream_indices, GFP_KERNEL); + if (index < 0) { + kfree(sdev); + return ERR_PTR(index); + } + + sdev->index = index; + sdev->ring_size = TBSTREAM_DEV_RING_SIZE; + sdev->throttling = TBSTREAM_DEV_THROTTLING; + mutex_init(&sdev->lock); + init_waitqueue_head(&sdev->wait); + INIT_LIST_HEAD(&sdev->list); + /* This point forward tbstream_dev_put() must be used to release sdev */ + kref_init(&sdev->kref); + + config_group_init_type_name(&sdev->group, name, &tbstream_dev_type); + + scoped_guard(mutex, &sg->lock) + list_add_tail(&sdev->list, &sg->dev_list); + + tbstream_dev_attach_stream(sdev, sg); + + sdev->misc.name = kasprintf(GFP_KERNEL, "tbstream%d", index); + sdev->misc.minor = MISC_DYNAMIC_MINOR; + sdev->misc.fops = &tbstream_dev_fops; + + ret = misc_register(&sdev->misc); + if (ret) { + tbstream_dev_detach_stream(sdev); + scoped_guard(mutex, &sg->lock) + list_del(&sdev->list); + /* Calls tbstream_dev_put() */ + config_group_put(&sdev->group); + return ERR_PTR(ret); + } + + return &sdev->group; +} + +static void +tbstream_dev_drop_item(struct config_group *group, struct config_item *item) +{ + struct config_group *sdev_group = to_config_group(item); + struct tbstream_dev *sdev = tbstream_dev_from_group(sdev_group); + struct tbstream_group *sg = to_tbstream_group(group); + + scoped_guard(mutex, &sg->lock) + list_del(&sdev->list); + /* Notify any task that the underlying group was removed */ + sdev->removed = true; + wake_up_interruptible_poll(&sdev->wait, EPOLLHUP | EPOLLERR); + config_item_put(item); +} + +static struct configfs_group_operations tbstream_dev_group_ops = { + .make_group = tbstream_dev_make_group, + .drop_item = tbstream_dev_drop_item, +}; + +static void tbstream_item_release(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + struct tbstream_group *sg = to_tbstream_group(group); + + tbstream_put(sg->stream); + kfree(sg); +} + +static struct configfs_item_operations tbstream_item_ops = { + .release = tbstream_item_release, +}; + +static const struct config_item_type tbstream_dev_group_type = { + .ct_owner = THIS_MODULE, + .ct_group_ops = &tbstream_dev_group_ops, + .ct_item_ops = &tbstream_item_ops, +}; + +static struct config_group * +tbstream_make_group(struct config_group *group, const char *name) +{ + struct tbstream_group *sg; + struct tbstream *stream; + int domain, index; + u64 route; + + /* Make sure the format is correct */ + if (sscanf(name, "%u-%llx.%u", &domain, &route, &index) != 3) + return ERR_PTR(-EINVAL); + + sg = kzalloc_obj(*sg, GFP_KERNEL); + if (!sg) + return ERR_PTR(-ENOMEM); + + mutex_init(&sg->lock); + INIT_LIST_HEAD(&sg->dev_list); + + guard(mutex)(&tbstream_lock); + list_for_each_entry(stream, &tbstream_list, list) { + tbstream_get(stream); + if (sysfs_streq(name, dev_name(&stream->svc->dev))) { + sg->stream = stream; + break; + } + tbstream_put(stream); + } + + config_group_init_type_name(&sg->group, name, &tbstream_dev_group_type); + return &sg->group; +} + +static struct configfs_group_operations tbstream_group_ops = { + .make_group = tbstream_make_group, +}; + +static const struct config_item_type tbstream_group_type = { + .ct_owner = THIS_MODULE, + .ct_group_ops = &tbstream_group_ops, +}; + +static struct config_group tbstream_group = { + .cg_item = { + .ci_namebuf = "stream", + .ci_type = &tbstream_group_type, + }, +}; + +/* Returns reference count increased */ +static struct tbstream_group *tbstream_group_find(struct tbstream *stream) +{ + const char *name = dev_name(&stream->svc->dev); + struct config_item *item; + + guard(mutex)(&tbstream_group.cg_subsys->su_mutex); + item = config_group_find_item(&tbstream_group, name); + if (!item) + return NULL; + return to_tbstream_group(to_config_group(item)); +} + +static void tbstream_group_attach_stream(struct tbstream *stream) +{ + struct tbstream_group *sg; + struct tbstream_dev *sdev; + + sg = tbstream_group_find(stream); + if (!sg) + return; + + guard(mutex)(&sg->lock); + if (WARN_ON(sg->stream)) { + config_group_put(&sg->group); + return; + } + sg->stream = tbstream_get(stream); + /* + * If there are existing stream devices, attach the stream to + * them now. + */ + list_for_each_entry(sdev, &sg->dev_list, list) { + tbstream_dev_get(sdev); + tbstream_dev_attach_stream(sdev, sg); + tbstream_dev_put(sdev); + } + + config_group_put(&sg->group); +} + +static void tbstream_group_detach_stream(struct tbstream *stream) +{ + struct tbstream_group *sg; + struct tbstream_dev *sdev; + + sg = tbstream_group_find(stream); + if (!sg) + return; + + guard(mutex)(&sg->lock); + if (sg->stream) { + /* Detach this stream from the stream devices */ + list_for_each_entry_reverse(sdev, &sg->dev_list, list) { + tbstream_dev_get(sdev); + tbstream_dev_detach_stream(sdev); + tbstream_dev_put(sdev); + } + tbstream_put(sg->stream); + sg->stream = NULL; + } + + config_group_put(&sg->group); +} + +static int tbstream_probe(struct tb_service *svc, const struct tb_service_id *id) +{ + struct tbstream *stream; + + stream = kzalloc_obj(*stream, GFP_KERNEL); + if (!stream) + return -ENOMEM; + + /* After this point, release stream by calling tbstream_put() */ + kref_init(&stream->kref); + stream->svc = tb_service_get(svc); + INIT_LIST_HEAD(&stream->list); + + scoped_guard(mutex, &tbstream_lock) + list_add_tail(&stream->list, &tbstream_list); + + tbstream_group_attach_stream(stream); + tb_service_set_drvdata(svc, stream); + return 0; +} + +static void tbstream_remove(struct tb_service *svc) +{ + struct tbstream *stream = tb_service_get_drvdata(svc); + + tbstream_group_detach_stream(stream); + scoped_guard(mutex, &tbstream_lock) + list_del(&stream->list); + tbstream_put(stream); +} + +static int __maybe_unused tbstream_suspend(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tbstream *stream = tb_service_get_drvdata(svc); + struct tbstream_group *sg; + struct tbstream_dev *sdev; + + sg = tbstream_group_find(stream); + if (!sg) + return 0; + + list_for_each_entry_reverse(sdev, &sg->dev_list, list) { + tbstream_dev_get(sdev); + /* Stop the stream (if it was open) */ + if (sdev->users) + tbstream_dev_stop(sdev); + tbstream_dev_put(sdev); + } + + config_group_put(&sg->group); + return 0; +} + +static int __maybe_unused tbstream_resume(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tbstream *stream = tb_service_get_drvdata(svc); + struct tbstream_group *sg; + struct tbstream_dev *sdev; + + sg = tbstream_group_find(stream); + if (!sg) + return 0; + + list_for_each_entry(sdev, &sg->dev_list, list) { + tbstream_dev_get(sdev); + if (sdev->users) { + int ret; + + ret = tbstream_dev_start(sdev); + if (ret) { + tbstream_dev_put(sdev); + config_group_put(&sg->group); + return ret; + } + } + tbstream_dev_put(sdev); + } + + config_group_put(&sg->group); + return 0; +} + +static const struct dev_pm_ops tbstream_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tbstream_suspend, tbstream_resume) +}; + +static const struct tb_service_id tbstream_ids[] = { + { TB_SERVICE("stream", 1) }, + { }, +}; +MODULE_DEVICE_TABLE(tbsvc, tbstream_ids); + +static struct tb_service_driver tbstream_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "thunderbolt_stream", + .pm = &tbstream_pm_ops, + }, + .probe = tbstream_probe, + .remove = tbstream_remove, + .id_table = tbstream_ids, +}; + +static int __init tbstream_init(void) +{ + int ret; + + tbstream_dir = tb_property_create_dir(&tbstream_dir_uuid); + if (!tbstream_dir) + return -ENOMEM; + + tb_property_add_immediate(tbstream_dir, "prtcid", 1); + tb_property_add_immediate(tbstream_dir, "prtcvers", 1); + tb_property_add_immediate(tbstream_dir, "prtcrevs", 0); + tb_property_add_immediate(tbstream_dir, "prtcstns", 0); + + ret = tb_register_property_dir("stream", tbstream_dir); + if (ret) + goto err_free_dir; + + config_group_init(&tbstream_group); + ret = tb_configfs_register_group(&tbstream_group); + if (ret) + goto err_unregister_dir; + + ret = tb_register_service_driver(&tbstream_driver); + if (ret) + goto err_unregister_group; + return 0; + +err_unregister_group: + tb_configfs_unregister_group(&tbstream_group); +err_unregister_dir: + tb_unregister_property_dir("stream", tbstream_dir); +err_free_dir: + tb_property_free_dir(tbstream_dir); + return ret; +} +module_init(tbstream_init); + +static void __exit tbstream_exit(void) +{ + tb_unregister_service_driver(&tbstream_driver); + tb_configfs_unregister_group(&tbstream_group); + tb_unregister_property_dir("stream", tbstream_dir); + tb_property_free_dir(tbstream_dir); + ida_destroy(&tbstream_indices); +} +module_exit(tbstream_exit); + +MODULE_AUTHOR("Alan Borzeszkowski "); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_DESCRIPTION("Stream data over Thunderbolt/USB4 cable"); +MODULE_LICENSE("GPL"); From af8922ffb322c4650dc536a236c4b42a1cf2829e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 6 May 2026 09:46:58 +0300 Subject: [PATCH 1144/5214] docs: admin-guide: thunderbolt: Add instructions how to use USB4STREAM Add instructions how USB4STREAM can be configured and used. Signed-off-by: Mika Westerberg --- Documentation/admin-guide/thunderbolt.rst | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 89df26553aa0..91a6cb109988 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -373,6 +373,67 @@ port which are named like ``thunderbolt0`` and so on. From this point you can either use standard userspace tools like ``ip`` to configure the interface or let your GUI handle it automatically. +Streaming data directly over Thunderbolt cable +---------------------------------------------- +In addition to Thunderbolt networking (aka. USB4NET) Linux supports +streaming data directly over a cable as well (aka. USB4STREAM). This is +possible through ``thunderbolt-stream`` driver. + +Similarly to ``thunderbolt-net`` you load the driver first on one end:: + + host1 # modprobe thunderbolt-stream + +Then you configure it via ``ConfigFS``:: + + host1 # cd /sys/kernel/config/thunderbolt/stream + host1 # mkdir -p 0-1.0/data + host1 # cd 0-1.0 + host1 # echo -1 > data/in_hopid + host1 # echo -1 > data/out_hopid + +This information is automatically announced to the other side via +XDomain properties so if you have cable connected the other side knows +that there is a stream named ``data`` available and can configure it for +you automatically:: + + host2 # cd /sys/kernel/config/thunderbolt/stream + host2 # mkdir -p 0-3.0/data + +Here we used auto-configuration but you can configure it manually too. +In that case you need to fill ``in_hopid`` and ``out_hopid`` accordingly. +If you set them to ``-1`` the next available HopID is used which is +typically what we want. + +Once they are configured you can use ``/dev/tbstreamX`` on both sides to +transfer data:: + + host2 # cat /dev/tbstream0 + host1 # dmesg > /dev/tbstream0 + +Once you are done with the stream you can remove them:: + + host2 # cd /sys/kernel/config/thunderbolt/stream + host2 # rmdir -p 0-1.0/data + host1 # cd /sys/kernel/config/thunderbolt/stream + host1 # rmdir -p 0-3.0/data + +Since streams are essentially files you can use any existing application +that supports ``read(2)`` and ``write(2)`` in some form. + +It is possible to have more than one stream and you can have both stream +and ``thunderbolt-net`` in use simultaneously. For example we can create +two streams with name ``control`` and ``data`` like this:: + + host1 # cd /sys/kernel/config/thunderbolt/stream + host1 # mkdir 0-1.0 + host1 # cd 0-1.0 + host1 # mkdir control + host1 # mkdir data + +Then you have ``/dev/tbstream0`` for ``control`` and ``/dev/tbstream1`` +for ``data``. Before you can use them you need to configure them as +shown above for the one stream case. + Forcing power ------------- Many OEMs include a method that can be used to force the power of a From 1563ae33dc4f5ebac96b93af2ef72e72aaaa31ae Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Mon, 11 May 2026 12:19:18 +0800 Subject: [PATCH 1145/5214] coresight: platform: defer connection counter increment until alloc succeeds coresight_add_out_conn() increments nr_outconns before calling devm_krealloc_array() and again before devm_kmalloc(). If either allocation fails, the counter is already bumped while the corresponding array entry is NULL or uninitialized garbage. coresight_add_in_conn() has the same problem with nr_inconns and devm_krealloc_array(). In both cases the probe returns -ENOMEM, which causes coresight_get_platform_data() to call coresight_release_platform_data() for cleanup. That function iterates up to nr_outconns (or nr_inconns) entries and dereferences each pointer unconditionally, hitting the NULL or garbage entry and panicking instead of failing gracefully. Fix by moving the counter increments to after all allocations succeed, so the struct is always consistent on any error path. Fixes: 3d4ff657e454 ("coresight: Dynamically add connections") Fixes: e3f4e68797a9 ("coresight: Store in-connections as well as out-connections") Signed-off-by: Jie Gan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260511-fix-ref-count-issue-v1-1-99d647810d3c@oss.qualcomm.com --- drivers/hwtracing/coresight/coresight-platform.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index e337b6e2bf32..93c2d075cad6 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -45,9 +45,8 @@ coresight_add_out_conn(struct device *dev, } } - pdata->nr_outconns++; pdata->out_conns = - devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns, + devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns + 1, sizeof(*pdata->out_conns), GFP_KERNEL); if (!pdata->out_conns) return ERR_PTR(-ENOMEM); @@ -63,7 +62,8 @@ coresight_add_out_conn(struct device *dev, * used right away. */ *conn = *new_conn; - pdata->out_conns[pdata->nr_outconns - 1] = conn; + pdata->out_conns[pdata->nr_outconns] = conn; + pdata->nr_outconns++; return conn; } EXPORT_SYMBOL_GPL(coresight_add_out_conn); @@ -86,13 +86,13 @@ int coresight_add_in_conn(struct coresight_connection *out_conn) return 0; } - pdata->nr_inconns++; pdata->in_conns = - devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns, + devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns + 1, sizeof(*pdata->in_conns), GFP_KERNEL); if (!pdata->in_conns) return -ENOMEM; - pdata->in_conns[pdata->nr_inconns - 1] = out_conn; + pdata->in_conns[pdata->nr_inconns] = out_conn; + pdata->nr_inconns++; return 0; } EXPORT_SYMBOL_GPL(coresight_add_in_conn); From d25f1dd6b986070e2324bf373097f0fced1e57e1 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 8 May 2026 21:34:32 +0800 Subject: [PATCH 1146/5214] PCI: dwc: Use DEFINE_SHOW_ATTRIBUTE for ltssm_status debugfs Replace the custom open function and file_operations with the standard DEFINE_SHOW_ATTRIBUTE macro to reduce boilerplate code. This also adds the previously missing .release() callback and fixes the seq_file leak during close. Signed-off-by: Hans Zhang <18255117159@163.com> [mani: added a note about implicit release callback change spotted by Sashiko] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260508133432.1964491-1-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index d0884253be97..87f5ec9c48eb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -507,11 +507,6 @@ static int ltssm_status_show(struct seq_file *s, void *v) return 0; } -static int ltssm_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, ltssm_status_show, inode->i_private); -} - #define dwc_debugfs_create(name) \ debugfs_create_file(#name, 0644, rasdes_debug, pci, \ &dbg_ ## name ## _fops) @@ -548,10 +543,7 @@ static const struct file_operations dwc_pcie_counter_value_ops = { .read = counter_value_read, }; -static const struct file_operations dwc_pcie_ltssm_status_ops = { - .open = ltssm_status_open, - .read = seq_read, -}; +DEFINE_SHOW_ATTRIBUTE(ltssm_status); static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { @@ -642,7 +634,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) static void dwc_pcie_ltssm_debugfs_init(struct dw_pcie *pci, struct dentry *dir) { debugfs_create_file("ltssm_status", 0444, dir, pci, - &dwc_pcie_ltssm_status_ops); + <ssm_status_fops); } static int dw_pcie_ptm_check_capability(void *drvdata) From 87f493041e20759ffc27262cc0490c41628a5ee2 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Fri, 15 May 2026 12:37:52 +0530 Subject: [PATCH 1147/5214] PCI: tegra194: Use aspm-l1-entry-delay-ns DT property for L1 entrance latency Program the Synopsys DesignWare PORT_AFR L1 entrance latency field from the optional aspm-l1-entry-delay-ns device tree property (nanoseconds). Convert delay to whole microseconds with ceiling division (DIV_ROUND_UP), then derive the 3-bit hw encoding as the minimum of order_base_2(us) and 7. If the property is not present or cannot be read, default to 7. Hardware encoding (PORT_AFR L1 entrance latency, bits 27:29): +--------------------------+----------+ | Advertised maximum | Code | +--------------------------+----------+ | Maximum of 1 us | 000b | +--------------------------+----------+ | Maximum of 2 us | 001b | +--------------------------+----------+ | Maximum of 4 us | 010b | +--------------------------+----------+ | Maximum of 8 us | 011b | +--------------------------+----------+ | Maximum of 16 us | 100b | +--------------------------+----------+ | Maximum of 32 us | 101b | +--------------------------+----------+ | Maximum of 64 us | 110b | +--------------------------+----------+ | Rest | 111b | +--------------------------+----------+ Signed-off-by: Manikanta Maddireddy Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260515070753.3852840-2-mmaddireddy@nvidia.com --- drivers/pci/controller/dwc/pcie-tegra194.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 9dcfa194050e..5309a2f1356d 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -272,6 +272,7 @@ struct tegra_pcie_dw { u32 aspm_cmrt; u32 aspm_pwr_on_t; u32 aspm_l0s_enter_lat; + u32 aspm_l1_enter_lat; struct regulator *pex_ctl_supply; struct regulator *slot_ctl_3v3; @@ -715,6 +716,8 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie) val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR); val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK; val |= (pcie->aspm_l0s_enter_lat << PORT_AFR_L0S_ENTRANCE_LAT_SHIFT); + val &= ~PORT_AFR_L1_ENTRANCE_LAT_MASK; + val |= (pcie->aspm_l1_enter_lat << PORT_AFR_L1_ENTRANCE_LAT_SHIFT); val |= PORT_AFR_ENTER_ASPM; dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val); } @@ -1115,6 +1118,7 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) { struct platform_device *pdev = to_platform_device(pcie->dev); struct device_node *np = pcie->dev->of_node; + u32 val; int ret; pcie->dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); @@ -1141,6 +1145,15 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) dev_info(pcie->dev, "Failed to read ASPM L0s Entrance latency: %d\n", ret); + /* Default to max latency of 7. */ + pcie->aspm_l1_enter_lat = 7; + ret = of_property_read_u32(np, "aspm-l1-entry-delay-ns", &val); + if (!ret) { + u32 us = DIV_ROUND_UP(val, 1000); + + pcie->aspm_l1_enter_lat = min_t(u32, order_base_2(us), 7); + } + ret = of_property_read_u32(np, "num-lanes", &pcie->num_lanes); if (ret < 0) { dev_err(pcie->dev, "Failed to read num-lanes: %d\n", ret); From 94ac934d2c054fba4a22d8dc84749094c5fa0ec0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 May 2026 13:17:55 +0300 Subject: [PATCH 1148/5214] PCI: dwc: Fix signedness bug in fault injection test code The kstrtou32() function returns negative error code or zero on success. However, in this case "val" is a u32 and the function returns signed long, so negative error codes from kstrtou32() are returned as high positive values. Store the error code in an int instead. Fixes: d20ee8e2dbd6 ("PCI: dwc: Add debugfs based Error Injection support for DWC") Signed-off-by: Dan Carpenter Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/agL-Uwfn26SI4Gb0@stanley.mountain --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 87f5ec9c48eb..ddbce69b2e9f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -306,6 +306,7 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, u32 val, counter, vc_num, err_group, type_mask; int val_diff = 0; char *kern_buf; + int ret; err_group = err_inj_list[pdata->idx].err_inj_group; type_mask = err_inj_type_mask[err_group]; @@ -327,10 +328,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, return -EINVAL; } } else { - val = kstrtou32(kern_buf, 0, &counter); - if (val) { + ret = kstrtou32(kern_buf, 0, &counter); + if (ret) { kfree(kern_buf); - return val; + return ret; } } From 8ba433753d9b131c2e43b1ff7ba8c5730cef8231 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 12 May 2026 18:33:45 +0800 Subject: [PATCH 1149/5214] PCI: mediatek-gen3: Fix incorrectly skipped pwrctrl error message When pwrctrl integration was added, the error message for pci_pwrctrl_create_devices() failure was incorrectly added after the goto statement, causing it to be skipped. Move the goto statement after the dev_err_probe() call so that the error message actually gets printed (or saved if probe is deferred). Fixes: 1a152e21940a ("PCI: mediatek-gen3: Integrate new pwrctrl API") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/adjNaKB5KGpl6qIp@stanley.mountain/ Signed-off-by: Chen-Yu Tsai Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260512103347.1751080-1-wenst@chromium.org --- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 1da2166d1017..654e63f8fb57 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1242,8 +1242,8 @@ static int mtk_pcie_probe(struct platform_device *pdev) err = pci_pwrctrl_create_devices(pcie->dev); if (err) { - goto err_tear_down_irq; dev_err_probe(dev, err, "failed to create pwrctrl devices\n"); + goto err_tear_down_irq; } err = mtk_pcie_setup(pcie); From d24d7339757aa5f3397cfdbba931ac39dfd4ba5e Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 7 May 2026 15:14:39 +0200 Subject: [PATCH 1150/5214] phy: ti: tusb1210: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260507131439.264906-1-marco.crivellari@suse.com Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-tusb1210.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c index c3ae9d7948d7..9956921c094b 100644 --- a/drivers/phy/ti/phy-tusb1210.c +++ b/drivers/phy/ti/phy-tusb1210.c @@ -197,7 +197,7 @@ static void tusb1210_chg_det_set_state(struct tusb1210 *tusb, tusb1210_chg_det_states[new_state], delay_ms); tusb->chg_det_state = new_state; - mod_delayed_work(system_long_wq, &tusb->chg_det_work, + mod_delayed_work(system_dfl_long_wq, &tusb->chg_det_work, msecs_to_jiffies(delay_ms)); } @@ -380,7 +380,7 @@ static int tusb1210_psy_notifier(struct notifier_block *nb, struct power_supply *psy = ptr; if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB) - queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0); + queue_delayed_work(system_dfl_long_wq, &tusb->chg_det_work, 0); return NOTIFY_OK; } @@ -458,7 +458,7 @@ static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) */ tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED; INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work); - queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ); + queue_delayed_work(system_dfl_long_wq, &tusb->chg_det_work, 2 * HZ); tusb->psy_nb.notifier_call = tusb1210_psy_notifier; power_supply_reg_notifier(&tusb->psy_nb); From dfd9538e78f57705e6c656d3ff5660ae7137789c Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 14 May 2026 00:22:20 +0530 Subject: [PATCH 1151/5214] dt-bindings: dma: qcom,bam-dma: Document BAM v2.0.0 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document compatible string for bam v2.0.0 version found on kaanapali. BAM v2.0.0 differs from the earlier v1.7.X revision in terms of register layout and offsets, requiring a distinct compatible for correct hardware description. Also add a new example for BAM v2.0.0 to illustrate a more complete configuration than the existing v1.4 example. The new example covers 64-bit address and size cells, IOMMU bindings and execution environment–related properties required on newer platforms. Signed-off-by: Kuldeep Singh Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260514-knp_qce-v2-1-890e3372eef8@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/qcom,bam-dma.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml index 6493a6968bb4..0923fb189ada 100644 --- a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml @@ -23,6 +23,8 @@ properties: - qcom,bam-v1.4.0 # MSM8916, SDM630 - qcom,bam-v1.7.0 + # Kaanapali + - qcom,bam-v2.0.0 - items: - enum: # SDM845, SM6115, SM8150, SM8250 and QCM2290 @@ -118,4 +120,23 @@ examples: #dma-cells = <1>; qcom,ee = <0>; }; + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dma-controller@1dc4000 { + compatible = "qcom,bam-v2.0.0"; + reg = <0x0 0x01dc4000 0x0 0x22000>; + interrupts = ; + #dma-cells = <1>; + iommus = <&apps_smmu 0xc0 0>, <&apps_smmu 0xc1 0>; + qcom,ee = <0>; + qcom,num-ees = <4>; + num-channels = <20>; + qcom,controlled-remotely; + }; + }; ... From 3331fc194b129582fa21cb3a7e5cc68aaac1081f Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 14 May 2026 00:22:21 +0530 Subject: [PATCH 1152/5214] dmaengine: qcom: bam_dma: Add support for BAM v2.0.0 Add register offset table entry for bam v2.0.0 version found on kaanapali. Signed-off-by: Kuldeep Singh Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260514-knp_qce-v2-2-890e3372eef8@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/dma/qcom/bam_dma.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 19116295f832..1bb26af0405f 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -199,6 +199,35 @@ static const struct reg_offset_data bam_v1_7_reg_info[] = { [BAM_P_FIFO_SIZES] = { 0x13820, 0x00, 0x1000, 0x00 }, }; +static const struct reg_offset_data bam_v2_0_reg_info[] = { + [BAM_CTRL] = { 0x0000, 0x00, 0x00, 0x00 }, + [BAM_REVISION] = { 0x1000, 0x00, 0x00, 0x00 }, + [BAM_NUM_PIPES] = { 0x1008, 0x00, 0x00, 0x00 }, + [BAM_DESC_CNT_TRSHLD] = { 0x0008, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS] = { 0x3010, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_MSK] = { 0x3014, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_UNMASKED] = { 0x3018, 0x00, 0x00, 0x00 }, + [BAM_IRQ_STTS] = { 0x0014, 0x00, 0x00, 0x00 }, + [BAM_IRQ_CLR] = { 0x0018, 0x00, 0x00, 0x00 }, + [BAM_IRQ_EN] = { 0x001C, 0x00, 0x00, 0x00 }, + [BAM_CNFG_BITS] = { 0x007C, 0x00, 0x00, 0x00 }, + [BAM_IRQ_SRCS_EE] = { 0x3000, 0x00, 0x00, 0x1000 }, + [BAM_IRQ_SRCS_MSK_EE] = { 0x3004, 0x00, 0x00, 0x1000 }, + [BAM_P_CTRL] = { 0xC000, 0x1000, 0x00, 0x00 }, + [BAM_P_RST] = { 0xC004, 0x1000, 0x00, 0x00 }, + [BAM_P_HALT] = { 0xC008, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_STTS] = { 0xC010, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_CLR] = { 0xC014, 0x1000, 0x00, 0x00 }, + [BAM_P_IRQ_EN] = { 0xC018, 0x1000, 0x00, 0x00 }, + [BAM_P_EVNT_DEST_ADDR] = { 0xC82C, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_REG] = { 0xC818, 0x00, 0x1000, 0x00 }, + [BAM_P_SW_OFSTS] = { 0xC800, 0x00, 0x1000, 0x00 }, + [BAM_P_DATA_FIFO_ADDR] = { 0xC824, 0x00, 0x1000, 0x00 }, + [BAM_P_DESC_FIFO_ADDR] = { 0xC81C, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_GEN_TRSHLD] = { 0xC828, 0x00, 0x1000, 0x00 }, + [BAM_P_FIFO_SIZES] = { 0xC820, 0x00, 0x1000, 0x00 }, +}; + /* BAM CTRL */ #define BAM_SW_RST BIT(0) #define BAM_EN BIT(1) @@ -1208,6 +1237,7 @@ static const struct of_device_id bam_of_match[] = { { .compatible = "qcom,bam-v1.3.0", .data = &bam_v1_3_reg_info }, { .compatible = "qcom,bam-v1.4.0", .data = &bam_v1_4_reg_info }, { .compatible = "qcom,bam-v1.7.0", .data = &bam_v1_7_reg_info }, + { .compatible = "qcom,bam-v2.0.0", .data = &bam_v2_0_reg_info }, {} }; From 998514806998f76d367f935940d0c5e5e5cd12c6 Mon Sep 17 00:00:00 2001 From: Xueyao An Date: Wed, 1 Apr 2026 18:10:28 +0530 Subject: [PATCH 1153/5214] dt-bindings: dma: qcom,gpi: Document GPI DMA engine for Hawi SoC The Hawi GPI DMA engine follows the same programming model and register interface as previous generation of Qualcomm SoCs like kaanapali, glymur, and is fully compatible with earlier GPI DMA implementations. Reviewed-by: Konrad Dybcio Signed-off-by: Xueyao An Signed-off-by: Mukesh Ojha Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260401124028.589931-1-mukesh.ojha@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml index fde1df035ad1..caa2ef90d8f2 100644 --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -25,6 +25,7 @@ properties: - items: - enum: - qcom,glymur-gpi-dma + - qcom,hawi-gpi-dma - qcom,kaanapali-gpi-dma - qcom,milos-gpi-dma - qcom,qcm2290-gpi-dma From 55620b11186c81757b05fb8e2df9ddc7127d6fd2 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:41 +0800 Subject: [PATCH 1154/5214] dt-bindings: dmaengine: Add SpacemiT K3 DMA compatible string Add the "spacemit,k3-pdma" compatible string for the SpacemiT K3 SoC. While the K3 PDMA IP reuses most of the design found on the earlier K1 SoC, a new compatible string is required because the DRCMR (DMA Request/Command Register) base address for extended DMA request numbers (>= 64) differs from the K1 implementation. Signed-off-by: Guodong Xu Acked-by: Conor Dooley Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-1-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml index ec06235baf5c..62ce6d81526b 100644 --- a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml +++ b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: spacemit,k1-pdma + enum: + - spacemit,k1-pdma + - spacemit,k3-pdma reg: maxItems: 1 From f46b47623e70dea8b03794a5420ffba060425e85 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:42 +0800 Subject: [PATCH 1155/5214] dmaengine: mmp_pdma: refactor DRCMR access with helper function Refactor the DRCMR macro into a helper function mmp_pdma_get_drcmr() to support variable extended DRCMR base addresses across different PDMA implementations, such as SpacemiT K3. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-2-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index d12e729ee12c..6112369006ee 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -51,7 +51,9 @@ #define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */ #define DCSR_EORINTR BIT(9) /* The end of Receive */ -#define DRCMR(n) ((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2)) +#define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_DEFAULT 0x1100 +#define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ #define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ @@ -154,6 +156,7 @@ struct mmp_pdma_phy { * @run_bits: Control bits in DCSR register for channel start/stop * @dma_width: DMA addressing width in bits (32 or 64). Determines the * DMA mask capability of the controller hardware. + * @drcmr_ext_base: Base DRCMR address for extended requests */ struct mmp_pdma_ops { /* Hardware Register Operations */ @@ -174,6 +177,7 @@ struct mmp_pdma_ops { /* Controller Configuration */ u32 run_bits; u32 dma_width; + u32 drcmr_ext_base; }; struct mmp_pdma_device { @@ -195,6 +199,13 @@ struct mmp_pdma_device { #define to_mmp_pdma_dev(dmadev) \ container_of(dmadev, struct mmp_pdma_device, device) +static u32 mmp_pdma_get_drcmr(struct mmp_pdma_device *pdev, u32 drcmr) +{ + if (drcmr < DRCMR_REQ_LIMIT) + return DRCMR_BASE + (drcmr << 2); + return pdev->ops->drcmr_ext_base + ((drcmr - DRCMR_REQ_LIMIT) << 2); +} + /* For 32-bit PDMA */ static void write_next_addr_32(struct mmp_pdma_phy *phy, dma_addr_t addr) { @@ -301,7 +312,7 @@ static void enable_chan(struct mmp_pdma_phy *phy) pdev = to_mmp_pdma_dev(phy->vchan->chan.device); - reg = DRCMR(phy->vchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); dalgn = readl(phy->base + DALGN); @@ -437,7 +448,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) return; /* clear the channel mapping in DRCMR */ - reg = DRCMR(pchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, pchan->drcmr); writel(0, pchan->phy->base + reg); spin_lock_irqsave(&pdev->phy_lock, flags); @@ -1179,6 +1190,7 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = { .get_desc_dst_addr = get_desc_dst_addr_32, .run_bits = (DCSR_RUN), .dma_width = 32, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { @@ -1192,6 +1204,7 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .get_desc_dst_addr = get_desc_dst_addr_64, .run_bits = (DCSR_RUN | DCSR_LPAEEN), .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; static const struct of_device_id mmp_pdma_dt_ids[] = { From 6587b8661a0b61c2f4b260bfc9f0e9ef9de0ea2e Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:43 +0800 Subject: [PATCH 1156/5214] dmaengine: mmp_pdma: add SpacemiT K3 support SpacemiT K3 reuses most of the PDMA IP design found on K1, with one difference being the extended DRCMR base address. Add "spacemit,k3-pdma" compatible string and define a new mmp_pdma_ops for K3 PDMA. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-3-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 6112369006ee..386e85cd4882 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -52,6 +52,7 @@ #define DCSR_EORINTR BIT(9) /* The end of Receive */ #define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_K3 0x1000 #define DRCMR_EXT_BASE_DEFAULT 0x1100 #define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ @@ -1207,6 +1208,20 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; +static const struct mmp_pdma_ops spacemit_k3_pdma_ops = { + .write_next_addr = write_next_addr_64, + .read_src_addr = read_src_addr_64, + .read_dst_addr = read_dst_addr_64, + .set_desc_next_addr = set_desc_next_addr_64, + .set_desc_src_addr = set_desc_src_addr_64, + .set_desc_dst_addr = set_desc_dst_addr_64, + .get_desc_src_addr = get_desc_src_addr_64, + .get_desc_dst_addr = get_desc_dst_addr_64, + .run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN), + .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_K3, +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { { .compatible = "marvell,pdma-1.0", @@ -1214,6 +1229,9 @@ static const struct of_device_id mmp_pdma_dt_ids[] = { }, { .compatible = "spacemit,k1-pdma", .data = &spacemit_k1_pdma_ops + }, { + .compatible = "spacemit,k3-pdma", + .data = &spacemit_k3_pdma_ops }, { /* sentinel */ } From 33a6c96b31035fccf6968e2d35e3b727cd42580b Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Fri, 15 May 2026 14:39:36 +0300 Subject: [PATCH 1157/5214] dt-bindings: dma: qcom,gpi: Document the Eliza GPI DMA engine Document the GPI DMA engine found on the Eliza SoC. It is fully compatible with the GPI DMA engine found on SM6350, thus using qcom,sm6350-gpi-dma as fallback compatible. Acked-by: Krzysztof Kozlowski Signed-off-by: Abel Vesa Reviewed-by: Pankaj Patil Link: https://patch.msgid.link/20260515-eliza-gpi-dma-v2-1-1255b43d5ca9@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml index caa2ef90d8f2..8f9a552fe30e 100644 --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -24,6 +24,7 @@ properties: - qcom,sm6350-gpi-dma - items: - enum: + - qcom,eliza-gpi-dma - qcom,glymur-gpi-dma - qcom,hawi-gpi-dma - qcom,kaanapali-gpi-dma From 362ee0c0dc522bcf585bde59ceba2038ec583b7d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 14 May 2026 08:05:26 +0200 Subject: [PATCH 1158/5214] dmaengine: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the drivers already have this correctly placed, so adjust the missing ones. No functional impact. Reviewed-by: Radhey Shyam Pandey Reviewed-by: Frank Li Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260514060525.9253-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/dma/hisi_dma.c | 2 +- drivers/dma/pch_dma.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c index 32a0e95c6a20..28bf818f9aa6 100644 --- a/drivers/dma/hisi_dma.c +++ b/drivers/dma/hisi_dma.c @@ -1037,6 +1037,7 @@ static const struct pci_device_id hisi_dma_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa122) }, { 0, } }; +MODULE_DEVICE_TABLE(pci, hisi_dma_pci_tbl); static struct pci_driver hisi_dma_pci_driver = { .name = "hisi_dma", @@ -1050,4 +1051,3 @@ MODULE_AUTHOR("Zhou Wang "); MODULE_AUTHOR("Zhenfa Qiu "); MODULE_DESCRIPTION("HiSilicon Kunpeng DMA controller driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, hisi_dma_pci_tbl); diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index e9fbfd5a3d51..bf805f1024f6 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -970,6 +970,7 @@ static const struct pci_device_id pch_dma_id_table[] = { { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA2_4CH), 4}, /* SPI */ { 0, }, }; +MODULE_DEVICE_TABLE(pci, pch_dma_id_table); static SIMPLE_DEV_PM_OPS(pch_dma_pm_ops, pch_dma_suspend, pch_dma_resume); @@ -987,4 +988,3 @@ MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH " "DMA controller driver"); MODULE_AUTHOR("Yong Wang "); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, pch_dma_id_table); From 8a8ba84886c905bd43dbdc3b3f78c7ba8f8661f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 17:01:56 +0200 Subject: [PATCH 1159/5214] ipmi: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. While touching these arrays, unify usage of whitespace in the list terminator. Signed-off-by: Uwe Kleine-König (The Capable Hub) Message-ID: <20260519150156.1590826-2-u.kleine-koenig@baylibre.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmb_dev_int.c | 4 ++-- drivers/char/ipmi/ipmi_ipmb.c | 4 ++-- drivers/char/ipmi/ipmi_ssif.c | 2 +- drivers/char/ipmi/ssif_bmc.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 2fe1d205ce4e..680ff15c30ab 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -353,8 +353,8 @@ static void ipmb_remove(struct i2c_client *client) } static const struct i2c_device_id ipmb_id[] = { - { "ipmb-dev" }, - {} + { .name = "ipmb-dev" }, + { } }; MODULE_DEVICE_TABLE(i2c, ipmb_id); diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c index 28818952a7a4..1f1e5718f082 100644 --- a/drivers/char/ipmi/ipmi_ipmb.c +++ b/drivers/char/ipmi/ipmi_ipmb.c @@ -566,8 +566,8 @@ MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match); #endif static const struct i2c_device_id ipmi_ipmb_id[] = { - { DEVICE_NAME }, - {} + { .name = DEVICE_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index f419b46bf002..add043b812ea 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -2094,7 +2094,7 @@ static int dmi_ipmi_probe(struct platform_device *pdev) #endif static const struct i2c_device_id ssif_id[] = { - { DEVICE_NAME }, + { .name = DEVICE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, ssif_id); diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c index 1df0e9284ad9..6036897725f3 100644 --- a/drivers/char/ipmi/ssif_bmc.c +++ b/drivers/char/ipmi/ssif_bmc.c @@ -874,7 +874,7 @@ static const struct of_device_id ssif_bmc_match[] = { MODULE_DEVICE_TABLE(of, ssif_bmc_match); static const struct i2c_device_id ssif_bmc_id[] = { - { DEVICE_NAME }, + { .name = DEVICE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, ssif_bmc_id); From e373c789bac0ad73b472d8b44714df3bd18a4edf Mon Sep 17 00:00:00 2001 From: Ziyao Li Date: Sun, 12 Apr 2026 18:17:31 +0800 Subject: [PATCH 1160/5214] PCI: loongson: Override PCIe bridge supported speeds for Loongson-3C6000 series Older steppings of the Loongson-3C6000 series incorrectly report the supported link speeds on their PCIe bridges (device IDs 0x3c19, 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds from 2.5 GT/s up to 16 GT/s. As a result, since commit 774c71c52aa4 ("PCI/bwctrl: Enable only if more than one speed is supported"), bwctrl will be disabled if there's only one 2.5 GT/s value in vector 'supported_speeds'. Manually override the 'supported_speeds' field for affected PCIe bridges with those found on the upstream bus to correctly reflect the supported link speeds. Updating the speeds to reflect what the hardware actually supports avoids quirks in drivers consuming the speed information. This commit was originally found from AOSC OS[1]. Fixes: cd89edda4002 ("PCI: loongson: Add ACPI init support") Signed-off-by: Ayden Meng Signed-off-by: Mingcong Bai [Ziyao Li: move from drivers/pci/quirks.c to drivers/pci/controller/pci-loongson.c] Signed-off-by: Ziyao Li [Xi Ruoyao: Fixed falling through logic, added debug log, Fixes tag and rebased to 7.0-rc7] Signed-off-by: Xi Ruoyao Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log, https://lore.kernel.org/all/9d815df3b33a63223112b97440c01247935363c1.camel@xry111.site] Signed-off-by: Bjorn Helgaas Tested-by: Lain Fearyncess Yang Tested-by: Ayden Meng Tested-by: Mingcong Bai Reviewed-by: Huacai Chen Cc: stable@vger.kernel.org Link: https://github.com/AOSC-Tracking/linux/commit/4392f441363abdf6fa0a0433d73175a17f493454 Link: https://github.com/AOSC-Tracking/linux/pull/2 #1 Link: https://patch.msgid.link/20260412101731.107059-1-xry111@xry111.site --- drivers/pci/controller/pci-loongson.c | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index de5e809a537d..d0c643996476 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -177,6 +177,42 @@ static void loongson_pci_msi_quirk(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_pci_msi_quirk); +/* + * Older steppings of the Loongson-3C6000 series incorrectly report the + * supported link speeds on their PCIe bridges (device IDs 0x3c19, + * 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds + * from 2.5 GT/s up to 16 GT/s. + */ +static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev) +{ + u8 old_supported_speeds = pdev->supported_speeds; + + switch (pdev->bus->max_bus_speed) { + case PCIE_SPEED_16_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_16_0GB; + fallthrough; + case PCIE_SPEED_8_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_8_0GB; + fallthrough; + case PCIE_SPEED_5_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_5_0GB; + fallthrough; + case PCIE_SPEED_2_5GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_2_5GB; + break; + default: + pci_warn(pdev, "unexpected max bus speed"); + + return; + } + + if (pdev->supported_speeds != old_supported_speeds) + pci_info(pdev, "fixed up supported link speeds: 0x%x => 0x%x", + old_supported_speeds, pdev->supported_speeds); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; From baa590018fbfc3e7384b604c7a7951eecf10a523 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:35 +0530 Subject: [PATCH 1161/5214] powerpc: rename arch_irq_disabled_regs Rename arch_irq_disabled_regs() to regs_irqs_disabled() to align with the naming used in the generic irqentry framework. This makes the function available for use both in the PowerPC architecture code and in the common entry/exit paths shared with other architectures. This is a preparatory change for enabling the generic irqentry framework on PowerPC. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Reviewed-by: Jinjie Ruan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-2-mkchauras@gmail.com --- arch/powerpc/include/asm/hw_irq.h | 4 ++-- arch/powerpc/include/asm/interrupt.h | 16 ++++++++-------- arch/powerpc/kernel/interrupt.c | 4 ++-- arch/powerpc/kernel/syscall.c | 2 +- arch/powerpc/kernel/traps.c | 2 +- arch/powerpc/kernel/watchdog.c | 2 +- arch/powerpc/perf/core-book3s.c | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index 9cd945f2acaf..b7eee6385ae5 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -393,7 +393,7 @@ static inline void do_hard_irq_enable(void) __hard_irq_enable(); } -static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +static inline bool regs_irqs_disabled(struct pt_regs *regs) { return (regs->softe & IRQS_DISABLED); } @@ -466,7 +466,7 @@ static inline bool arch_irqs_disabled(void) #define hard_irq_disable() arch_local_irq_disable() -static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +static inline bool regs_irqs_disabled(struct pt_regs *regs) { return !(regs->msr & MSR_EE); } diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index eb0e4a20b818..0e2cddf8bd21 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -172,7 +172,7 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs) /* Enable MSR[RI] early, to support kernel SLB and hash faults */ #endif - if (!arch_irq_disabled_regs(regs)) + if (!regs_irqs_disabled(regs)) trace_hardirqs_off(); if (user_mode(regs)) { @@ -192,11 +192,11 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs) CT_WARN_ON(ct_state() != CT_STATE_KERNEL && ct_state() != CT_STATE_IDLE); INT_SOFT_MASK_BUG_ON(regs, is_implicit_soft_masked(regs)); - INT_SOFT_MASK_BUG_ON(regs, arch_irq_disabled_regs(regs) && - search_kernel_restart_table(regs->nip)); + INT_SOFT_MASK_BUG_ON(regs, regs_irqs_disabled(regs) && + search_kernel_restart_table(regs->nip)); } - INT_SOFT_MASK_BUG_ON(regs, !arch_irq_disabled_regs(regs) && - !(regs->msr & MSR_EE)); + INT_SOFT_MASK_BUG_ON(regs, !regs_irqs_disabled(regs) && + !(regs->msr & MSR_EE)); booke_restore_dbcr0(); } @@ -298,7 +298,7 @@ static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct inte * Adjust regs->softe to be soft-masked if it had not been * reconcied (e.g., interrupt entry with MSR[EE]=0 but softe * not yet set disabled), or if it was in an implicit soft - * masked state. This makes arch_irq_disabled_regs(regs) + * masked state. This makes regs_irqs_disabled(regs) * behave as expected. */ regs->softe = IRQS_ALL_DISABLED; @@ -372,7 +372,7 @@ static inline void interrupt_nmi_exit_prepare(struct pt_regs *regs, struct inter #ifdef CONFIG_PPC64 #ifdef CONFIG_PPC_BOOK3S - if (arch_irq_disabled_regs(regs)) { + if (regs_irqs_disabled(regs)) { unsigned long rst = search_kernel_restart_table(regs->nip); if (rst) regs_set_return_ip(regs, rst); @@ -661,7 +661,7 @@ void replay_soft_interrupts(void); static inline void interrupt_cond_local_irq_enable(struct pt_regs *regs) { - if (!arch_irq_disabled_regs(regs)) + if (!regs_irqs_disabled(regs)) local_irq_enable(); } diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index e63bfde13e03..666eadb589a5 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -347,7 +347,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) unsigned long ret; BUG_ON(regs_is_unrecoverable(regs)); - BUG_ON(arch_irq_disabled_regs(regs)); + BUG_ON(regs_irqs_disabled(regs)); CT_WARN_ON(ct_state() == CT_STATE_USER); /* @@ -396,7 +396,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) local_irq_disable(); - if (!arch_irq_disabled_regs(regs)) { + if (!regs_irqs_disabled(regs)) { /* Returning to a kernel context with local irqs enabled. */ WARN_ON_ONCE(!(regs->msr & MSR_EE)); again: diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index b762677f8737..52d6e10eab22 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -32,7 +32,7 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) BUG_ON(regs_is_unrecoverable(regs)); BUG_ON(!user_mode(regs)); - BUG_ON(arch_irq_disabled_regs(regs)); + BUG_ON(regs_irqs_disabled(regs)); #ifdef CONFIG_PPC_PKEY if (mmu_has_feature(MMU_FTR_PKEY)) { diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index cb8e9357383e..629f2a2d4780 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1956,7 +1956,7 @@ DEFINE_INTERRUPT_HANDLER_RAW(performance_monitor_exception) * prevent hash faults on user addresses when reading callchains (and * looks better from an irq tracing perspective). */ - if (IS_ENABLED(CONFIG_PPC64) && unlikely(arch_irq_disabled_regs(regs))) + if (IS_ENABLED(CONFIG_PPC64) && unlikely(regs_irqs_disabled(regs))) performance_monitor_exception_nmi(regs); else performance_monitor_exception_async(regs); diff --git a/arch/powerpc/kernel/watchdog.c b/arch/powerpc/kernel/watchdog.c index 764001deb060..c40c69368476 100644 --- a/arch/powerpc/kernel/watchdog.c +++ b/arch/powerpc/kernel/watchdog.c @@ -376,7 +376,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(soft_nmi_interrupt) u64 tb; /* should only arrive from kernel, with irqs disabled */ - WARN_ON_ONCE(!arch_irq_disabled_regs(regs)); + WARN_ON_ONCE(!regs_irqs_disabled(regs)); if (!cpumask_test_cpu(cpu, &wd_cpus_enabled)) return 0; diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 2e6adf5b95c4..802bc9b7ef6f 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -2483,7 +2483,7 @@ static void __perf_event_interrupt(struct pt_regs *regs) * will trigger a PMI after waking up from idle. Since counter values are _not_ * saved/restored in idle path, can lead to below "Can't find PMC" message. */ - if (unlikely(!found) && !arch_irq_disabled_regs(regs)) + if (unlikely(!found) && !regs_irqs_disabled(regs)) printk_ratelimited(KERN_WARNING "Can't find PMC that caused IRQ\n"); /* From dec63ea6fafffb91811381d9a4686d4bc39f01ae Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:36 +0530 Subject: [PATCH 1162/5214] powerpc: Prepare to build with generic entry/exit framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces preparatory changes needed to support building PowerPC with the generic entry/exit (irqentry) framework. The following infrastructure updates are added: - Add a syscall_work field to struct thread_info to hold SYSCALL_WORK_* flags. - Provide a stub implementation of arch_syscall_is_vdso_sigreturn(), returning false for now. - Introduce on_thread_stack() helper to detect if the current stack pointer lies within the task’s kernel stack. These additions enable later integration with the generic entry/exit infrastructure while keeping existing PowerPC behavior unchanged. No functional change is intended in this patch. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-3-mkchauras@gmail.com --- arch/powerpc/include/asm/entry-common.h | 8 ++++++++ arch/powerpc/include/asm/stacktrace.h | 6 ++++++ arch/powerpc/include/asm/syscall.h | 5 +++++ arch/powerpc/include/asm/thread_info.h | 1 + 4 files changed, 20 insertions(+) create mode 100644 arch/powerpc/include/asm/entry-common.h diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h new file mode 100644 index 000000000000..05ce0583b600 --- /dev/null +++ b/arch/powerpc/include/asm/entry-common.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_PPC_ENTRY_COMMON_H +#define _ASM_PPC_ENTRY_COMMON_H + +#include + +#endif /* _ASM_PPC_ENTRY_COMMON_H */ diff --git a/arch/powerpc/include/asm/stacktrace.h b/arch/powerpc/include/asm/stacktrace.h index 6149b53b3bc8..987f2e996262 100644 --- a/arch/powerpc/include/asm/stacktrace.h +++ b/arch/powerpc/include/asm/stacktrace.h @@ -10,4 +10,10 @@ void show_user_instructions(struct pt_regs *regs); +static __always_inline bool on_thread_stack(void) +{ + return !(((unsigned long)(current->stack) ^ current_stack_pointer) + & ~(THREAD_SIZE - 1)); +} + #endif /* _ASM_POWERPC_STACKTRACE_H */ diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h index 4b3c52ed6e9d..834fcc4f7b54 100644 --- a/arch/powerpc/include/asm/syscall.h +++ b/arch/powerpc/include/asm/syscall.h @@ -139,4 +139,9 @@ static inline int syscall_get_arch(struct task_struct *task) else return AUDIT_ARCH_PPC64; } + +static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) +{ + return false; +} #endif /* _ASM_SYSCALL_H */ diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 97f35f9b1a96..ee3b9adb5b67 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -57,6 +57,7 @@ struct thread_info { #ifdef CONFIG_SMP unsigned int cpu; #endif + unsigned long syscall_work; /* SYSCALL_WORK_ flags */ unsigned long local_flags; /* private flags for thread */ #ifdef CONFIG_LIVEPATCH_64 unsigned long *livepatch_sp; From 09a9d3a8499db606563eb5e75a993419f4d1901e Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:37 +0530 Subject: [PATCH 1163/5214] powerpc: introduce arch_enter_from_user_mode Implement the arch_enter_from_user_mode() hook required by the generic entry/exit framework. This helper prepares the CPU state when entering the kernel from userspace, ensuring correct handling of KUAP/KUEP, transactional memory, and debug register state. This patch contains no functional changes, it is purely preparatory for enabling the generic syscall and interrupt entry path on PowerPC. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-4-mkchauras@gmail.com --- arch/powerpc/include/asm/entry-common.h | 118 ++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index 05ce0583b600..837a7e020e82 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -3,6 +3,124 @@ #ifndef _ASM_PPC_ENTRY_COMMON_H #define _ASM_PPC_ENTRY_COMMON_H +#include +#include #include +#include + +static __always_inline void booke_load_dbcr0(void) +{ +#ifdef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dbcr0 = current->thread.debug.dbcr0; + + if (likely(!(dbcr0 & DBCR0_IDM))) + return; + + /* + * Check to see if the dbcr0 register is set up to debug. + * Use the internal debug mode bit to do this. + */ + mtmsr(mfmsr() & ~MSR_DE); + if (IS_ENABLED(CONFIG_PPC32)) { + isync(); + global_dbcr0[smp_processor_id()] = mfspr(SPRN_DBCR0); + } + mtspr(SPRN_DBCR0, dbcr0); + mtspr(SPRN_DBSR, -1); +#endif +} + +static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs) +{ + kuap_lock(); + + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) + BUG_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED); + + BUG_ON(regs_is_unrecoverable(regs)); + BUG_ON(!user_mode(regs)); + BUG_ON(regs_irqs_disabled(regs)); + +#ifdef CONFIG_PPC_PKEY + if (mmu_has_feature(MMU_FTR_PKEY) && trap_is_syscall(regs)) { + unsigned long amr, iamr; + bool flush_needed = false; + /* + * When entering from userspace we mostly have the AMR/IAMR + * different from kernel default values. Hence don't compare. + */ + amr = mfspr(SPRN_AMR); + iamr = mfspr(SPRN_IAMR); + regs->amr = amr; + regs->iamr = iamr; + if (mmu_has_feature(MMU_FTR_KUAP)) { + mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); + flush_needed = true; + } + if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP)) { + mtspr(SPRN_IAMR, AMR_KUEP_BLOCKED); + flush_needed = true; + } + if (flush_needed) + isync(); + } +#endif + kuap_assert_locked(); + booke_restore_dbcr0(); + account_cpu_user_entry(); + account_stolen_time(); + + /* + * This is not required for the syscall exit path, but makes the + * stack frame look nicer. If this was initialised in the first stack + * frame, or if the unwinder was taught the first stack frame always + * returns to user with IRQS_ENABLED, this store could be avoided! + */ + irq_soft_mask_regs_set_state(regs, IRQS_ENABLED); + + /* + * If system call is called with TM active, set _TIF_RESTOREALL to + * prevent RFSCV being used to return to userspace, because POWER9 + * TM implementation has problems with this instruction returning to + * transactional state. Final register values are not relevant because + * the transaction will be aborted upon return anyway. Or in the case + * of unsupported_scv SIGILL fault, the return state does not much + * matter because it's an edge case. + */ + if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && + unlikely(MSR_TM_TRANSACTIONAL(regs->msr))) + set_bits(_TIF_RESTOREALL, ¤t_thread_info()->flags); + + /* + * If the system call was made with a transaction active, doom it and + * return without performing the system call. Unless it was an + * unsupported scv vector, in which case it's treated like an illegal + * instruction. + */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (unlikely(MSR_TM_TRANSACTIONAL(regs->msr)) && + !trap_is_unsupported_scv(regs)) { + /* Enable TM in the kernel, and disable EE (for scv) */ + hard_irq_disable(); + mtmsr(mfmsr() | MSR_TM); + + /* tabort, this dooms the transaction, nothing else */ + asm volatile(".long 0x7c00071d | ((%0) << 16)" + :: "r"(TM_CAUSE_SYSCALL | TM_CAUSE_PERSISTENT)); + + /* + * Userspace will never see the return value. Execution will + * resume after the tbegin. of the aborted transaction with the + * checkpointed register state. A context switch could occur + * or signal delivered to the process before resuming the + * doomed transaction context, but that should all be handled + * as expected. + */ + return; + } +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ +} + +#define arch_enter_from_user_mode arch_enter_from_user_mode #endif /* _ASM_PPC_ENTRY_COMMON_H */ From 02565a782c1ee7e8ada38ad24a698e01d6ea9ff8 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:38 +0530 Subject: [PATCH 1164/5214] powerpc: Introduce syscall exit arch functions Add PowerPC-specific implementations of the generic syscall exit hooks used by the generic entry/exit framework: - arch_exit_to_user_mode_work_prepare() - arch_exit_to_user_mode_work() These helpers handle user state restoration when returning from the kernel to userspace, including FPU/VMX/VSX state, transactional memory, KUAP restore, and per-CPU accounting. Additionally, move check_return_regs_valid() from interrupt.c to interrupt.h so it can be shared by the new entry/exit logic. No functional change is intended with this patch. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-5-mkchauras@gmail.com --- arch/powerpc/include/asm/entry-common.h | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index 837a7e020e82..ff0625e04778 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -6,6 +6,7 @@ #include #include #include +#include #include static __always_inline void booke_load_dbcr0(void) @@ -123,4 +124,52 @@ static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs) #define arch_enter_from_user_mode arch_enter_from_user_mode +static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, + unsigned long ti_work) +{ + unsigned long mathflags; + + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && IS_ENABLED(CONFIG_PPC_FPU)) { + if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && + unlikely((ti_work & _TIF_RESTORE_TM))) { + restore_tm_state(regs); + } else { + mathflags = MSR_FP; + + if (cpu_has_feature(CPU_FTR_VSX)) + mathflags |= MSR_VEC | MSR_VSX; + else if (cpu_has_feature(CPU_FTR_ALTIVEC)) + mathflags |= MSR_VEC; + + /* + * If userspace MSR has all available FP bits set, + * then they are live and no need to restore. If not, + * it means the regs were given up and restore_math + * may decide to restore them (to avoid taking an FP + * fault). + */ + if ((regs->msr & mathflags) != mathflags) + restore_math(regs); + } + } + + check_return_regs_valid(regs); +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + local_paca->tm_scratch = regs->msr; +#endif + /* Restore user access locks last */ + kuap_user_restore(regs); +} + +#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare + +static __always_inline void arch_exit_to_user_mode(void) +{ + booke_load_dbcr0(); + + account_cpu_user_exit(); +} + +#define arch_exit_to_user_mode arch_exit_to_user_mode + #endif /* _ASM_PPC_ENTRY_COMMON_H */ From d7a6797e0bc1ee7bddb8a298f2d1acddb300efb3 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:39 +0530 Subject: [PATCH 1165/5214] powerpc: add exit_flags field in pt_regs Add a new field `exit_flags` in the pt_regs structure. This field will hold the flags set during interrupt or syscall execution that are required during exit to user mode. Specifically, the `TIF_RESTOREALL` flag, stored in this field, helps the exit routine determine if any NVGPRs were modified and need to be restored before returning to userspace. This addition ensures a clean and architecture-specific mechanism to track per-syscall or per-interrupt state transitions related to register restore. Changes: - Add `exit_flags` and `__pt_regs_pad` to maintain 16-byte stack alignment - Update asm-offsets.c and ptrace.c for offset and validation - Update PT_* constants in uapi header to reflect the new layout Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-6-mkchauras@gmail.com --- arch/powerpc/include/asm/ptrace.h | 3 +++ arch/powerpc/include/uapi/asm/ptrace.h | 14 +++++++++----- arch/powerpc/kernel/ptrace/ptrace.c | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 94aa1de2b06e..2e741ea57b80 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -53,6 +53,9 @@ struct pt_regs unsigned long esr; }; unsigned long result; + unsigned long exit_flags; + /* Maintain 16 byte interrupt stack alignment */ + unsigned long __pt_regs_pad[3]; }; }; #if defined(CONFIG_PPC64) || defined(CONFIG_PPC_KUAP) diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h index 01e630149d48..a393b7f2760a 100644 --- a/arch/powerpc/include/uapi/asm/ptrace.h +++ b/arch/powerpc/include/uapi/asm/ptrace.h @@ -55,6 +55,8 @@ struct pt_regs unsigned long dar; /* Fault registers */ unsigned long dsisr; /* on 4xx/Book-E used for ESR */ unsigned long result; /* Result of a system call */ + unsigned long exit_flags; /* System call exit flags */ + unsigned long __pt_regs_pad[3]; /* Maintain 16 byte interrupt stack alignment */ }; #endif /* __ASSEMBLER__ */ @@ -114,10 +116,12 @@ struct pt_regs #define PT_DAR 41 #define PT_DSISR 42 #define PT_RESULT 43 -#define PT_DSCR 44 -#define PT_REGS_COUNT 44 +#define PT_EXIT_FLAGS 44 +#define PT_PAD 47 /* 3 times */ +#define PT_DSCR 48 +#define PT_REGS_COUNT 48 -#define PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ +#define PT_FPR0 (PT_REGS_COUNT + 4) /* each FP reg occupies 2 slots in this space */ #ifndef __powerpc64__ @@ -129,7 +133,7 @@ struct pt_regs #define PT_FPSCR (PT_FPR0 + 32) /* each FP reg occupies 1 slot in 64-bit space */ -#define PT_VR0 82 /* each Vector reg occupies 2 slots in 64-bit */ +#define PT_VR0 (PT_FPSCR + 2) /* <82> each Vector reg occupies 2 slots in 64-bit */ #define PT_VSCR (PT_VR0 + 32*2 + 1) #define PT_VRSAVE (PT_VR0 + 33*2) @@ -137,7 +141,7 @@ struct pt_regs /* * Only store first 32 VSRs here. The second 32 VSRs in VR0-31 */ -#define PT_VSR0 150 /* each VSR reg occupies 2 slots in 64-bit */ +#define PT_VSR0 (PT_VRSAVE + 2) /* each VSR reg occupies 2 slots in 64-bit */ #define PT_VSR31 (PT_VSR0 + 2*31) #endif /* __powerpc64__ */ diff --git a/arch/powerpc/kernel/ptrace/ptrace.c b/arch/powerpc/kernel/ptrace/ptrace.c index c6997df63287..2134b6d155ff 100644 --- a/arch/powerpc/kernel/ptrace/ptrace.c +++ b/arch/powerpc/kernel/ptrace/ptrace.c @@ -432,6 +432,7 @@ void __init pt_regs_check(void) CHECK_REG(PT_DAR, dar); CHECK_REG(PT_DSISR, dsisr); CHECK_REG(PT_RESULT, result); + CHECK_REG(PT_EXIT_FLAGS, exit_flags); #undef CHECK_REG BUILD_BUG_ON(PT_REGS_COUNT != sizeof(struct user_pt_regs) / sizeof(unsigned long)); From 893082ac769ba92a1338e7552d97de769f352a3e Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:40 +0530 Subject: [PATCH 1166/5214] powerpc: Prepare for IRQ entry exit Move interrupt entry and exit helper routines from interrupt.h into the PowerPC-specific entry-common.h header as a preparatory step for enabling the generic entry/exit framework. This consolidation places all PowerPC interrupt entry/exit handling in a single common header, aligning with the generic entry infrastructure. The helpers provide architecture-specific handling for interrupt and NMI entry/exit sequences, including: - arch_interrupt_enter/exit_prepare() - arch_interrupt_async_enter/exit_prepare() - arch_interrupt_nmi_enter/exit_prepare() - Supporting helpers such as nap_adjust_return(), check_return_regs_valid(), debug register maintenance, and soft mask handling. The functions are copied verbatim from interrupt.h.Subsequent patches will integrate these routines into the generic entry/exit flow. No functional change intended. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-7-mkchauras@gmail.com --- arch/powerpc/include/asm/entry-common.h | 358 ++++++++++++++++++++++++ 1 file changed, 358 insertions(+) diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index ff0625e04778..de5601282755 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -5,10 +5,75 @@ #include #include +#include #include #include #include +#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG +/* + * WARN/BUG is handled with a program interrupt so minimise checks here to + * avoid recursion and maximise the chance of getting the first oops handled. + */ +#define INT_SOFT_MASK_BUG_ON(regs, cond) \ +do { \ + if ((user_mode(regs) || (TRAP(regs) != INTERRUPT_PROGRAM))) \ + BUG_ON(cond); \ +} while (0) +#else +#define INT_SOFT_MASK_BUG_ON(regs, cond) +#endif + +#ifdef CONFIG_PPC_BOOK3S_64 +extern char __end_soft_masked[]; +bool search_kernel_soft_mask_table(unsigned long addr); +unsigned long search_kernel_restart_table(unsigned long addr); + +DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); + +static inline bool is_implicit_soft_masked(struct pt_regs *regs) +{ + if (user_mode(regs)) + return false; + + if (regs->nip >= (unsigned long)__end_soft_masked) + return false; + + return search_kernel_soft_mask_table(regs->nip); +} + +static inline void srr_regs_clobbered(void) +{ + local_paca->srr_valid = 0; + local_paca->hsrr_valid = 0; +} +#else +static inline unsigned long search_kernel_restart_table(unsigned long addr) +{ + return 0; +} + +static inline bool is_implicit_soft_masked(struct pt_regs *regs) +{ + return false; +} + +static inline void srr_regs_clobbered(void) +{ +} +#endif + +static inline void nap_adjust_return(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC_970_NAP + if (unlikely(test_thread_local_flags(_TLF_NAPPING))) { + /* Can avoid a test-and-clear because NMIs do not call this */ + clear_thread_local_flags(_TLF_NAPPING); + regs_set_return_ip(regs, (unsigned long)power4_idle_nap_return); + } +#endif +} + static __always_inline void booke_load_dbcr0(void) { #ifdef CONFIG_PPC_ADV_DEBUG_REGS @@ -31,6 +96,299 @@ static __always_inline void booke_load_dbcr0(void) #endif } +static inline void booke_restore_dbcr0(void) +{ +#ifdef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dbcr0 = current->thread.debug.dbcr0; + + if (IS_ENABLED(CONFIG_PPC32) && unlikely(dbcr0 & DBCR0_IDM)) { + mtspr(SPRN_DBSR, -1); + mtspr(SPRN_DBCR0, global_dbcr0[smp_processor_id()]); + } +#endif +} + +static inline void check_return_regs_valid(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC_BOOK3S_64 + unsigned long trap, srr0, srr1; + static bool warned; + u8 *validp; + char *h; + + if (trap_is_scv(regs)) + return; + + trap = TRAP(regs); + // EE in HV mode sets HSRRs like 0xea0 + if (cpu_has_feature(CPU_FTR_HVMODE) && trap == INTERRUPT_EXTERNAL) + trap = 0xea0; + + switch (trap) { + case 0x980: + case INTERRUPT_H_DATA_STORAGE: + case 0xe20: + case 0xe40: + case INTERRUPT_HMI: + case 0xe80: + case 0xea0: + case INTERRUPT_H_FAC_UNAVAIL: + case 0x1200: + case 0x1500: + case 0x1600: + case 0x1800: + validp = &local_paca->hsrr_valid; + if (!READ_ONCE(*validp)) + return; + + srr0 = mfspr(SPRN_HSRR0); + srr1 = mfspr(SPRN_HSRR1); + h = "H"; + + break; + default: + validp = &local_paca->srr_valid; + if (!READ_ONCE(*validp)) + return; + + srr0 = mfspr(SPRN_SRR0); + srr1 = mfspr(SPRN_SRR1); + h = ""; + break; + } + + if (srr0 == regs->nip && srr1 == regs->msr) + return; + + /* + * A NMI / soft-NMI interrupt may have come in after we found + * srr_valid and before the SRRs are loaded. The interrupt then + * comes in and clobbers SRRs and clears srr_valid. Then we load + * the SRRs here and test them above and find they don't match. + * + * Test validity again after that, to catch such false positives. + * + * This test in general will have some window for false negatives + * and may not catch and fix all such cases if an NMI comes in + * later and clobbers SRRs without clearing srr_valid, but hopefully + * such things will get caught most of the time, statistically + * enough to be able to get a warning out. + */ + if (!READ_ONCE(*validp)) + return; + + if (!data_race(warned)) { + data_race(warned = true); + pr_warn("%sSRR0 was: %lx should be: %lx\n", h, srr0, regs->nip); + pr_warn("%sSRR1 was: %lx should be: %lx\n", h, srr1, regs->msr); + show_regs(regs); + } + + WRITE_ONCE(*validp, 0); /* fixup */ +#endif +} + +static inline void arch_interrupt_enter_prepare(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC64 + irq_soft_mask_set(IRQS_ALL_DISABLED); + + /* + * If the interrupt was taken with HARD_DIS clear, then enable MSR[EE]. + * Asynchronous interrupts get here with HARD_DIS set (see below), so + * this enables MSR[EE] for synchronous interrupts. IRQs remain + * soft-masked. The interrupt handler may later call + * interrupt_cond_local_irq_enable() to achieve a regular process + * context. + */ + if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS)) { + INT_SOFT_MASK_BUG_ON(regs, !(regs->msr & MSR_EE)); + __hard_irq_enable(); + } else { + __hard_RI_enable(); + } + /* Enable MSR[RI] early, to support kernel SLB and hash faults */ +#endif + + if (!regs_irqs_disabled(regs)) + trace_hardirqs_off(); + + if (user_mode(regs)) { + kuap_lock(); + account_cpu_user_entry(); + account_stolen_time(); + } else { + kuap_save_and_lock(regs); + /* + * CT_WARN_ON comes here via program_check_exception, + * so avoid recursion. + */ + if (TRAP(regs) != INTERRUPT_PROGRAM) + CT_WARN_ON(ct_state() != CT_STATE_KERNEL && + ct_state() != CT_STATE_IDLE); + INT_SOFT_MASK_BUG_ON(regs, is_implicit_soft_masked(regs)); + INT_SOFT_MASK_BUG_ON(regs, regs_irqs_disabled(regs) && + search_kernel_restart_table(regs->nip)); + } + INT_SOFT_MASK_BUG_ON(regs, !regs_irqs_disabled(regs) && + !(regs->msr & MSR_EE)); + + booke_restore_dbcr0(); +} + +/* + * Care should be taken to note that arch_interrupt_exit_prepare and + * arch_interrupt_async_exit_prepare do not necessarily return immediately to + * regs context (e.g., if regs is usermode, we don't necessarily return to + * user mode). Other interrupts might be taken between here and return, + * context switch / preemption may occur in the exit path after this, or a + * signal may be delivered, etc. + * + * The real interrupt exit code is platform specific, e.g., + * interrupt_exit_user_prepare / interrupt_exit_kernel_prepare for 64s. + * + * However arch_interrupt_nmi_exit_prepare does return directly to regs, because + * NMIs do not do "exit work" or replay soft-masked interrupts. + */ +static inline void arch_interrupt_exit_prepare(struct pt_regs *regs) +{ + if (user_mode(regs)) { + BUG_ON(regs_is_unrecoverable(regs)); + BUG_ON(regs_irqs_disabled(regs)); + /* + * We don't need to restore AMR on the way back to userspace for KUAP. + * AMR can only have been unlocked if we interrupted the kernel. + */ + kuap_assert_locked(); + + local_irq_disable(); + } +} + +static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC64 + /* Ensure arch_interrupt_enter_prepare does not enable MSR[EE] */ + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; +#endif + arch_interrupt_enter_prepare(regs); +#ifdef CONFIG_PPC_BOOK3S_64 + /* + * RI=1 is set by arch_interrupt_enter_prepare, so this thread flags access + * has to come afterward (it can cause SLB faults). + */ + if (cpu_has_feature(CPU_FTR_CTRL) && + !test_thread_local_flags(_TLF_RUNLATCH)) + __ppc64_runlatch_on(); +#endif +} + +static inline void arch_interrupt_async_exit_prepare(struct pt_regs *regs) +{ + /* + * Adjust at exit so the main handler sees the true NIA. This must + * come before irq_exit() because irq_exit can enable interrupts, and + * if another interrupt is taken before nap_adjust_return has run + * here, then that interrupt would return directly to idle nap return. + */ + nap_adjust_return(regs); + + arch_interrupt_exit_prepare(regs); +} + +struct interrupt_nmi_state { +#ifdef CONFIG_PPC64 + u8 irq_soft_mask; + u8 irq_happened; + u8 ftrace_enabled; + u64 softe; +#endif +}; + +static inline bool nmi_disables_ftrace(struct pt_regs *regs) +{ + /* Allow DEC and PMI to be traced when they are soft-NMI */ + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { + if (TRAP(regs) == INTERRUPT_DECREMENTER) + return false; + if (TRAP(regs) == INTERRUPT_PERFMON) + return false; + } + if (IS_ENABLED(CONFIG_PPC_BOOK3E_64)) { + if (TRAP(regs) == INTERRUPT_PERFMON) + return false; + } + + return true; +} + +static inline void arch_interrupt_nmi_enter_prepare(struct pt_regs *regs, + struct interrupt_nmi_state *state) +{ +#ifdef CONFIG_PPC64 + state->irq_soft_mask = local_paca->irq_soft_mask; + state->irq_happened = local_paca->irq_happened; + state->softe = regs->softe; + + /* + * Set IRQS_ALL_DISABLED unconditionally so irqs_disabled() does + * the right thing, and set IRQ_HARD_DIS. We do not want to reconcile + * because that goes through irq tracing which we don't want in NMI. + */ + local_paca->irq_soft_mask = IRQS_ALL_DISABLED; + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; + + if (!(regs->msr & MSR_EE) || is_implicit_soft_masked(regs)) { + /* + * Adjust regs->softe to be soft-masked if it had not been + * reconcied (e.g., interrupt entry with MSR[EE]=0 but softe + * not yet set disabled), or if it was in an implicit soft + * masked state. This makes regs_irqs_disabled(regs) + * behave as expected. + */ + regs->softe = IRQS_ALL_DISABLED; + } + + __hard_RI_enable(); + + /* Don't do any per-CPU operations until interrupt state is fixed */ + + if (nmi_disables_ftrace(regs)) { + state->ftrace_enabled = this_cpu_get_ftrace_enabled(); + this_cpu_set_ftrace_enabled(0); + } +#endif +} + +static inline void arch_interrupt_nmi_exit_prepare(struct pt_regs *regs, + struct interrupt_nmi_state *state) +{ + /* + * nmi does not call nap_adjust_return because nmi should not create + * new work to do (must use irq_work for that). + */ + +#ifdef CONFIG_PPC64 +#ifdef CONFIG_PPC_BOOK3S + if (regs_irqs_disabled(regs)) { + unsigned long rst = search_kernel_restart_table(regs->nip); + + if (rst) + regs_set_return_ip(regs, rst); + } +#endif + + if (nmi_disables_ftrace(regs)) + this_cpu_set_ftrace_enabled(state->ftrace_enabled); + + /* Check we didn't change the pending interrupt mask. */ + WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened); + regs->softe = state->softe; + local_paca->irq_happened = state->irq_happened; + local_paca->irq_soft_mask = state->irq_soft_mask; +#endif +} + static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs) { kuap_lock(); From bee25f97ad24641df55cb3887eb5bfcb2b9d8fbc Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:41 +0530 Subject: [PATCH 1167/5214] powerpc: Enable GENERIC_ENTRY feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the generic IRQ entry/exit infrastructure on PowerPC by selecting GENERIC_ENTRY and integrating the architecture-specific interrupt and syscall handlers with the generic entry/exit APIs. This change replaces PowerPC’s local interrupt entry/exit handling with calls to the generic irqentry_* helpers, aligning the architecture with the common kernel entry model. The macros that define interrupt, async, and NMI handlers are updated to use irqentry_enter()/irqentry_exit() and irqentry_nmi_enter()/irqentry_nmi_exit() where applicable also convert the PowerPC syscall entry and exit paths to use the generic entry/exit framework and integrating with the common syscall handling routines. Key updates include: - The architecture now selects GENERIC_ENTRY in Kconfig. - Replace interrupt_enter/exit_prepare() with arch_interrupt_* helpers. - Integrate irqentry_enter()/exit() in standard and async interrupt paths. - Integrate irqentry_nmi_enter()/exit() in NMI handlers. - Remove redundant irq_enter()/irq_exit() calls now handled generically. - Use irqentry_exit_cond_resched() for preemption checks. - interrupt.c and syscall.c are simplified to delegate context management and user exit handling to the generic entry path. - The new pt_regs field `exit_flags` introduced earlier is now used to carry per-syscall exit state flags (e.g. _TIF_RESTOREALL). - Remove unused code. This change establishes the necessary wiring for PowerPC to use the generic IRQ entry/exit framework while maintaining existing semantics. This aligns PowerPC with the common entry code used by other architectures and reduces duplicated logic around syscall tracing, context tracking, and signal handling. The performance benchmarks from perf bench basic syscall are below: perf bench syscall usec/op (-ve is improvement) | Syscall | Base | test | change % | | ------- | ----------- | ----------- | -------- | | basic | 0.093543 | 0.093023 | -0.56 | | execve | 446.557781 | 450.107172 | +0.79 | | fork | 1142.204391 | 1156.377214 | +1.24 | | getpgid | 0.097666 | 0.092677 | -5.11 | perf bench syscall ops/sec (+ve is improvement) | Syscall | Base | New | change % | | ------- | -------- | -------- | -------- | | basic | 10690548 | 10750140 | +0.56 | | execve | 2239 | 2221 | -0.80 | | fork | 875 | 864 | -1.26 | | getpgid | 10239026 | 10790324 | +5.38 | IPI latency benchmark (-ve is improvement) | Metric | Base (ns) | New (ns) | % Change | | -------------- | ------------- | ------------- | -------- | | Dry run | 583136.56 | 584136.35 | 0.17% | | Self IPI | 4167393.42 | 4149093.90 | -0.44% | | Normal IPI | 61769347.82 | 61753728.39 | -0.03% | | Broadcast IPI | 2235584825.02 | 2227521401.45 | -0.36% | | Broadcast lock | 2164964433.31 | 2125658641.76 | -1.82% | Thats very close to performance earlier with arch specific handling. Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-8-mkchauras@gmail.com --- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/interrupt.h | 384 +++++---------------------- arch/powerpc/include/asm/kasan.h | 15 +- arch/powerpc/kernel/interrupt.c | 250 +++-------------- arch/powerpc/kernel/ptrace/ptrace.c | 3 - arch/powerpc/kernel/signal.c | 8 + arch/powerpc/kernel/syscall.c | 119 +-------- 7 files changed, 124 insertions(+), 656 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index e93df95b79e7..81642206f7de 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -206,6 +206,7 @@ config PPC select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_VULNERABILITIES if PPC_BARRIER_NOSPEC select GENERIC_EARLY_IOREMAP + select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index 0e2cddf8bd21..fb42a664ae54 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -66,11 +66,9 @@ #ifndef __ASSEMBLER__ -#include -#include -#include -#include -#include +#include /* for show_regs */ +#include + #include #include @@ -88,308 +86,6 @@ do { \ #define INT_SOFT_MASK_BUG_ON(regs, cond) #endif -#ifdef CONFIG_PPC_BOOK3S_64 -extern char __end_soft_masked[]; -bool search_kernel_soft_mask_table(unsigned long addr); -unsigned long search_kernel_restart_table(unsigned long addr); - -DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); - -static inline bool is_implicit_soft_masked(struct pt_regs *regs) -{ - if (user_mode(regs)) - return false; - - if (regs->nip >= (unsigned long)__end_soft_masked) - return false; - - return search_kernel_soft_mask_table(regs->nip); -} - -static inline void srr_regs_clobbered(void) -{ - local_paca->srr_valid = 0; - local_paca->hsrr_valid = 0; -} -#else -static inline unsigned long search_kernel_restart_table(unsigned long addr) -{ - return 0; -} - -static inline bool is_implicit_soft_masked(struct pt_regs *regs) -{ - return false; -} - -static inline void srr_regs_clobbered(void) -{ -} -#endif - -static inline void nap_adjust_return(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC_970_NAP - if (unlikely(test_thread_local_flags(_TLF_NAPPING))) { - /* Can avoid a test-and-clear because NMIs do not call this */ - clear_thread_local_flags(_TLF_NAPPING); - regs_set_return_ip(regs, (unsigned long)power4_idle_nap_return); - } -#endif -} - -static inline void booke_restore_dbcr0(void) -{ -#ifdef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long dbcr0 = current->thread.debug.dbcr0; - - if (IS_ENABLED(CONFIG_PPC32) && unlikely(dbcr0 & DBCR0_IDM)) { - mtspr(SPRN_DBSR, -1); - mtspr(SPRN_DBCR0, global_dbcr0[smp_processor_id()]); - } -#endif -} - -static inline void interrupt_enter_prepare(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC64 - irq_soft_mask_set(IRQS_ALL_DISABLED); - - /* - * If the interrupt was taken with HARD_DIS clear, then enable MSR[EE]. - * Asynchronous interrupts get here with HARD_DIS set (see below), so - * this enables MSR[EE] for synchronous interrupts. IRQs remain - * soft-masked. The interrupt handler may later call - * interrupt_cond_local_irq_enable() to achieve a regular process - * context. - */ - if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS)) { - INT_SOFT_MASK_BUG_ON(regs, !(regs->msr & MSR_EE)); - __hard_irq_enable(); - } else { - __hard_RI_enable(); - } - /* Enable MSR[RI] early, to support kernel SLB and hash faults */ -#endif - - if (!regs_irqs_disabled(regs)) - trace_hardirqs_off(); - - if (user_mode(regs)) { - kuap_lock(); - CT_WARN_ON(ct_state() != CT_STATE_USER); - user_exit_irqoff(); - - account_cpu_user_entry(); - account_stolen_time(); - } else { - kuap_save_and_lock(regs); - /* - * CT_WARN_ON comes here via program_check_exception, - * so avoid recursion. - */ - if (TRAP(regs) != INTERRUPT_PROGRAM) - CT_WARN_ON(ct_state() != CT_STATE_KERNEL && - ct_state() != CT_STATE_IDLE); - INT_SOFT_MASK_BUG_ON(regs, is_implicit_soft_masked(regs)); - INT_SOFT_MASK_BUG_ON(regs, regs_irqs_disabled(regs) && - search_kernel_restart_table(regs->nip)); - } - INT_SOFT_MASK_BUG_ON(regs, !regs_irqs_disabled(regs) && - !(regs->msr & MSR_EE)); - - booke_restore_dbcr0(); -} - -/* - * Care should be taken to note that interrupt_exit_prepare and - * interrupt_async_exit_prepare do not necessarily return immediately to - * regs context (e.g., if regs is usermode, we don't necessarily return to - * user mode). Other interrupts might be taken between here and return, - * context switch / preemption may occur in the exit path after this, or a - * signal may be delivered, etc. - * - * The real interrupt exit code is platform specific, e.g., - * interrupt_exit_user_prepare / interrupt_exit_kernel_prepare for 64s. - * - * However interrupt_nmi_exit_prepare does return directly to regs, because - * NMIs do not do "exit work" or replay soft-masked interrupts. - */ -static inline void interrupt_exit_prepare(struct pt_regs *regs) -{ -} - -static inline void interrupt_async_enter_prepare(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC64 - /* Ensure interrupt_enter_prepare does not enable MSR[EE] */ - local_paca->irq_happened |= PACA_IRQ_HARD_DIS; -#endif - interrupt_enter_prepare(regs); -#ifdef CONFIG_PPC_BOOK3S_64 - /* - * RI=1 is set by interrupt_enter_prepare, so this thread flags access - * has to come afterward (it can cause SLB faults). - */ - if (cpu_has_feature(CPU_FTR_CTRL) && - !test_thread_local_flags(_TLF_RUNLATCH)) - __ppc64_runlatch_on(); -#endif - irq_enter(); -} - -static inline void interrupt_async_exit_prepare(struct pt_regs *regs) -{ - /* - * Adjust at exit so the main handler sees the true NIA. This must - * come before irq_exit() because irq_exit can enable interrupts, and - * if another interrupt is taken before nap_adjust_return has run - * here, then that interrupt would return directly to idle nap return. - */ - nap_adjust_return(regs); - - irq_exit(); - interrupt_exit_prepare(regs); -} - -struct interrupt_nmi_state { -#ifdef CONFIG_PPC64 - u8 irq_soft_mask; - u8 irq_happened; - u8 ftrace_enabled; - u64 softe; -#endif -}; - -static inline bool nmi_disables_ftrace(struct pt_regs *regs) -{ - /* Allow DEC and PMI to be traced when they are soft-NMI */ - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { - if (TRAP(regs) == INTERRUPT_DECREMENTER) - return false; - if (TRAP(regs) == INTERRUPT_PERFMON) - return false; - } - if (IS_ENABLED(CONFIG_PPC_BOOK3E_64)) { - if (TRAP(regs) == INTERRUPT_PERFMON) - return false; - } - - return true; -} - -static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state) -{ -#ifdef CONFIG_PPC64 - state->irq_soft_mask = local_paca->irq_soft_mask; - state->irq_happened = local_paca->irq_happened; - state->softe = regs->softe; - - /* - * Set IRQS_ALL_DISABLED unconditionally so irqs_disabled() does - * the right thing, and set IRQ_HARD_DIS. We do not want to reconcile - * because that goes through irq tracing which we don't want in NMI. - */ - local_paca->irq_soft_mask = IRQS_ALL_DISABLED; - local_paca->irq_happened |= PACA_IRQ_HARD_DIS; - - if (!(regs->msr & MSR_EE) || is_implicit_soft_masked(regs)) { - /* - * Adjust regs->softe to be soft-masked if it had not been - * reconcied (e.g., interrupt entry with MSR[EE]=0 but softe - * not yet set disabled), or if it was in an implicit soft - * masked state. This makes regs_irqs_disabled(regs) - * behave as expected. - */ - regs->softe = IRQS_ALL_DISABLED; - } - - __hard_RI_enable(); - - /* Don't do any per-CPU operations until interrupt state is fixed */ - - if (nmi_disables_ftrace(regs)) { - state->ftrace_enabled = this_cpu_get_ftrace_enabled(); - this_cpu_set_ftrace_enabled(0); - } -#endif - - /* If data relocations are enabled, it's safe to use nmi_enter() */ - if (mfmsr() & MSR_DR) { - nmi_enter(); - return; - } - - /* - * But do not use nmi_enter() for pseries hash guest taking a real-mode - * NMI because not everything it touches is within the RMA limit. - */ - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && - firmware_has_feature(FW_FEATURE_LPAR) && - !radix_enabled()) - return; - - /* - * Likewise, don't use it if we have some form of instrumentation (like - * KASAN shadow) that is not safe to access in real mode (even on radix) - */ - if (IS_ENABLED(CONFIG_KASAN)) - return; - - /* - * Likewise, do not use it in real mode if percpu first chunk is not - * embedded. With CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK enabled there - * are chances where percpu allocation can come from vmalloc area. - */ - if (percpu_first_chunk_is_paged) - return; - - /* Otherwise, it should be safe to call it */ - nmi_enter(); -} - -static inline void interrupt_nmi_exit_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state) -{ - if (mfmsr() & MSR_DR) { - // nmi_exit if relocations are on - nmi_exit(); - } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && - firmware_has_feature(FW_FEATURE_LPAR) && - !radix_enabled()) { - // no nmi_exit for a pseries hash guest taking a real mode exception - } else if (IS_ENABLED(CONFIG_KASAN)) { - // no nmi_exit for KASAN in real mode - } else if (percpu_first_chunk_is_paged) { - // no nmi_exit if percpu first chunk is not embedded - } else { - nmi_exit(); - } - - /* - * nmi does not call nap_adjust_return because nmi should not create - * new work to do (must use irq_work for that). - */ - -#ifdef CONFIG_PPC64 -#ifdef CONFIG_PPC_BOOK3S - if (regs_irqs_disabled(regs)) { - unsigned long rst = search_kernel_restart_table(regs->nip); - if (rst) - regs_set_return_ip(regs, rst); - } -#endif - - if (nmi_disables_ftrace(regs)) - this_cpu_set_ftrace_enabled(state->ftrace_enabled); - - /* Check we didn't change the pending interrupt mask. */ - WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened); - regs->softe = state->softe; - local_paca->irq_happened = state->irq_happened; - local_paca->irq_soft_mask = state->irq_soft_mask; -#endif -} - /* * Don't use noinstr here like x86, but rather add NOKPROBE_SYMBOL to each * function definition. The reason for this is the noinstr section is placed @@ -470,11 +166,14 @@ static __always_inline void ____##func(struct pt_regs *regs); \ \ interrupt_handler void func(struct pt_regs *regs) \ { \ - interrupt_enter_prepare(regs); \ - \ + irqentry_state_t state; \ + arch_interrupt_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ ____##func (regs); \ - \ - interrupt_exit_prepare(regs); \ + instrumentation_end(); \ + arch_interrupt_exit_prepare(regs); \ + irqentry_exit(regs, state); \ } \ NOKPROBE_SYMBOL(func); \ \ @@ -504,12 +203,15 @@ static __always_inline long ____##func(struct pt_regs *regs); \ interrupt_handler long func(struct pt_regs *regs) \ { \ long ret; \ + irqentry_state_t state; \ \ - interrupt_enter_prepare(regs); \ - \ + arch_interrupt_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ ret = ____##func (regs); \ - \ - interrupt_exit_prepare(regs); \ + instrumentation_end(); \ + arch_interrupt_exit_prepare(regs); \ + irqentry_exit(regs, state); \ \ return ret; \ } \ @@ -538,11 +240,16 @@ static __always_inline void ____##func(struct pt_regs *regs); \ \ interrupt_handler void func(struct pt_regs *regs) \ { \ - interrupt_async_enter_prepare(regs); \ - \ + irqentry_state_t state; \ + arch_interrupt_async_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ + irq_enter_rcu(); \ ____##func (regs); \ - \ - interrupt_async_exit_prepare(regs); \ + irq_exit_rcu(); \ + instrumentation_end(); \ + arch_interrupt_async_exit_prepare(regs); \ + irqentry_exit(regs, state); \ } \ NOKPROBE_SYMBOL(func); \ \ @@ -572,14 +279,43 @@ ____##func(struct pt_regs *regs); \ \ interrupt_handler long func(struct pt_regs *regs) \ { \ - struct interrupt_nmi_state state; \ + irqentry_state_t state; \ + struct interrupt_nmi_state nmi_state; \ long ret; \ \ - interrupt_nmi_enter_prepare(regs, &state); \ - \ + arch_interrupt_nmi_enter_prepare(regs, &nmi_state); \ + if (mfmsr() & MSR_DR) { \ + /* nmi_entry if relocations are on */ \ + state = irqentry_nmi_enter(regs); \ + } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ + firmware_has_feature(FW_FEATURE_LPAR) && \ + !radix_enabled()) { \ + /* no nmi_entry for a pseries hash guest \ + * taking a real mode exception */ \ + } else if (IS_ENABLED(CONFIG_KASAN)) { \ + /* no nmi_entry for KASAN in real mode */ \ + } else if (percpu_first_chunk_is_paged) { \ + /* no nmi_entry if percpu first chunk is not embedded */\ + } else { \ + state = irqentry_nmi_enter(regs); \ + } \ ret = ____##func (regs); \ - \ - interrupt_nmi_exit_prepare(regs, &state); \ + arch_interrupt_nmi_exit_prepare(regs, &nmi_state); \ + if (mfmsr() & MSR_DR) { \ + /* nmi_exit if relocations are on */ \ + irqentry_nmi_exit(regs, state); \ + } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ + firmware_has_feature(FW_FEATURE_LPAR) && \ + !radix_enabled()) { \ + /* no nmi_exit for a pseries hash guest \ + * taking a real mode exception */ \ + } else if (IS_ENABLED(CONFIG_KASAN)) { \ + /* no nmi_exit for KASAN in real mode */ \ + } else if (percpu_first_chunk_is_paged) { \ + /* no nmi_exit if percpu first chunk is not embedded */ \ + } else { \ + irqentry_nmi_exit(regs, state); \ + } \ \ return ret; \ } \ diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h index 045804a86f98..a690e7da53c2 100644 --- a/arch/powerpc/include/asm/kasan.h +++ b/arch/powerpc/include/asm/kasan.h @@ -3,14 +3,19 @@ #define __ASM_KASAN_H #if defined(CONFIG_KASAN) && !defined(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX) -#define _GLOBAL_KASAN(fn) _GLOBAL(__##fn) -#define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(__##fn) -#define EXPORT_SYMBOL_KASAN(fn) EXPORT_SYMBOL(__##fn) -#else +#define _GLOBAL_KASAN(fn) \ + _GLOBAL(fn); \ + _GLOBAL(__##fn) +#define _GLOBAL_TOC_KASAN(fn) \ + _GLOBAL_TOC(fn); \ + _GLOBAL_TOC(__##fn) +#define EXPORT_SYMBOL_KASAN(fn) \ + EXPORT_SYMBOL(__##fn) +#else /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ #define _GLOBAL_KASAN(fn) _GLOBAL(fn) #define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(fn) #define EXPORT_SYMBOL_KASAN(fn) -#endif +#endif /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ #ifndef __ASSEMBLER__ diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index 666eadb589a5..89a999be1352 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -25,10 +26,6 @@ unsigned long global_dbcr0[NR_CPUS]; #endif -#if defined(CONFIG_PREEMPT_DYNAMIC) -DEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); -#endif - #ifdef CONFIG_PPC_BOOK3S_64 DEFINE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); static inline bool exit_must_hard_disable(void) @@ -78,181 +75,6 @@ static notrace __always_inline bool prep_irq_for_enabled_exit(bool restartable) return true; } -static notrace void booke_load_dbcr0(void) -{ -#ifdef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long dbcr0 = current->thread.debug.dbcr0; - - if (likely(!(dbcr0 & DBCR0_IDM))) - return; - - /* - * Check to see if the dbcr0 register is set up to debug. - * Use the internal debug mode bit to do this. - */ - mtmsr(mfmsr() & ~MSR_DE); - if (IS_ENABLED(CONFIG_PPC32)) { - isync(); - global_dbcr0[smp_processor_id()] = mfspr(SPRN_DBCR0); - } - mtspr(SPRN_DBCR0, dbcr0); - mtspr(SPRN_DBSR, -1); -#endif -} - -static notrace void check_return_regs_valid(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC_BOOK3S_64 - unsigned long trap, srr0, srr1; - static bool warned; - u8 *validp; - char *h; - - if (trap_is_scv(regs)) - return; - - trap = TRAP(regs); - // EE in HV mode sets HSRRs like 0xea0 - if (cpu_has_feature(CPU_FTR_HVMODE) && trap == INTERRUPT_EXTERNAL) - trap = 0xea0; - - switch (trap) { - case 0x980: - case INTERRUPT_H_DATA_STORAGE: - case 0xe20: - case 0xe40: - case INTERRUPT_HMI: - case 0xe80: - case 0xea0: - case INTERRUPT_H_FAC_UNAVAIL: - case 0x1200: - case 0x1500: - case 0x1600: - case 0x1800: - validp = &local_paca->hsrr_valid; - if (!READ_ONCE(*validp)) - return; - - srr0 = mfspr(SPRN_HSRR0); - srr1 = mfspr(SPRN_HSRR1); - h = "H"; - - break; - default: - validp = &local_paca->srr_valid; - if (!READ_ONCE(*validp)) - return; - - srr0 = mfspr(SPRN_SRR0); - srr1 = mfspr(SPRN_SRR1); - h = ""; - break; - } - - if (srr0 == regs->nip && srr1 == regs->msr) - return; - - /* - * A NMI / soft-NMI interrupt may have come in after we found - * srr_valid and before the SRRs are loaded. The interrupt then - * comes in and clobbers SRRs and clears srr_valid. Then we load - * the SRRs here and test them above and find they don't match. - * - * Test validity again after that, to catch such false positives. - * - * This test in general will have some window for false negatives - * and may not catch and fix all such cases if an NMI comes in - * later and clobbers SRRs without clearing srr_valid, but hopefully - * such things will get caught most of the time, statistically - * enough to be able to get a warning out. - */ - if (!READ_ONCE(*validp)) - return; - - if (!data_race(warned)) { - data_race(warned = true); - printk("%sSRR0 was: %lx should be: %lx\n", h, srr0, regs->nip); - printk("%sSRR1 was: %lx should be: %lx\n", h, srr1, regs->msr); - show_regs(regs); - } - - WRITE_ONCE(*validp, 0); /* fixup */ -#endif -} - -static notrace unsigned long -interrupt_exit_user_prepare_main(unsigned long ret, struct pt_regs *regs) -{ - unsigned long ti_flags; - -again: - ti_flags = read_thread_flags(); - while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) { - local_irq_enable(); - if (ti_flags & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) { - schedule(); - } else { - /* - * SIGPENDING must restore signal handler function - * argument GPRs, and some non-volatiles (e.g., r1). - * Restore all for now. This could be made lighter. - */ - if (ti_flags & _TIF_SIGPENDING) - ret |= _TIF_RESTOREALL; - do_notify_resume(regs, ti_flags); - } - local_irq_disable(); - ti_flags = read_thread_flags(); - } - - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && IS_ENABLED(CONFIG_PPC_FPU)) { - if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && - unlikely((ti_flags & _TIF_RESTORE_TM))) { - restore_tm_state(regs); - } else { - unsigned long mathflags = MSR_FP; - - if (cpu_has_feature(CPU_FTR_VSX)) - mathflags |= MSR_VEC | MSR_VSX; - else if (cpu_has_feature(CPU_FTR_ALTIVEC)) - mathflags |= MSR_VEC; - - /* - * If userspace MSR has all available FP bits set, - * then they are live and no need to restore. If not, - * it means the regs were given up and restore_math - * may decide to restore them (to avoid taking an FP - * fault). - */ - if ((regs->msr & mathflags) != mathflags) - restore_math(regs); - } - } - - check_return_regs_valid(regs); - - user_enter_irqoff(); - if (!prep_irq_for_enabled_exit(true)) { - user_exit_irqoff(); - local_irq_enable(); - local_irq_disable(); - goto again; - } - -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM - local_paca->tm_scratch = regs->msr; -#endif - - booke_load_dbcr0(); - - account_cpu_user_exit(); - - /* Restore user access locks last */ - kuap_user_restore(regs); - - return ret; -} - /* * This should be called after a syscall returns, with r3 the return value * from the syscall. If this function returns non-zero, the system call @@ -267,17 +89,12 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, long scv) { unsigned long ti_flags; - unsigned long ret = 0; bool is_not_scv = !IS_ENABLED(CONFIG_PPC_BOOK3S_64) || !scv; - CT_WARN_ON(ct_state() == CT_STATE_USER); - kuap_assert_locked(); regs->result = r3; - - /* Check whether the syscall is issued inside a restartable sequence */ - rseq_syscall(regs); + regs->exit_flags = 0; ti_flags = read_thread_flags(); @@ -290,7 +107,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, if (unlikely(ti_flags & _TIF_PERSYSCALL_MASK)) { if (ti_flags & _TIF_RESTOREALL) - ret = _TIF_RESTOREALL; + regs->exit_flags = _TIF_RESTOREALL; else regs->gpr[3] = r3; clear_bits(_TIF_PERSYSCALL_MASK, ¤t_thread_info()->flags); @@ -299,18 +116,28 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, } if (unlikely(ti_flags & _TIF_SYSCALL_DOTRACE)) { - do_syscall_trace_leave(regs); - ret |= _TIF_RESTOREALL; + regs->exit_flags |= _TIF_RESTOREALL; } - local_irq_disable(); - ret = interrupt_exit_user_prepare_main(ret, regs); + syscall_exit_to_user_mode(regs); + +again: + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } + + /* Restore user access locks last */ + kuap_user_restore(regs); #ifdef CONFIG_PPC64 - regs->exit_result = ret; + regs->exit_result = regs->exit_flags; #endif - return ret; + return regs->exit_flags; } #ifdef CONFIG_PPC64 @@ -330,13 +157,16 @@ notrace unsigned long syscall_exit_restart(unsigned long r3, struct pt_regs *reg set_kuap(AMR_KUAP_BLOCKED); #endif - trace_hardirqs_off(); - user_exit_irqoff(); - account_cpu_user_entry(); +again: + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } - BUG_ON(!user_mode(regs)); - - regs->exit_result = interrupt_exit_user_prepare_main(regs->exit_result, regs); + regs->exit_result |= regs->exit_flags; return regs->exit_result; } @@ -348,7 +178,6 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) BUG_ON(regs_is_unrecoverable(regs)); BUG_ON(regs_irqs_disabled(regs)); - CT_WARN_ON(ct_state() == CT_STATE_USER); /* * We don't need to restore AMR on the way back to userspace for KUAP. @@ -357,8 +186,21 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) kuap_assert_locked(); local_irq_disable(); + regs->exit_flags = 0; +again: + check_return_regs_valid(regs); + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } - ret = interrupt_exit_user_prepare_main(0, regs); + /* Restore user access locks last */ + kuap_user_restore(regs); + + ret = regs->exit_flags; #ifdef CONFIG_PPC64 regs->exit_result = ret; @@ -400,13 +242,6 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) /* Returning to a kernel context with local irqs enabled. */ WARN_ON_ONCE(!(regs->msr & MSR_EE)); again: - if (need_irq_preemption()) { - /* Return to preemptible kernel context */ - if (unlikely(read_thread_flags() & _TIF_NEED_RESCHED)) { - if (preempt_count() == 0) - preempt_schedule_irq(); - } - } check_return_regs_valid(regs); @@ -479,7 +314,6 @@ notrace unsigned long interrupt_exit_user_restart(struct pt_regs *regs) #endif trace_hardirqs_off(); - user_exit_irqoff(); account_cpu_user_entry(); BUG_ON(!user_mode(regs)); diff --git a/arch/powerpc/kernel/ptrace/ptrace.c b/arch/powerpc/kernel/ptrace/ptrace.c index 2134b6d155ff..f006a03a0211 100644 --- a/arch/powerpc/kernel/ptrace/ptrace.c +++ b/arch/powerpc/kernel/ptrace/ptrace.c @@ -21,9 +21,6 @@ #include #include -#define CREATE_TRACE_POINTS -#include - #include "ptrace-decl.h" /* diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index aa17e62f3754..9f1847b4742e 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -6,6 +6,7 @@ * Extracted from signal_32.c and signal_64.c */ +#include #include #include #include @@ -368,3 +369,10 @@ void signal_fault(struct task_struct *tsk, struct pt_regs *regs, printk_ratelimited(regs->msr & MSR_64BIT ? fm64 : fm32, tsk->comm, task_pid_nr(tsk), where, ptr, regs->nip, regs->link); } + +void arch_do_signal_or_restart(struct pt_regs *regs) +{ + BUG_ON(regs != current->thread.regs); + regs->exit_flags |= _TIF_RESTOREALL; + do_signal(current); +} diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index 52d6e10eab22..a9da2af6efa8 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -18,124 +19,10 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) long ret; syscall_fn f; - kuap_lock(); - - if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) - BUG_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED); - - trace_hardirqs_off(); /* finish reconciling */ - - CT_WARN_ON(ct_state() == CT_STATE_KERNEL); - user_exit_irqoff(); - add_random_kstack_offset(); + r0 = syscall_enter_from_user_mode(regs, r0); - BUG_ON(regs_is_unrecoverable(regs)); - BUG_ON(!user_mode(regs)); - BUG_ON(regs_irqs_disabled(regs)); - -#ifdef CONFIG_PPC_PKEY - if (mmu_has_feature(MMU_FTR_PKEY)) { - unsigned long amr, iamr; - bool flush_needed = false; - /* - * When entering from userspace we mostly have the AMR/IAMR - * different from kernel default values. Hence don't compare. - */ - amr = mfspr(SPRN_AMR); - iamr = mfspr(SPRN_IAMR); - regs->amr = amr; - regs->iamr = iamr; - if (mmu_has_feature(MMU_FTR_KUAP)) { - mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); - flush_needed = true; - } - if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP)) { - mtspr(SPRN_IAMR, AMR_KUEP_BLOCKED); - flush_needed = true; - } - if (flush_needed) - isync(); - } else -#endif - kuap_assert_locked(); - - booke_restore_dbcr0(); - - account_cpu_user_entry(); - - account_stolen_time(); - - /* - * This is not required for the syscall exit path, but makes the - * stack frame look nicer. If this was initialised in the first stack - * frame, or if the unwinder was taught the first stack frame always - * returns to user with IRQS_ENABLED, this store could be avoided! - */ - irq_soft_mask_regs_set_state(regs, IRQS_ENABLED); - - /* - * If system call is called with TM active, set _TIF_RESTOREALL to - * prevent RFSCV being used to return to userspace, because POWER9 - * TM implementation has problems with this instruction returning to - * transactional state. Final register values are not relevant because - * the transaction will be aborted upon return anyway. Or in the case - * of unsupported_scv SIGILL fault, the return state does not much - * matter because it's an edge case. - */ - if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && - unlikely(MSR_TM_TRANSACTIONAL(regs->msr))) - set_bits(_TIF_RESTOREALL, ¤t_thread_info()->flags); - - /* - * If the system call was made with a transaction active, doom it and - * return without performing the system call. Unless it was an - * unsupported scv vector, in which case it's treated like an illegal - * instruction. - */ -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM - if (unlikely(MSR_TM_TRANSACTIONAL(regs->msr)) && - !trap_is_unsupported_scv(regs)) { - /* Enable TM in the kernel, and disable EE (for scv) */ - hard_irq_disable(); - mtmsr(mfmsr() | MSR_TM); - - /* tabort, this dooms the transaction, nothing else */ - asm volatile(".long 0x7c00071d | ((%0) << 16)" - :: "r"(TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)); - - /* - * Userspace will never see the return value. Execution will - * resume after the tbegin. of the aborted transaction with the - * checkpointed register state. A context switch could occur - * or signal delivered to the process before resuming the - * doomed transaction context, but that should all be handled - * as expected. - */ - return -ENOSYS; - } -#endif // CONFIG_PPC_TRANSACTIONAL_MEM - - local_irq_enable(); - - if (unlikely(read_thread_flags() & _TIF_SYSCALL_DOTRACE)) { - if (unlikely(trap_is_unsupported_scv(regs))) { - /* Unsupported scv vector */ - _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); - return regs->gpr[3]; - } - /* - * We use the return value of do_syscall_trace_enter() as the - * syscall number. If the syscall was rejected for any reason - * do_syscall_trace_enter() returns an invalid syscall number - * and the test against NR_syscalls will fail and the return - * value to be used is in regs->gpr[3]. - */ - r0 = do_syscall_trace_enter(regs); - if (unlikely(r0 >= NR_syscalls)) - return regs->gpr[3]; - - } else if (unlikely(r0 >= NR_syscalls)) { + if (unlikely(r0 >= NR_syscalls)) { if (unlikely(trap_is_unsupported_scv(regs))) { /* Unsupported scv vector */ _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); From 6ed60999d33d49251b42976a5511f1bb089797ed Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Chaurasiya Date: Mon, 27 Apr 2026 17:57:42 +0530 Subject: [PATCH 1168/5214] powerpc: Remove unused functions After enabling GENERIC_ENTRY some functions are left unused. Cleanup all those functions which includes: - do_syscall_trace_enter - do_syscall_trace_leave - do_notify_resume - do_seccomp Signed-off-by: Mukesh Kumar Chaurasiya Tested-by: Samir M Tested-by: David Gow Tested-by: Venkat Rao Bagalkote Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427122742.210074-9-mkchauras@gmail.com --- arch/powerpc/include/asm/ptrace.h | 3 - arch/powerpc/include/asm/signal.h | 1 - arch/powerpc/kernel/ptrace/ptrace.c | 138 ---------------------------- arch/powerpc/kernel/signal.c | 17 ---- 4 files changed, 159 deletions(-) diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 2e741ea57b80..fdeb97421785 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -177,9 +177,6 @@ extern unsigned long profile_pc(struct pt_regs *regs); #define profile_pc(regs) instruction_pointer(regs) #endif -long do_syscall_trace_enter(struct pt_regs *regs); -void do_syscall_trace_leave(struct pt_regs *regs); - static inline void set_return_regs_changed(void) { #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/arch/powerpc/include/asm/signal.h b/arch/powerpc/include/asm/signal.h index 922d43700fb4..21af92cdb237 100644 --- a/arch/powerpc/include/asm/signal.h +++ b/arch/powerpc/include/asm/signal.h @@ -7,7 +7,6 @@ #include struct pt_regs; -void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); unsigned long get_min_sigframe_size_32(void); unsigned long get_min_sigframe_size_64(void); diff --git a/arch/powerpc/kernel/ptrace/ptrace.c b/arch/powerpc/kernel/ptrace/ptrace.c index f006a03a0211..316d4f5ead8e 100644 --- a/arch/powerpc/kernel/ptrace/ptrace.c +++ b/arch/powerpc/kernel/ptrace/ptrace.c @@ -192,144 +192,6 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -#ifdef CONFIG_SECCOMP -static int do_seccomp(struct pt_regs *regs) -{ - if (!test_thread_flag(TIF_SECCOMP)) - return 0; - - /* - * The ABI we present to seccomp tracers is that r3 contains - * the syscall return value and orig_gpr3 contains the first - * syscall parameter. This is different to the ptrace ABI where - * both r3 and orig_gpr3 contain the first syscall parameter. - */ - regs->gpr[3] = -ENOSYS; - - /* - * We use the __ version here because we have already checked - * TIF_SECCOMP. If this fails, there is nothing left to do, we - * have already loaded -ENOSYS into r3, or seccomp has put - * something else in r3 (via SECCOMP_RET_ERRNO/TRACE). - */ - if (__secure_computing()) - return -1; - - /* - * The syscall was allowed by seccomp, restore the register - * state to what audit expects. - * Note that we use orig_gpr3, which means a seccomp tracer can - * modify the first syscall parameter (in orig_gpr3) and also - * allow the syscall to proceed. - */ - regs->gpr[3] = regs->orig_gpr3; - - return 0; -} -#else -static inline int do_seccomp(struct pt_regs *regs) { return 0; } -#endif /* CONFIG_SECCOMP */ - -/** - * do_syscall_trace_enter() - Do syscall tracing on kernel entry. - * @regs: the pt_regs of the task to trace (current) - * - * Performs various types of tracing on syscall entry. This includes seccomp, - * ptrace, syscall tracepoints and audit. - * - * The pt_regs are potentially visible to userspace via ptrace, so their - * contents is ABI. - * - * One or more of the tracers may modify the contents of pt_regs, in particular - * to modify arguments or even the syscall number itself. - * - * It's also possible that a tracer can choose to reject the system call. In - * that case this function will return an illegal syscall number, and will put - * an appropriate return value in regs->r3. - * - * Return: the (possibly changed) syscall number. - */ -long do_syscall_trace_enter(struct pt_regs *regs) -{ - u32 flags; - - flags = read_thread_flags() & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE); - - if (flags) { - int rc = ptrace_report_syscall_entry(regs); - - if (unlikely(flags & _TIF_SYSCALL_EMU)) { - /* - * A nonzero return code from - * ptrace_report_syscall_entry() tells us to prevent - * the syscall execution, but we are not going to - * execute it anyway. - * - * Returning -1 will skip the syscall execution. We want - * to avoid clobbering any registers, so we don't goto - * the skip label below. - */ - return -1; - } - - if (rc) { - /* - * The tracer decided to abort the syscall. Note that - * the tracer may also just change regs->gpr[0] to an - * invalid syscall number, that is handled below on the - * exit path. - */ - goto skip; - } - } - - /* Run seccomp after ptrace; allow it to set gpr[3]. */ - if (do_seccomp(regs)) - return -1; - - /* Avoid trace and audit when syscall is invalid. */ - if (regs->gpr[0] >= NR_syscalls) - goto skip; - - if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) - trace_sys_enter(regs, regs->gpr[0]); - - if (!is_32bit_task()) - audit_syscall_entry(regs->gpr[0], regs->gpr[3], regs->gpr[4], - regs->gpr[5], regs->gpr[6]); - else - audit_syscall_entry(regs->gpr[0], - regs->gpr[3] & 0xffffffff, - regs->gpr[4] & 0xffffffff, - regs->gpr[5] & 0xffffffff, - regs->gpr[6] & 0xffffffff); - - /* Return the possibly modified but valid syscall number */ - return regs->gpr[0]; - -skip: - /* - * If we are aborting explicitly, or if the syscall number is - * now invalid, set the return value to -ENOSYS. - */ - regs->gpr[3] = -ENOSYS; - return -1; -} - -void do_syscall_trace_leave(struct pt_regs *regs) -{ - int step; - - audit_syscall_exit(regs); - - if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) - trace_sys_exit(regs, regs->result); - - step = test_thread_flag(TIF_SINGLESTEP); - if (step || test_thread_flag(TIF_SYSCALL_TRACE)) - ptrace_report_syscall_exit(regs, step); -} - void __init pt_regs_check(void); /* diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 9f1847b4742e..bb42a8b6c642 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -293,23 +293,6 @@ static void do_signal(struct task_struct *tsk) signal_setup_done(ret, &ksig, test_thread_flag(TIF_SINGLESTEP)); } -void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) -{ - if (thread_info_flags & _TIF_UPROBE) - uprobe_notify_resume(regs); - - if (thread_info_flags & _TIF_PATCH_PENDING) - klp_update_patch_state(current); - - if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) { - BUG_ON(regs != current->thread.regs); - do_signal(current); - } - - if (thread_info_flags & _TIF_NOTIFY_RESUME) - resume_user_mode_work(regs); -} - static unsigned long get_tm_stackpointer(struct task_struct *tsk) { /* When in an active transaction that takes a signal, we need to be From 906e410dcffbbd99fb4081abab817a830033aa28 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Wed, 13 May 2026 08:42:44 +0300 Subject: [PATCH 1169/5214] media: pwc: Drain fill_buf on start_streaming() failure pwc_isoc_init() submits its isochronous URBs with usb_submit_urb(.., GFP_KERNEL) in a loop. After the first URB is submitted, its completion handler pwc_isoc_handler() can run on another CPU before the loop finishes: start_streaming() pwc_isoc_init() usb_submit_urb(urbs[0], GFP_KERNEL) pwc_isoc_handler(urbs[0]) pdev->fill_buf = pwc_get_next_fill_buf(pdev) usb_submit_urb(urbs[i>0], ..) -> fails pwc_isoc_cleanup(pdev) /* kills URBs */ return ret; pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED) pwc_get_next_fill_buf() detaches a buffer from pdev->queued_bufs and stores it in pdev->fill_buf. The error path in start_streaming() only drains pdev->queued_bufs, so the buffer parked in pdev->fill_buf is leaked. vb2_start_streaming() then triggers WARN_ON(owned_by_drv_count). stop_streaming() already handles this since commit 80b0963e1698 ("[media] pwc: fix WARN_ON"), which added the fill_buf drain in the teardown path but not in the start_streaming() error path. Mirror that handling on failure so start_streaming() returns with no buffer owned by the driver. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 885fe18f5542 ("[media] pwc: Replace private buffer management code with videobuf2") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/pwc/pwc-if.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 59b99ac8fcb6..e2884b04d952 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -730,6 +730,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + pdev->fill_buf = NULL; + } } mutex_unlock(&pdev->v4l2_lock); From 436a693af04ffb889aaf87cb69ec1f2b21d3569c Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 13 May 2026 16:02:37 +0900 Subject: [PATCH 1170/5214] media: radio-si476x: Unregister v4l2_device on probe failure si476x_radio_probe() registers radio->v4l2dev before allocating the V4L2 controls and before registering the video device. If any of those later steps fails, probe returns through the exit label after freeing only the control handler. A failed probe does not call si476x_radio_remove(), so the v4l2_device_unregister() there is not reached. This leaves the parent device reference taken by v4l2_device_register() behind on the error path. Unregister the V4L2 device in the probe error path after freeing the controls. Fixes: b879a9c2a755 ("[media] v4l2: Add a V4L2 driver for SI476X MFD") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil --- drivers/media/radio/radio-si476x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 9980346cb5ea..bfe89782dce4 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev) return 0; exit: v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + v4l2_device_unregister(&radio->v4l2dev); return rval; } From ffdb7c4ecaed0325cbc20d78f601fec235c49487 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 13 May 2026 19:38:06 +0300 Subject: [PATCH 1171/5214] media: staging: atmel-isc: Remove driver atmel-isc has been in staging pending removal since 2022. Hence remove now. Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 - drivers/staging/media/Kconfig | 4 - drivers/staging/media/Makefile | 1 - .../staging/media/deprecated/atmel/Kconfig | 47 - .../staging/media/deprecated/atmel/Makefile | 8 - drivers/staging/media/deprecated/atmel/TODO | 34 - .../media/deprecated/atmel/atmel-isc-base.c | 2008 ----------------- .../media/deprecated/atmel/atmel-isc-clk.c | 311 --- .../media/deprecated/atmel/atmel-isc-regs.h | 413 ---- .../media/deprecated/atmel/atmel-isc.h | 362 --- .../deprecated/atmel/atmel-sama5d2-isc.c | 644 ------ .../deprecated/atmel/atmel-sama7g5-isc.c | 607 ----- 12 files changed, 4441 deletions(-) delete mode 100644 drivers/staging/media/deprecated/atmel/Kconfig delete mode 100644 drivers/staging/media/deprecated/atmel/Makefile delete mode 100644 drivers/staging/media/deprecated/atmel/TODO delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-base.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-clk.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-regs.h delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc.h delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c diff --git a/MAINTAINERS b/MAINTAINERS index 63389fea5d15..3124500ff432 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17395,8 +17395,6 @@ F: Documentation/devicetree/bindings/media/atmel,isc.yaml F: Documentation/devicetree/bindings/media/microchip,xisc.yaml F: drivers/media/platform/microchip/microchip-isc* F: drivers/media/platform/microchip/microchip-sama*-isc* -F: drivers/staging/media/deprecated/atmel/atmel-isc* -F: drivers/staging/media/deprecated/atmel/atmel-sama*-isc* F: include/linux/atmel-isc-media.h MICROCHIP ISI DRIVER diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 1aa31bddf970..06cbc5c548ca 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -50,8 +50,4 @@ menuconfig STAGING_MEDIA_DEPRECATED If in doubt, say N here. -if STAGING_MEDIA_DEPRECATED -source "drivers/staging/media/deprecated/atmel/Kconfig" -endif - endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 6f78b0edde1e..6fd7179733d8 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig deleted file mode 100644 index 418841ea5a0d..000000000000 --- a/drivers/staging/media/deprecated/atmel/Kconfig +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -comment "Atmel media platform drivers" - -config VIDEO_ATMEL_ISC - tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - help - This module makes the ATMEL Image Sensor Controller available - as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_XISC - tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This module makes the ATMEL eXtended Image Sensor Controller - available as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_ISC_BASE - tristate - default n - help - ATMEL ISC and XISC common code base. diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile deleted file mode 100644 index 34eaeeac5bba..000000000000 --- a/drivers/staging/media/deprecated/atmel/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -atmel-isc-objs = atmel-sama5d2-isc.o -atmel-xisc-objs = atmel-sama7g5-isc.o -atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o - -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o -obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o -obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO deleted file mode 100644 index 71691df07a80..000000000000 --- a/drivers/staging/media/deprecated/atmel/TODO +++ /dev/null @@ -1,34 +0,0 @@ -The Atmel ISC driver is not compliant with media controller specification. -In order to evolve this driver, it has to move to media controller, to -support enhanced features and future products which embed it. -The move to media controller involves several changes which are -not backwards compatible with the current usability of the driver. - -The best example is the way the format is propagated from the top video -driver /dev/videoX down to the sensor. - -In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt -and controls the sensor directly. This is achieved by having a lot of code -inside the driver that will query the subdev at probe time and make a list -of formats which are usable. -Basically the user has nothing to configure, as the isc will handle -everything at the top level. This is an easy way to capture, but also comes -with the drawback of lack of flexibility. -In a more complicated pipeline -sensor ==> controller 1 ==> controller 2 ==> isc -this will not be achievable, as controller 1 and controller 2 might be -media-controller configurable, and will not propagate the formats down to -the sensor. - -After discussions with the media maintainers, the decision is to move -Atmel ISC to staging as-is, to keep the Kconfig symbols and the users -to the driver in staging. Thus, all the existing users of the non -media-controller paradigm will continue to be happy and use the old config -way. - -The new driver was added in the media subsystem with a different -symbol, with the conversion to media controller done, and new users -of the driver will be able to use all the new features. - -The replacement driver is named VIDEO_MICROCHIP_ISC or -VIDEO_MICROCHIP_XISC depending on the product flavor. diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c deleted file mode 100644 index fb9ee8547392..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ /dev/null @@ -1,2008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common driver base - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); - -static unsigned int sensor_preferred = 1; -module_param(sensor_preferred, uint, 0644); -MODULE_PARM_DESC(sensor_preferred, - "Sensor is preferred to output the specified format (1-on 0-off), default 1"); - -#define ISC_IS_FORMAT_RAW(mbus_code) \ - (((mbus_code) & 0xf000) == 0x3000) - -#define ISC_IS_FORMAT_GREY(mbus_code) \ - (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ - (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) - -static inline void isc_update_v4l2_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set the v4l2 controls w.r.t. our pipeline config */ - v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); -} - -static inline void isc_update_awb_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set our actual hw pipeline config */ - - regmap_write(isc->regmap, ISC_WB_O_RGR, - ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); - regmap_write(isc->regmap, ISC_WB_O_BGB, - ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); - regmap_write(isc->regmap, ISC_WB_G_RGR, - ctrls->gain[ISC_HIS_CFG_MODE_R] | - (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); - regmap_write(isc->regmap, ISC_WB_G_BGB, - ctrls->gain[ISC_HIS_CFG_MODE_B] | - (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); -} - -static inline void isc_reset_awb_ctrls(struct isc_device *isc) -{ - unsigned int c; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* gains have a fixed point at 9 decimals */ - isc->ctrls.gain[c] = 1 << 9; - /* offsets are in 2's complements */ - isc->ctrls.offset[c] = 0; - } -} - - -static int isc_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned int size = isc->fmt.fmt.pix.sizeimage; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int isc_buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = isc->fmt.fmt.pix.sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, size); - - vbuf->field = isc->fmt.fmt.pix.field; - - return 0; -} - -static void isc_crop_pfe(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 h, w; - - h = isc->fmt.fmt.pix.height; - w = isc->fmt.fmt.pix.width; - - /* - * In case the sensor is not RAW, it will output a pixel (12-16 bits) - * with two samples on the ISC Data bus (which is 8-12) - * ISC will count each sample, so, we need to multiply these values - * by two, to get the real number of samples for the required pixels. - */ - if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) { - h <<= 1; - w <<= 1; - } - - /* - * We limit the column/row count that the ISC will output according - * to the configured resolution that we want. - * This will avoid the situation where the sensor is misconfigured, - * sending more data, and the ISC will just take it and DMA to memory, - * causing corruption. - */ - regmap_write(regmap, ISC_PFE_CFG1, - (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) | - (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK)); - - regmap_write(regmap, ISC_PFE_CFG2, - (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) | - (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK)); - - regmap_update_bits(regmap, ISC_PFE_CFG0, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); -} - -static void isc_start_dma(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sizeimage = isc->fmt.fmt.pix.sizeimage; - u32 dctrl_dview; - dma_addr_t addr0; - - addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); - regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); - - switch (isc->config.fourcc) { - case V4L2_PIX_FMT_YUV420: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + (sizeimage * 2) / 3); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 5) / 6); - break; - case V4L2_PIX_FMT_YUV422P: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + sizeimage / 2); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 3) / 4); - break; - default: - break; - } - - dctrl_dview = isc->config.dctrl_dview; - - regmap_write(regmap, ISC_DCTRL + isc->offsets.dma, - dctrl_dview | ISC_DCTRL_IE_IS); - spin_lock(&isc->awb_lock); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); - spin_unlock(&isc->awb_lock); -} - -static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 val, bay_cfg; - const u32 *gamma; - unsigned int i; - - /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - val = pipeline & BIT(i) ? 1 : 0; - regmap_field_write(isc->pipeline[i], val); - } - - if (!pipeline) - return; - - bay_cfg = isc->config.sd_format->cfa_baycfg; - - regmap_write(regmap, ISC_WB_CFG, bay_cfg); - isc_update_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - - regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); - - gamma = &isc->gamma_table[ctrls->gamma_index][0]; - regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES); - - isc->config_dpc(isc); - isc->config_csc(isc); - isc->config_cbc(isc); - isc->config_cc(isc); - isc->config_gam(isc); -} - -static int isc_update_profile(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sr; - int counter = 100; - - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); - - regmap_read(regmap, ISC_CTRLSR, &sr); - while ((sr & ISC_CTRL_UPPRO) && counter--) { - usleep_range(1000, 2000); - regmap_read(regmap, ISC_CTRLSR, &sr); - } - - if (counter < 0) { - v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static void isc_set_histogram(struct isc_device *isc, bool enable) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - - if (enable) { - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - ISC_HIS_CFG_MODE_GR | - (isc->config.sd_format->cfa_baycfg - << ISC_HIS_CFG_BAYSEL_SHIFT) | - ISC_HIS_CFG_RAR); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_EN); - regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); - ctrls->hist_id = ISC_HIS_CFG_MODE_GR; - isc_update_profile(isc); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - ctrls->hist_stat = HIST_ENABLED; - } else { - regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_DIS); - - ctrls->hist_stat = HIST_DISABLED; - } -} - -static int isc_configure(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 pfe_cfg0, dcfg, mask, pipeline; - struct isc_subdev_entity *subdev = isc->current_subdev; - - pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps; - pipeline = isc->config.bits_pipeline; - - dcfg = isc->config.dcfg_imode | isc->dcfg; - - pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; - mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | - ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | - ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI; - - regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0); - - isc->config_rlp(isc); - - regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg); - - /* Set the pipeline */ - isc_set_pipeline(isc, pipeline); - - /* - * The current implemented histogram is available for RAW R, B, GB, GR - * channels. We need to check if sensor is outputting RAW BAYER - */ - if (isc->ctrls.awb && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - else - isc_set_histogram(isc, false); - - /* Update profile */ - return isc_update_profile(isc); -} - -static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - struct regmap *regmap = isc->regmap; - struct isc_buffer *buf; - unsigned long flags; - int ret; - - /* Enable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) { - v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n", - ret); - goto err_start_stream; - } - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n", - ret); - goto err_pm_get; - } - - ret = isc_configure(isc); - if (unlikely(ret)) - goto err_configure; - - /* Enable DMA interrupt */ - regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - - isc->sequence = 0; - isc->stop = false; - reinit_completion(&isc->comp); - - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_crop_pfe(isc); - isc_start_dma(isc); - - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - /* if we streaming from RAW, we can do one-shot white balance adj */ - if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - v4l2_ctrl_activate(isc->do_wb_ctrl, true); - - return 0; - -err_configure: - pm_runtime_put_sync(isc->dev); -err_pm_get: - v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - -err_start_stream: - spin_lock_irqsave(&isc->dma_queue_lock, flags); - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - return ret; -} - -static void isc_stop_streaming(struct vb2_queue *vq) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned long flags; - struct isc_buffer *buf; - int ret; - - mutex_lock(&isc->awb_mutex); - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->stop = true; - - /* Wait until the end of the current frame */ - if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) - v4l2_err(&isc->v4l2_dev, - "Timeout waiting for end of the capture\n"); - - mutex_unlock(&isc->awb_mutex); - - /* Disable DMA interrupt */ - regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); - - pm_runtime_put_sync(isc->dev); - - /* Disable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - if (ret && ret != -ENOIOCTLCMD) - v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); - - /* Release all active buffers */ - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (unlikely(isc->cur_frm)) { - vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - isc->cur_frm = NULL; - } - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static void isc_buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (!isc->cur_frm && list_empty(&isc->dma_queue) && - vb2_start_streaming_called(vb->vb2_queue)) { - isc->cur_frm = buf; - isc_start_dma(isc); - } else - list_add_tail(&buf->list, &isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static struct isc_format *find_format_by_fourcc(struct isc_device *isc, - unsigned int fourcc) -{ - unsigned int num_formats = isc->num_user_formats; - struct isc_format *fmt; - unsigned int i; - - for (i = 0; i < num_formats; i++) { - fmt = isc->user_formats[i]; - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -static const struct vb2_ops isc_vb2_ops = { - .queue_setup = isc_queue_setup, - .buf_prepare = isc_buffer_prepare, - .start_streaming = isc_start_streaming, - .stop_streaming = isc_stop_streaming, - .buf_queue = isc_buffer_queue, -}; - -static int isc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "microchip-isc", sizeof(cap->driver)); - strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card)); - - return 0; -} - -static int isc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct isc_device *isc = video_drvdata(file); - u32 index = f->index; - u32 i, supported_index; - - if (index < isc->controller_formats_size) { - f->pixelformat = isc->controller_formats[index].fourcc; - return 0; - } - - index -= isc->controller_formats_size; - - supported_index = 0; - - for (i = 0; i < isc->formats_list_size; i++) { - if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) || - !isc->formats_list[i].sd_support) - continue; - if (supported_index == index) { - f->pixelformat = isc->formats_list[i].fourcc; - return 0; - } - supported_index++; - } - - return -EINVAL; -} - -static int isc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct isc_device *isc = video_drvdata(file); - - *fmt = isc->fmt; - - return 0; -} - -/* - * Checks the current configured format, if ISC can output it, - * considering which type of format the ISC receives from the sensor - */ -static int isc_try_validate_formats(struct isc_device *isc) -{ - int ret; - bool bayer = false, yuv = false, rgb = false, grey = false; - - /* all formats supported by the RLP module are OK */ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - ret = 0; - bayer = true; - break; - - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - ret = 0; - yuv = true; - break; - - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ARGB555: - ret = 0; - rgb = true; - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y10: - case V4L2_PIX_FMT_Y16: - ret = 0; - grey = true; - break; - default: - /* any other different formats are not supported */ - ret = -EINVAL; - } - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n", - rgb, yuv, grey, bayer); - - /* we cannot output RAW if we do not receive RAW */ - if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - /* we cannot output GREY if we do not receive RAW/GREY */ - if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) && - !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - return ret; -} - -/* - * Configures the RLP and DMA modules, depending on the output format - * configured for the ISC. - * If direct_dump == true, just dump raw data 8/16 bits depending on format. - */ -static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) -{ - isc->try_config.rlp_cfg_mode = 0; - - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_RGB565: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB444: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB555: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 32; - isc->try_config.bpp_v4l2 = 32; - break; - case V4L2_PIX_FMT_YUV420: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 12; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUV422P: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUYV: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_UYVY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_VYUY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_GREY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_Y16: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; - fallthrough; - case V4L2_PIX_FMT_Y10: - isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - default: - return -EINVAL; - } - - if (direct_dump) { - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - return 0; - } - - return 0; -} - -/* - * Configuring pipeline modules, depending on which format the ISC outputs - * and considering which format it has as input from the sensor. - */ -static int isc_try_configure_pipeline(struct isc_device *isc) -{ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ARGB555: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE | - CC_ENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV420: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | GAM_ENABLES | WB_ENABLE | - SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE | - DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV422P: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y16: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - default: - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE; - else - isc->try_config.bits_pipeline = 0x0; - } - - /* Tune the pipeline to product specific */ - isc->adapt_pipeline(isc); - - return 0; -} - -static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_rect *try_crop = - v4l2_subdev_state_get_crop(sd_state, 0); - struct v4l2_subdev_frame_size_enum fse = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret; - - /* - * If we do not know yet which format the subdev is using, we cannot - * do anything. - */ - if (!isc->try_config.sd_format) - return; - - fse.code = isc->try_config.sd_format->mbus_code; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - sd_state, &fse); - /* - * Attempt to obtain format size from subdev. If not available, - * just use the maximum ISC can receive. - */ - if (ret) { - try_crop->width = isc->max_width; - try_crop->height = isc->max_height; - } else { - try_crop->width = fse.max_width; - try_crop->height = fse.max_height; - } -} - -static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, - u32 *code) -{ - int i; - struct isc_format *sd_fmt = NULL, *direct_fmt = NULL; - struct v4l2_pix_format *pixfmt = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg = {}; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, - }; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - u32 mbus_code; - int ret; - bool rlp_dma_direct_dump = false; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* Step 1: find a RAW format that is supported */ - for (i = 0; i < isc->num_user_formats; i++) { - if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) { - sd_fmt = isc->user_formats[i]; - break; - } - } - /* Step 2: We can continue with this RAW format, or we can look - * for better: maybe sensor supports directly what we need. - */ - direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); - - /* Step 3: We have both. We decide given the module parameter which - * one to use. - */ - if (direct_fmt && sd_fmt && sensor_preferred) - sd_fmt = direct_fmt; - - /* Step 4: we do not have RAW but we have a direct format. Use it. */ - if (direct_fmt && !sd_fmt) - sd_fmt = direct_fmt; - - /* Step 5: if we are using a direct format, we need to package - * everything as 8 bit data and just dump it - */ - if (sd_fmt == direct_fmt) - rlp_dma_direct_dump = true; - - /* Step 6: We have no format. This can happen if the userspace - * requests some weird/invalid format. - * In this case, default to whatever we have - */ - if (!sd_fmt && !direct_fmt) { - sd_fmt = isc->user_formats[isc->num_user_formats - 1]; - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Sensor not supporting %.4s, using %.4s\n", - (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc); - } - - if (!sd_fmt) { - ret = -EINVAL; - goto isc_try_fmt_err; - } - - /* Step 7: Print out what we decided for debugging */ - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Preferring to have sensor using format %.4s\n", - (char *)&sd_fmt->fourcc); - - /* Step 8: at this moment we decided which format the subdev will use */ - isc->try_config.sd_format = sd_fmt; - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - /* - * The mbus format is the one the subdev outputs. - * The pixels will be transferred in this format Sensor -> ISC - */ - mbus_code = sd_fmt->mbus_code; - - /* - * Validate formats. If the required format is not OK, default to raw. - */ - - isc->try_config.fourcc = pixfmt->pixelformat; - - if (isc_try_validate_formats(isc)) { - pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc; - /* Re-try to validate the new format */ - ret = isc_try_validate_formats(isc); - if (ret) - goto isc_try_fmt_err; - } - - ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump); - if (ret) - goto isc_try_fmt_err; - - ret = isc_try_configure_pipeline(isc); - if (ret) - goto isc_try_fmt_err; - - /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_state); - - v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, - &pad_state, &format); - if (ret < 0) - goto isc_try_fmt_subdev_err; - - v4l2_fill_pix_format(pixfmt, &format.format); - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - pixfmt->field = V4L2_FIELD_NONE; - pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; - pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * - pixfmt->height; - - if (code) - *code = mbus_code; - - return 0; - -isc_try_fmt_err: - v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n"); -isc_try_fmt_subdev_err: - memset(&isc->try_config, 0, sizeof(isc->try_config)); - - return ret; -} - -static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) -{ - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - u32 mbus_code = 0; - int ret; - - ret = isc_try_fmt(isc, f, &mbus_code); - if (ret) - return ret; - - v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, - set_fmt, NULL, &format); - if (ret < 0) - return ret; - - /* Limit to Atmel ISC hardware capabilities */ - if (f->fmt.pix.width > isc->max_width) - f->fmt.pix.width = isc->max_width; - if (f->fmt.pix.height > isc->max_height) - f->fmt.pix.height = isc->max_height; - - isc->fmt = *f; - - if (isc->try_config.sd_format && isc->config.sd_format && - isc->try_config.sd_format != isc->config.sd_format) { - isc->ctrls.hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - } - /* make the try configuration active */ - isc->config = isc->try_config; - - v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n"); - - return 0; -} - -static int isc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - if (vb2_is_busy(&isc->vb2_vidq)) - return -EBUSY; - - return isc_set_fmt(isc, f); -} - -static int isc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - return isc_try_fmt(isc, f, NULL); -} - -static int isc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = 0; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int isc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int isc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct isc_device *isc = video_drvdata(file); - int ret = -EINVAL; - int i; - - if (fsize->index) - return -EINVAL; - - for (i = 0; i < isc->num_user_formats; i++) - if (isc->user_formats[i]->fourcc == fsize->pixel_format) - ret = 0; - - for (i = 0; i < isc->controller_formats_size; i++) - if (isc->controller_formats[i].fourcc == fsize->pixel_format) - ret = 0; - - if (ret) - return ret; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - - fsize->stepwise.min_width = 16; - fsize->stepwise.max_width = isc->max_width; - fsize->stepwise.min_height = 16; - fsize->stepwise.max_height = isc->max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - - return 0; -} - -static const struct v4l2_ioctl_ops isc_ioctl_ops = { - .vidioc_querycap = isc_querycap, - .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, - - .vidioc_enum_input = isc_enum_input, - .vidioc_g_input = isc_g_input, - .vidioc_s_input = isc_s_input, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_parm = isc_g_parm, - .vidioc_s_parm = isc_s_parm, - .vidioc_enum_framesizes = isc_enum_framesizes, - - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int isc_open(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - int ret; - - if (mutex_lock_interruptible(&isc->lock)) - return -ERESTARTSYS; - - ret = v4l2_fh_open(file); - if (ret < 0) - goto unlock; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - v4l2_fh_release(file); - goto unlock; - } - - ret = isc_set_fmt(isc, &isc->fmt); - if (ret) { - v4l2_subdev_call(sd, core, s_power, 0); - v4l2_fh_release(file); - } - -unlock: - mutex_unlock(&isc->lock); - return ret; -} - -static int isc_release(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - bool fh_singular; - int ret; - - mutex_lock(&isc->lock); - - fh_singular = v4l2_fh_is_singular_file(file); - - ret = _vb2_fop_release(file, NULL); - - if (fh_singular) - v4l2_subdev_call(sd, core, s_power, 0); - - mutex_unlock(&isc->lock); - - return ret; -} - -static const struct v4l2_file_operations isc_fops = { - .owner = THIS_MODULE, - .open = isc_open, - .release = isc_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id) -{ - struct isc_device *isc = (struct isc_device *)dev_id; - struct regmap *regmap = isc->regmap; - u32 isc_intsr, isc_intmask, pending; - irqreturn_t ret = IRQ_NONE; - - regmap_read(regmap, ISC_INTSR, &isc_intsr); - regmap_read(regmap, ISC_INTMASK, &isc_intmask); - - pending = isc_intsr & isc_intmask; - - if (likely(pending & ISC_INT_DDONE)) { - spin_lock(&isc->dma_queue_lock); - if (isc->cur_frm) { - struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; - struct vb2_buffer *vb = &vbuf->vb2_buf; - - vb->timestamp = ktime_get_ns(); - vbuf->sequence = isc->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - isc->cur_frm = NULL; - } - - if (!list_empty(&isc->dma_queue) && !isc->stop) { - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_start_dma(isc); - } - - if (isc->stop) - complete(&isc->comp); - - ret = IRQ_HANDLED; - spin_unlock(&isc->dma_queue_lock); - } - - if (pending & ISC_INT_HISDONE) { - schedule_work(&isc->awb_work); - ret = IRQ_HANDLED; - } - - return ret; -} -EXPORT_SYMBOL_GPL(atmel_isc_interrupt); - -static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 *hist_count = &ctrls->hist_count[ctrls->hist_id]; - u32 *hist_entry = &ctrls->hist_entry[0]; - u32 i; - - *min = 0; - *max = HIST_ENTRIES; - - regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry, - hist_entry, HIST_ENTRIES); - - *hist_count = 0; - /* - * we deliberately ignore the end of the histogram, - * the most white pixels - */ - for (i = 1; i < HIST_ENTRIES; i++) { - if (*hist_entry && !*min) - *min = i; - if (*hist_entry) - *max = i; - *hist_count += i * (*hist_entry++); - } - - if (!*min) - *min = 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: hist_id %u, hist_count %u", - ctrls->hist_id, *hist_count); -} - -static void isc_wb_update(struct isc_ctrls *ctrls) -{ - struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); - u32 *hist_count = &ctrls->hist_count[0]; - u32 c, offset[4]; - u64 avg = 0; - /* We compute two gains, stretch gain and grey world gain */ - u32 s_gain[4], gw_gain[4]; - - /* - * According to Grey World, we need to set gains for R/B to normalize - * them towards the green channel. - * Thus we want to keep Green as fixed and adjust only Red/Blue - * Compute the average of the both green channels first - */ - avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + - (u64)hist_count[ISC_HIS_CFG_MODE_GB]; - avg >>= 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: green components average %llu\n", avg); - - /* Green histogram is null, nothing to do */ - if (!avg) - return; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* - * the color offset is the minimum value of the histogram. - * we stretch this color to the full range by substracting - * this value from the color component. - */ - offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; - /* - * The offset is always at least 1. If the offset is 1, we do - * not need to adjust it, so our result must be zero. - * the offset is computed in a histogram on 9 bits (0..512) - * but the offset in register is based on - * 12 bits pipeline (0..4096). - * we need to shift with the 3 bits that the histogram is - * ignoring - */ - ctrls->offset[c] = (offset[c] - 1) << 3; - - /* - * the offset is then taken and converted to 2's complements, - * and must be negative, as we subtract this value from the - * color components - */ - ctrls->offset[c] = -ctrls->offset[c]; - - /* - * the stretch gain is the total number of histogram bins - * divided by the actual range of color component (Max - Min) - * If we compute gain like this, the actual color component - * will be stretched to the full histogram. - * We need to shift 9 bits for precision, we have 9 bits for - * decimals - */ - s_gain[c] = (HIST_ENTRIES << 9) / - (ctrls->hist_minmax[c][HIST_MAX_INDEX] - - ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); - - /* - * Now we have to compute the gain w.r.t. the average. - * Add/lose gain to the component towards the average. - * If it happens that the component is zero, use the - * fixed point value : 1.0 gain. - */ - if (hist_count[c]) - gw_gain[c] = div_u64(avg << 9, hist_count[c]); - else - gw_gain[c] = 1 << 9; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, s_gain %u, gw_gain %u\n", - c, s_gain[c], gw_gain[c]); - /* multiply both gains and adjust for decimals */ - ctrls->gain[c] = s_gain[c] * gw_gain[c]; - ctrls->gain[c] >>= 9; - - /* make sure we are not out of range */ - ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, final gain %u\n", - c, ctrls->gain[c]); - } -} - -static void isc_awb_work(struct work_struct *w) -{ - struct isc_device *isc = - container_of(w, struct isc_device, awb_work); - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 hist_id = ctrls->hist_id; - u32 baysel; - unsigned long flags; - u32 min, max; - int ret; - - if (ctrls->hist_stat != HIST_ENABLED) - return; - - isc_hist_count(isc, &min, &max); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); - - ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; - ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; - - if (hist_id != ISC_HIS_CFG_MODE_B) { - hist_id++; - } else { - isc_wb_update(ctrls); - hist_id = ISC_HIS_CFG_MODE_GR; - } - - ctrls->hist_id = hist_id; - baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) - return; - - /* - * only update if we have all the required histograms and controls - * if awb has been disabled, we need to reset registers as well. - */ - if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { - /* - * It may happen that DMA Done IRQ will trigger while we are - * updating white balance registers here. - * In that case, only parts of the controls have been updated. - * We can avoid that by locking the section. - */ - spin_lock_irqsave(&isc->awb_lock, flags); - isc_update_awb_ctrls(isc); - spin_unlock_irqrestore(&isc->awb_lock, flags); - - /* - * if we are doing just the one time white balance adjustment, - * we are basically done. - */ - if (ctrls->awb == ISC_WB_ONETIME) { - v4l2_info(&isc->v4l2_dev, - "Completed one time white-balance adjustment.\n"); - /* update the v4l2 controls values */ - isc_update_v4l2_ctrls(isc); - ctrls->awb = ISC_WB_NONE; - } - } - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - hist_id | baysel | ISC_HIS_CFG_RAR); - - /* - * We have to make sure the streaming has not stopped meanwhile. - * ISC requires a frame to clock the internal profile update. - * To avoid issues, lock the sequence with a mutex - */ - mutex_lock(&isc->awb_mutex); - - /* streaming is not active anymore */ - if (isc->stop) { - mutex_unlock(&isc->awb_mutex); - return; - } - - isc_update_profile(isc); - - mutex_unlock(&isc->awb_mutex); - - /* if awb has been disabled, we don't need to start another histogram */ - if (ctrls->awb) - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - pm_runtime_put_sync(isc->dev); -} - -static int isc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; - break; - case V4L2_CID_CONTRAST: - ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK; - break; - case V4L2_CID_GAMMA: - ctrls->gamma_index = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops isc_ctrl_ops = { - .s_ctrl = isc_s_ctrl, -}; - -static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - if (ctrl->val == 1) - ctrls->awb = ISC_WB_AUTO; - else - ctrls->awb = ISC_WB_NONE; - - /* configure the controls with new values from v4l2 */ - if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; - - if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; - - isc_update_awb_ctrls(isc); - - mutex_lock(&isc->awb_mutex); - if (vb2_is_streaming(&isc->vb2_vidq)) { - /* - * If we are streaming, we can update profile to - * have the new settings in place. - */ - isc_update_profile(isc); - } else { - /* - * The auto cluster will activate automatically this - * control. This has to be deactivated when not - * streaming. - */ - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - } - mutex_unlock(&isc->awb_mutex); - - /* if we have autowhitebalance on, start histogram procedure */ - if (ctrls->awb == ISC_WB_AUTO && - vb2_is_streaming(&isc->vb2_vidq) && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - - /* - * for one time whitebalance adjustment, check the button, - * if it's pressed, perform the one time operation. - */ - if (ctrls->awb == ISC_WB_NONE && - ctrl->cluster[ISC_CTRL_DO_WB]->is_new && - !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & - V4L2_CTRL_FLAG_INACTIVE)) { - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); - } - return 0; - } - return 0; -} - -static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - switch (ctrl->id) { - /* being a cluster, this id will be called for every control */ - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->cluster[ISC_CTRL_R_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GB]; - - ctrl->cluster[ISC_CTRL_R_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GB]; - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops isc_awb_ops = { - .s_ctrl = isc_s_awb_ctrl, - .g_volatile_ctrl = isc_g_volatile_awb_ctrl, -}; - -#define ISC_CTRL_OFF(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = -4095, \ - .max = 4095, \ - .step = 1, \ - .def = 0, \ - } - -ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); -ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); -ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); -ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); - -#define ISC_CTRL_GAIN(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = 0, \ - .max = 8191, \ - .step = 1, \ - .def = 512, \ - } - -ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); -ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); -ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); -ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); - -static int isc_ctrl_init(struct isc_device *isc) -{ - const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - int ret; - - ctrls->hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - - ret = v4l2_ctrl_handler_init(hdl, 13); - if (ret < 0) - return ret; - - /* Initialize product specific controls. For example, contrast */ - isc->config_ctrls(isc, ops); - - ctrls->brightness = 0; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, - isc->gamma_max); - isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - - /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_DO_WHITE_BALANCE, - 0, 0, 0, 0); - - if (!isc->do_wb_ctrl) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); - isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); - isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); - isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); - isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); - isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); - isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); - isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); - - /* - * The cluster is in auto mode with autowhitebalance enabled - * and manual mode otherwise. - */ - v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); - - v4l2_ctrl_handler_setup(hdl); - - return 0; -} - -static int isc_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct isc_subdev_entity *subdev_entity = - container_of(notifier, struct isc_subdev_entity, notifier); - - if (video_is_registered(&isc->video_dev)) { - v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); - return -EBUSY; - } - - subdev_entity->sd = subdev; - - return 0; -} - -static void isc_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - mutex_destroy(&isc->awb_mutex); - cancel_work_sync(&isc->awb_work); - video_unregister_device(&isc->video_dev); - v4l2_ctrl_handler_free(&isc->ctrls.handler); -} - -static struct isc_format *find_format_by_code(struct isc_device *isc, - unsigned int code, int *index) -{ - struct isc_format *fmt = &isc->formats_list[0]; - unsigned int i; - - for (i = 0; i < isc->formats_list_size; i++) { - if (fmt->mbus_code == code) { - *index = i; - return fmt; - } - - fmt++; - } - - return NULL; -} - -static int isc_formats_init(struct isc_device *isc) -{ - struct isc_format *fmt; - struct v4l2_subdev *subdev = isc->current_subdev->sd; - unsigned int num_fmts, i, j; - u32 list_size = isc->formats_list_size; - struct v4l2_subdev_mbus_code_enum mbus_code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - num_fmts = 0; - while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code)) { - mbus_code.index++; - - fmt = find_format_by_code(isc, mbus_code.code, &i); - if (!fmt) { - v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n", - mbus_code.code); - continue; - } - - fmt->sd_support = true; - num_fmts++; - } - - if (!num_fmts) - return -ENXIO; - - isc->num_user_formats = num_fmts; - isc->user_formats = devm_kcalloc(isc->dev, - num_fmts, sizeof(*isc->user_formats), - GFP_KERNEL); - if (!isc->user_formats) - return -ENOMEM; - - fmt = &isc->formats_list[0]; - for (i = 0, j = 0; i < list_size; i++) { - if (fmt->sd_support) - isc->user_formats[j++] = fmt; - fmt++; - } - - return 0; -} - -static int isc_set_default_fmt(struct isc_device *isc) -{ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .field = V4L2_FIELD_NONE, - .pixelformat = isc->user_formats[0]->fourcc, - }, - }; - int ret; - - ret = isc_try_fmt(isc, &f, NULL); - if (ret) - return ret; - - isc->fmt = f; - return 0; -} - -static int isc_async_complete(struct v4l2_async_notifier *notifier) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct video_device *vdev = &isc->video_dev; - struct vb2_queue *q = &isc->vb2_vidq; - int ret = 0; - - INIT_WORK(&isc->awb_work, isc_awb_work); - - ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n"); - return ret; - } - - isc->current_subdev = container_of(notifier, - struct isc_subdev_entity, notifier); - mutex_init(&isc->lock); - mutex_init(&isc->awb_mutex); - - init_completion(&isc->comp); - - /* Initialize videobuf2 queue */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = isc; - q->buf_struct_size = sizeof(struct isc_buffer); - q->ops = &isc_vb2_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &isc->lock; - q->min_queued_buffers = 1; - q->dev = isc->dev; - - ret = vb2_queue_init(q); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "vb2_queue_init() failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Init video dma queues */ - INIT_LIST_HEAD(&isc->dma_queue); - spin_lock_init(&isc->dma_queue_lock); - spin_lock_init(&isc->awb_lock); - - ret = isc_formats_init(isc); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "Init format failed: %d\n", ret); - goto isc_async_complete_err; - } - - ret = isc_set_default_fmt(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); - goto isc_async_complete_err; - } - - ret = isc_ctrl_init(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Register video device */ - strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); - vdev->release = video_device_release_empty; - vdev->fops = &isc_fops; - vdev->ioctl_ops = &isc_ioctl_ops; - vdev->v4l2_dev = &isc->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = q; - vdev->lock = &isc->lock; - vdev->ctrl_handler = &isc->ctrls.handler; - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - video_set_drvdata(vdev, isc); - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "video_register_device failed: %d\n", ret); - goto isc_async_complete_err; - } - - return 0; - -isc_async_complete_err: - mutex_destroy(&isc->awb_mutex); - mutex_destroy(&isc->lock); - return ret; -} - -const struct v4l2_async_notifier_operations atmel_isc_async_ops = { - .bound = isc_async_bound, - .unbind = isc_async_unbind, - .complete = isc_async_complete, -}; -EXPORT_SYMBOL_GPL(atmel_isc_async_ops); - -void atmel_isc_subdev_cleanup(struct isc_device *isc) -{ - struct isc_subdev_entity *subdev_entity; - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - v4l2_async_nf_unregister(&subdev_entity->notifier); - v4l2_async_nf_cleanup(&subdev_entity->notifier); - } - - INIT_LIST_HEAD(&isc->subdev_entities); -} -EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup); - -int atmel_isc_pipeline_init(struct isc_device *isc) -{ - struct device *dev = isc->dev; - struct regmap *regmap = isc->regmap; - struct regmap_field *regs; - unsigned int i; - - /* - * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC--> - * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420 - */ - const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { - REG_FIELD(ISC_DPC_CTRL, 0, 0), - REG_FIELD(ISC_DPC_CTRL, 1, 1), - REG_FIELD(ISC_DPC_CTRL, 2, 2), - REG_FIELD(ISC_WB_CTRL, 0, 0), - REG_FIELD(ISC_CFA_CTRL, 0, 0), - REG_FIELD(ISC_CC_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 1, 1), - REG_FIELD(ISC_GAM_CTRL, 2, 2), - REG_FIELD(ISC_GAM_CTRL, 3, 3), - REG_FIELD(ISC_VHXS_CTRL, 0, 0), - REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0), - REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0), - REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0), - REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0), - }; - - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - isc->pipeline[i] = regs; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init); - -/* regmap configuration */ -#define ATMEL_ISC_REG_MAX 0xd5c -const struct regmap_config atmel_isc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = ATMEL_ISC_REG_MAX, -}; -EXPORT_SYMBOL_GPL(atmel_isc_regmap_config); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_AUTHOR("Eugen Hristev"); -MODULE_DESCRIPTION("Atmel ISC common code base"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c deleted file mode 100644 index d442b5f4c931..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common clock driver setup - * - * Copyright (C) 2016 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static int isc_wait_clk_stable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - struct regmap *regmap = isc_clk->regmap; - unsigned long timeout = jiffies + usecs_to_jiffies(1000); - unsigned int status; - - while (time_before(jiffies, timeout)) { - regmap_read(regmap, ISC_CLKSR, &status); - if (!(status & ISC_CLKSR_SIP)) - return 0; - - usleep_range(10, 250); - } - - return -ETIMEDOUT; -} - -static int isc_clk_prepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return ret; - - return isc_wait_clk_stable(hw); -} - -static void isc_clk_unprepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - isc_wait_clk_stable(hw); - - pm_runtime_put_sync(isc_clk->dev); -} - -static int isc_clk_enable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - struct regmap *regmap = isc_clk->regmap; - unsigned long flags; - unsigned int status; - - dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", - __func__, id, isc_clk->div, isc_clk->parent_id); - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_update_bits(regmap, ISC_CLKCFG, - ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), - (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | - (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); - - regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); - - regmap_read(regmap, ISC_CLKSR, &status); - if (status & ISC_CLK(id)) - return 0; - else - return -EINVAL; -} - -static void isc_clk_disable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - unsigned long flags; - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); -} - -static int isc_clk_is_enabled(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 status; - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return 0; - - regmap_read(isc_clk->regmap, ISC_CLKSR, &status); - - pm_runtime_put_sync(isc_clk->dev); - - return status & ISC_CLK(isc_clk->id) ? 1 : 0; -} - -static unsigned long -isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); -} - -static int isc_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - long best_rate = -EINVAL; - int best_diff = -1; - unsigned int i, div; - - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - struct clk_hw *parent; - unsigned long parent_rate; - - parent = clk_hw_get_parent_by_index(hw, i); - if (!parent) - continue; - - parent_rate = clk_hw_get_rate(parent); - if (!parent_rate) - continue; - - for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { - unsigned long rate; - int diff; - - rate = DIV_ROUND_CLOSEST(parent_rate, div); - diff = abs(req->rate - rate); - - if (best_diff < 0 || best_diff > diff) { - best_rate = rate; - best_diff = diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } - - if (!best_diff || rate < req->rate) - break; - } - - if (!best_diff) - break; - } - - dev_dbg(isc_clk->dev, - "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", - __func__, best_rate, - __clk_get_name((req->best_parent_hw)->clk), - req->best_parent_rate); - - if (best_rate < 0) - return best_rate; - - req->rate = best_rate; - - return 0; -} - -static int isc_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - if (index >= clk_hw_get_num_parents(hw)) - return -EINVAL; - - isc_clk->parent_id = index; - - return 0; -} - -static u8 isc_clk_get_parent(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return isc_clk->parent_id; -} - -static int isc_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 div; - - if (!rate) - return -EINVAL; - - div = DIV_ROUND_CLOSEST(parent_rate, rate); - if (div > (ISC_CLK_MAX_DIV + 1) || !div) - return -EINVAL; - - isc_clk->div = div - 1; - - return 0; -} - -static const struct clk_ops isc_clk_ops = { - .prepare = isc_clk_prepare, - .unprepare = isc_clk_unprepare, - .enable = isc_clk_enable, - .disable = isc_clk_disable, - .is_enabled = isc_clk_is_enabled, - .recalc_rate = isc_clk_recalc_rate, - .determine_rate = isc_clk_determine_rate, - .set_parent = isc_clk_set_parent, - .get_parent = isc_clk_get_parent, - .set_rate = isc_clk_set_rate, -}; - -static int isc_clk_register(struct isc_device *isc, unsigned int id) -{ - struct regmap *regmap = isc->regmap; - struct device_node *np = isc->dev->of_node; - struct isc_clk *isc_clk; - struct clk_init_data init; - const char *clk_name = np->name; - const char *parent_names[3]; - int num_parents; - - if (id == ISC_ISPCK && !isc->ispck_required) - return 0; - - num_parents = of_clk_get_parent_count(np); - if (num_parents < 1 || num_parents > 3) - return -EINVAL; - - if (num_parents > 2 && id == ISC_ISPCK) - num_parents = 2; - - of_clk_parent_fill(np, parent_names, num_parents); - - if (id == ISC_MCK) - of_property_read_string(np, "clock-output-names", &clk_name); - else - clk_name = "isc-ispck"; - - init.parent_names = parent_names; - init.num_parents = num_parents; - init.name = clk_name; - init.ops = &isc_clk_ops; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; - - isc_clk = &isc->isc_clks[id]; - isc_clk->hw.init = &init; - isc_clk->regmap = regmap; - isc_clk->id = id; - isc_clk->dev = isc->dev; - spin_lock_init(&isc_clk->lock); - - isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); - if (IS_ERR(isc_clk->clk)) { - dev_err(isc->dev, "%s: clock register fail\n", clk_name); - return PTR_ERR(isc_clk->clk); - } else if (id == ISC_MCK) { - of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); - } - - return 0; -} - -int atmel_isc_clk_init(struct isc_device *isc) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) - isc->isc_clks[i].clk = ERR_PTR(-EINVAL); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - ret = isc_clk_register(isc, i); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_init); - -void atmel_isc_clk_cleanup(struct isc_device *isc) -{ - unsigned int i; - - of_clk_del_provider(isc->dev->of_node); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - struct isc_clk *isc_clk = &isc->isc_clks[i]; - - if (!IS_ERR(isc_clk->clk)) - clk_unregister(isc_clk->clk); - } -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h deleted file mode 100644 index d06b72228d4f..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h +++ /dev/null @@ -1,413 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ATMEL_ISC_REGS_H -#define __ATMEL_ISC_REGS_H - -#include - -/* ISC Control Enable Register 0 */ -#define ISC_CTRLEN 0x00000000 - -/* ISC Control Disable Register 0 */ -#define ISC_CTRLDIS 0x00000004 - -/* ISC Control Status Register 0 */ -#define ISC_CTRLSR 0x00000008 - -#define ISC_CTRL_CAPTURE BIT(0) -#define ISC_CTRL_UPPRO BIT(1) -#define ISC_CTRL_HISREQ BIT(2) -#define ISC_CTRL_HISCLR BIT(3) - -/* ISC Parallel Front End Configuration 0 Register */ -#define ISC_PFE_CFG0 0x0000000c - -#define ISC_PFE_CFG0_HPOL_LOW BIT(0) -#define ISC_PFE_CFG0_VPOL_LOW BIT(1) -#define ISC_PFE_CFG0_PPOL_LOW BIT(2) -#define ISC_PFE_CFG0_CCIR656 BIT(9) -#define ISC_PFE_CFG0_CCIR_CRC BIT(10) -#define ISC_PFE_CFG0_MIPI BIT(14) - -#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) -#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) - -#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) -#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) -#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) -#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) -#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) -#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) - -#define ISC_PFE_CFG0_COLEN BIT(12) -#define ISC_PFE_CFG0_ROWEN BIT(13) - -/* ISC Parallel Front End Configuration 1 Register */ -#define ISC_PFE_CFG1 0x00000010 - -#define ISC_PFE_CFG1_COLMIN(v) ((v)) -#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16) -#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16) - -/* ISC Parallel Front End Configuration 2 Register */ -#define ISC_PFE_CFG2 0x00000014 - -#define ISC_PFE_CFG2_ROWMIN(v) ((v)) -#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16) -#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16) - -/* ISC Clock Enable Register */ -#define ISC_CLKEN 0x00000018 - -/* ISC Clock Disable Register */ -#define ISC_CLKDIS 0x0000001c - -/* ISC Clock Status Register */ -#define ISC_CLKSR 0x00000020 -#define ISC_CLKSR_SIP BIT(31) - -#define ISC_CLK(n) BIT(n) - -/* ISC Clock Configuration Register */ -#define ISC_CLKCFG 0x00000024 -#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) -#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) -#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) -#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) - -/* ISC Interrupt Enable Register */ -#define ISC_INTEN 0x00000028 - -/* ISC Interrupt Disable Register */ -#define ISC_INTDIS 0x0000002c - -/* ISC Interrupt Mask Register */ -#define ISC_INTMASK 0x00000030 - -/* ISC Interrupt Status Register */ -#define ISC_INTSR 0x00000034 - -#define ISC_INT_DDONE BIT(8) -#define ISC_INT_HISDONE BIT(12) - -/* ISC DPC Control Register */ -#define ISC_DPC_CTRL 0x40 - -#define ISC_DPC_CTRL_DPCEN BIT(0) -#define ISC_DPC_CTRL_GDCEN BIT(1) -#define ISC_DPC_CTRL_BLCEN BIT(2) - -/* ISC DPC Config Register */ -#define ISC_DPC_CFG 0x44 - -#define ISC_DPC_CFG_BAYSEL_SHIFT 0 - -#define ISC_DPC_CFG_EITPOL BIT(4) - -#define ISC_DPC_CFG_TA_ENABLE BIT(14) -#define ISC_DPC_CFG_TC_ENABLE BIT(13) -#define ISC_DPC_CFG_TM_ENABLE BIT(12) - -#define ISC_DPC_CFG_RE_MODE BIT(17) - -#define ISC_DPC_CFG_GDCCLP_SHIFT 20 -#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20) - -#define ISC_DPC_CFG_BLOFF_SHIFT 24 -#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24) - -#define ISC_DPC_CFG_BAYCFG_SHIFT 0 -#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0) -/* ISC DPC Threshold Median Register */ -#define ISC_DPC_THRESHM 0x48 - -/* ISC DPC Threshold Closest Register */ -#define ISC_DPC_THRESHC 0x4C - -/* ISC DPC Threshold Average Register */ -#define ISC_DPC_THRESHA 0x50 - -/* ISC DPC STatus Register */ -#define ISC_DPC_SR 0x54 - -/* ISC White Balance Control Register */ -#define ISC_WB_CTRL 0x00000058 - -/* ISC White Balance Configuration Register */ -#define ISC_WB_CFG 0x0000005c - -/* ISC White Balance Offset for R, GR Register */ -#define ISC_WB_O_RGR 0x00000060 - -/* ISC White Balance Offset for B, GB Register */ -#define ISC_WB_O_BGB 0x00000064 - -/* ISC White Balance Gain for R, GR Register */ -#define ISC_WB_G_RGR 0x00000068 - -/* ISC White Balance Gain for B, GB Register */ -#define ISC_WB_G_BGB 0x0000006c - -/* ISC Color Filter Array Control Register */ -#define ISC_CFA_CTRL 0x00000070 - -/* ISC Color Filter Array Configuration Register */ -#define ISC_CFA_CFG 0x00000074 -#define ISC_CFA_CFG_EITPOL BIT(4) - -#define ISC_BAY_CFG_GRGR 0x0 -#define ISC_BAY_CFG_RGRG 0x1 -#define ISC_BAY_CFG_GBGB 0x2 -#define ISC_BAY_CFG_BGBG 0x3 - -/* ISC Color Correction Control Register */ -#define ISC_CC_CTRL 0x00000078 - -/* ISC Color Correction RR RG Register */ -#define ISC_CC_RR_RG 0x0000007c - -/* ISC Color Correction RB OR Register */ -#define ISC_CC_RB_OR 0x00000080 - -/* ISC Color Correction GR GG Register */ -#define ISC_CC_GR_GG 0x00000084 - -/* ISC Color Correction GB OG Register */ -#define ISC_CC_GB_OG 0x00000088 - -/* ISC Color Correction BR BG Register */ -#define ISC_CC_BR_BG 0x0000008c - -/* ISC Color Correction BB OB Register */ -#define ISC_CC_BB_OB 0x00000090 - -/* ISC Gamma Correction Control Register */ -#define ISC_GAM_CTRL 0x00000094 - -#define ISC_GAM_CTRL_BIPART BIT(4) - -/* ISC_Gamma Correction Blue Entry Register */ -#define ISC_GAM_BENTRY 0x00000098 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_GENTRY 0x00000198 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_RENTRY 0x00000298 - -/* ISC VHXS Control Register */ -#define ISC_VHXS_CTRL 0x398 - -/* ISC VHXS Source Size Register */ -#define ISC_VHXS_SS 0x39C - -/* ISC VHXS Destination Size Register */ -#define ISC_VHXS_DS 0x3A0 - -/* ISC Vertical Factor Register */ -#define ISC_VXS_FACT 0x3a4 - -/* ISC Horizontal Factor Register */ -#define ISC_HXS_FACT 0x3a8 - -/* ISC Vertical Config Register */ -#define ISC_VXS_CFG 0x3ac - -/* ISC Horizontal Config Register */ -#define ISC_HXS_CFG 0x3b0 - -/* ISC Vertical Tap Register */ -#define ISC_VXS_TAP 0x3b4 - -/* ISC Horizontal Tap Register */ -#define ISC_HXS_TAP 0x434 - -/* Offset for CSC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CSC_OFFSET 0 -/* Offset for CSC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CSC_OFFSET 0x11c - -/* Color Space Conversion Control Register */ -#define ISC_CSC_CTRL 0x00000398 - -/* Color Space Conversion YR YG Register */ -#define ISC_CSC_YR_YG 0x0000039c - -/* Color Space Conversion YB OY Register */ -#define ISC_CSC_YB_OY 0x000003a0 - -/* Color Space Conversion CBR CBG Register */ -#define ISC_CSC_CBR_CBG 0x000003a4 - -/* Color Space Conversion CBB OCB Register */ -#define ISC_CSC_CBB_OCB 0x000003a8 - -/* Color Space Conversion CRR CRG Register */ -#define ISC_CSC_CRR_CRG 0x000003ac - -/* Color Space Conversion CRB OCR Register */ -#define ISC_CSC_CRB_OCR 0x000003b0 - -/* Offset for CBC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CBC_OFFSET 0 -/* Offset for CBC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CBC_OFFSET 0x11c - -/* Contrast And Brightness Control Register */ -#define ISC_CBC_CTRL 0x000003b4 - -/* Contrast And Brightness Configuration Register */ -#define ISC_CBC_CFG 0x000003b8 - -/* Brightness Register */ -#define ISC_CBC_BRIGHT 0x000003bc -#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0) - -/* Contrast Register */ -#define ISC_CBC_CONTRAST 0x000003c0 -#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0) - -/* Hue Register */ -#define ISC_CBCHS_HUE 0x4e0 -/* Saturation Register */ -#define ISC_CBCHS_SAT 0x4e4 - -/* Offset for SUB422 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB422_OFFSET 0 -/* Offset for SUB422 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB422_OFFSET 0x124 - -/* Subsampling 4:4:4 to 4:2:2 Control Register */ -#define ISC_SUB422_CTRL 0x000003c4 - -/* Offset for SUB420 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB420_OFFSET 0 -/* Offset for SUB420 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB420_OFFSET 0x124 -/* Subsampling 4:2:2 to 4:2:0 Control Register */ -#define ISC_SUB420_CTRL 0x000003cc - -/* Offset for RLP register specific to sama5d2 product */ -#define ISC_SAMA5D2_RLP_OFFSET 0 -/* Offset for RLP register specific to sama7g5 product */ -#define ISC_SAMA7G5_RLP_OFFSET 0x124 -/* Rounding, Limiting and Packing Configuration Register */ -#define ISC_RLP_CFG 0x000003d0 - -#define ISC_RLP_CFG_MODE_DAT8 0x0 -#define ISC_RLP_CFG_MODE_DAT9 0x1 -#define ISC_RLP_CFG_MODE_DAT10 0x2 -#define ISC_RLP_CFG_MODE_DAT11 0x3 -#define ISC_RLP_CFG_MODE_DAT12 0x4 -#define ISC_RLP_CFG_MODE_DATY8 0x5 -#define ISC_RLP_CFG_MODE_DATY10 0x6 -#define ISC_RLP_CFG_MODE_ARGB444 0x7 -#define ISC_RLP_CFG_MODE_ARGB555 0x8 -#define ISC_RLP_CFG_MODE_RGB565 0x9 -#define ISC_RLP_CFG_MODE_ARGB32 0xa -#define ISC_RLP_CFG_MODE_YYCC 0xb -#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc -#define ISC_RLP_CFG_MODE_YCYC 0xd -#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) - -#define ISC_RLP_CFG_LSH BIT(5) - -#define ISC_RLP_CFG_YMODE_YUYV (3 << 6) -#define ISC_RLP_CFG_YMODE_YVYU (2 << 6) -#define ISC_RLP_CFG_YMODE_VYUY (0 << 6) -#define ISC_RLP_CFG_YMODE_UYVY (1 << 6) - -#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6) - -/* Offset for HIS register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_OFFSET 0 -/* Offset for HIS register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_OFFSET 0x124 -/* Histogram Control Register */ -#define ISC_HIS_CTRL 0x000003d4 - -#define ISC_HIS_CTRL_EN BIT(0) -#define ISC_HIS_CTRL_DIS 0x0 - -/* Histogram Configuration Register */ -#define ISC_HIS_CFG 0x000003d8 - -#define ISC_HIS_CFG_MODE_GR 0x0 -#define ISC_HIS_CFG_MODE_R 0x1 -#define ISC_HIS_CFG_MODE_GB 0x2 -#define ISC_HIS_CFG_MODE_B 0x3 -#define ISC_HIS_CFG_MODE_Y 0x4 -#define ISC_HIS_CFG_MODE_RAW 0x5 -#define ISC_HIS_CFG_MODE_YCCIR656 0x6 - -#define ISC_HIS_CFG_BAYSEL_SHIFT 4 - -#define ISC_HIS_CFG_RAR BIT(8) - -/* Offset for DMA register specific to sama5d2 product */ -#define ISC_SAMA5D2_DMA_OFFSET 0 -/* Offset for DMA register specific to sama7g5 product */ -#define ISC_SAMA7G5_DMA_OFFSET 0x13c - -/* DMA Configuration Register */ -#define ISC_DCFG 0x000003e0 -#define ISC_DCFG_IMODE_PACKED8 0x0 -#define ISC_DCFG_IMODE_PACKED16 0x1 -#define ISC_DCFG_IMODE_PACKED32 0x2 -#define ISC_DCFG_IMODE_YC422SP 0x3 -#define ISC_DCFG_IMODE_YC422P 0x4 -#define ISC_DCFG_IMODE_YC420SP 0x5 -#define ISC_DCFG_IMODE_YC420P 0x6 -#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) - -#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) -#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) -#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) -#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) -#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4) -#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4) - -#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) -#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) -#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) -#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) -#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8) -#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8) - -/* DMA Control Register */ -#define ISC_DCTRL 0x000003e4 - -#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) -#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) -#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) -#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) - -#define ISC_DCTRL_IE_IS (0x0 << 4) - -/* DMA Descriptor Address Register */ -#define ISC_DNDA 0x000003e8 - -/* DMA Address 0 Register */ -#define ISC_DAD0 0x000003ec - -/* DMA Address 1 Register */ -#define ISC_DAD1 0x000003f4 - -/* DMA Address 2 Register */ -#define ISC_DAD2 0x000003fc - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_VERSION_OFFSET 0 -#define ISC_SAMA7G5_VERSION_OFFSET 0x13c -/* Version Register */ -#define ISC_VERSION 0x0000040c - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0 -/* Offset for version register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c -/* Histogram Entry */ -#define ISC_HIS_ENTRY 0x00000410 - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h deleted file mode 100644 index 31767ea74be6..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc.h +++ /dev/null @@ -1,362 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Microchip Image Sensor Controller (ISC) driver header file - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#ifndef _ATMEL_ISC_H_ - -#include -#include - -#include -#include -#include - -#define ISC_CLK_MAX_DIV 255 - -enum isc_clk_id { - ISC_ISPCK = 0, - ISC_MCK = 1, -}; - -struct isc_clk { - struct clk_hw hw; - struct clk *clk; - struct regmap *regmap; - spinlock_t lock; /* serialize access to clock registers */ - u8 id; - u8 parent_id; - u32 div; - struct device *dev; -}; - -#define to_isc_clk(v) container_of(v, struct isc_clk, hw) - -struct isc_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct isc_subdev_entity { - struct v4l2_subdev *sd; - struct v4l2_async_connection *asd; - struct device_node *epn; - struct v4l2_async_notifier notifier; - - u32 pfe_cfg0; - - struct list_head list; -}; - -/* - * struct isc_format - ISC media bus format information - This structure represents the interface between the ISC - and the sensor. It's the input format received by - the ISC. - * @fourcc: Fourcc code for this format - * @mbus_code: V4L2 media bus format code. - * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer. - this is either BGBG, RGRG, etc. - * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC - */ - -struct isc_format { - u32 fourcc; - u32 mbus_code; - u32 cfa_baycfg; - - bool sd_support; - u32 pfe_cfg0_bps; -}; - -/* Pipeline bitmap */ -#define DPC_DPCENABLE BIT(0) -#define DPC_GDCENABLE BIT(1) -#define DPC_BLCENABLE BIT(2) -#define WB_ENABLE BIT(3) -#define CFA_ENABLE BIT(4) -#define CC_ENABLE BIT(5) -#define GAM_ENABLE BIT(6) -#define GAM_BENABLE BIT(7) -#define GAM_GENABLE BIT(8) -#define GAM_RENABLE BIT(9) -#define VHXS_ENABLE BIT(10) -#define CSC_ENABLE BIT(11) -#define CBC_ENABLE BIT(12) -#define SUB422_ENABLE BIT(13) -#define SUB420_ENABLE BIT(14) - -#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE) - -/* - * struct fmt_config - ISC format configuration and internal pipeline - This structure represents the internal configuration - of the ISC. - It also holds the format that ISC will present to v4l2. - * @sd_format: Pointer to an isc_format struct that holds the sensor - configuration. - * @fourcc: Fourcc code for this format. - * @bpp: Bytes per pixel in the current format. - * @bpp_v4l2: Bytes per pixel in the current format, for v4l2. - This differs from 'bpp' in the sense that in planar - formats, it refers only to the first plane. - * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging) - * @dcfg_imode: Configuration of the input of the DMA module - * @dctrl_dview: Configuration of the output of the DMA module - * @bits_pipeline: Configuration of the pipeline, which modules are enabled - */ -struct fmt_config { - struct isc_format *sd_format; - - u32 fourcc; - u8 bpp; - u8 bpp_v4l2; - - u32 rlp_cfg_mode; - u32 dcfg_imode; - u32 dctrl_dview; - - u32 bits_pipeline; -}; - -#define HIST_ENTRIES 512 -#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1) - -enum{ - HIST_INIT = 0, - HIST_ENABLED, - HIST_DISABLED, -}; - -struct isc_ctrls { - struct v4l2_ctrl_handler handler; - - u32 brightness; - u32 contrast; - u8 gamma_index; -#define ISC_WB_NONE 0 -#define ISC_WB_AUTO 1 -#define ISC_WB_ONETIME 2 - u8 awb; - - /* one for each component : GR, R, GB, B */ - u32 gain[HIST_BAYER]; - s32 offset[HIST_BAYER]; - - u32 hist_entry[HIST_ENTRIES]; - u32 hist_count[HIST_BAYER]; - u8 hist_id; - u8 hist_stat; -#define HIST_MIN_INDEX 0 -#define HIST_MAX_INDEX 1 - u32 hist_minmax[HIST_BAYER][2]; -}; - -#define ISC_PIPE_LINE_NODE_NUM 15 - -/* - * struct isc_reg_offsets - ISC device register offsets - * @csc: Offset for the CSC register - * @cbc: Offset for the CBC register - * @sub422: Offset for the SUB422 register - * @sub420: Offset for the SUB420 register - * @rlp: Offset for the RLP register - * @his: Offset for the HIS related registers - * @dma: Offset for the DMA related registers - * @version: Offset for the version register - * @his_entry: Offset for the HIS entries registers - */ -struct isc_reg_offsets { - u32 csc; - u32 cbc; - u32 sub422; - u32 sub420; - u32 rlp; - u32 his; - u32 dma; - u32 version; - u32 his_entry; -}; - -/* - * struct isc_device - ISC device driver data/config struct - * @regmap: Register map - * @hclock: Hclock clock input (refer datasheet) - * @ispck: iscpck clock (refer datasheet) - * @isc_clks: ISC clocks - * @ispck_required: ISC requires ISP Clock initialization - * @dcfg: DMA master configuration, architecture dependent - * - * @dev: Registered device driver - * @v4l2_dev: v4l2 registered device - * @video_dev: registered video device - * - * @vb2_vidq: video buffer 2 video queue - * @dma_queue_lock: lock to serialize the dma buffer queue - * @dma_queue: the queue for dma buffers - * @cur_frm: current isc frame/buffer - * @sequence: current frame number - * @stop: true if isc is not streaming, false if streaming - * @comp: completion reference that signals frame completion - * - * @fmt: current v42l format - * @user_formats: list of formats that are supported and agreed with sd - * @num_user_formats: how many formats are in user_formats - * - * @config: current ISC format configuration - * @try_config: the current ISC try format , not yet activated - * - * @ctrls: holds information about ISC controls - * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button - * @awb_work: workqueue reference for autowhitebalance histogram - * analysis - * - * @lock: lock for serializing userspace file operations - * with ISC operations - * @awb_mutex: serialize access to streaming status from awb work queue - * @awb_lock: lock for serializing awb work queue operations - * with DMA/buffer operations - * - * @pipeline: configuration of the ISC pipeline - * - * @current_subdev: current subdevice: the sensor - * @subdev_entities: list of subdevice entitites - * - * @gamma_table: pointer to the table with gamma values, has - * gamma_max sets of GAMMA_ENTRIES entries each - * @gamma_max: maximum number of sets of inside the gamma_table - * - * @max_width: maximum frame width, dependent on the internal RAM - * @max_height: maximum frame height, dependent on the internal RAM - * - * @config_dpc: pointer to a function that initializes product - * specific DPC module - * @config_csc: pointer to a function that initializes product - * specific CSC module - * @config_cbc: pointer to a function that initializes product - * specific CBC module - * @config_cc: pointer to a function that initializes product - * specific CC module - * @config_gam: pointer to a function that initializes product - * specific GAMMA module - * @config_rlp: pointer to a function that initializes product - * specific RLP module - * @config_ctrls: pointer to a functoin that initializes product - * specific v4l2 controls. - * - * @adapt_pipeline: pointer to a function that adapts the pipeline bits - * to the product specific pipeline - * - * @offsets: struct holding the product specific register offsets - * @controller_formats: pointer to the array of possible formats that the - * controller can output - * @formats_list: pointer to the array of possible formats that can - * be used as an input to the controller - * @controller_formats_size: size of controller_formats array - * @formats_list_size: size of formats_list array - */ -struct isc_device { - struct regmap *regmap; - struct clk *hclock; - struct clk *ispck; - struct isc_clk isc_clks[2]; - bool ispck_required; - u32 dcfg; - - struct device *dev; - struct v4l2_device v4l2_dev; - struct video_device video_dev; - - struct vb2_queue vb2_vidq; - spinlock_t dma_queue_lock; - struct list_head dma_queue; - struct isc_buffer *cur_frm; - unsigned int sequence; - bool stop; - struct completion comp; - - struct v4l2_format fmt; - struct isc_format **user_formats; - unsigned int num_user_formats; - - struct fmt_config config; - struct fmt_config try_config; - - struct isc_ctrls ctrls; - struct work_struct awb_work; - - struct mutex lock; - struct mutex awb_mutex; - spinlock_t awb_lock; - - struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; - - struct isc_subdev_entity *current_subdev; - struct list_head subdev_entities; - - struct { -#define ISC_CTRL_DO_WB 1 -#define ISC_CTRL_R_GAIN 2 -#define ISC_CTRL_B_GAIN 3 -#define ISC_CTRL_GR_GAIN 4 -#define ISC_CTRL_GB_GAIN 5 -#define ISC_CTRL_R_OFF 6 -#define ISC_CTRL_B_OFF 7 -#define ISC_CTRL_GR_OFF 8 -#define ISC_CTRL_GB_OFF 9 - struct v4l2_ctrl *awb_ctrl; - struct v4l2_ctrl *do_wb_ctrl; - struct v4l2_ctrl *r_gain_ctrl; - struct v4l2_ctrl *b_gain_ctrl; - struct v4l2_ctrl *gr_gain_ctrl; - struct v4l2_ctrl *gb_gain_ctrl; - struct v4l2_ctrl *r_off_ctrl; - struct v4l2_ctrl *b_off_ctrl; - struct v4l2_ctrl *gr_off_ctrl; - struct v4l2_ctrl *gb_off_ctrl; - }; - -#define GAMMA_ENTRIES 64 - /* pointer to the defined gamma table */ - const u32 (*gamma_table)[GAMMA_ENTRIES]; - u32 gamma_max; - - u32 max_width; - u32 max_height; - - struct { - void (*config_dpc)(struct isc_device *isc); - void (*config_csc)(struct isc_device *isc); - void (*config_cbc)(struct isc_device *isc); - void (*config_cc)(struct isc_device *isc); - void (*config_gam)(struct isc_device *isc); - void (*config_rlp)(struct isc_device *isc); - - void (*config_ctrls)(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops); - - void (*adapt_pipeline)(struct isc_device *isc); - }; - - struct isc_reg_offsets offsets; - const struct isc_format *controller_formats; - struct isc_format *formats_list; - u32 controller_formats_size; - u32 formats_list_size; -}; - -extern const struct regmap_config atmel_isc_regmap_config; -extern const struct v4l2_async_notifier_operations atmel_isc_async_ops; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id); -int atmel_isc_pipeline_init(struct isc_device *isc); -int atmel_isc_clk_init(struct isc_device *isc); -void atmel_isc_subdev_cleanup(struct isc_device *isc); -void atmel_isc_clk_cleanup(struct isc_device *isc); - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c deleted file mode 100644 index 71e6e278a4b3..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ /dev/null @@ -1,644 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip Image Sensor Controller (ISC) driver - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - * - * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - * CSC: Programmable color space conversion - * CBC: Contrast and Brightness control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592 -#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944 - -#define ISC_SAMA5D2_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama5d2_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama5d2_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, - -}; - -static void isc_sama5d2_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama5d2_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, - isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, - isc->ctrls.contrast); -} - -static void isc_sama5d2_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama5d2_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 256; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); -} - -static void isc_sama5d2_config_dpc(struct isc_device *isc) -{ - /* This module is not present on sama5d2 pipeline */ -} - -static void isc_sama5d2_config_gam(struct isc_device *isc) -{ - /* No specific gamma configuration */ -} - -static void isc_sama5d2_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - /* - * In sama5d2, the YUV planar modes and the YUYV modes are treated - * in the same way in RLP register. - * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n) - * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n) - * but in sama5d2, the YCYC mode does not exist, and YYCC must be - * selected for both planar and interleaved modes, as in fact - * both modes are supported. - * - * Thus, if the YCYC mode is selected, replace it with the - * sama5d2-compliant mode which is YYCC . - */ - if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) { - rlp_mode &= ~ISC_RLP_CFG_MODE_MASK; - rlp_mode |= ISC_RLP_CFG_MODE_YYCC; - } - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK, rlp_mode); -} - -static void isc_sama5d2_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { - /* 0 --> gamma 1/1.8 */ - { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A, - 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012, - 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F, - 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E, - 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C, - 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B, - 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A, - 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A, - 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A, - 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009, - 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 }, - - /* 1 --> gamma 1/2 */ - { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B, - 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013, - 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F, - 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D, - 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B, - 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A, - 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A, - 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009, - 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009, - 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009, - 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 }, - - /* 2 --> gamma 1/2.2 */ - { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B, - 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012, - 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F, - 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C, - 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B, - 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A, - 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009, - 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009, - 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008, - 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007, - 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 }, -}; - -static int isc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - - INIT_LIST_HEAD(&isc->subdev_entities); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int atmel_isc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "atmel-sama5d2-isc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama5d2_gamma_table; - isc->gamma_max = 2; - - isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama5d2_config_dpc; - isc->config_csc = isc_sama5d2_config_csc; - isc->config_cbc = isc_sama5d2_config_cbc; - isc->config_cc = isc_sama5d2_config_cc; - isc->config_gam = isc_sama5d2_config_gam; - isc->config_rlp = isc_sama5d2_config_rlp; - isc->config_ctrls = isc_sama5d2_config_ctrls; - - isc->adapt_pipeline = isc_sama5d2_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET; - isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET; - isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama5d2_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats); - isc->formats_list = sama5d2_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list); - - /* sama5d2-isc - 8 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8; - - /* sama5d2-isc : ISPCK is required and mandatory */ - isc->ispck_required = true; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_clk; - } - - ret = isc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - isc->ispck = isc->isc_clks[ISC_ISPCK].clk; - - ret = clk_prepare_enable(isc->ispck); - if (ret) { - dev_err(dev, "failed to enable ispck: %d\n", ret); - goto disable_pm; - } - - /* ispck should be greater or equal to hclock */ - ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); - if (ret) { - dev_err(dev, "failed to set ispck rate: %d\n", ret); - goto unprepare_clk; - } - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip ISC version %x\n", ver); - - return 0; - -unprepare_clk: - clk_disable_unprepare(isc->ispck); - -disable_pm: - pm_runtime_disable(dev); - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void atmel_isc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused isc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused isc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - ret = clk_prepare_enable(isc->ispck); - if (ret) - clk_disable_unprepare(isc->hclock); - - return ret; -} - -static const struct dev_pm_ops atmel_isc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id atmel_isc_of_match[] = { - { .compatible = "atmel,sama5d2-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, atmel_isc_of_match); -#endif - -static struct platform_driver atmel_isc_driver = { - .probe = atmel_isc_probe, - .remove = atmel_isc_remove, - .driver = { - .name = "atmel-sama5d2-isc", - .pm = &atmel_isc_dev_pm_ops, - .of_match_table = of_match_ptr(atmel_isc_of_match), - }, -}; - -module_platform_driver(atmel_isc_driver); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c deleted file mode 100644 index 1f74c2dd044c..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ /dev/null @@ -1,607 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip eXtended Image Sensor Controller (XISC) driver - * - * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries - * - * Author: Eugen Hristev - * - * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * DPC: Defective Pixel Correction with black offset correction, green disparity - * correction and defective pixel correction (3 modules total) - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - *VHXS: Vertical and Horizontal Scaler - * CSC: Programmable color space conversion - *CBHS: Contrast Brightness Hue and Saturation control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - * DMA: This module performs DMA master accesses to write frames to external RAM - * HIS: Histogram module performs statistic counters on the frames - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264 -#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464 - -#define ISC_SAMA7G5_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama7g5_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_Y16, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama7g5_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, -}; - -static void isc_sama7g5_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama7g5_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure what is set via v4l2 ctrls */ - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast); - /* Configure Hue and Saturation as neutral midpoint */ - regmap_write(regmap, ISC_CBCHS_HUE, 0); - regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4)); -} - -static void isc_sama7g5_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama7g5_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 16; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16); -} - -static void isc_sama7g5_config_dpc(struct isc_device *isc) -{ - u32 bay_cfg = isc->config.sd_format->cfa_baycfg; - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK, - (64 << ISC_DPC_CFG_BLOFF_SHIFT)); - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK, - (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT)); -} - -static void isc_sama7g5_config_gam(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART, - ISC_GAM_CTRL_BIPART); -} - -static void isc_sama7g5_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH | - ISC_RLP_CFG_YMODE_MASK, rlp_mode); -} - -static void isc_sama7g5_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { - /* index 0 --> gamma bipartite */ - { - 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180, - 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100, - 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0, - 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0, - 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080, - 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a, - 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030, - 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026, - 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020, - 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c, - 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a }, -}; - -static int xisc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - bool mipi_mode; - - INIT_LIST_HEAD(&isc->subdev_entities); - - mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - if (mipi_mode) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int microchip_xisc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "microchip-sama7g5-xisc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama7g5_gamma_table; - isc->gamma_max = 0; - - isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama7g5_config_dpc; - isc->config_csc = isc_sama7g5_config_csc; - isc->config_cbc = isc_sama7g5_config_cbc; - isc->config_cc = isc_sama7g5_config_cc; - isc->config_gam = isc_sama7g5_config_gam; - isc->config_rlp = isc_sama7g5_config_rlp; - isc->config_ctrls = isc_sama7g5_config_ctrls; - - isc->adapt_pipeline = isc_sama7g5_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET; - isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET; - isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama7g5_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats); - isc->formats_list = sama7g5_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list); - - /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32; - - /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */ - isc->ispck_required = false; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_hclk; - } - - ret = xisc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip XISC version %x\n", ver); - - return 0; - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void microchip_xisc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused xisc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused xisc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - return ret; -} - -static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id microchip_xisc_of_match[] = { - { .compatible = "microchip,sama7g5-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); -#endif - -static struct platform_driver microchip_xisc_driver = { - .probe = microchip_xisc_probe, - .remove = microchip_xisc_remove, - .driver = { - .name = "microchip-sama7g5-xisc", - .pm = µchip_xisc_dev_pm_ops, - .of_match_table = of_match_ptr(microchip_xisc_of_match), - }, -}; - -module_platform_driver(microchip_xisc_driver); - -MODULE_AUTHOR("Eugen Hristev "); -MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC"); -MODULE_LICENSE("GPL v2"); From 5d579ece43f4ed2d88bd5e14d6de682dbc532954 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 17 May 2026 19:17:43 +0200 Subject: [PATCH 1172/5214] media: ivtv: use clamp in ivtv_try_fmt_vid_{out,cap} Replace multiple min(), max() calls with clamp(). Signed-off-by: Thorsten Blum Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-ioctl.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 8d5ea3aec06f..fc95f0bf48d5 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -467,15 +467,13 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format int h = fmt->fmt.pix.height; int min_h = 2; - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { /* YUV height must be a multiple of 32 */ h &= ~0x1f; min_h = 32; } - h = min(h, itv->is_50hz ? 576 : 480); - h = max(h, min_h); + h = clamp(h, min_h, itv->is_50hz ? 576 : 480); ivtv_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; @@ -516,8 +514,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format int field = fmt->fmt.pix.field; int ret = ivtv_g_fmt_vid_out(file, fh, fmt); - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); /* Why can the height be 576 even when the output is NTSC? Internally the buffers of the PVR350 are always set to 720x576. The @@ -533,8 +530,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format resolution is locked to the broadcast standard and not scaled. Thanks to Ian Armstrong for this explanation. */ - h = min(h, 576); - h = max(h, 2); + h = clamp(h, 2, 576); if (id->type == IVTV_DEC_STREAM_TYPE_YUV) fmt->fmt.pix.field = field; fmt->fmt.pix.width = w; From 2958d579ffb13571c9788d741e6115dc917b05a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 18:15:28 +0200 Subject: [PATCH 1173/5214] media: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Hans Verkuil --- drivers/media/cec/i2c/tda9950.c | 2 +- drivers/media/dvb-frontends/a8293.c | 4 +- drivers/media/dvb-frontends/af9013.c | 4 +- drivers/media/dvb-frontends/af9033.c | 4 +- drivers/media/dvb-frontends/au8522_decoder.c | 4 +- drivers/media/dvb-frontends/cxd2099.c | 4 +- drivers/media/dvb-frontends/cxd2820r_core.c | 4 +- drivers/media/dvb-frontends/dvb-pll.c | 44 +++++++++---------- drivers/media/dvb-frontends/helene.c | 4 +- drivers/media/dvb-frontends/lgdt3306a.c | 4 +- drivers/media/dvb-frontends/lgdt330x.c | 4 +- drivers/media/dvb-frontends/m88ds3103.c | 10 ++--- drivers/media/dvb-frontends/mn88443x.c | 8 ++-- drivers/media/dvb-frontends/mn88472.c | 4 +- drivers/media/dvb-frontends/mn88473.c | 4 +- drivers/media/dvb-frontends/mxl692.c | 4 +- drivers/media/dvb-frontends/rtl2830.c | 4 +- drivers/media/dvb-frontends/rtl2832.c | 4 +- drivers/media/dvb-frontends/si2165.c | 4 +- drivers/media/dvb-frontends/si2168.c | 4 +- drivers/media/dvb-frontends/sp2.c | 4 +- drivers/media/dvb-frontends/stv090x.c | 4 +- drivers/media/dvb-frontends/stv6110x.c | 4 +- drivers/media/dvb-frontends/tc90522.c | 6 +-- drivers/media/dvb-frontends/tda10071.c | 4 +- drivers/media/dvb-frontends/ts2020.c | 6 +-- drivers/media/i2c/ad5820.c | 4 +- drivers/media/i2c/adp1653.c | 2 +- drivers/media/i2c/adv7170.c | 4 +- drivers/media/i2c/adv7175.c | 4 +- drivers/media/i2c/adv7180.c | 24 +++++----- drivers/media/i2c/adv7183.c | 4 +- drivers/media/i2c/adv7343.c | 4 +- drivers/media/i2c/adv7393.c | 4 +- drivers/media/i2c/adv7511-v4l2.c | 2 +- drivers/media/i2c/adv7604.c | 8 ++-- drivers/media/i2c/adv7842.c | 2 +- drivers/media/i2c/ak881x.c | 4 +- drivers/media/i2c/bt819.c | 6 +-- drivers/media/i2c/bt856.c | 2 +- drivers/media/i2c/bt866.c | 2 +- drivers/media/i2c/cs3308.c | 2 +- drivers/media/i2c/cs5345.c | 2 +- drivers/media/i2c/cs53l32a.c | 2 +- drivers/media/i2c/cx25840/cx25840-core.c | 2 +- drivers/media/i2c/ds90ub913.c | 4 +- drivers/media/i2c/ds90ub953.c | 6 +-- drivers/media/i2c/ds90ub960.c | 8 ++-- drivers/media/i2c/dw9714.c | 2 +- drivers/media/i2c/et8ek8/et8ek8_driver.c | 2 +- drivers/media/i2c/imx274.c | 2 +- drivers/media/i2c/ir-kbd-i2c.c | 6 +-- drivers/media/i2c/isl7998x.c | 2 +- drivers/media/i2c/ks0127.c | 6 +-- drivers/media/i2c/lm3560.c | 4 +- drivers/media/i2c/lm3646.c | 4 +- drivers/media/i2c/m52790.c | 2 +- drivers/media/i2c/max2175.c | 4 +- drivers/media/i2c/ml86v7667.c | 4 +- drivers/media/i2c/msp3400-driver.c | 2 +- drivers/media/i2c/mt9m001.c | 2 +- drivers/media/i2c/mt9m111.c | 2 +- drivers/media/i2c/mt9t112.c | 2 +- drivers/media/i2c/mt9v011.c | 2 +- drivers/media/i2c/ov13858.c | 4 +- drivers/media/i2c/ov2640.c | 2 +- drivers/media/i2c/ov2659.c | 2 +- drivers/media/i2c/ov5640.c | 4 +- drivers/media/i2c/ov5645.c | 4 +- drivers/media/i2c/ov5647.c | 2 +- drivers/media/i2c/ov7640.c | 2 +- drivers/media/i2c/ov7670.c | 4 +- drivers/media/i2c/ov772x.c | 2 +- drivers/media/i2c/ov7740.c | 2 +- drivers/media/i2c/ov9640.c | 2 +- drivers/media/i2c/ov9650.c | 4 +- drivers/media/i2c/rj54n1cb0c.c | 2 +- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 2 +- drivers/media/i2c/s5k5baf.c | 2 +- drivers/media/i2c/saa6588.c | 2 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/saa7110.c | 2 +- drivers/media/i2c/saa7115.c | 14 +++--- drivers/media/i2c/saa7127.c | 10 ++--- drivers/media/i2c/saa717x.c | 2 +- drivers/media/i2c/saa7185.c | 2 +- drivers/media/i2c/sony-btf-mpx.c | 2 +- drivers/media/i2c/tc358743.c | 4 +- drivers/media/i2c/tda1997x.c | 6 +-- drivers/media/i2c/tda7432.c | 2 +- drivers/media/i2c/tda9840.c | 2 +- drivers/media/i2c/tea6415c.c | 2 +- drivers/media/i2c/tea6420.c | 2 +- drivers/media/i2c/ths7303.c | 6 +-- drivers/media/i2c/ths8200.c | 4 +- drivers/media/i2c/tlv320aic23b.c | 2 +- drivers/media/i2c/tvaudio.c | 2 +- drivers/media/i2c/tvp514x.c | 8 ++-- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/i2c/tvp7002.c | 2 +- drivers/media/i2c/tw2804.c | 2 +- drivers/media/i2c/tw9900.c | 2 +- drivers/media/i2c/tw9903.c | 2 +- drivers/media/i2c/tw9906.c | 2 +- drivers/media/i2c/tw9910.c | 2 +- drivers/media/i2c/uda1342.c | 2 +- drivers/media/i2c/upd64031a.c | 2 +- drivers/media/i2c/upd64083.c | 2 +- drivers/media/i2c/video-i2c.c | 6 +-- drivers/media/i2c/vp27smpx.c | 2 +- drivers/media/i2c/vpx3220.c | 6 +-- drivers/media/i2c/wm8739.c | 2 +- drivers/media/i2c/wm8775.c | 2 +- drivers/media/radio/radio-tea5764.c | 2 +- drivers/media/radio/saa7706h.c | 4 +- drivers/media/radio/si470x/radio-si470x-i2c.c | 2 +- drivers/media/radio/si4713/si4713.c | 2 +- drivers/media/radio/tef6862.c | 4 +- .../media/test-drivers/vidtv/vidtv_demod.c | 4 +- .../media/test-drivers/vidtv/vidtv_tuner.c | 4 +- drivers/media/tuners/e4000.c | 4 +- drivers/media/tuners/fc2580.c | 4 +- drivers/media/tuners/m88rs6000t.c | 4 +- drivers/media/tuners/mt2060.c | 4 +- drivers/media/tuners/mxl301rf.c | 4 +- drivers/media/tuners/qm1d1b0004.c | 4 +- drivers/media/tuners/qm1d1c0042.c | 4 +- drivers/media/tuners/si2157.c | 10 ++--- drivers/media/tuners/tda18212.c | 4 +- drivers/media/tuners/tda18250.c | 4 +- drivers/media/tuners/tua9001.c | 4 +- drivers/media/usb/go7007/s2250-board.c | 2 +- drivers/media/v4l2-core/tuner-core.c | 2 +- 133 files changed, 268 insertions(+), 268 deletions(-) diff --git a/drivers/media/cec/i2c/tda9950.c b/drivers/media/cec/i2c/tda9950.c index cbff851e0c85..2bece4e63687 100644 --- a/drivers/media/cec/i2c/tda9950.c +++ b/drivers/media/cec/i2c/tda9950.c @@ -486,7 +486,7 @@ static void tda9950_remove(struct i2c_client *client) } static struct i2c_device_id tda9950_ids[] = { - { "tda9950" }, + { .name = "tda9950" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9950_ids); diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 7c0963054a8f..52e3dc928327 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -256,8 +256,8 @@ static void a8293_remove(struct i2c_client *client) } static const struct i2c_device_id a8293_id_table[] = { - { "a8293" }, - {} + { .name = "a8293" }, + { } }; MODULE_DEVICE_TABLE(i2c, a8293_id_table); diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 75f3063c193e..d335bfd18e44 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1553,8 +1553,8 @@ static void af9013_remove(struct i2c_client *client) } static const struct i2c_device_id af9013_id_table[] = { - { "af9013" }, - {} + { .name = "af9013" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9013_id_table); diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 9a3a4d6513dd..01761861b01f 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1173,8 +1173,8 @@ static void af9033_remove(struct i2c_client *client) } static const struct i2c_device_id af9033_id_table[] = { - { "af9033" }, - {} + { .name = "af9033" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9033_id_table); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 58b959b272c6..9ae47ea3976b 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -768,8 +768,8 @@ static void au8522_remove(struct i2c_client *client) } static const struct i2c_device_id au8522_id[] = { - { "au8522" }, - {} + { .name = "au8522" }, + { } }; MODULE_DEVICE_TABLE(i2c, au8522_id); diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index f95950a61307..e6110af1edcd 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -672,8 +672,8 @@ static void cxd2099_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2099_id[] = { - { "cxd2099" }, - {} + { .name = "cxd2099" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2099_id); diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 3deefeff4bd1..fbbffe9e7251 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -723,8 +723,8 @@ static void cxd2820r_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2820r_id_table[] = { - { "cxd2820r" }, - {} + { .name = "cxd2820r" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index f8fd23f60e3f..d36c163b772f 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -911,28 +911,28 @@ static void dvb_pll_remove(struct i2c_client *client) static const struct i2c_device_id dvb_pll_id[] = { - {"dtt7579", DVB_PLL_THOMSON_DTT7579}, - {"dtt759x", DVB_PLL_THOMSON_DTT759X}, - {"z201", DVB_PLL_LG_Z201}, - {"unknown_1", DVB_PLL_UNKNOWN_1}, - {"tua6010xs", DVB_PLL_TUA6010XS}, - {"env57h1xd5", DVB_PLL_ENV57H1XD5}, - {"tua6034", DVB_PLL_TUA6034}, - {"tda665x", DVB_PLL_TDA665X}, - {"tded4", DVB_PLL_TDED4}, - {"tdhu2", DVB_PLL_TDHU2}, - {"tbmv", DVB_PLL_SAMSUNG_TBMV}, - {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261}, - {"opera1", DVB_PLL_OPERA1}, - {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A}, - {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0}, - {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132}, - {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112}, - {"tdee4", DVB_PLL_TDEE4}, - {"dtt7520x", DVB_PLL_THOMSON_DTT7520X}, - {"tua6034_friio", DVB_PLL_TUA6034_FRIIO}, - {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1}, - {} + { .name = "dtt7579", .driver_data = DVB_PLL_THOMSON_DTT7579 }, + { .name = "dtt759x", .driver_data = DVB_PLL_THOMSON_DTT759X }, + { .name = "z201", .driver_data = DVB_PLL_LG_Z201 }, + { .name = "unknown_1", .driver_data = DVB_PLL_UNKNOWN_1 }, + { .name = "tua6010xs", .driver_data = DVB_PLL_TUA6010XS }, + { .name = "env57h1xd5", .driver_data = DVB_PLL_ENV57H1XD5 }, + { .name = "tua6034", .driver_data = DVB_PLL_TUA6034 }, + { .name = "tda665x", .driver_data = DVB_PLL_TDA665X }, + { .name = "tded4", .driver_data = DVB_PLL_TDED4 }, + { .name = "tdhu2", .driver_data = DVB_PLL_TDHU2 }, + { .name = "tbmv", .driver_data = DVB_PLL_SAMSUNG_TBMV }, + { .name = "sd1878_tda8261", .driver_data = DVB_PLL_PHILIPS_SD1878_TDA8261 }, + { .name = "opera1", .driver_data = DVB_PLL_OPERA1 }, + { .name = "dtos403ih102a", .driver_data = DVB_PLL_SAMSUNG_DTOS403IH102A }, + { .name = "tdtc9251dh0", .driver_data = DVB_PLL_SAMSUNG_TDTC9251DH0 }, + { .name = "tbdu18132", .driver_data = DVB_PLL_SAMSUNG_TBDU18132 }, + { .name = "tbmu24112", .driver_data = DVB_PLL_SAMSUNG_TBMU24112 }, + { .name = "tdee4", .driver_data = DVB_PLL_TDEE4 }, + { .name = "dtt7520x", .driver_data = DVB_PLL_THOMSON_DTT7520X }, + { .name = "tua6034_friio", .driver_data = DVB_PLL_TUA6034_FRIIO }, + { .name = "tda665x_earthpt1", .driver_data = DVB_PLL_TDA665X_EARTH_PT1 }, + { } }; diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 1402d124544e..993280fefc2c 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -1101,8 +1101,8 @@ static int helene_probe(struct i2c_client *client) } static const struct i2c_device_id helene_id[] = { - { "helene", }, - {} + { .name = "helene" }, + { } }; MODULE_DEVICE_TABLE(i2c, helene_id); diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index b6a66e122ed5..f4a3136baea3 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2244,8 +2244,8 @@ static void lgdt3306a_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt3306a_id_table[] = { - { "lgdt3306a" }, - {} + { .name = "lgdt3306a" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 19a4a05b3499..d90f642e9237 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -983,8 +983,8 @@ static void lgdt330x_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt330x_id_table[] = { - { "lgdt330x" }, - {} + { .name = "lgdt330x" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 44bee1f3c5e9..d79b88848a8c 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -2221,11 +2221,11 @@ static void m88ds3103_remove(struct i2c_client *client) } static const struct i2c_device_id m88ds3103_id_table[] = { - {"m88ds3103", M88DS3103_CHIPTYPE_3103}, - {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, - {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, - {"m88ds3103c", M88DS3103_CHIPTYPE_3103C}, - {} + { .name = "m88ds3103", .driver_data = M88DS3103_CHIPTYPE_3103 }, + { .name = "m88rs6000", .driver_data = M88DS3103_CHIPTYPE_RS6000 }, + { .name = "m88ds3103b", .driver_data = M88DS3103_CHIPTYPE_3103B }, + { .name = "m88ds3103c", .driver_data = M88DS3103_CHIPTYPE_3103C }, + { } }; MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c index 818c4e67364c..cc6bd17daf73 100644 --- a/drivers/media/dvb-frontends/mn88443x.c +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -787,10 +787,10 @@ static const struct of_device_id mn88443x_of_match[] = { MODULE_DEVICE_TABLE(of, mn88443x_of_match); static const struct i2c_device_id mn88443x_i2c_id[] = { - { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri }, - { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri }, - { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec }, - {} + { .name = "mn884433", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-0", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-1", .driver_data = (kernel_ulong_t)&mn88443x_spec_sec }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 275c404ce286..42b78b9aba20 100644 --- a/drivers/media/dvb-frontends/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -708,8 +708,8 @@ static void mn88472_remove(struct i2c_client *client) } static const struct i2c_device_id mn88472_id_table[] = { - { "mn88472" }, - {} + { .name = "mn88472" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88472_id_table); diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 40a0cb1d9c67..b7b11a000969 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -743,8 +743,8 @@ static void mn88473_remove(struct i2c_client *client) } static const struct i2c_device_id mn88473_id_table[] = { - { "mn88473" }, - {} + { .name = "mn88473" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88473_id_table); diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c index 2d8eaa20723f..bca4a4c3dd66 100644 --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1346,8 +1346,8 @@ static void mxl692_remove(struct i2c_client *client) } static const struct i2c_device_id mxl692_id_table[] = { - { "mxl692" }, - {} + { .name = "mxl692" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl692_id_table); diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index f0ee7c38ee79..4c44e2d695f8 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -876,8 +876,8 @@ static void rtl2830_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2830_id_table[] = { - { "rtl2830" }, - {} + { .name = "rtl2830" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 9898f729304a..b31cdb44b383 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1125,8 +1125,8 @@ static void rtl2832_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2832_id_table[] = { - { "rtl2832" }, - {} + { .name = "rtl2832" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 4170696af9f0..f1241b63aa5c 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1281,8 +1281,8 @@ static void si2165_remove(struct i2c_client *client) } static const struct i2c_device_id si2165_id_table[] = { - { "si2165" }, - {} + { .name = "si2165" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2165_id_table); diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 9c5bac8cda47..8bc3b6eb1dd3 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -790,8 +790,8 @@ static void si2168_remove(struct i2c_client *client) } static const struct i2c_device_id si2168_id_table[] = { - { "si2168" }, - {} + { .name = "si2168" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2168_id_table); diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 071c7371c699..63dec4b4fd2f 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -407,8 +407,8 @@ static void sp2_remove(struct i2c_client *client) } static const struct i2c_device_id sp2_id[] = { - { "sp2" }, - {} + { .name = "sp2" }, + { } }; MODULE_DEVICE_TABLE(i2c, sp2_id); diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 657df713865e..932bbed5497a 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -5079,8 +5079,8 @@ struct dvb_frontend *stv090x_attach(struct stv090x_config *config, EXPORT_SYMBOL_GPL(stv090x_attach); static const struct i2c_device_id stv090x_id_table[] = { - { "stv090x" }, - {} + { .name = "stv090x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv090x_id_table); diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index 7506f39ead55..5075333d7ffc 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -470,8 +470,8 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(stv6110x_attach); static const struct i2c_device_id stv6110x_id_table[] = { - { "stv6110x" }, - {} + { .name = "stv6110x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index f1343ba67b97..9a5ffb5f70fc 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -830,9 +830,9 @@ static void tc90522_remove(struct i2c_client *client) static const struct i2c_device_id tc90522_id[] = { - { TC90522_I2C_DEV_SAT, 0 }, - { TC90522_I2C_DEV_TER, 1 }, - {} + { .name = TC90522_I2C_DEV_SAT, .driver_data = 0 }, + { .name = TC90522_I2C_DEV_TER, .driver_data = 1 }, + { } }; MODULE_DEVICE_TABLE(i2c, tc90522_id); diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 6e6c2e5c427e..7a635c86b86c 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1230,8 +1230,8 @@ static void tda10071_remove(struct i2c_client *client) } static const struct i2c_device_id tda10071_id_table[] = { - { "tda10071_cx24118" }, - {} + { .name = "tda10071_cx24118" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda10071_id_table); diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 8b6006635e49..8775083f4dd6 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -716,9 +716,9 @@ static void ts2020_remove(struct i2c_client *client) } static const struct i2c_device_id ts2020_id_table[] = { - { "ts2020" }, - { "ts2022" }, - {} + { .name = "ts2020" }, + { .name = "ts2022" }, + { } }; MODULE_DEVICE_TABLE(i2c, ts2020_id_table); diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index f60271082fb5..fcce2857b69e 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -347,8 +347,8 @@ static void ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { "ad5820" }, - { "ad5821" }, + { .name = "ad5820" }, + { .name = "ad5821" }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 391bc75bfcd0..177a6e8d7fb8 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -522,7 +522,7 @@ static void adp1653_remove(struct i2c_client *client) } static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME }, + { .name = ADP1653_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, adp1653_id_table); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index ef8682b980b4..812998729207 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -377,8 +377,8 @@ static void adv7170_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7170_id[] = { - { "adv7170" }, - { "adv7171" }, + { .name = "adv7170" }, + { .name = "adv7171" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7170_id); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index 384da1ec5bf9..f1caab8e2abd 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -432,8 +432,8 @@ static void adv7175_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7175_id[] = { - { "adv7175" }, - { "adv7176" }, + { .name = "adv7175" }, + { .name = "adv7176" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7175_id); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 669b0b3165b1..e5d11a6e6766 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1626,18 +1626,18 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #endif static const struct i2c_device_id adv7180_id[] = { - { "adv7180", (kernel_ulong_t)&adv7180_info }, - { "adv7180cp", (kernel_ulong_t)&adv7180_info }, - { "adv7180st", (kernel_ulong_t)&adv7180_info }, - { "adv7182", (kernel_ulong_t)&adv7182_info }, - { "adv7280", (kernel_ulong_t)&adv7280_info }, - { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, - { "adv7281", (kernel_ulong_t)&adv7281_info }, - { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, - { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, - { "adv7282", (kernel_ulong_t)&adv7282_info }, - { "adv7282-m", (kernel_ulong_t)&adv7282_m_info }, - {} + { .name = "adv7180", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180cp", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180st", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7182", .driver_data = (kernel_ulong_t)&adv7182_info }, + { .name = "adv7280", .driver_data = (kernel_ulong_t)&adv7280_info }, + { .name = "adv7280-m", .driver_data = (kernel_ulong_t)&adv7280_m_info }, + { .name = "adv7281", .driver_data = (kernel_ulong_t)&adv7281_info }, + { .name = "adv7281-m", .driver_data = (kernel_ulong_t)&adv7281_m_info }, + { .name = "adv7281-ma", .driver_data = (kernel_ulong_t)&adv7281_ma_info }, + { .name = "adv7282", .driver_data = (kernel_ulong_t)&adv7282_info }, + { .name = "adv7282-m", .driver_data = (kernel_ulong_t)&adv7282_m_info }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7180_id); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 25a31a6dd456..a04a1a205fe0 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -619,8 +619,8 @@ static void adv7183_remove(struct i2c_client *client) } static const struct i2c_device_id adv7183_id[] = { - { "adv7183" }, - {} + { .name = "adv7183" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7183_id); diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index b96443404a26..9b91d7073d3c 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -502,8 +502,8 @@ static void adv7343_remove(struct i2c_client *client) } static const struct i2c_device_id adv7343_id[] = { - { "adv7343" }, - {} + { .name = "adv7343" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7343_id); diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index c7994bd0bbd4..6f948ba02f86 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -446,8 +446,8 @@ static void adv7393_remove(struct i2c_client *client) } static const struct i2c_device_id adv7393_id[] = { - { "adv7393" }, - {} + { .name = "adv7393" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7393_id); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 853c7806de92..860cff50c522 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -2008,7 +2008,7 @@ static void adv7511_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7511_id[] = { - { "adv7511-v4l2" }, + { .name = "adv7511-v4l2" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_id); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ae75982fb514..ac9c69ce438f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3236,10 +3236,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { }; static const struct i2c_device_id adv76xx_i2c_id[] = { - { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, - { "adv7610", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, + { .name = "adv7604", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, + { .name = "adv7610", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7611", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7612", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ea6966c0605e..3cfae89ce944 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3675,7 +3675,7 @@ static void adv7842_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7842_id[] = { - { "adv7842" }, + { .name = "adv7842" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7842_id); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ee575d01a676..cea46f01997d 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -304,8 +304,8 @@ static void ak881x_remove(struct i2c_client *client) } static const struct i2c_device_id ak881x_id[] = { - { "ak8813" }, - { "ak8814" }, + { .name = "ak8813" }, + { .name = "ak8814" }, { } }; MODULE_DEVICE_TABLE(i2c, ak881x_id); diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index f97245f91f88..da44100062fa 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -457,9 +457,9 @@ static void bt819_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id bt819_id[] = { - { "bt819a" }, - { "bt817a" }, - { "bt815a" }, + { .name = "bt819a" }, + { .name = "bt817a" }, + { .name = "bt815a" }, { } }; MODULE_DEVICE_TABLE(i2c, bt819_id); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 6852aa47cafb..656719158bb4 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -230,7 +230,7 @@ static void bt856_remove(struct i2c_client *client) } static const struct i2c_device_id bt856_id[] = { - { "bt856" }, + { .name = "bt856" }, { } }; MODULE_DEVICE_TABLE(i2c, bt856_id); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index a2cc34d35ed2..f5104c7d9f90 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -197,7 +197,7 @@ static void bt866_remove(struct i2c_client *client) } static const struct i2c_device_id bt866_id[] = { - { "bt866" }, + { .name = "bt866" }, { } }; MODULE_DEVICE_TABLE(i2c, bt866_id); diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index 3f2f993e16a6..ad37aeecf05e 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -109,7 +109,7 @@ static void cs3308_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs3308_id[] = { - { "cs3308" }, + { .name = "cs3308" }, { } }; MODULE_DEVICE_TABLE(i2c, cs3308_id); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 3a9797a50e82..49b8020fbea2 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -189,7 +189,7 @@ static void cs5345_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs5345_id[] = { - { "cs5345" }, + { .name = "cs5345" }, { } }; MODULE_DEVICE_TABLE(i2c, cs5345_id); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index c4cad3293905..a894dcf6b5a9 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -200,7 +200,7 @@ static void cs53l32a_remove(struct i2c_client *client) } static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a" }, + { .name = "cs53l32a" }, { } }; MODULE_DEVICE_TABLE(i2c, cs53l32a_id); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 69d5cc648c0f..8110d40931d9 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -3989,7 +3989,7 @@ static void cx25840_remove(struct i2c_client *client) } static const struct i2c_device_id cx25840_id[] = { - { "cx25840" }, + { .name = "cx25840" }, { } }; MODULE_DEVICE_TABLE(i2c, cx25840_id); diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 49aa5f4a172c..6abb5e324ae1 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -872,8 +872,8 @@ static void ub913_remove(struct i2c_client *client) } static const struct i2c_device_id ub913_id[] = { - { "ds90ub913a-q1" }, - {} + { .name = "ds90ub913a-q1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ub913_id); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index a8ab67f4137f..d4228e1134ff 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -1347,9 +1347,9 @@ static const struct ub953_hw_data ds90ub971_hw = { }; static const struct i2c_device_id ub953_id[] = { - { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw }, - { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw }, - {} + { .name = "ds90ub953-q1", .driver_data = (kernel_ulong_t)&ds90ub953_hw }, + { .name = "ds90ub971-q1", .driver_data = (kernel_ulong_t)&ds90ub971_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub953_id); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index d50e977cf6ce..15a9797b47ac 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -5266,10 +5266,10 @@ static const struct ub960_hw_data ds90ub9702_hw = { }; static const struct i2c_device_id ub960_id[] = { - { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw }, - { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, - { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, - {} + { .name = "ds90ub954-q1", .driver_data = (kernel_ulong_t)&ds90ub954_hw }, + { .name = "ds90ub960-q1", .driver_data = (kernel_ulong_t)&ds90ub960_hw }, + { .name = "ds90ub9702-q1", .driver_data = (kernel_ulong_t)&ds90ub9702_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub960_id); diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 3288de539452..1d686b1d2fd7 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -307,7 +307,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) } static const struct i2c_device_id dw9714_id_table[] = { - { DW9714_NAME }, + { .name = DW9714_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, dw9714_id_table); diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 50121c3e5b48..738e2801016a 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1485,7 +1485,7 @@ static const struct of_device_id et8ek8_of_table[] = { MODULE_DEVICE_TABLE(of, et8ek8_of_table); static const struct i2c_device_id et8ek8_id_table[] = { - { ET8EK8_NAME }, + { .name = ET8EK8_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 241821572e03..882dc2e9dd8f 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1913,7 +1913,7 @@ static const struct of_device_id imx274_of_id_table[] = { MODULE_DEVICE_TABLE(of, imx274_of_id_table); static const struct i2c_device_id imx274_id[] = { - { "IMX274" }, + { .name = "IMX274" }, { } }; MODULE_DEVICE_TABLE(i2c, imx274_id); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 604745317004..f2bf2b354000 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -978,10 +978,10 @@ static void ir_remove(struct i2c_client *client) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ - { "ir_video", 0 }, + { .name = "ir_video", .driver_data = 0 }, /* IR device specific entries should be added here */ - { "ir_z8f0811_haup", FLAG_TX }, - { "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR }, + { .name = "ir_z8f0811_haup", .driver_data = FLAG_TX }, + { .name = "ir_z8f0811_hdpvr", .driver_data = FLAG_TX | FLAG_HDPVR }, { } }; MODULE_DEVICE_TABLE(i2c, ir_kbd_id); diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 5ffd53e005ee..a77538d2343c 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1561,7 +1561,7 @@ static const struct of_device_id isl7998x_of_match[] = { MODULE_DEVICE_TABLE(of, isl7998x_of_match); static const struct i2c_device_id isl7998x_id[] = { - { "isl79987" }, + { .name = "isl79987" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, isl7998x_id); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index f3fba9179684..8c66be38adc3 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -677,9 +677,9 @@ static void ks0127_remove(struct i2c_client *client) } static const struct i2c_device_id ks0127_id[] = { - { "ks0127" }, - { "ks0127b" }, - { "ks0122s" }, + { .name = "ks0127" }, + { .name = "ks0127b" }, + { .name = "ks0122s" }, { } }; MODULE_DEVICE_TABLE(i2c, ks0127_id); diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 46d316a26e50..c3c90d830ee2 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -704,8 +704,8 @@ static const struct of_device_id lm3560_of_match[] = { MODULE_DEVICE_TABLE(of, lm3560_of_match); static const struct i2c_device_id lm3560_id_table[] = { - { "lm3559", (kernel_ulong_t)&lm3559_config }, - { "lm3560", (kernel_ulong_t)&lm3560_config }, + { .name = "lm3559", .driver_data = (kernel_ulong_t)&lm3559_config }, + { .name = "lm3560", .driver_data = (kernel_ulong_t)&lm3560_config }, { } }; MODULE_DEVICE_TABLE(i2c, lm3560_id_table); diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 2d16e42ec224..9030cbe32afc 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -386,8 +386,8 @@ static void lm3646_remove(struct i2c_client *client) } static const struct i2c_device_id lm3646_id_table[] = { - { LM3646_NAME }, - {} + { .name = LM3646_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3646_id_table); diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 9e1ecfd01e2a..f3adf8c2b27f 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -163,7 +163,7 @@ static void m52790_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id m52790_id[] = { - { "m52790" }, + { .name = "m52790" }, { } }; MODULE_DEVICE_TABLE(i2c, m52790_id); diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index bf02ca23a284..1cc388b52902 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1413,8 +1413,8 @@ static void max2175_remove(struct i2c_client *client) } static const struct i2c_device_id max2175_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, max2175_id); diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 57ba3693649a..48b7d589df31 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -424,8 +424,8 @@ static void ml86v7667_remove(struct i2c_client *client) } static const struct i2c_device_id ml86v7667_id[] = { - { DRV_NAME }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, ml86v7667_id); diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 4c0b0ad68c08..413cfbc2dd94 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -874,7 +874,7 @@ static const struct dev_pm_ops msp3400_pm_ops = { }; static const struct i2c_device_id msp_id[] = { - { "msp3400" }, + { .name = "msp3400" }, { } }; MODULE_DEVICE_TABLE(i2c, msp_id); diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 7a6114d18dfc..0ade967b357b 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -855,7 +855,7 @@ static void mt9m001_remove(struct i2c_client *client) } static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001" }, + { .name = "mt9m001" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 3532c7c38bec..4e748080b798 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1384,7 +1384,7 @@ static const struct of_device_id mt9m111_of_match[] = { MODULE_DEVICE_TABLE(of, mt9m111_of_match); static const struct i2c_device_id mt9m111_id[] = { - { "mt9m111" }, + { .name = "mt9m111" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m111_id); diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 2d2c840fc002..bd2268154ca7 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1108,7 +1108,7 @@ static void mt9t112_remove(struct i2c_client *client) } static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112" }, + { .name = "mt9t112" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9t112_id); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 055b7915260a..985517f1cff7 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -582,7 +582,7 @@ static void mt9v011_remove(struct i2c_client *c) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011" }, + { .name = "mt9v011" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v011_id); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 162b49046990..09d58e8b1c7f 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1747,8 +1747,8 @@ static void ov13858_remove(struct i2c_client *client) } static const struct i2c_device_id ov13858_id_table[] = { - { "ov13858" }, - {} + { .name = "ov13858" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov13858_id_table); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index d27fc2df64e6..50feb608b92b 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1271,7 +1271,7 @@ static void ov2640_remove(struct i2c_client *client) } static const struct i2c_device_id ov2640_id[] = { - { "ov2640" }, + { .name = "ov2640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov2640_id); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 061401b020fc..7d8c7c3465a4 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1553,7 +1553,7 @@ static const struct dev_pm_ops ov2659_pm_ops = { }; static const struct i2c_device_id ov2659_id[] = { - { "ov2659" }, + { .name = "ov2659" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov2659_id); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 85ecc23b3587..92d2d6cd4ba4 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -3999,8 +3999,8 @@ static const struct dev_pm_ops ov5640_pm_ops = { }; static const struct i2c_device_id ov5640_id[] = { - { "ov5640" }, - {} + { .name = "ov5640" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5640_id); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index b10d408034a1..c772ef6e51d2 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -1219,8 +1219,8 @@ static void ov5645_remove(struct i2c_client *client) } static const struct i2c_device_id ov5645_id[] = { - { "ov5645" }, - {} + { .name = "ov5645" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5645_id); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index db9bd2892140..3facf92b3841 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1278,7 +1278,7 @@ static const struct dev_pm_ops ov5647_pm_ops = { }; static const struct i2c_device_id ov5647_id[] = { - { "ov5647" }, + { .name = "ov5647" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index 9f68d89936eb..0fd90bc67e29 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -77,7 +77,7 @@ static void ov7640_remove(struct i2c_client *client) } static const struct i2c_device_id ov7640_id[] = { - { "ov7640" }, + { .name = "ov7640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov7640_id); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 0cb96b6c9990..b6d238ba0d53 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1997,8 +1997,8 @@ static const struct ov7670_devtype ov7675_devdata = { }; static const struct i2c_device_id ov7670_id[] = { - { "ov7670", (kernel_ulong_t)&ov7670_devdata }, - { "ov7675", (kernel_ulong_t)&ov7675_devdata }, + { .name = "ov7670", .driver_data = (kernel_ulong_t)&ov7670_devdata }, + { .name = "ov7675", .driver_data = (kernel_ulong_t)&ov7675_devdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7670_id); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 062e1023a411..be3ba284ee0b 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1546,7 +1546,7 @@ static void ov772x_remove(struct i2c_client *client) } static const struct i2c_device_id ov772x_id[] = { - { "ov772x" }, + { .name = "ov772x" }, { } }; MODULE_DEVICE_TABLE(i2c, ov772x_id); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 632fb80469be..c2e02f191816 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1149,7 +1149,7 @@ static int __maybe_unused ov7740_runtime_resume(struct device *dev) } static const struct i2c_device_id ov7740_id[] = { - { "ov7740" }, + { .name = "ov7740" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7740_id); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 2190c52b1433..122f411044ce 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -752,7 +752,7 @@ static void ov9640_remove(struct i2c_client *client) } static const struct i2c_device_id ov9640_id[] = { - { "ov9640" }, + { .name = "ov9640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov9640_id); diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index c94e8fe29f22..5c85db8a4a38 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1567,8 +1567,8 @@ static void ov965x_remove(struct i2c_client *client) } static const struct i2c_device_id ov965x_id[] = { - { "OV9650" }, - { "OV9652" }, + { .name = "OV9650" }, + { .name = "OV9652" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov965x_id); diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index e95342d706c3..23352d71a108 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1413,7 +1413,7 @@ static void rj54n1_remove(struct i2c_client *client) } static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c" }, + { .name = "rj54n1cb0c" }, { } }; MODULE_DEVICE_TABLE(i2c, rj54n1_id); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ab31ee2b596b..551387cea521 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1728,7 +1728,7 @@ static void s5c73m3_remove(struct i2c_client *client) } static const struct i2c_device_id s5c73m3_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5c73m3_id); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index d1d00eca8708..378d273055ee 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -2007,7 +2007,7 @@ static void s5k5baf_remove(struct i2c_client *c) } static const struct i2c_device_id s5k5baf_id[] = { - { S5K5BAF_DRIVER_NAME }, + { .name = S5K5BAF_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5k5baf_id); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 90ae4121a68a..56bfa3d2604b 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -495,7 +495,7 @@ static void saa6588_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa6588_id[] = { - { "saa6588" }, + { .name = "saa6588" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6588_id); diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 1c0031ba43b4..c6bf0b0902e8 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -770,7 +770,7 @@ static void saa6752hs_remove(struct i2c_client *client) } static const struct i2c_device_id saa6752hs_id[] = { - { "saa6752hs" }, + { .name = "saa6752hs" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6752hs_id); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 942aeeb40c52..652058b8f766 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -439,7 +439,7 @@ static void saa7110_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7110_id[] = { - { "saa7110" }, + { .name = "saa7110" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7110_id); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 48d6730d9271..7cce90750c93 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1928,13 +1928,13 @@ static void saa711x_remove(struct i2c_client *client) } static const struct i2c_device_id saa711x_id[] = { - { "saa7115_auto", 1 }, /* autodetect */ - { "saa7111", 0 }, - { "saa7113", 0 }, - { "saa7114", 0 }, - { "saa7115", 0 }, - { "saa7118", 0 }, - { "gm7113c", 0 }, + { .name = "saa7115_auto", .driver_data = 1 }, /* autodetect */ + { .name = "saa7111", .driver_data = 0 }, + { .name = "saa7113", .driver_data = 0 }, + { .name = "saa7114", .driver_data = 0 }, + { .name = "saa7115", .driver_data = 0 }, + { .name = "saa7118", .driver_data = 0 }, + { .name = "gm7113c", .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(i2c, saa711x_id); diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index a42a7ffe3768..fdf17f6fae67 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -797,11 +797,11 @@ static void saa7127_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7127_id[] = { - { "saa7127_auto", 0 }, /* auto-detection */ - { "saa7126", SAA7127 }, - { "saa7127", SAA7127 }, - { "saa7128", SAA7129 }, - { "saa7129", SAA7129 }, + { .name = "saa7127_auto", .driver_data = 0 }, /* auto-detection */ + { .name = "saa7126", .driver_data = SAA7127 }, + { .name = "saa7127", .driver_data = SAA7127 }, + { .name = "saa7128", .driver_data = SAA7129 }, + { .name = "saa7129", .driver_data = SAA7129 }, { } }; MODULE_DEVICE_TABLE(i2c, saa7127_id); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 713331be947c..0536ceb54650 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1334,7 +1334,7 @@ static void saa717x_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa717x_id[] = { - { "saa717x" }, + { .name = "saa717x" }, { } }; MODULE_DEVICE_TABLE(i2c, saa717x_id); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index c04e452a332b..8e5c0eab907d 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -334,7 +334,7 @@ static void saa7185_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7185_id[] = { - { "saa7185" }, + { .name = "saa7185" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7185_id); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 16072a9f8247..4c87de0fe1f0 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -366,7 +366,7 @@ static void sony_btf_mpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id sony_btf_mpx_id[] = { - { "sony-btf-mpx" }, + { .name = "sony-btf-mpx" }, { } }; MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a0ca19359c43..fbd38bbfee03 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2358,8 +2358,8 @@ static void tc358743_remove(struct i2c_client *client) } static const struct i2c_device_id tc358743_id[] = { - { "tc358743" }, - {} + { .name = "tc358743" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc358743_id); diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 5c6dda5338f5..afa1d6f34c9c 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2274,9 +2274,9 @@ static int tda1997x_set_power(struct tda1997x_state *state, bool on) } static const struct i2c_device_id tda1997x_i2c_id[] = { - {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, - {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, - { }, + { .name = "tda19971", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19971] }, + { .name = "tda19973", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19973] }, + { } }; MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 76ef0fdddf76..a0b65a595ba8 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -400,7 +400,7 @@ static void tda7432_remove(struct i2c_client *client) } static const struct i2c_device_id tda7432_id[] = { - { "tda7432" }, + { .name = "tda7432" }, { } }; MODULE_DEVICE_TABLE(i2c, tda7432_id); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index e3b266db571f..b34d992777fb 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -182,7 +182,7 @@ static void tda9840_remove(struct i2c_client *client) } static const struct i2c_device_id tda9840_id[] = { - { "tda9840" }, + { .name = "tda9840" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9840_id); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 0cd2e6c52e20..ea7730a7ee2c 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -141,7 +141,7 @@ static void tea6415c_remove(struct i2c_client *client) } static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c" }, + { .name = "tea6415c" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6415c_id); diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 400883fc0c0f..bebb9a8095db 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -123,7 +123,7 @@ static void tea6420_remove(struct i2c_client *client) } static const struct i2c_device_id tea6420_id[] = { - { "tea6420" }, + { .name = "tea6420" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6420_id); diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index ff268ebeb4d9..fa5b9c884c1d 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -369,9 +369,9 @@ static void ths7303_remove(struct i2c_client *client) } static const struct i2c_device_id ths7303_id[] = { - { "ths7303" }, - { "ths7353" }, - {} + { .name = "ths7303" }, + { .name = "ths7353" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths7303_id); diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 686f10641c7a..808ef16ec3b3 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -487,8 +487,8 @@ static void ths8200_remove(struct i2c_client *client) } static const struct i2c_device_id ths8200_id[] = { - { "ths8200" }, - {} + { .name = "ths8200" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths8200_id); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index 6f6bc5236565..7deeef317714 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -188,7 +188,7 @@ static void tlv320aic23b_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b" }, + { .name = "tlv320aic23b" }, { } }; MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 6267e9ad39c0..f136310b899d 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2086,7 +2086,7 @@ static void tvaudio_remove(struct i2c_client *client) detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio" }, + { .name = "tvaudio" }, { } }; MODULE_DEVICE_TABLE(i2c, tvaudio_id); diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 7af8f37646d6..376eecb0b673 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1129,10 +1129,10 @@ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (kernel_ulong_t)tvp5146_init_reg_seq }, - {"tvp5146m2", (kernel_ulong_t)tvp514xm_init_reg_seq }, - {"tvp5147", (kernel_ulong_t)tvp5147_init_reg_seq }, - {"tvp5147m1", (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5146", .driver_data = (kernel_ulong_t)tvp5146_init_reg_seq }, + { .name = "tvp5146m2", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5147", .driver_data = (kernel_ulong_t)tvp5147_init_reg_seq }, + { .name = "tvp5147m1", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e3675c744d9e..9c204f38935d 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2265,7 +2265,7 @@ static const struct dev_pm_ops tvp5150_pm_ops = { }; static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150" }, + { .name = "tvp5150" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp5150_id); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index c09a5bd71fd0..3979ccde5a95 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1070,7 +1070,7 @@ static void tvp7002_remove(struct i2c_client *c) /* I2C Device ID table */ static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002" }, + { .name = "tvp7002" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp7002_id); diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index 3d154f4fb5f9..713e078ff3da 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -414,7 +414,7 @@ static void tw2804_remove(struct i2c_client *client) } static const struct i2c_device_id tw2804_id[] = { - { "tw2804" }, + { .name = "tw2804" }, { } }; MODULE_DEVICE_TABLE(i2c, tw2804_id); diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c index 53efdeaed1db..617fcf7f0b45 100644 --- a/drivers/media/i2c/tw9900.c +++ b/drivers/media/i2c/tw9900.c @@ -753,7 +753,7 @@ static const struct dev_pm_ops tw9900_pm_ops = { }; static const struct i2c_device_id tw9900_id[] = { - { "tw9900" }, + { .name = "tw9900" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9900_id); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index c3eafd5d5dc8..db063b885b09 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -246,7 +246,7 @@ static void tw9903_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9903_id[] = { - { "tw9903" }, + { .name = "tw9903" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9903_id); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 0ab43fe42d7f..079e2f49f38f 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -214,7 +214,7 @@ static void tw9906_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9906_id[] = { - { "tw9906" }, + { .name = "tw9906" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9906_id); diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index f3e400304e04..872207e688bf 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -996,7 +996,7 @@ static void tw9910_remove(struct i2c_client *client) } static const struct i2c_device_id tw9910_id[] = { - { "tw9910" }, + { .name = "tw9910" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9910_id); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index 2e4540ee2df2..437726788ba0 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -79,7 +79,7 @@ static void uda1342_remove(struct i2c_client *client) } static const struct i2c_device_id uda1342_id[] = { - { "uda1342" }, + { .name = "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_id); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index a178af46e695..670118c16872 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -219,7 +219,7 @@ static void upd64031a_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64031a_id[] = { - { "upd64031a" }, + { .name = "upd64031a" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64031a_id); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 5421dc5e32c9..e610dffa9e10 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -190,7 +190,7 @@ static void upd64083_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64083_id[] = { - { "upd64083" }, + { .name = "upd64083" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64083_id); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 2b23914717d1..56b99eea54a1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -922,9 +922,9 @@ static const struct dev_pm_ops video_i2c_pm_ops = { }; static const struct i2c_device_id video_i2c_id_table[] = { - { "amg88xx", (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, - { "mlx90640", (kernel_ulong_t)&video_i2c_chip[MLX90640] }, - {} + { .name = "amg88xx", .driver_data = (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, + { .name = "mlx90640", .driver_data = (kernel_ulong_t)&video_i2c_chip[MLX90640] }, + { } }; MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index df21950be24f..21fcfdd4c163 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -172,7 +172,7 @@ static void vp27smpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id vp27smpx_id[] = { - { "vp27smpx" }, + { .name = "vp27smpx" }, { } }; MODULE_DEVICE_TABLE(i2c, vp27smpx_id); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 5f1a22284168..29bcbb5a5fbb 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -535,9 +535,9 @@ static void vpx3220_remove(struct i2c_client *client) } static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a" }, - { "vpx3216b" }, - { "vpx3214c" }, + { .name = "vpx3220a" }, + { .name = "vpx3216b" }, + { .name = "vpx3214c" }, { } }; MODULE_DEVICE_TABLE(i2c, vpx3220_id); diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 72eb10339d06..db62bafb447c 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -243,7 +243,7 @@ static void wm8739_remove(struct i2c_client *client) } static const struct i2c_device_id wm8739_id[] = { - { "wm8739" }, + { .name = "wm8739" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8739_id); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 56778d3bc28a..5db197bb6fda 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -289,7 +289,7 @@ static void wm8775_remove(struct i2c_client *client) } static const struct i2c_device_id wm8775_id[] = { - { "wm8775" }, + { .name = "wm8775" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8775_id); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 156cca2866aa..d9547f625e30 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -502,7 +502,7 @@ static void tea5764_i2c_remove(struct i2c_client *client) /* I2C subsystem interface */ static const struct i2c_device_id tea5764_id[] = { - { "radio-tea5764" }, + { .name = "radio-tea5764" }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(i2c, tea5764_id); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 9572a866defb..bd8bd295a9ec 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -395,8 +395,8 @@ static void saa7706h_remove(struct i2c_client *client) } static const struct i2c_device_id saa7706h_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, saa7706h_id); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 3932a449a1b1..a1e570af9c59 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,7 +28,7 @@ /* I2C Device ID List */ static const struct i2c_device_id si470x_i2c_id[] = { /* Generic Entry */ - { "si470x" }, + { .name = "si470x" }, /* Terminating entry */ { } }; diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index e71272c6de37..0c0354566b0a 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1639,7 +1639,7 @@ static void si4713_remove(struct i2c_client *client) /* si4713_i2c_driver - i2c driver interface */ static const struct i2c_device_id si4713_id[] = { - { "si4713" }, + { .name = "si4713" }, { } }; MODULE_DEVICE_TABLE(i2c, si4713_id); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 3a6d7926e856..2596d7f4f3f1 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -173,8 +173,8 @@ static void tef6862_remove(struct i2c_client *client) } static const struct i2c_device_id tef6862_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, tef6862_id); diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index c382e9e94c32..6e5fe402976b 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -407,8 +407,8 @@ static const struct dvb_frontend_ops vidtv_demod_ops = { }; static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { - { "dvb_vidtv_demod" }, - {} + { .name = "dvb_vidtv_demod" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index ee55df4029bc..bd50b86e927c 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -385,8 +385,8 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = { }; static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { - { "dvb_vidtv_tuner" }, - {} + { .name = "dvb_vidtv_tuner" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index b83f37a77224..94abb3715401 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -719,8 +719,8 @@ static void e4000_remove(struct i2c_client *client) } static const struct i2c_device_id e4000_id_table[] = { - { "e4000" }, - {} + { .name = "e4000" }, + { } }; MODULE_DEVICE_TABLE(i2c, e4000_id_table); diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 75087d9b224f..b76fa1320f5f 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -600,8 +600,8 @@ static void fc2580_remove(struct i2c_client *client) } static const struct i2c_device_id fc2580_id_table[] = { - { "fc2580" }, - {} + { .name = "fc2580" }, + { } }; MODULE_DEVICE_TABLE(i2c, fc2580_id_table); diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 0a724cdf0f6d..1addb3d229cf 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -709,8 +709,8 @@ static void m88rs6000t_remove(struct i2c_client *client) } static const struct i2c_device_id m88rs6000t_id[] = { - { "m88rs6000t" }, - {} + { .name = "m88rs6000t" }, + { } }; MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index ef3196e6bd30..c57233c7441a 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -514,8 +514,8 @@ static void mt2060_remove(struct i2c_client *client) } static const struct i2c_device_id mt2060_id_table[] = { - { "mt2060" }, - {} + { .name = "mt2060" }, + { } }; MODULE_DEVICE_TABLE(i2c, mt2060_id_table); diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index cfc78891ce03..1d84456cb19b 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -317,8 +317,8 @@ static void mxl301rf_remove(struct i2c_client *client) static const struct i2c_device_id mxl301rf_id[] = { - { "mxl301rf" }, - {} + { .name = "mxl301rf" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl301rf_id); diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index 07ad84f42c9f..59d98681e674 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -243,8 +243,8 @@ static void qm1d1b0004_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1b0004_id[] = { - { "qm1d1b0004" }, - {} + { .name = "qm1d1b0004" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id); diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index db60562ad698..2d19cfdb67b9 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -434,8 +434,8 @@ static void qm1d1c0042_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1c0042_id[] = { - { "qm1d1c0042" }, - {} + { .name = "qm1d1c0042" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id); diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4d67e347c22f..d517a91e6fbc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1097,11 +1097,11 @@ static void si2157_remove(struct i2c_client *client) * all SiLabs TER tuners, as the driver should auto-detect it. */ static const struct i2c_device_id si2157_id_table[] = { - {"si2157", SI2157}, - {"si2146", SI2146}, - {"si2141", SI2141}, - {"si2177", SI2177}, - {} + { .name = "si2157", .driver_data = SI2157 }, + { .name = "si2146", .driver_data = SI2146 }, + { .name = "si2141", .driver_data = SI2141 }, + { .name = "si2177", .driver_data = SI2177 }, + { } }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5f583c010408..18d1850691fb 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -254,8 +254,8 @@ static void tda18212_remove(struct i2c_client *client) } static const struct i2c_device_id tda18212_id[] = { - { "tda18212" }, - {} + { .name = "tda18212" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18212_id); diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index caaf5e0d0c9b..7bb945ba0989 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -868,8 +868,8 @@ static void tda18250_remove(struct i2c_client *client) } static const struct i2c_device_id tda18250_id_table[] = { - { "tda18250" }, - {} + { .name = "tda18250" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18250_id_table); diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index c0aed1b441e2..fcdbf1a0c1d8 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -245,8 +245,8 @@ static void tua9001_remove(struct i2c_client *client) } static const struct i2c_device_id tua9001_id_table[] = { - { "tua9001" }, - {} + { .name = "tua9001" }, + { } }; MODULE_DEVICE_TABLE(i2c, tua9001_id_table); diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 567f851d5896..0901d79e827d 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -611,7 +611,7 @@ static void s2250_remove(struct i2c_client *client) } static const struct i2c_device_id s2250_id[] = { - { "s2250" }, + { .name = "s2250" }, { } }; MODULE_DEVICE_TABLE(i2c, s2250_id); diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 004ec4d7beea..1e130e6f903f 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1401,7 +1401,7 @@ static const struct dev_pm_ops tuner_pm_ops = { }; static const struct i2c_device_id tuner_id[] = { - { "tuner", }, /* autodetect */ + { .name = "tuner" }, /* autodetect */ { } }; MODULE_DEVICE_TABLE(i2c, tuner_id); From 0812f34bf5dd3e1df495bf6f2c5fe4d140c10d37 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Sun, 22 Feb 2026 14:16:37 +0100 Subject: [PATCH 1174/5214] staging: media: atomisp: Fix spelling mistakes in comments Fix various spelling mistakes found by codespell: - aviod => avoid - corrent => correct - stablization => stabilization - addtional => additional - facor => factor - steams => streams Signed-off-by: Tomasz Unger Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 8 ++++---- drivers/staging/media/atomisp/pci/atomisp_cmd.h | 2 +- drivers/staging/media/atomisp/pci/atomisp_compat_css20.c | 8 ++++---- drivers/staging/media/atomisp/pci/atomisp_compat_css20.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index fec369575d88..5e5f3fe1ba7d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -811,7 +811,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, /* New global dvs 6axis config should be blocked * here if there's a buffer with per-frame parameters * pending in CSS frame buffer queue. - * This is to aviod zooming vibration since global + * This is to avoid zooming vibration since global * parameters take effect immediately while * per-frame parameters are taken after previous * buffers in CSS got processed. @@ -974,7 +974,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) * to a FIFO, then process the event in the FIFO. * This will not have issue in single stream mode, but it do have some * issue in multiple stream case. The issue is that - * ia_css_pipe_dequeue_buffer() will not return the corrent buffer in + * ia_css_pipe_dequeue_buffer() will not return the correct buffer in * a specific pipe. * * This is due to ia_css_pipe_dequeue_buffer() does not take the @@ -1575,7 +1575,7 @@ int atomisp_set_dis_vector(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) @@ -3232,7 +3232,7 @@ int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, } /* - * Function to enable/disable video image stablization + * Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index 82199dc9284e..d3d1f2574e77 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -153,7 +153,7 @@ int atomisp_bad_pixel(struct atomisp_sub_device *asd, int flag, int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, struct atomisp_dp_config *config); -/* Function to enable/disable video image stablization */ +/* Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value); diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index be5f37f4a6fd..c30501a36f86 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -1957,7 +1957,7 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, preview pipe could support bayer downscaling, yuv decimation and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -2044,7 +2044,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, } } /* - * calculate YUV Decimation, YUV downscaling facor: + * calculate YUV Decimation, YUV downscaling factor: * YUV Downscaling factor must not exceed 2. * YUV Decimation factor could be 2, 4. */ @@ -2085,7 +2085,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, offline video pipe could support bayer decimation, and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_video_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -3002,7 +3002,7 @@ int atomisp_css_get_zoom_factor(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h index 75781807544a..5188df4f469c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h @@ -40,7 +40,7 @@ enum atomisp_css_stream_state { }; /* - * Sensor of external ISP can send multiple steams with different mipi data + * Sensor of external ISP can send multiple streams with different mipi data * type in the same virtual channel. This information needs to come from the * sensor or external ISP */ From c565afd284e5b5f04e24b20018958ed7b9bc55a3 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Thu, 26 Feb 2026 12:16:53 +0100 Subject: [PATCH 1175/5214] staging: media: atomisp: replace sprintf() with strscpy() Auditing calls to sprintf(). This code is fine because we are copying 9 characters into a 52 character buffer. But it would be cleaner to use strscpy() instead. Additionally, the 2-argument version of strscpy() checks at compile time that dst is an array, not just a pointer. This is the only sprintf() call in the whole driver. Signed-off-by: Tomasz Unger Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_subdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 3d56ca83ecb7..cef44ec9ebde 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -808,7 +808,7 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) int ret; v4l2_subdev_init(sd, &isp_subdev_v4l2_ops); - sprintf(sd->name, "Atom ISP"); + strscpy(sd->name, "Atom ISP"); v4l2_set_subdevdata(sd, asd); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; From f3c450f5f13221798de7acbe667d55fbadc63b94 Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:23:58 +0000 Subject: [PATCH 1176/5214] staging: media: atomisp: Remove braces for single statement blocks Fix checkpatch.pl warnings: WARNING: braces {} are not necessary for single statement blocks WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../base/circbuf/interface/ia_css_circbuf.h | 3 +- .../circbuf/interface/ia_css_circbuf_desc.h | 3 +- .../atomisp/pci/base/refcount/src/refcount.c | 3 +- .../pci/camera/pipe/src/pipe_stagedesc.c | 9 ++---- .../atomisp/pci/camera/pipe/src/pipe_util.c | 3 +- .../host/isys_stream2mmio_private.h | 3 +- .../host/event_fifo_private.h | 3 +- .../host/input_formatter.c | 3 +- .../hive_isp_css_common/host/input_system.c | 14 ++++----- .../pci/hive_isp_css_common/host/irq.c | 30 ++++++++----------- .../pci/hive_isp_css_common/host/mmu.c | 3 +- .../pci/hive_isp_css_common/host/vmem.c | 4 +-- .../isp/kernels/anr/anr_2/ia_css_anr2.host.c | 3 +- .../pci/isp/kernels/bnlm/ia_css_bnlm.host.c | 6 ++-- .../isp/kernels/ctc/ctc2/ia_css_ctc2.host.c | 7 ++--- .../isp/kernels/eed1_8/ia_css_eed1_8.host.c | 13 ++++---- .../isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c | 3 +- .../kernels/sdis/sdis_1.0/ia_css_sdis.host.c | 6 ++-- .../atomisp/pci/runtime/binary/src/binary.c | 3 +- .../pci/runtime/debug/src/ia_css_debug.c | 6 ++-- .../pci/runtime/isys/src/virtual_isys.c | 3 +- .../pci/runtime/pipeline/src/pipeline.c | 3 +- .../staging/media/atomisp/pci/sh_css_params.c | 12 +++----- 23 files changed, 53 insertions(+), 93 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 86300991d30e..148f9f6febfb 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -146,9 +146,8 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( OP___assert(cb->desc->size > 0); /* step 1: adjudst the offset */ - while (offset < 0) { + while (offset < 0) offset += cb->desc->size; - } /* step 2: shift and round by the upper limit */ dest = OP_std_modadd(base, offset, cb->desc->size); diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 5645a7bf493c..3d89626a0733 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -84,9 +84,8 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( OP___assert(cb_desc->size > 0); /* step 1: adjust the offset */ - while (offset < 0) { + while (offset < 0) offset += cb_desc->size; - } /* step 2: shift and round by the upper limit */ dest = OP_std_modadd(base, offset, cb_desc->size); diff --git a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c index 58e4e3173b40..4a8675d0129a 100644 --- a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c +++ b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c @@ -238,9 +238,8 @@ void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr) hmm_free(entry->data); } - if (entry->count != 0) { + if (entry->count != 0) IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id); - } assert(entry->count == 0); diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c index a9f736398f50..fa2f8ed5b053 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c @@ -32,9 +32,8 @@ void ia_css_pipe_get_generic_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = binary->info->sp.pipeline.mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; ERR: IA_CSS_LEAVE_PRIVATE(""); @@ -59,9 +58,8 @@ void ia_css_pipe_get_firmwares_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; } @@ -82,8 +80,7 @@ void ia_css_pipe_get_sp_func_stage_desc( stage_desc->mode = (unsigned int)-1; stage_desc->in_frame = NULL; stage_desc->out_frame[0] = out_frame; - for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = NULL; - } stage_desc->vf_frame = NULL; } diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c index c7c42b472cc7..6cb3ecbd7297 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c @@ -26,9 +26,8 @@ void ia_css_pipe_util_create_output_frames( unsigned int i; assert(frames); - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) frames[i] = NULL; - } } void ia_css_pipe_util_set_output_frames( diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h index 3210dd6bf9ca..8e295cd78129 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h @@ -41,9 +41,8 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_state( * Get the values of the register-set per * stream2mmio-controller sids. */ - for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) { + for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) stream2mmio_get_sid_state(ID, i, &state->sid_state[i]); - } } /** diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h index 439c69444942..e77d7cf61356 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h @@ -26,9 +26,8 @@ STORAGE_CLASS_EVENT_C void event_wait_for(const event_ID_t ID) STORAGE_CLASS_EVENT_C void cnd_event_wait_for(const event_ID_t ID, const bool cnd) { - if (cnd) { + if (cnd) event_wait_for(ID); - } } STORAGE_CLASS_EVENT_C hrt_data event_receive_token(const event_ID_t ID) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c index 40b3f1e48c56..c353461dcdf2 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c @@ -62,9 +62,8 @@ void input_formatter_rst( * WICH USES THE STREAM2MEMRY BLOCK. * MUST BE FIXED PROPERLY */ - if (!HIVE_IF_BIN_COPY[ID]) { + if (!HIVE_IF_BIN_COPY[ID]) input_formatter_reg_store(ID, addr, rst); - } return; } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c index 9f1199c4761c..68b0ad27615d 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c @@ -138,11 +138,10 @@ void receiver_port_enable( hrt_data reg = receiver_port_reg_load(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX); - if (cnd) { + if (cnd) reg |= 0x01; - } else { + else reg &= ~0x01; - } receiver_port_reg_store(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX, reg); @@ -194,9 +193,8 @@ static void receiver_rst( assert(ID < N_RX_ID); // Disable all ports. - for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) { + for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) receiver_port_enable(ID, port_id, false); - } // AM: Additional actions for stopping receiver? } @@ -830,15 +828,13 @@ input_system_err_t input_system_configuration_commit(void) // The last configuration step is to configure the input buffer. input_system_err_t error = input_buffer_configuration(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into registers. error = configuration_to_registers(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into ctrl commands etc. diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c index b66560bca625..61dea386361a 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c @@ -55,9 +55,8 @@ void irq_clear_all( assert(ID < N_IRQ_ID); assert(IRQ_N_CHANNEL[ID] <= HRT_DATA_WIDTH); - if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) { + if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) mask = ~((~(hrt_data)0) >> IRQ_N_CHANNEL[ID]); - } irq_reg_store(ID, _HRT_IRQ_CONTROLLER_CLEAR_REG_IDX, mask); @@ -115,9 +114,9 @@ void irq_enable_pulse( { unsigned int edge_out = 0x0; - if (pulse) { + if (pulse) edge_out = 0xffffffff; - } + /* output is given as edge, not pulse */ irq_reg_store(ID, _HRT_IRQ_CONTROLLER_EDGE_NOT_PULSE_REG_IDX, edge_out); @@ -259,9 +258,9 @@ void virq_clear_all(void) { irq_ID_t irq_id; - for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) { + for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) irq_clear_all(irq_id); - } + return; } @@ -301,9 +300,9 @@ void virq_clear_info(struct virq_info *irq_info) assert(irq_info); - for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { + for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) irq_info->irq_status_reg[ID] = 0; - } + return; } @@ -324,20 +323,17 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[IRQ0_ID]) { + if (idx == IRQ_N_CHANNEL[IRQ0_ID]) return hrt_isp_css_irq_status_error; - } /* Check whether there are more bits set on device 0 */ - if (irq_status != (1U << idx)) { + if (irq_status != (1U << idx)) status = hrt_isp_css_irq_status_more_irqs; - } /* Check whether we have an IRQ on one of the nested devices */ for (ID = N_IRQ_ID - 1 ; ID > (irq_ID_t)0; ID--) { - if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) { + if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) break; - } } /* If we have a nested IRQ, load that state, discard the device 0 state */ @@ -350,9 +346,8 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[ID]) { + if (idx == IRQ_N_CHANNEL[ID]) return hrt_isp_css_irq_status_error; - } /* Alternatively check whether there are more bits set on this device */ if (irq_status != (1U << idx)) { @@ -408,9 +403,8 @@ static inline irq_ID_t virq_get_irq_id( assert(channel_ID); for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { - if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) { + if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) break; - } } *channel_ID = (unsigned int)irq_ID - IRQ_N_ID_OFFSET[ID]; diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c index 064e88a5e064..70d118fe1e70 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c @@ -32,7 +32,6 @@ void mmu_invalidate_cache_all(void) { mmu_ID_t mmu_id; - for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) { + for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) mmu_invalidate_cache(mmu_id); - } } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c index 722b684fbc37..1c5c50406633 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c @@ -168,9 +168,9 @@ static void store_vector( //load_vector (&v[1][0], &to[ISP_NWAY]); /* Fetch the next vector, since it will be overwritten. */ hive_uedge *data = (hive_uedge *)v; - for (i = 0; i < ISP_NWAY; i++) { + for (i = 0; i < ISP_NWAY; i++) hive_sim_wide_pack(data, (hive_wide)&from[i], ISP_VEC_ELEMBITS, i); - } + assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1); #if !defined(HRT_MEMORY_ACCESS) ia_css_device_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size); diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c index 09599884bdae..08af8a848fbf 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c @@ -22,9 +22,8 @@ ia_css_anr2_vmem_encode( for (i = 0; i < ANR_PARAM_SIZE; i++) { unsigned int j; - for (j = 0; j < ISP_VEC_NELEMS; j++) { + for (j = 0; j < ISP_VEC_NELEMS; j++) to->data[i][j] = from->data[i * ISP_VEC_NELEMS + j]; - } } } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c index cd867937ee13..fcd7d7e2afe8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c @@ -125,14 +125,12 @@ ia_css_bnlm_vmem_encode( bnlm_lut_encode(&to->div_lut, div_lut_nearests, div_lut_slopes, BNLM_DIV_LUT_SIZE); memset(to->div_lut_intercepts, 0, sizeof(to->div_lut_intercepts)); - for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) { + for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) to->div_lut_intercepts[0][i] = div_lut_intercepts[i]; - } memset(to->power_of_2, 0, sizeof(to->power_of_2)); - for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) { + for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) to->power_of_2[0][i] = 1 << i; - } } /* - Encodes BNLM public parameters into DMEM parameters */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c index 38751b8e9e6a..177487b6b237 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c @@ -52,13 +52,12 @@ static int ctc2_slope(int y1, int y0, int x1, int x0) /*the slope must lie within the range (-max_slope-1) >= (dydx) >= (max_slope) */ - if (slope <= -max_slope - 1) { + if (slope <= -max_slope - 1) dydx = -max_slope - 1; - } else if (slope >= max_slope) { + else if (slope >= max_slope) dydx = max_slope; - } else { + else dydx = slope; - } return dydx; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index 8e4451fcc8e3..76d76ca4401a 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -140,17 +140,14 @@ ia_css_eed1_8_vmem_encode( assert(tcinv_x[0] == 0); assert(fcinv_x[0] == 0); - for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) assert(chgrinv_x[j] > chgrinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) assert(tcinv_x[j] > tcinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) assert(fcinv_x[j] > fcinv_x[j - 1]); - } /* The implementation of the calculating 1/x is based on the availability * of the OP_vec_shuffle16 operation. @@ -260,9 +257,9 @@ ia_css_eed1_8_encode( to->margin_neg_diff = (from->neg_margin1 - from->neg_margin0); /* Encode DEWEnhance exp (e_dew_enh_asr) */ - for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) { + for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) min_exp = max(min_exp, from->dew_enhance_seg_exp[i]); - } + to->e_dew_enh_asr = 13 - clamp(min_exp, 0, 13); to->dedgew_max = from->dedgew_max; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c index 13678138c48c..823a1b0c0991 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c @@ -240,9 +240,8 @@ ia_css_s3a_hmem_decode( /* Calculate sum of histogram of R, which should not be less than count_for_3a */ sum_r = 0; - for (i = 0; i < HMEM_UNIT_SIZE; i++) { + for (i = 0; i < HMEM_UNIT_SIZE; i++) sum_r += out_ptr[i].r; - } if (sum_r < count_for_3a) { /* histogram is invalid */ return; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c index bd2def6c341a..0ca7ef5bb064 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c @@ -50,9 +50,8 @@ void ia_css_sdis_horicoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * width], width, padding); - } } void ia_css_sdis_vertcoef_vmem_encode( @@ -77,9 +76,8 @@ void ia_css_sdis_vertcoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * height], height, padding); - } } void ia_css_sdis_horiproj_encode( diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index af93ca96747c..60ae7bf5512b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -670,9 +670,8 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, err = ia_css_isp_param_allocate_isp_parameters( &binary->mem_params, &binary->css_params, &info->mem_initializers); - if (err) { + if (err) return err; - } } for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index b411ca2f415e..58d99abe70aa 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -1123,9 +1123,8 @@ ia_css_debug_pipe_graph_dump_prologue(void) void ia_css_debug_pipe_graph_dump_epilogue(void) { - if (strlen(ring_buffer) > 0) { + if (strlen(ring_buffer) > 0) dtrace_dot(ring_buffer); - } if (pg_inst.stream_format != N_ATOMISP_INPUT_FORMAT) { /* An input stream format has been set so assume we have @@ -1778,9 +1777,8 @@ static void debug_dump_one_trace(enum TRACE_CORE_ID proc_id) * When tid value is 111b, the data will be interpreted differently: * tid val is ignored, major field contains 2 bits (msb) for format type */ - if (tid_val == FIELD_TID_SEL_FORMAT_PAT) { + if (tid_val == FIELD_TID_SEL_FORMAT_PAT) dump_format = FIELD_FORMAT_UNPACK(trace_read_buf[j]); - } } switch (dump_format) { case TRACE_DUMP_FORMAT_POINT: diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c index e6c11d5f77b4..8e8433f5b07a 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c @@ -295,9 +295,8 @@ static bool create_input_system_channel( if (!rc) return false; - if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) { + if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) return false; - } if (!acquire_ib_buffer( metadata ? cfg->metadata.bits_per_pixel : diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index 0470871f8fff..8d11466fda1b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -575,9 +575,8 @@ static int pipeline_stage_create( binary = stage_desc->binary; firmware = stage_desc->firmware; vf_frame = stage_desc->vf_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) out_frame[i] = stage_desc->out_frame[i]; - } stage = kvzalloc_obj(*stage); if (!stage) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index fcebace11daf..8cbb5d598005 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -1928,9 +1928,8 @@ sh_css_set_per_frame_isp_config_on_pipe( params = stream->per_frame_isp_params_configs; /* update new ISP params object with the new config */ - if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) { + if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) err1 = -EINVAL; - } err2 = sh_css_init_isp_params_from_config(stream->pipes[0], params, config, pipe); @@ -2004,9 +2003,8 @@ sh_css_init_isp_params_from_config(struct ia_css_pipe *pipe, * user. */ /* we do not exit from this point immediately to allow internal * firmware feature testing. */ - if (is_dp_10bpp) { + if (is_dp_10bpp) err = -EINVAL; - } } else { err = -EINVAL; goto exit; @@ -3034,9 +3032,8 @@ process_kernel_parameters(unsigned int pipe_id, ia_css_ob_configure(¶ms->stream_configs.ob, isp_pipe_version, raw_bit_depth); } - if (params->config_changed[IA_CSS_S3A_ID]) { + if (params->config_changed[IA_CSS_S3A_ID]) ia_css_s3a_configure(raw_bit_depth); - } /* Copy stage uds parameters to config, since they can differ per stage. */ params->crop_config.crop_pos = params->uds[stage->stage_num].crop_pos; @@ -3899,9 +3896,8 @@ sh_css_invalidate_params(struct ia_css_stream *stream) params->isp_params_changed = true; for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) { for (j = 0; j < SH_CSS_MAX_STAGES; j++) { - for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) { + for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) params->isp_mem_params_changed[i][j][mem] = true; - } } } From 2785424faf21c6c43aa7b78ed9e800e580caa324 Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:24:02 +0000 Subject: [PATCH 1177/5214] staging: media: atomisp: Fix function indentation and braces Fix parameter indentation for functions and move opening braces onto new line. Fix checkpatch.pl errors: ERROR: open brace '{' following function definitions go on the next line Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c | 11 ++--- .../isp/kernels/raw/raw_1.0/ia_css_raw.host.c | 4 +- .../kernels/sdis/sdis_2/ia_css_sdis2.host.c | 7 ++- .../isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 22 ++++----- .../atomisp/pci/runtime/binary/src/binary.c | 48 +++++++++---------- .../pci/runtime/isp_param/src/isp_param.c | 18 ++++--- 6 files changed, 52 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index e9d6dd0bbfe2..4c85b5a62224 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -258,12 +258,11 @@ convert_allocate_dvs_6axis_config( return me; } -int -store_dvs_6axis_config( - const struct ia_css_dvs_6axis_config *dvs_6axis_config, - const struct ia_css_binary *binary, - const struct ia_css_frame_info *dvs_in_frame_info, - ia_css_ptr ddr_addr_y) { +int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_config, + const struct ia_css_binary *binary, + const struct ia_css_frame_info *dvs_in_frame_info, + ia_css_ptr ddr_addr_y) +{ struct ia_css_host_data *me; assert(dvs_6axis_config); diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c index a00f8d049a33..80fd64a8eb98 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c @@ -21,8 +21,8 @@ static const struct ia_css_raw_configuration default_config = { }; /* MW: These areMIPI / ISYS properties, not camera function properties */ -static enum sh_stream_format -css2isp_stream_format(enum atomisp_input_format from) { +static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from) +{ switch (from) { case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index 4a38d678f334..da58b61b091f 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -164,10 +164,9 @@ void ia_css_sdis2_clear_coefficients( dvs2_coefs->ver_coefs.even_imag = NULL; } -int -ia_css_get_dvs2_statistics( - struct ia_css_dvs2_statistics *host_stats, - const struct ia_css_isp_dvs_statistics *isp_stats) { +int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats, + const struct ia_css_isp_dvs_statistics *isp_stats) +{ struct ia_css_isp_dvs_statistics_map *map; int ret = 0; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index 3c675063c4a7..f8870f7e790d 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -45,11 +45,10 @@ int ia_css_vf_config(struct sh_css_isp_vf_isp_config *to, * to the requested viewfinder resolution on the upper side. The output cannot * be smaller than the requested viewfinder resolution. */ -int -sh_css_vf_downscale_log2( - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2) { +int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2) +{ unsigned int ds_log2 = 0; unsigned int out_width; @@ -80,13 +79,12 @@ sh_css_vf_downscale_log2( return 0; } -static int -configure_kernel( - const struct ia_css_binary_info *info, - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2, - struct ia_css_vf_configuration *config) { +static int configure_kernel(const struct ia_css_binary_info *info, + const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2, + struct ia_css_vf_configuration *config) +{ int err; unsigned int vf_log_ds = 0; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 60ae7bf5512b..c7962549e999 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -347,10 +347,10 @@ ia_css_binary_dvs_stat_grid_info( return; } -int -ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, - struct ia_css_grid_info *info, - struct ia_css_pipe *pipe) { +int ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, + struct ia_css_grid_info *info, + struct ia_css_pipe *pipe) +{ struct ia_css_3a_grid_info *s3a_info; int err = 0; @@ -439,9 +439,9 @@ supports_bds_factor(u32 supported_factors, return ((supported_factors & PACK_BDS_FACTOR(bds_factor)) != 0); } -static int -binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, - bool *binary_found) { +static int binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, + bool *binary_found) +{ const unsigned char *blob = sh_css_blob_info[i].blob; unsigned int size = sh_css_blob_info[i].header.blob.size; @@ -464,8 +464,8 @@ binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, /* When binaries are put at the beginning, they will only * be selected if no other primary matches. */ -int -ia_css_binary_init_infos(void) { +int ia_css_binary_init_infos(void) +{ unsigned int i; unsigned int num_of_isp_binaries = sh_css_num_binaries - NUM_OF_SPS - NUM_OF_BLS; @@ -497,8 +497,8 @@ ia_css_binary_init_infos(void) { return 0; } -int -ia_css_binary_uninit(void) { +int ia_css_binary_uninit(void) +{ unsigned int i; struct ia_css_binary_xinfo *b; @@ -625,19 +625,19 @@ binary_in_frame_padded_width(int in_frame_width, return rval; } -int -ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, - bool online, - bool two_ppc, - enum atomisp_input_format stream_format, - const struct ia_css_frame_info *in_info, /* can be NULL */ - const struct ia_css_frame_info *bds_out_info, /* can be NULL */ - const struct ia_css_frame_info *out_info[], /* can be NULL */ - const struct ia_css_frame_info *vf_info, /* can be NULL */ - struct ia_css_binary *binary, - struct ia_css_resolution *dvs_env, - int stream_config_left_padding, - bool accelerator) { +int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, + bool online, + bool two_ppc, + enum atomisp_input_format stream_format, + const struct ia_css_frame_info *in_info, /* can be NULL */ + const struct ia_css_frame_info *bds_out_info, /* can be NULL */ + const struct ia_css_frame_info *out_info[], /* can be NULL */ + const struct ia_css_frame_info *vf_info, /* can be NULL */ + struct ia_css_binary *binary, + struct ia_css_resolution *dvs_env, + int stream_config_left_padding, + bool accelerator) +{ const struct ia_css_binary_info *info = &xinfo->sp; unsigned int dvs_env_width = 0, dvs_env_height = 0, diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c index 251dd75a7613..354e5405fd60 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c @@ -93,11 +93,10 @@ ia_css_init_memory_interface( } } -int -ia_css_isp_param_allocate_isp_parameters( - struct ia_css_isp_param_host_segments *mem_params, - struct ia_css_isp_param_css_segments *css_params, - const struct ia_css_isp_param_isp_segments *mem_initializers) { +int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segments *mem_params, + struct ia_css_isp_param_css_segments *css_params, + const struct ia_css_isp_param_isp_segments *mem_initializers) +{ int err = 0; unsigned int mem, pclass; @@ -171,11 +170,10 @@ ia_css_isp_param_load_fw_params( } } -int -ia_css_isp_param_copy_isp_mem_if_to_ddr( - struct ia_css_isp_param_css_segments *ddr, - const struct ia_css_isp_param_host_segments *host, - enum ia_css_param_class pclass) { +int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments *ddr, + const struct ia_css_isp_param_host_segments *host, + enum ia_css_param_class pclass) +{ unsigned int mem; for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) From a380fa65d526b3e0d527cce0379b426bbe3f1c2e Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:24:15 +0000 Subject: [PATCH 1178/5214] staging: media: atomisp: Fix braces on incorrect lines Fix checkpatch.pl errors: ERROR: that open brace { should be on the previous line Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c | 3 +- .../isp/kernels/raw/raw_1.0/ia_css_raw.host.c | 3 +- .../kernels/sdis/sdis_2/ia_css_sdis2.host.c | 6 +- .../isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 3 +- .../atomisp/pci/runtime/binary/src/binary.c | 60 +++++++------------ .../pci/runtime/isp_param/src/isp_param.c | 6 +- drivers/staging/media/atomisp/pci/sh_css.c | 6 +- 7 files changed, 29 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index 4c85b5a62224..cf6c29155758 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -273,8 +273,7 @@ int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_confi binary, dvs_in_frame_info); - if (!me) - { + if (!me) { IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM); return -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c index 80fd64a8eb98..fb0e2a88cadb 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c @@ -23,8 +23,7 @@ static const struct ia_css_raw_configuration default_config = { /* MW: These areMIPI / ISYS properties, not camera function properties */ static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from) { - switch (from) - { + switch (from) { case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: return sh_stream_format_yuv420_legacy; case ATOMISP_INPUT_FORMAT_YUV420_8: diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index da58b61b091f..67860a5e4441 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -176,13 +176,11 @@ int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats, assert(isp_stats); map = ia_css_isp_dvs_statistics_map_allocate(isp_stats, NULL); - if (map) - { + if (map) { hmm_load(isp_stats->data_ptr, map->data_ptr, isp_stats->size); ia_css_translate_dvs2_statistics(host_stats, map); ia_css_isp_dvs_statistics_map_free(map); - } else - { + } else { IA_CSS_ERROR("out of memory"); ret = -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index f8870f7e790d..152faab2b169 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -64,8 +64,7 @@ int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info, * test for the height since the vmem buffers only put restrictions on * the width of a line, not on the number of lines in a frame. */ - while (out_width >= vf_info->res.width) - { + while (out_width >= vf_info->res.width) { ds_log2++; out_width /= 2; } diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index c7962549e999..39b37b557aff 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -477,8 +477,7 @@ int ia_css_binary_init_infos(void) if (!all_binaries) return -ENOMEM; - for (i = 0; i < num_of_isp_binaries; i++) - { + for (i = 0; i < num_of_isp_binaries; i++) { int ret; struct ia_css_binary_xinfo *binary = &all_binaries[i]; bool binary_found; @@ -502,8 +501,7 @@ int ia_css_binary_uninit(void) unsigned int i; struct ia_css_binary_xinfo *b; - for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) - { + for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) { for (b = binary_infos[i]; b; b = b->next) { if (b->xmem_addr) hmm_free(b->xmem_addr); @@ -664,8 +662,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, assert(binary); binary->info = xinfo; - if (!accelerator) - { + if (!accelerator) { /* binary->css_params has been filled by accelerator itself. */ err = ia_css_isp_param_allocate_isp_parameters( &binary->mem_params, &binary->css_params, @@ -673,15 +670,13 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, if (err) return err; } - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i] && (out_info[i]->res.width != 0)) { bin_out_info = out_info[i]; break; } } - if (in_info && bin_out_info) - { + if (in_info && bin_out_info) { need_scaling = (in_info->res.width != bin_out_info->res.width) || (in_info->res.height != bin_out_info->res.height); } @@ -712,8 +707,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->internal_frame_info.res.height = isp_internal_height; binary->internal_frame_info.raw_bit_depth = bits_per_pixel; - if (in_info) - { + if (in_info) { binary->effective_in_frame_res.width = in_info->res.width; binary->effective_in_frame_res.height = in_info->res.height; @@ -741,15 +735,13 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->in_frame_info.crop_info = in_info->crop_info; } - if (online) - { + if (online) { bits_per_pixel = ia_css_util_input_format_bpp( stream_format, two_ppc); } binary->in_frame_info.raw_bit_depth = bits_per_pixel; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i]) { binary->out_frame_info[i].res.width = out_info[i]->res.width; binary->out_frame_info[i].res.height = out_info[i]->res.height; @@ -768,8 +760,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, } } - if (vf_info && (vf_info->res.width != 0)) - { + if (vf_info && (vf_info->res.width != 0)) { err = ia_css_vf_configure(binary, bin_out_info, (struct ia_css_frame_info *)vf_info, &vf_log_ds); if (err) { @@ -787,8 +778,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->input_format = stream_format; /* viewfinder output info */ - if ((vf_info) && (vf_info->res.width != 0)) - { + if ((vf_info) && (vf_info->res.width != 0)) { unsigned int vf_out_vecs, vf_out_width, vf_out_height; binary->vf_frame_info.format = vf_info->format; @@ -820,23 +810,20 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->vf_frame_info.padded_width = vf_out_width; binary->vf_frame_info.res.height = vf_out_height; } - } else - { + } else { binary->vf_frame_info.res.width = 0; binary->vf_frame_info.padded_width = 0; binary->vf_frame_info.res.height = 0; } - if (info->enable.ca_gdc) - { + if (info->enable.ca_gdc) { binary->morph_tbl_width = _ISP_MORPH_TABLE_WIDTH(isp_internal_width); binary->morph_tbl_aligned_width = _ISP_MORPH_TABLE_ALIGNED_WIDTH(isp_internal_width); binary->morph_tbl_height = _ISP_MORPH_TABLE_HEIGHT(isp_internal_height); - } else - { + } else { binary->morph_tbl_width = 0; binary->morph_tbl_aligned_width = 0; binary->morph_tbl_height = 0; @@ -846,8 +833,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, sc_3a_dis_padded_width = binary->in_frame_info.padded_width; sc_3a_dis_height = binary->in_frame_info.res.height; if (bds_out_info && in_info && - bds_out_info->res.width != in_info->res.width) - { + bds_out_info->res.width != in_info->res.width) { /* TODO: Next, "internal_frame_info" should be derived from * bds_out. So this part will change once it is in place! */ sc_3a_dis_width = bds_out_info->res.width + info->pipeline.left_cropping; @@ -857,18 +843,15 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, s3a_isp_width = _ISP_S3A_ELEMS_ISP_WIDTH(sc_3a_dis_padded_width, info->pipeline.left_cropping); - if (info->s3a.fixed_s3a_deci_log) - { + if (info->s3a.fixed_s3a_deci_log) { s3a_log_deci = info->s3a.fixed_s3a_deci_log; - } else - { + } else { s3a_log_deci = binary_grid_deci_factor_log2(s3a_isp_width, sc_3a_dis_height); } binary->deci_factor_log2 = s3a_log_deci; - if (info->enable.s3a) - { + if (info->enable.s3a) { binary->s3atbl_width = _ISP_S3ATBL_WIDTH(sc_3a_dis_width, s3a_log_deci); @@ -881,21 +864,18 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->s3atbl_isp_height = _ISP_S3ATBL_ISP_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->s3atbl_width = 0; binary->s3atbl_height = 0; binary->s3atbl_isp_width = 0; binary->s3atbl_isp_height = 0; } - if (info->enable.sc) - { + if (info->enable.sc) { binary->sctbl_width_per_color = _ISP_SCTBL_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci); binary->sctbl_aligned_width_per_color = SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR; binary->sctbl_height = _ISP_SCTBL_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->sctbl_width_per_color = 0; binary->sctbl_aligned_width_per_color = 0; binary->sctbl_height = 0; diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c index 354e5405fd60..1d20eb650757 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c @@ -101,8 +101,7 @@ int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segmen unsigned int mem, pclass; pclass = IA_CSS_PARAM_CLASS_PARAM; - for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) - { + for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) { for (pclass = 0; pclass < IA_CSS_NUM_PARAM_CLASSES; pclass++) { u32 size = 0; @@ -176,8 +175,7 @@ int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments { unsigned int mem; - for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) - { + for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) { size_t size = host->params[pclass][mem].size; ia_css_ptr ddr_mem_ptr = ddr->params[pclass][mem].address; char *host_mem_ptr = host->params[pclass][mem].address; diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 6cda5925fa45..f40b6912c14b 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -2256,8 +2256,7 @@ alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW_PACKED\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW_PACKED; - } else - { + } else { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW; @@ -7856,8 +7855,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* check if mipi size specified */ if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) - if (!IS_ISP2401 || !stream_config->online) - { + if (!IS_ISP2401 || !stream_config->online) { unsigned int port = (unsigned int)stream_config->source.port.port; if (port >= N_MIPI_PORT_ID) { From 5cb289b5397d198f0ae1ee70dc31fee1c46ff5f9 Mon Sep 17 00:00:00 2001 From: Hamdan Khan Date: Mon, 9 Feb 2026 21:20:26 +0500 Subject: [PATCH 1179/5214] staging: media: atomisp: Fix typos and formatting in headers Update block and inline comments to follow kernel commenting conventions, fix typos and wording, remove redundant comments and reformat long comments for clarity and line length consistency. Although some comments used the /** ... */ style, they are not kernel-doc comments and are converted to normal comment style. No functional changes are intended. Signed-off-by: Hamdan Khan Signed-off-by: Sakari Ailus --- .../media/atomisp/include/linux/atomisp.h | 182 +++++++++--------- .../include/linux/atomisp_gmin_platform.h | 7 +- .../atomisp/include/linux/atomisp_platform.h | 42 ++-- 3 files changed, 120 insertions(+), 111 deletions(-) diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index 3c8fa3f5808d..7d7f57c2d67c 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -22,7 +22,7 @@ #define ATOMISP_HW_STEPPING_A0 0x00 #define ATOMISP_HW_STEPPING_B0 0x10 -/*ISP binary running mode*/ +/* ISP binary running mode */ #define CI_MODE_PREVIEW 0x8000 #define CI_MODE_VIDEO 0x4000 #define CI_MODE_STILL_CAPTURE 0x2000 @@ -59,9 +59,9 @@ /* Configuration used by Bayer noise reduction and YCC noise reduction */ struct atomisp_nr_config { - /* [gain] Strength of noise reduction for Bayer NR (Used by Bayer NR) */ + /* [gain] Strength of noise reduction for Bayer NR */ unsigned int bnr_gain; - /* [gain] Strength of noise reduction for YCC NR (Used by YCC NR) */ + /* [gain] Strength of noise reduction for YCC NR */ unsigned int ynr_gain; /* [intensity] Sensitivity of Edge (Used by Bayer NR) */ unsigned int direction; @@ -78,7 +78,8 @@ struct atomisp_tnr_config { unsigned int threshold_uv;/* [intensity] Motion sensitivity for U/V */ }; -/* Histogram. This contains num_elements values of type unsigned int. +/* + * Contains num_elements values of type unsigned int. * The data pointer is a DDR pointer (virtual address). */ struct atomisp_histogram { @@ -94,7 +95,7 @@ enum atomisp_ob_mode { /* Optical black level configuration */ struct atomisp_ob_config { - /* Obtical black level mode (Fixed / Raster) */ + /* Optical black level mode (Fixed / Raster) */ enum atomisp_ob_mode mode; /* [intensity] optical black level for GR (relevant for fixed mode) */ unsigned int level_gr; @@ -146,8 +147,7 @@ struct atomisp_3a_config { unsigned int ae_y_coef_r; /* [gain] Weight of R for Y */ unsigned int ae_y_coef_g; /* [gain] Weight of G for Y */ unsigned int ae_y_coef_b; /* [gain] Weight of B for Y */ - unsigned int awb_lg_high_raw; /* [intensity] - AWB level gate high for raw */ + unsigned int awb_lg_high_raw; /* [intensity] AWB level gate high for raw */ unsigned int awb_lg_low; /* [intensity] AWB level gate low */ unsigned int awb_lg_high; /* [intensity] AWB level gate high */ int af_fir1_coef[7]; /* [factor] AF FIR coefficients of fir1 */ @@ -188,14 +188,15 @@ struct atomisp_dis_vector { int y; }; -/* DVS 2.0 Coefficient types. This structure contains 4 pointers to - * arrays that contain the coefficients for each type. +/* + * DVS 2.0 Coefficient types. This structure contains 4 pointers to + * arrays that contain the coefficients for each type. */ struct atomisp_dvs2_coef_types { - short __user *odd_real; /** real part of the odd coefficients*/ - short __user *odd_imag; /** imaginary part of the odd coefficients*/ - short __user *even_real;/** real part of the even coefficients*/ - short __user *even_imag;/** imaginary part of the even coefficients*/ + short __user *odd_real; /* Real part of the odd coefficients */ + short __user *odd_imag; /* Imaginary part of the odd coefficients */ + short __user *even_real;/* Real part of the even coefficients */ + short __user *even_imag;/* Imaginary part of the even coefficients */ }; /* @@ -203,10 +204,10 @@ struct atomisp_dvs2_coef_types { * arrays that contain the statistics for each type. */ struct atomisp_dvs2_stat_types { - int __user *odd_real; /** real part of the odd statistics*/ - int __user *odd_imag; /** imaginary part of the odd statistics*/ - int __user *even_real;/** real part of the even statistics*/ - int __user *even_imag;/** imaginary part of the even statistics*/ + int __user *odd_real; /* Real part of the odd statistics */ + int __user *odd_imag; /* Imaginary part of the odd statistics */ + int __user *even_real;/* Real part of the even statistics */ + int __user *even_imag;/* Imaginary part of the even statistics */ }; struct atomisp_dis_coefficients { @@ -234,12 +235,12 @@ struct atomisp_3a_rgby_output { }; /* - * Because we have 2 pipes at max to output metadata, therefore driver will use - * ATOMISP_MAIN_METADATA to specify the metadata from the pipe which keeps - * streaming always and use ATOMISP_SEC_METADATA to specify the metadata from - * the pipe which is streaming by request like capture pipe of ZSL or SDV mode - * as secondary metadata. And for the use case which has only one pipe - * streaming like online capture, ATOMISP_MAIN_METADATA will be used. + * As the driver can output metadata on two pipes at max, + * ATOMISP_MAIN_METADATA is used for the pipe that streams continuously. + * ATOMISP_SEC_METADATA is used for the pipe that streams on demand, e.g., + * the capture pipe in ZSL or SDV modes. + * In use cases with a single streaming pipe (like online capture), + * only ATOMISP_MAIN_METADATA is used. */ enum atomisp_metadata_type { ATOMISP_MAIN_METADATA = 0, @@ -257,7 +258,7 @@ struct atomisp_3a_statistics { struct atomisp_3a_output __user *data; struct atomisp_3a_rgby_output __user *rgby_data; u32 exp_id; /* exposure ID */ - u32 isp_config_id; /* isp config ID */ + u32 isp_config_id; }; /* White Balance (Gain Adjust) */ @@ -272,8 +273,8 @@ struct atomisp_wb_config { /* Color Space Conversion settings */ struct atomisp_cc_config { unsigned int fraction_bits; - int matrix[3 * 3]; /* RGB2YUV Color matrix, signed - <13-fraction_bits>. */ + /* RGB2YUV Color matrix, signed <13-fraction_bits>. */ + int matrix[3 * 3]; }; /* De pixel noise configuration */ @@ -291,13 +292,15 @@ struct atomisp_ce_config { /* Defect pixel correction configuration */ struct atomisp_dp_config { - /* [intensity] The threshold of defect Pixel Correction, representing + /* + * [intensity] The threshold of defect Pixel Correction, representing * the permissible difference of intensity between one pixel and its * surrounding pixels. Smaller values result in more frequent pixel * corrections. u0_16 */ unsigned int threshold; - /* [gain] The sensitivity of mis-correction. ISP will miss a lot of + /* + * [gain] The sensitivity of mis-correction. ISP will miss a lot of * defects if the value is set too large. u8_8 */ unsigned int gain; @@ -312,7 +315,7 @@ struct atomisp_xnr_config { __u16 threshold; }; -/* metadata config */ +/* Metadata config */ struct atomisp_metadata_config { u32 metadata_height; u32 metadata_stride; @@ -322,31 +325,30 @@ struct atomisp_metadata_config { * Generic resolution structure. */ struct atomisp_resolution { - u32 width; /** Width */ - u32 height; /** Height */ + u32 width; + u32 height; }; /* - * This specifies the coordinates (x,y) + * Specifies the zoom point coordinates (x,y) */ struct atomisp_zoom_point { - s32 x; /** x coordinate */ - s32 y; /** y coordinate */ + s32 x; + s32 y; }; /* - * This specifies the region + * Specifies the zoom region */ struct atomisp_zoom_region { - struct atomisp_zoom_point - origin; /* Starting point coordinates for the region */ + struct atomisp_zoom_point origin; /* Starting point coordinates for the region */ struct atomisp_resolution resolution; /* Region resolution */ }; struct atomisp_dz_config { - u32 dx; /** Horizontal zoom factor */ - u32 dy; /** Vertical zoom factor */ - struct atomisp_zoom_region zoom_region; /** region for zoom */ + u32 dx; /* Horizontal zoom factor */ + u32 dy; /* Vertical zoom factor */ + struct atomisp_zoom_region zoom_region; }; struct atomisp_parm { @@ -367,8 +369,8 @@ struct atomisp_parm { }; struct dvs2_bq_resolution { - int width_bq; /* width [BQ] */ - int height_bq; /* height [BQ] */ + int width_bq; + int height_bq; }; struct atomisp_dvs2_bq_resolutions { @@ -378,9 +380,9 @@ struct atomisp_dvs2_bq_resolutions { struct dvs2_bq_resolution output_bq; /* GDC effective envelope size [BQ] */ struct dvs2_bq_resolution envelope_bq; - /* isp pipe filter size [BQ] */ + /* ISP pipe filter size [BQ] */ struct dvs2_bq_resolution ispfilter_bq; - /* GDC shit size [BQ] */ + /* GDC shift size [BQ] */ struct dvs2_bq_resolution gdc_shift_bq; }; @@ -411,8 +413,8 @@ struct atomisp_parameters { struct atomisp_cnr_config *cnr_config; /* Chroma Noise Reduction */ struct atomisp_macc_config *macc_config; /* MACC */ struct atomisp_ctc_config *ctc_config; /* Chroma Tone Control */ - struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ - struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ struct atomisp_ce_config *ce_config; struct atomisp_dvs_6axis_config *dvs_6axis_config; struct atomisp_ob_config *ob_config; /* Objective Black config */ @@ -425,10 +427,8 @@ struct atomisp_parameters { struct atomisp_3a_config *a3a_config; /* 3A Statistics config */ struct atomisp_xnr_config *xnr_config; /* eXtra Noise Reduction */ struct atomisp_dz_config *dz_config; /* Digital Zoom */ - struct atomisp_cc_config *yuv2rgb_cc_config; /* Color - Correction config */ - struct atomisp_cc_config *rgb2yuv_cc_config; /* Color - Correction config */ + struct atomisp_cc_config *yuv2rgb_cc_config; /* Color Correction config */ + struct atomisp_cc_config *rgb2yuv_cc_config; /* Color Correction config */ struct atomisp_macc_table *macc_table; struct atomisp_gamma_table *gamma_table; struct atomisp_ctc_table *ctc_table; @@ -466,8 +466,8 @@ struct atomisp_parameters { void *res_mgr_2500_config; /* - * Output frame pointer the config is to be applied to (optional), - * set to NULL to make this config is applied as global. + * Output frame pointer to which the config will be applied to (optional), + * Set to NULL to make this config be applied as global. */ void *output_frame; /* @@ -490,9 +490,9 @@ struct atomisp_gamma_table { unsigned short data[ATOMISP_GAMMA_TABLE_SIZE]; }; -/* Morphing table for advanced ISP. - * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements - * in memory. +/* + * Morphing table for advanced ISP. + * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements in memory. */ #define ATOMISP_MORPH_TABLE_NUM_PLANES 6 struct atomisp_morph_table { @@ -519,7 +519,7 @@ struct atomisp_shading_table { __u16 *data[ATOMISP_NUM_SC_COLORS]; }; -/* parameter for MACC */ +/* Parameter for MACC */ #define ATOMISP_NUM_MACC_AXES 16 struct atomisp_macc_table { short data[4 * ATOMISP_NUM_MACC_AXES]; @@ -530,7 +530,7 @@ struct atomisp_macc_config { struct atomisp_macc_table table; }; -/* Parameter for ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_CTC_TABLE_SIZE 1024 struct atomisp_ctc_table { unsigned short data[ATOMISP_CTC_TABLE_SIZE]; @@ -538,9 +538,10 @@ struct atomisp_ctc_table { /* Parameter for overlay image loading */ struct atomisp_overlay { - /* the frame containing the overlay data The overlay frame width should - * be the multiples of 2*ISP_VEC_NELEMS. The overlay frame height - * should be the multiples of 2. + /* + * The frame containing the overlay data. The overlay frame width should + * be a multiple of 2 * ISP_VEC_NELEMS. The overlay frame height + * should be a multiple of 2. */ struct v4l2_framebuffer *frame; /* Y value of overlay background */ @@ -549,23 +550,27 @@ struct atomisp_overlay { char bg_u; /* V value of overlay background */ char bg_v; - /* the blending percent of input data for Y subpixels */ + /* The blending percentage of input data for Y subpixels */ unsigned char blend_input_perc_y; - /* the blending percent of input data for U subpixels */ + /* The blending percentage of input data for U subpixels */ unsigned char blend_input_perc_u; - /* the blending percent of input data for V subpixels */ + /* The blending percentage of input data for V subpixels */ unsigned char blend_input_perc_v; - /* the blending percent of overlay data for Y subpixels */ + /* The blending percentage of overlay data for Y subpixels */ unsigned char blend_overlay_perc_y; - /* the blending percent of overlay data for U subpixels */ + /* The blending percentage of overlay data for U subpixels */ unsigned char blend_overlay_perc_u; - /* the blending percent of overlay data for V subpixels */ + /* The blending percentage of overlay data for V subpixels */ unsigned char blend_overlay_perc_v; - /* the overlay start x pixel position on output frame It should be the - multiples of 2*ISP_VEC_NELEMS. */ + /* + * The overlay "start x" pixel position on the output frame. It should be a + * multiple of 2 * ISP_VEC_NELEMS. + */ unsigned int overlay_start_x; - /* the overlay start y pixel position on output frame It should be the - multiples of 2. */ + /* + * The overlay "start y" pixel position on the output frame. It should be a + * multiple of 2. + */ unsigned int overlay_start_y; }; @@ -659,7 +664,7 @@ enum atomisp_burst_capture_options { #define EXT_ISP_SHOT_MODE_ANIMATED_PHOTO 10 #define EXT_ISP_SHOT_MODE_SPORTS 11 -/*Private IOCTLs for ISP */ +/* Private IOCTLs for ISP */ #define ATOMISP_IOC_G_XNR \ _IOR('v', BASE_VIDIOC_PRIVATE + 0, int) #define ATOMISP_IOC_S_XNR \ @@ -684,7 +689,8 @@ enum atomisp_burst_capture_options { _IOR('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) #define ATOMISP_IOC_S_EE \ _IOW('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) -/* Digital Image Stabilization: +/* + * Digital Image Stabilization: * 1. get dis statistics: reads DIS statistics from ISP (every frame) * 2. set dis coefficients: set DIS filter coefficients (one time) * 3. set dis motion vector: set motion vector (result of DIS, every frame) @@ -716,54 +722,54 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_GDC_TAB \ _IOW('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table) -/* macc parameter control*/ +/* MACC parameter control */ #define ATOMISP_IOC_G_ISP_MACC \ _IOR('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) #define ATOMISP_IOC_S_ISP_MACC \ _IOW('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) -/* Defect pixel detection & Correction */ +/* Defect pixel detection & correction */ #define ATOMISP_IOC_G_ISP_BAD_PIXEL_DETECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) #define ATOMISP_IOC_S_ISP_BAD_PIXEL_DETECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) -/* False Color Correction */ +/* False color correction */ #define ATOMISP_IOC_G_ISP_FALSE_COLOR_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) #define ATOMISP_IOC_S_ISP_FALSE_COLOR_CORRECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) -/* ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_IOC_G_ISP_CTC \ _IOR('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) #define ATOMISP_IOC_S_ISP_CTC \ _IOW('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_ISP_WHITE_BALANCE \ _IOR('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) #define ATOMISP_IOC_S_ISP_WHITE_BALANCE \ _IOW('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) -/* fpn table loading */ +/* FPN table loading */ #define ATOMISP_IOC_S_ISP_FPN_TABLE \ _IOW('v', BASE_VIDIOC_PRIVATE + 17, struct v4l2_framebuffer) -/* overlay image loading */ +/* Overlay image loading */ #define ATOMISP_IOC_G_ISP_OVERLAY \ _IOWR('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) #define ATOMISP_IOC_S_ISP_OVERLAY \ _IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) -/* bcd driver bridge */ +/* BCD driver bridge */ #define ATOMISP_IOC_CAMERA_BRIDGE \ _IOWR('v', BASE_VIDIOC_PRIVATE + 19, struct atomisp_bc_video_package) #define ATOMISP_IOC_S_EXPOSURE \ _IOW('v', BASE_VIDIOC_PRIVATE + 21, struct atomisp_exposure) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_3A_CONFIG \ _IOR('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config) #define ATOMISP_IOC_S_3A_CONFIG \ @@ -773,7 +779,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_SHD_TAB \ _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table) -/* Gamma Correction */ +/* Gamma correction */ #define ATOMISP_IOC_G_ISP_GAMMA_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 28, struct atomisp_gc_config) @@ -804,7 +810,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ARRAY_RESOLUTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 45, struct atomisp_resolution) -/* for depth mode sensor frame sync compensation */ +/* For depth mode sensor frame sync compensation */ #define ATOMISP_IOC_G_DEPTH_SYNC_COMP \ _IOR('v', BASE_VIDIOC_PRIVATE + 46, unsigned int) @@ -822,7 +828,7 @@ enum atomisp_burst_capture_options { * _IOW('v', BASE_VIDIOC_PRIVATE + 56, struct atomisp_sensor_regs) */ -/* ISP Private control IDs */ +/* ISP Private control IDs */ #define V4L2_CID_ATOMISP_BAD_PIXEL_DETECTION \ (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_ATOMISP_POSTPROCESS_GDC_CAC \ @@ -836,8 +842,10 @@ enum atomisp_burst_capture_options { #define V4L2_CID_ATOMISP_LOW_LIGHT \ (V4L2_CID_PRIVATE_BASE + 5) -/* Camera class: - * Exposure, Flash and privacy (indicator) light controls, to be upstreamed */ +/* + * Camera class: + * Exposure, Flash and privacy (indicator) light controls, to be upstreamed + */ #define V4L2_CID_CAMERA_LASTP1 (V4L2_CID_CAMERA_CLASS_BASE + 1024) #define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20) @@ -879,7 +887,7 @@ enum atomisp_burst_capture_options { #define V4L2_EVENT_ATOMISP_ACC_COMPLETE (V4L2_EVENT_PRIVATE_START + 4) #define V4L2_EVENT_ATOMISP_PAUSE_BUFFER (V4L2_EVENT_PRIVATE_START + 5) #define V4L2_EVENT_ATOMISP_CSS_RESET (V4L2_EVENT_PRIVATE_START + 6) -/* Nonstandard color effects for V4L2_CID_COLORFX */ +/* Non-standard color effects for V4L2_CID_COLORFX */ enum { V4L2_COLORFX_SKIN_WHITEN_LOW = 1001, V4L2_COLORFX_SKIN_WHITEN_HIGH = 1002, diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h index 426c5ee4ec18..74092af1c659 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h @@ -15,8 +15,7 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd); int gmin_get_var_int(struct device *dev, bool is_gmin, const char *var, int def); struct camera_sensor_platform_data * -gmin_camera_platform_data( - struct v4l2_subdev *subdev, - enum atomisp_input_format csi_format, - enum atomisp_bayer_order csi_bayer); + gmin_camera_platform_data(struct v4l2_subdev *subdev, + enum atomisp_input_format csi_format, + enum atomisp_bayer_order csi_bayer); #endif diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index 6146555fe9cf..0d6f2a4039f1 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -57,7 +57,8 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_RAW_16, /* RAW data, 16 bits per pixel */ ATOMISP_INPUT_FORMAT_BINARY_8, /* Binary byte stream. */ - /* CSI2-MIPI specific format: Generic short packet data. It is used to + /* + * CSI2-MIPI specific format: Generic short packet data. It is used to * keep the timing information for the opening/closing of shutters, * triggering of flashes and etc. */ @@ -70,18 +71,18 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_GENERIC_SHORT7, /* Generic Short Packet Code 7 */ ATOMISP_INPUT_FORMAT_GENERIC_SHORT8, /* Generic Short Packet Code 8 */ - /* CSI2-MIPI specific format: YUV data. - */ - ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ - ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ + /* YUV data */ + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, + /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, - /* CSI2-MIPI specific format: Generic long packet data - */ - ATOMISP_INPUT_FORMAT_EMBEDDED, /* Embedded 8-bit non Image Data */ + /* CSI2-MIPI specific format: Generic long packet data */ + /* Embedded 8-bit non Image Data */ + ATOMISP_INPUT_FORMAT_EMBEDDED, - /* CSI2-MIPI specific format: User defined byte-based data. For example, + /* + * User defined byte-based data. For example, * the data transmitter (e.g. the SoC sensor) can keep the JPEG data as * the User Defined Data Type 4 and the MPEG data as the * User Defined Data Type 7. @@ -105,9 +106,9 @@ struct intel_v4l2_subdev_table { }; /* - * Sensor of external ISP can send multiple streams with different mipi data + * Sensor of external ISP can send multiple streams with different MIPI data * type in the same virtual channel. This information needs to come from the - * sensor or external ISP + * sensor or external ISP. */ struct atomisp_isys_config_info { u8 input_format; @@ -118,16 +119,17 @@ struct atomisp_isys_config_info { struct atomisp_input_stream_info { enum atomisp_input_stream_id stream; u8 enable; - /* Sensor driver fills ch_id with the id - of the virtual channel. */ + /* Sensor driver fills ch_id with the id of the virtual channel. */ u8 ch_id; - /* Tells how many streams in this virtual channel. If 0 ignore rest - * and the input format will be from mipi_info */ + /* + * Tells the number of streams in this virtual channel. If 0, ignore the + * rest and the input format will be from mipi_info. + */ u8 isys_configs; /* - * if more isys_configs is more than 0, sensor needs to configure the - * input format differently. width and height can be 0. If width and - * height is not zero, then the corresponding data needs to be set + * If isys_configs is greater than 0, sensor needs to configure the + * input format differently. Width and height can be 0. If width and + * height are not zero, then the corresponding data needs to be set. */ struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL]; }; From 4d1e8d9ca1adde4109291cac6aaec135c4812c4e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2026 15:30:40 +0100 Subject: [PATCH 1180/5214] staging: media: atomisp: Kill OP_std_modadd() macro The OP_std_modadd() adds no value, kill it and update the users to perform the necessary operations themselves. No intended functional changes. Signed-off-by: Andy Shevchenko Reviewed-by: Ethan Tidmore Signed-off-by: Sakari Ailus --- .../atomisp/pci/base/circbuf/interface/ia_css_circbuf.h | 6 +----- .../pci/base/circbuf/interface/ia_css_circbuf_desc.h | 8 ++------ .../media/atomisp/pci/hive_isp_css_include/math_support.h | 6 ------ .../staging/media/atomisp/pci/runtime/queue/src/queue.c | 4 ++-- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 148f9f6febfb..4732b45b25ee 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -139,8 +139,6 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb); OP___assert(cb->desc); OP___assert(cb->desc->size > 0); @@ -150,9 +148,7 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( offset += cb->desc->size; /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb->desc->size); - - return dest; + return (base + offset) % cb->desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 3d89626a0733..64f754f1d49b 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -47,7 +47,7 @@ static inline bool ia_css_circbuf_desc_is_full( ia_css_circbuf_desc_t *cb_desc) { OP___assert(cb_desc); - return (OP_std_modadd(cb_desc->end, 1, cb_desc->size) == cb_desc->start); + return ((cb_desc->end + 1) % cb_desc->size) == cb_desc->start; } /** @@ -78,8 +78,6 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb_desc); OP___assert(cb_desc->size > 0); @@ -88,9 +86,7 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( offset += cb_desc->size; /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb_desc->size); - - return dest; + return (base + offset) % cb_desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index 2cb5c986790a..72a070d94736 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -14,10 +14,4 @@ #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) -/* - * For SP and ISP, SDK provides the definition of OP_std_modadd. - * We need it only for host - */ -#define OP_std_modadd(base, offset, size) ((base + offset) % (size)) - #endif /* __MATH_SUPPORT_H */ diff --git a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c index afe77d4373f8..d27c6567daeb 100644 --- a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c +++ b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c @@ -167,7 +167,7 @@ int ia_css_queue_dequeue(ia_css_queue_t *qhandle, uint32_t *item) *item = cb_elem.val; - cb_desc.start = OP_std_modadd(cb_desc.start, 1, cb_desc.size); + cb_desc.start = (cb_desc.start + 1) % cb_desc.size; /* c. Store the queue object */ /* Set only fields requiring update with @@ -315,7 +315,7 @@ int ia_css_queue_peek(ia_css_queue_t *qhandle, u32 offset, uint32_t *element) if (offset > num_elems) return -EINVAL; - offset = OP_std_modadd(cb_desc.start, offset, cb_desc.size); + offset = (cb_desc.start + offset) % cb_desc.size; error = ia_css_queue_item_load(qhandle, (uint8_t)offset, &cb_elem); if (error != 0) return error; From 835da7cf2da4bfbca140acc54c8d9bcb80695e6c Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Thu, 5 Mar 2026 14:45:27 +0100 Subject: [PATCH 1181/5214] media: staging: atomisp: Remove unnecessary return statement in void function Remove redundant 'return;' at the end of void function ia_css_dvs_statistics_get(). Void functions do not need an explicit return statement at the end. No other occurrences in this file. Found with checkpatch.pl --strict. Signed-off-by: Tomasz Unger Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_param_dvs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index 9ccdb66de2df..3d2cb2d25fdb 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -269,5 +269,4 @@ ia_css_dvs_statistics_get(enum dvs_statistics_type type, ia_css_get_dvs2_statistics(host_stats->p_dvs2_statistics_host, isp_stats->p_dvs_statistics_isp); } - return; } From f69992d08a06e9f105716a141731507f280f8b13 Mon Sep 17 00:00:00 2001 From: Ethan Lam Date: Mon, 2 Mar 2026 20:35:47 +0800 Subject: [PATCH 1182/5214] staging: media: atomisp: fix block comment style in atomisp_cmd.c Fix block comment style warnings reported by checkpatch.pl in atomisp_cmd.c. Signed-off-by: Ethan Lam Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_cmd.c | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 5e5f3fe1ba7d..7c45fa10f986 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -1380,8 +1380,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_css_get_grid_info(asd, pipe_id)) return; - /* We must free all buffers because they no longer match - the grid size. */ + /* + * We must free all buffers because they no longer match + * the grid size. + */ atomisp_css_free_stat_buffers(asd); err = atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL); @@ -1393,8 +1395,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_alloc_3a_output_buf(asd)) { /* Failure for 3A buffers does not influence DIS buffers */ if (asd->params.s3a_output_bytes != 0) { - /* For SOC sensor happens s3a_output_bytes == 0, - * using if condition to exclude false error log */ + /* + * For SOC sensor happens s3a_output_bytes == 0, + * using if condition to exclude false error log + */ dev_err(isp->dev, "Failed to allocate memory for 3A statistics\n"); } goto err; @@ -1686,9 +1690,11 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, return -EINVAL; if (atomisp_compare_grid(asd, &config->grid_info) != 0) { - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -2462,9 +2468,11 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (sizeof(*cur) != sizeof(coefs->grid) || memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { dev_err(asd->isp->dev, "dvs grid mismatch!\n"); - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -3025,9 +3033,11 @@ int atomisp_param(struct atomisp_sub_device *asd, int flag, atomisp_curr_user_grid_info(asd, &config->info); - /* We always return the resolution and stride even if there is + /* + * We always return the resolution and stride even if there is * no valid metadata. This allows the caller to get the - * information needed to allocate user-space buffers. */ + * information needed to allocate user-space buffers. + */ config->metadata_config.metadata_height = asd-> stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. metadata_info.resolution.height; @@ -3277,8 +3287,10 @@ atomisp_bytesperline_to_padded_width(unsigned int bytesperline, return bytesperline / 2; case IA_CSS_FRAME_FORMAT_RGBA888: return bytesperline / 4; - /* The following cases could be removed, but we leave them - in to document the formats that are included. */ + /* + * The following cases could be removed, but we leave them + * in to document the formats that are included. + */ case IA_CSS_FRAME_FORMAT_NV11: case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV16: @@ -3314,9 +3326,11 @@ atomisp_v4l2_framebuffer_to_css_frame(const struct v4l2_framebuffer *arg, padded_width = atomisp_bytesperline_to_padded_width( arg->fmt.bytesperline, sh_format); - /* Note: the padded width on an ia_css_frame is in elements, not in - bytes. The RAW frame we use here should always be a 16bit RAW - frame. This is why we bytesperline/2 is equal to the padded with */ + /* + * Note: the padded width on an ia_css_frame is in elements, not in + * bytes. The RAW frame we use here should always be a 16bit RAW + * frame. This is why we bytesperline/2 is equal to the padded with + */ if (ia_css_frame_allocate(&res, arg->fmt.width, arg->fmt.height, sh_format, padded_width, 0)) { ret = -ENOMEM; @@ -3925,8 +3939,10 @@ static inline int atomisp_set_sensor_mipi_to_isp( asd->stream_env[stream_id].isys_info[1].height); } - /* Compatibility for sensors which provide no media bus code - * in s_mbus_framefmt() nor support pad formats. */ + /* + * Compatibility for sensors which provide no media bus code + * in s_mbus_framefmt() nor support pad formats. + */ if (mipi_info && mipi_info->input_format != -1) { bayer_order = mipi_info->raw_bayer_order; @@ -4384,8 +4400,10 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); - /* Try to enable YUV downscaling if ISP input is 10 % (either - * width or height) bigger than the desired result. */ + /* + * Try to enable YUV downscaling if ISP input is 10 % (either + * width or height) bigger than the desired result. + */ if (!IS_MOFD || isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || isp_sink_crop.height * 9 / 10 < f->fmt.pix.height || From 4e8156bd9517fa18c3613ea39c222c7035f0221e Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Tue, 3 Feb 2026 16:31:34 +0000 Subject: [PATCH 1183/5214] media: atomisp: Fix memory leak in atomisp_fixed_pattern_table() atomisp_v4l2_framebuffer_to_css_frame() allocates memory for temporary variable raw_black_frame, which must be released via ia_css_frame_free() before the function returns. However, if sh_css_set_black_frame() fails, the function returns immediately without performing this cleanup, leading to a memory leak. Fix this by assigning the return value of sh_css_set_black_frame() to ret. This ensures that the error code is propagated while allowing the execution to fall through to the ia_css_frame_free() cleanup call. The bug was originally detected on v6.13-rc1 using an experimental static analysis tool we are developing, and we have verified that the issue persists in the latest mainline kernel. The tool is based on the LLVM framework and is specifically designed to detect memory management issues. It is currently under active development and not yet publicly available. We performed build testing on x86_64 with allyesconfig. Since triggering this error path in atomisp requires specific Intel Atom ISP hardware and firmware, we were unable to perform runtime testing and instead verified the fix according to the code logic. Fixes: 85b606e02ad7 ("media: atomisp: get rid of a bunch of other wrappers") Signed-off-by: Zilin Guan Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 7c45fa10f986..e401c23a7575 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3378,10 +3378,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, if (ret) return ret; - if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, - raw_black_frame) != 0) - return -ENOMEM; - + ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, + raw_black_frame); ia_css_frame_free(raw_black_frame); return ret; } From 0a41dd4aabc852692e9bc480e4a789683acd4777 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Mar 2026 13:18:26 +0100 Subject: [PATCH 1184/5214] media: atomisp: Fix alloc_pages_bulk() failed errors Systems with the atomisp ISP do not have a lot of memory for modern standards, so these are often under memory pressure and alloc_pages_bulk() does not try very hard to free pages before returning an amount of pages which is less then requested. This leads to streaming from the camera often failing with a "alloc_pages_bulk() failed" error. vmalloc() also uses alloc_pages_bulk(), but falls back to allocating one page at a time when that fails. Do the same in alloc_private_pages() to avoid these errors. While at it also drop the weird custom GFP flags and just use GFP_KERNEL and drop the dev_err() as alloc_pages() already complaints loudly if it fails itself. Closes: https://github.com/jfwells/linux-asus-t100ta/issues/4 Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/hmm/hmm_bo.c | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index 856561e951a5..b91cbd0262d0 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -620,14 +620,23 @@ static void free_private_bo_pages(struct hmm_buffer_object *bo) /*Allocate pages which will be used only by ISP*/ static int alloc_private_pages(struct hmm_buffer_object *bo) { - const gfp_t gfp = __GFP_NOWARN | __GFP_RECLAIM | __GFP_FS; + unsigned int nr_allocated = 0; + struct page *page; int ret; - ret = alloc_pages_bulk(gfp, bo->pgnr, bo->pages); - if (ret != bo->pgnr) { - free_pages_bulk_array(ret, bo->pages); - dev_err(atomisp_dev, "alloc_pages_bulk() failed\n"); - return -ENOMEM; + nr_allocated = alloc_pages_bulk(GFP_KERNEL, bo->pgnr, bo->pages); + /* + * alloc_pages_bulk() does not try very hard to get pages under memory + * pressure. If necessary fall back to alloc_page(). + */ + while (nr_allocated < bo->pgnr) { + page = alloc_pages(GFP_KERNEL, 0); + if (!page) { + free_pages_bulk_array(nr_allocated, bo->pages); + return -ENOMEM; + } + bo->pages[nr_allocated] = page; + nr_allocated++; } ret = set_pages_array_uc(bo->pages, bo->pgnr); From 5ed76a163b0ca078bad206b35eddd97360e2f0ca Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:05 +0500 Subject: [PATCH 1185/5214] media: atomisp: Remove redundant return statement The function mmu_reg_store() returns void. The final return statement is redundant as it is followed by the closing brace. Remove the redundant return statement to simplify code and adhere to kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/hive_isp_css_include/host/mmu_public.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 1a435a348318..2fc137ef46da 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -63,7 +63,6 @@ static inline void mmu_reg_store( assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); - return; } /*! Read from a control register of MMU[ID] From fff06b0538b1c5a690e8092757089a8078f94672 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:06 +0500 Subject: [PATCH 1186/5214] media: atomisp: Fix function signature alignment Fix checkpatch.pl warnings regarding lines ending with "(" and improper spacing for indentation. This change fixes the function signatures for both function prototypes and static inline function definations in mmu_public.h. The kernel coding style prefers arguments to start immediately after the open parenthesis. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 2fc137ef46da..90a2e4e8f510 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -18,9 +18,7 @@ \return none, MMU[ID].page_table_base_index = base_index */ -void mmu_set_page_table_base_index( - const mmu_ID_t ID, - const hrt_data base_index); +void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); /*! Get the page table base index of MMU[ID] @@ -29,8 +27,7 @@ void mmu_set_page_table_base_index( \return MMU[ID].page_table_base_index */ -hrt_data mmu_get_page_table_base_index( - const mmu_ID_t ID); +hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); /*! Invalidate the page table cache of MMU[ID] @@ -38,8 +35,7 @@ hrt_data mmu_get_page_table_base_index( \return none */ -void mmu_invalidate_cache( - const mmu_ID_t ID); +void mmu_invalidate_cache(const mmu_ID_t ID); /*! Invalidate the page table cache of all MMUs @@ -55,10 +51,7 @@ void mmu_invalidate_cache_all(void); \return none, MMU[ID].ctrl[reg] = value */ -static inline void mmu_reg_store( - const mmu_ID_t ID, - const unsigned int reg, - const hrt_data value) +static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); @@ -73,9 +66,7 @@ static inline void mmu_reg_store( \return MMU[ID].ctrl[reg] */ -static inline hrt_data mmu_reg_load( - const mmu_ID_t ID, - const unsigned int reg) +static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); From 2842e567b4b90645c2fda07f464f62624e89bc7d Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:07 +0500 Subject: [PATCH 1187/5214] media: atomisp: Fix block comment coding style Fix checkpatch.pl warnings regarding block comments. Add missing asterisks to block comments to adhere to the kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 90a2e4e8f510..c3495ec4835c 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -11,45 +11,50 @@ #include "device_access.h" #include "assert_support.h" -/*! Set the page table base index of MMU[ID] - - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return none, MMU[ID].page_table_base_index = base_index +/* + *! Set the page table base index of MMU[ID] + * + *\param ID[in] MMU identifier + *\param base_index[in] page table base index + * + *\return none, MMU[ID].page_table_base_index = base_index */ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); -/*! Get the page table base index of MMU[ID] - - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return MMU[ID].page_table_base_index +/* + *! Get the page table base index of MMU[ID] + * + *\param ID[in] MMU identifier + *\param base_index[in] page table base index + * + *\return MMU[ID].page_table_base_index */ hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); -/*! Invalidate the page table cache of MMU[ID] - - \param ID[in] MMU identifier - - \return none +/* + *! Invalidate the page table cache of MMU[ID] + * + *\param ID[in] MMU identifier + * + *\return none */ void mmu_invalidate_cache(const mmu_ID_t ID); -/*! Invalidate the page table cache of all MMUs - - \return none +/* + *! Invalidate the page table cache of all MMUs + * + *\return none */ void mmu_invalidate_cache_all(void); -/*! Write to a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return none, MMU[ID].ctrl[reg] = value +/* + *! Write to a control register of MMU[ID] + * + *\param ID[in] MMU identifier + *\param reg[in] register index + *\param value[in] The data to be written + * + *\return none, MMU[ID].ctrl[reg] = value */ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { @@ -58,13 +63,14 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); } -/*! Read from a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return MMU[ID].ctrl[reg] +/* + *! Read from a control register of MMU[ID] + * + *\param ID[in] MMU identifier + *\param reg[in] register index + *\param value[in] The data to be written + * + *\return MMU[ID].ctrl[reg] */ static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { From ffb694d75291b8d5433321bd16153bb973cf85bc Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:08 +0500 Subject: [PATCH 1188/5214] media: atomisp: Fix erroneous parameter descriptions The function mmu_get_page_table_base_index() accepts only one argument mmu_ID_t, the block comment for it shows an erroneous additional argument base_index[in]. Similarly, mmu_reg_load() only accepts two arguments, however the block comment explaining it shows an erroneous argument 'value[in]'. Remove incorrect documentation lines. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/hive_isp_css_include/host/mmu_public.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index c3495ec4835c..611755e88e9f 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -25,7 +25,6 @@ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index) *! Get the page table base index of MMU[ID] * *\param ID[in] MMU identifier - *\param base_index[in] page table base index * *\return MMU[ID].page_table_base_index */ @@ -68,7 +67,6 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons * *\param ID[in] MMU identifier *\param reg[in] register index - *\param value[in] The data to be written * *\return MMU[ID].ctrl[reg] */ From 33fea3423fbc91696dfeb6addf314cfb85af6ae0 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:09 +0500 Subject: [PATCH 1189/5214] media: atomisp: Convert comments to kernel-doc Existing comments in mmu_public.h used Doxygen syntax and had inconsistent formatting. Convert the function documentation to the standard kernel-doc format to adhere to the Linux kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 611755e88e9f..b321f4101193 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -11,49 +11,45 @@ #include "device_access.h" #include "assert_support.h" -/* - *! Set the page table base index of MMU[ID] +/** + * mmu_set_page_table_base_index() - Set the page table base index of MMU[ID] + * @ID: MMU identifier + * @base_index: page table base index * - *\param ID[in] MMU identifier - *\param base_index[in] page table base index - * - *\return none, MMU[ID].page_table_base_index = base_index + * Return: none, MMU[ID].page_table_base_index = base_index */ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); -/* - *! Get the page table base index of MMU[ID] +/** + * mmu_get_page_table_base_index() - Get the page table base index of MMU[ID] + * @ID: MMU identifier * - *\param ID[in] MMU identifier - * - *\return MMU[ID].page_table_base_index + * Return: MMU[ID].page_table_base_index */ hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); -/* - *! Invalidate the page table cache of MMU[ID] +/** + * mmu_invalidate_cache() - nvalidate the page table cache of MMU[ID] + * @ID: MMU identifier * - *\param ID[in] MMU identifier - * - *\return none + * Return: none */ void mmu_invalidate_cache(const mmu_ID_t ID); -/* - *! Invalidate the page table cache of all MMUs +/** + * mmu_invalidate_cache_all() - Invalidate the page table cache of all MMUs * - *\return none + * Return: none */ void mmu_invalidate_cache_all(void); -/* - *! Write to a control register of MMU[ID] +/** + * mmu_reg_store() - Write to a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index + * @value: The data to be written * - *\param ID[in] MMU identifier - *\param reg[in] register index - *\param value[in] The data to be written - * - *\return none, MMU[ID].ctrl[reg] = value + * Return: none, MMU[ID].ctrl[reg] = value */ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { @@ -62,13 +58,12 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); } -/* - *! Read from a control register of MMU[ID] +/** + * mmu_reg_load() - Read from a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index * - *\param ID[in] MMU identifier - *\param reg[in] register index - * - *\return MMU[ID].ctrl[reg] + * Return: MMU[ID].ctrl[reg] */ static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { From 2e6b2d43309758b4dff4074c9d6a2cb084066463 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Thu, 1 Jan 2026 21:41:17 +0500 Subject: [PATCH 1190/5214] media: atomisp: Fix block comment coding style in sh_css_param_shading.c Fix checkpatch.pl warnings in sh_css_param_shading.c regarding block comment formatting. The warning was: - Block comments use a trailing */ on a separate line This change also moves text from the first line of block comments to subsequent lines to adhere to the standard Linux kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/sh_css_param_shading.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 9105334c71b1..083ac5713b6d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -20,7 +20,8 @@ #include "platform_support.h" -/* Bilinear interpolation on shading tables: +/* + * Bilinear interpolation on shading tables: * For each target point T, we calculate the 4 surrounding source points: * ul (upper left), ur (upper right), ll (lower left) and lr (lower right). * We then calculate the distances from the T to the source points: x0, x1, @@ -116,8 +117,10 @@ crop_and_interpolate(unsigned int cropped_width, */ ty = out_start_row + i * out_cell_size; - /* calculate closest source points in shading table and - make sure they fall within the table */ + /* + * calculate closest source points in shading table and + * make sure they fall within the table + */ src_y0 = ty / (int)in_cell_size; if (in_cell_size < out_cell_size) src_y1 = (ty + out_cell_size) / in_cell_size; @@ -173,7 +176,8 @@ crop_and_interpolate(unsigned int cropped_width, dx0 = tx - sx0; dx1 = sx1 - tx; divx = sx1 - sx0; - /* if we're at the edge, we just use the closest + /* + * if we're at the edge, we just use the closest * point still in the grid. We make up for the divider * in this case by setting the distance to * out_cell_size, since it's actually 0. @@ -291,8 +295,10 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, input_width = min(input_width, in_table->sensor_width); input_height = min(input_height, in_table->sensor_height); - /* This prepare_shading_table() function is called only in legacy API (not in new API). - Then, the legacy shading table width and height should be used. */ + /* + * This prepare_shading_table() function is called only in legacy API (not in new API). + * Then, the legacy shading table width and height should be used. + */ table_width = binary->sctbl_width_per_color; table_height = binary->sctbl_height; From 2ebf05cc41603c23959752fa6fabb7f24444bae5 Mon Sep 17 00:00:00 2001 From: Karthikey Kadati Date: Thu, 15 Jan 2026 08:42:07 +0530 Subject: [PATCH 1191/5214] media: atomisp: replace ia_css_region with v4l2_rect The struct ia_css_region definition is redundant as struct v4l2_rect provides the same functionality (left, top, width, height) and is the standard V4L2 type. Replace usage of ia_css_region with v4l2_rect in ia_css_dz_config and remove the definition of ia_css_region from ia_css_types.h. Also remove historical comments referencing the addition of zoom_region and include to support the v4l2_rect type. Signed-off-by: Karthikey Kadati Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_cmd.c | 118 +++++++++--------- .../staging/media/atomisp/pci/ia_css_types.h | 14 +-- .../staging/media/atomisp/pci/sh_css_params.c | 18 ++- 3 files changed, 67 insertions(+), 83 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index e401c23a7575..7ccd4205ed1e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -1768,15 +1768,13 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, return -EINVAL; } - if (dz_config->zoom_region.resolution.width - == asd->sensor_array_res.width - || dz_config->zoom_region.resolution.height - == asd->sensor_array_res.height) { + if (dz_config->zoom_region.width == asd->sensor_array_res.width || + dz_config->zoom_region.height == asd->sensor_array_res.height) { /*no need crop region*/ - dz_config->zoom_region.origin.x = 0; - dz_config->zoom_region.origin.y = 0; - dz_config->zoom_region.resolution.width = eff_res.width; - dz_config->zoom_region.resolution.height = eff_res.height; + dz_config->zoom_region.left = 0; + dz_config->zoom_region.top = 0; + dz_config->zoom_region.width = eff_res.width; + dz_config->zoom_region.height = eff_res.height; return 0; } @@ -1787,18 +1785,14 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, */ if (!IS_ISP2401) { - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / asd->sensor_array_res.height; - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / asd->sensor_array_res.height; + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / asd->sensor_array_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / asd->sensor_array_res.height; /* * Set same ratio of crop region resolution and current pipe output * resolution @@ -1825,62 +1819,62 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, - asd->sensor_array_res.width * out_res.height / out_res.width; h_offset = h_offset / 2; - if (dz_config->zoom_region.origin.y < h_offset) - dz_config->zoom_region.origin.y = 0; + if (dz_config->zoom_region.top < h_offset) + dz_config->zoom_region.top = 0; else - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - h_offset; + dz_config->zoom_region.top = + dz_config->zoom_region.top - h_offset; w_offset = 0; } else { w_offset = asd->sensor_array_res.width - asd->sensor_array_res.height * out_res.width / out_res.height; w_offset = w_offset / 2; - if (dz_config->zoom_region.origin.x < w_offset) - dz_config->zoom_region.origin.x = 0; + if (dz_config->zoom_region.left < w_offset) + dz_config->zoom_region.left = 0; else - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - w_offset; + dz_config->zoom_region.left = + dz_config->zoom_region.left - w_offset; h_offset = 0; } - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); } - if (out_res.width * dz_config->zoom_region.resolution.height - > dz_config->zoom_region.resolution.width * out_res.height) { - dz_config->zoom_region.resolution.height = - dz_config->zoom_region.resolution.width - * out_res.height / out_res.width; + if (out_res.width * dz_config->zoom_region.height > + dz_config->zoom_region.width * out_res.height) { + dz_config->zoom_region.height = dz_config->zoom_region.width * + out_res.height / out_res.width; } else { - dz_config->zoom_region.resolution.width = - dz_config->zoom_region.resolution.height - * out_res.width / out_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.height * + out_res.width / out_res.height; } dev_dbg(asd->isp->dev, "%s crop region:(%d,%d),(%d,%d) eff_res(%d, %d) array_size(%d,%d) out_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, eff_res.width, eff_res.height, asd->sensor_array_res.width, asd->sensor_array_res.height, out_res.width, out_res.height); - if ((dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width + if ((dz_config->zoom_region.left + + dz_config->zoom_region.width > eff_res.width) || - (dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height + (dz_config->zoom_region.top + + dz_config->zoom_region.height > eff_res.height)) return -EINVAL; @@ -1905,10 +1899,10 @@ static bool atomisp_check_zoom_region( config.width = asd->sensor_array_res.width; config.height = asd->sensor_array_res.height; - w = dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width; - h = dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height; + w = dz_config->zoom_region.left + + dz_config->zoom_region.width; + h = dz_config->zoom_region.top + + dz_config->zoom_region.height; if ((w <= config.width) && (h <= config.height) && w > 0 && h > 0) flag = true; @@ -1916,10 +1910,10 @@ static bool atomisp_check_zoom_region( /* setting error zoom region */ dev_err(asd->isp->dev, "%s zoom region ERROR:dz_config:(%d,%d),(%d,%d)array_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, config.width, config.height); return flag; diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h index 676d7e20b282..2b7db9cda23a 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_types.h @@ -15,6 +15,8 @@ * directly but still need to forward parameters for it. */ +#include + #include #include "ia_css_frac.h" @@ -427,14 +429,6 @@ struct ia_css_point { s32 y; /** y coordinate */ }; -/** - * This specifies the region - */ -struct ia_css_region { - struct ia_css_point origin; /** Starting point coordinates for the region */ - struct ia_css_resolution resolution; /** Region resolution */ -}; - /** * Digital zoom: * This feature is currently available only for video, but will become @@ -442,7 +436,7 @@ struct ia_css_region { * Set the digital zoom factor, this is a logarithmic scale. The actual zoom * factor will be 64/x. * Setting dx or dy to 0 disables digital zoom for that direction. - * New API change for Digital zoom:(added struct ia_css_region zoom_region) + * * zoom_region specifies the origin of the zoom region and width and * height of that region. * origin : This is the coordinate (x,y) within the effective input resolution @@ -455,7 +449,7 @@ struct ia_css_region { struct ia_css_dz_config { u32 dx; /** Horizontal zoom factor */ u32 dy; /** Vertical zoom factor */ - struct ia_css_region zoom_region; /** region for zoom */ + struct v4l2_rect zoom_region; /** region for zoom */ }; /* The still capture mode, this can be RAW (simply copy sensor input to DDR), diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 8cbb5d598005..b8d492115196 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -657,11 +657,7 @@ static const int zoom_table[4][HRT_GDC_N] = { static const struct ia_css_dz_config default_dz_config = { HRT_GDC_N, HRT_GDC_N, - { - \ - {0, 0}, \ - {0, 0}, \ - } + { 0, 0, 0, 0 } }; static const struct ia_css_vector default_motion_config = { @@ -1210,8 +1206,8 @@ ia_css_process_zoom_and_motion( } assert(stage->stage_num < SH_CSS_MAX_STAGES); - if (params->dz_config.zoom_region.resolution.width == 0 && - params->dz_config.zoom_region.resolution.height == 0) { + if (params->dz_config.zoom_region.width == 0 && + params->dz_config.zoom_region.height == 0) { sh_css_update_uds_and_crop_info( &info->sp, &binary->in_frame_info, @@ -4092,10 +4088,10 @@ sh_css_update_uds_and_crop_info_based_on_zoom_region( assert(motion_vector); assert(uds); assert(sp_out_crop_pos); - x0 = zoom->zoom_region.origin.x; - y0 = zoom->zoom_region.origin.y; - x1 = zoom->zoom_region.resolution.width + x0; - y1 = zoom->zoom_region.resolution.height + y0; + x0 = zoom->zoom_region.left; + y0 = zoom->zoom_region.top; + x1 = zoom->zoom_region.width + x0; + y1 = zoom->zoom_region.height + y0; if ((x0 > x1) || (y0 > y1) || (x1 > pipe_in_res.width) || (y1 > pipe_in_res.height)) return -EINVAL; From 628f763aee0047ff44974388d6f70f75a763026b Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 2 Apr 2026 20:23:19 -0400 Subject: [PATCH 1192/5214] media: atomisp: gc2235: fix UAF and memory leak gc2235_probe() handles its error paths incorrectly. If media_entity_pads_init() fails, gc2235_remove() is called, which tears down the subdev and frees dev, but then still falls through to atomisp_register_i2c_module(). This results in use-after-free. If atomisp_register_i2c_module() fails, the media entity and control handler are left initialized and dev is leaked. gc2235_remove() unconditionally calls media_entity_cleanup() and v4l2_ctrl_handler_free(), but these are not initialized at every error path in gc2235_probe(). Replace gc2235_remove() calls in the probe error paths with explicit unwind labels that free only the resources initialized at each point of failure, in reverse order of initialization. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Yuho Choi Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- .../media/atomisp/i2c/atomisp-gc2235.c | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index d3414312e1de..998c9f46bd06 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client) ret = gc2235_s_config(&dev->sd, client->irq, gcpdev); if (ret) - goto out_free; + goto err_unregister_subdev; dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client) ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(gc2235_controls)); - if (ret) { - gc2235_remove(client); - return ret; - } + if (ret) + goto err_csi_cfg; for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i], NULL); if (dev->ctrl_handler.error) { - gc2235_remove(client); - return dev->ctrl_handler.error; + ret = dev->ctrl_handler.error; + goto err_ctrl_handler; } /* Use same lock for controls as for everything else. */ @@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client) ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); if (ret) - gc2235_remove(client); + goto err_ctrl_handler; - return atomisp_register_i2c_module(&dev->sd, gcpdev); + ret = atomisp_register_i2c_module(&dev->sd, gcpdev); + if (ret) + goto err_media_cleanup; -out_free: + return 0; + +err_media_cleanup: + media_entity_cleanup(&dev->sd.entity); +err_ctrl_handler: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +err_csi_cfg: + dev->platform_data->csi_cfg(&dev->sd, 0); +err_unregister_subdev: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); - return ret; } From ddc87e50b9611c509ffbeeef5e5f74282cbce709 Mon Sep 17 00:00:00 2001 From: Abinash Singh Date: Sun, 22 Mar 2026 12:36:14 +0530 Subject: [PATCH 1193/5214] staging: media: atomisp: replace uint32_t with u32 Replace usage of uint32_t with u32 to comply with kernel coding style guidelines. Reported by checkpatch.pl. CHECK: Prefer kernel type 'u32' over 'uint32_t' Signed-off-by: Abinash Singh Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_subdev.c | 18 +++++++++--------- .../staging/media/atomisp/pci/atomisp_subdev.h | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index cef44ec9ebde..9de9cd884d99 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -180,8 +180,8 @@ static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, - uint32_t target) +static int isp_subdev_validate_rect(struct v4l2_subdev *sd, u32 pad, + u32 target) { switch (pad) { case ATOMISP_SUBDEV_PAD_SINK: @@ -203,8 +203,8 @@ static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target) + u32 which, u32 pad, + u32 target) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -229,8 +229,8 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad) + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -242,7 +242,7 @@ struct v4l2_mbus_framefmt static void isp_get_fmt_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, struct v4l2_mbus_framefmt **ffmt, struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM], struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM]) @@ -291,7 +291,7 @@ static const char *atomisp_pad_str(unsigned int pad) int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -467,7 +467,7 @@ static int isp_subdev_set_selection(struct v4l2_subdev *sd, void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index e1d0168cb91d..b12bb65be3f2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -315,20 +315,20 @@ bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd); /* Get pointer to appropriate format */ struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad); + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad); struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target); + u32 which, u32 pad, + u32 target); int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r); /* Actually set the format */ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt); void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd); From c06b73f1e19176c0534cd4e1ee0afc044e9774d6 Mon Sep 17 00:00:00 2001 From: Anushka Badhe Date: Wed, 25 Mar 2026 18:54:34 +0530 Subject: [PATCH 1194/5214] staging: media: atomisp: pci: fix split GP_TIMER_BASE declaration Merge declaration of const GP_TIMER_BASE split across 2 lines to improve readability. Signed-off-by: Anushka Badhe Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/system_local.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/system_local.c b/drivers/staging/media/atomisp/pci/system_local.c index a8a93760d5b1..69bcd557a821 100644 --- a/drivers/staging/media/atomisp/pci/system_local.c +++ b/drivers/staging/media/atomisp/pci/system_local.c @@ -86,8 +86,7 @@ const hrt_address GP_DEVICE_BASE[N_GP_DEVICE_ID] = { /*GP TIMER , all timer registers are inter-twined, * so, having multiple base addresses for * different timers does not help*/ -const hrt_address GP_TIMER_BASE = - (hrt_address)0x0000000000000600ULL; +const hrt_address GP_TIMER_BASE = (hrt_address)0x0000000000000600ULL; /* GPIO */ const hrt_address GPIO_BASE[N_GPIO_ID] = { From e4e6bc933a63a2a4ed576e6449e455993f79cedd Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Sun, 5 Apr 2026 11:30:43 +0200 Subject: [PATCH 1195/5214] media: atomisp: gate ref and tnr frame config behind ISP enable flags The FIXME comment noted that delay_frames can be NULL for certain pipeline configurations, without knowing why. The reason is that when a binary does not enable ref_frame, delay frame allocation is intentionally skipped to save memory, leaving the pointers NULL by design. The ISP feature flags in binary->info->sp.enable accurately reflect which features are active for a given binary. Using enable.ref_frame and enable.tnr as the predicate for their respective configuration steps ensures the configuration path stays in sync with what was actually built into the pipeline Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_sp.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 6da151e7a873..abdffff41ae2 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -775,9 +775,13 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_fpn_configure(binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); - if (ret) - return ret; + + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); + if (ret) + return ret; + } + ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info); if (ret) return ret; @@ -807,22 +811,18 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, if (ret) return ret; - /* - * FIXME: args->delay_frames can be NULL here - * - * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to - * suffer from the same issue. - * - * Anyway, the function below should now handle a NULL delay_frames - * without crashing, but the pipeline should likely be built without - * adding it at the first place (or there are a hidden bug somewhere) - */ - ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); - if (ret) - return ret; - ret = ia_css_tnr_configure(binary, args->tnr_frames); - if (ret) - return ret; + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); + if (ret) + return ret; + } + + if (binary->info->sp.enable.tnr) { + ret = ia_css_tnr_configure(binary, args->tnr_frames); + if (ret) + return ret; + } + return ia_css_bayer_io_config(binary, args); } From 7d0e5f2ee1dc48def2e937415bf4dae3789767f9 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Sun, 5 Apr 2026 11:30:44 +0200 Subject: [PATCH 1196/5214] media: atomisp: remove redundant call to ia_css_output0_configure() The function configure_isp_from_args() contained a duplicate call to ia_css_output0_configure() using the same output frame index. Remove the redundant call to simplify the configuration path. The ia_css_output0_configure() function acts as a configuration setter. It populates a struct ia_css_output0_configuration from the frame info and caches it in the binary parameters. Calling it twice with the same out_frame[0] pointer merely overwrites the exact same state with identical values. It has no cumulative state, neither does its order matter relative to ia_css_copy_output_configure(). ia_css_configure_output0() writes into binary->mem_params.params[], a software-side DMEM parameter buffer in kernel memory. The ISP firmware receives these parameters later as a batch, not at the time of the call. Calling a pure memory write twice with the same pointer and same value simply overwrites the same location with identical data, there is no hardware interaction that could require repetition. Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_sp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index abdffff41ae2..2beb7168517f 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -792,9 +792,6 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, if (ret) return ret; ret = ia_css_copy_output_configure(binary, args->copy_output); - if (ret) - return ret; - ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame)); From 7488f3e0732b9226dbcaa09c74d14443a14de7a5 Mon Sep 17 00:00:00 2001 From: Mohamad El Harake Date: Fri, 10 Apr 2026 00:41:58 +0300 Subject: [PATCH 1197/5214] media: atomisp: avoid ACPI package count underflow in gmin_cfg_get_dsm gmin_cfg_get_dsm() iterates over ACPI _DSM package elements as key/value pairs using obj->package.count - 1 as the loop bound. If package.count is 0, the subtraction underflows and may lead to out-of-bounds access. Use i + 1 < obj->package.count instead. Signed-off-by: Mohamad El Harake Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index ba61cc28fac1..cca91c6d71a5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -113,7 +113,7 @@ static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key) if (!obj) return NULL; - for (i = 0; i < obj->package.count - 1; i += 2) { + for (i = 0; i + 1 < obj->package.count; i += 2) { key_el = &obj->package.elements[i + 0]; val_el = &obj->package.elements[i + 1]; From 79b29f7f26256ad16d2af7a53571c6d2ee2ffd60 Mon Sep 17 00:00:00 2001 From: Michael Ugrin Date: Sat, 11 Apr 2026 10:34:05 -0700 Subject: [PATCH 1198/5214] staging: media: atomisp: use umin() for strscpy size arguments Replace open-coded ternary min expressions with umin() in strscpy() calls, as suggested by Dan Carpenter. Signed-off-by: Michael Ugrin Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- .../atomisp/pci/runtime/debug/src/ia_css_debug.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 58d99abe70aa..5113aa5973f3 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -1255,8 +1255,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; /* Last comma found, copy till that comma */ - strscpy(enable_info1, ei, - p > sizeof(enable_info1) ? sizeof(enable_info1) : p); + strscpy(enable_info1, ei, umin(p, sizeof(enable_info1))); ei += p + 1; l = strlen(ei); @@ -1267,8 +1266,7 @@ ia_css_debug_pipe_graph_dump_stage( * it is not guaranteed dword aligned */ - strscpy(enable_info2, ei, - l > sizeof(enable_info2) ? sizeof(enable_info2) : l); + strscpy(enable_info2, ei, umin(l, sizeof(enable_info2))); snprintf(enable_info, sizeof(enable_info), "%s\\n%s", enable_info1, enable_info2); @@ -1279,8 +1277,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; - strscpy(enable_info2, ei, - p > sizeof(enable_info2) ? sizeof(enable_info2) : p); + strscpy(enable_info2, ei, umin(p, sizeof(enable_info2))); ei += p + 1; l = strlen(ei); @@ -1302,7 +1299,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; strscpy(enable_info3, ei, - p > sizeof(enable_info3) ? sizeof(enable_info3) : p); + umin(p, sizeof(enable_info3))); ei += p + 1; strscpy(enable_info3, ei, sizeof(enable_info3)); From 9087395a383212ab1beaefcbe3b57ed131c7823d Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:07 +0100 Subject: [PATCH 1199/5214] staging: media: atomisp: fix loop shadowing in ia_css_stream_destroy() The nested loop inside the IS_ISP2401 block incorrectly uses the same variable 'i' as the outer loop. This shadows the outer loop variable and causes premature termination or skipped array elements. Change the inner loop to use a new variable 'j' to prevent this. Fixes: 113401c67386 ("media: atomisp: sh_css: Removed #ifdef ISP2401 to make code generic") Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index f40b6912c14b..456c0b2df231 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8190,7 +8190,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i; + int i, j; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8221,10 +8221,10 @@ ia_css_stream_destroy(struct ia_css_stream *stream) sp_pipeline_input_terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) { + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]; - if (stream->config.isys_config[i].valid && isys_stream->valid) + &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) ia_css_isys_stream_destroy(isys_stream); } } From d133bd6ac9beb934875f924e61900990ceb38678 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:08 +0100 Subject: [PATCH 1200/5214] staging: media: atomisp: extract ISP2401 cleanup into helper function To reduce indentation and improve the readability of ia_css_stream_destroy(), extract the ISP2401-specific cleanup block into a new static helper function, ia_css_stream_destroy_isp2401(). Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 89 +++++++++++----------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 456c0b2df231..dcfb602c53e3 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8187,10 +8187,53 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, return err; } +static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) +{ + int i, j; + + for (i = 0; i < stream->num_pipes; i++) { + struct ia_css_pipe *entry = stream->pipes[i]; + unsigned int sp_thread_id; + struct sh_css_sp_pipeline_terminal *terminal; + + assert(entry); + if (entry) { + if (!ia_css_pipeline_get_sp_thread_id( + ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) + return; + + terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; + + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { + ia_css_isys_stream_h isys_stream = + &terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) + ia_css_isys_stream_destroy(isys_stream); + } + } + } + + if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { + for (i = 0; i < stream->num_pipes; i++) { + struct ia_css_pipe *entry = stream->pipes[i]; + /* + * free any mipi frames that are remaining: + * some test stream create-destroy cycles do + * not generate output frames + * and the mipi buffer is not freed in the + * deque function + */ + if (entry) + free_mipi_frames(entry); + } + } + stream_unregister_with_csi_rx(stream); +} + int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i, j; + int i; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8204,48 +8247,8 @@ ia_css_stream_destroy(struct ia_css_stream *stream) if ((stream->last_pipe) && ia_css_pipeline_is_mapped(stream->last_pipe->pipe_num)) { - if (IS_ISP2401) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - unsigned int sp_thread_id; - struct sh_css_sp_pipeline_terminal *sp_pipeline_input_terminal; - - assert(entry); - if (entry) { - /* get the SP thread id */ - if (!ia_css_pipeline_get_sp_thread_id( - ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) - return -EINVAL; - - /* get the target input terminal */ - sp_pipeline_input_terminal = - &sh_css_sp_group.pipe_io[sp_thread_id].input; - - for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { - ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; - if (stream->config.isys_config[j].valid && isys_stream->valid) - ia_css_isys_stream_destroy(isys_stream); - } - } - } - - if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - /* - * free any mipi frames that are remaining: - * some test stream create-destroy cycles do - * not generate output frames - * and the mipi buffer is not freed in the - * deque function - */ - if (entry) - free_mipi_frames(entry); - } - } - stream_unregister_with_csi_rx(stream); - } + if (IS_ISP2401) + ia_css_stream_destroy_isp2401(stream); for (i = 0; i < stream->num_pipes; i++) { struct ia_css_pipe *curr_pipe = stream->pipes[i]; From f14d52b67e63993e2d184da5f49d884d527dd475 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:09 +0100 Subject: [PATCH 1201/5214] staging: media: atomisp: improve cleanup robustness in ia_css_stream_destroy_isp2401() Remove the 'assert(entry)' call. In the atomisp driver, assert() is a wrapper around BUG(), which intentionally crashes the entire kernel. This is dangerous and inappropriate for a simple null pointer check during stream teardown. Replace it with a safe 'if (!entry) continue;' check. Change the early 'return' to a 'continue'. In a destruction path, it is better to proceed with cleaning up as many resources as possible rather than aborting early, which would result in memory leaks for the remaining pipes. Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index dcfb602c53e3..c6838772553d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8196,26 +8196,25 @@ static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) unsigned int sp_thread_id; struct sh_css_sp_pipeline_terminal *terminal; - assert(entry); - if (entry) { - if (!ia_css_pipeline_get_sp_thread_id( + if (!entry) + continue; + + if (!ia_css_pipeline_get_sp_thread_id( ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) - return; + continue; - terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; + terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { - ia_css_isys_stream_h isys_stream = - &terminal->context.virtual_input_system_stream[j]; - if (stream->config.isys_config[j].valid && isys_stream->valid) - ia_css_isys_stream_destroy(isys_stream); - } + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { + ia_css_isys_stream_h isys_stream = + &terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) + ia_css_isys_stream_destroy(isys_stream); } } if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; /* * free any mipi frames that are remaining: * some test stream create-destroy cycles do @@ -8223,8 +8222,8 @@ static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) * and the mipi buffer is not freed in the * deque function */ - if (entry) - free_mipi_frames(entry); + if (stream->pipes[i]) + free_mipi_frames(stream->pipes[i]); } } stream_unregister_with_csi_rx(stream); From afe9021d63b46233f5c87d52b820fa26e7f562cd Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 5 May 2026 16:08:34 +0200 Subject: [PATCH 1202/5214] thunderbolt: Improve multi-display DisplayPort tunnel allocation When 3 monitors are connected through Thunderbolt dock to the system at once, one of the monitors might fail to establish DisplayPort tunnel. This happens during DP bandwidth negotiation - each monitor takes maximum bandwidth that is supported and there might not be enough for 3rd display. In this case Thunderbolt driver drops DP tunnel and 'forgets' about it but with DP bandwidth allocation mode, that comes in later, some bandwidth might be freed. Make Thunderbolt driver check again if DP tunnel can be established after DP bandwidth consumption changed. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index a9d26a2ec259..72a0dd27937e 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -2849,6 +2849,9 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work) /* Update other clients about the allocation change */ tb_recalc_estimated_bandwidth(tb); + + tb_dbg(tb, "checking if more DP tunnels can be established now\n"); + tb_tunnel_dp(tb); } put_sw: From 95c4379e37a0abea72dfd389cfe2c54452523690 Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Thu, 7 May 2026 14:46:30 -0700 Subject: [PATCH 1203/5214] thunderbolt: Don't access path config space on Lane 1 adapters in tb_switch_reset_host() USB4 Lane 1 adapters do not have accessible path config space. Skip the path config space cleanup in tb_switch_reset_host() for these ports. The check is for USB4 switches only. Thunderbolt 1-3 Lane 1 adapters stay as is because we do need to program their path config space. Co-developed-by: Rene Sapiens Signed-off-by: Rene Sapiens Signed-off-by: Pooja Katiyar Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index bfcab98faf4b..ad0ec8f8ee28 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1620,6 +1620,12 @@ static int tb_switch_reset_host(struct tb_switch *sw) ret = tb_port_reset(port); if (ret) return ret; + /* + * USB4 Lane 1 adapters do not have accessible + * path config space. + */ + if (tb_switch_is_usb4(sw) && !port->usb4) + continue; } else if (tb_port_is_usb3_down(port) || tb_port_is_usb3_up(port)) { tb_usb3_port_enable(port, false); From 0ab47718345dc58a6e2ff4c1c23a9026a2cf1310 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:04 +0300 Subject: [PATCH 1204/5214] thunderbolt: Fix lane bonding log when bonding not possible Currently if lane bonding is not possible or not supported, we continue and read the updated number of Total Buffers from lane adapters unnecessarily and incorrectly log the bonding as successful. Fix this by bailing out early when bonding is not possible, avoiding the unnecessary read and the misleading log message. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index ad0ec8f8ee28..f421997c298d 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2977,14 +2977,14 @@ static int tb_switch_lane_bonding_enable(struct tb_switch *sw) int ret; if (!tb_switch_lane_bonding_possible(sw)) - return 0; + return -EOPNOTSUPP; up = tb_upstream_port(sw); down = tb_switch_downstream_port(sw); if (!tb_port_width_supported(up, TB_LINK_WIDTH_DUAL) || !tb_port_width_supported(down, TB_LINK_WIDTH_DUAL)) - return 0; + return -EOPNOTSUPP; /* * Both lanes need to be in CL0. Here we assume lane 0 already be in From b69af182b55665f5b0ccca8ed1ed239758671354 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:05 +0300 Subject: [PATCH 1205/5214] thunderbolt: Activate path hops from source to destination Currently, path activation starts from the last hop (destination adapter) and iterates backwards to the first hop (source adapter). This does not follow the order suggested in the USB4 Connection Manager guide and could potentially cause issues with tunnelled protocols. Reverse the activation order to start from the first hop (source adapter) and end at the last hop (destination adapter), as suggested in the Connection Manager guide. Adjust the rollback in the failure path to deactivate from the first hop, since hops are now activated starting at the source. Fix kernel-doc accordingly. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/path.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 0092b2ec7873..b2c322e76b8a 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -484,7 +484,7 @@ void tb_path_deactivate(struct tb_path *path) * tb_path_activate() - activate a path * @path: Path to activate * - * Activate a path starting with the last hop and iterating backwards. The + * Activate a path starting with the first hop and ending on the last hop. The * caller must fill path->hops before calling tb_path_activate(). * * Return: %0 on success, negative errno otherwise. @@ -526,7 +526,7 @@ int tb_path_activate(struct tb_path *path) } /* Activate hops. */ - for (i = path->path_length - 1; i >= 0; i--) { + for (i = 0; i < path->path_length; i++) { struct tb_regs_hop hop = { 0 }; /* If it is left active deactivate it first */ @@ -576,7 +576,7 @@ int tb_path_activate(struct tb_path *path) res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, 2 * path->hops[i].in_hop_index, 2); if (res) { - __tb_path_deactivate_hops(path, i); + __tb_path_deactivate_hops(path, 0); __tb_path_deallocate_nfc(path, 0); goto err; } From 69a7b98770b7e80deec0465d97710611a0e51774 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:06 +0300 Subject: [PATCH 1206/5214] thunderbolt: Verify PCIe adapter in detect state before tunnel setup The USB4 Connection Manager guide suggests that a PCIe downstream and PCIe upstream adapters of the USB4 router is in the Detect state before setting up a PCIe tunnel. Add this check by verifying the LTSSM field in ADP_PCIE_CS_0 before tunnel setup. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.h | 1 + drivers/thunderbolt/tb_regs.h | 15 +++++++++++++++ drivers/thunderbolt/tunnel.c | 35 +++++++++++++++++++++++++++++++++++ drivers/thunderbolt/usb4.c | 24 ++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e60f1bc3764e..003db653418a 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1481,6 +1481,7 @@ int usb4_dp_port_allocate_bandwidth(struct tb_port *port, int bw); int usb4_dp_port_requested_bandwidth(struct tb_port *port); int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable); +int usb4_pci_port_ltssm_state(struct tb_port *port); static inline bool tb_is_usb4_port_device(const struct device *dev) { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index c0bf136236e6..75131fcfe044 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -473,10 +473,25 @@ struct tb_regs_port_header { /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 +#define ADP_PCIE_CS_0_LTSSM_MASK GENMASK(28, 25) #define ADP_PCIE_CS_0_PE BIT(31) #define ADP_PCIE_CS_1 0x01 #define ADP_PCIE_CS_1_EE BIT(0) +enum tb_pcie_ltssm_state { + USB4_PCIE_LTSSM_DETECT, + USB4_PCIE_LTSSM_POLLING, + USB4_PCIE_LTSSM_CONFIG, + USB4_PCIE_LTSSM_CONFIG_IDLE, + USB4_PCIE_LTSSM_RECOVERY, + USB4_PCIE_LTSSM_RECOVERY_IDLE, + USB4_PCIE_LTSSM_L0, + USB4_PCIE_LTSSM_L1, + USB4_PCIE_LTSSM_L2, + USB4_PCIE_LTSSM_DISABLED, + USB4_PCIE_LTSSM_HOT_RESET, +}; + /* USB adapter registers */ #define ADP_USB3_CS_0 0x00 #define ADP_USB3_CS_0_V BIT(30) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index f38f7753b6e4..b7f32305f14a 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -290,6 +290,40 @@ static inline void tb_tunnel_changed(struct tb_tunnel *tunnel) tunnel->src_port, tunnel->dst_port); } +static int tb_pci_port_ltssm_state_detect(struct tb_port *port) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), 500); + + do { + int ret; + + ret = usb4_pci_port_ltssm_state(port); + if (ret < 0) + return ret; + if (ret == USB4_PCIE_LTSSM_DETECT) + return 0; + + fsleep(50); + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +static int tb_pci_pre_activate(struct tb_tunnel *tunnel) +{ + struct tb_port *down = tunnel->src_port; + struct tb_port *up = tunnel->dst_port; + int ret; + + ret = tb_switch_is_usb4(down->sw) ? + tb_pci_port_ltssm_state_detect(down) : 0; + if (ret) + return ret; + + return tb_switch_is_usb4(up->sw) ? + tb_pci_port_ltssm_state_detect(up) : 0; +} + static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) { struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); @@ -505,6 +539,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, if (!tunnel) return NULL; + tunnel->pre_activate = tb_pci_pre_activate; tunnel->activate = tb_pci_activate; tunnel->src_port = down; tunnel->dst_port = up; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 9e810b2ae0b5..6f76bcaefa49 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -3145,3 +3145,27 @@ int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable) return tb_port_write(port, &val, TB_CFG_PORT, port->cap_adap + ADP_PCIE_CS_1, 1); } + +/** + * usb4_pci_port_ltssm_state() - Read PCIe adapter LTSSM state + * @port: PCIe adapter + * + * Return: + * * LTSSM state of @port. + * * Negative errno - On failure. + */ +int usb4_pci_port_ltssm_state(struct tb_port *port) +{ + u32 val; + int ret; + + if (!tb_port_is_pcie_down(port) && !tb_port_is_pcie_up(port)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_0, 1); + if (ret) + return ret; + + return FIELD_GET(ADP_PCIE_CS_0_LTSSM_MASK, val); +} From 062023c4364ffdc72978ed2de1d1435e5d4eee43 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:07 +0300 Subject: [PATCH 1207/5214] thunderbolt: Verify Router Ready bit is set after router enumeration The USB4 Connection Manager guide specifies that after enumerating a router, the Connection Manager shall verify that the Router Ready bit (ROUTER_CS_6.RR) has been set to ensure hardware configuration has completed. Currently, this step is missing from the enumeration sequence. Add this check to follow the Connection Manager guide more closely. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb_regs.h | 1 + drivers/thunderbolt/usb4.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 75131fcfe044..69ca4c379cc9 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -216,6 +216,7 @@ struct tb_regs_switch_header { #define ROUTER_CS_6_WOPS BIT(2) #define ROUTER_CS_6_WOUS BIT(3) #define ROUTER_CS_6_HCI BIT(18) +#define ROUTER_CS_6_RR BIT(24) #define ROUTER_CS_6_CR BIT(25) #define ROUTER_CS_7 0x07 #define ROUTER_CS_9 0x09 diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 6f76bcaefa49..54f4f5fa3c5a 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -294,7 +294,12 @@ int usb4_switch_setup(struct tb_switch *sw) /* TBT3 supported by the CM */ val &= ~ROUTER_CS_5_CNS; - return tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + + return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_RR, + ROUTER_CS_6_RR, 500); } /** From ba2cc385110129d03cd0f18a1b5969a430b67a18 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:08 +0300 Subject: [PATCH 1208/5214] thunderbolt: Increase timeout for Configuration Ready bit After setting the Configuration Valid bit (ROUTER_CS_5.CV), the USB4 Connection Manager guide specifies a 500 ms timeout for the router to set the Configuration Ready bit (ROUTER_CS_6.CR). The current timeout is shorter than specified. While there, fix the kernel-doc typo. Increase the timeout to match the CM guide recommendation. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/usb4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 54f4f5fa3c5a..5fd4fe070f25 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -309,7 +309,7 @@ int usb4_switch_setup(struct tb_switch *sw) * Sets configuration valid bit for the router. Must be called before * any tunnels can be set through the router and after * usb4_switch_setup() has been called. Can be called to host and device - * routers (does nothing for the latter). + * routers (does nothing for the former). * * Return: %0 on success, negative errno otherwise. */ @@ -332,7 +332,7 @@ int usb4_switch_configuration_valid(struct tb_switch *sw) return ret; return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_CR, - ROUTER_CS_6_CR, 50); + ROUTER_CS_6_CR, 500); } /** From e24f3c0df48378214d9a67c5048d0faca144b163 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 6 May 2026 15:37:09 +0300 Subject: [PATCH 1209/5214] thunderbolt: Increase Notification Timeout to 255 ms for USB4 routers Currently we set the Notification Timeout field in ROUTER_CS_4 for USB4 routers to 10 ms, which is unnecessarily short and may cause unnecessary retransmissions of Hot Plug packets by the router in case of slow software response. Increase the timeout to 255 ms, aligning with Thunderbolt 3 routers and providing adequate time for software to process Hot Plug Events. While there, fix the comment describing the Notification Timeout field to match the USB4 specification. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 12 +++--------- drivers/thunderbolt/tb_regs.h | 3 +-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index f421997c298d..d7c53eb3221b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1767,8 +1767,6 @@ int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, /* * tb_plug_events_active() - enable/disable plug events on a switch * - * Also configures a sane plug_events_delay of 255ms. - * * Return: %0 on success, negative errno otherwise. */ static int tb_plug_events_active(struct tb_switch *sw, bool active) @@ -1779,11 +1777,6 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active) if (tb_switch_is_icm(sw) || tb_switch_is_usb4(sw)) return 0; - sw->config.plug_events_delay = 0xff; - res = tb_sw_write(sw, ((u32 *) &sw->config) + 4, TB_CFG_SWITCH, 4, 1); - if (res) - return res; - res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1); if (res) return res; @@ -2645,6 +2638,8 @@ int tb_switch_configure(struct tb_switch *sw) sw->config.enabled = 1; + /* Set Notification Timeout to 255 ms for all routers */ + sw->config.plug_events_delay = 0xff; if (tb_switch_is_usb4(sw)) { /* * For USB4 devices, we need to program the CM version @@ -2656,7 +2651,6 @@ int tb_switch_configure(struct tb_switch *sw) sw->config.cmuv = ROUTER_CS_4_CMUV_V1; else sw->config.cmuv = ROUTER_CS_4_CMUV_V2; - sw->config.plug_events_delay = 0xa; /* Enumerate the switch */ ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH, @@ -2677,7 +2671,7 @@ int tb_switch_configure(struct tb_switch *sw) /* Enumerate the switch */ ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH, - ROUTER_CS_1, 3); + ROUTER_CS_1, 4); } if (ret) return ret; diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 69ca4c379cc9..92d893634d2b 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -182,8 +182,7 @@ struct tb_regs_switch_header { /* DWORD 4 */ u32 plug_events_delay:8; /* * RW, pause between plug events in - * milliseconds. Writing 0x00 is interpreted - * as 255ms. + * milliseconds. */ u32 cmuv:8; u32 __unknown4:8; From 5cfb132da0afd3a711e2d5279243100102d2e836 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 May 2026 13:47:29 +0200 Subject: [PATCH 1210/5214] platform/x86: xo15-ebook: Use devres-based resource management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_kzalloc() and devm_input_allocate_device() in ebook_switch_probe() for allocating the button object and the input device, respectively, to simplify the rollback path in that function and ebook_switch_remove(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/6015220.DvuYhMxLoT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 43 ++++++++++--------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 2ca15f04b2d9..e40e385c52bd 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -82,30 +82,28 @@ static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); static int ebook_switch_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); const struct acpi_device_id *id; struct ebook_switch *button; struct input_dev *input; int error; - button = kzalloc_obj(struct ebook_switch); + button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL); if (!button) return -ENOMEM; platform_set_drvdata(pdev, button); - button->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_button; - } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + button->input = input; id = acpi_match_acpi_device(ebook_device_ids, device); - if (!id) { - dev_err(&pdev->dev, "Unsupported hid\n"); - error = -ENODEV; - goto err_free_input; - } + if (!id) + return dev_err_probe(dev, -ENODEV, "Unsupported hid\n"); strscpy(acpi_device_name(device), XO15_EBOOK_DEVICE_NAME); strscpy(acpi_device_class(device), XO15_EBOOK_CLASS "/" XO15_EBOOK_SUBCLASS); @@ -115,21 +113,20 @@ static int ebook_switch_probe(struct platform_device *pdev) input->name = acpi_device_name(device); input->phys = button->phys; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; input->evbit[0] = BIT_MASK(EV_SW); set_bit(SW_TABLET_MODE, input->swbit); error = input_register_device(input); if (error) - goto err_free_input; + return error; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - ebook_switch_notify, &pdev->dev); + ebook_switch_notify, dev); if (error) - goto err_unregister_input; + return error; - ebook_send_state(&pdev->dev); + ebook_send_state(dev); if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ @@ -139,16 +136,6 @@ static int ebook_switch_probe(struct platform_device *pdev) } return 0; - -err_free_input: - input_free_device(input); -err_free_button: - kfree(button); - return error; - -err_unregister_input: - input_unregister_device(input); - goto err_free_button; } static void ebook_switch_remove(struct platform_device *pdev) @@ -162,8 +149,6 @@ static void ebook_switch_remove(struct platform_device *pdev) acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, ebook_switch_notify); - input_unregister_device(button->input); - kfree(button); } static struct platform_driver xo15_ebook_driver = { From a4173887605121f61a5222911b11ac598336618e Mon Sep 17 00:00:00 2001 From: Sakurai Shun Date: Sun, 17 May 2026 11:41:44 +0900 Subject: [PATCH 1211/5214] docs: fix typo in uniwill-laptop.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "benifit" with "benefit". Signed-off-by: Sakurai Shun Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260517024148.9642-1-ssh1326@icloud.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/wmi/devices/uniwill-laptop.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/wmi/devices/uniwill-laptop.rst b/Documentation/wmi/devices/uniwill-laptop.rst index e246bf293450..65583b239706 100644 --- a/Documentation/wmi/devices/uniwill-laptop.rst +++ b/Documentation/wmi/devices/uniwill-laptop.rst @@ -189,7 +189,7 @@ Indexed IO Indexed IO with IO ports with a granularity of a single byte can be performed using the ``RIOP`` (read) and ``WIOP`` (write) ACPI control methods. Those ACPI methods are unused because they -provide no benifit when compared to the native IO port access functions provided by the kernel. +provide no benefit when compared to the native IO port access functions provided by the kernel. Special thanks go to github user `pobrn` which developed the `qc71_laptop `_ driver on which this driver is partly based. From 4baf44b4051940ba1abc68ef5136d25cb1806521 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:03:34 +0200 Subject: [PATCH 1212/5214] platform/x86: classmate-laptop: Address memory leaks on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch over cmpc_accel_add_v4() and cmpc_accel_add() to using devm_kzalloc() for allocating the accel object which will cause it to be freed automatically on device removal, so it won't be leaked any more. This also simplifies the rollback paths in these functions somewhat. Fixes: 529aa8cb0a59 ("classmate-laptop: add support for Classmate PC ACPI devices") Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/10846403.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index e6eed3d65580..a5fe34211afb 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -400,7 +400,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -411,7 +411,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); if (error) - goto failed_sensitivity; + return error; accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); @@ -434,8 +434,6 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); -failed_sensitivity: - kfree(accel); return error; } @@ -650,7 +648,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -659,7 +657,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); if (error) - goto failed_file; + return error; error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", cmpc_accel_idev_init); @@ -673,8 +671,6 @@ static int cmpc_accel_add(struct acpi_device *acpi) failed_input: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); -failed_file: - kfree(accel); return error; } From 87b3892ddc3ac5c62eaa97a1662f6f996d6d4870 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:04:39 +0200 Subject: [PATCH 1213/5214] platform/x86: classmate-laptop: Unify probe rollback and remove code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent modifications, change code ordering in cmpc_accel_add_v4(), cmpc_accel_add(), and cmpc_accel_remove_v4() so that the ordering of the probe rollback code is the same as the ordering of the corresponding removal code. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/2036641.PYKUYFuaPT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 40 +++++++++++++------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index a5fe34211afb..d1407a28c1de 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -406,12 +406,19 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", + cmpc_accel_idev_init_v4); + if (error) + return error; + + inputdev = dev_get_drvdata(&acpi->dev); + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); if (error) - return error; + goto failed_sensitivity; accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); @@ -420,27 +427,21 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) if (error) goto failed_g_select; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", - cmpc_accel_idev_init_v4); - if (error) - goto failed_input; - - inputdev = dev_get_drvdata(&acpi->dev); dev_set_drvdata(&inputdev->dev, accel); return 0; -failed_input: - device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); +failed_sensitivity: + cmpc_remove_acpi_notify_device(acpi); return error; } static void cmpc_accel_remove_v4(struct acpi_device *acpi) { - device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); cmpc_remove_acpi_notify_device(acpi); } @@ -652,25 +653,26 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", + cmpc_accel_idev_init); + if (error) + return error; + + inputdev = dev_get_drvdata(&acpi->dev); + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); if (error) - return error; + goto failed_file; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", - cmpc_accel_idev_init); - if (error) - goto failed_input; - - inputdev = dev_get_drvdata(&acpi->dev); dev_set_drvdata(&inputdev->dev, accel); return 0; -failed_input: - device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); +failed_file: + cmpc_remove_acpi_notify_device(acpi); return error; } From daca81d9ead06d85b749d6036ea8c4940e7ac128 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:06:59 +0200 Subject: [PATCH 1214/5214] platform/x86: classmate-laptop: Pass struct device pointer to helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to using struct platform_driver instead of struct acpi_driver, modify two helper functions in it, cmpc_add_acpi_notify_device() and cmpc_remove_acpi_notify_device(), to take a struct device pointer argument instead of a struct acpi_device pointer argument and update their callers accordingly. While at it, change the return type of cmpc_remove_acpi_notify_device() to void because its return value is never checked. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/9615385.CDJkKcVGEf@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 32 ++++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index d1407a28c1de..6521d42f8204 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -38,7 +38,7 @@ struct cmpc_accel { typedef void (*input_device_init)(struct input_dev *dev); -static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, +static int cmpc_add_acpi_notify_device(struct device *dev, char *name, input_device_init idev_init) { struct input_dev *inputdev; @@ -48,22 +48,20 @@ static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, if (!inputdev) return -ENOMEM; inputdev->name = name; - inputdev->dev.parent = &acpi->dev; + inputdev->dev.parent = dev; idev_init(inputdev); error = input_register_device(inputdev); if (error) { input_free_device(inputdev); return error; } - dev_set_drvdata(&acpi->dev, inputdev); + dev_set_drvdata(dev, inputdev); return 0; } -static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) +static void cmpc_remove_acpi_notify_device(struct device *dev) { - struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); - input_unregister_device(inputdev); - return 0; + input_unregister_device(dev_get_drvdata(dev)); } /* @@ -406,7 +404,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", + error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; @@ -434,7 +432,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); return error; } @@ -442,7 +440,7 @@ static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -653,7 +651,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", + error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; @@ -672,14 +670,14 @@ static int cmpc_accel_add(struct acpi_device *acpi) return 0; failed_file: - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); return error; } static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -750,13 +748,13 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", + return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); } static void cmpc_tablet_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } #ifdef CONFIG_PM_SLEEP @@ -1074,13 +1072,13 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", + return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); } static void cmpc_keys_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { From 5658770e6eb5d9cf8d077054120545a98d7316ee Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:07:31 +0200 Subject: [PATCH 1215/5214] platform/x86: classmate-laptop: Rename two helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since cmpc_add_acpi_notify_device() and cmpc_remove_acpi_notify_device() have been modified to take a plain struct device pointer as the first argument, then have nothing to do with ACPI in principle, so rename them to cmpc_add_notify_device() and cmpc_remove_notify_device(), respectively, and consolidate white space around the call sites of the former. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3338539.5fSG56mABF@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 30 +++++++++++-------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 6521d42f8204..bc229771e7ba 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -38,8 +38,8 @@ struct cmpc_accel { typedef void (*input_device_init)(struct input_dev *dev); -static int cmpc_add_acpi_notify_device(struct device *dev, char *name, - input_device_init idev_init) +static int cmpc_add_notify_device(struct device *dev, char *name, + input_device_init idev_init) { struct input_dev *inputdev; int error; @@ -59,7 +59,7 @@ static int cmpc_add_acpi_notify_device(struct device *dev, char *name, return 0; } -static void cmpc_remove_acpi_notify_device(struct device *dev) +static void cmpc_remove_notify_device(struct device *dev) { input_unregister_device(dev_get_drvdata(dev)); } @@ -404,8 +404,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel_v4", - cmpc_accel_idev_init_v4); + error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; @@ -432,7 +431,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); return error; } @@ -440,7 +439,7 @@ static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -651,8 +650,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; - error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel", - cmpc_accel_idev_init); + error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; @@ -670,14 +668,14 @@ static int cmpc_accel_add(struct acpi_device *acpi) return 0; failed_file: - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); return error; } static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -748,13 +746,12 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_tablet", - cmpc_tablet_idev_init); + return cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); } static void cmpc_tablet_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } #ifdef CONFIG_PM_SLEEP @@ -1072,13 +1069,12 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_keys", - cmpc_keys_idev_init); + return cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); } static void cmpc_keys_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { From b32123a11dd706c3d0eaa48306a6000666ec11fe Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:08:22 +0200 Subject: [PATCH 1216/5214] platform/x86: classmate-laptop: Register ACPI notify handlers directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to using struct platform_driver instead of struct acpi_driver, make it install its ACPI notify handlers directly instead of using struct acpi_driver .notify() callbacks. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/1856277.VLH7GnMWUR@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 66 +++++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index bc229771e7ba..8507483b23f9 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -177,8 +177,10 @@ static acpi_status cmpc_get_accel_v4(acpi_handle handle, return status; } -static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event) +static void cmpc_accel_handler_v4(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; + if (event == 0x81) { int16_t x, y, z; acpi_status status; @@ -424,10 +426,17 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) if (error) goto failed_g_select; + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler_v4, acpi); + if (error) + goto failed_notify_handler; + dev_set_drvdata(&inputdev->dev, accel); return 0; +failed_notify_handler: + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: @@ -437,6 +446,8 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) static void cmpc_accel_remove_v4(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); cmpc_remove_notify_device(&acpi->dev); @@ -457,7 +468,6 @@ static struct acpi_driver cmpc_accel_acpi_driver_v4 = { .ops = { .add = cmpc_accel_add_v4, .remove = cmpc_accel_remove_v4, - .notify = cmpc_accel_handler_v4, }, .drv.pm = &cmpc_accel_pm, }; @@ -539,8 +549,10 @@ static acpi_status cmpc_get_accel(acpi_handle handle, return status; } -static void cmpc_accel_handler(struct acpi_device *dev, u32 event) +static void cmpc_accel_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; + if (event == 0x81) { unsigned char x, y, z; acpi_status status; @@ -663,10 +675,17 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (error) goto failed_file; + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler, acpi); + if (error) + goto failed_notify_handler; + dev_set_drvdata(&inputdev->dev, accel); return 0; +failed_notify_handler: + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); failed_file: cmpc_remove_notify_device(&acpi->dev); return error; @@ -674,6 +693,8 @@ static int cmpc_accel_add(struct acpi_device *acpi) static void cmpc_accel_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); cmpc_remove_notify_device(&acpi->dev); } @@ -690,7 +711,6 @@ static struct acpi_driver cmpc_accel_acpi_driver = { .ops = { .add = cmpc_accel_add, .remove = cmpc_accel_remove, - .notify = cmpc_accel_handler, } }; @@ -716,8 +736,9 @@ static acpi_status cmpc_get_tablet(acpi_handle handle, return status; } -static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) +static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; unsigned long long val = 0; struct input_dev *inputdev = dev_get_drvdata(&dev->dev); @@ -746,11 +767,24 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + int error; + + error = cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + if (error) + return error; + + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_tablet_handler, acpi); + if (error) + cmpc_remove_notify_device(&acpi->dev); + + return error; } static void cmpc_tablet_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_tablet_handler); cmpc_remove_notify_device(&acpi->dev); } @@ -782,7 +816,6 @@ static struct acpi_driver cmpc_tablet_acpi_driver = { .ops = { .add = cmpc_tablet_add, .remove = cmpc_tablet_remove, - .notify = cmpc_tablet_handler, }, .drv.pm = &cmpc_tablet_pm, }; @@ -1046,8 +1079,9 @@ static int cmpc_keys_codes[] = { KEY_MAX }; -static void cmpc_keys_handler(struct acpi_device *dev, u32 event) +static void cmpc_keys_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; struct input_dev *inputdev; int code = KEY_MAX; @@ -1069,11 +1103,24 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + int error; + + error = cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + if (error) + return error; + + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_keys_handler, acpi); + if (error) + cmpc_remove_notify_device(&acpi->dev); + + return error; } static void cmpc_keys_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_keys_handler); cmpc_remove_notify_device(&acpi->dev); } @@ -1089,7 +1136,6 @@ static struct acpi_driver cmpc_keys_acpi_driver = { .ops = { .add = cmpc_keys_add, .remove = cmpc_keys_remove, - .notify = cmpc_keys_handler, } }; From 1588b83ad9b95dfa0bc3b9c22eb2608b64e1a3c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:10:32 +0200 Subject: [PATCH 1217/5214] platform/x86: classmate-laptop: Convert v4 accel driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_accel_acpi_driver_v4 in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding, but the sysfs attributes added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting them to be present there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3609276.QJadu78ljV@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 77 ++++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 8507483b23f9..454ba2823b51 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -13,6 +13,7 @@ #include #include #include +#include struct cmpc_accel { int sensitivity; @@ -179,15 +180,15 @@ static acpi_status cmpc_get_accel_v4(acpi_handle handle, static void cmpc_accel_handler_v4(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; if (event == 0x81) { int16_t x, y, z; acpi_status status; - status = cmpc_get_accel_v4(dev->handle, &x, &y, &z); + status = cmpc_get_accel_v4(ACPI_HANDLE(dev), &x, &y, &z); if (ACPI_SUCCESS(status)) { - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); input_report_abs(inputdev, ABS_X, x); input_report_abs(inputdev, ABS_Y, y); @@ -317,18 +318,17 @@ static struct device_attribute cmpc_accel_g_select_attr_v4 = { static int cmpc_accel_open_v4(struct input_dev *input) { - struct acpi_device *acpi; + acpi_handle handle = ACPI_HANDLE(input->dev.parent); struct cmpc_accel *accel; - acpi = to_acpi_device(input->dev.parent); accel = dev_get_drvdata(&input->dev); if (!accel) return -ENXIO; - cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); - cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); + cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(handle, accel->g_select); - if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) { + if (ACPI_SUCCESS(cmpc_start_accel_v4(handle))) { accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN; return 0; } @@ -337,13 +337,11 @@ static int cmpc_accel_open_v4(struct input_dev *input) static void cmpc_accel_close_v4(struct input_dev *input) { - struct acpi_device *acpi; struct cmpc_accel *accel; - acpi = to_acpi_device(input->dev.parent); accel = dev_get_drvdata(&input->dev); - cmpc_stop_accel_v4(acpi->handle); + cmpc_stop_accel_v4(ACPI_HANDLE(input->dev.parent)); accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; } @@ -367,7 +365,7 @@ static int cmpc_accel_suspend_v4(struct device *dev) accel = dev_get_drvdata(&inputdev->dev); if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) - return cmpc_stop_accel_v4(to_acpi_device(dev)->handle); + return cmpc_stop_accel_v4(ACPI_HANDLE(dev)); return 0; } @@ -381,12 +379,12 @@ static int cmpc_accel_resume_v4(struct device *dev) accel = dev_get_drvdata(&inputdev->dev); if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) { - cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle, - accel->sensitivity); - cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle, - accel->g_select); + acpi_handle handle = ACPI_HANDLE(dev); - if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle))) + cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(handle, accel->g_select); + + if (ACPI_FAILURE(cmpc_start_accel_v4(handle))) return -EIO; } @@ -394,23 +392,29 @@ static int cmpc_accel_resume_v4(struct device *dev) } #endif -static int cmpc_accel_add_v4(struct acpi_device *acpi) +static int cmpc_accel_probe_v4(struct platform_device *pdev) { int error; struct input_dev *inputdev; struct cmpc_accel *accel; + struct acpi_device *acpi; - accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); + error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; - inputdev = dev_get_drvdata(&acpi->dev); + inputdev = dev_get_drvdata(&pdev->dev); + dev_set_drvdata(&acpi->dev, inputdev); accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); @@ -427,7 +431,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) goto failed_g_select; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_accel_handler_v4, acpi); + cmpc_accel_handler_v4, &pdev->dev); if (error) goto failed_notify_handler; @@ -440,17 +444,21 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_accel_remove_v4(struct acpi_device *acpi) +static void cmpc_accel_remove_v4(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, cmpc_accel_handler_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -461,15 +469,14 @@ static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { {"", 0} }; -static struct acpi_driver cmpc_accel_acpi_driver_v4 = { - .name = "cmpc_accel_v4", - .class = "cmpc_accel_v4", - .ids = cmpc_accel_device_ids_v4, - .ops = { - .add = cmpc_accel_add_v4, - .remove = cmpc_accel_remove_v4, +static struct platform_driver cmpc_accel_acpi_driver_v4 = { + .probe = cmpc_accel_probe_v4, + .remove = cmpc_accel_remove_v4, + .driver = { + .name = "cmpc_accel_v4", + .acpi_match_table = cmpc_accel_device_ids_v4, + .pm = &cmpc_accel_pm, }, - .drv.pm = &cmpc_accel_pm, }; @@ -1164,7 +1171,7 @@ static int cmpc_init(void) if (r) goto failed_accel; - r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4); + r = platform_driver_register(&cmpc_accel_acpi_driver_v4); if (r) goto failed_accel_v4; @@ -1188,7 +1195,7 @@ static int cmpc_init(void) static void cmpc_exit(void) { - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4); + platform_driver_unregister(&cmpc_accel_acpi_driver_v4); acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); From e51effb3cefe1bffa1afdee6ee5e93ded1746606 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:11:39 +0200 Subject: [PATCH 1218/5214] platform/x86: classmate-laptop: Convert accel driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_accel_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding, but the sysfs attribute added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting it to be present there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/24365138.6Emhk5qWAg@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 63 +++++++++++++------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 454ba2823b51..0d5ad89d0f24 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -558,15 +558,15 @@ static acpi_status cmpc_get_accel(acpi_handle handle, static void cmpc_accel_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; if (event == 0x81) { unsigned char x, y, z; acpi_status status; - status = cmpc_get_accel(dev->handle, &x, &y, &z); + status = cmpc_get_accel(ACPI_HANDLE(dev), &x, &y, &z); if (ACPI_SUCCESS(status)) { - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); input_report_abs(inputdev, ABS_X, x); input_report_abs(inputdev, ABS_Y, y); @@ -633,20 +633,14 @@ static struct device_attribute cmpc_accel_sensitivity_attr = { static int cmpc_accel_open(struct input_dev *input) { - struct acpi_device *acpi; - - acpi = to_acpi_device(input->dev.parent); - if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) + if (ACPI_SUCCESS(cmpc_start_accel(ACPI_HANDLE(input->dev.parent)))) return 0; return -EIO; } static void cmpc_accel_close(struct input_dev *input) { - struct acpi_device *acpi; - - acpi = to_acpi_device(input->dev.parent); - cmpc_stop_accel(acpi->handle); + cmpc_stop_accel(ACPI_HANDLE(input->dev.parent)); } static void cmpc_accel_idev_init(struct input_dev *inputdev) @@ -659,21 +653,27 @@ static void cmpc_accel_idev_init(struct input_dev *inputdev) inputdev->close = cmpc_accel_close; } -static int cmpc_accel_add(struct acpi_device *acpi) +static int cmpc_accel_probe(struct platform_device *pdev) { int error; struct input_dev *inputdev; struct cmpc_accel *accel; + struct acpi_device *acpi; - accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); + error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; - inputdev = dev_get_drvdata(&acpi->dev); + inputdev = dev_get_drvdata(&pdev->dev); + dev_set_drvdata(&acpi->dev, inputdev); accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); @@ -683,7 +683,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) goto failed_file; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_accel_handler, acpi); + cmpc_accel_handler, &pdev->dev); if (error) goto failed_notify_handler; @@ -694,16 +694,20 @@ static int cmpc_accel_add(struct acpi_device *acpi) failed_notify_handler: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); failed_file: - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_accel_remove(struct acpi_device *acpi) +static void cmpc_accel_remove(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, cmpc_accel_handler); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -711,14 +715,13 @@ static const struct acpi_device_id cmpc_accel_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_accel_acpi_driver = { - .name = "cmpc_accel", - .class = "cmpc_accel", - .ids = cmpc_accel_device_ids, - .ops = { - .add = cmpc_accel_add, - .remove = cmpc_accel_remove, - } +static struct platform_driver cmpc_accel_acpi_driver = { + .probe = cmpc_accel_probe, + .remove = cmpc_accel_remove, + .driver = { + .name = "cmpc_accel", + .acpi_match_table = cmpc_accel_device_ids, + }, }; @@ -1167,7 +1170,7 @@ static int cmpc_init(void) if (r) goto failed_tablet; - r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); + r = platform_driver_register(&cmpc_accel_acpi_driver); if (r) goto failed_accel; @@ -1178,7 +1181,7 @@ static int cmpc_init(void) return r; failed_accel_v4: - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); + platform_driver_unregister(&cmpc_accel_acpi_driver); failed_accel: acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); @@ -1196,7 +1199,7 @@ static int cmpc_init(void) static void cmpc_exit(void) { platform_driver_unregister(&cmpc_accel_acpi_driver_v4); - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); + platform_driver_unregister(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); From 0fd6639706b79c0fbb5c0205aebfbbfd40ec44b0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:12:55 +0200 Subject: [PATCH 1219/5214] platform/x86: classmate-laptop: Convert tablet driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_tablet_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/4504318.ejJDZkT8p0@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 55 +++++++++++++------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 0d5ad89d0f24..464ab852b492 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -748,12 +748,12 @@ static acpi_status cmpc_get_tablet(acpi_handle handle, static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; unsigned long long val = 0; - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); if (event == 0x81) { - if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } @@ -762,40 +762,44 @@ static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) static void cmpc_tablet_idev_init(struct input_dev *inputdev) { + acpi_handle handle = ACPI_HANDLE(inputdev->dev.parent); unsigned long long val = 0; - struct acpi_device *acpi; set_bit(EV_SW, inputdev->evbit); set_bit(SW_TABLET_MODE, inputdev->swbit); - acpi = to_acpi_device(inputdev->dev.parent); - if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(handle, &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } } -static int cmpc_tablet_add(struct acpi_device *acpi) +static int cmpc_tablet_probe(struct platform_device *pdev) { + struct acpi_device *acpi; int error; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + error = cmpc_add_notify_device(&pdev->dev, "cmpc_tablet", cmpc_tablet_idev_init); if (error) return error; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_tablet_handler, acpi); + cmpc_tablet_handler, &pdev->dev); if (error) - cmpc_remove_notify_device(&acpi->dev); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_tablet_remove(struct acpi_device *acpi) +static void cmpc_tablet_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_tablet_handler); - cmpc_remove_notify_device(&acpi->dev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, cmpc_tablet_handler); + cmpc_remove_notify_device(&pdev->dev); } #ifdef CONFIG_PM_SLEEP @@ -804,7 +808,7 @@ static int cmpc_tablet_resume(struct device *dev) struct input_dev *inputdev = dev_get_drvdata(dev); unsigned long long val = 0; - if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } @@ -819,15 +823,14 @@ static const struct acpi_device_id cmpc_tablet_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_tablet_acpi_driver = { - .name = "cmpc_tablet", - .class = "cmpc_tablet", - .ids = cmpc_tablet_device_ids, - .ops = { - .add = cmpc_tablet_add, - .remove = cmpc_tablet_remove, +static struct platform_driver cmpc_tablet_acpi_driver = { + .probe = cmpc_tablet_probe, + .remove = cmpc_tablet_remove, + .driver = { + .name = "cmpc_tablet", + .acpi_match_table = cmpc_tablet_device_ids, + .pm = &cmpc_tablet_pm, }, - .drv.pm = &cmpc_tablet_pm, }; @@ -1166,7 +1169,7 @@ static int cmpc_init(void) if (r) goto failed_bl; - r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); + r = platform_driver_register(&cmpc_tablet_acpi_driver); if (r) goto failed_tablet; @@ -1184,7 +1187,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_accel_acpi_driver); failed_accel: - acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); + platform_driver_unregister(&cmpc_tablet_acpi_driver); failed_tablet: acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); @@ -1200,7 +1203,7 @@ static void cmpc_exit(void) { platform_driver_unregister(&cmpc_accel_acpi_driver_v4); platform_driver_unregister(&cmpc_accel_acpi_driver); - acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); + platform_driver_unregister(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); } From 069b06f8dfc9821fa54f0c5109ebbde891ea363a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:14:13 +0200 Subject: [PATCH 1220/5214] platform/x86: classmate-laptop: Convert ipml driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_ipml_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the backlight and rfkill devices registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3753883.R56niFO833@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 42 ++++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 464ab852b492..08ff4549e956 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -997,11 +997,16 @@ struct ipml200_dev { struct rfkill *rf; }; -static int cmpc_ipml_add(struct acpi_device *acpi) +static int cmpc_ipml_probe(struct platform_device *pdev) { int retval; struct ipml200_dev *ipml; struct backlight_properties props; + acpi_handle handle; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; ipml = kmalloc_obj(*ipml); if (ipml == NULL) @@ -1010,16 +1015,16 @@ static int cmpc_ipml_add(struct acpi_device *acpi) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; props.max_brightness = 7; - ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, - acpi->handle, &cmpc_bl_ops, + ipml->bd = backlight_device_register("cmpc_bl", &pdev->dev, + handle, &cmpc_bl_ops, &props); if (IS_ERR(ipml->bd)) { retval = PTR_ERR(ipml->bd); goto out_bd; } - ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, - &cmpc_rfkill_ops, acpi->handle); + ipml->rf = rfkill_alloc("cmpc_rfkill", &pdev->dev, RFKILL_TYPE_WLAN, + &cmpc_rfkill_ops, handle); /* * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). * This is OK, however, since all other uses of the device will not @@ -1033,7 +1038,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi) } } - dev_set_drvdata(&acpi->dev, ipml); + platform_set_drvdata(pdev, ipml); return 0; out_bd: @@ -1041,11 +1046,11 @@ static int cmpc_ipml_add(struct acpi_device *acpi) return retval; } -static void cmpc_ipml_remove(struct acpi_device *acpi) +static void cmpc_ipml_remove(struct platform_device *pdev) { struct ipml200_dev *ipml; - ipml = dev_get_drvdata(&acpi->dev); + ipml = platform_get_drvdata(pdev); backlight_device_unregister(ipml->bd); @@ -1062,14 +1067,13 @@ static const struct acpi_device_id cmpc_ipml_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_ipml_acpi_driver = { - .name = "cmpc", - .class = "cmpc", - .ids = cmpc_ipml_device_ids, - .ops = { - .add = cmpc_ipml_add, - .remove = cmpc_ipml_remove - } +static struct platform_driver cmpc_ipml_acpi_driver = { + .probe = cmpc_ipml_probe, + .remove = cmpc_ipml_remove, + .driver = { + .name = "cmpc", + .acpi_match_table = cmpc_ipml_device_ids, + }, }; @@ -1165,7 +1169,7 @@ static int cmpc_init(void) if (r) goto failed_keys; - r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); + r = platform_driver_register(&cmpc_ipml_acpi_driver); if (r) goto failed_bl; @@ -1190,7 +1194,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_tablet_acpi_driver); failed_tablet: - acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); + platform_driver_unregister(&cmpc_ipml_acpi_driver); failed_bl: acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); @@ -1204,7 +1208,7 @@ static void cmpc_exit(void) platform_driver_unregister(&cmpc_accel_acpi_driver_v4); platform_driver_unregister(&cmpc_accel_acpi_driver); platform_driver_unregister(&cmpc_tablet_acpi_driver); - acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); + platform_driver_unregister(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); } From 5fdac9983681f743cfaa89414ea154a2c5fd39c4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:15:18 +0200 Subject: [PATCH 1221/5214] platform/x86: classmate-laptop: Convert keys driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_keys_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/2002306.taCxCBeP46@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 46 ++++++++++++++----------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 08ff4549e956..05890815795d 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -1098,13 +1098,13 @@ static int cmpc_keys_codes[] = { static void cmpc_keys_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; struct input_dev *inputdev; int code = KEY_MAX; if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) code = cmpc_keys_codes[event & 0x0F]; - inputdev = dev_get_drvdata(&dev->dev); + inputdev = dev_get_drvdata(dev); input_report_key(inputdev, code, !(event & 0x10)); input_sync(inputdev); } @@ -1118,27 +1118,32 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) set_bit(cmpc_keys_codes[i], inputdev->keybit); } -static int cmpc_keys_add(struct acpi_device *acpi) +static int cmpc_keys_probe(struct platform_device *pdev) { + struct acpi_device *acpi; int error; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + error = cmpc_add_notify_device(&pdev->dev, "cmpc_keys", cmpc_keys_idev_init); if (error) return error; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_keys_handler, acpi); + cmpc_keys_handler, &pdev->dev); if (error) - cmpc_remove_notify_device(&acpi->dev); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_keys_remove(struct acpi_device *acpi) +static void cmpc_keys_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_keys_handler); - cmpc_remove_notify_device(&acpi->dev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, cmpc_keys_handler); + cmpc_remove_notify_device(&pdev->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { @@ -1146,14 +1151,13 @@ static const struct acpi_device_id cmpc_keys_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_keys_acpi_driver = { - .name = "cmpc_keys", - .class = "cmpc_keys", - .ids = cmpc_keys_device_ids, - .ops = { - .add = cmpc_keys_add, - .remove = cmpc_keys_remove, - } +static struct platform_driver cmpc_keys_acpi_driver = { + .probe = cmpc_keys_probe, + .remove = cmpc_keys_remove, + .driver = { + .name = "cmpc_keys", + .acpi_match_table = cmpc_keys_device_ids, + }, }; @@ -1165,7 +1169,7 @@ static int cmpc_init(void) { int r; - r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); + r = platform_driver_register(&cmpc_keys_acpi_driver); if (r) goto failed_keys; @@ -1197,7 +1201,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_ipml_acpi_driver); failed_bl: - acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); + platform_driver_unregister(&cmpc_keys_acpi_driver); failed_keys: return r; @@ -1209,7 +1213,7 @@ static void cmpc_exit(void) platform_driver_unregister(&cmpc_accel_acpi_driver); platform_driver_unregister(&cmpc_tablet_acpi_driver); platform_driver_unregister(&cmpc_ipml_acpi_driver); - acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); + platform_driver_unregister(&cmpc_keys_acpi_driver); } module_init(cmpc_init); From f4d51e55dd47ef467fbe37d8575e20eee41b092d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 25 Mar 2026 13:59:43 +0100 Subject: [PATCH 1222/5214] staging: media: atomisp: reduce load_primary_binaries() stack usage The load_primary_binaries() function is overly complex and has som large variables on the stack, which can cause warnings depending on CONFIG_FRAME_WARN setting: drivers/staging/media/atomisp/pci/sh_css.c: In function 'load_primary_binaries': drivers/staging/media/atomisp/pci/sh_css.c:5260:1: error: the frame size of 1560 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] Half of the stack usage is for the prim_descr[] array, but only one member of the array is used at any given time. Reduce the stack usage by turning the array into a single structure. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index c6838772553d..792379407828 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5019,7 +5019,6 @@ static int load_primary_binaries( struct ia_css_capture_settings *mycs; unsigned int i; bool need_extra_yuv_scaler = false; - struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES]; IA_CSS_ENTER_PRIVATE(""); assert(pipe); @@ -5188,15 +5187,16 @@ static int load_primary_binaries( /* Primary */ for (i = 0; i < mycs->num_primary_stage; i++) { + struct ia_css_binary_descr prim_descr; struct ia_css_frame_info *local_vf_info = NULL; if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; - ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], + ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); + err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; From 016a8735b8bab9a5a6c9c4b0f10e479e6c315315 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Mar 2026 22:54:19 +0100 Subject: [PATCH 1223/5214] media: atomisp: Drop unused include This driver includes the legacy header but does not use any symbols from it. Drop the inclusion. Signed-off-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 4026e98c5845..236122722703 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "../../include/linux/atomisp_platform.h" #include "../../include/linux/atomisp_gmin_platform.h" From de1844c0da4ad2987bca07d299402a6c73ba902d Mon Sep 17 00:00:00 2001 From: Zile Xiong Date: Thu, 19 Mar 2026 18:35:45 +0800 Subject: [PATCH 1224/5214] staging: media: atomisp: hmm: remove unnecessary casts Drop unnecessary casts when accessing vma->vm_private_data. No functional change. Signed-off-by: Zile Xiong Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/hmm/hmm_bo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index b91cbd0262d0..0a8f401a1ca1 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -984,8 +984,7 @@ void hmm_bo_unref(struct hmm_buffer_object *bo) static void hmm_bo_vm_open(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); @@ -1002,8 +1001,7 @@ static void hmm_bo_vm_open(struct vm_area_struct *vma) static void hmm_bo_vm_close(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); From 4d542f256cf2ff23a4a2c5411cbdf9af0191b8ca Mon Sep 17 00:00:00 2001 From: Lin YuChen Date: Sat, 14 Mar 2026 01:55:26 +0800 Subject: [PATCH 1225/5214] staging: media: atomisp: use kmalloc_array() for sh_css_blob_info Replace the open-coded multiplication in kmalloc() with kmalloc_array() to provide overflow protection and improve code readability. Signed-off-by: Lin YuChen Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_firmware.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index 57ecf5549c23..af12df2f9b09 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -253,9 +253,9 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, sh_css_num_binaries = file_header->binary_nr; /* Only allocate memory for ISP blob info */ if (sh_css_num_binaries > NUM_OF_SPS) { - sh_css_blob_info = kmalloc( - (sh_css_num_binaries - NUM_OF_SPS) * - sizeof(*sh_css_blob_info), GFP_KERNEL); + sh_css_blob_info = + kmalloc_array(sh_css_num_binaries - NUM_OF_SPS, + sizeof(*sh_css_blob_info), GFP_KERNEL); if (!sh_css_blob_info) return -ENOMEM; } else { From cede4f26b4df8b13dcfe3667b098eceb2ef26eb0 Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Sun, 1 Mar 2026 23:30:38 +0100 Subject: [PATCH 1226/5214] staging: media: atomisp: remove unnecessary else after return in atomisp_cmd.c Remove unnecessary else clause after return statement as the else branch is not needed when the if branch always returns. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 7ccd4205ed1e..6174177eb869 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -2032,8 +2032,8 @@ static unsigned int long copy_from_compatible(void *to, const void *from, { if (from_user) return copy_from_user(to, (void __user *)from, n); - else - memcpy(to, from, n); + + memcpy(to, from, n); return 0; } From 7222dc8751e0c1a179acd6f8a2c664a7f16081b1 Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Sun, 1 Mar 2026 22:44:25 +0100 Subject: [PATCH 1227/5214] staging: media: atomisp: use __func__ in debug message in atomisp_cmd.c Replace hardcoded function name string with __func__ macro in dev_dbg call as recommended by kernel coding style. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 6174177eb869..6cd500d9fd26 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -4011,7 +4011,7 @@ static int css_input_resolution_changed(struct atomisp_sub_device *asd, struct atomisp_metadata_buf *md_buf = NULL, *_md_buf; unsigned int i; - dev_dbg(asd->isp->dev, "css_input_resolution_changed to %ux%u\n", + dev_dbg(asd->isp->dev, "%s: to %ux%u\n", __func__, ffmt->width, ffmt->height); if (IS_ISP2401) From d178c7ca8fefc28115d35b94c3b1f4d653e34182 Mon Sep 17 00:00:00 2001 From: Feng Ning Date: Sun, 12 Apr 2026 00:05:08 +0000 Subject: [PATCH 1228/5214] staging: media: atomisp: use array3_size() for overflow-safe allocation Replace open-coded width * height * sizeof() multiplications with array3_size() to prevent integer overflow in buffer allocations. The atomisp driver computes DVS, morphing table, shading table and statistics buffer sizes using unchecked arithmetic. When dimensions are attacker-controlled or simply large, the product can silently wrap, causing kvmalloc() to allocate an undersized buffer. array3_size() saturates to SIZE_MAX on overflow, so kvmalloc() returns NULL instead of succeeding with too few bytes. Signed-off-by: Feng Ning Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/sh_css_param_dvs.c | 11 +++--- .../media/atomisp/pci/sh_css_param_shading.c | 4 ++- .../staging/media/atomisp/pci/sh_css_params.c | 35 +++++++++++-------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index 3d2cb2d25fdb..ad2a9b84e232 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include "sh_css_param_dvs.h" #include #include @@ -48,7 +49,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, } /* Generate Y buffers */ - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) { IA_CSS_ERROR("out of memory"); @@ -56,7 +57,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) { IA_CSS_ERROR("out of memory"); @@ -67,7 +68,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, /* Generate UV buffers */ IA_CSS_LOG("UV W %d H %d", width_uv, height_uv); - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) { IA_CSS_ERROR("out of memory"); @@ -75,7 +77,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) { IA_CSS_ERROR("out of memory"); diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 083ac5713b6d..0ac85cdea010 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include #include @@ -345,7 +346,8 @@ ia_css_shading_table_alloc( me->fraction_bits = 0; for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { me->data[i] = - kvmalloc(width * height * sizeof(*me->data[0]), + kvmalloc(array3_size(width, height, + sizeof(*me->data[0])), GFP_KERNEL); if (!me->data[i]) { unsigned int j; diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index b8d492115196..1c8af03db1c7 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include #include "gdc_device.h" /* gdc_lut_store(), ... */ @@ -1377,11 +1378,11 @@ struct ia_css_morph_table *ia_css_morph_table_allocate( } for (i = 0; i < IA_CSS_MORPH_TABLE_NUM_PLANES; i++) { - me->coordinates_x[i] = kvmalloc(height * width * - sizeof(*me->coordinates_x[i]), + me->coordinates_x[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_x[i])), GFP_KERNEL); - me->coordinates_y[i] = kvmalloc(height * width * - sizeof(*me->coordinates_y[i]), + me->coordinates_y[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_y[i])), GFP_KERNEL); if ((!me->coordinates_x[i]) || @@ -4198,13 +4199,17 @@ ia_css_dvs_statistics_allocate(const struct ia_css_dvs_grid_info *grid) goto err; me->grid = *grid; - me->hor_proj = kvmalloc(grid->height * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->hor_proj), GFP_KERNEL); + me->hor_proj = kvmalloc(array3_size(grid->height, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->hor_proj)), + GFP_KERNEL); if (!me->hor_proj) goto err; - me->ver_proj = kvmalloc(grid->width * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->ver_proj), GFP_KERNEL); + me->ver_proj = kvmalloc(array3_size(grid->width, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->ver_proj)), + GFP_KERNEL); if (!me->ver_proj) goto err; @@ -4470,24 +4475,26 @@ ia_css_dvs2_6axis_config_allocate(const struct ia_css_stream *stream) params->pipe_dvs_6axis_config[IA_CSS_PIPE_ID_VIDEO]->height_uv; IA_CSS_LOG("table Y: W %d H %d", width_y, height_y); IA_CSS_LOG("table UV: W %d H %d", width_uv, height_uv); - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) goto err; - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) goto err; - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) goto err; - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) goto err; From 07e475301406cff58281d1ffc9b0ea52bf682a5a Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Mon, 25 Aug 2025 03:15:15 -0700 Subject: [PATCH 1229/5214] media: atomisp: Use string choices helpers Use string_choices.h helpers instead of hard-coded strings. Signed-off-by: Chelsy Ratnawat Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_compat_css20.c | 3 ++- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 5 +++-- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 5 +++-- .../staging/media/atomisp/pci/runtime/binary/src/binary.c | 3 ++- drivers/staging/media/atomisp/pci/sh_css.c | 3 ++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index c30501a36f86..d2bacd1ffe7b 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -27,6 +27,7 @@ #include #include +#include /* Assume max number of ACC stages */ #define MAX_ACC_STAGES 20 @@ -841,7 +842,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp, { dev_dbg(isp->dev, "%s: css irq info 0x%08x: %s (%d).\n", __func__, info, - enable ? "enable" : "disable", enable); + str_enable_disable(enable), enable); if (ia_css_irq_enable(info, enable)) { dev_warn(isp->dev, "%s:Invalid irq info: 0x%08x when %s.\n", __func__, info, diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 236122722703..a1d3a16921ea 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../../include/linux/atomisp_platform.h" #include "../../include/linux/atomisp_gmin_platform.h" @@ -916,7 +917,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) return 0; dev_dbg(subdev->dev, "Setting power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); if (on) ret = acpi_device_set_power(adev, @@ -929,7 +930,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) gs->clock_on = on; else dev_err(subdev->dev, "Couldn't set power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); return ret; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 900a67552d6a..812230397409 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -527,7 +528,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) u32 val = enable ? MRFLD_ISPSSPM0_IUNIT_POWER_ON : MRFLD_ISPSSPM0_IUNIT_POWER_OFF; - dev_dbg(isp->dev, "IUNIT power-%s.\n", enable ? "on" : "off"); + dev_dbg(isp->dev, "IUNIT power-%s.\n", str_on_off(enable)); /* WA for P-Unit, if DVFS enabled, ISP timeout observed */ if (IS_CHT && enable && !isp->pm_only) { @@ -569,7 +570,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) usleep_range(100, 150); } while (1); - dev_err(isp->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off"); + dev_err(isp->dev, "IUNIT power-%s timeout.\n", str_on_off(enable)); return -EBUSY; } diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 39b37b557aff..e9016d7775dc 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -5,6 +5,7 @@ */ #include +#include #include #include /* HR_GDC_N */ @@ -1220,7 +1221,7 @@ int ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary * dev_dbg(atomisp_dev, "Using binary %s (id %d), type %d, mode %d, continuous %s\n", xcandidate->blob->name, xcandidate->sp.id, xcandidate->type, xcandidate->sp.pipeline.mode, - xcandidate->sp.enable.continuous ? "true" : "false"); + str_true_false(xcandidate->sp.enable.continuous)); if (err) dev_err(atomisp_dev, "Failed to find a firmware binary matching the pipeline parameters\n"); diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 792379407828..c136c1745eb9 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -7,6 +7,7 @@ /*! \file */ #include #include +#include #include #include "hmm.h" @@ -1478,7 +1479,7 @@ map_sp_threads(struct ia_css_stream *stream, bool map) enum ia_css_pipe_id pipe_id; IA_CSS_ENTER_PRIVATE("stream = %p, map = %s", - stream, map ? "true" : "false"); + stream, str_true_false(map)); if (!stream) { IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); From 3322fc4dcaf1fe6b6fe68b976f6d93c2b87ea169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Barna=C5=9B?= Date: Thu, 21 Aug 2025 13:35:20 +0000 Subject: [PATCH 1230/5214] staging: media: atomisp: Whitespaces style cleanup in gdc.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up coding style whitespace issues in drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c. Fixes include: - removal of unnecessary line breaks - correcting spacing around operators - correcting spaces between types and names Signed-off-by: Adrian Barnaś Signed-off-by: Sakari Ailus --- .../pci/hive_isp_css_common/host/gdc.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c index 8bb78b4d7c67..a06d69353939 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c @@ -26,9 +26,7 @@ static inline void gdc_reg_store( /* * Exported function implementations */ -void gdc_lut_store( - const gdc_ID_t ID, - const int data[4][HRT_GDC_N]) +void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N]) { unsigned int i, lut_offset = HRT_GDC_LUT_IDX; @@ -36,15 +34,13 @@ void gdc_lut_store( assert(HRT_GDC_LUT_COEFF_OFFSET <= (4 * sizeof(hrt_data))); for (i = 0; i < HRT_GDC_N; i++) { - hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data word_0 = entry_0 | - (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); - hrt_data word_1 = entry_2 | - (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_0 = entry_0 | (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_1 = entry_2 | (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); gdc_reg_store(ID, lut_offset++, word_0); gdc_reg_store(ID, lut_offset++, word_1); @@ -85,8 +81,7 @@ void gdc_lut_convert_to_isp_format(const int in_lut[4][HRT_GDC_N], } } -int gdc_get_unity( - const gdc_ID_t ID) +int gdc_get_unity(const gdc_ID_t ID) { assert(ID < N_GDC_ID); (void)ID; From 2bc7dd2b1fcc2a1f65db8967af60327ca8ec4f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Barna=C5=9B?= Date: Thu, 21 Aug 2025 13:35:21 +0000 Subject: [PATCH 1231/5214] staging: media: atomisp: Remove return from end of void function in gdc.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix checkpatch.pl warning on useless return on the end of the void function. Signed-off-by: Adrian Barnaś Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c index a06d69353939..b31e3809c0e4 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c @@ -45,7 +45,6 @@ void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N]) gdc_reg_store(ID, lut_offset++, word_0); gdc_reg_store(ID, lut_offset++, word_1); } - return; } /* From d13b75670eb29df50760f8139535ecc6e4b81589 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:11 +0800 Subject: [PATCH 1232/5214] staging: media: atomisp: improve kernel-doc for ia_css_aa_config Move kernel-doc comment for strength field in ia_css_aa_config from inline to structure-level comment for better readability and tooling compatibility. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h index 2f568a7062da..f825f537a536 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h @@ -28,11 +28,13 @@ * ISP block: BAA2 * ISP1: BAA2 is used. * ISP2: BAA2 is used. + * + * @strength: Strength of the filter, in u0.13 fixed-point format. + * Valid range: [0, 8191]. A value of 0 means the filter is + * ineffective (default). */ struct ia_css_aa_config { - u16 strength; /** Strength of the filter. - u0.13, [0,8191], - default/ineffective 0 */ + u16 strength; }; #endif /* __IA_CSS_AA2_TYPES_H */ From 15b4210a063877ddaa7f410069f86573bc0f5152 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:12 +0800 Subject: [PATCH 1233/5214] staging: media: atomisp: fix indentation in anr files Fix inconsistent tab/space usage and bring function definitions into a single-line format, matching kernel coding style. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../isp/kernels/anr/anr_1.0/ia_css_anr.host.c | 20 +++++++------------ .../isp/kernels/anr/anr_1.0/ia_css_anr.host.h | 20 +++++++------------ .../isp/kernels/anr/anr_2/ia_css_anr2.host.c | 14 +++++-------- .../isp/kernels/anr/anr_2/ia_css_anr2.host.h | 13 +++++------- 4 files changed, 24 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c index 899d566234b9..49c22a68ec55 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c @@ -21,20 +21,16 @@ const struct ia_css_anr_config default_anr_config = { {10, 20, 30} }; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size) +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size) { (void)size; to->threshold = from->threshold; } -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level) +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level) { if (!anr) return; ia_css_debug_dtrace(level, "Advance Noise Reduction:\n"); @@ -42,10 +38,8 @@ ia_css_anr_dump( "anr_threshold", anr->threshold); } -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, - unsigned int level) +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level) { ia_css_debug_dtrace(level, "config.threshold=%d\n", diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h index 4f77900871c8..2f17d62b9208 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h @@ -12,20 +12,14 @@ extern const struct ia_css_anr_config default_anr_config; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size); +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size); -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level); +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level); -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, unsigned int level) -; +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level); #endif /* __IA_CSS_ANR_HOST_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c index 08af8a848fbf..6ba34dc48ad4 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c @@ -10,11 +10,9 @@ #include "ia_css_anr2.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size) +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size) { unsigned int i; @@ -27,10 +25,8 @@ ia_css_anr2_vmem_encode( } } -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, - unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) { (void)config; (void)level; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h index 2b1105f21c1e..36fb6c259699 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h @@ -13,15 +13,12 @@ #include "ia_css_anr2_param.h" #include "ia_css_anr2_table.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size); +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size); -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) ; #endif /* __IA_CSS_ANR2_HOST_H */ From b643225f6189f65c436820e1585102612f8480d5 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:13 +0800 Subject: [PATCH 1234/5214] staging: media: atomisp: use designated initializer in anr config Improve readability by using designated initializer for default_anr_config. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c index 49c22a68ec55..f4dd3ca03d75 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c @@ -11,14 +11,14 @@ #include "ia_css_anr.host.h" const struct ia_css_anr_config default_anr_config = { - 10, - { + .threshold = 10, + .thresholds = { 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4 }, - {10, 20, 30} + .factors = {10, 20, 30}, }; void ia_css_anr_encode(struct sh_css_isp_anr_params *to, From 9862d3f9de6b5e70d2ff87377beece44ec4e4e45 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:14 +0800 Subject: [PATCH 1235/5214] staging: media: atomisp: fix indentation in bh host files Fix inconsistent tab/space usage and bring function definitions into a single-line format, matching kernel coding style. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../pci/isp/kernels/bh/bh_2/ia_css_bh.host.c | 14 +++++--------- .../pci/isp/kernels/bh/bh_2/ia_css_bh.host.h | 14 +++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c index 69c87e53f3c2..b87eb1a21b21 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c @@ -12,10 +12,8 @@ #include "ia_css_bh.host.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf) +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf) { int i; @@ -37,11 +35,9 @@ ia_css_bh_hmem_decode( } } -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size) +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size) { (void)size; /* coefficients to calculate Y */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h index 36b360cfe62e..964d658ceec3 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h @@ -10,15 +10,11 @@ #include "ia_css_bh_param.h" #include "s3a/s3a_1.0/ia_css_s3a_types.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf); +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf); -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size); +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size); #endif /* __IA_CSS_BH_HOST_H */ From 63c9b7def874b28471c5c75151cb43e8f10a9fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Tue, 15 Jul 2025 13:23:26 +0200 Subject: [PATCH 1236/5214] media: atomisp: style fix for trailing statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix checkpatch errors "ERROR: trailing statements should be on next line" in drivers/staging/media/atomisp/pci/sh_css_params.c. Signed-off-by: Timo Röhling Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_params.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 1c8af03db1c7..8420a22fd8f0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -872,7 +872,8 @@ ia_css_process_kernel(struct ia_css_stream *stream, /* update the other buffers to the pipe specific copies */ for (stage = pipeline->stages; stage; stage = stage->next) { - if (!stage || !stage->binary) continue; + if (!stage || !stage->binary) + continue; process(pipeline->pipe_id, stage, params); } } @@ -3039,7 +3040,8 @@ process_kernel_parameters(unsigned int pipe_id, /* Call parameter process functions for all kernels */ /* Skip SC, since that is called on a temp sc table */ for (param_id = 0; param_id < IA_CSS_NUM_PARAMETER_IDS; param_id++) { - if (param_id == IA_CSS_SC_ID) continue; + if (param_id == IA_CSS_SC_ID) + continue; if (params->config_changed[param_id]) ia_css_kernel_process_param[param_id](pipe_id, stage, params); } @@ -3594,7 +3596,8 @@ sh_css_params_write_to_ddr_internal( IA_CSS_PARAM_CLASS_PARAM, mem); size_t size = isp_data->size; - if (!size) continue; + if (!size) + continue; buff_realloced = reallocate_buffer(&ddr_map->isp_mem_param[stage_num][mem], &ddr_map_size->isp_mem_param[stage_num][mem], size, From 12ea58282087b9df896fd0a43a92fa83e56b33c9 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Thu, 16 Apr 2026 21:24:50 +0800 Subject: [PATCH 1237/5214] staging: media: atomisp: fix map and vmap leaks in stat buffer allocation There are memory leaks in drivers/staging/media/atomisp/pci/atomisp_compat_css20.c. In atomisp_css_allocate_stat_buffers(), s3a_map is allocated by ia_css_isp_3a_statistics_map_allocate() and its backing memory is mapped via hmm_vmap(). When dis_buf allocation fails, the error path frees s3a_data but does not unmap or free s3a_map. Similarly, when md_buf allocation fails, neither s3a_map nor dvs_map (and their hmm vmaps) are freed. Add the missing hmm_vunmap() and map free calls on both error paths, matching the cleanup order used in atomisp_css_free_3a_buffer() and atomisp_css_free_dis_buffer(). Signed-off-by: Huihui Huang Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/atomisp_compat_css20.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index d2bacd1ffe7b..0ee52637ea30 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -1117,8 +1117,11 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, dvs_grid_info); if (!dis_buf->dis_data) { dev_err(isp->dev, "dvs buf allocation failed.\n"); - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); + } return -EINVAL; } @@ -1132,10 +1135,16 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, md_buf->metadata = ia_css_metadata_allocate( &asd->stream_env[stream_id].stream_info.metadata_info); if (!md_buf->metadata) { - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); - if (dis_buf) + } + if (dis_buf) { + hmm_vunmap(dis_buf->dis_data->data_ptr); + ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map); ia_css_isp_dvs2_statistics_free(dis_buf->dis_data); + } dev_err(isp->dev, "metadata buf allocation failed.\n"); return -EINVAL; } From f6f729763c6a629893e679972dac9e2ab21f018f Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Tue, 10 Mar 2026 16:03:14 +0100 Subject: [PATCH 1238/5214] staging: media: atomisp: replace msleep() with fsleep() in atomisp-gc2235.c Replace msleep(5) with fsleep(5000) to avoid sleeping longer than necessary. msleep() with values less than 20ms may sleep for up to 20ms due to timer granularity. fsleep() selects the appropriate sleep function automatically. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/atomisp-gc2235.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 998c9f46bd06..f9cc5f45fe00 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -433,7 +433,7 @@ static int power_up(struct v4l2_subdev *sd) goto fail_power; } - msleep(5); + fsleep(5000); return 0; fail_clk: From 85354d984208ed64eb35cecbd8b4d84f390dd352 Mon Sep 17 00:00:00 2001 From: Taekyung Oh Date: Mon, 9 Feb 2026 16:57:34 +0000 Subject: [PATCH 1239/5214] staging: media: atomisp: Fix block comment style in ov2722.h Fix coding style warnings reported by checkpatch.pl. Move the comments above the corresponding code lines to align with guideline. Signed-off-by: Taekyung Oh Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/ov2722.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index 00317d105305..b67c26dcae79 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -566,8 +566,8 @@ static const struct ov2722_reg ov2722_VGA_30fps[] = { #endif static const struct ov2722_reg ov2722_1632_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -668,8 +668,8 @@ static const struct ov2722_reg ov2722_1632_1092_30fps[] = { }; static const struct ov2722_reg ov2722_1452_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -878,8 +878,8 @@ static const struct ov2722_reg ov2722_1M3_30fps[] = { #endif static const struct ov2722_reg ov2722_1080p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole - frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, From f4705de3a9d7594a7b5bab507d90ea57a9470b66 Mon Sep 17 00:00:00 2001 From: Taekyung Oh Date: Mon, 9 Feb 2026 16:57:43 +0000 Subject: [PATCH 1240/5214] staging: media: atomisp: remove dead code in ov2722.h Remove unused code blocks enclosed in #if 0 to clean up the code. Signed-off-by: Taekyung Oh Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/ov2722.h | 637 --------------------- 1 file changed, 637 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index b67c26dcae79..7d5ea8801fe7 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -232,339 +232,6 @@ struct ov2722_write_ctrl { struct ov2722_write_buffer buffer; }; -/* - * Register settings for various resolution - */ -#if 0 -static const struct ov2722_reg ov2722_QVGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x0c}, - {OV2722_8BIT, 0x373a, 0x1c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x0c}, - {OV2722_8BIT, 0x3705, 0x06}, - {OV2722_8BIT, 0x3730, 0x0e}, - {OV2722_8BIT, 0x3704, 0x1c}, - {OV2722_8BIT, 0x3f06, 0x00}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x46}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0x63}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x95}, /* H crop end: 1685 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x27}, /* V crop end: 1063 */ - {OV2722_8BIT, 0x3808, 0x01}, - {OV2722_8BIT, 0x3809, 0x50}, /* H output size: 336 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0x00}, /* V output size: 256 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0xc0}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x71}, - {OV2722_8BIT, 0x3815, 0x71}, - {OV2722_8BIT, 0x3612, 0x49}, - {OV2722_8BIT, 0x3618, 0x00}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0xc3}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x77}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x09}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, - -}; - -static const struct ov2722_reg ov2722_480P_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x00}, - {OV2722_8BIT, 0x3801, 0xf2}, /* H crop start: 322 - 80 = 242*/ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xBB}, /* H crop end: 1643 + 80 = 1723*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0xE0}, /* H output size: 656 +80 = 736*/ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; - -static const struct ov2722_reg ov2722_VGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x6B}, /* H crop end: 1643*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0x90}, /* H output size: 656 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x29}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1632_1092_30fps[] = { /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3021, 0x03}, @@ -768,115 +435,6 @@ static const struct ov2722_reg ov2722_1452_1092_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 -static const struct ov2722_reg ov2722_1M3_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x4a}, /* H crop start: 330 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x03}, /* V crop start: 3 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xe1}, /* H crop end: 1761 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x47}, /* V crop end: 1095 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x88}, /* H output size: 1416 */ - {OV2722_8BIT, 0x380a, 0x04}, - {OV2722_8BIT, 0x380b, 0x0a}, /* V output size: 1034 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x05}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1080p_30fps[] = { /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3021, 0x03}, @@ -982,108 +540,6 @@ static const struct ov2722_reg ov2722_1080p_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 /* Currently unused */ -static const struct ov2722_reg ov2722_720p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x40}, /* H crop start: 320 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0xb1}, /* V crop start: 177 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x55}, /* H crop end: 1621 */ - {OV2722_8BIT, 0x3806, 0x03}, - {OV2722_8BIT, 0x3807, 0x95}, /* V crop end: 918 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x10}, /* H output size: 0x0788==1928 */ - {OV2722_8BIT, 0x380a, 0x02}, - {OV2722_8BIT, 0x380b, 0xe0}, /* output size: 0x02DE==734 */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H timing: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa3}, /* V timing: 1187 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x03}, /* H window offset: 3 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* mirror */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, /* manual 3a */ - {OV2722_8BIT, 0x301d, 0xf0}, /* enable group hold */ - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, - {OV2722_8BIT, 0x5184, 0xb0}, - {OV2722_8BIT, 0x5185, 0xb0}, - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, /* {0x3036, 0x2c}, //422.4 MHz */ - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - {OV2722_8BIT, 0x3000, 0x00}, /* added for power optimization */ - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, /* manual 3a */ - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x3F}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x00}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static struct ov2722_resolution ov2722_res_preview[] = { { .desc = "ov2722_1632_1092_30fps", @@ -1128,99 +584,6 @@ static struct ov2722_resolution ov2722_res_preview[] = { #define N_RES_PREVIEW (ARRAY_SIZE(ov2722_res_preview)) -/* - * Disable non-preview configurations until the configuration selection is - * improved. - */ -#if 0 -struct ov2722_resolution ov2722_res_still[] = { - { - .desc = "ov2722_480P_30fps", - .width = 1632, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1632_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1452_1092_30fps", - .width = 1452, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1452_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_STILL (ARRAY_SIZE(ov2722_res_still)) - -struct ov2722_resolution ov2722_res_video[] = { - { - .desc = "ov2722_QVGA_30fps", - .width = 336, - .height = 256, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_QVGA_30fps, - .mipi_freq = 364800, - }, - { - .desc = "ov2722_480P_30fps", - .width = 736, - .height = 496, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_480P_30fps, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video)) -#endif - static struct ov2722_resolution *ov2722_res = ov2722_res_preview; static unsigned long N_RES = N_RES_PREVIEW; #endif From 11f94f5b1d3001314fb0f7d1739c74482fbba960 Mon Sep 17 00:00:00 2001 From: Pedro Pontes Date: Thu, 16 Apr 2026 10:42:15 -0300 Subject: [PATCH 1241/5214] media: atomisp: use kmalloc_objs for array allocations Convert manual kmalloc() multiplications to the modern kmalloc_objs() interface to improve type safety and prevent potential integer overflows. Signed-off-by: Pedro Pontes Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 57 ++++++++++++---------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index c136c1745eb9..00082276f1db 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5819,36 +5819,37 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( i *= max_scale_factor_per_stage; } - descr->in_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; @@ -5968,35 +5969,37 @@ ia_css_pipe_create_cas_scaler_desc(struct ia_css_pipe *pipe, descr->num_stage = num_stages; - descr->in_info = kmalloc_objs(struct ia_css_frame_info, - descr->num_stage); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; From 4f6f28ff24709710c08557c127b3e4c3fb1b4159 Mon Sep 17 00:00:00 2001 From: Martin Hecht Date: Fri, 8 May 2026 11:59:03 +0200 Subject: [PATCH 1242/5214] media: i2c: alvium: fix critical pointer access in alvium_ctrl_init The current implementation of alvium_ctrl_init creates several controls in function alvium_ctrl_init and uses the returned pointer without check. That can cause write access over NULL-pointer for several controls. The reworked code checks the pointers before adding flags. Fixes: 0a7af872915e ("media: i2c: Add support for alvium camera") Cc: stable@vger.kernel.org Signed-off-by: Martin Hecht Signed-off-by: Sakari Ailus --- drivers/media/i2c/alvium-csi2.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 955b7072a560..dd991c2ee700 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_PIXEL_RATE, 0, ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1, ALVIUM_DEFAULT_PIXEL_RATE_MHZ); - ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Link freq is fixed */ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 0, 0, &alvium->link_freq); - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Auto/manual white balance */ if (alvium->avail_ft.auto_whiteb) { ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + if (ctrls->auto_wb) + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); } ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, @@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_bbalance, alvium->inc_bbalance, alvium->dft_bbalance); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, alvium->min_rbalance, @@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + if (ctrls->auto_exp) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, + V4L2_EXPOSURE_MANUAL, true); } ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, @@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_exp, alvium->inc_exp, alvium->dft_exp); - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->exposure) + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; /* Auto/manual gain */ if (alvium->avail_ft.auto_gain) { ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + if (ctrls->auto_gain) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); } if (alvium->avail_ft.gain) { @@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_gain, alvium->inc_gain, alvium->dft_gain); - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->gain) + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; } if (alvium->avail_ft.sat) From 8ba166ff7c921da610376156d7c0a5fc985fa983 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:38 +0800 Subject: [PATCH 1243/5214] media: synopsys: Fix IPI using hardcoded datatype The imx93_csi2rx_dphy_ipi_enable() function configures the IPI datatype using csi2->formats->csi_dt, which is initialized during probe but never updated in set_fmt(). This causes the IPI to always use the probe-time default datatype, ignoring the actual media bus format negotiated at runtime. When userspace requests a different format, the IPI hardware is configured with the wrong datatype, resulting in incorrect image output. Fix by updating csi2->formats in the set_fmt callback to reflect the currently negotiated format, ensuring the IPI configuration matches the runtime datatype. Fixes: ec40b431f0ab ("media: synopsys: csi2rx: add i.MX93 support") Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 02eb4a6cafad..0b80e84983f9 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -311,7 +311,7 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) WARN_ON(csi2->formats_num == 0); for (unsigned int i = 0; i < csi2->formats_num; i++) { - const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; + const struct dw_mipi_csi2rx_format *format = &formats[i]; if (format->code == mbus_code) return format; @@ -433,7 +433,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= csi2->formats_num) return -EINVAL; - code->code = csi2->formats[code->index].code; + code->code = formats[code->index].code; return 0; default: return -EINVAL; @@ -470,6 +470,17 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, *src = *sink; + /* Store the CSIS format descriptor for active formats. */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + csi2->formats = fmt ? : + dw_mipi_csi2rx_find_format(csi2, default_format.code); + + if (!csi2->formats) { + dev_err(csi2->dev, "Failed to find valid format\n"); + return -EINVAL; + } + } + return 0; } From 4a7004c03d2227643c2ef2e546ad173b2b1480b7 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:39 +0800 Subject: [PATCH 1244/5214] media: synopsys: Add support for RAW16 Bayer formats Add higher bit-depth raw image data support for the sensors, which supports 16-bit output. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 0b80e84983f9..f45466ede2bb 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -252,6 +252,26 @@ static const struct dw_mipi_csi2rx_format formats[] = { .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, }, + { + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, }; static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) From 9823c6bd80082906e2b1d0ca03f470ff395b5557 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:40 +0800 Subject: [PATCH 1245/5214] media: synopsys: Add support for multiple streams The current driver only supports single stream operation. Add support for multiple concurrent streams by tracking enabled streams with a bitmask and only initializing the hardware once for the first stream. This enables use cases such as surround view systems where multiple camera streams need to be processed simultaneously through the same CSI-2 receiver interface. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index f45466ede2bb..92178a3dec5d 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -113,6 +113,7 @@ struct dw_mipi_csi2rx_device { enum v4l2_mbus_type bus_type; u32 lanes_num; + u64 enabled_streams; const struct dw_mipi_csi2rx_drvdata *drvdata; }; @@ -539,26 +540,33 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, DW_MIPI_CSI2RX_PAD_SRC, &streams_mask); - ret = pm_runtime_resume_and_get(dev); - if (ret) - goto err; + if (!csi2->enabled_streams) { + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err; - ret = dw_mipi_csi2rx_start(csi2); - if (ret) { - dev_err(dev, "failed to enable CSI hardware\n"); - goto err_pm_runtime_put; + ret = dw_mipi_csi2rx_start(csi2); + if (ret) { + dev_err(dev, "failed to enable CSI hardware\n"); + goto err_pm_runtime_put; + } } ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); if (ret) goto err_csi_stop; + csi2->enabled_streams |= streams_mask; + return 0; err_csi_stop: - dw_mipi_csi2rx_stop(csi2); + /* Stop CSI hardware if no streams are enabled */ + if (!csi2->enabled_streams) + dw_mipi_csi2rx_stop(csi2); err_pm_runtime_put: - pm_runtime_put(dev); + if (!csi2->enabled_streams) + pm_runtime_put(dev); err: return ret; } @@ -583,10 +591,15 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, &streams_mask); ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); + if (ret) + dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret); - dw_mipi_csi2rx_stop(csi2); + csi2->enabled_streams &= ~streams_mask; - pm_runtime_put(dev); + if (!csi2->enabled_streams) { + dw_mipi_csi2rx_stop(csi2); + pm_runtime_put(dev); + } return ret; } From 82cdee9be284af7f3aa2b2ba1fa61c410642cac4 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:41 +0800 Subject: [PATCH 1246/5214] media: synopsys: Add PHY stopstate wait for i.MX93 Implement waiting for D-PHY lanes to enter stop state on i.MX93. This ensures proper PHY initialization by verifying that the clock lane and all active data lanes have entered the stop state before proceeding with further operations. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 92178a3dec5d..8a34aec550ad 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #define DW_REG_EXIST BIT(31) #define DW_REG(x) (DW_REG_EXIST | (x)) +#define DPHY_STOPSTATE_CLK_LANE BIT(16) + #define DPHY_TEST_CTRL0_TEST_CLR BIT(0) #define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x)) @@ -65,6 +68,7 @@ enum dw_mipi_csi2rx_regs_index { DW_MIPI_CSI2RX_PHY_TST_CTRL0, DW_MIPI_CSI2RX_PHY_TST_CTRL1, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, + DW_MIPI_CSI2RX_PHY_STOPSTATE, DW_MIPI_CSI2RX_IPI_DATATYPE, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, DW_MIPI_CSI2RX_IPI_MODE, @@ -87,6 +91,7 @@ struct dw_mipi_csi2rx_drvdata { void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2); + int (*wait_for_phy_stopstate)(struct dw_mipi_csi2rx_device *csi2); }; struct dw_mipi_csi2rx_format { @@ -139,6 +144,7 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80), @@ -556,10 +562,19 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, if (ret) goto err_csi_stop; + if (!csi2->enabled_streams && + csi2->drvdata->wait_for_phy_stopstate) { + ret = csi2->drvdata->wait_for_phy_stopstate(csi2); + if (ret) + goto err_disable_streams; + } + csi2->enabled_streams |= streams_mask; return 0; +err_disable_streams: + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); err_csi_stop: /* Stop CSI hardware if no streams are enabled */ if (!csi2->enabled_streams) @@ -871,11 +886,32 @@ static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2) dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val); } +static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2) +{ + struct device *dev = csi2->dev; + u32 stopstate_mask; + u32 val; + int ret; + + stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0); + + ret = read_poll_timeout(dw_mipi_csi2rx_read, val, + (val & stopstate_mask) == stopstate_mask, + 10, 1000, true, + csi2, DW_MIPI_CSI2RX_PHY_STOPSTATE); + if (ret) + dev_err(dev, "lanes are not in stop state: %#x, expected %#x\n", + val, stopstate_mask); + + return ret; +} + static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { .regs = imx93_regs, .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, .ipi_enable = imx93_csi2rx_dphy_ipi_enable, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, }; static const struct of_device_id dw_mipi_csi2rx_of_match[] = { From 113423645c9363acdb03859c65b5b030ae9ddd43 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:42 +0800 Subject: [PATCH 1247/5214] media: dt-bindings: add NXP i.MX95 compatible string The i.MX95 CSI-2 controller is nearly identical to i.MX93, with the main difference being the data output interface: i.MX93 use IPI (Image Pixel Interface), which requires: - Pixel clock input - Software configuration through registers i.MX95 uses IDI (Image Data Interface), which: - Does not require pixel clock - Is software transparent (no register configuration needed) Due to these differences in register layout and initialization needs, the two variants cannot share the same compatible string. The driver needs to distinguish between them to handle the interface correctly. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/rockchip,rk3568-mipi-csi2.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml index fbcf28e9e1da..8bfad0fca3b7 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -19,6 +19,7 @@ properties: oneOf: - enum: - fsl,imx93-mipi-csi2 + - fsl,imx95-mipi-csi2 - rockchip,rk3568-mipi-csi2 - items: - enum: @@ -140,6 +141,21 @@ allOf: clock-names: minItems: 2 + - if: + properties: + compatible: + contains: + const: fsl,imx95-mipi-csi2 + then: + properties: + interrupts: + maxItems: 1 + interrupt-names: false + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + examples: - | #include From af92c6d86b2bc3849935dc6ecfe78eba016a47a4 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:43 +0800 Subject: [PATCH 1248/5214] media: synopsys: Add support for i.MX95 Add support for the i.MX95 MIPI CSI-2 receiver. The i.MX95 variant is nearly identical to i.MX93, with the main difference being the use of IDI (Image Data Interface) instead of IPI (Image Pixel Interface). However, the IDI interface is transparent to software, requiring only a different register map definition while sharing the same PHY control functions with i.MX93. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 8a34aec550ad..41e48365167e 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -154,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0), }; +static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = { + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4), + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8), + [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), + [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), + [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), + [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), +}; + static const struct v4l2_mbus_framefmt default_format = { .width = 3840, .height = 2160, @@ -914,11 +925,22 @@ static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, }; +static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = { + .regs = imx95_regs, + .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, + .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, +}; + static const struct of_device_id dw_mipi_csi2rx_of_match[] = { { .compatible = "fsl,imx93-mipi-csi2", .data = &imx93_drvdata, }, + { + .compatible = "fsl,imx95-mipi-csi2", + .data = &imx95_drvdata, + }, { .compatible = "rockchip,rk3568-mipi-csi2", .data = &rk3568_drvdata, From 614e627e0d0bf3de3819c098adde06cad633be2b Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Tue, 19 May 2026 23:00:30 +0300 Subject: [PATCH 1249/5214] media: i2c: imx274: trivial cleanup Fix some typos, removed superfluous comments/code, some minor code alignment. Signed-off-by: Eugen Hristev Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx274.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 882dc2e9dd8f..76da647f9cf9 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -182,7 +182,7 @@ struct imx274_mode { }; /* - * imx274 test pattern related structure + * imx274 test pattern related enum */ enum { TEST_PATTERN_DISABLED = 0, @@ -533,7 +533,7 @@ static const struct imx274_mode imx274_modes[] = { /* * struct imx274_ctrls - imx274 ctrl structure * @handler: V4L2 ctrl handler structure - * @exposure: Pointer to expsure ctrl structure + * @exposure: Pointer to exposure ctrl structure * @gain: Pointer to gain ctrl structure * @vflip: Pointer to vflip ctrl structure * @test_pattern: Pointer to test pattern ctrl structure @@ -547,7 +547,7 @@ struct imx274_ctrls { }; /* - * struct stim274 - imx274 device structure + * struct stimx274 - imx274 device structure * @sd: V4L2 subdevice structure * @pad: Media pad structure * @client: Pointer to I2C client @@ -587,9 +587,6 @@ struct stimx274 { ? rounddown((dim), (step)) \ : rounddown((dim) + (step) / 2, (step)))) -/* - * Function declaration - */ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl); static int imx274_set_exposure(struct stimx274 *priv, int val); static int imx274_set_vflip(struct stimx274 *priv, int val); @@ -640,9 +637,9 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) for (next = table;; next++) { if ((next->addr != range_start + range_count) || - (next->addr == IMX274_TABLE_END) || - (next->addr == IMX274_TABLE_WAIT_MS) || - (range_count == max_range_vals)) { + next->addr == IMX274_TABLE_END || + next->addr == IMX274_TABLE_WAIT_MS || + range_count == max_range_vals) { if (range_count == 1) err = regmap_write(regmap, range_start, range_vals[0]); @@ -650,8 +647,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) err = regmap_bulk_write(regmap, range_start, &range_vals[0], range_count); - else - err = 0; if (err) return err; @@ -1960,7 +1955,6 @@ static int imx274_probe(struct i2c_client *client) struct device *dev = &client->dev; int ret; - /* initialize imx274 */ imx274 = devm_kzalloc(dev, sizeof(*imx274), GFP_KERNEL); if (!imx274) return -ENOMEM; @@ -2125,7 +2119,7 @@ static const struct dev_pm_ops imx274_pm_ops = { static struct i2c_driver imx274_i2c_driver = { .driver = { - .name = DRIVER_NAME, + .name = DRIVER_NAME, .pm = &imx274_pm_ops, .of_match_table = imx274_of_id_table, }, From 148d96822b100a1ff81247f37957722074d07c81 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:06 +0530 Subject: [PATCH 1250/5214] media: ti: j721e-csi2rx: Remove word size alignment on frame width j721e-csi2rx driver has a limitation of frame width being a multiple word size. However, there is no such limitation imposed by the hardware [1]. Remove this limitation from the driver. Link: https://www.ti.com/lit/pdf/spruj16 Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index b75aa363d1bf..710d05a05353 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,7 +43,6 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 -#define PSIL_WORD_SIZE_BYTES 16 /* * There are no hard limits on the width or height. The DMA engine can handle * all sizes. The max width and height are arbitrary numbers for this driver. @@ -250,19 +249,12 @@ static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt, struct v4l2_format *v4l2_fmt) { struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix; - unsigned int pixels_in_word; - - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / csi_fmt->bpp; /* Clamp width and height to sensible maximums (16K x 16K) */ pix->width = clamp_t(unsigned int, pix->width, - pixels_in_word, - MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); + 1, MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); pix->height = clamp_t(unsigned int, pix->height, 1, MAX_HEIGHT_LINES); - /* Width should be a multiple of transfer word-size */ - pix->width = rounddown(pix->width, pixels_in_word); - v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix->pixelformat = csi_fmt->fourcc; pix->bytesperline = pix->width * (csi_fmt->bpp / 8); @@ -360,23 +352,15 @@ static int ti_csi2rx_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct ti_csi2rx_fmt *fmt; - unsigned int pixels_in_word; fmt = find_format_by_fourcc(fsize->pixel_format); if (!fmt || fsize->index != 0) return -EINVAL; - /* - * Number of pixels in one PSI-L word. The transfer happens in multiples - * of PSI-L word sizes. - */ - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / fmt->bpp; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = pixels_in_word; - fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES * 8 / fmt->bpp, - pixels_in_word); - fsize->stepwise.step_width = pixels_in_word; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = MAX_WIDTH_BYTES * 8 / fmt->bpp; + fsize->stepwise.step_width = 1; fsize->stepwise.min_height = 1; fsize->stepwise.max_height = MAX_HEIGHT_LINES; fsize->stepwise.step_height = 1; From bfd10a287ca277508b822f49ad469dbee316b597 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:07 +0530 Subject: [PATCH 1251/5214] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans The CSI2RX SHIM IP can support 32x DMA channels. These can be used to split incoming "streams" of data on the CSI-RX port, distinguished by MIPI Virtual Channel (or Data Type), into different locations in memory. Actual number of DMA channels allocated to CSI-RX is dependent on the usecase, and can be modified using the K3 Resource Partitioning tool [1]. So set the minimum channels as 1 and maximum as 32. Link: https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/10_00_07_04/exports/docs/linux/How_to_Guides/Host/K3_Resource_Partitioning_Tool.html [1] Link: https://www.ti.com/lit/pdf/spruiv7 Signed-off-by: Jai Luthra Reviewed-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring (Arm) Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../bindings/media/ti,j721e-csi2rx-shim.yaml | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml index b9f033f2f3ce..bf62998b0445 100644 --- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml +++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml @@ -20,11 +20,44 @@ properties: const: ti,j721e-csi2rx-shim dmas: - maxItems: 1 + minItems: 1 + maxItems: 32 dma-names: + minItems: 1 items: - const: rx0 + - const: rx1 + - const: rx2 + - const: rx3 + - const: rx4 + - const: rx5 + - const: rx6 + - const: rx7 + - const: rx8 + - const: rx9 + - const: rx10 + - const: rx11 + - const: rx12 + - const: rx13 + - const: rx14 + - const: rx15 + - const: rx16 + - const: rx17 + - const: rx18 + - const: rx19 + - const: rx20 + - const: rx21 + - const: rx22 + - const: rx23 + - const: rx24 + - const: rx25 + - const: rx26 + - const: rx27 + - const: rx28 + - const: rx29 + - const: rx30 + - const: rx31 reg: maxItems: 1 @@ -62,8 +95,8 @@ examples: ti_csi2rx0: ticsi2rx@4500000 { compatible = "ti,j721e-csi2rx-shim"; - dmas = <&main_udmap 0x4940>; - dma-names = "rx0"; + dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>; + dma-names = "rx0", "rx1"; reg = <0x4500000 0x1000>; power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; #address-cells = <1>; From c071e5fd44944745c078df12f27f86916b214ab7 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:08 +0530 Subject: [PATCH 1252/5214] media: ti: j721e-csi2rx: separate out device and context The TI CSI2RX wrapper has two parts: the main device and the DMA contexts. The driver was originally written with single camera capture in mind, so only one DMA context was needed. For the sake of simplicity, the context specific stuff was not modeled different to the main device. To enable multiplexed stream capture, the contexts need to be separated out from the main device. Create a struct ti_csi2rx_ctx that holds the DMA context specific things. Separate out functions handling the device and context related functionality. Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 425 ++++++++++-------- 1 file changed, 235 insertions(+), 190 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 710d05a05353..23717a3b6c4c 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,6 +43,8 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 +#define TI_CSI2RX_NUM_CTX 1 + /* * There are no hard limits on the width or height. The DMA engine can handle * all sizes. The max width and height are arbitrary numbers for this driver. @@ -69,7 +71,7 @@ struct ti_csi2rx_buffer { /* Common v4l2 buffer. Must be first. */ struct vb2_v4l2_buffer vb; struct list_head list; - struct ti_csi2rx_dev *csi; + struct ti_csi2rx_ctx *ctx; }; enum ti_csi2rx_dma_state { @@ -89,30 +91,38 @@ struct ti_csi2rx_dma { * Queue of buffers submitted to DMA engine. */ struct list_head submitted; - /* Buffer to drain stale data from PSI-L endpoint */ - struct { - void *vaddr; - dma_addr_t paddr; - size_t len; - } drain; +}; + +struct ti_csi2rx_dev; + +struct ti_csi2rx_ctx { + struct ti_csi2rx_dev *csi; + struct video_device vdev; + struct vb2_queue vidq; + struct mutex mutex; /* To serialize ioctls. */ + struct v4l2_format v_fmt; + struct ti_csi2rx_dma dma; + u32 sequence; + u32 idx; }; struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; struct v4l2_device v4l2_dev; - struct video_device vdev; struct media_device mdev; struct media_pipeline pipe; struct media_pad pad; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; - struct vb2_queue vidq; - struct mutex mutex; /* To serialize ioctls. */ - struct v4l2_format v_fmt; - struct ti_csi2rx_dma dma; - u32 sequence; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; u8 pix_per_clk; + /* Buffer to drain stale data from PSI-L endpoint */ + struct { + void *vaddr; + dma_addr_t paddr; + size_t len; + } drain; }; static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { @@ -218,7 +228,7 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { }; /* Forward declaration needed by ti_csi2rx_dma_callback. */ -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf); static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat) @@ -301,7 +311,7 @@ static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); *f = csi->v_fmt; @@ -332,7 +342,7 @@ static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); struct vb2_queue *q = &csi->vidq; int ret; @@ -410,26 +420,35 @@ static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier, static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); - struct video_device *vdev = &csi->vdev; - int ret; + int ret, i; - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) - return ret; + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; + struct video_device *vdev = &ctx->vdev; - ret = media_create_pad_link(&csi->source->entity, CSI2RX_BRIDGE_SOURCE_PAD, - &vdev->entity, csi->pad.index, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - - if (ret) { - video_unregister_device(vdev); - return ret; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto unregister_dev; } + ret = media_create_pad_link(&csi->source->entity, + CSI2RX_BRIDGE_SOURCE_PAD, + &csi->ctx[0].vdev.entity, csi->pad.index, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto unregister_dev; + ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); if (ret) - video_unregister_device(vdev); + goto unregister_dev; + return 0; + +unregister_dev: + i--; + for (; i >= 0; i--) + video_unregister_device(&csi->ctx[i].vdev); return ret; } @@ -474,13 +493,14 @@ static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi) } /* Request maximum possible pixels per clock from the bridge */ -static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; u8 ppc = TI_CSI2RX_MAX_PIX_PER_CLK; struct media_pad *pad; int ret; - pad = media_entity_remote_source_pad_unique(&csi->vdev.entity); + pad = media_entity_remote_source_pad_unique(&ctx->vdev.entity); if (IS_ERR(pad)) return; @@ -493,19 +513,20 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) } } -static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; const struct ti_csi2rx_fmt *fmt; unsigned int reg; - fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat); + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); /* De-assert the pixel interface reset. */ reg = SHIM_CNTL_PIX_RST; writel(reg, csi->shim + SHIM_CNTL); /* Negotiate pixel count from the source */ - ti_csi2rx_request_max_ppc(csi); + ti_csi2rx_request_max_ppc(ctx); reg = SHIM_DMACNTX_EN; reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); @@ -572,8 +593,9 @@ static void ti_csi2rx_drain_callback(void *param) * To prevent that stale data corrupting the subsequent transactions, it is * required to issue DMA requests to drain it out. */ -static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; struct dma_async_tx_descriptor *desc; struct completion drain_complete; dma_cookie_t cookie; @@ -581,8 +603,8 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) init_completion(&drain_complete); - desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr, - csi->dma.drain.len, DMA_DEV_TO_MEM, + desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr, + csi->drain.len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EIO; @@ -597,11 +619,11 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) if (ret) goto out; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); if (!wait_for_completion_timeout(&drain_complete, msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { - dmaengine_terminate_sync(csi->dma.chan); + dmaengine_terminate_sync(ctx->dma.chan); dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n"); ret = -ETIMEDOUT; goto out; @@ -613,8 +635,9 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) static void ti_csi2rx_dma_callback(void *param) { struct ti_csi2rx_buffer *buf = param; - struct ti_csi2rx_dev *csi = buf->csi; - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_ctx *ctx = buf->ctx; + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; /* @@ -622,7 +645,7 @@ static void ti_csi2rx_dma_callback(void *param) * hardware monitor registers. */ buf->vb.vb2_buf.timestamp = ktime_get_ns(); - buf->vb.sequence = csi->sequence++; + buf->vb.sequence = ctx->sequence++; spin_lock_irqsave(&dma->lock, flags); @@ -634,7 +657,7 @@ static void ti_csi2rx_dma_callback(void *param) while (!list_empty(&dma->queue)) { buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - if (ti_csi2rx_start_dma(csi, buf)) { + if (ti_csi2rx_start_dma(ctx, buf)) { dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); @@ -649,17 +672,17 @@ static void ti_csi2rx_dma_callback(void *param) spin_unlock_irqrestore(&dma->lock, flags); } -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf) { unsigned long addr; struct dma_async_tx_descriptor *desc; - size_t len = csi->v_fmt.fmt.pix.sizeimage; + size_t len = ctx->v_fmt.fmt.pix.sizeimage; dma_cookie_t cookie; int ret = 0; addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len, + desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) @@ -673,20 +696,20 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, if (ret) return ret; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); return 0; } -static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; enum ti_csi2rx_dma_state state; unsigned long flags; int ret; spin_lock_irqsave(&dma->lock, flags); - state = csi->dma.state; + state = ctx->dma.state; dma->state = TI_CSI2RX_DMA_STOPPED; spin_unlock_irqrestore(&dma->lock, flags); @@ -697,30 +720,30 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) * is stopped, as the module-level pixel reset cannot be * enforced before terminating DMA. */ - ret = ti_csi2rx_drain_dma(csi); + ret = ti_csi2rx_drain_dma(ctx); if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, + dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); } - ret = dmaengine_terminate_sync(csi->dma.chan); + ret = dmaengine_terminate_sync(ctx->dma.chan); if (ret) - dev_err(csi->dev, "Failed to stop DMA: %d\n", ret); + dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret); } -static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi, +static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_ctx *ctx, enum vb2_buffer_state state) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf, *tmp; unsigned long flags; spin_lock_irqsave(&dma->lock, flags); - list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } - list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.submitted, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } @@ -731,8 +754,8 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q); - unsigned int size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; if (*nplanes) { if (sizes[0] < size) @@ -748,11 +771,11 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = ctx->v_fmt.fmt.pix.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(csi->dev, "Data will not fit into plane\n"); + dev_err(ctx->csi->dev, "Data will not fit into plane\n"); return -EINVAL; } @@ -762,15 +785,15 @@ static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct ti_csi2rx_buffer *buf; - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; bool restart_dma = false; unsigned long flags = 0; int ret; buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); - buf->csi = csi; + buf->ctx = ctx; spin_lock_irqsave(&dma->lock, flags); /* @@ -799,18 +822,18 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) * the application and will only confuse it. Issue a DMA * transaction to drain that up. */ - ret = ti_csi2rx_drain_dma(csi); + ret = ti_csi2rx_drain_dma(ctx); if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, + dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); spin_lock_irqsave(&dma->lock, flags); - ret = ti_csi2rx_start_dma(csi, buf); + ret = ti_csi2rx_start_dma(ctx, buf); if (ret) { vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dma->state = TI_CSI2RX_DMA_IDLE; spin_unlock_irqrestore(&dma->lock, flags); - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); + dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret); } else { list_add_tail(&buf->list, &dma->submitted); spin_unlock_irqrestore(&dma->lock, flags); @@ -820,8 +843,9 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf; unsigned long flags; int ret = 0; @@ -833,18 +857,18 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) return ret; - ret = video_device_pipeline_start(&csi->vdev, &csi->pipe); + ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe); if (ret) goto err; - ti_csi2rx_setup_shim(csi); + ti_csi2rx_setup_shim(ctx); - csi->sequence = 0; + ctx->sequence = 0; spin_lock_irqsave(&dma->lock, flags); buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - ret = ti_csi2rx_start_dma(csi, buf); + ret = ti_csi2rx_start_dma(ctx, buf); if (ret) { dev_err(csi->dev, "Failed to start DMA: %d\n", ret); spin_unlock_irqrestore(&dma->lock, flags); @@ -862,22 +886,23 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err_dma: - ti_csi2rx_stop_dma(csi); + ti_csi2rx_stop_dma(ctx); err_pipeline: - video_device_pipeline_stop(&csi->vdev); + video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX); err: - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; } static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; int ret; - video_device_pipeline_stop(&csi->vdev); + video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX); @@ -886,8 +911,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); - ti_csi2rx_stop_dma(csi); - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR); + ti_csi2rx_stop_dma(ctx); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR); } static const struct vb2_ops csi_vb2_qops = { @@ -898,20 +923,43 @@ static const struct vb2_ops csi_vb2_qops = { .stop_streaming = ti_csi2rx_stop_streaming, }; -static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) { - struct vb2_queue *q = &csi->vidq; + media_device_unregister(&csi->mdev); + v4l2_device_unregister(&csi->v4l2_dev); + media_device_cleanup(&csi->mdev); +} + +static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi) +{ + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); +} + +static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) +{ + dma_release_channel(ctx->dma.chan); + vb2_queue_release(&ctx->vidq); + + video_unregister_device(&ctx->vdev); + + mutex_destroy(&ctx->mutex); +} + +static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx) +{ + struct vb2_queue *q = &ctx->vidq; int ret; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF; - q->drv_priv = csi; + q->drv_priv = ctx; q->buf_struct_size = sizeof(struct ti_csi2rx_buffer); q->ops = &csi_vb2_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->dev = dmaengine_get_dma_device(csi->dma.chan); - q->lock = &csi->mutex; + q->dev = dmaengine_get_dma_device(ctx->dma.chan); + q->lock = &ctx->mutex; q->min_queued_buffers = 1; q->allow_cache_hints = 1; @@ -919,7 +967,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) if (ret) return ret; - csi->vdev.queue = q; + ctx->vdev.queue = q; return 0; } @@ -928,8 +976,9 @@ static int ti_csi2rx_link_validate(struct media_link *link) { struct media_entity *entity = link->sink->entity; struct video_device *vdev = media_entity_to_video_device(entity); - struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev); - struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix; + struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev); + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix; struct v4l2_subdev_format source_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = link->source->index, @@ -982,84 +1031,37 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = { .link_validate = ti_csi2rx_link_validate, }; -static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) { struct dma_slave_config cfg = { .src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES, }; int ret; - INIT_LIST_HEAD(&csi->dma.queue); - INIT_LIST_HEAD(&csi->dma.submitted); - spin_lock_init(&csi->dma.lock); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0"); + if (IS_ERR(ctx->dma.chan)) + return PTR_ERR(ctx->dma.chan); - csi->dma.state = TI_CSI2RX_DMA_STOPPED; - - csi->dma.chan = dma_request_chan(csi->dev, "rx0"); - if (IS_ERR(csi->dma.chan)) - return PTR_ERR(csi->dma.chan); - - ret = dmaengine_slave_config(csi->dma.chan, &cfg); + ret = dmaengine_slave_config(ctx->dma.chan, &cfg); if (ret) { - dma_release_channel(csi->dma.chan); + dma_release_channel(ctx->dma.chan); return ret; } - csi->dma.drain.len = DRAIN_BUFFER_SIZE; - csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len, - &csi->dma.drain.paddr, - GFP_KERNEL); - if (!csi->dma.drain.vaddr) - return -ENOMEM; - return 0; } static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) { struct media_device *mdev = &csi->mdev; - struct video_device *vdev = &csi->vdev; - const struct ti_csi2rx_fmt *fmt; - struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix; int ret; - fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY); - if (!fmt) - return -EINVAL; - - pix_fmt->width = 640; - pix_fmt->height = 480; - pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; - - ti_csi2rx_fill_fmt(fmt, &csi->v_fmt); - mdev->dev = csi->dev; mdev->hw_revision = 1; strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model)); media_device_init(mdev); - strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name)); - vdev->v4l2_dev = &csi->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->fops = &csi_fops; - vdev->ioctl_ops = &csi_ioctl_ops; - vdev->release = video_device_release_empty; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_IO_MC; - vdev->lock = &csi->mutex; - video_set_drvdata(vdev, csi); - - csi->pad.flags = MEDIA_PAD_FL_SINK; - vdev->entity.ops = &ti_csi2rx_video_entity_ops; - ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad); - if (ret) - return ret; - csi->v4l2_dev.mdev = mdev; ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); @@ -1076,36 +1078,72 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) return 0; } -static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) { - dma_free_coherent(csi->dev, csi->dma.drain.len, - csi->dma.drain.vaddr, csi->dma.drain.paddr); - csi->dma.drain.vaddr = NULL; - dma_release_channel(csi->dma.chan); -} + struct ti_csi2rx_dev *csi = ctx->csi; + struct video_device *vdev = &ctx->vdev; + const struct ti_csi2rx_fmt *fmt; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; + int ret; -static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) -{ - media_device_unregister(&csi->mdev); - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(&csi->mdev); -} + mutex_init(&ctx->mutex); -static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi) -{ - v4l2_async_nf_unregister(&csi->notifier); - v4l2_async_nf_cleanup(&csi->notifier); -} + fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY); + if (!fmt) + return -EINVAL; -static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi) -{ - vb2_queue_release(&csi->vidq); + pix_fmt->width = 640; + pix_fmt->height = 480; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601, + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE, + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB, + + ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); + + csi->pad.flags = MEDIA_PAD_FL_SINK; + vdev->entity.ops = &ti_csi2rx_video_entity_ops; + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad); + if (ret) + return ret; + + snprintf(vdev->name, sizeof(vdev->name), "%s context %u", + dev_name(csi->dev), ctx->idx); + vdev->v4l2_dev = &csi->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->fops = &csi_fops; + vdev->ioctl_ops = &csi_ioctl_ops; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->lock = &ctx->mutex; + video_set_drvdata(vdev, ctx); + + INIT_LIST_HEAD(&ctx->dma.queue); + INIT_LIST_HEAD(&ctx->dma.submitted); + spin_lock_init(&ctx->dma.lock); + ctx->dma.state = TI_CSI2RX_DMA_STOPPED; + + ret = ti_csi2rx_init_dma(ctx); + if (ret) + return ret; + + ret = ti_csi2rx_init_vb2q(ctx); + if (ret) + goto cleanup_dma; + + return 0; + +cleanup_dma: + dma_release_channel(ctx->dma.chan); + return ret; } static int ti_csi2rx_probe(struct platform_device *pdev) { struct ti_csi2rx_dev *csi; - int ret; + int ret, i; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); if (!csi) @@ -1114,62 +1152,69 @@ static int ti_csi2rx_probe(struct platform_device *pdev) csi->dev = &pdev->dev; platform_set_drvdata(pdev, csi); - mutex_init(&csi->mutex); csi->shim = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi->shim)) { ret = PTR_ERR(csi->shim); - goto err_mutex; + return ret; } - ret = ti_csi2rx_init_dma(csi); - if (ret) - goto err_mutex; + csi->drain.len = DRAIN_BUFFER_SIZE; + csi->drain.vaddr = dma_alloc_coherent(csi->dev, csi->drain.len, + &csi->drain.paddr, + GFP_KERNEL); + if (!csi->drain.vaddr) + return -ENOMEM; ret = ti_csi2rx_v4l2_init(csi); - if (ret) - goto err_dma; - - ret = ti_csi2rx_init_vb2q(csi); if (ret) goto err_v4l2; + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + csi->ctx[i].idx = i; + csi->ctx[i].csi = csi; + ret = ti_csi2rx_init_ctx(&csi->ctx[i]); + if (ret) + goto err_ctx; + } + ret = ti_csi2rx_notifier_register(csi); if (ret) - goto err_vb2q; + goto err_ctx; ret = devm_of_platform_populate(csi->dev); if (ret) { dev_err(csi->dev, "Failed to create children: %d\n", ret); - goto err_subdev; + goto err_notifier; } return 0; -err_subdev: - ti_csi2rx_cleanup_subdev(csi); -err_vb2q: - ti_csi2rx_cleanup_vb2q(csi); -err_v4l2: +err_notifier: + ti_csi2rx_cleanup_notifier(csi); +err_ctx: + i--; + for (; i >= 0; i--) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); -err_dma: - ti_csi2rx_cleanup_dma(csi); -err_mutex: - mutex_destroy(&csi->mutex); +err_v4l2: + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); return ret; } static void ti_csi2rx_remove(struct platform_device *pdev) { struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); + unsigned int i; - video_unregister_device(&csi->vdev); + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); - ti_csi2rx_cleanup_vb2q(csi); - ti_csi2rx_cleanup_subdev(csi); + ti_csi2rx_cleanup_notifier(csi); ti_csi2rx_cleanup_v4l2(csi); - ti_csi2rx_cleanup_dma(csi); - mutex_destroy(&csi->mutex); + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); } static const struct of_device_id ti_csi2rx_of_match[] = { From e94ae7d2827d9c29a61a2ac61acc73a5e6d75cca Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:09 +0530 Subject: [PATCH 1253/5214] media: ti: j721e-csi2rx: prepare SHIM code for multiple contexts Currently the SHIM code to configure the context only touches the first context. Add support for writing to the context's registers based on the context index. Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 23717a3b6c4c..4adfae425f19 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -27,7 +27,7 @@ #define SHIM_CNTL 0x10 #define SHIM_CNTL_PIX_RST BIT(0) -#define SHIM_DMACNTX 0x20 +#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20)) #define SHIM_DMACNTX_EN BIT(31) #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) #define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24) @@ -38,7 +38,7 @@ #define SHIM_DMACNTX_SIZE_16 1 #define SHIM_DMACNTX_SIZE_32 2 -#define SHIM_PSI_CFG0 0x24 +#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20)) #define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0) #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) @@ -568,11 +568,11 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) break; } - writel(reg, csi->shim + SHIM_DMACNTX); + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0); - writel(reg, csi->shim + SHIM_PSI_CFG0); + writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx)); } static void ti_csi2rx_drain_callback(void *param) @@ -890,7 +890,7 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) err_pipeline: video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); err: ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; @@ -905,7 +905,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); ret = v4l2_subdev_call(csi->source, video, s_stream, 0); if (ret) From 16fe3fc16e4b30e2f61a8668afde11d8245aae5a Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:10 +0530 Subject: [PATCH 1254/5214] media: ti: j721e-csi2rx: allocate DMA channel based on context index With multiple contexts, there needs to be a different DMA channel for each context. Earlier, the DMA channel name was hard coded to "rx0" for the sake of simplicity. Generate the DMA channel name based on its index and get the channel corresponding to the context. Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 4adfae425f19..c781b312cea8 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -1036,9 +1036,11 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) struct dma_slave_config cfg = { .src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES, }; + char name[5]; int ret; - ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0"); + snprintf(name, sizeof(name), "rx%u", ctx->idx); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, name); if (IS_ERR(ctx->dma.chan)) return PTR_ERR(ctx->dma.chan); From 982135c0eac6d56c29cb42fdb0c3879cdd369d67 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:11 +0530 Subject: [PATCH 1255/5214] media: ti: j721e-csi2rx: add a subdev for the core device With single stream capture, it was simpler to use the video device as the media entity representing the main TI CSI2RX device. Now with multi stream capture coming into the picture, the model has shifted to each video device having a link to the main device's subdev. The routing would then be set on this subdev. Add this subdev, link each context to this subdev's entity and link the subdev's entity to the source. Also add an array of media pads. It will have one sink pad and source pads equal to the number of contexts. Support the new enable_stream()/disable_stream() APIs in the subdev instead of s_stream() hook. Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 288 +++++++++++++++--- 1 file changed, 244 insertions(+), 44 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index c781b312cea8..e01d95eab19c 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -54,6 +54,11 @@ #define MAX_WIDTH_BYTES SZ_16K #define MAX_HEIGHT_LINES SZ_16K +#define TI_CSI2RX_PAD_SINK 0 +#define TI_CSI2RX_PAD_FIRST_SOURCE 1 +#define TI_CSI2RX_NUM_SOURCE_PADS 1 +#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS) + #define DRAIN_TIMEOUT_MS 50 #define DRAIN_BUFFER_SIZE SZ_32K @@ -102,6 +107,7 @@ struct ti_csi2rx_ctx { struct mutex mutex; /* To serialize ioctls. */ struct v4l2_format v_fmt; struct ti_csi2rx_dma dma; + struct media_pad pad; u32 sequence; u32 idx; }; @@ -109,12 +115,14 @@ struct ti_csi2rx_ctx { struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; + unsigned int enable_count; struct v4l2_device v4l2_dev; struct media_device mdev; struct media_pipeline pipe; - struct media_pad pad; + struct media_pad pads[TI_CSI2RX_NUM_PADS]; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; + struct v4l2_subdev subdev; struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ @@ -125,6 +133,22 @@ struct ti_csi2rx_dev { } drain; }; +static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ti_csi2rx_dev, subdev); +} + +static const struct v4l2_mbus_framefmt ti_csi2rx_default_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, +}; + static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, @@ -422,6 +446,18 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); int ret, i; + /* Create link from source to subdev */ + ret = media_create_pad_link(&csi->source->entity, + CSI2RX_BRIDGE_SOURCE_PAD, + &csi->subdev.entity, + TI_CSI2RX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + + if (ret) + return ret; + + /* Create and link video nodes for all DMA contexts */ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; struct video_device *vdev = &ctx->vdev; @@ -429,15 +465,17 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto unregister_dev; - } - ret = media_create_pad_link(&csi->source->entity, - CSI2RX_BRIDGE_SOURCE_PAD, - &csi->ctx[0].vdev.entity, csi->pad.index, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto unregister_dev; + ret = media_create_pad_link(&csi->subdev.entity, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + video_unregister_device(vdev); + goto unregister_dev; + } + } ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); if (ret) @@ -447,8 +485,10 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) unregister_dev: i--; - for (; i >= 0; i--) + for (; i >= 0; i--) { + media_entity_remove_links(&csi->ctx[i].vdev.entity); video_unregister_device(&csi->ctx[i].vdev); + } return ret; } @@ -493,14 +533,13 @@ static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi) } /* Request maximum possible pixels per clock from the bridge */ -static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_ctx *ctx) +static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) { - struct ti_csi2rx_dev *csi = ctx->csi; u8 ppc = TI_CSI2RX_MAX_PIX_PER_CLK; struct media_pad *pad; int ret; - pad = media_entity_remote_source_pad_unique(&ctx->vdev.entity); + pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (IS_ERR(pad)) return; @@ -526,7 +565,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) writel(reg, csi->shim + SHIM_CNTL); /* Negotiate pixel count from the source */ - ti_csi2rx_request_max_ppc(ctx); + ti_csi2rx_request_max_ppc(csi); reg = SHIM_DMACNTX_EN; reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); @@ -879,7 +918,9 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) dma->state = TI_CSI2RX_DMA_ACTIVE; spin_unlock_irqrestore(&dma->lock, flags); - ret = v4l2_subdev_call(csi->source, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE, + BIT_U64(0)); if (ret) goto err_dma; @@ -907,7 +948,9 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); - ret = v4l2_subdev_call(csi->source, video, s_stream, 0); + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE, + BIT_U64(0)); if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); @@ -923,8 +966,121 @@ static const struct vb2_ops csi_vb2_qops = { .stop_streaming = ti_csi2rx_stop_streaming, }; +static int ti_csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(ti_csi2rx_formats)) + return -EINVAL; + + code_enum->code = ti_csi2rx_formats[code_enum->index].code; + + return 0; +} + +static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + + /* No transcoding, don't allow setting source fmt */ + if (format->pad > TI_CSI2RX_PAD_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + if (!find_format_by_code(format->format.code)) + format->format.code = ti_csi2rx_formats[0].code; + + format->format.field = V4L2_FIELD_NONE; + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE, + format->stream); + *fmt = format->format; + + return 0; +} + +static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_SINK); + *fmt = ti_csi2rx_default_fmt; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE); + *fmt = ti_csi2rx_default_fmt; + + return 0; +} + +static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct media_pad *remote_pad; + int ret = 0; + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (!remote_pad) + return -ENODEV; + + ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index, + BIT_U64(0)); + if (ret) + return ret; + + csi->enable_count++; + + return 0; +} + +static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct media_pad *remote_pad; + int ret = 0; + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (!remote_pad) + return -ENODEV; + + if (csi->enable_count == 0) + return -EINVAL; + + ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index, + BIT_U64(0)); + if (!ret) + --csi->enable_count; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { + .enum_mbus_code = ti_csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ti_csi2rx_sd_set_fmt, + .enable_streams = ti_csi2rx_sd_enable_streams, + .disable_streams = ti_csi2rx_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = { + .pad = &ti_csi2rx_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ti_csi2rx_internal_ops = { + .init_state = ti_csi2rx_sd_init_state, +}; + static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) { + v4l2_subdev_cleanup(&csi->subdev); media_device_unregister(&csi->mdev); v4l2_device_unregister(&csi->v4l2_dev); media_device_cleanup(&csi->mdev); @@ -979,48 +1135,52 @@ static int ti_csi2rx_link_validate(struct media_link *link) struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev); struct ti_csi2rx_dev *csi = ctx->csi; struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix; - struct v4l2_subdev_format source_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = link->source->index, - }; + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct ti_csi2rx_fmt *ti_fmt; - int ret; - ret = v4l2_subdev_call_state_active(csi->source, pad, - get_fmt, &source_fmt); - if (ret) - return ret; + state = v4l2_subdev_lock_and_get_active_state(&csi->subdev); + format = v4l2_subdev_state_get_format(state, link->source->index, 0); + v4l2_subdev_unlock_state(state); - if (source_fmt.format.width != csi_fmt->width) { + if (!format) { + dev_err(csi->dev, + "No format present on \"%s\":%u:0\n", + link->source->entity->name, link->source->index); + return 0; + } + + if (format->width != csi_fmt->width) { dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n", - source_fmt.format.width, csi_fmt->width); + format->width, csi_fmt->width); return -EPIPE; } - if (source_fmt.format.height != csi_fmt->height) { + if (format->height != csi_fmt->height) { dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n", - source_fmt.format.height, csi_fmt->height); + format->height, csi_fmt->height); return -EPIPE; } - if (source_fmt.format.field != csi_fmt->field && + if (format->field != csi_fmt->field && csi_fmt->field != V4L2_FIELD_NONE) { dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n", - source_fmt.format.field, csi_fmt->field); + format->field, csi_fmt->field); return -EPIPE; } - ti_fmt = find_format_by_code(source_fmt.format.code); + ti_fmt = find_format_by_code(format->code); if (!ti_fmt) { dev_dbg(csi->dev, "Media bus format 0x%x not supported\n", - source_fmt.format.code); + format->code); return -EPIPE; } if (ti_fmt->fourcc != csi_fmt->pixelformat) { dev_dbg(csi->dev, - "Cannot transform source fmt 0x%x to sink fmt 0x%x\n", - ti_fmt->fourcc, csi_fmt->pixelformat); + "Cannot transform \"%s\":%u format %p4cc to %p4cc\n", + link->source->entity->name, link->source->index, + &ti_fmt->fourcc, &csi_fmt->pixelformat); return -EPIPE; } @@ -1031,6 +1191,11 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = { .link_validate = ti_csi2rx_link_validate, }; +static const struct media_entity_operations ti_csi2rx_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) { struct dma_slave_config cfg = { @@ -1056,6 +1221,7 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) { struct media_device *mdev = &csi->mdev; + struct v4l2_subdev *sd = &csi->subdev; int ret; mdev->dev = csi->dev; @@ -1068,16 +1234,51 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); if (ret) - return ret; + goto cleanup_media; ret = media_device_register(mdev); - if (ret) { - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(mdev); - return ret; - } + if (ret) + goto unregister_v4l2; + + v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); + sd->internal_ops = &ti_csi2rx_internal_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); + sd->dev = csi->dev; + sd->entity.ops = &ti_csi2rx_subdev_entity_ops; + + csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE; + i < TI_CSI2RX_NUM_PADS; i++) + csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads), + csi->pads); + if (ret) + goto unregister_media; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto unregister_media; + + ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); + if (ret) + goto cleanup_subdev; return 0; + +cleanup_subdev: + v4l2_subdev_cleanup(sd); +unregister_media: + media_device_unregister(mdev); +unregister_v4l2: + v4l2_device_unregister(&csi->v4l2_dev); +cleanup_media: + media_device_cleanup(mdev); + + return ret; } static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) @@ -1104,9 +1305,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); - csi->pad.flags = MEDIA_PAD_FL_SINK; + ctx->pad.flags = MEDIA_PAD_FL_SINK; vdev->entity.ops = &ti_csi2rx_video_entity_ops; - ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad); + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad); if (ret) return ret; @@ -1214,7 +1415,6 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_notifier(csi); ti_csi2rx_cleanup_v4l2(csi); - dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); } From 8b08ed98f8601156ab7b36e3d054355123229fa3 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:12 +0530 Subject: [PATCH 1256/5214] media: cadence: csi2rx: Move to .enable/disable_streams API The enable_streams() API in v4l2 supports passing a bitmask to enable each pad/stream combination individually on any media subdev. Use this API instead of s_stream() API. Implement the enable_stream and disable_stream hooks in place of the stream-unaware s_stream hook. Remove the lock that was used to serialize stream starts/stops which is not required anymore since the v4l2-core serializes the enable/disable_streams() calls for the subdev. Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 114 +++++++++---------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cde690c6fdee..2b25314ba6ab 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -125,12 +125,6 @@ struct csi2rx_priv { unsigned int count; int error_irq; - /* - * Used to prevent race conditions between multiple, - * concurrent calls to start and stop. - */ - struct mutex lock; - void __iomem *base; struct clk *sys_clk; struct clk *p_clk; @@ -268,20 +262,21 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts = { }; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = CSI2RX_PAD_SINK, - }; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *framefmt; const struct csi2rx_fmt *fmt; s64 link_freq; int ret; - ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt, - &sd_fmt); - if (ret < 0) - return ret; + state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev); - fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code); + framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } + + fmt = csi2rx_get_fmt_by_code(framefmt->code); link_freq = v4l2_get_link_freq(src_pad, fmt->bpp, 2 * csi2rx->num_lanes); @@ -401,16 +396,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) reset_control_deassert(csi2rx->sys_rst); - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); - if (ret) - goto err_disable_sysclk; - clk_disable_unprepare(csi2rx->p_clk); return 0; -err_disable_sysclk: - clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) { reset_control_assert(csi2rx->pixel_rst[i - 1]); @@ -459,9 +448,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); - if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) - dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); - if (csi2rx->dphy) { writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); @@ -485,38 +471,56 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } -static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) +static int csi2rx_enable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); - int ret = 0; + int ret; - mutex_lock(&csi2rx->lock); - - if (enable) { - /* - * If we're not the first users, there's no need to - * enable the whole controller. - */ - if (!csi2rx->count) { - ret = csi2rx_start(csi2rx); - if (ret) - goto out; - } - - csi2rx->count++; - } else { - csi2rx->count--; - - /* - * Let the last user turn off the lights. - */ - if (!csi2rx->count) - csi2rx_stop(csi2rx); + /* + * If we're not the first users, there's no need to + * enable the whole controller. + */ + if (!csi2rx->count) { + ret = csi2rx_start(csi2rx); + if (ret) + return ret; } -out: - mutex_unlock(&csi2rx->lock); - return ret; + /* Start streaming on the source */ + ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad, + BIT_U64(0)); + if (ret) { + dev_err(csi2rx->dev, + "Failed to start streams %d on subdev\n", 0); + if (!csi2rx->count) + csi2rx_stop(csi2rx); + return ret; + } + + csi2rx->count++; + return 0; +} + +static int csi2rx_disable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + if (v4l2_subdev_disable_streams(csi2rx->source_subdev, + csi2rx->source_pad, BIT_U64(0))) { + dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); + } + + csi2rx->count--; + + /* Let the last user turn off the lights. */ + if (!csi2rx->count) + csi2rx_stop(csi2rx); + + return 0; } static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, @@ -611,10 +615,8 @@ static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .enum_mbus_code = csi2rx_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2rx_set_fmt, -}; - -static const struct v4l2_subdev_video_ops csi2rx_video_ops = { - .s_stream = csi2rx_s_stream, + .enable_streams = csi2rx_enable_streams, + .disable_streams = csi2rx_disable_streams, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { @@ -623,7 +625,6 @@ static const struct v4l2_subdev_core_ops csi2rx_core_ops = { static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .core = &csi2rx_core_ops, - .video = &csi2rx_video_ops, .pad = &csi2rx_pad_ops, }; @@ -829,7 +830,6 @@ static int csi2rx_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, csi2rx); csi2rx->dev = &pdev->dev; - mutex_init(&csi2rx->lock); ret = csi2rx_get_resources(csi2rx, pdev); if (ret) From 57283236b7a283ac4a7c54b69d7ab008f1d441e8 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:13 +0530 Subject: [PATCH 1257/5214] media: ti: j721e-csi2rx: get number of contexts from device tree Different platforms that use this driver might have different number of DMA channels allocated for CSI. So only as many DMA contexts can be used as the number of DMA channels available. Get the number of channels provided via device tree and only configure that many contexts, and hence only that many pads. Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Pratyush Yadav Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index e01d95eab19c..26a8eaa98b3d 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,7 +43,7 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 -#define TI_CSI2RX_NUM_CTX 1 +#define TI_CSI2RX_MAX_CTX 32 /* * There are no hard limits on the width or height. The DMA engine can handle @@ -56,8 +56,8 @@ #define TI_CSI2RX_PAD_SINK 0 #define TI_CSI2RX_PAD_FIRST_SOURCE 1 -#define TI_CSI2RX_NUM_SOURCE_PADS 1 -#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS) +#define TI_CSI2RX_MAX_SOURCE_PADS TI_CSI2RX_MAX_CTX +#define TI_CSI2RX_MAX_PADS (1 + TI_CSI2RX_MAX_SOURCE_PADS) #define DRAIN_TIMEOUT_MS 50 #define DRAIN_BUFFER_SIZE SZ_32K @@ -116,14 +116,15 @@ struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; unsigned int enable_count; + unsigned int num_ctx; struct v4l2_device v4l2_dev; struct media_device mdev; struct media_pipeline pipe; - struct media_pad pads[TI_CSI2RX_NUM_PADS]; + struct media_pad pads[TI_CSI2RX_MAX_PADS]; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; struct v4l2_subdev subdev; - struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ struct { @@ -458,7 +459,7 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) return ret; /* Create and link video nodes for all DMA contexts */ - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + for (i = 0; i < csi->num_ctx; i++) { struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; struct video_device *vdev = &ctx->vdev; @@ -1251,10 +1252,11 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE; - i < TI_CSI2RX_NUM_PADS; i++) + i < TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx; i++) csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads), + ret = media_entity_pads_init(&sd->entity, + TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx, csi->pads); if (ret) goto unregister_media; @@ -1345,8 +1347,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) static int ti_csi2rx_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct ti_csi2rx_dev *csi; - int ret, i; + int ret = 0, i, count; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); if (!csi) @@ -1368,11 +1371,28 @@ static int ti_csi2rx_probe(struct platform_device *pdev) if (!csi->drain.vaddr) return -ENOMEM; + /* Only use as many contexts as the number of DMA channels allocated. */ + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + dev_err(csi->dev, "Failed to get DMA channel count: %d\n", count); + ret = count; + goto err_dma_chan; + } + + csi->num_ctx = count; + if (csi->num_ctx > TI_CSI2RX_MAX_CTX) { + dev_err(csi->dev, + "%u DMA channels passed. Maximum is %u.\n", + csi->num_ctx, TI_CSI2RX_MAX_CTX); + ret = -EINVAL; + goto err_dma_chan; + } + ret = ti_csi2rx_v4l2_init(csi); if (ret) - goto err_v4l2; + goto err_dma_chan; - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + for (i = 0; i < csi->num_ctx; i++) { csi->ctx[i].idx = i; csi->ctx[i].csi = csi; ret = ti_csi2rx_init_ctx(&csi->ctx[i]); @@ -1399,7 +1419,7 @@ static int ti_csi2rx_probe(struct platform_device *pdev) for (; i >= 0; i--) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); -err_v4l2: +err_dma_chan: dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); return ret; @@ -1410,7 +1430,7 @@ static void ti_csi2rx_remove(struct platform_device *pdev) struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); unsigned int i; - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) + for (i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); From 94dfbd4f29b823025db59da18bcb3e4eb8148972 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:14 +0530 Subject: [PATCH 1258/5214] media: cadence: csi2rx: Add .get_frame_desc op The cdns-csi2rx subdev passes streams through without any modification Use v4l2_subdev_get_frame_desc_passthrough() helper and add the .get_frame_desc op Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 2b25314ba6ab..bba0e4c0a34d 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -617,6 +617,7 @@ static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .set_fmt = csi2rx_set_fmt, .enable_streams = csi2rx_enable_streams, .disable_streams = csi2rx_disable_streams, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { From d71fc9d98cf73aebe7227b8d941be3dcfb17ab2d Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:15 +0530 Subject: [PATCH 1259/5214] media: ti: j721e-csi2rx: add support for processing virtual channels Use get_frame_desc() to get the frame desc from the connected source, and use the provided virtual channel and DT instead of defaults. As we don't support multiple streams yet, we will just always use stream 0. If the source doesn't support get_frame_desc(), fall back to the previous method of always capturing virtual channel 0. Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 26a8eaa98b3d..d0a681ba78eb 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -32,6 +32,7 @@ #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) #define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24) #define SHIM_DMACNTX_SIZE GENMASK(21, 20) +#define SHIM_DMACNTX_VC GENMASK(9, 6) #define SHIM_DMACNTX_FMT GENMASK(5, 0) #define SHIM_DMACNTX_YUV422_MODE_11 3 #define SHIM_DMACNTX_SIZE_8 0 @@ -110,6 +111,9 @@ struct ti_csi2rx_ctx { struct media_pad pad; u32 sequence; u32 idx; + u32 vc; + u32 dt; + u32 stream; }; struct ti_csi2rx_dev { @@ -569,7 +573,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) ti_csi2rx_request_max_ppc(csi); reg = SHIM_DMACNTX_EN; - reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); + reg |= FIELD_PREP(SHIM_DMACNTX_FMT, ctx->dt); /* * The hardware assumes incoming YUV422 8-bit data on MIPI CSI2 bus @@ -608,6 +612,8 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) break; } + reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc); + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | @@ -881,12 +887,46 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) } } +static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_mbus_frame_desc fd; + struct media_pad *pad; + int ret, i; + + pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); + if (IS_ERR(pad)) + return PTR_ERR(pad); + + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, &fd); + if (ret) + return ret; + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (i = 0; i < fd.num_entries; i++) { + if (ctx->stream == fd.entry[i].stream) { + ctx->vc = fd.entry[i].bus.csi2.vc; + ctx->dt = fd.entry[i].bus.csi2.dt; + break; + } + + /* Return error if no matching stream found */ + if (i == fd.num_entries) + return -EINVAL; + } + + return 0; +} + static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) { struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf; + const struct ti_csi2rx_fmt *fmt; unsigned long flags; int ret = 0; @@ -901,6 +941,15 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + ret = ti_csi2rx_get_vc_and_dt(ctx); + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); + ctx->dt = fmt->csi_dt; + } else if (ret < 0) { + goto err; + } + ti_csi2rx_setup_shim(ctx); ctx->sequence = 0; From c974827d4b08d6eaf16b6010bf81960ba9ec1ac7 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:16 +0530 Subject: [PATCH 1260/5214] media: cadence: csi2rx: add multistream support Cadence CSI-2 bridge IP supports capturing multiple virtual "streams" of data over the same physical interface using MIPI Virtual Channels. While the hardware IP supports usecases where streams coming in the sink pad can be broadcasted to multiple source pads, the driver will need significant re-architecture to make that possible. The two users of this IP in mainline linux are TI Shim and StarFive JH7110 CAMSS, and both have only integrated the first source pad i.e stream0 of this IP. So for now keep it simple and only allow 1-to-1 mapping of streams from sink to source, without any broadcasting. Signed-off-by: Jai Luthra Reviewed-by: Changhuang Liang Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 241 +++++++++++++++---- 1 file changed, 190 insertions(+), 51 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index bba0e4c0a34d..8931e824c597 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -135,6 +135,7 @@ struct csi2rx_priv { struct phy *dphy; u8 num_pixels[CSI2RX_STREAMS_MAX]; + u32 vc_select[CSI2RX_STREAMS_MAX]; u8 lanes[CSI2RX_LANES_MAX]; u8 num_lanes; u8 max_lanes; @@ -229,6 +230,21 @@ static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) return NULL; } +static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_source_pad_unique(&csi2rx->subdev.entity); + if (IS_ERR(remote_pad)) { + dev_err(csi2rx->dev, "No remote pad found for sink\n"); + return PTR_ERR(remote_pad); + } + + return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc, + remote_pad->index, fd); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -258,30 +274,46 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) { - struct media_pad *src_pad = - &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts = { }; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev_state *state; const struct csi2rx_fmt *fmt; + struct v4l2_subdev_route *route; + int source_pad = csi2rx->source_pad; + struct media_pad *pad = &csi2rx->source_subdev->entity.pads[source_pad]; s64 link_freq; int ret; + u32 bpp; state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev); - framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); - if (!framefmt) { - dev_err(csi2rx->dev, "Did not find active sink format\n"); - return -EINVAL; + /* + * For multi-stream transmitters there is no single pixel rate. + * + * In multistream usecase pass bpp as 0 so that v4l2_get_link_freq() + * returns an error if it falls back to V4L2_CID_PIXEL_RATE. + */ + if (state->routing.num_routes > 1) { + bpp = 0; + } else { + route = &state->routing.routes[0]; + framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, + route->sink_stream); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } + + fmt = csi2rx_get_fmt_by_code(framefmt->code); + bpp = fmt->bpp; } - fmt = csi2rx_get_fmt_by_code(framefmt->code); - - link_freq = v4l2_get_link_freq(src_pad, - fmt->bpp, 2 * csi2rx->num_lanes); - if (link_freq < 0) + link_freq = v4l2_get_link_freq(pad, bpp, 2 * csi2rx->num_lanes); + if (link_freq < 0) { + dev_err(csi2rx->dev, "Unable to calculate link frequency\n"); return link_freq; + } ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq, csi2rx->num_lanes, cfg); @@ -379,11 +411,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) csi2rx->num_pixels[i]), csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); - /* - * Enable one virtual channel. When multiple virtual channels - * are supported this will have to be changed. - */ - writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0), + writel(csi2rx->vc_select[i], csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); writel(CSI2RX_STREAM_CTRL_START, @@ -471,18 +499,57 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } +static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_frame_desc fd = {0}; + struct v4l2_subdev_route *route; + unsigned int i; + int ret; + + ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd); + if (ret || fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_dbg(csi2rx->dev, + "Failed to get source frame desc, allowing only VC=0\n"); + for (i = 0; i < CSI2RX_STREAMS_MAX; i++) + csi2rx->vc_select[i] = CSI2RX_STREAM_DATA_CFG_VC_SELECT(0); + return; + } + + /* If source provides per-stream VC info, use it to filter by VC */ + memset(csi2rx->vc_select, 0, sizeof(csi2rx->vc_select)); + + for_each_active_route(&state->routing, route) { + u32 cdns_stream = route->source_pad - CSI2RX_PAD_SOURCE_STREAM0; + + for (i = 0; i < fd.num_entries; i++) { + if (fd.entry[i].stream != route->sink_stream) + continue; + + csi2rx->vc_select[cdns_stream] |= + CSI2RX_STREAM_DATA_CFG_VC_SELECT(fd.entry[i].bus.csi2.vc); + } + } +} + static int csi2rx_enable_streams(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; int ret; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); + /* * If we're not the first users, there's no need to * enable the whole controller. */ if (!csi2rx->count) { + csi2rx_update_vc_select(csi2rx, state); ret = csi2rx_start(csi2rx); if (ret) return ret; @@ -490,10 +557,11 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, /* Start streaming on the source */ ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad, - BIT_U64(0)); + sink_streams); if (ret) { dev_err(csi2rx->dev, - "Failed to start streams %d on subdev\n", 0); + "Failed to start streams %#llx on subdev\n", + sink_streams); if (!csi2rx->count) csi2rx_stop(csi2rx); return ret; @@ -508,9 +576,14 @@ static int csi2rx_disable_streams(struct v4l2_subdev *subdev, u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; + + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); if (v4l2_subdev_disable_streams(csi2rx->source_subdev, - csi2rx->source_pad, BIT_U64(0))) { + csi2rx->source_pad, sink_streams)) { dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); } @@ -535,12 +608,53 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, return 0; } +static int _csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + ret = v4l2_subdev_routing_validate(subdev, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format); +} + +static int csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + int ret; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count) + return -EBUSY; + + ret = _csi2rx_set_routing(subdev, state, routing); + if (ret) + return ret; + + return 0; +} + static int csi2rx_set_fmt(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt; - unsigned int i; /* No transcoding, source and sink formats must match. */ if (format->pad != CSI2RX_PAD_SINK) @@ -552,14 +666,16 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_state_get_format(state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt = format->format; - /* Propagate to source formats */ - for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_state_get_format(state, i); - *fmt = format->format; - } + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; return 0; } @@ -567,21 +683,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, static int csi2rx_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state) { - struct v4l2_subdev_format format = { - .pad = CSI2RX_PAD_SINK, - .format = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .ycbcr_enc = V4L2_YCBCR_ENC_601, - .quantization = V4L2_QUANTIZATION_LIM_RANGE, - .xfer_func = V4L2_XFER_FUNC_SRGB, + struct v4l2_subdev_route routes[] = { + { + .sink_pad = CSI2RX_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2RX_PAD_SOURCE_STREAM0, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; - return csi2rx_set_fmt(subdev, state, &format); + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _csi2rx_set_routing(subdev, state, &routing); } int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, @@ -589,35 +706,55 @@ int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); const struct csi2rx_fmt *csi_fmt; + struct v4l2_subdev_route *route; struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *fmt; + int ret = 0; if (!ppc || pad < CSI2RX_PAD_SOURCE_STREAM0 || pad >= CSI2RX_PAD_MAX) return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(subdev); - fmt = v4l2_subdev_state_get_format(state, pad); - csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + /* Check all streams on requested pad */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != pad) + continue; - /* Reduce requested PPC if it is too high */ - *ppc = min(*ppc, csi_fmt->max_pixels); + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + if (!fmt) { + ret = -EPIPE; + *ppc = 1; + break; + } + csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + if (!csi_fmt) { + ret = -EINVAL; + *ppc = 1; + break; + } + + /* Reduce requested PPC if it is too high for this stream */ + *ppc = min(*ppc, csi_fmt->max_pixels); + } v4l2_subdev_unlock_state(state); csi2rx->num_pixels[pad - CSI2RX_PAD_SOURCE_STREAM0] = CSI2RX_STREAM_CFG_NUM_PIXELS(*ppc); - return 0; + return ret; } EXPORT_SYMBOL_FOR_MODULES(cdns_csi2rx_negotiate_ppc, "j721e-csi2rx"); static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { - .enum_mbus_code = csi2rx_enum_mbus_code, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = csi2rx_set_fmt, - .enable_streams = csi2rx_enable_streams, - .disable_streams = csi2rx_disable_streams, - .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, + .enum_mbus_code = csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2rx_set_fmt, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, + .set_routing = csi2rx_set_routing, + .enable_streams = csi2rx_enable_streams, + .disable_streams = csi2rx_disable_streams, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { @@ -636,6 +773,7 @@ static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { static const struct media_entity_operations csi2rx_media_ops = { .link_validate = v4l2_subdev_link_validate, .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -853,7 +991,8 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; - csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2rx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_STREAMS; csi2rx->subdev.entity.ops = &csi2rx_media_ops; ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, From 3ed9c0a1fdbac36942af32fc1b1490a8ba241e55 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:17 +0530 Subject: [PATCH 1261/5214] media: ti: j721e-csi2rx: add multistream support Each CSI2 stream can be multiplexed into 32 independent streams, each identified by its virtual channel number and data type. The incoming data from these streams can be filtered on the basis of either the virtual channel or the data type. To capture this multiplexed stream, the application needs to tell the driver how it wants to route the data. It needs to specify which context should process which stream. This is done via the new routing APIs. Add ioctls to accept routing information from the application and save that in the driver. This can be used when starting streaming on a context to determine which route and consequently which virtual channel it should process. De-assert the pixel interface reset on first start_streaming() and assert it on the last stop_streaming(). Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 274 ++++++++++++------ 1 file changed, 193 insertions(+), 81 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index d0a681ba78eb..1ec63715baf2 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -143,17 +143,6 @@ static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) return container_of(sd, struct ti_csi2rx_dev, subdev); } -static const struct v4l2_mbus_framefmt ti_csi2rx_default_fmt = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .ycbcr_enc = V4L2_YCBCR_ENC_601, - .quantization = V4L2_QUANTIZATION_LIM_RANGE, - .xfer_func = V4L2_XFER_FUNC_SRGB, -}; - static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, @@ -565,10 +554,6 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); - /* De-assert the pixel interface reset. */ - reg = SHIM_CNTL_PIX_RST; - writel(reg, csi->shim + SHIM_CNTL); - /* Negotiate pixel count from the source */ ti_csi2rx_request_max_ppc(csi); @@ -887,34 +872,82 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) } } +static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct media_pad *pad; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *r; + + /* Get the source pad connected to this ctx */ + pad = media_entity_remote_source_pad_unique(ctx->pad.entity); + if (!pad) { + dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx); + return -ENODEV; + } + + state = v4l2_subdev_get_locked_active_state(&csi->subdev); + + for_each_active_route(&state->routing, r) { + if (r->source_pad == pad->index) { + ctx->stream = r->sink_stream; + return 0; + } + } + + /* No route found for this ctx */ + return -ENODEV; +} + static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx) { struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_ctx *curr_ctx; struct v4l2_mbus_frame_desc fd; - struct media_pad *pad; - int ret, i; + struct media_pad *source_pad; + const struct ti_csi2rx_fmt *fmt; + int ret; + unsigned int i, j; - pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); - if (IS_ERR(pad)) - return PTR_ERR(pad); + /* Get the frame desc from source */ + source_pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); + if (IS_ERR(source_pad)) + return PTR_ERR(source_pad); - ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, &fd); - if (ret) + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, source_pad->index, &fd); + if (ret) { + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); + ctx->dt = fmt->csi_dt; + } return ret; + } if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) return -EINVAL; - for (i = 0; i < fd.num_entries; i++) { - if (ctx->stream == fd.entry[i].stream) { - ctx->vc = fd.entry[i].bus.csi2.vc; - ctx->dt = fd.entry[i].bus.csi2.dt; - break; - } + for (i = 0; i < csi->num_ctx; i++) { + curr_ctx = &csi->ctx[i]; - /* Return error if no matching stream found */ - if (i == fd.num_entries) - return -EINVAL; + /* Capture VC 0 by default */ + curr_ctx->vc = 0; + + ret = ti_csi2rx_get_stream(curr_ctx); + if (ret) + continue; + + for (j = 0; j < fd.num_entries; j++) { + if (curr_ctx->stream == fd.entry[j].stream) { + curr_ctx->vc = fd.entry[j].bus.csi2.vc; + curr_ctx->dt = fd.entry[j].bus.csi2.dt; + break; + } + + /* Return error if no matching stream found */ + if (j == fd.num_entries) + return -EINVAL; + } } return 0; @@ -925,8 +958,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; - struct ti_csi2rx_buffer *buf; - const struct ti_csi2rx_fmt *fmt; unsigned long flags; int ret = 0; @@ -941,35 +972,9 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; - ret = ti_csi2rx_get_vc_and_dt(ctx); - if (ret == -ENOIOCTLCMD) { - ctx->vc = 0; - fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); - ctx->dt = fmt->csi_dt; - } else if (ret < 0) { - goto err; - } - - ti_csi2rx_setup_shim(ctx); - - ctx->sequence = 0; - - spin_lock_irqsave(&dma->lock, flags); - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - ret = ti_csi2rx_start_dma(ctx, buf); - if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); - spin_unlock_irqrestore(&dma->lock, flags); - goto err_pipeline; - } - - list_move_tail(&buf->list, &dma->submitted); - dma->state = TI_CSI2RX_DMA_ACTIVE; - spin_unlock_irqrestore(&dma->lock, flags); - + /* Start stream 0, we don't allow multiple streams on the source pad */ ret = v4l2_subdev_enable_streams(&csi->subdev, - TI_CSI2RX_PAD_FIRST_SOURCE, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, BIT_U64(0)); if (ret) goto err_dma; @@ -978,7 +983,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) err_dma: ti_csi2rx_stop_dma(ctx); -err_pipeline: video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); @@ -995,11 +999,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) video_device_pipeline_stop(&ctx->vdev); - writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); - ret = v4l2_subdev_disable_streams(&csi->subdev, - TI_CSI2RX_PAD_FIRST_SOURCE, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, BIT_U64(0)); if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); @@ -1046,25 +1047,84 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd, fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt = format->format; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE, - format->stream); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; return 0; } +static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + + return ret; +} + +static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + + if (csi->enable_count > 0) + return -EBUSY; + + return _ti_csi2rx_sd_set_routing(sd, state, routing); +} + static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { - struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_route routes[] = { { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_SINK); - *fmt = ti_csi2rx_default_fmt; + struct v4l2_subdev_krouting routing = { + .num_routes = 1, + .routes = routes, + }; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE); - *fmt = ti_csi2rx_default_fmt; - - return 0; + /* Initialize routing to single route to the fist source pad */ + return _ti_csi2rx_sd_set_routing(sd, state, &routing); } static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, @@ -1072,15 +1132,56 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; + struct ti_csi2rx_dma *dma = &ctx->dma; struct media_pad *remote_pad; + struct ti_csi2rx_buffer *buf; + unsigned long flags; + u64 sink_streams; int ret = 0; + unsigned int reg; + + ret = ti_csi2rx_get_stream(ctx); + if (ret) + return ret; + + /* Get the VC and DT for all enabled ctx on first stream start */ + if (!csi->enable_count) { + ret = ti_csi2rx_get_vc_and_dt(ctx); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* De-assert the pixel interface reset. */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + } + + ti_csi2rx_setup_shim(ctx); + ctx->sequence = 0; + + spin_lock_irqsave(&dma->lock, flags); + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(csi->dev, "Failed to start DMA: %d\n", ret); + spin_unlock_irqrestore(&dma->lock, flags); + return ret; + } + + list_move_tail(&buf->list, &dma->submitted); + dma->state = TI_CSI2RX_DMA_ACTIVE; + spin_unlock_irqrestore(&dma->lock, flags); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (!remote_pad) return -ENODEV; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index, - BIT_U64(0)); + sink_streams); if (ret) return ret; @@ -1094,18 +1195,28 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; struct media_pad *remote_pad; + u64 sink_streams; int ret = 0; + WARN_ON(csi->enable_count == 0); + + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* assert pixel reset to prevent stale data */ + if (csi->enable_count == 1) + writel(0, csi->shim + SHIM_CNTL); + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (!remote_pad) return -ENODEV; - - if (csi->enable_count == 0) - return -EINVAL; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index, - BIT_U64(0)); + sink_streams); if (!ret) --csi->enable_count; @@ -1114,6 +1225,7 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { .enum_mbus_code = ti_csi2rx_enum_mbus_code, + .set_routing = ti_csi2rx_sd_set_routing, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ti_csi2rx_sd_set_fmt, .enable_streams = ti_csi2rx_sd_enable_streams, @@ -1293,7 +1405,7 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); sd->internal_ops = &ti_csi2rx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); sd->dev = csi->dev; sd->entity.ops = &ti_csi2rx_subdev_entity_ops; From 7b6887a034e4586e7cb0371e192065d5740865f1 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:18 +0530 Subject: [PATCH 1262/5214] media: ti: j721e-csi2rx: Submit all available buffers We already make sure to submit all available buffers to DMA in each DMA completion callback. Move that logic in a separate function, and use it during stream start as well, as most application queue all their buffers before stream on. Signed-off-by: Jai Luthra Reviewed-by: Tomi Valkeinen Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 1ec63715baf2..071ad969dfa6 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -663,11 +663,32 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) return ret; } +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dma *dma = &ctx->dma; + struct ti_csi2rx_buffer *buf; + int ret = 0; + + /* If there are more buffers to process then start their transfer. */ + while (!list_empty(&dma->queue)) { + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(ctx->csi->dev, + "Failed to queue the next buffer for DMA\n"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&buf->list); + } else { + list_move_tail(&buf->list, &dma->submitted); + } + } + return ret; +} + static void ti_csi2rx_dma_callback(void *param) { struct ti_csi2rx_buffer *buf = param; struct ti_csi2rx_ctx *ctx = buf->ctx; - struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; @@ -684,18 +705,7 @@ static void ti_csi2rx_dma_callback(void *param) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); list_del(&buf->list); - /* If there are more buffers to process then start their transfer. */ - while (!list_empty(&dma->queue)) { - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - if (ti_csi2rx_start_dma(ctx, buf)) { - dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - list_move_tail(&buf->list, &dma->submitted); - } - } + ti_csi2rx_dma_submit_pending(ctx); if (list_empty(&dma->submitted)) dma->state = TI_CSI2RX_DMA_IDLE; @@ -1135,7 +1145,6 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; struct ti_csi2rx_dma *dma = &ctx->dma; struct media_pad *remote_pad; - struct ti_csi2rx_buffer *buf; unsigned long flags; u64 sink_streams; int ret = 0; @@ -1160,16 +1169,13 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, ctx->sequence = 0; spin_lock_irqsave(&dma->lock, flags); - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - ret = ti_csi2rx_start_dma(ctx, buf); + ret = ti_csi2rx_dma_submit_pending(ctx); if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); spin_unlock_irqrestore(&dma->lock, flags); return ret; } - list_move_tail(&buf->list, &dma->submitted); dma->state = TI_CSI2RX_DMA_ACTIVE; spin_unlock_irqrestore(&dma->lock, flags); From 13ef2b0fa7e848c6d4658aa2847f6725a71c8712 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:19 +0530 Subject: [PATCH 1263/5214] media: ti: j721e-csi2rx: Change the drain architecture for multistream On buffer starvation the DMA is marked IDLE, and the stale data in the internal FIFOs gets drained only on the next VIDIOC_QBUF call from the userspace. This approach works fine for a single stream case. But in multistream scenarios, buffer starvation for one stream can block the shared HW FIFO of the CSI2RX IP. This can stall the pipeline for all other streams, even if buffers are available for them. This patch introduces a new architecture, that continuously drains data from the shared HW FIFO into a small (32KiB) buffer if no buffers are made available to the driver from the userspace. This ensures independence between different streams, where a slower downstream element for one camera does not block streaming for other cameras. Additionally, after we drain for a stream, the next frame will be a partial frame, as a portion of its data will have already been drained before a valid buffer is queued by user space to the driver. Return the partial frame to user space with VB2_BUF_STATE_ERROR. Use wait for completion barrier to make sure the shared hardware FIFO is cleared of the data at the end of stream after the source has stopped sending data. Reviewed-by: Jai Luthra Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 121 +++++++++--------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 071ad969dfa6..3142849f9bb9 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -82,8 +82,8 @@ struct ti_csi2rx_buffer { enum ti_csi2rx_dma_state { TI_CSI2RX_DMA_STOPPED, /* Streaming not started yet. */ - TI_CSI2RX_DMA_IDLE, /* Streaming but no pending DMA operation. */ TI_CSI2RX_DMA_ACTIVE, /* Streaming and pending DMA operation. */ + TI_CSI2RX_DMA_DRAINING, /* Dumping all the data in drain buffer */ }; struct ti_csi2rx_dma { @@ -109,6 +109,7 @@ struct ti_csi2rx_ctx { struct v4l2_format v_fmt; struct ti_csi2rx_dma dma; struct media_pad pad; + struct completion drain_complete; u32 sequence; u32 idx; u32 vc; @@ -249,6 +250,10 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf); +/* Forward declarations needed by ti_csi2rx_drain_callback. */ +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx); +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx); + static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat) { unsigned int i; @@ -608,9 +613,32 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) static void ti_csi2rx_drain_callback(void *param) { - struct completion *drain_complete = param; + struct ti_csi2rx_ctx *ctx = param; + struct ti_csi2rx_dma *dma = &ctx->dma; + unsigned long flags; - complete(drain_complete); + spin_lock_irqsave(&dma->lock, flags); + + if (dma->state == TI_CSI2RX_DMA_STOPPED) { + complete(&ctx->drain_complete); + spin_unlock_irqrestore(&dma->lock, flags); + return; + } + + /* + * If dma->queue is empty, it indicates that no buffer has been + * provided by user space. In this case, initiate a transactions + * to drain the DMA. Since one drain of size DRAIN_BUFFER_SIZE + * will be done here, the subsequent frame will be a + * partial frame, with a size of frame_size - DRAIN_BUFFER_SIZE + */ + if (list_empty(&dma->queue)) { + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, "DMA drain failed\n"); + } else { + ti_csi2rx_dma_submit_pending(ctx); + } + spin_unlock_irqrestore(&dma->lock, flags); } /* @@ -628,12 +656,9 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) { struct ti_csi2rx_dev *csi = ctx->csi; struct dma_async_tx_descriptor *desc; - struct completion drain_complete; dma_cookie_t cookie; int ret; - init_completion(&drain_complete); - desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr, csi->drain.len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -643,7 +668,7 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) } desc->callback = ti_csi2rx_drain_callback; - desc->callback_param = &drain_complete; + desc->callback_param = ctx; cookie = dmaengine_submit(desc); ret = dma_submit_error(cookie); @@ -652,13 +677,6 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) dma_async_issue_pending(ctx->dma.chan); - if (!wait_for_completion_timeout(&drain_complete, - msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { - dmaengine_terminate_sync(ctx->dma.chan); - dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n"); - ret = -ETIMEDOUT; - goto out; - } out: return ret; } @@ -702,14 +720,24 @@ static void ti_csi2rx_dma_callback(void *param) spin_lock_irqsave(&dma->lock, flags); WARN_ON(!list_is_first(&buf->list, &dma->submitted)); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + if (dma->state == TI_CSI2RX_DMA_DRAINING) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dma->state = TI_CSI2RX_DMA_ACTIVE; + } else { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + list_del(&buf->list); ti_csi2rx_dma_submit_pending(ctx); - if (list_empty(&dma->submitted)) - dma->state = TI_CSI2RX_DMA_IDLE; - + if (list_empty(&dma->submitted)) { + dma->state = TI_CSI2RX_DMA_DRAINING; + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, + "DMA drain failed on one of the transactions\n"); + } spin_unlock_irqrestore(&dma->lock, flags); } @@ -754,6 +782,8 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) dma->state = TI_CSI2RX_DMA_STOPPED; spin_unlock_irqrestore(&dma->lock, flags); + init_completion(&ctx->drain_complete); + if (state != TI_CSI2RX_DMA_STOPPED) { /* * Normal DMA termination does not clean up pending data on @@ -762,11 +792,19 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) * enforced before terminating DMA. */ ret = ti_csi2rx_drain_dma(ctx); - if (ret && ret != -ETIMEDOUT) + if (ret) dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); } + /* We wait for the drain to complete so that the stream stops + * cleanly, making sure the shared hardware FIFO is cleared of + * data from the current stream. No more data will be coming from + * the source after this. + */ + wait_for_completion_timeout(&ctx->drain_complete, + msecs_to_jiffies(DRAIN_TIMEOUT_MS)); + ret = dmaengine_terminate_sync(ctx->dma.chan); if (ret) dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret); @@ -829,57 +867,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct ti_csi2rx_buffer *buf; struct ti_csi2rx_dma *dma = &ctx->dma; - bool restart_dma = false; unsigned long flags = 0; - int ret; buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); buf->ctx = ctx; spin_lock_irqsave(&dma->lock, flags); - /* - * Usually the DMA callback takes care of queueing the pending buffers. - * But if DMA has stalled due to lack of buffers, restart it now. - */ - if (dma->state == TI_CSI2RX_DMA_IDLE) { - /* - * Do not restart DMA with the lock held because - * ti_csi2rx_drain_dma() might block for completion. - * There won't be a race on queueing DMA anyway since the - * callback is not being fired. - */ - restart_dma = true; - dma->state = TI_CSI2RX_DMA_ACTIVE; - } else { - list_add_tail(&buf->list, &dma->queue); - } + list_add_tail(&buf->list, &dma->queue); spin_unlock_irqrestore(&dma->lock, flags); - - if (restart_dma) { - /* - * Once frames start dropping, some data gets stuck in the DMA - * pipeline somewhere. So the first DMA transfer after frame - * drops gives a partial frame. This is obviously not useful to - * the application and will only confuse it. Issue a DMA - * transaction to drain that up. - */ - ret = ti_csi2rx_drain_dma(ctx); - if (ret && ret != -ETIMEDOUT) - dev_warn(ctx->csi->dev, - "Failed to drain DMA. Next frame might be bogus\n"); - - spin_lock_irqsave(&dma->lock, flags); - ret = ti_csi2rx_start_dma(ctx, buf); - if (ret) { - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dma->state = TI_CSI2RX_DMA_IDLE; - spin_unlock_irqrestore(&dma->lock, flags); - dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret); - } else { - list_add_tail(&buf->list, &dma->submitted); - spin_unlock_irqrestore(&dma->lock, flags); - } - } } static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) From c85d3d5362a8352c9e94f390ecd1b245e97458d0 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Wed, 20 May 2026 17:30:20 +0530 Subject: [PATCH 1264/5214] media: cadence: csi2rx: Support runtime PM Use runtime power management hooks to save power when CSI-RX is not in use. Also, shift to goto based error handling in csi2rx_enable_streams() function Signed-off-by: Changhuang Liang Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/Kconfig | 1 + drivers/media/platform/cadence/cdns-csi2rx.c | 135 ++++++++++++------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 1aa608c00dbc..ea85ef82760e 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -5,6 +5,7 @@ comment "Cadence media platform drivers" config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" depends on VIDEO_DEV + depends on PM select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 8931e824c597..1ff2d8f78d5b 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -340,11 +340,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) u32 reg; int ret; - ret = clk_prepare_enable(csi2rx->p_clk); - if (ret) - return ret; - - reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); if (csi2rx->error_irq >= 0) @@ -385,7 +380,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) { dev_err(csi2rx->dev, "Failed to configure external DPHY: %d\n", ret); - goto err_disable_pclk; + return ret; } } @@ -400,12 +395,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) * hence the reference counting. */ for (i = 0; i < csi2rx->max_streams; i++) { - ret = clk_prepare_enable(csi2rx->pixel_clk[i]); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->pixel_rst[i]); - writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF | FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK, csi2rx->num_pixels[i]), @@ -418,30 +407,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); } - ret = clk_prepare_enable(csi2rx->sys_clk); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->sys_rst); - - clk_disable_unprepare(csi2rx->p_clk); return 0; - -err_disable_pixclk: - for (; i > 0; i--) { - reset_control_assert(csi2rx->pixel_rst[i - 1]); - clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); - } - - if (csi2rx->dphy) { - writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); - phy_power_off(csi2rx->dphy); - } -err_disable_pclk: - clk_disable_unprepare(csi2rx->p_clk); - - return ret; } static void csi2rx_stop(struct csi2rx_priv *csi2rx) @@ -450,10 +417,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) u32 val; int ret; - clk_prepare_enable(csi2rx->p_clk); - reset_control_assert(csi2rx->sys_rst); - clk_disable_unprepare(csi2rx->sys_clk); - writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG); for (i = 0; i < csi2rx->max_streams; i++) { @@ -468,14 +431,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) if (ret) dev_warn(csi2rx->dev, "Failed to stop streaming on pad%u\n", i); - - reset_control_assert(csi2rx->pixel_rst[i]); - clk_disable_unprepare(csi2rx->pixel_clk[i]); } - reset_control_assert(csi2rx->p_rst); - clk_disable_unprepare(csi2rx->p_clk); - if (csi2rx->dphy) { writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); @@ -549,10 +506,15 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, * enable the whole controller. */ if (!csi2rx->count) { + ret = pm_runtime_resume_and_get(csi2rx->dev); + if (ret < 0) + goto err; + csi2rx_update_vc_select(csi2rx, state); + ret = csi2rx_start(csi2rx); if (ret) - return ret; + goto err_put_pm; } /* Start streaming on the source */ @@ -562,13 +524,20 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, dev_err(csi2rx->dev, "Failed to start streams %#llx on subdev\n", sink_streams); - if (!csi2rx->count) - csi2rx_stop(csi2rx); - return ret; + goto err_stop_csi; } csi2rx->count++; return 0; + +err_stop_csi: + if (!csi2rx->count) + csi2rx_stop(csi2rx); +err_put_pm: + if (!csi2rx->count) + pm_runtime_put(csi2rx->dev); +err: + return ret; } static int csi2rx_disable_streams(struct v4l2_subdev *subdev, @@ -590,8 +559,10 @@ static int csi2rx_disable_streams(struct v4l2_subdev *subdev, csi2rx->count--; /* Let the last user turn off the lights. */ - if (!csi2rx->count) + if (!csi2rx->count) { csi2rx_stop(csi2rx); + pm_runtime_put(csi2rx->dev); + } return 0; } @@ -1019,6 +990,7 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + pm_runtime_enable(csi2rx->dev); ret = v4l2_async_register_subdev(&csi2rx->subdev); if (ret < 0) goto err_free_state; @@ -1033,6 +1005,7 @@ static int csi2rx_probe(struct platform_device *pdev) err_free_state: v4l2_subdev_cleanup(&csi2rx->subdev); + pm_runtime_disable(csi2rx->dev); err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); @@ -1051,9 +1024,72 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2rx->subdev); v4l2_subdev_cleanup(&csi2rx->subdev); media_entity_cleanup(&csi2rx->subdev.entity); + pm_runtime_disable(csi2rx->dev); kfree(csi2rx); } +static int csi2rx_runtime_suspend(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + + reset_control_assert(csi2rx->sys_rst); + clk_disable_unprepare(csi2rx->sys_clk); + + for (unsigned int i = 0; i < csi2rx->max_streams; i++) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return 0; +} + +static int csi2rx_runtime_resume(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + unsigned int i; + int ret; + + ret = clk_prepare_enable(csi2rx->p_clk); + if (ret) + return ret; + + reset_control_deassert(csi2rx->p_rst); + + for (i = 0; i < csi2rx->max_streams; i++) { + ret = clk_prepare_enable(csi2rx->pixel_clk[i]); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->pixel_rst[i]); + } + + ret = clk_prepare_enable(csi2rx->sys_clk); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->sys_rst); + + return 0; + +err_disable_pixclk: + while (i--) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return ret; +} + +static const struct dev_pm_ops csi2rx_pm_ops = { + RUNTIME_PM_OPS(csi2rx_runtime_suspend, csi2rx_runtime_resume, NULL) +}; + static const struct of_device_id csi2rx_of_table[] = { { .compatible = "starfive,jh7110-csi2rx" }, { .compatible = "cdns,csi2rx" }, @@ -1068,6 +1104,7 @@ static struct platform_driver csi2rx_driver = { .driver = { .name = "cdns-csi2rx", .of_match_table = csi2rx_of_table, + .pm = &csi2rx_pm_ops, }, }; module_platform_driver(csi2rx_driver); From c0429f49f4267496a710f8db619696e87c796832 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:21 +0530 Subject: [PATCH 1265/5214] media: ti: j721e-csi2rx: Support runtime suspend Add support for runtime power-management to enable powering off the shared power domain between Cadence CSI2RX and TI CSI2RX wrapper when the device(s) are not in use. When powering off the IP, the PSI-L endpoint loses the paired DMA channels. Thus we have to release the DMA channels at runtime suspend and request them again at resume. Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Jai Luthra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/ti/Kconfig | 1 + .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 56 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig index da33facf4467..d0cb05481bd8 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -83,6 +83,7 @@ config VIDEO_TI_J721E_CSI2RX depends on VIDEO_CADENCE_CSI2RX depends on PHY_CADENCE_DPHY_RX || COMPILE_TEST depends on ARCH_K3 || COMPILE_TEST + depends on PM select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 3142849f9bb9..d68b8d6ffeb1 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -964,14 +965,18 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; - int ret = 0; + int ret; + + ret = pm_runtime_resume_and_get(csi->dev); + if (ret) + return ret; spin_lock_irqsave(&dma->lock, flags); if (list_empty(&dma->queue)) ret = -EIO; spin_unlock_irqrestore(&dma->lock, flags); if (ret) - return ret; + goto err; ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe); if (ret) @@ -993,6 +998,8 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); err: ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); + pm_runtime_put(csi->dev); + return ret; } @@ -1012,6 +1019,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) ti_csi2rx_stop_dma(ctx); ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR); + pm_runtime_put(csi->dev); } static const struct vb2_ops csi_vb2_qops = { @@ -1257,7 +1265,9 @@ static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi) static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) { - dma_release_channel(ctx->dma.chan); + if (!pm_runtime_status_suspended(ctx->csi->dev)) + dma_release_channel(ctx->dma.chan); + vb2_queue_release(&ctx->vidq); video_unregister_device(&ctx->vdev); @@ -1507,6 +1517,38 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) return ret; } +static int ti_csi2rx_runtime_suspend(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + + if (csi->enable_count != 0) + return -EBUSY; + + for (unsigned int i = 0; i < csi->num_ctx; i++) + dma_release_channel(csi->ctx[i].dma.chan); + + return 0; +} + +static int ti_csi2rx_runtime_resume(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + int ret; + + for (unsigned int i = 0; i < csi->num_ctx; i++) { + ret = ti_csi2rx_init_dma(&csi->ctx[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct dev_pm_ops ti_csi2rx_pm_ops = { + RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume, + NULL) +}; + static int ti_csi2rx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1562,6 +1604,9 @@ static int ti_csi2rx_probe(struct platform_device *pdev) goto err_ctx; } + pm_runtime_set_active(csi->dev); + pm_runtime_enable(csi->dev); + ret = ti_csi2rx_notifier_register(csi); if (ret) goto err_ctx; @@ -1592,6 +1637,9 @@ static void ti_csi2rx_remove(struct platform_device *pdev) struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); unsigned int i; + if (!pm_runtime_status_suspended(&pdev->dev)) + pm_runtime_set_suspended(&pdev->dev); + for (i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); @@ -1599,6 +1647,7 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_v4l2(csi); dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); + pm_runtime_disable(&pdev->dev); } static const struct of_device_id ti_csi2rx_of_match[] = { @@ -1613,6 +1662,7 @@ static struct platform_driver ti_csi2rx_pdrv = { .driver = { .name = TI_CSI2RX_MODULE_NAME, .of_match_table = ti_csi2rx_of_match, + .pm = &ti_csi2rx_pm_ops, }, }; From ec8d4573c3d51b248fcc4fdd333a3b48f8e35c41 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:22 +0530 Subject: [PATCH 1266/5214] media: ti: j721e-csi2rx: Support system suspend using pm_notifier As this device is the "orchestrator" for the rest of the media pipeline, we need to stop all on-going streams before system suspend and enable them back when the system wakes up from sleep. Using .suspend/.resume callbacks does not work, as the order of those callbacks amongst various devices in the camera pipeline like the sensor, FPD serdes, CSI bridge etc. is impossible to enforce, even with device links. For example, the Cadence CSI bridge is a child device of this device, thus we cannot create a device link with the CSI bridge as a provider and this device as consumer. This can lead to situations where all the dependencies for the bridge have not yet resumed when we request the subdev to start streaming again through the .resume callback defined in this device. Instead here we register a notifier callback with the PM framework which is triggered when the system is fully functional. At this point we can cleanly stop or start the streams, because we know all other devices and their dependencies are functional. A downside of this approach is that the userspace is also alive (not frozen yet, or just thawed), so the suspend notifier might complete before the userspace has completed all ioctls, like QBUF/DQBUF/STREAMON/STREAMOFF. Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index d68b8d6ffeb1..21388284cbaa 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -131,6 +131,7 @@ struct ti_csi2rx_dev { struct v4l2_subdev *source; struct v4l2_subdev subdev; struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; + struct notifier_block pm_notifier; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ struct { @@ -1544,6 +1545,124 @@ static int ti_csi2rx_runtime_resume(struct device *dev) return 0; } +static int ti_csi2rx_suspend(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + enum ti_csi2rx_dma_state state; + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + unsigned long flags = 0; + int i, ret = 0; + + /* If device was not in use we can simply suspend */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* + * If device is running, assert the pixel reset to cleanly stop any + * on-going streams before we suspend. + */ + writel(0, csi->shim + SHIM_CNTL); + + for (i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + + spin_lock_irqsave(&dma->lock, flags); + state = dma->state; + spin_unlock_irqrestore(&dma->lock, flags); + + if (state != TI_CSI2RX_DMA_STOPPED) { + /* Disable source */ + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(csi->dev, "Failed to stop subdev stream\n"); + } + + /* Stop any on-going streams */ + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* Drain DMA */ + ti_csi2rx_drain_dma(ctx); + + /* Terminate DMA */ + ret = dmaengine_terminate_sync(ctx->dma.chan); + if (ret) + dev_err(csi->dev, "Failed to stop DMA\n"); + } + + return ret; +} + +static int ti_csi2rx_resume(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + struct ti_csi2rx_buffer *buf; + unsigned long flags = 0; + unsigned int reg; + int i, ret = 0; + + /* If device was not in use, we can simply wakeup */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* If device was in use before, restore all the running streams */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + + for (i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + spin_lock_irqsave(&dma->lock, flags); + if (dma->state != TI_CSI2RX_DMA_STOPPED) { + /* Re-submit all previously submitted buffers to DMA */ + list_for_each_entry(buf, &ctx->dma.submitted, list) { + ti_csi2rx_start_dma(ctx, buf); + } + spin_unlock_irqrestore(&dma->lock, flags); + + /* Restore stream config */ + ti_csi2rx_setup_shim(ctx); + + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(ctx->csi->dev, "Failed to start subdev\n"); + } else { + spin_unlock_irqrestore(&dma->lock, flags); + } + } + + return ret; +} + +static int ti_csi2rx_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ti_csi2rx_dev *csi = + container_of(nb, struct ti_csi2rx_dev, pm_notifier); + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + ti_csi2rx_suspend(csi->dev); + break; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + ti_csi2rx_resume(csi->dev); + break; + } + + return NOTIFY_DONE; +} + static const struct dev_pm_ops ti_csi2rx_pm_ops = { RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume, NULL) @@ -1617,6 +1736,20 @@ static int ti_csi2rx_probe(struct platform_device *pdev) goto err_notifier; } + /* + * Use PM notifier instead of .suspend/.resume callbacks because the + * ordering of callbacks among camera pipeline devices (sensor, serdes, + * CSI bridge) cannot be enforced even with device links. The notifier + * is called when the system is fully functional, ensuring all + * dependencies are available when stopping/starting streams. + */ + csi->pm_notifier.notifier_call = ti_csi2rx_pm_notifier; + ret = register_pm_notifier(&csi->pm_notifier); + if (ret) { + dev_err(csi->dev, "Failed to create PM notifier: %d\n", ret); + goto err_notifier; + } + return 0; err_notifier: @@ -1644,6 +1777,8 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); + unregister_pm_notifier(&csi->pm_notifier); + ti_csi2rx_cleanup_v4l2(csi); dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); From 1d793a29efb4260f90913f5287939bf95573b073 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:30:44 +0200 Subject: [PATCH 1267/5214] media: vivid: add vivid_update_reduced_fps() Don't call vivid_update_format_cap() when switching to/from reduced fps for HDMI inputs: that will also reset the format, which is overkill for this. Make a new vivid_update_reduced_fps() function that just updates the dev->timeperframe_vid_cap. Reviewed-by: Nicolas Dufresne Fixes: c79aa6aeadb0 ("[media] vivid-capture: add control for reduced frame rate") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil --- .../media/test-drivers/vivid/vivid-ctrls.c | 3 +- .../media/test-drivers/vivid/vivid-vid-cap.c | 32 +++++++++++-------- .../media/test-drivers/vivid/vivid-vid-cap.h | 1 + 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f94c15ff84f7..1077445f5772 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -609,7 +609,8 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_REDUCED_FPS: dev->reduced_fps = ctrl->val; - vivid_update_format_cap(dev, true); + if (dev->input_type[dev->input] == HDMI) + vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: dev->has_crop_cap = ctrl->val; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b95f06a9b5ae..76e0b161c049 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -364,6 +364,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) return TPG_PIXEL_ASPECT_SQUARE; } +void vivid_update_reduced_fps(struct vivid_dev *dev) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; + unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + u64 pixelclock; + + if (dev->reduced_fps && can_reduce_fps(bt)) { + pixelclock = div_u64(bt->pixelclock * 1000, 1001); + bt->flags |= V4L2_DV_FL_REDUCED_FPS; + } else { + pixelclock = bt->pixelclock; + bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; + } + dev->timeperframe_vid_cap = (struct v4l2_fract) { + size / 100, (u32)pixelclock / 100 + }; +} + /* * Called whenever the format has to be reset which can occur when * changing inputs, standard, timings, etc. @@ -372,8 +390,6 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; - unsigned size; - u64 pixelclock; switch (dev->input_type[dev->input]) { case WEBCAM: @@ -402,17 +418,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) case HDMI: dev->src_rect.width = bt->width; dev->src_rect.height = bt->height; - size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); - if (dev->reduced_fps && can_reduce_fps(bt)) { - pixelclock = div_u64(bt->pixelclock * 1000, 1001); - bt->flags |= V4L2_DV_FL_REDUCED_FPS; - } else { - pixelclock = bt->pixelclock; - bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; - } - dev->timeperframe_vid_cap = (struct v4l2_fract) { - size / 100, (u32)pixelclock / 100 - }; + vivid_update_reduced_fps(dev); if (bt->interlaced) dev->field_cap = V4L2_FIELD_ALTERNATE; else diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 38a99f7e038e..d08a85927510 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -9,6 +9,7 @@ #define _VIVID_VID_CAP_H_ void vivid_update_quality(struct vivid_dev *dev); +void vivid_update_reduced_fps(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); void vivid_update_outputs(struct vivid_dev *dev); void vivid_update_connected_outputs(struct vivid_dev *dev); From c2d1a2130c93f6d758af58590b86b2254c7a1dec Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:22:41 +0200 Subject: [PATCH 1268/5214] media: vivid: check for vb2_is_busy() when toggling caps The vivid_update_format_cap/out() functions must only be called if the capture/output queue are not busy. But for the controls that select the CROP/COMPOSE/SCALE capability that is not checked. Only when streaming starts will they be set to 'grabbed' and it is impossible to change the control, but between REQBUFS and STREAMON you are still allowed to set these controls. Since vivid_update_format_cap/out will change the format, this can cause unexpected results. Besides adding these checks, also add a WARN_ON in vivid_update_format_cap/out() if the queue is busy. I'm 90% certain that this is the cause of this syzbot bug: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 But since we never have reproducers, it is hard to be certain. In any case, these checks are needed regardless. Reviewed-by: Nicolas Dufresne Fixes: 73c3f48230cd ("[media] vivid: add the control handling code") Cc: stable@vger.kernel.org Reported-by: syzbot+dac8f5eaa46837e97b89@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-ctrls.c | 12 ++++++++++++ drivers/media/test-drivers/vivid/vivid-vid-cap.c | 6 ++++++ drivers/media/test-drivers/vivid/vivid-vid-out.c | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 1077445f5772..a8a134b36720 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -613,14 +613,20 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_crop_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_COMPOSE_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_compose_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_SCALER_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_scaler_cap = ctrl->val; vivid_update_format_cap(dev, true); break; @@ -1117,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_crop_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_COMPOSE_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_compose_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_SCALER_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_scaler_out = ctrl->val; vivid_update_format_out(dev); break; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 76e0b161c049..e20449084709 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -391,6 +391,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q))) + return; + switch (dev->input_type[dev->input]) { case WEBCAM: default: diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 8c037b90833e..23e1d5a189ee 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev) unsigned size, p; u64 pixelclock; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q))) + return; + switch (dev->output_type[dev->output]) { case SVID: default: From c8a3be5bc2b2f2d53c56a8b9cab731e917b95c07 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 30 Apr 2026 19:30:28 +0100 Subject: [PATCH 1269/5214] clk: microchip: mpfs-ccc: fix peripheral driver registration failures after oob fix Commit 2f7ae8ab6aa73 ("clk: microchip: mpfs-ccc: fix out of bounds access during output registration") fixed the out of bounds access, but it did so by packing sparse indices into a linear space. When peripheral drivers request clocks, they obviously don't care for this compression and use the sparse indices, and therefore try to request the wrong clocks or clocks that don't exist. The most straightforward fix here seems to stop being clever with the packing and just overallocate the array. Fixes: 2f7ae8ab6aa73 ("clk: microchip: mpfs-ccc: fix out of bounds access during output registration") Fixes: d39fb172760e ("clk: microchip: add PolarFire SoC fabric clock support") Reviewed-by: Brian Masney Signed-off-by: Conor Dooley --- drivers/clk/microchip/clk-mpfs-ccc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c index 0a76a1aaa50f..40c17593e594 100644 --- a/drivers/clk/microchip/clk-mpfs-ccc.c +++ b/drivers/clk/microchip/clk-mpfs-ccc.c @@ -32,6 +32,7 @@ #define MPFS_CCC_FIXED_DIV 4 #define MPFS_CCC_OUTPUTS_PER_PLL 4 #define MPFS_CCC_REFS_PER_PLL 2 +#define MPFS_CCC_NUM_CLKS 16 struct mpfs_ccc_data { void __iomem **pll_base; @@ -178,7 +179,7 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_ return dev_err_probe(dev, ret, "failed to register clock id: %d\n", out_hw->id); - data->hw_data.hws[out_hw->id - 2] = &out_hw->divider.hw; + data->hw_data.hws[out_hw->id] = &out_hw->divider.hw; } return 0; @@ -231,17 +232,9 @@ static int mpfs_ccc_probe(struct platform_device *pdev) { struct mpfs_ccc_data *clk_data; void __iomem *pll_base[ARRAY_SIZE(mpfs_ccc_pll_clks)]; - unsigned int num_clks; int ret; - /* - * If DLLs get added here, mpfs_ccc_register_outputs() currently packs - * sparse clock IDs in the hws array - */ - num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) + - ARRAY_SIZE(mpfs_ccc_pll1out_clks); - - clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, num_clks), + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, MPFS_CCC_NUM_CLKS), GFP_KERNEL); if (!clk_data) return -ENOMEM; @@ -255,7 +248,7 @@ static int mpfs_ccc_probe(struct platform_device *pdev) return PTR_ERR(pll_base[1]); clk_data->pll_base = pll_base; - clk_data->hw_data.num = num_clks; + clk_data->hw_data.num = MPFS_CCC_NUM_CLKS; clk_data->dev = &pdev->dev; ret = mpfs_ccc_register_plls(clk_data->dev, mpfs_ccc_pll_clks, From 6a082433bcc749f3e07bd2e28a733758ee875373 Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Wed, 20 May 2026 21:40:04 +0800 Subject: [PATCH 1270/5214] perf riscv: Add SDT argument parsing for RISC-V Implement __perf_sdt_arg_parse_op_riscv() to convert RISC-V GCC-generated SDT probe operands into uprobe-compatible format, and register it in the perf_sdt_arg_parse_op() dispatcher for EM_RISCV. RISC-V GCC uses the 'nor' constraint for SDT arguments, producing operands in the following formats: Format Example Uprobe format ----------- ----------- ------------- register a0 %a0 memory (+) 8(a0) +8(%a0) memory (-) -20(s0) -20(%s0) constant 99 (skip, not supported by uprobe) Key differences from other architectures: - Register names use ABI aliases (a0-a7, t0-t6, s0-s11, sp, ra, etc.) without any '%' prefix, unlike x86 (%rax) or arm64 (x0). - Memory operands use OFFSET(REG) syntax where OFFSET may be negative, unlike arm64's [sp, NUM] or powerpc's NUM(%rREG). Two regexes are used: - SDT_OP_REGEX1: matches RISC-V ABI register names saved in pt_regs - SDT_OP_REGEX2: matches [-]NUM(REG) memory operands Reviewed-by: Guo Ren Reviewed-by: Ian Rogers Signed-off-by: Chen Pei Cc: Alexandre Ghiti Cc: Dapeng Mi Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Walmsley Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- .../util/perf-regs-arch/perf_regs_riscv.c | 128 ++++++++++++++++++ tools/perf/util/perf_regs.c | 3 + tools/perf/util/perf_regs.h | 1 + 3 files changed, 132 insertions(+) diff --git a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c index 5b5f21fcba8c..bf769304c97c 100644 --- a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c +++ b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c @@ -1,8 +1,136 @@ // SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +#include "../debug.h" #include "../perf_regs.h" #include "../../arch/riscv/include/perf_regs.h" +/* + * RISC-V SDT argument formats (GCC 'nor' constraint): + * + * Register: REG e.g. a0, t1, s0, sp + * Memory: NUM(REG) e.g. 8(a0), -20(s0) + * Constant: NUM e.g. 99 (not supported by uprobe, skip) + * + * Note: 'zero' (x0) is hardwired to 0 and not in pt_regs; skip it. + * + * Uprobe target format: + * Register: %REG e.g. %a0 + * Memory: +NUM(%REG) or -NUM(%REG) + */ + +/* RISC-V register ABI names: ra, sp, gp, tp, t0-t6, s0-s11, a0-a7 */ +#define SDT_OP_REGEX1 "^(ra|sp|gp|tp|t[0-6]|s[0-9]|s1[01]|a[0-7])$" + +/* RISC-V memory operand: [-]NUM(REG) */ +#define SDT_OP_REGEX2 "^(\\-)?([0-9]+)\\((ra|sp|gp|tp|t[0-6]|s[0-9]|s1[01]|a[0-7])\\)$" + +static regex_t sdt_op_regex1, sdt_op_regex2; + +static int sdt_init_op_regex(void) +{ + static int initialized; + int ret = 0; + + if (initialized) + return 0; + + ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED); + if (ret) + goto error; + + ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED); + if (ret) + goto free_regex1; + + initialized = 1; + return 0; + +free_regex1: + regfree(&sdt_op_regex1); +error: + pr_debug4("Regex compilation error.\n"); + return -ret; +} + +/* + * Parse OP and convert it into uprobe format. + * Possible variants of OP (RISC-V, GCC 'nor' constraint): + * + * Format Example Uprobe + * ---------------------------------------- + * REG a0 %a0 + * NUM(REG) 8(a0) +8(%a0) + * -NUM(REG) -20(s0) -20(%s0) + * NUM 99 (skip, constant not supported) + */ +int __perf_sdt_arg_parse_op_riscv(char *old_op, char **new_op) +{ + int ret, new_len; + regmatch_t rm[4]; + char prefix; + + /* + * Constant argument: pure integer with no trailing '(' (e.g. "99", "-1"). + * uprobe does not support immediate values, so skip them. + * Memory operands like "8(a0)" or "-20(s0)" contain '(' so are NOT + * treated as constants here; they will be matched by REGEX2 below. + */ + if (strchr(old_op, '(') == NULL && + ((*old_op >= '0' && *old_op <= '9') || + (*old_op == '-' && old_op[1] >= '0' && old_op[1] <= '9'))) { + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op); + return SDT_ARG_SKIP; + } + + ret = sdt_init_op_regex(); + if (ret < 0) + return ret; + + if (!regexec(&sdt_op_regex1, old_op, 2, rm, 0)) { + /* REG --> %REG */ + new_len = 2; /* % NULL */ + new_len += (int)(rm[1].rm_eo - rm[1].rm_so); + + *new_op = zalloc(new_len); + if (!*new_op) + return -ENOMEM; + + scnprintf(*new_op, new_len, "%%%.*s", + (int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so); + } else if (!regexec(&sdt_op_regex2, old_op, 4, rm, 0)) { + /* + * NUM(REG) or -NUM(REG) --> +NUM(%REG) or -NUM(%REG) + * rm[1]: optional '-' + * rm[2]: decimal offset + * rm[3]: register name + */ + prefix = (rm[1].rm_so == -1) ? '+' : '-'; + + new_len = 5; /* sign ( % ) NULL */ + new_len += (int)(rm[2].rm_eo - rm[2].rm_so); + new_len += (int)(rm[3].rm_eo - rm[3].rm_so); + + *new_op = zalloc(new_len); + if (!*new_op) + return -ENOMEM; + + scnprintf(*new_op, new_len, "%c%.*s(%%%.*s)", prefix, + (int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so, + (int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so); + } else { + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op); + return SDT_ARG_SKIP; + } + + return SDT_ARG_VALID; +} + uint64_t __perf_reg_mask_riscv(bool intr __maybe_unused) { return PERF_REGS_MASK; diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index f52b0e1f7fc7..558c143abbab 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -19,6 +19,9 @@ int perf_sdt_arg_parse_op(uint16_t e_machine, char *old_op, char **new_op) case EM_PPC64: ret = __perf_sdt_arg_parse_op_powerpc(old_op, new_op); break; + case EM_RISCV: + ret = __perf_sdt_arg_parse_op_riscv(old_op, new_op); + break; case EM_386: case EM_X86_64: ret = __perf_sdt_arg_parse_op_x86(old_op, new_op); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 573f0d1dfe04..79be2b791509 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -53,6 +53,7 @@ const char *__perf_reg_name_powerpc(int id); uint64_t __perf_reg_ip_powerpc(void); uint64_t __perf_reg_sp_powerpc(void); +int __perf_sdt_arg_parse_op_riscv(char *old_op, char **new_op); uint64_t __perf_reg_mask_riscv(bool intr); const char *__perf_reg_name_riscv(int id); uint64_t __perf_reg_ip_riscv(void); From b5050b133e7ab3f49ac391f9e812a227ae09e01e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 27 Apr 2026 23:54:32 -0700 Subject: [PATCH 1271/5214] perf clang-format: Add a perf clang-format that overrides some kernel behaviors In particular, header file ordering is an issue in the tools/perf directory given the larger number of depended upon libraries. The order of header file includes was proposed in: https://lore.kernel.org/linux-perf-users/CAP-5=fUitzKwJONTngiW17XkS7kVr2cDS4cDL_HccJKcnR2EgQ@mail.gmail.com/ Sorting headers is desirable to avoid issues like duplicate includes. Signed-off-by: Ian Rogers Cc: Andrew Morton Cc: Bill Wendling Cc: Joe Perches Cc: Justin Stitt Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/.clang-format | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tools/perf/.clang-format diff --git a/tools/perf/.clang-format b/tools/perf/.clang-format new file mode 100644 index 000000000000..902b2f7456f6 --- /dev/null +++ b/tools/perf/.clang-format @@ -0,0 +1,20 @@ +BasedOnStyle: InheritParentConfig +SortIncludes: true +IncludeBlocks: Regroup +IncludeCategories: + # Implicitly the corresponding header for the C file has Priority 0 + # C Standard Library Headers + - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|iso646|limits|locale|math|setjmp|signal|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|tgmath|threads|time|uchar|wchar|wctype)\.h>' + Priority: 1 + # OS/System-Specific Headers (directories) + - Regex: '^<(sys|linux|asm|arpa|net|netinet|x86_64|machine)/.*>' + Priority: 2 + # OS/System-Specific Headers (POSIX/System flat headers) + - Regex: '^<(unistd|pthread|fcntl|dirent|dlfcn|poll|sched|semaphore|spawn|syslog|termios|pwd|grp|netdb|sysexits|err|paths|pty|utmp|resolv|ifaddrs|elf|libelf|gelf)\.h>' + Priority: 2 + # Third-Party Library Headers + - Regex: '^<.*>' + Priority: 3 + # Your Project's Other Headers + - Regex: '^".*"' + Priority: 4 From 017bca78e4d72b1ff027d368c20a1b2c654edaf7 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Wed, 20 May 2026 00:38:55 +0200 Subject: [PATCH 1272/5214] perf build-id: Fix off-by-one bug when printing kernel/module build-id When changing sprintf functions to snprintf, one byte got lost. Since snprintf ones do not handle the '\0' terminating character, the number of printed characters is 40, while sizeof(sbuild_id) is 41, including the terminating '\0' character. This makes the later check fail so that nothing is printed. Fix that. Before: [Michael@Carbon ~]$ perf buildid-list -k [Michael@Carbon ~]$ After: [Michael@Carbon ~]$ perf buildid-list -k a527806324d543c4bc3ff2f9c9519d494fed5f68 [Michael@Carbon ~]$ Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") Signed-off-by: Michael Petlan Tested-by: Ian Rogers Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a91bbb34ac94..e0881b0ac38f 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -61,7 +61,7 @@ static int sysfs__fprintf_build_id(FILE *fp) int ret; ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); @@ -73,7 +73,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) int ret; ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); From 059e9100d82aae2254f1b06835a55755936b1417 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 15:43:24 -0700 Subject: [PATCH 1273/5214] perf event: Fix size of synthesized sample with branch stacks Synthesizing branch stacks for Intel-PT highlighted an issue where PERF_SAMPLE_BRANCH_HW_INDEX was assumed to always be set in the perf_event_attr branch_sample_type. This caused an incorrect size calculation. Fix the writing of the nr and hw_idx values during sample event synthesis by passing the branch_sample_type into the sample size and synthesis functions. Also update hardware tracers (Intel PT, ARM SPE, CS-ETM) to retrieve and pass their branch_sample_type dynamically to prevent payload misalignment. Fixes: d3f85437ad6a5511 ("perf evsel: Support PERF_SAMPLE_BRANCH_HW_INDEX") Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Kan Liang Cc: Leo Yan Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/bench/inject-buildid.c | 9 ++++++--- tools/perf/builtin-inject.c | 12 +++++++++--- tools/perf/tests/dlfilter-test.c | 8 ++++++-- tools/perf/tests/sample-parsing.c | 5 +++-- tools/perf/util/arm-spe.c | 28 ++++++++++++++++++++++++---- tools/perf/util/cs-etm.c | 28 +++++++++++++++++++++++----- tools/perf/util/intel-bts.c | 3 ++- tools/perf/util/intel-pt.c | 27 +++++++++++++++++++++++---- tools/perf/util/synthetic-events.c | 25 ++++++++++++++++++------- tools/perf/util/synthetic-events.h | 6 ++++-- 10 files changed, 118 insertions(+), 33 deletions(-) diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index aad572a78d7f..bfd2c5ec9488 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -228,9 +228,12 @@ static ssize_t synthesize_sample(struct bench_data *data, struct bench_dso *dso, event.header.type = PERF_RECORD_SAMPLE; event.header.misc = PERF_RECORD_MISC_USER; - event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, 0); - - perf_event__synthesize_sample(&event, bench_sample_type, 0, &sample); + event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0); + perf_event__synthesize_sample(&event, bench_sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0, &sample); return writen(data->input_pipe[1], &event, event.header.size); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a2493f1097df..2f20e782c7f2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -465,8 +465,13 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, /* remove sample_type {STACK,REGS}_USER for synthesize */ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); - perf_event__synthesize_sample(event_copy, sample_type, - evsel->core.attr.read_format, sample); + ret = perf_event__synthesize_sample(event_copy, sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); + if (ret) { + pr_err("Failed to synthesize sample\n"); + return ret; + } return perf_event__repipe_synth(tool, event_copy); } @@ -1102,7 +1107,8 @@ static int perf_inject__sched_stat(const struct perf_tool *tool, sample_sw.period = sample->period; sample_sw.time = sample->time; perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type, - evsel->core.attr.read_format, &sample_sw); + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, &sample_sw); build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); ret = perf_event__repipe(tool, event_sw, &sample_sw, machine); perf_sample__exit(&sample_sw); diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index e63790c61d53..204663571943 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -188,8 +188,12 @@ static int write_sample(struct test_data *td, u64 sample_type, u64 id, pid_t pid event->header.type = PERF_RECORD_SAMPLE; event->header.misc = PERF_RECORD_MISC_USER; - event->header.size = perf_event__sample_event_size(&sample, sample_type, 0); - err = perf_event__synthesize_sample(event, sample_type, 0, &sample); + event->header.size = perf_event__sample_event_size(&sample, sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0); + err = perf_event__synthesize_sample(event, sample_type, + /*read_format=*/0, + /*branch_sample_type=*/0, &sample); if (err) return test_result("perf_event__synthesize_sample() failed", TEST_FAIL); diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index a7327c942ca2..55f0b73ca20e 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -310,7 +310,8 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) sample.read.one.lost = 1; } - sz = perf_event__sample_event_size(&sample, sample_type, read_format); + sz = perf_event__sample_event_size(&sample, sample_type, read_format, + evsel.core.attr.branch_sample_type); bufsz = sz + 4096; /* Add a bit for overrun checking */ event = malloc(bufsz); if (!event) { @@ -324,7 +325,7 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) event->header.size = sz; err = perf_event__synthesize_sample(event, sample_type, read_format, - &sample); + evsel.core.attr.branch_sample_type, &sample); if (err) { pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", "perf_event__synthesize_sample", sample_type, err); diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 2b31da231ef3..31f05f467810 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -487,10 +487,30 @@ static void arm_spe__prep_branch_stack(struct arm_spe_queue *speq) bstack->hw_idx = -1ULL; } -static int arm_spe__inject_event(union perf_event *event, struct perf_sample *sample, u64 type) +static int arm_spe__inject_event(struct arm_spe *spe, union perf_event *event, + struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && spe->session && spe->session->evlist) + evsel = evlist__id2evsel(spe->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + event->header.type = PERF_RECORD_SAMPLE; + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } static inline int @@ -502,7 +522,7 @@ arm_spe_deliver_synth_event(struct arm_spe *spe, int ret; if (spe->synth_opts.inject) { - ret = arm_spe__inject_event(event, sample, spe->sample_type); + ret = arm_spe__inject_event(spe, event, sample, spe->sample_type); if (ret) return ret; } diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 8a639d2e51a4..6ec48de29441 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1422,11 +1422,29 @@ static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq, bs->nr += 1; } -static int cs_etm__inject_event(union perf_event *event, +static int cs_etm__inject_event(struct cs_etm_auxtrace *etm, union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && etm->session && etm->session->evlist) + evsel = evlist__id2evsel(etm->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } @@ -1592,7 +1610,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, sample.branch_stack = tidq->last_branch; if (etm->synth_opts.inject) { - ret = cs_etm__inject_event(event, &sample, + ret = cs_etm__inject_event(etm, event, &sample, etm->instructions_sample_type); if (ret) return ret; @@ -1667,7 +1685,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, } if (etm->synth_opts.inject) { - ret = cs_etm__inject_event(event, &sample, + ret = cs_etm__inject_event(etm, event, &sample, etm->branches_sample_type); if (ret) return ret; diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 382255393fb3..0b18ebd13f7c 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -303,7 +303,8 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, event.sample.header.size = bts->branches_event_size; ret = perf_event__synthesize_sample(&event, bts->branches_sample_type, - 0, &sample); + /*read_format=*/0, /*branch_sample_type=*/0, + &sample); if (ret) return ret; } diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index fc9eec8b54b8..dd2637678b40 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1728,11 +1728,30 @@ static void intel_pt_prep_b_sample(struct intel_pt *pt, event->sample.header.misc = sample->cpumode; } -static int intel_pt_inject_event(union perf_event *event, +static int intel_pt_inject_event(struct intel_pt *pt, union perf_event *event, struct perf_sample *sample, u64 type) { - event->header.size = perf_event__sample_event_size(sample, type, 0); - return perf_event__synthesize_sample(event, type, 0, sample); + struct evsel *evsel = sample->evsel; + u64 branch_sample_type = 0; + size_t sz; + + if (!evsel && pt->session && pt->session->evlist) + evsel = evlist__id2evsel(pt->session->evlist, sample->id); + + if (evsel) + branch_sample_type = evsel->core.attr.branch_sample_type; + + event->header.type = PERF_RECORD_SAMPLE; + sz = perf_event__sample_event_size(sample, type, /*read_format=*/0, + branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event->header.size = sz; + + return perf_event__synthesize_sample(event, type, /*read_format=*/0, + branch_sample_type, sample); } static inline int intel_pt_opt_inject(struct intel_pt *pt, @@ -1742,7 +1761,7 @@ static inline int intel_pt_opt_inject(struct intel_pt *pt, if (!pt->synth_opts.inject) return 0; - return intel_pt_inject_event(event, sample, type); + return intel_pt_inject_event(pt, event, sample, type); } static int intel_pt_deliver_synth_event(struct intel_pt *pt, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 85bee747f4cd..2461f25a4d7d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1455,7 +1455,8 @@ int perf_event__synthesize_stat_round(const struct perf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format) +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format, + u64 branch_sample_type) { size_t sz, result = sizeof(struct perf_record_sample); @@ -1515,8 +1516,10 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, if (type & PERF_SAMPLE_BRANCH_STACK) { sz = sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz += 2 * sizeof(u64); + /* nr */ + sz += sizeof(u64); + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) + sz += sizeof(u64); result += sz; } @@ -1605,7 +1608,7 @@ static __u64 *copy_read_group_values(__u64 *array, __u64 read_format, } int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, - const struct perf_sample *sample) + u64 branch_sample_type, const struct perf_sample *sample) { __u64 *array; size_t sz; @@ -1719,9 +1722,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo if (type & PERF_SAMPLE_BRANCH_STACK) { sz = sample->branch_stack->nr * sizeof(struct branch_entry); - /* nr, hw_idx */ - sz += 2 * sizeof(u64); - memcpy(array, sample->branch_stack, sz); + + *array++ = sample->branch_stack->nr; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) { + if (sample->no_hw_idx) + *array++ = 0; + else + *array++ = sample->branch_stack->hw_idx; + } + + memcpy(array, perf_sample__branch_entries((struct perf_sample *)sample), sz); array = (void *)array + sz; } diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index b0edad0c3100..8c7f49f9ccf5 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -81,7 +81,8 @@ int perf_event__synthesize_mmap_events(const struct perf_tool *tool, union perf_ int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_namespaces(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_cgroups(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, const struct perf_sample *sample); +int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, + u64 branch_sample_type, const struct perf_sample *sample); int perf_event__synthesize_stat_config(const struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs); int perf_event__synthesize_stat_round(const struct perf_tool *tool, u64 time, u64 type, perf_event__handler_t process, struct machine *machine); @@ -97,7 +98,8 @@ void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); -size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format); +size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, + u64 read_format, u64 branch_sample_type); int __machine__synthesize_threads(struct machine *machine, const struct perf_tool *tool, struct target *target, struct perf_thread_map *threads, From daac18e7c42c012e289bfd310503f9417e4a9481 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 15:43:25 -0700 Subject: [PATCH 1274/5214] perf inject: Fix itrace branch stack synthesis When using "perf inject --itrace=L" to synthesize branch stacks from AUX data, several issues caused failures with the generated file: 1. The synthesized samples were delivered without the PERF_SAMPLE_BRANCH_STACK flag if it was not in the original event's sample_type. Fixed by using sample_type | evsel->synth_sample_type in intel_pt_do_synth_pebs_sample. 2. Modifying evsel->core.attr.sample_type early in __cmd_inject caused parse failures for subsequent records in the input file. Fixed by moving this modification to just before writing the header. 3. perf_event__repipe_sample was narrowed to only synthesize samples when branch stack injection was requested, and restored the use of perf_inject__cut_auxtrace_sample as a fallback to preserve functionality. 4. Potential Heap Overflow in perf_event__repipe_sample: Addressed by adding a check that prints an error and returns -EFAULT if the calculated event size exceeds PERF_SAMPLE_MAX_SIZE. 5. Header vs Payload Mismatch in __cmd_inject: Addressed by narrowing the condition so that HEADER_BRANCH_STACK is only set in the file header if add_last_branch was true. 6. NULL Pointer Dereference in intel-pt.c: When branch stack injection is requested (add_last_branch is true) but last_branch is false (e.g., perf inject --itrace=L), ptq->last_branch was not allocated. However, PEBS branch stack synthesis (via synth_sample_type) still forced LBR handling in do_synth_pebs_sample(), dereferencing the NULL ptq->last_branch pointer. Guarding the dereference is not sufficient because downstream sample size calculation and synthesis strictly require a non-NULL branch_stack when the bit is set. Fixed by ensuring ptq->last_branch is allocated in intel_pt_alloc_queue() when add_last_branch is requested. 7. Modifying event attributes in perf_event__repipe_attr in-place caused SIGSEGV on read-only mmap buffers in file mode and downstream parser breakage in pipe mode. Fixed by processing the unmodified attribute first, returning immediately in non-pipe mode, and correctly synthesizing a new attribute event for pipe output using perf_event__synthesize_attr. Also: - Added a size validation check and integer underflow protection when parsing n_ids. - Prevented Trailing ID memory corruption by zero-initializing the local attr copy and safely copying using min_t(size_t, sizeof(attr), event->attr.attr.size). - Resolved ID array parsing mismatch downstream by expanding attr.size to sizeof(struct perf_event_attr) before synthesis to guarantee perfect header/attribute size alignment. 8. Potential dangling pointer vulnerability in perf_event__repipe_sample: Addressed by restoring the original sample->branch_stack pointer before returning, including on early error return paths. 9. Off-by-one error in sample size check in perf_event__repipe_sample: Fixed by checking if sz >= PERF_SAMPLE_MAX_SIZE instead of >. 10. Unadvertised size field left in payload by cut_auxtrace_sample: Addressed by excluding the 8-byte size field from the copied payload to correctly match the cleared PERF_SAMPLE_AUX bit. Cut the AUX sample payload even if size is 0. 11. Inaccurate sample size calculation and uninitialized memory leaks in convert_sample_callchain: Fixed by replacing manual arithmetic with perf_event__sample_event_size and adding a bounds check against PERF_SAMPLE_MAX_SIZE. 12. Omission of branch_sample_type in file headers: Addressed by expanding older, smaller attributes to PERF_ATTR_SIZE_VER2 in __cmd_inject to ensure branch_sample_type is not silently omitted. Fixes: 0f0aa5e0693ce400 ("perf inject: Add Instruction Tracing support") Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 153 ++++++++++++++++++++++++++++++++---- tools/perf/util/intel-pt.c | 8 +- 2 files changed, 142 insertions(+), 19 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 2f20e782c7f2..7a64935b7e2b 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -216,12 +216,23 @@ static int perf_event__repipe_op4_synth(const struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } +static int perf_event__repipe_synth_cb(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + return perf_event__repipe_synth(tool, event); +} + static int perf_event__repipe_attr(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct perf_event_attr attr; + size_t n_ids; + u64 *ids; int ret; ret = perf_event__process_attr(tool, event, pevlist); @@ -232,7 +243,37 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, if (!inject->output.is_pipe) return 0; - return perf_event__repipe_synth(tool, event); + if (!inject->itrace_synth_opts.set) + return perf_event__repipe_synth(tool, event); + + if (event->header.size < sizeof(struct perf_event_header) + sizeof(u64)) { + pr_err("Attribute event size %u is too small\n", event->header.size); + return -EINVAL; + } + + if (event->header.size - sizeof(event->header) < event->attr.attr.size) { + pr_err("Attribute event size %u is too small for attr.size %u\n", + event->header.size, event->attr.attr.size); + return -EINVAL; + } + + memset(&attr, 0, sizeof(attr)); + memcpy(&attr, &event->attr.attr, + min_t(size_t, sizeof(attr), (size_t)event->attr.attr.size)); + + n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size; + n_ids /= sizeof(u64); + ids = perf_record_header_attr_id(event); + + attr.size = sizeof(struct perf_event_attr); + attr.sample_type &= ~PERF_SAMPLE_AUX; + + if (inject->itrace_synth_opts.add_last_branch) { + attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; + } + return perf_event__synthesize_attr(tool, &attr, (u32)n_ids, ids, + perf_event__repipe_synth_cb); } static int perf_event__repipe_event_update(const struct perf_tool *tool, @@ -331,8 +372,8 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, union perf_event *event, struct perf_sample *sample) { - size_t sz1 = sample->aux_sample.data - (void *)event; - size_t sz2 = event->header.size - sample->aux_sample.size - sz1; + size_t sz1 = sample->aux_sample.data - (void *)event - sizeof(u64); + size_t sz2 = event->header.size - sample->aux_sample.size - (sz1 + sizeof(u64)); union perf_event *ev; if (inject->event_copy == NULL) { @@ -343,13 +384,12 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, ev = (union perf_event *)inject->event_copy; if (sz1 > event->header.size || sz2 > event->header.size || sz1 + sz2 > event->header.size || - sz1 < sizeof(struct perf_event_header) + sizeof(u64)) + sz1 < sizeof(struct perf_event_header)) return event; memcpy(ev, event, sz1); memcpy((void *)ev + sz1, (void *)event + event->header.size - sz2, sz2); ev->header.size = sz1 + sz2; - ((u64 *)((void *)ev + sz1))[-1] = 0; return ev; } @@ -369,14 +409,77 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - if (evsel && evsel->handler) { + if (evsel == NULL) + return perf_event__repipe_synth(tool, event); + + if (evsel->handler) { inject_handler f = evsel->handler; return f(tool, event, sample, evsel, machine); } build_id__mark_dso_hit(tool, event, sample, evsel, machine); - if (inject->itrace_synth_opts.set && sample->aux_sample.size) { + if (inject->itrace_synth_opts.set && + (inject->itrace_synth_opts.last_branch || + inject->itrace_synth_opts.add_last_branch)) { + union perf_event *event_copy = (void *)inject->event_copy; + struct branch_stack dummy_bs = { .nr = 0, .hw_idx = 0 }; + int err; + size_t sz; + u64 orig_type = evsel->core.attr.sample_type; + u64 orig_branch_type = evsel->core.attr.branch_sample_type; + + struct branch_stack *orig_bs = sample->branch_stack; + + if (event_copy == NULL) { + inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE); + if (!inject->event_copy) + return -ENOMEM; + + event_copy = (void *)inject->event_copy; + } + + if (!sample->branch_stack) + sample->branch_stack = &dummy_bs; + + if (inject->itrace_synth_opts.add_last_branch) { + /* Temporarily add in type bits for synthesis. */ + evsel->core.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + evsel->core.attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; + } + evsel->core.attr.sample_type &= ~PERF_SAMPLE_AUX; + + sz = perf_event__sample_event_size(sample, evsel->core.attr.sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type); + + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + evsel->core.attr.sample_type = orig_type; + evsel->core.attr.branch_sample_type = orig_branch_type; + sample->branch_stack = orig_bs; + return -EFAULT; + } + + event_copy->header.type = PERF_RECORD_SAMPLE; + event_copy->header.misc = event->header.misc; + event_copy->header.size = sz; + + err = perf_event__synthesize_sample(event_copy, evsel->core.attr.sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type, sample); + + evsel->core.attr.sample_type = orig_type; + evsel->core.attr.branch_sample_type = orig_branch_type; + sample->branch_stack = orig_bs; + + if (err) { + pr_err("Failed to synthesize sample\n"); + return err; + } + event = event_copy; + } else if (inject->itrace_synth_opts.set && + (evsel->core.attr.sample_type & PERF_SAMPLE_AUX)) { event = perf_inject__cut_auxtrace_sample(inject, event, sample); if (IS_ERR(event)) return PTR_ERR(event); @@ -397,7 +500,7 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, struct callchain_cursor_node *node; struct thread *thread; u64 sample_type = evsel->core.attr.sample_type; - u32 sample_size = event->header.size; + size_t sz; u64 i, k; int ret; @@ -456,15 +559,18 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, out: memcpy(event_copy, event, sizeof(event->header)); - /* adjust sample size for stack and regs */ - sample_size -= sample->user_stack.size; - sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64); - sample_size += (sample->callchain->nr + 1) * sizeof(u64); - event_copy->header.size = sample_size; - /* remove sample_type {STACK,REGS}_USER for synthesize */ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); + sz = perf_event__sample_event_size(sample, sample_type, + evsel->core.attr.read_format, + evsel->core.attr.branch_sample_type); + if (sz >= PERF_SAMPLE_MAX_SIZE) { + pr_err("Sample size %zu exceeds max size %d\n", sz, PERF_SAMPLE_MAX_SIZE); + return -EFAULT; + } + event_copy->header.size = sz; + ret = perf_event__synthesize_sample(event_copy, sample_type, evsel->core.attr.read_format, evsel->core.attr.branch_sample_type, sample); @@ -2442,12 +2548,27 @@ static int __cmd_inject(struct perf_inject *inject) * synthesized hardware events, so clear the feature flag. */ if (inject->itrace_synth_opts.set) { + struct evsel *evsel; + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); - if (inject->itrace_synth_opts.last_branch || - inject->itrace_synth_opts.add_last_branch) + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type &= ~PERF_SAMPLE_AUX; + } + + if (inject->itrace_synth_opts.add_last_branch) { perf_header__set_feat(&session->header, HEADER_BRANCH_STACK); + + evlist__for_each_entry(session->evlist, evsel) { + evsel->core.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; + if (evsel->core.attr.size < PERF_ATTR_SIZE_VER2) + evsel->core.attr.size = PERF_ATTR_SIZE_VER2; + evsel->core.attr.branch_sample_type |= + PERF_SAMPLE_BRANCH_HW_INDEX; + } + } } /* diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dd2637678b40..d9c86ac49748 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1307,7 +1307,8 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, goto out_free; } - if (pt->synth_opts.last_branch || pt->synth_opts.other_events) { + if (pt->synth_opts.last_branch || pt->synth_opts.add_last_branch || + pt->synth_opts.other_events) { unsigned int entry_cnt = max(LBRS_MAX, pt->br_stack_sz); ptq->last_branch = intel_pt_alloc_br_stack(entry_cnt); @@ -2505,7 +2506,7 @@ static int intel_pt_do_synth_pebs_sample(struct intel_pt_queue *ptq, struct evse intel_pt_add_xmm(intr_regs, pos, items, regs_mask); } - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + if ((sample_type | evsel->synth_sample_type) & PERF_SAMPLE_BRANCH_STACK) { if (items->mask[INTEL_PT_LBR_0_POS] || items->mask[INTEL_PT_LBR_1_POS] || items->mask[INTEL_PT_LBR_2_POS]) { @@ -2576,7 +2577,8 @@ static int intel_pt_do_synth_pebs_sample(struct intel_pt_queue *ptq, struct evse sample.transaction = txn; } - ret = intel_pt_deliver_synth_event(pt, event, &sample, sample_type); + ret = intel_pt_deliver_synth_event(pt, event, &sample, + sample_type | evsel->synth_sample_type); perf_sample__exit(&sample); return ret; } From fc444d05b4e4bfa4aaec4efa1365c0be134fc3c8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:07 -0700 Subject: [PATCH 1275/5214] perf tool: Remove evsel from tool APIs that pass the sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from tool-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze [ Fixed up conflict with "perf inject: Fix itrace branch stack synthesis" series ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 3 +-- tools/perf/builtin-c2c.c | 2 +- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-inject.c | 19 ++++++--------- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-kvm.c | 5 ++-- tools/perf/builtin-kwork.c | 2 +- tools/perf/builtin-lock.c | 2 +- tools/perf/builtin-mem.c | 1 - tools/perf/builtin-record.c | 3 +-- tools/perf/builtin-report.c | 10 +++----- tools/perf/builtin-sched.c | 4 +-- tools/perf/builtin-script.c | 4 +-- tools/perf/builtin-timechart.c | 2 +- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 3 +-- tools/perf/util/build-id.h | 7 +----- tools/perf/util/data-convert-bt.c | 2 +- tools/perf/util/data-convert-json.c | 5 ++-- tools/perf/util/intel-tpebs.c | 3 +-- tools/perf/util/jitdump.c | 2 +- tools/perf/util/session.c | 38 ++++++++++++++--------------- tools/perf/util/tool.c | 4 +-- tools/perf/util/tool.h | 4 +-- 24 files changed, 55 insertions(+), 76 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5e57b78548f4..58e56f826367 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -283,7 +283,6 @@ static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool); @@ -302,7 +301,7 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; if (!al.filtered && - evsel__add_sample(evsel, sample, &al, ann, machine)) { + evsel__add_sample(sample->evsel, sample, &al, ann, machine)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); ret = -1; diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 72a7802775ee..7593e5908fcc 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -314,9 +314,9 @@ static void perf_c2c__evsel_hists_inc_stats(struct evsel *evsel, static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; struct c2c_hists *c2c_hists = &c2c.hists; struct c2c_hist_entry *c2c_he; struct c2c_stats stats = { .nr_entries = 0, }; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1b3df868849a..82e9e514922b 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -390,11 +390,11 @@ static struct hist_entry_ops block_hist_ops = { static int diff__process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool); struct addr_location al; + struct evsel *evsel = sample->evsel; struct hists *hists = evsel__hists(evsel); struct hist_entry_iter iter = { .evsel = evsel, diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 7a64935b7e2b..e8cf8d399528 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -403,11 +403,10 @@ typedef int (*inject_handler)(const struct perf_tool *tool, static int perf_event__repipe_sample(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { - struct perf_inject *inject = container_of(tool, struct perf_inject, - tool); + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct evsel *evsel = sample->evsel; if (evsel == NULL) return perf_event__repipe_synth(tool, event); @@ -417,7 +416,7 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, return f(tool, event, sample, evsel, machine); } - build_id__mark_dso_hit(tool, event, sample, evsel, machine); + build_id__mark_dso_hit(tool, event, sample, machine); if (inject->itrace_synth_opts.set && (inject->itrace_synth_opts.last_branch || @@ -491,10 +490,10 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, static int perf_event__convert_sample_callchain(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct evsel *evsel = sample->evsel; struct callchain_cursor *cursor = get_tls_callchain_cursor(); union perf_event *event_copy = (void *)inject->event_copy; struct callchain_cursor_node *node; @@ -1100,10 +1099,8 @@ static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) args->mmap_evsel, map, /*sample_in_dso=*/false); } -int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, - struct perf_sample *sample, - struct evsel *evsel __maybe_unused, - struct machine *machine) +static int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, + struct perf_sample *sample, struct machine *machine) { struct addr_location al; struct thread *thread; @@ -1133,7 +1130,7 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e /*sample_in_dso=*/true); } - sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, + sample__for_each_callchain_node(thread, sample->evsel, sample, PERF_MAX_STACK_DEPTH, /*symbols=*/false, mark_dso_hit_callback, &args); thread__put(thread); repipe: @@ -1215,7 +1212,7 @@ static int perf_inject__sched_stat(const struct perf_tool *tool, perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type, evsel->core.attr.read_format, evsel->core.attr.branch_sample_type, &sample_sw); - build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); + build_id__mark_dso_hit(tool, event_sw, &sample_sw, machine); ret = perf_event__repipe(tool, event_sw, &sample_sw, machine); perf_sample__exit(&sample_sw); return ret; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 9c64a0d74823..45b383fa9ce9 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -960,9 +960,9 @@ typedef int (*tracepoint_handler)(struct evsel *evsel, static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; int err = 0; struct thread *thread = machine__findnew_thread(machine, sample->pid, sample->tid); diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 0c5e6b3aac74..5e8e6fde097a 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -941,9 +941,9 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, static bool handle_kvm_event(struct perf_kvm_stat *kvm, struct thread *thread, - struct evsel *evsel, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; struct vcpu_event_record *vcpu_record; struct event_key key = { .key = INVALID_KEY, .exit_reasons = kvm->exit_reasons }; @@ -1133,7 +1133,6 @@ static bool skip_sample(struct perf_kvm_stat *kvm, static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { int err = 0; @@ -1156,7 +1155,7 @@ static int process_sample_event(const struct perf_tool *tool, return -1; } - if (!handle_kvm_event(kvm, thread, evsel, sample)) + if (!handle_kvm_event(kvm, thread, sample)) err = -1; thread__put(thread); diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 9d3a4c779a41..d4bb19ed91b5 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -1955,9 +1955,9 @@ typedef int (*tracepoint_handler)(const struct perf_tool *tool, static int perf_kwork__process_tracepoint_sample(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; int err = 0; if (evsel->handler != NULL) { diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 5585aeb97684..4d8ddf6391e8 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -1430,9 +1430,9 @@ typedef int (*tracepoint_handler)(struct evsel *evsel, static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; int err = 0; struct thread *thread = machine__findnew_thread(machine, sample->pid, sample->tid); diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index d43500b92a7b..6101a26b3a78 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -255,7 +255,6 @@ dump_raw_samples(const struct perf_tool *tool, static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel __maybe_unused, struct machine *machine) { return dump_raw_samples(tool, event, sample, machine); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 272bba7f4b9e..cc601796b2c8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1490,7 +1490,6 @@ static void set_timestamp_boundary(struct record *rec, u64 sample_time) static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct record *rec = container_of(tool, struct record, tool); @@ -1501,7 +1500,7 @@ static int process_sample_event(const struct perf_tool *tool, return 0; rec->samples++; - return build_id__mark_dso_hit(tool, event, sample, evsel, machine); + return build_id__mark_dso_hit(tool, event, sample, machine); } static int process_buildids(struct record *rec) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 0b0966d94128..9955ce8cce00 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -265,10 +265,10 @@ static int process_feature_event(const struct perf_tool *tool, static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct report *rep = container_of(tool, struct report, tool); + struct evsel *evsel = sample->evsel; struct addr_location al; struct hist_entry_iter iter = { .evsel = evsel, @@ -345,7 +345,6 @@ static int process_sample_event(const struct perf_tool *tool, static int process_read_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, - struct evsel *evsel, struct machine *machine __maybe_unused) { struct report *rep = container_of(tool, struct report, tool); @@ -353,7 +352,7 @@ static int process_read_event(const struct perf_tool *tool, if (rep->show_threads) { int err = perf_read_values_add_value(&rep->show_threads_values, event->read.pid, event->read.tid, - evsel, + sample->evsel, event->read.value); if (err) @@ -779,11 +778,10 @@ static void report__output_resort(struct report *rep) static int count_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, - struct perf_sample *sample __maybe_unused, - struct evsel *evsel, + struct perf_sample *sample, struct machine *machine __maybe_unused) { - struct hists *hists = evsel__hists(evsel); + struct hists *hists = evsel__hists(sample->evsel); hists__inc_nr_events(hists); return 0; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 53a93aa18853..2950c5c3026e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1867,9 +1867,9 @@ typedef int (*tracepoint_handler)(const struct perf_tool *tool, static int perf_sched__process_tracepoint_sample(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; int err = 0; if (evsel->handler != NULL) { @@ -3184,10 +3184,10 @@ typedef int (*sched_handler)(const struct perf_tool *tool, static int perf_timehist__process_sample(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); + struct evsel *evsel = sample->evsel; int err = 0; struct perf_cpu this_cpu = { .cpu = sample->cpu, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index fd0b4609516b..d4e8f49a8751 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2646,10 +2646,10 @@ static bool filter_cpu(struct perf_sample *sample) static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_script *scr = container_of(tool, struct perf_script, tool); + struct evsel *evsel = sample->evsel; struct addr_location al; struct addr_location addr_al; int ret = 0; @@ -2730,10 +2730,10 @@ static int process_sample_event(const struct perf_tool *tool, static int process_deferred_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_script *scr = container_of(tool, struct perf_script, tool); + struct evsel *evsel = sample->evsel; struct perf_event_attr *attr = &evsel->core.attr; struct evsel_script *es = evsel->priv; unsigned int type = output_type(attr->type); diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 28f33e39895d..8692d11ccd29 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -574,10 +574,10 @@ typedef int (*tracepoint_handler)(struct timechart *tchart, static int process_sample_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct timechart *tchart = container_of(tool, struct timechart, tool); + struct evsel *evsel = sample->evsel; if (evsel->core.attr.sample_type & PERF_SAMPLE_TIME) { if (!tchart->first_time || tchart->first_time > sample->time) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e9cc870a5acd..1aafa77f72cf 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3501,10 +3501,10 @@ static void trace__set_base_time(struct trace *trace, static int trace__process_sample(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine __maybe_unused) { struct trace *trace = container_of(tool, struct trace, tool); + struct evsel *evsel = sample->evsel; struct thread *thread; int err = 0; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index fdb35133fde4..55b72235f891 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -55,7 +55,6 @@ static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct addr_location al; @@ -74,7 +73,7 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, addr_location__exit(&al); - sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, + sample__for_each_callchain_node(thread, sample->evsel, sample, PERF_MAX_STACK_DEPTH, /*symbols=*/false, mark_dso_hit_callback, /*data=*/NULL); diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 47e621cebe1b..73bad90b06f9 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -34,12 +34,7 @@ char *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, bool is_debug, bool is_kallsyms); int build_id__mark_dso_hit(const struct perf_tool *tool, union perf_event *event, - struct perf_sample *sample, struct evsel *evsel, - struct machine *machine); - -int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, - struct perf_sample *sample, struct evsel *evsel, - struct machine *machine); + struct perf_sample *sample, struct machine *machine); bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); int perf_session__write_buildid_table(struct perf_session *session, diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 3b8f2df823a9..b3f745cff2a7 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -803,10 +803,10 @@ static bool is_flush_needed(struct ctf_stream *cs) static int process_sample_event(const struct perf_tool *tool, union perf_event *_event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine __maybe_unused) { struct convert *c = container_of(tool, struct convert, tool); + struct evsel *evsel = sample->evsel; struct evsel_priv *priv = evsel->priv; struct ctf_writer *cw = &c->writer; struct ctf_stream *cs; diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index d526c91312ed..a7da93a7ff0e 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -159,13 +159,12 @@ static void output_sample_callchain_entry(const struct perf_tool *tool, static int process_sample_event(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel __maybe_unused, struct machine *machine) { struct convert_json *c = container_of(tool, struct convert_json, tool); FILE *out = c->out; struct addr_location al; - u64 sample_type = __evlist__combined_sample_type(evsel->evlist); + u64 sample_type = __evlist__combined_sample_type(sample->evsel->evlist); u8 cpumode = PERF_RECORD_MISC_USER; addr_location__init(&al); @@ -245,7 +244,7 @@ static int process_sample_event(const struct perf_tool *tool, #ifdef HAVE_LIBTRACEEVENT if (sample->raw_data) { - struct tep_event *tp_format = evsel__tp_format(evsel); + struct tep_event *tp_format = evsel__tp_format(sample->evsel); struct tep_format_field **fields = tp_format ? tep_event_fields(tp_format) : NULL; if (fields) { diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index 8b615dc94e9e..ed8cfe2ba2fa 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -185,7 +185,6 @@ static bool should_ignore_sample(const struct perf_sample *sample, const struct static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine __maybe_unused) { struct tpebs_retire_lat *t; @@ -196,7 +195,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, mutex_unlock(tpebs_mtx_get()); return 0; } - t = tpebs_retire_lat__find(evsel); + t = tpebs_retire_lat__find(sample->evsel); if (!t) { mutex_unlock(tpebs_mtx_get()); return -EINVAL; diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index e0ce8b904729..52e6ffac2b3e 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -642,7 +642,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) ret = jit_inject_event(jd, event); if (!ret) - build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); + build_id__mark_dso_hit(tool, event, &sample, jd->machine); out: perf_sample__exit(&sample); return ret; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fe0de2a0277f..1e25892963b7 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1117,9 +1117,10 @@ char *get_page_size_name(u64 size, char *str) return str; } -static void dump_sample(struct machine *machine, struct evsel *evsel, union perf_event *event, +static void dump_sample(struct machine *machine, union perf_event *event, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; u64 sample_type; char str[PAGE_SIZE_NAME_LEN]; uint16_t e_machine = EM_NONE; @@ -1183,9 +1184,10 @@ static void dump_sample(struct machine *machine, struct evsel *evsel, union perf sample_read__printf(sample, evsel->core.attr.read_format); } -static void dump_deferred_callchain(struct evsel *evsel, union perf_event *event, - struct perf_sample *sample) +static void dump_deferred_callchain(union perf_event *event, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; + if (!dump_trace) return; @@ -1291,7 +1293,7 @@ static int deliver_sample_value(struct evlist *evlist, return 0; sample->evsel = container_of(sid->evsel, struct evsel, core); - ret = tool->sample(tool, event, sample, sample->evsel, machine); + ret = tool->sample(tool, event, sample, machine); sample->evsel = saved_evsel; return ret; } @@ -1323,8 +1325,9 @@ static int deliver_sample_group(struct evlist *evlist, static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) + struct machine *machine) { + struct evsel *evsel = sample->evsel; /* We know evsel != NULL. */ u64 sample_type = evsel->core.attr.sample_type; u64 read_format = evsel->core.attr.read_format; @@ -1332,7 +1335,7 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool /* Standard sample delivery. */ if (!(sample_type & PERF_SAMPLE_READ)) - return tool->sample(tool, event, sample, evsel, machine); + return tool->sample(tool, event, sample, machine); /* For PERF_SAMPLE_READ we have either single or group mode. */ if (read_format & PERF_FORMAT_GROUP) @@ -1371,8 +1374,7 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist, struct evsel *saved_evsel = sample->evsel; sample->evsel = evlist__id2evsel(evlist, sample->id); - ret = tool->callchain_deferred(tool, event, sample, - sample->evsel, machine); + ret = tool->callchain_deferred(tool, event, sample, machine); sample->evsel = saved_evsel; return ret; } @@ -1400,7 +1402,7 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist, orig_sample.evsel = evlist__id2evsel(evlist, orig_sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, - &orig_sample, orig_sample.evsel, machine); + &orig_sample, machine); perf_sample__exit(&orig_sample); list_del(&de->list); @@ -1438,7 +1440,7 @@ static int session__flush_deferred_samples(struct perf_session *session, sample.evsel = evlist__id2evsel(evlist, sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, - &sample, sample.evsel, machine); + &sample, machine); perf_sample__exit(&sample); list_del(&de->list); @@ -1458,7 +1460,6 @@ static int machines__deliver_event(struct machines *machines, const struct perf_tool *tool, u64 file_offset, const char *file_path) { - struct evsel *evsel; struct machine *machine; dump_event(evlist, event, file_offset, sample, file_path); @@ -1468,21 +1469,20 @@ static int machines__deliver_event(struct machines *machines, else assert(sample->evsel == evlist__id2evsel(evlist, sample->id)); - evsel = sample->evsel; machine = machines__find_for_cpumode(machines, event, sample); switch (event->header.type) { case PERF_RECORD_SAMPLE: - if (evsel == NULL) { + if (sample->evsel == NULL) { ++evlist->stats.nr_unknown_id; return 0; } if (machine == NULL) { ++evlist->stats.nr_unprocessable_samples; - dump_sample(machine, evsel, event, sample); + dump_sample(machine, event, sample); return 0; } - dump_sample(machine, evsel, event, sample); + dump_sample(machine, event, sample); if (sample->deferred_callchain && tool->merge_deferred_callchains) { struct deferred_event *de = malloc(sizeof(*de)); size_t sz = event->header.size; @@ -1499,7 +1499,7 @@ static int machines__deliver_event(struct machines *machines, list_add_tail(&de->list, &evlist->deferred_samples); return 0; } - return evlist__deliver_sample(evlist, tool, event, sample, evsel, machine); + return evlist__deliver_sample(evlist, tool, event, sample, machine); case PERF_RECORD_MMAP: return tool->mmap(tool, event, sample, machine); case PERF_RECORD_MMAP2: @@ -1527,8 +1527,8 @@ static int machines__deliver_event(struct machines *machines, evlist->stats.total_lost_samples += event->lost_samples.lost; return tool->lost_samples(tool, event, sample, machine); case PERF_RECORD_READ: - dump_read(evsel, event); - return tool->read(tool, event, sample, evsel, machine); + dump_read(sample->evsel, event); + return tool->read(tool, event, sample, machine); case PERF_RECORD_THROTTLE: return tool->throttle(tool, event, sample, machine); case PERF_RECORD_UNTHROTTLE: @@ -1557,7 +1557,7 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_AUX_OUTPUT_HW_ID: return tool->aux_output_hw_id(tool, event, sample, machine); case PERF_RECORD_CALLCHAIN_DEFERRED: - dump_deferred_callchain(evsel, event, sample); + dump_deferred_callchain(event, sample); return evlist__deliver_deferred_callchain(evlist, tool, event, sample, machine); default: diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index ff2150517b75..225a77d530ce 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -110,7 +110,6 @@ static int process_event_synth_event_update_stub(const struct perf_tool *tool __ int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, struct machine *machine __maybe_unused) { dump_printf(": unhandled!\n"); @@ -349,12 +348,11 @@ bool perf_tool__compressed_is_stub(const struct perf_tool *tool) static int delegate_ ## name(const struct perf_tool *tool, \ union perf_event *event, \ struct perf_sample *sample, \ - struct evsel *evsel, \ struct machine *machine) \ { \ struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool); \ struct perf_tool *delegate = del_tool->delegate; \ - return delegate->name(delegate, event, sample, evsel, machine); \ + return delegate->name(delegate, event, sample, machine); \ } CREATE_DELEGATE_SAMPLE(read); CREATE_DELEGATE_SAMPLE(sample); diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 2d9a4b1ca9d0..2a4f124ffd8d 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -9,7 +9,6 @@ struct perf_session; union perf_event; struct evlist; -struct evsel; struct perf_sample; struct perf_tool; struct machine; @@ -17,7 +16,7 @@ struct ordered_events; typedef int (*event_sample)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine); + struct machine *machine); typedef int (*event_op)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); @@ -103,7 +102,6 @@ bool perf_tool__compressed_is_stub(const struct perf_tool *tool); int process_event_sample_stub(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine); struct delegate_tool { From 1c8b07788618a30bd0bb6d83aeb39d07b8d57bbf Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:08 -0700 Subject: [PATCH 1276/5214] perf kvm: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from kvm-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kvm.c | 19 +++-- .../perf/util/kvm-stat-arch/kvm-stat-arm64.c | 17 +++-- .../util/kvm-stat-arch/kvm-stat-loongarch.c | 17 ++--- .../util/kvm-stat-arch/kvm-stat-powerpc.c | 17 ++--- .../perf/util/kvm-stat-arch/kvm-stat-riscv.c | 18 +++-- tools/perf/util/kvm-stat-arch/kvm-stat-s390.c | 20 +++--- tools/perf/util/kvm-stat-arch/kvm-stat-x86.c | 70 ++++++++----------- tools/perf/util/kvm-stat.c | 19 +++-- tools/perf/util/kvm-stat.h | 18 ++--- 9 files changed, 89 insertions(+), 126 deletions(-) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 5e8e6fde097a..d9b9792894a8 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -806,7 +806,6 @@ static bool update_kvm_event(struct perf_kvm_stat *kvm, } static bool is_child_event(struct perf_kvm_stat *kvm, - struct evsel *evsel, struct perf_sample *sample, struct event_key *key) { @@ -818,8 +817,8 @@ static bool is_child_event(struct perf_kvm_stat *kvm, return false; for (; child_ops->name; child_ops++) { - if (evsel__name_is(evsel, child_ops->name)) { - child_ops->get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, child_ops->name)) { + child_ops->get_key(sample, key); return true; } } @@ -917,11 +916,10 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, static struct vcpu_event_record *per_vcpu_record(struct thread *thread, - struct evsel *evsel, struct perf_sample *sample) { /* Only kvm_entry records vcpu id. */ - if (!thread__priv(thread) && kvm_entry_event(evsel)) { + if (!thread__priv(thread) && kvm_entry_event(sample->evsel)) { struct vcpu_event_record *vcpu_record; struct machine *machine = maps__machine(thread__maps(thread)); uint16_t e_machine = thread__e_machine(thread, machine, /*e_flags=*/NULL); @@ -932,7 +930,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, return NULL; } - vcpu_record->vcpu_id = evsel__intval(evsel, sample, vcpu_id_str(e_machine)); + vcpu_record->vcpu_id = evsel__intval(sample->evsel, sample, vcpu_id_str(e_machine)); thread__set_priv(thread, vcpu_record); } @@ -943,12 +941,11 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, struct thread *thread, struct perf_sample *sample) { - struct evsel *evsel = sample->evsel; struct vcpu_event_record *vcpu_record; struct event_key key = { .key = INVALID_KEY, .exit_reasons = kvm->exit_reasons }; - vcpu_record = per_vcpu_record(thread, evsel, sample); + vcpu_record = per_vcpu_record(thread, sample); if (!vcpu_record) return true; @@ -957,13 +954,13 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, (kvm->trace_vcpu != vcpu_record->vcpu_id)) return true; - if (kvm->events_ops->is_begin_event(evsel, sample, &key)) + if (kvm->events_ops->is_begin_event(sample, &key)) return handle_begin_event(kvm, vcpu_record, &key, sample); - if (is_child_event(kvm, evsel, sample, &key)) + if (is_child_event(kvm, sample, &key)) return handle_child_event(kvm, vcpu_record, &key, sample); - if (kvm->events_ops->is_end_event(evsel, sample, &key)) + if (kvm->events_ops->is_end_event(sample, &key)) return handle_end_event(kvm, vcpu_record, &key, sample); return true; diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c b/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c index c640dcd8af7c..1e76906f719c 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c @@ -17,10 +17,11 @@ static const char * const __kvm_events_tp[] = { NULL, }; -static void event_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_get_key(struct perf_sample *sample, struct event_key *key) { + struct evsel *evsel = sample->evsel; + key->info = 0; key->key = evsel__intval(evsel, sample, kvm_exit_reason(EM_AARCH64)); key->exit_reasons = arm64_exit_reasons; @@ -36,19 +37,17 @@ static void event_get_key(struct evsel *evsel, } } -static bool event_begin(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, +static bool event_begin(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return evsel__name_is(evsel, kvm_entry_trace(EM_AARCH64)); + return evsel__name_is(sample->evsel, kvm_entry_trace(EM_AARCH64)); } -static bool event_end(struct evsel *evsel, - struct perf_sample *sample, +static bool event_end(struct perf_sample *sample, struct event_key *key) { - if (evsel__name_is(evsel, kvm_exit_trace(EM_AARCH64))) { - event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, kvm_exit_trace(EM_AARCH64))) { + event_get_key(sample, key); return true; } return false; diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c b/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c index b802e516b138..9d6265290f6d 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c @@ -53,14 +53,12 @@ static const char * const __kvm_events_tp[] = { NULL, }; -static bool event_begin(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key) +static bool event_begin(struct perf_sample *sample, struct event_key *key) { - return exit_event_begin(evsel, sample, key); + return exit_event_begin(sample, key); } -static bool event_end(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, +static bool event_end(struct perf_sample *sample, struct event_key *key __maybe_unused) { /* @@ -71,17 +69,16 @@ static bool event_end(struct evsel *evsel, * kvm:kvm_enter means returning to vmm and then to guest * kvm:kvm_reenter means returning to guest immediately */ - return evsel__name_is(evsel, kvm_entry_trace(EM_LOONGARCH)) || - evsel__name_is(evsel, kvm_reenter_trace); + return evsel__name_is(sample->evsel, kvm_entry_trace(EM_LOONGARCH)) || + evsel__name_is(sample->evsel, kvm_reenter_trace); } -static void event_gspr_get_key(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key) +static void event_gspr_get_key(struct perf_sample *sample, struct event_key *key) { unsigned int insn; key->key = LOONGARCH_EXCEPTION_OTHERS; - insn = evsel__intval(evsel, sample, "inst_word"); + insn = evsel__intval(sample->evsel, sample, "inst_word"); switch (insn >> 24) { case 0: diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c index 42182d70beb6..5158d7e88ee6 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c @@ -28,12 +28,11 @@ static const char * const ppc_book3s_hv_kvm_tp[] = { /* 1 extra placeholder for NULL */ static const char *__kvm_events_tp[NR_TPS + 1]; -static void hcall_event_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void hcall_event_get_key(struct perf_sample *sample, struct event_key *key) { key->info = 0; - key->key = evsel__intval(evsel, sample, "req"); + key->key = evsel__intval(sample->evsel, sample, "req"); } static const char *get_hcall_exit_reason(u64 exit_code) @@ -51,18 +50,16 @@ static const char *get_hcall_exit_reason(u64 exit_code) return "UNKNOWN"; } -static bool hcall_event_end(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, +static bool hcall_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return evsel__name_is(evsel, __kvm_events_tp[3]); + return evsel__name_is(sample->evsel, __kvm_events_tp[3]); } -static bool hcall_event_begin(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key) +static bool hcall_event_begin(struct perf_sample *sample, struct event_key *key) { - if (evsel__name_is(evsel, __kvm_events_tp[2])) { - hcall_event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, __kvm_events_tp[2])) { + hcall_event_get_key(sample, key); return true; } diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c b/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c index 8d4d5d6ce720..e8db8b4f8e2e 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c @@ -20,30 +20,28 @@ static const char * const __kvm_events_tp[] = { NULL, }; -static void event_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_get_key(struct perf_sample *sample, struct event_key *key) { int xlen = 64; // TODO: 32-bit support. key->info = 0; - key->key = evsel__intval(evsel, sample, kvm_exit_reason(EM_RISCV)) & ~CAUSE_IRQ_FLAG(xlen); + key->key = evsel__intval(sample->evsel, sample, + kvm_exit_reason(EM_RISCV)) & ~CAUSE_IRQ_FLAG(xlen); key->exit_reasons = riscv_exit_reasons; } -static bool event_begin(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, +static bool event_begin(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return evsel__name_is(evsel, kvm_entry_trace(EM_RISCV)); + return evsel__name_is(sample->evsel, kvm_entry_trace(EM_RISCV)); } -static bool event_end(struct evsel *evsel, - struct perf_sample *sample, +static bool event_end(struct perf_sample *sample, struct event_key *key) { - if (evsel__name_is(evsel, kvm_exit_trace(EM_RISCV))) { - event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, kvm_exit_trace(EM_RISCV))) { + event_get_key(sample, key); return true; } return false; diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c b/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c index 7e29169f5bb0..158372ba0205 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c @@ -18,38 +18,34 @@ define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes); define_exit_reasons_table(sie_diagnose_codes, diagnose_codes); define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes); -static void event_icpt_insn_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_icpt_insn_get_key(struct perf_sample *sample, struct event_key *key) { u64 insn; - insn = evsel__intval(evsel, sample, "instruction"); + insn = evsel__intval(sample->evsel, sample, "instruction"); key->key = icpt_insn_decoder(insn); key->exit_reasons = sie_icpt_insn_codes; } -static void event_sigp_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_sigp_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "order_code"); + key->key = evsel__intval(sample->evsel, sample, "order_code"); key->exit_reasons = sie_sigp_order_codes; } -static void event_diag_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_diag_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "code"); + key->key = evsel__intval(sample->evsel, sample, "code"); key->exit_reasons = sie_diagnose_codes; } -static void event_icpt_prog_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void event_icpt_prog_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "code"); + key->key = evsel__intval(sample->evsel, sample, "code"); key->exit_reasons = sie_icpt_prog_codes; } diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c index 0f626db3a439..0ce543d82850 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c @@ -24,45 +24,43 @@ static const struct kvm_events_ops exit_events = { * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). */ -static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample, +static void mmio_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "gpa"); - key->info = evsel__intval(evsel, sample, "type"); + key->key = evsel__intval(sample->evsel, sample, "gpa"); + key->info = evsel__intval(sample->evsel, sample, "type"); } #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 #define KVM_TRACE_MMIO_READ 1 #define KVM_TRACE_MMIO_WRITE 2 -static bool mmio_event_begin(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key) +static bool mmio_event_begin(struct perf_sample *sample, struct event_key *key) { /* MMIO read begin event in kernel. */ - if (kvm_exit_event(evsel)) + if (kvm_exit_event(sample->evsel)) return true; /* MMIO write begin event in kernel. */ - if (evsel__name_is(evsel, "kvm:kvm_mmio") && - evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { - mmio_event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") && + evsel__intval(sample->evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { + mmio_event_get_key(sample, key); return true; } return false; } -static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample, - struct event_key *key) +static bool mmio_event_end(struct perf_sample *sample, struct event_key *key) { /* MMIO write end event in kernel. */ - if (kvm_entry_event(evsel)) + if (kvm_entry_event(sample->evsel)) return true; /* MMIO read end event in kernel.*/ - if (evsel__name_is(evsel, "kvm:kvm_mmio") && - evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { - mmio_event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") && + evsel__intval(sample->evsel, sample, "type") == KVM_TRACE_MMIO_READ) { + mmio_event_get_key(sample, key); return true; } @@ -86,31 +84,27 @@ static const struct kvm_events_ops mmio_events = { }; /* The time of emulation pio access is from kvm_pio to kvm_entry. */ -static void ioport_event_get_key(struct evsel *evsel, - struct perf_sample *sample, +static void ioport_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "port"); - key->info = evsel__intval(evsel, sample, "rw"); + key->key = evsel__intval(sample->evsel, sample, "port"); + key->info = evsel__intval(sample->evsel, sample, "rw"); } -static bool ioport_event_begin(struct evsel *evsel, - struct perf_sample *sample, +static bool ioport_event_begin(struct perf_sample *sample, struct event_key *key) { - if (evsel__name_is(evsel, "kvm:kvm_pio")) { - ioport_event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, "kvm:kvm_pio")) { + ioport_event_get_key(sample, key); return true; } return false; } -static bool ioport_event_end(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, - struct event_key *key __maybe_unused) +static bool ioport_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return kvm_entry_event(evsel); + return kvm_entry_event(sample->evsel); } static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, @@ -130,31 +124,25 @@ static const struct kvm_events_ops ioport_events = { }; /* The time of emulation msr is from kvm_msr to kvm_entry. */ -static void msr_event_get_key(struct evsel *evsel, - struct perf_sample *sample, - struct event_key *key) +static void msr_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(evsel, sample, "ecx"); - key->info = evsel__intval(evsel, sample, "write"); + key->key = evsel__intval(sample->evsel, sample, "ecx"); + key->info = evsel__intval(sample->evsel, sample, "write"); } -static bool msr_event_begin(struct evsel *evsel, - struct perf_sample *sample, - struct event_key *key) +static bool msr_event_begin(struct perf_sample *sample, struct event_key *key) { - if (evsel__name_is(evsel, "kvm:kvm_msr")) { - msr_event_get_key(evsel, sample, key); + if (evsel__name_is(sample->evsel, "kvm:kvm_msr")) { + msr_event_get_key(sample, key); return true; } return false; } -static bool msr_event_end(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, - struct event_key *key __maybe_unused) +static bool msr_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return kvm_entry_event(evsel); + return kvm_entry_event(sample->evsel); } static void msr_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, diff --git a/tools/perf/util/kvm-stat.c b/tools/perf/util/kvm-stat.c index 27f16810498c..f17a6132958d 100644 --- a/tools/perf/util/kvm-stat.c +++ b/tools/perf/util/kvm-stat.c @@ -11,22 +11,20 @@ bool kvm_exit_event(struct evsel *evsel) return evsel__name_is(evsel, kvm_exit_trace(e_machine)); } -void exit_event_get_key(struct evsel *evsel, - struct perf_sample *sample, +void exit_event_get_key(struct perf_sample *sample, struct event_key *key) { - uint16_t e_machine = evsel__e_machine(evsel, /*e_flags=*/NULL); + uint16_t e_machine = evsel__e_machine(sample->evsel, /*e_flags=*/NULL); key->info = 0; - key->key = evsel__intval(evsel, sample, kvm_exit_reason(e_machine)); + key->key = evsel__intval(sample->evsel, sample, kvm_exit_reason(e_machine)); } -bool exit_event_begin(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key) +bool exit_event_begin(struct perf_sample *sample, struct event_key *key) { - if (kvm_exit_event(evsel)) { - exit_event_get_key(evsel, sample, key); + if (kvm_exit_event(sample->evsel)) { + exit_event_get_key(sample, key); return true; } @@ -40,11 +38,10 @@ bool kvm_entry_event(struct evsel *evsel) return evsel__name_is(evsel, kvm_entry_trace(e_machine)); } -bool exit_event_end(struct evsel *evsel, - struct perf_sample *sample __maybe_unused, +bool exit_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused) { - return kvm_entry_event(evsel); + return kvm_entry_event(sample->evsel); } static const char *get_exit_reason(struct perf_kvm_stat *kvm, diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index 4a998aaece5d..cdbd921a555f 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h @@ -53,18 +53,15 @@ struct kvm_event { }; struct child_event_ops { - void (*get_key)(struct evsel *evsel, - struct perf_sample *sample, + void (*get_key)(struct perf_sample *sample, struct event_key *key); const char *name; }; struct kvm_events_ops { - bool (*is_begin_event)(struct evsel *evsel, - struct perf_sample *sample, + bool (*is_begin_event)(struct perf_sample *sample, struct event_key *key); - bool (*is_end_event)(struct evsel *evsel, - struct perf_sample *sample, struct event_key *key); + bool (*is_end_event)(struct perf_sample *sample, struct event_key *key); const struct child_event_ops *child_ops; void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, char *decode); @@ -116,14 +113,11 @@ struct kvm_reg_events_ops { #ifdef HAVE_LIBTRACEEVENT -void exit_event_get_key(struct evsel *evsel, - struct perf_sample *sample, +void exit_event_get_key(struct perf_sample *sample, struct event_key *key); -bool exit_event_begin(struct evsel *evsel, - struct perf_sample *sample, +bool exit_event_begin(struct perf_sample *sample, struct event_key *key); -bool exit_event_end(struct evsel *evsel, - struct perf_sample *sample, +bool exit_event_end(struct perf_sample *sample, struct event_key *key); void exit_event_decode_key(struct perf_kvm_stat *kvm, struct event_key *key, From fd7dd303c430bf230b2192eb06696a7361f19850 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:09 -0700 Subject: [PATCH 1277/5214] perf evsel: Refactor evsel tracepoint sample accessors perf_sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The evsel argument to evsel__intval, evsel__rawptr, and similar functions, is unnecessary as it can be read from the sample. Remove the evsel and rename the function to match that the data is coming from the sample. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 34 +++---- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-kwork.c | 29 +++--- tools/perf/builtin-lock.c | 32 +++---- tools/perf/builtin-sched.c | 92 +++++++++---------- tools/perf/builtin-timechart.c | 88 +++++++++--------- tools/perf/builtin-trace.c | 12 +-- tools/perf/tests/openat-syscall-tp-fields.c | 2 +- tools/perf/tests/switch-tracking.c | 4 +- tools/perf/util/evsel.c | 22 ++--- tools/perf/util/evsel.h | 12 +-- tools/perf/util/intel-pt.c | 2 +- .../perf/util/kvm-stat-arch/kvm-stat-arm64.c | 6 +- .../util/kvm-stat-arch/kvm-stat-loongarch.c | 2 +- .../util/kvm-stat-arch/kvm-stat-powerpc.c | 2 +- .../perf/util/kvm-stat-arch/kvm-stat-riscv.c | 3 +- tools/perf/util/kvm-stat-arch/kvm-stat-s390.c | 8 +- tools/perf/util/kvm-stat-arch/kvm-stat-x86.c | 16 ++-- tools/perf/util/kvm-stat.c | 2 +- 20 files changed, 183 insertions(+), 189 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index e8cf8d399528..1d245d509b20 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -1194,7 +1194,7 @@ static int perf_inject__sched_stat(const struct perf_tool *tool, union perf_event *event_sw; struct perf_sample sample_sw; struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - u32 pid = evsel__intval(evsel, sample, "pid"); + u32 pid = perf_sample__intval(sample, "pid"); int ret; list_for_each_entry(ent, &inject->samples, node) { diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 45b383fa9ce9..d7233a8b43be 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -173,10 +173,10 @@ static int insert_caller_stat(unsigned long call_site, static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample) { - unsigned long ptr = evsel__intval(evsel, sample, "ptr"), - call_site = evsel__intval(evsel, sample, "call_site"); - int bytes_req = evsel__intval(evsel, sample, "bytes_req"), - bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc"); + unsigned long ptr = perf_sample__intval(sample, "ptr"), + call_site = perf_sample__intval(sample, "call_site"); + int bytes_req = perf_sample__intval(sample, "bytes_req"), + bytes_alloc = perf_sample__intval(sample, "bytes_alloc"); if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) || insert_caller_stat(call_site, bytes_req, bytes_alloc)) @@ -202,7 +202,7 @@ static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *s int node1, node2; node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu}); - node2 = evsel__intval(evsel, sample, "node"); + node2 = perf_sample__intval(sample, "node"); /* * If the field "node" is NUMA_NO_NODE (-1), we don't take it @@ -243,9 +243,9 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr, return NULL; } -static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_free_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - unsigned long ptr = evsel__intval(evsel, sample, "ptr"); + unsigned long ptr = perf_sample__intval(sample, "ptr"); struct alloc_stat *s_alloc, *s_caller; s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); @@ -808,10 +808,9 @@ static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample, static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample) { u64 page; - unsigned int order = evsel__intval(evsel, sample, "order"); - unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags"); - unsigned int migrate_type = evsel__intval(evsel, sample, - "migratetype"); + unsigned int order = perf_sample__intval(sample, "order"); + unsigned int gfp_flags = perf_sample__intval(sample, "gfp_flags"); + unsigned int migrate_type = perf_sample__intval(sample, "migratetype"); u64 bytes = kmem_page_size << order; u64 callsite; struct page_stat *pstat; @@ -822,9 +821,9 @@ static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_samp }; if (use_pfn) - page = evsel__intval(evsel, sample, "pfn"); + page = perf_sample__intval(sample, "pfn"); else - page = evsel__intval(evsel, sample, "page"); + page = perf_sample__intval(sample, "page"); nr_page_allocs++; total_page_alloc_bytes += bytes; @@ -877,10 +876,11 @@ static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_samp return 0; } -static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_page_free_event(struct evsel *evsel __maybe_unused, + struct perf_sample *sample) { u64 page; - unsigned int order = evsel__intval(evsel, sample, "order"); + unsigned int order = perf_sample__intval(sample, "order"); u64 bytes = kmem_page_size << order; struct page_stat *pstat; struct page_stat this = { @@ -888,9 +888,9 @@ static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sampl }; if (use_pfn) - page = evsel__intval(evsel, sample, "pfn"); + page = perf_sample__intval(sample, "pfn"); else - page = evsel__intval(evsel, sample, "page"); + page = perf_sample__intval(sample, "page"); nr_page_frees++; total_page_free_bytes += bytes; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index d9b9792894a8..dd2ed21596aa 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -930,7 +930,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, return NULL; } - vcpu_record->vcpu_id = evsel__intval(sample->evsel, sample, vcpu_id_str(e_machine)); + vcpu_record->vcpu_id = perf_sample__intval(sample, vcpu_id_str(e_machine)); thread__set_priv(thread, vcpu_record); } diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index d4bb19ed91b5..ab4519287f47 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -1006,7 +1006,7 @@ static void irq_work_init(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1014,11 +1014,11 @@ static void irq_work_init(struct perf_kwork *kwork, work->cpu = sample->cpu; if (kwork->report == KWORK_REPORT_TOP) { - work->id = evsel__intval_common(evsel, sample, "common_pid"); + work->id = perf_sample__intval_common(sample, "common_pid"); work->name = NULL; } else { - work->id = evsel__intval(evsel, sample, "irq"); - work->name = evsel__strval(evsel, sample, "name"); + work->id = perf_sample__intval(sample, "irq"); + work->name = perf_sample__strval(sample, "name"); } } @@ -1144,10 +1144,10 @@ static void softirq_work_init(struct perf_kwork *kwork, work->cpu = sample->cpu; if (kwork->report == KWORK_REPORT_TOP) { - work->id = evsel__intval_common(evsel, sample, "common_pid"); + work->id = perf_sample__intval_common(sample, "common_pid"); work->name = NULL; } else { - num = evsel__intval(evsel, sample, "vec"); + num = perf_sample__intval(sample, "vec"); work->id = num; work->name = evsel__softirq_name(evsel, num); } @@ -1234,17 +1234,16 @@ static void workqueue_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { char *modp = NULL; - unsigned long long function_addr = evsel__intval(evsel, - sample, "function"); + unsigned long long function_addr = perf_sample__intval(sample, "function"); work->class = class; work->cpu = sample->cpu; - work->id = evsel__intval(evsel, sample, "work"); + work->id = perf_sample__intval(sample, "work"); work->name = function_addr == 0 ? NULL : machine__resolve_kernel_addr(machine, &function_addr, &modp); } @@ -1302,7 +1301,7 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1310,11 +1309,11 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, work->cpu = sample->cpu; if (src_type == KWORK_TRACE_EXIT) { - work->id = evsel__intval(evsel, sample, "prev_pid"); - work->name = strdup(evsel__strval(evsel, sample, "prev_comm")); + work->id = perf_sample__intval(sample, "prev_pid"); + work->name = strdup(perf_sample__strval(sample, "prev_comm")); } else if (src_type == KWORK_TRACE_ENTRY) { - work->id = evsel__intval(evsel, sample, "next_pid"); - work->name = strdup(evsel__strval(evsel, sample, "next_comm")); + work->id = perf_sample__intval(sample, "next_pid"); + work->name = strdup(perf_sample__strval(sample, "next_comm")); } } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 4d8ddf6391e8..cbf3a39c7837 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -563,15 +563,15 @@ static int get_key_by_aggr_mode(u64 *key, u64 addr, struct evsel *evsel, return get_key_by_aggr_mode_simple(key, addr, sample->tid); } -static int report_lock_acquire_event(struct evsel *evsel, +static int report_lock_acquire_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; struct lock_seq_stat *seq; - const char *name = evsel__strval(evsel, sample, "name"); - u64 addr = evsel__intval(evsel, sample, "lockdep_addr"); - int flag = evsel__intval(evsel, sample, "flags"); + const char *name = perf_sample__strval(sample, "name"); + u64 addr = perf_sample__intval(sample, "lockdep_addr"); + int flag = perf_sample__intval(sample, "flags"); u64 key; int ret; @@ -638,15 +638,15 @@ static int report_lock_acquire_event(struct evsel *evsel, return 0; } -static int report_lock_acquired_event(struct evsel *evsel, +static int report_lock_acquired_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; struct lock_seq_stat *seq; u64 contended_term; - const char *name = evsel__strval(evsel, sample, "name"); - u64 addr = evsel__intval(evsel, sample, "lockdep_addr"); + const char *name = perf_sample__strval(sample, "name"); + u64 addr = perf_sample__intval(sample, "lockdep_addr"); u64 key; int ret; @@ -704,14 +704,14 @@ static int report_lock_acquired_event(struct evsel *evsel, return 0; } -static int report_lock_contended_event(struct evsel *evsel, +static int report_lock_contended_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; struct lock_seq_stat *seq; - const char *name = evsel__strval(evsel, sample, "name"); - u64 addr = evsel__intval(evsel, sample, "lockdep_addr"); + const char *name = perf_sample__strval(sample, "name"); + u64 addr = perf_sample__intval(sample, "lockdep_addr"); u64 key; int ret; @@ -762,14 +762,14 @@ static int report_lock_contended_event(struct evsel *evsel, return 0; } -static int report_lock_release_event(struct evsel *evsel, +static int report_lock_release_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; struct lock_seq_stat *seq; - const char *name = evsel__strval(evsel, sample, "name"); - u64 addr = evsel__intval(evsel, sample, "lockdep_addr"); + const char *name = perf_sample__strval(sample, "name"); + u64 addr = perf_sample__intval(sample, "lockdep_addr"); u64 key; int ret; @@ -969,8 +969,8 @@ static int report_lock_contention_begin_event(struct evsel *evsel, struct lock_stat *ls; struct thread_stat *ts; struct lock_seq_stat *seq; - u64 addr = evsel__intval(evsel, sample, "lock_addr"); - unsigned int flags = evsel__intval(evsel, sample, "flags"); + u64 addr = perf_sample__intval(sample, "lock_addr"); + unsigned int flags = perf_sample__intval(sample, "flags"); u64 key; int i, ret; static bool kmap_loaded; @@ -1134,7 +1134,7 @@ static int report_lock_contention_end_event(struct evsel *evsel, struct thread_stat *ts; struct lock_seq_stat *seq; u64 contended_term; - u64 addr = evsel__intval(evsel, sample, "lock_addr"); + u64 addr = perf_sample__intval(sample, "lock_addr"); u64 key; int ret; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 2950c5c3026e..01228f630121 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -829,8 +829,8 @@ replay_wakeup_event(struct perf_sched *sched, struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { - const char *comm = evsel__strval(evsel, sample, "comm"); - const u32 pid = evsel__intval(evsel, sample, "pid"); + const char *comm = perf_sample__strval(sample, "comm"); + const u32 pid = perf_sample__intval(sample, "pid"); struct task_desc *waker, *wakee; if (verbose > 0) { @@ -851,10 +851,10 @@ static int replay_switch_event(struct perf_sched *sched, struct perf_sample *sample, struct machine *machine __maybe_unused) { - const char *prev_comm = evsel__strval(evsel, sample, "prev_comm"), - *next_comm = evsel__strval(evsel, sample, "next_comm"); - const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"), - next_pid = evsel__intval(evsel, sample, "next_pid"); + const char *prev_comm = perf_sample__strval(sample, "prev_comm"), + *next_comm = perf_sample__strval(sample, "next_comm"); + const u32 prev_pid = perf_sample__intval(sample, "prev_pid"), + next_pid = perf_sample__intval(sample, "next_pid"); struct task_desc *prev, __maybe_unused *next; u64 timestamp0, timestamp = sample->time; int cpu = sample->cpu; @@ -1134,13 +1134,13 @@ static void free_work_atoms(struct work_atoms *atoms) } static int latency_switch_event(struct perf_sched *sched, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { - const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"), - next_pid = evsel__intval(evsel, sample, "next_pid"); - const char prev_state = evsel__taskstate(evsel, sample, "prev_state"); + const u32 prev_pid = perf_sample__intval(sample, "prev_pid"), + next_pid = perf_sample__intval(sample, "next_pid"); + const char prev_state = perf_sample__taskstate(sample, "prev_state"); struct work_atoms *out_events, *in_events; struct thread *sched_out, *sched_in; u64 timestamp0, timestamp = sample->time; @@ -1204,12 +1204,12 @@ static int latency_switch_event(struct perf_sched *sched, } static int latency_runtime_event(struct perf_sched *sched, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { - const u32 pid = evsel__intval(evsel, sample, "pid"); - const u64 runtime = evsel__intval(evsel, sample, "runtime"); + const u32 pid = perf_sample__intval(sample, "pid"); + const u64 runtime = perf_sample__intval(sample, "runtime"); struct thread *thread = machine__findnew_thread(machine, -1, pid); struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); u64 timestamp = sample->time; @@ -1239,11 +1239,11 @@ static int latency_runtime_event(struct perf_sched *sched, } static int latency_wakeup_event(struct perf_sched *sched, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { - const u32 pid = evsel__intval(evsel, sample, "pid"); + const u32 pid = perf_sample__intval(sample, "pid"); struct work_atoms *atoms; struct work_atom *atom; struct thread *wakee; @@ -1300,11 +1300,11 @@ static int latency_wakeup_event(struct perf_sched *sched, } static int latency_migrate_task_event(struct perf_sched *sched, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { - const u32 pid = evsel__intval(evsel, sample, "pid"); + const u32 pid = perf_sample__intval(sample, "pid"); u64 timestamp = sample->time; struct work_atoms *atoms; struct work_atom *atom; @@ -1626,11 +1626,11 @@ static void print_sched_map(struct perf_sched *sched, struct perf_cpu this_cpu, } } -static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, +static int map_switch_event(struct perf_sched *sched, struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { - const u32 next_pid = evsel__intval(evsel, sample, "next_pid"); - const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"); + const u32 next_pid = perf_sample__intval(sample, "next_pid"); + const u32 prev_pid = perf_sample__intval(sample, "prev_pid"); struct thread *sched_in, *sched_out; struct thread_runtime *tr; int new_shortname; @@ -1797,8 +1797,8 @@ static int process_sched_switch_event(const struct perf_tool *tool, { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); int this_cpu = sample->cpu, err = 0; - u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"), - next_pid = evsel__intval(evsel, sample, "next_pid"); + u32 prev_pid = perf_sample__intval(sample, "prev_pid"), + next_pid = perf_sample__intval(sample, "next_pid"); if (sched->curr_pid[this_cpu] != (u32)-1) { /* @@ -2068,12 +2068,11 @@ static char *timehist_get_commstr(struct thread *thread) /* prio field format: xxx or xxx->yyy */ #define MAX_PRIO_STR_LEN 8 -static char *timehist_get_priostr(struct evsel *evsel, - struct thread *thread, +static char *timehist_get_priostr(struct thread *thread, struct perf_sample *sample) { static char prio_str[16]; - int prev_prio = (int)evsel__intval(evsel, sample, "prev_prio"); + int prev_prio = (int)perf_sample__intval(sample, "prev_prio"); struct thread_runtime *tr = thread__priv(thread); if (tr->prio != prev_prio && tr->prio != -1) @@ -2161,15 +2160,14 @@ static void timehist_header(struct perf_sched *sched) } static void timehist_print_sample(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, struct addr_location *al, struct thread *thread, u64 t, const char state) { struct thread_runtime *tr = thread__priv(thread); - const char *next_comm = evsel__strval(evsel, sample, "next_comm"); - const u32 next_pid = evsel__intval(evsel, sample, "next_pid"); + const char *next_comm = perf_sample__strval(sample, "next_comm"); + const u32 next_pid = perf_sample__intval(sample, "next_pid"); u32 max_cpus = sched->max_cpu.cpu + 1; char tstr[64]; char nstr[30]; @@ -2198,14 +2196,15 @@ static void timehist_print_sample(struct perf_sched *sched, } if (!thread__comm_set(thread)) { - const char *prev_comm = evsel__strval(evsel, sample, "prev_comm"); - thread__set_comm(thread, prev_comm, sample->time); + const char *prev_comm = perf_sample__strval(sample, "prev_comm"); + + thread__set_comm(thread, prev_comm, sample->time); } printf(" %-*s ", comm_width, timehist_get_commstr(thread)); if (sched->show_prio) - printf(" %-*s ", MAX_PRIO_STR_LEN, timehist_get_priostr(evsel, thread, sample)); + printf(" %-*s ", MAX_PRIO_STR_LEN, timehist_get_priostr(thread, sample)); wait_time = tr->dt_sleep + tr->dt_iowait + tr->dt_preempt; print_sched_time(wait_time, 6); @@ -2319,7 +2318,7 @@ static bool is_idle_sample(struct perf_sample *sample, { /* pid 0 == swapper == idle task */ if (evsel__name_is(evsel, "sched:sched_switch")) - return evsel__intval(evsel, sample, "prev_pid") == 0; + return perf_sample__intval(sample, "prev_pid") == 0; return sample->pid == 0; } @@ -2539,7 +2538,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, itr->last_thread = thread__get(thread); /* copy task callchain when entering to idle */ - if (evsel__intval(evsel, sample, "next_pid") == 0) + if (perf_sample__intval(sample, "next_pid") == 0) save_idle_callchain(sched, itr, sample); } } @@ -2572,7 +2571,7 @@ static bool timehist_skip_sample(struct perf_sched *sched, if (tr && tr->prio != -1) prio = tr->prio; else if (evsel__name_is(evsel, "sched:sched_switch")) - prio = evsel__intval(evsel, sample, "prev_prio"); + prio = perf_sample__intval(sample, "prev_prio"); if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { rc = true; @@ -2583,8 +2582,8 @@ static bool timehist_skip_sample(struct perf_sched *sched, if (sched->idle_hist) { if (!evsel__name_is(evsel, "sched:sched_switch")) rc = true; - else if (evsel__intval(evsel, sample, "prev_pid") != 0 && - evsel__intval(evsel, sample, "next_pid") != 0) + else if (perf_sample__intval(sample, "prev_pid") != 0 && + perf_sample__intval(sample, "next_pid") != 0) rc = true; } @@ -2647,7 +2646,7 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, struct thread *thread; struct thread_runtime *tr = NULL; /* want pid of awakened task not pid in sample */ - const u32 pid = evsel__intval(evsel, sample, "pid"); + const u32 pid = perf_sample__intval(sample, "pid"); thread = machine__findnew_thread(machine, 0, pid); if (thread == NULL) @@ -2686,8 +2685,8 @@ static void timehist_print_migration_event(struct perf_sched *sched, return; max_cpus = sched->max_cpu.cpu + 1; - ocpu = evsel__intval(evsel, sample, "orig_cpu"); - dcpu = evsel__intval(evsel, sample, "dest_cpu"); + ocpu = perf_sample__intval(sample, "orig_cpu"); + dcpu = perf_sample__intval(sample, "dest_cpu"); thread = machine__findnew_thread(machine, sample->pid, sample->tid); if (thread == NULL) @@ -2736,7 +2735,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, struct thread *thread; struct thread_runtime *tr = NULL; /* want pid of migrated task not pid in sample */ - const u32 pid = evsel__intval(evsel, sample, "pid"); + const u32 pid = perf_sample__intval(sample, "pid"); thread = machine__findnew_thread(machine, 0, pid); if (thread == NULL) @@ -2761,14 +2760,13 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, return 0; } -static void timehist_update_task_prio(struct evsel *evsel, - struct perf_sample *sample, +static void timehist_update_task_prio(struct perf_sample *sample, struct machine *machine) { struct thread *thread; struct thread_runtime *tr = NULL; - const u32 next_pid = evsel__intval(evsel, sample, "next_pid"); - const u32 next_prio = evsel__intval(evsel, sample, "next_prio"); + const u32 next_pid = perf_sample__intval(sample, "next_pid"); + const u32 next_prio = perf_sample__intval(sample, "next_prio"); if (next_pid == 0) thread = get_idle_thread(sample->cpu); @@ -2798,7 +2796,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, struct thread_runtime *tr = NULL; u64 tprev, t = sample->time; int rc = 0; - const char state = evsel__taskstate(evsel, sample, "prev_state"); + const char state = perf_sample__taskstate(sample, "prev_state"); addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { @@ -2809,7 +2807,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (sched->show_prio || sched->prio_str) - timehist_update_task_prio(evsel, sample, machine); + timehist_update_task_prio(sample, machine); thread = timehist_get_thread(sched, sample, machine, evsel); if (thread == NULL) { @@ -2891,7 +2889,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (!sched->summary_only) - timehist_print_sample(sched, evsel, sample, &al, thread, t, state); + timehist_print_sample(sched, sample, &al, thread, t, state); } out: diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 8692d11ccd29..034e0ba3f9cb 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -597,12 +597,12 @@ static int process_sample_event(const struct perf_tool *tool, static int process_sample_cpu_idle(struct timechart *tchart __maybe_unused, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { - u32 state = evsel__intval(evsel, sample, "state"); - u32 cpu_id = evsel__intval(evsel, sample, "cpu_id"); + u32 state = perf_sample__intval(sample, "state"); + u32 cpu_id = perf_sample__intval(sample, "cpu_id"); if (state == (u32)PWR_EVENT_EXIT) c_state_end(tchart, cpu_id, sample->time); @@ -613,12 +613,12 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused, static int process_sample_cpu_frequency(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { - u32 state = evsel__intval(evsel, sample, "state"); - u32 cpu_id = evsel__intval(evsel, sample, "cpu_id"); + u32 state = perf_sample__intval(sample, "state"); + u32 cpu_id = perf_sample__intval(sample, "cpu_id"); p_state_change(tchart, cpu_id, sample->time, state); return 0; @@ -626,13 +626,13 @@ process_sample_cpu_frequency(struct timechart *tchart, static int process_sample_sched_wakeup(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace) { - u8 flags = evsel__intval(evsel, sample, "common_flags"); - int waker = evsel__intval(evsel, sample, "common_pid"); - int wakee = evsel__intval(evsel, sample, "pid"); + u8 flags = perf_sample__intval(sample, "common_flags"); + int waker = perf_sample__intval(sample, "common_pid"); + int wakee = perf_sample__intval(sample, "pid"); sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace); return 0; @@ -640,13 +640,13 @@ process_sample_sched_wakeup(struct timechart *tchart, static int process_sample_sched_switch(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace) { - int prev_pid = evsel__intval(evsel, sample, "prev_pid"); - int next_pid = evsel__intval(evsel, sample, "next_pid"); - u64 prev_state = evsel__intval(evsel, sample, "prev_state"); + int prev_pid = perf_sample__intval(sample, "prev_pid"); + int next_pid = perf_sample__intval(sample, "next_pid"); + u64 prev_state = perf_sample__intval(sample, "prev_state"); sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, prev_state, backtrace); @@ -656,12 +656,12 @@ process_sample_sched_switch(struct timechart *tchart, #ifdef SUPPORT_OLD_POWER_EVENTS static int process_sample_power_start(struct timechart *tchart __maybe_unused, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { - u64 cpu_id = evsel__intval(evsel, sample, "cpu_id"); - u64 value = evsel__intval(evsel, sample, "value"); + u64 cpu_id = perf_sample__intval(sample, "cpu_id"); + u64 value = perf_sample__intval(sample, "value"); c_state_start(cpu_id, sample->time, value); return 0; @@ -679,12 +679,12 @@ process_sample_power_end(struct timechart *tchart, static int process_sample_power_frequency(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { - u64 cpu_id = evsel__intval(evsel, sample, "cpu_id"); - u64 value = evsel__intval(evsel, sample, "value"); + u64 cpu_id = perf_sample__intval(sample, "cpu_id"); + u64 value = perf_sample__intval(sample, "value"); p_state_change(tchart, cpu_id, sample->time, value); return 0; @@ -849,120 +849,120 @@ static int pid_end_io_sample(struct timechart *tchart, int pid, int type, static int process_enter_read(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, sample->time, fd); } static int process_exit_read(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, sample->time, ret); } static int process_enter_write(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, sample->time, fd); } static int process_exit_write(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, sample->time, ret); } static int process_enter_sync(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, sample->time, fd); } static int process_exit_sync(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, sample->time, ret); } static int process_enter_tx(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, sample->time, fd); } static int process_exit_tx(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, sample->time, ret); } static int process_enter_rx(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, sample->time, fd); } static int process_exit_rx(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, sample->time, ret); } static int process_enter_poll(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long fd = evsel__intval(evsel, sample, "fd"); + long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, sample->time, fd); } static int process_exit_poll(struct timechart *tchart, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct perf_sample *sample) { - long ret = evsel__intval(evsel, sample, "ret"); + long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, sample->time, ret); } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1aafa77f72cf..e8aabd7a72fe 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3094,7 +3094,7 @@ errno_print: { return err; } -static int trace__vfs_getname(struct trace *trace, struct evsel *evsel, +static int trace__vfs_getname(struct trace *trace, struct evsel *evsel __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample) { @@ -3103,7 +3103,7 @@ static int trace__vfs_getname(struct trace *trace, struct evsel *evsel, size_t filename_len, entry_str_len, to_move; ssize_t remaining_space; char *pos; - const char *filename = evsel__rawptr(evsel, sample, "pathname"); + const char *filename = perf_sample__strval(sample, "pathname"); if (!thread) goto out; @@ -3159,7 +3159,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel, union perf_event *event __maybe_unused, struct perf_sample *sample) { - u64 runtime = evsel__intval(evsel, sample, "runtime"); + u64 runtime = perf_sample__intval(sample, "runtime"); double runtime_ms = (double)runtime / NSEC_PER_MSEC; struct thread *thread = machine__findnew_thread(trace->host, sample->pid, @@ -3178,10 +3178,10 @@ static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel, out_dump: fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", evsel->name, - evsel__strval(evsel, sample, "comm"), - (pid_t)evsel__intval(evsel, sample, "pid"), + perf_sample__strval(sample, "comm"), + (pid_t)perf_sample__intval(sample, "pid"), runtime, - evsel__intval(evsel, sample, "vruntime")); + perf_sample__intval(sample, "vruntime")); goto out_put; } diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 5523cf4e9321..9ff8caff98c3 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -118,7 +118,7 @@ static int test__syscall_openat_tp_fields(struct test_suite *test __maybe_unused goto out_delete_evlist; } - tp_flags = evsel__intval(evsel, &sample, "flags"); + tp_flags = perf_sample__intval(&sample, "flags"); perf_sample__exit(&sample); /* C library wrapper may set additional flags, access mode must be unchanged */ diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 72a8289e846d..22b0302252db 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -140,8 +140,8 @@ static int process_sample_event(struct evlist *evlist, evsel = evlist__id2evsel(evlist, sample.id); if (evsel == switch_tracking->switch_evsel) { - next_tid = evsel__intval(evsel, &sample, "next_pid"); - prev_tid = evsel__intval(evsel, &sample, "prev_pid"); + next_tid = perf_sample__intval(&sample, "next_pid"); + prev_tid = perf_sample__intval(&sample, "prev_pid"); cpu = sample.cpu; pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n", cpu, prev_tid, next_tid); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2ee87fd84d3e..ee30e15af054 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3687,9 +3687,9 @@ struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *na return tp_format ? tep_find_common_field(tp_format, name) : NULL; } -void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char *name) +void *perf_sample__rawptr(struct perf_sample *sample, const char *name) { - struct tep_format_field *field = evsel__field(evsel, name); + struct tep_format_field *field = evsel__field(sample->evsel, name); int offset; if (!field) @@ -3746,21 +3746,21 @@ u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sam return 0; } -u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *name) +u64 perf_sample__intval(struct perf_sample *sample, const char *name) { - struct tep_format_field *field = evsel__field(evsel, name); + struct tep_format_field *field = evsel__field(sample->evsel, name); - return field ? format_field__intval(field, sample, evsel->needs_swap) : 0; + return field ? format_field__intval(field, sample, sample->evsel->needs_swap) : 0; } -u64 evsel__intval_common(struct evsel *evsel, struct perf_sample *sample, const char *name) +u64 perf_sample__intval_common(struct perf_sample *sample, const char *name) { - struct tep_format_field *field = evsel__common_field(evsel, name); + struct tep_format_field *field = evsel__common_field(sample->evsel, name); - return field ? format_field__intval(field, sample, evsel->needs_swap) : 0; + return field ? format_field__intval(field, sample, sample->evsel->needs_swap) : 0; } -char evsel__taskstate(struct evsel *evsel, struct perf_sample *sample, const char *name) +char perf_sample__taskstate(struct perf_sample *sample, const char *name) { static struct tep_format_field *prev_state_field; static const char *states; @@ -3769,7 +3769,7 @@ char evsel__taskstate(struct evsel *evsel, struct perf_sample *sample, const cha unsigned int bit; char state = '?'; /* '?' denotes unknown task state */ - field = evsel__field(evsel, name); + field = evsel__field(sample->evsel, name); if (!field) return state; @@ -3788,7 +3788,7 @@ char evsel__taskstate(struct evsel *evsel, struct perf_sample *sample, const cha * * We can change this if we have a good reason in the future. */ - val = evsel__intval(evsel, sample, name); + val = perf_sample__intval(sample, name); bit = val ? ffs(val) : 0; state = (!bit || bit > strlen(states)) ? 'R' : states[bit-1]; return state; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 339b5c08a33d..927e5b4756cc 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -371,14 +371,14 @@ bool evsel__precise_ip_fallback(struct evsel *evsel); struct perf_sample; #ifdef HAVE_LIBTRACEEVENT -void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char *name); -u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *name); -u64 evsel__intval_common(struct evsel *evsel, struct perf_sample *sample, const char *name); -char evsel__taskstate(struct evsel *evsel, struct perf_sample *sample, const char *name); +void *perf_sample__rawptr(struct perf_sample *sample, const char *name); +u64 perf_sample__intval(struct perf_sample *sample, const char *name); +u64 perf_sample__intval_common(struct perf_sample *sample, const char *name); +char perf_sample__taskstate(struct perf_sample *sample, const char *name); -static inline char *evsel__strval(struct evsel *evsel, struct perf_sample *sample, const char *name) +static inline char *perf_sample__strval(struct perf_sample *sample, const char *name) { - return evsel__rawptr(evsel, sample, name); + return perf_sample__rawptr(sample, name); } #endif diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index d9c86ac49748..b8add2b20033 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -3447,7 +3447,7 @@ static int intel_pt_process_switch(struct intel_pt *pt, if (evsel != pt->switch_evsel) return 0; - tid = evsel__intval(evsel, sample, "next_pid"); + tid = perf_sample__intval(sample, "next_pid"); cpu = sample->cpu; intel_pt_log("sched_switch: cpu %d tid %d time %"PRIu64" tsc %#"PRIx64"\n", diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c b/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c index 1e76906f719c..018b0db0e6e7 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-arm64.c @@ -20,10 +20,8 @@ static const char * const __kvm_events_tp[] = { static void event_get_key(struct perf_sample *sample, struct event_key *key) { - struct evsel *evsel = sample->evsel; - key->info = 0; - key->key = evsel__intval(evsel, sample, kvm_exit_reason(EM_AARCH64)); + key->key = perf_sample__intval(sample, kvm_exit_reason(EM_AARCH64)); key->exit_reasons = arm64_exit_reasons; /* @@ -32,7 +30,7 @@ static void event_get_key(struct perf_sample *sample, * properly decode event's est_ec. */ if (key->key == ARM_EXCEPTION_TRAP) { - key->key = evsel__intval(evsel, sample, kvm_trap_exit_reason); + key->key = perf_sample__intval(sample, kvm_trap_exit_reason); key->exit_reasons = arm64_trap_exit_reasons; } } diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c b/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c index 9d6265290f6d..a04cd09e3361 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c @@ -78,7 +78,7 @@ static void event_gspr_get_key(struct perf_sample *sample, struct event_key *key unsigned int insn; key->key = LOONGARCH_EXCEPTION_OTHERS; - insn = evsel__intval(sample->evsel, sample, "inst_word"); + insn = perf_sample__intval(sample, "inst_word"); switch (insn >> 24) { case 0: diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c index 5158d7e88ee6..96d9c4ae0209 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c @@ -32,7 +32,7 @@ static void hcall_event_get_key(struct perf_sample *sample, struct event_key *key) { key->info = 0; - key->key = evsel__intval(sample->evsel, sample, "req"); + key->key = perf_sample__intval(sample, "req"); } static const char *get_hcall_exit_reason(u64 exit_code) diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c b/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c index e8db8b4f8e2e..967bba261a47 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-riscv.c @@ -26,8 +26,7 @@ static void event_get_key(struct perf_sample *sample, int xlen = 64; // TODO: 32-bit support. key->info = 0; - key->key = evsel__intval(sample->evsel, sample, - kvm_exit_reason(EM_RISCV)) & ~CAUSE_IRQ_FLAG(xlen); + key->key = perf_sample__intval(sample, kvm_exit_reason(EM_RISCV)) & ~CAUSE_IRQ_FLAG(xlen); key->exit_reasons = riscv_exit_reasons; } diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c b/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c index 158372ba0205..4771fc69fa39 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-s390.c @@ -23,7 +23,7 @@ static void event_icpt_insn_get_key(struct perf_sample *sample, { u64 insn; - insn = evsel__intval(sample->evsel, sample, "instruction"); + insn = perf_sample__intval(sample, "instruction"); key->key = icpt_insn_decoder(insn); key->exit_reasons = sie_icpt_insn_codes; } @@ -31,21 +31,21 @@ static void event_icpt_insn_get_key(struct perf_sample *sample, static void event_sigp_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "order_code"); + key->key = perf_sample__intval(sample, "order_code"); key->exit_reasons = sie_sigp_order_codes; } static void event_diag_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "code"); + key->key = perf_sample__intval(sample, "code"); key->exit_reasons = sie_diagnose_codes; } static void event_icpt_prog_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "code"); + key->key = perf_sample__intval(sample, "code"); key->exit_reasons = sie_icpt_prog_codes; } diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c index 0ce543d82850..788d216f0852 100644 --- a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c +++ b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c @@ -27,8 +27,8 @@ static const struct kvm_events_ops exit_events = { static void mmio_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "gpa"); - key->info = evsel__intval(sample->evsel, sample, "type"); + key->key = perf_sample__intval(sample, "gpa"); + key->info = perf_sample__intval(sample, "type"); } #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 @@ -43,7 +43,7 @@ static bool mmio_event_begin(struct perf_sample *sample, struct event_key *key) /* MMIO write begin event in kernel. */ if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") && - evsel__intval(sample->evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { + perf_sample__intval(sample, "type") == KVM_TRACE_MMIO_WRITE) { mmio_event_get_key(sample, key); return true; } @@ -59,7 +59,7 @@ static bool mmio_event_end(struct perf_sample *sample, struct event_key *key) /* MMIO read end event in kernel.*/ if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") && - evsel__intval(sample->evsel, sample, "type") == KVM_TRACE_MMIO_READ) { + perf_sample__intval(sample, "type") == KVM_TRACE_MMIO_READ) { mmio_event_get_key(sample, key); return true; } @@ -87,8 +87,8 @@ static const struct kvm_events_ops mmio_events = { static void ioport_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "port"); - key->info = evsel__intval(sample->evsel, sample, "rw"); + key->key = perf_sample__intval(sample, "port"); + key->info = perf_sample__intval(sample, "rw"); } static bool ioport_event_begin(struct perf_sample *sample, @@ -126,8 +126,8 @@ static const struct kvm_events_ops ioport_events = { /* The time of emulation msr is from kvm_msr to kvm_entry. */ static void msr_event_get_key(struct perf_sample *sample, struct event_key *key) { - key->key = evsel__intval(sample->evsel, sample, "ecx"); - key->info = evsel__intval(sample->evsel, sample, "write"); + key->key = perf_sample__intval(sample, "ecx"); + key->info = perf_sample__intval(sample, "write"); } static bool msr_event_begin(struct perf_sample *sample, struct event_key *key) diff --git a/tools/perf/util/kvm-stat.c b/tools/perf/util/kvm-stat.c index f17a6132958d..755ab659a05c 100644 --- a/tools/perf/util/kvm-stat.c +++ b/tools/perf/util/kvm-stat.c @@ -17,7 +17,7 @@ void exit_event_get_key(struct perf_sample *sample, uint16_t e_machine = evsel__e_machine(sample->evsel, /*e_flags=*/NULL); key->info = 0; - key->key = evsel__intval(sample->evsel, sample, kvm_exit_reason(e_machine)); + key->key = perf_sample__intval(sample, kvm_exit_reason(e_machine)); } From a50a9b0d6e9bbf537ae2f581d63671394f98bffa Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:10 -0700 Subject: [PATCH 1278/5214] perf trace: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from trace-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 55 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e8aabd7a72fe..eca578212def 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -536,12 +536,12 @@ static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void * return NULL; } -#define perf_evsel__sc_tp_uint(evsel, name, sample) \ - ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ +#define perf_evsel__sc_tp_uint(name, sample) \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(sample->evsel); \ fields->name.integer(&fields->name, sample); }) -#define perf_evsel__sc_tp_ptr(evsel, name, sample) \ - ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ +#define perf_evsel__sc_tp_ptr(name, sample) \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(sample->evsel); \ fields->name.pointer(&fields->name, sample); }) size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val) @@ -2749,8 +2749,8 @@ static int trace__printf_interrupted_entry(struct trace *trace) return printed; } -static int trace__fprintf_sample(struct trace *trace, struct evsel *evsel, - struct perf_sample *sample, struct thread *thread) +static int trace__fprintf_sample(struct trace *trace, struct perf_sample *sample, + struct thread *thread) { int printed = 0; @@ -2758,7 +2758,7 @@ static int trace__fprintf_sample(struct trace *trace, struct evsel *evsel, double ts = (double)sample->time / NSEC_PER_MSEC; printed += fprintf(trace->output, "%22s %10.3f %s %d/%d [%d]\n", - evsel__name(evsel), ts, + evsel__name(sample->evsel), ts, thread__comm_str(thread), sample->pid, sample->tid, sample->cpu); } @@ -2813,7 +2813,7 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel, void *args; int printed = 0; struct thread *thread; - int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; + int id = perf_evsel__sc_tp_uint(id, sample), err = -1; int augmented_args_size = 0, e_machine; void *augmented_args = NULL; struct syscall *sc; @@ -2828,9 +2828,9 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel, if (ttrace == NULL) goto out_put; - trace__fprintf_sample(trace, evsel, sample, thread); + trace__fprintf_sample(trace, sample, thread); - args = perf_evsel__sc_tp_ptr(evsel, args, sample); + args = perf_evsel__sc_tp_ptr(args, sample); if (ttrace->entry_str == NULL) { ttrace->entry_str = malloc(trace__entry_str_size); @@ -2888,12 +2888,11 @@ static int trace__sys_enter(struct trace *trace, struct evsel *evsel, return err; } -static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, - struct perf_sample *sample) +static int trace__fprintf_sys_enter(struct trace *trace, struct perf_sample *sample) { struct thread_trace *ttrace; struct thread *thread; - int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; + int id = perf_evsel__sc_tp_uint(id, sample), err = -1; struct syscall *sc; char msg[1024]; void *args, *augmented_args = NULL; @@ -2903,7 +2902,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); e_machine = thread__e_machine(thread, trace->host, /*e_flags=*/NULL); - sc = trace__syscall_info(trace, evsel, e_machine, id); + sc = trace__syscall_info(trace, sample->evsel, e_machine, id); if (sc == NULL) goto out_put; ttrace = thread__trace(thread, trace); @@ -2914,7 +2913,7 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, if (ttrace == NULL) goto out_put; - args = perf_evsel__sc_tp_ptr(evsel, args, sample); + args = perf_evsel__sc_tp_ptr(args, sample); augmented_args = syscall__augmented_args(sc, sample, &augmented_args_size, trace->raw_augmented_syscalls_args_size); printed += syscall__scnprintf_args(sc, msg, sizeof(msg), args, augmented_args, augmented_args_size, trace, thread); fprintf(trace->output, "%.*s", (int)printed, msg); @@ -2924,10 +2923,11 @@ static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel, return err; } -static int trace__resolve_callchain(struct trace *trace, struct evsel *evsel, +static int trace__resolve_callchain(struct trace *trace, struct perf_sample *sample, struct callchain_cursor *cursor) { + struct evsel *evsel = sample->evsel; struct addr_location al; int max_stack = evsel->core.attr.sample_max_stack ? evsel->core.attr.sample_max_stack : @@ -2962,7 +2962,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, u64 duration = 0; bool duration_calculated = false; struct thread *thread; - int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0, printed = 0; + int id = perf_evsel__sc_tp_uint(id, sample), err = -1, callchain_ret = 0, printed = 0; int alignment = trace->args_alignment, e_machine; struct syscall *sc; struct thread_trace *ttrace; @@ -2976,9 +2976,9 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, if (ttrace == NULL) goto out_put; - trace__fprintf_sample(trace, evsel, sample, thread); + trace__fprintf_sample(trace, sample, thread); - ret = perf_evsel__sc_tp_uint(evsel, ret, sample); + ret = perf_evsel__sc_tp_uint(ret, sample); if (trace->summary) thread__update_stats(thread, ttrace, id, sample, ret, trace); @@ -3000,7 +3000,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, if (sample->callchain) { struct callchain_cursor *cursor = get_tls_callchain_cursor(); - callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor); + callchain_ret = trace__resolve_callchain(trace, sample, cursor); if (callchain_ret == 0) { if (cursor->nr < trace->min_stack) goto out; @@ -3217,9 +3217,10 @@ static void bpf_output__fprintf(struct trace *trace, ++trace->nr_events_printed; } -static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, struct perf_sample *sample, +static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample *sample, struct thread *thread, void *augmented_args, int augmented_args_size) { + struct evsel *evsel = sample->evsel; char bf[2048]; size_t size = sizeof(bf); const struct tep_event *tp_format = evsel__tp_format(evsel); @@ -3302,7 +3303,7 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, if (sample->callchain) { struct callchain_cursor *cursor = get_tls_callchain_cursor(); - callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor); + callchain_ret = trace__resolve_callchain(trace, sample, cursor); if (callchain_ret == 0) { if (cursor->nr < trace->min_stack) goto out; @@ -3323,7 +3324,7 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, trace__fprintf_comm_tid(trace, thread, trace->output); if (evsel == trace->syscalls.events.bpf_output) { - int id = perf_evsel__sc_tp_uint(evsel, id, sample); + int id = perf_evsel__sc_tp_uint(id, sample); int e_machine = thread ? thread__e_machine(thread, trace->host, /*e_flags=*/NULL) : EM_HOST; @@ -3331,7 +3332,7 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, if (sc) { fprintf(trace->output, "%s(", sc->name); - trace__fprintf_sys_enter(trace, evsel, sample); + trace__fprintf_sys_enter(trace, sample); fputc(')', trace->output); goto newline; } @@ -3351,13 +3352,13 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, const struct tep_event *tp_format = evsel__tp_format(evsel); if (tp_format && (strncmp(tp_format->name, "sys_enter_", 10) || - trace__fprintf_sys_enter(trace, evsel, sample))) { + trace__fprintf_sys_enter(trace, sample))) { if (trace->libtraceevent_print) { event_format__fprintf(tp_format, sample->cpu, sample->raw_data, sample->raw_size, trace->output); } else { - trace__fprintf_tp_fields(trace, evsel, sample, thread, NULL, 0); + trace__fprintf_tp_fields(trace, sample, thread, NULL, 0); } } } @@ -3416,7 +3417,7 @@ static int trace__pgfault(struct trace *trace, if (sample->callchain) { struct callchain_cursor *cursor = get_tls_callchain_cursor(); - callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor); + callchain_ret = trace__resolve_callchain(trace, sample, cursor); if (callchain_ret == 0) { if (cursor->nr < trace->min_stack) goto out_put; From 859e49597088b445173ed45cf4c4a44aff5e3f5a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:11 -0700 Subject: [PATCH 1279/5214] perf callchain: Don't pass evsel and sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from callchain-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 2 +- tools/perf/builtin-inject.c | 4 ++-- tools/perf/builtin-kmem.c | 6 ++--- tools/perf/builtin-kwork.c | 5 ++-- tools/perf/builtin-lock.c | 24 +++++++++---------- tools/perf/builtin-sched.c | 5 ++-- tools/perf/builtin-script.c | 6 ++--- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 2 +- tools/perf/util/callchain.c | 8 +++---- tools/perf/util/callchain.h | 6 ++--- tools/perf/util/db-export.c | 8 +++---- tools/perf/util/hist.c | 2 +- tools/perf/util/machine.c | 14 +++++------ tools/perf/util/machine.h | 3 --- .../util/scripting-engines/trace-event-perl.c | 4 ++-- .../scripting-engines/trace-event-python.c | 4 ++-- 17 files changed, 48 insertions(+), 57 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 7593e5908fcc..2fa3d7ec8a09 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -339,7 +339,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, cursor = get_tls_callchain_cursor(); ret = sample__resolve_callchain(sample, cursor, NULL, - evsel, &al, sysctl_perf_event_max_stack); + &al, sysctl_perf_event_max_stack); if (ret) goto out; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 1d245d509b20..92ce2be1e5e2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -521,7 +521,7 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, goto out; /* this will parse DWARF using stack and register data */ - ret = thread__resolve_callchain(thread, cursor, evsel, sample, + ret = thread__resolve_callchain(thread, cursor, sample, /*parent=*/NULL, /*root_al=*/NULL, PERF_MAX_STACK_DEPTH); thread__put(thread); @@ -1130,7 +1130,7 @@ static int perf_event__inject_buildid(const struct perf_tool *tool, union perf_e /*sample_in_dso=*/true); } - sample__for_each_callchain_node(thread, sample->evsel, sample, PERF_MAX_STACK_DEPTH, + sample__for_each_callchain_node(thread, sample, PERF_MAX_STACK_DEPTH, /*symbols=*/false, mark_dso_hit_callback, &args); thread__put(thread); repipe: diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index d7233a8b43be..2cdc56bc2616 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -394,7 +394,7 @@ static int build_alloc_func_list(void) * Find first non-memory allocation function from callchain. * The allocation functions are in the 'alloc_func_list'. */ -static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample) +static u64 find_callsite(struct perf_sample *sample) { struct addr_location al; struct machine *machine = &kmem_session->machines.host; @@ -414,7 +414,7 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample) if (cursor == NULL) goto out; - sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16); + sample__resolve_callchain(sample, cursor, /*parent=*/NULL, &al, 16); callchain_cursor_commit(cursor); while (true) { @@ -838,7 +838,7 @@ static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_samp if (parse_gfp_flags(evsel, sample, gfp_flags) < 0) return -1; - callsite = find_callsite(evsel, sample); + callsite = find_callsite(sample); /* * This is to find the current page (with correct gfp flags and diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index ab4519287f47..4b312afed830 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -688,7 +688,6 @@ static int latency_entry_event(struct perf_kwork *kwork, static void timehist_save_callchain(struct perf_kwork *kwork, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct symbol *sym; @@ -708,7 +707,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork, cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(thread, cursor, evsel, sample, + if (thread__resolve_callchain(thread, cursor, sample, NULL, NULL, kwork->max_stack + 2) != 0) { pr_debug("Failed to resolve callchain, skipping\n"); goto out_put; @@ -838,7 +837,7 @@ static int timehist_entry_event(struct perf_kwork *kwork, return ret; if (work != NULL) - timehist_save_callchain(kwork, sample, evsel, machine); + timehist_save_callchain(kwork, sample, machine); return 0; } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index cbf3a39c7837..e2b585e528ac 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -551,13 +551,13 @@ static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid) return 0; } -static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample); +static u64 callchain_id(struct perf_sample *sample); -static int get_key_by_aggr_mode(u64 *key, u64 addr, struct evsel *evsel, +static int get_key_by_aggr_mode(u64 *key, u64 addr, struct perf_sample *sample) { if (aggr_mode == LOCK_AGGR_CALLER) { - *key = callchain_id(evsel, sample); + *key = callchain_id(sample); return 0; } return get_key_by_aggr_mode_simple(key, addr, sample->tid); @@ -841,7 +841,7 @@ static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip, else return strlcpy(buf, sym->name, size); } -static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample, +static int lock_contention_caller(struct perf_sample *sample, char *buf, int size) { struct thread *thread; @@ -862,7 +862,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl cursor = get_tls_callchain_cursor(); /* use caller function name from the callchain */ - ret = thread__resolve_callchain(thread, cursor, evsel, sample, + ret = thread__resolve_callchain(thread, cursor, sample, NULL, NULL, max_stack_depth); if (ret != 0) { thread__put(thread); @@ -896,7 +896,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl return -1; } -static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample) +static u64 callchain_id(struct perf_sample *sample) { struct callchain_cursor *cursor; struct machine *machine = &session->machines.host; @@ -911,7 +911,7 @@ static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample) cursor = get_tls_callchain_cursor(); /* use caller function name from the callchain */ - ret = thread__resolve_callchain(thread, cursor, evsel, sample, + ret = thread__resolve_callchain(thread, cursor, sample, NULL, NULL, max_stack_depth); thread__put(thread); @@ -963,7 +963,7 @@ static u64 *get_callstack(struct perf_sample *sample, int max_stack) return callstack; } -static int report_lock_contention_begin_event(struct evsel *evsel, +static int report_lock_contention_begin_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; @@ -978,7 +978,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, struct map *kmap; struct symbol *sym; - ret = get_key_by_aggr_mode(&key, addr, evsel, sample); + ret = get_key_by_aggr_mode(&key, addr, sample); if (ret < 0) return ret; @@ -1025,7 +1025,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, break; case LOCK_AGGR_CALLER: name = buf; - if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0) + if (lock_contention_caller(sample, buf, sizeof(buf)) < 0) name = "Unknown"; break; case LOCK_AGGR_CGROUP: @@ -1127,7 +1127,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, return 0; } -static int report_lock_contention_end_event(struct evsel *evsel, +static int report_lock_contention_end_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) { struct lock_stat *ls; @@ -1138,7 +1138,7 @@ static int report_lock_contention_end_event(struct evsel *evsel, u64 key; int ret; - ret = get_key_by_aggr_mode(&key, addr, evsel, sample); + ret = get_key_by_aggr_mode(&key, addr, sample); if (ret < 0) return ret; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 01228f630121..2a971081918b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2325,7 +2325,6 @@ static bool is_idle_sample(struct perf_sample *sample, static void save_task_callchain(struct perf_sched *sched, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct callchain_cursor *cursor; @@ -2345,7 +2344,7 @@ static void save_task_callchain(struct perf_sched *sched, cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(thread, cursor, evsel, sample, + if (thread__resolve_callchain(thread, cursor, sample, NULL, NULL, sched->max_stack + 2) != 0) { if (verbose > 0) pr_err("Failed to resolve callchain. Skipping\n"); @@ -2519,7 +2518,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, sample->tid); } - save_task_callchain(sched, sample, evsel, machine); + save_task_callchain(sched, sample, machine); if (sched->idle_hist) { struct thread *idle; struct idle_thread_runtime *itr; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index d4e8f49a8751..b94a199ffcc9 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1684,7 +1684,7 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, if (symbol_conf.use_callchain && sample->callchain) { cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(al->thread, cursor, evsel, + if (thread__resolve_callchain(al->thread, cursor, sample, NULL, NULL, scripting_max_stack)) cursor = NULL; @@ -2507,7 +2507,7 @@ static void process_event(struct perf_script *script, if (symbol_conf.use_callchain && sample->callchain) { cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(al->thread, cursor, evsel, + if (thread__resolve_callchain(al->thread, cursor, sample, NULL, NULL, scripting_max_stack)) cursor = NULL; @@ -2792,7 +2792,7 @@ static int process_deferred_sample_event(const struct perf_tool *tool, if (symbol_conf.use_callchain && sample->callchain) { cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(al.thread, cursor, evsel, + if (thread__resolve_callchain(al.thread, cursor, sample, NULL, NULL, scripting_max_stack)) { pr_info("cannot resolve deferred callchains\n"); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index eca578212def..4bc30ad8fb07 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2938,7 +2938,7 @@ static int trace__resolve_callchain(struct trace *trace, if (machine__resolve(trace->host, &al, sample) < 0) goto out; - err = thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, max_stack); + err = thread__resolve_callchain(al.thread, cursor, sample, NULL, NULL, max_stack); out: addr_location__exit(&al); return err; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 55b72235f891..af4d874f1381 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -73,7 +73,7 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, addr_location__exit(&al); - sample__for_each_callchain_node(thread, sample->evsel, sample, PERF_MAX_STACK_DEPTH, + sample__for_each_callchain_node(thread, sample, PERF_MAX_STACK_DEPTH, /*symbols=*/false, mark_dso_hit_callback, /*data=*/NULL); diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index f031cbbeeba8..5c2282051e39 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1170,7 +1170,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor, int sample__resolve_callchain(struct perf_sample *sample, struct callchain_cursor *cursor, struct symbol **parent, - struct evsel *evsel, struct addr_location *al, + struct addr_location *al, int max_stack) { if (sample->callchain == NULL && !symbol_conf.show_branchflag_count) @@ -1178,7 +1178,7 @@ int sample__resolve_callchain(struct perf_sample *sample, if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || perf_hpp_list.parent || symbol_conf.show_branchflag_count) { - return thread__resolve_callchain(al->thread, cursor, evsel, sample, + return thread__resolve_callchain(al->thread, cursor, sample, parent, al, max_stack); } return 0; @@ -1853,7 +1853,7 @@ s64 callchain_avg_cycles(struct callchain_node *cnode) return cycles; } -int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, +int sample__for_each_callchain_node(struct thread *thread, struct perf_sample *sample, int max_stack, bool symbols, callchain_iter_fn cb, void *data) { @@ -1864,7 +1864,7 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, return -ENOMEM; /* Fill in the callchain. */ - ret = __thread__resolve_callchain(thread, cursor, evsel, sample, + ret = __thread__resolve_callchain(thread, cursor, sample, /*parent=*/NULL, /*root_al=*/NULL, max_stack, symbols); if (ret) diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index b7702d65ad60..0eb5d7a1a41d 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -8,11 +8,9 @@ #include "branch.h" struct addr_location; -struct evsel; struct hist_entry; struct hists; struct ip_callchain; -struct map; struct perf_sample; struct record_opts; struct thread; @@ -245,7 +243,7 @@ int record_opts__parse_callchain(struct record_opts *record, int sample__resolve_callchain(struct perf_sample *sample, struct callchain_cursor *cursor, struct symbol **parent, - struct evsel *evsel, struct addr_location *al, + struct addr_location *al, int max_stack); int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, @@ -306,7 +304,7 @@ s64 callchain_avg_cycles(struct callchain_node *cnode); typedef int (*callchain_iter_fn)(struct callchain_cursor_node *node, void *data); -int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, +int sample__for_each_callchain_node(struct thread *thread, struct perf_sample *sample, int max_stack, bool symbols, callchain_iter_fn cb, void *data); diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index cc2bb1af4243..d991e3168aed 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -208,8 +208,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, static struct call_path *call_path_from_sample(struct db_export *dbe, struct machine *machine, struct thread *thread, - struct perf_sample *sample, - struct evsel *evsel) + struct perf_sample *sample) { u64 kernel_start = machine__kernel_start(machine); struct call_path *current = &dbe->cpr->call_path; @@ -227,7 +226,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, */ callchain_param.order = ORDER_CALLER; cursor = get_tls_callchain_cursor(); - err = thread__resolve_callchain(thread, cursor, evsel, + err = thread__resolve_callchain(thread, cursor, sample, NULL, NULL, PERF_MAX_STACK_DEPTH); if (err) { callchain_param.order = saved_order; @@ -390,8 +389,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, if (dbe->cpr) { struct call_path *cp = call_path_from_sample(dbe, machine, - thread, sample, - evsel); + thread, sample); if (cp) { db_export__call_path(dbe, cp); es.call_path_id = cp->db_id; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 747fdc455c80..f641cf321ace 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1342,7 +1342,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, alm = map__get(al->map); err = sample__resolve_callchain(iter->sample, get_tls_callchain_cursor(), &iter->parent, - iter->evsel, al, max_stack_depth); + al, max_stack_depth); if (err) { map__put(alm); return err; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e76f8c86e62a..c2e0a99efe97 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2778,13 +2778,13 @@ static u64 get_leaf_frame_caller(struct perf_sample *sample, static int thread__resolve_callchain_sample(struct thread *thread, struct callchain_cursor *cursor, - struct evsel *evsel, struct perf_sample *sample, struct symbol **parent, struct addr_location *root_al, int max_stack, bool symbols) { + struct evsel *evsel = sample->evsel; struct branch_stack *branch = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); struct ip_callchain *chain = sample->callchain; @@ -2986,10 +2986,11 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) static int thread__resolve_callchain_unwind(struct thread *thread, struct callchain_cursor *cursor, - struct evsel *evsel, struct perf_sample *sample, int max_stack, bool symbols) { + struct evsel *evsel = sample->evsel; + /* Can we do dwarf post unwind? */ if (!((evsel->core.attr.sample_type & PERF_SAMPLE_REGS_USER) && (evsel->core.attr.sample_type & PERF_SAMPLE_STACK_USER))) @@ -3009,7 +3010,6 @@ static int thread__resolve_callchain_unwind(struct thread *thread, int __thread__resolve_callchain(struct thread *thread, struct callchain_cursor *cursor, - struct evsel *evsel, struct perf_sample *sample, struct symbol **parent, struct addr_location *root_al, @@ -3025,22 +3025,22 @@ int __thread__resolve_callchain(struct thread *thread, if (callchain_param.order == ORDER_CALLEE) { ret = thread__resolve_callchain_sample(thread, cursor, - evsel, sample, + sample, parent, root_al, max_stack, symbols); if (ret) return ret; ret = thread__resolve_callchain_unwind(thread, cursor, - evsel, sample, + sample, max_stack, symbols); } else { ret = thread__resolve_callchain_unwind(thread, cursor, - evsel, sample, + sample, max_stack, symbols); if (ret) return ret; ret = thread__resolve_callchain_sample(thread, cursor, - evsel, sample, + sample, parent, root_al, max_stack, symbols); } diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 22a42c5825fa..048b24e9bd38 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -187,7 +187,6 @@ struct callchain_cursor; int __thread__resolve_callchain(struct thread *thread, struct callchain_cursor *cursor, - struct evsel *evsel, struct perf_sample *sample, struct symbol **parent, struct addr_location *root_al, @@ -196,7 +195,6 @@ int __thread__resolve_callchain(struct thread *thread, static inline int thread__resolve_callchain(struct thread *thread, struct callchain_cursor *cursor, - struct evsel *evsel, struct perf_sample *sample, struct symbol **parent, struct addr_location *root_al, @@ -204,7 +202,6 @@ static inline int thread__resolve_callchain(struct thread *thread, { return __thread__resolve_callchain(thread, cursor, - evsel, sample, parent, root_al, diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index e261a57b87d4..af0d514b2397 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -257,7 +257,7 @@ static void define_event_symbols(struct tep_event *event, } static SV *perl_process_callchain(struct perf_sample *sample, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct addr_location *al) { struct callchain_cursor *cursor; @@ -272,7 +272,7 @@ static SV *perl_process_callchain(struct perf_sample *sample, cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(al->thread, cursor, evsel, + if (thread__resolve_callchain(al->thread, cursor, sample, NULL, NULL, scripting_max_stack) != 0) { pr_err("Failed to resolve callchain. Skipping\n"); goto exit; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 5a30caaec73e..1537122167d5 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -390,7 +390,7 @@ static unsigned long get_offset(struct symbol *sym, struct addr_location *al) } static PyObject *python_process_callchain(struct perf_sample *sample, - struct evsel *evsel, + struct evsel *evsel __maybe_unused, struct addr_location *al) { PyObject *pylist; @@ -404,7 +404,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample, goto exit; cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(al->thread, cursor, evsel, + if (thread__resolve_callchain(al->thread, cursor, sample, NULL, NULL, scripting_max_stack) != 0) { pr_err("Failed to resolve callchain. Skipping\n"); From 9d491ab9ede2a3ecc573fc77418ccb7f214eefd3 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:12 -0700 Subject: [PATCH 1280/5214] perf lock: Only pass sample to handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The evsel is within the sample and so only the sample needs to be passed. Remove the parameter and fix call site. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 65 ++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index e2b585e528ac..b6533483ba64 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -473,28 +473,22 @@ static struct lock_stat *pop_from_result(void) struct trace_lock_handler { /* it's used on CONFIG_LOCKDEP */ - int (*acquire_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*acquire_event)(struct perf_sample *sample); /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */ - int (*acquired_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*acquired_event)(struct perf_sample *sample); /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */ - int (*contended_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*contended_event)(struct perf_sample *sample); /* it's used on CONFIG_LOCKDEP */ - int (*release_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*release_event)(struct perf_sample *sample); /* it's used when CONFIG_LOCKDEP is off */ - int (*contention_begin_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*contention_begin_event)(struct perf_sample *sample); /* it's used when CONFIG_LOCKDEP is off */ - int (*contention_end_event)(struct evsel *evsel, - struct perf_sample *sample); + int (*contention_end_event)(struct perf_sample *sample); }; static struct lock_seq_stat *get_seq(struct thread_stat *ts, u64 addr) @@ -563,8 +557,7 @@ static int get_key_by_aggr_mode(u64 *key, u64 addr, return get_key_by_aggr_mode_simple(key, addr, sample->tid); } -static int report_lock_acquire_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_acquire_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -638,8 +631,7 @@ static int report_lock_acquire_event(struct evsel *evsel __maybe_unused, return 0; } -static int report_lock_acquired_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_acquired_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -704,8 +696,7 @@ static int report_lock_acquired_event(struct evsel *evsel __maybe_unused, return 0; } -static int report_lock_contended_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_contended_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -762,8 +753,7 @@ static int report_lock_contended_event(struct evsel *evsel __maybe_unused, return 0; } -static int report_lock_release_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_release_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -963,8 +953,7 @@ static u64 *get_callstack(struct perf_sample *sample, int max_stack) return callstack; } -static int report_lock_contention_begin_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_contention_begin_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -1127,8 +1116,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel __maybe_unused return 0; } -static int report_lock_contention_end_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int report_lock_contention_end_event(struct perf_sample *sample) { struct lock_stat *ls; struct thread_stat *ts; @@ -1208,45 +1196,45 @@ static struct trace_lock_handler contention_lock_ops = { static struct trace_lock_handler *trace_handler; -static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_lock_acquire(struct perf_sample *sample) { if (trace_handler->acquire_event) - return trace_handler->acquire_event(evsel, sample); + return trace_handler->acquire_event(sample); return 0; } -static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_lock_acquired(struct perf_sample *sample) { if (trace_handler->acquired_event) - return trace_handler->acquired_event(evsel, sample); + return trace_handler->acquired_event(sample); return 0; } -static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_lock_contended(struct perf_sample *sample) { if (trace_handler->contended_event) - return trace_handler->contended_event(evsel, sample); + return trace_handler->contended_event(sample); return 0; } -static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_lock_release(struct perf_sample *sample) { if (trace_handler->release_event) - return trace_handler->release_event(evsel, sample); + return trace_handler->release_event(sample); return 0; } -static int evsel__process_contention_begin(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_contention_begin(struct perf_sample *sample) { if (trace_handler->contention_begin_event) - return trace_handler->contention_begin_event(evsel, sample); + return trace_handler->contention_begin_event(sample); return 0; } -static int evsel__process_contention_end(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_contention_end(struct perf_sample *sample) { if (trace_handler->contention_end_event) - return trace_handler->contention_end_event(evsel, sample); + return trace_handler->contention_end_event(sample); return 0; } @@ -1424,8 +1412,7 @@ static int process_event_update(const struct perf_tool *tool, return 0; } -typedef int (*tracepoint_handler)(struct evsel *evsel, - struct perf_sample *sample); +typedef int (*tracepoint_handler)(struct perf_sample *sample); static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -1445,7 +1432,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - err = f(evsel, sample); + err = f(sample); } thread__put(thread); From 05e1a8077146e8c7385565d36a47e66b88d1090b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:13 -0700 Subject: [PATCH 1281/5214] perf hist: Remove evsel parameter from inc samples functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from hist-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 7 +++---- tools/perf/builtin-c2c.c | 2 +- tools/perf/builtin-report.c | 18 ++++++++---------- tools/perf/builtin-top.c | 6 +++--- tools/perf/util/annotate.c | 19 +++++++++---------- tools/perf/util/annotate.h | 6 ++---- 6 files changed, 26 insertions(+), 32 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 58e56f826367..ee1ba2dc35f4 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -176,16 +176,15 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter, struct hist_entry *he = iter->he; struct branch_info *bi; struct perf_sample *sample = iter->sample; - struct evsel *evsel = iter->evsel; int err; bi = he->branch_info; - err = addr_map_symbol__inc_samples(&bi->from, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->from, sample); if (err) goto out; - err = addr_map_symbol__inc_samples(&bi->to, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->to, sample); out: return err; @@ -275,7 +274,7 @@ static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, if (he == NULL) return -ENOMEM; - ret = hist_entry__inc_addr_samples(he, sample, evsel, al->addr); + ret = hist_entry__inc_addr_samples(he, sample, al->addr); hists__inc_nr_samples(hists, true); return ret; } diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 2fa3d7ec8a09..36f386949923 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -371,7 +371,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, if (perf_c2c__has_annotation(NULL)) { perf_c2c__evsel_hists_inc_stats(evsel, he, sample); - addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel); + addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample); } ret = hist_entry__append_callchain(he, sample); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 9955ce8cce00..cc3ee712fe62 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -171,7 +171,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter, int err = 0; struct report *rep = arg; struct hist_entry *he = iter->he; - struct evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; struct mem_info *mi; struct branch_info *bi; @@ -181,25 +180,25 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter, if (sort__mode == SORT_MODE__BRANCH) { bi = he->branch_info; - err = addr_map_symbol__inc_samples(&bi->from, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->from, sample); if (err) goto out; - err = addr_map_symbol__inc_samples(&bi->to, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->to, sample); } else if (rep->mem_mode) { mi = he->mem_info; - err = addr_map_symbol__inc_samples(mem_info__daddr(mi), sample, evsel); + err = addr_map_symbol__inc_samples(mem_info__daddr(mi), sample); if (err) goto out; - err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr); + err = hist_entry__inc_addr_samples(he, sample, al->addr); } else if (symbol_conf.cumulate_callchain) { if (single) - err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr); + err = hist_entry__inc_addr_samples(he, sample, al->addr); } else { - err = hist_entry__inc_addr_samples(he, sample, evsel, al->addr); + err = hist_entry__inc_addr_samples(he, sample, al->addr); } out: @@ -215,7 +214,6 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter, struct report *rep = arg; struct branch_info *bi = he->branch_info; struct perf_sample *sample = iter->sample; - struct evsel *evsel = iter->evsel; int err; branch_type_count(&rep->brtype_stat, &bi->flags, @@ -224,11 +222,11 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter, if (!ui__has_annotation() && !rep->symbol_ipc) return 0; - err = addr_map_symbol__inc_samples(&bi->from, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->from, sample); if (err) goto out; - err = addr_map_symbol__inc_samples(&bi->to, sample, evsel); + err = addr_map_symbol__inc_samples(&bi->to, sample); out: return err; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f6eb543de537..b4fc991b4eeb 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -199,7 +199,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip) static void perf_top__record_precise_ip(struct perf_top *top, struct hist_entry *he, struct perf_sample *sample, - struct evsel *evsel, u64 ip) + u64 ip) EXCLUSIVE_LOCKS_REQUIRED(he->hists->lock) { struct annotation *notes; @@ -216,7 +216,7 @@ static void perf_top__record_precise_ip(struct perf_top *top, if (!annotation__trylock(notes)) return; - err = hist_entry__inc_addr_samples(he, sample, evsel, ip); + err = hist_entry__inc_addr_samples(he, sample, ip); annotation__unlock(notes); @@ -735,7 +735,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter, struct evsel *evsel = iter->evsel; if (perf_hpp_list.sym && single) - perf_top__record_precise_ip(top, iter->he, iter->sample, evsel, al->addr); + perf_top__record_precise_ip(top, iter->he, iter->sample, al->addr); hist__account_cycles(iter->sample->branch_stack, al, iter->sample, !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY), diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e745f3034a0e..470569745abe 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -213,9 +213,10 @@ static int __symbol__account_cycles(struct cyc_hist *ch, } static int __symbol__inc_addr_samples(struct map_symbol *ms, - struct annotated_source *src, struct evsel *evsel, u64 addr, + struct annotated_source *src, u64 addr, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; struct symbol *sym = ms->sym; long hash_key; u64 offset; @@ -318,7 +319,7 @@ struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists) } static int symbol__inc_addr_samples(struct map_symbol *ms, - struct evsel *evsel, u64 addr, + u64 addr, struct perf_sample *sample) { struct symbol *sym = ms->sym; @@ -326,8 +327,8 @@ static int symbol__inc_addr_samples(struct map_symbol *ms, if (sym == NULL) return 0; - src = symbol__hists(sym, evsel->evlist->core.nr_entries); - return src ? __symbol__inc_addr_samples(ms, src, evsel, addr, sample) : 0; + src = symbol__hists(sym, sample->evsel->evlist->core.nr_entries); + return src ? __symbol__inc_addr_samples(ms, src, addr, sample) : 0; } static int symbol__account_br_cntr(struct annotated_branch *branch, @@ -581,16 +582,14 @@ static int annotation__compute_ipc(struct annotation *notes, size_t size, return 0; } -int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, - struct evsel *evsel) +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample) { - return symbol__inc_addr_samples(&ams->ms, evsel, ams->al_addr, sample); + return symbol__inc_addr_samples(&ams->ms, ams->al_addr, sample); } -int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, - struct evsel *evsel, u64 ip) +int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, u64 ip) { - return symbol__inc_addr_samples(&he->ms, evsel, ip, sample); + return symbol__inc_addr_samples(&he->ms, ip, sample); } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 696e36dbf013..1aa6df7d1618 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -422,8 +422,7 @@ static inline struct annotation *symbol__annotation(struct symbol *sym) return (void *)sym - symbol_conf.priv_size; } -int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, - struct evsel *evsel); +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample); struct annotated_branch *annotation__get_branch(struct annotation *notes); @@ -433,8 +432,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, struct evsel *evsel, u64 br_cntr); -int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, - struct evsel *evsel, u64 addr); +int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, u64 addr); struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists); void symbol__annotate_zero_histograms(struct symbol *sym); From 948b5874ef5e6e5c3d9eeb62df52472cedc23b04 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:14 -0700 Subject: [PATCH 1282/5214] perf db-export: Remove evsel from struct export_sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from db-export-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/db-export.c | 5 ++--- tools/perf/util/db-export.h | 3 +-- tools/perf/util/scripting-engines/trace-event-python.c | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index d991e3168aed..ba54b1119ee6 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -344,14 +344,13 @@ static int db_export__threads(struct db_export *dbe, struct thread *thread, } int db_export__sample(struct db_export *dbe, union perf_event *event, - struct perf_sample *sample, struct evsel *evsel, + struct perf_sample *sample, struct addr_location *al, struct addr_location *addr_al) { struct thread *thread = al->thread; struct export_sample es = { .event = event, .sample = sample, - .evsel = evsel, .al = al, }; struct thread *main_thread; @@ -364,7 +363,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, if (!machine) return -1; - err = db_export__evsel(dbe, evsel); + err = db_export__evsel(dbe, sample->evsel); if (err) return err; diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h index 23983cb35706..1abbfd398e3a 100644 --- a/tools/perf/util/db-export.h +++ b/tools/perf/util/db-export.h @@ -25,7 +25,6 @@ struct call_return; struct export_sample { union perf_event *event; struct perf_sample *sample; - struct evsel *evsel; struct addr_location *al; u64 db_id; u64 comm_db_id; @@ -96,7 +95,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym, int db_export__branch_type(struct db_export *dbe, u32 branch_type, const char *name); int db_export__sample(struct db_export *dbe, union perf_event *event, - struct perf_sample *sample, struct evsel *evsel, + struct perf_sample *sample, struct addr_location *al, struct addr_location *addr_al); int db_export__branch_types(struct db_export *dbe); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 1537122167d5..9e1069ec0578 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1312,7 +1312,7 @@ static void python_export_sample_table(struct db_export *dbe, t = tuple_new(28); tuple_set_d64(t, 0, es->db_id); - tuple_set_d64(t, 1, es->evsel->db_id); + tuple_set_d64(t, 1, es->sample->evsel->db_id); tuple_set_d64(t, 2, maps__machine(thread__maps(es->al->thread))->db_id); tuple_set_d64(t, 3, thread__db_id(es->al->thread)); tuple_set_d64(t, 4, es->comm_db_id); @@ -1353,7 +1353,7 @@ static void python_export_synth(struct db_export *dbe, struct export_sample *es) t = tuple_new(3); tuple_set_d64(t, 0, es->db_id); - tuple_set_d64(t, 1, es->evsel->core.attr.config); + tuple_set_d64(t, 1, es->sample->evsel->core.attr.config); tuple_set_bytes(t, 2, es->sample->raw_data, es->sample->raw_size); call_object(tables->synth_handler, t, "synth_data"); @@ -1368,7 +1368,7 @@ static int python_export_sample(struct db_export *dbe, python_export_sample_table(dbe, es); - if (es->evsel->core.attr.type == PERF_TYPE_SYNTH && tables->synth_handler) + if (es->sample->evsel->core.attr.type == PERF_TYPE_SYNTH && tables->synth_handler) python_export_synth(dbe, es); return 0; @@ -1517,7 +1517,7 @@ static void python_process_event(union perf_event *event, /* Reserve for future process_hw/sw/raw APIs */ default: if (tables->db_export_mode) - db_export__sample(&tables->dbe, event, sample, evsel, al, addr_al); + db_export__sample(&tables->dbe, event, sample, al, addr_al); else python_process_general_event(sample, evsel, al, addr_al); } From 7e0e6ac66cc44ab06ba70b28cdfabdbef8538c29 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:15 -0700 Subject: [PATCH 1283/5214] perf hist: Remove evsel from struct hist_entry_iter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from hist-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 10 ++++------ tools/perf/builtin-diff.c | 3 +-- tools/perf/builtin-report.c | 6 ++---- tools/perf/builtin-top.c | 9 +++------ tools/perf/tests/hists_cumulate.c | 1 - tools/perf/tests/hists_filter.c | 1 - tools/perf/tests/hists_output.c | 1 - tools/perf/util/hist.c | 24 ++++++++++++------------ tools/perf/util/hist.h | 3 +-- 9 files changed, 23 insertions(+), 35 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ee1ba2dc35f4..6f8be9ead43b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -190,14 +190,12 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter, return err; } -static int process_branch_callback(struct evsel *evsel, - struct perf_sample *sample, +static int process_branch_callback(struct perf_sample *sample, struct addr_location *al, struct perf_annotate *ann, struct machine *machine) { struct hist_entry_iter iter = { - .evsel = evsel, .sample = sample, .add_entry_cb = hist_iter__branch_callback, .hide_unresolved = symbol_conf.hide_unresolved, @@ -220,8 +218,8 @@ static int process_branch_callback(struct evsel *evsel, if (a.map != NULL) dso__set_hit(map__dso(a.map)); - hist__account_cycles(sample->branch_stack, al, sample, false, - NULL, evsel); + hist__account_cycles(sample->branch_stack, al, sample, /*nonany_branch_mode=*/false, + /*total_cycles=*/NULL); ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann); out: @@ -268,7 +266,7 @@ static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, process_branch_stack(sample->branch_stack, al, sample); if (ann->has_br_stack && has_annotation(ann)) - return process_branch_callback(evsel, sample, al, ann, machine); + return process_branch_callback(sample, al, ann, machine); he = hists__add_entry(hists, al, NULL, NULL, NULL, NULL, sample, true); if (he == NULL) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 82e9e514922b..b4ff863b304c 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -397,7 +397,6 @@ static int diff__process_sample_event(const struct perf_tool *tool, struct evsel *evsel = sample->evsel; struct hists *hists = evsel__hists(evsel); struct hist_entry_iter iter = { - .evsel = evsel, .sample = sample, .ops = &hist_iter_normal, }; @@ -431,7 +430,7 @@ static int diff__process_sample_event(const struct perf_tool *tool, } hist__account_cycles(sample->branch_stack, &al, sample, - false, NULL, evsel); + /*nonany_branch_mode=*/false, /*total_cycles=*/NULL); break; case COMPUTE_STREAM: diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cc3ee712fe62..11c7eff92b95 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -266,10 +266,8 @@ static int process_sample_event(const struct perf_tool *tool, struct machine *machine) { struct report *rep = container_of(tool, struct report, tool); - struct evsel *evsel = sample->evsel; struct addr_location al; struct hist_entry_iter iter = { - .evsel = evsel, .sample = sample, .hide_unresolved = symbol_conf.hide_unresolved, .add_entry_cb = hist_iter__report_callback, @@ -281,7 +279,7 @@ static int process_sample_event(const struct perf_tool *tool, return 0; } - if (evswitch__discard(&rep->evswitch, evsel)) + if (evswitch__discard(&rep->evswitch, sample->evsel)) return 0; addr_location__init(&al); @@ -325,7 +323,7 @@ static int process_sample_event(const struct perf_tool *tool, if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) { hist__account_cycles(sample->branch_stack, &al, sample, rep->nonany_branch_mode, - &rep->total_cycles, evsel); + &rep->total_cycles); } rep->total_samples++; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b4fc991b4eeb..6cf73bb0c7af 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -732,20 +732,18 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter, EXCLUSIVE_LOCKS_REQUIRED(iter->he->hists->lock) { struct perf_top *top = arg; - struct evsel *evsel = iter->evsel; if (perf_hpp_list.sym && single) perf_top__record_precise_ip(top, iter->he, iter->sample, al->addr); hist__account_cycles(iter->sample->branch_stack, al, iter->sample, !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY), - NULL, evsel); + /*total_cycles=*/NULL); return 0; } static void perf_event__process_sample(const struct perf_tool *tool, const union perf_event *event, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -831,9 +829,8 @@ static void perf_event__process_sample(const struct perf_tool *tool, } if (al.sym == NULL || !al.sym->idle) { - struct hists *hists = evsel__hists(evsel); + struct hists *hists = evsel__hists(sample->evsel); struct hist_entry_iter iter = { - .evsel = evsel, .sample = sample, .add_entry_cb = hist_iter__top_callback, }; @@ -1211,7 +1208,7 @@ static int deliver_event(struct ordered_events *qe, } if (event->header.type == PERF_RECORD_SAMPLE) { - perf_event__process_sample(&top->tool, event, evsel, + perf_event__process_sample(&top->tool, event, &sample, machine); } else if (event->header.type == PERF_RECORD_LOST) { perf_top__process_lost(top, event, evsel); diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 606aa926a8fc..267cbc24691a 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -87,7 +87,6 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) addr_location__init(&al); for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { struct hist_entry_iter iter = { - .evsel = evsel, .sample = &sample, .hide_unresolved = false, }; diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index cc6b26e373d1..002e3a4c1ca5 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -63,7 +63,6 @@ static int add_hist_entries(struct evlist *evlist, evlist__for_each_entry(evlist, evsel) { for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { struct hist_entry_iter iter = { - .evsel = evsel, .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index 7818950d786e..fa683fd7b1e5 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -57,7 +57,6 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) addr_location__init(&al); for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { struct hist_entry_iter iter = { - .evsel = evsel, .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f641cf321ace..811d68fa6770 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -935,8 +935,8 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al { u64 cost; struct mem_info *mi = iter->mi; - struct hists *hists = evsel__hists(iter->evsel); struct perf_sample *sample = iter->sample; + struct hists *hists = evsel__hists(sample->evsel); struct hist_entry *he; if (mi == NULL) @@ -968,7 +968,7 @@ static int iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { - struct evsel *evsel = iter->evsel; + struct evsel *evsel = iter->sample->evsel; struct hists *hists = evsel__hists(evsel); struct hist_entry *he = iter->he; int err = -EINVAL; @@ -1036,9 +1036,9 @@ static int iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) { struct branch_info *bi; - struct evsel *evsel = iter->evsel; - struct hists *hists = evsel__hists(evsel); struct perf_sample *sample = iter->sample; + struct evsel *evsel = sample->evsel; + struct hists *hists = evsel__hists(evsel); struct hist_entry *he = NULL; int i = iter->curr; int err = 0; @@ -1078,7 +1078,7 @@ static int iter_finish_branch_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { - struct evsel *evsel = iter->evsel; + struct evsel *evsel = iter->sample->evsel; struct hists *hists = evsel__hists(evsel); for (int i = 0; i < iter->total; i++) @@ -1103,8 +1103,8 @@ iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused, static int iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; + struct evsel *evsel = sample->evsel; struct hist_entry *he; he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, @@ -1121,8 +1121,8 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { struct hist_entry *he = iter->he; - struct evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; + struct evsel *evsel = sample->evsel; if (he == NULL) return 0; @@ -1165,9 +1165,9 @@ static int iter_add_single_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct evsel *evsel = iter->evsel; - struct hists *hists = evsel__hists(evsel); struct perf_sample *sample = iter->sample; + struct evsel *evsel = sample->evsel; + struct hists *hists = evsel__hists(evsel); struct hist_entry **he_cache = iter->he_cache; struct hist_entry *he; int err = 0; @@ -1224,8 +1224,8 @@ static int iter_add_next_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; + struct evsel *evsel = sample->evsel; struct hist_entry **he_cache = iter->he_cache; struct hist_entry *he; struct hist_entry he_tmp = { @@ -2826,7 +2826,7 @@ int hists__unlink(struct hists *hists) void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, struct perf_sample *sample, bool nonany_branch_mode, - u64 *total_cycles, struct evsel *evsel) + u64 *total_cycles) { struct branch_info *bi; struct branch_entry *entries = perf_sample__branch_entries(sample); @@ -2850,7 +2850,7 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, for (int i = bs->nr - 1; i >= 0; i--) { addr_map_symbol__account_cycles(&bi[i].from, nonany_branch_mode ? NULL : prev, - bi[i].flags.cycles, evsel, + bi[i].flags.cycles, sample->evsel, bi[i].branch_stack_cntr); prev = &bi[i].to; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d97a4efb9250..8fb89d81ef06 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -156,7 +156,6 @@ struct hist_entry_iter { int total; int curr; - struct evsel *evsel; struct perf_sample *sample; struct hist_entry *he; struct symbol *parent; @@ -799,7 +798,7 @@ unsigned int hists__overhead_width(struct hists *hists); void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, struct perf_sample *sample, bool nonany_branch_mode, - u64 *total_cycles, struct evsel *evsel); + u64 *total_cycles); struct option; int parse_filter_percentage(const struct option *opt, const char *arg, int unset); From a8cb37a5b050598234d8da0790df0aef5f6acf31 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:16 -0700 Subject: [PATCH 1284/5214] perf report: Directly use sample->evsel to avoid computing from sample->id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In count_lost_samples_events try to avoid searching for the evsel for the sample, just use the variable within the sample. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 11c7eff92b95..973d97af8501 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -789,9 +789,11 @@ static int count_lost_samples_event(const struct perf_tool *tool, struct machine *machine __maybe_unused) { struct report *rep = container_of(tool, struct report, tool); - struct evsel *evsel; + struct evsel *evsel = sample->evsel; + + if (!evsel) + evsel = evlist__id2evsel(rep->session->evlist, sample->id); - evsel = evlist__id2evsel(rep->session->evlist, sample->id); if (evsel) { struct hists *hists = evsel__hists(evsel); u32 count = event->lost_samples.lost; From 603495ab589dc94eecf7f438779a1c8080bc9257 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:17 -0700 Subject: [PATCH 1285/5214] perf annotate: Don't pass evsel to add_sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from annotate-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6f8be9ead43b..719b36d4eed5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -232,11 +232,11 @@ static bool has_annotation(struct perf_annotate *ann) return ui__has_annotation() || ann->use_stdio2; } -static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, - struct addr_location *al, struct perf_annotate *ann, - struct machine *machine) +static int add_sample(struct perf_sample *sample, + struct addr_location *al, struct perf_annotate *ann, + struct machine *machine) { - struct hists *hists = evsel__hists(evsel); + struct hists *hists = evsel__hists(sample->evsel); struct hist_entry *he; int ret; @@ -298,7 +298,7 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; if (!al.filtered && - evsel__add_sample(sample->evsel, sample, &al, ann, machine)) { + add_sample(sample, &al, ann, machine)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); ret = -1; From ed9475b215d5fbe8412fb9c52cf34dd6a3f2f350 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:18 -0700 Subject: [PATCH 1286/5214] perf inject: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from inject-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 52 ++++++++++++++++-------------- tools/perf/util/synthetic-events.c | 30 ++++++++--------- tools/perf/util/synthetic-events.h | 2 -- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 92ce2be1e5e2..41a3721a194d 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -147,14 +147,12 @@ struct event_entry { static int tool__inject_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, - const struct evsel *evsel, __u16 misc, const char *filename, struct dso *dso, u32 flags); static int tool__inject_mmap2_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, - const struct evsel *evsel, __u16 misc, __u32 pid, __u32 tid, __u64 start, __u64 len, __u64 pgoff, @@ -397,7 +395,6 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, typedef int (*inject_handler)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine); static int perf_event__repipe_sample(const struct perf_tool *tool, @@ -413,7 +410,7 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, if (evsel->handler) { inject_handler f = evsel->handler; - return f(tool, event, sample, evsel, machine); + return f(tool, event, sample, machine); } build_id__mark_dso_hit(tool, event, sample, machine); @@ -696,11 +693,12 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, } if (dso && !dso__hit(dso)) { - struct evsel *evsel = evlist__event2evsel(inject->session->evlist, event); + if (!sample->evsel) + sample->evsel = evlist__event2evsel(inject->session->evlist, event); - if (evsel) { + if (sample->evsel) { dso__set_hit(dso); - tool__inject_build_id(tool, sample, machine, evsel, + tool__inject_build_id(tool, sample, machine, /*misc=*/sample->cpumode, filename, dso, flags); } @@ -727,23 +725,26 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, } if ((inject->build_id_style == BID_RWS__MMAP2_BUILDID_ALL) && !(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { - struct evsel *evsel = evlist__event2evsel(inject->session->evlist, event); + struct evsel *saved_evsel = sample->evsel; - if (evsel && !dso_sought) { + sample->evsel = evlist__event2evsel(inject->session->evlist, event); + if (sample->evsel && !dso_sought) { dso = findnew_dso(pid, tid, filename, dso_id, machine); dso_sought = true; } - if (evsel && dso && - !tool__inject_mmap2_build_id(tool, sample, machine, evsel, + if (sample->evsel && dso && + !tool__inject_mmap2_build_id(tool, sample, machine, sample->cpumode | PERF_RECORD_MISC_MMAP_BUILD_ID, pid, tid, start, len, pgoff, dso, prot, flags, filename)) { /* Injected mmap2 so no need to repipe. */ + sample->evsel = saved_evsel; dso__put(dso); return 0; } + sample->evsel = saved_evsel; } dso__put(dso); if (inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) @@ -948,7 +949,6 @@ static bool perf_inject__lookup_known_build_id(struct perf_inject *inject, static int tool__inject_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, - const struct evsel *evsel, __u16 misc, const char *filename, struct dso *dso, u32 flags) @@ -972,7 +972,7 @@ static int tool__inject_build_id(const struct perf_tool *tool, err = perf_event__synthesize_build_id(tool, sample, machine, perf_event__repipe, - evsel, misc, dso__bid(dso), + misc, dso__bid(dso), filename); if (err) { pr_err("Can't synthesize build_id event for %s\n", filename); @@ -985,7 +985,6 @@ static int tool__inject_build_id(const struct perf_tool *tool, static int tool__inject_mmap2_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, - const struct evsel *evsel, __u16 misc, __u32 pid, __u32 tid, __u64 start, __u64 len, __u64 pgoff, @@ -1008,7 +1007,6 @@ static int tool__inject_mmap2_build_id(const struct perf_tool *tool, err = perf_event__synthesize_mmap2_build_id(tool, sample, machine, perf_event__repipe, - evsel, misc, pid, tid, start, len, pgoff, dso__bid(dso), @@ -1025,7 +1023,7 @@ static int mark_dso_hit(const struct perf_inject *inject, const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, - const struct evsel *mmap_evsel, + struct evsel *mmap_evsel, struct map *map, bool sample_in_dso) { struct dso *dso; @@ -1053,9 +1051,13 @@ static int mark_dso_hit(const struct perf_inject *inject, dso = map__dso(map); if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { if (dso && !dso__hit(dso)) { + /* + * The sample is just read for identifiers which we want + * to match the for the event of the sample. + */ dso__set_hit(dso); tool__inject_build_id(tool, sample, machine, - mmap_evsel, misc, dso__long_name(dso), dso, + misc, dso__long_name(dso), dso, map__flags(map)); } } else if (inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) { @@ -1063,11 +1065,13 @@ static int mark_dso_hit(const struct perf_inject *inject, const struct build_id null_bid = { .size = 0 }; const struct build_id *bid = dso ? dso__bid(dso) : &null_bid; const char *filename = dso ? dso__long_name(dso) : ""; + struct evsel *saved_evsel = sample->evsel; map__set_hit(map); + /* Creating a new mmap2 event which has an evsel for the mmap event. */ + sample->evsel = mmap_evsel; perf_event__synthesize_mmap2_build_id(tool, sample, machine, perf_event__repipe, - mmap_evsel, misc, sample->pid, sample->tid, map__start(map), @@ -1077,6 +1081,7 @@ static int mark_dso_hit(const struct perf_inject *inject, map__prot(map), map__flags(map), filename); + sample->evsel = saved_evsel; } } return 0; @@ -1087,7 +1092,7 @@ struct mark_dso_hit_args { const struct perf_tool *tool; struct perf_sample *sample; struct machine *machine; - const struct evsel *mmap_evsel; + struct evsel *mmap_evsel; }; static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) @@ -1142,7 +1147,6 @@ static int perf_event__inject_buildid(const struct perf_tool *tool, union perf_e static int perf_inject__sched_process_exit(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel __maybe_unused, struct machine *machine __maybe_unused) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); @@ -1162,13 +1166,12 @@ static int perf_inject__sched_process_exit(const struct perf_tool *tool, static int perf_inject__sched_switch(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); struct event_entry *ent; - perf_inject__sched_process_exit(tool, event, sample, evsel, machine); + perf_inject__sched_process_exit(tool, event, sample, machine); ent = malloc(event->header.size + sizeof(struct event_entry)); if (ent == NULL) { @@ -1187,13 +1190,13 @@ static int perf_inject__sched_switch(const struct perf_tool *tool, static int perf_inject__sched_stat(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct event_entry *ent; union perf_event *event_sw; struct perf_sample sample_sw; struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct evsel *evsel = sample->evsel; u32 pid = perf_sample__intval(sample, "pid"); int ret; @@ -1559,7 +1562,7 @@ static int synthesize_build_id(struct perf_inject *inject, struct dso *dso, pid_ dso__set_hit(dso); return perf_event__synthesize_build_id(&inject->tool, &synth_sample, machine, - process_build_id, inject__mmap_evsel(inject), + process_build_id, /*misc=*/synth_sample.cpumode, dso__bid(dso), dso__long_name(dso)); } @@ -2121,7 +2124,6 @@ static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char * static int drop_sample(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, struct machine *machine __maybe_unused) { return 0; diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 2461f25a4d7d..fd1d4c0345d6 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2263,13 +2263,15 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, perf_event__handler_t process, - const struct evsel *evsel, __u16 misc, const struct build_id *bid, const char *filename) { union perf_event ev; size_t len; + u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0; + void *array = &ev; + int ret; len = sizeof(ev.build_id) + strlen(filename) + 1; len = PERF_ALIGN(len, sizeof(u64)); @@ -2286,23 +2288,17 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, ev.build_id.header.size = len; strcpy(ev.build_id.filename, filename); - if (evsel) { - void *array = &ev; - int ret; + array += ev.header.size; + ret = perf_event__synthesize_id_sample(array, sample_type, sample); + if (ret < 0) + return ret; - array += ev.header.size; - ret = perf_event__synthesize_id_sample(array, evsel->core.attr.sample_type, sample); - if (ret < 0) - return ret; - - if (ret & 7) { - pr_err("Bad id sample size %d\n", ret); - return -EINVAL; - } - - ev.header.size += ret; + if (ret & 7) { + pr_err("Bad id sample size %d\n", ret); + return -EINVAL; } + ev.header.size += ret; return process(tool, &ev, sample, machine); } @@ -2310,7 +2306,6 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, perf_event__handler_t process, - const struct evsel *evsel, __u16 misc, __u32 pid, __u32 tid, __u64 start, __u64 len, __u64 pgoff, @@ -2320,6 +2315,7 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, { union perf_event ev; size_t ev_len; + u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0; void *array; int ret; @@ -2350,7 +2346,7 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, array = &ev; array += ev.header.size; - ret = perf_event__synthesize_id_sample(array, evsel->core.attr.sample_type, sample); + ret = perf_event__synthesize_id_sample(array, sample_type, sample); if (ret < 0) return ret; diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index 8c7f49f9ccf5..243115ea61ae 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -50,7 +50,6 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, perf_event__handler_t process, - const struct evsel *evsel, __u16 misc, const struct build_id *bid, const char *filename); @@ -58,7 +57,6 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, perf_event__handler_t process, - const struct evsel *evsel, __u16 misc, __u32 pid, __u32 tid, __u64 start, __u64 len, __u64 pgoff, From 511bcd0ca0fff7c47658eb80faff867830a24148 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:19 -0700 Subject: [PATCH 1287/5214] perf kmem: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from kmem-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 2cdc56bc2616..68a39f56204d 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -171,7 +171,7 @@ static int insert_caller_stat(unsigned long call_site, return 0; } -static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_alloc_event(struct perf_sample *sample) { unsigned long ptr = perf_sample__intval(sample, "ptr"), call_site = perf_sample__intval(sample, "call_site"); @@ -198,7 +198,7 @@ static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *s * If the tracepoint contains the field "node" the tool stats the * cross allocation. */ - if (evsel__field(evsel, "node")) { + if (evsel__field(sample->evsel, "node")) { int node1, node2; node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu}); @@ -243,7 +243,7 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr, return NULL; } -static int evsel__process_free_event(struct evsel *evsel __maybe_unused, struct perf_sample *sample) +static int evsel__process_free_event(struct perf_sample *sample) { unsigned long ptr = perf_sample__intval(sample, "ptr"); struct alloc_stat *s_alloc, *s_caller; @@ -751,8 +751,7 @@ static char *compact_gfp_string(unsigned long gfp_flags) return NULL; } -static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample, - unsigned int gfp_flags) +static int parse_gfp_flags(struct perf_sample *sample, unsigned int gfp_flags) { struct tep_record record = { .cpu = sample->cpu, @@ -773,7 +772,7 @@ static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample, } trace_seq_init(&seq); - tp_format = evsel__tp_format(evsel); + tp_format = evsel__tp_format(sample->evsel); if (tp_format) tep_print_event(tp_format->tep, &seq, &record, "%s", TEP_PRINT_INFO); @@ -805,7 +804,7 @@ static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample, return 0; } -static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample) +static int evsel__process_page_alloc_event(struct perf_sample *sample) { u64 page; unsigned int order = perf_sample__intval(sample, "order"); @@ -835,7 +834,7 @@ static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_samp return 0; } - if (parse_gfp_flags(evsel, sample, gfp_flags) < 0) + if (parse_gfp_flags(sample, gfp_flags) < 0) return -1; callsite = find_callsite(sample); @@ -876,8 +875,7 @@ static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_samp return 0; } -static int evsel__process_page_free_event(struct evsel *evsel __maybe_unused, - struct perf_sample *sample) +static int evsel__process_page_free_event(struct perf_sample *sample) { u64 page; unsigned int order = perf_sample__intval(sample, "order"); @@ -954,8 +952,7 @@ static bool perf_kmem__skip_sample(struct perf_sample *sample) return false; } -typedef int (*tracepoint_handler)(struct evsel *evsel, - struct perf_sample *sample); +typedef int (*tracepoint_handler)(struct perf_sample *sample); static int process_sample_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -973,14 +970,15 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, return -1; } - if (perf_kmem__skip_sample(sample)) + if (perf_kmem__skip_sample(sample)) { return 0; + } dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread__tid(thread)); if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - err = f(evsel, sample); + err = f(sample); } thread__put(thread); From bad8999c68895c151ad7c0d9495e59a88787e6ca Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:20 -0700 Subject: [PATCH 1288/5214] perf kwork: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from kwork-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kwork.c | 74 +++++++++++++------------------------- tools/perf/util/kwork.h | 9 +++-- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 4b312afed830..59a54d11f7fa 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -448,7 +448,6 @@ static int work_push_atom(struct perf_kwork *kwork, struct kwork_class *class, enum kwork_trace_type src_type, enum kwork_trace_type dst_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct kwork_work **ret_work, @@ -458,7 +457,7 @@ static int work_push_atom(struct perf_kwork *kwork, struct kwork_work *work, key; BUG_ON(class->work_init == NULL); - class->work_init(kwork, class, &key, src_type, evsel, sample, machine); + class->work_init(kwork, class, &key, src_type, sample, machine); atom = atom_new(kwork, sample); if (atom == NULL) @@ -507,7 +506,6 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, struct kwork_class *class, enum kwork_trace_type src_type, enum kwork_trace_type dst_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct kwork_work **ret_work) @@ -516,7 +514,7 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, struct kwork_work *work, key; BUG_ON(class->work_init == NULL); - class->work_init(kwork, class, &key, src_type, evsel, sample, machine); + class->work_init(kwork, class, &key, src_type, sample, machine); work = work_findnew(&class->work_root, &key, &kwork->cmp_id); if (ret_work != NULL) @@ -599,18 +597,16 @@ static void report_update_exit_event(struct kwork_work *work, static int report_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int report_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -618,7 +614,7 @@ static int report_exit_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (work == NULL) return -1; @@ -654,18 +650,16 @@ static void latency_update_entry_event(struct kwork_work *work, static int latency_raise_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_RAISE, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int latency_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -673,7 +667,7 @@ static int latency_entry_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; atom = work_pop_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_RAISE, evsel, sample, + KWORK_TRACE_RAISE, sample, machine, &work); if (work == NULL) return -1; @@ -812,18 +806,16 @@ static void timehist_print_event(struct perf_kwork *kwork, static int timehist_raise_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_RAISE, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int timehist_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -831,7 +823,7 @@ static int timehist_entry_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; ret = work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_RAISE, evsel, sample, + KWORK_TRACE_RAISE, sample, machine, &work, true); if (ret) return ret; @@ -844,7 +836,6 @@ static int timehist_entry_event(struct perf_kwork *kwork, static int timehist_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -861,7 +852,7 @@ static int timehist_exit_event(struct perf_kwork *kwork, } atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (work == NULL) { ret = -1; @@ -895,18 +886,16 @@ static void top_update_runtime(struct kwork_work *work, static int top_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int top_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -915,7 +904,7 @@ static int top_exit_event(struct perf_kwork *kwork, struct kwork_atom *atom; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (!work) return -1; @@ -936,7 +925,6 @@ static int top_exit_event(struct perf_kwork *kwork, static int top_sched_switch_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -944,7 +932,7 @@ static int top_sched_switch_event(struct perf_kwork *kwork, struct kwork_work *work; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (!work) return -1; @@ -954,12 +942,11 @@ static int top_sched_switch_event(struct perf_kwork *kwork, atom_del(atom); } - return top_entry_event(kwork, class, evsel, sample, machine); + return top_entry_event(kwork, class, sample, machine); } static struct kwork_class kwork_irq; static int process_irq_handler_entry_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -967,12 +954,11 @@ static int process_irq_handler_entry_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_irq, - evsel, sample, machine); + sample, machine); return 0; } static int process_irq_handler_exit_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -980,7 +966,7 @@ static int process_irq_handler_exit_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_irq, - evsel, sample, machine); + sample, machine); return 0; } @@ -1005,7 +991,6 @@ static void irq_work_init(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1038,7 +1023,6 @@ static struct kwork_class kwork_irq = { static struct kwork_class kwork_softirq; static int process_softirq_raise_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1046,13 +1030,12 @@ static int process_softirq_raise_event(const struct perf_tool *tool, if (kwork->tp_handler->raise_event) return kwork->tp_handler->raise_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } static int process_softirq_entry_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1060,13 +1043,12 @@ static int process_softirq_entry_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } static int process_softirq_exit_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1074,7 +1056,7 @@ static int process_softirq_exit_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } @@ -1133,7 +1115,6 @@ static void softirq_work_init(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1148,7 +1129,7 @@ static void softirq_work_init(struct perf_kwork *kwork, } else { num = perf_sample__intval(sample, "vec"); work->id = num; - work->name = evsel__softirq_name(evsel, num); + work->name = evsel__softirq_name(sample->evsel, num); } } @@ -1169,7 +1150,6 @@ static struct kwork_class kwork_softirq = { static struct kwork_class kwork_workqueue; static int process_workqueue_activate_work_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1177,13 +1157,12 @@ static int process_workqueue_activate_work_event(const struct perf_tool *tool, if (kwork->tp_handler->raise_event) return kwork->tp_handler->raise_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } static int process_workqueue_execute_start_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1191,13 +1170,12 @@ static int process_workqueue_execute_start_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } static int process_workqueue_execute_end_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1205,7 +1183,7 @@ static int process_workqueue_execute_end_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } @@ -1233,7 +1211,6 @@ static void workqueue_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { @@ -1267,7 +1244,6 @@ static struct kwork_class kwork_workqueue = { static struct kwork_class kwork_sched; static int process_sched_switch_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1275,7 +1251,7 @@ static int process_sched_switch_event(const struct perf_tool *tool, if (kwork->tp_handler->sched_switch_event) return kwork->tp_handler->sched_switch_event(kwork, &kwork_sched, - evsel, sample, machine); + sample, machine); return 0; } @@ -1300,7 +1276,6 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1946,7 +1921,6 @@ static int perf_kwork__report(struct perf_kwork *kwork) } typedef int (*tracepoint_handler)(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); @@ -1961,7 +1935,7 @@ static int perf_kwork__process_tracepoint_sample(const struct perf_tool *tool, if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - err = f(tool, evsel, sample, machine); + err = f(tool, sample, machine); } return err; diff --git a/tools/perf/util/kwork.h b/tools/perf/util/kwork.h index db00269b73f2..abf637d44794 100644 --- a/tools/perf/util/kwork.h +++ b/tools/perf/util/kwork.h @@ -157,7 +157,6 @@ struct kwork_class { struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); @@ -167,19 +166,19 @@ struct kwork_class { struct trace_kwork_handler { int (*raise_event)(struct perf_kwork *kwork, - struct kwork_class *class, struct evsel *evsel, + struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*entry_event)(struct perf_kwork *kwork, - struct kwork_class *class, struct evsel *evsel, + struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*exit_event)(struct perf_kwork *kwork, - struct kwork_class *class, struct evsel *evsel, + struct kwork_class *class, struct perf_sample *sample, struct machine *machine); int (*sched_switch_event)(struct perf_kwork *kwork, - struct kwork_class *class, struct evsel *evsel, + struct kwork_class *class, struct perf_sample *sample, struct machine *machine); }; From 1ac10645bb29265048b3e982d0de6b06ccb4cf2d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:21 -0700 Subject: [PATCH 1289/5214] perf sched: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from sched-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 92 ++++++++++++++------------------------ 1 file changed, 34 insertions(+), 58 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 2a971081918b..d984e58c7dbf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -129,21 +129,20 @@ typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); struct perf_sched; struct trace_sched_handler { - int (*switch_event)(struct perf_sched *sched, struct evsel *evsel, - struct perf_sample *sample, struct machine *machine); + int (*switch_event)(struct perf_sched *sched, struct perf_sample *sample, + struct machine *machine); - int (*runtime_event)(struct perf_sched *sched, struct evsel *evsel, - struct perf_sample *sample, struct machine *machine); + int (*runtime_event)(struct perf_sched *sched, struct perf_sample *sample, + struct machine *machine); - int (*wakeup_event)(struct perf_sched *sched, struct evsel *evsel, - struct perf_sample *sample, struct machine *machine); + int (*wakeup_event)(struct perf_sched *sched, struct perf_sample *sample, + struct machine *machine); /* PERF_RECORD_FORK event, not sched_process_fork tracepoint */ int (*fork_event)(struct perf_sched *sched, union perf_event *event, struct machine *machine); int (*migrate_task_event)(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); }; @@ -826,7 +825,7 @@ static void test_calibrations(struct perf_sched *sched) static int replay_wakeup_event(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, + struct perf_sample *sample, struct machine *machine __maybe_unused) { const char *comm = perf_sample__strval(sample, "comm"); @@ -834,7 +833,7 @@ replay_wakeup_event(struct perf_sched *sched, struct task_desc *waker, *wakee; if (verbose > 0) { - printf("sched_wakeup event %p\n", evsel); + printf("sched_wakeup event %p\n", sample->evsel); printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid); } @@ -847,7 +846,6 @@ replay_wakeup_event(struct perf_sched *sched, } static int replay_switch_event(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -861,7 +859,7 @@ static int replay_switch_event(struct perf_sched *sched, s64 delta; if (verbose > 0) - printf("sched_switch event %p\n", evsel); + printf("sched_switch event %p\n", sample->evsel); if (cpu >= MAX_CPUS || cpu < 0) return 0; @@ -1134,7 +1132,6 @@ static void free_work_atoms(struct work_atoms *atoms) } static int latency_switch_event(struct perf_sched *sched, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { @@ -1204,7 +1201,6 @@ static int latency_switch_event(struct perf_sched *sched, } static int latency_runtime_event(struct perf_sched *sched, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { @@ -1239,7 +1235,6 @@ static int latency_runtime_event(struct perf_sched *sched, } static int latency_wakeup_event(struct perf_sched *sched, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { @@ -1300,7 +1295,6 @@ static int latency_wakeup_event(struct perf_sched *sched, } static int latency_migrate_task_event(struct perf_sched *sched, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, struct machine *machine) { @@ -1519,20 +1513,18 @@ static void perf_sched__sort_lat(struct perf_sched *sched) } static int process_sched_wakeup_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); if (sched->tp_handler->wakeup_event) - return sched->tp_handler->wakeup_event(sched, evsel, sample, machine); + return sched->tp_handler->wakeup_event(sched, sample, machine); return 0; } static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { @@ -1626,8 +1618,8 @@ static void print_sched_map(struct perf_sched *sched, struct perf_cpu this_cpu, } } -static int map_switch_event(struct perf_sched *sched, struct evsel *evsel __maybe_unused, - struct perf_sample *sample, struct machine *machine) +static int map_switch_event(struct perf_sched *sched, struct perf_sample *sample, + struct machine *machine) { const u32 next_pid = perf_sample__intval(sample, "next_pid"); const u32 prev_pid = perf_sample__intval(sample, "prev_pid"); @@ -1791,7 +1783,6 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel __mayb } static int process_sched_switch_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1810,21 +1801,20 @@ static int process_sched_switch_event(const struct perf_tool *tool, } if (sched->tp_handler->switch_event) - err = sched->tp_handler->switch_event(sched, evsel, sample, machine); + err = sched->tp_handler->switch_event(sched, sample, machine); sched->curr_pid[this_cpu] = next_pid; return err; } static int process_sched_runtime_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); if (sched->tp_handler->runtime_event) - return sched->tp_handler->runtime_event(sched, evsel, sample, machine); + return sched->tp_handler->runtime_event(sched, sample, machine); return 0; } @@ -1847,20 +1837,18 @@ static int perf_sched__process_fork_event(const struct perf_tool *tool, } static int process_sched_migrate_task_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); if (sched->tp_handler->migrate_task_event) - return sched->tp_handler->migrate_task_event(sched, evsel, sample, machine); + return sched->tp_handler->migrate_task_event(sched, sample, machine); return 0; } typedef int (*tracepoint_handler)(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); @@ -1874,7 +1862,7 @@ static int perf_sched__process_tracepoint_sample(const struct perf_tool *tool __ if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - err = f(tool, evsel, sample, machine); + err = f(tool, sample, machine); } return err; @@ -2313,11 +2301,10 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, r->total_pre_mig_time += r->dt_pre_mig; } -static bool is_idle_sample(struct perf_sample *sample, - struct evsel *evsel) +static bool is_idle_sample(struct perf_sample *sample) { /* pid 0 == swapper == idle task */ - if (evsel__name_is(evsel, "sched:sched_switch")) + if (evsel__name_is(sample->evsel, "sched:sched_switch")) return perf_sample__intval(sample, "prev_pid") == 0; return sample->pid == 0; @@ -2499,12 +2486,11 @@ static void save_idle_callchain(struct perf_sched *sched, static struct thread *timehist_get_thread(struct perf_sched *sched, struct perf_sample *sample, - struct machine *machine, - struct evsel *evsel) + struct machine *machine) { struct thread *thread; - if (is_idle_sample(sample, evsel)) { + if (is_idle_sample(sample)) { thread = get_idle_thread(sample->cpu); if (thread == NULL) pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu); @@ -2547,7 +2533,6 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, static bool timehist_skip_sample(struct perf_sched *sched, struct thread *thread, - struct evsel *evsel, struct perf_sample *sample) { bool rc = false; @@ -2569,7 +2554,7 @@ static bool timehist_skip_sample(struct perf_sched *sched, tr = thread__get_runtime(thread); if (tr && tr->prio != -1) prio = tr->prio; - else if (evsel__name_is(evsel, "sched:sched_switch")) + else if (evsel__name_is(sample->evsel, "sched:sched_switch")) prio = perf_sample__intval(sample, "prev_prio"); if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { @@ -2579,7 +2564,7 @@ static bool timehist_skip_sample(struct perf_sched *sched, } if (sched->idle_hist) { - if (!evsel__name_is(evsel, "sched:sched_switch")) + if (!evsel__name_is(sample->evsel, "sched:sched_switch")) rc = true; else if (perf_sample__intval(sample, "prev_pid") != 0 && perf_sample__intval(sample, "next_pid") != 0) @@ -2590,7 +2575,6 @@ static bool timehist_skip_sample(struct perf_sched *sched, } static void timehist_print_wakeup_event(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct thread *awakened) @@ -2603,8 +2587,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, return; /* show wakeup unless both awakee and awaker are filtered */ - if (timehist_skip_sample(sched, thread, evsel, sample) && - timehist_skip_sample(sched, awakened, evsel, sample)) { + if (timehist_skip_sample(sched, thread, sample) && + timehist_skip_sample(sched, awakened, sample)) { thread__put(thread); return; } @@ -2628,7 +2612,6 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { @@ -2637,7 +2620,6 @@ static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unu static int timehist_sched_wakeup_event(const struct perf_tool *tool, union perf_event *event __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -2663,14 +2645,13 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, /* show wakeups if requested */ if (sched->show_wakeups && !perf_time__skip_sample(&sched->ptime, sample->time)) - timehist_print_wakeup_event(sched, evsel, sample, machine, thread); + timehist_print_wakeup_event(sched, sample, machine, thread); thread__put(thread); return 0; } static void timehist_print_migration_event(struct perf_sched *sched, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct thread *migrated) @@ -2691,8 +2672,8 @@ static void timehist_print_migration_event(struct perf_sched *sched, if (thread == NULL) return; - if (timehist_skip_sample(sched, thread, evsel, sample) && - timehist_skip_sample(sched, migrated, evsel, sample)) { + if (timehist_skip_sample(sched, thread, sample) && + timehist_skip_sample(sched, migrated, sample)) { thread__put(thread); return; } @@ -2726,7 +2707,6 @@ static void timehist_print_migration_event(struct perf_sched *sched, static int timehist_migrate_task_event(const struct perf_tool *tool, union perf_event *event __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -2751,8 +2731,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, /* show migrations if requested */ if (sched->show_migrations) { - timehist_print_migration_event(sched, evsel, sample, - machine, thread); + timehist_print_migration_event(sched, sample, machine, thread); } thread__put(thread); @@ -2784,7 +2763,6 @@ static void timehist_update_task_prio(struct perf_sample *sample, static int timehist_sched_change_event(const struct perf_tool *tool, union perf_event *event, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -2808,13 +2786,13 @@ static int timehist_sched_change_event(const struct perf_tool *tool, if (sched->show_prio || sched->prio_str) timehist_update_task_prio(sample, machine); - thread = timehist_get_thread(sched, sample, machine, evsel); + thread = timehist_get_thread(sched, sample, machine); if (thread == NULL) { rc = -1; goto out; } - if (timehist_skip_sample(sched, thread, evsel, sample)) + if (timehist_skip_sample(sched, thread, sample)) goto out; tr = thread__get_runtime(thread); @@ -2823,7 +2801,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, goto out; } - tprev = evsel__get_time(evsel, sample->cpu); + tprev = evsel__get_time(sample->evsel, sample->cpu); /* * If start time given: @@ -2913,7 +2891,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, tr->migrated = 0; } - evsel__save_time(evsel, sample->time, sample->cpu); + evsel__save_time(sample->evsel, sample->time, sample->cpu); thread__put(thread); addr_location__exit(&al); @@ -2922,11 +2900,10 @@ static int timehist_sched_change_event(const struct perf_tool *tool, static int timehist_sched_switch_event(const struct perf_tool *tool, union perf_event *event, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { - return timehist_sched_change_event(tool, event, evsel, sample, machine); + return timehist_sched_change_event(tool, event, sample, machine); } static int process_lost(const struct perf_tool *tool __maybe_unused, @@ -3174,7 +3151,6 @@ static void timehist_print_summary(struct perf_sched *sched, typedef int (*sched_handler)(const struct perf_tool *tool, union perf_event *event, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); @@ -3196,7 +3172,7 @@ static int perf_timehist__process_sample(const struct perf_tool *tool, if (evsel->handler != NULL) { sched_handler f = evsel->handler; - err = f(tool, event, evsel, sample, machine); + err = f(tool, event, sample, machine); } return err; From 6d2b06b4ef18f690beaf04d7f648c73ac2893ac5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:22 -0700 Subject: [PATCH 1290/5214] perf timechart: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from timechart-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 60 +++++++++++++++------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 034e0ba3f9cb..1d24d8738519 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -567,7 +567,6 @@ static const char *cat_backtrace(union perf_event *event, } typedef int (*tracepoint_handler)(struct timechart *tchart, - struct evsel *evsel, struct perf_sample *sample, const char *backtrace); @@ -588,8 +587,8 @@ static int process_sample_event(const struct perf_tool *tool, if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - return f(tchart, evsel, sample, - cat_backtrace(event, sample, machine)); + + return f(tchart, sample, cat_backtrace(event, sample, machine)); } return 0; @@ -597,7 +596,6 @@ static int process_sample_event(const struct perf_tool *tool, static int process_sample_cpu_idle(struct timechart *tchart __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { @@ -613,7 +611,6 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused, static int process_sample_cpu_frequency(struct timechart *tchart, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { @@ -626,7 +623,6 @@ process_sample_cpu_frequency(struct timechart *tchart, static int process_sample_sched_wakeup(struct timechart *tchart, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace) { @@ -640,7 +636,6 @@ process_sample_sched_wakeup(struct timechart *tchart, static int process_sample_sched_switch(struct timechart *tchart, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace) { @@ -656,7 +651,6 @@ process_sample_sched_switch(struct timechart *tchart, #ifdef SUPPORT_OLD_POWER_EVENTS static int process_sample_power_start(struct timechart *tchart __maybe_unused, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { @@ -669,7 +663,6 @@ process_sample_power_start(struct timechart *tchart __maybe_unused, static int process_sample_power_end(struct timechart *tchart, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { @@ -679,7 +672,6 @@ process_sample_power_end(struct timechart *tchart, static int process_sample_power_frequency(struct timechart *tchart, - struct evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused) { @@ -849,8 +841,8 @@ static int pid_end_io_sample(struct timechart *tchart, int pid, int type, static int process_enter_read(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, @@ -859,8 +851,8 @@ process_enter_read(struct timechart *tchart, static int process_exit_read(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, @@ -869,8 +861,8 @@ process_exit_read(struct timechart *tchart, static int process_enter_write(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, @@ -879,8 +871,8 @@ process_enter_write(struct timechart *tchart, static int process_exit_write(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, @@ -889,8 +881,8 @@ process_exit_write(struct timechart *tchart, static int process_enter_sync(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, @@ -899,8 +891,8 @@ process_enter_sync(struct timechart *tchart, static int process_exit_sync(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, @@ -909,8 +901,8 @@ process_exit_sync(struct timechart *tchart, static int process_enter_tx(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, @@ -919,8 +911,8 @@ process_enter_tx(struct timechart *tchart, static int process_exit_tx(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, @@ -929,8 +921,8 @@ process_exit_tx(struct timechart *tchart, static int process_enter_rx(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, @@ -939,8 +931,8 @@ process_enter_rx(struct timechart *tchart, static int process_exit_rx(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, @@ -949,8 +941,8 @@ process_exit_rx(struct timechart *tchart, static int process_enter_poll(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long fd = perf_sample__intval(sample, "fd"); return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, @@ -959,8 +951,8 @@ process_enter_poll(struct timechart *tchart, static int process_exit_poll(struct timechart *tchart, - struct evsel *evsel __maybe_unused, - struct perf_sample *sample) + struct perf_sample *sample, + const char *backtrace __maybe_unused) { long ret = perf_sample__intval(sample, "ret"); return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, From 1f44e8fe3d128915d3bbdc7203833eead721a309 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:23 -0700 Subject: [PATCH 1291/5214] perf trace: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from trace-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 4bc30ad8fb07..d4fafae853d4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2608,7 +2608,7 @@ static struct syscall *trace__find_syscall(struct trace *trace, int e_machine, i return sc; } -typedef int (*tracepoint_handler)(struct trace *trace, struct evsel *evsel, +typedef int (*tracepoint_handler)(struct trace *trace, union perf_event *event, struct perf_sample *sample); @@ -2805,10 +2805,11 @@ static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sam return NULL; } -static int trace__sys_enter(struct trace *trace, struct evsel *evsel, +static int trace__sys_enter(struct trace *trace, union perf_event *event __maybe_unused, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; char *msg; void *args; int printed = 0; @@ -2954,10 +2955,11 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam return sample__fprintf_callchain(sample, 38, print_opts, get_tls_callchain_cursor(), symbol_conf.bt_stop_list, trace->output); } -static int trace__sys_exit(struct trace *trace, struct evsel *evsel, +static int trace__sys_exit(struct trace *trace, union perf_event *event __maybe_unused, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; long ret; u64 duration = 0; bool duration_calculated = false; @@ -3094,7 +3096,7 @@ errno_print: { return err; } -static int trace__vfs_getname(struct trace *trace, struct evsel *evsel __maybe_unused, +static int trace__vfs_getname(struct trace *trace, union perf_event *event __maybe_unused, struct perf_sample *sample) { @@ -3155,7 +3157,7 @@ static int trace__vfs_getname(struct trace *trace, struct evsel *evsel __maybe_u return 0; } -static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel, +static int trace__sched_stat_runtime(struct trace *trace, union perf_event *event __maybe_unused, struct perf_sample *sample) { @@ -3177,7 +3179,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel, out_dump: fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", - evsel->name, + sample->evsel->name, perf_sample__strval(sample, "comm"), (pid_t)perf_sample__intval(sample, "pid"), runtime, @@ -3288,10 +3290,11 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample * return fprintf(trace->output, "%.*s", (int)printed, bf); } -static int trace__event_handler(struct trace *trace, struct evsel *evsel, +static int trace__event_handler(struct trace *trace, union perf_event *event __maybe_unused, struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; struct thread *thread; int callchain_ret = 0; @@ -3400,7 +3403,6 @@ static void print_location(FILE *f, struct perf_sample *sample, } static int trace__pgfault(struct trace *trace, - struct evsel *evsel, union perf_event *event __maybe_unused, struct perf_sample *sample) { @@ -3429,7 +3431,7 @@ static int trace__pgfault(struct trace *trace, if (ttrace == NULL) goto out_put; - if (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) { + if (sample->evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) { ttrace->pfmaj++; trace->pfmaj++; } else { @@ -3446,7 +3448,7 @@ static int trace__pgfault(struct trace *trace, sample->cpu, trace->output); fprintf(trace->output, "%sfault [", - evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? + sample->evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? "maj" : "min"); print_location(trace->output, sample, &al, false, true); @@ -3471,7 +3473,8 @@ static int trace__pgfault(struct trace *trace, if (callchain_ret > 0) trace__fprintf_callchain(trace, sample); else if (callchain_ret < 0) - pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel)); + pr_err("Problem processing %s callchain, skipping...\n", + evsel__name(sample->evsel)); ++trace->nr_events_printed; out: @@ -3483,7 +3486,6 @@ static int trace__pgfault(struct trace *trace, } static void trace__set_base_time(struct trace *trace, - struct evsel *evsel, struct perf_sample *sample) { /* @@ -3495,7 +3497,7 @@ static void trace__set_base_time(struct trace *trace, * appears in our event stream (vfs_getname comes to mind). */ if (trace->base_time == 0 && !trace->full_time && - (evsel->core.attr.sample_type & PERF_SAMPLE_TIME)) + (sample->evsel->core.attr.sample_type & PERF_SAMPLE_TIME)) trace->base_time = sample->time; } @@ -3515,11 +3517,11 @@ static int trace__process_sample(const struct perf_tool *tool, if (thread && thread__is_filtered(thread)) goto out; - trace__set_base_time(trace, evsel, sample); + trace__set_base_time(trace, sample); if (handler) { ++trace->nr_events; - handler(trace, evsel, event, sample); + handler(trace, event, sample); } out: thread__put(thread); @@ -3661,32 +3663,34 @@ static void evlist__free_syscall_tp_fields(struct evlist *evlist) static void trace__handle_event(struct trace *trace, union perf_event *event, struct perf_sample *sample) { const u32 type = event->header.type; - struct evsel *evsel; if (type != PERF_RECORD_SAMPLE) { trace__process_event(trace, trace->host, event, sample); return; } - evsel = evlist__id2evsel(trace->evlist, sample->id); - if (evsel == NULL) { + if (sample->evsel == NULL) + sample->evsel = evlist__id2evsel(trace->evlist, sample->id); + + if (sample->evsel == NULL) { fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample->id); return; } - if (evswitch__discard(&trace->evswitch, evsel)) + if (evswitch__discard(&trace->evswitch, sample->evsel)) return; - trace__set_base_time(trace, evsel, sample); + trace__set_base_time(trace, sample); - if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT && + if (sample->evsel->core.attr.type == PERF_TYPE_TRACEPOINT && sample->raw_data == NULL) { fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", - evsel__name(evsel), sample->tid, + evsel__name(sample->evsel), sample->tid, sample->cpu, sample->raw_size); } else { - tracepoint_handler handler = evsel->handler; - handler(trace, evsel, event, sample); + tracepoint_handler handler = sample->evsel->handler; + + handler(trace, event, sample); } if (trace->nr_events_printed >= trace->max_events && trace->max_events != ULONG_MAX) From 675073ddf8779593ca32ee0cdf5371a420b87dd5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:24 -0700 Subject: [PATCH 1292/5214] perf evlist: Try to avoid computing evsel from sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from evlist-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 4 +++- tools/perf/tests/mmap-basic.c | 4 +++- tools/perf/tests/switch-tracking.c | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 6cf73bb0c7af..c8474f7ac658 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1164,7 +1164,9 @@ static int deliver_event(struct ordered_events *qe, goto next_event; } - evsel = evlist__id2evsel(session->evlist, sample.id); + evsel = sample.evsel; + if (!evsel) + evsel = evlist__id2evsel(session->evlist, sample.id); assert(evsel != NULL); if (event->header.type == PERF_RECORD_SAMPLE) { diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 3313c236104e..a18d84d858aa 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -142,7 +142,9 @@ static int test__basic_mmap(struct test_suite *test __maybe_unused, int subtest } err = -1; - evsel = evlist__id2evsel(evlist, sample.id); + evsel = sample.evsel; + if (!evsel) + evsel = evlist__id2evsel(evlist, sample.id); perf_sample__exit(&sample); if (evsel == NULL) { pr_debug("event with id %" PRIu64 diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 22b0302252db..e32331fee277 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -138,7 +138,10 @@ static int process_sample_event(struct evlist *evlist, goto out; } - evsel = evlist__id2evsel(evlist, sample.id); + evsel = sample.evsel; + if (!evsel) + evsel = evlist__id2evsel(evlist, sample.id); + if (evsel == switch_tracking->switch_evsel) { next_tid = perf_sample__intval(&sample, "next_pid"); prev_tid = perf_sample__intval(&sample, "prev_pid"); From 5e807647e155c5b2ae256849965e8311b3916d70 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:25 -0700 Subject: [PATCH 1293/5214] perf script: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from script-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 12 ++++-- .../util/scripting-engines/trace-event-perl.c | 21 +++++------ .../scripting-engines/trace-event-python.c | 37 ++++++++----------- tools/perf/util/trace-event-scripting.c | 5 +-- tools/perf/util/trace-event.h | 3 -- 5 files changed, 33 insertions(+), 45 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b94a199ffcc9..c0918006e0ab 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2419,12 +2419,13 @@ static bool show_event(struct perf_sample *sample, } static void process_event(struct perf_script *script, - struct perf_sample *sample, struct evsel *evsel, + struct perf_sample *sample, struct addr_location *al, struct addr_location *addr_al, struct machine *machine) { struct thread *thread = al->thread; + struct evsel *evsel = sample->evsel; struct perf_event_attr *attr = &evsel->core.attr; unsigned int type = evsel__output_type(evsel); struct evsel_script *es = evsel->priv; @@ -2716,9 +2717,9 @@ static int process_sample_event(const struct perf_tool *tool, thread__resolve(al.thread, &addr_al, sample); addr_al_ptr = &addr_al; } - scripting_ops->process_event(event, sample, evsel, &al, addr_al_ptr); + scripting_ops->process_event(event, sample, &al, addr_al_ptr); } else { - process_event(scr, sample, evsel, &al, &addr_al, machine); + process_event(scr, sample, &al, &addr_al, machine); } out_put: @@ -2894,9 +2895,12 @@ static int print_event_with_time(const struct perf_tool *tool, { struct perf_script *script = container_of(tool, struct perf_script, tool); struct perf_session *session = script->session; - struct evsel *evsel = evlist__id2evsel(session->evlist, sample->id); + struct evsel *evsel = sample->evsel; struct thread *thread = NULL; + if (!evsel) + evsel = evlist__id2evsel(session->evlist, sample->id); + if (evsel && !evsel->core.attr.sample_id_all) { sample->cpu = 0; sample->time = timestamp; diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index af0d514b2397..7a18ea4b7d50 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -257,7 +257,6 @@ static void define_event_symbols(struct tep_event *event, } static SV *perl_process_callchain(struct perf_sample *sample, - struct evsel *evsel __maybe_unused, struct addr_location *al) { struct callchain_cursor *cursor; @@ -340,7 +339,6 @@ static SV *perl_process_callchain(struct perf_sample *sample, } static void perl_process_tracepoint(struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al) { struct thread *thread = al->thread; @@ -355,6 +353,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, unsigned long long nsecs = sample->time; const char *comm = thread__comm_str(thread); DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX); + struct evsel *evsel = sample->evsel; bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX); dSP; @@ -389,7 +388,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, XPUSHs(sv_2mortal(newSVuv(ns))); XPUSHs(sv_2mortal(newSViv(pid))); XPUSHs(sv_2mortal(newSVpv(comm, 0))); - XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); + XPUSHs(sv_2mortal(perl_process_callchain(sample, al))); /* common fields other than pid can be accessed via xsub fns */ @@ -426,7 +425,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, XPUSHs(sv_2mortal(newSVuv(nsecs))); XPUSHs(sv_2mortal(newSViv(pid))); XPUSHs(sv_2mortal(newSVpv(comm, 0))); - XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); + XPUSHs(sv_2mortal(perl_process_callchain(sample, al))); call_pv("main::trace_unhandled", G_SCALAR); } SPAGAIN; @@ -435,9 +434,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, LEAVE; } -static void perl_process_event_generic(union perf_event *event, - struct perf_sample *sample, - struct evsel *evsel) +static void perl_process_event_generic(union perf_event *event, struct perf_sample *sample) { dSP; @@ -448,7 +445,8 @@ static void perl_process_event_generic(union perf_event *event, SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size))); - XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->core.attr, sizeof(evsel->core.attr)))); + XPUSHs(sv_2mortal(newSVpvn((const char *)&sample->evsel->core.attr, + sizeof(sample->evsel->core.attr)))); XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample)))); XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size))); PUTBACK; @@ -461,13 +459,12 @@ static void perl_process_event_generic(union perf_event *event, static void perl_process_event(union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al) { - scripting_context__update(scripting_context, event, sample, evsel, al, addr_al); - perl_process_tracepoint(sample, evsel, al); - perl_process_event_generic(event, sample, evsel); + scripting_context__update(scripting_context, event, sample, al, addr_al); + perl_process_tracepoint(sample, al); + perl_process_event_generic(event, sample); } static void run_start_sub(void) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 9e1069ec0578..8edd2f36e5a9 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -390,7 +390,6 @@ static unsigned long get_offset(struct symbol *sym, struct addr_location *al) } static PyObject *python_process_callchain(struct perf_sample *sample, - struct evsel *evsel __maybe_unused, struct addr_location *al) { PyObject *pylist; @@ -651,11 +650,9 @@ static PyObject *get_sample_value_as_tuple(struct sample_read_value *value, return t; } -static void set_sample_read_in_dict(PyObject *dict_sample, - struct perf_sample *sample, - struct evsel *evsel) +static void set_sample_read_in_dict(PyObject *dict_sample, struct perf_sample *sample) { - u64 read_format = evsel->core.attr.read_format; + u64 read_format = sample->evsel->core.attr.read_format; PyObject *values; unsigned int i; @@ -741,11 +738,10 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, uint16_t e_machine, static int set_regs_in_dict(PyObject *dict, struct perf_sample *sample, - struct evsel *evsel, uint16_t e_machine, uint32_t e_flags) { - struct perf_event_attr *attr = &evsel->core.attr; + struct perf_event_attr *attr = &sample->evsel->core.attr; int size = (__sw_hweight64(attr->sample_regs_intr) * MAX_REG_SIZE) + 1; char *bf = NULL; @@ -831,7 +827,6 @@ static void python_process_sample_flags(struct perf_sample *sample, PyObject *di } static PyObject *get_perf_sample_dict(struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al, PyObject *callchain) @@ -839,6 +834,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, PyObject *dict, *dict_sample, *brstack, *brstacksym; uint16_t e_machine = EM_HOST; uint32_t e_flags = EF_HOST; + struct evsel *evsel = sample->evsel; dict = PyDict_New(); if (!dict) @@ -871,7 +867,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, PyLong_FromUnsignedLongLong(sample->phys_addr)); pydict_set_item_string_decref(dict_sample, "addr", PyLong_FromUnsignedLongLong(sample->addr)); - set_sample_read_in_dict(dict_sample, sample, evsel); + set_sample_read_in_dict(dict_sample, sample); pydict_set_item_string_decref(dict_sample, "weight", PyLong_FromUnsignedLongLong(sample->weight)); pydict_set_item_string_decref(dict_sample, "ins_lat", @@ -928,7 +924,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, if (al->thread) e_machine = thread__e_machine(al->thread, /*machine=*/NULL, &e_flags); - if (set_regs_in_dict(dict, sample, evsel, e_machine, e_flags)) + if (set_regs_in_dict(dict, sample, e_machine, e_flags)) Py_FatalError("Failed to setting regs in dict"); return dict; @@ -936,7 +932,6 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, #ifdef HAVE_LIBTRACEEVENT static void python_process_tracepoint(struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al) { @@ -954,6 +949,7 @@ static void python_process_tracepoint(struct perf_sample *sample, const char *comm = thread__comm_str(al->thread); const char *default_handler_name = "trace_unhandled"; DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX); + struct evsel *evsel = sample->evsel; bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX); @@ -995,7 +991,7 @@ static void python_process_tracepoint(struct perf_sample *sample, PyTuple_SetItem(t, n++, context); /* ip unwinding */ - callchain = python_process_callchain(sample, evsel, al); + callchain = python_process_callchain(sample, al); /* Need an additional reference for the perf_sample dict */ Py_INCREF(callchain); @@ -1051,7 +1047,7 @@ static void python_process_tracepoint(struct perf_sample *sample, PyTuple_SetItem(t, n++, dict); if (get_argument_count(handler) == (int) n + 1) { - all_entries_dict = get_perf_sample_dict(sample, evsel, al, addr_al, + all_entries_dict = get_perf_sample_dict(sample, al, addr_al, callchain); PyTuple_SetItem(t, n++, all_entries_dict); } else { @@ -1070,7 +1066,6 @@ static void python_process_tracepoint(struct perf_sample *sample, } #else static void python_process_tracepoint(struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, struct addr_location *al __maybe_unused, struct addr_location *addr_al __maybe_unused) { @@ -1465,7 +1460,6 @@ static int python_process_call_return(struct call_return *cr, u64 *parent_db_id, } static void python_process_general_event(struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al) { @@ -1488,8 +1482,8 @@ static void python_process_general_event(struct perf_sample *sample, Py_FatalError("couldn't create Python tuple"); /* ip unwinding */ - callchain = python_process_callchain(sample, evsel, al); - dict = get_perf_sample_dict(sample, evsel, al, addr_al, callchain); + callchain = python_process_callchain(sample, al); + dict = get_perf_sample_dict(sample, al, addr_al, callchain); PyTuple_SetItem(t, n++, dict); if (_PyTuple_Resize(&t, n) == -1) @@ -1502,24 +1496,23 @@ static void python_process_general_event(struct perf_sample *sample, static void python_process_event(union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al) { struct tables *tables = &tables_global; - scripting_context__update(scripting_context, event, sample, evsel, al, addr_al); + scripting_context__update(scripting_context, event, sample, al, addr_al); - switch (evsel->core.attr.type) { + switch (sample->evsel->core.attr.type) { case PERF_TYPE_TRACEPOINT: - python_process_tracepoint(sample, evsel, al, addr_al); + python_process_tracepoint(sample, al, addr_al); break; /* Reserve for future process_hw/sw/raw APIs */ default: if (tables->db_export_mode) db_export__sample(&tables->dbe, event, sample, al, addr_al); else - python_process_general_event(sample, evsel, al, addr_al); + python_process_general_event(sample, al, addr_al); } } diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index fa850e44cb46..dc584ac316a3 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -103,12 +103,11 @@ int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec) void scripting_context__update(struct scripting_context *c, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al) { #ifdef HAVE_LIBTRACEEVENT - const struct tep_event *tp_format = evsel__tp_format(evsel); + const struct tep_event *tp_format = evsel__tp_format(sample->evsel); c->pevent = tp_format ? tp_format->tep : NULL; #else @@ -117,7 +116,6 @@ void scripting_context__update(struct scripting_context *c, c->event_data = sample->raw_data; c->event = event; c->sample = sample; - c->evsel = evsel; c->al = al; c->addr_al = addr_al; } @@ -134,7 +132,6 @@ static int stop_script_unsupported(void) static void process_event_unsupported(union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, struct addr_location *al __maybe_unused, struct addr_location *addr_al __maybe_unused) { diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 914d9b69ed62..720121c74f1d 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -94,7 +94,6 @@ struct scripting_ops { int (*stop_script) (void); void (*process_event) (union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al); void (*process_switch)(union perf_event *event, @@ -124,7 +123,6 @@ struct scripting_context { void *event_data; union perf_event *event; struct perf_sample *sample; - struct evsel *evsel; struct addr_location *al; struct addr_location *addr_al; struct perf_session *session; @@ -133,7 +131,6 @@ struct scripting_context { void scripting_context__update(struct scripting_context *scripting_context, union perf_event *event, struct perf_sample *sample, - struct evsel *evsel, struct addr_location *al, struct addr_location *addr_al); From ec319c4d35523e12630631b3ae875758cfa3c6e0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:26 -0700 Subject: [PATCH 1294/5214] perf s390-sample-raw: Don't pass evsel or its PMU with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from s390-sample-raw-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/s390-sample-raw.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c index c6ae0ae8d86a..52bbca5c56c8 100644 --- a/tools/perf/util/s390-sample-raw.c +++ b/tools/perf/util/s390-sample-raw.c @@ -222,8 +222,9 @@ static char *get_counter_name(int set, int nr, struct perf_pmu *pmu) return result; } -static void s390_cpumcfdg_dump(struct perf_pmu *pmu, struct perf_sample *sample) +static void s390_cpumcfdg_dump(struct perf_sample *sample) { + struct perf_pmu *pmu = sample->evsel->pmu; size_t i, len = sample->raw_size, offset = 0; unsigned char *buf = sample->raw_data; const char *color = PERF_COLOR_BLUE; @@ -284,8 +285,9 @@ static bool s390_pai_all_test(struct perf_sample *sample) return true; } -static void s390_pai_all_dump(struct evsel *evsel, struct perf_sample *sample) +static void s390_pai_all_dump(struct perf_sample *sample) { + struct evsel *evsel = sample->evsel; size_t len = sample->raw_size, offset = 0; unsigned char *p = sample->raw_data; const char *color = PERF_COLOR_BLUE; @@ -332,31 +334,32 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample) { const char *pai_name; - struct evsel *evsel; if (event->header.type != PERF_RECORD_SAMPLE) return; - evsel = evlist__event2evsel(evlist, event); - if (!evsel) - return; + if (!sample->evsel) { + sample->evsel = evlist__event2evsel(evlist, event); + if (!sample->evsel) + return; + } /* Check for raw data in sample */ if (!sample->raw_size || !sample->raw_data) return; /* Display raw data on screen */ - if (evsel->core.attr.config == PERF_EVENT_CPUM_CF_DIAG) { - if (!evsel->pmu) - evsel->pmu = perf_pmus__find("cpum_cf"); + if (sample->evsel->core.attr.config == PERF_EVENT_CPUM_CF_DIAG) { + if (!sample->evsel->pmu) + sample->evsel->pmu = perf_pmus__find("cpum_cf"); if (!s390_cpumcfdg_testctr(sample)) pr_err("Invalid counter set data encountered\n"); else - s390_cpumcfdg_dump(evsel->pmu, sample); + s390_cpumcfdg_dump(sample); return; } - switch (evsel->core.attr.config) { + switch (sample->evsel->core.attr.config) { case PERF_EVENT_PAI_NNPA_ALL: pai_name = "NNPA_ALL"; break; @@ -370,8 +373,8 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, if (!s390_pai_all_test(sample)) { pr_err("Invalid %s raw data encountered\n", pai_name); } else { - if (!evsel->pmu) - evsel->pmu = perf_pmus__find_by_type(evsel->core.attr.type); - s390_pai_all_dump(evsel, sample); + if (!sample->evsel->pmu) + sample->evsel->pmu = perf_pmus__find_by_type(sample->evsel->core.attr.type); + s390_pai_all_dump(sample); } } From 2856524e4ef76938b7d509b8120b65c93b552525 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:27 -0700 Subject: [PATCH 1295/5214] perf evsel: Don't pass evsel with sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As struct perf_sample now directly contains its own resolved evsel pointer, passing the evsel separately is redundant and clutters the interface. Remove the redundant evsel parameter from evsel-specific handlers and structures, ensuring the tool always directly accesses the evsel bound to the sample. This simplifies the API signatures and eliminates the risk of passing an inconsistent evsel. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 61 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ee30e15af054..bb48568b8101 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3003,52 +3003,62 @@ int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads) return ret; } -static int perf_evsel__parse_id_sample(const struct evsel *evsel, - const union perf_event *event, +static int perf_evsel__parse_id_sample(const union perf_event *event, struct perf_sample *sample) { + const struct evsel *evsel = sample->evsel; u64 type = evsel->core.attr.sample_type; const __u64 *array = event->sample.array; bool swapped = evsel->needs_swap; union u64_swap u; - - array += ((event->header.size - - sizeof(event->header)) / sizeof(u64)) - 1; + int i = ((event->header.size - sizeof(event->header)) / sizeof(u64)) - 1; if (type & PERF_SAMPLE_IDENTIFIER) { - sample->id = *array; - array--; + if (i < 0) + return -EFAULT; + + sample->id = array[i--]; } if (type & PERF_SAMPLE_CPU) { - u.val64 = *array; + if (i < 0) + return -EFAULT; + + u.val64 = array[i--]; if (swapped) { /* undo swap of u64, then swap on individual u32s */ u.val64 = bswap_64(u.val64); u.val32[0] = bswap_32(u.val32[0]); } - sample->cpu = u.val32[0]; - array--; } if (type & PERF_SAMPLE_STREAM_ID) { - sample->stream_id = *array; - array--; + if (i < 0) + return -EFAULT; + + sample->stream_id = array[i--]; } if (type & PERF_SAMPLE_ID) { - sample->id = *array; - array--; + if (i < 0) + return -EFAULT; + + sample->id = array[i--]; } if (type & PERF_SAMPLE_TIME) { - sample->time = *array; - array--; + if (i < 0) + return -EFAULT; + + sample->time = array[i--]; } if (type & PERF_SAMPLE_TID) { - u.val64 = *array; + if (i < 0) + return -EFAULT; + + u.val64 = array[i--]; if (swapped) { /* undo swap of u64, then swap on individual u32s */ u.val64 = bswap_64(u.val64); @@ -3058,7 +3068,6 @@ static int perf_evsel__parse_id_sample(const struct evsel *evsel, sample->pid = u.val32[0]; sample->tid = u.val32[1]; - array--; } return 0; @@ -3244,15 +3253,18 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->deferred_cookie = event->callchain_deferred.cookie; - if (evsel->core.attr.sample_id_all) - perf_evsel__parse_id_sample(evsel, event, data); - + if (evsel->core.attr.sample_id_all) { + if (perf_evsel__parse_id_sample(event, data)) + goto out_efault; + } return 0; } if (event->header.type != PERF_RECORD_SAMPLE) { - if (evsel->core.attr.sample_id_all) - perf_evsel__parse_id_sample(evsel, event, data); + if (evsel->core.attr.sample_id_all) { + if (perf_evsel__parse_id_sample(event, data)) + goto out_efault; + } return 0; } @@ -3614,12 +3626,13 @@ int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, if (event->header.type != PERF_RECORD_SAMPLE) { struct perf_sample data = { + .evsel = evsel, .time = -1ULL, }; if (!evsel->core.attr.sample_id_all) return -1; - if (perf_evsel__parse_id_sample(evsel, event, &data)) + if (perf_evsel__parse_id_sample(event, &data)) return -1; *timestamp = data.time; From 9890b403d49eb6caf2d58224ade744aa06646260 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:28 -0700 Subject: [PATCH 1296/5214] perf lock: Constify trace_lock_handler variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constify the static trace_lock_handler structures. Since the trace lock handler callbacks and definitions do not change at runtime, declare them const to enforce read-only memory placement and improve safety. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index b6533483ba64..89a40d385b27 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -1179,7 +1179,7 @@ static int report_lock_contention_end_event(struct perf_sample *sample) /* lock oriented handlers */ /* TODO: handlers for CPU oriented, thread oriented */ -static struct trace_lock_handler report_lock_ops = { +static const struct trace_lock_handler report_lock_ops = { .acquire_event = report_lock_acquire_event, .acquired_event = report_lock_acquired_event, .contended_event = report_lock_contended_event, @@ -1188,13 +1188,13 @@ static struct trace_lock_handler report_lock_ops = { .contention_end_event = report_lock_contention_end_event, }; -static struct trace_lock_handler contention_lock_ops = { +static const struct trace_lock_handler contention_lock_ops = { .contention_begin_event = report_lock_contention_begin_event, .contention_end_event = report_lock_contention_end_event, }; -static struct trace_lock_handler *trace_handler; +static const struct trace_lock_handler *trace_handler; static int evsel__process_lock_acquire(struct perf_sample *sample) { From 8dca7ad2c2576b721e78244206143832d46a3b83 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:29 -0700 Subject: [PATCH 1297/5214] perf lock: Avoid segv if event is missing a callchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid a potential segmentation fault if a parsed sample unexpectedly lacks a valid callchain pointer. Add a check for a NULL sample->callchain pointer in get_callstack(). If the callchain is missing, return NULL to safely skip the event and log a debug warning rather than causing a tool segfault. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 89a40d385b27..064b3aa4bad7 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -938,9 +938,16 @@ static u64 *get_callstack(struct perf_sample *sample, int max_stack) u64 i; int c; - callstack = calloc(max_stack, sizeof(*callstack)); - if (callstack == NULL) + if (!sample->callchain) { + pr_debug("Sample unexpectedly missing callchain\n"); return NULL; + } + + callstack = calloc(max_stack, sizeof(*callstack)); + if (callstack == NULL) { + pr_debug("Failed to allocate callstack\n"); + return NULL; + } for (i = 0, c = 0; i < sample->callchain->nr && c < max_stack; i++) { u64 ip = sample->callchain->ips[i]; @@ -1059,7 +1066,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample) if (needs_callstack()) { u64 *callstack = get_callstack(sample, max_stack_depth); if (callstack == NULL) - return -ENOMEM; + return 0; if (!match_callstack_filter(machine, callstack, max_stack_depth)) { free(callstack); From 00b36b394c15f625fa166ba3b399cad5bd5065f9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:30 -0700 Subject: [PATCH 1298/5214] perf timechart: Fix memory leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve a major execution-time leak of backtrace strings in the timechart tool. - Modify cat_backtrace() to return dynamically allocated memory via open_memstream(), transferring ownership to the caller. - Free the returned backtrace string inside process_sample_event() immediately after invoking the tracepoint handler. - In handlers like pid_put_sample() and sched_wakeup(), make a separate copy of the backtrace using strdup() if it needs to be persisted in the sample struct, preventing lifetime issues and double-free vulnerabilities. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 1d24d8738519..d2e15d02304e 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -299,7 +299,7 @@ static void pid_put_sample(struct timechart *tchart, int pid, int type, sample->type = type; sample->next = c->samples; sample->cpu = cpu; - sample->backtrace = backtrace; + sample->backtrace = backtrace ? strdup(backtrace) : NULL; c->samples = sample; if (sample->type == TYPE_RUNNING && end > start && start > 0) { @@ -433,7 +433,7 @@ static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp, we->time = timestamp; we->waker = waker; - we->backtrace = backtrace; + we->backtrace = backtrace ? strdup(backtrace) : NULL; if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) we->waker = -1; @@ -489,9 +489,9 @@ static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, } } -static const char *cat_backtrace(union perf_event *event, - struct perf_sample *sample, - struct machine *machine) +static char *cat_backtrace(union perf_event *event, + struct perf_sample *sample, + struct machine *machine) { struct addr_location al; unsigned int i; @@ -577,6 +577,7 @@ static int process_sample_event(const struct perf_tool *tool, { struct timechart *tchart = container_of(tool, struct timechart, tool); struct evsel *evsel = sample->evsel; + int ret = 0; if (evsel->core.attr.sample_type & PERF_SAMPLE_TIME) { if (!tchart->first_time || tchart->first_time > sample->time) @@ -587,11 +588,13 @@ static int process_sample_event(const struct perf_tool *tool, if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; + char *backtrace = cat_backtrace(event, sample, machine); - return f(tchart, sample, cat_backtrace(event, sample, machine)); + ret = f(tchart, sample, backtrace); + free(backtrace); } - return 0; + return ret; } static int From b3ab668761786f9df9e19dd2805e657af2f19c41 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:31 -0700 Subject: [PATCH 1299/5214] perf kmem: Fix memory leaks on error path and when skipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix memory leaks on the error paths and skipped sample handling paths in the perf kmem tool. Ensure that all allocated GFP flags and thread references are properly freed and released via thread__put() when skipping samples or encountering parsing failures, preventing long-term memory usage leaks during large trace analyses. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 68a39f56204d..daf2272c7337 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -783,17 +783,21 @@ static int parse_gfp_flags(struct perf_sample *sample, unsigned int gfp_flags) new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps)); if (new == NULL) - return -ENOMEM; + goto err_out; gfps = new; - new += nr_gfps++; + new += nr_gfps; new->flags = gfp_flags; new->human_readable = strdup(str + 10); + if (!new->human_readable) + goto err_out; new->compact_str = compact_gfp_flags(str + 10); - if (!new->human_readable || !new->compact_str) - return -ENOMEM; - + if (!new->compact_str) { + free(new->human_readable); + goto err_out; + } + nr_gfps++; qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp); } @@ -802,6 +806,9 @@ static int parse_gfp_flags(struct perf_sample *sample, unsigned int gfp_flags) trace_seq_destroy(&seq); return 0; +err_out: + trace_seq_destroy(&seq); + return -ENOMEM; } static int evsel__process_page_alloc_event(struct perf_sample *sample) @@ -971,6 +978,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, } if (perf_kmem__skip_sample(sample)) { + thread__put(thread); return 0; } From e454fef5d2c6e3fc89bd87a2da491dae3977a9ed Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:32 -0700 Subject: [PATCH 1300/5214] perf synthetic-events: Bound check when synthesizing mmap2 and build_id events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add robust boundary checks when synthesizing mmap2 and build_id events to ensure that filename fields do not overflow the fixed-size stack allocations or the synthesized event structures. Verify that the filename fits safely within the allocated boundaries of the mmap2 event structure, and prevent potential heap/stack overflow corruptions from excessively long or corrupted kernel filenames. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/synthetic-events.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index fd1d4c0345d6..d665b0f94b32 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2268,14 +2268,20 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, const char *filename) { union perf_event ev; - size_t len; + size_t len, filename_len = strlen(filename); u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0; void *array = &ev; int ret; - len = sizeof(ev.build_id) + strlen(filename) + 1; + if (filename_len >= PATH_MAX) + return -EINVAL; + + len = sizeof(ev.build_id) + filename_len + 1; len = PERF_ALIGN(len, sizeof(u64)); + if (len + MAX_ID_HDR_ENTRIES * sizeof(__u64) > sizeof(ev)) + return -E2BIG; + memset(&ev, 0, len); ev.build_id.size = bid->size; @@ -2314,14 +2320,21 @@ int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, const char *filename) { union perf_event ev; + size_t filename_len = strlen(filename); size_t ev_len; u64 sample_type = sample->evsel ? sample->evsel->core.attr.sample_type : 0; void *array; int ret; - ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + strlen(filename) + 1; + if (filename_len >= sizeof(ev.mmap2.filename)) + return -EINVAL; + + ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + filename_len + 1; ev_len = PERF_ALIGN(ev_len, sizeof(u64)); + if (ev_len + MAX_ID_HDR_ENTRIES * sizeof(__u64) > sizeof(ev)) + return -E2BIG; + memset(&ev, 0, ev_len); ev.mmap2.header.type = PERF_RECORD_MMAP2; From 1bd2c9403ec5fe42bc2828b52c253e7cfed101e8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:33 -0700 Subject: [PATCH 1301/5214] perf kmem: Add bounds checks to tracepoint read values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sanitize order and migrate_type values from tracepoint payloads before using them as array indexes. When processing page_alloc_event and page_free_event, verify that 'order' is less than MAX_PAGE_ORDER and 'migrate_type' is less than MAX_MIGRATE_TYPES. This guarantees that indexing into order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES] remains strictly within bounds, avoiding out-of-bound heap or static segment accesses. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index daf2272c7337..33585e353efe 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -826,6 +826,16 @@ static int evsel__process_page_alloc_event(struct perf_sample *sample) .migrate_type = migrate_type, }; + if (order >= MAX_PAGE_ORDER) { + pr_debug("Out-of-bounds order %u\n", order); + return -1; + } + + if (migrate_type >= MAX_MIGRATE_TYPES) { + pr_debug("Out-of-bounds migratetype %u\n", migrate_type); + return -1; + } + if (use_pfn) page = perf_sample__intval(sample, "pfn"); else @@ -892,6 +902,11 @@ static int evsel__process_page_free_event(struct perf_sample *sample) .order = order, }; + if (order >= MAX_PAGE_ORDER) { + pr_debug("Out-of-bounds order %u\n", order); + return -1; + } + if (use_pfn) page = perf_sample__intval(sample, "pfn"); else From 16ccbec0f3e14f6ad06af6112ea4fa5668cab46a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:34 -0700 Subject: [PATCH 1302/5214] perf sched: Bounds check CPU in sched switch events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure CPU indexes parsed from sched switch and runtime events fit within the MAX_CPUS limit to prevent out-of-bounds indexing. Add explicit bounds checks for sample->cpu against MAX_CPUS inside process_sched_switch_event, process_sched_runtime_event, and timehist_sched_change_event. This prevents indexing beyond the boundaries of the sched->curr_pid tracking array, avoiding potential memory corruption or undefined behavior. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d984e58c7dbf..9d73c7043182 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1791,6 +1791,11 @@ static int process_sched_switch_event(const struct perf_tool *tool, u32 prev_pid = perf_sample__intval(sample, "prev_pid"), next_pid = perf_sample__intval(sample, "next_pid"); + if (this_cpu < 0 || this_cpu >= MAX_CPUS) { + pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", this_cpu); + return 0; + } + if (sched->curr_pid[this_cpu] != (u32)-1) { /* * Are we trying to switch away a PID that is @@ -1813,6 +1818,11 @@ static int process_sched_runtime_event(const struct perf_tool *tool, { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); + if (sample->cpu >= MAX_CPUS) { + pr_warning("Out-of-bound sample CPU %u. Skipping sample\n", sample->cpu); + return 0; + } + if (sched->tp_handler->runtime_event) return sched->tp_handler->runtime_event(sched, sample, machine); @@ -2775,6 +2785,11 @@ static int timehist_sched_change_event(const struct perf_tool *tool, int rc = 0; const char state = perf_sample__taskstate(sample, "prev_state"); + if (sample->cpu >= MAX_CPUS) { + pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", sample->cpu); + return 0; + } + addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { pr_err("problem processing %d event. skipping it\n", From a41a462334a239511bad6f4509c8625a46d36c84 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:35 -0700 Subject: [PATCH 1303/5214] perf timechart: Bounds check CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent out-of-bounds writes/reads in CPU state tracking arrays by enforcing strict MAX_CPUS bounds checks in timechart's tracepoint handlers. Ensure that cpu_id retrieved from idle/frequency and sched tracepoints is less than MAX_CPUS before indexing into cpus_cstate_state, cpus_cstate_start_times, and similar tracking arrays. Also, fix an off-by-one error in the CPU iteration loop inside end_sample_processing() by changing the loop condition from 'cpu <= tchart->numcpus' to 'cpu < tchart->numcpus'. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 35 +++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index d2e15d02304e..630756bebe32 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -605,6 +605,10 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused, u32 state = perf_sample__intval(sample, "state"); u32 cpu_id = perf_sample__intval(sample, "cpu_id"); + if (cpu_id >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu_id %u\n", cpu_id); + return -1; + } if (state == (u32)PWR_EVENT_EXIT) c_state_end(tchart, cpu_id, sample->time); else @@ -620,6 +624,10 @@ process_sample_cpu_frequency(struct timechart *tchart, u32 state = perf_sample__intval(sample, "state"); u32 cpu_id = perf_sample__intval(sample, "cpu_id"); + if (cpu_id >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu_id %u\n", cpu_id); + return -1; + } p_state_change(tchart, cpu_id, sample->time, state); return 0; } @@ -633,6 +641,10 @@ process_sample_sched_wakeup(struct timechart *tchart, int waker = perf_sample__intval(sample, "common_pid"); int wakee = perf_sample__intval(sample, "pid"); + if (sample->cpu >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + return -1; + } sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace); return 0; } @@ -646,6 +658,10 @@ process_sample_sched_switch(struct timechart *tchart, int next_pid = perf_sample__intval(sample, "next_pid"); u64 prev_state = perf_sample__intval(sample, "prev_state"); + if (sample->cpu >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + return -1; + } sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, prev_state, backtrace); return 0; @@ -660,6 +676,10 @@ process_sample_power_start(struct timechart *tchart __maybe_unused, u64 cpu_id = perf_sample__intval(sample, "cpu_id"); u64 value = perf_sample__intval(sample, "value"); + if (cpu_id >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); + return -1; + } c_state_start(cpu_id, sample->time, value); return 0; } @@ -669,6 +689,10 @@ process_sample_power_end(struct timechart *tchart, struct perf_sample *sample, const char *backtrace __maybe_unused) { + if (sample->cpu >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + return -1; + } c_state_end(tchart, sample->cpu, sample->time); return 0; } @@ -681,6 +705,10 @@ process_sample_power_frequency(struct timechart *tchart, u64 cpu_id = perf_sample__intval(sample, "cpu_id"); u64 value = perf_sample__intval(sample, "value"); + if (cpu_id >= MAX_CPUS) { + pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); + return -1; + } p_state_change(tchart, cpu_id, sample->time, value); return 0; } @@ -692,10 +720,9 @@ process_sample_power_frequency(struct timechart *tchart, */ static void end_sample_processing(struct timechart *tchart) { - u64 cpu; - struct power_event *pwr; + for (u64 cpu = 0; cpu < tchart->numcpus; cpu++) { + struct power_event *pwr; - for (cpu = 0; cpu <= tchart->numcpus; cpu++) { /* C state */ #if 0 pwr = zalloc(sizeof(*pwr)); @@ -1515,6 +1542,8 @@ static int process_header(struct perf_file_section *section __maybe_unused, switch (feat) { case HEADER_NRCPUS: tchart->numcpus = ph->env.nr_cpus_avail; + if (tchart->numcpus > MAX_CPUS) + tchart->numcpus = MAX_CPUS; break; case HEADER_CPU_TOPOLOGY: From 9aa1ec2e1d21be400767aa9b754adbae7d8e1d32 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 20 May 2026 12:05:36 -0700 Subject: [PATCH 1304/5214] perf evsel: Add bounds checking to trace point raw data accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent out-of-bounds memory reads when parsing corrupted or maliciously crafted perf.data files by introducing robust bounds validation to raw data accessors. - Add a helper out_of_bounds() to check if field offsets and sizes exceed the sample's raw_size boundary, preventing heap read overflows. - In perf_sample__rawptr(), properly resolve newer relative dynamic tracepoint fields (__rel_loc) by checking the boundaries before and after reading the dynamic field descriptor. - Byte-swap dynamic field offsets and sizes dynamically when endianness varies, ensuring cross-endian parsing is robust. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Andi Kleen Cc: Andrew Jones Cc: Anup Patel Cc: Athira Rajeev Cc: Blake Jones Cc: Chen Ni Cc: Chun-Tse Shao Cc: Dapeng Mi Cc: Derek Foreman Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Hrishikesh Suresh Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Krzysztof Łopatowski Cc: Leo Yan Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quan Zhou Cc: Ravi Bangoria Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tianyou Li Cc: Yujie Liu Cc: tanze Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 54 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bb48568b8101..713a250c7374 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3700,22 +3700,63 @@ struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *na return tp_format ? tep_find_common_field(tp_format, name) : NULL; } +static bool out_of_bounds(const struct tep_format_field *field, int offset, int size, u32 raw_size) +{ + if (offset < 0) { + pr_warning("Negative trace point field offset %d in %s\n", + offset, field->name); + return true; + } + if (size < 0) { + pr_warning("Negative trace point field size %d in %s\n", + size, field->name); + return true; + } + if ((u32)offset + (u32)size > raw_size) { + pr_warning("Out of bound tracepoint field (%s) offset %d size %d in %u\n", + field->name, offset, size, raw_size); + return true; + } + return false; +} + void *perf_sample__rawptr(struct perf_sample *sample, const char *name) { struct tep_format_field *field = evsel__field(sample->evsel, name); - int offset; + int offset, size; if (!field) return NULL; offset = field->offset; - + size = field->size; if (field->flags & TEP_FIELD_IS_DYNAMIC) { - offset = *(int *)(sample->raw_data + field->offset); - offset &= 0xffff; - if (tep_field_is_relative(field->flags)) + int dynamic_data; + + if (out_of_bounds(field, offset, 4, sample->raw_size)) + return NULL; + + dynamic_data = *(int *)(sample->raw_data + field->offset); + + if (sample->evsel->needs_swap) + dynamic_data = bswap_32(dynamic_data); + + offset = dynamic_data & 0xffff; + size = (dynamic_data >> 16) & 0xffff; + + if (tep_field_is_relative(field->flags)) { + /* + * Newer kernel feature: Relative offsets (__rel_loc). + * If the relative flag is set, the parsed offset is not + * absolute from the start of the record. Instead, it is + * relative to the *end* of the dynamic field descriptor + * itself. + */ offset += field->offset + field->size; + } } + if (out_of_bounds(field, offset, size, sample->raw_size)) + return NULL; return sample->raw_data + offset; } @@ -3726,6 +3767,9 @@ u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sam u64 value; void *ptr = sample->raw_data + field->offset; + if (out_of_bounds(field, field->offset, field->size, sample->raw_size)) + return 0; + switch (field->size) { case 1: return *(u8 *)ptr; From 9416008e5462cf7a1da62bbaca915f483cea8b77 Mon Sep 17 00:00:00 2001 From: Swapnil Sapkal Date: Wed, 20 May 2026 10:20:15 +0000 Subject: [PATCH 1305/5214] perf sched stats: Fix SIGCHLD vs pause() race in schedstat_record() If the profiled workload exits very quickly, SIGCHLD can be delivered and consumed by the empty signal handler before the process enters pause(), causing an indefinite hang. Fix this with a simpler approach: - The signal handler now sets a 'volatile sig_atomic_t done' flag. Reset 'done' before registering signal handlers so that an early signal during setup is not discarded by a later reset. - Replace pause() with a loop that checks 'done' and uses waitpid(WNOHANG) to detect child exit without blocking. This handles both workload mode (child exits) and system-wide mode (user sends SIGINT/SIGTERM). Using WNOHANG avoids the SA_RESTART problem where a blocking waitpid() would auto-restart and ignore the done flag if the child doesn't exit on signal. Suggested-by: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Swapnil Sapkal Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Cc: Ravi Bangoria Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9d73c7043182..4f54a08e6672 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -3746,8 +3747,11 @@ static int process_synthesized_schedstat_event(const struct perf_tool *tool, return 0; } +static volatile sig_atomic_t done; + static void sighandler(int sig __maybe_unused) { + done = 1; } static int enable_sched_schedstats(int *reset) @@ -3807,6 +3811,7 @@ static int perf_sched__schedstat_record(struct perf_sched *sched, .mode = PERF_DATA_MODE_WRITE, }; + done = 0; signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); signal(SIGTERM, sighandler); @@ -3891,8 +3896,11 @@ static int perf_sched__schedstat_record(struct perf_sched *sched, if (argc) evlist__start_workload(evlist); - /* wait for signal */ - pause(); + while (!done) { + if (argc && waitpid(evlist->workload.pid, NULL, WNOHANG) > 0) + break; + sleep(1); + } if (reset) { err = disable_sched_schedstat(); From 5b8c90e06622308ff4b6c14260baee6fad064289 Mon Sep 17 00:00:00 2001 From: Swapnil Sapkal Date: Wed, 20 May 2026 10:20:16 +0000 Subject: [PATCH 1306/5214] perf sched stats: Fix SIGCHLD vs pause() race in schedstat_live() perf_sched__schedstat_live() has the same lost-wakeup race as perf_sched__schedstat_record(): a short-lived workload's SIGCHLD can be consumed by the signal handler before pause() is entered, hanging the process. Apply the same fix: replace pause() with a loop checking the 'done' flag and using waitpid(WNOHANG) for the workload case. Suggested-by: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Swapnil Sapkal Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Cc: Ravi Bangoria Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 4f54a08e6672..812a1b0d56d6 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -4743,6 +4743,7 @@ static int perf_sched__schedstat_live(struct perf_sched *sched, int reset = 0; int err = 0; + done = 0; signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); signal(SIGTERM, sighandler); @@ -4788,8 +4789,11 @@ static int perf_sched__schedstat_live(struct perf_sched *sched, if (argc) evlist__start_workload(evlist); - /* wait for signal */ - pause(); + while (!done) { + if (argc && waitpid(evlist->workload.pid, NULL, WNOHANG) > 0) + break; + sleep(1); + } if (reset) { err = disable_sched_schedstat(); From 35c9fb22000f1ce10c0f3627d30fa8763e721719 Mon Sep 17 00:00:00 2001 From: Swapnil Sapkal Date: Wed, 20 May 2026 10:20:17 +0000 Subject: [PATCH 1307/5214] perf lock contention: Fix SIGCHLD vs pause() race in __cmd_contention() __cmd_contention() suffers from the same lost-wakeup race as the perf sched stats paths: SIGCHLD can be consumed by the signal handler before pause() is entered, hanging the process. Apply the same fix: replace pause() with a loop checking the 'done' flag and using waitpid(WNOHANG) for the workload case. Suggested-by: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Swapnil Sapkal Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Cc: Ravi Bangoria Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 064b3aa4bad7..94a8c35abb0b 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1915,8 +1916,11 @@ static int __cmd_report(bool display_info) return err; } +static volatile sig_atomic_t done; + static void sighandler(int sig __maybe_unused) { + done = 1; } static int check_lock_contention_options(const struct option *options, @@ -2054,6 +2058,7 @@ static int __cmd_contention(int argc, const char **argv) goto out_delete; } + done = 0; signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); signal(SIGTERM, sighandler); @@ -2121,8 +2126,11 @@ static int __cmd_contention(int argc, const char **argv) if (argc) evlist__start_workload(con.evlist); - /* wait for signal */ - pause(); + while (!done) { + if (argc && waitpid(con.evlist->workload.pid, NULL, WNOHANG) > 0) + break; + sleep(1); + } lock_contention_stop(); lock_contention_read(&con); From 537609924c43715e39a41762d3e3d3c7c534bb71 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:26 -0700 Subject: [PATCH 1308/5214] perf trace beauty: Make beauty generated C code standalone .o files Previously, builtin-trace.c directly included 15 embedded C files (e.g. trace/beauty/mmap.c and fsconfig_arrays.c), which in turn depend on dozens of generated beauty script arrays. To satisfy these embedded inclusions, the global Makefile.perf would define all the generator variables/rules and include them in the prepare umbrella target, choking parallel build startup. Furthermore, tools/perf/util/syscalltbl.c included its own generated mapper, and util/env.c conditionally included arch_errno_names.c inline, splitting consumers across directories and preventing clean Make encapsulation. Refactor the framework to achieve better encapsulation: 1. Move util/syscalltbl.[ch] into trace/beauty/ to co-locate with all generated code consumers. 2. Create fsconfig.c and flatten embedded beauty .c files to compile as independent standalone objects via trace/beauty/Build, exporting their formatting functions via beauty.h and env.h. Switch arch_errno_names.o and syscalltbl.o assignments directly to perf-util-y and add an unconditional top-level recursive kbuild hook (perf-util-y += trace/beauty/) to compile them into libperf-util.a, resolving remote linkage for util/env.c, util/bpf-trace-summary.c, and standalone python extensions. 3. Bridge private opaque references (struct trace) securely via accessors trace__show_zeros() and trace__host(), avoiding header entanglements. 4. Consolidate all generator variables, script paths, and array generation rules entirely out of Makefile.perf and place them directly inside the exact local Build files where their output objects are compiled (trace/beauty/Build and trace/beauty/tracepoints/Build), binding prerequisites locally. Use directly inside generator recipes to guarantee dynamic directory creation before script redirection, and append across all rules to print clean, standardized GEN ... file.c output during compilation. 5. Clean up clean target to recursively remove the generated directory instead of relying on dozens of individual variables. This unchokes the "prepare" target parallel barrier, allows make to evaluate generation scripts purely locally where consumed, and flattens the tracepoint formatting architecture. Testing a parallel build (make -j28 all from scratch) shows improvements: Before: real 0m28.689s user 2m38.490s sys 0m30.148s After: real 0m27.642s user 2m32.356s sys 0m26.683s So reclaiming ~9.6 seconds of raw CPU time and over 1 full second off overall real-world build latency, by overlapping sub-make startup and avoiding top-level double-parsing overhead. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Build | 2 + tools/perf/Makefile.perf | 282 +----------------- tools/perf/builtin-trace.c | 32 +- tools/perf/trace/beauty/Build | 276 +++++++++++++++++ tools/perf/trace/beauty/arch_errno_names.c | 2 + tools/perf/trace/beauty/arch_errno_names.sh | 2 +- tools/perf/trace/beauty/beauty.h | 60 ++++ tools/perf/trace/beauty/eventfd.c | 6 +- tools/perf/trace/beauty/fsconfig.c | 5 + tools/perf/trace/beauty/futex_op.c | 5 +- tools/perf/trace/beauty/futex_val3.c | 5 +- tools/perf/trace/beauty/mmap.c | 24 +- tools/perf/trace/beauty/mode_t.c | 6 +- tools/perf/trace/beauty/msg_flags.c | 8 +- tools/perf/trace/beauty/open_flags.c | 2 + tools/perf/trace/beauty/perf_event_open.c | 21 +- tools/perf/trace/beauty/pid.c | 5 +- tools/perf/trace/beauty/sched_policy.c | 8 +- tools/perf/trace/beauty/seccomp.c | 12 +- tools/perf/trace/beauty/signum.c | 6 +- tools/perf/trace/beauty/socket_type.c | 6 +- .../perf/{util => trace/beauty}/syscalltbl.c | 0 .../perf/{util => trace/beauty}/syscalltbl.h | 0 tools/perf/trace/beauty/tracepoints/Build | 21 ++ tools/perf/trace/beauty/waitid_options.c | 8 +- tools/perf/util/Build | 4 +- tools/perf/util/bpf-trace-summary.c | 2 +- tools/perf/util/env.c | 4 - tools/perf/util/env.h | 1 + 29 files changed, 447 insertions(+), 368 deletions(-) create mode 100644 tools/perf/trace/beauty/fsconfig.c rename tools/perf/{util => trace/beauty}/syscalltbl.c (100%) rename tools/perf/{util => trace/beauty}/syscalltbl.h (100%) diff --git a/tools/perf/Build b/tools/perf/Build index b03cc59dabf8..e18c80a5c1bc 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -34,6 +34,8 @@ ifeq ($(CONFIG_LIBTRACEEVENT),y) perf-$(CONFIG_TRACE) += trace/beauty/ endif +perf-util-y += trace/beauty/ + perf-$(CONFIG_LIBELF) += builtin-probe.o perf-bench-y += bench/ diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 0aba14f22a06..c81797ceec42 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -509,225 +509,6 @@ arm64-sysreg-defs-clean: $(Q)$(MAKE) -C $(arm64_gen_sysreg_dir) O=$(arm64_gen_sysreg_outdir) \ prefix= subdir= clean > /dev/null -beauty_linux_dir := $(srctree)/tools/perf/trace/beauty/include/linux/ -beauty_uapi_linux_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/linux/ -beauty_uapi_sound_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/sound/ -beauty_arch_asm_dir := $(srctree)/tools/perf/trace/beauty/arch/x86/include/asm/ -beauty_x86_arch_asm_uapi_dir := $(srctree)/tools/perf/trace/beauty/arch/x86/include/uapi/asm/ - -linux_uapi_dir := $(srctree)/tools/include/uapi/linux -asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic -arch_asm_uapi_dir := $(srctree)/tools/arch/$(SRCARCH)/include/uapi/asm/ -x86_arch_asm_dir := $(srctree)/tools/arch/x86/include/asm/ - -beauty_outdir := $(OUTPUT)trace/beauty/generated -beauty_ioctl_outdir := $(beauty_outdir)/ioctl - -# Create output directory if not already present -$(shell [ -d '$(beauty_ioctl_outdir)' ] || mkdir -p '$(beauty_ioctl_outdir)') - -syscall_array := $(beauty_outdir)/syscalltbl.c -syscall_tbl := $(srctree)/tools/perf/trace/beauty/syscalltbl.sh -syscall_tbl_data := $(srctree)/tools/scripts/syscall.tbl \ - $(wildcard $(srctree)/tools/perf/arch/*/entry/syscalls/syscall*.tbl) - -$(syscall_array): $(syscall_tbl) $(syscall_tbl_data) - $(Q)$(SHELL) '$(syscall_tbl)' $(srctree)/tools $@ - -fs_at_flags_array := $(beauty_outdir)/fs_at_flags_array.c -fs_at_flags_tbl := $(srctree)/tools/perf/trace/beauty/fs_at_flags.sh - -$(fs_at_flags_array): $(beauty_uapi_linux_dir)/fcntl.h $(fs_at_flags_tbl) - $(Q)$(SHELL) '$(fs_at_flags_tbl)' $(beauty_uapi_linux_dir) > $@ - -clone_flags_array := $(beauty_outdir)/clone_flags_array.c -clone_flags_tbl := $(srctree)/tools/perf/trace/beauty/clone.sh - -$(clone_flags_array): $(beauty_uapi_linux_dir)/sched.h $(clone_flags_tbl) - $(Q)$(SHELL) '$(clone_flags_tbl)' $(beauty_uapi_linux_dir) > $@ - -drm_ioctl_array := $(beauty_ioctl_outdir)/drm_ioctl_array.c -drm_hdr_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/drm -drm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/drm_ioctl.sh - -$(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_tbl) - $(Q)$(SHELL) '$(drm_ioctl_tbl)' $(drm_hdr_dir) > $@ - -fadvise_advice_array := $(beauty_outdir)/fadvise_advice_array.c -fadvise_advice_tbl := $(srctree)/tools/perf/trace/beauty/fadvise.sh - -$(fadvise_advice_array): $(beauty_uapi_linux_dir)/fadvise.h $(fadvise_advice_tbl) - $(Q)$(SHELL) '$(fadvise_advice_tbl)' $(beauty_uapi_linux_dir) > $@ - -fsmount_arrays := $(beauty_outdir)/fsmount_arrays.c -fsmount_tbls := $(srctree)/tools/perf/trace/beauty/fsmount.sh - -$(fsmount_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsmount_tbls) - $(Q)$(SHELL) '$(fsmount_tbls)' $(beauty_uapi_linux_dir) > $@ - -fspick_arrays := $(beauty_outdir)/fspick_arrays.c -fspick_tbls := $(srctree)/tools/perf/trace/beauty/fspick.sh - -$(fspick_arrays): $(beauty_uapi_linux_dir)/mount.h $(fspick_tbls) - $(Q)$(SHELL) '$(fspick_tbls)' $(beauty_uapi_linux_dir) > $@ - -fsconfig_arrays := $(beauty_outdir)/fsconfig_arrays.c -fsconfig_tbls := $(srctree)/tools/perf/trace/beauty/fsconfig.sh - -$(fsconfig_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsconfig_tbls) - $(Q)$(SHELL) '$(fsconfig_tbls)' $(beauty_uapi_linux_dir) > $@ - -pkey_alloc_access_rights_array := $(beauty_outdir)/pkey_alloc_access_rights_array.c -asm_generic_hdr_dir := $(srctree)/tools/include/uapi/asm-generic/ -pkey_alloc_access_rights_tbl := $(srctree)/tools/perf/trace/beauty/pkey_alloc_access_rights.sh - -$(pkey_alloc_access_rights_array): $(asm_generic_hdr_dir)/mman-common.h $(pkey_alloc_access_rights_tbl) - $(Q)$(SHELL) '$(pkey_alloc_access_rights_tbl)' $(asm_generic_hdr_dir) > $@ - -sndrv_ctl_ioctl_array := $(beauty_ioctl_outdir)/sndrv_ctl_ioctl_array.c -sndrv_ctl_hdr_dir := $(srctree)/tools/include/uapi/sound -sndrv_ctl_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh - -$(sndrv_ctl_ioctl_array): $(beauty_uapi_sound_dir)/asound.h $(sndrv_ctl_ioctl_tbl) - $(Q)$(SHELL) '$(sndrv_ctl_ioctl_tbl)' $(beauty_uapi_sound_dir) > $@ - -sndrv_pcm_ioctl_array := $(beauty_ioctl_outdir)/sndrv_pcm_ioctl_array.c -sndrv_pcm_hdr_dir := $(srctree)/tools/include/uapi/sound -sndrv_pcm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh - -$(sndrv_pcm_ioctl_array): $(beauty_uapi_sound_dir)/asound.h $(sndrv_pcm_ioctl_tbl) - $(Q)$(SHELL) '$(sndrv_pcm_ioctl_tbl)' $(beauty_uapi_sound_dir) > $@ - -kcmp_type_array := $(beauty_outdir)/kcmp_type_array.c -kcmp_hdr_dir := $(srctree)/tools/include/uapi/linux/ -kcmp_type_tbl := $(srctree)/tools/perf/trace/beauty/kcmp_type.sh - -$(kcmp_type_array): $(kcmp_hdr_dir)/kcmp.h $(kcmp_type_tbl) - $(Q)$(SHELL) '$(kcmp_type_tbl)' $(kcmp_hdr_dir) > $@ - -kvm_ioctl_array := $(beauty_ioctl_outdir)/kvm_ioctl_array.c -kvm_hdr_dir := $(srctree)/tools/include/uapi/linux -kvm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/kvm_ioctl.sh - -$(kvm_ioctl_array): $(kvm_hdr_dir)/kvm.h $(kvm_ioctl_tbl) - $(Q)$(SHELL) '$(kvm_ioctl_tbl)' $(kvm_hdr_dir) > $@ - -socket_arrays := $(beauty_outdir)/socket.c -socket_tbl := $(srctree)/tools/perf/trace/beauty/socket.sh - -$(socket_arrays): $(linux_uapi_dir)/in.h $(beauty_linux_dir)/socket.h $(socket_tbl) - $(Q)$(SHELL) '$(socket_tbl)' $(linux_uapi_dir) $(beauty_linux_dir) > $@ - -sockaddr_arrays := $(beauty_outdir)/sockaddr.c -sockaddr_tbl := $(srctree)/tools/perf/trace/beauty/sockaddr.sh - -$(sockaddr_arrays): $(beauty_linux_dir)/socket.h $(sockaddr_tbl) - $(Q)$(SHELL) '$(sockaddr_tbl)' $(beauty_linux_dir) > $@ - -vhost_virtio_ioctl_array := $(beauty_ioctl_outdir)/vhost_virtio_ioctl_array.c -vhost_virtio_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/vhost_virtio_ioctl.sh - -$(vhost_virtio_ioctl_array): $(beauty_uapi_linux_dir)/vhost.h $(vhost_virtio_ioctl_tbl) - $(Q)$(SHELL) '$(vhost_virtio_ioctl_tbl)' $(beauty_uapi_linux_dir) > $@ - -perf_ioctl_array := $(beauty_ioctl_outdir)/perf_ioctl_array.c -perf_hdr_dir := $(srctree)/tools/include/uapi/linux -perf_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/perf_ioctl.sh - -$(perf_ioctl_array): $(perf_hdr_dir)/perf_event.h $(perf_ioctl_tbl) - $(Q)$(SHELL) '$(perf_ioctl_tbl)' $(perf_hdr_dir) > $@ - -madvise_behavior_array := $(beauty_outdir)/madvise_behavior_array.c -madvise_hdr_dir := $(srctree)/tools/include/uapi/asm-generic/ -madvise_behavior_tbl := $(srctree)/tools/perf/trace/beauty/madvise_behavior.sh - -$(madvise_behavior_array): $(madvise_hdr_dir)/mman-common.h $(madvise_behavior_tbl) - $(Q)$(SHELL) '$(madvise_behavior_tbl)' $(madvise_hdr_dir) > $@ - -mmap_flags_array := $(beauty_outdir)/mmap_flags_array.c -mmap_flags_tbl := $(srctree)/tools/perf/trace/beauty/mmap_flags.sh - -$(mmap_flags_array): $(linux_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman-common.h $(mmap_flags_tbl) - $(Q)$(SHELL) '$(mmap_flags_tbl)' $(linux_uapi_dir) $(asm_generic_uapi_dir) $(arch_asm_uapi_dir) > $@ - -mremap_flags_array := $(beauty_outdir)/mremap_flags_array.c -mremap_flags_tbl := $(srctree)/tools/perf/trace/beauty/mremap_flags.sh - -$(mremap_flags_array): $(linux_uapi_dir)/mman.h $(mremap_flags_tbl) - $(Q)$(SHELL) '$(mremap_flags_tbl)' $(linux_uapi_dir) > $@ - -mount_flags_array := $(beauty_outdir)/mount_flags_array.c -mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/mount_flags.sh - -$(mount_flags_array): $(beauty_uapi_linux_dir)/mount.h $(mount_flags_tbl) - $(Q)$(SHELL) '$(mount_flags_tbl)' $(beauty_uapi_linux_dir) > $@ - -move_mount_flags_array := $(beauty_outdir)/move_mount_flags_array.c -move_mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/move_mount_flags.sh - -$(move_mount_flags_array): $(beauty_uapi_linux_dir)/mount.h $(move_mount_flags_tbl) - $(Q)$(SHELL) '$(move_mount_flags_tbl)' $(beauty_uapi_linux_dir) > $@ - -mmap_prot_array := $(beauty_outdir)/mmap_prot_array.c -mmap_prot_tbl := $(srctree)/tools/perf/trace/beauty/mmap_prot.sh - -$(mmap_prot_array): $(asm_generic_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman-common.h $(mmap_prot_tbl) - $(Q)$(SHELL) '$(mmap_prot_tbl)' $(asm_generic_uapi_dir) $(arch_asm_uapi_dir) > $@ - -prctl_option_array := $(beauty_outdir)/prctl_option_array.c -prctl_option_tbl := $(srctree)/tools/perf/trace/beauty/prctl_option.sh - -$(prctl_option_array): $(beauty_uapi_linux_dir)/prctl.h $(prctl_option_tbl) - $(Q)$(SHELL) '$(prctl_option_tbl)' $(beauty_uapi_linux_dir) > $@ - -usbdevfs_ioctl_array := $(beauty_ioctl_outdir)/usbdevfs_ioctl_array.c -usbdevfs_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/usbdevfs_ioctl.sh - -$(usbdevfs_ioctl_array): $(beauty_uapi_linux_dir)/usbdevice_fs.h $(usbdevfs_ioctl_tbl) - $(Q)$(SHELL) '$(usbdevfs_ioctl_tbl)' $(beauty_uapi_linux_dir) > $@ - -x86_arch_prctl_code_array := $(beauty_outdir)/x86_arch_prctl_code_array.c -x86_arch_prctl_code_tbl := $(srctree)/tools/perf/trace/beauty/x86_arch_prctl.sh - -$(x86_arch_prctl_code_array): $(beauty_x86_arch_asm_uapi_dir)/prctl.h $(x86_arch_prctl_code_tbl) - $(Q)$(SHELL) '$(x86_arch_prctl_code_tbl)' $(beauty_x86_arch_asm_uapi_dir) > $@ - -x86_arch_irq_vectors_array := $(beauty_outdir)/x86_arch_irq_vectors_array.c -x86_arch_irq_vectors_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh - -$(x86_arch_irq_vectors_array): $(beauty_arch_asm_dir)/irq_vectors.h $(x86_arch_irq_vectors_tbl) - $(Q)$(SHELL) '$(x86_arch_irq_vectors_tbl)' $(beauty_arch_asm_dir) > $@ - -x86_arch_MSRs_array := $(beauty_outdir)/x86_arch_MSRs_array.c -x86_arch_MSRs_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_msr.sh - -$(x86_arch_MSRs_array): $(x86_arch_asm_dir)/msr-index.h $(x86_arch_MSRs_tbl) - $(Q)$(SHELL) '$(x86_arch_MSRs_tbl)' $(x86_arch_asm_dir) > $@ - -rename_flags_array := $(beauty_outdir)/rename_flags_array.c -rename_flags_tbl := $(srctree)/tools/perf/trace/beauty/rename_flags.sh - -$(rename_flags_array): $(beauty_uapi_linux_dir)/fs.h $(rename_flags_tbl) - $(Q)$(SHELL) '$(rename_flags_tbl)' $(beauty_uapi_linux_dir) > $@ - -arch_errno_name_array := $(beauty_outdir)/arch_errno_name_array.c -arch_errno_hdr_dir := $(srctree)/tools -arch_errno_tbl := $(srctree)/tools/perf/trace/beauty/arch_errno_names.sh - -$(arch_errno_name_array): $(arch_errno_tbl) - $(Q)$(SHELL) '$(arch_errno_tbl)' '$(patsubst -%,,$(CC))' $(arch_errno_hdr_dir) > $@ - -statx_mask_array := $(beauty_outdir)/statx_mask_array.c -statx_mask_tbl := $(srctree)/tools/perf/trace/beauty/statx_mask.sh - -$(statx_mask_array): $(beauty_uapi_linux_dir)/stat.h $(statx_mask_tbl) - $(Q)$(SHELL) '$(statx_mask_tbl)' $(beauty_uapi_linux_dir) > $@ - -sync_file_range_arrays := $(beauty_outdir)/sync_file_range_arrays.c -sync_file_range_tbls := $(srctree)/tools/perf/trace/beauty/sync_file_range.sh - -$(sync_file_range_arrays): $(beauty_uapi_linux_dir)/fs.h $(sync_file_range_tbls) - $(Q)$(SHELL) '$(sync_file_range_tbls)' $(beauty_uapi_linux_dir) > $@ TESTS_CORESIGHT_DIR := $(srctree)/tools/perf/tests/shell/coresight @@ -848,38 +629,6 @@ build-dir = $(or $(__build-dir),.) prepare: $(OUTPUT)PERF-VERSION-FILE archheaders \ arm64-sysreg-defs \ - $(syscall_array) \ - $(fs_at_flags_array) \ - $(clone_flags_array) \ - $(drm_ioctl_array) \ - $(fadvise_advice_array) \ - $(fsconfig_arrays) \ - $(fsmount_arrays) \ - $(fspick_arrays) \ - $(pkey_alloc_access_rights_array) \ - $(sndrv_pcm_ioctl_array) \ - $(sndrv_ctl_ioctl_array) \ - $(kcmp_type_array) \ - $(kvm_ioctl_array) \ - $(socket_arrays) \ - $(sockaddr_arrays) \ - $(vhost_virtio_ioctl_array) \ - $(madvise_behavior_array) \ - $(mmap_flags_array) \ - $(mmap_prot_array) \ - $(mremap_flags_array) \ - $(mount_flags_array) \ - $(move_mount_flags_array) \ - $(perf_ioctl_array) \ - $(prctl_option_array) \ - $(usbdevfs_ioctl_array) \ - $(x86_arch_irq_vectors_array) \ - $(x86_arch_MSRs_array) \ - $(x86_arch_prctl_code_array) \ - $(rename_flags_array) \ - $(arch_errno_name_array) \ - $(statx_mask_array) \ - $(sync_file_range_arrays) \ $(LIBAPI) \ $(LIBPERF) \ $(LIBSUBCMD) \ @@ -1299,35 +1048,8 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $( TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE \ $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ $(OUTPUT)util/intel-pt-decoder/inat-tables.c \ - $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ - $(OUTPUT)$(fadvise_advice_array) \ - $(OUTPUT)$(fsconfig_arrays) \ - $(OUTPUT)$(fsmount_arrays) \ - $(OUTPUT)$(fspick_arrays) \ - $(OUTPUT)$(madvise_behavior_array) \ - $(OUTPUT)$(mmap_flags_array) \ - $(OUTPUT)$(mmap_prot_array) \ - $(OUTPUT)$(mremap_flags_array) \ - $(OUTPUT)$(mount_flags_array) \ - $(OUTPUT)$(move_mount_flags_array) \ - $(OUTPUT)$(drm_ioctl_array) \ - $(OUTPUT)$(pkey_alloc_access_rights_array) \ - $(OUTPUT)$(sndrv_ctl_ioctl_array) \ - $(OUTPUT)$(sndrv_pcm_ioctl_array) \ - $(OUTPUT)$(kvm_ioctl_array) \ - $(OUTPUT)$(kcmp_type_array) \ - $(OUTPUT)$(socket_arrays) \ - $(OUTPUT)$(sockaddr_arrays) \ - $(OUTPUT)$(vhost_virtio_ioctl_array) \ - $(OUTPUT)$(perf_ioctl_array) \ - $(OUTPUT)$(prctl_option_array) \ - $(OUTPUT)$(usbdevfs_ioctl_array) \ - $(OUTPUT)$(x86_arch_irq_vectors_array) \ - $(OUTPUT)$(x86_arch_MSRs_array) \ - $(OUTPUT)$(x86_arch_prctl_code_array) \ - $(OUTPUT)$(rename_flags_array) \ - $(OUTPUT)$(arch_errno_name_array) \ - $(OUTPUT)$(sync_file_range_arrays) + $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c + $(Q)$(RM) -r $(OUTPUT)trace/beauty/generated $(call QUIET_CLEAN, Documentation) \ $(MAKE) -C $(DOC_DIR) O=$(OUTPUT) clean >/dev/null diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d4fafae853d4..0730c1d9f0b3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -60,12 +60,13 @@ #include "callchain.h" #include "print_binary.h" #include "string2.h" -#include "syscalltbl.h" +#include "trace/beauty/syscalltbl.h" #include "../perf.h" #include "trace_augment.h" #include "dwarf-regs.h" #include +#include #include #include #include @@ -235,6 +236,16 @@ struct trace { const char *uid_str; }; +bool trace__show_zeros(const struct trace *trace) +{ + return trace->show_zeros; +} + +struct machine *trace__host(const struct trace *trace) +{ + return trace->host; +} + static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused) { #ifdef HAVE_LIBBPF_SUPPORT @@ -777,10 +788,6 @@ static const char *fsmount_flags[] = { }; static DEFINE_STRARRAY(fsmount_flags, "FSMOUNT_"); -#include "trace/beauty/generated/fsconfig_arrays.c" - -static DEFINE_STRARRAY(fsconfig_cmds, "FSCONFIG_"); - static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", }; static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, "EPOLL_CTL_", 1); @@ -1129,21 +1136,6 @@ static bool syscall_arg__strtoul_btf_type(char *bf __maybe_unused, size_t size _ .parm = &strarray__##array, \ .show_zero = true, } -#include "trace/beauty/eventfd.c" -#include "trace/beauty/futex_op.c" -#include "trace/beauty/futex_val3.c" -#include "trace/beauty/mmap.c" -#include "trace/beauty/mode_t.c" -#include "trace/beauty/msg_flags.c" -#include "trace/beauty/open_flags.c" -#include "trace/beauty/perf_event_open.c" -#include "trace/beauty/pid.c" -#include "trace/beauty/sched_policy.c" -#include "trace/beauty/seccomp.c" -#include "trace/beauty/signum.c" -#include "trace/beauty/socket_type.c" -#include "trace/beauty/waitid_options.c" - static const struct syscall_fmt syscall_fmts[] = { { .name = "access", .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index 561590ee8cda..93cde93461a3 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build @@ -19,6 +19,23 @@ perf-y += socket.o perf-y += statx.o perf-y += sync_file_range.o perf-y += timespec.o +perf-util-y += syscalltbl.o +perf-y += fsconfig.o +perf-y += eventfd.o +perf-y += futex_op.o +perf-y += futex_val3.o +perf-y += mmap.o +perf-y += mode_t.o +perf-y += msg_flags.o +perf-y += open_flags.o +perf-y += perf_event_open.o +perf-y += pid.o +perf-y += sched_policy.o +perf-y += seccomp.o +perf-y += signum.o +perf-y += socket_type.o +perf-y += waitid_options.o +perf-util-y += arch_errno_names.o perf-y += tracepoints/ ifdef SHELLCHECK @@ -34,3 +51,262 @@ $(OUTPUT)%.shellcheck_log: % $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-y += $(SHELL_TEST_LOGS) + +beauty_linux_dir := $(srctree)/tools/perf/trace/beauty/include/linux/ +beauty_uapi_linux_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/linux/ +beauty_uapi_sound_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/sound/ +beauty_arch_asm_dir := $(srctree)/tools/perf/trace/beauty/arch/x86/include/asm/ +beauty_x86_arch_asm_uapi_dir := $(srctree)/tools/perf/trace/beauty/arch/x86/include/uapi/asm/ + +linux_uapi_dir := $(srctree)/tools/include/uapi/linux +asm_generic_uapi_dir := $(srctree)/tools/include/uapi/asm-generic +arch_asm_uapi_dir := $(srctree)/tools/arch/$(SRCARCH)/include/uapi/asm/ +x86_arch_asm_dir := $(srctree)/tools/arch/x86/include/asm/ + +beauty_outdir := $(OUTPUT)trace/beauty/generated +beauty_ioctl_outdir := $(beauty_outdir)/ioctl + +syscall_array := $(beauty_outdir)/syscalltbl.c +syscall_tbl := $(srctree)/tools/perf/trace/beauty/syscalltbl.sh +syscall_tbl_data := $(srctree)/tools/scripts/syscall.tbl \ + $(wildcard $(srctree)/tools/perf/arch/*/entry/syscalls/syscall*.tbl) + +$(syscall_array): $(syscall_tbl) $(syscall_tbl_data) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(syscall_tbl)' $(srctree)/tools $@ + +fs_at_flags_array := $(beauty_outdir)/fs_at_flags_array.c +fs_at_flags_tbl := $(srctree)/tools/perf/trace/beauty/fs_at_flags.sh + +$(fs_at_flags_array): $(beauty_uapi_linux_dir)/fcntl.h $(fs_at_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fs_at_flags_tbl)' $(beauty_uapi_linux_dir) > $@ + +clone_flags_array := $(beauty_outdir)/clone_flags_array.c +clone_flags_tbl := $(srctree)/tools/perf/trace/beauty/clone.sh + +$(clone_flags_array): $(beauty_uapi_linux_dir)/sched.h $(clone_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(clone_flags_tbl)' $(beauty_uapi_linux_dir) > $@ + +drm_ioctl_array := $(beauty_ioctl_outdir)/drm_ioctl_array.c +drm_hdr_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/drm +drm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/drm_ioctl.sh + +$(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(drm_ioctl_tbl)' $(drm_hdr_dir) > $@ + +fadvise_advice_array := $(beauty_outdir)/fadvise_advice_array.c +fadvise_advice_tbl := $(srctree)/tools/perf/trace/beauty/fadvise.sh + +$(fadvise_advice_array): $(beauty_uapi_linux_dir)/fadvise.h $(fadvise_advice_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fadvise_advice_tbl)' $(beauty_uapi_linux_dir) > $@ + +fsmount_arrays := $(beauty_outdir)/fsmount_arrays.c +fsmount_tbls := $(srctree)/tools/perf/trace/beauty/fsmount.sh + +$(fsmount_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsmount_tbls) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fsmount_tbls)' $(beauty_uapi_linux_dir) > $@ + +fspick_arrays := $(beauty_outdir)/fspick_arrays.c +fspick_tbls := $(srctree)/tools/perf/trace/beauty/fspick.sh + +$(fspick_arrays): $(beauty_uapi_linux_dir)/mount.h $(fspick_tbls) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fspick_tbls)' $(beauty_uapi_linux_dir) > $@ + +fsconfig_arrays := $(beauty_outdir)/fsconfig_arrays.c +fsconfig_tbls := $(srctree)/tools/perf/trace/beauty/fsconfig.sh + +$(fsconfig_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsconfig_tbls) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fsconfig_tbls)' $(beauty_uapi_linux_dir) > $@ + +pkey_alloc_access_rights_array := $(beauty_outdir)/pkey_alloc_access_rights_array.c +asm_generic_hdr_dir := $(srctree)/tools/include/uapi/asm-generic/ +pkey_alloc_access_rights_tbl := $(srctree)/tools/perf/trace/beauty/pkey_alloc_access_rights.sh + +$(pkey_alloc_access_rights_array): $(asm_generic_hdr_dir)/mman-common.h $(pkey_alloc_access_rights_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(pkey_alloc_access_rights_tbl)' $(asm_generic_hdr_dir) > $@ + +sndrv_ctl_ioctl_array := $(beauty_ioctl_outdir)/sndrv_ctl_ioctl_array.c +sndrv_ctl_hdr_dir := $(srctree)/tools/include/uapi/sound +sndrv_ctl_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/sndrv_ctl_ioctl.sh + +$(sndrv_ctl_ioctl_array): $(beauty_uapi_sound_dir)/asound.h $(sndrv_ctl_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(sndrv_ctl_ioctl_tbl)' $(beauty_uapi_sound_dir) > $@ + +sndrv_pcm_ioctl_array := $(beauty_ioctl_outdir)/sndrv_pcm_ioctl_array.c +sndrv_pcm_hdr_dir := $(srctree)/tools/include/uapi/sound +sndrv_pcm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/sndrv_pcm_ioctl.sh + +$(sndrv_pcm_ioctl_array): $(beauty_uapi_sound_dir)/asound.h $(sndrv_pcm_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(sndrv_pcm_ioctl_tbl)' $(beauty_uapi_sound_dir) > $@ + +kcmp_type_array := $(beauty_outdir)/kcmp_type_array.c +kcmp_hdr_dir := $(srctree)/tools/include/uapi/linux/ +kcmp_type_tbl := $(srctree)/tools/perf/trace/beauty/kcmp_type.sh + +$(kcmp_type_array): $(kcmp_hdr_dir)/kcmp.h $(kcmp_type_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(kcmp_type_tbl)' $(kcmp_hdr_dir) > $@ + +kvm_ioctl_array := $(beauty_ioctl_outdir)/kvm_ioctl_array.c +kvm_hdr_dir := $(srctree)/tools/include/uapi/linux +kvm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/kvm_ioctl.sh + +$(kvm_ioctl_array): $(kvm_hdr_dir)/kvm.h $(kvm_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(kvm_ioctl_tbl)' $(kvm_hdr_dir) > $@ + +socket_arrays := $(beauty_outdir)/socket.c +socket_tbl := $(srctree)/tools/perf/trace/beauty/socket.sh + +$(socket_arrays): $(linux_uapi_dir)/in.h $(beauty_linux_dir)/socket.h $(socket_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(socket_tbl)' $(linux_uapi_dir) $(beauty_linux_dir) > $@ + +sockaddr_arrays := $(beauty_outdir)/sockaddr.c +sockaddr_tbl := $(srctree)/tools/perf/trace/beauty/sockaddr.sh + +$(sockaddr_arrays): $(beauty_linux_dir)/socket.h $(sockaddr_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(sockaddr_tbl)' $(beauty_linux_dir) > $@ + +vhost_virtio_ioctl_array := $(beauty_ioctl_outdir)/vhost_virtio_ioctl_array.c +vhost_virtio_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/vhost_virtio_ioctl.sh + +$(vhost_virtio_ioctl_array): $(beauty_uapi_linux_dir)/vhost.h $(vhost_virtio_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(vhost_virtio_ioctl_tbl)' $(beauty_uapi_linux_dir) > $@ + +perf_ioctl_array := $(beauty_ioctl_outdir)/perf_ioctl_array.c +perf_hdr_dir := $(srctree)/tools/include/uapi/linux +perf_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/perf_ioctl.sh + +$(perf_ioctl_array): $(perf_hdr_dir)/perf_event.h $(perf_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(perf_ioctl_tbl)' $(perf_hdr_dir) > $@ + +madvise_behavior_array := $(beauty_outdir)/madvise_behavior_array.c +madvise_hdr_dir := $(srctree)/tools/include/uapi/asm-generic/ +madvise_behavior_tbl := $(srctree)/tools/perf/trace/beauty/madvise_behavior.sh + +$(madvise_behavior_array): $(madvise_hdr_dir)/mman-common.h $(madvise_behavior_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(madvise_behavior_tbl)' $(madvise_hdr_dir) > $@ + +mmap_flags_array := $(beauty_outdir)/mmap_flags_array.c +mmap_flags_tbl := $(srctree)/tools/perf/trace/beauty/mmap_flags.sh + +$(mmap_flags_array): $(linux_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman-common.h $(mmap_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(mmap_flags_tbl)' $(linux_uapi_dir) $(asm_generic_uapi_dir) $(arch_asm_uapi_dir) > $@ + +mremap_flags_array := $(beauty_outdir)/mremap_flags_array.c +mremap_flags_tbl := $(srctree)/tools/perf/trace/beauty/mremap_flags.sh + +$(mremap_flags_array): $(linux_uapi_dir)/mman.h $(mremap_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(mremap_flags_tbl)' $(linux_uapi_dir) > $@ + +mount_flags_array := $(beauty_outdir)/mount_flags_array.c +mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/mount_flags.sh + +$(mount_flags_array): $(beauty_uapi_linux_dir)/mount.h $(mount_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(mount_flags_tbl)' $(beauty_uapi_linux_dir) > $@ + +move_mount_flags_array := $(beauty_outdir)/move_mount_flags_array.c +move_mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/move_mount_flags.sh + +$(move_mount_flags_array): $(beauty_uapi_linux_dir)/mount.h $(move_mount_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(move_mount_flags_tbl)' $(beauty_uapi_linux_dir) > $@ + +mmap_prot_array := $(beauty_outdir)/mmap_prot_array.c +mmap_prot_tbl := $(srctree)/tools/perf/trace/beauty/mmap_prot.sh + +$(mmap_prot_array): $(asm_generic_uapi_dir)/mman.h $(asm_generic_uapi_dir)/mman-common.h $(mmap_prot_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(mmap_prot_tbl)' $(asm_generic_uapi_dir) $(arch_asm_uapi_dir) > $@ + +prctl_option_array := $(beauty_outdir)/prctl_option_array.c +prctl_option_tbl := $(srctree)/tools/perf/trace/beauty/prctl_option.sh + +$(prctl_option_array): $(beauty_uapi_linux_dir)/prctl.h $(prctl_option_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(prctl_option_tbl)' $(beauty_uapi_linux_dir) > $@ + +usbdevfs_ioctl_array := $(beauty_ioctl_outdir)/usbdevfs_ioctl_array.c +usbdevfs_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/usbdevfs_ioctl.sh + +$(usbdevfs_ioctl_array): $(beauty_uapi_linux_dir)/usbdevice_fs.h $(usbdevfs_ioctl_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(usbdevfs_ioctl_tbl)' $(beauty_uapi_linux_dir) > $@ + +x86_arch_prctl_code_array := $(beauty_outdir)/x86_arch_prctl_code_array.c +x86_arch_prctl_code_tbl := $(srctree)/tools/perf/trace/beauty/x86_arch_prctl.sh + +$(x86_arch_prctl_code_array): $(beauty_x86_arch_asm_uapi_dir)/prctl.h $(x86_arch_prctl_code_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(x86_arch_prctl_code_tbl)' $(beauty_x86_arch_asm_uapi_dir) > $@ + + +rename_flags_array := $(beauty_outdir)/rename_flags_array.c +rename_flags_tbl := $(srctree)/tools/perf/trace/beauty/rename_flags.sh + +$(rename_flags_array): $(beauty_uapi_linux_dir)/fs.h $(rename_flags_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(rename_flags_tbl)' $(beauty_uapi_linux_dir) > $@ + + +statx_mask_array := $(beauty_outdir)/statx_mask_array.c +statx_mask_tbl := $(srctree)/tools/perf/trace/beauty/statx_mask.sh + +$(statx_mask_array): $(beauty_uapi_linux_dir)/stat.h $(statx_mask_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(statx_mask_tbl)' $(beauty_uapi_linux_dir) > $@ + +sync_file_range_arrays := $(beauty_outdir)/sync_file_range_arrays.c +sync_file_range_tbls := $(srctree)/tools/perf/trace/beauty/sync_file_range.sh + +$(sync_file_range_arrays): $(beauty_uapi_linux_dir)/fs.h $(sync_file_range_tbls) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(sync_file_range_tbls)' $(beauty_uapi_linux_dir) > $@ + +$(OUTPUT)trace/beauty/syscalltbl.o: $(syscall_array) +$(OUTPUT)trace/beauty/fsconfig.o: $(fsconfig_arrays) +$(OUTPUT)trace/beauty/clone.o: $(clone_flags_array) +$(OUTPUT)trace/beauty/fs_at_flags.o: $(fs_at_flags_array) +$(OUTPUT)trace/beauty/fsmount.o: $(fsmount_arrays) +$(OUTPUT)trace/beauty/fspick.o: $(fspick_arrays) +$(OUTPUT)trace/beauty/ioctl.o: $(drm_ioctl_array) $(sndrv_pcm_ioctl_array) $(sndrv_ctl_ioctl_array) $(kvm_ioctl_array) $(vhost_virtio_ioctl_array) $(perf_ioctl_array) $(usbdevfs_ioctl_array) +$(OUTPUT)trace/beauty/kcmp.o: $(kcmp_type_array) +$(OUTPUT)trace/beauty/mmap.o: $(mmap_prot_array) $(mmap_flags_array) $(mremap_flags_array) $(madvise_behavior_array) +$(OUTPUT)trace/beauty/mount_flags.o: $(mount_flags_array) +$(OUTPUT)trace/beauty/move_mount.o: $(move_mount_flags_array) +$(OUTPUT)trace/beauty/pkey_alloc.o: $(pkey_alloc_access_rights_array) +$(OUTPUT)trace/beauty/prctl.o: $(prctl_option_array) +$(OUTPUT)trace/beauty/renameat.o: $(rename_flags_array) +$(OUTPUT)trace/beauty/sockaddr.o: $(sockaddr_arrays) +$(OUTPUT)trace/beauty/socket.o: $(socket_arrays) +$(OUTPUT)trace/beauty/statx.o: $(statx_mask_array) +$(OUTPUT)trace/beauty/sync_file_range.o: $(sync_file_range_arrays) +$(OUTPUT)trace/beauty/arch_prctl.o: $(x86_arch_prctl_code_array) + +arch_errno_name_array := $(beauty_outdir)/arch_errno_name_array.c +arch_errno_hdr_dir := $(srctree)/tools +arch_errno_tbl := $(srctree)/tools/perf/trace/beauty/arch_errno_names.sh + +$(arch_errno_name_array): $(arch_errno_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(arch_errno_tbl)' '$(patsubst -%,,$(CC))' $(arch_errno_hdr_dir) > $@ + +$(OUTPUT)trace/beauty/arch_errno_names.o: $(arch_errno_name_array) diff --git a/tools/perf/trace/beauty/arch_errno_names.c b/tools/perf/trace/beauty/arch_errno_names.c index ede031c3a9e0..156c6537e747 100644 --- a/tools/perf/trace/beauty/arch_errno_names.c +++ b/tools/perf/trace/beauty/arch_errno_names.c @@ -1 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "util/env.h" #include "trace/beauty/generated/arch_errno_name_array.c" diff --git a/tools/perf/trace/beauty/arch_errno_names.sh b/tools/perf/trace/beauty/arch_errno_names.sh index b22890b8d272..d48d8561a7bb 100755 --- a/tools/perf/trace/beauty/arch_errno_names.sh +++ b/tools/perf/trace/beauty/arch_errno_names.sh @@ -57,7 +57,7 @@ create_arch_errno_table_func() archlist="$1" default="$2" - printf 'static arch_syscalls__strerrno_t *\n' + printf 'arch_syscalls__strerrno_t *\n' printf 'arch_syscalls__strerrno_function(const char *arch)\n' printf '{\n' for arch in $archlist; do diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 0a07ad158f87..931c4a80fea9 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -35,6 +35,8 @@ bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret); bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret); struct trace; +bool trace__show_zeros(const struct trace *trace); +struct machine *trace__host(const struct trace *trace); struct thread; struct file { @@ -265,4 +267,62 @@ size_t open__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool sh void syscall_arg__set_ret_scnprintf(struct syscall_arg *arg, size_t (*ret_scnprintf)(char *bf, size_t size, struct syscall_arg *arg)); +extern struct strarray strarray__fsconfig_cmds; + +size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags + +size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op + +size_t syscall_arg__scnprintf_futex_val3(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_FUTEX_VAL3 syscall_arg__scnprintf_futex_val3 + +size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot + +extern struct strarray strarray__mmap_flags; + +size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags + +size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags + +size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior + +size_t syscall_arg__scnprintf_mode_t(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MODE_T syscall_arg__scnprintf_mode_t + +size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_MSG_FLAGS syscall_arg__scnprintf_msg_flags + +size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_PERF_FLAGS syscall_arg__scnprintf_perf_flags + +size_t syscall_arg__scnprintf_perf_event_attr(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_PERF_ATTR syscall_arg__scnprintf_perf_event_attr +#define SCA_PERF_ATTR_FROM_USER(argname) \ + { .scnprintf = SCA_PERF_ATTR, \ + .from_user = true, } + +size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_SCHED_POLICY syscall_arg__scnprintf_sched_policy + +size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_SECCOMP_OP syscall_arg__scnprintf_seccomp_op + +size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_SECCOMP_FLAGS syscall_arg__scnprintf_seccomp_flags + +size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_SIGNUM syscall_arg__scnprintf_signum + +size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type + +size_t syscall_arg__scnprintf_waitid_options(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_WAITID_OPTIONS syscall_arg__scnprintf_waitid_options + #endif /* _PERF_TRACE_BEAUTY_H */ diff --git a/tools/perf/trace/beauty/eventfd.c b/tools/perf/trace/beauty/eventfd.c index 4bab106213c6..18b661282834 100644 --- a/tools/perf/trace/beauty/eventfd.c +++ b/tools/perf/trace/beauty/eventfd.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #ifndef EFD_SEMAPHORE #define EFD_SEMAPHORE 1 #endif @@ -11,7 +13,7 @@ #define EFD_CLOEXEC 02000000 #endif -static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "EFD_"; @@ -35,5 +37,3 @@ static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, struct return printed; } - -#define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags diff --git a/tools/perf/trace/beauty/fsconfig.c b/tools/perf/trace/beauty/fsconfig.c new file mode 100644 index 000000000000..98aa05315673 --- /dev/null +++ b/tools/perf/trace/beauty/fsconfig.c @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" +#include "trace/beauty/generated/fsconfig_arrays.c" + +DEFINE_STRARRAY(fsconfig_cmds, "FSCONFIG_"); diff --git a/tools/perf/trace/beauty/futex_op.c b/tools/perf/trace/beauty/futex_op.c index 00365156782b..05d2111e504b 100644 --- a/tools/perf/trace/beauty/futex_op.c +++ b/tools/perf/trace/beauty/futex_op.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" #include #ifndef FUTEX_WAIT_BITSET @@ -17,7 +18,7 @@ #define FUTEX_CLOCK_REALTIME 256 #endif -static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "FUTEX_"; @@ -59,5 +60,3 @@ static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct sysc return printed; } - -#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op diff --git a/tools/perf/trace/beauty/futex_val3.c b/tools/perf/trace/beauty/futex_val3.c index 9114f7620571..fe4e82741ffc 100644 --- a/tools/perf/trace/beauty/futex_val3.c +++ b/tools/perf/trace/beauty/futex_val3.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" #include #ifndef FUTEX_BITSET_MATCH_ANY #define FUTEX_BITSET_MATCH_ANY 0xffffffff #endif -static size_t syscall_arg__scnprintf_futex_val3(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_futex_val3(char *bf, size_t size, struct syscall_arg *arg) { const char *prefix = "FUTEX_BITSET_"; unsigned int bitset = arg->val; @@ -15,5 +16,3 @@ static size_t syscall_arg__scnprintf_futex_val3(char *bf, size_t size, struct sy return scnprintf(bf, size, "%#xd", bitset); } - -#define SCA_FUTEX_VAL3 syscall_arg__scnprintf_futex_val3 diff --git a/tools/perf/trace/beauty/mmap.c b/tools/perf/trace/beauty/mmap.c index 3c5e97b93dd5..c8a4cd49c845 100644 --- a/tools/perf/trace/beauty/mmap.c +++ b/tools/perf/trace/beauty/mmap.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 #include +#include "trace/beauty/beauty.h" #include "trace/beauty/generated/mmap_prot_array.c" static DEFINE_STRARRAY(mmap_prot, "PROT_"); @@ -8,8 +9,7 @@ static size_t mmap__scnprintf_prot(unsigned long prot, char *bf, size_t size, bo { return strarray__scnprintf_flags(&strarray__mmap_prot, bf, size, show_prefix, prot); } - -static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, struct syscall_arg *arg) { unsigned long prot = arg->val; @@ -19,18 +19,18 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, struct sys return mmap__scnprintf_prot(prot, bf, size, arg->show_string_prefix); } -#define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot + #include "trace/beauty/generated/mmap_flags_array.c" -static DEFINE_STRARRAY(mmap_flags, "MAP_"); +DEFINE_STRARRAY(mmap_flags, "MAP_"); static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix) { return strarray__scnprintf_flags(&strarray__mmap_flags, bf, size, show_prefix, flags); } -static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, + struct syscall_arg *arg) { unsigned long flags = arg->val; @@ -40,7 +40,7 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, return mmap__scnprintf_flags(flags, bf, size, arg->show_string_prefix); } -#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags + #include "trace/beauty/generated/mremap_flags_array.c" static DEFINE_STRARRAY(mremap_flags, "MREMAP_"); @@ -50,7 +50,7 @@ static size_t mremap__scnprintf_flags(unsigned long flags, char *bf, size_t size return strarray__scnprintf_flags(&strarray__mremap_flags, bf, size, show_prefix, flags); } -static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, struct syscall_arg *arg) { unsigned long flags = arg->val; @@ -60,7 +60,7 @@ static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, struct return mremap__scnprintf_flags(flags, bf, size, arg->show_string_prefix); } -#define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags + static size_t madvise__scnprintf_behavior(int behavior, char *bf, size_t size) { @@ -73,10 +73,8 @@ static size_t madvise__scnprintf_behavior(int behavior, char *bf, size_t size) return scnprintf(bf, size, "%#", behavior); } -static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, + struct syscall_arg *arg) { return madvise__scnprintf_behavior(arg->val, bf, size); } - -#define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior diff --git a/tools/perf/trace/beauty/mode_t.c b/tools/perf/trace/beauty/mode_t.c index 29a8fadfb7f9..9304b0bf3094 100644 --- a/tools/perf/trace/beauty/mode_t.c +++ b/tools/perf/trace/beauty/mode_t.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include #include #include @@ -20,7 +22,7 @@ #define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) #endif -static size_t syscall_arg__scnprintf_mode_t(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_mode_t(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "S_"; @@ -67,5 +69,3 @@ static size_t syscall_arg__scnprintf_mode_t(char *bf, size_t size, struct syscal return printed; } - -#define SCA_MODE_T syscall_arg__scnprintf_mode_t diff --git a/tools/perf/trace/beauty/msg_flags.c b/tools/perf/trace/beauty/msg_flags.c index 2da581ff0c80..be7f82677cbf 100644 --- a/tools/perf/trace/beauty/msg_flags.c +++ b/tools/perf/trace/beauty/msg_flags.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include #include @@ -27,8 +29,8 @@ # define MSG_CMSG_CLOEXEC 0x40000000 #endif -static size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, + struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "MSG_"; @@ -72,5 +74,3 @@ static size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size, return printed; } - -#define SCA_MSG_FLAGS syscall_arg__scnprintf_msg_flags diff --git a/tools/perf/trace/beauty/open_flags.c b/tools/perf/trace/beauty/open_flags.c index 78f6566ef110..c2c7769e6595 100644 --- a/tools/perf/trace/beauty/open_flags.c +++ b/tools/perf/trace/beauty/open_flags.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include #include #include diff --git a/tools/perf/trace/beauty/perf_event_open.c b/tools/perf/trace/beauty/perf_event_open.c index 9f1ed989c775..c1c7445dcff9 100644 --- a/tools/perf/trace/beauty/perf_event_open.c +++ b/tools/perf/trace/beauty/perf_event_open.c @@ -1,4 +1,8 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" +#include "util/evsel_fprintf.h" +#include + #ifndef PERF_FLAG_FD_NO_GROUP # define PERF_FLAG_FD_NO_GROUP (1UL << 0) #endif @@ -15,8 +19,8 @@ # define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */ #endif -static size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size, + struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "PERF_"; @@ -43,7 +47,7 @@ static size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size, return printed; } -#define SCA_PERF_FLAGS syscall_arg__scnprintf_perf_flags + struct attr_fprintf_args { size_t size, printed; @@ -76,19 +80,14 @@ static size_t perf_event_attr___scnprintf(struct perf_event_attr *attr, char *bf static size_t syscall_arg__scnprintf_augmented_perf_event_attr(struct syscall_arg *arg, char *bf, size_t size) { - return perf_event_attr___scnprintf((void *)arg->augmented.args->value, bf, size, arg->trace->show_zeros); + return perf_event_attr___scnprintf((void *)arg->augmented.args->value, bf, size, + trace__show_zeros(arg->trace)); } -static size_t syscall_arg__scnprintf_perf_event_attr(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_perf_event_attr(char *bf, size_t size, struct syscall_arg *arg) { if (arg->augmented.args) return syscall_arg__scnprintf_augmented_perf_event_attr(arg, bf, size); return scnprintf(bf, size, "%#lx", arg->val); } - -#define SCA_PERF_ATTR syscall_arg__scnprintf_perf_event_attr -// 'argname' is just documentational at this point, to remove the previous comment with that info -#define SCA_PERF_ATTR_FROM_USER(argname) \ - { .scnprintf = SCA_PERF_ATTR, \ - .from_user = true, } diff --git a/tools/perf/trace/beauty/pid.c b/tools/perf/trace/beauty/pid.c index 8f9c9950f8ba..cca4a3a5d9bd 100644 --- a/tools/perf/trace/beauty/pid.c +++ b/tools/perf/trace/beauty/pid.c @@ -1,11 +1,14 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" +#include "util/machine.h" +#include "util/thread.h" size_t syscall_arg__scnprintf_pid(char *bf, size_t size, struct syscall_arg *arg) { int pid = arg->val; struct trace *trace = arg->trace; size_t printed = scnprintf(bf, size, "%d", pid); - struct thread *thread = machine__findnew_thread(trace->host, pid, pid); + struct thread *thread = machine__findnew_thread(trace__host(trace), pid, pid); if (thread != NULL) { if (!thread__comm_set(thread)) diff --git a/tools/perf/trace/beauty/sched_policy.c b/tools/perf/trace/beauty/sched_policy.c index 68aa59eeed8d..3fb6d9e0fae9 100644 --- a/tools/perf/trace/beauty/sched_policy.c +++ b/tools/perf/trace/beauty/sched_policy.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include /* @@ -14,8 +16,8 @@ #define SCHED_RESET_ON_FORK 0x40000000 #endif -static size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size, + struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "SCHED_"; @@ -46,5 +48,3 @@ static size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size, return printed; } - -#define SCA_SCHED_POLICY syscall_arg__scnprintf_sched_policy diff --git a/tools/perf/trace/beauty/seccomp.c b/tools/perf/trace/beauty/seccomp.c index 637722e2796b..f345c66c1bfa 100644 --- a/tools/perf/trace/beauty/seccomp.c +++ b/tools/perf/trace/beauty/seccomp.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #ifndef SECCOMP_SET_MODE_STRICT #define SECCOMP_SET_MODE_STRICT 0 #endif @@ -6,7 +8,7 @@ #define SECCOMP_SET_MODE_FILTER 1 #endif -static size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "SECCOMP_SET_MODE_"; @@ -24,14 +26,14 @@ static size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct sy return printed; } -#define SCA_SECCOMP_OP syscall_arg__scnprintf_seccomp_op + #ifndef SECCOMP_FILTER_FLAG_TSYNC #define SECCOMP_FILTER_FLAG_TSYNC 1 #endif -static size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size, + struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "SECCOMP_FILTER_FLAG_"; @@ -51,5 +53,3 @@ static size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size, return printed; } - -#define SCA_SECCOMP_FLAGS syscall_arg__scnprintf_seccomp_flags diff --git a/tools/perf/trace/beauty/signum.c b/tools/perf/trace/beauty/signum.c index 21220c56500a..6817e9febab9 100644 --- a/tools/perf/trace/beauty/signum.c +++ b/tools/perf/trace/beauty/signum.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include -static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "SIG"; @@ -53,5 +55,3 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal return scnprintf(bf, size, "%#x", sig); } - -#define SCA_SIGNUM syscall_arg__scnprintf_signum diff --git a/tools/perf/trace/beauty/socket_type.c b/tools/perf/trace/beauty/socket_type.c index bed8d5761ca8..059e3041d338 100644 --- a/tools/perf/trace/beauty/socket_type.c +++ b/tools/perf/trace/beauty/socket_type.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include #include @@ -18,7 +20,7 @@ #define SOCK_TYPE_MASK 0xf #endif -static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg) +size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "SOCK_"; @@ -59,5 +61,3 @@ static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct s return printed; } - -#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/trace/beauty/syscalltbl.c similarity index 100% rename from tools/perf/util/syscalltbl.c rename to tools/perf/trace/beauty/syscalltbl.c diff --git a/tools/perf/util/syscalltbl.h b/tools/perf/trace/beauty/syscalltbl.h similarity index 100% rename from tools/perf/util/syscalltbl.h rename to tools/perf/trace/beauty/syscalltbl.h diff --git a/tools/perf/trace/beauty/tracepoints/Build b/tools/perf/trace/beauty/tracepoints/Build index e35087fdd108..9924ad24a6f1 100644 --- a/tools/perf/trace/beauty/tracepoints/Build +++ b/tools/perf/trace/beauty/tracepoints/Build @@ -1,2 +1,23 @@ perf-y += x86_irq_vectors.o perf-y += x86_msr.o + +beauty_outdir := $(OUTPUT)trace/beauty/generated +beauty_arch_asm_dir := $(srctree)/tools/perf/trace/beauty/arch/x86/include/asm/ +x86_arch_asm_dir := $(srctree)/tools/arch/x86/include/asm/ + +x86_arch_irq_vectors_array := $(beauty_outdir)/x86_arch_irq_vectors_array.c +x86_arch_irq_vectors_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh + +$(x86_arch_irq_vectors_array): $(beauty_arch_asm_dir)/irq_vectors.h $(x86_arch_irq_vectors_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(x86_arch_irq_vectors_tbl)' $(beauty_arch_asm_dir) > $@ + +x86_arch_MSRs_array := $(beauty_outdir)/x86_arch_MSRs_array.c +x86_arch_MSRs_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_msr.sh + +$(x86_arch_MSRs_array): $(x86_arch_asm_dir)/msr-index.h $(x86_arch_MSRs_tbl) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(x86_arch_MSRs_tbl)' $(x86_arch_asm_dir) > $@ + +$(OUTPUT)trace/beauty/tracepoints/x86_irq_vectors.o: $(x86_arch_irq_vectors_array) +$(OUTPUT)trace/beauty/tracepoints/x86_msr.o: $(x86_arch_MSRs_array) diff --git a/tools/perf/trace/beauty/waitid_options.c b/tools/perf/trace/beauty/waitid_options.c index d4d10b33ba0e..78ead5df985a 100644 --- a/tools/perf/trace/beauty/waitid_options.c +++ b/tools/perf/trace/beauty/waitid_options.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: LGPL-2.1 +#include "trace/beauty/beauty.h" + #include #include -static size_t syscall_arg__scnprintf_waitid_options(char *bf, size_t size, - struct syscall_arg *arg) +size_t syscall_arg__scnprintf_waitid_options(char *bf, size_t size, + struct syscall_arg *arg) { bool show_prefix = arg->show_string_prefix; const char *prefix = "W"; @@ -25,5 +27,3 @@ static size_t syscall_arg__scnprintf_waitid_options(char *bf, size_t size, return printed; } - -#define SCA_WAITID_OPTIONS syscall_arg__scnprintf_waitid_options diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 2bb60f50f62d..797d7bc909be 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -75,7 +75,7 @@ perf-util-y += sample.o perf-util-y += sample-raw.o perf-util-y += s390-sample-raw.o perf-util-y += amd-sample-raw.o -perf-util-$(CONFIG_TRACE) += syscalltbl.o + perf-util-y += ordered-events.o perf-util-y += namespaces.o perf-util-y += comm.o @@ -440,3 +440,5 @@ $(OUTPUT)%.pylint_log: % $(Q)$(call echo-cmd,test)pylint "$<" > $@ || (cat $@ && rm $@ && false) perf-util-y += $(PYLINT_TEST_LOGS) + + diff --git a/tools/perf/util/bpf-trace-summary.c b/tools/perf/util/bpf-trace-summary.c index cf6e1e4402d5..9a31dbf06cbb 100644 --- a/tools/perf/util/bpf-trace-summary.c +++ b/tools/perf/util/bpf-trace-summary.c @@ -6,7 +6,7 @@ #include #include "dwarf-regs.h" /* for EM_HOST */ -#include "syscalltbl.h" +#include "trace/beauty/syscalltbl.h" #include "util/cgroup.h" #include "util/hashmap.h" #include "util/trace.h" diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 1e54e2c86360..20953ef7b9d8 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -635,10 +635,6 @@ const char *perf_env__arch(struct perf_env *env) return normalize_arch(arch_name); } -#if defined(HAVE_LIBTRACEEVENT) -#include "trace/beauty/arch_errno_names.c" -#endif - const char *perf_env__arch_strerrno(struct perf_env *env __maybe_unused, int err __maybe_unused) { #if defined(HAVE_LIBTRACEEVENT) diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index c7052ac1f856..739d884fc236 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -189,6 +189,7 @@ void cpu_cache_level__free(struct cpu_cache_level *cache); const char *perf_env__arch(struct perf_env *env); const char *perf_env__arch_strerrno(struct perf_env *env, int err); +arch_syscalls__strerrno_t *arch_syscalls__strerrno_function(const char *arch); const char *perf_env__cpuid(struct perf_env *env); const char *perf_env__raw_arch(struct perf_env *env); int perf_env__nr_cpus_avail(struct perf_env *env); From b9453fa7726a225de8f80279e6085e164b3fc5a1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:27 -0700 Subject: [PATCH 1309/5214] perf build: Decouple pmu-events from prepare umbrella target Currently, the $(LIBPMU_EVENTS_IN) sub-make depends on the massive "prepare" umbrella target. Because "prepare" depends on external libraries (libapi, libperf, etc.) as well as dozens of generated headers, make completely serializes the launch of the pmu-events sub-make behind some of those unrelated prerequisites. Since pmu-events is a large compilation unit, unblock its startup by binding it directly to only $(LIBPERF) instead of prepare. This allows background python generation scripts to overlap simultaneously with the rest of the build. Testing a parallel build (make -j28 clean all) shows improvements: Before: real 0m27.642s user 2m32.356s sys 0m26.683s After: real 0m22.254s user 2m32.810s sys 0m24.646s This reclaims over 5 full seconds of build latency (~19.5% overall reduction) by elevating average CPU concurrency from ~5.5 active cores up to ~8 active cores. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c81797ceec42..c66af4c825fd 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -550,7 +550,7 @@ build := -f $(srctree)/tools/build/Makefile.build dir=. obj $(PERF_IN): prepare FORCE $(Q)$(MAKE) $(build)=perf -$(LIBPMU_EVENTS_IN): FORCE prepare +$(LIBPMU_EVENTS_IN): FORCE $(LIBPERF) $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events $(LIBPMU_EVENTS): $(LIBPMU_EVENTS_IN) From 5af31e15732e343528058f987bc54175bb9a8588 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:28 -0700 Subject: [PATCH 1310/5214] perf build: Remove empty archheaders target Remove empty target that doesn't do anything. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c66af4c825fd..24581941e912 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -627,7 +627,7 @@ endif __build-dir = $(subst $(OUTPUT),,$(dir $@)) build-dir = $(or $(__build-dir),.) -prepare: $(OUTPUT)PERF-VERSION-FILE archheaders \ +prepare: $(OUTPUT)PERF-VERSION-FILE \ arm64-sysreg-defs \ $(LIBAPI) \ $(LIBPERF) \ @@ -1070,7 +1070,7 @@ FORCE: .PHONY: all install clean config-clean strip install-gtk .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope FORCE prepare -.PHONY: archheaders python_perf_target +.PHONY: python_perf_target endif # force_fixdep From 713eeb2279402758bbfba301be6fae62c729b34e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:29 -0700 Subject: [PATCH 1311/5214] perf build: Move BPF skeleton generation out of Makefile.perf Currently, the top-level Makefile.perf defines a massive global bpf-skel umbrella target that pre-compiles all 12+ BPF skeletons (%.skel.h) upfront before launching sub-makes. This forces unrelated sub-makes to serialize behind bpftool and clang BPF target evaluations, causing parallel build bottlenecks. Furthermore, bench_uprobe.bpf.c lived inside util/bpf_skel/, breaking conceptual directory encapsulation since it is consumed purely by bench/uprobe.c. Refactor the BPF skeletons to better achieve directory isolation: 1. Move tools/perf/util/bpf_skel/bench_uprobe.bpf.c directly into tools/perf/bench/bpf_skel/. 2. Extract the skeleton generation infrastructure out of Makefile.perf into a shared inclusion file tools/perf/bpf_skel.mak. 3. Include bpf_skel.mak locally inside tools/perf/util/Build and tools/perf/bench/Build and bind precise local prerequisites. 4. Safely synchronize the shared bpftool bootstrap and vmlinux.h targets via the conditional prepare: umbrella to avoid parallel sub-make races, while evaluating the actual skeletons completely locally on demand. A later patch will move these targets into bpf_skel.mak. 5. Export CLANG from the global Makefile to ensure accurate tool propagation. 6. Clean up Makefile.perf by stripping the global bpf-skel umbrella target and its SKELETONS list. While removing code from Makefile.perf generally helps build performance, the impact here is minimal. The main motivation for the change is to better encapsulate things in the build and simplify Makefile.perf that has around 50 lines removed. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 59 ++----------------- tools/perf/bench/Build | 6 ++ .../bpf_skel/bench_uprobe.bpf.c | 0 tools/perf/bench/uprobe.c | 2 +- tools/perf/bpf_skel.mak | 54 +++++++++++++++++ tools/perf/util/Build | 13 ++++ 6 files changed, 78 insertions(+), 56 deletions(-) rename tools/perf/{util => bench}/bpf_skel/bench_uprobe.bpf.c (100%) create mode 100644 tools/perf/bpf_skel.mak diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 24581941e912..373eae7fb72a 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -274,7 +274,7 @@ ifeq ($(PYLINT),1) PYLINT := $(shell which pylint 2> /dev/null) endif -export srctree OUTPUT RM CC CXX RUSTC LD AR CFLAGS CXXFLAGS RUST_FLAGS V BISON FLEX AWK +export srctree OUTPUT RM CC CXX RUSTC CLANG LD AR CFLAGS CXXFLAGS RUST_FLAGS V BISON FLEX AWK LIBBPF export HOSTCC HOSTLD HOSTAR HOSTCFLAGS SHELLCHECK MYPY PYLINT include $(srctree)/tools/build/Makefile.include @@ -632,8 +632,7 @@ prepare: $(OUTPUT)PERF-VERSION-FILE \ $(LIBAPI) \ $(LIBPERF) \ $(LIBSUBCMD) \ - $(LIBSYMBOL) \ - bpf-skel + $(LIBSYMBOL) ifdef LIBBPF_STATIC prepare: $(LIBBPF) @@ -914,44 +913,13 @@ python-clean: SKEL_OUT := $(abspath $(OUTPUT)util/bpf_skel) SKEL_TMP_OUT := $(abspath $(SKEL_OUT)/.tmp) -SKELETONS := $(SKEL_OUT)/bpf_prog_profiler.skel.h -SKELETONS += $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.skel.h -SKELETONS += $(SKEL_OUT)/bperf_cgroup.skel.h $(SKEL_OUT)/func_latency.skel.h -SKELETONS += $(SKEL_OUT)/off_cpu.skel.h $(SKEL_OUT)/lock_contention.skel.h -SKELETONS += $(SKEL_OUT)/kwork_trace.skel.h $(SKEL_OUT)/sample_filter.skel.h -SKELETONS += $(SKEL_OUT)/kwork_top.skel.h $(SKEL_OUT)/syscall_summary.skel.h -SKELETONS += $(SKEL_OUT)/bench_uprobe.skel.h -SKELETONS += $(SKEL_OUT)/augmented_raw_syscalls.skel.h $(SKEL_TMP_OUT) $(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT): $(Q)$(MKDIR) -p $@ ifeq ($(CONFIG_PERF_BPF_SKEL),y) +prepare: $(BPFTOOL) $(SKEL_OUT)/vmlinux.h BPFTOOL := $(SKEL_TMP_OUT)/bootstrap/bpftool -# Get Clang's default includes on this system, as opposed to those seen by -# '--target=bpf'. This fixes "missing" files on some architectures/distros, -# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc. -# -# Use '-idirafter': Don't interfere with include mechanics except where the -# build would have failed anyways. -define get_sys_includes -$(shell $(1) $(2) -v -E - &1 \ - | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \ -$(shell $(1) $(2) -dM -E - $@ - -bpf-skel: $(SKELETONS) - -.PRECIOUS: $(SKEL_TMP_OUT)/%.bpf.o - -else # CONFIG_PERF_BPF_SKEL - -bpf-skel: - endif # CONFIG_PERF_BPF_SKEL bpf-skel-clean: - $(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKELETONS) $(SKEL_OUT)/vmlinux.h + $(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKEL_OUT)/*.skel.h $(SKEL_OUT)/vmlinux.h $(OUTPUT)bench/bpf_skel/*.skel.h $(OUTPUT)bench/bpf_skel/.tmp pmu-events-clean: ifeq ($(OUTPUT),) diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build index b558ab98719f..67b76fe20ba6 100644 --- a/tools/perf/bench/Build +++ b/tools/perf/bench/Build @@ -24,3 +24,9 @@ perf-bench-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o perf-bench-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o perf-bench-$(CONFIG_NUMA) += numa.o + +ifeq ($(CONFIG_PERF_BPF_SKEL),y) +include $(srctree)/tools/perf/bpf_skel.mak + +$(OUTPUT)bench/uprobe.o: $(SKEL_OUT)/bench_uprobe.skel.h +endif diff --git a/tools/perf/util/bpf_skel/bench_uprobe.bpf.c b/tools/perf/bench/bpf_skel/bench_uprobe.bpf.c similarity index 100% rename from tools/perf/util/bpf_skel/bench_uprobe.bpf.c rename to tools/perf/bench/bpf_skel/bench_uprobe.bpf.c diff --git a/tools/perf/bench/uprobe.c b/tools/perf/bench/uprobe.c index 89697ff788ef..616873bca243 100644 --- a/tools/perf/bench/uprobe.c +++ b/tools/perf/bench/uprobe.c @@ -44,7 +44,7 @@ static const char * const bench_uprobe_usage[] = { }; #ifdef HAVE_BPF_SKEL -#include "bpf_skel/bench_uprobe.skel.h" +#include "bench/bpf_skel/bench_uprobe.skel.h" #define bench_uprobe__attach_uprobe(prog) \ skel->links.prog = bpf_program__attach_uprobe_opts(/*prog=*/skel->progs.prog, \ diff --git a/tools/perf/bpf_skel.mak b/tools/perf/bpf_skel.mak new file mode 100644 index 000000000000..aa04d8b3ee07 --- /dev/null +++ b/tools/perf/bpf_skel.mak @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +# Shared BPF Skeleton Generator Rules + +include $(srctree)/tools/scripts/Makefile.include + +# Shared foundational tooling always lives in util/bpf_skel +SKEL_TOOL_OUT := $(abspath $(OUTPUT)util/bpf_skel) +SKEL_TOOL_TMP_OUT := $(abspath $(SKEL_TOOL_OUT)/.tmp) + +# Component specific output lives in $(dir)/bpf_skel +SKEL_OUT := $(abspath $(OUTPUT)$(dir)/bpf_skel) +SKEL_TMP_OUT := $(abspath $(SKEL_OUT)/.tmp) + +ifeq ($(CONFIG_PERF_BPF_SKEL),y) +BPFTOOL := $(SKEL_TOOL_TMP_OUT)/bootstrap/bpftool +VMLINUX_H := $(SKEL_TOOL_OUT)/vmlinux.h + +define get_sys_includes +$(shell $(1) $(2) -v -E - &1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \ +$(shell $(1) $(2) -dM -E - $@ + +.PRECIOUS: $(SKEL_TMP_OUT)/%.bpf.o +endif # CONFIG_PERF_BPF_SKEL diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 797d7bc909be..4bbc78b1f741 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -441,4 +441,17 @@ $(OUTPUT)%.pylint_log: % perf-util-y += $(PYLINT_TEST_LOGS) +ifeq ($(CONFIG_PERF_BPF_SKEL),y) +include $(srctree)/tools/perf/bpf_skel.mak +$(OUTPUT)util/bpf_ftrace.o: $(SKEL_OUT)/func_latency.skel.h +$(OUTPUT)util/bpf-filter.o: $(SKEL_OUT)/sample_filter.skel.h +$(OUTPUT)util/bpf_kwork_top.o: $(SKEL_OUT)/kwork_top.skel.h +$(OUTPUT)util/bpf_off_cpu.o: $(SKEL_OUT)/off_cpu.skel.h +$(OUTPUT)util/bpf-trace-summary.o: $(SKEL_OUT)/syscall_summary.skel.h +$(OUTPUT)util/bpf_counter_cgroup.o: $(SKEL_OUT)/bperf_cgroup.skel.h +$(OUTPUT)util/bpf_trace_augment.o: $(SKEL_OUT)/augmented_raw_syscalls.skel.h +$(OUTPUT)util/bpf_counter.o: $(SKEL_OUT)/bpf_prog_profiler.skel.h $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.skel.h +$(OUTPUT)util/bpf_lock_contention.o: $(SKEL_OUT)/lock_contention.skel.h +$(OUTPUT)util/bpf_kwork.o: $(SKEL_OUT)/kwork_trace.skel.h +endif From 9798ac0128daf35cb52ca648b1d93f0d40bd0cf5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:30 -0700 Subject: [PATCH 1312/5214] perf build: Encapsulate vmlinux.h and bpftool in bpf_skel.mak Currently, bpftool and vmlinux.h are prerequisites of the top-level prepare target in Makefile.perf. This unnecessarily blocks the massive parallel C compilation of libraries (perf-util, perf-ui, pmu-events) during the initial startup phase. Move all bpftool and vmlinux.h generation rules down into tools/perf/bpf_skel.mak to encapsulate BPF tooling completely within the skeleton framework. Remove them entirely from prepare to unblock immediate parallel build execution. To prevent parallel sub-makes (perf-util and perf-bench) from racing to build shared prerequisites concurrently, while maintaining strict directory encapsulation without top-level inclusions, serialize bench after the util static archive finishes using an order-only prerequisite: $(LIBPERF_BENCH_IN): FORCE prepare | $(LIBPERF_UTIL) Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 2 +- tools/perf/Makefile.perf | 61 +++++--------------------------------- tools/perf/bpf_skel.mak | 47 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 7a0e372824c6..8437c3f72125 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -681,7 +681,7 @@ ifeq ($(BUILD_BPF_SKEL),1) endif ifndef GEN_VMLINUX_H - VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h + VMLINUX_H_FILE=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h endif ifndef NO_LIBUNWIND diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 373eae7fb72a..ec0e91bedce1 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -274,7 +274,7 @@ ifeq ($(PYLINT),1) PYLINT := $(shell which pylint 2> /dev/null) endif -export srctree OUTPUT RM CC CXX RUSTC CLANG LD AR CFLAGS CXXFLAGS RUST_FLAGS V BISON FLEX AWK LIBBPF +export srctree OUTPUT RM CC CXX RUSTC CLANG LD AR CFLAGS CXXFLAGS RUST_FLAGS V BISON FLEX AWK LIBBPF READELF export HOSTCC HOSTLD HOSTAR HOSTCFLAGS SHELLCHECK MYPY PYLINT include $(srctree)/tools/build/Makefile.include @@ -324,7 +324,7 @@ else FEATURE_DUMP_EXPORT := $(realpath $(FEATURES_DUMP)) endif -export prefix bindir sharedir sysconfdir DESTDIR +export prefix bindir sharedir sysconfdir DESTDIR VMLINUX_H_FILE # sparse is architecture-neutral, which means that we need to tell it # explicitly what architecture to check for. Fix this up for yours.. @@ -556,7 +556,9 @@ $(LIBPMU_EVENTS_IN): FORCE $(LIBPERF) $(LIBPMU_EVENTS): $(LIBPMU_EVENTS_IN) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $< -$(LIBPERF_BENCH_IN): FORCE prepare +# The $(LIBPERF_UTIL) dependency is to ensure bpftool and vmlinux.h +# aren't racily built for bench/bpf_skel/bench_uprobe.bpf.c +$(LIBPERF_BENCH_IN): FORCE prepare | $(LIBPERF_UTIL) $(Q)$(MAKE) $(build)=perf-bench $(LIBPERF_BENCH): $(LIBPERF_BENCH_IN) @@ -911,60 +913,11 @@ $(INSTALL_DOC_TARGETS): python-clean: $(python-clean) -SKEL_OUT := $(abspath $(OUTPUT)util/bpf_skel) -SKEL_TMP_OUT := $(abspath $(SKEL_OUT)/.tmp) - -$(SKEL_TMP_OUT) $(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT): +$(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT): $(Q)$(MKDIR) -p $@ -ifeq ($(CONFIG_PERF_BPF_SKEL),y) -prepare: $(BPFTOOL) $(SKEL_OUT)/vmlinux.h -BPFTOOL := $(SKEL_TMP_OUT)/bootstrap/bpftool - -$(BPFTOOL): | $(SKEL_TMP_OUT) - $(Q)CFLAGS= $(MAKE) -C ../bpf/bpftool \ - OUTPUT=$(SKEL_TMP_OUT)/ bootstrap - -# Paths to search for a kernel to generate vmlinux.h from. -VMLINUX_BTF_ELF_PATHS ?= $(if $(O),$(O)/vmlinux) \ - $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \ - ../../vmlinux \ - /boot/vmlinux-$(shell uname -r) - -# Paths to BTF information. -VMLINUX_BTF_BTF_PATHS ?= /sys/kernel/btf/vmlinux - -# Filter out kernels that don't exist or without a BTF section. -VMLINUX_BTF_ELF_ABSPATHS ?= $(abspath $(wildcard $(VMLINUX_BTF_ELF_PATHS))) -VMLINUX_BTF_PATHS ?= $(shell for file in $(VMLINUX_BTF_ELF_ABSPATHS); \ - do \ - if [ -f $$file ] && ($(READELF) -S "$$file" | grep -q .BTF); \ - then \ - echo "$$file"; \ - fi; \ - done) \ - $(wildcard $(VMLINUX_BTF_BTF_PATHS)) - -# Select the first as the source of vmlinux.h. -VMLINUX_BTF ?= $(firstword $(VMLINUX_BTF_PATHS)) - -ifeq ($(VMLINUX_H),) - ifeq ($(VMLINUX_BTF),) - $(error Missing bpftool input for generating vmlinux.h) - endif -endif - -$(SKEL_OUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) $(VMLINUX_H) -ifeq ($(VMLINUX_H),) - $(QUIET_GEN)$(BPFTOOL) btf dump file $< format c > $@ -else - $(Q)cp "$(VMLINUX_H)" $@ -endif - -endif # CONFIG_PERF_BPF_SKEL - bpf-skel-clean: - $(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TMP_OUT) $(SKEL_OUT)/*.skel.h $(SKEL_OUT)/vmlinux.h $(OUTPUT)bench/bpf_skel/*.skel.h $(OUTPUT)bench/bpf_skel/.tmp + $(Q)$(MAKE) -f bpf_skel.mak clean pmu-events-clean: ifeq ($(OUTPUT),) diff --git a/tools/perf/bpf_skel.mak b/tools/perf/bpf_skel.mak index aa04d8b3ee07..30924ad140c4 100644 --- a/tools/perf/bpf_skel.mak +++ b/tools/perf/bpf_skel.mak @@ -35,6 +35,47 @@ ifneq ($(WERROR),0) CLANG_OPTIONS += -Werror endif +$(BPFTOOL): + $(Q)mkdir -p $(SKEL_TOOL_TMP_OUT) + $(Q)CFLAGS= $(MAKE) -C ../bpf/bpftool OUTPUT=$(SKEL_TOOL_TMP_OUT)/ bootstrap + +# Paths to search for a kernel to generate vmlinux.h from. +VMLINUX_BTF_ELF_PATHS ?= $(if $(O),$(O)/vmlinux) \ + $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \ + ../../vmlinux \ + /boot/vmlinux-$(shell uname -r) + +# Paths to BTF information. +VMLINUX_BTF_BTF_PATHS ?= /sys/kernel/btf/vmlinux + +# Filter out kernels that don't exist or without a BTF section. +VMLINUX_BTF_ELF_ABSPATHS ?= $(abspath $(wildcard $(VMLINUX_BTF_ELF_PATHS))) +VMLINUX_BTF_PATHS ?= $(shell for file in $(VMLINUX_BTF_ELF_ABSPATHS); \ + do \ + if [ -f $$file ] && ($(READELF) -S "$$file" | grep -q .BTF); \ + then \ + echo "$$file"; \ + fi; \ + done) \ + $(wildcard $(VMLINUX_BTF_BTF_PATHS)) + +# Select the first as the source of vmlinux.h. +VMLINUX_BTF ?= $(firstword $(VMLINUX_BTF_PATHS)) + +ifeq ($(VMLINUX_H_FILE),) + ifeq ($(VMLINUX_BTF),) + $(error Missing bpftool input for generating vmlinux.h) + endif +endif + +$(VMLINUX_H): $(VMLINUX_BTF) $(BPFTOOL) $(VMLINUX_H_FILE) + $(call rule_mkdir) +ifeq ($(VMLINUX_H_FILE),) + $(QUIET_GEN)$(BPFTOOL) btf dump file $< format c > $@ +else + $(Q)cp "$(VMLINUX_H_FILE)" $@ +endif + # Consolidated Pattern rule for $(dir)/bpf_skel/ $(SKEL_TMP_OUT)/%.bpf.o: $(srctree)/tools/perf/$(dir)/bpf_skel/%.bpf.c $(LIBBPF) $(VMLINUX_H) $(OUTPUT)PERF-VERSION-FILE util/bpf_skel/perf_version.h $(call rule_mkdir) @@ -51,4 +92,10 @@ $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o $(BPFTOOL) $(Q)$(BPFTOOL) gen skeleton $< > $@ .PRECIOUS: $(SKEL_TMP_OUT)/%.bpf.o + endif # CONFIG_PERF_BPF_SKEL + +clean: + $(call QUIET_CLEAN, bpf-skel) $(RM) -r $(SKEL_TOOL_TMP_OUT) $(OUTPUT)bench/bpf_skel/.tmp $(SKEL_TOOL_OUT)/*.skel.h $(OUTPUT)bench/bpf_skel/*.skel.h $(SKEL_TOOL_OUT)/vmlinux.h + +.PHONY: clean From 32969ef6e3e1979a903a53a2287dffa6fdcb74d4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:31 -0700 Subject: [PATCH 1313/5214] perf build: Pre-generate BPF skeleton tooling during umbrella prepare phase Currently, BPF skeleton generation rules (bpf_skel.mak) are evaluated as part of util/Build. However, because LIBPERF_UTIL_IN explicitly depends on the top-level static libbpf archive, Make completely blocked the execution of bpftool bootstrap and skeleton generation until libbpf finished compiling midway through the build. Since bpftool bootstrap compiles its own independent copy of libbpf.a, it does not depend on the top-level libbpf target. Decouple early skeleton tooling generation by attaching bpf-skel-prepare to the umbrella prepare target, exporting CONFIG_PERF_BPF_SKEL to ensure accurate feature propagation. This allows Make to compile bpftool and dump vmlinux.h in the background at build startup, eliminating the initial sub-make startup bottleneck before BPF object compilation while keeping 100% of tooling rules perfectly encapsulated in bpf_skel.mak. Provide an empty fallback target to ensure builds succeed when BPF skeletons are disabled. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 8 ++++++-- tools/perf/bpf_skel.mak | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index ec0e91bedce1..876065a02de6 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -275,7 +275,7 @@ ifeq ($(PYLINT),1) endif export srctree OUTPUT RM CC CXX RUSTC CLANG LD AR CFLAGS CXXFLAGS RUST_FLAGS V BISON FLEX AWK LIBBPF READELF -export HOSTCC HOSTLD HOSTAR HOSTCFLAGS SHELLCHECK MYPY PYLINT +export HOSTCC HOSTLD HOSTAR HOSTCFLAGS SHELLCHECK MYPY PYLINT CONFIG_PERF_BPF_SKEL include $(srctree)/tools/build/Makefile.include @@ -629,8 +629,12 @@ endif __build-dir = $(subst $(OUTPUT),,$(dir $@)) build-dir = $(or $(__build-dir),.) +bpf-skel-prepare: + $(Q)$(MAKE) -f bpf_skel.mak bpf-skel-prepare + prepare: $(OUTPUT)PERF-VERSION-FILE \ arm64-sysreg-defs \ + bpf-skel-prepare \ $(LIBAPI) \ $(LIBPERF) \ $(LIBSUBCMD) \ @@ -971,7 +975,7 @@ FORCE: .PHONY: all install clean config-clean strip install-gtk .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell -.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope FORCE prepare +.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope FORCE prepare bpf-skel-prepare .PHONY: python_perf_target endif # force_fixdep diff --git a/tools/perf/bpf_skel.mak b/tools/perf/bpf_skel.mak index 30924ad140c4..7704e7e635d8 100644 --- a/tools/perf/bpf_skel.mak +++ b/tools/perf/bpf_skel.mak @@ -15,6 +15,10 @@ ifeq ($(CONFIG_PERF_BPF_SKEL),y) BPFTOOL := $(SKEL_TOOL_TMP_OUT)/bootstrap/bpftool VMLINUX_H := $(SKEL_TOOL_OUT)/vmlinux.h +.PHONY: bpf-skel-prepare +bpf-skel-prepare: $(BPFTOOL) $(VMLINUX_H) + @: + define get_sys_includes $(shell $(1) $(2) -v -E - &1 \ | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \ @@ -93,6 +97,10 @@ $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o $(BPFTOOL) .PRECIOUS: $(SKEL_TMP_OUT)/%.bpf.o +else # CONFIG_PERF_BPF_SKEL +.PHONY: bpf-skel-prepare +bpf-skel-prepare: + @: endif # CONFIG_PERF_BPF_SKEL clean: From 96e808e5671dc6a72a392dd7696bbad92b3171f8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:32 -0700 Subject: [PATCH 1314/5214] perf build: Move libsymbol dependency out of prepare step The prepare step is a large serialization point before parallel sub-makes build the perf tool. The libsymbol headers are used in the bench and util libraries. Move the libsymbol dependency out of the prepare step and into the dependencies for those targets to avoid it being a source of serialization in the prepare step. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 876065a02de6..96a68723109f 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -558,7 +558,7 @@ $(LIBPMU_EVENTS): $(LIBPMU_EVENTS_IN) # The $(LIBPERF_UTIL) dependency is to ensure bpftool and vmlinux.h # aren't racily built for bench/bpf_skel/bench_uprobe.bpf.c -$(LIBPERF_BENCH_IN): FORCE prepare | $(LIBPERF_UTIL) +$(LIBPERF_BENCH_IN): FORCE prepare $(LIBSYMBOL) | $(LIBPERF_UTIL) $(Q)$(MAKE) $(build)=perf-bench $(LIBPERF_BENCH): $(LIBPERF_BENCH_IN) @@ -576,7 +576,7 @@ $(LIBPERF_UI_IN): FORCE prepare $(LIBPERF_UI): $(LIBPERF_UI_IN) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $< -$(LIBPERF_UTIL_IN): FORCE prepare +$(LIBPERF_UTIL_IN): FORCE prepare $(LIBSYMBOL) $(Q)$(MAKE) $(build)=perf-util $(LIBPERF_UTIL): $(LIBPERF_UTIL_IN) @@ -637,8 +637,7 @@ prepare: $(OUTPUT)PERF-VERSION-FILE \ bpf-skel-prepare \ $(LIBAPI) \ $(LIBPERF) \ - $(LIBSUBCMD) \ - $(LIBSYMBOL) + $(LIBSUBCMD) ifdef LIBBPF_STATIC prepare: $(LIBBPF) From 29331b2edb861e1a7e2fadc8d06403c460eb6064 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:33 -0700 Subject: [PATCH 1315/5214] perf build: Remove redundant libbpf feature check for static builds By default, the perf tool compiles and statically links against its own internal copy of libbpf (tools/lib/bpf/libbpf.a), setting LIBBPF_STATIC=1. Despite this static linkage, Makefile.config unconditionally executed $(call feature_check,libbpf), which forced a synchronous sub-make fork during AST parsing to detect dynamic system libbpf libraries. As noted in the internal Makefile comments, this check was executed purely so that running `make VF=1` would display the detection status of system libbpf. During standard builds without LIBBPF_DYNAMIC=1, the detection result was entirely ignored. Wrap the libbpf feature check inside LIBBPF_DYNAMIC=1 so Make avoids the redundant sub-make fork overhead during standard static builds. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 8437c3f72125..26de7528d6ce 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -575,10 +575,8 @@ ifndef NO_LIBELF ifndef NO_LIBBPF ifeq ($(feature-bpf), 1) - # detecting libbpf without LIBBPF_DYNAMIC, so make VF=1 shows libbpf detection status - $(call feature_check,libbpf) - ifdef LIBBPF_DYNAMIC + $(call feature_check,libbpf) ifeq ($(feature-libbpf), 1) EXTLIBS += -lbpf CFLAGS += -DHAVE_LIBBPF_SUPPORT From bb922345eec1fdef5b094bbb739caa136c8e8213 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:34 -0700 Subject: [PATCH 1316/5214] perf pmu-events: Split big_c_string storage into standalone compilation unit Currently, jevents.py emits both the massive 2.8 MB big_c_string literal and tens of thousands of compact_pmu_event struct arrays into a single pmu-events.c compilation unit. Compiling this giant file takes ~2.2 seconds on a single CPU core during Kbuild startup. Refactor jevents.py to emit big_c_string into a dedicated pmu-events-string.c compilation unit. This allows Kbuild to compile pmu-events.o and pmu-events-string.o simultaneously in parallel across two separate CPU cores, preserving 100% string deduplication and zero dynamic ELF relocations while cutting C compilation latency in half. Add pmu-events-string.c to tools/perf/.gitignore to ensure in-tree Kbuild runs do not leave untracked generated files in the working directory. To guarantee 100% backward compatibility with GNU Make 4.0+ (avoiding the Make 4.3+ grouped target &: syntax which causes older Make versions like 4.2.1 to spawn multiple concurrent jevents.py processes during parallel builds), implement a robust dependency chaining pattern: $(PMU_EVENTS_C): $(JEVENTS_DEPS) $(PMU_EVENTS_STRING_C): $(PMU_EVENTS_C) @: This ensures jevents.py is invoked exactly once. If jevents.py aborts early, Make's .DELETE_ON_ERROR: purges pmu-events.c, guaranteeing that subsequent Make invocations correctly re-execute the script and overwrite pmu-events-string.c. In jevents.py, explicitly close output_file first and output_string_file second at the tail of main() to guarantee that pmu-events-string.c receives a filesystem timestamp greater than or equal to pmu-events.c, completely avoiding redundant incremental rebuilds. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/.gitignore | 1 + tools/perf/Makefile.perf | 4 ++-- tools/perf/pmu-events/Build | 16 +++++++++++++++- tools/perf/pmu-events/jevents.py | 23 +++++++++++++++++++---- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 0f9451a6e39c..3b968c5158b8 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -38,6 +38,7 @@ arch/*/include/generated/ trace/beauty/generated/ pmu-events/arch/common/common/legacy-cache.json pmu-events/pmu-events.c +pmu-events/pmu-events-string.c pmu-events/jevents pmu-events/metric_test.log pmu-events/empty-pmu-events.log diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 96a68723109f..b8a81c9749a8 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -925,7 +925,7 @@ bpf-skel-clean: pmu-events-clean: ifeq ($(OUTPUT),) $(call QUIET_CLEAN, pmu-events) $(RM) \ - pmu-events/pmu-events.c \ + pmu-events/pmu-events*.c \ pmu-events/metric_test.log \ pmu-events/test-empty-pmu-events.c \ pmu-events/empty-pmu-events.log @@ -933,7 +933,7 @@ ifeq ($(OUTPUT),) -name 'extra-metricgroups.json' -delete else # When an OUTPUT directory is present, clean up the copied pmu-events/arch directory. $(call QUIET_CLEAN, pmu-events) $(RM) -r $(OUTPUT)pmu-events/arch \ - $(OUTPUT)pmu-events/pmu-events.c \ + $(OUTPUT)pmu-events/pmu-events*.c \ $(OUTPUT)pmu-events/metric_test.log \ $(OUTPUT)pmu-events/test-empty-pmu-events.c \ $(OUTPUT)pmu-events/empty-pmu-events.log diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index dc1df2d57ddc..95172a2a851f 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -1,7 +1,12 @@ EMPTY_PMU_EVENTS_C = pmu-events/empty-pmu-events.c # pmu-events.c will be generated by jevents.py or copied from EMPTY_PMU_EVENTS_C PMU_EVENTS_C = $(OUTPUT)pmu-events/pmu-events.c +PMU_EVENTS_STRING_C = $(OUTPUT)pmu-events/pmu-events-string.c + pmu-events-y += pmu-events.o +ifneq ($(NO_JEVENTS),1) +pmu-events-y += pmu-events-string.o +endif # pmu-events.c file is generated in the OUTPUT directory so it needs a # separate rule to depend on it properly @@ -9,6 +14,10 @@ $(OUTPUT)pmu-events/pmu-events.o: $(PMU_EVENTS_C) $(call rule_mkdir) $(call if_changed_dep,cc_o_c) +$(OUTPUT)pmu-events/pmu-events-string.o: $(PMU_EVENTS_STRING_C) + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + # Message for $(call echo-cmd,cp), possibly remove the src file from # the destination to save space in the build log. quiet_cmd_cp = COPY $(patsubst %$<,%,$@) <- $< @@ -118,6 +127,7 @@ CUR_OUT_JSON := $(shell [ -d $(OUT_DIR) ] && find $(OUT_DIR) -type f) # Things in the OUTPUT directory but shouldn't be there as computed by # OUT_JSON and GEN_JSON. + ORPHAN_FILES := $(filter-out $(OUT_JSON) $(GEN_JSON),$(CUR_OUT_JSON)) # Message for $(call echo-cmd,mkd). There is already a mkdir message @@ -224,6 +234,10 @@ endif # and inputs are dependencies. $(PMU_EVENTS_C): $(JEVENTS_DEPS) $(call rule_mkdir) - $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) $(OUT_DIR) $@ + $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) \ + $(OUT_DIR) $(PMU_EVENTS_C) $(PMU_EVENTS_STRING_C) + +$(PMU_EVENTS_STRING_C): $(PMU_EVENTS_C) + @: endif # ifeq ($(NO_JEVENTS),1) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 3a1bcdcdc685..7344940e776a 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -1422,6 +1422,8 @@ such as "arm/cortex-a34".''', ) ap.add_argument( 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) + ap.add_argument( + 'output_string_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=None) _args = ap.parse_args() _args.output_file.write(f""" @@ -1463,10 +1465,20 @@ struct pmu_table_entry { ftw(arch_path, [], preprocess_one_file) _bcs.compute() - _args.output_file.write('static const char *const big_c_string =\n') - for s in _bcs.big_string: - _args.output_file.write(s) - _args.output_file.write(';\n\n') + if not _args.output_string_file: + _args.output_file.write('static const char *const big_c_string =\n') + for s in _bcs.big_string: + _args.output_file.write(s) + _args.output_file.write(';\n\n') + else: + _args.output_string_file.write('/* SPDX-License-Identifier: GPL-2.0 */\n') + _args.output_string_file.write('/* Autogenerated by jevents.py */\n') + _args.output_string_file.write('extern const char big_c_string[];\n') + _args.output_string_file.write('const char big_c_string[] =\n') + for s in _bcs.big_string: + _args.output_string_file.write(s) + _args.output_string_file.write(';\n') + _args.output_file.write('extern const char big_c_string[];\n\n') for arch in archs: arch_path = f'{_args.starting_dir}/{arch}' ftw(arch_path, [], process_one_file) @@ -1476,6 +1488,9 @@ struct pmu_table_entry { print_mapping_table(archs) print_system_mapping_table() print_metricgroups() + _args.output_file.close() + if _args.output_string_file: + _args.output_string_file.close() if __name__ == '__main__': main() From 90a815a22abfd3d90436c021a5e8f7b8207df2be Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:37 -0700 Subject: [PATCH 1317/5214] perf pmu-events: Convert recursive shell assignments and macros to Make built-ins In pmu-events/Build, ZENS, ARMS, and INTELS were assigned using recursive assignment (=), and model_name/vendor_name were evaluated using shell macros (echo ... | sed ...). Because these variables were expanded inside the COPY_RULE dependency evaluation loop across hundreds of PMU JSON files and inside every metric generation recipe, Kbuild continuously re-executed 'ls', 'grep', and 'sed' shell forks thousands of times during AST parsing and execution. Convert ZENS, ARMS, and INTELS to simply expanded variables (:=) and replace model_name/vendor_name with pure GNU Make string functions. This guarantees Make executes directory probing shell forks exactly once when the Build file is parsed and evaluates path macros purely in memory, completely eliminating over 7,800 redundant sub-processes during build startup. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/Build | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index 95172a2a851f..372773b998e4 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -56,12 +56,12 @@ $(LEGACY_CACHE_JSON): $(LEGACY_CACHE_PY) # Python to generate architectural metrics GEN_METRIC_DEPS := pmu-events/metric.py pmu-events/common_metrics.py # Functions to extract the model from an extra-metrics.json or extra-metricgroups.json path. -model_name = $(shell echo $(1)|sed -e 's@.\+/\(.*\)/extra-metric.*\.json@\1@') -vendor_name = $(shell echo $(1)|sed -e 's@.\+/\(.*\)/[^/]*/extra-metric.*\.json@\1@') +model_name = $(notdir $(patsubst %/,%,$(dir $(1)))) +vendor_name = $(notdir $(patsubst %/,%,$(dir $(patsubst %/,%,$(dir $(1)))))) ifeq ($(JEVENTS_ARCH),$(filter $(JEVENTS_ARCH),x86 all)) # Generate AMD Json -ZENS = $(shell ls -d pmu-events/arch/x86/amdzen*) +ZENS := $(shell ls -d pmu-events/arch/x86/amdzen*) ZEN_METRICS = $(foreach x,$(ZENS),$(OUTPUT)$(x)/extra-metrics.json) ZEN_METRICGROUPS = $(foreach x,$(ZENS),$(OUTPUT)$(x)/extra-metricgroups.json) GEN_JSON += $(ZEN_METRICS) $(ZEN_METRICGROUPS) @@ -78,7 +78,7 @@ endif ifeq ($(JEVENTS_ARCH),$(filter $(JEVENTS_ARCH),arm64 all)) # Generate ARM Json -ARMS = $(shell ls -d pmu-events/arch/arm64/arm/*|grep -v cmn) +ARMS := $(shell ls -d pmu-events/arch/arm64/arm/*|grep -v cmn) ARM_METRICS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metrics.json) ARM_METRICGROUPS = $(foreach x,$(ARMS),$(OUTPUT)$(x)/extra-metricgroups.json) GEN_JSON += $(ARM_METRICS) $(ARM_METRICGROUPS) @@ -95,7 +95,7 @@ endif ifeq ($(JEVENTS_ARCH),$(filter $(JEVENTS_ARCH),x86 all)) # Generate Intel Json -INTELS = $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapfile.csv) +INTELS := $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapfile.csv) INTEL_METRICS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metrics.json) INTEL_METRICGROUPS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metricgroups.json) GEN_JSON += $(INTEL_METRICS) $(INTEL_METRICGROUPS) From 98e68cb7782347a07ab1a94f8f5b629c59df1a73 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:38 -0700 Subject: [PATCH 1318/5214] perf build: Convert llvm-config shell queries to simply expanded variables In Makefile.config, CFLAGS, CXXFLAGS, LIBLLVM, and EXTLIBS were assigned using recursive expansion or appended with raw $(shell $(LLVM_CONFIG) ...) calls. Because these variables were expanded during dependency evaluation across every single object file compilation rule, Kbuild continuously re-executed llvm-config forks nearly 200 times during incremental builds. Convert llvm-config shell queries to simply expanded variables (:=) to ensure Make evaluates LLVM compiler flags and library paths exactly once when Makefile.config is parsed, eliminating ~185 redundant sub-processes during build startup. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 26de7528d6ce..b56fa8419f7d 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -925,11 +925,14 @@ ifndef NO_LIBLLVM $(call feature_check,llvm-perf) ifeq ($(feature-llvm-perf), 1) CFLAGS += -DHAVE_LIBLLVM_SUPPORT - CFLAGS += $(shell $(LLVM_CONFIG) --cflags) - CXXFLAGS += -DHAVE_LIBLLVM_SUPPORT - CXXFLAGS += $(shell $(LLVM_CONFIG) --cxxflags) - LIBLLVM = $(shell $(LLVM_CONFIG) --libs all) $(shell $(LLVM_CONFIG) --system-libs) - EXTLIBS += -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) + LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --cflags 2>/dev/null) + LLVM_CXXFLAGS := $(shell $(LLVM_CONFIG) --cxxflags 2>/dev/null) + LLVM_LIBLLVM := $(shell $(LLVM_CONFIG) --libs all 2>/dev/null) $(shell $(LLVM_CONFIG) --system-libs 2>/dev/null) + LLVM_LIBDIR := $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) + CFLAGS += $(LLVM_CFLAGS) + CXXFLAGS += -DHAVE_LIBLLVM_SUPPORT $(LLVM_CXXFLAGS) + LIBLLVM := $(LLVM_LIBLLVM) + EXTLIBS += -L$(LLVM_LIBDIR) $(LIBLLVM) EXTLIBS += -lstdc++ $(call detected,CONFIG_LIBLLVM) else From 9f116f4b811b5846808e27507a3aa2824819edeb Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sun, 17 May 2026 23:29:03 -0700 Subject: [PATCH 1319/5214] perf tests: Add test for uncore event sorting Add a test for uncore event sorting matching multiple PMUs. Uncore PMUs may have a common prefix, like the PMUs uncore_imc_free_running_0 and uncore_imc_free_running_1 have a prefix of uncore_imc_free_running. Parsing an event group like "{data_read,data_write}" for those PMUs should result with two groups: "{uncore_imc_free_running_0/data_read/,uncore_imc_free_running_0/data_write/}, {uncore_imc_free_running_1/data_read/,uncore_imc_free_running_1/data_write/}" which means the evsels need resorting as when initially parsed the evsels are ordered with mixed PMUs: "{uncore_imc_free_running_0/data_read/,uncore_imc_free_running_1/data_read/, uncore_imc_free_running_0/data_write/,uncore_imc_free_running_1/data_write/}". Signed-off-by: Ian Rogers Tested-by: Zide Chen Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Collin Funk Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: German Gomez Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/tests.h | 1 + tools/perf/tests/uncore-event-sorting.c | 176 ++++++++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 tools/perf/tests/uncore-event-sorting.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index c2a67ce45941..66944a4f4968 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -3,6 +3,7 @@ perf-test-y += builtin-test.o perf-test-y += tests-scripts.o perf-test-y += parse-events.o +perf-test-y += uncore-event-sorting.o perf-test-y += dso-data.o perf-test-y += vmlinux-kallsyms.o perf-test-y += openat-syscall.o diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 06507066213b..f2c135891477 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -71,6 +71,7 @@ static struct test_suite *generic_tests[] = { &suite__basic_mmap, &suite__mem, &suite__parse_events, + &suite__uncore_event_sorting, &suite__expr, &suite__PERF_RECORD, &suite__pmu, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index f5f1238d1f7f..ee00518bf36f 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -177,6 +177,7 @@ DECLARE_SUITE(sigtrap); DECLARE_SUITE(event_groups); DECLARE_SUITE(symbols); DECLARE_SUITE(util); +DECLARE_SUITE(uncore_event_sorting); DECLARE_SUITE(subcmd_help); DECLARE_SUITE(kallsyms_split); diff --git a/tools/perf/tests/uncore-event-sorting.c b/tools/perf/tests/uncore-event-sorting.c new file mode 100644 index 000000000000..7d2fc304e21f --- /dev/null +++ b/tools/perf/tests/uncore-event-sorting.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include + +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "pmu.h" +#include "pmus.h" +#include "tests.h" + +struct match_state { + char *event1; + char *event2; +}; + +static char *clean_event_name(struct pmu_event_info *info) +{ + const char *name = info->name; + const char *pmu_name = info->pmu->name; + size_t pmu_len = strlen(pmu_name); + char *res; + size_t len; + + if (!strncmp(name, pmu_name, pmu_len) && name[pmu_len] == '/') + name += pmu_len + 1; + + res = strdup(name); + if (!res) + return NULL; + + len = strlen(res); + if (len > 0 && res[len - 1] == '/') + res[len - 1] = '\0'; + + return res; +} + +static int event_cb(void *state, struct pmu_event_info *info) +{ + struct match_state *m = state; + char *clean_name; + + if (m->event1 && m->event2) + return 1; + + clean_name = clean_event_name(info); + if (!clean_name) + return 0; + + if (!m->event1) { + m->event1 = clean_name; + } else { + if (strcmp(m->event1, clean_name)) { + m->event2 = clean_name; + return 1; + } + free(clean_name); + } + return 0; +} + +#define CHECK_COND(cond, text) \ +do { \ + if (!(cond)) { \ + pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + ret = TEST_FAIL; \ + goto out_err; \ + } \ +} while (0) + +#define CHECK_EQUAL(val, expected, text) \ +do { \ + if ((val) != (expected)) { \ + pr_debug("FAILED %s:%d %s (%d != %d)\n", \ + __FILE__, __LINE__, text, (val), (expected)); \ + ret = TEST_FAIL; \ + goto out_err; \ + } \ +} while (0) + +static int test__uncore_event_sorting(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + struct evlist *evlist = NULL; + struct parse_events_error err; + struct evsel *evsel; + struct perf_pmu *pmu = NULL; + char *pmu_prefix = NULL; + struct match_state m = { NULL, NULL }; + char buf[1024]; + int ret; + + parse_events_error__init(&err); + + while ((pmu = perf_pmus__scan(pmu)) != NULL) { + size_t len; + struct perf_pmu *sibling; + + if (pmu->is_core) + continue; + + len = pmu_name_len_no_suffix(pmu->name); + if (len == strlen(pmu->name)) + continue; + + sibling = pmu; + while ((sibling = perf_pmus__scan(sibling)) != NULL) { + if (sibling->is_core) + continue; + if (pmu_name_len_no_suffix(sibling->name) == len && + !strncmp(pmu->name, sibling->name, len)) + break; + } + + if (!sibling) + continue; + + m.event1 = m.event2 = NULL; + perf_pmu__for_each_event(pmu, false, &m, event_cb); + + if (m.event1 && m.event2) { + pmu_prefix = strndup(pmu->name, len); + break; + } + zfree(&m.event1); + } + + if (!pmu_prefix) { + pr_debug("No suitable uncore PMU found\n"); + ret = TEST_SKIP; + goto out_err; + } + + evlist = evlist__new(); + if (!evlist) { + ret = TEST_FAIL; + goto out_err; + } + + snprintf(buf, sizeof(buf), "{%s/%s/,%s/%s/}", pmu_prefix, m.event1, pmu_prefix, m.event2); + pr_debug("Parsing: %s\n", buf); + + ret = parse_events(evlist, buf, &err); + if (ret) { + pr_debug("parse_events failed\n"); + ret = TEST_FAIL; + goto out_err; + } + + CHECK_COND(evlist->core.nr_entries >= 4, "Number of events is >= 4"); + CHECK_EQUAL(evlist->core.nr_entries % 2, 0, "Number of events is a multiple of 2"); + + evlist__for_each_entry(evlist, evsel) { + struct evsel *next; + + if (!evsel__is_group_leader(evsel)) + continue; + + next = evsel__next(evsel); + CHECK_EQUAL(evsel->core.nr_members, 2, "Group size is 2"); + CHECK_COND(evsel->pmu == next->pmu, "PMU match"); + CHECK_COND(strstr(evsel__name(evsel), m.event1) != NULL, "First event name"); + CHECK_COND(strstr(evsel__name(next), m.event2) != NULL, "Second event name"); + } + ret = TEST_OK; + +out_err: + evlist__delete(evlist); + parse_events_error__exit(&err); + zfree(&pmu_prefix); + zfree(&m.event1); + zfree(&m.event2); + return ret; +} + +DEFINE_SUITE("Uncore event sorting", uncore_event_sorting); From b439c20b2fe1cfed2210e50c06006845ae424c48 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sun, 17 May 2026 23:29:04 -0700 Subject: [PATCH 1320/5214] perf arch x86 tests: Add test for topdown event sorting Add a test to capture the comment in tools/perf/arch/x86/util/evlist.c. Test that slots and topdown-retiring get appropriately sorted with respect to instructions when they're all specified together. When the PMU requires topdown event grouping (indicated by the pressence of the slots event) metric events should be after slots, which should be the group leader. Add a related test that when the slots event isn't given it is injected into the appropriate group. Signed-off-by: Ian Rogers Tested-by: Zide Chen Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Collin Funk Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: German Gomez Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/topdown.c | 166 +++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/topdown.c b/tools/perf/arch/x86/tests/topdown.c index 3ee4e5e71be3..221f2c4bbb61 100644 --- a/tools/perf/arch/x86/tests/topdown.c +++ b/tools/perf/arch/x86/tests/topdown.c @@ -75,4 +75,168 @@ static int test__x86_topdown(struct test_suite *test __maybe_unused, int subtest return ret; } -DEFINE_SUITE("x86 topdown", x86_topdown); +#define CHECK_COND(cond, text) \ +do { \ + if (!(cond)) { \ + pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + ret = TEST_FAIL; \ + goto out_err; \ + } \ +} while (0) + +#define CHECK_EQUAL(val, expected, text) \ +do { \ + if ((val) != (expected)) { \ + pr_debug("FAILED %s:%d %s (%d != %d)\n", \ + __FILE__, __LINE__, text, (val), (expected)); \ + ret = TEST_FAIL; \ + goto out_err; \ + } \ +} while (0) + +static int test_sort(const char *str, int expected_slots_group_size, + int expected_instructions_group_size) +{ + struct evlist *evlist = NULL; + struct parse_events_error err; + struct evsel *evsel; + int ret = TEST_FAIL; + bool slots_seen = false; + + parse_events_error__init(&err); + + evlist = evlist__new(); + if (!evlist) + goto out_err; + + if (parse_events(evlist, str, &err)) { + pr_debug("parse_events failed for %s\n", str); + goto out_err; + } + + evlist__for_each_entry(evlist, evsel) { + if (!evsel__is_group_leader(evsel)) + continue; + + if (strstr(evsel__name(evsel), "slots")) { + /* + * Slots as a leader means the PMU is for a perf metric + * group as the slots event isn't present when not. + */ + slots_seen = true; + CHECK_EQUAL(evsel->core.nr_members, expected_slots_group_size, + "slots group size"); + if (expected_slots_group_size == 3) { + struct evsel *next = evsel__next(evsel); + struct evsel *next2 = evsel__next(next); + + CHECK_COND(strstr(evsel__name(next), "instructions") != NULL, + "slots second event is instructions"); + CHECK_COND(strstr(evsel__name(next2), "topdown-retiring") != NULL, + "slots third event is topdown-retiring"); + } else if (expected_slots_group_size == 2) { + struct evsel *next = evsel__next(evsel); + + CHECK_COND(strstr(evsel__name(next), "topdown-retiring") != NULL, + "slots second event is topdown-retiring"); + } + } else if (strstr(evsel__name(evsel), "instructions")) { + CHECK_EQUAL(evsel->core.nr_members, expected_instructions_group_size, + "instructions group size"); + if (expected_instructions_group_size == 2) { + /* + * On Intel hybrid CPUs (e.g., Alder Lake/ + * Raptor Lake), E-cores (cpu_atom) do not + * support/enforce the slots event. When + * parsing event groups containing slots + * across all PMUs, slots is automatically + * filtered out from cpu_atom, leaving + * {cpu_atom/instructions/, + * cpu_atom/topdown-retiring/}. On cpu_atom, + * instructions correctly leads this group of + * 2 without slots reordering. + */ + struct evsel *next = evsel__next(evsel); + + CHECK_COND(strstr(evsel__name(next), "topdown-retiring") != NULL, + "instructions second event is topdown-retiring"); + } + } else if (strstr(evsel__name(evsel), "topdown-retiring")) { + /* + * A perf metric event where the PMU doesn't require + * slots as a leader. + */ + CHECK_EQUAL(evsel->core.nr_members, 1, "topdown-retiring group size"); + } else if (strstr(evsel__name(evsel), "cycles")) { + CHECK_EQUAL(evsel->core.nr_members, 1, "cycles group size"); + } + } + CHECK_COND(slots_seen, "slots seen"); + ret = TEST_OK; +out_err: + evlist__delete(evlist); + parse_events_error__exit(&err); + return ret; +} + +static int test__x86_topdown_sorting(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int ret; + + if (!topdown_sys_has_perf_metrics()) + return TEST_OK; + + ret = test_sort("{instructions,topdown-retiring,slots}", 3, 2); + TEST_ASSERT_EQUAL("all events in a group", ret, TEST_OK); + ret = test_sort("instructions,topdown-retiring,slots", 2, 1); + TEST_ASSERT_EQUAL("all events not in a group", ret, TEST_OK); + ret = test_sort("{instructions,slots},topdown-retiring", 2, 1); + TEST_ASSERT_EQUAL("slots event in a group but topdown metrics events outside the group", + ret, TEST_OK); + ret = test_sort("{instructions,slots},{topdown-retiring}", 2, 1); + TEST_ASSERT_EQUAL("slots event and topdown metrics events in two groups", + ret, TEST_OK); + ret = test_sort("{instructions,slots},cycles,topdown-retiring", 2, 1); + TEST_ASSERT_EQUAL("slots event and metrics event are not in a group and not adjacent", + ret, TEST_OK); + + return TEST_OK; +} + +static int test__x86_topdown_slots_injection(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int ret; + + if (!topdown_sys_has_perf_metrics()) + return TEST_OK; + + ret = test_sort("{instructions,topdown-retiring}", 3, 2); + TEST_ASSERT_EQUAL("all events in a group", ret, TEST_OK); + ret = test_sort("instructions,topdown-retiring", 2, 1); + TEST_ASSERT_EQUAL("all events not in a group", ret, TEST_OK); + ret = test_sort("{instructions},topdown-retiring", 2, 1); + TEST_ASSERT_EQUAL("event in a group but topdown metrics events outside the group", + ret, TEST_OK); + ret = test_sort("{instructions},{topdown-retiring}", 2, 1); + TEST_ASSERT_EQUAL("event and topdown metrics events in two groups", + ret, TEST_OK); + ret = test_sort("{instructions},cycles,topdown-retiring", 2, 1); + TEST_ASSERT_EQUAL("event and metrics event are not in a group and not adjacent", + ret, TEST_OK); + + return TEST_OK; +} + +static struct test_case x86_topdown_tests[] = { + TEST_CASE("topdown events", x86_topdown), + TEST_CASE("topdown sorting", x86_topdown_sorting), + TEST_CASE("topdown slots injection", x86_topdown_slots_injection), + { .name = NULL, } +}; + +struct test_suite suite__x86_topdown = { + .desc = "x86 topdown", + .test_cases = x86_topdown_tests, +}; From 85cc481af5ffa23809b7bb100aa774172369983c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 08:46:36 -0700 Subject: [PATCH 1321/5214] perf build: Prefix SCRIPTS with output directory to fix continuous rebuilds In Makefile.perf, ALL_PROGRAMS includes SCRIPTS (perf-archive, perf-iostat). However, unlike PROGRAMS and DLFILTERS, SCRIPTS was not prefixed with $(OUTPUT). During out-of-tree builds (or when O= is specified), Make checked for the unprefixed target 'tools/perf/perf-archive'. Since the actual script was installed into $(OUTPUT)perf-archive, Make concluded the target was missing and continuously re-executed the script installation rule on every single incremental build. Prefix SCRIPTS with $(OUTPUT) and update the static pattern rule to ensure Kbuild correctly tracks generated script prerequisites during incremental builds. Reviewed-by: Namhyung Kim Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Albert Ou Cc: Alexandre Chartre Cc: Alexandre Ghiti Cc: Andrii Nakryiko Cc: Ankur Arora Cc: Collin Funk Cc: Costa Shulyupin Cc: Daniel Borkmann Cc: Dapeng Mi Cc: David Sterba Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Eduard Zingerman Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kumar Kartikeya Dwivedi Cc: Leo Yan Cc: Markus Mayer Cc: Martin KaFai Lau Cc: Nathan Chancellor Cc: Nick Terrell Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Ricky Ringler Cc: Song Liu Cc: Swapnil Sapkal Cc: Thomas Falcon Cc: Tomas Glozar Cc: Yonghong Song Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index b8a81c9749a8..fc92d6ceac5b 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -405,7 +405,7 @@ python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT # Use the detected configuration -include $(OUTPUT).config-detected -SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) +SCRIPTS = $(addprefix $(OUTPUT),$(patsubst %.sh,%,$(SCRIPT_SH))) PROGRAMS += $(OUTPUT)perf @@ -592,8 +592,8 @@ $(GTK_IN): FORCE prepare $(OUTPUT)libperf-gtk.so: $(GTK_IN) $(PERFLIBS) $(QUIET_LINK)$(CC) -o $@ -shared $(LDFLAGS) $(filter %.o,$^) $(GTK_LIBS) -$(SCRIPTS) : % : %.sh - $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' +$(SCRIPTS) : $(OUTPUT)% : %.sh + $(QUIET_GEN)$(INSTALL) '$<' '$@' $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE $(Q)$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) From b3a035a02213c0409ce5f14bcbb0a435a6eca085 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 May 2026 19:54:37 +0300 Subject: [PATCH 1322/5214] media: mc-entity: Fix documentation typo in function name The media_entity_pads_init() function name is misspelled. Fix it. Reviewed-by: Frank Li Reviewed-by: Jacopo Mondi Link: https://patch.msgid.link/20260506165438.1767378-1-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index b91ff6f8c3bb..904f61c14dc1 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -726,7 +726,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, * the entity (currently, it does nothing). * * Calling media_entity_cleanup() on a media_entity whose memory has been - * zeroed but that has not been initialized with media_entity_pad_init() is + * zeroed but that has not been initialized with media_entity_pads_init() is * valid and is a no-op. */ #if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) From aef73a9dad5c761e4a70fa171fed8e85ca1c1abd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 May 2026 19:54:38 +0300 Subject: [PATCH 1323/5214] media: mc-entity: Drop ifdef for media_entity_cleanup definition The media_entity_cleanup() function is defined in media-entity.h as a static inline no-op when CONFIG_MEDIA_CONTROLLER is enabled, and as a no-op macro otherwise. This complexity is unneeded. Use a static inline function in all cases. Reviewed-by: Jacopo Mondi Link: https://patch.msgid.link/20260506165438.1767378-2-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 904f61c14dc1..d9b72cd87d52 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -729,11 +729,9 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, * zeroed but that has not been initialized with media_entity_pads_init() is * valid and is a no-op. */ -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) -static inline void media_entity_cleanup(struct media_entity *entity) {} -#else -#define media_entity_cleanup(entity) do { } while (false) -#endif +static inline void media_entity_cleanup(struct media_entity *entity) +{ +} /** * media_get_pad_index() - retrieves a pad index from an entity From c4c01c4fd4a3916ffdfb35ad9f511c48e289f51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 1 May 2026 21:03:39 +0200 Subject: [PATCH 1324/5214] media: uapi: rkisp: Correct name version enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of the enum to hold the mapping of parameter buffer versions have a typo in the name, correct it. While this is a uAPI header the impact should be minimal as the enum is only used as a collection for the one version number supported. Fixes: e9d05e9d5db1 ("media: uapi: rkisp1-config: Add extensible params format") Cc: stable@vger.kernel.org Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/uapi/linux/rkisp1-config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index b2d2a71f7baf..7638b2220600 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -1535,11 +1535,11 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_wdr_config)) /** - * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version + * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version * * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters */ -enum rksip1_ext_param_buffer_version { +enum rkisp1_ext_param_buffer_version { RKISP1_EXT_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1, }; @@ -1601,7 +1601,7 @@ enum rksip1_ext_param_buffer_version { * +---------------------------------------------------------------------+ * * @version: The RkISP1 extensible parameters buffer version, see - * :c:type:`rksip1_ext_param_buffer_version` + * :c:type:`rkisp1_ext_param_buffer_version` * @data_size: The RkISP1 configuration data effective size, excluding this * header * @data: The RkISP1 extensible configuration data blocks From 27caa94f4d13d16f7f63eec879ea7d4d79699415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Mon, 11 May 2026 17:09:57 +0200 Subject: [PATCH 1325/5214] media: rkisp1: Add support for CAC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CAC block implements chromatic aberration correction. Expose it to userspace using the extensible parameters format. This was tested on the i.MX8MP platform, but based on available documentation it is also present in the RK3399 variant (V10). Thus presumably also in later versions, so no feature flag is introduced. Signed-off-by: Barnabás Pőcze Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260511150957.581049-1-barnabas.pocze@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../platform/rockchip/rkisp1/rkisp1-params.c | 68 +++++++++++ .../platform/rockchip/rkisp1/rkisp1-regs.h | 15 ++- include/uapi/linux/rkisp1-config.h | 107 +++++++++++++++++- 3 files changed, 187 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 6442436a5e42..042b759eba62 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -64,6 +64,7 @@ union rkisp1_ext_params_config { struct rkisp1_ext_params_compand_bls_config compand_bls; struct rkisp1_ext_params_compand_curve_config compand_curve; struct rkisp1_ext_params_wdr_config wdr; + struct rkisp1_ext_params_cac_config cac; }; enum rkisp1_params_formats { @@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK); } +static void rkisp1_cac_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_cac_config *arg) +{ + u32 val; + + /* + * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL), + * so only set the clipping mode, and do not modify the other bits. + */ + val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL); + val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE | + RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE); + val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_CTRL_ENABLE); } +static void rkisp1_ext_params_cac(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_cac_config *cac = &block->cac; + + if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); + return; + } + + rkisp1_cac_config(params, &cac->config); + + if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(cac->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); +} + typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); @@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler { .handler = rkisp1_ext_params_wdr, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = { + .handler = rkisp1_ext_params_cac, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, }; #define RKISP1_PARAMS_BLOCK_INFO(block, data) \ @@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = { RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve), RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve), RKISP1_PARAMS_BLOCK_INFO(WDR, wdr), + RKISP1_PARAMS_BLOCK_INFO(CAC, cac), }; static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) == @@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params) rkisp1_ie_enable(params, false); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE, RKISP1_CIF_ISP_DPF_MODE_EN); + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); } static const struct rkisp1_params_ops rkisp1_v10_params_ops = { diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index fbeb186cde0d..2b842194de8f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -724,6 +724,17 @@ #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16) #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U +/* CAC */ +#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE BIT(0) +#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE GENMASK(2, 1) +#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE BIT(3) +#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK GENMASK(12, 0) +#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK GENMASK(28, 16) +#define RKISP1_CIF_ISP_CAC_RED_MASK GENMASK(8, 0) +#define RKISP1_CIF_ISP_CAC_BLUE_MASK GENMASK(24, 16) +#define RKISP1_CIF_ISP_CAC_NF_MASK GENMASK(4, 0) +#define RKISP1_CIF_ISP_CAC_NS_MASK GENMASK(19, 16) + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1196,8 +1207,8 @@ #define RKISP1_CIF_ISP_CAC_A (RKISP1_CIF_ISP_CAC_BASE + 0x00000008) #define RKISP1_CIF_ISP_CAC_B (RKISP1_CIF_ISP_CAC_BASE + 0x0000000c) #define RKISP1_CIF_ISP_CAC_C (RKISP1_CIF_ISP_CAC_BASE + 0x00000010) -#define RKISP1_CIF_ISP_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) -#define RKISP1_CIF_ISP_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) +#define RKISP1_CIF_ISP_CAC_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) +#define RKISP1_CIF_ISP_CAC_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) #define RKISP1_CIF_ISP_EXP_BASE 0x00002600 #define RKISP1_CIF_ISP_EXP_CTRL (RKISP1_CIF_ISP_EXP_BASE + 0x00000000) diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index 7638b2220600..d97384abc157 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config { __u8 use_iref; }; +/* + * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode + * + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_h_clip_mode { + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0, + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1, +}; + +/** + * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode + * + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_v_clip_mode { + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2, +}; + +/** + * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration + * + * The correction is carried out by shifting the red and blue pixels relative + * to the green ones, depending on the distance from the optical center: + * + * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * + * For each pixel, the x/y distances from the optical center are calculated and + * then transformed into the [0,255] range based on the following formula: + * + * (((d << 4) >> ns) * nf) >> 5 + * + * where `d` is the distance, `ns` and `nf` are the normalization parameters: + * + * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31]) + * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31]) + * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * These parameters should be chosen based on the image resolution, the position + * of the optical center, and the shape of pixels, so that no normalized distance + * is larger than 255. If the pixels have square shape, the two sets of parameters + * should be equal. + * + * The actual amount of correction is calculated with a third degree polynomial: + * + * c[0] * r + c[1] * r^2 + c[2] * r^3 + * + * where `c` is the set of coefficients for the given color, and `r` is distance: + * + * @red: red coefficients (5.4 two's complement; [-16,15.9375]) + * @blue: blue coefficients (5.4 two's complement; [-16,15.9375]) + * + * Finally, the amount is clipped as requested: + * + * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode) + * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode) + * + * A positive result will shift away from the optical center, while a negative + * one will shift towards the optical center. In the latter case, the pixel + * values at the edges are duplicated. + */ +struct rkisp1_cif_isp_cac_config { + __u8 h_clip_mode; + __u8 v_clip_mode; + + __u16 h_count_start; + __u16 v_count_start; + + __u16 red[3]; + __u16 blue[3]; + + __u8 x_nf; + __u8 x_ns; + + __u8 y_nf; + __u8 y_ns; +}; + /*---------- PART2: Measurement Statistics ------------*/ /** @@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer { * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction */ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS, @@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND, RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS, RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR, + RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC, }; /* For backward compatibility */ @@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config { struct rkisp1_cif_isp_wdr_config config; } __attribute__((aligned(8))); +/** + * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config + * + * RkISP1 extensible parameters CAC block. + * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`. + * + * @header: The RkISP1 extensible parameters header, see + * :c:type:`rkisp1_ext_params_block_header` + * @config: CAC configuration, see + * :c:type:`rkisp1_cif_isp_cac_config` + */ +struct rkisp1_ext_params_cac_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_cif_isp_cac_config config; +} __attribute__((aligned(8))); + /* * The rkisp1_ext_params_compand_curve_config structure is counted twice as it * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types. @@ -1532,7 +1636,8 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_compand_bls_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ - sizeof(struct rkisp1_ext_params_wdr_config)) + sizeof(struct rkisp1_ext_params_wdr_config) +\ + sizeof(struct rkisp1_ext_params_cac_config)) /** * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version From b670bf89824ede5d07d20bb9bfbafb754846081d Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:15 +0800 Subject: [PATCH 1326/5214] media: nxp: imx8-isi: Fix use-after-free on remove KASAN reports a slab-use-after-free in __media_entity_remove_link() during rmmod of imx8_isi: BUG: KASAN: slab-use-after-free in __media_entity_remove_link+0x608/0x650 Read of size 2 at addr ffff0000d47cb02a by task rmmod/724 Call trace: __media_entity_remove_link+0x608/0x650 __media_entity_remove_links+0x78/0x144 __media_device_unregister_entity+0x150/0x280 media_device_unregister_entity+0x48/0x68 v4l2_device_unregister_subdev+0x158/0x300 v4l2_async_unbind_subdev_one+0x22c/0x358 v4l2_async_nf_unbind_all_subdevs+0xfc/0x1c0 v4l2_async_nf_unregister+0x5c/0x14c mxc_isi_remove+0x124/0x2a0 [imx8_isi] Allocated by task 249: __kmalloc_noprof+0x27c/0x690 mxc_isi_crossbar_init+0x22c/0x560 [imx8_isi] Freed by task 724: kfree+0x1e4/0x5b0 mxc_isi_crossbar_cleanup+0x34/0x80 [imx8_isi] mxc_isi_remove+0x11c/0x2a0 [imx8_isi] The problem is that mxc_isi_remove() calls mxc_isi_crossbar_cleanup() before mxc_isi_v4l2_cleanup(). The crossbar cleanup frees the media entity pads, but the subsequent v4l2 cleanup still tries to remove media links that reference those pads. Fix this by calling mxc_isi_v4l2_cleanup() before mxc_isi_crossbar_cleanup() to ensure all media entities are properly unregistered while the pads are still valid. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-2-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 4bf8570e1b9e..2d639b789910 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -556,8 +556,8 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_pipe_cleanup(pipe); } - mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); + mxc_isi_crossbar_cleanup(&isi->crossbar); } static const struct of_device_id mxc_isi_of_match[] = { From 567418eedd25b3d86d489807682030b4b98b73d9 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:16 +0800 Subject: [PATCH 1327/5214] media: nxp: imx8-isi: Add missing v4l2_subdev_cleanup() in crossbar and pipe Both mxc_isi_crossbar_init() and mxc_isi_pipe_init() call v4l2_subdev_init_finalize() which allocates the subdev active state, but neither mxc_isi_crossbar_cleanup() nor mxc_isi_pipe_cleanup() calls v4l2_subdev_cleanup() to free it. This causes a memory leak on every rmmod, reported by kmemleak: unreferenced object 0xffff0000d06fc800 (size 192): comm "(udev-worker)", pid 254, jiffies 4294913455 backtrace (crc 36eeae58): kmemleak_alloc+0x34/0x40 __kvmalloc_node_noprof+0x5f8/0x7d8 __v4l2_subdev_state_alloc+0x1fc/0x30c __v4l2_subdev_init_finalize+0x178/0x368 Add the missing v4l2_subdev_cleanup() calls before media_entity_cleanup() in both crossbar and pipe cleanup paths. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-3-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 1 + drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 605a45124103..c580c831972e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -491,6 +491,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar) { + v4l2_subdev_cleanup(&xbar->sd); media_entity_cleanup(&xbar->sd.entity); kfree(xbar->pads); kfree(xbar->inputs); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a41c51dd9ce0..cb50af2270f6 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -819,6 +819,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) { struct v4l2_subdev *sd = &pipe->sd; + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); } From 8262de0663318124824aaafd97ddb5d7bb53bd77 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:17 +0800 Subject: [PATCH 1328/5214] media: nxp: imx8-isi: Fix missing v4l2_subdev_cleanup() in pipe init error path After v4l2_subdev_init_finalize() succeeds in mxc_isi_pipe_init(), if platform_get_irq() or devm_request_irq() fails, the error path jumps to a label that only calls media_entity_cleanup() and mutex_destroy(), missing the v4l2_subdev_cleanup() call needed to free the subdev active state allocated by v4l2_subdev_init_finalize(). Add an error_subdev label that calls v4l2_subdev_cleanup() before falling through to the existing error cleanup. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-4-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index cb50af2270f6..a59b9456b590 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -796,18 +796,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) irq = platform_get_irq(to_platform_device(isi->dev), id); if (irq < 0) { ret = irq; - goto error; + goto error_subdev; } ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 0, dev_name(isi->dev), pipe); if (ret < 0) { dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); - goto error; + goto error_subdev; } return 0; +error_subdev: + v4l2_subdev_cleanup(sd); error: media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); From d970b27cc48ec42f8a72bc3a4a4ad2e5c7a36395 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:18 +0800 Subject: [PATCH 1329/5214] media: nxp: imx8-isi: Clean up already-initialized pipes on probe failure When mxc_isi_pipe_init() fails partway through the channel loop or when mxc_isi_v4l2_init() fails, the already initialized pipes are not cleaned up. Fix this by calling mxc_isi_pipe_cleanup() for each already-initialized pipe in the err_xbar error path. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260507041318.491594-5-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 2d639b789910..e8545761b5ff 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -538,6 +538,8 @@ static int mxc_isi_probe(struct platform_device *pdev) return 0; err_xbar: + while (i--) + mxc_isi_pipe_cleanup(&isi->pipes[i]); mxc_isi_crossbar_cleanup(&isi->crossbar); return ret; From 75a54af91f33084c0cef11b3cb2f89765af78920 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Fri, 20 Mar 2026 14:42:02 +0800 Subject: [PATCH 1330/5214] media: nxp: imx8-isi: Prioritize pending buffers over discard buffers The number of times to use the discard buffer is determined by the out_pending list size: discard = list_empty(&video->out_pending) ? 2 : list_is_singular(&video->out_pending) ? 1 : 0; In the current buffer selection logic, when both discard and pending buffers are available, the driver fills hardware slots with discard buffers first which results in an unnecessary frame drop even though a user buffer was queued and ready. Change the buffer selection logic to use pending buffers first (up to the number available), and only use discard buffers to fill remaining slots when insufficient pending buffers are queued. This improves behavior by: - Reducing discarded frames at stream start when user buffers are ready - Decreasing latency in delivering captured frames to user-space - Ensuring user buffers are utilized as soon as they are queued - Improving overall buffer utilization efficiency Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260320-isi_min_buffers-v3-2-66e0fabccca3@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 1be3a728f32f..fe4adfa3a1f0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -792,7 +792,11 @@ static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video) struct mxc_isi_buffer *buf; struct list_head *list; - list = i < discard ? &video->out_discard : &video->out_pending; + /* + * Queue buffers: prioritize pending buffers, then discard + * buffers. + */ + list = (i < 2 - discard) ? &video->out_pending : &video->out_discard; buf = list_first_entry(list, struct mxc_isi_buffer, list); mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id); From 57a7ec5c9f38ce6c4d6209c4b75c8e57e1fea6cf Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:30 +0800 Subject: [PATCH 1331/5214] media: nxp: imx8-isi: Fix potential out-of-bounds issues The maximum downscaling factor supported by ISI can be up to 16. Add minimum value constraint before applying the setting to hardware. Otherwise, the process will not respond even when Ctrl+C is executed. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-1-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 16 ++++++++++++++++ .../media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 11 ++++++++--- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 13 ++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 14d63ec36416..7547a6559d4c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -11,6 +11,7 @@ #define __MXC_ISI_CORE_H__ #include +#include #include #include #include @@ -414,4 +415,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) } #endif +/* + * ISI scaling engine works in two parts: it performs pre-decimation of + * the image followed by bilinear filtering to achieve the desired + * downscaling factor. + * + * The decimation filter provides a maximum downscaling factor of 8, and + * the subsequent bilinear filter provides a maximum downscaling factor + * of 2. Combined, the maximum scaling factor can be up to 16. + */ +static inline unsigned int +mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val) +{ + return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val); +} + #endif /* __MXC_ISI_CORE_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index a39ad7a1ab18..de398b232d74 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, const enum mxc_isi_video_type type) { if (type == MXC_ISI_VIDEO_M2M_CAP) { - /* Downscaling only */ - pix->width = min(pix->width, ctx->queues.out.format.width); - pix->height = min(pix->height, ctx->queues.out.format.height); + const struct v4l2_pix_format_mplane *format = + &ctx->queues.out.format; + + /* Downscaling only, by up to 16. */ + pix->width = mxc_isi_clamp_downscale_16(pix->width, + format->width); + pix->height = mxc_isi_clamp_downscale_16(pix->height, + format->height); } return mxc_isi_format_try(ctx->m2m->pipe, pix, type); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a59b9456b590..2d0843c86534 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, /* Composing is supported on the sink only. */ return -EINVAL; - /* The sink crop is bound by the sink format downscaling only). */ + /* + * The ISI supports downscaling only, with a factor up to 16. + * Clamp the compose rectangle size accordingly. + */ format = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK); sel->r.left = 0; sel->r.top = 0; - sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, - format->width); - sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, - format->height); + sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width, + format->width); + sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height, + format->height); rect = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK); From 5eb54da3f874b44149556542d949909898865e29 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:31 +0800 Subject: [PATCH 1332/5214] media: nxp: imx8-isi: Fix scale factor calculation for hardware rounding The ISI hardware rounds the actual output size up to an integer, as described in i.MX93 Reference Manual section 57.7.8 (Channel 0 Scale Factor). The scale factor must be calculated to ensure the theoretical output value rounds up to exactly the desired size. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-2-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 0187d4ab97e8..16b20ea2d1db 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, else *dec = 8; - return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD); + /* + * The ISI rounds output dimensions up to the next integer (i.MX93 RM + * section 57.7.8). Calculate the scale factor such that the theoretical + * output (input / scale_factor) rounds up to exactly the desired + * output. + */ + return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec), + ISI_DOWNSCALE_THRESHOLD); } static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, From 27ae400e6e888153ded1ad807a94a94e506dd2df Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 11 May 2026 11:46:11 +0100 Subject: [PATCH 1333/5214] KVM: arm64: nv: Track L2 to L1 exception emulation While we currently track that we are emulating a nested ERET from L1 to L2, we currently don't track the reverse direction (an exception going from L2 to L1). Add a new vcpu state flag for this purpose, which will see some use shortly. Signed-off-by: Marc Zyngier Link: https://patch.msgid.link/20260520085036.541666-2-maz@kernel.org --- arch/arm64/include/asm/kvm_host.h | 3 ++- arch/arm64/kvm/emulate-nested.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 65eead8362e0..c79747d5f4dd 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1112,7 +1112,8 @@ struct kvm_vcpu_arch { #define IN_NESTED_ERET __vcpu_single_flag(sflags, BIT(7)) /* SError pending for nested guest */ #define NESTED_SERROR_PENDING __vcpu_single_flag(sflags, BIT(8)) - +/* KVM is currently emulating an L2 to L1 exception */ +#define IN_NESTED_EXCEPTION __vcpu_single_flag(sflags, BIT(9)) /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */ #define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \ diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index dba7ced74ca5..15c691a6266d 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2862,6 +2862,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2, preempt_disable(); + vcpu_set_flag(vcpu, IN_NESTED_EXCEPTION); + /* * We may have an exception or PC update in the EL0/EL1 context. * Commit it before entering EL2. @@ -2884,6 +2886,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2, __kvm_adjust_pc(vcpu); kvm_arch_vcpu_load(vcpu, smp_processor_id()); + vcpu_clear_flag(vcpu, IN_NESTED_EXCEPTION); + preempt_enable(); if (kvm_vcpu_has_pmu(vcpu)) From 435c466196148ae116f616e6cda97c33281defc2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 11 May 2026 11:46:41 +0100 Subject: [PATCH 1334/5214] KVM: arm64: nv: Don't save/restore FP register during a nested ERET or exception When switching between L1 and L2, we save the old state using kvm_arch_vcpu_put(), mutate the state in memory, then load the new state using kvm_arch_vcpu_load(). Any live FPSIMD/SVE state is saved and unbound, such that it can be lazily restored on a subsequent trap. The FPSIMD/SVE state is shared by exception levels, and only a handful of related control registers need to be changed when transitioning between L1 and L2. The save/restore of the common state is needless overhead, especially as trapping becomes exponentially more expensive with nesting. Avoid this overhead by leaving the common FPSIMD/SVE state live on the CPU, and only switching the state that is distinct for L1 and L2: - the trap controls: the effective values are recomputed on each entry into the guest to take the EL into account and merge the L0 and L1 configuration if in a nested context, or directly use the L0 configuration in non-nested context (see __activate_traps()). - the VL settings: the effective values are are also recomputed on each entry into the guest (see fpsimd_lazy_switch_to_guest()). Since we appear to cover all bases, use the vcpu flags indicating the handling of a nested ERET or exception delivery to avoid the whole FP save/restore shenanigans. SME will have to be similarly dealt with when it eventually gets supported. For an EL1 L3 guest where L1 and L2 have this optimisation, this results in at least a 10% wall clock reduction when running an I/O heavy workload, generating a high rate of nested exceptions. Reviewed-by: Joey Gouly Acked-by: Mark Rutland Signed-off-by: Marc Zyngier Link: https://patch.msgid.link/20260520085036.541666-3-maz@kernel.org --- arch/arm64/kvm/fpsimd.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index 15e17aca1dec..3f6b1e29cd6b 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -28,6 +28,20 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) if (!system_supports_fpsimd()) return; + /* + * Avoid needless save/restore of the guest's common + * FPSIMD/SVE/SME regs during transitions between L1/L2. + * + * These transitions only happens in a non-preemptible context + * where the host regs have already been saved and unbound. The + * live registers are either free or owned by the guest. + */ + if (vcpu_get_flag(vcpu, IN_NESTED_ERET) || + vcpu_get_flag(vcpu, IN_NESTED_EXCEPTION)) { + WARN_ON_ONCE(host_owns_fp_regs()); + return; + } + /* * Ensure that any host FPSIMD/SVE/SME state is saved and unbound such * that the host kernel is responsible for restoring this state upon @@ -102,6 +116,18 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) { unsigned long flags; + /* + * See comment in kvm_arch_vcpu_load_fp(). Note that we also rely on + * the guest's max VL to have been set by fpsimd_lazy_switch_to_host() + * so that any intervening kernel-mode SIMD (NEON or otherwise) + * operation sees the full guest state that needs saving. + */ + if (vcpu_get_flag(vcpu, IN_NESTED_ERET) || + vcpu_get_flag(vcpu, IN_NESTED_EXCEPTION)) { + WARN_ON_ONCE(host_owns_fp_regs()); + return; + } + local_irq_save(flags); if (guest_owns_fp_regs()) { From 68a612d4dbc7f2b9dac731c79676a21fce573d29 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:01:55 +0100 Subject: [PATCH 1335/5214] KVM: arm64: timer: Repaint kvm_timer_{should,irq_can}_fire() to kvm_timer_{pending,enabled}() kvm_timer_should_fire() seems to date back to a time where the author of the timer code didn't seem to have made the word "pending" part of their vocabulary. Having since slightly improved on that front, let's rename this predicate to kvm_timer_pending(), which clearly indicates whether the timer interrupt is pending or not. Similarly, kvm_timer_irq_can_fire() is renamed to kvm_timer_enabled(). Reviewed-by: Joey Gouly Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 55 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index cbea4d9ee955..d8add34717f0 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -39,10 +39,9 @@ static const u8 default_ppi[] = { [TIMER_HVTIMER] = 28, }; -static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx); static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, struct arch_timer_context *timer_ctx); -static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx); +static bool kvm_timer_pending(struct arch_timer_context *timer_ctx); static void kvm_arm_timer_write(struct kvm_vcpu *vcpu, struct arch_timer_context *timer, enum kvm_arch_timer_regs treg, @@ -224,7 +223,7 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) else ctx = map.direct_ptimer; - if (kvm_timer_should_fire(ctx)) + if (kvm_timer_pending(ctx)) kvm_timer_update_irq(vcpu, true, ctx); if (userspace_irqchip(vcpu->kvm) && @@ -257,7 +256,7 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx) return kvm_counter_compute_delta(timer_ctx, timer_get_cval(timer_ctx)); } -static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) +static bool kvm_timer_enabled(struct arch_timer_context *timer_ctx) { WARN_ON(timer_ctx && timer_ctx->loaded); return timer_ctx && @@ -294,7 +293,7 @@ static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu) struct arch_timer_context *ctx = &vcpu->arch.timer_cpu.timers[i]; WARN(ctx->loaded, "timer %d loaded\n", i); - if (kvm_timer_irq_can_fire(ctx)) + if (kvm_timer_enabled(ctx)) min_delta = min(min_delta, kvm_timer_compute_delta(ctx)); } @@ -358,7 +357,7 @@ static enum hrtimer_restart kvm_hrtimer_expire(struct hrtimer *hrt) return HRTIMER_NORESTART; } -static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) +static bool kvm_timer_pending(struct arch_timer_context *timer_ctx) { enum kvm_arch_timers index; u64 cval, now; @@ -391,7 +390,7 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) !(cnt_ctl & ARCH_TIMER_CTRL_IT_MASK); } - if (!kvm_timer_irq_can_fire(timer_ctx)) + if (!kvm_timer_enabled(timer_ctx)) return false; cval = timer_get_cval(timer_ctx); @@ -417,9 +416,9 @@ void kvm_timer_update_run(struct kvm_vcpu *vcpu) /* Populate the device bitmap with the timer states */ regs->device_irq_level &= ~(KVM_ARM_DEV_EL1_VTIMER | KVM_ARM_DEV_EL1_PTIMER); - if (kvm_timer_should_fire(vtimer)) + if (kvm_timer_pending(vtimer)) regs->device_irq_level |= KVM_ARM_DEV_EL1_VTIMER; - if (kvm_timer_should_fire(ptimer)) + if (kvm_timer_pending(ptimer)) regs->device_irq_level |= KVM_ARM_DEV_EL1_PTIMER; } @@ -473,21 +472,21 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, /* Only called for a fully emulated timer */ static void timer_emulate(struct arch_timer_context *ctx) { - bool should_fire = kvm_timer_should_fire(ctx); + bool pending = kvm_timer_pending(ctx); - trace_kvm_timer_emulate(ctx, should_fire); + trace_kvm_timer_emulate(ctx, pending); - if (should_fire != ctx->irq.level) - kvm_timer_update_irq(timer_context_to_vcpu(ctx), should_fire, ctx); + if (pending != ctx->irq.level) + kvm_timer_update_irq(timer_context_to_vcpu(ctx), pending, ctx); - kvm_timer_update_status(ctx, should_fire); + kvm_timer_update_status(ctx, pending); /* - * If the timer can fire now, we don't need to have a soft timer - * scheduled for the future. If the timer cannot fire at all, - * then we also don't need a soft timer. + * If the timer is pending, we don't need to have a soft timer + * scheduled for the future. If the timer is disabled, then + * we don't need a soft timer either. */ - if (should_fire || !kvm_timer_irq_can_fire(ctx)) + if (pending || !kvm_timer_enabled(ctx)) return; soft_timer_start(&ctx->hrtimer, kvm_timer_compute_delta(ctx)); @@ -594,10 +593,10 @@ static void kvm_timer_blocking(struct kvm_vcpu *vcpu) * If no timers are capable of raising interrupts (disabled or * masked), then there's no more work for us to do. */ - if (!kvm_timer_irq_can_fire(map.direct_vtimer) && - !kvm_timer_irq_can_fire(map.direct_ptimer) && - !kvm_timer_irq_can_fire(map.emul_vtimer) && - !kvm_timer_irq_can_fire(map.emul_ptimer) && + if (!kvm_timer_enabled(map.direct_vtimer) && + !kvm_timer_enabled(map.direct_ptimer) && + !kvm_timer_enabled(map.emul_vtimer) && + !kvm_timer_enabled(map.emul_ptimer) && !vcpu_has_wfit_active(vcpu)) return; @@ -685,7 +684,7 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) * this point and the register restoration, we'll take the * interrupt anyway. */ - kvm_timer_update_irq(vcpu, kvm_timer_should_fire(ctx), ctx); + kvm_timer_update_irq(vcpu, kvm_timer_pending(ctx), ctx); if (irqchip_in_kernel(vcpu->kvm)) phys_active = kvm_vgic_map_is_active(vcpu, timer_irq(ctx)); @@ -706,7 +705,7 @@ static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu) * this point and the register restoration, we'll take the * interrupt anyway. */ - kvm_timer_update_irq(vcpu, kvm_timer_should_fire(vtimer), vtimer); + kvm_timer_update_irq(vcpu, kvm_timer_pending(vtimer), vtimer); /* * When using a userspace irqchip with the architected timers and a @@ -917,8 +916,8 @@ bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) vlevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_VTIMER; plevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_PTIMER; - return kvm_timer_should_fire(vtimer) != vlevel || - kvm_timer_should_fire(ptimer) != plevel; + return kvm_timer_pending(vtimer) != vlevel || + kvm_timer_pending(ptimer) != plevel; } void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) @@ -1006,7 +1005,7 @@ static void unmask_vtimer_irq_user(struct kvm_vcpu *vcpu) { struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - if (!kvm_timer_should_fire(vtimer)) { + if (!kvm_timer_pending(vtimer)) { kvm_timer_update_irq(vcpu, false, vtimer); if (static_branch_likely(&has_gic_active_state)) set_timer_irq_phys_active(vtimer, false); @@ -1579,7 +1578,7 @@ static bool kvm_arch_timer_get_input_level(int vintid) ctx = vcpu_get_timer(vcpu, i); if (timer_irq(ctx) == vintid) - return kvm_timer_should_fire(ctx); + return kvm_timer_pending(ctx); } /* A timer IRQ has fired, but no matching timer was found? */ From 0d27b4b351493cb2fe1f87cd152856704d4e141d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:01:56 +0100 Subject: [PATCH 1336/5214] KVM: arm64: Simplify userspace notification of interrupt state The userspace notification of interrupts is has a few problems: - it is utterly pointless - it is annoyingly split between detecting the need for notification and the population of the interrupts in the run structure We can't do anything about the former (yet), but the latter can be addressed. If we detect that we must notify userspace, we know that we are going to exit, as we populate the exit status. Which means we can also populate the interrupt state at this stage and be done with it. This simplifies the structure of the code. Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 49 +++++++++++++++--------------------- arch/arm64/kvm/arm.c | 24 ++++++++++-------- arch/arm64/kvm/pmu-emul.c | 18 +++++-------- include/kvm/arm_arch_timer.h | 2 +- include/kvm/arm_pmu.h | 4 +-- 5 files changed, 43 insertions(+), 54 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index d8add34717f0..7236dd6a99e6 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -404,22 +404,30 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) return vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu) == 0; } +static u64 kvm_timer_needs_notify(struct kvm_vcpu *vcpu) +{ + u64 v = vcpu->run->s.regs.device_irq_level; + + v ^= kvm_timer_pending(vcpu_vtimer(vcpu)) ? KVM_ARM_DEV_EL1_VTIMER : 0; + v ^= kvm_timer_pending(vcpu_ptimer(vcpu)) ? KVM_ARM_DEV_EL1_PTIMER : 0; + + return v & (KVM_ARM_DEV_EL1_VTIMER | KVM_ARM_DEV_EL1_PTIMER); +} + +bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) +{ + return !!kvm_timer_needs_notify(vcpu); +} + /* * Reflect the timer output level into the kvm_run structure */ -void kvm_timer_update_run(struct kvm_vcpu *vcpu) +bool kvm_timer_update_run(struct kvm_vcpu *vcpu) { - struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - struct kvm_sync_regs *regs = &vcpu->run->s.regs; - - /* Populate the device bitmap with the timer states */ - regs->device_irq_level &= ~(KVM_ARM_DEV_EL1_VTIMER | - KVM_ARM_DEV_EL1_PTIMER); - if (kvm_timer_pending(vtimer)) - regs->device_irq_level |= KVM_ARM_DEV_EL1_VTIMER; - if (kvm_timer_pending(ptimer)) - regs->device_irq_level |= KVM_ARM_DEV_EL1_PTIMER; + u64 mask = kvm_timer_needs_notify(vcpu); + if (mask) + vcpu->run->s.regs.device_irq_level ^= mask; + return !!mask; } static void kvm_timer_update_status(struct arch_timer_context *ctx, bool level) @@ -903,23 +911,6 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu) timer_set_traps(vcpu, &map); } -bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) -{ - struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - struct kvm_sync_regs *sregs = &vcpu->run->s.regs; - bool vlevel, plevel; - - if (likely(irqchip_in_kernel(vcpu->kvm))) - return false; - - vlevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_VTIMER; - plevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_PTIMER; - - return kvm_timer_pending(vtimer) != vlevel || - kvm_timer_pending(ptimer) != plevel; -} - void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = vcpu_timer(vcpu); diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 8bb2c7422cc8..6e6dc17f8b60 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1163,6 +1163,15 @@ static bool vcpu_mode_is_bad_32bit(struct kvm_vcpu *vcpu) return !kvm_supports_32bit_el0(); } +static bool kvm_irq_update_run(struct kvm_vcpu *vcpu) +{ + bool r; + + r = kvm_timer_update_run(vcpu); + r |= kvm_pmu_update_run(vcpu); + return r; +} + /** * kvm_vcpu_exit_request - returns true if the VCPU should *not* enter the guest * @vcpu: The VCPU pointer @@ -1184,13 +1193,11 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret) /* * If we're using a userspace irqchip, then check if we need * to tell a userspace irqchip about timer or PMU level - * changes and if so, exit to userspace (the actual level - * state gets updated in kvm_timer_update_run and - * kvm_pmu_update_run below). + * changes and if so, exit to userspace while updating the run + * state. */ if (unlikely(!irqchip_in_kernel(vcpu->kvm))) { - if (kvm_timer_should_notify_user(vcpu) || - kvm_pmu_should_notify_user(vcpu)) { + if (unlikely(kvm_irq_update_run(vcpu))) { *ret = -EINTR; run->exit_reason = KVM_EXIT_INTR; return true; @@ -1405,11 +1412,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) ret = handle_exit(vcpu, ret); } - /* Tell userspace about in-kernel device output levels */ - if (unlikely(!irqchip_in_kernel(vcpu->kvm))) { - kvm_timer_update_run(vcpu); - kvm_pmu_update_run(vcpu); - } + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) + kvm_irq_update_run(vcpu); kvm_sigset_deactivate(vcpu); diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index e1860acae641..31a472a2c488 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -413,27 +413,21 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu) { - struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_sync_regs *sregs = &vcpu->run->s.regs; bool run_level = sregs->device_irq_level & KVM_ARM_DEV_PMU; - if (likely(irqchip_in_kernel(vcpu->kvm))) - return false; - - return pmu->irq_level != run_level; + return kvm_pmu_overflow_status(vcpu) != run_level; } /* * Reflect the PMU overflow interrupt output level into the kvm_run structure */ -void kvm_pmu_update_run(struct kvm_vcpu *vcpu) +bool kvm_pmu_update_run(struct kvm_vcpu *vcpu) { - struct kvm_sync_regs *regs = &vcpu->run->s.regs; - - /* Populate the timer bitmap for user space */ - regs->device_irq_level &= ~KVM_ARM_DEV_PMU; - if (vcpu->arch.pmu.irq_level) - regs->device_irq_level |= KVM_ARM_DEV_PMU; + bool update = kvm_pmu_should_notify_user(vcpu); + if (update) + vcpu->run->s.regs.device_irq_level ^= KVM_ARM_DEV_PMU; + return update; } /** diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index bf8cc9589bd0..9e4076eebd29 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -104,7 +104,7 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); void kvm_timer_sync_nested(struct kvm_vcpu *vcpu); void kvm_timer_sync_user(struct kvm_vcpu *vcpu); bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu); -void kvm_timer_update_run(struct kvm_vcpu *vcpu); +bool kvm_timer_update_run(struct kvm_vcpu *vcpu); void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); void kvm_timer_init_vm(struct kvm *kvm); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 0a36a3d5c894..3e844c5ee917 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -54,7 +54,7 @@ void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vcpu, u64 val); void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu); bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu); -void kvm_pmu_update_run(struct kvm_vcpu *vcpu); +bool kvm_pmu_update_run(struct kvm_vcpu *vcpu); void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val); void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val); void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data, @@ -131,7 +131,7 @@ static inline bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu) { return false; } -static inline void kvm_pmu_update_run(struct kvm_vcpu *vcpu) {} +static inline bool kvm_pmu_update_run(struct kvm_vcpu *vcpu) { return false; } static inline void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val) {} static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {} static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, From ac7002031852ab8f75b3debb1a4c4b2d1ff5a26c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:01:57 +0100 Subject: [PATCH 1337/5214] KVM: arm64: timer: Kill the per-timer irq level cache The timer code makes use of a per-timer irq level cache, which looks like a very minor optimisation to avoid taking a lock upon updating the GIC view of the interrupt when it is unchanged from the previous state. This is coming in the way of more important correctness issues, so get rid of the cache, which simplifies a couple of minor things. Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-4-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 20 +++++++++----------- include/kvm/arm_arch_timer.h | 5 ----- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 7236dd6a99e6..c3b8257888e8 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -453,9 +453,8 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, { kvm_timer_update_status(timer_ctx, new_level); - timer_ctx->irq.level = new_level; trace_kvm_timer_update_irq(vcpu->vcpu_id, timer_irq(timer_ctx), - timer_ctx->irq.level); + new_level); if (userspace_irqchip(vcpu->kvm)) return; @@ -473,7 +472,7 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, kvm_vgic_inject_irq(vcpu->kvm, vcpu, timer_irq(timer_ctx), - timer_ctx->irq.level, + new_level, timer_ctx); } @@ -484,10 +483,7 @@ static void timer_emulate(struct arch_timer_context *ctx) trace_kvm_timer_emulate(ctx, pending); - if (pending != ctx->irq.level) - kvm_timer_update_irq(timer_context_to_vcpu(ctx), pending, ctx); - - kvm_timer_update_status(ctx, pending); + kvm_timer_update_irq(timer_context_to_vcpu(ctx), pending, ctx); /* * If the timer is pending, we don't need to have a soft timer @@ -684,6 +680,7 @@ static inline void set_timer_irq_phys_active(struct arch_timer_context *ctx, boo static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) { struct kvm_vcpu *vcpu = timer_context_to_vcpu(ctx); + bool pending = kvm_timer_pending(ctx); bool phys_active = false; /* @@ -692,12 +689,12 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) * this point and the register restoration, we'll take the * interrupt anyway. */ - kvm_timer_update_irq(vcpu, kvm_timer_pending(ctx), ctx); + kvm_timer_update_irq(vcpu, pending, ctx); if (irqchip_in_kernel(vcpu->kvm)) phys_active = kvm_vgic_map_is_active(vcpu, timer_irq(ctx)); - phys_active |= ctx->irq.level; + phys_active |= pending; phys_active |= vgic_is_v5(vcpu->kvm); set_timer_irq_phys_active(ctx, phys_active); @@ -706,6 +703,7 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu) { struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + bool pending = kvm_timer_pending(vtimer); /* * Update the timer output so that it is likely to match the @@ -713,7 +711,7 @@ static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu) * this point and the register restoration, we'll take the * interrupt anyway. */ - kvm_timer_update_irq(vcpu, kvm_timer_pending(vtimer), vtimer); + kvm_timer_update_irq(vcpu, pending, vtimer); /* * When using a userspace irqchip with the architected timers and a @@ -725,7 +723,7 @@ static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu) * being de-asserted, we unmask the interrupt again so that we exit * from the guest when the timer fires. */ - if (vtimer->irq.level) + if (pending) disable_percpu_irq(host_vtimer_irq); else enable_percpu_irq(host_vtimer_irq, host_vtimer_irq_flags); diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 9e4076eebd29..15a4f97f8105 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -66,11 +66,6 @@ struct arch_timer_context { */ bool loaded; - /* Output level of the timer IRQ */ - struct { - bool level; - } irq; - /* Who am I? */ enum kvm_arch_timers timer_id; From 2772383afc5c65d6242f62947b5c184ffb049359 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:01:58 +0100 Subject: [PATCH 1338/5214] KVM: arm64: pmu: Kill the PMU interrupt level cache Just like the timer, the PMU has an interrupt cache that serves little purpose. Drop it. Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-5-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/pmu-emul.c | 13 +++---------- include/kvm/arm_pmu.h | 1 - 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 31a472a2c488..edb21239478a 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -396,19 +396,12 @@ static bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) { struct kvm_pmu *pmu = &vcpu->arch.pmu; - bool overflow; - overflow = kvm_pmu_overflow_status(vcpu); - if (pmu->irq_level == overflow) + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) return; - pmu->irq_level = overflow; - - if (likely(irqchip_in_kernel(vcpu->kvm))) { - int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu, - pmu->irq_num, overflow, pmu); - WARN_ON(ret); - } + WARN_ON(kvm_vgic_inject_irq(vcpu->kvm, vcpu, pmu->irq_num, + kvm_pmu_overflow_status(vcpu), pmu)); } bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu) diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 3e844c5ee917..b5e5942204fc 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -32,7 +32,6 @@ struct kvm_pmu { struct kvm_pmc pmc[KVM_ARMV8_PMU_MAX_COUNTERS]; int irq_num; bool created; - bool irq_level; }; struct arm_pmu_entry { From 1a8685ed8cd1ded20d0c81070a49b1cddf70481d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:01:59 +0100 Subject: [PATCH 1339/5214] KVM: arm64: vgic-v2: Force vgic init on injection outside the run loop Make sure that any attempt to inject an interrupt from userspace or an irqfd results in the GICv2 lazy init to take place. This is not currently necessary as the init is also performed on *any* interrupt injection. But as we're about to remove that, let's introduce it here. Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-6-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arm.c | 15 +++++++++++++-- arch/arm64/kvm/vgic/vgic-irqfd.c | 6 ++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 6e6dc17f8b60..cfb7921fc7d7 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -51,6 +51,7 @@ #include +#include "vgic/vgic.h" #include "sys_regs.h" static enum kvm_mode kvm_mode = KVM_MODE_DEFAULT; @@ -1497,8 +1498,13 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, return vcpu_interrupt_line(vcpu, irq_num, level); case KVM_ARM_IRQ_TYPE_PPI: - if (!irqchip_in_kernel(kvm)) + if (irqchip_in_kernel(kvm)) { + int ret = vgic_lazy_init(kvm); + if (ret) + return ret; + } else { return -ENXIO; + } vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id); if (!vcpu) @@ -1525,8 +1531,13 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, return kvm_vgic_inject_irq(kvm, vcpu, irq_num, level, NULL); case KVM_ARM_IRQ_TYPE_SPI: - if (!irqchip_in_kernel(kvm)) + if (irqchip_in_kernel(kvm)) { + int ret = vgic_lazy_init(kvm); + if (ret) + return ret; + } else { return -ENXIO; + } if (vgic_is_v5(kvm)) { /* Build a GICv5-style IntID here */ diff --git a/arch/arm64/kvm/vgic/vgic-irqfd.c b/arch/arm64/kvm/vgic/vgic-irqfd.c index b9b86e3a6c86..19a1094536e6 100644 --- a/arch/arm64/kvm/vgic/vgic-irqfd.c +++ b/arch/arm64/kvm/vgic/vgic-irqfd.c @@ -20,9 +20,15 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, int level, bool line_status) { unsigned int spi_id = e->irqchip.pin + VGIC_NR_PRIVATE_IRQS; + int ret; if (!vgic_valid_spi(kvm, spi_id)) return -EINVAL; + + ret = vgic_lazy_init(kvm); + if (ret) + return ret; + return kvm_vgic_inject_irq(kvm, NULL, spi_id, level, NULL); } From 958023d269e0312d10da85a6a49438d2e107dead Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 11:02:00 +0100 Subject: [PATCH 1340/5214] KVM: arm64: vgic-v2: Don't init the vgic on in-kernel interrupt injection We now have the lazy init on three paths: - on first run of a vcpu - on first injection of an interrupt from userspace and irqfd - on first injection of an interrupt from kernel space as part of the device emulation (timers, PMU, vgic MI) Given that we recompute the state of each in-kernel interrupt every time we are about to enter the guest, we can drop the lazy init from the kernel injection path. This solves a bunch of issues related to vgic_lazy_init() being called in non-preemptible context, such as vcpu reset. Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260520100200.543845-7-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 1e9fe8764584..9e29f03d3463 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -534,11 +534,9 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, { struct vgic_irq *irq; unsigned long flags; - int ret; - ret = vgic_lazy_init(kvm); - if (ret) - return ret; + if (unlikely(!vgic_initialized(kvm))) + return 0; if (!vcpu && irq_is_private(kvm, intid)) return -EINVAL; From f0748d9decedd0ef749d21c6b281cf26958dfd55 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:25 +0300 Subject: [PATCH 1341/5214] media: renesas: vsp1: Avoid forward function declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder functions to avoid the forward declaration of the vsp1_du_pipeline_configure(). No functional change intended. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-2-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_drm.c | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 15d266439564..79b85968b061 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -57,6 +57,50 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, * Pipeline Configuration */ +/* Configure all entities in the pipeline. */ +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_entity *entity; + struct vsp1_entity *next; + struct vsp1_dl_list *dl; + struct vsp1_dl_body *dlb; + unsigned int dl_flags = 0; + + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + + if (drm_pipe->force_brx_release) + dl_flags |= VSP1_DL_FRAME_END_INTERNAL; + if (pipe->output->writeback) + dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; + + dl = vsp1_dl_list_get(pipe->output->dlm); + dlb = vsp1_dl_list_get_body0(dl); + + list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { + /* Disconnect unused entities from the pipeline. */ + if (!entity->pipe) { + vsp1_dl_body_write(dlb, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + entity->sink = NULL; + list_del(&entity->list_pipe); + + continue; + } + + vsp1_entity_route_setup(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); + vsp1_entity_configure_frame(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); + } + + vsp1_dl_list_commit(dl, dl_flags); +} + /* * Insert the UIF in the pipeline between the prev and next entities. If no UIF * is available connect the two entities directly. @@ -224,8 +268,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, /* Setup the BRx source pad. */ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe); -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe); - static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe) { @@ -541,50 +583,6 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, return 0; } -/* Configure all entities in the pipeline. */ -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) -{ - struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - struct vsp1_dl_body *dlb; - unsigned int dl_flags = 0; - - vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], - drm_pipe->width, 0); - - if (drm_pipe->force_brx_release) - dl_flags |= VSP1_DL_FRAME_END_INTERNAL; - if (pipe->output->writeback) - dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; - - dl = vsp1_dl_list_get(pipe->output->dlm); - dlb = vsp1_dl_list_get_body0(dl); - - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - /* Disconnect unused entities from the pipeline. */ - if (!entity->pipe) { - vsp1_dl_body_write(dlb, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - entity->sink = NULL; - list_del(&entity->list_pipe); - - continue; - } - - vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, entity->state, pipe, - dl, dlb); - vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, - &pipe->part_table[0], dl, dlb); - } - - vsp1_dl_list_commit(dl, dl_flags); -} - static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, struct vsp1_rwpf *rwpf, u32 pixelformat, unsigned int pitch) From 79448d5386f62a3caaba7fe04204a587a8bfa5c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:26 +0300 Subject: [PATCH 1342/5214] media: renesas: vsp1: Split vsp1_du_setup_lif() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is used to configure and enable a pipeline, as well as disable it, depending on the cfg argument being a valid pointer or NULL. This creates a confusing API. Improve it by splitting the function in two, a vsp1_du_enable() function to configure a pipeline, and a vsp1_du_disable() function to disaple it. Keep vsp1_du_setup_lif() as an inline wrapper for existing callers in the DRM subsystem, to simplify merging. The callers will be updated separately and the old API will then be removed. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-3-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_drm.c | 140 ++++++++++-------- include/media/vsp1.h | 14 +- 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 79b85968b061..1f431874064d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -629,14 +629,14 @@ int vsp1_du_init(struct device *dev) EXPORT_SYMBOL_GPL(vsp1_du_init); /** - * vsp1_du_setup_lif - Setup the output part of the VSP pipeline + * vsp1_du_enable - Setup and enable a DU pipeline * @dev: the VSP device * @pipe_index: the DRM pipeline index * @cfg: the LIF configuration * * Configure the output part of VSP DRM pipeline for the given frame @cfg.width * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and - * source pads, and the LIF sink pad. + * source pads, and the LIF sink pad, and then starts the pipeline. * * The @pipe_index argument selects which DRM pipeline to setup. The number of * available pipelines depend on the VSP instance. @@ -649,14 +649,13 @@ EXPORT_SYMBOL_GPL(vsp1_du_init); * * Return 0 on success or a negative error code on failure. */ -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg) +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; unsigned long flags; - unsigned int i; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -665,60 +664,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - if (!cfg) { - struct vsp1_brx *brx; - - mutex_lock(&vsp1->drm->lock); - - brx = to_brx(&pipe->brx->subdev); - - /* - * NULL configuration means the CRTC is being disabled, stop - * the pipeline and turn the light off. - */ - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - struct vsp1_rwpf *rpf = pipe->inputs[i]; - - if (!rpf) - continue; - - /* - * Remove the RPF from the pipe and the list of BRx - * inputs. - */ - WARN_ON(!rpf->entity.pipe); - rpf->entity.pipe = NULL; - list_del(&rpf->entity.list_pipe); - pipe->inputs[i] = NULL; - - brx->inputs[rpf->brx_input].rpf = NULL; - } - - drm_pipe->du_complete = NULL; - pipe->num_inputs = 0; - - dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", - __func__, pipe->lif->index, - BRX_NAME(pipe->brx)); - - list_del(&pipe->brx->list_pipe); - pipe->brx->pipe = NULL; - pipe->brx = NULL; - - mutex_unlock(&vsp1->drm->lock); - - vsp1_dlm_reset(pipe->output->dlm); - vsp1_device_put(vsp1); - - dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); - - return 0; - } - /* Reset the underrun counter */ pipe->underrun_count = 0; @@ -741,7 +686,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; - vsp1_pipeline_dump(pipe, "LIF setup"); + vsp1_pipeline_dump(pipe, "DU enable"); /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); @@ -777,7 +722,80 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); +EXPORT_SYMBOL_GPL(vsp1_du_enable); + +/** + * vsp1_du_disable - Disable and stop a DU pipeline + * @dev: the VSP device + * @pipe_index: the DRM pipeline index + * + * The @pipe_index argument selects which DRM pipeline to disable. The number + * of available pipelines depend on the VSP instance. + * + * Return 0 on success or a negative error code on failure. + */ +int vsp1_du_disable(struct device *dev, unsigned int pipe_index) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_drm_pipeline *drm_pipe; + struct vsp1_pipeline *pipe; + struct vsp1_brx *brx; + unsigned int i; + int ret; + + if (pipe_index >= vsp1->info->lif_count) + return -EINVAL; + + drm_pipe = &vsp1->drm->pipe[pipe_index]; + pipe = &drm_pipe->pipe; + + mutex_lock(&vsp1->drm->lock); + + brx = to_brx(&pipe->brx->subdev); + + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); + + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + struct vsp1_rwpf *rpf = pipe->inputs[i]; + + if (!rpf) + continue; + + /* + * Remove the RPF from the pipe and the list of BRx + * inputs. + */ + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); + pipe->inputs[i] = NULL; + + brx->inputs[rpf->brx_input].rpf = NULL; + } + + drm_pipe->du_complete = NULL; + pipe->num_inputs = 0; + + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; + + mutex_unlock(&vsp1->drm->lock); + + vsp1_dlm_reset(pipe->output->dlm); + vsp1_device_put(vsp1); + + dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_disable); /** * vsp1_du_atomic_begin - Prepare for an atomic update diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d9b91ff02761..d2085cdb7fcb 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -44,8 +44,18 @@ struct vsp1_du_lif_config { void *callback_data; }; -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg); +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg); +int vsp1_du_disable(struct device *dev, unsigned int pipe_index); + +static inline int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg) +{ + if (cfg) + return vsp1_du_enable(dev, pipe_index, cfg); + else + return vsp1_du_disable(dev, pipe_index); +} /** * struct vsp1_du_atomic_config - VSP atomic configuration parameters From 5301fdf971972d36d7266432e91dc025d590c0fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:27 +0300 Subject: [PATCH 1343/5214] drm: renesas: rcar-du: Switch to new VSP API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is deprecated. Use the new vsp1_du_enable() and vsp1_du_disable() functions instead. Reviewed-by: Niklas Söderlund Acked-by: Dave Airlie Link: https://patch.msgid.link/20260511235637.3468558-4-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c index a4a49dcd8233..5cfb4d50bc67 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c @@ -87,12 +87,12 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) __rcar_du_plane_setup(crtc->group, &state); - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) From 6579799e96cefa8198253f4e059f8ca887c2e537 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:28 +0300 Subject: [PATCH 1344/5214] drm: renesas: rz-du: Switch to new VSP API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is deprecated. Use the new vsp1_du_enable() and vsp1_du_disable() functions instead. Reviewed-by: Niklas Söderlund Acked-by: Biju Das Acked-by: Dave Airlie Link: https://patch.msgid.link/20260511235637.3468558-5-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c index bd486377f037..d5a1d36db2c1 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c @@ -55,12 +55,12 @@ void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc) .callback_data = crtc, }; - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc) From 38abda35a6b8769a57c12338aab88e7e979f8c24 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:29 +0300 Subject: [PATCH 1345/5214] media: renesas: vsp1: Use mutex guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manual mutex locking and unlocking with guards. This simplifies error paths and reduces the amount of code. Limit the changes to locations where the guard covers until the end of the function to ease review. Scoped guards will be introduced separately. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-6-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_brx.c | 26 ++++------- .../media/platform/renesas/vsp1/vsp1_drm.c | 7 +-- .../media/platform/renesas/vsp1/vsp1_entity.c | 19 +++----- .../media/platform/renesas/vsp1/vsp1_histo.c | 35 +++++---------- .../media/platform/renesas/vsp1/vsp1_hsit.c | 15 +++---- .../media/platform/renesas/vsp1/vsp1_rwpf.c | 44 ++++++------------- .../media/platform/renesas/vsp1/vsp1_sru.c | 13 ++---- .../media/platform/renesas/vsp1/vsp1_uds.c | 13 ++---- .../media/platform/renesas/vsp1/vsp1_uif.c | 29 ++++-------- .../media/platform/renesas/vsp1/vsp1_video.c | 20 ++++----- .../media/platform/renesas/vsp1/vsp1_wpf.c | 17 +++---- 11 files changed, 77 insertions(+), 161 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index b1a2c68e9944..ce68d911ac30 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -130,15 +130,12 @@ static int brx_set_format(struct v4l2_subdev *subdev, struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; brx_try_format(brx, state, fmt->pad, &fmt->format); @@ -172,9 +169,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, *format = fmt->format; } -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static int brx_get_selection(struct v4l2_subdev *subdev, @@ -219,7 +214,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; - int ret = 0; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -227,13 +221,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* * The compose rectangle top left corner must be inside the output @@ -254,9 +246,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops brx_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 1f431874064d..1439cf7bfb59 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -920,7 +920,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, drm_pipe->crc = cfg->crc; - mutex_lock(&vsp1->drm->lock); + guard(mutex)(&vsp1->drm->lock); if (cfg->writeback.pixelformat) { const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback; @@ -929,7 +929,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, wb_cfg->pixelformat, wb_cfg->pitch); if (WARN_ON(ret < 0)) - goto done; + return; pipe->output->mem.addr[0] = wb_cfg->mem[0]; pipe->output->mem.addr[1] = wb_cfg->mem[1]; @@ -942,9 +942,6 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, vsp1_pipeline_dump(pipe, "atomic update"); vsp1_du_pipeline_configure(pipe); - -done: - mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1dad9589768c..1a1dafe5331e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -172,9 +172,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); - mutex_unlock(&entity->lock); return 0; } @@ -308,22 +308,19 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; - int ret = 0; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); state = vsp1_entity_get_state(entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ fmt->format = *format; - goto done; + return 0; } /* @@ -369,9 +366,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, selection->width = format->width; selection->height = format->height; -done: - mutex_unlock(&entity->lock); - return ret; + return 0; } static int vsp1_entity_init_state(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 3f87a2c9df0e..72f6ef2fdc4f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -196,18 +196,15 @@ static int histo_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -237,13 +234,10 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&histo->entity.lock); - return ret; + return 0; } static int histo_set_crop(struct v4l2_subdev *subdev, @@ -321,29 +315,22 @@ static int histo_set_selection(struct v4l2_subdev *subdev, { struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_subdev_state *state; - int ret; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; if (sel->target == V4L2_SEL_TGT_CROP) - ret = histo_set_crop(subdev, state, sel); + return histo_set_crop(subdev, state, sel); else if (sel->target == V4L2_SEL_TGT_COMPOSE) - ret = histo_set_compose(subdev, state, sel); + return histo_set_compose(subdev, state, sel); else - ret = -EINVAL; - -done: - mutex_unlock(&histo->entity.lock); - return ret; + return -EINVAL; } static int histo_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 830e124beb7b..df069c228243 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -115,15 +115,12 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&hsit->entity.lock); + guard(mutex)(&hsit->entity.lock); state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); @@ -133,7 +130,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, * modified. */ fmt->format = *format; - goto done; + return 0; } format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 @@ -161,9 +158,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, vsp1_entity_adjust_color_space(format); -done: - mutex_unlock(&hsit->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index c72518b29f84..ced01870acd6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -116,15 +116,12 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -174,7 +171,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; fmt->format.flags = flags; - goto done; + return 0; } format->code = fmt->format.code; @@ -213,9 +210,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->height = fmt->format.width; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, @@ -225,7 +220,6 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -234,13 +228,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -256,13 +248,10 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, @@ -275,7 +264,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -287,13 +275,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Make sure the crop rectangle is entirely contained in the image. */ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); @@ -342,9 +328,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format->width = crop->width; format->height = crop->height; -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 94149da0c900..3fd9fde5c724 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -216,15 +216,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&sru->entity.lock); + guard(mutex)(&sru->entity.lock); state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; sru_try_format(sru, state, fmt->pad, &fmt->format); @@ -239,9 +236,7 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, SRU_PAD_SOURCE, format); } -done: - mutex_unlock(&sru->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops sru_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index dd4722315c56..9f7bb112929e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -199,15 +199,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&uds->entity.lock); + guard(mutex)(&uds->entity.lock); state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; uds_try_format(uds, state, fmt->pad, &fmt->format); @@ -222,9 +219,7 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, UDS_PAD_SOURCE, format); } -done: - mutex_unlock(&uds->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 3aefe5c9d421..52dbfe58a70d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -60,18 +60,15 @@ static int uif_get_selection(struct v4l2_subdev *subdev, struct vsp1_uif *uif = to_uif(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; if (sel->pad != UIF_PAD_SINK) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -88,13 +85,10 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } static int uif_set_selection(struct v4l2_subdev *subdev, @@ -105,19 +99,16 @@ static int uif_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; - int ret = 0; if (sel->pad != UIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* The crop rectangle must be inside the input frame. */ format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); @@ -133,9 +124,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index fe1dac11d4ae..a9ed6bc87b00 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -595,9 +595,9 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe) { struct media_device *mdev = &pipe->output->entity.vsp1->media_dev; - mutex_lock(&mdev->graph_mutex); + guard(mutex)(&mdev->graph_mutex); + kref_put(&pipe->kref, vsp1_video_pipeline_release); - mutex_unlock(&mdev->graph_mutex); } /* ----------------------------------------------------------------------------- @@ -938,9 +938,9 @@ vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) if (format->type != video->queue.type) return -EINVAL; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); + format->fmt.pix_mp = video->rwpf->format; - mutex_unlock(&video->lock); return 0; } @@ -972,19 +972,15 @@ vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) if (ret < 0) return ret; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; video->rwpf->format = format->fmt.pix_mp; video->rwpf->fmtinfo = info; -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index cd6c5592221b..e7ed3c8e9e90 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -47,7 +47,6 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) struct v4l2_mbus_framefmt *sink_format; struct v4l2_mbus_framefmt *source_format; bool rotate; - int ret = 0; /* * Only consider the 0°/180° from/to 90°/270° modifications, the rest @@ -58,19 +57,17 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) return 0; /* Changing rotation isn't allowed when buffers are allocated. */ - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; sink_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SOURCE); - mutex_lock(&wpf->entity.lock); + guard(mutex)(&wpf->entity.lock); if (rotate) { source_format->width = sink_format->height; @@ -82,11 +79,7 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) wpf->flip.rotate = rotate; - mutex_unlock(&wpf->entity.lock); - -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) From 2bf10aeb232e91d25799885343f2e282bf9659a9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:30 +0300 Subject: [PATCH 1346/5214] media: renesas: vsp1: Use mutex scoped guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace remaining manual mutex locking and unlocking with scoped guards. This simplifies error paths and reduces the amount of code. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-7-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_brx.c | 7 +- .../media/platform/renesas/vsp1/vsp1_drm.c | 117 ++++++++---------- .../media/platform/renesas/vsp1/vsp1_entity.c | 8 +- .../media/platform/renesas/vsp1/vsp1_hgo.c | 10 +- .../media/platform/renesas/vsp1/vsp1_hgt.c | 16 +-- .../media/platform/renesas/vsp1/vsp1_video.c | 60 ++++----- 6 files changed, 102 insertions(+), 116 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index ce68d911ac30..d150a92b2617 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -196,9 +196,10 @@ static int brx_get_selection(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&brx->entity.lock); - sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); - mutex_unlock(&brx->entity.lock); + scoped_guard(mutex, &brx->entity.lock) { + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + } + return 0; default: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 1439cf7bfb59..2b64d9b5a81c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -675,43 +675,37 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, __func__, pipe_index, cfg->width, cfg->height, pipe->interlaced ? "i" : ""); - mutex_lock(&vsp1->drm->lock); + scoped_guard(mutex, &vsp1->drm->lock) { + /* Setup formats through the pipeline. */ + ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); + if (ret < 0) + return ret; - /* Setup formats through the pipeline. */ - ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); - if (ret < 0) - goto unlock; + ret = vsp1_du_pipeline_setup_output(vsp1, pipe); + if (ret < 0) + return ret; - ret = vsp1_du_pipeline_setup_output(vsp1, pipe); - if (ret < 0) - goto unlock; + vsp1_pipeline_dump(pipe, "DU enable"); - vsp1_pipeline_dump(pipe, "DU enable"); + /* Enable the VSP1. */ + ret = vsp1_device_get(vsp1); + if (ret < 0) + return ret; - /* Enable the VSP1. */ - ret = vsp1_device_get(vsp1); - if (ret < 0) - goto unlock; + /* + * Register a callback to allow us to notify the DRM driver of frame + * completion events. + */ + drm_pipe->du_complete = cfg->callback; + drm_pipe->du_private = cfg->callback_data; - /* - * Register a callback to allow us to notify the DRM driver of frame - * completion events. - */ - drm_pipe->du_complete = cfg->callback; - drm_pipe->du_private = cfg->callback_data; + /* Disable the display interrupts. */ + vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); + vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); - /* Disable the display interrupts. */ - vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); - vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); - - /* Configure all entities in the pipeline. */ - vsp1_du_pipeline_configure(pipe); - -unlock: - mutex_unlock(&vsp1->drm->lock); - - if (ret < 0) - return ret; + /* Configure all entities in the pipeline. */ + vsp1_du_pipeline_configure(pipe); + } /* Start the pipeline. */ spin_lock_irqsave(&pipe->irqlock, flags); @@ -739,7 +733,6 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - struct vsp1_brx *brx; unsigned int i; int ret; @@ -749,45 +742,43 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - mutex_lock(&vsp1->drm->lock); + scoped_guard(mutex, &vsp1->drm->lock) { + struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); - brx = to_brx(&pipe->brx->subdev); + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + struct vsp1_rwpf *rpf = pipe->inputs[i]; - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - struct vsp1_rwpf *rpf = pipe->inputs[i]; + if (!rpf) + continue; - if (!rpf) - continue; + /* + * Remove the RPF from the pipe and the list of BRx + * inputs. + */ + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); + pipe->inputs[i] = NULL; - /* - * Remove the RPF from the pipe and the list of BRx - * inputs. - */ - WARN_ON(!rpf->entity.pipe); - rpf->entity.pipe = NULL; - list_del(&rpf->entity.list_pipe); - pipe->inputs[i] = NULL; + brx->inputs[rpf->brx_input].rpf = NULL; + } - brx->inputs[rpf->brx_input].rpf = NULL; + drm_pipe->du_complete = NULL; + pipe->num_inputs = 0; + + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; } - drm_pipe->du_complete = NULL; - pipe->num_inputs = 0; - - dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", - __func__, pipe->lif->index, - BRX_NAME(pipe->brx)); - - list_del(&pipe->brx->list_pipe); - pipe->brx->pipe = NULL; - pipe->brx = NULL; - - mutex_unlock(&vsp1->drm->lock); - vsp1_dlm_reset(pipe->output->dlm); vsp1_device_put(vsp1); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1a1dafe5331e..50aae8ee724f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -216,10 +216,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); - format = v4l2_subdev_state_get_format(state, 0); - code->code = format->code; - mutex_unlock(&entity->lock); + scoped_guard(mutex, &entity->lock) { + format = v4l2_subdev_state_get_format(state, 0); + code->code = format->code; + } } return 0; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 2c8ce7175a4e..0ef512e3a94b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -153,11 +153,11 @@ static void hgo_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); - mutex_lock(hgo->ctrls.handler.lock); - hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; - if (hgo->ctrls.num_bins) - hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; - mutex_unlock(hgo->ctrls.handler.lock); + scoped_guard(mutex, hgo->ctrls.handler.lock) { + hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; + if (hgo->ctrls.num_bins) + hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; + } hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 858f330d44fa..78b5a9201c70 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -152,15 +152,15 @@ static void hgt_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); - mutex_lock(hgt->ctrls.lock); - for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { - lower = hgt->hue_areas[i*2 + 0]; - upper = hgt->hue_areas[i*2 + 1]; - vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), - (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | - (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + scoped_guard(mutex, hgt->ctrls.lock) { + for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { + lower = hgt->hue_areas[i*2 + 0]; + upper = hgt->hue_areas[i*2 + 1]; + vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), + (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | + (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + } } - mutex_unlock(hgt->ctrls.lock); hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index a9ed6bc87b00..b110f0309b26 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -815,22 +815,21 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; - mutex_lock(&pipe->lock); - if (pipe->stream_count == pipe->num_inputs) { - ret = vsp1_video_setup_pipeline(pipe); - if (ret < 0) { - vsp1_video_release_buffers(video); - vsp1_video_cleanup_pipeline(pipe); - mutex_unlock(&pipe->lock); - return ret; + scoped_guard(mutex, &pipe->lock) { + if (pipe->stream_count == pipe->num_inputs) { + ret = vsp1_video_setup_pipeline(pipe); + if (ret < 0) { + vsp1_video_release_buffers(video); + vsp1_video_cleanup_pipeline(pipe); + return ret; + } + + start_pipeline = true; } - start_pipeline = true; + pipe->stream_count++; } - pipe->stream_count++; - mutex_unlock(&pipe->lock); - /* * vsp1_pipeline_ready() is not sufficient to establish that all streams * are prepared and the pipeline is configured, as multiple streams @@ -864,16 +863,17 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) pipe->buffers_ready &= ~(1 << video->pipe_index); spin_unlock_irqrestore(&video->irqlock, flags); - mutex_lock(&pipe->lock); - if (--pipe->stream_count == pipe->num_inputs) { - /* Stop the pipeline. */ - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(video->vsp1->dev, "pipeline stop timeout\n"); + scoped_guard(mutex, &pipe->lock) { + if (--pipe->stream_count == pipe->num_inputs) { + /* Stop the pipeline. */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(video->vsp1->dev, + "pipeline stop timeout\n"); - vsp1_video_cleanup_pipeline(pipe); + vsp1_video_cleanup_pipeline(pipe); + } } - mutex_unlock(&pipe->lock); video_device_pipeline_stop(&video->video); vsp1_video_release_buffers(video); @@ -1000,22 +1000,16 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * touching an entity in the pipeline can be activated or deactivated * once streaming is started. */ - mutex_lock(&mdev->graph_mutex); + scoped_guard(mutex, &mdev->graph_mutex) { + pipe = vsp1_video_pipeline_get(video); + if (IS_ERR(pipe)) + return PTR_ERR(pipe); - pipe = vsp1_video_pipeline_get(video); - if (IS_ERR(pipe)) { - mutex_unlock(&mdev->graph_mutex); - return PTR_ERR(pipe); + ret = __video_device_pipeline_start(&video->video, &pipe->pipe); + if (ret < 0) + goto err_pipe; } - ret = __video_device_pipeline_start(&video->video, &pipe->pipe); - if (ret < 0) { - mutex_unlock(&mdev->graph_mutex); - goto err_pipe; - } - - mutex_unlock(&mdev->graph_mutex); - /* * Verify that the configured format matches the output of the connected * subdev. From 84928d3f32be0f6dc90822823e1a185956354386 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:31 +0300 Subject: [PATCH 1347/5214] media: renesas: vsp1: Use spinlock guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manual spinlock locking and unlocking with guards. This simplifies error paths and reduces the amount of code. Limit the changes to locations where the guard covers until the end of the function to ease review. Scoped guards will be introduced separately. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-8-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 49 ++++++------------- .../media/platform/renesas/vsp1/vsp1_histo.c | 20 +++----- .../media/platform/renesas/vsp1/vsp1_pipe.c | 9 +--- .../media/platform/renesas/vsp1/vsp1_video.c | 14 ++---- .../media/platform/renesas/vsp1/vsp1_wpf.c | 4 +- 5 files changed, 31 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 6c5578d9d2de..4a19ff1437b0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -336,9 +336,8 @@ void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) { struct vsp1_dl_body *dlb = NULL; - unsigned long flags; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); @@ -346,8 +345,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) refcount_set(&dlb->refcnt, 1); } - spin_unlock_irqrestore(&pool->lock, flags); - return dlb; } @@ -359,8 +356,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) */ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) { - unsigned long flags; - if (!dlb) return; @@ -369,9 +364,9 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) dlb->num_entries = 0; - spin_lock_irqsave(&dlb->pool->lock, flags); + guard(spinlock_irqsave)(&dlb->pool->lock); + list_add_tail(&dlb->free, &dlb->pool->free); - spin_unlock_irqrestore(&dlb->pool->lock, flags); } /** @@ -493,9 +488,8 @@ static struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) { struct vsp1_dl_ext_cmd *cmd = NULL; - unsigned long flags; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, @@ -503,24 +497,20 @@ struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) list_del(&cmd->free); } - spin_unlock_irqrestore(&pool->lock, flags); - return cmd; } static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd) { - unsigned long flags; - if (!cmd) return; /* Reset flags, these mark data usage. */ cmd->flags = 0; - spin_lock_irqsave(&cmd->pool->lock, flags); + guard(spinlock_irqsave)(&cmd->pool->lock); + list_add_tail(&cmd->free, &cmd->pool->free); - spin_unlock_irqrestore(&cmd->pool->lock, flags); } static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool) @@ -611,11 +601,10 @@ static void vsp1_dl_list_free(struct vsp1_dl_list *dl) struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl = NULL; - unsigned long flags; lockdep_assert_not_held(&dlm->lock); - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (!list_empty(&dlm->free)) { dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); @@ -629,8 +618,6 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) dl->allocated = true; } - spin_unlock_irqrestore(&dlm->lock, flags); - return dl; } @@ -690,14 +677,12 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) */ void vsp1_dl_list_put(struct vsp1_dl_list *dl) { - unsigned long flags; - if (!dl) return; - spin_lock_irqsave(&dl->dlm->lock, flags); + guard(spinlock_irqsave)(&dl->dlm->lock); + __vsp1_dl_list_put(dl); - spin_unlock_irqrestore(&dl->dlm->lock, flags); } /** @@ -937,7 +922,6 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_next; - unsigned long flags; /* Fill the header for the head and chained display lists. */ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); @@ -950,14 +934,12 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (dlm->singleshot) vsp1_dl_list_commit_singleshot(dl); else vsp1_dl_list_commit_continuous(dl); - - spin_unlock_irqrestore(&dlm->lock, flags); } /* ----------------------------------------------------------------------------- @@ -991,7 +973,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) u32 status = vsp1_read(vsp1, VI6_STATUS); unsigned int flags = 0; - spin_lock(&dlm->lock); + guard(spinlock)(&dlm->lock); /* * The mem-to-mem pipelines work in single-shot mode. No new display @@ -1001,7 +983,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) __vsp1_dl_list_put(dlm->active); dlm->active = NULL; flags |= VSP1_DL_FRAME_END_COMPLETED; - goto done; + return flags; } /* @@ -1011,7 +993,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * and retry. */ if (vsp1_dl_list_hw_update_pending(dlm)) - goto done; + return flags; /* * Progressive streams report only TOP fields. If we have a BOTTOM @@ -1019,7 +1001,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * next frame end interrupt. */ if (status & VI6_STATUS_FLD_STD(dlm->index)) - goto done; + return flags; /* * If the active display list has the writeback flag set, the frame @@ -1058,9 +1040,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -done: - spin_unlock(&dlm->lock); - return flags; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 72f6ef2fdc4f..97dbfb93abe9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -35,20 +35,18 @@ to_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf) struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { - struct vsp1_histogram_buffer *buf = NULL; + struct vsp1_histogram_buffer *buf; - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); if (list_empty(&histo->irqqueue)) - goto done; + return NULL; buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer, queue); list_del(&buf->queue); histo->readout = true; -done: - spin_unlock(&histo->irqlock); return buf; } @@ -68,10 +66,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); + histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -123,9 +121,9 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); + list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -138,7 +136,7 @@ static void histo_stop_streaming(struct vb2_queue *vq) struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -147,8 +145,6 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - - spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 5d769cc42fe1..9e2b9b15331a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -487,14 +487,9 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) { - unsigned long flags; - bool stopped; + guard(spinlock_irqsave)(&pipe->irqlock); - spin_lock_irqsave(&pipe->irqlock, flags); - stopped = pipe->state == VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); - - return stopped; + return pipe->state == VSP1_PIPELINE_STOPPED; } int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index b110f0309b26..b5b82e12fcb1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -672,7 +672,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) if (!empty) return; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; @@ -680,8 +680,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) if (vb2_start_streaming_called(&video->queue) && vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - - spin_unlock_irqrestore(&pipe->irqlock, flags); } static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) @@ -783,14 +781,13 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) static void vsp1_video_release_buffers(struct vsp1_video *video) { struct vsp1_vb2_buffer *buffer; - unsigned long flags; /* Remove all buffers from the IRQ queue. */ - spin_lock_irqsave(&video->irqlock, flags); + guard(spinlock_irqsave)(&video->irqlock); + list_for_each_entry(buffer, &video->irqqueue, queue) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); } static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) @@ -812,7 +809,6 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; - unsigned long flags; int ret; scoped_guard(mutex, &pipe->lock) { @@ -840,10 +836,10 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (!start_pipeline) return 0; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); + if (vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); return 0; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index e7ed3c8e9e90..327c7457126f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -111,9 +111,9 @@ static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) if (rotation == 180 || rotation == 270) flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); - spin_lock_irq(&wpf->flip.lock); + guard(spinlock_irq)(&wpf->flip.lock); + wpf->flip.pending = flip; - spin_unlock_irq(&wpf->flip.lock); return 0; } From de52d7a161290e64947c795195a231aa66a73c16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:32 +0300 Subject: [PATCH 1348/5214] media: renesas: vsp1: Use spinlock scoped guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace remaining manual spinlock locking and unlocking with scoped guards. This simplifies error paths and reduces the amount of code. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-9-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_clu.c | 15 +++-- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 14 ++--- .../media/platform/renesas/vsp1/vsp1_drm.c | 7 +-- .../media/platform/renesas/vsp1/vsp1_lut.c | 15 +++-- .../media/platform/renesas/vsp1/vsp1_pipe.c | 15 +++-- .../media/platform/renesas/vsp1/vsp1_video.c | 59 ++++++++----------- .../media/platform/renesas/vsp1/vsp1_wpf.c | 9 ++- 7 files changed, 59 insertions(+), 75 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 04c466c4da81..a6e4bcab5101 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -53,9 +53,9 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) for (i = 0; i < CLU_SIZE; ++i) vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); - spin_lock_irq(&clu->lock); - swap(clu->clu, dlb); - spin_unlock_irq(&clu->lock); + scoped_guard(spinlock_irq, &clu->lock) { + swap(clu->clu, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -162,7 +162,6 @@ static void clu_configure_frame(struct vsp1_entity *entity, { struct vsp1_clu *clu = to_clu(&entity->subdev); struct vsp1_dl_body *clu_dlb; - unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; /* 2D mode can only be used with the YCbCr pixel encoding. */ @@ -173,10 +172,10 @@ static void clu_configure_frame(struct vsp1_entity *entity, vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); - spin_lock_irqsave(&clu->lock, flags); - clu_dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + scoped_guard(spinlock_irqsave, &clu->lock) { + clu_dlb = clu->clu; + clu->clu = NULL; + } if (clu_dlb) { vsp1_dl_list_add_body(dl, clu_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 4a19ff1437b0..3dc74fed91dc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -1064,17 +1064,15 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) { - unsigned long flags; size_t list_count; - spin_lock_irqsave(&dlm->lock, flags); + scoped_guard(spinlock_irqsave, &dlm->lock) { + __vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->queued); + __vsp1_dl_list_put(dlm->pending); - __vsp1_dl_list_put(dlm->active); - __vsp1_dl_list_put(dlm->queued); - __vsp1_dl_list_put(dlm->pending); - - list_count = list_count_nodes(&dlm->free); - spin_unlock_irqrestore(&dlm->lock, flags); + list_count = list_count_nodes(&dlm->free); + } WARN_ON_ONCE(list_count != dlm->list_count); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 2b64d9b5a81c..f6fbd3475329 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -655,7 +655,6 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - unsigned long flags; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -708,9 +707,9 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, } /* Start the pipeline. */ - spin_lock_irqsave(&pipe->irqlock, flags); - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + vsp1_pipeline_run(pipe); + } dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 94bdedcc5c92..a22c31e17cb7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -50,9 +50,9 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); - spin_lock_irq(&lut->lock); - swap(lut->lut, dlb); - spin_unlock_irq(&lut->lock); + scoped_guard(spinlock_irq, &lut->lock) { + swap(lut->lut, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -132,12 +132,11 @@ static void lut_configure_frame(struct vsp1_entity *entity, { struct vsp1_lut *lut = to_lut(&entity->subdev); struct vsp1_dl_body *lut_dlb; - unsigned long flags; - spin_lock_irqsave(&lut->lock, flags); - lut_dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + scoped_guard(spinlock_irqsave, &lut->lock) { + lut_dlb = lut->lut; + lut->lut = NULL; + } if (lut_dlb) { vsp1_dl_list_add_body(dl, lut_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 9e2b9b15331a..12079fbdc3d3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -496,7 +496,6 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; - unsigned long flags; int ret; if (pipe->lif) { @@ -506,16 +505,16 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) */ ret = vsp1_reset_wpf(vsp1, pipe->output->entity.index); if (ret == 0) { - spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + pipe->state = VSP1_PIPELINE_STOPPED; + } } } else { /* Otherwise just request a stop and wait. */ - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index b5b82e12fcb1..5a1d284213ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -209,26 +209,21 @@ vsp1_video_complete_buffer(struct vsp1_video *video) struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; - unsigned long flags; unsigned int i; - spin_lock_irqsave(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + if (list_empty(&video->irqqueue)) + return NULL; - if (list_empty(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return NULL; - } - - done = list_first_entry(&video->irqqueue, - struct vsp1_vb2_buffer, queue); - - list_del(&done->queue); - - if (!list_empty(&video->irqqueue)) - next = list_first_entry(&video->irqqueue, + done = list_first_entry(&video->irqqueue, struct vsp1_vb2_buffer, queue); - spin_unlock_irqrestore(&video->irqlock, flags); + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_vb2_buffer, queue); + } done->buf.sequence = pipe->sequence; done->buf.vb2_buf.timestamp = ktime_get_ns(); @@ -661,13 +656,12 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); - unsigned long flags; bool empty; - spin_lock_irqsave(&video->irqlock, flags); - empty = list_empty(&video->irqqueue); - list_add_tail(&buf->queue, &video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + empty = list_empty(&video->irqqueue); + list_add_tail(&buf->queue, &video->irqqueue); + } if (!empty) return; @@ -848,16 +842,15 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; - unsigned long flags; int ret; /* * Clear the buffers ready flag to make sure the device won't be started * by a QBUF on the video node on the other side of the pipeline. */ - spin_lock_irqsave(&video->irqlock, flags); - pipe->buffers_ready &= ~(1 << video->pipe_index); - spin_unlock_irqrestore(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + pipe->buffers_ready &= ~(1 << video->pipe_index); + } scoped_guard(mutex, &pipe->lock) { if (--pipe->stream_count == pipe->num_inputs) { @@ -1123,7 +1116,6 @@ static const struct media_entity_operations vsp1_video_media_ops = { void vsp1_video_suspend(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; int ret; @@ -1143,10 +1135,10 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) if (pipe == NULL) continue; - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } } for (i = 0; i < vsp1->info->wpf_count; ++i) { @@ -1170,7 +1162,6 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) void vsp1_video_resume(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; /* Resume all running pipelines. */ @@ -1191,10 +1182,10 @@ void vsp1_video_resume(struct vsp1_device *vsp1) */ pipe->configured = false; - spin_lock_irqsave(&pipe->irqlock, flags); - if (vsp1_pipeline_ready(pipe)) - vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + } } } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 327c7457126f..0ec707d2913f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -366,13 +366,12 @@ static void wpf_configure_frame(struct vsp1_entity *entity, const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); - unsigned long flags; u32 outfmt; - spin_lock_irqsave(&wpf->flip.lock, flags); - wpf->flip.active = (wpf->flip.active & ~mask) - | (wpf->flip.pending & mask); - spin_unlock_irqrestore(&wpf->flip.lock, flags); + scoped_guard(spinlock_irqsave, &wpf->flip.lock) { + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + } outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; From 20b317d4862cce1b92195d5915b3f3a139b28b8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:33 +0300 Subject: [PATCH 1349/5214] media: renesas: vsp1: Simplify iteration over format arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a vsp1_for_each_format() macro to iterate over format arrays, to improve readability. No functional change intended. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-10-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_pipe.c | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 12079fbdc3d3..32bb02ce0366 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -229,6 +229,10 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { 1, { 32, 0, 0 }, false, false, 1, 1, false }, }; +#define vsp1_for_each_format(info, formats) \ + for (const struct vsp1_format_info *info = &formats[0]; \ + info < formats + ARRAY_SIZE(formats); ++info) + /** * vsp1_get_format_info - Retrieve format information for a 4CC * @vsp1: the VSP1 device @@ -240,30 +244,20 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->fourcc == fourcc) return info; } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->fourcc == fourcc) return info; } } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->fourcc == fourcc) return info; } @@ -287,8 +281,6 @@ const struct vsp1_format_info * vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, u32 code) { - unsigned int i; - if (!code) { if (index < ARRAY_SIZE(vsp1_video_formats)) return &vsp1_video_formats[index]; @@ -308,9 +300,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, return NULL; } - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->mbus == code) { if (!index) return info; @@ -319,10 +309,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->mbus == code) { if (!index) return info; @@ -332,10 +319,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->mbus == code) { if (!index) return info; From bb16e2895a4161aa90a884bc2e9ef3f229f40e46 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:35 +0300 Subject: [PATCH 1350/5214] media: renesas: vsp1: Drop deprecated vsp1_du_setup_lif() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() is deprecated and its last users are gone. Drop it. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-12-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/vsp1.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d2085cdb7fcb..98089e0a4385 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -48,15 +48,6 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, const struct vsp1_du_lif_config *cfg); int vsp1_du_disable(struct device *dev, unsigned int pipe_index); -static inline int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg) -{ - if (cfg) - return vsp1_du_enable(dev, pipe_index, cfg); - else - return vsp1_du_disable(dev, pipe_index); -} - /** * struct vsp1_du_atomic_config - VSP atomic configuration parameters * @pixelformat: plane pixel format (V4L2 4CC) From 570ff5c850e71b0d038758b91b5bbccea534d877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 30 Apr 2026 18:52:14 +0200 Subject: [PATCH 1351/5214] comedi: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the various struct pci_device_id arrays were initialized by list expressions. This isn't easily readable if you're not into PCI. Using named initializers is more explicit and thus easier to parse. Also skip explicit assignments of 0 (which the compiler takes care of). The secret plan is to make struct pci_device_id::driver_data an anonymous union (similar to https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/) and that requires named initializers. But it's also a nice cleanup on its own. This change doesn't introduce changes to the compiled pci_device_id arrays. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260430165214.449166-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/8255_pci.c | 32 +++--- drivers/comedi/drivers/addi_apci_1032.c | 4 +- drivers/comedi/drivers/addi_apci_1500.c | 4 +- drivers/comedi/drivers/addi_apci_1516.c | 8 +- drivers/comedi/drivers/addi_apci_1564.c | 4 +- drivers/comedi/drivers/addi_apci_16xx.c | 6 +- drivers/comedi/drivers/addi_apci_2032.c | 4 +- drivers/comedi/drivers/addi_apci_2200.c | 4 +- drivers/comedi/drivers/addi_apci_3120.c | 6 +- drivers/comedi/drivers/addi_apci_3501.c | 4 +- drivers/comedi/drivers/addi_apci_3xxx.c | 52 ++++----- drivers/comedi/drivers/adl_pci6208.c | 7 +- drivers/comedi/drivers/adl_pci7250.c | 18 +-- drivers/comedi/drivers/adl_pci7x3x.c | 14 +-- drivers/comedi/drivers/adl_pci8164.c | 4 +- drivers/comedi/drivers/adl_pci9111.c | 6 +- drivers/comedi/drivers/adl_pci9118.c | 8 +- drivers/comedi/drivers/adv_pci1710.c | 62 +++++----- drivers/comedi/drivers/adv_pci1720.c | 4 +- drivers/comedi/drivers/adv_pci1723.c | 4 +- drivers/comedi/drivers/adv_pci1724.c | 4 +- drivers/comedi/drivers/adv_pci1760.c | 4 +- drivers/comedi/drivers/adv_pci_dio.c | 30 ++--- drivers/comedi/drivers/amplc_dio200_pci.c | 12 +- drivers/comedi/drivers/amplc_pci224.c | 6 +- drivers/comedi/drivers/amplc_pci230.c | 6 +- drivers/comedi/drivers/amplc_pci236.c | 4 +- drivers/comedi/drivers/amplc_pci263.c | 4 +- drivers/comedi/drivers/cb_pcidas.c | 18 +-- drivers/comedi/drivers/cb_pcidas64.c | 44 ++++---- drivers/comedi/drivers/cb_pcidda.c | 14 +-- drivers/comedi/drivers/cb_pcimdas.c | 6 +- drivers/comedi/drivers/cb_pcimdda.c | 4 +- drivers/comedi/drivers/contec_pci_dio.c | 4 +- drivers/comedi/drivers/daqboard2000.c | 10 +- drivers/comedi/drivers/das08_pci.c | 4 +- drivers/comedi/drivers/dt3000.c | 16 +-- drivers/comedi/drivers/dyna_pci10xx.c | 4 +- drivers/comedi/drivers/gsc_hpdi.c | 6 +- drivers/comedi/drivers/icp_multi.c | 4 +- drivers/comedi/drivers/jr3_pci.c | 12 +- drivers/comedi/drivers/ke_counter.c | 4 +- drivers/comedi/drivers/me4000.c | 28 ++--- drivers/comedi/drivers/me_daq.c | 6 +- drivers/comedi/drivers/mf6x4.c | 11 +- drivers/comedi/drivers/ni_6527.c | 6 +- drivers/comedi/drivers/ni_65xx.c | 46 ++++---- drivers/comedi/drivers/ni_660x.c | 16 +-- drivers/comedi/drivers/ni_670x.c | 8 +- drivers/comedi/drivers/ni_labpc_pci.c | 4 +- drivers/comedi/drivers/ni_pcidio.c | 8 +- drivers/comedi/drivers/ni_pcimio.c | 132 +++++++++++----------- drivers/comedi/drivers/rtd520.c | 6 +- drivers/comedi/drivers/s626.c | 6 +- 54 files changed, 381 insertions(+), 371 deletions(-) diff --git a/drivers/comedi/drivers/8255_pci.c b/drivers/comedi/drivers/8255_pci.c index 8498cabe4d91..737e4cf45571 100644 --- a/drivers/comedi/drivers/8255_pci.c +++ b/drivers/comedi/drivers/8255_pci.c @@ -267,25 +267,25 @@ static int pci_8255_pci_probe(struct pci_dev *dev, static const struct pci_device_id pci_8255_pci_table[] = { #ifdef CONFIG_HAS_IOPORT - { PCI_VDEVICE(ADLINK, 0x7224), BOARD_ADLINK_PCI7224 }, - { PCI_VDEVICE(ADLINK, 0x7248), BOARD_ADLINK_PCI7248 }, - { PCI_VDEVICE(ADLINK, 0x7296), BOARD_ADLINK_PCI7296 }, - { PCI_VDEVICE(CB, 0x0028), BOARD_CB_PCIDIO24 }, - { PCI_VDEVICE(CB, 0x0014), BOARD_CB_PCIDIO24H }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_CB, 0x000b, 0x0000, 0x0000), + { PCI_VDEVICE(ADLINK, 0x7224), .driver_data = BOARD_ADLINK_PCI7224 }, + { PCI_VDEVICE(ADLINK, 0x7248), .driver_data = BOARD_ADLINK_PCI7248 }, + { PCI_VDEVICE(ADLINK, 0x7296), .driver_data = BOARD_ADLINK_PCI7296 }, + { PCI_VDEVICE(CB, 0x0028), .driver_data = BOARD_CB_PCIDIO24 }, + { PCI_VDEVICE(CB, 0x0014), .driver_data = BOARD_CB_PCIDIO24H }, + { PCI_VDEVICE_SUB(CB, 0x000b, 0x0000, 0x0000), .driver_data = BOARD_CB_PCIDIO48H_OLD }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_CB, 0x000b, PCI_VENDOR_ID_CB, 0x000b), + { PCI_VDEVICE_SUB(CB, 0x000b, PCI_VENDOR_ID_CB, 0x000b), .driver_data = BOARD_CB_PCIDIO48H_NEW }, - { PCI_VDEVICE(CB, 0x0017), BOARD_CB_PCIDIO96H }, + { PCI_VDEVICE(CB, 0x0017), .driver_data = BOARD_CB_PCIDIO96H }, #endif /* CONFIG_HAS_IOPORT */ - { PCI_VDEVICE(NI, 0x0160), BOARD_NI_PCIDIO96 }, - { PCI_VDEVICE(NI, 0x1630), BOARD_NI_PCIDIO96B }, - { PCI_VDEVICE(NI, 0x13c0), BOARD_NI_PXI6508 }, - { PCI_VDEVICE(NI, 0x0400), BOARD_NI_PCI6503 }, - { PCI_VDEVICE(NI, 0x1250), BOARD_NI_PCI6503B }, - { PCI_VDEVICE(NI, 0x17d0), BOARD_NI_PCI6503X }, - { PCI_VDEVICE(NI, 0x1800), BOARD_NI_PXI_6503 }, - { 0 } + { PCI_VDEVICE(NI, 0x0160), .driver_data = BOARD_NI_PCIDIO96 }, + { PCI_VDEVICE(NI, 0x1630), .driver_data = BOARD_NI_PCIDIO96B }, + { PCI_VDEVICE(NI, 0x13c0), .driver_data = BOARD_NI_PXI6508 }, + { PCI_VDEVICE(NI, 0x0400), .driver_data = BOARD_NI_PCI6503 }, + { PCI_VDEVICE(NI, 0x1250), .driver_data = BOARD_NI_PCI6503B }, + { PCI_VDEVICE(NI, 0x17d0), .driver_data = BOARD_NI_PCI6503X }, + { PCI_VDEVICE(NI, 0x1800), .driver_data = BOARD_NI_PXI_6503 }, + { } }; MODULE_DEVICE_TABLE(pci, pci_8255_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_1032.c b/drivers/comedi/drivers/addi_apci_1032.c index 8eec6d9402de..9ca6980705e5 100644 --- a/drivers/comedi/drivers/addi_apci_1032.c +++ b/drivers/comedi/drivers/addi_apci_1032.c @@ -378,8 +378,8 @@ static int apci1032_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci1032_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1003) }, + { } }; MODULE_DEVICE_TABLE(pci, apci1032_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_1500.c b/drivers/comedi/drivers/addi_apci_1500.c index c94c78588889..a203e27888e2 100644 --- a/drivers/comedi/drivers/addi_apci_1500.c +++ b/drivers/comedi/drivers/addi_apci_1500.c @@ -869,8 +869,8 @@ static int apci1500_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci1500_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMCC, 0x80fc) }, - { 0 } + { PCI_VDEVICE(AMCC, 0x80fc) }, + { } }; MODULE_DEVICE_TABLE(pci, apci1500_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_1516.c b/drivers/comedi/drivers/addi_apci_1516.c index 3c48b72dad9d..397f06f8858a 100644 --- a/drivers/comedi/drivers/addi_apci_1516.c +++ b/drivers/comedi/drivers/addi_apci_1516.c @@ -196,10 +196,10 @@ static int apci1516_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci1516_pci_table[] = { - { PCI_VDEVICE(ADDIDATA, 0x1000), BOARD_APCI1016 }, - { PCI_VDEVICE(ADDIDATA, 0x1001), BOARD_APCI1516 }, - { PCI_VDEVICE(ADDIDATA, 0x1002), BOARD_APCI2016 }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1000), .driver_data = BOARD_APCI1016 }, + { PCI_VDEVICE(ADDIDATA, 0x1001), .driver_data = BOARD_APCI1516 }, + { PCI_VDEVICE(ADDIDATA, 0x1002), .driver_data = BOARD_APCI2016 }, + { } }; MODULE_DEVICE_TABLE(pci, apci1516_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_1564.c b/drivers/comedi/drivers/addi_apci_1564.c index 0cd40948bee7..a54df64afc3d 100644 --- a/drivers/comedi/drivers/addi_apci_1564.c +++ b/drivers/comedi/drivers/addi_apci_1564.c @@ -802,8 +802,8 @@ static int apci1564_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci1564_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1006) }, + { } }; MODULE_DEVICE_TABLE(pci, apci1564_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_16xx.c b/drivers/comedi/drivers/addi_apci_16xx.c index ec2c321d2431..87d62c65a4d2 100644 --- a/drivers/comedi/drivers/addi_apci_16xx.c +++ b/drivers/comedi/drivers/addi_apci_16xx.c @@ -158,9 +158,9 @@ static int apci16xx_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci16xx_pci_table[] = { - { PCI_VDEVICE(ADDIDATA, 0x1009), BOARD_APCI1648 }, - { PCI_VDEVICE(ADDIDATA, 0x100a), BOARD_APCI1696 }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1009), .driver_data = BOARD_APCI1648 }, + { PCI_VDEVICE(ADDIDATA, 0x100a), .driver_data = BOARD_APCI1696 }, + { } }; MODULE_DEVICE_TABLE(pci, apci16xx_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_2032.c b/drivers/comedi/drivers/addi_apci_2032.c index d0f52d5ece8f..59bc0de4d864 100644 --- a/drivers/comedi/drivers/addi_apci_2032.c +++ b/drivers/comedi/drivers/addi_apci_2032.c @@ -312,8 +312,8 @@ static int apci2032_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci2032_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1004) }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1004) }, + { } }; MODULE_DEVICE_TABLE(pci, apci2032_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_2200.c b/drivers/comedi/drivers/addi_apci_2200.c index 00378c9dddc8..686f6ec2cbb6 100644 --- a/drivers/comedi/drivers/addi_apci_2200.c +++ b/drivers/comedi/drivers/addi_apci_2200.c @@ -125,8 +125,8 @@ static int apci2200_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci2200_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1005) }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x1005) }, + { } }; MODULE_DEVICE_TABLE(pci, apci2200_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_3120.c b/drivers/comedi/drivers/addi_apci_3120.c index 28a242e69721..049399cf5681 100644 --- a/drivers/comedi/drivers/addi_apci_3120.c +++ b/drivers/comedi/drivers/addi_apci_3120.c @@ -1098,9 +1098,9 @@ static int apci3120_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci3120_pci_table[] = { - { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, - { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, - { 0 } + { PCI_VDEVICE(AMCC, 0x818d), .driver_data = BOARD_APCI3120 }, + { PCI_VDEVICE(AMCC, 0x828d), .driver_data = BOARD_APCI3001 }, + { } }; MODULE_DEVICE_TABLE(pci, apci3120_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_3501.c b/drivers/comedi/drivers/addi_apci_3501.c index ecb5552f1785..3bcf5c067820 100644 --- a/drivers/comedi/drivers/addi_apci_3501.c +++ b/drivers/comedi/drivers/addi_apci_3501.c @@ -399,8 +399,8 @@ static int apci3501_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci3501_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x3001) }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x3001) }, + { } }; MODULE_DEVICE_TABLE(pci, apci3501_pci_table); diff --git a/drivers/comedi/drivers/addi_apci_3xxx.c b/drivers/comedi/drivers/addi_apci_3xxx.c index bc72273e6a29..695cce103177 100644 --- a/drivers/comedi/drivers/addi_apci_3xxx.c +++ b/drivers/comedi/drivers/addi_apci_3xxx.c @@ -918,32 +918,32 @@ static int apci3xxx_pci_probe(struct pci_dev *dev, } static const struct pci_device_id apci3xxx_pci_table[] = { - { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 }, - { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 }, - { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 }, - { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 }, - { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 }, - { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 }, - { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 }, - { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 }, - { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 }, - { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 }, - { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 }, - { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 }, - { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 }, - { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 }, - { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 }, - { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 }, - { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 }, - { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 }, - { 0 } + { PCI_VDEVICE(ADDIDATA, 0x3010), .driver_data = BOARD_APCI3000_16 }, + { PCI_VDEVICE(ADDIDATA, 0x300f), .driver_data = BOARD_APCI3000_8 }, + { PCI_VDEVICE(ADDIDATA, 0x300e), .driver_data = BOARD_APCI3000_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3013), .driver_data = BOARD_APCI3006_16 }, + { PCI_VDEVICE(ADDIDATA, 0x3014), .driver_data = BOARD_APCI3006_8 }, + { PCI_VDEVICE(ADDIDATA, 0x3015), .driver_data = BOARD_APCI3006_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3016), .driver_data = BOARD_APCI3010_16 }, + { PCI_VDEVICE(ADDIDATA, 0x3017), .driver_data = BOARD_APCI3010_8 }, + { PCI_VDEVICE(ADDIDATA, 0x3018), .driver_data = BOARD_APCI3010_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3019), .driver_data = BOARD_APCI3016_16 }, + { PCI_VDEVICE(ADDIDATA, 0x301a), .driver_data = BOARD_APCI3016_8 }, + { PCI_VDEVICE(ADDIDATA, 0x301b), .driver_data = BOARD_APCI3016_4 }, + { PCI_VDEVICE(ADDIDATA, 0x301c), .driver_data = BOARD_APCI3100_16_4 }, + { PCI_VDEVICE(ADDIDATA, 0x301d), .driver_data = BOARD_APCI3100_8_4 }, + { PCI_VDEVICE(ADDIDATA, 0x301e), .driver_data = BOARD_APCI3106_16_4 }, + { PCI_VDEVICE(ADDIDATA, 0x301f), .driver_data = BOARD_APCI3106_8_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3020), .driver_data = BOARD_APCI3110_16_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3021), .driver_data = BOARD_APCI3110_8_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3022), .driver_data = BOARD_APCI3116_16_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3023), .driver_data = BOARD_APCI3116_8_4 }, + { PCI_VDEVICE(ADDIDATA, 0x300B), .driver_data = BOARD_APCI3003 }, + { PCI_VDEVICE(ADDIDATA, 0x3002), .driver_data = BOARD_APCI3002_16 }, + { PCI_VDEVICE(ADDIDATA, 0x3003), .driver_data = BOARD_APCI3002_8 }, + { PCI_VDEVICE(ADDIDATA, 0x3004), .driver_data = BOARD_APCI3002_4 }, + { PCI_VDEVICE(ADDIDATA, 0x3024), .driver_data = BOARD_APCI3500 }, + { } }; MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table); diff --git a/drivers/comedi/drivers/adl_pci6208.c b/drivers/comedi/drivers/adl_pci6208.c index b27354a51f5c..57d1af105d41 100644 --- a/drivers/comedi/drivers/adl_pci6208.c +++ b/drivers/comedi/drivers/adl_pci6208.c @@ -180,10 +180,9 @@ static int adl_pci6208_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adl_pci6208_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - 0x9999, 0x6208) }, - { 0 } + { PCI_VDEVICE(ADLINK, 0x6208) }, + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, 0x9999, 0x6208) }, + { } }; MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); diff --git a/drivers/comedi/drivers/adl_pci7250.c b/drivers/comedi/drivers/adl_pci7250.c index 78c85a402435..b2772cf17ad2 100644 --- a/drivers/comedi/drivers/adl_pci7250.c +++ b/drivers/comedi/drivers/adl_pci7250.c @@ -194,16 +194,16 @@ static int adl_pci7250_pci_probe(struct pci_dev *dev, static const struct pci_device_id adl_pci7250_pci_table[] = { #ifdef CONFIG_HAS_IOPORT - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - 0x9999, 0x7250) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, - 0x9999, 0x7250) }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, - PCI_VENDOR_ID_ADLINK, 0x7250) }, + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + 0x9999, 0x7250) }, + { PCI_VDEVICE_SUB(ADLINK, 0x7250, + 0x9999, 0x7250) }, + { PCI_VDEVICE_SUB(ADLINK, 0x7250, + PCI_VENDOR_ID_ADLINK, 0x7250) }, #endif - { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADLINK, 0x7250, - PCI_VENDOR_ID_ADLINK, 0x7000) }, /* newer LPCIe-7250 */ - { 0 } + { PCI_VDEVICE_SUB(ADLINK, 0x7250, + PCI_VENDOR_ID_ADLINK, 0x7000) }, /* newer LPCIe-7250 */ + { } }; MODULE_DEVICE_TABLE(pci, adl_pci7250_pci_table); diff --git a/drivers/comedi/drivers/adl_pci7x3x.c b/drivers/comedi/drivers/adl_pci7x3x.c index e9f22de9b6f1..3e558a9b2ad7 100644 --- a/drivers/comedi/drivers/adl_pci7x3x.c +++ b/drivers/comedi/drivers/adl_pci7x3x.c @@ -518,13 +518,13 @@ static int adl_pci7x3x_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adl_pci7x3x_pci_table[] = { - { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 }, - { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 }, - { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 }, - { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 }, - { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 }, - { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 }, - { 0 } + { PCI_VDEVICE(ADLINK, 0x7230), .driver_data = BOARD_PCI7230 }, + { PCI_VDEVICE(ADLINK, 0x7233), .driver_data = BOARD_PCI7233 }, + { PCI_VDEVICE(ADLINK, 0x7234), .driver_data = BOARD_PCI7234 }, + { PCI_VDEVICE(ADLINK, 0x7432), .driver_data = BOARD_PCI7432 }, + { PCI_VDEVICE(ADLINK, 0x7433), .driver_data = BOARD_PCI7433 }, + { PCI_VDEVICE(ADLINK, 0x7434), .driver_data = BOARD_PCI7434 }, + { } }; MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table); diff --git a/drivers/comedi/drivers/adl_pci8164.c b/drivers/comedi/drivers/adl_pci8164.c index 0c513a67a264..3b56a307a900 100644 --- a/drivers/comedi/drivers/adl_pci8164.c +++ b/drivers/comedi/drivers/adl_pci8164.c @@ -135,8 +135,8 @@ static int adl_pci8164_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adl_pci8164_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x8164) }, - { 0 } + { PCI_VDEVICE(ADLINK, 0x8164) }, + { } }; MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table); diff --git a/drivers/comedi/drivers/adl_pci9111.c b/drivers/comedi/drivers/adl_pci9111.c index 086d93f40cb9..7e8e669c21d6 100644 --- a/drivers/comedi/drivers/adl_pci9111.c +++ b/drivers/comedi/drivers/adl_pci9111.c @@ -727,9 +727,9 @@ static int pci9111_pci_probe(struct pci_dev *dev, } static const struct pci_device_id pci9111_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) }, - /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */ - { 0 } + { PCI_VDEVICE(ADLINK, 0x9111) }, + /* { PCI_VDEVICE(ADLINK, PCI9111_HG_DEVICE_ID) }, */ + { } }; MODULE_DEVICE_TABLE(pci, pci9111_pci_table); diff --git a/drivers/comedi/drivers/adl_pci9118.c b/drivers/comedi/drivers/adl_pci9118.c index 67c663892e48..75c487bb2f5b 100644 --- a/drivers/comedi/drivers/adl_pci9118.c +++ b/drivers/comedi/drivers/adl_pci9118.c @@ -1715,10 +1715,10 @@ static int adl_pci9118_pci_probe(struct pci_dev *dev, /* FIXME: All the supported board types have the same device ID! */ static const struct pci_device_id adl_pci9118_pci_table[] = { - { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG }, -/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */ -/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */ - { 0 } + { PCI_VDEVICE(AMCC, 0x80d9), .driver_data = BOARD_PCI9118DG }, +/* { PCI_VDEVICE(AMCC, 0x80d9), .driver_data = BOARD_PCI9118HG }, */ +/* { PCI_VDEVICE(AMCC, 0x80d9), .driver_data = BOARD_PCI9118HR }, */ + { } }; MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table); diff --git a/drivers/comedi/drivers/adv_pci1710.c b/drivers/comedi/drivers/adv_pci1710.c index c49b0f1f5228..fc749241da41 100644 --- a/drivers/comedi/drivers/adv_pci1710.c +++ b/drivers/comedi/drivers/adv_pci1710.c @@ -892,60 +892,66 @@ static int adv_pci1710_pci_probe(struct pci_dev *dev, static const struct pci_device_id adv_pci1710_pci_table[] = { { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0x0000), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0x0000), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xb100), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xb100), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xb200), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xb200), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xc100), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xc100), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xc200), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xc200), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, 0x1000, 0xd100), .driver_data = BOARD_PCI1710, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0x0002), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0x0002), .driver_data = BOARD_PCI1710HG, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xb102), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xb102), .driver_data = BOARD_PCI1710HG, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xb202), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xb202), .driver_data = BOARD_PCI1710HG, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xc102), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xc102), .driver_data = BOARD_PCI1710HG, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, - PCI_VENDOR_ID_ADVANTECH, 0xc202), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, + PCI_VENDOR_ID_ADVANTECH, 0xc202), .driver_data = BOARD_PCI1710HG, }, { - PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102), + PCI_VDEVICE_SUB(ADVANTECH, 0x1710, 0x1000, 0xd102), .driver_data = BOARD_PCI1710HG, + }, { + PCI_VDEVICE(ADVANTECH, 0x1711), + .driver_data = BOARD_PCI1711, + }, { + PCI_VDEVICE(ADVANTECH, 0x1713), + .driver_data = BOARD_PCI1713, + }, { + PCI_VDEVICE(ADVANTECH, 0x1731), + .driver_data = BOARD_PCI1731, }, - { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 }, - { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 }, - { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 }, - { 0 } + { } }; MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table); diff --git a/drivers/comedi/drivers/adv_pci1720.c b/drivers/comedi/drivers/adv_pci1720.c index 2619591ba301..cc21212c5d52 100644 --- a/drivers/comedi/drivers/adv_pci1720.c +++ b/drivers/comedi/drivers/adv_pci1720.c @@ -167,8 +167,8 @@ static int adv_pci1720_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adv_pci1720_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1720) }, - { 0 } + { PCI_VDEVICE(ADVANTECH, 0x1720) }, + { } }; MODULE_DEVICE_TABLE(pci, adv_pci1720_pci_table); diff --git a/drivers/comedi/drivers/adv_pci1723.c b/drivers/comedi/drivers/adv_pci1723.c index e2aedb152068..e7f55251500c 100644 --- a/drivers/comedi/drivers/adv_pci1723.c +++ b/drivers/comedi/drivers/adv_pci1723.c @@ -208,8 +208,8 @@ static int adv_pci1723_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adv_pci1723_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, - { 0 } + { PCI_VDEVICE(ADVANTECH, 0x1723) }, + { } }; MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); diff --git a/drivers/comedi/drivers/adv_pci1724.c b/drivers/comedi/drivers/adv_pci1724.c index bb43b7deeb56..e736f2bcdb04 100644 --- a/drivers/comedi/drivers/adv_pci1724.c +++ b/drivers/comedi/drivers/adv_pci1724.c @@ -189,8 +189,8 @@ static int adv_pci1724_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adv_pci1724_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) }, - { 0 } + { PCI_VDEVICE(ADVANTECH, 0x1724) }, + { } }; MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table); diff --git a/drivers/comedi/drivers/adv_pci1760.c b/drivers/comedi/drivers/adv_pci1760.c index 27f3890f471d..c9b0600be7f5 100644 --- a/drivers/comedi/drivers/adv_pci1760.c +++ b/drivers/comedi/drivers/adv_pci1760.c @@ -405,8 +405,8 @@ static int pci1760_pci_probe(struct pci_dev *dev, } static const struct pci_device_id pci1760_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1760) }, - { 0 } + { PCI_VDEVICE(ADVANTECH, 0x1760) }, + { } }; MODULE_DEVICE_TABLE(pci, pci1760_pci_table); diff --git a/drivers/comedi/drivers/adv_pci_dio.c b/drivers/comedi/drivers/adv_pci_dio.c index ca8054504760..49e2b2efe46d 100644 --- a/drivers/comedi/drivers/adv_pci_dio.c +++ b/drivers/comedi/drivers/adv_pci_dio.c @@ -768,21 +768,21 @@ static int adv_pci_dio_pci_probe(struct pci_dev *dev, } static const struct pci_device_id adv_pci_dio_pci_table[] = { - { PCI_VDEVICE(ADVANTECH, 0x1730), TYPE_PCI1730 }, - { PCI_VDEVICE(ADVANTECH, 0x1733), TYPE_PCI1733 }, - { PCI_VDEVICE(ADVANTECH, 0x1734), TYPE_PCI1734 }, - { PCI_VDEVICE(ADVANTECH, 0x1735), TYPE_PCI1735 }, - { PCI_VDEVICE(ADVANTECH, 0x1736), TYPE_PCI1736 }, - { PCI_VDEVICE(ADVANTECH, 0x1739), TYPE_PCI1739 }, - { PCI_VDEVICE(ADVANTECH, 0x1750), TYPE_PCI1750 }, - { PCI_VDEVICE(ADVANTECH, 0x1751), TYPE_PCI1751 }, - { PCI_VDEVICE(ADVANTECH, 0x1752), TYPE_PCI1752 }, - { PCI_VDEVICE(ADVANTECH, 0x1753), TYPE_PCI1753 }, - { PCI_VDEVICE(ADVANTECH, 0x1754), TYPE_PCI1754 }, - { PCI_VDEVICE(ADVANTECH, 0x1756), TYPE_PCI1756 }, - { PCI_VDEVICE(ADVANTECH, 0x1761), TYPE_PCI1761 }, - { PCI_VDEVICE(ADVANTECH, 0x1762), TYPE_PCI1762 }, - { 0 } + { PCI_VDEVICE(ADVANTECH, 0x1730), .driver_data = TYPE_PCI1730 }, + { PCI_VDEVICE(ADVANTECH, 0x1733), .driver_data = TYPE_PCI1733 }, + { PCI_VDEVICE(ADVANTECH, 0x1734), .driver_data = TYPE_PCI1734 }, + { PCI_VDEVICE(ADVANTECH, 0x1735), .driver_data = TYPE_PCI1735 }, + { PCI_VDEVICE(ADVANTECH, 0x1736), .driver_data = TYPE_PCI1736 }, + { PCI_VDEVICE(ADVANTECH, 0x1739), .driver_data = TYPE_PCI1739 }, + { PCI_VDEVICE(ADVANTECH, 0x1750), .driver_data = TYPE_PCI1750 }, + { PCI_VDEVICE(ADVANTECH, 0x1751), .driver_data = TYPE_PCI1751 }, + { PCI_VDEVICE(ADVANTECH, 0x1752), .driver_data = TYPE_PCI1752 }, + { PCI_VDEVICE(ADVANTECH, 0x1753), .driver_data = TYPE_PCI1753 }, + { PCI_VDEVICE(ADVANTECH, 0x1754), .driver_data = TYPE_PCI1754 }, + { PCI_VDEVICE(ADVANTECH, 0x1756), .driver_data = TYPE_PCI1756 }, + { PCI_VDEVICE(ADVANTECH, 0x1761), .driver_data = TYPE_PCI1761 }, + { PCI_VDEVICE(ADVANTECH, 0x1762), .driver_data = TYPE_PCI1762 }, + { } }; MODULE_DEVICE_TABLE(pci, adv_pci_dio_pci_table); diff --git a/drivers/comedi/drivers/amplc_dio200_pci.c b/drivers/comedi/drivers/amplc_dio200_pci.c index cb5b328a28e3..b057bbdd0063 100644 --- a/drivers/comedi/drivers/amplc_dio200_pci.c +++ b/drivers/comedi/drivers/amplc_dio200_pci.c @@ -394,13 +394,13 @@ static struct comedi_driver dio200_pci_comedi_driver = { static const struct pci_device_id dio200_pci_table[] = { #ifdef CONFIG_HAS_IOPORT - { PCI_VDEVICE(AMPLICON, 0x000b), pci215_model }, - { PCI_VDEVICE(AMPLICON, 0x000a), pci272_model }, + { PCI_VDEVICE(AMPLICON, 0x000b), .driver_data = pci215_model }, + { PCI_VDEVICE(AMPLICON, 0x000a), .driver_data = pci272_model }, #endif /* CONFIG_HAS_IOPORT */ - { PCI_VDEVICE(AMPLICON, 0x0011), pcie236_model }, - { PCI_VDEVICE(AMPLICON, 0x0012), pcie215_model }, - { PCI_VDEVICE(AMPLICON, 0x0014), pcie296_model }, - {0} + { PCI_VDEVICE(AMPLICON, 0x0011), .driver_data = pcie236_model }, + { PCI_VDEVICE(AMPLICON, 0x0012), .driver_data = pcie215_model }, + { PCI_VDEVICE(AMPLICON, 0x0014), .driver_data = pcie296_model }, + { } }; MODULE_DEVICE_TABLE(pci, dio200_pci_table); diff --git a/drivers/comedi/drivers/amplc_pci224.c b/drivers/comedi/drivers/amplc_pci224.c index 1373637c2ca2..55292a28f28c 100644 --- a/drivers/comedi/drivers/amplc_pci224.c +++ b/drivers/comedi/drivers/amplc_pci224.c @@ -1122,9 +1122,9 @@ static int amplc_pci224_pci_probe(struct pci_dev *dev, } static const struct pci_device_id amplc_pci224_pci_table[] = { - { PCI_VDEVICE(AMPLICON, 0x0007), pci224_model }, - { PCI_VDEVICE(AMPLICON, 0x0008), pci234_model }, - { 0 } + { PCI_VDEVICE(AMPLICON, 0x0007), .driver_data = pci224_model }, + { PCI_VDEVICE(AMPLICON, 0x0008), .driver_data = pci234_model }, + { } }; MODULE_DEVICE_TABLE(pci, amplc_pci224_pci_table); diff --git a/drivers/comedi/drivers/amplc_pci230.c b/drivers/comedi/drivers/amplc_pci230.c index c74209c2e83a..aa9c502b9429 100644 --- a/drivers/comedi/drivers/amplc_pci230.c +++ b/drivers/comedi/drivers/amplc_pci230.c @@ -2554,9 +2554,9 @@ static int amplc_pci230_pci_probe(struct pci_dev *dev, } static const struct pci_device_id amplc_pci230_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI230) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI260) }, - { 0 } + { PCI_VDEVICE(AMPLICON, PCI_DEVICE_ID_PCI230) }, + { PCI_VDEVICE(AMPLICON, PCI_DEVICE_ID_PCI260) }, + { } }; MODULE_DEVICE_TABLE(pci, amplc_pci230_pci_table); diff --git a/drivers/comedi/drivers/amplc_pci236.c b/drivers/comedi/drivers/amplc_pci236.c index 482eb261c333..b5d8c9e8d48a 100644 --- a/drivers/comedi/drivers/amplc_pci236.c +++ b/drivers/comedi/drivers/amplc_pci236.c @@ -116,8 +116,8 @@ static struct comedi_driver amplc_pci236_driver = { }; static const struct pci_device_id pci236_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x0009) }, - { 0 } + { PCI_VDEVICE(AMPLICON, 0x0009) }, + { } }; MODULE_DEVICE_TABLE(pci, pci236_pci_table); diff --git a/drivers/comedi/drivers/amplc_pci263.c b/drivers/comedi/drivers/amplc_pci263.c index 1609665c4b18..5a248bf5a7d2 100644 --- a/drivers/comedi/drivers/amplc_pci263.c +++ b/drivers/comedi/drivers/amplc_pci263.c @@ -85,8 +85,8 @@ static struct comedi_driver amplc_pci263_driver = { }; static const struct pci_device_id pci263_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x000c) }, - {0} + { PCI_VDEVICE(AMPLICON, 0x000c) }, + { } }; MODULE_DEVICE_TABLE(pci, pci263_pci_table); diff --git a/drivers/comedi/drivers/cb_pcidas.c b/drivers/comedi/drivers/cb_pcidas.c index 8bb9b0623869..bb6c32f90221 100644 --- a/drivers/comedi/drivers/cb_pcidas.c +++ b/drivers/comedi/drivers/cb_pcidas.c @@ -1474,15 +1474,15 @@ static int cb_pcidas_pci_probe(struct pci_dev *dev, } static const struct pci_device_id cb_pcidas_pci_table[] = { - { PCI_VDEVICE(CB, 0x0001), BOARD_PCIDAS1602_16 }, - { PCI_VDEVICE(CB, 0x000f), BOARD_PCIDAS1200 }, - { PCI_VDEVICE(CB, 0x0010), BOARD_PCIDAS1602_12 }, - { PCI_VDEVICE(CB, 0x0019), BOARD_PCIDAS1200_JR }, - { PCI_VDEVICE(CB, 0x001c), BOARD_PCIDAS1602_16_JR }, - { PCI_VDEVICE(CB, 0x004c), BOARD_PCIDAS1000 }, - { PCI_VDEVICE(CB, 0x001a), BOARD_PCIDAS1001 }, - { PCI_VDEVICE(CB, 0x001b), BOARD_PCIDAS1002 }, - { 0 } + { PCI_VDEVICE(CB, 0x0001), .driver_data = BOARD_PCIDAS1602_16 }, + { PCI_VDEVICE(CB, 0x000f), .driver_data = BOARD_PCIDAS1200 }, + { PCI_VDEVICE(CB, 0x0010), .driver_data = BOARD_PCIDAS1602_12 }, + { PCI_VDEVICE(CB, 0x0019), .driver_data = BOARD_PCIDAS1200_JR }, + { PCI_VDEVICE(CB, 0x001c), .driver_data = BOARD_PCIDAS1602_16_JR }, + { PCI_VDEVICE(CB, 0x004c), .driver_data = BOARD_PCIDAS1000 }, + { PCI_VDEVICE(CB, 0x001a), .driver_data = BOARD_PCIDAS1001 }, + { PCI_VDEVICE(CB, 0x001b), .driver_data = BOARD_PCIDAS1002 }, + { } }; MODULE_DEVICE_TABLE(pci, cb_pcidas_pci_table); diff --git a/drivers/comedi/drivers/cb_pcidas64.c b/drivers/comedi/drivers/cb_pcidas64.c index d398c6df9482..549caadc2e8d 100644 --- a/drivers/comedi/drivers/cb_pcidas64.c +++ b/drivers/comedi/drivers/cb_pcidas64.c @@ -4074,28 +4074,28 @@ static int cb_pcidas64_pci_probe(struct pci_dev *dev, } static const struct pci_device_id cb_pcidas64_pci_table[] = { - { PCI_VDEVICE(CB, 0x001d), BOARD_PCIDAS6402_16 }, - { PCI_VDEVICE(CB, 0x001e), BOARD_PCIDAS6402_12 }, - { PCI_VDEVICE(CB, 0x0035), BOARD_PCIDAS64_M1_16 }, - { PCI_VDEVICE(CB, 0x0036), BOARD_PCIDAS64_M2_16 }, - { PCI_VDEVICE(CB, 0x0037), BOARD_PCIDAS64_M3_16 }, - { PCI_VDEVICE(CB, 0x0052), BOARD_PCIDAS4020_12 }, - { PCI_VDEVICE(CB, 0x005d), BOARD_PCIDAS6023 }, - { PCI_VDEVICE(CB, 0x005e), BOARD_PCIDAS6025 }, - { PCI_VDEVICE(CB, 0x005f), BOARD_PCIDAS6030 }, - { PCI_VDEVICE(CB, 0x0060), BOARD_PCIDAS6031 }, - { PCI_VDEVICE(CB, 0x0061), BOARD_PCIDAS6032 }, - { PCI_VDEVICE(CB, 0x0062), BOARD_PCIDAS6033 }, - { PCI_VDEVICE(CB, 0x0063), BOARD_PCIDAS6034 }, - { PCI_VDEVICE(CB, 0x0064), BOARD_PCIDAS6035 }, - { PCI_VDEVICE(CB, 0x0065), BOARD_PCIDAS6040 }, - { PCI_VDEVICE(CB, 0x0066), BOARD_PCIDAS6052 }, - { PCI_VDEVICE(CB, 0x0067), BOARD_PCIDAS6070 }, - { PCI_VDEVICE(CB, 0x0068), BOARD_PCIDAS6071 }, - { PCI_VDEVICE(CB, 0x006f), BOARD_PCIDAS6036 }, - { PCI_VDEVICE(CB, 0x0078), BOARD_PCIDAS6013 }, - { PCI_VDEVICE(CB, 0x0079), BOARD_PCIDAS6014 }, - { 0 } + { PCI_VDEVICE(CB, 0x001d), .driver_data = BOARD_PCIDAS6402_16 }, + { PCI_VDEVICE(CB, 0x001e), .driver_data = BOARD_PCIDAS6402_12 }, + { PCI_VDEVICE(CB, 0x0035), .driver_data = BOARD_PCIDAS64_M1_16 }, + { PCI_VDEVICE(CB, 0x0036), .driver_data = BOARD_PCIDAS64_M2_16 }, + { PCI_VDEVICE(CB, 0x0037), .driver_data = BOARD_PCIDAS64_M3_16 }, + { PCI_VDEVICE(CB, 0x0052), .driver_data = BOARD_PCIDAS4020_12 }, + { PCI_VDEVICE(CB, 0x005d), .driver_data = BOARD_PCIDAS6023 }, + { PCI_VDEVICE(CB, 0x005e), .driver_data = BOARD_PCIDAS6025 }, + { PCI_VDEVICE(CB, 0x005f), .driver_data = BOARD_PCIDAS6030 }, + { PCI_VDEVICE(CB, 0x0060), .driver_data = BOARD_PCIDAS6031 }, + { PCI_VDEVICE(CB, 0x0061), .driver_data = BOARD_PCIDAS6032 }, + { PCI_VDEVICE(CB, 0x0062), .driver_data = BOARD_PCIDAS6033 }, + { PCI_VDEVICE(CB, 0x0063), .driver_data = BOARD_PCIDAS6034 }, + { PCI_VDEVICE(CB, 0x0064), .driver_data = BOARD_PCIDAS6035 }, + { PCI_VDEVICE(CB, 0x0065), .driver_data = BOARD_PCIDAS6040 }, + { PCI_VDEVICE(CB, 0x0066), .driver_data = BOARD_PCIDAS6052 }, + { PCI_VDEVICE(CB, 0x0067), .driver_data = BOARD_PCIDAS6070 }, + { PCI_VDEVICE(CB, 0x0068), .driver_data = BOARD_PCIDAS6071 }, + { PCI_VDEVICE(CB, 0x006f), .driver_data = BOARD_PCIDAS6036 }, + { PCI_VDEVICE(CB, 0x0078), .driver_data = BOARD_PCIDAS6013 }, + { PCI_VDEVICE(CB, 0x0079), .driver_data = BOARD_PCIDAS6014 }, + { } }; MODULE_DEVICE_TABLE(pci, cb_pcidas64_pci_table); diff --git a/drivers/comedi/drivers/cb_pcidda.c b/drivers/comedi/drivers/cb_pcidda.c index c353d0f87da9..31f368e7c9df 100644 --- a/drivers/comedi/drivers/cb_pcidda.c +++ b/drivers/comedi/drivers/cb_pcidda.c @@ -396,13 +396,13 @@ static int cb_pcidda_pci_probe(struct pci_dev *dev, } static const struct pci_device_id cb_pcidda_pci_table[] = { - { PCI_VDEVICE(CB, 0x0020), BOARD_DDA02_12 }, - { PCI_VDEVICE(CB, 0x0021), BOARD_DDA04_12 }, - { PCI_VDEVICE(CB, 0x0022), BOARD_DDA08_12 }, - { PCI_VDEVICE(CB, 0x0023), BOARD_DDA02_16 }, - { PCI_VDEVICE(CB, 0x0024), BOARD_DDA04_16 }, - { PCI_VDEVICE(CB, 0x0025), BOARD_DDA08_16 }, - { 0 } + { PCI_VDEVICE(CB, 0x0020), .driver_data = BOARD_DDA02_12 }, + { PCI_VDEVICE(CB, 0x0021), .driver_data = BOARD_DDA04_12 }, + { PCI_VDEVICE(CB, 0x0022), .driver_data = BOARD_DDA08_12 }, + { PCI_VDEVICE(CB, 0x0023), .driver_data = BOARD_DDA02_16 }, + { PCI_VDEVICE(CB, 0x0024), .driver_data = BOARD_DDA04_16 }, + { PCI_VDEVICE(CB, 0x0025), .driver_data = BOARD_DDA08_16 }, + { } }; MODULE_DEVICE_TABLE(pci, cb_pcidda_pci_table); diff --git a/drivers/comedi/drivers/cb_pcimdas.c b/drivers/comedi/drivers/cb_pcimdas.c index 641c30df392e..ae25347d8375 100644 --- a/drivers/comedi/drivers/cb_pcimdas.c +++ b/drivers/comedi/drivers/cb_pcimdas.c @@ -455,9 +455,9 @@ static int cb_pcimdas_pci_probe(struct pci_dev *dev, } static const struct pci_device_id cb_pcimdas_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) }, /* PCIM-DAS1602/16 */ - { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) }, /* PCIe-DAS1602/16 */ - { 0 } + { PCI_VDEVICE(CB, 0x0056) }, /* PCIM-DAS1602/16 */ + { PCI_VDEVICE(CB, 0x0115) }, /* PCIe-DAS1602/16 */ + { } }; MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table); diff --git a/drivers/comedi/drivers/cb_pcimdda.c b/drivers/comedi/drivers/cb_pcimdda.c index 541b5742bb1b..2f270b044b38 100644 --- a/drivers/comedi/drivers/cb_pcimdda.c +++ b/drivers/comedi/drivers/cb_pcimdda.c @@ -172,8 +172,8 @@ static int cb_pcimdda_pci_probe(struct pci_dev *dev, } static const struct pci_device_id cb_pcimdda_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_ID_PCIM_DDA06_16) }, - { 0 } + { PCI_VDEVICE(CB, PCI_ID_PCIM_DDA06_16) }, + { } }; MODULE_DEVICE_TABLE(pci, cb_pcimdda_pci_table); diff --git a/drivers/comedi/drivers/contec_pci_dio.c b/drivers/comedi/drivers/contec_pci_dio.c index 41d42ff14144..56b11a280b20 100644 --- a/drivers/comedi/drivers/contec_pci_dio.c +++ b/drivers/comedi/drivers/contec_pci_dio.c @@ -98,8 +98,8 @@ static int contec_pci_dio_pci_probe(struct pci_dev *dev, } static const struct pci_device_id contec_pci_dio_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CONTEC, 0x8172) }, - { 0 } + { PCI_VDEVICE(CONTEC, 0x8172) }, + { } }; MODULE_DEVICE_TABLE(pci, contec_pci_dio_pci_table); diff --git a/drivers/comedi/drivers/daqboard2000.c b/drivers/comedi/drivers/daqboard2000.c index 897bf46b95ee..f05b8d3afc54 100644 --- a/drivers/comedi/drivers/daqboard2000.c +++ b/drivers/comedi/drivers/daqboard2000.c @@ -764,11 +764,11 @@ static int db2k_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id db2k_pci_table[] = { - { PCI_DEVICE_SUB(PCI_VENDOR_ID_IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, - 0x0002), .driver_data = BOARD_DAQBOARD2000, }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, - 0x0004), .driver_data = BOARD_DAQBOARD2001, }, - { 0 } + { PCI_VDEVICE_SUB(IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, 0x0002), + .driver_data = BOARD_DAQBOARD2000 }, + { PCI_VDEVICE_SUB(IOTECH, 0x0409, PCI_VENDOR_ID_IOTECH, 0x0004), + .driver_data = BOARD_DAQBOARD2001 }, + { } }; MODULE_DEVICE_TABLE(pci, db2k_pci_table); diff --git a/drivers/comedi/drivers/das08_pci.c b/drivers/comedi/drivers/das08_pci.c index 982f3ab0ccbd..a439e0ddbb6d 100644 --- a/drivers/comedi/drivers/das08_pci.c +++ b/drivers/comedi/drivers/das08_pci.c @@ -77,8 +77,8 @@ static int das08_pci_probe(struct pci_dev *dev, } static const struct pci_device_id das08_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0029) }, - { 0 } + { PCI_VDEVICE(CB, 0x0029) }, + { } }; MODULE_DEVICE_TABLE(pci, das08_pci_table); diff --git a/drivers/comedi/drivers/dt3000.c b/drivers/comedi/drivers/dt3000.c index fc6e9c30e522..198aaf812747 100644 --- a/drivers/comedi/drivers/dt3000.c +++ b/drivers/comedi/drivers/dt3000.c @@ -715,14 +715,14 @@ static int dt3000_pci_probe(struct pci_dev *dev, } static const struct pci_device_id dt3000_pci_table[] = { - { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 }, - { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 }, - { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 }, - { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 }, - { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 }, - { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL }, - { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL }, - { 0 } + { PCI_VDEVICE(DT, 0x0022), .driver_data = BOARD_DT3001 }, + { PCI_VDEVICE(DT, 0x0023), .driver_data = BOARD_DT3002 }, + { PCI_VDEVICE(DT, 0x0024), .driver_data = BOARD_DT3003 }, + { PCI_VDEVICE(DT, 0x0025), .driver_data = BOARD_DT3004 }, + { PCI_VDEVICE(DT, 0x0026), .driver_data = BOARD_DT3005 }, + { PCI_VDEVICE(DT, 0x0027), .driver_data = BOARD_DT3001_PGL }, + { PCI_VDEVICE(DT, 0x0028), .driver_data = BOARD_DT3003_PGL }, + { } }; MODULE_DEVICE_TABLE(pci, dt3000_pci_table); diff --git a/drivers/comedi/drivers/dyna_pci10xx.c b/drivers/comedi/drivers/dyna_pci10xx.c index 407a038fb3e0..3b11bbf50648 100644 --- a/drivers/comedi/drivers/dyna_pci10xx.c +++ b/drivers/comedi/drivers/dyna_pci10xx.c @@ -246,8 +246,8 @@ static int dyna_pci10xx_pci_probe(struct pci_dev *dev, } static const struct pci_device_id dyna_pci10xx_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) }, - { 0 } + { PCI_VDEVICE(PLX, 0x1050) }, + { } }; MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table); diff --git a/drivers/comedi/drivers/gsc_hpdi.c b/drivers/comedi/drivers/gsc_hpdi.c index c09d135df38d..b71abefabd96 100644 --- a/drivers/comedi/drivers/gsc_hpdi.c +++ b/drivers/comedi/drivers/gsc_hpdi.c @@ -703,9 +703,9 @@ static int gsc_hpdi_pci_probe(struct pci_dev *dev, } static const struct pci_device_id gsc_hpdi_pci_table[] = { - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, - PCI_VENDOR_ID_PLX, 0x2400) }, - { 0 } + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9080, + PCI_VENDOR_ID_PLX, 0x2400) }, + { } }; MODULE_DEVICE_TABLE(pci, gsc_hpdi_pci_table); diff --git a/drivers/comedi/drivers/icp_multi.c b/drivers/comedi/drivers/icp_multi.c index ac4b11dbd741..2abee92bfae4 100644 --- a/drivers/comedi/drivers/icp_multi.c +++ b/drivers/comedi/drivers/icp_multi.c @@ -317,8 +317,8 @@ static int icp_multi_pci_probe(struct pci_dev *dev, } static const struct pci_device_id icp_multi_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ICP, 0x8000) }, - { 0 } + { PCI_VDEVICE(ICP, 0x8000) }, + { } }; MODULE_DEVICE_TABLE(pci, icp_multi_pci_table); diff --git a/drivers/comedi/drivers/jr3_pci.c b/drivers/comedi/drivers/jr3_pci.c index 51287cbc3e48..603e57dd2f13 100644 --- a/drivers/comedi/drivers/jr3_pci.c +++ b/drivers/comedi/drivers/jr3_pci.c @@ -779,12 +779,12 @@ static int jr3_pci_pci_probe(struct pci_dev *dev, } static const struct pci_device_id jr3_pci_pci_table[] = { - { PCI_VDEVICE(JR3, 0x1111), BOARD_JR3_1 }, - { PCI_VDEVICE(JR3, 0x3111), BOARD_JR3_1 }, - { PCI_VDEVICE(JR3, 0x3112), BOARD_JR3_2 }, - { PCI_VDEVICE(JR3, 0x3113), BOARD_JR3_3 }, - { PCI_VDEVICE(JR3, 0x3114), BOARD_JR3_4 }, - { 0 } + { PCI_VDEVICE(JR3, 0x1111), .driver_data = BOARD_JR3_1 }, + { PCI_VDEVICE(JR3, 0x3111), .driver_data = BOARD_JR3_1 }, + { PCI_VDEVICE(JR3, 0x3112), .driver_data = BOARD_JR3_2 }, + { PCI_VDEVICE(JR3, 0x3113), .driver_data = BOARD_JR3_3 }, + { PCI_VDEVICE(JR3, 0x3114), .driver_data = BOARD_JR3_4 }, + { } }; MODULE_DEVICE_TABLE(pci, jr3_pci_pci_table); diff --git a/drivers/comedi/drivers/ke_counter.c b/drivers/comedi/drivers/ke_counter.c index b825cf60e1e0..40177ffc904d 100644 --- a/drivers/comedi/drivers/ke_counter.c +++ b/drivers/comedi/drivers/ke_counter.c @@ -213,8 +213,8 @@ static int ke_counter_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ke_counter_pci_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_KOLTER, 0x0014) }, - { 0 } + { PCI_VDEVICE(KOLTER, 0x0014) }, + { } }; MODULE_DEVICE_TABLE(pci, ke_counter_pci_table); diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c index effe9fdbbafe..e62f6e5eca3d 100644 --- a/drivers/comedi/drivers/me4000.c +++ b/drivers/comedi/drivers/me4000.c @@ -1254,20 +1254,20 @@ static int me4000_pci_probe(struct pci_dev *dev, } static const struct pci_device_id me4000_pci_table[] = { - { PCI_VDEVICE(MEILHAUS, 0x4650), BOARD_ME4650 }, - { PCI_VDEVICE(MEILHAUS, 0x4660), BOARD_ME4660 }, - { PCI_VDEVICE(MEILHAUS, 0x4661), BOARD_ME4660I }, - { PCI_VDEVICE(MEILHAUS, 0x4662), BOARD_ME4660S }, - { PCI_VDEVICE(MEILHAUS, 0x4663), BOARD_ME4660IS }, - { PCI_VDEVICE(MEILHAUS, 0x4670), BOARD_ME4670 }, - { PCI_VDEVICE(MEILHAUS, 0x4671), BOARD_ME4670I }, - { PCI_VDEVICE(MEILHAUS, 0x4672), BOARD_ME4670S }, - { PCI_VDEVICE(MEILHAUS, 0x4673), BOARD_ME4670IS }, - { PCI_VDEVICE(MEILHAUS, 0x4680), BOARD_ME4680 }, - { PCI_VDEVICE(MEILHAUS, 0x4681), BOARD_ME4680I }, - { PCI_VDEVICE(MEILHAUS, 0x4682), BOARD_ME4680S }, - { PCI_VDEVICE(MEILHAUS, 0x4683), BOARD_ME4680IS }, - { 0 } + { PCI_VDEVICE(MEILHAUS, 0x4650), .driver_data = BOARD_ME4650 }, + { PCI_VDEVICE(MEILHAUS, 0x4660), .driver_data = BOARD_ME4660 }, + { PCI_VDEVICE(MEILHAUS, 0x4661), .driver_data = BOARD_ME4660I }, + { PCI_VDEVICE(MEILHAUS, 0x4662), .driver_data = BOARD_ME4660S }, + { PCI_VDEVICE(MEILHAUS, 0x4663), .driver_data = BOARD_ME4660IS }, + { PCI_VDEVICE(MEILHAUS, 0x4670), .driver_data = BOARD_ME4670 }, + { PCI_VDEVICE(MEILHAUS, 0x4671), .driver_data = BOARD_ME4670I }, + { PCI_VDEVICE(MEILHAUS, 0x4672), .driver_data = BOARD_ME4670S }, + { PCI_VDEVICE(MEILHAUS, 0x4673), .driver_data = BOARD_ME4670IS }, + { PCI_VDEVICE(MEILHAUS, 0x4680), .driver_data = BOARD_ME4680 }, + { PCI_VDEVICE(MEILHAUS, 0x4681), .driver_data = BOARD_ME4680I }, + { PCI_VDEVICE(MEILHAUS, 0x4682), .driver_data = BOARD_ME4680S }, + { PCI_VDEVICE(MEILHAUS, 0x4683), .driver_data = BOARD_ME4680IS }, + { } }; MODULE_DEVICE_TABLE(pci, me4000_pci_table); diff --git a/drivers/comedi/drivers/me_daq.c b/drivers/comedi/drivers/me_daq.c index 2f2ea029cffc..ff8699620ec2 100644 --- a/drivers/comedi/drivers/me_daq.c +++ b/drivers/comedi/drivers/me_daq.c @@ -538,9 +538,9 @@ static int me_daq_pci_probe(struct pci_dev *dev, } static const struct pci_device_id me_daq_pci_table[] = { - { PCI_VDEVICE(MEILHAUS, 0x2600), BOARD_ME2600 }, - { PCI_VDEVICE(MEILHAUS, 0x2000), BOARD_ME2000 }, - { 0 } + { PCI_VDEVICE(MEILHAUS, 0x2600), .driver_data = BOARD_ME2600 }, + { PCI_VDEVICE(MEILHAUS, 0x2000), .driver_data = BOARD_ME2000 }, + { } }; MODULE_DEVICE_TABLE(pci, me_daq_pci_table); diff --git a/drivers/comedi/drivers/mf6x4.c b/drivers/comedi/drivers/mf6x4.c index 14f1d5e9cd59..0e63c374bc3b 100644 --- a/drivers/comedi/drivers/mf6x4.c +++ b/drivers/comedi/drivers/mf6x4.c @@ -290,9 +290,14 @@ static int mf6x4_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id mf6x4_pci_table[] = { - { PCI_VDEVICE(HUMUSOFT, 0x0634), BOARD_MF634 }, - { PCI_VDEVICE(HUMUSOFT, 0x0624), BOARD_MF624 }, - { 0 } + { + PCI_VDEVICE(HUMUSOFT, 0x0634), + .driver_data = BOARD_MF634, + }, { + PCI_VDEVICE(HUMUSOFT, 0x0624), + .driver_data = BOARD_MF624, + }, + { } }; MODULE_DEVICE_TABLE(pci, mf6x4_pci_table); diff --git a/drivers/comedi/drivers/ni_6527.c b/drivers/comedi/drivers/ni_6527.c index ac5820085231..8c0d27c7f8a9 100644 --- a/drivers/comedi/drivers/ni_6527.c +++ b/drivers/comedi/drivers/ni_6527.c @@ -473,9 +473,9 @@ static int ni6527_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni6527_pci_table[] = { - { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 }, - { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 }, - { 0 } + { PCI_VDEVICE(NI, 0x2b10), .driver_data = BOARD_PXI6527 }, + { PCI_VDEVICE(NI, 0x2b20), .driver_data = BOARD_PCI6527 }, + { } }; MODULE_DEVICE_TABLE(pci, ni6527_pci_table); diff --git a/drivers/comedi/drivers/ni_65xx.c b/drivers/comedi/drivers/ni_65xx.c index 58334de3b253..6ab91f6e75c5 100644 --- a/drivers/comedi/drivers/ni_65xx.c +++ b/drivers/comedi/drivers/ni_65xx.c @@ -783,29 +783,29 @@ static int ni_65xx_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni_65xx_pci_table[] = { - { PCI_VDEVICE(NI, 0x1710), BOARD_PXI6509 }, - { PCI_VDEVICE(NI, 0x7085), BOARD_PCI6509 }, - { PCI_VDEVICE(NI, 0x7086), BOARD_PXI6528 }, - { PCI_VDEVICE(NI, 0x7087), BOARD_PCI6515 }, - { PCI_VDEVICE(NI, 0x7088), BOARD_PCI6514 }, - { PCI_VDEVICE(NI, 0x70a9), BOARD_PCI6528 }, - { PCI_VDEVICE(NI, 0x70c3), BOARD_PCI6511 }, - { PCI_VDEVICE(NI, 0x70c8), BOARD_PCI6513 }, - { PCI_VDEVICE(NI, 0x70c9), BOARD_PXI6515 }, - { PCI_VDEVICE(NI, 0x70cc), BOARD_PCI6512 }, - { PCI_VDEVICE(NI, 0x70cd), BOARD_PXI6514 }, - { PCI_VDEVICE(NI, 0x70d1), BOARD_PXI6513 }, - { PCI_VDEVICE(NI, 0x70d2), BOARD_PXI6512 }, - { PCI_VDEVICE(NI, 0x70d3), BOARD_PXI6511 }, - { PCI_VDEVICE(NI, 0x7124), BOARD_PCI6510 }, - { PCI_VDEVICE(NI, 0x7125), BOARD_PCI6516 }, - { PCI_VDEVICE(NI, 0x7126), BOARD_PCI6517 }, - { PCI_VDEVICE(NI, 0x7127), BOARD_PCI6518 }, - { PCI_VDEVICE(NI, 0x7128), BOARD_PCI6519 }, - { PCI_VDEVICE(NI, 0x718b), BOARD_PCI6521 }, - { PCI_VDEVICE(NI, 0x718c), BOARD_PXI6521 }, - { PCI_VDEVICE(NI, 0x71c5), BOARD_PCI6520 }, - { 0 } + { PCI_VDEVICE(NI, 0x1710), .driver_data = BOARD_PXI6509 }, + { PCI_VDEVICE(NI, 0x7085), .driver_data = BOARD_PCI6509 }, + { PCI_VDEVICE(NI, 0x7086), .driver_data = BOARD_PXI6528 }, + { PCI_VDEVICE(NI, 0x7087), .driver_data = BOARD_PCI6515 }, + { PCI_VDEVICE(NI, 0x7088), .driver_data = BOARD_PCI6514 }, + { PCI_VDEVICE(NI, 0x70a9), .driver_data = BOARD_PCI6528 }, + { PCI_VDEVICE(NI, 0x70c3), .driver_data = BOARD_PCI6511 }, + { PCI_VDEVICE(NI, 0x70c8), .driver_data = BOARD_PCI6513 }, + { PCI_VDEVICE(NI, 0x70c9), .driver_data = BOARD_PXI6515 }, + { PCI_VDEVICE(NI, 0x70cc), .driver_data = BOARD_PCI6512 }, + { PCI_VDEVICE(NI, 0x70cd), .driver_data = BOARD_PXI6514 }, + { PCI_VDEVICE(NI, 0x70d1), .driver_data = BOARD_PXI6513 }, + { PCI_VDEVICE(NI, 0x70d2), .driver_data = BOARD_PXI6512 }, + { PCI_VDEVICE(NI, 0x70d3), .driver_data = BOARD_PXI6511 }, + { PCI_VDEVICE(NI, 0x7124), .driver_data = BOARD_PCI6510 }, + { PCI_VDEVICE(NI, 0x7125), .driver_data = BOARD_PCI6516 }, + { PCI_VDEVICE(NI, 0x7126), .driver_data = BOARD_PCI6517 }, + { PCI_VDEVICE(NI, 0x7127), .driver_data = BOARD_PCI6518 }, + { PCI_VDEVICE(NI, 0x7128), .driver_data = BOARD_PCI6519 }, + { PCI_VDEVICE(NI, 0x718b), .driver_data = BOARD_PCI6521 }, + { PCI_VDEVICE(NI, 0x718c), .driver_data = BOARD_PXI6521 }, + { PCI_VDEVICE(NI, 0x71c5), .driver_data = BOARD_PCI6520 }, + { } }; MODULE_DEVICE_TABLE(pci, ni_65xx_pci_table); diff --git a/drivers/comedi/drivers/ni_660x.c b/drivers/comedi/drivers/ni_660x.c index 0679bc39e0bc..5508cdb1b0ba 100644 --- a/drivers/comedi/drivers/ni_660x.c +++ b/drivers/comedi/drivers/ni_660x.c @@ -1230,14 +1230,14 @@ static int ni_660x_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni_660x_pci_table[] = { - { PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 }, - { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 }, - { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 }, - { PCI_VDEVICE(NI, 0x2db0), BOARD_PCI6608 }, - { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 }, - { PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 }, - { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 }, - { 0 } + { PCI_VDEVICE(NI, 0x1310), .driver_data = BOARD_PCI6602 }, + { PCI_VDEVICE(NI, 0x1360), .driver_data = BOARD_PXI6602 }, + { PCI_VDEVICE(NI, 0x2c60), .driver_data = BOARD_PCI6601 }, + { PCI_VDEVICE(NI, 0x2db0), .driver_data = BOARD_PCI6608 }, + { PCI_VDEVICE(NI, 0x2cc0), .driver_data = BOARD_PXI6608 }, + { PCI_VDEVICE(NI, 0x1e30), .driver_data = BOARD_PCI6624 }, + { PCI_VDEVICE(NI, 0x1e40), .driver_data = BOARD_PXI6624 }, + { } }; MODULE_DEVICE_TABLE(pci, ni_660x_pci_table); diff --git a/drivers/comedi/drivers/ni_670x.c b/drivers/comedi/drivers/ni_670x.c index 9455c2abcc24..670ffe2c744b 100644 --- a/drivers/comedi/drivers/ni_670x.c +++ b/drivers/comedi/drivers/ni_670x.c @@ -259,10 +259,10 @@ static int ni_670x_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni_670x_pci_table[] = { - { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 }, - { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 }, - { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 }, - { 0 } + { PCI_VDEVICE(NI, 0x1290), .driver_data = BOARD_PCI6704 }, + { PCI_VDEVICE(NI, 0x1920), .driver_data = BOARD_PXI6704 }, + { PCI_VDEVICE(NI, 0x2c90), .driver_data = BOARD_PCI6703 }, + { } }; MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); diff --git a/drivers/comedi/drivers/ni_labpc_pci.c b/drivers/comedi/drivers/ni_labpc_pci.c index e2a44bbd9fa6..4c8cf9776924 100644 --- a/drivers/comedi/drivers/ni_labpc_pci.c +++ b/drivers/comedi/drivers/ni_labpc_pci.c @@ -106,8 +106,8 @@ static struct comedi_driver labpc_pci_comedi_driver = { }; static const struct pci_device_id labpc_pci_table[] = { - { PCI_VDEVICE(NI, 0x161), BOARD_NI_PCI1200 }, - { 0 } + { PCI_VDEVICE(NI, 0x0161), .driver_data = BOARD_NI_PCI1200 }, + { } }; MODULE_DEVICE_TABLE(pci, labpc_pci_table); diff --git a/drivers/comedi/drivers/ni_pcidio.c b/drivers/comedi/drivers/ni_pcidio.c index 2c7bb9c1ea5b..b137bf14bcfd 100644 --- a/drivers/comedi/drivers/ni_pcidio.c +++ b/drivers/comedi/drivers/ni_pcidio.c @@ -987,10 +987,10 @@ static int ni_pcidio_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni_pcidio_pci_table[] = { - { PCI_VDEVICE(NI, 0x1150), BOARD_PCIDIO_32HS }, - { PCI_VDEVICE(NI, 0x12b0), BOARD_PCI6534 }, - { PCI_VDEVICE(NI, 0x1320), BOARD_PXI6533 }, - { 0 } + { PCI_VDEVICE(NI, 0x1150), .driver_data = BOARD_PCIDIO_32HS }, + { PCI_VDEVICE(NI, 0x12b0), .driver_data = BOARD_PCI6534 }, + { PCI_VDEVICE(NI, 0x1320), .driver_data = BOARD_PXI6533 }, + { } }; MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table); diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index f63c390314e1..4be9ca4f4828 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -1402,72 +1402,72 @@ static int ni_pcimio_pci_probe(struct pci_dev *dev, } static const struct pci_device_id ni_pcimio_pci_table[] = { - { PCI_VDEVICE(NI, 0x0162), BOARD_PCIMIO_16XE_50 }, /* 0x1620? */ - { PCI_VDEVICE(NI, 0x1170), BOARD_PCIMIO_16XE_10 }, - { PCI_VDEVICE(NI, 0x1180), BOARD_PCIMIO_16E_1 }, - { PCI_VDEVICE(NI, 0x1190), BOARD_PCIMIO_16E_4 }, - { PCI_VDEVICE(NI, 0x11b0), BOARD_PXI6070E }, - { PCI_VDEVICE(NI, 0x11c0), BOARD_PXI6040E }, - { PCI_VDEVICE(NI, 0x11d0), BOARD_PXI6030E }, - { PCI_VDEVICE(NI, 0x1270), BOARD_PCI6032E }, - { PCI_VDEVICE(NI, 0x1330), BOARD_PCI6031E }, - { PCI_VDEVICE(NI, 0x1340), BOARD_PCI6033E }, - { PCI_VDEVICE(NI, 0x1350), BOARD_PCI6071E }, - { PCI_VDEVICE(NI, 0x14e0), BOARD_PCI6110 }, - { PCI_VDEVICE(NI, 0x14f0), BOARD_PCI6111 }, - { PCI_VDEVICE(NI, 0x1580), BOARD_PXI6031E }, - { PCI_VDEVICE(NI, 0x15b0), BOARD_PXI6071E }, - { PCI_VDEVICE(NI, 0x1880), BOARD_PCI6711 }, - { PCI_VDEVICE(NI, 0x1870), BOARD_PCI6713 }, - { PCI_VDEVICE(NI, 0x18b0), BOARD_PCI6052E }, - { PCI_VDEVICE(NI, 0x18c0), BOARD_PXI6052E }, - { PCI_VDEVICE(NI, 0x2410), BOARD_PCI6733 }, - { PCI_VDEVICE(NI, 0x2420), BOARD_PXI6733 }, - { PCI_VDEVICE(NI, 0x2430), BOARD_PCI6731 }, - { PCI_VDEVICE(NI, 0x2890), BOARD_PCI6036E }, - { PCI_VDEVICE(NI, 0x28c0), BOARD_PCI6014 }, - { PCI_VDEVICE(NI, 0x2a60), BOARD_PCI6023E }, - { PCI_VDEVICE(NI, 0x2a70), BOARD_PCI6024E }, - { PCI_VDEVICE(NI, 0x2a80), BOARD_PCI6025E }, - { PCI_VDEVICE(NI, 0x2ab0), BOARD_PXI6025E }, - { PCI_VDEVICE(NI, 0x2b80), BOARD_PXI6713 }, - { PCI_VDEVICE(NI, 0x2b90), BOARD_PXI6711 }, - { PCI_VDEVICE(NI, 0x2c80), BOARD_PCI6035E }, - { PCI_VDEVICE(NI, 0x2ca0), BOARD_PCI6034E }, - { PCI_VDEVICE(NI, 0x70aa), BOARD_PCI6229 }, - { PCI_VDEVICE(NI, 0x70ab), BOARD_PCI6259 }, - { PCI_VDEVICE(NI, 0x70ac), BOARD_PCI6289 }, - { PCI_VDEVICE(NI, 0x70ad), BOARD_PXI6251 }, - { PCI_VDEVICE(NI, 0x70ae), BOARD_PXI6220 }, - { PCI_VDEVICE(NI, 0x70af), BOARD_PCI6221 }, - { PCI_VDEVICE(NI, 0x70b0), BOARD_PCI6220 }, - { PCI_VDEVICE(NI, 0x70b1), BOARD_PXI6229 }, - { PCI_VDEVICE(NI, 0x70b2), BOARD_PXI6259 }, - { PCI_VDEVICE(NI, 0x70b3), BOARD_PXI6289 }, - { PCI_VDEVICE(NI, 0x70b4), BOARD_PCI6250 }, - { PCI_VDEVICE(NI, 0x70b5), BOARD_PXI6221 }, - { PCI_VDEVICE(NI, 0x70b6), BOARD_PCI6280 }, - { PCI_VDEVICE(NI, 0x70b7), BOARD_PCI6254 }, - { PCI_VDEVICE(NI, 0x70b8), BOARD_PCI6251 }, - { PCI_VDEVICE(NI, 0x70b9), BOARD_PXI6250 }, - { PCI_VDEVICE(NI, 0x70ba), BOARD_PXI6254 }, - { PCI_VDEVICE(NI, 0x70bb), BOARD_PXI6280 }, - { PCI_VDEVICE(NI, 0x70bc), BOARD_PCI6284 }, - { PCI_VDEVICE(NI, 0x70bd), BOARD_PCI6281 }, - { PCI_VDEVICE(NI, 0x70be), BOARD_PXI6284 }, - { PCI_VDEVICE(NI, 0x70bf), BOARD_PXI6281 }, - { PCI_VDEVICE(NI, 0x70c0), BOARD_PCI6143 }, - { PCI_VDEVICE(NI, 0x70f2), BOARD_PCI6224 }, - { PCI_VDEVICE(NI, 0x70f3), BOARD_PXI6224 }, - { PCI_VDEVICE(NI, 0x710d), BOARD_PXI6143 }, - { PCI_VDEVICE(NI, 0x716c), BOARD_PCI6225 }, - { PCI_VDEVICE(NI, 0x716d), BOARD_PXI6225 }, - { PCI_VDEVICE(NI, 0x717d), BOARD_PCIE6251 }, - { PCI_VDEVICE(NI, 0x717f), BOARD_PCIE6259 }, - { PCI_VDEVICE(NI, 0x71bc), BOARD_PCI6221_37PIN }, - { PCI_VDEVICE(NI, 0x72e8), BOARD_PXIE6251 }, - { PCI_VDEVICE(NI, 0x72e9), BOARD_PXIE6259 }, - { 0 } + { PCI_VDEVICE(NI, 0x0162), .driver_data = BOARD_PCIMIO_16XE_50 }, /* 0x1620? */ + { PCI_VDEVICE(NI, 0x1170), .driver_data = BOARD_PCIMIO_16XE_10 }, + { PCI_VDEVICE(NI, 0x1180), .driver_data = BOARD_PCIMIO_16E_1 }, + { PCI_VDEVICE(NI, 0x1190), .driver_data = BOARD_PCIMIO_16E_4 }, + { PCI_VDEVICE(NI, 0x11b0), .driver_data = BOARD_PXI6070E }, + { PCI_VDEVICE(NI, 0x11c0), .driver_data = BOARD_PXI6040E }, + { PCI_VDEVICE(NI, 0x11d0), .driver_data = BOARD_PXI6030E }, + { PCI_VDEVICE(NI, 0x1270), .driver_data = BOARD_PCI6032E }, + { PCI_VDEVICE(NI, 0x1330), .driver_data = BOARD_PCI6031E }, + { PCI_VDEVICE(NI, 0x1340), .driver_data = BOARD_PCI6033E }, + { PCI_VDEVICE(NI, 0x1350), .driver_data = BOARD_PCI6071E }, + { PCI_VDEVICE(NI, 0x14e0), .driver_data = BOARD_PCI6110 }, + { PCI_VDEVICE(NI, 0x14f0), .driver_data = BOARD_PCI6111 }, + { PCI_VDEVICE(NI, 0x1580), .driver_data = BOARD_PXI6031E }, + { PCI_VDEVICE(NI, 0x15b0), .driver_data = BOARD_PXI6071E }, + { PCI_VDEVICE(NI, 0x1880), .driver_data = BOARD_PCI6711 }, + { PCI_VDEVICE(NI, 0x1870), .driver_data = BOARD_PCI6713 }, + { PCI_VDEVICE(NI, 0x18b0), .driver_data = BOARD_PCI6052E }, + { PCI_VDEVICE(NI, 0x18c0), .driver_data = BOARD_PXI6052E }, + { PCI_VDEVICE(NI, 0x2410), .driver_data = BOARD_PCI6733 }, + { PCI_VDEVICE(NI, 0x2420), .driver_data = BOARD_PXI6733 }, + { PCI_VDEVICE(NI, 0x2430), .driver_data = BOARD_PCI6731 }, + { PCI_VDEVICE(NI, 0x2890), .driver_data = BOARD_PCI6036E }, + { PCI_VDEVICE(NI, 0x28c0), .driver_data = BOARD_PCI6014 }, + { PCI_VDEVICE(NI, 0x2a60), .driver_data = BOARD_PCI6023E }, + { PCI_VDEVICE(NI, 0x2a70), .driver_data = BOARD_PCI6024E }, + { PCI_VDEVICE(NI, 0x2a80), .driver_data = BOARD_PCI6025E }, + { PCI_VDEVICE(NI, 0x2ab0), .driver_data = BOARD_PXI6025E }, + { PCI_VDEVICE(NI, 0x2b80), .driver_data = BOARD_PXI6713 }, + { PCI_VDEVICE(NI, 0x2b90), .driver_data = BOARD_PXI6711 }, + { PCI_VDEVICE(NI, 0x2c80), .driver_data = BOARD_PCI6035E }, + { PCI_VDEVICE(NI, 0x2ca0), .driver_data = BOARD_PCI6034E }, + { PCI_VDEVICE(NI, 0x70aa), .driver_data = BOARD_PCI6229 }, + { PCI_VDEVICE(NI, 0x70ab), .driver_data = BOARD_PCI6259 }, + { PCI_VDEVICE(NI, 0x70ac), .driver_data = BOARD_PCI6289 }, + { PCI_VDEVICE(NI, 0x70ad), .driver_data = BOARD_PXI6251 }, + { PCI_VDEVICE(NI, 0x70ae), .driver_data = BOARD_PXI6220 }, + { PCI_VDEVICE(NI, 0x70af), .driver_data = BOARD_PCI6221 }, + { PCI_VDEVICE(NI, 0x70b0), .driver_data = BOARD_PCI6220 }, + { PCI_VDEVICE(NI, 0x70b1), .driver_data = BOARD_PXI6229 }, + { PCI_VDEVICE(NI, 0x70b2), .driver_data = BOARD_PXI6259 }, + { PCI_VDEVICE(NI, 0x70b3), .driver_data = BOARD_PXI6289 }, + { PCI_VDEVICE(NI, 0x70b4), .driver_data = BOARD_PCI6250 }, + { PCI_VDEVICE(NI, 0x70b5), .driver_data = BOARD_PXI6221 }, + { PCI_VDEVICE(NI, 0x70b6), .driver_data = BOARD_PCI6280 }, + { PCI_VDEVICE(NI, 0x70b7), .driver_data = BOARD_PCI6254 }, + { PCI_VDEVICE(NI, 0x70b8), .driver_data = BOARD_PCI6251 }, + { PCI_VDEVICE(NI, 0x70b9), .driver_data = BOARD_PXI6250 }, + { PCI_VDEVICE(NI, 0x70ba), .driver_data = BOARD_PXI6254 }, + { PCI_VDEVICE(NI, 0x70bb), .driver_data = BOARD_PXI6280 }, + { PCI_VDEVICE(NI, 0x70bc), .driver_data = BOARD_PCI6284 }, + { PCI_VDEVICE(NI, 0x70bd), .driver_data = BOARD_PCI6281 }, + { PCI_VDEVICE(NI, 0x70be), .driver_data = BOARD_PXI6284 }, + { PCI_VDEVICE(NI, 0x70bf), .driver_data = BOARD_PXI6281 }, + { PCI_VDEVICE(NI, 0x70c0), .driver_data = BOARD_PCI6143 }, + { PCI_VDEVICE(NI, 0x70f2), .driver_data = BOARD_PCI6224 }, + { PCI_VDEVICE(NI, 0x70f3), .driver_data = BOARD_PXI6224 }, + { PCI_VDEVICE(NI, 0x710d), .driver_data = BOARD_PXI6143 }, + { PCI_VDEVICE(NI, 0x716c), .driver_data = BOARD_PCI6225 }, + { PCI_VDEVICE(NI, 0x716d), .driver_data = BOARD_PXI6225 }, + { PCI_VDEVICE(NI, 0x717d), .driver_data = BOARD_PCIE6251 }, + { PCI_VDEVICE(NI, 0x717f), .driver_data = BOARD_PCIE6259 }, + { PCI_VDEVICE(NI, 0x71bc), .driver_data = BOARD_PCI6221_37PIN }, + { PCI_VDEVICE(NI, 0x72e8), .driver_data = BOARD_PXIE6251 }, + { PCI_VDEVICE(NI, 0x72e9), .driver_data = BOARD_PXIE6259 }, + { } }; MODULE_DEVICE_TABLE(pci, ni_pcimio_pci_table); diff --git a/drivers/comedi/drivers/rtd520.c b/drivers/comedi/drivers/rtd520.c index 44bb0decd7a4..0575913abfe7 100644 --- a/drivers/comedi/drivers/rtd520.c +++ b/drivers/comedi/drivers/rtd520.c @@ -1345,9 +1345,9 @@ static int rtd520_pci_probe(struct pci_dev *dev, } static const struct pci_device_id rtd520_pci_table[] = { - { PCI_VDEVICE(RTD, 0x7520), BOARD_DM7520 }, - { PCI_VDEVICE(RTD, 0x4520), BOARD_PCI4520 }, - { 0 } + { PCI_VDEVICE(RTD, 0x7520), .driver_data = BOARD_DM7520 }, + { PCI_VDEVICE(RTD, 0x4520), .driver_data = BOARD_PCI4520 }, + { } }; MODULE_DEVICE_TABLE(pci, rtd520_pci_table); diff --git a/drivers/comedi/drivers/s626.c b/drivers/comedi/drivers/s626.c index 0e5f9a9a7fd3..ce7ae6b6d40b 100644 --- a/drivers/comedi/drivers/s626.c +++ b/drivers/comedi/drivers/s626.c @@ -2585,9 +2585,9 @@ static int s626_pci_probe(struct pci_dev *dev, * Philips SAA7146 media/dvb based cards. */ static const struct pci_device_id s626_pci_table[] = { - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, - 0x6000, 0x0272) }, - { 0 } + { PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, + 0x6000, 0x0272) }, + { } }; MODULE_DEVICE_TABLE(pci, s626_pci_table); From be5e8d5f61b31105e0ed7f51cd591653aea5054f Mon Sep 17 00:00:00 2001 From: Ayush Mukkanwar Date: Mon, 11 May 2026 20:39:29 +0530 Subject: [PATCH 1352/5214] staging: octeon: ethernet-mem: replace pr_warn with dev_warn in free functions Add struct platform_device parameter to cvm_oct_free_hw_skbuff, cvm_oct_free_hw_memory and cvm_oct_mem_empty_fpa. Replace pr_warn calls with dev_warn, using &pdev->dev for device-aware logging. Signed-off-by: Ayush Mukkanwar Link: https://patch.msgid.link/20260511150931.93382-2-ayushmukkanwar@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/ethernet-mem.c | 31 ++++++++++++++++----------- drivers/staging/octeon/ethernet-mem.h | 5 ++++- drivers/staging/octeon/ethernet.c | 6 +++--- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c index 532594957ebc..af79b2bdac27 100644 --- a/drivers/staging/octeon/ethernet-mem.c +++ b/drivers/staging/octeon/ethernet-mem.c @@ -5,6 +5,7 @@ * Copyright (c) 2003-2010 Cavium Networks */ +#include #include #include #include @@ -40,11 +41,13 @@ static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) /** * cvm_oct_free_hw_skbuff- free hardware pool skbuffs + * @pdev: Platform device for logging * @pool: Pool to allocate an skbuff for * @size: Size of the buffer needed for the pool * @elements: Number of buffers to allocate */ -static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) +static void cvm_oct_free_hw_skbuff(struct platform_device *pdev, + int pool, int size, int elements) { char *memory; @@ -59,11 +62,11 @@ static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) } while (memory); if (elements < 0) - pr_warn("Freeing of pool %u had too many skbuffs (%d)\n", - pool, elements); + dev_warn(&pdev->dev, "Freeing of pool %u had too many skbuffs (%d)\n", + pool, elements); else if (elements > 0) - pr_warn("Freeing of pool %u is missing %d skbuffs\n", - pool, elements); + dev_warn(&pdev->dev, "Freeing of pool %u is missing %d skbuffs\n", + pool, elements); } /** @@ -107,11 +110,13 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) /** * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory + * @pdev: Platform device for logging * @pool: FPA pool to free * @size: Size of each buffer in the pool * @elements: Number of buffers that should be in the pool */ -static void cvm_oct_free_hw_memory(int pool, int size, int elements) +static void cvm_oct_free_hw_memory(struct platform_device *pdev, + int pool, int size, int elements) { char *memory; char *fpa; @@ -127,11 +132,11 @@ static void cvm_oct_free_hw_memory(int pool, int size, int elements) } while (fpa); if (elements < 0) - pr_warn("Freeing of pool %u had too many buffers (%d)\n", - pool, elements); + dev_warn(&pdev->dev, "Freeing of pool %u had too many buffers (%d)\n", + pool, elements); else if (elements > 0) - pr_warn("Warning: Freeing of pool %u is missing %d buffers\n", - pool, elements); + dev_warn(&pdev->dev, "Freeing of pool %u is missing %d buffers\n", + pool, elements); } int cvm_oct_mem_fill_fpa(int pool, int size, int elements) @@ -145,10 +150,10 @@ int cvm_oct_mem_fill_fpa(int pool, int size, int elements) return freed; } -void cvm_oct_mem_empty_fpa(int pool, int size, int elements) +void cvm_oct_mem_empty_fpa(struct platform_device *pdev, int pool, int size, int elements) { if (pool == CVMX_FPA_PACKET_POOL) - cvm_oct_free_hw_skbuff(pool, size, elements); + cvm_oct_free_hw_skbuff(pdev, pool, size, elements); else - cvm_oct_free_hw_memory(pool, size, elements); + cvm_oct_free_hw_memory(pdev, pool, size, elements); } diff --git a/drivers/staging/octeon/ethernet-mem.h b/drivers/staging/octeon/ethernet-mem.h index 692dcdb7154d..ff10ba4525ee 100644 --- a/drivers/staging/octeon/ethernet-mem.h +++ b/drivers/staging/octeon/ethernet-mem.h @@ -6,4 +6,7 @@ */ int cvm_oct_mem_fill_fpa(int pool, int size, int elements); -void cvm_oct_mem_empty_fpa(int pool, int size, int elements); +struct platform_device; + +void cvm_oct_mem_empty_fpa(struct platform_device *pdev, int pool, int size, + int elements); diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index d85a9991faf6..31464ed63b58 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -958,12 +958,12 @@ static void cvm_oct_remove(struct platform_device *pdev) cvmx_ipd_free_ptr(); /* Free the HW pools */ - cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, + cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); - cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, + cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) - cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, + cvm_oct_mem_empty_fpa(pdev, CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); } From 91442512943b50329f8f20ffc5db28e53782aff7 Mon Sep 17 00:00:00 2001 From: Ayush Mukkanwar Date: Mon, 11 May 2026 20:39:30 +0530 Subject: [PATCH 1353/5214] staging: octeon: ethernet: replace pr_err and pr_info with dev_err and netdev_err Replace pr_err() and pr_info() calls in cvm_oct_probe() with dev_err(), netdev_err(), and netdev_info() to include device information in log messages. Use dev_err() where no net_device is available (allocation failures), and netdev_err()/netdev_info() where a net_device exists. Signed-off-by: Ayush Mukkanwar Link: https://patch.msgid.link/20260511150931.93382-3-ayushmukkanwar@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/ethernet.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 31464ed63b58..eaa4f04093b8 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -685,7 +685,7 @@ static int cvm_oct_probe(struct platform_device *pdev) pip = pdev->dev.of_node; if (!pip) { - pr_err("Error: No 'pip' in /aliases\n"); + dev_err(&pdev->dev, "No 'pip' in /aliases\n"); return -EINVAL; } @@ -783,16 +783,16 @@ static int cvm_oct_probe(struct platform_device *pdev) dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead; if (register_netdev(dev) < 0) { - pr_err("Failed to register ethernet device for POW\n"); + netdev_err(dev, "Failed to register ethernet device for POW\n"); free_netdev(dev); } else { cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev; - pr_info("%s: POW send group %d, receive group %d\n", - dev->name, pow_send_group, - pow_receive_group); + netdev_info(dev, "POW send group %d, receive group %d\n", + pow_send_group, + pow_receive_group); } } else { - pr_err("Failed to allocate ethernet device for POW\n"); + dev_err(&pdev->dev, "Failed to allocate ethernet device for POW\n"); } } @@ -812,8 +812,8 @@ static int cvm_oct_probe(struct platform_device *pdev) struct net_device *dev = alloc_etherdev(sizeof(struct octeon_ethernet)); if (!dev) { - pr_err("Failed to allocate ethernet device for port %d\n", - port); + dev_err(&pdev->dev, "Failed to allocate ethernet device for port %d\n", + port); continue; } @@ -897,8 +897,8 @@ static int cvm_oct_probe(struct platform_device *pdev) if (!dev->netdev_ops) { free_netdev(dev); } else if (register_netdev(dev) < 0) { - pr_err("Failed to register ethernet device for interface %d, port %d\n", - interface, priv->port); + netdev_err(dev, "Failed to register ethernet device for interface %d, port %d\n", + interface, priv->port); free_netdev(dev); } else { cvm_oct_device[priv->port] = dev; From 2191a8dfd1f5a3091e9f388899beb137686c6532 Mon Sep 17 00:00:00 2001 From: Ayush Mukkanwar Date: Mon, 11 May 2026 20:39:31 +0530 Subject: [PATCH 1354/5214] staging: octeon: replace pr_warn with dev_warn in fill and rx paths Add struct platform_device parameter to cvm_oct_fill_hw_memory, cvm_oct_mem_fill_fpa, cvm_oct_rx_refill_pool and cvm_oct_rx_initialize to support device-aware logging. Replace pr_warn with dev_warn using &pdev->dev. To avoid passing these parameters through global state, introduce struct octeon_ethernet_platform to hold per-device state including the rx_refill_work and the oct_rx_group array. This ensures all receive group state and workers are correctly associated with the platform device. Define struct oct_rx_group and struct octeon_ethernet_platform in octeon-ethernet.h so they are shared across compilation units. Signed-off-by: Ayush Mukkanwar Link: https://patch.msgid.link/20260511150931.93382-4-ayushmukkanwar@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/ethernet-mem.c | 12 +++--- drivers/staging/octeon/ethernet-mem.h | 3 +- drivers/staging/octeon/ethernet-rx.c | 49 ++++++++++++------------ drivers/staging/octeon/ethernet-rx.h | 13 ++++--- drivers/staging/octeon/ethernet.c | 37 +++++++++++------- drivers/staging/octeon/octeon-ethernet.h | 14 +++++++ 6 files changed, 79 insertions(+), 49 deletions(-) diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c index af79b2bdac27..68c3ef984e56 100644 --- a/drivers/staging/octeon/ethernet-mem.c +++ b/drivers/staging/octeon/ethernet-mem.c @@ -71,13 +71,15 @@ static void cvm_oct_free_hw_skbuff(struct platform_device *pdev, /** * cvm_oct_fill_hw_memory - fill a hardware pool with memory. + * @pdev: Platform device for logging * @pool: Pool to populate * @size: Size of each buffer in the pool * @elements: Number of buffers to allocate * * Returns the actual number of buffers allocated. */ -static int cvm_oct_fill_hw_memory(int pool, int size, int elements) +static int cvm_oct_fill_hw_memory(struct platform_device *pdev, int pool, int size, + int elements) { char *memory; char *fpa; @@ -96,8 +98,8 @@ static int cvm_oct_fill_hw_memory(int pool, int size, int elements) */ memory = kmalloc(size + 256, GFP_ATOMIC); if (unlikely(!memory)) { - pr_warn("Unable to allocate %u bytes for FPA pool %d\n", - elements * size, pool); + dev_warn(&pdev->dev, "Unable to allocate %u bytes for FPA pool %d\n", + elements * size, pool); break; } fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL); @@ -139,14 +141,14 @@ static void cvm_oct_free_hw_memory(struct platform_device *pdev, pool, elements); } -int cvm_oct_mem_fill_fpa(int pool, int size, int elements) +int cvm_oct_mem_fill_fpa(struct platform_device *pdev, int pool, int size, int elements) { int freed; if (pool == CVMX_FPA_PACKET_POOL) freed = cvm_oct_fill_hw_skbuff(pool, size, elements); else - freed = cvm_oct_fill_hw_memory(pool, size, elements); + freed = cvm_oct_fill_hw_memory(pdev, pool, size, elements); return freed; } diff --git a/drivers/staging/octeon/ethernet-mem.h b/drivers/staging/octeon/ethernet-mem.h index ff10ba4525ee..9279bb0de2db 100644 --- a/drivers/staging/octeon/ethernet-mem.h +++ b/drivers/staging/octeon/ethernet-mem.h @@ -5,8 +5,9 @@ * Copyright (c) 2003-2007 Cavium Networks */ -int cvm_oct_mem_fill_fpa(int pool, int size, int elements); struct platform_device; +int cvm_oct_mem_fill_fpa(struct platform_device *pdev, int pool, int size, + int elements); void cvm_oct_mem_empty_fpa(struct platform_device *pdev, int pool, int size, int elements); diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index d0b43d50b83c..cd36b5ba6f6c 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -5,6 +5,7 @@ * Copyright (c) 2003-2010 Cavium Networks */ +#include #include #include #include @@ -31,12 +32,6 @@ static atomic_t oct_rx_ready = ATOMIC_INIT(0); -static struct oct_rx_group { - int irq; - int group; - struct napi_struct napi; -} oct_rx_group[16]; - /** * cvm_oct_do_interrupt - interrupt handler. * @irq: Interrupt number. @@ -397,7 +392,7 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget) /* Restore the scratch area */ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); } - cvm_oct_rx_refill_pool(0); + cvm_oct_rx_refill_pool(rx_group->pdev, 0); return rx_count; } @@ -434,24 +429,28 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) */ void cvm_oct_poll_controller(struct net_device *dev) { + struct platform_device *pdev = to_platform_device(dev->dev.parent); + struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev); int i; if (!atomic_read(&oct_rx_ready)) return; - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) { if (!(pow_receive_groups & BIT(i))) continue; - cvm_oct_poll(&oct_rx_group[i], 16); + cvm_oct_poll(&plat->rx_group[i], 16); } } #endif -void cvm_oct_rx_initialize(void) +void cvm_oct_rx_initialize(struct platform_device *pdev) { int i; struct net_device *dev_for_napi = NULL; + struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev); + struct oct_rx_group *rx_group = plat->rx_group; for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) { if (cvm_oct_device[i]) { @@ -463,27 +462,28 @@ void cvm_oct_rx_initialize(void) if (!dev_for_napi) panic("No net_devices were allocated."); - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) { int ret; if (!(pow_receive_groups & BIT(i))) continue; - netif_napi_add_weight(dev_for_napi, &oct_rx_group[i].napi, + netif_napi_add_weight(dev_for_napi, &rx_group[i].napi, cvm_oct_napi_poll, rx_napi_weight); - napi_enable(&oct_rx_group[i].napi); + napi_enable(&rx_group[i].napi); - oct_rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i; - oct_rx_group[i].group = i; + rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i; + rx_group[i].group = i; + rx_group[i].pdev = pdev; /* Register an IRQ handler to receive POW interrupts */ - ret = request_irq(oct_rx_group[i].irq, cvm_oct_do_interrupt, 0, - "Ethernet", &oct_rx_group[i].napi); + ret = request_irq(rx_group[i].irq, cvm_oct_do_interrupt, 0, + "Ethernet", &rx_group[i].napi); if (ret) panic("Could not acquire Ethernet IRQ %d\n", - oct_rx_group[i].irq); + rx_group[i].irq); - disable_irq_nosync(oct_rx_group[i].irq); + disable_irq_nosync(rx_group[i].irq); /* Enable POW interrupt when our port has at least one packet */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { @@ -515,16 +515,17 @@ void cvm_oct_rx_initialize(void) /* Schedule NAPI now. This will indirectly enable the * interrupt. */ - napi_schedule(&oct_rx_group[i].napi); + napi_schedule(&rx_group[i].napi); } atomic_inc(&oct_rx_ready); } -void cvm_oct_rx_shutdown(void) +void cvm_oct_rx_shutdown(struct platform_device *pdev) { + struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev); int i; - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + for (i = 0; i < ARRAY_SIZE(plat->rx_group); i++) { if (!(pow_receive_groups & BIT(i))) continue; @@ -535,8 +536,8 @@ void cvm_oct_rx_shutdown(void) cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0); /* Free the interrupt handler */ - free_irq(oct_rx_group[i].irq, &oct_rx_group[i].napi); + free_irq(plat->rx_group[i].irq, &plat->rx_group[i].napi); - netif_napi_del(&oct_rx_group[i].napi); + netif_napi_del(&plat->rx_group[i].napi); } } diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h index ff6482fa20d6..6093694326cb 100644 --- a/drivers/staging/octeon/ethernet-rx.h +++ b/drivers/staging/octeon/ethernet-rx.h @@ -5,11 +5,14 @@ * Copyright (c) 2003-2007 Cavium Networks */ -void cvm_oct_poll_controller(struct net_device *dev); -void cvm_oct_rx_initialize(void); -void cvm_oct_rx_shutdown(void); +struct platform_device; -static inline void cvm_oct_rx_refill_pool(int fill_threshold) +void cvm_oct_poll_controller(struct net_device *dev); +void cvm_oct_rx_initialize(struct platform_device *pdev); +void cvm_oct_rx_shutdown(struct platform_device *pdev); + +static inline void cvm_oct_rx_refill_pool(struct platform_device *pdev, + int fill_threshold) { int number_to_free; int num_freed; @@ -20,7 +23,7 @@ static inline void cvm_oct_rx_refill_pool(int fill_threshold) if (number_to_free > fill_threshold) { cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -number_to_free); - num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, + num_freed = cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, number_to_free); if (num_freed != number_to_free) { diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index eaa4f04093b8..f3fa221f452e 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -104,11 +104,10 @@ struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; u64 cvm_oct_tx_poll_interval; -static void cvm_oct_rx_refill_worker(struct work_struct *work); -static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker); - static void cvm_oct_rx_refill_worker(struct work_struct *work) { + struct octeon_ethernet_platform *plat = container_of(work, + struct octeon_ethernet_platform, rx_refill_work.work); /* * FPA 0 may have been drained, try to refill it if we need * more than num_packet_buffers / 2, otherwise normal receive @@ -116,10 +115,10 @@ static void cvm_oct_rx_refill_worker(struct work_struct *work) * could be received so cvm_oct_napi_poll would never be * invoked to do the refill. */ - cvm_oct_rx_refill_pool(num_packet_buffers / 2); + cvm_oct_rx_refill_pool(plat->pdev, num_packet_buffers / 2); if (!atomic_read(&cvm_oct_poll_queue_stopping)) - schedule_delayed_work(&cvm_oct_rx_refill_work, HZ); + schedule_delayed_work(&plat->rx_refill_work, HZ); } static void cvm_oct_periodic_worker(struct work_struct *work) @@ -138,16 +137,16 @@ static void cvm_oct_periodic_worker(struct work_struct *work) schedule_delayed_work(&priv->port_periodic_work, HZ); } -static void cvm_oct_configure_common_hw(void) +static void cvm_oct_configure_common_hw(struct platform_device *pdev) { /* Setup the FPA */ cvmx_fpa_enable(); - cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, + cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); - cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, + cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) - cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, + cvm_oct_mem_fill_fpa(pdev, CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 1024); #ifdef __LITTLE_ENDIAN @@ -678,6 +677,15 @@ static int cvm_oct_probe(struct platform_device *pdev) int qos; struct device_node *pip; int mtu_overhead = ETH_HLEN + ETH_FCS_LEN; + struct octeon_ethernet_platform *plat; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->pdev = pdev; + INIT_DELAYED_WORK(&plat->rx_refill_work, cvm_oct_rx_refill_worker); + platform_set_drvdata(pdev, plat); #if IS_ENABLED(CONFIG_VLAN_8021Q) mtu_overhead += VLAN_HLEN; @@ -689,7 +697,7 @@ static int cvm_oct_probe(struct platform_device *pdev) return -EINVAL; } - cvm_oct_configure_common_hw(); + cvm_oct_configure_common_hw(pdev); cvmx_helper_initialize_packet_io_global(); @@ -912,28 +920,29 @@ static int cvm_oct_probe(struct platform_device *pdev) } cvm_oct_tx_initialize(); - cvm_oct_rx_initialize(); + cvm_oct_rx_initialize(pdev); /* * 150 uS: about 10 1500-byte packets at 1GE. */ cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000); - schedule_delayed_work(&cvm_oct_rx_refill_work, HZ); + schedule_delayed_work(&plat->rx_refill_work, HZ); return 0; } static void cvm_oct_remove(struct platform_device *pdev) { + struct octeon_ethernet_platform *plat = platform_get_drvdata(pdev); int port; cvmx_ipd_disable(); atomic_inc_return(&cvm_oct_poll_queue_stopping); - cancel_delayed_work_sync(&cvm_oct_rx_refill_work); + cancel_delayed_work_sync(&plat->rx_refill_work); - cvm_oct_rx_shutdown(); + cvm_oct_rx_shutdown(pdev); cvm_oct_tx_shutdown(); cvmx_pko_disable(); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h index a6140705706f..0ac430db1e6e 100644 --- a/drivers/staging/octeon/octeon-ethernet.h +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -11,6 +11,7 @@ #ifndef OCTEON_ETHERNET_H #define OCTEON_ETHERNET_H +#include #include #include @@ -74,6 +75,19 @@ struct octeon_ethernet { struct device_node *of_node; }; +struct oct_rx_group { + int irq; + int group; + struct napi_struct napi; + struct platform_device *pdev; +}; + +struct octeon_ethernet_platform { + struct platform_device *pdev; + struct delayed_work rx_refill_work; + struct oct_rx_group rx_group[16]; +}; + int cvm_oct_free_work(void *work_queue_entry); int cvm_oct_rgmii_open(struct net_device *dev); From 0007c790711717319977dc14cdf18b8fbce6d7ae Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Mon, 11 May 2026 20:17:45 +0300 Subject: [PATCH 1355/5214] staging: sm750fb: remove unnecessary initializations Remove two instances of `de_ctrl = 0` initializations since the variable is overridden unconditionally before being used. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260511171745.79646-1-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_accel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index ec2f0a6aa57d..5ba1af274730 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -159,7 +159,6 @@ int sm750_hw_copyarea(struct lynx_accel *accel, direction = LEFT_TO_RIGHT; /* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */ - de_ctrl = 0; /* If source and destination are the same surface, need to check for overlay cases */ if (source_base == dest_base && source_pitch == dest_pitch) { @@ -326,7 +325,7 @@ int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf, unsigned int bytes_per_scan; unsigned int words_per_scan; unsigned int bytes_remain; - unsigned int de_ctrl = 0; + unsigned int de_ctrl; unsigned char remain[4]; int i, j; int ret; From 6602a11fdb2d9b23e0334b96ce77c0c343e9c913 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Mon, 11 May 2026 11:20:37 -0700 Subject: [PATCH 1356/5214] staging: rtl8723bs: remove unnecessary blank lines in rtw_mlme_ext.c Remove unnecessary blank lines around braces {} to improve readability. This fixes the following checkpatch.pl checks in rtw_mlme_ext.c: - CHECK: Blank lines aren't necessary after an open brace '{' - CHECK: Blank lines aren't necessary before a close brace '}' Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260511182038.6625-2-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 54 ------------------- 1 file changed, 54 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index ba19a162896e..a86d6f97cf02 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -272,7 +272,6 @@ static void init_channel_list(struct adapter *padapter, struct rt_channel_info * u8 chanset_size, struct p2p_channels *channel_list) { - static const struct p2p_oper_class_map op_class[] = { { IEEE80211G, 81, 1, 13, 1, BW20 }, { IEEE80211G, 82, 14, 14, 1, BW20 }, @@ -317,7 +316,6 @@ static void init_channel_list(struct adapter *padapter, struct rt_channel_info * } } channel_list->reg_classes = cla; - } static u8 init_channel_set(struct adapter *padapter, u8 ChannelPlan, struct rt_channel_info *channel_set) @@ -534,7 +532,6 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) } return _SUCCESS; - } unsigned int OnProbeRsp(struct adapter *padapter, union recv_frame *precv_frame) @@ -547,7 +544,6 @@ unsigned int OnProbeRsp(struct adapter *padapter, union recv_frame *precv_frame) } return _SUCCESS; - } unsigned int OnBeacon(struct adapter *padapter, union recv_frame *precv_frame) @@ -660,7 +656,6 @@ unsigned int OnBeacon(struct adapter *padapter, union recv_frame *precv_frame) _END_ONBEACON_: return _SUCCESS; - } unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) @@ -727,7 +722,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) pstat = rtw_get_stainfo(pstapriv, sa); if (!pstat) { - /* allocate a new one */ pstat = rtw_alloc_stainfo(pstapriv, sa); if (!pstat) { @@ -741,7 +735,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) /* pstat->flags = 0; */ /* pstat->capability = 0; */ } else { - spin_lock_bh(&pstapriv->asoc_list_lock); if (!list_empty(&pstat->asoc_list)) { list_del_init(&pstat->asoc_list); @@ -759,7 +752,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) spin_lock_bh(&pstapriv->auth_list_lock); if (list_empty(&pstat->auth_list)) { - list_add_tail(&pstat->auth_list, &pstapriv->auth_list); pstapriv->auth_list_cnt++; } @@ -792,7 +784,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) pstat->state |= WIFI_FW_AUTH_STATE; pstat->authalg = algorithm; } else if (seq == 3) { - p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len, len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4); @@ -839,7 +830,6 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) issue_auth(padapter, pstat, (unsigned short)status); return _FAIL; - } unsigned int OnAuthClient(struct adapter *padapter, union recv_frame *precv_frame) @@ -916,7 +906,6 @@ unsigned int OnAuthClient(struct adapter *padapter, union recv_frame *precv_fram /* pmlmeinfo->state &= ~(WIFI_FW_AUTH_STATE); */ return _FAIL; - } unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) @@ -1028,7 +1017,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, WLAN_EID_EXT_SUPP_RATES, &ie_len, pkt_len - WLAN_HDR_A3_LEN - ie_offset); if (p) { - if (support_rate_num + ie_len <= sizeof(supportRate)) { memcpy(supportRate + support_rate_num, p + 2, ie_len); support_rate_num += ie_len; @@ -1053,7 +1041,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) pstat->wpa2_pairwise_cipher = 0; memset(pstat->wpa_ie, 0, sizeof(pstat->wpa_ie)); if ((psecuritypriv->wpa_psk & BIT(1)) && elems.rsn_ie) { - int group_cipher = 0, pairwise_cipher = 0; wpa_ie = elems.rsn_ie; @@ -1076,7 +1063,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) } } else if ((psecuritypriv->wpa_psk & BIT(0)) && elems.wpa_ie) { - int group_cipher = 0, pairwise_cipher = 0; wpa_ie = elems.wpa_ie; @@ -1138,7 +1124,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) status = WLAN_STATUS_INVALID_IE; goto OnAssocReqFail; - } if (elems.wps_ie) { @@ -1150,7 +1135,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) if (copy_len > 0) memcpy(pstat->wpa_ie, wpa_ie-2, copy_len); - } /* check if there is WMM IE & support WWM-PS */ @@ -1206,7 +1190,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) pstat->uapsd_be = BIT(0)|BIT(1); else pstat->uapsd_be = 0; - } break; @@ -1263,7 +1246,6 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) /* if (pstat->aid > NUM_STA) { */ if (pstat->aid > pstapriv->max_num_sta) { - pstat->aid = 0; status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; @@ -1461,7 +1443,6 @@ unsigned int OnDeAuth(struct adapter *padapter, union recv_frame *precv_frame) list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, false, reason); - } spin_unlock_bh(&pstapriv->asoc_list_lock); @@ -1532,7 +1513,6 @@ unsigned int OnDisassoc(struct adapter *padapter, union recv_frame *precv_frame) list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, false, reason); - } spin_unlock_bh(&pstapriv->asoc_list_lock); @@ -1549,7 +1529,6 @@ unsigned int OnDisassoc(struct adapter *padapter, union recv_frame *precv_frame) pmlmepriv->link_detect_info.busy_traffic = false; return _SUCCESS; - } unsigned int OnAtim(struct adapter *padapter, union recv_frame *precv_frame) @@ -1857,11 +1836,9 @@ unsigned int OnAction(struct adapter *padapter, union recv_frame *precv_frame) if (category == ptable->num) ptable->func(padapter, precv_frame); - } return _SUCCESS; - } unsigned int DoReserved(struct adapter *padapter, union recv_frame *precv_frame) @@ -1896,7 +1873,6 @@ static struct xmit_frame *_alloc_mgtxmitframe(struct xmit_priv *pxmitpriv, bool exit: return pmgntframe; - } inline struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv) @@ -1949,7 +1925,6 @@ void update_mgntframe_attrib(struct adapter *padapter, struct pkt_attrib *pattri pattrib->retry_ctrl = true; pattrib->mbssid = 0; - } void update_mgntframe_attrib_addr(struct adapter *padapter, struct xmit_frame *pmgntframe) @@ -2139,7 +2114,6 @@ void issue_beacon(struct adapter *padapter, int timeout_ms) } goto _issue_bcn; - } /* below for ad-hoc mode */ @@ -2206,7 +2180,6 @@ void issue_beacon(struct adapter *padapter, int timeout_ms) dump_mgntframe_and_wait(padapter, pmgntframe, timeout_ms); else dump_mgntframe(padapter, pmgntframe); - } void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p_probereq) @@ -2388,7 +2361,6 @@ void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p pframe = rtw_set_ie(pframe, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (cur_network->supported_rates + 8), &pattrib->pktlen); /* todo:HT for adhoc */ - } pattrib->last_txcmdsz = pattrib->pktlen; @@ -2641,9 +2613,7 @@ void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short pattrib->icv_len = 4; pattrib->pktlen += pattrib->icv_len; - } - } pattrib->last_txcmdsz = pattrib->pktlen; @@ -2737,7 +2707,6 @@ void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_i pframe += (ie_len+2); pattrib->pktlen += (ie_len+2); } - } /* FILL WMM IE */ @@ -2758,7 +2727,6 @@ void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_i if (!pbuf || ie_len == 0) break; } - } if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK) @@ -3576,7 +3544,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) iedata |= BIT(2);/* 20 MHz BSS Width Request */ pframe = rtw_set_ie(pframe, WLAN_EID_BSS_COEX_2040, 1, &iedata, &(pattrib->pktlen)); - } /* */ @@ -3614,7 +3581,6 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) if (ICS[0][0] == 0) ICS[0][0] = 1; } - } spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); @@ -3641,9 +3607,7 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter) } pframe = rtw_set_ie(pframe, WLAN_EID_BSS_INTOLERANT_CHL_REPORT, k, InfoContent, &(pattrib->pktlen)); - } - } pattrib->last_txcmdsz = pattrib->pktlen; @@ -3682,13 +3646,11 @@ unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr) issue_action_BA(padapter, addr, WLAN_ACTION_DELBA, (((tid << 1) | initiator)&0x1F)); psta->htpriv.agg_enable_bitmap &= ~BIT(tid); psta->htpriv.candidate_tid_bitmap &= ~BIT(tid); - } } } return _SUCCESS; - } unsigned int send_beacon(struct adapter *padapter) @@ -3779,7 +3741,6 @@ void site_survey(struct adapter *padapter) set_survey_timer(pmlmeext, channel_scan_time_ms); } else { - /* channel number is 0 or this channel is not valid. */ { @@ -4029,7 +3990,6 @@ void start_create_ibss(struct adapter *padapter) } /* update bc/mc sta_info */ update_bmc_sta(padapter); - } void start_clnt_join(struct adapter *padapter) @@ -4088,7 +4048,6 @@ void start_clnt_join(struct adapter *padapter) } else { return; } - } void start_clnt_auth(struct adapter *padapter) @@ -4111,7 +4070,6 @@ void start_clnt_auth(struct adapter *padapter) issue_auth(padapter, NULL, 0); set_link_timer(pmlmeext, REAUTH_TO); - } void start_clnt_assoc(struct adapter *padapter) @@ -4245,7 +4203,6 @@ static void process_80211d(struct adapter *padapter, struct wlan_bssid_ex *bssid while ((i < MAX_CHANNEL_NUM) && (chplan_sta[i].ChannelNum != 0) && (chplan_sta[i].ChannelNum <= 14)) { - chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum; chplan_new[k].ScanType = SCAN_PASSIVE; i++; @@ -4614,7 +4571,6 @@ void update_sta_info(struct adapter *padapter, struct sta_info *psta) psta->htpriv.sgi_20m = false; psta->htpriv.sgi_40m = false; psta->qos_option = false; - } psta->htpriv.ch_offset = pmlmeext->cur_ch_offset; @@ -4633,7 +4589,6 @@ void update_sta_info(struct adapter *padapter, struct sta_info *psta) spin_lock_bh(&psta->lock); psta->state = _FW_LINKED; spin_unlock_bh(&psta->lock); - } static void rtw_mlmeext_disconnect(struct adapter *padapter) @@ -4679,7 +4634,6 @@ static void rtw_mlmeext_disconnect(struct adapter *padapter) pmlmepriv->link_detect_info.traffic_transition_count = 0; pmlmepriv->link_detect_info.low_power_transition_count = 0; - } void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) @@ -4786,7 +4740,6 @@ void mlmeext_sta_add_event_callback(struct adapter *padapter, struct sta_info *p } pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS; - } join_type = 2; @@ -4828,7 +4781,6 @@ void _linked_info_dump(struct adapter *padapter) struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter); if (padapter->bLinkInfoDump) { - if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE) rtw_hal_get_def_var(padapter, HAL_DEF_UNDERCORATEDSMOOTHEDPWDB, @@ -5055,7 +5007,6 @@ void addba_timer_hdl(struct timer_list *t) if (phtpriv->ht_option && phtpriv->ampdu_enable) { if (phtpriv->candidate_tid_bitmap) phtpriv->candidate_tid_bitmap = 0x0; - } } @@ -5164,11 +5115,9 @@ u8 createbss_hdl(struct adapter *padapter, u8 *pbuf) memcpy(pnetwork->ies, ((struct wlan_bssid_ex *)pbuf)->ies, pnetwork->ie_length); start_create_ibss(padapter); - } return H2C_SUCCESS; - } u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) @@ -5311,7 +5260,6 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) start_clnt_join(padapter); return H2C_SUCCESS; - } u8 disconnect_hdl(struct adapter *padapter, unsigned char *pbuf) @@ -5351,7 +5299,6 @@ static int rtw_scan_ch_decision(struct adapter *padapter, struct rtw_ieee80211_c /* acquire channels from in */ j = 0; for (i = 0; i < in_num; i++) { - set_idx = rtw_ch_set_search_ch(pmlmeext->channel_set, in[i].hw_value); if (in[i].hw_value && !(in[i].flags & RTW_IEEE80211_CHAN_DISABLED) && set_idx >= 0 @@ -5377,7 +5324,6 @@ static int rtw_scan_ch_decision(struct adapter *padapter, struct rtw_ieee80211_c /* if out is empty, use channel_set as default */ if (j == 0) { for (i = 0; i < pmlmeext->max_chan_nums; i++) { - if (j >= out_num) { netdev_dbg(padapter->pnetdev, FUNC_ADPT_FMT " out_num:%u not enough\n", From 9f7da7b1f29d70f33272448ba9be773421be534d Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Mon, 11 May 2026 11:20:38 -0700 Subject: [PATCH 1357/5214] staging: rtl8723bs: remove unnecessary blank lines in rtw_security.c Remove unnecessary blank lines around braces {} to improve readability. This fixes the following checkpatch.pl checks in rtw_security.c: - CHECK: Blank lines aren't necessary after an open brace '{' - CHECK: Blank lines aren't necessary before a close brace '}' Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260511182038.6625-3-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_security.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index 50d8e30f7eda..0cfd7734c174 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -123,7 +123,6 @@ void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe) /* calculate icv and compare the icv */ *((u32 *)crc) = ~crc32_le(~0, payload, length - 4); - } } @@ -198,7 +197,6 @@ void rtw_secgetmic(struct mic_data *pmicdata, u8 *dst) void rtw_seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_code, u8 pri) { - struct mic_data micdata; u8 priority[4] = {0x0, 0x0, 0x0, 0x0}; From 75f9613aef20b2e4d7b2677e71ab4be0bb4ec5be Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Mon, 11 May 2026 22:08:26 -0700 Subject: [PATCH 1358/5214] staging: rtl8723bs: fix unbalanced braces in rtw_recv.c Add missing braces to if/else statements to comply with kernel coding style. This fixes the following checkpatch.pl checks for rtw_recv.c: - CHECK: Unbalanced braces around else statement - CHECK: braces {} should be used on all arms of this statement Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260512050826.3117-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_recv.c | 30 ++++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index e9ec6def7216..86c5e2c4e7dd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -115,9 +115,9 @@ union recv_frame *_rtw_alloc_recvframe(struct __queue *pfree_recv_queue) struct adapter *padapter; struct recv_priv *precvpriv; - if (list_empty(&pfree_recv_queue->queue)) + if (list_empty(&pfree_recv_queue->queue)) { precvframe = NULL; - else { + } else { phead = get_list_head(pfree_recv_queue); plist = get_next(phead); @@ -276,9 +276,9 @@ struct recv_buf *rtw_dequeue_recvbuf(struct __queue *queue) spin_lock_bh(&queue->lock); - if (list_empty(&queue->queue)) + if (list_empty(&queue->queue)) { precvbuf = NULL; - else { + } else { phead = get_list_head(queue); plist = get_next(phead); @@ -471,8 +471,9 @@ static union recv_frame *decryptor(struct adapter *padapter, union recv_frame *p if (res == _FAIL) { rtw_free_recvframe(return_packet, &padapter->recvpriv.free_recv_queue); return_packet = NULL; - } else + } else { prxattrib->bdecrypted = true; + } return return_packet; } @@ -518,9 +519,9 @@ static union recv_frame *portctrl(struct adapter *adapter, union recv_frame *pre memcpy(&be_tmp, ptr, 2); ether_type = ntohs(be_tmp); - if (ether_type == eapol_type) + if (ether_type == eapol_type) { prtnframe = precv_frame; - else { + } else { /* free this frame */ rtw_free_recvframe(precv_frame, &adapter->recvpriv.free_recv_queue); prtnframe = NULL; @@ -539,8 +540,9 @@ static union recv_frame *portctrl(struct adapter *adapter, union recv_frame *pre /* else { */ /* */ } - } else + } else { prtnframe = precv_frame; + } return prtnframe; } @@ -746,8 +748,9 @@ static signed int sta2sta_data_frame(struct adapter *adapter, union recv_frame * memcpy(pattrib->ta, pattrib->src, ETH_ALEN); sta_addr = mybssid; - } else + } else { ret = _FAIL; + } if (bmcast) *psta = rtw_get_bcmc_stainfo(adapter); @@ -1144,10 +1147,12 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re if (type != WIFI_DATA_TYPE) { psta = rtw_get_bcmc_stainfo(padapter); pdefrag_q = &psta->sta_recvpriv.defrag_q; - } else + } else { pdefrag_q = NULL; - } else + } + } else { pdefrag_q = &psta->sta_recvpriv.defrag_q; + } if ((ismfrag == 0) && (fragnum == 0)) prtnframe = precv_frame;/* isn't a fragment frame */ @@ -1530,9 +1535,10 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe) !memcmp(psnap, bridge_tunnel_header, SNAP_SIZE)) { /* remove RFC1042 or Bridge-Tunnel encapsulation and replace EtherType */ bsnaphdr = true; - } else + } else { /* Leave Ethernet header part of hdr and full payload */ bsnaphdr = false; + } rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr ? SNAP_SIZE : 0); len = precvframe->u.hdr.len - rmv_len; From 9893f32976b5dd8ca6ed43a97794531d8d49981e Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Mon, 11 May 2026 22:46:55 -0700 Subject: [PATCH 1359/5214] staging: rtl8723bs: fix unbalanced braces in 3 files Add missing braces to if/else statements to comply with kernel coding style. This fixes the following checkpatch.pl checks: - CHECK: Unbalanced braces around else statement - CHECK: braces {} should be used on all arms of this statement Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260512054655.4800-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_mlme.c | 17 ++++++++++------- drivers/staging/rtl8723bs/core/rtw_xmit.c | 3 ++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 803a608b74fa..a40d6eab7249 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -76,9 +76,9 @@ u16 Address) break; } return rtw_read8(Adapter, EFUSE_CTRL); - } else + } else { return 0xFF; - + } } /* rtw_efuse_read_1_byte */ /* 11/16/2008 MH Read one byte from real Efuse. */ diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 963b53027c68..6be53e6df1f5 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -742,9 +742,9 @@ void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) } else { pmlmepriv->to_join = true; } - } else + } else { rtw_indicate_disconnect(adapter); - + } _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); } } @@ -1554,15 +1554,17 @@ void rtw_mlme_reset_auto_scan_int(struct adapter *adapter) struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - if (pmlmeinfo->VHT_enable) /* disable auto scan when connect to 11AC AP */ + if (pmlmeinfo->VHT_enable) { + /* disable auto scan when connect to 11AC AP */ mlme->auto_scan_int_ms = 0; - else if (adapter->registrypriv.wifi_spec && is_client_associated_to_ap(adapter)) + } else if (adapter->registrypriv.wifi_spec && is_client_associated_to_ap(adapter)) { mlme->auto_scan_int_ms = 60 * 1000; - else if (rtw_chk_roam_flags(adapter, RTW_ROAM_ACTIVE)) { + } else if (rtw_chk_roam_flags(adapter, RTW_ROAM_ACTIVE)) { if (check_fwstate(mlme, WIFI_STATION_STATE) && check_fwstate(mlme, _FW_LINKED)) mlme->auto_scan_int_ms = mlme->roam_scan_int_ms; - } else + } else { mlme->auto_scan_int_ms = 0; /* disabled */ + } } static void rtw_auto_scan_handler(struct adapter *padapter) @@ -2282,9 +2284,10 @@ unsigned int rtw_restructure_ht_ie(struct adapter *padapter, u8 *in_ie, u8 *out_ operation_bw = padapter->mlmeextpriv.cur_bwmode; if (operation_bw > CHANNEL_WIDTH_40) operation_bw = CHANNEL_WIDTH_40; - } else + } else { /* TDLS: TODO 40? */ operation_bw = CHANNEL_WIDTH_40; + } } else { p = rtw_get_ie(in_ie, WLAN_EID_HT_OPERATION, &ielen, in_len); if (p && (ielen == sizeof(struct ieee80211_ht_addt_info))) { diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 458e471535ad..a28830b4f4e7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -2037,8 +2037,9 @@ inline bool xmitframe_hiq_filter(struct xmit_frame *xmitframe) } else if (registry->hiq_filter == RTW_HIQ_FILTER_ALLOW_ALL) allow = true; else if (registry->hiq_filter == RTW_HIQ_FILTER_DENY_ALL) { - } else + } else { WARN_ON(1); + } return allow; } From 2014f38e4afa61e5925c0830fa00ef42be1c7adc Mon Sep 17 00:00:00 2001 From: Ayushman Rout Date: Tue, 12 May 2026 12:01:35 +0530 Subject: [PATCH 1360/5214] staging: rtl8723bs: fix line lengths in rtw_cmd.c Minor Fix in lines exceeding 100 characters Signed-off-by: Ayushman Rout Link: https://patch.msgid.link/20260512063135.2880-1-ayushmanrout27@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index adc151326feb..94a0fb8a52ea 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -184,7 +184,8 @@ int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv) return -ENOMEM; } - pcmdpriv->rsp_buf = pcmdpriv->rsp_allocated_buf + 4 - ((SIZE_PTR)(pcmdpriv->rsp_allocated_buf) & 3); + pcmdpriv->rsp_buf = pcmdpriv->rsp_allocated_buf + 4 - + ((SIZE_PTR)(pcmdpriv->rsp_allocated_buf) & 3); pcmdpriv->cmd_issued_cnt = 0; pcmdpriv->cmd_done_cnt = 0; @@ -735,12 +736,19 @@ u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) if (!pmlmepriv->assoc_by_bssid) memcpy(&pmlmepriv->assoc_bssid[0], &pnetwork->network.mac_address[0], ETH_ALEN); - psecnetwork->ie_length = rtw_restruct_sec_ie(padapter, &pnetwork->network.ies[0], &psecnetwork->ies[0], pnetwork->network.ie_length); + psecnetwork->ie_length = rtw_restruct_sec_ie(padapter, + &pnetwork->network.ies[0], + &psecnetwork->ies[0], + pnetwork->network.ie_length); pqospriv->qos_option = 0; if (pregistrypriv->wmm_enable) { - tmp_len = rtw_restruct_wmm_ie(padapter, &pnetwork->network.ies[0], &psecnetwork->ies[0], pnetwork->network.ie_length, psecnetwork->ie_length); + tmp_len = rtw_restruct_wmm_ie(padapter, + &pnetwork->network.ies[0], + &psecnetwork->ies[0], + pnetwork->network.ie_length, + psecnetwork->ie_length); if (psecnetwork->ie_length != tmp_len) { psecnetwork->ie_length = tmp_len; From ed2b7541e7db47e589be6d20d8e6e9a9620addac Mon Sep 17 00:00:00 2001 From: Ayushman Rout Date: Tue, 12 May 2026 16:15:39 +0530 Subject: [PATCH 1361/5214] staging: rtl8723bs: fix CamelCase of DelayLPSLastTimeStamp Rename CamelCase variable DelayLPSLastTimeStamp to delay_lps_last_time_stamp across rtw_cmd.c, rtw_pwrctrl.c and rtw_pwrctrl.h. Signed-off-by: Ayushman Rout Link: https://patch.msgid.link/20260512104539.18629-1-ayushmanrout27@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_pwrctrl.c | 2 +- drivers/staging/rtl8723bs/include/rtw_pwrctrl.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 94a0fb8a52ea..f01b0a221b6a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -1308,7 +1308,7 @@ void lps_ctrl_wk_hdl(struct adapter *padapter, u8 lps_ctrl_type) rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus)); break; case LPS_CTRL_SPECIAL_PACKET: - pwrpriv->DelayLPSLastTimeStamp = jiffies; + pwrpriv->delay_lps_last_time_stamp = jiffies; hal_btcoex_SpecialPacketNotify(padapter, PACKET_DHCP); LPS_Leave(padapter, "LPS_CTRL_SPECIAL_PACKET"); break; diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c index ca1cb58fc801..b9f8cf1014ed 100644 --- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c @@ -302,7 +302,7 @@ static u8 PS_RDY_CHECK(struct adapter *padapter) curr_time = jiffies; - delta_time = curr_time - pwrpriv->DelayLPSLastTimeStamp; + delta_time = curr_time - pwrpriv->delay_lps_last_time_stamp; if (delta_time < LPS_DELAY_TIME) return false; diff --git a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h index 67dc74512e8f..6977892a5742 100644 --- a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h +++ b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h @@ -169,7 +169,7 @@ struct pwrctrl_priv { u8 power_mgnt; u8 org_power_mgnt; bool fw_current_in_ps_mode; - unsigned long DelayLPSLastTimeStamp; + unsigned long delay_lps_last_time_stamp; s32 pnp_current_pwr_state; u8 pnp_bstop_trx; From ab6148c24213771b86444f1e224c1f28946c6b73 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 12 May 2026 15:55:31 +0300 Subject: [PATCH 1362/5214] staging: rtl8723bs: remove unused SysIntrMask from struct hal_com_data This field is set to 0 once, and its use becomes optional. Remove it to clean up dead code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260512125703.6878-2-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_ops.c | 4 +--- drivers/staging/rtl8723bs/include/hal_data.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 5d74913be573..0c0c243c46eb 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -592,8 +592,6 @@ void InitSysInterrupt8723BSdio(struct adapter *adapter) struct hal_com_data *haldata; haldata = GET_HAL_DATA(adapter); - - haldata->SysIntrMask = (0); } /* @@ -616,7 +614,7 @@ void rtw_sdio_enable_interrupt(struct adapter *adapter) /* Update current system IMR settings */ tmp = rtw_read32(adapter, REG_HSIMR); - rtw_write32(adapter, REG_HSIMR, tmp | haldata->SysIntrMask); + rtw_write32(adapter, REG_HSIMR, tmp); /* */ /* There are some C2H CMDs have been sent before system interrupt is enabled, e.g., C2H, CPWM. */ diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index bc9d8c945f38..d3e5c4039693 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -389,7 +389,6 @@ struct hal_com_data { /* Interrupt related register information. */ u32 SysIntrStatus; - u32 SysIntrMask; /* Chip version information */ bool chip_normal; /* true - normal chip, false - test chip */ From 591e09e74971bbbba5dd825fd8130e98af8e12bd Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 12 May 2026 15:55:32 +0300 Subject: [PATCH 1363/5214] staging: rtl8723bs: remove empty InitSysInterrupt8723BSdio() This function does not do any useful work to initialize interrupt, so remove it co clean up dead code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260512125703.6878-3-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_halinit.c | 5 ----- drivers/staging/rtl8723bs/hal/sdio_ops.c | 13 ------------- drivers/staging/rtl8723bs/include/sdio_ops.h | 1 - 3 files changed, 19 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index 1e0498268aee..eecfc593e2dd 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -532,11 +532,6 @@ static void _InitInterrupt(struct adapter *padapter) /* Initialize and enable SDIO Host Interrupt. */ /* */ InitInterrupt8723BSdio(padapter); - - /* */ - /* Initialize system Host Interrupt. */ - /* */ - InitSysInterrupt8723BSdio(padapter); } static void _InitRFType(struct adapter *padapter) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 0c0c243c46eb..f7f6e273c868 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -581,19 +581,6 @@ void InitInterrupt8723BSdio(struct adapter *adapter) 0); } -/* */ -/* Description: */ -/* Initialize System Host Interrupt Mask configuration variables for future use. */ -/* */ -/* Created by Roger, 2011.08.03. */ -/* */ -void InitSysInterrupt8723BSdio(struct adapter *adapter) -{ - struct hal_com_data *haldata; - - haldata = GET_HAL_DATA(adapter); -} - /* * Enable SDIO Host Interrupt Mask configuration on SDIO local domain. * diff --git a/drivers/staging/rtl8723bs/include/sdio_ops.h b/drivers/staging/rtl8723bs/include/sdio_ops.h index 13f13076bc16..252b0e1646e5 100644 --- a/drivers/staging/rtl8723bs/include/sdio_ops.h +++ b/drivers/staging/rtl8723bs/include/sdio_ops.h @@ -26,7 +26,6 @@ extern void sd_int_hdl(struct adapter *padapter); extern u8 CheckIPSStatus(struct adapter *padapter); extern void InitInterrupt8723BSdio(struct adapter *padapter); -extern void InitSysInterrupt8723BSdio(struct adapter *padapter); void rtw_sdio_enable_interrupt(struct adapter *padapter); void rtw_sdio_disable_interrupt(struct adapter *padapter); extern u8 HalQueryTxBufferStatus8723BSdio(struct adapter *padapter); From a00e23438d608c9e9710781f69cbb447672a50f1 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 12 May 2026 15:55:33 +0300 Subject: [PATCH 1364/5214] staging: rtl8723bs: remove overwriting of current IMR settings Originally, this code use to Step 1: Read a value from register Step 2: Add some bits to the value Step 3: Write the result back to the same register The problem was that the bits in Step 2 were always zero and didn't change the value, so I have removed that code. Now this function just reads a value and writes the same value back. It is unnecessary and can be removed. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260512125703.6878-4-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/sdio_ops.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index f7f6e273c868..91580f6ba52e 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -592,17 +592,12 @@ void rtw_sdio_enable_interrupt(struct adapter *adapter) { struct hal_com_data *haldata; __le32 himr; - u32 tmp; haldata = GET_HAL_DATA(adapter); himr = cpu_to_le32(haldata->sdio_himr); sdio_local_write(adapter, SDIO_REG_HIMR, 4, (u8 *)&himr); - /* Update current system IMR settings */ - tmp = rtw_read32(adapter, REG_HSIMR); - rtw_write32(adapter, REG_HSIMR, tmp); - /* */ /* There are some C2H CMDs have been sent before system interrupt is enabled, e.g., C2H, CPWM. */ /* So we need to clear all C2H events that FW has notified, otherwise FW won't schedule any commands anymore. */ From 6ddafcd8c8d028c23ecbda6b08db33868f65eaf1 Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 12 May 2026 15:55:34 +0300 Subject: [PATCH 1365/5214] staging: rtl8723bs: remove unused SysIntrStatus from struct hal_com_data This field has not been used anywhere since the driver was added, so remove it to eliminate dead code. Signed-off-by: Nikolay Kulikov Link: https://patch.msgid.link/20260512125703.6878-5-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/hal_data.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index d3e5c4039693..3a93ac312ded 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -387,9 +387,6 @@ struct hal_com_data { /* For bluetooth co-existence */ struct bt_coexist bt_coexist; - /* Interrupt related register information. */ - u32 SysIntrStatus; - /* Chip version information */ bool chip_normal; /* true - normal chip, false - test chip */ }; From 632b69132cd55e17e3efc84b9be947e8c59c8a43 Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Sun, 17 May 2026 09:28:02 +0200 Subject: [PATCH 1366/5214] staging: rtl8723bs: remove READ_AND_CONFIG_MP macro Remove the READ_AND_CONFIG_MP macro from odm_HWConfig.c and call the ODM_ReadAndConfig_MP_* functions directly to reduce code complexity and improve readability. Signed-off-by: Michael Straube Reviewed-by: Dan Carpenter Link: https://patch.msgid.link/20260517072802.71149-1-straube.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm_HWConfig.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c index 4784d33eab0e..937002495d09 100644 --- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c +++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c @@ -7,9 +7,6 @@ #include "odm_precomp.h" -#define READ_AND_CONFIG_MP(ic, txt) (ODM_ReadAndConfig_MP_##ic##txt(pDM_Odm)) -#define READ_AND_CONFIG READ_AND_CONFIG_MP - static u8 odm_query_rx_pwr_percentage(s8 ant_power) { if ((ant_power <= -100) || (ant_power >= 20)) @@ -415,9 +412,9 @@ enum hal_status ODM_ConfigRFWithHeaderFile( ) { if (ConfigType == CONFIG_RF_RADIO) - READ_AND_CONFIG(8723B, _RadioA); + ODM_ReadAndConfig_MP_8723B_RadioA(pDM_Odm); else if (ConfigType == CONFIG_RF_TXPWR_LMT) - READ_AND_CONFIG(8723B, _TXPWR_LMT); + ODM_ReadAndConfig_MP_8723B_TXPWR_LMT(pDM_Odm); return HAL_STATUS_SUCCESS; } @@ -425,7 +422,7 @@ enum hal_status ODM_ConfigRFWithHeaderFile( enum hal_status ODM_ConfigRFWithTxPwrTrackHeaderFile(struct dm_odm_t *pDM_Odm) { if (pDM_Odm->SupportInterface == ODM_ITRF_SDIO) - READ_AND_CONFIG(8723B, _TxPowerTrack_SDIO); + ODM_ReadAndConfig_MP_8723B_TxPowerTrack_SDIO(pDM_Odm); return HAL_STATUS_SUCCESS; } @@ -435,11 +432,11 @@ enum hal_status ODM_ConfigBBWithHeaderFile( ) { if (ConfigType == CONFIG_BB_PHY_REG) - READ_AND_CONFIG(8723B, _PHY_REG); + ODM_ReadAndConfig_MP_8723B_PHY_REG(pDM_Odm); else if (ConfigType == CONFIG_BB_AGC_TAB) - READ_AND_CONFIG(8723B, _AGC_TAB); + ODM_ReadAndConfig_MP_8723B_AGC_TAB(pDM_Odm); else if (ConfigType == CONFIG_BB_PHY_REG_PG) - READ_AND_CONFIG(8723B, _PHY_REG_PG); + ODM_ReadAndConfig_MP_8723B_PHY_REG_PG(pDM_Odm); return HAL_STATUS_SUCCESS; } From 7cc812e634bdea9958606eaa9d5ba3e5ec52e6b7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:38 +0900 Subject: [PATCH 1367/5214] firewire: core: minor code refactoring for case-dependent parameters of iso resources management The generation parameter is specific to the auto case of iso resources management, while it is in the common parameter structure. Move the generation member to the structure specific to auto case. Link: https://lore.kernel.org/r/20260520130840.629934-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c166e7617d2a..c669c9e42d34 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -129,7 +129,6 @@ struct descriptor_resource { }; struct iso_resource_params { - int generation; u64 channels; s32 bandwidth; }; @@ -144,6 +143,7 @@ struct iso_resource_auto { ISO_RES_AUTO_REALLOC, ISO_RES_AUTO_DEALLOC, } todo; + int generation; struct iso_resource_params params; struct iso_resource_event *e_alloc, *e_dealloc; }; @@ -1316,7 +1316,6 @@ static int fill_iso_resource_params(struct iso_resource_params *params, request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) return -EINVAL; - params->generation = -1; params->channels = request->channels; params->bandwidth = request->bandwidth; @@ -1336,8 +1335,8 @@ static void iso_resource_auto_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) { reset_jiffies = client->device->card->reset_jiffies; current_generation = client->device->generation; - resource_generation = r->params.generation; - r->params.generation = current_generation; + resource_generation = r->generation; + r->generation = current_generation; todo = r->todo; } @@ -1495,7 +1494,6 @@ static void iso_resource_once_work(struct work_struct *work) scoped_guard(spinlock_irq, &client->lock) generation = client->device->generation; - r->params.generation = generation; bandwidth = r->params.bandwidth; fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, From 9e38ee1c5522b1b6ba62e0766202bcc9979d6ae3 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:39 +0900 Subject: [PATCH 1368/5214] firewire: core: rename member name for channel mask of isoc resource The iso_resource_params structure has a member for channel mask, while the name of field is easy to misinterpret. Append _mask to the member name. Link: https://lore.kernel.org/r/20260520130840.629934-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c669c9e42d34..56c21cabc20c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -129,7 +129,7 @@ struct descriptor_resource { }; struct iso_resource_params { - u64 channels; + u64 channels_mask; s32 bandwidth; }; @@ -1316,7 +1316,7 @@ static int fill_iso_resource_params(struct iso_resource_params *params, request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) return -EINVAL; - params->channels = request->channels; + params->channels_mask = request->channels; params->bandwidth = request->bandwidth; return 0; @@ -1360,7 +1360,7 @@ static void iso_resource_auto_work(struct work_struct *work) bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, current_generation, r->params.channels, + fw_iso_resource_manage(client->device->card, current_generation, r->params.channels_mask, &channel, &bandwidth, todo != ISO_RES_AUTO_DEALLOC); if (todo == ISO_RES_AUTO_DEALLOC) { @@ -1402,7 +1402,7 @@ static void iso_resource_auto_work(struct work_struct *work) r->todo = ISO_RES_AUTO_REALLOC; if (channel >= 0) - r->params.channels = 1ULL << channel; + r->params.channels_mask = BIT_ULL(channel); e = r->e_alloc; r->e_alloc = NULL; @@ -1496,7 +1496,7 @@ static void iso_resource_once_work(struct work_struct *work) bandwidth = r->params.bandwidth; - fw_iso_resource_manage(client->device->card, generation, r->params.channels, &channel, + fw_iso_resource_manage(client->device->card, generation, r->params.channels_mask, &channel, &bandwidth, r->todo == ISO_RES_ONCE_ALLOC); e->iso_resource.handle = UNAVAILABLE_HANDLE; From 21e163988c87219b3973efe9ca934b7acf0e4fe8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 20 May 2026 22:08:40 +0900 Subject: [PATCH 1369/5214] firewire: core: cancel using delayed work for iso_resource_once management There is no need to use deferrable type of work for iso_resource_once management because the work is queued to run immediately. Link: https://lore.kernel.org/r/20260520130840.629934-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-cdev.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 56c21cabc20c..e49d8a58be09 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -150,8 +150,7 @@ struct iso_resource_auto { struct iso_resource_once { struct client *client; - // Schedule work and access todo only with client->lock held. - struct delayed_work work; + struct work_struct work; enum { ISO_RES_ONCE_ALLOC, ISO_RES_ONCE_DEALLOC, @@ -1486,7 +1485,7 @@ static int ioctl_deallocate_iso_resource(struct client *client, static void iso_resource_once_work(struct work_struct *work) { - struct iso_resource_once *r = from_work(r, work, work.work); + struct iso_resource_once *r = from_work(r, work, work); struct client *client = r->client; struct iso_resource_event *e = r->event; int generation, channel, bandwidth; @@ -1505,7 +1504,7 @@ static void iso_resource_once_work(struct work_struct *work) queue_event(client, &e->event, &e->iso_resource, sizeof(e->iso_resource), NULL, 0); - cancel_delayed_work(&r->work); + cancel_work(&r->work); kfree(r); client_put(client); @@ -1525,7 +1524,7 @@ static int init_iso_resource_once(struct client *client, if (err < 0) return err; - INIT_DELAYED_WORK(&r->work, iso_resource_once_work); + INIT_WORK(&r->work, iso_resource_once_work); r->client = client; r->todo = todo; @@ -1539,7 +1538,7 @@ static int init_iso_resource_once(struct client *client, // Keep the client until work item finishing. client_get(r->client); - queue_delayed_work(fw_workqueue, &no_free_ptr(r)->work, 0); + queue_work(fw_workqueue, &no_free_ptr(r)->work); request->handle = UNAVAILABLE_HANDLE; From 44730eac1778c72d3667ff5372f254056f542da8 Mon Sep 17 00:00:00 2001 From: Xukai Wang Date: Sat, 25 Apr 2026 17:29:31 +0800 Subject: [PATCH 1370/5214] dt-bindings: clock: Add Canaan K230 clock controller This patch adds the Device Tree binding for the clock controller on Canaan k230. The binding defines the clocks and the required properties to configure them correctly. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Xukai Wang Signed-off-by: Conor Dooley --- .../bindings/clock/canaan,k230-clk.yaml | 59 +++++ include/dt-bindings/clock/canaan,k230-clk.h | 220 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml create mode 100644 include/dt-bindings/clock/canaan,k230-clk.h diff --git a/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml b/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml new file mode 100644 index 000000000000..34c93cb5db40 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/canaan,k230-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan Kendryte K230 Clock + +maintainers: + - Xukai Wang + +description: + The Canaan K230 clock controller generates various clocks for SoC + peripherals. See include/dt-bindings/clock/canaan,k230-clk.h for + valid clock IDs. + +properties: + compatible: + const: canaan,k230-clk + + reg: + items: + - description: PLL control registers + - description: Sysclk control registers + + clocks: + items: + - description: Main external reference clock + - description: + External clock which used as the pulse input + for the timer to provide timing signals. + + clock-names: + items: + - const: osc24m + - const: timer-pulse-in + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@91102000 { + compatible = "canaan,k230-clk"; + reg = <0x91102000 0x40>, + <0x91100000 0x108>; + clocks = <&osc24m>, <&timerx_pulse_in>; + clock-names = "osc24m", "timer-pulse-in"; + #clock-cells = <1>; + }; diff --git a/include/dt-bindings/clock/canaan,k230-clk.h b/include/dt-bindings/clock/canaan,k230-clk.h new file mode 100644 index 000000000000..3b916678cc5b --- /dev/null +++ b/include/dt-bindings/clock/canaan,k230-clk.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Kendryte Canaan K230 Clock Drivers + * + * Author: Xukai Wang + */ + +#ifndef __DT_BINDINGS_CANAAN_K230_CLOCK_H__ +#define __DT_BINDINGS_CANAAN_K230_CLOCK_H__ + +#define K230_CPU0_SRC_GATE 0 +#define K230_CPU0_PLIC_GATE 1 +#define K230_CPU0_NOC_DDRCP4_GATE 2 +#define K230_CPU0_APB_GATE 3 +#define K230_CPU0_SRC_RATE 4 +#define K230_CPU0_AXI_RATE 5 +#define K230_CPU0_PLIC_RATE 6 +#define K230_CPU0_APB_RATE 7 +#define K230_HS_SSI0_MUX 8 +#define K230_HS_USB_REF_MUX 9 +#define K230_HS_HCLK_HIGH_GATE 10 +#define K230_HS_HCLK_GATE 11 +#define K230_HS_SD0_AHB_GATE 12 +#define K230_HS_SD1_AHB_GATE 13 +#define K230_HS_SSI1_AHB_GATE 14 +#define K230_HS_SSI2_AHB_GATE 15 +#define K230_HS_USB0_AHB_GATE 16 +#define K230_HS_USB1_AHB_GATE 17 +#define K230_HS_SSI0_AXI_GATE 18 +#define K230_HS_SSI1_GATE 19 +#define K230_HS_SSI2_GATE 20 +#define K230_HS_QSPI_AXI_SRC_GATE 21 +#define K230_HS_SSI1_AXI_GATE 22 +#define K230_HS_SSI2_AXI_GATE 23 +#define K230_HS_SD_CARD_SRC_GATE 24 +#define K230_HS_SD0_CARD_GATE 25 +#define K230_HS_SD1_CARD_GATE 26 +#define K230_HS_SD_AXI_SRC_GATE 27 +#define K230_HS_SD0_AXI_GATE 28 +#define K230_HS_SD1_AXI_GATE 29 +#define K230_HS_SD0_BASE_GATE 30 +#define K230_HS_SD1_BASE_GATE 31 +#define K230_HS_SSI0_GATE 32 +#define K230_HS_SD_TIMER_SRC_GATE 33 +#define K230_HS_SD0_TIMER_GATE 34 +#define K230_HS_SD1_TIMER_GATE 35 +#define K230_HS_USB0_REF_GATE 36 +#define K230_HS_USB1_REF_GATE 37 +#define K230_HS_HCLK_HIGH_RATE 38 +#define K230_HS_HCLK_RATE 39 +#define K230_HS_SSI0_AXI_RATE 40 +#define K230_HS_SSI1_RATE 41 +#define K230_HS_SSI2_RATE 42 +#define K230_HS_QSPI_AXI_SRC_RATE 43 +#define K230_HS_SD_CARD_SRC_RATE 44 +#define K230_HS_SD_AXI_SRC_RATE 45 +#define K230_HS_USB_REF_50M_RATE 46 +#define K230_HS_SD_TIMER_SRC_RATE 47 +#define K230_TIMER0_MUX 48 +#define K230_TIMER1_MUX 49 +#define K230_TIMER2_MUX 50 +#define K230_TIMER3_MUX 51 +#define K230_TIMER4_MUX 52 +#define K230_TIMER5_MUX 53 +#define K230_SHRM_SRAM_MUX 54 +#define K230_DDRC_SRC_MUX 55 +#define K230_AI_SRC_MUX 56 +#define K230_CAMERA0_MUX 57 +#define K230_CAMERA1_MUX 58 +#define K230_CAMERA2_MUX 59 +#define K230_CPU1_SRC_MUX 60 +#define K230_CPU1_SRC_GATE 61 +#define K230_CPU1_PLIC_GATE 62 +#define K230_CPU1_APB_GATE 63 +#define K230_CPU1_SRC_RATE 64 +#define K230_CPU1_AXI_RATE 65 +#define K230_CPU1_PLIC_RATE 66 +#define K230_PMU_APB_GATE 67 +#define K230_LS_APB_SRC_GATE 68 +#define K230_LS_UART0_APB_GATE 69 +#define K230_LS_UART1_APB_GATE 70 +#define K230_LS_UART2_APB_GATE 71 +#define K230_LS_UART3_APB_GATE 72 +#define K230_LS_UART4_APB_GATE 73 +#define K230_LS_I2C0_APB_GATE 74 +#define K230_LS_I2C1_APB_GATE 75 +#define K230_LS_I2C2_APB_GATE 76 +#define K230_LS_I2C3_APB_GATE 77 +#define K230_LS_I2C4_APB_GATE 78 +#define K230_LS_GPIO_APB_GATE 79 +#define K230_LS_PWM_APB_GATE 80 +#define K230_LS_JAMLINK0_APB_GATE 81 +#define K230_LS_JAMLINK1_APB_GATE 82 +#define K230_LS_JAMLINK2_APB_GATE 83 +#define K230_LS_JAMLINK3_APB_GATE 84 +#define K230_LS_AUDIO_APB_GATE 85 +#define K230_LS_ADC_APB_GATE 86 +#define K230_LS_CODEC_APB_GATE 87 +#define K230_LS_I2C0_GATE 88 +#define K230_LS_I2C1_GATE 89 +#define K230_LS_I2C2_GATE 90 +#define K230_LS_I2C3_GATE 91 +#define K230_LS_I2C4_GATE 92 +#define K230_LS_CODEC_ADC_GATE 93 +#define K230_LS_CODEC_DAC_GATE 94 +#define K230_LS_AUDIO_DEV_GATE 95 +#define K230_LS_PDM_GATE 96 +#define K230_LS_ADC_GATE 97 +#define K230_LS_UART0_GATE 98 +#define K230_LS_UART1_GATE 99 +#define K230_LS_UART2_GATE 100 +#define K230_LS_UART3_GATE 101 +#define K230_LS_UART4_GATE 102 +#define K230_LS_JAMLINK0CO_GATE 103 +#define K230_LS_JAMLINK1CO_GATE 104 +#define K230_LS_JAMLINK2CO_GATE 105 +#define K230_LS_JAMLINK3CO_GATE 106 +#define K230_LS_GPIO_DEBOUNCE_GATE 107 +#define K230_SYSCTL_WDT0_APB_GATE 108 +#define K230_SYSCTL_WDT1_APB_GATE 109 +#define K230_SYSCTL_TIMER_APB_GATE 110 +#define K230_SYSCTL_IOMUX_APB_GATE 111 +#define K230_SYSCTL_MAILBOX_APB_GATE 112 +#define K230_SYSCTL_HDI_GATE 113 +#define K230_SYSCTL_TIME_STAMP_GATE 114 +#define K230_SYSCTL_WDT0_GATE 115 +#define K230_SYSCTL_WDT1_GATE 116 +#define K230_TIMER0_GATE 117 +#define K230_TIMER1_GATE 118 +#define K230_TIMER2_GATE 119 +#define K230_TIMER3_GATE 120 +#define K230_TIMER4_GATE 121 +#define K230_TIMER5_GATE 122 +#define K230_SHRM_APB_GATE 123 +#define K230_SHRM_AXI_GATE 124 +#define K230_SHRM_AXI_SLAVE_GATE 125 +#define K230_SHRM_NONAI2D_AXI_GATE 126 +#define K230_SHRM_SRAM_GATE 127 +#define K230_SHRM_DECOMPRESS_AXI_GATE 128 +#define K230_SHRM_SDMA_AXI_GATE 129 +#define K230_SHRM_PDMA_AXI_GATE 130 +#define K230_DDRC_SRC_GATE 131 +#define K230_DDRC_BYPASS_GATE 132 +#define K230_DDRC_APB_GATE 133 +#define K230_DISPLAY_AHB_GATE 134 +#define K230_DISPLAY_AXI_GATE 135 +#define K230_DISPLAY_GPU_GATE 136 +#define K230_DISPLAY_DPIP_GATE 137 +#define K230_DISPLAY_CFG_GATE 138 +#define K230_DISPLAY_REF_GATE 139 +#define K230_USB_480M_GATE 140 +#define K230_USB_100M_GATE 141 +#define K230_DPHY_DFT_GATE 142 +#define K230_SPI2AXI_GATE 143 +#define K230_AI_SRC_GATE 144 +#define K230_AI_AXI_GATE 145 +#define K230_AI_SRC_RATE 146 +#define K230_CAMERA0_GATE 147 +#define K230_CAMERA1_GATE 148 +#define K230_CAMERA2_GATE 149 +#define K230_LS_APB_SRC_RATE 150 +#define K230_LS_I2C0_RATE 151 +#define K230_LS_I2C1_RATE 152 +#define K230_LS_I2C2_RATE 153 +#define K230_LS_I2C3_RATE 154 +#define K230_LS_I2C4_RATE 155 +#define K230_LS_CODEC_ADC_RATE 156 +#define K230_LS_CODEC_DAC_RATE 157 +#define K230_LS_AUDIO_DEV_RATE 158 +#define K230_LS_PDM_RATE 159 +#define K230_LS_ADC_RATE 160 +#define K230_LS_UART0_RATE 161 +#define K230_LS_UART1_RATE 162 +#define K230_LS_UART2_RATE 163 +#define K230_LS_UART3_RATE 164 +#define K230_LS_UART4_RATE 165 +#define K230_LS_JAMLINKCO_SRC_RATE 166 +#define K230_LS_GPIO_DEBOUNCE_RATE 167 +#define K230_SYSCTL_HDI_RATE 168 +#define K230_SYSCTL_TIME_STAMP_RATE 169 +#define K230_SYSCTL_TEMP_SENSOR_RATE 170 +#define K230_SYSCTL_WDT0_RATE 171 +#define K230_SYSCTL_WDT1_RATE 172 +#define K230_TIMER0_SRC_RATE 173 +#define K230_TIMER1_SRC_RATE 174 +#define K230_TIMER2_SRC_RATE 175 +#define K230_TIMER3_SRC_RATE 176 +#define K230_TIMER4_SRC_RATE 177 +#define K230_TIMER5_SRC_RATE 178 +#define K230_SHRM_APB_RATE 179 +#define K230_DDRC_SRC_RATE 180 +#define K230_DDRC_APB_RATE 181 +#define K230_DISPLAY_AHB_RATE 182 +#define K230_DISPLAY_CLKEXT_RATE 183 +#define K230_DISPLAY_GPU_RATE 184 +#define K230_DISPLAY_DPIP_RATE 185 +#define K230_DISPLAY_CFG_RATE 186 +#define K230_VPU_SRC_GATE 187 +#define K230_VPU_AXI_GATE 188 +#define K230_VPU_DDRCP2_GATE 189 +#define K230_VPU_CFG_GATE 190 +#define K230_VPU_SRC_RATE 191 +#define K230_VPU_AXI_SRC_RATE 192 +#define K230_VPU_CFG_RATE 193 +#define K230_SEC_APB_GATE 194 +#define K230_SEC_FIX_GATE 195 +#define K230_SEC_AXI_GATE 196 +#define K230_SEC_APB_RATE 197 +#define K230_SEC_FIX_RATE 198 +#define K230_SEC_AXI_RATE 199 +#define K230_USB_480M_RATE 200 +#define K230_USB_100M_RATE 201 +#define K230_DPHY_DFT_RATE 202 +#define K230_SPI2AXI_RATE 203 +#define K230_CAMERA0_RATE 204 +#define K230_CAMERA1_RATE 205 +#define K230_CAMERA2_RATE 206 +#define K230_SHRM_SRAM_DIV2 207 + +#endif /* __DT_BINDINGS_CANAAN_K230_CLOCK_H__ */ From 4abd2f68950a296b6d547376d7d0f8b351e72f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:06 +0200 Subject: [PATCH 1371/5214] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new compatible for the RGA3 (Raster Graphic Acceleration 3) peripheral found on the RK3588 SoC. Also specify an iommu property, as the RGA3 contains the generic rockchip iommu. While other versions also have an iommu, it's usually specific to them. The RK3588 contains one RGA2-Enhance core (also contained on the RK3399) and two RGA3 cores. Both feature a similar functionality of scaling, cropping and rotating of up to two input images into one output image. Key differences of the RGA3 are: - supports 10bit YUV output formats - supports 8x8 tiles and FBCD as inputs and outputs - supports BT2020 color space conversion - max output resolution of (8192-64)x(8192-64) - MMU can map up to 32G DDR RAM - fully planar formats (3 planes) are not supported - max scale up/down factor of 8 (RGA2 allows up to 16) Acked-by: Rob Herring (Arm) Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/rockchip-rga.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml index ac17cda65191..7bd92f733666 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml @@ -9,7 +9,11 @@ title: Rockchip 2D raster graphic acceleration controller (RGA) description: RGA is a standalone 2D raster graphic acceleration unit. It accelerates 2D graphics operations, such as point/line drawing, image scaling, rotation, - BitBLT, alpha blending and image blur/sharpness. + BitBLT, alpha blending and image blur/sharpness. There exist many versions + of this unit that differ in the supported inputs/output formats, + the attached IOMMU and the supported operations on the input. As some SoCs + include multiple RGA units with different versions, a more specific + compatible name to differentiate the concrete unit is used for them. maintainers: - Jacob Chen @@ -20,6 +24,7 @@ properties: oneOf: - const: rockchip,rk3288-rga - const: rockchip,rk3399-rga + - const: rockchip,rk3588-rga3 - items: - enum: - rockchip,rk3228-rga @@ -45,6 +50,9 @@ properties: power-domains: maxItems: 1 + iommus: + maxItems: 1 + resets: maxItems: 3 From 3bb5a7d25a2bdc6ad22af404fb6f2cca01e96aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:07 +0200 Subject: [PATCH 1372/5214] media: v4l2-common: sort RGB formats in v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the RGB formats in v4l2_format_info to match the format definitions in include/uapi/linux/videodev2.h . Also introduce the same sections to partition the list of formats and align the format info in each section. The alignment of the 1 or 2 bytes RGB formats contains an additional space in preparation of adding the missing formats to the list, as for V4L2_PIX_FMT_ARGB555X an additional space is necessary. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 54 +++++++++++++++------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index bceafc4e92c8..f6cb7e14f223 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -245,33 +245,39 @@ EXPORT_SYMBOL_GPL(v4l2_s_parm_cap); const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { - /* RGB formats */ - { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* RGB formats (1 or 2 bytes per pixel) */ + { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* RGB formats (3 or 4 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* RGB formats (6 or 8 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* HSV formats */ + { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, From e623f2b7397079005e940d101b80796b3c39d229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:08 +0200 Subject: [PATCH 1373/5214] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add all missing one and two byte RGB formats to v4l2_format_info. This allows drivers to more consistently use v4l2_format_info, as it now covers all currently defined RGB formats. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index f6cb7e14f223..b55ab72958ea 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -246,8 +246,29 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { /* RGB formats (1 or 2 bytes per pixel) */ + { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, /* RGB formats (3 or 4 bytes per pixel) */ From 84fbe791a6203e35a602f7fef94d967466cd8d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:09 +0200 Subject: [PATCH 1374/5214] media: v4l2-common: add has_alpha to v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a has_alpha value to the v4l2_format_info struct to indicate if the format contains an alpha component. This information can currently not be queried in a generic way, but might be useful for potential drivers to properly setup alpha blending to copy or set the alpha value. The implementation is based on the drm_format_info implementation. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 32 +++++++++++++-------------- include/media/v4l2-common.h | 2 ++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index b55ab72958ea..3cc8b04e1ea6 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -248,26 +248,26 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) /* RGB formats (1 or 2 bytes per pixel) */ { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, @@ -276,24 +276,24 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, /* RGB formats (6 or 8 bytes per pixel) */ { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, /* HSV formats */ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index f8b1faced79c..401d8506c24b 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -520,6 +520,7 @@ enum v4l2_pixel_encoding { * @vdiv: Vertical chroma subsampling factor * @block_w: Per-plane macroblock pixel width (optional) * @block_h: Per-plane macroblock pixel height (optional) + * @has_alpha: Does the format embeds an alpha component? */ struct v4l2_format_info { u32 format; @@ -532,6 +533,7 @@ struct v4l2_format_info { u8 vdiv; u8 block_w[4]; u8 block_h[4]; + bool has_alpha; }; static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f) From 2c225846271fc6b4f7e2a34271ebe828ed461772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:10 +0200 Subject: [PATCH 1375/5214] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a v4l2_fill_pixfmt_mp_aligned helper which allows the user to specify a custom stride alignment in bytes. This is necessary for hardware like the Rockchip RGA3, which requires the stride value to be aligned to a 16 bytes boundary. The code makes some assumptions about the v4l2 format to simplify the calculation. They currently hold for all known v4l2 formats. v4l2_format_plane_stride uses an unsigned int as argument type to avoid the later multiplication from overflowing the u8 value. All other places use u8, as no practical use cases for a larger alignment are known at the moment. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 56 +++++++++++++++++++++------ include/media/v4l2-common.h | 4 ++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 3cc8b04e1ea6..65db7340ad38 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -432,14 +432,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane, - unsigned int width) + unsigned int width, unsigned int byte_alignment) { unsigned int hdiv = plane ? info->hdiv : 1; unsigned int aligned_width = ALIGN(width, v4l2_format_block_width(info, plane)); - return DIV_ROUND_UP(aligned_width, hdiv) * - info->bpp[plane] / info->bpp_div[plane]; + /* + * Formats with a single memory plane derive the stride of the + * other planes from the y stride. To avoid hardware or software + * deriving a different stride for the composite plane, + * multiply the alignment accordingly. + * + * It assumes the following format properties: + * - bpp_div[0] == bpp_div[1] + * - The multiplication factor doesn't differ between the non y planes + * - The multiplication factor is a power of 2 + */ + if (info->mem_planes == 1 && info->comp_planes > 1) { + if (plane == 0) + byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]); + else + byte_alignment *= DIV_ROUND_UP(info->bpp[1], info->hdiv * info->bpp[0]); + } + + return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] / + info->bpp_div[plane], + byte_alignment); } static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, @@ -453,9 +472,10 @@ static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u8 stride_alignment) { - return v4l2_format_plane_stride(info, plane, width) * + return v4l2_format_plane_stride(info, plane, width, stride_alignment) * v4l2_format_plane_height(info, plane, height); } @@ -476,8 +496,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, } EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints); -int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, - u32 pixelformat, u32 width, u32 height) +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment) { const struct v4l2_format_info *info; struct v4l2_plane_pix_format *plane; @@ -494,23 +515,34 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, if (info->mem_planes == 1) { plane = &pixfmt->plane_fmt[0]; - plane->bytesperline = v4l2_format_plane_stride(info, 0, width); + plane->bytesperline = v4l2_format_plane_stride(info, 0, width, + stride_alignment); plane->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) plane->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, + stride_alignment); } else { for (i = 0; i < info->comp_planes; i++) { plane = &pixfmt->plane_fmt[i]; plane->bytesperline = - v4l2_format_plane_stride(info, i, width); + v4l2_format_plane_stride(info, i, width, + stride_alignment); plane->sizeimage = plane->bytesperline * v4l2_format_plane_height(info, i, height); } } return 0; } +EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp_aligned); + +int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height) +{ + return v4l2_fill_pixfmt_mp_aligned(pixfmt, pixelformat, + width, height, 1); +} EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp); int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, @@ -530,12 +562,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, pixfmt->width = width; pixfmt->height = height; pixfmt->pixelformat = pixelformat; - pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width); + pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width, 1); pixfmt->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) pixfmt->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, 1); return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 401d8506c24b..edd416178c33 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -558,6 +558,10 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, u32 width, u32 height); int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, u32 width, u32 height); +/* @stride_alignment is a power of 2 value in bytes */ +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment); /** * v4l2_get_link_freq - Get link rate from transmitter From 65017e26c065d1240b0512c15867ef1128034fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:11 +0200 Subject: [PATCH 1376/5214] media: rockchip: rga: fix too small buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the command buffer size being only a quarter of the actual size. The RGA_CMDBUF_SIZE macro was potentially intended to specify the length of the cmdbuf u32 array pointer. But as it's used to specify the size of the allocation, which is counted in bytes. Therefore adjust the macro size to bytes as it better matches the variable name and adjust it's users accordingly. As the command buffer is relatively small, it probably didn't caused an issue due to being smaller than a single page. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43ed742a1649..d1618bb24750 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -414,7 +414,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); + memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index cc6bd7f5b030..2b8537a5fd0d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,7 +6,7 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ -#define RGA_CMDBUF_SIZE 0x20 +#define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ #define MAX_WIDTH 8192 From a2a4f69fd2e333bacd17d1788eb5211cb6f373cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:12 +0200 Subject: [PATCH 1377/5214] media: rockchip: rga: use clk_bulk api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the clk_bulk API to avoid code duplication for each of the three clocks. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 65 +++-------------------- drivers/media/platform/rockchip/rga/rga.h | 6 +-- 2 files changed, 11 insertions(+), 60 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index fea63b94c5f3..4e710a050cb7 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -698,48 +698,10 @@ static const struct video_device rga_videodev = { .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; -static int rga_enable_clocks(struct rockchip_rga *rga) -{ - int ret; - - ret = clk_prepare_enable(rga->sclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(rga->aclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret); - goto err_disable_sclk; - } - - ret = clk_prepare_enable(rga->hclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret); - goto err_disable_aclk; - } - - return 0; - -err_disable_aclk: - clk_disable_unprepare(rga->aclk); -err_disable_sclk: - clk_disable_unprepare(rga->sclk); - - return ret; -} - -static void rga_disable_clocks(struct rockchip_rga *rga) -{ - clk_disable_unprepare(rga->sclk); - clk_disable_unprepare(rga->hclk); - clk_disable_unprepare(rga->aclk); -} - static int rga_parse_dt(struct rockchip_rga *rga) { struct reset_control *core_rst, *axi_rst, *ahb_rst; + int ret; core_rst = devm_reset_control_get(rga->dev, "core"); if (IS_ERR(core_rst)) { @@ -771,23 +733,12 @@ static int rga_parse_dt(struct rockchip_rga *rga) udelay(1); reset_control_deassert(ahb_rst); - rga->sclk = devm_clk_get(rga->dev, "sclk"); - if (IS_ERR(rga->sclk)) { - dev_err(rga->dev, "failed to get sclk clock\n"); - return PTR_ERR(rga->sclk); - } - - rga->aclk = devm_clk_get(rga->dev, "aclk"); - if (IS_ERR(rga->aclk)) { - dev_err(rga->dev, "failed to get aclk clock\n"); - return PTR_ERR(rga->aclk); - } - - rga->hclk = devm_clk_get(rga->dev, "hclk"); - if (IS_ERR(rga->hclk)) { - dev_err(rga->dev, "failed to get hclk clock\n"); - return PTR_ERR(rga->hclk); + ret = devm_clk_bulk_get_all(rga->dev, &rga->clks); + if (ret < 0) { + dev_err(rga->dev, "failed to get clocks\n"); + return ret; } + rga->num_clks = ret; return 0; } @@ -935,7 +886,7 @@ static int __maybe_unused rga_runtime_suspend(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - rga_disable_clocks(rga); + clk_bulk_disable_unprepare(rga->num_clks, rga->clks); return 0; } @@ -944,7 +895,7 @@ static int __maybe_unused rga_runtime_resume(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - return rga_enable_clocks(rga); + return clk_bulk_prepare_enable(rga->num_clks, rga->clks); } static const struct dev_pm_ops rga_pm = { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 72a28b120fab..2db10acecb40 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -6,6 +6,7 @@ #ifndef __RGA_H__ #define __RGA_H__ +#include #include #include #include @@ -81,9 +82,8 @@ struct rockchip_rga { struct device *dev; struct regmap *grf; void __iomem *regs; - struct clk *sclk; - struct clk *aclk; - struct clk *hclk; + struct clk_bulk_data *clks; + int num_clks; struct rockchip_rga_version version; /* vfd lock */ From 74d010fbdc4478293ceb96eed7f6609fe6c7529c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:13 +0200 Subject: [PATCH 1378/5214] media: rockchip: rga: use stride for offset calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the stride instead of the width for the offset calculation. This ensures that the bytesperline value doesn't need to match the width value of the image. Furthermore this patch removes the dependency on the uv_factor property and instead reuses the v4l2_format_info to determine the correct division factor. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 14 +++++++++----- drivers/media/platform/rockchip/rga/rga.c | 16 ---------------- drivers/media/platform/rockchip/rga/rga.h | 1 - 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index bb575873f2b2..65fc0d5b4aa1 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -14,7 +14,6 @@ #include #include -#include "rga-hw.h" #include "rga.h" static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc, @@ -95,14 +94,19 @@ static int rga_buf_init(struct vb2_buffer *vb) return 0; } -static int get_plane_offset(struct rga_frame *f, int plane) +static int get_plane_offset(struct rga_frame *f, + const struct v4l2_format_info *info, + int plane) { + u32 stride = f->pix.plane_fmt[0].bytesperline; + if (plane == 0) return 0; if (plane == 1) - return f->width * f->height; + return stride * f->height; if (plane == 2) - return f->width * f->height + (f->width * f->height / f->fmt->uv_factor); + return stride * f->height + + (stride * f->height / info->hdiv / info->vdiv); return -EINVAL; } @@ -148,7 +152,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) /* Fill the remaining planes */ info = v4l2_format_info(f->fmt->fourcc); for (i = info->mem_planes; i < info->comp_planes; i++) - offsets[i] = get_plane_offset(f, i); + offsets[i] = get_plane_offset(f, info, i); rbuf->offset.y_off = offsets[0]; rbuf->offset.u_off = offsets[1]; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 4e710a050cb7..c07207edffdb 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -190,7 +190,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -199,7 +198,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -208,7 +206,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -217,7 +214,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -226,7 +222,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -235,7 +230,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -244,7 +238,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -253,7 +246,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -262,7 +254,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -271,7 +262,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 1, }, @@ -280,7 +270,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -289,7 +278,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -298,7 +286,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 1, }, @@ -307,7 +294,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 2, }, @@ -316,7 +302,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 2, }, @@ -325,7 +310,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 2, }, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 2db10acecb40..477cf5b62bbb 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -17,7 +17,6 @@ struct rga_fmt { u32 fourcc; int depth; - u8 uv_factor; u8 y_div; u8 x_div; u8 color_swap; From dbf91b1ab6d95662ceb66cb55abceda5b4dc2f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:14 +0200 Subject: [PATCH 1379/5214] media: rockchip: rga: remove redundant rga_frame variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the redundant rga_frame variables width, height and color space. The value of these variables is already contained in the pix member of rga_frame. The code also keeps these values in sync. Therefore drop them in favor of the existing pix member. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 6 ++-- drivers/media/platform/rockchip/rga/rga-hw.c | 6 ++-- drivers/media/platform/rockchip/rga/rga.c | 32 +++++++------------ drivers/media/platform/rockchip/rga/rga.h | 5 --- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 65fc0d5b4aa1..ffc6162b2e68 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -103,10 +103,10 @@ static int get_plane_offset(struct rga_frame *f, if (plane == 0) return 0; if (plane == 1) - return stride * f->height; + return stride * f->pix.height; if (plane == 2) - return stride * f->height + - (stride * f->height / info->hdiv / info->vdiv); + return stride * f->pix.height + + (stride * f->pix.height / info->hdiv / info->vdiv); return -EINVAL; } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index d1618bb24750..ec6c17504ca1 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -53,7 +53,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, x_div = frm->fmt->x_div; y_div = frm->fmt->y_div; uv_stride = frm->stride / x_div; - pixel_width = frm->stride / frm->width; + pixel_width = frm->stride / frm->pix.width; lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; @@ -191,7 +191,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { - switch (ctx->in.colorspace) { + switch (ctx->in.pix.colorspace) { case V4L2_COLORSPACE_REC709: src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; @@ -203,7 +203,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { - switch (ctx->out.colorspace) { + switch (ctx->out.pix.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index c07207edffdb..ca8d8a53dc25 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -329,9 +329,6 @@ static struct rga_fmt *rga_fmt_find(u32 pixelformat) } static struct rga_frame def_frame = { - .width = DEFAULT_WIDTH, - .height = DEFAULT_HEIGHT, - .colorspace = V4L2_COLORSPACE_DEFAULT, .crop.left = 0, .crop.top = 0, .crop.width = DEFAULT_WIDTH, @@ -363,9 +360,9 @@ static int rga_open(struct file *file) ctx->out = def_frame; v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height); + ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height); + ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); @@ -453,10 +450,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); - v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height); - + *pix_fmt = frm->pix; pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = frm->colorspace; return 0; } @@ -505,27 +500,24 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->width = pix_fmt->width; - frm->height = pix_fmt->height; frm->size = 0; for (i = 0; i < pix_fmt->num_planes; i++) frm->size += pix_fmt->plane_fmt[i].sizeimage; frm->fmt = rga_fmt_find(pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; - frm->colorspace = pix_fmt->colorspace; /* Reset crop settings */ frm->crop.left = 0; frm->crop.top = 0; - frm->crop.width = frm->width; - frm->crop.height = frm->height; + frm->crop.width = pix_fmt->width; + frm->crop.height = pix_fmt->height; frm->pix = *pix_fmt; v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", - &frm->fmt->fourcc, frm->width, frm->height, + &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, frm->stride, frm->size); for (i = 0; i < pix_fmt->num_planes; i++) { @@ -579,8 +571,8 @@ static int vidioc_g_selection(struct file *file, void *priv, } else { s->r.left = 0; s->r.top = 0; - s->r.width = f->width; - s->r.height = f->height; + s->r.width = f->pix.width; + s->r.height = f->pix.height; } return 0; @@ -629,8 +621,8 @@ static int vidioc_s_selection(struct file *file, void *priv, return -EINVAL; } - if (s->r.left + s->r.width > f->width || - s->r.top + s->r.height > f->height || + if (s->r.left + s->r.width > f->pix.width || + s->r.top + s->r.height > f->pix.height || s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) { v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n"); return -EINVAL; @@ -821,8 +813,8 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * def_frame.height; + def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3; + def_frame.size = def_frame.stride * DEFAULT_HEIGHT; ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 477cf5b62bbb..c4a3905a48f0 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -24,11 +24,6 @@ struct rga_fmt { }; struct rga_frame { - /* Original dimensions */ - u32 width; - u32 height; - u32 colorspace; - /* Crop */ struct v4l2_rect crop; From ce1b5e4239aae88e71aca0e9db1116d46be2fc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:15 +0200 Subject: [PATCH 1380/5214] media: rockchip: rga: announce and sync colorimetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Announce the capability to adjust the quantization and ycbcr_enc on the capture side and check if the SET_CSC flag is set when the colorimetry is changed. Furthermore copy the colorimetry from the output to the capture side to fix the currently failing v4l2-compliance tests, which expect exactly this behavior. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index ca8d8a53dc25..8c34f73d6976 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -437,6 +437,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + + /* allow changing the quantization and xfer func for YUV formats */ + if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat))) + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION | + V4L2_FMT_FLAG_CSC_YCBCR_ENC; + return 0; } @@ -459,8 +468,25 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; + struct rga_ctx *ctx = file_to_rga_ctx(file); struct rga_fmt *fmt; + if (V4L2_TYPE_IS_CAPTURE(f->type)) { + const struct rga_frame *frm; + + frm = rga_get_frame(ctx, f->type); + if (IS_ERR(frm)) + return PTR_ERR(frm); + + if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) { + pix_fmt->quantization = frm->pix.quantization; + pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc; + } + /* disallow values not announced in vidioc_enum_fmt */ + pix_fmt->colorspace = frm->pix.colorspace; + pix_fmt->xfer_func = frm->pix.xfer_func; + } + fmt = rga_fmt_find(pix_fmt->pixelformat); if (!fmt) fmt = &formats[0]; @@ -506,6 +532,17 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->fmt = rga_fmt_find(pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; + /* + * Copy colorimetry from output to capture as required by the + * v4l2-compliance tests + */ + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + ctx->out.pix.colorspace = pix_fmt->colorspace; + ctx->out.pix.ycbcr_enc = pix_fmt->ycbcr_enc; + ctx->out.pix.quantization = pix_fmt->quantization; + ctx->out.pix.xfer_func = pix_fmt->xfer_func; + } + /* Reset crop settings */ frm->crop.left = 0; frm->crop.top = 0; From 0fab01bb159a3c0cf79f44a7126987548e001a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:16 +0200 Subject: [PATCH 1381/5214] media: rockchip: rga: move hw specific parts to a dedicated struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for the RGA3 unit, move RGA2 specific parts from rga.c to rga-hw.c and create a struct to reference the RGA2 specific functions and formats. This also allows to remove the rga-hw.h reference from the include list of the rga driver. Also document the command finish interrupt with a dedicated define. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 166 ++++++++++++++- drivers/media/platform/rockchip/rga/rga-hw.h | 5 +- drivers/media/platform/rockchip/rga/rga.c | 211 ++++--------------- drivers/media/platform/rockchip/rga/rga.h | 23 +- 4 files changed, 227 insertions(+), 178 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index ec6c17504ca1..40498796507e 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -437,8 +437,8 @@ static void rga_cmd_set(struct rga_ctx *ctx, PAGE_SIZE, DMA_BIDIRECTIONAL); } -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +static void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rga_ctx *ctx = rga->curr; @@ -452,3 +452,165 @@ void rga_hw_start(struct rockchip_rga *rga, rga_write(rga, RGA_CMD_CTRL, 0x1); } + +static bool rga_handle_irq(struct rockchip_rga *rga) +{ + int intr; + + intr = rga_read(rga, RGA_INT) & 0xf; + + rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); + + return intr & RGA_INT_COMMAND_FINISHED; +} + +static void rga_get_version(struct rockchip_rga *rga) +{ + rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; + rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; +} + +static struct rga_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .color_swap = RGA_COLOR_ALPHA_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_XBGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + .depth = 24, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + .depth = 24, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB444, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR4444, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB555, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR1555, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_BGR565, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + .depth = 12, + .y_div = 2, + .x_div = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422P, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422P, + .depth = 16, + .y_div = 1, + .x_div = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + .depth = 12, + .y_div = 2, + .x_div = 2, + }, +}; + +const struct rga_hw rga2_hw = { + .formats = formats, + .num_formats = ARRAY_SIZE(formats), + .cmdbuf_size = RGA_CMDBUF_SIZE, + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + + .start = rga_hw_start, + .handle_irq = rga_handle_irq, + .get_version = rga_get_version, +}; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 2b8537a5fd0d..c2e34be75193 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -15,9 +15,6 @@ #define MIN_WIDTH 34 #define MIN_HEIGHT 34 -#define DEFAULT_WIDTH 100 -#define DEFAULT_HEIGHT 100 - #define RGA_TIMEOUT 500 /* Registers address */ @@ -178,6 +175,8 @@ #define RGA_ALPHA_COLOR_NORMAL 0 #define RGA_ALPHA_COLOR_MULTIPLY_CAL 1 +#define RGA_INT_COMMAND_FINISHED 4 + /* Registers union */ union rga_mode_ctrl { unsigned int val; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 8c34f73d6976..f599c992829d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -25,7 +25,6 @@ #include #include -#include "rga-hw.h" #include "rga.h" static int debug; @@ -47,7 +46,7 @@ static void device_run(void *prv) dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); + rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst)); spin_unlock_irqrestore(&rga->ctrl_lock, flags); } @@ -55,13 +54,8 @@ static void device_run(void *prv) static irqreturn_t rga_isr(int irq, void *prv) { struct rockchip_rga *rga = prv; - int intr; - intr = rga_read(rga, RGA_INT) & 0xf; - - rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); - - if (intr & 0x04) { + if (rga->hw->handle_irq(rga)) { struct vb2_v4l2_buffer *src, *dst; struct rga_ctx *ctx = rga->curr; @@ -184,158 +178,17 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } -static struct rga_fmt formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB32, - .color_swap = RGA_COLOR_ALPHA_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ABGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_XBGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB24, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_BGR24, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB444, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR4444, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB555, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR1555, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_BGR565, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV61, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12M, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV16, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .y_div = 2, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YUV422P, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422P, - .depth = 16, - .y_div = 1, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YVU420, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .y_div = 2, - .x_div = 2, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(formats) - -static struct rga_fmt *rga_fmt_find(u32 pixelformat) +static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) { unsigned int i; - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].fourcc == pixelformat) - return &formats[i]; + for (i = 0; i < rga->hw->num_formats; i++) { + if (rga->hw->formats[i].fourcc == pixelformat) + return &rga->hw->formats[i]; } return NULL; } -static struct rga_frame def_frame = { - .crop.left = 0, - .crop.top = 0, - .crop.width = DEFAULT_WIDTH, - .crop.height = DEFAULT_HEIGHT, - .fmt = &formats[0], -}; - struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -350,6 +203,18 @@ static int rga_open(struct file *file) struct rockchip_rga *rga = video_drvdata(file); struct rga_ctx *ctx = NULL; int ret = 0; + u32 def_width = clamp(DEFAULT_WIDTH, rga->hw->min_width, rga->hw->max_width); + u32 def_height = clamp(DEFAULT_HEIGHT, rga->hw->min_height, rga->hw->max_height); + struct rga_frame def_frame = { + .crop.left = 0, + .crop.top = 0, + .crop.width = def_width, + .crop.height = def_height, + .fmt = &rga->hw->formats[0], + }; + + def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; + def_frame.size = def_frame.stride * def_height; ctx = kzalloc_obj(*ctx); if (!ctx) @@ -360,9 +225,9 @@ static int rga_open(struct file *file) ctx->out = def_frame; v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); + ctx->in.fmt->fourcc, def_width, def_height); v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); + ctx->out.fmt->fourcc, def_width, def_height); if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); @@ -429,12 +294,13 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { + struct rockchip_rga *rga = video_drvdata(file); struct rga_fmt *fmt; - if (f->index >= NUM_FORMATS) + if (f->index >= rga->hw->num_formats) return -EINVAL; - fmt = &formats[f->index]; + fmt = &rga->hw->formats[f->index]; f->pixelformat = fmt->fourcc; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && @@ -469,6 +335,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = file_to_rga_ctx(file); + const struct rga_hw *hw = ctx->rga->hw; struct rga_fmt *fmt; if (V4L2_TYPE_IS_CAPTURE(f->type)) { @@ -487,14 +354,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt->xfer_func = frm->pix.xfer_func; } - fmt = rga_fmt_find(pix_fmt->pixelformat); + fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat); if (!fmt) - fmt = &formats[0]; + fmt = &hw->formats[0]; pix_fmt->width = clamp(pix_fmt->width, - (u32)MIN_WIDTH, (u32)MAX_WIDTH); + hw->min_width, hw->max_width); pix_fmt->height = clamp(pix_fmt->height, - (u32)MIN_HEIGHT, (u32)MAX_HEIGHT); + hw->min_height, hw->max_height); v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); pix_fmt->field = V4L2_FIELD_NONE; @@ -529,7 +396,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->size = 0; for (i = 0; i < pix_fmt->num_planes; i++) frm->size += pix_fmt->plane_fmt[i].sizeimage; - frm->fmt = rga_fmt_find(pix_fmt->pixelformat); + frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; /* @@ -660,7 +527,7 @@ static int vidioc_s_selection(struct file *file, void *priv, if (s->r.left + s->r.width > f->pix.width || s->r.top + s->r.height > f->pix.height || - s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) { + s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) { v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n"); return -EINVAL; } @@ -770,6 +637,10 @@ static int rga_probe(struct platform_device *pdev) if (!rga) return -ENOMEM; + rga->hw = of_device_get_match_data(&pdev->dev); + if (!rga->hw) + return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n"); + rga->dev = &pdev->dev; spin_lock_init(&rga->ctrl_lock); mutex_init(&rga->mutex); @@ -833,8 +704,7 @@ static int rga_probe(struct platform_device *pdev) if (ret < 0) goto rel_m2m; - rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; - rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; + rga->hw->get_version(rga); v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n", rga->version.major, rga->version.minor); @@ -842,7 +712,7 @@ static int rga_probe(struct platform_device *pdev) pm_runtime_put(rga->dev); /* Create CMD buffer */ - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE, + rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, &rga->cmdbuf_phy, GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); if (!rga->cmdbuf_virt) { @@ -850,9 +720,6 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * DEFAULT_HEIGHT; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); @@ -865,7 +732,7 @@ static int rga_probe(struct platform_device *pdev) return 0; free_dma: - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_m2m: v4l2_m2m_release(rga->m2m_dev); @@ -883,7 +750,7 @@ static void rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); v4l2_info(&rga->v4l2_dev, "Removing\n"); @@ -919,9 +786,11 @@ static const struct dev_pm_ops rga_pm = { static const struct of_device_id rockchip_rga_match[] = { { .compatible = "rockchip,rk3288-rga", + .data = &rga2_hw, }, { .compatible = "rockchip,rk3399-rga", + .data = &rga2_hw, }, {}, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index c4a3905a48f0..640e51028534 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -14,6 +14,9 @@ #define RGA_NAME "rockchip-rga" +#define DEFAULT_WIDTH 100 +#define DEFAULT_HEIGHT 100 + struct rga_fmt { u32 fourcc; int depth; @@ -68,6 +71,8 @@ static inline struct rga_ctx *file_to_rga_ctx(struct file *filp) return container_of(file_to_v4l2_fh(filp), struct rga_ctx, fh); } +struct rga_hw; + struct rockchip_rga { struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; @@ -88,6 +93,8 @@ struct rockchip_rga { struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; + + const struct rga_hw *hw; }; struct rga_addr_offset { @@ -138,7 +145,19 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst); +struct rga_hw { + struct rga_fmt *formats; + u32 num_formats; + size_t cmdbuf_size; + u32 min_width, min_height; + u32 max_width, max_height; + + void (*start)(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst); + bool (*handle_irq)(struct rockchip_rga *rga); + void (*get_version)(struct rockchip_rga *rga); +}; + +extern const struct rga_hw rga2_hw; #endif From 92f50870ae987b8e2e5334e4ee38f82f6f405d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:17 +0200 Subject: [PATCH 1382/5214] media: rockchip: rga: avoid odd frame sizes for YUV formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid odd frame sizes for YUV formats, as they may cause undefined behavior. This is done in preparation for the RGA3, which hangs when the output format is set to 129x129 pixel YUV420 SP (NV12). This requirement is documented explicitly for the RGA3 in section 5.6.3 of the RK3588 TRM Part 2. For the RGA2 the RK3588 TRM Part 2 (section 6.1.2) and RK3568 TRM Part 2 (section 14.2) only mentions the x/y offsets and stride aligning requirements. But the vendor driver for the RGA2 also contains checks for the width and height to be aligned to 2 bytes. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index f599c992829d..77b8c7ab7427 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -337,6 +337,19 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) struct rga_ctx *ctx = file_to_rga_ctx(file); const struct rga_hw *hw = ctx->rga->hw; struct rga_fmt *fmt; + struct v4l2_frmsize_stepwise frmsize = { + .min_width = hw->min_width, + .max_width = hw->max_width, + .min_height = hw->min_height, + .max_height = hw->max_height, + .step_width = 1, + .step_height = 1, + }; + + if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) { + frmsize.step_width = 2; + frmsize.step_height = 2; + } if (V4L2_TYPE_IS_CAPTURE(f->type)) { const struct rga_frame *frm; @@ -358,11 +371,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) if (!fmt) fmt = &hw->formats[0]; - pix_fmt->width = clamp(pix_fmt->width, - hw->min_width, hw->max_width); - pix_fmt->height = clamp(pix_fmt->height, - hw->min_height, hw->max_height); - + v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); pix_fmt->field = V4L2_FIELD_NONE; From 3de2ea285a15e27a2e81c442124062806235b26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:18 +0200 Subject: [PATCH 1383/5214] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calculate the x_div and y_div variables with the information from v4l2_format_info instead of storing these in the rga_fmt struct. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 45 +++++--------------- drivers/media/platform/rockchip/rga/rga.h | 2 - 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 40498796507e..17f7a67c0b4b 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -42,6 +42,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, { struct rga_corners_addr_offset offsets; struct rga_addr_offset *lt, *lb, *rt, *rb; + const struct v4l2_format_info *format_info; unsigned int x_div = 0, y_div = 0, uv_stride = 0, pixel_width = 0; @@ -50,8 +51,16 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, rt = &offsets.right_top; rb = &offsets.right_bottom; - x_div = frm->fmt->x_div; - y_div = frm->fmt->y_div; + format_info = v4l2_format_info(frm->pix.pixelformat); + /* x_div is only used for the u/v planes. + * When the format doesn't have these, use 1 to avoid a division by zero. + */ + if (format_info->bpp[1]) + x_div = format_info->hdiv * format_info->bpp_div[1] / + format_info->bpp[1]; + else + x_div = 1; + y_div = format_info->vdiv; uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->pix.width; @@ -476,128 +485,96 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ABGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_XBGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_RGB24, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_BGR24, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ARGB444, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ARGB555, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_RGB565, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV21, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV61, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV12, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV12M, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_YUV420, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .y_div = 2, - .x_div = 2, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, .depth = 16, - .y_div = 1, - .x_div = 2, }, { .fourcc = V4L2_PIX_FMT_YVU420, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .y_div = 2, - .x_div = 2, }, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 640e51028534..27b3c9b4f220 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -20,8 +20,6 @@ struct rga_fmt { u32 fourcc; int depth; - u8 y_div; - u8 x_div; u8 color_swap; u8 hw_format; }; From 6f4e57d57c474be1202a2e0c5c7a9b543503216a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:19 +0200 Subject: [PATCH 1384/5214] media: rockchip: rga: move cmdbuf to rga_ctx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the command buffer to the rga_ctx struct in preparation to reuse an already prepared command buffer. This allows to split the command buffer setup in a further commit to setup a template for the command buffer at streamon and only update the buffer addresses in device_run and trigger the command stream. No sync point is added, as one command buffer should only be used for one conversion at a time. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 28 +++++------- drivers/media/platform/rockchip/rga/rga.c | 48 +++++++++++--------- drivers/media/platform/rockchip/rga/rga.h | 5 +- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 17f7a67c0b4b..9881c14f908d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -122,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG; @@ -135,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG; @@ -148,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG; @@ -162,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_trans_info(struct rga_ctx *ctx) { struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int scale_dst_w, scale_dst_h; unsigned int src_h, src_w, dst_h, dst_w; union rga_src_info src_info; @@ -322,8 +319,7 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx, struct rga_addr_offset *offset) { struct rga_corners_addr_offset src_offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int src_h, src_w, src_x, src_y; src_h = ctx->in.crop.height; @@ -350,8 +346,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, { struct rga_addr_offset *dst_offset; struct rga_corners_addr_offset offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int dst_h, dst_w, dst_x, dst_y; unsigned int mir_mode = 0; unsigned int rot_mode = 0; @@ -397,8 +392,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, static void rga_cmd_set_mode(struct rga_ctx *ctx) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; union rga_mode_ctrl mode; union rga_alpha_ctrl0 alpha_ctrl0; union rga_alpha_ctrl1 alpha_ctrl1; @@ -423,7 +417,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); + memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* @@ -439,11 +433,11 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_dst_info(ctx, &dst->offset); rga_cmd_set_trans_info(ctx); - rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy); + rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); /* sync CMD buf for RGA */ - dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy, - PAGE_SIZE, DMA_BIDIRECTIONAL); + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); } static void rga_hw_start(struct rockchip_rga *rga, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 77b8c7ab7427..bf6bbcbfc869 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -219,6 +219,16 @@ static int rga_open(struct file *file) ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; + + /* Create CMD buffer */ + ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, + &ctx->cmdbuf_phy, GFP_KERNEL, + DMA_ATTR_WRITE_COMBINE); + if (!ctx->cmdbuf_virt) { + ret = -ENOMEM; + goto rel_ctx; + } + ctx->rga = rga; /* Set default formats */ ctx->in = def_frame; @@ -230,15 +240,13 @@ static int rga_open(struct file *file) ctx->out.fmt->fourcc, def_width, def_height); if (mutex_lock_interruptible(&rga->mutex)) { - kfree(ctx); - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto rel_cmdbuf; } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init); if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); - mutex_unlock(&rga->mutex); - kfree(ctx); - return ret; + goto unlock_mutex; } v4l2_fh_init(&ctx->fh, video_devdata(file)); v4l2_fh_add(&ctx->fh, file); @@ -252,6 +260,15 @@ static int rga_open(struct file *file) mutex_unlock(&rga->mutex); return 0; + +unlock_mutex: + mutex_unlock(&rga->mutex); +rel_cmdbuf: + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); +rel_ctx: + kfree(ctx); + return ret; } static int rga_release(struct file *file) @@ -266,6 +283,10 @@ static int rga_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); + + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); + kfree(ctx); mutex_unlock(&rga->mutex); @@ -720,19 +741,10 @@ static int rga_probe(struct platform_device *pdev) pm_runtime_put(rga->dev); - /* Create CMD buffer */ - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, - &rga->cmdbuf_phy, GFP_KERNEL, - DMA_ATTR_WRITE_COMBINE); - if (!rga->cmdbuf_virt) { - ret = -ENOMEM; - goto rel_m2m; - } - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto free_dma; + goto rel_m2m; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -740,9 +752,6 @@ static int rga_probe(struct platform_device *pdev) return 0; -free_dma: - dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_m2m: v4l2_m2m_release(rga->m2m_dev); rel_vdev: @@ -759,9 +768,6 @@ static void rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); - v4l2_info(&rga->v4l2_dev, "Removing\n"); v4l2_m2m_release(rga->m2m_dev); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 27b3c9b4f220..04aeb7b42952 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -53,6 +53,9 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + void *cmdbuf_virt; + dma_addr_t cmdbuf_phy; + int osequence; int csequence; @@ -89,8 +92,6 @@ struct rockchip_rga { spinlock_t ctrl_lock; struct rga_ctx *curr; - dma_addr_t cmdbuf_phy; - void *cmdbuf_virt; const struct rga_hw *hw; }; From 00910c4f731773276d75acdcee3bc15f19a37705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:20 +0200 Subject: [PATCH 1385/5214] media: rockchip: rga: align stride to 4 bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an alignment setting to rga_hw to set the desired stride alignment. As the RGA2 register for the stride counts in word units, the code already divides the bytesperline value by 4 when writing it into the register. Therefore fix the alignment to a multiple of 4 to avoid potential off by one errors due from the division. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 11 ++++++----- drivers/media/platform/rockchip/rga/rga.h | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 9881c14f908d..dac3cb6aa17d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -580,6 +580,7 @@ const struct rga_hw rga2_hw = { .max_width = MAX_WIDTH, .min_height = MIN_HEIGHT, .max_height = MAX_HEIGHT, + .stride_alignment = 4, .start = rga_hw_start, .handle_irq = rga_handle_irq, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index bf6bbcbfc869..d080cb672740 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -234,10 +234,10 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; - v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, def_width, def_height); - v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, def_width, def_height); + v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc, + def_width, def_height, rga->hw->stride_alignment); + v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc, + def_width, def_height, rga->hw->stride_alignment); if (mutex_lock_interruptible(&rga->mutex)) { ret = -ERESTARTSYS; @@ -393,7 +393,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) fmt = &hw->formats[0]; v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); - v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); + v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc, + pix_fmt->width, pix_fmt->height, hw->stride_alignment); pix_fmt->field = V4L2_FIELD_NONE; return 0; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 04aeb7b42952..38518146910a 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -150,6 +150,7 @@ struct rga_hw { size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; + u8 stride_alignment; void (*start)(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst); From 10e2141179fe480763d10b604d7e4f9c62e8ba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:21 +0200 Subject: [PATCH 1386/5214] media: rockchip: rga: reuse cmdbuf contents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse the command buffer contents instead of completely writing it for every frame. Therefore we only need to replace the source and destination addresses for each frame. This reduces the amount of CPU and memory operations done in each frame. A new cmdbuf_dirty flag notes if the cmdbuf has to be rewritten on the next frame. The initial idea of initializing the cmdbuf on streamon broke the ability to update controls while streaming (e.g. mirroring). Signed-off-by: Sven Püschel Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 13 +++++++++---- drivers/media/platform/rockchip/rga/rga.c | 11 +++++++++-- drivers/media/platform/rockchip/rga/rga.h | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index dac3cb6aa17d..567d39e58d33 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -417,8 +417,6 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); - rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* * Due to hardware bug, @@ -427,11 +425,9 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa); rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_mode(ctx); rga_cmd_set_src_info(ctx, &src->offset); rga_cmd_set_dst_info(ctx, &dst->offset); - rga_cmd_set_trans_info(ctx); rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); @@ -440,6 +436,14 @@ static void rga_cmd_set(struct rga_ctx *ctx, PAGE_SIZE, DMA_BIDIRECTIONAL); } +static void rga_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); + + rga_cmd_set_mode(ctx); + rga_cmd_set_trans_info(ctx); +} + static void rga_hw_start(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { @@ -582,6 +586,7 @@ const struct rga_hw rga2_hw = { .max_height = MAX_HEIGHT, .stride_alignment = 4, + .setup_cmdbuf = rga_hw_setup_cmdbuf, .start = rga_hw_start, .handle_irq = rga_handle_irq, .get_version = rga_get_version, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index d080cb672740..394b14b9469d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -38,6 +38,11 @@ static void device_run(void *prv) unsigned long flags; spin_lock_irqsave(&rga->ctrl_lock, flags); + if (ctx->cmdbuf_dirty) { + ctx->cmdbuf_dirty = false; + rga->hw->setup_cmdbuf(ctx); + } + spin_unlock_irqrestore(&rga->ctrl_lock, flags); rga->curr = ctx; @@ -47,8 +52,6 @@ static void device_run(void *prv) dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst)); - - spin_unlock_irqrestore(&rga->ctrl_lock, flags); } static irqreturn_t rga_isr(int irq, void *prv) @@ -141,6 +144,7 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) ctx->fill_color = ctrl->val; break; } + ctx->cmdbuf_dirty = true; spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags); return 0; } @@ -228,6 +232,7 @@ static int rga_open(struct file *file) ret = -ENOMEM; goto rel_ctx; } + ctx->cmdbuf_dirty = true; ctx->rga = rga; /* Set default formats */ @@ -448,6 +453,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->crop.height = pix_fmt->height; frm->pix = *pix_fmt; + ctx->cmdbuf_dirty = true; v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", @@ -564,6 +570,7 @@ static int vidioc_s_selection(struct file *file, void *priv, } f->crop = s->r; + ctx->cmdbuf_dirty = true; return ret; } diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 38518146910a..5360f092fecf 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -55,6 +55,7 @@ struct rga_ctx { void *cmdbuf_virt; dma_addr_t cmdbuf_phy; + bool cmdbuf_dirty; int osequence; int csequence; @@ -152,6 +153,7 @@ struct rga_hw { u32 max_width, max_height; u8 stride_alignment; + void (*setup_cmdbuf)(struct rga_ctx *ctx); void (*start)(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst); bool (*handle_irq)(struct rockchip_rga *rga); From 9481ec73f6e6bff573a5716692ceb9669b68f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:22 +0200 Subject: [PATCH 1387/5214] media: rockchip: rga: check scaling factor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the scaling factor to avoid potential problems. This is relevant for the upcoming RGA3 support, as it can hang when the scaling factor is exceeded. The check is done at streamon when the other side is already streaming to avoid incorrectly failing if the application configures the other side after calling streamon. As try_fmt shouldn't be state aware, it cannot be used to limit the format based on the scaling factor. Therefore the check is done just before the actual streaming would be started. As the driver allows changing the rotation and selection while streaming, add additional checks to ensure these changes don't exceed the scaling factor. Signed-off-by: Sven Püschel Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 28 +++++++++ drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga-hw.h | 1 + drivers/media/platform/rockchip/rga/rga.c | 63 ++++++++++++++++++- drivers/media/platform/rockchip/rga/rga.h | 4 ++ 5 files changed, 94 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index ffc6162b2e68..dcaba66f5c1f 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -197,6 +197,33 @@ static void rga_buf_return_buffers(struct vb2_queue *q, } } +static int rga_buf_prepare_streaming(struct vb2_queue *q) +{ + struct rga_ctx *ctx = vb2_get_drv_priv(q); + const struct rga_hw *hw = ctx->rga->hw; + int ret; + + /* It's safe to check the streaming state of the other queue, + * as the streamon ioctl's can't race due to the lock set in + * the queue_init function. + */ + if ((V4L2_TYPE_IS_OUTPUT(q->type) && + vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) || + (V4L2_TYPE_IS_CAPTURE(q->type) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)))) { + /* + * As the other side is already streaming, + * check that the max scaling factor isn't exceeded. + */ + ret = rga_check_scaling(hw, &ctx->in.crop, &ctx->out.crop, + ctx->rotate); + if (ret < 0) + return ret; + } + + return 0; +} + static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) { struct rga_ctx *ctx = vb2_get_drv_priv(q); @@ -232,6 +259,7 @@ const struct vb2_ops rga_qops = { .buf_prepare = rga_buf_prepare, .buf_queue = rga_buf_queue, .buf_cleanup = rga_buf_cleanup, + .prepare_streaming = rga_buf_prepare_streaming, .start_streaming = rga_buf_start_streaming, .stop_streaming = rga_buf_stop_streaming, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 567d39e58d33..f2900812ba76 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -584,6 +584,7 @@ const struct rga_hw rga2_hw = { .max_width = MAX_WIDTH, .min_height = MIN_HEIGHT, .max_height = MAX_HEIGHT, + .max_scaling_factor = MAX_SCALING_FACTOR, .stride_alignment = 4, .setup_cmdbuf = rga_hw_setup_cmdbuf, diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index c2e34be75193..805ec23e5e3f 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -14,6 +14,7 @@ #define MIN_WIDTH 34 #define MIN_HEIGHT 34 +#define MAX_SCALING_FACTOR 16 #define RGA_TIMEOUT 500 diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 394b14b9469d..22954bbae55f 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -127,7 +127,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) { struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx, ctrl_handler); + const struct rga_hw *hw = ctx->rga->hw; unsigned long flags; + int ret = 0; spin_lock_irqsave(&ctx->rga->ctrl_lock, flags); switch (ctrl->id) { @@ -138,6 +140,13 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + ret = rga_check_scaling(hw, &ctx->in.crop, + &ctx->out.crop, ctrl->val); + if (ret < 0) + goto s_ctrl_done; + } ctx->rotate = ctrl->val; break; case V4L2_CID_BG_COLOR: @@ -145,8 +154,10 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) break; } ctx->cmdbuf_dirty = true; + +s_ctrl_done: spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags); - return 0; + return ret; } static const struct v4l2_ctrl_ops rga_ctrl_ops = { @@ -182,6 +193,38 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } +static bool check_scaling_factor(const struct rga_hw *hw, u32 src_size, + u32 dst_size) +{ + if (src_size < dst_size) + return src_size * hw->max_scaling_factor >= dst_size; + else + return dst_size * hw->max_scaling_factor >= src_size; +} + +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate) +{ + u32 scaled_width; + u32 scaled_height; + + if (rotate == 90 || rotate == 270) { + scaled_width = crop_out->height; + scaled_height = crop_out->width; + } else { + scaled_width = crop_out->width; + scaled_height = crop_out->height; + } + + if (!check_scaling_factor(hw, crop_in->width, scaled_width)) + return -EINVAL; + + if (!check_scaling_factor(hw, crop_in->height, scaled_height)) + return -EINVAL; + + return 0; +} + static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) { unsigned int i; @@ -525,7 +568,6 @@ static int vidioc_s_selection(struct file *file, void *priv, struct rga_ctx *ctx = file_to_rga_ctx(file); struct rockchip_rga *rga = ctx->rga; struct rga_frame *f; - int ret = 0; f = rga_get_frame(ctx, s->type); if (IS_ERR(f)) @@ -569,10 +611,25 @@ static int vidioc_s_selection(struct file *file, void *priv, return -EINVAL; } + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop, + ctx->rotate); + else + ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r, + ctx->rotate); + + if (ret < 0) + return ret; + } + f->crop = s->r; ctx->cmdbuf_dirty = true; - return ret; + return 0; } static const struct v4l2_ioctl_ops rga_ioctl_ops = { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5360f092fecf..df525c6aea8b 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -123,6 +123,9 @@ static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type); +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate); + /* RGA Buffers Manage */ extern const struct vb2_ops rga_qops; @@ -151,6 +154,7 @@ struct rga_hw { size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; + u8 max_scaling_factor; u8 stride_alignment; void (*setup_cmdbuf)(struct rga_ctx *ctx); From d8eb890079d19e4b5ac8073038c4fea006992e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:23 +0200 Subject: [PATCH 1388/5214] media: rockchip: rga: use card type to specify rga type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of the RGA3 support add a filed to the rga_hw struct to specify the desired card type value. This allows the user to differentiate the RGA2 and RGA3 video device nodes. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 4 +++- drivers/media/platform/rockchip/rga/rga.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index f2900812ba76..43fd023b7571 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -577,6 +577,7 @@ static struct rga_fmt formats[] = { }; const struct rga_hw rga2_hw = { + .card_type = "rga2", .formats = formats, .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 22954bbae55f..91775b43ff61 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -354,8 +354,10 @@ static const struct v4l2_file_operations rga_fops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct rockchip_rga *rga = video_drvdata(file); + strscpy(cap->driver, RGA_NAME, sizeof(cap->driver)); - strscpy(cap->card, "rockchip-rga", sizeof(cap->card)); + strscpy(cap->card, rga->hw->card_type, sizeof(cap->card)); strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info)); return 0; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index df525c6aea8b..cee2e75ea89f 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -149,6 +149,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) }; struct rga_hw { + const char *card_type; struct rga_fmt *formats; u32 num_formats; size_t cmdbuf_size; From e21531a6b031a46f37f61d4d7c7508868fb3d434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:24 +0200 Subject: [PATCH 1389/5214] media: rockchip: rga: change offset to dma_addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the offset to dma_addresses, as the current naming is misleading. The offset naming comes from the fact that it references the offset in the mapped iommu address space. But from the hardware point of view this is an address, as also pointed out by the register naming (e.g. RGA_DST_Y_RGB_BASE_ADDR). Therefore also change the type to dma_addr_t, as with an external iommu driver this would also be the correct type. This change is a preparation for the RGA3 support, which uses an external iommu and therefore just gets an dma_addr_t for each buffer. The field renaming allows to reuse the existing fields of rga_vb_buffer to store these values. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 12 +- drivers/media/platform/rockchip/rga/rga-hw.c | 105 +++++++++--------- drivers/media/platform/rockchip/rga/rga.h | 12 +- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index dcaba66f5c1f..ab9554c1c4cd 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -121,7 +121,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) size_t curr_desc = 0; int i; const struct v4l2_format_info *info; - unsigned int offsets[VIDEO_MAX_PLANES]; + dma_addr_t dma_addrs[VIDEO_MAX_PLANES]; if (IS_ERR(f)) return PTR_ERR(f); @@ -145,18 +145,18 @@ static int rga_buf_prepare(struct vb2_buffer *vb) "Failed to map video buffer to RGA\n"); return n_desc; } - offsets[i] = curr_desc << PAGE_SHIFT; + dma_addrs[i] = curr_desc << PAGE_SHIFT; curr_desc += n_desc; } /* Fill the remaining planes */ info = v4l2_format_info(f->fmt->fourcc); for (i = info->mem_planes; i < info->comp_planes; i++) - offsets[i] = get_plane_offset(f, info, i); + dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i); - rbuf->offset.y_off = offsets[0]; - rbuf->offset.u_off = offsets[1]; - rbuf->offset.v_off = offsets[2]; + rbuf->dma_addrs.y_addr = dma_addrs[0]; + rbuf->dma_addrs.u_addr = dma_addrs[1]; + rbuf->dma_addrs.v_addr = dma_addrs[2]; return 0; } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43fd023b7571..99cf57d5ba89 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -16,11 +16,11 @@ enum e_rga_start_pos { RB = 3, }; -struct rga_corners_addr_offset { - struct rga_addr_offset left_top; - struct rga_addr_offset right_top; - struct rga_addr_offset left_bottom; - struct rga_addr_offset right_bottom; +struct rga_corners_addrs { + struct rga_addrs left_top; + struct rga_addrs right_top; + struct rga_addrs left_bottom; + struct rga_addrs right_bottom; }; static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) @@ -36,20 +36,20 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst); } -static struct rga_corners_addr_offset -rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, - unsigned int x, unsigned int y, unsigned int w, unsigned int h) +static struct rga_corners_addrs +rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, + unsigned int x, unsigned int y, unsigned int w, unsigned int h) { - struct rga_corners_addr_offset offsets; - struct rga_addr_offset *lt, *lb, *rt, *rb; + struct rga_corners_addrs corner_addrs; + struct rga_addrs *lt, *lb, *rt, *rb; const struct v4l2_format_info *format_info; unsigned int x_div = 0, y_div = 0, uv_stride = 0, pixel_width = 0; - lt = &offsets.left_top; - lb = &offsets.left_bottom; - rt = &offsets.right_top; - rb = &offsets.right_bottom; + lt = &corner_addrs.left_top; + lb = &corner_addrs.left_bottom; + rt = &corner_addrs.right_top; + rb = &corner_addrs.right_bottom; format_info = v4l2_format_info(frm->pix.pixelformat); /* x_div is only used for the u/v planes. @@ -64,29 +64,28 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->pix.width; - lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; - lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; - lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div; + lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width; + lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div; + lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div; - lb->y_off = lt->y_off + (h - 1) * frm->stride; - lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride; - lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride; + lb->y_addr = lt->y_addr + (h - 1) * frm->stride; + lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride; + lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride; - rt->y_off = lt->y_off + (w - 1) * pixel_width; - rt->u_off = lt->u_off + w / x_div - 1; - rt->v_off = lt->v_off + w / x_div - 1; + rt->y_addr = lt->y_addr + (w - 1) * pixel_width; + rt->u_addr = lt->u_addr + w / x_div - 1; + rt->v_addr = lt->v_addr + w / x_div - 1; - rb->y_off = lb->y_off + (w - 1) * pixel_width; - rb->u_off = lb->u_off + w / x_div - 1; - rb->v_off = lb->v_off + w / x_div - 1; + rb->y_addr = lb->y_addr + (w - 1) * pixel_width; + rb->u_addr = lb->u_addr + w / x_div - 1; + rb->v_addr = lb->v_addr + w / x_div - 1; - return offsets; + return corner_addrs; } -static struct rga_addr_offset *rga_lookup_draw_pos(struct - rga_corners_addr_offset - * offsets, u32 rotate_mode, - u32 mirr_mode) +static struct rga_addrs *rga_lookup_draw_pos(struct rga_corners_addrs *corner_addrs, + u32 rotate_mode, + u32 mirr_mode) { static enum e_rga_start_pos rot_mir_point_matrix[4][4] = { { @@ -103,18 +102,18 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct }, }; - if (!offsets) + if (!corner_addrs) return NULL; switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) { case LT: - return &offsets->left_top; + return &corner_addrs->left_top; case LB: - return &offsets->left_bottom; + return &corner_addrs->left_bottom; case RT: - return &offsets->right_top; + return &corner_addrs->right_top; case RB: - return &offsets->right_bottom; + return &corner_addrs->right_bottom; } return NULL; @@ -316,9 +315,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } static void rga_cmd_set_src_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_corners_addr_offset src_offsets; + struct rga_corners_addrs src_corner_addrs; u32 *dest = ctx->cmdbuf_virt; unsigned int src_h, src_w, src_x, src_y; @@ -330,22 +329,22 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx, /* * Calculate the source framebuffer base address with offset pixel. */ - src_offsets = rga_get_addr_offset(&ctx->in, offset, - src_x, src_y, src_w, src_h); + src_corner_addrs = rga_get_corner_addrs(&ctx->in, addrs, + src_x, src_y, src_w, src_h); dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.y_off; + src_corner_addrs.left_top.y_addr; dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.u_off; + src_corner_addrs.left_top.u_addr; dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.v_off; + src_corner_addrs.left_top.v_addr; } static void rga_cmd_set_dst_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_addr_offset *dst_offset; - struct rga_corners_addr_offset offsets; + struct rga_addrs *dst_addrs; + struct rga_corners_addrs corner_addrs; u32 *dest = ctx->cmdbuf_virt; unsigned int dst_h, dst_w, dst_x, dst_y; unsigned int mir_mode = 0; @@ -379,15 +378,15 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, /* * Configure the dest framebuffer base address with pixel offset. */ - offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h); - dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode); + corner_addrs = rga_get_corner_addrs(&ctx->out, addrs, dst_x, dst_y, dst_w, dst_h); + dst_addrs = rga_lookup_draw_pos(&corner_addrs, rot_mode, mir_mode); dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->y_off; + dst_addrs->y_addr; dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->u_off; + dst_addrs->u_addr; dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->v_off; + dst_addrs->v_addr; } static void rga_cmd_set_mode(struct rga_ctx *ctx) @@ -426,8 +425,8 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_src_info(ctx, &src->offset); - rga_cmd_set_dst_info(ctx, &dst->offset); + rga_cmd_set_src_info(ctx, &src->dma_addrs); + rga_cmd_set_dst_info(ctx, &dst->dma_addrs); rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index cee2e75ea89f..bf21a57555a5 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -97,10 +97,10 @@ struct rockchip_rga { const struct rga_hw *hw; }; -struct rga_addr_offset { - unsigned int y_off; - unsigned int u_off; - unsigned int v_off; +struct rga_addrs { + dma_addr_t y_addr; + dma_addr_t u_addr; + dma_addr_t v_addr; }; struct rga_vb_buffer { @@ -112,8 +112,8 @@ struct rga_vb_buffer { dma_addr_t dma_desc_pa; size_t n_desc; - /* Plane offsets of this buffer into the mapping */ - struct rga_addr_offset offset; + /* Plane DMA addresses after the MMU mapping of the buffer */ + struct rga_addrs dma_addrs; }; static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) From 5873cb8ce139bdeb60747d672667de19736b759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:25 +0200 Subject: [PATCH 1390/5214] media: rockchip: rga: support external iommus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for the RGA3 add support for external iommus. This is a transition step to just disable the RGA2 specific mmu table setup code. Currently a simple rga_hw struct field is used to set the internal iommu. But to handle the case of more sophisticated detection mechanisms (e.g. check for an iommu property in the device tree), it is abstracted by an inline function. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/Kconfig | 1 + drivers/media/platform/rockchip/rga/rga-buf.c | 31 +++++++++++++------ drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 11 +++++-- drivers/media/platform/rockchip/rga/rga.h | 6 ++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig index 727a0f6ea466..846e555829f3 100644 --- a/drivers/media/platform/rockchip/rga/Kconfig +++ b/drivers/media/platform/rockchip/rga/Kconfig @@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_RGA depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index ab9554c1c4cd..cd6904d5fe5a 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "rga.h" @@ -82,6 +83,9 @@ static int rga_buf_init(struct vb2_buffer *vb) if (IS_ERR(f)) return PTR_ERR(f); + if (!rga_has_internal_iommu(rga)) + return 0; + n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); rbuf->n_desc = n_desc; @@ -136,17 +140,21 @@ static int rga_buf_prepare(struct vb2_buffer *vb) for (i = 0; i < vb->num_planes; i++) { vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage); - /* Create local MMU table for RGA */ - n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], - rbuf->n_desc - curr_desc, - vb2_dma_sg_plane_desc(vb, i)); - if (n_desc < 0) { - v4l2_err(&ctx->rga->v4l2_dev, - "Failed to map video buffer to RGA\n"); - return n_desc; + if (rga_has_internal_iommu(ctx->rga)) { + /* Create local MMU table for RGA */ + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], + rbuf->n_desc - curr_desc, + vb2_dma_sg_plane_desc(vb, i)); + if (n_desc < 0) { + v4l2_err(&ctx->rga->v4l2_dev, + "Failed to map video buffer to RGA\n"); + return n_desc; + } + dma_addrs[i] = curr_desc << PAGE_SHIFT; + curr_desc += n_desc; + } else { + dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i); } - dma_addrs[i] = curr_desc << PAGE_SHIFT; - curr_desc += n_desc; } /* Fill the remaining planes */ @@ -176,6 +184,9 @@ static void rga_buf_cleanup(struct vb2_buffer *vb) struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct rockchip_rga *rga = ctx->rga; + if (!rga_has_internal_iommu(rga)) + return; + dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc), rbuf->dma_desc, rbuf->dma_desc_pa); } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 99cf57d5ba89..73584706a47e 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -577,6 +577,7 @@ static struct rga_fmt formats[] = { const struct rga_hw rga2_hw = { .card_type = "rga2", + .has_internal_iommu = true, .formats = formats, .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 91775b43ff61..e3c99c3f7c5b 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "rga.h" @@ -98,7 +99,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; - src_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + src_vq->mem_ops = &vb2_dma_sg_memops; + else + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->gfp_flags = __GFP_DMA32; src_vq->buf_struct_size = sizeof(struct rga_vb_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -113,7 +117,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &rga_qops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + dst_vq->mem_ops = &vb2_dma_sg_memops; + else + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->gfp_flags = __GFP_DMA32; dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index bf21a57555a5..b180df5c4837 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -150,6 +150,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) struct rga_hw { const char *card_type; + bool has_internal_iommu; struct rga_fmt *formats; u32 num_formats; size_t cmdbuf_size; @@ -165,6 +166,11 @@ struct rga_hw { void (*get_version)(struct rockchip_rga *rga); }; +static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) +{ + return rga->hw->has_internal_iommu; +} + extern const struct rga_hw rga2_hw; #endif From 6b7c18553720a2a9594f88940c47d4a7ff9687b1 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Thu, 21 May 2026 00:44:26 +0200 Subject: [PATCH 1391/5214] media: rockchip: rga: share the interrupt when an external iommu is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RGA3 and the corresponding iommu share the interrupt. So in that case, request a shared interrupt so that the iommu driver can request it as well. Signed-off-by: Michael Olbrich Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e3c99c3f7c5b..cda3cecb1ce8 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -767,7 +767,8 @@ static int rga_probe(struct platform_device *pdev) goto err_put_clk; } - ret = devm_request_irq(rga->dev, irq, rga_isr, 0, + ret = devm_request_irq(rga->dev, irq, rga_isr, + rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED, dev_name(rga->dev), rga); if (ret < 0) { dev_err(rga->dev, "failed to request irq\n"); From d11d812ddea312659c28d4402c059f88f2b4e348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:27 +0200 Subject: [PATCH 1392/5214] media: rockchip: rga: remove size from rga_frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size member is only used for the mmu page table mapping. Therefore avoid storing the value and instead only calculate it in place. This also avoids the calculation entirely when an external iommu is used. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 6 +++++- drivers/media/platform/rockchip/rga/rga.c | 8 ++------ drivers/media/platform/rockchip/rga/rga.h | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index cd6904d5fe5a..092c2bdf6b67 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -79,6 +79,8 @@ static int rga_buf_init(struct vb2_buffer *vb) struct rockchip_rga *rga = ctx->rga; struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); size_t n_desc = 0; + u32 size = 0; + u8 i; if (IS_ERR(f)) return PTR_ERR(f); @@ -86,7 +88,9 @@ static int rga_buf_init(struct vb2_buffer *vb) if (!rga_has_internal_iommu(rga)) return 0; - n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); + for (i = 0; i < f->pix.num_planes; i++) + size += f->pix.plane_fmt[i].sizeimage; + n_desc = DIV_ROUND_UP(size, PAGE_SIZE); rbuf->n_desc = n_desc; rbuf->dma_desc = dma_alloc_coherent(rga->dev, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index cda3cecb1ce8..e44e81814b7c 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -268,7 +268,6 @@ static int rga_open(struct file *file) }; def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * def_height; ctx = kzalloc_obj(*ctx); if (!ctx) @@ -481,9 +480,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->size = 0; - for (i = 0; i < pix_fmt->num_planes; i++) - frm->size += pix_fmt->plane_fmt[i].sizeimage; frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; @@ -508,10 +504,10 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) ctx->cmdbuf_dirty = true; v4l2_dbg(debug, 1, &rga->v4l2_dev, - "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", + "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, - frm->stride, frm->size); + frm->stride); for (i = 0; i < pix_fmt->num_planes; i++) { v4l2_dbg(debug, 1, &rga->v4l2_dev, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index b180df5c4837..d47c6821ce2c 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -34,7 +34,6 @@ struct rga_frame { /* Variables that can calculated once and reused */ u32 stride; - u32 size; }; struct rga_dma_desc { From 918c4589a0e2bb7338b556c28ad5cb36e8e27795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:28 +0200 Subject: [PATCH 1393/5214] media: rockchip: rga: remove stride from rga_frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the stride variable from rga_frame. Despite the comment it didn't involve any calculation and is just a copy of the plane_fmt[0].bytesperline value. Therefore avoid this struct member and use the bytesperline value directly in the places where it is required. Also drop the dependency on the depth format member, which was only used to calculate the stride of the default format. This is already done by the v4l2_fill_pixfmt_mp_aligned helper and used as stride in try_fmt. Therefore using it's value also for the default format stride is just more consistent. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 36 +++++++------------- drivers/media/platform/rockchip/rga/rga.c | 5 +-- drivers/media/platform/rockchip/rga/rga.h | 4 --- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 73584706a47e..fac3975e2d0c 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -44,7 +44,7 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, struct rga_addrs *lt, *lb, *rt, *rb; const struct v4l2_format_info *format_info; unsigned int x_div = 0, - y_div = 0, uv_stride = 0, pixel_width = 0; + y_div = 0, y_stride = 0, uv_stride = 0, pixel_width = 0; lt = &corner_addrs.left_top; lb = &corner_addrs.left_bottom; @@ -61,14 +61,15 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, else x_div = 1; y_div = format_info->vdiv; - uv_stride = frm->stride / x_div; - pixel_width = frm->stride / frm->pix.width; + y_stride = frm->pix.plane_fmt[0].bytesperline; + uv_stride = y_stride / x_div; + pixel_width = y_stride / frm->pix.width; - lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width; + lt->y_addr = addrs->y_addr + y * y_stride + x * pixel_width; lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div; lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div; - lb->y_addr = lt->y_addr + (h - 1) * frm->stride; + lb->y_addr = lt->y_addr + (h - 1) * y_stride; lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride; lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride; @@ -169,6 +170,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_src_act_info src_act_info; union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; + u32 in_stride, out_stride; src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; @@ -291,13 +293,15 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) * Calculate the framebuffer virtual strides and active size, * note that the step of vir_stride / vir_width is 4 byte words */ - src_vir_info.data.vir_stride = ctx->in.stride >> 2; - src_vir_info.data.vir_width = ctx->in.stride >> 2; + in_stride = ctx->in.pix.plane_fmt[0].bytesperline; + src_vir_info.data.vir_stride = in_stride >> 2; + src_vir_info.data.vir_width = in_stride >> 2; src_act_info.data.act_height = src_h - 1; src_act_info.data.act_width = src_w - 1; - dst_vir_info.data.vir_stride = ctx->out.stride >> 2; + out_stride = ctx->out.pix.plane_fmt[0].bytesperline; + dst_vir_info.data.vir_stride = out_stride >> 2; dst_act_info.data.act_height = dst_h - 1; dst_act_info.data.act_width = dst_w - 1; @@ -481,97 +485,81 @@ static struct rga_fmt formats[] = { .fourcc = V4L2_PIX_FMT_ARGB32, .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_ABGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_XBGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_RGB24, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, }, { .fourcc = V4L2_PIX_FMT_BGR24, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, }, { .fourcc = V4L2_PIX_FMT_ARGB444, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_ARGB555, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_RGB565, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_NV21, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV61, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_NV12, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV12M, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_YUV420, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_YVU420, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, }, }; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e44e81814b7c..e52d0577b236 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -267,8 +267,6 @@ static int rga_open(struct file *file) .fmt = &rga->hw->formats[0], }; - def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; - ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; @@ -481,7 +479,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); - frm->stride = pix_fmt->plane_fmt[0].bytesperline; /* * Copy colorimetry from output to capture as required by the @@ -507,7 +504,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, - frm->stride); + pix_fmt->plane_fmt[0].bytesperline); for (i = 0; i < pix_fmt->num_planes; i++) { v4l2_dbg(debug, 1, &rga->v4l2_dev, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index d47c6821ce2c..227ab6c0532c 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -19,7 +19,6 @@ struct rga_fmt { u32 fourcc; - int depth; u8 color_swap; u8 hw_format; }; @@ -31,9 +30,6 @@ struct rga_frame { /* Image format */ struct rga_fmt *fmt; struct v4l2_pix_format_mplane pix; - - /* Variables that can calculated once and reused */ - u32 stride; }; struct rga_dma_desc { From d8427bb2efbb301632247ae15878d0e6fad41a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:29 +0200 Subject: [PATCH 1394/5214] media: rockchip: rga: move rga_fmt to rga-hw.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move rga_fmt to rga-hw in preparation of the RGA3 addition, as the struct contains many RGA2 specific values. They are used to write the correct register values quickly based on the chosen format. Therefore the pointer to the rga_fmt struct is kept but changed to an opaque void pointer outside of the rga-hw.h. To enumerate and set the correct formats, two helper functions need to be exposed in the rga_hw struct: enum_format just get's the vidioc_enum_fmt format and it's return value is also returned from vidioc_enum_fmt. This is a simple pass-through, as the implementation is very simple. adjust_and_map_format is a simple abstraction around the previous rga_find_format. But unlike rga_find_format, it always returns a valid format. Therefore the passed format value is also a pointer to update it in case the values are not supported by the hardware. Due to the RGA3 supporting different formats on the capture and output side, an additional parameter is_capture has been added to support this use-case. The additional ctx parameter is also added to allow the RGA3 to limit the colorimetry only if an RGB<->YCrCb transformation happens. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.c | 49 +++++++++++++++---- drivers/media/platform/rockchip/rga/rga-hw.h | 8 +++ drivers/media/platform/rockchip/rga/rga.c | 41 ++++++---------- drivers/media/platform/rockchip/rga/rga.h | 14 ++---- 5 files changed, 67 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 092c2bdf6b67..c0ea6003336b 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -162,7 +162,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) } /* Fill the remaining planes */ - info = v4l2_format_info(f->fmt->fourcc); + info = v4l2_format_info(f->pix.pixelformat); for (i = info->mem_planes; i < info->comp_planes; i++) dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i); diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index fac3975e2d0c..616ea1bc1131 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -171,6 +171,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; u32 in_stride, out_stride; + struct rga_fmt *in_fmt = ctx->in.fmt; + struct rga_fmt *out_fmt = ctx->out.fmt; src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; @@ -186,18 +188,18 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2]; dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2]; - src_info.data.format = ctx->in.fmt->hw_format; - src_info.data.swap = ctx->in.fmt->color_swap; - dst_info.data.format = ctx->out.fmt->hw_format; - dst_info.data.swap = ctx->out.fmt->color_swap; + src_info.data.format = in_fmt->hw_format; + src_info.data.swap = in_fmt->color_swap; + dst_info.data.format = out_fmt->hw_format; + dst_info.data.swap = out_fmt->color_swap; /* * CSC mode must only be set when the colorspace families differ between * input and output. It must remain unset (zeroed) if both are the same. */ - if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_YUV(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_RGB(out_fmt->hw_format)) { switch (ctx->in.pix.colorspace) { case V4L2_COLORSPACE_REC709: src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; @@ -208,8 +210,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } } - if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_RGB(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_YUV(out_fmt->hw_format)) { switch (ctx->out.pix.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; @@ -563,11 +565,36 @@ static struct rga_fmt formats[] = { }, }; +static void *rga_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + + if (!format) + return &formats[0]; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == format->pixelformat) + return &formats[i]; + } + + format->pixelformat = formats[0].fourcc; + return &formats[0]; +} + +static int rga_enum_format(struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + return 0; +} + const struct rga_hw rga2_hw = { .card_type = "rga2", .has_internal_iommu = true, - .formats = formats, - .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, .min_width = MIN_WIDTH, .max_width = MAX_WIDTH, @@ -580,4 +607,6 @@ const struct rga_hw rga2_hw = { .start = rga_hw_start, .handle_irq = rga_handle_irq, .get_version = rga_get_version, + .adjust_and_map_format = rga_adjust_and_map_format, + .enum_format = rga_enum_format, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 805ec23e5e3f..14ffa5ebd453 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,6 +6,8 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ +#include + #define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ @@ -431,4 +433,10 @@ union rga_pat_con { } data; }; +struct rga_fmt { + u32 fourcc; + u8 color_swap; + u8 hw_format; +}; + #endif diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e52d0577b236..1878b4e26360 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -232,17 +232,6 @@ int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, return 0; } -static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < rga->hw->num_formats; i++) { - if (rga->hw->formats[i].fourcc == pixelformat) - return &rga->hw->formats[i]; - } - return NULL; -} - struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -264,7 +253,6 @@ static int rga_open(struct file *file) .crop.top = 0, .crop.width = def_width, .crop.height = def_height, - .fmt = &rga->hw->formats[0], }; ctx = kzalloc_obj(*ctx); @@ -286,9 +274,12 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; - v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc, + ctx->in.fmt = rga->hw->adjust_and_map_format(ctx, &ctx->in.pix, true); + v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.pix.pixelformat, def_width, def_height, rga->hw->stride_alignment); - v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc, + ctx->out.fmt = + rga->hw->adjust_and_map_format(ctx, &ctx->out.pix, false); + v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.pix.pixelformat, def_width, def_height, rga->hw->stride_alignment); if (mutex_lock_interruptible(&rga->mutex)) { @@ -370,13 +361,11 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rockchip_rga *rga = video_drvdata(file); - struct rga_fmt *fmt; + int ret; - if (f->index >= rga->hw->num_formats) - return -EINVAL; - - fmt = &rga->hw->formats[f->index]; - f->pixelformat = fmt->fourcc; + ret = rga->hw->enum_format(f); + if (ret != 0) + return ret; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) @@ -411,7 +400,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = file_to_rga_ctx(file); const struct rga_hw *hw = ctx->rga->hw; - struct rga_fmt *fmt; struct v4l2_frmsize_stepwise frmsize = { .min_width = hw->min_width, .max_width = hw->max_width, @@ -442,12 +430,10 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt->xfer_func = frm->pix.xfer_func; } - fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat); - if (!fmt) - fmt = &hw->formats[0]; + hw->adjust_and_map_format(ctx, pix_fmt, V4L2_TYPE_IS_OUTPUT(f->type)); v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); - v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc, + v4l2_fill_pixfmt_mp_aligned(pix_fmt, pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height, hw->stride_alignment); pix_fmt->field = V4L2_FIELD_NONE; @@ -478,7 +464,8 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); + frm->fmt = rga->hw->adjust_and_map_format(ctx, pix_fmt, + V4L2_TYPE_IS_OUTPUT(f->type)); /* * Copy colorimetry from output to capture as required by the @@ -503,7 +490,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", - &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, + &pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height, pix_fmt->plane_fmt[0].bytesperline); for (i = 0; i < pix_fmt->num_planes; i++) { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 227ab6c0532c..effe364a86b0 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -17,18 +17,12 @@ #define DEFAULT_WIDTH 100 #define DEFAULT_HEIGHT 100 -struct rga_fmt { - u32 fourcc; - u8 color_swap; - u8 hw_format; -}; - struct rga_frame { /* Crop */ struct v4l2_rect crop; /* Image format */ - struct rga_fmt *fmt; + void *fmt; struct v4l2_pix_format_mplane pix; }; @@ -146,8 +140,6 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) struct rga_hw { const char *card_type; bool has_internal_iommu; - struct rga_fmt *formats; - u32 num_formats; size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; @@ -159,6 +151,10 @@ struct rga_hw { struct rga_vb_buffer *src, struct rga_vb_buffer *dst); bool (*handle_irq)(struct rockchip_rga *rga); void (*get_version)(struct rockchip_rga *rga); + void *(*adjust_and_map_format)(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output); + int (*enum_format)(struct v4l2_fmtdesc *f); }; static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) From ded2efe97dcf6d888878bb99174f9484dd81c928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:30 +0200 Subject: [PATCH 1395/5214] media: rockchip: rga: add feature flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to the RGA3 addition add feature flags, which can limit the exposed feature set of the video device, like rotating or selection support. This is necessary as the RGA3 doesn't initially implement the full feature set currently exposed by the driver. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 3 +++ drivers/media/platform/rockchip/rga/rga.c | 20 ++++++++++++-------- drivers/media/platform/rockchip/rga/rga.h | 6 ++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 616ea1bc1131..be1bc8ddbd03 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -602,6 +602,9 @@ const struct rga_hw rga2_hw = { .max_height = MAX_HEIGHT, .max_scaling_factor = MAX_SCALING_FACTOR, .stride_alignment = 4, + .features = RGA_FEATURE_FLIP + | RGA_FEATURE_ROTATE + | RGA_FEATURE_BG_COLOR, .setup_cmdbuf = rga_hw_setup_cmdbuf, .start = rga_hw_start, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 1878b4e26360..8d60e94da32d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -177,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + if (rga->hw->features & RGA_FEATURE_FLIP) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_ROTATE, 0, 270, 90, 0); + if (rga->hw->features & RGA_FEATURE_ROTATE) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); + if (rga->hw->features & RGA_FEATURE_BG_COLOR) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); if (ctx->ctrl_handler.error) { int err = ctx->ctrl_handler.error; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index effe364a86b0..feaf40acd4ee 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -6,6 +6,7 @@ #ifndef __RGA_H__ #define __RGA_H__ +#include #include #include #include @@ -137,6 +138,10 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; +#define RGA_FEATURE_FLIP BIT(0) +#define RGA_FEATURE_ROTATE BIT(1) +#define RGA_FEATURE_BG_COLOR BIT(2) + struct rga_hw { const char *card_type; bool has_internal_iommu; @@ -145,6 +150,7 @@ struct rga_hw { u32 max_width, max_height; u8 max_scaling_factor; u8 stride_alignment; + u8 features; void (*setup_cmdbuf)(struct rga_ctx *ctx); void (*start)(struct rockchip_rga *rga, From bf0ee2589053b103426e1179b87f83ea512264df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:31 +0200 Subject: [PATCH 1396/5214] media: rockchip: rga: disable multi-core support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable multi-core support in preparation of the RGA3 addition. The RK3588 SoC features two equal RGA3 cores. This allows scheduling of the work between both cores, which is not yet implemented. Until it is implemented avoid exposing both cores as independent video devices to prevent an ABI breakage when multi-core support is added. This patch is copied from the Hantro driver patch to disable multi core support by Sebastian Reichel. See commit ccdeb8d57f7f ("media: hantro: Disable multicore support") Link: https://lore.kernel.org/all/20240618183816.77597-4-sebastian.reichel@collabora.com/ Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 8d60e94da32d..0152b8ef2da2 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -711,6 +711,49 @@ static int rga_parse_dt(struct rockchip_rga *rga) return 0; } +/* + * Some SoCs, like RK3588 have multiple identical RGA3 cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the main core can be used to cluster all cores together. + */ +static int rga_disable_multicore(struct device *dev) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_main_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; + + is_main_core = (dev->of_node == node); + + of_node_put(node); + + if (!is_main_core) { + dev_info(dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; + } + + return 0; +} + static int rga_probe(struct platform_device *pdev) { struct rockchip_rga *rga; @@ -721,6 +764,10 @@ static int rga_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV; + ret = rga_disable_multicore(&pdev->dev); + if (ret) + return ret; + rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL); if (!rga) return -ENOMEM; From 24a63d4c9d3ce3afc99f698cff62d79c457fc5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:32 +0200 Subject: [PATCH 1397/5214] media: rockchip: rga: add rga3 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the RGA3 unit contained in the RK3588. Only a basic feature set consisting of scaling and color conversion is implemented. Currently unimplemented features include: - Advanced formats like 10bit YUV, FBCE mode and Tile8x8 mode - Background color (V4L2_CID_BG_COLOR) - Configurable alpha value (V4L2_CID_ALPHA_COMPONENT) - Image flipping (V4L2_CID_HFLIP and V4L2_CID_VFLIP) - Image rotation (V4L2_CID_ROTATE) - Image cropping/composing (VIDIOC_S_SELECTION) - Only very basic output cropping for 1088 -> 1080 cases is implemented The register address defines were copied from the vendor Rockchip kernel sources and slightly adjusted to not start at 0 again for the cmd registers. During testing it has been noted that the scaling of the hardware is slightly incorrect. A test conversion of 128x128 RGBA to 256x256 RGBA causes a slightly larger scaling. The scaling is suddle, as it seems that the image is scaled to a 2px larger version and then cropped to it's final size. Trying to use the RGA2 scaling factor calculation didn't work. As the calculation matches the vendor kernel driver, no further research has been utilized to check if there may be some kind of better scaling factor calculation. Furthermore comparing the RGA3 conversion with the GStreamer videoconvertscale element, the chroma-site is different. A quick testing didn't reveal a chroma-site that creates the same image with the GStreamer Element. Also when converting from YUV to RGB the RGB values differ by 1 or 2. This doesn't seem to be a colorspace conversion issue but rather a slightly different precision on the calculation. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/Makefile | 2 +- drivers/media/platform/rockchip/rga/rga.c | 4 + drivers/media/platform/rockchip/rga/rga.h | 1 + drivers/media/platform/rockchip/rga/rga3-hw.c | 507 ++++++++++++++++++ drivers/media/platform/rockchip/rga/rga3-hw.h | 192 +++++++ 5 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/rockchip/rga/rga3-hw.c create mode 100644 drivers/media/platform/rockchip/rga/rga3-hw.h diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile index 1bbecdc3d8df..7326a548f3dc 100644 --- a/drivers/media/platform/rockchip/rga/Makefile +++ b/drivers/media/platform/rockchip/rga/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -rockchip-rga-objs := rga.o rga-hw.o rga-buf.o +rockchip-rga-objs := rga.o rga-hw.o rga3-hw.o rga-buf.o obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 0152b8ef2da2..b3cb6bf8eb86 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -913,6 +913,10 @@ static const struct of_device_id rockchip_rga_match[] = { .compatible = "rockchip,rk3399-rga", .data = &rga2_hw, }, + { + .compatible = "rockchip,rk3588-rga3", + .data = &rga3_hw, + }, {}, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index feaf40acd4ee..bd431534d0d3 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -169,5 +169,6 @@ static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) } extern const struct rga_hw rga2_hw; +extern const struct rga_hw rga3_hw; #endif diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c new file mode 100644 index 000000000000..ca1c268303dd --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025-2026 Pengutronix e.K. + * Author: Sven Püschel + */ + +#include +#include +#include +#include + +#include + +#include "rga3-hw.h" +#include "rga.h" + +static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst) +{ + /* + * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize + * of the TRM Part 2. The resulting scaling factor is a 16-bit value + * and therefore normalized with 2^16. + * + * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case, + * it didn't work as the value always exceeds 16 bit. Flipping the + * factors results in a correct up-scaling. This is possible as the + * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does + * an up or downscale. + * + * The scaling factor can potentially cause a slightly larger scaling + * (e.g. 1/2px larger scale and then cropped to the destination size). + * This can be seen when scaling 128x128px RGBA to 256x256px RGBA. + * The RGA2 scaling factor calculation (without the various +/-1 + * doesn't work for the RGA3. It's assumed that this is an hardware + * accuracy limitation, as the vendor kernel driver uses the same + * scaling factor calculation. + * + * With a scaling factor of 1.0 the calculation technically also + * overflows 16 bit. This isn't relevant, as in this case the + * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation. + */ + if (dst > src) { + if (((src - 1) << 16) % (dst - 1) == 0) + return ((src - 1) << 16) / (dst - 1) - 1; + else + return ((src - 1) << 16) / (dst - 1); + } else { + return ((dst - 1) << 16) / (src - 1) + 1; + } +} + +/* + * Check if the given format can be captured, as the RGA3 doesn't support all + * input formats also on it's output. + */ +static bool rga3_can_capture(const struct rga3_fmt *fmt) +{ + return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT; +} + +/* + * Map the transformations to the RGA3 command buffer. + * Currently this is just the scaling settings and a fixed alpha value. + */ +static void rga3_cmd_set_trans_info(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int src_h, src_w, dst_h, dst_w; + unsigned int reg; + u16 hor_scl_fac, ver_scl_fac; + const struct rga3_fmt *in = ctx->in.fmt; + + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w) + | FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w) + | FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h) + | FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h); + + hor_scl_fac = rga3_get_scaling(src_w, dst_w); + ver_scl_fac = rga3_get_scaling(src_h, dst_h); + reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac) + | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac); + + if (v4l2_format_info(in->fourcc)->has_alpha) { + /* copy alpha from input */ + reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + } else { + /* just use a 255 alpha value */ + reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + } +} + +static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +/* Map the input pixel format to win0 of the comamnd buffer. */ +static void rga3_cmd_set_win0_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *in = ctx->in.fmt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *in_fmt, *out_fmt; + unsigned int act_h, act_w, src_h, src_w; + bool r2y, y2r; + u8 rd_format; + const struct v4l2_pix_format_mplane *csc_pix; + u8 csc_mode; + unsigned int reg; + + act_h = ctx->in.pix.height; + act_w = ctx->in.pix.width; + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + + in_fmt = v4l2_format_info(in->fourcc); + out_fmt = v4l2_format_info(out->fourcc); + r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt); + y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt); + + /* The Hardware only supports formats with 1/2 planes */ + if (in_fmt->comp_planes == 2) + rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + rd_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format and CSC */ + csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix; + switch (csc_pix->ycbcr_enc) { + case V4L2_YCBCR_ENC_BT2020: + csc_mode = RGA3_WIN_CSC_MODE_BT2020_L; + break; + case V4L2_YCBCR_ENC_709: + csc_mode = RGA3_WIN_CSC_MODE_BT709_L; + break; + default: /* should be fixed to BT601 in adjust_and_map_format */ + if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE) + csc_mode = RGA3_WIN_CSC_MODE_BT601_L; + else + csc_mode = RGA3_WIN_CSC_MODE_BT601_F; + break; + } + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1) + | FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format) + | FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap) + | FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap) + | FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format) + | FIELD_PREP(RGA3_WIN_R2Y, r2y) + | FIELD_PREP(RGA3_WIN_Y2R, y2r) + | FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode); + + /* set stride */ + reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->in.pix.num_planes == 2) + cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + + /* set size */ + reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w) + | FIELD_PREP(RGA3_HEIGHT, act_h); + reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w) + | FIELD_PREP(RGA3_HEIGHT, src_h); +} + +/* Map the output pixel format to the command buffer */ +static void rga3_cmd_set_wr_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *out_fmt; + unsigned int dst_h, dst_w; + u8 wr_format; + unsigned int reg; + + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + out_fmt = v4l2_format_info(out->fourcc); + + /* The Hardware only supports formats with 1/2 planes */ + if (out_fmt->comp_planes == 2) + wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + wr_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format */ + reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format) + | FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap) + | FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap) + | FIELD_PREP(RGA3_WR_FORMAT, wr_format) + /* Use the max value to avoid limiting the write speed */ + | FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63); + + /* set stride */ + reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->out.pix.num_planes == 2) + cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + + /* Set size. + * As two inputs are not supported, we don't use win1. + * Therefore only set the size for win0. + */ + reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w) + | FIELD_PREP(RGA3_HEIGHT, dst_h); +} + +static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE); + + rga3_cmd_set_win0_format(ctx); + rga3_cmd_set_trans_info(ctx); + rga3_cmd_set_wr_format(ctx); +} + +static void rga3_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +{ + struct rga_ctx *ctx = rga->curr; + + rga3_cmd_set_win0_addr(ctx, &src->dma_addrs); + rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs); + + rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy); + + /* sync CMD buf for RGA */ + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); + + /* set to master mode and start the conversion */ + rga_write(rga, RGA3_SYS_CTRL, + FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER)); + rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1)); + rga_write(rga, RGA3_CMD_CTRL, + FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1)); +} + +static bool rga3_handle_irq(struct rockchip_rga *rga) +{ + u32 intr; + + intr = rga_read(rga, RGA3_INT_RAW); + /* clear all interrupts */ + rga_write(rga, RGA3_INT_CLR, intr); + + return FIELD_GET(RGA3_INT_FRM_DONE, intr); +} + +static void rga3_get_version(struct rockchip_rga *rga) +{ + u32 version = rga_read(rga, RGA3_VERSION_NUM); + + rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version); + rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version); +} + +static struct rga3_fmt rga3_formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB24, + .hw_format = RGA3_COLOR_FMT_BGR888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .hw_format = RGA3_COLOR_FMT_BGR888, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBA32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .hw_format = RGA3_COLOR_FMT_BGR565, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + /* Input only formats last to keep rga3_enum_format simple */ + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRA32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRX32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, +}; + +static int rga3_enum_format(struct v4l2_fmtdesc *f) +{ + struct rga3_fmt *fmt; + + if (f->index >= ARRAY_SIZE(rga3_formats)) + return -EINVAL; + + fmt = &rga3_formats[f->index]; + if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt)) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static void *rga3_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + const struct v4l2_format_info *format_info; + const struct v4l2_pix_format_mplane *other_format; + const struct v4l2_format_info *other_format_info; + + if (!format) + return &rga3_formats[0]; + + format_info = v4l2_format_info(format->pixelformat); + other_format = is_output ? &ctx->out.pix : &ctx->in.pix; + other_format_info = v4l2_format_info(other_format->pixelformat); + + if ((v4l2_is_format_rgb(format_info) && + v4l2_is_format_yuv(other_format_info)) || + (v4l2_is_format_yuv(format_info) && + v4l2_is_format_rgb(other_format_info))) { + /* + * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions + * Additionally BT709 and BT2020 only support limited range YUV. + */ + switch (format->ycbcr_enc) { + case V4L2_YCBCR_ENC_601: + /* supports full and limited range */ + break; + case V4L2_YCBCR_ENC_709: + case V4L2_YCBCR_ENC_BT2020: + format->quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + default: + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) { + if (!is_output && !rga3_can_capture(&rga3_formats[i])) + continue; + + if (rga3_formats[i].fourcc == format->pixelformat) + return &rga3_formats[i]; + } + + format->pixelformat = rga3_formats[0].fourcc; + return &rga3_formats[0]; +} + +const struct rga_hw rga3_hw = { + .card_type = "rga3", + .has_internal_iommu = false, + .cmdbuf_size = RGA3_CMDBUF_SIZE, + .min_width = RGA3_MIN_WIDTH, + .min_height = RGA3_MIN_HEIGHT, + /* use output size, as it's a bit smaller than the input size */ + .max_width = RGA3_MAX_OUTPUT_WIDTH, + .max_height = RGA3_MAX_OUTPUT_HEIGHT, + .max_scaling_factor = RGA3_MAX_SCALING_FACTOR, + .stride_alignment = 16, + .features = 0, + + .setup_cmdbuf = rga3_hw_setup_cmdbuf, + .start = rga3_hw_start, + .handle_irq = rga3_handle_irq, + .get_version = rga3_get_version, + .enum_format = rga3_enum_format, + .adjust_and_map_format = rga3_adjust_and_map_format, +}; diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.h b/drivers/media/platform/rockchip/rga/rga3-hw.h new file mode 100644 index 000000000000..85fd8ae257ec --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) Pengutronix e.K. + * Author: Sven Püschel + */ +#ifndef __RGA3_HW_H__ +#define __RGA3_HW_H__ + +#include +#include + +#define RGA3_CMDBUF_SIZE 0xb8 + +#define RGA3_MIN_WIDTH 128 +#define RGA3_MIN_HEIGHT 128 +#define RGA3_MAX_INPUT_WIDTH (8192 - 16) +#define RGA3_MAX_INPUT_HEIGHT (8192 - 16) +#define RGA3_MAX_OUTPUT_WIDTH (8192 - 64) +#define RGA3_MAX_OUTPUT_HEIGHT (8192 - 64) +#define RGA3_MAX_SCALING_FACTOR 8 +#define RGA3_RESET_TIMEOUT 1000 + +/* Registers address */ +/* sys reg */ +#define RGA3_SYS_CTRL 0x000 +#define RGA3_CMD_CTRL 0x004 +#define RGA3_CMD_ADDR 0x008 +#define RGA3_MI_GROUP_CTRL 0x00c +#define RGA3_ARQOS_CTRL 0x010 +#define RGA3_VERSION_NUM 0x018 +#define RGA3_VERSION_TIM 0x01c +#define RGA3_INT_EN 0x020 +#define RGA3_INT_RAW 0x024 +#define RGA3_INT_MSK 0x028 +#define RGA3_INT_CLR 0x02c +#define RGA3_RO_SRST 0x030 +#define RGA3_STATUS0 0x034 +#define RGA3_SCAN_CNT 0x038 +#define RGA3_CMD_STATE 0x040 + +/* cmd reg */ +#define RGA3_WIN0_RD_CTRL 0x100 +#define RGA3_FIRST_CMD_REG RGA3_WIN0_RD_CTRL +#define RGA3_WIN0_Y_BASE 0x110 +#define RGA3_WIN0_U_BASE 0x114 +#define RGA3_WIN0_V_BASE 0x118 +#define RGA3_WIN0_VIR_STRIDE 0x11c +#define RGA3_WIN0_FBC_OFF 0x120 +#define RGA3_WIN0_SRC_SIZE 0x124 +#define RGA3_WIN0_ACT_OFF 0x128 +#define RGA3_WIN0_ACT_SIZE 0x12c +#define RGA3_WIN0_DST_SIZE 0x130 +#define RGA3_WIN0_SCL_FAC 0x134 +#define RGA3_WIN0_UV_VIR_STRIDE 0x138 +#define RGA3_WIN1_RD_CTRL 0x140 +#define RGA3_WIN1_Y_BASE 0x150 +#define RGA3_WIN1_U_BASE 0x154 +#define RGA3_WIN1_V_BASE 0x158 +#define RGA3_WIN1_VIR_STRIDE 0x15c +#define RGA3_WIN1_FBC_OFF 0x160 +#define RGA3_WIN1_SRC_SIZE 0x164 +#define RGA3_WIN1_ACT_OFF 0x168 +#define RGA3_WIN1_ACT_SIZE 0x16c +#define RGA3_WIN1_DST_SIZE 0x170 +#define RGA3_WIN1_SCL_FAC 0x174 +#define RGA3_WIN1_UV_VIR_STRIDE 0x178 +#define RGA3_OVLP_CTRL 0x180 +#define RGA3_OVLP_OFF 0x184 +#define RGA3_OVLP_TOP_KEY_MIN 0x188 +#define RGA3_OVLP_TOP_KEY_MAX 0x18c +#define RGA3_OVLP_TOP_CTRL 0x190 +#define RGA3_OVLP_BOT_CTRL 0x194 +#define RGA3_OVLP_TOP_ALPHA 0x198 +#define RGA3_OVLP_BOT_ALPHA 0x19c +#define RGA3_WR_CTRL 0x1a0 +#define RGA3_WR_FBCE_CTRL 0x1a4 +#define RGA3_WR_VIR_STRIDE 0x1a8 +#define RGA3_WR_PL_VIR_STRIDE 0x1ac +#define RGA3_WR_Y_BASE 0x1b0 +#define RGA3_WR_U_BASE 0x1b4 +#define RGA3_WR_V_BASE 0x1b8 + +/* Registers value */ +#define RGA3_COLOR_FMT_YUV420 0x0 +#define RGA3_COLOR_FMT_YUV422 0x1 +#define RGA3_COLOR_FMT_YUV420_10B 0x2 +#define RGA3_COLOR_FMT_YUV422_10B 0x3 +/* + * Use memory ordering names + * instead of the datasheet naming RGB formats in big endian order + */ +#define RGA3_COLOR_FMT_BGR565 0x4 +#define RGA3_COLOR_FMT_BGR888 0x5 +#define RGA3_COLOR_FMT_FIRST_HAS_ALPHA RGA3_COLOR_FMT_BGRA8888 +#define RGA3_COLOR_FMT_BGRA8888 0x6 +#define RGA3_COLOR_FMT_LAST_OUTPUT RGA3_COLOR_FMT_BGRA8888 +/* the following are only supported as inputs */ +#define RGA3_COLOR_FMT_ABGR8888 0x7 +/* + * the following seem to be unnecessary, + * as they can be achieved with RB swaps + */ +#define RGA3_COLOR_FMT_RGBA8888 0x8 +#define RGA3_COLOR_FMT_ARGB8888 0x9 + +#define RGA3_RDWR_FORMAT_SEMI_PLANAR 0x1 +#define RGA3_RDWR_FORMAT_INTERLEAVED 0x2 + +#define RGA3_CMD_MODE_MASTER 0x1 + +#define RGA3_WIN_CSC_MODE_BT601_L 0x0 +#define RGA3_WIN_CSC_MODE_BT709_L 0x1 +#define RGA3_WIN_CSC_MODE_BT601_F 0x2 +#define RGA3_WIN_CSC_MODE_BT2020_L 0x3 + +/* RGA masks */ +/* SYS_CTRL */ +#define RGA3_CCLK_SRESET BIT(4) +#define RGA3_ACLK_SRESET BIT(3) +#define RGA3_CMD_MODE BIT(1) + +/* CMD_CTRL */ +#define RGA3_CMD_LINE_START_PULSE BIT(0) + +/* VERSION_NUM */ +#define RGA3_VERSION_NUM_MAJOR GENMASK(31, 28) +#define RGA3_VERSION_NUM_MINOR GENMASK(27, 20) + +/* INT_* */ +#define RGA3_INT_FRM_DONE BIT(0) +#define RGA3_INT_DMA_READ_BUS_ERR BIT(2) +#define RGA3_INT_WIN0_FBC_DEC_ERR BIT(5) +#define RGA3_INT_WIN0_HOR_ERR BIT(6) +#define RGA3_INT_WIN0_VER_ERR BIT(7) +#define RGA3_INT_WR_VER_ERR BIT(13) +#define RGA3_INT_WR_HOR_ERR BIT(14) +#define RGA3_INT_WR_BUS_ERR BIT(15) +#define RGA3_INT_WIN0_IN_FIFO_WR_ERR BIT(16) +#define RGA3_INT_WIN0_IN_FIFO_RD_ERR BIT(17) +#define RGA3_INT_WIN0_HOR_FIFO_WR_ERR BIT(18) +#define RGA3_INT_WIN0_HOR_FIFO_RD_ERR BIT(19) +#define RGA3_INT_WIN0_VER_FIFO_WR_ERR BIT(20) +#define RGA3_INT_WIN0_VER_FIFO_RD_ERR BIT(21) + +/* RO_SRST */ +#define RGA3_RO_SRST_DONE GENMASK(5, 0) + +/* *_SIZE */ +#define RGA3_HEIGHT GENMASK(28, 16) +#define RGA3_WIDTH GENMASK(12, 0) + +/* SCL_FAC */ +#define RGA3_SCALE_VER_FAC GENMASK(31, 16) +#define RGA3_SCALE_HOR_FAC GENMASK(15, 0) + +/* WINx_CTRL */ +#define RGA3_WIN_CSC_MODE GENMASK(27, 26) +#define RGA3_WIN_R2Y BIT(25) +#define RGA3_WIN_Y2R BIT(24) +#define RGA3_WIN_SCALE_VER_UP BIT(23) +#define RGA3_WIN_SCALE_VER_BYPASS BIT(22) +#define RGA3_WIN_SCALE_HOR_UP BIT(21) +#define RGA3_WIN_SCALE_HOR_BYPASS BIT(20) +#define RGA3_WIN_YC_SWAP BIT(13) +#define RGA3_WIN_RBUV_SWAP BIT(12) +#define RGA3_WIN_RD_FORMAT GENMASK(9, 8) +#define RGA3_WIN_PIC_FORMAT GENMASK(7, 4) +#define RGA3_WIN_ENABLE BIT(0) + +/* COLOR_CTRL */ +#define RGA3_OVLP_GLOBAL_ALPHA GENMASK(23, 16) +#define RGA3_OVLP_COLOR_MODE BIT(0) + +/* ALPHA_CTRL */ +#define RGA3_ALPHA_SELECT_MODE BIT(4) +#define RGA3_ALPHA_BLEND_MODE GENMASK(3, 2) + +/* WR_CTRL */ +#define RGA3_WR_YC_SWAP BIT(20) +#define RGA3_WR_SW_OUTSTANDING_MAX GENMASK(18, 13) +#define RGA3_WR_RBUV_SWAP BIT(12) +#define RGA3_WR_FORMAT GENMASK(9, 8) +#define RGA3_WR_PIC_FORMAT GENMASK(7, 4) + +struct rga3_fmt { + u32 fourcc; + u8 hw_format; + bool rbuv_swap; + bool yc_swap; +}; + +#endif From 2c869b6969f3061cbbdab587f4c0a88bd7fc3cc9 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 6 May 2026 21:41:16 +0900 Subject: [PATCH 1398/5214] media: cedrus: clean up media device on probe failure cedrus_probe() initializes the media device before registering the video device, the media controller, and the media device. If any of those later steps fails, probe returns without calling media_device_cleanup(), so the media device internals initialized by media_device_init() are left behind. Add a media-device cleanup label to the probe unwind path and route video registration failures through it as well. Fixes: 50e761516f2b8c ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Reviewed-by: Paul Kocialkowski Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index ee0e286add67..bbd186b8035b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -508,7 +508,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_m2m; + goto err_media; } v4l2_info(&dev->v4l2_dev, @@ -534,7 +534,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_unregister_media_controller(dev->m2m_dev); err_video: video_unregister_device(&dev->vfd); -err_m2m: +err_media: + media_device_cleanup(&dev->mdev); v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); From 2a1a564adab12eef528970611a74bbe3d6f1a422 Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Fri, 8 May 2026 10:57:31 +0500 Subject: [PATCH 1399/5214] staging: media: meson: fix typo in codec files Fix spelling mistake: substracted -> subtracted Signed-off-by: Maha Maryam Javaid Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/codec_h264.c | 2 +- drivers/staging/media/meson/vdec/codec_mpeg12.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c index 89e0f8624e5b..a6074de15118 100644 --- a/drivers/staging/media/meson/vdec/codec_h264.c +++ b/drivers/staging/media/meson/vdec/codec_h264.c @@ -16,7 +16,7 @@ #define SIZE_SEI (8 * SZ_1K) /* - * Offset added by the firmware which must be substracted + * Offset added by the firmware which must be subtracted * from the workspace phyaddr */ #define WORKSPACE_BUF_OFFSET 0x1000000 diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.c b/drivers/staging/media/meson/vdec/codec_mpeg12.c index 76e9ca7191ab..ab4374e3b2ef 100644 --- a/drivers/staging/media/meson/vdec/codec_mpeg12.c +++ b/drivers/staging/media/meson/vdec/codec_mpeg12.c @@ -12,7 +12,7 @@ #include "vdec_helpers.h" #define SIZE_WORKSPACE SZ_128K -/* Offset substracted by the firmware from the workspace paddr */ +/* Offset subtracted by the firmware from the workspace paddr */ #define WORKSPACE_OFFSET (5 * SZ_1K) /* map firmware registers to known MPEG1/2 functions */ From 940f161f734b25f175a95d2684c2021f6323693a Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 20 May 2026 10:10:41 +0530 Subject: [PATCH 1400/5214] media: meson: vdec: Fix memory leak in error path of vdec_open The vdec_open() function previously jumped directly to err_m2m_release when vdec_init_ctrls() failed, skipping release of the m2m context. This caused a resource leak. Fix it by introducing a proper err_m2m_ctx_release label that calls v4l2_m2m_ctx_release(sess->m2m_ctx) before releasing the m2m device. This was identified via kmemleak: unreferenced object 0xffff0000205d6878 (size 8): comm "v4l_id", pid 5289, jiffies 4294938580 hex dump (first 8 bytes): 40 d2 49 18 00 00 ff ff @.I..... backtrace (crc d3204599): kmemleak_alloc+0xc8/0xf0 __kvmalloc_node_noprof+0x60c/0x850 v4l2_ctrl_handler_init_class+0x1b4/0x2e8 [videodev] vdec_open+0x1f4/0x788 [meson_vdec] v4l2_open+0x144/0x460 [videodev] chrdev_open+0x1ac/0x500 do_dentry_open+0x3f0/0xfe8 vfs_open+0x68/0x320 do_open+0x2d8/0x9a8 path_openat+0x1d0/0x4f0 do_filp_open+0x190/0x380 do_sys_openat2+0xf8/0x1b0 __arm64_sys_openat+0x13c/0x1e8 invoke_syscall+0xdc/0x268 el0_svc_common.constprop.0+0x178/0x258 do_el0_svc+0x4c/0x70 Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver") Cc: stable@vger.kernel.org Signed-off-by: Anand Moon Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 4b77ec1af5a7..a039d925c0fe 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -889,7 +889,7 @@ static int vdec_open(struct file *file) ret = vdec_init_ctrls(sess); if (ret) - goto err_m2m_release; + goto err_m2m_ctx_release; sess->pixfmt_cap = formats[0].pixfmts_cap[0]; sess->fmt_out = &formats[0]; @@ -913,6 +913,8 @@ static int vdec_open(struct file *file) return 0; +err_m2m_ctx_release: + v4l2_m2m_ctx_release(sess->m2m_ctx); err_m2m_release: v4l2_m2m_release(sess->m2m_dev); err_free_sess: From ee681e7b6a269cd400969fbd3b2d85291a602528 Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Tue, 12 May 2026 16:41:21 +0000 Subject: [PATCH 1401/5214] staging: sm750fb: remove unused includes Remove unused header file includes from multiple source files to declutter the include sections. Also remove the unused CONFIG_MTRR include block after verifying that no MTRR interfaces are used by the driver. No functional changes. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260512164124.188210-2-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 12 ------------ drivers/staging/sm750fb/sm750_accel.c | 14 -------------- drivers/staging/sm750fb/sm750_cursor.c | 14 -------------- drivers/staging/sm750fb/sm750_hw.c | 17 ----------------- 4 files changed, 57 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 996a586a3727..d26e9fab846a 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -1,19 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include -#include -#include #include #include "sm750.h" diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 5ba1af274730..6e2120c4dce9 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -1,19 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include "sm750.h" #include "sm750_accel.h" diff --git a/drivers/staging/sm750fb/sm750_cursor.c b/drivers/staging/sm750fb/sm750_cursor.c index f0338e6e76b1..9d6bca3106f4 100644 --- a/drivers/staging/sm750fb/sm750_cursor.c +++ b/drivers/staging/sm750fb/sm750_cursor.c @@ -1,19 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include "sm750.h" #include "sm750_cursor.h" diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index 6f7c354a34a1..34a837fb4b64 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -1,23 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include -#include -#include -#ifdef CONFIG_MTRR -#include -#endif -#include -#include #include "sm750.h" #include "ddk750.h" From 145a22aab14661de14f14f514b9f796e084a711a Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Tue, 12 May 2026 16:41:22 +0000 Subject: [PATCH 1402/5214] staging: sm750fb: use early returns in frequency checks Invert the frequency validation conditions and use early returns to reduce nesting and improve readability. No functional changes. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260512164124.188210-3-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/ddk750_chip.c | 145 +++++++++++++------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/drivers/staging/sm750fb/ddk750_chip.c b/drivers/staging/sm750fb/ddk750_chip.c index 136692b00804..0bb56bbec43f 100644 --- a/drivers/staging/sm750fb/ddk750_chip.c +++ b/drivers/staging/sm750fb/ddk750_chip.c @@ -61,25 +61,26 @@ static void set_chip_clock(unsigned int frequency) if (sm750_get_chip_type() == SM750LE) return; - if (frequency) { - /* - * Set up PLL structure to hold the value to be set in clocks. - */ - pll.input_freq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */ - pll.clock_type = MXCLK_PLL; + if (!frequency) + return; - /* - * Call sm750_calc_pll_value() to fill the other fields - * of the PLL structure. Sometimes, the chip cannot set - * up the exact clock required by the User. - * Return value of sm750_calc_pll_value gives the actual - * possible clock. - */ - sm750_calc_pll_value(frequency, &pll); + /* + * Set up PLL structure to hold the value to be set in clocks. + */ + pll.input_freq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */ + pll.clock_type = MXCLK_PLL; - /* Master Clock Control: MXCLK_PLL */ - poke32(MXCLK_PLL_CTRL, sm750_format_pll_reg(&pll)); - } + /* + * Call sm750_calc_pll_value() to fill the other fields + * of the PLL structure. Sometimes, the chip cannot set + * up the exact clock required by the User. + * Return value of sm750_calc_pll_value gives the actual + * possible clock. + */ + sm750_calc_pll_value(frequency, &pll); + + /* Master Clock Control: MXCLK_PLL */ + poke32(MXCLK_PLL_CTRL, sm750_format_pll_reg(&pll)); } static void set_memory_clock(unsigned int frequency) @@ -93,37 +94,38 @@ static void set_memory_clock(unsigned int frequency) if (sm750_get_chip_type() == SM750LE) return; - if (frequency) { - /* - * Set the frequency to the maximum frequency - * that the DDR Memory can take which is 336MHz. - */ - if (frequency > MHz(336)) - frequency = MHz(336); + if (!frequency) + return; - /* Calculate the divisor */ - divisor = DIV_ROUND_CLOSEST(get_mxclk_freq(), frequency); + /* + * Set the frequency to the maximum frequency + * that the DDR Memory can take which is 336MHz. + */ + if (frequency > MHz(336)) + frequency = MHz(336); - /* Set the corresponding divisor in the register. */ - reg = peek32(CURRENT_GATE) & ~CURRENT_GATE_M2XCLK_MASK; - switch (divisor) { - default: - case 1: - reg |= CURRENT_GATE_M2XCLK_DIV_1; - break; - case 2: - reg |= CURRENT_GATE_M2XCLK_DIV_2; - break; - case 3: - reg |= CURRENT_GATE_M2XCLK_DIV_3; - break; - case 4: - reg |= CURRENT_GATE_M2XCLK_DIV_4; - break; - } + /* Calculate the divisor */ + divisor = DIV_ROUND_CLOSEST(get_mxclk_freq(), frequency); - sm750_set_current_gate(reg); + /* Set the corresponding divisor in the register. */ + reg = peek32(CURRENT_GATE) & ~CURRENT_GATE_M2XCLK_MASK; + switch (divisor) { + default: + case 1: + reg |= CURRENT_GATE_M2XCLK_DIV_1; + break; + case 2: + reg |= CURRENT_GATE_M2XCLK_DIV_2; + break; + case 3: + reg |= CURRENT_GATE_M2XCLK_DIV_3; + break; + case 4: + reg |= CURRENT_GATE_M2XCLK_DIV_4; + break; } + + sm750_set_current_gate(reg); } /* @@ -145,37 +147,38 @@ static void set_master_clock(unsigned int frequency) if (sm750_get_chip_type() == SM750LE) return; - if (frequency) { - /* - * Set the frequency to the maximum frequency - * that the SM750 engine can run, which is about 190 MHz. - */ - if (frequency > MHz(190)) - frequency = MHz(190); + if (!frequency) + return; - /* Calculate the divisor */ - divisor = DIV_ROUND_CLOSEST(get_mxclk_freq(), frequency); + /* + * Set the frequency to the maximum frequency + * that the SM750 engine can run, which is about 190 MHz. + */ + if (frequency > MHz(190)) + frequency = MHz(190); - /* Set the corresponding divisor in the register. */ - reg = peek32(CURRENT_GATE) & ~CURRENT_GATE_MCLK_MASK; - switch (divisor) { - default: - case 3: - reg |= CURRENT_GATE_MCLK_DIV_3; - break; - case 4: - reg |= CURRENT_GATE_MCLK_DIV_4; - break; - case 6: - reg |= CURRENT_GATE_MCLK_DIV_6; - break; - case 8: - reg |= CURRENT_GATE_MCLK_DIV_8; - break; - } + /* Calculate the divisor */ + divisor = DIV_ROUND_CLOSEST(get_mxclk_freq(), frequency); - sm750_set_current_gate(reg); + /* Set the corresponding divisor in the register. */ + reg = peek32(CURRENT_GATE) & ~CURRENT_GATE_MCLK_MASK; + switch (divisor) { + default: + case 3: + reg |= CURRENT_GATE_MCLK_DIV_3; + break; + case 4: + reg |= CURRENT_GATE_MCLK_DIV_4; + break; + case 6: + reg |= CURRENT_GATE_MCLK_DIV_6; + break; + case 8: + reg |= CURRENT_GATE_MCLK_DIV_8; + break; } + + sm750_set_current_gate(reg); } unsigned int ddk750_get_vm_size(void) From 293d3fe685a0d255f8c5ccbf3609d18591e3c1f6 Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Tue, 12 May 2026 16:41:23 +0000 Subject: [PATCH 1403/5214] staging: sm750fb: remove unnecessary initialization Remove `data = 0` initialization since the variable is reset each time in the for loop. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260512164124.188210-4-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_cursor.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/sm750fb/sm750_cursor.c b/drivers/staging/sm750fb/sm750_cursor.c index 9d6bca3106f4..f822d147ede9 100644 --- a/drivers/staging/sm750fb/sm750_cursor.c +++ b/drivers/staging/sm750fb/sm750_cursor.c @@ -84,7 +84,6 @@ void sm750_hw_cursor_set_data(struct lynx_cursor *cursor, u16 rop, /* in byte */ offset = cursor->max_w * 2 / 8; - data = 0; pstart = cursor->vstart; pbuffer = pstart; From 90cafd6b3c0b17ffa1d973ecad16e9b10ee8b4ee Mon Sep 17 00:00:00 2001 From: Ahmet Sezgin Duran Date: Tue, 12 May 2026 16:41:24 +0000 Subject: [PATCH 1404/5214] staging: sm750fb: remove double space in assignment Remove extra space after `=` in assignment of `reg` variable. Signed-off-by: Ahmet Sezgin Duran Link: https://patch.msgid.link/20260512164124.188210-5-ahmet@sezginduran.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_accel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 6e2120c4dce9..03f5678fa430 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -26,7 +26,7 @@ void sm750_hw_de_init(struct lynx_accel *accel) write_dpr(accel, DE_MASKS, 0xFFFFFFFF); /* dpr1c */ - reg = 0x3; + reg = 0x3; clr = DE_STRETCH_FORMAT_PATTERN_XY | DE_STRETCH_FORMAT_PATTERN_Y_MASK | From 75ba9ce635ff9357caec23ae1c307811e4c4f09b Mon Sep 17 00:00:00 2001 From: Salman Alghamdi Date: Wed, 13 May 2026 23:35:00 +0300 Subject: [PATCH 1405/5214] staging: rtl8723bs: rtw_mlme: wrap rtw_sitesurvey_cmd condition Inline rtw_sitesurvey_cmd() directly in the if condition to reduce line length. Signed-off-by: Salman Alghamdi Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260513203611.31872-3-me@cipherat.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 6be53e6df1f5..f9ae36bf4e13 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -733,9 +733,9 @@ void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) rtw_indicate_connect(adapter); } else { if (rtw_to_roam(adapter) != 0) { - if (rtw_dec_to_roam(adapter) == 0 - || _SUCCESS != rtw_sitesurvey_cmd(adapter, &pmlmepriv->assoc_ssid, 1, NULL, 0) - ) { + if (rtw_dec_to_roam(adapter) == 0 || + rtw_sitesurvey_cmd(adapter, &pmlmepriv->assoc_ssid, + 1, NULL, 0) != _SUCCESS) { rtw_set_to_roam(adapter, 0); rtw_free_assoc_resources(adapter, 1); rtw_indicate_disconnect(adapter); From f717b5b686a176bf094a0d6bb192cc9167c3e88f Mon Sep 17 00:00:00 2001 From: Salman Alghamdi Date: Wed, 13 May 2026 23:35:04 +0300 Subject: [PATCH 1406/5214] staging: rtl8723bs: rtw_mlme: add blank line for readability Add a blank line between the WIFI_UNDER_WPS block and the wifi_spec block to improve readability. Signed-off-by: Salman Alghamdi Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260513203611.31872-7-me@cipherat.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index f9ae36bf4e13..4fb74729180f 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -596,6 +596,7 @@ static bool rtw_is_desired_network(struct adapter *adapter, struct wlan_network else return false; } + if (adapter->registrypriv.wifi_spec == 1) { /* for correct flow of 8021X to do.... */ u8 *p = NULL; uint ie_len = 0; From 3ad4b506f4edcc258a85335f2be1ab41a8ce04cd Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Wed, 13 May 2026 14:27:47 -0700 Subject: [PATCH 1407/5214] staging: rtl8723bs: fix multiple blank lines in hal/ files Remove multiple consecutive blank lines in hal/hal_* and hal/sdio_* files. This fixes the following checkpatch.pl check: CHECK: Please don't use multiple blank lines Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260513212747.50900-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_btcoex.c | 27 ------------------- drivers/staging/rtl8723bs/hal/hal_com.c | 3 --- .../staging/rtl8723bs/hal/hal_com_phycfg.c | 1 - drivers/staging/rtl8723bs/hal/hal_intf.c | 1 - drivers/staging/rtl8723bs/hal/hal_sdio.c | 1 - drivers/staging/rtl8723bs/hal/sdio_halinit.c | 12 --------- drivers/staging/rtl8723bs/hal/sdio_ops.c | 2 -- 7 files changed, 47 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index e508bf8dd7ff..195ccc4b710f 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -30,7 +30,6 @@ static void halbtcoutsrc_LeaveLps(struct btc_coexist *pBtCoexist) { struct adapter *padapter; - padapter = pBtCoexist->Adapter; pBtCoexist->btInfo.bBtCtrlLps = true; @@ -43,7 +42,6 @@ static void halbtcoutsrc_EnterLps(struct btc_coexist *pBtCoexist) { struct adapter *padapter; - padapter = pBtCoexist->Adapter; pBtCoexist->btInfo.bBtCtrlLps = true; @@ -79,7 +77,6 @@ static void halbtcoutsrc_LeaveLowPower(struct btc_coexist *pBtCoexist) unsigned long utime; u32 timeout; /* unit: ms */ - padapter = pBtCoexist->Adapter; ready = _FAIL; #ifdef LPS_RPWM_WAIT_MS @@ -110,7 +107,6 @@ static void halbtcoutsrc_NormalLowPower(struct btc_coexist *pBtCoexist) { struct adapter *padapter; - padapter = pBtCoexist->Adapter; rtw_unregister_task_alive(padapter, BTCOEX_ALIVE); } @@ -129,7 +125,6 @@ static void halbtcoutsrc_AggregationCheck(struct btc_coexist *pBtCoexist) struct adapter *padapter; bool bNeedToAct; - padapter = pBtCoexist->Adapter; bNeedToAct = false; @@ -161,7 +156,6 @@ static u8 halbtcoutsrc_IsWifiBusy(struct adapter *padapter) { struct mlme_priv *pmlmepriv; - pmlmepriv = &padapter->mlmepriv; if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE)) { @@ -180,7 +174,6 @@ static u32 _halbtcoutsrc_GetWifiLinkStatus(struct adapter *padapter) u8 bp2p; u32 portConnectedStatus; - pmlmepriv = &padapter->mlmepriv; bp2p = false; portConnectedStatus = 0; @@ -214,7 +207,6 @@ static u32 halbtcoutsrc_GetWifiLinkStatus(struct btc_coexist *pBtCoexist) u32 retVal; u32 portConnectedStatus, numOfConnectedPort; - padapter = pBtCoexist->Adapter; portConnectedStatus = 0; numOfConnectedPort = 0; @@ -270,7 +262,6 @@ static u8 halbtcoutsrc_Get(void *pBtcContext, u8 getType, void *pOutBuf) u32 *pU4Tmp; u8 ret; - pBtCoexist = (struct btc_coexist *)pBtcContext; if (!halbtcoutsrc_IsBtCoexistAvailable(pBtCoexist)) return false; @@ -429,7 +420,6 @@ static u8 halbtcoutsrc_Set(void *pBtcContext, u8 setType, void *pInBuf) u32 *pU4Tmp; u8 ret; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; pu8 = pInBuf; @@ -566,7 +556,6 @@ static u8 halbtcoutsrc_Read1Byte(void *pBtcContext, u32 RegAddr) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -578,7 +567,6 @@ static u16 halbtcoutsrc_Read2Byte(void *pBtcContext, u32 RegAddr) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -590,7 +578,6 @@ static u32 halbtcoutsrc_Read4Byte(void *pBtcContext, u32 RegAddr) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -602,7 +589,6 @@ static void halbtcoutsrc_Write1Byte(void *pBtcContext, u32 RegAddr, u8 Data) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -616,7 +602,6 @@ static void halbtcoutsrc_BitMaskWrite1Byte(void *pBtcContext, u32 regAddr, u8 bi u8 originalValue, bitShift; u8 i; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; originalValue = 0; @@ -642,7 +627,6 @@ static void halbtcoutsrc_Write2Byte(void *pBtcContext, u32 RegAddr, u16 Data) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -654,7 +638,6 @@ static void halbtcoutsrc_Write4Byte(void *pBtcContext, u32 RegAddr, u32 Data) struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -677,20 +660,17 @@ static void halbtcoutsrc_SetBbReg(void *pBtcContext, u32 RegAddr, u32 BitMask, u struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; PHY_SetBBReg(padapter, RegAddr, BitMask, Data); } - static u32 halbtcoutsrc_GetBbReg(void *pBtcContext, u32 RegAddr, u32 BitMask) { struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -702,7 +682,6 @@ static void halbtcoutsrc_SetRfReg(void *pBtcContext, u8 eRFPath, u32 RegAddr, u3 struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -714,7 +693,6 @@ static u32 halbtcoutsrc_GetRfReg(void *pBtcContext, u8 eRFPath, u32 RegAddr, u32 struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -762,7 +740,6 @@ static void halbtcoutsrc_FillH2cCmd(void *pBtcContext, u8 elementId, u32 cmdLen, struct btc_coexist *pBtCoexist; struct adapter *padapter; - pBtCoexist = (struct btc_coexist *)pBtcContext; padapter = pBtCoexist->Adapter; @@ -912,7 +889,6 @@ void EXhalbtcoutsrc_LpsNotify(struct btc_coexist *pBtCoexist, u8 type) { u8 lpsType; - if (!halbtcoutsrc_IsBtCoexistAvailable(pBtCoexist)) return; @@ -1141,7 +1117,6 @@ void hal_btcoex_SetBTCoexist(struct adapter *padapter, u8 bBtExist) { struct hal_com_data *pHalData; - pHalData = GET_HAL_DATA(padapter); pHalData->bt_coexist.bBtExist = bBtExist; } @@ -1158,7 +1133,6 @@ bool hal_btcoex_IsBtExist(struct adapter *padapter) { struct hal_com_data *pHalData; - pHalData = GET_HAL_DATA(padapter); return pHalData->bt_coexist.bBtExist; } @@ -1178,7 +1152,6 @@ void hal_btcoex_SetPgAntNum(struct adapter *padapter, u8 antNum) { struct hal_com_data *pHalData; - pHalData = GET_HAL_DATA(padapter); pHalData->bt_coexist.btTotalAntNum = antNum; diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 792cd071df22..634af4657eee 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -335,7 +335,6 @@ static void _TwoOutPipeMapping(struct adapter *padapter, bool bWIFICfg) } else { /* typical setting */ - /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */ /* 1, 1, 0, 0, 0, 0, 0, 0, 0 }; */ /* 0:ep_0 num, 1:ep_1 num */ @@ -376,7 +375,6 @@ static void _ThreeOutPipeMapping(struct adapter *padapter, bool bWIFICfg) } else { /* typical setting */ - /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */ /* 2, 2, 1, 0, 0, 0, 0, 0, 0 }; */ /* 0:H, 1:N, 2:L */ @@ -708,7 +706,6 @@ void SetHalODMVar( } } - bool GetU1ByteIntegerFromStringInDecimal(char *Str, u8 *pInt) { u16 i = 0; diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index ea79e300f83b..b16456d5b6bb 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -734,7 +734,6 @@ s8 phy_get_tx_pwr_lmt(struct adapter *adapter, u32 reg_pwr_tbl_sel, idx_rate_sctn == -1 || idx_channel == -1) return MAX_POWER_INDEX; - for (i = 0; i < MAX_REGULATION_NUM; i++) limits[i] = hal_data->TxPwrLimit_2_4G[i] [idx_bandwidth] diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 03f23b75d532..19e16270381e 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -223,7 +223,6 @@ void beacon_timing_control(struct adapter *padapter) rtl8723b_SetBeaconRelatedRegisters(padapter); } - s32 rtw_hal_xmit_thread_handler(struct adapter *padapter) { return rtl8723bs_xmit_buf_handler(padapter); diff --git a/drivers/staging/rtl8723bs/hal/hal_sdio.c b/drivers/staging/rtl8723bs/hal/hal_sdio.c index 665c85eccbdf..1a865d4de8ac 100644 --- a/drivers/staging/rtl8723bs/hal/hal_sdio.c +++ b/drivers/staging/rtl8723bs/hal/hal_sdio.c @@ -81,7 +81,6 @@ u32 rtw_hal_get_sdio_tx_max_length(struct adapter *padapter, u8 queue_idx) struct hal_com_data *pHalData = GET_HAL_DATA(padapter); u32 deviceId, max_len; - deviceId = ffaddr2deviceId(pdvobjpriv, queue_idx); switch (deviceId) { case WLAN_TX_HIQ_DEVICE_ID: diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index eecfc593e2dd..ed9b8fb07fec 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -21,7 +21,6 @@ static u8 CardEnable(struct adapter *padapter) u8 bMacPwrCtrlOn; u8 ret = _FAIL; - rtw_hal_get_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); if (!bMacPwrCtrlOn) { /* RSV_CTRL 0x1C[7:0] = 0x00 */ @@ -235,7 +234,6 @@ static void _InitNormalChipTwoOutEpPriority(struct adapter *Adapter) struct registry_priv *pregistrypriv = &Adapter->registrypriv; u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ; - u16 valueHi = 0; u16 valueLow = 0; @@ -319,8 +317,6 @@ static void _InitQueuePriority(struct adapter *Adapter) default: break; } - - } static void _InitPageBoundary(struct adapter *padapter) @@ -363,7 +359,6 @@ static void _InitWMACSetting(struct adapter *padapter) struct hal_com_data *pHalData; u16 value16; - pHalData = GET_HAL_DATA(padapter); pHalData->ReceiveConfig = 0; @@ -555,7 +550,6 @@ static bool HalDetectPwrDownMode(struct adapter *Adapter) struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); struct pwrctrl_priv *pwrctrlpriv = adapter_to_pwrctl(Adapter); - rtw_efuse_shadow_read(Adapter, 1, 0x7B/*EEPROM_RF_OPT3_92C*/, (u32 *)&tmpvalue); /* 2010/08/25 MH INF priority > PDN Efuse value. */ @@ -730,7 +724,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) /* set 0x0 to 0xFF by tynli. Default enable HW SEQ NUM. */ rtw_write8(padapter, REG_HWSEQ_CTRL, 0xFF); - /* */ /* Configure SDIO TxRx Control to enable Rx DMA timer masking. */ /* 2010.02.24. */ @@ -739,7 +732,6 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) _RfPowerSave(padapter); - rtl8723b_InitHalDm(padapter); /* */ @@ -903,7 +895,6 @@ void rtl8723bs_init_default_value(struct adapter *padapter) { struct hal_com_data *pHalData; - pHalData = GET_HAL_DATA(padapter); rtl8723b_init_default_value(padapter); @@ -919,7 +910,6 @@ void rtl8723bs_interface_configure(struct adapter *padapter) struct registry_priv *pregistrypriv = &padapter->registrypriv; bool bWiFiConfig = pregistrypriv->wifi_spec; - pdvobjpriv->RtOutPipe[0] = WLAN_TX_HIQ_DEVICE_ID; pdvobjpriv->RtOutPipe[1] = WLAN_TX_MIQ_DEVICE_ID; pdvobjpriv->RtOutPipe[2] = WLAN_TX_LOQ_DEVICE_ID; @@ -971,7 +961,6 @@ static void _ReadRFType(struct adapter *Adapter) pHalData->rf_chip = RF_6052; } - static void Hal_EfuseParseMACAddr_8723BS( struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail ) @@ -1065,7 +1054,6 @@ static s32 _ReadAdapterInfo8723BS(struct adapter *padapter) if (!padapter->hw_init_completed) _InitPowerOn_8723BS(padapter); - val8 = rtw_read8(padapter, 0x4e); val8 |= BIT(6); rtw_write8(padapter, 0x4e, val8); diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index 91580f6ba52e..c4058cfe73f2 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -857,5 +857,3 @@ void HalQueryTxOQTBufferStatus8723BSdio(struct adapter *adapter) haldata->SdioTxOQTFreeSpace = SdioLocalCmd52Read1Byte(adapter, SDIO_REG_OQT_FREE_PG); } - - From 16e3162240c4c2d8568333eb4c3a07d606820bf6 Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Thu, 14 May 2026 11:06:42 -0700 Subject: [PATCH 1408/5214] staging: rtl8723bs: fix multiple blank lines in hal/Hal* files Remove multiple consecutive blank lines in hal/Hal* source and header files. This fixes the following checkpatch.pl check: CHECK: Please don't use multiple blank lines Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260514180642.4608-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/hal/HalBtc8723b1Ant.c | 13 --------- .../staging/rtl8723bs/hal/HalBtc8723b2Ant.c | 5 ---- .../staging/rtl8723bs/hal/HalHWImg8723B_MAC.h | 1 - .../staging/rtl8723bs/hal/HalHWImg8723B_RF.c | 1 - .../staging/rtl8723bs/hal/HalHWImg8723B_RF.h | 1 - drivers/staging/rtl8723bs/hal/HalPhyRf.c | 1 - drivers/staging/rtl8723bs/hal/HalPhyRf.h | 1 - .../staging/rtl8723bs/hal/HalPhyRf_8723B.c | 27 ------------------- .../staging/rtl8723bs/hal/HalPhyRf_8723B.h | 2 -- drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c | 1 - 10 files changed, 53 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index f855df776a07..17b0d8c2ace4 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -235,8 +235,6 @@ static void halbtc8723b1ant_LimitedRx( pBtCoexist->fBtcSet(pBtCoexist, BTC_SET_U1_AGG_BUF_SIZE, &rxAggSize); /* real update aggregation setting */ pBtCoexist->fBtcSet(pBtCoexist, BTC_SET_ACT_AGGREGATE_CTRL, NULL); - - } static void halbtc8723b1ant_QueryBtInfo(struct btc_coexist *pBtCoexist) @@ -298,7 +296,6 @@ static void halbtc8723b1ant_MonitorBtCtr(struct btc_coexist *pBtCoexist) } } - static void halbtc8723b1ant_MonitorWiFiCtr(struct btc_coexist *pBtCoexist) { s32 wifiRssi = 0; @@ -333,7 +330,6 @@ static void halbtc8723b1ant_MonitorWiFiCtr(struct btc_coexist *pBtCoexist) pCoexSta->nCRCErr_11nAgg = pBtCoexist->fBtcRead2Byte(pBtCoexist, 0xfba); } - /* reset counter */ pBtCoexist->fBtcWrite1ByteBitMask(pBtCoexist, 0xf16, 0x1, 0x1); pBtCoexist->fBtcWrite1ByteBitMask(pBtCoexist, 0xf16, 0x1, 0x0); @@ -381,8 +377,6 @@ static void halbtc8723b1ant_MonitorWiFiCtr(struct btc_coexist *pBtCoexist) } pCoexSta->bPreCCKLock = pCoexSta->bCCKLock; - - } static bool halbtc8723b1ant_IsWifiStatusChanged(struct btc_coexist *pBtCoexist) @@ -880,7 +874,6 @@ static void halbtc8723b1ant_SetAntPath( } } - /* ext switch setting */ switch (antPosType) { case BTC_ANT_PATH_WIFI: @@ -930,7 +923,6 @@ static void halbtc8723b1ant_SetAntPath( } } - /* internal switch setting */ switch (antPosType) { case BTC_ANT_PATH_WIFI: @@ -991,7 +983,6 @@ static void halbtc8723b1ant_SetFwPstdma( pBtCoexist->fBtcFillH2c(pBtCoexist, 0x60, 5, H2C_Parameter); } - static void halbtc8723b1ant_PsTdma( struct btc_coexist *pBtCoexist, bool bForceExec, bool bTurnOn, u8 type ) @@ -1029,12 +1020,10 @@ static void halbtc8723b1ant_PsTdma( psTdmaByte4Val = 0x10; /* 0x778 = d/1 toggle */ } - if (bTurnOn) { if (pBtLinkInfo->bSlaveRole) psTdmaByte4Val = psTdmaByte4Val | 0x1; /* 0x778 = 0x1 at wifi slot (no blocking BT Low-Pri pkts) */ - switch (type) { default: halbtc8723b1ant_SetFwPstdma( @@ -1320,7 +1309,6 @@ static bool halbtc8723b1ant_IsCommonAction(struct btc_coexist *pBtCoexist) return bCommon; } - static void halbtc8723b1ant_TdmaDurationAdjustForAcl( struct btc_coexist *pBtCoexist, u8 wifiStatus ) @@ -2041,7 +2029,6 @@ static void halbtc8723b1ant_RunCoexistMechanism(struct btc_coexist *pBtCoexist) return; } - if (!bWifiConnected) { bool bScan = false, bLink = false, bRoam = false; diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c index e4b056873d73..6da6940c9043 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c @@ -552,7 +552,6 @@ static void halbtc8723b2ant_SetSwFullTimeDacSwing( halbtc8723b2ant_SetDacSwingReg(pBtCoexist, 0x18); } - static void halbtc8723b2ant_DacSwing( struct btc_coexist *pBtCoexist, bool bForceExec, @@ -600,7 +599,6 @@ static void halbtc8723b2ant_SetAgcTable( pBtCoexist->fBtcWrite4Byte(pBtCoexist, 0xc78, 0xa4200001); } - /* RF Gain */ pBtCoexist->fBtcSetRfReg(pBtCoexist, BTC_RF_A, 0xef, 0xfffff, 0x02000); if (bAgcTableEn) { @@ -1637,7 +1635,6 @@ static void halbtc8723b2ant_ActionSco(struct btc_coexist *pBtCoexist) } } - static void halbtc8723b2ant_ActionHid(struct btc_coexist *pBtCoexist) { u8 wifiRssiState, btRssiState; @@ -1885,7 +1882,6 @@ static void halbtc8723b2ant_ActionPanEdr(struct btc_coexist *pBtCoexist) } } - /* PAN(HS) only */ static void halbtc8723b2ant_ActionPanHs(struct btc_coexist *pBtCoexist) { @@ -2229,7 +2225,6 @@ static void halbtc8723b2ant_RunCoexistMechanism(struct btc_coexist *pBtCoexist) pCoexDm->bAutoTdmaAdjust = false; } - switch (pCoexDm->curAlgorithm) { case BT_8723B_2ANT_COEX_ALGO_SCO: halbtc8723b2ant_ActionSco(pBtCoexist); diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h index 82de8aa0008b..250861325919 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_MAC.h @@ -8,7 +8,6 @@ #ifndef __INC_MP_MAC_HW_IMG_8723B_H #define __INC_MP_MAC_HW_IMG_8723B_H - /****************************************************************************** * MAC_REG.TXT ******************************************************************************/ diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c index bfdebc840730..30c8624bba25 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.c @@ -310,7 +310,6 @@ void ODM_ReadAndConfig_MP_8723B_TxPowerTrack_SDIO(struct dm_odm_t *pDM_Odm) { struct odm_rf_cal_t *pRFCalibrateInfo = &pDM_Odm->RFCalibrateInfo; - memcpy( pRFCalibrateInfo->DeltaSwingTableIdx_2GA_P, gDeltaSwingTableIdx_MP_2GA_P_TxPowerTrack_SDIO_8723B, diff --git a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h index 04441fdeaa26..7b4f399fcdff 100644 --- a/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h +++ b/drivers/staging/rtl8723bs/hal/HalHWImg8723B_RF.h @@ -8,7 +8,6 @@ #ifndef __INC_MP_RF_HW_IMG_8723B_H #define __INC_MP_RF_HW_IMG_8723B_H - /****************************************************************************** * RadioA.TXT ******************************************************************************/ diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf.c b/drivers/staging/rtl8723bs/hal/HalPhyRf.c index 7bef05a9a063..9e4d42e3a122 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf.c @@ -70,7 +70,6 @@ void ODM_TXPowerTrackingCallback_ThermalMeter(struct adapter *Adapter) struct txpwrtrack_cfg c; - /* 4 1. The following TWO tables decide the final index of OFDM/CCK swing table. */ u8 *deltaSwingTableIdx_TUP_A; u8 *deltaSwingTableIdx_TDOWN_A; diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf.h b/drivers/staging/rtl8723bs/hal/HalPhyRf.h index 1e79ab9cb773..bd9430db968f 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf.h +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf.h @@ -32,7 +32,6 @@ struct txpwrtrack_cfg { void ConfigureTxpowerTrack(struct dm_odm_t *pDM_Odm, struct txpwrtrack_cfg *pConfig); - void ODM_ClearTxPowerTrackingState(struct dm_odm_t *pDM_Odm); void ODM_TXPowerTrackingCallback_ThermalMeter(struct adapter *Adapter); diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index de431bc75627..70b68f473d58 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -39,7 +39,6 @@ static u8 DeltaSwingTableIdx_2GA_P_8188E[] = { /* 3 Tx Power Tracking */ /* 3 ============================================================ */ - static void setIqkMatrix_8723B( struct dm_odm_t *pDM_Odm, u8 OFDM_index, @@ -118,7 +117,6 @@ static void setIqkMatrix_8723B( } } - static void setCCKFilterCoefficient(struct dm_odm_t *pDM_Odm, u8 CCKSwingIndex) { u8 (*swingtable)[8]; @@ -322,7 +320,6 @@ static void GetDeltaSwingTable_8723B( } } - void ConfigureTxpowerTrack_8723B(struct txpwrtrack_cfg *pConfig) { pConfig->SwingTableSize_CCK = CCK_TABLE_SIZE; @@ -414,13 +411,11 @@ static u8 phy_PathA_IQK_8723B( /* leave IQK mode */ PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_IQK, bMaskH3Bytes, 0x000000); - /* Check failed */ regEAC = PHY_QueryBBReg(pDM_Odm->Adapter, rRx_Power_After_IQK_A_2, bMaskDWord); regE94 = PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_Before_IQK_A, bMaskDWord); regE9C = PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_After_IQK_A, bMaskDWord); - /* Allen 20131125 */ tmp = (regE9C & 0x03FF0000)>>16; if ((tmp & 0x200) > 0) @@ -553,7 +548,6 @@ static u8 phy_PathA_RxIQK8723B( PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0xdf, bRFRegOffsetMask, 0xf80); PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0x55, bRFRegOffsetMask, 0x4021f); - /* IQK setting */ PHY_SetBBReg(pDM_Odm->Adapter, rRx_IQK, bMaskDWord, 0x01004800); @@ -652,8 +646,6 @@ static u8 phy_PathB_IQK_8723B(struct adapter *padapter) PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0xed, 0x20, 0x1); PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0x43, bRFRegOffsetMask, 0x30fc1); - - /* 1 Tx IQK */ /* IQK setting */ PHY_SetBBReg(pDM_Odm->Adapter, rTx_IQK, bMaskDWord, 0x01007c00); @@ -746,12 +738,10 @@ static u8 phy_PathB_RxIQK8723B(struct adapter *padapter, bool configPathB) PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0xed, 0x20, 0x1); PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_A, 0x43, bRFRegOffsetMask, 0x30fcd); - /* IQK setting */ PHY_SetBBReg(pDM_Odm->Adapter, rTx_IQK, bMaskDWord, 0x01007c00); PHY_SetBBReg(pDM_Odm->Adapter, rRx_IQK, bMaskDWord, 0x01004800); - /* path-B IQK setting */ PHY_SetBBReg(pDM_Odm->Adapter, rTx_IQK_Tone_A, bMaskDWord, 0x18008c1c); PHY_SetBBReg(pDM_Odm->Adapter, rRx_IQK_Tone_A, bMaskDWord, 0x38008c1c); @@ -781,7 +771,6 @@ static u8 phy_PathB_RxIQK8723B(struct adapter *padapter, bool configPathB) PHY_SetBBReg(pDM_Odm->Adapter, rIQK_AGC_Pts, bMaskDWord, 0xf9000000); PHY_SetBBReg(pDM_Odm->Adapter, rIQK_AGC_Pts, bMaskDWord, 0xf8000000); - /* delay x ms */ /* PlatformStallExecution(IQK_DELAY_TIME_88E*1000); */ mdelay(IQK_DELAY_TIME_8723B); @@ -889,8 +878,6 @@ static u8 phy_PathB_RxIQK8723B(struct adapter *padapter, bool configPathB) /* PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_IQK, 0xffffff00, 0x00000000); */ /* PHY_SetRFReg(pDM_Odm->Adapter, RF_PATH_B, 0xdf, bRFRegOffsetMask, 0x180); */ - - /* Allen 20131125 */ tmp = (regEAC & 0x03FF0000)>>16; if ((tmp & 0x200) > 0) @@ -1114,7 +1101,6 @@ static void _PHY_SaveADDARegisters8723B( } } - static void _PHY_SaveMACRegisters8723B( struct adapter *padapter, u32 *MACReg, u32 *MACBackup ) @@ -1130,7 +1116,6 @@ static void _PHY_SaveMACRegisters8723B( } - static void _PHY_ReloadADDARegisters8723B( struct adapter *padapter, u32 *ADDAReg, @@ -1159,7 +1144,6 @@ static void _PHY_ReloadMACRegisters8723B( rtw_write32(padapter, MACReg[i], MACBackup[i]); } - static void _PHY_PathADDAOn8723B( struct adapter *padapter, u32 *ADDAReg, @@ -1283,8 +1267,6 @@ static bool phy_SimularityCompare_8723B( } } - - static void phy_IQCalibrate_8723B( struct adapter *padapter, s32 result[][8], @@ -1370,13 +1352,11 @@ static void phy_IQCalibrate_8723B( PHY_SetBBReg(pDM_Odm->Adapter, rOFDM0_TRMuxPar, bMaskDWord, 0x000800e4); PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_XCD_RFInterfaceSW, bMaskDWord, 0x22204000); - /* PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_XAB_RFInterfaceSW, BIT10, 0x01); */ /* PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_XAB_RFInterfaceSW, BIT26, 0x01); */ /* PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_XA_RFInterfaceOE, BIT10, 0x00); */ /* PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_XB_RFInterfaceOE, BIT10, 0x00); */ - /* RX IQ calibration setting for 8723B D cut large current issue when leaving IPS */ PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_IQK, bMaskH3Bytes, 0x000000); @@ -1481,7 +1461,6 @@ static void phy_IQCalibrate_8723B( } - static void phy_LCCalibrate_8723B(struct dm_odm_t *pDM_Odm, bool is2T) { u8 tmpReg; @@ -1590,7 +1569,6 @@ void PHY_IQCalibrate_8723B( if (pDM_Odm->RFCalibrateInfo.bIQKInProgress) return; - pDM_Odm->RFCalibrateInfo.bIQKInProgress = true; if (bRestore) { @@ -1648,7 +1626,6 @@ void PHY_IQCalibrate_8723B( /* PHY_SetBBReg(pDM_Odm->Adapter, 0x764, BIT12, 0x0); */ /* PHY_SetBBReg(pDM_Odm->Adapter, 0x764, BIT11, 0x1); */ - for (i = 0; i < 8; i++) { result[0][i] = 0; result[1][i] = 0; @@ -1663,7 +1640,6 @@ void PHY_IQCalibrate_8723B( is23simular = false; is13simular = false; - for (i = 0; i < 3; i++) { phy_IQCalibrate_8723B(padapter, result, i, Is2ant, RF_Path); @@ -1766,7 +1742,6 @@ void PHY_IQCalibrate_8723B( pDM_Odm->RFCalibrateInfo.bIQKInProgress = false; } - void PHY_LCCalibrate_8723B(struct dm_odm_t *pDM_Odm) { bool bSingleTone = false, bCarrierSuppression = false; @@ -1786,9 +1761,7 @@ void PHY_LCCalibrate_8723B(struct dm_odm_t *pDM_Odm) pDM_Odm->RFCalibrateInfo.bLCKInProgress = true; - phy_LCCalibrate_8723B(pDM_Odm, false); - pDM_Odm->RFCalibrateInfo.bLCKInProgress = false; } diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.h b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.h index c83442917f9d..14b328378b8b 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.h +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.h @@ -15,7 +15,6 @@ #define AVG_THERMAL_NUM_8723B 4 #define RF_T_METER_8723B 0x42 /* */ - void ConfigureTxpowerTrack_8723B(struct txpwrtrack_cfg *pConfig); void ODM_TxPwrTrackSetPwr_8723B( @@ -46,7 +45,6 @@ void PHY_LCCalibrate_8723B(struct dm_odm_t *pDM_Odm); /* */ void PHY_DigitalPredistortion_8723B(struct adapter *padapter); - void _PHY_SaveADDARegisters_8723B( struct adapter *padapter, u32 *ADDAReg, diff --git a/drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c b/drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c index 86404b5e6c52..a59505cc4a07 100644 --- a/drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c +++ b/drivers/staging/rtl8723bs/hal/HalPwrSeqCmd.c @@ -23,7 +23,6 @@ Major Change History: #include #include - /* */ /* Description: */ /* This routine deal with the Power Configuration CMDs parsing for RTL8723/RTL8188E Series IC. */ From f25efd7794eb636d0bf544274de8b5c37ea05ecf Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Fri, 15 May 2026 18:12:52 +0300 Subject: [PATCH 1409/5214] staging: rtl8723bs: remove unused DBG_XMIT_BUF and DBG_XMIT_BUF_EXT code Remove the DBG_XMIT_BUF and DBG_XMIT_BUF_EXT code since it is not used as the macros are not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260515151253.8106-2-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 7 ------- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 5 ----- drivers/staging/rtl8723bs/include/rtw_xmit.h | 5 ----- 3 files changed, 17 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index a28830b4f4e7..f090d25b0ce3 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -191,10 +191,6 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) list_add_tail(&pxmitbuf->list, &pxmitpriv->free_xmitbuf_queue.queue); - #ifdef DBG_XMIT_BUF - pxmitbuf->no = i; - #endif - pxmitbuf++; } @@ -264,9 +260,6 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) list_add_tail(&pxmitbuf->list, &pxmitpriv->free_xmit_extbuf_queue.queue); - #ifdef DBG_XMIT_BUF_EXT - pxmitbuf->no = i; - #endif pxmitbuf++; } diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index a7f23335cdd9..49d556a72834 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -256,11 +256,6 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv); if (!pxmitbuf) { -#ifdef DBG_XMIT_BUF - netdev_err(padapter->pnetdev, - "%s: xmit_buf is not enough!\n", - __func__); -#endif err = -2; complete(&(pxmitpriv->xmit_comp)); break; diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index b0189a703d28..adce0d211494 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -254,11 +254,6 @@ struct xmit_buf { u32 ff_hwaddr; u8 pg_num; u8 agg_num; - -#if defined(DBG_XMIT_BUF) || defined(DBG_XMIT_BUF_EXT) - u8 no; -#endif - }; From e98ec17535a1b823a5b4ba3f54b869a334f6f072 Mon Sep 17 00:00:00 2001 From: Andrei Khomenkov Date: Fri, 15 May 2026 18:12:53 +0300 Subject: [PATCH 1410/5214] staging: rtl8723bs: remove unused TXDESC_64_BYTES code Remove the TXDESC_64_BYTES code since it is not used as the macro is not defined anywhere. Signed-off-by: Andrei Khomenkov Link: https://patch.msgid.link/20260515151253.8106-3-khomenkov@mailbox.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/include/rtw_xmit.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index adce0d211494..056c2ae990f2 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -94,23 +94,9 @@ struct tx_desc { __le32 txdw6; __le32 txdw7; -#if defined(TXDESC_40_BYTES) || defined(TXDESC_64_BYTES) +#ifdef TXDESC_40_BYTES __le32 txdw8; __le32 txdw9; -#endif /* TXDESC_40_BYTES */ - -#ifdef TXDESC_64_BYTES - __le32 txdw10; - __le32 txdw11; - - /* 2008/05/15 MH Because PCIE HW memory R/W 4K limit. And now, our descriptor */ - /* size is 40 bytes. If you use more than 102 descriptor(103*40>4096), HW will execute */ - /* memoryR/W CRC error. And then all DMA fetch will fail. We must decrease descriptor */ - /* number or enlarge descriptor size as 64 bytes. */ - __le32 txdw12; - __le32 txdw13; - __le32 txdw14; - __le32 txdw15; #endif }; From 07ff92ba0aa1573da1a961cb379b12ff5ae890eb Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Fri, 15 May 2026 10:51:40 -0700 Subject: [PATCH 1411/5214] staging: rtl8723bs: fix multiple blank lines in more hal/ files Remove multiple consecutive blank lines in more hal/ files. This fixes the following checkpatch.pl check: CHECK: Please don't use multiple blank lines Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260515175140.7439-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm.c | 10 ---------- drivers/staging/rtl8723bs/hal/odm.h | 2 -- drivers/staging/rtl8723bs/hal/odm_DIG.c | 10 ---------- drivers/staging/rtl8723bs/hal/odm_HWConfig.c | 3 --- drivers/staging/rtl8723bs/hal/odm_HWConfig.h | 1 - .../rtl8723bs/hal/odm_RegConfig8723B.c | 1 - .../staging/rtl8723bs/hal/odm_RegDefine11N.h | 2 -- drivers/staging/rtl8723bs/hal/odm_interface.h | 3 --- drivers/staging/rtl8723bs/hal/odm_types.h | 1 - drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c | 4 ---- drivers/staging/rtl8723bs/hal/rtl8723b_dm.c | 5 ----- .../staging/rtl8723bs/hal/rtl8723b_hal_init.c | 19 ------------------- .../staging/rtl8723bs/hal/rtl8723b_rf6052.c | 3 --- .../staging/rtl8723bs/hal/rtl8723b_rxdesc.c | 1 - .../staging/rtl8723bs/hal/rtl8723bs_recv.c | 1 - .../staging/rtl8723bs/hal/rtl8723bs_xmit.c | 4 ---- 16 files changed, 70 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index df394323d901..180aac640a14 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -434,8 +434,6 @@ static void odm_RSSIMonitorCheckCE(struct dm_odm_t *pDM_Odm) } } - - if (tmpEntryMaxPWDB != 0) /* If associated entry is found */ pdmpriv->EntryMaxUndecoratedSmoothedPWDB = tmpEntryMaxPWDB; else @@ -509,7 +507,6 @@ void odm_TXPowerTrackingInit(struct dm_odm_t *pDM_Odm) struct adapter *Adapter = pDM_Odm->Adapter; struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - struct dm_priv *pdmpriv = &pHalData->dmpriv; pdmpriv->bTXPowerTracking = true; @@ -623,7 +620,6 @@ void ODM_DMWatchdog(struct dm_odm_t *pDM_Odm) if (*pDM_Odm->pbPowerSaving) return; - odm_RefreshRateAdaptiveMask(pDM_Odm); odm_EdcaTurboCheck(pDM_Odm); ODM_CfoTracking(pDM_Odm); @@ -637,7 +633,6 @@ void ODM_DMWatchdog(struct dm_odm_t *pDM_Odm) pDM_Odm->PhyDbgInfo.NumQryBeaconPkt = 0; } - /* */ /* Init /.. Fixed HW value. Only init time. */ /* */ @@ -732,7 +727,6 @@ void ODM_CmnInfoInit(struct dm_odm_t *pDM_Odm, enum odm_cmninfo_e CmnInfo, u32 V } - void ODM_CmnInfoHook(struct dm_odm_t *pDM_Odm, enum odm_cmninfo_e CmnInfo, void *pValue) { /* */ @@ -856,7 +850,6 @@ void ODM_CmnInfoHook(struct dm_odm_t *pDM_Odm, enum odm_cmninfo_e CmnInfo, void } - void ODM_CmnInfoPtrArrayHook( struct dm_odm_t *pDM_Odm, enum odm_cmninfo_e CmnInfo, @@ -882,7 +875,6 @@ void ODM_CmnInfoPtrArrayHook( } - /* */ /* Update Band/CHannel/.. The values are dynamic but non-per-packet. */ /* */ @@ -956,8 +948,6 @@ void ODM_CmnInfoUpdate(struct dm_odm_t *pDM_Odm, u32 CmnInfo, u64 Value) /* do nothing */ break; } - - } /* 3 ============================================================ */ diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h index ef23a2088733..ee263790b32b 100644 --- a/drivers/staging/rtl8723bs/hal/odm.h +++ b/drivers/staging/rtl8723bs/hal/odm.h @@ -5,7 +5,6 @@ * ******************************************************************************/ - #ifndef __HALDMOUTSRC_H__ #define __HALDMOUTSRC_H__ @@ -174,7 +173,6 @@ struct swat_t { /* _SW_Antenna_Switch_ */ /* Remove Edca by YuChen */ - struct odm_rate_adaptive { u8 Type; /* DM_Type_ByFW/DM_Type_ByDriver */ u8 LdpcThres; /* if RSSI > LdpcThres => switch from LPDC to BCC */ diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index ef3c2db8553e..4e28b7742bfe 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -80,7 +80,6 @@ void odm_NHMBB(void *pDM_VOID) pDM_Odm->NHMLastRxOkcnt = *(pDM_Odm->pNumRxBytesUnicast); - if ((pDM_Odm->NHMCurTxOkcnt) + 1 > (u64)(pDM_Odm->NHMCurRxOkcnt<<2) + 1) { /* Tx > 4*Rx possible for adaptivity test */ if (pDM_Odm->NHM_cnt_0 >= 190 || pDM_Odm->adaptivity_flag) { /* Enable EDCCA since it is possible running Adaptivity testing */ @@ -128,7 +127,6 @@ void odm_SearchPwdBLowerBound(void *pDM_VOID, u8 IGI_target) IGI = 0x50; /* find H2L, L2H lower bound */ ODM_Write_DIG(pDM_Odm, IGI); - Diff = IGI_target-(s8)IGI; TH_L2H_dmc = pDM_Odm->TH_L2H_ini + Diff; if (TH_L2H_dmc > 10) @@ -212,7 +210,6 @@ void odm_AdaptivityInit(void *pDM_VOID) PHY_SetBBReg(pDM_Odm->Adapter, REG_RD_CTRL, BIT(11), 1); /* stop counting if EDCCA is asserted */ } - void odm_Adaptivity(void *pDM_VOID, u8 IGI) { struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; @@ -363,7 +360,6 @@ void odm_DIGInit(void *pDM_VOID) pDM_DigTable->rx_gain_range_min = DM_DIG_MIN_NIC; } - void odm_DIG(void *pDM_VOID) { struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; @@ -388,7 +384,6 @@ void odm_DIG(void *pDM_VOID) if (pDM_Odm->adaptivity_flag) Adap_IGI_Upper = pDM_Odm->Adaptivity_IGI_upper; - /* 1 Update status */ DIG_Dynamic_MIN = pDM_DigTable->DIG_Dynamic_MIN_0; FirstConnect = (pDM_Odm->bLinked) && !pDM_DigTable->bMediaConnect_0; @@ -469,7 +464,6 @@ void odm_DIG(void *pDM_VOID) if (pDM_DigTable->rx_gain_range_min > pDM_DigTable->rx_gain_range_max) pDM_DigTable->rx_gain_range_min = pDM_DigTable->rx_gain_range_max; - /* 1 False alarm threshold decision */ odm_FAThresholdCheck(pDM_Odm, bDFSBand, bPerformance, RxTp, TxTp, dm_FA_thres); @@ -536,7 +530,6 @@ void odm_DIG(void *pDM_VOID) } } - /* 1 Update status */ if (pDM_Odm->bBtHsOperation) { if (pDM_Odm->bLinked) { @@ -581,7 +574,6 @@ void odm_DIGbyRSSI_LPS(void *pDM_VOID) else if (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH0_LPS) CurrentIGI = CurrentIGI-2; - /* Lower bound checking */ /* RSSI Lower bound check */ @@ -683,7 +675,6 @@ void odm_FalseAlarmCounterStatistics(void *pDM_VOID) FalseAlmCnt->Cnt_OFDM_CCA + FalseAlmCnt->Cnt_CCK_CCA; } - void odm_FAThresholdCheck( void *pDM_VOID, bool bDFSBand, @@ -763,7 +754,6 @@ void odm_CCKPacketDetectionThresh(void *pDM_VOID) struct false_ALARM_STATISTICS *FalseAlmCnt = &pDM_Odm->FalseAlmCnt; u8 CurCCK_CCAThres; - if ( !(pDM_Odm->SupportAbility & ODM_BB_CCK_PD) || !(pDM_Odm->SupportAbility & ODM_BB_FA_CNT) diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c index 937002495d09..c88d669cb086 100644 --- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c +++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c @@ -113,7 +113,6 @@ static void odm_rx_phy_status_parsing(struct dm_odm_t *dm_odm, phy_info->rx_mimo_signal_quality[RF_PATH_A] = -1; phy_info->rx_mimo_signal_quality[RF_PATH_B] = -1; - if (is_cck_rate) { u8 cck_agc_rpt; @@ -255,7 +254,6 @@ static void odm_Process_RSSIForDM( u32 Weighting = 0; PSTA_INFO_T pEntry; - if (pPktinfo->station_id == 0xFF) return; @@ -386,7 +384,6 @@ static void odm_Process_RSSIForDM( } } - /* */ /* Endianness before calling this API */ /* */ diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.h b/drivers/staging/rtl8723bs/hal/odm_HWConfig.h index 0c3697c38e64..615145812ed2 100644 --- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.h +++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.h @@ -5,7 +5,6 @@ * ******************************************************************************/ - #ifndef __HALHWOUTSRC_H__ #define __HALHWOUTSRC_H__ diff --git a/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c b/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c index b0ca46aec1a5..6514a711e6bd 100644 --- a/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c +++ b/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c @@ -83,7 +83,6 @@ void odm_ConfigRFReg_8723B( } } - void odm_ConfigRF_RadioA_8723B(struct dm_odm_t *pDM_Odm, u32 Addr, u32 Data) { u32 content = 0x1000; /* RF_Content: radioa_txt */ diff --git a/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h b/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h index 6d7062a1cbbe..9ba8e06e5ad5 100644 --- a/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h +++ b/drivers/staging/rtl8723bs/hal/odm_RegDefine11N.h @@ -8,7 +8,6 @@ #ifndef __ODM_REGDEFINE11N_H__ #define __ODM_REGDEFINE11N_H__ - /* 2 RF REG LIST */ #define ODM_REG_RF_MODE_11N 0x00 #define ODM_REG_RF_0B_11N 0x0B @@ -152,7 +151,6 @@ #define ODM_REG_ANT_TRAIN_PARA1_11N 0x7b0 #define ODM_REG_ANT_TRAIN_PARA2_11N 0x7b4 - /* DIG Related */ #define ODM_BIT_IGI_11N 0x0000007F #define ODM_BIT_CCK_RPT_FORMAT_11N BIT(9) diff --git a/drivers/staging/rtl8723bs/hal/odm_interface.h b/drivers/staging/rtl8723bs/hal/odm_interface.h index c3b13f5f37db..f79c3d57edff 100644 --- a/drivers/staging/rtl8723bs/hal/odm_interface.h +++ b/drivers/staging/rtl8723bs/hal/odm_interface.h @@ -5,12 +5,9 @@ * ******************************************************************************/ - #ifndef __ODM_INTERFACE_H__ #define __ODM_INTERFACE_H__ - - /* =========== Macro Define */ #define _reg_all(_name) ODM_##_name diff --git a/drivers/staging/rtl8723bs/hal/odm_types.h b/drivers/staging/rtl8723bs/hal/odm_types.h index bc0c4d027492..25f0edd716cc 100644 --- a/drivers/staging/rtl8723bs/hal/odm_types.h +++ b/drivers/staging/rtl8723bs/hal/odm_types.h @@ -20,7 +20,6 @@ enum hal_status { HAL_STATUS_FAILURE, }; - #if defined(__LITTLE_ENDIAN) #define ODM_ENDIAN_TYPE ODM_ENDIAN_LITTLE #else diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index 2485c415f14b..c35c7f1c38ef 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -30,7 +30,6 @@ static u8 _is_fw_read_cmd_down(struct adapter *padapter, u8 msgbox_num) return !ret; } - /***************************************** * H2C Msg format : *| 31 - 8 |7-5 | 4 - 0 | @@ -167,15 +166,12 @@ static void ConstructBeacon(struct adapter *padapter, u8 *pframe, u32 *pLength) pframe = rtw_set_ie(pframe, WLAN_EID_IBSS_PARAMS, 2, (unsigned char *)(&ATIMWindow), &pktlen); } - /* todo: ERP IE */ - /* EXTERNDED SUPPORTED RATE */ if (rate_len > 8) pframe = rtw_set_ie(pframe, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (cur_network->supported_rates + 8), &pktlen); - /* todo:HT for adhoc */ _ConstructBeacon: diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c index 612351010359..2276dc1cb44a 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_dm.c @@ -99,7 +99,6 @@ static void Update_ODM_ComInfo_8723b(struct adapter *Adapter) ODM_CmnInfoHook(pDM_Odm, ODM_CMNINFO_SCAN, &(pmlmepriv->bScanInProcess)); ODM_CmnInfoHook(pDM_Odm, ODM_CMNINFO_POWER_SAVING, &(pwrctrlpriv->bpower_saving)); - for (i = 0; i < NUM_STA; i++) ODM_CmnInfoPtrArrayHook(pDM_Odm, ODM_CMNINFO_STA_STATUS, i, NULL); } @@ -183,7 +182,6 @@ void rtl8723b_hal_dm_in_lps(struct adapter *padapter) /* update IGI */ ODM_Write_DIG(pDM_Odm, pDM_Odm->RSSI_Min); - /* set rssi to fw */ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); if (psta && (psta->rssi_stat.UndecoratedSmoothedPWDB > 0)) { @@ -208,7 +206,6 @@ void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *Adapter) if (!Adapter->hw_init_completed) goto skip_lps_dm; - if (rtw_linked_check(Adapter)) bLinked = true; @@ -220,7 +217,6 @@ void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *Adapter) if (!(pDM_Odm->SupportAbility & ODM_BB_RSSI_MONITOR)) goto skip_lps_dm; - /* ODM_DMWatchdog(&pHalData->odmpriv); */ /* Do DIG by RSSI In LPS-32K */ @@ -245,7 +241,6 @@ void rtl8723b_HalDmWatchDog_in_LPS(struct adapter *Adapter) ) rtw_dm_in_lps_wk_cmd(Adapter); - skip_lps_dm: return; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 2a748367fbba..bcaf63b2893c 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -152,7 +152,6 @@ void _8051Reset8723(struct adapter *padapter) u8 cpu_rst; u8 io_rst; - /* Reset 8051(WLMCU) IO wrapper */ /* 0x1c[8] = 0 */ /* Suggested by Isaac@SD1 and Gimmy@SD1, coding by Lucas@20130624 */ @@ -571,7 +570,6 @@ void Hal_EfusePowerSwitch( u8 tempval; u16 tmpV16; - if (PwrState) { /* To avoid cannot access efuse registers after disable/enable several times during DTM test. */ /* Suggested by SD1 IsaacHsu. 2013.07.08, added by tynli. */ @@ -579,7 +577,6 @@ void Hal_EfusePowerSwitch( if (tempval & BIT(0)) { /* SDIO local register is suspend */ u8 count = 0; - tempval &= ~BIT(0); rtw_write8(padapter, SDIO_LOCAL_BASE|SDIO_REG_HSUS_CTRL, tempval); @@ -721,7 +718,6 @@ static void hal_ReadEFuse_BT( u16 i, total, used; u8 efuse_usage; - /* */ /* Do NOT excess total size of EFuse table. Added by Roger, 2008.11.10. */ /* */ @@ -756,7 +752,6 @@ static void hal_ReadEFuse_BT( if (ALL_WORDS_DISABLED(efuseExtHdr)) continue; - offset |= ((efuseExtHdr & 0xF0) >> 1); wden = (efuseExtHdr & 0x0F); } else { @@ -859,7 +854,6 @@ void rtl8723b_InitBeaconParameters(struct adapter *padapter) u16 val16; u8 val8 = DIS_TSF_UDT; - val16 = val8 | (val8 << 8); /* port0 and port1 */ /* Enable prot0 beacon function for PSTDMA */ @@ -959,7 +953,6 @@ void rtl8723b_SetBeaconRelatedRegisters(struct adapter *padapter) /* REG_DUAL_TSF_RST */ /* REG_BCN_CTRL (0x550) */ - bcn_ctrl_reg = REG_BCN_CTRL; /* */ @@ -1062,7 +1055,6 @@ void rtl8723b_init_default_value(struct adapter *padapter) struct dm_priv *pdmpriv; u8 i; - pHalData = GET_HAL_DATA(padapter); pdmpriv = &pHalData->dmpriv; @@ -1190,7 +1182,6 @@ void Hal_EfuseParseIDCode(struct adapter *padapter, u8 *hwinfo) /* struct hal_com_data *pHalData = GET_HAL_DATA(padapter); */ u16 EEPROMId; - /* Check 0x8129 again for making sure autoload status!! */ EEPROMId = le16_to_cpu(*((__le16 *)hwinfo)); if (EEPROMId != RTL_EEPROM_ID) { @@ -1313,7 +1304,6 @@ static void Hal_ReadPowerValueFromPROM_8723B( } } - void Hal_EfuseParseTxPowerInfo_8723B( struct adapter *padapter, u8 *PROMContent, bool AutoLoadFail ) @@ -1426,8 +1416,6 @@ void Hal_EfuseParseEEPROMVer_8723B( pHalData->EEPROMVersion = 1; } - - void Hal_EfuseParsePackageType_8723B( struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail ) @@ -1461,7 +1449,6 @@ void Hal_EfuseParsePackageType_8723B( } } - void Hal_EfuseParseVoltage_8723B( struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail ) @@ -1513,7 +1500,6 @@ void Hal_EfuseParseXtal_8723B( pHalData->CrystalCap = EEPROM_Default_CrystalCap_8723B; } - void Hal_EfuseParseThermalMeter_8723B( struct adapter *padapter, u8 *PROMContent, u8 AutoLoadFail ) @@ -1534,7 +1520,6 @@ void Hal_EfuseParseThermalMeter_8723B( } } - void Hal_ReadRFGainOffset( struct adapter *Adapter, u8 *PROMContent, bool AutoloadFail ) @@ -1603,7 +1588,6 @@ static void rtl8723b_cal_txdesc_chksum(struct tx_desc *ptxdesc) u32 index; u16 checksum = 0; - /* Clear first */ ptxdesc->txdw7 &= cpu_to_le32(0xffff0000); @@ -2098,7 +2082,6 @@ static void hw_var_set_mlme_sitesurvey(struct adapter *padapter, u8 variable, u8 struct hal_com_data *pHalData; struct mlme_priv *pmlmepriv; - pHalData = GET_HAL_DATA(padapter); pmlmepriv = &padapter->mlmepriv; @@ -2161,7 +2144,6 @@ static void hw_var_set_mlme_join(struct adapter *padapter, u8 variable, u8 *val) struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct eeprom_priv *pEEPROM; - pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); if (type == 0) { /* prepare to join */ @@ -2225,7 +2207,6 @@ s32 c2h_id_filter_ccx_8723b(u8 *buf) return ret; } - s32 c2h_handler_8723b(struct adapter *padapter, u8 *buf) { struct c2h_evt_hdr_88xx *pC2hEvent = (struct c2h_evt_hdr_88xx *)buf; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c index 5564f3f20ba5..da4cb28276b2 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c @@ -31,11 +31,9 @@ /*---------------------------Define Local Constant---------------------------*/ /*---------------------------Define Local Constant---------------------------*/ - /*------------------------Define global variable-----------------------------*/ /*------------------------Define global variable-----------------------------*/ - /*------------------------Define local variable------------------------------*/ /* 2008/11/20 MH For Debug only, RF */ /*------------------------Define local variable------------------------------*/ @@ -149,7 +147,6 @@ static int phy_RF6052_Config_ParaFile(struct adapter *Adapter) return _SUCCESS; } - int PHY_RF6052_Config8723B(struct adapter *Adapter) { struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rxdesc.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rxdesc.c index db3d7d72bffa..ac1249676794 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_rxdesc.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rxdesc.c @@ -49,7 +49,6 @@ static void process_link_qual(struct adapter *padapter, union recv_frame *prfram signal_stat->avg_val = signal_stat->total_val / signal_stat->total_num; } /* Process_UiLinkQuality8192S */ - void rtl8723b_process_phy_info(struct adapter *padapter, void *prframe) { union recv_frame *precvframe = prframe; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c index 888029e21dcb..97dce28fd0a3 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c @@ -110,7 +110,6 @@ static void update_recvframe_phyinfo(union recv_frame *precvframe, pkt_info.to_self = pkt_info.bssid_match && ether_addr_equal(rx_ra, my_hwaddr); - pkt_info.is_beacon = pkt_info.bssid_match && (GetFrameSubType(wlanhdr) == WIFI_BEACON); diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index 49d556a72834..c75bff728483 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -393,7 +393,6 @@ static s32 rtl8723bs_xmit_handler(struct adapter *padapter) if (ret == 1) goto next; - return _SUCCESS; } @@ -469,7 +468,6 @@ s32 rtl8723bs_hal_xmit( struct xmit_priv *pxmitpriv; s32 err; - pxmitframe->attrib.qsel = pxmitframe->attrib.priority; pxmitpriv = &padapter->xmitpriv; @@ -529,7 +527,6 @@ s32 rtl8723bs_init_xmit_priv(struct adapter *padapter) struct xmit_priv *xmitpriv = &padapter->xmitpriv; struct hal_com_data *phal; - phal = GET_HAL_DATA(padapter); spin_lock_init(&phal->SdioTxFIFOFreePageLock); @@ -547,7 +544,6 @@ void rtl8723bs_free_xmit_priv(struct adapter *padapter) struct list_head *plist, *phead; struct list_head tmplist; - phead = get_list_head(pqueue); INIT_LIST_HEAD(&tmplist); From 95f290497329b7433bc6dfef062bd34b8f66e976 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 13 May 2026 21:37:16 +0000 Subject: [PATCH 1412/5214] staging: rtl8723bs: simplify _rtw_enqueue_cmd control flow Replace the goto exit pattern with a direct return to simplify the control flow and improve readability. No functional change intended. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260513213719.12246-2-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index f01b0a221b6a..9c21980a80e0 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -253,7 +253,7 @@ int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) unsigned long irqL; if (!obj) - goto exit; + return _SUCCESS; /* spin_lock_bh(&queue->lock); */ spin_lock_irqsave(&queue->lock, irqL); @@ -263,7 +263,6 @@ int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) /* spin_unlock_bh(&queue->lock); */ spin_unlock_irqrestore(&queue->lock, irqL); -exit: return _SUCCESS; } From 4c6cfd7f25ee13fe54c65d5dcdd58efb3e0e1450 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 13 May 2026 21:37:17 +0000 Subject: [PATCH 1413/5214] staging: rtl8723bs: make _rtw_enqueue_cmd static The function _rtw_enqueue_cmd() is only used within rtw_cmd.c, so make it static and remove the unnecessary declaration from the header file. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260513213719.12246-3-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/include/cmd_osdep.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 9c21980a80e0..a9b06c10c9aa 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -248,7 +248,7 @@ void _rtw_free_cmd_priv(struct cmd_priv *pcmdpriv) * */ -int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) +static int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) { unsigned long irqL; diff --git a/drivers/staging/rtl8723bs/include/cmd_osdep.h b/drivers/staging/rtl8723bs/include/cmd_osdep.h index 41daa3c339a8..91d933d71945 100644 --- a/drivers/staging/rtl8723bs/include/cmd_osdep.h +++ b/drivers/staging/rtl8723bs/include/cmd_osdep.h @@ -11,7 +11,6 @@ int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv); int rtw_init_evt_priv(struct evt_priv *pevtpriv); extern void _rtw_free_evt_priv(struct evt_priv *pevtpriv); extern void _rtw_free_cmd_priv(struct cmd_priv *pcmdpriv); -int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj); extern struct cmd_obj *_rtw_dequeue_cmd(struct __queue *queue); #endif From 65f92917a927f55b99a3f0beade26543678c27d8 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 13 May 2026 21:37:18 +0000 Subject: [PATCH 1414/5214] staging: rtl8723bs: simplify rtw_enqueue_cmd control flow Replace the goto exit pattern with direct returns to simplify the control flow and improve readability. No functional change intended. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260513213719.12246-4-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index a9b06c10c9aa..169e20bbd90b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -313,18 +313,18 @@ int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj) int rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj) { - int res = _FAIL; + int res; struct adapter *padapter = pcmdpriv->padapter; if (!cmd_obj) - goto exit; + return _FAIL; cmd_obj->padapter = padapter; res = rtw_cmd_filter(pcmdpriv, cmd_obj); if (res == _FAIL) { rtw_free_cmd_obj(cmd_obj); - goto exit; + return _FAIL; } res = _rtw_enqueue_cmd(&pcmdpriv->cmd_queue, cmd_obj); @@ -332,7 +332,6 @@ int rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj) if (res == _SUCCESS) complete(&pcmdpriv->cmd_queue_comp); -exit: return res; } From 254eb1212108779da5d313709314f5dfa9387f0e Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 13 May 2026 21:37:19 +0000 Subject: [PATCH 1415/5214] staging: rtl8723bs: make _rtw_enqueue_cmd return 0 on success Convert the private helper _rtw_enqueue_cmd() to return 0 on success instead of the legacy _SUCCESS value. Keep rtw_enqueue_cmd() translating the result back to the existing return convention for now. No functional change intended. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260513213719.12246-5-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 169e20bbd90b..b932670f5d63 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -253,7 +253,7 @@ static int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) unsigned long irqL; if (!obj) - return _SUCCESS; + return 0; /* spin_lock_bh(&queue->lock); */ spin_lock_irqsave(&queue->lock, irqL); @@ -263,7 +263,7 @@ static int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj) /* spin_unlock_bh(&queue->lock); */ spin_unlock_irqrestore(&queue->lock, irqL); - return _SUCCESS; + return 0; } struct cmd_obj *_rtw_dequeue_cmd(struct __queue *queue) @@ -329,10 +329,12 @@ int rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj) res = _rtw_enqueue_cmd(&pcmdpriv->cmd_queue, cmd_obj); - if (res == _SUCCESS) + if (res == 0) { complete(&pcmdpriv->cmd_queue_comp); + return _SUCCESS; + } - return res; + return _FAIL; } struct cmd_obj *rtw_dequeue_cmd(struct cmd_priv *pcmdpriv) From dd711d244b5d413df1f8b99bab57950f89c050eb Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 14 May 2026 10:07:04 +0000 Subject: [PATCH 1416/5214] staging: rtl8723bs: simplify rtw_xmit_classifier control flow Simplify rtw_xmit_classifier() by removing the exit label and using direct returns for error handling. No functional change. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260514100708.25031-2-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index f090d25b0ce3..6e76a94cccdd 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -1867,16 +1867,13 @@ s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) struct tx_servq *ptxservq; struct pkt_attrib *pattrib = &pxmitframe->attrib; struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits; - signed int res = _SUCCESS; psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra); if (pattrib->psta != psta) return _FAIL; - if (!psta) { - res = _FAIL; - goto exit; - } + if (!psta) + return _FAIL; if (!(psta->state & _FW_LINKED)) return _FAIL; @@ -1890,9 +1887,7 @@ s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) ptxservq->qcnt++; phwxmits[ac_index].accnt++; -exit: - - return res; + return _SUCCESS; } void rtw_free_hwxmits(struct adapter *padapter) From 2803ff08e7983265302ef97892367bc0336bdce3 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 14 May 2026 10:07:05 +0000 Subject: [PATCH 1417/5214] staging: rtl8723bs: make rtw_xmit_classifier static The rtw_xmit_classifier() function is only used within rtw_xmit.c. Move it above its caller and make it static to limit its scope. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260514100708.25031-3-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 68 ++++++++++---------- drivers/staging/rtl8723bs/include/rtw_xmit.h | 1 - 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 6e76a94cccdd..b9dc54cdf401 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -1814,6 +1814,40 @@ void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue *pfram spin_unlock_bh(&pframequeue->lock); } +/* + * Will enqueue pxmitframe to the proper queue, + * and indicate it to xx_pending list..... + */ +static s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) +{ + u8 ac_index; + struct sta_info *psta; + struct tx_servq *ptxservq; + struct pkt_attrib *pattrib = &pxmitframe->attrib; + struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits; + + psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra); + if (pattrib->psta != psta) + return _FAIL; + + if (!psta) + return _FAIL; + + if (!(psta->state & _FW_LINKED)) + return _FAIL; + + ptxservq = rtw_get_sta_pending(padapter, psta, pattrib->priority, (u8 *)(&ac_index)); + + if (list_empty(&ptxservq->tx_pending)) + list_add_tail(&ptxservq->tx_pending, get_list_head(phwxmits[ac_index].sta_queue)); + + list_add_tail(&pxmitframe->list, get_list_head(&ptxservq->sta_pending)); + ptxservq->qcnt++; + phwxmits[ac_index].accnt++; + + return _SUCCESS; +} + s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) { if (rtw_xmit_classifier(padapter, pxmitframe) == _FAIL) @@ -1856,40 +1890,6 @@ struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info * return ptxservq; } -/* - * Will enqueue pxmitframe to the proper queue, - * and indicate it to xx_pending list..... - */ -s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) -{ - u8 ac_index; - struct sta_info *psta; - struct tx_servq *ptxservq; - struct pkt_attrib *pattrib = &pxmitframe->attrib; - struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits; - - psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra); - if (pattrib->psta != psta) - return _FAIL; - - if (!psta) - return _FAIL; - - if (!(psta->state & _FW_LINKED)) - return _FAIL; - - ptxservq = rtw_get_sta_pending(padapter, psta, pattrib->priority, (u8 *)(&ac_index)); - - if (list_empty(&ptxservq->tx_pending)) - list_add_tail(&ptxservq->tx_pending, get_list_head(phwxmits[ac_index].sta_queue)); - - list_add_tail(&pxmitframe->list, get_list_head(&ptxservq->sta_pending)); - ptxservq->qcnt++; - phwxmits[ac_index].accnt++; - - return _SUCCESS; -} - void rtw_free_hwxmits(struct adapter *padapter) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index 056c2ae990f2..01c0f85a50f1 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -421,7 +421,6 @@ extern void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info *psta, signed int up, u8 *ac); extern s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); -extern s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe); extern u32 rtw_calculate_wlan_pkt_size_by_attribue(struct pkt_attrib *pattrib); #define rtw_wlan_pkt_size(f) rtw_calculate_wlan_pkt_size_by_attribue(&f->attrib) extern s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct xmit_frame *pxmitframe); From 091729b2e09dbd97309abc59246d727602014c4a Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 14 May 2026 10:07:06 +0000 Subject: [PATCH 1418/5214] staging: rtl8723bs: convert rtw_xmit_classifier to return errno Convert rtw_xmit_classifier() to return 0 on success and negative error codes on failure. Update the caller to check for non-zero return values and preserve the existing _FAIL/_SUCCESS semantics. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260514100708.25031-4-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index b9dc54cdf401..46ee8f43064a 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -1818,7 +1818,7 @@ void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue *pfram * Will enqueue pxmitframe to the proper queue, * and indicate it to xx_pending list..... */ -static s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) +static int rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe) { u8 ac_index; struct sta_info *psta; @@ -1828,13 +1828,13 @@ static s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmi psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra); if (pattrib->psta != psta) - return _FAIL; + return -ENODEV; if (!psta) - return _FAIL; + return -EINVAL; if (!(psta->state & _FW_LINKED)) - return _FAIL; + return -ENETDOWN; ptxservq = rtw_get_sta_pending(padapter, psta, pattrib->priority, (u8 *)(&ac_index)); @@ -1845,12 +1845,15 @@ static s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmi ptxservq->qcnt++; phwxmits[ac_index].accnt++; - return _SUCCESS; + return 0; } s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) { - if (rtw_xmit_classifier(padapter, pxmitframe) == _FAIL) + int res; + + res = rtw_xmit_classifier(padapter, pxmitframe); + if (res) return _FAIL; return _SUCCESS; From 37dca26788f7523375f53506abd8e1ae4a117dd4 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 14 May 2026 10:07:07 +0000 Subject: [PATCH 1419/5214] staging: rtl8723bs: propagate errno through xmit enqueue path Propagate errno values from rtw_xmit_classifier() through rtw_xmitframe_enqueue(), and update the local enqueue caller to check for non-zero return values. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260514100708.25031-5-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_xmit.c | 6 +++--- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 10 +++++----- drivers/staging/rtl8723bs/include/rtw_xmit.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 46ee8f43064a..444966c0de7f 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -1848,15 +1848,15 @@ static int rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmi return 0; } -s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) +int rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) { int res; res = rtw_xmit_classifier(padapter, pxmitframe); if (res) - return _FAIL; + return res; - return _SUCCESS; + return 0; } struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info *psta, signed int up, u8 *ac) diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index c75bff728483..f4f3632f0646 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -458,8 +458,8 @@ s32 rtl8723bs_mgnt_xmit( *Handle xmitframe(packet) come from rtw_xmit() * * Return: - *true dump packet directly ok - *false enqueue, temporary can't transmit packets to hardware + * true dump packet directly ok + * false enqueue, temporary can't transmit packets to hardware */ s32 rtl8723bs_hal_xmit( struct adapter *padapter, struct xmit_frame *pxmitframe @@ -484,7 +484,7 @@ s32 rtl8723bs_hal_xmit( spin_lock_bh(&pxmitpriv->lock); err = rtw_xmitframe_enqueue(padapter, pxmitframe); spin_unlock_bh(&pxmitpriv->lock); - if (err != _SUCCESS) { + if (err) { rtw_free_xmitframe(pxmitpriv, pxmitframe); pxmitpriv->tx_drop++; @@ -504,7 +504,7 @@ s32 rtl8723bs_hal_xmitframe_enqueue( s32 err; err = rtw_xmitframe_enqueue(padapter, pxmitframe); - if (err != _SUCCESS) { + if (err) { rtw_free_xmitframe(pxmitpriv, pxmitframe); pxmitpriv->tx_drop++; @@ -512,7 +512,7 @@ s32 rtl8723bs_hal_xmitframe_enqueue( complete(&pxmitpriv->SdioXmitStart); } - return err; + return err ? _FAIL : _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index 01c0f85a50f1..f67cb22e3396 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -419,7 +419,7 @@ struct xmit_frame *rtw_alloc_xmitframe_once(struct xmit_priv *pxmitpriv); extern s32 rtw_free_xmitframe(struct xmit_priv *pxmitpriv, struct xmit_frame *pxmitframe); extern void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue *pframequeue); struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info *psta, signed int up, u8 *ac); -extern s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); +int rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); extern u32 rtw_calculate_wlan_pkt_size_by_attribue(struct pkt_attrib *pattrib); #define rtw_wlan_pkt_size(f) rtw_calculate_wlan_pkt_size_by_attribue(&f->attrib) From bfe73cfa779f55ce5fe1899133cf6ca86720a86a Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 14 May 2026 10:07:08 +0000 Subject: [PATCH 1420/5214] staging: rtl8723bs: propagate errno through hal xmit path Propagate errno values from rtl8723bs_hal_xmitframe_enqueue() through rtw_hal_xmitframe_enqueue() by returning the error code directly. Update rtw_hal_xmit() to explicitly map the boolean return value of rtl8723bs_hal_xmit() to _SUCCESS/_FAIL, clarifying the return semantics at the HAL boundary. None of the callers of rtw_hal_xmitframe_enqueue() check the return value, so they do not need to be updated. This change does not affect runtime behavior. Signed-off-by: Hungyu Lin Link: https://patch.msgid.link/20260514100708.25031-6-dennylin0707@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/hal_intf.c | 7 +++++-- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/hal_intf.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/hal_intf.c b/drivers/staging/rtl8723bs/hal/hal_intf.c index 19e16270381e..9a3ebad35efc 100644 --- a/drivers/staging/rtl8723bs/hal/hal_intf.c +++ b/drivers/staging/rtl8723bs/hal/hal_intf.c @@ -99,14 +99,17 @@ u8 rtw_hal_check_ips_status(struct adapter *padapter) return CheckIPSStatus(padapter); } -s32 rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) +int rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) { return rtl8723bs_hal_xmitframe_enqueue(padapter, pxmitframe); } s32 rtw_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe) { - return rtl8723bs_hal_xmit(padapter, pxmitframe); + if (rtl8723bs_hal_xmit(padapter, pxmitframe)) + return _FAIL; + + return _SUCCESS; } /* diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index f4f3632f0646..e40f69c13c44 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -512,7 +512,7 @@ s32 rtl8723bs_hal_xmitframe_enqueue( complete(&pxmitpriv->SdioXmitStart); } - return err ? _FAIL : _SUCCESS; + return err; } diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 9b000681ff80..ee8ad26325b5 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -195,7 +195,7 @@ void rtw_hal_set_odm_var(struct adapter *padapter, enum hal_odm_variable eVariab u8 rtw_hal_check_ips_status(struct adapter *padapter); -s32 rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); +int rtw_hal_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe); s32 rtw_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe); s32 rtw_hal_mgnt_xmit(struct adapter *padapter, struct xmit_frame *pmgntframe); From ddf58ed94154c883590c58ceec05b77cacf5805e Mon Sep 17 00:00:00 2001 From: Len Bao Date: Sat, 16 May 2026 14:23:21 +0000 Subject: [PATCH 1421/5214] staging: sm750fb: Mark g_noaccel, g_nomtrr and g_dualview as __ro_after_init The 'g_noaccel', 'g_nomtrr' and 'g_dualview' variables are initialized only during the init phase in the 'lynxfb_setup' function and never changed. So, mark them as __ro_after_init. Signed-off-by: Len Bao Link: https://patch.msgid.link/20260516142326.36018-1-len.bao@gmx.us Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index d26e9fab846a..89c811e0806c 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -19,12 +19,12 @@ /* common var for all device */ static int g_hwcursor = 1; -static int g_noaccel; -static int g_nomtrr; +static int g_noaccel __ro_after_init; +static int g_nomtrr __ro_after_init; static const char *g_fbmode[] = {NULL, NULL}; static const char *g_def_fbmode = "1024x768-32@60"; static char *g_settings; -static int g_dualview; +static int g_dualview __ro_after_init; static char *g_option; static const struct fb_videomode lynx750_ext[] = { From c5357b3566309ebc817f02133afc2affeb54f42e Mon Sep 17 00:00:00 2001 From: Jennifer Guo Date: Sat, 16 May 2026 21:43:30 -0700 Subject: [PATCH 1422/5214] staging: rtl8723bs: delete superfluous switch statement This switch statement doesn't do anything in all of its branches. As such we can clean it up, and only keep the assignment in the else block. Signed-off-by: Jennifer Guo Link: https://patch.msgid.link/20260517044330.8517-1-guojy.bj@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/hal/HalBtc8723b1Ant.c | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index 17b0d8c2ace4..0613beaa3752 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -1906,40 +1906,6 @@ static void halbtc8723b1ant_RunSwCoexistMechanism(struct btc_coexist *pBtCoexist if (halbtc8723b1ant_IsCommonAction(pBtCoexist)) { } else { - switch (pCoexDm->curAlgorithm) { - case BT_8723B_1ANT_COEX_ALGO_SCO: - /* halbtc8723b1ant_ActionSco(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_HID: - /* halbtc8723b1ant_ActionHid(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_A2DP: - /* halbtc8723b1ant_ActionA2dp(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_A2DP_PANHS: - /* halbtc8723b1ant_ActionA2dpPanHs(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR: - /* halbtc8723b1ant_ActionPanEdr(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_PANHS: - /* halbtc8723b1ant_ActionPanHs(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR_A2DP: - /* halbtc8723b1ant_ActionPanEdrA2dp(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR_HID: - /* halbtc8723b1ant_ActionPanEdrHid(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_HID_A2DP_PANEDR: - /* halbtc8723b1ant_ActionHidA2dpPanEdr(pBtCoexist); */ - break; - case BT_8723B_1ANT_COEX_ALGO_HID_A2DP: - /* halbtc8723b1ant_ActionHidA2dp(pBtCoexist); */ - break; - default: - break; - } pCoexDm->preAlgorithm = pCoexDm->curAlgorithm; } } From d5c28c0db2ef88132e4502108cac726bac1ab715 Mon Sep 17 00:00:00 2001 From: Rupesh Majhi Date: Sun, 17 May 2026 16:19:18 +0300 Subject: [PATCH 1423/5214] staging: sm750: rename CamelCase variable Bpp to bpp Rename the CamelCase variable 'Bpp' to 'bpp' in both the header and implementation files to comply with Linux kernel coding style. Issue found by checkpatch. Signed-off-by: Rupesh Majhi Link: https://patch.msgid.link/20260517131918.197943-1-zoone.rupert@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/sm750fb/sm750_accel.c | 22 +++++++++++----------- drivers/staging/sm750fb/sm750_accel.h | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 03f5678fa430..0316ea69d009 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -71,7 +71,7 @@ void sm750_hw_set2dformat(struct lynx_accel *accel, int fmt) } int sm750_hw_fillrect(struct lynx_accel *accel, - u32 base, u32 pitch, u32 Bpp, + u32 base, u32 pitch, u32 bpp, u32 x, u32 y, u32 width, u32 height, u32 color, u32 rop) { @@ -89,14 +89,14 @@ int sm750_hw_fillrect(struct lynx_accel *accel, write_dpr(accel, DE_WINDOW_DESTINATION_BASE, base); /* dpr40 */ write_dpr(accel, DE_PITCH, - ((pitch / Bpp << DE_PITCH_DESTINATION_SHIFT) & + ((pitch / bpp << DE_PITCH_DESTINATION_SHIFT) & DE_PITCH_DESTINATION_MASK) | - (pitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ + (pitch / bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ write_dpr(accel, DE_WINDOW_WIDTH, - ((pitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) & + ((pitch / bpp << DE_WINDOW_WIDTH_DST_SHIFT) & DE_WINDOW_WIDTH_DST_MASK) | - (pitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr44 */ + (pitch / bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr44 */ write_dpr(accel, DE_FOREGROUND, color); /* DPR14 */ @@ -125,7 +125,7 @@ int sm750_hw_fillrect(struct lynx_accel *accel, * @sy: Starting y coordinate of source surface * @dest_base: Address of destination: offset in frame buffer * @dest_pitch: Pitch value of destination surface in BYTE - * @Bpp: Color depth of destination surface + * @bpp: Color depth of destination surface * @dx: Starting x coordinate of destination surface * @dy: Starting y coordinate of destination surface * @width: width of rectangle in pixel value @@ -136,7 +136,7 @@ int sm750_hw_copyarea(struct lynx_accel *accel, unsigned int source_base, unsigned int source_pitch, unsigned int sx, unsigned int sy, unsigned int dest_base, unsigned int dest_pitch, - unsigned int Bpp, unsigned int dx, unsigned int dy, + unsigned int bpp, unsigned int dx, unsigned int dy, unsigned int width, unsigned int height, unsigned int rop2) { @@ -236,9 +236,9 @@ int sm750_hw_copyarea(struct lynx_accel *accel, * pixel values. Need Byte to pixel conversion. */ write_dpr(accel, DE_PITCH, - ((dest_pitch / Bpp << DE_PITCH_DESTINATION_SHIFT) & + ((dest_pitch / bpp << DE_PITCH_DESTINATION_SHIFT) & DE_PITCH_DESTINATION_MASK) | - (source_pitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ + (source_pitch / bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ /* * Screen Window width in Pixels. @@ -246,9 +246,9 @@ int sm750_hw_copyarea(struct lynx_accel *accel, * for a given point. */ write_dpr(accel, DE_WINDOW_WIDTH, - ((dest_pitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) & + ((dest_pitch / bpp << DE_WINDOW_WIDTH_DST_SHIFT) & DE_WINDOW_WIDTH_DST_MASK) | - (source_pitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr3c */ + (source_pitch / bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr3c */ ret = accel->de_wait(); if (ret) diff --git a/drivers/staging/sm750fb/sm750_accel.h b/drivers/staging/sm750fb/sm750_accel.h index 2c79cb730a0a..d15a40cacb84 100644 --- a/drivers/staging/sm750fb/sm750_accel.h +++ b/drivers/staging/sm750fb/sm750_accel.h @@ -190,7 +190,7 @@ void sm750_hw_set2dformat(struct lynx_accel *accel, int fmt); void sm750_hw_de_init(struct lynx_accel *accel); int sm750_hw_fillrect(struct lynx_accel *accel, - u32 base, u32 pitch, u32 Bpp, + u32 base, u32 pitch, u32 bpp, u32 x, u32 y, u32 width, u32 height, u32 color, u32 rop); @@ -202,7 +202,7 @@ int sm750_hw_fillrect(struct lynx_accel *accel, * @sy: Starting y coordinate of source surface * @dBase: Address of destination: offset in frame buffer * @dPitch: Pitch value of destination surface in BYTE - * @Bpp: Color depth of destination surface + * @bpp: Color depth of destination surface * @dx: Starting x coordinate of destination surface * @dy: Starting y coordinate of destination surface * @width: width of rectangle in pixel value @@ -213,7 +213,7 @@ int sm750_hw_copyarea(struct lynx_accel *accel, unsigned int sBase, unsigned int sPitch, unsigned int sx, unsigned int sy, unsigned int dBase, unsigned int dPitch, - unsigned int Bpp, unsigned int dx, unsigned int dy, + unsigned int bpp, unsigned int dx, unsigned int dy, unsigned int width, unsigned int height, unsigned int rop2); From 7cb1c5b32a2bfde961fff8d5204526b609bcb30a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sun, 17 May 2026 19:12:18 +0800 Subject: [PATCH 1424/5214] staging: most: video: avoid double free on video register failure comp_register_videodev() allocates a video_device with video_device_alloc() and releases it if video_register_device() fails. This can double free the video_device when __video_register_device() reaches device_register() and that call fails: video_register_device() -> __video_register_device() -> device_register() fails -> put_device(&vdev->dev) -> v4l2_device_release() -> vdev->release(vdev) -> video_device_release(vdev) comp_register_videodev() -> video_device_release(mdev->vdev) Use video_device_release_empty() while registering the device so that registration failure paths do not free mdev->vdev through vdev->release(). comp_register_videodev() then releases mdev->vdev exactly once on failure. Restore video_device_release() after successful registration so the registered device keeps its normal lifetime handling. This issue was found by a static analysis tool I am developing. Fixes: eab231c0398a ("staging: most: v4l2-aim: remove unnecessary label err_vbi_dev") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260517111218.945796-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/video/video.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index f846bb343b2e..78234620d48d 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -420,6 +420,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Fill the video capture device struct */ *mdev->vdev = comp_videodev_template; + mdev->vdev->release = video_device_release_empty; mdev->vdev->v4l2_dev = &mdev->v4l2_dev; mdev->vdev->lock = &mdev->lock; snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", @@ -432,9 +433,13 @@ static int comp_register_videodev(struct most_video_dev *mdev) v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); video_device_release(mdev->vdev); + return ret; } - return ret; + mdev->vdev->release = video_device_release; + + return 0; + } static void comp_unregister_videodev(struct most_video_dev *mdev) From 8c3ff7c5ae15cc71000f10f4d0f26669b9471faa Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 21 May 2026 12:40:00 +0200 Subject: [PATCH 1425/5214] thunderbolt: Move pci_device out of tb_nhi Not all USB4/TB implementations are based on a PCIe-attached controller. In order to make way for these, start off with moving the pci_device reference out of the main tb_nhi structure. Encapsulate the existing struct in a new tb_nhi_pci, that shall also house all properties that relate to the parent bus. Similarly, any other type of controller will be expected to contain tb_nhi as a member. Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/acpi.c | 14 +-- drivers/thunderbolt/ctl.c | 16 ++-- drivers/thunderbolt/domain.c | 2 +- drivers/thunderbolt/eeprom.c | 2 +- drivers/thunderbolt/icm.c | 24 ++--- drivers/thunderbolt/nhi.c | 155 ++++++++++++++++++-------------- drivers/thunderbolt/nhi_ops.c | 26 +++--- drivers/thunderbolt/switch.c | 6 +- drivers/thunderbolt/tb.c | 11 +-- drivers/thunderbolt/tb.h | 10 +-- drivers/thunderbolt/usb4_port.c | 2 +- include/linux/thunderbolt.h | 8 +- 12 files changed, 154 insertions(+), 122 deletions(-) diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c index 45d1415871b4..53546bc477a5 100644 --- a/drivers/thunderbolt/acpi.c +++ b/drivers/thunderbolt/acpi.c @@ -28,7 +28,7 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, return AE_OK; /* It needs to reference this NHI */ - if (dev_fwnode(&nhi->pdev->dev) != fwnode) + if (dev_fwnode(nhi->dev) != fwnode) goto out_put; /* @@ -57,16 +57,16 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, */ pm_runtime_get_sync(&pdev->dev); - link = device_link_add(&pdev->dev, &nhi->pdev->dev, + link = device_link_add(&pdev->dev, nhi->dev, DL_FLAG_AUTOREMOVE_SUPPLIER | DL_FLAG_RPM_ACTIVE | DL_FLAG_PM_RUNTIME); if (link) { - dev_dbg(&nhi->pdev->dev, "created link from %s\n", + dev_dbg(nhi->dev, "created link from %s\n", dev_name(&pdev->dev)); *(bool *)ret = true; } else { - dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", + dev_warn(nhi->dev, "device link creation from %s failed\n", dev_name(&pdev->dev)); } @@ -93,7 +93,7 @@ bool tb_acpi_add_links(struct tb_nhi *nhi) acpi_status status; bool ret = false; - if (!has_acpi_companion(&nhi->pdev->dev)) + if (!has_acpi_companion(nhi->dev)) return false; /* @@ -103,7 +103,7 @@ bool tb_acpi_add_links(struct tb_nhi *nhi) status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 32, tb_acpi_add_link, NULL, nhi, (void **)&ret); if (ACPI_FAILURE(status)) { - dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n"); + dev_warn(nhi->dev, "failed to enumerate tunneled ports\n"); return false; } @@ -305,7 +305,7 @@ static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw) struct tb_nhi *nhi = sw->tb->nhi; struct acpi_device *parent_adev; - parent_adev = ACPI_COMPANION(&nhi->pdev->dev); + parent_adev = ACPI_COMPANION(nhi->dev); if (parent_adev) adev = acpi_find_child_device(parent_adev, 0, false); } diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index b2fd60fc7bcc..cd47b627f97b 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -56,22 +56,22 @@ struct tb_ctl { #define tb_ctl_WARN(ctl, format, arg...) \ - dev_WARN(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_WARN((ctl)->nhi->dev, format, ## arg) #define tb_ctl_err(ctl, format, arg...) \ - dev_err(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_err((ctl)->nhi->dev, format, ## arg) #define tb_ctl_warn(ctl, format, arg...) \ - dev_warn(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_warn((ctl)->nhi->dev, format, ## arg) #define tb_ctl_info(ctl, format, arg...) \ - dev_info(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_info((ctl)->nhi->dev, format, ## arg) #define tb_ctl_dbg(ctl, format, arg...) \ - dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_dbg((ctl)->nhi->dev, format, ## arg) #define tb_ctl_dbg_once(ctl, format, arg...) \ - dev_dbg_once(&(ctl)->nhi->pdev->dev, format, ## arg) + dev_dbg_once((ctl)->nhi->dev, format, ## arg) static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue); /* Serializes access to request kref_get/put */ @@ -666,8 +666,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int index, int timeout_msec, mutex_init(&ctl->request_queue_lock); INIT_LIST_HEAD(&ctl->request_queue); - ctl->frame_pool = dma_pool_create("thunderbolt_ctl", &nhi->pdev->dev, - TB_FRAME_SIZE, 4, 0); + ctl->frame_pool = dma_pool_create("thunderbolt_ctl", nhi->dev, + TB_FRAME_SIZE, 4, 0); if (!ctl->frame_pool) goto err; diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index b381f184340e..479fa4d265c2 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -405,7 +405,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize if (!tb->ctl) goto err_destroy_wq; - tb->dev.parent = &nhi->pdev->dev; + tb->dev.parent = nhi->dev; tb->dev.bus = &tb_bus_type; tb->dev.type = &tb_domain_type; tb->dev.groups = domain_attr_groups; diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 5477b9437048..5681c17f82ec 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -465,7 +465,7 @@ static void tb_switch_drom_free(struct tb_switch *sw) */ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size) { - struct device *dev = &sw->tb->nhi->pdev->dev; + struct device *dev = sw->tb->nhi->dev; int len, res; len = device_property_count_u8(dev, "ThunderboltDROM"); diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index c492995166f7..10fefac3b1d9 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1466,6 +1466,7 @@ static struct pci_dev *get_upstream_port(struct pci_dev *pdev) static bool icm_ar_is_supported(struct tb *tb) { + struct pci_dev *pdev = to_pci_dev(tb->nhi->dev); struct pci_dev *upstream_port; struct icm *icm = tb_priv(tb); @@ -1483,7 +1484,7 @@ static bool icm_ar_is_supported(struct tb *tb) * Find the upstream PCIe port in case we need to do reset * through its vendor specific registers. */ - upstream_port = get_upstream_port(tb->nhi->pdev); + upstream_port = get_upstream_port(pdev); if (upstream_port) { int cap; @@ -1519,7 +1520,7 @@ static int icm_ar_get_mode(struct tb *tb) } while (--retries); if (!retries) { - dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); + dev_err(nhi->dev, "ICM firmware not authenticated\n"); return -ENODEV; } @@ -1685,11 +1686,11 @@ icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, static void icm_icl_set_uuid(struct tb *tb) { - struct tb_nhi *nhi = tb->nhi; + struct pci_dev *pdev = to_pci_dev(tb->nhi->dev); u32 uuid[4]; - pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]); - pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]); + pci_read_config_dword(pdev, VS_CAP_10, &uuid[0]); + pci_read_config_dword(pdev, VS_CAP_11, &uuid[1]); uuid[2] = 0xffffffff; uuid[3] = 0xffffffff; @@ -1866,7 +1867,7 @@ static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) if (icm_firmware_running(nhi)) return 0; - dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n"); + dev_dbg(nhi->dev, "starting ICM firmware\n"); ret = icm_firmware_reset(tb, nhi); if (ret) @@ -1961,7 +1962,7 @@ static int icm_firmware_init(struct tb *tb) ret = icm_firmware_start(tb, nhi); if (ret) { - dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); + dev_err(nhi->dev, "could not start ICM firmware\n"); return ret; } @@ -1993,10 +1994,10 @@ static int icm_firmware_init(struct tb *tb) */ ret = icm_reset_phy_port(tb, 0); if (ret) - dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); + dev_warn(nhi->dev, "failed to reset links on port0\n"); ret = icm_reset_phy_port(tb, 1); if (ret) - dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); + dev_warn(nhi->dev, "failed to reset links on port1\n"); return 0; } @@ -2477,6 +2478,7 @@ static const struct tb_cm_ops icm_icl_ops = { struct tb *icm_probe(struct tb_nhi *nhi) { + struct pci_dev *pdev = to_pci_dev(nhi->dev); struct icm *icm; struct tb *tb; @@ -2488,7 +2490,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); mutex_init(&icm->request_lock); - switch (nhi->pdev->device) { + switch (pdev->device) { case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: icm->can_upgrade_nvm = true; @@ -2594,7 +2596,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) } if (!icm->is_supported || !icm->is_supported(tb)) { - dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); + dev_dbg(nhi->dev, "ICM not supported on this controller\n"); tb_domain_put(tb); return NULL; } diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index a0a789bfb680..d21c8d330f6c 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -2,7 +2,7 @@ /* * Thunderbolt driver - NHI driver * - * The NHI (native host interface) is the pci device that allows us to send and + * The NHI (native host interface) is the device that allows us to send and * receive frames from the thunderbolt bus. * * Copyright (c) 2014 Andreas Noever @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -51,6 +50,21 @@ static bool host_reset = true; module_param(host_reset, bool, 0444); MODULE_PARM_DESC(host_reset, "reset USB4 host router (default: true)"); +/** + * struct tb_nhi_pci - NHI device connected over PCIe + * @nhi: NHI device + * @msix_ida: Used to allocate MSI-X vectors for rings + */ +struct tb_nhi_pci { + struct tb_nhi nhi; + struct ida msix_ida; +}; + +static inline struct tb_nhi_pci *nhi_to_pci(struct tb_nhi *nhi) +{ + return container_of(nhi, struct tb_nhi_pci, nhi); +} + static int ring_interrupt_index(const struct tb_ring *ring) { int bit = ring->hop; @@ -145,15 +159,14 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) else new = old & ~mask; - dev_dbg(&ring->nhi->pdev->dev, + dev_dbg(ring->nhi->dev, "%s interrupt at register %#x bit %d (%#x -> %#x)\n", active ? "enabling" : "disabling", reg, interrupt_bit, old, new); if (new == old) - dev_WARN(&ring->nhi->pdev->dev, - "interrupt for %s %d is already %s\n", - RING_TYPE(ring), ring->hop, - str_enabled_disabled(active)); + dev_WARN(ring->nhi->dev, "interrupt for %s %d is already %s\n", + RING_TYPE(ring), ring->hop, + str_enabled_disabled(active)); if (active) iowrite32(new, ring->nhi->iobase + reg); @@ -470,19 +483,21 @@ static irqreturn_t ring_msix(int irq, void *data) static int ring_request_msix(struct tb_ring *ring, bool no_suspend) { struct tb_nhi *nhi = ring->nhi; + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); unsigned long irqflags; int ret; - if (!nhi->pdev->msix_enabled) + if (!pdev->msix_enabled) return 0; - ret = ida_alloc_max(&nhi->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL); + ret = ida_alloc_max(&nhi_pci->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL); if (ret < 0) return ret; ring->vector = ret; - ret = pci_irq_vector(ring->nhi->pdev, ring->vector); + ret = pci_irq_vector(pdev, ring->vector); if (ret < 0) goto err_ida_remove; @@ -496,18 +511,20 @@ static int ring_request_msix(struct tb_ring *ring, bool no_suspend) return 0; err_ida_remove: - ida_free(&nhi->msix_ida, ring->vector); + ida_free(&nhi_pci->msix_ida, ring->vector); return ret; } static void ring_release_msix(struct tb_ring *ring) { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(ring->nhi); + if (ring->irq <= 0) return; free_irq(ring->irq, ring); - ida_free(&ring->nhi->msix_ida, ring->vector); + ida_free(&nhi_pci->msix_ida, ring->vector); ring->vector = 0; ring->irq = 0; } @@ -520,7 +537,7 @@ static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring) if (nhi->quirks & QUIRK_E2E) { start_hop = RING_FIRST_USABLE_HOPID + 1; if (ring->flags & RING_FLAG_E2E && !ring->is_tx) { - dev_dbg(&nhi->pdev->dev, "quirking E2E TX HopID %u -> %u\n", + dev_dbg(nhi->dev, "quirking E2E TX HopID %u -> %u\n", ring->e2e_tx_hop, RING_E2E_RESERVED_HOPID); ring->e2e_tx_hop = RING_E2E_RESERVED_HOPID; } @@ -551,23 +568,23 @@ static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring) } if (ring->hop > 0 && ring->hop < start_hop) { - dev_warn(&nhi->pdev->dev, "invalid hop: %d\n", ring->hop); + dev_warn(nhi->dev, "invalid hop: %d\n", ring->hop); ret = -EINVAL; goto err_unlock; } if (ring->hop < 0 || ring->hop >= nhi->hop_count) { - dev_warn(&nhi->pdev->dev, "invalid hop: %d\n", ring->hop); + dev_warn(nhi->dev, "invalid hop: %d\n", ring->hop); ret = -EINVAL; goto err_unlock; } if (ring->is_tx && nhi->tx_rings[ring->hop]) { - dev_warn(&nhi->pdev->dev, "TX hop %d already allocated\n", + dev_warn(nhi->dev, "TX hop %d already allocated\n", ring->hop); ret = -EBUSY; goto err_unlock; } if (!ring->is_tx && nhi->rx_rings[ring->hop]) { - dev_warn(&nhi->pdev->dev, "RX hop %d already allocated\n", + dev_warn(nhi->dev, "RX hop %d already allocated\n", ring->hop); ret = -EBUSY; goto err_unlock; @@ -592,7 +609,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, { struct tb_ring *ring = NULL; - dev_dbg(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", + dev_dbg(nhi->dev, "allocating %s ring %d of size %d\n", transmit ? "TX" : "RX", hop, size); ring = kzalloc_obj(*ring); @@ -619,9 +636,9 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->start_poll = start_poll; ring->poll_data = poll_data; - ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, - size * sizeof(*ring->descriptors), - &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO); + ring->descriptors = dma_alloc_coherent(ring->nhi->dev, + size * sizeof(*ring->descriptors), + &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO); if (!ring->descriptors) goto err_free_ring; @@ -636,7 +653,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, err_release_msix: ring_release_msix(ring); err_free_descs: - dma_free_coherent(&ring->nhi->pdev->dev, + dma_free_coherent(ring->nhi->dev, ring->size * sizeof(*ring->descriptors), ring->descriptors, ring->descriptors_dma); err_free_ring: @@ -703,10 +720,10 @@ void tb_ring_start(struct tb_ring *ring) if (ring->nhi->going_away) goto err; if (ring->running) { - dev_WARN(&ring->nhi->pdev->dev, "ring already started\n"); + dev_WARN(ring->nhi->dev, "ring already started\n"); goto err; } - dev_dbg(&ring->nhi->pdev->dev, "starting %s %d\n", + dev_dbg(ring->nhi->dev, "starting %s %d\n", RING_TYPE(ring), ring->hop); if (ring->flags & RING_FLAG_FRAME) { @@ -743,11 +760,11 @@ void tb_ring_start(struct tb_ring *ring) hop &= REG_RX_OPTIONS_E2E_HOP_MASK; flags |= hop; - dev_dbg(&ring->nhi->pdev->dev, + dev_dbg(ring->nhi->dev, "enabling E2E for %s %d with TX HopID %d\n", RING_TYPE(ring), ring->hop, ring->e2e_tx_hop); } else { - dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n", + dev_dbg(ring->nhi->dev, "enabling E2E for %s %d\n", RING_TYPE(ring), ring->hop); } @@ -806,12 +823,12 @@ void tb_ring_stop(struct tb_ring *ring) { spin_lock_irq(&ring->nhi->lock); spin_lock(&ring->lock); - dev_dbg(&ring->nhi->pdev->dev, "stopping %s %d\n", + dev_dbg(ring->nhi->dev, "stopping %s %d\n", RING_TYPE(ring), ring->hop); if (ring->nhi->going_away) goto err; if (!ring->running) { - dev_WARN(&ring->nhi->pdev->dev, "%s %d already stopped\n", + dev_WARN(ring->nhi->dev, "%s %d already stopped\n", RING_TYPE(ring), ring->hop); goto err; } @@ -860,14 +877,14 @@ void tb_ring_free(struct tb_ring *ring) ring->nhi->rx_rings[ring->hop] = NULL; if (ring->running) { - dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n", + dev_WARN(ring->nhi->dev, "%s %d still running\n", RING_TYPE(ring), ring->hop); } spin_unlock_irq(&ring->nhi->lock); ring_release_msix(ring); - dma_free_coherent(&ring->nhi->pdev->dev, + dma_free_coherent(ring->nhi->dev, ring->size * sizeof(*ring->descriptors), ring->descriptors, ring->descriptors_dma); @@ -875,7 +892,7 @@ void tb_ring_free(struct tb_ring *ring) ring->descriptors_dma = 0; - dev_dbg(&ring->nhi->pdev->dev, "freeing %s %d\n", RING_TYPE(ring), + dev_dbg(ring->nhi->dev, "freeing %s %d\n", RING_TYPE(ring), ring->hop); /* @@ -994,9 +1011,7 @@ static void nhi_interrupt_work(struct work_struct *work) if ((value & (1 << (bit % 32))) == 0) continue; if (type == 2) { - dev_warn(&nhi->pdev->dev, - "RX overflow for ring %d\n", - hop); + dev_warn(nhi->dev, "RX overflow for ring %d\n", hop); continue; } if (type == 0) @@ -1004,7 +1019,7 @@ static void nhi_interrupt_work(struct work_struct *work) else ring = nhi->rx_rings[hop]; if (ring == NULL) { - dev_warn(&nhi->pdev->dev, + dev_warn(nhi->dev, "got interrupt for inactive %s ring %d\n", type ? "RX" : "TX", hop); @@ -1173,16 +1188,18 @@ static int nhi_runtime_resume(struct device *dev) static void nhi_shutdown(struct tb_nhi *nhi) { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); int i; - dev_dbg(&nhi->pdev->dev, "shutdown\n"); + dev_dbg(nhi->dev, "shutdown\n"); for (i = 0; i < nhi->hop_count; i++) { if (nhi->tx_rings[i]) - dev_WARN(&nhi->pdev->dev, + dev_WARN(nhi->dev, "TX ring %d is still active\n", i); if (nhi->rx_rings[i]) - dev_WARN(&nhi->pdev->dev, + dev_WARN(nhi->dev, "RX ring %d is still active\n", i); } nhi_disable_interrupts(nhi); @@ -1190,19 +1207,22 @@ static void nhi_shutdown(struct tb_nhi *nhi) * We have to release the irq before calling flush_work. Otherwise an * already executing IRQ handler could call schedule_work again. */ - if (!nhi->pdev->msix_enabled) { - devm_free_irq(&nhi->pdev->dev, nhi->pdev->irq, nhi); + if (!pdev->msix_enabled) { + devm_free_irq(nhi->dev, pdev->irq, nhi); flush_work(&nhi->interrupt_work); } - ida_destroy(&nhi->msix_ida); + ida_destroy(&nhi_pci->msix_ida); if (nhi->ops && nhi->ops->shutdown) nhi->ops->shutdown(nhi); } -static void nhi_check_quirks(struct tb_nhi *nhi) +static void nhi_check_quirks(struct tb_nhi_pci *nhi_pci) { - if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL) { + struct tb_nhi *nhi = &nhi_pci->nhi; + struct pci_dev *pdev = to_pci_dev(nhi->dev); + + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { /* * Intel hardware supports auto clear of the interrupt * status register right after interrupt is being @@ -1210,7 +1230,7 @@ static void nhi_check_quirks(struct tb_nhi *nhi) */ nhi->quirks |= QUIRK_AUTO_CLEAR_INT; - switch (nhi->pdev->device) { + switch (pdev->device) { case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: /* @@ -1224,7 +1244,7 @@ static void nhi_check_quirks(struct tb_nhi *nhi) } } -static int nhi_check_iommu_pdev(struct pci_dev *pdev, void *data) +static int nhi_check_iommu_pci_dev(struct pci_dev *pdev, void *data) { if (!pdev->external_facing || !device_iommu_capable(&pdev->dev, IOMMU_CAP_PRE_BOOT_PROTECTION)) @@ -1233,9 +1253,11 @@ static int nhi_check_iommu_pdev(struct pci_dev *pdev, void *data) return 1; /* Stop walking */ } -static void nhi_check_iommu(struct tb_nhi *nhi) +static void nhi_check_iommu(struct tb_nhi_pci *nhi_pci) { - struct pci_bus *bus = nhi->pdev->bus; + struct tb_nhi *nhi = &nhi_pci->nhi; + struct pci_dev *pdev = to_pci_dev(nhi->dev); + struct pci_bus *bus = pdev->bus; bool port_ok = false; /* @@ -1258,10 +1280,10 @@ static void nhi_check_iommu(struct tb_nhi *nhi) while (bus->parent) bus = bus->parent; - pci_walk_bus(bus, nhi_check_iommu_pdev, &port_ok); + pci_walk_bus(bus, nhi_check_iommu_pci_dev, &port_ok); nhi->iommu_dma_protection = port_ok; - dev_dbg(&nhi->pdev->dev, "IOMMU DMA protection is %s\n", + dev_dbg(nhi->dev, "IOMMU DMA protection is %s\n", str_enabled_disabled(port_ok)); } @@ -1276,7 +1298,7 @@ static void nhi_reset(struct tb_nhi *nhi) return; if (!host_reset) { - dev_dbg(&nhi->pdev->dev, "skipping host router reset\n"); + dev_dbg(nhi->dev, "skipping host router reset\n"); return; } @@ -1287,25 +1309,23 @@ static void nhi_reset(struct tb_nhi *nhi) do { val = ioread32(nhi->iobase + REG_RESET); if (!(val & REG_RESET_HRR)) { - dev_warn(&nhi->pdev->dev, "host router reset successful\n"); + dev_warn(nhi->dev, "host router reset successful\n"); return; } usleep_range(10, 20); } while (ktime_before(ktime_get(), timeout)); - dev_warn(&nhi->pdev->dev, "timeout resetting host router\n"); + dev_warn(nhi->dev, "timeout resetting host router\n"); } -static int nhi_init_msi(struct tb_nhi *nhi) +static int nhi_init_msi(struct tb_nhi_pci *nhi_pci) { - struct pci_dev *pdev = nhi->pdev; + struct tb_nhi *nhi = &nhi_pci->nhi; + struct pci_dev *pdev = to_pci_dev(nhi->dev); struct device *dev = &pdev->dev; int res, irq, nvec; - /* In case someone left them on. */ - nhi_disable_interrupts(nhi); - - ida_init(&nhi->msix_ida); + ida_init(&nhi_pci->msix_ida); /* * The NHI has 16 MSI-X vectors or a single MSI. We first try to @@ -1322,7 +1342,7 @@ static int nhi_init_msi(struct tb_nhi *nhi) INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work); - irq = pci_irq_vector(nhi->pdev, 0); + irq = pci_irq_vector(pdev, 0); if (irq < 0) return irq; @@ -1371,6 +1391,7 @@ static struct tb *nhi_select_cm(struct tb_nhi *nhi) static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; + struct tb_nhi_pci *nhi_pci; struct tb_nhi *nhi; struct tb *tb; int res; @@ -1382,11 +1403,12 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (res) return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n"); - nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL); - if (!nhi) + nhi_pci = devm_kzalloc(dev, sizeof(*nhi_pci), GFP_KERNEL); + if (!nhi_pci) return -ENOMEM; - nhi->pdev = pdev; + nhi = &nhi_pci->nhi; + nhi->dev = dev; nhi->ops = (const struct tb_nhi_ops *)id->driver_data; nhi->iobase = pcim_iomap_region(pdev, 0, "thunderbolt"); @@ -1404,11 +1426,14 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!nhi->tx_rings || !nhi->rx_rings) return -ENOMEM; - nhi_check_quirks(nhi); - nhi_check_iommu(nhi); + nhi_check_quirks(nhi_pci); + nhi_check_iommu(nhi_pci); nhi_reset(nhi); - res = nhi_init_msi(nhi); + /* In case someone left them on. */ + nhi_disable_interrupts(nhi); + + res = nhi_init_msi(nhi_pci); if (res) return dev_err_probe(dev, res, "cannot enable MSI, aborting\n"); diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c index 96da07e88c52..8c50066f3411 100644 --- a/drivers/thunderbolt/nhi_ops.c +++ b/drivers/thunderbolt/nhi_ops.c @@ -24,7 +24,7 @@ static int check_for_device(struct device *dev, void *data) static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) { - struct tb *tb = pci_get_drvdata(nhi->pdev); + struct tb *tb = dev_get_drvdata(nhi->dev); int ret; ret = device_for_each_child(&tb->root_switch->dev, NULL, @@ -34,6 +34,7 @@ static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) { + struct pci_dev *pdev = to_pci_dev(nhi->dev); u32 vs_cap; /* @@ -48,7 +49,7 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) * The actual power management happens inside shared ACPI power * resources using standard ACPI methods. */ - pci_read_config_dword(nhi->pdev, VS_CAP_22, &vs_cap); + pci_read_config_dword(pdev, VS_CAP_22, &vs_cap); if (power) { vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; @@ -56,7 +57,7 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) } else { vs_cap &= ~VS_CAP_22_FORCE_POWER; } - pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap); + pci_write_config_dword(pdev, VS_CAP_22, vs_cap); if (power) { unsigned int retries = 350; @@ -64,7 +65,7 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) /* Wait until the firmware tells it is up and running */ do { - pci_read_config_dword(nhi->pdev, VS_CAP_9, &val); + pci_read_config_dword(pdev, VS_CAP_9, &val); if (val & VS_CAP_9_FW_READY) return 0; usleep_range(3000, 3100); @@ -78,14 +79,16 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) { + struct pci_dev *pdev = to_pci_dev(nhi->dev); u32 data; data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; - pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID); + pci_write_config_dword(pdev, VS_CAP_19, data | VS_CAP_19_VALID); } static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) { + struct pci_dev *pdev = to_pci_dev(nhi->dev); unsigned long end; u32 data; @@ -94,7 +97,7 @@ static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) end = jiffies + msecs_to_jiffies(timeout); do { - pci_read_config_dword(nhi->pdev, VS_CAP_18, &data); + pci_read_config_dword(pdev, VS_CAP_18, &data); if (data & VS_CAP_18_DONE) goto clear; usleep_range(1000, 1100); @@ -104,24 +107,25 @@ static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) clear: /* Clear the valid bit */ - pci_write_config_dword(nhi->pdev, VS_CAP_19, 0); + pci_write_config_dword(pdev, VS_CAP_19, 0); return 0; } static void icl_nhi_set_ltr(struct tb_nhi *nhi) { + struct pci_dev *pdev = to_pci_dev(nhi->dev); u32 max_ltr, ltr; - pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr); + pci_read_config_dword(pdev, VS_CAP_16, &max_ltr); max_ltr &= 0xffff; /* Program the same value for both snoop and no-snoop */ ltr = max_ltr << 16 | max_ltr; - pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr); + pci_write_config_dword(pdev, VS_CAP_15, ltr); } static int icl_nhi_suspend(struct tb_nhi *nhi) { - struct tb *tb = pci_get_drvdata(nhi->pdev); + struct tb *tb = dev_get_drvdata(nhi->dev); int ret; if (icl_nhi_is_device_connected(nhi)) @@ -144,7 +148,7 @@ static int icl_nhi_suspend(struct tb_nhi *nhi) static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) { - struct tb *tb = pci_get_drvdata(nhi->pdev); + struct tb *tb = dev_get_drvdata(nhi->dev); enum icl_lc_mailbox_cmd cmd; if (!pm_suspend_via_firmware()) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index d7c53eb3221b..769b57ff276b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -211,6 +211,7 @@ static int nvm_authenticate_device_dma_port(struct tb_switch *sw) static void nvm_authenticate_start_dma_port(struct tb_switch *sw) { + struct pci_dev *pdev = to_pci_dev(sw->tb->nhi->dev); struct pci_dev *root_port; /* @@ -219,16 +220,17 @@ static void nvm_authenticate_start_dma_port(struct tb_switch *sw) * itself. To be on the safe side keep the root port in D0 during * the whole upgrade process. */ - root_port = pcie_find_root_port(sw->tb->nhi->pdev); + root_port = pcie_find_root_port(pdev); if (root_port) pm_runtime_get_noresume(&root_port->dev); } static void nvm_authenticate_complete_dma_port(struct tb_switch *sw) { + struct pci_dev *pdev = to_pci_dev(sw->tb->nhi->dev); struct pci_dev *root_port; - root_port = pcie_find_root_port(sw->tb->nhi->pdev); + root_port = pcie_find_root_port(pdev); if (root_port) pm_runtime_put(&root_port->dev); } diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 72a0dd27937e..d60c0b8eb390 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -3310,13 +3310,14 @@ static const struct tb_cm_ops tb_cm_ops = { */ static bool tb_apple_add_links(struct tb_nhi *nhi) { + struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev); struct pci_dev *upstream, *pdev; bool ret; if (!x86_apple_machine) return false; - switch (nhi->pdev->device) { + switch (nhi_pdev->device) { case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C: case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: @@ -3326,7 +3327,7 @@ static bool tb_apple_add_links(struct tb_nhi *nhi) return false; } - upstream = pci_upstream_bridge(nhi->pdev); + upstream = pci_upstream_bridge(nhi_pdev); while (upstream) { if (!pci_is_pcie(upstream)) return false; @@ -3353,15 +3354,15 @@ static bool tb_apple_add_links(struct tb_nhi *nhi) !pdev->is_pciehp) continue; - link = device_link_add(&pdev->dev, &nhi->pdev->dev, + link = device_link_add(&pdev->dev, nhi->dev, DL_FLAG_AUTOREMOVE_SUPPLIER | DL_FLAG_PM_RUNTIME); if (link) { - dev_dbg(&nhi->pdev->dev, "created link from %s\n", + dev_dbg(nhi->dev, "created link from %s\n", dev_name(&pdev->dev)); ret = true; } else { - dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", + dev_warn(nhi->dev, "device link creation from %s failed\n", dev_name(&pdev->dev)); } } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 003db653418a..ec9192b61bc0 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -725,11 +725,11 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, length); } -#define tb_err(tb, fmt, arg...) dev_err(&(tb)->nhi->pdev->dev, fmt, ## arg) -#define tb_WARN(tb, fmt, arg...) dev_WARN(&(tb)->nhi->pdev->dev, fmt, ## arg) -#define tb_warn(tb, fmt, arg...) dev_warn(&(tb)->nhi->pdev->dev, fmt, ## arg) -#define tb_info(tb, fmt, arg...) dev_info(&(tb)->nhi->pdev->dev, fmt, ## arg) -#define tb_dbg(tb, fmt, arg...) dev_dbg(&(tb)->nhi->pdev->dev, fmt, ## arg) +#define tb_err(tb, fmt, arg...) dev_err((tb)->nhi->dev, fmt, ## arg) +#define tb_WARN(tb, fmt, arg...) dev_WARN((tb)->nhi->dev, fmt, ## arg) +#define tb_warn(tb, fmt, arg...) dev_warn((tb)->nhi->dev, fmt, ## arg) +#define tb_info(tb, fmt, arg...) dev_info((tb)->nhi->dev, fmt, ## arg) +#define tb_dbg(tb, fmt, arg...) dev_dbg((tb)->nhi->dev, fmt, ## arg) #define __TB_SW_PRINT(level, sw, fmt, arg...) \ do { \ diff --git a/drivers/thunderbolt/usb4_port.c b/drivers/thunderbolt/usb4_port.c index c32d3516e780..890de530debc 100644 --- a/drivers/thunderbolt/usb4_port.c +++ b/drivers/thunderbolt/usb4_port.c @@ -138,7 +138,7 @@ bool usb4_usb3_port_match(struct device *usb4_port_dev, return false; /* Check if USB3 fwnode references same NHI where USB4 port resides */ - if (!device_match_fwnode(&nhi->pdev->dev, nhi_fwnode)) + if (!device_match_fwnode(nhi->dev, nhi_fwnode)) return false; if (fwnode_property_read_u8(usb3_port_fwnode, "usb4-port-number", &usb4_port_num)) diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 0be9b298e692..b5659f883517 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -498,12 +498,11 @@ void tb_service_properties_changed(struct tb_service *svc); * struct tb_nhi - thunderbolt native host interface * @lock: Must be held during ring creation/destruction. Is acquired by * interrupt_work when dispatching interrupts to individual rings. - * @pdev: Pointer to the PCI device + * @dev: Device associated with this NHI instance * @ops: NHI specific optional ops * @iobase: MMIO space of the NHI * @tx_rings: All Tx rings available on this host controller * @rx_rings: All Rx rings available on this host controller - * @msix_ida: Used to allocate MSI-X vectors for rings * @going_away: The host controller device is about to disappear so when * this flag is set, avoid touching the hardware anymore. * @iommu_dma_protection: An IOMMU will isolate external-facing ports. @@ -515,12 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc); */ struct tb_nhi { spinlock_t lock; - struct pci_dev *pdev; + struct device *dev; const struct tb_nhi_ops *ops; void __iomem *iobase; struct tb_ring **tx_rings; struct tb_ring **rx_rings; - struct ida msix_ida; bool going_away; bool iommu_dma_protection; struct work_struct interrupt_work; @@ -722,7 +720,7 @@ int tb_ring_throttling(struct tb_ring *ring, unsigned int interval_nsec); */ static inline struct device *tb_ring_dma_device(struct tb_ring *ring) { - return &ring->nhi->pdev->dev; + return ring->nhi->dev; } bool usb4_usb3_port_match(struct device *usb4_port_dev, From e241d98e04ef0513b78de2d87d8d1eb2993d9d34 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 21 May 2026 12:40:01 +0200 Subject: [PATCH 1426/5214] thunderbolt: Separate out common NHI bits Add a new file encapsulating most of the PCI NHI specifics (intentionally leaving some odd cookies behind to make the layering simpler). Most notably, separate out nhi_probe() to make it easier to register other types of NHIs. Also, fold in Intel Icelake (nhi_ops.c) support to contain all PCIe-related bits in pci.c. Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/nhi.c | 461 +++---------------------- drivers/thunderbolt/nhi.h | 33 +- drivers/thunderbolt/nhi_ops.c | 189 ----------- drivers/thunderbolt/pci.c | 622 ++++++++++++++++++++++++++++++++++ drivers/thunderbolt/switch.c | 43 +-- 6 files changed, 712 insertions(+), 638 deletions(-) delete mode 100644 drivers/thunderbolt/nhi_ops.c create mode 100644 drivers/thunderbolt/pci.c diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index c792b8084ffa..beb054c3126b 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only ccflags-y := -I$(src) obj-${CONFIG_USB4} := thunderbolt.o -thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o +thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o pci.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o clx.o diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index d21c8d330f6c..be00ffb04766 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -33,38 +33,13 @@ * transferred. */ #define RING_E2E_RESERVED_HOPID RING_FIRST_USABLE_HOPID -/* - * Minimal number of vectors when we use MSI-X. Two for control channel - * Rx/Tx and the rest four are for cross domain DMA paths. - */ -#define MSIX_MIN_VECS 6 -#define MSIX_MAX_VECS 16 #define NHI_MAILBOX_TIMEOUT 500 /* ms */ -/* Host interface quirks */ -#define QUIRK_AUTO_CLEAR_INT BIT(0) -#define QUIRK_E2E BIT(1) - static bool host_reset = true; module_param(host_reset, bool, 0444); MODULE_PARM_DESC(host_reset, "reset USB4 host router (default: true)"); -/** - * struct tb_nhi_pci - NHI device connected over PCIe - * @nhi: NHI device - * @msix_ida: Used to allocate MSI-X vectors for rings - */ -struct tb_nhi_pci { - struct tb_nhi nhi; - struct ida msix_ida; -}; - -static inline struct tb_nhi_pci *nhi_to_pci(struct tb_nhi *nhi) -{ - return container_of(nhi, struct tb_nhi_pci, nhi); -} - static int ring_interrupt_index(const struct tb_ring *ring) { int bit = ring->hop; @@ -179,7 +154,7 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) * * Use only during init and shutdown. */ -static void nhi_disable_interrupts(struct tb_nhi *nhi) +void nhi_disable_interrupts(struct tb_nhi *nhi) { int i = 0; /* disable interrupts */ @@ -466,7 +441,7 @@ static void ring_clear_msix(const struct tb_ring *ring) 4 * (ring->nhi->hop_count / 32)); } -static irqreturn_t ring_msix(int irq, void *data) +irqreturn_t ring_msix(int irq, void *data) { struct tb_ring *ring = data; @@ -480,55 +455,6 @@ static irqreturn_t ring_msix(int irq, void *data) return IRQ_HANDLED; } -static int ring_request_msix(struct tb_ring *ring, bool no_suspend) -{ - struct tb_nhi *nhi = ring->nhi; - struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); - struct pci_dev *pdev = to_pci_dev(nhi->dev); - unsigned long irqflags; - int ret; - - if (!pdev->msix_enabled) - return 0; - - ret = ida_alloc_max(&nhi_pci->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL); - if (ret < 0) - return ret; - - ring->vector = ret; - - ret = pci_irq_vector(pdev, ring->vector); - if (ret < 0) - goto err_ida_remove; - - ring->irq = ret; - - irqflags = no_suspend ? IRQF_NO_SUSPEND : 0; - ret = request_irq(ring->irq, ring_msix, irqflags, "thunderbolt", ring); - if (ret) - goto err_ida_remove; - - return 0; - -err_ida_remove: - ida_free(&nhi_pci->msix_ida, ring->vector); - - return ret; -} - -static void ring_release_msix(struct tb_ring *ring) -{ - struct tb_nhi_pci *nhi_pci = nhi_to_pci(ring->nhi); - - if (ring->irq <= 0) - return; - - free_irq(ring->irq, ring); - ida_free(&nhi_pci->msix_ida, ring->vector); - ring->vector = 0; - ring->irq = 0; -} - static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring) { unsigned int start_hop = RING_FIRST_USABLE_HOPID; @@ -642,8 +568,10 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, if (!ring->descriptors) goto err_free_ring; - if (ring_request_msix(ring, flags & RING_FLAG_NO_SUSPEND)) - goto err_free_descs; + if (nhi->ops && nhi->ops->request_ring_irq) { + if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) + goto err_free_descs; + } if (nhi_alloc_hop(nhi, ring)) goto err_release_msix; @@ -651,7 +579,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, return ring; err_release_msix: - ring_release_msix(ring); + if (nhi->ops && nhi->ops->release_ring_irq) + nhi->ops->release_ring_irq(ring); err_free_descs: dma_free_coherent(ring->nhi->dev, ring->size * sizeof(*ring->descriptors), @@ -866,6 +795,8 @@ EXPORT_SYMBOL_GPL(tb_ring_stop); */ void tb_ring_free(struct tb_ring *ring) { + struct tb_nhi *nhi = ring->nhi; + spin_lock_irq(&ring->nhi->lock); /* * Dissociate the ring from the NHI. This also ensures that @@ -882,7 +813,8 @@ void tb_ring_free(struct tb_ring *ring) } spin_unlock_irq(&ring->nhi->lock); - ring_release_msix(ring); + if (nhi->ops && nhi->ops->release_ring_irq) + nhi->ops->release_ring_irq(ring); dma_free_coherent(ring->nhi->dev, ring->size * sizeof(*ring->descriptors), @@ -983,7 +915,7 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi) return (enum nhi_fw_mode)val; } -static void nhi_interrupt_work(struct work_struct *work) +void nhi_interrupt_work(struct work_struct *work) { struct tb_nhi *nhi = container_of(work, typeof(*nhi), interrupt_work); int value = 0; /* Suppress uninitialized usage warning. */ @@ -1033,7 +965,7 @@ static void nhi_interrupt_work(struct work_struct *work) spin_unlock_irq(&nhi->lock); } -static irqreturn_t nhi_msi(int irq, void *data) +irqreturn_t nhi_msi(int irq, void *data) { struct tb_nhi *nhi = data; schedule_work(&nhi->interrupt_work); @@ -1042,8 +974,7 @@ static irqreturn_t nhi_msi(int irq, void *data) static int __nhi_suspend_noirq(struct device *dev, bool wakeup) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); struct tb_nhi *nhi = tb->nhi; int ret; @@ -1067,21 +998,19 @@ static int nhi_suspend_noirq(struct device *dev) static int nhi_freeze_noirq(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); return tb_domain_freeze_noirq(tb); } static int nhi_thaw_noirq(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); return tb_domain_thaw_noirq(tb); } -static bool nhi_wake_supported(struct pci_dev *pdev) +static bool nhi_wake_supported(struct device *dev) { u8 val; @@ -1089,7 +1018,7 @@ static bool nhi_wake_supported(struct pci_dev *pdev) * If power rails are sustainable for wakeup from S4 this * property is set by the BIOS. */ - if (!device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) + if (!device_property_read_u8(dev, "WAKE_SUPPORTED", &val)) return !!val; return true; @@ -1097,17 +1026,15 @@ static bool nhi_wake_supported(struct pci_dev *pdev) static int nhi_poweroff_noirq(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); bool wakeup; - wakeup = device_may_wakeup(dev) && nhi_wake_supported(pdev); + wakeup = device_may_wakeup(dev) && nhi_wake_supported(dev); return __nhi_suspend_noirq(dev, wakeup); } static int nhi_resume_noirq(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); struct tb_nhi *nhi = tb->nhi; int ret; @@ -1116,7 +1043,7 @@ static int nhi_resume_noirq(struct device *dev) * unplugged last device which causes the host controller to go * away on PCs. */ - if (!pci_device_is_present(pdev)) { + if ((nhi->ops->is_present && !nhi->ops->is_present(nhi))) { nhi->going_away = true; } else if (nhi->ops && nhi->ops->resume_noirq) { ret = nhi->ops->resume_noirq(nhi); @@ -1129,32 +1056,29 @@ static int nhi_resume_noirq(struct device *dev) static int nhi_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); return tb_domain_suspend(tb); } static void nhi_complete(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); /* * If we were runtime suspended when system suspend started, * schedule runtime resume now. It should bring the domain back * to functional state. */ - if (pm_runtime_suspended(&pdev->dev)) - pm_runtime_resume(&pdev->dev); + if (pm_runtime_suspended(dev)) + pm_runtime_resume(dev); else tb_domain_complete(tb); } static int nhi_runtime_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); struct tb_nhi *nhi = tb->nhi; int ret; @@ -1172,8 +1096,7 @@ static int nhi_runtime_suspend(struct device *dev) static int nhi_runtime_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct tb *tb = pci_get_drvdata(pdev); + struct tb *tb = dev_get_drvdata(dev); struct tb_nhi *nhi = tb->nhi; int ret; @@ -1186,10 +1109,8 @@ static int nhi_runtime_resume(struct device *dev) return tb_domain_runtime_resume(tb); } -static void nhi_shutdown(struct tb_nhi *nhi) +void nhi_shutdown(struct tb_nhi *nhi) { - struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); - struct pci_dev *pdev = to_pci_dev(nhi->dev); int i; dev_dbg(nhi->dev, "shutdown\n"); @@ -1203,90 +1124,11 @@ static void nhi_shutdown(struct tb_nhi *nhi) "RX ring %d is still active\n", i); } nhi_disable_interrupts(nhi); - /* - * We have to release the irq before calling flush_work. Otherwise an - * already executing IRQ handler could call schedule_work again. - */ - if (!pdev->msix_enabled) { - devm_free_irq(nhi->dev, pdev->irq, nhi); - flush_work(&nhi->interrupt_work); - } - ida_destroy(&nhi_pci->msix_ida); if (nhi->ops && nhi->ops->shutdown) nhi->ops->shutdown(nhi); } -static void nhi_check_quirks(struct tb_nhi_pci *nhi_pci) -{ - struct tb_nhi *nhi = &nhi_pci->nhi; - struct pci_dev *pdev = to_pci_dev(nhi->dev); - - if (pdev->vendor == PCI_VENDOR_ID_INTEL) { - /* - * Intel hardware supports auto clear of the interrupt - * status register right after interrupt is being - * issued. - */ - nhi->quirks |= QUIRK_AUTO_CLEAR_INT; - - switch (pdev->device) { - case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: - case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: - /* - * Falcon Ridge controller needs the end-to-end - * flow control workaround to avoid losing Rx - * packets when RING_FLAG_E2E is set. - */ - nhi->quirks |= QUIRK_E2E; - break; - } - } -} - -static int nhi_check_iommu_pci_dev(struct pci_dev *pdev, void *data) -{ - if (!pdev->external_facing || - !device_iommu_capable(&pdev->dev, IOMMU_CAP_PRE_BOOT_PROTECTION)) - return 0; - *(bool *)data = true; - return 1; /* Stop walking */ -} - -static void nhi_check_iommu(struct tb_nhi_pci *nhi_pci) -{ - struct tb_nhi *nhi = &nhi_pci->nhi; - struct pci_dev *pdev = to_pci_dev(nhi->dev); - struct pci_bus *bus = pdev->bus; - bool port_ok = false; - - /* - * Ideally what we'd do here is grab every PCI device that - * represents a tunnelling adapter for this NHI and check their - * status directly, but unfortunately USB4 seems to make it - * obnoxiously difficult to reliably make any correlation. - * - * So for now we'll have to bodge it... Hoping that the system - * is at least sane enough that an adapter is in the same PCI - * segment as its NHI, if we can find *something* on that segment - * which meets the requirements for Kernel DMA Protection, we'll - * take that to imply that firmware is aware and has (hopefully) - * done the right thing in general. We need to know that the PCI - * layer has seen the ExternalFacingPort property which will then - * inform the IOMMU layer to enforce the complete "untrusted DMA" - * flow, but also that the IOMMU driver itself can be trusted not - * to have been subverted by a pre-boot DMA attack. - */ - while (bus->parent) - bus = bus->parent; - - pci_walk_bus(bus, nhi_check_iommu_pci_dev, &port_ok); - - nhi->iommu_dma_protection = port_ok; - dev_dbg(nhi->dev, "IOMMU DMA protection is %s\n", - str_enabled_disabled(port_ok)); -} - static void nhi_reset(struct tb_nhi *nhi) { ktime_t timeout; @@ -1318,53 +1160,6 @@ static void nhi_reset(struct tb_nhi *nhi) dev_warn(nhi->dev, "timeout resetting host router\n"); } -static int nhi_init_msi(struct tb_nhi_pci *nhi_pci) -{ - struct tb_nhi *nhi = &nhi_pci->nhi; - struct pci_dev *pdev = to_pci_dev(nhi->dev); - struct device *dev = &pdev->dev; - int res, irq, nvec; - - ida_init(&nhi_pci->msix_ida); - - /* - * The NHI has 16 MSI-X vectors or a single MSI. We first try to - * get all MSI-X vectors and if we succeed, each ring will have - * one MSI-X. If for some reason that does not work out, we - * fallback to a single MSI. - */ - nvec = pci_alloc_irq_vectors(pdev, MSIX_MIN_VECS, MSIX_MAX_VECS, - PCI_IRQ_MSIX); - if (nvec < 0) { - nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (nvec < 0) - return nvec; - - INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work); - - irq = pci_irq_vector(pdev, 0); - if (irq < 0) - return irq; - - res = devm_request_irq(&pdev->dev, irq, nhi_msi, - IRQF_NO_SUSPEND, "thunderbolt", nhi); - if (res) - return dev_err_probe(dev, res, "request_irq failed, aborting\n"); - } - - return 0; -} - -static bool nhi_imr_valid(struct pci_dev *pdev) -{ - u8 val; - - if (!device_property_read_u8(&pdev->dev, "IMR_VALID", &val)) - return !!val; - - return true; -} - static struct tb *nhi_select_cm(struct tb_nhi *nhi) { struct tb *tb; @@ -1388,63 +1183,39 @@ static struct tb *nhi_select_cm(struct tb_nhi *nhi) return tb; } -static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +int nhi_probe(struct tb_nhi *nhi) { - struct device *dev = &pdev->dev; - struct tb_nhi_pci *nhi_pci; - struct tb_nhi *nhi; + struct device *dev = nhi->dev; struct tb *tb; int res; - if (!nhi_imr_valid(pdev)) - return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n"); - - res = pcim_enable_device(pdev); - if (res) - return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n"); - - nhi_pci = devm_kzalloc(dev, sizeof(*nhi_pci), GFP_KERNEL); - if (!nhi_pci) - return -ENOMEM; - - nhi = &nhi_pci->nhi; - nhi->dev = dev; - nhi->ops = (const struct tb_nhi_ops *)id->driver_data; - - nhi->iobase = pcim_iomap_region(pdev, 0, "thunderbolt"); - res = PTR_ERR_OR_ZERO(nhi->iobase); - if (res) - return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n"); - nhi->hop_count = ioread32(nhi->iobase + REG_CAPS) & 0x3ff; dev_dbg(dev, "total paths: %d\n", nhi->hop_count); - nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, + nhi->tx_rings = devm_kcalloc(dev, nhi->hop_count, sizeof(*nhi->tx_rings), GFP_KERNEL); - nhi->rx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, + nhi->rx_rings = devm_kcalloc(dev, nhi->hop_count, sizeof(*nhi->rx_rings), GFP_KERNEL); if (!nhi->tx_rings || !nhi->rx_rings) return -ENOMEM; - nhi_check_quirks(nhi_pci); - nhi_check_iommu(nhi_pci); nhi_reset(nhi); /* In case someone left them on. */ nhi_disable_interrupts(nhi); - res = nhi_init_msi(nhi_pci); - if (res) - return dev_err_probe(dev, res, "cannot enable MSI, aborting\n"); + if (nhi->ops && nhi->ops->init_interrupts) { + res = nhi->ops->init_interrupts(nhi); + if (res) + return dev_err_probe(dev, res, "cannot enable interrupts, aborting\n"); + } spin_lock_init(&nhi->lock); - res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + res = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (res) return dev_err_probe(dev, res, "failed to set DMA mask\n"); - pci_set_master(pdev); - if (nhi->ops && nhi->ops->init) { res = nhi->ops->init(nhi); if (res) @@ -1471,38 +1242,24 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) nhi_shutdown(nhi); return res; } - pci_set_drvdata(pdev, tb); + dev_set_drvdata(dev, tb); - device_wakeup_enable(&pdev->dev); + device_wakeup_enable(dev); - pm_runtime_allow(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, TB_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_allow(dev); + pm_runtime_set_autosuspend_delay(dev, TB_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_put_autosuspend(dev); return 0; } -static void nhi_remove(struct pci_dev *pdev) -{ - struct tb *tb = pci_get_drvdata(pdev); - struct tb_nhi *nhi = tb->nhi; - - pm_runtime_get_sync(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_forbid(&pdev->dev); - - tb_domain_remove(tb); - wait_for_completion(&nhi->domain_released); - nhi_shutdown(nhi); -} - /* * The tunneled pci bridges are siblings of us. Use resume_noirq to reenable * the tunnels asap. A corresponding pci quirk blocks the downstream bridges * resume_noirq until we are done. */ -static const struct dev_pm_ops nhi_pm_ops = { +const struct dev_pm_ops nhi_pm_ops = { .suspend_noirq = nhi_suspend_noirq, .resume_noirq = nhi_resume_noirq, .freeze_noirq = nhi_freeze_noirq, /* @@ -1518,129 +1275,3 @@ static const struct dev_pm_ops nhi_pm_ops = { .runtime_suspend = nhi_runtime_suspend, .runtime_resume = nhi_runtime_resume, }; - -static struct pci_device_id nhi_ids[] = { - /* - * We have to specify class, the TB bridges use the same device and - * vendor (sub)id on gen 1 and gen 2 controllers. - */ - { - .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_LIGHT_RIDGE, - .subvendor = 0x2222, .subdevice = 0x1111, - }, - { - .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, - .subvendor = 0x2222, .subdevice = 0x1111, - }, - { - .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI, - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - }, - { - .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI, - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - }, - - /* Thunderbolt 3 */ - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - /* Thunderbolt 4 */ - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_M_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI1), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_WCL_NHI0), - .driver_data = (kernel_ulong_t)&icl_nhi_ops }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, - - /* Any USB4 compliant host */ - { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, - - { 0,} -}; - -MODULE_DEVICE_TABLE(pci, nhi_ids); -MODULE_DESCRIPTION("Thunderbolt/USB4 core driver"); -MODULE_LICENSE("GPL"); - -static struct pci_driver nhi_driver = { - .name = "thunderbolt", - .id_table = nhi_ids, - .probe = nhi_probe, - .remove = nhi_remove, - .shutdown = nhi_remove, - .driver.pm = &nhi_pm_ops, -}; - -static int __init nhi_init(void) -{ - int ret; - - ret = tb_domain_init(); - if (ret) - return ret; - ret = pci_register_driver(&nhi_driver); - if (ret) - tb_domain_exit(); - return ret; -} - -static void __exit nhi_unload(void) -{ - pci_unregister_driver(&nhi_driver); - tb_domain_exit(); -} - -rootfs_initcall(nhi_init); -module_exit(nhi_unload); diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 24ac4246d0ca..d488eadadfce 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -29,6 +29,14 @@ enum nhi_mailbox_cmd { int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data); enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); +void nhi_enable_int_throttling(struct tb_nhi *nhi); +void nhi_disable_interrupts(struct tb_nhi *nhi); +void nhi_interrupt_work(struct work_struct *work); +irqreturn_t nhi_msi(int irq, void *data); +irqreturn_t ring_msix(int irq, void *data); +int nhi_probe(struct tb_nhi *nhi); +void nhi_shutdown(struct tb_nhi *nhi); +extern const struct dev_pm_ops nhi_pm_ops; /** * struct tb_nhi_ops - NHI specific optional operations @@ -38,6 +46,12 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); * @runtime_suspend: NHI specific runtime_suspend hook * @runtime_resume: NHI specific runtime_resume hook * @shutdown: NHI specific shutdown + * @pre_nvm_auth: hook to run before Thunderbolt 3 NVM authentication + * @post_nvm_auth: hook to run after Thunderbolt 3 NVM authentication + * @request_ring_irq: NHI specific interrupt retrieval hook + * @release_ring_irq: NHI specific interrupt release hook + * @is_present: Whether the device is currently present on the parent bus + * @init_interrupts: NHI specific interrupt initialization hook */ struct tb_nhi_ops { int (*init)(struct tb_nhi *nhi); @@ -46,10 +60,14 @@ struct tb_nhi_ops { int (*runtime_suspend)(struct tb_nhi *nhi); int (*runtime_resume)(struct tb_nhi *nhi); void (*shutdown)(struct tb_nhi *nhi); + void (*pre_nvm_auth)(struct tb_nhi *nhi); + void (*post_nvm_auth)(struct tb_nhi *nhi); + int (*request_ring_irq)(struct tb_ring *ring, bool no_suspend); + void (*release_ring_irq)(struct tb_ring *ring); + bool (*is_present)(struct tb_nhi *nhi); + int (*init_interrupts)(struct tb_nhi *nhi); }; -extern const struct tb_nhi_ops icl_nhi_ops; - /* * PCI IDs used in this driver from Win Ridge forward. There is no * need for the PCI quirk anymore as we will use ICM also on Apple @@ -100,4 +118,15 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 +/* Host interface quirks */ +#define QUIRK_AUTO_CLEAR_INT BIT(0) +#define QUIRK_E2E BIT(1) + +/* + * Minimal number of vectors when we use MSI-X. Two for control channel + * Rx/Tx and the rest four are for cross domain DMA paths. + */ +#define MSIX_MIN_VECS 6 +#define MSIX_MAX_VECS 16 + #endif diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c deleted file mode 100644 index 8c50066f3411..000000000000 --- a/drivers/thunderbolt/nhi_ops.c +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * NHI specific operations - * - * Copyright (C) 2019, Intel Corporation - * Author: Mika Westerberg - */ - -#include -#include - -#include "nhi.h" -#include "nhi_regs.h" -#include "tb.h" - -/* Ice Lake specific NHI operations */ - -#define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ - -static int check_for_device(struct device *dev, void *data) -{ - return tb_is_switch(dev); -} - -static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) -{ - struct tb *tb = dev_get_drvdata(nhi->dev); - int ret; - - ret = device_for_each_child(&tb->root_switch->dev, NULL, - check_for_device); - return ret > 0; -} - -static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) -{ - struct pci_dev *pdev = to_pci_dev(nhi->dev); - u32 vs_cap; - - /* - * The Thunderbolt host controller is present always in Ice Lake - * but the firmware may not be loaded and running (depending - * whether there is device connected and so on). Each time the - * controller is used we need to "Force Power" it first and wait - * for the firmware to indicate it is up and running. This "Force - * Power" is really not about actually powering on/off the - * controller so it is accessible even if "Force Power" is off. - * - * The actual power management happens inside shared ACPI power - * resources using standard ACPI methods. - */ - pci_read_config_dword(pdev, VS_CAP_22, &vs_cap); - if (power) { - vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; - vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; - vs_cap |= VS_CAP_22_FORCE_POWER; - } else { - vs_cap &= ~VS_CAP_22_FORCE_POWER; - } - pci_write_config_dword(pdev, VS_CAP_22, vs_cap); - - if (power) { - unsigned int retries = 350; - u32 val; - - /* Wait until the firmware tells it is up and running */ - do { - pci_read_config_dword(pdev, VS_CAP_9, &val); - if (val & VS_CAP_9_FW_READY) - return 0; - usleep_range(3000, 3100); - } while (--retries); - - return -ETIMEDOUT; - } - - return 0; -} - -static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) -{ - struct pci_dev *pdev = to_pci_dev(nhi->dev); - u32 data; - - data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; - pci_write_config_dword(pdev, VS_CAP_19, data | VS_CAP_19_VALID); -} - -static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) -{ - struct pci_dev *pdev = to_pci_dev(nhi->dev); - unsigned long end; - u32 data; - - if (!timeout) - goto clear; - - end = jiffies + msecs_to_jiffies(timeout); - do { - pci_read_config_dword(pdev, VS_CAP_18, &data); - if (data & VS_CAP_18_DONE) - goto clear; - usleep_range(1000, 1100); - } while (time_before(jiffies, end)); - - return -ETIMEDOUT; - -clear: - /* Clear the valid bit */ - pci_write_config_dword(pdev, VS_CAP_19, 0); - return 0; -} - -static void icl_nhi_set_ltr(struct tb_nhi *nhi) -{ - struct pci_dev *pdev = to_pci_dev(nhi->dev); - u32 max_ltr, ltr; - - pci_read_config_dword(pdev, VS_CAP_16, &max_ltr); - max_ltr &= 0xffff; - /* Program the same value for both snoop and no-snoop */ - ltr = max_ltr << 16 | max_ltr; - pci_write_config_dword(pdev, VS_CAP_15, ltr); -} - -static int icl_nhi_suspend(struct tb_nhi *nhi) -{ - struct tb *tb = dev_get_drvdata(nhi->dev); - int ret; - - if (icl_nhi_is_device_connected(nhi)) - return 0; - - if (tb_switch_is_icm(tb->root_switch)) { - /* - * If there is no device connected we need to perform - * both: a handshake through LC mailbox and force power - * down before entering D3. - */ - icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); - ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); - if (ret) - return ret; - } - - return icl_nhi_force_power(nhi, false); -} - -static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) -{ - struct tb *tb = dev_get_drvdata(nhi->dev); - enum icl_lc_mailbox_cmd cmd; - - if (!pm_suspend_via_firmware()) - return icl_nhi_suspend(nhi); - - if (!tb_switch_is_icm(tb->root_switch)) - return 0; - - cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; - icl_nhi_lc_mailbox_cmd(nhi, cmd); - return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); -} - -static int icl_nhi_resume(struct tb_nhi *nhi) -{ - int ret; - - ret = icl_nhi_force_power(nhi, true); - if (ret) - return ret; - - icl_nhi_set_ltr(nhi); - return 0; -} - -static void icl_nhi_shutdown(struct tb_nhi *nhi) -{ - icl_nhi_force_power(nhi, false); -} - -const struct tb_nhi_ops icl_nhi_ops = { - .init = icl_nhi_resume, - .suspend_noirq = icl_nhi_suspend_noirq, - .resume_noirq = icl_nhi_resume, - .runtime_suspend = icl_nhi_suspend, - .runtime_resume = icl_nhi_resume, - .shutdown = icl_nhi_shutdown, -}; diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c new file mode 100644 index 000000000000..bbd186c29ef7 --- /dev/null +++ b/drivers/thunderbolt/pci.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Thunderbolt driver - PCI NHI driver + * + * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nhi.h" +#include "nhi_regs.h" +#include "tb.h" + +/** + * struct tb_nhi_pci - NHI device connected over PCIe + * @nhi: NHI device + * @msix_ida: Used to allocate MSI-X vectors for rings + */ +struct tb_nhi_pci { + struct tb_nhi nhi; + struct ida msix_ida; +}; + +static inline struct tb_nhi_pci *nhi_to_pci(struct tb_nhi *nhi) +{ + return container_of(nhi, struct tb_nhi_pci, nhi); +} + +static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) +{ + struct tb_nhi *nhi = &nhi_pci->nhi; + struct pci_dev *pdev = to_pci_dev(nhi->dev); + + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + /* + * Intel hardware supports auto clear of the interrupt + * status register right after interrupt is being + * issued. + */ + nhi->quirks |= QUIRK_AUTO_CLEAR_INT; + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: + /* + * Falcon Ridge controller needs the end-to-end + * flow control workaround to avoid losing Rx + * packets when RING_FLAG_E2E is set. + */ + nhi->quirks |= QUIRK_E2E; + break; + } + } +} + +static int nhi_pci_check_iommu_pdev(struct pci_dev *pdev, void *data) +{ + if (!pdev->external_facing || + !device_iommu_capable(&pdev->dev, IOMMU_CAP_PRE_BOOT_PROTECTION)) + return 0; + *(bool *)data = true; + return 1; /* Stop walking */ +} + +static void nhi_pci_check_iommu(struct tb_nhi_pci *nhi_pci) +{ + struct tb_nhi *nhi = &nhi_pci->nhi; + struct pci_dev *pdev = to_pci_dev(nhi->dev); + struct pci_bus *bus = pdev->bus; + bool port_ok = false; + + /* + * Ideally what we'd do here is grab every PCI device that + * represents a tunnelling adapter for this NHI and check their + * status directly, but unfortunately USB4 seems to make it + * obnoxiously difficult to reliably make any correlation. + * + * So for now we'll have to bodge it... Hoping that the system + * is at least sane enough that an adapter is in the same PCI + * segment as its NHI, if we can find *something* on that segment + * which meets the requirements for Kernel DMA Protection, we'll + * take that to imply that firmware is aware and has (hopefully) + * done the right thing in general. We need to know that the PCI + * layer has seen the ExternalFacingPort property which will then + * inform the IOMMU layer to enforce the complete "untrusted DMA" + * flow, but also that the IOMMU driver itself can be trusted not + * to have been subverted by a pre-boot DMA attack. + */ + while (bus->parent) + bus = bus->parent; + + pci_walk_bus(bus, nhi_pci_check_iommu_pdev, &port_ok); + + nhi->iommu_dma_protection = port_ok; + dev_dbg(nhi->dev, "IOMMU DMA protection is %s\n", + str_enabled_disabled(port_ok)); +} + +static int nhi_pci_init_msi(struct tb_nhi *nhi) +{ + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); + struct device *dev = &pdev->dev; + int res, irq, nvec; + + ida_init(&nhi_pci->msix_ida); + + /* + * The NHI has 16 MSI-X vectors or a single MSI. We first try to + * get all MSI-X vectors and if we succeed, each ring will have + * one MSI-X. If for some reason that does not work out, we + * fallback to a single MSI. + */ + nvec = pci_alloc_irq_vectors(pdev, MSIX_MIN_VECS, MSIX_MAX_VECS, + PCI_IRQ_MSIX); + if (nvec < 0) { + nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (nvec < 0) + return nvec; + + INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work); + + irq = pci_irq_vector(pdev, 0); + if (irq < 0) + return irq; + + res = devm_request_irq(&pdev->dev, irq, nhi_msi, + IRQF_NO_SUSPEND, "thunderbolt", nhi); + if (res) + return dev_err_probe(dev, res, "request_irq failed, aborting\n"); + } + + return 0; +} + +static bool nhi_pci_imr_valid(struct pci_dev *pdev) +{ + u8 val; + + if (!device_property_read_u8(&pdev->dev, "IMR_VALID", &val)) + return !!val; + + return true; +} + +static void nhi_pci_start_dma_port(struct tb_nhi *nhi) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + struct pci_dev *root_port; + + /* + * During host router NVM upgrade we should not allow root port to + * go into D3cold because some root ports cannot trigger PME + * itself. To be on the safe side keep the root port in D0 during + * the whole upgrade process. + */ + root_port = pcie_find_root_port(pdev); + if (root_port) + pm_runtime_get_noresume(&root_port->dev); +} + +static void nhi_pci_complete_dma_port(struct tb_nhi *nhi) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + struct pci_dev *root_port; + + root_port = pcie_find_root_port(pdev); + if (root_port) + pm_runtime_put(&root_port->dev); +} + +static int nhi_pci_ring_request_msix(struct tb_ring *ring, bool no_suspend) +{ + struct tb_nhi *nhi = ring->nhi; + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); + unsigned long irqflags; + int ret; + + if (!pdev->msix_enabled) + return 0; + + ret = ida_alloc_max(&nhi_pci->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL); + if (ret < 0) + return ret; + + ring->vector = ret; + + ret = pci_irq_vector(pdev, ring->vector); + if (ret < 0) + goto err_ida_remove; + + ring->irq = ret; + + irqflags = no_suspend ? IRQF_NO_SUSPEND : 0; + ret = request_irq(ring->irq, ring_msix, irqflags, "thunderbolt", ring); + if (ret) + goto err_ida_remove; + + return 0; + +err_ida_remove: + ida_free(&nhi_pci->msix_ida, ring->vector); + + return ret; +} + +static void nhi_pci_ring_release_msix(struct tb_ring *ring) +{ + struct tb_nhi_pci *nhi_pci = nhi_to_pci(ring->nhi); + + if (ring->irq <= 0) + return; + + free_irq(ring->irq, ring); + ida_free(&nhi_pci->msix_ida, ring->vector); + ring->vector = 0; + ring->irq = 0; +} + +static void nhi_pci_shutdown(struct tb_nhi *nhi) +{ + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); + + /* + * We have to release the irq before calling flush_work. Otherwise an + * already executing IRQ handler could call schedule_work again. + */ + if (!pdev->msix_enabled) { + devm_free_irq(nhi->dev, pdev->irq, nhi); + flush_work(&nhi->interrupt_work); + } + ida_destroy(&nhi_pci->msix_ida); +} + +static bool nhi_pci_is_present(struct tb_nhi *nhi) +{ + return pci_device_is_present(to_pci_dev(nhi->dev)); +} + +static const struct tb_nhi_ops pci_nhi_default_ops = { + .pre_nvm_auth = nhi_pci_start_dma_port, + .post_nvm_auth = nhi_pci_complete_dma_port, + .request_ring_irq = nhi_pci_ring_request_msix, + .release_ring_irq = nhi_pci_ring_release_msix, + .shutdown = nhi_pci_shutdown, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +}; + +/* Ice Lake specific NHI operations */ + +#define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ + +static int check_for_device(struct device *dev, void *data) +{ + return tb_is_switch(dev); +} + +static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) +{ + struct tb *tb = dev_get_drvdata(nhi->dev); + int ret; + + ret = device_for_each_child(&tb->root_switch->dev, NULL, + check_for_device); + return ret > 0; +} + +static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + u32 vs_cap; + + /* + * The Thunderbolt host controller is present always in Ice Lake + * but the firmware may not be loaded and running (depending + * whether there is device connected and so on). Each time the + * controller is used we need to "Force Power" it first and wait + * for the firmware to indicate it is up and running. This "Force + * Power" is really not about actually powering on/off the + * controller so it is accessible even if "Force Power" is off. + * + * The actual power management happens inside shared ACPI power + * resources using standard ACPI methods. + */ + pci_read_config_dword(pdev, VS_CAP_22, &vs_cap); + if (power) { + vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; + vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; + vs_cap |= VS_CAP_22_FORCE_POWER; + } else { + vs_cap &= ~VS_CAP_22_FORCE_POWER; + } + pci_write_config_dword(pdev, VS_CAP_22, vs_cap); + + if (power) { + unsigned int retries = 350; + u32 val; + + /* Wait until the firmware tells it is up and running */ + do { + pci_read_config_dword(pdev, VS_CAP_9, &val); + if (val & VS_CAP_9_FW_READY) + return 0; + usleep_range(3000, 3100); + } while (--retries); + + return -ETIMEDOUT; + } + + return 0; +} + +static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + u32 data; + + data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; + pci_write_config_dword(pdev, VS_CAP_19, data | VS_CAP_19_VALID); +} + +static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + unsigned long end; + u32 data; + + if (!timeout) + goto clear; + + end = jiffies + msecs_to_jiffies(timeout); + do { + pci_read_config_dword(pdev, VS_CAP_18, &data); + if (data & VS_CAP_18_DONE) + goto clear; + usleep_range(1000, 1100); + } while (time_before(jiffies, end)); + + return -ETIMEDOUT; + +clear: + /* Clear the valid bit */ + pci_write_config_dword(pdev, VS_CAP_19, 0); + return 0; +} + +static void icl_nhi_set_ltr(struct tb_nhi *nhi) +{ + struct pci_dev *pdev = to_pci_dev(nhi->dev); + u32 max_ltr, ltr; + + pci_read_config_dword(pdev, VS_CAP_16, &max_ltr); + max_ltr &= 0xffff; + /* Program the same value for both snoop and no-snoop */ + ltr = max_ltr << 16 | max_ltr; + pci_write_config_dword(pdev, VS_CAP_15, ltr); +} + +static int icl_nhi_suspend(struct tb_nhi *nhi) +{ + struct tb *tb = dev_get_drvdata(nhi->dev); + int ret; + + if (icl_nhi_is_device_connected(nhi)) + return 0; + + if (tb_switch_is_icm(tb->root_switch)) { + /* + * If there is no device connected we need to perform + * both: a handshake through LC mailbox and force power + * down before entering D3. + */ + icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); + ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); + if (ret) + return ret; + } + + return icl_nhi_force_power(nhi, false); +} + +static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) +{ + struct tb *tb = dev_get_drvdata(nhi->dev); + enum icl_lc_mailbox_cmd cmd; + + if (!pm_suspend_via_firmware()) + return icl_nhi_suspend(nhi); + + if (!tb_switch_is_icm(tb->root_switch)) + return 0; + + cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; + icl_nhi_lc_mailbox_cmd(nhi, cmd); + return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); +} + +static int icl_nhi_resume(struct tb_nhi *nhi) +{ + int ret; + + ret = icl_nhi_force_power(nhi, true); + if (ret) + return ret; + + icl_nhi_set_ltr(nhi); + return 0; +} + +static void icl_nhi_shutdown(struct tb_nhi *nhi) +{ + nhi_pci_shutdown(nhi); + + icl_nhi_force_power(nhi, false); +} + +static const struct tb_nhi_ops icl_nhi_ops = { + .init = icl_nhi_resume, + .suspend_noirq = icl_nhi_suspend_noirq, + .resume_noirq = icl_nhi_resume, + .runtime_suspend = icl_nhi_suspend, + .runtime_resume = icl_nhi_resume, + .shutdown = icl_nhi_shutdown, + .pre_nvm_auth = nhi_pci_start_dma_port, + .post_nvm_auth = nhi_pci_complete_dma_port, + .request_ring_irq = nhi_pci_ring_request_msix, + .release_ring_irq = nhi_pci_ring_release_msix, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +}; + +static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct tb_nhi_pci *nhi_pci; + struct tb_nhi *nhi; + int res; + + if (!nhi_pci_imr_valid(pdev)) + return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n"); + + res = pcim_enable_device(pdev); + if (res) + return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n"); + + nhi_pci = devm_kzalloc(dev, sizeof(*nhi_pci), GFP_KERNEL); + if (!nhi_pci) + return -ENOMEM; + + nhi = &nhi_pci->nhi; + nhi->dev = dev; + nhi->ops = (const struct tb_nhi_ops *)id->driver_data ?: &pci_nhi_default_ops; + + nhi->iobase = pcim_iomap_region(pdev, 0, "thunderbolt"); + res = PTR_ERR_OR_ZERO(nhi->iobase); + if (res) + return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n"); + + nhi_pci_check_quirks(nhi_pci); + nhi_pci_check_iommu(nhi_pci); + + pci_set_master(pdev); + + return nhi_probe(&nhi_pci->nhi); +} + +static void nhi_pci_remove(struct pci_dev *pdev) +{ + struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + + pm_runtime_get_sync(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + + tb_domain_remove(tb); + wait_for_completion(&nhi->domain_released); + nhi_shutdown(nhi); +} + +static struct pci_device_id nhi_ids[] = { + /* + * We have to specify class, the TB bridges use the same device and + * vendor (sub)id on gen 1 and gen 2 controllers. + */ + { + .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_LIGHT_RIDGE, + .subvendor = 0x2222, .subdevice = 0x1111, + }, + { + .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, + .subvendor = 0x2222, .subdevice = 0x1111, + }, + { + .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI, + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, + }, + { + .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI, + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, + }, + + /* Thunderbolt 3 */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + /* Thunderbolt 4 */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_M_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_WCL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, + + /* Any USB4 compliant host */ + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, + + { 0,} +}; + +MODULE_DEVICE_TABLE(pci, nhi_ids); +MODULE_DESCRIPTION("Thunderbolt/USB4 core driver"); +MODULE_LICENSE("GPL"); + +static struct pci_driver nhi_driver = { + .name = "thunderbolt", + .id_table = nhi_ids, + .probe = nhi_pci_probe, + .remove = nhi_pci_remove, + .shutdown = nhi_pci_remove, + .driver.pm = &nhi_pm_ops, +}; + +static int __init nhi_init(void) +{ + int ret; + + ret = tb_domain_init(); + if (ret) + return ret; + + ret = pci_register_driver(&nhi_driver); + if (ret) + tb_domain_exit(); + + return ret; +} + +static void __exit nhi_unload(void) +{ + pci_unregister_driver(&nhi_driver); + tb_domain_exit(); +} + +rootfs_initcall(nhi_init); +module_exit(nhi_unload); diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 769b57ff276b..a29b9887cd6b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -209,32 +209,6 @@ static int nvm_authenticate_device_dma_port(struct tb_switch *sw) return -ETIMEDOUT; } -static void nvm_authenticate_start_dma_port(struct tb_switch *sw) -{ - struct pci_dev *pdev = to_pci_dev(sw->tb->nhi->dev); - struct pci_dev *root_port; - - /* - * During host router NVM upgrade we should not allow root port to - * go into D3cold because some root ports cannot trigger PME - * itself. To be on the safe side keep the root port in D0 during - * the whole upgrade process. - */ - root_port = pcie_find_root_port(pdev); - if (root_port) - pm_runtime_get_noresume(&root_port->dev); -} - -static void nvm_authenticate_complete_dma_port(struct tb_switch *sw) -{ - struct pci_dev *pdev = to_pci_dev(sw->tb->nhi->dev); - struct pci_dev *root_port; - - root_port = pcie_find_root_port(pdev); - if (root_port) - pm_runtime_put(&root_port->dev); -} - static inline bool nvm_readable(struct tb_switch *sw) { if (tb_switch_is_usb4(sw)) { @@ -260,6 +234,7 @@ static inline bool nvm_upgradeable(struct tb_switch *sw) static int nvm_authenticate(struct tb_switch *sw, bool auth_only) { + struct tb_nhi *nhi = sw->tb->nhi; int ret; if (tb_switch_is_usb4(sw)) { @@ -276,7 +251,8 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only) sw->nvm->authenticating = true; if (!tb_route(sw)) { - nvm_authenticate_start_dma_port(sw); + if (nhi->ops && nhi->ops->pre_nvm_auth) + nhi->ops->pre_nvm_auth(nhi); ret = nvm_authenticate_host_dma_port(sw); } else { ret = nvm_authenticate_device_dma_port(sw); @@ -2745,6 +2721,7 @@ static int tb_switch_set_uuid(struct tb_switch *sw) static int tb_switch_add_dma_port(struct tb_switch *sw) { + struct tb_nhi *nhi = sw->tb->nhi; u32 status; int ret; @@ -2804,8 +2781,10 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) */ nvm_get_auth_status(sw, &status); if (status) { - if (!tb_route(sw)) - nvm_authenticate_complete_dma_port(sw); + if (!tb_route(sw)) { + if (nhi->ops && nhi->ops->post_nvm_auth) + nhi->ops->post_nvm_auth(nhi); + } return 0; } @@ -2819,8 +2798,10 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) return ret; /* Now we can allow root port to suspend again */ - if (!tb_route(sw)) - nvm_authenticate_complete_dma_port(sw); + if (!tb_route(sw)) { + if (nhi->ops && nhi->ops->post_nvm_auth) + nhi->ops->post_nvm_auth(nhi); + } if (status) { tb_sw_info(sw, "switch flash authentication failed\n"); From dd60fb487e55445656284dbc9dc0c865ff9fb34c Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 21 May 2026 12:40:02 +0200 Subject: [PATCH 1427/5214] thunderbolt: Require nhi->ops be valid Because of how fundamental ops->init_interrupts() is, it no longer makes sense to consider cases where nhi->ops is NULL. Drop some boilerplate around it and add a single sanity-check in nhi_probe() instead. Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 32 ++++++++++++++++++-------------- drivers/thunderbolt/switch.c | 6 +++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index be00ffb04766..e9ba8ffbe349 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -568,7 +568,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, if (!ring->descriptors) goto err_free_ring; - if (nhi->ops && nhi->ops->request_ring_irq) { + if (nhi->ops->request_ring_irq) { if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) goto err_free_descs; } @@ -579,7 +579,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, return ring; err_release_msix: - if (nhi->ops && nhi->ops->release_ring_irq) + if (nhi->ops->release_ring_irq) nhi->ops->release_ring_irq(ring); err_free_descs: dma_free_coherent(ring->nhi->dev, @@ -813,7 +813,7 @@ void tb_ring_free(struct tb_ring *ring) } spin_unlock_irq(&ring->nhi->lock); - if (nhi->ops && nhi->ops->release_ring_irq) + if (nhi->ops->release_ring_irq) nhi->ops->release_ring_irq(ring); dma_free_coherent(ring->nhi->dev, @@ -982,7 +982,7 @@ static int __nhi_suspend_noirq(struct device *dev, bool wakeup) if (ret) return ret; - if (nhi->ops && nhi->ops->suspend_noirq) { + if (nhi->ops->suspend_noirq) { ret = nhi->ops->suspend_noirq(tb->nhi, wakeup); if (ret) return ret; @@ -1045,7 +1045,7 @@ static int nhi_resume_noirq(struct device *dev) */ if ((nhi->ops->is_present && !nhi->ops->is_present(nhi))) { nhi->going_away = true; - } else if (nhi->ops && nhi->ops->resume_noirq) { + } else if (nhi->ops->resume_noirq) { ret = nhi->ops->resume_noirq(nhi); if (ret) return ret; @@ -1086,7 +1086,7 @@ static int nhi_runtime_suspend(struct device *dev) if (ret) return ret; - if (nhi->ops && nhi->ops->runtime_suspend) { + if (nhi->ops->runtime_suspend) { ret = nhi->ops->runtime_suspend(tb->nhi); if (ret) return ret; @@ -1100,7 +1100,7 @@ static int nhi_runtime_resume(struct device *dev) struct tb_nhi *nhi = tb->nhi; int ret; - if (nhi->ops && nhi->ops->runtime_resume) { + if (nhi->ops->runtime_resume) { ret = nhi->ops->runtime_resume(nhi); if (ret) return ret; @@ -1125,7 +1125,7 @@ void nhi_shutdown(struct tb_nhi *nhi) } nhi_disable_interrupts(nhi); - if (nhi->ops && nhi->ops->shutdown) + if (nhi->ops->shutdown) nhi->ops->shutdown(nhi); } @@ -1189,6 +1189,12 @@ int nhi_probe(struct tb_nhi *nhi) struct tb *tb; int res; + if (!nhi->ops) + return dev_err_probe(dev, -EINVAL, "NHI ops not set\n"); + + if (!nhi->ops->init_interrupts) + return dev_err_probe(dev, -EINVAL, "missing required NHI ops\n"); + nhi->hop_count = ioread32(nhi->iobase + REG_CAPS) & 0x3ff; dev_dbg(dev, "total paths: %d\n", nhi->hop_count); @@ -1204,11 +1210,9 @@ int nhi_probe(struct tb_nhi *nhi) /* In case someone left them on. */ nhi_disable_interrupts(nhi); - if (nhi->ops && nhi->ops->init_interrupts) { - res = nhi->ops->init_interrupts(nhi); - if (res) - return dev_err_probe(dev, res, "cannot enable interrupts, aborting\n"); - } + res = nhi->ops->init_interrupts(nhi); + if (res) + return dev_err_probe(dev, res, "cannot enable interrupts, aborting\n"); spin_lock_init(&nhi->lock); @@ -1216,7 +1220,7 @@ int nhi_probe(struct tb_nhi *nhi) if (res) return dev_err_probe(dev, res, "failed to set DMA mask\n"); - if (nhi->ops && nhi->ops->init) { + if (nhi->ops->init) { res = nhi->ops->init(nhi); if (res) return res; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index a29b9887cd6b..a830c82bb905 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -251,7 +251,7 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only) sw->nvm->authenticating = true; if (!tb_route(sw)) { - if (nhi->ops && nhi->ops->pre_nvm_auth) + if (nhi->ops->pre_nvm_auth) nhi->ops->pre_nvm_auth(nhi); ret = nvm_authenticate_host_dma_port(sw); } else { @@ -2782,7 +2782,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) nvm_get_auth_status(sw, &status); if (status) { if (!tb_route(sw)) { - if (nhi->ops && nhi->ops->post_nvm_auth) + if (nhi->ops->post_nvm_auth) nhi->ops->post_nvm_auth(nhi); } return 0; @@ -2799,7 +2799,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) /* Now we can allow root port to suspend again */ if (!tb_route(sw)) { - if (nhi->ops && nhi->ops->post_nvm_auth) + if (nhi->ops->post_nvm_auth) nhi->ops->post_nvm_auth(nhi); } From 15bcac35ba045a9a459144901443210d8b1df7a3 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 21 May 2026 12:40:03 +0200 Subject: [PATCH 1428/5214] thunderbolt: Add some more descriptive probe error messages Currently there's a lot of silent error-return paths in various places where nhi_probe() can fail. Sprinkle some prints to make it clearer where the problem is. Signed-off-by: Konrad Dybcio Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 4 ++-- drivers/thunderbolt/tb.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index e9ba8ffbe349..0f795ea58756 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1223,7 +1223,7 @@ int nhi_probe(struct tb_nhi *nhi) if (nhi->ops->init) { res = nhi->ops->init(nhi); if (res) - return res; + return dev_err_probe(dev, res, "NHI specific init failed\n"); } tb = nhi_select_cm(nhi); @@ -1244,7 +1244,7 @@ int nhi_probe(struct tb_nhi *nhi) tb_domain_put(tb); wait_for_completion(&nhi->domain_released); nhi_shutdown(nhi); - return res; + return dev_err_probe(dev, res, "failed to add domain\n"); } dev_set_drvdata(dev, tb); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index d60c0b8eb390..76323255439a 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -3000,7 +3000,8 @@ static int tb_start(struct tb *tb, bool reset) tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); if (IS_ERR(tb->root_switch)) - return PTR_ERR(tb->root_switch); + return dev_err_probe(tb->nhi->dev, PTR_ERR(tb->root_switch), + "failed to allocate host router\n"); /* * ICM firmware upgrade needs running firmware and in native @@ -3017,14 +3018,14 @@ static int tb_start(struct tb *tb, bool reset) ret = tb_switch_configure(tb->root_switch); if (ret) { tb_switch_put(tb->root_switch); - return ret; + return dev_err_probe(tb->nhi->dev, ret, "failed to configure host router\n"); } /* Announce the switch to the world */ ret = tb_switch_add(tb->root_switch); if (ret) { tb_switch_put(tb->root_switch); - return ret; + return dev_err_probe(tb->nhi->dev, ret, "failed to add host router\n"); } /* From 6aa9e61c46465d231e9beddf56af7effd71be682 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 21 May 2026 14:06:27 +0100 Subject: [PATCH 1429/5214] ipmi: Fix user refcount underflow in event delivery ipmi_alloc_recv_msg(user) takes the temporary user reference owned by the receive message, and ipmi_free_recv_msg() drops it again. If event delivery fails after allocating receive messages for earlier users, handle_read_event_rsp() rolls those messages back with ipmi_free_recv_msg(). That rollback path still drops user->refcount explicitly after freeing each message. The extra put can free a user that remains linked on intf->users, so later event delivery may dereference a freed user or trip refcount_t's addition-on-zero warning when ipmi_alloc_recv_msg() tries to acquire another reference. Remove the stale explicit put and the now-dead user assignment. Keep the list_del() and ipmi_free_recv_msg() calls; they are the required rollback operations. Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Matt Fleming Message-ID: <20260521130628.3641050-1-matt@readmodwrite.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7a4566046b68..7ca2cacbaa05 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4472,10 +4472,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, mutex_unlock(&intf->users_mutex); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { - user = recv_msg->user; list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); - kref_put(&user->refcount, free_ipmi_user); } /* * We couldn't allocate memory for the From df64224e712187745b783fcc4d2f29b40cf4b4dc Mon Sep 17 00:00:00 2001 From: Shuping Bu Date: Wed, 20 May 2026 13:50:23 +0800 Subject: [PATCH 1430/5214] usb: dwc3: core: Fix incorrect kernel-doc comment for dwc3_alloc_event_buffers The kernel-doc comment for dwc3_alloc_event_buffers states that the function "Allocates @num event buffers", but the function does not have a @num parameter and only allocates a single event buffer. Remove the misleading "@num" reference from the brief description to accurately reflect the function's behavior. Signed-off-by: Shuping Bu Link: https://patch.msgid.link/20260520055023.2415635-1-bushuping007@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 65213896de99..a2587f9f0bb8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -525,7 +525,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) } /** - * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * dwc3_alloc_event_buffers - Allocate one event buffer of size @length * @dwc: pointer to our controller context structure * @length: size of event buffer * From 5d928bd82cb3c96250cc5b77455d148c317baf7d Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 5 May 2026 18:45:09 +0200 Subject: [PATCH 1431/5214] dt-bindings: usb: dwc3: Add compatible for RK3528 The USB dwc3 core on Rockchip RK3528 is the same as the one already described by the generic snps,dwc3 schema. Add the compatible for the Rockchip RK3528 variant. Signed-off-by: Jonas Karlman Acked-by: Rob Herring (Arm) Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260505164509.3254707-1-heiko@sntech.de Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml index fd1b13c0ed6b..0554dbc4b854 100644 --- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml @@ -26,6 +26,7 @@ select: contains: enum: - rockchip,rk3328-dwc3 + - rockchip,rk3528-dwc3 - rockchip,rk3562-dwc3 - rockchip,rk3568-dwc3 - rockchip,rk3576-dwc3 @@ -38,6 +39,7 @@ properties: items: - enum: - rockchip,rk3328-dwc3 + - rockchip,rk3528-dwc3 - rockchip,rk3562-dwc3 - rockchip,rk3568-dwc3 - rockchip,rk3576-dwc3 @@ -135,6 +137,7 @@ allOf: compatible: contains: enum: + - rockchip,rk3528-dwc3 - rockchip,rk3568-dwc3 - rockchip,rk3576-dwc3 then: From 6dea5fcffbc5a6137932ddc84059a0b868505626 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:14 +0800 Subject: [PATCH 1432/5214] usb: dwc3: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-2-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a2587f9f0bb8..517aa7f1486d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -425,8 +425,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) } reg = dwc3_readl(dwc, DWC3_GUCTL); - reg &= ~DWC3_GUCTL_REFCLKPER_MASK; - reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); + FIELD_MODIFY(DWC3_GUCTL_REFCLKPER_MASK, ®, period); dwc3_writel(dwc, DWC3_GUCTL, reg); if (DWC3_VER_IS_PRIOR(DWC3, 250A)) @@ -456,12 +455,9 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) decr = 480000000 / rate; reg = dwc3_readl(dwc, DWC3_GFLADJ); - reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK - & ~DWC3_GFLADJ_240MHZDECR - & ~DWC3_GFLADJ_240MHZDECR_PLS1; - reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); + FIELD_MODIFY(DWC3_GFLADJ_REFCLK_FLADJ_MASK, ®, fladj); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR, ®, decr >> 1); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR_PLS1, ®, decr & 1); if (dwc->gfladj_refclk_lpm_sel) reg |= DWC3_GFLADJ_REFCLK_LPM_SEL; From dd9bb349a0b4f4a5fb8b03f8b9f1a1e0c2b3b137 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:15 +0800 Subject: [PATCH 1433/5214] usb: dwc3: google: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-3-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-google.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 4ca567ec01d0..60ee4cc99b28 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -104,9 +104,8 @@ static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state) regmap_read(google->usb_cfg_regmap, google->host_cfg_offset + HOST_CFG1_OFFSET, ®); - reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST; - reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) | - HOST_CFG1_PME_EN); + FIELD_MODIFY(HOST_CFG1_PM_POWER_STATE_REQUEST, ®, state); + reg |= HOST_CFG1_PME_EN; regmap_write(google->usb_cfg_regmap, google->host_cfg_offset + HOST_CFG1_OFFSET, reg); From b45172f4f6172b18c0ee598bf171aea47d898c86 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:16 +0800 Subject: [PATCH 1434/5214] usb: dwc3: dwc3-octeon: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-4-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-octeon.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 42bfc14ae0c4..2201f0f34abb 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -296,8 +296,7 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, return div; } val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div); + FIELD_MODIFY(USBDRD_UCTL_CTL_H_CLKDIV_SEL, &val, div); val |= USBDRD_UCTL_CTL_H_CLK_EN; dwc3_octeon_writeq(uctl_ctl_reg, val); val = dwc3_octeon_readq(uctl_ctl_reg); @@ -314,14 +313,11 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, /* Step 5a: Reference clock configuration. */ val = dwc3_octeon_readq(uctl_ctl_reg); val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2; - val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel); + FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_SEL, &val, ref_clk_sel); - val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel); + FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_FSEL, &val, ref_clk_fsel); - val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER; - val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); + FIELD_MODIFY(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, &val, mpll_mul); /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ val |= USBDRD_UCTL_CTL_SSC_EN; From ac66a00ed2059447ffbdc86afd9cbbc6098333a6 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:17 +0800 Subject: [PATCH 1435/5214] usb: xhci: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-5-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index bacd0ddd0d09..3830d4123961 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -226,9 +226,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, USB_SSP_SUBLINK_SPEED_ST_SYM_RX); ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); - attr &= ~USB_SSP_SUBLINK_SPEED_ST; - attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, - USB_SSP_SUBLINK_SPEED_ST_SYM_TX); + FIELD_MODIFY(USB_SSP_SUBLINK_SPEED_ST, &attr, + USB_SSP_SUBLINK_SPEED_ST_SYM_TX); ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); break; case PLT_ASYM_RX: From bcb5da7a9f279ba47b12563f131219deec5055a9 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:18 +0800 Subject: [PATCH 1436/5214] usb: xhci-mtk: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-6-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 06043c7c3100..d9b865546a67 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -185,9 +185,9 @@ static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk) return; value = readl(hcd->regs + HSCH_CFG1); - value &= ~SCH3_RXFIFO_DEPTH_MASK; - value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK, - SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + FIELD_MODIFY(SCH3_RXFIFO_DEPTH_MASK, &value, + SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + writel(value, hcd->regs + HSCH_CFG1); } From dfb128920f7502062896ce4b304e155224cd4e07 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:39:19 +0800 Subject: [PATCH 1437/5214] usb: typec: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430163919.47372-7-18255117159@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 0148b8f50412..24c87dfa6b64 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -141,13 +141,10 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) } if (vconn_pres) { - if (polarity == TYPEC_POLARITY_CC2) { - reg &= ~TCPC_ROLE_CTRL_CC1; - reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN); - } else { - reg &= ~TCPC_ROLE_CTRL_CC2; - reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN); - } + if (polarity == TYPEC_POLARITY_CC2) + FIELD_MODIFY(TCPC_ROLE_CTRL_CC1, ®, TCPC_ROLE_CTRL_CC_OPEN); + else + FIELD_MODIFY(TCPC_ROLE_CTRL_CC2, ®, TCPC_ROLE_CTRL_CC_OPEN); } ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); From 05c5075c3115010f97434c53d353d9a0d4b0e64e Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Wed, 20 May 2026 19:32:50 -0300 Subject: [PATCH 1438/5214] usb: atm: ueagle-atm: use dev_dbg() for 'device found' message Convert dev_info() to dev_dbg(). Per 'Documentation/process/coding-style.rst': 13) Printing kernel messages ... When drivers are working properly they are quiet, so prefer to use dev_dbg/pr_debug unless something is wrong. While in there, correct the verb form and add 'with' for clarity. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051628-squatter-stature-c0e0@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-1-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index f3ae72feb5bf..71559a934133 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2590,7 +2590,7 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) int ret; uea_enters(usb); - uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n", + uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), le16_to_cpu(usb->descriptor.bcdDevice), From 1414b2242c2e9d670c4d8b83480f13db57627ba9 Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Wed, 20 May 2026 19:32:51 -0300 Subject: [PATCH 1439/5214] usb: atm: ueagle-atm: remove function entry/exit debug messages Remove the driver-internal function entry/exit debug messages in favor of existing kernel-level function tracing mechanisms. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051657-scruffy-embark-45ea@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-2-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 56 ++++-------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 71559a934133..ed2611aacb25 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -51,12 +51,6 @@ "[ueagle-atm vdbg] " format, ##args); \ } while (0) -#define uea_enters(usb_dev) \ - uea_vdbg(usb_dev, "entering %s\n" , __func__) - -#define uea_leaves(usb_dev) \ - uea_vdbg(usb_dev, "leaving %s\n" , __func__) - #define uea_err(usb_dev, format, args...) \ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) @@ -606,7 +600,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, u32 crc = 0; int ret, size; - uea_enters(usb); if (!fw_entry) { uea_err(usb, "firmware is not available\n"); goto err; @@ -670,7 +663,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); - uea_leaves(usb); } /* @@ -681,7 +673,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) int ret; char *fw_name = EAGLE_FIRMWARE; - uea_enters(usb); uea_info(usb, "pre-firmware device, uploading firmware\n"); switch (ver) { @@ -710,7 +701,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) else uea_info(usb, "loading firmware %s\n", fw_name); - uea_leaves(usb); return ret; } @@ -1137,7 +1127,6 @@ static int uea_cmv_e1(struct uea_softc *sc, struct cmv_e1 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " "offset : 0x%04x, data : 0x%08x\n", E1_FUNCTION_TYPE(function), @@ -1164,9 +1153,8 @@ static int uea_cmv_e1(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static int uea_cmv_e4(struct uea_softc *sc, @@ -1175,7 +1163,6 @@ static int uea_cmv_e4(struct uea_softc *sc, struct cmv_e4 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); memset(&cmv, 0, sizeof(cmv)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " @@ -1199,9 +1186,8 @@ static int uea_cmv_e4(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static inline int uea_read_cmv_e1(struct uea_softc *sc, @@ -1295,7 +1281,6 @@ static int uea_stat_e1(struct uea_softc *sc) u32 data; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state); @@ -1438,7 +1423,6 @@ static int uea_stat_e4(struct uea_softc *sc) u32 tmp_arr[2]; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; /* XXX only need to be done before operationnal... */ @@ -1805,7 +1789,6 @@ static int uea_start_reset(struct uea_softc *sc) u16 zero = 0; /* ;-) */ int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); /* mask interrupt */ @@ -1873,7 +1856,6 @@ static int uea_start_reset(struct uea_softc *sc) return ret; sc->reset = 0; - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1889,7 +1871,6 @@ static int uea_kthread(void *data) int ret = -EAGAIN; set_freezable(); - uea_enters(INS_TO_USBDEV(sc)); while (!kthread_should_stop()) { if (ret < 0 || sc->reset) ret = uea_start_reset(sc); @@ -1898,7 +1879,7 @@ static int uea_kthread(void *data) if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); } - uea_leaves(INS_TO_USBDEV(sc)); + return ret; } @@ -1911,8 +1892,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) u8 value; char *fw_name = FPGA930_FIRMWARE; - uea_enters(INS_TO_USBDEV(sc)); - ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); if (ret) { uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", @@ -1956,7 +1935,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) err1: release_firmware(fw_entry); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1966,7 +1944,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1; struct cmv_e1 *cmv = &intr->u.e1.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE) goto bad1; @@ -1990,7 +1967,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2004,7 +1980,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) sc->data = sc->data << 16 | sc->data >> 16; wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2012,14 +1987,12 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E1_FUNCTION_TYPE(cmv->bFunction), E1_FUNCTION_SUBTYPE(cmv->bFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; bad1: uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " "wPreamble %d, bDirection %d\n", le16_to_cpu(cmv->wPreamble), cmv->bDirection); - uea_leaves(INS_TO_USBDEV(sc)); } /* The modem send us an ack. First with check if it right */ @@ -2028,7 +2001,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4; struct cmv_e4 *cmv = &intr->u.e4.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n", be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction), be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress), @@ -2040,7 +2012,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2053,7 +2024,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) sc->data = be32_to_cpu(cmv->dwData[0]); sc->data1 = be32_to_cpu(cmv->dwData[1]); wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2061,7 +2031,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E4_FUNCTION_TYPE(cmv->wFunction), E4_FUNCTION_SUBTYPE(cmv->wFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2089,8 +2058,6 @@ static void uea_intr(struct urb *urb) struct intr_pkt *intr = urb->transfer_buffer; int status = urb->status; - uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", status); @@ -2130,8 +2097,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) int ret = -ENOMEM; int size; - uea_enters(INS_TO_USBDEV(sc)); - if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { size = E4_INTR_PKT_SIZE; sc->dispatch_cmv = uea_dispatch_cmv_e4; @@ -2188,7 +2153,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) goto err2; } - uea_leaves(INS_TO_USBDEV(sc)); return 0; err2: @@ -2198,7 +2162,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) sc->urb_int = NULL; kfree(intr); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -2208,7 +2171,7 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) static void uea_stop(struct uea_softc *sc) { int ret; - uea_enters(INS_TO_USBDEV(sc)); + ret = kthread_stop(sc->kthread); uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); @@ -2222,7 +2185,6 @@ static void uea_stop(struct uea_softc *sc) flush_work(&sc->task); release_firmware(sc->dsp_firm); - uea_leaves(INS_TO_USBDEV(sc)); } /* syfs interface */ @@ -2495,8 +2457,6 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; unsigned int alt; - uea_enters(usb); - /* interface 0 is for firmware/monitoring */ if (ifnum != UEA_INTR_IFACE_NO) return -ENODEV; @@ -2589,7 +2549,6 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_device *usb = interface_to_usbdev(intf); int ret; - uea_enters(usb); uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), @@ -2620,7 +2579,6 @@ static void uea_disconnect(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; - uea_enters(usb); /* ADI930 has 2 interfaces and eagle 3 interfaces. * Pre-firmware device has one interface @@ -2631,8 +2589,6 @@ static void uea_disconnect(struct usb_interface *intf) mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); } - - uea_leaves(usb); } /* From 9e896b4a48c4e815956d28961448041c80ad5a19 Mon Sep 17 00:00:00 2001 From: Jihong Min Date: Tue, 19 May 2026 09:07:31 +0900 Subject: [PATCH 1440/5214] usb: xhci-pci: add AMD Promontory 21 PCI glue AMD Promontory 21 (PROM21) xHCI PCI functions use the common xhci-pci core for USB operation, but also expose controller-specific sensor data. Add a small PROM21 PCI glue driver for AMD 1022:43fc and 1022:43fd controllers. The glue delegates USB host operation to the common xhci-pci core and publishes a "hwmon" auxiliary device with parent-provided MMIO data. Auxiliary device creation failure is logged but does not fail the xHCI probe. Make the PROM21 glue a hidden Kconfig tristate driven by the user-visible SENSORS_PROM21_XHCI option. If sensor support is disabled, generic xhci-pci binds PROM21 controllers normally. If sensor support is enabled, the glue follows USB_XHCI_PCI. This keeps the auxiliary device available for a modular sensor driver while avoiding a built-in xhci-pci core handing PROM21 controllers to a glue driver that is only available as a module during initramfs. Assisted-by: Codex:gpt-5.5 Signed-off-by: Jihong Min Reviewed-by: Mario Limonciello (AMD) Tested-by: Yaroslav Isakov Acked-by: Guenter Roeck Link: https://patch.msgid.link/20260519000732.2334711-2-hurryman2212@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 6 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-pci-prom21.c | 137 ++++++++++++++++++ drivers/usb/host/xhci-pci.c | 11 ++ drivers/usb/host/xhci-pci.h | 3 + include/linux/platform_data/usb-xhci-prom21.h | 22 +++ 6 files changed, 180 insertions(+) create mode 100644 drivers/usb/host/xhci-pci-prom21.c create mode 100644 include/linux/platform_data/usb-xhci-prom21.h diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0a277a07cf70..43f30cd867e4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -42,6 +42,12 @@ config USB_XHCI_PCI depends on USB_PCI default y +config USB_XHCI_PCI_PROM21 + tristate + depends on USB_XHCI_PCI + default USB_XHCI_PCI if SENSORS_PROM21_XHCI != n + select AUXILIARY_BUS + config USB_XHCI_PCI_RENESAS tristate "Support for additional Renesas xHCI controller with firmware" depends on USB_XHCI_PCI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index a07e7ba9cd53..174580c1281a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o +obj-$(CONFIG_USB_XHCI_PCI_PROM21) += xhci-pci-prom21.o obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o diff --git a/drivers/usb/host/xhci-pci-prom21.c b/drivers/usb/host/xhci-pci-prom21.c new file mode 100644 index 000000000000..6486f4a09345 --- /dev/null +++ b/drivers/usb/host/xhci-pci-prom21.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Promontory 21 xHCI host controller PCI Bus Glue. + * + * This does not add any PROM21-specific USB or xHCI operation. It exists only + * to publish an auxiliary device for integrated temperature sensor support. + * + * Copyright (C) 2026 Jihong Min + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xhci-pci.h" + +struct prom21_xhci_auxdev { + struct auxiliary_device *auxdev; + struct prom21_xhci_pdata pdata; + int id; +}; + +static DEFINE_IDA(prom21_xhci_auxdev_ida); + +static void prom21_xhci_auxdev_release(struct device *dev, void *res) +{ + struct prom21_xhci_auxdev *prom21_auxdev = res; + + auxiliary_device_destroy(prom21_auxdev->auxdev); + ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id); +} + +static int prom21_xhci_create_auxdev(struct pci_dev *pdev) +{ + struct prom21_xhci_auxdev *prom21_auxdev; + struct usb_hcd *hcd = pci_get_drvdata(pdev); + int ret; + + prom21_auxdev = devres_alloc(prom21_xhci_auxdev_release, + sizeof(*prom21_auxdev), GFP_KERNEL); + if (!prom21_auxdev) + return -ENOMEM; + + prom21_auxdev->pdata.pdev = pdev; + prom21_auxdev->pdata.regs = hcd->regs; + prom21_auxdev->pdata.rsrc_len = hcd->rsrc_len; + + prom21_auxdev->id = ida_alloc(&prom21_xhci_auxdev_ida, GFP_KERNEL); + if (prom21_auxdev->id < 0) { + ret = prom21_auxdev->id; + goto err_free_devres; + } + + prom21_auxdev->auxdev = auxiliary_device_create(&pdev->dev, + KBUILD_MODNAME, "hwmon", + &prom21_auxdev->pdata, + prom21_auxdev->id); + if (!prom21_auxdev->auxdev) { + ret = -ENOMEM; + goto err_free_ida; + } + + devres_add(&pdev->dev, prom21_auxdev); + return 0; + +err_free_ida: + ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id); +err_free_devres: + devres_free(prom21_auxdev); + return ret; +} + +static void prom21_xhci_destroy_auxdev(struct pci_dev *pdev) +{ + devres_release(&pdev->dev, prom21_xhci_auxdev_release, NULL, NULL); +} + +static int prom21_xhci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int retval; + + retval = xhci_pci_common_probe(dev, id); + if (retval) + return retval; + + retval = prom21_xhci_create_auxdev(dev); + if (retval) { + /* + * The auxiliary device only provides optional temperature sensor + * support. Keep the xHCI controller usable if it fails. + */ + dev_err(&dev->dev, + "failed to create PROM21 hwmon auxiliary device: %d\n", + retval); + } + + return 0; +} + +static void prom21_xhci_remove(struct pci_dev *dev) +{ + prom21_xhci_destroy_auxdev(dev); + xhci_pci_remove(dev); +} + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver prom21_xhci_driver = { + .name = "xhci-pci-prom21", + .id_table = pci_ids, + + .probe = prom21_xhci_probe, + .remove = prom21_xhci_remove, + + .shutdown = usb_hcd_pci_shutdown, + .driver = { + .pm = pm_ptr(&usb_hcd_pci_pm_ops), + }, +}; +module_pci_driver(prom21_xhci_driver); + +MODULE_AUTHOR("Jihong Min "); +MODULE_DESCRIPTION("AMD Promontory 21 xHCI PCI Host Controller Driver"); +MODULE_IMPORT_NS("xhci"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 585b2f3117b0..039c26b241d0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -696,12 +696,23 @@ static const struct pci_device_id pci_ids_renesas[] = { { /* end: all zeroes */ } }; +/* handled by xhci-pci-prom21 if enabled */ +static const struct pci_device_id pci_ids_prom21[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) }, + { /* end: all zeroes */ } +}; + static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { if (IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) && pci_match_id(pci_ids_renesas, dev)) return -ENODEV; + if (IS_ENABLED(CONFIG_USB_XHCI_PCI_PROM21) && + pci_match_id(pci_ids_prom21, dev)) + return -ENODEV; + return xhci_pci_common_probe(dev, id); } diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index e87c7d9d76b8..11f435f94322 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -4,6 +4,9 @@ #ifndef XHCI_PCI_H #define XHCI_PCI_H +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC 0x43fc +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD 0x43fd + int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id); void xhci_pci_remove(struct pci_dev *dev); diff --git a/include/linux/platform_data/usb-xhci-prom21.h b/include/linux/platform_data/usb-xhci-prom21.h new file mode 100644 index 000000000000..ee672ad452a8 --- /dev/null +++ b/include/linux/platform_data/usb-xhci-prom21.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Promontory 21 xHCI auxiliary device platform data. + * + * Copyright (C) 2026 Jihong Min + */ + +#ifndef _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H +#define _LINUX_PLATFORM_DATA_USB_XHCI_PROM21_H + +#include +#include + +struct pci_dev; + +struct prom21_xhci_pdata { + struct pci_dev *pdev; + void __iomem *regs; + resource_size_t rsrc_len; +}; + +#endif From 7e916914666667bd0698131fb24342203d1b4ef1 Mon Sep 17 00:00:00 2001 From: Jihong Min Date: Tue, 19 May 2026 09:07:32 +0900 Subject: [PATCH 1441/5214] hwmon: add AMD Promontory 21 xHCI temperature sensor support Add an auxiliary-bus hwmon driver for the temperature sensor exposed by AMD Promontory 21 (PROM21) xHCI PCI functions. The driver binds to the "hwmon" auxiliary device published by the PROM21 xHCI PCI glue and exposes the sensor as temp1_input under the prom21_xhci hwmon device. The sensor is accessed through a PROM21 vendor index/data register pair in the xHCI PCI MMIO BAR. The driver consumes parent-provided MMIO data from the PROM21 PCI glue instead of inspecting the parent PCI driver's drvdata. The read path restores the previous vendor index value after sampling and does not runtime-resume the parent PCI device; reads from a suspended parent return -ENODATA. Document the supported device, register access, runtime PM behavior, and sysfs lookup method. The documentation also records the observation method used to identify the register pair and derive the conversion formula. Assisted-by: Codex:gpt-5.5 Signed-off-by: Jihong Min Reviewed-by: Mario Limonciello (AMD) Tested-by: Yaroslav Isakov Reviewed-by: Guenter Roeck Link: https://patch.msgid.link/20260519000732.2334711-3-hurryman2212@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/prom21-xhci.rst | 101 ++++++++++++ drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/prom21-xhci.c | 239 ++++++++++++++++++++++++++++ 5 files changed, 352 insertions(+) create mode 100644 Documentation/hwmon/prom21-xhci.rst create mode 100644 drivers/hwmon/prom21-xhci.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8b655e5d6b68..324208f1faa2 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -216,6 +216,7 @@ Hardware Monitoring Kernel Drivers pmbus powerz powr1220 + prom21-xhci pt5161l pxe1610 pwm-fan diff --git a/Documentation/hwmon/prom21-xhci.rst b/Documentation/hwmon/prom21-xhci.rst new file mode 100644 index 000000000000..7984fb187bd8 --- /dev/null +++ b/Documentation/hwmon/prom21-xhci.rst @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver prom21-xhci +========================= + +Supported chips: + + * AMD Promontory 21 (PROM21) xHCI USB host controller + + Prefix: 'prom21_xhci' + + PCI IDs: 1022:43fc, 1022:43fd + +Author: + + - Jihong Min + +Description +----------- + +This driver exposes the temperature sensor in AMD PROM21 xHCI controllers. + +The driver binds to an auxiliary device created by the xHCI PCI driver for +supported controllers. The sensor value is accessed through a vendor-specific +index/data register pair in the controller's PCI MMIO BAR. +The auxiliary device is created by the ``xhci-pci-prom21`` PCI glue driver. +USB host operation is otherwise delegated to the common ``xhci-pci`` code. + +PROM21 is an AMD chipset IP used in single-chip or daisy-chained configurations +to build AMD 6xx/8xx series chipsets. Since the xHCI controllers are +integrated in PROM21, this temperature can also be used as a monitor for a +temperature close to the AMD chipset temperature. + +Register access +--------------- + +The temperature value is read through a vendor-specific index/data register +pair in the xHCI PCI MMIO BAR. The driver uses the following byte offsets from +the MMIO BAR base: + +======================= ===================================================== +0x3000 Vendor index register +0x3008 Vendor data register +======================= ===================================================== + +The driver saves the current vendor index register value, writes the +temperature selector ``0x0001e520`` to the vendor index register, reads the +vendor data register, and restores the previous vendor index value before +returning. The raw temperature value is the low 8 bits of the vendor data +register value. + +The hwmon core serializes this driver's callbacks, and the driver restores the +previous index value after each read. This does not provide synchronization +with firmware, SMM, ACPI AML, or any other user outside this driver. + +No public AMD reference is available for the register pair or the raw value. +The register pair was identified on an X870E system with two PROM21 xHCI +controllers. One controller was passed through to a Windows VM, and the same +controller's PCI MMIO BAR was observed from the Linux host while HWiNFO64 was +reporting the PROM21 xHCI temperature. In the test environment, the reported +temperature was very stable at idle and the displayed sensor resolution was +low, which made it possible to look for a consistently repeating MMIO response +for the same reported temperature. During observation, offset 0x3000 repeatedly +contained selector ``0x0001e520``. Writing the same selector to offset 0x3000 +from Linux and then reading offset 0x3008 reproduced the same raw value, so the +offsets are treated as a vendor index/data register pair. + +The conversion formula was empirically inferred by matching observed raw +8-bit values against HWiNFO64's reported PROM21 xHCI temperature for the same +controller. The observed mapping is: + + temp[C] = raw * 0.9066 - 78.624 + +Runtime PM +---------- + +The driver does not wake the xHCI PCI device for hwmon reads. It reads the +temperature only when the parent device is already active. A read from a +suspended device returns ``-ENODATA``. After a successful read, the driver +drops its active-only runtime PM reference and lets the PM core re-evaluate the +idle state. + +Sysfs entries +------------- + +======================= ===================================================== +temp1_input Temperature in millidegrees Celsius +======================= ===================================================== + +The hwmon device name is ``prom21_xhci``. The sysfs path depends on the hwmon +device number assigned by the kernel. Userspace can locate the device by +matching the ``name`` attribute: + +.. code-block:: sh + + for hwmon in /sys/class/hwmon/hwmon*; do + [ "$(cat "$hwmon/name")" = "prom21_xhci" ] || continue + cat "$hwmon/temp1_input" + done + +If the raw register value is invalid, ``temp1_input`` returns ``-ENODATA``. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc..1e770b548810 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -951,6 +951,16 @@ config SENSORS_POWR1220 This driver can also be built as a module. If so, the module will be called powr1220. +config SENSORS_PROM21_XHCI + tristate "AMD Promontory 21 xHCI temperature sensor" + depends on USB_XHCI_PCI + help + If you say yes here you get support for the AMD Promontory 21 + (PROM21) xHCI temperature sensor. + + This driver can also be built as a module. If so, the module + will be called prom21-xhci. + config SENSORS_LAN966X tristate "Microchip LAN966x Hardware Monitoring" depends on SOC_LAN966 || COMPILE_TEST diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9de..f833aed890d8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -196,6 +196,7 @@ obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_POWERZ) += powerz.o obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o +obj-$(CONFIG_SENSORS_PROM21_XHCI) += prom21-xhci.o obj-$(CONFIG_SENSORS_PT5161L) += pt5161l.o obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o diff --git a/drivers/hwmon/prom21-xhci.c b/drivers/hwmon/prom21-xhci.c new file mode 100644 index 000000000000..d40d0c53ce45 --- /dev/null +++ b/drivers/hwmon/prom21-xhci.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Promontory 21 xHCI Hwmon Implementation + * (only temperature monitoring is supported) + * + * This can be effectively used as the alternative chipset temperature monitor. + * + * Copyright (C) 2026 Jihong Min + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROM21_XHCI_INDEX_OFFSET 0x3000 +#define PROM21_XHCI_DATA_OFFSET 0x3008 +#define PROM21_XHCI_TEMP_SELECTOR 0x0001e520 + +struct prom21_xhci { + struct pci_dev *pdev; + struct device *hwmon_dev; + void __iomem *regs; +}; + +static int prom21_xhci_pm_get(struct prom21_xhci *hwmon) +{ + struct device *dev = &hwmon->pdev->dev; + int ret; + + /* + * PROM21 temperature register access does not return a valid value while + * the parent xHCI PCI function is suspended. Do not wake the device from + * a hwmon read. On success, hold a usage reference without changing the + * runtime PM state; if runtime PM is disabled, allow the read unless the + * device is still marked suspended. + */ + ret = pm_runtime_get_if_active(dev); + if (ret > 0) + return 0; + + if (ret == -EINVAL) { + if (pm_runtime_status_suspended(dev)) + return -ENODATA; + + pm_runtime_get_noresume(dev); + return 0; + } + + if (!ret) + return -ENODATA; + + return ret; +} + +/* + * This is not a pure MMIO read. The PROM21 vendor data register is selected + * by temporarily writing PROM21_XHCI_TEMP_SELECTOR to the vendor index + * register. + * The hwmon core already serializes this driver's callbacks, so this driver + * does not need an additional private lock. That does not synchronize with + * firmware, SMM, ACPI, or other possible users. Keep the sequence short and + * restore the previous index before returning. + */ +static int prom21_xhci_read_temp_raw_restore_index(struct prom21_xhci *hwmon, + u8 *raw) +{ + struct device *dev = &hwmon->pdev->dev; + u32 index; + u8 data; + int ret; + + ret = prom21_xhci_pm_get(hwmon); + if (ret) + return ret; + + index = readl(hwmon->regs + PROM21_XHCI_INDEX_OFFSET); + /* Select the PROM21 temperature register through the vendor index. */ + writel(PROM21_XHCI_TEMP_SELECTOR, + hwmon->regs + PROM21_XHCI_INDEX_OFFSET); + /* Use a 32-bit read for PCI MMIO register access. */ + data = readl(hwmon->regs + PROM21_XHCI_DATA_OFFSET) & 0xff; + /* Restore the previous vendor index register value. */ + writel(index, hwmon->regs + PROM21_XHCI_INDEX_OFFSET); + readl(hwmon->regs + PROM21_XHCI_INDEX_OFFSET); + + /* + * Drop the usage reference taken by prom21_xhci_pm_get(). This is + * enough because the read path never resumes the device; use the normal + * put path so the PM core can re-evaluate idle state after the read. + * Otherwise, a racing xHCI autosuspend attempt can see a nonzero + * runtime PM usage count and skip autosuspend, and a later + * pm_runtime_put_noidle(), which does not check for an idle device, + * would leave the device active. + */ + pm_runtime_put(dev); + + if (!data) + return -ENODATA; + + *raw = data; + return 0; +} + +static long prom21_xhci_raw_to_millicelsius(u8 raw) +{ + /* + * No public AMD reference is available for this value. + * The scale was derived from observed PROM21 xHCI temperature readings: + * temp[C] = raw * 0.9066 - 78.624 + */ + return DIV_ROUND_CLOSEST(raw * 9066, 10) - 78624; +} + +static umode_t prom21_xhci_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static int prom21_xhci_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct prom21_xhci *hwmon = dev_get_drvdata(dev); + u8 raw; + int ret; + + if (type != hwmon_temp || attr != hwmon_temp_input) + return -EOPNOTSUPP; + + ret = prom21_xhci_read_temp_raw_restore_index(hwmon, &raw); + if (ret) + return ret; + + *val = prom21_xhci_raw_to_millicelsius(raw); + return 0; +} + +static const struct hwmon_ops prom21_xhci_ops = { + .is_visible = prom21_xhci_is_visible, + .read = prom21_xhci_read, +}; + +static const struct hwmon_channel_info *const prom21_xhci_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL, +}; + +static const struct hwmon_chip_info prom21_xhci_chip_info = { + .ops = &prom21_xhci_ops, + .info = prom21_xhci_info, +}; + +static int prom21_xhci_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &auxdev->dev; + const struct prom21_xhci_pdata *pdata = dev_get_platdata(dev); + struct prom21_xhci *hwmon; + + if (!pdata) + return dev_err_probe(dev, -ENODEV, + "platform data unavailable\n"); + + if (!pdata->regs || + pdata->rsrc_len < PROM21_XHCI_DATA_OFFSET + sizeof(u32)) + return dev_err_probe(dev, -ENODEV, "invalid MMIO resource\n"); + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->pdev = pdata->pdev; + hwmon->regs = pdata->regs; + auxiliary_set_drvdata(auxdev, hwmon); + + /* + * Parent the hwmon device to the PCI function because the temperature + * value is read from that function's MMIO BAR, and systems may contain + * multiple PROM21 xHCI functions. This lets userspace identify the PCI + * endpoint for each reading. The auxiliary driver still owns the hwmon + * lifetime and unregisters it before HCD teardown. + */ + hwmon->hwmon_dev = + hwmon_device_register_with_info(&pdata->pdev->dev, "prom21_xhci", + hwmon, &prom21_xhci_chip_info, + NULL); + if (IS_ERR(hwmon->hwmon_dev)) + return PTR_ERR(hwmon->hwmon_dev); + + return 0; +} + +static void prom21_xhci_remove(struct auxiliary_device *auxdev) +{ + struct prom21_xhci *hwmon = auxiliary_get_drvdata(auxdev); + + /* + * The PROM21 PCI glue destroys the auxiliary device before HCD teardown. + * Unregister the hwmon device here so sysfs removes the attributes, + * stops new reads, and drains active hwmon callbacks before the xHCI + * MMIO mapping is released. + */ + hwmon_device_unregister(hwmon->hwmon_dev); +} + +static const struct auxiliary_device_id prom21_xhci_id_table[] = { + { .name = "xhci_pci_prom21.hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, prom21_xhci_id_table); + +static struct auxiliary_driver prom21_xhci_driver = { + .name = "prom21-xhci", + .probe = prom21_xhci_probe, + .remove = prom21_xhci_remove, + .id_table = prom21_xhci_id_table, +}; +module_auxiliary_driver(prom21_xhci_driver); + +MODULE_AUTHOR("Jihong Min "); +MODULE_DESCRIPTION("AMD Promontory 21 xHCI temperature sensor driver"); +MODULE_LICENSE("GPL"); From cb8b9537901be8c17e8d5b2f09da0e7f8bb4393f Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:06 +0800 Subject: [PATCH 1442/5214] docs/zh_CN: Add index.rst translation Translate .../usb/index.rst into Chinese and update subsystem-apis.rst Update the translation through commit a592a36e4937 ("Documentation: use a source-read extension for the index link boilerplate") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/27f8c02f2d78b37dd5d640fbafaa60d0f0ea9c95.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- .../translations/zh_CN/subsystem-apis.rst | 2 +- .../translations/zh_CN/usb/index.rst | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/index.rst diff --git a/Documentation/translations/zh_CN/subsystem-apis.rst b/Documentation/translations/zh_CN/subsystem-apis.rst index 830217140fb6..b52e1feb0167 100644 --- a/Documentation/translations/zh_CN/subsystem-apis.rst +++ b/Documentation/translations/zh_CN/subsystem-apis.rst @@ -90,6 +90,7 @@ TODOList: security/index PCI/index peci/index + usb/index TODOList: @@ -104,6 +105,5 @@ TODOList: * accel/index * crypto/index * bpf/index -* usb/index * misc-devices/index * wmi/index diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst new file mode 100644 index 000000000000..b4cb0ccaa39b --- /dev/null +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/index.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +======== +USB 支持 +======== + +.. toctree:: + :maxdepth: 1 + + +Todolist: + +* acm +* authorization +* chipidea +* dwc3 +* ehci +* usbmon +* functionfs +* functionfs-desc +* gadget_configfs +* gadget_hid +* gadget_multi +* gadget_printer +* gadget_serial +* gadget_uvc +* gadget-testing +* iuu_phoenix +* mass-storage +* misc_usbsevseg +* mtouchusb +* ohci +* raw-gadget +* usbip_protocol +* usb-serial +* usb-help +* text_files + +.. only:: subproject and html + + 索引 + ==== + + * :ref:`genindex` From 07ee5cf9fe4bf2b0c12a25ad327d997e09fd8c30 Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:07 +0800 Subject: [PATCH 1443/5214] docs/zh_CN: Add acm.rst translation Translate .../usb/acm.rst into Chinese Update the translation through commit ecefae6db042 ("docs: usb: rename files to .rst and add them to drivers-api") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/9f865599e837c90d3048b9a8004efd65b2e3f9d3.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/translations/zh_CN/usb/acm.rst | 147 ++++++++++++++++++ .../translations/zh_CN/usb/index.rst | 2 +- 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/acm.rst diff --git a/Documentation/translations/zh_CN/usb/acm.rst b/Documentation/translations/zh_CN/usb/acm.rst new file mode 100644 index 000000000000..51d6eb8f5660 --- /dev/null +++ b/Documentation/translations/zh_CN/usb/acm.rst @@ -0,0 +1,147 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/acm.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +==================== +Linux ACM 驱动 v0.16 +==================== + +版权所有 (c) 1999 Vojtech Pavlik + +由 SuSE 赞助 + +0. 免责声明 +~~~~~~~~~~~ +本程序是自由软件;你可以在自由软件基金会发布的 +GNU 通用公共许可证第 2 版,或者(按你的选择) +任何后续版本的条款下重新发布和/或修改它。 + +发布本程序是希望它能发挥作用,但它不附带任何担保; +甚至不包括对适销性或特定用途适用性的默示担保。 +详情见 GNU 通用公共许可证。 + +你应该已经随本程序收到了 GNU 通用公共许可证的副本; +如果没有,请致信:Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA。 + +如需联系作者,可发送电子邮件至 vojtech@suse.cz, +或邮寄至: +Vojtech Pavlik, Ucitelska 1576, Prague 8, +182 00, Czech Republic。 + +为方便起见,软件包中已附带 GNU 通用公共许可证 +第 2 版:见 COPYING 文件。 + +1. 使用方法 +~~~~~~~~~~~ +``drivers/usb/class/cdc-acm.c`` 驱动可用于符合 USB +通信设备类抽象控制模型(USB CDC ACM)规范的 +USB 调制解调器和 USB ISDN 终端适配器。 + +许多调制解调器支持此驱动,以下是我所知道的一些型号: + + - 3Com OfficeConnect 56k + - 3Com Voice FaxModem Pro + - 3Com Sportster + - MultiTech MultiModem 56k + - Zoom 2986L FaxModem + - Compaq 56k FaxModem + - ELSA Microlink 56k + +我知道有一款 ISDN 终端适配器可以与 ACM 驱动一起使用: + + - 3Com USR ISDN Pro TA + +一些手机也可以通过 USB 连接。 +我知道以下机型可以正常工作: + + - SonyEricsson K800i + +遗憾的是,许多调制解调器和大多数 ISDN TA +都使用专有接口,因此无法与此驱动配合工作。 +购买前请先确认设备是否符合 ACM 规范。 + +要使用这些调制解调器,需要加载以下模块:: + + usbcore.ko + uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko + cdc-acm.ko + +之后就应该可以访问这些调制解调器了。 +应当可以使用 ``minicom``、``ppp`` 和 ``mgetty`` +与它们通信。 + +2. 验证驱动是否正常工作 +~~~~~~~~~~~~~~~~~~~~~~~ + +第一步是检查 ``/sys/kernel/debug/usb/devices``, +其内容应该类似如下:: + + T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 + B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 + D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + P: Vendor=0000 ProdID=0000 Rev= 0.00 + S: Product=USB UHCI Root Hub + S: SerialNumber=6800 + C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA + I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub + E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms + T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 + D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 + P: Vendor=04c1 ProdID=008f Rev= 2.07 + S: Manufacturer=3Com Inc. + S: Product=3Com U.S. Robotics Pro ISDN TA + S: SerialNumber=UFT53A49BVT7 + C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA + I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm + E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms + C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA + I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm + E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms + I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm + E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + +这三行的存在很关键(以及 ``Cls=`` 字段里出现的 +``comm`` 和 ``data`` 类);它说明这是一个 ACM +设备。``Driver=acm`` 表示该设备正在使用 acm 驱动。 +如果只看到 ``Cls=ff(vend.)``,那就无能为力了: +这说明你手上的设备使用的是厂商专有接口:: + + D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 + I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm + I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm + +在系统日志中应该可以看到:: + + usb.c: USB new device connect, assigned device number 2 + usb.c: kmalloc IF c7691fa0, numif 1 + usb.c: kmalloc IF c7b5f3e0, numif 2 + usb.c: skipped 4 class/vendor specific interface descriptors + usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3 + usb.c: USB device number 2 default language ID 0x409 + Manufacturer: 3Com Inc. + Product: 3Com U.S. Robotics Pro ISDN TA + SerialNumber: UFT53A49BVT7 + acm.c: probing config 1 + acm.c: probing config 2 + ttyACM0: USB ACM device + acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0 + acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7 + usb.c: acm driver claimed interface c7b5f3e0 + usb.c: acm driver claimed interface c7b5f3f8 + usb.c: acm driver claimed interface c7691fa0 + +如果以上都正常,请启动 ``minicom``, +把它配置为连接 ``ttyACM`` 设备,然后 +尝试输入 ``at``。如果返回 ``OK``,说明一切工作正常。 diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index b4cb0ccaa39b..686e5b0a9384 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -17,10 +17,10 @@ USB 支持 .. toctree:: :maxdepth: 1 + acm Todolist: -* acm * authorization * chipidea * dwc3 From 5aded7536f4bbc1e3f33be721755a3427668ac9a Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:08 +0800 Subject: [PATCH 1444/5214] docs/zh_CN: Add authorization.rst translation Translate .../usb/authorization.rst into Chinese Update the translation through commit f176638af476 ("USB: Remove Wireless USB and UWB documentation") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/81ba1ef4d22d6e5104614987b09dfce85af6ed0e.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- .../translations/zh_CN/usb/authorization.rst | 139 ++++++++++++++++++ .../translations/zh_CN/usb/index.rst | 2 +- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/authorization.rst diff --git a/Documentation/translations/zh_CN/usb/authorization.rst b/Documentation/translations/zh_CN/usb/authorization.rst new file mode 100644 index 000000000000..2aa311f6b967 --- /dev/null +++ b/Documentation/translations/zh_CN/usb/authorization.rst @@ -0,0 +1,139 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/authorization.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +============================= +授权或禁止 USB 设备连接到系统 +============================= + +版权 (C) 2007 Inaky Perez-Gonzalez + 英特尔公司 + +此功能允许你控制 USB 设备是否可以在系统中使用。 +借助它,你可以完全通过用户空间实现对 USB 设备的锁定。 + +目前,当插入一个 USB 设备时,系统会对其进行配置, +其接口会立即向用户开放。 +有了这项改动,只有在 root 授权设备完成配置后, +设备才可被使用。 + + +用法 +==== + +授权设备接入:: + + $ echo 1 > /sys/bus/usb/devices/DEVICE/authorized + +取消对设备的授权:: + + $ echo 0 > /sys/bus/usb/devices/DEVICE/authorized + +将新连接到 ``hostX`` 的设备默认设为未授权(即锁定):: + + $ echo 0 > /sys/bus/usb/devices/usbX/authorized_default + +解除锁定:: + + $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default + +默认情况下,所有 USB 设备都是授权的。 +向 ``authorized_default`` 属性写入 ``2`` 会使内核 +默认只授权连接到内部 USB 端口的设备。 + +系统锁定示例(比较粗糙) +------------------------ + +假设你想实现一个锁定功能,只允许类型为 XYZ 的设备接入 +(例如某台带有外露 USB 端口的自助服务终端):: + + 启动系统 + rc.local -> + + for host in /sys/bus/usb/devices/usb* + do + echo 0 > $host/authorized_default + done + +给 udev 挂一个脚本,用于处理新插入的 USB 设备:: + + if device_is_my_type $DEV + then + echo 1 > $device_path/authorized + done + + +``device_is_my_type()`` 才是锁定方案真正见功夫的 +地方。仅仅检查 class、type 和 protocol 是否匹配 +某个值,是你能做出的最糟糕的安全验证之一; +对想绕过它的人来说,这反而是最容易利用的方案。 +如果你需要真正安全的办法,那就该使用加密、 +证书认证之类的机制。把 USB 存储设备当作 +“钥匙”的一个简单例子可以是:: + + function device_is_my_type() + { + echo 1 > authorized # 暂时授权它 + # FIXME: 确保没人能挂载它 + mount DEVICENODE /mntpoint + sum=$(md5sum /mntpoint/.signature) + if [ $sum = $(cat /etc/lockdown/keysum) ] + then + echo "We are good, connected" + umount /mntpoint + # 再做一些额外处理,让其他人也能使用它 + else + echo 0 > authorized + fi + } + + +当然,这个例子很粗糙;真正要做的话, +你会想用基于 PKI 的证书校验,这样就不必依赖 +共享密钥之类的东西。不过你应该已经明白意思了。 +任何拿到设备仿真工具包的人都能伪造描述符和设备信息。 +别信这个。 + +接口授权 +-------- + +也有类似的方法用于允许或拒绝特定 USB 接口。 +这使得你可以只阻止某个 USB 设备中的部分接口。 + +授权接口:: + + $ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized + +取消接口授权:: + + $ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized + +也可以更改特定 USB 总线上新接口的默认授权值。 + +默认允许接口:: + + $ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default + +默认拒绝接口:: + + $ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default + +默认情况下, +``interface_authorized_default`` 位为 ``1``, +因此所有接口默认都处于已授权状态。 + +注意: + 如果把一个先前未授权的接口改为已授权, + 则必须通过将 ``INTERFACE`` 写入 ``/sys/bus/usb/drivers_probe`` + 来手动触发驱动探测。 + +对于需要多个接口的驱动程序,应先授权所有必需接口, +然后再触发驱动探测。这样做可以避免副作用。 diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index 686e5b0a9384..3480966fee19 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -18,10 +18,10 @@ USB 支持 :maxdepth: 1 acm + authorization Todolist: -* authorization * chipidea * dwc3 * ehci From d361fb4bc2c71d239a6fbae108ce8a109630cb1b Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:09 +0800 Subject: [PATCH 1445/5214] docs/zh_CN: Add chipidea.rst translation Translate .../usb/chipidea.rst into Chinese Update the translation through commit e4157519ad46 ("Documentation: usb: correct spelling") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/62f3a25c6695bb3b6ef61d430e5858ba6513939e.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- .../translations/zh_CN/usb/chipidea.rst | 150 ++++++++++++++++++ .../translations/zh_CN/usb/index.rst | 2 +- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/chipidea.rst diff --git a/Documentation/translations/zh_CN/usb/chipidea.rst b/Documentation/translations/zh_CN/usb/chipidea.rst new file mode 100644 index 000000000000..ea0dc3043189 --- /dev/null +++ b/Documentation/translations/zh_CN/usb/chipidea.rst @@ -0,0 +1,150 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/chipidea.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +============================= +ChipIdea 高速双角色控制器驱动 +============================= + +1. 如何测试 OTG FSM(HNP 和 SRP) +--------------------------------- + +下面以两块 Freescale i.MX6Q Sabre SD 开发板为例, +说明如何通过 sysfs 输入文件演示 OTG 的 HNP 和 SRP 功能。 + +1.1 如何使能 OTG FSM +-------------------- + +1.1.1 在 ``menuconfig`` 中选择 ``CONFIG_USB_OTG_FSM``,并重新编译内核 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +重新构建内核镜像和模块。如果想查看 OTG FSM 的 +一些内部变量,可以挂载 ``debugfs``;其中有两个文件 +可以显示 OTG FSM 变量以及部分控制器寄存器的值:: + + cat /sys/kernel/debug/ci_hdrc.0/otg + cat /sys/kernel/debug/ci_hdrc.0/registers + +1.1.2 在控制器节点对应的 ``dts`` 文件中添加以下条目 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + otg-rev = <0x0200>; + adp-disable; + +1.2 测试步骤 +------------ + +1) 给两块 Freescale i.MX6Q Sabre SD 开发板上电, + 并加载 gadget 类驱动(例如 ``g_mass_storage``)。 + +2) 用 USB 线连接两块开发板: + 一端是 micro A 插头,另一端是 micro B 插头。 + + 插入 micro A 插头的一端是 A 设备,它应枚举另一端的 B 设备。 + +3) 角色切换 + + 在 B 设备上执行:: + + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req + + B 设备应接管主机角色并枚举 A 设备。 + +4) A 设备切回主机角色 + + 在 B 设备上执行:: + + echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req + + 或者,通过引入 HNP 轮询,B 端主机可以知道 + A 端外设希望切换为主机角色,因此这次角色切换 + 也可以通过 A 端外设响应 B 端主机的轮询, + 在 A 侧触发。 + 这可以通过在 A 设备上执行下面的命令来完成:: + + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req + + A 设备应切回主机角色并枚举 B 设备。 + +5) 拔掉 B 设备(拔掉 micro B 插头), + 并在 10 秒内重新插入; + A 设备应重新枚举 B 设备。 + +6) 拔掉 B 设备(拔掉 micro B 插头), + 并在 10 秒后重新插入; + A 设备不应重新枚举 B 设备。 + + 如果 A 设备希望使用总线: + + 在 A 设备上执行:: + + echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req + + 如果 B 设备希望使用总线: + + 在 B 设备上执行:: + + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req + +7) A 设备关闭总线供电 + + 在 A 设备上执行:: + + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop + + A 设备应断开与 B 设备的连接,并关闭总线供电。 + +8) B 设备发出 SRP 数据脉冲 + + 在 B 设备上执行:: + + echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req + + A 设备应恢复 USB 总线并枚举 B 设备。 + +1.3 参考文档 +------------ +《On-The-Go and Embedded Host Supplement +to the USB Revision 2.0 Specification +July 27, 2012 Revision 2.0 version 1.1a》 + +2. 如何将 USB 用作系统唤醒源 +---------------------------- +下面是在 i.MX6 平台上把 USB 用作系统唤醒源的示例。 + +2.1 使能核心控制器的唤醒功能:: + + echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup + +2.2 使能 glue 层的唤醒功能:: + + echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup + +2.3 使能 PHY 的唤醒功能(可选):: + + echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup + +2.4 使能根集线器的唤醒功能:: + + echo enabled > /sys/bus/usb/devices/usb1/power/wakeup + +2.5 使能相关设备的唤醒功能:: + + echo enabled > /sys/bus/usb/devices/1-1/power/wakeup + +如果系统只有一个 USB 端口, +而你希望在该端口上启用 USB 唤醒功能, +可以使用下面的脚本:: + + for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done; diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index 3480966fee19..e6d0a4fceff7 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -19,10 +19,10 @@ USB 支持 acm authorization + chipidea Todolist: -* chipidea * dwc3 * ehci * usbmon From c5ae290c534e916252510c0e92b857c3523e7e7c Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:10 +0800 Subject: [PATCH 1446/5214] docs/zh_CN: Add dwc3.rst translation Translate .../usb/dwc3.rst into Chinese Update the translation through commit ecefae6db042 ("docs: usb: rename files to .rst and add them to drivers-api") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/1fd5678e36ebeb4f37e224a59136ef4f4dcc8340.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/translations/zh_CN/usb/dwc3.rst | 63 +++++++++++++++++++ .../translations/zh_CN/usb/index.rst | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/dwc3.rst diff --git a/Documentation/translations/zh_CN/usb/dwc3.rst b/Documentation/translations/zh_CN/usb/dwc3.rst new file mode 100644 index 000000000000..3468ce50c5ba --- /dev/null +++ b/Documentation/translations/zh_CN/usb/dwc3.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/dwc3.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +========= +DWC3 驱动 +========= + + +待办 +~~~~ + +阅读时如果想顺手认领点任务,可以从下面挑一项 :) + +- 将中断处理程序改为每个端点各自使用线程化 IRQ + + 事实证明,有些 DWC3 命令大约需要 ``~1 ms`` 才能完成。 + 当前代码会一直自旋等待命令完成,这种设计并不好。 + + 实现思路: + + - DWC 核心实现了一个按端点对中断进行解复用的 IRQ 控制器。 + 中断号在探测(``probe``)阶段分配,并归属于该设备。 + 如果硬件通过 ``MSI`` 为每个端点提供独立中断, + 那么这个“虚拟”IRQ 控制器就可以被真实的端点中断取代。 + + - 在调用 ``usb_ep_enable()`` 时请求并分配中断资源, + 在调用 ``usb_ep_disable()`` 时释放中断资源。 + 最坏情况下需要 32 个中断,最少是 ``ep0/1`` 的两个中断。 + - ``dwc3_send_gadget_ep_cmd()`` 将在 ``wait_for_completion_timeout()`` + 中休眠,直到命令完成。 + - 中断处理程序分为以下几个部分: + + - 设备级主中断处理程序 + 遍历每个事件,并对其调用 ``generic_handle_irq()``。 + 从 ``generic_handle_irq()`` 返回后,确认事件计数器,使中断最终消失。 + + - 设备级线程化处理程序 + 无。 + + - 端点中断的主处理程序 + 读取事件并尽量处理它。凡是需要睡眠的操作都交给线程处理。 + 事件保存在每个端点的数据结构中。 + 还要注意,一旦把某项工作交给线程处理, + 就不要再在主处理程序里处理它, + 以免出现优先级反转之类的问题。 + + - 端点中断的线程化处理程序 + 处理剩余的端点工作,这些工作可能会睡眠,例如等待命令完成。 + + 延迟: + + 不应增加延迟,因为中断线程具有较高优先级, + 会在普通用户态任务之前运行 + (除非用户更改了调度优先级)。 diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index e6d0a4fceff7..7c739627077b 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -20,10 +20,10 @@ USB 支持 acm authorization chipidea + dwc3 Todolist: -* dwc3 * ehci * usbmon * functionfs From db5f8b5bc629a57fe0115286d2cb58d12717f7a0 Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:11 +0800 Subject: [PATCH 1447/5214] docs/zh_CN: Add ehci.rst translation Translate .../usb/ehci.rst into Chinese Update the translation through commit 570eb861243c ("docs: usb: replace some characters") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/7323e9def418415c3eab67bc539ceec0e986f3cb.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/translations/zh_CN/usb/ehci.rst | 261 ++++++++++++++++++ .../translations/zh_CN/usb/index.rst | 2 +- 2 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/ehci.rst diff --git a/Documentation/translations/zh_CN/usb/ehci.rst b/Documentation/translations/zh_CN/usb/ehci.rst new file mode 100644 index 000000000000..e05e493a30d3 --- /dev/null +++ b/Documentation/translations/zh_CN/usb/ehci.rst @@ -0,0 +1,261 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/ehci.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +========= +EHCI 驱动 +========= + +2002年12月27日 + +EHCI 驱动用于通过支持 USB 2.0 的主机控制器 +硬件与高速 USB 2.0 设备通信。USB 2.0 兼容 +USB 1.1 标准,它定义了三种传输速率: + + - “高速”(High Speed)480 Mbit/sec(60 MByte/sec) + - “全速”(Full Speed)12 Mbit/sec(1.5 MByte/sec) + - “低速”(Low Speed)1.5 Mbit/sec + +USB 1.1 仅支持全速与低速。 +高速设备可以在 USB 1.1 系统上使用, +但速度会降到 USB 1.1 的速率。 + +USB 1.1 设备也可以在 USB 2.0 系统上使用。当它们 +插入 EHCI 控制器时,会被交由 USB 1.1 的伴随 +(companion)控制器处理,该控制器通常是 OHCI 或 UHCI。 + +当 USB 1.1 设备插入 USB 2.0 集线器时,它们通过 +集线器中的事务转换器(Transaction Translator,TT) +与 EHCI 控制器交互,该转换器将低速或全速事务转换为 +高速分割事务,从而避免浪费传输带宽。 + +截至本文撰写时,该驱动已在以下 EHCI 实现上成功运行 +(按字母顺序):Intel、NEC、Philips 和 VIA。 +其他供应商的 EHCI 实现正在陆续问世; +预计该驱动在这些实现上也可正常运行。 + +自 2001 年年中起,usb-storage 设备就已可用 +(在 2.4 版该驱动上速度相当不错), +集线器则直到 2001 年底才开始可用,而其他类型的高速设备 +似乎要等到更多系统内置 USB 2.0 后才会出现。 +这类新系统从 2002 年初开始上市, +并在 2002 年下半年变得更加常见。 + +注意,USB 2.0 支持并不只是 EHCI 本身。 +它还需要对 Linux-USB 核心 API 作出其他修改, +包括 hub 驱动;不过这些修改并不需要真正改变 +暴露给 USB 设备驱动的基本 ``usbcore`` API。 + +- David Brownell + + + +功能 +==== + +该驱动会定期在 x86 硬件上进行测试, +也已在 PPC 硬件上使用,因此大小端问题应当已经解决。 +因此可以认为,它已经处理好了所有必要的 PCI 细节, +所以即便在 DMA 映射有些特殊的系统上, +I/O 也应能正常运行。 + +传输类型 +-------- + +截至本文撰写时,该驱动应当已经能够很好地处理 +所有控制传输、批量传输和中断传输, +包括通过 USB 2.0 集线器中的事务转换器 +与 USB 1.1 设备通信;但仍可能存在 bug。 + +高速等时(ISO)传输支持也已可用,但截至本文撰写时, +还没有 Linux 驱动使用这项支持。 + +目前尚不支持通过事务转换器实现全速等时传输。 +需要注意,ISO 传输的 split transaction 支持 +与高速 ISO 传输几乎无法共用代码, +因为 EHCI 用不同的数据结构表示它们。 +因此,目前大多数 USB 音频和视频设备 +还不能通过高速总线连接使用。 + +驱动行为 +-------- + +所有类型的传输都可以排队。 +这意味着来自一个接口驱动的控制传输 +(或通过 usbfs 发出的控制传输)不会干扰 +另一个驱动的控制传输,而且中断传输可以使用 1 帧的周期, +而不必担心中断处理开销导致的数据丢失。 + + +EHCI 根集线器代码会将 USB 1.1 设备移交给其伴随控制器。 +该驱动不需要了解那些驱动的任何细节; +一个原本就能正常工作的 OHCI 或 UHCI 驱动, +并不会因为 EHCI 驱动也存在而需要更改。 + +电源管理方面还有一些问题; +当前挂起/恢复的行为还不完全正确。 + +此外,在调度周期性事务 +(中断和等时传输)时还采取了一些简化处理。 +这些简化会限制可调度的周期性事务数量, +并且无法使用小于一帧的轮询间隔。 + +使用方式 +======== + +假设有一个 EHCI 控制器(位于 PCI 卡或主板上), +并且已将此驱动编译为模块,可这样加载:: + + # modprobe ehci-hcd + +卸载方式:: + + # rmmod ehci-hcd + +还应加载一个伴随控制器驱动, +例如 ``ohci-hcd`` 或 ``uhci-hcd``。 +如果 EHCI 驱动出现任何问题,只需卸载它的模块, +随后该伴随控制器驱动就会接手 +此前由 EHCI 驱动处理的所有设备 +(但速度会降低)。 + +模块参数(传给 ``modprobe``)包括: + + log2_irq_thresh(默认值 0): + 默认中断延迟的 log2 值,单位是微帧。默认值 0 表示 1 个微帧 + (125 微秒)。最大值 6 表示 2^6 = 64 个微帧。 + 该值控制 EHCI 控制器发出中断的频率。 + +如果在 2.5 内核上使用此驱动,并且启用了 USB 调试支持, +则会在任一 EHCI 控制器的 ``sysfs`` 目录中看到三个文件: + + ``async`` + 转储异步调度,用于控制传输和批量传输。它会显示每个活动的 ``qh`` + 以及待处理的 ``qtd``,通常每个 ``urb`` 对应一个 ``qtd``。 + (可以在 ``usb-storage`` 做磁盘 I/O 时看它;顺便观察请求队列!) + + ``periodic`` + 转储周期性调度,用于中断传输和等时传输。不显示 ``qtd``。 + + ``registers`` + 显示控制器寄存器状态。 + +这些文件的内容有助于定位驱动问题。 + + +设备驱动通常不需要关心自己是否运行在 EHCI 之上, +但它们可能想检查 +``usb_device->speed == USB_SPEED_HIGH``。 +高速设备能做到全速(或低速)设备做不到的事, +例如高带宽的周期性传输(中断或 ISO 传输)。 +另外,设备描述符中的某些值 +(例如周期性传输的轮询间隔) +在高速模式下使用不同的编码方式。 + +不过,一定要让设备驱动经过 USB 2.0 集线器的测试。 +当使用事务转换器时,这些集线器报告某些故障 +(例如断开连接)的方式会不同; +已经见过一些驱动在遇到与 OHCI 或 UHCI +所报告的不同故障时表现不佳。 + +性能 +==== + +USB 2.0 吞吐量主要受两个因素制约: +主机控制器处理请求的速度,以及设备响应这些请求的速度。 +480 Mbit/sec 的“原始传输率”对所有设备都成立, +但总吞吐量还会受到诸如单个高速包之间的延迟、 +驱动是否足够聪明,以及系统整体负载等因素的影响。 +延迟也是性能考量因素。 + +批量传输最常用于关注吞吐量的场景。 +需要记住的是,批量传输总是以 512 字节包为单位, +而一个 USB 2.0 微帧中最多只能容纳 13 个这样的包。 +8 个 USB 2.0 微帧构成一个 USB 1.1 帧; +一个微帧的时长是 1 毫秒 / 8 = 125 微秒。 + +因此,只要硬件和设备驱动软件都允许, +批量传输可提供超过 50 MByte/sec 的带宽。 +周期性传输模式(等时和中断)允许使用更大的包大小, +从而可以逼近所宣称的 480 Mbit/sec 传输率。 + +硬件性能 +-------- + +截至本文撰写时,单个 USB 2.0 设备的最大传输速率 +通常约为 20 MByte/sec。 +这当然会随着时间改变:一些设备现在更快,一些更慢。 + +第一代 NEC EHCI 实现似乎存在 +大约 28 MByte/sec 的硬件瓶颈。 +虽然这对单个 20 MByte/sec 的设备显然已经够用, +但把三个这样的设备挂到同一总线上, +并不能得到 60 MByte/sec。 +问题似乎在于控制器硬件无法并发进行 USB 与 PCI 访问, +因此它每个微帧只会尝试 6 次(也许是 7 次) +USB 事务,而不是 13 次。 +(对一个比其他产品早上市一年的芯片来说, +这是个合理的妥协!) + + +预计较新的实现会在这方面做得更好, +通过投入更多芯片面积来解决这个问题, +使新的主板芯片组更接近 60 MByte/sec 的目标。 +这既包括 NEC 的更新实现,也包括其他厂商的芯片。 + + +主机从 EHCI 控制器收到“请求已完成”中断的最小延迟 +为一个微帧(125 微秒)。该延迟可以调节; +驱动提供了一个模块选项。默认情况下, +``ehci-hcd`` 使用最小延迟,这意味着当发出一个控制 +或批量请求时,通常可以在不到 250 微秒内得知它已完成 +(具体取决于传输大小)。 + +软件性能 +-------- + +即便只是要达到 20 MByte/sec 的传输速率, +Linux-USB 设备驱动也必须让 EHCI 队列始终保持满载。 +这意味着要发出较大的请求, +或者在需要发出一连串小请求时使用批量请求排队。 +如果驱动未做到这一点,那么会直接从性能结果上表现出来。 + + +在典型情况下,使用 ``usb_bulk_msg()`` +以 4 KB 块循环写出, +会浪费超过一半的 USB 2.0 带宽。 +I/O 完成与驱动发出下一次请求之间的延迟, +通常会比一次 I/O 本身耗时更长。 +如果同样的循环改用 16 KB 块,会好一些; +若使用一连串 128 KB 块,则浪费会少得多。 + + +但与其依赖这么大的 I/O 缓冲区来让同步 I/O 高效, +不如直接向主机控制器排入多个(批量)请求, +然后等待它们全部完成(或在出错时取消)。 +这种 URB 排队方式对所有 USB 1.1 +主机控制器驱动也同样适用。 + + +在 Linux 2.5 内核中,定义了新的 ``usb_sg_*()`` API; +它们会把 scatterlist 中的所有缓冲区都排入队列。 +它们还使用 scatterlist 的 DMA 映射 +(其中可能应用 IOMMU)并减少中断次数, +这些都有助于让高速传输尽可能快地运行。 + +待办: + 中断传输和等时(ISO)传输的性能问题。 + 这些周期性传输都是完全调度的,因此,主要问题可能在于如何触发高带宽模式。 + +待办: + 通过 ``sysfs`` 中的 ``uframe_periodic_max`` 参数, + 可以分配超过标准 80% 的周期性带宽。 + 后续将对此进行说明。 diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index 7c739627077b..8c6b26912320 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -21,10 +21,10 @@ USB 支持 authorization chipidea dwc3 + ehci Todolist: -* ehci * usbmon * functionfs * functionfs-desc From a8d61e443fafcd1a7c03cb0cdb6c697be59560a4 Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:12 +0800 Subject: [PATCH 1448/5214] docs/zh_CN: Add usbmon.rst translation Translate .../usb/usbmon.rst into Chinese Update the translation through commit 788183a6e8b0 ("docs: usb: fix literal block marker in usbmon verification example") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/f81ab69649d6cc2b0481db6eb73978e725005f3a.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- .../translations/zh_CN/usb/index.rst | 2 +- .../translations/zh_CN/usb/usbmon.rst | 427 ++++++++++++++++++ 2 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/usb/usbmon.rst diff --git a/Documentation/translations/zh_CN/usb/index.rst b/Documentation/translations/zh_CN/usb/index.rst index 8c6b26912320..eb5aca0c13ec 100644 --- a/Documentation/translations/zh_CN/usb/index.rst +++ b/Documentation/translations/zh_CN/usb/index.rst @@ -22,10 +22,10 @@ USB 支持 chipidea dwc3 ehci + usbmon Todolist: -* usbmon * functionfs * functionfs-desc * gadget_configfs diff --git a/Documentation/translations/zh_CN/usb/usbmon.rst b/Documentation/translations/zh_CN/usb/usbmon.rst new file mode 100644 index 000000000000..11b6d5b59dce --- /dev/null +++ b/Documentation/translations/zh_CN/usb/usbmon.rst @@ -0,0 +1,427 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/usbmon.rst + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +====== +usbmon +====== + +简介 +==== +小写形式的 ``usbmon`` 指的是内核中的一项功能, +用于收集 USB 总线上的 I/O 跟踪信息。它类似于网络监控工具 +``tcpdump(1)`` 或 Ethereal 所使用的数据包套接字。 +类似地,人们希望使用 usbdump 或 USBMon +(首字母大写)之类的工具来检查 +usbmon 生成的原始跟踪数据。 + +usbmon 报告的是各个外设驱动 +向主机控制器驱动(HCD)发出的请求。 +因此,如果 HCD 本身有 bug,那么 usbmon 报告的跟踪信息 +可能无法精确对应实际的总线事务。 +这和 tcpdump 的情况是一样的。 + +目前实现了两种 API: ``text`` 和 ``binary``。 +二进制 API 通过 ``/dev`` 命名空间中的字符设备提供, +并且属于 ABI。文本 API 自内核 2.6.35 起已废弃, +但为了方便仍然可用。 + +如何使用 usbmon 收集原始文本跟踪信息 +==================================== + +与数据包套接字不同,usbmon 提供了一种接口, +可以输出文本格式的跟踪信息。这样做有两个目的: +第一,在更完善的格式最终确定之前, +它作为工具间通用的跟踪交换格式; +第二,在不使用工具的情况下,人们也可以直接阅读这些信息。 + +要收集原始文本跟踪信息,请按以下步骤进行操作。 + +1. 准备 +------- + +挂载 debugfs(内核配置中必须启用它),并加载 usbmon 模块 +(如果它是作为模块构建的)。如果 usbmon 已经编入内核, +那么第二步可以省略。 + +命令示例:: + + # mount -t debugfs none_debugs /sys/kernel/debug + # modprobe usbmon + # + +确认总线套接字是否存在:: + + # ls /sys/kernel/debug/usb/usbmon + 0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u + # + +现在,你可以选择使用 ``0u`` 捕获所有总线上的数据包, +并跳到第 3 步; +也可以先按第 2 步找到目标设备所在的总线。 +这样可以过滤掉那些持续输出数据的烦人设备。 + +2. 查找目标设备连接的是哪条总线 +------------------------------- + +运行 ``cat /sys/kernel/debug/usb/devices``, +找到对应设备的 T 行。通常可以通过厂商字符串来查找。 +如果有许多类似设备,可以拔掉其中一个, +再比较前后两次 ``/sys/kernel/debug/usb/devices`` +的输出。T 行里会包含总线编号。 + +示例:: + + T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 + D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + P: Vendor=0557 ProdID=2004 Rev= 1.00 + S: Manufacturer=ATEN + S: Product=UC100KM V2.00 + +``Bus=03`` 表示它位于 3 号总线上。或者, +也可以查看 ``lsusb`` 的输出,并从对应行得到总线编号。 + +示例如下:: + + Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00 + + +3. 启动 cat 命令 +---------------- + +如果只监听单条总线,可执行:: + + # cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out + +否则,如果要监听所有总线,则执行:: + + # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out + +此进程会一直读取,直到被终止。 +由于输出通常会很长,因此更推荐将输出重定向到某个位置。 + + +4. 在 USB 总线上执行期望的操作 +------------------------------ + +此处需要执行一些会产生 USB 流量的动作, +比如插入 U 盘、拷贝文件、操作摄像头等。 + + +5. 停止 cat +----------- + +这一步通常通过键盘中断(Control-C)完成。 + +此时输出文件(本例中为 ``/tmp/1.mon.out``) +可以保存、通过电子邮件发送,或使用文本编辑器查看。 +如果使用最后一种方式,请确保文件不会大到编辑器无法打开。 + + +原始文本数据格式 +================ + +目前支持两种格式:原始格式,也就是 ``1t`` 格式, +以及 ``1u`` 格式。``1t`` 格式在内核 2.6.21 中已被废弃。 +``1u`` 格式增加了一些字段,例如 ISO 帧描述符、 +``interval`` 等。它生成的行会稍长一些, +但在其他方面是 ``1t`` 格式的完整超集。 + +如果程序需要区分上述两种格式, +可以查看 ``address`` 字段(见下文)。 +如果其中有两个冒号,就是 ``1t`` 格式; +否则是 ``1u`` 格式。 + +任何文本格式的数据由一系列事件组成, +如 URB 提交、URB 回调、提交错误等。 +每个事件对应单独的一行文本, +由使用空白符间隔的若干字段组成。 +字段的数量与位置可能取决于事件类型, +但以下字段对所有类型都通用: + +下面按从左到右的顺序列出这些共有字段: + +- URB Tag。用于标识 URB,通常是 URB 结构体在内核中的地址 + (以十六进制表示), + 但也可能是序号或其他合理的唯一字符串。 + +- 时间戳(微秒),十进制数字。 + 时间戳的精度取决于可用时钟, + 因此可能远差于 + 1 微秒(例如实现使用的是 jiffies)。 + +- 事件类型。它表示的是事件的格式,而不是 URB 的类型。 + 可用值为:``S`` 表示提交,``C`` 表示回调,``E`` 表示提交错误。 + +- ``Address`` 字段(以前称作 ``pipe``)。 + 它包含四个由冒号分隔的字段: + URB 类型及方向、总线号、设备地址和端点号。类型与方向的编码如下: + + == == ========================== + Ci Co 控制输入和输出 + Zi Zo 等时输入和输出 + Ii Io 中断输入和输出 + Bi Bo 批量输入和输出 + == == ========================== + + 总线号、设备地址和端点号使用十进制,但可能有前导零。 + +- URB 状态字段。这个字段要么是一个字母, + 要么是几个由冒号分隔的数字: + URB 状态、``interval``、``start frame`` 和 ``error count``。 + 与 ``address`` 字段不同,除了状态外,其余字段都是可选的。 + ``interval`` 只会为中断和等时 URB 打印;``start frame`` 只会为 + 等时 URB 打印;错误计数只会在等时回调事件中打印。 + + 状态字段是一个十进制数字,有时为负数, + 对应 URB 的 ``status`` 字段。 + 对于提交事件,这个字段本身没有实际意义, + 但为了便于脚本解析,它仍然存在。 + 当发生错误时,该字段包含错误码。 + + 在提交控制包时,这个字段包含的是 ``Setup Tag``, + 而不是一组数字。 + 判断 ``Setup Tag`` 是否存在很容易,因为它从来不是数字。 + 因此,如果脚本在这个字段里发现的是一组数字, + 就会继续读取数据长度(等时 URB 除外)。 + 如果发现的是其他内容,比如一个字母, + 那么脚本会先读取 ``Setup`` 包,再读取数据长度或等时描述符。 + +- ``Setup`` 包由 5 个字段组成: + ``bmRequestType``、``bRequest``、``wValue``、 + ``wIndex`` 和 ``wLength``。这些字段由 USB 2.0 规范定义。 + 如果 ``Setup Tag`` 为 ``s``,就可以安全地解码这些字段。 + 否则,说明 Setup 包虽然存在,但并未被捕获,此时各字段中会填入占位内容。 + +- 等时传输帧描述符的数量及其内容: + 如果一个等时传输事件带有一组描述符,首先打印该 URB 中描述符的总数, + 然后为每个描述符打印一个字段,最多打印 5 个字段。 + 每个字段由三个用冒号分隔的十进制数字组成, + 分别表示状态(status)、偏移(offset)和长度(length)。 + 对于提交(submission),报告的是初始长度; + 对于回调(callback),报告的是实际长度。 + +- 数据长度: + 对于提交,表示请求的长度;对于回调,表示实际传输的长度。 + +- 数据标签: + 即使数据长度非零,usbmon 也不一定会捕获数据。 + 仅当标签为 ``=`` 时,才会有数据字段。 + +- 数据字段: + 以大端十六进制格式显示。注意,这些并不是真正的机器字, + 而只是把字节流拆成若干“字”以便阅读。因此最后一个字可能只包含 + 1 到 4 个字节。 + 收集的数据长度是有限的,可能小于数据长度字段中报告的值。 + 因为数据长度字段只统计实际接收到的字节,而数据字段包含整个传输缓冲区, + 所以,在等时输入(Zi)完成且缓冲区中接收到的数据稀疏的情况下, + 收集的数据长度可能大于数据长度字段的值。 + + + +示例: + +获取端口状态的输入控制传输:: + + d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 < + d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000 + +向地址为 5 的存储设备发送 +31 字节 Bulk 包装的 SCSI 命令 ``0x28`` +(``READ_10``)的输出批量传输:: + + dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000 + dd65f0e8 4128379808 C Bo:1:005:2 0 31 > + +原始二进制格式与 API +==================== +API 的整体架构与前文大体相同,只是事件以二进制格式传递。 +每个事件都通过下面的结构发送 +(这个名字是为了叙述方便而虚构的):: + + + struct usbmon_packet { + u64 id; /* 0: URB ID - 从提交到回调 */ + unsigned char type; /* 8: 与文本相同;可扩展 */ + unsigned char xfer_type; /* ISO (0)、中断、控制、批量 (3) */ + unsigned char epnum; /* 端点号和传输方向 */ + unsigned char devnum; /* 设备地址 */ + u16 busnum; /* 12: 总线号 */ + char flag_setup; /* 14: 与文本相同 */ + char flag_data; /* 15: 与文本相同;二进制零也可 */ + s64 ts_sec; /* 16: gettimeofday */ + s32 ts_usec; /* 24: gettimeofday */ + int status; /* 28: */ + unsigned int length; /* 32: 数据长度(提交或实际) */ + unsigned int len_cap; /* 36: 已捕获的数据长度 */ + union { /* 40: */ + unsigned char setup[SETUP_LEN]; /* 仅用于控制类 S 事件 */ + struct iso_rec { /* 仅用于 ISO */ + int error_count; + int numdesc; + } iso; + } s; + int interval; /* 48: 仅用于中断和 ISO */ + int start_frame; /* 52: 仅用于 ISO */ + unsigned int xfer_flags; /* 56: URB 的 transfer_flags 副本 */ + unsigned int ndesc; /* 60: 实际 ISO 描述符数量 */ + }; /* 64 总长度 */ + +可以用 ``read(2)``、``ioctl(2)``, +或者通过 ``mmap`` 访问缓冲区, +从字符设备接收这些事件。 +不过,出于兼容性原因,``read(2)`` +只返回前 48 个字节。 + +字符设备通常命名为 ``/dev/usbmonN``, +其中 ``N`` 是 USB 总线号。 +编号为零的设备(``/dev/usbmon0``)比较特殊, +表示“所有总线”。 +请注意,具体命名策略由 Linux 发行版决定。 + +如果你手动创建 ``/dev/usbmon0``, +请确保它归 root 所有,并且权限为 ``0600``。 +否则,非特权用户将能够窃听键盘流量。 + +以下 ``MON_IOC_MAGIC`` 为 ``0x92`` 的 ioctl 调用可用: + +``MON_IOCQ_URB_LEN``,定义为 ``_IO(MON_IOC_MAGIC, 1)`` + +该调用返回下一个事件的数据长度。 +注意大多数事件不包含数据, +因此如果该调用返回零,并不意味着没有事件。 + +``MON_IOCG_STATS``,定义为 +``_IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)`` + +参数是指向以下结构的指针:: + + struct mon_bin_stats { + u32 queued; + u32 dropped; + }; + +成员 ``queued`` 表示当前缓冲区中已经排队的事件数量, +而不是自上次重置以来处理过的事件数量。 + +成员 ``dropped`` 表示自上次调用 +``MON_IOCG_STATS`` 以来丢失的事件数量。 + +``MON_IOCT_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 4)`` + +此调用设置缓冲区大小。参数为以字节为单位的缓冲区大小。 +大小可能会向下取整到下一个块(或页)。 +如果请求的大小超出该内核的 [未指定] 范围, +则调用会失败并返回 ``-EINVAL``。 + +``MON_IOCQ_RING_SIZE``,定义为 ``_IO(MON_IOC_MAGIC, 5)`` + +该调用返回缓冲区当前大小(以字节为单位)。 + +``MON_IOCX_GET``,定义为 +``_IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)`` +``MON_IOCX_GETX``,定义为 +``_IOW(MON_IOC_MAGIC, 10, struct mon_get_arg)`` + +如果内核缓冲区中没有事件, +这些调用就会一直等待,直到有事件到达, +然后返回第一个事件。 +参数是指向以下结构的指针:: + + struct mon_get_arg { + struct usbmon_packet *hdr; + void *data; + size_t alloc; /* 数据长度可以为零 */ + }; + + +调用前,应填好 ``hdr``、``data`` 和 ``alloc``。 +调用返回后,``hdr`` 指向的区域中包含下一个事件的结构; +如果存在数据,那么数据缓冲区中也会包含相应数据。 +该事件会从内核缓冲区中移除。 + +``MON_IOCX_GET`` 会将 48 字节的数据复制到 ``hdr`` 区域, +``MON_IOCX_GETX`` 会复制 64 字节。 + +``MON_IOCX_MFETCH``,定义为 +``_IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)`` + +当应用程序通过 ``mmap(2)`` 访问缓冲区时, +主要使用这个 ioctl。 +其参数是指向以下结构的指针:: + + struct mon_mfetch_arg { + uint32_t *offvec; /* 获取的事件偏移向量 */ + uint32_t nfetch; /* 要获取的事件数量(输出:已获取) */ + uint32_t nflush; /* 要刷新的事件数量 */ + }; + + +该 ioctl 的操作分为三个阶段: + +首先,从内核缓冲区移除并丢弃最多 ``nflush`` 个事件。 +实际丢弃的事件数量会写回 ``nflush``。 + +其次,除非伪设备以 ``O_NONBLOCK`` 打开,否则会一直等待, +直到缓冲区中出现事件。 + +第三,将最多 ``nfetch`` 个偏移量提取到 mmap +缓冲区,并存入 ``offvec`` 中。 +实际提取到的事件偏移数量会存回 ``nfetch``。 + +``MON_IOCH_MFLUSH``,定义为 ``_IO(MON_IOC_MAGIC, 8)`` + +此调用从内核缓冲区移除若干事件。 +其参数为要移除的事件数量。 +如果缓冲区中的事件少于请求数量, +则移除所有事件,且不报告错误。 +当没有事件时也可使用。 + +``FIONBIO`` + +如果有需要,将来可能会实现 ``FIONBIO`` ioctl。 + +除了 ``ioctl(2)`` 和 ``read(2)`` 之外, +二进制 API 的特殊文件也可以用 ``select(2)`` 和 +``poll(2)`` 轮询。 +但 ``lseek(2)`` 不起作用。 + +* 二进制 API 的内核缓冲区内存映射访问 + +基本思想很简单: + +准备时,先获取当前大小,再用 ``mmap(2)`` 映射缓冲区。 +然后执行类似下面伪代码的循环:: + + struct mon_mfetch_arg fetch; + struct usbmon_packet *hdr; + int nflush = 0; + for (;;) { + fetch.offvec = vec; // 有 N 个 32 位字 + fetch.nfetch = N; // 或者少于 N + fetch.nflush = nflush; + ioctl(fd, MON_IOCX_MFETCH, &fetch); // 同时处理错误 + nflush = fetch.nfetch; // 完成后要刷新这么多包 + for (i = 0; i < nflush; i++) { + hdr = (struct ubsmon_packet *) &mmap_area[vec[i]]; + if (hdr->type == '@') // 填充包 + continue; + caddr_t data = &mmap_area[vec[i]] + 64; + process_packet(hdr, data); + } + } + + + +因此,主要思想是每 N 个事件只执行一次 ioctl。 + +虽然缓冲区是环形的,但返回的头和数据不会跨越缓冲区末端, +因此上面的伪代码无需任何合并操作。 From 84d2b46ec18ac897cf39ea4b51d085df5e2f4bf4 Mon Sep 17 00:00:00 2001 From: Kefan Bai Date: Thu, 21 May 2026 17:55:13 +0800 Subject: [PATCH 1449/5214] docs/zh_CN: Add CREDITS translation Translate .../usb/CREDITS into Chinese Update the translation through commit 7b2328c5a009 ("docs: Fix typo in usb/CREDITS") Reviewed-by: Yanteng Si Signed-off-by: Kefan Bai Link: https://patch.msgid.link/634084009a0833a70efd269229919476ffe81b22.1779355170.git.baikefan@leap-io-kernel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/translations/zh_CN/usb/CREDITS | 163 +++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 Documentation/translations/zh_CN/usb/CREDITS diff --git a/Documentation/translations/zh_CN/usb/CREDITS b/Documentation/translations/zh_CN/usb/CREDITS new file mode 100644 index 000000000000..c133b1a5daff --- /dev/null +++ b/Documentation/translations/zh_CN/usb/CREDITS @@ -0,0 +1,163 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/usb/CREDITS + +:翻译: + + 白钶凡 Kefan Bai + +:校译: + + +简易 Linux USB 驱动的致谢名单: + +以下人员都为 Linux USB 驱动代码作出了贡献 +(按姓氏字母顺序排列)。我相信这份名单本应 +更长一些,但确实不容易维护。 +如需将自己加入名单,请提交补丁。 + + Georg Acher + David Brownell + Alan Cox + Randy Dunlap + Johannes Erdfelt + Deti Fliegl + ham + Bradley M Keryan + Greg Kroah-Hartman + Pavel Machek + Paul Mackerras + Petko Manlolov + David E. Nelson + Vojtech Pavlik + Bill Ryder + Thomas Sailer + Gregory P. Smith + Linus Torvalds + Roman Weissgaerber + + +特别感谢: + + Inaky Perez Gonzalez + 感谢他发起了 Linux USB 驱动开发工作,并编写了体量较大的 uusbd + 驱动中的大部分代码。我们从那项工作中学到了很多。 + + NetBSD 和 FreeBSD 的 USB 开发者们 + 感谢他们加入 Linux USB 邮件列表,提供建议并分享实现经验。 + +附加感谢: + 还要感谢以下公司与个人在硬件、支持、时间投入和开发方面提供的捐赠与帮助 + (摘自 Inaky 驱动原始的 THANKS 文件): + + 以下公司曾帮助我们开发 Linux USB / UUSBD: + + - 3Com GmbH 捐赠了一台 ISDN Pro TA,并在技术问题和测试设备方面为我 + 提供支持。没想到能得到这么大的帮助。 + + - USAR Systems 向我们提供了他们出色的 USB 评估套件, + 使我们能够测试 Linux USB 驱动对最新 USB 规范的符合性。 + USAR Systems 认识到保持开放操作系统与时俱进的重要性, + 并以硬件支持这个项目。感谢! + + - 感谢英特尔提供的宝贵帮助。 + + - 我们与 Cherry 合作,使 Linux 成为首个内置 USB 支持的操作系统。 + Cherry 是全球最大的键盘制造商之一。 + + - CMD Technology, Inc. 慷慨捐赠了一块 CSA-6700 PCI-to-USB + 控制卡,用于测试 OHCI 实现。 + + - 由于他们对我们的支持,Keytronic 可以放心, + 他们的键盘能卖给至少 300 万 Linux 用户中的一部分。 + + - ing büro h doran [http://www.ibhdoran.com]! + 在欧洲,想给主板买一个 PC 背板 USB 连接器几乎是不可能的 + (我自己做的那个相当糟糕 :))。现在我知道该去哪里买漂亮的 USB + 配件了! + + - Genius Germany 捐赠了一只 USB 鼠标,用于测试鼠标启动协议; + 他们还捐赠了 F-23 数字摇杆和 NetMouse Pro。感谢! + + - AVM GmbH Berlin 支持我们开发 Linux 下的 AVM ISDN Controller B1 USB 驱动。 + AVM 是领先的 ISDN 控制器制造商,其主动式设计对包括 Linux 在内的 + 所有操作系统平台开放。 + + - 非常感谢 Y-E Data, Inc 捐赠的 FlashBuster-U USB 软驱, + 使我们能够测试批量传输代码。 + + - 感谢 Logitech 捐赠了一只三轴 USB 鼠标。 + + Logitech 负责设计、制造并销售各种人机接口设备, + 在键盘、鼠标、轨迹球、摄像头、扬声器,以及面向游戏和专业用途的 + 控制设备方面拥有悠久历史和丰富经验。 + + 作为这些设备广为人知的供应商和销售商,他们捐赠了 USB 鼠标、 + 摇杆和扫描仪,以表明 Linux 的重要性,也让 Logitech 的客户 + 能在自己喜欢的操作系统上获得支持,并让所有 Linux 用户都能使用 + Logitech 以及其他 USB 硬件。 + + Logitech 也是 1999 年 2 月 11 日维也纳 Linux 大会的官方赞助商, + 我们将在会上展示 Linux USB 工作的最新进展。 + + - 感谢 CATC 提供 USB Inspector,帮助我们揭开 UHCI 内部实现中 + 那些不为人知的角落。 + + - 感谢 Entrega 为开发工作提供 PCI 转 USB 卡、集线器和转换器产品。 + + - 感谢 ConnectTech 提供 WhiteHEAT USB 转串口转换器以及相关文档, + 让这个驱动得以写成。 + + - 感谢 ADMtek 提供 Pegasus 和 Pegasus II 评估板、规格说明, + 以及驱动开发过程中的宝贵建议。 + + 另外还要感谢以下个人(嘿,顺序不分先后 :)) + + - Oren Tirosh , + 他非常耐心地听我唠叨各种 USB 疑问,还给了很多很酷的想法。 + + - Jochen Karrer , + 指出了致命 bug,并给出了宝贵建议。 + + - Edmund Humemberger ,他在公共关系与项目管理方面 + 为 Linux-USB 项目付出了巨大的努力。 + + - Alberto Menegazzi 正在着手编写 UUSBD 文档,加油! + + - Ric Klaren 编写了很好的入门文档, + 与 Alberto 的作品形成良性竞争:)。 + + - Christian Groessler ,感谢他在那些棘手细节上的帮助。 + + - Paul MacKerras 改进了 OHCI 实现,推动了对 iMac 的支持, + 并提供了大量的改进意见。 + + - Fernando Herrera + 负责撰写、维护并不断补充那份期待已久、独一无二又精彩的 + UUSBD FAQ!太棒了! + + - Rasca Gmelch 重新启用了 raw 驱动, + 指出了一些错误,并启动了 uusbd-utils 软件包。 + + - Peter Dettori ,像疯了一样挖掘 bug, + 还提出了很多很酷的建议,太棒了! + + - 自由软件与 Linux 社区的所有成员,包括 FSF、GNU 项目、 + MIT X 联盟、TeX 社区等等,谢谢你们! + + - 特别感谢 Richard Stallman 创造了 Emacs! + + - 感谢 linux-usb 邮件列表的所有成员,读了那么多邮件——不开玩笑了, + 感谢你们提出的所有建议! + + - 感谢 USB Implementers Forum 成员们的帮助与支持。 + + - Nathan Myers ,感谢他的建议! + (希望你喜欢 Cibeles 的派对。) + + - 感谢 Linus Torvalds 创建、开发并管理 Linux。 + + - Mike Smith、Craig Keithley、Thierry Giron 和 Janet Schank + 感谢他们让我认识到标准 USB 集线器其实也没那么“标准”, + 这有助于我们在标准集线器驱动中加入厂商特定的特殊处理。 From 52be520ca9efd41d5fb06b172489c13ceb68dbc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 13:01:42 +0200 Subject: [PATCH 1450/5214] usb: typec: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace in the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260518110142.637215-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/anx7411.c | 4 ++-- drivers/usb/typec/mux/fsa4480.c | 2 +- drivers/usb/typec/mux/it5205.c | 2 +- drivers/usb/typec/mux/nb7vpq904m.c | 2 +- drivers/usb/typec/mux/pi3usb30532.c | 2 +- drivers/usb/typec/mux/ptn36502.c | 2 +- drivers/usb/typec/mux/wcd939x-usbss.c | 2 +- drivers/usb/typec/tcpm/fusb302.c | 4 ++-- drivers/usb/typec/tcpm/tcpci.c | 2 +- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 2 +- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 8 ++++---- drivers/usb/typec/tipd/core.c | 2 +- drivers/usb/typec/ucsi/ucsi_ccg.c | 4 ++-- drivers/usb/typec/ucsi/ucsi_stm32g0.c | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 2e8ae1d2faf9..604868ebf422 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1577,8 +1577,8 @@ static void anx7411_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id anx7411_id[] = { - { "anx7411" }, - {} + { .name = "anx7411" }, + { } }; MODULE_DEVICE_TABLE(i2c, anx7411_id); diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index c54e42c7e6a1..bea0c1deec94 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -336,7 +336,7 @@ static void fsa4480_remove(struct i2c_client *client) } static const struct i2c_device_id fsa4480_table[] = { - { "fsa4480" }, + { .name = "fsa4480" }, { } }; MODULE_DEVICE_TABLE(i2c, fsa4480_table); diff --git a/drivers/usb/typec/mux/it5205.c b/drivers/usb/typec/mux/it5205.c index 4357cc67a867..5e1a120b2e3b 100644 --- a/drivers/usb/typec/mux/it5205.c +++ b/drivers/usb/typec/mux/it5205.c @@ -266,7 +266,7 @@ static void it5205_remove(struct i2c_client *client) } static const struct i2c_device_id it5205_table[] = { - { "it5205" }, + { .name = "it5205" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, it5205_table); diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c index b57b6c9c40fe..d1fa26ff442c 100644 --- a/drivers/usb/typec/mux/nb7vpq904m.c +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -499,7 +499,7 @@ static void nb7vpq904m_remove(struct i2c_client *client) } static const struct i2c_device_id nb7vpq904m_table[] = { - { "nb7vpq904m" }, + { .name = "nb7vpq904m" }, { } }; MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table); diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 8eeec135dcdb..985683fe49e9 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -169,7 +169,7 @@ static void pi3usb30532_remove(struct i2c_client *client) } static const struct i2c_device_id pi3usb30532_table[] = { - { "pi3usb30532" }, + { .name = "pi3usb30532" }, { } }; MODULE_DEVICE_TABLE(i2c, pi3usb30532_table); diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c index 129d9d24b932..afd16775dbaf 100644 --- a/drivers/usb/typec/mux/ptn36502.c +++ b/drivers/usb/typec/mux/ptn36502.c @@ -404,7 +404,7 @@ static void ptn36502_remove(struct i2c_client *client) } static const struct i2c_device_id ptn36502_table[] = { - { "ptn36502" }, + { .name = "ptn36502" }, { } }; MODULE_DEVICE_TABLE(i2c, ptn36502_table); diff --git a/drivers/usb/typec/mux/wcd939x-usbss.c b/drivers/usb/typec/mux/wcd939x-usbss.c index d46c353dfaf2..73db3aa3cec4 100644 --- a/drivers/usb/typec/mux/wcd939x-usbss.c +++ b/drivers/usb/typec/mux/wcd939x-usbss.c @@ -753,7 +753,7 @@ static void wcd939x_usbss_remove(struct i2c_client *client) } static const struct i2c_device_id wcd939x_usbss_table[] = { - { "wcd9390-usbss" }, + { .name = "wcd9390-usbss" }, { } }; MODULE_DEVICE_TABLE(i2c, wcd939x_usbss_table); diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 889c4c29c1b8..6560da0c523b 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1841,8 +1841,8 @@ static const struct of_device_id fusb302_dt_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, fusb302_dt_match); static const struct i2c_device_id fusb302_i2c_device_id[] = { - { "typec_fusb302" }, - {} + { .name = "typec_fusb302" }, + { } }; MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id); diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 24c87dfa6b64..7ac7000b2d13 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -1017,7 +1017,7 @@ static int tcpci_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); static const struct i2c_device_id tcpci_id[] = { - { "tcpci" }, + { .name = "tcpci" }, { } }; MODULE_DEVICE_TABLE(i2c, tcpci_id); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index c0ee7e6959ed..6ceb25e5bdd0 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -570,7 +570,7 @@ static int max_tcpci_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(max_tcpci_pm_ops, max_tcpci_suspend, max_tcpci_resume); static const struct i2c_device_id max_tcpci_id[] = { - { "maxtcpc" }, + { .name = "maxtcpc" }, { } }; MODULE_DEVICE_TABLE(i2c, max_tcpci_id); diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 4b3e4e22a82e..a8726da6fc71 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -373,10 +373,10 @@ static const struct rt1711h_chip_info rt1715 = { }; static const struct i2c_device_id rt1711h_id[] = { - { "et7304", (kernel_ulong_t)&rt1715 }, - { "rt1711h", (kernel_ulong_t)&rt1711h }, - { "rt1715", (kernel_ulong_t)&rt1715 }, - {} + { .name = "et7304", .driver_data = (kernel_ulong_t)&rt1715 }, + { .name = "rt1711h", .driver_data = (kernel_ulong_t)&rt1711h }, + { .name = "rt1715", .driver_data = (kernel_ulong_t)&rt1715 }, + { } }; MODULE_DEVICE_TABLE(i2c, rt1711h_id); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 43faec794b95..f560606c588c 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -2027,7 +2027,7 @@ static const struct of_device_id tps6598x_of_match[] = { MODULE_DEVICE_TABLE(of, tps6598x_of_match); static const struct i2c_device_id tps6598x_id[] = { - { "tps6598x", (kernel_ulong_t)&tps6598x_data }, + { .name = "tps6598x", .driver_data = (kernel_ulong_t)&tps6598x_data }, { } }; MODULE_DEVICE_TABLE(i2c, tps6598x_id); diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 199799b319c2..ddde0a7702f0 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1525,8 +1525,8 @@ static const struct of_device_id ucsi_ccg_of_match_table[] = { MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table); static const struct i2c_device_id ucsi_ccg_device_id[] = { - { "ccgx-ucsi" }, - {} + { .name = "ccgx-ucsi" }, + { } }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 838ac0185082..848ed459a6de 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -737,8 +737,8 @@ static const struct of_device_id __maybe_unused ucsi_stm32g0_typec_of_match[] = MODULE_DEVICE_TABLE(of, ucsi_stm32g0_typec_of_match); static const struct i2c_device_id ucsi_stm32g0_typec_i2c_devid[] = { - { "stm32g0-typec" }, - {} + { .name = "stm32g0-typec" }, + { } }; MODULE_DEVICE_TABLE(i2c, ucsi_stm32g0_typec_i2c_devid); From 555f100cb85347dd84ab2d89cc0cdf5f5f70c1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 15:55:36 +0200 Subject: [PATCH 1451/5214] usb: misc: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. While touching all these arrays, unify usage of whitespace in the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Richard Leitner Link: https://patch.msgid.link/20260518135536.781168-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 18 +++++++++--------- drivers/usb/misc/usb3503.c | 2 +- drivers/usb/misc/usb4604.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 7c0778631bea..fb0693742f01 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -746,15 +746,15 @@ static int usb251xb_i2c_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_i2c_pm_ops, usb251xb_i2c_suspend, usb251xb_i2c_resume); static const struct i2c_device_id usb251xb_id[] = { - { "usb2422" }, - { "usb2512b" }, - { "usb2512bi" }, - { "usb2513b" }, - { "usb2513bi" }, - { "usb2514b" }, - { "usb2514bi" }, - { "usb2517" }, - { "usb2517i" }, + { .name = "usb2422" }, + { .name = "usb2512b" }, + { .name = "usb2512bi" }, + { .name = "usb2513b" }, + { .name = "usb2513bi" }, + { .name = "usb2514b" }, + { .name = "usb2514bi" }, + { .name = "usb2517" }, + { .name = "usb2517i" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, usb251xb_id); diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 322e59381b78..759770a13260 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -390,7 +390,7 @@ static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend, usb3503_platform_resume); static const struct i2c_device_id usb3503_id[] = { - { USB3503_I2C_NAME }, + { .name = USB3503_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, usb3503_id); diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c index c9a2fb3518ae..2ae9656715e0 100644 --- a/drivers/usb/misc/usb4604.c +++ b/drivers/usb/misc/usb4604.c @@ -135,7 +135,7 @@ static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend, usb4604_i2c_resume); static const struct i2c_device_id usb4604_id[] = { - { "usb4604" }, + { .name = "usb4604" }, { } }; MODULE_DEVICE_TABLE(i2c, usb4604_id); From 753542d6f8a30fc2edecd2958df8238adfa5cc85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 18:13:00 +0200 Subject: [PATCH 1452/5214] usb: phy: isp1301: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260519161300.1598095-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-isp1301.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 2940f0c84e1b..73cc70e958de 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -25,7 +25,7 @@ struct isp1301 { #define phy_to_isp(p) (container_of((p), struct isp1301, phy)) static const struct i2c_device_id isp1301_id[] = { - { "isp1301" }, + { .name = "isp1301" }, { } }; MODULE_DEVICE_TABLE(i2c, isp1301_id); From 16abda69f44099626a972589f163c108bdb99cfb Mon Sep 17 00:00:00 2001 From: Akash Sukhavasi Date: Wed, 20 May 2026 12:04:51 -0500 Subject: [PATCH 1453/5214] dt-bindings: usb: richtek,rt1711h: remove deprecated .txt file Remove the deprecated .txt binding for richtek,rt1711h. It was superseded by the YAML schema added in commit a72095ed8e65 ("dt-bindings usb: typec: rt1711h: Add binding for Richtek RT1711H"). Signed-off-by: Akash Sukhavasi Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260520170451.2403-1-akash.sukhavasi@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/richtek,rt1711h.txt | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/richtek,rt1711h.txt diff --git a/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt b/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt deleted file mode 100644 index 6f8115db2ea9..000000000000 --- a/Documentation/devicetree/bindings/usb/richtek,rt1711h.txt +++ /dev/null @@ -1,44 +0,0 @@ -Richtek RT1711H TypeC PD Controller. - -Required properties: - - compatible : Must be "richtek,rt1711h". - - reg : Must be 0x4e, it's slave address of RT1711H. - - interrupts : where a is the interrupt number and b represents an - encoding of the sense and level information for the interrupt. - -Required sub-node: -- connector: The "usb-c-connector" attached to the tcpci chip, the bindings - of connector node are specified in - Documentation/devicetree/bindings/connector/usb-connector.yaml - -Example : -rt1711h@4e { - compatible = "richtek,rt1711h"; - reg = <0x4e>; - interrupt-parent = <&gpio26>; - interrupts = <0 IRQ_TYPE_LEVEL_LOW>; - - usb_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - source-pdos = ; - sink-pdos = ; - op-sink-microwatt = <10000000>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - usb_con_ss: endpoint { - remote-endpoint = <&usb3_data_ss>; - }; - }; - }; - }; -}; From 1d2a29ccd3a17d8e3ab8c3a4821b96d19c799b12 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 29 Apr 2026 12:12:23 +0530 Subject: [PATCH 1454/5214] PCI: host-common: Add pci_host_common_d3cold_possible() helper Add a common helper, pci_host_common_d3cold_possible(), to determine whether PCIe devices under host bridge can safely transition to D3cold. This helper is intended to be used by PCI host controller drivers to decide whether they can safely put the endpoint devices into D3cold based on their power state and wakeup capabilities. Once the devices are transitioned into D3cold, the host controller can be safely powered off. The helper walks all devices on the all downstream buses and only allows the devices to enter D3cold if all PCIe endpoints are already in PCI_D3hot. This ensures that the host controller driver does not broadcast PME_Turn_Off or power down the controller while any active endpoint still requires the link to remain powered. For devices that may wake the system, the helper additionally requires that the device supports PME wake from D3cold (via WAKE#). Devices that do not have wakeup enabled are not restricted by this check and do not block the devices under host bridge from entering D3cold. Devices without a bound driver and with PCI not enabled via sysfs are treated as inactive and therefore do not prevent the devices under host bridge from entering D3cold. This allows controllers to power down more aggressively when there are no actively managed endpoints. Some devices (e.g. M.2 without auxiliary power) lose PME detection when main power is removed. Even if such devices advertise PME-from-D3cold capability, entering D3cold may break wakeup. Return PME-from-D3cold capability via 'pme_capable' parameter so PCIe controller drivers can apply platform-specific handling to preserve wakeup functionality. Signed-off-by: Krishna Chaitanya Chundru [mani: commit log and removed the device checks for d3cold] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429-d3cold-v5-1-89e9735b9df6@oss.qualcomm.com --- drivers/pci/controller/pci-host-common.c | 62 ++++++++++++++++++++++++ drivers/pci/controller/pci-host-common.h | 3 ++ 2 files changed, 65 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5..2e785449d02c 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -17,6 +17,9 @@ #include "pci-host-common.h" +#define PCI_HOST_D3COLD_ALLOWED BIT(0) +#define PCI_HOST_PME_D3COLD_CAPABLE BIT(1) + static void gen_pci_unmap_cfg(void *ptr) { pci_ecam_free((struct pci_config_window *)ptr); @@ -106,5 +109,64 @@ void pci_host_common_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(pci_host_common_remove); +static int __pci_host_common_d3cold_possible(struct pci_dev *pdev, + void *userdata) +{ + u32 *flags = userdata; + + if (!pdev->dev.driver && !pci_is_enabled(pdev)) + return 0; + + if (pdev->current_state != PCI_D3hot) + goto exit; + + if (device_may_wakeup(&pdev->dev)) { + if (!pci_pme_capable(pdev, PCI_D3cold)) + goto exit; + else + *flags |= PCI_HOST_PME_D3COLD_CAPABLE; + } + + return 0; + +exit: + *flags &= ~PCI_HOST_D3COLD_ALLOWED; + + return -EOPNOTSUPP; +} + +/** + * pci_host_common_d3cold_possible - Determine whether the host bridge can + * transition the devices into D3cold. + * + * @bridge: PCI host bridge to check + * @pme_capable: Pointer to update if there is any device capable of generating + * PME from D3cold. + * + * Walk downstream PCIe endpoint devices and determine whether the host bridge + * is permitted to transition the devices into D3cold. + * + * Devices under host bridge can enter D3cold only if all active PCIe + * endpoints are in PCI_D3hot and any wakeup-enabled endpoint is capable of + * generating PME from D3cold. Inactive endpoints are ignored. + * + * The @pme_capable output allows PCIe controller drivers to apply + * platform-specific handling to preserve wakeup functionality. + * + * Return: %true if the host bridge may enter D3cold, otherwise %false. + */ +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, + bool *pme_capable) +{ + u32 flags = PCI_HOST_D3COLD_ALLOWED; + + pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags); + + *pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE); + + return !!(flags & PCI_HOST_D3COLD_ALLOWED); +} +EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible); + MODULE_DESCRIPTION("Common library for PCI host controller drivers"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index b5075d4bd7eb..164985878265 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -20,4 +20,7 @@ void pci_host_common_remove(struct platform_device *pdev); struct pci_config_window *pci_host_common_ecam_create(struct device *dev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); + +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, + bool *pme_capable); #endif From 131a93dbcb9546683384e31dc4057d4aaf38fa21 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 29 Apr 2026 12:12:24 +0530 Subject: [PATCH 1455/5214] PCI: qcom: Add .get_ltssm() callback to query LTSSM status For older SoCs like SC7280, reading DBI LTSSM register after sending PME_Turn_Off message causes NOC error. To avoid unsafe DBI accesses, introduce qcom_pcie_get_ltssm() to retrieve the LTSSM state without DBI. For newer platforms, read the LTSSM state from the PARF_LTSSM register; for older platforms continue to retrieve it from ELBI_SYS_STTS. This helper is used in place of direct DBI-based link state checks in the D3cold path after sending PME_Turn_Off message, ensuring the LTSSM state can be queried safely even after DBI access is no longer valid. Signed-off-by: Krishna Chaitanya Chundru [mani: commit log and fixed get_ltssm() check] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429-d3cold-v5-2-89e9735b9df6@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index f21f3806fc1f..881746efe3b6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -71,6 +71,7 @@ /* ELBI registers */ #define ELBI_SYS_CTRL 0x04 +#define ELBI_SYS_STTS 0x08 /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 @@ -131,6 +132,7 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) +#define PARF_LTSSM_STATE_MASK GENMASK(5, 0) /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) @@ -145,6 +147,9 @@ /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) +/* ELBI_SYS_STTS register fields */ +#define ELBI_SYS_STTS_LTSSM_STATE_MASK GENMASK(17, 12) + /* AXI_MSTR_RESP_COMP_CTRL0 register fields */ #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 @@ -245,6 +250,7 @@ struct qcom_pcie_ops { void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); + enum dw_pcie_ltssm (*get_ltssm)(struct qcom_pcie *pcie); }; /** @@ -428,6 +434,15 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) writel(val, pci->elbi_base + ELBI_SYS_CTRL); } +static enum dw_pcie_ltssm qcom_pcie_2_1_0_get_ltssm(struct qcom_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + u32 val; + + val = readl(pci->elbi_base + ELBI_SYS_STTS); + return (enum dw_pcie_ltssm)FIELD_GET(ELBI_SYS_STTS_LTSSM_STATE_MASK, val); +} + static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; @@ -1260,6 +1275,19 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci) return val & PCI_EXP_LNKSTA_DLLLA; } +static enum dw_pcie_ltssm qcom_pcie_get_ltssm(struct dw_pcie *pci) +{ + struct qcom_pcie *pcie = to_qcom_pcie(pci); + u32 val; + + if (pcie->cfg->ops->get_ltssm) + return pcie->cfg->ops->get_ltssm(pcie); + + val = readl(pcie->parf + PARF_LTSSM); + + return (enum dw_pcie_ltssm)FIELD_GET(PARF_LTSSM_STATE_MASK, val); +} + static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie) { struct qcom_pcie_port *port; @@ -1385,6 +1413,7 @@ static const struct qcom_pcie_ops ops_2_1_0 = { .post_init = qcom_pcie_post_init_2_1_0, .deinit = qcom_pcie_deinit_2_1_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */ @@ -1394,6 +1423,7 @@ static const struct qcom_pcie_ops ops_1_0_0 = { .post_init = qcom_pcie_post_init_1_0_0, .deinit = qcom_pcie_deinit_1_0_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */ @@ -1512,6 +1542,7 @@ static const struct qcom_pcie_cfg cfg_fw_managed = { static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, .start_link = qcom_pcie_start_link, + .get_ltssm = qcom_pcie_get_ltssm, }; static int qcom_pcie_icc_init(struct qcom_pcie *pcie) From 8a847d3e9e5f1700beb5a0196e682f71837dfe5c Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 29 Apr 2026 12:12:25 +0530 Subject: [PATCH 1456/5214] PCI: qcom: Power down PHY via PARF_PHY_CTRL before disabling rails/clocks Some Qcom PCIe controller variants bring the PHY out of test power-down (PHY_TEST_PWR_DOWN) during init. When the link is later transitioned to D3cold and the driver disables PCIe clocks and/or regulators without explicitly re-asserting PHY_TEST_PWR_DOWN, the PHY can remain partially powered, leading to avoidable power leakage. Update the init-path comments to reflect that PARF_PHY_CTRL is used to power the PHY on. Also, for controller revisions that enable PHY power in init (2.3.2, 2.3.3, 2.4.0, 2.7.0 and 2.9.0), explicitly power the PHY down via PARF_PHY_CTRL in the deinit path before disabling clocks or regulators. This ensures the PHY is put into a defined low-power state prior to removing its supplies, preventing leakage when entering D3cold. Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429-d3cold-v5-3-89e9735b9df6@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 38 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 881746efe3b6..179d26038da8 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -532,7 +532,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) u32 val; int ret; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -699,6 +699,12 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + u32 val; + + /* Force PHY to lowest power state*/ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -731,7 +737,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) { u32 val; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -795,6 +801,12 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + u32 val; + + /* Force PHY to lowest power state*/ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); reset_control_bulk_assert(res->num_resets, res->resets); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -863,6 +875,12 @@ static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); } @@ -918,6 +936,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1013,7 +1032,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) /* configure PCIe to RC mode */ writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE); - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1084,6 +1103,12 @@ static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -1188,6 +1213,12 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); } @@ -1228,6 +1259,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) u32 val; int i; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); From 56378c03c1a80aeeab45f39b303cc92a3bb7716e Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 29 Apr 2026 12:12:26 +0530 Subject: [PATCH 1457/5214] PCI: dwc: Use common D3cold eligibility helper in suspend path Previously, the driver skipped putting the link into L2 and device state in D3cold whenever L1 ASPM was enabled, since some devices (e.g. NVMe) expect low resume latency and may not tolerate deeper power states. However, such devices typically remain in D0 and are already covered by the new helper's requirement that all endpoints be in D3hot before the devices under host bridge may enter D3cold. Replace the local L1/L1SS-based check in dw_pcie_suspend_noirq() with the shared pci_host_common_d3cold_possible() helper to decide whether the devices under host bridge can safely transition to D3cold. In addition, propagate PME-from-D3cold capability information from the helper and record it in skip_pwrctrl_off. Some devices (e.g. M.2 cards without auxiliary power) cannot send PME when the main power is removed, even if they advertise PME-from-D3cold support. This allows controller power-off to be skipped when required to preserve wakeup functionality. While at it, update the 'dw_pcie::suspended' flag in dw_pcie_resume_noirq() only after the PCIe link resumes successfully, to avoid marking the controller as active when link resume fails. Signed-off-by: Krishna Chaitanya Chundru [mani: commit log and added TODO to query Vaux] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429-d3cold-v5-4-89e9735b9df6@oss.qualcomm.com --- .../pci/controller/dwc/pcie-designware-host.c | 23 ++++++++++++------- drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c9517a348836..cffb34f6f3a9 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include +#include "../pci-host-common.h" #include "../../pci.h" #include "pcie-designware.h" @@ -1218,18 +1220,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) int dw_pcie_suspend_noirq(struct dw_pcie *pci) { - u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + bool pme_capable = false; int ret = 0; u32 val; if (!dw_pcie_link_up(pci)) goto stop_link; - /* - * If L1SS is supported, then do not put the link into L2 as some - * devices such as NVMe expect low resume latency. - */ - if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1) + if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable)) return 0; if (pci->pp.ops->pme_turn_off) { @@ -1273,6 +1271,15 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) udelay(1); stop_link: + /* + * TODO: "pme_capable" means some downstream device is wakeup- + * enabled and is capable of generating PME from D3cold, which + * requires auxiliary power. Instead of always skipping power off + * if PME is supported from D3cold, query the pwrctrl core and skip + * power off only if device supports PME from D3cold and Vaux is + * not supported. + */ + pci->pp.skip_pwrctrl_off = pme_capable; dw_pcie_stop_link(pci); if (pci->pp.ops->deinit) pci->pp.ops->deinit(&pci->pp); @@ -1290,8 +1297,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (!pci->suspended) return 0; - pci->suspended = false; - if (pci->pp.ops->init) { ret = pci->pp.ops->init(&pci->pp); if (ret) { @@ -1313,6 +1318,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (pci->pp.ops->post_init) pci->pp.ops->post_init(&pci->pp); + pci->suspended = false; + return 0; err_stop_link: diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..e759c5c7257e 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -450,6 +450,7 @@ struct dw_pcie_rp { bool ecam_enabled; bool native_ecam; bool skip_l23_ready; + bool skip_pwrctrl_off; }; struct dw_pcie_ep_ops { From 2cc0e7454c7891345f92e96b2f812b808be7fbdb Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Wed, 29 Apr 2026 12:12:27 +0530 Subject: [PATCH 1458/5214] PCI: qcom: Add D3cold support Add support for transitioning PCIe endpoints under host bridge into D3cold by integrating with the DWC core suspend/resume helpers. Implement PME_Turn_Off message generation via ELBI_SYS_CTRL and hook it into the DWC host operations so the controller follows the standard PME_Turn_Off based power-down sequence before entering D3cold. When the device is suspended into D3cold, fully tear down interconnect bandwidth and OPP votes. If D3cold is not entered, retain existing behavior by keeping the required interconnect and OPP votes. Use dw_pcie::skip_pwrctrl_off to avoid powering off devices during suspend to preserve wakeup capability of the devices and also not to power on the devices in the init path. Finally, drop the qcom_pcie::suspended flag and rely on the existing dw_pcie::suspended state, which now drives both the power-management flow and the interconnect/OPP handling. Signed-off-by: Krishna Chaitanya Chundru [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429-d3cold-v5-5-89e9735b9df6@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 163 ++++++++++++++++--------- 1 file changed, 103 insertions(+), 60 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 179d26038da8..bc5a31efc0d0 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -146,6 +146,7 @@ /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) +#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4) /* ELBI_SYS_STTS register fields */ #define ELBI_SYS_STTS_LTSSM_STATE_MASK GENMASK(17, 12) @@ -288,7 +289,6 @@ struct qcom_pcie { const struct qcom_pcie_cfg *cfg; struct dentry *debugfs; struct list_head ports; - bool suspended; bool use_pm_opp; }; @@ -1364,13 +1364,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_deinit; - ret = pci_pwrctrl_create_devices(pci->dev); - if (ret) - goto err_disable_phy; + if (!pci->suspended) { + ret = pci_pwrctrl_create_devices(pci->dev); + if (ret) + goto err_disable_phy; + } - ret = pci_pwrctrl_power_on_devices(pci->dev); - if (ret) - goto err_pwrctrl_destroy; + if (!pp->skip_pwrctrl_off) { + ret = pci_pwrctrl_power_on_devices(pci->dev); + if (ret) + goto err_pwrctrl_destroy; + } if (pcie->cfg->ops->post_init) { ret = pcie->cfg->ops->post_init(pcie); @@ -1395,9 +1399,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) err_assert_reset: qcom_pcie_perst_assert(pcie); err_pwrctrl_power_off: - pci_pwrctrl_power_off_devices(pci->dev); + if (!pp->skip_pwrctrl_off) + pci_pwrctrl_power_off_devices(pci->dev); err_pwrctrl_destroy: - if (ret != -EPROBE_DEFER) + if (ret != -EPROBE_DEFER && !pci->suspended) pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); @@ -1414,11 +1419,14 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) qcom_pcie_perst_assert(pcie); - /* - * No need to destroy pwrctrl devices as this function only gets called - * during system suspend as of now. - */ - pci_pwrctrl_power_off_devices(pci->dev); + if (!pci->pp.skip_pwrctrl_off) { + /* + * No need to destroy pwrctrl devices as this function only + * gets called during system suspend as of now. + */ + pci_pwrctrl_power_off_devices(pci->dev); + } + qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -1432,10 +1440,18 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) pcie->cfg->ops->host_post_init(pcie); } +static void qcom_pcie_host_pme_turn_off(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + writel(ELBI_SYS_CTRL_PME_TURNOFF_MSG, pci->elbi_base + ELBI_SYS_CTRL); +} + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { .init = qcom_pcie_host_init, .deinit = qcom_pcie_host_deinit, .post_init = qcom_pcie_host_post_init, + .pme_turn_off = qcom_pcie_host_pme_turn_off, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -2104,53 +2120,51 @@ static int qcom_pcie_suspend_noirq(struct device *dev) if (!pcie) return 0; - /* - * Set minimum bandwidth required to keep data path functional during - * suspend. - */ - if (pcie->icc_mem) { - ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); - if (ret) { - dev_err(dev, - "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", - ret); - return ret; - } - } + ret = dw_pcie_suspend_noirq(pcie->pci); + if (ret) + return ret; - /* - * Turn OFF the resources only for controllers without active PCIe - * devices. For controllers with active devices, the resources are kept - * ON and the link is expected to be in L0/L1 (sub)states. - * - * Turning OFF the resources for controllers with active PCIe devices - * will trigger access violation during the end of the suspend cycle, - * as kernel tries to access the PCIe devices config space for masking - * MSIs. - * - * Also, it is not desirable to put the link into L2/L3 state as that - * implies VDD supply will be removed and the devices may go into - * powerdown state. This will affect the lifetime of the storage devices - * like NVMe. - */ - if (!dw_pcie_link_up(pcie->pci)) { - qcom_pcie_host_deinit(&pcie->pci->pp); - pcie->suspended = true; - } + if (pcie->pci->suspended) { + ret = icc_disable(pcie->icc_mem); + if (ret) + dev_err(dev, "Failed to disable PCIe-MEM interconnect path: %d\n", ret); - /* - * Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM. - * Because on some platforms, DBI access can happen very late during the - * S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC - * error. - */ - if (pm_suspend_target_state != PM_SUSPEND_MEM) { ret = icc_disable(pcie->icc_cpu); if (ret) dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret); if (pcie->use_pm_opp) dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } else { + /* + * Set minimum bandwidth required to keep data path + * functional during suspend. + */ + if (pcie->icc_mem) { + ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); + if (ret) { + dev_err(dev, + "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", + ret); + return ret; + } + } + + /* + * Only disable CPU-PCIe interconnect path if the suspend + * is non-S2RAM. On some platforms, DBI access can happen + * very late during S2RAM and a non-active CPU-PCIe + * interconnect path may lead to NoC error. + */ + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + ret = icc_disable(pcie->icc_cpu); + if (ret) + dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", + ret); + + if (pcie->use_pm_opp) + dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } } return ret; } @@ -2164,7 +2178,7 @@ static int qcom_pcie_resume_noirq(struct device *dev) if (!pcie) return 0; - if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->pci->suspended) { if (pcie->use_pm_opp) { ret = qcom_pcie_set_max_opp(dev); if (ret) { @@ -2178,19 +2192,48 @@ static int qcom_pcie_resume_noirq(struct device *dev) dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); return ret; } - } - if (pcie->suspended) { - ret = qcom_pcie_host_init(&pcie->pci->pp); - if (ret) - return ret; + ret = icc_enable(pcie->icc_mem); + if (ret) { + dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret); + goto disable_icc_cpu; + } - pcie->suspended = false; + /* + * Ignore -ENODEV & -EIO here since it is expected when no + * endpoint is connected to the PCIe link. + */ + ret = dw_pcie_resume_noirq(pcie->pci); + if (ret && ret != -ENODEV && ret != -EIO) + goto disable_icc_mem; + } else { + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->use_pm_opp) { + ret = qcom_pcie_set_max_opp(dev); + if (ret) { + dev_err(dev, "Failed to set max OPP: %d\n", ret); + return ret; + } + } + + ret = icc_enable(pcie->icc_cpu); + if (ret) { + dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", + ret); + return ret; + } + } } qcom_pcie_icc_opp_update(pcie); return 0; +disable_icc_mem: + icc_disable(pcie->icc_mem); +disable_icc_cpu: + icc_disable(pcie->icc_cpu); + + return ret; } static const struct of_device_id qcom_pcie_match[] = { From f8d0db39bcc536ef652968c45ca23f8f27f58997 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 21 May 2026 08:35:58 -0700 Subject: [PATCH 1459/5214] perf build: Fix fsmount.o build A merge conflict between: commit 552636b9317c8a84 ("perf trace: Add beautifier script for fsmount flags") commit 32969ef6e3e1979a ("perf build: Pre-generate BPF skeleton tooling during umbrella prepare phase") Resulted in a missed build dependency in the linux-next merge: commit 61da860eee0798d3 ("Merge branch 'perf-tools-next' of https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git") Fix the build by adding the necessary build dependencies. Signed-off-by: Ian Rogers Cc: Mark Brown Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/Build | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index 93cde93461a3..996e63cdf765 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build @@ -111,6 +111,13 @@ $(fsmount_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsmount_tbls) $(call rule_mkdir) $(Q)$(call echo-cmd,gen)$(SHELL) '$(fsmount_tbls)' $(beauty_uapi_linux_dir) > $@ +fsmount_attr_arrays := $(beauty_outdir)/fsmount_attr_arrays.c +fsmount_attr_tbls := $(srctree)/tools/perf/trace/beauty/fsmount_attr.sh + +$(fsmount_attr_arrays): $(beauty_uapi_linux_dir)/mount.h $(fsmount_attr_tbls) + $(call rule_mkdir) + $(Q)$(call echo-cmd,gen)$(SHELL) '$(fsmount_attr_tbls)' $(beauty_uapi_linux_dir) > $@ + fspick_arrays := $(beauty_outdir)/fspick_arrays.c fspick_tbls := $(srctree)/tools/perf/trace/beauty/fspick.sh @@ -285,7 +292,7 @@ $(OUTPUT)trace/beauty/syscalltbl.o: $(syscall_array) $(OUTPUT)trace/beauty/fsconfig.o: $(fsconfig_arrays) $(OUTPUT)trace/beauty/clone.o: $(clone_flags_array) $(OUTPUT)trace/beauty/fs_at_flags.o: $(fs_at_flags_array) -$(OUTPUT)trace/beauty/fsmount.o: $(fsmount_arrays) +$(OUTPUT)trace/beauty/fsmount.o: $(fsmount_arrays) $(fsmount_attr_arrays) $(OUTPUT)trace/beauty/fspick.o: $(fspick_arrays) $(OUTPUT)trace/beauty/ioctl.o: $(drm_ioctl_array) $(sndrv_pcm_ioctl_array) $(sndrv_ctl_ioctl_array) $(kvm_ioctl_array) $(vhost_virtio_ioctl_array) $(perf_ioctl_array) $(usbdevfs_ioctl_array) $(OUTPUT)trace/beauty/kcmp.o: $(kcmp_type_array) From 26679fad81a471428707d2dd7b0418204c52b7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 12:51:08 +0200 Subject: [PATCH 1460/5214] selftests/landlock: Filter dealloc records in audit_count_records() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit audit_count_records() counts both AUDIT_LANDLOCK_DOMAIN allocation and deallocation records in records.domain . Domain deallocation is tied to asynchronous credential freeing via kworker threads (landlock_put_ruleset_deferred), so the dealloc record can arrive after the drain in audit_init() and after the preceding audit_match_record() call. This causes flaky failures in tests that assert an exact records.domain count: a stale dealloc record from a previous test's domain inflates the count by one. Observed on x86_64 under build configurations that delay the kworker firing the dealloc callback (e.g. coverage instrumentation): the audit_layout1 tests in fs_test.c intermittently saw records.domain == 2 where 1 was expected. The fix is in the shared helper, so those existing checks become robust without needing a fs_test.c edit. Filter audit_count_records() with a regex to skip records containing deallocation status. The remaining domain records (allocation, emitted synchronously during landlock_log_denial()) are deterministic. Deallocation records are already tested explicitly via matches_log_domain_deallocated() in audit_test.c, which uses its own domain-ID-based filtering and longer timeout. With this filter in place, re-add the records.domain == 0 checks that were removed in commit 3647a4977fb7 ("selftests/landlock: Drain stale audit records on init") as a workaround for this race. Cc: Günther Noack Cc: stable@vger.kernel.org Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Tested-by: Günther Noack Link: https://patch.msgid.link/20260513105112.140137-1-mic@digikod.net Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/audit.h | 39 ++++++++++++------- tools/testing/selftests/landlock/audit_test.c | 2 + .../testing/selftests/landlock/ptrace_test.c | 1 + .../landlock/scoped_abstract_unix_test.c | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 834005b2b0f0..699aed5ffab4 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -381,18 +381,24 @@ struct audit_records { }; /* - * WARNING: Do not assert records.domain == 0 without a preceding - * audit_match_record() call. Domain deallocation records are emitted - * asynchronously from kworker threads and can arrive after the drain in - * audit_init(), corrupting the domain count. A preceding audit_match_record() - * call consumes stale records while scanning, making the assertion safe in - * practice because stale deallocation records arrive before the expected access - * records. + * Counts remaining audit records by type, skipping domain deallocation records. + * Deallocation records are emitted asynchronously from kworker threads after a + * previous test's child has exited, so they can arrive after the drain in + * audit_init() and after the preceding audit_match_record() call. Allocation + * records are emitted synchronously during landlock_log_denial() in the current + * test's syscall context, so only those are counted in records->domain. */ static int audit_count_records(int audit_fd, struct audit_records *records) { + static const char dealloc_pattern[] = REGEX_LANDLOCK_PREFIX + " status=deallocated "; struct audit_message msg; - int err; + regex_t dealloc_re; + int ret, err = 0; + + ret = regcomp(&dealloc_re, dealloc_pattern, 0); + if (ret) + return -ENOMEM; records->access = 0; records->domain = 0; @@ -402,9 +408,8 @@ static int audit_count_records(int audit_fd, struct audit_records *records) err = audit_recv(audit_fd, &msg); if (err) { if (err == -EAGAIN) - return 0; - else - return err; + err = 0; + break; } switch (msg.header.nlmsg_type) { @@ -412,12 +417,20 @@ static int audit_count_records(int audit_fd, struct audit_records *records) records->access++; break; case AUDIT_LANDLOCK_DOMAIN: - records->domain++; + ret = regexec(&dealloc_re, msg.data, 0, NULL, 0); + if (ret == REG_NOMATCH) { + records->domain++; + } else if (ret != 0) { + err = -EIO; + goto out; + } break; } } while (true); - return 0; +out: + regfree(&dealloc_re); + return err; } static int audit_init(void) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 93ae5bd0dcce..758cf2368281 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -730,6 +730,7 @@ TEST_F(audit_flags, signal) } else { EXPECT_EQ(1, records.access); } + EXPECT_EQ(0, records.domain); /* Updates filter rules to match the drop record. */ set_cap(_metadata, CAP_AUDIT_CONTROL); @@ -917,6 +918,7 @@ TEST_F(audit_exec, signal_and_open) /* Tests that there was no denial until now. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); /* * Wait for the child to do a first denied action by layer1 and diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index 1b6c8b53bf33..4f64c90583cd 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -342,6 +342,7 @@ TEST_F(audit, trace) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); yama_ptrace_scope = get_yama_ptrace_scope(); ASSERT_LE(0, yama_ptrace_scope); diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c index c47491d2d1c1..72f97648d4a7 100644 --- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c @@ -312,6 +312,7 @@ TEST_F(scoped_audit, connect_to_child) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); From d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 12:51:09 +0200 Subject: [PATCH 1461/5214] selftests/landlock: Increase default audit socket timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit matches_log_fs() and other audit_match_record() callers intermittently return -EAGAIN under heavy debug configs (KASAN, lockdep). The audit record delivery pipeline is asynchronous: landlock_log_denial() queues the record to audit_queue, and kauditd_thread dequeues and delivers via netlink. Under debug configs, kauditd scheduling between audit_log_end() and netlink_unicast() can exceed a syscall round trip (more than 1 usec), which was the value of the socket timeout used for the recvfrom() calls. The observed failure [1] is an EAGAIN error code (-11) which means that the access record had not arrived within the 1 usec timeout of recvfrom(). The expected record does arrive, but only after matches_log_fs() has already returned. It is then consumed by a later audit_count_records() call, making records.access == 1 instead of 0. Switch the default socket timeout to the slow value (1 second) so all audit_match_record() callers wait long enough for kauditd delivery, and lower it to the fast value (1 usec) only on the two paths that expect no record: audit_count_records() and the expected_domain_id == 0 probe in matches_log_domain_deallocated(). audit_init() drains stale records with the fast timeout (terminating on -EAGAIN once the backlog is empty) and switches to the patient default before returning. 1 second gives ~10x margin over the observed maximum (~100 ms, while the happy path is ~23 us). Rename the timeval constants to reflect their new roles: - audit_tv_dom_drop (1 second) -> audit_tv_default: default socket timeout, patient enough for asynchronous kauditd delivery. - audit_tv_default (1 usec) -> audit_tv_fast: fast timeout for paths that expect no record (drain, audit_count_records(), probes). Invert the conditional in matches_log_domain_deallocated(). Check setsockopt returns on both the lower and restore paths; preserve the first error via !err when the restore fails after a prior error so the actionable return code is not masked by a bookkeeping failure. Cc: Günther Noack Cc: Thomas Weißschuh Cc: stable@vger.kernel.org Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Reported-by: Günther Noack Closes: https://lore.kernel.org/r/20260402.eb5c4e85f472@gnoack.org [1] Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202605111649.a8b30a62-lkp@intel.com Closes: https://lore.kernel.org/oe-lkp/202604300436.a07fae12-lkp@intel.com Tested-by: Günther Noack Link: https://patch.msgid.link/20260513105112.140137-2-mic@digikod.net Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/audit.h | 80 +++++++++++++++++++----- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 699aed5ffab4..936fe20f020e 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -45,17 +45,25 @@ struct audit_message { }; }; -static const struct timeval audit_tv_dom_drop = { +static const struct timeval audit_tv_default = { /* - * Because domain deallocation is tied to asynchronous credential - * freeing, receiving such event may take some time. In practice, - * on a small VM, it should not exceed 100k usec, but let's wait up - * to 1 second to be safe. + * Default socket timeout for audit_match_record() callers that expect a + * record to arrive. Asynchronous kauditd delivery can exceed 1 usec + * under heavy debug configs (KASAN, lockdep), where kauditd_thread + * scheduling between audit_log_end() and netlink_unicast() takes longer + * than the previous 1 usec timeout. 1 second is a generous ceiling: on + * the happy path, kauditd delivers within dozens of usec. */ .tv_sec = 1, }; -static const struct timeval audit_tv_default = { +static const struct timeval audit_tv_fast = { + /* + * Fast timeout for paths that expect no record (audit_init() drain, + * audit_count_records(), probes). Causes audit_recv() to return + * -EAGAIN once the socket buffer is empty, naturally terminating the + * read loop. + */ .tv_usec = 1, }; @@ -334,8 +342,13 @@ static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid, * Matches a domain deallocation record. When expected_domain_id is non-zero, * the pattern includes the specific domain ID so that stale deallocation * records from a previous test (with a different domain ID) are skipped by - * audit_match_record(), and the socket timeout is temporarily increased to - * audit_tv_dom_drop to wait for the asynchronous kworker deallocation. + * audit_match_record(), waiting for the asynchronous kworker deallocation with + * the default patient timeout. + * + * When expected_domain_id is zero, the caller is probing for any dealloc record + * that may or may not arrive. Temporarily lowers the socket timeout to + * audit_tv_fast for this probe so it returns promptly when no record is + * pending; restores audit_tv_default after. */ static int __maybe_unused matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, @@ -361,16 +374,21 @@ matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, if (log_match_len >= sizeof(log_match)) return -E2BIG; - if (expected_domain_id) - setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_dom_drop, sizeof(audit_tv_dom_drop)); + if (!expected_domain_id) { + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_fast, sizeof(audit_tv_fast))) + return -errno; + } err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, domain_id); - if (expected_domain_id) - setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, - sizeof(audit_tv_default)); + if (!expected_domain_id) { + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_default, sizeof(audit_tv_default)) && + !err) + err = -errno; + } return err; } @@ -387,6 +405,11 @@ struct audit_records { * audit_init() and after the preceding audit_match_record() call. Allocation * records are emitted synchronously during landlock_log_denial() in the current * test's syscall context, so only those are counted in records->domain. + * + * Temporarily lowers SO_RCVTIMEO to audit_tv_fast for the read loop: this is a + * "no record expected" path that should terminate on the first -EAGAIN. The + * default patient timeout is restored on exit for subsequent + * audit_match_record() callers. */ static int audit_count_records(int audit_fd, struct audit_records *records) { @@ -403,6 +426,12 @@ static int audit_count_records(int audit_fd, struct audit_records *records) records->access = 0; records->domain = 0; + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, + sizeof(audit_tv_fast))) { + err = -errno; + goto out; + } + do { memset(&msg, 0, sizeof(msg)); err = audit_recv(audit_fd, &msg); @@ -429,6 +458,10 @@ static int audit_count_records(int audit_fd, struct audit_records *records) } while (true); out: + if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)) && + !err) + err = -errno; regfree(&dealloc_re); return err; } @@ -449,9 +482,9 @@ static int audit_init(void) if (err) goto err_close; - /* Sets a timeout for negative tests. */ - err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, - sizeof(audit_tv_default)); + /* Uses the fast timeout to drain stale records below. */ + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, + sizeof(audit_tv_fast)); if (err) { err = -errno; goto err_close; @@ -467,6 +500,19 @@ static int audit_init(void) while (audit_recv(fd, NULL) == 0) ; + /* + * Restores the default timeout for audit_match_record() callers that + * expect a record to arrive. Paths that expect no record restore the + * fast timeout locally (audit_count_records(), the expected_domain_id + * == 0 probe in matches_log_domain_deallocated()). + */ + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)); + if (err) { + err = -errno; + goto err_close; + } + return fd; err_close: From 6d27f92c54ce28cfbd2a8a479a96d6f4a781b7d2 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 16 Mar 2026 11:58:22 -0400 Subject: [PATCH 1462/5214] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work If a UVC camera has an asynchronous control, uvc_status_stop may be called from async_ctrl.work: uvc_ctrl_status_event_work() uvc_ctrl_status_event() uvc_ctrl_clear_handle() uvc_pm_put() uvc_status_put() uvc_status_stop() cancel_work_sync() This will cause a deadlock, since cancel_work_sync will wait for uvc_ctrl_status_event_work to complete before returning. Fix this by returning early from uvc_status_stop if we are currently in the work function. flush_status now remains false until uvc_status_start is called again, ensuring that uvc_ctrl_status_event_work won't resubmit the URB. Fixes: a32d9c41bdb8 ("media: uvcvideo: Make power management granular") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/6733bdfb-3e88-479f-8956-ab09c04c433e@linux.dev/ Signed-off-by: Sean Anderson Link: https://patch.msgid.link/20260316155823.1855434-1-sean.anderson@linux.dev Reviewed-by: Ricardo Ribalda Tested-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_status.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 65f5356bebb3..b632cf5e3fe9 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags) if (!dev->int_urb) return 0; + /* + * If the previous uvc_status_stop() call was from the async work, + * the work may still be running. Wait for it to finish before we submit + * the urb. + */ + flush_work(&dev->async_ctrl.work); + + /* Clear the flush status if we were previously stopped. */ + smp_store_release(&dev->flush_status, false); + return usb_submit_urb(dev->int_urb, flags); } @@ -336,6 +346,15 @@ static void uvc_status_stop(struct uvc_device *dev) */ smp_store_release(&dev->flush_status, true); + /* + * If we are called from the event work function, the URB is guaranteed + * to not be in flight as it has completed and has not been resubmitted. + * There's no need to cancel the work (which would deadlock), or to kill + * the URB. + */ + if (current_work() == &w->work) + return; + /* * Cancel any pending asynchronous work. If any status event was queued, * process it synchronously. @@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev) */ if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); - - /* - * From this point, there are no events on the queue and the status URB - * is dead. No events will be queued until uvc_status_start() is called. - * The barrier is needed to make sure that flush_status is visible to - * uvc_ctrl_status_event_work() when uvc_status_start() will be called - * again. - */ - smp_store_release(&dev->flush_status, false); } int uvc_status_resume(struct uvc_device *dev) From a307316ae7db7961ac485463501a040495f4e634 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:28 +0000 Subject: [PATCH 1463/5214] media: uvcvideo: Do not open code uvc_queue_get_current_buffer Do not re-implement uvc_queue_get_current_buffer() logic inside uvc_video_complete(), just call the function. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-1-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 62db4db4e565..a2ff73511d0e 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1704,7 +1704,6 @@ static void uvc_video_complete(struct urb *urb) struct vb2_queue *vb2_qmeta = stream->meta.queue.vdev.queue; struct uvc_buffer *buf = NULL; struct uvc_buffer *buf_meta = NULL; - unsigned long flags; int ret; switch (urb->status) { @@ -1730,13 +1729,8 @@ static void uvc_video_complete(struct urb *urb) buf = uvc_queue_get_current_buffer(queue); - if (vb2_qmeta) { - spin_lock_irqsave(&qmeta->irqlock, flags); - if (!list_empty(&qmeta->irqqueue)) - buf_meta = list_first_entry(&qmeta->irqqueue, - struct uvc_buffer, queue); - spin_unlock_irqrestore(&qmeta->irqlock, flags); - } + if (vb2_qmeta) + buf_meta = uvc_queue_get_current_buffer(qmeta); /* Re-initialise the URB async work. */ uvc_urb->async_operations = 0; From a15b773fe4ffa450b56347cc506b2d1405600f5d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:29 +0000 Subject: [PATCH 1464/5214] media: uvcvideo: Avoid partial metadata buffers If the metadata queue that is empty receives a new buffer while we are in the middle of processing a frame, the first metadata buffer will contain partial information. Avoid this by tracking the state of the metadata buffer and making sure that it is in sync with the data buffer. Now that we are at it, make sure that we skip buffers of size 1 or 0. They are not allowed by the spec... but it is a simple check to add and better be safe than sorry. Fixes: 088ead255245 ("media: uvcvideo: Add a metadata device node") Cc: stable@vger.kernel.org Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-2-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a2ff73511d0e..0e691b872701 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1149,7 +1149,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const u8 *data, int len) + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf, + const u8 *data, int len) { u8 header_len; u8 fid; @@ -1278,6 +1280,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; + if (meta_buf) + meta_buf->state = UVC_BUF_STATE_ACTIVE; } stream->last_fid = fid; @@ -1435,7 +1439,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, ktime_t time; const u8 *scr; - if (!meta_buf || length == 2) + if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE) return; has_pts = mem[1] & UVC_STREAM_PTS; @@ -1552,7 +1556,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(stream, buf, mem, + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); @@ -1601,7 +1605,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, + len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); From edc1917599c5339aedc83135cade66517e0a2972 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:20 +0000 Subject: [PATCH 1465/5214] media: uvcvideo: Fix dev_sof filtering in hw timestamp To avoid filling the clock circular buffer with duplicated data we only add it if the new value sof is different than the last added sof. The issue is that we compare the unprocess sof with the processed sof. If there is a sof_offset, or UVC_QUIRK_INVALID_DEVICE_SOF is enabled, the comparison will not work as expected. This patch moves the comparison to the right place. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-1-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 0e691b872701..efd746dd5db0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -583,16 +583,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* - * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. This filtering is also needed to support UVC 1.5, where - * all the data packets of the same frame contains the same SOF. In that - * case only the first one will match the host_sof. - */ sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (sample.dev_sof == stream->clock.last_sof) - return; - sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -664,6 +655,16 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, } sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + + /* + * To limit the amount of data, drop SCRs with an SOF identical to the + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. + */ + if (sample.dev_sof == stream->clock.last_sof) + return; + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof = sample.dev_sof; } From ede7de6e6b3db552d10ac50557d69c50d1b08486 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:21 +0000 Subject: [PATCH 1466/5214] media: uvcvideo: Use hw timestaming if the clock buffer is full In some situations, even with a full clock buffer, it does not contain 250msec of data. This results in the driver jumping back from software to hardware timestapsing creating a nasty artifact in the video. If the clock buffer is full, use it to calculate the timestamp instead of defaulting to software stamps, the reduced accuracy is less visible than jumping from one timestamping mechanism to the other. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-2-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index efd746dd5db0..bb3ee942e570 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -834,15 +834,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y2 += 2048 << 16; /* - * Have at least 1/4 of a second of timestamps before we - * try to do any calculation. Otherwise we do not have enough - * precision. This value was determined by running Android CTS - * on different devices. + * If the buffer is not full, we want to gather at least 1/4th of + * timestamps before using HW timestamping. We do this to avoid jitter + * on the initial frames. + * + * If the buffer is full we would use it regardless of how much data + * it represents. This could be solved with an infinite big circular + * buffer, but RAM is expensive these days, specially the infinitely + * big. + * + * The value of 1/4th of a second was determined by running Android's + * CTS on different devices. * * dev_sof runs at 1KHz, and we have a fixed point precision of * 16 bits. */ - if ((y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From 1719d78f832dda8dd3f09a867ba74e05d6f11308 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:22 +0000 Subject: [PATCH 1467/5214] media: uvcvideo: Relax the constrains for interpolating the hw clock In the initial version we set the min value to 250msec. Looks like 100msec can also provide a good value. Now that we are at it, add a macro to make it cleaner. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-3-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index bb3ee942e570..d2e61be35bf6 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +/* + * The accuracy of the hardware timestamping depends on having enough data to + * interpolate between the different clock domains. This value is sof cycles, + * this is, milliseconds. + */ +#define UVC_MIN_HW_TIMESTAMP_DIFF 100 + static inline ktime_t uvc_video_get_time(void) { if (uvc_clock_param == CLOCK_MONOTONIC) @@ -843,13 +850,13 @@ void uvc_video_clock_update(struct uvc_streaming *stream, * buffer, but RAM is expensive these days, specially the infinitely * big. * - * The value of 1/4th of a second was determined by running Android's - * CTS on different devices. + * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running + * Android's CTS on different devices. * - * dev_sof runs at 1KHz, and we have a fixed point precision of - * 16 bits. + * y1 and y2 are dev_sof with a fixed point precision of 16 bits. */ - if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && + (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From ba649fff36c1fe68489a86d00285224907edd436 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:23 +0000 Subject: [PATCH 1468/5214] media: uvcvideo: Do not add clock samples with small sof delta Some UVC 1.1 cameras running in fast isochronous mode tend to spam the USB host with a lot of empty packets. These packets contain clock information and are added to the clock buffer but do not add any accuracy to the calculation. In fact, it is quite the opposite, in our calculations, only the first and the last timestamp is used, and we only have 32 slots. Ignore the samples that will produce less than MIN_HW_TIMESTAMP_DIFF data. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Tested-by: Yunke Cao Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-4-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index d2e61be35bf6..09d6baf87ec5 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -544,6 +544,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_unlock_irqrestore(&clock->lock, flags); } +static inline u16 sof_diff(u16 a, u16 b) +{ + /* + * Because the result is modulo 2048 (via & 2047), we do not need a + * special case for a < b. + */ + return (a - b) & 2047; +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) @@ -664,12 +673,13 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; /* - * To limit the amount of data, drop SCRs with an SOF identical to the + * To limit the amount of data, drop SCRs with an SOF similar to the * previous one. This filtering is also needed to support UVC 1.5, where * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sample.dev_sof == stream->clock.last_sof) + if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); From 0491c93a510c32eaf8ed404b99a447e10c30291e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:24 +0000 Subject: [PATCH 1469/5214] media: uvcvideo: Do not add samples if dev_sof has not changed We only save relevant samples into the circular buffer. If the data is very similar to the previous one, exit early, this allows us to avoid some expensive operations such as usb_get_current_frame_number(). Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Tested-by: Yunke Cao Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-5-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 16 +++++++++++----- drivers/media/usb/uvc/uvcvideo.h | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 09d6baf87ec5..c104ffe3b3c1 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -524,7 +524,7 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_lock_irqsave(&clock->lock, flags); - if (clock->count > 0 && clock->last_sof > sample->dev_sof) { + if (clock->count > 0 && clock->last_sof_processed > sample->dev_sof) { /* * Remove data from the circular buffer that is older than the * last SOF overflow. We only support one SOF overflow per @@ -599,7 +599,12 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); + sample.dev_sof = get_unaligned_le16(&data[header_size - 2]) & 2047; + /* If the sample SOF is identical to the previous one, quit early. */ + if (stream->clock.last_sof_raw == sample.dev_sof) + return; + stream->clock.last_sof_raw = sample.dev_sof; + sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -678,19 +683,20 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + if (sof_diff(sample.dev_sof, stream->clock.last_sof_processed) <= (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); - stream->clock.last_sof = sample.dev_sof; + stream->clock.last_sof_processed = sample.dev_sof; } static void uvc_video_clock_reset(struct uvc_clock *clock) { clock->head = 0; clock->count = 0; - clock->last_sof = -1; + clock->last_sof_processed = -1; + clock->last_sof_raw = -1; clock->last_sof_overflow = -1; clock->sof_offset = -1; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 4ba35727e954..b6bcee4a222f 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -522,7 +522,8 @@ struct uvc_streaming { unsigned int size; unsigned int last_sof_overflow; - u16 last_sof; + u16 last_sof_processed; + u16 last_sof_raw; u16 sof_offset; u8 last_scr[6]; From a3d78e74dd3ed04797ea351edb7f0a19b961c063 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:25 +0000 Subject: [PATCH 1470/5214] media: uvcvideo: Only do uvc_video_get_time() if needed There is no need to calculate the current time if the sample is going to be filtered. Move the assignment close to uvc_video_clock_add_sample(). Suggested-by: Hans de Goede Signed-off-by: Ricardo Ribalda Tested-by: Yunke Cao Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-6-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index c104ffe3b3c1..fc3536a4399f 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -645,8 +645,6 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) sample.dev_sof = sample.host_sof; - sample.host_time = uvc_video_get_time(); - /* * The UVC specification allows device implementations that can't obtain * the USB frame number to keep their own frame counters as long as they @@ -687,6 +685,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; + /* This is expensive, only do it if the sample will be added. */ + sample.host_time = uvc_video_get_time(); + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof_processed = sample.dev_sof; } From e15836e4aa684d4f8d84376c54d3fd202e1bad50 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 20 May 2026 17:52:19 +0530 Subject: [PATCH 1471/5214] dt-bindings: interconnect: qcom-bwmon: Add Hawi llcc-bwmon compatible Document BWMONv5 instance present on Hawi SoC for monitoring bandwidth LLCC to DDR. Signed-off-by: Mukesh Ojha Link: https://patch.msgid.link/20260520122219.2372694-1-mukesh.ojha@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 82b1d94d3010..ff64225e8281 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -46,6 +46,7 @@ properties: - const: qcom,sdm845-bwmon # BWMON v4, unified register space - items: - enum: + - qcom,hawi-llcc-bwmon - qcom,qcs615-llcc-bwmon - qcom,qcs8300-llcc-bwmon - qcom,sa8775p-llcc-bwmon From 07548b04dc36678898f1d1274c2e1731ce264701 Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 11:38:46 -0700 Subject: [PATCH 1472/5214] dt-bindings: interconnect: qcom: document the RPMh NoC for Hawi SoC Document the RPMh Network-On-Chip interconnect for the Qualcomm Hawi SoC. Reviewed-by: Mike Tipton Reviewed-by: Krzysztof Kozlowski Signed-off-by: Vivek Aknurwar Link: https://patch.msgid.link/20260506-icc-hawi-v4-1-35447fdc482b@oss.qualcomm.com Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,hawi-rpmh.yaml | 131 ++++++++++++++ .../dt-bindings/interconnect/qcom,hawi-rpmh.h | 165 ++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,hawi-rpmh.yaml create mode 100644 include/dt-bindings/interconnect/qcom,hawi-rpmh.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,hawi-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,hawi-rpmh.yaml new file mode 100644 index 000000000000..49a2dca5db62 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,hawi-rpmh.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,hawi-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Hawi + +maintainers: + - Vivek Aknurwar + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,hawi-rpmh.h + +properties: + compatible: + enum: + - qcom,hawi-aggre1-noc + - qcom,hawi-clk-virt + - qcom,hawi-cnoc-main + - qcom,hawi-gem-noc + - qcom,hawi-llclpi-noc + - qcom,hawi-lpass-ag-noc + - qcom,hawi-lpass-lpiaon-noc + - qcom,hawi-lpass-lpicx-noc + - qcom,hawi-mc-virt + - qcom,hawi-mmss-noc + - qcom,hawi-nsp-noc + - qcom,hawi-pcie-anoc + - qcom,hawi-stdst-cfg + - qcom,hawi-stdst-main + - qcom,hawi-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 3 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,hawi-clk-virt + - qcom,hawi-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,hawi-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,hawi-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,hawi-aggre1-noc + - qcom,hawi-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + clk_virt: interconnect-0 { + compatible = "qcom,hawi-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre_noc: interconnect@f00000 { + compatible = "qcom,hawi-aggre1-noc"; + reg = <0x0 0xf00000 0x0 0x54400>; + #interconnect-cells = <2>; + clocks = <&gcc_aggre_ufs_phy_axi_clk>, + <&gcc_aggre_usb3_prim_axi_clk>, + <&rpmhcc_ipa_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + }; diff --git a/include/dt-bindings/interconnect/qcom,hawi-rpmh.h b/include/dt-bindings/interconnect/qcom,hawi-rpmh.h new file mode 100644 index 000000000000..a8b649679846 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,hawi-rpmh.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_HAWI_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_HAWI_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_2 1 +#define MASTER_QUP_3 2 +#define MASTER_QUP_4 3 +#define MASTER_CRYPTO 4 +#define MASTER_IPA 5 +#define MASTER_QUP_1 6 +#define MASTER_SOCCP_PROC 7 +#define MASTER_QDSS_ETR 8 +#define MASTER_QDSS_ETR_1 9 +#define MASTER_SDCC_2 10 +#define MASTER_SDCC_4 11 +#define MASTER_UFS_MEM 12 +#define MASTER_USB3 13 +#define SLAVE_A1NOC_SNOC 14 + +#define MASTER_DDR_EFF_VETO 0 +#define MASTER_QUP_CORE_0 1 +#define MASTER_QUP_CORE_1 2 +#define MASTER_QUP_CORE_2 3 +#define MASTER_QUP_CORE_3 4 +#define MASTER_QUP_CORE_4 5 +#define SLAVE_DDR_EFF_VETO 6 +#define SLAVE_QUP_CORE_0 7 +#define SLAVE_QUP_CORE_1 8 +#define SLAVE_QUP_CORE_2 9 +#define SLAVE_QUP_CORE_3 10 +#define SLAVE_QUP_CORE_4 11 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_IPA_CFG 3 +#define SLAVE_IPC_ROUTER_FENCE 4 +#define SLAVE_SOCCP 5 +#define SLAVE_TME_CFG 6 +#define SLAVE_CNOC_CFG 7 +#define SLAVE_DDRSS_CFG 8 +#define SLAVE_IMEM 9 +#define SLAVE_PCIE_0 10 +#define SLAVE_PCIE_1 11 + +#define MASTER_GIC 0 +#define MASTER_GPU_TCU 1 +#define MASTER_SYS_TCU 2 +#define MASTER_APPSS_PROC 3 +#define MASTER_GFX3D 4 +#define MASTER_LPASS_GEM_NOC 5 +#define MASTER_MSS_PROC 6 +#define MASTER_MNOC_HF_MEM_NOC 7 +#define MASTER_MNOC_SF_MEM_NOC 8 +#define MASTER_COMPUTE_NOC 9 +#define MASTER_ANOC_PCIE_GEM_NOC 10 +#define MASTER_QPACE 11 +#define MASTER_SNOC_SF_MEM_NOC 12 +#define MASTER_WLAN_Q6 13 +#define SLAVE_GEM_NOC_CNOC 14 +#define SLAVE_LLCC 15 +#define SLAVE_MEM_NOC_PCIE_SNOC 16 + +#define MASTER_LPIAON_NOC_LLCLPI_NOC 0 +#define SLAVE_LPASS_LPI_CC 1 +#define SLAVE_LLCC_ISLAND 2 +#define SLAVE_SERVICE_LLCLPI_NOC 3 +#define SLAVE_SERVICE_LLCLPI_NOC_CHIPCX 4 + +#define MASTER_LPIAON_NOC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LPASS_LPINOC 0 +#define SLAVE_LPIAON_NOC_LLCLPI_NOC 1 +#define SLAVE_LPIAON_NOC_LPASS_AG_NOC 2 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPICX_NOC_LPIAON_NOC 1 + +#define MASTER_LLCC 0 +#define MASTER_DDR_RT 1 +#define SLAVE_EBI1 2 +#define SLAVE_DDR_RT 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_NRT_ICP_SF 1 +#define MASTER_CAMNOC_RT_CDM_SF 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP 4 +#define MASTER_MDSS_DCP 5 +#define MASTER_CDSP_HCP 6 +#define MASTER_VIDEO_CV_PROC 7 +#define MASTER_VIDEO_EVA 8 +#define MASTER_VIDEO_MVP 9 +#define MASTER_VIDEO_V_PROC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_MNOC_SF_MEM_NOC 12 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_CFG_CENTER 0 +#define MASTER_CFG_EAST 1 +#define MASTER_CFG_MM 2 +#define MASTER_CFG_NORTH 3 +#define MASTER_CFG_SOUTH 4 +#define MASTER_CFG_SOUTHWEST 5 +#define SLAVE_AHB2PHY_SOUTH 6 +#define SLAVE_BOOT_ROM 7 +#define SLAVE_CAMERA_CFG 8 +#define SLAVE_CLK_CTL 9 +#define SLAVE_CRYPTO_CFG 10 +#define SLAVE_DISPLAY_CFG 11 +#define SLAVE_EVA_CFG 12 +#define SLAVE_GFX3D_CFG 13 +#define SLAVE_I2C 14 +#define SLAVE_IMEM_CFG 15 +#define SLAVE_IPC_ROUTER_CFG 16 +#define SLAVE_IRIS_CFG 17 +#define SLAVE_CNOC_MSS 18 +#define SLAVE_PCIE_0_CFG 19 +#define SLAVE_PCIE_1_CFG 20 +#define SLAVE_PRNG 21 +#define SLAVE_QSPI_0 22 +#define SLAVE_QUP_1 23 +#define SLAVE_QUP_2 24 +#define SLAVE_QUP_3 25 +#define SLAVE_QUP_4 26 +#define SLAVE_SDCC_2 27 +#define SLAVE_SDCC_4 28 +#define SLAVE_TLMM 29 +#define SLAVE_UFS_MEM_CFG 30 +#define SLAVE_USB3 31 +#define SLAVE_VSENSE_CTRL_CFG 32 +#define SLAVE_PCIE_ANOC_CFG 33 +#define SLAVE_QDSS_CFG 34 +#define SLAVE_QDSS_STM 35 +#define SLAVE_TCSR 36 +#define SLAVE_TCU 37 + +#define MASTER_CNOC_STARDUST 0 +#define SLAVE_STARDUST_CENTER_CFG 1 +#define SLAVE_STARDUST_EAST_CFG 2 +#define SLAVE_STARDUST_MM_CFG 3 +#define SLAVE_STARDUST_NORTH_CFG 4 +#define SLAVE_STARDUST_SOUTH_CFG 5 +#define SLAVE_STARDUST_SOUTHWEST_CFG 6 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_APSS_NOC 1 +#define MASTER_CNOC_SNOC 2 +#define SLAVE_SNOC_GEM_NOC_SF 3 + +#endif From ffaa88a08486f6703434fd5ad81da6775f5c984f Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Wed, 6 May 2026 11:38:47 -0700 Subject: [PATCH 1473/5214] interconnect: qcom: add Hawi interconnect provider driver Add driver for the Qualcomm interconnect buses found in Hawi based platforms. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pair. Reviewed-by: Mike Tipton Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Acked-by: Krzysztof Kozlowski Signed-off-by: Vivek Aknurwar Link: https://patch.msgid.link/20260506-icc-hawi-v4-2-35447fdc482b@oss.qualcomm.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/hawi.c | 2028 ++++++++++++++++++++++++++++ 3 files changed, 2039 insertions(+) create mode 100644 drivers/interconnect/qcom/hawi.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 786b4eda44b4..0f3ba252c51d 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -26,6 +26,15 @@ config INTERCONNECT_QCOM_GLYMUR This is a driver for the Qualcomm Network-on-Chip on glymur-based platforms. +config INTERCONNECT_QCOM_HAWI + tristate "Qualcomm HAWI interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on hawi-based + platforms. + config INTERCONNECT_QCOM_KAANAPALI tristate "Qualcomm Kaanapali interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index cdf2c6c9fbf3..51aeb07a1707 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -6,6 +6,7 @@ interconnect_qcom-y := icc-common.o icc-bcm-voter-objs := bcm-voter.o qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o +qnoc-hawi-objs := hawi.o qnoc-kaanapali-objs := kaanapali.o qnoc-milos-objs := milos.o qnoc-msm8909-objs := msm8909.o @@ -51,6 +52,7 @@ icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o +obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o diff --git a/drivers/interconnect/qcom/hawi.c b/drivers/interconnect/qcom/hawi.c new file mode 100644 index 000000000000..6ece9828c62d --- /dev/null +++ b/drivers/interconnect/qcom/hawi.c @@ -0,0 +1,2028 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node ddr_eff_veto_slave = { + .name = "ddr_eff_veto_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup3_core_slave = { + .name = "qup3_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup4_core_slave = { + .name = "qup4_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router_fence = { + .name = "qhs_ipc_router_fence", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_g4x1 = { + .name = "xs_pcie_g4x1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_lpi_cc = { + .name = "qhs_lpi_cc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qns_lb = { + .name = "qns_lb", + .channels = 4, + .buswidth = 32, +}; + +static struct qcom_icc_node srvc_llclpi_noc = { + .name = "srvc_llclpi_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_llclpi_noc_chipcx = { + .name = "srvc_llclpi_noc_chipcx", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node ddr_rt_slave = { + .name = "ddr_rt_slave", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto_cfg = { + .name = "qhs_crypto_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_eva_cfg = { + .name = "qhs_eva_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_iris_cfg = { + .name = "qhs_iris_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_cfg = { + .name = "qhs_pcie_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_g4x1_cfg = { + .name = "qhs_pcie_g4x1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup3 = { + .name = "qhs_qup3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup4 = { + .name = "qhs_qup4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3 = { + .name = "qhs_usb3", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_cfg = { + .name = "qss_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_qdss_stm = { + .name = "qss_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_tcsr = { + .name = "qss_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ddr_eff_veto_master = { + .name = "ddr_eff_veto_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qup3_core_master = { + .name = "qup3_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_node qup4_core_master = { + .name = "qup4_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 2, + .link_nodes = { &xs_pcie, &xs_pcie_g4x1 }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc_llclpi_noc = { + .name = "qnm_lpiaon_noc_llclpi_noc", + .channels = 1, + .buswidth = 16, + .num_links = 4, + .link_nodes = { &qhs_lpi_cc, &qns_lb, + &srvc_llclpi_noc, &srvc_llclpi_noc_chipcx }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node ddr_rt_mc = { + .name = "ddr_rt_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_node qsm_cfg_east = { + .name = "qsm_cfg_east", + .channels = 1, + .buswidth = 4, + .num_links = 4, + .link_nodes = { &qhs_crypto_cfg, &qhs_gpuss_cfg, + &qhs_qup2, &qhs_vsense_ctrl_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_mm = { + .name = "qsm_cfg_mm", + .channels = 1, + .buswidth = 4, + .num_links = 5, + .link_nodes = { &qhs_boot_rom, &qhs_camera_cfg, + &qhs_display_cfg, &qhs_eva_cfg, + &qhs_iris_cfg }, +}; + +static struct qcom_icc_node qsm_cfg_north = { + .name = "qsm_cfg_north", + .channels = 1, + .buswidth = 4, + .num_links = 5, + .link_nodes = { &qhs_pcie_cfg, &qhs_pcie_g4x1_cfg, + &qhs_qup3, &qhs_qup4, + &qhs_sdc2 }, +}; + +static struct qcom_icc_node qsm_cfg_south = { + .name = "qsm_cfg_south", + .channels = 1, + .buswidth = 4, + .num_links = 6, + .link_nodes = { &qhs_ahb2phy0, &qhs_qspi, + &qhs_qup1, &qhs_sdc4, + &qhs_ufs_mem_cfg, &qhs_usb3 }, +}; + +static struct qcom_icc_node qsm_cfg_southwest = { + .name = "qsm_cfg_southwest", + .channels = 1, + .buswidth = 4, + .num_links = 2, + .link_nodes = { &qhs_ipc_router, &qhs_mss_cfg }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 4, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qns_llc_lpinoc = { + .name = "qns_llc_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc_llclpi_noc }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_stdst_east_cfg = { + .name = "qss_stdst_east_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_east }, +}; + +static struct qcom_icc_node qss_stdst_mm_cfg = { + .name = "qss_stdst_mm_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_mm }, +}; + +static struct qcom_icc_node qss_stdst_north_cfg = { + .name = "qss_stdst_north_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_north }, +}; + +static struct qcom_icc_node qss_stdst_south_cfg = { + .name = "qss_stdst_south_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_south }, +}; + +static struct qcom_icc_node qss_stdst_southwest_cfg = { + .name = "qss_stdst_southwest_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_southwest }, +}; + +static struct qcom_icc_node alm_gic = { + .name = "alm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qnm_qpace = { + .name = "qnm_qpace", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x153000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qsm_cfg_center = { + .name = "qsm_cfg_center", + .channels = 1, + .buswidth = 4, + .num_links = 10, + .link_nodes = { &qhs_clk_ctl, &qhs_i2c, + &qhs_imem_cfg, &qhs_prng, + &qhs_tlmm, &qss_pcie_anoc_cfg, + &qss_qdss_cfg, &qss_qdss_stm, + &qss_tcsr, &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node qss_stdst_center_cfg = { + .name = "qss_stdst_center_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg_center }, +}; + +static struct qcom_icc_node qsm_cnoc_main = { + .name = "qsm_cnoc_main", + .channels = 1, + .buswidth = 4, + .num_links = 6, + .link_nodes = { &qss_stdst_center_cfg, &qss_stdst_east_cfg, + &qss_stdst_mm_cfg, &qss_stdst_north_cfg, + &qss_stdst_south_cfg, &qss_stdst_southwest_cfg }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cnoc_main }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 8, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x145000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x147000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 4, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 4, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x51000, 0x53000, 0xd1000, 0xd3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x149000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x55000, 0xd5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x57000, 0xd7000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 4, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 4, + .port_offsets = { 0x59000, 0x5b000, 0xd9000, 0xdb000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_wlan_q6 = { + .name = "qnm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 4, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_gemnoc = { + .name = "qns_pcie_gemnoc", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2a000, 0x2b000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2c000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x38000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2d000, 0x2e000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2f000, 0x30000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdss_dcp = { + .name = "qnm_mdss_dcp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x39000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x34000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x35000, 0x36000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x32000, 0x33000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x37000 }, + .prio = 4, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .channels = 4, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie = { + .name = "xm_pcie", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie_g4x1 = { + .name = "xm_pcie_g4x1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_node qnm_aggre_noc = { + .name = "qnm_aggre_noc", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x20000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_apss_noc = { + .name = "qnm_apss_noc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x49000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x48000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup3 = { + .name = "qhm_qup3", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x46000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup4 = { + .name = "qhm_qup4", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x47000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x40000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x41000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_qup1 = { + .name = "qxm_qup1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4d000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x45000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x42000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x43000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x44000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4b000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3 = { + .name = "xm_usb3", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 32, + .num_links = 2, + .link_nodes = { &qns_llc_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = { + .name = "qnm_lpinoc_dsp_qns4m", + .channels = 1, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 24, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_aoss, &qhs_ipa, + &qhs_ipc_router_fence, &qhs_soccp, + &qhs_tme_cfg, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem, + &xs_pcie, &xs_pcie_g4x1, + &qsm_cfg_center, &qsm_cfg_east, + &qsm_cfg_mm, &qsm_cfg_north, + &qsm_cfg_south, &qsm_cfg_southwest, + &qhs_ahb2phy0, &qhs_boot_rom, + &qhs_camera_cfg, &qhs_clk_ctl, + &qhs_crypto_cfg, &qhs_eva_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 1, + .nodes = { &qhs_display_cfg }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_de0 = { + .name = "DE0", + .enable_mask = BIT(0), + .num_nodes = 1, + .nodes = { &ddr_eff_veto_slave }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 5, + .nodes = { &qnm_lpiaon_noc_llclpi_noc, &qns_lb, + &qnm_lpass_lpinoc, &qns_llc_lpinoc, + &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mc5 = { + .name = "MC5", + .num_nodes = 1, + .nodes = { &ddr_rt_slave }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 9, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_vapss_hcp, &qnm_video_cv_cpu, + &qnm_video_mvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qpc0 = { + .name = "QPC0", + .num_nodes = 1, + .nodes = { &qnm_qpace }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup3 = { + .name = "QUP3", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup3_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup4 = { + .name = "QUP4", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup4_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 15, + .nodes = { &alm_gic, &alm_gpu_tcu, + &alm_sys_tcu, &chm_apps, + &qnm_gpu, &qnm_lpass_gemnoc, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qnm_wlan_q6, &qns_gem_noc_cnoc, + &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qns_pcie_gemnoc }, +}; + +static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_QUP_3] = &qhm_qup3, + [MASTER_QUP_4] = &qhm_qup4, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QUP_1] = &qxm_qup1, + [MASTER_SOCCP_PROC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3] = &xm_usb3, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config hawi_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x54400, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_aggre1_noc = { + .config = &hawi_aggre1_noc_regmap_config, + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_de0, + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, + &bcm_qup3, + &bcm_qup4, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_DDR_EFF_VETO] = &ddr_eff_veto_master, + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [MASTER_QUP_CORE_3] = &qup3_core_master, + [MASTER_QUP_CORE_4] = &qup4_core_master, + [SLAVE_DDR_EFF_VETO] = &ddr_eff_veto_slave, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, + [SLAVE_QUP_CORE_3] = &qup3_core_slave, + [SLAVE_QUP_CORE_4] = &qup4_core_slave, +}; + +static const struct qcom_icc_desc hawi_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_FENCE] = &qhs_ipc_router_fence, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &xs_pcie, + [SLAVE_PCIE_1] = &xs_pcie_g4x1, +}; + +static const struct regmap_config hawi_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_cnoc_main = { + .config = &hawi_cnoc_main_regmap_config, + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_qpc0, + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GIC] = &alm_gic, + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_QPACE] = &qnm_qpace, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qnm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config hawi_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x160200, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_gem_noc = { + .config = &hawi_gem_noc_regmap_config, + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm * const llclpi_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const llclpi_noc_nodes[] = { + [MASTER_LPIAON_NOC_LLCLPI_NOC] = &qnm_lpiaon_noc_llclpi_noc, + [SLAVE_LPASS_LPI_CC] = &qhs_lpi_cc, + [SLAVE_LLCC_ISLAND] = &qns_lb, + [SLAVE_SERVICE_LLCLPI_NOC] = &srvc_llclpi_noc, + [SLAVE_SERVICE_LLCLPI_NOC_CHIPCX] = &srvc_llclpi_noc_chipcx, +}; + +static const struct regmap_config hawi_llclpi_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_llclpi_noc = { + .config = &hawi_llclpi_noc_regmap_config, + .nodes = llclpi_noc_nodes, + .num_nodes = ARRAY_SIZE(llclpi_noc_nodes), + .bcms = llclpi_noc_bcms, + .num_bcms = ARRAY_SIZE(llclpi_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct regmap_config hawi_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_lpass_ag_noc = { + .config = &hawi_lpass_ag_noc_regmap_config, + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LLCLPI_NOC] = &qns_llc_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct regmap_config hawi_lpass_lpiaon_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x19080, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_lpass_lpiaon_noc = { + .config = &hawi_lpass_lpiaon_noc_regmap_config, + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qnm_lpinoc_dsp_qns4m, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct regmap_config hawi_lpass_lpicx_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x46080, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_lpass_lpicx_noc = { + .config = &hawi_lpass_lpicx_noc_regmap_config, + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_mc5, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [MASTER_DDR_RT] = &ddr_rt_mc, + [SLAVE_EBI1] = &ebi, + [SLAVE_DDR_RT] = &ddr_rt_slave, +}; + +static const struct qcom_icc_desc hawi_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_MDSS_DCP] = &qnm_mdss_dcp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, +}; + +static const struct regmap_config hawi_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5f800, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_mmss_noc = { + .config = &hawi_mmss_noc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qnm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct regmap_config hawi_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x21280, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_nsp_noc = { + .config = &hawi_nsp_noc_regmap_config, + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn3, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie, + [MASTER_PCIE_1] = &xm_pcie_g4x1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_gemnoc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct regmap_config hawi_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x12400, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_pcie_anoc = { + .config = &hawi_pcie_anoc_regmap_config, + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const stdst_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const stdst_cfg_nodes[] = { + [MASTER_CFG_CENTER] = &qsm_cfg_center, + [MASTER_CFG_EAST] = &qsm_cfg_east, + [MASTER_CFG_MM] = &qsm_cfg_mm, + [MASTER_CFG_NORTH] = &qsm_cfg_north, + [MASTER_CFG_SOUTH] = &qsm_cfg_south, + [MASTER_CFG_SOUTHWEST] = &qsm_cfg_southwest, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_CFG] = &qhs_crypto_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_IRIS_CFG] = &qhs_iris_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie_g4x1_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_QUP_3] = &qhs_qup3, + [SLAVE_QUP_4] = &qhs_qup4, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_CFG] = &qss_qdss_cfg, + [SLAVE_QDSS_STM] = &qss_qdss_stm, + [SLAVE_TCSR] = &qss_tcsr, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct regmap_config hawi_stdst_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xb1000, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_stdst_cfg = { + .config = &hawi_stdst_cfg_regmap_config, + .nodes = stdst_cfg_nodes, + .num_nodes = ARRAY_SIZE(stdst_cfg_nodes), + .bcms = stdst_cfg_bcms, + .num_bcms = ARRAY_SIZE(stdst_cfg_bcms), +}; + +static struct qcom_icc_node * const stdst_main_nodes[] = { + [MASTER_CNOC_STARDUST] = &qsm_cnoc_main, + [SLAVE_STARDUST_CENTER_CFG] = &qss_stdst_center_cfg, + [SLAVE_STARDUST_EAST_CFG] = &qss_stdst_east_cfg, + [SLAVE_STARDUST_MM_CFG] = &qss_stdst_mm_cfg, + [SLAVE_STARDUST_NORTH_CFG] = &qss_stdst_north_cfg, + [SLAVE_STARDUST_SOUTH_CFG] = &qss_stdst_south_cfg, + [SLAVE_STARDUST_SOUTHWEST_CFG] = &qss_stdst_southwest_cfg, +}; + +static const struct regmap_config hawi_stdst_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_stdst_main = { + .config = &hawi_stdst_main_regmap_config, + .nodes = stdst_main_nodes, + .num_nodes = ARRAY_SIZE(stdst_main_nodes), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre_noc, + [MASTER_APSS_NOC] = &qnm_apss_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct regmap_config hawi_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20080, + .fast_io = true, +}; + +static const struct qcom_icc_desc hawi_system_noc = { + .config = &hawi_system_noc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,hawi-aggre1-noc", .data = &hawi_aggre1_noc }, + { .compatible = "qcom,hawi-clk-virt", .data = &hawi_clk_virt }, + { .compatible = "qcom,hawi-cnoc-main", .data = &hawi_cnoc_main }, + { .compatible = "qcom,hawi-gem-noc", .data = &hawi_gem_noc }, + { .compatible = "qcom,hawi-llclpi-noc", .data = &hawi_llclpi_noc }, + { .compatible = "qcom,hawi-lpass-ag-noc", .data = &hawi_lpass_ag_noc }, + { .compatible = "qcom,hawi-lpass-lpiaon-noc", .data = &hawi_lpass_lpiaon_noc }, + { .compatible = "qcom,hawi-lpass-lpicx-noc", .data = &hawi_lpass_lpicx_noc }, + { .compatible = "qcom,hawi-mc-virt", .data = &hawi_mc_virt }, + { .compatible = "qcom,hawi-mmss-noc", .data = &hawi_mmss_noc }, + { .compatible = "qcom,hawi-nsp-noc", .data = &hawi_nsp_noc }, + { .compatible = "qcom,hawi-pcie-anoc", .data = &hawi_pcie_anoc }, + { .compatible = "qcom,hawi-stdst-cfg", .data = &hawi_stdst_cfg }, + { .compatible = "qcom,hawi-stdst-main", .data = &hawi_stdst_main }, + { .compatible = "qcom,hawi-system-noc", .data = &hawi_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-hawi", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Qualcomm Hawi NoC driver"); +MODULE_LICENSE("GPL"); From c5bad4fa2d5dfd8c25140051a9807eba387a19b8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 15:26:29 -0700 Subject: [PATCH 1474/5214] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and MOV/POP SS or STI blocking is active into the exception injection code so that KVM fixes up the VMCS for all injected #DBs, not only those that are reflected back into the guest after #DB interception. E.g. if KVM queues a #DB in the emulator, or more importantly if userspace does save/restore exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to avoid the VM-Entry consistency check. Opportunistically update the wording of the comment to describe the behavior as a workaround of flawed CPU behavior/architecture, to make it clear that the *only* thing KVM is doing is fudging around a consistency check. Per the SDM: There are no pending debug exceptions after VM entry if any of the following are true: * The VM entry is vectoring with one of the following interruption types: external interrupt, non-maskable interrupt (NMI), hardware exception, or privileged software exception. I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest- visible behavior. Fixes: b9bed78e2fa9 ("KVM: VMX: Set vmcs.PENDING_DBG.BS on #DB in STI/MOVSS blocking shadow") Cc: stable@vger.kernel.org Reported-by: Hou Wenlong Closes: https://lore.kernel.org/all/b1a294bc9ed4dae532474a5dc6c8cb6e5962de7c.1757416809.git.houwenlong.hwl@antgroup.com Reviewed-by: Hou Wenlong Link: https://patch.msgid.link/20260515222638.1949982-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d81b22359918..cf9f2f55f569 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -1909,6 +1909,24 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) u32 intr_info = ex->vector | INTR_INFO_VALID_MASK; struct vcpu_vmx *vmx = to_vmx(vcpu); + /* + * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI + * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to + * prevent a false positive from VM-Entry consistency check. VM-Entry + * asserts that a single-step #DB _must_ be pending in this scenario, + * as the previous instruction cannot have toggled RFLAGS.TF 0=>1 + * (because STI and POP/MOV don't modify RFLAGS), therefore the one + * instruction delay when activating single-step breakpoints must have + * already expired. However, the CPU isn't smart enough to peek at + * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there + * is indeed a #DB pending/imminent. + */ + if (ex->vector == DB_VECTOR && + (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && + vmx_get_interrupt_shadow(vcpu)) + vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, + vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); + kvm_deliver_exception_payload(vcpu, ex); if (ex->has_error_code) { @@ -5485,26 +5503,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) * avoid single-step #DB and MTF updates, as ICEBP is * higher priority. Note, skipping ICEBP still clears * STI and MOVSS blocking. - * - * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS - * if single-step is enabled in RFLAGS and STI or MOVSS - * blocking is active, as the CPU doesn't set the bit - * on VM-Exit due to #DB interception. VM-Entry has a - * consistency check that a single-step #DB is pending - * in this scenario as the previous instruction cannot - * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV - * don't modify RFLAGS), therefore the one instruction - * delay when activating single-step breakpoints must - * have already expired. Note, the CPU sets/clears BS - * as appropriate for all other VM-Exits types. */ if (is_icebp(intr_info)) WARN_ON(!skip_emulated_instruction(vcpu)); - else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && - (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & - (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS))) - vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, - vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); return 1; From 17afe9b750f2aba44ea398edda8c0740e438b7cf Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:30 -0700 Subject: [PATCH 1475/5214] KVM: x86: Capture "struct x86_exception" in inject_emulated_exception() As all callers in inject_emulated_exception() use "struct x86_exception" directly, capture it locally instead of using the context. No functional change intended. Suggested-by: Sean Christopherson Signed-off-by: Hou Wenlong Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260515222638.1949982-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b01f9a4d3363..6934c0d01419 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8979,15 +8979,14 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask) static void inject_emulated_exception(struct kvm_vcpu *vcpu) { - struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; + struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception; - if (ctxt->exception.vector == PF_VECTOR) - kvm_inject_emulated_page_fault(vcpu, &ctxt->exception); - else if (ctxt->exception.error_code_valid) - kvm_queue_exception_e(vcpu, ctxt->exception.vector, - ctxt->exception.error_code); + if (ex->vector == PF_VECTOR) + kvm_inject_emulated_page_fault(vcpu, ex); + else if (ex->error_code_valid) + kvm_queue_exception_e(vcpu, ex->vector, ex->error_code); else - kvm_queue_exception(vcpu, ctxt->exception.vector); + kvm_queue_exception(vcpu, ex->vector); } static struct x86_emulate_ctxt *alloc_emulate_ctxt(struct kvm_vcpu *vcpu) From dfc1c4687e9cb33a53536a0cb7fc4011c419dd85 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:31 -0700 Subject: [PATCH 1476/5214] KVM: x86: Set guest DR6 by kvm_queue_exception_p() in instruction emulation Record DR6 in emulate_db() and use kvm_queue_exception_p() to set DR6 instead of directly using kvm_set_dr6() in emulation, i.e. rely on the standard exception path to set DR6 via kvm_deliver_exception_payload(). This keeps the handling of DR6 during #DB injection consistent with other code paths. No functional change intended. Signed-off-by: Hou Wenlong [sean: fix e vs. p goof, add kvm_inject_emulated_db() right away] Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260515222638.1949982-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/emulate.c | 14 ++++---------- arch/x86/kvm/kvm_emulate.h | 6 +++++- arch/x86/kvm/x86.c | 10 +++++++++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index c8c6cc0406d6..510244555a74 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -540,8 +540,9 @@ static int emulate_exception(struct x86_emulate_ctxt *ctxt, int vec, return X86EMUL_PROPAGATE_FAULT; } -static int emulate_db(struct x86_emulate_ctxt *ctxt) +static int emulate_db(struct x86_emulate_ctxt *ctxt, unsigned long dr6) { + ctxt->exception.dr6 = dr6; return emulate_exception(ctxt, DB_VECTOR, 0, false); } @@ -3847,15 +3848,8 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt) if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5)) return emulate_ud(ctxt); - if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD) { - ulong dr6; - - dr6 = ctxt->ops->get_dr(ctxt, 6); - dr6 &= ~DR_TRAP_BITS; - dr6 |= DR6_BD | DR6_ACTIVE_LOW; - ctxt->ops->set_dr(ctxt, 6, dr6); - return emulate_db(ctxt); - } + if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD) + return emulate_db(ctxt, DR6_BD); return X86EMUL_CONTINUE; } diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index 0abff36d0994..bb2a2aee0e13 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -24,7 +24,11 @@ struct x86_exception { bool error_code_valid; u16 error_code; bool nested_page_fault; - u64 address; /* cr2 or nested page fault gpa */ + union { + u64 address; /* cr2 or nested page fault gpa */ + unsigned long dr6; + u64 payload; + }; u8 async_page_fault; unsigned long exit_qualification; }; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6934c0d01419..af6f3b5d2f3d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8977,11 +8977,18 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask) } } +static void kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6) +{ + kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); +} + static void inject_emulated_exception(struct kvm_vcpu *vcpu) { struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception; - if (ex->vector == PF_VECTOR) + if (ex->vector == DB_VECTOR) + kvm_inject_emulated_db(vcpu, ex->dr6); + else if (ex->vector == PF_VECTOR) kvm_inject_emulated_page_fault(vcpu, ex); else if (ex->error_code_valid) kvm_queue_exception_e(vcpu, ex->vector, ex->error_code); @@ -9026,6 +9033,7 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu) ctxt->interruptibility = 0; ctxt->have_exception = false; ctxt->exception.vector = -1; + ctxt->exception.payload = 0; ctxt->perm_ok = false; init_decode_cache(ctxt); From 51543660c521a30450bcbf74956f14b591c4a58c Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:32 -0700 Subject: [PATCH 1477/5214] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when emulating MOV DR (in emulator) When emulating a MOV DR instruction, honor KVM_GUESTDBG_USE_HW_BP when checking DR7.GD, and if there is a general-detect #DB, route it to host userspace as appropriate. Consulting only the guest's actual DR7 causes KVM to fail to report a DR access to userspace (assuming the guest itself doesn't have DR7.GD=1). Fixes: ae675ef01cd8 ("KVM: x86: Wire-up hardware breakpoints for guest debugging") Suggested-by: Lai Jiangshan Signed-off-by: Hou Wenlong [sean: only expose effective DR7 to emulator, massage changelog] Link: https://patch.msgid.link/20260515222638.1949982-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/emulate.c | 2 +- arch/x86/kvm/kvm_emulate.h | 1 + arch/x86/kvm/x86.c | 41 ++++++++++++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 510244555a74..dd0e19af4997 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3848,7 +3848,7 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt) if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5)) return emulate_ud(ctxt); - if (ctxt->ops->get_dr(ctxt, 7) & DR7_GD) + if (ctxt->ops->get_effective_dr7(ctxt) & DR7_GD) return emulate_db(ctxt, DR6_BD); return X86EMUL_CONTINUE; diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index bb2a2aee0e13..ee5004beeb4d 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -215,6 +215,7 @@ struct x86_emulate_ops { ulong (*get_cr)(struct x86_emulate_ctxt *ctxt, int cr); int (*set_cr)(struct x86_emulate_ctxt *ctxt, int cr, ulong val); int (*cpl)(struct x86_emulate_ctxt *ctxt); + ulong (*get_effective_dr7)(struct x86_emulate_ctxt *ctxt); ulong (*get_dr)(struct x86_emulate_ctxt *ctxt, int dr); int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value); int (*set_msr_with_filter)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index af6f3b5d2f3d..42ab2fd0cb9f 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1606,6 +1606,14 @@ unsigned long kvm_get_dr(struct kvm_vcpu *vcpu, int dr) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_get_dr); +static unsigned long kvm_get_effective_dr7(struct kvm_vcpu *vcpu) +{ + if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) + return vcpu->arch.guest_debug_dr7; + + return vcpu->arch.dr7; +} + int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu) { u32 pmc = kvm_rcx_read(vcpu); @@ -8549,6 +8557,11 @@ static void emulator_wbinvd(struct x86_emulate_ctxt *ctxt) kvm_emulate_wbinvd_noskip(emul_to_vcpu(ctxt)); } +static unsigned long emulator_get_effective_dr7(struct x86_emulate_ctxt *ctxt) +{ + return kvm_get_effective_dr7(emul_to_vcpu(ctxt)); +} + static unsigned long emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr) { return kvm_get_dr(emul_to_vcpu(ctxt), dr); @@ -8931,6 +8944,7 @@ static const struct x86_emulate_ops emulate_ops = { .get_cr = emulator_get_cr, .set_cr = emulator_set_cr, .cpl = emulator_get_cpl, + .get_effective_dr7 = emulator_get_effective_dr7, .get_dr = emulator_get_dr, .set_dr = emulator_set_dr, .set_msr_with_filter = emulator_set_msr_with_filter, @@ -8977,23 +8991,36 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask) } } -static void kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6) +static int kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6) { + struct kvm_run *kvm_run = vcpu->run; + + if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) { + kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW; + kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu); + kvm_run->debug.arch.exception = DB_VECTOR; + kvm_run->exit_reason = KVM_EXIT_DEBUG; + return 0; + } + kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); + return 1; } -static void inject_emulated_exception(struct kvm_vcpu *vcpu) +static int inject_emulated_exception(struct kvm_vcpu *vcpu) { struct x86_exception *ex = &vcpu->arch.emulate_ctxt->exception; if (ex->vector == DB_VECTOR) - kvm_inject_emulated_db(vcpu, ex->dr6); - else if (ex->vector == PF_VECTOR) + return kvm_inject_emulated_db(vcpu, ex->dr6); + + if (ex->vector == PF_VECTOR) kvm_inject_emulated_page_fault(vcpu, ex); else if (ex->error_code_valid) kvm_queue_exception_e(vcpu, ex->vector, ex->error_code); else kvm_queue_exception(vcpu, ex->vector); + return 1; } static struct x86_emulate_ctxt *alloc_emulate_ctxt(struct kvm_vcpu *vcpu) @@ -9502,8 +9529,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, */ WARN_ON_ONCE(ctxt->exception.vector == UD_VECTOR || exception_type(ctxt->exception.vector) == EXCPT_TRAP); - inject_emulated_exception(vcpu); - return 1; + return inject_emulated_exception(vcpu); } return handle_emulation_failure(vcpu, emulation_type); } @@ -9598,8 +9624,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, if (ctxt->have_exception) { WARN_ON_ONCE(vcpu->mmio_needed && !vcpu->mmio_is_write); vcpu->mmio_needed = false; - r = 1; - inject_emulated_exception(vcpu); + r = inject_emulated_exception(vcpu); } else if (vcpu->arch.pio.count) { if (!vcpu->arch.pio.in) { /* FIXME: return into emulator if single-stepping. */ From 00b6520669bc7a1d7df1e138a555e9ef8c314233 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:33 -0700 Subject: [PATCH 1478/5214] KVM: x86: Honor KVM_GUESTDBG_USE_HW_BP when checking for code breakpoints in emulation When KVM_GUESTDBG_USE_HW_BP is enabled, i.e. userspace is usurping the guest's hardware debug registers, the guest's effective breakpoints are controlled by userspace rather than by the guest itself. Honor the KVM_GUESTDBG_USE_HW_BP behavior when handling code #DBs in the emulator so that userspace (and the guest) gets consistent behavior for code #DBs regardless of whether an instruction is executed natively or emulated by KVM. To aid in userspace debug, don't treat code breakpoints as inhibited if KVM_GUESTDBG_USE_HW_BP is enabled as accurately emulating x86 architecture is obviously a non-goal of guest-debug. Fixes: 4a1e10d5b5d8 ("KVM: x86: handle hardware breakpoints during emulation") Signed-off-by: Hou Wenlong [sean: massage changelog] Link: https://patch.msgid.link/20260515222638.1949982-6-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 42ab2fd0cb9f..f50151d11fa2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9320,6 +9320,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_skip_emulated_instruction); static bool kvm_is_code_breakpoint_inhibited(struct kvm_vcpu *vcpu) { + if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) + return false; + if (kvm_get_rflags(vcpu) & X86_EFLAGS_RF) return true; @@ -9336,6 +9339,8 @@ static bool kvm_is_code_breakpoint_inhibited(struct kvm_vcpu *vcpu) static bool kvm_vcpu_check_code_breakpoint(struct kvm_vcpu *vcpu, int emulation_type, int *r) { + unsigned long dr7 = kvm_get_effective_dr7(vcpu); + WARN_ON_ONCE(emulation_type & EMULTYPE_NO_DECODE); /* @@ -9356,34 +9361,14 @@ static bool kvm_vcpu_check_code_breakpoint(struct kvm_vcpu *vcpu, EMULTYPE_TRAP_UD | EMULTYPE_VMWARE_GP | EMULTYPE_PF)) return false; - if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) && - (vcpu->arch.guest_debug_dr7 & DR7_BP_EN_MASK)) { - struct kvm_run *kvm_run = vcpu->run; - unsigned long eip = kvm_get_linear_rip(vcpu); - u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0, - vcpu->arch.guest_debug_dr7, - vcpu->arch.eff_db); - - if (dr6 != 0) { - kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW; - kvm_run->debug.arch.pc = eip; - kvm_run->debug.arch.exception = DB_VECTOR; - kvm_run->exit_reason = KVM_EXIT_DEBUG; - *r = 0; - return true; - } - } - - if (unlikely(vcpu->arch.dr7 & DR7_BP_EN_MASK) && + if (unlikely(dr7 & DR7_BP_EN_MASK) && !kvm_is_code_breakpoint_inhibited(vcpu)) { unsigned long eip = kvm_get_linear_rip(vcpu); - u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0, - vcpu->arch.dr7, - vcpu->arch.db); + u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0, dr7, + vcpu->arch.eff_db); - if (dr6 != 0) { - kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); - *r = 1; + if (dr6) { + *r = kvm_inject_emulated_db(vcpu, dr6); return true; } } From 6d79302b0834a8b162bfa551e7e0e22647676fb5 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:34 -0700 Subject: [PATCH 1479/5214] KVM: x86: Move KVM_GUESTDBG_SINGLESTEP handling into kvm_inject_emulated_db() Move KVM_GUESTDBG_SINGLESTEP handling from kvm_vcpu_do_singlestep() into kvm_inject_emulated_db() to dedup the USE_HW_BP vs. SINGLESTEP logic, and to allow for removing kvm_vcpu_do_singlestep() entirely. No functional change intended. Suggested-by: Lai Jiangshan Signed-off-by: Hou Wenlong [sean: massage changelog] Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260515222638.1949982-7-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f50151d11fa2..c676863f7e11 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8995,7 +8995,7 @@ static int kvm_inject_emulated_db(struct kvm_vcpu *vcpu, unsigned long dr6) { struct kvm_run *kvm_run = vcpu->run; - if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) { + if (vcpu->guest_debug & (KVM_GUESTDBG_USE_HW_BP | KVM_GUESTDBG_SINGLESTEP)) { kvm_run->debug.arch.dr6 = dr6 | DR6_ACTIVE_LOW; kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu); kvm_run->debug.arch.exception = DB_VECTOR; @@ -9280,17 +9280,7 @@ static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7, static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu) { - struct kvm_run *kvm_run = vcpu->run; - - if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) { - kvm_run->debug.arch.dr6 = DR6_BS | DR6_ACTIVE_LOW; - kvm_run->debug.arch.pc = kvm_get_linear_rip(vcpu); - kvm_run->debug.arch.exception = DB_VECTOR; - kvm_run->exit_reason = KVM_EXIT_DEBUG; - return 0; - } - kvm_queue_exception_p(vcpu, DB_VECTOR, DR6_BS); - return 1; + return kvm_inject_emulated_db(vcpu, DR6_BS); } int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) From a1e7aeb1be11386883f1c1928acde32dceaa9898 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 15:26:35 -0700 Subject: [PATCH 1480/5214] KVM: x86: Drop kvm_vcpu_do_singlestep() now that it's been gutted Now that all of kvm_vcpu_do_singlestep()'s previously-unique functionality has been moved into kvm_inject_emulated_db(), drop the one-line wrapper. No functional change intended. Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260515222638.1949982-8-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c676863f7e11..9c52c11ba5f5 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -133,7 +133,6 @@ static void process_nmi(struct kvm_vcpu *vcpu); static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); static void store_regs(struct kvm_vcpu *vcpu); static int sync_regs(struct kvm_vcpu *vcpu); -static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu); static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); static void __get_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); @@ -9278,11 +9277,6 @@ static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7, return dr6; } -static int kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu) -{ - return kvm_inject_emulated_db(vcpu, DR6_BS); -} - int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) { unsigned long rflags = kvm_x86_call(get_rflags)(vcpu); @@ -9303,7 +9297,7 @@ int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) * that sets the TF flag". */ if (unlikely(rflags & X86_EFLAGS_TF)) - r = kvm_vcpu_do_singlestep(vcpu); + r = kvm_inject_emulated_db(vcpu, DR6_BS); return r; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_skip_emulated_instruction); @@ -9642,7 +9636,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, kvm_pmu_branch_retired(vcpu); kvm_rip_write(vcpu, ctxt->eip); if (r && (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP))) - r = kvm_vcpu_do_singlestep(vcpu); + r = kvm_inject_emulated_db(vcpu, DR6_BS); kvm_x86_call(update_emulated_instruction)(vcpu); __kvm_set_rflags(vcpu, ctxt->eflags); } From 35c08038cbdc14d9d638c32d5663e043d607a314 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 15:26:36 -0700 Subject: [PATCH 1481/5214] KVM: selftests: Add all (known) EFLAGS bit definitions Add #defines for all known EFLAGS bit, e.g. so that tests can use things like EFLAGS.TF to validate single-stepping behavior. Opportunistically use X86_EFLAGS_FIXED instead of an open-coded equivalent when stuffing initial vCPU state. No functional change intended. Link: https://patch.msgid.link/20260515222638.1949982-9-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/include/x86/processor.h | 19 ++++++++++++++++++- .../testing/selftests/kvm/lib/x86/processor.c | 2 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 77f576ee7789..851ffcd3340c 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -38,7 +38,24 @@ extern u64 guest_tsc_khz; const char *ex_str(int vector); -#define X86_EFLAGS_FIXED (1u << 1) +#define X86_EFLAGS_CF BIT(0) /* Carry Flag */ +#define X86_EFLAGS_FIXED BIT(1) /* Bit 1 - always on */ +#define X86_EFLAGS_PF BIT(2) /* Parity Flag */ +#define X86_EFLAGS_AF BIT(4) /* Auxiliary carry Flag */ +#define X86_EFLAGS_ZF BIT(6) /* Zero Flag */ +#define X86_EFLAGS_SF BIT(7) /* Sign Flag */ +#define X86_EFLAGS_TF BIT(8) /* Trap Flag */ +#define X86_EFLAGS_IF BIT(9) /* Interrupt Flag */ +#define X86_EFLAGS_DF BIT(10) /* Direction Flag */ +#define X86_EFLAGS_OF BIT(11) /* Overflow Flag */ +#define X86_EFLAGS_IOPL BIT(12) /* I/O Privilege Level (2 bits) */ +#define X86_EFLAGS_NT BIT(14) /* Nested Task */ +#define X86_EFLAGS_RF BIT(16) /* Resume Flag */ +#define X86_EFLAGS_VM BIT(17) /* Virtual Mode */ +#define X86_EFLAGS_AC BIT(18) /* Alignment Check/Access Control */ +#define X86_EFLAGS_VIF BIT(19) /* Virtual Interrupt Flag */ +#define X86_EFLAGS_VIP BIT(20) /* Virtual Interrupt Pending */ +#define X86_EFLAGS_ID BIT(21) /* CPUID detection */ #define X86_CR4_VME (1ul << 0) #define X86_CR4_PVI (1ul << 1) diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index b51467d70f6e..4ca48de7a926 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -848,7 +848,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) /* Setup guest general purpose registers */ vcpu_regs_get(vcpu, ®s); - regs.rflags = regs.rflags | 0x2; + regs.rflags = regs.rflags | X86_EFLAGS_FIXED; regs.rsp = stack_gva; vcpu_regs_set(vcpu, ®s); diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index 67642759e4a0..7c10ba6e6fb4 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -360,7 +360,7 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp) vmwrite(GUEST_DR7, 0x400); vmwrite(GUEST_RSP, (u64)rsp); vmwrite(GUEST_RIP, (u64)rip); - vmwrite(GUEST_RFLAGS, 2); + vmwrite(GUEST_RFLAGS, X86_EFLAGS_FIXED); vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0); vmwrite(GUEST_SYSENTER_ESP, vmreadz(HOST_IA32_SYSENTER_ESP)); vmwrite(GUEST_SYSENTER_EIP, vmreadz(HOST_IA32_SYSENTER_EIP)); From 063a22451fd9541b43fcb862a8ecdf104fa2ebe7 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:37 -0700 Subject: [PATCH 1482/5214] KVM: selftests: Verify guest debug DR7.GD checking during instruction emulation Similar to the global disable test case in x86's debug_regs test, use 'KVM_FEP' to trigger instruction emulation in order to verify the guest debug DR7.GD checking during instruction emulation. Signed-off-by: Hou Wenlong Link: https://patch.msgid.link/20260515222638.1949982-10-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/debug_regs.c | 23 +++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c index 0dfaf03cd0a0..13aaa7f3f8d9 100644 --- a/tools/testing/selftests/kvm/x86/debug_regs.c +++ b/tools/testing/selftests/kvm/x86/debug_regs.c @@ -19,6 +19,7 @@ u32 guest_value; extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; +extern unsigned char fep_bd_start; static void guest_code(void) { @@ -64,6 +65,10 @@ static void guest_code(void) /* DR6.BD test */ asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); + + if (is_forced_emulation_enabled) + asm volatile(KVM_FEP "fep_bd_start: mov %%dr0, %%rax" : : : "rax"); + GUEST_DONE(); } @@ -185,7 +190,7 @@ int main(void) target_dr6); } - /* Finally test global disable */ + /* test global disable */ memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; debug.arch.debugreg[7] = 0x400 | DR7_GD; @@ -202,6 +207,22 @@ int main(void) run->debug.arch.pc, target_rip, run->debug.arch.dr6, target_dr6); + /* test global disable in emulation */ + if (is_forced_emulation_enabled) { + /* Skip the 3-bytes "mov dr0" */ + vcpu_skip_insn(vcpu, 3); + vcpu_run(vcpu); + TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && + run->debug.arch.exception == DB_VECTOR && + run->debug.arch.pc == CAST_TO_RIP(fep_bd_start) && + run->debug.arch.dr6 == target_dr6, + "DR7.GD: exit %d exception %d rip 0x%llx " + "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", + run->exit_reason, run->debug.arch.exception, + run->debug.arch.pc, CAST_TO_RIP(fep_bd_start), + run->debug.arch.dr6, target_dr6); + } + /* Disable all debug controls, run to the end */ memset(&debug, 0, sizeof(debug)); vcpu_guest_debug_set(vcpu, &debug); From b8284279b7325503739b5e0e86c9f08caaeeccf4 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 15 May 2026 15:26:38 -0700 Subject: [PATCH 1483/5214] KVM: selftests: Verify VMX's GUEST_PENDING_DBG_EXCEPTIONS.BS Consistency Check In x86's debug_regs test, add a test case to cover the scenario where a single-step #DB occurs in an STI-shadow, in which case KVM needs to stuff vmcs.GUEST_PENDING_DBG_EXCEPTIONS.BS in order to satisfy a flawed VM-Entry Consistency Check. Wire up an IRQ handler to gain a bit of bonus coverage, as the subsequent IRET from the #DB sets RFLAGS.IF, but *without* STI-blocking, and so the pending IRQ is expected on the instruction immediately following STI. Signed-off-by: Hou Wenlong [sean: expect the IRQ on the CLI, and explain why] Link: https://patch.msgid.link/20260515222638.1949982-11-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/debug_regs.c | 69 ++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c index 13aaa7f3f8d9..2a2ef3e179ff 100644 --- a/tools/testing/selftests/kvm/x86/debug_regs.c +++ b/tools/testing/selftests/kvm/x86/debug_regs.c @@ -15,11 +15,51 @@ #define IRQ_VECTOR 0xAA +#define CAST_TO_RIP(v) ((unsigned long long)&(v)) + /* For testing data access debug BP */ u32 guest_value; extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; -extern unsigned char fep_bd_start; +extern unsigned char fep_bd_start, fep_sti_start, fep_sti_end; + +static int irqs_received; + +static void guest_db_handler(struct ex_regs *regs) +{ + static int count; + unsigned long target_rips[2] = { + CAST_TO_RIP(fep_sti_start), + CAST_TO_RIP(fep_sti_end), + }; + + __GUEST_ASSERT(regs->rip == target_rips[count], + "STI[%u]: unexpected rip 0x%lx (should be 0x%lx)", + count, regs->rip, target_rips[count]); + regs->rflags &= ~X86_EFLAGS_TF; + count++; +} + +static void guest_irq_handler(struct ex_regs *regs) +{ + /* + * The pending IRQ should finally be take when KVM_GUESTDBG_BLOCKIRQ is + * cleared and IRQs are enabled. Note, the IRQ is expected to arrive + * on the instruction immediately after STI, even though its in an STI + * shadow. Because the next instruction has a coincident #DB, and #DBs + * are not subject to STI-blocking, the #DB will push RFLAGS.IF=1 on + * the stack, and the eventual IRET will unmask IRQs and obliterate the + * STI shadow in the process. + */ + unsigned long target_rip = CAST_TO_RIP(fep_sti_start); + + __GUEST_ASSERT(regs->rip == target_rip, + "IRQ: unexpected rip 0x%lx (should be 0x%lx)", + regs->rip, target_rip); + + irqs_received++; + x2apic_write_reg(APIC_EOI, 0); +} static void guest_code(void) { @@ -66,14 +106,32 @@ static void guest_code(void) /* DR6.BD test */ asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); - if (is_forced_emulation_enabled) + /* + * Note, the IRET from the #DB that occurs in the below STI-shadow will + * unmask IRQs, i.e. the pending interrupt will be delivered after #DB + * handling, on the CLI! + */ + if (is_forced_emulation_enabled) { asm volatile(KVM_FEP "fep_bd_start: mov %%dr0, %%rax" : : : "rax"); + /* pending debug exceptions for emulation */ + asm volatile("pushf\n\t" + "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t" + "popf\n\t" + "sti\n\t" + "fep_sti_start:" + "cli\n\t" + "pushf\n\t" + "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t" + "popf\n\t" + KVM_FEP "sti\n\t" + "fep_sti_end:" + "cli\n\t"); + GUEST_ASSERT(irqs_received == 1); + } GUEST_DONE(); } -#define CAST_TO_RIP(v) ((unsigned long long)&(v)) - static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) { struct kvm_regs regs; @@ -227,6 +285,9 @@ int main(void) memset(&debug, 0, sizeof(debug)); vcpu_guest_debug_set(vcpu, &debug); + vm_install_exception_handler(vm, DB_VECTOR, guest_db_handler); + vm_install_exception_handler(vm, IRQ_VECTOR, guest_irq_handler); + vcpu_run(vcpu); TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); cmd = get_ucall(vcpu, &uc); From 5aeb1e35a4211e54803fb83de8189d6a8346450b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 20 May 2026 07:42:43 -0700 Subject: [PATCH 1484/5214] KVM: x86: Remove unused X86EMUL_MODE_HOST define Drop the emulator's X86EMUL_MODE_HOST #defines, as they have never been used by KVM (they were likely copy+pasted from Xen's emulator). No functional change intended. Fixes: 6aa8b732ca01 ("[PATCH] kvm: userspace interface") Link: https://patch.msgid.link/20260520144244.2657106-1-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/kvm_emulate.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index ee5004beeb4d..72aece9ef575 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -525,13 +525,6 @@ enum x86_intercept { nr_x86_intercepts }; -/* Host execution mode. */ -#if defined(CONFIG_X86_32) -#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT32 -#elif defined(CONFIG_X86_64) -#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT64 -#endif - int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int emulation_type); bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt); #define EMULATION_FAILED -1 From 3839a8eedde57a9f5b869025ce2626a7a37f5ccb Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Mon, 20 Apr 2026 15:42:36 +0530 Subject: [PATCH 1485/5214] tracing: Remove redundant IS_ERR() check in trace_pipe_open() in trace_pipe_open() already check the IS_ERR(iter) and return early on error,so iter after will be valid and it is safe to return 0 at end. Link: https://patch.msgid.link/20260420101236.223919-1-yashsuthar983@gmail.com Signed-off-by: Yash Suthar Signed-off-by: Steven Rostedt --- kernel/trace/trace_remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index d6c3f94d67cd..2a6cc000ec98 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -602,7 +602,7 @@ static int trace_pipe_open(struct inode *inode, struct file *filp) filp->private_data = iter; - return IS_ERR(iter) ? PTR_ERR(iter) : 0; + return 0; } static int trace_pipe_release(struct inode *inode, struct file *filp) From ae3197ee07cc8ec66261d891b2b5bf3879e7b852 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Thu, 9 Apr 2026 02:08:51 +0545 Subject: [PATCH 1486/5214] seq_buf: Export seq_buf_putmem_hex() and add KUnit tests The seq_buf KUnit suite does not exercise seq_buf_putmem_hex(). Add one test for the len > 8 chunking path and one overflow test where a later chunk no longer fits in the buffer. Export seq_buf_putmem_hex() as well so SEQ_BUF_KUNIT_TEST=m links cleanly. Without the export, modpost reports seq_buf_putmem_hex as undefined when seq_buf_kunit is built as a module. Cc: Andrew Morton Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: David Gow Link: https://patch.msgid.link/20260408202351.21829-1-shuvampandey1@gmail.com Acked-by: Steven Rostedt (Google) Signed-off-by: Shuvam Pandey Signed-off-by: Steven Rostedt --- lib/seq_buf.c | 1 + lib/tests/seq_buf_kunit.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/seq_buf.c b/lib/seq_buf.c index f3f3436d60a9..b59488fa8135 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -298,6 +298,7 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, } return 0; } +EXPORT_SYMBOL_GPL(seq_buf_putmem_hex); /** * seq_buf_path - copy a path into the sequence buffer diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c index 8a01579a978e..eb466386bbef 100644 --- a/lib/tests/seq_buf_kunit.c +++ b/lib/tests/seq_buf_kunit.c @@ -184,6 +184,38 @@ static void seq_buf_get_buf_commit_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); } +static void seq_buf_putmem_hex_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 24); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 0809 "; +#else + const char *expected = "0706050403020100 0908 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), 0); + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), strlen(expected)); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + +static void seq_buf_putmem_hex_overflow_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 20); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 "; +#else + const char *expected = "0706050403020100 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1); + KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 20); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_init_test), KUNIT_CASE(seq_buf_declare_test), @@ -194,6 +226,8 @@ static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_printf_test), KUNIT_CASE(seq_buf_printf_overflow_test), KUNIT_CASE(seq_buf_get_buf_commit_test), + KUNIT_CASE(seq_buf_putmem_hex_test), + KUNIT_CASE(seq_buf_putmem_hex_overflow_test), {} }; From f07883450eb14d1cf020b55d9f3a7ec5683bcd26 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 30 Apr 2026 12:33:50 +0800 Subject: [PATCH 1487/5214] tracing: Bound synthetic-field strings with seq_buf The synthetic field helpers build a prefixed synthetic variable name and a generated hist command in fixed MAX_FILTER_STR_VAL buffers. The current code appends those strings with raw strcat(), so long key lists, field names, or saved filters can run past the end of the staging buffers. Build both strings with seq_buf and propagate -E2BIG if either the synthetic variable name or the generated command exceeds MAX_FILTER_STR_VAL. This keeps the existing tracing-side limit while using the helper intended for bounded command construction. Cc: Mathieu Desnoyers Cc: Tom Zanussi Link: https://patch.msgid.link/20260430043350.57928-1-pengpeng@iscas.ac.cn Fixes: 02205a6752f2 ("tracing: Add support for 'field variables'") Acked-by: Masami Hiramatsu (Google) Reviewed-by: Tom Zanussi Signed-off-by: Pengpeng Hou [ sdr: Moved struct seq_buf *s for upside-down x-mas tree formatting ] Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_hist.c | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index eb2c2bc8bc3d..9701650c89b2 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -2967,13 +2968,22 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data, { struct hist_field *event_var; char *synthetic_name; + struct seq_buf s; synthetic_name = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); if (!synthetic_name) return ERR_PTR(-ENOMEM); - strcpy(synthetic_name, "synthetic_"); - strcat(synthetic_name, field_name); + seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL); + seq_buf_printf(&s, "synthetic_%s", field_name); + + /* Terminate synthetic_name with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(synthetic_name); + return ERR_PTR(-E2BIG); + } event_var = find_event_var(target_hist_data, system, event_name, synthetic_name); @@ -3019,6 +3029,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, struct hist_field *key_field; struct hist_field *event_var; char *saved_filter; + struct seq_buf s; char *cmd; int ret; @@ -3063,28 +3074,34 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, return ERR_PTR(-ENOMEM); } + seq_buf_init(&s, cmd, MAX_FILTER_STR_VAL); + /* Use the same keys as the compatible histogram */ - strcat(cmd, "keys="); + seq_buf_puts(&s, "keys="); for_each_hist_key_field(i, hist_data) { key_field = hist_data->fields[i]; if (!first) - strcat(cmd, ","); - strcat(cmd, key_field->field->name); + seq_buf_putc(&s, ','); + seq_buf_puts(&s, key_field->field->name); first = false; } /* Create the synthetic field variable specification */ - strcat(cmd, ":synthetic_"); - strcat(cmd, field_name); - strcat(cmd, "="); - strcat(cmd, field_name); + seq_buf_printf(&s, ":synthetic_%s=%s", field_name, field_name); /* Use the same filter as the compatible histogram */ saved_filter = find_trigger_filter(hist_data, file); - if (saved_filter) { - strcat(cmd, " if "); - strcat(cmd, saved_filter); + if (saved_filter) + seq_buf_printf(&s, " if %s", saved_filter); + + /* Terminate cmd with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(cmd); + kfree(var_hist); + return ERR_PTR(-E2BIG); } var_hist->cmd = kstrdup(cmd, GFP_KERNEL); From 9764e731ef6abacdfc00d914061d4d900e302670 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 30 Apr 2026 15:41:59 +0100 Subject: [PATCH 1488/5214] tracepoint: Add lockdep rcu_is_watching() check to trace_##name##_enabled() The trace_##name##_enabled() static call branch is used when work needs to be done for a tracepoint. It allows that work to be skipped when the tracepoint is not active and still uses the static_branch() of the tracepoint to keep performance. Tracepoints themselves require being called in "RCU watching" locations otherwise races can occur that corrupts things. In order to make sure lockdep triggers at tracepoint locations, the lockdep checks are added to the tracepoint calling location and trigger even if the tracepoint is not enabled. This is done because a poorly placed tracepoint may never be detected if it is never enabled when lockdep is enabled. As trace_##name##_enabled() also prevents the lockdep checks when the tracepoint is disabled add lockdep checks to that as well so that if one is placed in a location that RCU is not watching, it will trigger a lockdep splat even when the tracepoint is not enabled. Cc: Vineeth Pillai (Google) Cc: Mathieu Desnoyers Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: https://patch.msgid.link/20260430144159.10985-1-devnexen@gmail.com Signed-off-by: David Carlier [ Updated the change log ] Signed-off-by: Steven Rostedt --- include/linux/tracepoint.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 763eea4d80d8..c29fc57392bb 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -293,6 +293,10 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) static inline bool \ trace_##name##_enabled(void) \ { \ + if (IS_ENABLED(CONFIG_LOCKDEP)) { \ + WARN_ONCE(!rcu_is_watching(), \ + "RCU not watching for tracepoint"); \ + } \ return static_branch_unlikely(&__tracepoint_##name.key);\ } From 04fcbcf2dfe6a3fadc50b0f925c0b757386b82f8 Mon Sep 17 00:00:00 2001 From: Qian-Yu Lin Date: Sat, 2 May 2026 15:55:34 +0800 Subject: [PATCH 1489/5214] tracing: Remove local variable for argument detection from trace_printk() The trace_printk() macro uses a local variable _______STR to detect whether variadic arguments are present. This name can shadow outer variables. Replace the local variable with sizeof applied directly to the stringified arguments: if (sizeof __stringify((__VA_ARGS__)) > 3) This eliminates the shadowing risk entirely without introducing any additional includes or local variables. Verified with objdump on samples/trace_printk that all four cases branch correctly: __trace_bputs, __trace_puts, __trace_bprintk, and __trace_printk. Link: https://patch.msgid.link/20260502075535.34997-1-tiffany019230@gmail.com Suggested-by: David Laight Signed-off-by: Qian-Yu Lin Signed-off-by: Steven Rostedt --- include/linux/trace_printk.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/trace_printk.h b/include/linux/trace_printk.h index 2670ec7f4262..3d54f440dccf 100644 --- a/include/linux/trace_printk.h +++ b/include/linux/trace_printk.h @@ -86,8 +86,7 @@ do { \ #define trace_printk(fmt, ...) \ do { \ - char _______STR[] = __stringify((__VA_ARGS__)); \ - if (sizeof(_______STR) > 3) \ + if (sizeof __stringify((__VA_ARGS__)) > 3) \ do_trace_printk(fmt, ##__VA_ARGS__); \ else \ trace_puts(fmt); \ From 8a6e9af2fac8cf212f86cff282ae96f6c7d7ca18 Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Sat, 2 May 2026 23:17:41 +0530 Subject: [PATCH 1490/5214] tracing: Switch trace_recursion_record.c code over to use guard() Switch mutex_lock()/mutex_unlock() to guard(). also drop the ret local variable and return directly. Link: https://patch.msgid.link/20260502174741.39636-1-yashsuthar983@gmail.com Signed-off-by: Yash Suthar Signed-off-by: Steven Rostedt --- kernel/trace/trace_recursion_record.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace_recursion_record.c b/kernel/trace/trace_recursion_record.c index 784fe1fbb866..bac4bc844ccd 100644 --- a/kernel/trace/trace_recursion_record.c +++ b/kernel/trace/trace_recursion_record.c @@ -180,9 +180,8 @@ static const struct seq_operations recursed_function_seq_ops = { static int recursed_function_open(struct inode *inode, struct file *file) { - int ret = 0; + guard(mutex)(&recursed_function_lock); - mutex_lock(&recursed_function_lock); /* If this file was opened for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { /* disable updating records */ @@ -194,10 +193,9 @@ static int recursed_function_open(struct inode *inode, struct file *file) atomic_set(&nr_records, 0); } if (file->f_mode & FMODE_READ) - ret = seq_open(file, &recursed_function_seq_ops); - mutex_unlock(&recursed_function_lock); + return seq_open(file, &recursed_function_seq_ops); - return ret; + return 0; } static ssize_t recursed_function_write(struct file *file, From b3efa3b2bb72ec3e3a13f2cca8bd3e855eec539d Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 7 May 2026 10:09:06 +0200 Subject: [PATCH 1491/5214] tracefs: Fix typo in a comment of eventfs_callback() kerneldoc Fix a typo "evetnfs files" to "eventfs files" in a comment. Cc: Masami Hiramatsu Link: https://patch.msgid.link/20260507081041.885781-2-martin@kaiser.cx Signed-off-by: Martin Kaiser Signed-off-by: Steven Rostedt --- include/linux/tracefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h index d03f74658716..bc354d340046 100644 --- a/include/linux/tracefs.h +++ b/include/linux/tracefs.h @@ -30,7 +30,7 @@ struct eventfs_file; * @data: data to pass to the created file ops * @fops: the file operations of the created file * - * The evetnfs files are dynamically created. The struct eventfs_entry array + * The eventfs files are dynamically created. The struct eventfs_entry array * is passed to eventfs_create_dir() or eventfs_create_events_dir() that will * be used to create the files within those directories. When a lookup * or access to a file within the directory is made, the struct eventfs_entry From 2dad375e5f21223c5ed51dabd9ae22e35db1ab79 Mon Sep 17 00:00:00 2001 From: Vineeth Pillai Date: Fri, 15 May 2026 10:01:21 -0400 Subject: [PATCH 1492/5214] cpufreq: amd-pstate: Use trace_call__##name() at guarded tracepoint call site Replace trace_foo() with the new trace_call__foo() at sites already guarded by trace_foo_enabled(), avoiding a redundant static_branch_unlikely() re-evaluation inside the tracepoint. trace_call__foo() calls the tracepoint callbacks directly without utilizing the static branch again. Cc: Huang Rui Cc: "Rafael J. Wysocki" Cc: Viresh Kumar Link: https://patch.msgid.link/20260515140121.2239414-1-vineeth@bitbyteword.org Suggested-by: Steven Rostedt Suggested-by: Peter Zijlstra Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Mario Limonciello Signed-off-by: Vineeth Pillai (Google) Signed-off-by: Steven Rostedt --- drivers/cpufreq/amd-pstate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 453084c67327..4722de25149b 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -368,7 +368,8 @@ static int amd_pstate_set_floor_perf(struct cpufreq_policy *policy, u8 perf) out_trace: if (trace_amd_pstate_cppc_req2_enabled()) - trace_amd_pstate_cppc_req2(cpudata->cpu, perf, changed, ret); + trace_call__amd_pstate_cppc_req2(cpudata->cpu, perf, changed, + ret); return ret; } From 5307505fe7937358c426e19af8686928581b4c6e Mon Sep 17 00:00:00 2001 From: Vineeth Pillai Date: Fri, 15 May 2026 09:59:41 -0400 Subject: [PATCH 1493/5214] HID: Use trace_call__##name() at guarded tracepoint call sites Replace trace_foo() with the new trace_call__foo() at sites already guarded by trace_foo_enabled(), avoiding a redundant static_branch_unlikely() re-evaluation inside the tracepoint. trace_call__foo() calls the tracepoint callbacks directly without utilizing the static branch again. Original v2 series: https://lore.kernel.org/linux-trace-kernel/20260323160052.17528-1-vineeth@bitbyteword.org/ Parts of the original v2 series have already been merged in mainline. This patch is being reposted as a follow-up cleanup for the remaining unmerged pieces. Cc: Srinivas Pandruvada Cc: Jiri Kosina Cc: Benjamin Tissoires Link: https://patch.msgid.link/20260515135941.2238861-1-vineeth@bitbyteword.org Suggested-by: Steven Rostedt Suggested-by: Peter Zijlstra Signed-off-by: Vineeth Pillai (Google) Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Steven Rostedt --- drivers/hid/intel-ish-hid/ipc/pci-ish.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index ed3405c05e73..8d36ae96a3ee 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -110,7 +110,7 @@ void ish_event_tracer(struct ishtp_device *dev, const char *format, ...) vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); va_end(args); - trace_ishtp_dump(tmp_buf); + trace_call__ishtp_dump(tmp_buf); } } From bb4613842e8033a714bacf3b62cc70638926cdcf Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 13 May 2026 15:00:07 -0400 Subject: [PATCH 1494/5214] tracing: Allow perf to read synthetic events Currently, perf can not enable synthetic events. When it does, it either causes a warning in the kernel or errors with "no such device". Add the necessary code to allow perf to also attach to synthetic events. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Peter Zijlstra Link: https://patch.msgid.link/20260513150007.3b280e87@gandalf.local.home Reported-by: Ian Rogers Acked-by: Namhyung Kim Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_events_synth.c | 121 +++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index 39ac4eba0702..e6871230bde9 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -499,28 +499,19 @@ static unsigned int trace_stack(struct synth_trace_event *entry, return len; } -static void trace_event_raw_event_synth(void *__data, - u64 *var_ref_vals, - unsigned int *var_ref_idx) +static __always_inline int get_field_size(struct synth_event *event, + u64 *var_ref_vals, + unsigned int *var_ref_idx) { - unsigned int i, n_u64, val_idx, len, data_size = 0; - struct trace_event_file *trace_file = __data; - struct synth_trace_event *entry; - struct trace_event_buffer fbuffer; - struct trace_buffer *buffer; - struct synth_event *event; - int fields_size = 0; - - event = trace_file->event_call->data; - - if (trace_trigger_soft_disabled(trace_file)) - return; + int fields_size; fields_size = event->n_u64 * sizeof(u64); - for (i = 0; i < event->n_dynamic_fields; i++) { + for (int i = 0; i < event->n_dynamic_fields; i++) { unsigned int field_pos = event->dynamic_fields[i]->field_pos; char *str_val; + int val_idx; + int len; val_idx = var_ref_idx[field_pos]; str_val = (char *)(long)var_ref_vals[val_idx]; @@ -535,18 +526,18 @@ static void trace_event_raw_event_synth(void *__data, fields_size += len; } + return fields_size; +} - /* - * Avoid ring buffer recursion detection, as this event - * is being performed within another event. - */ - buffer = trace_file->tr->array_buffer.buffer; - guard(ring_buffer_nest)(buffer); - - entry = trace_event_buffer_reserve(&fbuffer, trace_file, - sizeof(*entry) + fields_size); - if (!entry) - return; +static __always_inline void write_synth_entry(struct synth_event *event, + struct synth_trace_event *entry, + u64 *var_ref_vals, + unsigned int *var_ref_idx) +{ + int data_size = 0; + int i, n_u64; + int val_idx; + int len; for (i = 0, n_u64 = 0; i < event->n_fields; i++) { val_idx = var_ref_idx[i]; @@ -587,10 +578,83 @@ static void trace_event_raw_event_synth(void *__data, n_u64++; } } +} + +static void trace_event_raw_event_synth(void *__data, + u64 *var_ref_vals, + unsigned int *var_ref_idx) +{ + struct trace_event_file *trace_file = __data; + struct synth_trace_event *entry; + struct trace_event_buffer fbuffer; + struct trace_buffer *buffer; + struct synth_event *event; + int fields_size; + + event = trace_file->event_call->data; + + if (trace_trigger_soft_disabled(trace_file)) + return; + + fields_size = get_field_size(event, var_ref_vals, var_ref_idx); + + /* + * Avoid ring buffer recursion detection, as this event + * is being performed within another event. + */ + buffer = trace_file->tr->array_buffer.buffer; + guard(ring_buffer_nest)(buffer); + + entry = trace_event_buffer_reserve(&fbuffer, trace_file, + sizeof(*entry) + fields_size); + if (!entry) + return; + + write_synth_entry(event, entry, var_ref_vals, var_ref_idx); trace_event_buffer_commit(&fbuffer); } +#ifdef CONFIG_PERF_EVENTS +static void perf_event_raw_event_synth(void *__data, + u64 *var_ref_vals, + unsigned int *var_ref_idx) +{ + struct trace_event_call *call = __data; + struct synth_trace_event *entry; + struct hlist_head *perf_head; + struct synth_event *event; + struct pt_regs *regs; + int fields_size; + size_t size; + int context; + + event = call->data; + + perf_head = this_cpu_ptr(call->perf_events); + + if (!perf_head || hlist_empty(perf_head)) + return; + + fields_size = get_field_size(event, var_ref_vals, var_ref_idx); + + size = ALIGN(sizeof(*entry) + fields_size, 8); + + entry = perf_trace_buf_alloc(size, ®s, &context); + + if (unlikely(!entry)) + return; + + write_synth_entry(event, entry, var_ref_vals, var_ref_idx); + + perf_fetch_caller_regs(regs); + + perf_trace_buf_submit(entry, size, context, + call->event.type, 1, regs, + perf_head, NULL); +} +#endif + static void free_synth_event_print_fmt(struct trace_event_call *call) { if (call) { @@ -917,6 +981,9 @@ static int register_synth_event(struct synth_event *event) call->flags = TRACE_EVENT_FL_TRACEPOINT; call->class->reg = synth_event_reg; call->class->probe = trace_event_raw_event_synth; +#ifdef CONFIG_PERF_EVENTS + call->class->perf_probe = perf_event_raw_event_synth; +#endif call->data = event; call->tp = event->tp; From 5ec75d6a1b5b8beebea3bcaa85baad295ee09dcb Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Tue, 19 May 2026 16:16:20 +0800 Subject: [PATCH 1495/5214] tracing/branch: Use pr_warn() instead of printk(KERN_WARNING) Use pr_warn() instead of printk(KERN_WARNING ...) for the branch tracer warning messages. Keep the message text unchanged. The change only removes the open-coded log level from these warnings. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Link: https://patch.msgid.link/20260519081620.3874441-1-pengyu@kylinos.cn Signed-off-by: Yu Peng Signed-off-by: Steven Rostedt --- kernel/trace/trace_branch.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index d1564db95a8f..d8e97ad798f0 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c @@ -181,8 +181,7 @@ __init static int init_branch_tracer(void) ret = register_trace_event(&trace_branch_event); if (!ret) { - printk(KERN_WARNING "Warning: could not register " - "branch events\n"); + pr_warn("Warning: could not register branch events\n"); return 1; } return register_tracer(&branch_trace); @@ -374,8 +373,7 @@ __init static int init_annotated_branch_stats(void) ret = register_stat_tracer(&annotated_branch_stats); if (ret) { - printk(KERN_WARNING "Warning: could not register " - "annotated branches stats\n"); + pr_warn("Warning: could not register annotated branches stats\n"); return ret; } return 0; @@ -439,8 +437,7 @@ __init static int all_annotated_branch_stats(void) ret = register_stat_tracer(&all_branch_stats); if (ret) { - printk(KERN_WARNING "Warning: could not register " - "all branches stats\n"); + pr_warn("Warning: could not register all branches stats\n"); return ret; } return 0; From 153498f200d295f3efa6bd37fc6e7ff105ab344f Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Tue, 19 May 2026 16:34:09 +0800 Subject: [PATCH 1496/5214] tracing: Use krealloc_array() for trace option array growth Use krealloc_array() when growing tr->topts instead of open-coding the size calculation in krealloc(). This makes the resize path use the helper intended for array allocations and avoids manual multiplication of the element count and element size. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Link: https://patch.msgid.link/20260519083409.3885032-1-pengyu@kylinos.cn Signed-off-by: Yu Peng Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6eb4d3097a4d..aec3f31ed027 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7928,8 +7928,8 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer, if (!topts) return 0; - tr_topts = krealloc(tr->topts, sizeof(*tr->topts) * (tr->nr_topts + 1), - GFP_KERNEL); + tr_topts = krealloc_array(tr->topts, tr->nr_topts + 1, sizeof(*tr->topts), + GFP_KERNEL); if (!tr_topts) { kfree(topts); return -ENOMEM; From 3a5b29657593fe7695337dfcf40c49eb6fa544d4 Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Thu, 21 May 2026 01:52:34 +0000 Subject: [PATCH 1497/5214] tracing: Fix README path for synthetic_events The events/ prefix should be removed, since synthetic_events is now directly under the tracing root directory. Cc: Cc: Link: https://patch.msgid.link/20260521015211.111-1-ao.sun@transsion.com Signed-off-by: Ao Sun Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index aec3f31ed027..4218c174460b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4474,7 +4474,7 @@ static const char readme_msg[] = "\t snapshot() - snapshot the trace buffer\n\n" #endif #ifdef CONFIG_SYNTH_EVENTS - " events/synthetic_events\t- Create/append/remove/show synthetic events\n" + " synthetic_events\t- Create/append/remove/show synthetic events\n" "\t Write into this file to define/undefine new synthetic events.\n" "\t example: echo 'myevent u64 lat; char name[]; long[] stack' >> synthetic_events\n" #endif From 817e148935dd76ce39bf468f51308ec77de87033 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 20 May 2026 14:50:06 -0700 Subject: [PATCH 1498/5214] tracing: Simplify pages allocation for tracing_map logic Change to a flexible array member to allocate together with the array struct. Simplifies code slightly by removing no longer correct null checks for pages and removing kfrees. Cc: Mathieu Desnoyers Cc: Kees Cook Cc: "Gustavo A. R. Silva" Link: https://patch.msgid.link/20260520215006.12008-1-rosenp@gmail.com Signed-off-by: Rosen Penev Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/tracing_map.c | 30 +++++++++++------------------- kernel/trace/tracing_map.h | 2 +- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c index 0dd7927df22a..d7922f40dbe2 100644 --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -288,9 +288,6 @@ static void tracing_map_array_clear(struct tracing_map_array *a) { unsigned int i; - if (!a->pages) - return; - for (i = 0; i < a->n_pages; i++) memset(a->pages[i], 0, PAGE_SIZE); } @@ -302,9 +299,6 @@ static void tracing_map_array_free(struct tracing_map_array *a) if (!a) return; - if (!a->pages) - goto free; - for (i = 0; i < a->n_pages; i++) { if (!a->pages[i]) break; @@ -312,9 +306,6 @@ static void tracing_map_array_free(struct tracing_map_array *a) free_page((unsigned long)a->pages[i]); } - kfree(a->pages); - - free: kfree(a); } @@ -322,24 +313,25 @@ static struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts, unsigned int entry_size) { struct tracing_map_array *a; + unsigned int entry_size_shift; + unsigned int entries_per_page; + unsigned int n_pages; unsigned int i; - a = kzalloc_obj(*a); + entry_size_shift = fls(roundup_pow_of_two(entry_size) - 1); + entries_per_page = PAGE_SIZE / (1 << entry_size_shift); + n_pages = max(1, n_elts / entries_per_page); + + a = kzalloc_flex(*a, pages, n_pages); if (!a) return NULL; - a->entry_size_shift = fls(roundup_pow_of_two(entry_size) - 1); - a->entries_per_page = PAGE_SIZE / (1 << a->entry_size_shift); - a->n_pages = n_elts / a->entries_per_page; - if (!a->n_pages) - a->n_pages = 1; + a->entry_size_shift = entry_size_shift; + a->entries_per_page = entries_per_page; + a->n_pages = n_pages; a->entry_shift = fls(a->entries_per_page) - 1; a->entry_mask = (1 << a->entry_shift) - 1; - a->pages = kcalloc(a->n_pages, sizeof(void *), GFP_KERNEL); - if (!a->pages) - goto free; - for (i = 0; i < a->n_pages; i++) { a->pages[i] = (void *)get_zeroed_page(GFP_KERNEL); if (!a->pages[i]) diff --git a/kernel/trace/tracing_map.h b/kernel/trace/tracing_map.h index 99c37eeebc16..18a02959d77b 100644 --- a/kernel/trace/tracing_map.h +++ b/kernel/trace/tracing_map.h @@ -167,7 +167,7 @@ struct tracing_map_array { unsigned int entry_shift; unsigned int entry_mask; unsigned int n_pages; - void **pages; + void *pages[] __counted_by(n_pages); }; #define TRACING_MAP_ARRAY_ELT(array, idx) \ From 292c8197e3685aa7ea5a3803ca240b2210fcd021 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 21 May 2026 09:50:26 -0400 Subject: [PATCH 1499/5214] tracing: Move trace_iterator_increment() into trace_find_next_entry_inc() trace_iterator_increment() is only called from trace_find_next_entry_inc(). It's a small enough function that really doesn't need to be separated. Move the code from trace_iterator_increment() into trace_find_next_entry_inc() and remove trace_iterator_increment(). Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Link: https://patch.msgid.link/20260521095026.20c9799d@gandalf.local.home Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4218c174460b..ae527c419508 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2338,15 +2338,6 @@ void trace_last_func_repeats(struct trace_array *tr, __buffer_unlock_commit(buffer, event); } -static void trace_iterator_increment(struct trace_iterator *iter) -{ - struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, iter->cpu); - - iter->idx++; - if (buf_iter) - ring_buffer_iter_advance(buf_iter); -} - static struct trace_entry * peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts, unsigned long *lost_events) @@ -2676,11 +2667,17 @@ struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, /* Find the next real entry, and increment the iterator to the next entry */ void *trace_find_next_entry_inc(struct trace_iterator *iter) { + struct ring_buffer_iter *buf_iter; + iter->ent = __find_next_entry(iter, &iter->cpu, &iter->lost_events, &iter->ts); - if (iter->ent) - trace_iterator_increment(iter); + if (iter->ent) { + iter->idx++; + buf_iter = trace_buffer_iter(iter, iter->cpu); + if (buf_iter) + ring_buffer_iter_advance(buf_iter); + } return iter->ent ? iter : NULL; } From d62b0e3104cfd2171281867196cb1365098a25e0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 12:34:32 +0200 Subject: [PATCH 1500/5214] dt-bindings: clock: qcom: add the definition for the USB2 PHY reset Provide the USB2 PHY reset definition in dt-bindings for the Nord negcc module in order to enable adding the USB nodes in DTS. Signed-off-by: Bartosz Golaszewski Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260518-nord-clk-usb2-phy-v2-1-17a86cb307c3@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/dt-bindings/clock/qcom,nord-negcc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/qcom,nord-negcc.h b/include/dt-bindings/clock/qcom,nord-negcc.h index 95f333d8e1aa..a7024317e90b 100644 --- a/include/dt-bindings/clock/qcom,nord-negcc.h +++ b/include/dt-bindings/clock/qcom,nord-negcc.h @@ -120,5 +120,6 @@ #define NE_GCC_USB3_PHY_SEC_BCR 10 #define NE_GCC_USB3PHY_PHY_PRIM_BCR 11 #define NE_GCC_USB3PHY_PHY_SEC_BCR 12 +#define NE_GCC_QUSB2PHY_PRIM_BCR 13 #endif From 9bee0a0a33e56122834a18e865fa83fdd2c99ebd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 12:34:33 +0200 Subject: [PATCH 1501/5214] clk: qcom: nord: negcc: add support for the USB2 PHY reset Expose the USB2 PHY reset in order to enable adding the USB nodes in DTS for Nord. Signed-off-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260518-nord-clk-usb2-phy-v2-2-17a86cb307c3@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/negcc-nord.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/qcom/negcc-nord.c b/drivers/clk/qcom/negcc-nord.c index 2cb66b0691a6..2e653ef0fe0e 100644 --- a/drivers/clk/qcom/negcc-nord.c +++ b/drivers/clk/qcom/negcc-nord.c @@ -1918,6 +1918,7 @@ static const struct qcom_reset_map ne_gcc_nord_resets[] = { [NE_GCC_USB3_PHY_SEC_BCR] = { 0x2d000 }, [NE_GCC_USB3PHY_PHY_PRIM_BCR] = { 0x2b004 }, [NE_GCC_USB3PHY_PHY_SEC_BCR] = { 0x2d004 }, + [NE_GCC_QUSB2PHY_PRIM_BCR] = { 0x2e000 }, }; static const struct clk_rcg_dfs_data ne_gcc_nord_dfs_clocks[] = { From e6c8140bd06d7dd8ee1e3c690445d3cfcaf1d892 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Fri, 17 Apr 2026 11:51:26 +0800 Subject: [PATCH 1502/5214] f2fs: map data writes to FDP streams From: Wenjie Qi F2FS already classifies DATA writes using its existing hot, warm and cold temperature policy, but it only passes that intent down as a write hint. That hint alone is not sufficient for NVMe FDP placement, because the current NVMe command path consumes `bio->bi_write_stream` rather than `bio->bi_write_hint` when selecting a placement ID. When the target block device exposes write streams, map the existing F2FS DATA temperature classes onto stream IDs and set `bio->bi_write_stream` for both buffered and direct writes. If the device exposes no write streams, keep the current behavior by leaving the stream unset. The stream mapping is evaluated against the target block device of each bio, so the existing per-device fallback behavior stays unchanged for multi-device filesystems. Existing blkzoned restrictions also remain in place. The mapping is intentionally small and deterministic: - 1 stream: hot, warm and cold all use stream 1 - 2 streams: hot/warm use 1, cold uses 2 - 3+ streams: hot uses 1, warm uses 2, cold uses 3 Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.rst | 9 +++++++++ fs/f2fs/data.c | 2 ++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 2 ++ fs/f2fs/segment.c | 13 +++++++++++++ 5 files changed, 28 insertions(+) diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 7e4031631286..8c4a14ae444f 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -137,6 +137,15 @@ noacl Disable POSIX Access Control List. Note: acl is enabled active_logs=%u Support configuring the number of active logs. In the current design, f2fs supports only 2, 4, and 6 logs. Default number is 6. + When the underlying block device exposes write + streams, the default active_logs=6 configuration + maps hot, warm, and cold DATA writes to streams 1, + 2, and 3, respectively. If only one or two write + streams are available, f2fs falls back to mapping + all DATA writes to stream 1 or mapping hot/warm + to stream 1 and cold to stream 2. If no write + streams are exposed, f2fs leaves the stream + unset. disable_ext_identify Disable the extension list configured by mkfs, so f2fs is not aware of cold files such as media files. inline_xattr Enable the inline xattrs feature. diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8d4f1e75dee3..0f92a8805635 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -509,6 +509,8 @@ static struct bio *__bio_alloc(struct f2fs_io_info *fio, int npages) bio->bi_private = sbi; bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, fio->type, fio->temp); + bio->bi_write_stream = f2fs_io_type_to_write_stream(bdev, fio->type, + fio->temp); } iostat_alloc_and_bind_ctx(sbi, bio, NULL); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 91f506e7c9cf..dc8f3b55b560 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4061,6 +4061,8 @@ void f2fs_destroy_segment_manager_caches(void); int f2fs_rw_hint_to_seg_type(struct f2fs_sb_info *sbi, enum rw_hint hint); enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi, enum page_type type, enum temp_type temp); +u8 f2fs_io_type_to_write_stream(struct block_device *bdev, + enum page_type type, enum temp_type temp); unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi); unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi, unsigned int segno); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index fb12c5c9affd..2d8b383ecf52 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -5076,6 +5076,8 @@ static void f2fs_dio_write_submit_io(const struct iomap_iter *iter, enum temp_type temp = f2fs_get_segment_temp(sbi, type); bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp); + bio->bi_write_stream = + f2fs_io_type_to_write_stream(bio->bi_bdev, DATA, temp); blk_crypto_submit_bio(bio); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 788f8b050249..9cce4d94ac82 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3636,6 +3636,19 @@ enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi, } } +u8 f2fs_io_type_to_write_stream(struct block_device *bdev, + enum page_type type, enum temp_type temp) +{ + unsigned short nr = bdev_max_write_streams(bdev); + + if (type != DATA || !nr) + return 0; + if (nr < NR_TEMP_TYPE) + return temp == COLD ? nr : HOT + 1; + + return temp + 1; +} + static int __get_segment_type_2(struct f2fs_io_info *fio) { if (fio->type == DATA) From ccc6436abb847352b8ed2de7a8ab2df45bb06622 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Mar 2026 08:36:02 +0000 Subject: [PATCH 1503/5214] f2fs: support to report fserror This patch supports to report fserror, it provides another way to let userspace to monitor filesystem level error. In addition, it exports /sys/fs/f2fs/features/fserror once f2fs kernel module start to support the new feature, then generic/791 of fstests can notice the feature, and verify validation of fserror report. Cc: Darrick J. Wong Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 3 ++- fs/f2fs/compress.c | 2 ++ fs/f2fs/data.c | 5 ++++- fs/f2fs/dir.c | 2 ++ fs/f2fs/inline.c | 3 +++ fs/f2fs/inode.c | 5 +++++ fs/f2fs/node.c | 8 ++++++++ fs/f2fs/recovery.c | 2 ++ fs/f2fs/segment.c | 2 ++ fs/f2fs/super.c | 26 +++++++++++++++++++++++++ fs/f2fs/sysfs.c | 2 ++ fs/f2fs/verity.c | 2 ++ fs/f2fs/xattr.c | 6 ++++++ 13 files changed, 66 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 423ec40e2e4e..27d5e88facbe 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -270,7 +270,8 @@ Description: Shows all enabled kernel features. inode_checksum, flexible_inline_xattr, quota_ino, inode_crtime, lost_found, verity, sb_checksum, casefold, readonly, compression, test_dummy_encryption_v2, - atomic_write, pin_file, encrypted_casefold, linear_lookup. + atomic_write, pin_file, encrypted_casefold, linear_lookup, + fserror. What: /sys/fs/f2fs//inject_rate Date: May 2016 diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 881e76158b96..caf522d667d6 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -760,6 +761,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task) /* Avoid f2fs_commit_super in irq context */ f2fs_handle_error(sbi, ERROR_FAIL_DECOMPRESSION); + fserror_report_file_metadata(dic->inode, ret, GFP_NOFS); goto out_release; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0f92a8805635..b7b8a72d6486 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -377,9 +378,10 @@ static void f2fs_write_end_io(struct bio *bio) if (unlikely(bio->bi_status != BLK_STS_OK)) { mapping_set_error(folio->mapping, -EIO); - if (type == F2FS_WB_CP_DATA) + if (type == F2FS_WB_CP_DATA) { f2fs_stop_checkpoint(sbi, true, STOP_CP_REASON_WRITE_FAIL); + } } if (is_node_folio(folio)) { @@ -1750,6 +1752,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag) err = -EFSCORRUPTED; f2fs_handle_error(sbi, ERROR_CORRUPTED_CLUSTER); + fserror_report_file_metadata(inode, err, GFP_NOFS); goto sync_out; } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 38802ee2e40d..b1697194c3c4 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" #include "acl.h" @@ -1020,6 +1021,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, set_sbi_flag(sbi, SBI_NEED_FSCK); err = -EFSCORRUPTED; f2fs_handle_error(sbi, ERROR_CORRUPTED_DIRENT); + fserror_report_file_metadata(d->inode, err, GFP_NOFS); goto out; } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 7aabfc9b43cb..099f72089701 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -179,6 +180,7 @@ int f2fs_convert_inline_folio(struct dnode_of_data *dn, struct folio *folio) f2fs_warn(fio.sbi, "%s: corrupted inline inode ino=%llu, i_addr[0]:0x%x, run fsck to fix.", __func__, dn->inode->i_ino, dn->data_blkaddr); f2fs_handle_error(fio.sbi, ERROR_INVALID_BLKADDR); + fserror_report_file_metadata(dn->inode, -EFSCORRUPTED, GFP_NOFS); return -EFSCORRUPTED; } @@ -435,6 +437,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct folio *ifolio, __func__, dir->i_ino, dn.data_blkaddr); f2fs_handle_error(F2FS_F_SB(folio), ERROR_INVALID_BLKADDR); err = -EFSCORRUPTED; + fserror_report_file_metadata(dn.inode, err, GFP_NOFS); goto out; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c6dcda447882..1694726122e6 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -480,6 +481,7 @@ static int do_read_inode(struct inode *inode) f2fs_folio_put(node_folio, true); set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS); return -EFSCORRUPTED; } @@ -541,6 +543,7 @@ static int do_read_inode(struct inode *inode) if (!sanity_check_extent_cache(inode, node_folio)) { f2fs_folio_put(node_folio, true); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS); return -EFSCORRUPTED; } @@ -583,6 +586,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) trace_f2fs_iget_exit(inode, ret); iput(inode); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); + fserror_report_file_metadata(inode, ret, GFP_NOFS); return ERR_PTR(ret); } @@ -787,6 +791,7 @@ void f2fs_update_inode_page(struct inode *inode) if (err == -ENOMEM || ++count <= DEFAULT_RETRY_IO_COUNT) goto retry; stop_checkpoint: + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_UPDATE_INODE); return; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4e5bd9e4cfc3..b1247de25411 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -1265,6 +1266,8 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) if (err == -ENOENT) { set_sbi_flag(F2FS_F_SB(folio), SBI_NEED_FSCK); f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); + fserror_report_file_metadata(dn.inode, -EFSCORRUPTED, + GFP_NOFS); f2fs_err_ratelimited(sbi, "truncate node fail, ino:%llu, nid:%u, " "offset[0]:%d, offset[1]:%d, nofs:%d", @@ -1556,6 +1559,8 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, next_blkaddr_of_node(folio)); f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER); + fserror_report_file_metadata(folio->mapping->host, + -EFSCORRUPTED, in_irq ? GFP_NOWAIT : GFP_NOFS); return -EFSCORRUPTED; } @@ -1778,6 +1783,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync, if (f2fs_sanity_check_node_footer(sbi, folio, nid, NODE_TYPE_REGULAR, false)) { + fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID); goto redirty_out; } @@ -2703,6 +2709,8 @@ bool f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) spin_unlock(&nm_i->nid_list_lock); f2fs_err(sbi, "Corrupted nid %u in free_nid_list", i->nid); + fserror_report_metadata(sbi->sb, -EFSCORRUPTED, + GFP_NOFS); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID); return false; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 3d3dacec9482..89af8407b667 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" #include "segment.h" @@ -679,6 +680,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, ofs_of_node(folio)); err = -EFSCORRUPTED; f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER); + fserror_report_file_metadata(dn.inode, err, GFP_NOFS); goto err; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9cce4d94ac82..008432d674dc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -2896,6 +2897,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi, /* set it as dirty segment in free segmap */ if (test_bit(segno, free_i->free_segmap)) { ret = -EFSCORRUPTED; + fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_FREE_BITMAP); goto out_unlock; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ccf806b676f5..aab4345f3ee7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -4635,6 +4636,8 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi) f2fs_err_ratelimited(sbi, "f2fs_commit_super fails to record stop_reason, err:%d", err); + + fserror_report_shutdown(sbi->sb, GFP_NOFS); } void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag) @@ -4649,6 +4652,27 @@ void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag) spin_unlock_irqrestore(&sbi->error_lock, flags); } +static void f2fs_report_fserror(struct f2fs_sb_info *sbi, unsigned char error) +{ + switch (error) { + case ERROR_INVALID_BLKADDR: + case ERROR_CORRUPTED_INODE: + case ERROR_INCONSISTENT_SUMMARY: + case ERROR_INCONSISTENT_SUM_TYPE: + case ERROR_CORRUPTED_JOURNAL: + case ERROR_INCONSISTENT_NODE_COUNT: + case ERROR_INCONSISTENT_BLOCK_COUNT: + case ERROR_INVALID_CURSEG: + case ERROR_INCONSISTENT_SIT: + case ERROR_INVALID_NODE_REFERENCE: + case ERROR_INCONSISTENT_NAT: + fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS); + break; + default: + return; + } +} + void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error) { f2fs_save_errors(sbi, error); @@ -4658,6 +4682,8 @@ void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error) if (!test_bit(error, (unsigned long *)sbi->errors)) return; schedule_work(&sbi->s_error_work); + + f2fs_report_fserror(sbi, error); } static bool system_going_down(void) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 352e96ad5c3a..665687244c93 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -1399,6 +1399,7 @@ F2FS_FEATURE_RO_ATTR(pin_file); F2FS_FEATURE_RO_ATTR(linear_lookup); #endif F2FS_FEATURE_RO_ATTR(packed_ssa); +F2FS_FEATURE_RO_ATTR(fserror); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -1566,6 +1567,7 @@ static struct attribute *f2fs_feat_attrs[] = { BASE_ATTR_LIST(linear_lookup), #endif BASE_ATTR_LIST(packed_ssa), + BASE_ATTR_LIST(fserror), NULL, }; ATTRIBUTE_GROUPS(f2fs_feat); diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index 92ebcc19cab0..39f482515445 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -25,6 +25,7 @@ */ #include +#include #include "f2fs.h" #include "xattr.h" @@ -243,6 +244,7 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf, f2fs_warn(F2FS_I_SB(inode), "invalid verity xattr"); f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_VERITY_XATTR); + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS); return -EFSCORRUPTED; } if (buf_size) { diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 610d5810074d..24cef7e1f56a 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "f2fs.h" #include "xattr.h" #include "segment.h" @@ -371,6 +372,7 @@ static int lookup_all_xattrs(struct inode *inode, struct folio *ifolio, err = -ENODATA; f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); + fserror_report_file_metadata(inode, err, GFP_NOFS); goto out; } check: @@ -590,6 +592,8 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); + fserror_report_file_metadata(inode, + -EFSCORRUPTED, GFP_NOFS); break; } @@ -677,6 +681,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, error = -EFSCORRUPTED; f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); + fserror_report_file_metadata(inode, error, GFP_NOFS); goto exit; } @@ -705,6 +710,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, error = -EFSCORRUPTED; f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); + fserror_report_file_metadata(inode, error, GFP_NOFS); goto exit; } last = XATTR_NEXT_ENTRY(last); From 1f70ddb28a3c71df124da5fa4040c808116d6bb9 Mon Sep 17 00:00:00 2001 From: Yongpeng Yang Date: Mon, 27 Apr 2026 21:10:51 +0800 Subject: [PATCH 1504/5214] f2fs: fix incorrect FI_NO_EXTENT handling in __destroy_extent_node() When __destroy_extent_node() sets the inode flag FI_NO_EXTENT, it does not reset the length of the largest extent to 0 and update the inode folio. Since modifications to the extent tree are disallowed afterward, the cached largest extent may become stale. This can trigger the following error in xfstests generic/388: F2FS-fs (dm-0): sanity_check_extent_cache: inode (ino=1761) extent info [220057, 57, 6] is incorrect, run fsck to fix In the f2fs_drop_inode path, __destroy_extent_node() does not need to guarantee that et->node_cnt is 0, because concurrency with writeback is expected in this path, and writeback may update the extent cache. This patch reverts commit ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback"), and remove the unnecessary zero check of et->node_cnt. Fixes: ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback") Cc: stable@vger.kernel.org Reported-by: Chao Yu Suggested-by: Chao Yu Signed-off-by: Yongpeng Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index d2e006420f04..61f6b9714366 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type) if (!__init_may_extent_tree(inode, type)) return false; - if (is_inode_flag_set(inode, FI_NO_EXTENT)) - return false; - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return false; if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) && !f2fs_sb_has_readonly(F2FS_I_SB(inode))) return false; @@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode, while (atomic_read(&et->node_cnt)) { write_lock(&et->lock); - if (!is_inode_flag_set(inode, FI_NO_EXTENT)) - set_inode_flag(inode, FI_NO_EXTENT); node_cnt += __free_extent_tree(sbi, et, nr_shrink); write_unlock(&et->lock); } - f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); - return node_cnt; } @@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode, write_lock(&et->lock); - if (is_inode_flag_set(inode, FI_NO_EXTENT)) { - write_unlock(&et->lock); - return; - } - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) { + write_unlock(&et->lock); + return; + } + prev = et->largest; dei.len = 0; From cac16750e5a8856addb0385b207cff03cd012018 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 22 Apr 2026 18:48:47 +0800 Subject: [PATCH 1505/5214] f2fs: doc: fix the wrong description for critical_task_priority The default value should be 120 rather than 100, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 27d5e88facbe..1b58c029abd0 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -1001,4 +1001,4 @@ Contact: "Chao Yu" Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ckpt, f2fs_gc threads, limitation as below: - it requires user has CAP_SYS_NICE capability. - - the range is [100, 139], by default the value is 100. + - the range is [100, 139], by default the value is 120. From dd3114870771562036fdcf5abe813956f36d224d Mon Sep 17 00:00:00 2001 From: Ruipeng Qi Date: Sat, 2 May 2026 20:41:57 +0800 Subject: [PATCH 1506/5214] f2fs: fix potential deadlock in f2fs_balance_fs() When the f2fs filesystem space is nearly exhausted, we encounter deadlock issues as below: INFO: task A:1890 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:A state:D stack:0 pid:1890 tgid:1626 ppid:1153 flags:0x00000204 Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 folio_wait_bit+0x20/0x38 folio_wait_writeback+0x54/0xc8 truncate_inode_partial_folio+0x70/0x1e0 truncate_inode_pages_range+0x1b0/0x450 truncate_pagecache+0x54/0x88 f2fs_file_write_iter+0x3e8/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:11 state:D stack:0 pid:2680853 tgid:2680853 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 __filemap_get_folio+0x214/0x348 pagecache_get_page+0x20/0x70 f2fs_get_read_data_page+0x150/0x3e8 f2fs_get_lock_data_page+0x2c/0x160 move_data_page+0x50/0x478 do_garbage_collect+0xd38/0x1528 f2fs_gc+0x240/0x7e0 f2fs_balance_fs+0x1a0/0x208 f2fs_write_single_data_page+0x6e4/0x730 f2fs_write_cache_pages+0x378/0x9b0 f2fs_write_data_pages+0x2e4/0x388 do_writepages+0x8c/0x2c8 __writeback_single_inode+0x4c/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x200 INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:8 state:D stack:0 pid:2641297 tgid:2641297 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_write_inode+0xf4/0x328 __writeback_single_inode+0x370/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x20 INFO: task B:1902 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:B state:D stack:0 pid:1902 tgid:1626 ppid:1153 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_map_blocks+0x94c/0x1110 f2fs_file_write_iter+0x228/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task sync:2769849 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:sync state:D stack:0 pid:2769849 tgid:2769849 ppid:736 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 wb_wait_for_completion+0xb0/0xe8 sync_inodes_sb+0xc8/0x2b0 sync_inodes_one_sb+0x24/0x38 iterate_supers+0xa8/0x138 ksys_sync+0x54/0xc8 __arm64_sys_sync+0x18/0x30 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 The root cause is a potential deadlock between the following tasks: kworker/u8:11 Thread A - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback(X) - f2fs_outplace_write_data - bio_add_folio(X) - folio_unlock(X) - truncate_inode_pages_range - __filemap_get_folio(X, FGP_LOCK) - truncate_inode_partial_folio(X) - folio_wait_writeback(X) - f2fs_balance_fs - f2fs_gc - do_garbage_collect - move_data_page - f2fs_get_lock_data_page - __filemap_get_folio(X, FGP_LOCK) Both threads try to access folio X. Thread A holds the lock but waits for writeback, while kworker waits for the lock. This causes a deadlock. Other threads also enter D state, waiting for locks such as gc_lock and writepages. OPU/IPU DATA folio are all affected by this issue. To avoid such potential deadlocks, always commit these cached folios before triggering f2fs_gc() in f2fs_balance_fs(). Suggested-by: Chao Yu Reviewed-by: Chao Yu Signed-off-by: Ruipeng Qi Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 29 +++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 8 ++++++++ 3 files changed, 38 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b7b8a72d6486..5d18119a214a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -947,6 +947,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, } } +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi) +{ + struct bio_entry *be, *tmp; + struct f2fs_bio_info *io; + enum temp_type temp; + + for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { + LIST_HEAD(list); + + io = sbi->write_io[DATA] + temp; + + /* A lockless list_empty() check is safe here: any bios from + * other kworkers that we miss will be submitted by those + * kworkers accordingly. + */ + if (list_empty(&io->bio_list)) + continue; + + f2fs_down_write(&io->bio_list_lock); + list_splice_init(&io->bio_list, &list); + f2fs_up_write(&io->bio_list_lock); + + list_for_each_entry_safe(be, tmp, &list, list) { + f2fs_submit_write_bio(sbi, be->bio, DATA); + del_bio_entry(be); + } + } +} + int f2fs_merge_page_bio(struct f2fs_io_info *fio) { struct bio *bio = *fio->bio; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dc8f3b55b560..e555c1d794df 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4155,6 +4155,7 @@ void f2fs_submit_merged_write_folio(struct f2fs_sb_info *sbi, struct folio *folio, enum page_type type); void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, struct bio **bio, struct folio *folio); +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi); void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); int f2fs_submit_page_bio(struct f2fs_io_info *fio); int f2fs_merge_page_bio(struct f2fs_io_info *fio); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 008432d674dc..7c8ac62b1b0d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -463,6 +463,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .should_migrate_blocks = false, .err_gc_skipped = false, .nr_free_secs = 1 }; + + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From 6549ef9cc236cd09d42ae521e459817ae6b5c5fa Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 19 May 2026 15:15:13 +0100 Subject: [PATCH 1507/5214] clk: renesas: rzg2l: Simplify SAM PLL configuration macro Replace the PLL146_CONF() macro and its associated CPG_SAMPLL_CLK{1,2}(n) helpers with a single CPG_SAM_PLL_CONF(stby) macro that takes the PLL standby register offset directly. This removes the implicit coupling between PLL index n and register layout and eliminates the now-redundant GET_REG_SAMPLL_CLK2() macro. The RZ/V2M PLL4 definition is also updated to use the new macro with its explicit standby offset (0x100), removing the local PLL4_CONF define. No functional changes. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://patch.msgid.link/20260519141518.389670-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a07g043-cpg.c | 2 +- drivers/clk/renesas/r9a07g044-cpg.c | 2 +- drivers/clk/renesas/r9a09g011-cpg.c | 7 +------ drivers/clk/renesas/rzg2l-cpg.c | 9 ++++++--- drivers/clk/renesas/rzg2l-cpg.h | 6 +----- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c index 70944ef8c5b8..59d6ee2e888f 100644 --- a/drivers/clk/renesas/r9a07g043-cpg.c +++ b/drivers/clk/renesas/r9a07g043-cpg.c @@ -103,7 +103,7 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = { /* Internal Core Clocks */ DEF_FIXED(".osc", R9A07G043_OSCCLK, CLK_EXTAL, 1, 1), DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, PLL146_CONF(0)), + DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_SAM_PLL_CONF(0)), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), DEF_FIXED(".clk_800", CLK_PLL2_800, CLK_PLL2, 1, 2), diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c index 2d3487203bf5..913cca6dd46f 100644 --- a/drivers/clk/renesas/r9a07g044-cpg.c +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -159,7 +159,7 @@ static const struct { /* Internal Core Clocks */ DEF_FIXED(".osc", R9A07G044_OSCCLK, CLK_EXTAL, 1, 1), DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, PLL146_CONF(0)), + DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_SAM_PLL_CONF(0)), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll2_533", CLK_PLL2_533, CLK_PLL2, 1, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), diff --git a/drivers/clk/renesas/r9a09g011-cpg.c b/drivers/clk/renesas/r9a09g011-cpg.c index ba25429c244d..a99ab1375f07 100644 --- a/drivers/clk/renesas/r9a09g011-cpg.c +++ b/drivers/clk/renesas/r9a09g011-cpg.c @@ -16,11 +16,6 @@ #include "rzg2l-cpg.h" -#define RZV2M_SAMPLL4_CLK1 0x104 -#define RZV2M_SAMPLL4_CLK2 0x108 - -#define PLL4_CONF (RZV2M_SAMPLL4_CLK1 << 22 | RZV2M_SAMPLL4_CLK2 << 12) - #define DIV_A DDIV_PACK(0x200, 0, 3) #define DIV_B DDIV_PACK(0x204, 0, 2) #define DIV_D DDIV_PACK(0x204, 4, 2) @@ -131,7 +126,7 @@ static const struct cpg_core_clk r9a09g011_core_clks[] __initconst = { DEF_FIXED(".pll2_400", CLK_PLL2_400, CLK_PLL2_800, 1, 2), DEF_FIXED(".pll2_200", CLK_PLL2_200, CLK_PLL2_800, 1, 4), DEF_FIXED(".pll2_100", CLK_PLL2_100, CLK_PLL2_800, 1, 8), - DEF_SAMPLL(".pll4", CLK_PLL4, CLK_MAIN_2, PLL4_CONF), + DEF_SAMPLL(".pll4", CLK_PLL4, CLK_MAIN_2, CPG_SAM_PLL_CONF(0x100)), DEF_DIV_RO(".diva", CLK_DIV_A, CLK_PLL1, DIV_A, dtable_diva), DEF_DIV_RO(".divb", CLK_DIV_B, CLK_PLL2_400, DIV_B, dtable_divb), diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 426e93dc7a98..ad9aab2ecc62 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -58,6 +58,10 @@ #define RZG3S_DIV_NF GENMASK(12, 1) #define RZG3S_SEL_PLL BIT(0) +#define CPG_PLL_STBY_OFFSET(conf) FIELD_GET(GENMASK(23, 12), (conf)) +#define CPG_PLL_CLK1_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x4) +#define CPG_PLL_CLK2_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x8) + #define RZG3L_PLL_STBY_OFFSET(x) (GET_REG_SAMPLL_CLK1(x) - 0x4) #define RZG3L_PLL_STBY_RESETB BIT(0) #define RZG3L_PLL_STBY_RESETB_WEN BIT(16) @@ -72,7 +76,6 @@ #define GET_REG_OFFSET(val) ((val >> 20) & 0xfff) #define GET_REG_SAMPLL_CLK1(val) ((val >> 22) & 0xfff) -#define GET_REG_SAMPLL_CLK2(val) ((val >> 12) & 0xfff) #define GET_REG_SAMPLL_SETTING(val) ((val) & 0xfff) #define CPG_WEN_BIT BIT(16) @@ -1093,8 +1096,8 @@ static unsigned long rzg2l_cpg_pll_clk_recalc_rate(struct clk_hw *hw, if (pll_clk->type != CLK_TYPE_SAM_PLL) return parent_rate; - val1 = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf)); - val2 = readl(priv->base + GET_REG_SAMPLL_CLK2(pll_clk->conf)); + val1 = readl(priv->base + CPG_PLL_CLK1_OFFSET(pll_clk->conf)); + val2 = readl(priv->base + CPG_PLL_CLK2_OFFSET(pll_clk->conf)); rate = mul_u64_u32_shr(parent_rate, (MDIV(val1) << 16) + KDIV(val1), 16 + SDIV(val2)); diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 33f54ba0e64e..17ec6f285c21 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -58,11 +58,7 @@ #define CPG_CLKSTATUS_SELSDHI0_STS BIT(28) #define CPG_CLKSTATUS_SELSDHI1_STS BIT(29) -/* n = 0/1/2 for PLL1/4/6 */ -#define CPG_SAMPLL_CLK1(n) (0x04 + (16 * n)) -#define CPG_SAMPLL_CLK2(n) (0x08 + (16 * n)) - -#define PLL146_CONF(n) (CPG_SAMPLL_CLK1(n) << 22 | CPG_SAMPLL_CLK2(n) << 12) +#define CPG_SAM_PLL_CONF(stby) ((stby) << 12) #define DDIV_PACK(offset, bitpos, size) \ (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) From 2e3974747e83de21559d1f937746414e7f881253 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 19 May 2026 15:15:14 +0100 Subject: [PATCH 1508/5214] clk: renesas: rzg3s/rzg3l: Simplify PLL configuration macro Replace the per-SoC G3S_PLL146_CONF() and G3L_PLL1467_CONF() macros with a unified CPG_PLL_CONF(stby, setting) macro defined in rzg2l-cpg.h. Drop the now-redundant GET_REG_SAMPLL_{CLK1, SETTING}() macros, replacing the latter with CPG_PLL1_SETTING_OFFSET() using FIELD_GET() to extract the offset value. Update RZG3L_PLL_{STBY,MON}_OFFSET() macros to derive offsets directly from CPG_PLL_STBY_OFFSET(). No functional changes. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260519141518.389670-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g045-cpg.c | 5 +---- drivers/clk/renesas/r9a08g046-cpg.c | 7 ++----- drivers/clk/renesas/rzg2l-cpg.c | 11 +++++------ drivers/clk/renesas/rzg2l-cpg.h | 1 + 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 1232fec913eb..9610676058de 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -50,9 +50,6 @@ #define G3S_SEL_SDHI1 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 4, 2) #define G3S_SEL_SDHI2 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 8, 2) -/* PLL 1/4/6 configuration registers macro. */ -#define G3S_PLL146_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) - #define DEF_G3S_MUX(_name, _id, _conf, _parent_names, _mux_flags, _clk_flags) \ DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = (_conf), \ .parent_names = (_parent_names), \ @@ -134,7 +131,7 @@ static const struct cpg_core_clk r9a08g045_core_clks[] __initconst = { /* Internal Core Clocks */ DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_G3S_PLL(".pll1", CLK_PLL1, CLK_EXTAL, G3S_PLL146_CONF(0x4, 0x8, 0x100), + DEF_G3S_PLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_PLL_CONF(0, 0x100), 1100000000UL), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index fc9db5a2f0ac..a57638734ce7 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -81,9 +81,6 @@ #define G3L_SEL_RSPI1 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 2, 2) #define G3L_SEL_RSPI2 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 4, 2) -/* PLL 1/4/6/7 configuration registers macro. */ -#define G3L_PLL1467_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) - enum clk_ids { /* Core Clock Outputs exported to DT */ LAST_DT_CORE_CLK = R9A08G046_USB_SCLK, @@ -207,11 +204,11 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_INPUT("eth1_rxc_rx_clk", CLK_ETH1_RXC_RX_CLK_IN), /* Internal Core Clocks */ - DEF_G3L_PLL(".pll1", CLK_PLL1, CLK_EXTAL, G3L_PLL1467_CONF(0x4, 0x8, 0x100), + DEF_G3L_PLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_PLL_CONF(0, 0x100), 1200000000UL), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), - DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, G3L_PLL1467_CONF(0x54, 0x58, 0), + DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, CPG_PLL_CONF(0x50, 0), 500000000UL), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), DEF_FIXED(".pll2_div2_4", CLK_PLL2_DIV2_4, CLK_PLL2_DIV2, 1, 4), diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index ad9aab2ecc62..096901e25317 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -58,14 +58,15 @@ #define RZG3S_DIV_NF GENMASK(12, 1) #define RZG3S_SEL_PLL BIT(0) +#define CPG_PLL1_SETTING_OFFSET(conf) FIELD_GET(GENMASK(11, 0), (conf)) #define CPG_PLL_STBY_OFFSET(conf) FIELD_GET(GENMASK(23, 12), (conf)) #define CPG_PLL_CLK1_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x4) #define CPG_PLL_CLK2_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x8) -#define RZG3L_PLL_STBY_OFFSET(x) (GET_REG_SAMPLL_CLK1(x) - 0x4) +#define RZG3L_PLL_STBY_OFFSET(x) (CPG_PLL_STBY_OFFSET(x)) #define RZG3L_PLL_STBY_RESETB BIT(0) #define RZG3L_PLL_STBY_RESETB_WEN BIT(16) -#define RZG3L_PLL_MON_OFFSET(x) (GET_REG_SAMPLL_CLK1(x) + 0x8) +#define RZG3L_PLL_MON_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0xc) #define RZG3L_PLL_MON_RESETB BIT(0) #define RZG3L_PLL_MON_LOCK BIT(4) @@ -75,8 +76,6 @@ #define CLK_MRST_R(reg) (0x180 + (reg)) #define GET_REG_OFFSET(val) ((val >> 20) & 0xfff) -#define GET_REG_SAMPLL_CLK1(val) ((val >> 22) & 0xfff) -#define GET_REG_SAMPLL_SETTING(val) ((val) & 0xfff) #define CPG_WEN_BIT BIT(16) @@ -1117,14 +1116,14 @@ static unsigned long rzg3s_cpg_pll_clk_recalc_rate(struct clk_hw *hw, u32 nir, nfr, mr, pr, val, setting; u64 rate; - setting = GET_REG_SAMPLL_SETTING(pll_clk->conf); + setting = CPG_PLL1_SETTING_OFFSET(pll_clk->conf); if (setting) { val = readl(priv->base + setting); if (val & RZG3S_SEL_PLL) return pll_clk->default_rate; } - val = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf)); + val = readl(priv->base + CPG_PLL_CLK1_OFFSET(pll_clk->conf)); pr = 1 << FIELD_GET(RZG3S_DIV_P, val); /* Hardware interprets values higher than 8 as p = 16. */ diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 17ec6f285c21..bd6169f62538 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -59,6 +59,7 @@ #define CPG_CLKSTATUS_SELSDHI1_STS BIT(29) #define CPG_SAM_PLL_CONF(stby) ((stby) << 12) +#define CPG_PLL_CONF(stby, setting) ((stby) << 12 | (setting)) #define DDIV_PACK(offset, bitpos, size) \ (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) From c94433be057ba23071215a4cd6f743cb2757431c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 19 May 2026 15:15:15 +0100 Subject: [PATCH 1509/5214] clk: renesas: rzg2l: Rename RZG3L-prefixed PLL macros to CPG-prefixed ones Rename RZG3L_PLL_STBY_OFFSET(), RZG3L_PLL_STBY_RESETB, RZG3L_PLL_STBY_RESETB_WEN, RZG3L_PLL_MON_OFFSET(), RZG3L_PLL_MON_RESETB, and RZG3L_PLL_MON_LOCK to their CPG_PLL_* equivalents to reflect that these macros are not RZG3L-specific and are shared across SoCs. Also fold CPG_PLL_MON_OFFSET() into rzg2l-cpg.c alongside the other CPG_PLL_*_OFFSET() helpers introduced in previous patches. No functional changes. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://patch.msgid.link/20260519141518.389670-4-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 096901e25317..0abe00e2960b 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -60,15 +60,13 @@ #define CPG_PLL1_SETTING_OFFSET(conf) FIELD_GET(GENMASK(11, 0), (conf)) #define CPG_PLL_STBY_OFFSET(conf) FIELD_GET(GENMASK(23, 12), (conf)) +#define CPG_PLL_STBY_RESETB_WEN BIT(16) +#define CPG_PLL_STBY_RESETB BIT(0) #define CPG_PLL_CLK1_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x4) #define CPG_PLL_CLK2_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x8) - -#define RZG3L_PLL_STBY_OFFSET(x) (CPG_PLL_STBY_OFFSET(x)) -#define RZG3L_PLL_STBY_RESETB BIT(0) -#define RZG3L_PLL_STBY_RESETB_WEN BIT(16) -#define RZG3L_PLL_MON_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0xc) -#define RZG3L_PLL_MON_RESETB BIT(0) -#define RZG3L_PLL_MON_LOCK BIT(4) +#define CPG_PLL_MON_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0xc) +#define CPG_PLL_MON_LOCK BIT(4) +#define CPG_PLL_MON_RESETB BIT(0) #define CLK_ON_R(reg) (reg) #define CLK_MON_R(reg) (0x180 + (reg)) @@ -1188,8 +1186,8 @@ static int rzg3l_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); struct rzg2l_cpg_priv *priv = pll_clk->priv; - u32 val = readl(priv->base + RZG3L_PLL_MON_OFFSET(pll_clk->conf)); - u32 mon_val = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; + u32 val = readl(priv->base + CPG_PLL_MON_OFFSET(pll_clk->conf)); + u32 mon_val = CPG_PLL_MON_RESETB | CPG_PLL_MON_LOCK; /* Ensure both RESETB and LOCK bits are set */ return (mon_val == (val & mon_val)); @@ -1199,17 +1197,17 @@ static int rzg3l_cpg_pll_clk_endisable(struct clk_hw *hw, bool enable) { struct pll_clk *pll_clk = to_pll(hw); struct rzg2l_cpg_priv *priv = pll_clk->priv; - u32 mon_mask = RZG3L_PLL_MON_RESETB | RZG3L_PLL_MON_LOCK; - u32 val = RZG3L_PLL_STBY_RESETB_WEN; + u32 mon_mask = CPG_PLL_MON_RESETB | CPG_PLL_MON_LOCK; + u32 val = CPG_PLL_STBY_RESETB_WEN; u32 stby_offset, mon_offset; u32 mon_val = 0; int ret; - stby_offset = RZG3L_PLL_STBY_OFFSET(pll_clk->conf); - mon_offset = RZG3L_PLL_MON_OFFSET(pll_clk->conf); + stby_offset = CPG_PLL_STBY_OFFSET(pll_clk->conf); + mon_offset = CPG_PLL_MON_OFFSET(pll_clk->conf); if (enable) { - val |= RZG3L_PLL_STBY_RESETB; + val |= CPG_PLL_STBY_RESETB; mon_val = mon_mask; } From 09a4b56100f8667f192f8a6aa4eae190331066c9 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:32 +0100 Subject: [PATCH 1510/5214] KVM: arm64: vgic-v5: Add for_each_visible_v5_ppi() iterator We have multiple instances of iterators walking the vgic_ppi_mask mask, and the way it is written has a tendency to make one's eyes bleed. Factor it as a helper and use that across the code base. Link: https://lore.kernel.org/r/20260520091949.542365-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 2 +- arch/arm64/kvm/vgic/vgic-v5.c | 10 ++++------ arch/arm64/kvm/vgic/vgic.h | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 148fc3400ea8..513f5f1429b5 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -751,7 +751,7 @@ static bool access_gicv5_ppi_enabler(struct kvm_vcpu *vcpu, * Sync the change in enable states to the vgic_irqs. We consider all * PPIs as we don't expose many to the guest. */ - for_each_set_bit(i, mask, VGIC_V5_NR_PRIVATE_IRQS) { + for_each_visible_v5_ppi(i, vcpu->kvm) { u32 intid = vgic_v5_make_ppi(i); struct vgic_irq *irq; diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index fdd39ea7f83e..c0d36658ffe7 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -316,7 +316,7 @@ static void vgic_v5_sync_ppi_priorities(struct kvm_vcpu *vcpu) * those actually exposed to the guest by first iterating over the mask * of exposed PPIs. */ - for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) { + for_each_visible_v5_ppi(i, vcpu->kvm) { u32 intid = vgic_v5_make_ppi(i); struct vgic_irq *irq; int pri_idx, pri_reg, pri_bit; @@ -358,7 +358,7 @@ bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu) if (!priority_mask) return false; - for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) { + for_each_visible_v5_ppi(i, vcpu->kvm) { u32 intid = vgic_v5_make_ppi(i); bool has_pending = false; struct vgic_irq *irq; @@ -391,8 +391,7 @@ void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu) activer = host_data_ptr(vgic_v5_ppi_state)->activer_exit; pendr = host_data_ptr(vgic_v5_ppi_state)->pendr; - for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, - VGIC_V5_NR_PRIVATE_IRQS) { + for_each_visible_v5_ppi(i, vcpu->kvm) { u32 intid = vgic_v5_make_ppi(i); struct vgic_irq *irq; @@ -429,8 +428,7 @@ void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu) * ICC_PPI_PENDRx_EL1, however. */ bitmap_zero(pendr, VGIC_V5_NR_PRIVATE_IRQS); - for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, - VGIC_V5_NR_PRIVATE_IRQS) { + for_each_visible_v5_ppi(i, vcpu->kvm) { u32 intid = vgic_v5_make_ppi(i); struct vgic_irq *irq; diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 9d941241c8a2..f45f7e3ec4d6 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -378,6 +378,9 @@ void vgic_v5_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); void vgic_v5_restore_state(struct kvm_vcpu *vcpu); void vgic_v5_save_state(struct kvm_vcpu *vcpu); +#define for_each_visible_v5_ppi(__i, __k) \ + for_each_set_bit(__i, (__k)->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) + static inline int vgic_v3_max_apr_idx(struct kvm_vcpu *vcpu) { struct vgic_cpu *cpu_if = &vcpu->arch.vgic_cpu; From 2e83ac3b3b1a1b3b248a4af07efb19a1acb29845 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:33 +0100 Subject: [PATCH 1511/5214] KVM: arm64: vgic-v5: Move PPI caps into kvm_vgic_global_state Constant vgic properties are usually kept in kvm_vgic_global_state, but the vgic-v5 code does its own thing. Move the ppi_caps data into the global structure, which has the modest additional advantage of making it ro_after_init. Link: https://lore.kernel.org/r/20260520091949.542365-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 2 +- include/kvm/arm_vgic.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index c0d36658ffe7..7c146fccc968 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -10,7 +10,7 @@ #include "vgic.h" -static struct vgic_v5_ppi_caps ppi_caps; +#define ppi_caps kvm_vgic_global_state.vgic_v5_ppi_caps /* * Not all PPIs are guaranteed to be implemented for GICv5. Deterermine which diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 1388dc6028a9..ea793479ab25 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -177,6 +177,11 @@ struct vgic_global { bool has_gcie_v3_compat; u32 ich_vtr_el2; + + /* GICv5 PPI capabilities */ + struct { + DECLARE_BITMAP(impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); + } vgic_v5_ppi_caps; }; extern struct vgic_global kvm_vgic_global_state; @@ -492,11 +497,6 @@ struct vgic_v5_cpu_if { struct gicv5_vpe gicv5_vpe; }; -/* What PPI capabilities does a GICv5 host have */ -struct vgic_v5_ppi_caps { - DECLARE_BITMAP(impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); -}; - struct vgic_cpu { /* CPU vif control registers for world switch */ union { From 2295f5eca95d86b98225795a9d4c529796615d53 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:34 +0100 Subject: [PATCH 1512/5214] KVM: arm64: vgic-v5: Remove use of __assign_bit() with a constant Using __assign_bit() is very useful when the value of the bit is not known at compile time. In all other cases, __set_bit() and __clear_bit() are the correct tool for the job. This also fixes an odd case of using VGIC_V5_NR_PRIVATE_IRQS as the bit value... Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-4-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 7c146fccc968..4d62b1c31fe8 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -25,13 +25,13 @@ static void vgic_v5_get_implemented_ppis(void) * If we have KVM, we have EL2, which means that we have support for the * EL1 and EL2 Physical & Virtual timers. */ - __assign_bit(GICV5_ARCH_PPI_CNTHP, ppi_caps.impl_ppi_mask, 1); - __assign_bit(GICV5_ARCH_PPI_CNTV, ppi_caps.impl_ppi_mask, 1); - __assign_bit(GICV5_ARCH_PPI_CNTHV, ppi_caps.impl_ppi_mask, 1); - __assign_bit(GICV5_ARCH_PPI_CNTP, ppi_caps.impl_ppi_mask, 1); + __set_bit(GICV5_ARCH_PPI_CNTHP, ppi_caps.impl_ppi_mask); + __set_bit(GICV5_ARCH_PPI_CNTV, ppi_caps.impl_ppi_mask); + __set_bit(GICV5_ARCH_PPI_CNTHV, ppi_caps.impl_ppi_mask); + __set_bit(GICV5_ARCH_PPI_CNTP, ppi_caps.impl_ppi_mask); /* The SW_PPI should be available */ - __assign_bit(GICV5_ARCH_PPI_SW_PPI, ppi_caps.impl_ppi_mask, 1); + __set_bit(GICV5_ARCH_PPI_SW_PPI, ppi_caps.impl_ppi_mask); /* The PMUIRQ is available if we have the PMU */ __assign_bit(GICV5_ARCH_PPI_PMUIRQ, ppi_caps.impl_ppi_mask, system_supports_pmuv3()); @@ -146,9 +146,7 @@ int vgic_v5_init(struct kvm *kvm) /* We only allow userspace to drive the SW_PPI, if it is implemented. */ bitmap_zero(kvm->arch.vgic.gicv5_vm.userspace_ppis, VGIC_V5_NR_PRIVATE_IRQS); - __assign_bit(GICV5_ARCH_PPI_SW_PPI, - kvm->arch.vgic.gicv5_vm.userspace_ppis, - VGIC_V5_NR_PRIVATE_IRQS); + __set_bit(GICV5_ARCH_PPI_SW_PPI, kvm->arch.vgic.gicv5_vm.userspace_ppis); bitmap_and(kvm->arch.vgic.gicv5_vm.userspace_ppis, kvm->arch.vgic.gicv5_vm.userspace_ppis, ppi_caps.impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); @@ -197,7 +195,7 @@ int vgic_v5_finalize_ppi_state(struct kvm *kvm) /* Expose PPIs with an owner or the SW_PPI, only */ scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { if (irq->owner || i == GICV5_ARCH_PPI_SW_PPI) { - __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, 1); + __set_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask); __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr, irq->config == VGIC_CONFIG_LEVEL); } From e6fdea20cffb0108e3d4b5af1c850cccc8e8866c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:35 +0100 Subject: [PATCH 1513/5214] KVM: arm64: vgic-v5: Drop pointless ARM64_HAS_GICV5_CPUIF check vgic_v5_get_implemented_ppis() can only be called when we have a GICv5, by construction. Remove the pointless check against ARM64_HAS_GICV5_CPUIF. Link: https://lore.kernel.org/r/20260520091949.542365-5-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 4d62b1c31fe8..0101ec3f5528 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -18,9 +18,6 @@ */ static void vgic_v5_get_implemented_ppis(void) { - if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) - return; - /* * If we have KVM, we have EL2, which means that we have support for the * EL1 and EL2 Physical & Virtual timers. From c4a1191f802792fe22fc261fa0e918d048915911 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:36 +0100 Subject: [PATCH 1514/5214] KVM: arm64: vgic: Constify struct irq_ops usage vgic-v5 has introduced much more prevalent usage of the struct irq_ops mechanism. In the process, it becomes evident that suffers from two related problems: - it contains flags, rather than only callbacks - it is mutable, because we need to update the above flags Swap the flags for a helper retrieving the flags, and make all irq_ops const, something that is slightly satisfying. Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-6-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 14 +++++++++----- arch/arm64/kvm/vgic/vgic-v5.c | 2 +- arch/arm64/kvm/vgic/vgic.c | 2 +- include/kvm/arm_vgic.h | 9 +++++---- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index cbea4d9ee955..f003df76fdda 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -52,11 +52,17 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu, enum kvm_arch_timer_regs treg); static bool kvm_arch_timer_get_input_level(int vintid); -static struct irq_ops arch_timer_irq_ops = { +static unsigned long kvm_arch_timer_get_irq_flags(void) +{ + return kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0; +} + +static const struct irq_ops arch_timer_irq_ops = { + .get_flags = kvm_arch_timer_get_irq_flags, .get_input_level = kvm_arch_timer_get_input_level, }; -static struct irq_ops arch_timer_irq_ops_vgic_v5 = { +static const struct irq_ops arch_timer_irq_ops_vgic_v5 = { .get_input_level = kvm_arch_timer_get_input_level, .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock, .set_direct_injection = vgic_v5_set_ppi_dvi, @@ -1392,8 +1398,6 @@ static int kvm_irq_init(struct arch_timer_kvm_info *info) return -ENOMEM; } - if (kvm_vgic_global_state.no_hw_deactivation) - arch_timer_irq_ops.flags |= VGIC_IRQ_SW_RESAMPLE; WARN_ON(irq_domain_push_irq(domain, host_vtimer_irq, (void *)TIMER_VTIMER)); } @@ -1591,8 +1595,8 @@ static bool kvm_arch_timer_get_input_level(int vintid) int kvm_timer_enable(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = vcpu_timer(vcpu); + const struct irq_ops *ops; struct timer_map map; - struct irq_ops *ops; int ret; if (timer->enabled) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 0101ec3f5528..757484d2493b 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -285,7 +285,7 @@ void vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, struct vgic_irq *irq, bool dvi) __assign_bit(ppi, cpu_if->vgic_ppi_dvir, dvi); } -static struct irq_ops vgic_v5_ppi_irq_ops = { +static const struct irq_ops vgic_v5_ppi_irq_ops = { .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock, .set_direct_injection = vgic_v5_set_ppi_dvi, }; diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 1e9fe8764584..3ac6d49bc487 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -573,7 +573,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, } void kvm_vgic_set_irq_ops(struct kvm_vcpu *vcpu, u32 vintid, - struct irq_ops *ops) + const struct irq_ops *ops) { struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index ea793479ab25..fe49fb56dc3c 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -205,7 +205,7 @@ struct vgic_irq; */ struct irq_ops { /* Per interrupt flags for special-cased interrupts */ - unsigned long flags; + unsigned long (*get_flags)(void); #define VGIC_IRQ_SW_RESAMPLE BIT(0) /* Clear the active state for resampling */ @@ -271,7 +271,7 @@ struct vgic_irq { u8 priority; u8 group; /* 0 == group 0, 1 == group 1 */ - struct irq_ops *ops; + const struct irq_ops *ops; void *owner; /* Opaque pointer to reserve an interrupt for in-kernel devices. */ @@ -279,7 +279,8 @@ struct vgic_irq { static inline bool vgic_irq_needs_resampling(struct vgic_irq *irq) { - return irq->ops && (irq->ops->flags & VGIC_IRQ_SW_RESAMPLE); + return irq->ops && irq->ops->get_flags && + (irq->ops->get_flags() & VGIC_IRQ_SW_RESAMPLE); } struct vgic_register_region; @@ -557,7 +558,7 @@ void kvm_vgic_init_cpu_hardware(void); int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, unsigned int intid, bool level, void *owner); void kvm_vgic_set_irq_ops(struct kvm_vcpu *vcpu, u32 vintid, - struct irq_ops *ops); + const struct irq_ops *ops); void kvm_vgic_clear_irq_ops(struct kvm_vcpu *vcpu, u32 vintid); int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, u32 vintid); From 849fbc130627663b4f7c8c4468025e4babc7a65a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:37 +0100 Subject: [PATCH 1515/5214] KVM: arm64: vgic: Consolidate vgic_allocate_private_irqs_locked() vgic_allocate_private_irqs_locked() calls two helpers, oddly named vgic_{,v5_}allocate_private_irq(). Not only these helpers don't allocate anything, but they also contain duplicate init code that would be better placed in the caller. Consolidate the common init code in the caller, rename the helpers to vgic_{,v5_}setup_private_irq(), and pass the irq pointer around instead of the index of the interrupt. Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-7-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-init.c | 45 +++++++++++++-------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 933983bb2005..907057881b26 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -271,18 +271,12 @@ int kvm_vgic_vcpu_nv_init(struct kvm_vcpu *vcpu) return ret; } -static void vgic_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) +static void vgic_setup_private_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, + u32 type) { - struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i]; + irq->intid = irq - &vcpu->arch.vgic_cpu.private_irqs[0]; - INIT_LIST_HEAD(&irq->ap_list); - raw_spin_lock_init(&irq->irq_lock); - irq->vcpu = NULL; - irq->target_vcpu = vcpu; - refcount_set(&irq->refcount, 0); - - irq->intid = i; - if (vgic_irq_is_sgi(i)) { + if (vgic_irq_is_sgi(irq->intid)) { /* SGIs */ irq->enabled = 1; irq->config = VGIC_CONFIG_EDGE; @@ -303,18 +297,11 @@ static void vgic_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) } } -static void vgic_v5_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) +static void vgic_v5_setup_private_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq) { - struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i]; - u32 intid = vgic_v5_make_ppi(i); + int i = irq - &vcpu->arch.vgic_cpu.private_irqs[0]; - INIT_LIST_HEAD(&irq->ap_list); - raw_spin_lock_init(&irq->irq_lock); - irq->vcpu = NULL; - irq->target_vcpu = vcpu; - refcount_set(&irq->refcount, 0); - - irq->intid = intid; + irq->intid = vgic_v5_make_ppi(i); /* The only Edge architected PPI is the SW_PPI */ if (i == GICV5_ARCH_PPI_SW_PPI) @@ -323,7 +310,7 @@ static void vgic_v5_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) irq->config = VGIC_CONFIG_LEVEL; /* Register the GICv5-specific PPI ops */ - vgic_v5_set_ppi_ops(vcpu, intid); + vgic_v5_set_ppi_ops(vcpu, irq->intid); } static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type) @@ -349,15 +336,19 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type) if (!vgic_cpu->private_irqs) return -ENOMEM; - /* - * Enable and configure all SGIs to be edge-triggered and - * configure all PPIs as level-triggered. - */ for (i = 0; i < num_private_irqs; i++) { + struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i]; + + INIT_LIST_HEAD(&irq->ap_list); + raw_spin_lock_init(&irq->irq_lock); + irq->vcpu = NULL; + irq->target_vcpu = vcpu; + refcount_set(&irq->refcount, 0); + if (vgic_is_v5(vcpu->kvm)) - vgic_v5_allocate_private_irq(vcpu, i, type); + vgic_v5_setup_private_irq(vcpu, irq); else - vgic_allocate_private_irq(vcpu, i, type); + vgic_setup_private_irq(vcpu, irq, type); } return 0; From 319c1ceef7d236e80f8a8e048cda1f986457d834 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:38 +0100 Subject: [PATCH 1516/5214] KVM: arm64: vgic-v5: Drop defensive checks from vgic_v5_ppi_queue_irq_unlock() vgic_v5_ppi_queue_irq_unlock() performs a bunch of sanity checks that are pretty pointless as there is no code path that can result in these invariants to be violated. And if they are, a nice crash is just as instructive than a warning. Drop what is evidently debug code and simplify the whole thing. Link: https://lore.kernel.org/r/20260520091949.542365-8-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 757484d2493b..7916bd8d564e 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -238,9 +238,9 @@ static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu *vcpu) /* * For GICv5, the PPIs are mostly directly managed by the hardware. We (the - * hypervisor) handle the pending, active, enable state save/restore, but don't - * need the PPIs to be queued on a per-VCPU AP list. Therefore, sanity check the - * state, unlock, and return. + * hypervisor) handle the pending, active, enable state save/restore, but + * don't need the PPIs to be queued on a per-VCPU AP list. Therefore, + * unlock, kick the vcpu and return. */ bool vgic_v5_ppi_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, unsigned long flags) @@ -250,12 +250,7 @@ bool vgic_v5_ppi_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, lockdep_assert_held(&irq->irq_lock); - if (WARN_ON_ONCE(!__irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, irq->intid))) - goto out_unlock_fail; - vcpu = irq->target_vcpu; - if (WARN_ON_ONCE(!vcpu)) - goto out_unlock_fail; raw_spin_unlock_irqrestore(&irq->irq_lock, flags); @@ -264,11 +259,6 @@ bool vgic_v5_ppi_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, kvm_vcpu_kick(vcpu); return true; - -out_unlock_fail: - raw_spin_unlock_irqrestore(&irq->irq_lock, flags); - - return false; } /* From 8f5dd53590b8a810a4004494bd2b07ad587464ed Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:39 +0100 Subject: [PATCH 1517/5214] KVM: arm64: vgic: Rationalise per-CPU irq accessor Despite adding the necessary infrastructure to identify irq types, vgic_get_vcpu_irq() treats GICv5 PPIs in a special way, which impairs the readability of the code. Use the existing irq classifiers to handle per-CPU irqs for all vgic types, and let the normal control flow reach global interrupt handling without any v5-specific path. Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-9-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 3ac6d49bc487..b697678d68b0 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -106,24 +106,23 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid) struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid) { + enum kvm_device_type type; + if (WARN_ON(!vcpu)) return NULL; - if (vgic_is_v5(vcpu->kvm)) { - u32 int_num, hwirq_id; + type = vcpu->kvm->arch.vgic.vgic_model; - if (!__irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, intid)) - return NULL; + if (__irq_is_sgi(type, intid) || __irq_is_ppi(type, intid)) { + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V5: + intid = vgic_v5_get_hwirq_id(intid); + intid = array_index_nospec(intid, VGIC_V5_NR_PRIVATE_IRQS); + break; + default: + intid = array_index_nospec(intid, VGIC_NR_PRIVATE_IRQS); + } - hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, intid); - int_num = array_index_nospec(hwirq_id, VGIC_V5_NR_PRIVATE_IRQS); - - return &vcpu->arch.vgic_cpu.private_irqs[int_num]; - } - - /* SGIs and PPIs */ - if (intid < VGIC_NR_PRIVATE_IRQS) { - intid = array_index_nospec(intid, VGIC_NR_PRIVATE_IRQS); return &vcpu->arch.vgic_cpu.private_irqs[intid]; } From 35a4f8d151d6aabb5e74fea4e67993dcad7b526b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 May 2026 10:19:40 +0100 Subject: [PATCH 1518/5214] KVM: arm64: vgic-v5: Limit support to 64 PPIs Although we have some code supporting 128 PPIs, the only supported configuration is 64 PPIs. There is no way to test the 128 PPI code, so it is bound to bitrot very quickly. Given that KVM/arm64's goal has always been to stick to non-IMPDEF behaviours, drop the 128 PPI support. Someone motivated enough and with very strong arguments can always bring it back -- it's all in the git history. Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-10-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/vgic-v5-sr.c | 78 +++++---------------------- arch/arm64/kvm/sys_regs.c | 17 +++--- arch/arm64/kvm/vgic/vgic-kvm-device.c | 9 ++-- 3 files changed, 24 insertions(+), 80 deletions(-) diff --git a/arch/arm64/kvm/hyp/vgic-v5-sr.c b/arch/arm64/kvm/hyp/vgic-v5-sr.c index 47e6bcd43702..6d69dfe89a96 100644 --- a/arch/arm64/kvm/hyp/vgic-v5-sr.c +++ b/arch/arm64/kvm/hyp/vgic-v5-sr.c @@ -30,10 +30,9 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) { /* * The following code assumes that the bitmap storage that we have for - * PPIs is either 64 (architected PPIs, only) or 128 bits (architected & - * impdef PPIs). + * PPIs is either 64 (architected PPIs, only). */ - BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); + BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS != 64); bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, read_sysreg_s(SYS_ICH_PPI_ACTIVER0_EL2), 0, 64); @@ -49,22 +48,6 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) cpu_if->vgic_ppi_priorityr[6] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR6_EL2); cpu_if->vgic_ppi_priorityr[7] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR7_EL2); - if (VGIC_V5_NR_PRIVATE_IRQS == 128) { - bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, - read_sysreg_s(SYS_ICH_PPI_ACTIVER1_EL2), 64, 64); - bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr, - read_sysreg_s(SYS_ICH_PPI_PENDR1_EL2), 64, 64); - - cpu_if->vgic_ppi_priorityr[8] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR8_EL2); - cpu_if->vgic_ppi_priorityr[9] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR9_EL2); - cpu_if->vgic_ppi_priorityr[10] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR10_EL2); - cpu_if->vgic_ppi_priorityr[11] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR11_EL2); - cpu_if->vgic_ppi_priorityr[12] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR12_EL2); - cpu_if->vgic_ppi_priorityr[13] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR13_EL2); - cpu_if->vgic_ppi_priorityr[14] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR14_EL2); - cpu_if->vgic_ppi_priorityr[15] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR15_EL2); - } - /* Now that we are done, disable DVI */ write_sysreg_s(0, SYS_ICH_PPI_DVIR0_EL2); write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); @@ -74,9 +57,6 @@ void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if) { DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); - /* We assume 64 or 128 PPIs - see above comment */ - BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); - /* Enable DVI so that the guest's interrupt config takes over */ write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 0, 64), SYS_ICH_PPI_DVIR0_EL2); @@ -108,50 +88,20 @@ void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if) write_sysreg_s(cpu_if->vgic_ppi_priorityr[7], SYS_ICH_PPI_PRIORITYR7_EL2); - if (VGIC_V5_NR_PRIVATE_IRQS == 128) { - /* Enable DVI so that the guest's interrupt config takes over */ - write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 64, 64), - SYS_ICH_PPI_DVIR1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); - write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_activer, 64, 64), - SYS_ICH_PPI_ACTIVER1_EL2); - write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_enabler, 64, 64), - SYS_ICH_PPI_ENABLER1_EL2); - write_sysreg_s(bitmap_read(pendr, 64, 64), - SYS_ICH_PPI_PENDR1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_ACTIVER1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_ENABLER1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PENDR1_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[8], - SYS_ICH_PPI_PRIORITYR8_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[9], - SYS_ICH_PPI_PRIORITYR9_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[10], - SYS_ICH_PPI_PRIORITYR10_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[11], - SYS_ICH_PPI_PRIORITYR11_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[12], - SYS_ICH_PPI_PRIORITYR12_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[13], - SYS_ICH_PPI_PRIORITYR13_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[14], - SYS_ICH_PPI_PRIORITYR14_EL2); - write_sysreg_s(cpu_if->vgic_ppi_priorityr[15], - SYS_ICH_PPI_PRIORITYR15_EL2); - } else { - write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); - - write_sysreg_s(0, SYS_ICH_PPI_ACTIVER1_EL2); - write_sysreg_s(0, SYS_ICH_PPI_ENABLER1_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PENDR1_EL2); - - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR8_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR9_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR10_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR11_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR12_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR13_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR14_EL2); - write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR15_EL2); - } + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR8_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR9_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR10_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR11_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR12_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR13_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR14_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR15_EL2); } void __vgic_v5_save_state(struct vgic_v5_cpu_if *cpu_if) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 513f5f1429b5..6083a1b23dbf 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -724,6 +724,7 @@ static bool access_gicv5_ppi_enabler(struct kvm_vcpu *vcpu, { unsigned long *mask = vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask; struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + unsigned long reg = p->regval; int i; /* We never expect to get here with a read! */ @@ -731,21 +732,17 @@ static bool access_gicv5_ppi_enabler(struct kvm_vcpu *vcpu, return undef_access(vcpu, p, r); /* - * If we're only handling architected PPIs and the guest writes to the - * enable for the non-architected PPIs, we just return as there's - * nothing to do at all. We don't even allocate the storage for them in - * this case. + * As we're only handling architected PPIs, the guest writes to the + * enable for the non-architected PPIs just return as there's + * nothing to do at all. We don't even allocate the storage for them. */ - if (VGIC_V5_NR_PRIVATE_IRQS == 64 && p->Op2 % 2) + if (p->Op2 % 2) return true; /* - * Merge the raw guest write into out bitmap at an offset of either 0 or - * 64, then and it with our PPI mask. + * Merge the raw guest write into out bitmap, anded with our PPI mask. */ - bitmap_write(cpu_if->vgic_ppi_enabler, p->regval, 64 * (p->Op2 % 2), 64); - bitmap_and(cpu_if->vgic_ppi_enabler, cpu_if->vgic_ppi_enabler, mask, - VGIC_V5_NR_PRIVATE_IRQS); + bitmap_and(cpu_if->vgic_ppi_enabler, ®, mask, VGIC_V5_NR_PRIVATE_IRQS); /* * Sync the change in enable states to the vgic_irqs. We consider all diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index a96c77dccf35..90be99443df3 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -730,18 +730,15 @@ static int vgic_v5_get_userspace_ppis(struct kvm_device *dev, guard(mutex)(&dev->kvm->arch.config_lock); /* - * We either support 64 or 128 PPIs. In the former case, we need to - * return 0s for the second 64 bits as we have no storage backing those. + * We only support 64 PPIs, so, we need to return 0s for the + * second 64 bits as we have no storage backing those. */ ret = put_user(bitmap_read(gicv5_vm->userspace_ppis, 0, 64), uaddr); if (ret) return ret; uaddr++; - if (VGIC_V5_NR_PRIVATE_IRQS == 128) - ret = put_user(bitmap_read(gicv5_vm->userspace_ppis, 64, 128), uaddr); - else - ret = put_user(0, uaddr); + ret = put_user(0, uaddr); return ret; } From a3fe1408e801b3406d2786557bcea8838973c036 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 27 Apr 2026 15:56:52 +0800 Subject: [PATCH 1519/5214] usb: chipidea: udc: add a helper ci_udc_enable_vbus_irq() The VBUS interrupt is configured in multiple places, add a helper function ci_udc_enable_vbus_irq() to simplify the code. Acked-by: Peter Chen Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260427075653.3611180-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index f2de86d0ce40..d4277d6611ee 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1835,6 +1835,20 @@ static const struct usb_ep_ops usb_ep_ops = { * GADGET block *****************************************************************************/ +static void ci_udc_enable_vbus_irq(struct ci_hdrc *ci, bool enable) +{ + u32 reg = OTGSC_BSVIS; + + if (!ci->is_otg) + return; + + if (enable) + reg |= OTGSC_BSVIE; + + /* Clear pending BSVIS and enable/disable BSVIE */ + hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, reg); +} + static int ci_udc_get_frame(struct usb_gadget *_gadget) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); @@ -2352,23 +2366,13 @@ static int udc_id_switch_for_device(struct ci_hdrc *ci) pinctrl_select_state(ci->platdata->pctl, ci->platdata->pins_device); - if (ci->is_otg) - /* Clear and enable BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, - OTGSC_BSVIS | OTGSC_BSVIE); - + ci_udc_enable_vbus_irq(ci, true); return 0; } static void udc_id_switch_for_host(struct ci_hdrc *ci) { - /* - * host doesn't care B_SESSION_VALID event - * so clear and disable BSV irq - */ - if (ci->is_otg) - hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); - + ci_udc_enable_vbus_irq(ci, false); ci->vbus_active = 0; if (ci->platdata->pins_device && ci->platdata->pins_default) @@ -2395,9 +2399,7 @@ static void udc_suspend(struct ci_hdrc *ci) static void udc_resume(struct ci_hdrc *ci, bool power_lost) { if (power_lost) { - if (ci->is_otg) - hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, - OTGSC_BSVIS | OTGSC_BSVIE); + ci_udc_enable_vbus_irq(ci, true); if (ci->vbus_active) usb_gadget_vbus_disconnect(&ci->gadget); } else if (ci->vbus_active && ci->driver && From dd0c03d685225cb8fc60c477417d1b9c3b05ba9d Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 27 Apr 2026 15:56:53 +0800 Subject: [PATCH 1520/5214] usb: chipidea: udc: support dynamic gadget add/remove An asynchronous vbus_event_work() keep running when switch the role from device to host. This affects EHCI host controller initialization. USBCMD.RUNSTOP bit is set at ehci_run() and cleared by following vbus_event_work() if bus_event_work() run after ehci_run(). The log below shows what happens: [ 87.819925] ci_hdrc ci_hdrc.0: EHCI Host Controller [ 87.819963] ci_hdrc ci_hdrc.0: new USB bus registered, assigned bus number 1 [ 87.955634] ci_hdrc ci_hdrc.0: USB 2.0, controller refused to start: -110 [ 87.955658] ci_hdrc ci_hdrc.0: startup error -110 [ 87.955682] ci_hdrc ci_hdrc.0: USB bus 1 deregistered The problem is that the chipidea UDC driver call usb_udc_vbus_handler() to pull down data line but it don't wait for completion before host controller starts running. Now UDC core can properly delete usb gadget device and make sure that vbus work is cancelled or completed after usb_del_gadget_udc() is returned. But the udc.c only call usb_del_gadget_udc() in ci_hdrc_gadget_destroy(). To avoid above issue, add/remove the gadget device dynamically during USB role switching. To support dynamic gadget add/remove, do below steps: - clear ci->gadget and ci->ci_hw_ep at initialization. - assign udc_[start|stop]() to rdrv->[start|stop] and properly merge the operations in udc_id_switch_for_[device|host]() to udc_[start|stop]() Adjust the order ci_handle_vbus_change() and ci_role_start() to avoid NULL pointer reference since ci_hdrc_gadget_init() doesn't add gadget anymore. Acked-by: Peter Chen Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260427075653.3611180-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 11 +++---- drivers/usb/chipidea/udc.c | 65 +++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7cfabb04a4fb..95d9db159ce8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1191,19 +1191,16 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->role = ci_get_role(ci); if (!ci_otg_is_fsm_mode(ci)) { - /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) { - /* Pull down DP for possible charger detection */ - hw_write(ci, OP_USBCMD, USBCMD_RS, 0); - ci_handle_vbus_change(ci); - } - ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", ci_role(ci)->name); goto stop; } + + /* only update vbus status for peripheral */ + if (ci->role == CI_ROLE_GADGET) + ci_handle_vbus_change(ci); } ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index d4277d6611ee..d52f89489893 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -2044,6 +2044,8 @@ static int init_eps(struct ci_hdrc *ci) { int retval = 0, i, j; + memset(ci->ci_hw_ep, 0, sizeof(ci->ci_hw_ep)); + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { int k = i + j * ci->hw_ep_max/2; @@ -2289,6 +2291,8 @@ static int udc_start(struct ci_hdrc *ci) struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps; int retval = 0; + memset(&ci->gadget, 0, sizeof(ci->gadget)); + ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; @@ -2327,10 +2331,15 @@ static int udc_start(struct ci_hdrc *ci) ci->gadget.ep0 = &ci->ep0in->ep; + if (ci->platdata->pins_device) + pinctrl_select_state(ci->platdata->pctl, + ci->platdata->pins_device); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) goto destroy_eps; + ci_udc_enable_vbus_irq(ci, true); return retval; destroy_eps: @@ -2342,38 +2351,20 @@ static int udc_start(struct ci_hdrc *ci) return retval; } -/* - * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC - * - * No interrupts active, the IRQ has been released +/** + * udc_stop: deinitialize gadget role + * @ci: chipidea controller */ -void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +static void udc_stop(struct ci_hdrc *ci) { - if (!ci->roles[CI_ROLE_GADGET]) - return; - + ci_udc_enable_vbus_irq(ci, false); usb_del_gadget_udc(&ci->gadget); + ci->vbus_active = 0; destroy_eps(ci); dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); -} - -static int udc_id_switch_for_device(struct ci_hdrc *ci) -{ - if (ci->platdata->pins_device) - pinctrl_select_state(ci->platdata->pctl, - ci->platdata->pins_device); - - ci_udc_enable_vbus_irq(ci, true); - return 0; -} - -static void udc_id_switch_for_host(struct ci_hdrc *ci) -{ - ci_udc_enable_vbus_irq(ci, false); - ci->vbus_active = 0; if (ci->platdata->pins_device && ci->platdata->pins_default) pinctrl_select_state(ci->platdata->pctl, @@ -2422,7 +2413,6 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost) int ci_hdrc_gadget_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; - int ret; if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC)) return -ENXIO; @@ -2431,8 +2421,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) if (!rdrv) return -ENOMEM; - rdrv->start = udc_id_switch_for_device; - rdrv->stop = udc_id_switch_for_host; + rdrv->start = udc_start; + rdrv->stop = udc_stop; #ifdef CONFIG_PM_SLEEP rdrv->suspend = udc_suspend; rdrv->resume = udc_resume; @@ -2440,9 +2430,22 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) rdrv->irq = udc_irq; rdrv->name = "gadget"; - ret = udc_start(ci); - if (!ret) - ci->roles[CI_ROLE_GADGET] = rdrv; + ci->roles[CI_ROLE_GADGET] = rdrv; - return ret; + /* Pull down DP for possible charger detection */ + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + return 0; +} + +/* + * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC + * + * No interrupts active, the IRQ has been released + */ +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +{ + struct device *dev = &ci->gadget.dev; + + if (ci->roles[CI_ROLE_GADGET] && device_is_registered(dev)) + udc_stop(ci); } From d5559f43d76b398392b26a15cbc16d731969cd1c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 29 Apr 2026 11:44:04 +0200 Subject: [PATCH 1521/5214] usb: core: hcd: fix possible deadlock in rh control transfers >From within the SCSI error handler memory allocations must not trigger IO. Handling errors in UAS and the storage driver may involve resetting a device. The thread doing the reset itself relies on VM magic. However, that is insufficient, as resetting a device involves resuming it. Resumption as well as resetting involves conrol transfers to the parent of the device to be reset. That may be a root hub. Hence usbcore must heed the flags passed to usb_submit_urb() processing control transfers to root hubs. The problem exist since the storage driver has been merged. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20260429094413.181038-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 89221f1ce769..29c74ed40526 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -448,7 +448,8 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len) /* Root hub control transfers execute synchronously */ -static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) +static int rh_call_control(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; @@ -483,8 +484,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) * tbuf should be at least as big as the * USB hub descriptor. */ - tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); - tbuf = kzalloc(tbuf_size, GFP_KERNEL); + tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); + tbuf = kzalloc(tbuf_size, mem_flags); if (!tbuf) { status = -ENOMEM; goto err_alloc; @@ -809,12 +810,13 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) return retval; } -static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) +static int rh_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { if (usb_endpoint_xfer_int(&urb->ep->desc)) return rh_queue_status (hcd, urb); if (usb_endpoint_xfer_control(&urb->ep->desc)) - return rh_call_control (hcd, urb); + return rh_call_control(hcd, urb, mem_flags); return -EINVAL; } @@ -1535,7 +1537,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) */ if (is_root_hub(urb->dev)) { - status = rh_urb_enqueue(hcd, urb); + status = rh_urb_enqueue(hcd, urb, mem_flags); } else { status = map_urb_for_dma(hcd, urb, mem_flags); if (likely(status == 0)) { From bc150783542ba2e7c1257d1299c6f3269bdba270 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Mon, 13 Apr 2026 20:00:49 -0500 Subject: [PATCH 1522/5214] usbip: vhci_hcd: fix NULL deref in status_show_vhci platform_get_drvdata() can return NULL if a VHCI host controller's probe failed (e.g. due to USB bus number exhaustion). status_show_vhci() checked for a NULL pdev but not for a NULL hcd returned by platform_get_drvdata(). Passing NULL to hcd_to_vhci_hcd() does not return NULL - it returns a pointer offset of 0x260, causing a NULL pointer dereference when that value is subsequently dereferenced. Add a NULL check on hcd before calling hcd_to_vhci_hcd(). Move status_show_not_ready() above status_show_vhci() to make it callable from the new error path without a forward declaration. Signed-off-by: Adrian Wowk Reviewed-by: Shuah Khan Link: https://patch.msgid.link/20260414010050.158064-2-dev@adrianwowk.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_sysfs.c | 52 +++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 5bc8c47788d4..e4cde1645b90 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -59,6 +59,29 @@ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vd *out += sprintf(*out, "\n"); } +static ssize_t status_show_not_ready(int pdev_nr, char *out) +{ + char *s = out; + int i = 0; + + for (i = 0; i < VHCI_HC_PORTS; i++) { + out += sprintf(out, "hs %04u %03u ", + (pdev_nr * VHCI_PORTS) + i, + VDEV_ST_NOTASSIGNED); + out += sprintf(out, "000 00000000 0000000000000000 0-0"); + out += sprintf(out, "\n"); + } + + for (i = 0; i < VHCI_HC_PORTS; i++) { + out += sprintf(out, "ss %04u %03u ", + (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, + VDEV_ST_NOTASSIGNED); + out += sprintf(out, "000 00000000 0000000000000000 0-0"); + out += sprintf(out, "\n"); + } + return out - s; +} + /* Sysfs entry to show port status */ static ssize_t status_show_vhci(int pdev_nr, char *out) { @@ -76,6 +99,12 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) } hcd = platform_get_drvdata(pdev); + + if (!hcd) { + usbip_dbg_vhci_sysfs("show status error (hcd is NULL)\n"); + return status_show_not_ready(pdev_nr, out); + } + vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; @@ -104,29 +133,6 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) return out - s; } -static ssize_t status_show_not_ready(int pdev_nr, char *out) -{ - char *s = out; - int i = 0; - - for (i = 0; i < VHCI_HC_PORTS; i++) { - out += sprintf(out, "hs %04u %03u ", - (pdev_nr * VHCI_PORTS) + i, - VDEV_ST_NOTASSIGNED); - out += sprintf(out, "000 00000000 0000000000000000 0-0"); - out += sprintf(out, "\n"); - } - - for (i = 0; i < VHCI_HC_PORTS; i++) { - out += sprintf(out, "ss %04u %03u ", - (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, - VDEV_ST_NOTASSIGNED); - out += sprintf(out, "000 00000000 0000000000000000 0-0"); - out += sprintf(out, "\n"); - } - return out - s; -} - static int status_name_to_id(const char *name) { char *c; From 105644154a60c8ec0c7b3e0758bd18c9a243a3b4 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Mon, 13 Apr 2026 20:00:50 -0500 Subject: [PATCH 1523/5214] usbip: vhci_hcd: reduce CONFIG_USBIP_VHCI_NR_HCS upper bound to 32 Each VHCI HC instance registers two USB buses (one HS, one SS). USB_MAXBUS in drivers/usb/core/hcd.c is hard-coded to 64, giving an effective maximum of 32 VHCI HC instances (32 * 2 = 64 buses). The Kconfig range for USBIP_VHCI_NR_HCS currently allows up to 128, which will cause probe failures for any HC instance beyond the 32nd. These probe failures trigger the NULL pointer dereference fixed in the previous commit. Reduce the upper bound to 32 to reflect the real maximum imposed by USB_MAXBUS. Note that probe failures can still occur below this limit if real hardware has already claimed enough USB bus numbers, making the NULL check fix necessary regardless. Signed-off-by: Adrian Wowk Reviewed-by: Shuah Khan Link: https://patch.msgid.link/20260414010050.158064-3-dev@adrianwowk.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig index b9f94e2e278d..50945b6fae1a 100644 --- a/drivers/usb/usbip/Kconfig +++ b/drivers/usb/usbip/Kconfig @@ -40,7 +40,7 @@ config USBIP_VHCI_HC_PORTS config USBIP_VHCI_NR_HCS int "Number of USB/IP virtual host controllers" - range 1 128 + range 1 32 default 1 depends on USBIP_VHCI_HCD help From c708d07ce70655d20350487ac2e2bc2a1f4f5038 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 6 May 2026 14:05:54 +0800 Subject: [PATCH 1524/5214] usb: typec: intel_pmc_mux: Zero initialize num_ports in pmc_usb_probe() Clang warns (or errors with CONFIG_WERROR=y / W=e): drivers/usb/typec/mux/intel_pmc_mux.c:740:3: error: variable 'num_ports' is uninitialized when used here [-Werror,-Wuninitialized] 740 | num_ports++; | ^~~~~~~~~ This should have been initialized to zero. Do so now to clean up the warning and ensure num_ports does not use uninitialized memory. Fixes: 8bdb0b3830ea ("usb: typec: intel_pmc_mux: combine kzalloc + kcalloc") Reported-by: kernelci.org bot Closes: https://lore.kernel.org/177793914437.2560.9287713196857718000@997d03828cfd/ Signed-off-by: Nathan Chancellor Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260506-typec-intel_pmc_mux-fix-uninit-num_ports-v1-1-929b128a32e9@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index e22b070a140f..219a32da1348 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -732,7 +732,7 @@ static int pmc_usb_probe(struct platform_device *pdev) { struct fwnode_handle *fwnode = NULL; struct pmc_usb *pmc; - u8 num_ports; + u8 num_ports = 0; int i = 0; int ret; From 5bf5e3fba9bc7dfd69701521dbe9809f8ccbdb02 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 9 May 2026 16:06:36 +0500 Subject: [PATCH 1525/5214] usb: gadget: goku_udc: avoid NULL deref of dev->driver in INT_USBRESET log goku_irq() handles a number of bus events under a single ep0 path. It already guards the gadget driver suspend/resume callbacks against a NULL ->driver: if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume) { spin_unlock(&dev->lock); dev->driver->resume(&dev->gadget); ... } but the very next branch unconditionally dereferences dev->driver when an INT_USBRESET arrives: if (stat & INT_USBRESET) { ACK(INT_USBRESET); INFO(dev, "USB reset done, gadget %s\n", dev->driver->driver.name); } If the controller raises INT_USBRESET before any gadget driver has been bound (or after one has been unbound), dev->driver is NULL and the printk dereferences NULL. smatch flags the inconsistency: drivers/usb/gadget/udc/goku_udc.c:1618 goku_irq() error: we previously assumed 'dev->driver' could be null (see line 1607) Fall back to a placeholder when the gadget driver is not bound. No functional change while a gadget driver is bound. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260509110636.19762-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/goku_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index db42a5e3e805..ac2a984c2f87 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1616,7 +1616,8 @@ static irqreturn_t goku_irq(int irq, void *_dev) if (stat & INT_USBRESET) { /* hub reset done */ ACK(INT_USBRESET); INFO(dev, "USB reset done, gadget %s\n", - dev->driver->driver.name); + dev->driver ? dev->driver->driver.name : + ""); } // and INT_ERR on some endpoint's crc/bitstuff/... problem } From e663be4218d32d5d58374f8bed5bdfe3d2161932 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 14 May 2026 09:01:14 +0800 Subject: [PATCH 1526/5214] usb: cdns3: plat: fix leaked role switch on core role initialization failure Calling usb_role_switch_unregister if core role initialization failure. Fixes: e4d7362dc9cd ("usb: cdns3: Add USBSSP platform driver support") Reported-by: sashiko-bot Closes: https://lore.kernel.org/linux-devicetree/agKaEePSFknhDBg2@nchen-desktop/T/#m21e1d9c1574eb127ce03c0c2a1a49002ce435b52 Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260514010114.2436781-2-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 3fe3109a3688..91159910fe95 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -167,7 +167,7 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->host_init = cdns3_plat_host_init; ret = cdns_core_init_role(cdns); if (ret) - goto err_cdns_init; + goto err_cdns_init_role; device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); @@ -186,6 +186,9 @@ static int cdns3_plat_probe(struct platform_device *pdev) return 0; +err_cdns_init_role: + if (cdns->role_sw) + usb_role_switch_unregister(cdns->role_sw); err_cdns_init: set_phy_power_off(cdns); err_phy_power_on: From e5ab27ddd74e2d67a94c51c6f2ad87b1ff13912b Mon Sep 17 00:00:00 2001 From: Dave Carey Date: Fri, 15 May 2026 10:19:40 -0400 Subject: [PATCH 1527/5214] USB: cdc-acm: start bulk-IN polling when ALWAYS_POLL_CTRL is set The INGENIC 17EF:6161 touchscreen composite device has a ~55-second watchdog that resets the USB device if the bulk-IN endpoint on the CDC data interface goes unread. The existing ALWAYS_POLL_CTRL quirk keeps the notification endpoint (ctrlurb / EP 0x82) polling continuously, but that alone is insufficient: the firmware monitors bulk-IN activity, not just notification-endpoint activity. Add acm_submit_read_urbs() calls to the two ALWAYS_POLL_CTRL paths that already restart the ctrlurb: 1. acm_probe(): start bulk reads at probe time alongside the ctrlurb, so the watchdog is satisfied from first bind without requiring a userspace process to open /dev/ttyACMn. 2. acm_port_shutdown(): restart bulk reads after port close alongside the ctrlurb restart, so the watchdog keeps running when the last TTY user closes the port. acm_read_bulk_callback() already resubmits each URB unconditionally on normal completion, so once submitted the reads remain active until an explicit kill (disconnect, suspend). acm_submit_read_urb() is a no-op for URBs that are already in flight (read_urbs_free bit clear), so the existing acm_port_activate() call remains correct and races are avoided. Tested on Lenovo Yoga Book 9 14IAH10 (83KJ): without this patch the device resets every ~55 s when no TTY is open; with it the device remains stable indefinitely. Signed-off-by: Dave Carey Link: https://patch.msgid.link/20260515141940.751397-1-carvsdriver@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 54059e4fc6ed..0c6cdf553206 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -799,6 +799,9 @@ static void acm_port_shutdown(struct tty_port *port) "ctrl polling restart failed after port close\n"); /* port_shutdown() cleared DTR/RTS; restore them */ acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + if (acm_submit_read_urbs(acm, GFP_KERNEL)) + dev_dbg(&acm->control->dev, + "read urb restart failed after port close\n"); } } @@ -1566,6 +1569,9 @@ static int acm_probe(struct usb_interface *intf, if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) dev_warn(&intf->dev, "failed to start persistent ctrl polling\n"); + if (acm_submit_read_urbs(acm, GFP_KERNEL)) + dev_warn(&intf->dev, + "failed to start persistent bulk read polling\n"); } return 0; From 7fd6fb1fe40b992b2c39c3f586624ad93ee66c22 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Soemardi Date: Sat, 16 May 2026 10:53:14 +0000 Subject: [PATCH 1528/5214] dt-bindings: usb: ci-hdrc-usb2: allow up to 3 clocks for qcom,ci-hdrc Some Qualcomm SoCs such as apq8064 and msm8960 require a third "fs" clock in addition to "iface" and "core", needed to propagate resets through the controller and wrapper logic. Later SoCs such as msm8974 dropped this requirement and only use two clocks. Note that the existing apq8064 and msm8960 DTS files currently specify the "iface" and "core" clocks in reverse order compared to most newer SoCs DTS, which causes dtbs_check warnings for these older SoCs. The dependent patch series will fix that clock ordering. Signed-off-by: Antony Kurniawan Soemardi Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260516-qcom-ci-hdrc-clock-fix-v2-1-aaec8d33d0aa@smankusors.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/ci-hdrc-usb2.yaml | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index 691d6cf02c27..a4575a413f42 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -45,11 +45,11 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 3 clock-names: minItems: 1 - maxItems: 2 + maxItems: 3 operating-points-v2: description: A phandle to the OPP table containing the performance states. @@ -91,6 +91,27 @@ allOf: - $ref: chipidea,usb2-common.yaml# - $ref: usb-hcd.yaml# - $ref: usb-drd.yaml# + - if: + properties: + compatible: + contains: + const: qcom,ci-hdrc + then: + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + items: + - const: iface + - const: core + - const: fs + else: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 unevaluatedProperties: false From 8c22256bbafad3dc5fdbe9f684d045b67ff06a68 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Fri, 15 May 2026 15:00:30 +0900 Subject: [PATCH 1529/5214] usb: typec: ucsi: split connector lock classes Lockdep detects a possible recursive locking scenario during ucsi init: [ 5.418616] ============================================ [ 5.418634] WARNING: possible recursive locking detected [ 5.418706] -------------------------------------------- [ 5.418725] kworker/4:1/82 is trying to acquire lock: [ 5.418759] ffff888119a34648 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] [ 5.418801] but task is already holding lock: [ 5.418835] ffff888119a34080 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] [ 5.418884] other info that might help us debug this: [ 5.418904] Possible unsafe locking scenario: [ 5.418937] CPU0 [ 5.418956] ---- [ 5.418991] lock(&con->lock); [ 5.419013] lock(&con->lock); [ 5.419033] *** DEADLOCK *** [ 5.419387] Call Trace: [ 5.419406] [ 5.419425] dump_stack_lvl+0x61/0xa0 [ 5.419448] print_deadlock_bug+0x4a6/0x650 [ 5.419483] __lock_acquire+0x62b6/0x7f50 [ 5.419507] lock_acquire+0x11b/0x390 [ 5.419654] __mutex_lock+0xbc/0xcd0 [ 5.419741] ucsi_init_work+0x1a78/0x2eb0 [ 5.419785] ? worker_thread+0xf53/0x2bc0 [ 5.419819] worker_thread+0xff4/0x2bc0 [ 5.419842] kthread+0x2a7/0x330 [ 5.419863] ? __pfx_worker_thread+0x10/0x10 [ 5.419896] ? __pfx_kthread+0x10/0x10 [ 5.419916] ret_from_fork+0x38/0x70 [ 5.419936] ? __pfx_kthread+0x10/0x10 [ 5.419969] ret_from_fork_asm+0x1b/0x30 [ 5.419991] [ 5.420009] ---[ end trace 0000000000000000 ]--- The problem is that all connector locks belong to the same lockdep lock class, so the following loop: for (i = 0; i < ucsi->cap.num_connectors; i++) ucsi_register_port(connector[i]) mutex_lock(&connector[i]->lock) looks like a recursive acquire of the same mutex. Put each connector lock into a dedicated lock class so that lockdep doesn't see it as a possible recursion. Signed-off-by: Sergey Senozhatsky Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260515060042.136083-1-senozhatsky@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 8 ++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 5b7ad9e99cb9..43da7512dea0 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1642,6 +1642,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) INIT_WORK(&con->work, ucsi_handle_connector_change); init_completion(&con->complete); mutex_init(&con->lock); + lockdep_set_class(&con->lock, &con->lock_key); INIT_LIST_HEAD(&con->partner_tasks); con->ucsi = ucsi; @@ -1887,6 +1888,9 @@ static int ucsi_init(struct ucsi *ucsi) goto err_reset; } + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_register_key(&connector[i].lock_key); + /* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { connector[i].num = i + 1; @@ -1916,6 +1920,9 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_unregister_key(&connector[i].lock_key); + for (con = connector; con->port; con++) { if (con->wq) destroy_workqueue(con->wq); @@ -2166,6 +2173,7 @@ void ucsi_unregister(struct ucsi *ucsi) usb_power_delivery_unregister(ucsi->connector[i].pd); ucsi->connector[i].pd = NULL; typec_unregister_port(ucsi->connector[i].port); + lockdep_unregister_key(&ucsi->connector[i].lock_key); } kfree(ucsi->connector); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index cff9ddc2ae21..51f6c3c0d365 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -517,6 +517,7 @@ struct ucsi_connector { struct ucsi *ucsi; struct mutex lock; /* port lock */ + struct lock_class_key lock_key; struct work_struct work; struct completion complete; struct workqueue_struct *wq; From cff06b03b530ae1fe8a13e93a7848f2130e00fb4 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:00 -0400 Subject: [PATCH 1530/5214] usb: host: max3421: Fix shift-out-of-bounds in max3421_hub_control() The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. In the `default` branches of both the `ClearPortFeature` and `SetPortFeature` switch statements, it modifies `max3421_hcd->port_status` by left shifting 1 by the request's `value` parameter. However, it does not validate whether this shift will exceed the width of `port_status`. So if a malicious userspace task with access to the root hub via /dev/bus/usb/.../001 issues a USBDEVFS_CONTROL ioctl with `wValue` greater than or equal to 32, the left shift operation invokes shift-out-of-bounds undefined behavior. This results in arbitrary bit corruption of `port_status`, including the normally-immutable change bits, which can bypass internal state checks and confuse the hub status. Fix this by rejecting requests whose `value` exceeds the shift width before performing the shift. This issue was found using a KLEE-based symbolic execution tool for kernel drivers that I'm currently developing. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0e17c988d36a..3d6b351dcb1a 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1694,6 +1694,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, !pdata->vbus_active_level); fallthrough; default: + if (value >= 32) + goto error; max3421_hcd->port_status &= ~(1 << value); } break; @@ -1747,6 +1749,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, max3421_reset_port(hcd); fallthrough; default: + if (value >= 32) + goto error; if ((max3421_hcd->port_status & USB_PORT_STAT_POWER) != 0) max3421_hcd->port_status |= (1 << value); From 11b5c101e2fd206104b05bc92554d356989423a8 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:02 -0400 Subject: [PATCH 1531/5214] usb: host: max3421: Reject hub port requests for non-existent ports The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. The `GetPortStatus` case correctly rejects requests with `index != 1`, since the virtual root hub has only a single port. However, the `ClearPortFeature` and `SetPortFeature` cases lack the same check. Fix this by extending the `index != 1` rejection to both cases, matching the existing behavior of `GetPortStatus`. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Suggested-by: Alan Stern Reviewed-by: Alan Stern Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-3-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 3d6b351dcb1a..73e76d0e6973 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, case ClearHubFeature: break; case ClearPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_SUSPEND: break; @@ -1728,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, break; case SetPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_LINK_STATE: case USB_PORT_FEAT_U1_TIMEOUT: From 4fb7e6b46e46d6545d99f94c37c34ab3a9dca081 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 May 2026 12:00:15 +0200 Subject: [PATCH 1532/5214] dt-bindings: usb: qcom,pmic-typec: Drop redundant port The binding defines both "port" and "connector" properties, where the "port" is claimed to be for "data-role switching messages". There is no such dedicated data port for this device and role switching is part of connector ports - the port going to the USB controller. The driver does not use the "port" property and there is no upstream DTS which would have it. It looks like it's left-over of early versions of this patchset and is completely redundant now, so let's drop it. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519100014.282058-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml index 6d3fa2bc9cee..975032ba6004 100644 --- a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml @@ -84,11 +84,6 @@ properties: vdd-pdphy-supply: description: VDD regulator supply to the PDPHY. - port: - $ref: /schemas/graph.yaml#/properties/port - description: - Contains a port which produces data-role switching messages. - required: - compatible - reg From 5092914dd92efd38462d24f8119fdb781a693c81 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 May 2026 12:00:16 +0200 Subject: [PATCH 1533/5214] USB: typec: qcom-pmic-typec: Drop redundant header includes Unlike other units in this module, this one does not request interrupts or regulator supplies. It does not use OF graph, USB role switching or TypeC muxing APIs. Drop redundant header includes to speed up preprocessor. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Heikki Krogerus Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519100014.282058-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index 3766790c1548..35320f89dad2 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -4,19 +4,14 @@ */ #include -#include #include #include #include #include -#include #include #include -#include #include -#include #include -#include #include From fe2195b019f622258d43d926ba9f10894e4eee09 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 19 May 2026 13:48:03 +0300 Subject: [PATCH 1534/5214] dt-bindings: usb: qcom,pmic-typec: deprecate device-specific VBUS The Qualcomm PMIC Type-C devices historically provided their own way of specifying the VBUS regulator, via the device's vdd-vbus-supply node. This is not ideal as the VBUS is supplied to the connector and not to the Type-C block in the PMIC. Deprecate this property in favour of the standard way of specifying it (via the connector's vbus-supply property). Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-1-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml index 975032ba6004..ba790c4488b7 100644 --- a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml @@ -79,7 +79,8 @@ properties: - const: fr-swap vdd-vbus-supply: - description: VBUS power supply. + deprecated: true + description: use connector/vbus-supply instead. vdd-pdphy-supply: description: VDD regulator supply to the PDPHY. @@ -89,7 +90,6 @@ required: - reg - interrupts - interrupt-names - - vdd-vbus-supply allOf: - if: From 506927b6bf291c521e931f946e0e0c384baf347d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 19 May 2026 13:48:04 +0300 Subject: [PATCH 1535/5214] usb: typec: tcpm: qcom: prefer VBUS supply from the connector node Current way of specifying VBUS supply (via the device's vdd-vbus-supply property) is not ideal. In the end, VBUS is supplied to the USB-C connector rather than the Type-C block in the PMIC. Follow the standard way of specifying it (via the connector node) and fallback to the old property if there is no vbus-supply in the connector node. Signed-off-by: Dmitry Baryshkov Reviewed-by: Heikki Krogerus Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-2-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index 8051eaa46991..429bd42a0e62 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -704,6 +705,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev, struct device *dev = &pdev->dev; struct pmic_typec_port_irq_data *irq_data; struct pmic_typec_port *pmic_typec_port; + struct fwnode_handle *connector; int i, ret, irq; pmic_typec_port = devm_kzalloc(dev, sizeof(*pmic_typec_port), GFP_KERNEL); @@ -720,7 +722,15 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev, mutex_init(&pmic_typec_port->vbus_lock); - pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus"); + connector = device_get_named_child_node(dev, "connector"); + if (!connector) + return -EINVAL; + + pmic_typec_port->vdd_vbus = devm_of_regulator_get_optional(dev, + to_of_node(connector), + "vbus"); + if (pmic_typec_port->vdd_vbus == ERR_PTR(-ENODEV)) + pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus"); if (IS_ERR(pmic_typec_port->vdd_vbus)) return PTR_ERR(pmic_typec_port->vdd_vbus); From f28560fc0275372b2198fa3f7ab162c3f1db002a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 19 May 2026 13:48:05 +0300 Subject: [PATCH 1536/5214] arm64: dts: qcom: pm4125: move vdd-vbus-supply to connector nodes Instead of specifying the VBUS supply as powering on the Type-C block in the PMIC, follow the standard schema and use vbus-supply property of the usb-c connector itself. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-3-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/pm4125.dtsi | 1 - arch/arm64/boot/dts/qcom/qrb2210-rb1.dts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/pm4125.dtsi b/arch/arm64/boot/dts/qcom/pm4125.dtsi index cf8c822e80ce..542e8fe030da 100644 --- a/arch/arm64/boot/dts/qcom/pm4125.dtsi +++ b/arch/arm64/boot/dts/qcom/pm4125.dtsi @@ -61,7 +61,6 @@ pm4125_typec: typec@1500 { "attach-detach", "legacy-cable-detect", "try-snk-src-detect"; - vdd-vbus-supply = <&pm4125_vbus>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts index da46e9d65528..4ace2d6c06ce 100644 --- a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts +++ b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts @@ -409,6 +409,8 @@ connector { typec-power-opmode = "default"; pd-disable; + vbus-supply = <&pm4125_vbus>; + ports { #address-cells = <1>; #size-cells = <0>; From 8eb14b34ba3a2631413336656e91149b987cb089 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 19 May 2026 13:48:06 +0300 Subject: [PATCH 1537/5214] arm64: dts: qcom: pm7250b: move vdd-vbus-supply to connector nodes Instead of specifying the VBUS supply as powering on the Type-C block in the PMIC, follow the standard schema and use vbus-supply property of the usb-c connector itself. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-4-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/pm7250b.dtsi | 1 - arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/pm7250b.dtsi b/arch/arm64/boot/dts/qcom/pm7250b.dtsi index 0761e6b5fd8d..43cab07126c5 100644 --- a/arch/arm64/boot/dts/qcom/pm7250b.dtsi +++ b/arch/arm64/boot/dts/qcom/pm7250b.dtsi @@ -86,7 +86,6 @@ pm7250b_typec: typec@1500 { "msg-tx-discarded", "msg-rx-discarded", "fr-swap"; - vdd-vbus-supply = <&pm7250b_vbus>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts index 3964aae47fd4..23f950067a08 100644 --- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts +++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts @@ -953,6 +953,8 @@ connector { typec-power-opmode = "default"; pd-disable; + vbus-supply = <&pm7250b_vbus>; + ports { #address-cells = <1>; #size-cells = <0>; From b5817fa4026c83818c2f6c0659158d8bf5bb59c3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 19 May 2026 13:48:07 +0300 Subject: [PATCH 1538/5214] arm64: dts: qcom: pm8150b: move vdd-vbus-supply to connector nodes Instead of specifying the VBUS supply as powering on the Type-C block in the PMIC, follow the standard schema and use vbus-supply property of the usb-c connector itself. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-5-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/pm8150b.dtsi | 1 - arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 2 ++ arch/arm64/boot/dts/qcom/sm8150-hdk.dts | 2 ++ arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi index 3f7b0b6a1d10..b83be4b6cb1c 100644 --- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi @@ -95,7 +95,6 @@ pm8150b_typec: typec@1500 { "msg-tx-discarded", "msg-rx-discarded", "fr-swap"; - vdd-vbus-supply = <&pm8150b_vbus>; }; pm8150b_temp: temp-alarm@2400 { diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts index 54da0d759a67..690b484352ed 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -1499,6 +1499,8 @@ connector { data-role = "dual"; self-powered; + vbus-supply = <&pm8150b_vbus>; + source-pdos = ; + source-pdos = ; + source-pdos = Date: Tue, 19 May 2026 13:48:08 +0300 Subject: [PATCH 1539/5214] arm64: dts: qcom: pmi632: move vdd-vbus-supply to connector nodes Instead of specifying the VBUS supply as powering on the Type-C block in the PMIC, follow the standard schema and use vbus-supply property of the usb-c connector itself. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519-fix-tcpm-vbus-v1-6-14754695282d@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/pmi632.dtsi | 1 - arch/arm64/boot/dts/qcom/qrb4210-rb2.dts | 2 ++ arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts | 2 ++ arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi index 8c899d148e46..b0ed35094a98 100644 --- a/arch/arm64/boot/dts/qcom/pmi632.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi @@ -69,7 +69,6 @@ pmi632_typec: typec@1500 { "attach-detach", "legacy-cable-detect", "try-snk-src-detect"; - vdd-vbus-supply = <&pmi632_vbus>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts index 1203172729fa..22baee407fbe 100644 --- a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts +++ b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts @@ -323,6 +323,8 @@ connector { typec-power-opmode = "default"; pd-disable; + vbus-supply = <&pmi632_vbus>; + ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts b/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts index 0edb2992b902..3223884f9cd6 100644 --- a/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts +++ b/arch/arm64/boot/dts/qcom/sdm632-fairphone-fp3.dts @@ -252,6 +252,8 @@ connector { typec-power-opmode = "default"; pd-disable; + vbus-supply = <&pmi632_vbus>; + port { pmi632_hs_in: endpoint { remote-endpoint = <&usb_dwc3_hs>; diff --git a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts index 466ad409e924..0f23eaef01f2 100644 --- a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts +++ b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts @@ -251,6 +251,8 @@ connector { typec-power-opmode = "default"; pd-disable; + vbus-supply = <&pmi632_vbus>; + ports { #address-cells = <1>; #size-cells = <0>; From ffeaf31f05d664581aa436d9cb92b4d1d8d301ce Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Tue, 19 May 2026 18:49:02 +0200 Subject: [PATCH 1540/5214] usb: host: add ARCH_AIROHA in XHCI MTK dependency Airoha SoC use the same register map and logic of the Mediatek xHCI driver, hence add it to the dependency list to permit compilation also on this ARCH. Signed-off-by: Christian Marangi Link: https://patch.msgid.link/20260519164903.31258-1-ansuelsmth@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 43f30cd867e4..b3b1ec696bf5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -77,7 +77,7 @@ config USB_XHCI_HISTB config USB_XHCI_MTK tristate "xHCI support for MediaTek SoCs" select MFD_SYSCON - depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST + depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || ARCH_AIROHA || COMPILE_TEST help Say 'Y' to enable the support for the xHCI host controller found in MediaTek SoCs. From 7c40306927107c8c88030a30875e1c98abb908fd Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Tue, 19 May 2026 11:45:12 -0700 Subject: [PATCH 1541/5214] usb: typec: ucsi: Add support for message_out data structure Add support for UCSI message_out data structure. The UCSI interface defines separate message_in and message_out data structure for bidirectional communication, where commands like Set PDOs and LPM Firmware Update require writing data to message_out before command execution. Add write_message_out operation to ucsi_operations structure to allow platform drivers to implement message_out data writing capability. Update ucsi_sync_control_common to accept message_out parameters and call write_message_out followed by command execution to maintain proper sequencing as per the UCSI specification. Introduce ucsi_write_message_out_command for commands that need to send message_out data, while maintaining ucsi_send_command for commands that only require message_in response data. Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/6d4e1ba7f92e713638f66925ae6389528597df6e.1778798352.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/cros_ec_ucsi.c | 6 ++- drivers/usb/typec/ucsi/ucsi.c | 52 ++++++++++++++++++++----- drivers/usb/typec/ucsi/ucsi.h | 13 ++++++- drivers/usb/typec/ucsi/ucsi_acpi.c | 22 ++++++++++- drivers/usb/typec/ucsi/ucsi_ccg.c | 6 ++- drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 6 ++- 6 files changed, 85 insertions(+), 20 deletions(-) diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index 251aa7251ce6..c192d42d449e 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -114,12 +114,14 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) } static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); + ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size, msg_out, + msg_out_size); switch (ret) { case -EBUSY: /* EC may return -EBUSY if CCI.busy is set. diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 43da7512dea0..32f6d6b4919d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -63,7 +63,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) EXPORT_SYMBOL_GPL(ucsi_notify_common); int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -75,6 +76,17 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, reinit_completion(&ucsi->complete); + if (msg_out && msg_out_size) { + if (!ucsi->ops->write_message_out) { + ret = -EOPNOTSUPP; + goto out_clear_bit; + } + + ret = ucsi->ops->write_message_out(ucsi, msg_out, msg_out_size); + if (ret) + goto out_clear_bit; + } + ret = ucsi->ops->async_control(ucsi, command); if (ret) goto out_clear_bit; @@ -110,11 +122,12 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); + return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0, NULL, 0); } static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size, bool conn_ack) + void *data, size_t size, void *msg_out, + size_t msg_out_size, bool conn_ack) { int ret, err; @@ -123,10 +136,12 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, if (size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); + ret = ucsi->ops->sync_control(ucsi, command, cci, data, size, + msg_out, msg_out_size); if (*cci & UCSI_CCI_BUSY) - return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, + NULL, 0, NULL, 0, false) ?: -EBUSY; if (ret) return ret; @@ -158,7 +173,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) int ret; command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num); - ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); + ret = ucsi_run_command(ucsi, command, &cci, &error, + sizeof(error), NULL, 0, false); if (ret < 0) return ret; @@ -208,7 +224,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) } static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, - void *data, size_t size, bool conn_ack) + void *data, size_t size, void *msg_out, + size_t msg_out_size, bool conn_ack) { u8 connector_num; u32 cci; @@ -236,7 +253,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack); + ret = ucsi_run_command(ucsi, cmd, &cci, data, size, + msg_out, msg_out_size, conn_ack); if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); @@ -250,10 +268,23 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, int ucsi_send_command(struct ucsi *ucsi, u64 command, void *data, size_t size) { - return ucsi_send_command_common(ucsi, command, data, size, false); + return ucsi_send_command_common(ucsi, command, data, + size, NULL, 0, false); } EXPORT_SYMBOL_GPL(ucsi_send_command); +int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, + void *data, size_t size, void *msg_out, + size_t msg_out_size) +{ + if (msg_out_size > UCSI_MAX_MSG_OUT_DATA_LEN(ucsi)) + return -EINVAL; + + return ucsi_send_command_common(ucsi, command, data, + size, msg_out, msg_out_size, false); +} +EXPORT_SYMBOL_GPL(ucsi_write_message_out_command); + /* -------------------------------------------------------------------------- */ struct ucsi_work { @@ -681,7 +712,8 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) UCSI_MAX_DATA_LENGTH(con->ucsi)); int ret; - ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, + NULL, 0, conn_ack); return ret < 0 ? ret : 0; } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 51f6c3c0d365..402f45494dfa 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -65,6 +65,7 @@ struct dentry; * @read_cci: Read CCI register * @poll_cci: Read CCI register while polling with notifications disabled * @read_message_in: Read message data from UCSI + * @write_message_out: Write message data to UCSI * @sync_control: Blocking control operation * @async_control: Non-blocking control operation * @update_altmodes: Squashes duplicate DP altmodes @@ -82,8 +83,9 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); + int (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len); int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size); + void *data, size_t size, void *msg_out, size_t msg_out_size); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, struct ucsi_altmode *orig, @@ -503,6 +505,9 @@ struct ucsi { }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) +#define UCSI_MAX_MSG_OUT_DATA_LEN(u) \ + (((u)->version >= UCSI_VERSION_3_0) ? 255 : \ + (((u)->version >= UCSI_VERSION_2_0) ? 256 : 16)) #define UCSI_MAX_SVID 5 #define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) @@ -565,13 +570,17 @@ struct ucsi_connector { int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size); +int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, + void *retval, size_t size, + void *msg_out, size_t msg_out_size); void ucsi_altmode_update_active(struct ucsi_connector *con); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size); + void *data, size_t size, void *msg_out, + size_t msg_out_size); #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e985..60b12961e1a4 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -86,6 +86,21 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le return 0; } +static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + + if (!data || !data_len) + return -EINVAL; + + if (ucsi->version <= UCSI_VERSION_1_2) + memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len); + else + memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len); + + return 0; +} + static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); @@ -101,19 +116,22 @@ static const struct ucsi_operations ucsi_acpi_ops = { .read_cci = ucsi_acpi_read_cci, .poll_cci = ucsi_acpi_poll_cci, .read_message_in = ucsi_acpi_read_message_in, + .write_message_out = ucsi_acpi_write_message_out, .sync_control = ucsi_sync_control_common, .async_control = ucsi_acpi_async_control }; static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *val, size_t len) + void *val, size_t len, void *msg_out, + size_t msg_out_size) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, command, cci, val, len); + ret = ucsi_sync_control_common(ucsi, command, cci, val, len, + msg_out, msg_out_size); if (ret < 0) return ret; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index ddde0a7702f0..57d8e49ef8ae 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -608,7 +608,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command) } static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -630,7 +631,8 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci, data, size, + msg_out, msg_out_size); switch (UCSI_COMMAND(command)) { case UCSI_GET_CURRENT_CAM: diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index 0187c1c4b21a..1be18d101842 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -89,7 +89,8 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, + void *msg_out, size_t msg_out_size) { int ret; @@ -126,7 +127,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, return 0; } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci, + data, size, msg_out, msg_out_size); if (ret < 0) return ret; From 201b015f95b8d6276d467f81c150ec491c7bda26 Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Tue, 19 May 2026 11:45:13 -0700 Subject: [PATCH 1542/5214] usb: typec: ucsi: Enable debugfs for message_out data structure Add debugfs entry for writing message_out data structure to handle UCSI 2.1 and 3.0 commands through debugfs interface. Users writing to the message_out debugfs file should ensure the input data adheres to the following format: 1. Input must be a non-empty valid hexadecimal string. 2. Input length of hexadecimal string must not exceed 256 bytes of length to be in alignment with the message out data structure size as per the UCSI specification v2.1. 3. If the input string length is odd, then user needs to prepend a '0' to the first character for proper hex conversion. Below are examples of valid hex strings. Note that these values are just examples. The exact values depend on specific command use case. #echo 1A2B3C4D > message_out #echo 01234567 > message_out Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/812820ed3caae2d9ab86e4b26022c5a36b645f86.1778798352.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 26 ++++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index a4b9a6b51649..be987e53a8bd 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -110,6 +110,30 @@ static int ucsi_vbus_volt_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt); +static ssize_t ucsi_message_out_write(struct file *file, + const char __user *data, size_t count, loff_t *ppos) +{ + struct ucsi *ucsi = file->private_data; + int ret; + + char *buf __free(kfree) = memdup_user_nul(data, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = hex2bin(ucsi->debugfs->message_out, buf, + min(count / 2, sizeof(ucsi->debugfs->message_out))); + if (ret) + return ret; + + return count; +} + +static const struct file_operations ucsi_message_out_fops = { + .open = simple_open, + .write = ucsi_message_out_write, + .llseek = generic_file_llseek, +}; + void ucsi_debugfs_register(struct ucsi *ucsi) { ucsi->debugfs = kzalloc_obj(*ucsi->debugfs); @@ -122,6 +146,8 @@ void ucsi_debugfs_register(struct ucsi *ucsi) debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops); debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops); debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops); + debugfs_create_file("message_out", 0200, ucsi->debugfs->dentry, ucsi, + &ucsi_message_out_fops); } void ucsi_debugfs_unregister(struct ucsi *ucsi) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 402f45494dfa..64ccfd6a6d50 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -455,6 +455,8 @@ struct ucsi_bitfield { /* -------------------------------------------------------------------------- */ +#define MESSAGE_OUT_MAX_LEN 256 + struct ucsi_debugfs_entry { u64 command; struct ucsi_data { @@ -462,6 +464,7 @@ struct ucsi_debugfs_entry { u64 high; } response; int status; + u8 message_out[MESSAGE_OUT_MAX_LEN]; struct dentry *dentry; }; From cc15435481696c09668934cd84ea1a8c63ebecb5 Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Tue, 19 May 2026 11:45:14 -0700 Subject: [PATCH 1543/5214] usb: typec: ucsi: Add support for SET_PDOS command Add support for UCSI SET_PDOS command as per UCSI specification v2.1 and above to debugfs. Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/e3e127122c0a6910c4840a13d5c74ab5fc4eb868.1778798352.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 5 +++++ drivers/usb/typec/ucsi/ucsi.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index be987e53a8bd..ff33a5e7c6b0 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -40,6 +40,11 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_READ_POWER_LEVEL: ret = ucsi_send_command(ucsi, val, NULL, 0); break; + case UCSI_SET_PDOS: + ret = ucsi_write_message_out_command(ucsi, val, NULL, 0, + ucsi->debugfs->message_out, + UCSI_COMMAND_DATA_LEN(val)); + break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: case UCSI_GET_ALTERNATE_MODES: diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 64ccfd6a6d50..325ed1e5ca80 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -138,6 +138,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_PD_MESSAGE 0x15 #define UCSI_GET_CAM_CS 0x18 #define UCSI_SET_SINK_PATH 0x1c +#define UCSI_SET_PDOS 0x1d #define UCSI_READ_POWER_LEVEL 0x1e #define UCSI_SET_USB 0x21 #define UCSI_GET_LPM_PPM_INFO 0x22 @@ -215,6 +216,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_PD_MESSAGE_TYPE_IDENTITY 4 #define UCSI_GET_PD_MESSAGE_TYPE_REVISION 5 +/* Data length bits */ +#define UCSI_COMMAND_DATA_LEN(_cmd_) (((_cmd_) >> 8) & GENMASK(7, 0)) + /* -------------------------------------------------------------------------- */ /* Error information returned by PPM in response to GET_ERROR_STATUS command. */ From 52c9780404963fea7300a7517ef1290439a1e08b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 20 May 2026 21:37:11 +0800 Subject: [PATCH 1544/5214] usb: dwc2: remove WARN in dwc2_hcd_save_data_toggle The WARN() in dwc2_hcd_save_data_toggle() was introduced in commit 62943b7dfa35 ("usb: dwc2: host: fix the data toggle error in full speed descriptor dma"), it looks like the WARN() is to ensure proper usage of dwc2_hcd_save_data_toggle(): either qtd is provided for control eps or qh is provided for non-control eps. This check is good even if there's no such improper usage in current code. But the WARN() usage in driver is discouraged nowadays: imagine there is an improper usage, then kernel panic due to warn if 'panic_on_warn' is enabled. While emitting the err msg for improper usage is still valueable, so let's replace the WARN with check and dev_err(). At the same time, it looks a bit strange we check !chan after dereference of this pointer with "if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL)". In fact, when entering the dwc2_hcd_save_data_toggle(), the chan won't be NULL, because its caller or indirect caller has ensured this, specifically, it's checked with below line in dwc2_hc_n_intr() if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); return; } This addresses the following issue reported by klocwork tool: - Suspicious dereference of pointer 'chan' before NULL check at line 518 Suggested-by: Greg Kroah-Hartman Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260520133711.14410-1-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd_intr.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 5c7538d498dd..f380275ac696 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -515,18 +515,20 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { - if (WARN(!chan || !chan->qh, - "chan->qh must be specified for non-control eps\n")) + if (!chan->qh) { + dev_err(hsotg->dev, "chan->qh must be specified for non-control eps\n"); return; + } if (pid == TSIZ_SC_MC_PID_DATA0) chan->qh->data_toggle = DWC2_HC_PID_DATA0; else chan->qh->data_toggle = DWC2_HC_PID_DATA1; } else { - if (WARN(!qtd, - "qtd must be specified for control eps\n")) + if (!qtd) { + dev_err(hsotg->dev, "qtd must be specified for control eps\n"); return; + } if (pid == TSIZ_SC_MC_PID_DATA0) qtd->data_toggle = DWC2_HC_PID_DATA0; From e2ffaac1884b921b8ec2b3a964c6a8b5d610bf4b Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 21 May 2026 14:54:28 +0800 Subject: [PATCH 1545/5214] usb: gadget: aspeed_udc: avoid past-the-end iterator in dequeue ast_udc_ep_dequeue() declares the loop cursor `req` outside the list_for_each_entry(). After the loop it tests `&req->req != _req` to decide whether the request was found. If the queue holds no match, `req` is past-the-end. It then aliases container_of(&ep->queue, struct ast_udc_request, queue) via offset cancellation. Whether that synthetic address equals `_req` depends on heap layout. The function can return 0 without dequeueing anything. Default `rc` to -EINVAL and set it to 0 only inside the match branch. `req` is no longer read after the loop, so the past-the-end dereference goes away. No extra cursor variable or post-loop test is needed. Suggested-by: Alan Stern Suggested-by: Andrew Jeffery Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260521065428.3261238-1-maoyixie.tju@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/aspeed_udc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 7fc6696b7694..75f9c831b21a 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -694,7 +694,7 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct ast_udc_dev *udc = ep->udc; struct ast_udc_request *req; unsigned long flags; - int rc = 0; + int rc = -EINVAL; spin_lock_irqsave(&udc->lock, flags); @@ -704,14 +704,11 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) list_del_init(&req->queue); ast_udc_done(ep, req, -ESHUTDOWN); _req->status = -ECONNRESET; + rc = 0; break; } } - /* dequeue request not found */ - if (&req->req != _req) - rc = -EINVAL; - spin_unlock_irqrestore(&udc->lock, flags); return rc; From a82fb629ce5976cfdd6c1ac819f7bd8ce967aae3 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Thu, 21 May 2026 10:16:23 +0200 Subject: [PATCH 1546/5214] dt-bindings: usb: cdns3: Add cdns,cdnsp compatible string Introduce a new generic fallback compatible string 'cdns,cdnsp' for Cadence USBSSP controllers to support hardware configurations where the Dual-Role Device (DRD) register block is missing or inaccessible. Following the maintainer's feedback, avoid generic property-like naming (such as "-no-drd") and use a clean generic fallback. To keep the schema resource-driven and strictly validated, define a two-string compatible matrix using an empty schema ({}) wildcard. This allows future vendor SoC compatibles to be prepended while safely falling back to the 2-resource USBSSP configuration. When 'cdns,cdnsp' is matched: - The 'otg' register and interrupt resources are not required. - The 'reg' and 'interrupts' properties are restricted to 2 items (host and device). - 'dr_mode' must be explicitly set to either 'host' or 'peripheral'. The standard 'cdns,usb3' compatible remains unchanged, maintaining backward compatibility by requiring all 3 resource sets (otg, host, dev). Signed-off-by: Pawel Laszczak Acked-by: Conor Dooley Link: https://patch.msgid.link/20260521-no_drd_config_v9-v9-1-2512cef10104@cadence.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/cdns,usb3.yaml | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index 2d95fb7321af..e8082c5c05a2 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml @@ -17,22 +17,24 @@ description: properties: compatible: - const: cdns,usb3 + oneOf: + - const: cdns,usb3 + - items: + - {} + - const: cdns,cdnsp reg: - items: - - description: OTG controller registers - - description: XHCI Host controller registers - - description: DEVICE controller registers + minItems: 2 + maxItems: 3 reg-names: + minItems: 2 + maxItems: 3 items: - - const: otg - - const: xhci - - const: dev + enum: [ otg, xhci, dev ] interrupts: - minItems: 3 + minItems: 2 items: - description: XHCI host controller interrupt - description: Device controller interrupt @@ -41,7 +43,7 @@ properties: cleared by xhci core, this interrupt is optional interrupt-names: - minItems: 3 + minItems: 2 items: - const: host - const: peripheral @@ -93,6 +95,47 @@ allOf: - $ref: usb-drd.yaml# - $ref: usb-xhci.yaml# + - if: + properties: + compatible: + contains: + const: cdns,cdnsp + then: + properties: + reg: + items: + - description: XHCI Host controller registers + - description: DEVICE controller registers + reg-names: + items: + - const: xhci + - const: dev + interrupts: + maxItems: 2 + interrupt-names: + items: + - const: host + - const: peripheral + dr_mode: + enum: [host, peripheral] + else: + properties: + reg: + items: + - description: OTG controller registers + - description: XHCI Host controller registers + - description: DEVICE controller registers + reg-names: + items: + - const: otg + - const: xhci + - const: dev + interrupts: + minItems: 3 + maxItems: 4 + interrupt-names: + minItems: 3 + unevaluatedProperties: false examples: From 22d91cef94b5b86cff0d68ebfce7741740672704 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Thu, 21 May 2026 10:16:24 +0200 Subject: [PATCH 1547/5214] usb: cdnsp: Add support for device-only configuration This patch introduces support for the Cadence USBSSP (cdnsp) controller in hardware configurations where the Dual-Role Device (DRD) register block is not implemented or is inaccessible. In such cases, the driver cannot rely on the DRD logic to manage roles and must operate exclusively in a fixed peripheral/host mode. The change in BAR indexing (from BAR 2 to BAR 1) is a direct consequence of the 32-bit addressing used in this specific DRD-disabled hardware layout, compared to the 64-bit addressing used in DRD-enabled configurations. Tested on a PCI platform with a hardware configuration that lacks DRD support. Platform-side changes are included to support the PCI glue layer's property injection to handle this specific layout. Acked-by: Peter Chen Acked-by: Bjorn Helgaas Signed-off-by: Pawel Laszczak Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605141023.18vWXyw3-lkp@intel.com/ Link: Closes: https://lore.kernel.org/oe-kbuild-all/202605141023.18vWXyw3-lkp@intel.com/ Link: https://patch.msgid.link/20260521-no_drd_config_v9-v9-2-2512cef10104@cadence.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 28 +++++++++++++------- drivers/usb/cdns3/cdnsp-pci.c | 48 +++++++++++++++++++++++++++------- drivers/usb/cdns3/core.c | 3 ++- drivers/usb/cdns3/core.h | 4 +++ drivers/usb/cdns3/drd.c | 45 ++++++++++++++++++++++++++++--- 5 files changed, 105 insertions(+), 23 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 91159910fe95..b483b9808f57 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -81,6 +81,12 @@ static int cdns3_plat_probe(struct platform_device *pdev) if (cdns->pdata && cdns->pdata->override_apb_timeout) cdns->override_apb_timeout = cdns->pdata->override_apb_timeout; + if (device_is_compatible(dev, "cdns,cdnsp")) { + cdns->no_drd = true; + cdns->version = CDNSP_CONTROLLER_V2; + dev_dbg(dev, "No DRD support\n"); + } + platform_set_drvdata(pdev, cdns); ret = platform_get_irq_byname(pdev, "host"); @@ -113,21 +119,22 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev_regs = regs; - cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); - if (cdns->otg_irq < 0) - return dev_err_probe(dev, cdns->otg_irq, - "Failed to get otg IRQ\n"); + if (!cdns->no_drd) { + cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); + if (cdns->otg_irq < 0) + return dev_err_probe(dev, cdns->otg_irq, + "Failed to get otg IRQ\n"); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); - if (!res) { - dev_err(dev, "couldn't get otg resource\n"); - return -ENXIO; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); + if (!res) { + dev_err(dev, "couldn't get otg resource\n"); + return -ENXIO; + } + cdns->otg_res = *res; } cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable"); - cdns->otg_res = *res; - cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); if (cdns->wakeup_irq == -EPROBE_DEFER) return cdns->wakeup_irq; @@ -341,6 +348,7 @@ static const struct dev_pm_ops cdns3_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id of_cdns3_match[] = { { .compatible = "cdns,usb3" }, + { .compatible = "cdns,cdnsp" }, { }, }; MODULE_DEVICE_TABLE(of, of_cdns3_match); diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 432007cfe695..c38a3dc7111a 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -19,6 +19,7 @@ struct cdnsp_wrap { struct platform_device *plat_dev; + struct property_entry prop[3]; struct resource dev_res[6]; int devfn; }; @@ -29,10 +30,15 @@ struct cdnsp_wrap { #define RES_HOST_ID 3 #define RES_DEV_ID 4 #define RES_DRD_ID 5 - +/* DRD PCI configuration - 64-bit addressing */ +/* First PCI function */ #define PCI_BAR_HOST 0 -#define PCI_BAR_OTG 0 #define PCI_BAR_DEV 2 +/* Second PCI function */ +#define PCI_BAR_OTG 0 +/* Device only PCI configuration - 32-bit addressing */ +/* First PCI function */ +#define PCI_BAR_ONLY_DEV 1 #define PCI_DEV_FN_HOST_DEVICE 0 #define PCI_DEV_FN_OTG 1 @@ -40,6 +46,7 @@ struct cdnsp_wrap { #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usb3" +#define PCI_DEVICE_ID_CDNS_UDC_USBSSP 0x0400 #define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) @@ -65,6 +72,7 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, struct cdnsp_wrap *wrap; struct resource *res; struct pci_dev *func; + bool no_drd = false; int ret = 0; /* @@ -75,11 +83,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, pdev->devfn != PCI_DEV_FN_OTG)) return -EINVAL; + if (pdev->device == PCI_DEVICE_ID_CDNS_UDC_USBSSP) + no_drd = true; + func = cdnsp_get_second_fun(pdev); - if (!func) + if (!func && !no_drd) return -EINVAL; - if (func->class == PCI_CLASS_SERIAL_USB_XHCI || + if ((func && func->class == PCI_CLASS_SERIAL_USB_XHCI) || pdev->class == PCI_CLASS_SERIAL_USB_XHCI) { ret = -EINVAL; goto put_pci; @@ -93,7 +104,7 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); - if (pci_is_enabled(func)) { + if (func && pci_is_enabled(func)) { wrap = pci_get_drvdata(func); } else { wrap = kzalloc_obj(*wrap); @@ -106,10 +117,12 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, res = wrap->dev_res; if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) { + int bar_dev = no_drd ? PCI_BAR_ONLY_DEV : PCI_BAR_DEV; + /* Function 0: host(BAR_0) + device(BAR_2). */ dev_dbg(&pdev->dev, "Initialize Device resources\n"); - res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV); - res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV); + res[RES_DEV_ID].start = pci_resource_start(pdev, bar_dev); + res[RES_DEV_ID].end = pci_resource_end(pdev, bar_dev); res[RES_DEV_ID].name = "dev"; res[RES_DEV_ID].flags = IORESOURCE_MEM; dev_dbg(&pdev->dev, "USBSSP-DEV physical base addr: %pa\n", @@ -145,9 +158,21 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ; } - if (pci_is_enabled(func)) { + if (no_drd || pci_is_enabled(func)) { + u8 idx = 0; + /* set up platform device info */ pdata.override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; + + if (no_drd) { + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("compatible", + "cdns,cdnsp"); + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "peripheral"); + } else { + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "otg"); + wrap->prop[idx++] = PROPERTY_ENTRY_BOOL("usb-role-switch"); + } + memset(&plat_info, 0, sizeof(plat_info)); plat_info.parent = &pdev->dev; plat_info.fwnode = pdev->dev.fwnode; @@ -158,6 +183,7 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, plat_info.dma_mask = pdev->dma_mask; plat_info.data = &pdata; plat_info.size_data = sizeof(pdata); + plat_info.properties = wrap->prop; wrap->devfn = pdev->devfn; /* register platform device */ wrap->plat_dev = platform_device_register_full(&plat_info); @@ -185,13 +211,17 @@ static void cdnsp_pci_remove(struct pci_dev *pdev) if (wrap->devfn == pdev->devfn) platform_device_unregister(wrap->plat_dev); - if (!pci_is_enabled(func)) + if (!func || !pci_is_enabled(func)) kfree(wrap); pci_dev_put(func); } static const struct pci_device_id cdnsp_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP), + .class = PCI_CLASS_SERIAL_USB_CDNS }, { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), .class = PCI_CLASS_SERIAL_USB_DEVICE }, { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 6a8d1fefbc0d..504bdf13ea80 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -70,7 +70,8 @@ static void cdns_role_stop(struct cdns *cdns) static void cdns_exit_roles(struct cdns *cdns) { cdns_role_stop(cdns); - cdns_drd_exit(cdns); + if (!cdns->no_drd) + cdns_drd_exit(cdns); } /** diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index bca973b999a4..8c492fda924c 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -84,6 +84,9 @@ struct cdns3_platform_data { * value in CHICKEN_BITS_3 will be preserved. * @gadget_init: pointer to gadget initialization function * @host_init: pointer to host initialization function + * @no_drd: DRD register block is inaccessible. The controller is hardwired to + * single role (host or device) or the logic for role switching is + * missing. */ struct cdns { struct device *dev; @@ -124,6 +127,7 @@ struct cdns { u32 override_apb_timeout; int (*gadget_init)(struct cdns *cdns); int (*host_init)(struct cdns *cdns); + bool no_drd; }; int cdns_hw_role_switch(struct cdns *cdns); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 84fb38a5723a..d2bb682e4552 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -87,6 +87,9 @@ int cdns_get_id(struct cdns *cdns) { int id; + if (cdns->no_drd) + return 0; + id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE; dev_dbg(cdns->dev, "OTG ID: %d", id); @@ -107,7 +110,7 @@ void cdns_clear_vbus(struct cdns *cdns) { u32 reg; - if (cdns->version != CDNSP_CONTROLLER_V2) + if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd) return; reg = readl(&cdns->otg_cdnsp_regs->override); @@ -120,7 +123,7 @@ void cdns_set_vbus(struct cdns *cdns) { u32 reg; - if (cdns->version != CDNSP_CONTROLLER_V2) + if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd) return; reg = readl(&cdns->otg_cdnsp_regs->override); @@ -179,7 +182,10 @@ static void cdns_otg_enable_irq(struct cdns *cdns) int cdns_drd_host_on(struct cdns *cdns) { u32 val, ready_bit; - int ret; + int ret = 0; + + if (cdns->no_drd) + goto phy_set; /* Enable host mode. */ writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, @@ -197,6 +203,7 @@ int cdns_drd_host_on(struct cdns *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; @@ -210,6 +217,9 @@ void cdns_drd_host_off(struct cdns *cdns) { u32 val; + if (cdns->no_drd) + goto phy_set; + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, &cdns->otg_regs->cmd); @@ -218,6 +228,8 @@ void cdns_drd_host_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -234,6 +246,9 @@ int cdns_drd_gadget_on(struct cdns *cdns) u32 ready_bit; int ret, val; + if (cdns->no_drd) + goto phy_set; + /* switch OTG core */ writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); @@ -251,6 +266,7 @@ int cdns_drd_gadget_on(struct cdns *cdns) return ret; } +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; @@ -265,6 +281,9 @@ void cdns_drd_gadget_off(struct cdns *cdns) { u32 val; + if (cdns->no_drd) + goto phy_set; + /* * Driver should wait at least 10us after disabling Device * before turning-off Device (DEV_BUS_DROP). @@ -277,6 +296,8 @@ void cdns_drd_gadget_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -392,6 +413,18 @@ int cdns_drd_init(struct cdns *cdns) u32 state, reg; int ret; + if (cdns->no_drd) { + cdns->dr_mode = usb_get_dr_mode(cdns->dev); + + if (cdns->dr_mode != USB_DR_MODE_HOST && + cdns->dr_mode != USB_DR_MODE_PERIPHERAL) { + dev_err(cdns->dev, "Incorrect dr_mode\n"); + return -EINVAL; + } + + return 0; + } + regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -492,6 +525,9 @@ int cdns_drd_init(struct cdns *cdns) int cdns_drd_exit(struct cdns *cdns) { + if (cdns->no_drd) + return 0; + cdns_otg_disable_irq(cdns); return 0; @@ -500,6 +536,9 @@ int cdns_drd_exit(struct cdns *cdns) /* Indicate the cdns3 core was power lost before */ bool cdns_power_is_lost(struct cdns *cdns) { + if (cdns->no_drd) + return false; + if (cdns->version == CDNS3_CONTROLLER_V0) { if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0))) return true; From f18643843bc622e24dd6c3c704273c1f411b1b7a Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 21 May 2026 11:33:29 -0400 Subject: [PATCH 1548/5214] serial: max310x: fix compile errors if CONFIG_SPI_MASTER is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 20ffe4b3330a8 ("serial: max310x: allow driver to be built with SPI or I2C"), if I2C is enabled and SPI_MASTER is disabled, we have these compile errors: drivers/tty/serial/max310x.c: In function 'max310x_uart_init': drivers/tty/serial/max310x.c: error: 'max310x_spi_driver' undeclared... drivers/tty/serial/max310x.c: In function ‘max310x_uart_init’: drivers/tty/serial/max310x.c: error: label ‘err_spi_register’ defined but not used... drivers/tty/serial/max310x.c: error: ‘regcfg’ defined but not used Fix by properly encapsulating i2c/spi code/variables in their respective context with IS_ENABLED() macros for CONFIG_I2C and CONFIG_SPI_MASTER. Also fix link failure with SERIAL_MAX310X=y and I2C=m by modifying Kconfig depends. Fixes: 20ffe4b3330a8 ("serial: max310x: allow driver to be built with SPI or I2C") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605121847.N9DVLNg2-lkp@intel.com/ Signed-off-by: Hugo Villeneuve Link: https://patch.msgid.link/20260521153333.2336642-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/max310x.c | 48 +++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f834e5d292fd..4accbfa75074 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -321,7 +321,7 @@ config SERIAL_MAX3100 config SERIAL_MAX310X tristate "MAX310X support" - depends on SPI_MASTER || I2C + depends on (SPI_MASTER && !I2C) || I2C select SERIAL_CORE select REGMAP_SPI if SPI_MASTER select REGMAP_I2C if I2C diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 9f423b3b4201..bad5329a0c84 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1507,6 +1508,21 @@ static const struct of_device_id __maybe_unused max310x_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, max310x_dt_ids); +static const char *max310x_regmap_name(u8 port_id) +{ + switch (port_id) { + case 0: return "port0"; + case 1: return "port1"; + case 2: return "port2"; + case 3: return "port3"; + default: + WARN_ON(true); + return NULL; + } +} + +#if IS_ENABLED(CONFIG_SPI_MASTER) + static struct regmap_config regcfg = { .reg_bits = 8, .val_bits = 8, @@ -1522,20 +1538,6 @@ static struct regmap_config regcfg = { .max_raw_write = MAX310X_FIFO_SIZE, }; -static const char *max310x_regmap_name(u8 port_id) -{ - switch (port_id) { - case 0: return "port0"; - case 1: return "port1"; - case 2: return "port2"; - case 3: return "port3"; - default: - WARN_ON(true); - return NULL; - } -} - -#ifdef CONFIG_SPI_MASTER static int max310x_spi_extended_reg_enable(struct device *dev, bool enable) { struct max310x_port *s = dev_get_drvdata(dev); @@ -1606,7 +1608,8 @@ static struct spi_driver max310x_spi_driver = { }; #endif -#ifdef CONFIG_I2C +#if IS_ENABLED(CONFIG_I2C) + static int max310x_i2c_extended_reg_enable(struct device *dev, bool enable) { return 0; @@ -1726,13 +1729,13 @@ static int __init max310x_uart_init(void) if (ret) return ret; -#ifdef CONFIG_SPI_MASTER +#if IS_ENABLED(CONFIG_SPI_MASTER) ret = spi_register_driver(&max310x_spi_driver); if (ret) goto err_spi_register; #endif -#ifdef CONFIG_I2C +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&max310x_i2c_driver); if (ret) goto err_i2c_register; @@ -1740,12 +1743,13 @@ static int __init max310x_uart_init(void) return 0; -#ifdef CONFIG_I2C +#if IS_ENABLED(CONFIG_I2C) err_i2c_register: - spi_unregister_driver(&max310x_spi_driver); #endif - +#if IS_ENABLED(CONFIG_SPI_MASTER) + spi_unregister_driver(&max310x_spi_driver); err_spi_register: +#endif uart_unregister_driver(&max310x_uart); return ret; @@ -1754,11 +1758,11 @@ module_init(max310x_uart_init); static void __exit max310x_uart_exit(void) { -#ifdef CONFIG_I2C +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&max310x_i2c_driver); #endif -#ifdef CONFIG_SPI_MASTER +#if IS_ENABLED(CONFIG_SPI_MASTER) spi_unregister_driver(&max310x_spi_driver); #endif From 52f203f9a8c7e57322e79577d1a8d50a5e3a5ef2 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 23 Apr 2026 09:20:54 +0000 Subject: [PATCH 1549/5214] dt-bindings: arm: amlogic: add A311Y3 support Add bindings for the Amlogic BY401 board, using A311Y3 Soc from Amlogic A9 family chip. Reviewed-by: Martin Blumenstingl Acked-by: Krzysztof Kozlowski Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20260423-a9-baisc-dts-v4-1-c26b480a068c@amlogic.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/amlogic.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/amlogic.yaml b/Documentation/devicetree/bindings/arm/amlogic.yaml index a885278bc4e2..9f73a0054fb2 100644 --- a/Documentation/devicetree/bindings/arm/amlogic.yaml +++ b/Documentation/devicetree/bindings/arm/amlogic.yaml @@ -234,6 +234,12 @@ properties: - amlogic,av400 - const: amlogic,a5 + - description: Boards with the Amlogic A9 A311Y3 SoC + items: + - enum: + - amlogic,by401 + - const: amlogic,a9 + - description: Boards with the Amlogic C3 C302X/C308L SoC items: - enum: From ab5eadfea17e9aae0760eec8f730a71a4be9ba5e Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 23 Apr 2026 09:20:55 +0000 Subject: [PATCH 1550/5214] arm64: dts: add support for A9 based Amlogic BY401 Add basic support for the A9 based Amlogic BY401 board, which describes the following components: CPU, GIC, IRQ, Timer and UART. These are capable of booting up into the serial console. Reviewed-by: Martin Blumenstingl Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20260423-a9-baisc-dts-v4-2-c26b480a068c@amlogic.com Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/amlogic/Makefile | 1 + .../dts/amlogic/amlogic-a9-a311y3-by401.dts | 40 ++++++ arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi | 128 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 arch/arm64/boot/dts/amlogic/amlogic-a9-a311y3-by401.dts create mode 100644 arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile index 15f9c817e502..57bc440fa55c 100644 --- a/arch/arm64/boot/dts/amlogic/Makefile +++ b/arch/arm64/boot/dts/amlogic/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_MESON) += amlogic-a4-a113l2-ba400.dtb dtb-$(CONFIG_ARCH_MESON) += amlogic-a5-a113x2-av400.dtb +dtb-$(CONFIG_ARCH_MESON) += amlogic-a9-a311y3-by401.dtb dtb-$(CONFIG_ARCH_MESON) += amlogic-c3-c302x-aw409.dtb dtb-$(CONFIG_ARCH_MESON) += amlogic-c3-c308l-aw419.dtb dtb-$(CONFIG_ARCH_MESON) += amlogic-s6-s905x5-bl209.dtb diff --git a/arch/arm64/boot/dts/amlogic/amlogic-a9-a311y3-by401.dts b/arch/arm64/boot/dts/amlogic/amlogic-a9-a311y3-by401.dts new file mode 100644 index 000000000000..a6b380ca47a5 --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/amlogic-a9-a311y3-by401.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 Amlogic, Inc. All rights reserved. + */ + +/dts-v1/; + +#include "amlogic-a9.dtsi" +/ { + model = "Amlogic A311DY3 BY401 Development Board"; + compatible = "amlogic,by401", "amlogic,a9"; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart_b; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x0 0x0 0x80000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 35 MiB reserved for ARM Trusted Firmware */ + secmon_reserved: secmon@5000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x05000000 0x0 0x2300000>; + no-map; + }; + }; +}; + +&uart_b { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi new file mode 100644 index 000000000000..660c8556a864 --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 Amlogic, Inc. All rights reserved. + */ + +#include +#include +#include + +/ { + interrupt-parent = <&gic>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x0>; + enable-method = "psci"; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x100>; + enable-method = "psci"; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x200>; + enable-method = "psci"; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x300>; + enable-method = "psci"; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x400>; + enable-method = "psci"; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0 0x500>; + enable-method = "psci"; + }; + + cpu6: cpu@600 { + device_type = "cpu"; + compatible = "arm,cortex-a78"; + reg = <0x0 0x600>; + enable-method = "psci"; + }; + + cpu7: cpu@700 { + device_type = "cpu"; + compatible = "arm,cortex-a78"; + reg = <0x0 0x700>; + enable-method = "psci"; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + xtal: xtal-clk { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "xtal"; + #clock-cells = <0>; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic: interrupt-controller@ff800000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x0 0xff800000 0 0x10000>, + <0x0 0xff840000 0 0x100000>; + interrupts = ; + }; + + aobus: bus@ffa00000 { + compatible = "simple-bus"; + reg = <0x0 0xffa00000 0x0 0x100000>; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0xffa00000 0x0 0x100000>; + + uart_b: serial@1e000 { + compatible = "amlogic,a9-uart", + "amlogic,meson-s4-uart"; + reg = <0x0 0x1e000 0x0 0x18>; + interrupts = ; + clocks = <&xtal>, <&xtal>, <&xtal>; + clock-names = "xtal", "pclk", "baud"; + status = "disabled"; + }; + }; + }; +}; From 941c9f84c9b6310f7aaa1c8c785dcc634ee33050 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 8 May 2026 23:12:37 +0500 Subject: [PATCH 1551/5214] tty: serial: 8250: protect against NULL uart->port.dev in register serial8250_register_8250_port() conditionally copies uart->port.dev from up->port.dev only when up->port.dev is non-NULL: if (up->port.dev) { uart->port.dev = up->port.dev; ... } So if both the existing uart slot and up have a NULL ->dev, uart->port.dev remains NULL. The very next ACPI companion check then dereferences it unconditionally: if (!has_acpi_companion(uart->port.dev)) { has_acpi_companion() reads dev->fwnode without a NULL guard (include/linux/acpi.h), so this NULL-derefs the kernel for the remaining no-dev case rather than just skipping the mctrl_gpio_init() initialisation as intended. smatch flags the inconsistency: drivers/tty/serial/8250/8250_core.c:767 serial8250_register_8250_port() error: 'uart->port.dev' could be null (see line 719) Guard the call with a NULL check so register continues to work for callers that legitimately have no parent device (legacy non-OF/non-ACPI registrations). No functional change for callers that pass a non-NULL ->dev. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260508181237.11146-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index a428e88938eb..e136cec0c0be 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -764,7 +764,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) * Only call mctrl_gpio_init(), if the device has no ACPI * companion device */ - if (!has_acpi_companion(uart->port.dev)) { + if (uart->port.dev && !has_acpi_companion(uart->port.dev)) { struct mctrl_gpios *gpios = mctrl_gpio_init(&uart->port, 0); if (IS_ERR(gpios)) { ret = PTR_ERR(gpios); From f69ec492244d54068f08c20f90979274d8ac3655 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 11 May 2026 17:33:00 +0206 Subject: [PATCH 1552/5214] serial: 8250: Set cons_flow on port registration Since console flow control policy is no longer part of uart_port.flags, explicitly set the policy for the port. Signed-off-by: John Ogness Link: https://patch.msgid.link/20260511152706.151498-2-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index e136cec0c0be..5ea9a8827b04 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -746,6 +746,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->lsr_save_mask = up->lsr_save_mask; uart->dma = up->dma; + uart_set_cons_flow_enabled(&uart->port, uart_cons_flow_enabled(&up->port)); + /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) uart->tx_loadsz = uart->port.fifosize; From 39d307074f919aed4fe7b1c131e624f34fec3d51 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 11 May 2026 17:33:01 +0206 Subject: [PATCH 1553/5214] serial: 8250: Check LSR timeout on console flow control wait_for_xmitr() calls wait_for_lsr() to wait for the transmission registers to be empty. wait_for_lsr() can timeout after a reasonable amount of time. When console flow control is active, wait_for_xmitr() additionally polls CTS, waiting for the peer to signal that it is ready to receive more data. If hardware flow control is enabled (auto CTS) and the peer deasserts CTS, wait_for_lsr() will timeout. If additionally console flow control is active and while polling CTS the peer asserts CTS, the console will assume it can immediately transmit, even though the transmission registers may not be empty. This can lead to data loss. Avoid this problem by performing an extra wait_for_lsr() upon CTS assertion if wait_for_lsr() previously timed out. Signed-off-by: John Ogness Link: https://patch.msgid.link/20260511152706.151498-3-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index e4e6a53ebea3..fe2e0f1e66c2 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1986,16 +1986,20 @@ static bool wait_for_lsr(struct uart_8250_port *up, int bits) static void wait_for_xmitr(struct uart_8250_port *up, int bits) { unsigned int tmout; + bool tx_ready; - wait_for_lsr(up, bits); + tx_ready = wait_for_lsr(up, bits); /* Wait up to 1s for flow control if necessary */ if (uart_cons_flow_enabled(&up->port)) { for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; - if (msr & UART_MSR_CTS) + if (msr & UART_MSR_CTS) { + if (!tx_ready) + wait_for_lsr(up, bits); break; + } udelay(1); touch_nmi_watchdog(); } From 5e6dfb87b191f34b1bb7cfb4d668665e5b70687b Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 11 May 2026 17:33:02 +0206 Subject: [PATCH 1554/5214] serial: 8250: Add support for console flow control The kernel documentation specifies that the console option 'r' can be used to enable hardware flow control for console writes. The 8250 driver does include code for hardware flow control on the console if cons_flow is set, but there is no code path that actually sets this. However, that is not the only issue. The problems are: 1. Specifying the console option 'r' does not lead to cons_flow being set. 2. Even if cons_flow would be set, serial8250_register_8250_port() clears it. 3. When the console option 'r' is specified, uart_set_options() attempts to initialize the port for CRTSCTS. However, afterwards it does not set the UPSTAT_CTS_ENABLE status bit and therefore on boot, uart_cts_enabled() is always false. This policy bit is important for console drivers as a criteria if they may poll CTS. 4. Even though uart_set_options() attempts to initialize the port for CRTSCTS, the 8250 set_termios() callback does not enable the RTS signal (TIOCM_RTS) and thus the hardware is not properly initialized for CTS polling. 5. Even if modem control was properly setup for CTS polling (TIOCM_RTS), uart_configure_port() clears TIOCM_RTS, thus breaking CTS polling. 6. wait_for_xmitr() and serial8250_console_write() use cons_flow to decide if CTS polling should occur. However, the condition should also include a check that it is not in RS485 mode and CRTSCTS is actually enabled in the hardware. Address all these issues as conservatively as possible by gating them behind checks focussed on the user specifying console hardware flow control support and the hardware being configured for CTS polling at the time of the write to the UART. Since checking the UPSTAT_CTS_ENABLE status bit is a part of the new condition gate, these changes also support runtime termios updates to disable/enable CRTSCTS. Signed-off-by: John Ogness Link: https://patch.msgid.link/20260511152706.151498-4-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 6 +++++- drivers/tty/serial/8250/8250_port.c | 13 +++++++++++-- drivers/tty/serial/serial_core.c | 21 ++++++++++++++++++++- include/linux/serial_core.h | 8 ++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 5ea9a8827b04..f49862d90eeb 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -693,6 +693,7 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work) int serial8250_register_8250_port(const struct uart_8250_port *up) { struct uart_8250_port *uart; + bool cons_flow; int ret; if (up->port.uartclk == 0) @@ -716,6 +717,9 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) if (uart->port.type == PORT_8250_CIR) return -ENODEV; + /* Preserve specified console flow control. */ + cons_flow = uart_cons_flow_enabled(&uart->port); + if (uart->port.dev) uart_remove_one_port(&serial8250_reg, &uart->port); @@ -746,7 +750,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->lsr_save_mask = up->lsr_save_mask; uart->dma = up->dma; - uart_set_cons_flow_enabled(&uart->port, uart_cons_flow_enabled(&up->port)); + uart_set_cons_flow_enabled(&uart->port, uart_cons_flow_enabled(&up->port) | cons_flow); /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index fe2e0f1e66c2..ef245114105b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1991,7 +1991,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) tx_ready = wait_for_lsr(up, bits); /* Wait up to 1s for flow control if necessary */ - if (uart_cons_flow_enabled(&up->port)) { + if (uart_console_hwflow_active(&up->port)) { for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; @@ -2788,6 +2788,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial8250_set_efr(port, termios); serial8250_set_divisor(port, baud, quot, frac); serial8250_set_fcr(port, termios); + /* Consoles manually poll CTS for hardware flow control. */ + if (uart_console(port) && + !(port->rs485.flags & SER_RS485_ENABLED) + && termios->c_cflag & CRTSCTS) { + port->mctrl |= TIOCM_RTS; + } serial8250_set_mctrl(port, port->mctrl); } @@ -3357,7 +3363,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, * it regardless of the CTS state. Therefore, only use fifo * if we don't use control flow. */ - !uart_cons_flow_enabled(&up->port); + !uart_console_hwflow_active(&up->port); if (likely(use_fifo)) serial8250_console_fifo_write(up, s, count); @@ -3427,6 +3433,9 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) if (ret) return ret; + /* Track user-specified console flow control. */ + uart_set_cons_flow_enabled(port, flow == 'r'); + if (port->dev) pm_runtime_get_sync(port->dev); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 075a69164aa7..98ace5c492fa 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2231,6 +2231,18 @@ uart_set_options(struct uart_port *port, struct console *co, port->mctrl |= TIOCM_DTR; port->ops->set_termios(port, &termios, &dummy); + + /* + * If console hardware flow control was specified and is supported, + * the related policy UPSTAT_CTS_ENABLE must be set to allow console + * drivers to identify if CTS should be used for polling. + */ + if (flow == 'r' && (termios.c_cflag & CRTSCTS)) { + /* Synchronize @status RMW update against the console. */ + guard(uart_port_lock_irqsave)(port); + port->status |= UPSTAT_CTS_ENABLE; + } + /* * Allow the setting of the UART parameters with a NULL console * too: @@ -2537,7 +2549,14 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, * We probably don't need a spinlock around this, but */ scoped_guard(uart_port_lock_irqsave, port) { - port->mctrl &= TIOCM_DTR; + unsigned int mask = TIOCM_DTR; + + /* Console hardware flow control polls CTS. */ + if (uart_console_hwflow_active(port)) + mask |= TIOCM_RTS; + + port->mctrl &= mask; + if (!(port->rs485.flags & SER_RS485_ENABLED)) port->ops->set_mctrl(port, port->mctrl); } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 4f7bbdd90017..17fcff466e30 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1175,6 +1175,14 @@ static inline bool uart_cons_flow_enabled(const struct uart_port *uport) return uport->cons_flow; } +static inline bool uart_console_hwflow_active(struct uart_port *uport) +{ + return uart_console(uport) && + !(uport->rs485.flags & SER_RS485_ENABLED) && + uart_cons_flow_enabled(uport) && + uart_cts_enabled(uport); +} + /* * The following are helper functions for the low level drivers. */ From d9ee199c0fef5e4074e241ed328e5c18a790454b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Thu, 14 May 2026 00:34:57 +0300 Subject: [PATCH 1555/5214] amba/serial: amba-pl011: Bring back zx29 UART support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is based on code removed in commit 89d4f98ae90d ("ARM: remove zte zx platform"). I did not bring back the zx29-uart .compatible as the arm,primecell-periphid does the job. Reviewed-by: Linus Walleij Signed-off-by: Stefan Dösinger Link: https://patch.msgid.link/20260514-zx29uart-v1-2-68470ecc3977@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 028e37ad8d79..8ed91e1da22b 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -240,6 +240,38 @@ static struct vendor_data vendor_nvidia = { .get_fifosize = get_fifosize_nvidia, }; +static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = { + [REG_DR] = ZX_UART011_DR, + [REG_FR] = ZX_UART011_FR, + [REG_LCRH_RX] = ZX_UART011_LCRH, + [REG_LCRH_TX] = ZX_UART011_LCRH, + [REG_IBRD] = ZX_UART011_IBRD, + [REG_FBRD] = ZX_UART011_FBRD, + [REG_CR] = ZX_UART011_CR, + [REG_IFLS] = ZX_UART011_IFLS, + [REG_IMSC] = ZX_UART011_IMSC, + [REG_RIS] = ZX_UART011_RIS, + [REG_MIS] = ZX_UART011_MIS, + [REG_ICR] = ZX_UART011_ICR, + [REG_DMACR] = ZX_UART011_DMACR, +}; + +static unsigned int get_fifosize_zte(struct amba_device *dev) +{ + return 16; +} + +static struct vendor_data vendor_zte = { + .reg_offset = pl011_zte_offsets, + .access_32b = true, + .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, + .fr_busy = ZX_UART01x_FR_BUSY, + .fr_dsr = ZX_UART01x_FR_DSR, + .fr_cts = ZX_UART01x_FR_CTS, + .fr_ri = ZX_UART011_FR_RI, + .get_fifosize = get_fifosize_zte, +}; + /* Deals with DMA transactions */ struct pl011_dmabuf { @@ -3180,6 +3212,16 @@ static const struct amba_id pl011_ids[] = { .mask = 0x000fffff, .data = &vendor_nvidia, }, + { + /* This is an invented ID. The actual hardware that contains + * these ZTE UARTs (zx29 boards) has no AMBA PIDs stored. ZTE + * JEDEC ID (ignoring banks) and the "011" part number as used + * by ARM. + */ + .id = 0x0008c011, + .mask = 0x000fffff, + .data = &vendor_zte, + }, { 0, 0 }, }; From 10fc708b4de7f86002d2d735a2dbf3b5b7f65692 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:45 +0500 Subject: [PATCH 1556/5214] serial: 8250_dw: unregister 8250 port if clk_notifier_register() fails dw8250_probe() registers the 8250 port via serial8250_register_8250_port() and then, if the device has a clock, registers a clock notifier. If clk_notifier_register() fails, probe returns the error but leaves the 8250 port registered. The matching serial8250_unregister_port() lives in dw8250_remove(), which is not called when probe fails, so the port slot stays occupied until the device is rebound or the system is rebooted. The devm-allocated driver data is freed while the port still references it (via the saved private_data and serial_in/serial_out callbacks), so any access to that port slot before a rebind is a use-after-free hazard. Unregister the port on the clk_notifier_register() error path. Fixes: cc816969d7b5 ("serial: 8250_dw: Fix common clocks usage race condition") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260514143746.23671-2-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 55e40c10f46a..2d30a164eeae 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -839,8 +839,10 @@ static int dw8250_probe(struct platform_device *pdev) */ if (data->clk) { err = clk_notifier_register(data->clk, &data->clk_notifier); - if (err) + if (err) { + serial8250_unregister_port(data->data.line); return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); + } queue_work(system_dfl_wq, &data->clk_work); } From 01569a29af76aec748b77e0629bcb5615c25468b Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:46 +0500 Subject: [PATCH 1557/5214] serial: 8250_dw: remove clock-notifier infrastructure The clock notifier and matching work_struct in dw8250_data were added in 2020 for the Baikal-T1 SoC, whose multiple UART ports share a single reference clock and need to be informed when another consumer re-rates that clock. Baikal SoC support has since been removed from the kernel (see e.g. commit 5d6c477687ae ("clk: baikal-t1: Remove not-going-to-be-supported code for Baikal SoC") and the matching removals across bus/, mtd/, PCI/, hwmon/, memory/). No remaining in-tree user needs the cross-device baudclk rate-change notification path: the only configuration that wired up the notifier was Baikal-T1's shared reference clock topology. Drop the now-unused clock-notifier and its deferred-update worker: - struct dw8250_data fields clk_notifier and clk_work, - the clk_to_dw8250_data() and work_to_dw8250_data() helpers, - the dw8250_clk_work_cb() and dw8250_clk_notifier_cb() callbacks, - the INIT_WORK / notifier_call setup in dw8250_probe(), - the clk_notifier_register() / queue_work() in dw8250_probe(), - the matching clk_notifier_unregister() / flush_work() in dw8250_remove(), - the stale comment in dw8250_set_termios() about the worker blocking, - the linux/notifier.h and linux/workqueue.h includes that are no longer used. dw8250_set_termios() keeps calling clk_set_rate() directly, which is all the remaining single-UART configurations require. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260514143746.23671-3-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 81 ------------------------------- 1 file changed, 81 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 2d30a164eeae..7920e2cb5307 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -19,13 +19,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include @@ -75,8 +73,6 @@ struct dw8250_data { u32 msr_mask_off; struct clk *clk; struct clk *pclk; - struct notifier_block clk_notifier; - struct work_struct clk_work; struct reset_control *rst; unsigned int skip_autocfg:1; @@ -91,16 +87,6 @@ static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) return container_of(data, struct dw8250_data, data); } -static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) -{ - return container_of(nb, struct dw8250_data, clk_notifier); -} - -static inline struct dw8250_data *work_to_dw8250_data(struct work_struct *work) -{ - return container_of(work, struct dw8250_data, clk_work); -} - static inline u32 dw8250_modify_msr(struct uart_port *p, unsigned int offset, u32 value) { struct dw8250_data *d = to_dw8250_data(p->private_data); @@ -473,46 +459,6 @@ static int dw8250_handle_irq(struct uart_port *p) return 1; } -static void dw8250_clk_work_cb(struct work_struct *work) -{ - struct dw8250_data *d = work_to_dw8250_data(work); - struct uart_8250_port *up; - unsigned long rate; - - rate = clk_get_rate(d->clk); - if (rate <= 0) - return; - - up = serial8250_get_port(d->data.line); - - serial8250_update_uartclk(&up->port, rate); -} - -static int dw8250_clk_notifier_cb(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct dw8250_data *d = clk_to_dw8250_data(nb); - - /* - * We have no choice but to defer the uartclk update due to two - * deadlocks. First one is caused by a recursive mutex lock which - * happens when clk_set_rate() is called from dw8250_set_termios(). - * Second deadlock is more tricky and is caused by an inverted order of - * the clk and tty-port mutexes lock. It happens if clock rate change - * is requested asynchronously while set_termios() is executed between - * tty-port mutex lock and clk_set_rate() function invocation and - * vise-versa. Anyway if we didn't have the reference clock alteration - * in the dw8250_set_termios() method we wouldn't have needed this - * deferred event handling complication. - */ - if (event == POST_RATE_CHANGE) { - queue_work(system_dfl_wq, &d->clk_work); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - static void dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) { @@ -536,10 +482,6 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, clk_disable_unprepare(d->clk); rate = clk_round_rate(d->clk, newrate); if (rate > 0) { - /* - * Note that any clock-notifier worker will block in - * serial8250_update_uartclk() until we are done. - */ ret = clk_set_rate(d->clk, newrate); if (!ret) p->uartclk = rate; @@ -772,9 +714,6 @@ static int dw8250_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(data->clk), "failed to get baudclk\n"); - INIT_WORK(&data->clk_work, dw8250_clk_work_cb); - data->clk_notifier.notifier_call = dw8250_clk_notifier_cb; - if (data->clk) p->uartclk = clk_get_rate(data->clk); @@ -832,20 +771,6 @@ static int dw8250_probe(struct platform_device *pdev) if (data->data.line < 0) return data->data.line; - /* - * Some platforms may provide a reference clock shared between several - * devices. In this case any clock state change must be known to the - * UART port at least post factum. - */ - if (data->clk) { - err = clk_notifier_register(data->clk, &data->clk_notifier); - if (err) { - serial8250_unregister_port(data->data.line); - return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); - } - queue_work(system_dfl_wq, &data->clk_work); - } - platform_set_drvdata(pdev, data); pm_runtime_enable(dev); @@ -860,12 +785,6 @@ static void dw8250_remove(struct platform_device *pdev) pm_runtime_get_sync(dev); - if (data->clk) { - clk_notifier_unregister(data->clk, &data->clk_notifier); - - flush_work(&data->clk_work); - } - serial8250_unregister_port(data->data.line); pm_runtime_disable(dev); From cd8e495713de3881658560aa1603bc5cc87994ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 12:14:56 +0200 Subject: [PATCH 1558/5214] tty: serial: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace in the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260518101456.632410-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 8 ++++---- drivers/tty/serial/sc16is7xx_i2c.c | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index bad5329a0c84..e28e3065c99d 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1699,10 +1699,10 @@ static void max310x_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id max310x_i2c_id_table[] = { - { "max3107", (kernel_ulong_t)&max3107_devtype, }, - { "max3108", (kernel_ulong_t)&max3108_devtype, }, - { "max3109", (kernel_ulong_t)&max3109_devtype, }, - { "max14830", (kernel_ulong_t)&max14830_devtype, }, + { .name = "max3107", .driver_data = (kernel_ulong_t)&max3107_devtype }, + { .name = "max3108", .driver_data = (kernel_ulong_t)&max3108_devtype }, + { .name = "max3109", .driver_data = (kernel_ulong_t)&max3109_devtype }, + { .name = "max14830", .driver_data = (kernel_ulong_t)&max14830_devtype }, { } }; MODULE_DEVICE_TABLE(i2c, max310x_i2c_id_table); diff --git a/drivers/tty/serial/sc16is7xx_i2c.c b/drivers/tty/serial/sc16is7xx_i2c.c index 699376c3b3a5..6c2a697556a6 100644 --- a/drivers/tty/serial/sc16is7xx_i2c.c +++ b/drivers/tty/serial/sc16is7xx_i2c.c @@ -39,13 +39,13 @@ static void sc16is7xx_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { - { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, - { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, - { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, - { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { .name = "sc16is74x", .driver_data = (kernel_ulong_t)&sc16is74x_devtype }, + { .name = "sc16is740", .driver_data = (kernel_ulong_t)&sc16is74x_devtype }, + { .name = "sc16is741", .driver_data = (kernel_ulong_t)&sc16is74x_devtype }, + { .name = "sc16is750", .driver_data = (kernel_ulong_t)&sc16is750_devtype }, + { .name = "sc16is752", .driver_data = (kernel_ulong_t)&sc16is752_devtype }, + { .name = "sc16is760", .driver_data = (kernel_ulong_t)&sc16is760_devtype }, + { .name = "sc16is762", .driver_data = (kernel_ulong_t)&sc16is762_devtype }, { } }; MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); From 578b269e2f5c6d990d3970a11ef43606f80dc5f8 Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Mon, 18 May 2026 23:26:55 +0530 Subject: [PATCH 1559/5214] serial: qcom-geni: trace: Add tracepoint support for Qualcomm GENI serial Add tracepoint support to the Qualcomm GENI serial driver to provide runtime visibility into driver behavior without requiring invasive debug patches. The trace events cover UART termios configuration, clock setup, modem control state, interrupt status, and TX/RX data, making it easier to diagnose communication issues in the field. Reviewed-by: Konrad Dybcio Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260518-add-tracepoints-for-qcom-geni-serial-v3-1-b4addb151376@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- include/trace/events/qcom_geni_serial.h | 164 ++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 include/trace/events/qcom_geni_serial.h diff --git a/include/trace/events/qcom_geni_serial.h b/include/trace/events/qcom_geni_serial.h new file mode 100644 index 000000000000..417ec01f9fc8 --- /dev/null +++ b/include/trace/events/qcom_geni_serial.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_geni_serial + +#if !defined(_TRACE_QCOM_GENI_SERIAL_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_GENI_SERIAL_H + +#include +#include + +TRACE_EVENT(geni_serial_set_termios, + TP_PROTO(struct device *dev, unsigned int baud, + unsigned int bits_per_char, u32 tx_trans_cfg, + u32 tx_parity_cfg, u32 rx_trans_cfg, + u32 rx_parity_cfg, u32 stop_bit_len), + TP_ARGS(dev, baud, bits_per_char, tx_trans_cfg, tx_parity_cfg, + rx_trans_cfg, rx_parity_cfg, stop_bit_len), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, baud) + __field(unsigned int, bits_per_char) + __field(u32, tx_trans_cfg) + __field(u32, tx_parity_cfg) + __field(u32, rx_trans_cfg) + __field(u32, rx_parity_cfg) + __field(u32, stop_bit_len) + ), + + TP_fast_assign(__assign_str(name); + __entry->baud = baud; + __entry->bits_per_char = bits_per_char; + __entry->tx_trans_cfg = tx_trans_cfg; + __entry->tx_parity_cfg = tx_parity_cfg; + __entry->rx_trans_cfg = rx_trans_cfg; + __entry->rx_parity_cfg = rx_parity_cfg; + __entry->stop_bit_len = stop_bit_len; + ), + + TP_printk("%s: baud=%u bpc=%u tx_trans=0x%08x tx_par=0x%08x rx_trans=0x%08x rx_par=0x%08x stop=%u", + __get_str(name), __entry->baud, __entry->bits_per_char, + __entry->tx_trans_cfg, __entry->tx_parity_cfg, + __entry->rx_trans_cfg, __entry->rx_parity_cfg, + __entry->stop_bit_len) +); + +TRACE_EVENT(geni_serial_clk_cfg, + TP_PROTO(struct device *dev, unsigned int desired_rate, + unsigned long clk_rate, unsigned int clk_div, + unsigned int clk_idx), + TP_ARGS(dev, desired_rate, clk_rate, clk_div, clk_idx), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, desired_rate) + __field(unsigned long, clk_rate) + __field(unsigned int, clk_div) + __field(unsigned int, clk_idx) + ), + + TP_fast_assign(__assign_str(name); + __entry->desired_rate = desired_rate; + __entry->clk_rate = clk_rate; + __entry->clk_div = clk_div; + __entry->clk_idx = clk_idx; + ), + + TP_printk("%s: desired_rate=%u clk_rate=%lu clk_div=%u clk_idx=%u", + __get_str(name), __entry->desired_rate, __entry->clk_rate, + __entry->clk_div, __entry->clk_idx) +); + +TRACE_EVENT(geni_serial_irq, + TP_PROTO(struct device *dev, u32 m_irq, u32 s_irq, + u32 dma_tx, u32 dma_rx), + TP_ARGS(dev, m_irq, s_irq, dma_tx, dma_rx), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(u32, m_irq) + __field(u32, s_irq) + __field(u32, dma_tx) + __field(u32, dma_rx) + ), + + TP_fast_assign(__assign_str(name); + __entry->m_irq = m_irq; + __entry->s_irq = s_irq; + __entry->dma_tx = dma_tx; + __entry->dma_rx = dma_rx; + ), + + TP_printk("%s: m_irq=0x%08x s_irq=0x%08x dma_tx=0x%08x dma_rx=0x%08x", + __get_str(name), __entry->m_irq, __entry->s_irq, + __entry->dma_tx, __entry->dma_rx) +); + +DECLARE_EVENT_CLASS(geni_serial_data, + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len), + TP_ARGS(dev, buf, len), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, len) + __dynamic_array(u8, data, len) + ), + + TP_fast_assign(__assign_str(name); + __entry->len = len; + memcpy(__get_dynamic_array(data), buf, len); + ), + + TP_printk("%s: len=%u data=%s", + __get_str(name), __entry->len, + __print_hex(__get_dynamic_array(data), __entry->len)) +); + +DEFINE_EVENT(geni_serial_data, geni_serial_tx_data, + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len), + TP_ARGS(dev, buf, len) +); + +DEFINE_EVENT(geni_serial_data, geni_serial_rx_data, + TP_PROTO(struct device *dev, const u8 *buf, unsigned int len), + TP_ARGS(dev, buf, len) +); + +TRACE_EVENT(geni_serial_set_mctrl, + TP_PROTO(struct device *dev, unsigned int mctrl, + u32 uart_manual_rfr), + TP_ARGS(dev, mctrl, uart_manual_rfr), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, mctrl) + __field(u32, uart_manual_rfr) + ), + + TP_fast_assign(__assign_str(name); + __entry->mctrl = mctrl; + __entry->uart_manual_rfr = uart_manual_rfr; + ), + + TP_printk("%s: mctrl=0x%04x uart_manual_rfr=0x%08x", + __get_str(name), __entry->mctrl, __entry->uart_manual_rfr) +); + +TRACE_EVENT(geni_serial_get_mctrl, + TP_PROTO(struct device *dev, unsigned int mctrl, u32 geni_ios), + TP_ARGS(dev, mctrl, geni_ios), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, mctrl) + __field(u32, geni_ios) + ), + + TP_fast_assign(__assign_str(name); + __entry->mctrl = mctrl; + __entry->geni_ios = geni_ios; + ), + + TP_printk("%s: mctrl=0x%04x geni_ios=0x%08x", + __get_str(name), __entry->mctrl, __entry->geni_ios) +); + +#endif /* _TRACE_QCOM_GENI_SERIAL_H */ + +/* This part must be outside protection */ +#include From 5aaaf31af7d61c20b283dba9e84faf0d10fb265b Mon Sep 17 00:00:00 2001 From: Akash Sukhavasi Date: Thu, 21 May 2026 11:21:35 -0500 Subject: [PATCH 1560/5214] dt-bindings: serial: rs485: remove deprecated .txt binding stub The plain text binding file was superseded by the YAML schema in commit d50f974c4f7f ("dt-bindings: serial: Convert rs485 bindings to json-schema"). The file now contains only a redirect notice. Remove it, and update references in serial_core.c and serial-rs485.rst to point to the YAML schema. Signed-off-by: Akash Sukhavasi Acked-by: Conor Dooley Link: https://patch.msgid.link/20260521162137.6325-1-akash.sukhavasi@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/rs485.txt | 1 - Documentation/driver-api/serial/serial-rs485.rst | 2 +- drivers/tty/serial/serial_core.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 Documentation/devicetree/bindings/serial/rs485.txt diff --git a/Documentation/devicetree/bindings/serial/rs485.txt b/Documentation/devicetree/bindings/serial/rs485.txt deleted file mode 100644 index a7fe93efc4a5..000000000000 --- a/Documentation/devicetree/bindings/serial/rs485.txt +++ /dev/null @@ -1 +0,0 @@ -See rs485.yaml diff --git a/Documentation/driver-api/serial/serial-rs485.rst b/Documentation/driver-api/serial/serial-rs485.rst index dce061ef7647..f53043d21071 100644 --- a/Documentation/driver-api/serial/serial-rs485.rst +++ b/Documentation/driver-api/serial/serial-rs485.rst @@ -132,4 +132,4 @@ RS485 Serial Communications 6. References ============= -.. [#DT-bindings] Documentation/devicetree/bindings/serial/rs485.txt +.. [#DT-bindings] Documentation/devicetree/bindings/serial/rs485.yaml diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 98ace5c492fa..a530ad372b43 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3511,7 +3511,7 @@ EXPORT_SYMBOL_GPL(uart_try_toggle_sysrq); * @port: uart device's target port * * This function implements the device tree binding described in - * Documentation/devicetree/bindings/serial/rs485.txt. + * Documentation/devicetree/bindings/serial/rs485.yaml. */ int uart_get_rs485_mode(struct uart_port *port) { From 0c6bf45e5a345cc3b9ffbeaf9083ecac3c2293eb Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Tue, 19 May 2026 11:57:00 +0200 Subject: [PATCH 1561/5214] serial: 8250: fix possible ISR soft lockup There are rare cases in which the host gets stuck in the ISR because it is flooded with messages during the startup phase. The reason for the soft lockup in the ISR is the missing FIFO error IRQ (FIFOE) handling. Not handling it and reporting IRQ_HANDLED triggers the IRQ immediately again. Fix this by adding a check for the FIFOE status and clearing the FIFO if no data is ready (DR). This behavior was observed on an AM62L device which uses the OMAP 8250 driver. Fix it for all 8250 drivers, since the OMAP driver's special IRQ setup handling may trigger this behavior more frequently, but it is not ensured that other 8250 drivers aren't affected. Signed-off-by: Marco Felsch Link: https://patch.msgid.link/20260519-v7-1-topic-serial-8250-v1-1-56b04293a246@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index ef245114105b..141229b9bc17 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1799,6 +1799,13 @@ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) status = serial_lsr_in(up); + /* + * Recover from no-data-ready and FIFO error condition to avoid getting + * stuck in the ISR. + */ + if (!(status & UART_LSR_DR) && (status & UART_LSR_FIFOE)) + serial8250_clear_and_reinit_fifos(up); + /* * If port is stopped and there are no error conditions in the * FIFO, then don't drain the FIFO, as this may lead to TTY buffer From 255dc0ec0b79c354bff017f6d6202adaa092a1c9 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 14 May 2026 23:48:57 -0400 Subject: [PATCH 1562/5214] vt: merge ucs_is_zero_width()/ucs_is_double_width() into ucs_get_width() The hot path in vc_process_ucs() asks two independent questions about the same code point -- "is it double-width?" and "is it zero-width?" -- and was answering each with its own bsearch over its own table. For anything past the leading bounds check that meant two scans of the BMP width tables back to back for what is logically a single lookup. Replace both with one ucs_get_width(cp) returning 0, 1, or 2 in a single bsearch, while keeping the total table footprint at the same 2384 B as before. To do so, merge the zero-width and double-width ranges per region into one sorted-by-`first` table. BMP entries stay 4 bytes; per-entry width is hosted in spare bits of the non-BMP table's `last` field. Non-BMP code points use only 20 of 32 bits, so each u32 has 12 unused high bits. Store first/last shifted left by 12 and use the low 12 bits of `last` for metadata: bit 11 is this entry's own width flag, bits 0..7 host an 8-bit chunk of the BMP double-width bitmap. Because the metadata bits sit strictly below the lowest cp-scale bit, the bsearch comparator remains a plain u32 compare on shifted keys with no masking. In vc_process_ucs() the overwhelmingly common single-width path now collapses to a single predicted branch: if (likely(w == 1)) return 1; Note: scripts/checkpatch.pl complains about "Macros with complex values should be enclosed in parentheses" for the BMP_*WIDTH and RANGE_*WIDTH macros. They are deliberately defined to expand to a comma-separated (first, last) pair so they can populate the two adjacent fields of a struct initializer; wrapping them in parentheses would turn that into a comma-expression and defeat the whole construction. Please ignore. Signed-off-by: Nicolas Pitre Link: https://patch.msgid.link/20260515034857.2514225-1-nico@fluxnic.net Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/gen_ucs_width_table.py | 119 ++- drivers/tty/vt/ucs.c | 174 +++-- drivers/tty/vt/ucs_width_table.h_shipped | 919 ++++++++++++----------- drivers/tty/vt/vt.c | 11 +- include/linux/consolemap.h | 12 +- 5 files changed, 678 insertions(+), 557 deletions(-) diff --git a/drivers/tty/vt/gen_ucs_width_table.py b/drivers/tty/vt/gen_ucs_width_table.py index 76e80ebeff13..4d2476842750 100755 --- a/drivers/tty/vt/gen_ucs_width_table.py +++ b/drivers/tty/vt/gen_ucs_width_table.py @@ -190,12 +190,23 @@ def write_tables(zero_width_ranges, double_width_ranges, out_file=DEFAULT_OUT_FI """ Write the generated tables to C header file. + The output uses a single sorted-by-`first` table per region (BMP and + non-BMP), with zero-width and double-width ranges merged together. The + non-BMP table also hosts the BMP double-width bitmap in spare bits of + `last`. See the encoding comment at the top of ucs.c for the layout. + Args: zero_width_ranges: List of (start, end) ranges for zero-width characters double_width_ranges: List of (start, end) ranges for double-width characters out_file: Output file name (default: DEFAULT_OUT_FILE) """ + # Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field. + # 8 bits makes `idx / BITS_PER_CHUNK` / `idx % BITS_PER_CHUNK` compile to + # a cheap shift+mask in the lookup. The chunk size is also emitted as + # UCS_NONBMP_BMP_BITS in the generated header so ucs.c stays in sync. + BITS_PER_CHUNK = 8 + # Function to split ranges into BMP (16-bit) and non-BMP (above 16-bit) def split_ranges_by_size(ranges): bmp_ranges = [] @@ -217,6 +228,40 @@ def write_tables(zero_width_ranges, double_width_ranges, out_file=DEFAULT_OUT_FI zero_width_bmp, zero_width_non_bmp = split_ranges_by_size(zero_width_ranges) double_width_bmp, double_width_non_bmp = split_ranges_by_size(double_width_ranges) + # Merge zero- and double-width ranges per region, tagging each with its + # width, then sort by `first` so binary search works on the union. + bmp_entries = sorted( + [(s, e, 0) for s, e in zero_width_bmp] + + [(s, e, 2) for s, e in double_width_bmp], + key=lambda t: t[0]) + nonbmp_entries = sorted( + [(s, e, 0) for s, e in zero_width_non_bmp] + + [(s, e, 2) for s, e in double_width_non_bmp], + key=lambda t: t[0]) + + # Build the BMP double-width bitmap: one bit per BMP entry (in sort + # order), set iff that entry is double-width. Pack into BITS_PER_CHUNK- + # wide chunks, with bit j of the chunk corresponding to entry + # (chunk_index * BITS_PER_CHUNK + j). + bmp_w2_bits = [1 if w == 2 else 0 for _, _, w in bmp_entries] + n_chunks = (len(bmp_w2_bits) + BITS_PER_CHUNK - 1) // BITS_PER_CHUNK + + if n_chunks > len(nonbmp_entries): + raise RuntimeError( + f"BMP bitmap needs {n_chunks} host entries, " + f"but only {len(nonbmp_entries)} non-BMP entries are available") + + chunks = [] # list of (base_index, end_index, packed_value) + for c in range(n_chunks): + base = c * BITS_PER_CHUNK + end_idx = min(base + BITS_PER_CHUNK - 1, len(bmp_w2_bits) - 1) + value = 0 + for j in range(BITS_PER_CHUNK): + k = base + j + if k < len(bmp_w2_bits) and bmp_w2_bits[k]: + value |= 1 << j + chunks.append((base, end_idx, value)) + # Function to generate code point description comments def get_code_point_comment(start, end): try: @@ -242,48 +287,47 @@ def write_tables(zero_width_ranges, double_width_ranges, out_file=DEFAULT_OUT_FI * Auto-generated by {this_file} * * Unicode Version: {unicodedata.unidata_version} + * + * Zero-width and double-width ranges are merged into one sorted-by-`first` + * table per region. The non-BMP table additionally hosts the BMP + * double-width bitmap in the low {BITS_PER_CHUNK} bits of `last` of its + * first {n_chunks} entries (covering {len(bmp_w2_bits)} BMP entries). + * See ucs.c for the encoding details and the lookup code. */ -/* Zero-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ -static const struct ucs_interval16 ucs_zero_width_bmp_ranges[] = {{ +/* Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field. */ +#define UCS_NONBMP_BMP_BITS {BITS_PER_CHUNK} + +/* Combined zero- and double-width ranges + * (BMP - Basic Multilingual Plane, U+0000 to U+FFFF). */ +static const struct ucs_width16 ucs_bmp_ranges[] = {{ """) - for start, end in zero_width_bmp: - comment = get_code_point_comment(start, end) - f.write(f"\t{{ 0x{start:04X}, 0x{end:04X} }}, {comment}\n") + for s, e, w in bmp_entries: + macro = "BMP_0WIDTH" if w == 0 else "BMP_2WIDTH" + comment = get_code_point_comment(s, e) + f.write(f"\t{{ {macro}(0x{s:04X}, 0x{e:04X}) }}, {comment}\n") - f.write("""\ -}; + f.write(f"""\ +}}; -/* Zero-width character ranges (non-BMP, U+10000 and above) */ -static const struct ucs_interval32 ucs_zero_width_non_bmp_ranges[] = { +/* Combined zero- and double-width ranges (non-BMP, U+10000 and above). + * The first {n_chunks} entries host the BMP double-width bitmap in the low + * {BITS_PER_CHUNK} bits of `last`. */ +static const struct ucs_width32 ucs_nonbmp_ranges[] = {{ """) - for start, end in zero_width_non_bmp: - comment = get_code_point_comment(start, end) - f.write(f"\t{{ 0x{start:05X}, 0x{end:05X} }}, {comment}\n") - - f.write("""\ -}; - -/* Double-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ -static const struct ucs_interval16 ucs_double_width_bmp_ranges[] = { -""") - - for start, end in double_width_bmp: - comment = get_code_point_comment(start, end) - f.write(f"\t{{ 0x{start:04X}, 0x{end:04X} }}, {comment}\n") - - f.write("""\ -}; - -/* Double-width character ranges (non-BMP, U+10000 and above) */ -static const struct ucs_interval32 ucs_double_width_non_bmp_ranges[] = { -""") - - for start, end in double_width_non_bmp: - comment = get_code_point_comment(start, end) - f.write(f"\t{{ 0x{start:05X}, 0x{end:05X} }}, {comment}\n") + for i, (s, e, w) in enumerate(nonbmp_entries): + macro = "RANGE_0WIDTH" if w == 0 else "RANGE_2WIDTH" + comment = get_code_point_comment(s, e) + if i < len(chunks): + base, end_idx, value = chunks[i] + f.write( + f"\t{{ {macro}(0x{s:05X}, 0x{e:05X}) {comment}\n" + f"\t | BMP_2W_BITS(0b{value:0{BITS_PER_CHUNK}b}) }}," + f" /* BMP entries [{base:>3}..{end_idx:>3}] */\n") + else: + f.write(f"\t{{ {macro}(0x{s:05X}, 0x{e:05X}) }}, {comment}\n") f.write("};\n") @@ -301,7 +345,10 @@ if __name__ == "__main__": # Print summary zero_width_count = sum(end - start + 1 for start, end in zero_width_ranges) double_width_count = sum(end - start + 1 for start, end in double_width_ranges) + n_zero = len(zero_width_ranges) + n_double = len(double_width_ranges) print(f"Generated {args.output_file} with:") - print(f"- {len(zero_width_ranges)} zero-width ranges covering ~{zero_width_count} code points") - print(f"- {len(double_width_ranges)} double-width ranges covering ~{double_width_count} code points") + print(f"- {n_zero} zero-width ranges covering ~{zero_width_count} code points") + print(f"- {n_double} double-width ranges covering ~{double_width_count} code points") + print(f"- {n_zero + n_double} merged ranges total") print(f"- Unicode Version: {unicodedata.unidata_version}") diff --git a/drivers/tty/vt/ucs.c b/drivers/tty/vt/ucs.c index 03877485dfb7..fc41c0bb5d7b 100644 --- a/drivers/tty/vt/ucs.c +++ b/drivers/tty/vt/ucs.c @@ -4,96 +4,138 @@ */ #include +#include #include #include -#include +#include -struct ucs_interval16 { +struct ucs_width16 { u16 first; u16 last; }; -struct ucs_interval32 { +struct ucs_width32 { u32 first; u32 last; }; +/* + * Width table encoding (consumed by ucs_width_table.h): + * + * Zero- and double-width ranges are merged into one sorted-by-`first` table + * per region (BMP / non-BMP). The BMP table stores plain (first, last) + * pairs; per-entry width lives in a packed bitmap *hosted by the non-BMP + * table*. + * + * That hosting is the whole point of the encoding. Non-BMP code points use + * only 20 bits, so each u32 has 12 spare high bits sitting around doing + * nothing — we'd rather use them than spend a separate parallel array for + * width and BMP-bitmap bits. So we move the cp value up by UCS_CP_SHIFT + * and stash metadata in the now-free low bits of `last`: + * - bit UCS_NONBMP_W2_FLAG_BIT: this entry's own width (0=zero, 1=double), + * - bits 0..UCS_NONBMP_BMP_BITS-1: a chunk of the BMP double-width + * bitmap. Bit `j` of the chunk in non-BMP entry `c` is set iff BMP + * entry (c * UCS_NONBMP_BMP_BITS + j) is double-width. The first + * ceil(N_BMP / UCS_NONBMP_BMP_BITS) non-BMP entries carry the bitmap; + * the rest leave these bits zero. + * + * Because the metadata bits sit strictly below the lowest cp-scale bit, + * the bsearch comparator does plain u32 comparison on the shifted key and + * stored values without masking — ordering between distinct code points is + * undisturbed. + */ +#define UCS_CP_SHIFT 12 +#define UCS_NONBMP_W2_FLAG_BIT 11 +#define UCS_NONBMP_W2_FLAG (1u << UCS_NONBMP_W2_FLAG_BIT) + +#define BMP_0WIDTH(first, last) first, last +#define BMP_2WIDTH(first, last) first, last +#define RANGE_0WIDTH(first, last) \ + (u32)(first) << UCS_CP_SHIFT, (u32)(last) << UCS_CP_SHIFT +#define RANGE_2WIDTH(first, last) \ + (u32)(first) << UCS_CP_SHIFT, ((u32)(last) << UCS_CP_SHIFT) | UCS_NONBMP_W2_FLAG +#define BMP_2W_BITS(b) (b) + #include "ucs_width_table.h" -static int interval16_cmp(const void *key, const void *element) -{ - u16 cp = *(u16 *)key; - const struct ucs_interval16 *entry = element; - - if (cp < entry->first) - return -1; - if (cp > entry->last) - return 1; - return 0; -} - -static int interval32_cmp(const void *key, const void *element) -{ - u32 cp = *(u32 *)key; - const struct ucs_interval32 *entry = element; - - if (cp < entry->first) - return -1; - if (cp > entry->last) - return 1; - return 0; -} - -static bool cp_in_range16(u16 cp, const struct ucs_interval16 *ranges, size_t size) -{ - if (cp < ranges[0].first || cp > ranges[size - 1].last) - return false; - - return __inline_bsearch(&cp, ranges, size, sizeof(*ranges), - interval16_cmp) != NULL; -} - -static bool cp_in_range32(u32 cp, const struct ucs_interval32 *ranges, size_t size) -{ - if (cp < ranges[0].first || cp > ranges[size - 1].last) - return false; - - return __inline_bsearch(&cp, ranges, size, sizeof(*ranges), - interval32_cmp) != NULL; -} +static_assert(UCS_NONBMP_BMP_BITS <= UCS_NONBMP_W2_FLAG_BIT, + "BMP bitmap chunk would overlap the per-entry width flag"); +static_assert(UCS_NONBMP_W2_FLAG_BIT < UCS_CP_SHIFT, + "Metadata bits collide with the shifted cp value"); +static_assert(DIV_ROUND_UP(ARRAY_SIZE(ucs_bmp_ranges), UCS_NONBMP_BMP_BITS) + <= ARRAY_SIZE(ucs_nonbmp_ranges), + "Not enough non-BMP entries to host the BMP width bitmap"); #define UCS_IS_BMP(cp) ((cp) <= 0xffff) -/** - * ucs_is_zero_width() - Determine if a Unicode code point is zero-width. - * @cp: Unicode code point (UCS-4) - * - * Return: true if the character is zero-width, false otherwise - */ -bool ucs_is_zero_width(u32 cp) +static int width16_cmp(const void *key, const void *element) { - if (UCS_IS_BMP(cp)) - return cp_in_range16(cp, ucs_zero_width_bmp_ranges, - ARRAY_SIZE(ucs_zero_width_bmp_ranges)); - else - return cp_in_range32(cp, ucs_zero_width_non_bmp_ranges, - ARRAY_SIZE(ucs_zero_width_non_bmp_ranges)); + u16 cp = *(u16 *)key; + const struct ucs_width16 *entry = element; + + if (cp < entry->first) + return -1; + if (cp > entry->last) + return 1; + return 0; +} + +static int width32_cmp(const void *key, const void *element) +{ + u32 k = *(u32 *)key; + const struct ucs_width32 *entry = element; + + if (k < entry->first) + return -1; + if (k > entry->last) + return 1; + return 0; } /** - * ucs_is_double_width() - Determine if a Unicode code point is double-width. + * ucs_get_width() - Get the display width of a Unicode code point. * @cp: Unicode code point (UCS-4) * - * Return: true if the character is double-width, false otherwise + * Return: 2 for double-width (East Asian Wide/Fullwidth, emoji, ...), + * 0 for zero-width (combining marks, format characters, ...), + * 1 for everything else (the common case). */ -bool ucs_is_double_width(u32 cp) +unsigned int ucs_get_width(u32 cp) { - if (UCS_IS_BMP(cp)) - return cp_in_range16(cp, ucs_double_width_bmp_ranges, - ARRAY_SIZE(ucs_double_width_bmp_ranges)); - else - return cp_in_range32(cp, ucs_double_width_non_bmp_ranges, - ARRAY_SIZE(ucs_double_width_non_bmp_ranges)); + const struct ucs_width16 *e16; + const struct ucs_width32 *e32; + unsigned int idx; + u32 k; + + if (UCS_IS_BMP(cp)) { + u16 bmp = cp; + + if (bmp < ucs_bmp_ranges[0].first || + bmp > ucs_bmp_ranges[ARRAY_SIZE(ucs_bmp_ranges) - 1].last) + return 1; + + e16 = __inline_bsearch(&bmp, ucs_bmp_ranges, + ARRAY_SIZE(ucs_bmp_ranges), + sizeof(*ucs_bmp_ranges), width16_cmp); + if (!e16) + return 1; + + idx = e16 - ucs_bmp_ranges; + return (ucs_nonbmp_ranges[idx / UCS_NONBMP_BMP_BITS].last + >> (idx % UCS_NONBMP_BMP_BITS)) & 1 ? 2 : 0; + } + + k = cp << UCS_CP_SHIFT; + if (k < ucs_nonbmp_ranges[0].first || + k > ucs_nonbmp_ranges[ARRAY_SIZE(ucs_nonbmp_ranges) - 1].last) + return 1; + + e32 = __inline_bsearch(&k, ucs_nonbmp_ranges, + ARRAY_SIZE(ucs_nonbmp_ranges), + sizeof(*ucs_nonbmp_ranges), width32_cmp); + if (!e32) + return 1; + return (e32->last & UCS_NONBMP_W2_FLAG) ? 2 : 0; } /* diff --git a/drivers/tty/vt/ucs_width_table.h_shipped b/drivers/tty/vt/ucs_width_table.h_shipped index 6fcb8f1d577d..5cd6434bf329 100644 --- a/drivers/tty/vt/ucs_width_table.h_shipped +++ b/drivers/tty/vt/ucs_width_table.h_shipped @@ -5,449 +5,486 @@ * Auto-generated by gen_ucs_width_table.py * * Unicode Version: 16.0.0 + * + * Zero-width and double-width ranges are merged into one sorted-by-`first` + * table per region. The non-BMP table additionally hosts the BMP + * double-width bitmap in the low 8 bits of `last` of its + * first 33 entries (covering 262 BMP entries). + * See ucs.c for the encoding details and the lookup code. */ -/* Zero-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ -static const struct ucs_interval16 ucs_zero_width_bmp_ranges[] = { - { 0x00AD, 0x00AD }, /* SOFT HYPHEN */ - { 0x0300, 0x036F }, /* COMBINING GRAVE ACCENT - COMBINING LATIN SMALL LETTER X */ - { 0x0483, 0x0489 }, /* COMBINING CYRILLIC TITLO - COMBINING CYRILLIC MILLIONS SIGN */ - { 0x0591, 0x05BD }, /* HEBREW ACCENT ETNAHTA - HEBREW POINT METEG */ - { 0x05BF, 0x05BF }, /* HEBREW POINT RAFE */ - { 0x05C1, 0x05C2 }, /* HEBREW POINT SHIN DOT - HEBREW POINT SIN DOT */ - { 0x05C4, 0x05C5 }, /* HEBREW MARK UPPER DOT - HEBREW MARK LOWER DOT */ - { 0x05C7, 0x05C7 }, /* HEBREW POINT QAMATS QATAN */ - { 0x0600, 0x0605 }, /* ARABIC NUMBER SIGN - ARABIC NUMBER MARK ABOVE */ - { 0x0610, 0x061A }, /* ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM - ARABIC SMALL KASRA */ - { 0x061C, 0x061C }, /* ARABIC LETTER MARK */ - { 0x064B, 0x065F }, /* ARABIC FATHATAN - ARABIC WAVY HAMZA BELOW */ - { 0x0670, 0x0670 }, /* ARABIC LETTER SUPERSCRIPT ALEF */ - { 0x06D6, 0x06DD }, /* ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA - ARABIC END OF AYAH */ - { 0x06DF, 0x06E4 }, /* ARABIC SMALL HIGH ROUNDED ZERO - ARABIC SMALL HIGH MADDA */ - { 0x06E7, 0x06E8 }, /* ARABIC SMALL HIGH YEH - ARABIC SMALL HIGH NOON */ - { 0x06EA, 0x06ED }, /* ARABIC EMPTY CENTRE LOW STOP - ARABIC SMALL LOW MEEM */ - { 0x070F, 0x070F }, /* SYRIAC ABBREVIATION MARK */ - { 0x0711, 0x0711 }, /* SYRIAC LETTER SUPERSCRIPT ALAPH */ - { 0x0730, 0x074A }, /* SYRIAC PTHAHA ABOVE - SYRIAC BARREKH */ - { 0x07A6, 0x07B0 }, /* THAANA ABAFILI - THAANA SUKUN */ - { 0x07EB, 0x07F3 }, /* NKO COMBINING SHORT HIGH TONE - NKO COMBINING DOUBLE DOT ABOVE */ - { 0x07FD, 0x07FD }, /* NKO DANTAYALAN */ - { 0x0816, 0x0819 }, /* SAMARITAN MARK IN - SAMARITAN MARK DAGESH */ - { 0x081B, 0x0823 }, /* SAMARITAN MARK EPENTHETIC YUT - SAMARITAN VOWEL SIGN A */ - { 0x0825, 0x0827 }, /* SAMARITAN VOWEL SIGN SHORT A - SAMARITAN VOWEL SIGN U */ - { 0x0829, 0x082D }, /* SAMARITAN VOWEL SIGN LONG I - SAMARITAN MARK NEQUDAA */ - { 0x0859, 0x085B }, /* MANDAIC AFFRICATION MARK - MANDAIC GEMINATION MARK */ - { 0x0890, 0x0891 }, /* ARABIC POUND MARK ABOVE - ARABIC PIASTRE MARK ABOVE */ - { 0x0897, 0x089F }, /* ARABIC PEPET - ARABIC HALF MADDA OVER MADDA */ - { 0x08CA, 0x0903 }, /* ARABIC SMALL HIGH FARSI YEH - DEVANAGARI SIGN VISARGA */ - { 0x093A, 0x093C }, /* DEVANAGARI VOWEL SIGN OE - DEVANAGARI SIGN NUKTA */ - { 0x093E, 0x094F }, /* DEVANAGARI VOWEL SIGN AA - DEVANAGARI VOWEL SIGN AW */ - { 0x0951, 0x0957 }, /* DEVANAGARI STRESS SIGN UDATTA - DEVANAGARI VOWEL SIGN UUE */ - { 0x0962, 0x0963 }, /* DEVANAGARI VOWEL SIGN VOCALIC L - DEVANAGARI VOWEL SIGN VOCALIC LL */ - { 0x0981, 0x0983 }, /* BENGALI SIGN CANDRABINDU - BENGALI SIGN VISARGA */ - { 0x09BC, 0x09BC }, /* BENGALI SIGN NUKTA */ - { 0x09BE, 0x09C4 }, /* BENGALI VOWEL SIGN AA - BENGALI VOWEL SIGN VOCALIC RR */ - { 0x09C7, 0x09C8 }, /* BENGALI VOWEL SIGN E - BENGALI VOWEL SIGN AI */ - { 0x09CB, 0x09CD }, /* BENGALI VOWEL SIGN O - BENGALI SIGN VIRAMA */ - { 0x09D7, 0x09D7 }, /* BENGALI AU LENGTH MARK */ - { 0x09E2, 0x09E3 }, /* BENGALI VOWEL SIGN VOCALIC L - BENGALI VOWEL SIGN VOCALIC LL */ - { 0x09FE, 0x09FE }, /* BENGALI SANDHI MARK */ - { 0x0A01, 0x0A03 }, /* GURMUKHI SIGN ADAK BINDI - GURMUKHI SIGN VISARGA */ - { 0x0A3C, 0x0A3C }, /* GURMUKHI SIGN NUKTA */ - { 0x0A3E, 0x0A42 }, /* GURMUKHI VOWEL SIGN AA - GURMUKHI VOWEL SIGN UU */ - { 0x0A47, 0x0A48 }, /* GURMUKHI VOWEL SIGN EE - GURMUKHI VOWEL SIGN AI */ - { 0x0A4B, 0x0A4D }, /* GURMUKHI VOWEL SIGN OO - GURMUKHI SIGN VIRAMA */ - { 0x0A51, 0x0A51 }, /* GURMUKHI SIGN UDAAT */ - { 0x0A70, 0x0A71 }, /* GURMUKHI TIPPI - GURMUKHI ADDAK */ - { 0x0A75, 0x0A75 }, /* GURMUKHI SIGN YAKASH */ - { 0x0A81, 0x0A83 }, /* GUJARATI SIGN CANDRABINDU - GUJARATI SIGN VISARGA */ - { 0x0ABC, 0x0ABC }, /* GUJARATI SIGN NUKTA */ - { 0x0ABE, 0x0AC5 }, /* GUJARATI VOWEL SIGN AA - GUJARATI VOWEL SIGN CANDRA E */ - { 0x0AC7, 0x0AC9 }, /* GUJARATI VOWEL SIGN E - GUJARATI VOWEL SIGN CANDRA O */ - { 0x0ACB, 0x0ACD }, /* GUJARATI VOWEL SIGN O - GUJARATI SIGN VIRAMA */ - { 0x0AE2, 0x0AE3 }, /* GUJARATI VOWEL SIGN VOCALIC L - GUJARATI VOWEL SIGN VOCALIC LL */ - { 0x0AFA, 0x0AFF }, /* GUJARATI SIGN SUKUN - GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE */ - { 0x0B01, 0x0B03 }, /* ORIYA SIGN CANDRABINDU - ORIYA SIGN VISARGA */ - { 0x0B3C, 0x0B3C }, /* ORIYA SIGN NUKTA */ - { 0x0B3E, 0x0B44 }, /* ORIYA VOWEL SIGN AA - ORIYA VOWEL SIGN VOCALIC RR */ - { 0x0B47, 0x0B48 }, /* ORIYA VOWEL SIGN E - ORIYA VOWEL SIGN AI */ - { 0x0B4B, 0x0B4D }, /* ORIYA VOWEL SIGN O - ORIYA SIGN VIRAMA */ - { 0x0B55, 0x0B57 }, /* ORIYA SIGN OVERLINE - ORIYA AU LENGTH MARK */ - { 0x0B62, 0x0B63 }, /* ORIYA VOWEL SIGN VOCALIC L - ORIYA VOWEL SIGN VOCALIC LL */ - { 0x0B82, 0x0B82 }, /* TAMIL SIGN ANUSVARA */ - { 0x0BBE, 0x0BC2 }, /* TAMIL VOWEL SIGN AA - TAMIL VOWEL SIGN UU */ - { 0x0BC6, 0x0BC8 }, /* TAMIL VOWEL SIGN E - TAMIL VOWEL SIGN AI */ - { 0x0BCA, 0x0BCD }, /* TAMIL VOWEL SIGN O - TAMIL SIGN VIRAMA */ - { 0x0BD7, 0x0BD7 }, /* TAMIL AU LENGTH MARK */ - { 0x0C00, 0x0C04 }, /* TELUGU SIGN COMBINING CANDRABINDU ABOVE - TELUGU SIGN COMBINING ANUSVARA ABOVE */ - { 0x0C3C, 0x0C3C }, /* TELUGU SIGN NUKTA */ - { 0x0C3E, 0x0C44 }, /* TELUGU VOWEL SIGN AA - TELUGU VOWEL SIGN VOCALIC RR */ - { 0x0C46, 0x0C48 }, /* TELUGU VOWEL SIGN E - TELUGU VOWEL SIGN AI */ - { 0x0C4A, 0x0C4D }, /* TELUGU VOWEL SIGN O - TELUGU SIGN VIRAMA */ - { 0x0C55, 0x0C56 }, /* TELUGU LENGTH MARK - TELUGU AI LENGTH MARK */ - { 0x0C62, 0x0C63 }, /* TELUGU VOWEL SIGN VOCALIC L - TELUGU VOWEL SIGN VOCALIC LL */ - { 0x0C81, 0x0C83 }, /* KANNADA SIGN CANDRABINDU - KANNADA SIGN VISARGA */ - { 0x0CBC, 0x0CBC }, /* KANNADA SIGN NUKTA */ - { 0x0CBE, 0x0CC4 }, /* KANNADA VOWEL SIGN AA - KANNADA VOWEL SIGN VOCALIC RR */ - { 0x0CC6, 0x0CC8 }, /* KANNADA VOWEL SIGN E - KANNADA VOWEL SIGN AI */ - { 0x0CCA, 0x0CCD }, /* KANNADA VOWEL SIGN O - KANNADA SIGN VIRAMA */ - { 0x0CD5, 0x0CD6 }, /* KANNADA LENGTH MARK - KANNADA AI LENGTH MARK */ - { 0x0CE2, 0x0CE3 }, /* KANNADA VOWEL SIGN VOCALIC L - KANNADA VOWEL SIGN VOCALIC LL */ - { 0x0CF3, 0x0CF3 }, /* KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT */ - { 0x0D00, 0x0D03 }, /* MALAYALAM SIGN COMBINING ANUSVARA ABOVE - MALAYALAM SIGN VISARGA */ - { 0x0D3B, 0x0D3C }, /* MALAYALAM SIGN VERTICAL BAR VIRAMA - MALAYALAM SIGN CIRCULAR VIRAMA */ - { 0x0D3E, 0x0D44 }, /* MALAYALAM VOWEL SIGN AA - MALAYALAM VOWEL SIGN VOCALIC RR */ - { 0x0D46, 0x0D48 }, /* MALAYALAM VOWEL SIGN E - MALAYALAM VOWEL SIGN AI */ - { 0x0D4A, 0x0D4D }, /* MALAYALAM VOWEL SIGN O - MALAYALAM SIGN VIRAMA */ - { 0x0D57, 0x0D57 }, /* MALAYALAM AU LENGTH MARK */ - { 0x0D62, 0x0D63 }, /* MALAYALAM VOWEL SIGN VOCALIC L - MALAYALAM VOWEL SIGN VOCALIC LL */ - { 0x0D81, 0x0D83 }, /* SINHALA SIGN CANDRABINDU - SINHALA SIGN VISARGAYA */ - { 0x0DCA, 0x0DCA }, /* SINHALA SIGN AL-LAKUNA */ - { 0x0DCF, 0x0DD4 }, /* SINHALA VOWEL SIGN AELA-PILLA - SINHALA VOWEL SIGN KETTI PAA-PILLA */ - { 0x0DD6, 0x0DD6 }, /* SINHALA VOWEL SIGN DIGA PAA-PILLA */ - { 0x0DD8, 0x0DDF }, /* SINHALA VOWEL SIGN GAETTA-PILLA - SINHALA VOWEL SIGN GAYANUKITTA */ - { 0x0DF2, 0x0DF3 }, /* SINHALA VOWEL SIGN DIGA GAETTA-PILLA - SINHALA VOWEL SIGN DIGA GAYANUKITTA */ - { 0x0E31, 0x0E31 }, /* THAI CHARACTER MAI HAN-AKAT */ - { 0x0E34, 0x0E3A }, /* THAI CHARACTER SARA I - THAI CHARACTER PHINTHU */ - { 0x0E47, 0x0E4E }, /* THAI CHARACTER MAITAIKHU - THAI CHARACTER YAMAKKAN */ - { 0x0EB1, 0x0EB1 }, /* LAO VOWEL SIGN MAI KAN */ - { 0x0EB4, 0x0EBC }, /* LAO VOWEL SIGN I - LAO SEMIVOWEL SIGN LO */ - { 0x0EC8, 0x0ECE }, /* LAO TONE MAI EK - LAO YAMAKKAN */ - { 0x0F18, 0x0F19 }, /* TIBETAN ASTROLOGICAL SIGN -KHYUD PA - TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS */ - { 0x0F35, 0x0F35 }, /* TIBETAN MARK NGAS BZUNG NYI ZLA */ - { 0x0F37, 0x0F37 }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS */ - { 0x0F39, 0x0F39 }, /* TIBETAN MARK TSA -PHRU */ - { 0x0F3E, 0x0F3F }, /* TIBETAN SIGN YAR TSHES - TIBETAN SIGN MAR TSHES */ - { 0x0F71, 0x0F84 }, /* TIBETAN VOWEL SIGN AA - TIBETAN MARK HALANTA */ - { 0x0F86, 0x0F87 }, /* TIBETAN SIGN LCI RTAGS - TIBETAN SIGN YANG RTAGS */ - { 0x0F8D, 0x0F97 }, /* TIBETAN SUBJOINED SIGN LCE TSA CAN - TIBETAN SUBJOINED LETTER JA */ - { 0x0F99, 0x0FBC }, /* TIBETAN SUBJOINED LETTER NYA - TIBETAN SUBJOINED LETTER FIXED-FORM RA */ - { 0x0FC6, 0x0FC6 }, /* TIBETAN SYMBOL PADMA GDAN */ - { 0x102B, 0x103E }, /* MYANMAR VOWEL SIGN TALL AA - MYANMAR CONSONANT SIGN MEDIAL HA */ - { 0x1056, 0x1059 }, /* MYANMAR VOWEL SIGN VOCALIC R - MYANMAR VOWEL SIGN VOCALIC LL */ - { 0x105E, 0x1060 }, /* MYANMAR CONSONANT SIGN MON MEDIAL NA - MYANMAR CONSONANT SIGN MON MEDIAL LA */ - { 0x1062, 0x1064 }, /* MYANMAR VOWEL SIGN SGAW KAREN EU - MYANMAR TONE MARK SGAW KAREN KE PHO */ - { 0x1067, 0x106D }, /* MYANMAR VOWEL SIGN WESTERN PWO KAREN EU - MYANMAR SIGN WESTERN PWO KAREN TONE-5 */ - { 0x1071, 0x1074 }, /* MYANMAR VOWEL SIGN GEBA KAREN I - MYANMAR VOWEL SIGN KAYAH EE */ - { 0x1082, 0x108D }, /* MYANMAR CONSONANT SIGN SHAN MEDIAL WA - MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */ - { 0x108F, 0x108F }, /* MYANMAR SIGN RUMAI PALAUNG TONE-5 */ - { 0x109A, 0x109D }, /* MYANMAR SIGN KHAMTI TONE-1 - MYANMAR VOWEL SIGN AITON AI */ - { 0x135D, 0x135F }, /* ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK - ETHIOPIC COMBINING GEMINATION MARK */ - { 0x1712, 0x1715 }, /* TAGALOG VOWEL SIGN I - TAGALOG SIGN PAMUDPOD */ - { 0x1732, 0x1734 }, /* HANUNOO VOWEL SIGN I - HANUNOO SIGN PAMUDPOD */ - { 0x1752, 0x1753 }, /* BUHID VOWEL SIGN I - BUHID VOWEL SIGN U */ - { 0x1772, 0x1773 }, /* TAGBANWA VOWEL SIGN I - TAGBANWA VOWEL SIGN U */ - { 0x17B4, 0x17D3 }, /* KHMER VOWEL INHERENT AQ - KHMER SIGN BATHAMASAT */ - { 0x17DD, 0x17DD }, /* KHMER SIGN ATTHACAN */ - { 0x180B, 0x180F }, /* MONGOLIAN FREE VARIATION SELECTOR ONE - MONGOLIAN FREE VARIATION SELECTOR FOUR */ - { 0x1885, 0x1886 }, /* MONGOLIAN LETTER ALI GALI BALUDA - MONGOLIAN LETTER ALI GALI THREE BALUDA */ - { 0x18A9, 0x18A9 }, /* MONGOLIAN LETTER ALI GALI DAGALGA */ - { 0x1920, 0x192B }, /* LIMBU VOWEL SIGN A - LIMBU SUBJOINED LETTER WA */ - { 0x1930, 0x193B }, /* LIMBU SMALL LETTER KA - LIMBU SIGN SA-I */ - { 0x1A17, 0x1A1B }, /* BUGINESE VOWEL SIGN I - BUGINESE VOWEL SIGN AE */ - { 0x1A55, 0x1A5E }, /* TAI THAM CONSONANT SIGN MEDIAL RA - TAI THAM CONSONANT SIGN SA */ - { 0x1A60, 0x1A7C }, /* TAI THAM SIGN SAKOT - TAI THAM SIGN KHUEN-LUE KARAN */ - { 0x1A7F, 0x1A7F }, /* TAI THAM COMBINING CRYPTOGRAMMIC DOT */ - { 0x1AB0, 0x1ACE }, /* COMBINING DOUBLED CIRCUMFLEX ACCENT - COMBINING LATIN SMALL LETTER INSULAR T */ - { 0x1B00, 0x1B04 }, /* BALINESE SIGN ULU RICEM - BALINESE SIGN BISAH */ - { 0x1B34, 0x1B44 }, /* BALINESE SIGN REREKAN - BALINESE ADEG ADEG */ - { 0x1B6B, 0x1B73 }, /* BALINESE MUSICAL SYMBOL COMBINING TEGEH - BALINESE MUSICAL SYMBOL COMBINING GONG */ - { 0x1B80, 0x1B82 }, /* SUNDANESE SIGN PANYECEK - SUNDANESE SIGN PANGWISAD */ - { 0x1BA1, 0x1BAD }, /* SUNDANESE CONSONANT SIGN PAMINGKAL - SUNDANESE CONSONANT SIGN PASANGAN WA */ - { 0x1BE6, 0x1BF3 }, /* BATAK SIGN TOMPI - BATAK PANONGONAN */ - { 0x1C24, 0x1C37 }, /* LEPCHA SUBJOINED LETTER YA - LEPCHA SIGN NUKTA */ - { 0x1CD0, 0x1CD2 }, /* VEDIC TONE KARSHANA - VEDIC TONE PRENKHA */ - { 0x1CD4, 0x1CE8 }, /* VEDIC SIGN YAJURVEDIC MIDLINE SVARITA - VEDIC SIGN VISARGA ANUDATTA WITH TAIL */ - { 0x1CED, 0x1CED }, /* VEDIC SIGN TIRYAK */ - { 0x1CF4, 0x1CF4 }, /* VEDIC TONE CANDRA ABOVE */ - { 0x1CF7, 0x1CF9 }, /* VEDIC SIGN ATIKRAMA - VEDIC TONE DOUBLE RING ABOVE */ - { 0x1DC0, 0x1DFF }, /* COMBINING DOTTED GRAVE ACCENT - COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW */ - { 0x200B, 0x200F }, /* ZERO WIDTH SPACE - RIGHT-TO-LEFT MARK */ - { 0x202A, 0x202E }, /* LEFT-TO-RIGHT EMBEDDING - RIGHT-TO-LEFT OVERRIDE */ - { 0x2060, 0x2064 }, /* WORD JOINER - INVISIBLE PLUS */ - { 0x2066, 0x206F }, /* LEFT-TO-RIGHT ISOLATE - NOMINAL DIGIT SHAPES */ - { 0x20D0, 0x20F0 }, /* COMBINING LEFT HARPOON ABOVE - COMBINING ASTERISK ABOVE */ - { 0x2640, 0x2640 }, /* FEMALE SIGN */ - { 0x2642, 0x2642 }, /* MALE SIGN */ - { 0x26A7, 0x26A7 }, /* MALE WITH STROKE AND MALE AND FEMALE SIGN */ - { 0x2CEF, 0x2CF1 }, /* COPTIC COMBINING NI ABOVE - COPTIC COMBINING SPIRITUS LENIS */ - { 0x2D7F, 0x2D7F }, /* TIFINAGH CONSONANT JOINER */ - { 0x2DE0, 0x2DFF }, /* COMBINING CYRILLIC LETTER BE - COMBINING CYRILLIC LETTER IOTIFIED BIG YUS */ - { 0x302A, 0x302F }, /* IDEOGRAPHIC LEVEL TONE MARK - HANGUL DOUBLE DOT TONE MARK */ - { 0x3099, 0x309A }, /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK - COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ - { 0xA66F, 0xA672 }, /* COMBINING CYRILLIC VZMET - COMBINING CYRILLIC THOUSAND MILLIONS SIGN */ - { 0xA674, 0xA67D }, /* COMBINING CYRILLIC LETTER UKRAINIAN IE - COMBINING CYRILLIC PAYEROK */ - { 0xA69E, 0xA69F }, /* COMBINING CYRILLIC LETTER EF - COMBINING CYRILLIC LETTER IOTIFIED E */ - { 0xA6F0, 0xA6F1 }, /* BAMUM COMBINING MARK KOQNDON - BAMUM COMBINING MARK TUKWENTIS */ - { 0xA802, 0xA802 }, /* SYLOTI NAGRI SIGN DVISVARA */ - { 0xA806, 0xA806 }, /* SYLOTI NAGRI SIGN HASANTA */ - { 0xA80B, 0xA80B }, /* SYLOTI NAGRI SIGN ANUSVARA */ - { 0xA823, 0xA827 }, /* SYLOTI NAGRI VOWEL SIGN A - SYLOTI NAGRI VOWEL SIGN OO */ - { 0xA82C, 0xA82C }, /* SYLOTI NAGRI SIGN ALTERNATE HASANTA */ - { 0xA880, 0xA881 }, /* SAURASHTRA SIGN ANUSVARA - SAURASHTRA SIGN VISARGA */ - { 0xA8B4, 0xA8C5 }, /* SAURASHTRA CONSONANT SIGN HAARU - SAURASHTRA SIGN CANDRABINDU */ - { 0xA8E0, 0xA8F1 }, /* COMBINING DEVANAGARI DIGIT ZERO - COMBINING DEVANAGARI SIGN AVAGRAHA */ - { 0xA8FF, 0xA8FF }, /* DEVANAGARI VOWEL SIGN AY */ - { 0xA926, 0xA92D }, /* KAYAH LI VOWEL UE - KAYAH LI TONE CALYA PLOPHU */ - { 0xA947, 0xA953 }, /* REJANG VOWEL SIGN I - REJANG VIRAMA */ - { 0xA980, 0xA983 }, /* JAVANESE SIGN PANYANGGA - JAVANESE SIGN WIGNYAN */ - { 0xA9B3, 0xA9C0 }, /* JAVANESE SIGN CECAK TELU - JAVANESE PANGKON */ - { 0xA9E5, 0xA9E5 }, /* MYANMAR SIGN SHAN SAW */ - { 0xAA29, 0xAA36 }, /* CHAM VOWEL SIGN AA - CHAM CONSONANT SIGN WA */ - { 0xAA43, 0xAA43 }, /* CHAM CONSONANT SIGN FINAL NG */ - { 0xAA4C, 0xAA4D }, /* CHAM CONSONANT SIGN FINAL M - CHAM CONSONANT SIGN FINAL H */ - { 0xAA7B, 0xAA7D }, /* MYANMAR SIGN PAO KAREN TONE - MYANMAR SIGN TAI LAING TONE-5 */ - { 0xAAB0, 0xAAB0 }, /* TAI VIET MAI KANG */ - { 0xAAB2, 0xAAB4 }, /* TAI VIET VOWEL I - TAI VIET VOWEL U */ - { 0xAAB7, 0xAAB8 }, /* TAI VIET MAI KHIT - TAI VIET VOWEL IA */ - { 0xAABE, 0xAABF }, /* TAI VIET VOWEL AM - TAI VIET TONE MAI EK */ - { 0xAAC1, 0xAAC1 }, /* TAI VIET TONE MAI THO */ - { 0xAAEB, 0xAAEF }, /* MEETEI MAYEK VOWEL SIGN II - MEETEI MAYEK VOWEL SIGN AAU */ - { 0xAAF5, 0xAAF6 }, /* MEETEI MAYEK VOWEL SIGN VISARGA - MEETEI MAYEK VIRAMA */ - { 0xABE3, 0xABEA }, /* MEETEI MAYEK VOWEL SIGN ONAP - MEETEI MAYEK VOWEL SIGN NUNG */ - { 0xABEC, 0xABED }, /* MEETEI MAYEK LUM IYEK - MEETEI MAYEK APUN IYEK */ - { 0xFB1E, 0xFB1E }, /* HEBREW POINT JUDEO-SPANISH VARIKA */ - { 0xFE00, 0xFE0F }, /* VARIATION SELECTOR-1 - VARIATION SELECTOR-16 */ - { 0xFE20, 0xFE2F }, /* COMBINING LIGATURE LEFT HALF - COMBINING CYRILLIC TITLO RIGHT HALF */ - { 0xFEFF, 0xFEFF }, /* ZERO WIDTH NO-BREAK SPACE */ - { 0xFFF9, 0xFFFB }, /* INTERLINEAR ANNOTATION ANCHOR - INTERLINEAR ANNOTATION TERMINATOR */ +/* Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field. */ +#define UCS_NONBMP_BMP_BITS 8 + +/* Combined zero- and double-width ranges + * (BMP - Basic Multilingual Plane, U+0000 to U+FFFF). */ +static const struct ucs_width16 ucs_bmp_ranges[] = { + { BMP_0WIDTH(0x00AD, 0x00AD) }, /* SOFT HYPHEN */ + { BMP_0WIDTH(0x0300, 0x036F) }, /* COMBINING GRAVE ACCENT - COMBINING LATIN SMALL LETTER X */ + { BMP_0WIDTH(0x0483, 0x0489) }, /* COMBINING CYRILLIC TITLO - COMBINING CYRILLIC MILLIONS SIGN */ + { BMP_0WIDTH(0x0591, 0x05BD) }, /* HEBREW ACCENT ETNAHTA - HEBREW POINT METEG */ + { BMP_0WIDTH(0x05BF, 0x05BF) }, /* HEBREW POINT RAFE */ + { BMP_0WIDTH(0x05C1, 0x05C2) }, /* HEBREW POINT SHIN DOT - HEBREW POINT SIN DOT */ + { BMP_0WIDTH(0x05C4, 0x05C5) }, /* HEBREW MARK UPPER DOT - HEBREW MARK LOWER DOT */ + { BMP_0WIDTH(0x05C7, 0x05C7) }, /* HEBREW POINT QAMATS QATAN */ + { BMP_0WIDTH(0x0600, 0x0605) }, /* ARABIC NUMBER SIGN - ARABIC NUMBER MARK ABOVE */ + { BMP_0WIDTH(0x0610, 0x061A) }, /* ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM - ARABIC SMALL KASRA */ + { BMP_0WIDTH(0x061C, 0x061C) }, /* ARABIC LETTER MARK */ + { BMP_0WIDTH(0x064B, 0x065F) }, /* ARABIC FATHATAN - ARABIC WAVY HAMZA BELOW */ + { BMP_0WIDTH(0x0670, 0x0670) }, /* ARABIC LETTER SUPERSCRIPT ALEF */ + { BMP_0WIDTH(0x06D6, 0x06DD) }, /* ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA - ARABIC END OF AYAH */ + { BMP_0WIDTH(0x06DF, 0x06E4) }, /* ARABIC SMALL HIGH ROUNDED ZERO - ARABIC SMALL HIGH MADDA */ + { BMP_0WIDTH(0x06E7, 0x06E8) }, /* ARABIC SMALL HIGH YEH - ARABIC SMALL HIGH NOON */ + { BMP_0WIDTH(0x06EA, 0x06ED) }, /* ARABIC EMPTY CENTRE LOW STOP - ARABIC SMALL LOW MEEM */ + { BMP_0WIDTH(0x070F, 0x070F) }, /* SYRIAC ABBREVIATION MARK */ + { BMP_0WIDTH(0x0711, 0x0711) }, /* SYRIAC LETTER SUPERSCRIPT ALAPH */ + { BMP_0WIDTH(0x0730, 0x074A) }, /* SYRIAC PTHAHA ABOVE - SYRIAC BARREKH */ + { BMP_0WIDTH(0x07A6, 0x07B0) }, /* THAANA ABAFILI - THAANA SUKUN */ + { BMP_0WIDTH(0x07EB, 0x07F3) }, /* NKO COMBINING SHORT HIGH TONE - NKO COMBINING DOUBLE DOT ABOVE */ + { BMP_0WIDTH(0x07FD, 0x07FD) }, /* NKO DANTAYALAN */ + { BMP_0WIDTH(0x0816, 0x0819) }, /* SAMARITAN MARK IN - SAMARITAN MARK DAGESH */ + { BMP_0WIDTH(0x081B, 0x0823) }, /* SAMARITAN MARK EPENTHETIC YUT - SAMARITAN VOWEL SIGN A */ + { BMP_0WIDTH(0x0825, 0x0827) }, /* SAMARITAN VOWEL SIGN SHORT A - SAMARITAN VOWEL SIGN U */ + { BMP_0WIDTH(0x0829, 0x082D) }, /* SAMARITAN VOWEL SIGN LONG I - SAMARITAN MARK NEQUDAA */ + { BMP_0WIDTH(0x0859, 0x085B) }, /* MANDAIC AFFRICATION MARK - MANDAIC GEMINATION MARK */ + { BMP_0WIDTH(0x0890, 0x0891) }, /* ARABIC POUND MARK ABOVE - ARABIC PIASTRE MARK ABOVE */ + { BMP_0WIDTH(0x0897, 0x089F) }, /* ARABIC PEPET - ARABIC HALF MADDA OVER MADDA */ + { BMP_0WIDTH(0x08CA, 0x0903) }, /* ARABIC SMALL HIGH FARSI YEH - DEVANAGARI SIGN VISARGA */ + { BMP_0WIDTH(0x093A, 0x093C) }, /* DEVANAGARI VOWEL SIGN OE - DEVANAGARI SIGN NUKTA */ + { BMP_0WIDTH(0x093E, 0x094F) }, /* DEVANAGARI VOWEL SIGN AA - DEVANAGARI VOWEL SIGN AW */ + { BMP_0WIDTH(0x0951, 0x0957) }, /* DEVANAGARI STRESS SIGN UDATTA - DEVANAGARI VOWEL SIGN UUE */ + { BMP_0WIDTH(0x0962, 0x0963) }, /* DEVANAGARI VOWEL SIGN VOCALIC L - DEVANAGARI VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0981, 0x0983) }, /* BENGALI SIGN CANDRABINDU - BENGALI SIGN VISARGA */ + { BMP_0WIDTH(0x09BC, 0x09BC) }, /* BENGALI SIGN NUKTA */ + { BMP_0WIDTH(0x09BE, 0x09C4) }, /* BENGALI VOWEL SIGN AA - BENGALI VOWEL SIGN VOCALIC RR */ + { BMP_0WIDTH(0x09C7, 0x09C8) }, /* BENGALI VOWEL SIGN E - BENGALI VOWEL SIGN AI */ + { BMP_0WIDTH(0x09CB, 0x09CD) }, /* BENGALI VOWEL SIGN O - BENGALI SIGN VIRAMA */ + { BMP_0WIDTH(0x09D7, 0x09D7) }, /* BENGALI AU LENGTH MARK */ + { BMP_0WIDTH(0x09E2, 0x09E3) }, /* BENGALI VOWEL SIGN VOCALIC L - BENGALI VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x09FE, 0x09FE) }, /* BENGALI SANDHI MARK */ + { BMP_0WIDTH(0x0A01, 0x0A03) }, /* GURMUKHI SIGN ADAK BINDI - GURMUKHI SIGN VISARGA */ + { BMP_0WIDTH(0x0A3C, 0x0A3C) }, /* GURMUKHI SIGN NUKTA */ + { BMP_0WIDTH(0x0A3E, 0x0A42) }, /* GURMUKHI VOWEL SIGN AA - GURMUKHI VOWEL SIGN UU */ + { BMP_0WIDTH(0x0A47, 0x0A48) }, /* GURMUKHI VOWEL SIGN EE - GURMUKHI VOWEL SIGN AI */ + { BMP_0WIDTH(0x0A4B, 0x0A4D) }, /* GURMUKHI VOWEL SIGN OO - GURMUKHI SIGN VIRAMA */ + { BMP_0WIDTH(0x0A51, 0x0A51) }, /* GURMUKHI SIGN UDAAT */ + { BMP_0WIDTH(0x0A70, 0x0A71) }, /* GURMUKHI TIPPI - GURMUKHI ADDAK */ + { BMP_0WIDTH(0x0A75, 0x0A75) }, /* GURMUKHI SIGN YAKASH */ + { BMP_0WIDTH(0x0A81, 0x0A83) }, /* GUJARATI SIGN CANDRABINDU - GUJARATI SIGN VISARGA */ + { BMP_0WIDTH(0x0ABC, 0x0ABC) }, /* GUJARATI SIGN NUKTA */ + { BMP_0WIDTH(0x0ABE, 0x0AC5) }, /* GUJARATI VOWEL SIGN AA - GUJARATI VOWEL SIGN CANDRA E */ + { BMP_0WIDTH(0x0AC7, 0x0AC9) }, /* GUJARATI VOWEL SIGN E - GUJARATI VOWEL SIGN CANDRA O */ + { BMP_0WIDTH(0x0ACB, 0x0ACD) }, /* GUJARATI VOWEL SIGN O - GUJARATI SIGN VIRAMA */ + { BMP_0WIDTH(0x0AE2, 0x0AE3) }, /* GUJARATI VOWEL SIGN VOCALIC L - GUJARATI VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0AFA, 0x0AFF) }, /* GUJARATI SIGN SUKUN - GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE */ + { BMP_0WIDTH(0x0B01, 0x0B03) }, /* ORIYA SIGN CANDRABINDU - ORIYA SIGN VISARGA */ + { BMP_0WIDTH(0x0B3C, 0x0B3C) }, /* ORIYA SIGN NUKTA */ + { BMP_0WIDTH(0x0B3E, 0x0B44) }, /* ORIYA VOWEL SIGN AA - ORIYA VOWEL SIGN VOCALIC RR */ + { BMP_0WIDTH(0x0B47, 0x0B48) }, /* ORIYA VOWEL SIGN E - ORIYA VOWEL SIGN AI */ + { BMP_0WIDTH(0x0B4B, 0x0B4D) }, /* ORIYA VOWEL SIGN O - ORIYA SIGN VIRAMA */ + { BMP_0WIDTH(0x0B55, 0x0B57) }, /* ORIYA SIGN OVERLINE - ORIYA AU LENGTH MARK */ + { BMP_0WIDTH(0x0B62, 0x0B63) }, /* ORIYA VOWEL SIGN VOCALIC L - ORIYA VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0B82, 0x0B82) }, /* TAMIL SIGN ANUSVARA */ + { BMP_0WIDTH(0x0BBE, 0x0BC2) }, /* TAMIL VOWEL SIGN AA - TAMIL VOWEL SIGN UU */ + { BMP_0WIDTH(0x0BC6, 0x0BC8) }, /* TAMIL VOWEL SIGN E - TAMIL VOWEL SIGN AI */ + { BMP_0WIDTH(0x0BCA, 0x0BCD) }, /* TAMIL VOWEL SIGN O - TAMIL SIGN VIRAMA */ + { BMP_0WIDTH(0x0BD7, 0x0BD7) }, /* TAMIL AU LENGTH MARK */ + { BMP_0WIDTH(0x0C00, 0x0C04) }, /* TELUGU SIGN COMBINING CANDRABINDU ABOVE - TELUGU SIGN COMBINING ANUSVARA ABOVE */ + { BMP_0WIDTH(0x0C3C, 0x0C3C) }, /* TELUGU SIGN NUKTA */ + { BMP_0WIDTH(0x0C3E, 0x0C44) }, /* TELUGU VOWEL SIGN AA - TELUGU VOWEL SIGN VOCALIC RR */ + { BMP_0WIDTH(0x0C46, 0x0C48) }, /* TELUGU VOWEL SIGN E - TELUGU VOWEL SIGN AI */ + { BMP_0WIDTH(0x0C4A, 0x0C4D) }, /* TELUGU VOWEL SIGN O - TELUGU SIGN VIRAMA */ + { BMP_0WIDTH(0x0C55, 0x0C56) }, /* TELUGU LENGTH MARK - TELUGU AI LENGTH MARK */ + { BMP_0WIDTH(0x0C62, 0x0C63) }, /* TELUGU VOWEL SIGN VOCALIC L - TELUGU VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0C81, 0x0C83) }, /* KANNADA SIGN CANDRABINDU - KANNADA SIGN VISARGA */ + { BMP_0WIDTH(0x0CBC, 0x0CBC) }, /* KANNADA SIGN NUKTA */ + { BMP_0WIDTH(0x0CBE, 0x0CC4) }, /* KANNADA VOWEL SIGN AA - KANNADA VOWEL SIGN VOCALIC RR */ + { BMP_0WIDTH(0x0CC6, 0x0CC8) }, /* KANNADA VOWEL SIGN E - KANNADA VOWEL SIGN AI */ + { BMP_0WIDTH(0x0CCA, 0x0CCD) }, /* KANNADA VOWEL SIGN O - KANNADA SIGN VIRAMA */ + { BMP_0WIDTH(0x0CD5, 0x0CD6) }, /* KANNADA LENGTH MARK - KANNADA AI LENGTH MARK */ + { BMP_0WIDTH(0x0CE2, 0x0CE3) }, /* KANNADA VOWEL SIGN VOCALIC L - KANNADA VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0CF3, 0x0CF3) }, /* KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT */ + { BMP_0WIDTH(0x0D00, 0x0D03) }, /* MALAYALAM SIGN COMBINING ANUSVARA ABOVE - MALAYALAM SIGN VISARGA */ + { BMP_0WIDTH(0x0D3B, 0x0D3C) }, /* MALAYALAM SIGN VERTICAL BAR VIRAMA - MALAYALAM SIGN CIRCULAR VIRAMA */ + { BMP_0WIDTH(0x0D3E, 0x0D44) }, /* MALAYALAM VOWEL SIGN AA - MALAYALAM VOWEL SIGN VOCALIC RR */ + { BMP_0WIDTH(0x0D46, 0x0D48) }, /* MALAYALAM VOWEL SIGN E - MALAYALAM VOWEL SIGN AI */ + { BMP_0WIDTH(0x0D4A, 0x0D4D) }, /* MALAYALAM VOWEL SIGN O - MALAYALAM SIGN VIRAMA */ + { BMP_0WIDTH(0x0D57, 0x0D57) }, /* MALAYALAM AU LENGTH MARK */ + { BMP_0WIDTH(0x0D62, 0x0D63) }, /* MALAYALAM VOWEL SIGN VOCALIC L - MALAYALAM VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x0D81, 0x0D83) }, /* SINHALA SIGN CANDRABINDU - SINHALA SIGN VISARGAYA */ + { BMP_0WIDTH(0x0DCA, 0x0DCA) }, /* SINHALA SIGN AL-LAKUNA */ + { BMP_0WIDTH(0x0DCF, 0x0DD4) }, /* SINHALA VOWEL SIGN AELA-PILLA - SINHALA VOWEL SIGN KETTI PAA-PILLA */ + { BMP_0WIDTH(0x0DD6, 0x0DD6) }, /* SINHALA VOWEL SIGN DIGA PAA-PILLA */ + { BMP_0WIDTH(0x0DD8, 0x0DDF) }, /* SINHALA VOWEL SIGN GAETTA-PILLA - SINHALA VOWEL SIGN GAYANUKITTA */ + { BMP_0WIDTH(0x0DF2, 0x0DF3) }, /* SINHALA VOWEL SIGN DIGA GAETTA-PILLA - SINHALA VOWEL SIGN DIGA GAYANUKITTA */ + { BMP_0WIDTH(0x0E31, 0x0E31) }, /* THAI CHARACTER MAI HAN-AKAT */ + { BMP_0WIDTH(0x0E34, 0x0E3A) }, /* THAI CHARACTER SARA I - THAI CHARACTER PHINTHU */ + { BMP_0WIDTH(0x0E47, 0x0E4E) }, /* THAI CHARACTER MAITAIKHU - THAI CHARACTER YAMAKKAN */ + { BMP_0WIDTH(0x0EB1, 0x0EB1) }, /* LAO VOWEL SIGN MAI KAN */ + { BMP_0WIDTH(0x0EB4, 0x0EBC) }, /* LAO VOWEL SIGN I - LAO SEMIVOWEL SIGN LO */ + { BMP_0WIDTH(0x0EC8, 0x0ECE) }, /* LAO TONE MAI EK - LAO YAMAKKAN */ + { BMP_0WIDTH(0x0F18, 0x0F19) }, /* TIBETAN ASTROLOGICAL SIGN -KHYUD PA - TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS */ + { BMP_0WIDTH(0x0F35, 0x0F35) }, /* TIBETAN MARK NGAS BZUNG NYI ZLA */ + { BMP_0WIDTH(0x0F37, 0x0F37) }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS */ + { BMP_0WIDTH(0x0F39, 0x0F39) }, /* TIBETAN MARK TSA -PHRU */ + { BMP_0WIDTH(0x0F3E, 0x0F3F) }, /* TIBETAN SIGN YAR TSHES - TIBETAN SIGN MAR TSHES */ + { BMP_0WIDTH(0x0F71, 0x0F84) }, /* TIBETAN VOWEL SIGN AA - TIBETAN MARK HALANTA */ + { BMP_0WIDTH(0x0F86, 0x0F87) }, /* TIBETAN SIGN LCI RTAGS - TIBETAN SIGN YANG RTAGS */ + { BMP_0WIDTH(0x0F8D, 0x0F97) }, /* TIBETAN SUBJOINED SIGN LCE TSA CAN - TIBETAN SUBJOINED LETTER JA */ + { BMP_0WIDTH(0x0F99, 0x0FBC) }, /* TIBETAN SUBJOINED LETTER NYA - TIBETAN SUBJOINED LETTER FIXED-FORM RA */ + { BMP_0WIDTH(0x0FC6, 0x0FC6) }, /* TIBETAN SYMBOL PADMA GDAN */ + { BMP_0WIDTH(0x102B, 0x103E) }, /* MYANMAR VOWEL SIGN TALL AA - MYANMAR CONSONANT SIGN MEDIAL HA */ + { BMP_0WIDTH(0x1056, 0x1059) }, /* MYANMAR VOWEL SIGN VOCALIC R - MYANMAR VOWEL SIGN VOCALIC LL */ + { BMP_0WIDTH(0x105E, 0x1060) }, /* MYANMAR CONSONANT SIGN MON MEDIAL NA - MYANMAR CONSONANT SIGN MON MEDIAL LA */ + { BMP_0WIDTH(0x1062, 0x1064) }, /* MYANMAR VOWEL SIGN SGAW KAREN EU - MYANMAR TONE MARK SGAW KAREN KE PHO */ + { BMP_0WIDTH(0x1067, 0x106D) }, /* MYANMAR VOWEL SIGN WESTERN PWO KAREN EU - MYANMAR SIGN WESTERN PWO KAREN TONE-5 */ + { BMP_0WIDTH(0x1071, 0x1074) }, /* MYANMAR VOWEL SIGN GEBA KAREN I - MYANMAR VOWEL SIGN KAYAH EE */ + { BMP_0WIDTH(0x1082, 0x108D) }, /* MYANMAR CONSONANT SIGN SHAN MEDIAL WA - MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */ + { BMP_0WIDTH(0x108F, 0x108F) }, /* MYANMAR SIGN RUMAI PALAUNG TONE-5 */ + { BMP_0WIDTH(0x109A, 0x109D) }, /* MYANMAR SIGN KHAMTI TONE-1 - MYANMAR VOWEL SIGN AITON AI */ + { BMP_2WIDTH(0x1100, 0x115F) }, /* HANGUL CHOSEONG KIYEOK - HANGUL CHOSEONG FILLER */ + { BMP_0WIDTH(0x135D, 0x135F) }, /* ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK - ETHIOPIC COMBINING GEMINATION MARK */ + { BMP_0WIDTH(0x1712, 0x1715) }, /* TAGALOG VOWEL SIGN I - TAGALOG SIGN PAMUDPOD */ + { BMP_0WIDTH(0x1732, 0x1734) }, /* HANUNOO VOWEL SIGN I - HANUNOO SIGN PAMUDPOD */ + { BMP_0WIDTH(0x1752, 0x1753) }, /* BUHID VOWEL SIGN I - BUHID VOWEL SIGN U */ + { BMP_0WIDTH(0x1772, 0x1773) }, /* TAGBANWA VOWEL SIGN I - TAGBANWA VOWEL SIGN U */ + { BMP_0WIDTH(0x17B4, 0x17D3) }, /* KHMER VOWEL INHERENT AQ - KHMER SIGN BATHAMASAT */ + { BMP_0WIDTH(0x17DD, 0x17DD) }, /* KHMER SIGN ATTHACAN */ + { BMP_0WIDTH(0x180B, 0x180F) }, /* MONGOLIAN FREE VARIATION SELECTOR ONE - MONGOLIAN FREE VARIATION SELECTOR FOUR */ + { BMP_0WIDTH(0x1885, 0x1886) }, /* MONGOLIAN LETTER ALI GALI BALUDA - MONGOLIAN LETTER ALI GALI THREE BALUDA */ + { BMP_0WIDTH(0x18A9, 0x18A9) }, /* MONGOLIAN LETTER ALI GALI DAGALGA */ + { BMP_0WIDTH(0x1920, 0x192B) }, /* LIMBU VOWEL SIGN A - LIMBU SUBJOINED LETTER WA */ + { BMP_0WIDTH(0x1930, 0x193B) }, /* LIMBU SMALL LETTER KA - LIMBU SIGN SA-I */ + { BMP_0WIDTH(0x1A17, 0x1A1B) }, /* BUGINESE VOWEL SIGN I - BUGINESE VOWEL SIGN AE */ + { BMP_0WIDTH(0x1A55, 0x1A5E) }, /* TAI THAM CONSONANT SIGN MEDIAL RA - TAI THAM CONSONANT SIGN SA */ + { BMP_0WIDTH(0x1A60, 0x1A7C) }, /* TAI THAM SIGN SAKOT - TAI THAM SIGN KHUEN-LUE KARAN */ + { BMP_0WIDTH(0x1A7F, 0x1A7F) }, /* TAI THAM COMBINING CRYPTOGRAMMIC DOT */ + { BMP_0WIDTH(0x1AB0, 0x1ACE) }, /* COMBINING DOUBLED CIRCUMFLEX ACCENT - COMBINING LATIN SMALL LETTER INSULAR T */ + { BMP_0WIDTH(0x1B00, 0x1B04) }, /* BALINESE SIGN ULU RICEM - BALINESE SIGN BISAH */ + { BMP_0WIDTH(0x1B34, 0x1B44) }, /* BALINESE SIGN REREKAN - BALINESE ADEG ADEG */ + { BMP_0WIDTH(0x1B6B, 0x1B73) }, /* BALINESE MUSICAL SYMBOL COMBINING TEGEH - BALINESE MUSICAL SYMBOL COMBINING GONG */ + { BMP_0WIDTH(0x1B80, 0x1B82) }, /* SUNDANESE SIGN PANYECEK - SUNDANESE SIGN PANGWISAD */ + { BMP_0WIDTH(0x1BA1, 0x1BAD) }, /* SUNDANESE CONSONANT SIGN PAMINGKAL - SUNDANESE CONSONANT SIGN PASANGAN WA */ + { BMP_0WIDTH(0x1BE6, 0x1BF3) }, /* BATAK SIGN TOMPI - BATAK PANONGONAN */ + { BMP_0WIDTH(0x1C24, 0x1C37) }, /* LEPCHA SUBJOINED LETTER YA - LEPCHA SIGN NUKTA */ + { BMP_0WIDTH(0x1CD0, 0x1CD2) }, /* VEDIC TONE KARSHANA - VEDIC TONE PRENKHA */ + { BMP_0WIDTH(0x1CD4, 0x1CE8) }, /* VEDIC SIGN YAJURVEDIC MIDLINE SVARITA - VEDIC SIGN VISARGA ANUDATTA WITH TAIL */ + { BMP_0WIDTH(0x1CED, 0x1CED) }, /* VEDIC SIGN TIRYAK */ + { BMP_0WIDTH(0x1CF4, 0x1CF4) }, /* VEDIC TONE CANDRA ABOVE */ + { BMP_0WIDTH(0x1CF7, 0x1CF9) }, /* VEDIC SIGN ATIKRAMA - VEDIC TONE DOUBLE RING ABOVE */ + { BMP_0WIDTH(0x1DC0, 0x1DFF) }, /* COMBINING DOTTED GRAVE ACCENT - COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW */ + { BMP_0WIDTH(0x200B, 0x200F) }, /* ZERO WIDTH SPACE - RIGHT-TO-LEFT MARK */ + { BMP_0WIDTH(0x202A, 0x202E) }, /* LEFT-TO-RIGHT EMBEDDING - RIGHT-TO-LEFT OVERRIDE */ + { BMP_0WIDTH(0x2060, 0x2064) }, /* WORD JOINER - INVISIBLE PLUS */ + { BMP_0WIDTH(0x2066, 0x206F) }, /* LEFT-TO-RIGHT ISOLATE - NOMINAL DIGIT SHAPES */ + { BMP_0WIDTH(0x20D0, 0x20F0) }, /* COMBINING LEFT HARPOON ABOVE - COMBINING ASTERISK ABOVE */ + { BMP_2WIDTH(0x231A, 0x231B) }, /* WATCH - HOURGLASS */ + { BMP_2WIDTH(0x2329, 0x232A) }, /* LEFT-POINTING ANGLE BRACKET - RIGHT-POINTING ANGLE BRACKET */ + { BMP_2WIDTH(0x23E9, 0x23EC) }, /* BLACK RIGHT-POINTING DOUBLE TRIANGLE - BLACK DOWN-POINTING DOUBLE TRIANGLE */ + { BMP_2WIDTH(0x23F0, 0x23F0) }, /* ALARM CLOCK */ + { BMP_2WIDTH(0x23F3, 0x23F3) }, /* HOURGLASS WITH FLOWING SAND */ + { BMP_2WIDTH(0x25FD, 0x25FE) }, /* WHITE MEDIUM SMALL SQUARE - BLACK MEDIUM SMALL SQUARE */ + { BMP_2WIDTH(0x2614, 0x2615) }, /* UMBRELLA WITH RAIN DROPS - HOT BEVERAGE */ + { BMP_2WIDTH(0x2630, 0x2637) }, /* TRIGRAM FOR HEAVEN - TRIGRAM FOR EARTH */ + { BMP_0WIDTH(0x2640, 0x2640) }, /* FEMALE SIGN */ + { BMP_0WIDTH(0x2642, 0x2642) }, /* MALE SIGN */ + { BMP_2WIDTH(0x2648, 0x2653) }, /* ARIES - PISCES */ + { BMP_2WIDTH(0x267F, 0x267F) }, /* WHEELCHAIR SYMBOL */ + { BMP_2WIDTH(0x268A, 0x268F) }, /* MONOGRAM FOR YANG - DIGRAM FOR GREATER YIN */ + { BMP_2WIDTH(0x2693, 0x2693) }, /* ANCHOR */ + { BMP_2WIDTH(0x26A1, 0x26A1) }, /* HIGH VOLTAGE SIGN */ + { BMP_0WIDTH(0x26A7, 0x26A7) }, /* MALE WITH STROKE AND MALE AND FEMALE SIGN */ + { BMP_2WIDTH(0x26AA, 0x26AB) }, /* MEDIUM WHITE CIRCLE - MEDIUM BLACK CIRCLE */ + { BMP_2WIDTH(0x26BD, 0x26BE) }, /* SOCCER BALL - BASEBALL */ + { BMP_2WIDTH(0x26C4, 0x26C5) }, /* SNOWMAN WITHOUT SNOW - SUN BEHIND CLOUD */ + { BMP_2WIDTH(0x26CE, 0x26CE) }, /* OPHIUCHUS */ + { BMP_2WIDTH(0x26D4, 0x26D4) }, /* NO ENTRY */ + { BMP_2WIDTH(0x26EA, 0x26EA) }, /* CHURCH */ + { BMP_2WIDTH(0x26F2, 0x26F3) }, /* FOUNTAIN - FLAG IN HOLE */ + { BMP_2WIDTH(0x26F5, 0x26F5) }, /* SAILBOAT */ + { BMP_2WIDTH(0x26FA, 0x26FA) }, /* TENT */ + { BMP_2WIDTH(0x26FD, 0x26FD) }, /* FUEL PUMP */ + { BMP_2WIDTH(0x2705, 0x2705) }, /* WHITE HEAVY CHECK MARK */ + { BMP_2WIDTH(0x270A, 0x270B) }, /* RAISED FIST - RAISED HAND */ + { BMP_2WIDTH(0x2728, 0x2728) }, /* SPARKLES */ + { BMP_2WIDTH(0x274C, 0x274C) }, /* CROSS MARK */ + { BMP_2WIDTH(0x274E, 0x274E) }, /* NEGATIVE SQUARED CROSS MARK */ + { BMP_2WIDTH(0x2753, 0x2755) }, /* BLACK QUESTION MARK ORNAMENT - WHITE EXCLAMATION MARK ORNAMENT */ + { BMP_2WIDTH(0x2757, 0x2757) }, /* HEAVY EXCLAMATION MARK SYMBOL */ + { BMP_2WIDTH(0x2795, 0x2797) }, /* HEAVY PLUS SIGN - HEAVY DIVISION SIGN */ + { BMP_2WIDTH(0x27B0, 0x27B0) }, /* CURLY LOOP */ + { BMP_2WIDTH(0x27BF, 0x27BF) }, /* DOUBLE CURLY LOOP */ + { BMP_2WIDTH(0x2B1B, 0x2B1C) }, /* BLACK LARGE SQUARE - WHITE LARGE SQUARE */ + { BMP_2WIDTH(0x2B50, 0x2B50) }, /* WHITE MEDIUM STAR */ + { BMP_2WIDTH(0x2B55, 0x2B55) }, /* HEAVY LARGE CIRCLE */ + { BMP_0WIDTH(0x2CEF, 0x2CF1) }, /* COPTIC COMBINING NI ABOVE - COPTIC COMBINING SPIRITUS LENIS */ + { BMP_0WIDTH(0x2D7F, 0x2D7F) }, /* TIFINAGH CONSONANT JOINER */ + { BMP_0WIDTH(0x2DE0, 0x2DFF) }, /* COMBINING CYRILLIC LETTER BE - COMBINING CYRILLIC LETTER IOTIFIED BIG YUS */ + { BMP_2WIDTH(0x2E80, 0x2E99) }, /* CJK RADICAL REPEAT - CJK RADICAL RAP */ + { BMP_2WIDTH(0x2E9B, 0x2EF3) }, /* CJK RADICAL CHOKE - CJK RADICAL C-SIMPLIFIED TURTLE */ + { BMP_2WIDTH(0x2F00, 0x2FD5) }, /* KANGXI RADICAL ONE - KANGXI RADICAL FLUTE */ + { BMP_2WIDTH(0x2FF0, 0x3029) }, /* IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT - HANGZHOU NUMERAL NINE */ + { BMP_0WIDTH(0x302A, 0x302F) }, /* IDEOGRAPHIC LEVEL TONE MARK - HANGUL DOUBLE DOT TONE MARK */ + { BMP_2WIDTH(0x3030, 0x303E) }, /* WAVY DASH - IDEOGRAPHIC VARIATION INDICATOR */ + { BMP_2WIDTH(0x3041, 0x3096) }, /* HIRAGANA LETTER SMALL A - HIRAGANA LETTER SMALL KE */ + { BMP_0WIDTH(0x3099, 0x309A) }, /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK - COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { BMP_2WIDTH(0x309B, 0x30FF) }, /* KATAKANA-HIRAGANA VOICED SOUND MARK - KATAKANA DIGRAPH KOTO */ + { BMP_2WIDTH(0x3105, 0x312F) }, /* BOPOMOFO LETTER B - BOPOMOFO LETTER NN */ + { BMP_2WIDTH(0x3131, 0x318E) }, /* HANGUL LETTER KIYEOK - HANGUL LETTER ARAEAE */ + { BMP_2WIDTH(0x3190, 0x31E5) }, /* IDEOGRAPHIC ANNOTATION LINKING MARK - CJK STROKE SZP */ + { BMP_2WIDTH(0x31EF, 0x321E) }, /* IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION - PARENTHESIZED KOREAN CHARACTER O HU */ + { BMP_2WIDTH(0x3220, 0x3247) }, /* PARENTHESIZED IDEOGRAPH ONE - CIRCLED IDEOGRAPH KOTO */ + { BMP_2WIDTH(0x3250, 0xA48C) }, /* PARTNERSHIP SIGN - YI SYLLABLE YYR */ + { BMP_2WIDTH(0xA490, 0xA4C6) }, /* YI RADICAL QOT - YI RADICAL KE */ + { BMP_0WIDTH(0xA66F, 0xA672) }, /* COMBINING CYRILLIC VZMET - COMBINING CYRILLIC THOUSAND MILLIONS SIGN */ + { BMP_0WIDTH(0xA674, 0xA67D) }, /* COMBINING CYRILLIC LETTER UKRAINIAN IE - COMBINING CYRILLIC PAYEROK */ + { BMP_0WIDTH(0xA69E, 0xA69F) }, /* COMBINING CYRILLIC LETTER EF - COMBINING CYRILLIC LETTER IOTIFIED E */ + { BMP_0WIDTH(0xA6F0, 0xA6F1) }, /* BAMUM COMBINING MARK KOQNDON - BAMUM COMBINING MARK TUKWENTIS */ + { BMP_0WIDTH(0xA802, 0xA802) }, /* SYLOTI NAGRI SIGN DVISVARA */ + { BMP_0WIDTH(0xA806, 0xA806) }, /* SYLOTI NAGRI SIGN HASANTA */ + { BMP_0WIDTH(0xA80B, 0xA80B) }, /* SYLOTI NAGRI SIGN ANUSVARA */ + { BMP_0WIDTH(0xA823, 0xA827) }, /* SYLOTI NAGRI VOWEL SIGN A - SYLOTI NAGRI VOWEL SIGN OO */ + { BMP_0WIDTH(0xA82C, 0xA82C) }, /* SYLOTI NAGRI SIGN ALTERNATE HASANTA */ + { BMP_0WIDTH(0xA880, 0xA881) }, /* SAURASHTRA SIGN ANUSVARA - SAURASHTRA SIGN VISARGA */ + { BMP_0WIDTH(0xA8B4, 0xA8C5) }, /* SAURASHTRA CONSONANT SIGN HAARU - SAURASHTRA SIGN CANDRABINDU */ + { BMP_0WIDTH(0xA8E0, 0xA8F1) }, /* COMBINING DEVANAGARI DIGIT ZERO - COMBINING DEVANAGARI SIGN AVAGRAHA */ + { BMP_0WIDTH(0xA8FF, 0xA8FF) }, /* DEVANAGARI VOWEL SIGN AY */ + { BMP_0WIDTH(0xA926, 0xA92D) }, /* KAYAH LI VOWEL UE - KAYAH LI TONE CALYA PLOPHU */ + { BMP_0WIDTH(0xA947, 0xA953) }, /* REJANG VOWEL SIGN I - REJANG VIRAMA */ + { BMP_2WIDTH(0xA960, 0xA97C) }, /* HANGUL CHOSEONG TIKEUT-MIEUM - HANGUL CHOSEONG SSANGYEORINHIEUH */ + { BMP_0WIDTH(0xA980, 0xA983) }, /* JAVANESE SIGN PANYANGGA - JAVANESE SIGN WIGNYAN */ + { BMP_0WIDTH(0xA9B3, 0xA9C0) }, /* JAVANESE SIGN CECAK TELU - JAVANESE PANGKON */ + { BMP_0WIDTH(0xA9E5, 0xA9E5) }, /* MYANMAR SIGN SHAN SAW */ + { BMP_0WIDTH(0xAA29, 0xAA36) }, /* CHAM VOWEL SIGN AA - CHAM CONSONANT SIGN WA */ + { BMP_0WIDTH(0xAA43, 0xAA43) }, /* CHAM CONSONANT SIGN FINAL NG */ + { BMP_0WIDTH(0xAA4C, 0xAA4D) }, /* CHAM CONSONANT SIGN FINAL M - CHAM CONSONANT SIGN FINAL H */ + { BMP_0WIDTH(0xAA7B, 0xAA7D) }, /* MYANMAR SIGN PAO KAREN TONE - MYANMAR SIGN TAI LAING TONE-5 */ + { BMP_0WIDTH(0xAAB0, 0xAAB0) }, /* TAI VIET MAI KANG */ + { BMP_0WIDTH(0xAAB2, 0xAAB4) }, /* TAI VIET VOWEL I - TAI VIET VOWEL U */ + { BMP_0WIDTH(0xAAB7, 0xAAB8) }, /* TAI VIET MAI KHIT - TAI VIET VOWEL IA */ + { BMP_0WIDTH(0xAABE, 0xAABF) }, /* TAI VIET VOWEL AM - TAI VIET TONE MAI EK */ + { BMP_0WIDTH(0xAAC1, 0xAAC1) }, /* TAI VIET TONE MAI THO */ + { BMP_0WIDTH(0xAAEB, 0xAAEF) }, /* MEETEI MAYEK VOWEL SIGN II - MEETEI MAYEK VOWEL SIGN AAU */ + { BMP_0WIDTH(0xAAF5, 0xAAF6) }, /* MEETEI MAYEK VOWEL SIGN VISARGA - MEETEI MAYEK VIRAMA */ + { BMP_0WIDTH(0xABE3, 0xABEA) }, /* MEETEI MAYEK VOWEL SIGN ONAP - MEETEI MAYEK VOWEL SIGN NUNG */ + { BMP_0WIDTH(0xABEC, 0xABED) }, /* MEETEI MAYEK LUM IYEK - MEETEI MAYEK APUN IYEK */ + { BMP_2WIDTH(0xAC00, 0xD7A3) }, /* HANGUL SYLLABLE GA - HANGUL SYLLABLE HIH */ + { BMP_2WIDTH(0xF900, 0xFAFF) }, /* U+F900 - U+FAFF */ + { BMP_0WIDTH(0xFB1E, 0xFB1E) }, /* HEBREW POINT JUDEO-SPANISH VARIKA */ + { BMP_0WIDTH(0xFE00, 0xFE0F) }, /* VARIATION SELECTOR-1 - VARIATION SELECTOR-16 */ + { BMP_2WIDTH(0xFE10, 0xFE19) }, /* PRESENTATION FORM FOR VERTICAL COMMA - PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS */ + { BMP_0WIDTH(0xFE20, 0xFE2F) }, /* COMBINING LIGATURE LEFT HALF - COMBINING CYRILLIC TITLO RIGHT HALF */ + { BMP_2WIDTH(0xFE30, 0xFE52) }, /* PRESENTATION FORM FOR VERTICAL TWO DOT LEADER - SMALL FULL STOP */ + { BMP_2WIDTH(0xFE54, 0xFE66) }, /* SMALL SEMICOLON - SMALL EQUALS SIGN */ + { BMP_2WIDTH(0xFE68, 0xFE6B) }, /* SMALL REVERSE SOLIDUS - SMALL COMMERCIAL AT */ + { BMP_0WIDTH(0xFEFF, 0xFEFF) }, /* ZERO WIDTH NO-BREAK SPACE */ + { BMP_2WIDTH(0xFF01, 0xFF60) }, /* FULLWIDTH EXCLAMATION MARK - FULLWIDTH RIGHT WHITE PARENTHESIS */ + { BMP_2WIDTH(0xFFE0, 0xFFE6) }, /* FULLWIDTH CENT SIGN - FULLWIDTH WON SIGN */ + { BMP_0WIDTH(0xFFF9, 0xFFFB) }, /* INTERLINEAR ANNOTATION ANCHOR - INTERLINEAR ANNOTATION TERMINATOR */ }; -/* Zero-width character ranges (non-BMP, U+10000 and above) */ -static const struct ucs_interval32 ucs_zero_width_non_bmp_ranges[] = { - { 0x101FD, 0x101FD }, /* PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE */ - { 0x102E0, 0x102E0 }, /* COPTIC EPACT THOUSANDS MARK */ - { 0x10376, 0x1037A }, /* COMBINING OLD PERMIC LETTER AN - COMBINING OLD PERMIC LETTER SII */ - { 0x10A01, 0x10A03 }, /* KHAROSHTHI VOWEL SIGN I - KHAROSHTHI VOWEL SIGN VOCALIC R */ - { 0x10A05, 0x10A06 }, /* KHAROSHTHI VOWEL SIGN E - KHAROSHTHI VOWEL SIGN O */ - { 0x10A0C, 0x10A0F }, /* KHAROSHTHI VOWEL LENGTH MARK - KHAROSHTHI SIGN VISARGA */ - { 0x10A38, 0x10A3A }, /* KHAROSHTHI SIGN BAR ABOVE - KHAROSHTHI SIGN DOT BELOW */ - { 0x10A3F, 0x10A3F }, /* KHAROSHTHI VIRAMA */ - { 0x10AE5, 0x10AE6 }, /* MANICHAEAN ABBREVIATION MARK ABOVE - MANICHAEAN ABBREVIATION MARK BELOW */ - { 0x10D24, 0x10D27 }, /* HANIFI ROHINGYA SIGN HARBAHAY - HANIFI ROHINGYA SIGN TASSI */ - { 0x10D69, 0x10D6D }, /* GARAY VOWEL SIGN E - GARAY CONSONANT NASALIZATION MARK */ - { 0x10EAB, 0x10EAC }, /* YEZIDI COMBINING HAMZA MARK - YEZIDI COMBINING MADDA MARK */ - { 0x10EFC, 0x10EFF }, /* ARABIC COMBINING ALEF OVERLAY - ARABIC SMALL LOW WORD MADDA */ - { 0x10F46, 0x10F50 }, /* SOGDIAN COMBINING DOT BELOW - SOGDIAN COMBINING STROKE BELOW */ - { 0x10F82, 0x10F85 }, /* OLD UYGHUR COMBINING DOT ABOVE - OLD UYGHUR COMBINING TWO DOTS BELOW */ - { 0x11000, 0x11002 }, /* BRAHMI SIGN CANDRABINDU - BRAHMI SIGN VISARGA */ - { 0x11038, 0x11046 }, /* BRAHMI VOWEL SIGN AA - BRAHMI VIRAMA */ - { 0x11070, 0x11070 }, /* BRAHMI SIGN OLD TAMIL VIRAMA */ - { 0x11073, 0x11074 }, /* BRAHMI VOWEL SIGN OLD TAMIL SHORT E - BRAHMI VOWEL SIGN OLD TAMIL SHORT O */ - { 0x1107F, 0x11082 }, /* BRAHMI NUMBER JOINER - KAITHI SIGN VISARGA */ - { 0x110B0, 0x110BA }, /* KAITHI VOWEL SIGN AA - KAITHI SIGN NUKTA */ - { 0x110BD, 0x110BD }, /* KAITHI NUMBER SIGN */ - { 0x110C2, 0x110C2 }, /* KAITHI VOWEL SIGN VOCALIC R */ - { 0x110CD, 0x110CD }, /* KAITHI NUMBER SIGN ABOVE */ - { 0x11100, 0x11102 }, /* CHAKMA SIGN CANDRABINDU - CHAKMA SIGN VISARGA */ - { 0x11127, 0x11134 }, /* CHAKMA VOWEL SIGN A - CHAKMA MAAYYAA */ - { 0x11145, 0x11146 }, /* CHAKMA VOWEL SIGN AA - CHAKMA VOWEL SIGN EI */ - { 0x11173, 0x11173 }, /* MAHAJANI SIGN NUKTA */ - { 0x11180, 0x11182 }, /* SHARADA SIGN CANDRABINDU - SHARADA SIGN VISARGA */ - { 0x111B3, 0x111C0 }, /* SHARADA VOWEL SIGN AA - SHARADA SIGN VIRAMA */ - { 0x111C9, 0x111CC }, /* SHARADA SANDHI MARK - SHARADA EXTRA SHORT VOWEL MARK */ - { 0x111CE, 0x111CF }, /* SHARADA VOWEL SIGN PRISHTHAMATRA E - SHARADA SIGN INVERTED CANDRABINDU */ - { 0x1122C, 0x11237 }, /* KHOJKI VOWEL SIGN AA - KHOJKI SIGN SHADDA */ - { 0x1123E, 0x1123E }, /* KHOJKI SIGN SUKUN */ - { 0x11241, 0x11241 }, /* KHOJKI VOWEL SIGN VOCALIC R */ - { 0x112DF, 0x112EA }, /* KHUDAWADI SIGN ANUSVARA - KHUDAWADI SIGN VIRAMA */ - { 0x11300, 0x11303 }, /* GRANTHA SIGN COMBINING ANUSVARA ABOVE - GRANTHA SIGN VISARGA */ - { 0x1133B, 0x1133C }, /* COMBINING BINDU BELOW - GRANTHA SIGN NUKTA */ - { 0x1133E, 0x11344 }, /* GRANTHA VOWEL SIGN AA - GRANTHA VOWEL SIGN VOCALIC RR */ - { 0x11347, 0x11348 }, /* GRANTHA VOWEL SIGN EE - GRANTHA VOWEL SIGN AI */ - { 0x1134B, 0x1134D }, /* GRANTHA VOWEL SIGN OO - GRANTHA SIGN VIRAMA */ - { 0x11357, 0x11357 }, /* GRANTHA AU LENGTH MARK */ - { 0x11362, 0x11363 }, /* GRANTHA VOWEL SIGN VOCALIC L - GRANTHA VOWEL SIGN VOCALIC LL */ - { 0x11366, 0x1136C }, /* COMBINING GRANTHA DIGIT ZERO - COMBINING GRANTHA DIGIT SIX */ - { 0x11370, 0x11374 }, /* COMBINING GRANTHA LETTER A - COMBINING GRANTHA LETTER PA */ - { 0x113B8, 0x113C0 }, /* TULU-TIGALARI VOWEL SIGN AA - TULU-TIGALARI VOWEL SIGN VOCALIC LL */ - { 0x113C2, 0x113C2 }, /* TULU-TIGALARI VOWEL SIGN EE */ - { 0x113C5, 0x113C5 }, /* TULU-TIGALARI VOWEL SIGN AI */ - { 0x113C7, 0x113CA }, /* TULU-TIGALARI VOWEL SIGN OO - TULU-TIGALARI SIGN CANDRA ANUNASIKA */ - { 0x113CC, 0x113D0 }, /* TULU-TIGALARI SIGN ANUSVARA - TULU-TIGALARI CONJOINER */ - { 0x113D2, 0x113D2 }, /* TULU-TIGALARI GEMINATION MARK */ - { 0x113E1, 0x113E2 }, /* TULU-TIGALARI VEDIC TONE SVARITA - TULU-TIGALARI VEDIC TONE ANUDATTA */ - { 0x11435, 0x11446 }, /* NEWA VOWEL SIGN AA - NEWA SIGN NUKTA */ - { 0x1145E, 0x1145E }, /* NEWA SANDHI MARK */ - { 0x114B0, 0x114C3 }, /* TIRHUTA VOWEL SIGN AA - TIRHUTA SIGN NUKTA */ - { 0x115AF, 0x115B5 }, /* SIDDHAM VOWEL SIGN AA - SIDDHAM VOWEL SIGN VOCALIC RR */ - { 0x115B8, 0x115C0 }, /* SIDDHAM VOWEL SIGN E - SIDDHAM SIGN NUKTA */ - { 0x115DC, 0x115DD }, /* SIDDHAM VOWEL SIGN ALTERNATE U - SIDDHAM VOWEL SIGN ALTERNATE UU */ - { 0x11630, 0x11640 }, /* MODI VOWEL SIGN AA - MODI SIGN ARDHACANDRA */ - { 0x116AB, 0x116B7 }, /* TAKRI SIGN ANUSVARA - TAKRI SIGN NUKTA */ - { 0x1171D, 0x1172B }, /* AHOM CONSONANT SIGN MEDIAL LA - AHOM SIGN KILLER */ - { 0x1182C, 0x1183A }, /* DOGRA VOWEL SIGN AA - DOGRA SIGN NUKTA */ - { 0x11930, 0x11935 }, /* DIVES AKURU VOWEL SIGN AA - DIVES AKURU VOWEL SIGN E */ - { 0x11937, 0x11938 }, /* DIVES AKURU VOWEL SIGN AI - DIVES AKURU VOWEL SIGN O */ - { 0x1193B, 0x1193E }, /* DIVES AKURU SIGN ANUSVARA - DIVES AKURU VIRAMA */ - { 0x11940, 0x11940 }, /* DIVES AKURU MEDIAL YA */ - { 0x11942, 0x11943 }, /* DIVES AKURU MEDIAL RA - DIVES AKURU SIGN NUKTA */ - { 0x119D1, 0x119D7 }, /* NANDINAGARI VOWEL SIGN AA - NANDINAGARI VOWEL SIGN VOCALIC RR */ - { 0x119DA, 0x119E0 }, /* NANDINAGARI VOWEL SIGN E - NANDINAGARI SIGN VIRAMA */ - { 0x119E4, 0x119E4 }, /* NANDINAGARI VOWEL SIGN PRISHTHAMATRA E */ - { 0x11A01, 0x11A0A }, /* ZANABAZAR SQUARE VOWEL SIGN I - ZANABAZAR SQUARE VOWEL LENGTH MARK */ - { 0x11A33, 0x11A39 }, /* ZANABAZAR SQUARE FINAL CONSONANT MARK - ZANABAZAR SQUARE SIGN VISARGA */ - { 0x11A3B, 0x11A3E }, /* ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA - ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA */ - { 0x11A47, 0x11A47 }, /* ZANABAZAR SQUARE SUBJOINER */ - { 0x11A51, 0x11A5B }, /* SOYOMBO VOWEL SIGN I - SOYOMBO VOWEL LENGTH MARK */ - { 0x11A8A, 0x11A99 }, /* SOYOMBO FINAL CONSONANT SIGN G - SOYOMBO SUBJOINER */ - { 0x11C2F, 0x11C36 }, /* BHAIKSUKI VOWEL SIGN AA - BHAIKSUKI VOWEL SIGN VOCALIC L */ - { 0x11C38, 0x11C3F }, /* BHAIKSUKI VOWEL SIGN E - BHAIKSUKI SIGN VIRAMA */ - { 0x11C92, 0x11CA7 }, /* MARCHEN SUBJOINED LETTER KA - MARCHEN SUBJOINED LETTER ZA */ - { 0x11CA9, 0x11CB6 }, /* MARCHEN SUBJOINED LETTER YA - MARCHEN SIGN CANDRABINDU */ - { 0x11D31, 0x11D36 }, /* MASARAM GONDI VOWEL SIGN AA - MASARAM GONDI VOWEL SIGN VOCALIC R */ - { 0x11D3A, 0x11D3A }, /* MASARAM GONDI VOWEL SIGN E */ - { 0x11D3C, 0x11D3D }, /* MASARAM GONDI VOWEL SIGN AI - MASARAM GONDI VOWEL SIGN O */ - { 0x11D3F, 0x11D45 }, /* MASARAM GONDI VOWEL SIGN AU - MASARAM GONDI VIRAMA */ - { 0x11D47, 0x11D47 }, /* MASARAM GONDI RA-KARA */ - { 0x11D8A, 0x11D8E }, /* GUNJALA GONDI VOWEL SIGN AA - GUNJALA GONDI VOWEL SIGN UU */ - { 0x11D90, 0x11D91 }, /* GUNJALA GONDI VOWEL SIGN EE - GUNJALA GONDI VOWEL SIGN AI */ - { 0x11D93, 0x11D97 }, /* GUNJALA GONDI VOWEL SIGN OO - GUNJALA GONDI VIRAMA */ - { 0x11EF3, 0x11EF6 }, /* MAKASAR VOWEL SIGN I - MAKASAR VOWEL SIGN O */ - { 0x11F00, 0x11F01 }, /* KAWI SIGN CANDRABINDU - KAWI SIGN ANUSVARA */ - { 0x11F03, 0x11F03 }, /* KAWI SIGN VISARGA */ - { 0x11F34, 0x11F3A }, /* KAWI VOWEL SIGN AA - KAWI VOWEL SIGN VOCALIC R */ - { 0x11F3E, 0x11F42 }, /* KAWI VOWEL SIGN E - KAWI CONJOINER */ - { 0x11F5A, 0x11F5A }, /* KAWI SIGN NUKTA */ - { 0x13430, 0x13440 }, /* EGYPTIAN HIEROGLYPH VERTICAL JOINER - EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY */ - { 0x13447, 0x13455 }, /* EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START - EGYPTIAN HIEROGLYPH MODIFIER DAMAGED */ - { 0x1611E, 0x1612F }, /* GURUNG KHEMA VOWEL SIGN AA - GURUNG KHEMA SIGN THOLHOMA */ - { 0x16AF0, 0x16AF4 }, /* BASSA VAH COMBINING HIGH TONE - BASSA VAH COMBINING HIGH-LOW TONE */ - { 0x16B30, 0x16B36 }, /* PAHAWH HMONG MARK CIM TUB - PAHAWH HMONG MARK CIM TAUM */ - { 0x16F4F, 0x16F4F }, /* MIAO SIGN CONSONANT MODIFIER BAR */ - { 0x16F51, 0x16F87 }, /* MIAO SIGN ASPIRATION - MIAO VOWEL SIGN UI */ - { 0x16F8F, 0x16F92 }, /* MIAO TONE RIGHT - MIAO TONE BELOW */ - { 0x16FE4, 0x16FE4 }, /* KHITAN SMALL SCRIPT FILLER */ - { 0x16FF0, 0x16FF1 }, /* VIETNAMESE ALTERNATE READING MARK CA - VIETNAMESE ALTERNATE READING MARK NHAY */ - { 0x1BC9D, 0x1BC9E }, /* DUPLOYAN THICK LETTER SELECTOR - DUPLOYAN DOUBLE MARK */ - { 0x1BCA0, 0x1BCA3 }, /* SHORTHAND FORMAT LETTER OVERLAP - SHORTHAND FORMAT UP STEP */ - { 0x1CF00, 0x1CF2D }, /* ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT - ZNAMENNY COMBINING MARK KRYZH ON LEFT */ - { 0x1CF30, 0x1CF46 }, /* ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO - ZNAMENNY PRIZNAK MODIFIER ROG */ - { 0x1D165, 0x1D169 }, /* MUSICAL SYMBOL COMBINING STEM - MUSICAL SYMBOL COMBINING TREMOLO-3 */ - { 0x1D16D, 0x1D182 }, /* MUSICAL SYMBOL COMBINING AUGMENTATION DOT - MUSICAL SYMBOL COMBINING LOURE */ - { 0x1D185, 0x1D18B }, /* MUSICAL SYMBOL COMBINING DOIT - MUSICAL SYMBOL COMBINING TRIPLE TONGUE */ - { 0x1D1AA, 0x1D1AD }, /* MUSICAL SYMBOL COMBINING DOWN BOW - MUSICAL SYMBOL COMBINING SNAP PIZZICATO */ - { 0x1D242, 0x1D244 }, /* COMBINING GREEK MUSICAL TRISEME - COMBINING GREEK MUSICAL PENTASEME */ - { 0x1DA00, 0x1DA36 }, /* SIGNWRITING HEAD RIM - SIGNWRITING AIR SUCKING IN */ - { 0x1DA3B, 0x1DA6C }, /* SIGNWRITING MOUTH CLOSED NEUTRAL - SIGNWRITING EXCITEMENT */ - { 0x1DA75, 0x1DA75 }, /* SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS */ - { 0x1DA84, 0x1DA84 }, /* SIGNWRITING LOCATION HEAD NECK */ - { 0x1DA9B, 0x1DA9F }, /* SIGNWRITING FILL MODIFIER-2 - SIGNWRITING FILL MODIFIER-6 */ - { 0x1DAA1, 0x1DAAF }, /* SIGNWRITING ROTATION MODIFIER-2 - SIGNWRITING ROTATION MODIFIER-16 */ - { 0x1E000, 0x1E006 }, /* COMBINING GLAGOLITIC LETTER AZU - COMBINING GLAGOLITIC LETTER ZHIVETE */ - { 0x1E008, 0x1E018 }, /* COMBINING GLAGOLITIC LETTER ZEMLJA - COMBINING GLAGOLITIC LETTER HERU */ - { 0x1E01B, 0x1E021 }, /* COMBINING GLAGOLITIC LETTER SHTA - COMBINING GLAGOLITIC LETTER YATI */ - { 0x1E023, 0x1E024 }, /* COMBINING GLAGOLITIC LETTER YU - COMBINING GLAGOLITIC LETTER SMALL YUS */ - { 0x1E026, 0x1E02A }, /* COMBINING GLAGOLITIC LETTER YO - COMBINING GLAGOLITIC LETTER FITA */ - { 0x1E08F, 0x1E08F }, /* COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ - { 0x1E130, 0x1E136 }, /* NYIAKENG PUACHUE HMONG TONE-B - NYIAKENG PUACHUE HMONG TONE-D */ - { 0x1E2AE, 0x1E2AE }, /* TOTO SIGN RISING TONE */ - { 0x1E2EC, 0x1E2EF }, /* WANCHO TONE TUP - WANCHO TONE KOINI */ - { 0x1E4EC, 0x1E4EF }, /* NAG MUNDARI SIGN MUHOR - NAG MUNDARI SIGN SUTUH */ - { 0x1E5EE, 0x1E5EF }, /* OL ONAL SIGN MU - OL ONAL SIGN IKIR */ - { 0x1E8D0, 0x1E8D6 }, /* MENDE KIKAKUI COMBINING NUMBER TEENS - MENDE KIKAKUI COMBINING NUMBER MILLIONS */ - { 0x1E944, 0x1E94A }, /* ADLAM ALIF LENGTHENER - ADLAM NUKTA */ - { 0x1F3FB, 0x1F3FF }, /* EMOJI MODIFIER FITZPATRICK TYPE-1-2 - EMOJI MODIFIER FITZPATRICK TYPE-6 */ - { 0x1F9B0, 0x1F9B3 }, /* EMOJI COMPONENT RED HAIR - EMOJI COMPONENT WHITE HAIR */ - { 0xE0001, 0xE0001 }, /* LANGUAGE TAG */ - { 0xE0020, 0xE007F }, /* TAG SPACE - CANCEL TAG */ - { 0xE0100, 0xE01EF }, /* VARIATION SELECTOR-17 - VARIATION SELECTOR-256 */ -}; - -/* Double-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ -static const struct ucs_interval16 ucs_double_width_bmp_ranges[] = { - { 0x1100, 0x115F }, /* HANGUL CHOSEONG KIYEOK - HANGUL CHOSEONG FILLER */ - { 0x231A, 0x231B }, /* WATCH - HOURGLASS */ - { 0x2329, 0x232A }, /* LEFT-POINTING ANGLE BRACKET - RIGHT-POINTING ANGLE BRACKET */ - { 0x23E9, 0x23EC }, /* BLACK RIGHT-POINTING DOUBLE TRIANGLE - BLACK DOWN-POINTING DOUBLE TRIANGLE */ - { 0x23F0, 0x23F0 }, /* ALARM CLOCK */ - { 0x23F3, 0x23F3 }, /* HOURGLASS WITH FLOWING SAND */ - { 0x25FD, 0x25FE }, /* WHITE MEDIUM SMALL SQUARE - BLACK MEDIUM SMALL SQUARE */ - { 0x2614, 0x2615 }, /* UMBRELLA WITH RAIN DROPS - HOT BEVERAGE */ - { 0x2630, 0x2637 }, /* TRIGRAM FOR HEAVEN - TRIGRAM FOR EARTH */ - { 0x2648, 0x2653 }, /* ARIES - PISCES */ - { 0x267F, 0x267F }, /* WHEELCHAIR SYMBOL */ - { 0x268A, 0x268F }, /* MONOGRAM FOR YANG - DIGRAM FOR GREATER YIN */ - { 0x2693, 0x2693 }, /* ANCHOR */ - { 0x26A1, 0x26A1 }, /* HIGH VOLTAGE SIGN */ - { 0x26AA, 0x26AB }, /* MEDIUM WHITE CIRCLE - MEDIUM BLACK CIRCLE */ - { 0x26BD, 0x26BE }, /* SOCCER BALL - BASEBALL */ - { 0x26C4, 0x26C5 }, /* SNOWMAN WITHOUT SNOW - SUN BEHIND CLOUD */ - { 0x26CE, 0x26CE }, /* OPHIUCHUS */ - { 0x26D4, 0x26D4 }, /* NO ENTRY */ - { 0x26EA, 0x26EA }, /* CHURCH */ - { 0x26F2, 0x26F3 }, /* FOUNTAIN - FLAG IN HOLE */ - { 0x26F5, 0x26F5 }, /* SAILBOAT */ - { 0x26FA, 0x26FA }, /* TENT */ - { 0x26FD, 0x26FD }, /* FUEL PUMP */ - { 0x2705, 0x2705 }, /* WHITE HEAVY CHECK MARK */ - { 0x270A, 0x270B }, /* RAISED FIST - RAISED HAND */ - { 0x2728, 0x2728 }, /* SPARKLES */ - { 0x274C, 0x274C }, /* CROSS MARK */ - { 0x274E, 0x274E }, /* NEGATIVE SQUARED CROSS MARK */ - { 0x2753, 0x2755 }, /* BLACK QUESTION MARK ORNAMENT - WHITE EXCLAMATION MARK ORNAMENT */ - { 0x2757, 0x2757 }, /* HEAVY EXCLAMATION MARK SYMBOL */ - { 0x2795, 0x2797 }, /* HEAVY PLUS SIGN - HEAVY DIVISION SIGN */ - { 0x27B0, 0x27B0 }, /* CURLY LOOP */ - { 0x27BF, 0x27BF }, /* DOUBLE CURLY LOOP */ - { 0x2B1B, 0x2B1C }, /* BLACK LARGE SQUARE - WHITE LARGE SQUARE */ - { 0x2B50, 0x2B50 }, /* WHITE MEDIUM STAR */ - { 0x2B55, 0x2B55 }, /* HEAVY LARGE CIRCLE */ - { 0x2E80, 0x2E99 }, /* CJK RADICAL REPEAT - CJK RADICAL RAP */ - { 0x2E9B, 0x2EF3 }, /* CJK RADICAL CHOKE - CJK RADICAL C-SIMPLIFIED TURTLE */ - { 0x2F00, 0x2FD5 }, /* KANGXI RADICAL ONE - KANGXI RADICAL FLUTE */ - { 0x2FF0, 0x3029 }, /* IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT - HANGZHOU NUMERAL NINE */ - { 0x3030, 0x303E }, /* WAVY DASH - IDEOGRAPHIC VARIATION INDICATOR */ - { 0x3041, 0x3096 }, /* HIRAGANA LETTER SMALL A - HIRAGANA LETTER SMALL KE */ - { 0x309B, 0x30FF }, /* KATAKANA-HIRAGANA VOICED SOUND MARK - KATAKANA DIGRAPH KOTO */ - { 0x3105, 0x312F }, /* BOPOMOFO LETTER B - BOPOMOFO LETTER NN */ - { 0x3131, 0x318E }, /* HANGUL LETTER KIYEOK - HANGUL LETTER ARAEAE */ - { 0x3190, 0x31E5 }, /* IDEOGRAPHIC ANNOTATION LINKING MARK - CJK STROKE SZP */ - { 0x31EF, 0x321E }, /* IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION - PARENTHESIZED KOREAN CHARACTER O HU */ - { 0x3220, 0x3247 }, /* PARENTHESIZED IDEOGRAPH ONE - CIRCLED IDEOGRAPH KOTO */ - { 0x3250, 0xA48C }, /* PARTNERSHIP SIGN - YI SYLLABLE YYR */ - { 0xA490, 0xA4C6 }, /* YI RADICAL QOT - YI RADICAL KE */ - { 0xA960, 0xA97C }, /* HANGUL CHOSEONG TIKEUT-MIEUM - HANGUL CHOSEONG SSANGYEORINHIEUH */ - { 0xAC00, 0xD7A3 }, /* HANGUL SYLLABLE GA - HANGUL SYLLABLE HIH */ - { 0xF900, 0xFAFF }, /* U+F900 - U+FAFF */ - { 0xFE10, 0xFE19 }, /* PRESENTATION FORM FOR VERTICAL COMMA - PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS */ - { 0xFE30, 0xFE52 }, /* PRESENTATION FORM FOR VERTICAL TWO DOT LEADER - SMALL FULL STOP */ - { 0xFE54, 0xFE66 }, /* SMALL SEMICOLON - SMALL EQUALS SIGN */ - { 0xFE68, 0xFE6B }, /* SMALL REVERSE SOLIDUS - SMALL COMMERCIAL AT */ - { 0xFF01, 0xFF60 }, /* FULLWIDTH EXCLAMATION MARK - FULLWIDTH RIGHT WHITE PARENTHESIS */ - { 0xFFE0, 0xFFE6 }, /* FULLWIDTH CENT SIGN - FULLWIDTH WON SIGN */ -}; - -/* Double-width character ranges (non-BMP, U+10000 and above) */ -static const struct ucs_interval32 ucs_double_width_non_bmp_ranges[] = { - { 0x16FE0, 0x16FE3 }, /* TANGUT ITERATION MARK - OLD CHINESE ITERATION MARK */ - { 0x17000, 0x187F7 }, /* U+17000 - U+187F7 */ - { 0x18800, 0x18CD5 }, /* TANGUT COMPONENT-001 - KHITAN SMALL SCRIPT CHARACTER-18CD5 */ - { 0x18CFF, 0x18D08 }, /* U+18CFF - U+18D08 */ - { 0x1AFF0, 0x1AFF3 }, /* KATAKANA LETTER MINNAN TONE-2 - KATAKANA LETTER MINNAN TONE-5 */ - { 0x1AFF5, 0x1AFFB }, /* KATAKANA LETTER MINNAN TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-5 */ - { 0x1AFFD, 0x1AFFE }, /* KATAKANA LETTER MINNAN NASALIZED TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-8 */ - { 0x1B000, 0x1B122 }, /* KATAKANA LETTER ARCHAIC E - KATAKANA LETTER ARCHAIC WU */ - { 0x1B132, 0x1B132 }, /* HIRAGANA LETTER SMALL KO */ - { 0x1B150, 0x1B152 }, /* HIRAGANA LETTER SMALL WI - HIRAGANA LETTER SMALL WO */ - { 0x1B155, 0x1B155 }, /* KATAKANA LETTER SMALL KO */ - { 0x1B164, 0x1B167 }, /* KATAKANA LETTER SMALL WI - KATAKANA LETTER SMALL N */ - { 0x1B170, 0x1B2FB }, /* NUSHU CHARACTER-1B170 - NUSHU CHARACTER-1B2FB */ - { 0x1D300, 0x1D356 }, /* MONOGRAM FOR EARTH - TETRAGRAM FOR FOSTERING */ - { 0x1D360, 0x1D376 }, /* COUNTING ROD UNIT DIGIT ONE - IDEOGRAPHIC TALLY MARK FIVE */ - { 0x1F000, 0x1F02F }, /* U+1F000 - U+1F02F */ - { 0x1F0A0, 0x1F0FF }, /* U+1F0A0 - U+1F0FF */ - { 0x1F18E, 0x1F18E }, /* NEGATIVE SQUARED AB */ - { 0x1F191, 0x1F19A }, /* SQUARED CL - SQUARED VS */ - { 0x1F200, 0x1F202 }, /* SQUARE HIRAGANA HOKA - SQUARED KATAKANA SA */ - { 0x1F210, 0x1F23B }, /* SQUARED CJK UNIFIED IDEOGRAPH-624B - SQUARED CJK UNIFIED IDEOGRAPH-914D */ - { 0x1F240, 0x1F248 }, /* TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C - TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 */ - { 0x1F250, 0x1F251 }, /* CIRCLED IDEOGRAPH ADVANTAGE - CIRCLED IDEOGRAPH ACCEPT */ - { 0x1F260, 0x1F265 }, /* ROUNDED SYMBOL FOR FU - ROUNDED SYMBOL FOR CAI */ - { 0x1F300, 0x1F3FA }, /* CYCLONE - AMPHORA */ - { 0x1F400, 0x1F64F }, /* RAT - PERSON WITH FOLDED HANDS */ - { 0x1F680, 0x1F9AF }, /* ROCKET - PROBING CANE */ - { 0x1F9B4, 0x1FAFF }, /* U+1F9B4 - U+1FAFF */ - { 0x20000, 0x2FFFD }, /* U+20000 - U+2FFFD */ - { 0x30000, 0x3FFFD }, /* U+30000 - U+3FFFD */ +/* Combined zero- and double-width ranges (non-BMP, U+10000 and above). + * The first 33 entries host the BMP double-width bitmap in the low + * 8 bits of `last`. */ +static const struct ucs_width32 ucs_nonbmp_ranges[] = { + { RANGE_0WIDTH(0x101FD, 0x101FD) /* PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 0.. 7] */ + { RANGE_0WIDTH(0x102E0, 0x102E0) /* COPTIC EPACT THOUSANDS MARK */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 8.. 15] */ + { RANGE_0WIDTH(0x10376, 0x1037A) /* COMBINING OLD PERMIC LETTER AN - COMBINING OLD PERMIC LETTER SII */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 16.. 23] */ + { RANGE_0WIDTH(0x10A01, 0x10A03) /* KHAROSHTHI VOWEL SIGN I - KHAROSHTHI VOWEL SIGN VOCALIC R */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 24.. 31] */ + { RANGE_0WIDTH(0x10A05, 0x10A06) /* KHAROSHTHI VOWEL SIGN E - KHAROSHTHI VOWEL SIGN O */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 32.. 39] */ + { RANGE_0WIDTH(0x10A0C, 0x10A0F) /* KHAROSHTHI VOWEL LENGTH MARK - KHAROSHTHI SIGN VISARGA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 40.. 47] */ + { RANGE_0WIDTH(0x10A38, 0x10A3A) /* KHAROSHTHI SIGN BAR ABOVE - KHAROSHTHI SIGN DOT BELOW */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 48.. 55] */ + { RANGE_0WIDTH(0x10A3F, 0x10A3F) /* KHAROSHTHI VIRAMA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 56.. 63] */ + { RANGE_0WIDTH(0x10AE5, 0x10AE6) /* MANICHAEAN ABBREVIATION MARK ABOVE - MANICHAEAN ABBREVIATION MARK BELOW */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 64.. 71] */ + { RANGE_0WIDTH(0x10D24, 0x10D27) /* HANIFI ROHINGYA SIGN HARBAHAY - HANIFI ROHINGYA SIGN TASSI */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 72.. 79] */ + { RANGE_0WIDTH(0x10D69, 0x10D6D) /* GARAY VOWEL SIGN E - GARAY CONSONANT NASALIZATION MARK */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 80.. 87] */ + { RANGE_0WIDTH(0x10EAB, 0x10EAC) /* YEZIDI COMBINING HAMZA MARK - YEZIDI COMBINING MADDA MARK */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 88.. 95] */ + { RANGE_0WIDTH(0x10EFC, 0x10EFF) /* ARABIC COMBINING ALEF OVERLAY - ARABIC SMALL LOW WORD MADDA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 96..103] */ + { RANGE_0WIDTH(0x10F46, 0x10F50) /* SOGDIAN COMBINING DOT BELOW - SOGDIAN COMBINING STROKE BELOW */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [104..111] */ + { RANGE_0WIDTH(0x10F82, 0x10F85) /* OLD UYGHUR COMBINING DOT ABOVE - OLD UYGHUR COMBINING TWO DOTS BELOW */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [112..119] */ + { RANGE_0WIDTH(0x11000, 0x11002) /* BRAHMI SIGN CANDRABINDU - BRAHMI SIGN VISARGA */ + | BMP_2W_BITS(0b00001000) }, /* BMP entries [120..127] */ + { RANGE_0WIDTH(0x11038, 0x11046) /* BRAHMI VOWEL SIGN AA - BRAHMI VIRAMA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [128..135] */ + { RANGE_0WIDTH(0x11070, 0x11070) /* BRAHMI SIGN OLD TAMIL VIRAMA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [136..143] */ + { RANGE_0WIDTH(0x11073, 0x11074) /* BRAHMI VOWEL SIGN OLD TAMIL SHORT E - BRAHMI VOWEL SIGN OLD TAMIL SHORT O */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [144..151] */ + { RANGE_0WIDTH(0x1107F, 0x11082) /* BRAHMI NUMBER JOINER - KAITHI SIGN VISARGA */ + | BMP_2W_BITS(0b10000000) }, /* BMP entries [152..159] */ + { RANGE_0WIDTH(0x110B0, 0x110BA) /* KAITHI VOWEL SIGN AA - KAITHI SIGN NUKTA */ + | BMP_2W_BITS(0b01111111) }, /* BMP entries [160..167] */ + { RANGE_0WIDTH(0x110BD, 0x110BD) /* KAITHI NUMBER SIGN */ + | BMP_2W_BITS(0b10111110) }, /* BMP entries [168..175] */ + { RANGE_0WIDTH(0x110C2, 0x110C2) /* KAITHI VOWEL SIGN VOCALIC R */ + | BMP_2W_BITS(0b11111111) }, /* BMP entries [176..183] */ + { RANGE_0WIDTH(0x110CD, 0x110CD) /* KAITHI NUMBER SIGN ABOVE */ + | BMP_2W_BITS(0b11111111) }, /* BMP entries [184..191] */ + { RANGE_0WIDTH(0x11100, 0x11102) /* CHAKMA SIGN CANDRABINDU - CHAKMA SIGN VISARGA */ + | BMP_2W_BITS(0b00111111) }, /* BMP entries [192..199] */ + { RANGE_0WIDTH(0x11127, 0x11134) /* CHAKMA VOWEL SIGN A - CHAKMA MAAYYAA */ + | BMP_2W_BITS(0b11011110) }, /* BMP entries [200..207] */ + { RANGE_0WIDTH(0x11145, 0x11146) /* CHAKMA VOWEL SIGN AA - CHAKMA VOWEL SIGN EI */ + | BMP_2W_BITS(0b11111110) }, /* BMP entries [208..215] */ + { RANGE_0WIDTH(0x11173, 0x11173) /* MAHAJANI SIGN NUKTA */ + | BMP_2W_BITS(0b00000001) }, /* BMP entries [216..223] */ + { RANGE_0WIDTH(0x11180, 0x11182) /* SHARADA SIGN CANDRABINDU - SHARADA SIGN VISARGA */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [224..231] */ + { RANGE_0WIDTH(0x111B3, 0x111C0) /* SHARADA VOWEL SIGN AA - SHARADA SIGN VIRAMA */ + | BMP_2W_BITS(0b00000001) }, /* BMP entries [232..239] */ + { RANGE_0WIDTH(0x111C9, 0x111CC) /* SHARADA SANDHI MARK - SHARADA EXTRA SHORT VOWEL MARK */ + | BMP_2W_BITS(0b00000000) }, /* BMP entries [240..247] */ + { RANGE_0WIDTH(0x111CE, 0x111CF) /* SHARADA VOWEL SIGN PRISHTHAMATRA E - SHARADA SIGN INVERTED CANDRABINDU */ + | BMP_2W_BITS(0b10100110) }, /* BMP entries [248..255] */ + { RANGE_0WIDTH(0x1122C, 0x11237) /* KHOJKI VOWEL SIGN AA - KHOJKI SIGN SHADDA */ + | BMP_2W_BITS(0b00011011) }, /* BMP entries [256..261] */ + { RANGE_0WIDTH(0x1123E, 0x1123E) }, /* KHOJKI SIGN SUKUN */ + { RANGE_0WIDTH(0x11241, 0x11241) }, /* KHOJKI VOWEL SIGN VOCALIC R */ + { RANGE_0WIDTH(0x112DF, 0x112EA) }, /* KHUDAWADI SIGN ANUSVARA - KHUDAWADI SIGN VIRAMA */ + { RANGE_0WIDTH(0x11300, 0x11303) }, /* GRANTHA SIGN COMBINING ANUSVARA ABOVE - GRANTHA SIGN VISARGA */ + { RANGE_0WIDTH(0x1133B, 0x1133C) }, /* COMBINING BINDU BELOW - GRANTHA SIGN NUKTA */ + { RANGE_0WIDTH(0x1133E, 0x11344) }, /* GRANTHA VOWEL SIGN AA - GRANTHA VOWEL SIGN VOCALIC RR */ + { RANGE_0WIDTH(0x11347, 0x11348) }, /* GRANTHA VOWEL SIGN EE - GRANTHA VOWEL SIGN AI */ + { RANGE_0WIDTH(0x1134B, 0x1134D) }, /* GRANTHA VOWEL SIGN OO - GRANTHA SIGN VIRAMA */ + { RANGE_0WIDTH(0x11357, 0x11357) }, /* GRANTHA AU LENGTH MARK */ + { RANGE_0WIDTH(0x11362, 0x11363) }, /* GRANTHA VOWEL SIGN VOCALIC L - GRANTHA VOWEL SIGN VOCALIC LL */ + { RANGE_0WIDTH(0x11366, 0x1136C) }, /* COMBINING GRANTHA DIGIT ZERO - COMBINING GRANTHA DIGIT SIX */ + { RANGE_0WIDTH(0x11370, 0x11374) }, /* COMBINING GRANTHA LETTER A - COMBINING GRANTHA LETTER PA */ + { RANGE_0WIDTH(0x113B8, 0x113C0) }, /* TULU-TIGALARI VOWEL SIGN AA - TULU-TIGALARI VOWEL SIGN VOCALIC LL */ + { RANGE_0WIDTH(0x113C2, 0x113C2) }, /* TULU-TIGALARI VOWEL SIGN EE */ + { RANGE_0WIDTH(0x113C5, 0x113C5) }, /* TULU-TIGALARI VOWEL SIGN AI */ + { RANGE_0WIDTH(0x113C7, 0x113CA) }, /* TULU-TIGALARI VOWEL SIGN OO - TULU-TIGALARI SIGN CANDRA ANUNASIKA */ + { RANGE_0WIDTH(0x113CC, 0x113D0) }, /* TULU-TIGALARI SIGN ANUSVARA - TULU-TIGALARI CONJOINER */ + { RANGE_0WIDTH(0x113D2, 0x113D2) }, /* TULU-TIGALARI GEMINATION MARK */ + { RANGE_0WIDTH(0x113E1, 0x113E2) }, /* TULU-TIGALARI VEDIC TONE SVARITA - TULU-TIGALARI VEDIC TONE ANUDATTA */ + { RANGE_0WIDTH(0x11435, 0x11446) }, /* NEWA VOWEL SIGN AA - NEWA SIGN NUKTA */ + { RANGE_0WIDTH(0x1145E, 0x1145E) }, /* NEWA SANDHI MARK */ + { RANGE_0WIDTH(0x114B0, 0x114C3) }, /* TIRHUTA VOWEL SIGN AA - TIRHUTA SIGN NUKTA */ + { RANGE_0WIDTH(0x115AF, 0x115B5) }, /* SIDDHAM VOWEL SIGN AA - SIDDHAM VOWEL SIGN VOCALIC RR */ + { RANGE_0WIDTH(0x115B8, 0x115C0) }, /* SIDDHAM VOWEL SIGN E - SIDDHAM SIGN NUKTA */ + { RANGE_0WIDTH(0x115DC, 0x115DD) }, /* SIDDHAM VOWEL SIGN ALTERNATE U - SIDDHAM VOWEL SIGN ALTERNATE UU */ + { RANGE_0WIDTH(0x11630, 0x11640) }, /* MODI VOWEL SIGN AA - MODI SIGN ARDHACANDRA */ + { RANGE_0WIDTH(0x116AB, 0x116B7) }, /* TAKRI SIGN ANUSVARA - TAKRI SIGN NUKTA */ + { RANGE_0WIDTH(0x1171D, 0x1172B) }, /* AHOM CONSONANT SIGN MEDIAL LA - AHOM SIGN KILLER */ + { RANGE_0WIDTH(0x1182C, 0x1183A) }, /* DOGRA VOWEL SIGN AA - DOGRA SIGN NUKTA */ + { RANGE_0WIDTH(0x11930, 0x11935) }, /* DIVES AKURU VOWEL SIGN AA - DIVES AKURU VOWEL SIGN E */ + { RANGE_0WIDTH(0x11937, 0x11938) }, /* DIVES AKURU VOWEL SIGN AI - DIVES AKURU VOWEL SIGN O */ + { RANGE_0WIDTH(0x1193B, 0x1193E) }, /* DIVES AKURU SIGN ANUSVARA - DIVES AKURU VIRAMA */ + { RANGE_0WIDTH(0x11940, 0x11940) }, /* DIVES AKURU MEDIAL YA */ + { RANGE_0WIDTH(0x11942, 0x11943) }, /* DIVES AKURU MEDIAL RA - DIVES AKURU SIGN NUKTA */ + { RANGE_0WIDTH(0x119D1, 0x119D7) }, /* NANDINAGARI VOWEL SIGN AA - NANDINAGARI VOWEL SIGN VOCALIC RR */ + { RANGE_0WIDTH(0x119DA, 0x119E0) }, /* NANDINAGARI VOWEL SIGN E - NANDINAGARI SIGN VIRAMA */ + { RANGE_0WIDTH(0x119E4, 0x119E4) }, /* NANDINAGARI VOWEL SIGN PRISHTHAMATRA E */ + { RANGE_0WIDTH(0x11A01, 0x11A0A) }, /* ZANABAZAR SQUARE VOWEL SIGN I - ZANABAZAR SQUARE VOWEL LENGTH MARK */ + { RANGE_0WIDTH(0x11A33, 0x11A39) }, /* ZANABAZAR SQUARE FINAL CONSONANT MARK - ZANABAZAR SQUARE SIGN VISARGA */ + { RANGE_0WIDTH(0x11A3B, 0x11A3E) }, /* ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA - ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA */ + { RANGE_0WIDTH(0x11A47, 0x11A47) }, /* ZANABAZAR SQUARE SUBJOINER */ + { RANGE_0WIDTH(0x11A51, 0x11A5B) }, /* SOYOMBO VOWEL SIGN I - SOYOMBO VOWEL LENGTH MARK */ + { RANGE_0WIDTH(0x11A8A, 0x11A99) }, /* SOYOMBO FINAL CONSONANT SIGN G - SOYOMBO SUBJOINER */ + { RANGE_0WIDTH(0x11C2F, 0x11C36) }, /* BHAIKSUKI VOWEL SIGN AA - BHAIKSUKI VOWEL SIGN VOCALIC L */ + { RANGE_0WIDTH(0x11C38, 0x11C3F) }, /* BHAIKSUKI VOWEL SIGN E - BHAIKSUKI SIGN VIRAMA */ + { RANGE_0WIDTH(0x11C92, 0x11CA7) }, /* MARCHEN SUBJOINED LETTER KA - MARCHEN SUBJOINED LETTER ZA */ + { RANGE_0WIDTH(0x11CA9, 0x11CB6) }, /* MARCHEN SUBJOINED LETTER YA - MARCHEN SIGN CANDRABINDU */ + { RANGE_0WIDTH(0x11D31, 0x11D36) }, /* MASARAM GONDI VOWEL SIGN AA - MASARAM GONDI VOWEL SIGN VOCALIC R */ + { RANGE_0WIDTH(0x11D3A, 0x11D3A) }, /* MASARAM GONDI VOWEL SIGN E */ + { RANGE_0WIDTH(0x11D3C, 0x11D3D) }, /* MASARAM GONDI VOWEL SIGN AI - MASARAM GONDI VOWEL SIGN O */ + { RANGE_0WIDTH(0x11D3F, 0x11D45) }, /* MASARAM GONDI VOWEL SIGN AU - MASARAM GONDI VIRAMA */ + { RANGE_0WIDTH(0x11D47, 0x11D47) }, /* MASARAM GONDI RA-KARA */ + { RANGE_0WIDTH(0x11D8A, 0x11D8E) }, /* GUNJALA GONDI VOWEL SIGN AA - GUNJALA GONDI VOWEL SIGN UU */ + { RANGE_0WIDTH(0x11D90, 0x11D91) }, /* GUNJALA GONDI VOWEL SIGN EE - GUNJALA GONDI VOWEL SIGN AI */ + { RANGE_0WIDTH(0x11D93, 0x11D97) }, /* GUNJALA GONDI VOWEL SIGN OO - GUNJALA GONDI VIRAMA */ + { RANGE_0WIDTH(0x11EF3, 0x11EF6) }, /* MAKASAR VOWEL SIGN I - MAKASAR VOWEL SIGN O */ + { RANGE_0WIDTH(0x11F00, 0x11F01) }, /* KAWI SIGN CANDRABINDU - KAWI SIGN ANUSVARA */ + { RANGE_0WIDTH(0x11F03, 0x11F03) }, /* KAWI SIGN VISARGA */ + { RANGE_0WIDTH(0x11F34, 0x11F3A) }, /* KAWI VOWEL SIGN AA - KAWI VOWEL SIGN VOCALIC R */ + { RANGE_0WIDTH(0x11F3E, 0x11F42) }, /* KAWI VOWEL SIGN E - KAWI CONJOINER */ + { RANGE_0WIDTH(0x11F5A, 0x11F5A) }, /* KAWI SIGN NUKTA */ + { RANGE_0WIDTH(0x13430, 0x13440) }, /* EGYPTIAN HIEROGLYPH VERTICAL JOINER - EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY */ + { RANGE_0WIDTH(0x13447, 0x13455) }, /* EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START - EGYPTIAN HIEROGLYPH MODIFIER DAMAGED */ + { RANGE_0WIDTH(0x1611E, 0x1612F) }, /* GURUNG KHEMA VOWEL SIGN AA - GURUNG KHEMA SIGN THOLHOMA */ + { RANGE_0WIDTH(0x16AF0, 0x16AF4) }, /* BASSA VAH COMBINING HIGH TONE - BASSA VAH COMBINING HIGH-LOW TONE */ + { RANGE_0WIDTH(0x16B30, 0x16B36) }, /* PAHAWH HMONG MARK CIM TUB - PAHAWH HMONG MARK CIM TAUM */ + { RANGE_0WIDTH(0x16F4F, 0x16F4F) }, /* MIAO SIGN CONSONANT MODIFIER BAR */ + { RANGE_0WIDTH(0x16F51, 0x16F87) }, /* MIAO SIGN ASPIRATION - MIAO VOWEL SIGN UI */ + { RANGE_0WIDTH(0x16F8F, 0x16F92) }, /* MIAO TONE RIGHT - MIAO TONE BELOW */ + { RANGE_2WIDTH(0x16FE0, 0x16FE3) }, /* TANGUT ITERATION MARK - OLD CHINESE ITERATION MARK */ + { RANGE_0WIDTH(0x16FE4, 0x16FE4) }, /* KHITAN SMALL SCRIPT FILLER */ + { RANGE_0WIDTH(0x16FF0, 0x16FF1) }, /* VIETNAMESE ALTERNATE READING MARK CA - VIETNAMESE ALTERNATE READING MARK NHAY */ + { RANGE_2WIDTH(0x17000, 0x187F7) }, /* U+17000 - U+187F7 */ + { RANGE_2WIDTH(0x18800, 0x18CD5) }, /* TANGUT COMPONENT-001 - KHITAN SMALL SCRIPT CHARACTER-18CD5 */ + { RANGE_2WIDTH(0x18CFF, 0x18D08) }, /* U+18CFF - U+18D08 */ + { RANGE_2WIDTH(0x1AFF0, 0x1AFF3) }, /* KATAKANA LETTER MINNAN TONE-2 - KATAKANA LETTER MINNAN TONE-5 */ + { RANGE_2WIDTH(0x1AFF5, 0x1AFFB) }, /* KATAKANA LETTER MINNAN TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-5 */ + { RANGE_2WIDTH(0x1AFFD, 0x1AFFE) }, /* KATAKANA LETTER MINNAN NASALIZED TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-8 */ + { RANGE_2WIDTH(0x1B000, 0x1B122) }, /* KATAKANA LETTER ARCHAIC E - KATAKANA LETTER ARCHAIC WU */ + { RANGE_2WIDTH(0x1B132, 0x1B132) }, /* HIRAGANA LETTER SMALL KO */ + { RANGE_2WIDTH(0x1B150, 0x1B152) }, /* HIRAGANA LETTER SMALL WI - HIRAGANA LETTER SMALL WO */ + { RANGE_2WIDTH(0x1B155, 0x1B155) }, /* KATAKANA LETTER SMALL KO */ + { RANGE_2WIDTH(0x1B164, 0x1B167) }, /* KATAKANA LETTER SMALL WI - KATAKANA LETTER SMALL N */ + { RANGE_2WIDTH(0x1B170, 0x1B2FB) }, /* NUSHU CHARACTER-1B170 - NUSHU CHARACTER-1B2FB */ + { RANGE_0WIDTH(0x1BC9D, 0x1BC9E) }, /* DUPLOYAN THICK LETTER SELECTOR - DUPLOYAN DOUBLE MARK */ + { RANGE_0WIDTH(0x1BCA0, 0x1BCA3) }, /* SHORTHAND FORMAT LETTER OVERLAP - SHORTHAND FORMAT UP STEP */ + { RANGE_0WIDTH(0x1CF00, 0x1CF2D) }, /* ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT - ZNAMENNY COMBINING MARK KRYZH ON LEFT */ + { RANGE_0WIDTH(0x1CF30, 0x1CF46) }, /* ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO - ZNAMENNY PRIZNAK MODIFIER ROG */ + { RANGE_0WIDTH(0x1D165, 0x1D169) }, /* MUSICAL SYMBOL COMBINING STEM - MUSICAL SYMBOL COMBINING TREMOLO-3 */ + { RANGE_0WIDTH(0x1D16D, 0x1D182) }, /* MUSICAL SYMBOL COMBINING AUGMENTATION DOT - MUSICAL SYMBOL COMBINING LOURE */ + { RANGE_0WIDTH(0x1D185, 0x1D18B) }, /* MUSICAL SYMBOL COMBINING DOIT - MUSICAL SYMBOL COMBINING TRIPLE TONGUE */ + { RANGE_0WIDTH(0x1D1AA, 0x1D1AD) }, /* MUSICAL SYMBOL COMBINING DOWN BOW - MUSICAL SYMBOL COMBINING SNAP PIZZICATO */ + { RANGE_0WIDTH(0x1D242, 0x1D244) }, /* COMBINING GREEK MUSICAL TRISEME - COMBINING GREEK MUSICAL PENTASEME */ + { RANGE_2WIDTH(0x1D300, 0x1D356) }, /* MONOGRAM FOR EARTH - TETRAGRAM FOR FOSTERING */ + { RANGE_2WIDTH(0x1D360, 0x1D376) }, /* COUNTING ROD UNIT DIGIT ONE - IDEOGRAPHIC TALLY MARK FIVE */ + { RANGE_0WIDTH(0x1DA00, 0x1DA36) }, /* SIGNWRITING HEAD RIM - SIGNWRITING AIR SUCKING IN */ + { RANGE_0WIDTH(0x1DA3B, 0x1DA6C) }, /* SIGNWRITING MOUTH CLOSED NEUTRAL - SIGNWRITING EXCITEMENT */ + { RANGE_0WIDTH(0x1DA75, 0x1DA75) }, /* SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS */ + { RANGE_0WIDTH(0x1DA84, 0x1DA84) }, /* SIGNWRITING LOCATION HEAD NECK */ + { RANGE_0WIDTH(0x1DA9B, 0x1DA9F) }, /* SIGNWRITING FILL MODIFIER-2 - SIGNWRITING FILL MODIFIER-6 */ + { RANGE_0WIDTH(0x1DAA1, 0x1DAAF) }, /* SIGNWRITING ROTATION MODIFIER-2 - SIGNWRITING ROTATION MODIFIER-16 */ + { RANGE_0WIDTH(0x1E000, 0x1E006) }, /* COMBINING GLAGOLITIC LETTER AZU - COMBINING GLAGOLITIC LETTER ZHIVETE */ + { RANGE_0WIDTH(0x1E008, 0x1E018) }, /* COMBINING GLAGOLITIC LETTER ZEMLJA - COMBINING GLAGOLITIC LETTER HERU */ + { RANGE_0WIDTH(0x1E01B, 0x1E021) }, /* COMBINING GLAGOLITIC LETTER SHTA - COMBINING GLAGOLITIC LETTER YATI */ + { RANGE_0WIDTH(0x1E023, 0x1E024) }, /* COMBINING GLAGOLITIC LETTER YU - COMBINING GLAGOLITIC LETTER SMALL YUS */ + { RANGE_0WIDTH(0x1E026, 0x1E02A) }, /* COMBINING GLAGOLITIC LETTER YO - COMBINING GLAGOLITIC LETTER FITA */ + { RANGE_0WIDTH(0x1E08F, 0x1E08F) }, /* COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { RANGE_0WIDTH(0x1E130, 0x1E136) }, /* NYIAKENG PUACHUE HMONG TONE-B - NYIAKENG PUACHUE HMONG TONE-D */ + { RANGE_0WIDTH(0x1E2AE, 0x1E2AE) }, /* TOTO SIGN RISING TONE */ + { RANGE_0WIDTH(0x1E2EC, 0x1E2EF) }, /* WANCHO TONE TUP - WANCHO TONE KOINI */ + { RANGE_0WIDTH(0x1E4EC, 0x1E4EF) }, /* NAG MUNDARI SIGN MUHOR - NAG MUNDARI SIGN SUTUH */ + { RANGE_0WIDTH(0x1E5EE, 0x1E5EF) }, /* OL ONAL SIGN MU - OL ONAL SIGN IKIR */ + { RANGE_0WIDTH(0x1E8D0, 0x1E8D6) }, /* MENDE KIKAKUI COMBINING NUMBER TEENS - MENDE KIKAKUI COMBINING NUMBER MILLIONS */ + { RANGE_0WIDTH(0x1E944, 0x1E94A) }, /* ADLAM ALIF LENGTHENER - ADLAM NUKTA */ + { RANGE_2WIDTH(0x1F000, 0x1F02F) }, /* U+1F000 - U+1F02F */ + { RANGE_2WIDTH(0x1F0A0, 0x1F0FF) }, /* U+1F0A0 - U+1F0FF */ + { RANGE_2WIDTH(0x1F18E, 0x1F18E) }, /* NEGATIVE SQUARED AB */ + { RANGE_2WIDTH(0x1F191, 0x1F19A) }, /* SQUARED CL - SQUARED VS */ + { RANGE_2WIDTH(0x1F200, 0x1F202) }, /* SQUARE HIRAGANA HOKA - SQUARED KATAKANA SA */ + { RANGE_2WIDTH(0x1F210, 0x1F23B) }, /* SQUARED CJK UNIFIED IDEOGRAPH-624B - SQUARED CJK UNIFIED IDEOGRAPH-914D */ + { RANGE_2WIDTH(0x1F240, 0x1F248) }, /* TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C - TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 */ + { RANGE_2WIDTH(0x1F250, 0x1F251) }, /* CIRCLED IDEOGRAPH ADVANTAGE - CIRCLED IDEOGRAPH ACCEPT */ + { RANGE_2WIDTH(0x1F260, 0x1F265) }, /* ROUNDED SYMBOL FOR FU - ROUNDED SYMBOL FOR CAI */ + { RANGE_2WIDTH(0x1F300, 0x1F3FA) }, /* CYCLONE - AMPHORA */ + { RANGE_0WIDTH(0x1F3FB, 0x1F3FF) }, /* EMOJI MODIFIER FITZPATRICK TYPE-1-2 - EMOJI MODIFIER FITZPATRICK TYPE-6 */ + { RANGE_2WIDTH(0x1F400, 0x1F64F) }, /* RAT - PERSON WITH FOLDED HANDS */ + { RANGE_2WIDTH(0x1F680, 0x1F9AF) }, /* ROCKET - PROBING CANE */ + { RANGE_0WIDTH(0x1F9B0, 0x1F9B3) }, /* EMOJI COMPONENT RED HAIR - EMOJI COMPONENT WHITE HAIR */ + { RANGE_2WIDTH(0x1F9B4, 0x1FAFF) }, /* U+1F9B4 - U+1FAFF */ + { RANGE_2WIDTH(0x20000, 0x2FFFD) }, /* U+20000 - U+2FFFD */ + { RANGE_2WIDTH(0x30000, 0x3FFFD) }, /* U+30000 - U+3FFFD */ + { RANGE_0WIDTH(0xE0001, 0xE0001) }, /* LANGUAGE TAG */ + { RANGE_0WIDTH(0xE0020, 0xE007F) }, /* TAG SPACE - CANCEL TAG */ + { RANGE_0WIDTH(0xE0100, 0xE01EF) }, /* VARIATION SELECTOR-17 - VARIATION SELECTOR-256 */ }; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e99636ab9db5..cf26fb8f6545 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3094,8 +3094,12 @@ static void vc_con_rewind(struct vc_data *vc) static int vc_process_ucs(struct vc_data *vc, int *c, int *tc) { u32 prev_c, curr_c = *c; + unsigned int w = ucs_get_width(curr_c); - if (ucs_is_double_width(curr_c)) { + if (likely(w == 1)) + return 1; + + if (w == 2) { /* * The Unicode screen memory is allocated only when * required. This is one such case as we need to remember @@ -3105,12 +3109,9 @@ static int vc_process_ucs(struct vc_data *vc, int *c, int *tc) return 2; } - if (!ucs_is_zero_width(curr_c)) - return 1; - /* From here curr_c is known to be zero-width. */ - if (ucs_is_double_width(vc_uniscr_getc(vc, -2))) { + if (ucs_get_width(vc_uniscr_getc(vc, -2)) == 2) { /* * Let's merge this zero-width code point with the preceding * double-width code point by replacing the existing diff --git a/include/linux/consolemap.h b/include/linux/consolemap.h index 6180b803795c..539d488fdc03 100644 --- a/include/linux/consolemap.h +++ b/include/linux/consolemap.h @@ -28,8 +28,7 @@ int conv_uni_to_pc(struct vc_data *conp, long ucs); u32 conv_8bit_to_uni(unsigned char c); int conv_uni_to_8bit(u32 uni); void console_map_init(void); -bool ucs_is_double_width(uint32_t cp); -bool ucs_is_zero_width(uint32_t cp); +unsigned int ucs_get_width(uint32_t cp); u32 ucs_recompose(u32 base, u32 mark); u32 ucs_get_fallback(u32 cp); #else @@ -62,14 +61,9 @@ static inline int conv_uni_to_8bit(u32 uni) static inline void console_map_init(void) { } -static inline bool ucs_is_double_width(uint32_t cp) +static inline unsigned int ucs_get_width(uint32_t cp) { - return false; -} - -static inline bool ucs_is_zero_width(uint32_t cp) -{ - return false; + return 1; } static inline u32 ucs_recompose(u32 base, u32 mark) From 658325c9c507ac2b8e4703afb3e6caa38474c09b Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:48 +0200 Subject: [PATCH 1563/5214] scripts/sbom: add documentation Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- Documentation/tools/index.rst | 1 + Documentation/tools/sbom/sbom.rst | 206 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 Documentation/tools/sbom/sbom.rst diff --git a/Documentation/tools/index.rst b/Documentation/tools/index.rst index 5f2f63bcb284..1adf4a6f909b 100644 --- a/Documentation/tools/index.rst +++ b/Documentation/tools/index.rst @@ -13,3 +13,4 @@ more additions are needed here: rtla/index rv/index python + sbom/sbom diff --git a/Documentation/tools/sbom/sbom.rst b/Documentation/tools/sbom/sbom.rst new file mode 100644 index 000000000000..029b08b6ad87 --- /dev/null +++ b/Documentation/tools/sbom/sbom.rst @@ -0,0 +1,206 @@ +.. SPDX-License-Identifier: GPL-2.0-only OR MIT +.. Copyright (C) 2025 TNG Technology Consulting GmbH + +KernelSbom +========== + +Introduction +------------ + +KernelSbom is a Python script ``scripts/sbom/sbom.py`` that can be +executed after a successful kernel build. When invoked, KernelSbom +analyzes all files involved in the build and generates Software Bill of +Materials (SBOM) documents in SPDX 3.0.1 format. +The generated SBOM documents capture: + +* **Final output artifacts**, typically the kernel image and modules +* **All source files** that contributed to the build with metadata + and licensing information +* **Details of the build process**, including intermediate artifacts + and the build commands linking source files to the final output + artifacts + +KernelSbom is originally developed in the +`KernelSbom repository `_. + +Requirements +------------ + +Python 3.10 or later. No libraries or other dependencies are required. + +Basic Usage +----------- + +Run the ``make sbom`` target. +For example:: + + $ make defconfig O=kernel_build + $ make sbom O=kernel_build -j$(nproc) + +This will trigger a kernel build. After all build outputs have been +generated, KernelSbom produces three SPDX documents in the root +directory of the object tree: + +* ``sbom-source.spdx.json`` + Describes all source files involved in the build and + associates each file with its corresponding license expression. + +* ``sbom-output.spdx.json`` + Captures all final build outputs (kernel image and ``.ko`` module files) + and includes build metadata such as environment variables and + a hash of the ``.config`` file used for the build. + +* ``sbom-build.spdx.json`` + Imports files from the source and output documents and describes every + intermediate build artifact. For each artifact, it records the exact + build command used and establishes the relationship between + input files and generated outputs. + +When invoking the sbom target, it is recommended to perform +out-of-tree builds using ``O=``. KernelSbom classifies files as +source files when they are located in the source tree and not in the +object tree. For in-tree builds, where the source and object trees are +the same directory, this distinction can no longer be made reliably. +In that case, KernelSbom does not generate a dedicated source SBOM. +Instead, source files are included in the build SBOM. + +Standalone Usage +---------------- + +KernelSbom can also be used as a standalone script to generate +SPDX documents for specific build outputs. For example, after a +successful x86 kernel build, KernelSbom can generate SPDX documents +for the ``bzImage`` kernel image:: + + $ SRCARCH=x86 python3 scripts/sbom/sbom.py \ + --src-tree . \ + --obj-tree ./kernel_build \ + --roots arch/x86/boot/bzImage \ + --generate-spdx \ + --generate-used-files \ + --prettify-json \ + --debug + +Note that when KernelSbom is invoked outside of the ``make`` process, +the environment variables used during compilation are not available and +therefore cannot be included in the generated SPDX documents. It is +recommended to set at least the ``SRCARCH`` environment variable to the +architecture for which the build was performed. + +For a full list of command-line options, run:: + + $ python3 scripts/sbom/sbom.py --help + +Output Format +------------- + +KernelSbom generates documents conforming to the +`SPDX 3.0.1 specification `_ +serialized as JSON-LD. + +To reduce file size, the output documents use the JSON-LD ``@context`` +to define custom prefixes for ``spdxId`` values. While this is compliant +with the SPDX specification, only a limited number of tools in the +current SPDX ecosystem support custom JSON-LD contexts. To use such +tools with the generated documents, the custom JSON-LD context must +be expanded before providing the documents. +See https://lists.spdx.org/g/Spdx-tech/message/6064 for more information. + +How it Works +------------ + +KernelSbom operates in two major phases: + +1. **Generate the cmd graph**, an acyclic directed dependency graph. +2. **Generate SPDX documents** based on the cmd graph. + +KernelSbom begins from the root artifacts specified by the user, e.g., +``arch/x86/boot/bzImage``. For each root artifact, it collects all +dependencies required to build that artifact. The dependencies come +from multiple sources: + +* **.cmd files**: The primary source is the ``.cmd`` file of the + generated artifact, e.g., ``arch/x86/boot/.bzImage.cmd``. These files + contain the exact command used to build the artifact and often include + an explicit list of input dependencies. By parsing the ``.cmd`` + file, the full list of dependencies can be obtained. + +* **.incbin statements**: The second source are include binary + ``.incbin`` statements in ``.S`` assembly files. + +* **Hardcoded dependencies**: Unfortunately, not all build dependencies + can be found via ``.cmd`` files and ``.incbin`` statements. Some build + dependencies are directly defined in Makefiles or Kbuild files. + Parsing these files is considered too complex for the scope of this + project. Instead, the remaining gaps of the graph are filled using a + list of manually defined dependencies, see + ``scripts/sbom/sbom/cmd_graph/hardcoded_dependencies.py``. This list is + known to be incomplete. However, analysis of the cmd graph indicates a + ~99% completeness. For more information about the completeness analysis, + see `KernelSbom #95 `_. + +Given the list of dependency files, KernelSbom recursively processes +each file, expanding the dependency chain all the way to the version +controlled source files. The result is a complete dependency graph +where nodes represent files, and edges represent "file A was used to +build file B" relationships. + +Using the cmd graph, KernelSbom produces three SPDX documents. +For every file in the graph, KernelSbom: + +* Parses ``SPDX-License-Identifier`` headers, +* Computes file hashes, +* Estimates the file type based on extension and path, +* Records build relationships between files. + +Each root output file is additionally associated with an SPDX Package +element that captures version information, license data, and copyright. + +Advanced Usage +-------------- + +Including Kernel Modules +~~~~~~~~~~~~~~~~~~~~~~~~ + +The list of all ``.ko`` kernel modules produced during a build can be +extracted from the ``modules.order`` file within the object tree. +For example:: + + $ echo "arch/x86/boot/bzImage" > sbom-roots.txt + $ sed 's/\.o$/.ko/' ./kernel_build/modules.order >> sbom-roots.txt + +Then use the generated roots file:: + + $ SRCARCH=x86 python3 scripts/sbom/sbom.py \ + --src-tree . \ + --obj-tree ./kernel_build \ + --roots-file sbom-roots.txt \ + --generate-spdx + +Equal Source and Object Trees +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the source tree and object tree are identical (for example, when +building in-tree), source files can no longer be reliably distinguished +from generated files. +In this scenario, KernelSbom does not produce a dedicated +``sbom-source.spdx.json`` document. Instead, both source files and build +artifacts are included together in ``sbom-build.spdx.json``, and +``sbom.used-files.txt`` lists all files referenced in the build document. + +Unknown Build Commands +~~~~~~~~~~~~~~~~~~~~~~ + +Because the kernel supports a wide range of configurations and versions, +KernelSbom may encounter build commands in ``.cmd`` files that it does +not yet support. By default, KernelSbom will fail if an unknown build +command is encountered. + +If you still wish to generate SPDX documents despite unsupported +commands, you can use the ``--do-not-fail-on-unknown-build-command`` +option. KernelSbom will continue and produce the documents, although +the resulting SBOM will be incomplete. + +This option should only be used when the missing portion of the +dependency graph is small and an incomplete SBOM is acceptable for +your use case. From e72b635ceaf7e8d5ad757169d5950c43adeb5261 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:49 +0200 Subject: [PATCH 1564/5214] scripts/sbom: integrate script in make process integrate SBOM script into the kernel build process. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Acked-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- .gitignore | 1 + MAINTAINERS | 6 ++++++ Makefile | 20 ++++++++++++++++++-- scripts/sbom/sbom.py | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 scripts/sbom/sbom.py diff --git a/.gitignore b/.gitignore index 3044b9590f05..f0d35a9d591d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ *.s *.so *.so.dbg +*.spdx.json *.su *.symtypes *.tab.[ch] diff --git a/MAINTAINERS b/MAINTAINERS index c2c6d79275c6..36dac854a21d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23903,6 +23903,12 @@ R: Marc Murphy S: Supported F: arch/arm/boot/dts/ti/omap/am335x-sancloud* +SBOM +M: Luis Augenstein +M: Maximilian Huber +S: Maintained +F: scripts/sbom/ + SC1200 WDT DRIVER M: Zwane Mwaikambo S: Maintained diff --git a/Makefile b/Makefile index 9f59598d3a08..ec54f7d51cf4 100644 --- a/Makefile +++ b/Makefile @@ -787,7 +787,7 @@ endif # in addition to whatever we do anyway. # Just "make" or "make all" shall build modules as well -ifneq ($(filter all modules nsdeps compile_commands.json clang-%,$(MAKECMDGOALS)),) +ifneq ($(filter all modules nsdeps compile_commands.json clang-% sbom,$(MAKECMDGOALS)),) KBUILD_MODULES := y endif @@ -1692,7 +1692,7 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \ compile_commands.json rust/test \ rust-project.json .vmlinux.objs .vmlinux.export.c \ - .builtin-dtbs-list .builtin-dtbs.S + .builtin-dtbs-list .builtin-dtbs.S sbom-*.spdx.json # Directories & files removed with 'make mrproper' MRPROPER_FILES += include/config include/generated \ @@ -1811,6 +1811,7 @@ help: @echo '' @echo 'Tools:' @echo ' nsdeps - Generate missing symbol namespace dependencies' + @echo ' sbom - Generate Software Bill of Materials' @echo '' @echo 'Kernel selftest:' @echo ' kselftest - Build and run kernel selftest' @@ -2197,6 +2198,21 @@ nsdeps: export KBUILD_NSDEPS=1 nsdeps: modules $(Q)$(CONFIG_SHELL) $(srctree)/scripts/nsdeps +# Script to generate .spdx.json SBOM documents describing the build +# --------------------------------------------------------------------------- + +ifdef building_out_of_srctree +sbom_targets := sbom-source.spdx.json +endif +sbom_targets += sbom-build.spdx.json sbom-output.spdx.json +quiet_cmd_sbom = GEN $(sbom_targets) + cmd_sbom = printf "%s\n" "$(KBUILD_IMAGE)" >"$(tmp-target)"; \ + $(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' $(objtree)/modules.order >> "$(tmp-target)";) \ + $(PYTHON3) $(srctree)/scripts/sbom/sbom.py; +PHONY += sbom +sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules modules.order) + $(call cmd,sbom) + # Clang Tooling # --------------------------------------------------------------------------- diff --git a/scripts/sbom/sbom.py b/scripts/sbom/sbom.py new file mode 100644 index 000000000000..9c2e4c7f17ce --- /dev/null +++ b/scripts/sbom/sbom.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +""" +Compute software bill of materials in SPDX format describing a kernel build. +""" + + +def main(): + pass + + +# Call main method +if __name__ == "__main__": + main() From 3fd79200835f382261a7b53fed659625b22484af Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:50 +0200 Subject: [PATCH 1565/5214] scripts/sbom: setup sbom logging Add logging infrastructure for warnings and errors. Errors and warnings are accumulated and summarized in the end. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/sbom.py | 26 ++++++++- scripts/sbom/sbom/__init__.py | 0 scripts/sbom/sbom/config.py | 46 +++++++++++++++ scripts/sbom/sbom/sbom_logging.py | 94 +++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 scripts/sbom/sbom/__init__.py create mode 100644 scripts/sbom/sbom/config.py create mode 100644 scripts/sbom/sbom/sbom_logging.py diff --git a/scripts/sbom/sbom.py b/scripts/sbom/sbom.py index 9c2e4c7f17ce..3bd466720b0d 100644 --- a/scripts/sbom/sbom.py +++ b/scripts/sbom/sbom.py @@ -6,9 +6,33 @@ Compute software bill of materials in SPDX format describing a kernel build. """ +import logging +import sys +import sbom.sbom_logging as sbom_logging +from sbom.config import get_config + + +def _exit_with_summary(write_output_on_error: bool = False) -> None: + warning_summary = sbom_logging.summarize_warnings() + error_summary = sbom_logging.summarize_errors() + if warning_summary: + logging.warning(warning_summary) + if error_summary: + logging.error(error_summary) + sys.exit(1) + def main(): - pass + # Read config + config = get_config() + + # Configure logging + logging.basicConfig( + level=logging.DEBUG if config.debug else logging.INFO, + format="[%(levelname)s] %(message)s", + ) + + _exit_with_summary(config.write_output_on_error) # Call main method diff --git a/scripts/sbom/sbom/__init__.py b/scripts/sbom/sbom/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/sbom/sbom/config.py b/scripts/sbom/sbom/config.py new file mode 100644 index 000000000000..c1ac9ad5737f --- /dev/null +++ b/scripts/sbom/sbom/config.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import argparse +from dataclasses import dataclass + + +@dataclass +class KernelSbomConfig: + debug: bool + """Whether to enable debug logging.""" + + +def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, bool]: + """ + Parse command-line arguments using argparse. + + Returns: + Dictionary of parsed arguments. + """ + parser.add_argument( + "--debug", + action="store_true", + default=False, + help="Enable debug logs (default: False)", + ) + + args = vars(parser.parse_args()) + return args + + +def get_config() -> KernelSbomConfig: + """ + Parse command-line arguments and construct the configuration object. + + Returns: + KernelSbomConfig: Configuration object with all settings for SBOM generation. + """ + parser = argparse.ArgumentParser( + description="Generate SPDX SBOM documents for kernel builds", + ) + args = _parse_cli_arguments(parser) + + debug = args["debug"] + + return KernelSbomConfig(debug=debug) diff --git a/scripts/sbom/sbom/sbom_logging.py b/scripts/sbom/sbom/sbom_logging.py new file mode 100644 index 000000000000..fbc53cc77ef4 --- /dev/null +++ b/scripts/sbom/sbom/sbom_logging.py @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import logging +import inspect +from typing import Literal + + +MessageTemplate = str + + +class MessageLogger: + """Logger that suppresses repeated messages and stores a summary of all logged messages.""" + + _messages: dict[MessageTemplate, list[str]] + _message_counts: dict[MessageTemplate, int] + _repeated_logs_limit: int + """Maximum number of repeated messages of the same type to log before suppressing further output.""" + + def __init__(self, level: Literal["error", "warning"], repeated_logs_limit: int = 3) -> None: + self._level = level + self._messages = {} + self._message_counts = {} + self._repeated_logs_limit = repeated_logs_limit + + def log(self, template: MessageTemplate, /, **kwargs: str) -> None: + """Log a message based on a template and optional variables. Example: `log("Missing {path}", path=str(p))`.""" + message = template + for key, value in kwargs.items(): + message = message.replace("{" + key + "}", value) + if template not in self._messages: + self._messages[template] = [] + self._message_counts[template] = 0 + self._message_counts[template] += 1 + if self._message_counts[template] <= self._repeated_logs_limit: + if self._level == "error": + logging.error(message) + elif self._level == "warning": + logging.warning(message) + self._messages[template].append(message) + + def get_summary(self) -> str: + if len(self._messages) == 0: + return "" + summary: list[str] = [f"Summarize {self._level}s:"] + for template, messages in self._messages.items(): + for message in messages: + summary.append(message) + n_suppressed_messages = self._message_counts[template] - self._repeated_logs_limit + if n_suppressed_messages > 0: + instances = "instance" if n_suppressed_messages == 1 else "instances" + summary.append(f"... (Found {n_suppressed_messages} more {instances} of this {self._level})") + return "\n".join(summary) + + def has_messages(self) -> bool: + return len(self._message_counts) > 0 + + +_warning_logger: MessageLogger +_error_logger: MessageLogger + + +def warning(msg_template: MessageTemplate, /, **kwargs: str) -> None: + _warning_logger.log(msg_template, **kwargs) + + +def error(msg_template: MessageTemplate, /, **kwargs: str) -> None: + frame = inspect.currentframe() + caller_frame = frame.f_back if frame else None + info = inspect.getframeinfo(caller_frame) if caller_frame else None + if info: + msg_template = f'File "{info.filename}", line {info.lineno}, in {info.function}\n{msg_template}' + _error_logger.log(msg_template, **kwargs) + + +def summarize_warnings() -> str: + return _warning_logger.get_summary() + + +def summarize_errors() -> str: + return _error_logger.get_summary() + + +def has_errors() -> bool: + return _error_logger.has_messages() + + +def init() -> None: + global _warning_logger, _error_logger + _warning_logger = MessageLogger("warning") + _error_logger = MessageLogger("error") + + +init() From a1a248adf1b0d79e9386d007cbcd4be85d643f03 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:51 +0200 Subject: [PATCH 1566/5214] scripts/sbom: add command parsers Implement savedcmd_parser module for extracting input files from kernel build commands. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- .../cmd_graph/savedcmd_parser/__init__.py | 6 + .../command_parser_registry.py | 516 ++++++++++++++++++ .../savedcmd_parser/command_splitter.py | 128 +++++ .../savedcmd_parser/savedcmd_parser.py | 67 +++ .../cmd_graph/savedcmd_parser/tokenizer.py | 92 ++++ scripts/sbom/sbom/environment.py | 192 +++++++ 6 files changed, 1001 insertions(+) create mode 100644 scripts/sbom/sbom/cmd_graph/savedcmd_parser/__init__.py create mode 100644 scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_parser_registry.py create mode 100644 scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_splitter.py create mode 100644 scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py create mode 100644 scripts/sbom/sbom/cmd_graph/savedcmd_parser/tokenizer.py create mode 100644 scripts/sbom/sbom/environment.py diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/__init__.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/__init__.py new file mode 100644 index 000000000000..d13876af4dfd --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from sbom.cmd_graph.savedcmd_parser.savedcmd_parser import parse_inputs_from_commands + +__all__ = ["parse_inputs_from_commands"] diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_parser_registry.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_parser_registry.py new file mode 100644 index 000000000000..a48040b2c13c --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_parser_registry.py @@ -0,0 +1,516 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import re +import shlex +from typing import Callable, Iterator + +import sbom.sbom_logging as sbom_logging +from sbom.environment import Environment +from sbom.cmd_graph.savedcmd_parser.command_splitter import IfBlock, split_commands +from sbom.cmd_graph.savedcmd_parser.tokenizer import ( + CmdParsingError, + Option, + Positional, + tokenize_single_command, + tokenize_single_command_positionals_only, +) +from sbom.path_utils import PathStr + +CommandParser = Callable[[str], list[PathStr]] +CommandParserRegistryEntry = tuple[re.Pattern[str], CommandParser] + + +def _parse_dd_command(command: str) -> list[PathStr]: + match = re.match(r"dd.*?if=(\S+)", command) + if match: + return [match.group(1)] + return [] + + +def _parse_cat_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["cat", input1, input2, ...] + return [p for p in positionals[1:]] + + +def _parse_compound_command(command: str) -> list[PathStr]: + compound_command_parsers: list[CommandParserRegistryEntry] = [ + (re.compile(r"dd\b"), _parse_dd_command), + (re.compile(r"cat.*?\|"), lambda c: _parse_cat_command(c.split("|")[0])), + (re.compile(r"cat\b[^|>]*$"), _parse_cat_command), + (re.compile(r"echo\b"), _parse_noop), + (re.compile(r"\S+="), _parse_noop), + (re.compile(r"printf\b"), _parse_noop), + (re.compile(r"sed\b"), _parse_sed_command), + ( + re.compile(r"(.*/)scripts/bin2c\s*<"), + lambda c: [input] if (input := c.split("<")[1].split(">")[0].strip()) != "/dev/null" else [], + ), + (re.compile(r"^:$"), _parse_noop), + ] + + match = re.match(r"\s*[\(\{](.*)[\)\}]\s*>", command, re.DOTALL) + if match is None: + raise CmdParsingError("No inner commands found for compound command") + input_files: list[PathStr] = [] + inner_commands = split_commands(match.group(1)) + for inner_command in inner_commands: + if isinstance(inner_command, IfBlock): + sbom_logging.error( + "Skip parsing inner command {inner_command} of compound command because IfBlock is not supported", + inner_command=inner_command, + ) + continue + + parser = next((parser for pattern, parser in compound_command_parsers if pattern.match(inner_command)), None) + if parser is None: + sbom_logging.error( + "Skip parsing inner command {inner_command} of compound command because no matching parser was found", + inner_command=inner_command, + ) + continue + try: + input_files += parser(inner_command) + except (CmdParsingError, IndexError) as e: + sbom_logging.error( + "Skip parsing inner command {inner_command} of compound command because of command parsing error: {error_message}", + inner_command=inner_command, + error_message=str(e), + ) + return input_files + + +def _parse_objcopy_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command, flag_options=["-S", "-w"]) + positionals = [part.value for part in command_parts if isinstance(part, Positional)] + # expect positionals to be ['objcopy', input_file] or ['objcopy', input_file, output_file] + return [positionals[1]] + + +def _parse_link_vmlinux_command(command: str) -> list[PathStr]: + """ + For simplicity we do not parse the `scripts/link-vmlinux.sh` script. + Instead the `vmlinux.a` dependency is just hardcoded for now. + """ + return ["vmlinux.a"] + + +def _parse_cp_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["cp", input1, ..., destination] + return positionals[1:-1] + + +def _parse_noop(command: str) -> list[PathStr]: + """ + No-op parser for commands with no input files (e.g., 'rm', 'true'). + Returns an empty list. + """ + return [] + + +def _parse_ar_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ['ar', flags, output, input1, input2, ...] + flags = positionals[1] + if "r" not in flags: + # 'r' option indicates that new files are added to the archive. + # If this option is missing we won't find any relevant input files. + return [] + return positionals[3:] + + +def _parse_ar_piped_xargs_command(command: str) -> list[PathStr]: + printf_command, _ = command.split("|", 1) + positionals = tokenize_single_command_positionals_only(printf_command.strip()) + # expect positionals to be ['printf', '{prefix_path}%s ', input1, input2, ...] + prefix_path = positionals[1].removesuffix("%s ") + return [f"{prefix_path}{filename}" for filename in positionals[2:]] + + +def _parse_gcc_or_clang_command(command: str) -> list[PathStr]: + parts = shlex.split(command) + # compile mode: expect last positional argument ending in a source file extension to be the input file + for part in reversed(parts): + if not part.startswith("-") and any(part.endswith(suffix) for suffix in [".c", ".S", ".dts"]): + return [part] + + # linking mode: expect all .o files to be the inputs + return [p for p in parts if p.endswith(".o")] + + +def _parse_rustc_command(command: str) -> list[PathStr]: + parts = shlex.split(command) + # expect last positional argument ending in `.rs` to be the input file + for part in reversed(parts): + if not part.startswith("-") and part.endswith(".rs"): + return [part] + raise CmdParsingError("Could not find .rs input source file") + + +def _parse_rustdoc_command(command: str) -> list[PathStr]: + parts = shlex.split(command) + # expect last positional argument ending in `.rs` to be the input file + for part in reversed(parts): + if not part.startswith("-") and part.endswith(".rs"): + return [part] + raise CmdParsingError("Could not find .rs input source file") + + +def _parse_syscallhdr_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command.strip(), flag_options=["--emit-nr"]) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["sh", path/to/syscallhdr.sh, input, output] + return [positionals[2]] + + +def _parse_syscalltbl_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command.strip()) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["sh", path/to/syscalltbl.sh, input, output] + return [positionals[2]] + + +def _parse_mkcapflags_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["sh", path/to/mkcapflags.sh, output, input1, input2] + return [positionals[3], positionals[4]] + + +def _parse_orc_hash_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["sh", path/to/orc_hash.sh, '<', input, '>', output] + return [positionals[3]] + + +def _parse_xen_hypercalls_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["sh", path/to/xen-hypercalls.sh, output, input1, input2, ...] + return positionals[3:] + + +def _parse_gen_initramfs_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["sh", path/to/gen_initramfs.sh, input1, input2, ...] + return positionals[2:] + + +def _parse_vdso2c_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ['vdso2c', raw_input, stripped_input, output] + return [positionals[1], positionals[2]] + + +def _parse_vdsomunge_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ['vdsomunge', input, output] + return [positionals[1]] + + +def _parse_ld_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command( + command=command.strip(), + flag_options=[ + "-shared", + "--no-undefined", + "--eh-frame-hdr", + "-Bsymbolic", + "-r", + "--no-ld-generated-unwind-info", + "--no-dynamic-linker", + "-pie", + "--no-dynamic-linker--whole-archive", + "--whole-archive", + "--no-whole-archive", + "--start-group", + "--end-group", + ], + ) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["ld", input1, input2, ...] + return positionals[1:] + + +def _parse_sed_command(command: str) -> list[PathStr]: + command_parts = shlex.split(command) + # expect command parts to be ["sed", *, input] + input = command_parts[-1] + if input == "/dev/null": + return [] + return [input] + + +def _parse_awk(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command) + options = [p for p in command_parts if isinstance(p, Option)] + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + has_script_file = any(p.name == "-f" for p in options) + # With -f option: expect ["awk", input1, input2, ...] + # Without -f option: expect ["awk", inline_program, input1, input2, ...] + return positionals[1:] if has_script_file else positionals[2:] + + +def _parse_nm_piped_command(command: str) -> list[PathStr]: + nm_command, _ = command.split("|", 1) + command_parts = tokenize_single_command( + command=nm_command.strip(), + flag_options=["-p", "--defined-only"], + ) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["nm", input1, input2, ...] + return [p for p in positionals[1:]] + + +def _parse_pnm_to_logo_command(command: str) -> list[PathStr]: + command_parts = shlex.split(command) + # expect command parts to be ["pnmtologo", , input] + return [command_parts[-1]] + + +def _parse_relacheck(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["relacheck", input, log_reference] + return [positionals[1]] + + +def _parse_gen_hyprel_command(command: str) -> list[PathStr]: + gen_hyprel_command, _ = command.split(">", 1) + command_parts = shlex.split(gen_hyprel_command) + # expect command_parts to be ["gen-hyprel", input] + return [command_parts[1]] + + +def _parse_perl_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command.strip()) + # expect positionals to be ["perl", input] + return [positionals[1]] + + +def _parse_strip_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command, flag_options=["--strip-debug"]) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["strip", input1, input2, ...] + return positionals[1:] + + +def _parse_mkpiggy_command(command: str) -> list[PathStr]: + mkpiggy_command, _ = command.split(">", 1) + positionals = tokenize_single_command_positionals_only(mkpiggy_command) + # expect positionals to be ["mkpiggy", input] + return [positionals[1]] + + +def _parse_relocs_command(command: str) -> list[PathStr]: + if ">" not in command: + # Only consider relocs commands that redirect output to a file. + # If there's no redirection, we assume it produces no output file and therefore has no input we care about. + return [] + relocs_command, _ = command.split(">", 1) + command_parts = shlex.split(relocs_command) + # expect command_parts to be ["relocs", options, input] + return [command_parts[-1]] + + +def _parse_mk_elfconfig_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["mk_elfconfig", "<", input, ">", output] + return [positionals[2]] + + +def _parse_flex_command(command: str) -> list[PathStr]: + parts = shlex.split(command) + # expect last positional argument ending in `.l` to be the input file + for part in reversed(parts): + if not part.startswith("-") and part.endswith(".l"): + return [part] + raise CmdParsingError("Could not find .l input source file in command") + + +def _parse_bison_command(command: str) -> list[PathStr]: + parts = shlex.split(command) + # expect last positional argument ending in `.y` to be the input file + for part in reversed(parts): + if not part.startswith("-") and part.endswith(".y"): + return [part] + raise CmdParsingError("Could not find input .y input source file in command") + + +def _parse_tools_build_command(command: str) -> list[PathStr]: + positionals = tokenize_single_command_positionals_only(command) + # expect positionals to be ["tools/build", "input1", "input2", "input3", "output"] + return positionals[1:-1] + + +def _parse_extract_cert_command(command: str) -> list[PathStr]: + command_parts = shlex.split(command) + # expect command parts to be [path/to/extract-cert, input, output] + input = command_parts[1] + if not input: + return [] + return [input] + + +def _parse_dtc_command(command: str) -> list[PathStr]: + wno_flags = [command_part for command_part in shlex.split(command) if command_part.startswith("-Wno-")] + command_parts = tokenize_single_command(command, flag_options=wno_flags) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be [path/to/dtc, input] + return [positionals[1]] + + +def _parse_bindgen_command(command: str) -> list[PathStr]: + command_parts = shlex.split(command) + header_file_input_paths = [part for part in command_parts if part.endswith(".h")] + return header_file_input_paths + + +def _parse_gen_header(command: str) -> list[PathStr]: + command_parts = shlex.split(command) + # expect command parts to be ["python3", path/to/gen_headers.py, ..., "--xml", input] + i = next((i for i, token in enumerate(command_parts) if token == "--xml"), None) + if i is None: + raise CmdParsingError(f"Expected --xml input file in gen_headers command but got {command}") + return [command_parts[i + 1]] + +def _parse_mkuboot_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command) + # mkuboot.sh passes all args to mkimage; -d specifies the data/input image file + for part in command_parts: + if isinstance(part, Option) and part.name == "-d" and part.value is not None: + return [part.value] + raise CmdParsingError("Could not find -d (data file) option in mkuboot.sh command") + + +def _parse_syscallnr_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command.strip()) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["sh", path/to/syscallnr.sh, input, output] + return [positionals[2]] + + +def _parse_gen_kernel_hwcaps_command(command: str) -> list[PathStr]: + command_parts = tokenize_single_command(command.strip(), flag_options=["-e"]) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + # expect positionals to be ["sh", path/to/gen-kernel-hwcaps.sh, input] + return [positionals[2]] + + +class CommandParserRegistry: + """ + Registry mapping command patterns to their input-file parsers. + """ + + def __init__(self, entries: list[CommandParserRegistryEntry]) -> None: + self._entries = entries + + def __iter__(self) -> Iterator[CommandParserRegistryEntry]: + return iter(self._entries) + + @staticmethod + def create() -> "CommandParserRegistry": + def env_or_default_pattern(env_value: str | None, default_pattern: str) -> str: + if env_value is None or not env_value.strip(): + return default_pattern + return rf"(?:{re.escape(env_value.strip())}|{default_pattern})" + + cc_pattern = env_or_default_pattern(Environment.CC(), r"([^\s]+-)?(gcc|clang)") + ld_pattern = env_or_default_pattern(Environment.LD(), r"([^\s]+-)?ld") + ar_pattern = env_or_default_pattern(Environment.AR(), r"([^\s]+-)?ar") + nm_pattern = env_or_default_pattern(Environment.NM(), r"([^\s]+-)?nm") + objcopy_pattern = env_or_default_pattern(Environment.OBJCOPY(), r"([^\s]+-)?objcopy") + strip_pattern = env_or_default_pattern(Environment.STRIP(), r"([^\s]+-)?strip") + + entries: list[CommandParserRegistryEntry] = [ + # Compound commands + (re.compile(r"\(.*?\)\s*>", re.DOTALL), _parse_compound_command), + (re.compile(r"\{.*?\}\s*>", re.DOTALL), _parse_compound_command), + # Standard Unix utilities and system tools + (re.compile(r"^rm\b"), _parse_noop), + (re.compile(r"^mkdir\b"), _parse_noop), + (re.compile(r"^touch\b"), _parse_noop), + (re.compile(r"^cp\b"), _parse_cp_command), + (re.compile(r"^truncate\b"), _parse_noop), + (re.compile(r"^cat\b.*?[\|>]"), lambda c: _parse_cat_command(c.split("|")[0].split(">")[0])), + (re.compile(r"^echo[^|]*$"), _parse_noop), + (re.compile(r"^sed.*?>"), lambda c: _parse_sed_command(c.split(">")[0])), + (re.compile(r"^sed\b"), _parse_noop), + (re.compile(r"^awk.*?<.*?>"), lambda c: [c.split("<")[1].split(">")[0]]), + (re.compile(r"^awk.*?>"), lambda c: _parse_awk(c.split(">")[0])), + (re.compile(r"^(/bin/)?true\b"), _parse_noop), + (re.compile(r"^(/bin/)?false\b"), _parse_noop), + (re.compile(r"^openssl\s+req.*?-new.*?-keyout"), _parse_noop), + # Compilers and code generators + # (C/LLVM toolchain, Rust, Flex/Bison, Bindgen, Perl, etc.) + ( + re.compile(rf"^{cc_pattern}\b"), + lambda command: _parse_gcc_or_clang_command(re.sub(rf"^{cc_pattern}\b", "gcc", command, count=1)), + ), + ( + re.compile(rf"^{ld_pattern}\b"), + lambda command: _parse_ld_command(re.sub(rf"^{ld_pattern}\b", "ld", command, count=1)), + ), + ( + re.compile(rf"^printf\b.*\| xargs {ar_pattern}\b"), + lambda command: _parse_ar_piped_xargs_command( + re.sub(rf"xargs {ar_pattern}\b", "xargs ar", command, count=1) + ), + ), + ( + re.compile(rf"^{ar_pattern}\b"), + lambda command: _parse_ar_command(re.sub(rf"^{ar_pattern}\b", "ar", command, count=1)), + ), + ( + re.compile(rf"^{nm_pattern}\b.*?\|"), + lambda command: _parse_nm_piped_command(re.sub(rf"^{nm_pattern}\b", "nm", command, count=1)), + ), + ( + re.compile(rf"^{objcopy_pattern}\b"), + lambda command: _parse_objcopy_command(re.sub(rf"^{objcopy_pattern}\b", "objcopy", command, count=1)), + ), + ( + re.compile(rf"^{strip_pattern}\b"), + lambda command: _parse_strip_command(re.sub(rf"^{strip_pattern}\b", "strip", command, count=1)), + ), + (re.compile(r".*?rustc\b"), _parse_rustc_command), + (re.compile(r".*?rustdoc\b"), _parse_rustdoc_command), + (re.compile(r"^flex\b"), _parse_flex_command), + (re.compile(r"^bison\b"), _parse_bison_command), + (re.compile(r"^bindgen\b"), _parse_bindgen_command), + (re.compile(r"^perl\b"), _parse_perl_command), + # Kernel-specific build scripts and tools + (re.compile(r"^(.*/)?link-vmlinux\.sh\b"), _parse_link_vmlinux_command), + (re.compile(r"sh (.*/)?syscallhdr\.sh\b"), _parse_syscallhdr_command), + (re.compile(r"sh (.*/)?syscalltbl\.sh\b"), _parse_syscalltbl_command), + (re.compile(r"sh (.*/)?mkcapflags\.sh\b"), _parse_mkcapflags_command), + (re.compile(r"sh (.*/)?orc_hash\.sh\b"), _parse_orc_hash_command), + (re.compile(r"sh (.*/)?xen-hypercalls\.sh\b"), _parse_xen_hypercalls_command), + (re.compile(r"sh (.*/)?gen_initramfs\.sh\b"), _parse_gen_initramfs_command), + (re.compile(r"sh (.*/)?checkundef\.sh\b"), _parse_noop), + (re.compile(r"(bash|sh) (.*/)?mkuboot\.sh\b"), _parse_mkuboot_command), + (re.compile(r"sh (.*/)?syscallnr\.sh\b"), _parse_syscallnr_command), + (re.compile(r"(/bin/)?sh (.*/)?gen-kernel-hwcaps\.sh\b"), lambda c: _parse_gen_kernel_hwcaps_command(c.split(">")[0])), + (re.compile(r"(.*/)?vdso2c\b"), _parse_vdso2c_command), + (re.compile(r"(.*/)?vdsomunge\b"), _parse_vdsomunge_command), + (re.compile(r"^(.*/)?mkpiggy.*?>"), _parse_mkpiggy_command), + (re.compile(r"^(.*/)?relocs\b"), _parse_relocs_command), + (re.compile(r"^(.*/)?mk_elfconfig.*?<.*?>"), _parse_mk_elfconfig_command), + (re.compile(r"^(.*/)?tools/build\b"), _parse_tools_build_command), + (re.compile(r"^(.*/)?certs/extract-cert"), _parse_extract_cert_command), + (re.compile(r"^(.*/)?scripts/dtc/dtc\b"), _parse_dtc_command), + (re.compile(r"^(.*/)?pnmtologo\b"), _parse_pnm_to_logo_command), + (re.compile(r"^(.*/)?kernel/pi/relacheck"), _parse_relacheck), + (re.compile(r"^(.*/)?gen-hyprel\b"), _parse_gen_hyprel_command), + (re.compile(r"^drivers/gpu/drm/radeon/mkregtable"), lambda c: [c.split(" ")[1]]), + (re.compile(r"(.*/)?genheaders\b"), _parse_noop), + (re.compile(r"^(.*/)?mkcpustr\s+>"), _parse_noop), + (re.compile(r"^(.*/)polgen\b"), _parse_noop), + (re.compile(r"make -f .*/arch/x86/Makefile\.postlink"), _parse_noop), + (re.compile(r"^(.*/)?raid6/mktables\s+>"), _parse_noop), + (re.compile(r"^(.*/)?objtool\b"), _parse_noop), + (re.compile(r"^(.*/)?module/gen_test_kallsyms.sh"), _parse_noop), + (re.compile(r"^(.*/)?gen_header.py"), _parse_gen_header), + (re.compile(r"^(.*/)?scripts/rustdoc_test_gen"), _parse_noop), + ] + return CommandParserRegistry(entries) diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_splitter.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_splitter.py new file mode 100644 index 000000000000..4749f4bd669e --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/command_splitter.py @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import re +from dataclasses import dataclass + + +# If Block pattern to match a simple, single-level if-then-fi block. Nested If blocks are not supported. +IF_BLOCK_PATTERN = re.compile( + r""" + ^if(.*?);\s* # Match 'if ;' (non-greedy) + then(.*?);\s* # Match 'then ;' (non-greedy) + fi\b # Match 'fi' + """, + re.VERBOSE, +) + + +@dataclass +class IfBlock: + condition: str + then_statement: str + + +def _unwrap_outer_parentheses(s: str) -> str: + s = s.strip() + if not (s.startswith("(") and s.endswith(")")): + return s + + count = 0 + for i, char in enumerate(s): + if char == "(": + count += 1 + elif char == ")": + count -= 1 + # If count is 0 before the end, outer parentheses don't match + if count == 0 and i != len(s) - 1: + return s + + # outer parentheses do match, unwrap once + return _unwrap_outer_parentheses(s[1:-1]) + + +def _find_first_top_level_command_separator( + commands: str, separators: list[str] = [";", "&&"] +) -> tuple[int | None, int | None]: + def is_escaped(index: int) -> bool: + preceding = commands[:index] + return (len(preceding) - len(preceding.rstrip("\\"))) % 2 == 1 + + in_single_quote = False + in_double_quote = False + in_curly_braces = 0 + in_braces = 0 + for i, char in enumerate(commands): + if char == "'" and not in_double_quote and not is_escaped(i): + # Toggle single quote state (unless inside double quotes or escaped) + in_single_quote = not in_single_quote + elif char == '"' and not in_single_quote and not is_escaped(i): + # Toggle double quote state (unless inside single quotes or escaped) + in_double_quote = not in_double_quote + + if in_single_quote or in_double_quote: + continue + + # Toggle braces state + if char == "{": + in_curly_braces += 1 + if char == "}": + in_curly_braces -= 1 + + if char == "(": + in_braces += 1 + if char == ")": + in_braces -= 1 + + if in_curly_braces > 0 or in_braces > 0: + continue + + # return found separator position and separator length + for separator in separators: + if commands[i : i + len(separator)] == separator: + return i, len(separator) + + return None, None + + +def split_commands(commands: str) -> list[str | IfBlock]: + """ + Splits a string of command-line commands into individual parts. + + This function handles: + - Top-level command separators (e.g., `;` and `&&`) to split multiple commands. + - Conditional if-blocks, returning them as `IfBlock` instances. + - Preserves the order of commands and trims whitespace. + + Args: + commands (str): The raw command string. + + Returns: + list[str | IfBlock]: A list of single commands or `IfBlock` objects. + """ + single_commands: list[str | IfBlock] = [] + remaining_commands = _unwrap_outer_parentheses(commands) + while len(remaining_commands) > 0: + remaining_commands = remaining_commands.strip() + + # if block + matched_if = IF_BLOCK_PATTERN.match(remaining_commands) + if matched_if: + condition, then_statement = matched_if.groups() + single_commands.append(IfBlock(condition.strip(), then_statement.strip())) + full_matched = matched_if.group(0) + remaining_commands = remaining_commands.removeprefix(full_matched).lstrip("; \n") + continue + + # command until next separator + separator_position, separator_length = _find_first_top_level_command_separator(remaining_commands) + if separator_position is not None and separator_length is not None: + single_commands.append(remaining_commands[:separator_position].strip()) + remaining_commands = remaining_commands[separator_position + separator_length :].strip() + continue + + # single last command + single_commands.append(remaining_commands) + break + + return single_commands diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py new file mode 100644 index 000000000000..6a7ea4787aa1 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/savedcmd_parser.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import sbom.sbom_logging as sbom_logging +from sbom.cmd_graph.savedcmd_parser.command_splitter import IfBlock, split_commands +from sbom.cmd_graph.savedcmd_parser.command_parser_registry import CommandParserRegistry +from sbom.cmd_graph.savedcmd_parser.tokenizer import CmdParsingError +from sbom.path_utils import PathStr + +DEFAULT_COMMAND_PARSER_REGISTRY = CommandParserRegistry.create() + + +def parse_inputs_from_commands( + commands: str, + fail_on_unknown_build_command: bool, + registry: CommandParserRegistry | None = None, +) -> list[PathStr]: + """ + Extract input files referenced in a set of command-line commands. + + Args: + commands (str): Command line expression to parse. + fail_on_unknown_build_command (bool): Whether to fail if an unknown build command is encountered. If False, errors are logged as warnings. + registry (CommandParserRegistry | None): Registry of single command parsers. + + Returns: + list[PathStr]: List of input file paths required by the commands. + """ + + def log_error_or_warning(message: str, /, **kwargs: str) -> None: + if fail_on_unknown_build_command: + sbom_logging.error(message, **kwargs) + else: + sbom_logging.warning(message, **kwargs) + + if registry is None: + registry = DEFAULT_COMMAND_PARSER_REGISTRY + + input_files: list[PathStr] = [] + for single_command in split_commands(commands): + if isinstance(single_command, IfBlock): + inputs = parse_inputs_from_commands(single_command.then_statement, fail_on_unknown_build_command, registry) + if inputs: + log_error_or_warning( + "Skipped parsing command {then_statement} because input files in IfBlock 'then' statement are not supported", + then_statement=single_command.then_statement, + ) + continue + + matched_parser = next((parser for pattern, parser in registry if pattern.match(single_command)), None) + if matched_parser is None: + log_error_or_warning( + "Skipped parsing command {single_command} because no matching parser was found", + single_command=single_command, + ) + continue + try: + inputs = matched_parser(single_command) + input_files.extend(inputs) + except (CmdParsingError, IndexError) as e: + log_error_or_warning( + "Skipped parsing command {single_command} because of command parsing error: {error_message}", + single_command=single_command, + error_message=str(e), + ) + + return [input.strip().rstrip("/") for input in input_files] diff --git a/scripts/sbom/sbom/cmd_graph/savedcmd_parser/tokenizer.py b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/tokenizer.py new file mode 100644 index 000000000000..1bf081f40be7 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/savedcmd_parser/tokenizer.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import re +import shlex +from dataclasses import dataclass +from typing import Union + + +class CmdParsingError(Exception): + pass + + +@dataclass +class Option: + name: str + value: str | None = None + + +@dataclass +class Positional: + value: str + + +_SUBCOMMAND_PATTERN = re.compile(r"\$\$\(([^()]*)\)") +"""Pattern to match $$(...) blocks""" + + +def tokenize_single_command(command: str, flag_options: list[str] | None = None) -> list[Union[Option, Positional]]: + """ + Parse a shell command into a list of Options and Positionals. + - Positional: the command and any positional arguments. + - Options: handles flags and options with values provided as space-separated, or equals-sign + (e.g., '--opt val', '--opt=val', '--flag'). + + Args: + command: Command line string. + flag_options: Options that are flags without values (e.g., '--verbose'). + + Returns: + List of `Option` and `Positional` objects in command order. + """ + + # Wrap all $$(...) blocks in double quotes to prevent shlex from splitting them. + command_with_protected_subcommands = _SUBCOMMAND_PATTERN.sub(lambda m: f'"$$({m.group(1)})"', command) + tokens = shlex.split(command_with_protected_subcommands) + + parsed: list[Option | Positional] = [] + i = 0 + while i < len(tokens): + token = tokens[i] + + # Positional + if not token.startswith("-"): + parsed.append(Positional(token)) + i += 1 + continue + + # Option without value (--flag) + if (token.startswith("-") and i + 1 < len(tokens) and tokens[i + 1].startswith("-")) or ( + flag_options and token in flag_options + ): + parsed.append(Option(name=token)) + i += 1 + continue + + # Option with equals sign (--opt=val) + if "=" in token: + name, value = token.split("=", 1) + parsed.append(Option(name=name, value=value)) + i += 1 + continue + + # Option with space-separated value (--opt val) + if i + 1 < len(tokens) and not tokens[i + 1].startswith("-"): + parsed.append(Option(name=token, value=tokens[i + 1])) + i += 2 + continue + + raise CmdParsingError(f"Unrecognized token: {token} in command {command}") + + return parsed + + +def tokenize_single_command_positionals_only(command: str) -> list[str]: + command_parts = tokenize_single_command(command) + positionals = [p.value for p in command_parts if isinstance(p, Positional)] + if len(positionals) != len(command_parts): + raise CmdParsingError( + f"Invalid command format: expected positional arguments only but got options in command {command}." + ) + return positionals diff --git a/scripts/sbom/sbom/environment.py b/scripts/sbom/sbom/environment.py new file mode 100644 index 000000000000..4304066fe974 --- /dev/null +++ b/scripts/sbom/sbom/environment.py @@ -0,0 +1,192 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import os + +KERNEL_BUILD_VARIABLES_ALLOWLIST = [ + "AFLAGS_KERNEL", + "AFLAGS_MODULE", + "AR", + "ARCH", + "ARCH_CORE", + "ARCH_DRIVERS", + "ARCH_LIB", + "AWK", + "BASH", + "BINDGEN", + "BITS", + "CC", + "CC_FLAGS_FPU", + "CC_FLAGS_NO_FPU", + "CFLAGS_GCOV", + "CFLAGS_KERNEL", + "CFLAGS_MODULE", + "CHECK", + "CHECKFLAGS", + "CLIPPY_CONF_DIR", + "CONFIG_SHELL", + "CPP", + "CROSS_COMPILE", + "CURDIR", + "GNUMAKEFLAGS", + "HOSTCC", + "HOSTCXX", + "HOSTPKG_CONFIG", + "HOSTRUSTC", + "INSTALLKERNEL", + "INSTALL_DTBS_PATH", + "INSTALL_HDR_PATH", + "INSTALL_PATH", + "KBUILD_AFLAGS", + "KBUILD_AFLAGS_KERNEL", + "KBUILD_AFLAGS_MODULE", + "KBUILD_BUILTIN", + "KBUILD_CFLAGS", + "KBUILD_CFLAGS_KERNEL", + "KBUILD_CFLAGS_MODULE", + "KBUILD_CHECKSRC", + "KBUILD_CLIPPY", + "KBUILD_CPPFLAGS", + "KBUILD_EXTMOD", + "KBUILD_EXTRA_WARN", + "KBUILD_HOSTCFLAGS", + "KBUILD_HOSTCXXFLAGS", + "KBUILD_HOSTLDFLAGS", + "KBUILD_HOSTLDLIBS", + "KBUILD_HOSTRUSTFLAGS", + "KBUILD_IMAGE", + "KBUILD_LDFLAGS", + "KBUILD_LDFLAGS_MODULE", + "KBUILD_LDS", + "KBUILD_MODULES", + "KBUILD_PROCMACROLDFLAGS", + "KBUILD_RUSTFLAGS", + "KBUILD_RUSTFLAGS_KERNEL", + "KBUILD_RUSTFLAGS_MODULE", + "KBUILD_USERCFLAGS", + "KBUILD_USERLDFLAGS", + "KBUILD_VERBOSE", + "KBUILD_VMLINUX_LIBS", + "KBZIP2", + "KCONFIG_CONFIG", + "KERNELDOC", + "KERNELRELEASE", + "KERNELVERSION", + "KGZIP", + "KLZOP", + "LC_COLLATE", + "LC_NUMERIC", + "LD", + "LDFLAGS_MODULE", + "LEX", + "LINUXINCLUDE", + "LZ4", + "LZMA", + "MAKE", + "MAKEFILES", + "MAKEFILE_LIST", + "MAKEFLAGS", + "MAKELEVEL", + "MAKEOVERRIDES", + "MAKE_COMMAND", + "MAKE_HOST", + "MAKE_TERMERR", + "MAKE_TERMOUT", + "MAKE_VERSION", + "MFLAGS", + "MODLIB", + "NM", + "NOSTDINC_FLAGS", + "O", + "OBJCOPY", + "OBJCOPYFLAGS", + "OBJDUMP", + "PAHOLE", + "PATCHLEVEL", + "PERL", + "PYTHON3", + "Q", + "RCS_FIND_IGNORE", + "READELF", + "REALMODE_CFLAGS", + "RESOLVE_BTFIDS", + "RETHUNK_CFLAGS", + "RETHUNK_RUSTFLAGS", + "RETPOLINE_CFLAGS", + "RETPOLINE_RUSTFLAGS", + "RETPOLINE_VDSO_CFLAGS", + "RUSTC", + "RUSTC_BOOTSTRAP", + "RUSTC_OR_CLIPPY", + "RUSTC_OR_CLIPPY_QUIET", + "RUSTDOC", + "RUSTFLAGS_KERNEL", + "RUSTFLAGS_MODULE", + "RUSTFMT", + "SRCARCH", + "STRIP", + "SUBLEVEL", + "SUFFIXES", + "TAR", + "UTS_MACHINE", + "VERSION", + "VPATH", + "XZ", + "YACC", + "ZSTD", + "building_out_of_srctree", + "cross_compiling", + "objtree", + "quiet", + "rust_common_flags", + "srcroot", + "srctree", + "sub_make_done", + "subdir", +] + + +class Environment: + """ + Read-only accessor for kernel build environment variables. + """ + + @classmethod + def KERNEL_BUILD_VARIABLES(cls) -> dict[str, str]: + return { + name: value.strip() + for name in KERNEL_BUILD_VARIABLES_ALLOWLIST + if (value := os.getenv(name)) is not None and value.strip() + } + + @classmethod + def ARCH(cls) -> str | None: + return os.getenv("ARCH") + + @classmethod + def SRCARCH(cls) -> str | None: + return os.getenv("SRCARCH") + + @classmethod + def CC(cls) -> str | None: + return os.getenv("CC") + + @classmethod + def LD(cls) -> str | None: + return os.getenv("LD") + + @classmethod + def AR(cls) -> str | None: + return os.getenv("AR") + + @classmethod + def NM(cls) -> str | None: + return os.getenv("NM") + + @classmethod + def OBJCOPY(cls) -> str | None: + return os.getenv("OBJCOPY") + + @classmethod + def STRIP(cls) -> str | None: + return os.getenv("STRIP") From 9c16c1ea466d6c58b82c5d91353c3c6747c059bc Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:52 +0200 Subject: [PATCH 1567/5214] scripts/sbom: add cmd graph generation Implement command graph generation by parsing .cmd files to build a dependency graph. Add CmdGraph, CmdGraphNode, and .cmd file parsing. Supports generating a flat list of used source files via the --generate-used-files cli argument. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- Makefile | 6 +- scripts/sbom/sbom.py | 39 +++++ scripts/sbom/sbom/cmd_graph/__init__.py | 7 + scripts/sbom/sbom/cmd_graph/cmd_file.py | 162 ++++++++++++++++++ scripts/sbom/sbom/cmd_graph/cmd_graph.py | 46 +++++ scripts/sbom/sbom/cmd_graph/cmd_graph_node.py | 111 ++++++++++++ scripts/sbom/sbom/cmd_graph/deps_parser.py | 52 ++++++ scripts/sbom/sbom/config.py | 149 +++++++++++++++- scripts/sbom/sbom/path_utils.py | 22 +++ 9 files changed, 591 insertions(+), 3 deletions(-) create mode 100644 scripts/sbom/sbom/cmd_graph/__init__.py create mode 100644 scripts/sbom/sbom/cmd_graph/cmd_file.py create mode 100644 scripts/sbom/sbom/cmd_graph/cmd_graph.py create mode 100644 scripts/sbom/sbom/cmd_graph/cmd_graph_node.py create mode 100644 scripts/sbom/sbom/cmd_graph/deps_parser.py create mode 100644 scripts/sbom/sbom/path_utils.py diff --git a/Makefile b/Makefile index ec54f7d51cf4..4c6133af5549 100644 --- a/Makefile +++ b/Makefile @@ -2208,7 +2208,11 @@ sbom_targets += sbom-build.spdx.json sbom-output.spdx.json quiet_cmd_sbom = GEN $(sbom_targets) cmd_sbom = printf "%s\n" "$(KBUILD_IMAGE)" >"$(tmp-target)"; \ $(if $(CONFIG_MODULES),sed 's/\.o$$/.ko/' $(objtree)/modules.order >> "$(tmp-target)";) \ - $(PYTHON3) $(srctree)/scripts/sbom/sbom.py; + $(PYTHON3) $(srctree)/scripts/sbom/sbom.py \ + --src-tree $(abspath $(srctree)) \ + --obj-tree $(abspath $(objtree)) \ + --roots-file "$(tmp-target)" \ + --output-directory $(abspath $(objtree)); PHONY += sbom sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules modules.order) $(call cmd,sbom) diff --git a/scripts/sbom/sbom.py b/scripts/sbom/sbom.py index 3bd466720b0d..d700e4f294f7 100644 --- a/scripts/sbom/sbom.py +++ b/scripts/sbom/sbom.py @@ -7,9 +7,13 @@ Compute software bill of materials in SPDX format describing a kernel build. """ import logging +import os import sys +import time import sbom.sbom_logging as sbom_logging from sbom.config import get_config +from sbom.path_utils import is_relative_to +from sbom.cmd_graph import CmdGraph def _exit_with_summary(write_output_on_error: bool = False) -> None: @@ -19,6 +23,11 @@ def _exit_with_summary(write_output_on_error: bool = False) -> None: logging.warning(warning_summary) if error_summary: logging.error(error_summary) + if not write_output_on_error: + logging.info( + "Use --write-output-on-error to generate output documents even when errors occur. " + "Note that in this case the generated documents may be incomplete." + ) sys.exit(1) @@ -32,6 +41,36 @@ def main(): format="[%(levelname)s] %(message)s", ) + # Build cmd graph + logging.debug("Start building cmd graph") + start_time = time.time() + cmd_graph = CmdGraph.create(config.root_paths, config) + logging.debug(f"Built cmd graph in {time.time() - start_time} seconds") + + # Save used files document + if config.generate_used_files: + if config.src_tree == config.obj_tree: + logging.info( + f"Extracting all files from the cmd graph to {config.used_files_file_name} " + "instead of only source files because source files cannot be " + "reliably classified when the source and object trees are identical.", + ) + used_files = [os.path.relpath(node.absolute_path, config.src_tree) for node in cmd_graph] + logging.debug(f"Found {len(used_files)} files in cmd graph.") + else: + used_files = [ + os.path.relpath(node.absolute_path, config.src_tree) + for node in cmd_graph + if is_relative_to(node.absolute_path, config.src_tree) + and not is_relative_to(node.absolute_path, config.obj_tree) + ] + logging.debug(f"Found {len(used_files)} source files in cmd graph") + if not sbom_logging.has_errors() or config.write_output_on_error: + used_files_path = os.path.join(config.output_directory, config.used_files_file_name) + with open(used_files_path, "w", encoding="utf-8") as f: + f.write("\n".join(str(file_path) for file_path in used_files)) + logging.debug(f"Successfully saved {used_files_path}") + _exit_with_summary(config.write_output_on_error) diff --git a/scripts/sbom/sbom/cmd_graph/__init__.py b/scripts/sbom/sbom/cmd_graph/__init__.py new file mode 100644 index 000000000000..9d661a5c3d93 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from .cmd_graph import CmdGraph +from .cmd_graph_node import CmdGraphNode, CmdGraphNodeConfig + +__all__ = ["CmdGraph", "CmdGraphNode", "CmdGraphNodeConfig"] diff --git a/scripts/sbom/sbom/cmd_graph/cmd_file.py b/scripts/sbom/sbom/cmd_graph/cmd_file.py new file mode 100644 index 000000000000..dcd63e284a38 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/cmd_file.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import os +import re +from dataclasses import dataclass, field +from sbom.cmd_graph.deps_parser import parse_cmd_file_deps +from sbom.cmd_graph.savedcmd_parser import parse_inputs_from_commands +import sbom.sbom_logging as sbom_logging +from sbom.path_utils import PathStr + +SAVEDCMD_PATTERN = re.compile(r"^(saved)?cmd_.*?:=\s*(?P.+)$") +SOURCE_PATTERN = re.compile(r"^source.*?:=\s*(?P.+)$") + + +@dataclass +class CmdFile: + cmd_file_path: PathStr + savedcmd: str + source: PathStr | None = None + deps: list[str] = field(default_factory=list) + make_rules: list[str] = field(default_factory=list) + + @classmethod + def create(cls, cmd_file_path: PathStr) -> "CmdFile | None": + """ + Parses a .cmd file. + .cmd files are assumed to have one of the following structures: + 1. Full Cmd File + (saved)?cmd_ := + source_ := + deps_ := \ + + := $(deps_) + $(deps_): + + 2. Command Only Cmd File + (saved)?cmd_ := + + 3. Single Dependency Cmd File + (saved)?cmd_ := + : + + Args: + cmd_file_path (Path): absolute Path to a .cmd file + + Returns: + cmd_file (CmdFile): Parsed cmd file. + """ + with open(cmd_file_path, "rt", encoding="utf-8") as f: + lines = [line.strip() for line in f.readlines() if line.strip() != "" and not line.startswith("#")] + + # savedcmd + match = SAVEDCMD_PATTERN.match(lines[0] if lines else "") + if match is None: + sbom_logging.error( + "Skip parsing '{cmd_file_path}' because no 'savedcmd_' command was found.", cmd_file_path=cmd_file_path + ) + return None + savedcmd = match.group("full_command") + + # Command Only Cmd File + if len(lines) == 1: + return CmdFile(cmd_file_path, savedcmd) + + # Single Dependency Cmd File + if len(lines) == 2: + parts = lines[1].split(":", 1) + if len(parts) != 2: + sbom_logging.error( + "Skip parsing '{cmd_file_path}'. Expected dependency line ': ' but got {second_line}", cmd_file_path=cmd_file_path, second_line=lines[1] + ) + return None + dep = parts[1].strip() + return CmdFile(cmd_file_path, savedcmd, deps=[dep]) + + # Full Cmd File + # source + line1 = SOURCE_PATTERN.match(lines[1]) + if line1 is None: + sbom_logging.error( + "Skip parsing '{cmd_file_path}' because no 'source_' entry was found.", cmd_file_path=cmd_file_path + ) + return CmdFile(cmd_file_path, savedcmd) + source = line1.group("source_file") + + # deps + deps: list[str] = [] + i = 3 # lines[2] includes the variable assignment but no actual dependency, so we need to start at lines[3]. + while i < len(lines): + if not lines[i].endswith("\\"): + break + deps.append(lines[i][:-1].strip()) + i += 1 + + # make_rules + make_rules = lines[i:] + + return CmdFile(cmd_file_path, savedcmd, source, deps, make_rules) + + def get_dependencies( + self: "CmdFile", target_path: PathStr, obj_tree: PathStr, fail_on_unknown_build_command: bool + ) -> list[PathStr]: + """ + Parses all dependencies required to build a target file from its cmd file. + + Args: + target_path: path to the target file relative to `obj_tree`. + obj_tree: absolute path to the object tree. + fail_on_unknown_build_command: Whether to fail if an unknown build command is encountered. + + Returns: + list[PathStr]: dependency file paths relative to `obj_tree`. + """ + input_files: list[PathStr] = [ + str(p) for p in parse_inputs_from_commands(self.savedcmd, fail_on_unknown_build_command) + ] + if self.deps: + input_files += [str(p) for p in parse_cmd_file_deps(self.deps)] + input_files = _expand_resolve_files(input_files, obj_tree) + + cmd_file_dependencies: list[PathStr] = [] + for input_file in input_files: + # input files are either absolute or relative to the object tree + if os.path.isabs(input_file): + input_file = os.path.relpath(input_file, obj_tree) + if input_file == target_path: + # Skip target file to prevent cycles. This is necessary because some multi stage commands first create an output and then pass it as input to the next command, e.g., objcopy. + continue + cmd_file_dependencies.append(input_file) + unique_cmd_file_dependencies = list(dict.fromkeys(cmd_file_dependencies)) + return unique_cmd_file_dependencies + + +def _expand_resolve_files(input_files: list[PathStr], obj_tree: PathStr) -> list[PathStr]: + """ + Expands resolve files which may reference additional files via '@' notation. + + Args: + input_files (list[PathStr]): List of file paths relative to the object tree, where paths starting with '@' refer to files + containing further file paths, each on a separate line. + obj_tree: Absolute path to the root of the object tree. + + Returns: + list[PathStr]: Flattened list of all input file paths, with any nested '@' file references resolved recursively. + """ + expanded_input_files: list[PathStr] = [] + for input_file in input_files: + if not input_file.startswith("@"): + expanded_input_files.append(input_file) + continue + resolve_file_path = os.path.join(obj_tree, input_file.removeprefix("@")) + if not os.path.exists(resolve_file_path): + sbom_logging.error( + "Skip resolving '{resolve_file_path}' because the response file does not exist.", + resolve_file_path=resolve_file_path, + ) + continue + with open(resolve_file_path, "rt", encoding="utf-8") as f: + resolve_file_content = [line_stripped for line in f.readlines() if (line_stripped := line.strip())] + expanded_input_files += _expand_resolve_files(resolve_file_content, obj_tree) + return expanded_input_files diff --git a/scripts/sbom/sbom/cmd_graph/cmd_graph.py b/scripts/sbom/sbom/cmd_graph/cmd_graph.py new file mode 100644 index 000000000000..2f57965237f4 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/cmd_graph.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from collections import deque +from dataclasses import dataclass, field +from typing import Iterator + +from sbom.cmd_graph.cmd_graph_node import CmdGraphNode, CmdGraphNodeConfig +from sbom.path_utils import PathStr + + +@dataclass +class CmdGraph: + """Directed acyclic graph of build dependencies primarily inferred from .cmd files produced during kernel builds""" + + roots: list[CmdGraphNode] = field(default_factory=list) + + @classmethod + def create(cls, root_paths: list[PathStr], config: CmdGraphNodeConfig) -> "CmdGraph": + """ + Recursively builds a dependency graph starting from `root_paths`. + Dependencies are mainly discovered by parsing the `.cmd` files. + + Args: + root_paths (list[PathStr]): List of paths to root outputs relative to obj_tree + config (CmdGraphNodeConfig): Configuration options + + Returns: + CmdGraph: A graph of all build dependencies for the given root files. + """ + node_cache: dict[PathStr, CmdGraphNode] = {} + root_nodes = [CmdGraphNode.create(root_path, config, node_cache) for root_path in root_paths] + return CmdGraph(root_nodes) + + def __iter__(self) -> Iterator[CmdGraphNode]: + """Traverse the graph in breadth-first order, yielding each unique node.""" + visited: set[PathStr] = set() + node_stack: deque[CmdGraphNode] = deque(self.roots) + while len(node_stack) > 0: + node = node_stack.popleft() + if node.absolute_path in visited: + continue + + visited.add(node.absolute_path) + node_stack.extend(node.children) + yield node diff --git a/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py b/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py new file mode 100644 index 000000000000..7dde1c28eef1 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass, field +import logging +import os +from typing import Iterator, Protocol + +from sbom import sbom_logging +from sbom.cmd_graph.cmd_file import CmdFile +from sbom.path_utils import PathStr, has_link, is_relative_to + + +class CmdGraphNodeConfig(Protocol): + obj_tree: PathStr + src_tree: PathStr + fail_on_unknown_build_command: bool + + +@dataclass +class CmdGraphNode: + """A node in the cmd graph representing a single file and its dependencies.""" + + absolute_path: PathStr + """Absolute path to the file this node represents.""" + + cmd_file: CmdFile | None = None + """Parsed .cmd file describing how the file at absolute_path was built, or None if not available.""" + + cmd_file_dependencies: list["CmdGraphNode"] = field(default_factory=list) + + @property + def children(self) -> Iterator["CmdGraphNode"]: + seen: set[PathStr] = set() + for node in self.cmd_file_dependencies: + if node.absolute_path not in seen: + seen.add(node.absolute_path) + yield node + + @classmethod + def create( + cls, + target_path: PathStr, + config: CmdGraphNodeConfig, + cache: dict[PathStr, "CmdGraphNode"] | None = None, + depth: int = 0, + ) -> "CmdGraphNode": + """ + Recursively builds a dependency graph starting from `target_path`. + Dependencies are mainly discovered by parsing the `..cmd` file. + + Args: + target_path: Path to the target file relative to obj_tree. + config: Config options + cache: Tracks processed nodes to prevent cycles. + depth: Internal parameter to track the current recursion depth. + + Returns: + CmdGraphNode: cmd graph node representing the target file + """ + if cache is None: + cache = {} + + target_path_absolute = ( + os.path.realpath(p) + if has_link(p:=os.path.join(config.obj_tree, target_path)) + else os.path.normpath(p) + ) + + if target_path_absolute in cache: + return cache[target_path_absolute] + + if depth == 0: + logging.debug(f"Build node: {target_path}") + + cmd_file_path = _to_cmd_path(target_path_absolute) + cmd_file = CmdFile.create(cmd_file_path) if os.path.exists(cmd_file_path) else None + node = CmdGraphNode(target_path_absolute, cmd_file) + cache[target_path_absolute] = node + + if not os.path.exists(target_path_absolute): + error_or_warning = ( + sbom_logging.error + if is_relative_to(target_path_absolute, config.obj_tree) + or is_relative_to(target_path_absolute, config.src_tree) + else sbom_logging.warning + ) + error_or_warning( + "Skip parsing '{target_path_absolute}' because file does not exist", + target_path_absolute=target_path_absolute, + ) + return node + + # Search for dependencies to add to the graph as child nodes. Child paths are always relative to the output tree. + def _build_child_node(child_path: PathStr) -> "CmdGraphNode": + return CmdGraphNode.create(child_path, config, cache, depth + 1) + + if cmd_file is not None: + node.cmd_file_dependencies = [ + _build_child_node(cmd_file_dependency_path) + for cmd_file_dependency_path in cmd_file.get_dependencies( + target_path, config.obj_tree, config.fail_on_unknown_build_command + ) + ] + + return node + + +def _to_cmd_path(path: PathStr) -> PathStr: + name = os.path.basename(path) + return path.removesuffix(name) + f".{name}.cmd" diff --git a/scripts/sbom/sbom/cmd_graph/deps_parser.py b/scripts/sbom/sbom/cmd_graph/deps_parser.py new file mode 100644 index 000000000000..6a2d92f0778c --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/deps_parser.py @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import re +import sbom.sbom_logging as sbom_logging +from sbom.path_utils import PathStr + +# Match dependencies on config files +# Example match: "$(wildcard include/config/CONFIG_SOMETHING)" +CONFIG_PATTERN = re.compile(r"\$\(wildcard (include/config/[^)]+)\)") + +# Match dependencies on the objtool binary +# Example match: "$(wildcard ./tools/objtool/objtool)" +OBJTOOL_PATTERN = re.compile(r"\$\(wildcard \./tools/objtool/objtool\)") + +# Match any Makefile wildcard reference +# Example match: "$(wildcard path/to/file)" +WILDCARD_PATTERN = re.compile(r"\$\(wildcard (?P[^)]+)\)") + +# Match ordinary paths: +# - ^(\/)?: Optionally starts with a '/' +# - (([\w\-\.,+~=@ ]*)\/)*: Zero or more directory levels +# - [\w\-\.,+~=@ ]+$: Path component (file or directory) +# Example matches: "/foo/bar.c", "dir1/dir2/file.txt", "plainfile" +VALID_PATH_PATTERN = re.compile(r"^(\/)?(([\w\-\.,+~=@ ]*)\/)*[\w\-\.,+~=@ ]+$") + + +def parse_cmd_file_deps(deps: list[str]) -> list[PathStr]: + """ + Parse dependency strings of a .cmd file and return valid input file paths. + + Args: + deps: List of dependency strings as found in `.cmd` files. + + Returns: + input_files: List of input file paths + """ + input_files: list[PathStr] = [] + for dep in deps: + dep = dep.strip() + match dep: + case _ if CONFIG_PATTERN.match(dep) or OBJTOOL_PATTERN.match(dep): + # config paths like include/config/ should not be included in the graph + continue + case _ if match := WILDCARD_PATTERN.match(dep): + path = match.group("path") + input_files.append(path) + case _ if VALID_PATH_PATTERN.match(dep): + input_files.append(dep) + case _: + sbom_logging.error("Skip parsing dependency {dep} because of unrecognized format", dep=dep) + return input_files diff --git a/scripts/sbom/sbom/config.py b/scripts/sbom/sbom/config.py index c1ac9ad5737f..b8c1a2b404df 100644 --- a/scripts/sbom/sbom/config.py +++ b/scripts/sbom/sbom/config.py @@ -3,21 +3,88 @@ import argparse from dataclasses import dataclass +import os +from typing import Any +from sbom.path_utils import PathStr @dataclass class KernelSbomConfig: + src_tree: PathStr + """Absolute path to the Linux kernel source directory.""" + + obj_tree: PathStr + """Absolute path to the build output directory.""" + + root_paths: list[PathStr] + """List of paths to root outputs (relative to obj_tree) to base the SBOM on.""" + + generate_used_files: bool + """Whether to generate a flat list of all source files used in the build. + If False, no used-files document is created.""" + + used_files_file_name: str + """If `generate_used_files` is True, specifies the file name for the used-files document.""" + + output_directory: PathStr + """Path to the directory where the generated output documents will be saved.""" + debug: bool """Whether to enable debug logging.""" + fail_on_unknown_build_command: bool + """Whether to fail if an unknown build command is encountered in a .cmd file.""" -def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, bool]: + write_output_on_error: bool + """Whether to write output documents even if errors occur.""" + + +def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, Any]: """ Parse command-line arguments using argparse. Returns: Dictionary of parsed arguments. """ + parser.add_argument( + "--src-tree", + default="../linux", + help="Path to the kernel source tree (default: ../linux)", + ) + parser.add_argument( + "--obj-tree", + default="../linux/kernel_build", + help="Path to the build output directory (default: ../linux/kernel_build)", + ) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument( + "--roots", + nargs="+", + help="Space-separated list of paths relative to obj-tree for which the SBOM will be created.\n" + "Cannot be used together with --roots-file.", + ) + group.add_argument( + "--roots-file", + help="Path to a file containing the root paths (one per line). Cannot be used together with --roots.", + ) + parser.add_argument( + "--generate-used-files", + action="store_true", + default=False, + help=( + "Whether to create the sbom.used-files.txt file, a flat list of all " + "source files used for the kernel build.\n" + "If src-tree and obj-tree are equal it is not possible to reliably " + "classify source files.\n" + "In this case sbom.used-files.txt will contain all files used for the " + "kernel build including all build artifacts. (default: False)" + ), + ) + parser.add_argument( + "--output-directory", + default=".", + help="Path to the directory where the generated output documents will be stored (default: .)", + ) parser.add_argument( "--debug", action="store_true", @@ -25,6 +92,28 @@ def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, bool]: help="Enable debug logs (default: False)", ) + # Error handling settings + parser.add_argument( + "--do-not-fail-on-unknown-build-command", + action="store_true", + default=False, + help=( + "Whether to fail if an unknown build command is encountered in a .cmd file.\n" + "If set to True, errors are logged as warnings instead. (default: False)" + ), + ) + parser.add_argument( + "--write-output-on-error", + action="store_true", + default=False, + help=( + "Write output documents even if errors occur. The resulting documents " + "may be incomplete.\n" + "A summary of warnings and errors can be found in the 'comment' property " + "of the CreationInfo element. (default: False)" + ), + ) + args = vars(parser.parse_args()) return args @@ -37,10 +126,66 @@ def get_config() -> KernelSbomConfig: KernelSbomConfig: Configuration object with all settings for SBOM generation. """ parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, description="Generate SPDX SBOM documents for kernel builds", ) args = _parse_cli_arguments(parser) + # Extract and validate cli arguments + src_tree = os.path.realpath(args["src_tree"]) + obj_tree = os.path.realpath(args["obj_tree"]) + root_paths = [] + if args["roots_file"]: + with open(args["roots_file"], "rt", encoding="utf-8") as f: + root_paths = [root.strip() for root in f.readlines()] + if len(root_paths) == 0: + parser.error("--roots-file must contain at least one path") + else: + root_paths = args["roots"] + _validate_path_arguments(parser, src_tree, obj_tree, root_paths) + + generate_used_files = args["generate_used_files"] + output_directory = os.path.realpath(args["output_directory"]) debug = args["debug"] - return KernelSbomConfig(debug=debug) + fail_on_unknown_build_command = not args["do_not_fail_on_unknown_build_command"] + write_output_on_error = args["write_output_on_error"] + + # Hardcoded config + used_files_file_name = "sbom.used-files.txt" + + return KernelSbomConfig( + src_tree=src_tree, + obj_tree=obj_tree, + root_paths=root_paths, + generate_used_files=generate_used_files, + used_files_file_name=used_files_file_name, + output_directory=output_directory, + debug=debug, + fail_on_unknown_build_command=fail_on_unknown_build_command, + write_output_on_error=write_output_on_error, + ) + + +def _validate_path_arguments( + parser: argparse.ArgumentParser, + src_tree: PathStr, + obj_tree: PathStr, + root_paths: list[PathStr], +) -> None: + """ + Validate that the provided paths exist. + + Args: + parser: The argument parser, used to emit well-formatted error messages. + src_tree: Absolute path to the source tree. + obj_tree: Absolute path to the object tree. + root_paths: List of root paths relative to obj_tree. + """ + if not os.path.exists(src_tree): + parser.error(f"--src-tree {src_tree} does not exist") + if not os.path.exists(obj_tree): + parser.error(f"--obj-tree {obj_tree} does not exist") + for root_path in root_paths: + if not os.path.isfile(root_path_absolute := os.path.join(obj_tree, root_path)): + parser.error(f"path to root artifact {root_path_absolute} is not a file") diff --git a/scripts/sbom/sbom/path_utils.py b/scripts/sbom/sbom/path_utils.py new file mode 100644 index 000000000000..29820046dc88 --- /dev/null +++ b/scripts/sbom/sbom/path_utils.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import os +from functools import lru_cache + +PathStr = str +"""Filesystem path represented as a plain string for better performance than pathlib.Path.""" + + +def is_relative_to(path: PathStr, base: PathStr) -> bool: + return os.path.commonpath([path, base]) == base + +@lru_cache(maxsize=None) +def has_link(path: PathStr) -> bool: + """Returns True if path or any of its ancestor directories is a symlink. Results are cached to avoid duplicate lstat syscalls.""" + if os.path.islink(path): + return True + parent = os.path.dirname(path) + if parent == path: + return False + return has_link(parent) From d764b54e2885d55a6d272507e8b8e1b2cbbc2530 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:53 +0200 Subject: [PATCH 1568/5214] scripts/sbom: add additional dependency sources for cmd graph Add hardcoded dependencies and .incbin directive parsing to discover dependencies not tracked by .cmd files. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/sbom/cmd_graph/cmd_graph_node.py | 33 ++++++- .../sbom/cmd_graph/hardcoded_dependencies.py | 87 +++++++++++++++++++ scripts/sbom/sbom/cmd_graph/incbin_parser.py | 42 +++++++++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 scripts/sbom/sbom/cmd_graph/hardcoded_dependencies.py create mode 100644 scripts/sbom/sbom/cmd_graph/incbin_parser.py diff --git a/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py b/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py index 7dde1c28eef1..61f3a8140cea 100644 --- a/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py +++ b/scripts/sbom/sbom/cmd_graph/cmd_graph_node.py @@ -2,15 +2,24 @@ # Copyright (C) 2025 TNG Technology Consulting GmbH from dataclasses import dataclass, field +from itertools import chain import logging import os from typing import Iterator, Protocol from sbom import sbom_logging from sbom.cmd_graph.cmd_file import CmdFile +from sbom.cmd_graph.hardcoded_dependencies import get_hardcoded_dependencies +from sbom.cmd_graph.incbin_parser import parse_incbin_statements from sbom.path_utils import PathStr, has_link, is_relative_to +@dataclass +class IncbinDependency: + node: "CmdGraphNode" + full_statement: str + + class CmdGraphNodeConfig(Protocol): obj_tree: PathStr src_tree: PathStr @@ -28,11 +37,17 @@ class CmdGraphNode: """Parsed .cmd file describing how the file at absolute_path was built, or None if not available.""" cmd_file_dependencies: list["CmdGraphNode"] = field(default_factory=list) + incbin_dependencies: list[IncbinDependency] = field(default_factory=list) + hardcoded_dependencies: list["CmdGraphNode"] = field(default_factory=list) @property def children(self) -> Iterator["CmdGraphNode"]: seen: set[PathStr] = set() - for node in self.cmd_file_dependencies: + for node in chain( + self.cmd_file_dependencies, + (dep.node for dep in self.incbin_dependencies), + self.hardcoded_dependencies, + ): if node.absolute_path not in seen: seen.add(node.absolute_path) yield node @@ -95,6 +110,13 @@ class CmdGraphNode: def _build_child_node(child_path: PathStr) -> "CmdGraphNode": return CmdGraphNode.create(child_path, config, cache, depth + 1) + node.hardcoded_dependencies = [ + _build_child_node(hardcoded_dependency_path) + for hardcoded_dependency_path in get_hardcoded_dependencies( + target_path_absolute, config.obj_tree, config.src_tree + ) + ] + if cmd_file is not None: node.cmd_file_dependencies = [ _build_child_node(cmd_file_dependency_path) @@ -103,6 +125,15 @@ class CmdGraphNode: ) ] + if node.absolute_path.endswith(".S"): + node.incbin_dependencies = [ + IncbinDependency( + node=_build_child_node(incbin_statement.path), + full_statement=incbin_statement.full_statement, + ) + for incbin_statement in parse_incbin_statements(node.absolute_path) + ] + return node diff --git a/scripts/sbom/sbom/cmd_graph/hardcoded_dependencies.py b/scripts/sbom/sbom/cmd_graph/hardcoded_dependencies.py new file mode 100644 index 000000000000..2eb04d30f4e6 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/hardcoded_dependencies.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import os +from typing import Callable +import sbom.sbom_logging as sbom_logging +from sbom.path_utils import PathStr, is_relative_to +from sbom.environment import Environment + +HARDCODED_DEPENDENCIES: dict[str, list[str]] = { + # defined in linux/Kbuild + "include/generated/rq-offsets.h": ["kernel/sched/rq-offsets.s"], + "kernel/sched/rq-offsets.s": ["include/generated/asm-offsets.h"], + "include/generated/bounds.h": ["kernel/bounds.s"], + "include/generated/asm-offsets.h": ["arch/{arch}/kernel/asm-offsets.s"], +} +""" +Maps file paths to the list of dependencies required to build them +which are not tracked by the .cmd dependency mechanism. +Paths are relative to either the source tree or the object tree. +""" + +def get_hardcoded_dependencies(path: PathStr, obj_tree: PathStr, src_tree: PathStr) -> list[PathStr]: + """ + Some files in the kernel build process are not tracked by the .cmd dependency mechanism. + Parsing these dependencies programmatically is too complex for the scope of this project. + Therefore, this function provides manually defined dependencies to be added to the build graph. + + Args: + path: absolute path to a file within the src tree or object tree. + obj_tree: absolute Path to the base directory of the object tree. + src_tree: absolute Path to the `linux` source directory. + + Returns: + list[PathStr]: A list of dependency file paths (relative to the object tree) required to build the file at the given path. + """ + if is_relative_to(path, obj_tree): + path = os.path.relpath(path, obj_tree) + elif is_relative_to(path, src_tree): + path = os.path.relpath(path, src_tree) + + if path not in HARDCODED_DEPENDENCIES: + return [] + + template_variables: dict[str, Callable[[], str | None]] = { + "arch": lambda: _get_arch(path), + } + + dependencies: list[PathStr] = [] + for dependency_template in HARDCODED_DEPENDENCIES[path]: + dependency = _evaluate_template(dependency_template, template_variables) + if dependency is None: + continue + if os.path.exists(os.path.join(obj_tree, dependency)): + dependencies.append(dependency) + elif os.path.exists(dependency_absolute := os.path.join(src_tree, dependency)): + dependencies.append(os.path.relpath(dependency_absolute, obj_tree)) + else: + sbom_logging.error( + "Skip hardcoded dependency '{dependency}' for '{path}' because the dependency lies neither in the src tree nor the object tree.", + dependency=dependency, + path=path, + ) + + return dependencies + + +def _evaluate_template(template: str, variables: dict[str, Callable[[], str | None]]) -> str | None: + for key, value_function in variables.items(): + template_key = "{" + key + "}" + if template_key in template: + value = value_function() + if value is None: + return None + template = template.replace(template_key, value) + return template + + +def _get_arch(path: PathStr): + srcarch = Environment.SRCARCH() + if srcarch is None: + sbom_logging.error( + "Skipped architecture specific hardcoded dependency for '{path}' because the SRCARCH environment variable was not set.", + path=path, + ) + return None + return srcarch diff --git a/scripts/sbom/sbom/cmd_graph/incbin_parser.py b/scripts/sbom/sbom/cmd_graph/incbin_parser.py new file mode 100644 index 000000000000..ca289c2b8888 --- /dev/null +++ b/scripts/sbom/sbom/cmd_graph/incbin_parser.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +import re + +from sbom.path_utils import PathStr + +INCBIN_PATTERN = re.compile(r'\s*\.incbin\s+"(?P[^"]+)"') +"""Regex pattern for matching `.incbin ""` statements.""" + + +@dataclass +class IncbinStatement: + """A parsed `.incbin ""` directive.""" + + path: PathStr + """path to the file referenced by the `.incbin` directive.""" + + full_statement: str + """Full `.incbin ""` statement as it originally appeared in the file.""" + + +def parse_incbin_statements(absolute_path: PathStr) -> list[IncbinStatement]: + """ + Parses `.incbin` directives from an `.S` assembly file. + + Args: + absolute_path: Absolute path to the `.S` assembly file. + + Returns: + list[IncbinStatement]: Parsed `.incbin` statements. + """ + with open(absolute_path, "rt", encoding="utf-8") as f: + content = f.read() + return [ + IncbinStatement( + path=match.group("path"), + full_statement=match.group(0).strip(), + ) + for match in INCBIN_PATTERN.finditer(content) + ] From 06f4e57165caf3012876b211cb687ba802188aff Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:54 +0200 Subject: [PATCH 1569/5214] scripts/sbom: add SPDX classes Implement Python dataclasses to model the SPDX classes required within an SPDX document. The class and property names are consistent with the SPDX 3.0.1 specification. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/sbom/spdx/__init__.py | 7 + scripts/sbom/sbom/spdx/build.py | 17 +++ scripts/sbom/sbom/spdx/core.py | 170 ++++++++++++++++++++++ scripts/sbom/sbom/spdx/serialization.py | 62 ++++++++ scripts/sbom/sbom/spdx/simplelicensing.py | 20 +++ scripts/sbom/sbom/spdx/software.py | 69 +++++++++ scripts/sbom/sbom/spdx/spdxId.py | 36 +++++ 7 files changed, 381 insertions(+) create mode 100644 scripts/sbom/sbom/spdx/__init__.py create mode 100644 scripts/sbom/sbom/spdx/build.py create mode 100644 scripts/sbom/sbom/spdx/core.py create mode 100644 scripts/sbom/sbom/spdx/serialization.py create mode 100644 scripts/sbom/sbom/spdx/simplelicensing.py create mode 100644 scripts/sbom/sbom/spdx/software.py create mode 100644 scripts/sbom/sbom/spdx/spdxId.py diff --git a/scripts/sbom/sbom/spdx/__init__.py b/scripts/sbom/sbom/spdx/__init__.py new file mode 100644 index 000000000000..4097b59f8f17 --- /dev/null +++ b/scripts/sbom/sbom/spdx/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from .spdxId import SpdxId, SpdxIdGenerator +from .serialization import JsonLdSpdxDocument + +__all__ = ["JsonLdSpdxDocument", "SpdxId", "SpdxIdGenerator"] diff --git a/scripts/sbom/sbom/spdx/build.py b/scripts/sbom/sbom/spdx/build.py new file mode 100644 index 000000000000..a39ec9c09b16 --- /dev/null +++ b/scripts/sbom/sbom/spdx/build.py @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass, field +from sbom.spdx.core import DictionaryEntry, Element, Hash + + +@dataclass(kw_only=True) +class Build(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Build/Classes/Build/""" + + type: str = field(init=False, default="build_Build") + build_buildType: str + build_buildId: str + build_environment: list[DictionaryEntry] = field(default_factory=list) + build_configSourceUri: list[str] = field(default_factory=list) + build_configSourceDigest: list[Hash] = field(default_factory=list) diff --git a/scripts/sbom/sbom/spdx/core.py b/scripts/sbom/sbom/spdx/core.py new file mode 100644 index 000000000000..7eb376a1cd88 --- /dev/null +++ b/scripts/sbom/sbom/spdx/core.py @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass, field + +from typing import Any, Literal +from sbom.spdx.spdxId import SpdxId + +SPDX_SPEC_VERSION = "3.0.1" + +ExternalIdentifierType = Literal["email", "gitoid", "urlScheme"] +HashAlgorithm = Literal["sha256", "sha512"] +ProfileIdentifierType = Literal["core", "software", "build", "lite", "simpleLicensing"] +RelationshipType = Literal[ + "contains", + "generates", + "hasDeclaredLicense", + "hasInput", + "hasOutput", + "ancestorOf", + "hasDistributionArtifact", + "dependsOn", +] +RelationshipCompleteness = Literal["complete", "incomplete", "noAssertion"] + + +@dataclass +class SpdxObject: + def to_dict(self) -> dict[str, Any]: + def _to_dict(v: Any): + return v.to_dict() if hasattr(v, "to_dict") else v + + d: dict[str, Any] = {} + for field_name in self.__dataclass_fields__: + value = getattr(self, field_name) + if value is None or value == [] or value == "": + continue + + if isinstance(value, Element): + d[field_name] = value.spdxId + elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], Element): # type: ignore + value: list[Element] = value + d[field_name] = [v.spdxId for v in value] + else: + d[field_name] = [_to_dict(v) for v in value] if isinstance(value, list) else _to_dict(value) # type: ignore + return d + + +@dataclass(kw_only=True) +class IntegrityMethod(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/IntegrityMethod/""" + + +@dataclass(kw_only=True) +class Hash(IntegrityMethod): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Hash/""" + + type: str = field(init=False, default="Hash") + hashValue: str + algorithm: HashAlgorithm + + +@dataclass(kw_only=True) +class Element(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Element/""" + + type: str = field(init=False, default="Element") + spdxId: SpdxId + creationInfo: str = "_:creationinfo" + name: str | None = None + verifiedUsing: list[Hash] = field(default_factory=list) + comment: str | None = None + + +@dataclass(kw_only=True) +class ExternalMap(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/ExternalMap/""" + + type: str = field(init=False, default="ExternalMap") + externalSpdxId: SpdxId + + +@dataclass(kw_only=True) +class NamespaceMap(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/NamespaceMap/""" + + type: str = field(init=False, default="NamespaceMap") + prefix: str + namespace: str + + +@dataclass(kw_only=True) +class ElementCollection(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/ElementCollection/""" + + type: str = field(init=False, default="ElementCollection") + element: list[Element] = field(default_factory=list) + rootElement: list[Element] = field(default_factory=list) + profileConformance: list[ProfileIdentifierType] = field(default_factory=list) + + +@dataclass(kw_only=True) +class SpdxDocument(ElementCollection): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/SpdxDocument/""" + + type: str = field(init=False, default="SpdxDocument") + import_: list[ExternalMap] = field(default_factory=list) + namespaceMap: list[NamespaceMap] = field(default_factory=list) + + def to_dict(self) -> dict[str, Any]: + return {("import" if k == "import_" else k): v for k, v in super().to_dict().items()} + + +@dataclass(kw_only=True) +class Agent(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Agent/""" + + type: str = field(init=False, default="Agent") + + +@dataclass(kw_only=True) +class SoftwareAgent(Agent): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/SoftwareAgent/""" + + type: str = field(init=False, default="SoftwareAgent") + + +@dataclass(kw_only=True) +class CreationInfo(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/CreationInfo/""" + + type: str = field(init=False, default="CreationInfo") + id: SpdxId = "_:creationinfo" + specVersion: str = SPDX_SPEC_VERSION + createdBy: list[Agent] + created: str + comment: str | None = None + + def to_dict(self) -> dict[str, Any]: + return {("@id" if k == "id" else k): v for k, v in super().to_dict().items()} + + +@dataclass(kw_only=True) +class Relationship(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Relationship/""" + + type: str = field(init=False, default="Relationship") + relationshipType: RelationshipType + from_: Element # underscore because 'from' is a reserved keyword + to: list[Element] + completeness: RelationshipCompleteness | None = None + + def to_dict(self) -> dict[str, Any]: + return {("from" if k == "from_" else k): v for k, v in super().to_dict().items()} + + +@dataclass(kw_only=True) +class Artifact(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Artifact/""" + + type: str = field(init=False, default="Artifact") + + +@dataclass(kw_only=True) +class DictionaryEntry(SpdxObject): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/DictionaryEntry/""" + + type: str = field(init=False, default="DictionaryEntry") + key: str + value: str diff --git a/scripts/sbom/sbom/spdx/serialization.py b/scripts/sbom/sbom/spdx/serialization.py new file mode 100644 index 000000000000..b4df7d368d46 --- /dev/null +++ b/scripts/sbom/sbom/spdx/serialization.py @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import json +from typing import Any +from sbom.path_utils import PathStr +from sbom.spdx.core import SPDX_SPEC_VERSION, SpdxDocument, SpdxObject + + +class JsonLdSpdxDocument: + """Represents an SPDX document in JSON-LD format for serialization.""" + + graph: list[SpdxObject] + + def __init__(self, graph: list[SpdxObject]) -> None: + """ + Initialize a JSON-LD SPDX document from a graph of SPDX objects. + The graph must contain a single SpdxDocument element. + + Args: + graph: List of SPDX objects representing the complete SPDX document. + """ + self.graph = graph + + @property + def context(self) -> list[str | dict[str, str]]: + spdx_document = next(element for element in self.graph if isinstance(element, SpdxDocument)) + return [ + f"https://spdx.org/rdf/{SPDX_SPEC_VERSION}/spdx-context.jsonld", + {ns.prefix: ns.namespace for ns in spdx_document.namespaceMap}, + ] + + def to_dict(self) -> dict[str, Any]: + """ + Convert the SPDX document to a dictionary representation suitable for JSON serialization. + + Returns: + Dictionary with @context and @graph keys following JSON-LD format. + """ + def _item_to_dict(item: SpdxObject) -> dict: + d = item.to_dict() + if isinstance(item, SpdxDocument): + d.pop("namespaceMap", None) + return d + return { + "@context": self.context, + "@graph": [_item_to_dict(item) for item in self.graph], + } + + def save(self, path: PathStr, prettify: bool) -> None: + """ + Save the SPDX document to a JSON file. + + Args: + path: File path where the document will be saved. + prettify: Whether to pretty-print the JSON with indentation. + """ + with open(path, "w", encoding="utf-8") as f: + if prettify: + json.dump(self.to_dict(), f, indent=2) + else: + json.dump(self.to_dict(), f, separators=(",", ":")) diff --git a/scripts/sbom/sbom/spdx/simplelicensing.py b/scripts/sbom/sbom/spdx/simplelicensing.py new file mode 100644 index 000000000000..750ddd24ad89 --- /dev/null +++ b/scripts/sbom/sbom/spdx/simplelicensing.py @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass, field +from sbom.spdx.core import Element + + +@dataclass(kw_only=True) +class AnyLicenseInfo(Element): + """https://spdx.github.io/spdx-spec/v3.0.1/model/SimpleLicensing/Classes/AnyLicenseInfo/""" + + type: str = field(init=False, default="simplelicensing_AnyLicenseInfo") + + +@dataclass(kw_only=True) +class LicenseExpression(AnyLicenseInfo): + """https://spdx.github.io/spdx-spec/v3.0.1/model/SimpleLicensing/Classes/LicenseExpression/""" + + type: str = field(init=False, default="simplelicensing_LicenseExpression") + simplelicensing_licenseExpression: str diff --git a/scripts/sbom/sbom/spdx/software.py b/scripts/sbom/sbom/spdx/software.py new file mode 100644 index 000000000000..2f46de7c3167 --- /dev/null +++ b/scripts/sbom/sbom/spdx/software.py @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass, field +from typing import Literal +from sbom.spdx.core import Artifact, ElementCollection, IntegrityMethod + + +SbomType = Literal["source", "build"] +FileKindType = Literal["file", "directory"] +SoftwarePurpose = Literal[ + "source", + "archive", + "library", + "file", + "data", + "configuration", + "executable", + "module", + "application", + "documentation", + "other", +] +ContentIdentifierType = Literal["gitoid", "swhid"] + + +@dataclass(kw_only=True) +class Sbom(ElementCollection): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/Sbom/""" + + type: str = field(init=False, default="software_Sbom") + software_sbomType: list[SbomType] = field(default_factory=list) + + +@dataclass(kw_only=True) +class ContentIdentifier(IntegrityMethod): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/ContentIdentifier/""" + + type: str = field(init=False, default="software_ContentIdentifier") + software_contentIdentifierType: ContentIdentifierType + software_contentIdentifierValue: str + + +@dataclass(kw_only=True) +class SoftwareArtifact(Artifact): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/SoftwareArtifact/""" + + type: str = field(init=False, default="software_Artifact") + software_primaryPurpose: SoftwarePurpose | None = None + software_copyrightText: str | None = None + software_contentIdentifier: list[ContentIdentifier] = field(default_factory=list) + + +@dataclass(kw_only=True) +class Package(SoftwareArtifact): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/Package/""" + + type: str = field(init=False, default="software_Package") + name: str # type: ignore + software_packageVersion: str | None = None + + +@dataclass(kw_only=True) +class File(SoftwareArtifact): + """https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/File/""" + + type: str = field(init=False, default="software_File") + name: str # type: ignore + software_fileKind: FileKindType | None = None diff --git a/scripts/sbom/sbom/spdx/spdxId.py b/scripts/sbom/sbom/spdx/spdxId.py new file mode 100644 index 000000000000..589e85c5f706 --- /dev/null +++ b/scripts/sbom/sbom/spdx/spdxId.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from itertools import count +from typing import Iterator + +SpdxId = str + + +class SpdxIdGenerator: + _namespace: str + _prefix: str | None = None + _counter: Iterator[int] + + def __init__(self, namespace: str, prefix: str | None = None) -> None: + """ + Initialize the SPDX ID generator with a namespace. + + Args: + namespace: The full namespace to use for generated IDs. + prefix: Optional. If provided, generated IDs will use this prefix instead of the full namespace. + """ + self._namespace = namespace + self._prefix = prefix + self._counter = count(0) + + def generate(self) -> SpdxId: + return f"{f'{self._prefix}:' if self._prefix else self._namespace}{next(self._counter)}" + + @property + def prefix(self) -> str | None: + return self._prefix + + @property + def namespace(self) -> str: + return self._namespace From a68a29a1cc3ae6c129acdf945964dea16c8a49dc Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:55 +0200 Subject: [PATCH 1570/5214] scripts/sbom: add JSON-LD serialization Add infrastructure to serialize an SPDX graph as a JSON-LD document. NamespaceMaps in the SPDX document are converted to custom prefixes in the @context field of the JSON-LD output. The SBOM tool uses NamespaceMaps solely to shorten SPDX IDs, avoiding repetition of full namespace URIs by using short prefixes. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- Makefile | 3 +- scripts/sbom/sbom.py | 56 +++++++++++++++++++ scripts/sbom/sbom/config.py | 56 +++++++++++++++++++ scripts/sbom/sbom/spdx_graph/__init__.py | 7 +++ .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 36 ++++++++++++ .../sbom/sbom/spdx_graph/spdx_graph_model.py | 36 ++++++++++++ 6 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 scripts/sbom/sbom/spdx_graph/__init__.py create mode 100644 scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py create mode 100644 scripts/sbom/sbom/spdx_graph/spdx_graph_model.py diff --git a/Makefile b/Makefile index 4c6133af5549..2443d4c82454 100644 --- a/Makefile +++ b/Makefile @@ -2212,7 +2212,8 @@ quiet_cmd_sbom = GEN $(sbom_targets) --src-tree $(abspath $(srctree)) \ --obj-tree $(abspath $(objtree)) \ --roots-file "$(tmp-target)" \ - --output-directory $(abspath $(objtree)); + --output-directory $(abspath $(objtree)) \ + --generate-spdx; PHONY += sbom sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules modules.order) $(call cmd,sbom) diff --git a/scripts/sbom/sbom.py b/scripts/sbom/sbom.py index d700e4f294f7..764175b9c893 100644 --- a/scripts/sbom/sbom.py +++ b/scripts/sbom/sbom.py @@ -6,13 +6,18 @@ Compute software bill of materials in SPDX format describing a kernel build. """ +import json import logging import os import sys import time +import uuid import sbom.sbom_logging as sbom_logging from sbom.config import get_config from sbom.path_utils import is_relative_to +from sbom.spdx import JsonLdSpdxDocument, SpdxIdGenerator +from sbom.spdx.core import CreationInfo, SpdxDocument +from sbom.spdx_graph import SpdxIdGeneratorCollection, build_spdx_graphs from sbom.cmd_graph import CmdGraph @@ -71,6 +76,57 @@ def main(): f.write("\n".join(str(file_path) for file_path in used_files)) logging.debug(f"Successfully saved {used_files_path}") + if config.generate_spdx is False: + _exit_with_summary(config.write_output_on_error) + return + + # Build SPDX Documents + logging.debug("Start generating SPDX graph based on cmd graph") + start_time = time.time() + + # The real uuid will be generated based on the content of the SPDX graphs + # to ensure that the same SPDX document is always assigned the same uuid. + PLACEHOLDER_UUID = "00000000-0000-0000-0000-000000000000" + spdx_id_base_namespace = f"{config.spdxId_prefix}{PLACEHOLDER_UUID}/" + spdx_id_generators = SpdxIdGeneratorCollection( + base=SpdxIdGenerator(prefix="p", namespace=spdx_id_base_namespace), + source=SpdxIdGenerator(prefix="s", namespace=f"{spdx_id_base_namespace}source/"), + build=SpdxIdGenerator(prefix="b", namespace=f"{spdx_id_base_namespace}build/"), + output=SpdxIdGenerator(prefix="o", namespace=f"{spdx_id_base_namespace}output/"), + ) + + spdx_graphs = build_spdx_graphs( + cmd_graph, + spdx_id_generators, + config, + ) + spdx_id_uuid = uuid.uuid5( + uuid.NAMESPACE_URL, + "".join( + json.dumps(element.to_dict()) for spdx_graph in spdx_graphs.values() for element in spdx_graph.to_list() + ), + ) + logging.debug(f"Generated SPDX graph in {time.time() - start_time} seconds") + + if not sbom_logging.has_errors() or config.write_output_on_error: + for kernel_sbom_kind, spdx_graph in spdx_graphs.items(): + spdx_graph_objects = spdx_graph.to_list() + # Add warning and error summary to creation info comment + creation_info = next(element for element in spdx_graph_objects if isinstance(element, CreationInfo)) + creation_info.comment = "\n".join([ + sbom_logging.summarize_warnings(), + sbom_logging.summarize_errors(), + ]).strip() + # Replace Placeholder uuid with real uuid for spdxIds + spdx_document = next(element for element in spdx_graph_objects if isinstance(element, SpdxDocument)) + for namespaceMap in spdx_document.namespaceMap: + namespaceMap.namespace = namespaceMap.namespace.replace(PLACEHOLDER_UUID, str(spdx_id_uuid)) + # Serialize SPDX graph to JSON-LD + spdx_doc = JsonLdSpdxDocument(graph=spdx_graph_objects) + save_path = os.path.join(config.output_directory, config.spdx_file_names[kernel_sbom_kind]) + spdx_doc.save(save_path, config.prettify_json) + logging.debug(f"Successfully saved {save_path}") + _exit_with_summary(config.write_output_on_error) diff --git a/scripts/sbom/sbom/config.py b/scripts/sbom/sbom/config.py index b8c1a2b404df..98c7d939364d 100644 --- a/scripts/sbom/sbom/config.py +++ b/scripts/sbom/sbom/config.py @@ -3,11 +3,18 @@ import argparse from dataclasses import dataclass +from enum import Enum import os from typing import Any from sbom.path_utils import PathStr +class KernelSpdxDocumentKind(Enum): + SOURCE = "source" + BUILD = "build" + OUTPUT = "output" + + @dataclass class KernelSbomConfig: src_tree: PathStr @@ -19,6 +26,13 @@ class KernelSbomConfig: root_paths: list[PathStr] """List of paths to root outputs (relative to obj_tree) to base the SBOM on.""" + generate_spdx: bool + """Whether to generate SPDX SBOM documents. If False, no SPDX files are created.""" + + spdx_file_names: dict[KernelSpdxDocumentKind, str] + """If `generate_spdx` is True, defines the file names for each SPDX SBOM kind + (source, build, output) to store on disk.""" + generate_used_files: bool """Whether to generate a flat list of all source files used in the build. If False, no used-files document is created.""" @@ -38,6 +52,12 @@ class KernelSbomConfig: write_output_on_error: bool """Whether to write output documents even if errors occur.""" + spdxId_prefix: str + """Prefix to use for all SPDX element IDs.""" + + prettify_json: bool + """Whether to pretty-print generated SPDX JSON documents.""" + def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, Any]: """ @@ -67,6 +87,15 @@ def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, Any]: "--roots-file", help="Path to a file containing the root paths (one per line). Cannot be used together with --roots.", ) + parser.add_argument( + "--generate-spdx", + action="store_true", + default=False, + help=( + "Whether to create sbom-source.spdx.json, sbom-build.spdx.json and " + "sbom-output.spdx.json documents (default: False)" + ), + ) parser.add_argument( "--generate-used-files", action="store_true", @@ -114,6 +143,20 @@ def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, Any]: ), ) + # SPDX specific options + spdx_group = parser.add_argument_group("SPDX options", "Options for customizing SPDX document generation") + spdx_group.add_argument( + "--spdxId-prefix", + default="urn:spdx.dev:", + help="The prefix to use for all spdxId properties. (default: urn:spdx.dev:)", + ) + spdx_group.add_argument( + "--prettify-json", + action="store_true", + default=False, + help="Whether to pretty print the generated spdx.json documents (default: False)", + ) + args = vars(parser.parse_args()) return args @@ -144,6 +187,7 @@ def get_config() -> KernelSbomConfig: root_paths = args["roots"] _validate_path_arguments(parser, src_tree, obj_tree, root_paths) + generate_spdx = args["generate_spdx"] generate_used_files = args["generate_used_files"] output_directory = os.path.realpath(args["output_directory"]) debug = args["debug"] @@ -151,19 +195,31 @@ def get_config() -> KernelSbomConfig: fail_on_unknown_build_command = not args["do_not_fail_on_unknown_build_command"] write_output_on_error = args["write_output_on_error"] + spdxId_prefix = args["spdxId_prefix"] + prettify_json = args["prettify_json"] + # Hardcoded config + spdx_file_names = { + KernelSpdxDocumentKind.SOURCE: "sbom-source.spdx.json", + KernelSpdxDocumentKind.BUILD: "sbom-build.spdx.json", + KernelSpdxDocumentKind.OUTPUT: "sbom-output.spdx.json", + } used_files_file_name = "sbom.used-files.txt" return KernelSbomConfig( src_tree=src_tree, obj_tree=obj_tree, root_paths=root_paths, + generate_spdx=generate_spdx, + spdx_file_names=spdx_file_names, generate_used_files=generate_used_files, used_files_file_name=used_files_file_name, output_directory=output_directory, debug=debug, fail_on_unknown_build_command=fail_on_unknown_build_command, write_output_on_error=write_output_on_error, + spdxId_prefix=spdxId_prefix, + prettify_json=prettify_json, ) diff --git a/scripts/sbom/sbom/spdx_graph/__init__.py b/scripts/sbom/sbom/spdx_graph/__init__.py new file mode 100644 index 000000000000..3557b1d51bf9 --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/__init__.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from .build_spdx_graphs import build_spdx_graphs +from .spdx_graph_model import SpdxIdGeneratorCollection + +__all__ = ["build_spdx_graphs", "SpdxIdGeneratorCollection"] diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py new file mode 100644 index 000000000000..bb3db4e423da --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + + +from typing import Protocol + +from sbom.config import KernelSpdxDocumentKind +from sbom.cmd_graph import CmdGraph +from sbom.path_utils import PathStr +from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection + + +class SpdxGraphConfig(Protocol): + obj_tree: PathStr + src_tree: PathStr + + +def build_spdx_graphs( + cmd_graph: CmdGraph, + spdx_id_generators: SpdxIdGeneratorCollection, + config: SpdxGraphConfig, +) -> dict[KernelSpdxDocumentKind, SpdxGraph]: + """ + Builds SPDX graphs (output, source, and build) based on a cmd dependency graph. + If the source and object trees are identical, no dedicated source graph can be created. + In that case the source files are added to the build graph instead. + + Args: + cmd_graph: The dependency graph of a kernel build. + spdx_id_generators: Collection of SPDX ID generators. + config: Configuration options. + + Returns: + Dictionary of SPDX graphs + """ + return {} diff --git a/scripts/sbom/sbom/spdx_graph/spdx_graph_model.py b/scripts/sbom/sbom/spdx_graph/spdx_graph_model.py new file mode 100644 index 000000000000..682194d4362a --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/spdx_graph_model.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +from sbom.spdx.core import CreationInfo, SoftwareAgent, SpdxDocument, SpdxObject +from sbom.spdx.software import Sbom +from sbom.spdx.spdxId import SpdxIdGenerator + + +@dataclass +class SpdxGraph: + """Represents the complete graph of a single SPDX document.""" + + spdx_document: SpdxDocument + agent: SoftwareAgent + creation_info: CreationInfo + sbom: Sbom + + def to_list(self) -> list[SpdxObject]: + return [ + self.spdx_document, + self.agent, + self.creation_info, + self.sbom, + *self.sbom.element, + ] + + +@dataclass +class SpdxIdGeneratorCollection: + """Holds SPDX ID generators for different document types to ensure globally unique SPDX IDs.""" + + base: SpdxIdGenerator + source: SpdxIdGenerator + build: SpdxIdGenerator + output: SpdxIdGenerator From 11f9c14d0e498933b586f8de2e3d0f0c9b22dfee Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:56 +0200 Subject: [PATCH 1571/5214] scripts/sbom: add shared SPDX elements Implement shared SPDX elements used in all three documents. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/sbom/config.py | 9 ++++++ .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 5 ++- .../sbom/spdx_graph/shared_spdx_elements.py | 32 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 scripts/sbom/sbom/spdx_graph/shared_spdx_elements.py diff --git a/scripts/sbom/sbom/config.py b/scripts/sbom/sbom/config.py index 98c7d939364d..b1dd30790f5b 100644 --- a/scripts/sbom/sbom/config.py +++ b/scripts/sbom/sbom/config.py @@ -3,6 +3,7 @@ import argparse from dataclasses import dataclass +from datetime import datetime, timezone from enum import Enum import os from typing import Any @@ -52,6 +53,9 @@ class KernelSbomConfig: write_output_on_error: bool """Whether to write output documents even if errors occur.""" + created: datetime + """Datetime to use for the SPDX created property of the CreationInfo element.""" + spdxId_prefix: str """Prefix to use for all SPDX element IDs.""" @@ -195,6 +199,10 @@ def get_config() -> KernelSbomConfig: fail_on_unknown_build_command = not args["do_not_fail_on_unknown_build_command"] write_output_on_error = args["write_output_on_error"] + created = datetime.fromtimestamp( + max([os.path.getmtime(os.path.join(obj_tree, root_path)) for root_path in root_paths]), + tz=timezone.utc, + ) spdxId_prefix = args["spdxId_prefix"] prettify_json = args["prettify_json"] @@ -218,6 +226,7 @@ def get_config() -> KernelSbomConfig: debug=debug, fail_on_unknown_build_command=fail_on_unknown_build_command, write_output_on_error=write_output_on_error, + created=created, spdxId_prefix=spdxId_prefix, prettify_json=prettify_json, ) diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py index bb3db4e423da..9c47258a31c6 100644 --- a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -1,18 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only OR MIT # Copyright (C) 2025 TNG Technology Consulting GmbH - +from datetime import datetime from typing import Protocol from sbom.config import KernelSpdxDocumentKind from sbom.cmd_graph import CmdGraph from sbom.path_utils import PathStr from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection +from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements class SpdxGraphConfig(Protocol): obj_tree: PathStr src_tree: PathStr + created: datetime def build_spdx_graphs( @@ -33,4 +35,5 @@ def build_spdx_graphs( Returns: Dictionary of SPDX graphs """ + shared_elements = SharedSpdxElements.create(spdx_id_generators.base, config.created) return {} diff --git a/scripts/sbom/sbom/spdx_graph/shared_spdx_elements.py b/scripts/sbom/sbom/spdx_graph/shared_spdx_elements.py new file mode 100644 index 000000000000..115e8778a467 --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/shared_spdx_elements.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +from datetime import datetime, timezone +from sbom.spdx.core import CreationInfo, SoftwareAgent +from sbom.spdx.spdxId import SpdxIdGenerator + + +@dataclass(frozen=True) +class SharedSpdxElements: + agent: SoftwareAgent + creation_info: CreationInfo + + @classmethod + def create(cls, spdx_id_generator: SpdxIdGenerator, created: datetime) -> "SharedSpdxElements": + """ + Creates shared SPDX elements used across multiple documents. + + Args: + spdx_id_generator: Generator for creating SPDX IDs. + created: SPDX 'created' property used for the creation info. + + Returns: + SharedSpdxElements with agent and creation info. + """ + agent = SoftwareAgent( + spdxId=spdx_id_generator.generate(), + name="KernelSbom", + ) + creation_info = CreationInfo(createdBy=[agent], created=created.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")) + return SharedSpdxElements(agent=agent, creation_info=creation_info) From ef0675c8712fea2db638594e619e7a557c2502d6 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:57 +0200 Subject: [PATCH 1572/5214] scripts/sbom: collect file metadata Implement the kernel_file module that collects file metadata, including license identifier for source files, SHA-256 hash, Git blob object ID, an estimation of the file type, and whether files belong to the source, build, or output SBOM. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 2 + scripts/sbom/sbom/spdx_graph/kernel_file.py | 315 ++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 scripts/sbom/sbom/spdx_graph/kernel_file.py diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py index 9c47258a31c6..0f95f99d560a 100644 --- a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -7,6 +7,7 @@ from typing import Protocol from sbom.config import KernelSpdxDocumentKind from sbom.cmd_graph import CmdGraph from sbom.path_utils import PathStr +from sbom.spdx_graph.kernel_file import KernelFileCollection from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements @@ -36,4 +37,5 @@ def build_spdx_graphs( Dictionary of SPDX graphs """ shared_elements = SharedSpdxElements.create(spdx_id_generators.base, config.created) + kernel_files = KernelFileCollection.create(cmd_graph, config.obj_tree, config.src_tree, spdx_id_generators) return {} diff --git a/scripts/sbom/sbom/spdx_graph/kernel_file.py b/scripts/sbom/sbom/spdx_graph/kernel_file.py new file mode 100644 index 000000000000..505f25f66ebb --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/kernel_file.py @@ -0,0 +1,315 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +from enum import Enum +import hashlib +import os +import re +from sbom.cmd_graph import CmdGraph +from sbom.path_utils import PathStr, is_relative_to +from sbom.spdx import SpdxId, SpdxIdGenerator +from sbom.spdx.core import Hash +from sbom.spdx.software import ContentIdentifier, File, SoftwarePurpose +import sbom.sbom_logging as sbom_logging +from sbom.spdx_graph.spdx_graph_model import SpdxIdGeneratorCollection + + +class KernelFileLocation(Enum): + """Represents the location of a file relative to the source/object trees.""" + + SOURCE_TREE = "source_tree" + """File is located in the source tree.""" + OBJ_TREE = "obj_tree" + """File is located in the object tree.""" + EXTERNAL = "external" + """File is located outside both source and object trees.""" + BOTH = "both" + """File is located in a folder that is both source and object tree.""" + + +@dataclass +class KernelFile: + """kernel-specific metadata used to generate an SPDX File element.""" + + absolute_path: PathStr + """Absolute path of the file.""" + file_location: KernelFileLocation + """Location of the file relative to the source/object trees.""" + name: str + """Name of the file element. Should be relative to the source tree if + file_location equals SOURCE_TREE and relative to the object tree if + file_location equals OBJ_TREE. If file_location equals EXTERNAL, the + absolute path is used.""" + license_identifier: str | None + """SPDX license ID if file_location equals SOURCE_TREE or BOTH; otherwise None.""" + spdx_id_generator: SpdxIdGenerator + """Generator for the SPDX ID of the file element.""" + + _spdx_file_element: File | None = None + + @classmethod + def create( + cls, + absolute_path: PathStr, + obj_tree: PathStr, + src_tree: PathStr, + spdx_id_generators: SpdxIdGeneratorCollection, + is_output: bool, + ) -> "KernelFile": + is_in_obj_tree = is_relative_to(absolute_path, obj_tree) + is_in_src_tree = is_relative_to(absolute_path, src_tree) + + # file element name should be relative to output or src tree if possible + if not is_in_src_tree and not is_in_obj_tree: + file_element_name = str(absolute_path) + file_location = KernelFileLocation.EXTERNAL + spdx_id_generator = spdx_id_generators.source if src_tree != obj_tree else spdx_id_generators.build + elif is_in_src_tree and src_tree == obj_tree: + file_element_name = os.path.relpath(absolute_path, obj_tree) + file_location = KernelFileLocation.BOTH + spdx_id_generator = spdx_id_generators.output if is_output else spdx_id_generators.build + elif is_in_obj_tree: + file_element_name = os.path.relpath(absolute_path, obj_tree) + file_location = KernelFileLocation.OBJ_TREE + spdx_id_generator = spdx_id_generators.output if is_output else spdx_id_generators.build + else: + file_element_name = os.path.relpath(absolute_path, src_tree) + file_location = KernelFileLocation.SOURCE_TREE + spdx_id_generator = spdx_id_generators.source + + # parse spdx license identifier + license_identifier = ( + _parse_spdx_license_identifier(absolute_path) + if file_location == KernelFileLocation.SOURCE_TREE or file_location == KernelFileLocation.BOTH + else None + ) + + return KernelFile( + absolute_path, + file_location, + file_element_name, + license_identifier, + spdx_id_generator, + ) + + @property + def spdx_file_element(self) -> File: + if self._spdx_file_element is None: + self._spdx_file_element = _build_file_element( + self.absolute_path, + self.name, + self.spdx_id_generator.generate(), + self.file_location, + ) + return self._spdx_file_element + + +@dataclass +class KernelFileCollection: + """Collection of kernel files.""" + + source: dict[PathStr, KernelFile] + build: dict[PathStr, KernelFile] + output: dict[PathStr, KernelFile] + external: dict[PathStr, KernelFile] + + @classmethod + def create( + cls, + cmd_graph: CmdGraph, + obj_tree: PathStr, + src_tree: PathStr, + spdx_id_generators: SpdxIdGeneratorCollection, + ) -> "KernelFileCollection": + source: dict[PathStr, KernelFile] = {} + build: dict[PathStr, KernelFile] = {} + output: dict[PathStr, KernelFile] = {} + external: dict[PathStr, KernelFile] = {} + root_node_paths = {node.absolute_path for node in cmd_graph.roots} + for node in cmd_graph: + is_root = node.absolute_path in root_node_paths + kernel_file = KernelFile.create( + node.absolute_path, + obj_tree, + src_tree, + spdx_id_generators, + is_root, + ) + if is_root: + output[kernel_file.absolute_path] = kernel_file + elif kernel_file.file_location == KernelFileLocation.SOURCE_TREE: + source[kernel_file.absolute_path] = kernel_file + elif kernel_file.file_location == KernelFileLocation.EXTERNAL: + external[kernel_file.absolute_path] = kernel_file + else: + build[kernel_file.absolute_path] = kernel_file + + return KernelFileCollection(source, build, output, external) + + def to_dict(self) -> dict[PathStr, KernelFile]: + return {**self.source, **self.build, **self.output, **self.external} + + +def _build_file_element(absolute_path: PathStr, name: str, spdx_id: SpdxId, file_location: KernelFileLocation) -> File: + verifiedUsing: list[Hash] = [] + content_identifier: list[ContentIdentifier] = [] + if os.path.isfile(absolute_path): + verifiedUsing = [Hash(algorithm="sha256", hashValue=_sha256(absolute_path))] + content_identifier = [ + ContentIdentifier( + software_contentIdentifierType="gitoid", + software_contentIdentifierValue=_git_blob_oid(absolute_path), + ) + ] + elif file_location == KernelFileLocation.EXTERNAL: + sbom_logging.warning( + "Cannot compute hash for {absolute_path} because file does not exist.", + absolute_path=absolute_path, + ) + else: + sbom_logging.error( + "Cannot compute hash for {absolute_path} because file does not exist.", + absolute_path=absolute_path, + ) + + # primary purpose + primary_purpose = _get_primary_purpose(absolute_path) + + return File( + spdxId=spdx_id, + name=name, + verifiedUsing=verifiedUsing, + software_primaryPurpose=primary_purpose, + software_contentIdentifier=content_identifier, + ) + + +def _sha256(file_path: PathStr, chunk_size: int = 1 << 20) -> str: + """Compute the SHA-256 hex digest of a file, reading it in chunks of chunk_size bytes.""" + h = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(chunk_size), b""): + h.update(chunk) + return h.hexdigest() + + +def _git_blob_oid(file_path: str, chunk_size: int = 1 << 20) -> str: + """Compute the Git blob object ID (SHA-1 hex) for a file, like `git hash-object`, reading it in chunks of chunk_size bytes.""" + h = hashlib.sha1() + h.update(f"blob {os.path.getsize(file_path)}\0".encode()) + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(chunk_size), b""): + h.update(chunk) + return h.hexdigest() + + +# REUSE-IgnoreStart +SPDX_LICENSE_IDENTIFIER_PATTERN = re.compile( + r"SPDX-License-Identifier:" # literal tag + r"\s*" # optional whitespace after colon + r"(?P.*?)" # license expression (non-greedy, stops before terminator) + r"(?:\s*" # optional whitespace before terminator (not captured) + r"(-->|\*/|$))", # terminator: XML "-->", C-style "*/", or end of line + re.MULTILINE, # match end of each line, not just end of string +) +# REUSE-IgnoreEnd + + +def _parse_spdx_license_identifier(absolute_path: str, max_bytes: int = 512) -> str | None: + """ + Extracts the SPDX-License-Identifier from the beginning of a source file. + + Args: + absolute_path: Path to the source file. + max_bytes: Maximum number of bytes to scan for the license identifier. + + Returns: + The license identifier string (e.g., 'GPL-2.0-only') if found, otherwise None. + """ + try: + with open(absolute_path, "r", encoding="utf-8") as f: + match = SPDX_LICENSE_IDENTIFIER_PATTERN.search(f.read(max_bytes)) + if match: + return match.group("id") + except (UnicodeDecodeError, OSError): + return None + return None + + +def _get_primary_purpose(absolute_path: PathStr) -> SoftwarePurpose | None: + def ends_with(suffixes: list[str]) -> bool: + return any(absolute_path.endswith(suffix) for suffix in suffixes) + + def includes_path_segments(path_segments: list[str]) -> bool: + return any(segment in absolute_path for segment in path_segments) + + # Source code + if ends_with([".c", ".h", ".S", ".s", ".rs", ".pl", "gen_smb1_mapping", "gen_smb2_mapping"]): + return "source" + + # Libraries + if ends_with([".a", ".so", ".so.raw", ".rlib"]): + return "library" + + # Archives + if ends_with([".xz", ".cpio", ".gz", ".tar", ".zip", "piggy_data"]): + return "archive" + + # Applications + if ends_with(["bzImage", "Image", ".efi"]): + return "application" + + # Executables / machine code + if ends_with([".bin", ".elf", "vmlinux", "vmlinux.unstripped", "vmlinuz", "bpfilter_umh"]): + return "executable" + + # Kernel modules + if ends_with([".ko"]): + return "module" + + # Data files + if ends_with( + [ + ".tbl", + ".relocs", + ".rmeta", + ".in", + ".dbg", + ".x509", + ".pbm", + ".ppm", + ".dtb", + ".uc", + ".inc", + ".dts", + ".dtsi", + ".dtbo", + ".xml", + ".ro", + "initramfs_inc_data", + "default_cpio_list", + "x509_certificate_list", + "utf8data.c_shipped", + "blacklist_hash_list", + "x509_revocation_list", + "cpucaps", + "sysreg", + "mach-types", + ] + ) or includes_path_segments(["drivers/gpu/drm/radeon/reg_srcs/"]): + return "data" + + # Configuration files + if ends_with([".pem", ".key", ".conf", ".config", ".cfg", ".bconf"]): + return "configuration" + + # Documentation + if ends_with([".md"]): + return "documentation" + + # Other / miscellaneous + if ends_with([".o", ".tmp"]): + return "other" + + sbom_logging.warning("Could not infer primary purpose for {absolute_path}", absolute_path=absolute_path) From b01912114e2c1b378287fcdd013bb9a894d1879e Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:58 +0200 Subject: [PATCH 1573/5214] scripts/sbom: add SPDX output graph Implement the SPDX output graph which contains the distributable build outputs and high level metadata about the build. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- Makefile | 5 +- scripts/sbom/sbom/config.py | 64 ++++++ .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 18 +- .../sbom/sbom/spdx_graph/spdx_output_graph.py | 187 ++++++++++++++++++ 4 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 scripts/sbom/sbom/spdx_graph/spdx_output_graph.py diff --git a/Makefile b/Makefile index 2443d4c82454..e02d2a614c53 100644 --- a/Makefile +++ b/Makefile @@ -2213,7 +2213,10 @@ quiet_cmd_sbom = GEN $(sbom_targets) --obj-tree $(abspath $(objtree)) \ --roots-file "$(tmp-target)" \ --output-directory $(abspath $(objtree)) \ - --generate-spdx; + --generate-spdx \ + --package-license "GPL-2.0 WITH Linux-syscall-note" \ + --package-version "$(KERNELVERSION)" \ + --write-output-on-error; PHONY += sbom sbom: $(notdir $(KBUILD_IMAGE)) include/generated/autoconf.h $(if $(CONFIG_MODULES),modules modules.order) $(call cmd,sbom) diff --git a/scripts/sbom/sbom/config.py b/scripts/sbom/sbom/config.py index b1dd30790f5b..6811f782943e 100644 --- a/scripts/sbom/sbom/config.py +++ b/scripts/sbom/sbom/config.py @@ -59,6 +59,21 @@ class KernelSbomConfig: spdxId_prefix: str """Prefix to use for all SPDX element IDs.""" + build_type: str + """SPDX buildType property to use for all Build elements.""" + + build_id: str | None + """SPDX buildId property to use for all Build elements.""" + + package_license: str + """License expression applied to all SPDX Packages.""" + + package_version: str | None + """Version string applied to all SPDX Packages.""" + + package_copyright_text: str | None + """Copyright text applied to all SPDX Packages.""" + prettify_json: bool """Whether to pretty-print generated SPDX JSON documents.""" @@ -154,6 +169,40 @@ def _parse_cli_arguments(parser: argparse.ArgumentParser) -> dict[str, Any]: default="urn:spdx.dev:", help="The prefix to use for all spdxId properties. (default: urn:spdx.dev:)", ) + spdx_group.add_argument( + "--build-type", + default="urn:spdx.dev:Kbuild", + help="The SPDX buildType property to use for all Build elements. (default: urn:spdx.dev:Kbuild)", + ) + spdx_group.add_argument( + "--build-id", + default=None, + help="The SPDX buildId property to use for all Build elements.\n" + "If not provided the spdxId of the high level Build element is used as the buildId. (default: None)", + ) + spdx_group.add_argument( + "--package-license", + default="NOASSERTION", + help=( + "The SPDX licenseExpression property to use for the LicenseExpression " + "linked to all SPDX Package elements. (default: NOASSERTION)" + ), + ) + spdx_group.add_argument( + "--package-version", + default=None, + help="The SPDX packageVersion property to use for all SPDX Package elements. (default: None)", + ) + spdx_group.add_argument( + "--package-copyright-text", + default=None, + help=( + "The SPDX copyrightText property to use for all SPDX Package elements.\n" + "If not specified, and if a COPYING file exists in the source tree,\n" + "the package-copyright-text is set to the content of this file. " + "(default: None)" + ), + ) spdx_group.add_argument( "--prettify-json", action="store_true", @@ -204,6 +253,16 @@ def get_config() -> KernelSbomConfig: tz=timezone.utc, ) spdxId_prefix = args["spdxId_prefix"] + build_type = args["build_type"] + build_id = args["build_id"] + package_license = args["package_license"] + package_version = args["package_version"] if args["package_version"] is not None else None + package_copyright_text: str | None = None + if args["package_copyright_text"] is not None: + package_copyright_text = args["package_copyright_text"] + elif os.path.isfile(copying_path := os.path.join(src_tree, "COPYING")): + with open(copying_path, "r", encoding="utf-8") as f: + package_copyright_text = f.read() prettify_json = args["prettify_json"] # Hardcoded config @@ -228,6 +287,11 @@ def get_config() -> KernelSbomConfig: write_output_on_error=write_output_on_error, created=created, spdxId_prefix=spdxId_prefix, + build_type=build_type, + build_id=build_id, + package_license=package_license, + package_version=package_version, + package_copyright_text=package_copyright_text, prettify_json=prettify_json, ) diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py index 0f95f99d560a..2af0fbe6cdbe 100644 --- a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -10,12 +10,18 @@ from sbom.path_utils import PathStr from sbom.spdx_graph.kernel_file import KernelFileCollection from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements +from sbom.spdx_graph.spdx_output_graph import SpdxOutputGraph class SpdxGraphConfig(Protocol): obj_tree: PathStr src_tree: PathStr created: datetime + build_type: str + build_id: str | None + package_license: str + package_version: str | None + package_copyright_text: str | None def build_spdx_graphs( @@ -38,4 +44,14 @@ def build_spdx_graphs( """ shared_elements = SharedSpdxElements.create(spdx_id_generators.base, config.created) kernel_files = KernelFileCollection.create(cmd_graph, config.obj_tree, config.src_tree, spdx_id_generators) - return {} + output_graph = SpdxOutputGraph.create( + root_files=list(kernel_files.output.values()), + shared_elements=shared_elements, + spdx_id_generators=spdx_id_generators, + config=config, + ) + spdx_graphs: dict[KernelSpdxDocumentKind, SpdxGraph] = { + KernelSpdxDocumentKind.OUTPUT: output_graph, + } + + return spdx_graphs diff --git a/scripts/sbom/sbom/spdx_graph/spdx_output_graph.py b/scripts/sbom/sbom/spdx_graph/spdx_output_graph.py new file mode 100644 index 000000000000..ff9b2c31fb04 --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/spdx_output_graph.py @@ -0,0 +1,187 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +import os +from typing import Protocol +from sbom.environment import Environment +from sbom.path_utils import PathStr +from sbom.spdx.build import Build +from sbom.spdx.core import DictionaryEntry, NamespaceMap, Relationship, SpdxDocument +from sbom.spdx.simplelicensing import LicenseExpression +from sbom.spdx.software import File, Package, Sbom +from sbom.spdx.spdxId import SpdxIdGenerator +from sbom.spdx_graph.kernel_file import KernelFile +from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements +from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection + + +class SpdxOutputGraphConfig(Protocol): + obj_tree: PathStr + src_tree: PathStr + build_type: str + build_id: str | None + package_license: str + package_version: str | None + package_copyright_text: str | None + + +@dataclass +class SpdxOutputGraph(SpdxGraph): + """SPDX graph representing distributable output files""" + + high_level_build_element: Build + + @classmethod + def create( + cls, + root_files: list[KernelFile], + shared_elements: SharedSpdxElements, + spdx_id_generators: SpdxIdGeneratorCollection, + config: SpdxOutputGraphConfig, + ) -> "SpdxOutputGraph": + """ + Args: + root_files: List of distributable output files which act as roots + of the dependency graph. + shared_elements: Shared SPDX elements used across multiple documents. + spdx_id_generators: Collection of SPDX ID generators. + config: Configuration options. + + Returns: + SpdxOutputGraph: The SPDX output graph. + """ + # SpdxDocument + spdx_document = SpdxDocument( + spdxId=spdx_id_generators.output.generate(), + profileConformance=["core", "software", "build", "simpleLicensing"], + namespaceMap=[ + NamespaceMap(prefix=generator.prefix, namespace=generator.namespace) + for generator in [spdx_id_generators.output, spdx_id_generators.base] + if generator.prefix is not None + ], + ) + + # Sbom + sbom = Sbom( + spdxId=spdx_id_generators.output.generate(), + software_sbomType=["build"], + ) + + # High-level Build elements + config_source_element = KernelFile.create( + absolute_path=os.path.join(config.obj_tree, ".config"), + obj_tree=config.obj_tree, + src_tree=config.src_tree, + spdx_id_generators=spdx_id_generators, + is_output=True, + ).spdx_file_element + high_level_build_element, high_level_build_element_hasOutput_relationship = _high_level_build_elements( + config.build_type, + config.build_id, + config_source_element, + spdx_id_generators.output, + ) + + # Root file elements + root_file_elements: list[File] = [file.spdx_file_element for file in root_files] + + # Package elements + package_elements = [ + Package( + spdxId=spdx_id_generators.output.generate(), + name=_get_package_name(file.name), + software_packageVersion=config.package_version, + software_copyrightText=config.package_copyright_text, + comment=f"Architecture={arch}" if (arch := Environment.ARCH() or Environment.SRCARCH()) else None, + software_primaryPurpose=file.software_primaryPurpose, + ) + for file in root_file_elements + ] + package_hasDistributionArtifact_file_relationships = [ + Relationship( + spdxId=spdx_id_generators.output.generate(), + relationshipType="hasDistributionArtifact", + from_=package, + to=[file], + ) + for package, file in zip(package_elements, root_file_elements) + ] + package_license_expression = LicenseExpression( + spdxId=spdx_id_generators.output.generate(), + simplelicensing_licenseExpression=config.package_license, + ) + package_hasDeclaredLicense_relationships = [ + Relationship( + spdxId=spdx_id_generators.output.generate(), + relationshipType="hasDeclaredLicense", + from_=package, + to=[package_license_expression], + ) + for package in package_elements + ] + + # Update relationships + spdx_document.rootElement = [sbom] + + sbom.rootElement = [*package_elements] + sbom.element = [ + config_source_element, + high_level_build_element, + high_level_build_element_hasOutput_relationship, + *root_file_elements, + *package_elements, + *package_hasDistributionArtifact_file_relationships, + package_license_expression, + *package_hasDeclaredLicense_relationships, + ] + + high_level_build_element_hasOutput_relationship.to = [*root_file_elements] + + output_graph = SpdxOutputGraph( + spdx_document, + shared_elements.agent, + shared_elements.creation_info, + sbom, + high_level_build_element, + ) + return output_graph + + +def _get_package_name(filename: str) -> str: + """ + Generates a SPDX package name from a filename. + Kernel images (bzImage, Image) get a descriptive name, others use the basename of the file. + """ + KERNEL_FILENAMES = ["bzImage", "Image"] + basename = os.path.basename(filename) + return f"Linux Kernel ({basename})" if basename in KERNEL_FILENAMES else basename + + +def _high_level_build_elements( + build_type: str, + build_id: str | None, + config_source_element: File, + spdx_id_generator: SpdxIdGenerator, +) -> tuple[Build, Relationship]: + build_spdxId = spdx_id_generator.generate() + high_level_build_element = Build( + spdxId=build_spdxId, + build_buildType=build_type, + build_buildId=build_id if build_id is not None else build_spdxId, + build_environment=[ + DictionaryEntry(key=key, value=value) + for key, value in Environment.KERNEL_BUILD_VARIABLES().items() + if value + ], + build_configSourceUri=[config_source_element.spdxId], + build_configSourceDigest=config_source_element.verifiedUsing, + ) + + high_level_build_element_hasOutput_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="hasOutput", + from_=high_level_build_element, + to=[], + ) + return high_level_build_element, high_level_build_element_hasOutput_relationship From e70c84a5649e6e233326a22732dc08f9c31d4f43 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:20:59 +0200 Subject: [PATCH 1574/5214] scripts/sbom: add SPDX source graph Implement the SPDX source graph which contains all source files involved during the build, along with the licensing information for each file. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 9 ++ .../sbom/sbom/spdx_graph/spdx_source_graph.py | 130 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 scripts/sbom/sbom/spdx_graph/spdx_source_graph.py diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py index 2af0fbe6cdbe..f2567d44960b 100644 --- a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -10,6 +10,7 @@ from sbom.path_utils import PathStr from sbom.spdx_graph.kernel_file import KernelFileCollection from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements +from sbom.spdx_graph.spdx_source_graph import SpdxSourceGraph from sbom.spdx_graph.spdx_output_graph import SpdxOutputGraph @@ -54,4 +55,12 @@ def build_spdx_graphs( KernelSpdxDocumentKind.OUTPUT: output_graph, } + if len(kernel_files.source) > 0: + spdx_graphs[KernelSpdxDocumentKind.SOURCE] = SpdxSourceGraph.create( + source_files=list(kernel_files.source.values()), + external_files=list(kernel_files.external.values()), + shared_elements=shared_elements, + spdx_id_generators=spdx_id_generators, + ) + return spdx_graphs diff --git a/scripts/sbom/sbom/spdx_graph/spdx_source_graph.py b/scripts/sbom/sbom/spdx_graph/spdx_source_graph.py new file mode 100644 index 000000000000..90880212dedd --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/spdx_source_graph.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +from sbom.spdx import SpdxIdGenerator +from sbom.spdx.core import Element, NamespaceMap, Relationship, SpdxDocument +from sbom.spdx.simplelicensing import LicenseExpression +from sbom.spdx.software import File, Sbom +from sbom.spdx_graph.kernel_file import KernelFile +from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements +from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection + + +@dataclass +class SpdxSourceGraph(SpdxGraph): + """SPDX graph representing source files""" + + @classmethod + def create( + cls, + source_files: list[KernelFile], + external_files: list[KernelFile], + shared_elements: SharedSpdxElements, + spdx_id_generators: SpdxIdGeneratorCollection, + ) -> "SpdxSourceGraph": + """ + Args: + source_files: List of files within the kernel source tree. + external_files: Files outside both source and object trees. + shared_elements: Shared SPDX elements used across multiple documents. + spdx_id_generators: Collection of SPDX ID generators. + + Returns: + SpdxSourceGraph: The SPDX source graph. + """ + # SpdxDocument + source_spdx_document = SpdxDocument( + spdxId=spdx_id_generators.source.generate(), + profileConformance=["core", "software", "simpleLicensing"], + namespaceMap=[ + NamespaceMap(prefix=generator.prefix, namespace=generator.namespace) + for generator in [spdx_id_generators.source, spdx_id_generators.base] + if generator.prefix is not None + ], + ) + + # Sbom + source_sbom = Sbom( + spdxId=spdx_id_generators.source.generate(), + software_sbomType=["source"], + ) + + # Src Tree Elements + src_tree_element = File( + spdxId=spdx_id_generators.source.generate(), + name="$(src_tree)", + software_fileKind="directory", + ) + src_tree_contains_relationship = Relationship( + spdxId=spdx_id_generators.source.generate(), + relationshipType="contains", + from_=src_tree_element, + to=[], + ) + + # Source file elements + source_file_elements: list[Element] = [file.spdx_file_element for file in source_files] + external_file_elements: list[Element] = [file.spdx_file_element for file in external_files] + + # Source file license elements + source_file_license_identifiers, source_file_license_relationships = source_file_license_elements( + source_files, spdx_id_generators.source + ) + + # Update relationships + source_spdx_document.rootElement = [source_sbom] + source_sbom.rootElement = [src_tree_element] + source_sbom.element = [ + src_tree_element, + src_tree_contains_relationship, + *source_file_elements, + *external_file_elements, + *source_file_license_identifiers, + *source_file_license_relationships, + ] + src_tree_contains_relationship.to = source_file_elements + + source_graph = SpdxSourceGraph( + source_spdx_document, + shared_elements.agent, + shared_elements.creation_info, + source_sbom, + ) + return source_graph + + +def source_file_license_elements( + source_files: list[KernelFile], spdx_id_generator: SpdxIdGenerator +) -> tuple[list[LicenseExpression], list[Relationship]]: + """ + Creates SPDX license expressions and links them to the given source files + via hasDeclaredLicense relationships. + + Args: + source_files: List of files within the kernel source tree. + spdx_id_generator: Generator for unique SPDX IDs. + + Returns: + Tuple of (license expressions, hasDeclaredLicense relationships). + """ + license_expressions: dict[str, LicenseExpression] = {} + for file in source_files: + if file.license_identifier is None or file.license_identifier in license_expressions: + continue + license_expressions[file.license_identifier] = LicenseExpression( + spdxId=spdx_id_generator.generate(), + simplelicensing_licenseExpression=file.license_identifier, + ) + + source_file_license_relationships = [ + Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="hasDeclaredLicense", + from_=file.spdx_file_element, + to=[license_expressions[file.license_identifier]], + ) + for file in source_files + if file.license_identifier is not None + ] + return ([*license_expressions.values()], source_file_license_relationships) From db8d07ef5e81457eb330240945d56d83a5a68b01 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:21:00 +0200 Subject: [PATCH 1575/5214] scripts/sbom: add SPDX build graph Implement the SPDX build graph to describe the relationships between source files in the source SBOM and output files in the output SBOM. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- .../sbom/sbom/spdx_graph/build_spdx_graphs.py | 17 + .../sbom/sbom/spdx_graph/spdx_build_graph.py | 318 ++++++++++++++++++ 2 files changed, 335 insertions(+) create mode 100644 scripts/sbom/sbom/spdx_graph/spdx_build_graph.py diff --git a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py index f2567d44960b..ee24e9eaf603 100644 --- a/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py +++ b/scripts/sbom/sbom/spdx_graph/build_spdx_graphs.py @@ -4,6 +4,7 @@ from datetime import datetime from typing import Protocol +import logging from sbom.config import KernelSpdxDocumentKind from sbom.cmd_graph import CmdGraph from sbom.path_utils import PathStr @@ -11,6 +12,7 @@ from sbom.spdx_graph.kernel_file import KernelFileCollection from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements from sbom.spdx_graph.spdx_source_graph import SpdxSourceGraph +from sbom.spdx_graph.spdx_build_graph import SpdxBuildGraph from sbom.spdx_graph.spdx_output_graph import SpdxOutputGraph @@ -62,5 +64,20 @@ def build_spdx_graphs( shared_elements=shared_elements, spdx_id_generators=spdx_id_generators, ) + else: + logging.info( + "Skipped creating a dedicated source SBOM because source files cannot be " + "reliably classified when the source and object trees are identical. " + "Added source files to the build SBOM instead." + ) + + build_graph = SpdxBuildGraph.create( + cmd_graph, + kernel_files, + shared_elements, + output_graph.high_level_build_element, + spdx_id_generators, + ) + spdx_graphs[KernelSpdxDocumentKind.BUILD] = build_graph return spdx_graphs diff --git a/scripts/sbom/sbom/spdx_graph/spdx_build_graph.py b/scripts/sbom/sbom/spdx_graph/spdx_build_graph.py new file mode 100644 index 000000000000..4d738bc3b3e2 --- /dev/null +++ b/scripts/sbom/sbom/spdx_graph/spdx_build_graph.py @@ -0,0 +1,318 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +from dataclasses import dataclass +from typing import Mapping +from sbom.cmd_graph import CmdGraph +from sbom.path_utils import PathStr +from sbom.spdx import SpdxIdGenerator +from sbom.spdx.build import Build +from sbom.spdx.core import ExternalMap, NamespaceMap, Relationship, SpdxDocument +from sbom.spdx.software import File, Sbom +from sbom.spdx_graph.kernel_file import KernelFileCollection +from sbom.spdx_graph.shared_spdx_elements import SharedSpdxElements +from sbom.spdx_graph.spdx_graph_model import SpdxGraph, SpdxIdGeneratorCollection +from sbom.spdx_graph.spdx_source_graph import source_file_license_elements + + +@dataclass +class SpdxBuildGraph(SpdxGraph): + """SPDX graph representing build dependencies connecting source files and + distributable output files""" + + @classmethod + def create( + cls, + cmd_graph: CmdGraph, + kernel_files: KernelFileCollection, + shared_elements: SharedSpdxElements, + high_level_build_element: Build, + spdx_id_generators: SpdxIdGeneratorCollection, + ) -> "SpdxBuildGraph": + if len(kernel_files.source) > 0: + return _create_spdx_build_graph( + cmd_graph, + kernel_files, + shared_elements, + high_level_build_element, + spdx_id_generators, + ) + else: + return _create_spdx_build_graph_with_mixed_sources( + cmd_graph, + kernel_files, + shared_elements, + high_level_build_element, + spdx_id_generators, + ) + + +def _create_spdx_build_graph( + cmd_graph: CmdGraph, + kernel_files: KernelFileCollection, + shared_elements: SharedSpdxElements, + high_level_build_element: Build, + spdx_id_generators: SpdxIdGeneratorCollection, +) -> SpdxBuildGraph: + """ + Creates an SPDX build graph where source and output files are referenced + from external documents. + + Args: + cmd_graph: The dependency graph of a kernel build. + kernel_files: Collection of categorized kernel files involved in the build. + shared_elements: SPDX elements shared across multiple documents. + high_level_build_element: The high-level Build element referenced by the build graph. + spdx_id_generators: Collection of generators for SPDX element IDs. + + Returns: + SpdxBuildGraph: The SPDX build graph connecting source files and distributable output files. + """ + # SpdxDocument + build_spdx_document = SpdxDocument( + spdxId=spdx_id_generators.build.generate(), + profileConformance=["core", "software", "build"], + namespaceMap=[ + NamespaceMap(prefix=generator.prefix, namespace=generator.namespace) + for generator in [ + spdx_id_generators.build, + spdx_id_generators.source, + spdx_id_generators.output, + spdx_id_generators.base, + ] + if generator.prefix is not None + ], + ) + + # Sbom + build_sbom = Sbom( + spdxId=spdx_id_generators.build.generate(), + software_sbomType=["build"], + ) + + # Src and object tree elements + obj_tree_element = File( + spdxId=spdx_id_generators.build.generate(), + name="$(obj_tree)", + software_fileKind="directory", + ) + obj_tree_contains_relationship = Relationship( + spdxId=spdx_id_generators.build.generate(), + relationshipType="contains", + from_=obj_tree_element, + to=[], + ) + + # File elements + build_file_elements = [file.spdx_file_element for file in kernel_files.build.values()] + file_relationships = _file_relationships( + cmd_graph=cmd_graph, + file_elements={key: file.spdx_file_element for key, file in kernel_files.to_dict().items()}, + high_level_build_element=high_level_build_element, + spdx_id_generator=spdx_id_generators.build, + ) + + # Update relationships + build_spdx_document.rootElement = [build_sbom] + + build_spdx_document.import_ = [ + *( + ExternalMap(externalSpdxId=file.spdx_file_element.spdxId) + for file in (*kernel_files.source.values(), *kernel_files.external.values()) + ), + ExternalMap(externalSpdxId=high_level_build_element.spdxId), + *(ExternalMap(externalSpdxId=file.spdx_file_element.spdxId) for file in kernel_files.output.values()), + ] + + build_sbom.rootElement = [obj_tree_element] + build_sbom.element = [ + obj_tree_element, + obj_tree_contains_relationship, + *build_file_elements, + *file_relationships, + ] + + obj_tree_contains_relationship.to = [ + *build_file_elements, + *(file.spdx_file_element for file in kernel_files.output.values()), + ] + + # create Spdx graphs + build_graph = SpdxBuildGraph( + build_spdx_document, + shared_elements.agent, + shared_elements.creation_info, + build_sbom, + ) + return build_graph + + +def _create_spdx_build_graph_with_mixed_sources( + cmd_graph: CmdGraph, + kernel_files: KernelFileCollection, + shared_elements: SharedSpdxElements, + high_level_build_element: Build, + spdx_id_generators: SpdxIdGeneratorCollection, +) -> SpdxBuildGraph: + """ + Creates an SPDX build graph where only output files are referenced from + an external document. Source files are included directly in the build graph. + + Args: + cmd_graph: The dependency graph of a kernel build. + kernel_files: Collection of categorized kernel files involved in the build. + shared_elements: SPDX elements shared across multiple documents. + high_level_build_element: The high-level Build element referenced by the build graph. + spdx_id_generators: Collection of generators for SPDX element IDs. + + Returns: + SpdxBuildGraph: The SPDX build graph connecting source files and distributable output files. + """ + # SpdxDocument + build_spdx_document = SpdxDocument( + spdxId=spdx_id_generators.build.generate(), + profileConformance=["core", "software", "build"], + namespaceMap=[ + NamespaceMap(prefix=generator.prefix, namespace=generator.namespace) + for generator in [ + spdx_id_generators.build, + spdx_id_generators.output, + spdx_id_generators.base, + ] + if generator.prefix is not None + ], + ) + + # Sbom + build_sbom = Sbom( + spdxId=spdx_id_generators.build.generate(), + software_sbomType=["build"], + ) + + # File elements + build_file_elements = [file.spdx_file_element for file in kernel_files.build.values()] + external_file_elements = [file.spdx_file_element for file in kernel_files.external.values()] + file_relationships = _file_relationships( + cmd_graph=cmd_graph, + file_elements={key: file.spdx_file_element for key, file in kernel_files.to_dict().items()}, + high_level_build_element=high_level_build_element, + spdx_id_generator=spdx_id_generators.build, + ) + + # Source file license elements + source_file_license_identifiers, source_file_license_relationships = source_file_license_elements( + list(kernel_files.build.values()), spdx_id_generators.build + ) + + # Update relationships + build_spdx_document.rootElement = [build_sbom] + root_file_elements = [file.spdx_file_element for file in kernel_files.output.values()] + build_spdx_document.import_ = [ + ExternalMap(externalSpdxId=high_level_build_element.spdxId), + *(ExternalMap(externalSpdxId=file.spdxId) for file in root_file_elements), + ] + + build_sbom.rootElement = [*root_file_elements] + build_sbom.element = [ + *build_file_elements, + *external_file_elements, + *source_file_license_identifiers, + *source_file_license_relationships, + *file_relationships, + ] + + build_graph = SpdxBuildGraph( + build_spdx_document, + shared_elements.agent, + shared_elements.creation_info, + build_sbom, + ) + return build_graph + + +def _file_relationships( + cmd_graph: CmdGraph, + file_elements: Mapping[PathStr, File], + high_level_build_element: Build, + spdx_id_generator: SpdxIdGenerator, +) -> list[Build | Relationship]: + """ + Construct SPDX Build and Relationship elements representing dependency + relationships in the cmd graph. + + Args: + cmd_graph: The dependency graph of a kernel build. + file_elements: Mapping of filesystem paths (PathStr) to their + corresponding SPDX File elements. + high_level_build_element: The SPDX Build element representing the overall build process/root. + spdx_id_generator: Generator for unique SPDX IDs. + + Returns: + list[Build | Relationship]: List of SPDX Build and Relationship elements + """ + high_level_build_ancestorOf_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="ancestorOf", + from_=high_level_build_element, + completeness="complete", + to=[], + ) + + # Create a relationship between each node (output file) + # and its children (input files) + build_and_relationship_elements: list[Build | Relationship] = [high_level_build_ancestorOf_relationship] + for node in cmd_graph: + # .cmd file dependencies + if node.cmd_file is not None: + build_element = Build( + spdxId=spdx_id_generator.generate(), + build_buildType=high_level_build_element.build_buildType, + build_buildId=high_level_build_element.build_buildId, + comment=node.cmd_file.savedcmd, + ) + build_and_relationship_elements.append(build_element) + + if node.cmd_file_dependencies: + hasInput_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="hasInput", + from_=build_element, + to=[file_elements[dep.absolute_path] for dep in node.cmd_file_dependencies], + ) + build_and_relationship_elements.append(hasInput_relationship) + + hasOutput_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="hasOutput", + from_=build_element, + to=[file_elements[node.absolute_path]], + ) + build_and_relationship_elements.append(hasOutput_relationship) + + high_level_build_ancestorOf_relationship.to.append(build_element) + + # incbin dependencies + if len(node.incbin_dependencies) > 0: + incbin_dependsOn_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="dependsOn", + comment="\n".join([incbin_dependency.full_statement for incbin_dependency in node.incbin_dependencies]), + from_=file_elements[node.absolute_path], + to=[ + file_elements[incbin_dependency.node.absolute_path] + for incbin_dependency in node.incbin_dependencies + ], + ) + build_and_relationship_elements.append(incbin_dependsOn_relationship) + + # hardcoded dependencies + if len(node.hardcoded_dependencies) > 0: + hardcoded_dependency_relationship = Relationship( + spdxId=spdx_id_generator.generate(), + relationshipType="dependsOn", + from_=file_elements[node.absolute_path], + to=[file_elements[n.absolute_path] for n in node.hardcoded_dependencies], + ) + build_and_relationship_elements.append(hardcoded_dependency_relationship) + + return build_and_relationship_elements From 0de18f407c8169b51a525e208c0cd690df2d2b1a Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:21:01 +0200 Subject: [PATCH 1576/5214] scripts/sbom: add unit tests for command parsers Add unit tests to verify that command parsers correctly extract input files from build commands. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/tests/__init__.py | 0 scripts/sbom/tests/cmd_graph/__init__.py | 0 .../tests/cmd_graph/test_savedcmd_parser.py | 443 ++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 scripts/sbom/tests/__init__.py create mode 100644 scripts/sbom/tests/cmd_graph/__init__.py create mode 100644 scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py diff --git a/scripts/sbom/tests/__init__.py b/scripts/sbom/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/sbom/tests/cmd_graph/__init__.py b/scripts/sbom/tests/cmd_graph/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py new file mode 100644 index 000000000000..a061a748e1bf --- /dev/null +++ b/scripts/sbom/tests/cmd_graph/test_savedcmd_parser.py @@ -0,0 +1,443 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import os +import unittest +from unittest.mock import patch + +from sbom.cmd_graph.savedcmd_parser import parse_inputs_from_commands +from sbom.cmd_graph.savedcmd_parser.command_parser_registry import CommandParserRegistry +import sbom.sbom_logging as sbom_logging + + +class TestSavedCmdParser(unittest.TestCase): + def _assert_parsing(self, cmd: str, expected: str, registry: CommandParserRegistry | None = None) -> None: + sbom_logging.init() + parsed = parse_inputs_from_commands(cmd, fail_on_unknown_build_command=False, registry=registry) + target = [] if expected == "" else expected.split(" ") + self.assertEqual(parsed, target) + errors = sbom_logging._error_logger._message_counts # type: ignore + self.assertEqual(errors, {}) + + # Compound command tests + def test_dd_cat(self): + cmd = "(dd if=arch/x86/boot/setup.bin bs=4k conv=sync status=none; cat arch/x86/boot/vmlinux.bin) >arch/x86/boot/bzImage" + expected = "arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin" + self._assert_parsing(cmd, expected) + + def test_manual_file_creation(self): + cmd = """{ symbase=__dtbo_overlay_bad_unresolved; echo '$(pound)include '; echo '.section .rodata,"a"'; echo '.balign STRUCT_ALIGNMENT'; echo ".global $${symbase}_begin"; echo "$${symbase}_begin:"; echo '.incbin "drivers/of/unittest-data/overlay_bad_unresolved.dtbo" '; echo ".global $${symbase}_end"; echo "$${symbase}_end:"; echo '.balign STRUCT_ALIGNMENT'; } > drivers/of/unittest-data/overlay_bad_unresolved.dtbo.S""" + expected = "" + self._assert_parsing(cmd, expected) + + def test_cat_xz_wrap(self): + cmd = "{ cat arch/x86/boot/compressed/vmlinux.bin | sh ../scripts/xz_wrap.sh; printf \\130\\064\\024\\000; } > arch/x86/boot/compressed/vmlinux.bin.xz" + expected = "arch/x86/boot/compressed/vmlinux.bin" + self._assert_parsing(cmd, expected) + + def test_printf_sed(self): + cmd = r"""{ printf 'static char tomoyo_builtin_profile[] __initdata =\n'; sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- /dev/null; printf '\t"";\n'; printf 'static char tomoyo_builtin_exception_policy[] __initdata =\n'; sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- ../security/tomoyo/policy/exception_policy.conf.default; printf '\t"";\n'; printf 'static char tomoyo_builtin_domain_policy[] __initdata =\n'; sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- /dev/null; printf '\t"";\n'; printf 'static char tomoyo_builtin_manager[] __initdata =\n'; sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- /dev/null; printf '\t"";\n'; printf 'static char tomoyo_builtin_stat[] __initdata =\n'; sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- /dev/null; printf '\t"";\n'; } > security/tomoyo/builtin-policy.h""" + expected = "../security/tomoyo/policy/exception_policy.conf.default" + self._assert_parsing(cmd, expected) + + def test_bin2c_echo(self): + cmd = """(echo "static char tomoyo_builtin_profile[] __initdata ="; ./scripts/bin2c security/tomoyo/builtin-policy.h""" + expected = "../security/tomoyo/policy/exception_policy.conf.default" + self._assert_parsing(cmd, expected) + + def test_cat_colon(self): + cmd = "{ cat init/modules.order; cat usr/modules.order; cat arch/x86/modules.order; cat arch/x86/boot/startup/modules.order; cat kernel/modules.order; cat certs/modules.order; cat mm/modules.order; cat fs/modules.order; cat ipc/modules.order; cat security/modules.order; cat crypto/modules.order; cat block/modules.order; cat io_uring/modules.order; cat lib/modules.order; cat arch/x86/lib/modules.order; cat drivers/modules.order; cat sound/modules.order; cat samples/modules.order; cat net/modules.order; cat virt/modules.order; cat arch/x86/pci/modules.order; cat arch/x86/power/modules.order; cat arch/x86/video/modules.order; :; } > modules.order" + expected = "init/modules.order usr/modules.order arch/x86/modules.order arch/x86/boot/startup/modules.order kernel/modules.order certs/modules.order mm/modules.order fs/modules.order ipc/modules.order security/modules.order crypto/modules.order block/modules.order io_uring/modules.order lib/modules.order arch/x86/lib/modules.order drivers/modules.order sound/modules.order samples/modules.order net/modules.order virt/modules.order arch/x86/pci/modules.order arch/x86/power/modules.order arch/x86/video/modules.order" + self._assert_parsing(cmd, expected) + + def test_cat_zstd(self): + cmd = "{ cat arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs | zstd -22 --ultra; printf \\340\\362\\066\\003; } > arch/x86/boot/compressed/vmlinux.bin.zst" + expected = "arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs" + self._assert_parsing(cmd, expected) + + # cat command tests + def test_cat_redirect(self): + cmd = "cat ../fs/unicode/utf8data.c_shipped > fs/unicode/utf8data.c" + expected = "../fs/unicode/utf8data.c_shipped" + self._assert_parsing(cmd, expected) + + def test_cat_piped(self): + cmd = "cat arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs | gzip -n -f -9 > arch/x86/boot/compressed/vmlinux.bin.gz" + expected = "arch/x86/boot/compressed/vmlinux.bin arch/x86/boot/compressed/vmlinux.relocs" + self._assert_parsing(cmd, expected) + + # sed command tests + def test_sed(self): + cmd = "sed -n 's/.*define *BLIST_\\([A-Z0-9_]*\\) *.*/BLIST_FLAG_NAME(\\1),/p' ../include/scsi/scsi_devinfo.h > drivers/scsi/scsi_devinfo_tbl.c" + expected = "../include/scsi/scsi_devinfo.h" + self._assert_parsing(cmd, expected) + + # awk command tests + def test_awk(self): + cmd = "awk -f ../arch/arm64/tools/gen-cpucaps.awk ../arch/arm64/tools/cpucaps > arch/arm64/include/generated/asm/cpucap-defs.h" + expected = "../arch/arm64/tools/cpucaps" + self._assert_parsing(cmd, expected) + + def test_awk_with_input_redirection(self): + cmd = "awk -v N=1 -f ../lib/raid6/unroll.awk < ../lib/raid6/int.uc > lib/raid6/int1.c" + expected = "../lib/raid6/int.uc" + self._assert_parsing(cmd, expected) + + # openssl command tests + def test_openssl(self): + cmd = "openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 -config certs/x509.genkey -outform PEM -out certs/signing_key.pem -keyout certs/signing_key.pem 2>&1" + expected = "" + self._assert_parsing(cmd, expected) + + # gcc/clang command tests + def test_gcc(self): + cmd = ( + "gcc -Wp,-MMD,arch/x86/pci/.i386.o.d -nostdinc -I../arch/x86/include -I./arch/x86/include/generated -I../include -I./include -I../arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I../include/uapi -I./include/generated/uapi -include ../include/linux/compiler-version.h -include ../include/linux/kconfig.h -include ../include/linux/compiler_types.h -D__KERNEL__ -fmacro-prefix-map=../= -Werror -std=gnu11 -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=branch -fno-jump-tables -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -march=x86-64 -mtune=generic -mno-red-zone -mcmodel=kernel -mstack-protector-guard-reg=gs -mstack-protector-guard-symbol=__ref_stack_chk_guard -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -mindirect-branch-cs-prefix -mfunction-return=thunk-extern -fno-jump-tables -fpatchable-function-entry=16,16 -fno-delete-null-pointer-checks -O2 -fno-allow-store-data-races -fstack-protector-strong -fomit-frame-pointer -fno-stack-clash-protection -falign-functions=16 -fno-strict-overflow -fno-stack-check -fconserve-stack -fno-builtin-wcslen -Wall -Wextra -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wvla-larger-than=1 -Wno-pointer-sign -Wcast-function-type -Wno-array-bounds -Wno-stringop-overflow -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wenum-conversion -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-type-limits -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -I../arch/x86/pci -Iarch/x86/pci -DKBUILD_MODFILE=" + "arch/x86/pci/i386" + " -DKBUILD_BASENAME=" + "i386" + " -DKBUILD_MODNAME=" + "i386" + " -D__KBUILD_MODNAME=kmod_i386 -c -o arch/x86/pci/i386.o ../arch/x86/pci/i386.c " + ) + expected = "../arch/x86/pci/i386.c" + self._assert_parsing(cmd, expected) + + def test_gcc_linking(self): + cmd = "gcc -o arch/x86/tools/relocs arch/x86/tools/relocs_32.o arch/x86/tools/relocs_64.o arch/x86/tools/relocs_common.o" + expected = "arch/x86/tools/relocs_32.o arch/x86/tools/relocs_64.o arch/x86/tools/relocs_common.o" + self._assert_parsing(cmd, expected) + + def test_gcc_without_compile_flag(self): + cmd = "gcc -Wp,-MMD,arch/x86/boot/compressed/.mkpiggy.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -I ../scripts/include -I../tools/include -I arch/x86/boot/compressed -o arch/x86/boot/compressed/mkpiggy ../arch/x86/boot/compressed/mkpiggy.c" + expected = "../arch/x86/boot/compressed/mkpiggy.c" + self._assert_parsing(cmd, expected) + + def test_gcc_with_env_override(self): + with patch.dict(os.environ, {"CC": "ccache gcc"}): + registry = CommandParserRegistry.create() + cmd = "gcc -o arch/x86/tools/relocs arch/x86/tools/relocs_32.o arch/x86/tools/relocs_64.o arch/x86/tools/relocs_common.o" + expected = "arch/x86/tools/relocs_32.o arch/x86/tools/relocs_64.o arch/x86/tools/relocs_common.o" + self._assert_parsing(cmd, expected, registry) + self._assert_parsing(f"ccache {cmd}", expected, registry) + + def test_gcc_dts_preprocessing(self): + cmd = "gcc -E -Wp,-MMD,drivers/of/.empty_root.dtb.d.pre.tmp -nostdinc -I ../scripts/dtc/include-prefixes -undef -D__DTS__ -x assembler-with-cpp -o drivers/of/.empty_root.dtb.dts.tmp ../drivers/of/empty_root.dts" + expected = "../drivers/of/empty_root.dts" + self._assert_parsing(cmd, expected) + + def test_clang(self): + cmd = """clang -Wp,-MMD,arch/x86/entry/.entry_64_compat.o.d -nostdinc -I../arch/x86/include -I./arch/x86/include/generated -I../include -I./include -I../arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I../include/uapi -I./include/generated/uapi -include ../include/linux/compiler-version.h -include ../include/linux/kconfig.h -D__KERNEL__ --target=x86_64-linux-gnu -fintegrated-as -Werror=unknown-warning-option -Werror=ignored-optimization-argument -Werror=option-ignored -Werror=unused-command-line-argument -fmacro-prefix-map=../= -Werror -D__ASSEMBLY__ -fno-PIE -m64 -I../arch/x86/entry -Iarch/x86/entry -DKBUILD_MODFILE='"arch/x86/entry/entry_64_compat"' -DKBUILD_MODNAME='"entry_64_compat"' -D__KBUILD_MODNAME=kmod_entry_64_compat -c -o arch/x86/entry/entry_64_compat.o ../arch/x86/entry/entry_64_compat.S""" + expected = "../arch/x86/entry/entry_64_compat.S" + self._assert_parsing(cmd, expected) + + # ld command tests + def test_ld(self): + cmd = r'ld -o arch/x86/entry/vdso/vdso64.so.dbg -shared --hash-style=both --build-id=sha1 --no-undefined --eh-frame-hdr -Bsymbolic -z noexecstack -m elf_x86_64 -soname linux-vdso.so.1 -z max-page-size=4096 -T arch/x86/entry/vdso/vdso.lds arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vgetrandom.o arch/x86/entry/vdso/vgetrandom-chacha.o; if readelf -rW arch/x86/entry/vdso/vdso64.so.dbg | grep -v _NONE | grep -q " R_\w*_"; then (echo >&2 "arch/x86/entry/vdso/vdso64.so.dbg: dynamic relocations are not supported"; rm -f arch/x86/entry/vdso/vdso64.so.dbg; /bin/false); fi' + expected = "arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vgetrandom.o arch/x86/entry/vdso/vgetrandom-chacha.o" + self._assert_parsing(cmd, expected) + + def test_ld_with_env_override(self): + with patch.dict(os.environ, {"LD": "some-tool ld"}): + registry = CommandParserRegistry.create() + cmd = r'ld -o arch/x86/entry/vdso/vdso64.so.dbg -shared --hash-style=both --build-id=sha1 --no-undefined --eh-frame-hdr -Bsymbolic -z noexecstack -m elf_x86_64 -soname linux-vdso.so.1 -z max-page-size=4096 -T arch/x86/entry/vdso/vdso.lds arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vgetrandom.o arch/x86/entry/vdso/vgetrandom-chacha.o; if readelf -rW arch/x86/entry/vdso/vdso64.so.dbg | grep -v _NONE | grep -q " R_\w*_"; then (echo >&2 "arch/x86/entry/vdso/vdso64.so.dbg: dynamic relocations are not supported"; rm -f arch/x86/entry/vdso/vdso64.so.dbg; /bin/false); fi' + expected = "arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vgetrandom.o arch/x86/entry/vdso/vgetrandom-chacha.o" + self._assert_parsing(cmd, expected, registry) + self._assert_parsing(f"some-tool {cmd}", expected, registry) + + def test_ld_whole_archive(self): + cmd = "ld -m elf_x86_64 -z noexecstack -r -o vmlinux.o --whole-archive vmlinux.a --no-whole-archive --start-group --end-group" + expected = "vmlinux.a" + self._assert_parsing(cmd, expected) + + def test_ld_with_at_symbol(self): + cmd = "ld.lld -m elf_x86_64 -z noexecstack -r -o fs/efivarfs/efivarfs.o @fs/efivarfs/efivarfs.mod ; ./tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --orc --retpoline --rethunk --static-call --uaccess --prefix=16 --link --module fs/efivarfs/efivarfs.o" + expected = "@fs/efivarfs/efivarfs.mod" + self._assert_parsing(cmd, expected) + + def test_ld_if_objdump(self): + cmd = """ld -o arch/x86/entry/vdso/vdso64.so.dbg -shared --hash-style=both --build-id=sha1 --eh-frame-hdr -Bsymbolic -z noexecstack -m elf_x86_64 -soname linux-vdso.so.1 --no-undefined -z max-page-size=4096 -T arch/x86/entry/vdso/vdso.lds arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vsgx.o && sh ./arch/x86/entry/vdso/checkundef.sh 'nm' 'arch/x86/entry/vdso/vdso64.so.dbg'; if objdump -R arch/x86/entry/vdso/vdso64.so.dbg | grep -E -h "R_X86_64_JUMP_SLOT|R_X86_64_GLOB_DAT|R_X86_64_RELATIVE| R_386_GLOB_DAT|R_386_JMP_SLOT|R_386_RELATIVE"; then (echo >&2 "arch/x86/entry/vdso/vdso64.so.dbg: dynamic relocations are not supported"; rm -f arch/x86/entry/vdso/vdso64.so.dbg; /bin/false); fi""" + expected = "arch/x86/entry/vdso/vdso-note.o arch/x86/entry/vdso/vclock_gettime.o arch/x86/entry/vdso/vgetcpu.o arch/x86/entry/vdso/vsgx.o" + self._assert_parsing(cmd, expected) + + # printf | xargs ar command tests + def test_ar_printf(self): + cmd = 'rm -f built-in.a; printf "./%s " init/built-in.a usr/built-in.a arch/x86/built-in.a arch/x86/boot/startup/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a io_uring/built-in.a lib/built-in.a arch/x86/lib/built-in.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a | xargs ar cDPrST built-in.a' + expected = "./init/built-in.a ./usr/built-in.a ./arch/x86/built-in.a ./arch/x86/boot/startup/built-in.a ./kernel/built-in.a ./certs/built-in.a ./mm/built-in.a ./fs/built-in.a ./ipc/built-in.a ./security/built-in.a ./crypto/built-in.a ./block/built-in.a ./io_uring/built-in.a ./lib/built-in.a ./arch/x86/lib/built-in.a ./drivers/built-in.a ./sound/built-in.a ./net/built-in.a ./virt/built-in.a ./arch/x86/pci/built-in.a ./arch/x86/power/built-in.a ./arch/x86/video/built-in.a" + self._assert_parsing(cmd, expected) + + def test_ar_printf_nested(self): + cmd = 'rm -f arch/x86/pci/built-in.a; printf "arch/x86/pci/%s " i386.o init.o mmconfig_64.o direct.o mmconfig-shared.o fixup.o acpi.o legacy.o irq.o common.o early.o bus_numa.o amd_bus.o | xargs ar cDPrST arch/x86/pci/built-in.a' + expected = "arch/x86/pci/i386.o arch/x86/pci/init.o arch/x86/pci/mmconfig_64.o arch/x86/pci/direct.o arch/x86/pci/mmconfig-shared.o arch/x86/pci/fixup.o arch/x86/pci/acpi.o arch/x86/pci/legacy.o arch/x86/pci/irq.o arch/x86/pci/common.o arch/x86/pci/early.o arch/x86/pci/bus_numa.o arch/x86/pci/amd_bus.o" + self._assert_parsing(cmd, expected) + + # ar command tests + def test_ar_reordering(self): + cmd = "rm -f vmlinux.a; ar cDPrST vmlinux.a built-in.a lib/lib.a arch/x86/lib/lib.a; ar mPiT $$(ar t vmlinux.a | sed -n 1p) vmlinux.a $$(ar t vmlinux.a | grep -F -f ../scripts/head-object-list.txt)" + expected = "built-in.a lib/lib.a arch/x86/lib/lib.a" + self._assert_parsing(cmd, expected) + + def test_ar_default(self): + cmd = "rm -f lib/lib.a; ar cDPrsT lib/lib.a lib/argv_split.o lib/bug.o lib/buildid.o lib/clz_tab.o lib/cmdline.o lib/cpumask.o lib/ctype.o lib/dec_and_lock.o lib/decompress.o lib/decompress_bunzip2.o lib/decompress_inflate.o lib/decompress_unlz4.o lib/decompress_unlzma.o lib/decompress_unlzo.o lib/decompress_unxz.o lib/decompress_unzstd.o lib/dump_stack.o lib/earlycpio.o lib/extable.o lib/flex_proportions.o lib/idr.o lib/iomem_copy.o lib/irq_regs.o lib/is_single_threaded.o lib/klist.o lib/kobject.o lib/kobject_uevent.o lib/logic_pio.o lib/maple_tree.o lib/memcat_p.o lib/nmi_backtrace.o lib/objpool.o lib/plist.o lib/radix-tree.o lib/ratelimit.o lib/rbtree.o lib/seq_buf.o lib/siphash.o lib/string.o lib/sys_info.o lib/timerqueue.o lib/union_find.o lib/vsprintf.o lib/win_minmax.o lib/xarray.o" + expected = "lib/argv_split.o lib/bug.o lib/buildid.o lib/clz_tab.o lib/cmdline.o lib/cpumask.o lib/ctype.o lib/dec_and_lock.o lib/decompress.o lib/decompress_bunzip2.o lib/decompress_inflate.o lib/decompress_unlz4.o lib/decompress_unlzma.o lib/decompress_unlzo.o lib/decompress_unxz.o lib/decompress_unzstd.o lib/dump_stack.o lib/earlycpio.o lib/extable.o lib/flex_proportions.o lib/idr.o lib/iomem_copy.o lib/irq_regs.o lib/is_single_threaded.o lib/klist.o lib/kobject.o lib/kobject_uevent.o lib/logic_pio.o lib/maple_tree.o lib/memcat_p.o lib/nmi_backtrace.o lib/objpool.o lib/plist.o lib/radix-tree.o lib/ratelimit.o lib/rbtree.o lib/seq_buf.o lib/siphash.o lib/string.o lib/sys_info.o lib/timerqueue.o lib/union_find.o lib/vsprintf.o lib/win_minmax.o lib/xarray.o" + self._assert_parsing(cmd, expected) + + def test_ar_llvm(self): + cmd = "llvm-ar mPiT $$(llvm-ar t vmlinux.a | sed -n 1p) vmlinux.a $$(llvm-ar t vmlinux.a | grep -F -f ../scripts/head-object-list.txt)" + expected = "" + self._assert_parsing(cmd, expected) + + # nm command tests + def test_nm(self): + cmd = """llvm-nm -p --defined-only rust/core.o | awk '$$2~/(T|R|D|B)/ && $$3!~/__(pfx|cfi|odr_asan)/ { printf "EXPORT_SYMBOL_RUST_GPL(%s);\n",$$3 }' > rust/exports_core_generated.h""" + expected = "rust/core.o" + self._assert_parsing(cmd, expected) + + def test_nm_vmlinux(self): + cmd = r"nm vmlinux | sed -n -e 's/^\([0-9a-fA-F]*\) [ABbCDGRSTtVW] \(_text\|__start_rodata\|__bss_start\|_end\)$/#define VO_\2 _AC(0x\1,UL)/p' > arch/x86/boot/voffset.h" + expected = "vmlinux" + self._assert_parsing(cmd, expected) + + # objcopy command tests + def test_objcopy(self): + cmd = "objcopy --remove-section='.rel*' --remove-section=!'.rel*.dyn' vmlinux.unstripped vmlinux" + expected = "vmlinux.unstripped" + self._assert_parsing(cmd, expected) + + def test_objcopy_llvm(self): + cmd = "llvm-objcopy --remove-section='.rel*' --remove-section=!'.rel*.dyn' vmlinux.unstripped vmlinux" + expected = "vmlinux.unstripped" + self._assert_parsing(cmd, expected) + + # strip command tests + def test_strip(self): + cmd = "strip --strip-debug -o drivers/firmware/efi/libstub/mem.stub.o drivers/firmware/efi/libstub/mem.o" + expected = "drivers/firmware/efi/libstub/mem.o" + self._assert_parsing(cmd, expected) + + # cp command tests + def test_cp_truncate(self): + cmd = "cp arch/arm64/boot/Image arch/arm64/boot/vmlinux.bin; truncate -s $$(hexdump -s16 -n4 -e '\"%u\"' arch/arm64/boot/Image) arch/arm64/boot/vmlinux.bin" + expected = "arch/arm64/boot/Image" + self._assert_parsing(cmd, expected) + + # rustc command tests + def test_rustc(self): + cmd = """OBJTREE=/workspace/linux/kernel_build rustc -Zbinary_dep_depinfo=y -Astable_features -Dnon_ascii_idents -Dunsafe_op_in_unsafe_fn -Wmissing_docs -Wrust_2018_idioms -Wclippy::all -Wclippy::as_ptr_cast_mut -Wclippy::as_underscore -Wclippy::cast_lossless -Wclippy::ignored_unit_patterns -Wclippy::mut_mut -Wclippy::needless_bitwise_bool -Aclippy::needless_lifetimes -Wclippy::no_mangle_with_rust_abi -Wclippy::ptr_as_ptr -Wclippy::ptr_cast_constness -Wclippy::ref_as_ptr -Wclippy::undocumented_unsafe_blocks -Wclippy::unnecessary_safety_comment -Wclippy::unnecessary_safety_doc -Wrustdoc::missing_crate_level_docs -Wrustdoc::unescaped_backticks -Cpanic=abort -Cembed-bitcode=n -Clto=n -Cforce-unwind-tables=n -Ccodegen-units=1 -Csymbol-mangling-version=v0 -Crelocation-model=static -Zfunction-sections=n -Wclippy::float_arithmetic --target=./scripts/target.json -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2 -Zcf-protection=branch -Zno-jump-tables -Ctarget-cpu=x86-64 -Ztune-cpu=generic -Cno-redzone=y -Ccode-model=kernel -Zfunction-return=thunk-extern -Zpatchable-function-entry=16,16 -Copt-level=2 -Cdebug-assertions=n -Coverflow-checks=y -Dwarnings @./include/generated/rustc_cfg --edition=2021 --cfg no_fp_fmt_parse --emit=dep-info=rust/.core.o.d --emit=obj=rust/core.o --emit=metadata=rust/libcore.rmeta --crate-type rlib -L./rust --crate-name core /usr/lib/rust-1.84/lib/rustlib/src/rust/library/core/src/lib.rs --sysroot=/dev/null ;llvm-objcopy --redefine-sym __addsf3=__rust__addsf3 --redefine-sym __eqsf2=__rust__eqsf2 --redefine-sym __extendsfdf2=__rust__extendsfdf2 --redefine-sym __gesf2=__rust__gesf2 --redefine-sym __lesf2=__rust__lesf2 --redefine-sym __ltsf2=__rust__ltsf2 --redefine-sym __mulsf3=__rust__mulsf3 --redefine-sym __nesf2=__rust__nesf2 --redefine-sym __truncdfsf2=__rust__truncdfsf2 --redefine-sym __unordsf2=__rust__unordsf2 --redefine-sym __adddf3=__rust__adddf3 --redefine-sym __eqdf2=__rust__eqdf2 --redefine-sym __ledf2=__rust__ledf2 --redefine-sym __ltdf2=__rust__ltdf2 --redefine-sym __muldf3=__rust__muldf3 --redefine-sym __unorddf2=__rust__unorddf2 --redefine-sym __muloti4=__rust__muloti4 --redefine-sym __multi3=__rust__multi3 --redefine-sym __udivmodti4=__rust__udivmodti4 --redefine-sym __udivti3=__rust__udivti3 --redefine-sym __umodti3=__rust__umodti3 rust/core.o""" + expected = "/usr/lib/rust-1.84/lib/rustlib/src/rust/library/core/src/lib.rs rust/core.o" + self._assert_parsing(cmd, expected) + + # rustdoc command tests + def test_rustdoc(self): + cmd = """OBJTREE=/workspace/linux/kernel_build rustdoc --test --edition=2021 -Zbinary_dep_depinfo=y -Astable_features -Dnon_ascii_idents -Dunsafe_op_in_unsafe_fn -Wmissing_docs -Wrust_2018_idioms -Wunreachable_pub -Wclippy::all -Wclippy::as_ptr_cast_mut -Wclippy::as_underscore -Wclippy::cast_lossless -Wclippy::ignored_unit_patterns -Wclippy::mut_mut -Wclippy::needless_bitwise_bool -Aclippy::needless_lifetimes -Wclippy::no_mangle_with_rust_abi -Wclippy::ptr_as_ptr -Wclippy::ptr_cast_constness -Wclippy::ref_as_ptr -Wclippy::undocumented_unsafe_blocks -Wclippy::unnecessary_safety_comment -Wclippy::unnecessary_safety_doc -Wrustdoc::missing_crate_level_docs -Wrustdoc::unescaped_backticks -Cpanic=abort -Cembed-bitcode=n -Clto=n -Cforce-unwind-tables=n -Ccodegen-units=1 -Csymbol-mangling-version=v0 -Crelocation-model=static -Zfunction-sections=n -Wclippy::float_arithmetic --target=aarch64-unknown-none -Ctarget-feature="-neon" -Cforce-unwind-tables=n -Zbranch-protection=pac-ret -Copt-level=2 -Cdebug-assertions=y -Coverflow-checks=y -Dwarnings -Cforce-frame-pointers=y -Zsanitizer=kernel-address -Zsanitizer-recover=kernel-address -Cllvm-args=-asan-mapping-offset=0xdfff800000000000 -Cpasses=sancov-module -Cllvm-args=-sanitizer-coverage-level=3 -Cllvm-args=-sanitizer-coverage-trace-pc -Cllvm-args=-sanitizer-coverage-trace-compares @./include/generated/rustc_cfg -L./rust --extern ffi --extern pin_init --extern kernel --extern build_error --extern macros --extern bindings --extern uapi --no-run --crate-name kernel -Zunstable-options --sysroot=/dev/null --test-builder ./scripts/rustdoc_test_builder ../rust/kernel/lib.rs >/dev/null""" + expected = "../rust/kernel/lib.rs" + self._assert_parsing(cmd, expected) + + def test_rustdoc_test_gen(self): + cmd = "./scripts/rustdoc_test_gen" + expected = "" + self._assert_parsing(cmd, expected) + + # flex command tests + def test_flex(self): + cmd = "flex -oscripts/kconfig/lexer.lex.c -L ../scripts/kconfig/lexer.l" + expected = "../scripts/kconfig/lexer.l" + self._assert_parsing(cmd, expected) + + # bison command tests + def test_bison(self): + cmd = "bison -o scripts/kconfig/parser.tab.c --defines=scripts/kconfig/parser.tab.h -t -l ../scripts/kconfig/parser.y" + expected = "../scripts/kconfig/parser.y" + self._assert_parsing(cmd, expected) + + # bindgen command tests + def test_bindgen(self): + cmd = ( + "bindgen ../rust/bindings/bindings_helper.h " + "--blocklist-type __kernel_s?size_t --blocklist-type __kernel_ptrdiff_t " + "--opaque-type xregs_state --opaque-type desc_struct --no-doc-comments " + "--rust-target 1.68 --use-core --with-derive-default -o rust/bindings/bindings_generated.rs " + "-- -Wp,-MMD,rust/bindings/.bindings_generated.rs.d -nostdinc -I../arch/x86/include " + "-include ../include/linux/compiler-version.h -D__KERNEL__ -fintegrated-as -fno-builtin -DMODULE; " + "sed -Ei 's/pub const RUST_CONST_HELPER_([a-zA-Z0-9_]*)/pub const \\1/g' rust/bindings/bindings_generated.rs" + ) + expected = "../rust/bindings/bindings_helper.h ../include/linux/compiler-version.h" + self._assert_parsing(cmd, expected) + + # perl command tests + def test_perl(self): + cmd = "perl ../lib/crypto/x86/poly1305-x86_64-cryptogams.pl > lib/crypto/x86/poly1305-x86_64-cryptogams.S" + expected = "../lib/crypto/x86/poly1305-x86_64-cryptogams.pl" + self._assert_parsing(cmd, expected) + + # link-vmlinux.sh command tests + def test_link_vmlinux(self): + cmd = '../scripts/link-vmlinux.sh "ld" "-m elf_x86_64 -z noexecstack" "-z max-page-size=0x200000 --build-id=sha1 --orphan-handling=error --emit-relocs --discard-none" "vmlinux.unstripped"; true' + expected = "vmlinux.a" + self._assert_parsing(cmd, expected) + + def test_link_vmlinux_postlink(self): + cmd = '../scripts/link-vmlinux.sh "ld" "-m elf_x86_64 -z noexecstack --no-warn-rwx-segments" "--emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 -X --orphan-handling=error"; make -f ../arch/x86/Makefile.postlink vmlinux' + expected = "vmlinux.a" + self._assert_parsing(cmd, expected) + + # syscallhdr.sh command tests + def test_syscallhdr(self): + cmd = "sh ../scripts/syscallhdr.sh --abis common,64 --emit-nr ../arch/x86/entry/syscalls/syscall_64.tbl arch/x86/include/generated/uapi/asm/unistd_64.h" + expected = "../arch/x86/entry/syscalls/syscall_64.tbl" + self._assert_parsing(cmd, expected) + + # syscalltbl.sh command tests + def test_syscalltbl(self): + cmd = "sh ../scripts/syscalltbl.sh --abis common,64 ../arch/x86/entry/syscalls/syscall_64.tbl arch/x86/include/generated/asm/syscalls_64.h" + expected = "../arch/x86/entry/syscalls/syscall_64.tbl" + self._assert_parsing(cmd, expected) + + # mkcapflags.sh command tests + def test_mkcapflags(self): + cmd = "sh ../arch/x86/kernel/cpu/mkcapflags.sh arch/x86/kernel/cpu/capflags.c ../arch/x86/kernel/cpu/../../include/asm/cpufeatures.h ../arch/x86/kernel/cpu/../../include/asm/vmxfeatures.h ../arch/x86/kernel/cpu/mkcapflags.sh FORCE" + expected = "../arch/x86/kernel/cpu/../../include/asm/cpufeatures.h ../arch/x86/kernel/cpu/../../include/asm/vmxfeatures.h" + self._assert_parsing(cmd, expected) + + # orc_hash.sh command tests + def test_orc_hash(self): + cmd = "mkdir -p arch/x86/include/generated/asm/; sh ../scripts/orc_hash.sh < ../arch/x86/include/asm/orc_types.h > arch/x86/include/generated/asm/orc_hash.h" + expected = "../arch/x86/include/asm/orc_types.h" + self._assert_parsing(cmd, expected) + + # xen-hypercalls.sh command tests + def test_xen_hypercalls(self): + cmd = "sh '../scripts/xen-hypercalls.sh' arch/x86/include/generated/asm/xen-hypercalls.h ../include/xen/interface/xen-mca.h ../include/xen/interface/xen.h ../include/xen/interface/xenpmu.h" + expected = "../include/xen/interface/xen-mca.h ../include/xen/interface/xen.h ../include/xen/interface/xenpmu.h" + self._assert_parsing(cmd, expected) + + # gen_initramfs.sh command tests + def test_gen_initramfs(self): + cmd = "sh ../usr/gen_initramfs.sh -o usr/initramfs_data.cpio -l usr/.initramfs_data.cpio.d ../usr/default_cpio_list" + expected = "../usr/default_cpio_list" + self._assert_parsing(cmd, expected) + + # mkuboot.sh command tests + def test_mkuboot(self): + cmd = "bash ../scripts/mkuboot.sh -A arm -O linux -C none -T kernel -a 0x8000 -e 0x8000 -n 'Linux-6.15.0' -d arch/arm/boot/zImage arch/arm/boot/uImage" + expected = "arch/arm/boot/zImage" + self._assert_parsing(cmd, expected) + + # syscallnr.sh command tests + def test_syscallnr(self): + cmd = "sh ../arch/arm/tools/syscallnr.sh ../arch/arm/tools/syscall.tbl arch/arm/include/generated/asm/unistd-nr.h" + expected = "../arch/arm/tools/syscall.tbl" + self._assert_parsing(cmd, expected) + + # gen-kernel-hwcaps.sh command tests + def test_gen_kernel_hwcaps(self): + cmd = "/bin/sh -e ../arch/arm64/tools/gen-kernel-hwcaps.sh ../arch/arm64/include/uapi/asm/hwcap.h > arch/arm64/include/generated/asm/kernel-hwcap.h" + expected = "../arch/arm64/include/uapi/asm/hwcap.h" + self._assert_parsing(cmd, expected) + + # vdso2c command tests + def test_vdso2c(self): + cmd = "arch/x86/entry/vdso/vdso2c arch/x86/entry/vdso/vdso64.so.dbg arch/x86/entry/vdso/vdso64.so arch/x86/entry/vdso/vdso-image-64.c" + expected = "arch/x86/entry/vdso/vdso64.so.dbg arch/x86/entry/vdso/vdso64.so" + self._assert_parsing(cmd, expected) + + # vdsomunge command tests + def test_vdsomunge(self): + cmd = "arch/arm64/kernel/vdso32/../../../arm/vdso/vdsomunge arch/arm64/kernel/vdso32/vdso.so.raw arch/arm64/kernel/vdso32/vdso32.so.dbg" + expected = "arch/arm64/kernel/vdso32/vdso.so.raw" + self._assert_parsing(cmd, expected) + + # mkpiggy command tests + def test_mkpiggy(self): + cmd = "arch/x86/boot/compressed/mkpiggy arch/x86/boot/compressed/vmlinux.bin.gz > arch/x86/boot/compressed/piggy.S" + expected = "arch/x86/boot/compressed/vmlinux.bin.gz" + self._assert_parsing(cmd, expected) + + # relocs command tests + def test_relocs(self): + cmd = "arch/x86/tools/relocs vmlinux.unstripped > arch/x86/boot/compressed/vmlinux.relocs;arch/x86/tools/relocs --abs-relocs vmlinux.unstripped" + expected = "vmlinux.unstripped" + self._assert_parsing(cmd, expected) + + def test_relocs_with_realmode(self): + cmd = ( + "arch/x86/tools/relocs --realmode arch/x86/realmode/rm/realmode.elf > arch/x86/realmode/rm/realmode.relocs" + ) + expected = "arch/x86/realmode/rm/realmode.elf" + self._assert_parsing(cmd, expected) + + # mk_elfconfig command tests + def test_mk_elfconfig(self): + cmd = "scripts/mod/mk_elfconfig < scripts/mod/empty.o > scripts/mod/elfconfig.h" + expected = "scripts/mod/empty.o" + self._assert_parsing(cmd, expected) + + # tools/build command tests + def test_build(self): + cmd = "arch/x86/boot/tools/build arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h arch/x86/boot/bzImage" + expected = "arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h" + self._assert_parsing(cmd, expected) + + # extract-cert command tests + def test_extract_cert(self): + cmd = 'certs/extract-cert "" certs/signing_key.x509' + expected = "" + self._assert_parsing(cmd, expected) + + # dtc command tests + def test_dtc_cat(self): + cmd = "./scripts/dtc/dtc -o drivers/of/empty_root.dtb -b 0 -i../drivers/of/ -i../scripts/dtc/include-prefixes -Wno-unique_unit_address -Wno-unit_address_vs_reg -Wno-avoid_unnecessary_addr_size -Wno-alias_paths -Wno-graph_child_address -Wno-simple_bus_reg -d drivers/of/.empty_root.dtb.d.dtc.tmp drivers/of/.empty_root.dtb.dts.tmp ; cat drivers/of/.empty_root.dtb.d.pre.tmp drivers/of/.empty_root.dtb.d.dtc.tmp > drivers/of/.empty_root.dtb.d" + expected = "drivers/of/.empty_root.dtb.dts.tmp drivers/of/.empty_root.dtb.d.pre.tmp drivers/of/.empty_root.dtb.d.dtc.tmp" + self._assert_parsing(cmd, expected) + + # pnmtologo command tests + def test_pnmtologo(self): + cmd = "drivers/video/logo/pnmtologo -t clut224 -n logo_linux_clut224 -o drivers/video/logo/logo_linux_clut224.c ../drivers/video/logo/logo_linux_clut224.ppm" + expected = "../drivers/video/logo/logo_linux_clut224.ppm" + self._assert_parsing(cmd, expected) + + # relacheck command tests + def test_relacheck(self): + cmd = "arch/arm64/kernel/pi/relacheck arch/arm64/kernel/pi/idreg-override.pi.o arch/arm64/kernel/pi/idreg-override.o" + expected = "arch/arm64/kernel/pi/idreg-override.pi.o" + self._assert_parsing(cmd, expected) + + # gen-hyprel command tests + def test_gen_hyprel(self): + cmd = "arch/arm64/kvm/hyp/nvhe/gen-hyprel arch/arm64/kvm/hyp/nvhe/kvm_nvhe.tmp.o > arch/arm64/kvm/hyp/nvhe/hyp-reloc.S" + expected = "arch/arm64/kvm/hyp/nvhe/kvm_nvhe.tmp.o" + self._assert_parsing(cmd, expected) + + # mkregtable command tests + def test_mkregtable(self): + cmd = "drivers/gpu/drm/radeon/mkregtable ../drivers/gpu/drm/radeon/reg_srcs/r100 > drivers/gpu/drm/radeon/r100_reg_safe.h" + expected = "../drivers/gpu/drm/radeon/reg_srcs/r100" + self._assert_parsing(cmd, expected) + + # genheaders command tests + def test_genheaders(self): + cmd = "security/selinux/genheaders security/selinux/flask.h security/selinux/av_permissions.h" + expected = "" + self._assert_parsing(cmd, expected) + + # mkcpustr command tests + def test_mkcpustr(self): + cmd = "arch/x86/boot/mkcpustr > arch/x86/boot/cpustr.h" + expected = "" + self._assert_parsing(cmd, expected) + + # polgen command tests + def test_polgen(self): + cmd = "scripts/ipe/polgen/polgen security/ipe/boot_policy.c" + expected = "" + self._assert_parsing(cmd, expected) + + # gen_header.py command tests + def test_gen_header(self): + cmd = "mkdir -p drivers/gpu/drm/msm/generated && python3 ../drivers/gpu/drm/msm/registers/gen_header.py --no-validate --rnn ../drivers/gpu/drm/msm/registers --xml ../drivers/gpu/drm/msm/registers/adreno/a2xx.xml c-defines > drivers/gpu/drm/msm/generated/a2xx.xml.h" + expected = "../drivers/gpu/drm/msm/registers/adreno/a2xx.xml" + self._assert_parsing(cmd, expected) + + +if __name__ == "__main__": + unittest.main() From 880bae5f1269b4d81bb2a254963e84377cd37bc1 Mon Sep 17 00:00:00 2001 From: Luis Augenstein Date: Mon, 18 May 2026 08:21:02 +0200 Subject: [PATCH 1577/5214] scripts/sbom: add unit tests for SPDX-License-Identifier parsing Verify that SPDX-License-Identifier headers at the top of source files are parsed correctly. Assisted-by: Cursor:claude-sonnet-4-5 Assisted-by: OpenCode:GLM-4-7 Co-developed-by: Maximilian Huber Signed-off-by: Maximilian Huber Signed-off-by: Luis Augenstein Signed-off-by: Greg Kroah-Hartman --- scripts/sbom/tests/spdx_graph/__init__.py | 0 .../sbom/tests/spdx_graph/test_kernel_file.py | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 scripts/sbom/tests/spdx_graph/__init__.py create mode 100644 scripts/sbom/tests/spdx_graph/test_kernel_file.py diff --git a/scripts/sbom/tests/spdx_graph/__init__.py b/scripts/sbom/tests/spdx_graph/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/sbom/tests/spdx_graph/test_kernel_file.py b/scripts/sbom/tests/spdx_graph/test_kernel_file.py new file mode 100644 index 000000000000..35a63a768ba2 --- /dev/null +++ b/scripts/sbom/tests/spdx_graph/test_kernel_file.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (C) 2025 TNG Technology Consulting GmbH + +import unittest +from pathlib import Path +import tempfile +from sbom.spdx_graph.kernel_file import _parse_spdx_license_identifier # type: ignore + + +class TestKernelFile(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.TemporaryDirectory() + self.src_tree = Path(self.tmpdir.name) + + def tearDown(self): + self.tmpdir.cleanup() + + def test_parse_spdx_license_identifier(self): + # REUSE-IgnoreStart + test_cases: list[tuple[str, str | None]] = [ + ("/* SPDX-License-Identifier: MIT*/", "MIT"), + ("// SPDX-License-Identifier: GPL-2.0-only", "GPL-2.0-only"), + ("# SPDX-License-Identifier: GPL-2.0-only", "GPL-2.0-only"), + ("#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0-only", "GPL-2.0-only"), + ("/* SPDX-License-Identifier: GPL-2.0-or-later OR MIT */", "GPL-2.0-or-later OR MIT"), + ("/* SPDX-License-Identifier: Apache-2.0 */\n extra text", "Apache-2.0"), + ("", "GPL-2.0"), + ("int main() { return 0; }", None), + ] + # REUSE-IgnoreEnd + + for i, (file_content, expected_identifier) in enumerate(test_cases): + file_path = self.src_tree / f"file_{i}.c" + file_path.write_text(file_content) + self.assertEqual(_parse_spdx_license_identifier(str(file_path)), expected_identifier) From 2a3987540cab00c5b24a82ec6b4dc5d18e41e112 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 18 May 2026 15:10:08 +0800 Subject: [PATCH 1578/5214] KVM: selftests: Fix vcpu_get_stats_fd() ioctl name vcpu_get_stats_fd() invokes KVM_GET_STATS_FD, but its assertion reports KVM_CHECK_EXTENSION if the ioctl fails. Use KVM_GET_STATS_FD in the assertion so failures point at the ioctl that actually failed. Fixes: 1b78d474ce4e ("KVM: selftests: Add logic to detect if ioctl() failed because VM was killed") Signed-off-by: Zongyao Chen Link: https://patch.msgid.link/20260518071008.2091335-1-ZongYao.Chen@linux.alibaba.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/kvm_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 2ecaaa0e9965..04a910164a29 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -876,7 +876,7 @@ static inline int vcpu_get_stats_fd(struct kvm_vcpu *vcpu) { int fd = __vcpu_ioctl(vcpu, KVM_GET_STATS_FD, NULL); - TEST_ASSERT_VM_VCPU_IOCTL(fd >= 0, KVM_CHECK_EXTENSION, fd, vcpu->vm); + TEST_ASSERT_VM_VCPU_IOCTL(fd >= 0, KVM_GET_STATS_FD, fd, vcpu->vm); return fd; } From 66472e86703f10cda1a6a220f682841b200af87d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 18 May 2026 12:41:55 +0200 Subject: [PATCH 1579/5214] MAINTAINERS: KVM: Include maintainer profile No dedicated KVM maintainers are returned by get_maintainers.pl for the subsystem maintainer profile, thus patches changing that file miss the actual owners of the file. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260518104154.38915-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Sean Christopherson --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db..fcbde976adea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14148,6 +14148,7 @@ L: kvm@vger.kernel.org S: Supported P: Documentation/process/maintainer-kvm-x86.rst T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git +F: Documentation/process/maintainer-kvm-x86.rst F: arch/x86/include/asm/kvm* F: arch/x86/include/asm/svm.h F: arch/x86/include/asm/vmx*.h From a7b7c7c6c01679efef0fd2f2ca1c5114f303e4f5 Mon Sep 17 00:00:00 2001 From: Xukai Wang Date: Sat, 25 Apr 2026 17:29:32 +0800 Subject: [PATCH 1580/5214] clk: canaan: Add clock driver for Canaan K230 This patch provides basic support for the K230 clock, which covers all clocks in K230 SoC. The clock tree of the K230 SoC consists of a 24MHZ external crystal oscillator, PLLs and an external pulse input for timerX, and their derived clocks. Co-developed-by: Troy Mitchell Signed-off-by: Troy Mitchell Signed-off-by: Xukai Wang Signed-off-by: Conor Dooley --- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-k230.c | 2452 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2459 insertions(+) create mode 100644 drivers/clk/clk-k230.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index b2efbe9f6acb..1717ce75a907 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -480,6 +480,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks. +config COMMON_CLK_K230 + bool "Clock driver for the Canaan Kendryte K230 SoC" + depends on ARCH_CANAAN || COMPILE_TEST + help + Support for the Canaan Kendryte K230 RISC-V SoC clocks. + config COMMON_CLK_SP7021 tristate "Clock driver for Sunplus SP7021 SoC" depends on SOC_SP7021 || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a3e2862ebd7e..3dbc05f76101 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o +obj-$(CONFIG_COMMON_CLK_K230) += clk-k230.o obj-$(CONFIG_LMK04832) += clk-lmk04832.o obj-$(CONFIG_COMMON_CLK_LAN966X) += clk-lan966x.o obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o diff --git a/drivers/clk/clk-k230.c b/drivers/clk/clk-k230.c new file mode 100644 index 000000000000..cfc437038e4e --- /dev/null +++ b/drivers/clk/clk-k230.c @@ -0,0 +1,2452 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Kendryte Canaan K230 Clock Drivers + * + * Author: Xukai Wang + * Author: Troy Mitchell + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PLL control register bits. */ +#define K230_PLL_BYPASS_ENABLE BIT(19) +#define K230_PLL_GATE_ENABLE BIT(2) +#define K230_PLL_GATE_WRITE_ENABLE BIT(18) +#define K230_PLL_OD_MASK GENMASK(27, 24) +#define K230_PLL_R_MASK GENMASK(21, 16) +#define K230_PLL_F_MASK GENMASK(12, 0) +#define K230_PLL_DIV_REG_OFFSET 0x00 +#define K230_PLL_BYPASS_REG_OFFSET 0x04 +#define K230_PLL_GATE_REG_OFFSET 0x08 +#define K230_PLL_LOCK_REG_OFFSET 0x0C + +/* PLL lock register */ +#define K230_PLL_LOCK_STATUS_MASK BIT(0) +#define K230_PLL_LOCK_TIME_DELAY 400 +#define K230_PLL_LOCK_TIMEOUT 0 + +/* K230 CLK registers offset */ +#define K230_CLK_AUDIO_CLKDIV_OFFSET 0x34 +#define K230_CLK_PDM_CLKDIV_OFFSET 0x40 +#define K230_CLK_CODEC_ADC_MCLKDIV_OFFSET 0x38 +#define K230_CLK_CODEC_DAC_MCLKDIV_OFFSET 0x3c + +#define K230_PLLX_DIV_ADDR(base, idx) \ + (K230_PLL_DIV_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_BYPASS_ADDR(base, idx) \ + (K230_PLL_BYPASS_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_GATE_ADDR(base, idx) \ + (K230_PLL_GATE_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_LOCK_ADDR(base, idx) \ + (K230_PLL_LOCK_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_CLK_RATE_FORMAT_PNAME(_var, _id, \ + _mul_min, _mul_max, _mul_shift, _mul_mask, \ + _div_min, _div_max, _div_shift, _div_mask, \ + _reg, _bit, _method, _reg2, \ + _read_only, _flags, \ + _pname) \ + static struct k230_clk_rate _var = { \ + .div_reg_off = _reg, \ + .mul_reg_off = _reg2, \ + .id = _id, \ + .clk = { \ + .write_enable_bit = _bit, \ + .mul_min = _mul_min, \ + .mul_max = _mul_max, \ + .mul_shift = _mul_shift, \ + .mul_mask = _mul_mask, \ + .div_min = _div_min, \ + .div_max = _div_max, \ + .div_shift = _div_shift, \ + .div_mask = _div_mask, \ + .read_only = _read_only, \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &k230_clk_ops_##_method, \ + _flags), \ + }, \ + } + +#define K230_CLK_RATE_FORMAT(_var, _id, \ + _mul_min, _mul_max, _mul_shift, _mul_mask, \ + _div_min, _div_max, _div_shift, _div_mask, \ + _reg, _bit, _method, _reg2, \ + _read_only, _flags, \ + _phw) \ + static struct k230_clk_rate _var = { \ + .div_reg_off = _reg, \ + .mul_reg_off = _reg2, \ + .id = _id, \ + .clk = { \ + .write_enable_bit = _bit, \ + .mul_min = _mul_min, \ + .mul_max = _mul_max, \ + .mul_shift = _mul_shift, \ + .mul_mask = _mul_mask, \ + .div_min = _div_min, \ + .div_max = _div_max, \ + .div_shift = _div_shift, \ + .div_mask = _div_mask, \ + .read_only = _read_only, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &k230_clk_ops_##_method, \ + _flags), \ + }, \ + } + +#define K230_CLK_GATE_FORMAT_PNAME(_var, _id, \ + _reg, _bit, _flags, _gate_flags, \ + _pname) \ + static struct k230_clk_gate _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .bit_idx = _bit, \ + .flags = _gate_flags, \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &clk_gate_ops, _flags), \ + }, \ + } + +#define K230_CLK_GATE_FORMAT(_var, _id, \ + _reg, _bit, _flags, _gate_flags, \ + _phw) \ + static struct k230_clk_gate _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .bit_idx = _bit, \ + .flags = _gate_flags, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &clk_gate_ops, _flags), \ + }, \ + } + +#define K230_CLK_MUX_FORMAT(_var, _id, \ + _reg, _shift, _mask, _flags, _mux_flags, _pdata) \ + static struct k230_clk_mux _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .flags = _mux_flags, \ + .shift = _shift, \ + .mask = _mask, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(#_var, \ + _pdata, &clk_mux_ops, _flags), \ + }, \ + } + +#define K230_CLK_FIXED_FACTOR_FORMAT(_var, \ + _mul, _div, _flags, \ + _phw) \ + static struct clk_fixed_factor _var = { \ + .mult = _mul, \ + .div = _div, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &clk_fixed_factor_ops, _flags), \ + } + +#define K230_CLK_PLL_FORMAT(_var, _id, _flags, _pname) \ + static struct k230_pll _var = { \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &k230_pll_ops, _flags), \ + .id = _id, \ + } + +struct k230_pll { + struct clk_hw hw; + void __iomem *reg; + /* ensures mutual exclusion for concurrent register access. */ + spinlock_t *lock; + int id; +}; + +#define hw_to_k230_pll(_hw) container_of(_hw, struct k230_pll, hw) + +struct k230_clk_rate_self { + struct clk_hw hw; + void __iomem *reg; + bool read_only; + u32 write_enable_bit; + u32 mul_min; + u32 mul_max; + u32 mul_shift; + u32 mul_mask; + u32 div_min; + u32 div_max; + u32 div_shift; + u32 div_mask; + /* ensures mutual exclusion for concurrent register access. */ + spinlock_t *lock; +}; + +#define hw_to_k230_clk_rate_self(_hw) container_of(_hw, \ + struct k230_clk_rate_self, hw) + +struct k230_clk_rate { + u32 mul_reg_off; + u32 div_reg_off; + struct k230_clk_rate_self clk; + int id; +}; + +static inline struct k230_clk_rate *hw_to_k230_clk_rate(struct clk_hw *hw) +{ + return container_of(hw_to_k230_clk_rate_self(hw), struct k230_clk_rate, + clk); +} + +struct k230_clk_gate { + u32 reg_off; + struct clk_gate clk; + int id; +}; + +struct k230_clk_mux { + u32 reg_off; + struct clk_mux clk; + int id; +}; + +static int k230_pll_prepare(struct clk_hw *hw); +static int k230_pll_enable(struct clk_hw *hw); +static void k230_pll_disable(struct clk_hw *hw); +static int k230_pll_is_enabled(struct clk_hw *hw); +static unsigned long k230_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate); +static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_mul(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_mul(struct clk_hw *hw, + unsigned long parent_rate); +static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_div(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_div(struct clk_hw *hw, + unsigned long parent_rate); +static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_mul_div(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_mul_div(struct clk_hw *hw, + unsigned long parent_rate); + +static const struct clk_ops k230_pll_ops = { + .prepare = k230_pll_prepare, + .enable = k230_pll_enable, + .disable = k230_pll_disable, + .is_enabled = k230_pll_is_enabled, + .recalc_rate = k230_pll_get_rate, +}; + +/* clk_ops for clocks whose rate is determined by a configurable multiplier */ +static const struct clk_ops k230_clk_ops_mul = { + .set_rate = k230_clk_set_rate_mul, + .determine_rate = k230_clk_determine_rate_mul, + .recalc_rate = k230_clk_get_rate_mul, +}; + +/* clk_ops for clocks whose rate is determined by a configurable divider */ +static const struct clk_ops k230_clk_ops_div = { + .set_rate = k230_clk_set_rate_div, + .determine_rate = k230_clk_determine_rate_div, + .recalc_rate = k230_clk_get_rate_div, +}; + +/* clk_ops for clocks whose rate is determined by both a multiplier and a divider */ +static const struct clk_ops k230_clk_ops_mul_div = { + .set_rate = k230_clk_set_rate_mul_div, + .determine_rate = k230_clk_determine_rate_mul_div, + .recalc_rate = k230_clk_get_rate_mul_div, +}; + +K230_CLK_PLL_FORMAT(pll0, 0, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll1, 1, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll2, 2, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll3, 3, CLK_IS_CRITICAL, NULL); + +static struct k230_pll *k230_plls[] = { + &pll0, + &pll1, + &pll2, + &pll3, +}; + +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div2, 1, 2, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div3, 1, 3, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div4, 1, 4, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div16, 1, 16, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div2, 1, 2, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div3, 1, 3, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div4, 1, 4, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div2, 1, 2, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div3, 1, 3, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div4, 1, 4, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div2, 1, 2, 0, &pll3.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div3, 1, 3, 0, &pll3.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div4, 1, 4, 0, &pll3.hw); + +static struct clk_fixed_factor *k230_pll_divs[] = { + &pll0_div2, + &pll0_div3, + &pll0_div4, + &pll0_div16, + &pll1_div2, + &pll1_div3, + &pll1_div4, + &pll2_div2, + &pll2_div3, + &pll2_div4, + &pll3_div2, + &pll3_div3, + &pll3_div4, +}; + +K230_CLK_GATE_FORMAT(cpu0_src_gate, + K230_CPU0_SRC_GATE, + 0, 0, CLK_IS_CRITICAL, 0, + &pll0_div2.hw); + +K230_CLK_RATE_FORMAT(cpu0_src_rate, + K230_CPU0_SRC_RATE, + 1, 16, 1, 0xF, + 16, 16, 0, 0x0, + 0x0, 31, mul, 0x0, + false, 0, + &cpu0_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu0_axi_rate, + K230_CPU0_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x0, 31, div, 0x0, + 0, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_plic_gate, + K230_CPU0_PLIC_GATE, + 0x0, 9, CLK_IS_CRITICAL, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu0_plic_rate, + K230_CPU0_PLIC_RATE, + 1, 1, 0, 0, + 1, 8, 10, 0x7, + 0x0, 31, div, 0x0, + false, 0, + &cpu0_plic_gate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_noc_ddrcp4_gate, + K230_CPU0_NOC_DDRCP4_GATE, + 0x60, 7, CLK_IS_CRITICAL, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_apb_gate, + K230_CPU0_APB_GATE, + 0x0, 13, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(cpu0_apb_rate, + K230_CPU0_APB_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x0, 31, div, 0x0, + false, 0, + &cpu0_apb_gate.clk.hw); + +static const struct clk_parent_data k230_cpu1_src_mux_pdata[] = { + { .hw = &pll1_div2.hw, }, + { .hw = &pll3.hw, }, + { .hw = &pll0.hw, }, +}; + +K230_CLK_MUX_FORMAT(cpu1_src_mux, + K230_CPU1_SRC_MUX, + 0x4, 1, 0x3, + 0, 0, + k230_cpu1_src_mux_pdata); + +K230_CLK_GATE_FORMAT(cpu1_src_gate, + K230_CPU1_SRC_GATE, + 0x4, 0, CLK_IS_CRITICAL, 0, + &cpu1_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_src_rate, + K230_CPU1_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_axi_rate, + K230_CPU1_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu1_plic_gate, + K230_CPU1_PLIC_GATE, + 0x4, 15, CLK_IS_CRITICAL, 0, + &cpu1_src_rate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_plic_rate, + K230_CPU1_PLIC_RATE, + 1, 1, 0, 0, + 1, 8, 16, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_plic_gate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu1_apb_gate, + K230_CPU1_APB_GATE, + 0x4, 19, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_GATE_FORMAT_PNAME(pmu_apb_gate, + K230_PMU_APB_GATE, + 0x10, 0, 0, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT(hs_hclk_high_gate, + K230_HS_HCLK_HIGH_GATE, + 0x18, 1, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_hclk_high_rate, + K230_HS_HCLK_HIGH_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_hclk_high_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_hclk_gate, + K230_HS_HCLK_GATE, + 0x18, 0, 0, 0, + &hs_hclk_high_rate.clk.hw); + +K230_CLK_RATE_FORMAT(hs_hclk_rate, + K230_HS_HCLK_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_hclk_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_ahb_gate, + K230_HS_SD0_AHB_GATE, + 0x18, 2, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_ahb_gate, + K230_HS_SD1_AHB_GATE, + 0x18, 3, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_ahb_gate, + K230_HS_SSI1_AHB_GATE, + 0x18, 7, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_ahb_gate, + K230_HS_SSI2_AHB_GATE, + 0x18, 8, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb0_ahb_gate, + K230_HS_USB0_AHB_GATE, + 0x18, 4, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb1_ahb_gate, + K230_HS_USB1_AHB_GATE, + 0x18, 5, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi0_axi_gate, + K230_HS_SSI0_AXI_GATE, + 0x18, 27, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi0_axi_rate, + K230_HS_SSI0_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi0_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_gate, + K230_HS_SSI1_GATE, + 0x18, 25, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi1_rate, + K230_HS_SSI1_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_gate, + K230_HS_SSI2_GATE, + 0x18, 26, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi2_rate, + K230_HS_SSI2_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_qspi_axi_src_gate, + K230_HS_QSPI_AXI_SRC_GATE, + 0x18, 28, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_qspi_axi_src_rate, + K230_HS_QSPI_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_qspi_axi_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_axi_gate, + K230_HS_SSI1_AXI_GATE, + 0x18, 29, 0, 0, + &hs_qspi_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_axi_gate, + K230_HS_SSI2_AXI_GATE, + 0x18, 30, 0, 0, + &hs_qspi_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd_card_src_gate, + K230_HS_SD_CARD_SRC_GATE, + 0x18, 11, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_sd_card_src_rate, + K230_HS_SD_CARD_SRC_RATE, + 1, 1, 0, 0, + 2, 8, 12, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_card_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_card_gate, + K230_HS_SD0_CARD_GATE, + 0x18, 15, 0, 0, + &hs_sd_card_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_card_gate, + K230_HS_SD1_CARD_GATE, + 0x18, 19, 0, 0, + &hs_sd_card_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd_axi_src_gate, + K230_HS_SD_AXI_SRC_GATE, + 0x18, 9, 0, 0, + &pll2_div4.hw); + +K230_CLK_RATE_FORMAT(hs_sd_axi_src_rate, + K230_HS_SD_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_axi_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_axi_gate, + K230_HS_SD0_AXI_GATE, + 0x18, 13, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_axi_gate, + K230_HS_SD1_AXI_GATE, + 0x18, 17, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_base_gate, + K230_HS_SD0_BASE_GATE, + 0x18, 14, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_base_gate, + K230_HS_SD1_BASE_GATE, + 0x18, 18, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +static const struct clk_parent_data k230_hs_ssi0_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll2_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(hs_ssi0_mux, + K230_HS_SSI0_MUX, + 0x20, 18, 0x1, + 0, 0, + k230_hs_ssi0_mux_pdata); + +K230_CLK_GATE_FORMAT(hs_ssi0_gate, + K230_HS_SSI0_GATE, + 0x18, 24, CLK_IGNORE_UNUSED, 0, + &hs_ssi0_mux.clk.hw); + +K230_CLK_RATE_FORMAT(hs_usb_ref_50m_rate, + K230_HS_USB_REF_50M_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT_PNAME(hs_sd_timer_src_gate, + K230_HS_SD_TIMER_SRC_GATE, + 0x18, 12, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(hs_sd_timer_src_rate, + K230_HS_SD_TIMER_SRC_RATE, + 1, 1, 0, 0, + 24, 32, 15, 0x1F, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_timer_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_timer_gate, + K230_HS_SD0_TIMER_GATE, + 0x18, 16, 0, 0, + &hs_sd_timer_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_timer_gate, + K230_HS_SD1_TIMER_GATE, + 0x18, 20, 0, 0, + &hs_sd_timer_src_rate.clk.hw); + +static const struct clk_parent_data k230_hs_usb_ref_mux_pdata[] = { + { .fw_name = "osc24m", }, + { .hw = &hs_usb_ref_50m_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(hs_usb_ref_mux, + K230_HS_USB_REF_MUX, + 0x18, 23, 0x1, + 0, 0, + k230_hs_usb_ref_mux_pdata); + +K230_CLK_GATE_FORMAT(hs_usb0_ref_gate, + K230_HS_USB0_REF_GATE, + 0x18, 21, CLK_IGNORE_UNUSED, 0, + &hs_usb_ref_mux.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb1_ref_gate, + K230_HS_USB1_REF_GATE, + 0x18, 22, CLK_IGNORE_UNUSED, 0, + &hs_usb_ref_mux.clk.hw); + +K230_CLK_GATE_FORMAT(ls_apb_src_gate, + K230_LS_APB_SRC_GATE, + 0x24, 0, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_apb_src_rate, + K230_LS_APB_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x30, 31, div, 0x0, + false, 0, + &ls_apb_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart0_apb_gate, + K230_LS_UART0_APB_GATE, + 0x24, 1, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart1_apb_gate, + K230_LS_UART1_APB_GATE, + 0x24, 2, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart2_apb_gate, + K230_LS_UART2_APB_GATE, + 0x24, 3, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart3_apb_gate, + K230_LS_UART3_APB_GATE, + 0x24, 4, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart4_apb_gate, + K230_LS_UART4_APB_GATE, + 0x24, 5, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c0_apb_gate, + K230_LS_I2C0_APB_GATE, + 0x24, 6, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c1_apb_gate, + K230_LS_I2C1_APB_GATE, + 0x24, 7, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c2_apb_gate, + K230_LS_I2C2_APB_GATE, + 0x24, 8, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c3_apb_gate, + K230_LS_I2C3_APB_GATE, + 0x24, 9, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c4_apb_gate, + K230_LS_I2C4_APB_GATE, + 0x24, 10, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_gpio_apb_gate, + K230_LS_GPIO_APB_GATE, + 0x24, 11, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_pwm_apb_gate, + K230_LS_PWM_APB_GATE, + 0x24, 12, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink0_apb_gate, + K230_LS_JAMLINK0_APB_GATE, + 0x28, 4, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink1_apb_gate, + K230_LS_JAMLINK1_APB_GATE, + 0x28, 5, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink2_apb_gate, + K230_LS_JAMLINK2_APB_GATE, + 0x28, 6, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink3_apb_gate, + K230_LS_JAMLINK3_APB_GATE, + 0x28, 7, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_audio_apb_gate, + K230_LS_AUDIO_APB_GATE, + 0x24, 13, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_adc_apb_gate, + K230_LS_ADC_APB_GATE, + 0x24, 15, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_apb_gate, + K230_LS_CODEC_APB_GATE, + 0x24, 14, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c0_gate, + K230_LS_I2C0_GATE, + 0x24, 21, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c0_rate, + K230_LS_I2C0_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c0_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c1_gate, + K230_LS_I2C1_GATE, + 0x24, 22, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c1_rate, + K230_LS_I2C1_RATE, + 1, 1, 0, 0, + 1, 8, 18, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c2_gate, + K230_LS_I2C2_GATE, + 0x24, 23, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c2_rate, + K230_LS_I2C2_RATE, + 1, 1, 0, 0, + 1, 8, 21, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c3_gate, + K230_LS_I2C3_GATE, + 0x24, 24, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c3_rate, + K230_LS_I2C3_RATE, + 1, 1, 0, 0, + 1, 8, 24, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c3_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c4_gate, + K230_LS_I2C4_GATE, + 0x24, 25, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c4_rate, + K230_LS_I2C4_RATE, + 1, 1, 0, 0, + 1, 8, 27, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c4_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_adc_gate, + K230_LS_CODEC_ADC_GATE, + 0x24, 29, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_codec_adc_rate, + K230_LS_CODEC_ADC_RATE, + 0x10, 0x1B9, 14, 0x1FFF, + 0xC35, 0x3D09, 0, 0x3FFF, + 0x38, 31, mul_div, 0x38, + false, 0, + &ls_codec_adc_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_dac_gate, + K230_LS_CODEC_DAC_GATE, + 0x24, 30, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_codec_dac_rate, + K230_LS_CODEC_DAC_RATE, + 0x10, 0x1B9, 14, 0x1FFF, + 0xC35, 0x3D09, 0, 0x3FFF, + 0x3C, 31, mul_div, 0x3C, + false, 0, + &ls_codec_dac_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_audio_dev_gate, + K230_LS_AUDIO_DEV_GATE, + 0x24, 28, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_audio_dev_rate, + K230_LS_AUDIO_DEV_RATE, + 0x4, 0x1B9, 16, 0x7FFF, + 0xC35, 0xF424, 0, 0xFFFF, + 0x34, 31, mul_div, 0x34, + false, 0, + &ls_audio_dev_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_pdm_gate, + K230_LS_PDM_GATE, + 0x24, 31, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_pdm_rate, + K230_LS_PDM_RATE, + 0x2, 0x1B9, 0, 0xFFFF, + 0xC35, 0x1E848, 0, 0x1FFFF, + 0x40, 0, mul_div, 0x44, + false, 0, + &ls_pdm_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_adc_gate, + K230_LS_ADC_GATE, + 0x24, 26, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_adc_rate, + K230_LS_ADC_RATE, + 1, 1, 0, 0, + 1, 1024, 3, 0x3FF, + 0x30, 31, div, 0x0, + false, 0, + &ls_adc_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart0_gate, + K230_LS_UART0_GATE, + 0x24, 16, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart0_rate, + K230_LS_UART0_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart0_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart1_gate, + K230_LS_UART1_GATE, + 0x24, 17, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart1_rate, + K230_LS_UART1_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart2_gate, + K230_LS_UART2_GATE, + 0x24, 18, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart2_rate, + K230_LS_UART2_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart3_gate, + K230_LS_UART3_GATE, + 0x24, 19, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart3_rate, + K230_LS_UART3_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart3_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart4_gate, + K230_LS_UART4_GATE, + 0x24, 20, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart4_rate, + K230_LS_UART4_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart4_gate.clk.hw); + +K230_CLK_RATE_FORMAT(ls_jamlinkco_src_rate, + K230_LS_JAMLINKCO_SRC_RATE, + 1, 1, 0, 0, + 2, 512, 23, 0xFF, + 0x30, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink0co_gate, + K230_LS_JAMLINK0CO_GATE, + 0x28, 0, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink1co_gate, + K230_LS_JAMLINK1CO_GATE, + 0x28, 1, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink2co_gate, + K230_LS_JAMLINK2CO_GATE, + 0x28, 2, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink3co_gate, + K230_LS_JAMLINK3CO_GATE, + 0x28, 3, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(ls_gpio_debounce_gate, + K230_LS_GPIO_DEBOUNCE_GATE, + 0x24, 27, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(ls_gpio_debounce_rate, + K230_LS_GPIO_DEBOUNCE_RATE, + 1, 1, 0, 0, + 1, 1024, 13, 0x3FF, + 0x30, 31, div, 0x0, + false, 0, + &ls_gpio_debounce_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sysctl_wdt0_apb_gate, + K230_SYSCTL_WDT0_APB_GATE, + 0x50, 1, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_wdt1_apb_gate, + K230_SYSCTL_WDT1_APB_GATE, + 0x50, 2, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_timer_apb_gate, + K230_SYSCTL_TIMER_APB_GATE, + 0x50, 3, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_iomux_apb_gate, + K230_SYSCTL_IOMUX_APB_GATE, + 0x50, 20, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_mailbox_apb_gate, + K230_SYSCTL_MAILBOX_APB_GATE, + 0x50, 4, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_hdi_gate, + K230_SYSCTL_HDI_GATE, + 0x50, 21, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(sysctl_hdi_rate, + K230_SYSCTL_HDI_RATE, + 1, 1, 0, 0, + 1, 8, 28, 0x7, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_hdi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sysctl_time_stamp_gate, + K230_SYSCTL_TIME_STAMP_GATE, + 0x50, 19, CLK_IS_CRITICAL, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sysctl_time_stamp_rate, + K230_SYSCTL_TIME_STAMP_RATE, + 1, 1, 0, 0, + 1, 32, 15, 0x1F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_time_stamp_gate.clk.hw); + +K230_CLK_RATE_FORMAT_PNAME(sysctl_temp_sensor_rate, + K230_SYSCTL_TEMP_SENSOR_RATE, + 1, 1, 0, 0, + 1, 256, 20, 0xFF, + 0x58, 31, div, 0x0, + false, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT_PNAME(sysctl_wdt0_gate, + K230_SYSCTL_WDT0_GATE, + 0x50, 5, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(sysctl_wdt0_rate, + K230_SYSCTL_WDT0_RATE, + 1, 1, 0, 0, + 1, 64, 3, 0x3F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_wdt0_gate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(sysctl_wdt1_gate, + K230_SYSCTL_WDT1_GATE, + 0x50, 6, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(sysctl_wdt1_rate, + K230_SYSCTL_WDT1_RATE, + 1, 1, 0, 0, + 1, 64, 9, 0x3F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_wdt1_gate.clk.hw); + +K230_CLK_RATE_FORMAT(timer0_src_rate, + K230_TIMER0_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer1_src_rate, + K230_TIMER1_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer2_src_rate, + K230_TIMER2_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer3_src_rate, + K230_TIMER3_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer4_src_rate, + K230_TIMER4_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer5_src_rate, + K230_TIMER5_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +static const struct clk_parent_data k230_timer0_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer0_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer0_mux, + K230_TIMER0_MUX, + 0x50, 7, 0x1, + 0, 0, + k230_timer0_mux_pdata); + +K230_CLK_GATE_FORMAT(timer0_gate, + K230_TIMER0_GATE, + 0x50, 13, CLK_IGNORE_UNUSED, 0, + &timer0_mux.clk.hw); + +static const struct clk_parent_data k230_timer1_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer1_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer1_mux, + K230_TIMER1_MUX, + 0x50, 8, 0x1, + 0, 0, + k230_timer1_mux_pdata); + +K230_CLK_GATE_FORMAT(timer1_gate, + K230_TIMER1_GATE, + 0x50, 14, CLK_IGNORE_UNUSED, 0, + &timer1_mux.clk.hw); + +static const struct clk_parent_data k230_timer2_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer2_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer2_mux, + K230_TIMER2_MUX, + 0x50, 9, 0x1, + 0, 0, + k230_timer2_mux_pdata); + +K230_CLK_GATE_FORMAT(timer2_gate, + K230_TIMER2_GATE, + 0x50, 15, CLK_IGNORE_UNUSED, 0, + &timer2_mux.clk.hw); + +static const struct clk_parent_data k230_timer3_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer3_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer3_mux, + K230_TIMER3_MUX, + 0x50, 10, 0x1, + 0, 0, + k230_timer3_mux_pdata); + +K230_CLK_GATE_FORMAT(timer3_gate, + K230_TIMER3_GATE, + 0x50, 16, CLK_IGNORE_UNUSED, 0, + &timer3_mux.clk.hw); + +static const struct clk_parent_data k230_timer4_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer4_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer4_mux, + K230_TIMER4_MUX, + 0x50, 11, 0x1, + 0, 0, + k230_timer4_mux_pdata); + +K230_CLK_GATE_FORMAT(timer4_gate, + K230_TIMER4_GATE, + 0x50, 17, CLK_IGNORE_UNUSED, 0, + &timer4_mux.clk.hw); + +static const struct clk_parent_data k230_timer5_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer5_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer5_mux, + K230_TIMER5_MUX, + 0x50, 12, 0x1, + 0, 0, + k230_timer5_mux_pdata); + +K230_CLK_GATE_FORMAT(timer5_gate, + K230_TIMER5_GATE, + 0x50, 18, CLK_IGNORE_UNUSED, 0, + &timer5_mux.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_apb_gate, + K230_SHRM_APB_GATE, + 0x5C, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(shrm_apb_rate, + K230_SHRM_APB_RATE, + 1, 1, 0, 0, + 1, 8, 18, 0x7, + 0x5C, 31, div, 0x0, + false, 0, + &shrm_apb_gate.clk.hw); + +static const struct clk_parent_data k230_shrm_sram_mux_pdata[] = { + { .hw = &pll3_div2.hw, }, + { .hw = &pll0_div2.hw, }, +}; + +K230_CLK_MUX_FORMAT(shrm_sram_mux, + K230_SHRM_SRAM_MUX, + 0x50, 14, 0x1, + 0, 0, + k230_shrm_sram_mux_pdata); + +K230_CLK_GATE_FORMAT(shrm_sram_gate, + K230_SHRM_SRAM_GATE, + 0x5c, 10, CLK_IGNORE_UNUSED, 0, + &shrm_sram_mux.clk.hw); + +K230_CLK_FIXED_FACTOR_FORMAT(shrm_sram_div2, + 1, 2, 0, + &shrm_sram_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_axi_slave_gate, + K230_SHRM_AXI_SLAVE_GATE, + 0x5C, 11, CLK_IGNORE_UNUSED, 0, + &shrm_sram_div2.hw); + +K230_CLK_GATE_FORMAT(shrm_axi_gate, + K230_SHRM_AXI_GATE, + 0x5C, 12, 0, 0, + &pll0_div4.hw); + +K230_CLK_GATE_FORMAT(shrm_nonai2d_axi_gate, + K230_SHRM_NONAI2D_AXI_GATE, + 0x5C, 9, 0, 0, + &shrm_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_decompress_axi_gate, + K230_SHRM_DECOMPRESS_AXI_GATE, + 0x5C, 7, CLK_IGNORE_UNUSED, 0, + &shrm_sram_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_sdma_axi_gate, + K230_SHRM_SDMA_AXI_GATE, + 0x5C, 5, 0, 0, + &shrm_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_pdma_axi_gate, + K230_SHRM_PDMA_AXI_GATE, + 0x5C, 3, 0, 0, + &shrm_axi_gate.clk.hw); + +static const struct clk_parent_data k230_ddrc_src_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll0_div3.hw, }, + { .hw = &pll2_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(ddrc_src_mux, + K230_DDRC_SRC_MUX, + 0x60, 0, 0x3, + 0, 0, + k230_ddrc_src_mux_pdata); + +K230_CLK_GATE_FORMAT(ddrc_src_gate, + K230_DDRC_SRC_GATE, + 0x60, 2, CLK_IGNORE_UNUSED, 0, + &ddrc_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(ddrc_src_rate, + K230_DDRC_SRC_RATE, + 1, 1, 0, 0, + 1, 16, 10, 0xF, + 0x60, 31, div, 0x0, + false, 0, + &ddrc_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ddrc_bypass_gate, + K230_DDRC_BYPASS_GATE, + 0x60, 8, 0, 0, + &pll2_div4.hw); + +K230_CLK_GATE_FORMAT(ddrc_apb_gate, + K230_DDRC_APB_GATE, + 0x60, 9, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ddrc_apb_rate, + K230_DDRC_APB_RATE, + 1, 1, 0, 0, + 1, 16, 14, 0xF, + 0x60, 31, div, 0x0, + false, 0, + &ddrc_apb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_ahb_gate, + K230_DISPLAY_AHB_GATE, + 0x74, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(display_ahb_rate, + K230_DISPLAY_AHB_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x78, 31, div, 0x0, + false, 0, + &display_ahb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_axi_gate, + K230_DISPLAY_AXI_GATE, + 0x74, 1, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(display_clkext_rate, + K230_DISPLAY_CLKEXT_RATE, + 1, 1, 0, 0, + 1, 16, 16, 0xF, + 0x78, 31, div, 0x0, + false, 0, + &pll0_div3.hw); + +K230_CLK_GATE_FORMAT(display_gpu_gate, + K230_DISPLAY_GPU_GATE, + 0x74, 6, 0, 0, + &pll0_div3.hw); + +K230_CLK_RATE_FORMAT(display_gpu_rate, + K230_DISPLAY_GPU_RATE, + 1, 1, 0, 0, + 1, 16, 20, 0xF, + 0x78, 31, div, 0x0, + false, 0, + &display_gpu_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_dpip_gate, + K230_DISPLAY_DPIP_GATE, + 0x74, 2, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(display_dpip_rate, + K230_DISPLAY_DPIP_RATE, + 1, 1, 0, 0, + 1, 256, 3, 0xFF, + 0x78, 31, div, 0x0, + false, 0, + &display_dpip_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_cfg_gate, + K230_DISPLAY_CFG_GATE, + 0x74, 4, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(display_cfg_rate, + K230_DISPLAY_CFG_RATE, + 1, 1, 0, 0, + 1, 32, 11, 0x1F, + 0x78, 31, div, 0x0, + false, 0, + &display_cfg_gate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(display_ref_gate, + K230_DISPLAY_REF_GATE, + 0x74, 3, 0, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT(vpu_src_gate, + K230_VPU_SRC_GATE, + 0xC, 0, 0, 0, + &pll0_div2.hw); + +K230_CLK_RATE_FORMAT(vpu_src_rate, + K230_VPU_SRC_RATE, + 1, 16, 1, 0xF, + 16, 16, 0, 0, + 0x0, 31, mul, 0xC, + false, 0, + &vpu_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(vpu_axi_src_rate, + K230_VPU_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 16, 6, 0xF, + 0xC, 31, div, 0x0, + false, 0, + &vpu_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_axi_gate, + K230_VPU_AXI_GATE, + 0xC, 5, 0, 0, + &vpu_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_ddrcp2_gate, + K230_VPU_DDRCP2_GATE, + 0x60, 5, 0, 0, + &vpu_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_cfg_gate, + K230_VPU_CFG_GATE, + 0xC, 10, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(vpu_cfg_rate, + K230_VPU_CFG_RATE, + 1, 1, 0, 0, + 1, 16, 11, 0xF, + 0xC, 31, div, 0x0, + false, 0, + &vpu_cfg_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_apb_gate, + K230_SEC_APB_GATE, + 0x80, 0, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_apb_rate, + K230_SEC_APB_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x80, 31, div, 0x0, + false, 0, + &sec_apb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_fix_gate, + K230_SEC_FIX_GATE, + 0x80, 5, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_fix_rate, + K230_SEC_FIX_RATE, + 1, 1, 0, 0, + 1, 32, 6, 0x1F, + 0x80, 31, div, 0x0, + false, 0, + &sec_fix_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_axi_gate, + K230_SEC_AXI_GATE, + 0x80, 4, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_axi_rate, + K230_SEC_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 11, 0x3, + 0x80, 31, div, 0, + false, 0, + &sec_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(usb_480m_gate, + K230_USB_480M_GATE, + 0x100, 0, 0, 0, + &pll1.hw); + +K230_CLK_RATE_FORMAT(usb_480m_rate, + K230_USB_480M_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x100, 31, div, 0, + false, 0, + &usb_480m_gate.clk.hw); + +K230_CLK_GATE_FORMAT(usb_100m_gate, + K230_USB_100M_GATE, + 0x100, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(usb_100m_rate, + K230_USB_100M_RATE, + 1, 1, 0, 0, + 1, 8, 4, 0x7, + 0x100, 31, div, 0, + false, 0, + &usb_100m_gate.clk.hw); + +K230_CLK_GATE_FORMAT(dphy_dft_gate, + K230_DPHY_DFT_GATE, + 0x100, 0, 0, 0, + &pll0.hw); + +K230_CLK_RATE_FORMAT(dphy_dft_rate, + K230_DPHY_DFT_RATE, + 1, 1, 0, 0, + 1, 16, 1, 0xF, + 0x104, 31, div, 0, + false, 0, + &dphy_dft_gate.clk.hw); + +K230_CLK_GATE_FORMAT(spi2axi_gate, + K230_SPI2AXI_GATE, + 0x108, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(spi2axi_rate, + K230_SPI2AXI_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x108, 31, div, 0x0, + false, 0, + &spi2axi_gate.clk.hw); + +static const struct clk_parent_data k230_ai_src_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll3_div2.hw, }, +}; + +K230_CLK_MUX_FORMAT(ai_src_mux, + K230_AI_SRC_MUX, + 0x8, 2, 0x1, + 0, 0, + k230_ai_src_mux_pdata); + +K230_CLK_GATE_FORMAT(ai_src_gate, + K230_AI_SRC_GATE, + 0x8, 0, CLK_IGNORE_UNUSED, 0, + &ai_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(ai_src_rate, + K230_AI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x8, 31, div, 0x0, + false, 0, + &ai_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ai_axi_gate, + K230_AI_AXI_GATE, + 0x8, 10, 0, 0, + &pll0_div4.hw); + +static const struct clk_parent_data k230_camera0_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera0_mux, + K230_CAMERA0_MUX, + 0x6C, 3, 0x3, + 0, 0, + k230_camera0_mux_pdata); + +K230_CLK_GATE_FORMAT(camera0_gate, + K230_CAMERA0_GATE, + 0x6C, 0, CLK_IGNORE_UNUSED, 0, + &camera0_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera0_rate, + K230_CAMERA0_RATE, + 1, 1, 0, 0, + 1, 32, 5, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera0_gate.clk.hw); + +static const struct clk_parent_data k230_camera1_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera1_mux, + K230_CAMERA1_MUX, + 0x6C, 10, 0x3, + 0, 0, + k230_camera1_mux_pdata); + +K230_CLK_GATE_FORMAT(camera1_gate, + K230_CAMERA1_GATE, + 0x6C, 1, CLK_IGNORE_UNUSED, 0, + &camera1_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera1_rate, + K230_CAMERA1_RATE, + 1, 1, 0, 0, + 1, 32, 12, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera1_gate.clk.hw); + +static const struct clk_parent_data k230_camera2_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera2_mux, + K230_CAMERA2_MUX, + 0x6C, 17, 0x3, + 0, 0, + k230_camera2_mux_pdata); + +K230_CLK_GATE_FORMAT(camera2_gate, + K230_CAMERA2_GATE, + 0x6C, 2, CLK_IGNORE_UNUSED, 0, + &camera2_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera2_rate, + K230_CAMERA2_RATE, + 1, 1, 0, 0, + 1, 32, 19, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera2_gate.clk.hw); + +static struct k230_clk_mux *k230_clk_muxs[] = { + &hs_ssi0_mux, + &hs_usb_ref_mux, + &cpu1_src_mux, + &timer0_mux, + &timer1_mux, + &timer2_mux, + &timer3_mux, + &timer4_mux, + &timer5_mux, + &shrm_sram_mux, + &ddrc_src_mux, + &ai_src_mux, + &camera0_mux, + &camera1_mux, + &camera2_mux, +}; + +#define K230_CLK_MUX_NUM ARRAY_SIZE(k230_clk_muxs) + +static struct k230_clk_gate *k230_clk_gates[] = { + &cpu0_src_gate, + &cpu0_plic_gate, + &cpu0_noc_ddrcp4_gate, + &cpu0_apb_gate, + &cpu1_src_gate, + &cpu1_plic_gate, + &cpu1_apb_gate, + &pmu_apb_gate, + &hs_hclk_high_gate, + &hs_hclk_gate, + &hs_sd0_ahb_gate, + &hs_sd1_ahb_gate, + &hs_ssi1_ahb_gate, + &hs_ssi2_ahb_gate, + &hs_usb0_ahb_gate, + &hs_usb1_ahb_gate, + &hs_ssi0_axi_gate, + &hs_ssi1_gate, + &hs_ssi2_gate, + &hs_qspi_axi_src_gate, + &hs_ssi1_axi_gate, + &hs_ssi2_axi_gate, + &hs_sd_card_src_gate, + &hs_sd0_card_gate, + &hs_sd1_card_gate, + &hs_sd_axi_src_gate, + &hs_sd0_axi_gate, + &hs_sd1_axi_gate, + &hs_sd0_base_gate, + &hs_sd1_base_gate, + &hs_ssi0_gate, + &hs_sd_timer_src_gate, + &hs_sd0_timer_gate, + &hs_sd1_timer_gate, + &hs_usb0_ref_gate, + &hs_usb1_ref_gate, + &ls_apb_src_gate, + &ls_uart0_apb_gate, + &ls_uart1_apb_gate, + &ls_uart2_apb_gate, + &ls_uart3_apb_gate, + &ls_uart4_apb_gate, + &ls_i2c0_apb_gate, + &ls_i2c1_apb_gate, + &ls_i2c2_apb_gate, + &ls_i2c3_apb_gate, + &ls_i2c4_apb_gate, + &ls_gpio_apb_gate, + &ls_pwm_apb_gate, + &ls_jamlink0_apb_gate, + &ls_jamlink1_apb_gate, + &ls_jamlink2_apb_gate, + &ls_jamlink3_apb_gate, + &ls_audio_apb_gate, + &ls_adc_apb_gate, + &ls_codec_apb_gate, + &ls_i2c0_gate, + &ls_i2c1_gate, + &ls_i2c2_gate, + &ls_i2c3_gate, + &ls_i2c4_gate, + &ls_codec_adc_gate, + &ls_codec_dac_gate, + &ls_audio_dev_gate, + &ls_pdm_gate, + &ls_adc_gate, + &ls_uart0_gate, + &ls_uart1_gate, + &ls_uart2_gate, + &ls_uart3_gate, + &ls_uart4_gate, + &ls_jamlink0co_gate, + &ls_jamlink1co_gate, + &ls_jamlink2co_gate, + &ls_jamlink3co_gate, + &ls_gpio_debounce_gate, + &sysctl_wdt0_apb_gate, + &sysctl_wdt1_apb_gate, + &sysctl_timer_apb_gate, + &sysctl_iomux_apb_gate, + &sysctl_mailbox_apb_gate, + &sysctl_hdi_gate, + &sysctl_time_stamp_gate, + &sysctl_wdt0_gate, + &sysctl_wdt1_gate, + &timer0_gate, + &timer1_gate, + &timer2_gate, + &timer3_gate, + &timer4_gate, + &timer5_gate, + &shrm_apb_gate, + &shrm_sram_gate, + &shrm_axi_gate, + &shrm_axi_slave_gate, + &shrm_nonai2d_axi_gate, + &shrm_decompress_axi_gate, + &shrm_sdma_axi_gate, + &shrm_pdma_axi_gate, + &ddrc_src_gate, + &ddrc_bypass_gate, + &ddrc_apb_gate, + &display_ahb_gate, + &display_axi_gate, + &display_gpu_gate, + &display_dpip_gate, + &display_cfg_gate, + &display_ref_gate, + &vpu_src_gate, + &vpu_axi_gate, + &vpu_ddrcp2_gate, + &vpu_cfg_gate, + &sec_apb_gate, + &sec_fix_gate, + &sec_axi_gate, + &usb_480m_gate, + &usb_100m_gate, + &dphy_dft_gate, + &spi2axi_gate, + &ai_src_gate, + &ai_axi_gate, + &camera0_gate, + &camera1_gate, + &camera2_gate, +}; + +#define K230_CLK_GATE_NUM ARRAY_SIZE(k230_clk_gates) + +static struct k230_clk_rate *k230_clk_rates[] = { + &cpu0_src_rate, + &cpu0_axi_rate, + &cpu0_plic_rate, + &cpu0_apb_rate, + &cpu1_src_rate, + &cpu1_axi_rate, + &cpu1_plic_rate, + &hs_hclk_high_rate, + &hs_hclk_rate, + &hs_ssi0_axi_rate, + &hs_ssi1_rate, + &hs_ssi2_rate, + &hs_qspi_axi_src_rate, + &hs_sd_card_src_rate, + &hs_sd_axi_src_rate, + &hs_usb_ref_50m_rate, + &hs_sd_timer_src_rate, + &ls_apb_src_rate, + &ls_gpio_debounce_rate, + &ls_i2c0_rate, + &ls_i2c1_rate, + &ls_i2c2_rate, + &ls_i2c3_rate, + &ls_i2c4_rate, + &ls_codec_adc_rate, + &ls_codec_dac_rate, + &ls_audio_dev_rate, + &ls_pdm_rate, + &ls_adc_rate, + &ls_uart0_rate, + &ls_uart1_rate, + &ls_uart2_rate, + &ls_uart3_rate, + &ls_uart4_rate, + &ls_jamlinkco_src_rate, + &sysctl_hdi_rate, + &sysctl_time_stamp_rate, + &sysctl_temp_sensor_rate, + &sysctl_wdt0_rate, + &sysctl_wdt1_rate, + &timer0_src_rate, + &timer1_src_rate, + &timer2_src_rate, + &timer3_src_rate, + &timer4_src_rate, + &timer5_src_rate, + &shrm_apb_rate, + &ddrc_src_rate, + &ddrc_apb_rate, + &display_ahb_rate, + &display_clkext_rate, + &display_gpu_rate, + &display_dpip_rate, + &display_cfg_rate, + &vpu_src_rate, + &vpu_axi_src_rate, + &vpu_cfg_rate, + &sec_apb_rate, + &sec_fix_rate, + &sec_axi_rate, + &usb_480m_rate, + &usb_100m_rate, + &dphy_dft_rate, + &spi2axi_rate, + &ai_src_rate, + &camera0_rate, + &camera1_rate, + &camera2_rate, +}; + +#define K230_CLK_RATE_NUM ARRAY_SIZE(k230_clk_rates) + +#define K230_CLK_NUM (K230_CLK_MUX_NUM + K230_CLK_GATE_NUM + K230_CLK_RATE_NUM + 1) + +static int k230_pll_prepare(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + + /* wait for PLL lock until it reaches lock status */ + return readl_poll_timeout(K230_PLLX_LOCK_ADDR(pll->reg, pll->id), reg, + reg & K230_PLL_LOCK_STATUS_MASK, + K230_PLL_LOCK_TIME_DELAY, K230_PLL_LOCK_TIMEOUT); +} + +static inline bool k230_pll_hw_is_enabled(struct k230_pll *pll) +{ + return readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)) & K230_PLL_GATE_ENABLE; +} + +static void k230_pll_enable_hw(struct k230_pll *pll) +{ + u32 reg; + + if (k230_pll_hw_is_enabled(pll)) + return; + + reg = readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)); + reg |= K230_PLL_GATE_ENABLE | K230_PLL_GATE_WRITE_ENABLE; + writel(reg, K230_PLLX_GATE_ADDR(pll->reg, pll->id)); +} + +static int k230_pll_enable(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + + guard(spinlock)(pll->lock); + + k230_pll_enable_hw(pll); + + return 0; +} + +static void k230_pll_disable(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + + guard(spinlock)(pll->lock); + + reg = readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)); + reg &= ~(K230_PLL_GATE_ENABLE); + reg |= (K230_PLL_GATE_WRITE_ENABLE); + writel(reg, K230_PLLX_GATE_ADDR(pll->reg, pll->id)); +} + +static int k230_pll_is_enabled(struct clk_hw *hw) +{ + return k230_pll_hw_is_enabled(hw_to_k230_pll(hw)); +} + +static unsigned long k230_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + u32 r, f, od; + + guard(spinlock)(pll->lock); + + reg = readl(K230_PLLX_BYPASS_ADDR(pll->reg, pll->id)); + if (reg & K230_PLL_BYPASS_ENABLE) + return parent_rate; + + reg = readl(K230_PLLX_LOCK_ADDR(pll->reg, pll->id)); + if (!(reg & (K230_PLL_LOCK_STATUS_MASK))) + return 0; + + reg = readl(K230_PLLX_DIV_ADDR(pll->reg, pll->id)); + r = FIELD_GET(K230_PLL_R_MASK, reg) + 1; + f = FIELD_GET(K230_PLL_F_MASK, reg) + 1; + od = FIELD_GET(K230_PLL_OD_MASK, reg) + 1; + + return mul_u64_u32_div(parent_rate, f, r * od); +} + +static int k230_register_plls(struct platform_device *pdev, spinlock_t *lock, + void __iomem *reg) +{ + int i, ret; + struct k230_pll *pll; + + for (i = 0; i < ARRAY_SIZE(k230_plls); i++) { + const char *name; + + pll = k230_plls[i]; + + name = pll->hw.init->name; + pll->lock = lock; + pll->reg = reg; + + ret = devm_clk_hw_register(&pdev->dev, &pll->hw); + if (ret) + return ret; + + ret = devm_clk_hw_register_clkdev(&pdev->dev, &pll->hw, name, NULL); + if (ret) + return ret; + } + + return 0; +} + +static int k230_register_pll_divs(struct platform_device *pdev) +{ + struct clk_fixed_factor *pll_div; + int ret; + + for (int i = 0; i < ARRAY_SIZE(k230_pll_divs); i++) { + const char *name; + + pll_div = k230_pll_divs[i]; + + name = pll_div->hw.init->name; + + ret = devm_clk_hw_register(&pdev->dev, &pll_div->hw); + if (ret) + return ret; + + ret = devm_clk_hw_register_clkdev(&pdev->dev, &pll_div->hw, + name, NULL); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long k230_clk_get_rate_mul(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + div = rate_self->div_max; + mul = (readl(rate_self->reg + clk->mul_reg_off) >> rate_self->mul_shift) + & rate_self->mul_mask; + + return mul_u64_u32_div(parent_rate, mul + 1, div); +} + +static unsigned long k230_clk_get_rate_div(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + mul = rate_self->mul_max; + div = (readl(rate_self->reg + clk->div_reg_off) >> rate_self->div_shift) + & rate_self->div_mask; + + return mul_u64_u32_div(parent_rate, mul, div + 1); +} + +static unsigned long k230_clk_get_rate_mul_div(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + div = (readl(rate_self->reg + clk->div_reg_off) >> rate_self->div_shift) + & rate_self->div_mask; + mul = (readl(rate_self->reg + clk->mul_reg_off) >> rate_self->mul_shift) + & rate_self->mul_mask; + + return mul_u64_u32_div(parent_rate, mul, div); +} + +static int k230_clk_find_approximate_mul(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, unsigned long parent_rate, + u32 *div, u32 *mul) +{ + long abs_min; + long abs_current; + long perfect_divide; + + if (!rate || !parent_rate || !mul_min) + return -EINVAL; + + perfect_divide = (long)((parent_rate * 1000) / rate); + abs_min = abs(perfect_divide - + (long)(((long)div_max * 1000) / (long)mul_min)); + *mul = mul_min; + + for (u32 i = mul_min + 1; i <= mul_max; i++) { + abs_current = abs(perfect_divide - + (long)(((long)div_max * 1000) / (long)i)); + + if (abs_min > abs_current) { + abs_min = abs_current; + *mul = i; + } + } + + *div = div_max; + + return 0; +} + +static int k230_clk_find_approximate_div(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, unsigned long parent_rate, + u32 *div, u32 *mul) +{ + long abs_min; + long abs_current; + long perfect_divide; + + if (!rate || !parent_rate || !mul_max) + return -EINVAL; + + perfect_divide = (long)((parent_rate * 1000) / rate); + abs_min = abs(perfect_divide - + (long)(((long)div_min * 1000) / (long)mul_max)); + *div = div_min; + + for (u32 i = div_min + 1; i <= div_max; i++) { + abs_current = abs(perfect_divide - + (long)(((long)i * 1000) / (long)mul_max)); + + if (abs_min > abs_current) { + abs_min = abs_current; + *div = i; + } + } + + *mul = mul_max; + + return 0; +} + +static int k230_clk_find_approximate_mul_div(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, + unsigned long parent_rate, + u32 *div, u32 *mul) +{ + unsigned long best_mul, best_div; + + if (!rate || !parent_rate || !mul_min) + return -EINVAL; + + rational_best_approximation(rate, parent_rate, + (unsigned long)mul_max, (unsigned long)div_max, + &best_mul, &best_div); + + if (best_mul < mul_min) + best_mul = mul_min; + + if (best_div < div_min) + best_div = div_min; + + *mul = (u32)best_mul; + *div = (u32)best_div; + + return 0; +} + +static int k230_clk_determine_rate_mul(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_mul(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_determine_rate_div(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_determine_rate_mul_div(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_mul_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, mul_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_mul(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); + mul_reg |= BIT(rate_self->write_enable_bit); + writel(mul_reg, rate_self->reg + clk->mul_reg_off); + + return 0; +} + +static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, div_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); + div_reg |= BIT(rate_self->write_enable_bit); + writel(div_reg, rate_self->reg + clk->div_reg_off); + + return 0; +} + +static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, div_reg, mul_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_mul_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); + div_reg |= BIT(rate_self->write_enable_bit); + writel(div_reg, rate_self->reg + clk->div_reg_off); + + mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); + mul_reg |= BIT(rate_self->write_enable_bit); + writel(mul_reg, rate_self->reg + clk->mul_reg_off); + + return 0; +} + +static int k230_register_clk(int id, struct clk_hw *hw, struct device *dev, + struct clk_hw_onecell_data *hw_data) +{ + int ret; + + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + hw_data->hws[id] = hw; + + return 0; +} + +static int k230_register_clks(struct platform_device *pdev, + struct clk_hw_onecell_data *hw_data, + spinlock_t *lock, void __iomem *reg) +{ + int i, ret; + struct device *dev = &pdev->dev; + struct clk_fixed_factor *fixed_factor = &shrm_sram_div2; + struct k230_clk_mux *mux; + struct k230_clk_gate *gate; + struct k230_clk_rate *rate; + + for (i = 0; i < K230_CLK_MUX_NUM; i++) { + mux = k230_clk_muxs[i]; + mux->clk.lock = lock; + mux->clk.reg = reg + mux->reg_off; + + ret = k230_register_clk(mux->id, &mux->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + for (i = 0; i < K230_CLK_GATE_NUM; i++) { + gate = k230_clk_gates[i]; + gate->clk.lock = lock; + gate->clk.reg = reg + gate->reg_off; + + ret = k230_register_clk(gate->id, &gate->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + for (i = 0; i < K230_CLK_RATE_NUM; i++) { + rate = k230_clk_rates[i]; + rate->clk.lock = lock; + rate->clk.reg = reg; + + ret = k230_register_clk(rate->id, &rate->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + ret = k230_register_clk(K230_SHRM_SRAM_DIV2, &fixed_factor->hw, dev, hw_data); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, hw_data); +} + +static int k230_clk_init_plls(struct platform_device *pdev) +{ + int ret; + void __iomem *reg; + /* used for all the plls */ + spinlock_t *lock; + + lock = devm_kzalloc(&pdev->dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = k230_register_plls(pdev, lock, reg); + if (ret) + return ret; + + ret = k230_register_pll_divs(pdev); + if (ret) + return ret; + + return 0; +} + +static int k230_clk_init_clks(struct platform_device *pdev, + struct clk_hw_onecell_data *hw_data) +{ + int ret; + void __iomem *reg; + /* used for all the clocks */ + spinlock_t *lock; + + lock = devm_kzalloc(&pdev->dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + hw_data->num = K230_CLK_NUM; + + reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = k230_register_clks(pdev, hw_data, lock, reg); + if (ret) + return ret; + + return 0; +} + +static int k230_clk_probe(struct platform_device *pdev) +{ + int ret; + struct clk_hw_onecell_data *hw_data; + + hw_data = devm_kzalloc(&pdev->dev, struct_size(hw_data, hws, K230_CLK_NUM), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + ret = k230_clk_init_plls(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init plls failed\n"); + + ret = k230_clk_init_clks(pdev, hw_data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init clks failed\n"); + + return 0; +} + +static const struct of_device_id k230_clk_ids[] = { + { .compatible = "canaan,k230-clk" }, + { /* Sentinel */ } +}; + +static struct platform_driver k230_clk_driver = { + .driver = { + .name = "k230_clock_controller", + .of_match_table = k230_clk_ids, + }, + .probe = k230_clk_probe, +}; +builtin_platform_driver(k230_clk_driver); From 7a978c2ac722d527852c0239ecc71108547c7a23 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 13 May 2026 07:49:06 -0700 Subject: [PATCH 1581/5214] perf stat: Make metric only column line up with header Since some time the metric-only output columns are messed up and do not line up with the header, which makes it hard to read. I haven't bisected it, but presumably it was broken for some time. There were multiple problems: - The dummy pm invocation at the beginning did print a bogus field - The column computation in pm did not agree with the header length - The color escape strings from highlighting confuse printf's field length computation Fix all those. I simplified the column width computation significantly, ignoring EVNAME_LEN, MGROUP_LEN, config->unit_width which don't seem to be useful in the metric only context. It now only uses the actual unit width as well as config->metric_only_len. The result is more code removed than added. Before: % perf stat --topdown -a -I 1000 + time % tma_backend_bound % tma_frontend_bound % tma_bad_speculation % tma_retiring 1.000190386 45.5 40.0 5.3 9.2 2.005185654 45.3 40.1 5.6 9.0 3.009193207 45.4 39.9 5.6 9.1 After: % perf stat --topdown -a -I 1000 + time % tma_backend_bound % tma_frontend_bound % tma_bad_speculation % tma_retiring 1.000810024 46.3 39.7 5.3 8.7 2.004800656 45.8 39.8 5.4 8.9 3.008804783 46.0 39.6 5.4 9.0 Reviewed-by: Ian Rogers Signed-off-by: Andi Kleen Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat-display.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 993f4c4b8f44..2b69d238858c 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -580,16 +580,13 @@ static void print_metricgroup_header_std(struct perf_stat_config *config, const char *metricgroup_name) { struct outstate *os = ctx; - int n; if (!metricgroup_name) { __new_line_std(config, os); return; } - n = fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name); - - fprintf(config->output, "%*s", MGROUP_LEN + config->unit_width + 2 - n, ""); + fprintf(config->output, " %*s", config->metric_only_len, metricgroup_name); } static void print_metric_only(struct perf_stat_config *config, @@ -599,19 +596,20 @@ static void print_metric_only(struct perf_stat_config *config, struct outstate *os = ctx; FILE *out = os->fh; char str[1024]; - unsigned mlen = config->metric_only_len; + unsigned mlen; const char *color = metric_threshold_classify__color(thresh); + int olen; - if (!unit) - unit = ""; - if (mlen < strlen(unit)) - mlen = strlen(unit) + 1; + if (!unit) { + os->first = false; + return; + } - if (color) - mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; + mlen = max_t(unsigned, strlen(unit), config->metric_only_len); + olen = snprintf(str, sizeof(str), fmt ?: "", val); color_snprintf(str, sizeof(str), color ?: "", fmt ?: "", val); - fprintf(out, "%*s ", mlen, str); + fprintf(out, "%*s%s", max_t(int, mlen - olen, 1), "", str); os->first = false; } From 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:13 -0700 Subject: [PATCH 1582/5214] KVM: x86: Add dedicated API for getting mask of accelerated x2APIC MSRs Add a dedicated local APIC API, kvm_x2apic_disable_intercept_reg_mask(), to provide the mask of x2APIC registers whose MSRs can and should be passed through to the guest when x2APIC virtualization is enable, and use it in lieu of the open-coded equivalent VMX logic. Providing a common helper will allow sharing the logic with SVM (x2AVIC), and as a bonus eliminates the somewhat confusing code where KVM enables interception for MSR_TYPE_RW, even though only the READ case actually needs to be updated. No functional change intended. Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/lapic.c | 21 +++++++++++++++++++-- arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/vmx/vmx.c | 3 +-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 4078e624ca66..4e34f75e705d 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1730,7 +1730,7 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev) #define APIC_REGS_MASK(first, count) \ (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) +static u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) { /* Leave bits '0' for reserved and write-only registers. */ u64 valid_reg_mask = @@ -1766,7 +1766,24 @@ u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) return valid_reg_mask; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_lapic_readable_reg_mask); + +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu) +{ + if (WARN_ON_ONCE(!lapic_in_kernel(vcpu))) + return 0; + + /* + * TMMCT, a.k.a. the current APIC timer count, reads aren't accelerated + * by hardware (Intel or AMD) as the timer is emulated in software (by + * KVM), i.e. reads from the virtual APIC page would return garbage. + * Intercept RDMSR, as handling the fault-like APIC-access VM-Exit is + * more expensive than handling a RDMSR VM-Exit (the APIC-access exit + * requires slow emulation of the code stream). + */ + return kvm_lapic_readable_reg_mask(vcpu->arch.apic) & + ~APIC_REG_MASK(APIC_TMCCT); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_x2apic_disable_read_intercept_reg_mask); static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, void *data) diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 274885af4ebc..f763cd29a508 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -156,7 +156,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len); void kvm_lapic_exit(void); -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu); static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 1701db1b2e18..d13565c9285c 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4153,7 +4153,7 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) * mode, only the current timer count needs on-demand emulation by KVM. */ if (mode & MSR_BITMAP_MODE_X2APIC_APICV) - msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask(vcpu->arch.apic); + msr_bitmap[read_idx] = ~kvm_x2apic_disable_read_intercept_reg_mask(vcpu); else msr_bitmap[read_idx] = ~0ull; msr_bitmap[write_idx] = ~0ull; @@ -4166,7 +4166,6 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) !(mode & MSR_BITMAP_MODE_X2APIC)); if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { - vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); if (enable_ipiv) From 7f4b7092d9a173a4271e28c0ed1fc235994e309b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:14 -0700 Subject: [PATCH 1583/5214] KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports When toggling x2AVIC on/off, use KVM's curated mask of x2APIC MSRs that can/should be passed through to the guest (or not) when 2AVIC is enabled. Using the effective list provided by the local APIC emulation fixes multiple (classes of) bugs, as the existing hand-coded list of MSRs is wrong on multiple fronts: - ARBPRI isn't supported by KVM, isn't accelerated by AVIC (for read or write), and its #VMEXIT is fault-like, i.e. requires decoding the instruction. Disabling interception is nonsensical and suboptimal. - DFR and ICR2 aren't supported by x2APIC and so don't need their intercepts disabled for performance reasons. While the #GP due to x2APIC being abled has higher priority than the trap-like #VMEXIT, disabling interception of unsupported MSRs is confusing and unnecessary. - RRR is completely unsupported. - AVIC currently fails to pass through the "range of vectors" registers, IRR, ISR, and TMR, as e.g. X2APIC_MSR(APIC_IRR) only affects IRR0, and thus only disables intercept for vectors 31:0 (which are the *least* interesting registers). - TMCCT (the current APIC timer count) isn't accelerated by hardware, and generates a fault-like AVIC_UNACCELERATED_ACCESS #VMEXIT, i.e. requires KVM to decode the instruction to figure out what the guest was trying to access. Note, the only reason this isn't a fatal bug is that the AVIC architecture had the foresight to guard against buggy hypervisors. E.g. if hardware simply read from the virtual APIC page, the guest would get garbage (because the timer is emulated in software). Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/avic.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index adf211860949..8e4926c7b8dc 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -122,6 +122,9 @@ static u32 x2avic_max_physical_id; static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) { + struct kvm_vcpu *vcpu = &svm->vcpu; + u64 rd_regs; + static const u32 x2avic_passthrough_msrs[] = { X2APIC_MSR(APIC_ID), X2APIC_MSR(APIC_LVR), @@ -162,9 +165,15 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, if (!x2avic_enabled) return; + rd_regs = kvm_x2apic_disable_read_intercept_reg_mask(vcpu); + + for_each_set_bit(i, (unsigned long *)&rd_regs, BITS_PER_TYPE(rd_regs)) + svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, + MSR_TYPE_R, intercept); + for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(&svm->vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_RW, intercept); + svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], + MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From 8c63179d975f2029c948ecce622f72af616dbff7 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:15 -0700 Subject: [PATCH 1584/5214] KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated When x2AVIC is enabled, disable WRMSR interception only for MSRs that are actually accelerated by hardware. Disabling interception for MSRs that aren't accelerated is functionally "fine", and in some cases a weird "win" for performance, but only for cases that should never be triggered by a well-behaved VM (writes to read-only registers; the #GP will typically occur in the guest without taking a #VMEXIT, even for fault-like exits). But overall, disabling interception for MSRs that aren't accelerated is at best confusing and unintuitive, and at worst introduces avoidable risk, as the APM's documentation is imperfect and contradictory. The table in "15.29.3.1 Virtual APIC Register Accesses" of simply states that such writes generate exits, where as "Section 15.29.10 x2AVIC" says: x2APIC MSR intercept checks and access checks have higher priority than AVIC access permission checks. CPU behavior follows the latter (which makes perfect sense), but all in all there's simply no reason to disable interception just to make a #GP faster. Note, the set of MSRs that are passed through for write is identical to VMX's set when IPI virtualization is enabled. This is not a coincidence, and is another motiviating factor for cleaning up the intercepts, as x2AVIC is functionally equivalent to APICv+IPIv. Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/avic.c | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 8e4926c7b8dc..724a45c2aa23 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -124,39 +124,6 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, { struct kvm_vcpu *vcpu = &svm->vcpu; u64 rd_regs; - - static const u32 x2avic_passthrough_msrs[] = { - X2APIC_MSR(APIC_ID), - X2APIC_MSR(APIC_LVR), - X2APIC_MSR(APIC_TASKPRI), - X2APIC_MSR(APIC_ARBPRI), - X2APIC_MSR(APIC_PROCPRI), - X2APIC_MSR(APIC_EOI), - X2APIC_MSR(APIC_RRR), - X2APIC_MSR(APIC_LDR), - X2APIC_MSR(APIC_DFR), - X2APIC_MSR(APIC_SPIV), - X2APIC_MSR(APIC_ISR), - X2APIC_MSR(APIC_TMR), - X2APIC_MSR(APIC_IRR), - X2APIC_MSR(APIC_ESR), - X2APIC_MSR(APIC_ICR), - X2APIC_MSR(APIC_ICR2), - - /* - * Note! Always intercept LVTT, as TSC-deadline timer mode - * isn't virtualized by hardware, and the CPU will generate a - * #GP instead of a #VMEXIT. - */ - X2APIC_MSR(APIC_LVTTHMR), - X2APIC_MSR(APIC_LVTPC), - X2APIC_MSR(APIC_LVT0), - X2APIC_MSR(APIC_LVT1), - X2APIC_MSR(APIC_LVTERR), - X2APIC_MSR(APIC_TMICT), - X2APIC_MSR(APIC_TMCCT), - X2APIC_MSR(APIC_TDCR), - }; int i; if (intercept == svm->x2avic_msrs_intercepted) @@ -171,9 +138,10 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, MSR_TYPE_R, intercept); - for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_ICR), MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From fdec0a81cad5f14a7d3451a49acfa5adc898aa59 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 21 May 2026 00:24:27 -0700 Subject: [PATCH 1585/5214] perf build: Unconditionally set up libunwind feature build flags A "make feature-dump" build does not specify LIBUNWIND=1 because it is run with the default configuration to detect system-wide capabilities. This sets NO_LIBUNWIND := 1, causing Makefile.config to skip setting LIBUNWIND_LIBS and FEATURE_CHECK_LDFLAGS-libunwind. Consequently, when Makefile.feature is included and attempts to run all feature checks (via FEATURE_TESTS := all), the local feature test test-libunwind.bin compiles without the required architecture-specific library flags (-lunwind-x86_64) and fails to link on x86_64. This results in a corrupted cached BUILD_TEST_FEATURE_DUMP showing feature-libunwind=0 even when the host supports it. Subsequent test builds (like make_libunwind_O in the build-test suite) which reuse the feature dump and specify LIBUNWIND=1 will fail to compile due to a mismatch where CONFIG_LIBUNWIND is set (via remote architecture checks which are self-contained) but HAVE_LIBUNWIND_SUPPORT is disabled, causing compiler errors due to missing maps__e_machine definitions in maps.h. Fix this by unconditionally setting up the libunwind library lists and feature check LDFLAGS in Makefile.config so they are always populated and available to the feature detection engine regardless of whether LIBUNWIND=1 is opted-in for the current run. Fixes: 444508cd7c7b4f05 ("perf build: Be more programmatic when setting up libunwind variables") Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 99 +++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index b56fa8419f7d..c531b9315609 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -79,42 +79,43 @@ ifeq ($(ARCH),s390) CFLAGS += -fPIC endif +# Unconditionally set up the libunwind feature build flags as a +# feature-dump build doesn't specify LIBUNWIND=1. This means that +# dumping the libunwind features will be broken that can impact later +# builds that use the feature dump. +ifeq ($(SRCARCH),arm) + LIBUNWIND_LIBS = -lunwind -lunwind-arm +endif +ifeq ($(SRCARCH),arm64) + LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 +endif +ifeq ($(SRCARCH),loongarch) + LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64 +endif +ifeq ($(ARCH),mips) + LIBUNWIND_LIBS = -lunwind -lunwind-mips +endif +ifeq ($(SRCARCH),powerpc) + LIBUNWIND_LIBS := -lunwind -lunwind-ppc64 +endif +ifeq ($(SRCARCH),riscv) + LIBUNWIND_LIBS := -lunwind -lunwind-riscv +endif +ifeq ($(SRCARCH),s390) + LIBUNWIND_LIBS := -lunwind -lunwind-s390x +endif +ifeq ($(SRCARCH),x86) + ifeq (${IS_64_BIT}, 1) + LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma + else + LIBUNWIND_LIBS = -lunwind-x86 -lunwind -llzma + endif +endif ifneq ($(LIBUNWIND),1) NO_LIBUNWIND := 1 endif - -ifndef NO_LIBUNWIND - ifeq ($(SRCARCH),arm) - LIBUNWIND_LIBS = -lunwind -lunwind-arm - endif - ifeq ($(SRCARCH),arm64) - LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 - endif - ifeq ($(SRCARCH),loongarch) - LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64 - endif - ifeq ($(ARCH),mips) - LIBUNWIND_LIBS = -lunwind -lunwind-mips - endif - ifeq ($(SRCARCH),powerpc) - LIBUNWIND_LIBS := -lunwind -lunwind-ppc64 - endif - ifeq ($(SRCARCH),riscv) - LIBUNWIND_LIBS := -lunwind -lunwind-riscv - endif - ifeq ($(SRCARCH),s390) - LIBUNWIND_LIBS := -lunwind -lunwind-s390x - endif - ifeq ($(SRCARCH),x86) - ifeq (${IS_64_BIT}, 1) - LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma - else - LIBUNWIND_LIBS = -lunwind-x86 -lunwind -llzma - endif - endif - ifeq ($(LIBUNWIND_LIBS),) - NO_LIBUNWIND := 1 - endif +ifeq ($(LIBUNWIND_LIBS),) + NO_LIBUNWIND := 1 endif # @@ -124,24 +125,24 @@ endif # LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64 -ifndef NO_LIBUNWIND - FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) - FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) - FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) - FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) +# "Local" (no arch specified) feature test flags. +FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) +FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) +FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) +FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS) - ifdef LIBUNWIND_DIR - LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include - LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib +# Add directory into the "remote" (build for a a specific arch) feature tests. +ifdef LIBUNWIND_DIR + LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include + LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib - define libunwind_arch_set_flags - FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include - FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib -lunwind -lunwind-$(1) - endef - $(foreach arch,$(LIBUNWIND_ARCHS), \ - $(eval $(call libunwind_arch_set_flags,$(arch))) \ - ) - endif + define libunwind_arch_set_flags + FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include + FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib -lunwind -lunwind-$(1) + endef + $(foreach arch,$(LIBUNWIND_ARCHS), \ + $(eval $(call libunwind_arch_set_flags,$(arch))) \ + ) endif ifdef CSINCLUDES From ca426c0f6e9cce1db51348cfc8048a42e39cfa9a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 21 May 2026 00:24:28 -0700 Subject: [PATCH 1586/5214] perf kwork: Fix address sanitizer issues There is a double free in the record array due to how parse_options will mutate the array. Fix by keeping an array that isn't mutated. Ensure kwork_usage is freed on all paths. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kwork.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 59a54d11f7fa..a4604e152002 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -2258,7 +2258,7 @@ static void setup_event_list(struct perf_kwork *kwork, static int perf_kwork__record(struct perf_kwork *kwork, int argc, const char **argv) { - const char **rec_argv; + const char **rec_argv, **to_free = NULL; unsigned int rec_argc, i, j; struct kwork_class *class; int ret; @@ -2295,16 +2295,27 @@ static int perf_kwork__record(struct perf_kwork *kwork, BUG_ON(i != rec_argc); + /* Save the pointers as the array will be mutated by cmd_record. */ + to_free = calloc(rec_argc + 1, sizeof(char *)); + if (to_free == NULL) { + ret = -ENOMEM; + goto EXIT; + } + pr_debug("record comm: "); - for (j = 0; j < rec_argc; j++) + for (j = 0; j < rec_argc; j++) { pr_debug("%s ", rec_argv[j]); + to_free[j] = rec_argv[j]; + } pr_debug("\n"); ret = cmd_record(i, rec_argv); EXIT: for (i = 0; i < rec_argc; i++) - free((void *)rec_argv[i]); + free((void *)(to_free ? to_free[i] : rec_argv[i])); + + free(to_free); free(rec_argv); return ret; } @@ -2447,6 +2458,7 @@ int cmd_kwork(int argc, const char **argv) const char *const kwork_subcommands[] = { "record", "report", "latency", "timehist", "top", NULL }; + int ret = 0; perf_tool__init(&kwork.tool, /*ordered_events=*/true); kwork.tool.mmap = perf_event__process_mmap; @@ -2463,7 +2475,7 @@ int cmd_kwork(int argc, const char **argv) if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) { setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__record(&kwork, argc, argv); + ret = perf_kwork__record(&kwork, argc, argv); } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) { kwork.sort_order = default_report_sort_order; if (argc > 1) { @@ -2474,7 +2486,7 @@ int cmd_kwork(int argc, const char **argv) kwork.report = KWORK_REPORT_RUNTIME; setup_sorting(&kwork, report_options, report_usage); setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__report(&kwork); + ret = perf_kwork__report(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) { kwork.sort_order = default_latency_sort_order; if (argc > 1) { @@ -2485,7 +2497,7 @@ int cmd_kwork(int argc, const char **argv) kwork.report = KWORK_REPORT_LATENCY; setup_sorting(&kwork, latency_options, latency_usage); setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__report(&kwork); + ret = perf_kwork__report(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("timehist", argv[0])) { if (argc > 1) { argc = parse_options(argc, argv, timehist_options, timehist_usage, 0); @@ -2494,7 +2506,7 @@ int cmd_kwork(int argc, const char **argv) } kwork.report = KWORK_REPORT_TIMEHIST; setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__timehist(&kwork); + ret = perf_kwork__timehist(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("top", argv[0])) { kwork.sort_order = default_top_sort_order; if (argc > 1) { @@ -2507,12 +2519,12 @@ int cmd_kwork(int argc, const char **argv) kwork.event_list_str = "sched, irq, softirq"; setup_event_list(&kwork, kwork_options, kwork_usage); setup_sorting(&kwork, top_options, top_usage); - return perf_kwork__top(&kwork); + ret = perf_kwork__top(&kwork); } else usage_with_options(kwork_usage, kwork_options); /* free usage string allocated by parse_options_subcommand */ free((void *)kwork_usage[0]); - return 0; + return ret; } From e898c505b0ee4efb5b2dad9f494c94f74b478ab0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 21 May 2026 00:24:29 -0700 Subject: [PATCH 1587/5214] perf kwork: Fix memory management of kwork_work This commit addresses several memory management issues in builtin-kwork.c: 1. Implements a global cleanup function perf_kwork__exit to free all kwork_work and kwork_atom_page objects at the end of the command. 2. Ensures all 'name' fields in struct kwork_work are malloc-ed (or NULL) and properly freed by using strdup and zfree. 3. Fixes memory leaks in top_merge_tasks where kwork_work objects were dropped without being freed. 4. Adds robustness with NULL checks for name fields. 5. Fixes workqueue_work_init to correctly resolve and strdup kernel function names, preventing bad-free errors. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kwork.c | 121 ++++++++++++++++++++++++++++-------- tools/perf/util/bpf_kwork.c | 14 +++-- tools/perf/util/kwork.h | 9 +++ 3 files changed, 115 insertions(+), 29 deletions(-) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index a4604e152002..f793ea578515 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -323,8 +323,8 @@ static struct kwork_work *work_search(struct rb_root_cached *root, else if (cmp < 0) node = node->rb_right; else { - if (work->name == NULL) - work->name = key->name; + if (work->name == NULL && key->name != NULL) + work->name = strdup(key->name); return work; } } @@ -371,11 +371,54 @@ static struct kwork_work *work_new(struct kwork_work *key) work->id = key->id; work->cpu = key->cpu; - work->name = key->name; + work->name = key->name ? strdup(key->name) : NULL; work->class = key->class; return work; } + +static void work_delete(struct kwork_work *work) +{ + if (work) { + work_exit(work); + free(work); + } +} + +static void kwork_work__free_root(struct rb_root_cached *root) +{ + struct rb_node *next; + struct kwork_work *work; + + while ((next = rb_first_cached(root))) { + work = rb_entry(next, struct kwork_work, node); + rb_erase_cached(next, root); + work_delete(work); + } +} + +static void perf_kwork__exit(struct perf_kwork *kwork) +{ + struct kwork_class *class; + struct kwork_atom_page *page, *tmp_page; + + list_for_each_entry(class, &kwork->class_list, list) { + kwork_work__free_root(&class->work_root); + } + + kwork_work__free_root(&kwork->sorted_work_root); + + list_for_each_entry_safe(page, tmp_page, &kwork->atom_page_list, list) { + list_del_init(&page->list); + free(page); + } + + INIT_LIST_HEAD(&kwork->class_list); + INIT_LIST_HEAD(&kwork->atom_page_list); + INIT_LIST_HEAD(&kwork->sort_list); + INIT_LIST_HEAD(&kwork->cmp_id); +} + static struct kwork_work *work_findnew(struct rb_root_cached *root, struct kwork_work *key, struct list_head *sort_list) @@ -453,25 +496,29 @@ static int work_push_atom(struct perf_kwork *kwork, struct kwork_work **ret_work, bool overwrite) { - struct kwork_atom *atom, *dst_atom, *last_atom; + struct kwork_atom *atom = NULL, *dst_atom, *last_atom; struct kwork_work *work, key; + int ret = 0; BUG_ON(class->work_init == NULL); class->work_init(kwork, class, &key, src_type, sample, machine); atom = atom_new(kwork, sample); - if (atom == NULL) - return -1; + if (atom == NULL) { + ret = -1; + goto out; + } work = work_findnew(&class->work_root, &key, &kwork->cmp_id); if (work == NULL) { atom_free(atom); - return -1; + ret = -1; + goto out; } if (!profile_event_match(kwork, work, sample)) { atom_free(atom); - return 0; + goto out; } if (dst_type < KWORK_TRACE_MAX) { @@ -498,8 +545,9 @@ static int work_push_atom(struct perf_kwork *kwork, } list_add_tail(&atom->list, &work->atom_list[src_type]); - - return 0; +out: + work_exit(&key); + return ret; } static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, @@ -510,7 +558,7 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, struct machine *machine, struct kwork_work **ret_work) { - struct kwork_atom *atom, *src_atom; + struct kwork_atom *atom = NULL, *src_atom; struct kwork_work *work, key; BUG_ON(class->work_init == NULL); @@ -521,15 +569,15 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, *ret_work = work; if (work == NULL) - return NULL; + goto out; if (!profile_event_match(kwork, work, sample)) - return NULL; + goto out; atom = list_last_entry_or_null(&work->atom_list[dst_type], struct kwork_atom, list); if (atom != NULL) - return atom; + goto out; src_atom = atom_new(kwork, sample); if (src_atom != NULL) @@ -538,8 +586,9 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, if (ret_work != NULL) *ret_work = NULL; } - - return NULL; +out: + work_exit(&key); + return atom; } static struct kwork_work *find_work_by_id(struct rb_root_cached *root, @@ -1002,13 +1051,16 @@ static void irq_work_init(struct perf_kwork *kwork, work->name = NULL; } else { work->id = perf_sample__intval(sample, "irq"); - work->name = perf_sample__strval(sample, "name"); + work->name = strdup(perf_sample__strval(sample, "name") ?: ""); } } static void irq_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "%s:%" PRIu64 "", work->name, work->id); + if (work->name != NULL) + snprintf(buf, len, "%s:%" PRIu64 "", work->name, work->id); + else + snprintf(buf, len, "%" PRIu64 "", work->id); } static struct kwork_class kwork_irq = { @@ -1135,7 +1187,10 @@ static void softirq_work_init(struct perf_kwork *kwork, static void softirq_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "(s)%s:%" PRIu64 "", work->name, work->id); + if (work->name != NULL) + snprintf(buf, len, "(s)%s:%" PRIu64 "", work->name, work->id); + else + snprintf(buf, len, "(s)%" PRIu64 "", work->id); } static struct kwork_class kwork_softirq = { @@ -1220,8 +1275,14 @@ static void workqueue_work_init(struct perf_kwork *kwork __maybe_unused, work->class = class; work->cpu = sample->cpu; work->id = perf_sample__intval(sample, "work"); - work->name = function_addr == 0 ? NULL : - machine__resolve_kernel_addr(machine, &function_addr, &modp); + work->name = NULL; + + if (function_addr != 0) { + const char *name = machine__resolve_kernel_addr(machine, &function_addr, &modp); + + if (name) + work->name = strdup(name); + } } static void workqueue_work_name(struct kwork_work *work, char *buf, int len) @@ -1284,16 +1345,16 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, if (src_type == KWORK_TRACE_EXIT) { work->id = perf_sample__intval(sample, "prev_pid"); - work->name = strdup(perf_sample__strval(sample, "prev_comm")); + work->name = strdup(perf_sample__strval(sample, "prev_comm") ?: ""); } else if (src_type == KWORK_TRACE_ENTRY) { work->id = perf_sample__intval(sample, "next_pid"); - work->name = strdup(perf_sample__strval(sample, "next_comm")); + work->name = strdup(perf_sample__strval(sample, "next_comm") ?: ""); } } static void sched_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "%s", work->name); + snprintf(buf, len, "%s", work->name ?: ""); } static struct kwork_class kwork_sched = { @@ -2100,8 +2161,10 @@ static void top_merge_tasks(struct perf_kwork *kwork) rb_erase_cached(node, &class->work_root); data = rb_entry(node, struct kwork_work, node); - if (!profile_name_match(kwork, data)) + if (!profile_name_match(kwork, data)) { + work_delete(data); continue; + } cpu = data->cpu; merged_work = find_work_by_id(&merged_root, data->id, @@ -2109,11 +2172,17 @@ static void top_merge_tasks(struct perf_kwork *kwork) if (!merged_work) { work_insert(&merged_root, data, &kwork->cmp_id); } else { + if (merged_work->name == NULL && data->name != NULL) + merged_work->name = strdup(data->name); + merged_work->total_runtime += data->total_runtime; merged_work->cpu_usage += data->cpu_usage; } top_calc_load_runtime(kwork, data); + + if (merged_work) + work_delete(data); } work_sort(kwork, class, &merged_root); @@ -2523,6 +2592,8 @@ int cmd_kwork(int argc, const char **argv) } else usage_with_options(kwork_usage, kwork_options); + perf_kwork__exit(&kwork); + /* free usage string allocated by parse_options_subcommand */ free((void *)kwork_usage[0]); diff --git a/tools/perf/util/bpf_kwork.c b/tools/perf/util/bpf_kwork.c index d3a2e548f2b6..2248f462a847 100644 --- a/tools/perf/util/bpf_kwork.c +++ b/tools/perf/util/bpf_kwork.c @@ -273,6 +273,7 @@ static int add_work(struct perf_kwork *kwork, .cpu = key->cpu, }; enum kwork_class_type type = key->type; + int ret = 0; if (!valid_kwork_class_type(type)) { pr_debug("Invalid class type %d to add work\n", type); @@ -287,8 +288,10 @@ static int add_work(struct perf_kwork *kwork, return -1; work = kwork->add_work(kwork, tmp.class, &tmp); - if (work == NULL) - return -1; + if (work == NULL) { + ret = -1; + goto out; + } if (kwork->report == KWORK_REPORT_RUNTIME) { work->nr_atoms = data->nr; @@ -304,13 +307,16 @@ static int add_work(struct perf_kwork *kwork, work->max_latency_end = data->max_time_end; } else { pr_debug("Invalid bpf report type %d\n", kwork->report); - return -1; + ret = -1; + goto out; } kwork->timestart = (u64)ts_start.tv_sec * NSEC_PER_SEC + ts_start.tv_nsec; kwork->timeend = (u64)ts_end.tv_sec * NSEC_PER_SEC + ts_end.tv_nsec; - return 0; +out: + work_exit(&tmp); + return ret; } int perf_kwork__report_read_bpf(struct perf_kwork *kwork) diff --git a/tools/perf/util/kwork.h b/tools/perf/util/kwork.h index abf637d44794..81d39a7f78c8 100644 --- a/tools/perf/util/kwork.h +++ b/tools/perf/util/kwork.h @@ -5,6 +5,7 @@ #include "util/tool.h" #include "util/time-utils.h" +#include #include #include #include @@ -164,6 +165,14 @@ struct kwork_class { char *buf, int len); }; +static inline void work_exit(struct kwork_work *work) +{ + if (work) { + free(work->name); + work->name = NULL; + } +} + struct trace_kwork_handler { int (*raise_event)(struct perf_kwork *kwork, struct kwork_class *class, From 0b97e92393a178765ee1ea01fe5087efece2c425 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 05:59:57 +0000 Subject: [PATCH 1588/5214] perf test amd ibs: Fix incorrect kernel version check "AMD IBS sample period" unit test is getting skipped on kernel v7.x. Fix the kernel version >= v6.15 check. Fixes: 21fb366b2f457611 ("perf test amd: Skip amd-ibs-period test on kernel < v6.15") Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/amd-ibs-period.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c index cee9e11c05e0..6a92b3a23ed7 100644 --- a/tools/perf/arch/x86/tests/amd-ibs-period.c +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c @@ -932,7 +932,7 @@ static bool kernel_v6_15_or_newer(void) endptr++; minor = strtol(endptr, NULL, 10); - return major >= 6 && minor >= 15; + return major > 6 || (major == 6 && minor >= 15); } int test__amd_ibs_period(struct test_suite *test __maybe_unused, From 45bd2d77fbedec862204bb5c0fcaba2b7fa5fb56 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 05:59:58 +0000 Subject: [PATCH 1589/5214] perf tool ibs: Sync AMD IBS header file IBS_OP_DATA2 register will have two more fields: strm_st and rmt_socket in Zen6 and future AMD platforms. Kernel header file is already updated. Add those fields in tools copy as well. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/amd/ibs.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/arch/x86/include/asm/amd/ibs.h b/tools/arch/x86/include/asm/amd/ibs.h index d0777b597322..6f25074e669f 100644 --- a/tools/arch/x86/include/asm/amd/ibs.h +++ b/tools/arch/x86/include/asm/amd/ibs.h @@ -99,7 +99,9 @@ union ibs_op_data2 { rmt_node:1, /* 4: destination node */ cache_hit_st:1, /* 5: cache hit state */ data_src_hi:2, /* 6-7: data source high */ - reserved1:56; /* 8-63: reserved */ + strm_st:1, /* 8: streaming store */ + rmt_socket:1, /* 9: remote socket */ + reserved1:54; /* 10-63: reserved */ }; }; From 540dc628ab5dc2cf176053c9794ed9891269484b Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 05:59:59 +0000 Subject: [PATCH 1590/5214] perf test ibs: Skip privilege test on Zen6 and newer platforms IBS on pre-Zen6 platforms lacked a hardware privilege filter, so the kernel enabled swfilt=1. Zen6 and newer platforms provides privilege filtering via the RIP[63] bit, making swfilt redundant. Skip the perf unit test that assumes IBS has no hardware-assisted privilege filter on Zen6 and newer platforms. swfilt is ignored by kernel on platforms that support RIP[63] bit filter i.e. all amd-ibs-swfilt.sh tests will test hardware assisted privilege filter. Without the patch on Zen6: # sudo ./perf test -vv 77 77: AMD IBS software filtering: --- start --- test child forked, pid 30813 check availability of IBS swfilt run perf record with modifier and swfilt [FAIL] IBS PMU should not accept exclude_kernel ---- end(-1) ---- 77: AMD IBS software filtering : FAILED! With the patch: # ./perf test -vv 77 77: AMD IBS software filtering: --- start --- test child forked, pid 30903 check availability of IBS swfilt run perf record with modifier and swfilt [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.000 MB /dev/null ] [ perf record: Woken up 3 times to write data ] [ perf record: Captured and wrote 0.000 MB /dev/null ] [ perf record: Woken up 3 times to write data ] [ perf record: Captured and wrote 0.000 MB /dev/null ] [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.000 MB /dev/null ] check number of samples with swfilt [ perf record: Woken up 4 times to write data ] [ perf record: Captured and wrote 0.051 MB - ] [ perf record: Woken up 4 times to write data ] [ perf record: Captured and wrote 0.063 MB - ] ---- end(0) ---- 77: AMD IBS software filtering : Ok Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/amd-ibs-swfilt.sh | 37 +++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/shell/amd-ibs-swfilt.sh b/tools/perf/tests/shell/amd-ibs-swfilt.sh index e7f66df05c4b..5d17dc624cbe 100755 --- a/tools/perf/tests/shell/amd-ibs-swfilt.sh +++ b/tools/perf/tests/shell/amd-ibs-swfilt.sh @@ -1,6 +1,33 @@ #!/bin/bash # AMD IBS software filtering +cpu_family() { + grep -m1 '^cpu family[[:space:]]*:' /proc/cpuinfo \ + | awk -F: '{gsub(/^[ \t]+/, "", $2); print $2}' +} + +cpu_model() { + grep -m1 '^model[[:space:]]*:' /proc/cpuinfo \ + | awk -F: '{gsub(/^[ \t]+/, "", $2); print $2}' +} + +# IBS PMUs does not advertize privilege filtering capability. Rely +# on Family / Model check. +hw_priv_filter_supported() { + family=$(cpu_family) + model=$(cpu_model) + + if (( family > 0x1a )) || + { (( family == 0x1a )) && + { { (( model >= 0x50 && model <= 0x5f )) || + (( model >= 0x80 && model <= 0xaf )) || + (( model >= 0xc0 && model <= 0xcf )); }; }; }; then + return 0 # True + else + return 1 # False + fi +} + ParanoidAndNotRoot() { [ "$(id -u)" != 0 ] && [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt $1 ] } @@ -23,10 +50,12 @@ echo "run perf record with modifier and swfilt" err=0 # setting any modifiers should fail -perf record -B -e ibs_op//u -o /dev/null true 2> /dev/null -if [ $? -eq 0 ]; then - echo "[FAIL] IBS PMU should not accept exclude_kernel" - exit 1 +if ! hw_priv_filter_supported; then + perf record -B -e ibs_op//u -o /dev/null true 2> /dev/null + if [ $? -eq 0 ]; then + echo "[FAIL] IBS PMU should not accept exclude_kernel" + exit 1 + fi fi # setting it with swfilt should be fine From f04a8a7649f9b248f89971b7a440f5d158d57d46 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 06:00:00 +0000 Subject: [PATCH 1591/5214] perf amd ibs: Suppress bogus TlbRefillLat and DCPhysAd on Zen4+ On Zen4 (and future) CPUs, IBS_OP_DATA3[TlbRefillLat] is valid only if IBS_OP_DATA3[DcPhyAddrValid] is set. Similarly, IBS_DC_PHYSADDR is valid if IBS_OP_DATA3[DcLinAddrValid] is _also_ set. Add these checks while decoding IBS MSRs. When IBS is triggered by an unprivileged user, the kernel now zeroes PhysAddr before storing raw IBS register values in the perf sample. The perf tool, however, still outputs these zero physical addresses, which serves no purpose. So avoid printing zero physical addresses. Instead of explicit family/model checks use the !zen4_ibs_extensions as a proxy flag to cover Zen 3 and earlier revisions. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/amd-sample-raw.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index b084dee76b1a..385308c55f34 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -172,6 +172,7 @@ static void pr_ibs_op_data3(union ibs_op_data3 reg) char dc_l1_l2tlb_miss_str[sizeof(" DcL1TlbMiss _ DcL2TlbMiss _")] = ""; char dc_l1tlb_hit_str[sizeof(" DcL1TlbHit2M _ DcL1TlbHit1G _")] = ""; char op_mem_width_str[sizeof(" OpMemWidth _____ bytes")] = ""; + char tlb_refill_lat_str[sizeof(" TlbRefillLat _____")] = ""; char dc_l2tlb_hit_2m_str[sizeof(" DcL2TlbHit2M _")] = ""; char dc_l2tlb_hit_1g_str[sizeof(" DcL2TlbHit1G _")] = ""; char dc_page_size_str[sizeof(" DcPageSize ____")] = ""; @@ -214,17 +215,23 @@ static void pr_ibs_op_data3(union ibs_op_data3 reg) " DcL2TlbHit1G %d", reg.dc_l2_tlb_hit_1g); } + /* Use !zen4_ibs_extensions as a proxy for Zen3 and earlier */ + if (!zen4_ibs_extensions || reg.dc_phy_addr_valid) { + snprintf(tlb_refill_lat_str, sizeof(tlb_refill_lat_str), + " TlbRefillLat %5d", reg.tlb_refill_lat); + } + printf("ibs_op_data3:\t%016llx LdOp %d StOp %d%s%s%s DcMiss %d DcMisAcc %d " "DcWcMemAcc %d DcUcMemAcc %d DcLockedOp %d DcMissNoMabAlloc %d " "DcLinAddrValid %d DcPhyAddrValid %d%s%s SwPf %d%s%s " - "DcMissLat %5d TlbRefillLat %5d\n", + "DcMissLat %5d%s\n", reg.val, reg.ld_op, reg.st_op, dc_l1_l2tlb_miss_str, dtlb_pgsize_cap ? dc_page_size_str : dc_l1tlb_hit_str, dc_l2tlb_hit_2m_str, reg.dc_miss, reg.dc_mis_acc, reg.dc_wc_mem_acc, reg.dc_uc_mem_acc, reg.dc_locked_op, reg.dc_miss_no_mab_alloc, reg.dc_lin_addr_valid, reg.dc_phy_addr_valid, dc_l2tlb_hit_1g_str, l2_miss_str, reg.sw_pf, op_mem_width_str, op_dc_miss_open_mem_reqs_str, - reg.dc_miss_lat, reg.tlb_refill_lat); + reg.dc_miss_lat, tlb_refill_lat_str); } /* @@ -253,8 +260,12 @@ static void amd_dump_ibs_op(struct perf_sample *sample) pr_ibs_op_data3(*op_data3); if (op_data3->dc_lin_addr_valid) printf("IbsDCLinAd:\t%016llx\n", *(rip + 4)); - if (op_data3->dc_phy_addr_valid) + + /* Use !zen4_ibs_extensions as a proxy for Zen3 and earlier */ + if (op_data3->dc_phy_addr_valid && *(rip + 5) && + (!zen4_ibs_extensions || op_data3->dc_lin_addr_valid)) { printf("IbsDCPhysAd:\t%016llx\n", *(rip + 5)); + } if (op_data->op_brn_ret && *(rip + 6)) printf("IbsBrTarget:\t%016llx\n", *(rip + 6)); } From ea0ce2e12ed5347ecefc7b7c2a01623b855d9ddb Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 06:00:01 +0000 Subject: [PATCH 1592/5214] perf amd ibs: Make Fetch status bits dependent on PhyAddrValid for newer platforms On Zen6 and future platforms, IBS_FETCH_CTL status fields are valid only if IBS_FETCH_CTL[IbsPhyAddrValid] is set. Same for IBS_FETCH_CTL_EXT. Add these checks while decoding IBS MSRs. Unfortunately, there is no CPUID bit to indicate the change. Fallback to Family/Model check. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/amd-sample-raw.c | 42 ++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index 385308c55f34..ae005eb0363d 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -22,6 +22,29 @@ static bool zen4_ibs_extensions; static bool ldlat_cap; static bool dtlb_pgsize_cap; +/* + * Status fields of IBS_FETCH_CTL and IBS_FETCH_CTL_EXT are valid only if + * IBS_FETCH_CTL[PhyAddrValid] is set. + */ +static int fetch_ctl_depends_on_phy_addr_valid(void) +{ + static int depends = -1; /* -1: Don't know, 1: Yes, 0: No */ + + if (depends != -1) + return depends; + + depends = 0; + if (cpu_family > 0x1a || + (cpu_family == 0x1a && ( + (cpu_model >= 0x50 && cpu_model <= 0x5f) || + (cpu_model >= 0x80 && cpu_model <= 0xaf) || + (cpu_model >= 0xc0 && cpu_model <= 0xcf)))) { + depends = 1; + } + + return depends; +} + static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) { const char * const ic_miss_strs[] = { @@ -43,6 +66,18 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) const char *ic_miss_str = NULL; const char *l1tlb_pgsz_str = NULL; char l3_miss_str[sizeof(" L3MissOnly _ FetchOcMiss _ FetchL3Miss _")] = ""; + char l3_miss_only_str[sizeof(" L3MissOnly _")] = ""; + + if (fetch_ctl_depends_on_phy_addr_valid() && !reg.phy_addr_valid) { + snprintf(l3_miss_only_str, sizeof(l3_miss_only_str), + " L3MissOnly %d", reg.l3_miss_only); + + printf("ibs_fetch_ctl:\t%016llx MaxCnt %7d Cnt %7d En %d Val %d Comp %d " + "PhyAddrValid 0 RandEn %d%s\n", reg.val, reg.fetch_maxcnt << 4, + reg.fetch_cnt << 4, reg.fetch_en, reg.fetch_val, reg.fetch_comp, + reg.rand_en, l3_miss_only_str); + return; + } if (cpu_family == 0x19 && cpu_model < 0x10) { /* @@ -72,8 +107,11 @@ static void pr_ibs_fetch_ctl(union ibs_fetch_ctl reg) l3_miss_str); } -static void pr_ic_ibs_extd_ctl(union ic_ibs_extd_ctl reg) +static void pr_ic_ibs_extd_ctl(union ibs_fetch_ctl fetch_ctl, union ic_ibs_extd_ctl reg) { + if (fetch_ctl_depends_on_phy_addr_valid() && !fetch_ctl.phy_addr_valid) + return; + printf("ic_ibs_ext_ctl:\t%016llx IbsItlbRefillLat %3d\n", reg.val, reg.itlb_refill_lat); } @@ -285,7 +323,7 @@ static void amd_dump_ibs_fetch(struct perf_sample *sample) printf("IbsFetchLinAd:\t%016llx\n", *addr++); if (fetch_ctl->phy_addr_valid) printf("IbsFetchPhysAd:\t%016llx\n", *addr); - pr_ic_ibs_extd_ctl(*extd_ctl); + pr_ic_ibs_extd_ctl(*fetch_ctl, *extd_ctl); } /* From 7c04195ccefcfc95d46abfd30a435679a28493b2 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 06:00:02 +0000 Subject: [PATCH 1593/5214] perf amd ibs: Decode Remote-Socket flag in IBS OP raw dump IBS OP on Zen6 and future platform can mark a data source as coming from a remote socket. When the PMU advertises this feature, interpret IBS_OP_DATA2[9] bit as the Remote-Socket indicator and show it in the raw dump output. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/amd-sample-raw.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index ae005eb0363d..3e5530008b14 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -21,6 +21,7 @@ static u32 cpu_family, cpu_model, ibs_fetch_type, ibs_op_type; static bool zen4_ibs_extensions; static bool ldlat_cap; static bool dtlb_pgsize_cap; +static bool rmtsocket_cap; /* * Status fields of IBS_FETCH_CTL and IBS_FETCH_CTL_EXT are valid only if @@ -164,8 +165,13 @@ static void pr_ibs_op_data2_extended(union ibs_op_data2 reg) /* 13 to 31 are reserved. Avoid printing them. */ }; int data_src = (reg.data_src_hi << 3) | reg.data_src_lo; + char rmtsocket[sizeof("RmtSocket _ ")] = ""; - printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val, + if (rmtsocket_cap) + snprintf(rmtsocket, sizeof(rmtsocket), "RmtSocket %d ", reg.rmt_socket); + + printf("ibs_op_data2:\t%016llx %s%sRmtNode %d%s\n", reg.val, + rmtsocket, (data_src == 1 || data_src == 2 || data_src == 5) ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", reg.rmt_node, @@ -184,8 +190,13 @@ static void pr_ibs_op_data2_default(union ibs_op_data2 reg) " DataSrc 6=(reserved)", " DataSrc 7=Other" }; + char rmtsocket[sizeof("RmtSocket _ ")] = ""; - printf("ibs_op_data2:\t%016llx %sRmtNode %d%s\n", reg.val, + if (rmtsocket_cap) + snprintf(rmtsocket, sizeof(rmtsocket), "RmtSocket %d ", reg.rmt_socket); + + printf("ibs_op_data2:\t%016llx %s%sRmtNode %d%s\n", reg.val, + rmtsocket, reg.data_src_lo == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", reg.rmt_node, data_src_str[reg.data_src_lo]); @@ -429,6 +440,9 @@ bool evlist__has_amd_ibs(struct evlist *evlist) if (perf_env__find_pmu_cap(env, "ibs_op", "dtlb_pgsize")) dtlb_pgsize_cap = 1; + if (perf_env__find_pmu_cap(env, "ibs_op", "rmtsocket")) + rmtsocket_cap = 1; + if (ibs_fetch_type || ibs_op_type) { if (!cpu_family) parse_cpuid(env); From 873f232d09622767c57557003d4bd98ed6c6f327 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 06:00:03 +0000 Subject: [PATCH 1594/5214] perf amd ibs: Decode Streaming-store flag in IBS OP raw dump IBS OP on Zen6 and future platform can tag IBS samples that originate from streaming-store instruction. When the PMU advertises this feature, interpret IBS_OP_DATA2[8] bit as the streaming store indicator and show it in the raw dump output. Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/amd-sample-raw.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index 3e5530008b14..394c061fbeb3 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -22,6 +22,7 @@ static bool zen4_ibs_extensions; static bool ldlat_cap; static bool dtlb_pgsize_cap; static bool rmtsocket_cap; +static bool strmst_cap; /* * Status fields of IBS_FETCH_CTL and IBS_FETCH_CTL_EXT are valid only if @@ -166,12 +167,15 @@ static void pr_ibs_op_data2_extended(union ibs_op_data2 reg) }; int data_src = (reg.data_src_hi << 3) | reg.data_src_lo; char rmtsocket[sizeof("RmtSocket _ ")] = ""; + char strmst[sizeof("StrmSt _ ")] = ""; if (rmtsocket_cap) snprintf(rmtsocket, sizeof(rmtsocket), "RmtSocket %d ", reg.rmt_socket); + if (strmst_cap) + snprintf(strmst, sizeof(strmst), "StrmSt %d ", reg.strm_st); - printf("ibs_op_data2:\t%016llx %s%sRmtNode %d%s\n", reg.val, - rmtsocket, + printf("ibs_op_data2:\t%016llx %s%s%sRmtNode %d%s\n", reg.val, + rmtsocket, strmst, (data_src == 1 || data_src == 2 || data_src == 5) ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", reg.rmt_node, @@ -191,12 +195,15 @@ static void pr_ibs_op_data2_default(union ibs_op_data2 reg) " DataSrc 7=Other" }; char rmtsocket[sizeof("RmtSocket _ ")] = ""; + char strmst[sizeof("StrmSt _ ")] = ""; if (rmtsocket_cap) snprintf(rmtsocket, sizeof(rmtsocket), "RmtSocket %d ", reg.rmt_socket); + if (strmst_cap) + snprintf(strmst, sizeof(strmst), "StrmSt %d ", reg.strm_st); - printf("ibs_op_data2:\t%016llx %s%sRmtNode %d%s\n", reg.val, - rmtsocket, + printf("ibs_op_data2:\t%016llx %s%s%sRmtNode %d%s\n", reg.val, + rmtsocket, strmst, reg.data_src_lo == 2 ? (reg.cache_hit_st ? "CacheHitSt 1=O-State " : "CacheHitSt 0=M-state ") : "", reg.rmt_node, data_src_str[reg.data_src_lo]); @@ -443,6 +450,9 @@ bool evlist__has_amd_ibs(struct evlist *evlist) if (perf_env__find_pmu_cap(env, "ibs_op", "rmtsocket")) rmtsocket_cap = 1; + if (perf_env__find_pmu_cap(env, "ibs_op", "strmst")) + strmst_cap = 1; + if (ibs_fetch_type || ibs_op_type) { if (!cpu_family) parse_cpuid(env); From a31423e67c0e8c716c9ecdf0d4836dd7049ad78b Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 06:00:04 +0000 Subject: [PATCH 1595/5214] perf doc: Document new IBS capabilities in man page Include examples of: o Privilege filter with Fetch and Op PMUs, including swfilt approach on Zen5 and older platforms and hardware assisted filter on Zen6 and newer platforms o Streaming store filter with Op PMU o Fetch latency filter with Fetch PMU Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-amd-ibs.txt | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/perf/Documentation/perf-amd-ibs.txt b/tools/perf/Documentation/perf-amd-ibs.txt index 548549935760..253a7375c88a 100644 --- a/tools/perf/Documentation/perf-amd-ibs.txt +++ b/tools/perf/Documentation/perf-amd-ibs.txt @@ -69,6 +69,14 @@ Per-cpu profile (cpu10), cycles event, sampling period: 100000 # perf record -e ibs_op// -c 100000 -C 10 +Userspace only, per-cpu profile (cpu10), cycles event, sampling period: 100000 + + Zen6 onward (See NOTES): + # perf record -e ibs_op//u -c 100000 -C 10 + + Until Zen5: + # perf record -e ibs_op/swfilt=1/u -c 100000 -C 10 + Per-cpu profile (cpu10), cycles event, sampling freq: 1000 # perf record -e ibs_op// -F 1000 -C 10 @@ -94,6 +102,11 @@ onward) Latency value which is a multiple of 128 incurs a little less profiling overhead compared to other values. +System-wide profile, cycles event, sampling period: 100000, streaming store +filter (Zen6 onward) + + # perf record -e ibs_op/strmst=1/ -c 100000 -a + Per process(upstream v6.2 onward), uOps event, sampling period: 100000 # perf record -e ibs_op/cnt_ctl=1/ -c 100000 -p 1234 @@ -150,6 +163,14 @@ System-wide profile, fetch ops event, sampling period: 100000 # perf record -e ibs_fetch// -c 100000 -a +Userspace only, system-wide profile, fetch ops event, sampling period: 100000 + + Zen6 onward (See NOTES): + # perf record -e ibs_fetch//u -c 100000 -a + + Until Zen5: + # perf record -e ibs_fetch/swfilt=1/u -c 100000 -a + System-wide profile, fetch ops event, sampling period: 100000, Random enable # perf record -e ibs_fetch/rand_en=1/ -c 100000 -a @@ -158,6 +179,15 @@ System-wide profile, fetch ops event, sampling period: 100000, Random enable helps in cases like long running loops where PMU is tagging the same instruction over and over because of fixed sample period. +System-wide profile, fetch ops event, sampling period: 10000, fetch latency +filter (Zen6 onward) + + # perf record -e ibs_fetch/fetchlat=128/ -c 10000 -a + + Supported fetch latency threshold values are 128 to 1920 (both inclusive). + Latency value which is a multiple of 128 incurs a little less profiling + overhead compared to other values. + etc. PERF MEM AND PERF C2C @@ -216,6 +246,15 @@ sort keys. For example: Please refer to their man page for more detail. +NOTES +----- +Hardware privilege filtering uses bit 63 to distinguish between kernel +and userspace addresses. Hardware privilege filtering is not supported +on 32-bit systems. Also, the bit 63 convention is not universal and can +fail in specific environments, such as, using 64-bit host IBS to profile +a 32-bit guest, using 64-bit host IBS to profile non-Linux 64-bit guests +that do not adhere to the bit 63 privilege standard etc. + SEE ALSO -------- From 0b18bced5444f4fc54ba4500c377f2e88d31ea09 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 18:41:05 -0700 Subject: [PATCH 1596/5214] perf tool_pmu: Make tool PMU events respect enable/disable Tool PMU events (duration_time, user_time, system_time) currently count from when the event is opened to when it is read. This causes issues with features like the delay option (-D) or control fd, where events are opened but should not start counting immediately. Make these events behave more like regular counters by implementing proper enable and disable support. Add accumulated_time to struct evsel to track time while enabled, and implement enable/disable CPU callbacks to start/stop counting. Also generalize userspace PMU mixed group handling. Userspace synthetic PMUs (type > PERF_PMU_TYPE_PE_END) do not have kernel implementations and cannot be grouped in the kernel (opened with group_fd = -1), and are skipped by kernel enable/disable calls. Iterate over group members in userspace and manually enable/disable any members if the leader or the member is a non-perf-event open PMU, and synchronize their disabled flags. Closes: https://lore.kernel.org/linux-perf-users/20260517093650.2540920-1-nigro.fra@gmail.com/ Fixes: b71f46a6a7086175 ("perf stat: Remove hard coded shadow metrics") Reported-by: Francesco Nigro Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 10 +- tools/perf/util/evsel.c | 205 ++++++++++++++++++++++++------- tools/perf/util/evsel.h | 15 ++- tools/perf/util/tool_pmu.c | 246 +++++++++++++++++++++++++++++-------- tools/perf/util/tool_pmu.h | 4 + 5 files changed, 379 insertions(+), 101 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ee971d15b3c6..1a238b245b3a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -529,7 +529,7 @@ static int evlist__is_enabled(struct evlist *evlist) static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl_dummy) { - struct evsel *pos; + struct evsel *pos, *member; struct evlist_cpu_iterator evlist_cpu_itr; bool has_imm = false; @@ -561,6 +561,9 @@ static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl if (excl_dummy && evsel__is_dummy_event(pos)) continue; pos->disabled = true; + + for_each_group_member(member, pos) + member->disabled = true; } /* @@ -590,7 +593,7 @@ void evlist__disable_evsel(struct evlist *evlist, char *evsel_name) static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool excl_dummy) { - struct evsel *pos; + struct evsel *pos, *member; struct evlist_cpu_iterator evlist_cpu_itr; evlist__for_each_cpu(evlist_cpu_itr, evlist) { @@ -611,6 +614,9 @@ static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool excl_ if (excl_dummy && evsel__is_dummy_event(pos)) continue; pos->disabled = false; + + for_each_group_member(member, pos) + member->disabled = false; } /* diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 713a250c7374..ac92f9e0e5b4 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -11,68 +11,71 @@ */ #define __SANE_USERSPACE_TYPES__ -#include +#include "evsel.h" + #include #include +#include + +#include #include -#include -#include +#include +#include +#include #include #include -#include -#include #include #include #include #include #include -#include -#include + +#include +#include +#include +#include +#include +#include +#include #include + +#include "../perf-sys.h" #include "asm/bug.h" +#include "bpf-filter.h" #include "bpf_counter.h" #include "callchain.h" #include "cgroup.h" #include "counts.h" -#include "dwarf-regs.h" -#include "event.h" -#include "evsel.h" -#include "time-utils.h" -#include "util/env.h" -#include "util/evsel_config.h" -#include "util/evsel_fprintf.h" -#include "evlist.h" -#include -#include "thread_map.h" -#include "target.h" -#include "perf_regs.h" -#include "record.h" #include "debug.h" -#include "trace-event.h" +#include "drm_pmu.h" +#include "dwarf-regs.h" +#include "env.h" +#include "event.h" +#include "evlist.h" +#include "evsel_config.h" +#include "evsel_fprintf.h" +#include "hashmap.h" +#include "hist.h" +#include "hwmon_pmu.h" +#include "intel-tpebs.h" +#include "memswap.h" +#include "off_cpu.h" +#include "parse-branch-options.h" +#include "perf_regs.h" +#include "pmu.h" +#include "pmus.h" +#include "record.h" +#include "rlimit.h" #include "session.h" #include "stat.h" #include "string2.h" -#include "memswap.h" -#include "util.h" -#include "util/hashmap.h" -#include "off_cpu.h" -#include "pmu.h" -#include "pmus.h" -#include "drm_pmu.h" -#include "hwmon_pmu.h" +#include "target.h" +#include "thread_map.h" +#include "time-utils.h" #include "tool_pmu.h" #include "tp_pmu.h" -#include "rlimit.h" -#include "../perf-sys.h" -#include "util/parse-branch-options.h" -#include "util/bpf-filter.h" -#include "util/hist.h" -#include -#include -#include -#include "util/intel-tpebs.h" - -#include +#include "trace-event.h" +#include "util.h" #ifdef HAVE_LIBTRACEEVENT #include @@ -1795,27 +1798,114 @@ int evsel__append_addr_filter(struct evsel *evsel, const char *filter) /* Caller has to clear disabled after going through all CPUs. */ int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); + int err; + + if (evsel__is_tool(evsel)) + err = evsel__tool_pmu_enable_cpu(evsel, cpu_map_idx); + else + err = perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel (opened with group_fd = -1) + * and are skipped by the kernel when enabling the + * group leader. We must manually enable them in + * userspace. + */ + int mem_err = evsel__enable_cpu(member, cpu_map_idx); + + if (mem_err) + return mem_err; + } + } + } + return err; } int evsel__enable(struct evsel *evsel) { - int err = perf_evsel__enable(&evsel->core); + int err; + + if (evsel__is_tool(evsel)) + err = evsel__tool_pmu_enable(evsel); + else + err = perf_evsel__enable(&evsel->core); if (!err) evsel->disabled = false; + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel (opened with group_fd = -1) + * and are skipped by the kernel when enabling the + * group leader. We must manually enable them in + * userspace. + */ + int mem_err = evsel__enable(member); + + if (mem_err) + return mem_err; + } + member->disabled = false; + } + } + return err; } /* Caller has to set disabled after going through all CPUs. */ int evsel__disable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__disable_cpu(&evsel->core, cpu_map_idx); + int err; + + if (evsel__is_tool(evsel)) + err = evsel__tool_pmu_disable_cpu(evsel, cpu_map_idx); + else + err = perf_evsel__disable_cpu(&evsel->core, cpu_map_idx); + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel and are skipped by the + * kernel when disabling the group leader. We must + * manually disable them in userspace. + */ + int mem_err = evsel__disable_cpu(member, cpu_map_idx); + + if (mem_err) + return mem_err; + } + } + } + return err; } int evsel__disable(struct evsel *evsel) { - int err = perf_evsel__disable(&evsel->core); + int err; + + if (evsel__is_tool(evsel)) + err = evsel__tool_pmu_disable(evsel); + else + err = perf_evsel__disable(&evsel->core); + /* * We mark it disabled here so that tools that disable a event can * ignore events after they disable it. I.e. the ring buffer may have @@ -1825,6 +1915,27 @@ int evsel__disable(struct evsel *evsel) if (!err) evsel->disabled = true; + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel and are skipped by the + * kernel when disabling the group leader. We must + * manually disable them in userspace. + */ + int mem_err = evsel__disable(member); + + if (mem_err) + return mem_err; + } + member->disabled = true; + } + } + return err; } @@ -1885,8 +1996,10 @@ void evsel__exit(struct evsel *evsel) evsel__priv_destructor(evsel->priv); perf_evsel__object.fini(evsel); if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME || - evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) - xyarray__delete(evsel->start_times); + evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) { + xyarray__delete(evsel->process_time.start_times); + xyarray__delete(evsel->process_time.accumulated_times); + } } void evsel__delete(struct evsel *evsel) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 927e5b4756cc..3b12d99f0aa9 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -190,12 +190,18 @@ struct evsel { double max; } retirement_latency; /* duration_time is a single global time. */ - __u64 start_time; + struct { + __u64 start_time; + __u64 accumulated_time; + } duration_time; /* * user_time and system_time read an initial value potentially * per-CPU or per-pid. */ - struct xyarray *start_times; + struct { + struct xyarray *start_times; + struct xyarray *accumulated_times; + } process_time; }; /* Is the tool's fd for /proc/pid/stat or /proc/stat. */ bool pid_stat; @@ -350,6 +356,11 @@ void arch_evsel__apply_ratio_to_prev(struct evsel *evsel, struct perf_event_attr int evsel__set_filter(struct evsel *evsel, const char *filter); int evsel__append_tp_filter(struct evsel *evsel, const char *filter); int evsel__append_addr_filter(struct evsel *evsel, const char *filter); +static inline bool evsel__is_non_perf_event_open_pmu(const struct evsel *evsel) +{ + return evsel->pmu && evsel->pmu->type > PERF_PMU_TYPE_PE_END; +} + int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx); int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c index 6a9df3dc0e07..5c30854b4644 100644 --- a/tools/perf/util/tool_pmu.c +++ b/tools/perf/util/tool_pmu.c @@ -17,6 +17,8 @@ #include #include +#define INVALID_START_TIME ~0ULL + static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = { NULL, "duration_time", @@ -205,20 +207,57 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, int nthreads) { - if ((evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME || - evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) && - !evsel->start_times) { - evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), - nthreads, - sizeof(__u64)); - if (!evsel->start_times) - return -ENOMEM; + enum tool_pmu_event ev = evsel__tool_event(evsel); + + if (ev == TOOL_PMU__EVENT_SYSTEM_TIME || ev == TOOL_PMU__EVENT_USER_TIME) { + if (!evsel->process_time.start_times) { + evsel->process_time.start_times = + xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); + if (!evsel->process_time.start_times) + return -ENOMEM; + } + if (!evsel->process_time.accumulated_times) { + evsel->process_time.accumulated_times = + xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); + if (!evsel->process_time.accumulated_times) + return -ENOMEM; + } } return 0; } #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) +static int tool_pmu__read_stat(struct evsel *evsel, int cpu_map_idx, int thread, __u64 *val) +{ + enum tool_pmu_event ev = evsel__tool_event(evsel); + bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME; + int fd = FD(evsel, cpu_map_idx, thread); + int err = 0; + + if (fd < 0) { + *val = 0; + return 0; + } + + lseek(fd, 0, SEEK_SET); + if (evsel->pid_stat) { + if (cpu_map_idx == 0) + err = read_pid_stat_field(fd, system ? 15 : 14, val); + else + *val = 0; + } else { + if (thread == 0) { + struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, cpu_map_idx); + + err = read_stat_field(fd, cpu, system ? 3 : 1, val); + } else { + *val = 0; + } + } + return err; +} + int evsel__tool_pmu_open(struct evsel *evsel, struct perf_thread_map *threads, int start_cpu_map_idx, int end_cpu_map_idx) @@ -232,7 +271,14 @@ int evsel__tool_pmu_open(struct evsel *evsel, if (ev == TOOL_PMU__EVENT_DURATION_TIME) { if (evsel->core.attr.sample_period) /* no sampling */ return -EINVAL; - evsel->start_time = rdclock(); + evsel->duration_time.accumulated_time = 0; + if (evsel->core.attr.disabled) { + evsel->disabled = true; + evsel->duration_time.start_time = INVALID_START_TIME; + } else { + evsel->disabled = false; + evsel->duration_time.start_time = rdclock(); + } return 0; } @@ -246,8 +292,8 @@ int evsel__tool_pmu_open(struct evsel *evsel, pid = perf_thread_map__pid(threads, thread); if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) { - bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME; __u64 *start_time = NULL; + __u64 *accumulated_time = NULL; int fd; if (evsel->core.attr.sample_period) { @@ -269,21 +315,25 @@ int evsel__tool_pmu_open(struct evsel *evsel, err = -errno; goto out_close; } - start_time = xyarray__entry(evsel->start_times, idx, thread); - if (pid > -1) { - err = read_pid_stat_field(fd, system ? 15 : 14, - start_time); + start_time = xyarray__entry(evsel->process_time.start_times, idx, + thread); + accumulated_time = xyarray__entry( + evsel->process_time.accumulated_times, idx, thread); + *accumulated_time = 0; + + if (evsel->core.attr.disabled) { + evsel->disabled = true; + *start_time = INVALID_START_TIME; } else { - struct perf_cpu cpu; - - cpu = perf_cpu_map__cpu(evsel->core.cpus, idx); - err = read_stat_field(fd, cpu, system ? 3 : 1, - start_time); + evsel->disabled = false; + err = tool_pmu__read_stat(evsel, idx, thread, start_time); + if (err) { + close(fd); + FD(evsel, idx, thread) = -1; + goto out_close; + } } - if (err) - goto out_close; } - } } return 0; @@ -467,10 +517,111 @@ static void perf_counts__update(struct perf_counts_values *count, count->lost = 0; } } +int evsel__tool_pmu_enable_cpu(struct evsel *evsel, int cpu_map_idx) +{ + enum tool_pmu_event ev = evsel__tool_event(evsel); + int thread, nthreads; + + if (!evsel->disabled) + return 0; + + if (ev == TOOL_PMU__EVENT_DURATION_TIME) { + if (cpu_map_idx == 0) + evsel->duration_time.start_time = rdclock(); + return 0; + } + + if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) { + nthreads = xyarray__max_y(evsel->process_time.start_times); + for (thread = 0; thread < nthreads; thread++) { + __u64 *start_time = xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 val; + int err; + + err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val); + if (!err) + *start_time = val; + else + *start_time = INVALID_START_TIME; + } + } + return 0; +} + +int evsel__tool_pmu_enable(struct evsel *evsel) +{ + unsigned int idx; + int err = 0; + + if (!evsel->disabled) + return 0; + + for (idx = 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) { + err = evsel__tool_pmu_enable_cpu(evsel, idx); + if (err) + break; + } + return err; +} + +int evsel__tool_pmu_disable_cpu(struct evsel *evsel, int cpu_map_idx) +{ + enum tool_pmu_event ev = evsel__tool_event(evsel); + int thread, nthreads; + + if (evsel->disabled) + return 0; + + if (ev == TOOL_PMU__EVENT_DURATION_TIME) { + if (cpu_map_idx == 0) { + __u64 delta = rdclock() - evsel->duration_time.start_time; + + evsel->duration_time.accumulated_time += delta; + } + return 0; + } + + if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) { + nthreads = xyarray__max_y(evsel->process_time.start_times); + for (thread = 0; thread < nthreads; thread++) { + __u64 *start_time = xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 *accumulated_time = xyarray__entry( + evsel->process_time.accumulated_times, cpu_map_idx, thread); + __u64 val; + int err; + + err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val); + if (!err) { + if (*start_time != INVALID_START_TIME && val >= *start_time) + *accumulated_time += (val - *start_time); + } + *start_time = INVALID_START_TIME; + } + } + return 0; +} + +int evsel__tool_pmu_disable(struct evsel *evsel) +{ + unsigned int idx; + int err = 0; + + if (evsel->disabled) + return 0; + + for (idx = 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) { + err = evsel__tool_pmu_disable_cpu(evsel, idx); + if (err) + break; + } + return err; +} int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) { - __u64 *start_time, cur_time, delta_start; + __u64 delta_start = 0; int err = 0; struct perf_counts_values *count, *old_count = NULL; bool adjust = false; @@ -507,39 +658,33 @@ int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) return 0; } case TOOL_PMU__EVENT_DURATION_TIME: - /* - * Pretend duration_time is only on the first CPU and thread, or - * else aggregation will scale duration_time by the number of - * CPUs/threads. - */ - start_time = &evsel->start_time; - if (cpu_map_idx == 0 && thread == 0) - cur_time = rdclock(); - else - cur_time = *start_time; + if (cpu_map_idx == 0 && thread == 0) { + delta_start = evsel->duration_time.accumulated_time; + if (!evsel->disabled && + evsel->duration_time.start_time != INVALID_START_TIME) + delta_start += (rdclock() - evsel->duration_time.start_time); + } else { + delta_start = 0; + } break; case TOOL_PMU__EVENT_USER_TIME: case TOOL_PMU__EVENT_SYSTEM_TIME: { - bool system = evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME; - int fd = FD(evsel, cpu_map_idx, thread); + __u64 accumulated = *(__u64 *)xyarray__entry(evsel->process_time.accumulated_times, + cpu_map_idx, thread); - start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread); - lseek(fd, SEEK_SET, 0); - if (evsel->pid_stat) { - /* The event exists solely on 1 CPU. */ - if (cpu_map_idx == 0) - err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time); - else - cur_time = 0; + if (evsel->disabled) { + delta_start = accumulated; } else { - /* The event is for all threads. */ - if (thread == 0) { - struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, - cpu_map_idx); + __u64 *start_time = xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 cur_time; - err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time); - } else { - cur_time = 0; + err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &cur_time); + if (!err) { + if (*start_time != INVALID_START_TIME && cur_time >= *start_time) + delta_start = accumulated + (cur_time - *start_time); + else + delta_start = accumulated; } } adjust = true; @@ -553,7 +698,6 @@ int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) if (err) return err; - delta_start = cur_time - *start_time; if (adjust) { __u64 ticks_per_sec = sysconf(_SC_CLK_TCK); diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h index f1714001bc1d..f66d24cf3502 100644 --- a/tools/perf/util/tool_pmu.h +++ b/tools/perf/util/tool_pmu.h @@ -56,6 +56,10 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel, int evsel__tool_pmu_open(struct evsel *evsel, struct perf_thread_map *threads, int start_cpu_map_idx, int end_cpu_map_idx); +int evsel__tool_pmu_enable_cpu(struct evsel *evsel, int cpu_map_idx); +int evsel__tool_pmu_enable(struct evsel *evsel); +int evsel__tool_pmu_disable_cpu(struct evsel *evsel, int cpu_map_idx); +int evsel__tool_pmu_disable(struct evsel *evsel); int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread); struct perf_pmu *tool_pmu__new(void); From f83deb058025d1b6eb0ff297422634a5a11ef87b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 18 May 2026 18:41:06 -0700 Subject: [PATCH 1597/5214] perf tests: Add test for stat delay option with duration_time Add a new test case `test_stat_delay` to `stat.sh` to verify that `duration_time` correctly excludes the delay period when using the delay option (-D). The test runs `perf stat -D 1000 -e duration_time sleep 2` and verifies that `duration_time` is ~1s (excluding the 1s delay), not ~2s. Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Francesco Nigro Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat.sh | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh index 4edb04039036..1e17bee026bd 100755 --- a/tools/perf/tests/shell/stat.sh +++ b/tools/perf/tests/shell/stat.sh @@ -483,6 +483,58 @@ test_stat_pid() { wait $pid 2>/dev/null || true } +test_stat_delay() { + echo "stat -D test" + if ! env LC_ALL=C perf stat -D 1000 -e duration_time sleep 2 > "${stat_output}" 2>&1 + then + echo "stat -D test [Failed - command failed]" + cat "${stat_output}" + err=1 + return + fi + + duration=$(grep "duration_time" "${stat_output}" | awk '{print $1}' | tr -d ',') + elapsed=$(grep "seconds time elapsed" "${stat_output}" | awk '{print $1}') + + if [ -z "$duration" ] || [ -z "$elapsed" ] + then + echo "stat -D test [Failed - failed to find duration_time or time elapsed in output]" + cat "${stat_output}" + err=1 + return + fi + + # Compare duration (ns) and elapsed (s) using awk to handle float and allow tolerance. + if ! awk -v d="$duration" -v e="$elapsed" ' + BEGIN { + diff = d - (e * 1e9); + if (diff < 0) diff = -diff; + # Allow 200ms tolerance (200,000,000 ns) for loaded CI machines. + if (diff > 200000000) { + printf "Fail: duration (%d ns) and elapsed (%f s) mismatch (diff %d ns)\n", d, e, diff; + exit 1; + } + # Lower bound check: must be at least 0.5s. + if (d < 500000000) { + printf "Fail: duration (%d ns) is abnormally small\n", d; + exit 1; + } + # Upper bound check: must be strictly less than 1.7s (proving delay was excluded). + if (d > 1700000000) { + printf "Fail: duration (%d ns) is too large (delay might not be excluded)\n", d; + exit 1; + } + exit 0; + }' + then + echo "stat -D test [Failed - validation failed]" + cat "${stat_output}" + err=1 + return + fi + echo "stat -D test [Success]" +} + test_default_stat test_null_stat test_offline_cpu_stat @@ -498,6 +550,7 @@ test_stat_no_aggr test_stat_detailed test_stat_repeat test_stat_pid +test_stat_delay cleanup exit $err From 09be9d404f42fd2e7d4d378cae07499879f837f4 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 17 May 2026 19:15:47 +0200 Subject: [PATCH 1598/5214] scsi: scsi_ioctl: Use strnlen() in scsi_ioctl_get_pci() Use strnlen() to limit string scanning to 20 characters. Reformat the code and use tabs instead of spaces while at it. [mkp: tweaked comment formatting] Signed-off-by: Thorsten Blum Reviewed-by: John Garry Link: https://patch.msgid.link/20260517171546.2304-2-thorsten.blum@linux.dev Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_ioctl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 0ddc95bafc71..c14f81403a09 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -176,10 +176,13 @@ static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg) name = dev_name(dev); - /* compatibility with old ioctl which only returned - * 20 characters */ - return copy_to_user(arg, name, min(strlen(name), (size_t)20)) - ? -EFAULT: 0; + /* + * Compatibility with old ioctl which only returned 20 characters. + */ + if (copy_to_user(arg, name, strnlen(name, 20))) + return -EFAULT; + + return 0; } static int sg_get_version(int __user *p) From 8933fa6695aaea6559d3469581139a4c1f427369 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Fri, 1 May 2026 06:16:40 -0700 Subject: [PATCH 1599/5214] scsi: ufs: core: Add a quirk for extended TX EQTR Adapt L0L1L2L3 length Add a quirk to support TX Equalization Training (EQTR) using Adapt L0L1L2L3 length which is larger than what is allowed by M-PHY spec ver 6.0. Signed-off-by: Can Guo Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Reviewed-by: Peter Wang Reviewed-by: Ziqi Chen Link: https://patch.msgid.link/20260501131641.826258-2-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-txeq.c | 8 ++++++-- include/ufs/ufshcd.h | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index 4b264adfdf49..aa64f2bf4f1e 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -885,7 +885,9 @@ static int ufshcd_setup_tx_eqtr_adapt_length(struct ufs_hba *hba, if (adapt_l0l1l2l3_cap_local > ADAPT_L0L1L2L3_LENGTH_MAX) { dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", gear, adapt_l0l1l2l3_cap_local); - return -EINVAL; + + if (!(hba->quirks & UFSHCD_QUIRK_EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3)) + return -EINVAL; } ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), @@ -896,7 +898,9 @@ static int ufshcd_setup_tx_eqtr_adapt_length(struct ufs_hba *hba, if (adapt_l0l1l2l3_cap_peer > ADAPT_L0L1L2L3_LENGTH_MAX) { dev_err(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", gear, adapt_l0l1l2l3_cap_peer); - return -EINVAL; + + if (!(hba->quirks & UFSHCD_QUIRK_EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3)) + return -EINVAL; } t_adapt_l0l1l2l3_local = adapt_cap_to_t_adapt_l0l1l2l3(adapt_l0l1l2l3_cap_local); diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index f48d6416e299..3eaae082329c 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -806,6 +806,13 @@ enum ufshcd_quirks { * delay after enabling VCC to ensure it's stable. */ UFSHCD_QUIRK_VCC_ON_DELAY = 1 << 27, + + /* + * This quirk indicates that Host supports TX Equalization Training + * (EQTR) using Adapt L0L1L2L3 length which is larger than what is + * allowed by M-PHY spec ver 6.0. + */ + UFSHCD_QUIRK_EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3 = 1 << 28, }; enum ufshcd_caps { From 0f51fd84684316375d5c191a1ec5e18743fb1601 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Fri, 1 May 2026 06:16:41 -0700 Subject: [PATCH 1600/5214] scsi: ufs: ufs-qcom: Use quirk EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3 Use UFSHCD_QUIRK_EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3 for UFS Hosts HW major version 0x7 & minor version 0x1. Signed-off-by: Can Guo Reviewed-by: Ziqi Chen Reviewed-by: Bean Huo Link: https://patch.msgid.link/20260501131641.826258-3-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index c084ccc72523..2940d64a04ea 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -1315,6 +1315,9 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) if (host->hw_ver.major > 0x3) hba->quirks |= UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH; + if (host->hw_ver.major == 0x7 && host->hw_ver.minor == 0x1) + hba->quirks |= UFSHCD_QUIRK_EXTENDED_TX_EQTR_ADAPT_LENGTH_L0L1L2L3; + if (drvdata && drvdata->quirks) hba->quirks |= drvdata->quirks; } From f199cee401f7dc7280c8ed070d2433478253b665 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 May 2026 10:14:53 -0700 Subject: [PATCH 1601/5214] scsi: scsi_debug: Remove unused variable sdebug_any_injecting_opt The static variable sdebug_any_injecting_opt is no longer read. Commit 3a90a63d02b8 ("scsi: scsi_debug: every_nth triggered error injection") removed all code that reads this variable. Hence, also remove this variable itself. Remove SDEBUG_OPT_ALL_INJECTING because there is no code left that uses this constant if sdebug_any_injecting_opt is removed. This has been detected by building the scsi_debug driver with the git HEAD version of Clang and with W=1. Signed-off-by: Bart Van Assche Reviewed-by: John Garry Link: https://patch.msgid.link/20260520171454.4035623-1-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 1515495fd9ea..a2f85ee1ae57 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -233,13 +233,6 @@ struct tape_block { #define SDEBUG_OPT_UNALIGNED_WRITE 0x20000 #define SDEBUG_OPT_ALL_NOISE (SDEBUG_OPT_NOISE | SDEBUG_OPT_Q_NOISE | \ SDEBUG_OPT_RESET_NOISE) -#define SDEBUG_OPT_ALL_INJECTING (SDEBUG_OPT_RECOVERED_ERR | \ - SDEBUG_OPT_TRANSPORT_ERR | \ - SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR | \ - SDEBUG_OPT_SHORT_TRANSFER | \ - SDEBUG_OPT_HOST_BUSY | \ - SDEBUG_OPT_CMD_ABORT | \ - SDEBUG_OPT_UNALIGNED_WRITE) #define SDEBUG_OPT_RECOV_DIF_DIX (SDEBUG_OPT_RECOVERED_ERR | \ SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR) @@ -955,7 +948,6 @@ static bool sdebug_removable = DEF_REMOVABLE; static bool sdebug_clustering; static bool sdebug_host_lock = DEF_HOST_LOCK; static bool sdebug_strict = DEF_STRICT; -static bool sdebug_any_injecting_opt; static bool sdebug_no_rwlock; static bool sdebug_verbose; static bool have_dif_prot; @@ -7528,7 +7520,6 @@ static int scsi_debug_write_info(struct Scsi_Host *host, char *buffer, return -EINVAL; sdebug_opts = opts; sdebug_verbose = !!(SDEBUG_OPT_NOISE & opts); - sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & opts); if (sdebug_every_nth != 0) tweak_cmnd_count(); return length; @@ -7748,7 +7739,6 @@ static ssize_t opts_store(struct device_driver *ddp, const char *buf, opts_done: sdebug_opts = opts; sdebug_verbose = !!(SDEBUG_OPT_NOISE & opts); - sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & opts); tweak_cmnd_count(); return count; } @@ -9659,7 +9649,6 @@ static int sdebug_driver_probe(struct device *dev) scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_CRC); sdebug_verbose = !!(SDEBUG_OPT_NOISE & sdebug_opts); - sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & sdebug_opts); if (sdebug_every_nth) /* need stats counters for every_nth */ sdebug_statistics = true; error = scsi_add_host(hpnt, &sdbg_host->dev); From c1f7275b613b5bb140efe22071167ed6c68c9a05 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 May 2026 22:21:24 +0200 Subject: [PATCH 1602/5214] scsi: megaraid_mbox: Reduce stack usage in megaraid_cmm_register() The megaraid_cmm_register() function has a local copy of mraid_mmadp_t on the stack that gets copied into the actual structure used at runtime. When -fsanitize=thread is enabled, this causes the per-function stack frame to grow beyond the warning limit: megaraid_mbox.c: In function 'megaraid_cmm_register': megaraid_mbox.c:3472:1: error: the frame size of 1312 bytes is larger than 1280 bytes [-Werror=frame-larger-than=] Refactor this by moving the allocation into the caller to save the extra on-stack copy of the structure. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260519202143.1305850-1-arnd@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_mbox.c | 26 ++++++++++++++--------- drivers/scsi/megaraid/megaraid_mm.c | 30 +++++++-------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 06cf94ee4e36..ce89032a5a74 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -3396,7 +3396,7 @@ static int megaraid_cmm_register(adapter_t *adapter) { mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); - mraid_mmadp_t adp; + mraid_mmadp_t *adp; scb_t *scb; mbox_ccb_t *ccb; int rval; @@ -3404,11 +3404,16 @@ megaraid_cmm_register(adapter_t *adapter) // Allocate memory for the base list of scb for management module. adapter->uscb_list = kzalloc_objs(scb_t, MBOX_MAX_USER_CMDS); + adp = kzalloc_obj(*adp); - if (adapter->uscb_list == NULL) { + if (!adapter->uscb_list || !adp) { con_log(CL_ANN, (KERN_WARNING "megaraid: out of memory, %s %d\n", __func__, __LINE__)); + + kfree(adapter->uscb_list); + kfree(adp); + return -1; } @@ -3452,20 +3457,21 @@ megaraid_cmm_register(adapter_t *adapter) list_add_tail(&scb->list, &adapter->uscb_pool); } - adp.unique_id = adapter->unique_id; - adp.drvr_type = DRVRTYPE_MBOX; - adp.drvr_data = (unsigned long)adapter; - adp.pdev = adapter->pdev; - adp.issue_uioc = megaraid_mbox_mm_handler; - adp.timeout = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; - adp.max_kioc = MBOX_MAX_USER_CMDS; + adp->unique_id = adapter->unique_id; + adp->drvr_type = DRVRTYPE_MBOX; + adp->drvr_data = (unsigned long)adapter; + adp->pdev = adapter->pdev; + adp->issue_uioc = megaraid_mbox_mm_handler; + adp->timeout = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; + adp->max_kioc = MBOX_MAX_USER_CMDS; - if ((rval = mraid_mm_register_adp(&adp)) != 0) { + if ((rval = mraid_mm_register_adp(adp)) != 0) { con_log(CL_ANN, (KERN_WARNING "megaraid mbox: did not register with CMM\n")); kfree(adapter->uscb_list); + kfree(adp); } return rval; diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index 538da0e98131..60db48dc8f3a 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -898,42 +898,28 @@ hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo) /** * mraid_mm_register_adp - Registration routine for low level drivers - * @lld_adp : Adapter object + * @adapter : Adapter object */ int -mraid_mm_register_adp(mraid_mmadp_t *lld_adp) +mraid_mm_register_adp(mraid_mmadp_t *adapter) { - mraid_mmadp_t *adapter; mbox64_t *mbox_list; uioc_t *kioc; uint32_t rval; int i; - if (lld_adp->drvr_type != DRVRTYPE_MBOX) + if (adapter->drvr_type != DRVRTYPE_MBOX) return (-EINVAL); - adapter = kzalloc_obj(mraid_mmadp_t); - - if (!adapter) - return -ENOMEM; - - - adapter->unique_id = lld_adp->unique_id; - adapter->drvr_type = lld_adp->drvr_type; - adapter->drvr_data = lld_adp->drvr_data; - adapter->pdev = lld_adp->pdev; - adapter->issue_uioc = lld_adp->issue_uioc; - adapter->timeout = lld_adp->timeout; - adapter->max_kioc = lld_adp->max_kioc; adapter->quiescent = 1; /* * Allocate single blocks of memory for all required kiocs, * mailboxes and passthru structures. */ - adapter->kioc_list = kmalloc_objs(uioc_t, lld_adp->max_kioc); - adapter->mbox_list = kmalloc_objs(mbox64_t, lld_adp->max_kioc); + adapter->kioc_list = kmalloc_objs(uioc_t, adapter->max_kioc); + adapter->mbox_list = kmalloc_objs(mbox64_t, adapter->max_kioc); adapter->pthru_dma_pool = dma_pool_create("megaraid mm pthru pool", &adapter->pdev->dev, sizeof(mraid_passthru_t), @@ -956,11 +942,11 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp) */ INIT_LIST_HEAD(&adapter->kioc_pool); spin_lock_init(&adapter->kioc_pool_lock); - sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc); + sema_init(&adapter->kioc_semaphore, adapter->max_kioc); mbox_list = (mbox64_t *)adapter->mbox_list; - for (i = 0; i < lld_adp->max_kioc; i++) { + for (i = 0; i < adapter->max_kioc; i++) { kioc = adapter->kioc_list + i; kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i); @@ -997,7 +983,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp) pthru_dma_pool_error: - for (i = 0; i < lld_adp->max_kioc; i++) { + for (i = 0; i < adapter->max_kioc; i++) { kioc = adapter->kioc_list + i; if (kioc->pthru32) { dma_pool_free(adapter->pthru_dma_pool, kioc->pthru32, From 727e78887e62e16be30ec7ecf62017f0a86d53bd Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 19 May 2026 14:21:27 -0700 Subject: [PATCH 1603/5214] scsi: ufs: core: Inline two functions related to UIC commands The implementation of the two functions ufshcd_get_uic_cmd_result() and ufshcd_get_dme_attr_val() is very short. Additionally, both functions only have one caller. Inline both functions to make the code shorter. Reviewed-by: Peter Wang Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/20260519212135.3130556-2-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 1aad1c03c3fc..f3e226e47c90 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -912,33 +912,6 @@ static inline int ufshcd_get_lists_status(u32 reg) return !((reg & UFSHCD_STATUS_READY) == UFSHCD_STATUS_READY); } -/** - * ufshcd_get_uic_cmd_result - Get the UIC command result - * @hba: Pointer to adapter instance - * - * This function gets the result of UIC command completion - * - * Return: 0 on success; non-zero value on error. - */ -static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) -{ - return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) & - MASK_UIC_COMMAND_RESULT; -} - -/** - * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command - * @hba: Pointer to adapter instance - * - * This function gets UIC command argument3 - * - * Return: 0 on success; non-zero value on error. - */ -static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba) -{ - return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3); -} - /** * ufshcd_get_req_rsp - returns the TR response transaction type * @ucd_rsp_ptr: pointer to response UPIU @@ -5810,8 +5783,14 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status); if (intr_status & UIC_COMMAND_COMPL) { - cmd->argument2 |= ufshcd_get_uic_cmd_result(hba); - cmd->argument3 = ufshcd_get_dme_attr_val(hba); + /* + * Store the UIC command result in the lowest byte of + * cmd->argument2. + */ + cmd->argument2 |= ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) & + MASK_UIC_COMMAND_RESULT; + /* Store the DME attribute value in cmd->argument3. */ + cmd->argument3 = ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3); if (!hba->uic_async_done) cmd->cmd_active = false; complete(&cmd->done); From 9fb4c793223b618da6bee50840746522a7da226e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 19 May 2026 14:21:28 -0700 Subject: [PATCH 1604/5214] scsi: ufs: core: Complain if UIC argument 2 is invalid According to the UFSHCI standard, the lowest byte of UIC argument 2 is an output value. Additionally, ufshcd_uic_cmd_compl() is based on the assumption that the lowest byte of UIC argument 2 is zero. Hence, complain if the result byte is set when a UIC command is submitted. Reviewed-by: Peter Wang Signed-off-by: Bart Van Assche Link: https://patch.msgid.link/20260519212135.3130556-3-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index f3e226e47c90..cfb362fe9784 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -2571,6 +2571,7 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) lockdep_assert_held(&hba->uic_cmd_mutex); WARN_ON(hba->active_uic_cmd); + WARN_ON_ONCE(uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT); hba->active_uic_cmd = uic_cmd; From f8380c57dcff5ac3b32393a05ff6a6ff0108bf3e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 19 May 2026 14:21:29 -0700 Subject: [PATCH 1605/5214] scsi: ufs: core: Optimize ufshcd_add_uic_command_trace() Use cached values in ufshcd_add_uic_command_trace() instead of calling readl() when tracing command submission (UFS_CMD_SEND). Signed-off-by: Bart Van Assche Reviewed-by: Peter Wang Link: https://patch.msgid.link/20260519212135.3130556-4-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index cfb362fe9784..c1a0a79e386d 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -460,20 +460,24 @@ static void ufshcd_add_uic_command_trace(struct ufs_hba *hba, const struct uic_command *ucmd, enum ufs_trace_str_t str_t) { - u32 cmd; + u32 cmd, arg1, arg2, arg3; if (!trace_ufshcd_uic_command_enabled()) return; - if (str_t == UFS_CMD_SEND) + if (str_t == UFS_CMD_SEND) { cmd = ucmd->command; - else + arg1 = ucmd->argument1; + arg2 = ucmd->argument2; + arg3 = ucmd->argument3; + } else { cmd = ufshcd_readl(hba, REG_UIC_COMMAND); + arg1 = ufshcd_readl(hba, REG_UIC_COMMAND_ARG_1); + arg2 = ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2); + arg3 = ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3); + } - trace_ufshcd_uic_command(hba, str_t, cmd, - ufshcd_readl(hba, REG_UIC_COMMAND_ARG_1), - ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2), - ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3)); + trace_ufshcd_uic_command(hba, str_t, cmd, arg1, arg2, arg3); } static void ufshcd_add_command_trace(struct ufs_hba *hba, struct scsi_cmnd *cmd, From b1968f46509e077d3241ac509e41fd14ec2395db Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 15 May 2026 13:52:19 -0700 Subject: [PATCH 1606/5214] scsi: core: target: Add INQUIRY-related constants to scsi_common.h Move three constants from target/target_core_base.h into scsi/scsi_common.h. Add three new constants in the scsi_common.h header file. This patch prepares for using these constants in the SCSI core. Signed-off-by: Bart Van Assche Tested-by: Brian Bunker Link: https://patch.msgid.link/20260515205222.1754621-2-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- include/scsi/scsi_common.h | 8 ++++++++ include/target/target_core_base.h | 5 +---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/scsi/scsi_common.h b/include/scsi/scsi_common.h index fb58715fac86..00c8a16d3cd2 100644 --- a/include/scsi/scsi_common.h +++ b/include/scsi/scsi_common.h @@ -10,6 +10,14 @@ #include #include +/* From the standard INQUIRY data description in SPC-6. */ +#define INQUIRY_VENDOR_OFFSET 8 +#define INQUIRY_VENDOR_LEN 8 +#define INQUIRY_MODEL_OFFSET 16 +#define INQUIRY_MODEL_LEN 16 +#define INQUIRY_REVISION_OFFSET 32 +#define INQUIRY_REVISION_LEN 4 + enum scsi_pr_type { SCSI_PR_WRITE_EXCLUSIVE = 0x01, SCSI_PR_EXCLUSIVE_ACCESS = 0x03, diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 9a0e9f9e1ec4..002b0fc57587 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -8,6 +8,7 @@ #include #include /* struct semaphore */ #include +#include #define TARGET_CORE_VERSION "v5.0" @@ -46,10 +47,6 @@ /* Used by transport_get_inquiry_vpd_device_ident() */ #define INQUIRY_VPD_DEVICE_IDENTIFIER_LEN 254 -#define INQUIRY_VENDOR_LEN 8 -#define INQUIRY_MODEL_LEN 16 -#define INQUIRY_REVISION_LEN 4 - /* Attempts before moving from SHORT to LONG */ #define PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD 3 #define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT 3 /* In milliseconds */ From 28ff38b9d8e1a189606e36319401dc98419a3746 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 15 May 2026 13:52:20 -0700 Subject: [PATCH 1607/5214] scsi: core: Use the INQUIRY-related constants Use symbolic names instead of numeric constants to access the vendor and model information. Signed-off-by: Bart Van Assche Tested-by: Brian Bunker Link: https://patch.msgid.link/20260515205222.1754621-3-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a35a5f777d16..b0473f4595c1 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -728,9 +728,13 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, } if (result == 0) { - scsi_sanitize_inquiry_string(&inq_result[8], 8); - scsi_sanitize_inquiry_string(&inq_result[16], 16); - scsi_sanitize_inquiry_string(&inq_result[32], 4); + scsi_sanitize_inquiry_string(&inq_result[INQUIRY_VENDOR_OFFSET], + INQUIRY_VENDOR_LEN); + scsi_sanitize_inquiry_string(&inq_result[INQUIRY_MODEL_OFFSET], + INQUIRY_MODEL_LEN); + scsi_sanitize_inquiry_string( + &inq_result[INQUIRY_REVISION_OFFSET], + INQUIRY_REVISION_LEN); response_len = inq_result[4] + 5; if (response_len > 255) @@ -743,8 +747,9 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, * corresponding bit fields in scsi_device, so bflags * need not be passed as an argument. */ - *bflags = scsi_get_device_flags(sdev, &inq_result[8], - &inq_result[16]); + *bflags = scsi_get_device_flags(sdev, + &inq_result[INQUIRY_VENDOR_OFFSET], + &inq_result[INQUIRY_MODEL_OFFSET]); /* When the first pass succeeds we gain information about * what larger transfer lengths might work. */ From 20fd1648f35399f114351b67c14ff8d3233a30e2 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 15 May 2026 13:52:21 -0700 Subject: [PATCH 1608/5214] scsi: core: Convert INQUIRY information Currently the vendor, model, and revision members of struct scsi_device are pointers to fixed-length strings that are not NUL-terminated. Fixed-precision format specifiers (e.g., "%.8s") are required whenever they are printed and strncmp() must be used to compare these fields. This is error-prone. Convert these fields to fixed-size character arrays within struct scsi_device. Remove an !sdev->model check because sdev->model is now guaranteed not to be NULL. This patch fixes a bug in the qla2xxx driver. It makes the following code safe: if (state_flags & BIT_4) scmd_printk(KERN_WARNING, cp, "Unsupported device '%s' found.\n", cp->device->vendor); Signed-off-by: Bart Van Assche Tested-by: Brian Bunker Link: https://patch.msgid.link/20260515205222.1754621-4-bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/hwmon/drivetemp.c | 5 +---- drivers/scsi/scsi_scan.c | 18 ++++++++++++------ include/scsi/scsi_device.h | 7 ++++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 002e0660a0b8..efe8b229bdbe 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -306,13 +306,10 @@ static bool drivetemp_sct_avoid(struct drivetemp_data *st) struct scsi_device *sdev = st->sdev; unsigned int ctr; - if (!sdev->model) - return false; - /* * The "model" field contains just the raw SCSI INQUIRY response * "product identification" field, which has a width of 16 bytes. - * This field is space-filled, but is NOT NULL-terminated. + * This field is space-filled and NUL-terminated. */ for (ctr = 0; ctr < ARRAY_SIZE(sct_avoid_models); ctr++) if (!strncmp(sdev->model, sct_avoid_models[ctr], diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b0473f4595c1..7e60e3a4bca6 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -292,9 +292,9 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, if (!sdev) goto out; - sdev->vendor = scsi_null_device_strs; - sdev->model = scsi_null_device_strs; - sdev->rev = scsi_null_device_strs; + strscpy(sdev->vendor, scsi_null_device_strs); + strscpy(sdev->model, scsi_null_device_strs); + strscpy(sdev->rev, scsi_null_device_strs); sdev->host = shost; sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD; sdev->id = starget->id; @@ -910,9 +910,15 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, if (sdev->inquiry == NULL) return SCSI_SCAN_NO_RESPONSE; - sdev->vendor = (char *) (sdev->inquiry + 8); - sdev->model = (char *) (sdev->inquiry + 16); - sdev->rev = (char *) (sdev->inquiry + 32); + strscpy(sdev->vendor, sdev->inquiry + INQUIRY_VENDOR_OFFSET); + strscpy(sdev->model, sdev->inquiry + INQUIRY_MODEL_OFFSET); + /* + * memcpy() instead of strscpy() because strscpy() would read past + * the end of sdev->inquiry if its length is exactly 36 bytes. + */ + memcpy(sdev->rev, sdev->inquiry + INQUIRY_REVISION_OFFSET, + INQUIRY_REVISION_LEN); + sdev->rev[INQUIRY_REVISION_LEN] = '\0'; sdev->is_ata = strncmp(sdev->vendor, "ATA ", 8) == 0; if (sdev->is_ata) { diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 9c2a7bbe5891..029f5115b2ea 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -137,9 +138,9 @@ struct scsi_device { struct mutex inquiry_mutex; unsigned char inquiry_len; /* valid bytes in 'inquiry' */ unsigned char * inquiry; /* INQUIRY response data */ - const char * vendor; /* [back_compat] point into 'inquiry' ... */ - const char * model; /* ... after scan; point to static string */ - const char * rev; /* ... "nullnullnullnull" before scan */ + char vendor[INQUIRY_VENDOR_LEN + 1]; + char model[INQUIRY_MODEL_LEN + 1]; + char rev[INQUIRY_REVISION_LEN + 1]; #define SCSI_DEFAULT_VPD_LEN 255 /* default SCSI VPD page size (max) */ struct scsi_vpd __rcu *vpd_pg0; From cf40e2cee8fe86d54f7eeb38944366d5c23e42dc Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 16:49:51 +0100 Subject: [PATCH 1609/5214] openrisc: Cache invalidation cleanup When working on new cache invalidation functions I noticed these cleanups in the cache initialization code. Remove unused and commented instructions to avoid confusion. Signed-off-by: Stafford Horne --- arch/openrisc/kernel/head.S | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/openrisc/kernel/head.S b/arch/openrisc/kernel/head.S index bd760066f1cd..65f474c2df4f 100644 --- a/arch/openrisc/kernel/head.S +++ b/arch/openrisc/kernel/head.S @@ -852,26 +852,19 @@ _ic_enable: l.sll r14,r30,r28 /* Establish number of cache sets - r16 contains number of cache sets r28 contains log(# of cache sets) */ l.andi r26,r24,SPR_ICCFGR_NCS l.srli r28,r26,3 - l.ori r30,r0,1 - l.sll r16,r30,r28 /* Invalidate IC */ l.addi r6,r0,0 l.sll r5,r14,r28 -// l.mul r5,r14,r16 -// l.trap 1 -// l.addi r5,r0,IC_SIZE 1: l.mtspr r0,r6,SPR_ICBIR l.sfne r6,r5 l.bf 1b l.add r6,r6,r14 - // l.addi r6,r6,IC_LINE /* Enable IC */ l.mfspr r6,r0,SPR_SR @@ -918,13 +911,10 @@ _dc_enable: l.sll r14,r30,r28 /* Establish number of cache sets - r16 contains number of cache sets r28 contains log(# of cache sets) */ l.andi r26,r24,SPR_DCCFGR_NCS l.srli r28,r26,3 - l.ori r30,r0,1 - l.sll r16,r30,r28 /* Invalidate DC */ l.addi r6,r0,0 From 1be031de802ba486a7605b52eda825f128b026e2 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 16:56:03 +0100 Subject: [PATCH 1610/5214] openrisc: Add full instruction cache invalidate functions Add functions to invalidate all cache lines which we will use for static_key patching. On OpenRISC there is no instruction to invalidate an entire cache so we loop and invalidate cache lines one by one. This is not extremely expensive on OpenRISC as we usually have only a few hundred cache lines. I considered using the invalidate cache page or range functions. However, tracking which ranges need invalidation would have been more expensive than flushing all pages. Cc: stable@vger.kernel.org Signed-off-by: Stafford Horne --- arch/openrisc/include/asm/cacheflush.h | 4 ++++ arch/openrisc/kernel/smp.c | 21 +++++++++++++++++++++ arch/openrisc/mm/cache.c | 16 ++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/arch/openrisc/include/asm/cacheflush.h b/arch/openrisc/include/asm/cacheflush.h index cd8f971c0fec..7b8c043a831d 100644 --- a/arch/openrisc/include/asm/cacheflush.h +++ b/arch/openrisc/include/asm/cacheflush.h @@ -26,6 +26,7 @@ extern void local_icache_page_inv(struct page *page); extern void local_dcache_range_flush(unsigned long start, unsigned long end); extern void local_dcache_range_inv(unsigned long start, unsigned long end); extern void local_icache_range_inv(unsigned long start, unsigned long end); +extern void local_icache_all_inv(void); /* * Data cache flushing always happen on the local cpu. Instruction cache @@ -35,10 +36,13 @@ extern void local_icache_range_inv(unsigned long start, unsigned long end); #ifndef CONFIG_SMP #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) local_icache_page_inv(page) +#define icache_all_inv() local_icache_all_inv() #else /* CONFIG_SMP */ #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) smp_icache_page_inv(page) +#define icache_all_inv() smp_icache_all_inv() extern void smp_icache_page_inv(struct page *page); +extern void smp_icache_all_inv(void); #endif /* CONFIG_SMP */ /* diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c index 040ca201b692..65599252f3d4 100644 --- a/arch/openrisc/kernel/smp.c +++ b/arch/openrisc/kernel/smp.c @@ -346,3 +346,24 @@ void smp_icache_page_inv(struct page *page) on_each_cpu(ipi_icache_page_inv, page, 1); } EXPORT_SYMBOL(smp_icache_page_inv); + +static void ipi_icache_all_inv(void *arg) +{ + local_icache_all_inv(); +} + +void smp_icache_all_inv(void) +{ + if (num_online_cpus() < 2) { + local_icache_all_inv(); + return; + } + + /* + * Ensure stores complete before we request remote icaches + * to invalidate. + */ + mb(); + + on_each_cpu(ipi_icache_all_inv, NULL, 1); +} diff --git a/arch/openrisc/mm/cache.c b/arch/openrisc/mm/cache.c index f33df46dae4e..2667d90691b5 100644 --- a/arch/openrisc/mm/cache.c +++ b/arch/openrisc/mm/cache.c @@ -63,6 +63,22 @@ void local_icache_page_inv(struct page *page) } EXPORT_SYMBOL(local_icache_page_inv); +void local_icache_all_inv(void) +{ + if (cpu_cache_is_present(SPR_UPR_ICP)) { + unsigned long iccfgr = mfspr(SPR_ICCFGR); + unsigned long sets = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3); + unsigned long block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7); + unsigned long paddr = 0; + unsigned long end = sets * block_size; + + while (paddr < end) { + mtspr(SPR_ICBIR, paddr); + paddr += block_size; + } + } +} + void local_dcache_range_flush(unsigned long start, unsigned long end) { cache_loop(start, end, SPR_DCBFR, SPR_UPR_DCP); From aca063c9024522e4e5b9a9d1927433f6a01785a3 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 17:28:31 +0100 Subject: [PATCH 1611/5214] openrisc: Fix jump_label smp syncing The original commit 8c30b0018f9d ("openrisc: Add jump label support") copies from arm64 and does not properly consider how icache invalidation on remote cores works in OpenRISC. On OpenRISC remote icaches need to be invalidated otherwise static key's may remain state after updating. Fix SMP cache syncing by: 1. Properly invalidate remote core icaches on SMP systems by using icache_all_inv. The old code uses kick_all_cpus_sync() which runs a no-op IPI function call on remote CPU's which does execute a lot of code and flushes many cache lines in the process, but does not flush all and it's not correct on OpenRISC. 2. For architectures that do not have WRITETHROUGH caches be sure to flush the dcache after patching. To test this I first reproduced the issue using a custom test module [0]. The test confirmed that some icache lines maintained stale static_key code sequences after calling static_branch_enable(). After this patch there are no longer jump_label coherency issues. [0] https://github.com/stffrdhrn/or1k-utils/tree/master/tests/smp_static_key_test Cc: stable@vger.kernel.org # depends on openrisc: Add icache_all_inv Fixes: 8c30b0018f9d ("openrisc: Add jump label support") Signed-off-by: Stafford Horne --- arch/openrisc/kernel/jump_label.c | 2 +- arch/openrisc/kernel/patching.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c index ab7137c23b46..9cb63f2d2e2b 100644 --- a/arch/openrisc/kernel/jump_label.c +++ b/arch/openrisc/kernel/jump_label.c @@ -47,5 +47,5 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry, void arch_jump_label_transform_apply(void) { - kick_all_cpus_sync(); + icache_all_inv(); } diff --git a/arch/openrisc/kernel/patching.c b/arch/openrisc/kernel/patching.c index d186172beb33..5db027b78bc4 100644 --- a/arch/openrisc/kernel/patching.c +++ b/arch/openrisc/kernel/patching.c @@ -49,6 +49,9 @@ static int __patch_insn_write(void *addr, u32 insn) waddr = patch_map(addr, FIX_TEXT_POKE0); ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE); + if (!IS_ENABLED(CONFIG_DCACHE_WRITETHROUGH)) + local_dcache_range_flush((unsigned long)waddr, + (unsigned long)waddr + OPENRISC_INSN_SIZE); local_icache_range_inv((unsigned long)waddr, (unsigned long)waddr + OPENRISC_INSN_SIZE); From c1bf20b2f449e914b0a6a607a6ee717f902850a7 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Tue, 12 May 2026 18:55:43 +0530 Subject: [PATCH 1612/5214] dt-bindings: pinctrl: qcom: Document Shikra Top Level Mode Multiplexer Add a DeviceTree binding to describe the TLMM block on Qualcomm's Shikra SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Komal Bajaj Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- .../bindings/pinctrl/qcom,shikra-tlmm.yaml | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,shikra-tlmm.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,shikra-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,shikra-tlmm.yaml new file mode 100644 index 000000000000..411c402f9044 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,shikra-tlmm.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,shikra-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. Shikra TLMM block + +maintainers: + - Komal Bajaj + +description: | + Top Level Mode Multiplexer pin controller in Qualcomm Shikra SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,shikra-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 83 + + gpio-line-names: + maxItems: 166 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-shikra-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-shikra-tlmm-state" + additionalProperties: false + +$defs: + qcom-shikra-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-5][0-9]|16[0-5])$" + - enum: [ sdc1_rclk, sdc1_clk, sdc1_cmd, sdc1_data, + sdc2_clk, sdc2_cmd, sdc2_data ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ gpio, agera_pll, atest_bbrx, atest_char, atest_gpsadc, + atest_tsens, atest_usb, cam_mclk, cci_async, cci_i2c0, + cci_i2c1, cci_timer, char_exec, cri_trng, dac_calib, + dbg_out_clk, ddr_bist, ddr_pxi, dmic, emac_dll, emac_mcg, + emac_phy, emac0_ptp_aux, emac0_ptp_pps, emac1_ptp_aux, + emac1_ptp_pps, ext_mclk, gcc_gp, gsm0_tx, i2s0, i2s1, + i2s2, i2s3, jitter_bist, m_voc, mdp_vsync_e, mdp_vsync_out0, + mdp_vsync_out1, mdp_vsync_p, mdp_vsync_s, mpm_pwr, mss_lte, + nav_gpio, pa_indicator_or, pbs_in, pbs_out, pcie0_clk_req_n, + phase_flag, pll, prng_rosc, pwm, qdss_cti, qup0_se0, + qup0_se1, qup0_se1_01, qup0_se1_23, qup0_se2, qup0_se3_01, + qup0_se3_23, qup0_se4_01, qup0_se4_23, qup0_se5, qup0_se6, + qup0_se7_01, qup0_se7_23, qup0_se8, qup0_se9, qup0_se9_01, + qup0_se9_23, rgmii, sd_write_protect, sdc_cdc, sdc_tb_trig, + ssbi_wtr, swr0_rx, swr0_tx, tgu_ch_trigout, tsc_async, + tsense_pwm, uim1, uim2, unused_adsp, unused_gsm1, usb0_phy_ps, + vfr, vsense_trigger_mirnat, wlan ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@500000 { + compatible = "qcom,shikra-tlmm"; + reg = <0x00500000 0x800000>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-ranges = <&tlmm 0 0 166>; + + qup-uart0-default-state { + pins = "gpio0", "gpio1"; + function = "qup0_se1"; + drive-strength = <2>; + bias-disable; + }; + }; +... From 9db68ec534c5fdcfeb2e330b8eb6f555b3af78ed Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Tue, 12 May 2026 18:55:44 +0530 Subject: [PATCH 1613/5214] pinctrl: qcom: Add Shikra pinctrl driver Add pinctrl driver for TLMM block found in Shikra SoC. Signed-off-by: Komal Bajaj Reviewed-by: Konrad Dybcio Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig.msm | 11 + drivers/pinctrl/qcom/Makefile | 1 + drivers/pinctrl/qcom/pinctrl-shikra.c | 1253 +++++++++++++++++++++++++ 3 files changed, 1265 insertions(+) create mode 100644 drivers/pinctrl/qcom/pinctrl-shikra.c diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 65bc7424065a..9409e678ec6d 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -432,6 +432,17 @@ config PINCTRL_SDX75 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SDX75 platform. +config PINCTRL_SHIKRA + tristate "Qualcomm Shikra pin controller driver" + depends on ARM64 || COMPILE_TEST + default ARCH_QCOM + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) + found on the Qualcomm Technologies Inc Shikra platform. + Say Y here to compile statically, or M here to compile it as a module. + If unsure, say N. + config PINCTRL_SM4450 tristate "Qualcomm SM4450 pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index a1e9e2e292a0..93cc4e7965ca 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_SDX55) += pinctrl-sdx55.o obj-$(CONFIG_PINCTRL_SDX65) += pinctrl-sdx65.o obj-$(CONFIG_PINCTRL_SDX75) += pinctrl-sdx75.o +obj-$(CONFIG_PINCTRL_SHIKRA) += pinctrl-shikra.o obj-$(CONFIG_PINCTRL_SM4250_LPASS_LPI) += pinctrl-sm4250-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM4450) += pinctrl-sm4450.o obj-$(CONFIG_PINCTRL_SM6115) += pinctrl-sm6115.o diff --git a/drivers/pinctrl/qcom/pinctrl-shikra.c b/drivers/pinctrl/qcom/pinctrl-shikra.c new file mode 100644 index 000000000000..0fc98369948c --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-shikra.c @@ -0,0 +1,1253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 \ + }, \ + .nfuncs = 12, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_wakeup_enable_bit = 7, \ + .intr_wakeup_present_bit = 6, \ + .intr_target_bit = 8, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc shikra_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "SDC1_RCLK"), + PINCTRL_PIN(167, "SDC1_CLK"), + PINCTRL_PIN(168, "SDC1_CMD"), + PINCTRL_PIN(169, "SDC1_DATA"), + PINCTRL_PIN(170, "SDC2_CLK"), + PINCTRL_PIN(171, "SDC2_CMD"), + PINCTRL_PIN(172, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); + +static const unsigned int sdc1_rclk_pins[] = { 166 }; +static const unsigned int sdc1_clk_pins[] = { 167 }; +static const unsigned int sdc1_cmd_pins[] = { 168 }; +static const unsigned int sdc1_data_pins[] = { 169 }; +static const unsigned int sdc2_clk_pins[] = { 170 }; +static const unsigned int sdc2_cmd_pins[] = { 171 }; +static const unsigned int sdc2_data_pins[] = { 172 }; + +enum shikra_functions { + msm_mux_gpio, + msm_mux_agera_pll, + msm_mux_atest_bbrx, + msm_mux_atest_char, + msm_mux_atest_gpsadc, + msm_mux_atest_tsens, + msm_mux_atest_usb, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c0, + msm_mux_cci_i2c1, + msm_mux_cci_timer, + msm_mux_char_exec, + msm_mux_cri_trng, + msm_mux_dac_calib, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist, + msm_mux_ddr_pxi, + msm_mux_dmic, + msm_mux_emac_dll, + msm_mux_emac_mcg, + msm_mux_emac_phy, + msm_mux_emac0_ptp_aux, + msm_mux_emac0_ptp_pps, + msm_mux_emac1_ptp_aux, + msm_mux_emac1_ptp_pps, + msm_mux_ext_mclk, + msm_mux_gcc_gp, + msm_mux_gsm0_tx, + msm_mux_i2s0, + msm_mux_i2s1, + msm_mux_i2s2, + msm_mux_i2s3, + msm_mux_jitter_bist, + msm_mux_m_voc, + msm_mux_mdp_vsync_e, + msm_mux_mdp_vsync_out0, + msm_mux_mdp_vsync_out1, + msm_mux_mdp_vsync_p, + msm_mux_mdp_vsync_s, + msm_mux_mpm_pwr, + msm_mux_mss_lte, + msm_mux_nav_gpio, + msm_mux_pa_indicator_or, + msm_mux_pbs_in, + msm_mux_pbs_out, + msm_mux_pcie0_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll, + msm_mux_prng_rosc, + msm_mux_pwm, + msm_mux_qdss_cti, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se1_01, + msm_mux_qup0_se1_23, + msm_mux_qup0_se2, + msm_mux_qup0_se3_01, + msm_mux_qup0_se3_23, + msm_mux_qup0_se4_01, + msm_mux_qup0_se4_23, + msm_mux_qup0_se5, + msm_mux_qup0_se6, + msm_mux_qup0_se7_01, + msm_mux_qup0_se7_23, + msm_mux_qup0_se8, + msm_mux_qup0_se9, + msm_mux_qup0_se9_01, + msm_mux_qup0_se9_23, + msm_mux_rgmii, + msm_mux_sd_write_protect, + msm_mux_sdc_cdc, + msm_mux_sdc_tb_trig, + msm_mux_ssbi_wtr, + msm_mux_swr0_rx, + msm_mux_swr0_tx, + msm_mux_tgu_ch_trigout, + msm_mux_tsc_async, + msm_mux_tsense_pwm, + msm_mux_uim1, + msm_mux_uim2, + msm_mux_unused_adsp, + msm_mux_unused_gsm1, + msm_mux_usb0_phy_ps, + msm_mux_vfr, + msm_mux_vsense_trigger_mirnat, + msm_mux_wlan, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", + "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", + "gpio126", "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", + "gpio132", "gpio133", "gpio134", "gpio135", "gpio136", "gpio137", + "gpio138", "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", + "gpio144", "gpio145", "gpio146", "gpio147", "gpio148", "gpio149", + "gpio150", "gpio151", "gpio152", "gpio153", "gpio154", "gpio155", + "gpio156", "gpio157", "gpio158", "gpio159", "gpio160", "gpio161", + "gpio162", "gpio163", "gpio164", "gpio165", +}; + +static const char *const agera_pll_groups[] = { + "gpio22", "gpio23", +}; + +static const char *const atest_bbrx_groups[] = { + "gpio58", "gpio59", +}; + +static const char *const atest_char_groups[] = { + "gpio56", "gpio57", "gpio54", "gpio55", "gpio62", +}; + +static const char *const atest_gpsadc_groups[] = { + "gpio60", "gpio96", +}; + +static const char *const atest_tsens_groups[] = { + "gpio1", "gpio2", +}; + +static const char *const atest_usb_groups[] = { + "gpio53", "gpio58", "gpio59", "gpio60", "gpio61", "gpio96", + "gpio98", "gpio99", "gpio100", "gpio101", +}; + +static const char *const cam_mclk_groups[] = { + "gpio34", "gpio35", "gpio96", "gpio98", +}; + +static const char *const cci_async_groups[] = { + "gpio39", +}; + +static const char *const cci_i2c0_groups[] = { + "gpio36", "gpio37", +}; + +static const char *const cci_i2c1_groups[] = { + "gpio41", "gpio42", +}; + +static const char *const cci_timer_groups[] = { + "gpio38", "gpio40", "gpio43", "gpio47", +}; + +static const char *const char_exec_groups[] = { + "gpio12", "gpio13", +}; + +static const char *const cri_trng_groups[] = { + "gpio6", "gpio7", "gpio20", +}; + +static const char *const dac_calib_groups[] = { + "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", + "gpio9", "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", + "gpio19", "gpio63", "gpio64", "gpio66", "gpio68", "gpio69", + "gpio70", "gpio88", "gpio89", "gpio90", "gpio97", "gpio116", + "gpio117", "gpio118", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio61", +}; + +static const char *const ddr_bist_groups[] = { + "gpio1", "gpio2", "gpio3", "gpio4", +}; + +static const char *const ddr_pxi_groups[] = { + "gpio98", "gpio99", "gpio100", "gpio101", +}; + +static const char *const dmic_groups[] = { + "gpio96", "gpio97", "gpio98", "gpio99", +}; + +static const char *const emac_dll_groups[] = { + "gpio58", "gpio59", "gpio60", "gpio61", +}; + +static const char *const emac_mcg_groups[] = { + "gpio28", "gpio29", "gpio40", "gpio43", "gpio44", "gpio45", + "gpio46", "gpio47", +}; + +static const char *const emac_phy_groups[] = { + "gpio120", "gpio136", +}; + +static const char *const emac0_ptp_aux_groups[] = { + "gpio60", "gpio63", "gpio69", "gpio85", +}; + +static const char *const emac0_ptp_pps_groups[] = { + "gpio60", "gpio63", "gpio69", "gpio85", +}; + +static const char *const emac1_ptp_aux_groups[] = { + "gpio31", "gpio33", "gpio60", "gpio68", +}; + +static const char *const emac1_ptp_pps_groups[] = { + "gpio31", "gpio33", "gpio60", "gpio68", +}; + +static const char *const ext_mclk_groups[] = { + "gpio103", "gpio104", "gpio110", "gpio114", +}; + +static const char *const gcc_gp_groups[] = { + "gpio45", "gpio53", "gpio61", "gpio88", "gpio89", "gpio110", +}; + +static const char *const gsm0_tx_groups[] = { + "gpio75", +}; + +static const char *const i2s0_groups[] = { + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", +}; + +static const char *const i2s1_groups[] = { + "gpio96", "gpio97", "gpio98", "gpio99", +}; + +static const char *const i2s2_groups[] = { + "gpio100", "gpio101", "gpio102", "gpio103", +}; + +static const char *const i2s3_groups[] = { + "gpio111", "gpio112", "gpio113", "gpio114", +}; + +static const char *const jitter_bist_groups[] = { + "gpio96", "gpio99", +}; + +static const char *const m_voc_groups[] = { + "gpio0", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio94", +}; + +static const char *const mdp_vsync_out0_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync_out1_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync_p_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync_s_groups[] = { + "gpio95", +}; + +static const char *const mpm_pwr_groups[] = { + "gpio1", +}; + +static const char *const mss_lte_groups[] = { + "gpio115", "gpio116", +}; + +static const char *const nav_gpio_groups[] = { + "gpio53", "gpio58", "gpio63", "gpio71", "gpio91", "gpio92", + "gpio95", "gpio100", "gpio101", "gpio104", +}; + +static const char *const pa_indicator_or_groups[] = { + "gpio61", +}; + +static const char *const pbs_in_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", "gpio53", "gpio54", + "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", "gpio60", + "gpio61", "gpio62", "gpio63", "gpio74", +}; + +static const char *const pbs_out_groups[] = { + "gpio22", "gpio23", "gpio24", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio117", +}; + +static const char *const phase_flag_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio11", "gpio16", + "gpio17", "gpio28", "gpio29", "gpio30", "gpio31", "gpio48", + "gpio49", "gpio50", "gpio54", "gpio55", "gpio56", "gpio57", + "gpio62", "gpio63", "gpio64", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio74", "gpio102", +}; + +static const char *const pll_groups[] = { + "gpio14", "gpio22", "gpio43", "gpio44", "gpio74", "gpio76", +}; + +static const char *const prng_rosc_groups[] = { + "gpio27", "gpio28", +}; + +static const char *const pwm_groups[] = { + "gpio32", "gpio40", "gpio45", "gpio53", "gpio54", "gpio55", + "gpio56", "gpio57", "gpio58", "gpio61", "gpio62", "gpio68", + "gpio77", "gpio79", "gpio80", "gpio87", "gpio102" +}; + +static const char *const qdss_cti_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", "gpio94", "gpio95", +}; + +static const char *const qup0_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qup0_se1_groups[] = { + "gpio28", "gpio29", +}; + +static const char *const qup0_se1_01_groups[] = { + "gpio4", "gpio5", +}; + +static const char *const qup0_se1_23_groups[] = { + "gpio4", "gpio5", +}; + +static const char *const qup0_se2_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio30", "gpio31", +}; + +static const char *const qup0_se3_01_groups[] = { + "gpio10", "gpio11", +}; + +static const char *const qup0_se3_23_groups[] = { + "gpio10", "gpio11", +}; + +static const char *const qup0_se4_01_groups[] = { + "gpio12", "gpio13", +}; + +static const char *const qup0_se4_23_groups[] = { + "gpio12", "gpio13", +}; + +static const char *const qup0_se5_groups[] = { + "gpio14", "gpio15", "gpio16", "gpio17", +}; + +static const char *const qup0_se6_groups[] = { + "gpio18", "gpio19", "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const qup0_se7_01_groups[] = { + "gpio20", "gpio21", +}; + +static const char *const qup0_se7_23_groups[] = { + "gpio20", "gpio21", +}; + +static const char *const qup0_se8_groups[] = { + "gpio22", "gpio23", "gpio24", "gpio25", +}; + +static const char *const qup0_se9_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", +}; + +static const char *const qup0_se9_01_groups[] = { + "gpio26", "gpio27", +}; + +static const char *const qup0_se9_23_groups[] = { + "gpio26", "gpio27", +}; + +static const char *const rgmii_groups[] = { + "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", "gpio126", + "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", "gpio132", + "gpio133", "gpio134", "gpio137", "gpio138", "gpio139", "gpio140", + "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", "gpio150", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio109", +}; + +static const char *const sdc_cdc_groups[] = { + "gpio98", "gpio99", "gpio100", "gpio101", +}; + +static const char *const sdc_tb_trig_groups[] = { + "gpio32", "gpio33", +}; + +static const char *const ssbi_wtr_groups[] = { + "gpio68", "gpio69", "gpio70", "gpio71", +}; + +static const char *const swr0_rx_groups[] = { + "gpio107", "gpio108", "gpio109", +}; + +static const char *const swr0_tx_groups[] = { + "gpio105", "gpio106", +}; + +static const char *const tgu_ch_trigout_groups[] = { + "gpio14", "gpio15", "gpio16", "gpio17", +}; + +static const char *const tsc_async_groups[] = { + "gpio45", "gpio46", +}; + +static const char *const tsense_pwm_groups[] = { + "gpio21", +}; + +static const char *const uim1_groups[] = { + "gpio81", "gpio82", "gpio83", "gpio84", +}; + +static const char *const uim2_groups[] = { + "gpio77", "gpio78", "gpio79", "gpio80", +}; + +static const char *const unused_adsp_groups[] = { + "gpio35", +}; + +static const char *const unused_gsm1_groups[] = { + "gpio64", +}; + +static const char *const usb0_phy_ps_groups[] = { + "gpio90", +}; + +static const char *const vfr_groups[] = { + "gpio59", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio58", +}; + +static const char *const wlan_groups[] = { + "gpio14", "gpio15", +}; + +static const struct pinfunction shikra_functions[] = { + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(agera_pll), + MSM_PIN_FUNCTION(atest_bbrx), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_gpsadc), + MSM_PIN_FUNCTION(atest_tsens), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c0), + MSM_PIN_FUNCTION(cci_i2c1), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(char_exec), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dac_calib), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi), + MSM_PIN_FUNCTION(dmic), + MSM_PIN_FUNCTION(emac_dll), + MSM_PIN_FUNCTION(emac_mcg), + MSM_PIN_FUNCTION(emac_phy), + MSM_PIN_FUNCTION(emac0_ptp_aux), + MSM_PIN_FUNCTION(emac0_ptp_pps), + MSM_PIN_FUNCTION(emac1_ptp_aux), + MSM_PIN_FUNCTION(emac1_ptp_pps), + MSM_PIN_FUNCTION(ext_mclk), + MSM_PIN_FUNCTION(gcc_gp), + MSM_PIN_FUNCTION(gsm0_tx), + MSM_PIN_FUNCTION(i2s0), + MSM_PIN_FUNCTION(i2s1), + MSM_PIN_FUNCTION(i2s2), + MSM_PIN_FUNCTION(i2s3), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(m_voc), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(mdp_vsync_out0), + MSM_PIN_FUNCTION(mdp_vsync_out1), + MSM_PIN_FUNCTION(mdp_vsync_p), + MSM_PIN_FUNCTION(mdp_vsync_s), + MSM_PIN_FUNCTION(mpm_pwr), + MSM_PIN_FUNCTION(mss_lte), + MSM_PIN_FUNCTION(nav_gpio), + MSM_PIN_FUNCTION(pa_indicator_or), + MSM_PIN_FUNCTION(pbs_in), + MSM_PIN_FUNCTION(pbs_out), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll), + MSM_PIN_FUNCTION(prng_rosc), + MSM_PIN_FUNCTION(pwm), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se1_01), + MSM_PIN_FUNCTION(qup0_se1_23), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3_01), + MSM_PIN_FUNCTION(qup0_se3_23), + MSM_PIN_FUNCTION(qup0_se4_01), + MSM_PIN_FUNCTION(qup0_se4_23), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup0_se6), + MSM_PIN_FUNCTION(qup0_se7_01), + MSM_PIN_FUNCTION(qup0_se7_23), + MSM_PIN_FUNCTION(qup0_se8), + MSM_PIN_FUNCTION(qup0_se9), + MSM_PIN_FUNCTION(qup0_se9_01), + MSM_PIN_FUNCTION(qup0_se9_23), + MSM_PIN_FUNCTION(rgmii), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sdc_cdc), + MSM_PIN_FUNCTION(sdc_tb_trig), + MSM_PIN_FUNCTION(ssbi_wtr), + MSM_PIN_FUNCTION(swr0_rx), + MSM_PIN_FUNCTION(swr0_tx), + MSM_PIN_FUNCTION(tgu_ch_trigout), + MSM_PIN_FUNCTION(tsc_async), + MSM_PIN_FUNCTION(tsense_pwm), + MSM_PIN_FUNCTION(uim1), + MSM_PIN_FUNCTION(uim2), + MSM_PIN_FUNCTION(unused_adsp), + MSM_PIN_FUNCTION(unused_gsm1), + MSM_PIN_FUNCTION(usb0_phy_ps), + MSM_PIN_FUNCTION(vfr), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wlan), +}; + +static const struct msm_pingroup shikra_groups[] = { + [0] = PINGROUP(0, qup0_se0, m_voc, _, phase_flag, _, _, _, _, _, _, _), + [1] = PINGROUP(1, qup0_se0, mpm_pwr, ddr_bist, _, phase_flag, atest_tsens, _, _, _, _, _), + [2] = PINGROUP(2, qup0_se0, ddr_bist, _, phase_flag, atest_tsens, _, _, _, _, _, _), + [3] = PINGROUP(3, qup0_se0, ddr_bist, _, phase_flag, dac_calib, _, _, _, _, _, _), + [4] = PINGROUP(4, qup0_se1_23, qup0_se1_01, ddr_bist, _, phase_flag, dac_calib, _, _, _, + _, _), + [5] = PINGROUP(5, qup0_se1_23, qup0_se1_01, _, phase_flag, dac_calib, _, _, _, _, _, _), + [6] = PINGROUP(6, qup0_se2, cri_trng, _, phase_flag, dac_calib, _, _, _, _, _, _), + [7] = PINGROUP(7, qup0_se2, cri_trng, _, phase_flag, dac_calib, _, _, _, _, _, _), + [8] = PINGROUP(8, qup0_se2, _, phase_flag, dac_calib, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup0_se2, _, phase_flag, dac_calib, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup0_se3_01, qup0_se3_23, _, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup0_se3_01, qup0_se3_23, _, phase_flag, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup0_se4_01, qup0_se4_23, char_exec, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup0_se4_01, qup0_se4_23, char_exec, _, _, _, _, _, _, _, _), + [14] = PINGROUP(14, qup0_se5, pll, tgu_ch_trigout, dac_calib, wlan, _, _, _, _, _, _), + [15] = PINGROUP(15, qup0_se5, tgu_ch_trigout, _, dac_calib, wlan, _, _, _, _, _, _), + [16] = PINGROUP(16, qup0_se5, tgu_ch_trigout, _, phase_flag, dac_calib, _, _, _, _, _, _), + [17] = PINGROUP(17, qup0_se5, tgu_ch_trigout, _, phase_flag, dac_calib, _, _, _, _, _, _), + [18] = PINGROUP(18, qup0_se6, dac_calib, _, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, qup0_se6, dac_calib, _, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup0_se7_01, qup0_se7_23, cri_trng, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se7_01, qup0_se7_23, tsense_pwm, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, qup0_se8, pll, agera_pll, pbs_out, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup0_se8, agera_pll, pbs_out, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup0_se8, pbs_out, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup0_se8, _, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup0_se9_23, qup0_se9_01, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup0_se9_23, qup0_se9_01, prng_rosc, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, qup0_se1, qup0_se6, emac_mcg, prng_rosc, _, phase_flag, qdss_cti, + _, _, _, _), + [29] = PINGROUP(29, qup0_se1, qup0_se6, emac_mcg, _, phase_flag, qdss_cti, _, _, _, _, _), + [30] = PINGROUP(30, qup0_se2, qup0_se6, _, phase_flag, qdss_cti, _, _, _, _, _, _), + [31] = PINGROUP(31, qup0_se2, qup0_se6, emac1_ptp_aux, emac1_ptp_pps, _, phase_flag, + qdss_cti, _, _, _, _), + [32] = PINGROUP(32, pwm, sdc_tb_trig, _, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, emac1_ptp_aux, emac1_ptp_pps, sdc_tb_trig, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [35] = PINGROUP(35, cam_mclk, unused_adsp, _, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, cci_i2c0, _, _, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, cci_i2c0, _, _, _, _, _, _, _, _, _, _), + [38] = PINGROUP(38, cci_timer, _, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, cci_async, _, _, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, cci_timer, emac_mcg, pwm, _, _, _, _, _, _, _, _), + [41] = PINGROUP(41, cci_i2c1, _, _, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, cci_i2c1, _, _, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, cci_timer, emac_mcg, pll, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, emac_mcg, pll, _, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, tsc_async, emac_mcg, pwm, gcc_gp, _, _, _, _, _, _, _), + [46] = PINGROUP(46, tsc_async, emac_mcg, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, cci_timer, emac_mcg, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, _, qup0_se9, _, _, pbs_in, phase_flag, _, _, _, _, _), + [49] = PINGROUP(49, _, qup0_se9, _, _, pbs_in, phase_flag, _, _, _, _, _), + [50] = PINGROUP(50, _, qup0_se9, _, _, pbs_in, phase_flag, _, _, _, _, _), + [51] = PINGROUP(51, _, qup0_se9, pbs_in, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, _, _, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, _, nav_gpio, gcc_gp, pwm, _, pbs_in, atest_usb, _, _, _, _), + [54] = PINGROUP(54, _, pwm, _, pbs_in, phase_flag, atest_char, _, _, _, _, _), + [55] = PINGROUP(55, _, pwm, _, pbs_in, phase_flag, atest_char, _, _, _, _, _), + [56] = PINGROUP(56, _, pwm, _, pbs_in, phase_flag, atest_char, _, _, _, _, _), + [57] = PINGROUP(57, _, pwm, _, pbs_in, phase_flag, atest_char, _, _, _, _, _), + [58] = PINGROUP(58, _, nav_gpio, pwm, _, pbs_in, atest_bbrx, atest_usb, + vsense_trigger_mirnat, emac_dll, _, _), + [59] = PINGROUP(59, _, vfr, _, pbs_in, atest_bbrx, atest_usb, emac_dll, _, _, _, _), + [60] = PINGROUP(60, _, emac1_ptp_aux, emac1_ptp_pps, emac0_ptp_aux, emac0_ptp_pps, _, + pbs_in, atest_gpsadc, atest_usb, emac_dll, _), + [61] = PINGROUP(61, _, pwm, gcc_gp, pa_indicator_or, dbg_out_clk, pbs_in, atest_usb, + emac_dll, _, _, _), + [62] = PINGROUP(62, _, pwm, _, pbs_in, phase_flag, atest_char, _, _, _, _, _), + [63] = PINGROUP(63, _, nav_gpio, emac0_ptp_aux, emac0_ptp_pps, _, pbs_in, phase_flag, + dac_calib, _, _, _), + [64] = PINGROUP(64, _, unused_gsm1, dac_calib, _, _, _, _, _, _, _, _), + [65] = PINGROUP(65, _, _, _, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, _, dac_calib, _, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, _, _, _, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, _, ssbi_wtr, emac1_ptp_aux, emac1_ptp_pps, pwm, dac_calib, _, _, _, + _, _), + [69] = PINGROUP(69, _, ssbi_wtr, emac0_ptp_aux, emac0_ptp_pps, _, phase_flag, dac_calib, + _, _, _, _), + [70] = PINGROUP(70, _, ssbi_wtr, _, phase_flag, dac_calib, _, _, _, _, _, _), + [71] = PINGROUP(71, _, ssbi_wtr, nav_gpio, _, phase_flag, _, _, _, _, _, _), + [72] = PINGROUP(72, _, _, phase_flag, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, _, _, _, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, pll, _, pbs_in, phase_flag, _, _, _, _, _, _, _), + [75] = PINGROUP(75, gsm0_tx, _, _, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, pll, _, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, uim2, pwm, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, uim2, _, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, uim2, pwm, _, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, uim2, pwm, _, _, _, _, _, _, _, _, _), + [81] = PINGROUP(81, uim1, _, _, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, uim1, _, _, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, uim1, _, _, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, uim1, _, _, _, _, _, _, _, _, _, _), + [85] = PINGROUP(85, emac0_ptp_aux, emac0_ptp_pps, _, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, mdp_vsync_p, mdp_vsync_out0, mdp_vsync_out1, _, _, _, _, _, _, _, _), + [87] = PINGROUP(87, _, pwm, _, _, _, _, _, _, _, _, _), + [88] = PINGROUP(88, gcc_gp, _, dac_calib, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, gcc_gp, _, dac_calib, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, usb0_phy_ps, _, dac_calib, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, nav_gpio, _, _, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, nav_gpio, _, _, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, _, _, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, mdp_vsync_e, qdss_cti, qdss_cti, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, nav_gpio, mdp_vsync_s, qdss_cti, qdss_cti, _, _, _, _, _, _, _), + [96] = PINGROUP(96, dmic, cam_mclk, i2s1, jitter_bist, atest_gpsadc, atest_usb, _, _, _, + _, _), + [97] = PINGROUP(97, dmic, i2s1, dac_calib, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, dmic, cam_mclk, i2s1, _, sdc_cdc, atest_usb, ddr_pxi, _, _, _, _), + [99] = PINGROUP(99, dmic, i2s1, jitter_bist, sdc_cdc, atest_usb, ddr_pxi, _, _, _, _, _), + [100] = PINGROUP(100, i2s2, nav_gpio, _, sdc_cdc, atest_usb, ddr_pxi, _, _, _, _, _), + [101] = PINGROUP(101, i2s2, nav_gpio, _, sdc_cdc, atest_usb, ddr_pxi, _, _, _, _, _), + [102] = PINGROUP(102, i2s2, pwm, _, phase_flag, _, _, _, _, _, _, _), + [103] = PINGROUP(103, ext_mclk, i2s2, _, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, ext_mclk, nav_gpio, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, swr0_tx, i2s0, _, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, swr0_tx, i2s0, _, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, swr0_rx, i2s0, _, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, swr0_rx, i2s0, _, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, swr0_rx, i2s0, sd_write_protect, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, ext_mclk, i2s0, _, gcc_gp, _, _, _, _, _, _, _), + [111] = PINGROUP(111, i2s3, _, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, i2s3, _, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, i2s3, _, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, ext_mclk, i2s3, _, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, mss_lte, _, _, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, mss_lte, _, dac_calib, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, pcie0_clk_req_n, _, dac_calib, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, _, dac_calib, _, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, _, _, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, emac_phy, _, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, rgmii, _, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, rgmii, _, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, rgmii, _, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, rgmii, _, _, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, rgmii, _, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, rgmii, _, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, rgmii, _, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, rgmii, _, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, rgmii, _, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, rgmii, _, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, rgmii, _, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, rgmii, _, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, rgmii, _, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, rgmii, _, _, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, _, _, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, emac_phy, _, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, rgmii, _, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, rgmii, _, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, rgmii, _, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, rgmii, _, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, rgmii, _, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, rgmii, _, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, rgmii, _, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, rgmii, _, _, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, rgmii, _, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, rgmii, _, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, rgmii, _, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, rgmii, _, _, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, rgmii, _, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, rgmii, _, _, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, _, _, _, _, _, _, _, _, _, _, _), + [152] = PINGROUP(152, _, _, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, _, _, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, _, _, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, _, _, _, _, _, _, _, _, _, _, _), + [156] = PINGROUP(156, _, _, _, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, _, _, _, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, _, _, _, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, _, _, _, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, _, _, _, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, _, _, _, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, _, _, _, _, _, _, _, _, _, _, _), + [163] = PINGROUP(163, _, _, _, _, _, _, _, _, _, _, _), + [164] = PINGROUP(164, _, _, _, _, _, _, _, _, _, _, _), + [165] = PINGROUP(165, _, _, _, _, _, _, _, _, _, _, _), + [166] = SDC_QDSD_PINGROUP(sdc1_rclk, 0xac004, 0, 0), + [167] = SDC_QDSD_PINGROUP(sdc1_clk, 0xac000, 13, 6), + [168] = SDC_QDSD_PINGROUP(sdc1_cmd, 0xac000, 11, 3), + [169] = SDC_QDSD_PINGROUP(sdc1_data, 0xac000, 9, 0), + [170] = SDC_QDSD_PINGROUP(sdc2_clk, 0xaa000, 14, 6), + [171] = SDC_QDSD_PINGROUP(sdc2_cmd, 0xaa000, 11, 3), + [172] = SDC_QDSD_PINGROUP(sdc2_data, 0xaa000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map shikra_mpm_map[] = { + {1, 9 }, {2, 31 }, {5, 49 }, {6, 53 }, {9, 72 }, {10, 10 }, + {12, 22 }, {14, 26 }, {17, 29 }, {18, 24 }, {20, 32 }, {22, 33 }, + {25, 34 }, {27, 35 }, {28, 36 }, {29, 37 }, {30, 38 }, {31, 39 }, + {32, 40 }, {33, 41 }, {38, 42 }, {40, 43 }, {43, 44 }, {44, 45 }, + {45, 46 }, {46, 47 }, {47, 48 }, {48, 60 }, {50, 50 }, {51, 51 }, + {52, 61 }, {53, 62 }, {57, 52 }, {58, 63 }, {60, 54 }, {63, 64 }, + {73, 55 }, {74, 56 }, {75, 57 }, {77, 3 }, {80, 4 }, {84, 5 }, + {85, 67 }, {86, 69 }, {88, 70 }, {89, 71 }, {90, 73 }, {91, 74 }, + {92, 75 }, {93, 76 }, {94, 77 }, {95, 78 }, {97, 79 }, {99, 80 }, + {100, 11 }, {101, 13 }, {102, 14 }, {103, 15 }, {106, 16 }, {108, 17 }, + {112, 18 }, {116, 19 }, {117, 20 }, {119, 21 }, {120, 23 }, {136, 25 }, + {159, 27 }, {161, 28 }, +}; + +static const struct msm_pinctrl_soc_data shikra_tlmm = { + .pins = shikra_pins, + .npins = ARRAY_SIZE(shikra_pins), + .functions = shikra_functions, + .nfunctions = ARRAY_SIZE(shikra_functions), + .groups = shikra_groups, + .ngroups = ARRAY_SIZE(shikra_groups), + .ngpios = 166, + .wakeirq_map = shikra_mpm_map, + .nwakeirq_map = ARRAY_SIZE(shikra_mpm_map), +}; + +static int shikra_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &shikra_tlmm); +} + +static const struct of_device_id shikra_tlmm_of_match[] = { + { .compatible = "qcom,shikra-tlmm", .data = &shikra_tlmm }, + {}, +}; +MODULE_DEVICE_TABLE(of, shikra_tlmm_of_match); + +static struct platform_driver shikra_tlmm_driver = { + .driver = { + .name = "shikra-tlmm", + .of_match_table = shikra_tlmm_of_match, + }, + .probe = shikra_tlmm_probe, +}; + +static int __init shikra_tlmm_init(void) +{ + return platform_driver_register(&shikra_tlmm_driver); +} +arch_initcall(shikra_tlmm_init); + +static void __exit shikra_tlmm_exit(void) +{ + platform_driver_unregister(&shikra_tlmm_driver); +} +module_exit(shikra_tlmm_exit); + +MODULE_DESCRIPTION("QTI Shikra TLMM driver"); +MODULE_LICENSE("GPL"); From 30a0d3eda1baebeaed58690f16a31b4ddca36314 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 7 May 2026 22:34:07 -0700 Subject: [PATCH 1614/5214] dt-bindings: pinctrl: qcom,pmic-gpio: Document PM8010 GPIO support Update the binding documentation to include the compatible string for PM8010 PMIC which has 2 GPIO modules. Signed-off-by: Fenglin Wu Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml index 386c31e9c52b..b8109e6c2a10 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml @@ -30,6 +30,7 @@ properties: - qcom,pm7550-gpio - qcom,pm7550ba-gpio - qcom,pm8005-gpio + - qcom,pm8010-gpio - qcom,pm8018-gpio - qcom,pm8019-gpio - qcom,pm8038-gpio @@ -134,6 +135,7 @@ allOf: compatible: contains: enum: + - qcom,pm8010-gpio - qcom,pmi8950-gpio - qcom,pmr735d-gpio then: @@ -465,6 +467,7 @@ $defs: - gpio1-gpio10 for pm7325 - gpio1-gpio8 for pm7550ba - gpio1-gpio4 for pm8005 + - gpio1-gpio2 for pm8010 - gpio1-gpio6 for pm8018 - gpio1-gpio12 for pm8038 - gpio1-gpio40 for pm8058 From bdb939247b269f2018412e9089d6aed3c7997d4f Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 7 May 2026 22:34:08 -0700 Subject: [PATCH 1615/5214] pinctrl: qcom: spmi-gpio: Add PM8010 GPIO support Add PM8010 GPIO support with its compatible string and match data. Signed-off-by: Fenglin Wu Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index d02d42513ebb..cdd61dae74cf 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -1229,6 +1229,7 @@ static const struct of_device_id pmic_gpio_of_match[] = { { .compatible = "qcom,pm7550-gpio", .data = (void *) 12 }, { .compatible = "qcom,pm7550ba-gpio", .data = (void *) 8}, { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, + { .compatible = "qcom,pm8010-gpio", .data = (void *) 2 }, { .compatible = "qcom,pm8019-gpio", .data = (void *) 6 }, /* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */ { .compatible = "qcom,pm8150-gpio", .data = (void *) 10 }, From ea024873fe4316e4dc1d18657b8059b73f72b5d0 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 8 Apr 2026 01:07:01 -0400 Subject: [PATCH 1616/5214] pinctrl: Add OF dependency for PINCTRL_GENERIC_MUX Add an explicit OF dependency for PINCTRL_GENERIC_MUX to ensure the generic mux support is only enabled when device tree is available. Also fix the stub implementation of pinctrl_generic_to_map() by correcting its last argument to match the non-stub prototype. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604072013.aI84l57L-lkp@intel.com/ Signed-off-by: Frank Li Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/pinconf.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 31d698fbaa01..f4ffe1f3b720 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -275,6 +275,7 @@ config PINCTRL_GEMINI config PINCTRL_GENERIC_MUX tristate "Generic Pinctrl driver by using multiplexer" depends on MULTIPLEXER + depends on OF select PINMUX select GENERIC_PINCTRL help diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index fa8fb0d290d1..9711d16c38b6 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -195,7 +195,7 @@ pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, unsigned int *num_maps, unsigned int *num_reserved_maps, const char **group_name, unsigned int ngroups, const char **functions, unsigned int *pins, - void *function_data) + unsigned int npins) { return -ENOTSUPP; } From f2c49ae0ce2ea518c140cbd8fca352c5a6f43bd0 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Fri, 15 May 2026 14:21:51 +0300 Subject: [PATCH 1617/5214] dt-bindings: pinctrl: qcom,eliza-tlmm: Merge QUP1_SE4 lane functions QUP1_SE4 uses GPIO36 and GPIO37 for two selectable lane pairs. The previous split added one function name per lane. Since these are usually configured in pairs in devicetree, it makes more sense to have them grouped. So replace the per-lane qup1_se4_l[0-3] names with names for the two selectable pairs, qup1_se4_01 and qup1_se4_23. Fixes: 1bd5c56253c5 ("dt-bindings: pinctrl: qcom,eliza-tlmm: Split QUP1_SE4 lanes") Suggested-by: Bjorn Andersson Acked-by: Krzysztof Kozlowski Signed-off-by: Abel Vesa Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml index fa0177529277..aaaeca8e7bb7 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml @@ -88,8 +88,8 @@ $defs: qlink_little_request, qlink_wmss, qspi0, qspi_clk, qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se2_l2_mira, qup1_se2_l2_mirb, qup1_se2_l3_mira, qup1_se2_l3_mirb, - qup1_se3, qup1_se4_l0, qup1_se4_l1, qup1_se4_l2, - qup1_se4_l3, qup1_se5, qup1_se6, qup1_se6_l1_mira, + qup1_se3, qup1_se4_01, qup1_se4_23, + qup1_se5, qup1_se6, qup1_se6_l1_mira, qup1_se6_l1_mirb, qup1_se6_l3_mira, qup1_se6_l3_mirb, qup1_se7, qup1_se7_l0_mira, qup1_se7_l0_mirb, qup1_se7_l1_mira, qup1_se7_l1_mirb, qup2_se0, qup2_se1, From 2e4fe82344727b558ce870b7a023785b3f8edf33 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Fri, 15 May 2026 14:21:52 +0300 Subject: [PATCH 1618/5214] pinctrl: qcom: eliza: Merge QUP1_SE4 lanes in groups QUP1_SE4 uses GPIO36 and GPIO37 for two selectable lane pairs. The current driver exposes lanes 0, 1, 2 and 3 as independent functions. However, since these are usually configured in pairs in devicetree, it makes more sense to merge them into groups. So merge the per-lane functions into qup1_se4_01 and qup1_se4_23, and list both GPIO36 and GPIO37 in each function group. Fixes: 4f5b1f4e770b ("pinctrl: qcom: eliza: Split QUP1_SE4 lanes") Suggested-by: Bjorn Andersson Signed-off-by: Abel Vesa Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-eliza.c | 32 +++++++++------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index bcd69c79a628..4a2ef7b2a28c 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -544,10 +544,8 @@ enum eliza_functions { msm_mux_qup1_se2_l3_mira, msm_mux_qup1_se2_l3_mirb, msm_mux_qup1_se3, - msm_mux_qup1_se4_l0, - msm_mux_qup1_se4_l1, - msm_mux_qup1_se4_l2, - msm_mux_qup1_se4_l3, + msm_mux_qup1_se4_01, + msm_mux_qup1_se4_23, msm_mux_qup1_se5, msm_mux_qup1_se6, msm_mux_qup1_se6_l1_mira, @@ -996,20 +994,12 @@ static const char *const qup1_se3_groups[] = { "gpio44", "gpio45", "gpio46", "gpio47", }; -static const char *const qup1_se4_l0_groups[] = { - "gpio36", +static const char *const qup1_se4_01_groups[] = { + "gpio36", "gpio37", }; -static const char *const qup1_se4_l1_groups[] = { - "gpio37", -}; - -static const char *const qup1_se4_l2_groups[] = { - "gpio37", -}; - -static const char *const qup1_se4_l3_groups[] = { - "gpio36", +static const char *const qup1_se4_23_groups[] = { + "gpio36", "gpio37", }; static const char *const qup1_se5_groups[] = { @@ -1312,10 +1302,8 @@ static const struct pinfunction eliza_functions[] = { MSM_PIN_FUNCTION(qup1_se2_l3_mira), MSM_PIN_FUNCTION(qup1_se2_l3_mirb), MSM_PIN_FUNCTION(qup1_se3), - MSM_PIN_FUNCTION(qup1_se4_l0), - MSM_PIN_FUNCTION(qup1_se4_l1), - MSM_PIN_FUNCTION(qup1_se4_l2), - MSM_PIN_FUNCTION(qup1_se4_l3), + MSM_PIN_FUNCTION(qup1_se4_01), + MSM_PIN_FUNCTION(qup1_se4_23), MSM_PIN_FUNCTION(qup1_se5), MSM_PIN_FUNCTION(qup1_se6), MSM_PIN_FUNCTION(qup1_se6_l1_mira), @@ -1412,8 +1400,8 @@ static const struct msm_pingroup eliza_groups[] = { [33] = PINGROUP(33, qup1_se1, ibi_i3c, host2wlan_sol, gcc_gp3, _, _, _, _, _, _, _), [34] = PINGROUP(34, qup1_se1, qup1_se5, tb_trig_sdc1, ddr_bist_start, qdss_gpio_tracedata, _, _, _, _, _, _), [35] = PINGROUP(35, qup1_se1, qup1_se5, tb_trig_sdc2, gcc_gp2, qdss_gpio_tracedata, _, _, _, _, _, _), - [36] = PINGROUP(36, qup1_se4_l0, qup1_se4_l3, ibi_i3c, _, _, _, _, _, _, _, _), - [37] = PINGROUP(37, qup1_se4_l1, qup1_se4_l2, ibi_i3c, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se4_01, qup1_se4_23, ibi_i3c, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se4_01, qup1_se4_23, ibi_i3c, _, _, _, _, _, _, _, _), [38] = PINGROUP(38, _, _, _, _, _, _, _, _, _, _, _), [39] = PINGROUP(39, _, _, _, _, _, _, _, _, _, _, _), [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6_l3_mira, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), From ada02d0894fd0ae65bda4d5fbf88dad0a6cb0788 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 13:41:20 +0530 Subject: [PATCH 1619/5214] PCI: Add pci_suspend_retains_context() to check if device state is preserved during suspend Currently, PCI endpoint drivers (e.g. nvme) use pm_suspend_via_firmware() to check whether device state is preserved during system suspend. If firmware will be invoked at the end of suspend, we don't know whether devices will retain their internal state. But device context might be lost due to platform issues as well. Having those checks in endpoint drivers will not scale and will cause a lot of code duplication. Add pci_suspend_retains_context() as a sole point of truth that the endpoint drivers can rely on to check whether they can expect the device context to be retained or not. If pci_suspend_retains_context() returns 'false', drivers need to prepare for context loss by performing actions such as resetting the device, saving the context, shutting it down etc. If it returns 'true', drivers do not need to perform any special action and can leave the device in active state. Right now, this API only incorporates pm_suspend_via_firmware(), but will be extended in future commits. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260519-l1ss-fix-v2-1-b2c3a4bdeb15@oss.qualcomm.com --- drivers/pci/pci.c | 23 +++++++++++++++++++++++ include/linux/pci.h | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..86961551ec51 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "pci.h" DEFINE_MUTEX(pci_slot_mutex); @@ -2899,6 +2900,28 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) pm_runtime_put_sync(parent); } +/** + * pci_suspend_retains_context - Check if the platform can retain the device + * context during system suspend + * @pdev: PCI device to check + * + * Return: true if the platform can guarantee to retain the device context, + * false otherwise. + */ +bool pci_suspend_retains_context(struct pci_dev *pdev) +{ + /* + * If the platform firmware (like ACPI) is involved at the end of + * system suspend, device context may not be retained. + */ + if (pm_suspend_via_firmware()) + return false; + + /* Assume that the context is retained by default */ + return true; +} +EXPORT_SYMBOL_GPL(pci_suspend_retains_context); + static const struct dmi_system_id bridge_d3_blacklist[] = { #ifdef CONFIG_X86 { diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..f60f9e4e7b39 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2086,6 +2086,8 @@ pci_release_mem_regions(struct pci_dev *pdev) pci_select_bars(pdev, IORESOURCE_MEM)); } +bool pci_suspend_retains_context(struct pci_dev *pdev); + #else /* CONFIG_PCI is not enabled */ static inline void pci_set_flags(int flags) { } @@ -2244,6 +2246,11 @@ pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, static inline void pci_free_irq_vectors(struct pci_dev *dev) { } + +static inline bool pci_suspend_retains_context(struct pci_dev *pdev) +{ + return true; +} #endif /* CONFIG_PCI */ /* Include architecture-dependent settings and functions */ From 908ad5bcccdcd8ab5582c0b7fe3091c5146c0b7a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 7 May 2026 11:07:47 +0000 Subject: [PATCH 1620/5214] rust_binder: use lock_vma_under_rcu() in shrinker The shrinker callback currently uses the mmap read trylock operation to attempt to access the vma, but it's generally better to only lock the vma instead of the whole mmap when you can. When lock_vma_under_rcu() fails, there is no reason to lock the mmap lock instead because it's already a trylock operation that is allowed to fail. Signed-off-by: Alice Ryhl Acked-by: Lorenzo Stoakes Link: https://patch.msgid.link/20260507-binder-shrinker-lockvma-v1-1-76e3406bbfa6@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/page_range.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index e54a90e62402..e82a5523804f 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -705,7 +705,7 @@ fn drop(self: Pin<&mut Self>) { let page; let page_index; let mm; - let mmap_read; + let vma_read; let mm_mutex; let vma_addr; let range_ptr; @@ -728,17 +728,18 @@ fn drop(self: Pin<&mut Self>) { None => return LRU_SKIP, }; - mmap_read = match mm.mmap_read_trylock() { - Some(guard) => guard, - None => return LRU_SKIP, - }; - // We can't lock it normally here, since we hold the lru lock. let inner = match range.lock.try_lock() { Some(inner) => inner, None => return LRU_SKIP, }; + vma_addr = inner.vma_addr; + vma_read = match mm.lock_vma_under_rcu(vma_addr) { + Some(guard) => guard, + None => return LRU_SKIP, + }; + // SAFETY: The item is in this lru list, so it's okay to remove it. unsafe { bindings::list_lru_isolate(lru, item) }; @@ -751,7 +752,6 @@ fn drop(self: Pin<&mut Self>) { // `zap_page_range` before we release the mmap lock, so `use_page_slow` will not be able to // insert a new page until after our call to `zap_page_range`. page = unsafe { PageInfo::take_page(info) }; - vma_addr = inner.vma_addr; // From this point on, we don't access this PageInfo or ShrinkablePageRange again, because // they can be freed at any point after we unlock `lru_lock`. This is with the exception of @@ -761,14 +761,12 @@ fn drop(self: Pin<&mut Self>) { // SAFETY: The lru lock is locked when this method is called. unsafe { bindings::spin_unlock(&raw mut (*lru).lock) }; - if let Some(unchecked_vma) = mmap_read.vma_lookup(vma_addr) { - if let Some(vma) = check_vma(unchecked_vma, range_ptr) { - let user_page_addr = vma_addr + (page_index << PAGE_SHIFT); - vma.zap_vma_range(user_page_addr, PAGE_SIZE); - } + if let Some(vma) = check_vma(&vma_read, range_ptr) { + let user_page_addr = vma_addr + (page_index << PAGE_SHIFT); + vma.zap_vma_range(user_page_addr, PAGE_SIZE); } - drop(mmap_read); + drop(vma_read); drop(mm_mutex); drop(mm); drop(page); From 91f818b184b44b105b1c92859ea8d2d6f47912a9 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 19 May 2026 13:56:23 +0800 Subject: [PATCH 1621/5214] binder: Use LIST_HEAD() to initialize on stack list head Use LIST_HEAD to initialize on stack list head. No intentional functional impact. Change generated with below coccinelle script: @@ identifier name; @@ - struct list_head name; + LIST_HEAD(name); ... when != name - INIT_LIST_HEAD(&name); Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260519055623.13142-1-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 9e6194224593..ec0ab4f28530 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3080,12 +3080,10 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; - struct list_head sgc_head; - struct list_head pf_head; + LIST_HEAD(sgc_head); + LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) (uintptr_t)tr->data.ptr.buffer; - INIT_LIST_HEAD(&sgc_head); - INIT_LIST_HEAD(&pf_head); e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; From 1dbd8c44fc8d07a1bbca86a301f5c548a3aed152 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Wed, 20 May 2026 10:40:08 +0800 Subject: [PATCH 1622/5214] rust: miscdevice: use vertical import style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert `use` imports to vertical layout for better readability and maintainability. Signed-off-by: Alvin Sun Reviewed-by: Onur Özkan Link: https://patch.msgid.link/20260520-miscdev-use-format-v2-1-64dc48fc1345@linux.dev Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index c3c2052c9206..05a6b6b9770f 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -11,16 +11,38 @@ use crate::{ bindings, device::Device, - error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, - ffi::{c_int, c_long, c_uint, c_ulong}, - fs::{File, Kiocb}, - iov::{IovIterDest, IovIterSource}, + error::{ + to_result, + Error, + Result, + VTABLE_DEFAULT_ERROR, // + }, + ffi::{ + c_int, + c_long, + c_uint, + c_ulong, // + }, + fs::{ + File, + Kiocb, // + }, + iov::{ + IovIterDest, + IovIterSource, // + }, mm::virt::VmaNew, prelude::*, seq_file::SeqFile, - types::{ForeignOwnable, Opaque}, + types::{ + ForeignOwnable, + Opaque, // + }, +}; +use core::{ + marker::PhantomData, + pin::Pin, // }; -use core::{marker::PhantomData, pin::Pin}; /// Options for creating a misc device. #[derive(Copy, Clone)] From bc58905eb07278914c376af5237a2f39c7ba483b Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Wed, 20 May 2026 10:40:09 +0800 Subject: [PATCH 1623/5214] samples: rust_misc_device: use vertical import style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert `use` imports to vertical layout for better readability and maintainability. Signed-off-by: Alvin Sun Reviewed-by: Onur Özkan Link: https://patch.msgid.link/20260520-miscdev-use-format-v2-2-64dc48fc1345@linux.dev Signed-off-by: Greg Kroah-Hartman --- samples/rust/rust_misc_device.rs | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index 87a1fe63533a..41e26c825060 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -97,14 +97,36 @@ use kernel::{ device::Device, - fs::{File, Kiocb}, - ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, - iov::{IovIterDest, IovIterSource}, - miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, + fs::{ + File, + Kiocb, // + }, + ioctl::{ + _IO, + _IOC_SIZE, + _IOR, + _IOW, // + }, + iov::{ + IovIterDest, + IovIterSource, // + }, + miscdevice::{ + MiscDevice, + MiscDeviceOptions, + MiscDeviceRegistration, // + }, new_mutex, prelude::*, - sync::{aref::ARef, Mutex}, - uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, + sync::{ + aref::ARef, + Mutex, // + }, + uaccess::{ + UserSlice, + UserSliceReader, + UserSliceWriter, // + }, }; const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80); From 7afb5806b8d8220e47330117cddf2df774e8a4a5 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Wed, 20 May 2026 10:40:10 +0800 Subject: [PATCH 1624/5214] rust: miscdevice: remove redundant imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop `Error`, `Result`, `Pin`, `c_int`, `c_long`, `c_uint`, and `c_ulong` imports already provided by `kernel::prelude`. Signed-off-by: Alvin Sun Reviewed-by: Onur Özkan Link: https://patch.msgid.link/20260520-miscdev-use-format-v2-3-64dc48fc1345@linux.dev Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 05a6b6b9770f..83ce50def5ac 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -13,16 +13,8 @@ device::Device, error::{ to_result, - Error, - Result, VTABLE_DEFAULT_ERROR, // }, - ffi::{ - c_int, - c_long, - c_uint, - c_ulong, // - }, fs::{ File, Kiocb, // @@ -39,10 +31,7 @@ Opaque, // }, }; -use core::{ - marker::PhantomData, - pin::Pin, // -}; +use core::marker::PhantomData; /// Options for creating a misc device. #[derive(Copy, Clone)] From 89f34ef577cdee4d41754824f6bc18cc91106f5b Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 12:20:24 +0200 Subject: [PATCH 1625/5214] gpib: Remove useless code This code is a hangover from an earlier approach in the driver where the driver modules were called gpibXX It no longer serves any purpose. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411102025.2000-2-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 5909274ddc12..ccb69d0b28bc 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -544,13 +544,6 @@ int ibopen(struct inode *inode, struct file *filep) priv = filep->private_data; init_gpib_file_private((struct gpib_file_private *)filep->private_data); - if (board->use_count == 0) { - int retval; - - retval = request_module("gpib%i", minor); - if (retval) - dev_dbg(board->gpib_dev, "request module returned %i\n", retval); - } if (board->interface) { if (!try_module_get(board->provider_module)) { dev_err(board->gpib_dev, "try_module_get() failed\n"); From 70ea440324e8c1a10837f721352f5bd469c85007 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 12:20:25 +0200 Subject: [PATCH 1626/5214] gpib: Fix inappropriate ioctl error return The driver was returning -ENOTTY in the case the ioctl command was not recognised. Change it to -EBADRQC. Fixes: 9dde4559e939 ("staging: gpib: Add GPIB common core driver") Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411102025.2000-3-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index ccb69d0b28bc..4c6c43f012c8 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -606,7 +606,7 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) unsigned int minor = iminor(file_inode(filep)); struct gpib_board *board; struct gpib_file_private *file_priv = filep->private_data; - long retval = -ENOTTY; + long retval = -EBADRQC; if (minor >= GPIB_MAX_NUM_BOARDS) { pr_err("gpib: invalid minor number of device file\n"); @@ -799,7 +799,6 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) mutex_unlock(&board->big_gpib_mutex); return write_ioctl(file_priv, board, arg); default: - retval = -ENOTTY; goto done; } From 63625f43d1cd2c966e49da2ad4a28b4771b2df0c Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:06 +0200 Subject: [PATCH 1627/5214] gpib: Add enums for INES 72130 based cards Add Chip type enum Add offset for 72130 bus status register Add bit masks for line state in 72130 bus status register Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-2-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpib/ines/ines.h b/drivers/gpib/ines/ines.h index 6ad57e9a1216..22af59682870 100644 --- a/drivers/gpib/ines/ines.h +++ b/drivers/gpib/ines/ines.h @@ -21,6 +21,7 @@ enum ines_pci_chip { PCI_CHIP_AMCC5920, PCI_CHIP_QUANCOM, PCI_CHIP_QUICKLOGIC5030, + PCI_CHIP_INES_72130, }; struct ines_priv { @@ -162,4 +163,19 @@ enum ines_auxd_bits { INES_T6_50us = 0x10, }; +enum ines72130_regs { + BUS_STATUS_REG = 0xc, +}; + +enum ines_72130_bus_status_bits { + BSR_NRFD_BIT = 0x1, + BSR_NDAC_BIT = 0x2, + BSR_DAV_BIT = 0x4, + BSR_EOI_BIT = 0x8, + BSR_SRQ_BIT = 0x10, + BSR_ATN_BIT = 0x20, + BSR_REN_BIT = 0x40, + BSR_IFC_BIT = 0x80, +}; + #endif // _INES_GPIB_H From 3be376ac8f020f0c9bed8e7aa2cc2757087b4352 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:07 +0200 Subject: [PATCH 1628/5214] gpib: Add ines 72130 line_status routine The 72130 chip has a different bus statue register offset and layout. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-3-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines_gpib.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index c000f647fbb5..dd98cb261a4c 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -57,6 +57,34 @@ static int ines_line_status(const struct gpib_board *board) return status; } +static int ines72130_line_status(const struct gpib_board *board) +{ + int status = VALID_ALL; + int bsr_bits; + struct ines_priv *ines_priv = board->private_data; + + bsr_bits = ines_inb(ines_priv, BUS_STATUS_REG); + + if (bsr_bits & BSR_REN_BIT) + status |= BUS_REN; + if (bsr_bits & BSR_IFC_BIT) + status |= BUS_IFC; + if (bsr_bits & BSR_SRQ_BIT) + status |= BUS_SRQ; + if (bsr_bits & BSR_EOI_BIT) + status |= BUS_EOI; + if (bsr_bits & BSR_NRFD_BIT) + status |= BUS_NRFD; + if (bsr_bits & BSR_NDAC_BIT) + status |= BUS_NDAC; + if (bsr_bits & BSR_DAV_BIT) + status |= BUS_DAV; + if (bsr_bits & BSR_ATN_BIT) + status |= BUS_ATN; + + return status; +} + static void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count) { if (count > 0xffff) { From 1b2bfb7ac25bca1b184a525bdac77ab51fabb295 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:08 +0200 Subject: [PATCH 1629/5214] gpib: Don't use extended registers When the chip type is 72310 then avoid accessing extended registers Apart from the BSR the 72310 supports only the standard NEC u7210 registers. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-4-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines_gpib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index dd98cb261a4c..df299a9d7f4d 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -103,6 +103,9 @@ static int ines_t1_delay(struct gpib_board *board, unsigned int nano_sec) retval = nec7210_t1_delay(board, nec_priv, nano_sec); + if (ines_priv->pci_chip_type == PCI_CHIP_INES_72130) + return retval; + if (nano_sec <= 250) { write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_250ns | INES_INITIAL_T1_2000ns, AUXMR); @@ -322,6 +325,8 @@ static irqreturn_t ines_interrupt(struct gpib_board *board) spin_lock_irqsave(&board->spinlock, flags); nec7210_interrupt(board, nec_priv); + if (priv->pci_chip_type == PCI_CHIP_INES_72130) + goto out; isr3_bits = ines_inb(priv, ISR3); isr4_bits = ines_inb(priv, ISR4); if (isr3_bits & IFC_ACTIVE_BIT) { @@ -339,6 +344,7 @@ static irqreturn_t ines_interrupt(struct gpib_board *board) if (wake) wake_up_interruptible(&board->wait); +out: spin_unlock_irqrestore(&board->spinlock, flags); return IRQ_HANDLED; } From 1d0e413791b89e64b6bea976866863751e46f5ad Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:09 +0200 Subject: [PATCH 1630/5214] gpib: Add ines_pci_xl_interface Add new interface initialisation struct for 72130 based boards. It is basically the same as the ines_pci_interface apart from the name, attach and line_status fields. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-5-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines_gpib.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index df299a9d7f4d..118e6c7b0ff1 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -603,6 +603,34 @@ static struct gpib_interface ines_pci_unaccel_interface = { .return_to_local = ines_return_to_local, }; +static struct gpib_interface ines_pci_xl_interface = { + .name = "ines_pci_xl", + .attach = ines_pci_xl_attach, + .detach = ines_pci_detach, + .read = ines_read, + .write = ines_write, + .command = ines_command, + .take_control = ines_take_control, + .go_to_standby = ines_go_to_standby, + .request_system_control = ines_request_system_control, + .interface_clear = ines_interface_clear, + .remote_enable = ines_remote_enable, + .enable_eos = ines_enable_eos, + .disable_eos = ines_disable_eos, + .parallel_poll = ines_parallel_poll, + .parallel_poll_configure = ines_parallel_poll_configure, + .parallel_poll_response = ines_parallel_poll_response, + .local_parallel_poll_mode = NULL, // XXX + .line_status = ines72130_line_status, + .update_status = ines_update_status, + .primary_address = ines_primary_address, + .secondary_address = ines_secondary_address, + .serial_poll_response = ines_serial_poll_response, + .serial_poll_status = ines_serial_poll_status, + .t1_delay = ines_t1_delay, + .return_to_local = ines_return_to_local, +}; + static struct gpib_interface ines_pci_interface = { .name = "ines_pci", .attach = ines_pci_accel_attach, From b9e4f6e9b256da27744d99b5ad81ae5b75e4861a Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:10 +0200 Subject: [PATCH 1631/5214] gpib: Add attach routine for pci_xl board Add new attach routine for 72130 based boards. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-6-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines_gpib.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index 118e6c7b0ff1..af9693c33b23 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -350,6 +350,7 @@ static irqreturn_t ines_interrupt(struct gpib_board *board) } static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config); +static int ines_pci_xl_attach(struct gpib_board *board, const struct gpib_board_config *config); static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config); static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config); @@ -932,6 +933,24 @@ static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_con return 0; } +static int ines_pci_xl_attach(struct gpib_board *board, const struct gpib_board_config *config) +{ + struct ines_priv *ines_priv; + struct nec7210_priv *nec_priv; + int retval; + + retval = ines_common_pci_attach(board, config); + if (retval < 0) + return retval; + + ines_priv = board->private_data; + ines_priv->pci_chip_type = PCI_CHIP_INES_72130; + nec_priv = &ines_priv->nec7210_priv; + nec7210_board_online(nec_priv, board); + + return 0; +} + static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config) { struct ines_priv *ines_priv; From 4f2d31d243b465a5ee7750356e956bf479e5603f Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 19:25:11 +0200 Subject: [PATCH 1632/5214] gpib; Add register and unregister calls Register the driver for new 72130 based pci_xl board type with the common driver on module initialisation. Unregister the driver on registration error and module exit. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411172511.26546-7-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ines/ines_gpib.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c index af9693c33b23..3562f3184c28 100644 --- a/drivers/gpib/ines/ines_gpib.c +++ b/drivers/gpib/ines/ines_gpib.c @@ -1500,6 +1500,12 @@ static int __init ines_init_module(void) goto err_pci_unaccel; } + ret = gpib_register_driver(&ines_pci_xl_interface, THIS_MODULE); + if (ret) { + pr_err("gpib_register_driver failed: error = %d\n", ret); + goto err_pci_xl; + } + ret = gpib_register_driver(&ines_pci_accel_interface, THIS_MODULE); if (ret) { pr_err("gpib_register_driver failed: error = %d\n", ret); @@ -1554,6 +1560,8 @@ static int __init ines_init_module(void) gpib_unregister_driver(&ines_pci_accel_interface); err_pci_accel: gpib_unregister_driver(&ines_pci_unaccel_interface); +err_pci_xl: + gpib_unregister_driver(&ines_pci_xl_interface); err_pci_unaccel: gpib_unregister_driver(&ines_pci_interface); err_pci: @@ -1566,6 +1574,7 @@ static void __exit ines_exit_module(void) { gpib_unregister_driver(&ines_pci_interface); gpib_unregister_driver(&ines_pci_unaccel_interface); + gpib_unregister_driver(&ines_pci_xl_interface); gpib_unregister_driver(&ines_pci_accel_interface); gpib_unregister_driver(&ines_isa_interface); #ifdef CONFIG_GPIB_PCMCIA From 7c19b47f5a1839817e5ddc5ba589224fcfb6255d Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Wed, 22 Apr 2026 09:48:07 +0200 Subject: [PATCH 1633/5214] gpib: Suppress setting END on error from NI_USB dongle The NI USB adapter sets the END bit in the status word when an error occurs such as a read being interrupted by the setting of ATN. This happens for example when a device clear is received from the controller in charge during a read. The common driver changes the error return to 0 whenever the END bit is set in order to avoid errors such as timeout or interrupt to be reported after the full message has actually been read. The behaviour of the NI USB adapter in setting the END bit on errors was causing actual errors (-EINTR, -ETIMEDOUT) not to be reported. We avoid setting the END bit in the ni_usb_gpib driver when an error is reported in error_code of the status from the adaptor. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260422074807.3194-1-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/ni_usb/ni_usb_gpib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c index 0bbc13ecebf9..28de1d543ad6 100644 --- a/drivers/gpib/ni_usb/ni_usb_gpib.c +++ b/drivers/gpib/ni_usb/ni_usb_gpib.c @@ -720,7 +720,7 @@ static int ni_usb_read(struct gpib_board *board, u8 *buffer, size_t length, break; } ni_usb_soft_update_status(board, status.ibsta, 0); - if (status.ibsta & END) + if ((status.ibsta & END) && (status.error_code == NIUSB_NO_ERROR)) *end = 1; else *end = 0; From 00d56ac0777ca9e2da37b97f017f5da5069d8b4a Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 9 Apr 2026 15:55:22 +0300 Subject: [PATCH 1634/5214] mei: store kind as enum Simplify flows and prepare for future flexibility by storing kind as enum and converting to string only in sysfs. Reviewed-by: Andy Shevchenko Co-developed-by: Reuven Abliyev Signed-off-by: Reuven Abliyev Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260409125524.111530-2-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 61 ++++++++++++++++++++++++++------- drivers/misc/mei/hw-me.h | 4 +-- drivers/misc/mei/main.c | 18 ++++++---- drivers/misc/mei/mei_dev.h | 27 ++++++++++++--- drivers/misc/mei/pci-txe.c | 1 + drivers/misc/mei/platform-vsc.c | 2 +- 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index e286763b52ec..28fbd432d80d 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1584,18 +1584,41 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; } -#define MEI_CFG_KIND_ITOUCH \ - .kind = "itouch" - -#define MEI_CFG_TYPE_GSC \ - .kind = "gsc" - -#define MEI_CFG_TYPE_GSCFI \ - .kind = "gscfi" - #define MEI_CFG_FW_SPS_IGN \ .quirk_probe = mei_me_fw_type_sps_ign +static enum mei_dev_kind mei_cfg_kind_mei(const struct device *parent) +{ + return MEI_DEV_KIND_MEI; +} + +#define MEI_CFG_KIND_MEI \ + .get_kind = mei_cfg_kind_mei + +static enum mei_dev_kind mei_cfg_kind_itouch(const struct device *parent) +{ + return MEI_DEV_KIND_ITOUCH; +} + +#define MEI_CFG_KIND_ITOUCH \ + .get_kind = mei_cfg_kind_itouch + +static enum mei_dev_kind mei_cfg_kind_gsc(const struct device *parent) +{ + return MEI_DEV_KIND_GSC; +} + +#define MEI_CFG_KIND_GSC \ + .get_kind = mei_cfg_kind_gsc + +static enum mei_dev_kind mei_cfg_kind_gscfi(const struct device *parent) +{ + return MEI_DEV_KIND_GSCFI; +} + +#define MEI_CFG_KIND_GSCFI \ + .get_kind = mei_cfg_kind_gscfi + #define MEI_CFG_FW_VER_SUPP \ .fw_ver_supported = 1 @@ -1630,27 +1653,32 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) /* ICH Legacy devices */ static const struct mei_cfg mei_me_ich_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_ICH_HFS, }; /* ICH devices */ static const struct mei_cfg mei_me_ich10_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_ICH10_HFS, }; /* PCH6 devices */ static const struct mei_cfg mei_me_pch6_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH_HFS, }; /* PCH7 devices */ static const struct mei_cfg mei_me_pch7_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH_HFS, MEI_CFG_FW_VER_SUPP, }; /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_NM, @@ -1658,6 +1686,7 @@ static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { /* PCH8 Lynx Point and newer devices */ static const struct mei_cfg mei_me_pch8_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, }; @@ -1671,6 +1700,7 @@ static const struct mei_cfg mei_me_pch8_itouch_cfg = { /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ static const struct mei_cfg mei_me_pch8_sps_4_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_SPS_4, @@ -1678,6 +1708,7 @@ static const struct mei_cfg mei_me_pch8_sps_4_cfg = { /* LBG with quirk for SPS (4.0) Firmware exclusion */ static const struct mei_cfg mei_me_pch12_sps_4_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_SPS_4, @@ -1685,6 +1716,7 @@ static const struct mei_cfg mei_me_pch12_sps_4_cfg = { /* Cannon Lake and newer devices */ static const struct mei_cfg mei_me_pch12_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, @@ -1692,6 +1724,7 @@ static const struct mei_cfg mei_me_pch12_cfg = { /* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion */ static const struct mei_cfg mei_me_pch12_sps_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, @@ -1710,6 +1743,7 @@ static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = { /* Tiger Lake and newer devices */ static const struct mei_cfg mei_me_pch15_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, @@ -1718,6 +1752,7 @@ static const struct mei_cfg mei_me_pch15_cfg = { /* Tiger Lake with quirk for SPS 5.0 and newer Firmware exclusion */ static const struct mei_cfg mei_me_pch15_sps_cfg = { + MEI_CFG_KIND_MEI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_DMA_128, @@ -1727,21 +1762,21 @@ static const struct mei_cfg mei_me_pch15_sps_cfg = { /* Graphics System Controller */ static const struct mei_cfg mei_me_gsc_cfg = { - MEI_CFG_TYPE_GSC, + MEI_CFG_KIND_GSC, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, }; /* Graphics System Controller Firmware Interface */ static const struct mei_cfg mei_me_gscfi_cfg = { - MEI_CFG_TYPE_GSCFI, + MEI_CFG_KIND_GSCFI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, }; /* Chassis System Controller Firmware Interface */ static const struct mei_cfg mei_me_csc_cfg = { - MEI_CFG_TYPE_GSCFI, + MEI_CFG_KIND_GSCFI, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, }; @@ -1812,7 +1847,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; - dev->kind = cfg->kind; + dev->kind = cfg->get_kind(parent); return dev; } diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 8da8662a9d61..884736d76fbb 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -19,7 +19,7 @@ * * @fw_status: FW status * @quirk_probe: device exclusion quirk - * @kind: MEI head kind + * @get_kind: MEI head kind helper * @dma_size: device DMA buffers size * @fw_ver_supported: is fw version retrievable from FW * @hw_trc_supported: does the hw support trc register @@ -27,7 +27,7 @@ struct mei_cfg { const struct mei_fw_status fw_status; bool (*quirk_probe)(const struct pci_dev *pdev); - const char *kind; + enum mei_dev_kind (*get_kind)(const struct device *parent); size_t dma_size[DMA_DSCR_NUM]; u32 fw_ver_supported:1; u32 hw_trc_supported:1; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 54f70f513482..43667e5d3708 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -1165,6 +1165,15 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) } } +static const char * const mei_kind_names[] = { + "mei", + "itouch", + "gsc", + "gscfi", + "ivsc", +}; +static_assert(ARRAY_SIZE(mei_kind_names) == MEI_DEV_KIND_MAX); + /** * kind_show - display device kind * @@ -1178,14 +1187,11 @@ static ssize_t kind_show(struct device *device, struct device_attribute *attr, char *buf) { struct mei_device *dev = dev_get_drvdata(device); - ssize_t ret; - if (dev->kind) - ret = sprintf(buf, "%s\n", dev->kind); - else - ret = sprintf(buf, "%s\n", "mei"); + if (dev->kind < MEI_DEV_KIND_MEI || dev->kind >= MEI_DEV_KIND_MAX) + return -EINVAL; - return ret; + return sysfs_emit(buf, "%s\n", mei_kind_names[dev->kind]); } static DEVICE_ATTR_RO(kind); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d8634a726990..6d081c496ccc 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -469,6 +469,25 @@ struct mei_dev_timeouts { unsigned long link_reset_wait; /* link reset wait timeout, in jiffies */ }; +/** + * enum mei_dev_kind - device type + * + * @MEI_DEV_KIND_MEI: basic device + * @MEI_DEV_KIND_ITOUCH: itouch support + * @MEI_DEV_KIND_GSC: discete graphics content protection + * @MEI_DEV_KIND_GSCFI: discete graphics chassis controller + * @MEI_DEV_KIND_IVSC: visual sensing controller + * @MEI_DEV_KIND_MAX: sentinel + */ +enum mei_dev_kind { + MEI_DEV_KIND_MEI, + MEI_DEV_KIND_ITOUCH, + MEI_DEV_KIND_GSC, + MEI_DEV_KIND_GSCFI, + MEI_DEV_KIND_IVSC, + MEI_DEV_KIND_MAX +}; + /** * struct mei_device - MEI private device struct * @@ -650,7 +669,7 @@ struct mei_device { struct list_head device_list; struct mutex cl_bus_lock; - const char *kind; + enum mei_dev_kind kind; #if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *dbgfs_dir; @@ -911,8 +930,7 @@ static inline ssize_t mei_fw_status_str(struct mei_device *dev, */ static inline bool kind_is_gsc(struct mei_device *dev) { - /* check kind for NULL because it may be not set, like at the fist call to hw_start */ - return dev->kind && (strcmp(dev->kind, "gsc") == 0); + return dev->kind == MEI_DEV_KIND_GSC; } /** @@ -924,7 +942,6 @@ static inline bool kind_is_gsc(struct mei_device *dev) */ static inline bool kind_is_gscfi(struct mei_device *dev) { - /* check kind for NULL because it may be not set, like at the fist call to hw_start */ - return dev->kind && (strcmp(dev->kind, "gscfi") == 0); + return dev->kind == MEI_DEV_KIND_GSCFI; } #endif diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 98d1bc2c7f4b..f5441bc5efe2 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -84,6 +84,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = -ENOMEM; goto end; } + dev->kind = MEI_DEV_KIND_MEI; hw = to_txe_hw(dev); hw->mem_addr = pcim_iomap_table(pdev); diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 9787b9cee71c..5100234ba5b6 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -350,7 +350,7 @@ static int mei_vsc_probe(struct platform_device *pdev) mei_device_init(mei_dev, dev, false, &mei_vsc_hw_ops); mei_dev->fw_f_fw_ver_supported = 0; - mei_dev->kind = "ivsc"; + mei_dev->kind = MEI_DEV_KIND_IVSC; hw = mei_dev_to_vsc_hw(mei_dev); atomic_set(&hw->write_lock_cnt, 0); From 25b18b85918df43757fa9d9488a5b618085c8605 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 9 Apr 2026 15:55:23 +0300 Subject: [PATCH 1635/5214] mei: expose device kind for ioe device Detect IO extender device and set appropriate kind. Reviewed-by: Andy Shevchenko Co-developed-by: Reuven Abliyev Signed-off-by: Reuven Abliyev Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260409125524.111530-3-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 4 ++++ drivers/misc/mei/hw-me.c | 35 +++++++++++++++++++++++++++++++---- drivers/misc/mei/hw-me.h | 2 ++ drivers/misc/mei/main.c | 1 + drivers/misc/mei/mei_dev.h | 2 ++ drivers/misc/mei/pci-me.c | 2 +- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index f145e8e36cb3..b9a67629496b 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -6,6 +6,8 @@ #ifndef _MEI_HW_MEI_REGS_H_ #define _MEI_HW_MEI_REGS_H_ +#include + /* * MEI device IDs */ @@ -142,6 +144,8 @@ # define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */ # define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000 #define PCI_CFG_HFS_3 0x60 +# define PCI_CFG_HFS_3_EXT_SKU_MSK GENMASK(3, 0) /* IOE detection bits */ +# define PCI_CFG_HFS_3_EXT_SKU_IOE 0x00000001 # define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070 # define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000 # define PCI_CFG_HFS_3_FW_SKU_SPS 0x00000060 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 28fbd432d80d..e7fbc02fb70f 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -4,13 +4,13 @@ * Intel Management Engine Interface (Intel MEI) Linux driver */ -#include - -#include +#include +#include #include +#include +#include #include #include -#include #include "mei_dev.h" #include "hbm.h" @@ -1619,6 +1619,23 @@ static enum mei_dev_kind mei_cfg_kind_gscfi(const struct device *parent) #define MEI_CFG_KIND_GSCFI \ .get_kind = mei_cfg_kind_gscfi +static enum mei_dev_kind mei_cfg_kind_ioe(const struct device *parent) +{ + const struct pci_dev *pdev = to_pci_dev(parent); + unsigned int devfn; + u32 reg; + int ret; + + devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, ®); + trace_mei_pci_cfg_read(parent, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg, ret); + return FIELD_GET(PCI_CFG_HFS_3_EXT_SKU_MSK, reg) == PCI_CFG_HFS_3_EXT_SKU_IOE ? + MEI_DEV_KIND_IOE : MEI_DEV_KIND_MEI; +} + +#define MEI_CFG_KIND_IOE \ + .get_kind = mei_cfg_kind_ioe + #define MEI_CFG_FW_VER_SUPP \ .fw_ver_supported = 1 @@ -1781,6 +1798,15 @@ static const struct mei_cfg mei_me_csc_cfg = { MEI_CFG_FW_VER_SUPP, }; +/* Nova Lake with possible IOE devices */ +static const struct mei_cfg mei_me_pch22_ioe_cfg = { + MEI_CFG_KIND_IOE, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, + MEI_CFG_TRC, +}; + /* * mei_cfg_list - A list of platform platform specific configurations. * Note: has to be synchronized with enum mei_cfg_idx. @@ -1804,6 +1830,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, [MEI_ME_CSC_CFG] = &mei_me_csc_cfg, + [MEI_ME_PCH22_IOE_CFG] = &mei_me_pch22_ioe_cfg, }; const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 884736d76fbb..e36f871f323e 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -105,6 +105,7 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw) * @MEI_ME_GSC_CFG: Graphics System Controller * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface * @MEI_ME_CSC_CFG: Chassis System Controller Firmware Interface + * @MEI_ME_PCH22_IOE_CFG: Platform Controller Hub Gen22 and newer with IOE detection * @MEI_ME_NUM_CFG: Upper Sentinel. */ enum mei_cfg_idx { @@ -126,6 +127,7 @@ enum mei_cfg_idx { MEI_ME_GSC_CFG, MEI_ME_GSCFI_CFG, MEI_ME_CSC_CFG, + MEI_ME_PCH22_IOE_CFG, MEI_ME_NUM_CFG, }; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 43667e5d3708..4fbf0b323616 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -1171,6 +1171,7 @@ static const char * const mei_kind_names[] = { "gsc", "gscfi", "ivsc", + "ioe", }; static_assert(ARRAY_SIZE(mei_kind_names) == MEI_DEV_KIND_MAX); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 6d081c496ccc..e651b06704a1 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -477,6 +477,7 @@ struct mei_dev_timeouts { * @MEI_DEV_KIND_GSC: discete graphics content protection * @MEI_DEV_KIND_GSCFI: discete graphics chassis controller * @MEI_DEV_KIND_IVSC: visual sensing controller + * @MEI_DEV_KIND_IOE: IO extender * @MEI_DEV_KIND_MAX: sentinel */ enum mei_dev_kind { @@ -485,6 +486,7 @@ enum mei_dev_kind { MEI_DEV_KIND_GSC, MEI_DEV_KIND_GSCFI, MEI_DEV_KIND_IVSC, + MEI_DEV_KIND_IOE, MEI_DEV_KIND_MAX }; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 9efeafa8f1ca..55e0b8a98827 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -130,7 +130,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH22_IOE_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, /* required last entry */ From 314e01d7f67aaa72617aa5e88e4fea09373bd04d Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 9 Apr 2026 15:55:24 +0300 Subject: [PATCH 1636/5214] mei: me: remove comma from mei_cfg_idx sentinel Adhere to termnator line rule and remove comma from sentinel in enum mei_cfg_idx. Reviewed-by: Andy Shevchenko Suggested-by: Andy Shevchenko Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260409125524.111530-4-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index e36f871f323e..0038a6d431fd 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -128,7 +128,7 @@ enum mei_cfg_idx { MEI_ME_GSCFI_CFG, MEI_ME_CSC_CFG, MEI_ME_PCH22_IOE_CFG, - MEI_ME_NUM_CFG, + MEI_ME_NUM_CFG }; const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx); From bbf003b7794d6ad6f939fdd29f1f1bde8ac554c1 Mon Sep 17 00:00:00 2001 From: James Kim Date: Sun, 3 May 2026 19:11:31 +0900 Subject: [PATCH 1637/5214] char: tlclk: fix use-after-free in tlclk_cleanup() This patch improves the module cleanup process in the tlclk driver to prevent potential use-after-free and race conditions. Currently, the file_operations structure does not specify the .owner field, which could allow the module to be unloaded while user-space processes are still interacting with the device. Additionally, the tlclk_cleanup() function frees the alarm_events memory before ensuring that blocked processes in the waitqueue are fully awakened and that the switchover_timer has completed. To address these cases, this patch: - Sets '.owner = THIS_MODULE' in tlclk_fops to safely defer module unloading while the device is in use. - Updates tlclk_cleanup() to explicitly wake up all blocked readers (wake_up_all), properly release hardware I/O regions, and safely delete the timer (timer_delete_sync) prior to freeing memory. Fixes: 1a80ba882730 ("[PATCH] Telecom Clock Driver for MPCBL0010 ATCA computer blade") Signed-off-by: James Kim Link: https://patch.msgid.link/20260503101131.64219-1-james010kim@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/tlclk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 677d230a226c..dd45fe5eb6f2 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -264,6 +264,7 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, } static const struct file_operations tlclk_fops = { + .owner = THIS_MODULE, .read = tlclk_read, .open = tlclk_open, .release = tlclk_release, @@ -837,6 +838,9 @@ static void __exit tlclk_cleanup(void) misc_deregister(&tlclk_miscdev); unregister_chrdev(tlclk_major, "telco_clock"); + got_event = 1; + wake_up_all(&wq); + release_region(TLCLK_BASE, 8); timer_delete_sync(&switchover_timer); kfree(alarm_events); From 048a96b73ffb499130ccd5fffd1a9c05e08a3d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 4 May 2026 17:57:15 +0200 Subject: [PATCH 1638/5214] misc: rtsx: Use named initializers for struct pci_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initializing structures using list initializers is harder to read than using named initializers. Seeing the member name is more ideomatic and easier to understand. Use named initializers for the driver's pci_device_id array. While at it also drop an explicit zero in the terminating array entry. There are no changes to the compiled result of the array; verified with builds for x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260504155715.2163032-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 2b6a994c6fe1..c4d54ca2fa80 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -42,21 +42,21 @@ static struct mfd_cell rtsx_pcr_cells[] = { }; static const struct pci_device_id rtsx_pci_ids[] = { - { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x522A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5261), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5228), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5264), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { 0, } + { PCI_DEVICE(0x10EC, 0x5209), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5229), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5289), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5227), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x522A), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5249), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5287), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5286), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x524A), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x525A), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5260), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5261), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5228), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5264), .class = PCI_CLASS_OTHERS << 16, .class_mask = 0xFF0000 }, + { } }; MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); From 4c4c83e9ee74ce172bd33ed7d98191803ef453cb Mon Sep 17 00:00:00 2001 From: Abhishikth J Date: Tue, 5 May 2026 00:11:44 +0530 Subject: [PATCH 1639/5214] drivers: misc: vmw_vmci: fix typo in comment Correct spelling of "Intializes" to "Initializes" in comment. Signed-off-by: Abhishikth J Link: https://patch.msgid.link/20260504184144.33665-1-abhishikthj7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_queue_pair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index b777bc3fdde2..46bba9bef553 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -420,7 +420,7 @@ static int qp_memcpy_from_queue_iter(struct iov_iter *to, /* * Allocates two list of PPNs --- one for the pages in the produce queue, - * and the other for the pages in the consume queue. Intializes the list + * and the other for the pages in the consume queue. Initializes the list * of PPNs with the page frame numbers of the KVA for the two queues (and * the queue headers). */ From adb2d73d420a9fb797fa69450f17de4e60492bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 7 May 2026 10:04:02 +0200 Subject: [PATCH 1640/5214] misc: tifm: Use PCI_VDEVICE to initialize pci_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCI_VDEVICE macro allows to assign the first four members of pci_device_id more idiomatic and compact. Also drop trailing zeros in the list initializer that the compiler takes care of then. The driver doesn't use neither .class, .class_mask nor .driver_data, so it's fine to not assign these explicitly. There are no changes to the compiled data; confirmed using an x86 and an arm64 build. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260507080402.2672527-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tifm_7xx1.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 1d54680d6ed2..4677d5a30941 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -400,12 +400,9 @@ static void tifm_7xx1_remove(struct pci_dev *dev) } static const struct pci_device_id tifm_7xx1_pci_tbl[] = { - { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX21_XX11_FM, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, /* xx21 - the one I have */ - { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX12_FM, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX20_FM, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, + { PCI_VDEVICE(TI, PCI_DEVICE_ID_TI_XX21_XX11_FM) }, /* xx21 - the one I have */ + { PCI_VDEVICE(TI, PCI_DEVICE_ID_TI_XX12_FM) }, + { PCI_VDEVICE(TI, PCI_DEVICE_ID_TI_XX20_FM) }, { } }; From fa5e2952341b792b72d5410dbfcd0b2ac8046d2a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:20:09 +0200 Subject: [PATCH 1641/5214] hpet: Check ACPI_COMPANION() against NULL at probe time Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the hpet driver. Fixes: 71f0a267346b ("hpet: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4750803.LvFx2qVVIh@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/char/hpet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 46c84e5df00f..285c6037417a 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -976,10 +976,14 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) static int hpet_acpi_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status result; struct hpet_data data; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + memset(&data, 0, sizeof(data)); result = From 950f35211b85db26fc1aae67bf1e14ccef393a0a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:20:53 +0200 Subject: [PATCH 1642/5214] sonypi: Check ACPI_COMPANION() against NULL at probe time Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the sonypi driver. Fixes: 7e488b0af021 ("sonypi: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5087721.GXAFRqVoOG@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/char/sonypi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index ccda997a9098..24c1b26f34d6 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1117,7 +1117,11 @@ static int sonypi_disable(void) #ifdef CONFIG_ACPI static int sonypi_acpi_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; + + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; sonypi_acpi_device = device; strcpy(acpi_device_name(device), "Sony laptop hotkeys"); From e8c715f3a7dae43fabae261493a26474fec11863 Mon Sep 17 00:00:00 2001 From: Venkat Rao Bagalkote Date: Tue, 28 Apr 2026 11:45:40 +0530 Subject: [PATCH 1643/5214] char/nvram: Remove redundant nvram_mutex The global nvram_mutex in drivers/char/nvram.c is redundant and unused, and this triggers compiler warnings on some configurations. All platform-specific nvram operations already provide their own internal synchronization, meaning the wrapper-level mutex does not provide any additional safety. Remove the nvram_mutex definition along with all remaining lock/unlock users across PPC32, x86, and m68k code paths, and rely entirely on the per-architecture nvram implementations for locking. Reviewed-by: Arnd Bergmann Suggested-by: Arnd Bergmann Tested-by: Tellakula Yeswanth Krishna Signed-off-by: Venkat Rao Bagalkote Tested-by: yeswanth Reviewed-by: Ritesh Harjani (IBM) Link: https://patch.msgid.link/20260428061540.73668-1-venkat88@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/nvram.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 9eff426a9286..e89cc1f1c89e 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -53,7 +53,6 @@ #include #endif -static DEFINE_MUTEX(nvram_mutex); static DEFINE_SPINLOCK(nvram_state_lock); static int nvram_open_cnt; /* #times opened */ static int nvram_open_mode; /* special open modes */ @@ -310,11 +309,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, break; #ifdef CONFIG_PPC32 case IOC_NVRAM_SYNC: - if (ppc_md.nvram_sync != NULL) { - mutex_lock(&nvram_mutex); + if (ppc_md.nvram_sync) ppc_md.nvram_sync(); - mutex_unlock(&nvram_mutex); - } ret = 0; break; #endif @@ -324,11 +320,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (arch_nvram_ops.initialize != NULL) { - mutex_lock(&nvram_mutex); + if (arch_nvram_ops.initialize) ret = arch_nvram_ops.initialize(); - mutex_unlock(&nvram_mutex); - } break; case NVRAM_SETCKS: /* just set checksum, contents unchanged (maybe useful after @@ -336,11 +329,8 @@ static long nvram_misc_ioctl(struct file *file, unsigned int cmd, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (arch_nvram_ops.set_checksum != NULL) { - mutex_lock(&nvram_mutex); + if (arch_nvram_ops.set_checksum) ret = arch_nvram_ops.set_checksum(); - mutex_unlock(&nvram_mutex); - } break; #endif /* CONFIG_X86 || CONFIG_M68K */ } From d14b649fd99f1691848bc57789c2cf6908e3d4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 21 May 2026 12:46:35 +0200 Subject: [PATCH 1644/5214] misc: pch_phub: Drop two unused functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two functions are unused since commit 34afa1d657d4 ("misc/pch_phub.c: use generic power management") but the compiler didn't warn about it because the same commit marked the functions as __maybe_unsed. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/aaa24e2dbb2be5fb2dffa61c89fc190aaa391ad0.1779360001.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 123 ---------------------------------------- 1 file changed, 123 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index fd147fd2800f..539912e825eb 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -149,129 +149,6 @@ static void pch_phub_read_modify_write_reg(struct pch_phub_reg *chip, iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr); } -/* pch_phub_save_reg_conf - saves register configuration */ -static void __maybe_unused pch_phub_save_reg_conf(struct pci_dev *pdev) -{ - unsigned int i; - struct pch_phub_reg *chip = pci_get_drvdata(pdev); - - void __iomem *p = chip->pch_phub_base_address; - - chip->phub_id_reg = ioread32(p + PCH_PHUB_ID_REG); - chip->q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG); - chip->rc_q_maxsize_reg = ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG); - chip->bri_q_maxsize_reg = ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG); - chip->comp_resp_timeout_reg = - ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG); - chip->bus_slave_control_reg = - ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG); - chip->deadlock_avoid_type_reg = - ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG); - chip->intpin_reg_wpermit_reg0 = - ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0); - chip->intpin_reg_wpermit_reg1 = - ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1); - chip->intpin_reg_wpermit_reg2 = - ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2); - chip->intpin_reg_wpermit_reg3 = - ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3); - dev_dbg(&pdev->dev, "%s : " - "chip->phub_id_reg=%x, " - "chip->q_pri_val_reg=%x, " - "chip->rc_q_maxsize_reg=%x, " - "chip->bri_q_maxsize_reg=%x, " - "chip->comp_resp_timeout_reg=%x, " - "chip->bus_slave_control_reg=%x, " - "chip->deadlock_avoid_type_reg=%x, " - "chip->intpin_reg_wpermit_reg0=%x, " - "chip->intpin_reg_wpermit_reg1=%x, " - "chip->intpin_reg_wpermit_reg2=%x, " - "chip->intpin_reg_wpermit_reg3=%x\n", __func__, - chip->phub_id_reg, - chip->q_pri_val_reg, - chip->rc_q_maxsize_reg, - chip->bri_q_maxsize_reg, - chip->comp_resp_timeout_reg, - chip->bus_slave_control_reg, - chip->deadlock_avoid_type_reg, - chip->intpin_reg_wpermit_reg0, - chip->intpin_reg_wpermit_reg1, - chip->intpin_reg_wpermit_reg2, - chip->intpin_reg_wpermit_reg3); - for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) { - chip->int_reduce_control_reg[i] = - ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i); - dev_dbg(&pdev->dev, "%s : " - "chip->int_reduce_control_reg[%d]=%x\n", - __func__, i, chip->int_reduce_control_reg[i]); - } - chip->clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET); - if ((chip->ioh_type == 2) || (chip->ioh_type == 4)) - chip->funcsel_reg = ioread32(p + FUNCSEL_REG_OFFSET); -} - -/* pch_phub_restore_reg_conf - restore register configuration */ -static void __maybe_unused pch_phub_restore_reg_conf(struct pci_dev *pdev) -{ - unsigned int i; - struct pch_phub_reg *chip = pci_get_drvdata(pdev); - void __iomem *p; - p = chip->pch_phub_base_address; - - iowrite32(chip->phub_id_reg, p + PCH_PHUB_ID_REG); - iowrite32(chip->q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG); - iowrite32(chip->rc_q_maxsize_reg, p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG); - iowrite32(chip->bri_q_maxsize_reg, p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG); - iowrite32(chip->comp_resp_timeout_reg, - p + PCH_PHUB_COMP_RESP_TIMEOUT_REG); - iowrite32(chip->bus_slave_control_reg, - p + PCH_PHUB_BUS_SLAVE_CONTROL_REG); - iowrite32(chip->deadlock_avoid_type_reg, - p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG); - iowrite32(chip->intpin_reg_wpermit_reg0, - p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0); - iowrite32(chip->intpin_reg_wpermit_reg1, - p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1); - iowrite32(chip->intpin_reg_wpermit_reg2, - p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2); - iowrite32(chip->intpin_reg_wpermit_reg3, - p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3); - dev_dbg(&pdev->dev, "%s : " - "chip->phub_id_reg=%x, " - "chip->q_pri_val_reg=%x, " - "chip->rc_q_maxsize_reg=%x, " - "chip->bri_q_maxsize_reg=%x, " - "chip->comp_resp_timeout_reg=%x, " - "chip->bus_slave_control_reg=%x, " - "chip->deadlock_avoid_type_reg=%x, " - "chip->intpin_reg_wpermit_reg0=%x, " - "chip->intpin_reg_wpermit_reg1=%x, " - "chip->intpin_reg_wpermit_reg2=%x, " - "chip->intpin_reg_wpermit_reg3=%x\n", __func__, - chip->phub_id_reg, - chip->q_pri_val_reg, - chip->rc_q_maxsize_reg, - chip->bri_q_maxsize_reg, - chip->comp_resp_timeout_reg, - chip->bus_slave_control_reg, - chip->deadlock_avoid_type_reg, - chip->intpin_reg_wpermit_reg0, - chip->intpin_reg_wpermit_reg1, - chip->intpin_reg_wpermit_reg2, - chip->intpin_reg_wpermit_reg3); - for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) { - iowrite32(chip->int_reduce_control_reg[i], - p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i); - dev_dbg(&pdev->dev, "%s : " - "chip->int_reduce_control_reg[%d]=%x\n", - __func__, i, chip->int_reduce_control_reg[i]); - } - - iowrite32(chip->clkcfg_reg, p + CLKCFG_REG_OFFSET); - if ((chip->ioh_type == 2) || (chip->ioh_type == 4)) - iowrite32(chip->funcsel_reg, p + FUNCSEL_REG_OFFSET); -} - /** * pch_phub_read_serial_rom() - Reading Serial ROM * @chip: Pointer to the PHUB register structure From 7b1d4ad96ea47b3275328fa385d0497e164f1f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 21 May 2026 12:46:36 +0200 Subject: [PATCH 1645/5214] misc: pch_phub: Introduce an enum for device indentification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using magic constants give them names that make the code more idiomatic. While touching the pci_device_id array, use named initializers to assign .driver_data. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/62223b743982616b1085c03f67ff88a2412d3da1.1779360001.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 539912e825eb..19c4fa017f24 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -537,6 +537,14 @@ static const struct bin_attribute pch_bin_attr = { .write = pch_phub_bin_write, }; +enum { + PCH_EG20T, + PCH_ML7213, + PCH_ML7223M, + PCH_ML7223N, + PCH_ML7831, +}; + static int pch_phub_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -579,7 +587,7 @@ static int pch_phub_probe(struct pci_dev *pdev, chip->pdev = pdev; /* Save pci device struct */ - if (id->driver_data == 1) { /* EG20T PCH */ + if (id->driver_data == PCH_EG20T) { /* EG20T PCH */ const char *board_name; unsigned int prefetch = 0x000affaa; @@ -627,7 +635,7 @@ static int pch_phub_probe(struct pci_dev *pdev, CLKCFG_UART_MASK); } } - } else if (id->driver_data == 2) { /* ML7213 IOH */ + } else if (id->driver_data == PCH_ML7213) { /* ML7213 IOH */ ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); if (ret) goto err_sysfs_create; @@ -640,7 +648,7 @@ static int pch_phub_probe(struct pci_dev *pdev, iowrite32(0x000affa0, chip->pch_phub_base_address + 0x14); chip->pch_opt_rom_start_address =\ PCH_PHUB_ROM_START_ADDR_ML7213; - } else if (id->driver_data == 3) { /* ML7223 IOH Bus-m*/ + } else if (id->driver_data == PCH_ML7223M) { /* ML7223 IOH Bus-m*/ /* set the prefech value * Device8(GbE) */ @@ -650,7 +658,7 @@ static int pch_phub_probe(struct pci_dev *pdev, chip->pch_opt_rom_start_address =\ PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; - } else if (id->driver_data == 4) { /* ML7223 IOH Bus-n*/ + } else if (id->driver_data == PCH_ML7223N) { /* ML7223 IOH Bus-n*/ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); if (ret) @@ -667,7 +675,7 @@ static int pch_phub_probe(struct pci_dev *pdev, chip->pch_opt_rom_start_address =\ PCH_PHUB_ROM_START_ADDR_ML7223; chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_ML7223; - } else if (id->driver_data == 5) { /* ML7831 */ + } else if (id->driver_data == PCH_ML7831) { /* ML7831 */ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); if (ret) @@ -731,11 +739,11 @@ static int __maybe_unused pch_phub_resume(struct device *dev_d) } static const struct pci_device_id pch_phub_pcidev_id[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_mPHUB), 3, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_nPHUB), 4, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7831_PHUB), 5, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), .driver_data = PCH_EG20T }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), .driver_data = PCH_ML7213 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_mPHUB), .driver_data = PCH_ML7223M }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_nPHUB), .driver_data = PCH_ML7223N }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7831_PHUB), .driver_data = PCH_ML7831 }, { } }; MODULE_DEVICE_TABLE(pci, pch_phub_pcidev_id); From 666c7f9e07925aa0863348f960e09bb89f8f05a3 Mon Sep 17 00:00:00 2001 From: Sicong Huang Date: Tue, 19 May 2026 19:20:18 +0800 Subject: [PATCH 1646/5214] virt: acrn: Fix irqfd use-after-free during eventfd shutdown acrn_irqfd_deassign() and the eventfd EPOLLHUP wakeup can race and free the same struct hsm_irqfd: CPU0 CPU1 ---- ---- eventfd_release() wake_up_poll(EPOLLHUP) hsm_irqfd_wakeup() queue_work(&irqfd->shutdown) acrn_irqfd_deassign() hsm_irqfd_shutdown() list_del_init() eventfd_ctx_remove_wait_queue() eventfd_ctx_put() kfree(irqfd) hsm_irqfd_shutdown_work() container_of(work, ..., shutdown) irqfd->vm <-- use-after-free The deassign path freed the irqfd while a shutdown work item was already queued by EPOLLHUP (or vice versa), so the work item could resurrect a dangling pointer through container_of(). Switch to the lifetime model used by KVM irqfds: - Deassign/deinit only deactivate the irqfd: remove it from vm->irqfds under irqfds_lock and queue the cleanup work. - hsm_irqfd_shutdown_work() becomes the sole owner that unhooks the eventfd waitqueue entry, drops the eventfd reference and frees the irqfd. - A new HSM_IRQFD_FLAG_SHUTDOWN bit guarded by test_and_set_bit() ensures the cleanup work is queued at most once, no matter how many of {EPOLLHUP, deassign, deinit} fire concurrently. This is safe to call from the waitqueue callback, which runs with wqh->lock held and IRQs disabled and therefore cannot take irqfds_lock. - acrn_irqfd_deassign() flushes vm->irqfd_wq before returning so the eventfd is fully detached on return. acrn_irqfd_deinit() deactivates every irqfd, flushes the workqueue and only then destroys it, so no path can queue_work() onto a torn-down workqueue. - acrn_irqfd_assign() now installs the eventfd waitqueue entry and publishes the irqfd to vm->irqfds under irqfds_lock, so the irqfd is never visible to deassign/deinit before its waitqueue entry is in place, and any EPOLLHUP that fires in the assign window queues cleanup work that blocks on irqfds_lock until publication is done. Signed-off-by: Sicong Huang Reviewed-by: Fei Li Link: https://patch.msgid.link/20260519112018.2135000-2-congei42@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/acrn/irqfd.c | 75 ++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/drivers/virt/acrn/irqfd.c b/drivers/virt/acrn/irqfd.c index acf8cd5f8f8c..feeba7eda494 100644 --- a/drivers/virt/acrn/irqfd.c +++ b/drivers/virt/acrn/irqfd.c @@ -16,6 +16,9 @@ #include "acrn_drv.h" +/* Cleanup work has been queued; set via test_and_set_bit(). */ +#define HSM_IRQFD_FLAG_SHUTDOWN 0 + /** * struct hsm_irqfd - Properties of HSM irqfd * @vm: Associated VM pointer @@ -25,6 +28,7 @@ * @list: Entry within &acrn_vm.irqfds of irqfds of a VM * @pt: Structure for select/poll on the associated eventfd * @msi: MSI data + * @flags: Internal lifecycle flags (HSM_IRQFD_FLAG_*) */ struct hsm_irqfd { struct acrn_vm *vm; @@ -34,6 +38,7 @@ struct hsm_irqfd { struct list_head list; poll_table pt; struct acrn_msi_entry msi; + unsigned long flags; }; static void acrn_irqfd_inject(struct hsm_irqfd *irqfd) @@ -44,49 +49,47 @@ static void acrn_irqfd_inject(struct hsm_irqfd *irqfd) irqfd->msi.msi_data); } -static void hsm_irqfd_shutdown(struct hsm_irqfd *irqfd) +/* Queue the cleanup work at most once. Safe from atomic context. */ +static void hsm_irqfd_queue_shutdown(struct hsm_irqfd *irqfd) { + if (!test_and_set_bit(HSM_IRQFD_FLAG_SHUTDOWN, &irqfd->flags)) + queue_work(irqfd->vm->irqfd_wq, &irqfd->shutdown); +} + +/* Sole owner of @irqfd: unhook waitqueue, drop eventfd ref, free. */ +static void hsm_irqfd_shutdown_work(struct work_struct *work) +{ + struct hsm_irqfd *irqfd = container_of(work, struct hsm_irqfd, + shutdown); + struct acrn_vm *vm = irqfd->vm; u64 cnt; - lockdep_assert_held(&irqfd->vm->irqfds_lock); + mutex_lock(&vm->irqfds_lock); + if (!list_empty(&irqfd->list)) + list_del_init(&irqfd->list); + mutex_unlock(&vm->irqfds_lock); - /* remove from wait queue */ - list_del_init(&irqfd->list); eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); eventfd_ctx_put(irqfd->eventfd); kfree(irqfd); } -static void hsm_irqfd_shutdown_work(struct work_struct *work) -{ - struct hsm_irqfd *irqfd; - struct acrn_vm *vm; - - irqfd = container_of(work, struct hsm_irqfd, shutdown); - vm = irqfd->vm; - mutex_lock(&vm->irqfds_lock); - if (!list_empty(&irqfd->list)) - hsm_irqfd_shutdown(irqfd); - mutex_unlock(&vm->irqfds_lock); -} - /* Called with wqh->lock held and interrupts disabled */ static int hsm_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key) { unsigned long poll_bits = (unsigned long)key; struct hsm_irqfd *irqfd; - struct acrn_vm *vm; irqfd = container_of(wait, struct hsm_irqfd, wait); - vm = irqfd->vm; + if (poll_bits & POLLIN) /* An event has been signaled, inject an interrupt */ acrn_irqfd_inject(irqfd); if (poll_bits & POLLHUP) - /* Do shutdown work in thread to hold wqh->lock */ - queue_work(vm->irqfd_wq, &irqfd->shutdown); + /* Defer teardown to the cleanup work; can't sleep here. */ + hsm_irqfd_queue_shutdown(irqfd); return 0; } @@ -142,6 +145,12 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args) init_waitqueue_func_entry(&irqfd->wait, hsm_irqfd_wakeup); init_poll_funcptr(&irqfd->pt, hsm_irqfd_poll_func); + /* + * Hold irqfds_lock across waitqueue install and list_add so the + * irqfd is not visible to deassign/deinit before its waitqueue + * entry is in place, and any racing EPOLLHUP cleanup work blocks + * on irqfds_lock until publication completes. + */ mutex_lock(&vm->irqfds_lock); list_for_each_entry(tmp, &vm->irqfds, list) { if (irqfd->eventfd != tmp->eventfd) @@ -150,14 +159,12 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args) mutex_unlock(&vm->irqfds_lock); goto fail; } - list_add_tail(&irqfd->list, &vm->irqfds); - mutex_unlock(&vm->irqfds_lock); - /* Check the pending event in this stage */ events = vfs_poll(fd_file(f), &irqfd->pt); - + list_add_tail(&irqfd->list, &vm->irqfds); if (events & EPOLLIN) acrn_irqfd_inject(irqfd); + mutex_unlock(&vm->irqfds_lock); return 0; fail: @@ -180,13 +187,17 @@ static int acrn_irqfd_deassign(struct acrn_vm *vm, mutex_lock(&vm->irqfds_lock); list_for_each_entry_safe(irqfd, tmp, &vm->irqfds, list) { if (irqfd->eventfd == eventfd) { - hsm_irqfd_shutdown(irqfd); + list_del_init(&irqfd->list); + hsm_irqfd_queue_shutdown(irqfd); break; } } mutex_unlock(&vm->irqfds_lock); eventfd_ctx_put(eventfd); + /* Wait for cleanup work to finish so the eventfd is fully detached. */ + flush_workqueue(vm->irqfd_wq); + return 0; } @@ -219,9 +230,15 @@ void acrn_irqfd_deinit(struct acrn_vm *vm) struct hsm_irqfd *irqfd, *next; dev_dbg(acrn_dev.this_device, "VM %u irqfd deinit.\n", vm->vmid); - destroy_workqueue(vm->irqfd_wq); + mutex_lock(&vm->irqfds_lock); - list_for_each_entry_safe(irqfd, next, &vm->irqfds, list) - hsm_irqfd_shutdown(irqfd); + list_for_each_entry_safe(irqfd, next, &vm->irqfds, list) { + list_del_init(&irqfd->list); + hsm_irqfd_queue_shutdown(irqfd); + } mutex_unlock(&vm->irqfds_lock); + + /* Drain all cleanup work before tearing the workqueue down. */ + flush_workqueue(vm->irqfd_wq); + destroy_workqueue(vm->irqfd_wq); } From 160625174ab4058855452d0340ce25f2c76c335b Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 1 May 2026 21:33:28 -0700 Subject: [PATCH 1647/5214] char: dtlk: remove driver for ISA speech synthesizer card The dtlk driver supports the RC Systems DoubleTalk PC ISA speech synthesizer card. It has severe coding style issues and has only received tree-wide fixes and drive-by cleanups in the entire Git history (since Linux 2.6.12-rc2). The same hardware is supported by drivers/accessibility/speakup for screen reader use, but that implementation does not share any code with this driver. Given all of these factors, it is likely the driver is entirely unused. Remove it to reduce future maintenance workload. Note: The removed maintainer is already listed in CREDITS. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260502043341.34324-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../userspace-api/ioctl/ioctl-number.rst | 1 - MAINTAINERS | 7 - arch/powerpc/configs/ppc6xx_defconfig | 1 - drivers/char/Kconfig | 11 - drivers/char/Makefile | 1 - drivers/char/dtlk.c | 663 ------------------ include/linux/dtlk.h | 86 --- 7 files changed, 770 deletions(-) delete mode 100644 drivers/char/dtlk.c delete mode 100644 include/linux/dtlk.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 331223761fff..23ca772fbdf0 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -342,7 +342,6 @@ Code Seq# Include File Comments 0xA2 all uapi/linux/acrn.h ACRN hypervisor 0xA3 80-8F Port ACL in development: -0xA3 90-9F linux/dtlk.h 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem 0xA4 00-1F uapi/asm/sgx.h 0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator diff --git a/MAINTAINERS b/MAINTAINERS index b2040011a386..a61a229670b3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7726,13 +7726,6 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807-vcm.yaml F: drivers/media/i2c/dw9807-vcm.c -DOUBLETALK DRIVER -M: "James R. Van Zandt" -L: blinux-list@redhat.com -S: Maintained -F: drivers/char/dtlk.c -F: include/linux/dtlk.h - DPAA2 DATAPATH I/O (DPIO) DRIVER M: Roy Pledge L: linux-kernel@vger.kernel.org diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index ccabc6e17168..0763da873eb0 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -576,7 +576,6 @@ CONFIG_PPDEV=m CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=m CONFIG_NVRAM=y -CONFIG_DTLK=m CONFIG_IPWIRELESS=m CONFIG_I2C_CHARDEV=m CONFIG_I2C_HYDRA=m diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2a3a37b2cf3c..3063b337907b 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -199,17 +199,6 @@ config NWFLASH source "drivers/char/hw_random/Kconfig" -config DTLK - tristate "Double Talk PC internal speech card support" - depends on ISA - help - This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (). It is also - called the `internal DoubleTalk'. - - To compile this driver as a module, choose M here: the - module will be called dtlk. - config XILINX_HWICAP tristate "Xilinx HWICAP Support" depends on MICROBLAZE diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 47bdc882797a..c9060054ddbc 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o -obj-$(CONFIG_DTLK) += dtlk.o obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_HPET) += hpet.o diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c deleted file mode 100644 index 16618079298a..000000000000 --- a/drivers/char/dtlk.c +++ /dev/null @@ -1,663 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* -*- linux-c -*- - * dtlk.c - DoubleTalk PC driver for Linux - * - * Original author: Chris Pallotta - * Current maintainer: Jim Van Zandt - * - * 2000-03-18 Jim Van Zandt: Fix polling. - * Eliminate dtlk_timer_active flag and separate dtlk_stop_timer - * function. Don't restart timer in dtlk_timer_tick. Restart timer - * in dtlk_poll after every poll. dtlk_poll returns mask (duh). - * Eliminate unused function dtlk_write_byte. Misc. code cleanups. - */ - -/* This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (http://www.rcsys.com/). It was written - based on documentation in their User's Manual file and Developer's - Tools disk. - - The DoubleTalk PC contains four voice synthesizers: text-to-speech - (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It - also has a tone generator. Output data for LPC are written to the - LPC port, and output data for the other modes are written to the - TTS port. - - Two kinds of data can be read from the DoubleTalk: status - information (in response to the "\001?" interrogation command) is - read from the TTS port, and index markers (which mark the progress - of the speech) are read from the LPC port. Not all models of the - DoubleTalk PC implement index markers. Both the TTS and LPC ports - can also display status flags. - - The DoubleTalk PC generates no interrupts. - - These characteristics are mapped into the Unix stream I/O model as - follows: - - "write" sends bytes to the TTS port. It is the responsibility of - the user program to switch modes among TTS, PCM/ADPCM, and CVSD. - This driver was written for use with the text-to-speech - synthesizer. If LPC output is needed some day, other minor device - numbers can be used to select among output modes. - - "read" gets index markers from the LPC port. If the device does - not implement index markers, the read will fail with error EINVAL. - - Status information is available using the DTLK_INTERROGATE ioctl. - - */ - -#include - -#define KERNEL -#include -#include -#include -#include /* for -EBUSY */ -#include /* for request_region */ -#include /* for loops_per_jiffy */ -#include -#include -#include /* for inb_p, outb_p, inb, outb, etc. */ -#include /* for get_user, etc. */ -#include /* for wait_queue */ -#include /* for __init, module_{init,exit} */ -#include /* for EPOLLIN, etc. */ -#include /* local header file for DoubleTalk values */ - -#ifdef TRACING -#define TRACE_TEXT(str) printk(str); -#define TRACE_RET printk(")") -#else /* !TRACING */ -#define TRACE_TEXT(str) ((void) 0) -#define TRACE_RET ((void) 0) -#endif /* TRACING */ - -static DEFINE_MUTEX(dtlk_mutex); -static void dtlk_timer_tick(struct timer_list *unused); - -static int dtlk_major; -static int dtlk_port_lpc; -static int dtlk_port_tts; -static int dtlk_busy; -static int dtlk_has_indexing; -static unsigned int dtlk_portlist[] = -{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0}; -static wait_queue_head_t dtlk_process_list; -static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick); - -/* prototypes for file_operations struct */ -static ssize_t dtlk_read(struct file *, char __user *, - size_t nbytes, loff_t * ppos); -static ssize_t dtlk_write(struct file *, const char __user *, - size_t nbytes, loff_t * ppos); -static __poll_t dtlk_poll(struct file *, poll_table *); -static int dtlk_open(struct inode *, struct file *); -static int dtlk_release(struct inode *, struct file *); -static long dtlk_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); - -static const struct file_operations dtlk_fops = -{ - .owner = THIS_MODULE, - .read = dtlk_read, - .write = dtlk_write, - .poll = dtlk_poll, - .unlocked_ioctl = dtlk_ioctl, - .open = dtlk_open, - .release = dtlk_release, -}; - -/* local prototypes */ -static int dtlk_dev_probe(void); -static struct dtlk_settings *dtlk_interrogate(void); -static int dtlk_readable(void); -static char dtlk_read_lpc(void); -static char dtlk_read_tts(void); -static int dtlk_writeable(void); -static char dtlk_write_bytes(const char *buf, int n); -static char dtlk_write_tts(char); -/* - static void dtlk_handle_error(char, char, unsigned int); - */ - -static ssize_t dtlk_read(struct file *file, char __user *buf, - size_t count, loff_t * ppos) -{ - unsigned int minor = iminor(file_inode(file)); - char ch; - int i = 0, retries; - - TRACE_TEXT("(dtlk_read"); - /* printk("DoubleTalk PC - dtlk_read()\n"); */ - - if (minor != DTLK_MINOR || !dtlk_has_indexing) - return -EINVAL; - - for (retries = 0; retries < loops_per_jiffy; retries++) { - while (i < count && dtlk_readable()) { - ch = dtlk_read_lpc(); - /* printk("dtlk_read() reads 0x%02x\n", ch); */ - if (put_user(ch, buf++)) - return -EFAULT; - i++; - } - if (i) - return i; - if (file->f_flags & O_NONBLOCK) - break; - msleep_interruptible(100); - } - if (retries == loops_per_jiffy) - printk(KERN_ERR "dtlk_read times out\n"); - TRACE_RET; - return -EAGAIN; -} - -static ssize_t dtlk_write(struct file *file, const char __user *buf, - size_t count, loff_t * ppos) -{ - int i = 0, retries = 0, ch; - - TRACE_TEXT("(dtlk_write"); -#ifdef TRACING - printk(" \""); - { - int i, ch; - for (i = 0; i < count; i++) { - if (get_user(ch, buf + i)) - return -EFAULT; - if (' ' <= ch && ch <= '~') - printk("%c", ch); - else - printk("\\%03o", ch); - } - printk("\""); - } -#endif - - if (iminor(file_inode(file)) != DTLK_MINOR) - return -EINVAL; - - while (1) { - while (i < count && !get_user(ch, buf) && - (ch == DTLK_CLEAR || dtlk_writeable())) { - dtlk_write_tts(ch); - buf++; - i++; - if (i % 5 == 0) - /* We yield our time until scheduled - again. This reduces the transfer - rate to 500 bytes/sec, but that's - still enough to keep up with the - speech synthesizer. */ - msleep_interruptible(1); - else { - /* the RDY bit goes zero 2-3 usec - after writing, and goes 1 again - 180-190 usec later. Here, we wait - up to 250 usec for the RDY bit to - go nonzero. */ - for (retries = 0; - retries < loops_per_jiffy / (4000/HZ); - retries++) - if (inb_p(dtlk_port_tts) & - TTS_WRITABLE) - break; - } - retries = 0; - } - if (i == count) - return i; - if (file->f_flags & O_NONBLOCK) - break; - - msleep_interruptible(1); - - if (++retries > 10 * HZ) { /* wait no more than 10 sec - from last write */ - printk("dtlk: write timeout. " - "inb_p(dtlk_port_tts) = 0x%02x\n", - inb_p(dtlk_port_tts)); - TRACE_RET; - return -EBUSY; - } - } - TRACE_RET; - return -EAGAIN; -} - -static __poll_t dtlk_poll(struct file *file, poll_table * wait) -{ - __poll_t mask = 0; - unsigned long expires; - - TRACE_TEXT(" dtlk_poll"); - /* - static long int j; - printk("."); - printk("<%ld>", jiffies-j); - j=jiffies; - */ - poll_wait(file, &dtlk_process_list, wait); - - if (dtlk_has_indexing && dtlk_readable()) { - timer_delete(&dtlk_timer); - mask = EPOLLIN | EPOLLRDNORM; - } - if (dtlk_writeable()) { - timer_delete(&dtlk_timer); - mask |= EPOLLOUT | EPOLLWRNORM; - } - /* there are no exception conditions */ - - /* There won't be any interrupts, so we set a timer instead. */ - expires = jiffies + 3*HZ / 100; - mod_timer(&dtlk_timer, expires); - - return mask; -} - -static void dtlk_timer_tick(struct timer_list *unused) -{ - TRACE_TEXT(" dtlk_timer_tick"); - wake_up_interruptible(&dtlk_process_list); -} - -static long dtlk_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) -{ - char __user *argp = (char __user *)arg; - struct dtlk_settings *sp; - char portval; - TRACE_TEXT(" dtlk_ioctl"); - - switch (cmd) { - - case DTLK_INTERROGATE: - mutex_lock(&dtlk_mutex); - sp = dtlk_interrogate(); - mutex_unlock(&dtlk_mutex); - if (copy_to_user(argp, sp, sizeof(struct dtlk_settings))) - return -EINVAL; - return 0; - - case DTLK_STATUS: - portval = inb_p(dtlk_port_tts); - return put_user(portval, argp); - - default: - return -EINVAL; - } -} - -/* Note that nobody ever sets dtlk_busy... */ -static int dtlk_open(struct inode *inode, struct file *file) -{ - TRACE_TEXT("(dtlk_open"); - - switch (iminor(inode)) { - case DTLK_MINOR: - if (dtlk_busy) - return -EBUSY; - return stream_open(inode, file); - - default: - return -ENXIO; - } -} - -static int dtlk_release(struct inode *inode, struct file *file) -{ - TRACE_TEXT("(dtlk_release"); - - switch (iminor(inode)) { - case DTLK_MINOR: - break; - - default: - break; - } - TRACE_RET; - - timer_delete_sync(&dtlk_timer); - - return 0; -} - -static int __init dtlk_init(void) -{ - int err; - - dtlk_port_lpc = 0; - dtlk_port_tts = 0; - dtlk_busy = 0; - dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops); - if (dtlk_major < 0) { - printk(KERN_ERR "DoubleTalk PC - cannot register device\n"); - return dtlk_major; - } - err = dtlk_dev_probe(); - if (err) { - unregister_chrdev(dtlk_major, "dtlk"); - return err; - } - printk(", MAJOR %d\n", dtlk_major); - - init_waitqueue_head(&dtlk_process_list); - - return 0; -} - -static void __exit dtlk_cleanup (void) -{ - dtlk_write_bytes("goodbye", 8); - msleep_interruptible(500); /* nap 0.50 sec but - could be awakened - earlier by - signals... */ - - dtlk_write_tts(DTLK_CLEAR); - unregister_chrdev(dtlk_major, "dtlk"); - release_region(dtlk_port_lpc, DTLK_IO_EXTENT); -} - -module_init(dtlk_init); -module_exit(dtlk_cleanup); - -/* ------------------------------------------------------------------------ */ - -static int dtlk_readable(void) -{ -#ifdef TRACING - printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies); -#endif - return inb_p(dtlk_port_lpc) != 0x7f; -} - -static int dtlk_writeable(void) -{ - /* TRACE_TEXT(" dtlk_writeable"); */ -#ifdef TRACINGMORE - printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0); -#endif - return inb_p(dtlk_port_tts) & TTS_WRITABLE; -} - -static int __init dtlk_dev_probe(void) -{ - unsigned int testval = 0; - int i = 0; - struct dtlk_settings *sp; - - if (dtlk_port_lpc | dtlk_port_tts) - return -EBUSY; - - for (i = 0; dtlk_portlist[i]; i++) { -#if 0 - printk("DoubleTalk PC - Port %03x = %04x\n", - dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i]))); -#endif - - if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, - "dtlk")) - continue; - testval = inw_p(dtlk_portlist[i]); - if ((testval &= 0xfbff) == 0x107f) { - dtlk_port_lpc = dtlk_portlist[i]; - dtlk_port_tts = dtlk_port_lpc + 1; - - sp = dtlk_interrogate(); - printk("DoubleTalk PC at %03x-%03x, " - "ROM version %s, serial number %u", - dtlk_portlist[i], dtlk_portlist[i] + - DTLK_IO_EXTENT - 1, - sp->rom_version, sp->serial_number); - - /* put LPC port into known state, so - dtlk_readable() gives valid result */ - outb_p(0xff, dtlk_port_lpc); - - /* INIT string and index marker */ - dtlk_write_bytes("\036\1@\0\0012I\r", 8); - /* posting an index takes 18 msec. Here, we - wait up to 100 msec to see whether it - appears. */ - msleep_interruptible(100); - dtlk_has_indexing = dtlk_readable(); -#ifdef TRACING - printk(", indexing %d\n", dtlk_has_indexing); -#endif -#ifdef INSCOPE - { -/* This macro records ten samples read from the LPC port, for later display */ -#define LOOK \ -for (i = 0; i < 10; i++) \ - { \ - buffer[b++] = inb_p(dtlk_port_lpc); \ - __delay(loops_per_jiffy/(1000000/HZ)); \ - } - char buffer[1000]; - int b = 0, i, j; - - LOOK - outb_p(0xff, dtlk_port_lpc); - buffer[b++] = 0; - LOOK - dtlk_write_bytes("\0012I\r", 4); - buffer[b++] = 0; - __delay(50 * loops_per_jiffy / (1000/HZ)); - outb_p(0xff, dtlk_port_lpc); - buffer[b++] = 0; - LOOK - - printk("\n"); - for (j = 0; j < b; j++) - printk(" %02x", buffer[j]); - printk("\n"); - } -#endif /* INSCOPE */ - -#ifdef OUTSCOPE - { -/* This macro records ten samples read from the TTS port, for later display */ -#define LOOK \ -for (i = 0; i < 10; i++) \ - { \ - buffer[b++] = inb_p(dtlk_port_tts); \ - __delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \ - } - char buffer[1000]; - int b = 0, i, j; - - mdelay(10); /* 10 ms */ - LOOK - outb_p(0x03, dtlk_port_tts); - buffer[b++] = 0; - LOOK - LOOK - - printk("\n"); - for (j = 0; j < b; j++) - printk(" %02x", buffer[j]); - printk("\n"); - } -#endif /* OUTSCOPE */ - - dtlk_write_bytes("Double Talk found", 18); - - return 0; - } - release_region(dtlk_portlist[i], DTLK_IO_EXTENT); - } - - printk(KERN_INFO "DoubleTalk PC - not found\n"); - return -ENODEV; -} - -/* - static void dtlk_handle_error(char op, char rc, unsigned int minor) - { - printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", - minor, op, rc); - return; - } - */ - -/* interrogate the DoubleTalk PC and return its settings */ -static struct dtlk_settings *dtlk_interrogate(void) -{ - unsigned char *t; - static char buf[sizeof(struct dtlk_settings) + 1]; - int total, i; - static struct dtlk_settings status; - TRACE_TEXT("(dtlk_interrogate"); - dtlk_write_bytes("\030\001?", 3); - for (total = 0, i = 0; i < 50; i++) { - buf[total] = dtlk_read_tts(); - if (total > 2 && buf[total] == 0x7f) - break; - if (total < sizeof(struct dtlk_settings)) - total++; - } - /* - if (i==50) printk("interrogate() read overrun\n"); - for (i=0; i DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_read_tts() timeout\n"); - - ch = inb_p(dtlk_port_tts); /* input from TTS port */ - ch &= 0x7f; - outb_p(ch, dtlk_port_tts); - - retries = 0; - do { - portval = inb_p(dtlk_port_tts); - } while ((portval & TTS_READABLE) != 0 && - retries++ < DTLK_MAX_RETRIES); - if (retries > DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_read_tts() timeout\n"); - - TRACE_RET; - return ch; -} - -static char dtlk_read_lpc(void) -{ - int retries = 0; - char ch; - TRACE_TEXT("(dtlk_read_lpc"); - - /* no need to test -- this is only called when the port is readable */ - - ch = inb_p(dtlk_port_lpc); /* input from LPC port */ - - outb_p(0xff, dtlk_port_lpc); - - /* acknowledging a read takes 3-4 - usec. Here, we wait up to 20 usec - for the acknowledgement */ - retries = (loops_per_jiffy * 20) / (1000000/HZ); - while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0); - if (retries == 0) - printk(KERN_ERR "dtlk_read_lpc() timeout\n"); - - TRACE_RET; - return ch; -} - -/* write n bytes to tts port */ -static char dtlk_write_bytes(const char *buf, int n) -{ - char val = 0; - /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */ - TRACE_TEXT("(dtlk_write_bytes"); - while (n-- > 0) - val = dtlk_write_tts(*buf++); - TRACE_RET; - return val; -} - -static char dtlk_write_tts(char ch) -{ - int retries = 0; -#ifdef TRACINGMORE - printk(" dtlk_write_tts("); - if (' ' <= ch && ch <= '~') - printk("'%c'", ch); - else - printk("0x%02x", ch); -#endif - if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */ - while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 && - retries++ < DTLK_MAX_RETRIES) /* DT ready? */ - ; - if (retries > DTLK_MAX_RETRIES) - printk(KERN_ERR "dtlk_write_tts() timeout\n"); - - outb_p(ch, dtlk_port_tts); /* output to TTS port */ - /* the RDY bit goes zero 2-3 usec after writing, and goes - 1 again 180-190 usec later. Here, we wait up to 10 - usec for the RDY bit to go zero. */ - for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++) - if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0) - break; - -#ifdef TRACINGMORE - printk(")\n"); -#endif - return 0; -} - -MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/dtlk.h b/include/linux/dtlk.h deleted file mode 100644 index 27b95e70bde3..000000000000 --- a/include/linux/dtlk.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#define DTLK_MINOR 0 -#define DTLK_IO_EXTENT 0x02 - - /* ioctl's use magic number of 0xa3 */ -#define DTLK_INTERROGATE 0xa390 /* get settings from the DoubleTalk */ -#define DTLK_STATUS 0xa391 /* get status from the DoubleTalk */ - - -#define DTLK_CLEAR 0x18 /* stops speech */ - -#define DTLK_MAX_RETRIES (loops_per_jiffy/(10000/HZ)) - - /* TTS Port Status Flags */ -#define TTS_READABLE 0x80 /* mask for bit which is nonzero if a - byte can be read from the TTS port */ -#define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero - while DoubleTalk is producing - output with TTS, PCM or CVSD - synthesizers or tone generators - (that is, all but LPC) */ -#define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit, - which falls to zero up to 0.4 sec - before speech stops */ -#define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to - 1, indicates the TTS port is ready - to accept a byte of data. The RDY - bit goes zero 2-3 usec after - writing, and goes 1 again 180-190 - usec later. */ -#define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1, - indicates that less than 300 free - bytes are available in the TTS - input buffer. AF is always 0 in the - PCM, TGN and CVSD modes. */ -#define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1, - indicates that less than 300 bytes - of data remain in DoubleTalk's - input (TTS or PCM) buffer. AE is - always 1 in the TGN and CVSD - modes. */ - - /* LPC speak commands */ -#define LPC_5220_NORMAL 0x60 /* 5220 format decoding table, normal rate */ -#define LPC_5220_FAST 0x64 /* 5220 format decoding table, fast rate */ -#define LPC_D6_NORMAL 0x20 /* D6 format decoding table, normal rate */ -#define LPC_D6_FAST 0x24 /* D6 format decoding table, fast rate */ - - /* LPC Port Status Flags (valid only after one of the LPC - speak commands) */ -#define LPC_SPEAKING 0x80 /* mask for TS bit: When set to 1, - indicates the LPC synthesizer is - producing speech.*/ -#define LPC_BUFFER_LOW 0x40 /* mask for BL bit: When set to 1, - indicates that the hardware LPC - data buffer has less than 30 bytes - remaining. (Total internal buffer - size = 4096 bytes.) */ -#define LPC_BUFFER_EMPTY 0x20 /* mask for BE bit: When set to 1, - indicates that the LPC data buffer - ran out of data (error condition if - TS is also 1). */ - - /* data returned by Interrogate command */ -struct dtlk_settings -{ - unsigned short serial_number; /* 0-7Fh:0-7Fh */ - unsigned char rom_version[24]; /* null terminated string */ - unsigned char mode; /* 0=Character; 1=Phoneme; 2=Text */ - unsigned char punc_level; /* nB; 0-7 */ - unsigned char formant_freq; /* nF; 0-9 */ - unsigned char pitch; /* nP; 0-99 */ - unsigned char speed; /* nS; 0-9 */ - unsigned char volume; /* nV; 0-9 */ - unsigned char tone; /* nX; 0-2 */ - unsigned char expression; /* nE; 0-9 */ - unsigned char ext_dict_loaded; /* 1=exception dictionary loaded */ - unsigned char ext_dict_status; /* 1=exception dictionary enabled */ - unsigned char free_ram; /* # pages (truncated) remaining for - text buffer */ - unsigned char articulation; /* nA; 0-9 */ - unsigned char reverb; /* nR; 0-9 */ - unsigned char eob; /* 7Fh value indicating end of - parameter block */ - unsigned char has_indexing; /* nonzero if indexing is implemented */ -}; From 05cb9529a43076538f9bf22816af280a8315b057 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sat, 2 May 2026 20:58:16 -0700 Subject: [PATCH 1648/5214] char: applicom: remove low-quality, unused driver The applicom driver supports PCI Profibus cards from Applicom, later acquired by Molex. It has severe coding style issues and has attracted a number of bug and security fixes over the years, despite the fact that no one appears to be using it. It was broken from at least the beginning of Git history (Linux 2.6.12-rc2 in April 2005) until October 2008, when a fatal bug was fixed in commit bc20589bf1c6 ("applicom.c: fix apparently-broken code in do_ac_read()"). In the commit message, the author commented that no one they knew was able to test the change. Since then, there have been no commits that indicate the driver is being used. Later PCI and PCI-Express Applicom Profibus cards only officially support Windows [1], and even the PCI-Express cards have been discontinued [2]. Given all these factors, remove the driver to reduce future maintenance workload. [1] https://www.sarcitalia.it/file_upload/prodotti//PCIE1500S7_PFB_987651-3769_0876250001505823933.pdf [2] https://us.rs-online.com/product/molex-woodhead-brad/112011-5026/70631928/ Signed-off-by: Ethan Nelson-Moore Acked-by: David Woodhouse Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260503035824.24078-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/devices.txt | 1 - drivers/char/Kconfig | 15 - drivers/char/Makefile | 1 - drivers/char/applicom.c | 857 -------------------------- drivers/char/applicom.h | 86 --- 5 files changed, 960 deletions(-) delete mode 100644 drivers/char/applicom.c delete mode 100644 drivers/char/applicom.h diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt index 440633642fea..f544399675fa 100644 --- a/Documentation/admin-guide/devices.txt +++ b/Documentation/admin-guide/devices.txt @@ -291,7 +291,6 @@ 154 = /dev/pmu Macintosh PowerBook power manager 155 = 156 = /dev/lcd Front panel LCD display - 157 = /dev/ac Applicom Intl Profibus card 158 = /dev/nwbutton Netwinder external button 159 = /dev/nwdebug Netwinder debug interface 160 = /dev/nwflash Netwinder flash memory diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3063b337907b..9865227af167 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -209,21 +209,6 @@ config XILINX_HWICAP If unsure, say N. -config APPLICOM - tristate "Applicom intelligent fieldbus card support" - depends on PCI - help - This driver provides the kernel-side support for the intelligent - fieldbus cards made by Applicom International. More information - about these cards can be found on the WWW at the address - , or by email from David Woodhouse - . - - To compile this driver as a module, choose M here: the - module will be called applicom. - - If unsure, say N. - config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" depends on X86_32 && PCI && INPUT && HAS_IOPORT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index c9060054ddbc..a46d7bf7c4c8 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o -obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c deleted file mode 100644 index c138c468f3a4..000000000000 --- a/drivers/char/applicom.c +++ /dev/null @@ -1,857 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Derived from Applicom driver ac.c for SCO Unix */ -/* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ -/* dwmw2@infradead.org 30/8/98 */ -/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */ -/* This module is for Linux 2.1 and 2.2 series kernels. */ -/*****************************************************************************/ -/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt */ -/* ceci pour reseter correctement apres une sortie sauvage */ -/* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption, */ -/* LoopCount n'etait pas initialise a 0. */ -/* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte */ -/* pour liberer le bus */ -/* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */ -/* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter */ -/* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3 */ -/* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes */ -/* addresses de base des cartes, IOCTL 6 plus complet */ -/* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification */ -/* de code autre que le texte V2.6.1 en V2.8.0 */ -/*****************************************************************************/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "applicom.h" - - -/* NOTE: We use for loops with {write,read}b() instead of - memcpy_{from,to}io throughout this driver. This is because - the board doesn't correctly handle word accesses - only - bytes. -*/ - - -#undef DEBUG - -#define MAX_BOARD 8 /* maximum of pc board possible */ -#define MAX_ISA_BOARD 4 -#define LEN_RAM_IO 0x800 - -#ifndef PCI_VENDOR_ID_APPLICOM -#define PCI_VENDOR_ID_APPLICOM 0x1389 -#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 -#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 -#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 -#endif - -static DEFINE_MUTEX(ac_mutex); -static char *applicom_pci_devnames[] = { - "PCI board", - "PCI2000IBS / PCI2000CAN", - "PCI2000PFB" -}; - -static const struct pci_device_id applicom_pci_tbl[] = { - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) }, - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) }, - { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, applicom_pci_tbl); - -MODULE_AUTHOR("David Woodhouse & Applicom International"); -MODULE_DESCRIPTION("Driver for Applicom Profibus card"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(AC_MINOR); - -static struct applicom_board { - unsigned long PhysIO; - void __iomem *RamIO; - wait_queue_head_t FlagSleepSend; - long irq; - spinlock_t mutex; -} apbs[MAX_BOARD]; - -static unsigned int irq; /* interrupt number IRQ */ -static unsigned long mem; /* physical segment of board */ - -module_param_hw(irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ of the Applicom board"); -module_param_hw(mem, ulong, iomem, 0); -MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board"); - -static unsigned int numboards; /* number of installed boards */ -static volatile unsigned char Dummy; -static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec); -static unsigned int WriteErrorCount; /* number of write error */ -static unsigned int ReadErrorCount; /* number of read error */ -static unsigned int DeviceErrorCount; /* number of device error */ - -static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *); -static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *); -static long ac_ioctl(struct file *, unsigned int, unsigned long); -static irqreturn_t ac_interrupt(int, void *); - -static const struct file_operations ac_fops = { - .owner = THIS_MODULE, - .read = ac_read, - .write = ac_write, - .unlocked_ioctl = ac_ioctl, -}; - -static struct miscdevice ac_miscdev = { - AC_MINOR, - "ac", - &ac_fops -}; - -static int dummy; /* dev_id for request_irq() */ - -static int ac_register_board(unsigned long physloc, void __iomem *loc, - unsigned char boardno) -{ - volatile unsigned char byte_reset_it; - - if((readb(loc + CONF_END_TEST) != 0x00) || - (readb(loc + CONF_END_TEST + 1) != 0x55) || - (readb(loc + CONF_END_TEST + 2) != 0xAA) || - (readb(loc + CONF_END_TEST + 3) != 0xFF)) - return 0; - - if (!boardno) - boardno = readb(loc + NUMCARD_OWNER_TO_PC); - - if (!boardno || boardno > MAX_BOARD) { - printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", - boardno, physloc, MAX_BOARD); - return 0; - } - - if (apbs[boardno - 1].RamIO) { - printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", - boardno, physloc, boardno, apbs[boardno-1].PhysIO); - return 0; - } - - boardno--; - - apbs[boardno].PhysIO = physloc; - apbs[boardno].RamIO = loc; - init_waitqueue_head(&apbs[boardno].FlagSleepSend); - spin_lock_init(&apbs[boardno].mutex); - byte_reset_it = readb(loc + RAM_IT_TO_PC); - - numboards++; - return boardno + 1; -} - -static void __exit applicom_exit(void) -{ - unsigned int i; - - misc_deregister(&ac_miscdev); - - for (i = 0; i < MAX_BOARD; i++) { - - if (!apbs[i].RamIO) - continue; - - if (apbs[i].irq) - free_irq(apbs[i].irq, &dummy); - - iounmap(apbs[i].RamIO); - } -} - -static int __init applicom_init(void) -{ - int i, numisa = 0; - struct pci_dev *dev = NULL; - void __iomem *RamIO; - int boardno, ret; - - printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n"); - - /* No mem and irq given - check for a PCI card */ - - while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) { - - if (!pci_match_id(applicom_pci_tbl, dev)) - continue; - - if (pci_enable_device(dev)) { - pci_dev_put(dev); - return -EIO; - } - - RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO); - - if (!RamIO) { - printk(KERN_INFO "ac.o: Failed to ioremap PCI memory " - "space at 0x%llx\n", - (unsigned long long)pci_resource_start(dev, 0)); - pci_disable_device(dev); - pci_dev_put(dev); - return -EIO; - } - - printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n", - applicom_pci_devnames[dev->device-1], - (unsigned long long)pci_resource_start(dev, 0), - dev->irq); - - boardno = ac_register_board(pci_resource_start(dev, 0), - RamIO, 0); - if (!boardno) { - printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n"); - iounmap(RamIO); - pci_disable_device(dev); - continue; - } - - if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) { - printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); - iounmap(RamIO); - pci_disable_device(dev); - apbs[boardno - 1].RamIO = NULL; - continue; - } - - /* Enable interrupts. */ - - writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC); - - apbs[boardno - 1].irq = dev->irq; - } - - /* Finished with PCI cards. If none registered, - * and there was no mem/irq specified, exit */ - - if (!mem || !irq) { - if (numboards) - goto fin; - else { - printk(KERN_INFO "ac.o: No PCI boards found.\n"); - printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n"); - return -ENXIO; - } - } - - /* Now try the specified ISA cards */ - - for (i = 0; i < MAX_ISA_BOARD; i++) { - RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO); - - if (!RamIO) { - printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1); - continue; - } - - if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i), - RamIO,i+1))) { - iounmap(RamIO); - continue; - } - - printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq); - - if (!numisa) { - if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) { - printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq); - iounmap(RamIO); - apbs[boardno - 1].RamIO = NULL; - } - else - apbs[boardno - 1].irq = irq; - } - else - apbs[boardno - 1].irq = 0; - - numisa++; - } - - if (!numisa) - printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found " - "at mem 0x%lx\n", mem); - - fin: - init_waitqueue_head(&FlagSleepRec); - - WriteErrorCount = 0; - ReadErrorCount = 0; - DeviceErrorCount = 0; - - if (numboards) { - ret = misc_register(&ac_miscdev); - if (ret) { - printk(KERN_WARNING "ac.o: Unable to register misc device\n"); - goto out; - } - for (i = 0; i < MAX_BOARD; i++) { - int serial; - char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; - - if (!apbs[i].RamIO) - continue; - - for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) - boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); - - boardname[serial] = 0; - - - printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d", - i+1, boardname, - (int)(readb(apbs[i].RamIO + VERS) >> 4), - (int)(readb(apbs[i].RamIO + VERS) & 0xF)); - - serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); - - if (serial != 0) - printk(" S/N %d\n", serial); - else - printk("\n"); - } - return 0; - } - - else - return -ENXIO; - -out: - for (i = 0; i < MAX_BOARD; i++) { - if (!apbs[i].RamIO) - continue; - if (apbs[i].irq) - free_irq(apbs[i].irq, &dummy); - iounmap(apbs[i].RamIO); - } - return ret; -} - -module_init(applicom_init); -module_exit(applicom_exit); - - -static ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) -{ - unsigned int NumCard; /* Board number 1 -> 8 */ - unsigned int IndexCard; /* Index board number 0 -> 7 */ - unsigned char TicCard; /* Board TIC to send */ - unsigned long flags; /* Current priority */ - struct st_ram_io st_loc; - struct mailbox tmpmailbox; -#ifdef DEBUG - int c; -#endif - DECLARE_WAITQUEUE(wait, current); - - if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { - static int warncount = 5; - if (warncount) { - printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd\n", - count, sizeof(struct st_ram_io) + sizeof(struct mailbox)); - warncount--; - } - return -EINVAL; - } - - if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) - return -EFAULT; - - if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)], - sizeof(struct mailbox))) - return -EFAULT; - - NumCard = st_loc.num_card; /* board number to send */ - TicCard = st_loc.tic_des_from_pc; /* tic number to send */ - IndexCard = NumCard - 1; - - if (IndexCard >= MAX_BOARD) - return -EINVAL; - IndexCard = array_index_nospec(IndexCard, MAX_BOARD); - - if (!apbs[IndexCard].RamIO) - return -EINVAL; - -#ifdef DEBUG - printk("Write to applicom card #%d. struct st_ram_io follows:", - IndexCard+1); - - for (c = 0; c < sizeof(struct st_ram_io);) { - - printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]); - - for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { - printk(" %2.2X", ((unsigned char *) &st_loc)[c]); - } - } - - printk("\nstruct mailbox follows:"); - - for (c = 0; c < sizeof(struct mailbox);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]); - - for (c++; c % 8 && c < sizeof(struct mailbox); c++) { - printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]); - } - } - - printk("\n"); -#endif - - spin_lock_irqsave(&apbs[IndexCard].mutex, flags); - - /* Test octet ready correct */ - if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) { - Dummy = readb(apbs[IndexCard].RamIO + VERS); - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n", - IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY)); - DeviceErrorCount++; - return -EIO; - } - - /* Place ourselves on the wait queue */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); - - /* Check whether the card is ready for us */ - while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) { - Dummy = readb(apbs[IndexCard].RamIO + VERS); - /* It's busy. Sleep. */ - - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - schedule(); - if (signal_pending(current)) { - remove_wait_queue(&apbs[IndexCard].FlagSleepSend, - &wait); - return -EINTR; - } - spin_lock_irqsave(&apbs[IndexCard].mutex, flags); - set_current_state(TASK_INTERRUPTIBLE); - } - - /* We may not have actually slept */ - set_current_state(TASK_RUNNING); - remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); - - writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - - /* Which is best - lock down the pages with rawio and then - copy directly, or use bounce buffers? For now we do the latter - because it works with 2.2 still */ - { - unsigned char *from = (unsigned char *) &tmpmailbox; - void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC; - int c; - - for (c = 0; c < sizeof(struct mailbox); c++) - writeb(*(from++), to++); - } - - writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC); - writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC); - writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC); - writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); - writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - Dummy = readb(apbs[IndexCard].RamIO + VERS); - spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); - return 0; -} - -static int do_ac_read(int IndexCard, char __user *buf, - struct st_ram_io *st_loc, struct mailbox *mailbox) -{ - void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC; - unsigned char *to = (unsigned char *)mailbox; -#ifdef DEBUG - int c; -#endif - - st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC); - st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); - - - { - int c; - - for (c = 0; c < sizeof(struct mailbox); c++) - *(to++) = readb(from++); - } - writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC); - writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); - writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), - apbs[IndexCard].RamIO + TIC_ACK_FROM_PC); - writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); - writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY); - writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - Dummy = readb(apbs[IndexCard].RamIO + VERS); - -#ifdef DEBUG - printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard); - - for (c = 0; c < sizeof(struct st_ram_io);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]); - - for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { - printk(" %2.2X", ((unsigned char *)st_loc)[c]); - } - } - - printk("\nstruct mailbox follows:"); - - for (c = 0; c < sizeof(struct mailbox);) { - printk("\n%5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]); - - for (c++; c % 8 && c < sizeof(struct mailbox); c++) { - printk(" %2.2X", ((unsigned char *)mailbox)[c]); - } - } - printk("\n"); -#endif - return (sizeof(struct st_ram_io) + sizeof(struct mailbox)); -} - -static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr) -{ - unsigned long flags; - unsigned int i; - unsigned char tmp; - int ret = 0; - DECLARE_WAITQUEUE(wait, current); -#ifdef DEBUG - int loopcount=0; -#endif - /* No need to ratelimit this. Only root can trigger it anyway */ - if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { - printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd\n", - count,sizeof(struct st_ram_io) + sizeof(struct mailbox)); - return -EINVAL; - } - - while(1) { - /* Stick ourself on the wait queue */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&FlagSleepRec, &wait); - - /* Scan each board, looking for one which has a packet for us */ - for (i=0; i < MAX_BOARD; i++) { - if (!apbs[i].RamIO) - continue; - spin_lock_irqsave(&apbs[i].mutex, flags); - - tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY); - - if (tmp == 2) { - struct st_ram_io st_loc; - struct mailbox mailbox; - - /* Got a packet for us */ - memset(&st_loc, 0, sizeof(st_loc)); - ret = do_ac_read(i, buf, &st_loc, &mailbox); - spin_unlock_irqrestore(&apbs[i].mutex, flags); - set_current_state(TASK_RUNNING); - remove_wait_queue(&FlagSleepRec, &wait); - - if (copy_to_user(buf, &st_loc, sizeof(st_loc))) - return -EFAULT; - if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox))) - return -EFAULT; - return tmp; - } - - if (tmp > 2) { - /* Got an error */ - Dummy = readb(apbs[i].RamIO + VERS); - - spin_unlock_irqrestore(&apbs[i].mutex, flags); - set_current_state(TASK_RUNNING); - remove_wait_queue(&FlagSleepRec, &wait); - - printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", - i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); - DeviceErrorCount++; - return -EIO; - } - - /* Nothing for us. Try the next board */ - Dummy = readb(apbs[i].RamIO + VERS); - spin_unlock_irqrestore(&apbs[i].mutex, flags); - - } /* per board */ - - /* OK - No boards had data for us. Sleep now */ - - schedule(); - remove_wait_queue(&FlagSleepRec, &wait); - - if (signal_pending(current)) - return -EINTR; - -#ifdef DEBUG - if (loopcount++ > 2) { - printk(KERN_DEBUG "Looping in ac_read. loopcount %d\n", loopcount); - } -#endif - } -} - -static irqreturn_t ac_interrupt(int vec, void *dev_instance) -{ - unsigned int i; - unsigned int FlagInt; - unsigned int LoopCount; - int handled = 0; - - // printk("Applicom interrupt on IRQ %d occurred\n", vec); - - LoopCount = 0; - - do { - FlagInt = 0; - for (i = 0; i < MAX_BOARD; i++) { - - /* Skip if this board doesn't exist */ - if (!apbs[i].RamIO) - continue; - - spin_lock(&apbs[i].mutex); - - /* Skip if this board doesn't want attention */ - if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) { - spin_unlock(&apbs[i].mutex); - continue; - } - - handled = 1; - FlagInt = 1; - writeb(0, apbs[i].RamIO + RAM_IT_TO_PC); - - if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) { - printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", - i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); - DeviceErrorCount++; - } - - if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && - (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) { - - printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", - i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY)); - DeviceErrorCount++; - } - - if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) { /* mailbox sent by the card ? */ - if (waitqueue_active(&FlagSleepRec)) { - wake_up_interruptible(&FlagSleepRec); - } - } - - if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) { /* ram i/o free for write by pc ? */ - if (waitqueue_active(&apbs[i].FlagSleepSend)) { /* process sleep during read ? */ - wake_up_interruptible(&apbs[i].FlagSleepSend); - } - } - Dummy = readb(apbs[i].RamIO + VERS); - - if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) { - /* There's another int waiting on this card */ - spin_unlock(&apbs[i].mutex); - i--; - } else { - spin_unlock(&apbs[i].mutex); - } - } - if (FlagInt) - LoopCount = 0; - else - LoopCount++; - } while(LoopCount < 2); - return IRQ_RETVAL(handled); -} - - - -static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg) - -{ /* @ ADG ou ATO selon le cas */ - int i; - unsigned char IndexCard; - void __iomem *pmem; - int ret = 0; - static int warncount = 10; - volatile unsigned char byte_reset_it; - struct st_ram_io *adgl; - void __user *argp = (void __user *)arg; - - /* In general, the device is only openable by root anyway, so we're not - particularly concerned that bogus ioctls can flood the console. */ - - adgl = memdup_user(argp, sizeof(struct st_ram_io)); - if (IS_ERR(adgl)) - return PTR_ERR(adgl); - - mutex_lock(&ac_mutex); - IndexCard = adgl->num_card-1; - - if (cmd != 6 && IndexCard >= MAX_BOARD) - goto err; - IndexCard = array_index_nospec(IndexCard, MAX_BOARD); - - if (cmd != 6 && !apbs[IndexCard].RamIO) - goto err; - - switch (cmd) { - - case 0: - pmem = apbs[IndexCard].RamIO; - for (i = 0; i < sizeof(struct st_ram_io); i++) - ((unsigned char *)adgl)[i]=readb(pmem++); - if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 1: - pmem = apbs[IndexCard].RamIO + CONF_END_TEST; - for (i = 0; i < 4; i++) - adgl->conf_end_test[i] = readb(pmem++); - for (i = 0; i < 2; i++) - adgl->error_code[i] = readb(pmem++); - for (i = 0; i < 4; i++) - adgl->parameter_error[i] = readb(pmem++); - pmem = apbs[IndexCard].RamIO + VERS; - adgl->vers = readb(pmem); - pmem = apbs[IndexCard].RamIO + TYPE_CARD; - for (i = 0; i < 20; i++) - adgl->reserv1[i] = readb(pmem++); - *(int *)&adgl->reserv1[20] = - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) ); - - if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 2: - pmem = apbs[IndexCard].RamIO + CONF_END_TEST; - for (i = 0; i < 10; i++) - writeb(0xff, pmem++); - writeb(adgl->data_from_pc_ready, - apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - - for (i = 0; i < MAX_BOARD; i++) { - if (apbs[i].RamIO) { - byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC); - } - } - break; - case 3: - pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC; - writeb(adgl->tic_des_from_pc, pmem); - break; - case 4: - pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC; - adgl->tic_owner_to_pc = readb(pmem++); - adgl->numcard_owner_to_pc = readb(pmem); - if (copy_to_user(argp, adgl,sizeof(struct st_ram_io))) - ret = -EFAULT; - break; - case 5: - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); - writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); - writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); - writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); - break; - case 6: - printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n"); - printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards); - printk(KERN_INFO "Segment of board ........... %X\n", (int) mem); - printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq); - for (i = 0; i < MAX_BOARD; i++) { - int serial; - char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; - - if (!apbs[i].RamIO) - continue; - - for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) - boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); - boardname[serial] = 0; - - printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", - i+1, - (int)(readb(apbs[i].RamIO + VERS) >> 4), - (int)(readb(apbs[i].RamIO + VERS) & 0xF), - boardname); - - - serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + - (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); - - if (serial != 0) - printk(" S/N %d\n", serial); - else - printk("\n"); - } - if (DeviceErrorCount != 0) - printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount); - if (ReadErrorCount != 0) - printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount); - if (WriteErrorCount != 0) - printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount); - if (waitqueue_active(&FlagSleepRec)) - printk(KERN_INFO "Process in read pending\n"); - for (i = 0; i < MAX_BOARD; i++) { - if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend)) - printk(KERN_INFO "Process in write pending board %d\n",i+1); - } - break; - default: - ret = -ENOTTY; - break; - } - - if (cmd != 6) - Dummy = readb(apbs[IndexCard].RamIO + VERS); - - kfree(adgl); - mutex_unlock(&ac_mutex); - return ret; - -err: - if (warncount) { - pr_warn("APPLICOM driver IOCTL, bad board number %d\n", - (int)IndexCard + 1); - warncount--; - } - kfree(adgl); - mutex_unlock(&ac_mutex); - return -EINVAL; - -} - diff --git a/drivers/char/applicom.h b/drivers/char/applicom.h deleted file mode 100644 index 282e08f159d5..000000000000 --- a/drivers/char/applicom.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* $Id: applicom.h,v 1.2 1999/08/28 15:09:49 dwmw2 Exp $ */ - - -#ifndef __LINUX_APPLICOM_H__ -#define __LINUX_APPLICOM_H__ - - -#define DATA_TO_PC_READY 0x00 -#define TIC_OWNER_TO_PC 0x01 -#define NUMCARD_OWNER_TO_PC 0x02 -#define TIC_DES_TO_PC 0x03 -#define NUMCARD_DES_TO_PC 0x04 -#define DATA_FROM_PC_READY 0x05 -#define TIC_OWNER_FROM_PC 0x06 -#define NUMCARD_OWNER_FROM_PC 0x07 -#define TIC_DES_FROM_PC 0x08 -#define NUMCARD_DES_FROM_PC 0x09 -#define ACK_FROM_PC_READY 0x0E -#define TIC_ACK_FROM_PC 0x0F -#define NUMCARD_ACK_FROM_PC 0x010 -#define TYP_ACK_FROM_PC 0x011 -#define CONF_END_TEST 0x012 -#define ERROR_CODE 0x016 -#define PARAMETER_ERROR 0x018 -#define VERS 0x01E -#define RAM_TO_PC 0x040 -#define RAM_FROM_PC 0x0170 -#define TYPE_CARD 0x03C0 -#define SERIAL_NUMBER 0x03DA -#define RAM_IT_FROM_PC 0x03FE -#define RAM_IT_TO_PC 0x03FF - -struct mailbox{ - u16 stjb_codef; /* offset 00 */ - s16 stjb_status; /* offset 02 */ - u16 stjb_ticuser_root; /* offset 04 */ - u8 stjb_piduser[4]; /* offset 06 */ - u16 stjb_mode; /* offset 0A */ - u16 stjb_time; /* offset 0C */ - u16 stjb_stop; /* offset 0E */ - u16 stjb_nfonc; /* offset 10 */ - u16 stjb_ncard; /* offset 12 */ - u16 stjb_nchan; /* offset 14 */ - u16 stjb_nes; /* offset 16 */ - u16 stjb_nb; /* offset 18 */ - u16 stjb_typvar; /* offset 1A */ - u32 stjb_adr; /* offset 1C */ - u16 stjb_ticuser_dispcyc; /* offset 20 */ - u16 stjb_ticuser_protocol; /* offset 22 */ - u8 stjb_filler[12]; /* offset 24 */ - u8 stjb_data[256]; /* offset 30 */ - }; - -struct st_ram_io -{ - unsigned char data_to_pc_ready; - unsigned char tic_owner_to_pc; - unsigned char numcard_owner_to_pc; - unsigned char tic_des_to_pc; - unsigned char numcard_des_to_pc; - unsigned char data_from_pc_ready; - unsigned char tic_owner_from_pc; - unsigned char numcard_owner_from_pc; - unsigned char tic_des_from_pc; - unsigned char numcard_des_from_pc; - unsigned char ack_to_pc_ready; - unsigned char tic_ack_to_pc; - unsigned char numcard_ack_to_pc; - unsigned char typ_ack_to_pc; - unsigned char ack_from_pc_ready; - unsigned char tic_ack_from_pc; - unsigned char numcard_ack_from_pc; - unsigned char typ_ack_from_pc; - unsigned char conf_end_test[4]; - unsigned char error_code[2]; - unsigned char parameter_error[4]; - unsigned char time_base; - unsigned char nul_inc; - unsigned char vers; - unsigned char num_card; - unsigned char reserv1[32]; -}; - - -#endif /* __LINUX_APPLICOM_H__ */ From 6be50f680a3b2e3c87ac5faf5eddf44bb3ad6558 Mon Sep 17 00:00:00 2001 From: Joe Simmons-Talbott Date: Thu, 26 Mar 2026 09:12:56 -0400 Subject: [PATCH 1649/5214] gpib: agilent_82357a: don't check a NULL serial string The agilent_82357a driver uses the USB device serial string for device matching but does not verify that the string exists before passing it to strcmp(). Verify that the device has a serial number before accessing it to avoid triggering a NULL-pointer dereference with devices that don't provide a serial number (iSerialNumber = 0). Similar to commit aa79f996eb41 ("i2c: cp2615: fix serial string NULL-deref at probe"). Found by Claude:sonnet-4.5 Signed-off-by: Joe Simmons-Talbott Acked-by: Dave Penkler Link: https://patch.msgid.link/20260326131256.1758014-1-joest@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/agilent_82357a/agilent_82357a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c index 770ba6eb40d1..2468a471d175 100644 --- a/drivers/gpib/agilent_82357a/agilent_82357a.c +++ b/drivers/gpib/agilent_82357a/agilent_82357a.c @@ -1298,7 +1298,7 @@ static inline int agilent_82357a_device_match(struct usb_interface *interface, if (gpib_match_device_path(&interface->dev, config->device_path) == 0) return 0; if (config->serial_number && - strcmp(usbdev->serial, config->serial_number) != 0) + (!usbdev->serial || strcmp(usbdev->serial, config->serial_number) != 0)) return 0; return 1; From c4faab452b3c1ada003d49c477609dd80523b9bf Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Fri, 24 Apr 2026 19:37:47 +0700 Subject: [PATCH 1650/5214] gpib: fix double decrement of descriptor_busy in command_ioctl() commit d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") introduced a descriptor_busy reference counter to pin struct gpib_descriptor across IO ioctl operations. In command_ioctl(), the error path inside the loop decrements descriptor_busy and breaks, but execution then falls through to the unconditional decrement after the loop, underflowing the counter to -1. This re-enables the use-after-free that the original fix was meant to prevent: a concurrent close_dev_ioctl() sees descriptor_busy == 0 on an actively-used descriptor and frees it. Remove the early decrement from the error path. The post-loop decrement already handles all exit paths, matching the correct pattern used in read_ioctl() and write_ioctl(). Fixes: d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") Reported-by: Ruikai Peng Signed-off-by: Adam Crosser Link: https://patch.msgid.link/20260424123750.855863-1-adam.r.crosser@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/common/gpib_os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 4c6c43f012c8..69f6aa73ab9a 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -1010,7 +1010,6 @@ static int command_ioctl(struct gpib_file_private *file_priv, userbuf += bytes_written; if (retval < 0) { atomic_set(&desc->io_in_progress, 0); - atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); break; From 5ad28496055858166eb2268344c8fda2c26d3561 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Sun, 3 May 2026 17:30:36 +0800 Subject: [PATCH 1651/5214] gpib: cb7210: Fix region leak when request_irq fails When request_irq() fails, the region allocated by request_region() is not released. Fix this by calling release_region() before returning. Smatch warning: drivers/gpib/cb7210/cb7210.c:1068 cb_isa_attach() warn: 'config->ibbase' from __request_region() not released on lines: 1064. Fixes: 82e3508046f9 ("staging: gpib: cb7210 console messaging cleanup") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260503093036.283546-1-zenghongling@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/cb7210/cb7210.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964..05058bf2cd50 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1062,6 +1062,7 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); + release_region(nec7210_iobase(cb_priv), cb7210_iosize); return -EBUSY; } cb_priv->irq = config->ibirq; From b583b8fda6ddcf81fe1b02bec0d3c7fb43efc6de Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 13:41:21 +0530 Subject: [PATCH 1652/5214] PCI: Indicate context lost if L1SS exit is broken during resume from system suspend Per PCIe v7.0, sec 5.5.3.3.1, when exiting L1.2 due to an endpoint asserting CLKREQ# signal, the REFCLK must be turned on within the latency advertised in the LTR message. This requirement applies to L1.1 as well. On some platforms like Qcom, these requirements are satisfied during OS runtime, but not while resuming from the system suspend. This happens because the PCIe RC driver may remove all resource votes and turn off the PHY analog circuitry during suspend to maximize power savings while keeping the link in L1SS. Consequently, when the endpoint asserts CLKREQ# to wake up, the RC driver must restore the PHY and enable the REFCLK. When this recovery process exceeds the L1SS exit latency time (roughly L10_REFCLK_ON + T_COMMONMODE), the endpoint may treat it as a fatal condition and trigger Link Down (LDn). This results in a reset that destroys the internal device state. So to indicate this platform limitation to the client drivers, introduce a new flag 'pci_host_bridge::broken_l1ss_resume' and check it in pci_suspend_retains_context(). If the flag is set by the RC driver, the API will return 'false' indicating the client drivers that the device context may not be retained and the drivers must be prepared for context loss. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260519-l1ss-fix-v2-2-b2c3a4bdeb15@oss.qualcomm.com --- drivers/pci/pci.c | 12 ++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 86961551ec51..2f7e7e186b47 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2910,6 +2910,8 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) */ bool pci_suspend_retains_context(struct pci_dev *pdev) { + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); + /* * If the platform firmware (like ACPI) is involved at the end of * system suspend, device context may not be retained. @@ -2917,6 +2919,16 @@ bool pci_suspend_retains_context(struct pci_dev *pdev) if (pm_suspend_via_firmware()) return false; + /* + * Some host bridges power off the PHY to enter deep low-power + * modes during system suspend. Exiting L1SS from this condition + * may violate timing requirements and result in Link Down (LDn), + * which causes a reset of the device. On such platforms, the + * endpoint must be prepared for context loss. + */ + if (bridge && bridge->broken_l1ss_resume) + return false; + /* Assume that the context is retained by default */ return true; } diff --git a/include/linux/pci.h b/include/linux/pci.h index f60f9e4e7b39..4fee00ec59d7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -660,6 +660,8 @@ struct pci_host_bridge { unsigned int preserve_config:1; /* Preserve FW resource setup */ unsigned int size_windows:1; /* Enable root bus sizing */ unsigned int msi_domain:1; /* Bridge wants MSI domain */ + unsigned int broken_l1ss_resume:1; /* Resuming from L1SS during + system suspend is broken */ /* Resource alignment requirements */ resource_size_t (*align_resource)(struct pci_dev *dev, From ced835ae1f62c5a316a4229dde5762c63d0ac27b Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 13:41:22 +0530 Subject: [PATCH 1653/5214] PCI: qcom: Indicate broken L1SS exit during resume from system suspend Qcom PCIe RCs can successfully exit from L1SS during OS runtime. However, during system suspend, the Qcom PCIe RC driver may remove all resource votes and turn off the PHY to maximize power savings. Consequently, when the host is in system suspend with the link in L1SS and the endpoint asserts CLKREQ#, the RC driver must restore the PHY and enable the REFCLK. This recovery process causes the L1SS exit latency time to be exceeded (roughly L10_REFCLK_ON + T_COMMONMODE). If the RC driver were to retain all votes during suspend, L1SS exit would succeed without issue but at the expense of higher power consumption. When the host fails to move the link from L1SS to L0 within the L10_REFCLK_ON + T_COMMONMODE time, the endpoint may treat it as a fatal condition and trigger Link Down (LDn) during resume. This LDn results in a reset that destroys the internal device state. To ensure that the client drivers can properly handle this scenario, let them know about this platform limitation by setting the 'pci_host_bridge::broken_l1ss_resume' flag. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260519-l1ss-fix-v2-3-b2c3a4bdeb15@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index af6bf5cce65b..a0b59b4ef1d2 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1368,6 +1368,19 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); + /* + * During system suspend, the Qcom RC driver may turn off the + * analog circuitry of PHY and remove controller votes to save + * power. If the link is in L1SS and the endpoint asserts CLKREQ# + * to exit L1SS, the time required to wake the system and restore + * the PHY/REFCLK may exceed the L1SS exit timing (L10_REFCLK_ON + + * T_COMMONMODE), resulting in Link Down (LDn) and a reset of the + * endpoint. Set this flag to indicate this limitation to client + * drivers so that they can avoid relying on device state being + * preserved during system suspend. + */ + pp->bridge->broken_l1ss_resume = true; + if (pcie->cfg->ops->host_post_init) pcie->cfg->ops->host_post_init(pcie); } From 748a927e081bffc9a9be17b4d96088515e1c5f01 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 13:41:23 +0530 Subject: [PATCH 1654/5214] nvme-pci: Use pci_suspend_retains_context() during suspend pci_suspend_retains_context() lets PCI client drivers know if the platform can retain the device context during suspend. This is decided based on several factors like: - Firmware involvement at the end of suspend - Any platform limitation in waking from low power state (e.g., L1SS) This API may be extended in the future to cover other platform specific issues impacting the device low power mode during system suspend. Use this API instead of checks like pm_suspend_via_firmware(). When pci_suspend_retains_context() returns false, assume the platform cannot retain the context and shutdown the controller. If it returns true, assume that the context will be retained and keep the device in low power mode. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260519-l1ss-fix-v2-4-b2c3a4bdeb15@oss.qualcomm.com --- drivers/nvme/host/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index db5fc9bf6627..a6664983ce5d 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3915,6 +3915,7 @@ static int nvme_suspend(struct device *dev) * use host managed nvme power settings for lowest idle power if * possible. This should have quicker resume latency than a full device * shutdown. But if the firmware is involved after the suspend or the + * platform has any limitation in waking from low power states or the * device does not support any non-default power states, shut down the * device fully. * @@ -3923,7 +3924,7 @@ static int nvme_suspend(struct device *dev) * down, so as to allow the platform to achieve its minimum low-power * state (which may not be possible if the link is up). */ - if (pm_suspend_via_firmware() || !ctrl->npss || + if (!pci_suspend_retains_context(pdev) || !ctrl->npss || !pcie_aspm_enabled(pdev) || (ndev->ctrl.quirks & NVME_QUIRK_SIMPLE_SUSPEND)) return nvme_disable_prepare_reset(ndev, true); From eec44c56e67ca78c377f4c3d85ef94fd105bea81 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:41 +0100 Subject: [PATCH 1655/5214] KVM: arm64: vgic-v5: Add missing trap handing for NV triage As things stand, there is no support for Nested Virt with GICv5 guests yet. However, this is coming and therefore we need to be able to correctly triage the traps when running with NV. Add the missing fgtreg lookups required for that to triage_sysreg_trap(). These are specific to the FGT regs added as part of GICv5: * ICH_HFGRTR_EL2 * ICH_HFGWTR_EL2 * ICH_HFGITR_EL2 Fixes: 9d6d9514c08f ("KVM: arm64: gic-v5: Support GICv5 FGTs & FGUs") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Reviewed-by: Joey Gouly Link: https://lore.kernel.org/r/20260520091949.542365-11-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/emulate-nested.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index dba7ced74ca5..a4eb36b4c442 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2631,6 +2631,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index) fgtreg = HFGITR2_EL2; break; + case ICH_HFGRTR_GROUP: + fgtreg = is_read ? ICH_HFGRTR_EL2 : ICH_HFGWTR_EL2; + break; + + case ICH_HFGITR_GROUP: + fgtreg = ICH_HFGITR_EL2; + break; + default: /* Something is really wrong, bail out */ WARN_ONCE(1, "Bad FGT group (encoding %08x, config %016llx)\n", From 2427e8c1cd4f467770cb52e5e723adf88cc61ae0 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:42 +0100 Subject: [PATCH 1656/5214] KVM: arm64: vgic-v5: Atomically assign bits to PPI DVI bitmap For GICv5 guests we make use of the DVI mechanism for PPIs where possible. When mapping a virtual irq to a physical one for a GICv5 guest, the corresponding bit in the DVI bitmap is set. When unmapping, said bit is cleared again. The key user of this mechanism is the arch timer. The existing code used the non-atomic __assign_bit() rather than doing the update atomically. This could technically result in losing state if a second PPI's DVI bit were being manipulated concurrently. Each individual bit within the DVI bitmap is guarded using vgic_irq->irq_lock, but there's no locking for the overall bitmap. Therefore, switch to using the atomic assign_bit() function instead. Fixes: 5a98d0e17e59 ("KVM: arm64: gic-v5: Implement direct injection of PPIs") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-12-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 7916bd8d564e..d4789ff3e740 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -272,7 +272,7 @@ void vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, struct vgic_irq *irq, bool dvi) lockdep_assert_held(&irq->irq_lock); ppi = vgic_v5_get_hwirq_id(irq->intid); - __assign_bit(ppi, cpu_if->vgic_ppi_dvir, dvi); + assign_bit(ppi, cpu_if->vgic_ppi_dvir, dvi); } static const struct irq_ops vgic_v5_ppi_irq_ops = { From f034ae93bcb68b06bb52344788f4cb82ae691719 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:43 +0100 Subject: [PATCH 1657/5214] KVM: arm64: selftests: Add missing GIC CDEN to no-vgic-v5 selftest The selftest mistakenly omitted the GIC CDEN instruction from the testing. Add it in. Fixes: ce29261ec648 ("KVM: arm64: selftests: Add no-vgic-v5 selftest") Reviewed-by: Joey Gouly Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-13-maz@kernel.org Signed-off-by: Marc Zyngier --- tools/testing/selftests/kvm/arm64/no-vgic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kvm/arm64/no-vgic.c b/tools/testing/selftests/kvm/arm64/no-vgic.c index 25b2e3222f68..ab57902ce429 100644 --- a/tools/testing/selftests/kvm/arm64/no-vgic.c +++ b/tools/testing/selftests/kvm/arm64/no-vgic.c @@ -159,6 +159,7 @@ static void guest_code_gicv5(void) check_gicv5_gic_op(CDAFF); check_gicv5_gic_op(CDDI); check_gicv5_gic_op(CDDIS); + check_gicv5_gic_op(CDEN); check_gicv5_gic_op(CDEOI); check_gicv5_gic_op(CDHM); check_gicv5_gic_op(CDPEND); From 8192f783b5e15c2966f67918b1e8073171c928c9 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:44 +0100 Subject: [PATCH 1658/5214] KVM: arm64: selftests: Cleanup unused vars in GICv5 PPI selftest Clean up a set of unused variables around the size of the guest's PA space as they are completely irrelevant for GICv5 when only considering PPIs. Fixes: 0a9f38bf612b ("KVM: arm64: selftests: Introduce a minimal GICv5 PPI selftest") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-14-maz@kernel.org Signed-off-by: Marc Zyngier --- tools/testing/selftests/kvm/arm64/vgic_v5.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vgic_v5.c b/tools/testing/selftests/kvm/arm64/vgic_v5.c index d785b660d847..a8707120de0d 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_v5.c +++ b/tools/testing/selftests/kvm/arm64/vgic_v5.c @@ -20,8 +20,6 @@ struct vm_gic { u32 gic_dev_type; }; -static u64 max_phys_size; - #define GUEST_CMD_IRQ_CDIA 10 #define GUEST_CMD_IRQ_DIEOI 11 #define GUEST_CMD_IS_AWAKE 12 @@ -208,13 +206,9 @@ void run_tests(u32 gic_dev_type) int main(int ac, char **av) { int ret; - int pa_bits; test_disable_default_vgic(); - pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits; - max_phys_size = 1ULL << pa_bits; - ret = test_kvm_device(KVM_DEV_TYPE_ARM_VGIC_V5); if (ret) { pr_info("No GICv5 support; Not running GIC_v5 tests.\n"); From 441623bcb8ccb957fbb4b536b194352a186c0155 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:45 +0100 Subject: [PATCH 1659/5214] KVM: arm64: selftests: Improve error handling for GICv5 PPI selftest Cases where the KVM_RUN ioctl returned an error were wrongly reported as incorrect ucalls. Furthermore, potential failures when calling KVM_IRQ_LINE were being hidden. Improve the error handling to correctly propagate the error in both cases. Fixes: 0a9f38bf612b ("KVM: arm64: selftests: Introduce a minimal GICv5 PPI selftest") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-15-maz@kernel.org Signed-off-by: Marc Zyngier --- tools/testing/selftests/kvm/arm64/vgic_v5.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/arm64/vgic_v5.c b/tools/testing/selftests/kvm/arm64/vgic_v5.c index a8707120de0d..96cfd6bb32f6 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_v5.c +++ b/tools/testing/selftests/kvm/arm64/vgic_v5.c @@ -129,6 +129,8 @@ static void test_vgic_v5_ppis(u32 gic_dev_type) while (1) { ret = run_vcpu(vcpus[0]); + if (ret) + break; switch (get_ucall(vcpus[0], &uc)) { case UCALL_SYNC: @@ -144,7 +146,7 @@ static void test_vgic_v5_ppis(u32 gic_dev_type) irq = FIELD_PREP(KVM_ARM_IRQ_NUM_MASK, 3); irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT; - _kvm_irq_line(v.vm, irq, level); + kvm_irq_line(v.vm, irq, level); } else if (uc.args[1] == GUEST_CMD_IS_AWAKE) { pr_info("Guest skipping WFI due to pending IRQ\n"); } else if (uc.args[1] == GUEST_CMD_IRQ_CDIA) { From 6930980bf2f1625c5eb25a27b8673faa89c5792a Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:46 +0100 Subject: [PATCH 1660/5214] Documentation: KVM: Fix typos in VGICv5 documentation Fix two typos in the VGICv5 documentation. Fixes: d51c978b7d3e ("KVM: arm64: gic-v5: Communicate userspace-driveable PPIs via a UAPI") Fixes: eb3c4d2c9a4d ("Documentation: KVM: Introduce documentation for VGICv5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-16-maz@kernel.org Signed-off-by: Marc Zyngier --- Documentation/virt/kvm/devices/arm-vgic-v5.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/devices/arm-vgic-v5.rst b/Documentation/virt/kvm/devices/arm-vgic-v5.rst index 29335ea823fc..70b9162755c7 100644 --- a/Documentation/virt/kvm/devices/arm-vgic-v5.rst +++ b/Documentation/virt/kvm/devices/arm-vgic-v5.rst @@ -12,8 +12,8 @@ Only one VGIC instance may be instantiated through this API. The created VGIC will act as the VM interrupt controller, requiring emulated user-space devices to inject interrupts to the VGIC instead of directly to CPUs. -Creating a guest GICv5 device requires a host GICv5 host. The current VGICv5 -device only supports PPI interrupts. These can either be injected from emulated +Creating a guest GICv5 device requires a GICv5 host. The current VGICv5 device +only supports PPI interrupts. These can either be injected from emulated in-kernel devices (such as the Arch Timer, or PMU), or via the KVM_IRQ_LINE ioctl. @@ -25,7 +25,7 @@ Groups: request the initialization of the VGIC, no additional parameter in kvm_device_attr.addr. Must be called after all VCPUs have been created. - KVM_DEV_ARM_VGIC_USERPSPACE_PPIs + KVM_DEV_ARM_VGIC_USERSPACE_PPIS request the mask of userspace-drivable PPIs. Only a subset of the PPIs can be directly driven from userspace with GICv5, and the returned mask informs userspace of which it is allowed to drive via KVM_IRQ_LINE. From 3f5aeaf8d9d3a6693979a92e6ac94b1e2884b911 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:47 +0100 Subject: [PATCH 1661/5214] Documentation: KVM: Clarify that PMU_V3_IRQ IntID requirements for GICv5 When running a GICv5-based guest, the PMU must use PPI 23. This, however, must be communicated via the KVM_ARM_VCPU_PMU_V3_CTRL->KVM_ARM_VCPU_PMU_V3_IRQ ioctl as a full GICv5-style Interrupt ID. That is, 0x20000017. Optionally, the whole ioctl can be skipped for GICv5. This was previously not clearly documented, so bump the documentation accordingly. Fixes: 7c31c06e2d2d ("KVM: arm64: gic-v5: Mandate architected PPI for PMU emulation on GICv5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-17-maz@kernel.org Signed-off-by: Marc Zyngier --- Documentation/virt/kvm/devices/vcpu.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst index 5e3805820010..66e714f2fcfa 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -37,8 +37,11 @@ Returns: A value describing the PMUv3 (Performance Monitor Unit v3) overflow interrupt number for this vcpu. This interrupt could be a PPI or SPI, but the interrupt type must be same for each vcpu. As a PPI, the interrupt number is the same for -all vcpus, while as an SPI it must be a separate number per vcpu. For -GICv5-based guests, the architected PPI (23) must be used. +all vcpus, while as an SPI it must be a separate number per vcpu. + +For GICv5-based guests, the architected PPI (23) must be used, and must be +communicated as the full GICv5-style Interrupt ID, i.e., 0x20000017. This ioctl +can be omitted altogether for a GICv5-based guest. 1.2 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_INIT --------------------------------------- From abf60331ebe9a9a7937a72aac7699c2907ab9307 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:48 +0100 Subject: [PATCH 1662/5214] irqchip/gic-v5: Immediately exec priority drop following activate With GICv5 an interrupt of equal or lower priority cannot be signalled until there has been a priority drop. This is done via the GIC CDEOI system instruction. Once this has been executed, the hardware is able to signal the next interrupt if there is one. As all interrupts are programmed to have the same priority, no new interrupts can be signalled until the priority drop has happened. This can cause issues when, for example, an interrupt remains active while a long running process takes place, such as when injecting a physical interrupt into a guest VM in software. The GICv5 driver has so far done the priority drop as part of irq_eoi(), i.e., at the same time as deactivating the interrupt. This means that any long running process (or VM) could block incoming interrupts, effectively causing a denial of service for all other interrupts. Rather than doing the EOI as part of irq_eoi() (which the name would suggest would be a good place for it), move it to happen immediately after acknowledging an interrupt in the main GICv5 interrupt handler. The deactivation of interrupts (GIC CDDI) remains implemented as part of irq_eoi(), which means that the same interrupt cannot be signalled a second time until deactivated by software. Suggested-by: Marc Zyngier Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-18-maz@kernel.org Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v5.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 6b0903be8ebf..58e457d4c147 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -218,17 +218,13 @@ static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type) FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, hwirq_type); gic_insn(cddi, CDDI); - - gic_insn(0, CDEOI); } static void gicv5_ppi_irq_eoi(struct irq_data *d) { /* Skip deactivate for forwarded PPI interrupts */ - if (irqd_is_forwarded_to_vcpu(d)) { - gic_insn(0, CDEOI); + if (irqd_is_forwarded_to_vcpu(d)) return; - } gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI); } @@ -963,6 +959,13 @@ static void __exception_irq_entry gicv5_handle_irq(struct pt_regs *regs) */ isb(); + /* + * Ensure that we can receive the next interrupts in the event that we + * have a long running handler or directly enter a guest by doing the + * priority drop immediately. + */ + gic_insn(0, CDEOI); + hwirq = FIELD_GET(GICV5_HWIRQ_INTID, ia); handle_irq_per_domain(hwirq); From bee399ea20c8fea361c0ada06afdec9fcbf6dfde Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 20 May 2026 10:19:49 +0100 Subject: [PATCH 1663/5214] KVM: arm64: Fix arch timer interrupts for GICv3-on-GICv5 guests When running on a GICv5 host, we push an arch-timer-specific interrupt domain for the timer interrupts. This interrupt domain is used to mask the host interrupt when a GICv5 guest is running. However, this interrupt domain is still in place when running with a GICv3 guest on GICv5 hardware. The result is that some interrupt state changes are not correctly propragated to the host irqchip driver for legacy guests. Explicitly pass irqchip state changes though to the host irqchip driver when running a GICv3-based guest on a GICv5 host. This bypasses all masking, and thereby operates just as a native GICv3 guest would, with the exception of having an additional irq domain in the hierarchy. Fixes: 9491c63b6cd7 ("KVM: arm64: gic-v5: Enlighten arch timer for GICv5") Suggested-by: Marc Zyngier Signed-off-by: Sascha Bischoff Link: https://lore.kernel.org/r/20260520091949.542365-19-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index f003df76fdda..53b67b4d0bf2 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -1294,7 +1294,12 @@ static int timer_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) static int timer_irq_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool val) { - if (which != IRQCHIP_STATE_ACTIVE || !irqd_is_forwarded_to_vcpu(d)) + bool passthrough = which != IRQCHIP_STATE_ACTIVE || + !irqd_is_forwarded_to_vcpu(d) || + (kvm_vgic_global_state.type == VGIC_V5 && + vgic_is_v3(kvm_get_running_vcpu()->kvm)); + + if (passthrough) return irq_chip_set_parent_state(d, which, val); if (val) @@ -1307,15 +1312,7 @@ static int timer_irq_set_irqchip_state(struct irq_data *d, static void timer_irq_eoi(struct irq_data *d) { - /* - * On a GICv5 host, we still need to call EOI on the parent for - * PPIs. The host driver already handles irqs which are forwarded to - * vcpus, and skips the GIC CDDI while still doing the GIC CDEOI. This - * is required to emulate the EOIMode=1 on GICv5 hardware. Failure to - * call EOI unsurprisingly results in *BAD* lock-ups. - */ - if (!irqd_is_forwarded_to_vcpu(d) || - kvm_vgic_global_state.type == VGIC_V5) + if (!irqd_is_forwarded_to_vcpu(d)) irq_chip_eoi_parent(d); } From 7378b6656aa46fda56f2743d5a7c1f619c2f6f9b Mon Sep 17 00:00:00 2001 From: Li Guan Date: Thu, 14 May 2026 02:07:21 +0800 Subject: [PATCH 1664/5214] perf riscv: Fix discarded const qualifier in _get_field() The assignment of strrchr() return values to non-const char * variables triggers a -Werror=discarded-qualifiers warning when building with GCC 14. This happens because in newer glibc versions, strrchr() returns a 'const char *' if the input string is const. Properly declare 'line2' and 'nl' as const char * to match the glibc function signature and ensure type safety. This avoids the need for explicit type casting and aligns with the design pattern of not modifying read-only memory in the perf tool. Reviewed-by: Ian Rogers Signed-off-by: Li Guan Cc: Adrian Hunter Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/riscv/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/riscv/util/header.c b/tools/perf/arch/riscv/util/header.c index 4b839203d4a5..891984e909bd 100644 --- a/tools/perf/arch/riscv/util/header.c +++ b/tools/perf/arch/riscv/util/header.c @@ -19,7 +19,7 @@ static char *_get_field(const char *line) { - char *line2, *nl; + const char *line2, *nl; line2 = strrchr(line, ' '); if (!line2) From ac536de6d4247726977b1d6388fb7745c56f5188 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 15 May 2026 18:05:31 +0200 Subject: [PATCH 1665/5214] pinctrl: core: Make pin group callbacks optional for pin-only drivers Currently, the pinctrl core strictly requires all drivers to implement .get_groups_count and .get_group_name callbacks in their pinctrl_ops. However, for simple pinctrl drivers that act purely as GPIO controllers and pin-specific configuration proxies, without any concept of muxing or pin groups, this strict requirement forces the implementation of dummy callbacks just to satisfy pinctrl_check_ops(). Relax this requirement for pin-only drivers by making the group callbacks optional when no muxing or group pin configuration support is provided. Update the core and debugfs helpers to check for the existence of these callbacks before invoking them. Drivers that provide muxing or group pin configuration operations still must implement group enumeration and naming callbacks, and are rejected at registration time if they do not. Suggested-by: Linus Walleij Signed-off-by: Oleksij Rempel Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 40 ++++++++++++++++++++++++++++++++++----- drivers/pinctrl/pinconf.c | 9 +++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 6cbcaa6709da..3fcb7e584a93 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -622,8 +622,13 @@ static int pinctrl_generic_group_name_to_selector(struct pinctrl_dev *pctldev, const char *function) { const struct pinctrl_ops *ops = pctldev->desc->pctlops; - int ngroups = ops->get_groups_count(pctldev); int selector = 0; + int ngroups; + + if (!ops->get_groups_count || !ops->get_group_name) + return -EINVAL; + + ngroups = ops->get_groups_count(pctldev); /* See if this pctldev has this group */ while (selector < ngroups) { @@ -738,8 +743,15 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group) { const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned int ngroups = pctlops->get_groups_count(pctldev); unsigned int group_selector = 0; + unsigned int ngroups; + + if (!pctlops->get_groups_count || !pctlops->get_group_name) { + dev_err(pctldev->dev, "does not support pin groups\n"); + return -EINVAL; + } + + ngroups = pctlops->get_groups_count(pctldev); while (group_selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, @@ -1801,6 +1813,11 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) mutex_lock(&pctldev->mutex); + if (!ops->get_groups_count || !ops->get_group_name) { + mutex_unlock(&pctldev->mutex); + return 0; + } + ngroups = ops->get_groups_count(pctldev); seq_puts(s, "registered pin groups:\n"); @@ -2081,12 +2098,25 @@ static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) static int pinctrl_check_ops(struct pinctrl_dev *pctldev) { const struct pinctrl_ops *ops = pctldev->desc->pctlops; + const struct pinconf_ops *confops = pctldev->desc->confops; + bool needs_groups = false; - if (!ops || - !ops->get_groups_count || - !ops->get_group_name) + if (!ops) return -EINVAL; + if (pctldev->desc->pmxops) + needs_groups = true; + + if (confops && (confops->pin_config_group_get || + confops->pin_config_group_set)) + needs_groups = true; + + if (needs_groups && (!ops->get_groups_count || !ops->get_group_name)) { + dev_err(pctldev->dev, + "driver needs group callbacks for mux or group config\n"); + return -EINVAL; + } + return 0; } diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index dca963633b5d..81686844dfa5 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -275,7 +275,7 @@ void pinconf_show_setting(struct seq_file *s, case PIN_MAP_TYPE_CONFIGS_GROUP: seq_printf(s, "group %s (%d)", pctlops->get_group_name(pctldev, - setting->data.configs.group_or_pin), + setting->data.configs.group_or_pin), setting->data.configs.group_or_pin); break; default: @@ -348,8 +348,13 @@ static int pinconf_groups_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned int ngroups = pctlops->get_groups_count(pctldev); unsigned int selector = 0; + unsigned int ngroups; + + if (!pctlops->get_groups_count || !pctlops->get_group_name) + return 0; + + ngroups = pctlops->get_groups_count(pctldev); seq_puts(s, "Pin config settings per pin group\n"); seq_puts(s, "Format: group (name): configs\n"); From ae20d41bc230efa5972aaf5b27cadf1ff022aee8 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 21 May 2026 17:17:45 +0800 Subject: [PATCH 1666/5214] dt-bindings: pinctrl: Add aspeed,ast2700-soc1-pinctrl SoC1 in the AST2700 integrates its own pin controller responsible for pin multiplexing and pin configuration. The controller manages various peripheral functions such as eSPI, LPC, VPI, SD, UART, I2C, I3C, PWM and others through SCU registers. The binding reuses the standard pinmux and generic pin configuration schemas and does not introduce custom Devicetree properties. Acked-by: Conor Dooley Signed-off-by: Billy Tsai Signed-off-by: Linus Walleij --- .../pinctrl/aspeed,ast2700-soc1-pinctrl.yaml | 760 ++++++++++++++++++ 1 file changed, 760 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml new file mode 100644 index 000000000000..76944fd14e2c --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml @@ -0,0 +1,760 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED AST2700 SoC1 Pin Controller + +maintainers: + - Billy Tsai + +description: + The AST2700 features a dual-SoC architecture with two interconnected SoCs, + each having its own System Control Unit (SCU) for independent pin control. + This pin controller manages the pin multiplexing for SoC1. + + The SoC1 pin controller manages pin functions including eSPI, LPC and I2C, + among others. + +properties: + compatible: + const: aspeed,ast2700-soc1-pinctrl + reg: + maxItems: 1 + +patternProperties: + '-state$': + description: | + Pin control state. + + If `function` is present, the node describes a pinmux state and must + specify `groups`. + + For pin configuration, exactly one of `groups` or `pins` must be + specified in each state node. Group-level configuration applies to all + pins in the group. Pin-level configuration may be supplied in a + separate state node for individual pins; when both group-level and + pin-level configuration apply to the same pin, the pin-level + configuration takes precedence. + + type: object + allOf: + - $ref: pinmux-node.yaml# + - $ref: pincfg-node.yaml# + - if: + required: + - function + then: + required: + - groups + - oneOf: + - required: + - groups + - required: + - pins + additionalProperties: false + + properties: + function: + enum: + - ADC0 + - ADC1 + - ADC10 + - ADC11 + - ADC12 + - ADC13 + - ADC14 + - ADC15 + - ADC2 + - ADC3 + - ADC4 + - ADC5 + - ADC6 + - ADC7 + - ADC8 + - ADC9 + - AUXPWRGOOD0 + - AUXPWRGOOD1 + - CANBUS + - ESPI0 + - ESPI1 + - FSI0 + - FSI1 + - FSI2 + - FSI3 + - FWQSPI + - FWSPIABR + - FWWPN + - HBLED + - I2C0 + - I2C1 + - I2C10 + - I2C11 + - I2C12 + - I2C13 + - I2C14 + - I2C15 + - I2C2 + - I2C3 + - I2C4 + - I2C5 + - I2C6 + - I2C7 + - I2C8 + - I2C9 + - I2CF0 + - I2CF1 + - I2CF2 + - I3C0 + - I3C1 + - I3C10 + - I3C11 + - I3C12 + - I3C13 + - I3C14 + - I3C15 + - I3C2 + - I3C3 + - I3C4 + - I3C5 + - I3C6 + - I3C7 + - I3C8 + - I3C9 + - JTAGM1 + - LPC0 + - LPC1 + - LTPI + - MACLINK0 + - MACLINK1 + - MACLINK2 + - MDIO0 + - MDIO1 + - MDIO2 + - NCTS0 + - NCTS1 + - NCTS5 + - NCTS6 + - NDCD0 + - NDCD1 + - NDCD5 + - NDCD6 + - NDSR0 + - NDSR1 + - NDSR5 + - NDSR6 + - NDTR0 + - NDTR1 + - NDTR5 + - NDTR6 + - NRI0 + - NRI1 + - NRI5 + - NRI6 + - NRTS0 + - NRTS1 + - NRTS5 + - NRTS6 + - OSCCLK + - PCIERC + - PWM0 + - PWM1 + - PWM10 + - PWM11 + - PWM12 + - PWM13 + - PWM14 + - PWM15 + - PWM2 + - PWM3 + - PWM4 + - PWM5 + - PWM6 + - PWM7 + - PWM8 + - PWM9 + - QSPI0 + - QSPI1 + - QSPI2 + - RGMII0 + - RGMII1 + - RMII0 + - RMII0RCLKO + - RMII1 + - RMII1RCLKO + - SALT0 + - SALT1 + - SALT10 + - SALT11 + - SALT12 + - SALT13 + - SALT14 + - SALT15 + - SALT2 + - SALT3 + - SALT4 + - SALT5 + - SALT6 + - SALT7 + - SALT8 + - SALT9 + - SD + - SGMII + - SGPM0 + - SGPM1 + - SGPS + - SIOONCTRLN0 + - SIOONCTRLN1 + - SIOPBIN0 + - SIOPBIN1 + - SIOPBON0 + - SIOPBON1 + - SIOPWREQN0 + - SIOPWREQN1 + - SIOPWRGD1 + - SIOS3N0 + - SIOS3N1 + - SIOS5N0 + - SIOS5N1 + - SIOSCIN0 + - SIOSCIN1 + - SMON0 + - SMON1 + - SPI0 + - SPI0ABR + - SPI0CS1 + - SPI0WPN + - SPI1 + - SPI1ABR + - SPI1CS1 + - SPI1WPN + - SPI2 + - SPI2CS1 + - TACH0 + - TACH1 + - TACH10 + - TACH11 + - TACH12 + - TACH13 + - TACH14 + - TACH15 + - TACH2 + - TACH3 + - TACH4 + - TACH5 + - TACH6 + - TACH7 + - TACH8 + - TACH9 + - THRU0 + - THRU1 + - THRU2 + - THRU3 + - UART0 + - UART1 + - UART10 + - UART11 + - UART2 + - UART3 + - UART5 + - UART6 + - UART7 + - UART8 + - UART9 + - USB2C + - USB2D + - USBUART + - VGA + - VPI + - WDTRST0N + - WDTRST1N + - WDTRST2N + - WDTRST3N + - WDTRST4N + - WDTRST5N + - WDTRST6N + - WDTRST7N + + groups: + enum: + - ADC0 + - ADC1 + - ADC10 + - ADC11 + - ADC12 + - ADC13 + - ADC14 + - ADC15 + - ADC2 + - ADC3 + - ADC4 + - ADC5 + - ADC6 + - ADC7 + - ADC8 + - ADC9 + - AUXPWRGOOD0 + - AUXPWRGOOD1 + - CANBUS + - DI2C0 + - DI2C1 + - DI2C10 + - DI2C11 + - DI2C12 + - DI2C13 + - DI2C14 + - DI2C15 + - DI2C2 + - DI2C3 + - DI2C8 + - DI2C9 + - DSGPM0 + - ESPI0 + - ESPI1 + - FSI0 + - FSI1 + - FSI2 + - FSI3 + - FWQSPI + - FWSPIABR + - FWWPN + - HBLED + - HVI3C0 + - HVI3C1 + - HVI3C12 + - HVI3C13 + - HVI3C14 + - HVI3C15 + - HVI3C2 + - HVI3C3 + - I2C0 + - I2C1 + - I2C10 + - I2C11 + - I2C12 + - I2C13 + - I2C14 + - I2C15 + - I2C2 + - I2C3 + - I2C4 + - I2C5 + - I2C6 + - I2C7 + - I2C8 + - I2C9 + - I2CF0 + - I2CF1 + - I2CF2 + - I3C10 + - I3C11 + - I3C4 + - I3C5 + - I3C6 + - I3C7 + - I3C8 + - I3C9 + - JTAGM1 + - LPC0 + - LPC1 + - LTPI + - LTPI_PS_I2C0 + - LTPI_PS_I2C1 + - LTPI_PS_I2C2 + - LTPI_PS_I2C3 + - MACLINK0 + - MACLINK1 + - MACLINK2 + - MDIO0 + - MDIO1 + - MDIO2 + - NCTS0 + - NCTS1 + - NCTS5 + - NCTS6 + - NDCD0 + - NDCD1 + - NDCD5 + - NDCD6 + - NDSR0 + - NDSR1 + - NDSR5 + - NDSR6 + - NDTR0 + - NDTR1 + - NDTR5 + - NDTR6 + - NRI0 + - NRI1 + - NRI5 + - NRI6 + - NRTS0 + - NRTS1 + - NRTS5 + - NRTS6 + - OSCCLK + - PE2SGRSTN + - PWM0 + - PWM1 + - PWM10 + - PWM11 + - PWM12 + - PWM13 + - PWM14 + - PWM15 + - PWM2 + - PWM3 + - PWM4 + - PWM5 + - PWM6 + - PWM7 + - PWM8 + - PWM9 + - QSPI0 + - QSPI1 + - QSPI2 + - RGMII0 + - RGMII1 + - RMII0 + - RMII0RCLKO + - RMII1 + - RMII1RCLKO + - SALT0 + - SALT1 + - SALT10 + - SALT11 + - SALT12 + - SALT13 + - SALT14 + - SALT15 + - SALT2 + - SALT3 + - SALT4 + - SALT5 + - SALT6 + - SALT7 + - SALT8 + - SALT9 + - SD + - SGMII + - SGPM0 + - SGPM1 + - SGPS + - SIOONCTRLN0 + - SIOONCTRLN1 + - SIOPBIN0 + - SIOPBIN1 + - SIOPBON0 + - SIOPBON1 + - SIOPWREQN0 + - SIOPWREQN1 + - SIOPWRGD1 + - SIOS3N0 + - SIOS3N1 + - SIOS5N0 + - SIOS5N1 + - SIOSCIN0 + - SIOSCIN1 + - SMON0 + - SMON1 + - SPI0 + - SPI0ABR + - SPI0CS1 + - SPI0WPN + - SPI1 + - SPI1ABR + - SPI1CS1 + - SPI1WPN + - SPI2 + - SPI2CS1 + - TACH0 + - TACH1 + - TACH10 + - TACH11 + - TACH12 + - TACH13 + - TACH14 + - TACH15 + - TACH2 + - TACH3 + - TACH4 + - TACH5 + - TACH6 + - TACH7 + - TACH8 + - TACH9 + - THRU0 + - THRU1 + - THRU2 + - THRU3 + - UART0 + - UART1 + - UART10 + - UART11 + - UART2 + - UART3 + - UART5 + - UART6 + - UART7 + - UART8 + - UART9 + - USB2CD + - USB2CH + - USB2CU + - USB2CUD + - USB2DD + - USB2DH + - USBUART + - VGA + - VPI + - WDTRST0N + - WDTRST1N + - WDTRST2N + - WDTRST3N + - WDTRST4N + - WDTRST5N + - WDTRST6N + - WDTRST7N + + pins: + enum: + - A14 + - A15 + - A18 + - A19 + - A21 + - A22 + - A23 + - A24 + - A25 + - A26 + - A6 + - A7 + - A8 + - AA12 + - AA13 + - AA14 + - AA15 + - AA16 + - AA17 + - AA18 + - AA20 + - AA21 + - AA22 + - AA23 + - AA24 + - AA25 + - AA26 + - AB15 + - AB16 + - AB17 + - AB18 + - AB19 + - AB20 + - AB21 + - AB22 + - AB23 + - AB24 + - AB25 + - AB26 + - AC15 + - AC16 + - AC17 + - AC18 + - AC19 + - AC20 + - AC22 + - AC24 + - AC25 + - AC26 + - AD15 + - AD16 + - AD17 + - AD18 + - AD19 + - AD20 + - AD22 + - AD25 + - AD26 + - AE16 + - AE17 + - AE18 + - AE19 + - AE20 + - AE21 + - AE23 + - AE25 + - AE26 + - AF16 + - AF17 + - AF18 + - AF19 + - AF20 + - AF21 + - AF23 + - AF25 + - AF26 + - B10 + - B11 + - B12 + - B13 + - B14 + - B15 + - B16 + - B18 + - B19 + - B21 + - B22 + - B23 + - B24 + - B25 + - B26 + - B6 + - B7 + - B8 + - B9 + - C10 + - C11 + - C12 + - C13 + - C14 + - C15 + - C16 + - C17 + - C18 + - C19 + - C20 + - C23 + - C26 + - C6 + - C7 + - C8 + - C9 + - D10 + - D12 + - D14 + - D15 + - D19 + - D20 + - D24 + - D26 + - D7 + - D8 + - D9 + - E10 + - E11 + - E12 + - E13 + - E14 + - E26 + - E7 + - E8 + - E9 + - F10 + - F11 + - F12 + - F13 + - F14 + - F26 + - F7 + - F8 + - F9 + - G10 + - G11 + - G7 + - G8 + - G9 + - H10 + - H11 + - H7 + - H8 + - H9 + - J10 + - J11 + - J12 + - J13 + - J9 + - K12 + - K13 + - L12 + - M13 + - M14 + - M15 + - M16 + - N13 + - N14 + - N15 + - N25 + - N26 + - P13 + - P14 + - P25 + - P26 + - R14 + - R25 + - R26 + - T23 + - T24 + - U21 + - U22 + - U25 + - U26 + - V14 + - V16 + - V17 + - V18 + - V19 + - V20 + - V21 + - V22 + - V23 + - V24 + - W14 + - W16 + - W17 + - W18 + - W20 + - W21 + - W22 + - W25 + - W26 + - Y11 + - Y15 + - Y16 + - Y17 + - Y18 + - Y20 + - Y21 + - Y22 + - Y23 + - Y24 + - Y25 + - Y26 + + drive-strength: + enum: [4, 8, 12, 16] + + bias-disable: true + bias-pull-up: true + bias-pull-down: true + +required: + - compatible + - reg + +allOf: + - $ref: pinctrl.yaml# + +additionalProperties: false + +examples: + - | + pinctrl@400 { + compatible = "aspeed,ast2700-soc1-pinctrl"; + reg = <0x400 0x2A0>; + sgpm0-state { + function = "SGPM0"; + groups = "SGPM0"; + }; + }; From 4af4eb66aac382b08a8fcf822161f48cbed3aa08 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 21 May 2026 17:17:46 +0800 Subject: [PATCH 1667/5214] pinctrl: aspeed: Add AST2700 SoC1 support Implement pin multiplexing (and pin configuration where applicable) for the AST2700 SoC1 SCU pinctrl block using static SoC data tables. Unlike legacy ASPEED pin controllers, the SoC1 pin function control fields are highly regular, which makes it practical to describe the packed-field register layout directly in driver data rather than reuse the existing Aspeed pinctrl macro infrastructure. The driver uses the generic pinctrl, pinmux and pinconf frameworks. The controller registers are accessed via regmap from the parent syscon, allowing shared ownership of the SCU register block. Signed-off-by: Billy Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/aspeed/Kconfig | 14 + drivers/pinctrl/aspeed/Makefile | 1 + .../pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c | 1747 +++++++++++++++++ 3 files changed, 1762 insertions(+) create mode 100644 drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig index f9672cca891e..8e1d4da0891d 100644 --- a/drivers/pinctrl/aspeed/Kconfig +++ b/drivers/pinctrl/aspeed/Kconfig @@ -40,3 +40,17 @@ config PINCTRL_ASPEED_G7_SOC0 Say Y here to enable pin controller support for the SoC0 instance of Aspeed's 7th generation SoCs. GPIO is provided by a separate GPIO driver. + +config PINCTRL_ASPEED_G7_SOC1 + bool "Aspeed G7 SoC1 pin control" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF + select MFD_SYSCON + select PINMUX + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to enable pin controller support for the SoC1 instance + of Aspeed's 7th generation SoCs. GPIO is provided by a separate + GPIO driver. diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile index 0de524ca2c72..7a41ca45c6ba 100644 --- a/drivers/pinctrl/aspeed/Makefile +++ b/drivers/pinctrl/aspeed/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o obj-$(CONFIG_PINCTRL_ASPEED_G6) += pinctrl-aspeed-g6.o obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC0) += pinctrl-aspeed-g7-soc0.o +obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC1) += pinctrl-aspeed-g7-soc1.o diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c new file mode 100644 index 000000000000..a1ef52ad5c75 --- /dev/null +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c @@ -0,0 +1,1747 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Pinctrl driver for Aspeed G7 SoC1 + * + * Copyright (C) 2026 Aspeed Technology Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinctrl-utils.h" +#include "../pinmux.h" + +#define ASPEED_G7_SOC1_NR_PINS 220 +#define ASPEED_G7_SOC1_REG_WIDTH 32 +#define ASPEED_G7_SOC1_REG_STRIDE 4 + +#define ASPEED_G7_SOC1_MUX_BASE 0x400 +#define ASPEED_G7_SOC1_BIAS_BASE 0x480 +#define ASPEED_G7_SOC1_DRV_BASE 0x4C0 +#define ASPEED_G7_SOC1_PCIE_REG 0x908 +#define ASPEED_G7_SOC1_USB_MODE_REG 0x3B0 +#define ASPEED_G7_SOC1_SGMII_REG 0x47C + +/* + * Each pin occupies a 4-bit slot in the MUX registers (MUX_BITS_PER_PIN), + * but only bits [2:0] select the mux function; bit 3 is reserved read-only + * and must not be written. MUX_FUNC_MASK therefore covers 3 bits, not 4. + */ +#define ASPEED_G7_SOC1_MUX_FUNC_MASK 0x7 +#define ASPEED_G7_SOC1_MUX_BITS_PER_PIN 4 +#define ASPEED_G7_SOC1_MUX_PINS_PER_REG \ + (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_MUX_BITS_PER_PIN) + +#define ASPEED_G7_SOC1_BIAS_FUNC_MASK 0x1 +#define ASPEED_G7_SOC1_BIAS_BITS_PER_PIN 1 +#define ASPEED_G7_SOC1_BIAS_PINS_PER_REG \ + (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_BIAS_BITS_PER_PIN) + +#define ASPEED_G7_SOC1_DRV_FUNC_MASK 0x3 +#define ASPEED_G7_SOC1_DRV_BITS_PER_PIN 2 +#define ASPEED_G7_SOC1_DRV_PINS_PER_REG \ + (ASPEED_G7_SOC1_REG_WIDTH / ASPEED_G7_SOC1_DRV_BITS_PER_PIN) + +#define ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA 4 +#define ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE 1 +#define ASPEED_G7_SOC1_DRV_STRENGTH_MIN_MA \ + (ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE * ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA) +#define ASPEED_G7_SOC1_DRV_STRENGTH_MAX_MA \ + ((ASPEED_G7_SOC1_DRV_FUNC_MASK + ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE) * \ + ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA) + +/* + * NOTE: The numeric values of these enum entries are significant. + * They must match the SoC GPIO numbering / ball-to-GPIO ID mapping. + * Do not reorder alphabetically. + */ +enum { + C16, + C14, + C11, + D9, + F14, + D10, + C12, + C13, + AC26, + AA25, + AB23, + U22, + V21, + N26, + P25, + N25, + V23, + W22, + AB26, + AD26, + P26, + AE26, + AF26, + AF25, + AE25, + AD25, + AF23, + AF20, + AF21, + AE21, + AE23, + AD22, + AF17, + AA16, + Y16, + V17, + J13, + AB16, + AC16, + AF16, + AA15, + AB15, + AC15, + AD15, + Y15, + AA14, + W16, + V16, + AB18, + AC18, + K13, + AA17, + AB17, + AD16, + AC17, + AD17, + AE16, + AE17, + AB24, + W26, + HOLE0, + HOLE1, + HOLE2, + HOLE3, + W25, + Y23, + Y24, + W21, + AA23, + AC22, + AB22, + Y21, + AE20, + AF19, + Y22, + AA20, + AA22, + AB20, + AF18, + AE19, + AD20, + AC20, + AA21, + AB21, + AC19, + AE18, + AD19, + AD18, + U25, + U26, + Y26, + AA24, + R25, + AA26, + R26, + Y25, + B16, + D14, + B15, + B14, + C17, + B13, + E14, + C15, + D24, + B23, + B22, + C23, + B18, + B21, + M15, + B19, + B26, + A25, + A24, + B24, + E26, + A21, + A19, + A18, + D26, + C26, + A23, + A22, + B25, + F26, + A26, + A14, + E10, + E13, + D12, + F10, + E11, + F11, + F13, + N15, + C20, + C19, + A8, + R14, + A7, + P14, + D20, + A6, + B6, + N14, + B7, + B8, + B9, + M14, + J11, + E7, + D19, + B11, + D15, + B12, + B10, + P13, + C18, + C6, + C7, + D7, + N13, + C8, + C9, + C10, + M16, + A15, + G11, + H7, + H8, + H9, + H10, + H11, + J9, + J10, + E9, + F9, + F8, + M13, + F7, + D8, + E8, + L12, + F12, + E12, + J12, + G7, + G8, + G9, + G10, + K12, + W17, + V18, + W18, + Y17, + AA18, + AA13, + Y18, + AA12, + W20, + V20, + Y11, + V14, + V19, + W14, + Y20, + AB19, + U21, + T24, + V24, + V22, + T23, + AC25, + AB25, + AC24, + PCIERC2_PERST, + PORTC_MODE, + PORTD_MODE, + SGMII0, +}; + +struct aspeed_g7_soc1_pinctrl { + struct device *dev; + struct regmap *regmap; + struct pinctrl_dev *pctl; +}; + +struct aspeed_g7_field { + unsigned int reg; + unsigned int shift; + unsigned int mask; +}; + +static struct aspeed_g7_field +aspeed_g7_soc1_pinmux_field_from_pin(unsigned int pin) +{ + return (struct aspeed_g7_field){ + .reg = ASPEED_G7_SOC1_MUX_BASE + + (pin / ASPEED_G7_SOC1_MUX_PINS_PER_REG) * + ASPEED_G7_SOC1_REG_STRIDE, + .shift = (pin % ASPEED_G7_SOC1_MUX_PINS_PER_REG) * + ASPEED_G7_SOC1_MUX_BITS_PER_PIN, + .mask = ASPEED_G7_SOC1_MUX_FUNC_MASK, + }; +} + +static struct aspeed_g7_field +aspeed_g7_soc1_bias_field_from_pin(unsigned int pin) +{ + return (struct aspeed_g7_field){ + .reg = ASPEED_G7_SOC1_BIAS_BASE + + (pin / ASPEED_G7_SOC1_BIAS_PINS_PER_REG) * + ASPEED_G7_SOC1_REG_STRIDE, + .shift = pin % ASPEED_G7_SOC1_BIAS_PINS_PER_REG, + .mask = ASPEED_G7_SOC1_BIAS_FUNC_MASK, + }; +} + +static struct aspeed_g7_field +aspeed_g7_soc1_drv_field_from_idx(unsigned int idx) +{ + return (struct aspeed_g7_field){ + .reg = ASPEED_G7_SOC1_DRV_BASE + + (idx / ASPEED_G7_SOC1_DRV_PINS_PER_REG) * + ASPEED_G7_SOC1_REG_STRIDE, + .shift = (idx % ASPEED_G7_SOC1_DRV_PINS_PER_REG) * + ASPEED_G7_SOC1_DRV_BITS_PER_PIN, + .mask = ASPEED_G7_SOC1_DRV_FUNC_MASK, + }; +} + +#define PIN(n) PINCTRL_PIN(n, #n) + +static const struct pinctrl_pin_desc aspeed_g7_soc1_pins[] = { + PIN(C16), + PIN(C14), + PIN(C11), + PIN(D9), + PIN(F14), + PIN(D10), + PIN(C12), + PIN(C13), + PIN(AC26), + PIN(AA25), + PIN(AB23), + PIN(U22), + PIN(V21), + PIN(N26), + PIN(P25), + PIN(N25), + PIN(V23), + PIN(W22), + PIN(AB26), + PIN(AD26), + PIN(P26), + PIN(AE26), + PIN(AF26), + PIN(AF25), + PIN(AE25), + PIN(AD25), + PIN(AF23), + PIN(AF20), + PIN(AF21), + PIN(AE21), + PIN(AE23), + PIN(AD22), + PIN(AF17), + PIN(AA16), + PIN(Y16), + PIN(V17), + PIN(J13), + PIN(AB16), + PIN(AC16), + PIN(AF16), + PIN(AA15), + PIN(AB15), + PIN(AC15), + PIN(AD15), + PIN(Y15), + PIN(AA14), + PIN(W16), + PIN(V16), + PIN(AB18), + PIN(AC18), + PIN(K13), + PIN(AA17), + PIN(AB17), + PIN(AD16), + PIN(AC17), + PIN(AD17), + PIN(AE16), + PIN(AE17), + PIN(AB24), + PIN(W26), + PIN(HOLE0), + PIN(HOLE1), + PIN(HOLE2), + PIN(HOLE3), + PIN(W25), + PIN(Y23), + PIN(Y24), + PIN(W21), + PIN(AA23), + PIN(AC22), + PIN(AB22), + PIN(Y21), + PIN(AE20), + PIN(AF19), + PIN(Y22), + PIN(AA20), + PIN(AA22), + PIN(AB20), + PIN(AF18), + PIN(AE19), + PIN(AD20), + PIN(AC20), + PIN(AA21), + PIN(AB21), + PIN(AC19), + PIN(AE18), + PIN(AD19), + PIN(AD18), + PIN(U25), + PIN(U26), + PIN(Y26), + PIN(AA24), + PIN(R25), + PIN(AA26), + PIN(R26), + PIN(Y25), + PIN(B16), + PIN(D14), + PIN(B15), + PIN(B14), + PIN(C17), + PIN(B13), + PIN(E14), + PIN(C15), + PIN(D24), + PIN(B23), + PIN(B22), + PIN(C23), + PIN(B18), + PIN(B21), + PIN(M15), + PIN(B19), + PIN(B26), + PIN(A25), + PIN(A24), + PIN(B24), + PIN(E26), + PIN(A21), + PIN(A19), + PIN(A18), + PIN(D26), + PIN(C26), + PIN(A23), + PIN(A22), + PIN(B25), + PIN(F26), + PIN(A26), + PIN(A14), + PIN(E10), + PIN(E13), + PIN(D12), + PIN(F10), + PIN(E11), + PIN(F11), + PIN(F13), + PIN(N15), + PIN(C20), + PIN(C19), + PIN(A8), + PIN(R14), + PIN(A7), + PIN(P14), + PIN(D20), + PIN(A6), + PIN(B6), + PIN(N14), + PIN(B7), + PIN(B8), + PIN(B9), + PIN(M14), + PIN(J11), + PIN(E7), + PIN(D19), + PIN(B11), + PIN(D15), + PIN(B12), + PIN(B10), + PIN(P13), + PIN(C18), + PIN(C6), + PIN(C7), + PIN(D7), + PIN(N13), + PIN(C8), + PIN(C9), + PIN(C10), + PIN(M16), + PIN(A15), + PIN(G11), + PIN(H7), + PIN(H8), + PIN(H9), + PIN(H10), + PIN(H11), + PIN(J9), + PIN(J10), + PIN(E9), + PIN(F9), + PIN(F8), + PIN(M13), + PIN(F7), + PIN(D8), + PIN(E8), + PIN(L12), + PIN(F12), + PIN(E12), + PIN(J12), + PIN(G7), + PIN(G8), + PIN(G9), + PIN(G10), + PIN(K12), + PIN(W17), + PIN(V18), + PIN(W18), + PIN(Y17), + PIN(AA18), + PIN(AA13), + PIN(Y18), + PIN(AA12), + PIN(W20), + PIN(V20), + PIN(Y11), + PIN(V14), + PIN(V19), + PIN(W14), + PIN(Y20), + PIN(AB19), + PIN(U21), + PIN(T24), + PIN(V24), + PIN(V22), + PIN(T23), + PIN(AC25), + PIN(AB25), + PIN(AC24), + PIN(PCIERC2_PERST), + PIN(PORTC_MODE), + PIN(PORTD_MODE), + PIN(SGMII0), +}; + +static const struct pinctrl_ops aspeed_g7_soc1_pctl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_free_map, +}; + +struct aspeed_g7_soc1_function { + struct pinfunction pinfunction; + const u8 *muxvals; +}; + +static int aspeed_g7_soc1_drive_strength_to_hw(u32 strength, + unsigned int *val) +{ + if (strength < ASPEED_G7_SOC1_DRV_STRENGTH_MIN_MA || + strength > ASPEED_G7_SOC1_DRV_STRENGTH_MAX_MA || + strength % ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA) + return -EINVAL; + + *val = (strength / ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA) - + ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE; + + return 0; +} + +static int aspeed_g7_soc1_set_mux(struct pinctrl_dev *pctldev, + unsigned int fselector, unsigned int group) +{ + struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + const struct aspeed_g7_soc1_function *soc1_func; + const struct function_desc *fd; + const struct pinfunction *func; + const struct pingroup *grp; + struct group_desc *gd; + const char *gname; + int i, g_idx = -1, ret; + + gd = pinctrl_generic_get_group(pctldev, group); + if (!gd) + return -EINVAL; + + grp = &gd->grp; + + fd = pinmux_generic_get_function(pctldev, fselector); + if (!fd) + return -EINVAL; + + soc1_func = fd->data; + if (!soc1_func) + return -EINVAL; + + func = &soc1_func->pinfunction; + gname = grp->name; + + for (i = 0; i < func->ngroups; i++) { + if (!strcmp(gname, func->groups[i])) { + g_idx = i; + break; + } + } + + if (g_idx < 0) + return -EINVAL; + + for (i = 0; i < grp->npins; i++) { + unsigned int val = soc1_func->muxvals[g_idx]; + unsigned int pin = grp->pins[i]; + struct aspeed_g7_field field; + + if (pin == PCIERC2_PERST) { + /* + * PCIERC2_PERST is a special case: it is managed by a + * dedicated control register (0x908) instead of the + * standard 4-bit multi-function field. + */ + field.reg = ASPEED_G7_SOC1_PCIE_REG; + field.shift = 0; + field.mask = 0x1; + val = 1; + } else if (pin == PORTC_MODE || pin == PORTD_MODE) { + /* + * PORTC_MODE and PORTD_MODE are virtual "pins" that + * control the USB 2.0 controller mode settings. + * These reside in a specific control register (0x3B0) + * with non-standard bit widths. + */ + field.reg = ASPEED_G7_SOC1_USB_MODE_REG; + field.mask = 0x3; + field.shift = pin == PORTC_MODE ? 0 : 2; + } else if (pin == SGMII0) { + /* + * SGMII0 is a virtual pin whose mux control resides at + * SCU47C bit 0, outside the contiguous pin-indexed MUX + * register range starting at MUX_BASE. The field is + * 1 bit wide; use a 1-bit mask to avoid clobbering + * adjacent bits in SCU47C. + */ + field.reg = ASPEED_G7_SOC1_SGMII_REG; + field.shift = 0; + field.mask = 0x1; + } else { + /* Standard 4-bit-per-pin multi-function configuration */ + field = aspeed_g7_soc1_pinmux_field_from_pin(pin); + } + + dev_dbg(pctl->dev, + "Setting pin %u reg 0x%x shift %u to function %s (muxval=0x%x)\n", + pin, field.reg, field.shift, func->name, val); + + ret = regmap_update_bits(pctl->regmap, field.reg, + field.mask << field.shift, + val << field.shift); + if (ret) + return ret; + } + + return 0; +} + +static int aspeed_g7_soc1_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct aspeed_g7_field field; + int ret = -ENOTSUPP; + + if (pin <= AC24) { + field = aspeed_g7_soc1_pinmux_field_from_pin(pin); + ret = regmap_update_bits(pctl->regmap, field.reg, + field.mask << field.shift, 0); + } + + return ret; +} + +static const struct pinmux_ops aspeed_g7_soc1_pmx_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = aspeed_g7_soc1_set_mux, + .gpio_request_enable = aspeed_g7_soc1_gpio_request_enable, + .strict = true, +}; + +/* + * aspeed_g7_soc1_drv_map - Mapping table for pin drive strength control. + * + * In AST2700 SOC1, drive strength configuration is architecturally decoupled + * from the main pin mux registers (0x400 range). It is managed by a separate + * set of registers starting at 0x4C0. + * + * This table is required because: + * 1. The mapping between physical pin IDs and drive strength control slots + * is non-linear and sparse. + * For example, W25 maps to field index 8 (stored as 9), + * meaning it occupies bits [17:16] of the first 0x4C0 register. + * 2. Only a subset of physical pins supports drive strength configuration. + * + * The table stores (drive strength field index + 1). + * The field index refers to the 2-bit drive strength field position within the + * 0x4C0 register range. A value of 0 indicates that the pin does not support + * drive strength configuration (returning -ENOTSUPP). + * This +1 offset allows us to rely on C's default zero-initialization for + * unsupported pins while avoiding compiler warnings regarding overridden + * initializers. + */ +static const int aspeed_g7_soc1_drv_map[ASPEED_G7_SOC1_NR_PINS] = { + [C16] = 1, [C14] = 2, [C11] = 3, [D9] = 4, [F14] = 5, [D10] = 6, [C12] = 7, + [C13] = 8, [W25] = 9, [Y23] = 10, [Y24] = 11, [W21] = 12, [AA23] = 13, [AC22] = 14, + [AB22] = 15, [Y21] = 16, [AE20] = 17, [AF19] = 18, [Y22] = 19, [AA20] = 20, [AA22] = 21, + [AB20] = 22, [AF18] = 23, [AE19] = 24, [AD20] = 25, [AC20] = 26, [AA21] = 27, [AB21] = 28, + [AC19] = 29, [AE18] = 30, [AD19] = 31, [AD18] = 32, [U25] = 33, [U26] = 34, [Y26] = 35, + [AA24] = 36, [R25] = 37, [AA26] = 38, [R26] = 39, [Y25] = 40, [B16] = 41, [D14] = 42, + [B15] = 43, [B14] = 44, [C17] = 45, [B13] = 46, [E14] = 47, [C15] = 48, [D24] = 49, + [B23] = 50, [B22] = 51, [C23] = 52, [B18] = 53, [B21] = 54, [M15] = 55, [B19] = 56, + [B26] = 57, [A25] = 58, [A24] = 59, [B24] = 60, [E26] = 61, [A21] = 62, [A19] = 63, + [A18] = 64, [D26] = 65, [C26] = 66, [A23] = 67, [A22] = 68, [B25] = 69, [F26] = 70, + [A26] = 71, [A14] = 72, [E10] = 73, [E13] = 74, [D12] = 75, [F10] = 76, [E11] = 77, + [F11] = 78, [F13] = 79, [N15] = 80, [C20] = 81, [C19] = 82, [A8] = 83, [R14] = 84, + [A7] = 85, [P14] = 86, [D20] = 87, [A6] = 88, [B6] = 89, [N14] = 90, [B7] = 91, + [B8] = 92, [B9] = 93, [M14] = 94, [J11] = 95, [E7] = 96, [D19] = 97, [B11] = 98, + [D15] = 99, [B12] = 100, [B10] = 101, [P13] = 102, [C18] = 103, [C6] = 104, [C7] = 105, + [D7] = 106, [N13] = 107, [C8] = 108, [C9] = 109, [C10] = 110, [M16] = 111, [A15] = 112, + [E9] = 113, [F9] = 114, [F8] = 115, [M13] = 116, [F7] = 117, [D8] = 118, [E8] = 119, + [L12] = 120, +}; + +static int aspeed_g7_soc1_pin_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *config) +{ + struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + struct aspeed_g7_field field; + unsigned int val, val_raw; + int ret, ds_idx; + + if (pin > AC24) + return -EINVAL; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + field = aspeed_g7_soc1_bias_field_from_pin(pin); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + /* + * The hardware has a single 1-bit enable/disable field per + * pin; pull direction is fixed in silicon and cannot be read + * back from the register. Reject readback requests for a + * specific pull direction. + */ + return -ENOTSUPP; + case PIN_CONFIG_DRIVE_STRENGTH: + ds_idx = aspeed_g7_soc1_drv_map[pin]; + if (!ds_idx) + return -ENOTSUPP; + ds_idx--; /* Adjust back to 0-based hardware index */ + field = aspeed_g7_soc1_drv_field_from_idx(ds_idx); + break; + default: + return -ENOTSUPP; + } + + ret = regmap_read(pctl->regmap, field.reg, &val_raw); + if (ret) + return ret; + + val = (val_raw & (field.mask << field.shift)) >> field.shift; + if (param == PIN_CONFIG_DRIVE_STRENGTH) + val = (val + ASPEED_G7_SOC1_DRV_STRENGTH_HW_BASE) * + ASPEED_G7_SOC1_DRV_STRENGTH_STEP_MA; + + if (!val) + return -EINVAL; + + *config = pinconf_to_config_packed(param, val); + + return 0; +} + +static int aspeed_g7_soc1_pin_config_set(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *configs, + unsigned int num_configs) +{ + struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct aspeed_g7_field field; + enum pin_config_param param; + int i, ret, ds_idx; + unsigned int val; + u32 arg; + + if (pin > AC24) + return -EINVAL; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + /* + * The hardware has one enable/disable bit per pin; + * pull direction is fixed in silicon. Both PULL_UP + * and PULL_DOWN map to "enable bias"; the caller must + * request the direction that the hardware provides. + */ + case PIN_CONFIG_BIAS_DISABLE: + field = aspeed_g7_soc1_bias_field_from_pin(pin); + val = (param == PIN_CONFIG_BIAS_DISABLE) ? 1 : 0; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + ds_idx = aspeed_g7_soc1_drv_map[pin]; + if (!ds_idx) + return -ENOTSUPP; + ds_idx--; /* Adjust back to 0-based hardware index */ + field = aspeed_g7_soc1_drv_field_from_idx(ds_idx); + ret = aspeed_g7_soc1_drive_strength_to_hw(arg, &val); + if (ret) + return ret; + break; + default: + return -ENOTSUPP; + } + + dev_dbg(pctl->dev, + "Configuring pin %u reg 0x%x shift %u param %d arg %u val 0x%x\n", + pin, field.reg, field.shift, param, arg, val); + + ret = regmap_update_bits(pctl->regmap, field.reg, + field.mask << field.shift, + val << field.shift); + + if (ret) + return ret; + } + + return 0; +} + +static int aspeed_g7_soc1_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, selector, &pins, &npins); + if (ret) + return ret; + if (!npins) + return -ENODEV; + + return aspeed_g7_soc1_pin_config_get(pctldev, pins[0], config); +} + +static int aspeed_g7_soc1_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins; + int ret; + int i; + + ret = pinctrl_generic_get_group_pins(pctldev, selector, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; i++) { + ret = aspeed_g7_soc1_pin_config_set(pctldev, pins[i], configs, + num_configs); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinconf_ops aspeed_g7_soc1_conf_ops = { + .is_generic = true, + .pin_config_get = aspeed_g7_soc1_pin_config_get, + .pin_config_set = aspeed_g7_soc1_pin_config_set, + .pin_config_group_get = aspeed_g7_soc1_pin_config_group_get, + .pin_config_group_set = aspeed_g7_soc1_pin_config_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +static const struct pinctrl_desc aspeed_g7_soc1_desc = { + .name = "aspeed-g7-soc1-pinctrl", + .pins = aspeed_g7_soc1_pins, + .npins = ARRAY_SIZE(aspeed_g7_soc1_pins), + .pctlops = &aspeed_g7_soc1_pctl_ops, + .pmxops = &aspeed_g7_soc1_pmx_ops, + .confops = &aspeed_g7_soc1_conf_ops, + .owner = THIS_MODULE, +}; + + #define PIN_GROUP(name, ...) static const unsigned int name ## _pins[] = { __VA_ARGS__ } + +/* Pin Groups and Functions */ +PIN_GROUP(ADC0, W17); +PIN_GROUP(ADC1, V18); +PIN_GROUP(ADC10, Y11); +PIN_GROUP(ADC11, V14); +PIN_GROUP(ADC12, V19); +PIN_GROUP(ADC13, W14); +PIN_GROUP(ADC14, Y20); +PIN_GROUP(ADC15, AB19); +PIN_GROUP(ADC2, W18); +PIN_GROUP(ADC3, Y17); +PIN_GROUP(ADC4, AA18); +PIN_GROUP(ADC5, AA13); +PIN_GROUP(ADC6, Y18); +PIN_GROUP(ADC7, AA12); +PIN_GROUP(ADC8, W20); +PIN_GROUP(ADC9, V20); +PIN_GROUP(AUXPWRGOOD0, W14); +PIN_GROUP(AUXPWRGOOD1, Y20); +PIN_GROUP(CANBUS, G7, G8, G9); +PIN_GROUP(DI2C0, C16, D9); +PIN_GROUP(DI2C1, C14, F14); +PIN_GROUP(DI2C10, R25, AA26); +PIN_GROUP(DI2C11, R26, Y25); +PIN_GROUP(DI2C12, W25, Y23); +PIN_GROUP(DI2C13, Y24, W21); +PIN_GROUP(DI2C14, AA23, AC22); +PIN_GROUP(DI2C15, AB22, Y21); +PIN_GROUP(DI2C2, D10, C12); +PIN_GROUP(DI2C3, C11, C13); +PIN_GROUP(DI2C8, U25, U26); +PIN_GROUP(DI2C9, Y26, AA24); +PIN_GROUP(DSGPM0, D19, B10, C7, D7); +PIN_GROUP(ESPI0, B16, D14, B15, B14, C17, B13, E14, C15); +PIN_GROUP(ESPI1, C16, C14, C11, D9, F14, D10, C12, C13); +PIN_GROUP(FSI0, AD20, AC20); +PIN_GROUP(FSI1, AA21, AB21); +PIN_GROUP(FSI2, AC19, AE18); +PIN_GROUP(FSI3, AD19, AD18); +PIN_GROUP(FWQSPI, M16, A15); +PIN_GROUP(FWSPIABR, A14); +PIN_GROUP(FWWPN, N15); +PIN_GROUP(HBLED, V24); +PIN_GROUP(HVI3C0, U25, U26); +PIN_GROUP(HVI3C1, Y26, AA24); +PIN_GROUP(HVI3C12, W25, Y23); +PIN_GROUP(HVI3C13, Y24, W21); +PIN_GROUP(HVI3C14, AA23, AC22); +PIN_GROUP(HVI3C15, AB22, Y21); +PIN_GROUP(HVI3C2, R25, AA26); +PIN_GROUP(HVI3C3, R26, Y25); +PIN_GROUP(I2C0, G11, H7); +PIN_GROUP(I2C1, H8, H9); +PIN_GROUP(I2C10, G8, G9); +PIN_GROUP(I2C11, G10, K12); +PIN_GROUP(I2C12, AC18, AA17); +PIN_GROUP(I2C13, AB17, AD16); +PIN_GROUP(I2C14, AC17, AD17); +PIN_GROUP(I2C15, AE16, AE17); +PIN_GROUP(I2C2, H10, H11); +PIN_GROUP(I2C3, J9, J10); +PIN_GROUP(I2C4, E9, F9); +PIN_GROUP(I2C5, F8, M13); +PIN_GROUP(I2C6, F7, D8); +PIN_GROUP(I2C7, E8, L12); +PIN_GROUP(I2C8, F12, E12); +PIN_GROUP(I2C9, J12, G7); +PIN_GROUP(I2CF0, F12, E12, J12, G7); +PIN_GROUP(I2CF1, E9, F9, F8, M13); +PIN_GROUP(I2CF2, F7, D8, E8, L12); +PIN_GROUP(I3C10, AC19, AE18); +PIN_GROUP(I3C11, AD19, AD18); +PIN_GROUP(I3C4, AE20, AF19); +PIN_GROUP(I3C5, Y22, AA20); +PIN_GROUP(I3C6, AA22, AB20); +PIN_GROUP(I3C7, AF18, AE19); +PIN_GROUP(I3C8, AD20, AC20); +PIN_GROUP(I3C9, AA21, AB21); +PIN_GROUP(JTAGM1, D12, F10, E11, F11, F13); +PIN_GROUP(LPC0, AF26, AF25, B16, D14, B15, B14, C17, B13, E14, C15); +PIN_GROUP(LPC1, C16, C14, C11, D9, F14, D10, C12, C13, AE16, AE17); +PIN_GROUP(LTPI, U25, U26, Y26, AA24); +PIN_GROUP(LTPI_PS_I2C0, G11, H7); +PIN_GROUP(LTPI_PS_I2C1, H8, H9); +PIN_GROUP(LTPI_PS_I2C2, H10, H11); +PIN_GROUP(LTPI_PS_I2C3, J9, J10); +PIN_GROUP(MACLINK0, U21); +PIN_GROUP(MACLINK1, AC24); +PIN_GROUP(MACLINK2, T24); +PIN_GROUP(MDIO0, B9, M14); +PIN_GROUP(MDIO1, C9, C10); +PIN_GROUP(MDIO2, E10, E13); +PIN_GROUP(NCTS0, AF17); +PIN_GROUP(NCTS1, AA15); +PIN_GROUP(NCTS5, V21); +PIN_GROUP(NCTS6, AB26); +PIN_GROUP(NDCD0, AA16); +PIN_GROUP(NDCD1, AB15); +PIN_GROUP(NDCD5, N26); +PIN_GROUP(NDCD6, AD26); +PIN_GROUP(NDSR0, Y16); +PIN_GROUP(NDSR1, AC15); +PIN_GROUP(NDSR5, P25); +PIN_GROUP(NDSR6, P26); +PIN_GROUP(NDTR0, J13); +PIN_GROUP(NDTR1, Y15); +PIN_GROUP(NDTR5, V23); +PIN_GROUP(NDTR6, AF26); +PIN_GROUP(NRI0, V17); +PIN_GROUP(NRI1, AD15); +PIN_GROUP(NRI5, N25); +PIN_GROUP(NRI6, AE26); +PIN_GROUP(NRTS0, AB16); +PIN_GROUP(NRTS1, AA14); +PIN_GROUP(NRTS5, W22); +PIN_GROUP(NRTS6, AF25); +PIN_GROUP(OSCCLK, C17); +PIN_GROUP(PE2SGRSTN, E10, PCIERC2_PERST); +PIN_GROUP(PWM0, AE25); +PIN_GROUP(PWM1, AD25); +PIN_GROUP(PWM10, AB17); +PIN_GROUP(PWM11, AD16); +PIN_GROUP(PWM12, AC17); +PIN_GROUP(PWM13, AD17); +PIN_GROUP(PWM14, AE16); +PIN_GROUP(PWM15, AE17); +PIN_GROUP(PWM2, AF23); +PIN_GROUP(PWM3, AF20); +PIN_GROUP(PWM4, AF21); +PIN_GROUP(PWM5, AE21); +PIN_GROUP(PWM6, AE23); +PIN_GROUP(PWM7, AD22); +PIN_GROUP(PWM8, K13); +PIN_GROUP(PWM9, AA17); +PIN_GROUP(QSPI0, C23, B18); +PIN_GROUP(QSPI1, B24, E26); +PIN_GROUP(QSPI2, B25, F26); +PIN_GROUP(RGMII0, C20, C19, A8, R14, A7, P14, D20, A6, B6, N14, B7, B8); +PIN_GROUP(RGMII1, D19, B11, D15, B12, B10, P13, C18, C6, C7, D7, N13, C8); +PIN_GROUP(RMII0, C20, A8, R14, A7, P14, A6, B6, N14); +PIN_GROUP(RMII0RCLKO, D20); +PIN_GROUP(RMII1, D19, D15, B12, B10, P13, C6, C7, D7); +PIN_GROUP(RMII1RCLKO, C18); +PIN_GROUP(SALT0, AC17); +PIN_GROUP(SALT1, AD17); +PIN_GROUP(SALT10, Y18); +PIN_GROUP(SALT11, AA12); +PIN_GROUP(SALT12, AB26); +PIN_GROUP(SALT13, AD26); +PIN_GROUP(SALT14, P26); +PIN_GROUP(SALT15, AE26); +PIN_GROUP(SALT2, AC15); +PIN_GROUP(SALT3, AD15); +PIN_GROUP(SALT4, W17); +PIN_GROUP(SALT5, V18); +PIN_GROUP(SALT6, W18); +PIN_GROUP(SALT7, Y17); +PIN_GROUP(SALT8, AA18); +PIN_GROUP(SALT9, AA13); +PIN_GROUP(SD, C16, C14, C11, D9, F14, D10, C12, C13); +PIN_GROUP(SGMII, SGMII0); +PIN_GROUP(SGPM0, U21, T24, V22, T23); +PIN_GROUP(SGPM1, AC25, AB25, AB24, W26); +PIN_GROUP(SGPS, B11, C18, N13, C8); +PIN_GROUP(SIOONCTRLN0, AE23); +PIN_GROUP(SIOONCTRLN1, AA15); +PIN_GROUP(SIOPBIN0, AD25); +PIN_GROUP(SIOPBIN1, AA16); +PIN_GROUP(SIOPBON0, AE25); +PIN_GROUP(SIOPBON1, AF17); +PIN_GROUP(SIOPWREQN0, AE21); +PIN_GROUP(SIOPWREQN1, AB16); +PIN_GROUP(SIOPWRGD1, AB15); +PIN_GROUP(SIOS3N0, AF20); +PIN_GROUP(SIOS3N1, V17); +PIN_GROUP(SIOS5N0, AF21); +PIN_GROUP(SIOS5N1, J13); +PIN_GROUP(SIOSCIN0, AF23); +PIN_GROUP(SIOSCIN1, Y16); +PIN_GROUP(SMON0, U21, T24, V22, T23); +PIN_GROUP(SMON1, W26, AC25, AB25); +PIN_GROUP(SPI0, D24, B23, B22); +PIN_GROUP(SPI0ABR, M15); +PIN_GROUP(SPI0CS1, B21); +PIN_GROUP(SPI0WPN, B19); +PIN_GROUP(SPI1, B26, A25, A24); +PIN_GROUP(SPI1ABR, A19); +PIN_GROUP(SPI1CS1, A21); +PIN_GROUP(SPI1WPN, A18); +PIN_GROUP(SPI2, D26, C26, A23, A22); +PIN_GROUP(SPI2CS1, A26); +PIN_GROUP(TACH0, AC26); +PIN_GROUP(TACH1, AA25); +PIN_GROUP(TACH10, AB26); +PIN_GROUP(TACH11, AD26); +PIN_GROUP(TACH12, P26); +PIN_GROUP(TACH13, AE26); +PIN_GROUP(TACH14, AF26); +PIN_GROUP(TACH15, AF25); +PIN_GROUP(TACH2, AB23); +PIN_GROUP(TACH3, U22); +PIN_GROUP(TACH4, V21); +PIN_GROUP(TACH5, N26); +PIN_GROUP(TACH6, P25); +PIN_GROUP(TACH7, N25); +PIN_GROUP(TACH8, V23); +PIN_GROUP(TACH9, W22); +PIN_GROUP(THRU0, AC26, AA25); +PIN_GROUP(THRU1, AB23, U22); +PIN_GROUP(THRU2, A19, A18); +PIN_GROUP(THRU3, B25, F26); +PIN_GROUP(UART0, AC16, AF16); +PIN_GROUP(UART1, W16, V16); +PIN_GROUP(UART2, AB18, AC18); +PIN_GROUP(UART3, K13, AA17); +PIN_GROUP(UART5, AB17, AD16); +PIN_GROUP(UART6, AC17, AD17); +PIN_GROUP(UART7, AE16, AE17); +PIN_GROUP(UART8, M15, B19); +PIN_GROUP(UART9, B26, A25); +PIN_GROUP(UART10, A24, B24); +PIN_GROUP(UART11, E26, A21); +PIN_GROUP(USB2CD, PORTC_MODE); +PIN_GROUP(USB2CH, PORTC_MODE); +PIN_GROUP(USB2CU, PORTC_MODE); +PIN_GROUP(USB2CUD, PORTC_MODE); +PIN_GROUP(USB2DD, PORTD_MODE); +PIN_GROUP(USB2DH, PORTD_MODE); +PIN_GROUP(USBUART, G10, K12); +PIN_GROUP(VGA, J11, E7); +PIN_GROUP(VPI, C16, C14, C11, D9, F14, D10, AC26, AA25, AB23, U22, V21, N26, + P25, N25, V23, W22, AB26, AD26, P26, AE26, AF26, AF25, AE25, AD25, + AF23, AF20, AF21, AE21); +PIN_GROUP(WDTRST0N, K13); +PIN_GROUP(WDTRST1N, AA17); +PIN_GROUP(WDTRST2N, AB17); +PIN_GROUP(WDTRST3N, AD16); +PIN_GROUP(WDTRST4N, AC25); +PIN_GROUP(WDTRST5N, AB25); +PIN_GROUP(WDTRST6N, AC24); +PIN_GROUP(WDTRST7N, AB24); + +#define GROUP(n) PINCTRL_PINGROUP(#n, n##_pins, ARRAY_SIZE(n##_pins)) + +static const struct pingroup aspeed_g7_soc1_groups[] = { + GROUP(ADC0), + GROUP(ADC1), + GROUP(ADC10), + GROUP(ADC11), + GROUP(ADC12), + GROUP(ADC13), + GROUP(ADC14), + GROUP(ADC15), + GROUP(ADC2), + GROUP(ADC3), + GROUP(ADC4), + GROUP(ADC5), + GROUP(ADC6), + GROUP(ADC7), + GROUP(ADC8), + GROUP(ADC9), + GROUP(AUXPWRGOOD0), + GROUP(AUXPWRGOOD1), + GROUP(CANBUS), + GROUP(DI2C0), + GROUP(DI2C1), + GROUP(DI2C10), + GROUP(DI2C11), + GROUP(DI2C12), + GROUP(DI2C13), + GROUP(DI2C14), + GROUP(DI2C15), + GROUP(DI2C2), + GROUP(DI2C3), + GROUP(DI2C8), + GROUP(DI2C9), + GROUP(DSGPM0), + GROUP(ESPI0), + GROUP(ESPI1), + GROUP(FSI0), + GROUP(FSI1), + GROUP(FSI2), + GROUP(FSI3), + GROUP(FWQSPI), + GROUP(FWSPIABR), + GROUP(FWWPN), + GROUP(HBLED), + GROUP(HVI3C0), + GROUP(HVI3C1), + GROUP(HVI3C12), + GROUP(HVI3C13), + GROUP(HVI3C14), + GROUP(HVI3C15), + GROUP(HVI3C2), + GROUP(HVI3C3), + GROUP(I2C0), + GROUP(I2C1), + GROUP(I2C10), + GROUP(I2C11), + GROUP(I2C12), + GROUP(I2C13), + GROUP(I2C14), + GROUP(I2C15), + GROUP(I2C2), + GROUP(I2C3), + GROUP(I2C4), + GROUP(I2C5), + GROUP(I2C6), + GROUP(I2C7), + GROUP(I2C8), + GROUP(I2C9), + GROUP(I2CF0), + GROUP(I2CF1), + GROUP(I2CF2), + GROUP(I3C10), + GROUP(I3C11), + GROUP(I3C4), + GROUP(I3C5), + GROUP(I3C6), + GROUP(I3C7), + GROUP(I3C8), + GROUP(I3C9), + GROUP(JTAGM1), + GROUP(LPC0), + GROUP(LPC1), + GROUP(LTPI), + GROUP(LTPI_PS_I2C0), + GROUP(LTPI_PS_I2C1), + GROUP(LTPI_PS_I2C2), + GROUP(LTPI_PS_I2C3), + GROUP(MACLINK0), + GROUP(MACLINK1), + GROUP(MACLINK2), + GROUP(MDIO0), + GROUP(MDIO1), + GROUP(MDIO2), + GROUP(NCTS0), + GROUP(NCTS1), + GROUP(NCTS5), + GROUP(NCTS6), + GROUP(NDCD0), + GROUP(NDCD1), + GROUP(NDCD5), + GROUP(NDCD6), + GROUP(NDSR0), + GROUP(NDSR1), + GROUP(NDSR5), + GROUP(NDSR6), + GROUP(NDTR0), + GROUP(NDTR1), + GROUP(NDTR5), + GROUP(NDTR6), + GROUP(NRI0), + GROUP(NRI1), + GROUP(NRI5), + GROUP(NRI6), + GROUP(NRTS0), + GROUP(NRTS1), + GROUP(NRTS5), + GROUP(NRTS6), + GROUP(OSCCLK), + GROUP(PE2SGRSTN), + GROUP(PWM0), + GROUP(PWM1), + GROUP(PWM10), + GROUP(PWM11), + GROUP(PWM12), + GROUP(PWM13), + GROUP(PWM14), + GROUP(PWM15), + GROUP(PWM2), + GROUP(PWM3), + GROUP(PWM4), + GROUP(PWM5), + GROUP(PWM6), + GROUP(PWM7), + GROUP(PWM8), + GROUP(PWM9), + GROUP(QSPI0), + GROUP(QSPI1), + GROUP(QSPI2), + GROUP(RGMII0), + GROUP(RGMII1), + GROUP(RMII0), + GROUP(RMII0RCLKO), + GROUP(RMII1), + GROUP(RMII1RCLKO), + GROUP(SALT0), + GROUP(SALT1), + GROUP(SALT10), + GROUP(SALT11), + GROUP(SALT12), + GROUP(SALT13), + GROUP(SALT14), + GROUP(SALT15), + GROUP(SALT2), + GROUP(SALT3), + GROUP(SALT4), + GROUP(SALT5), + GROUP(SALT6), + GROUP(SALT7), + GROUP(SALT8), + GROUP(SALT9), + GROUP(SD), + GROUP(SGMII), + GROUP(SGPM0), + GROUP(SGPM1), + GROUP(SGPS), + GROUP(SIOONCTRLN0), + GROUP(SIOONCTRLN1), + GROUP(SIOPBIN0), + GROUP(SIOPBIN1), + GROUP(SIOPBON0), + GROUP(SIOPBON1), + GROUP(SIOPWREQN0), + GROUP(SIOPWREQN1), + GROUP(SIOPWRGD1), + GROUP(SIOS3N0), + GROUP(SIOS3N1), + GROUP(SIOS5N0), + GROUP(SIOS5N1), + GROUP(SIOSCIN0), + GROUP(SIOSCIN1), + GROUP(SMON0), + GROUP(SMON1), + GROUP(SPI0), + GROUP(SPI0ABR), + GROUP(SPI0CS1), + GROUP(SPI0WPN), + GROUP(SPI1), + GROUP(SPI1ABR), + GROUP(SPI1CS1), + GROUP(SPI1WPN), + GROUP(SPI2), + GROUP(SPI2CS1), + GROUP(TACH0), + GROUP(TACH1), + GROUP(TACH10), + GROUP(TACH11), + GROUP(TACH12), + GROUP(TACH13), + GROUP(TACH14), + GROUP(TACH15), + GROUP(TACH2), + GROUP(TACH3), + GROUP(TACH4), + GROUP(TACH5), + GROUP(TACH6), + GROUP(TACH7), + GROUP(TACH8), + GROUP(TACH9), + GROUP(THRU0), + GROUP(THRU1), + GROUP(THRU2), + GROUP(THRU3), + GROUP(UART0), + GROUP(UART1), + GROUP(UART10), + GROUP(UART11), + GROUP(UART2), + GROUP(UART3), + GROUP(UART5), + GROUP(UART6), + GROUP(UART7), + GROUP(UART8), + GROUP(UART9), + GROUP(USB2CD), + GROUP(USB2CH), + GROUP(USB2CU), + GROUP(USB2CUD), + GROUP(USB2DD), + GROUP(USB2DH), + GROUP(USBUART), + GROUP(VGA), + GROUP(VPI), + GROUP(WDTRST0N), + GROUP(WDTRST1N), + GROUP(WDTRST2N), + GROUP(WDTRST3N), + GROUP(WDTRST4N), + GROUP(WDTRST5N), + GROUP(WDTRST6N), + GROUP(WDTRST7N), +}; + +/** + * VM() - Helper macro to unwrap a parenthesized list of arguments. + * @...: The parenthesized list to be unwrapped. + * + * Since the C preprocessor treats commas inside braces {} as argument + * separators for macros, we wrap lists (like mux values) in parentheses () + * to protect them during macro expansion. This macro strips those + * parentheses when the values are needed for array initialization. + */ +#define VM(...) __VA_ARGS__ + +/** + * FUNC() - Macro to initialize an aspeed_g7_soc1_function entry. + * @n: Name of the pin function. + * @m: Parenthesized list of mux values, mapped 1:1 to the groups list. + * @...: Variable list of pin group names associated with this function. + * + * This macro solves complex static initialization by: + * 1. Creating anonymous arrays for both group names and mux values + * using C99 Compound Literals. + * 2. Using VM(m) to unwrap mux values into the array initializer. + * 3. Calculating the number of groups via sizeof() division, which + * bypasses the __must_be_array() check performed by ARRAY_SIZE() + * that often fails on compound literals in the kernel environment. + * + * Example: FUNC(i2c0, (1, 4), "i2c0", "di2c0") + * Maps "i2c0" group to mux value 1 and "di2c0" group to mux value 4. + */ +#define FUNC(n, m, ...) \ + { \ + .pinfunction = { \ + .name = #n, \ + .groups = (const char *const[]){ __VA_ARGS__ }, \ + .ngroups = sizeof((const char *const[]){ __VA_ARGS__ }) / sizeof(char *), \ + }, \ + .muxvals = (const u8[]){ VM m } \ + } + +static const struct aspeed_g7_soc1_function aspeed_g7_soc1_functions[] = { + FUNC(ADC0, (0), "ADC0"), + FUNC(ADC1, (0), "ADC1"), + FUNC(ADC10, (0), "ADC10"), + FUNC(ADC11, (0), "ADC11"), + FUNC(ADC12, (0), "ADC12"), + FUNC(ADC13, (0), "ADC13"), + FUNC(ADC14, (0), "ADC14"), + FUNC(ADC15, (0), "ADC15"), + FUNC(ADC2, (0), "ADC2"), + FUNC(ADC3, (0), "ADC3"), + FUNC(ADC4, (0), "ADC4"), + FUNC(ADC5, (0), "ADC5"), + FUNC(ADC6, (0), "ADC6"), + FUNC(ADC7, (0), "ADC7"), + FUNC(ADC8, (0), "ADC8"), + FUNC(ADC9, (0), "ADC9"), + FUNC(AUXPWRGOOD0, (2), "AUXPWRGOOD0"), + FUNC(AUXPWRGOOD1, (2), "AUXPWRGOOD1"), + FUNC(CANBUS, (2), "CANBUS"), + FUNC(ESPI0, (1), "ESPI0"), + FUNC(ESPI1, (1), "ESPI1"), + FUNC(FSI0, (2), "FSI0"), + FUNC(FSI1, (2), "FSI1"), + FUNC(FSI2, (2), "FSI2"), + FUNC(FSI3, (2), "FSI3"), + FUNC(FWQSPI, (1), "FWQSPI"), + FUNC(FWSPIABR, (1), "FWSPIABR"), + FUNC(FWWPN, (1), "FWWPN"), + FUNC(HBLED, (2), "HBLED"), + FUNC(I2C0, (1, 2, 4), "I2C0", "LTPI_PS_I2C0", "DI2C0"), + FUNC(I2C1, (1, 2, 4), "I2C1", "LTPI_PS_I2C1", "DI2C1"), + FUNC(I2C10, (1, 2), "I2C10", "DI2C10"), + FUNC(I2C11, (1, 2), "I2C11", "DI2C11"), + FUNC(I2C12, (4, 2), "I2C12", "DI2C12"), + FUNC(I2C13, (4, 2), "I2C13", "DI2C13"), + FUNC(I2C14, (4, 2), "I2C14", "DI2C14"), + FUNC(I2C15, (2, 2), "I2C15", "DI2C15"), + FUNC(I2C2, (1, 2, 4), "I2C2", "LTPI_PS_I2C2", "DI2C2"), + FUNC(I2C3, (1, 2, 4), "I2C3", "LTPI_PS_I2C3", "DI2C3"), + FUNC(I2C4, (1), "I2C4"), + FUNC(I2C5, (1), "I2C5"), + FUNC(I2C6, (1), "I2C6"), + FUNC(I2C7, (1), "I2C7"), + FUNC(I2C8, (1, 2), "I2C8", "DI2C8"), + FUNC(I2C9, (1, 2), "I2C9", "DI2C9"), + FUNC(I2CF0, (5), "I2CF0"), + FUNC(I2CF1, (5), "I2CF1"), + FUNC(I2CF2, (5), "I2CF2"), + FUNC(I3C0, (1), "HVI3C0"), + FUNC(I3C1, (1), "HVI3C1"), + FUNC(I3C10, (1), "I3C10"), + FUNC(I3C11, (1), "I3C11"), + FUNC(I3C12, (1), "HVI3C12"), + FUNC(I3C13, (1), "HVI3C13"), + FUNC(I3C14, (1), "HVI3C14"), + FUNC(I3C15, (1), "HVI3C15"), + FUNC(I3C2, (1), "HVI3C2"), + FUNC(I3C3, (1), "HVI3C3"), + FUNC(I3C4, (1), "I3C4"), + FUNC(I3C5, (1), "I3C5"), + FUNC(I3C6, (1), "I3C6"), + FUNC(I3C7, (1), "I3C7"), + FUNC(I3C8, (1), "I3C8"), + FUNC(I3C9, (1), "I3C9"), + FUNC(JTAGM1, (1), "JTAGM1"), + FUNC(LPC0, (2), "LPC0"), + FUNC(LPC1, (2), "LPC1"), + FUNC(LTPI, (2), "LTPI"), + FUNC(MACLINK0, (4), "MACLINK0"), + FUNC(MACLINK1, (3), "MACLINK1"), + FUNC(MACLINK2, (4), "MACLINK2"), + FUNC(MDIO0, (1), "MDIO0"), + FUNC(MDIO1, (1), "MDIO1"), + FUNC(MDIO2, (1), "MDIO2"), + FUNC(NCTS0, (1), "NCTS0"), + FUNC(NCTS1, (1), "NCTS1"), + FUNC(NCTS5, (4), "NCTS5"), + FUNC(NCTS6, (4), "NCTS6"), + FUNC(NDCD0, (1), "NDCD0"), + FUNC(NDCD1, (1), "NDCD1"), + FUNC(NDCD5, (4), "NDCD5"), + FUNC(NDCD6, (4), "NDCD6"), + FUNC(NDSR0, (1), "NDSR0"), + FUNC(NDSR1, (1), "NDSR1"), + FUNC(NDSR5, (4), "NDSR5"), + FUNC(NDSR6, (4), "NDSR6"), + FUNC(NDTR0, (1), "NDTR0"), + FUNC(NDTR1, (1), "NDTR1"), + FUNC(NDTR5, (4), "NDTR5"), + FUNC(NDTR6, (4), "NDTR6"), + FUNC(NRI0, (1), "NRI0"), + FUNC(NRI1, (1), "NRI1"), + FUNC(NRI5, (4), "NRI5"), + FUNC(NRI6, (4), "NRI6"), + FUNC(NRTS0, (1), "NRTS0"), + FUNC(NRTS1, (1), "NRTS1"), + FUNC(NRTS5, (4), "NRTS5"), + FUNC(NRTS6, (4), "NRTS6"), + FUNC(OSCCLK, (3), "OSCCLK"), + FUNC(PCIERC, (2), "PE2SGRSTN"), + FUNC(PWM0, (1), "PWM0"), + FUNC(PWM1, (1), "PWM1"), + FUNC(PWM10, (3), "PWM10"), + FUNC(PWM11, (3), "PWM11"), + FUNC(PWM12, (3), "PWM12"), + FUNC(PWM13, (3), "PWM13"), + FUNC(PWM14, (3), "PWM14"), + FUNC(PWM15, (3), "PWM15"), + FUNC(PWM2, (1), "PWM2"), + FUNC(PWM3, (1), "PWM3"), + FUNC(PWM4, (1), "PWM4"), + FUNC(PWM5, (1), "PWM5"), + FUNC(PWM6, (1), "PWM6"), + FUNC(PWM7, (1), "PWM7"), + FUNC(PWM8, (3), "PWM8"), + FUNC(PWM9, (3), "PWM9"), + FUNC(QSPI0, (1), "QSPI0"), + FUNC(QSPI1, (1), "QSPI1"), + FUNC(QSPI2, (1), "QSPI2"), + FUNC(RGMII0, (1), "RGMII0"), + FUNC(RGMII1, (1), "RGMII1"), + FUNC(RMII0, (2), "RMII0"), + FUNC(RMII0RCLKO, (2), "RMII0RCLKO"), + FUNC(RMII1, (2), "RMII1"), + FUNC(RMII1RCLKO, (2), "RMII1RCLKO"), + FUNC(SALT0, (2), "SALT0"), + FUNC(SALT1, (2), "SALT1"), + FUNC(SALT10, (2), "SALT10"), + FUNC(SALT11, (2), "SALT11"), + FUNC(SALT12, (2), "SALT12"), + FUNC(SALT13, (2), "SALT13"), + FUNC(SALT14, (2), "SALT14"), + FUNC(SALT15, (2), "SALT15"), + FUNC(SALT2, (2), "SALT2"), + FUNC(SALT3, (2), "SALT3"), + FUNC(SALT4, (2), "SALT4"), + FUNC(SALT5, (2), "SALT5"), + FUNC(SALT6, (2), "SALT6"), + FUNC(SALT7, (2), "SALT7"), + FUNC(SALT8, (2), "SALT8"), + FUNC(SALT9, (2), "SALT9"), + FUNC(SD, (3), "SD"), + FUNC(SGMII, (1), "SGMII"), + FUNC(SGPM0, (1, 4), "SGPM0", "DSGPM0"), + FUNC(SGPM1, (1), "SGPM1"), + FUNC(SGPS, (5), "SGPS"), + FUNC(SIOONCTRLN0, (2), "SIOONCTRLN0"), + FUNC(SIOONCTRLN1, (2), "SIOONCTRLN1"), + FUNC(SIOPBIN0, (2), "SIOPBIN0"), + FUNC(SIOPBIN1, (2), "SIOPBIN1"), + FUNC(SIOPBON0, (2), "SIOPBON0"), + FUNC(SIOPBON1, (2), "SIOPBON1"), + FUNC(SIOPWREQN0, (2), "SIOPWREQN0"), + FUNC(SIOPWREQN1, (2), "SIOPWREQN1"), + FUNC(SIOPWRGD1, (2), "SIOPWRGD1"), + FUNC(SIOS3N0, (2), "SIOS3N0"), + FUNC(SIOS3N1, (2), "SIOS3N1"), + FUNC(SIOS5N0, (2), "SIOS5N0"), + FUNC(SIOS5N1, (2), "SIOS5N1"), + FUNC(SIOSCIN0, (2), "SIOSCIN0"), + FUNC(SIOSCIN1, (2), "SIOSCIN1"), + FUNC(SMON0, (2), "SMON0"), + FUNC(SMON1, (4), "SMON1"), + FUNC(SPI0, (1), "SPI0"), + FUNC(SPI0ABR, (1), "SPI0ABR"), + FUNC(SPI0CS1, (1), "SPI0CS1"), + FUNC(SPI0WPN, (1), "SPI0WPN"), + FUNC(SPI1, (1), "SPI1"), + FUNC(SPI1ABR, (1), "SPI1ABR"), + FUNC(SPI1CS1, (1), "SPI1CS1"), + FUNC(SPI1WPN, (1), "SPI1WPN"), + FUNC(SPI2, (1), "SPI2"), + FUNC(SPI2CS1, (1), "SPI2CS1"), + FUNC(TACH0, (1), "TACH0"), + FUNC(TACH1, (1), "TACH1"), + FUNC(TACH10, (1), "TACH10"), + FUNC(TACH11, (1), "TACH11"), + FUNC(TACH12, (1), "TACH12"), + FUNC(TACH13, (1), "TACH13"), + FUNC(TACH14, (1), "TACH14"), + FUNC(TACH15, (1), "TACH15"), + FUNC(TACH2, (1), "TACH2"), + FUNC(TACH3, (1), "TACH3"), + FUNC(TACH4, (1), "TACH4"), + FUNC(TACH5, (1), "TACH5"), + FUNC(TACH6, (1), "TACH6"), + FUNC(TACH7, (1), "TACH7"), + FUNC(TACH8, (1), "TACH8"), + FUNC(TACH9, (1), "TACH9"), + FUNC(THRU0, (2), "THRU0"), + FUNC(THRU1, (2), "THRU1"), + FUNC(THRU2, (4), "THRU2"), + FUNC(THRU3, (4), "THRU3"), + FUNC(UART0, (1), "UART0"), + FUNC(UART1, (1), "UART1"), + FUNC(UART10, (3), "UART10"), + FUNC(UART11, (3), "UART11"), + FUNC(UART2, (1), "UART2"), + FUNC(UART3, (1), "UART3"), + FUNC(UART5, (4), "UART5"), + FUNC(UART6, (4), "UART6"), + FUNC(UART7, (1), "UART7"), + FUNC(UART8, (3), "UART8"), + FUNC(UART9, (3), "UART9"), + FUNC(USB2C, (0, 1, 2, 3), "USB2CUD", "USB2CD", "USB2CH", "USB2CU"), + FUNC(USB2D, (1, 2), "USB2DD", "USB2DH"), + FUNC(USBUART, (2), "USBUART"), + FUNC(VGA, (1), "VGA"), + FUNC(VPI, (5), "VPI"), + FUNC(WDTRST0N, (2), "WDTRST0N"), + FUNC(WDTRST1N, (2), "WDTRST1N"), + FUNC(WDTRST2N, (2), "WDTRST2N"), + FUNC(WDTRST3N, (2), "WDTRST3N"), + FUNC(WDTRST4N, (2), "WDTRST4N"), + FUNC(WDTRST5N, (2), "WDTRST5N"), + FUNC(WDTRST6N, (2), "WDTRST6N"), + FUNC(WDTRST7N, (2), "WDTRST7N"), +}; + +static int aspeed_g7_soc1_pinctrl_probe(struct platform_device *pdev) +{ + struct aspeed_g7_soc1_pinctrl *pctl; + struct device *dev = &pdev->dev; + int i, ret; + + pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + + pctl->dev = dev; + pctl->regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(pctl->regmap)) { + dev_err(dev, "Failed to get regmap from parent\n"); + return PTR_ERR(pctl->regmap); + } + + ret = devm_pinctrl_register_and_init(dev, &aspeed_g7_soc1_desc, pctl, + &pctl->pctl); + if (ret) { + dev_err(dev, "Failed to register pinctrl\n"); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(aspeed_g7_soc1_groups); i++) { + const struct pingroup *grp = &aspeed_g7_soc1_groups[i]; + + ret = pinctrl_generic_add_group(pctl->pctl, grp->name, + (const unsigned int *)grp->pins, + grp->npins, pctl); + if (ret < 0) { + dev_err(dev, "Failed to add group %s\n", grp->name); + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(aspeed_g7_soc1_functions); i++) { + const struct aspeed_g7_soc1_function *func = &aspeed_g7_soc1_functions[i]; + + ret = pinmux_generic_add_function(pctl->pctl, func->pinfunction.name, + func->pinfunction.groups, + func->pinfunction.ngroups, (void *)func); + if (ret < 0) { + dev_err(dev, "Failed to add function %s\n", func->pinfunction.name); + return ret; + } + } + + return pinctrl_enable(pctl->pctl); +} + +static const struct of_device_id aspeed_g7_soc1_pinctrl_match[] = { + { .compatible = "aspeed,ast2700-soc1-pinctrl" }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_g7_soc1_pinctrl_match); + +static struct platform_driver aspeed_g7_soc1_pinctrl_driver = { + .probe = aspeed_g7_soc1_pinctrl_probe, + .driver = { + .name = "aspeed-g7-soc1-pinctrl", + .of_match_table = aspeed_g7_soc1_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init aspeed_g7_soc1_pinctrl_init(void) +{ + return platform_driver_register(&aspeed_g7_soc1_pinctrl_driver); +} +arch_initcall(aspeed_g7_soc1_pinctrl_init); From 50bb01954a457b573c0c574eb89249c776555568 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 18 May 2026 01:01:19 +0200 Subject: [PATCH 1668/5214] pinctrl: starfive: jh7110: Use __counted_by() flexarray Flexible arrays should use __counted_by() to be able to do runtime checks that the array does not go out of range. Cc: Rosen Penev Fixes: 87182ef0bf93 ("pinctrl: starfive: jh7110: use struct_size") Signed-off-by: Linus Walleij --- drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 1 + drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index 3572e8edd9f3..3fb9aa8ddf07 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -865,6 +865,7 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) #endif if (!sfp) return -ENOMEM; + sfp->num_saved_regs = info->nsaved_regs; sfp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfp->base)) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h index 188fc9d96269..12568be28527 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.h @@ -21,7 +21,8 @@ struct jh7110_pinctrl { /* register read/write mutex */ struct mutex mutex; const struct jh7110_pinctrl_soc_info *info; - u32 saved_regs[]; + unsigned int num_saved_regs; + u32 saved_regs[] __counted_by(num_saved_regs); }; struct jh7110_gpio_irq_reg { From e0a17856f2c3cddcdc354b924cc1a03e93f625ae Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 18 May 2026 01:01:20 +0200 Subject: [PATCH 1669/5214] pinctrl: starfive: jh7110: Avoid ifdeffery Use IS_ENABLED() inline assigning a variable instead of ifdeffery. Cc: Rosen Penev Suggested-by: Geert Uytterhoeven Link: https://lore.kernel.org/linux-gpio/CAMuHMdX7t7VHTzybjYo3s8SU3XLEH9GKsxmLBbh7p4D1CT3H_Q@mail.gmail.com/ Signed-off-by: Linus Walleij --- drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index 3fb9aa8ddf07..ec359cb873c4 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -845,6 +845,7 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) struct jh7110_pinctrl *sfp; struct pinctrl_desc *jh7110_pinctrl_desc; struct reset_control *rst; + unsigned int num_saved_regs; struct clk *clk; int ret; @@ -857,15 +858,12 @@ int jh7110_pinctrl_probe(struct platform_device *pdev) return -EINVAL; } -#if IS_ENABLED(CONFIG_PM_SLEEP) - sfp = devm_kzalloc(dev, struct_size(sfp, saved_regs, info->nsaved_regs), - GFP_KERNEL); -#else - sfp = devm_kzalloc(dev, sizeof(*sfp), GFP_KERNEL); -#endif + num_saved_regs = IS_ENABLED(CONFIG_PM_SLEEP) ? info->nsaved_regs : 0; + sfp = devm_kzalloc(dev, struct_size(sfp, saved_regs, num_saved_regs), + GFP_KERNEL); if (!sfp) return -ENOMEM; - sfp->num_saved_regs = info->nsaved_regs; + sfp->num_saved_regs = num_saved_regs; sfp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfp->base)) From 88f643ec105ea0d2872b2c34b23503ad6076518c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 May 2026 09:51:14 +0200 Subject: [PATCH 1670/5214] pinctrl: avoid duplicate function definitions The pinctrl_generic_to_map() and pinctrl_generic_pins_function_dt_node_to_map() functions are built whenever CONFIG_GENERIC_PINCTRL is enabled, including configurations without CONFIG_OF. When CONFIG_OF is disabled, the dummy definitions are also present in the header, which causes the build to fail: drivers/pinctrl/pinctrl-generic.c:20:5: error: conflicting types for 'pinctrl_generic_to_map'; have 'int(struct pinctrl_dev *, struct device_node *, struct device_node *, struct pinctrl_map **, unsigned int *, unsigned int *, const char **, unsigned int, const char **, unsigned int *, unsigned int)' 20 | int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, | ^~~~~~~~~~~~~~~~~~~~~~ In file included from drivers/pinctrl/pinctrl-generic.c:16: drivers/pinctrl/pinconf.h:193:1: note: previous definition of 'pinctrl_generic_to_map' with type 'int(struct pinctrl_dev *, struct device_node *, struct device_node *, struct pinctrl_map **, unsigned int *, unsigned int *, const char **, unsigned int, const char **, unsigned int *, void *)' 193 | pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, | ^~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/pinctrl-generic.c:130:5: error: redefinition of 'pinctrl_generic_pins_function_dt_node_to_map' 130 | int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/pinconf.h:184:1: note: previous definition of 'pinctrl_generic_pins_function_dt_node_to_map' with type 'int(struct pinctrl_dev *, struct device_node *, struct pinctrl_map **, unsigned int *)' 184 | pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Removing either set of definitions is sufficient to avoid the problem. Remove the copy from the header for simplicity. Fixes: aaaf31be0426 ("pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map()") Fixes: 43722575e5cd ("pinctrl: add generic functions + pins mapper") Signed-off-by: Arnd Bergmann Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 9711d16c38b6..c6c9dc07b08d 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -167,7 +167,7 @@ pinconf_generic_parse_dt_pinmux(struct device_node *np, struct device *dev, } #endif -#if defined(CONFIG_GENERIC_PINCTRL) && defined (CONFIG_OF) +#if defined(CONFIG_GENERIC_PINCTRL) int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **maps, From 3f30211b023fcc810db7e16bb100a246ed7be52b Mon Sep 17 00:00:00 2001 From: Khristine Andreea Barbulescu Date: Mon, 4 May 2026 15:11:42 +0200 Subject: [PATCH 1671/5214] pinctrl: s32cc: use dev_err_probe() and improve error messages Change dev_err&return statements into dev_err_probe throughout the driver on the probing path. Signed-off-by: Andrei Stefanescu Signed-off-by: Khristine Andreea Barbulescu Reviewed-by: Enric Balletbo i Serra Reviewed-by: Bartosz Golaszewski Tested-by: Enric Balletbo i Serra Signed-off-by: Linus Walleij --- drivers/pinctrl/nxp/pinctrl-s32cc.c | 64 ++++++++++++++--------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c index fe7cd641fddd..56be6e8d624e 100644 --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c @@ -2,7 +2,7 @@ /* * Core driver for the S32 CC (Common Chassis) pin controller * - * Copyright 2017-2022,2024 NXP + * Copyright 2017-2022,2024-2025 NXP * Copyright (C) 2022 SUSE LLC * Copyright 2015-2016 Freescale Semiconductor, Inc. */ @@ -236,10 +236,10 @@ static int s32_dt_group_node_to_map(struct pinctrl_dev *pctldev, } ret = pinconf_generic_parse_dt_config(np, pctldev, &cfgs, &n_cfgs); - if (ret) { - dev_err(dev, "%pOF: could not parse node property\n", np); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "%pOF: could not parse node property\n", + np); if (n_cfgs) reserve++; @@ -763,15 +763,15 @@ static int s32_pinctrl_parse_groups(struct device_node *np, grp->data.name = np->name; npins = of_property_count_elems_of_size(np, "pinmux", sizeof(u32)); - if (npins < 0) { - dev_err(dev, "Failed to read 'pinmux' property in node %s.\n", - grp->data.name); - return -EINVAL; - } - if (!npins) { - dev_err(dev, "The group %s has no pins.\n", grp->data.name); - return -EINVAL; - } + if (npins < 0) + return dev_err_probe(dev, -EINVAL, + "Failed to read 'pinmux' in node %s\n", + grp->data.name); + + if (!npins) + return dev_err_probe(dev, -EINVAL, + "The group %s has no pins\n", + grp->data.name); grp->data.npins = npins; @@ -812,10 +812,9 @@ static int s32_pinctrl_parse_functions(struct device_node *np, /* Initialise function */ func->name = np->name; func->ngroups = of_get_child_count(np); - if (func->ngroups == 0) { - dev_err(info->dev, "no groups defined in %pOF\n", np); - return -EINVAL; - } + if (func->ngroups == 0) + return dev_err_probe(info->dev, -EINVAL, + "No groups defined in %pOF\n", np); groups = devm_kcalloc(info->dev, func->ngroups, sizeof(*func->groups), GFP_KERNEL); @@ -886,10 +885,9 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev, } nfuncs = of_get_child_count(np); - if (nfuncs <= 0) { - dev_err(&pdev->dev, "no functions defined\n"); - return -EINVAL; - } + if (nfuncs <= 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "No functions defined\n"); info->nfunctions = nfuncs; info->functions = devm_kcalloc(&pdev->dev, nfuncs, @@ -919,18 +917,17 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev, int s32_pinctrl_probe(struct platform_device *pdev, const struct s32_pinctrl_soc_data *soc_data) { - struct s32_pinctrl *ipctl; - int ret; - struct pinctrl_desc *s32_pinctrl_desc; - struct s32_pinctrl_soc_info *info; #ifdef CONFIG_PM_SLEEP struct s32_pinctrl_context *saved_context; #endif + struct pinctrl_desc *s32_pinctrl_desc; + struct s32_pinctrl_soc_info *info; + struct s32_pinctrl *ipctl; + int ret; - if (!soc_data || !soc_data->pins || !soc_data->npins) { - dev_err(&pdev->dev, "wrong pinctrl info\n"); - return -EINVAL; - } + if (!soc_data || !soc_data->pins || !soc_data->npins) + return dev_err_probe(&pdev->dev, -EINVAL, + "Wrong pinctrl info\n"); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -965,10 +962,9 @@ int s32_pinctrl_probe(struct platform_device *pdev, s32_pinctrl_desc->owner = THIS_MODULE; ret = s32_pinctrl_probe_dt(pdev, ipctl); - if (ret) { - dev_err(&pdev->dev, "fail to probe dt properties\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Fail to probe dt properties\n"); ipctl->pctl = devm_pinctrl_register(&pdev->dev, s32_pinctrl_desc, ipctl); From 09c816e5c4d3a8d6d6e4b7537433e5e98505d934 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:40:07 +0800 Subject: [PATCH 1672/5214] pinctrl: spacemit: fix NULL check in spacemit_pin_set_config spacemit_pin_set_config() looks up the per-pin descriptor with spacemit_get_pin() then checks the wrong variable for failure: const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); ... if (!pin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); pin is an unsigned int pin id, where 0 (GPIO_0 / gmac0_rxdv on K3) is a valid pin, so rejecting it here drops the PAD config write for the first pin of every group. On K3 Pico-ITX the GMAC RGMII group lists pin 0 as its first entry, so its drive-strength / bias configuration was silently ignored. The intended guard is against spacemit_get_pin() returning NULL when the pin id isn't in the SoC's pin table. Check spin instead, which both restores PAD setup for pin 0 and prevents a NULL deref on spin->pin. Fixes: a83c29e1d145 ("pinctrl: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Han Gao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Signed-off-by: Linus Walleij --- drivers/pinctrl/spacemit/pinctrl-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index b0be62b1c816..95024e2bb5a5 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -795,7 +795,7 @@ static int spacemit_pin_set_config(struct spacemit_pinctrl *pctrl, void __iomem *reg; unsigned int mux; - if (!pin) + if (!spin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); From 8bcf12d53bc799758a6ae82705510a089fdc25a7 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 May 2026 10:25:16 +0100 Subject: [PATCH 1673/5214] clk: renesas: r9a08g045: Drop unused DEF_G3S_MUX macro Drop the unused DEF_G3S_MUX helper macro from the r9a08g045 CPG driver. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260520092516.69819-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g045-cpg.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 9610676058de..3e816c881a14 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -50,13 +50,6 @@ #define G3S_SEL_SDHI1 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 4, 2) #define G3S_SEL_SDHI2 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 8, 2) -#define DEF_G3S_MUX(_name, _id, _conf, _parent_names, _mux_flags, _clk_flags) \ - DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = (_conf), \ - .parent_names = (_parent_names), \ - .num_parents = ARRAY_SIZE((_parent_names)), \ - .mux_flags = CLK_MUX_HIWORD_MASK | (_mux_flags), \ - .flag = (_clk_flags)) - enum clk_ids { /* Core Clock Outputs exported to DT */ LAST_DT_CORE_CLK = R9A08G045_SWD, From 1f10c4509649e7c5f6d5d3acccf3ef6fbb5cdd46 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 May 2026 10:29:47 +0100 Subject: [PATCH 1674/5214] clk: renesas: rzg2l: Rename iterator in for_each_mod_clock() to avoid shadowing Rename the internal loop iterator variable in the for_each_mod_clock() macro from 'i' to '__i'. The current naming conflicts with local loop variables named 'i' inside code blocks that utilize the macro, triggering compiler warnings due to variable shadowing: drivers/clk/renesas/rzg2l-cpg.c:1494:36: warning: declaration of `i` shadows a previous local [-Wshadow] 1494 | for (unsigned int i = 0; i < clk->num_shared_mstop_clks; i++) Using a unique identifier for the macro-internal iterator resolves the shadowing warnings globally across all macro expansions. Fixes: 3fd4a8bb4b63 ("clk: renesas: rzg2l: Add macro to loop through module clocks") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260520092947.70596-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 0abe00e2960b..51c9e19e1575 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1402,10 +1402,10 @@ struct mod_clock { #define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) #define for_each_mod_clock(mod_clock, hw, priv) \ - for (unsigned int i = 0; (priv) && i < (priv)->num_mod_clks; i++) \ - if ((priv)->clks[(priv)->num_core_clks + i] == ERR_PTR(-ENOENT)) \ + for (unsigned int __i = 0; (priv) && __i < (priv)->num_mod_clks; __i++) \ + if ((priv)->clks[(priv)->num_core_clks + __i] == ERR_PTR(-ENOENT)) \ continue; \ - else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + i])) && \ + else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + __i])) && \ ((mod_clock) = to_mod_clock(hw))) /* Need to be called with a lock held to avoid concurrent access to mstop->usecnt. */ From ccf707ca74cbb1d7ad0f99877518d2e3fe58611a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 13 May 2026 12:53:11 +0100 Subject: [PATCH 1675/5214] pinctrl: renesas: rzt2h: Remove unused variable in rzt2h_pinctrl_register() Variable 'j' in rzt2h_pinctrl_register() is incremented during pin descriptor initialization but never used afterwards. Remove the unused variable and the associated dead code. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260513115312.1574367-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 4ba11a83b604..4b790fa72b49 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -1140,7 +1140,7 @@ static int rzt2h_pinctrl_register(struct rzt2h_pinctrl *pctrl) struct pinctrl_desc *desc = &pctrl->desc; struct device *dev = pctrl->dev; struct pinctrl_pin_desc *pins; - unsigned int i, j; + unsigned int i; int ret; desc->name = DRV_NAME; @@ -1157,11 +1157,9 @@ static int rzt2h_pinctrl_register(struct rzt2h_pinctrl *pctrl) pctrl->pins = pins; desc->pins = pins; - for (i = 0, j = 0; i < pctrl->data->n_port_pins; i++) { + for (i = 0; i < pctrl->data->n_port_pins; i++) { pins[i].number = i; pins[i].name = rzt2h_gpio_names[i]; - if (i && !(i % RZT2H_PINS_PER_PORT)) - j++; } ret = devm_pinctrl_register_and_init(dev, desc, pctrl, &pctrl->pctl); From f9fb67bc77d322568bf573e81335be0e9be2a7c8 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 13 May 2026 12:53:12 +0100 Subject: [PATCH 1676/5214] pinctrl: renesas: rzt2h: Skip PFC mode configuration if already set In rzt2h_pinctrl_set_pfc_mode(), read the PMC and PFC registers upfront and skip the pin function configuration if the pin is already in peripheral mode with the desired function. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260513115312.1574367-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 4b790fa72b49..eb70aee39f1a 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -191,6 +191,12 @@ static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl, guard(raw_spinlock_irqsave)(&pctrl->lock); + reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); + /* Check if pin is already configured to the desired function */ + if ((rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(pin)) && + field_get(PFC_PIN_MASK(pin), reg64) == func) + return; + /* Set pin to 'Non-use (Hi-Z input protection)' */ reg16 = rzt2h_pinctrl_readw(pctrl, port, PM(port)); reg16 &= ~PM_PIN_MASK(pin); @@ -200,7 +206,6 @@ static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl, rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true); /* Select Pin function mode with PFC register */ - reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); reg64 &= ~PFC_PIN_MASK(pin); rzt2h_pinctrl_writeq(pctrl, port, reg64 | ((u64)func << (pin * 8)), PFC(port)); From c1492da3939c89372929e062d731f328f7693f1e Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 15 May 2026 15:40:07 +0300 Subject: [PATCH 1677/5214] pinctrl: renesas: rzg2l: Use -ENOTSUPP instead of -EOPNOTSUPP The pinctrl and GPIO core code make exceptions for the -ENOTSUPP error code. One such example is gpio_set_config_with_argument_optional(), which returns success when gpio_set_config_with_argument() returns -ENOTSUPP, but reports failure for all other error codes. Returning -EOPNOTSUPP from the pinctrl driver on the unsupported pinctrl operation may lead to boot failures when pinctrl drivers implements struct gpio_chip::set_config, the system uses GPIO hogs, and the struct gpio_chip::set_config implementation returns -EOPNOTSUPP for the unsupported operations. Return -ENOTSUPP for the unsupported pinctrl operation. Fixes: 560c633d378a ("pinctrl: renesas: rzg2l: Drop oen_read and oen_write callbacks") Fixes: c4c4637eb57f ("pinctrl: renesas: Add RZ/G2L pin and gpio controller driver") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea Reviewed-by: Bartosz Golaszewski Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260515124008.2947838-2-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index a106e087c224..05a33655e6cc 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1281,7 +1281,7 @@ static int rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin) int bit; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1323,7 +1323,7 @@ static int rzg2l_write_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oe u8 val; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1754,7 +1754,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, break; default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } @@ -1837,7 +1837,7 @@ static int rzg2l_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, /* Check config matching between to pin */ if (i && prev_config != *config) - return -EOPNOTSUPP; + return -ENOTSUPP; prev_config = *config; } From 60a19a68779b1f24e3160c8e4317d0334a397687 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 15 May 2026 15:40:08 +0300 Subject: [PATCH 1678/5214] pinctrl: renesas: rzg2l: Populate struct gpio_chip::set_config Populate struct gpio_chip::set_config to allow various GPIO settings. Signed-off-by: Claudiu Beznea Reviewed-by: Bartosz Golaszewski Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260515124008.2947838-3-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 05a33655e6cc..ac42093fc579 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -3212,6 +3212,7 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl) chip->direction_output = rzg2l_gpio_direction_output; chip->get = rzg2l_gpio_get; chip->set = rzg2l_gpio_set; + chip->set_config = gpiochip_generic_config; chip->label = name; chip->parent = pctrl->dev; chip->owner = THIS_MODULE; From 0590420c2f90de497d342c9a41a618f46f4d09ab Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 6 May 2026 10:31:03 +0530 Subject: [PATCH 1679/5214] remoteproc: Move resource table data structure to its own header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The resource table data structure has traditionally been associated with the remoteproc framework, where the resource table is included as a section within the remote processor firmware binary. However, it is also possible to obtain the resource table through other means—such as from a reserved memory region populated by the boot firmware, statically maintained driver data, or via a secure SMC call—when it is not embedded in the firmware. There are multiple Qualcomm remote processors (e.g., Venus, Iris, GPU, etc.) in the upstream kernel that do not use the remoteproc framework to manage their lifecycle for various reasons. When Linux is running at EL2, similar to the Qualcomm PAS driver (qcom_q6v5_pas.c), client drivers for subsystems like video and GPU may also want to use the resource table SMC call to retrieve and map resources before they are used by the remote processor. In such cases, the resource table data structure is no longer tightly coupled with the remoteproc headers. Client drivers that do not use the remoteproc framework should still be able to parse the resource table obtained through alternative means. Therefore, there is a need to decouple the resource table definitions from the remoteproc headers. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260506050107.1985033-2-mukesh.ojha@oss.qualcomm.com --- include/linux/remoteproc.h | 269 +------------------------------- include/linux/rsc_table.h | 306 +++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+), 268 deletions(-) create mode 100644 include/linux/rsc_table.h diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b4795698d8c2..7c1546d48008 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -42,274 +42,7 @@ #include #include #include - -/** - * struct resource_table - firmware resource table header - * @ver: version number - * @num: number of resource entries - * @reserved: reserved (must be zero) - * @offset: array of offsets pointing at the various resource entries - * - * A resource table is essentially a list of system resources required - * by the remote processor. It may also include configuration entries. - * If needed, the remote processor firmware should contain this table - * as a dedicated ".resource_table" ELF section. - * - * Some resources entries are mere announcements, where the host is informed - * of specific remoteproc configuration. Other entries require the host to - * do something (e.g. allocate a system resource). Sometimes a negotiation - * is expected, where the firmware requests a resource, and once allocated, - * the host should provide back its details (e.g. address of an allocated - * memory region). - * - * The header of the resource table, as expressed by this structure, - * contains a version number (should we need to change this format in the - * future), the number of available resource entries, and their offsets - * in the table. - * - * Immediately following this header are the resource entries themselves, - * each of which begins with a resource entry header (as described below). - */ -struct resource_table { - u32 ver; - u32 num; - u32 reserved[2]; - u32 offset[]; -} __packed; - -/** - * struct fw_rsc_hdr - firmware resource entry header - * @type: resource type - * @data: resource data - * - * Every resource entry begins with a 'struct fw_rsc_hdr' header providing - * its @type. The content of the entry itself will immediately follow - * this header, and it should be parsed according to the resource type. - */ -struct fw_rsc_hdr { - u32 type; - u8 data[]; -} __packed; - -/** - * enum fw_resource_type - types of resource entries - * - * @RSC_CARVEOUT: request for allocation of a physically contiguous - * memory region. - * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. - * @RSC_TRACE: announces the availability of a trace buffer into which - * the remote processor will be writing logs. - * @RSC_VDEV: declare support for a virtio device, and serve as its - * virtio header. - * @RSC_LAST: just keep this one at the end of standard resources - * @RSC_VENDOR_START: start of the vendor specific resource types range - * @RSC_VENDOR_END: end of the vendor specific resource types range - * - * For more details regarding a specific resource type, please see its - * dedicated structure below. - * - * Please note that these values are used as indices to the rproc_handle_rsc - * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to - * check the validity of an index before the lookup table is accessed, so - * please update it as needed. - */ -enum fw_resource_type { - RSC_CARVEOUT = 0, - RSC_DEVMEM = 1, - RSC_TRACE = 2, - RSC_VDEV = 3, - RSC_LAST = 4, - RSC_VENDOR_START = 128, - RSC_VENDOR_END = 512, -}; - -#define FW_RSC_ADDR_ANY (-1) - -/** - * struct fw_rsc_carveout - physically contiguous memory request - * @da: device address - * @pa: physical address - * @len: length (in bytes) - * @flags: iommu protection flags - * @reserved: reserved (must be zero) - * @name: human-readable name of the requested memory region - * - * This resource entry requests the host to allocate a physically contiguous - * memory region. - * - * These request entries should precede other firmware resource entries, - * as other entries might request placing other data objects inside - * these memory regions (e.g. data/code segments, trace resource entries, ...). - * - * Allocating memory this way helps utilizing the reserved physical memory - * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries - * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB - * pressure is important; it may have a substantial impact on performance. - * - * If the firmware is compiled with static addresses, then @da should specify - * the expected device address of this memory region. If @da is set to - * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then - * overwrite @da with the dynamically allocated address. - * - * We will always use @da to negotiate the device addresses, even if it - * isn't using an iommu. In that case, though, it will obviously contain - * physical addresses. - * - * Some remote processors needs to know the allocated physical address - * even if they do use an iommu. This is needed, e.g., if they control - * hardware accelerators which access the physical memory directly (this - * is the case with OMAP4 for instance). In that case, the host will - * overwrite @pa with the dynamically allocated physical address. - * Generally we don't want to expose physical addresses if we don't have to - * (remote processors are generally _not_ trusted), so we might want to - * change this to happen _only_ when explicitly required by the hardware. - * - * @flags is used to provide IOMMU protection flags, and @name should - * (optionally) contain a human readable name of this carveout region - * (mainly for debugging purposes). - */ -struct fw_rsc_carveout { - u32 da; - u32 pa; - u32 len; - u32 flags; - u32 reserved; - u8 name[32]; -} __packed; - -/** - * struct fw_rsc_devmem - iommu mapping request - * @da: device address - * @pa: physical address - * @len: length (in bytes) - * @flags: iommu protection flags - * @reserved: reserved (must be zero) - * @name: human-readable name of the requested region to be mapped - * - * This resource entry requests the host to iommu map a physically contiguous - * memory region. This is needed in case the remote processor requires - * access to certain memory-based peripherals; _never_ use it to access - * regular memory. - * - * This is obviously only needed if the remote processor is accessing memory - * via an iommu. - * - * @da should specify the required device address, @pa should specify - * the physical address we want to map, @len should specify the size of - * the mapping and @flags is the IOMMU protection flags. As always, @name may - * (optionally) contain a human readable name of this mapping (mainly for - * debugging purposes). - * - * Note: at this point we just "trust" those devmem entries to contain valid - * physical addresses, but this isn't safe and will be changed: eventually we - * want remoteproc implementations to provide us ranges of physical addresses - * the firmware is allowed to request, and not allow firmwares to request - * access to physical addresses that are outside those ranges. - */ -struct fw_rsc_devmem { - u32 da; - u32 pa; - u32 len; - u32 flags; - u32 reserved; - u8 name[32]; -} __packed; - -/** - * struct fw_rsc_trace - trace buffer declaration - * @da: device address - * @len: length (in bytes) - * @reserved: reserved (must be zero) - * @name: human-readable name of the trace buffer - * - * This resource entry provides the host information about a trace buffer - * into which the remote processor will write log messages. - * - * @da specifies the device address of the buffer, @len specifies - * its size, and @name may contain a human readable name of the trace buffer. - * - * After booting the remote processor, the trace buffers are exposed to the - * user via debugfs entries (called trace0, trace1, etc..). - */ -struct fw_rsc_trace { - u32 da; - u32 len; - u32 reserved; - u8 name[32]; -} __packed; - -/** - * struct fw_rsc_vdev_vring - vring descriptor entry - * @da: device address - * @align: the alignment between the consumer and producer parts of the vring - * @num: num of buffers supported by this vring (must be power of two) - * @notifyid: a unique rproc-wide notify index for this vring. This notify - * index is used when kicking a remote processor, to let it know that this - * vring is triggered. - * @pa: physical address - * - * This descriptor is not a resource entry by itself; it is part of the - * vdev resource type (see below). - * - * Note that @da should either contain the device address where - * the remote processor is expecting the vring, or indicate that - * dynamically allocation of the vring's device address is supported. - */ -struct fw_rsc_vdev_vring { - u32 da; - u32 align; - u32 num; - u32 notifyid; - u32 pa; -} __packed; - -/** - * struct fw_rsc_vdev - virtio device header - * @id: virtio device id (as in virtio_ids.h) - * @notifyid: a unique rproc-wide notify index for this vdev. This notify - * index is used when kicking a remote processor, to let it know that the - * status/features of this vdev have changes. - * @dfeatures: specifies the virtio device features supported by the firmware - * @gfeatures: a place holder used by the host to write back the - * negotiated features that are supported by both sides. - * @config_len: the size of the virtio config space of this vdev. The config - * space lies in the resource table immediate after this vdev header. - * @status: a place holder where the host will indicate its virtio progress. - * @num_of_vrings: indicates how many vrings are described in this vdev header - * @reserved: reserved (must be zero) - * @vring: an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'. - * - * This resource is a virtio device header: it provides information about - * the vdev, and is then used by the host and its peer remote processors - * to negotiate and share certain virtio properties. - * - * By providing this resource entry, the firmware essentially asks remoteproc - * to statically allocate a vdev upon registration of the rproc (dynamic vdev - * allocation is not yet supported). - * - * Note: - * 1. unlike virtualization systems, the term 'host' here means - * the Linux side which is running remoteproc to control the remote - * processors. We use the name 'gfeatures' to comply with virtio's terms, - * though there isn't really any virtualized guest OS here: it's the host - * which is responsible for negotiating the final features. - * Yeah, it's a bit confusing. - * - * 2. immediately following this structure is the virtio config space for - * this vdev (which is specific to the vdev; for more info, read the virtio - * spec). The size of the config space is specified by @config_len. - */ -struct fw_rsc_vdev { - u32 id; - u32 notifyid; - u32 dfeatures; - u32 gfeatures; - u32 config_len; - u8 status; - u8 num_of_vrings; - u8 reserved[2]; - struct fw_rsc_vdev_vring vring[]; -} __packed; +#include struct rproc; diff --git a/include/linux/rsc_table.h b/include/linux/rsc_table.h new file mode 100644 index 000000000000..c32c8b6cd2a7 --- /dev/null +++ b/include/linux/rsc_table.h @@ -0,0 +1,306 @@ +/* + * Resource table and its types data structure + * + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RSC_TABLE_H +#define RSC_TABLE_H + +/** + * struct resource_table - firmware resource table header + * @ver: version number + * @num: number of resource entries + * @reserved: reserved (must be zero) + * @offset: array of offsets pointing at the various resource entries + * + * A resource table is essentially a list of system resources required + * by the remote processor. It may also include configuration entries. + * If needed, the remote processor firmware should contain this table + * as a dedicated ".resource_table" ELF section. + * + * Some resources entries are mere announcements, where the host is informed + * of specific remoteproc configuration. Other entries require the host to + * do something (e.g. allocate a system resource). Sometimes a negotiation + * is expected, where the firmware requests a resource, and once allocated, + * the host should provide back its details (e.g. address of an allocated + * memory region). + * + * The header of the resource table, as expressed by this structure, + * contains a version number (should we need to change this format in the + * future), the number of available resource entries, and their offsets + * in the table. + * + * Immediately following this header are the resource entries themselves, + * each of which begins with a resource entry header (as described below). + */ +struct resource_table { + u32 ver; + u32 num; + u32 reserved[2]; + u32 offset[]; +} __packed; + +/** + * struct fw_rsc_hdr - firmware resource entry header + * @type: resource type + * @data: resource data + * + * Every resource entry begins with a 'struct fw_rsc_hdr' header providing + * its @type. The content of the entry itself will immediately follow + * this header, and it should be parsed according to the resource type. + */ +struct fw_rsc_hdr { + u32 type; + u8 data[]; +} __packed; + +/** + * enum fw_resource_type - types of resource entries + * + * @RSC_CARVEOUT: request for allocation of a physically contiguous + * memory region. + * @RSC_DEVMEM: request to iommu_map a memory-based peripheral. + * @RSC_TRACE: announces the availability of a trace buffer into which + * the remote processor will be writing logs. + * @RSC_VDEV: declare support for a virtio device, and serve as its + * virtio header. + * @RSC_LAST: just keep this one at the end of standard resources + * @RSC_VENDOR_START: start of the vendor specific resource types range + * @RSC_VENDOR_END: end of the vendor specific resource types range + * + * For more details regarding a specific resource type, please see its + * dedicated structure below. + * + * Please note that these values are used as indices to the rproc_handle_rsc + * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to + * check the validity of an index before the lookup table is accessed, so + * please update it as needed. + */ +enum fw_resource_type { + RSC_CARVEOUT = 0, + RSC_DEVMEM = 1, + RSC_TRACE = 2, + RSC_VDEV = 3, + RSC_LAST = 4, + RSC_VENDOR_START = 128, + RSC_VENDOR_END = 512, +}; + +#define FW_RSC_ADDR_ANY (-1) + +/** + * struct fw_rsc_carveout - physically contiguous memory request + * @da: device address + * @pa: physical address + * @len: length (in bytes) + * @flags: iommu protection flags + * @reserved: reserved (must be zero) + * @name: human-readable name of the requested memory region + * + * This resource entry requests the host to allocate a physically contiguous + * memory region. + * + * These request entries should precede other firmware resource entries, + * as other entries might request placing other data objects inside + * these memory regions (e.g. data/code segments, trace resource entries, ...). + * + * Allocating memory this way helps utilizing the reserved physical memory + * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries + * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB + * pressure is important; it may have a substantial impact on performance. + * + * If the firmware is compiled with static addresses, then @da should specify + * the expected device address of this memory region. If @da is set to + * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then + * overwrite @da with the dynamically allocated address. + * + * We will always use @da to negotiate the device addresses, even if it + * isn't using an iommu. In that case, though, it will obviously contain + * physical addresses. + * + * Some remote processors needs to know the allocated physical address + * even if they do use an iommu. This is needed, e.g., if they control + * hardware accelerators which access the physical memory directly (this + * is the case with OMAP4 for instance). In that case, the host will + * overwrite @pa with the dynamically allocated physical address. + * Generally we don't want to expose physical addresses if we don't have to + * (remote processors are generally _not_ trusted), so we might want to + * change this to happen _only_ when explicitly required by the hardware. + * + * @flags is used to provide IOMMU protection flags, and @name should + * (optionally) contain a human readable name of this carveout region + * (mainly for debugging purposes). + */ +struct fw_rsc_carveout { + u32 da; + u32 pa; + u32 len; + u32 flags; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_devmem - iommu mapping request + * @da: device address + * @pa: physical address + * @len: length (in bytes) + * @flags: iommu protection flags + * @reserved: reserved (must be zero) + * @name: human-readable name of the requested region to be mapped + * + * This resource entry requests the host to iommu map a physically contiguous + * memory region. This is needed in case the remote processor requires + * access to certain memory-based peripherals; _never_ use it to access + * regular memory. + * + * This is obviously only needed if the remote processor is accessing memory + * via an iommu. + * + * @da should specify the required device address, @pa should specify + * the physical address we want to map, @len should specify the size of + * the mapping and @flags is the IOMMU protection flags. As always, @name may + * (optionally) contain a human readable name of this mapping (mainly for + * debugging purposes). + * + * Note: at this point we just "trust" those devmem entries to contain valid + * physical addresses, but this isn't safe and will be changed: eventually we + * want remoteproc implementations to provide us ranges of physical addresses + * the firmware is allowed to request, and not allow firmwares to request + * access to physical addresses that are outside those ranges. + */ +struct fw_rsc_devmem { + u32 da; + u32 pa; + u32 len; + u32 flags; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_trace - trace buffer declaration + * @da: device address + * @len: length (in bytes) + * @reserved: reserved (must be zero) + * @name: human-readable name of the trace buffer + * + * This resource entry provides the host information about a trace buffer + * into which the remote processor will write log messages. + * + * @da specifies the device address of the buffer, @len specifies + * its size, and @name may contain a human readable name of the trace buffer. + * + * After booting the remote processor, the trace buffers are exposed to the + * user via debugfs entries (called trace0, trace1, etc..). + */ +struct fw_rsc_trace { + u32 da; + u32 len; + u32 reserved; + u8 name[32]; +} __packed; + +/** + * struct fw_rsc_vdev_vring - vring descriptor entry + * @da: device address + * @align: the alignment between the consumer and producer parts of the vring + * @num: num of buffers supported by this vring (must be power of two) + * @notifyid: a unique rproc-wide notify index for this vring. This notify + * index is used when kicking a remote processor, to let it know that this + * vring is triggered. + * @pa: physical address + * + * This descriptor is not a resource entry by itself; it is part of the + * vdev resource type (see below). + * + * Note that @da should either contain the device address where + * the remote processor is expecting the vring, or indicate that + * dynamically allocation of the vring's device address is supported. + */ +struct fw_rsc_vdev_vring { + u32 da; + u32 align; + u32 num; + u32 notifyid; + u32 pa; +} __packed; + +/** + * struct fw_rsc_vdev - virtio device header + * @id: virtio device id (as in virtio_ids.h) + * @notifyid: a unique rproc-wide notify index for this vdev. This notify + * index is used when kicking a remote processor, to let it know that the + * status/features of this vdev have changes. + * @dfeatures: specifies the virtio device features supported by the firmware + * @gfeatures: a place holder used by the host to write back the + * negotiated features that are supported by both sides. + * @config_len: the size of the virtio config space of this vdev. The config + * space lies in the resource table immediate after this vdev header. + * @status: a place holder where the host will indicate its virtio progress. + * @num_of_vrings: indicates how many vrings are described in this vdev header + * @reserved: reserved (must be zero) + * @vring: an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'. + * + * This resource is a virtio device header: it provides information about + * the vdev, and is then used by the host and its peer remote processors + * to negotiate and share certain virtio properties. + * + * By providing this resource entry, the firmware essentially asks remoteproc + * to statically allocate a vdev upon registration of the rproc (dynamic vdev + * allocation is not yet supported). + * + * Note: + * 1. unlike virtualization systems, the term 'host' here means + * the Linux side which is running remoteproc to control the remote + * processors. We use the name 'gfeatures' to comply with virtio's terms, + * though there isn't really any virtualized guest OS here: it's the host + * which is responsible for negotiating the final features. + * Yeah, it's a bit confusing. + * + * 2. immediately following this structure is the virtio config space for + * this vdev (which is specific to the vdev; for more info, read the virtio + * spec). The size of the config space is specified by @config_len. + */ +struct fw_rsc_vdev { + u32 id; + u32 notifyid; + u32 dfeatures; + u32 gfeatures; + u32 config_len; + u8 status; + u8 num_of_vrings; + u8 reserved[2]; + struct fw_rsc_vdev_vring vring[]; +} __packed; + +#endif /* RSC_TABLE_H */ From 49abb5d6e1ac8169cdfc0c3aa4408e0d90ee5696 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 6 May 2026 10:31:04 +0530 Subject: [PATCH 1680/5214] remoteproc: use rsc_table_for_each_entry() in rproc_handle_resources() Replace the open-coded resource table iteration loop in rproc_handle_resources() with the rsc_table_for_each_entry() helper. The remoteproc-specific dispatch logic (vendor resource handling via rproc_handle_rsc(), RSC_LAST bounds check, handler table lookup) is moved into a local callback rproc_handle_rsc_entry(), keeping the iteration mechanics in one canonical place. The callback receives the payload offset within the table so that handlers which write back into the resource table (e.g. rproc_handle_carveout() recording a dynamically allocated address via rsc_offset) continue to work correctly. No functional change. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260506050107.1985033-3-mukesh.ojha@oss.qualcomm.com --- drivers/remoteproc/remoteproc_core.c | 87 +++++++++++++--------------- include/linux/rsc_table.h | 53 +++++++++++++++++ 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index b087ed21858a..f003be006b1b 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1011,60 +1011,55 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = { [RSC_VDEV] = rproc_handle_vdev, }; +struct rproc_rsc_cb_data { + struct rproc *rproc; + rproc_handle_resource_t *handlers; +}; + +static int rproc_handle_rsc_entry(u32 type, void *rsc, int offset, + int avail, void *data) +{ + struct rproc_rsc_cb_data *d = data; + struct rproc *rproc = d->rproc; + struct device *dev = &rproc->dev; + rproc_handle_resource_t handler; + int ret; + + dev_dbg(dev, "rsc: type %d\n", type); + + if (type >= RSC_VENDOR_START && type <= RSC_VENDOR_END) { + ret = rproc_handle_rsc(rproc, type, rsc, offset, avail); + if (ret == RSC_HANDLED) + return 0; + if (ret < 0) + return ret; + dev_warn(dev, "unsupported vendor resource %d\n", type); + return 0; + } + + if (type >= RSC_LAST) { + dev_warn(dev, "unsupported resource %d\n", type); + return 0; + } + + handler = d->handlers[type]; + if (!handler) + return 0; + + return handler(rproc, rsc, offset, avail); +} + /* handle firmware resource entries before booting the remote processor */ static int rproc_handle_resources(struct rproc *rproc, rproc_handle_resource_t handlers[RSC_LAST]) { - struct device *dev = &rproc->dev; - rproc_handle_resource_t handler; - int ret = 0, i; + struct rproc_rsc_cb_data d = { .rproc = rproc, .handlers = handlers }; if (!rproc->table_ptr) return 0; - for (i = 0; i < rproc->table_ptr->num; i++) { - int offset = rproc->table_ptr->offset[i]; - struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset; - int avail = rproc->table_sz - offset - sizeof(*hdr); - void *rsc = (void *)hdr + sizeof(*hdr); - - /* make sure table isn't truncated */ - if (avail < 0) { - dev_err(dev, "rsc table is truncated\n"); - return -EINVAL; - } - - dev_dbg(dev, "rsc: type %d\n", hdr->type); - - if (hdr->type >= RSC_VENDOR_START && - hdr->type <= RSC_VENDOR_END) { - ret = rproc_handle_rsc(rproc, hdr->type, rsc, - offset + sizeof(*hdr), avail); - if (ret == RSC_HANDLED) - continue; - else if (ret < 0) - break; - - dev_warn(dev, "unsupported vendor resource %d\n", - hdr->type); - continue; - } - - if (hdr->type >= RSC_LAST) { - dev_warn(dev, "unsupported resource %d\n", hdr->type); - continue; - } - - handler = handlers[hdr->type]; - if (!handler) - continue; - - ret = handler(rproc, rsc, offset + sizeof(*hdr), avail); - if (ret) - break; - } - - return ret; + return rsc_table_for_each_entry(rproc->table_ptr, rproc->table_sz, + &rproc->dev, rproc_handle_rsc_entry, &d); } static int rproc_prepare_subdevices(struct rproc *rproc) diff --git a/include/linux/rsc_table.h b/include/linux/rsc_table.h index c32c8b6cd2a7..c6d6d553d8f1 100644 --- a/include/linux/rsc_table.h +++ b/include/linux/rsc_table.h @@ -303,4 +303,57 @@ struct fw_rsc_vdev { struct fw_rsc_vdev_vring vring[]; } __packed; +/** + * rsc_table_for_each_entry() - iterate over all entries in a resource table + * @table: pointer to the resource table + * @table_sz: total size of the table buffer in bytes + * @dev: device used for error logging + * @cb: callback invoked for each entry: + * @type - value from enum fw_resource_type + * @rsc - pointer to the entry payload (past struct fw_rsc_hdr) + * @offset - byte offset of the payload within the table; callers + * that write back into the table (e.g. to record a + * dynamically allocated address) use this to locate the + * entry for later update + * @avail - bytes available in the payload + * @data - caller-supplied private pointer + * Return 0 to continue iteration, non-zero to stop. + * @data: private pointer forwarded to @cb on every call + * + * Iterates over every resource entry in @table, performing the standard + * truncation check, and invokes @cb for each one. Iteration stops on the + * first non-zero return from @cb or on a malformed table. + * + * Returns 0 after a complete iteration, -EINVAL if the table is truncated, + * or the first non-zero value returned by @cb. + */ +static inline int rsc_table_for_each_entry(struct resource_table *table, + size_t table_sz, + struct device *dev, + int (*cb)(u32 type, void *rsc, + int offset, int avail, + void *data), + void *data) { + int i, ret; + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + int avail = table_sz - offset - sizeof(*hdr); + int rsc_offset = offset + sizeof(*hdr); + void *rsc = (void *)hdr + sizeof(*hdr); + + if (avail < 0) { + dev_err(dev, "rsc table is truncated\n"); + return -EINVAL; + } + + ret = cb(hdr->type, rsc, rsc_offset, avail, data); + if (ret) + return ret; + } + + return 0; +} + #endif /* RSC_TABLE_H */ From 65bd18199ad5358f7c879dc69b4de3671b33e62e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 19 May 2026 08:27:15 -0700 Subject: [PATCH 1681/5214] perf stat: Propagate supported flag to follower cgroup BPF events When using BPF counters with cgroups, follower events (for cgroups other than the first one) are not opened. Because they are not opened, their `supported` flag was left as `false`. During metric calculation, `prepare_metric` checks if the event is supported. If it is not supported (like the follower events), it explicitly sets the value to `NAN`, which eventually causes the metric to be reported as `nan %`. Fix this by propagating the `supported` flag from the "leader" events (the ones opened for the first cgroup) to the "follower" events. Also add a validation check to `bperf_load_program` to ensure `nr_cgroups` is not zero and the number of events is a multiple of `nr_cgroups`, preventing a potential division-by-zero (SIGFPE) exception when `num_events` evaluates to 0 (e.g., with a trailing comma in cgroups list). Reported-by: Svilen Kanev Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_counter_cgroup.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 519fee3dc3d0..e1ce5aa3b957 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -104,6 +104,11 @@ static int bperf_load_program(struct evlist *evlist) set_max_rlimit(); + if (nr_cgroups == 0 || evlist->core.nr_entries % nr_cgroups != 0) { + pr_err("Invalid cgroup or event count\n"); + return -EINVAL; + } + test_max_events_program_load(); skel = bperf_cgroup_bpf__open(); @@ -186,6 +191,21 @@ static int bperf_load_program(struct evlist *evlist) i++; } + /* + * Propagate supported flag from leaders to followers. Follower events + * are not opened, so their supported flag remains false. + */ + { + struct evsel *leader; + int num_events = evlist->core.nr_entries / nr_cgroups; + + evlist__for_each_entry(evlist, evsel) { + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); + if (leader) + evsel->supported = leader->supported; + } + } + /* * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check * whether the kernel support it From bd2a5be1fe731bc7548205dd148db75f1d588da2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 19 May 2026 08:27:16 -0700 Subject: [PATCH 1682/5214] perf test: Add stat metrics --for-each-cgroup test Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting with `--for-each-cgroup`, both with and without `--bpf-counters`. The test: - Checks if system-wide monitoring is supported (skips if not). - Finds cgroups to test. - Runs `perf stat` with `insn_per_cycle` metric and verifies that the metric is reported for each cgroup. - Dynamically pairs and verifies instructions and cycles counts to avoid false failures on idle cgroups. - Tests both standard mode and BPF counters mode (if supported). Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Svilen Kanev Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/stat_metrics_cgrp.sh | 200 ++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh new file mode 100755 index 000000000000..d4226ee0ae98 --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -0,0 +1,200 @@ +#!/bin/bash +# perf stat metrics --for-each-cgroup test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +test_cgroups= + +log_verbose() { + echo "$1" +} + +is_numeric_and_non_zero() { + local val="$1" + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] + then + return 0 # True + fi + return 1 # False +} + +# skip if system-wide is not supported +check_system_wide() +{ + log_verbose "Checking system-wide..." + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 + then + log_verbose "Skipping: system-wide monitoring not supported" + exit 2 + fi +} + +# find two cgroups to measure +find_cgroups() +{ + log_verbose "Finding cgroups..." + # try usual systemd slices first + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] + then + test_cgroups="system.slice,user.slice" + log_verbose "Found cgroups: ${test_cgroups}" + return + fi + + # try root and self cgroups + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z "${find_cgroups_self_cgrp}" ] + then + # cgroup v2 doesn't specify perf_event + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + fi + + if [ -z "${find_cgroups_self_cgrp}" ] + then + test_cgroups="/" + else + test_cgroups="/,${find_cgroups_self_cgrp}" + fi + log_verbose "Found cgroups: ${test_cgroups}" +} + +# Check if metric is reported for each cgroup +# $1: extra options (e.g. --bpf-counters) +check_metric_reported() +{ + local opts="$1" + local output + + log_verbose "Running check_metric_reported with opts '${opts}'..." + # Run perf stat + if ! output=$(perf stat -a ${opts} \ + --metrics=insn_per_cycle \ + --for-each-cgroup "${test_cgroups}" \ + -x, sleep 0.1 2>&1) + then + echo "FAIL: perf stat failed with exit code $?" + echo "Output: ${output}" + exit 1 + fi + + log_verbose "perf stat output:" + log_verbose "${output}" + + # Split test_cgroups by comma + IFS=',' read -r -a cgrps <<< "${test_cgroups}" + + for cgrp in "${cgrps[@]}"; do + # Find metric lines for this cgroup + # We use exact cgroup match with surrounding commas + local cgrp_lines + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) + + if [ -z "${cgrp_lines}" ] + then + echo "FAIL: No metric lines found for cgroup '${cgrp}'" + exit 1 + fi + + # Parse each metric line + while read -r line; do + [ -z "${line}" ] && continue + + local val1 + val1=$(echo "${line}" | cut -d, -f1) + + local event_name + event_name=$(echo "${line}" | cut -d, -f3) + + local cycles_val="" + local inst_val="" + + if echo "${event_name}" | grep -q -i "cycles" + then + cycles_val="${val1}" + # Find corresponding instructions event + local inst_event_name + inst_event_name="${event_name/cpu-cycles/instructions}" + inst_event_name="${inst_event_name/cycles/instructions}" + + local inst_line + inst_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${inst_event_name}" || true) + inst_val=$(echo "${inst_line}" | cut -d, -f1) + elif echo "${event_name}" | grep -q -i "instructions" + then + inst_val="${val1}" + # Find corresponding cycles event (try cpu-cycles + # first, then cycles) + local cycles_event_name + cycles_event_name="${event_name/instructions/cpu-cycles}" + local cycles_line + cycles_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${cycles_event_name}" || true) + + if [ -z "${cycles_line}" ] + then + # Try "cycles" instead of "cpu-cycles" + cycles_event_name="${event_name/instructions/cycles}" + cycles_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${cycles_event_name}" || true) + fi + cycles_val=$(echo "${cycles_line}" | cut -d, -f1) + fi + + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ +val '${cycles_val}', inst val '${inst_val}'" + + # Only enforce metric check if both cycles and + # instructions have non-zero numeric counts + if is_numeric_and_non_zero "${cycles_val}" && \ + is_numeric_and_non_zero "${inst_val}" + then + log_verbose "Enforcing metric check for cgroup '${cgrp}' \ +event '${event_name}'" + # Check for nan or nested in the metric value (7th field) + # Actually we can just check the whole line for simplicity + if echo "${line}" | grep -q -i -E ",nan,|,nested," + then + echo "FAIL: Invalid metric value (nan/nested) \ +for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + # Check for empty metric value (2 consecutive + # commas before the unit) + if echo "${line}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" + then + echo "FAIL: Empty metric value for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + else + log_verbose "Skipping metric check for cgroup '${cgrp}' \ +event '${event_name}' (idle or not counted)" + fi + done <<< "${cgrp_lines}" + done + log_verbose "check_metric_reported passed for opts '${opts}'" +} + +check_system_wide +find_cgroups + +# Test 1: Without BPF counters +check_metric_reported "" + +# Test 2: With BPF counters (if supported) +log_verbose "Checking BPF support..." +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 +then + log_verbose "BPF supported, running Test 2..." + check_metric_reported "--bpf-counters" +else + log_verbose "BPF not supported, skipping Test 2" +fi + +exit 0 From 871cb269cd43b5d90f4e59d1458f8c09204759d4 Mon Sep 17 00:00:00 2001 From: Durai Manickam KR Date: Mon, 25 May 2026 14:54:02 +0530 Subject: [PATCH 1683/5214] clk: at91: sama7d65: add peripheral clock for I3C Add peripheral clock description for I3C. Signed-off-by: Durai Manickam KR Reviewed-by: Claudiu Beznea Signed-off-by: Manikandan Muralidharan Link: https://lore.kernel.org/r/20260525092405.1514213-3-manikandan.m@microchip.com Signed-off-by: Claudiu Beznea --- drivers/clk/at91/sama7d65.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/at91/sama7d65.c b/drivers/clk/at91/sama7d65.c index 7dee2b160ffb..ba8ff413fa2c 100644 --- a/drivers/clk/at91/sama7d65.c +++ b/drivers/clk/at91/sama7d65.c @@ -677,6 +677,7 @@ static struct { { .n = "uhphs_clk", .p = PCK_PARENT_HW_MCK5, .id = 101, }, { .n = "dsi_clk", .p = PCK_PARENT_HW_MCK3, .id = 103, }, { .n = "lvdsc_clk", .p = PCK_PARENT_HW_MCK3, .id = 104, }, + { .n = "i3cc_clk", .p = PCK_PARENT_HW_MCK8, .id = 105, }, }; /* From 1e7f56205813a2c48cdb3e9a4b0a24f49fd9a548 Mon Sep 17 00:00:00 2001 From: Adrian Ng Ho Yin Date: Fri, 22 May 2026 17:02:54 +0800 Subject: [PATCH 1684/5214] clk: socfpga: agilex: implement l3_main_free_clk The AGILEX_L3_MAIN_FREE_CLK is defined in the dt-bindings header but was never implemented in the clock driver. Per the Agilex TRM, l3_main_free_clk has no divider or mux and is a fixed 1:1 derivative of noc_free_clk that clocks most of the interconnect datapath. Signed-off-by: Adrian Ng Ho Yin Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/clk-agilex.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/socfpga/clk-agilex.c b/drivers/clk/socfpga/clk-agilex.c index 8dd94f64756b..2bdea1997b5e 100644 --- a/drivers/clk/socfpga/clk-agilex.c +++ b/drivers/clk/socfpga/clk-agilex.c @@ -259,6 +259,8 @@ static const struct stratix10_perip_cnt_clock agilex_main_perip_cnt_clks[] = { 0, 0x3C, 0, 0, 0}, { AGILEX_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux), 0, 0x40, 0, 0, 0}, + { AGILEX_L3_MAIN_FREE_CLK, "l3_main_free_clk", "noc_free_clk", NULL, + 1, 0, 0, 1, 0, 0}, { AGILEX_L4_SYS_FREE_CLK, "l4_sys_free_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0, 4, 0x30, 1}, { AGILEX_EMAC_A_FREE_CLK, "emaca_free_clk", NULL, emaca_free_mux, ARRAY_SIZE(emaca_free_mux), From 5eb6d68fe24dc856df173700958e90ac0f990333 Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Mon, 25 May 2026 15:29:28 +0530 Subject: [PATCH 1685/5214] RISC-V: KVM: Fix ebreak self test failure The ebreak self test enables/disables guest debugging as a part of the test. However the KVM_SET_GUEST_DEBUG ioctl doesn't actually do it. Fixing it by calling kvm_riscv_vcpu_config_guest_debug. Fixes: 6ed523e2b612 ("RISC-V: KVM: Factor-out VCPU config into separate sources") Signed-off-by: Mayuresh Chitale Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260525095930.3924905-1-mayuresh.chitale@oss.qualcomm.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index a73690eda84b..cf6e231e76e2 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -538,6 +538,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, else vcpu->guest_debug = 0; + kvm_riscv_vcpu_config_guest_debug(vcpu); return 0; } From 59ebc16276c51fa3c462e5a0d21280249755502d Mon Sep 17 00:00:00 2001 From: "Guo Ren (Alibaba DAMO Academy)" Date: Mon, 25 May 2026 15:19:44 +0530 Subject: [PATCH 1686/5214] irqchip/riscv-imsic: Add nr_guest_files in per-HART local config Add nr_guest_files in per-HART local config to represent the number of guest files available on a particular HART whereas the nr_guest_files in the global config represents the number of guest files available across all HARTs. This allows KVM RISC-V to use nr_guest_files from per-HART local config for asymmetric big.Little systems. Signed-off-by: Guo Ren (Alibaba DAMO Academy) Acked-by: Thomas Gleixner Signed-off-by: Anup Patel Link: https://lore.kernel.org/r/20260525094945.3721783-2-anup.patel@oss.qualcomm.com Signed-off-by: Anup Patel --- drivers/irqchip/irq-riscv-imsic-state.c | 9 ++++----- include/linux/irqchip/riscv-imsic.h | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c index e3ed874d89e7..b8d1bbbf42f7 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.c +++ b/drivers/irqchip/irq-riscv-imsic-state.c @@ -920,13 +920,12 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque) local->msi_va = mmios_va[index] + reloff; /* - * KVM uses global->nr_guest_files to determine the available guest - * interrupt files on each CPU. Take the minimum number of guest - * interrupt files across all CPUs to avoid KVM incorrectly allocating - * an unexisted or unmapped guest interrupt file on some CPUs. + * KVM uses both local->nr_guest_files and global->nr_guest_files + * to determine the available guest interrupt files on each CPU. */ nr_guest_files = (resource_size(&mmios[index]) - reloff) / IMSIC_MMIO_PAGE_SZ - 1; - global->nr_guest_files = min(global->nr_guest_files, nr_guest_files); + local->nr_guest_files = min((BIT(global->guest_index_bits) - 1), nr_guest_files); + global->nr_guest_files = min(global->nr_guest_files, local->nr_guest_files); nr_handlers++; } diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h index 4b348836de7a..61af3a5bea09 100644 --- a/include/linux/irqchip/riscv-imsic.h +++ b/include/linux/irqchip/riscv-imsic.h @@ -40,6 +40,9 @@ struct imsic_local_config { phys_addr_t msi_pa; void __iomem *msi_va; + + /* Number of guest interrupt files per-HART */ + u32 nr_guest_files; }; struct imsic_global_config { @@ -68,7 +71,7 @@ struct imsic_global_config { /* Number of guest interrupt identities */ u32 nr_guest_ids; - /* Number of guest interrupt files per core */ + /* Number of guest interrupt files across all HARTs */ u32 nr_guest_files; /* Per-CPU IMSIC addresses */ From 11a713db4394a0ec61f9b9bda056363e45ff1961 Mon Sep 17 00:00:00 2001 From: "Guo Ren (Alibaba DAMO Academy)" Date: Mon, 25 May 2026 15:19:45 +0530 Subject: [PATCH 1687/5214] RISC-V: KVM: AIA: Make HGEI number management fully per-CPU Previously, the number of Hypervisor Guest External Interrupt (HGEI) lines was stored in a single global variable `kvm_riscv_aia_nr_hgei` and assumed to be the same for all HARTs. This assumption does not hold on heterogeneous RISC-V SoCs where different cores may expose different HGEIE CSR widths. Introduce `nr_hgei` field into the per-CPU `struct aia_hgei_control` and probe the actual supported HGEI count for the current HART in `kvm_riscv_aia_enable()` using the standard RISC-V CSR probe technique: csr_write(CSR_HGEIE, -1UL); nr = fls_long(csr_read(CSR_HGEIE)); if (nr) nr--; All HGEI allocation, free and disable paths (`kvm_riscv_aia_free_hgei()`, `kvm_riscv_aia_disable()`, etc.) now use the per-CPU value instead of the global one. The global `kvm_riscv_aia_nr_hgei` now represents the minimum number of HGEI lines across HARTs and can be used to check whether HGEI support is available or not. This makes KVM AIA robust on big.LITTLE-style asymmetric platforms. Signed-off-by: Guo Ren (Alibaba DAMO Academy) Signed-off-by: Anup Patel Link: https://lore.kernel.org/r/20260525094945.3721783-3-anup.patel@oss.qualcomm.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_aia.h | 2 +- arch/riscv/kvm/aia.c | 88 +++++++++++++++++++------------- arch/riscv/kvm/aia_device.c | 4 +- arch/riscv/kvm/main.c | 8 +-- 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/arch/riscv/include/asm/kvm_aia.h b/arch/riscv/include/asm/kvm_aia.h index b04ecdd1a860..c67ec5ac0a14 100644 --- a/arch/riscv/include/asm/kvm_aia.h +++ b/arch/riscv/include/asm/kvm_aia.h @@ -79,7 +79,7 @@ struct kvm_vcpu_aia { #define irqchip_in_kernel(k) ((k)->arch.aia.in_kernel) -extern unsigned int kvm_riscv_aia_nr_hgei; +extern atomic_t kvm_riscv_aia_nr_hgei; extern unsigned int kvm_riscv_aia_max_ids; DECLARE_STATIC_KEY_FALSE(kvm_riscv_aia_available); #define kvm_riscv_aia_available() \ diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index 5ec503288555..bafb009c5ce5 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -21,13 +21,15 @@ struct aia_hgei_control { raw_spinlock_t lock; + bool free_bitmap_initialized; unsigned long free_bitmap; struct kvm_vcpu *owners[BITS_PER_LONG]; + unsigned int nr_hgei; }; static DEFINE_PER_CPU(struct aia_hgei_control, aia_hgei); static int hgei_parent_irq; -unsigned int kvm_riscv_aia_nr_hgei; +atomic_t kvm_riscv_aia_nr_hgei; unsigned int kvm_riscv_aia_max_ids; DEFINE_STATIC_KEY_FALSE(kvm_riscv_aia_available); @@ -452,7 +454,7 @@ void kvm_riscv_aia_free_hgei(int cpu, int hgei) raw_spin_lock_irqsave(&hgctrl->lock, flags); - if (hgei > 0 && hgei <= kvm_riscv_aia_nr_hgei) { + if (hgei > 0 && hgei <= hgctrl->nr_hgei) { if (!(hgctrl->free_bitmap & BIT(hgei))) { hgctrl->free_bitmap |= BIT(hgei); hgctrl->owners[hgei] = NULL; @@ -486,26 +488,18 @@ static irqreturn_t hgei_interrupt(int irq, void *dev_id) static int aia_hgei_init(void) { - int cpu, rc; - struct irq_domain *domain; struct aia_hgei_control *hgctrl; + struct irq_domain *domain; + int cpu, rc; /* Initialize per-CPU guest external interrupt line management */ for_each_possible_cpu(cpu) { hgctrl = per_cpu_ptr(&aia_hgei, cpu); raw_spin_lock_init(&hgctrl->lock); - if (kvm_riscv_aia_nr_hgei) { - hgctrl->free_bitmap = - BIT(kvm_riscv_aia_nr_hgei + 1) - 1; - hgctrl->free_bitmap &= ~BIT(0); - } else - hgctrl->free_bitmap = 0; + hgctrl->free_bitmap_initialized = false; + hgctrl->free_bitmap = 0; } - /* Skip SGEI interrupt setup for zero guest external interrupts */ - if (!kvm_riscv_aia_nr_hgei) - goto skip_sgei_interrupt; - /* Find INTC irq domain */ domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); @@ -529,25 +523,60 @@ static int aia_hgei_init(void) return rc; } -skip_sgei_interrupt: return 0; } static void aia_hgei_exit(void) { - /* Do nothing for zero guest external interrupts */ - if (!kvm_riscv_aia_nr_hgei) - return; - /* Free per-CPU SGEI interrupt */ free_percpu_irq(hgei_parent_irq, &aia_hgei); } void kvm_riscv_aia_enable(void) { + const struct imsic_global_config *gc; + const struct imsic_local_config *lc; + struct aia_hgei_control *hgctrl; + unsigned long flags; + int aia_nr_hgei; + if (!kvm_riscv_aia_available()) return; + gc = imsic_get_global_config(); + lc = (gc) ? this_cpu_ptr(gc->local) : NULL; + hgctrl = this_cpu_ptr(&aia_hgei); + + /* Figure-out number of bits in HGEIE */ + csr_write(CSR_HGEIE, -1UL); + hgctrl->nr_hgei = fls_long(csr_read(CSR_HGEIE)); + csr_write(CSR_HGEIE, 0); + if (hgctrl->nr_hgei) + hgctrl->nr_hgei--; + + /* + * Number of usable per-HART HGEI lines should be minimum of + * per-HART IMSIC guest files and number of bits in HGEIE. + */ + if (lc) + hgctrl->nr_hgei = min((ulong)hgctrl->nr_hgei, lc->nr_guest_files); + else + hgctrl->nr_hgei = 0; + + /* Update the number of IMSIC guest files across all HARTs */ + aia_nr_hgei = atomic_read(&kvm_riscv_aia_nr_hgei); + do { + if (aia_nr_hgei <= hgctrl->nr_hgei) + break; + } while (!atomic_try_cmpxchg(&kvm_riscv_aia_nr_hgei, &aia_nr_hgei, hgctrl->nr_hgei)); + + raw_spin_lock_irqsave(&hgctrl->lock, flags); + if (!hgctrl->free_bitmap_initialized) { + hgctrl->free_bitmap = (hgctrl->nr_hgei) ? GENMASK_ULL(hgctrl->nr_hgei, 1) : 0; + hgctrl->free_bitmap_initialized = true; + } + raw_spin_unlock_irqrestore(&hgctrl->lock, flags); + csr_write(CSR_HVICTL, aia_hvictl_value(false)); csr_write(CSR_HVIPRIO1, 0x0); csr_write(CSR_HVIPRIO2, 0x0); @@ -588,7 +617,7 @@ void kvm_riscv_aia_disable(void) raw_spin_lock_irqsave(&hgctrl->lock, flags); - for (i = 0; i <= kvm_riscv_aia_nr_hgei; i++) { + for (i = 0; i <= hgctrl->nr_hgei; i++) { vcpu = hgctrl->owners[i]; if (!vcpu) continue; @@ -628,26 +657,15 @@ int kvm_riscv_aia_init(void) return -ENODEV; gc = imsic_get_global_config(); - /* Figure-out number of bits in HGEIE */ - csr_write(CSR_HGEIE, -1UL); - kvm_riscv_aia_nr_hgei = fls_long(csr_read(CSR_HGEIE)); - csr_write(CSR_HGEIE, 0); - if (kvm_riscv_aia_nr_hgei) - kvm_riscv_aia_nr_hgei--; - - /* - * Number of usable HGEI lines should be minimum of per-HART - * IMSIC guest files and number of bits in HGEIE - */ + /* Set initial value of IMSIC guest files across all HARTs */ if (gc) - kvm_riscv_aia_nr_hgei = min((ulong)kvm_riscv_aia_nr_hgei, - gc->nr_guest_files); + atomic_set(&kvm_riscv_aia_nr_hgei, gc->nr_guest_files); else - kvm_riscv_aia_nr_hgei = 0; + atomic_set(&kvm_riscv_aia_nr_hgei, 0); /* Find number of guest MSI IDs */ kvm_riscv_aia_max_ids = IMSIC_MAX_ID; - if (gc && kvm_riscv_aia_nr_hgei) + if (gc) kvm_riscv_aia_max_ids = gc->nr_guest_ids + 1; /* Initialize guest external interrupt line management */ diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c index 3d1e81e2a36b..be83c2d5fc30 100644 --- a/arch/riscv/kvm/aia_device.c +++ b/arch/riscv/kvm/aia_device.c @@ -71,7 +71,7 @@ static int aia_config(struct kvm *kvm, unsigned long type, * external interrupts (i.e. non-zero * VS-level IMSIC pages). */ - if (!kvm_riscv_aia_nr_hgei) + if (!atomic_read(&kvm_riscv_aia_nr_hgei)) return -EINVAL; break; default: @@ -628,7 +628,7 @@ void kvm_riscv_aia_init_vm(struct kvm *kvm) */ /* Initialize default values in AIA global context */ - aia->mode = (kvm_riscv_aia_nr_hgei) ? + aia->mode = (atomic_read(&kvm_riscv_aia_nr_hgei)) ? KVM_DEV_RISCV_AIA_MODE_AUTO : KVM_DEV_RISCV_AIA_MODE_EMUL; aia->nr_ids = kvm_riscv_aia_max_ids - 1; aia->nr_sources = 0; diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c index cb8a65273c1f..0924c75100a2 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -168,10 +168,6 @@ static int __init riscv_kvm_init(void) kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits()); - if (kvm_riscv_aia_available()) - kvm_info("AIA available with %d guest external interrupts\n", - kvm_riscv_aia_nr_hgei); - kvm_riscv_setup_vendor_features(); kvm_register_perf_callbacks(); @@ -182,6 +178,10 @@ static int __init riscv_kvm_init(void) return rc; } + if (kvm_riscv_aia_available()) + kvm_info("AIA available with %d guest external interrupts\n", + atomic_read(&kvm_riscv_aia_nr_hgei)); + return 0; } module_init(riscv_kvm_init); From 45ad4de324cb1bba88e568b5bef633a79d926aed Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Mon, 18 May 2026 08:26:20 +0000 Subject: [PATCH 1688/5214] pinctrl: meson: amlogic-a4: fix gpio output glitch When the system transitions from bootloader to kernel, the GPIO is expected to keep driving high. However, the Linux kernel first configures the pin direction and then sets the output value. This may cause a brief low-level glitch on the GPIO line, which can be problematic for regulator control. By configuring the output value before switching the pin direction to output, the glitch can be avoided. This commit fixes the issue by swapping the configuration order. Fixes: 6e9be3abb78c ("pinctrl: Add driver support for Amlogic SoCs") Signed-off-by: Xianwei Zhao Reviewed-by: Neil Armstrong Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index e2293a872dcb..4c88ea1de909 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -548,11 +548,11 @@ static int aml_pinconf_set_output_drive(struct aml_pinctrl *info, { int ret; - ret = aml_pinconf_set_output(info, pin, true); + ret = aml_pinconf_set_drive(info, pin, high); if (ret) return ret; - return aml_pinconf_set_drive(info, pin, high); + return aml_pinconf_set_output(info, pin, true); } static int aml_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin, @@ -921,15 +921,14 @@ static int aml_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, unsigned int bit, reg; int ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); - ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); + ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), + value ? BIT(bit) : 0); if (ret < 0) return ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); - - return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), - value ? BIT(bit) : 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); + return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); } static int aml_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) From 2fe8d79e09de0f8f34d89d80462586b910e18a31 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 19 May 2026 10:37:22 +0100 Subject: [PATCH 1689/5214] pinctrl: generic: change signature of pinctrl_generic_to_map() to pass void data In order to make pinctrl_generic_to_map() usable for controllers that use pinmux, change the functions char array pointer that it passes to pinctrl_generic_add_group() to a void pointer. In the pinmux case this property will contain the mux setting as a number rather than as strings in the pins + functions case. Signed-off-by: Conor Dooley Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.h | 6 ++---- drivers/pinctrl/pinctrl-generic.c | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index c6c9dc07b08d..396f65ffe7a6 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -177,8 +177,7 @@ int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *pare struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps, unsigned int *num_reserved_maps, const char **group_name, unsigned int ngroups, - const char **functions, unsigned int *pins, - unsigned int npins); + void *data, unsigned int *pins, unsigned int npins); #else static inline int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, @@ -194,8 +193,7 @@ pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps, unsigned int *num_reserved_maps, const char **group_name, unsigned int ngroups, - const char **functions, unsigned int *pins, - unsigned int npins) + void *data, unsigned int *pins, unsigned int npins) { return -ENOTSUPP; } diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c index e4cd16ce2bda..a3faad7911cb 100644 --- a/drivers/pinctrl/pinctrl-generic.c +++ b/drivers/pinctrl/pinctrl-generic.c @@ -21,8 +21,7 @@ int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *pare struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps, unsigned int *num_reserved_maps, const char **group_names, unsigned int ngroups, - const char **functions, unsigned int *pins, - unsigned int npins) + void *data, unsigned int *pins, unsigned int npins) { struct device *dev = pctldev->dev; unsigned int num_configs; @@ -45,7 +44,7 @@ int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *pare if (ret < 0) return ret; - ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, functions); + ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, data); if (ret < 0) return dev_err_probe(dev, ret, "failed to add group %s: %d\n", group_name, ret); From 6a350ccc4dc3f46ed2ee2592e3050956e415ef64 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 19 May 2026 10:37:23 +0100 Subject: [PATCH 1690/5214] pinctrl: add new generic groups/function creation function for pinmux Akin to my recently added pinctrl_generic_pins_functions_dt_node_to_map(), create an analogue that performs the same role of dynamically creating groups at runtime for controllers using the pinmux property. The pinmux property is freeform, so this function mandates that the upper 16 bits contain the pin and the lower 16 bits contains the mux setting. The group's data pointer is populated with an array of the mux settings for each pin it contains. Since the node parsing and subsequent pinctrl core function calls are practically identical to the pins + functions case, other than which properties are examined, it makes sense to extract the common code from pinctrl_generic_pins_function_dt_node_to_map() into a generic function that takes the case-specific devicetree parsing function as an argument. Signed-off-by: Conor Dooley Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.h | 14 ++++ drivers/pinctrl/pinctrl-generic.c | 119 ++++++++++++++++++++++++------ 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 396f65ffe7a6..fc2d6cce9afb 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -173,6 +173,11 @@ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, struct pinctrl_map **maps, unsigned int *num_maps); +int pinctrl_generic_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps); + int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, struct device_node *np, struct pinctrl_map **maps, unsigned int *num_maps, unsigned int *num_reserved_maps, @@ -188,6 +193,15 @@ pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, return -ENOTSUPP; } +static inline int +pinctrl_generic_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + return -ENOTSUPP; +} + static inline int pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent, struct device_node *np, struct pinctrl_map **maps, diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c index a3faad7911cb..9759b0186bcc 100644 --- a/drivers/pinctrl/pinctrl-generic.c +++ b/drivers/pinctrl/pinctrl-generic.c @@ -119,17 +119,64 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p functions, pins, npins); } -/* - * For platforms that do not define groups or functions in the driver, but - * instead use the devicetree to describe them. This function will, unlike - * pinconf_generic_dt_node_to_map() etc which rely on driver defined groups - * and functions, create them in addition to parsing pinconf properties and - * adding mappings. - */ -int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, - struct device_node *np, - struct pinctrl_map **maps, - unsigned int *num_maps) +static int pinctrl_generic_pinmux_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *parent, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps, + unsigned int *num_reserved_maps, + const char **group_names, + unsigned int ngroups) +{ + struct device *dev = pctldev->dev; + unsigned int *pins, *muxes; + int npins, ret; + + npins = of_property_count_u32_elems(np, "pinmux"); + + if (npins < 1) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", + parent, np, npins); + return npins; + } + + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + muxes = devm_kcalloc(dev, npins, sizeof(*muxes), GFP_KERNEL); + if (!muxes) + return -ENOMEM; + + for (int i = 0; i < npins; i++) { + unsigned int pinmux; + + ret = of_property_read_u32_index(np, "pinmux", i, &pinmux); + if (ret) + return ret; + + pins[i] = pinmux >> 16; + muxes[i] = pinmux & GENMASK(15, 0); + } + + return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps, + num_reserved_maps, group_names, ngroups, + muxes, pins, npins); +} + +static int pinctrl_generic_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps, + int (dt_subnode_to_map)( + struct pinctrl_dev *, + struct device_node *, + struct device_node *, + struct pinctrl_map **, + unsigned int *, + unsigned int *, + const char **, + unsigned int)) { struct device *dev = pctldev->dev; struct device_node *child_np; @@ -152,11 +199,8 @@ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, if (!group_names) return -ENOMEM; - ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, np, - maps, num_maps, - &num_reserved_maps, - group_names, - ngroups); + ret = dt_subnode_to_map(pctldev, np, np, maps, num_maps, + &num_reserved_maps, group_names, ngroups); if (ret) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", np->name); @@ -180,11 +224,8 @@ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, ngroups = 0; for_each_available_child_of_node_scoped(np, child_np) { - ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, child_np, - maps, num_maps, - &num_reserved_maps, - group_names, - ngroups); + ret = dt_subnode_to_map(pctldev, np, child_np, maps, num_maps, + &num_reserved_maps, group_names, ngroups); if (ret) { pinctrl_utils_free_map(pctldev, *maps, *num_maps); return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", @@ -202,4 +243,40 @@ int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } + +/* + * For platforms that do not define groups or functions in the driver, but + * instead use the devicetree to describe them. This function will, unlike + * pinconf_generic_dt_node_to_map() etc which rely on driver defined groups + * and functions, create them in addition to parsing pinconf properties and + * adding mappings. + */ +int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + return pinctrl_generic_dt_node_to_map(pctldev, np, maps, num_maps, + &pinctrl_generic_pins_function_dt_subnode_to_map); +} EXPORT_SYMBOL_GPL(pinctrl_generic_pins_function_dt_node_to_map); + +/* + * For platforms that do not define groups or functions in the driver, but + * instead use the devicetree to describe them. This function will, unlike + * pinconf_generic_dt_node_to_map() etc which rely on driver defined groups + * and functions, create them in addition to parsing pinconf properties and + * adding mappings. + * + * It assumes that the upper 16 bits of the pinmux items contain the pin + * and the lower 16 the mux setting. + */ +int pinctrl_generic_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + return pinctrl_generic_dt_node_to_map(pctldev, np, maps, num_maps, + &pinctrl_generic_pinmux_dt_subnode_to_map); +}; +EXPORT_SYMBOL_GPL(pinctrl_generic_pinmux_dt_node_to_map); From 97a061a300da59b2073006803113d258a40a9086 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 19 May 2026 10:37:24 +0100 Subject: [PATCH 1691/5214] pinctrl: spacemit: delete spacemit_pctrl_check_power() As far as I can tell spacemit_pctrl_check_power(), called during the custom implementation of dt_node_to_map, is redundant because the driver's implementation generate_config performs the check too. Removing this would allow the driver to use the newly added common function pinctrl_generic_pinmux_dt_node_to_map(). Signed-off-by: Conor Dooley Signed-off-by: Linus Walleij --- drivers/pinctrl/spacemit/pinctrl-k1.c | 37 --------------------------- 1 file changed, 37 deletions(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index 95024e2bb5a5..ae8f9eb61a8c 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -409,38 +409,6 @@ static inline u32 spacemit_get_drive_strength_mA(enum spacemit_pin_io_type type, } } -static int spacemit_pctrl_check_power(struct pinctrl_dev *pctldev, - struct device_node *dn, - struct spacemit_pin_mux_config *pinmuxs, - int num_pins, const char *grpname) -{ - struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - struct device *dev = pctrl->dev; - enum spacemit_pin_io_type type; - u32 power = 0, i; - - of_property_read_u32(dn, "power-source", &power); - - for (i = 0; i < num_pins; i++) { - type = spacemit_to_pin_io_type(pinmuxs[i].pin); - - if (type != IO_TYPE_EXTERNAL) - continue; - - switch (power) { - case PIN_POWER_STATE_1V8: - case PIN_POWER_STATE_3V3: - break; - default: - dev_err(dev, "group %s has unsupported power\n", - grpname); - return -ENOTSUPP; - } - } - - return 0; -} - static void spacemit_set_io_pwr_domain(struct spacemit_pinctrl *pctrl, const struct spacemit_pin *spin, const enum spacemit_pin_io_type type) @@ -548,11 +516,6 @@ static int spacemit_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return dev_err_probe(dev, -ENODEV, "failed to get pin %d\n", pins[i]); } - ret = spacemit_pctrl_check_power(pctldev, child, pinmuxs, - npins, grpname); - if (ret < 0) - return ret; - map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; map[nmaps].data.mux.function = np->name; map[nmaps].data.mux.group = grpname; From 993d38647804592e1fd017a1584665be8f8f0308 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 19 May 2026 10:37:25 +0100 Subject: [PATCH 1692/5214] pinctrl: spacemit: move over to generic pinmux dt_node_to_map implementation Replace the custom implementation of dt_node_to_map with pinctrl_generic_dt_node_to_map() to demonstrate its use. spacemit_pin_mux_config didn't provide much value in the first place, because the group contains the information required to look up the spacemit_pin struct corresponding to a pin, so there's no loss in functionality as a result of the generic function carrying only the mux data in the group's data pointer rather than having an array of spacemit_pin_mux_config structs. Signed-off-by: Conor Dooley Reviewed-by: Troy Mitchell Signed-off-by: Linus Walleij --- drivers/pinctrl/spacemit/Kconfig | 4 +- drivers/pinctrl/spacemit/pinctrl-k1.c | 140 ++------------------------ 2 files changed, 12 insertions(+), 132 deletions(-) diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig index c021d51033d1..b2365deffe1b 100644 --- a/drivers/pinctrl/spacemit/Kconfig +++ b/drivers/pinctrl/spacemit/Kconfig @@ -8,9 +8,7 @@ config PINCTRL_SPACEMIT_K1 depends on ARCH_SPACEMIT || COMPILE_TEST depends on OF default ARCH_SPACEMIT - select GENERIC_PINCTRL_GROUPS - select GENERIC_PINMUX_FUNCTIONS - select GENERIC_PINCONF + select GENERIC_PINCTRL help Say Y to select the pinctrl driver for K1/K3 SoC. This pin controller allows selecting the mux function for diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index ae8f9eb61a8c..67de499349bb 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -114,11 +114,6 @@ struct spacemit_pinctrl_data { const struct spacemit_pinctrl_dconf *dconf; }; -struct spacemit_pin_mux_config { - const struct spacemit_pin *pin; - u32 config; -}; - /* map pin id to pinctrl register offset, refer MFPR definition */ static unsigned int spacemit_k1_pin_to_offset(unsigned int pin) { @@ -228,16 +223,6 @@ static inline void __iomem *spacemit_pin_to_reg(struct spacemit_pinctrl *pctrl, return pctrl->regs + pctrl->data->pin_to_offset(pin); } -static u16 spacemit_dt_get_pin(u32 value) -{ - return value >> 16; -} - -static u16 spacemit_dt_get_pin_mux(u32 value) -{ - return value & GENMASK(15, 0); -} - static const struct spacemit_pin *spacemit_get_pin(struct spacemit_pinctrl *pctrl, unsigned long pin) { @@ -445,121 +430,12 @@ static void spacemit_set_io_pwr_domain(struct spacemit_pinctrl *pctrl, writel_relaxed(val, pctrl->regs + IO_PWR_DOMAIN_OFFSET + offset); } -static int spacemit_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, - struct device_node *np, - struct pinctrl_map **maps, - unsigned int *num_maps) -{ - struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - struct device *dev = pctrl->dev; - struct device_node *child; - struct pinctrl_map *map; - const char **grpnames; - const char *grpname; - int ngroups = 0; - int nmaps = 0; - int ret; - - for_each_available_child_of_node(np, child) - ngroups += 1; - - grpnames = devm_kcalloc(dev, ngroups, sizeof(*grpnames), GFP_KERNEL); - if (!grpnames) - return -ENOMEM; - - map = kzalloc_objs(*map, ngroups * 2); - if (!map) - return -ENOMEM; - - ngroups = 0; - guard(mutex)(&pctrl->mutex); - for_each_available_child_of_node_scoped(np, child) { - struct spacemit_pin_mux_config *pinmuxs; - unsigned int config, *pins; - int i, npins; - - npins = of_property_count_u32_elems(child, "pinmux"); - - if (npins < 1) { - dev_err(dev, "invalid pinctrl group %pOFn.%pOFn\n", - np, child); - return -EINVAL; - } - - grpname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", - np, child); - if (!grpname) - return -ENOMEM; - - grpnames[ngroups++] = grpname; - - pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); - if (!pins) - return -ENOMEM; - - pinmuxs = devm_kcalloc(dev, npins, sizeof(*pinmuxs), GFP_KERNEL); - if (!pinmuxs) - return -ENOMEM; - - for (i = 0; i < npins; i++) { - ret = of_property_read_u32_index(child, "pinmux", - i, &config); - - if (ret) - return -EINVAL; - - pins[i] = spacemit_dt_get_pin(config); - pinmuxs[i].config = config; - pinmuxs[i].pin = spacemit_get_pin(pctrl, pins[i]); - - if (!pinmuxs[i].pin) - return dev_err_probe(dev, -ENODEV, "failed to get pin %d\n", pins[i]); - } - - map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP; - map[nmaps].data.mux.function = np->name; - map[nmaps].data.mux.group = grpname; - nmaps += 1; - - ret = pinctrl_generic_add_group(pctldev, grpname, - pins, npins, pinmuxs); - if (ret < 0) - return dev_err_probe(dev, ret, "failed to add group %s: %d\n", grpname, ret); - - ret = pinconf_generic_parse_dt_config(child, pctldev, - &map[nmaps].data.configs.configs, - &map[nmaps].data.configs.num_configs); - if (ret) - return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n", - grpname); - - if (map[nmaps].data.configs.num_configs == 0) - continue; - - map[nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; - map[nmaps].data.configs.group_or_pin = grpname; - nmaps += 1; - } - - ret = pinmux_generic_add_function(pctldev, np->name, - grpnames, ngroups, NULL); - if (ret < 0) { - pinctrl_utils_free_map(pctldev, map, nmaps); - return dev_err_probe(dev, ret, "error adding function %s\n", np->name); - } - - *maps = map; - *num_maps = nmaps; - - return 0; -} - static const struct pinctrl_ops spacemit_pctrl_ops = { .get_groups_count = pinctrl_generic_get_group_count, .get_group_name = pinctrl_generic_get_group_name, .get_group_pins = pinctrl_generic_get_group_pins, .pin_dbg_show = spacemit_pctrl_dbg_show, - .dt_node_to_map = spacemit_pctrl_dt_node_to_map, + .dt_node_to_map = pinctrl_generic_pinmux_dt_node_to_map, .dt_free_map = pinctrl_utils_free_map, }; @@ -568,8 +444,8 @@ static int spacemit_pmx_set_mux(struct pinctrl_dev *pctldev, { struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); const struct group_desc *group; - const struct spacemit_pin_mux_config *configs; unsigned int i, mux; + unsigned int *configs; void __iomem *reg; group = pinctrl_generic_get_group(pctldev, gsel); @@ -579,11 +455,17 @@ static int spacemit_pmx_set_mux(struct pinctrl_dev *pctldev, configs = group->data; for (i = 0; i < group->grp.npins; i++) { - const struct spacemit_pin *spin = configs[i].pin; - u32 value = configs[i].config; + const struct spacemit_pin *spin; + u32 value = configs[i]; + + spin = spacemit_get_pin(pctrl, group->grp.pins[i]); + if (!spin) { + dev_err(pctrl->dev, "Invalid pin %u\n", group->grp.pins[i]); + return -EINVAL; + } reg = spacemit_pin_to_reg(pctrl, spin->pin); - mux = spacemit_dt_get_pin_mux(value); + mux = value; guard(raw_spinlock_irqsave)(&pctrl->lock); value = readl_relaxed(reg) & ~PAD_MUX; From d73a08958e66849ea713d2f458b2fcf7b183f987 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 25 May 2026 05:28:30 -0400 Subject: [PATCH 1693/5214] thunderbolt: test: Add KUnit tests for property parser bounds checks Add regression tests for the zero-length entry and root directory bounds fixes: - tb_test_property_parse_zero_length: TEXT entry with length 0 must be rejected by the validator. - tb_test_property_parse_rootdir_overflow: root directory whose content_offset + content_len exceeds block_len must be rejected. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/test.c | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index fa8bee0ccf8b..463186a1abf9 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -2975,6 +2975,44 @@ static void tb_test_property_parse_dir_len_underflow(struct kunit *test) tb_property_free_dir(dir); } +static void tb_test_property_parse_zero_length(struct kunit *test) +{ + u32 *block = kunit_kzalloc(test, 6 * sizeof(u32), GFP_KERNEL); + struct tb_property_dir *dir; + + KUNIT_ASSERT_NOT_NULL(test, block); + + block[0] = 0x55584401; /* rootdir magic */ + block[1] = 0x00000004; /* root length: one entry */ + + block[2] = 0x61616161; /* key_hi */ + block[3] = 0x61616161; /* key_lo */ + block[4] = 0x74000000; /* type=TEXT, reserved=0, length=0 */ + block[5] = 0x00000000; /* value */ + + dir = tb_property_parse_dir(block, 6); + KUNIT_EXPECT_NULL(test, dir); + tb_property_free_dir(dir); +} + +static void tb_test_property_parse_rootdir_overflow(struct kunit *test) +{ + u32 *block = kunit_kzalloc(test, 4 * sizeof(u32), GFP_KERNEL); + struct tb_property_dir *dir; + + KUNIT_ASSERT_NOT_NULL(test, block); + + block[0] = 0x55584401; /* rootdir magic */ + block[1] = 0x00000004; /* root length claims 4 dwords of content */ + block[2] = 0x61616161; + block[3] = 0x61616161; + + /* content_offset(2) + content_len(4) = 6 > block_len(4) */ + dir = tb_property_parse_dir(block, 4); + KUNIT_EXPECT_NULL(test, dir); + tb_property_free_dir(dir); +} + static void tb_test_property_merge(struct kunit *test) { struct tb_property_dir *dir1, *dir2, *dir3; @@ -3099,6 +3137,8 @@ static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_property_parse), KUNIT_CASE(tb_test_property_format), KUNIT_CASE(tb_test_property_copy), + KUNIT_CASE(tb_test_property_parse_zero_length), + KUNIT_CASE(tb_test_property_parse_rootdir_overflow), KUNIT_CASE(tb_test_property_merge), { } }; From 2e7a8ad7980dce462e557c9ce7f66e8e168ed5b8 Mon Sep 17 00:00:00 2001 From: Jiafei Pan Date: Fri, 8 May 2026 11:20:16 +0800 Subject: [PATCH 1694/5214] remoteproc: imx_rproc: Use device node name as processor name As currently there are maybe multiple remote processors, so change from using fixed name to using device node name as remote processor name in order to make them can be distinguished by through of name in sys filesystem. Signed-off-by: Jiafei Pan Reviewed-by: Daniel Baluta Reviewed-by: Peng Fan Link: https://lore.kernel.org/r/20260508032016.27716-1-Jiafei.Pan@nxp.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/imx_rproc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 7f54322244ac..7662ebd9d2f4 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -1287,8 +1287,7 @@ static int imx_rproc_probe(struct platform_device *pdev) const struct imx_rproc_dcfg *dcfg; int ret; - /* set some other name then imx */ - rproc = devm_rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, + rproc = devm_rproc_alloc(dev, np->name, &imx_rproc_ops, NULL, sizeof(*priv)); if (!rproc) return -ENOMEM; From c5e8e224eb7ac003edcebfc7a00cefc38063bd18 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 13:23:13 +0200 Subject: [PATCH 1695/5214] MIPS: RB532: attach the software node to its target GPIO controller GPIOLIB wants to remove the software node's name matching against GPIO controller's label that is going on behind the scenes in software node lookup. To that end, we need to convert all existing users to using software nodes actually attached to the GPIO devices they represent. In order to use an attached software node with the GPIO controller on rb532: convert the GPIO module into a real platform device, provide platform device info for it in device.c and assign the software node using its swnode field. The software node will get inherited by the GPIO chip from the parent platform device in devm_gpiochip_add_data() as we don't set the fwnode using any other of the mechanisms. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Thomas Bogendoerfer --- arch/mips/rb532/devices.c | 32 ++++++++++++++++++++++++------ arch/mips/rb532/gpio.c | 41 ++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c index c3d8d96d0ef5..a6e3dc11298b 100644 --- a/arch/mips/rb532/devices.c +++ b/arch/mips/rb532/devices.c @@ -240,6 +240,25 @@ static struct platform_device *rb532_devs[] = { &rb532_wdt }; +#define GPIOBASE 0x050000 + +static struct resource rb532_gpio_reg0_res[] = { + { + .name = "gpio_reg0", + .start = REGBASE + GPIOBASE, + .end = REGBASE + GPIOBASE + sizeof(struct rb532_gpio_reg) - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device_info rb532_gpio_devinfo = { + .name = "rb532-gpio", + .id = PLATFORM_DEVID_NONE, + .res = rb532_gpio_reg0_res, + .num_res = ARRAY_SIZE(rb532_gpio_reg0_res), + .swnode = &rb532_gpio0_node, +}; + static const struct property_entry rb532_button_properties[] = { PROPERTY_ENTRY_GPIO("button-gpios", &rb532_gpio0_node, GPIO_BTN_S1, GPIO_ACTIVE_LOW), @@ -309,17 +328,18 @@ static int __init plat_setup_devices(void) /* set the uart clock to the current cpu frequency */ rb532_uart_res[0].uartclk = idt_cpu_freq; + pd = platform_device_register_full(&rb532_gpio_devinfo); + ret = PTR_ERR_OR_ZERO(pd); + if (ret) { + pr_err("failed to create the GPIO device: %d\n", ret); + return ret; + } + gpiod_add_lookup_table(&cf_slot0_gpio_table); ret = platform_add_devices(rb532_devs, ARRAY_SIZE(rb532_devs)); if (ret) return ret; - /* - * Stack devices using full info and properties here, after we - * register the node for the GPIO chip. - */ - software_node_register(&rb532_gpio0_node); - pd = platform_device_register_full(&nand0_info); ret = PTR_ERR_OR_ZERO(pd); if (ret) { diff --git a/arch/mips/rb532/gpio.c b/arch/mips/rb532/gpio.c index 9aa5ef374465..49e1a15a3827 100644 --- a/arch/mips/rb532/gpio.c +++ b/arch/mips/rb532/gpio.c @@ -37,7 +37,6 @@ #include #include -#define GPIOBASE 0x050000 /* Offsets relative to GPIOBASE */ #define GPIOFUNC 0x00 #define GPIOCFG 0x04 @@ -52,15 +51,6 @@ struct rb532_gpio_chip { void __iomem *regbase; }; -static struct resource rb532_gpio_reg0_res[] = { - { - .name = "gpio_reg0", - .start = REGBASE + GPIOBASE, - .end = REGBASE + GPIOBASE + sizeof(struct rb532_gpio_reg) - 1, - .flags = IORESOURCE_MEM, - } -}; - /* rb532_set_bit - sanely set a bit * * bitval: new value for the bit @@ -199,21 +189,32 @@ void rb532_gpio_set_func(unsigned gpio) } EXPORT_SYMBOL(rb532_gpio_set_func); -static int __init rb532_gpio_init(void) +static int rb532_gpio_probe(struct platform_device *pdev) { - struct resource *r; + struct device *dev = &pdev->dev; + struct resource *res; - r = rb532_gpio_reg0_res; - rb532_gpio_chip->regbase = ioremap(r->start, resource_size(r)); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; - if (!rb532_gpio_chip->regbase) { - printk(KERN_ERR "rb532: cannot remap GPIO register 0\n"); - return -ENXIO; - } + rb532_gpio_chip->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(rb532_gpio_chip->regbase)) + return PTR_ERR(rb532_gpio_chip->regbase); /* Register our GPIO chip */ - gpiochip_add_data(&rb532_gpio_chip->chip, rb532_gpio_chip); + return devm_gpiochip_add_data(dev, &rb532_gpio_chip->chip, rb532_gpio_chip); +} - return 0; +static struct platform_driver rb532_gpio_driver = { + .driver = { + .name = "rb532-gpio", + }, + .probe = rb532_gpio_probe, +}; + +static int __init rb532_gpio_init(void) +{ + return platform_driver_register(&rb532_gpio_driver); } arch_initcall(rb532_gpio_init); From c76cde70457d5f4f6809964b3d9d0a6deaed7fc8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 13:24:10 +0200 Subject: [PATCH 1696/5214] MIPS: RB532: serial: statify setup_serial_port() This function is not used outside of this compilation unit so make it static. Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/rb532/serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/rb532/serial.c b/arch/mips/rb532/serial.c index 70482540b3db..228eff07e5e8 100644 --- a/arch/mips/rb532/serial.c +++ b/arch/mips/rb532/serial.c @@ -45,7 +45,7 @@ static struct uart_port rb532_uart = { .regshift = 2 }; -int __init setup_serial_port(void) +static int __init setup_serial_port(void) { rb532_uart.uartclk = idt_cpu_freq; From 8e0780d30b1b51248e42895b7acc7a20f147b40b Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sat, 2 May 2026 00:14:20 +0100 Subject: [PATCH 1697/5214] MIPS: Fix big-endian stack argument fetching in o32 wrapper Fix an issue in call_o32() where the upper 32-bit half of incoming n64 stack arguments is fetched and used for outgoing o32 stack arguments on big-endian platforms. This code was adapted from arch/mips/dec/prom/call_o32.S which was meant for a little-endian platform only and therefore using 32-bit loads from 64-bit stack slot locations holding incoming stack arguments resulted in correct values being retrieved for data that is expected to be 32-bit. This works on little-endian platforms where the lower 32-bit half of the 64-bit value is located at every 64-bit stack slot location. However on big-endian platforms the lower 32-bit half is instead located at offset 4 from every 64-bit stack slot location. So to fix the issue the offset of 4 would have to be used on big-endian platforms only, or alternatively a 64-bit load from the 64-bit stack slot location can be used across the board, as the subsequent 32-bit store to the corresponding outgoing stack argument slot will correctly truncate the value and cause no unpredictable result. We already take advantage of this architectural feature for the incoming arguments held in $a6 and $a7 registers, since the o32 wrapper does not know how many incoming arguments there are and consequently propagates incoming data which may not be 32-bit. Since this code is generally supposed to be used with the stack located in cached memory there is no extra overhead expected for 64-bit loads as opposed to 32-bit ones, so pick this variant for code simplicity. Fixes: 231a35d37293 ("[MIPS] RM: Collected changes") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/fw/lib/call_o32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S index ee856709e0b6..77533cfbdfc1 100644 --- a/arch/mips/fw/lib/call_o32.S +++ b/arch/mips/fw/lib/call_o32.S @@ -74,7 +74,7 @@ NESTED(call_o32, O32_FRAMESZ, ra) PTR_LA t1,6*O32_SZREG(fp) li t2,O32_ARGC-6 1: - lw t3,(t0) + ld t3,(t0) REG_ADDU t0,SZREG sw t3,(t1) REG_SUBU t2,1 From a032121b273ec7bcad15812e624d28f0eece6b3f Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 4 May 2026 21:13:55 +0100 Subject: [PATCH 1698/5214] MIPS: Make do_IRQ() available for assembly callers As from commit 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") do_IRQ() is not a macro anymore and can be invoked directly from assembly code again, however its `asmlinkage' annotation has never been brought back from the previous removal of the function with commit 187933f23679 ("[MIPS] do_IRQ cleanup"). Since calling the function directly from assembly code has a performance advantage, add the annotation back so that the DEC platform can make use of this again. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/irq.h | 2 +- arch/mips/kernel/irq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h index 3a848e7e69f7..ee5f5245c04a 100644 --- a/arch/mips/include/asm/irq.h +++ b/arch/mips/include/asm/irq.h @@ -54,7 +54,7 @@ static inline int irq_canonicalize(int irq) asmlinkage void plat_irq_dispatch(void); -extern void do_IRQ(unsigned int irq); +asmlinkage void do_IRQ(unsigned int irq); struct irq_domain; extern void do_domain_IRQ(struct irq_domain *domain, unsigned int irq); diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 5e11582fe308..4ed73784a800 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -100,7 +100,7 @@ static inline void check_stack_overflow(void) {} * SMP cross-CPU interrupts have their own specific * handlers). */ -void __irq_entry do_IRQ(unsigned int irq) +asmlinkage void __irq_entry do_IRQ(unsigned int irq) { irq_enter(); check_stack_overflow(); From 35554eadc1b127bb5294504a4ac8f3a5dd9e299d Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 4 May 2026 21:14:00 +0100 Subject: [PATCH 1699/5214] MIPS: DEC: Remove do_IRQ() call indirection As from commit 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") do_IRQ() is not a macro anymore and can be invoked directly from assembly code, as a tail call. Remove the dec_irq_dispatch() stub then and the indirection previously introduced with commit 187933f23679 ("[MIPS] do_IRQ cleanup"), improving performance by reducing the number of control flow changes and the overall instruction count, while fixing a compiler's complaint about a missing prototype for said stub: arch/mips/dec/setup.c:780:25: warning: no previous prototype for 'dec_irq_dispatch' [-Wmissing-prototypes] 780 | asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) | ^~~~~~~~~~~~~~~~ (which gets promoted to a compilation error with CONFIG_WERROR). Fixes: 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/int-handler.S | 2 +- arch/mips/dec/setup.c | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 011d1d678840..a0b439c90488 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -277,7 +277,7 @@ srlv t3,t1,t2 handle_it: - j dec_irq_dispatch + j do_IRQ nop #if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c index 87f0a1436bf9..abe42616498d 100644 --- a/arch/mips/dec/setup.c +++ b/arch/mips/dec/setup.c @@ -776,9 +776,3 @@ void __init arch_init_irq(void) pr_err("Failed to register halt interrupt\n"); } } - -asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) -{ - do_IRQ(irq); - return 0; -} From 0eb77376912eba996cd2bc2a259e8e6a49d5998c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 4 May 2026 21:14:08 +0100 Subject: [PATCH 1700/5214] MIPS: DEC: Fix prototypes for halt/reset handlers Remove a bunch of compilation warnings for halt/reset handlers: arch/mips/dec/reset.c:22:17: warning: no previous prototype for 'dec_machine_restart' [-Wmissing-prototypes] 22 | void __noreturn dec_machine_restart(char *command) | ^~~~~~~~~~~~~~~~~~~ arch/mips/dec/reset.c:27:17: warning: no previous prototype for 'dec_machine_halt' [-Wmissing-prototypes] 27 | void __noreturn dec_machine_halt(void) | ^~~~~~~~~~~~~~~~ arch/mips/dec/reset.c:32:17: warning: no previous prototype for 'dec_machine_power_off' [-Wmissing-prototypes] 32 | void __noreturn dec_machine_power_off(void) | ^~~~~~~~~~~~~~~~~~~~~ arch/mips/dec/reset.c:38:13: warning: no previous prototype for 'dec_intr_halt' [-Wmissing-prototypes] 38 | irqreturn_t dec_intr_halt(int irq, void *dev_id) | ^~~~~~~~~~~~~ (which get promoted to compilation errors with CONFIG_WERROR), by moving the local prototypes from arch/mips/dec/setup.c to a dedicated header for arch/mips/dec/reset.c to use as well. No functional change. Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/reset.c | 2 ++ arch/mips/dec/setup.c | 6 +----- arch/mips/include/asm/dec/reset.h | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 arch/mips/include/asm/dec/reset.h diff --git a/arch/mips/dec/reset.c b/arch/mips/dec/reset.c index 3df01f1da347..ee1ad38f4a69 100644 --- a/arch/mips/dec/reset.c +++ b/arch/mips/dec/reset.c @@ -10,6 +10,8 @@ #include +#include + typedef void __noreturn (* noret_func_t)(void); static inline void __noreturn back_to_prom(void) diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c index abe42616498d..78ad28ac16db 100644 --- a/arch/mips/dec/setup.c +++ b/arch/mips/dec/setup.c @@ -48,14 +48,10 @@ #include #include #include +#include #include -extern void dec_machine_restart(char *command); -extern void dec_machine_halt(void); -extern void dec_machine_power_off(void); -extern irqreturn_t dec_intr_halt(int irq, void *dev_id); - unsigned long dec_kn_slot_base, dec_kn_slot_size; EXPORT_SYMBOL(dec_kn_slot_base); diff --git a/arch/mips/include/asm/dec/reset.h b/arch/mips/include/asm/dec/reset.h new file mode 100644 index 000000000000..6af49f3f24da --- /dev/null +++ b/arch/mips/include/asm/dec/reset.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/mips/include/asm/dec/reset.h + * + * DECstation/DECsystem halt/reset support. + * + * Copyright (C) 2026 Maciej W. Rozycki + */ +#ifndef __ASM_DEC_RESET_H +#define __ASM_DEC_RESET_H + +#include + +void __noreturn dec_machine_restart(char *command); +void __noreturn dec_machine_halt(void); +void __noreturn dec_machine_power_off(void); +irqreturn_t dec_intr_halt(int irq, void *dev_id); + +#endif /* __ASM_DEC_RESET_H */ From 04481cd30e8ec8b86b4dc17d0f89dd24845670a0 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 12:15:21 +0100 Subject: [PATCH 1701/5214] MIPS: DEC: Remove IRQF_ONESHOT reference for IOASIC DMA error IRQs There is no need for IOASIC DMA error interrupts to use the IRQF_ONESHOT flag, because while they do need to have the source cleared only at the conclusion of handling, the action handler supplied is either run in the hardirq context with interrupts disabled at the CPU level or, where IRQ threading has been forced, the primary handler has the IRQF_ONESHOT flag implicitly added and therefore the original action handler, now run as the thread handler and with interrupts enabled in the CPU, is executed with the originating interrupt line masked. Therefore no interrupt will retrigger regardless until the original request has been handled. Link: https://lore.kernel.org/r/20260127135334.qUEaYP9G@linutronix.de/ Reported-by: Sebastian Andrzej Siewior Signed-off-by: Maciej W. Rozycki Acked-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/ioasic-irq.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/mips/dec/ioasic-irq.c b/arch/mips/dec/ioasic-irq.c index 971f7b46759b..c690ef33397f 100644 --- a/arch/mips/dec/ioasic-irq.c +++ b/arch/mips/dec/ioasic-irq.c @@ -78,10 +78,7 @@ static struct irq_chip ioasic_dma_irq_type = { * cleared. This cannot be done until after a corrective action has been * taken and this also means they will not retrigger. Therefore they use * the `handle_fasteoi_irq' handler that only clears the request on the - * way out. Because MIPS processor interrupt inputs, one of which the I/O - * ASIC is cascaded to, are level-triggered it is recommended that error - * DMA interrupt action handlers are registered with the IRQF_ONESHOT flag - * set so that they are run with the interrupt line masked. + * way out. * * This mask has `1' bits in the positions of informational interrupts. */ From 5ff79e8bdc75db51e30298a75939e2308e7658e0 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:23 +0100 Subject: [PATCH 1702/5214] MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf() In 64-bit configurations calling any firmware entry points from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment. Consequently the stack pointer is no longer a 32-bit value and when the 32-bit firmware code called uses 32-bit ALU operations to manipulate the stack pointer, the calculated result is incorrect (in fact in the 64-bit MIPS ISA almost all 32-bit ALU operations will produce an unpredictable result when executed on 64-bit data) and control goes astray. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap, or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started, and the kernel will hang at: pid_max: default: 32768 minimum: 301 or somewhat later, but always before: cblist_init_generic: Setting adjustable number of callback queues. has been printed. It seems that only the prom_printf() entry point is affected. Of all the other entry points wired only rex_slot_address() and rex_gettcinfo() are called from a kernel thread other than the initial one, specifically kernel_init(), and they are leaf functions that do no business with the stack, having worked with no issue ever since 64-bit support was added for the platform back in 2002. To address this issue then, arrange for the stack to be switched in the o32 wrapper as required for prom_printf() only, by supplying call_o32() with a pointer to a chunk of initdata space, which is placed in the CKSEG0 32-bit compatibility segment, observing that prom_printf() is only called from console output handler and therefore with the console lock held, implying no need for this code to be reentrant. Other firmware entry points may be called with interrupts enabled and no lock held, and may therefore require that call_o32() be reentrant. They trigger no issue at this point and "if it ain't broke, don't fix it," so just leave them alone. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/prom/init.c | 6 +++++- arch/mips/include/asm/dec/prom.h | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c index 8d74d7d6c05b..e46c7accefff 100644 --- a/arch/mips/dec/prom/init.c +++ b/arch/mips/dec/prom/init.c @@ -3,7 +3,7 @@ * init.c: PROM library initialisation code. * * Copyright (C) 1998 Harald Koerfgen - * Copyright (C) 2002, 2004 Maciej W. Rozycki + * Copyright (C) 2002, 2004, 2026 Maciej W. Rozycki */ #include #include @@ -20,6 +20,10 @@ #include +#ifdef CONFIG_64BIT +unsigned long o32_stk[O32_STK_SIZE] __initdata = { 0 }; +#endif + int (*__rex_bootinit)(void); int (*__rex_bootread)(void); int (*__rex_getbitmap)(memmap *); diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h index 8fcad6984389..af068e634f1e 100644 --- a/arch/mips/include/asm/dec/prom.h +++ b/arch/mips/include/asm/dec/prom.h @@ -4,7 +4,7 @@ * * DECstation PROM interface. * - * Copyright (C) 2002 Maciej W. Rozycki + * Copyright (C) 2002, 2026 Maciej W. Rozycki * * Based on arch/mips/dec/prom/prom.h by the Anonymous. */ @@ -97,6 +97,17 @@ extern int (*__pmax_close)(int); #ifdef CONFIG_64BIT +#define O32_STK_SIZE 512 +extern unsigned long o32_stk[]; + +/* Switch the stack if outside the 32-bit address space. */ +static inline unsigned long *o32_get_stk(void) +{ + long fp = (long)__builtin_frame_address(0); + + return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL; +} + /* * On MIPS64 we have to call PROM functions via a helper * dispatcher to accommodate ABI incompatibilities. @@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...)); #define prom_getchar() _prom_getchar(__prom_getchar, NULL) #define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x) -#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x) +#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x) #else /* !CONFIG_64BIT */ From 7fb13fd35110ebe95eb053faf79d018f51144d85 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:27 +0100 Subject: [PATCH 1703/5214] MIPS: DEC: Prevent initial console buffer from landing in XKPHYS In 64-bit configurations calling the initial console output handler from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment and consequently so has been the buffer allocated there that is used as the argument corresponding to the `%s' output conversion specifier for the firmware's printf() entry point. This 64-bit address will then be truncated by 32-bit firmware, resulting in an attempt to access the wrong memory location, which in turn will cause all kinds of unpredictable behaviour, such as a kernel crash: Console: colour dummy device 160x64 Calibrating delay loop... 49.36 BogoMIPS (lpj=192512) pid_max: default: 32768 minimum: 301 CPU 0 Unable to handle kernel paging request at virtual address 000000000203bd00, epc == ffffffffbfc08364, ra == ffffffffbfc08800 Oops[#1]: CPU: 0 PID: 0 Comm: swapper Not tainted 5.18.0-rc2-00254-gfb649bda6f56-dirty #121 $ 0 : 0000000000000000 0000000000000001 0000000000000023 ffffffff80684ba0 $ 4 : 000000000203bd00 ffffffffbfc0f3b4 ffffffffffffffff 0000000000000073 $ 8 : 0a303d7469000000 0000000000000000 0000000000000073 ffffffffbfc0f473 $12 : 0000000000000002 0000000000000000 ffffffff80684c1c 0000000000000000 $16 : 0000000000000000 ffffffff80596dc9 0000000000000000 ffffffffbfc09240 $20 : ffffffff80684c40 ffffffffbfc0f400 000000000000002d 000000000000002b $24 : ffffffffffffffbf 000000000203bd00 $28 : ffffffff805f0000 ffffffff80684b58 0000000000000030 ffffffffbfc08800 Hi : 0000000000000000 Lo : 0000000000000aa8 epc : ffffffffbfc08364 0xffffffffbfc08364 ra : ffffffffbfc08800 0xffffffffbfc08800 Status: 140120e2 KX SX UX KERNEL EXL Cause : 00000008 (ExcCode 02) BadVA : 000000000203bd00 PrId : 00000430 (R4000SC) Modules linked in: Process swapper (pid: 0, threadinfo=(____ptrval____), task=(____ptrval____), tls=0000000000000000) Stack : 0000000000000000 0000000000000000 0000000000000000 0000004d0000004d 80684cc0806a2a40 80596dc80000004d 8061000000000000 bfc0850c80684c38 0000000000000000 000000000203bd00 0000000000000000 0000000000000000 0000000000000000 00000000bfc0f3b4 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000002500000000 0000000000000000 0000000000000000 802c1a7400000000 0203bd0080596dc8 0203bd4d69000000 6c61632000000018 5f746567646e6172 6c616320625f6d6f 5f736e5f6d6f7266 206361323778302b 303d74696e726320 806a0a38806b0000 806a0a38806b0000 00000000806b0000 80683c58806b0000 ... Call Trace: Code: a082ffff 03e00008 00601021 <80820000> 00001821 10400005 24840001 80820000 24630001 ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Fatal exception in interrupt KN04 V2.1k (PC: 0xa0026768, SP: 0x806848e8) >> In this case the pointer in $4 was truncated from 0x980000000203bd00 to 0x000000000203bd00. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started. Fix the issue by making the buffer static and initdata, and therefore placed in the CKSEG0 32-bit compatibility segment, observing that the console output handler is called with the console lock held, implying no need for this code to be reentrant. Add an assertion to verify the buffer actually has been placed in a compatibility segment. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer --- arch/mips/dec/prom/console.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c index 31a8441d8431..b4f0dba3fa20 100644 --- a/arch/mips/dec/prom/console.c +++ b/arch/mips/dec/prom/console.c @@ -2,8 +2,9 @@ /* * DECstation PROM-based early console support. * - * Copyright (C) 2004, 2007 Maciej W. Rozycki + * Copyright (C) 2004, 2007, 2026 Maciej W. Rozycki */ +#include #include #include #include @@ -14,9 +15,11 @@ static void __init prom_console_write(struct console *con, const char *s, unsigned int c) { - char buf[81]; + static char buf[81] __initdata = { 0 }; unsigned int chunk = sizeof(buf) - 1; + BUG_ON((long)buf != (int)(long)buf); + while (c > 0) { if (chunk > c) chunk = c; From e5d64f868e484da06f5c141c18c32c01c269625e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 7 May 2026 16:23:23 -0700 Subject: [PATCH 1704/5214] mips: cps: Assemble jr.hb with an R2 ISA level A MIPS allmodconfig built with LLVM can select CPU_MIPS32_R1 together with MIPS_MT_SMP. In that configuration clang invokes the integrated assembler with -march=mips32, and the MIPS MT path in cps-vec.S fails to assemble two jr.hb instructions: arch/mips/kernel/cps-vec.S:376:2: error: instruction requires a CPU feature not currently enabled arch/mips/kernel/cps-vec.S:490:4: error: instruction requires a CPU feature not currently enabled The earlier jr.hb in the same file is already assembled inside a .set MIPS_ISA_LEVEL_RAW scope. The two failing sites are reached after popping back to the file's base ISA level, so LLVM correctly rejects them for an R1 target. Wrap those jr.hb instructions in the same ISA-level push/pop used by the working site. This keeps the MT code unchanged while making the required R2 hazard-branch encoding explicit to the assembler. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Reviewed-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer --- arch/mips/kernel/cps-vec.S | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S index 2ae7034a3d5c..70413c816eb0 100644 --- a/arch/mips/kernel/cps-vec.S +++ b/arch/mips/kernel/cps-vec.S @@ -373,8 +373,11 @@ LEAF(mips_cps_boot_vpes) .set pop PTR_LA t1, 1f + .set push + .set MIPS_ISA_LEVEL_RAW jr.hb t1 nop + .set pop 1: mfc0 t1, CP0_MVPCONTROL ori t1, t1, MVPCONTROL_VPC mtc0 t1, CP0_MVPCONTROL @@ -487,8 +490,11 @@ LEAF(mips_cps_boot_vpes) li t0, TCHALT_H mtc0 t0, CP0_TCHALT PTR_LA t0, 1f + .set push + .set MIPS_ISA_LEVEL_RAW 1: jr.hb t0 nop + .set pop 2: From 4ddaf88aadd3bd09c2eb3734c53d2864af6b144e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 7 May 2026 21:27:24 -0700 Subject: [PATCH 1705/5214] mips: ralink: mt7621: add missing __iomem raw_readl and writel calls expect pointers annotated with __iomem. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild/202211060456.cnV6IK6G-lkp@intel.com/ Fixes: cc19db8b312a ("MIPS: ralink: mt7621: do memory detection on KSEG1") Signed-off-by: Rosen Penev Reviewed-by: Sergio Paracuellos Signed-off-by: Thomas Bogendoerfer --- arch/mips/ralink/mt7621.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/ralink/mt7621.c b/arch/mips/ralink/mt7621.c index a4bdda8541c0..ae7b8cfedd5f 100644 --- a/arch/mips/ralink/mt7621.c +++ b/arch/mips/ralink/mt7621.c @@ -63,7 +63,7 @@ phys_addr_t mips_cpc_default_phys_base(void) static bool __init mt7621_addr_wraparound_test(phys_addr_t size) { - void *dm = (void *)KSEG1ADDR(&detect_magic); + void __iomem *dm = (void __iomem *)KSEG1ADDR(&detect_magic); if (CPHYSADDR(dm + size) >= MT7621_LOWMEM_MAX_SIZE) return true; From 579f5329d5df2dbcf4bb5ef398701c2501d24892 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 20:57:27 -0700 Subject: [PATCH 1706/5214] mips: n64: add __iomem for writel call sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *mem @@ got unsigned int [usertype] * Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202105261445.AcvPd2EE-lkp@intel.com/ Fixes: baec970aa5ba ("mips: Add N64 machine type") Signed-off-by: Rosen Penev Signed-off-by: Thomas Bogendoerfer --- arch/mips/n64/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/n64/init.c b/arch/mips/n64/init.c index dfbd864f4667..66ec28ab41f3 100644 --- a/arch/mips/n64/init.c +++ b/arch/mips/n64/init.c @@ -50,7 +50,7 @@ void __init prom_init(void) #define W 320 #define H 240 -#define REG_BASE ((u32 *) CKSEG1ADDR(0x4400000)) +#define REG_BASE ((u32 __iomem *) CKSEG1ADDR(0x4400000)) static void __init n64rdp_write_reg(const u8 reg, const u32 value) { From 56388011db71026e04ffe9963c816269f0bb3165 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 15 May 2026 21:42:23 +0300 Subject: [PATCH 1707/5214] include: Remove unused jz4740-adc.h The last user was the JZ4740 MFD ADC driver, removed in commit ff71266aa490 ("mfd: Drop obsolete JZ4740 driver") and replaced by a self-contained IIO driver. No file includes or references this header. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Costa Shulyupin Signed-off-by: Thomas Bogendoerfer --- include/linux/jz4740-adc.h | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 include/linux/jz4740-adc.h diff --git a/include/linux/jz4740-adc.h b/include/linux/jz4740-adc.h deleted file mode 100644 index 19d995c8bf06..000000000000 --- a/include/linux/jz4740-adc.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __LINUX_JZ4740_ADC -#define __LINUX_JZ4740_ADC - -struct device; - -/* - * jz4740_adc_set_config - Configure a JZ4740 adc device - * @dev: Pointer to a jz4740-adc device - * @mask: Mask for the config value to be set - * @val: Value to be set - * - * This function can be used by the JZ4740 ADC mfd cells to configure their - * options in the shared config register. -*/ -int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val); - -#define JZ_ADC_CONFIG_SPZZ BIT(31) -#define JZ_ADC_CONFIG_EX_IN BIT(30) -#define JZ_ADC_CONFIG_DNUM_MASK (0x7 << 16) -#define JZ_ADC_CONFIG_DMA_ENABLE BIT(15) -#define JZ_ADC_CONFIG_XYZ_MASK (0x2 << 13) -#define JZ_ADC_CONFIG_SAMPLE_NUM_MASK (0x7 << 10) -#define JZ_ADC_CONFIG_CLKDIV_MASK (0xf << 5) -#define JZ_ADC_CONFIG_BAT_MB BIT(4) - -#define JZ_ADC_CONFIG_DNUM(dnum) ((dnum) << 16) -#define JZ_ADC_CONFIG_XYZ_OFFSET(dnum) ((xyz) << 13) -#define JZ_ADC_CONFIG_SAMPLE_NUM(x) ((x) << 10) -#define JZ_ADC_CONFIG_CLKDIV(div) ((div) << 5) - -#endif From eb013e9685e2ec6e608bbeb79e7ce98782fdcc63 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 15 May 2026 21:50:41 +0300 Subject: [PATCH 1708/5214] include: Remove unused jz4740-battery.h The last user was removed in commit aea12071d6fc ("power/supply: Drop obsolete JZ4740 driver") and replaced by a self-contained IIO-based driver. No file includes this header. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Costa Shulyupin Signed-off-by: Thomas Bogendoerfer --- include/linux/power/jz4740-battery.h | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 include/linux/power/jz4740-battery.h diff --git a/include/linux/power/jz4740-battery.h b/include/linux/power/jz4740-battery.h deleted file mode 100644 index 10da211678c8..000000000000 --- a/include/linux/power/jz4740-battery.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2009, Jiejing Zhang - */ - -#ifndef __JZ4740_BATTERY_H -#define __JZ4740_BATTERY_H - -struct jz_battery_platform_data { - struct power_supply_info info; - int gpio_charge; /* GPIO port of Charger state */ - int gpio_charge_active_low; -}; - -#endif From 1dc85756b759c3ecd21a22b0aef84ed41ddc7596 Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 28 Apr 2026 16:25:40 +0800 Subject: [PATCH 1709/5214] MIPS: ralink: reduce ARCH_DMA_MINALIGN Currently, Ralink SoCs use the default ARCH_DMA_MINALIGN value of 128 bytes defined in mach-generic. This is excessive for these platforms and leads to significant memory waste in kmalloc. Override ARCH_DMA_MINALIGN to use L1_CACHE_BYTES, which is 16 bytes for RT288X and 32 bytes for other Ralink SoCs. Signed-off-by: Qingfang Deng Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/mach-ralink/kmalloc.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 arch/mips/include/asm/mach-ralink/kmalloc.h diff --git a/arch/mips/include/asm/mach-ralink/kmalloc.h b/arch/mips/include/asm/mach-ralink/kmalloc.h new file mode 100644 index 000000000000..1693209d3f37 --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/kmalloc.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MACH_RALINK_KMALLOC_H +#define __ASM_MACH_RALINK_KMALLOC_H + +#ifdef CONFIG_DMA_NONCOHERENT +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES +#endif + +#endif /* __ASM_MACH_RALINK_KMALLOC_H */ From 1cb79c435a2ff812f7b9e9318962fda4b4663347 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:32 -0400 Subject: [PATCH 1710/5214] MIPS: mobileye: Remove duplicate FIT_IMAGE_FDT_EPM5 from main Kconfig kconfiglint reports: K008: config FIT_IMAGE_FDT_EPM5 has prompts in 2 separate definitions The FIT_IMAGE_FDT_EPM5 Kconfig symbol is defined identically in two places: arch/mips/Kconfig:1052 arch/mips/mobileye/Kconfig:17 Both have the same prompt, depends, default, and help text. Since arch/mips/mobileye/Kconfig is sourced from arch/mips/Kconfig, both definitions are parsed and the symbol ends up with two prompts. The symbol was first introduced in commit 101bd58fde10 ("MIPS: Add support for Mobileye EyeQ5") directly in arch/mips/Kconfig. Three months later, commit fbe0fae601b7 ("MIPS: mobileye: Add EyeQ6H support") created the arch/mips/mobileye/Kconfig sub-file to organize the growing Mobileye platform code and added the MACH_EYEQ5/MACH_EYEQ6H choice along with a copy of FIT_IMAGE_FDT_EPM5. However, the original definition in arch/mips/Kconfig was not removed at that time, leaving a duplicate. Remove the definition from arch/mips/Kconfig, keeping the one in arch/mips/mobileye/Kconfig where it belongs alongside the related MACH_EYEQ5 machine type definition that it depends on. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Signed-off-by: Thomas Bogendoerfer --- arch/mips/Kconfig | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4364f3dba688..1cd8fc903387 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1049,15 +1049,6 @@ config CAVIUM_OCTEON_SOC endchoice -config FIT_IMAGE_FDT_EPM5 - bool "Include FDT for Mobileye EyeQ5 development platforms" - depends on MACH_EYEQ5 - default n - help - Enable this to include the FDT for the EyeQ5 development platforms - from Mobileye in the FIT kernel image. - This requires u-boot on the platform. - source "arch/mips/alchemy/Kconfig" source "arch/mips/ath25/Kconfig" source "arch/mips/ath79/Kconfig" From 62d6592cb28882bc693df6c04276ebf41c931013 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 11 Apr 2026 18:17:43 +0800 Subject: [PATCH 1711/5214] MIPS: Loongson64: dts: Sort nodes The RTC's address is after UARTs, however the node is currently before them. Re-order the node to match address sequence. Signed-off-by: Icenowy Zheng Acked-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/boot/dts/loongson/ls7a-pch.dtsi | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi index 6dee85909f5a..59ca1ef0a7b6 100644 --- a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi +++ b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi @@ -19,13 +19,6 @@ pic: interrupt-controller@10000000 { #interrupt-cells = <2>; }; - rtc0: rtc@100d0100 { - compatible = "loongson,ls7a-rtc"; - reg = <0 0x100d0100 0 0x78>; - interrupt-parent = <&pic>; - interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; - }; - ls7a_uart0: serial@10080000 { compatible = "ns16550a"; reg = <0 0x10080000 0 0x100>; @@ -65,6 +58,13 @@ ls7a_uart3: serial@10080300 { no-loopback-test; }; + rtc0: rtc@100d0100 { + compatible = "loongson,ls7a-rtc"; + reg = <0 0x100d0100 0 0x78>; + interrupt-parent = <&pic>; + interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; + }; + pci@1a000000 { compatible = "loongson,ls7a-pci"; device_type = "pci"; From 8b498817d62c3e6c4ded294e8822159ea9a9a1f0 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 11 Apr 2026 18:17:44 +0800 Subject: [PATCH 1712/5214] MIPS: Loongson64: dts: Add node for LS7A PCH LPC Loongson 7A series PCH contain a LPC IRQ controller. Add the device tree node of it. Signed-off-by: Icenowy Zheng Acked-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/boot/dts/loongson/ls7a-pch.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi index 59ca1ef0a7b6..f304f99946f1 100644 --- a/arch/mips/boot/dts/loongson/ls7a-pch.dtsi +++ b/arch/mips/boot/dts/loongson/ls7a-pch.dtsi @@ -19,6 +19,15 @@ pic: interrupt-controller@10000000 { #interrupt-cells = <2>; }; + lpc: interrupt-controller@10002000 { + compatible = "loongson,ls7a-lpc"; + reg = <0 0x10002000 0 0x1000>; + interrupt-controller; + interrupt-parent = <&pic>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <2>; + }; + ls7a_uart0: serial@10080000 { compatible = "ns16550a"; reg = <0 0x10080000 0 0x100>; From 1a23bcb452d95f099e530414504c0d99ee076b3f Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Fri, 8 May 2026 02:54:19 -0700 Subject: [PATCH 1713/5214] PCI: qcom: Handle mixed PERST#/PHY DT configuration The driver currently supports two PERST# and PHY DT configurations. In one case, PHY and PERST# are described in the RC node. In the other case, they are described in the RP node. A mixed setup is not supported. One common example is PHY on the RP node while PERST# remains on the RC node. In that case the driver goes through the RP parse path, does not find PERST# on RP, and does not report an error because PERST# is optional. Probe can then succeed silently while PERST# is left uncontrolled, and PCIe endpoints fail to work later. This silent probe success makes debugging difficult. Handle this mixed case in the RP parse path by checking whether PERST# is present on RC and, if so, using the RC PERST# GPIO for RP ports while keeping RP parsing for PHY. Emit a warning to indicate mixed DT content so it can be fixed. This keeps mixed systems functional and makes the configuration issue visible instead of failing later at endpoint bring-up. Suggested-by: Manivannan Sadhasivam Signed-off-by: Qiang Yu [mani: folded the fix: https://lore.kernel.org/linux-pci/20260526-fix_perst_gpio_handling-v1-1-9170507bb4e9@oss.qualcomm.com] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260508-mix_perst_phy_dts-v1-1-9eff6ee9b51a@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index bc5a31efc0d0..9173369cca87 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -289,6 +289,7 @@ struct qcom_pcie { const struct qcom_pcie_cfg *cfg; struct dentry *debugfs; struct list_head ports; + struct gpio_desc *reset; bool use_pm_opp; }; @@ -1798,6 +1799,13 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, struct gpio_desc *reset; int ret; + if (pcie->reset) { + dev_warn_once(dev, + "Reusing PERST# from Root Complex node. DT needs to be fixed!\n"); + reset = pcie->reset; + goto skip_perst_parsing; + } + if (!of_find_property(np, "reset-gpios", NULL)) goto parse_child_node; @@ -1816,6 +1824,7 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, return PTR_ERR(reset); } +skip_perst_parsing: perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); if (!perst) return -ENOMEM; @@ -1873,6 +1882,13 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) struct device *dev = pcie->pci->dev; int ret = -ENODEV; + if (of_find_property(dev->of_node, "perst-gpios", NULL)) { + pcie->reset = devm_gpiod_get_optional(dev, "perst", + GPIOD_OUT_HIGH); + if (IS_ERR(pcie->reset)) + return PTR_ERR(pcie->reset); + } + for_each_available_child_of_node_scoped(dev->of_node, of_port) { if (!of_node_is_type(of_port, "pci")) continue; @@ -1899,7 +1915,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) struct device *dev = pcie->pci->dev; struct qcom_pcie_perst *perst; struct qcom_pcie_port *port; - struct gpio_desc *reset; struct phy *phy; int ret; @@ -1907,10 +1922,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) if (IS_ERR(phy)) return PTR_ERR(phy); - reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); - if (IS_ERR(reset)) - return PTR_ERR(reset); - ret = phy_init(phy); if (ret) return ret; @@ -1927,7 +1938,7 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); - perst->desc = reset; + perst->desc = pcie->reset; INIT_LIST_HEAD(&port->perst); INIT_LIST_HEAD(&perst->list); list_add_tail(&perst->list, &port->perst); From c93db46192779ff82a85eec571b2d0324e18beec Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Tue, 28 Apr 2026 14:07:15 +0530 Subject: [PATCH 1714/5214] PCI/ASPM: Add pcie_encode_t_power_on() helper to encode L1SS T_POWER_ON fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add pcie_encode_t_power_on() to encode the PCIe L1 PM Substates T_POWER_ON parameter into the T_POWER_ON Scale and T_POWER_ON Value fields. This helper can be used by the controller drivers to change the default/wrong value of T_POWER_ON in L1SS capability register to avoid incorrect calculation of LTR_L1.2_THRESHOLD value. The helper converts a T_POWER_ON time specified in microseconds into the appropriate scale/value encoding defined by PCIe r7.0, sec 7.8.3.2. Values that exceed the maximum encodable range are clamped to the largest representable encoding. Signed-off-by: Krishna Chaitanya Chundru [mani: changed t_power_on_us to u32, added helper name to subject] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Shawn Lin Reviewed-by: Shawn Lin Reviewed-by: Ilpo Järvinen Acked-by: Bjorn Helgaas Link: https://patch.msgid.link/20260428-t_power_on_fux-v5-1-f1ef926a91ff@oss.qualcomm.com --- drivers/pci/pci.h | 6 ++++++ drivers/pci/pcie/aspm.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4a14f88e543a..52953f2cef63 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1110,6 +1110,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked); void pcie_aspm_powersave_config_link(struct pci_dev *pdev); void pci_configure_ltr(struct pci_dev *pdev); void pci_bridge_reconfigure_ltr(struct pci_dev *pdev); +void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value); #else static inline void pcie_aspm_remove_cap(struct pci_dev *pdev, u32 lnkcap) { } static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { } @@ -1118,6 +1119,11 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { } static inline void pci_configure_ltr(struct pci_dev *pdev) { } static inline void pci_bridge_reconfigure_ltr(struct pci_dev *pdev) { } +static inline void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value) +{ + *scale = 0; + *value = 0; +} #endif #ifdef CONFIG_PCIE_ECRC diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 925373b98dff..172783e7f519 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -525,6 +525,46 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val) return 0; } +/** + * pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields + * @t_power_on_us: T_POWER_ON time in microseconds + * @scale: Encoded T_POWER_ON Scale (0..2) + * @value: Encoded T_POWER_ON Value + * + * T_POWER_ON is encoded as: + * T_POWER_ON(us) = scale_unit(us) * value + * + * where scale_unit is selected by @scale: + * 0: 2us + * 1: 10us + * 2: 100us + * + * If @t_power_on_us exceeds the maximum representable value, the result + * is clamped to the largest encodable T_POWER_ON. + * + * See PCIe r7.0, sec 7.8.3.2. + */ +void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value) +{ + u8 maxv = FIELD_MAX(PCI_L1SS_CAP_P_PWR_ON_VALUE); + + /* T_POWER_ON_Value ("value") is a 5-bit field with max value of 31. */ + if (t_power_on_us <= 2 * maxv) { + *scale = 0; /* Value times 2us */ + *value = DIV_ROUND_UP(t_power_on_us, 2); + } else if (t_power_on_us <= 10 * maxv) { + *scale = 1; /* Value times 10us */ + *value = DIV_ROUND_UP(t_power_on_us, 10); + } else if (t_power_on_us <= 100 * maxv) { + *scale = 2; /* value times 100us */ + *value = DIV_ROUND_UP(t_power_on_us, 100); + } else { + *scale = 2; + *value = maxv; + } +} +EXPORT_SYMBOL(pcie_encode_t_power_on); + /* * Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1 * register. Ports enter L1.2 when the most recent LTR value is greater From d697e90672484186e2676426bf810ad6fe269579 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Tue, 28 Apr 2026 14:07:16 +0530 Subject: [PATCH 1715/5214] PCI: dwc: Add dw_pcie_program_t_power_on() to program T_POWER_ON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The T_POWER_ON indicates the time (in μs) that a Port requires the port on the opposite side of Link to wait in L1.2.Exit after sampling CLKREQ# asserted before actively driving the interface. This value is used by the ASPM driver to compute the LTR_L1.2_THRESHOLD. Currently, some controllers expose T_POWER_ON value of zero in the L1SS capability registers, leading to incorrect LTR_L1.2_THRESHOLD calculations, which can result in improper L1.2 exit behavior and if AER happens to be supported and enabled, the error may be *reported* via AER. Add a helper to override T_POWER_ON value by the DWC controller drivers. Signed-off-by: Krishna Chaitanya Chundru [mani: changed t_power_on to u32] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Shawn Lin Reviewed-by: Shawn Lin Link: https://patch.msgid.link/20260428-t_power_on_fux-v5-2-f1ef926a91ff@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-designware.c | 28 ++++++++++++++++++++ drivers/pci/controller/dwc/pcie-designware.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..a78553a6f5d6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -1249,6 +1249,34 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci) dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap); } +/* TODO: Need to handle multi Root Ports */ +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on) +{ + u8 scale, value; + u16 offset; + u32 val; + + if (!t_power_on) + return; + + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS); + if (!offset) + return; + + pcie_encode_t_power_on(t_power_on, &scale, &value); + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, offset + PCI_L1SS_CAP); + val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE); + FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, &val, scale); + FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val, value); + + dw_pcie_writel_dbi(pci, offset + PCI_L1SS_CAP, val); + + dw_pcie_dbi_ro_wr_dis(pci); +} + void dw_pcie_setup(struct dw_pcie *pci) { u32 val; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index e759c5c7257e..ace141b90774 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -606,6 +606,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, u8 bar, size_t size); void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index); void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci); +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on); void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); From e9769bb172696b7e924eb30fc94909e0c30786cf Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Tue, 28 Apr 2026 14:07:17 +0530 Subject: [PATCH 1716/5214] PCI: qcom: Program T_POWER_ON Some platforms have incorrect T_POWER_ON value programmed in hardware. Generally these will be corrected by bootloaders, but not all targets support bootloaders to program correct values. That means the LTR_L1.2_THRESHOLD value calculated by aspm.c can be wrong, which can result in improper L1.2 exit behavior. If AER happens to be supported and enabled, the error may be *reported* via AER. Parse the 't-power-on-us' property from each Root Port node and program it as part of host initialization using dw_pcie_program_t_power_on() before link training. This property in added to the dtschema here [1]. Signed-off-by: Krishna Chaitanya Chundru [mani: reworded comment] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link[1]: https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/ Link: https://patch.msgid.link/20260428-t_power_on_fux-v5-3-f1ef926a91ff@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 9173369cca87..65688e28f10d 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -276,6 +276,7 @@ struct qcom_pcie_perst { struct qcom_pcie_port { struct list_head list; struct phy *phy; + u32 l1ss_t_power_on; struct list_head perst; }; @@ -1349,6 +1350,14 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie) return 0; } +static void qcom_pcie_configure_ports(struct qcom_pcie *pcie) +{ + struct qcom_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + dw_pcie_program_t_power_on(pcie->pci, port->l1ss_t_power_on); +} + static int qcom_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -1387,6 +1396,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX); dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC); + qcom_pcie_configure_ports(pcie); + qcom_pcie_perst_deassert(pcie); if (pcie->cfg->ops->config_sid) { @@ -1868,6 +1879,9 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node if (ret) return ret; + /* TODO: Move to DWC core after multi Root Port support is added */ + of_property_read_u32(node, "t-power-on-us", &port->l1ss_t_power_on); + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); From 6835fbed39bb329744a3f44e8e6a39e24079af10 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 21 May 2026 15:36:24 +0100 Subject: [PATCH 1717/5214] KVM: arm64: Reset page order in pKVM hyp_pool When a VM fails to initialise after its stage-2 hyp_pool has been initialised, that stage-2 must be torn down entirely. This requires resetting both the refcount and the order of its pages back to 0. Currently, reclaim_pgtable_pages() implicitly resets the page order by allocating the entire pool with order-0 granularity. However, in the VM initialisation error path, the addresses of the donated memory (the PGD) are already known, making it unnecessary to iterate over all pages in the pool. Since the vmemmap page order is a hyp_pool-specific field, leaving a non-zero order on hyp_pool destruction is harmless until another pool attempts to admit the page. Instead of resetting this field during destruction, reset it during pool initialization in hyp_pool_init(). For 'external' pages, we can't trust the order either as they bypass hyp_pool_init(). Since we never coalesce them, enforce order-0 to ensure safe insertion into the pool. This leaves no vmemmap order users outside of hyp_pool. Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260521143626.1005660-2-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 2 -- arch/arm64/kvm/hyp/nvhe/page_alloc.c | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 28a471d1927c..5c1e1742db4f 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -202,7 +202,6 @@ static void *guest_s2_zalloc_page(void *mc) memset(addr, 0, PAGE_SIZE); p = hyp_virt_to_page(addr); p->refcount = 1; - p->order = 0; return addr; } @@ -307,7 +306,6 @@ void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc) while (addr) { page = hyp_virt_to_page(addr); page->refcount = 0; - page->order = 0; push_hyp_memcache(mc, addr, hyp_virt_to_phys); WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1)); addr = hyp_alloc_pages(&vm->pool, 0); diff --git a/arch/arm64/kvm/hyp/nvhe/page_alloc.c b/arch/arm64/kvm/hyp/nvhe/page_alloc.c index a1eb27a1a747..57f86aa0f82f 100644 --- a/arch/arm64/kvm/hyp/nvhe/page_alloc.c +++ b/arch/arm64/kvm/hyp/nvhe/page_alloc.c @@ -94,13 +94,22 @@ static void __hyp_attach_page(struct hyp_pool *pool, struct hyp_page *p) { phys_addr_t phys = hyp_page_to_phys(p); - u8 order = p->order; struct hyp_page *buddy; + bool coalesce = true; + u8 order = p->order; - memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order); + /* + * 'external' pages are never coalesced and their ->order field + * untrusted as they bypass hyp_pool_init(). Enforce order-0. + */ + if (phys < pool->range_start || phys >= pool->range_end) { + order = 0; + coalesce = false; + } - /* Skip coalescing for 'external' pages being freed into the pool. */ - if (phys < pool->range_start || phys >= pool->range_end) + memset(hyp_page_to_virt(p), 0, PAGE_SIZE << order); + + if (!coalesce) goto insert; /* @@ -237,8 +246,10 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages, /* Init the vmemmap portion */ p = hyp_phys_to_page(phys); - for (i = 0; i < nr_pages; i++) + for (i = 0; i < nr_pages; i++) { hyp_set_page_refcounted(&p[i]); + p[i].order = 0; + } /* Attach the unused pages to the buddy tree */ for (i = reserved_pages; i < nr_pages; i++) From 20d2753295b1cd3c766199b5990119ca514e1302 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 21 May 2026 15:36:25 +0100 Subject: [PATCH 1718/5214] KVM: arm64: Fix __pkvm_init_vm error path In the unlikely case where insert_vm_table_entry fails, __pkvm_init_vm release the memory donated by the host for the PGD, but as the stage-2 is still set-up the hypervisor keeps a refcount on those pages, effectively leaking the references. Fix the rollback with the newly added kvm_guest_destroy_stage2(). Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization") Reported-by: Sashiko Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Signed-off-by: Vincent Donnefort Link: https://patch.msgid.link/20260521143626.1005660-3-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 13 +++++++++---- arch/arm64/kvm/hyp/nvhe/pkvm.c | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index 3cbfae0e3dda..4f2b871199cb 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -56,6 +56,7 @@ int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot p int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id); int kvm_host_prepare_stage2(void *pgt_pool_base); int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd); +void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm); void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt); int hyp_pin_shared_mem(void *from, void *to); diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 5c1e1742db4f..88cd72332208 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -290,16 +290,21 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd) return 0; } +void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm) +{ + guest_lock_component(vm); + kvm_pgtable_stage2_destroy(&vm->pgt); + vm->kvm.arch.mmu.pgd_phys = 0ULL; + guest_unlock_component(vm); +} + void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc) { struct hyp_page *page; void *addr; /* Dump all pgtable pages in the hyp_pool */ - guest_lock_component(vm); - kvm_pgtable_stage2_destroy(&vm->pgt); - vm->kvm.arch.mmu.pgd_phys = 0ULL; - guest_unlock_component(vm); + kvm_guest_destroy_stage2(vm); /* Drain the hyp_pool into the memcache */ addr = hyp_alloc_pages(&vm->pool, 0); diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index e7496eb85628..ff89b30f5c4a 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -839,10 +839,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva, /* Must be called last since this publishes the VM. */ ret = insert_vm_table_entry(handle, hyp_vm); if (ret) - goto err_remove_mappings; + goto err_destroy_stage2; return 0; +err_destroy_stage2: + kvm_guest_destroy_stage2(hyp_vm); err_remove_mappings: unmap_donated_memory(hyp_vm, vm_size); unmap_donated_memory(pgd, pgd_size); From 5c30c9fc117cc7d0c1b45741d1127edd4c2ae990 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 21 May 2026 15:36:26 +0100 Subject: [PATCH 1719/5214] KVM: arm64: Add fail-safe for refcounted pages in __pkvm_hyp_donate_host A previous bug in __pkvm_init_vm error path showed that the hypervisor could leak refcounted pages, (i.e. losing access to a page while its refcount is still elevated). This poses a threat to the pKVM state machine. Address this by introducing a fail-safe in __pkvm_hyp_donate_host. Transitions are not a hot path so added security is worth the extra check. Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Signed-off-by: Vincent Donnefort Link: https://patch.msgid.link/20260521143626.1005660-4-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 88cd72332208..3263f6d0a0a4 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -833,6 +833,16 @@ static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_pa return 0; } +static int __hyp_check_page_count_range(phys_addr_t phys, u64 size) +{ + for_each_hyp_page(page, phys, size) { + if (page->refcount) + return -EBUSY; + } + + return 0; +} + static bool guest_pte_is_poisoned(kvm_pte_t pte) { if (kvm_pte_valid(pte)) @@ -1031,7 +1041,6 @@ int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn) int __pkvm_host_unshare_hyp(u64 pfn) { u64 phys = hyp_pfn_to_phys(pfn); - u64 virt = (u64)__hyp_va(phys); u64 size = PAGE_SIZE; int ret; @@ -1044,10 +1053,9 @@ int __pkvm_host_unshare_hyp(u64 pfn) ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED); if (ret) goto unlock; - if (hyp_page_count((void *)virt)) { - ret = -EBUSY; + ret = __hyp_check_page_count_range(phys, size); + if (ret) goto unlock; - } __hyp_set_page_state_range(phys, size, PKVM_NOPAGE); WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_OWNED)); @@ -1110,6 +1118,10 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages) if (ret) goto unlock; + ret = __hyp_check_page_count_range(phys, size); + if (ret) + goto unlock; + __hyp_set_page_state_range(phys, size, PKVM_NOPAGE); WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size); WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HOST)); From 829dff83597615208aedf0f5abb3878b47f2314d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:25 +0200 Subject: [PATCH 1720/5214] mtd: spi-nor: debugfs: Fix the flags list As mentioned above the spi_nor_option_flags enumeration in core.h, this list should be kept in sync with the one in the core. Add the missing flag. Fixes: 6a42bc97ccda ("mtd: spi-nor: core: Allow specifying the byte order in Octal DTR mode") Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/debugfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index fa6956144d2e..d700e0b27182 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -28,6 +28,7 @@ static const char *const snor_f_names[] = { SNOR_F_NAME(RWW), SNOR_F_NAME(ECC), SNOR_F_NAME(NO_WP), + SNOR_F_NAME(SWAP16), }; #undef SNOR_F_NAME From e1d456b26bf23e30db305a6184e8abd9ab68bbf2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:26 +0200 Subject: [PATCH 1721/5214] mtd: spi-nor: swp: Improve locking user experience In the case of the first block being locked (or the few first blocks), if the user want to fully unlock the device it has two possibilities: - either it asks to unlock the entire device, and this works; - or it asks to unlock just the block(s) that are currently locked, which fails. It fails because the conditions "can_be_top" and "can_be_bottom" are true. Indeed, in this case, we unlock everything, so the TB bit does not matter. However in the current implementation, use_top would be true (as this is the favourite option) and lock_len, which in practice should be reduced down to 0, is set to "nor->params->size - (ofs + len)" which is a positive number. This is wrong. An easy way is to simply add an extra condition. In the unlock() path, if we can achieve the same result from both sides, it means we unlock everything and lock_len must simply be 0. A comment is added to clarify that logic. Fixes: 3dd8012a8eeb ("mtd: spi-nor: add TB (Top/Bottom) protect support") Cc: stable@kernel.org Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index e67a81dbb6bf..d5f4bf555cfc 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -282,8 +282,15 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) /* Prefer top, if both are valid */ use_top = can_be_top; - /* lock_len: length of region that should remain locked */ - if (use_top) + /* + * lock_len: length of region that should remain locked. + * + * When can_be_top and can_be_bottom booleans are true, both adjacent + * regions are unlocked, thus the entire flash can be unlocked. + */ + if (can_be_top && can_be_bottom) + lock_len = 0; + else if (use_top) lock_len = nor->params->size - (ofs + len); else lock_len = ofs; From a6470e2162e9c3779a4bd6ff3bed1b81d796e46e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:27 +0200 Subject: [PATCH 1722/5214] mtd: spi-nor: Drop duplicate Kconfig dependency I do not think the MTD dependency is needed twice. This is likely a duplicate coming from a former rebase when the spi-nor core got cleaned up a while ago. Remove the extra line. Fixes: b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 24cd25de2b8b..fd05a24d64a9 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig MTD_SPI_NOR tristate "SPI NOR device support" - depends on MTD depends on MTD && SPI_MASTER select SPI_MEM help From f316b8535887c50be009902bd02098fddb47e2e7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:28 +0200 Subject: [PATCH 1723/5214] mtd: spi-nor: Make sure the QE bit is kept enabled if useful Not all chips implement the 4BAIT table which typically indicates the program capability, while many of them do implement the relevant SFDP parts indicating the read capabilities. In such a situation, programs can happen in single mode (1-1-1) and reads in quad mode (1-1-4 or 1-4-4). For the reads to work in such condition, the QE bit must be set. In case we later use the spi_nor_write_16bit_sr_and_check() helper with a chip with such configuration, the QE bit would get incorrectly cleared. Make sure this doesn't happen by keeping the QE bit under a simpler condition: - the quad enable hook is there (no change) - and at least one of the two protocols is based on quad I/O cycles Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5dd0b3cb5250..394c27de02d6 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -869,8 +869,8 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) ret = spi_nor_read_cr(nor, &sr_cr[1]); if (ret) return ret; - } else if (spi_nor_get_protocol_width(nor->read_proto) == 4 && - spi_nor_get_protocol_width(nor->write_proto) == 4 && + } else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4) && nor->params->quad_enable) { /* * If the Status Register 2 Read command (35h) is not From 7c4e909b176f661e834853fa726a6e58acab00e1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:29 +0200 Subject: [PATCH 1724/5214] mtd: spi-nor: Improve opcodes documentation There are two status registers, named 1 and 2. The current wording is misleading as "1" may refer to the status register ID as well as the number of bytes required (which, in this case can be 1 or 2). Clarify the comments by aligning them on the same pattern: "{read,write} status {1,2} register" Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- include/linux/mtd/spi-nor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index cdcfe0fd2e7d..90a0cf583512 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -21,8 +21,8 @@ /* Flash opcodes. */ #define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_WREN 0x06 /* Write enable */ -#define SPINOR_OP_RDSR 0x05 /* Read status register */ -#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SPINOR_OP_RDSR 0x05 /* Read status register 1 */ +#define SPINOR_OP_WRSR 0x01 /* Write status register 1 */ #define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */ #define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */ #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ From 154f375f7d31f2f57b4d312ac22562d64a7bb64f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:30 +0200 Subject: [PATCH 1725/5214] mtd: spi-nor: debugfs: Align variable access with the rest of the file The "params" variable is used everywhere else, align this particular line of the file to use "params" directly rather than the "nor" pointer. Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index d700e0b27182..69830ad43990 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -139,7 +139,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); - seq_printf(s, " %02x (%s)\n", nor->params->die_erase_opcode, buf); + seq_printf(s, " %02x (%s)\n", params->die_erase_opcode, buf); } seq_puts(s, "\nsector map\n"); From 461e2f8a2d943ea2b1b966e8ffc8e5ac02c6a686 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:31 +0200 Subject: [PATCH 1726/5214] mtd: spi-nor: debugfs: Enhance output Align the number of dashes to the bigger column width (the title in this case) to make the output more pleasant and aligned with what is done in the "params" file output. Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 69830ad43990..d0191eb9f879 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -144,7 +144,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_puts(s, "\nsector map\n"); seq_puts(s, " region (in hex) | erase mask | overlaid\n"); - seq_puts(s, " ------------------+------------+----------\n"); + seq_puts(s, " ------------------+------------+---------\n"); for (i = 0; i < erase_map->n_regions; i++) { u64 start = region[i].offset; u64 end = start + region[i].size - 1; From 5eeff82d389e381422f48a4bde4b7f4a5a2dc584 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:32 +0200 Subject: [PATCH 1727/5214] mtd: spi-nor: swp: Explain the MEMLOCK ioctl implementation behaviour Add more details about how these requests are actually handled in the SPI NOR core. Their behaviour was not entirely clear to me at first, and explaining them in plain English sounds the way to go. Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index e838c40a2589..65bb4b5e1e2b 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -279,9 +279,14 @@ struct spi_nor_erase_map { /** * struct spi_nor_locking_ops - SPI NOR locking methods - * @lock: lock a region of the SPI NOR. - * @unlock: unlock a region of the SPI NOR. - * @is_locked: check if a region of the SPI NOR is completely locked + * @lock: lock a region of the SPI NOR, never locks more than what is + * requested, ie. may lock less. + * @unlock: unlock a region of the SPI NOR, may unlock more than what is + * requested. + * @is_locked: check if a region of the SPI NOR is completely locked, returns + * false otherwise. This feedback may be misleading because users + * may get an "unlocked" status even though a subpart of the region + * is effectively locked. */ struct spi_nor_locking_ops { int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len); From cb3021466daa3d7f87cdec8c294649315dad711f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:33 +0200 Subject: [PATCH 1728/5214] mtd: spi-nor: swp: Clarify a comment The comment states that some power of two sizes are not supported. This is very device dependent (based on the size), so modulate a bit the sentence to make it more accurate. Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index d5f4bf555cfc..f221d6361b57 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -305,7 +305,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) val = (val & ~SR_BP3) | SR_BP3_BIT6; - /* Some power-of-two sizes are not supported */ + /* Some power-of-two sizes may not be supported */ if (val & ~mask) return -EINVAL; } From 2188bbbb4a20ca1864de8e1447d13e5d19e8355a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:34 +0200 Subject: [PATCH 1729/5214] mtd: spi-nor: swp: Use a pointer for SR instead of a single byte At this stage, the Status Register is most often seen as a single byte. This is subject to change when we will need to read the CMP bit which is located in the Control Register (kind of secondary status register). Both will need to be carried. Change a few prototypes to carry a u8 pointer. This way it also makes it very clear where we access the first register, and where we will access the second. There is no functional change. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 48 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index f221d6361b57..61830f18a147 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -55,13 +55,13 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) return sector_size; } -static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, +static void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 *len) { u64 min_prot_len; u8 mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); - u8 bp, val = sr & mask; + u8 bp, val = sr[0] & mask; if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; @@ -81,7 +81,7 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, if (*len > nor->params->size) *len = nor->params->size; - if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) + if (nor->flags & SNOR_F_HAS_SR_TB && sr[0] & tb_mask) *ofs = 0; else *ofs = nor->params->size - *len; @@ -92,7 +92,7 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, * (if @locked is false); false otherwise. */ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, - u64 len, u8 sr, bool locked) + u64 len, const u8 *sr, bool locked) { loff_t lock_offs, lock_offs_max, offs_max; u64 lock_len; @@ -113,13 +113,13 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, return (ofs >= lock_offs_max) || (offs_max <= lock_offs); } -static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, u8 sr) +static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); } static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len, - u8 sr) + const u8 *sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); } @@ -160,7 +160,8 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len, static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { u64 min_prot_len; - int ret, status_old, status_new; + int ret; + u8 status_old[1] = {}, status_new[1] = {}; u8 mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; @@ -172,7 +173,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) if (ret) return ret; - status_old = nor->bouncebuf[0]; + status_old[0] = nor->bouncebuf[0]; /* If nothing in our range is unlocked, we don't need to do anything */ if (spi_nor_is_locked_sr(nor, ofs, len, status_old)) @@ -217,7 +218,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) return -EINVAL; } - status_new = (status_old & ~mask & ~tb_mask) | val; + status_new[0] = (status_old[0] & ~mask & ~tb_mask) | val; /* * Disallow further writes if WP# pin is neither left floating nor @@ -225,20 +226,20 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) * WP# pin hard strapped to GND can be a valid use case. */ if (!(nor->flags & SNOR_F_NO_WP)) - status_new |= SR_SRWD; + status_new[0] |= SR_SRWD; if (!use_top) - status_new |= tb_mask; + status_new[0] |= tb_mask; /* Don't bother if they're the same */ - if (status_new == status_old) + if (status_new[0] == status_old[0]) return 0; /* Only modify protection if it will not unlock other areas */ - if ((status_new & mask) < (status_old & mask)) + if ((status_new[0] & mask) < (status_old[0] & mask)) return -EINVAL; - return spi_nor_write_sr_and_check(nor, status_new); + return spi_nor_write_sr_and_check(nor, status_new[0]); } /* @@ -249,7 +250,8 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { u64 min_prot_len; - int ret, status_old, status_new; + int ret; + u8 status_old[1], status_new[1]; u8 mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; @@ -261,7 +263,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) if (ret) return ret; - status_old = nor->bouncebuf[0]; + status_old[0] = nor->bouncebuf[0]; /* If nothing in our range is locked, we don't need to do anything */ if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old)) @@ -310,24 +312,24 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) return -EINVAL; } - status_new = (status_old & ~mask & ~tb_mask) | val; + status_new[0] = (status_old[0] & ~mask & ~tb_mask) | val; /* Don't protect status register if we're fully unlocked */ if (lock_len == 0) - status_new &= ~SR_SRWD; + status_new[0] &= ~SR_SRWD; if (!use_top) - status_new |= tb_mask; + status_new[0] |= tb_mask; /* Don't bother if they're the same */ - if (status_new == status_old) + if (status_new[0] == status_old[0]) return 0; /* Only modify protection if it will not lock other areas */ - if ((status_new & mask) > (status_old & mask)) + if ((status_new[0] & mask) > (status_old[0] & mask)) return -EINVAL; - return spi_nor_write_sr_and_check(nor, status_new); + return spi_nor_write_sr_and_check(nor, status_new[0]); } /* @@ -345,7 +347,7 @@ static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) if (ret) return ret; - return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); + return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf); } static const struct spi_nor_locking_ops spi_nor_sr_locking_ops = { From 82877fd772f7884474add88c476108b1dd670ef6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:35 +0200 Subject: [PATCH 1730/5214] mtd: spi-nor: swp: Create a helper that writes SR, CR and checks There are many helpers already to either read and/or write SR and/or CR, as well as sometimes check the returned values. In order to be able to switch from a 1 byte status register to a 2 bytes status register while keeping the same level of verification, let's introduce a new helper that writes them both (atomically) and then reads them back (separated) to compare the values. In case 2 bytes registers are not supported, we still have the usual fallback available in the helper being exported to the rest of the core. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.c | 65 ++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/core.h | 1 + 2 files changed, 66 insertions(+) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 394c27de02d6..2799c21d0b67 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -976,6 +976,54 @@ int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) return 0; } +/** + * spi_nor_write_16bit_sr_cr_and_check() - Write the Status Register 1 and the + * Configuration Register in one shot. Ensure that the bytes written in both + * registers match the received value. + * @nor: pointer to a 'struct spi_nor'. + * @regs: two-byte array with values to be written to the status and + * configuration registers. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_16bit_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +{ + u8 written_regs[2]; + int ret; + + written_regs[0] = regs[0]; + written_regs[1] = regs[1]; + nor->bouncebuf[0] = regs[0]; + nor->bouncebuf[1] = regs[1]; + + ret = spi_nor_write_sr(nor, nor->bouncebuf, 2); + if (ret) + return ret; + + ret = spi_nor_read_sr(nor, &nor->bouncebuf[0]); + if (ret) + return ret; + + if (written_regs[0] != nor->bouncebuf[0]) { + dev_dbg(nor->dev, "SR: Read back test failed\n"); + return -EIO; + } + + if (nor->flags & SNOR_F_NO_READ_CR) + return 0; + + ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]); + if (ret) + return ret; + + if (written_regs[1] != nor->bouncebuf[1]) { + dev_dbg(nor->dev, "CR: read back test failed\n"); + return -EIO; + } + + return 0; +} + /** * spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that * the byte written match the received value without affecting other bits in the @@ -993,6 +1041,23 @@ int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) return spi_nor_write_sr1_and_check(nor, sr1); } +/** + * spi_nor_write_sr_cr_and_check() - Write the Status Register 1 and ensure that + * the byte written match the received value. Same for the Control Register if + * available. + * @nor: pointer to a 'struct spi_nor'. + * @regs: byte array to be written to the registers. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +{ + if (nor->flags & SNOR_F_HAS_16BIT_SR) + return spi_nor_write_16bit_sr_cr_and_check(nor, regs); + + return spi_nor_write_sr1_and_check(nor, regs[0]); +} + /** * spi_nor_write_sr2() - Write the Status Register 2 using the * SPINOR_OP_WRSR2 (3eh) command. diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 65bb4b5e1e2b..0753d0a6f8b8 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -637,6 +637,7 @@ int spi_nor_read_cr(struct spi_nor *nor, u8 *cr); int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len); int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr); +int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); From 8851f82645b42307b74dba190bee5a19cec97ae3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:36 +0200 Subject: [PATCH 1731/5214] mtd: spi-nor: swp: Rename a mask "mask" is not very descriptive when we already manipulate two masks, and soon will manipulate three. Rename it "bp_mask" to align with the existing "tb_mask" and soon "cmp_mask". Signed-off-by: Miquel Raynal Reviewed-by: Tudor Ambarus Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 61830f18a147..07269e09370a 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -59,9 +59,9 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_ u64 *len) { u64 min_prot_len; - u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); - u8 bp, val = sr[0] & mask; + u8 bp, val = sr[0] & bp_mask; if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; @@ -162,7 +162,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) u64 min_prot_len; int ret; u8 status_old[1] = {}, status_new[1] = {}; - u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; loff_t lock_len; @@ -201,7 +201,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) lock_len = ofs + len; if (lock_len == nor->params->size) { - val = mask; + val = bp_mask; } else { min_prot_len = spi_nor_get_min_prot_length_sr(nor); pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; @@ -210,15 +210,15 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) val = (val & ~SR_BP3) | SR_BP3_BIT6; - if (val & ~mask) + if (val & ~bp_mask) return -EINVAL; /* Don't "lock" with no region! */ - if (!(val & mask)) + if (!(val & bp_mask)) return -EINVAL; } - status_new[0] = (status_old[0] & ~mask & ~tb_mask) | val; + status_new[0] = (status_old[0] & ~bp_mask & ~tb_mask) | val; /* * Disallow further writes if WP# pin is neither left floating nor @@ -236,7 +236,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) return 0; /* Only modify protection if it will not unlock other areas */ - if ((status_new[0] & mask) < (status_old[0] & mask)) + if ((status_new[0] & bp_mask) < (status_old[0] & bp_mask)) return -EINVAL; return spi_nor_write_sr_and_check(nor, status_new[0]); @@ -252,7 +252,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) u64 min_prot_len; int ret; u8 status_old[1], status_new[1]; - u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; loff_t lock_len; @@ -308,11 +308,11 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) val = (val & ~SR_BP3) | SR_BP3_BIT6; /* Some power-of-two sizes may not be supported */ - if (val & ~mask) + if (val & ~bp_mask) return -EINVAL; } - status_new[0] = (status_old[0] & ~mask & ~tb_mask) | val; + status_new[0] = (status_old[0] & ~bp_mask & ~tb_mask) | val; /* Don't protect status register if we're fully unlocked */ if (lock_len == 0) @@ -326,7 +326,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) return 0; /* Only modify protection if it will not lock other areas */ - if ((status_new[0] & mask) > (status_old[0] & mask)) + if ((status_new[0] & bp_mask) > (status_old[0] & bp_mask)) return -EINVAL; return spi_nor_write_sr_and_check(nor, status_new[0]); From ba32a259dd32af911e4f4fd49cdd7f32a40ccde5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:37 +0200 Subject: [PATCH 1732/5214] mtd: spi-nor: swp: Create a TB intermediate variable Ease the future reuse of the tb (Top/Bottom) boolean by creating an intermediate variable. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 07269e09370a..540cd221c455 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -62,6 +62,7 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_ u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 bp, val = sr[0] & bp_mask; + bool tb = (nor->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; @@ -81,7 +82,7 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_ if (*len > nor->params->size) *len = nor->params->size; - if (nor->flags & SNOR_F_HAS_SR_TB && sr[0] & tb_mask) + if (tb) *ofs = 0; else *ofs = nor->params->size - *len; From 290e83833688b4df0da746af4c5fb87dbed2834a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:38 +0200 Subject: [PATCH 1733/5214] mtd: spi-nor: swp: Create helpers for building the SR register The status register contains 3 or 4 BP (Block Protect) bits, 0 or 1 TB (Top/Bottom) bit, soon 0 or 1 CMP (Complement) bit. The last BP bit and the TB bit locations change between vendors. The whole logic of buildling the content of the status register based on some input conditions is used two times and soon will be used 4 times. Create dedicated helpers for these steps. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 83 ++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 540cd221c455..8a75cd59cb10 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -125,6 +125,43 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len, return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); } +static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow) +{ + u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 val = pow << SR_BP_SHIFT; + + if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + val = (val & ~SR_BP3) | SR_BP3_BIT6; + + if (val & ~mask) + return -EINVAL; + + sr[0] |= val; + + return 0; +} + +static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, + u8 pow, bool use_top) +{ + u8 bp_mask = spi_nor_get_sr_bp_mask(nor); + u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + int ret; + + new_sr[0] = old_sr[0] & ~bp_mask & ~tb_mask; + + /* Build BP field */ + ret = spi_nor_sr_set_bp_mask(nor, &new_sr[0], pow); + if (ret) + return ret; + + /* Build TB field */ + if (!use_top) + new_sr[0] |= tb_mask; + + return 0; +} + /* * Lock a region of the flash. Compatible with ST Micro and similar flash. * Supports the block protection bits BP{0,1,2}/BP{0,1,2,3} in the status @@ -164,11 +201,10 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) int ret; u8 status_old[1] = {}, status_new[1] = {}; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); - u8 tb_mask = spi_nor_get_sr_tb_mask(nor); - u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; + u8 pow; ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) @@ -202,24 +238,19 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) lock_len = ofs + len; if (lock_len == nor->params->size) { - val = bp_mask; + pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); } else { min_prot_len = spi_nor_get_min_prot_length_sr(nor); pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - val = pow << SR_BP_SHIFT; - - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) - val = (val & ~SR_BP3) | SR_BP3_BIT6; - - if (val & ~bp_mask) - return -EINVAL; - - /* Don't "lock" with no region! */ - if (!(val & bp_mask)) - return -EINVAL; } - status_new[0] = (status_old[0] & ~bp_mask & ~tb_mask) | val; + ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); + if (ret) + return ret; + + /* Don't "lock" with no region! */ + if (!(status_new[0] & bp_mask)) + return -EINVAL; /* * Disallow further writes if WP# pin is neither left floating nor @@ -229,9 +260,6 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) if (!(nor->flags & SNOR_F_NO_WP)) status_new[0] |= SR_SRWD; - if (!use_top) - status_new[0] |= tb_mask; - /* Don't bother if they're the same */ if (status_new[0] == status_old[0]) return 0; @@ -254,11 +282,10 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) int ret; u8 status_old[1], status_new[1]; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); - u8 tb_mask = spi_nor_get_sr_tb_mask(nor); - u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; + u8 pow; ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) @@ -299,29 +326,21 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) lock_len = ofs; if (lock_len == 0) { - val = 0; /* fully unlocked */ + pow = 0; /* fully unlocked */ } else { min_prot_len = spi_nor_get_min_prot_length_sr(nor); pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - val = pow << SR_BP_SHIFT; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) - val = (val & ~SR_BP3) | SR_BP3_BIT6; - - /* Some power-of-two sizes may not be supported */ - if (val & ~bp_mask) - return -EINVAL; } - status_new[0] = (status_old[0] & ~bp_mask & ~tb_mask) | val; + ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); + if (ret) + return ret; /* Don't protect status register if we're fully unlocked */ if (lock_len == 0) status_new[0] &= ~SR_SRWD; - if (!use_top) - status_new[0] |= tb_mask; - /* Don't bother if they're the same */ if (status_new[0] == status_old[0]) return 0; From c672673c4b7b82d21537cd5aa32b98f48c20a2b2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:39 +0200 Subject: [PATCH 1734/5214] mtd: spi-nor: swp: Simplify checking the locked/unlocked range In both the locking/unlocking steps, at the end we verify whether we do not lock/unlock more than requested (in which case an error must be returned). While being possible to do that with very simple mask comparisons, it does not scale when adding extra locking features such as the CMP possibility. In order to make these checks slightly easier to read and more future proof, use existing helpers to read the (future) status register, extract the covered range, and compare it with very usual algebric comparisons. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 8a75cd59cb10..229074d4e121 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -200,7 +200,8 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) u64 min_prot_len; int ret; u8 status_old[1] = {}, status_new[1] = {}; - u8 bp_mask = spi_nor_get_sr_bp_mask(nor); + loff_t ofs_old, ofs_new; + u64 len_old, len_new; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -248,10 +249,6 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) if (ret) return ret; - /* Don't "lock" with no region! */ - if (!(status_new[0] & bp_mask)) - return -EINVAL; - /* * Disallow further writes if WP# pin is neither left floating nor * wrongly tied to GND (that includes internal pull-downs). @@ -260,12 +257,20 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) if (!(nor->flags & SNOR_F_NO_WP)) status_new[0] |= SR_SRWD; + spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + + /* Don't "lock" with no region! */ + if (!len_new) + return -EINVAL; + /* Don't bother if they're the same */ if (status_new[0] == status_old[0]) return 0; /* Only modify protection if it will not unlock other areas */ - if ((status_new[0] & bp_mask) < (status_old[0] & bp_mask)) + if (len_old && + (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old))) return -EINVAL; return spi_nor_write_sr_and_check(nor, status_new[0]); @@ -281,7 +286,8 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) u64 min_prot_len; int ret; u8 status_old[1], status_new[1]; - u8 bp_mask = spi_nor_get_sr_bp_mask(nor); + loff_t ofs_old, ofs_new; + u64 len_old, len_new; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -346,7 +352,10 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) return 0; /* Only modify protection if it will not lock other areas */ - if ((status_new[0] & bp_mask) > (status_old[0] & bp_mask)) + spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + if (len_old && len_new && + (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new))) return -EINVAL; return spi_nor_write_sr_and_check(nor, status_new[0]); From 0e005045292dde634ed3973b5070eb2de62dc1df Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:40 +0200 Subject: [PATCH 1735/5214] mtd: spi-nor: swp: Cosmetic changes As a final preparation step for the introduction of CMP support, make a few more cosmetic changes to simplify the reading of the diff when adding the CMP feature. In particular, define "min_prot_len" earlier as it will be reused and move the definition of the "ret" variable at the end of the stack just because it looks better. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/swp.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 229074d4e121..2d1b1c8a535a 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -197,14 +197,14 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, */ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { - u64 min_prot_len; - int ret; + u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[1] = {}, status_new[1] = {}; loff_t ofs_old, ofs_new; u64 len_old, len_new; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; + int ret; u8 pow; ret = spi_nor_read_sr(nor, nor->bouncebuf); @@ -238,12 +238,10 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) else lock_len = ofs + len; - if (lock_len == nor->params->size) { + if (lock_len == nor->params->size) pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); - } else { - min_prot_len = spi_nor_get_min_prot_length_sr(nor); + else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - } ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); if (ret) @@ -283,14 +281,14 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) */ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { - u64 min_prot_len; - int ret; + u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[1], status_new[1]; loff_t ofs_old, ofs_new; u64 len_old, len_new; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; + int ret; u8 pow; ret = spi_nor_read_sr(nor, nor->bouncebuf); @@ -331,14 +329,11 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) else lock_len = ofs; - if (lock_len == 0) { + if (lock_len == 0) pow = 0; /* fully unlocked */ - } else { - min_prot_len = spi_nor_get_min_prot_length_sr(nor); + else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - } - ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); if (ret) return ret; From b7b63475903cc9967ae53c579bc1ad9ed2519080 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:41 +0200 Subject: [PATCH 1736/5214] mtd: spi-nor: Create a local SR cache In order to be able to generate debugfs output without having to actually reach the flash, create a SPI NOR local cache of the status registers. What matters in our case are all the bits related to sector locking. As such, in order to make it clear that this cache is not intended to be used anywhere else, we zero the irrelevant bits. The cache is initialized once during the early init, and then maintained every time the write protection scheme is updated. Suggested-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.c | 4 +++- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/swp.c | 35 +++++++++++++++++++++++++++++++++-- include/linux/mtd/spi-nor.h | 2 ++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 2799c21d0b67..1fc71d6e0fec 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3326,10 +3326,12 @@ static int spi_nor_init(struct spi_nor *nor) * protection bits are volatile. The latter is indicated by * SNOR_F_SWP_IS_VOLATILE. */ + spi_nor_cache_sr_lock_bits(nor, NULL); if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) || (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) && - nor->flags & SNOR_F_SWP_IS_VOLATILE)) + nor->flags & SNOR_F_SWP_IS_VOLATILE)) { spi_nor_try_unlock_all(nor); + } if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 0753d0a6f8b8..cd355e94b97e 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -679,6 +679,7 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, void spi_nor_init_default_locking_ops(struct spi_nor *nor); void spi_nor_try_unlock_all(struct spi_nor *nor); +void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr); void spi_nor_set_mtd_locking_ops(struct spi_nor *nor); void spi_nor_set_mtd_otp_ops(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 2d1b1c8a535a..cd37fec08c0e 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -162,6 +162,25 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, return 0; } +/* + * Keep a local cache containing all lock-related bits for debugfs use only. + * This way, debugfs never needs to access the flash directly. + */ +void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) +{ + u8 bp_mask = spi_nor_get_sr_bp_mask(nor); + u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + + if (!sr) { + if (spi_nor_read_sr(nor, nor->bouncebuf)) + return; + + sr = nor->bouncebuf; + } + + nor->dfs_sr_cache[0] = sr[0] & (bp_mask | tb_mask | SR_SRWD); +} + /* * Lock a region of the flash. Compatible with ST Micro and similar flash. * Supports the block protection bits BP{0,1,2}/BP{0,1,2,3} in the status @@ -271,7 +290,13 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old))) return -EINVAL; - return spi_nor_write_sr_and_check(nor, status_new[0]); + ret = spi_nor_write_sr_and_check(nor, status_new[0]); + if (ret) + return ret; + + spi_nor_cache_sr_lock_bits(nor, status_new); + + return 0; } /* @@ -353,7 +378,13 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new))) return -EINVAL; - return spi_nor_write_sr_and_check(nor, status_new[0]); + ret = spi_nor_write_sr_and_check(nor, status_new[0]); + if (ret) + return ret; + + spi_nor_cache_sr_lock_bits(nor, status_new); + + return 0; } /* diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 90a0cf583512..9ad77f9e76c2 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -371,6 +371,7 @@ struct spi_nor_flash_parameter; * @reg_proto: the SPI protocol for read_reg/write_reg/erase operations * @sfdp: the SFDP data of the flash * @debugfs_root: pointer to the debugfs directory + * @dfs_sr_cache: Status Register cached value for debugfs use only * @controller_ops: SPI NOR controller driver specific operations. * @params: [FLASH-SPECIFIC] SPI NOR flash parameters and settings. * The structure includes legacy flash parameters and @@ -409,6 +410,7 @@ struct spi_nor { enum spi_nor_cmd_ext cmd_ext_type; struct sfdp *sfdp; struct dentry *debugfs_root; + u8 dfs_sr_cache[2]; const struct spi_nor_controller_ops *controller_ops; From 47eb01f46e2d0741e058006dafa1fdbbbccf9b15 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:42 +0200 Subject: [PATCH 1737/5214] mtd: spi-nor: debugfs: Add locking support The ioctl output may be counter intuitive in some cases. Asking for a "locked status" over a region that is only partially locked will return "unlocked" whereas in practice maybe the biggest part is actually locked. Knowing what is the real software locking state through debugfs would be very convenient for development/debugging purposes, hence this proposal for adding an extra block at the end of the file: a "locked sectors" array which lists every section, if it is locked or not, showing both the address ranges and the sizes in numbers of "lock sectors" (which on small density devices is typically different than erase blocks). Here is an example of output, what is after the "sector map" is new. $ cat /sys/kernel/debug/spi-nor/spi0.0/params name (null) id ef a0 20 00 00 00 size 64.0 MiB write size 1 page size 256 address nbytes 4 flags HAS_SR_TB | 4B_OPCODES | HAS_4BAIT | HAS_LOCK | HAS_16BIT_SR | HAS_SR_TB_BIT6 | HAS_4BIT_BP | SOFT_RESET | NO_WP opcodes read 0xec dummy cycles 6 erase 0xdc program 0x34 8D extension none protocols read 1S-4S-4S write 1S-1S-4S register 1S-1S-1S erase commands 21 (4.00 KiB) [1] dc (64.0 KiB) [3] c7 (64.0 MiB) sector map region (in hex) | erase mask | overlaid ------------------+------------+--------- 00000000-03ffffff | [ 3] | no locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-03ffffff | unlocked | 1024 Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.h | 8 ++++++++ drivers/mtd/spi-nor/debugfs.c | 28 ++++++++++++++++++++++++++++ drivers/mtd/spi-nor/swp.c | 13 +++++++++---- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index cd355e94b97e..ce46771ecdc8 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -287,6 +287,9 @@ struct spi_nor_erase_map { * false otherwise. This feedback may be misleading because users * may get an "unlocked" status even though a subpart of the region * is effectively locked. + * + * If in doubt during development, check-out the debugfs output which tries to + * be more user friendly. */ struct spi_nor_locking_ops { int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len); @@ -678,6 +681,7 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_bfpt *bfpt); void spi_nor_init_default_locking_ops(struct spi_nor *nor); +bool spi_nor_has_default_locking_ops(struct spi_nor *nor); void spi_nor_try_unlock_all(struct spi_nor *nor); void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr); void spi_nor_set_mtd_locking_ops(struct spi_nor *nor); @@ -712,6 +716,10 @@ static inline bool spi_nor_needs_sfdp(const struct spi_nor *nor) return !nor->info->size; } +u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor); +void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 *len); +bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr); + #ifdef CONFIG_DEBUG_FS void spi_nor_debugfs_register(struct spi_nor *nor); void spi_nor_debugfs_shutdown(void); diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index d0191eb9f879..82df8ad4176c 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include @@ -77,11 +78,14 @@ static void spi_nor_print_flags(struct seq_file *s, unsigned long flags, static int spi_nor_params_show(struct seq_file *s, void *data) { struct spi_nor *nor = s->private; + u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *erase_map = ¶ms->erase_map; struct spi_nor_erase_region *region = erase_map->regions; const struct flash_info *info = nor->info; char buf[16], *str; + loff_t lock_start; + u64 lock_length; unsigned int i; seq_printf(s, "name\t\t%s\n", info->name); @@ -159,6 +163,30 @@ static int spi_nor_params_show(struct seq_file *s, void *data) region[i].overlaid ? "yes" : "no"); } + if (!spi_nor_has_default_locking_ops(nor)) + return 0; + + seq_puts(s, "\nlocked sectors\n"); + seq_puts(s, " region (in hex) | status | #sectors\n"); + seq_puts(s, " ------------------+----------+---------\n"); + + spi_nor_get_locked_range_sr(nor, nor->dfs_sr_cache, &lock_start, &lock_length); + if (!lock_length || lock_length == params->size) { + seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, params->size - 1, + lock_length ? " locked" : "unlocked", + div_u64(params->size, min_prot_len)); + } else if (!lock_start) { + seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_length - 1, + " locked", div_u64(lock_length, min_prot_len)); + seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_length, params->size - 1, + "unlocked", div_u64(params->size - lock_length, min_prot_len)); + } else { + seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_start - 1, + "unlocked", div_u64(lock_start, min_prot_len)); + seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_start, params->size - 1, + " locked", div_u64(lock_length, min_prot_len)); + } + return 0; } DEFINE_SHOW_ATTRIBUTE(spi_nor_params); diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index cd37fec08c0e..2411d8f1012d 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -34,7 +34,7 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) return 0; } -static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) +u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) { unsigned int bp_slots, bp_slots_needed; /* @@ -55,8 +55,8 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) return sector_size; } -static void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, - u64 *len) +void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, + u64 *len) { u64 min_prot_len; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); @@ -114,7 +114,7 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, return (ofs >= lock_offs_max) || (offs_max <= lock_offs); } -static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr) +bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); } @@ -416,6 +416,11 @@ void spi_nor_init_default_locking_ops(struct spi_nor *nor) nor->params->locking_ops = &spi_nor_sr_locking_ops; } +bool spi_nor_has_default_locking_ops(struct spi_nor *nor) +{ + return nor->params->locking_ops == &spi_nor_sr_locking_ops; +} + static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, u64 len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); From f05f0d62c5c6b981315bd10017ec98504165e355 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:43 +0200 Subject: [PATCH 1738/5214] mtd: spi-nor: debugfs: Add a locked sectors map In order to get a very clear view of the sectors being locked, besides the `params` output giving the ranges, we may want to see a proper map of the sectors and for each of them, their status. Depending on the use case, this map may be easier to parse by humans and gives a more acurate feeling of the situation. At least myself, for the few locking-related developments I recently went through, I found it very useful to get a clearer mental model of what was locked/unlocked. Here is an example of output: $ cat /sys/kernel/debug/spi-nor/spi0.0/locked-sectors-map Locked sectors map (x: locked, .: unlocked, unit: 64kiB) 0x00000000 (# 0): ................ ................ ................ ................ 0x00400000 (# 64): ................ ................ ................ ................ 0x00800000 (# 128): ................ ................ ................ ................ 0x00c00000 (# 192): ................ ................ ................ ................ 0x01000000 (# 256): ................ ................ ................ ................ 0x01400000 (# 320): ................ ................ ................ ................ 0x01800000 (# 384): ................ ................ ................ ................ 0x01c00000 (# 448): ................ ................ ................ ................ 0x02000000 (# 512): ................ ................ ................ ................ 0x02400000 (# 576): ................ ................ ................ ................ 0x02800000 (# 640): ................ ................ ................ ................ 0x02c00000 (# 704): ................ ................ ................ ................ 0x03000000 (# 768): ................ ................ ................ ................ 0x03400000 (# 832): ................ ................ ................ ................ 0x03800000 (# 896): ................ ................ ................ ................ 0x03c00000 (# 960): ................ ................ ................ ..............xx The output is wrapped at 64 sectors, spaces every 16 sectors are improving the readability, every line starts by the first sector offset (hex) and number (decimal). Signed-off-by: Miquel Raynal [pratyush@kernel.org: split the debugfs_create_file() into two lines] Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/debugfs.c | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 82df8ad4176c..ef710bd4f307 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -191,6 +191,41 @@ static int spi_nor_params_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(spi_nor_params); +static int spi_nor_locked_sectors_map_show(struct seq_file *s, void *data) +{ + struct spi_nor *nor = s->private; + struct spi_nor_flash_parameter *params = nor->params; + u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); + unsigned int sector = 0; + u64 offset = 0; + bool locked; + int i; + + seq_printf(s, "Locked sectors map (x: locked, .: unlocked, unit: %lldkiB)\n", + min_prot_len / 1024); + while (offset < params->size) { + seq_printf(s, " 0x%08llx (#%5d): ", offset, sector); + for (i = 0; i < 64 && offset < params->size; i++) { + locked = spi_nor_is_locked_sr(nor, offset, min_prot_len, + nor->dfs_sr_cache); + if (locked) + seq_puts(s, "x"); + else + seq_puts(s, "."); + + if (((i + 1) % 16) == 0) + seq_puts(s, " "); + + offset += min_prot_len; + sector++; + } + seq_puts(s, "\n"); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(spi_nor_locked_sectors_map); + static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap, struct spi_nor_read_command *cmd) { @@ -276,6 +311,9 @@ void spi_nor_debugfs_register(struct spi_nor *nor) debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops); debugfs_create_file("capabilities", 0444, d, nor, &spi_nor_capabilities_fops); + if (spi_nor_has_default_locking_ops(nor)) + debugfs_create_file("locked-sectors-map", 0444, d, nor, + &spi_nor_locked_sectors_map_fops); } void spi_nor_debugfs_shutdown(void) From f13e900599089b10113ceb36013423f0837c6792 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 15:46:06 -0700 Subject: [PATCH 1739/5214] KVM: SEV: Pin source page for write when adding CPUID data for SNP guest When populating a guest_memfd instance with the initial CPUID data for an SNP guest, acquire a writable pin on the source page as KVM will write back the "correct" CPUID information if the userspace provided data is rejected by trusted firmware. Because KVM writes to the source page using a kernel mapping, pinning for read could result in KVM clobbering read-only memory. Note, well-behaved VMMs are unlikely to be affected, as CPUID information is almost always dynamically generated by userspace, i.e. it's unlikely for the CPUID information to be backed by a read-only mapping. Fixes: 2a62345b30529 ("KVM: guest_memfd: GUP source pages prior to populating guest memory") Cc: stable@vger.kernel.org Signed-off-by: Ackerley Tng Link: https://patch.msgid.link/20260522-fix-sev-gmem-post-populate-v2-1-3f196bfad5a1@google.com [sean: rewrite shortlog and changelog, tag for stable@] Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 1 + arch/x86/kvm/vmx/tdx.c | 2 +- include/linux/kvm_host.h | 3 ++- virt/kvm/guest_memfd.c | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 37d4cfa5d980..c73c028d72c1 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2456,6 +2456,7 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) sev_populate_args.type = params.type; count = kvm_gmem_populate(kvm, params.gfn_start, src, npages, + params.type == KVM_SEV_SNP_PAGE_TYPE_CPUID, sev_gmem_post_populate, &sev_populate_args); if (count < 0) { argp->error = sev_populate_args.fw_error; diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b8c3d3d8bbfe..00dcfcbc47f6 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -3185,7 +3185,7 @@ static int tdx_vcpu_init_mem_region(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *c }; gmem_ret = kvm_gmem_populate(kvm, gpa_to_gfn(region.gpa), u64_to_user_ptr(region.source_addr), - 1, tdx_gmem_post_populate, &arg); + 1, false, tdx_gmem_post_populate, &arg); if (gmem_ret < 0) { ret = gmem_ret; break; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 4c14aee1fb06..2c5ad9a6d5ce 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -2596,7 +2596,8 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord typedef int (*kvm_gmem_populate_cb)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, struct page *page, void *opaque); -long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages, +long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, + long npages, bool may_writeback_src, kvm_gmem_populate_cb post_populate, void *opaque); #endif diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 69c9d6d546b2..07d8db344872 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -858,7 +858,8 @@ static long __kvm_gmem_populate(struct kvm *kvm, struct kvm_memory_slot *slot, return ret; } -long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long npages, +long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, + long npages, bool may_writeback_src, kvm_gmem_populate_cb post_populate, void *opaque) { struct kvm_memory_slot *slot; @@ -892,8 +893,9 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long if (src) { unsigned long uaddr = (unsigned long)src + i * PAGE_SIZE; + unsigned int flags = may_writeback_src ? FOLL_WRITE : 0; - ret = get_user_pages_fast(uaddr, 1, 0, &src_page); + ret = get_user_pages_fast(uaddr, 1, flags, &src_page); if (ret < 0) break; if (ret != 1) { From 138f5f9cbe374ff87084f1d32181d2a4a2d924e8 Mon Sep 17 00:00:00 2001 From: Ackerley Tng Date: Fri, 22 May 2026 15:46:09 -0700 Subject: [PATCH 1740/5214] KVM: SEV: Unmap local kmaps in LIFO order, per highmem requirements Per highmem.h, local kernel mappings must be unmapped in the reserve order they were acquired, following a LIFO (last-in, first-out) stack-based approach, and that failure to do so "is invalid and causes malfunction". Swap the kunmap_local() calls in SNP post-populate flow to ensure the mappings are released in the correct order. Note, because SNP is 64-bit only, the bugs are benign as there are no highmem mappings to unwind. Fixes: 2a62345b3052 ("KVM: guest_memfd: GUP source pages prior to populating guest memory") Signed-off-by: Ackerley Tng Link: https://patch.msgid.link/20260522-fix-sev-gmem-post-populate-v2-4-3f196bfad5a1@google.com [sean: call out that the bug is benign] Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index c73c028d72c1..0e7f8b5cd4cb 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2347,8 +2347,8 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, memcpy(dst_vaddr, src_vaddr, PAGE_SIZE); - kunmap_local(src_vaddr); kunmap_local(dst_vaddr); + kunmap_local(src_vaddr); } ret = rmp_make_private(pfn, gfn << PAGE_SHIFT, PG_LEVEL_4K, @@ -2383,8 +2383,8 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, memcpy(src_vaddr, dst_vaddr, PAGE_SIZE); - kunmap_local(src_vaddr); kunmap_local(dst_vaddr); + kunmap_local(src_vaddr); } out: From 97cd21d57e9bd2da79845178d9250cfd19289cd4 Mon Sep 17 00:00:00 2001 From: Ackerley Tng Date: Fri, 22 May 2026 15:46:10 -0700 Subject: [PATCH 1741/5214] KVM: SEV: Mark source page dirty when writing back CPUID data on failure When writing back CPUID data (provided by trusted firmware) to the source page on failure, mark the page/folio as dirty so that the data isn't lost in the unlikely scenario the page is reclaimed before its read by userspace. Fixes: 2a62345b3052 ("KVM: guest_memfd: GUP source pages prior to populating guest memory") Signed-off-by: Ackerley Tng Link: https://patch.msgid.link/20260522-fix-sev-gmem-post-populate-v2-5-3f196bfad5a1@google.com [sean: use set_page_dirty(), massage changelog] Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0e7f8b5cd4cb..1d513778d88d 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2382,6 +2382,7 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, void *dst_vaddr = kmap_local_pfn(pfn); memcpy(src_vaddr, dst_vaddr, PAGE_SIZE); + set_page_dirty(src_page); kunmap_local(dst_vaddr); kunmap_local(src_vaddr); From 5a9c90350be4f6f175bdd193e8bd60a7aedfb4d2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 11 Jan 2026 16:55:28 +0000 Subject: [PATCH 1742/5214] iio: adc: ad7768-1: Select GPIOLIB This driver provides a gpio chip a some related functions are not stubbed if GPIOLIB is not built. ad7768-1.c:420: undefined reference to `gpiochip_get_data' ad7768-1.c:498: undefined reference to `devm_gpiochip_add_data_with_key' Dual sign off reflects change of affiliation whilst this was under review. Fixes: d569ae0f052e ("iio: adc: ad7768-1: Add GPIO controller support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512271235.WwAmAbOa-lkp@intel.com/ Signed-off-by: Jonathan Cameron Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2dcb9980d7c8..827aba88e7a2 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -415,6 +415,7 @@ config AD7766 config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI + select GPIOLIB select REGULATOR select REGMAP_SPI select RATIONAL From 6325d6e2204327965b849c0a16efb6ac9202e5a8 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 27 Apr 2026 19:11:39 +0800 Subject: [PATCH 1743/5214] iio: buffer: hw-consumer: free scan_mask on buffer release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scan_mask lifetime changed in commit 9a2e1233d38c ("iio: buffer: hw-consumer: remove redundant scan_mask flexible array"). Before that change, the scan mask storage was embedded in struct hw_consumer_buffer, so iio_hw_buf_release() could free the whole allocation with a single kfree(hw_buf). That commit moved the scan mask to a separate bitmap_zalloc() allocation stored in buffer.scan_mask, but left iio_hw_buf_release() unchanged. Free the scan mask in iio_hw_buf_release() before freeing the buffer wrapper. Fixes: 9a2e1233d38c ("iio: buffer: hw-consumer: remove redundant scan_mask flexible array") Signed-off-by: Felix Gu Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-hw-consumer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 700528c9a0a4..10e912bbf0c5 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -40,6 +40,8 @@ static void iio_hw_buf_release(struct iio_buffer *buffer) { struct hw_consumer_buffer *hw_buf = iio_buffer_to_hw_consumer_buffer(buffer); + + bitmap_free(buffer->scan_mask); kfree(hw_buf); } From 3c5eed894efd93d68d7f6a359a81ddef0e928774 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 17 May 2026 23:26:13 +0500 Subject: [PATCH 1744/5214] iio: temperature: tmp006: use devm_iio_trigger_register tmp006_probe() allocates the DRDY trigger with devm_iio_trigger_alloc() but registers it with plain iio_trigger_register(). The driver has no .remove() callback, so on module unload the trigger stays in the global trigger list while its memory is freed by devm, leaving a dangling entry. Switch to devm_iio_trigger_register() so the registration is undone in the same devm scope as the allocation. Fixes: 91f75ccf9f03 ("iio: temperature: tmp006: add triggered buffer support") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp006.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index d8d8c8936d17..bf62143fa719 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -350,7 +350,7 @@ static int tmp006_probe(struct i2c_client *client) data->drdy_trig->ops = &tmp006_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); - ret = iio_trigger_register(data->drdy_trig); + ret = devm_iio_trigger_register(&client->dev, data->drdy_trig); if (ret) return ret; From ee78fae068f52a5582aaf448d9414f826855c106 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:57 +0200 Subject: [PATCH 1745/5214] iio: light: al3010: read both ALS ADC registers again al3010_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3010.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 0932fa2b49fa..6af7b5f14a31 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -111,7 +111,8 @@ static int al3010_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3010_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -120,11 +121,12 @@ static int al3010_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3010_REG_DATA_LOW * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3010_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3010_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 744bccc2647c6b2206290e8e40890a23812116b3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:58 +0200 Subject: [PATCH 1746/5214] iio: light: al3320a: read both ALS ADC registers again al3320a_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3320a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 63f5a85912fc..b9076f434417 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -135,7 +135,8 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3320a_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -144,11 +145,12 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3320A_REG_DATA_LOW * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3320A_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3320A_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 929fec2964f71d4b1ac664ee963d8226c5cf01c6 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 21 May 2026 00:09:24 +0500 Subject: [PATCH 1747/5214] iio: adc: qcom-spmi-iadc: balance enable_irq_wake() on driver unbind iadc_probe() calls enable_irq_wake() after a successful devm_request_irq(), but the driver has no remove callback or matching disable_irq_wake(), so the wake reference count on the IRQ is leaked on module unload or driver unbind. Check the IRQ request error first, then register a devm action that calls disable_irq_wake() so the wake reference is released in the same scope as the enable. While here, drop the inverted "if (!ret) ... else return ret" in favour of the standard "if (ret) return ret;" pattern. Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-iadc.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c index b64a8a407168..0ec3a0c4b1de 100644 --- a/drivers/iio/adc/qcom-spmi-iadc.c +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -481,6 +481,11 @@ static const struct iio_chan_spec iadc_channels[] = { }, }; +static void iadc_disable_irq_wake(void *data) +{ + disable_irq_wake((unsigned long)data); +} + static int iadc_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -538,9 +543,16 @@ static int iadc_probe(struct platform_device *pdev) if (!iadc->poll_eoc) { ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0, "spmi-iadc", iadc); - if (!ret) - enable_irq_wake(irq_eoc); - else + if (ret) + return ret; + + ret = enable_irq_wake(irq_eoc); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, iadc_disable_irq_wake, + (void *)(unsigned long)irq_eoc); + if (ret) return ret; } else { ret = devm_device_init_wakeup(iadc->dev); From eaaa7eef181892ef7ba56c6295b81f0ae4492c13 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:15:46 +0300 Subject: [PATCH 1748/5214] iio: dac: ad3552r-hs: fix uninitialized data ni ad3552r_hs_write_data_source() If the *ppos value is non-zero then the simple_write_to_buffer() function won't initialized the start of the buf[] buffer. Non-zero values for *ppos won't work here generally, so just test for them and return -ENOSPC at the start of the function. Fixes: b1c5d68ea66e ("iio: dac: ad3552r-hs: add support for internal ramp") Signed-off-by: Dan Carpenter Reviewed-by: Angelo Dureghello Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3552r-hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index a9578afa7015..6bc64f53bce9 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -549,7 +549,7 @@ static ssize_t ad3552r_hs_write_data_source(struct file *f, guard(mutex)(&st->lock); - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From a6e8b14a4897d0b6df9744f33d0a30e6b92368eb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:11 +0300 Subject: [PATCH 1749/5214] iio: backend: fix uninitialized data in debugfs If the *ppos value is non-zero then simple_write_to_buffer() will not initialize the start of the buf[] buffer. Non-zero ppos values aren't going to work at all. Check for that at the start of the function and return -ENOSPC. Fixes: cdf01e0809a4 ("iio: backend: add debugFs interface") Signed-off-by: Dan Carpenter Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 10e689f49441..cd697fbeae47 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -156,7 +156,7 @@ static ssize_t iio_backend_debugfs_write_reg(struct file *file, ssize_t rc; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count); From ab92ed206d41fd171ebd37bc46360d9f2140d043 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:27 +0300 Subject: [PATCH 1750/5214] iio: core: fix uninitialized data in debugfs If *ppos is non-zero then simple_write_to_buffer() will not initialize the start of buf[]. Non zero values for *ppos aren't going to work anyway. Test for them at the start of the function and return -EINVAL. Fixes: 6d5dd486c715 ("iio: core: make use of simple_write_to_buffer()") Signed-off-by: Dan Carpenter Reviewed-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 22eefd048ba9..15b56f8972fc 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -418,7 +418,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, char buf[80]; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From 7ea987a905855d89073d172556720c93f95de93f Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 22 May 2026 10:21:49 -0700 Subject: [PATCH 1751/5214] KVM: guest_memfd: Return -EEXIST for overlapping bindings KVM_SET_USER_MEMORY_REGION2 rejects guest_memfd ranges that overlap an existing binding, but kvm_gmem_bind() currently reports the failure through its generic -EINVAL path. That makes binding conflicts indistinguishable from malformed guest_memfd parameters. Return -EEXIST when the target guest_memfd range is already bound, matching the errno used for overlapping GPA memslots and making the two types of range conflicts report the same class of error to userspace. Note, returning -EINVAL was definitely not intentional, as guest_memfd support was accompanied by a selftest to verify that attempting to create overlapping bindings fails with -EEXIST. Except the selftest was also flawed in that it unintentionally overlapped memslot GPAs, and so failed on KVM's common memslot checks before reaching guest_memfd. Fixes: a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory") Signed-off-by: Zongyao Chen Reviewed-by: Ackerley Tng Tested-by: Ackerley Tng [sean: call out that the original intent was to return -EEXIST] Link: https://patch.msgid.link/20260522172151.3530267-2-seanjc@google.com Signed-off-by: Sean Christopherson --- virt/kvm/guest_memfd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 69c9d6d546b2..46727539d08a 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -675,6 +675,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, if (!xa_empty(&f->bindings) && xa_find(&f->bindings, &start, end - 1, XA_PRESENT)) { + r = -EEXIST; filemap_invalidate_unlock(inode->i_mapping); goto err; } From 772989854acd17e6cc9b87c54595aba4ff002130 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 22 May 2026 10:21:50 -0700 Subject: [PATCH 1752/5214] KVM: selftests: Test guest_memfd binding overlap without GPA overlap The guest_memfd binding overlap test recreates the deleted slot with GPA ranges that overlap the still-live slot. KVM rejects those attempts from the generic memslot overlap check before reaching kvm_gmem_bind(), so the test can pass even if guest_memfd binding overlap detection is broken. Recreate the slot at its original, non-overlapping GPA and use guest_memfd offsets that overlap the front and back halves of the other slot's binding. Expand the guest_memfd so the back-half case remains within the file size. Fixes: 2feabb855df8 ("KVM: selftests: Expand set_memory_region_test to validate guest_memfd()") Signed-off-by: Zongyao Chen Reviewed-by: Ackerley Tng Tested-by: Ackerley Tng [sean: keep the existing GPA overlap testcases] Link: https://patch.msgid.link/20260522172151.3530267-3-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/set_memory_region_test.c | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 9b919a231c93..be99c1ff5a5a 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -510,7 +510,7 @@ static void test_add_overlapping_private_memory_regions(void) vm = vm_create_barebones_type(KVM_X86_SW_PROTECTED_VM); - memfd = vm_create_guest_memfd(vm, MEM_REGION_SIZE * 4, 0); + memfd = vm_create_guest_memfd(vm, MEM_REGION_SIZE * 5, 0); vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA, MEM_REGION_SIZE * 2, 0, memfd, 0); @@ -526,7 +526,30 @@ static void test_add_overlapping_private_memory_regions(void) vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA, 0, NULL, -1, 0); - /* Overlap the front half of the other slot. */ + /* + * Verify that overlap in the guest_memfd bindings (i.e. in guest_memfd + * file offsets), but _not_ in the GPA space, fails with -EEXIST. + */ + r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, + MEM_REGION_GPA, + MEM_REGION_SIZE * 2, + 0, memfd, MEM_REGION_SIZE); + TEST_ASSERT(r == -1 && errno == EEXIST, + "Overlapping guest_memfd() bindings should fail with EEXIST"); + + /* And now the back half of the other slot's guest_memfd binding. */ + r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, + MEM_REGION_GPA, + MEM_REGION_SIZE * 2, + 0, memfd, MEM_REGION_SIZE * 3); + TEST_ASSERT(r == -1 && errno == EEXIST, + "Overlapping guest_memfd() bindings should fail with EEXIST"); + + /* + * Repeat the overlap tests, but this time with overlap in the memslots + * GPA space. Regardless of where there is overlap, KVM should return + * -EEXIST. + */ r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA * 2 - MEM_REGION_SIZE, MEM_REGION_SIZE * 2, From b0e476eb755e44c1c066871187d695e6a45f4e27 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 10:21:51 -0700 Subject: [PATCH 1753/5214] KVM: selftests: Remove unnecessary "%s" formatting of a constant string Drop superfluous %s formatting from assertions in the guest_memfd overlap testcases, as the string being printed doesn't require runtime formatting. No functional change intended. Reported-by: Ackerley Tng Reviewed-by: Ackerley Tng Link: https://patch.msgid.link/20260522172151.3530267-4-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/set_memory_region_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index be99c1ff5a5a..e0800bfb11eb 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -554,7 +554,7 @@ static void test_add_overlapping_private_memory_regions(void) MEM_REGION_GPA * 2 - MEM_REGION_SIZE, MEM_REGION_SIZE * 2, 0, memfd, 0); - TEST_ASSERT(r == -1 && errno == EEXIST, "%s", + TEST_ASSERT(r == -1 && errno == EEXIST, "Overlapping guest_memfd() bindings should fail with EEXIST"); /* And now the back half of the other slot. */ @@ -562,7 +562,7 @@ static void test_add_overlapping_private_memory_regions(void) MEM_REGION_GPA * 2 + MEM_REGION_SIZE, MEM_REGION_SIZE * 2, 0, memfd, 0); - TEST_ASSERT(r == -1 && errno == EEXIST, "%s", + TEST_ASSERT(r == -1 && errno == EEXIST, "Overlapping guest_memfd() bindings should fail with EEXIST"); close(memfd); From 849d65e27bd62bca9a9383f7dfa14da711f4bebc Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 10:15:34 -0700 Subject: [PATCH 1754/5214] KVM: selftests: Cast guest_memfd fd to a signed int when checking for >= 0 When conditionally closing a memory region's guest_memfd file descriptor, cast the field to a signed it so that negative values are correctly detected. Because selftests reuse "struct kvm_userspace_memory_region2" instead of providing custom storage, they pick up the kernel uAPI's __u32 definition of the file descriptor, not the more common "int" definition, e.g. that's used for userspace_mem_region.fd. Fixes: bb2968ad6c33 ("KVM: selftests: Add support for creating private memslots") Reported-by: Bibo Mao Closes: https://lore.kernel.org/all/20260508015013.4108345-1-maobibo@loongson.cn Reviewed-by: Bibo Mao Reviewed-by: Ackerley Tng Link: https://patch.msgid.link/20260522171535.3525890-2-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/lib/kvm_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 2a76eca7029d..464ac07e86b3 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -817,7 +817,7 @@ static void __vm_mem_region_delete(struct kvm_vm *vm, kvm_munmap(region->mmap_alias, region->mmap_size); close(region->fd); } - if (region->region.guest_memfd >= 0) + if ((int)region->region.guest_memfd >= 0) close(region->region.guest_memfd); free(region); From 3e8a0b9912239026defe839291c669984c734650 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 10:15:35 -0700 Subject: [PATCH 1755/5214] KVM: selftests: Add and use kvm_free_fd() to harden against fd goofs Add a kvm_free_fd() macro to close and invalidate a file descriptor, and use it through the core infrastructure to harden against goofs where a selftest attempts to reuse a closed file descriptor. Cc: Bibo Mao Cc: Fuad Tabba Cc: Ackerley Tng Reviewed-by: Ackerley Tng Link: https://patch.msgid.link/20260522171535.3525890-3-seanjc@google.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/include/kvm_syscalls.h | 6 +++++ tools/testing/selftests/kvm/lib/kvm_util.c | 23 +++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h index 843c9904c46f..e0ef13fcb3c7 100644 --- a/tools/testing/selftests/kvm/include/kvm_syscalls.h +++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h @@ -79,4 +79,10 @@ __KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, l __KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length); __KVM_SYSCALL_DEFINE(madvise, 3, void *, addr, size_t, length, int, advice); +#define kvm_free_fd(fd) \ +do { \ + kvm_close(fd); \ + (fd) = -1; \ +} while (0) + #endif /* SELFTEST_KVM_SYSCALLS_H */ diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 464ac07e86b3..534bf849386c 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -77,7 +77,8 @@ static ssize_t get_module_param(const char *module_name, const char *param, int fd, r; /* Verify KVM is loaded, to provide a more helpful SKIP message. */ - close(open_kvm_dev_path_or_exit()); + fd = open_kvm_dev_path_or_exit(); + kvm_free_fd(fd); r = snprintf(path, path_size, "/sys/module/%s/parameters/%s", module_name, param); @@ -90,8 +91,7 @@ static ssize_t get_module_param(const char *module_name, const char *param, TEST_ASSERT(bytes_read > 0, "read(%s) returned %ld, wanted %ld bytes", path, bytes_read, buffer_size); - r = close(fd); - TEST_ASSERT(!r, "close(%s) failed", path); + kvm_free_fd(fd); return bytes_read; } @@ -160,7 +160,7 @@ unsigned int kvm_check_cap(long cap) ret = __kvm_ioctl(kvm_fd, KVM_CHECK_EXTENSION, (void *)cap); TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_CHECK_EXTENSION, ret)); - close(kvm_fd); + kvm_free_fd(kvm_fd); return (unsigned int)ret; } @@ -747,8 +747,7 @@ static void kvm_stats_release(struct kvm_binary_stats *stats) stats->desc = NULL; } - kvm_close(stats->fd); - stats->fd = -1; + kvm_free_fd(stats->fd); } __weak void vcpu_arch_free(struct kvm_vcpu *vcpu) @@ -777,7 +776,7 @@ static void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) kvm_munmap(vcpu->run, vcpu_mmap_sz()); - kvm_close(vcpu->fd); + kvm_free_fd(vcpu->fd); kvm_stats_release(&vcpu->stats); list_del(&vcpu->list); @@ -793,8 +792,8 @@ void kvm_vm_release(struct kvm_vm *vmp) list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list) vm_vcpu_rm(vmp, vcpu); - kvm_close(vmp->fd); - kvm_close(vmp->kvm_fd); + kvm_free_fd(vmp->fd); + kvm_free_fd(vmp->kvm_fd); /* Free cached stats metadata and close FD */ kvm_stats_release(&vmp->stats); @@ -815,10 +814,10 @@ static void __vm_mem_region_delete(struct kvm_vm *vm, if (region->fd >= 0) { /* There's an extra map when using shared memory. */ kvm_munmap(region->mmap_alias, region->mmap_size); - close(region->fd); + kvm_free_fd(region->fd); } if ((int)region->region.guest_memfd >= 0) - close(region->region.guest_memfd); + kvm_free_fd(region->region.guest_memfd); free(region); } @@ -1311,7 +1310,7 @@ static size_t vcpu_mmap_sz(void) TEST_ASSERT(ret >= 0 && ret >= sizeof(struct kvm_run), KVM_IOCTL_ERROR(KVM_GET_VCPU_MMAP_SIZE, ret)); - close(dev_fd); + kvm_free_fd(dev_fd); return ret; } From 4e0fdd9b0d7d22bdde676fce10ffc22e7f4933ed Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 10:02:30 -0700 Subject: [PATCH 1756/5214] KVM: selftests: Randomize dirty_log_test's delay before reaping the bitmap In the dirty log test, randomize the delay before the initial call to get the dirty log bitmap for a given iteration, so that the amount of memory dirtied by the guest varies from iteration to iteration, and so that the user can effectively control the duration (by increasing the interval). Always waiting 1ms effectively hides a KVM RISC-V bug as the test reaps the dirty bitmap before the guest has a chance to trigger the problematic flow in KVM. Reported-by: Wu Fei Closes: https://lore.kernel.org/all/202605111130.64BBUXDN013040@mse-fl2.zte.com.cn Cc: Wu Fei Link: https://patch.msgid.link/20260522170230.3518669-1-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/dirty_log_test.c | 26 +++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c index 12446a4b6e8d..74ca096bf976 100644 --- a/tools/testing/selftests/kvm/dirty_log_test.c +++ b/tools/testing/selftests/kvm/dirty_log_test.c @@ -694,7 +694,17 @@ static void run_test(enum vm_guest_mode mode, void *arg) pthread_create(&vcpu_thread, NULL, vcpu_worker, vcpu); for (iteration = 1; iteration <= p->iterations; iteration++) { - unsigned long i; + unsigned long i, reap_i; + + /* + * Select a random point in the time interval to reap the dirty + * bitmap/ring while the guest is running, i.e. randomize how + * long the guest gets to initially run and thus how many pages + * it can dirty, before collecting the dirty bitmap/ring. See + * the loop below for details. + */ + reap_i = random() % p->interval; + printf("Reaping after a %lu ms delay\n", reap_i); sync_global_to_guest(vm, iteration); @@ -729,13 +739,17 @@ static void run_test(enum vm_guest_mode mode, void *arg) * that's effectively blocked. Collecting while the * guest is running also verifies KVM doesn't lose any * state. - * + */ + if (i < reap_i) + continue; + + /* * For bitmap modes, KVM overwrites the entire bitmap, * i.e. collecting the bitmaps is destructive. Collect - * the bitmap only on the first pass, otherwise this - * test would lose track of dirty pages. + * the bitmap while the guest is running only once, + * otherwise this test would lose track of dirty pages. */ - if (i && host_log_mode != LOG_MODE_DIRTY_RING) + if (i > reap_i && host_log_mode != LOG_MODE_DIRTY_RING) continue; /* @@ -745,7 +759,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) * the ring on every pass would make it unlikely the * vCPU would ever fill the fing). */ - if (i && !READ_ONCE(dirty_ring_vcpu_ring_full)) + if (i > reap_i && !READ_ONCE(dirty_ring_vcpu_ring_full)) continue; log_mode_collect_dirty_pages(vcpu, TEST_MEM_SLOT_INDEX, From 5804f58901af77ab507e199d31dfa263404e177c Mon Sep 17 00:00:00 2001 From: Jiun Jeong Date: Fri, 1 May 2026 23:44:13 +0900 Subject: [PATCH 1757/5214] call_once:: Fix typo in comment for call_once() Change "succesfully" to "successfully" in the kerneldoc comment of call_once(). Signed-off-by: Jiun Jeong Link: https://patch.msgid.link/20260501144413.49419-1-jiun.jeong.cs@gmail.com [sean: don't scope to KVM, massage changelog] Signed-off-by: Sean Christopherson --- include/linux/call_once.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/call_once.h b/include/linux/call_once.h index 13cd6469e7e5..1625a9d6ff5b 100644 --- a/include/linux/call_once.h +++ b/include/linux/call_once.h @@ -36,7 +36,7 @@ do { \ * it returns a zero or positive value, mark @once as completed. Return * the value returned by @cb * - * If @once has completed succesfully before, return 0. + * If @once has completed successfully before, return 0. * * The call to @cb is implicitly surrounded by a mutex, though for * efficiency the * function avoids taking it after the first call. From 5bd0387e3b56f7e0a81386f6d0a5fa2c3a92ad5d Mon Sep 17 00:00:00 2001 From: Piotr Zarycki Date: Sat, 23 May 2026 13:18:57 +0200 Subject: [PATCH 1758/5214] KVM: selftests: hyperv_features: test write of 1 to HV_X64_MSR_RESET Writing 1 to HV_X64_MSR_RESET triggers a real vCPU reset; the test was writing 0 because the host loop was not prepared to handle the resulting KVM_EXIT_SYSTEM_EVENT. Add the missing handling and write 1 to actually exercise the reset path. Signed-off-by: Piotr Zarycki Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/20260523111857.195396-1-piotr.zarycki@gmail.com Signed-off-by: Sean Christopherson --- .../selftests/kvm/x86/hyperv_features.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/hyperv_features.c b/tools/testing/selftests/kvm/x86/hyperv_features.c index 5053a4454811..2effde85c4c8 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_features.c +++ b/tools/testing/selftests/kvm/x86/hyperv_features.c @@ -26,6 +26,7 @@ struct msr_data { bool fault_expected; bool write; u64 write_val; + bool reset_expected; }; struct hcall_data { @@ -267,14 +268,9 @@ static void guest_test_msrs_access(void) case 16: msr->idx = HV_X64_MSR_RESET; msr->write = true; - /* - * TODO: the test only writes '0' to HV_X64_MSR_RESET - * at the moment, writing some other value there will - * trigger real vCPU reset and the code is not prepared - * to handle it yet. - */ - msr->write_val = 0; + msr->write_val = 1; msr->fault_expected = false; + msr->reset_expected = true; break; case 17: @@ -497,6 +493,15 @@ static void guest_test_msrs_access(void) msr->idx, msr->write ? "write" : "read"); vcpu_run(vcpu); + + if (msr->reset_expected) { + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SYSTEM_EVENT); + TEST_ASSERT(vcpu->run->system_event.type == KVM_SYSTEM_EVENT_RESET, + "Expected reset system event, got type %u", + vcpu->run->system_event.type); + goto next_stage; + } + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); switch (get_ucall(vcpu, &uc)) { From bb24edbb673f6f3d72ad347cfc107e03d1c6792a Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Fri, 22 May 2026 16:26:57 -0700 Subject: [PATCH 1759/5214] KVM: x86: Widen x86_exception's error_code to 64 bits Widen the error_code field in struct x86_exception from u16 to u64 to accommodate AMD's NPF error code, which defines information bits above bit 31, e.g. PFERR_GUEST_FINAL_MASK (bit 32), and PFERR_GUEST_PAGE_MASK (bit 33). Retain the u16 type for the local errcode variable in walk_addr_generic as the walker synthesizes conventional #PF error codes that are architecturally limited to bits 15:0. Signed-off-by: Kevin Cheng Link: https://patch.msgid.link/20260522232701.3671446-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/kvm_emulate.h | 2 +- arch/x86/kvm/mmu/paging_tmpl.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index 72aece9ef575..f5df31a52996 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -22,7 +22,7 @@ enum x86_intercept_stage; struct x86_exception { u8 vector; bool error_code_valid; - u16 error_code; + u64 error_code; bool nested_page_fault; union { u64 address; /* cr2 or nested page fault gpa */ diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 07100bbfc270..51f8b4522314 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -328,6 +328,12 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, const int write_fault = access & PFERR_WRITE_MASK; const int user_fault = access & PFERR_USER_MASK; const int fetch_fault = access & PFERR_FETCH_MASK; + /* + * Note! Track the error_code that's common to legacy shadow paging + * and NPT shadow paging as a u16 to guard against unintentionally + * setting any of bits 63:16. Architecturally, the #PF error code is + * 32 bits, and Intel CPUs don't support settings bits 31:16. + */ u16 errcode = 0; gpa_t real_gpa; gfn_t gfn; From 6ad0badd765ce6c7ddf2c70ac3b26882069a40c9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 30 Apr 2026 09:49:29 +0800 Subject: [PATCH 1760/5214] x86/tdx: Use PFN directly for mapping guest private memory Remove struct page assumptions/constraints in the SEAMCALL wrapper APIs for mapping guest private memory and have them take PFN directly. Having core TDX make assumptions that guest private memory must be backed by struct page (and/or folio) will create subtle dependencies on how KVM/guest_memfd allocates/manages memory (e.g., whether it uses memory allocated from core MM, if the memory is refcounted, or if the folio is split) that are easily avoided. [1]. KVM's MMUs work with PFNs. This is very much an intentional design choice. It ensures that the KVM MMUs remain flexible and are not too tied to the regular CPU MMUs and the kernel code around them. Using 'struct page' for TDX guest memory is not a good fit anywhere near the KVM MMU code [2]. Use "kvm_pfn_t pfn" for type safety. Using this KVM type is appropriate since APIs tdh_mem_page_add() and tdh_mem_page_aug() are exported to KVM only. [ Yan: Replace "u64 pfn" with "kvm_pfn_t pfn" ] Signed-off-by: Yan Zhao Link: https://lore.kernel.org/all/aWgyhmTJphGQqO0Y@google.com [1] Link: https://lore.kernel.org/all/ac7V0g2q2hN3dU5u@google.com [2] Acked-by: Kiryl Shutsemau Reviewed-by: Xiaoyao Li Reviewed-by: Ackerley Tng Acked-by: Dave Hansen Link: https://patch.msgid.link/20260430014929.24210-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/tdx.h | 7 +++++-- arch/x86/kvm/vmx/tdx.c | 7 +++---- arch/x86/virt/vmx/tdx/tdx.c | 19 ++++++++++++------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index c140ddde59ff..648d350616e4 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -189,10 +190,12 @@ static inline u64 mk_keyed_paddr(u16 hkid, struct page *page) u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, kvm_pfn_t pfn, struct page *source, + u64 *ext_err1, u64 *ext_err2); u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, enum pg_level level, struct page *page, u64 *ext_err1, u64 *ext_err2); u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, enum pg_level level, struct page *page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, enum pg_level level, kvm_pfn_t pfn, + u64 *ext_err1, u64 *ext_err2); u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, enum pg_level level, u64 *ext_err1, u64 *ext_err2); u64 tdh_mng_key_config(struct tdx_td *td); u64 tdh_mng_create(struct tdx_td *td, u16 hkid); diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 72d916c957bc..43bb9c492a4e 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1624,8 +1624,8 @@ static int tdx_mem_page_add(struct kvm *kvm, gfn_t gfn, enum pg_level level, KVM_BUG_ON(!kvm_tdx->page_add_src, kvm)) return -EIO; - err = tdh_mem_page_add(&kvm_tdx->td, gpa, pfn_to_page(pfn), - kvm_tdx->page_add_src, &entry, &level_state); + err = tdh_mem_page_add(&kvm_tdx->td, gpa, pfn, kvm_tdx->page_add_src, + &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1639,12 +1639,11 @@ static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, enum pg_level level, kvm_pfn_t pfn) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - struct page *page = pfn_to_page(pfn); gpa_t gpa = gfn_to_gpa(gfn); u64 entry, level_state; u64 err; - err = tdh_mem_page_aug(&kvm_tdx->td, gpa, level, page, &entry, &level_state); + err = tdh_mem_page_aug(&kvm_tdx->td, gpa, level, pfn, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index a6e77afafa79..b24b81cea5ea 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -1568,6 +1567,11 @@ static void tdx_clflush_page(struct page *page) clflush_cache_range(page_to_virt(page), PAGE_SIZE); } +static void tdx_clflush_pfn(kvm_pfn_t pfn) +{ + clflush_cache_range(__va(PFN_PHYS(pfn)), PAGE_SIZE); +} + static int pg_level_to_tdx_sept_level(enum pg_level level) { WARN_ON_ONCE(level == PG_LEVEL_NONE); @@ -1594,17 +1598,18 @@ u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page) } EXPORT_SYMBOL_FOR_KVM(tdh_mng_addcx); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, kvm_pfn_t pfn, struct page *source, + u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = PFN_PHYS(pfn), .r9 = page_to_phys(source), }; u64 ret; - tdx_clflush_page(page); + tdx_clflush_pfn(pfn); ret = seamcall_ret(TDH_MEM_PAGE_ADD, &args); *ext_err1 = args.rcx; @@ -1647,16 +1652,16 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) EXPORT_SYMBOL_FOR_KVM(tdh_vp_addcx); u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, enum pg_level level, - struct page *page, u64 *ext_err1, u64 *ext_err2) + kvm_pfn_t pfn, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa | pg_level_to_tdx_sept_level(level), .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = PFN_PHYS(pfn), }; u64 ret; - tdx_clflush_page(page); + tdx_clflush_pfn(pfn); ret = seamcall_ret(TDH_MEM_PAGE_AUG, &args); *ext_err1 = args.rcx; From 4c7a1247646c46f6ab906167a5f6d5577ea63472 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 30 Apr 2026 09:49:48 +0800 Subject: [PATCH 1761/5214] x86/tdx: Use PFN directly for unmapping guest private memory Remove struct page assumptions/constraints in APIs for unmapping guest private memory and have them take physical address directly. Having core TDX make assumptions that guest private memory must be backed by struct page (and/or folio) will create subtle dependencies on how KVM/guest_memfd allocates/manages memory (e.g., whether it uses memory allocated from core MM, if the memory is refcounted, or if the folio is split) that are easily avoided. [1]. KVM's MMUs work with PFNs. This is very much an intentional design choice. It ensures that the KVM MMUs remain flexible and are not too tightly tied to the regular CPU MMUs and the kernel code around them. Using "struct page" for TDX guest memory is not a good fit anywhere near the KVM MMU code [2]. Therefore, for unmapping guest private memory: export tdx_quirk_reset_paddr() for direct KVM invocation, and convert the SEAMCALL wrapper API tdh_phymem_page_wbinvd_hkid() to take PFN as input (thus updating mk_keyed_paddr() and tdh_phymem_page_wbinvd_tdr()). Intentionally have KVM pass PAGE_SIZE (rather than KVM_HPAGE_SIZE(level)) to tdx_quirk_reset_paddr() in tdx_sept_remove_private_spte() to avoid mixing in huge page changes. The KVM_BUG_ON() check for !PG_LEVEL_4K in tdx_sept_remove_private_spte() justifies using PAGE_SIZE. Do not convert tdx_reclaim_page() to use PFN as input since it currently does not remove guest private memory. Use "kvm_pfn_t pfn" for type safety. Using this KVM type is appropriate since APIs tdh_phymem_page_wbinvd_hkid() and tdx_quirk_reset_paddr() are exported to KVM only. [Yan: Use kvm_pfn_t,exclude tdx_reclaim_page(),use tdx_quirk_reset_paddr()] Signed-off-by: Yan Zhao Link: https://lore.kernel.org/all/aWgyhmTJphGQqO0Y@google.com [1] Link: https://lore.kernel.org/all/ac7V0g2q2hN3dU5u@google.com [2] Acked-by: Kiryl Shutsemau Reviewed-by: Xiaoyao Li Reviewed-by: Ackerley Tng Acked-by: Dave Hansen Link: https://patch.msgid.link/20260430014948.24226-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/tdx.h | 14 +++++--------- arch/x86/kvm/vmx/tdx.c | 6 +++--- arch/x86/virt/vmx/tdx/tdx.c | 9 +++++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 648d350616e4..046060ef44c7 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -154,6 +154,7 @@ u32 tdx_get_nr_guest_keyids(void); void tdx_guest_keyid_free(unsigned int keyid); void tdx_quirk_reset_page(struct page *page); +void tdx_quirk_reset_paddr(unsigned long base, unsigned long size); struct tdx_td { /* TD root structure: */ @@ -177,15 +178,10 @@ struct tdx_vp { struct page **tdcx_pages; }; -static inline u64 mk_keyed_paddr(u16 hkid, struct page *page) +static inline u64 mk_keyed_paddr(u16 hkid, kvm_pfn_t pfn) { - u64 ret; - - ret = page_to_phys(page); - /* KeyID bits are just above the physical address bits: */ - ret |= (u64)hkid << boot_cpu_data.x86_phys_bits; - - return ret; + /* KeyID bits are just above the physical address bits. */ + return PFN_PHYS(pfn) | ((u64)hkid << boot_cpu_data.x86_phys_bits); } u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); @@ -215,7 +211,7 @@ u64 tdh_mem_track(struct tdx_td *tdr); u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, enum pg_level level, u64 *ext_err1, u64 *ext_err2); u64 tdh_phymem_cache_wb(bool resume); u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, kvm_pfn_t pfn); #else static inline void tdx_init(void) { } static inline u32 tdx_get_nr_guest_keyids(void) { return 0; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 43bb9c492a4e..d8ed1d3b7252 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1774,8 +1774,8 @@ static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { - struct page *page = pfn_to_page(spte_to_pfn(mirror_spte)); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); + kvm_pfn_t pfn = spte_to_pfn(mirror_spte); gpa_t gpa = gfn_to_gpa(gfn); u64 err, entry, level_state; @@ -1814,11 +1814,11 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, if (TDX_BUG_ON_2(err, TDH_MEM_PAGE_REMOVE, entry, level_state, kvm)) return; - err = tdh_phymem_page_wbinvd_hkid((u16)kvm_tdx->hkid, page); + err = tdh_phymem_page_wbinvd_hkid((u16)kvm_tdx->hkid, pfn); if (TDX_BUG_ON(err, TDH_PHYMEM_PAGE_WBINVD, kvm)) return; - tdx_quirk_reset_page(page); + tdx_quirk_reset_paddr(PFN_PHYS(pfn), PAGE_SIZE); } void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index b24b81cea5ea..e5a37ea2d4a0 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -710,7 +710,7 @@ static __init int tdmrs_set_up_pamt_all(struct tdmr_info_list *tdmr_list, * to normal kernel memory. Systems with the X86_BUG_TDX_PW_MCE erratum need to * do the conversion explicitly via MOVDIR64B. */ -static void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) +void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) { const void *zero_page = (const void *)page_address(ZERO_PAGE(0)); unsigned long phys, end; @@ -729,6 +729,7 @@ static void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) */ mb(); } +EXPORT_SYMBOL_FOR_KVM(tdx_quirk_reset_paddr); void tdx_quirk_reset_page(struct page *page) { @@ -1920,17 +1921,17 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td) { struct tdx_module_args args = {}; - args.rcx = mk_keyed_paddr(tdx_global_keyid, td->tdr_page); + args.rcx = mk_keyed_paddr(tdx_global_keyid, page_to_pfn(td->tdr_page)); return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args); } EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_tdr); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page) +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, kvm_pfn_t pfn) { struct tdx_module_args args = {}; - args.rcx = mk_keyed_paddr(hkid, page); + args.rcx = mk_keyed_paddr(hkid, pfn); return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args); } From 4a72a6dc447d697441f578bb144d1add3ea8f326 Mon Sep 17 00:00:00 2001 From: Yan Zhao Date: Thu, 30 Apr 2026 09:50:01 +0800 Subject: [PATCH 1762/5214] x86/tdx: Drop exported function tdx_quirk_reset_page() KVM invokes tdx_quirk_reset_page() to reset TDX control pages (including S-EPT pages, TDR page, etc.), as all those pages are allocated by KVM TDX and thus always have struct page. However, it's also reasonable for KVM to reset those TDX control pages via tdx_quirk_reset_paddr() directly, eliminating the need to export two parallel APIs. Keeping tdx_quirk_reset_page() as a one-line helper in the header file is also unnecessary. No functional change intended. Suggested-by: Paolo Bonzini Suggested-by: Xiaoyao Li Signed-off-by: Yan Zhao Acked-by: Kiryl Shutsemau Reviewed-by: Xiaoyao Li Reviewed-by: Ackerley Tng Acked-by: Dave Hansen Link: https://patch.msgid.link/20260430015001.24242-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/tdx.h | 1 - arch/x86/kvm/vmx/tdx.c | 4 ++-- arch/x86/virt/vmx/tdx/tdx.c | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 046060ef44c7..1a83ef88af17 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -153,7 +153,6 @@ int tdx_guest_keyid_alloc(void); u32 tdx_get_nr_guest_keyids(void); void tdx_guest_keyid_free(unsigned int keyid); -void tdx_quirk_reset_page(struct page *page); void tdx_quirk_reset_paddr(unsigned long base, unsigned long size); struct tdx_td { diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index d8ed1d3b7252..3f956dde4a51 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -343,7 +343,7 @@ static int tdx_reclaim_page(struct page *page) r = __tdx_reclaim_page(page); if (!r) - tdx_quirk_reset_page(page); + tdx_quirk_reset_paddr(page_to_phys(page), PAGE_SIZE); return r; } @@ -597,7 +597,7 @@ static void tdx_reclaim_td_control_pages(struct kvm *kvm) if (TDX_BUG_ON(err, TDH_PHYMEM_PAGE_WBINVD, kvm)) return; - tdx_quirk_reset_page(kvm_tdx->td.tdr_page); + tdx_quirk_reset_paddr(page_to_phys(kvm_tdx->td.tdr_page), PAGE_SIZE); __free_page(kvm_tdx->td.tdr_page); kvm_tdx->td.tdr_page = NULL; diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index e5a37ea2d4a0..deb67e68f85f 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -731,12 +731,6 @@ void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) } EXPORT_SYMBOL_FOR_KVM(tdx_quirk_reset_paddr); -void tdx_quirk_reset_page(struct page *page) -{ - tdx_quirk_reset_paddr(page_to_phys(page), PAGE_SIZE); -} -EXPORT_SYMBOL_FOR_KVM(tdx_quirk_reset_page); - static __init void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr) { From 3f330fbb918fbbfb5b50b190fff8a13d7810b6f0 Mon Sep 17 00:00:00 2001 From: Yan Zhao Date: Thu, 30 Apr 2026 09:50:14 +0800 Subject: [PATCH 1763/5214] x86/virt/tdx: Move mk_keyed_paddr() to tdx.c due to no external users Move mk_keyed_paddr() from tdx.h to tdx.c to avoid unnecessary header inclusion and improve encapsulation since there are no users outside of tdx.c. No functional change intended. Signed-off-by: Yan Zhao Acked-by: Kiryl Shutsemau Reviewed-by: Xiaoyao Li Acked-by: Dave Hansen Link: https://patch.msgid.link/20260430015014.24261-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/tdx.h | 6 ------ arch/x86/virt/vmx/tdx/tdx.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 1a83ef88af17..32fbdf8f55ae 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -177,12 +177,6 @@ struct tdx_vp { struct page **tdcx_pages; }; -static inline u64 mk_keyed_paddr(u16 hkid, kvm_pfn_t pfn) -{ - /* KeyID bits are just above the physical address bits. */ - return PFN_PHYS(pfn) | ((u64)hkid << boot_cpu_data.x86_phys_bits); -} - u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page); u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, kvm_pfn_t pfn, struct page *source, diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index deb67e68f85f..967482ae3c80 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -1911,6 +1911,12 @@ u64 tdh_phymem_cache_wb(bool resume) } EXPORT_SYMBOL_FOR_KVM(tdh_phymem_cache_wb); +static inline u64 mk_keyed_paddr(u16 hkid, kvm_pfn_t pfn) +{ + /* KeyID bits are just above the physical address bits. */ + return PFN_PHYS(pfn) | ((u64)hkid << boot_cpu_data.x86_phys_bits); +} + u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td) { struct tdx_module_args args = {}; From fe0b872d750023eda270cbc01c53ead52b1049d8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 22 May 2026 16:26:58 -0700 Subject: [PATCH 1764/5214] KVM: x86: Tell ->inject_page_fault() whether or a fault came from hardware When injecting a page fault (including nested TDP faults into L1), tell the injection routine whether or not the fault originated in hardware, i.e. if KVM is effectively forwarding a fault it intercept. For nested TDP fault injection, KVM needs to grab PAGE_WALK vs. GUEST_FINAL information from the VMCB/VMCS, _if_ the fault originated in hardware. Note, simply checking whether or not the original exit was due a #NPF or EPT Violation isn't sufficient/correct, as the fault being synthesized for L1 may or may not be the "same" fault that triggered a VM-Exit from L2. E.g. if access to emulated MMIO in L2 hits a !PRESENT fault (EPT Violation or #NPF), e.g. because MMIO caching is disabled or it's the first time the GPA has been accessed by L2, then KVM will enter the emulator. If emulating the MMIO instruction then hits a nested TDP fault, e.g. because L2 was accessing MMIO with a MOVSQ (memory-to-memory move), or because L1 has since unmapped the code stream, then the TDP fault synthesized to L1 will not be the same emulated fault the triggered the VM-Exit. No functional change intended (nothing uses the new param, yet...). Link: https://patch.msgid.link/20260522232701.3671446-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 18 ++++++++++++++---- arch/x86/kvm/mmu/paging_tmpl.h | 2 +- arch/x86/kvm/svm/nested.c | 3 ++- arch/x86/kvm/vmx/nested.c | 3 ++- arch/x86/kvm/x86.c | 16 +++++++++------- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 2b986a733cd6..29fec7c59a6f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -484,7 +484,8 @@ struct kvm_mmu { u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index); int (*page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault); void (*inject_page_fault)(struct kvm_vcpu *vcpu, - struct x86_exception *fault); + struct x86_exception *fault, + bool from_hardware); gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, gpa_t gva_or_gpa, u64 access, struct x86_exception *exception); @@ -2304,9 +2305,18 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, bool has_error_code, u32 error_code); -void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); -void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault); +void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault, + bool from_hardware); +void __kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault, + bool from_hardware); + +static inline void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault) +{ + __kvm_inject_emulated_page_fault(vcpu, fault, false); +} + bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl); bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 51f8b4522314..cc9c7deb34bc 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -813,7 +813,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault */ if (!r) { if (!fault->prefetch) - kvm_inject_emulated_page_fault(vcpu, &walker.fault); + __kvm_inject_emulated_page_fault(vcpu, &walker.fault, true); return RET_PF_RETRY; } diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 3d1fd1776e19..edb15f9c6403 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -34,7 +34,8 @@ #define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu, - struct x86_exception *fault) + struct x86_exception *fault, + bool from_hardware) { struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb = svm->vmcb; diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 4690a4d23709..3bb7eaa7b2a5 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -411,7 +411,8 @@ static void nested_ept_invalidate_addr(struct kvm_vcpu *vcpu, gpa_t eptp, } static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault) + struct x86_exception *fault, + bool from_hardware) { struct vmcs12 *vmcs12 = get_vmcs12(vcpu); struct vcpu_vmx *vmx = to_vmx(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 9c52c11ba5f5..93958df30230 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -969,7 +969,8 @@ static int complete_emulated_insn_gp(struct kvm_vcpu *vcpu, int err) EMULTYPE_COMPLETE_USER_EXIT); } -void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault) +void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault, + bool from_hardware) { ++vcpu->stat.pf_guest; @@ -986,8 +987,9 @@ void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault) fault->address); } -void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault) +void __kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault, + bool from_hardware) { struct kvm_mmu *fault_mmu; WARN_ON_ONCE(fault->vector != PF_VECTOR); @@ -1004,9 +1006,9 @@ void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, kvm_mmu_invalidate_addr(vcpu, fault_mmu, fault->address, KVM_MMU_ROOT_CURRENT); - fault_mmu->inject_page_fault(vcpu, fault); + fault_mmu->inject_page_fault(vcpu, fault, from_hardware); } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_inject_emulated_page_fault); +EXPORT_SYMBOL_FOR_KVM_INTERNAL(__kvm_inject_emulated_page_fault); void kvm_inject_nmi(struct kvm_vcpu *vcpu) { @@ -14058,7 +14060,7 @@ bool kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu, fault.nested_page_fault = false; fault.address = work->arch.token; fault.async_page_fault = true; - kvm_inject_page_fault(vcpu, &fault); + kvm_inject_page_fault(vcpu, &fault, false); return true; } else { /* @@ -14229,7 +14231,7 @@ void kvm_fixup_and_inject_pf_error(struct kvm_vcpu *vcpu, gva_t gva, u16 error_c fault.address = gva; fault.async_page_fault = false; } - vcpu->arch.walk_mmu->inject_page_fault(vcpu, &fault); + vcpu->arch.walk_mmu->inject_page_fault(vcpu, &fault, true); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_fixup_and_inject_pf_error); From 297c2fe249db38bb28f343fa3ce9d3c1b3a9b1d6 Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Fri, 22 May 2026 16:26:59 -0700 Subject: [PATCH 1765/5214] KVM: SVM: Fix nested NPF injection of PFERR_GUEST_{PAGE,FINAL}_MASK bits Fix KVM's generation of PFERR_GUEST_{PAGE,FINAL}_MASK bits when injecting a Nested Page Fault into L1. Currently, KVM blindly stuffs GUEST_FINAL into L1, which is blatantly wrong given that KVM obviously generates NPFs for page table accesses. There are two paths that trigger NPF injection: hardware NPF exits (from L2) and emulation-triggered faults, i.e. when KVM detects a NPF as part of emulating an L2 GVA access. For the hardware case, use the bits verbatim from the VMCB, as KVM is simply forwarding a NPF to L1. For the emulation case, propagate the GUEST_{PAGE,FINAL} bits from the access field (which were recently added for MBEC+GMET support). To differentiate between the two cases, add "hardware_nested_page_fault" to "struct x86_exception", and set it when injecting a NPF in response to an NPF exit from L2. To help guard against future goofs, assert that exactly one of GUEST_PAGE or GUEST_FINAL is set when injecting a NPF. Unlike VMX, there are no (known) cases where hardware doesn't set either bit, and KVM should always set one or the other when emulating a GVA access. Signed-off-by: Kevin Cheng [sean: use plumbed in @access bits, massage changelog] Link: https://patch.msgid.link/20260522232701.3671446-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/mmu/paging_tmpl.h | 15 +++++--------- arch/x86/kvm/svm/nested.c | 35 ++++++++++++++++++++++----------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 29fec7c59a6f..7114707b9856 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -284,6 +284,8 @@ enum x86_intercept_stage; #define PFERR_GUEST_RMP_MASK BIT_ULL(31) #define PFERR_GUEST_FINAL_MASK BIT_ULL(32) #define PFERR_GUEST_PAGE_MASK BIT_ULL(33) +#define PFERR_GUEST_FAULT_STAGE_MASK \ + (PFERR_GUEST_FINAL_MASK | PFERR_GUEST_PAGE_MASK) #define PFERR_GUEST_ENC_MASK BIT_ULL(34) #define PFERR_GUEST_SIZEM_MASK BIT_ULL(35) #define PFERR_GUEST_VMPL_MASK BIT_ULL(36) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index cc9c7deb34bc..66eee6914234 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -397,16 +397,6 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, nested_access | PFERR_GUEST_PAGE_MASK, &walker->fault, 0); - /* - * FIXME: This can happen if emulation (for of an INS/OUTS - * instruction) triggers a nested page fault. The exit - * qualification / exit info field will incorrectly have - * "guest page access" as the nested page fault's cause, - * instead of "guest page structure access". To fix this, - * the x86_exception struct should be augmented with enough - * information to fix the exit_qualification or exit_info_1 - * fields. - */ if (unlikely(real_gpa == INVALID_GPA)) return 0; @@ -548,6 +538,11 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, walker->fault.nested_page_fault = mmu != vcpu->arch.walk_mmu; walker->fault.async_page_fault = false; +#if PTTYPE != PTTYPE_EPT + if (walker->fault.nested_page_fault) + walker->fault.error_code |= access & PFERR_GUEST_FAULT_STAGE_MASK; +#endif + trace_kvm_mmu_walker_error(walker->fault.error_code); return 0; } diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index edb15f9c6403..997a740c653d 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -39,19 +39,32 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu, { struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb = svm->vmcb; + u64 fault_stage; - if (vmcb->control.exit_code != SVM_EXIT_NPF) { - /* - * TODO: track the cause of the nested page fault, and - * correctly fill in the high bits of exit_info_1. - */ - vmcb->control.exit_code = SVM_EXIT_NPF; - vmcb->control.exit_info_1 = (1ULL << 32); - vmcb->control.exit_info_2 = fault->address; - } + /* + * For hardware NPF exits, the GUEST_FAULT_STAGE bits are only + * available in the hardware exit_info_1, since the guest_mmu + * walker doesn't know whether the faulting GPA was a page table + * page or final page from L2's perspective. + */ + if (from_hardware) + fault_stage = vmcb->control.exit_info_1 & + PFERR_GUEST_FAULT_STAGE_MASK; + else + fault_stage = fault->error_code & PFERR_GUEST_FAULT_STAGE_MASK; - vmcb->control.exit_info_1 &= ~0xffffffffULL; - vmcb->control.exit_info_1 |= fault->error_code; + /* + * All nested page faults should be annotated as occurring on the + * final translation *or* the page walk. Arbitrarily choose "final" + * if KVM is buggy and enumerated both or neither. + */ + if (WARN_ON_ONCE(hweight64(fault_stage) != 1)) + fault_stage = PFERR_GUEST_FINAL_MASK; + + vmcb->control.exit_code = SVM_EXIT_NPF; + vmcb->control.exit_info_1 = fault_stage | + (fault->error_code & ~PFERR_GUEST_FAULT_STAGE_MASK); + vmcb->control.exit_info_2 = fault->address; nested_svm_vmexit(svm); } From 96b067b59ad9ccede585fcadab27543188353bff Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Fri, 22 May 2026 16:27:00 -0700 Subject: [PATCH 1766/5214] KVM: VMX: Synthesize nested EPT violation GVA_IS_VALID/GVA_TRANSLATED bits When injecting an EPT Violation into L2 in response to a fault detected while emulating an L2 GVA access, synthesize the GVA_IS_VALID and GVA_TRANSLATED bits using information provided by the walker, instead of pulling the bits from vmcs02.EXIT_QUALIFICATION. The information in vmcs02.EXIT_QUALIFICATION is valid/correct if and only if the fault being injected into L1 is the direct result of an EPT Violation VM-Exit from L2. E.g. if KVM is emulating an I/O instruction and the memory operand's translation through L1's EPT fails, using vmcs02.EXIT_QUALIFICATION is wrong as the semantics for EXIT_QUALIFICATION would be for an I/O exit, not an EPT Violation exit. Opportunistically clean up the formatting for creating the mask of bits to pull from vmcs02.EXIT_QUALIFICATION. Signed-off-by: Kevin Cheng [sean: use plumbed in @access bits, massage changelog] Link: https://patch.msgid.link/20260522232701.3671446-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/paging_tmpl.h | 13 ++++++++++++- arch/x86/kvm/vmx/nested.c | 26 +++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 66eee6914234..df3ae0c7ec2c 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -502,7 +502,8 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, * [2:0] - Derive from the access bits. The exit_qualification might be * out of date if it is serving an EPT misconfiguration. * [5:3] - Calculated by the page walk of the guest EPT page tables - * [7:11] - Derived from [7:11] of real exit_qualification + * [7:8] - Derived from "fault stage" access bits + * [9:11] - Derived from [9:11] of real exit_qualification * * The other bits are set to 0. */ @@ -516,6 +517,14 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, else walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ; + /* + * KVM doesn't emulate features that access GPAs directly, e.g. + * Intel Processor Trace. Assume the GVA is always valid; when + * propagating faults from hardware, KVM will discard this info + * and use the EXIT_QUALIFICATION bits from the VMCS. + */ + walker->fault.exit_qualification |= EPT_VIOLATION_GVA_IS_VALID; + /* * Accesses to guest paging structures are either "reads" or * "read+write" accesses, so consider them the latter if write_fault @@ -523,6 +532,8 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, */ if (access & PFERR_GUEST_PAGE_MASK) walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ; + else + walker->fault.exit_qualification |= EPT_VIOLATION_GVA_TRANSLATED; /* * Note, pte_access holds the raw RWX bits from the EPTE, not diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 3bb7eaa7b2a5..a78ce0080963 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -445,13 +445,29 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu, exit_qualification = 0; } else { u64 mask = EPT_VIOLATION_GVA_IS_VALID | - EPT_VIOLATION_GVA_TRANSLATED; + EPT_VIOLATION_GVA_TRANSLATED; + if (vmx->nested.msrs.ept_caps & VMX_EPT_ADVANCED_VMEXIT_INFO_BIT) mask |= EPT_VIOLATION_GVA_USER | - EPT_VIOLATION_GVA_WRITABLE | - EPT_VIOLATION_GVA_NX; - exit_qualification = fault->exit_qualification; - exit_qualification |= vmx_get_exit_qual(vcpu) & mask; + EPT_VIOLATION_GVA_WRITABLE | + EPT_VIOLATION_GVA_NX; + + exit_qualification = fault->exit_qualification & ~mask; + + /* + * Use the EXIT_QUALIFICATION from the VMCS if and only + * if the hardware VM-Exit from L2 was an EPT Violation. + * If the fault is synthesized, then EXIT_QUALIFICATION + * is stale and/or holds entirely different data. And + * conversely, KVM _must_ rely on EXIT_QUALIFICATION if + * the fault came from hardware, because KVM only sees + * and walks the faulting GPA. + */ + if (from_hardware) + exit_qualification |= vmx_get_exit_qual(vcpu) & mask; + else + exit_qualification |= fault->exit_qualification & mask; + vm_exit_reason = EXIT_REASON_EPT_VIOLATION; } From 0de1020f7bbb3e1c9cd5b6f3eb4bdd661b1ff735 Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Fri, 22 May 2026 16:27:01 -0700 Subject: [PATCH 1767/5214] KVM: selftests: Add nested page fault injection test Add a test that exercises nested page fault injection during L2 execution. L2 executes I/O string instructions (OUTSB/INSB) that access memory restricted in L1's nested page tables (NPT/EPT), triggering a nested page fault that L0 must inject to L1. The test supports both AMD SVM (NPF) and Intel VMX (EPT violation) and verifies that: - The exit reason is an NPF/EPT violation - The access type and permission bits are correct - The faulting GPA is correct Three test cases are implemented: - Unmap the final data page (final translation fault, OUTSB read) - Unmap a PT page (page walk fault, OUTSB read) - Write-protect the final data page (protection violation, INSB write) - Write-protect a PT page (protection violation on A/D update, OUTSB read) Signed-off-by: Kevin Cheng [sean: name it nested_tdp_fault_test, consolidate asserts] Link: https://patch.msgid.link/20260522232701.3671446-6-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/include/x86/processor.h | 9 + .../selftests/kvm/x86/nested_tdp_fault_test.c | 313 ++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..0a0a02a1dffe 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -97,6 +97,7 @@ TEST_GEN_PROGS_x86 += x86/nested_emulation_test TEST_GEN_PROGS_x86 += x86/nested_exceptions_test TEST_GEN_PROGS_x86 += x86/nested_invalid_cr3_test TEST_GEN_PROGS_x86 += x86/nested_set_state_test +TEST_GEN_PROGS_x86 += x86/nested_tdp_fault_test TEST_GEN_PROGS_x86 += x86/nested_tsc_adjust_test TEST_GEN_PROGS_x86 += x86/nested_tsc_scaling_test TEST_GEN_PROGS_x86 += x86/nested_vmsave_vmload_test diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 851ffcd3340c..06878e7c7347 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1573,6 +1573,15 @@ u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa); #define PFERR_GUEST_PAGE_MASK BIT_ULL(PFERR_GUEST_PAGE_BIT) #define PFERR_IMPLICIT_ACCESS BIT_ULL(PFERR_IMPLICIT_ACCESS_BIT) +#define EPT_VIOLATION_ACC_READ BIT(0) +#define EPT_VIOLATION_ACC_WRITE BIT(1) +#define EPT_VIOLATION_ACC_INSTR BIT(2) +#define EPT_VIOLATION_PROT_READ BIT(3) +#define EPT_VIOLATION_PROT_WRITE BIT(4) +#define EPT_VIOLATION_PROT_EXEC BIT(5) +#define EPT_VIOLATION_GVA_IS_VALID BIT(7) +#define EPT_VIOLATION_GVA_TRANSLATED BIT(8) + bool sys_clocksource_is_based_on_tsc(void); #endif /* SELFTEST_KVM_PROCESSOR_H */ diff --git a/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c new file mode 100644 index 000000000000..fa95568f55ff --- /dev/null +++ b/tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, Google, Inc. + */ + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" +#include "vmx.h" + +#define L2_GUEST_STACK_SIZE 64 + +enum test_type { + TEST_FINAL_PAGE_UNMAPPED, /* Final data page not present */ + TEST_PT_PAGE_UNMAPPED, /* Page table page not present */ + TEST_FINAL_PAGE_WRITE_PROTECTED, /* Final data page read-only */ + TEST_PT_PAGE_WRITE_PROTECTED, /* Page table page read-only */ +}; + +static gva_t l2_test_page; +static void (*l2_entry)(void); + +#define TEST_IO_PORT 0x80 +#define TEST1_VADDR 0x8000000ULL +#define TEST2_VADDR 0x10000000ULL +#define TEST3_VADDR 0x18000000ULL +#define TEST4_VADDR 0x20000000ULL + +/* + * L2 executes OUTS reading from l2_test_page, triggering a nested page + * fault on the read access. + */ +static void l2_guest_code_outs(void) +{ + asm volatile("outsb" ::"S"(l2_test_page), "d"(TEST_IO_PORT) : "memory"); + GUEST_FAIL("L2 should not reach here"); +} + +/* + * L2 executes INS writing to l2_test_page, triggering a nested page + * fault on the write access. + */ +static void l2_guest_code_ins(void) +{ + asm volatile("insb" ::"D"(l2_test_page), "d"(TEST_IO_PORT) : "memory"); + GUEST_FAIL("L2 should not reach here"); +} + +#define GUEST_ASSERT_EXIT_QUAL(ac_eq, ex_eq) \ + __GUEST_ASSERT((ac_eq) == (ex_eq), \ + "Wanted EXIT_QUAL '0x%lx', got '0x%lx'", ex_eq, ac_eq) + +static void l1_vmx_code(struct vmx_pages *vmx, u64 expected_fault_gpa, + u64 test_type) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + u64 exit_qual; + + GUEST_ASSERT(vmx->vmcs_gpa); + GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + GUEST_ASSERT(load_vmcs(vmx)); + + prepare_vmcs(vmx, l2_entry, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + GUEST_ASSERT(!vmlaunch()); + + /* Verify we got an EPT violation exit */ + __GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION, + "Expected EPT violation (0x%x), got 0x%lx", + EXIT_REASON_EPT_VIOLATION, + vmreadz(VM_EXIT_REASON)); + + __GUEST_ASSERT(vmreadz(GUEST_PHYSICAL_ADDRESS) == expected_fault_gpa, + "Expected guest_physical_address = 0x%lx, got 0x%lx", + expected_fault_gpa, + vmreadz(GUEST_PHYSICAL_ADDRESS)); + + exit_qual = vmreadz(EXIT_QUALIFICATION); + + /* + * Note, EPT page table accesses are always read+write, e.g. so that + * the CPU can do A/D updates at-will. + */ + switch (test_type) { + case TEST_FINAL_PAGE_UNMAPPED: + GUEST_ASSERT_EXIT_QUAL(exit_qual, EPT_VIOLATION_ACC_READ | + EPT_VIOLATION_GVA_IS_VALID | + EPT_VIOLATION_GVA_TRANSLATED); + break; + case TEST_PT_PAGE_UNMAPPED: + GUEST_ASSERT_EXIT_QUAL(exit_qual, EPT_VIOLATION_ACC_READ | + EPT_VIOLATION_ACC_WRITE | + EPT_VIOLATION_GVA_IS_VALID); + break; + case TEST_FINAL_PAGE_WRITE_PROTECTED: + GUEST_ASSERT_EXIT_QUAL(exit_qual, EPT_VIOLATION_ACC_WRITE | + EPT_VIOLATION_PROT_READ | + EPT_VIOLATION_PROT_EXEC | + EPT_VIOLATION_GVA_IS_VALID | + EPT_VIOLATION_GVA_TRANSLATED); + break; + case TEST_PT_PAGE_WRITE_PROTECTED: + GUEST_ASSERT_EXIT_QUAL(exit_qual, EPT_VIOLATION_ACC_READ | + EPT_VIOLATION_ACC_WRITE | + EPT_VIOLATION_PROT_READ | + EPT_VIOLATION_PROT_EXEC | + EPT_VIOLATION_GVA_IS_VALID); + break; + } + + GUEST_DONE(); +} + +#define GUEST_ASSERT_NPF_EC(ac_ec, ex_ec) \ + __GUEST_ASSERT((ac_ec) == (ex_ec), \ + "Wanted NPF error code '0x%lx', got '0x%lx'", (u64)(ex_ec), ac_ec) + + +static void l1_svm_code(struct svm_test_data *svm, u64 expected_fault_gpa, + u64 test_type) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + u64 exit_info_1; + + generic_svm_setup(svm, l2_entry, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + run_guest(vmcb, svm->vmcb_gpa); + + /* Verify we got an NPF exit */ + __GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_NPF, + "Expected NPF exit (0x%x), got 0x%lx", SVM_EXIT_NPF, + vmcb->control.exit_code); + + __GUEST_ASSERT(vmcb->control.exit_info_2 == expected_fault_gpa, + "Expected exit_info_2 = 0x%lx, got 0x%lx", + expected_fault_gpa, + vmcb->control.exit_info_2); + + exit_info_1 = vmcb->control.exit_info_1; + + /* + * Note, without GMET enabled, NPT walks are always user accesses. And + * like EPT, page table accesses are always read+write. + */ + switch (test_type) { + case TEST_FINAL_PAGE_UNMAPPED: + GUEST_ASSERT_NPF_EC(exit_info_1, PFERR_USER_MASK | + PFERR_GUEST_FINAL_MASK); + break; + case TEST_PT_PAGE_UNMAPPED: + GUEST_ASSERT_NPF_EC(exit_info_1, PFERR_WRITE_MASK | + PFERR_USER_MASK | + PFERR_GUEST_PAGE_MASK); + break; + case TEST_FINAL_PAGE_WRITE_PROTECTED: + GUEST_ASSERT_NPF_EC(exit_info_1, PFERR_PRESENT_MASK | + PFERR_WRITE_MASK | + PFERR_USER_MASK | + PFERR_GUEST_FINAL_MASK); + break; + case TEST_PT_PAGE_WRITE_PROTECTED: + GUEST_ASSERT_NPF_EC(exit_info_1, PFERR_PRESENT_MASK | + PFERR_WRITE_MASK | + PFERR_USER_MASK | + PFERR_GUEST_PAGE_MASK); + break; + } + + GUEST_DONE(); +} + +static void l1_guest_code(void *data, u64 expected_fault_gpa, + u64 test_type) +{ + if (this_cpu_has(X86_FEATURE_VMX)) + l1_vmx_code(data, expected_fault_gpa, test_type); + else + l1_svm_code(data, expected_fault_gpa, test_type); +} + +/* Returns the GPA of the PT page that maps @vaddr. */ +static u64 get_pt_gpa_for_vaddr(struct kvm_vm *vm, u64 vaddr) +{ + u64 *pte; + + pte = vm_get_pte(vm, vaddr); + TEST_ASSERT(pte && (*pte & 0x1), "PTE not present for vaddr 0x%lx", + (unsigned long)vaddr); + + return addr_hva2gpa(vm, (void *)((u64)pte & ~0xFFFULL)); +} + +static void run_test(enum test_type type) +{ + gpa_t expected_fault_gpa; + gva_t nested_gva; + + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + vm_enable_tdp(vm); + + if (kvm_cpu_has(X86_FEATURE_VMX)) + vcpu_alloc_vmx(vm, &nested_gva); + else + vcpu_alloc_svm(vm, &nested_gva); + + switch (type) { + case TEST_FINAL_PAGE_UNMAPPED: + /* + * Unmap the final data page from NPT/EPT. The guest page + * table walk succeeds, but the final GPA->HPA translation + * fails. L2 reads from the page via OUTS. + */ + l2_entry = l2_guest_code_outs; + l2_test_page = vm_alloc(vm, vm->page_size, TEST1_VADDR); + expected_fault_gpa = addr_gva2gpa(vm, l2_test_page); + break; + case TEST_PT_PAGE_UNMAPPED: + /* + * Unmap a page table page from NPT/EPT. The hardware page + * table walk fails when translating the PT page's GPA + * through NPT/EPT. L2 reads from the page via OUTS. + */ + l2_entry = l2_guest_code_outs; + l2_test_page = vm_alloc(vm, vm->page_size, TEST2_VADDR); + expected_fault_gpa = get_pt_gpa_for_vaddr(vm, l2_test_page); + break; + case TEST_FINAL_PAGE_WRITE_PROTECTED: + /* + * Write-protect the final data page in NPT/EPT. The page + * is present and readable, but not writable. L2 writes to + * the page via INS, triggering a protection violation. + */ + l2_entry = l2_guest_code_ins; + l2_test_page = vm_alloc(vm, vm->page_size, TEST3_VADDR); + expected_fault_gpa = addr_gva2gpa(vm, l2_test_page); + break; + case TEST_PT_PAGE_WRITE_PROTECTED: + /* + * Write-protect a page table page in NPT/EPT. The page is + * present and readable, but not writable. The guest page + * table walk needs write access to set A/D bits, so it + * triggers a protection violation on the PT page. + * L2 reads from the page via OUTS. + */ + l2_entry = l2_guest_code_outs; + l2_test_page = vm_alloc(vm, vm->page_size, TEST4_VADDR); + expected_fault_gpa = get_pt_gpa_for_vaddr(vm, l2_test_page); + break; + } + + tdp_identity_map_default_memslots(vm); + + if (type == TEST_FINAL_PAGE_WRITE_PROTECTED || + type == TEST_PT_PAGE_WRITE_PROTECTED) + *tdp_get_pte(vm, expected_fault_gpa) &= ~PTE_WRITABLE_MASK(&vm->stage2_mmu); + else + *tdp_get_pte(vm, expected_fault_gpa) &= ~(PTE_PRESENT_MASK(&vm->stage2_mmu) | + PTE_READABLE_MASK(&vm->stage2_mmu) | + PTE_WRITABLE_MASK(&vm->stage2_mmu) | + PTE_EXECUTABLE_MASK(&vm->stage2_mmu)); + + sync_global_to_guest(vm, l2_entry); + sync_global_to_guest(vm, l2_test_page); + vcpu_args_set(vcpu, 3, nested_gva, expected_fault_gpa, (u64)type); + + /* + * For the INS-based write test, KVM emulates the instruction and + * first reads from the I/O port, which exits to userspace. + * Re-enter the guest so emulation can proceed to the memory + * write, where the nested page fault is triggered. + */ + for (;;) { + vcpu_run(vcpu); + + if (vcpu->run->exit_reason == KVM_EXIT_IO && + vcpu->run->io.port == TEST_IO_PORT && + vcpu->run->io.direction == KVM_EXIT_IO_IN) { + continue; + } + break; + } + + switch (get_ucall(vcpu, &uc)) { + case UCALL_DONE: + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + default: + TEST_FAIL("Unexpected exit reason: %d", vcpu->run->exit_reason); + } + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || kvm_cpu_has(X86_FEATURE_SVM)); + TEST_REQUIRE(kvm_cpu_has_tdp()); + + run_test(TEST_FINAL_PAGE_UNMAPPED); + run_test(TEST_PT_PAGE_UNMAPPED); + run_test(TEST_FINAL_PAGE_WRITE_PROTECTED); + run_test(TEST_PT_PAGE_WRITE_PROTECTED); + + return 0; +} From 986833d381d2c48bdfa52652006f445570aea0e4 Mon Sep 17 00:00:00 2001 From: Vishal Annapurve Date: Fri, 22 May 2026 15:15:34 +0000 Subject: [PATCH 1768/5214] KVM: x86: Treat KVM's virtual PMU as disabled for TDX VMs Introduce a "protected PMU" concept, and use it to disable KVM's virtual PMU for TDX VMs, as the PMU state for TDX VMs is virtualized by the TDX Module[1], i.e. _can't_ emulated/virtualized by KVM, and KVM doesn't yet support enabling/exposing PMU functionality for/to TDX VMs. For now, simply treat the PMU as disabled, as it's not clear what all needs to be changed, e.g. KVM needs to do at least: 1) Configure TD_PARAMS to allow guests to use performance monitoring. 2) Restrict the TD to a subset of the PEBS counters if supported. 3) Limit the TD to setup a certain perfmon events using basic/enhanced event filtering. Explicitly disallow enabling the PMU via KVM_CAP_PMU_CAPABILITY for VMs with a protected PMU to prevent userspace from circumventing KVM's protections. Link: https://cdrdv2.intel.com/v1/dl/getContent/733575 Section 15.2[1] Suggested-by: Sean Christopherson Signed-off-by: Vishal Annapurve Link: https://patch.msgid.link/20260522151534.652522-1-vannapurve@google.com [sean: massage shortlog and changelog] Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/vmx/tdx.c | 6 ++++++ arch/x86/kvm/x86.c | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7114707b9856..beb83eea65ef 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1442,6 +1442,7 @@ struct kvm_arch { bool has_private_mem; bool has_protected_state; bool has_protected_eoi; + bool has_protected_pmu; bool pre_fault_allowed; struct hlist_head *mmu_page_hash; struct list_head active_mmu_pages; diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b8c3d3d8bbfe..5fb3f606501b 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -638,6 +638,12 @@ int tdx_vm_init(struct kvm *kvm) kvm->arch.has_private_mem = true; kvm->arch.disabled_quirks |= KVM_X86_QUIRK_IGNORE_GUEST_PAT; + /* + * PMU support is provided by the TDX-Module (if enabled for the VM). + * From KVM's perspective, the VM doesn't have a virtual PMU. + */ + kvm->arch.has_protected_pmu = true; + /* * Because guest TD is protected, VMM can't parse the instruction in TD. * Instead, guest uses MMIO hypercall. For unmodified device driver, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 93958df30230..c560c52b95f0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6920,6 +6920,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, if (!enable_pmu || (cap->args[0] & ~KVM_CAP_PMU_VALID_MASK)) break; + if (kvm->arch.has_protected_pmu && + cap->args[0] != KVM_PMU_CAP_DISABLE) + break; + mutex_lock(&kvm->lock); if (!kvm->created_vcpus && !kvm->arch.created_mediated_pmu) { kvm->arch.enable_pmu = !(cap->args[0] & KVM_PMU_CAP_DISABLE); @@ -13362,7 +13366,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.default_tsc_khz = max_tsc_khz ? : tsc_khz; kvm->arch.apic_bus_cycle_ns = APIC_BUS_CYCLE_NS_DEFAULT; kvm->arch.guest_can_read_msr_platform_info = true; - kvm->arch.enable_pmu = enable_pmu; + kvm->arch.enable_pmu = enable_pmu && !kvm->arch.has_protected_pmu; #if IS_ENABLED(CONFIG_HYPERV) spin_lock_init(&kvm->arch.hv_root_tdp_lock); From c62cdd3e919bdf84c37ec46810f87cdb1736e822 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:45 +0200 Subject: [PATCH 1769/5214] MIPS: ip22-gio: fix kfree() of static object The gio bus root device is a statically allocated object which must not be freed by kfree() on failure to register the device or bus. Fixes: 82242d28ff8b ("MIPS: IP22: Add missing put_device call") Cc: stable@vger.kernel.org # 3.17 Cc: Levente Kurusa Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer --- arch/mips/sgi-ip22/ip22-gio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 9eec8842ffb7..a574441fa44b 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -30,7 +30,6 @@ static struct { static void gio_bus_release(struct device *dev) { - kfree(dev); } static struct device gio_bus = { From 7de9a1b45f5a95b58145653c525c8fb80292d9ab Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:46 +0200 Subject: [PATCH 1770/5214] MIPS: ip22-gio: fix gio device memory leak The gio device release callback was never wired up so gio devices are not freed when the last reference is dropped. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer --- arch/mips/sgi-ip22/ip22-gio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index a574441fa44b..2f5c6c6f8254 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -100,6 +100,8 @@ int gio_device_register(struct gio_device *giodev) { giodev->dev.bus = &gio_bus_type; giodev->dev.parent = &gio_bus; + giodev->dev.release = gio_release_dev; + return device_register(&giodev->dev); } EXPORT_SYMBOL_GPL(gio_device_register); From b82930a4c5dbc5c4df39c0f93d968c239f2c6885 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:47 +0200 Subject: [PATCH 1771/5214] MIPS: ip22-gio: fix device reference leak in probe The gio probe function needlessly takes a device reference which is never released and therefore prevents unbound gio devices from being freed. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer --- arch/mips/sgi-ip22/ip22-gio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 2f5c6c6f8254..7b7572d11250 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -133,13 +133,9 @@ static int gio_device_probe(struct device *dev) if (!drv->probe) return error; - gio_dev_get(gio_dev); - match = gio_match_device(drv->id_table, gio_dev); if (match) error = drv->probe(gio_dev, match); - if (error) - gio_dev_put(gio_dev); return error; } From b8f0962b3f4e603d76e610484b0ef81c5e3325fe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:48 +0200 Subject: [PATCH 1772/5214] MIPS: ip22-gio: switch to dynamic root device Driver core expects devices to be dynamically allocated and will, for example, complain loudly when no release function has been provided. Use root_device_register() to allocate and register the root device instead of open coding using a static device. Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer --- arch/mips/sgi-ip22/ip22-gio.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 7b7572d11250..d29067430b44 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -28,14 +28,7 @@ static struct { { .name = "SGI GR2/GR3", .id = 0x7f }, }; -static void gio_bus_release(struct device *dev) -{ -} - -static struct device gio_bus = { - .init_name = "gio", - .release = &gio_bus_release, -}; +static struct device *gio_bus; /** * gio_match_device - Tell if an of_device structure has a matching @@ -99,7 +92,7 @@ EXPORT_SYMBOL_GPL(gio_release_dev); int gio_device_register(struct gio_device *giodev) { giodev->dev.bus = &gio_bus_type; - giodev->dev.parent = &gio_bus; + giodev->dev.parent = gio_bus; giodev->dev.release = gio_release_dev; return device_register(&giodev->dev); @@ -397,11 +390,9 @@ static int __init ip22_gio_init(void) unsigned int pbdma __maybe_unused; int ret; - ret = device_register(&gio_bus); - if (ret) { - put_device(&gio_bus); - return ret; - } + gio_bus = root_device_register("gio"); + if (IS_ERR(gio_bus)) + return PTR_ERR(gio_bus); ret = bus_register(&gio_bus_type); if (!ret) { @@ -420,8 +411,9 @@ static int __init ip22_gio_init(void) ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIOEXP0_IRQ); ip22_check_gio(2, GIO_SLOT_EXP1_BASE, SGI_GIOEXP1_IRQ); } - } else - device_unregister(&gio_bus); + } else { + root_device_unregister(gio_bus); + } return ret; } From 4d9dd68708f814ddeef4015bc67f2b13d192d0be Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:49 +0200 Subject: [PATCH 1773/5214] MIPS: ip22-gio: do not export device release function There is no need to export the gio device release function as the devices are reference counted. Drop the export along with the unused inline wrapper. Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/gio_device.h | 6 ------ arch/mips/sgi-ip22/ip22-gio.c | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/mips/include/asm/gio_device.h b/arch/mips/include/asm/gio_device.h index 159087f5386e..f9c102f038d6 100644 --- a/arch/mips/include/asm/gio_device.h +++ b/arch/mips/include/asm/gio_device.h @@ -37,12 +37,6 @@ extern void gio_dev_put(struct gio_device *); extern int gio_device_register(struct gio_device *); extern void gio_device_unregister(struct gio_device *); -extern void gio_release_dev(struct device *); - -static inline void gio_device_free(struct gio_device *dev) -{ - gio_release_dev(&dev->dev); -} extern int gio_register_driver(struct gio_driver *); extern void gio_unregister_driver(struct gio_driver *); diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index d29067430b44..54e17c8693e2 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -80,14 +80,13 @@ EXPORT_SYMBOL_GPL(gio_dev_put); * Will be called only by the device core when all users of this gio device are * done. */ -void gio_release_dev(struct device *dev) +static void gio_release_dev(struct device *dev) { struct gio_device *giodev; giodev = to_gio_device(dev); kfree(giodev); } -EXPORT_SYMBOL_GPL(gio_release_dev); int gio_device_register(struct gio_device *giodev) { From e2f48710459487341a70b5c433b87046da861ac9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 26 May 2026 18:40:32 +0200 Subject: [PATCH 1774/5214] MIPS: alchemy: platform: add missing include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull in the platform.h header into platform.c to fix the following warning: arch/mips/alchemy/devboards/platform.c:68:12: warning: no previous prototype for ‘db1x_register_pcmcia_socket’ [-Wmissing-prototypes] 68 | int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ arch/mips/alchemy/devboards/platform.c:152:12: warning: no previous prototype for ‘db1x_register_norflash’ [-Wmissing-prototypes] 152 | int __init db1x_register_norflash(unsigned long size, int width, | ^~~~~~~~~~~~~~~~~~~~~~ Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/devboards/platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c index 46262c823fcb..fbc93d729c50 100644 --- a/arch/mips/alchemy/devboards/platform.c +++ b/arch/mips/alchemy/devboards/platform.c @@ -20,6 +20,8 @@ #include +#include "platform.h" + void prom_putchar(char c) { if (alchemy_get_cputype() == ALCHEMY_CPU_AU1300) From d9a316fd994ee3c43ea1dedf76d9f452da79fc2c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 26 May 2026 18:40:33 +0200 Subject: [PATCH 1775/5214] MIPS: alchemy: provide visible function prototypes to board files Board files under arch/mips/alchemy/ define functions called from db1xxx.c but their prototypes are only in that .c file instead of being declared in a common header. This causes several build warnings about missing prototypes. Provide these prototypes in a new header and include it where necessary. Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/devboards/db1000.c | 2 ++ arch/mips/alchemy/devboards/db1200.c | 1 + arch/mips/alchemy/devboards/db1300.c | 1 + arch/mips/alchemy/devboards/db1550.c | 2 ++ arch/mips/alchemy/devboards/db1xxx.c | 11 +---------- arch/mips/alchemy/devboards/db1xxx.h | 18 ++++++++++++++++++ 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 arch/mips/alchemy/devboards/db1xxx.h diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c index 6984cd5169b5..65238f14f28d 100644 --- a/arch/mips/alchemy/devboards/db1000.c +++ b/arch/mips/alchemy/devboards/db1000.c @@ -27,6 +27,8 @@ #include #include #include + +#include "db1xxx.h" #include "platform.h" #define F_SWAPPED (bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1000_SWAPBOOT) diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c index 67f067706af2..de2a9083ed9a 100644 --- a/arch/mips/alchemy/devboards/db1200.c +++ b/arch/mips/alchemy/devboards/db1200.c @@ -30,6 +30,7 @@ #include #include +#include "db1xxx.h" #include "platform.h" #define BCSR_INT_IDE 0x0001 diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index d377e043b49f..b46f5e47da2c 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -32,6 +32,7 @@ #include #include +#include "db1xxx.h" #include "platform.h" /* FPGA (external mux) interrupt sources */ diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index 6c6837181f55..b8295a5c2e9a 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -28,6 +28,8 @@ #include #include #include + +#include "db1xxx.h" #include "platform.h" static void __init db1550_hw_setup(void) diff --git a/arch/mips/alchemy/devboards/db1xxx.c b/arch/mips/alchemy/devboards/db1xxx.c index e6d25aad8350..2e8c68d97b34 100644 --- a/arch/mips/alchemy/devboards/db1xxx.c +++ b/arch/mips/alchemy/devboards/db1xxx.c @@ -7,16 +7,7 @@ #include #include -int __init db1000_board_setup(void); -int __init db1000_dev_setup(void); -int __init db1500_pci_setup(void); -int __init db1200_board_setup(void); -int __init db1200_dev_setup(void); -int __init db1300_board_setup(void); -int __init db1300_dev_setup(void); -int __init db1550_board_setup(void); -int __init db1550_dev_setup(void); -int __init db1550_pci_setup(int); +#include "db1xxx.h" static const char *board_type_str(void) { diff --git a/arch/mips/alchemy/devboards/db1xxx.h b/arch/mips/alchemy/devboards/db1xxx.h new file mode 100644 index 000000000000..f39e3551e3b3 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1xxx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_MIPS_DB1XXX__ +#define __LINUX_MIPS_DB1XXX__ + +const char *get_system_type(void); +int db1000_board_setup(void); +int db1000_dev_setup(void); +int db1500_pci_setup(void); +int db1200_board_setup(void); +int db1200_dev_setup(void); +int db1300_board_setup(void); +int db1300_dev_setup(void); +int db1550_board_setup(void); +int db1550_dev_setup(void); +int db1550_pci_setup(int id); + +#endif /* __LINUX_MIPS_DB1XXX__ */ From 054f568e0515fef01e3ee71728763aff7eb4623b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 26 May 2026 18:40:34 +0200 Subject: [PATCH 1776/5214] MIPS: alchemy: mtx1: attach software nodes to GPIO chips GPIO subsystem is switching the way it locates GPIO chip instances for GPIO references in software nodes from matching on node names to identity matching, which necessitates assigning firmware nodes (software nodes) to GPIO chips. Move the node definitions for alchemy-gpio1 and alchemy-gpio2 to arch/misp/alchemy/common/gpiolib.c, register them there, and attach them to gpio_chip instances. Adjust MTX1 board file to use these nodes. Note that because nodes need to be registered before they can be used in PROPERTY_ENTRY_GPIO() we have to do the registration at postcore_initcall level, otherwise (due to the link order) MTX1 board initialization code will run first. Signed-off-by: Dmitry Torokhov [Bartosz: use platform_device_info::swnode] Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/board-mtx1.c | 34 +++++++--------- arch/mips/alchemy/common/gpiolib.c | 39 ++++++++++++++++++- .../include/asm/mach-au1x00/gpio-au1000.h | 5 +++ 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/arch/mips/alchemy/board-mtx1.c b/arch/mips/alchemy/board-mtx1.c index cb6be58808a0..88c20c0ca96d 100644 --- a/arch/mips/alchemy/board-mtx1.c +++ b/arch/mips/alchemy/board-mtx1.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -78,17 +79,13 @@ void __init board_setup(void) /******************************************************************************/ -static const struct software_node mtx1_gpiochip_node = { - .name = "alchemy-gpio2", -}; - static const struct software_node mtx1_gpio_keys_node = { .name = "mtx1-gpio-keys", }; static const struct property_entry mtx1_button_props[] = { PROPERTY_ENTRY_U32("linux,code", BTN_0), - PROPERTY_ENTRY_GPIO("gpios", &mtx1_gpiochip_node, 7, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio2_node, 7, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_STRING("label", "System button"), { } }; @@ -98,7 +95,7 @@ static const struct software_node mtx1_button_node = { .properties = mtx1_button_props, }; -static const struct software_node *mtx1_gpio_keys_swnodes[] __initconst = { +static const struct software_node * const mtx1_gpio_keys_swnodes[] __initconst = { &mtx1_gpio_keys_node, &mtx1_button_node, NULL @@ -127,13 +124,13 @@ static void __init mtx1_keys_init(void) pr_err("failed to create gpio-keys device: %d\n", err); } -/* Global number 215 is offset 15 on Alchemy GPIO 2 */ static const struct property_entry mtx1_wdt_props[] = { - PROPERTY_ENTRY_GPIO("gpios", &mtx1_gpiochip_node, 15, GPIO_ACTIVE_HIGH), + /* Global number 215 is offset 15 on Alchemy GPIO 2 */ + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio2_node, 15, GPIO_ACTIVE_HIGH), { } }; -static struct platform_device_info mtx1_wdt_info __initconst = { +static const struct platform_device_info mtx1_wdt_info __initconst = { .name = "mtx1-wdt", .id = 0, .properties = mtx1_wdt_props, @@ -147,7 +144,7 @@ static void __init mtx1_wdt_init(void) pd = platform_device_register_full(&mtx1_wdt_info); err = PTR_ERR_OR_ZERO(pd); if (err) - pr_err("failed to create gpio-keys device: %d\n", err); + pr_err("failed to create watchdog device: %d\n", err); } static const struct software_node mtx1_gpio_leds_node = { @@ -155,7 +152,7 @@ static const struct software_node mtx1_gpio_leds_node = { }; static const struct property_entry mtx1_green_led_props[] = { - PROPERTY_ENTRY_GPIO("gpios", &mtx1_gpiochip_node, 11, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio2_node, 11, GPIO_ACTIVE_HIGH), { } }; @@ -166,7 +163,7 @@ static const struct software_node mtx1_green_led_node = { }; static const struct property_entry mtx1_red_led_props[] = { - PROPERTY_ENTRY_GPIO("gpios", &mtx1_gpiochip_node, 12, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio2_node, 12, GPIO_ACTIVE_HIGH), { } }; @@ -176,7 +173,7 @@ static const struct software_node mtx1_red_led_node = { .properties = mtx1_red_led_props, }; -static const struct software_node *mtx1_gpio_leds_swnodes[] = { +static const struct software_node * const mtx1_gpio_leds_swnodes[] __initconst = { &mtx1_gpio_leds_node, &mtx1_green_led_node, &mtx1_red_led_node, @@ -185,9 +182,10 @@ static const struct software_node *mtx1_gpio_leds_swnodes[] = { static void __init mtx1_leds_init(void) { - struct platform_device_info led_info = { + const struct platform_device_info pdevinfo = { .name = "leds-gpio", .id = PLATFORM_DEVID_NONE, + .swnode = &mtx1_gpio_leds_node, }; struct platform_device *led_dev; int err; @@ -198,9 +196,7 @@ static void __init mtx1_leds_init(void) return; } - led_info.fwnode = software_node_fwnode(&mtx1_gpio_leds_node); - - led_dev = platform_device_register_full(&led_info); + led_dev = platform_device_register_full(&pdevinfo); err = PTR_ERR_OR_ZERO(led_dev); if (err) pr_err("failed to create LED device: %d\n", err); @@ -335,10 +331,6 @@ static int __init mtx1_register_devices(void) au1xxx_override_eth_cfg(0, &mtx1_au1000_eth0_pdata); - rc = software_node_register(&mtx1_gpiochip_node); - if (rc) - return rc; - rc = platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs)); if (rc) return rc; diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c index e79e26ffac99..2141eae5ce45 100644 --- a/arch/mips/alchemy/common/gpiolib.c +++ b/arch/mips/alchemy/common/gpiolib.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -95,7 +96,21 @@ static int gpio1_to_irq(struct gpio_chip *chip, unsigned offset) return alchemy_gpio1_to_irq(offset + ALCHEMY_GPIO1_BASE); } -struct gpio_chip alchemy_gpio_chip[] = { +const struct software_node alchemy_gpio1_node = { + .name = "alchemy-gpio1", +}; + +const struct software_node alchemy_gpio2_node = { + .name = "alchemy-gpio2", +}; + +static const struct software_node *alchemy_gpio_node_group[] = { + &alchemy_gpio1_node, + &alchemy_gpio2_node, + NULL +}; + +static struct gpio_chip alchemy_gpio_chip[] = { [0] = { .label = "alchemy-gpio1", .direction_input = gpio1_direction_input, @@ -157,6 +172,28 @@ static struct gpio_chip au1300_gpiochip = { .ngpio = AU1300_GPIO_NUM, }; +/* + * Software nodes must be registered before board-specific code (that runs + * at arch_initcall level) attempts to use them as GPIO targets or as fwnodes + * for registered devices. We can not do registration in alchemy_gpiochip_init + * because it also runs as arch_initcall and runs after board-specific code + * because of the link order, and so we do it at postcore_initcall level. + */ +static int __init alchemy_gpio_nodes_init(void) +{ + int ret; + + ret = software_node_register_node_group(alchemy_gpio_node_group); + if (ret) + return ret; + + alchemy_gpio_chip[0].fwnode = software_node_fwnode(&alchemy_gpio1_node); + alchemy_gpio_chip[1].fwnode = software_node_fwnode(&alchemy_gpio2_node); + + return 0; +} +postcore_initcall(alchemy_gpio_nodes_init); + static int __init alchemy_gpiochip_init(void) { int ret = 0; diff --git a/arch/mips/include/asm/mach-au1x00/gpio-au1000.h b/arch/mips/include/asm/mach-au1x00/gpio-au1000.h index e6306f6820e6..de7f857cb333 100644 --- a/arch/mips/include/asm/mach-au1x00/gpio-au1000.h +++ b/arch/mips/include/asm/mach-au1x00/gpio-au1000.h @@ -40,6 +40,11 @@ #define AU1000_GPIO2_INTENABLE 0x10 #define AU1000_GPIO2_ENABLE 0x14 +struct software_node; + +extern const struct software_node alchemy_gpio1_node; +extern const struct software_node alchemy_gpio2_node; + static inline int au1000_gpio1_to_irq(int gpio) { return MAKE_IRQ(1, gpio - ALCHEMY_GPIO1_BASE); From dc26a10ef98f3dabd9dd9420c5d615891d3321cd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 26 May 2026 18:40:35 +0200 Subject: [PATCH 1777/5214] MIPS: alchemy: db1000: use nodes attached to GPIO chips in properties GPIO subsystem is switching the way it locates GPIO chip instances for GPIO references in software nodes by doing identity matching instead of matching on node names. Switch to using software nodes attached to gpio chips instead of using freestanding software nodes. Also stop supplying platform data for the spi-gpio controller since spi-gpio driver can derive number of chipselect lines from device properties. Signed-off-by: Dmitry Torokhov Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/devboards/db1000.c | 29 ++++++++-------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c index 65238f14f28d..8fb24b220e3a 100644 --- a/arch/mips/alchemy/devboards/db1000.c +++ b/arch/mips/alchemy/devboards/db1000.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -377,20 +376,15 @@ static struct platform_device db1100_mmc1_dev = { /******************************************************************************/ -static const struct software_node db1100_alchemy2_gpiochip = { - .name = "alchemy-gpio2", -}; - -static const struct property_entry db1100_ads7846_properties[] = { +static const struct property_entry db1100_ads7846_props[] = { PROPERTY_ENTRY_U16("ti,vref_min", 3300), - PROPERTY_ENTRY_GPIO("pendown-gpios", - &db1100_alchemy2_gpiochip, 21, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("pendown-gpios", &alchemy_gpio2_node, 21, GPIO_ACTIVE_LOW), { } }; static const struct software_node db1100_ads7846_swnode = { .name = "ads7846", - .properties = db1100_ads7846_properties, + .properties = db1100_ads7846_props, }; static struct spi_board_info db1100_spi_info[] __initdata = { @@ -405,32 +399,26 @@ static struct spi_board_info db1100_spi_info[] __initdata = { }, }; -static const struct spi_gpio_platform_data db1100_spictl_pd __initconst = { - .num_chipselect = 1, -}; - /* * Alchemy GPIO 2 has its base at 200 so the GPIO lines * 207 thru 210 are GPIOs at offset 7 thru 10 at this chip. */ static const struct property_entry db1100_spi_dev_properties[] __initconst = { PROPERTY_ENTRY_GPIO("miso-gpios", - &db1100_alchemy2_gpiochip, 7, GPIO_ACTIVE_HIGH), + &alchemy_gpio2_node, 7, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("mosi-gpios", - &db1100_alchemy2_gpiochip, 8, GPIO_ACTIVE_HIGH), + &alchemy_gpio2_node, 8, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("sck-gpios", - &db1100_alchemy2_gpiochip, 9, GPIO_ACTIVE_HIGH), + &alchemy_gpio2_node, 9, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("cs-gpios", - &db1100_alchemy2_gpiochip, 10, GPIO_ACTIVE_HIGH), + &alchemy_gpio2_node, 10, GPIO_ACTIVE_HIGH), { } }; static const struct platform_device_info db1100_spi_dev_info __initconst = { .name = "spi_gpio", .id = 0, - .data = &db1100_spictl_pd, - .size_data = sizeof(db1100_spictl_pd), - .dma_mask = DMA_BIT_MASK(32), + .dma_mask = DMA_BIT_MASK(32), .properties = db1100_spi_dev_properties, }; @@ -483,7 +471,6 @@ int __init db1000_dev_setup(void) pfc |= (1 << 0); /* SSI0 pins as GPIOs */ alchemy_wrsys(pfc, AU1000_SYS_PINFUNC); - software_node_register(&db1100_alchemy2_gpiochip); spi_register_board_info(db1100_spi_info, ARRAY_SIZE(db1100_spi_info)); From 28c4800dd5db722b27034fc192ba544e82217a14 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 26 May 2026 18:40:36 +0200 Subject: [PATCH 1778/5214] MIPS: alchemy: gpr: switch to static device properties Convert I2C-gpio device and GPIO-connected LEDs on GPR board to software nodes/properties, so that support for platform data can be removed from gpio-leds driver (which will rely purely on generic device properties for configuration). Signed-off-by: Dmitry Torokhov [Bartosz: use platform_device_info::swnode] Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/board-gpr.c | 127 ++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/arch/mips/alchemy/board-gpr.c b/arch/mips/alchemy/board-gpr.c index f587c40b6d00..387252337a18 100644 --- a/arch/mips/alchemy/board-gpr.c +++ b/arch/mips/alchemy/board-gpr.c @@ -13,11 +13,11 @@ #include #include #include -#include -#include #include #include #include +#include +#include #include #include #include @@ -161,66 +161,90 @@ static struct platform_device gpr_mtd_device = { /* * LEDs */ -static const struct gpio_led gpr_gpio_leds[] = { - { /* green */ - .name = "gpr:green", - .gpio = 4, - .active_low = 1, - }, - { /* red */ - .name = "gpr:red", - .gpio = 5, - .active_low = 1, - } +static const struct software_node gpr_gpio_leds_node = { + .name = "gpr-leds", }; -static struct gpio_led_platform_data gpr_led_data = { - .num_leds = ARRAY_SIZE(gpr_gpio_leds), - .leds = gpr_gpio_leds, +static const struct property_entry gpr_green_led_props[] = { + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio1_node, 4, GPIO_ACTIVE_LOW), + { } }; -static struct platform_device gpr_led_devices = { - .name = "leds-gpio", - .id = -1, - .dev = { - .platform_data = &gpr_led_data, - } +static const struct software_node gpr_green_led_node = { + .name = "gpr:green", + .parent = &gpr_gpio_leds_node, + .properties = gpr_green_led_props, }; +static const struct property_entry gpr_red_led_props[] = { + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpio1_node, 5, GPIO_ACTIVE_LOW), + { } +}; + +static const struct software_node gpr_red_led_node = { + .name = "gpr:red", + .parent = &gpr_gpio_leds_node, + .properties = gpr_red_led_props, +}; + +static const struct software_node * const gpr_gpio_leds_swnodes[] __initconst = { + &gpr_gpio_leds_node, + &gpr_green_led_node, + &gpr_red_led_node, + NULL +}; + +static void __init gpr_leds_init(void) +{ + const struct platform_device_info pdevinfo = { + .name = "leds-gpio", + .id = PLATFORM_DEVID_NONE, + .swnode = &gpr_gpio_leds_node, + }; + struct platform_device *pd; + int err; + + err = software_node_register_node_group(gpr_gpio_leds_swnodes); + if (err) { + pr_err("failed to register LED software nodes: %d\n", err); + return; + } + + pd = platform_device_register_full(&pdevinfo); + err = PTR_ERR_OR_ZERO(pd); + if (err) + pr_err("failed to create LED device: %d\n", err); +} + /* * I2C */ -static struct gpiod_lookup_table gpr_i2c_gpiod_table = { - .dev_id = "i2c-gpio", - .table = { - /* - * This should be on "GPIO2" which has base at 200 so - * the global numbers 209 and 210 should correspond to - * local offsets 9 and 10. - */ - GPIO_LOOKUP_IDX("alchemy-gpio2", 9, NULL, 0, - GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX("alchemy-gpio2", 10, NULL, 1, - GPIO_ACTIVE_HIGH), - }, +static const struct property_entry gpr_i2c_props[] __initconst = { + PROPERTY_ENTRY_GPIO("sda-gpios", &alchemy_gpio2_node, 9, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("scl-gpios", &alchemy_gpio2_node, 10, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_U32("i2c-gpio,delay-us", 2), /* ~100 kHz */ + PROPERTY_ENTRY_U32("i2c-gpio,timeout-ms", 1000), + PROPERTY_ENTRY_BOOL("i2c-gpio,sda-open-drain"), + PROPERTY_ENTRY_BOOL("i2c-gpio,scl-open-drain"), + { } }; -static struct i2c_gpio_platform_data gpr_i2c_data = { - /* - * The open drain mode is hardwired somewhere or an electrical - * property of the alchemy GPIO controller. - */ - .sda_is_open_drain = 1, - .scl_is_open_drain = 1, - .udelay = 2, /* ~100 kHz */ - .timeout = HZ, +static const struct platform_device_info gpr_i2c_pdev_info __initconst = { + .name = "i2c-gpio", + .id = PLATFORM_DEVID_NONE, + .properties = gpr_i2c_props, }; -static struct platform_device gpr_i2c_device = { - .name = "i2c-gpio", - .id = -1, - .dev.platform_data = &gpr_i2c_data, -}; +static void __init gpr_i2c_init(void) +{ + struct platform_device *pd; + int err; + + pd = platform_device_register_full(&gpr_i2c_pdev_info); + err = PTR_ERR_OR_ZERO(pd); + if (err) + pr_err("failed to create I2C device: %d\n", err); +} static struct i2c_board_info gpr_i2c_info[] __initdata = { { @@ -270,8 +294,6 @@ static struct platform_device gpr_pci_host_dev = { static struct platform_device *gpr_devices[] __initdata = { &gpr_wdt_device, &gpr_mtd_device, - &gpr_i2c_device, - &gpr_led_devices, }; static int __init gpr_pci_init(void) @@ -284,8 +306,9 @@ arch_initcall(gpr_pci_init); static int __init gpr_dev_init(void) { - gpiod_add_lookup_table(&gpr_i2c_gpiod_table); i2c_register_board_info(0, gpr_i2c_info, ARRAY_SIZE(gpr_i2c_info)); + gpr_i2c_init(); + gpr_leds_init(); return platform_add_devices(gpr_devices, ARRAY_SIZE(gpr_devices)); } From 6d96cc123ce33cd74e799c5434440393ed022bb7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 26 May 2026 18:40:37 +0200 Subject: [PATCH 1779/5214] MIPS: alchemy: db1300: switch to static device properties Convert "5way switch" gpio-keys device and smsc911x ethernet controller to use static device properties instead of bespoke platform data structures for configuration. Signed-off-by: Dmitry Torokhov [Bartosz: use platform_device_info::swnode] Tested-by: Manuel Lauss Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Bogendoerfer --- arch/mips/alchemy/common/gpiolib.c | 6 + arch/mips/alchemy/devboards/db1300.c | 218 ++++++++++++------ .../include/asm/mach-au1x00/gpio-au1300.h | 3 + 3 files changed, 156 insertions(+), 71 deletions(-) diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c index 2141eae5ce45..c926cc137561 100644 --- a/arch/mips/alchemy/common/gpiolib.c +++ b/arch/mips/alchemy/common/gpiolib.c @@ -104,9 +104,14 @@ const struct software_node alchemy_gpio2_node = { .name = "alchemy-gpio2", }; +const struct software_node alchemy_gpic_node = { + .name = "alchemy-gpic", +}; + static const struct software_node *alchemy_gpio_node_group[] = { &alchemy_gpio1_node, &alchemy_gpio2_node, + &alchemy_gpic_node, NULL }; @@ -189,6 +194,7 @@ static int __init alchemy_gpio_nodes_init(void) alchemy_gpio_chip[0].fwnode = software_node_fwnode(&alchemy_gpio1_node); alchemy_gpio_chip[1].fwnode = software_node_fwnode(&alchemy_gpio2_node); + au1300_gpiochip.fwnode = software_node_fwnode(&alchemy_gpic_node); return 0; } diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index b46f5e47da2c..a7b8b7e8291f 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -7,10 +7,10 @@ #include #include -#include -#include +#include +#include #include -#include /* KEY_* codes */ +#include #include #include #include @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -237,23 +238,38 @@ static struct resource db1300_eth_res[] = { }, }; -static struct smsc911x_platform_config db1300_eth_config = { - .phy_interface = PHY_INTERFACE_MODE_MII, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, - .flags = SMSC911X_USE_32BIT, +static u8 db1300_eth_macaddr[6]; + +static const struct property_entry db1300_eth_props[] __initconst = { + PROPERTY_ENTRY_U32("reg-io-width", 4), + PROPERTY_ENTRY_U32("reg-shift", 0), + PROPERTY_ENTRY_BOOL("smsc,irq-push-pull"), + PROPERTY_ENTRY_STRING("phy-mode", "mii"), + PROPERTY_ENTRY_U8_ARRAY("local-mac-address", db1300_eth_macaddr), + { } }; -static struct platform_device db1300_eth_dev = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(db1300_eth_res), - .resource = db1300_eth_res, - .dev = { - .platform_data = &db1300_eth_config, - }, +static const struct platform_device_info db1300_eth_info __initconst = { + .name = "smsc911x", + .id = PLATFORM_DEVID_NONE, + .res = db1300_eth_res, + .num_res = ARRAY_SIZE(db1300_eth_res), + .properties = db1300_eth_props, }; +static void __init db1300_eth_init(void) +{ + struct platform_device *pd; + int err; + + prom_get_ethernet_addr(db1300_eth_macaddr); + + pd = platform_device_register_full(&db1300_eth_info); + err = PTR_ERR_OR_ZERO(pd); + if (err) + pr_err("failed to create eth device: %d\n", err); +} + /**********************************************************************/ static struct resource au1300_psc1_res[] = { @@ -352,67 +368,128 @@ static struct platform_device db1300_i2c_dev = { /**********************************************************************/ +static const struct property_entry db1300_5waysw_props[] = { + PROPERTY_ENTRY_BOOL("autorepeat"), + PROPERTY_ENTRY_STRING("label", "db1300-5wayswitch"), + { } +}; + /* proper key assignments when facing the LCD panel. For key assignments * according to the schematics swap up with down and left with right. * I chose to use it to emulate the arrow keys of a keyboard. */ -static struct gpio_keys_button db1300_5waysw_arrowkeys[] = { - { - .code = KEY_DOWN, - .gpio = AU1300_PIN_LCDPWM0, - .type = EV_KEY, - .debounce_interval = 1, - .active_low = 1, - .desc = "5waysw-down", - }, - { - .code = KEY_UP, - .gpio = AU1300_PIN_PSC2SYNC1, - .type = EV_KEY, - .debounce_interval = 1, - .active_low = 1, - .desc = "5waysw-up", - }, - { - .code = KEY_RIGHT, - .gpio = AU1300_PIN_WAKE3, - .type = EV_KEY, - .debounce_interval = 1, - .active_low = 1, - .desc = "5waysw-right", - }, - { - .code = KEY_LEFT, - .gpio = AU1300_PIN_WAKE2, - .type = EV_KEY, - .debounce_interval = 1, - .active_low = 1, - .desc = "5waysw-left", - }, - { - .code = KEY_ENTER, - .gpio = AU1300_PIN_WAKE1, - .type = EV_KEY, - .debounce_interval = 1, - .active_low = 1, - .desc = "5waysw-push", - }, +static const struct software_node db1300_5waysw_node = { + .name = "db1300-5wayswitch", + .properties = db1300_5waysw_props, }; -static struct gpio_keys_platform_data db1300_5waysw_data = { - .buttons = db1300_5waysw_arrowkeys, - .nbuttons = ARRAY_SIZE(db1300_5waysw_arrowkeys), - .rep = 1, - .name = "db1300-5wayswitch", +static const struct property_entry db1300_5waysw_down_props[] = { + PROPERTY_ENTRY_U32("linux,code", KEY_DOWN), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpic_node, + AU1300_PIN_LCDPWM0, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_U32("debounce-interval", 1), + PROPERTY_ENTRY_STRING("label", "5waysw-down"), + { } }; -static struct platform_device db1300_5waysw_dev = { - .name = "gpio-keys", - .dev = { - .platform_data = &db1300_5waysw_data, - }, +static const struct software_node db1300_5waysw_down_node = { + .name = "5waysw-down", + .parent = &db1300_5waysw_node, + .properties = db1300_5waysw_down_props, }; +static const struct property_entry db1300_5waysw_up_props[] = { + PROPERTY_ENTRY_U32("linux,code", KEY_UP), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpic_node, + AU1300_PIN_PSC2SYNC1, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_U32("debounce-interval", 1), + PROPERTY_ENTRY_STRING("label", "5waysw-up"), + { } +}; + +static const struct software_node db1300_5waysw_up_node = { + .name = "5waysw-up", + .parent = &db1300_5waysw_node, + .properties = db1300_5waysw_up_props, +}; + +static const struct property_entry db1300_5waysw_right_props[] = { + PROPERTY_ENTRY_U32("linux,code", KEY_RIGHT), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpic_node, + AU1300_PIN_WAKE3, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_U32("debounce-interval", 1), + PROPERTY_ENTRY_STRING("label", "5waysw-right"), + { } +}; + +static const struct software_node db1300_5waysw_right_node = { + .name = "5waysw-right", + .parent = &db1300_5waysw_node, + .properties = db1300_5waysw_right_props, +}; + +static const struct property_entry db1300_5waysw_left_props[] = { + PROPERTY_ENTRY_U32("linux,code", KEY_LEFT), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpic_node, + AU1300_PIN_WAKE2, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_U32("debounce-interval", 1), + PROPERTY_ENTRY_STRING("label", "5waysw-left"), + { } +}; + +static const struct software_node db1300_5waysw_left_node = { + .name = "5waysw-left", + .parent = &db1300_5waysw_node, + .properties = db1300_5waysw_left_props, +}; + +static const struct property_entry db1300_5waysw_push_props[] = { + PROPERTY_ENTRY_U32("linux,code", KEY_ENTER), + PROPERTY_ENTRY_GPIO("gpios", &alchemy_gpic_node, + AU1300_PIN_WAKE1, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_U32("debounce-interval", 1), + PROPERTY_ENTRY_STRING("label", "5waysw-push"), + { } +}; + +static const struct software_node db1300_5waysw_push_node = { + .name = "5waysw-push", + .parent = &db1300_5waysw_node, + .properties = db1300_5waysw_push_props, +}; + +static const struct software_node * const db1300_5waysw_swnodes[] __initconst = { + &db1300_5waysw_node, + &db1300_5waysw_down_node, + &db1300_5waysw_up_node, + &db1300_5waysw_right_node, + &db1300_5waysw_left_node, + &db1300_5waysw_push_node, + NULL +}; + +static void __init db1300_5waysw_init(void) +{ + const struct platform_device_info pdevinfo = { + .name = "gpio-keys", + .id = PLATFORM_DEVID_NONE, + .swnode = &db1300_5waysw_node, + }; + struct platform_device *pd; + int err; + + err = software_node_register_node_group(db1300_5waysw_swnodes); + if (err) { + pr_err("failed to register 5waysw software nodes: %d\n", err); + return; + } + + pd = platform_device_register_full(&pdevinfo); + err = PTR_ERR_OR_ZERO(pd); + if (err) + pr_err("failed to create 5waysw device: %d\n", err); +} + /**********************************************************************/ static struct pata_platform_info db1300_ide_info = { @@ -765,9 +842,7 @@ static struct platform_driver db1300_wm97xx_driver = { /**********************************************************************/ static struct platform_device *db1300_dev[] __initdata = { - &db1300_eth_dev, &db1300_i2c_dev, - &db1300_5waysw_dev, &db1300_nand_dev, &db1300_ide_dev, #ifdef CONFIG_MMC_AU1X @@ -805,8 +880,6 @@ int __init db1300_dev_setup(void) /* * setup board */ - prom_get_ethernet_addr(&db1300_eth_config.mac[0]); - i2c_register_board_info(0, db1300_i2c_devs, ARRAY_SIZE(db1300_i2c_devs)); @@ -849,6 +922,9 @@ int __init db1300_dev_setup(void) swapped = bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1200_SWAPBOOT; db1x_register_norflash(64 << 20, 2, swapped); + db1300_eth_init(); + db1300_5waysw_init(); + return platform_add_devices(db1300_dev, ARRAY_SIZE(db1300_dev)); } diff --git a/arch/mips/include/asm/mach-au1x00/gpio-au1300.h b/arch/mips/include/asm/mach-au1x00/gpio-au1300.h index b12f37262cfa..c92d3c2a5ef8 100644 --- a/arch/mips/include/asm/mach-au1x00/gpio-au1300.h +++ b/arch/mips/include/asm/mach-au1x00/gpio-au1300.h @@ -13,6 +13,9 @@ #include struct gpio_chip; +struct software_node; + +extern const struct software_node alchemy_gpic_node; /* with the current GPIC design, up to 128 GPIOs are possible. * The only implementation so far is in the Au1300, which has 75 externally From a947fc3ba8a0c097862613bbaaa0bbd06d10b0d2 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 4 May 2026 14:43:14 +0200 Subject: [PATCH 1780/5214] media: rcar-vin: Drop min_queued_buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R-Car VIN driver already uses a scratch buffer to sustain capture operations in absence of a frame buffer provided by userspace. There is no reason to require 4 buffers queued at all times for the driver to operate. Drop min_queued_buffers from the VIN driver to allow single-frame capture operations. Signed-off-by: Jacopo Mondi Reviewed-by: Jai Luthra Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index f9af9177e02f..73cda0e2d45a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1494,7 +1494,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = 4; q->dev = vin->dev; ret = vb2_queue_init(q); From e52b90b8277a433a680a03680f1e344d4a8894e6 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:49 +0100 Subject: [PATCH 1781/5214] media: staging: imx-csi: move media_pipeline to video device The imx-media driver has a single imx_media_device. Attaching the media_pipeline to the imx_media_device prevents the execution of multiple media pipelines on the device. This should be possible as long as the media_pipelines don't use the same pads or pads that be configured while the other media pipeline is streaming. Move the media_pipeline to the imx_media_video_dev to be able to construct media pipelines per imx capture device. If different media pipelines in the media device conflict, the validation will fail. Thus, the pipeline will fail to start and signal an error to user space. Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-capture.c | 8 ++++---- drivers/staging/media/imx/imx-media-utils.c | 3 ++- drivers/staging/media/imx/imx-media.h | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index e9cef7af000a..bfd71d25facc 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -722,8 +722,8 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) goto return_bufs; } - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - true); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, true); if (ret) { dev_err(priv->dev, "pipeline start failed with %d\n", ret); goto return_bufs; @@ -749,8 +749,8 @@ static void capture_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - false); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, false); if (ret) dev_warn(priv->dev, "pipeline stop failed with %d\n", ret); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 1b5af8945e6b..f520529a7cfe 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -749,6 +749,7 @@ EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev); * Turn current pipeline streaming on/off starting from entity. */ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on) { @@ -762,7 +763,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, mutex_lock(&imxmd->md.graph_mutex); if (on) { - ret = __media_pipeline_start(entity->pads, &imxmd->pipe); + ret = __media_pipeline_start(entity->pads, &vdev->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 135daca7d55b..537558a7bece 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -104,6 +104,9 @@ struct imx_media_buffer { struct imx_media_video_dev { struct video_device *vfd; + /* the pipeline object */ + struct media_pipeline pipe; + /* the user format */ struct v4l2_pix_format fmt; /* the compose rectangle */ @@ -145,9 +148,6 @@ struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; - /* the pipeline object */ - struct media_pipeline pipe; - struct mutex mutex; /* protect elements below */ /* master video device list */ @@ -223,6 +223,7 @@ int imx_media_alloc_dma_buf(struct device *dev, int size); int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on); From 262d2e7cfc2805464248a4fa1635636a834f09a9 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:50 +0100 Subject: [PATCH 1782/5214] media: staging: imx-csi: explicitly start media pipeline on pad 0 entity->pads is an array that contains all the pads of an entity. Calling __media_pipeline_start() or __media_pipeline_stop() on the pads, implicitly starts the pipeline with the first pad in this array as origin. Explicitly use the first pad to start the pipeline to make this more obvious to the reader. Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index f520529a7cfe..bd9af147a801 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -754,6 +754,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, bool on) { struct v4l2_subdev *sd; + struct media_pad *pad; int ret = 0; if (!is_media_entity_v4l2_subdev(entity)) @@ -762,17 +763,19 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, mutex_lock(&imxmd->md.graph_mutex); + pad = &entity->pads[0]; + if (on) { - ret = __media_pipeline_start(entity->pads, &vdev->pipe); + ret = __media_pipeline_start(pad, &vdev->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) - __media_pipeline_stop(entity->pads); + __media_pipeline_stop(pad); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (media_pad_pipeline(entity->pads)) - __media_pipeline_stop(entity->pads); + if (media_pad_pipeline(pad)) + __media_pipeline_stop(pad); } out: From e33062c19b65f03353932ae3ff1b6ef672cf1bac Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:51 +0100 Subject: [PATCH 1783/5214] media: staging: imx-csi: use media_pad_is_streaming helper The media_pad_is_streaming() helper is explicitly intended to check whether a pad has been started with media_pipeline_start(). Use it instead of relying on the implicit assumption that a pad with a pipeline is streaming. Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Reviewed-by: Frank Li Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index bd9af147a801..1a2e5e40c99c 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -774,7 +774,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, __media_pipeline_stop(pad); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (media_pad_pipeline(pad)) + if (media_pad_is_streaming(pad)) __media_pipeline_stop(pad); } From a96fcde9fa9a6696991d1f172a8bc2bade82a6d9 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sun, 3 May 2026 20:30:26 +0530 Subject: [PATCH 1784/5214] media: staging: imx: remove unnecessary out-of-memory error message Remove dev_err() call after dma_alloc_coherent() failure. checkpatch.pl reports this as an unnecessary out-of-memory message because failure is already conveyed by returning -ENOMEM, and the current message does not provide additional useful debugging information. Signed-off-by: Shyam Sunder Reddy Padira Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 1a2e5e40c99c..f119477cac6b 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -589,10 +589,8 @@ int imx_media_alloc_dma_buf(struct device *dev, buf->len = PAGE_ALIGN(size); buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys, GFP_DMA | GFP_KERNEL); - if (!buf->virt) { - dev_err(dev, "%s: failed\n", __func__); + if (!buf->virt) return -ENOMEM; - } return 0; } From 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b Mon Sep 17 00:00:00 2001 From: Arseniy Krasnov Date: Tue, 5 May 2026 11:30:30 +0300 Subject: [PATCH 1785/5214] mtd: rawnand: fix condition in 'nand_select_target()' 'cs' here must be in range [0:nanddev_ntargets[. Cc: stable@vger.kernel.org Fixes: 32813e288414 ("mtd: rawnand: Get rid of chip->numchips") Signed-off-by: Arseniy Krasnov Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index d6d3e17ab407..e4e1383bd5e1 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs) * cs should always lie between 0 and nanddev_ntargets(), when that's * not the case it's a bug and the caller should be fixed. */ - if (WARN_ON(cs > nanddev_ntargets(&chip->base))) + if (WARN_ON(cs >= nanddev_ntargets(&chip->base))) return; chip->cur_cs = cs; From 801f11633d5eb2021dd1a4b6a4c14479c54af244 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 6 May 2026 15:42:47 +0100 Subject: [PATCH 1786/5214] mtd: ts5500_flash: Remove mapping since board is no longer supported Since commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), this board is no longer supported. Remove the mtd map too. Signed-off-by: Sean Young Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 18 ------ drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/ts5500_flash.c | 108 -------------------------------- 3 files changed, 127 deletions(-) delete mode 100644 drivers/mtd/maps/ts5500_flash.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 99d5ff9a1fbe..e48221e97f8d 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -141,24 +141,6 @@ config MTD_NETSC520 demonstration board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. -config MTD_TS5500 - tristate "JEDEC Flash device mapped on Technologic Systems TS-5500" - depends on TS5500 || COMPILE_TEST - select MTD_JEDECPROBE - select MTD_CFI_AMDSTD - help - This provides a driver for the on-board flash of the Technologic - System's TS-5500 board. The 2MB flash is split into 3 partitions - which are accessed as separate MTD devices. - - mtd0 and mtd2 are the two BIOS drives, which use the resident - flash disk (RFD) flash translation layer. - - mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL. - - Note that jumper 3 ("Write Enable Drive A") must be set - otherwise detection won't succeed. - config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 51af1d2ebd52..46ff278006a7 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -28,7 +28,6 @@ obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o -obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c deleted file mode 100644 index 70d6e865f555..000000000000 --- a/drivers/mtd/maps/ts5500_flash.c +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board - * - * Copyright (C) 2004 Sean Young - * - * Note: - * - In order for detection to work, jumper 3 must be set. - * - Drive A and B use the resident flash disk (RFD) flash translation layer. - * - If you have created your own jffs file system and the bios overwrites - * it during boot, try disabling Drive A: and B: in the boot order. - */ - -#include -#include -#include -#include -#include -#include -#include - - -#define WINDOW_ADDR 0x09400000 -#define WINDOW_SIZE 0x00200000 - -static struct map_info ts5500_map = { - .name = "TS-5500 Flash", - .size = WINDOW_SIZE, - .bankwidth = 1, - .phys = WINDOW_ADDR -}; - -static const struct mtd_partition ts5500_partitions[] = { - { - .name = "Drive A", - .offset = 0, - .size = 0x0e0000 - }, - { - .name = "BIOS", - .offset = 0x0e0000, - .size = 0x020000, - }, - { - .name = "Drive B", - .offset = 0x100000, - .size = 0x100000 - } -}; - -#define NUM_PARTITIONS ARRAY_SIZE(ts5500_partitions) - -static struct mtd_info *mymtd; - -static int __init init_ts5500_map(void) -{ - int rc = 0; - - ts5500_map.virt = ioremap(ts5500_map.phys, ts5500_map.size); - - if (!ts5500_map.virt) { - printk(KERN_ERR "Failed to ioremap\n"); - rc = -EIO; - goto err2; - } - - simple_map_init(&ts5500_map); - - mymtd = do_map_probe("jedec_probe", &ts5500_map); - if (!mymtd) - mymtd = do_map_probe("map_rom", &ts5500_map); - - if (!mymtd) { - rc = -ENXIO; - goto err1; - } - - mymtd->owner = THIS_MODULE; - mtd_device_register(mymtd, ts5500_partitions, NUM_PARTITIONS); - - return 0; - -err1: - iounmap(ts5500_map.virt); -err2: - return rc; -} - -static void __exit cleanup_ts5500_map(void) -{ - if (mymtd) { - mtd_device_unregister(mymtd); - map_destroy(mymtd); - } - - if (ts5500_map.virt) { - iounmap(ts5500_map.virt); - ts5500_map.virt = NULL; - } -} - -module_init(init_ts5500_map); -module_exit(cleanup_ts5500_map); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sean Young "); -MODULE_DESCRIPTION("MTD map driver for Technology Systems TS-5500 board"); - From b60e5b5738d86735ebdb8b952054194b636b1cf6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 6 May 2026 15:42:48 +0100 Subject: [PATCH 1787/5214] mtd: netsc520: Remove mapping since board is no longer supported Since commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), this board is no longer supported. Remove the mtd map too. Signed-off-by: Sean Young Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 8 --- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/netsc520.c | 127 ------------------------------------ 3 files changed, 136 deletions(-) delete mode 100644 drivers/mtd/maps/netsc520.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index e48221e97f8d..b34f3c34c626 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -133,14 +133,6 @@ config MTD_SC520CDP Dual-in-line JEDEC chip. This 'mapping' driver supports that arrangement, implementing three MTD devices. -config MTD_NETSC520 - tristate "CFI Flash device mapped on AMD NetSc520" - depends on (MELAN || COMPILE_TEST) && MTD_CFI - help - This enables access routines for the flash chips on the AMD NetSc520 - demonstration board. If you have one of these boards and would like - to use the flash chips on it, say 'Y'. - config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 46ff278006a7..e8b5caa17ccc 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o -obj-$(CONFIG_MTD_NETSC520) += netsc520.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c deleted file mode 100644 index 0bb651624f05..000000000000 --- a/drivers/mtd/maps/netsc520.c +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* netsc520.c -- MTD map driver for AMD NetSc520 Demonstration Board - * - * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) - * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH - * - * The NetSc520 is a demonstration board for the Elan Sc520 processor available - * from AMD. It has a single back of 16 megs of 32-bit Flash ROM and another - * 16 megs of SDRAM. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* -** The single, 16 megabyte flash bank is divided into four virtual -** partitions. The first partition is 768 KiB and is intended to -** store the kernel image loaded by the bootstrap loader. The second -** partition is 256 KiB and holds the BIOS image. The third -** partition is 14.5 MiB and is intended for the flash file system -** image. The last partition is 512 KiB and contains another copy -** of the BIOS image and the reset vector. -** -** Only the third partition should be mounted. The first partition -** should not be mounted, but it can erased and written to using the -** MTD character routines. The second and fourth partitions should -** not be touched - it is possible to corrupt the BIOS image by -** mounting these partitions, and potentially the board will not be -** recoverable afterwards. -*/ - -/* partition_info gives details on the logical partitions that the split the - * single flash device into. If the size if zero we use up to the end of the - * device. */ -static const struct mtd_partition partition_info[] = { - { - .name = "NetSc520 boot kernel", - .offset = 0, - .size = 0xc0000 - }, - { - .name = "NetSc520 Low BIOS", - .offset = 0xc0000, - .size = 0x40000 - }, - { - .name = "NetSc520 file system", - .offset = 0x100000, - .size = 0xe80000 - }, - { - .name = "NetSc520 High BIOS", - .offset = 0xf80000, - .size = 0x80000 - }, -}; -#define NUM_PARTITIONS ARRAY_SIZE(partition_info) - -#define WINDOW_SIZE 0x00100000 -#define WINDOW_ADDR 0x00200000 - -static struct map_info netsc520_map = { - .name = "netsc520 Flash Bank", - .size = WINDOW_SIZE, - .bankwidth = 4, - .phys = WINDOW_ADDR, -}; - -#define NUM_FLASH_BANKS ARRAY_SIZE(netsc520_map) - -static struct mtd_info *mymtd; - -static int __init init_netsc520(void) -{ - printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n", - (unsigned long long)netsc520_map.size, - (unsigned long long)netsc520_map.phys); - netsc520_map.virt = ioremap(netsc520_map.phys, netsc520_map.size); - - if (!netsc520_map.virt) { - printk("Failed to ioremap\n"); - return -EIO; - } - - simple_map_init(&netsc520_map); - - mymtd = do_map_probe("cfi_probe", &netsc520_map); - if(!mymtd) - mymtd = do_map_probe("map_ram", &netsc520_map); - if(!mymtd) - mymtd = do_map_probe("map_rom", &netsc520_map); - - if (!mymtd) { - iounmap(netsc520_map.virt); - return -ENXIO; - } - - mymtd->owner = THIS_MODULE; - mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); - return 0; -} - -static void __exit cleanup_netsc520(void) -{ - if (mymtd) { - mtd_device_unregister(mymtd); - map_destroy(mymtd); - } - if (netsc520_map.virt) { - iounmap(netsc520_map.virt); - netsc520_map.virt = NULL; - } -} - -module_init(init_netsc520); -module_exit(cleanup_netsc520); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mark Langsdorf "); -MODULE_DESCRIPTION("MTD map driver for AMD NetSc520 Demonstration Board"); From f4aeb151b8b025202a3cc55fabc45234700ed470 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 6 May 2026 15:42:49 +0100 Subject: [PATCH 1788/5214] mtd: sc520cdp: Remove mapping since board is no longer supported Since commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), this board is no longer supported. Remove the mtd map too. Signed-off-by: Sean Young Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 8 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/sc520cdp.c | 294 ------------------------------------ 3 files changed, 303 deletions(-) delete mode 100644 drivers/mtd/maps/sc520cdp.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index b34f3c34c626..db63b5af4b39 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -125,14 +125,6 @@ config MTD_SUN_UFLASH Sun Microsystems boardsets. This driver will require CFI support in the kernel, so if you did not enable CFI previously, do that now. -config MTD_SC520CDP - tristate "CFI Flash device mapped on AMD SC520 CDP" - depends on (MELAN || COMPILE_TEST) && MTD_CFI - help - The SC520 CDP board has two banks of CFI-compliant chips and one - Dual-in-line JEDEC chip. This 'mapping' driver supports that - arrangement, implementing three MTD devices. - config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index e8b5caa17ccc..c545d1e68e91 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o -obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c deleted file mode 100644 index 8ef7aec634c7..000000000000 --- a/drivers/mtd/maps/sc520cdp.c +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform - * - * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH - * - * The SC520CDP is an evaluation board for the Elan SC520 processor available - * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, - * and up to 512 KiB of 8-bit DIL Flash ROM. - * For details see https://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* -** The Embedded Systems BIOS decodes the first FLASH starting at -** 0x8400000. This is a *terrible* place for it because accessing -** the flash at this location causes the A22 address line to be high -** (that's what 0x8400000 binary's ought to be). But this is the highest -** order address line on the raw flash devices themselves!! -** This causes the top HALF of the flash to be accessed first. Beyond -** the physical limits of the flash, the flash chip aliases over (to -** 0x880000 which causes the bottom half to be accessed. This splits the -** flash into two and inverts it! If you then try to access this from another -** program that does NOT do this insanity, then you *will* access the -** first half of the flash, but not find what you expect there. That -** stuff is in the *second* half! Similarly, the address used by the -** BIOS for the second FLASH bank is also quite a bad choice. -** If REPROGRAM_PAR is defined below (the default), then this driver will -** choose more useful addresses for the FLASH banks by reprogramming the -** responsible PARxx registers in the SC520's MMCR region. This will -** cause the settings to be incompatible with the BIOS's settings, which -** shouldn't be a problem since you are running Linux, (i.e. the BIOS is -** not much use anyway). However, if you need to be compatible with -** the BIOS for some reason, just undefine REPROGRAM_PAR. -*/ -#define REPROGRAM_PAR - - - -#ifdef REPROGRAM_PAR - -/* These are the addresses we want.. */ -#define WINDOW_ADDR_0 0x08800000 -#define WINDOW_ADDR_1 0x09000000 -#define WINDOW_ADDR_2 0x09800000 - -/* .. and these are the addresses the BIOS gives us */ -#define WINDOW_ADDR_0_BIOS 0x08400000 -#define WINDOW_ADDR_1_BIOS 0x08c00000 -#define WINDOW_ADDR_2_BIOS 0x09400000 - -#else - -#define WINDOW_ADDR_0 0x08400000 -#define WINDOW_ADDR_1 0x08C00000 -#define WINDOW_ADDR_2 0x09400000 - -#endif - -#define WINDOW_SIZE_0 0x00800000 -#define WINDOW_SIZE_1 0x00800000 -#define WINDOW_SIZE_2 0x00080000 - - -static struct map_info sc520cdp_map[] = { - { - .name = "SC520CDP Flash Bank #0", - .size = WINDOW_SIZE_0, - .bankwidth = 4, - .phys = WINDOW_ADDR_0 - }, - { - .name = "SC520CDP Flash Bank #1", - .size = WINDOW_SIZE_1, - .bankwidth = 4, - .phys = WINDOW_ADDR_1 - }, - { - .name = "SC520CDP DIL Flash", - .size = WINDOW_SIZE_2, - .bankwidth = 1, - .phys = WINDOW_ADDR_2 - }, -}; - -#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map) - -static struct mtd_info *mymtd[NUM_FLASH_BANKS]; -static struct mtd_info *merged_mtd; - -#ifdef REPROGRAM_PAR - -/* -** The SC520 MMCR (memory mapped control register) region resides -** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers -** are at offset 0x88 in the MMCR: -*/ -#define SC520_MMCR_BASE 0xFFFEF000 -#define SC520_MMCR_EXTENT 0x1000 -#define SC520_PAR(x) ((0x88/sizeof(unsigned long)) + (x)) -#define NUM_SC520_PAR 16 /* total number of PAR registers */ - -/* -** The highest three bits in a PAR register determine what target -** device is controlled by this PAR. Here, only ROMCS? and BOOTCS -** devices are of interest. -*/ -#define SC520_PAR_BOOTCS (0x4<<29) -#define SC520_PAR_ROMCS0 (0x5<<29) -#define SC520_PAR_ROMCS1 (0x6<<29) -#define SC520_PAR_TRGDEV (0x7<<29) - -/* -** Bits 28 thru 26 determine some attributes for the -** region controlled by the PAR. (We only use non-cacheable) -*/ -#define SC520_PAR_WRPROT (1<<26) /* write protected */ -#define SC520_PAR_NOCACHE (1<<27) /* non-cacheable */ -#define SC520_PAR_NOEXEC (1<<28) /* code execution denied */ - - -/* -** Bit 25 determines the granularity: 4K or 64K -*/ -#define SC520_PAR_PG_SIZ4 (0<<25) -#define SC520_PAR_PG_SIZ64 (1<<25) - -/* -** Build a value to be written into a PAR register. -** We only need ROM entries, 64K page size: -*/ -#define SC520_PAR_ENTRY(trgdev, address, size) \ - ((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \ - (address) >> 16 | (((size) >> 16) - 1) << 14) - -struct sc520_par_table -{ - unsigned long trgdev; - unsigned long new_par; - unsigned long default_address; -}; - -static const struct sc520_par_table par_table[NUM_FLASH_BANKS] = -{ - { /* Flash Bank #0: selected by ROMCS0 */ - SC520_PAR_ROMCS0, - SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0), - WINDOW_ADDR_0_BIOS - }, - { /* Flash Bank #1: selected by ROMCS1 */ - SC520_PAR_ROMCS1, - SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1), - WINDOW_ADDR_1_BIOS - }, - { /* DIL (BIOS) Flash: selected by BOOTCS */ - SC520_PAR_BOOTCS, - SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2), - WINDOW_ADDR_2_BIOS - } -}; - - -static void sc520cdp_setup_par(void) -{ - unsigned long __iomem *mmcr; - unsigned long mmcr_val; - int i, j; - - /* map in SC520's MMCR area */ - mmcr = ioremap(SC520_MMCR_BASE, SC520_MMCR_EXTENT); - if(!mmcr) { /* ioremap failed: skip the PAR reprogramming */ - /* force physical address fields to BIOS defaults: */ - for(i = 0; i < NUM_FLASH_BANKS; i++) - sc520cdp_map[i].phys = par_table[i].default_address; - return; - } - - /* - ** Find the PARxx registers that are responsible for activating - ** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a - ** new value from the table. - */ - for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */ - for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */ - mmcr_val = readl(&mmcr[SC520_PAR(j)]); - /* if target device field matches, reprogram the PAR */ - if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev) - { - writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]); - break; - } - } - if(j == NUM_SC520_PAR) - { /* no matching PAR found: try default BIOS address */ - printk(KERN_NOTICE "Could not find PAR responsible for %s\n", - sc520cdp_map[i].name); - printk(KERN_NOTICE "Trying default address 0x%lx\n", - par_table[i].default_address); - sc520cdp_map[i].phys = par_table[i].default_address; - } - } - iounmap(mmcr); -} -#endif - - -static int __init init_sc520cdp(void) -{ - int i, j, devices_found = 0; - -#ifdef REPROGRAM_PAR - /* reprogram PAR registers so flash appears at the desired addresses */ - sc520cdp_setup_par(); -#endif - - for (i = 0; i < NUM_FLASH_BANKS; i++) { - printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", - (unsigned long long)sc520cdp_map[i].size, - (unsigned long long)sc520cdp_map[i].phys); - - sc520cdp_map[i].virt = ioremap(sc520cdp_map[i].phys, sc520cdp_map[i].size); - - if (!sc520cdp_map[i].virt) { - printk("Failed to ioremap\n"); - for (j = 0; j < i; j++) { - if (mymtd[j]) { - map_destroy(mymtd[j]); - iounmap(sc520cdp_map[j].virt); - } - } - return -EIO; - } - - simple_map_init(&sc520cdp_map[i]); - - mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); - if(!mymtd[i]) - mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); - if(!mymtd[i]) - mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); - - if (mymtd[i]) { - mymtd[i]->owner = THIS_MODULE; - ++devices_found; - } - else { - iounmap(sc520cdp_map[i].virt); - } - } - if(devices_found >= 2) { - /* Combine the two flash banks into a single MTD device & register it: */ - merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1"); - if(merged_mtd) - mtd_device_register(merged_mtd, NULL, 0); - } - if(devices_found == 3) /* register the third (DIL-Flash) device */ - mtd_device_register(mymtd[2], NULL, 0); - return(devices_found ? 0 : -ENXIO); -} - -static void __exit cleanup_sc520cdp(void) -{ - int i; - - if (merged_mtd) { - mtd_device_unregister(merged_mtd); - mtd_concat_destroy(merged_mtd); - } - if (mymtd[2]) - mtd_device_unregister(mymtd[2]); - - for (i = 0; i < NUM_FLASH_BANKS; i++) { - if (mymtd[i]) - map_destroy(mymtd[i]); - if (sc520cdp_map[i].virt) { - iounmap(sc520cdp_map[i].virt); - sc520cdp_map[i].virt = NULL; - } - } -} - -module_init(init_sc520cdp); -module_exit(cleanup_sc520cdp); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH"); -MODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform"); From cf496ebf1380bde700c5d1790c31919eea2f4851 Mon Sep 17 00:00:00 2001 From: Cheng Ming Lin Date: Tue, 5 May 2026 09:34:51 +0800 Subject: [PATCH 1789/5214] dt-bindings: mtd: nand: Add nand-randomizer property Add the 'nand-randomizer' property to control the data randomizer feature. This is used to improve data reliability by reducing cell-to-cell interference. Depending on the hardware architecture, this property is designed to be generic and can apply to either the NAND chip's internal randomizer or the hardware randomizer engine embedded in the NAND host controller. This property is defined as a uint32 enum (0 or 1) instead of a simple boolean. This design choice explicitly supports the "not present" case. If the property is omitted, the driver will not interfere and will leave the randomizer in its current state (e.g., as already configured by the bootloader or hardware default). Signed-off-by: Cheng Ming Lin Reviewed-by: Rob Herring (Arm) Signed-off-by: Miquel Raynal --- Documentation/devicetree/bindings/mtd/nand-chip.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/mtd/nand-chip.yaml b/Documentation/devicetree/bindings/mtd/nand-chip.yaml index 8800d1d07266..effdc4f99017 100644 --- a/Documentation/devicetree/bindings/mtd/nand-chip.yaml +++ b/Documentation/devicetree/bindings/mtd/nand-chip.yaml @@ -23,6 +23,15 @@ properties: description: Contains the chip-select IDs. + nand-randomizer: + description: | + Control the data randomizer feature. + 0: Disable randomizer. + 1: Enable randomizer. + If absent, the current hardware state is left unchanged. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + required: - reg From 54e1bc80af0c993afc6a2283722f8d4535b10d40 Mon Sep 17 00:00:00 2001 From: Cheng Ming Lin Date: Tue, 5 May 2026 09:34:52 +0800 Subject: [PATCH 1790/5214] mtd: spinand: Add support for randomizer This patch adds support for the randomizer feature. It introduces a 'set_randomizer' callback in 'struct spinand_info' and 'struct spinand_device'. If a driver implements this callback, the core will invoke it during device initialization (spinand_init) to enable or disable the randomizer feature based on the device tree configuration. Signed-off-by: Cheng Ming Lin Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 20 ++++++++++++++++++++ include/linux/mtd/spinand.h | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f1084d5e04b9..f86786344d52 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1328,6 +1328,22 @@ static int spinand_create_dirmaps(struct spinand_device *spinand) return 0; } +static int spinand_randomizer_init(struct spinand_device *spinand) +{ + struct device_node *np = spinand->spimem->spi->dev.of_node; + u32 rand_val; + int ret; + + if (!spinand->set_randomizer) + return 0; + + ret = of_property_read_u32(np, "nand-randomizer", &rand_val); + if (ret) + return 0; + + return spinand->set_randomizer(spinand, rand_val); +} + static const struct nand_ops spinand_ops = { .erase = spinand_erase, .markbad = spinand_markbad, @@ -1619,6 +1635,7 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->user_otp = &table[i].user_otp; spinand->read_retries = table[i].read_retries; spinand->set_read_retry = table[i].set_read_retry; + spinand->set_randomizer = table[i].set_randomizer; /* I/O variants selection with single-spi SDR commands */ @@ -1942,6 +1959,9 @@ static int spinand_init(struct spinand_device *spinand) * ECC initialization must have happened previously. */ spinand_cont_read_init(spinand); + ret = spinand_randomizer_init(spinand); + if (ret) + goto err_cleanup_nanddev; mtd->_read_oob = spinand_mtd_read; mtd->_write_oob = spinand_mtd_write; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 44f4347104d6..ec6efcfeef83 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -593,6 +593,7 @@ enum spinand_bus_interface { * @user_otp: SPI NAND user OTP info. * @read_retries: the number of read retry modes supported * @set_read_retry: enable/disable read retry for data recovery + * @set_randomizer: enable/disable randomizer support * * Each SPI NAND manufacturer driver should have a spinand_info table * describing all the chips supported by the driver. @@ -622,6 +623,8 @@ struct spinand_info { unsigned int read_retries; int (*set_read_retry)(struct spinand_device *spinand, unsigned int read_retry); + int (*set_randomizer)(struct spinand_device *spinand, + bool enable); }; #define SPINAND_ID(__method, ...) \ @@ -686,6 +689,9 @@ struct spinand_info { .read_retries = __read_retries, \ .set_read_retry = __set_read_retry +#define SPINAND_RANDOMIZER(__set_randomizer) \ + .set_randomizer = __set_randomizer + #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ __flags, ...) \ { \ @@ -771,6 +777,7 @@ struct spinand_mem_ops { * @user_otp: SPI NAND user OTP info. * @read_retries: the number of read retry modes supported * @set_read_retry: Enable/disable the read retry feature + * @set_randomizer: Enable/disable the randomizer feature */ struct spinand_device { struct nand_device base; @@ -804,6 +811,8 @@ struct spinand_device { bool cont_read_possible; int (*set_cont_read)(struct spinand_device *spinand, bool enable); + int (*set_randomizer)(struct spinand_device *spinand, + bool enable); const struct spinand_fact_otp *fact_otp; const struct spinand_user_otp *user_otp; From 8ac45f4ff182586bb06fd9b2dfa0dff2af0734a1 Mon Sep 17 00:00:00 2001 From: Cheng Ming Lin Date: Tue, 5 May 2026 09:34:53 +0800 Subject: [PATCH 1791/5214] mtd: spinand: macronix: Enable randomizer support Implement the 'set_randomizer' callback for Macronix SPI NAND chips. The randomizer is enabled by setting bit 1 of the Configuration Register (address 0x10). This patch adds support for the following chips: - MX35LFxG24AD series - MX35UFxG24AD series When the randomizer is enabled, data is scrambled internally during program operations and automatically descrambled during read operations. This helps reduce bit errors caused by program disturbance. Please refer to the following link for randomizer feature: Link: https://www.mxic.com.tw/Lists/ApplicationNote/Attachments/2151/AN1051V1-The%20Introduction%20of%20Randomizer%20Feature%20on%20MX30xFxG28AD_MX35xFxG24AD.pdf Signed-off-by: Cheng Ming Lin Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/macronix.c | 38 ++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 67cafa1bb8ef..7dfcc34e9b72 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -14,6 +14,8 @@ #define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr) #define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), eccsr) #define MACRONIX_CFG_CONT_READ BIT(2) +#define MACRONIX_CFG_RANDOMIZER_EN BIT(1) +#define MACRONIX_FEATURE_ADDR_RANDOMIZER 0x10 #define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70 #define MACRONIX_NUM_READ_RETRY_MODES 5 @@ -170,6 +172,12 @@ static int macronix_set_read_retry(struct spinand_device *spinand, return spi_mem_exec_op(spinand->spimem, &op); } +static int macronix_set_randomizer(struct spinand_device *spinand, bool enable) +{ + return spinand_write_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, + enable ? MACRONIX_CFG_RANDOMIZER_EN : 0); +} + static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO("MX35LF1GE4AB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), @@ -231,7 +239,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35LF2G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), @@ -243,7 +252,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35LF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -254,7 +264,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35LF4G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), @@ -266,7 +277,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35LF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -277,7 +289,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX31LF1GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -327,7 +340,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35UF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -340,7 +354,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35UF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -381,7 +396,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35UF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -394,7 +410,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35UF2GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -444,7 +461,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, - macronix_set_read_retry)), + macronix_set_read_retry), + SPINAND_RANDOMIZER(macronix_set_randomizer)), SPINAND_INFO("MX35UF1GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), From 79d1661502c6e4b6f626185cef72cf2fa78116e1 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 18 May 2026 13:45:20 +0200 Subject: [PATCH 1792/5214] mtd: maps: vmu-flash: fix fault in unaligned fixup Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs, instead of kmalloc_obj() / kmalloc_objs() to prevent access to uninitialized data. Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at mtd_get_fact_prot_info. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal --- drivers/mtd/maps/vmu-flash.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 75e06d249ce9..b76d0609d1b7 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -609,7 +609,7 @@ static int vmu_connect(struct maple_device *mdev) basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); - card = kmalloc_obj(struct memcard); + card = kzalloc_obj(struct memcard); if (!card) { error = -ENOMEM; goto fail_nomem; @@ -627,13 +627,13 @@ static int vmu_connect(struct maple_device *mdev) * Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we */ - card->parts = kmalloc_objs(struct vmupart, card->partitions); + card->parts = kzalloc_objs(struct vmupart, card->partitions); if (!card->parts) { error = -ENOMEM; goto fail_partitions; } - card->mtd = kmalloc_objs(struct mtd_info, card->partitions); + card->mtd = kzalloc_objs(struct mtd_info, card->partitions); if (!card->mtd) { error = -ENOMEM; goto fail_mtd_info; From 357e3b8e3a8769ba36eb8ec5e053e4825f1a9329 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 18 May 2026 13:45:21 +0200 Subject: [PATCH 1793/5214] mtd: maps: vmu-flash: fix NULL pointer dereference in initialization The mtd_info contains a struct device, which must be linked to its parent. Without this, the initialization of the MTD fails with a NULL pointer dereference. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal --- drivers/mtd/maps/vmu-flash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index b76d0609d1b7..10244e6731d0 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -547,6 +547,7 @@ static void vmu_queryblocks(struct mapleq *mq) mpart->partition = card->partition; mtd_cur->priv = mpart; mtd_cur->owner = THIS_MODULE; + mtd_cur->dev.parent = &mdev->dev; pcache = kzalloc_obj(struct vmu_cache); if (!pcache) From 8e4531667d718e2e9b193928cf9b2497fa0d01ef Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 22 May 2026 11:17:39 +0200 Subject: [PATCH 1794/5214] mtd: rawnand: Pause continuous reads at block boundaries Some chips do not support sequential cached reads past block boundaries, like Winbond. In practice when using UBI, this should very rarely happen, but let's make sure it never happens. Cc: stable@vger.kernel.org Fixes: 003fe4b9545b ("mtd: rawnand: Support for sequential cache reads") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e4e1383bd5e1..a5b278ab9384 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1216,32 +1216,32 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, return nand_exec_op(chip, &op); } -static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun) +static unsigned int rawnand_last_page_of_block(unsigned int ppb, unsigned int block) { - /* lun is expected to be very small */ - return (lun * pages_per_lun) + pages_per_lun - 1; + /* block is expected to be very small */ + return (block * ppb) + ppb - 1; } static void rawnand_cap_cont_reads(struct nand_chip *chip) { struct nand_memory_organization *memorg; - unsigned int ppl, first_lun, last_lun; + unsigned int ppb, first_block, last_block; memorg = nanddev_get_memorg(&chip->base); - ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun; - first_lun = chip->cont_read.first_page / ppl; - last_lun = chip->cont_read.last_page / ppl; + ppb = memorg->pages_per_eraseblock; + first_block = chip->cont_read.first_page / ppb; + last_block = chip->cont_read.last_page / ppb; - /* Prevent sequential cache reads across LUN boundaries */ - if (first_lun != last_lun) - chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun); + /* Prevent sequential cache reads across block boundaries */ + if (first_block != last_block) + chip->cont_read.pause_page = rawnand_last_page_of_block(ppb, first_block); else chip->cont_read.pause_page = chip->cont_read.last_page; if (chip->cont_read.first_page == chip->cont_read.pause_page) { chip->cont_read.first_page++; chip->cont_read.pause_page = min(chip->cont_read.last_page, - rawnand_last_page_of_lun(ppl, first_lun + 1)); + rawnand_last_page_of_block(ppb, first_block + 1)); } if (chip->cont_read.first_page >= chip->cont_read.last_page) From 7fbdbc7d028a20a78b7d28a9510a216c76b5fbfd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 25 May 2026 15:04:40 -0700 Subject: [PATCH 1795/5214] mtd: rawnand: qcom: embed nand_controller into qcom_nand_controller The qcom_nand_controller had a struct nand_controller *controller pointer that was assigned to (struct nand_controller *)&nandc[1], with the allocation oversized by sizeof(*controller) to make room. get_qcom_nand_controller() then walked backwards from chip->controller using sizeof()-based arithmetic to recover the enclosing nandc. Embed the nand_controller directly into qcom_nand_controller and use container_of() in get_qcom_nand_controller(). The header now needs the full rawnand.h definition rather than a forward declaration. Assisted-by: Claude:Opus-4.7 Signed-off-by: Rosen Penev Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 16 ++++++---------- include/linux/mtd/nand-qpic-common.h | 4 +++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b7e79b76654d..4b80ce084d9a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -128,8 +128,8 @@ static struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) static struct qcom_nand_controller * get_qcom_nand_controller(struct nand_chip *chip) { - return (struct qcom_nand_controller *) - ((u8 *)chip->controller - sizeof(struct qcom_nand_controller)); + return container_of(chip->controller, struct qcom_nand_controller, + controller); } static u32 nandc_read(struct qcom_nand_controller *nandc, int offset) @@ -2034,8 +2034,8 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) { u32 nand_ctrl; - nand_controller_init(nandc->controller); - nandc->controller->ops = &qcom_nandc_ops; + nand_controller_init(&nandc->controller); + nandc->controller.ops = &qcom_nandc_ops; /* kill onenand */ if (!nandc->props->nandc_part_of_qpic) @@ -2175,7 +2175,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, chip->legacy.block_bad = qcom_nandc_block_bad; chip->legacy.block_markbad = qcom_nandc_block_markbad; - chip->controller = nandc->controller; + chip->controller = &nandc->controller; chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA | NAND_SKIP_BBTSCAN; @@ -2256,21 +2256,17 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev) static int qcom_nandc_probe(struct platform_device *pdev) { struct qcom_nand_controller *nandc; - struct nand_controller *controller; const void *dev_data; struct device *dev = &pdev->dev; struct resource *res; int ret; - nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc) + sizeof(*controller), - GFP_KERNEL); + nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); if (!nandc) return -ENOMEM; - controller = (struct nand_controller *)&nandc[1]; platform_set_drvdata(pdev, nandc); nandc->dev = dev; - nandc->controller = controller; dev_data = of_device_get_match_data(dev); if (!dev_data) { diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h index e8201d1b7cf9..006ca8c978a9 100644 --- a/include/linux/mtd/nand-qpic-common.h +++ b/include/linux/mtd/nand-qpic-common.h @@ -9,6 +9,8 @@ #ifndef __MTD_NAND_QPIC_COMMON_H__ #define __MTD_NAND_QPIC_COMMON_H__ +#include + /* NANDc reg offsets */ #define NAND_FLASH_CMD 0x00 #define NAND_ADDR0 0x04 @@ -394,7 +396,7 @@ struct qcom_nand_controller { const struct qcom_nandc_props *props; - struct nand_controller *controller; + struct nand_controller controller; struct qpic_spi_nand *qspi; struct list_head host_list; From 19ed11aee966d91beebdef9d32ce926474872f79 Mon Sep 17 00:00:00 2001 From: Bastien Curutchet Date: Tue, 26 May 2026 09:10:00 +0200 Subject: [PATCH 1796/5214] mtd: rawnand: pl353: fix probe resource allocation During probe(), the devm_ioremap() is called with the parent device instead of the current one. So when the module is unloaded, the register area isn't released. Target the pl35x device in the devm_ioremap() instead of its parent. Cc: stable@vger.kernel.org Fixes: 08d8c62164a3 ("mtd: rawnand: pl353: Add support for the ARM PL353 SMC NAND controller") Signed-off-by: Bastien Curutchet Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/pl35x-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index f2c65eb7a8d9..06f8f1e14b9c 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -1155,7 +1155,7 @@ static int pl35x_nand_probe(struct platform_device *pdev) nfc->controller.ops = &pl35x_nandc_ops; INIT_LIST_HEAD(&nfc->chips); - nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res); + nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res); if (IS_ERR(nfc->conf_regs)) return PTR_ERR(nfc->conf_regs); From ad1bcd3469bdb2c5d75c8c29f1944b3ac0f12f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 22 May 2026 19:32:34 +0200 Subject: [PATCH 1797/5214] mtd: Consistently define pci_device_ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initializing a pci_device_id using plain list expressions is hard to understand for a human who isn't into the Linux PCI subsystem. So for each initializer use one of the PCI_DEVICE macros for .vendor, .device, .subvendor and .subdevice and named initializers for the remaining assignments. This makes it easier to parse and also more robust against chagnes to the struct definition. Also drop useless zeros and commas. The mentioned robustness is relevant for a planned change to struct pci_device_id that replaces .driver_data by an anonymous union. This change doesn't introduce changes to the compiled pci_device_id array. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Miquel Raynal --- drivers/mtd/maps/amd76xrom.c | 10 ++++------ drivers/mtd/maps/ck804xrom.c | 2 +- drivers/mtd/maps/esb2rom.c | 20 +++++++------------- drivers/mtd/maps/ichxrom.c | 17 ++++++----------- drivers/mtd/maps/pci.c | 16 +++++----------- drivers/mtd/maps/scb2_flash.c | 9 ++------- drivers/mtd/nand/raw/cafe_nand.c | 3 +-- drivers/mtd/nand/raw/denali_pci.c | 4 ++-- drivers/mtd/nand/raw/r852.c | 4 ++-- 9 files changed, 30 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 95f9b99ce5fc..457e6ab7f3fb 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -296,12 +296,10 @@ static void amd76xrom_remove_one(struct pci_dev *pdev) } static const struct pci_device_id amd76xrom_pci_tbl[] = { - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */ - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_AMD, 0x7468, 0, 0) }, /* amd8111 support */ + { } }; MODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl); diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 5e44134caa8e..ef4afa8c1931 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -335,7 +335,7 @@ static const struct pci_device_id ck804xrom_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0365), .driver_data = DEV_MCP55 }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0366), .driver_data = DEV_MCP55 }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0367), .driver_data = DEV_MCP55 }, - { 0, } + { } }; #if 0 diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index fc7f2c3a62b0..043bad544414 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -385,19 +385,13 @@ static void esb2rom_remove_one(struct pci_dev *pdev) } static const struct pci_device_id esb2rom_pci_tbl[] = { - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0) }, + { }, }; #if 0 diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 6608b782149b..8fecd1820f6e 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -323,17 +323,12 @@ static void ichxrom_remove_one(struct pci_dev *pdev) } static const struct pci_device_id ichxrom_pci_tbl[] = { - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, - PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, - PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1), }, + { }, }; #if 0 diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 4a84ca8aed40..34041cc8f173 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -227,22 +227,16 @@ static struct mtd_pci_info intel_dc21285_info = { static const struct pci_device_id mtd_pci_ids[] = { { - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x530d, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x530d), .class = PCI_CLASS_MEMORY_OTHER << 8, .class_mask = 0xffff00, .driver_data = (unsigned long)&intel_iq80310_info, - }, - { - .vendor = PCI_VENDOR_ID_DEC, - .device = PCI_DEVICE_ID_DEC_21285, - .subvendor = 0, /* DC21285 defaults to 0 on reset */ - .subdevice = 0, /* DC21285 defaults to 0 on reset */ + }, { + /* DC21285 defaults to 0 for .subvendor and .subdevice on reset */ + PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, 0, 0), .driver_data = (unsigned long)&intel_dc21285_info, }, - { 0, } + { } }; /* diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 57303f904bc1..5c7e1dad101b 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -215,13 +215,8 @@ static void scb2_flash_remove(struct pci_dev *dev) } static struct pci_device_id scb2_flash_pci_ids[] = { - { - .vendor = PCI_VENDOR_ID_SERVERWORKS, - .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID - }, - { 0, } + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5) }, + { } }; static struct pci_driver scb2_flash_driver = { diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index c4018bc59670..f3117b031cd2 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -830,8 +830,7 @@ static void cafe_nand_remove(struct pci_dev *pdev) } static const struct pci_device_id cafe_nand_tbl[] = { - { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, - PCI_ANY_ID, PCI_ANY_ID }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND) }, { } }; diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 97fa32d73441..f53df1b32dda 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -19,8 +19,8 @@ /* List of platforms this NAND controller has be integrated into */ static const struct pci_device_id denali_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, - { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { PCI_VDEVICE(INTEL, 0x0701), .driver_data = INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), .driver_data = INTEL_MRST }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(pci, denali_pci_ids); diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index 8a5572b30893..92c47d351c27 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -1070,8 +1070,8 @@ static int r852_resume(struct device *device) static const struct pci_device_id r852_pci_id_tbl[] = { - { PCI_VDEVICE(RICOH, 0x0852), }, - { }, + { PCI_VDEVICE(RICOH, 0x0852) }, + { } }; MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); From ff111dc2158562ddc5e9988c26b4c0a9cd9f978c Mon Sep 17 00:00:00 2001 From: Li Xinyu Date: Tue, 26 May 2026 17:45:40 +0800 Subject: [PATCH 1798/5214] mtd: inftlmount: convert printk(KERN_WARNING) to pr_warn Replace all printk(KERN_WARNING ...) calls with pr_warn() to follow the kernel's recommended logging style and resolve checkpatch warnings. Also convert one bare printk() that was missing a log level. No functional change. Signed-off-by: Li Xinyu Signed-off-by: Miquel Raynal --- drivers/mtd/inftlmount.c | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 6276daa296da..87e246a6f488 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -67,12 +67,12 @@ static int find_boot_record(struct INFTLrecord *inftl) static int warncount = 5; if (warncount) { - printk(KERN_WARNING "INFTL: block read at 0x%x " + pr_warn("INFTL: block read at 0x%x " "of mtd%d failed: %d\n", block * inftl->EraseSize, inftl->mbd.mtd->index, ret); if (!--warncount) - printk(KERN_WARNING "INFTL: further " + pr_warn("INFTL: further " "failures for this block will " "not be printed\n"); } @@ -89,7 +89,7 @@ static int find_boot_record(struct INFTLrecord *inftl) block * inftl->EraseSize + SECTORSIZE + 8, 8, &retlen,(char *)&h1); if (ret < 0) { - printk(KERN_WARNING "INFTL: ANAND header found at " + pr_warn("INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, inftl->mbd.mtd->index, ret); @@ -107,13 +107,13 @@ static int find_boot_record(struct INFTLrecord *inftl) mtd_read(mtd, block * inftl->EraseSize + 4096, SECTORSIZE, &retlen, buf); if (retlen != SECTORSIZE) { - printk(KERN_WARNING "INFTL: Unable to read spare " + pr_warn("INFTL: Unable to read spare " "Media Header\n"); return -1; } /* Check if this one is the same as the first one we found. */ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { - printk(KERN_WARNING "INFTL: Primary and spare Media " + pr_warn("INFTL: Primary and spare Media " "Headers disagree.\n"); return -1; } @@ -141,14 +141,14 @@ static int find_boot_record(struct INFTLrecord *inftl) mh->OsakVersion, mh->PercentUsed); if (mh->NoOfBDTLPartitions == 0) { - printk(KERN_WARNING "INFTL: Media Header sanity check " + pr_warn("INFTL: Media Header sanity check " "failed: NoOfBDTLPartitions (%d) == 0, " "must be at least 1\n", mh->NoOfBDTLPartitions); return -1; } if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { - printk(KERN_WARNING "INFTL: Media Header sanity check " + pr_warn("INFTL: Media Header sanity check " "failed: Total Partitions (%d) > 4, " "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions, @@ -158,12 +158,12 @@ static int find_boot_record(struct INFTLrecord *inftl) } if (mh->BlockMultiplierBits > 1) { - printk(KERN_WARNING "INFTL: sorry, we don't support " + pr_warn("INFTL: sorry, we don't support " "UnitSizeFactor 0x%02x\n", mh->BlockMultiplierBits); return -1; } else if (mh->BlockMultiplierBits == 1) { - printk(KERN_WARNING "INFTL: support for INFTL with " + pr_warn("INFTL: support for INFTL with " "UnitSizeFactor 0x%02x is experimental\n", mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << @@ -207,7 +207,7 @@ static int find_boot_record(struct INFTLrecord *inftl) mtd_erase(mtd, instr); } if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { - printk(KERN_WARNING "INFTL: Media Header " + pr_warn("INFTL: Media Header " "Partition %d sanity check failed\n" " firstUnit %d : lastUnit %d > " "virtualUnits %d\n", i, ip->lastUnit, @@ -215,7 +215,7 @@ static int find_boot_record(struct INFTLrecord *inftl) return -1; } if (ip->Reserved1 != 0) { - printk(KERN_WARNING "INFTL: Media Header " + pr_warn("INFTL: Media Header " "Partition %d sanity check failed: " "Reserved1 %d != 0\n", i, ip->Reserved1); @@ -227,7 +227,7 @@ static int find_boot_record(struct INFTLrecord *inftl) } if (i >= 4) { - printk(KERN_WARNING "INFTL: Media Header Partition " + pr_warn("INFTL: Media Header Partition " "sanity check failed:\n No partition " "marked as Disk Partition\n"); return -1; @@ -237,7 +237,7 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->numvunits = ip->virtualUnits; if (inftl->numvunits > (inftl->nb_blocks - inftl->nb_boot_blocks - 2)) { - printk(KERN_WARNING "INFTL: Media Header sanity check " + pr_warn("INFTL: Media Header sanity check " "failed:\n numvunits (%d) > nb_blocks " "(%d) - nb_boot_blocks(%d) - 2\n", inftl->numvunits, inftl->nb_blocks, @@ -385,7 +385,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) ret = mtd_erase(inftl->mbd.mtd, instr); if (ret) { - printk(KERN_WARNING "INFTL: error while formatting block %d\n", + pr_warn("INFTL: error while formatting block %d\n", block); goto fail; } @@ -428,13 +428,13 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) { unsigned int block = first_block, block1; - printk(KERN_WARNING "INFTL: formatting chain at block %d\n", + pr_warn("INFTL: formatting chain at block %d\n", first_block); for (;;) { block1 = inftl->PUtable[block]; - printk(KERN_WARNING "INFTL: formatting block %d\n", block); + pr_warn("INFTL: formatting block %d\n", block); if (INFTL_formatblock(inftl, block) < 0) { /* * Cannot format !!!! Mark it as Bad Unit, @@ -539,7 +539,7 @@ int INFTL_mount(struct INFTLrecord *s) /* Search for INFTL MediaHeader and Spare INFTL Media Header */ if (find_boot_record(s) < 0) { - printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); + pr_warn("INFTL: could not find valid boot record?\n"); return -ENXIO; } @@ -610,7 +610,7 @@ int INFTL_mount(struct INFTLrecord *s) /* Check for invalid block */ if (erase_mark != ERASE_MARK) { - printk(KERN_WARNING "INFTL: corrupt block %d " + pr_warn("INFTL: corrupt block %d " "in chain %d, chain length %d, erase " "mark 0x%x?\n", block, first_block, chain_length, erase_mark); @@ -635,7 +635,7 @@ int INFTL_mount(struct INFTLrecord *s) ((prev_block >= s->nb_blocks) && (prev_block != BLOCK_NIL))) { if (chain_length > 0) { - printk(KERN_WARNING "INFTL: corrupt " + pr_warn("INFTL: corrupt " "block %d in chain %d?\n", block, first_block); do_format_chain++; @@ -670,7 +670,7 @@ int INFTL_mount(struct INFTLrecord *s) /* Validate next block before following it... */ if (block > s->lastEUN) { - printk(KERN_WARNING "INFTL: invalid previous " + pr_warn("INFTL: invalid previous " "block %d in chain %d?\n", block, first_block); do_format_chain++; @@ -714,7 +714,7 @@ int INFTL_mount(struct INFTLrecord *s) if (s->PUtable[block] == BLOCK_NIL) break; if (s->PUtable[block] > s->lastEUN) { - printk(KERN_WARNING "INFTL: invalid prev %d, " + pr_warn("INFTL: invalid prev %d, " "in virtual chain %d\n", s->PUtable[block], logical_block); s->PUtable[block] = BLOCK_NIL; @@ -757,7 +757,7 @@ int INFTL_mount(struct INFTLrecord *s) pr_debug("INFTL: pass 3, format unused blocks\n"); for (block = s->firstEUN; block <= s->lastEUN; block++) { if (s->PUtable[block] == BLOCK_NOTEXPLORED) { - printk("INFTL: unreferenced block %d, formatting it\n", + pr_warn("INFTL: unreferenced block %d, formatting it\n", block); if (INFTL_formatblock(s, block) < 0) s->PUtable[block] = BLOCK_RESERVED; From 6dcd852e9b43971a84bfc442ad45e69f224f3b11 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:32:04 +0200 Subject: [PATCH 1799/5214] =?UTF-8?q?mtd:=20maps:=20remove=20AMD=20=C3=89l?= =?UTF-8?q?an=20specific=20drivers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were four MTD maps drivers that were used with AMD Élan SoCs. Since 486-class CPU support was removed in commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), it is no longer possible to actually use these. Three of them have already been removed, so remove the remaining driver entirely. Signed-off-by: Arnd Bergmann [Miquel Raynal: Only NETtel was still in the tree, adapt the patch and the commit log] Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 6 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/nettel.c | 462 -------------------------------------- 3 files changed, 469 deletions(-) delete mode 100644 drivers/mtd/maps/nettel.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index db63b5af4b39..784dba14e908 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -204,12 +204,6 @@ config MTD_TSUNAMI help Support for the flash chip on Tsunami TIG bus. -config MTD_NETtel - tristate "CFI flash device on SnapGear/SecureEdge" - depends on X86 && MTD_JEDECPROBE - help - Support for flash chips on NETtel/SecureEdge/SnapGear boards. - config MTD_LANTIQ tristate "Lantiq SoC NOR support" depends on LANTIQ diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c545d1e68e91..157f911f86d8 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o -obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c deleted file mode 100644 index 7d349874ffeb..000000000000 --- a/drivers/mtd/maps/nettel.c +++ /dev/null @@ -1,462 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/****************************************************************************/ - -/* - * nettel.c -- mappings for NETtel/SecureEdge/SnapGear (x86) boards. - * - * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) - * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) - */ - -/****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/****************************************************************************/ - -#define INTEL_BUSWIDTH 1 -#define AMD_WINDOW_MAXSIZE 0x00200000 -#define AMD_BUSWIDTH 1 - -/* - * PAR masks and shifts, assuming 64K pages. - */ -#define SC520_PAR_ADDR_MASK 0x00003fff -#define SC520_PAR_ADDR_SHIFT 16 -#define SC520_PAR_TO_ADDR(par) \ - (((par)&SC520_PAR_ADDR_MASK) << SC520_PAR_ADDR_SHIFT) - -#define SC520_PAR_SIZE_MASK 0x01ffc000 -#define SC520_PAR_SIZE_SHIFT 2 -#define SC520_PAR_TO_SIZE(par) \ - ((((par)&SC520_PAR_SIZE_MASK) << SC520_PAR_SIZE_SHIFT) + (64*1024)) - -#define SC520_PAR(cs, addr, size) \ - ((cs) | \ - ((((size)-(64*1024)) >> SC520_PAR_SIZE_SHIFT) & SC520_PAR_SIZE_MASK) | \ - (((addr) >> SC520_PAR_ADDR_SHIFT) & SC520_PAR_ADDR_MASK)) - -#define SC520_PAR_BOOTCS 0x8a000000 -#define SC520_PAR_ROMCS1 0xaa000000 -#define SC520_PAR_ROMCS2 0xca000000 /* Cache disabled, 64K page */ - -static void *nettel_mmcrp = NULL; - -#ifdef CONFIG_MTD_CFI_INTELEXT -static struct mtd_info *intel_mtd; -#endif -static struct mtd_info *amd_mtd; - -/****************************************************************************/ - -/****************************************************************************/ - -#ifdef CONFIG_MTD_CFI_INTELEXT -static struct map_info nettel_intel_map = { - .name = "SnapGear Intel", - .size = 0, - .bankwidth = INTEL_BUSWIDTH, -}; - -static struct mtd_partition nettel_intel_partitions[] = { - { - .name = "SnapGear kernel", - .offset = 0, - .size = 0x000e0000 - }, - { - .name = "SnapGear filesystem", - .offset = 0x00100000, - }, - { - .name = "SnapGear config", - .offset = 0x000e0000, - .size = 0x00020000 - }, - { - .name = "SnapGear Intel", - .offset = 0 - }, - { - .name = "SnapGear BIOS Config", - .offset = 0x007e0000, - .size = 0x00020000 - }, - { - .name = "SnapGear BIOS", - .offset = 0x007e0000, - .size = 0x00020000 - }, -}; -#endif - -static struct map_info nettel_amd_map = { - .name = "SnapGear AMD", - .size = AMD_WINDOW_MAXSIZE, - .bankwidth = AMD_BUSWIDTH, -}; - -static const struct mtd_partition nettel_amd_partitions[] = { - { - .name = "SnapGear BIOS config", - .offset = 0x000e0000, - .size = 0x00010000 - }, - { - .name = "SnapGear BIOS", - .offset = 0x000f0000, - .size = 0x00010000 - }, - { - .name = "SnapGear AMD", - .offset = 0 - }, - { - .name = "SnapGear high BIOS", - .offset = 0x001f0000, - .size = 0x00010000 - } -}; - -#define NUM_AMD_PARTITIONS ARRAY_SIZE(nettel_amd_partitions) - -/****************************************************************************/ - -#ifdef CONFIG_MTD_CFI_INTELEXT - -/* - * Set the Intel flash back to read mode since some old boot - * loaders don't. - */ -static int nettel_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v) -{ - struct cfi_private *cfi = nettel_intel_map.fldrv_priv; - unsigned long b; - - /* Make sure all FLASH chips are put back into read mode */ - for (b = 0; (b < nettel_intel_partitions[3].size); b += 0x100000) { - cfi_send_gen_cmd(0xff, 0x55, b, &nettel_intel_map, cfi, - cfi->device_type, NULL); - } - return(NOTIFY_OK); -} - -static struct notifier_block nettel_notifier_block = { - nettel_reboot_notifier, NULL, 0 -}; - -#endif - -/****************************************************************************/ - -static int __init nettel_init(void) -{ - volatile unsigned long *amdpar; - unsigned long amdaddr, maxsize; - int num_amd_partitions=0; -#ifdef CONFIG_MTD_CFI_INTELEXT - volatile unsigned long *intel0par, *intel1par; - unsigned long orig_bootcspar, orig_romcs1par; - unsigned long intel0addr, intel0size; - unsigned long intel1addr, intel1size; - int intelboot, intel0cs, intel1cs; - int num_intel_partitions; -#endif - int rc = 0; - - nettel_mmcrp = (void *) ioremap(0xfffef000, 4096); - if (nettel_mmcrp == NULL) { - printk("SNAPGEAR: failed to disable MMCR cache??\n"); - return(-EIO); - } - - /* Set CPU clock to be 33.000MHz */ - *((unsigned char *) (nettel_mmcrp + 0xc64)) = 0x01; - - amdpar = (volatile unsigned long *) (nettel_mmcrp + 0xc4); - -#ifdef CONFIG_MTD_CFI_INTELEXT - intelboot = 0; - intel0cs = SC520_PAR_ROMCS1; - intel0par = (volatile unsigned long *) (nettel_mmcrp + 0xc0); - intel1cs = SC520_PAR_ROMCS2; - intel1par = (volatile unsigned long *) (nettel_mmcrp + 0xbc); - - /* - * Save the CS settings then ensure ROMCS1 and ROMCS2 are off, - * otherwise they might clash with where we try to map BOOTCS. - */ - orig_bootcspar = *amdpar; - orig_romcs1par = *intel0par; - *intel0par = 0; - *intel1par = 0; -#endif - - /* - * The first thing to do is determine if we have a separate - * boot FLASH device. Typically this is a small (1 to 2MB) - * AMD FLASH part. It seems that device size is about the - * only way to tell if this is the case... - */ - amdaddr = 0x20000000; - maxsize = AMD_WINDOW_MAXSIZE; - - *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); - __asm__ ("wbinvd"); - - nettel_amd_map.phys = amdaddr; - nettel_amd_map.virt = ioremap(amdaddr, maxsize); - if (!nettel_amd_map.virt) { - printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); - iounmap(nettel_mmcrp); - return(-EIO); - } - simple_map_init(&nettel_amd_map); - - if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { - printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", - (int)(amd_mtd->size>>10)); - - amd_mtd->owner = THIS_MODULE; - - /* The high BIOS partition is only present for 2MB units */ - num_amd_partitions = NUM_AMD_PARTITIONS; - if (amd_mtd->size < AMD_WINDOW_MAXSIZE) - num_amd_partitions--; - /* Don't add the partition until after the primary INTEL's */ - -#ifdef CONFIG_MTD_CFI_INTELEXT - /* - * Map the Intel flash into memory after the AMD - * It has to start on a multiple of maxsize. - */ - maxsize = SC520_PAR_TO_SIZE(orig_romcs1par); - if (maxsize < (32 * 1024 * 1024)) - maxsize = (32 * 1024 * 1024); - intel0addr = amdaddr + maxsize; -#endif - } else { -#ifdef CONFIG_MTD_CFI_INTELEXT - /* INTEL boot FLASH */ - intelboot++; - - if (!orig_romcs1par) { - intel0cs = SC520_PAR_BOOTCS; - intel0par = (volatile unsigned long *) - (nettel_mmcrp + 0xc4); - intel1cs = SC520_PAR_ROMCS1; - intel1par = (volatile unsigned long *) - (nettel_mmcrp + 0xc0); - - intel0addr = SC520_PAR_TO_ADDR(orig_bootcspar); - maxsize = SC520_PAR_TO_SIZE(orig_bootcspar); - } else { - /* Kernel base is on ROMCS1, not BOOTCS */ - intel0cs = SC520_PAR_ROMCS1; - intel0par = (volatile unsigned long *) - (nettel_mmcrp + 0xc0); - intel1cs = SC520_PAR_BOOTCS; - intel1par = (volatile unsigned long *) - (nettel_mmcrp + 0xc4); - - intel0addr = SC520_PAR_TO_ADDR(orig_romcs1par); - maxsize = SC520_PAR_TO_SIZE(orig_romcs1par); - } - - /* Destroy useless AMD MTD mapping */ - amd_mtd = NULL; - iounmap(nettel_amd_map.virt); - nettel_amd_map.virt = NULL; -#else - /* Only AMD flash supported */ - rc = -ENXIO; - goto out_unmap2; -#endif - } - -#ifdef CONFIG_MTD_CFI_INTELEXT - /* - * We have determined the INTEL FLASH configuration, so lets - * go ahead and probe for them now. - */ - - /* Set PAR to the maximum size */ - if (maxsize < (32 * 1024 * 1024)) - maxsize = (32 * 1024 * 1024); - *intel0par = SC520_PAR(intel0cs, intel0addr, maxsize); - - /* Turn other PAR off so the first probe doesn't find it */ - *intel1par = 0; - - /* Probe for the size of the first Intel flash */ - nettel_intel_map.size = maxsize; - nettel_intel_map.phys = intel0addr; - nettel_intel_map.virt = ioremap(intel0addr, maxsize); - if (!nettel_intel_map.virt) { - printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); - rc = -EIO; - goto out_unmap2; - } - simple_map_init(&nettel_intel_map); - - intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); - if (!intel_mtd) { - rc = -ENXIO; - goto out_unmap1; - } - - /* Set PAR to the detected size */ - intel0size = intel_mtd->size; - *intel0par = SC520_PAR(intel0cs, intel0addr, intel0size); - - /* - * Map second Intel FLASH right after first. Set its size to the - * same maxsize used for the first Intel FLASH. - */ - intel1addr = intel0addr + intel0size; - *intel1par = SC520_PAR(intel1cs, intel1addr, maxsize); - __asm__ ("wbinvd"); - - maxsize += intel0size; - - /* Delete the old map and probe again to do both chips */ - map_destroy(intel_mtd); - intel_mtd = NULL; - iounmap(nettel_intel_map.virt); - - nettel_intel_map.size = maxsize; - nettel_intel_map.virt = ioremap(intel0addr, maxsize); - if (!nettel_intel_map.virt) { - printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); - rc = -EIO; - goto out_unmap2; - } - - intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); - if (! intel_mtd) { - rc = -ENXIO; - goto out_unmap1; - } - - intel1size = intel_mtd->size - intel0size; - if (intel1size > 0) { - *intel1par = SC520_PAR(intel1cs, intel1addr, intel1size); - __asm__ ("wbinvd"); - } else { - *intel1par = 0; - } - - printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n", - (unsigned long long)(intel_mtd->size >> 10)); - - intel_mtd->owner = THIS_MODULE; - - num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions); - - if (intelboot) { - /* - * Adjust offset and size of last boot partition. - * Must allow for BIOS region at end of FLASH. - */ - nettel_intel_partitions[1].size = (intel0size + intel1size) - - (1024*1024 + intel_mtd->erasesize); - nettel_intel_partitions[3].size = intel0size + intel1size; - nettel_intel_partitions[4].offset = - (intel0size + intel1size) - intel_mtd->erasesize; - nettel_intel_partitions[4].size = intel_mtd->erasesize; - nettel_intel_partitions[5].offset = - nettel_intel_partitions[4].offset; - nettel_intel_partitions[5].size = - nettel_intel_partitions[4].size; - } else { - /* No BIOS regions when AMD boot */ - num_intel_partitions -= 2; - } - rc = mtd_device_register(intel_mtd, nettel_intel_partitions, - num_intel_partitions); - if (rc) - goto out_map_destroy; -#endif - - if (amd_mtd) { - rc = mtd_device_register(amd_mtd, nettel_amd_partitions, - num_amd_partitions); - if (rc) - goto out_mtd_unreg; - } - -#ifdef CONFIG_MTD_CFI_INTELEXT - register_reboot_notifier(&nettel_notifier_block); -#endif - - return rc; - -out_mtd_unreg: -#ifdef CONFIG_MTD_CFI_INTELEXT - mtd_device_unregister(intel_mtd); -out_map_destroy: - map_destroy(intel_mtd); -out_unmap1: - iounmap(nettel_intel_map.virt); -#endif - -out_unmap2: - iounmap(nettel_mmcrp); - iounmap(nettel_amd_map.virt); - - return rc; -} - -/****************************************************************************/ - -static void __exit nettel_cleanup(void) -{ -#ifdef CONFIG_MTD_CFI_INTELEXT - unregister_reboot_notifier(&nettel_notifier_block); -#endif - if (amd_mtd) { - mtd_device_unregister(amd_mtd); - map_destroy(amd_mtd); - } - if (nettel_mmcrp) { - iounmap(nettel_mmcrp); - nettel_mmcrp = NULL; - } - if (nettel_amd_map.virt) { - iounmap(nettel_amd_map.virt); - nettel_amd_map.virt = NULL; - } -#ifdef CONFIG_MTD_CFI_INTELEXT - if (intel_mtd) { - mtd_device_unregister(intel_mtd); - map_destroy(intel_mtd); - } - if (nettel_intel_map.virt) { - iounmap(nettel_intel_map.virt); - nettel_intel_map.virt = NULL; - } -#endif -} - -/****************************************************************************/ - -module_init(nettel_init); -module_exit(nettel_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Ungerer "); -MODULE_DESCRIPTION("SnapGear/SecureEdge FLASH support"); - -/****************************************************************************/ From c584b8a7ad01a334a89732f3c5791ba14e58642b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:32:05 +0200 Subject: [PATCH 1800/5214] mtd: maps: remove uclinux map driver Rather than using platform data or DT properties, the configuration for this mtd map driver used to be passed through the global uclinux_ram_map structure, but the last instance was removed in commit 4ba66a976072 ("arch: remove blackfin port") in 2018. After commit 251f26c9e828 ("mtd: maps: Make uclinux_ram_map static"), it became impossible to configure it at all, even with out-of-tree platform code. Clearly nobody is using it, so remove it. Signed-off-by: Arnd Bergmann Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 6 -- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/uclinux.c | 118 ------------------------------------- 3 files changed, 125 deletions(-) delete mode 100644 drivers/mtd/maps/uclinux.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 784dba14e908..1e8f2b518ddd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -284,12 +284,6 @@ config MTD_PCMCIA_ANONYMOUS If unsure, say N. -config MTD_UCLINUX - bool "Generic uClinux RAM/ROM filesystem support" - depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE) - help - Map driver to support image based filesystems for uClinux. - config MTD_PLATRAM tristate "Map driver for platform device RAM (mtd-ram)" select MTD_RAM diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 157f911f86d8..eff28fc0b60e 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_IMPA7) += impa7.o -obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c deleted file mode 100644 index de4c46318abb..000000000000 --- a/drivers/mtd/maps/uclinux.c +++ /dev/null @@ -1,118 +0,0 @@ -/****************************************************************************/ - -/* - * uclinux.c -- generic memory mapped MTD driver for uclinux - * - * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * License: GPL - */ - -/****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/****************************************************************************/ - -#ifdef CONFIG_MTD_ROM -#define MAP_NAME "rom" -#else -#define MAP_NAME "ram" -#endif - -static struct map_info uclinux_ram_map = { - .name = MAP_NAME, - .size = 0, -}; - -static unsigned long physaddr = -1; -module_param(physaddr, ulong, S_IRUGO); - -static struct mtd_info *uclinux_ram_mtdinfo; - -/****************************************************************************/ - -static const struct mtd_partition uclinux_romfs[] = { - { .name = "ROMfs" } -}; - -#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs) - -/****************************************************************************/ - -static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, resource_size_t *phys) -{ - struct map_info *map = mtd->priv; - *virt = map->virt + from; - if (phys) - *phys = map->phys + from; - *retlen = len; - return(0); -} - -/****************************************************************************/ - -static int __init uclinux_mtd_init(void) -{ - struct mtd_info *mtd; - struct map_info *mapp; - - mapp = &uclinux_ram_map; - - if (physaddr == -1) - mapp->phys = (resource_size_t)__bss_stop; - else - mapp->phys = physaddr; - - if (!mapp->size) - mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); - mapp->bankwidth = 4; - - printk("uclinux[mtd]: probe address=0x%x size=0x%x\n", - (int) mapp->phys, (int) mapp->size); - - /* - * The filesystem is guaranteed to be in direct mapped memory. It is - * directly following the kernels own bss region. Following the same - * mechanism used by architectures setting up traditional initrds we - * use phys_to_virt to get the virtual address of its start. - */ - mapp->virt = phys_to_virt(mapp->phys); - - if (mapp->virt == 0) { - printk("uclinux[mtd]: no virtual mapping?\n"); - return(-EIO); - } - - simple_map_init(mapp); - - mtd = do_map_probe("map_" MAP_NAME, mapp); - if (!mtd) { - printk("uclinux[mtd]: failed to find a mapping?\n"); - return(-ENXIO); - } - - mtd->owner = THIS_MODULE; - mtd->_point = uclinux_point; - mtd->priv = mapp; - - uclinux_ram_mtdinfo = mtd; - mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS); - - return(0); -} -device_initcall(uclinux_mtd_init); - -/****************************************************************************/ From 72de4a7c8e2953ae489120203d2c45bb59c5c54f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:32:06 +0200 Subject: [PATCH 1801/5214] mtd: maps: remove obsolete impa7 map driver This driver was originally merged in 2002 for a board using the Cirrus Logic CL/PS711x platform, but the actual board file never made it upstream. The SoC platform is still supported but uses devicetree based probing, so if anyone ever wanted to upstream board support, they would just use the regular physmap driver. Signed-off-by: Arnd Bergmann Signed-off-by: Miquel Raynal --- drivers/mtd/maps/Kconfig | 7 --- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/impa7.c | 115 -------------------------------------- 3 files changed, 123 deletions(-) delete mode 100644 drivers/mtd/maps/impa7.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 1e8f2b518ddd..dce5e67ce3c2 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -249,13 +249,6 @@ config MTD_DC21285 21285 bridge used with Intel's StrongARM processors. More info at . -config MTD_IMPA7 - tristate "JEDEC Flash device mapped on impA7" - depends on ARM && MTD_JEDECPROBE - help - This enables access to the NOR Flash on the impA7 board of - implementa GmbH. If you have such a board, say 'Y' here. - # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index eff28fc0b60e..fbed278157f6 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c deleted file mode 100644 index b41401852fb7..000000000000 --- a/drivers/mtd/maps/impa7.c +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Handle mapping of the NOR flash on implementa A7 boards - * - * Copyright 2002 SYSGO Real-Time Solutions GmbH - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define WINDOW_ADDR0 0x00000000 /* physical properties of flash */ -#define WINDOW_SIZE0 0x00800000 -#define WINDOW_ADDR1 0x10000000 /* physical properties of flash */ -#define WINDOW_SIZE1 0x00800000 -#define NUM_FLASHBANKS 2 -#define BUSWIDTH 4 - -#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ -#define MTDID "impa7-%d" /* for mtdparts= partitioning */ - -static struct mtd_info *impa7_mtd[NUM_FLASHBANKS]; - -static const char * const rom_probe_types[] = { "jedec_probe", NULL }; - -static struct map_info impa7_map[NUM_FLASHBANKS] = { - { - .name = "impA7 NOR Flash Bank #0", - .size = WINDOW_SIZE0, - .bankwidth = BUSWIDTH, - }, - { - .name = "impA7 NOR Flash Bank #1", - .size = WINDOW_SIZE1, - .bankwidth = BUSWIDTH, - }, -}; - -/* - * MTD partitioning stuff - */ -static const struct mtd_partition partitions[] = -{ - { - .name = "FileSystem", - .size = 0x800000, - .offset = 0x00000000 - }, -}; - -static int __init init_impa7(void) -{ - const char * const *type; - int i; - static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { - { WINDOW_ADDR0, WINDOW_SIZE0 }, - { WINDOW_ADDR1, WINDOW_SIZE1 }, - }; - int devicesfound = 0; - - for(i=0; iowner = THIS_MODULE; - devicesfound++; - mtd_device_register(impa7_mtd[i], partitions, - ARRAY_SIZE(partitions)); - } else { - iounmap((void __iomem *)impa7_map[i].virt); - } - } - return devicesfound == 0 ? -ENXIO : 0; -} - -static void __exit cleanup_impa7(void) -{ - int i; - for (i=0; i"); -MODULE_DESCRIPTION("MTD map driver for implementa impA7"); From 60d877910a43c305b5165131b258a17b1d772d57 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Tue, 26 May 2026 17:55:24 -0500 Subject: [PATCH 1802/5214] iio: chemical: scd30: Cleanup initializations and fix sign-extension bug Include linux/bitfield.h for FIELD_GET(). Create new macros for bit manipulation in combination with manual bit manipulation being replaced with FIELD_GET(). The current variable declaration and initializations are barely readable and use comma separations across multiple lines. Refactor the initializations so that mantissa and exp have separate declarations and sign gets initialized later. In addition (and due to the nature of the cleanup), fix a sign-extension bug where, float32 would get bitwise anded with ~BIT(31) (which is 0xFFFFFFFF7FFFFFFF) which corrupted the exponent. Fixes: 64b3d8b1b0f5c ("iio: chemical: scd30: add core driver") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260524020309.18618-1-m32285159%40gmail.com Signed-off-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 11d6bc1b63e6..d02f2de42327 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -4,6 +4,8 @@ * * Copyright (c) 2020 Tomasz Duszynski */ + +#include #include #include #include @@ -43,6 +45,11 @@ #define SCD30_TEMP_OFFSET_MAX 655360 #define SCD30_EXTRA_TIMEOUT_PER_S 250 +/* Floating point arithmetic macros */ +#define SCD30_FLOAT_MANTISSA_MSK GENMASK(22, 0) +#define SCD30_FLOAT_EXP_MSK GENMASK(30, 23) +#define SCD30_FLOAT_SIGN_MSK BIT(31) + enum { SCD30_CONC, SCD30_TEMP, @@ -89,10 +96,14 @@ static int scd30_reset(struct scd30_state *state) /* simplified float to fixed point conversion with a scaling factor of 0.01 */ static int scd30_float_to_fp(int float32) { - int fraction, shift, - mantissa = float32 & GENMASK(22, 0), - sign = (float32 & BIT(31)) ? -1 : 1, - exp = (float32 & ~BIT(31)) >> 23; + int fraction, shift, sign; + int mantissa = FIELD_GET(SCD30_FLOAT_MANTISSA_MSK, float32); + int exp = FIELD_GET(SCD30_FLOAT_EXP_MSK, float32); + + if (float32 & SCD30_FLOAT_SIGN_MSK) + sign = -1; + else + sign = 1; /* special case 0 */ if (!exp && !mantissa) From 789d22d77879eabb042627f6627cdb62787bc142 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Mon, 4 May 2026 20:43:20 +0530 Subject: [PATCH 1803/5214] powerpc tools perf: Initialize error code in auxtrace_record_init function perf trace record fails some cases in powerpc # perf test "perf trace record and replay" 128: perf trace record and replay : FAILED! # perf trace record sleep 1 # echo $? 32 This is happening because of non-zero err value from auxtrace_record__init() function. static int record__auxtrace_init(struct record *rec) { int err; if ((rec->opts.auxtrace_snapshot_opts || rec->opts.auxtrace_sample_opts) && record__threads_enabled(rec)) { pr_err("AUX area tracing options are not available in parallel streaming mode.\n"); return -EINVAL; } if (!rec->itr) { rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; } Here "int err" is not initialised. The code expects "err" to be set from auxtrace_record__init() function. Update auxtrace_record__init() in arch/powerpc/util/auxtrace.c to clear err value in the beginning. - Clear err value in beginning of function. Any fail later will set appropriate return code to err. - Even if we haven't found any event for auxtrace, perf record should continue for other events. NULL return will indicate that there is no auxtrace record initialized. - Not having "err" set here will affect monitoring of other events also because perf record will fail seeing random value in err. Set err to -EINVAL before invoking auxtrace_record__init() in builtin-record.c With the fix, # perf trace record sleep 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.033 MB perf.data (228 samples) ] Fixes: 1dbfaf94cf66ec4b ("perf powerpc: Add basic CONFIG_AUXTRACE support for VPA pmu on powerpc") Reviewed-by: Adrian Hunter Signed-off-by: Athira Rajeev Acked-by: Namhyung Kim Cc: Athira Rajeev Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: linuxppc-dev@lists.ozlabs.org Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Shivani Nittor Cc: Tanushree Shah Cc: Tejas Manhas Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/util/auxtrace.c | 6 ++++++ tools/perf/builtin-record.c | 1 + 2 files changed, 7 insertions(+) diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c index e39deff6c857..4600a1661b4f 100644 --- a/tools/perf/arch/powerpc/util/auxtrace.c +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -71,6 +71,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, struct evsel *pos; int found = 0; + /* + * Set err value to zero here. Any fail later + * will set appropriate return code to err. + */ + *err = 0; + evlist__for_each_entry(evlist, pos) { if (strstarts(pos->name, "vpa_dtl")) { found = 1; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index cc601796b2c8..4a30688699d5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -866,6 +866,7 @@ static int record__auxtrace_init(struct record *rec) } if (!rec->itr) { + err = -EINVAL; rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; From c123ca6ee26ad98f70a866ff428b08145c5a24fe Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 26 May 2026 13:15:29 +0200 Subject: [PATCH 1804/5214] iio: light: opt3001: fix missing state reset on timeout Currently in the function opt3001_get_processed(), there is a check that directly returns -ETIMEDOUT if the conversion IRQ times out, completely bypassing the err label, leaving ok_to_ignore_lock permanently true, potentially breaking the device's falling threshold interrupt detection. Assign -ETIMEDOUT to the return variable and jump to the error label to ensure ok_to_ignore_lock is properly reset. Fixes: 26d90b559057 ("iio: light: opt3001: Fixed timeout error when 0 lux") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260525-opt3001-cleanup-v4-0-65b36a174f78%40gmail.com?part=1 Signed-off-by: Joshua Crofts Cc: stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 53bc455b7bad..0d35d23da091 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -366,8 +366,10 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) - return -ETIMEDOUT; + if (ret == 0) { + ret = -ETIMEDOUT; + goto err; + } } else { /* Sleep for result ready time */ timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? From 3ca7f9ef8fa1d96793b855d331e9fe06d47d2a60 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Mon, 4 May 2026 20:43:21 +0530 Subject: [PATCH 1805/5214] perf auxtrace: Add kernel-doc comment to auxtrace_record__init() function Add documentation comment describing the parameters and return code for auxtrace_record__init() in util/auxtrace.c Reviewed-by: Adrian Hunter Signed-off-by: Athira Rajeev Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Namhyung Kim Cc: Shivani Nittor Cc: Tanushree.Shah@ibm.com Cc: Tejas.Manhas1@ibm.com Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/auxtrace.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index a224687ffbc1..a9f007d47c0b 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -896,6 +896,21 @@ int auxtrace_parse_aux_action(struct evlist *evlist) return 0; } +/** + * auxtrace_record__init - Initialize an AUX area tracing record. + * @evlist: The list of events to check for AUX area tracing event. + * @err: Pointer to an integer to store return code. + * + * This function looks through the @evlist to determine which AUX area + * tracing hardware is being used and initializes the auxtrace_record + * structure. + * + * Return: + * a) A pointer to the struct auxtrace_record with @err = 0 on success. + * b) NULL with @err = 0 if no AUX area tracing event is found/supported + * (not considered an error). + * c) NULL with non-zero @err on actual auxtrace_record__init failure. + */ struct auxtrace_record *__weak auxtrace_record__init(struct evlist *evlist __maybe_unused, int *err) { From e987110f09d2817d74a5d624c7bfb620d2bb0d40 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Fri, 22 May 2026 16:26:03 +0800 Subject: [PATCH 1806/5214] perf: Extract is_ignored_kernel_symbol() for kernel mapping symbol filtering Mapping symbol filtering is scattered across multiple files with inconsistent checks. The kernel's own is_mapping_symbol() covers x86 local symbols ('.L*' and 'L0*') on top of the '$' prefix used by ARM/AArch64/RISC-V, but the perf tool only checks '$'. Extract is_ignored_kernel_symbol() into symbol.h matching the kernel definition, and convert the kallsyms and ksymbol event paths to use it. Add ksymbol event name validation and early mapping symbol filtering before any state mutation. Signed-off-by: Rui Qi Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 18 +++++++++++++++++- tools/perf/util/symbol.c | 4 ++-- tools/perf/util/symbol.h | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index c2e0a99efe97..e5d1e8b882a9 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "callchain.h" #include "debug.h" #include "dso.h" @@ -729,9 +730,15 @@ static int machine__process_ksymbol_register(struct machine *machine, { struct symbol *sym; struct dso *dso = NULL; - struct map *map = maps__find(machine__kernel_maps(machine), event->ksymbol.addr); + struct map *map; int err = 0; + /* Ignore mapping symbols in ksymbol events - check early before any state mutation */ + if (is_ignored_kernel_symbol(event->ksymbol.name)) + return 0; + + map = maps__find(machine__kernel_maps(machine), event->ksymbol.addr); + if (!map) { dso = dso__new(event->ksymbol.name); @@ -790,6 +797,10 @@ static int machine__process_ksymbol_unregister(struct machine *machine, struct symbol *sym; struct map *map; + /* Ignore mapping symbols in ksymbol events */ + if (is_ignored_kernel_symbol(event->ksymbol.name)) + return 0; + map = maps__find(machine__kernel_maps(machine), event->ksymbol.addr); if (!map) return 0; @@ -814,6 +825,11 @@ int machine__process_ksymbol(struct machine *machine __maybe_unused, if (dump_trace) perf_event__fprintf_ksymbol(event, stdout); + if (event->header.size < offsetof(struct perf_record_ksymbol, name) + 2 || + !memchr(event->ksymbol.name, '\0', + event->header.size - offsetof(struct perf_record_ksymbol, name))) + return -EINVAL; + /* no need to process non-JIT BPF as it cannot get samples */ if (event->ksymbol.len == 0) return 0; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fcaeeddbbb6b..714b6e6048fa 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -770,8 +770,8 @@ static int map__process_kallsym_symbol(void *arg, const char *name, if (!symbol_type__filter(type)) return 0; - /* Ignore local symbols for ARM modules */ - if (name[0] == '$') + /* Ignore mapping symbols in kallsyms */ + if (is_ignored_kernel_symbol(name)) return 0; /* diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index bd6eb90c8668..95592779eb77 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -28,6 +28,21 @@ struct maps; struct option; struct build_id; +/* + * Ignore kernel mapping symbols, matching kernel is_mapping_symbol() logic. + * This checks for '$' prefix (used by ARM, AArch64, RISC-V) and + * x86 local symbol prefixes (.L* and L0*). + * Only use this for kernel symbols (kallsyms, ksymbol events, kernel ELF DSOs). + */ +static inline bool is_ignored_kernel_symbol(const char *str) +{ + if (str[0] == '.' && str[1] == 'L') + return true; + if (str[0] == 'L' && str[1] == '0') + return true; + return str[0] == '$'; +} + /* * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: From 43b16bf3e7306b4c9ec1aa804872b32a969f5777 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Fri, 22 May 2026 16:26:04 +0800 Subject: [PATCH 1807/5214] perf: Apply is_ignored_kernel_symbol() filter in ELF loading path for kernel DSOs dso__load_sym_internal() had no filtering for .L* and L0* mapping symbols while the kallsyms path already filters them via is_ignored_kernel_symbol(). Add the same check gated by dso__kernel() so that kernel ELF objects (vmlinux, .ko) have mapping symbols filtered across all architectures, but userspace ELF objects are unaffected -- '$' is a valid prefix in languages like Java and Scala. The existing ARM/AArch64 and RISC-V architecture-specific mapping symbol checks are preserved; the new is_ignored_kernel_symbol() check adds x86 local symbol (.L*, L0*) filtering and provides unified cross-architecture coverage for kernel DSOs. Signed-off-by: Rui Qi Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 7afa8a117139..77e6dcba8fda 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1592,9 +1592,11 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss, if (!is_label && !elf_sym__filter(&sym)) continue; - /* Reject ARM ELF "mapping symbols": these aren't unique and + /* + * Reject ARM ELF "mapping symbols": these aren't unique and * don't identify functions, so will confuse the profile - * output: */ + * output: + */ if (ehdr.e_machine == EM_ARM || ehdr.e_machine == EM_AARCH64) { if (elf_name[0] == '$' && strchr("adtx", elf_name[1]) && (elf_name[2] == '\0' || elf_name[2] == '.')) @@ -1607,6 +1609,10 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss, continue; } + /* Reject kernel mapping symbols for kernel DSOs only */ + if (dso__kernel(dso) && is_ignored_kernel_symbol(elf_name)) + continue; + if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) { u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr; u64 *opd = opddata->d_buf + offset; From 390d5ea26622f794c2d29cefd5a01ef116b4fe1d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 22 May 2026 15:04:12 -0700 Subject: [PATCH 1808/5214] perf arch arm: Sort includes and add missed explicit dependencies Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alice Rogers Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/cs-etm.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index b7a839de8707..cdf8e3e60606 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -3,10 +3,13 @@ * Copyright(C) 2015 Linaro Limited. All rights reserved. * Author: Mathieu Poirier */ +#include "../../../util/cs-etm.h" + +#include +#include -#include -#include #include +#include #include #include #include @@ -14,25 +17,24 @@ #include #include #include +#include + +#include +#include // page_size -#include "cs-etm.h" -#include "../../../util/debug.h" -#include "../../../util/record.h" #include "../../../util/auxtrace.h" #include "../../../util/cpumap.h" +#include "../../../util/debug.h" #include "../../../util/event.h" #include "../../../util/evlist.h" #include "../../../util/evsel.h" -#include "../../../util/perf_api_probe.h" #include "../../../util/evsel_config.h" +#include "../../../util/perf_api_probe.h" +#include "../../../util/pmu.h" #include "../../../util/pmus.h" -#include "../../../util/cs-etm.h" -#include // page_size +#include "../../../util/record.h" #include "../../../util/session.h" - -#include -#include -#include +#include "cs-etm.h" struct cs_etm_recording { struct auxtrace_record itr; From 03f5a800545eb483308988b578f8f8543aaf9c86 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 22 May 2026 15:04:13 -0700 Subject: [PATCH 1809/5214] perf arch x86: Sort includes and add missed explicit dependencies Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alice Rogers Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/intel-bts.c | 32 ++++++++++++++----------- tools/perf/arch/x86/util/intel-pt.c | 35 +++++++++++++++------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 85c8186300c8..100a23d27998 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -4,27 +4,31 @@ * Copyright (c) 2013-2015, Intel Corporation. */ +#include "../../../util/intel-bts.h" + #include -#include -#include + #include +#include #include +#include #include -#include "../../../util/cpumap.h" -#include "../../../util/event.h" -#include "../../../util/evsel.h" -#include "../../../util/evlist.h" -#include "../../../util/mmap.h" -#include "../../../util/session.h" -#include "../../../util/pmus.h" -#include "../../../util/debug.h" -#include "../../../util/record.h" -#include "../../../util/tsc.h" -#include "../../../util/auxtrace.h" -#include "../../../util/intel-bts.h" #include // page_size +#include "../../../util/auxtrace.h" +#include "../../../util/cpumap.h" +#include "../../../util/debug.h" +#include "../../../util/event.h" +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" +#include "../../../util/mmap.h" +#include "../../../util/pmu.h" +#include "../../../util/pmus.h" +#include "../../../util/record.h" +#include "../../../util/session.h" +#include "../../../util/tsc.h" + #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) #define KiB_MASK(x) (KiB(x) - 1) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index c131a727774f..0307ff15d9fc 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -3,36 +3,39 @@ * intel_pt.c: Intel Processor Trace support * Copyright (c) 2013-2015, Intel Corporation. */ +#include "../../../util/intel-pt.h" #include #include -#include -#include -#include -#include -#include -#include -#include "../../../util/session.h" +#include +#include +#include +#include +#include +#include + +#include +#include // page_size +#include + +#include "../../../util/auxtrace.h" +#include "../../../util/config.h" +#include "../../../util/cpumap.h" +#include "../../../util/debug.h" #include "../../../util/event.h" #include "../../../util/evlist.h" #include "../../../util/evsel.h" #include "../../../util/evsel_config.h" -#include "../../../util/config.h" -#include "../../../util/cpumap.h" #include "../../../util/mmap.h" -#include #include "../../../util/parse-events.h" -#include "../../../util/pmus.h" -#include "../../../util/debug.h" -#include "../../../util/auxtrace.h" #include "../../../util/perf_api_probe.h" +#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/record.h" +#include "../../../util/session.h" #include "../../../util/target.h" #include "../../../util/tsc.h" -#include // page_size -#include "../../../util/intel-pt.h" -#include #include "cpuid.h" #define KiB(x) ((x) * 1024) From 7b1aa97e976953141486a943ed47acdc19209d1e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 22 May 2026 15:04:14 -0700 Subject: [PATCH 1810/5214] perf tests: Sort includes and add missed explicit dependencies Fix missing #includes found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alice Rogers Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hwmon_pmu.c | 14 +++++++++----- tools/perf/tests/mmap-basic.c | 18 +++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index 4aa4aac94f09..ada6e445c4c4 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -1,15 +1,19 @@ // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -#include "debug.h" -#include "evlist.h" #include "hwmon_pmu.h" -#include "parse-events.h" -#include "tests.h" + #include + #include -#include #include #include #include +#include + +#include "debug.h" +#include "evlist.h" +#include "parse-events.h" +#include "pmus.h" +#include "tests.h" static const struct test_event { const char *name; diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index a18d84d858aa..a69cd1046e9a 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -1,25 +1,29 @@ // SPDX-License-Identifier: GPL-2.0 #include -#include #include #include + +#include +#include +#include +#include + #include +#include +#include #include "cpumap.h" #include "debug.h" #include "event.h" #include "evlist.h" #include "evsel.h" -#include "thread_map.h" +#include "pmu.h" +#include "pmus.h" #include "tests.h" +#include "thread_map.h" #include "util/affinity.h" #include "util/mmap.h" #include "util/sample.h" -#include -#include -#include -#include -#include /* * This test will generate random numbers of calls to some getpid syscalls, From 60d69f2c4e283bddcbb9b37778222b78ec4067f6 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 22 May 2026 15:04:15 -0700 Subject: [PATCH 1811/5214] perf script: Sort includes and add missed explicit dependencies Fix missing #include of pmu.h found while cleaning the evsel/evlist header files. Sort the remaining header files for consistency with the rest of the code. Doing this exposed a missing forward declaration of addr_location in print_insn.h, add this and sort the forward declarations. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alice Rogers Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Leo Yan Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 123 ++++++++++++++++++----------------- tools/perf/util/print_insn.h | 5 +- 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c0918006e0ab..e330ae7f725e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1,75 +1,78 @@ // SPDX-License-Identifier: GPL-2.0 -#include "builtin.h" - -#include "util/counts.h" -#include "util/debug.h" -#include "util/dso.h" -#include -#include "util/header.h" -#include -#include "util/perf_regs.h" -#include "util/session.h" -#include "util/tool.h" -#include "util/map.h" -#include "util/srcline.h" -#include "util/symbol.h" -#include "util/thread.h" -#include "util/trace-event.h" -#include "util/env.h" -#include "util/evlist.h" -#include "util/evsel.h" -#include "util/evsel_fprintf.h" -#include "util/evswitch.h" -#include "util/sort.h" -#include "util/data.h" -#include "util/auxtrace.h" -#include "util/cpumap.h" -#include "util/thread_map.h" -#include "util/stat.h" -#include "util/color.h" -#include "util/string2.h" -#include "util/thread-stack.h" -#include "util/time-utils.h" -#include "util/path.h" -#include "util/event.h" -#include "util/mem-info.h" -#include "util/metricgroup.h" -#include "ui/ui.h" -#include "print_binary.h" -#include "print_insn.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "asm/bug.h" -#include "util/mem-events.h" -#include "util/dump-insn.h" -#include #include #include #include #include -#include -#include -#include + +#include #include -#include -#include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "asm/bug.h" +#include "builtin.h" +#include "perf.h" +#include "print_binary.h" +#include "print_insn.h" +#include "ui/ui.h" +#include "util/annotate.h" +#include "util/auxtrace.h" +#include "util/cgroup.h" +#include "util/color.h" +#include "util/counts.h" +#include "util/cpumap.h" +#include "util/data.h" +#include "util/debug.h" #include "util/dlfilter.h" +#include "util/dso.h" +#include "util/dump-insn.h" +#include "util/env.h" +#include "util/event.h" +#include "util/evlist.h" +#include "util/evsel.h" +#include "util/evsel_fprintf.h" +#include "util/evswitch.h" +#include "util/header.h" +#include "util/map.h" +#include "util/mem-events.h" +#include "util/mem-info.h" +#include "util/metricgroup.h" +#include "util/path.h" +#include "util/perf_regs.h" +#include "util/pmu.h" #include "util/record.h" +#include "util/session.h" +#include "util/sort.h" +#include "util/srcline.h" +#include "util/stat.h" +#include "util/string2.h" +#include "util/symbol.h" +#include "util/thread-stack.h" +#include "util/thread.h" +#include "util/thread_map.h" +#include "util/time-utils.h" +#include "util/tool.h" +#include "util/trace-event.h" #include "util/unwind.h" #include "util/util.h" -#include "util/cgroup.h" -#include "util/annotate.h" -#include "perf.h" -#include #ifdef HAVE_LIBTRACEEVENT #include #endif diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h index 07d11af3fc1c..a54f7e858e49 100644 --- a/tools/perf/util/print_insn.h +++ b/tools/perf/util/print_insn.h @@ -5,10 +5,11 @@ #include #include -struct perf_sample; -struct thread; +struct addr_location; struct machine; struct perf_insn; +struct perf_sample; +struct thread; #define PRINT_INSN_IMM_HEX (1<<0) From e624fb83ccb44e202e1d6d4dc74681bec4f8a597 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 27 Apr 2026 13:42:26 +0000 Subject: [PATCH 1812/5214] pinctrl: tegra: Export tegra_pinctrl_probe() Export tegra_pinctrl_probe() to allow SoC-specific Tegra pinctrl drivers built as modules to use the common probe path. Signed-off-by: Prathamesh Shete Reviewed-by: Jon Hunter Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/pinctrl-tegra.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index bac2adeb5c63..3f58f7db525f 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -936,3 +937,4 @@ int tegra_pinctrl_probe(struct platform_device *pdev, return 0; } +EXPORT_SYMBOL_GPL(tegra_pinctrl_probe); From 9323f8a0e12ccd30f5855067924c41224e7364ca Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 27 Apr 2026 13:42:27 +0000 Subject: [PATCH 1813/5214] dt-bindings: pinctrl: Document Tegra238 pin controllers Tegra238 contains two pin controllers. Document their compatible strings and describe the list of pins and functions that they provide. Signed-off-by: Prathamesh Shete Reviewed-by: Krzysztof Kozlowski Reviewed-by: Jon Hunter Signed-off-by: Linus Walleij --- .../pinctrl/nvidia,tegra238-pinmux-aon.yaml | 82 +++++++ .../nvidia,tegra238-pinmux-common.yaml | 73 ++++++ .../pinctrl/nvidia,tegra238-pinmux.yaml | 219 ++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-common.yaml create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml new file mode 100644 index 000000000000..ab9264d87c88 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra238-pinmux-aon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra238 AON Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra238-pinmux-aon + + reg: + maxItems: 1 + +patternProperties: + "^pinmux(-[a-z0-9-]+)?$": + type: object + + # pin groups + additionalProperties: + $ref: nvidia,tegra238-pinmux-common.yaml + + properties: + nvidia,pins: + items: + enum: [ bootv_ctl_n_paa0, soc_gpio00_paa1, vcomp_alert_paa2, + pwm1_paa3, batt_oc_paa4, soc_gpio04_paa5, + soc_gpio25_paa6, soc_gpio26_paa7, + hdmi_cec_pbb0, + spi2_sck_pcc0, spi2_miso_pcc1, spi2_mosi_pcc2, + spi2_cs0_pcc3, spi2_cs1_pcc4, uart3_tx_pcc5, + uart3_rx_pcc6, gen2_i2c_scl_pcc7, + gen2_i2c_sda_pdd0, gen8_i2c_scl_pdd1, + gen8_i2c_sda_pdd2, touch_clk_pdd3, dmic1_clk_pdd4, + dmic1_dat_pdd5, soc_gpio19_pdd6, pwm2_pdd7, + pwm3_pee0, pwm7_pee1, + # drive groups (ordered PAA, PBB, PCC, PDD, PEE) + drive_bootv_ctl_n_paa0, drive_soc_gpio00_paa1, + drive_vcomp_alert_paa2, drive_pwm1_paa3, + drive_batt_oc_paa4, drive_soc_gpio04_paa5, + drive_soc_gpio25_paa6, drive_soc_gpio26_paa7, + drive_hdmi_cec_pbb0, + drive_spi2_sck_pcc0, drive_spi2_miso_pcc1, + drive_spi2_mosi_pcc2, drive_spi2_cs0_pcc3, + drive_spi2_cs1_pcc4, drive_uart3_tx_pcc5, + drive_uart3_rx_pcc6, drive_gen2_i2c_scl_pcc7, + drive_gen2_i2c_sda_pdd0, drive_gen8_i2c_scl_pdd1, + drive_gen8_i2c_sda_pdd2, drive_touch_clk_pdd3, + drive_dmic1_clk_pdd4, drive_dmic1_dat_pdd5, + drive_soc_gpio19_pdd6, drive_pwm2_pdd7, + drive_pwm3_pee0, drive_pwm7_pee1 ] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + pinmux@c300000 { + compatible = "nvidia,tegra238-pinmux-aon"; + reg = <0x0c300000 0x4000>; + + pinctrl-names = "cec"; + pinctrl-0 = <&cec_state>; + + cec_state: pinmux-cec { + cec { + nvidia,pins = "hdmi_cec_pbb0"; + nvidia,function = "hdmi_cec"; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-common.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-common.yaml new file mode 100644 index 000000000000..5c7608981f2d --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-common.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra238-pinmux-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra238 Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +$ref: nvidia,tegra-pinmux-common.yaml + +properties: + nvidia,function: + enum: [ dca_vsync, dca_hsync, displaya, rsvd0, i2c7_clk, i2c7_dat, + i2c4_dat, i2c4_clk, i2c9_dat, i2c9_clk, usb_vbus_en0, + usb_vbus_en1, spi3_din, spi1_cs0, spi3_cs0, spi1_din, + spi3_cs1, spi1_sck, spi3_sck, spi1_cs1, spi1_dout, spi3_dout, + gp_pwm5, gp_pwm6, extperiph2_clk, extperiph1_clk, i2c3_dat, + i2c3_clk, extperiph4_clk, extperiph3_clk, dmic2_dat, + dmic2_clk, uarta_cts, uarta_rts, uarta_rxd, uarta_txd, + i2c5_clk, i2c5_dat, uartd_cts, uartd_rts, uartd_rxd, + uartd_txd, i2c1_clk, i2c1_dat, sdmmc1_cd, i2s2_sclk, + i2s2_sdata_out, i2s2_sdata_in, i2s2_lrck, i2s4_sclk, + i2s4_sdata_out, i2s4_sdata_in, i2s4_lrck, i2s1_sclk, + i2s1_sdata_out, i2s1_sdata_in, i2s1_lrck, aud_mclk, + i2s3_lrck, i2s3_sclk, i2s3_sdata_in, i2s3_sdata_out, + pe2_clkreq_l, pe1_clkreq_l, pe1_rst_l, pe0_clkreq_l, + pe0_rst_l, pe2_rst_l, pe3_clkreq_l, pe3_rst_l, + dp_aux_ch0_hpd, qspi0_io0, qspi0_io1, qspi0_sck, qspi0_cs_n, + uartg_cts, uartg_rts, uartg_txd, uartg_rxd, sdmmc1_clk, + sdmmc1_cmd, sdmmc1_comp, sdmmc1_dat3, sdmmc1_dat2, + sdmmc1_dat1, sdmmc1_dat0, ufs0, soc_therm_oc1, hdmi_cec, + gp_pwm4, uartc_rxd, uartc_txd, i2c8_dat, i2c8_clk, + spi2_dout, i2c2_clk, spi2_cs0, i2c2_dat, spi2_sck, spi2_din, + ppc_mode_1, ppc_ready, ppc_mode_2, ppc_cc, ppc_mode_0, + ppc_int_n, uarte_txd, uarte_rxd, uartb_txd, uartb_rxd, + uartb_cts, uartb_rts, uarte_cts, uarte_rts, gp_pwm7, + gp_pwm2, gp_pwm3, gp_pwm1, spi2_cs1, dmic1_clk, dmic1_dat, + rsvd1, dcb_hsync, dcb_vsync, soc_therm_oc4, gp_pwm8, + nv_therm_fan_tach0, wdt_reset_outa, ccla_la_trigger_mux, + dspk1_dat, dspk1_clk, nv_therm_fan_tach1, dspk0_dat, + dspk0_clk, i2s5_sclk, i2s6_lrck, i2s6_sdata_in, i2s6_sclk, + i2s6_sdata_out, i2s5_lrck, i2s5_sdata_out, i2s5_sdata_in, + sdmmc1_pe3_rst_l, sdmmc1_pe3_clkreq_l, touch_clk, + ppc_i2c_dat, wdt_reset_outb, spi5_cs1, ppc_rst_n, + ppc_i2c_clk, spi4_cs1, soc_therm_oc3, spi5_sck, spi5_miso, + spi4_sck, spi4_miso, spi4_cs0, spi4_mosi, spi5_cs0, + spi5_mosi, led_blink, rsvd2, dmic3_clk, dmic3_dat, + dmic4_clk, dmic4_dat, tsc_edge_out0, tsc_edge_out3, + tsc_edge_out1, tsc_edge_out2, dmic5_clk, dmic5_dat, rsvd3, + sdmmc1_wp, tsc_edge_out0a, tsc_edge_out0d, tsc_edge_out0b, + tsc_edge_out0c, soc_therm_oc2 ] + + # out of the common properties, only these are allowed for Tegra238 + nvidia,pins: true + nvidia,pull: true + nvidia,tristate: true + nvidia,schmitt: true + nvidia,enable-input: true + nvidia,open-drain: true + nvidia,lock: true + nvidia,drive-type: true + nvidia,io-hv: true + +required: + - nvidia,pins + +additionalProperties: false + +... diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux.yaml new file mode 100644 index 000000000000..92d276634d76 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux.yaml @@ -0,0 +1,219 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra238-pinmux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra238 Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra238-pinmux + + reg: + maxItems: 1 + +patternProperties: + "^pinmux(-[a-z0-9-]+)?$": + type: object + + # pin groups + additionalProperties: + $ref: nvidia,tegra238-pinmux-common.yaml + + properties: + nvidia,pins: + items: + enum: [ gpu_pwr_req_pa0, gp_pwm5_pa1, gp_pwm6_pa2, spi3_sck_pa3, + spi3_miso_pa4, spi3_mosi_pa5, spi3_cs0_pa6, spi3_cs1_pa7, + spi1_sck_pb0, spi1_miso_pb1, spi1_mosi_pb2, spi1_cs0_pb3, + spi1_cs1_pb4, pwr_i2c_scl_pc0, pwr_i2c_sda_pc1, + extperiph1_clk_pc2, extperiph2_clk_pc3, cam_i2c_scl_pc4, + cam_i2c_sda_pc5, soc_gpio23_pc6, soc_gpio24_pc7, + soc_gpio27_pd0, soc_gpio55_pd1, soc_gpio29_pd2, + soc_gpio33_pd3, soc_gpio32_pd4, soc_gpio35_pd5, + soc_gpio37_pd6, soc_gpio56_pd7, uart1_tx_pe0, + uart1_rx_pe1, uart1_rts_pe2, uart1_cts_pe3, + soc_gpio13_pf0, soc_gpio14_pf1, soc_gpio15_pf2, + soc_gpio16_pf3, soc_gpio17_pf4, soc_gpio18_pf5, + soc_gpio20_pf6, soc_gpio21_pf7, soc_gpio22_pg0, + soc_gpio06_pg1, uart4_tx_pg2, uart4_rx_pg3, + uart4_rts_pg4, uart4_cts_pg5, soc_gpio41_pg6, + soc_gpio42_pg7, soc_gpio43_ph0, soc_gpio44_ph1, + gen1_i2c_scl_ph2, gen1_i2c_sda_ph3, cpu_pwr_req_ph4, + soc_gpio07_ph5, dap3_clk_pj0, dap3_dout_pj1, + dap3_din_pj2, dap3_fs_pj3, soc_gpio57_pj4, + soc_gpio58_pj5, soc_gpio59_pj6, soc_gpio60_pj7, + soc_gpio45_pk0, soc_gpio46_pk1, soc_gpio47_pk2, + soc_gpio48_pk3, qspi0_sck_pl0, qspi0_io0_pl1, + qspi0_io1_pl2, qspi0_cs_n_pl3, soc_gpio152_pl4, + soc_gpio153_pl5, soc_gpio154_pl6, soc_gpio155_pl7, + soc_gpio156_pm0, soc_gpio157_pm1, soc_gpio158_pm2, + soc_gpio159_pm3, soc_gpio160_pm4, soc_gpio161_pm5, + soc_gpio162_pm6, uart7_tx_pm7, uart7_rx_pn0, + uart7_rts_pn1, uart7_cts_pn2, soc_gpio167_pp0, + soc_gpio168_pp1, soc_gpio169_pp2, soc_gpio170_pp3, + dap4_sclk_pp4, dap4_dout_pp5, dap4_din_pp6, dap4_fs_pp7, + soc_gpio171_pq0, soc_gpio172_pq1, soc_gpio173_pq2, + soc_gpio61_pr0, soc_gpio62_pr1, soc_gpio63_pr2, + soc_gpio64_pr3, soc_gpio65_pr4, soc_gpio66_pr5, + soc_gpio67_pr6, soc_gpio68_pr7, gen4_i2c_scl_ps0, + gen4_i2c_sda_ps1, soc_gpio75_ps2, gen7_i2c_scl_ps3, + gen7_i2c_sda_ps4, soc_gpio78_ps5, gen9_i2c_scl_ps6, + gen9_i2c_sda_ps7, soc_gpio81_pt0, soc_gpio36_pt1, + soc_gpio53_pt2, soc_gpio38_pt3, soc_gpio40_pt4, + soc_gpio34_pt5, usb_vbus_en0_pt6, usb_vbus_en1_pt7, + sdmmc1_clk_pu0, sdmmc1_cmd_pu1, sdmmc1_dat0_pu2, + sdmmc1_dat1_pu3, sdmmc1_dat2_pu4, sdmmc1_dat3_pu5, + ufs0_ref_clk_pv0, ufs0_rst_n_pv1, pex_l0_clkreq_n_pw0, + pex_l0_rst_n_pw1, pex_l1_clkreq_n_pw2, + pex_l1_rst_n_pw3, pex_l2_clkreq_n_pw4, + pex_l2_rst_n_pw5, pex_l3_clkreq_n_pw6, + pex_l3_rst_n_pw7, pex_wake_n_px0, dp_aux_ch0_hpd_px1, + bootv_ctl_n_paa0, soc_gpio00_paa1, vcomp_alert_paa2, + pwm1_paa3, batt_oc_paa4, soc_gpio04_paa5, + soc_gpio25_paa6, soc_gpio26_paa7, hdmi_cec_pbb0, + spi2_sck_pcc0, spi2_miso_pcc1, spi2_mosi_pcc2, + spi2_cs0_pcc3, spi2_cs1_pcc4, uart3_tx_pcc5, + uart3_rx_pcc6, gen2_i2c_scl_pcc7, gen2_i2c_sda_pdd0, + gen8_i2c_scl_pdd1, gen8_i2c_sda_pdd2, touch_clk_pdd3, + dmic1_clk_pdd4, dmic1_dat_pdd5, soc_gpio19_pdd6, + pwm2_pdd7, pwm3_pee0, pwm7_pee1, soc_gpio49_pee2, + soc_gpio82_pee3, soc_gpio50_pee4, soc_gpio83_pee5, + soc_gpio69_pff0, soc_gpio70_pff1, soc_gpio71_pff2, + soc_gpio72_pff3, soc_gpio73_pff4, soc_gpio74_pff5, + soc_gpio80_pff6, soc_gpio76_pff7, soc_gpio77_pgg0, + soc_gpio84_pgg1, uart2_tx_pgg2, uart2_rx_pgg3, + uart2_rts_pgg4, uart2_cts_pgg5, soc_gpio85_pgg6, + uart5_tx_pgg7, uart5_rx_phh0, uart5_rts_phh1, + uart5_cts_phh2, soc_gpio86_phh3, sdmmc1_comp, + # drive groups + drive_soc_gpio36_pt1, drive_soc_gpio53_pt2, + drive_soc_gpio38_pt3, drive_soc_gpio40_pt4, + drive_soc_gpio75_ps2, drive_soc_gpio81_pt0, + drive_soc_gpio78_ps5, drive_soc_gpio34_pt5, + drive_gen7_i2c_scl_ps3, drive_gen7_i2c_sda_ps4, + drive_gen4_i2c_sda_ps1, drive_gen4_i2c_scl_ps0, + drive_gen9_i2c_sda_ps7, drive_gen9_i2c_scl_ps6, + drive_usb_vbus_en0_pt6, drive_usb_vbus_en1_pt7, + drive_soc_gpio61_pr0, drive_soc_gpio62_pr1, + drive_soc_gpio63_pr2, drive_soc_gpio64_pr3, + drive_soc_gpio65_pr4, drive_soc_gpio66_pr5, + drive_soc_gpio67_pr6, drive_soc_gpio68_pr7, + drive_spi3_miso_pa4, drive_spi1_cs0_pb3, + drive_spi3_cs0_pa6, drive_spi1_miso_pb1, + drive_spi3_cs1_pa7, drive_spi1_sck_pb0, + drive_spi3_sck_pa3, drive_spi1_cs1_pb4, + drive_spi1_mosi_pb2, drive_spi3_mosi_pa5, + drive_gpu_pwr_req_pa0, drive_gp_pwm5_pa1, + drive_gp_pwm6_pa2, drive_extperiph2_clk_pc3, + drive_extperiph1_clk_pc2, drive_cam_i2c_sda_pc5, + drive_cam_i2c_scl_pc4, drive_soc_gpio23_pc6, + drive_soc_gpio24_pc7, drive_soc_gpio27_pd0, + drive_soc_gpio29_pd2, drive_soc_gpio32_pd4, + drive_soc_gpio33_pd3, drive_soc_gpio35_pd5, + drive_soc_gpio37_pd6, drive_soc_gpio56_pd7, + drive_soc_gpio55_pd1, drive_uart1_cts_pe3, + drive_uart1_rts_pe2, drive_uart1_rx_pe1, + drive_uart1_tx_pe0, drive_pwr_i2c_scl_pc0, + drive_pwr_i2c_sda_pc1, drive_cpu_pwr_req_ph4, + drive_uart4_cts_pg5, drive_uart4_rts_pg4, + drive_uart4_rx_pg3, drive_uart4_tx_pg2, + drive_gen1_i2c_scl_ph2, drive_gen1_i2c_sda_ph3, + drive_soc_gpio20_pf6, drive_soc_gpio21_pf7, + drive_soc_gpio22_pg0, drive_soc_gpio13_pf0, + drive_soc_gpio14_pf1, drive_soc_gpio15_pf2, + drive_soc_gpio16_pf3, drive_soc_gpio17_pf4, + drive_soc_gpio18_pf5, drive_soc_gpio41_pg6, + drive_soc_gpio42_pg7, drive_soc_gpio43_ph0, + drive_soc_gpio44_ph1, drive_soc_gpio06_pg1, + drive_soc_gpio07_ph5, drive_dap4_sclk_pp4, + drive_dap4_dout_pp5, drive_dap4_din_pp6, + drive_dap4_fs_pp7, drive_soc_gpio167_pp0, + drive_soc_gpio168_pp1, drive_soc_gpio169_pp2, + drive_soc_gpio170_pp3, drive_soc_gpio171_pq0, + drive_soc_gpio172_pq1, drive_soc_gpio173_pq2, + drive_soc_gpio45_pk0, drive_soc_gpio46_pk1, + drive_soc_gpio47_pk2, drive_soc_gpio48_pk3, + drive_soc_gpio57_pj4, drive_soc_gpio58_pj5, + drive_soc_gpio59_pj6, drive_soc_gpio60_pj7, + drive_dap3_fs_pj3, drive_dap3_clk_pj0, + drive_dap3_din_pj2, drive_dap3_dout_pj1, + drive_pex_l2_clkreq_n_pw4, drive_pex_wake_n_px0, + drive_pex_l1_clkreq_n_pw2, drive_pex_l1_rst_n_pw3, + drive_pex_l0_clkreq_n_pw0, drive_pex_l0_rst_n_pw1, + drive_pex_l2_rst_n_pw5, drive_pex_l3_clkreq_n_pw6, + drive_pex_l3_rst_n_pw7, drive_dp_aux_ch0_hpd_px1, + drive_qspi0_io0_pl1, drive_qspi0_io1_pl2, + drive_qspi0_sck_pl0, drive_qspi0_cs_n_pl3, + drive_soc_gpio156_pm0, drive_soc_gpio155_pl7, + drive_soc_gpio160_pm4, drive_soc_gpio154_pl6, + drive_soc_gpio152_pl4, drive_soc_gpio153_pl5, + drive_soc_gpio161_pm5, drive_soc_gpio162_pm6, + drive_soc_gpio159_pm3, drive_soc_gpio157_pm1, + drive_soc_gpio158_pm2, drive_uart7_cts_pn2, + drive_uart7_rts_pn1, drive_uart7_tx_pm7, + drive_uart7_rx_pn0, drive_sdmmc1_clk_pu0, + drive_sdmmc1_cmd_pu1, drive_sdmmc1_dat3_pu5, + drive_sdmmc1_dat2_pu4, drive_sdmmc1_dat1_pu3, + drive_sdmmc1_dat0_pu2, drive_ufs0_rst_n_pv1, + drive_ufs0_ref_clk_pv0, drive_batt_oc_paa4, + drive_bootv_ctl_n_paa0, drive_vcomp_alert_paa2, + drive_hdmi_cec_pbb0, drive_touch_clk_pdd3, + drive_uart3_rx_pcc6, drive_uart3_tx_pcc5, + drive_gen8_i2c_sda_pdd2, drive_gen8_i2c_scl_pdd1, + drive_spi2_mosi_pcc2, drive_gen2_i2c_scl_pcc7, + drive_spi2_cs0_pcc3, drive_gen2_i2c_sda_pdd0, + drive_spi2_sck_pcc0, drive_spi2_miso_pcc1, + drive_soc_gpio49_pee2, drive_soc_gpio50_pee4, + drive_soc_gpio82_pee3, drive_soc_gpio71_pff2, + drive_soc_gpio76_pff7, drive_soc_gpio74_pff5, + drive_soc_gpio00_paa1, drive_soc_gpio19_pdd6, + drive_soc_gpio86_phh3, drive_soc_gpio72_pff3, + drive_soc_gpio77_pgg0, drive_soc_gpio80_pff6, + drive_soc_gpio84_pgg1, drive_soc_gpio83_pee5, + drive_soc_gpio73_pff4, drive_soc_gpio70_pff1, + drive_soc_gpio04_paa5, drive_soc_gpio85_pgg6, + drive_soc_gpio69_pff0, drive_soc_gpio25_paa6, + drive_soc_gpio26_paa7, drive_uart5_tx_pgg7, + drive_uart5_rx_phh0, drive_uart2_tx_pgg2, + drive_uart2_rx_pgg3, drive_uart2_cts_pgg5, + drive_uart2_rts_pgg4, drive_uart5_cts_phh2, + drive_uart5_rts_phh1, drive_pwm7_pee1, + drive_pwm2_pdd7, drive_pwm3_pee0, drive_pwm1_paa3, + drive_spi2_cs1_pcc4, drive_dmic1_clk_pdd4, + drive_dmic1_dat_pdd5, drive_sdmmc1_comp ] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + pinmux@2430000 { + compatible = "nvidia,tegra238-pinmux"; + reg = <0x2430000 0x17000>; + + pinctrl-names = "pex_rst"; + pinctrl-0 = <&pex_rst_c5_out_state>; + + pex_rst_c5_out_state: pinmux-pex-rst-c5-out { + pexrst { + nvidia,pins = "pex_l3_rst_n_pw7"; + nvidia,schmitt = ; + nvidia,enable-input = ; + nvidia,io-hv = ; + nvidia,tristate = ; + nvidia,pull = ; + }; + }; + }; +... From 25cac7292d49f4fc5ee352eff7c8874d1b296380 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 27 Apr 2026 13:42:28 +0000 Subject: [PATCH 1814/5214] pinctrl: tegra: Add Tegra238 pinmux driver Add support for the two pin controllers (MAIN and AON) found on Tegra238. Signed-off-by: Prathamesh Shete Reviewed-by: Jon Hunter Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/Kconfig | 10 + drivers/pinctrl/tegra/Makefile | 1 + drivers/pinctrl/tegra/pinctrl-tegra238.c | 2056 ++++++++++++++++++++++ 3 files changed, 2067 insertions(+) create mode 100644 drivers/pinctrl/tegra/pinctrl-tegra238.c diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index 3e8789871f0f..96b9f5c8ed51 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -37,6 +37,16 @@ config PINCTRL_TEGRA234 bool "NVIDIA Tegra234 pin controller" if COMPILE_TEST && !ARCH_TEGRA select PINCTRL_TEGRA +config PINCTRL_TEGRA238 + tristate "NVIDIA Tegra238 pinctrl driver" + default m if ARCH_TEGRA_238_SOC + select PINCTRL_TEGRA + help + Say Y or M here to enable support for the pinctrl driver for + NVIDIA Tegra238 SoC. This driver controls the pin multiplexing + and configuration for the MAIN and AON pin controllers found + on Tegra238. + config PINCTRL_TEGRA_XUSB bool "NVIDIA Tegra XUSB pin controller" if COMPILE_TEST && !ARCH_TEGRA default y if ARCH_TEGRA diff --git a/drivers/pinctrl/tegra/Makefile b/drivers/pinctrl/tegra/Makefile index 82176526549e..ce700bbcbf6e 100644 --- a/drivers/pinctrl/tegra/Makefile +++ b/drivers/pinctrl/tegra/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_PINCTRL_TEGRA210) += pinctrl-tegra210.o obj-$(CONFIG_PINCTRL_TEGRA186) += pinctrl-tegra186.o obj-$(CONFIG_PINCTRL_TEGRA194) += pinctrl-tegra194.o obj-$(CONFIG_PINCTRL_TEGRA234) += pinctrl-tegra234.o +obj-$(CONFIG_PINCTRL_TEGRA238) += pinctrl-tegra238.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o diff --git a/drivers/pinctrl/tegra/pinctrl-tegra238.c b/drivers/pinctrl/tegra/pinctrl-tegra238.c new file mode 100644 index 000000000000..421da334151c --- /dev/null +++ b/drivers/pinctrl/tegra/pinctrl-tegra238.c @@ -0,0 +1,2056 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Pinctrl data for the NVIDIA Tegra238 pinmux + * + * Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "pinctrl-tegra.h" + +/* Define unique ID for each pins */ +enum { + TEGRA_PIN_GPU_PWR_REQ_PA0, + TEGRA_PIN_GP_PWM5_PA1, + TEGRA_PIN_GP_PWM6_PA2, + TEGRA_PIN_SPI3_SCK_PA3, + TEGRA_PIN_SPI3_MISO_PA4, + TEGRA_PIN_SPI3_MOSI_PA5, + TEGRA_PIN_SPI3_CS0_PA6, + TEGRA_PIN_SPI3_CS1_PA7, + TEGRA_PIN_SPI1_SCK_PB0, + TEGRA_PIN_SPI1_MISO_PB1, + TEGRA_PIN_SPI1_MOSI_PB2, + TEGRA_PIN_SPI1_CS0_PB3, + TEGRA_PIN_SPI1_CS1_PB4, + TEGRA_PIN_PWR_I2C_SCL_PC0, + TEGRA_PIN_PWR_I2C_SDA_PC1, + TEGRA_PIN_EXTPERIPH1_CLK_PC2, + TEGRA_PIN_EXTPERIPH2_CLK_PC3, + TEGRA_PIN_CAM_I2C_SCL_PC4, + TEGRA_PIN_CAM_I2C_SDA_PC5, + TEGRA_PIN_SOC_GPIO23_PC6, + TEGRA_PIN_SOC_GPIO24_PC7, + TEGRA_PIN_SOC_GPIO27_PD0, + TEGRA_PIN_SOC_GPIO55_PD1, + TEGRA_PIN_SOC_GPIO29_PD2, + TEGRA_PIN_SOC_GPIO33_PD3, + TEGRA_PIN_SOC_GPIO32_PD4, + TEGRA_PIN_SOC_GPIO35_PD5, + TEGRA_PIN_SOC_GPIO37_PD6, + TEGRA_PIN_SOC_GPIO56_PD7, + TEGRA_PIN_UART1_TX_PE0, + TEGRA_PIN_UART1_RX_PE1, + TEGRA_PIN_UART1_RTS_PE2, + TEGRA_PIN_UART1_CTS_PE3, + TEGRA_PIN_SOC_GPIO13_PF0, + TEGRA_PIN_SOC_GPIO14_PF1, + TEGRA_PIN_SOC_GPIO15_PF2, + TEGRA_PIN_SOC_GPIO16_PF3, + TEGRA_PIN_SOC_GPIO17_PF4, + TEGRA_PIN_SOC_GPIO18_PF5, + TEGRA_PIN_SOC_GPIO20_PF6, + TEGRA_PIN_SOC_GPIO21_PF7, + TEGRA_PIN_SOC_GPIO22_PG0, + TEGRA_PIN_SOC_GPIO06_PG1, + TEGRA_PIN_UART4_TX_PG2, + TEGRA_PIN_UART4_RX_PG3, + TEGRA_PIN_UART4_RTS_PG4, + TEGRA_PIN_UART4_CTS_PG5, + TEGRA_PIN_SOC_GPIO41_PG6, + TEGRA_PIN_SOC_GPIO42_PG7, + TEGRA_PIN_SOC_GPIO43_PH0, + TEGRA_PIN_SOC_GPIO44_PH1, + TEGRA_PIN_GEN1_I2C_SCL_PH2, + TEGRA_PIN_GEN1_I2C_SDA_PH3, + TEGRA_PIN_CPU_PWR_REQ_PH4, + TEGRA_PIN_SOC_GPIO07_PH5, + TEGRA_PIN_DAP3_CLK_PJ0, + TEGRA_PIN_DAP3_DOUT_PJ1, + TEGRA_PIN_DAP3_DIN_PJ2, + TEGRA_PIN_DAP3_FS_PJ3, + TEGRA_PIN_SOC_GPIO57_PJ4, + TEGRA_PIN_SOC_GPIO58_PJ5, + TEGRA_PIN_SOC_GPIO59_PJ6, + TEGRA_PIN_SOC_GPIO60_PJ7, + TEGRA_PIN_SOC_GPIO45_PK0, + TEGRA_PIN_SOC_GPIO46_PK1, + TEGRA_PIN_SOC_GPIO47_PK2, + TEGRA_PIN_SOC_GPIO48_PK3, + TEGRA_PIN_QSPI0_SCK_PL0, + TEGRA_PIN_QSPI0_IO0_PL1, + TEGRA_PIN_QSPI0_IO1_PL2, + TEGRA_PIN_QSPI0_CS_N_PL3, + TEGRA_PIN_SOC_GPIO152_PL4, + TEGRA_PIN_SOC_GPIO153_PL5, + TEGRA_PIN_SOC_GPIO154_PL6, + TEGRA_PIN_SOC_GPIO155_PL7, + TEGRA_PIN_SOC_GPIO156_PM0, + TEGRA_PIN_SOC_GPIO157_PM1, + TEGRA_PIN_SOC_GPIO158_PM2, + TEGRA_PIN_SOC_GPIO159_PM3, + TEGRA_PIN_SOC_GPIO160_PM4, + TEGRA_PIN_SOC_GPIO161_PM5, + TEGRA_PIN_SOC_GPIO162_PM6, + TEGRA_PIN_UART7_TX_PM7, + TEGRA_PIN_UART7_RX_PN0, + TEGRA_PIN_UART7_RTS_PN1, + TEGRA_PIN_UART7_CTS_PN2, + TEGRA_PIN_SOC_GPIO167_PP0, + TEGRA_PIN_SOC_GPIO168_PP1, + TEGRA_PIN_SOC_GPIO169_PP2, + TEGRA_PIN_SOC_GPIO170_PP3, + TEGRA_PIN_DAP4_SCLK_PP4, + TEGRA_PIN_DAP4_DOUT_PP5, + TEGRA_PIN_DAP4_DIN_PP6, + TEGRA_PIN_DAP4_FS_PP7, + TEGRA_PIN_SOC_GPIO171_PQ0, + TEGRA_PIN_SOC_GPIO172_PQ1, + TEGRA_PIN_SOC_GPIO173_PQ2, + TEGRA_PIN_SOC_GPIO61_PR0, + TEGRA_PIN_SOC_GPIO62_PR1, + TEGRA_PIN_SOC_GPIO63_PR2, + TEGRA_PIN_SOC_GPIO64_PR3, + TEGRA_PIN_SOC_GPIO65_PR4, + TEGRA_PIN_SOC_GPIO66_PR5, + TEGRA_PIN_SOC_GPIO67_PR6, + TEGRA_PIN_SOC_GPIO68_PR7, + TEGRA_PIN_GEN4_I2C_SCL_PS0, + TEGRA_PIN_GEN4_I2C_SDA_PS1, + TEGRA_PIN_SOC_GPIO75_PS2, + TEGRA_PIN_GEN7_I2C_SCL_PS3, + TEGRA_PIN_GEN7_I2C_SDA_PS4, + TEGRA_PIN_SOC_GPIO78_PS5, + TEGRA_PIN_GEN9_I2C_SCL_PS6, + TEGRA_PIN_GEN9_I2C_SDA_PS7, + TEGRA_PIN_SOC_GPIO81_PT0, + TEGRA_PIN_SOC_GPIO36_PT1, + TEGRA_PIN_SOC_GPIO53_PT2, + TEGRA_PIN_SOC_GPIO38_PT3, + TEGRA_PIN_SOC_GPIO40_PT4, + TEGRA_PIN_SOC_GPIO34_PT5, + TEGRA_PIN_USB_VBUS_EN0_PT6, + TEGRA_PIN_USB_VBUS_EN1_PT7, + TEGRA_PIN_SDMMC1_CLK_PU0, + TEGRA_PIN_SDMMC1_CMD_PU1, + TEGRA_PIN_SDMMC1_DAT0_PU2, + TEGRA_PIN_SDMMC1_DAT1_PU3, + TEGRA_PIN_SDMMC1_DAT2_PU4, + TEGRA_PIN_SDMMC1_DAT3_PU5, + TEGRA_PIN_UFS0_REF_CLK_PV0, + TEGRA_PIN_UFS0_RST_N_PV1, + TEGRA_PIN_PEX_L0_CLKREQ_N_PW0, + TEGRA_PIN_PEX_L0_RST_N_PW1, + TEGRA_PIN_PEX_L1_CLKREQ_N_PW2, + TEGRA_PIN_PEX_L1_RST_N_PW3, + TEGRA_PIN_PEX_L2_CLKREQ_N_PW4, + TEGRA_PIN_PEX_L2_RST_N_PW5, + TEGRA_PIN_PEX_L3_CLKREQ_N_PW6, + TEGRA_PIN_PEX_L3_RST_N_PW7, + TEGRA_PIN_PEX_WAKE_N_PX0, + TEGRA_PIN_DP_AUX_CH0_HPD_PX1, + TEGRA_PIN_SDMMC1_COMP, +}; + +enum { + TEGRA_PIN_BOOTV_CTL_N_PAA0, + TEGRA_PIN_SOC_GPIO00_PAA1, + TEGRA_PIN_VCOMP_ALERT_PAA2, + TEGRA_PIN_PWM1_PAA3, + TEGRA_PIN_BATT_OC_PAA4, + TEGRA_PIN_SOC_GPIO04_PAA5, + TEGRA_PIN_SOC_GPIO25_PAA6, + TEGRA_PIN_SOC_GPIO26_PAA7, + TEGRA_PIN_HDMI_CEC_PBB0, + TEGRA_PIN_SPI2_SCK_PCC0, + TEGRA_PIN_SPI2_MISO_PCC1, + TEGRA_PIN_SPI2_MOSI_PCC2, + TEGRA_PIN_SPI2_CS0_PCC3, + TEGRA_PIN_SPI2_CS1_PCC4, + TEGRA_PIN_UART3_TX_PCC5, + TEGRA_PIN_UART3_RX_PCC6, + TEGRA_PIN_GEN2_I2C_SCL_PCC7, + TEGRA_PIN_GEN2_I2C_SDA_PDD0, + TEGRA_PIN_GEN8_I2C_SCL_PDD1, + TEGRA_PIN_GEN8_I2C_SDA_PDD2, + TEGRA_PIN_TOUCH_CLK_PDD3, + TEGRA_PIN_DMIC1_CLK_PDD4, + TEGRA_PIN_DMIC1_DAT_PDD5, + TEGRA_PIN_SOC_GPIO19_PDD6, + TEGRA_PIN_PWM2_PDD7, + TEGRA_PIN_PWM3_PEE0, + TEGRA_PIN_PWM7_PEE1, + TEGRA_PIN_SOC_GPIO49_PEE2, + TEGRA_PIN_SOC_GPIO82_PEE3, + TEGRA_PIN_SOC_GPIO50_PEE4, + TEGRA_PIN_SOC_GPIO83_PEE5, + TEGRA_PIN_SOC_GPIO69_PFF0, + TEGRA_PIN_SOC_GPIO70_PFF1, + TEGRA_PIN_SOC_GPIO71_PFF2, + TEGRA_PIN_SOC_GPIO72_PFF3, + TEGRA_PIN_SOC_GPIO73_PFF4, + TEGRA_PIN_SOC_GPIO74_PFF5, + TEGRA_PIN_SOC_GPIO80_PFF6, + TEGRA_PIN_SOC_GPIO76_PFF7, + TEGRA_PIN_SOC_GPIO77_PGG0, + TEGRA_PIN_SOC_GPIO84_PGG1, + TEGRA_PIN_UART2_TX_PGG2, + TEGRA_PIN_UART2_RX_PGG3, + TEGRA_PIN_UART2_RTS_PGG4, + TEGRA_PIN_UART2_CTS_PGG5, + TEGRA_PIN_SOC_GPIO85_PGG6, + TEGRA_PIN_UART5_TX_PGG7, + TEGRA_PIN_UART5_RX_PHH0, + TEGRA_PIN_UART5_RTS_PHH1, + TEGRA_PIN_UART5_CTS_PHH2, + TEGRA_PIN_SOC_GPIO86_PHH3, +}; + +/* Table for pin descriptor */ +static const struct pinctrl_pin_desc tegra238_pins[] = { + PINCTRL_PIN(TEGRA_PIN_GPU_PWR_REQ_PA0, "GPU_PWR_REQ_PA0"), + PINCTRL_PIN(TEGRA_PIN_GP_PWM5_PA1, "GP_PWM5_PA1"), + PINCTRL_PIN(TEGRA_PIN_GP_PWM6_PA2, "GP_PWM6_PA2"), + PINCTRL_PIN(TEGRA_PIN_SPI3_SCK_PA3, "SPI3_SCK_PA3"), + PINCTRL_PIN(TEGRA_PIN_SPI3_MISO_PA4, "SPI3_MISO_PA4"), + PINCTRL_PIN(TEGRA_PIN_SPI3_MOSI_PA5, "SPI3_MOSI_PA5"), + PINCTRL_PIN(TEGRA_PIN_SPI3_CS0_PA6, "SPI3_CS0_PA6"), + PINCTRL_PIN(TEGRA_PIN_SPI3_CS1_PA7, "SPI3_CS1_PA7"), + PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PB0, "SPI1_SCK_PB0"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PB1, "SPI1_MISO_PB1"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PB2, "SPI1_MOSI_PB2"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_PB3, "SPI1_CS0_PB3"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS1_PB4, "SPI1_CS1_PB4"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PC0, "PWR_I2C_SCL_PC0"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PC1, "PWR_I2C_SDA_PC1"), + PINCTRL_PIN(TEGRA_PIN_EXTPERIPH1_CLK_PC2, "EXTPERIPH1_CLK_PC2"), + PINCTRL_PIN(TEGRA_PIN_EXTPERIPH2_CLK_PC3, "EXTPERIPH2_CLK_PC3"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PC4, "CAM_I2C_SCL_PC4"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PC5, "CAM_I2C_SDA_PC5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO23_PC6, "SOC_GPIO23_PC6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO24_PC7, "SOC_GPIO24_PC7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO27_PD0, "SOC_GPIO27_PD0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO55_PD1, "SOC_GPIO55_PD1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO29_PD2, "SOC_GPIO29_PD2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO33_PD3, "SOC_GPIO33_PD3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO32_PD4, "SOC_GPIO32_PD4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO35_PD5, "SOC_GPIO35_PD5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO37_PD6, "SOC_GPIO37_PD6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO56_PD7, "SOC_GPIO56_PD7"), + PINCTRL_PIN(TEGRA_PIN_UART1_TX_PE0, "UART1_TX_PE0"), + PINCTRL_PIN(TEGRA_PIN_UART1_RX_PE1, "UART1_RX_PE1"), + PINCTRL_PIN(TEGRA_PIN_UART1_RTS_PE2, "UART1_RTS_PE2"), + PINCTRL_PIN(TEGRA_PIN_UART1_CTS_PE3, "UART1_CTS_PE3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO13_PF0, "SOC_GPIO13_PF0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO14_PF1, "SOC_GPIO14_PF1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO15_PF2, "SOC_GPIO15_PF2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO16_PF3, "SOC_GPIO16_PF3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO17_PF4, "SOC_GPIO17_PF4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO18_PF5, "SOC_GPIO18_PF5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO20_PF6, "SOC_GPIO20_PF6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO21_PF7, "SOC_GPIO21_PF7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO22_PG0, "SOC_GPIO22_PG0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO06_PG1, "SOC_GPIO06_PG1"), + PINCTRL_PIN(TEGRA_PIN_UART4_TX_PG2, "UART4_TX_PG2"), + PINCTRL_PIN(TEGRA_PIN_UART4_RX_PG3, "UART4_RX_PG3"), + PINCTRL_PIN(TEGRA_PIN_UART4_RTS_PG4, "UART4_RTS_PG4"), + PINCTRL_PIN(TEGRA_PIN_UART4_CTS_PG5, "UART4_CTS_PG5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO41_PG6, "SOC_GPIO41_PG6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO42_PG7, "SOC_GPIO42_PG7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO43_PH0, "SOC_GPIO43_PH0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO44_PH1, "SOC_GPIO44_PH1"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PH2, "GEN1_I2C_SCL_PH2"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PH3, "GEN1_I2C_SDA_PH3"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ_PH4, "CPU_PWR_REQ_PH4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO07_PH5, "SOC_GPIO07_PH5"), + PINCTRL_PIN(TEGRA_PIN_DAP3_CLK_PJ0, "DAP3_CLK_PJ0"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PJ1, "DAP3_DOUT_PJ1"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PJ2, "DAP3_DIN_PJ2"), + PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PJ3, "DAP3_FS_PJ3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO57_PJ4, "SOC_GPIO57_PJ4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO58_PJ5, "SOC_GPIO58_PJ5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO59_PJ6, "SOC_GPIO59_PJ6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO60_PJ7, "SOC_GPIO60_PJ7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO45_PK0, "SOC_GPIO45_PK0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO46_PK1, "SOC_GPIO46_PK1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO47_PK2, "SOC_GPIO47_PK2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO48_PK3, "SOC_GPIO48_PK3"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_SCK_PL0, "QSPI0_SCK_PL0"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO0_PL1, "QSPI0_IO0_PL1"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO1_PL2, "QSPI0_IO1_PL2"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_CS_N_PL3, "QSPI0_CS_N_PL3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO152_PL4, "SOC_GPIO152_PL4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO153_PL5, "SOC_GPIO153_PL5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO154_PL6, "SOC_GPIO154_PL6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO155_PL7, "SOC_GPIO155_PL7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO156_PM0, "SOC_GPIO156_PM0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO157_PM1, "SOC_GPIO157_PM1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO158_PM2, "SOC_GPIO158_PM2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO159_PM3, "SOC_GPIO159_PM3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO160_PM4, "SOC_GPIO160_PM4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO161_PM5, "SOC_GPIO161_PM5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO162_PM6, "SOC_GPIO162_PM6"), + PINCTRL_PIN(TEGRA_PIN_UART7_TX_PM7, "UART7_TX_PM7"), + PINCTRL_PIN(TEGRA_PIN_UART7_RX_PN0, "UART7_RX_PN0"), + PINCTRL_PIN(TEGRA_PIN_UART7_RTS_PN1, "UART7_RTS_PN1"), + PINCTRL_PIN(TEGRA_PIN_UART7_CTS_PN2, "UART7_CTS_PN2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO167_PP0, "SOC_GPIO167_PP0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO168_PP1, "SOC_GPIO168_PP1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO169_PP2, "SOC_GPIO169_PP2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO170_PP3, "SOC_GPIO170_PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP4, "DAP4_SCLK_PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP5, "DAP4_DOUT_PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP6, "DAP4_DIN_PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP7, "DAP4_FS_PP7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO171_PQ0, "SOC_GPIO171_PQ0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO172_PQ1, "SOC_GPIO172_PQ1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO173_PQ2, "SOC_GPIO173_PQ2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO61_PR0, "SOC_GPIO61_PR0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO62_PR1, "SOC_GPIO62_PR1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO63_PR2, "SOC_GPIO63_PR2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO64_PR3, "SOC_GPIO64_PR3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO65_PR4, "SOC_GPIO65_PR4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO66_PR5, "SOC_GPIO66_PR5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO67_PR6, "SOC_GPIO67_PR6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO68_PR7, "SOC_GPIO68_PR7"), + PINCTRL_PIN(TEGRA_PIN_GEN4_I2C_SCL_PS0, "GEN4_I2C_SCL_PS0"), + PINCTRL_PIN(TEGRA_PIN_GEN4_I2C_SDA_PS1, "GEN4_I2C_SDA_PS1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO75_PS2, "SOC_GPIO75_PS2"), + PINCTRL_PIN(TEGRA_PIN_GEN7_I2C_SCL_PS3, "GEN7_I2C_SCL_PS3"), + PINCTRL_PIN(TEGRA_PIN_GEN7_I2C_SDA_PS4, "GEN7_I2C_SDA_PS4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO78_PS5, "SOC_GPIO78_PS5"), + PINCTRL_PIN(TEGRA_PIN_GEN9_I2C_SCL_PS6, "GEN9_I2C_SCL_PS6"), + PINCTRL_PIN(TEGRA_PIN_GEN9_I2C_SDA_PS7, "GEN9_I2C_SDA_PS7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO81_PT0, "SOC_GPIO81_PT0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO36_PT1, "SOC_GPIO36_PT1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO53_PT2, "SOC_GPIO53_PT2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO38_PT3, "SOC_GPIO38_PT3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO40_PT4, "SOC_GPIO40_PT4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO34_PT5, "SOC_GPIO34_PT5"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN0_PT6, "USB_VBUS_EN0_PT6"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN1_PT7, "USB_VBUS_EN1_PT7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PU0, "SDMMC1_CLK_PU0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PU1, "SDMMC1_CMD_PU1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PU2, "SDMMC1_DAT0_PU2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PU3, "SDMMC1_DAT1_PU3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PU4, "SDMMC1_DAT2_PU4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PU5, "SDMMC1_DAT3_PU5"), + PINCTRL_PIN(TEGRA_PIN_UFS0_REF_CLK_PV0, "UFS0_REF_CLK_PV0"), + PINCTRL_PIN(TEGRA_PIN_UFS0_RST_N_PV1, "UFS0_RST_N_PV1"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_CLKREQ_N_PW0, "PEX_L0_CLKREQ_N_PW0"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_RST_N_PW1, "PEX_L0_RST_N_PW1"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_CLKREQ_N_PW2, "PEX_L1_CLKREQ_N_PW2"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_RST_N_PW3, "PEX_L1_RST_N_PW3"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_CLKREQ_N_PW4, "PEX_L2_CLKREQ_N_PW4"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_RST_N_PW5, "PEX_L2_RST_N_PW5"), + PINCTRL_PIN(TEGRA_PIN_PEX_L3_CLKREQ_N_PW6, "PEX_L3_CLKREQ_N_PW6"), + PINCTRL_PIN(TEGRA_PIN_PEX_L3_RST_N_PW7, "PEX_L3_RST_N_PW7"), + PINCTRL_PIN(TEGRA_PIN_PEX_WAKE_N_PX0, "PEX_WAKE_N_PX0"), + PINCTRL_PIN(TEGRA_PIN_DP_AUX_CH0_HPD_PX1, "DP_AUX_CH0_HPD_PX1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_COMP, "SDMMC1_COMP"), +}; + +static const struct pinctrl_pin_desc tegra238_aon_pins[] = { + PINCTRL_PIN(TEGRA_PIN_BOOTV_CTL_N_PAA0, "BOOTV_CTL_N_PAA0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO00_PAA1, "SOC_GPIO00_PAA1"), + PINCTRL_PIN(TEGRA_PIN_VCOMP_ALERT_PAA2, "VCOMP_ALERT_PAA2"), + PINCTRL_PIN(TEGRA_PIN_PWM1_PAA3, "PWM1_PAA3"), + PINCTRL_PIN(TEGRA_PIN_BATT_OC_PAA4, "BATT_OC_PAA4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO04_PAA5, "SOC_GPIO04_PAA5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO25_PAA6, "SOC_GPIO25_PAA6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO26_PAA7, "SOC_GPIO26_PAA7"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PBB0, "HDMI_CEC_PBB0"), + PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PCC0, "SPI2_SCK_PCC0"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PCC1, "SPI2_MISO_PCC1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PCC2, "SPI2_MOSI_PCC2"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_PCC3, "SPI2_CS0_PCC3"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_PCC4, "SPI2_CS1_PCC4"), + PINCTRL_PIN(TEGRA_PIN_UART3_TX_PCC5, "UART3_TX_PCC5"), + PINCTRL_PIN(TEGRA_PIN_UART3_RX_PCC6, "UART3_RX_PCC6"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PCC7, "GEN2_I2C_SCL_PCC7"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PDD0, "GEN2_I2C_SDA_PDD0"), + PINCTRL_PIN(TEGRA_PIN_GEN8_I2C_SCL_PDD1, "GEN8_I2C_SCL_PDD1"), + PINCTRL_PIN(TEGRA_PIN_GEN8_I2C_SDA_PDD2, "GEN8_I2C_SDA_PDD2"), + PINCTRL_PIN(TEGRA_PIN_TOUCH_CLK_PDD3, "TOUCH_CLK_PDD3"), + PINCTRL_PIN(TEGRA_PIN_DMIC1_CLK_PDD4, "DMIC1_CLK_PDD4"), + PINCTRL_PIN(TEGRA_PIN_DMIC1_DAT_PDD5, "DMIC1_DAT_PDD5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO19_PDD6, "SOC_GPIO19_PDD6"), + PINCTRL_PIN(TEGRA_PIN_PWM2_PDD7, "PWM2_PDD7"), + PINCTRL_PIN(TEGRA_PIN_PWM3_PEE0, "PWM3_PEE0"), + PINCTRL_PIN(TEGRA_PIN_PWM7_PEE1, "PWM7_PEE1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO49_PEE2, "SOC_GPIO49_PEE2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO82_PEE3, "SOC_GPIO82_PEE3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO50_PEE4, "SOC_GPIO50_PEE4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO83_PEE5, "SOC_GPIO83_PEE5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO69_PFF0, "SOC_GPIO69_PFF0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO70_PFF1, "SOC_GPIO70_PFF1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO71_PFF2, "SOC_GPIO71_PFF2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO72_PFF3, "SOC_GPIO72_PFF3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO73_PFF4, "SOC_GPIO73_PFF4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO74_PFF5, "SOC_GPIO74_PFF5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO80_PFF6, "SOC_GPIO80_PFF6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO76_PFF7, "SOC_GPIO76_PFF7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO77_PGG0, "SOC_GPIO77_PGG0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO84_PGG1, "SOC_GPIO84_PGG1"), + PINCTRL_PIN(TEGRA_PIN_UART2_TX_PGG2, "UART2_TX_PGG2"), + PINCTRL_PIN(TEGRA_PIN_UART2_RX_PGG3, "UART2_RX_PGG3"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_PGG4, "UART2_RTS_PGG4"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_PGG5, "UART2_CTS_PGG5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO85_PGG6, "SOC_GPIO85_PGG6"), + PINCTRL_PIN(TEGRA_PIN_UART5_TX_PGG7, "UART5_TX_PGG7"), + PINCTRL_PIN(TEGRA_PIN_UART5_RX_PHH0, "UART5_RX_PHH0"), + PINCTRL_PIN(TEGRA_PIN_UART5_RTS_PHH1, "UART5_RTS_PHH1"), + PINCTRL_PIN(TEGRA_PIN_UART5_CTS_PHH2, "UART5_CTS_PHH2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO86_PHH3, "SOC_GPIO86_PHH3"), +}; + +static const unsigned int gpu_pwr_req_pa0_pins[] = { + TEGRA_PIN_GPU_PWR_REQ_PA0, +}; + +static const unsigned int gp_pwm5_pa1_pins[] = { + TEGRA_PIN_GP_PWM5_PA1, +}; + +static const unsigned int gp_pwm6_pa2_pins[] = { + TEGRA_PIN_GP_PWM6_PA2, +}; + +static const unsigned int spi3_sck_pa3_pins[] = { + TEGRA_PIN_SPI3_SCK_PA3, +}; + +static const unsigned int spi3_miso_pa4_pins[] = { + TEGRA_PIN_SPI3_MISO_PA4, +}; + +static const unsigned int spi3_mosi_pa5_pins[] = { + TEGRA_PIN_SPI3_MOSI_PA5, +}; + +static const unsigned int spi3_cs0_pa6_pins[] = { + TEGRA_PIN_SPI3_CS0_PA6, +}; + +static const unsigned int spi3_cs1_pa7_pins[] = { + TEGRA_PIN_SPI3_CS1_PA7, +}; + +static const unsigned int spi1_sck_pb0_pins[] = { + TEGRA_PIN_SPI1_SCK_PB0, +}; + +static const unsigned int spi1_miso_pb1_pins[] = { + TEGRA_PIN_SPI1_MISO_PB1, +}; + +static const unsigned int spi1_mosi_pb2_pins[] = { + TEGRA_PIN_SPI1_MOSI_PB2, +}; + +static const unsigned int spi1_cs0_pb3_pins[] = { + TEGRA_PIN_SPI1_CS0_PB3, +}; + +static const unsigned int spi1_cs1_pb4_pins[] = { + TEGRA_PIN_SPI1_CS1_PB4, +}; + +static const unsigned int pwr_i2c_scl_pc0_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PC0, +}; + +static const unsigned int pwr_i2c_sda_pc1_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PC1, +}; + +static const unsigned int extperiph1_clk_pc2_pins[] = { + TEGRA_PIN_EXTPERIPH1_CLK_PC2, +}; + +static const unsigned int extperiph2_clk_pc3_pins[] = { + TEGRA_PIN_EXTPERIPH2_CLK_PC3, +}; + +static const unsigned int cam_i2c_scl_pc4_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PC4, +}; + +static const unsigned int cam_i2c_sda_pc5_pins[] = { + TEGRA_PIN_CAM_I2C_SDA_PC5, +}; + +static const unsigned int soc_gpio23_pc6_pins[] = { + TEGRA_PIN_SOC_GPIO23_PC6, +}; + +static const unsigned int soc_gpio24_pc7_pins[] = { + TEGRA_PIN_SOC_GPIO24_PC7, +}; + +static const unsigned int soc_gpio27_pd0_pins[] = { + TEGRA_PIN_SOC_GPIO27_PD0, +}; + +static const unsigned int soc_gpio55_pd1_pins[] = { + TEGRA_PIN_SOC_GPIO55_PD1, +}; + +static const unsigned int soc_gpio29_pd2_pins[] = { + TEGRA_PIN_SOC_GPIO29_PD2, +}; + +static const unsigned int soc_gpio33_pd3_pins[] = { + TEGRA_PIN_SOC_GPIO33_PD3, +}; + +static const unsigned int soc_gpio32_pd4_pins[] = { + TEGRA_PIN_SOC_GPIO32_PD4, +}; + +static const unsigned int soc_gpio35_pd5_pins[] = { + TEGRA_PIN_SOC_GPIO35_PD5, +}; + +static const unsigned int soc_gpio37_pd6_pins[] = { + TEGRA_PIN_SOC_GPIO37_PD6, +}; + +static const unsigned int soc_gpio56_pd7_pins[] = { + TEGRA_PIN_SOC_GPIO56_PD7, +}; + +static const unsigned int uart1_tx_pe0_pins[] = { + TEGRA_PIN_UART1_TX_PE0, +}; + +static const unsigned int uart1_rx_pe1_pins[] = { + TEGRA_PIN_UART1_RX_PE1, +}; + +static const unsigned int uart1_rts_pe2_pins[] = { + TEGRA_PIN_UART1_RTS_PE2, +}; + +static const unsigned int uart1_cts_pe3_pins[] = { + TEGRA_PIN_UART1_CTS_PE3, +}; + +static const unsigned int soc_gpio13_pf0_pins[] = { + TEGRA_PIN_SOC_GPIO13_PF0, +}; + +static const unsigned int soc_gpio14_pf1_pins[] = { + TEGRA_PIN_SOC_GPIO14_PF1, +}; + +static const unsigned int soc_gpio15_pf2_pins[] = { + TEGRA_PIN_SOC_GPIO15_PF2, +}; + +static const unsigned int soc_gpio16_pf3_pins[] = { + TEGRA_PIN_SOC_GPIO16_PF3, +}; + +static const unsigned int soc_gpio17_pf4_pins[] = { + TEGRA_PIN_SOC_GPIO17_PF4, +}; + +static const unsigned int soc_gpio18_pf5_pins[] = { + TEGRA_PIN_SOC_GPIO18_PF5, +}; + +static const unsigned int soc_gpio20_pf6_pins[] = { + TEGRA_PIN_SOC_GPIO20_PF6, +}; + +static const unsigned int soc_gpio21_pf7_pins[] = { + TEGRA_PIN_SOC_GPIO21_PF7, +}; + +static const unsigned int soc_gpio22_pg0_pins[] = { + TEGRA_PIN_SOC_GPIO22_PG0, +}; + +static const unsigned int soc_gpio06_pg1_pins[] = { + TEGRA_PIN_SOC_GPIO06_PG1, +}; + +static const unsigned int uart4_tx_pg2_pins[] = { + TEGRA_PIN_UART4_TX_PG2, +}; + +static const unsigned int uart4_rx_pg3_pins[] = { + TEGRA_PIN_UART4_RX_PG3, +}; + +static const unsigned int uart4_rts_pg4_pins[] = { + TEGRA_PIN_UART4_RTS_PG4, +}; + +static const unsigned int uart4_cts_pg5_pins[] = { + TEGRA_PIN_UART4_CTS_PG5, +}; + +static const unsigned int soc_gpio41_pg6_pins[] = { + TEGRA_PIN_SOC_GPIO41_PG6, +}; + +static const unsigned int soc_gpio42_pg7_pins[] = { + TEGRA_PIN_SOC_GPIO42_PG7, +}; + +static const unsigned int soc_gpio43_ph0_pins[] = { + TEGRA_PIN_SOC_GPIO43_PH0, +}; + +static const unsigned int soc_gpio44_ph1_pins[] = { + TEGRA_PIN_SOC_GPIO44_PH1, +}; + +static const unsigned int gen1_i2c_scl_ph2_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PH2, +}; + +static const unsigned int gen1_i2c_sda_ph3_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PH3, +}; + +static const unsigned int cpu_pwr_req_ph4_pins[] = { + TEGRA_PIN_CPU_PWR_REQ_PH4, +}; + +static const unsigned int soc_gpio07_ph5_pins[] = { + TEGRA_PIN_SOC_GPIO07_PH5, +}; + +static const unsigned int dap3_clk_pj0_pins[] = { + TEGRA_PIN_DAP3_CLK_PJ0, +}; + +static const unsigned int dap3_dout_pj1_pins[] = { + TEGRA_PIN_DAP3_DOUT_PJ1, +}; + +static const unsigned int dap3_din_pj2_pins[] = { + TEGRA_PIN_DAP3_DIN_PJ2, +}; + +static const unsigned int dap3_fs_pj3_pins[] = { + TEGRA_PIN_DAP3_FS_PJ3, +}; + +static const unsigned int soc_gpio57_pj4_pins[] = { + TEGRA_PIN_SOC_GPIO57_PJ4, +}; + +static const unsigned int soc_gpio58_pj5_pins[] = { + TEGRA_PIN_SOC_GPIO58_PJ5, +}; + +static const unsigned int soc_gpio59_pj6_pins[] = { + TEGRA_PIN_SOC_GPIO59_PJ6, +}; + +static const unsigned int soc_gpio60_pj7_pins[] = { + TEGRA_PIN_SOC_GPIO60_PJ7, +}; + +static const unsigned int soc_gpio45_pk0_pins[] = { + TEGRA_PIN_SOC_GPIO45_PK0, +}; + +static const unsigned int soc_gpio46_pk1_pins[] = { + TEGRA_PIN_SOC_GPIO46_PK1, +}; + +static const unsigned int soc_gpio47_pk2_pins[] = { + TEGRA_PIN_SOC_GPIO47_PK2, +}; + +static const unsigned int soc_gpio48_pk3_pins[] = { + TEGRA_PIN_SOC_GPIO48_PK3, +}; + +static const unsigned int qspi0_sck_pl0_pins[] = { + TEGRA_PIN_QSPI0_SCK_PL0, +}; + +static const unsigned int qspi0_io0_pl1_pins[] = { + TEGRA_PIN_QSPI0_IO0_PL1, +}; + +static const unsigned int qspi0_io1_pl2_pins[] = { + TEGRA_PIN_QSPI0_IO1_PL2, +}; + +static const unsigned int qspi0_cs_n_pl3_pins[] = { + TEGRA_PIN_QSPI0_CS_N_PL3, +}; + +static const unsigned int soc_gpio152_pl4_pins[] = { + TEGRA_PIN_SOC_GPIO152_PL4, +}; + +static const unsigned int soc_gpio153_pl5_pins[] = { + TEGRA_PIN_SOC_GPIO153_PL5, +}; + +static const unsigned int soc_gpio154_pl6_pins[] = { + TEGRA_PIN_SOC_GPIO154_PL6, +}; + +static const unsigned int soc_gpio155_pl7_pins[] = { + TEGRA_PIN_SOC_GPIO155_PL7, +}; + +static const unsigned int soc_gpio156_pm0_pins[] = { + TEGRA_PIN_SOC_GPIO156_PM0, +}; + +static const unsigned int soc_gpio157_pm1_pins[] = { + TEGRA_PIN_SOC_GPIO157_PM1, +}; + +static const unsigned int soc_gpio158_pm2_pins[] = { + TEGRA_PIN_SOC_GPIO158_PM2, +}; + +static const unsigned int soc_gpio159_pm3_pins[] = { + TEGRA_PIN_SOC_GPIO159_PM3, +}; + +static const unsigned int soc_gpio160_pm4_pins[] = { + TEGRA_PIN_SOC_GPIO160_PM4, +}; + +static const unsigned int soc_gpio161_pm5_pins[] = { + TEGRA_PIN_SOC_GPIO161_PM5, +}; + +static const unsigned int soc_gpio162_pm6_pins[] = { + TEGRA_PIN_SOC_GPIO162_PM6, +}; + +static const unsigned int uart7_tx_pm7_pins[] = { + TEGRA_PIN_UART7_TX_PM7, +}; + +static const unsigned int uart7_rx_pn0_pins[] = { + TEGRA_PIN_UART7_RX_PN0, +}; + +static const unsigned int uart7_rts_pn1_pins[] = { + TEGRA_PIN_UART7_RTS_PN1, +}; + +static const unsigned int uart7_cts_pn2_pins[] = { + TEGRA_PIN_UART7_CTS_PN2, +}; + +static const unsigned int soc_gpio167_pp0_pins[] = { + TEGRA_PIN_SOC_GPIO167_PP0, +}; + +static const unsigned int soc_gpio168_pp1_pins[] = { + TEGRA_PIN_SOC_GPIO168_PP1, +}; + +static const unsigned int soc_gpio169_pp2_pins[] = { + TEGRA_PIN_SOC_GPIO169_PP2, +}; + +static const unsigned int soc_gpio170_pp3_pins[] = { + TEGRA_PIN_SOC_GPIO170_PP3, +}; + +static const unsigned int dap4_sclk_pp4_pins[] = { + TEGRA_PIN_DAP4_SCLK_PP4, +}; + +static const unsigned int dap4_dout_pp5_pins[] = { + TEGRA_PIN_DAP4_DOUT_PP5, +}; + +static const unsigned int dap4_din_pp6_pins[] = { + TEGRA_PIN_DAP4_DIN_PP6, +}; + +static const unsigned int dap4_fs_pp7_pins[] = { + TEGRA_PIN_DAP4_FS_PP7, +}; + +static const unsigned int soc_gpio171_pq0_pins[] = { + TEGRA_PIN_SOC_GPIO171_PQ0, +}; + +static const unsigned int soc_gpio172_pq1_pins[] = { + TEGRA_PIN_SOC_GPIO172_PQ1, +}; + +static const unsigned int soc_gpio173_pq2_pins[] = { + TEGRA_PIN_SOC_GPIO173_PQ2, +}; + +static const unsigned int soc_gpio61_pr0_pins[] = { + TEGRA_PIN_SOC_GPIO61_PR0, +}; + +static const unsigned int soc_gpio62_pr1_pins[] = { + TEGRA_PIN_SOC_GPIO62_PR1, +}; + +static const unsigned int soc_gpio63_pr2_pins[] = { + TEGRA_PIN_SOC_GPIO63_PR2, +}; + +static const unsigned int soc_gpio64_pr3_pins[] = { + TEGRA_PIN_SOC_GPIO64_PR3, +}; + +static const unsigned int soc_gpio65_pr4_pins[] = { + TEGRA_PIN_SOC_GPIO65_PR4, +}; + +static const unsigned int soc_gpio66_pr5_pins[] = { + TEGRA_PIN_SOC_GPIO66_PR5, +}; + +static const unsigned int soc_gpio67_pr6_pins[] = { + TEGRA_PIN_SOC_GPIO67_PR6, +}; + +static const unsigned int soc_gpio68_pr7_pins[] = { + TEGRA_PIN_SOC_GPIO68_PR7, +}; + +static const unsigned int gen4_i2c_scl_ps0_pins[] = { + TEGRA_PIN_GEN4_I2C_SCL_PS0, +}; + +static const unsigned int gen4_i2c_sda_ps1_pins[] = { + TEGRA_PIN_GEN4_I2C_SDA_PS1, +}; + +static const unsigned int soc_gpio75_ps2_pins[] = { + TEGRA_PIN_SOC_GPIO75_PS2, +}; + +static const unsigned int gen7_i2c_scl_ps3_pins[] = { + TEGRA_PIN_GEN7_I2C_SCL_PS3, +}; + +static const unsigned int gen7_i2c_sda_ps4_pins[] = { + TEGRA_PIN_GEN7_I2C_SDA_PS4, +}; + +static const unsigned int soc_gpio78_ps5_pins[] = { + TEGRA_PIN_SOC_GPIO78_PS5, +}; + +static const unsigned int gen9_i2c_scl_ps6_pins[] = { + TEGRA_PIN_GEN9_I2C_SCL_PS6, +}; + +static const unsigned int gen9_i2c_sda_ps7_pins[] = { + TEGRA_PIN_GEN9_I2C_SDA_PS7, +}; + +static const unsigned int soc_gpio81_pt0_pins[] = { + TEGRA_PIN_SOC_GPIO81_PT0, +}; + +static const unsigned int soc_gpio36_pt1_pins[] = { + TEGRA_PIN_SOC_GPIO36_PT1, +}; + +static const unsigned int soc_gpio53_pt2_pins[] = { + TEGRA_PIN_SOC_GPIO53_PT2, +}; + +static const unsigned int soc_gpio38_pt3_pins[] = { + TEGRA_PIN_SOC_GPIO38_PT3, +}; + +static const unsigned int soc_gpio40_pt4_pins[] = { + TEGRA_PIN_SOC_GPIO40_PT4, +}; + +static const unsigned int soc_gpio34_pt5_pins[] = { + TEGRA_PIN_SOC_GPIO34_PT5, +}; + +static const unsigned int usb_vbus_en0_pt6_pins[] = { + TEGRA_PIN_USB_VBUS_EN0_PT6, +}; + +static const unsigned int usb_vbus_en1_pt7_pins[] = { + TEGRA_PIN_USB_VBUS_EN1_PT7, +}; + +static const unsigned int sdmmc1_clk_pu0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PU0, +}; + +static const unsigned int sdmmc1_cmd_pu1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PU1, +}; + +static const unsigned int sdmmc1_dat0_pu2_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PU2, +}; + +static const unsigned int sdmmc1_dat1_pu3_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PU3, +}; + +static const unsigned int sdmmc1_dat2_pu4_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PU4, +}; + +static const unsigned int sdmmc1_dat3_pu5_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PU5, +}; + +static const unsigned int ufs0_ref_clk_pv0_pins[] = { + TEGRA_PIN_UFS0_REF_CLK_PV0, +}; + +static const unsigned int ufs0_rst_n_pv1_pins[] = { + TEGRA_PIN_UFS0_RST_N_PV1, +}; + +static const unsigned int pex_l0_clkreq_n_pw0_pins[] = { + TEGRA_PIN_PEX_L0_CLKREQ_N_PW0, +}; + +static const unsigned int pex_l0_rst_n_pw1_pins[] = { + TEGRA_PIN_PEX_L0_RST_N_PW1, +}; + +static const unsigned int pex_l1_clkreq_n_pw2_pins[] = { + TEGRA_PIN_PEX_L1_CLKREQ_N_PW2, +}; + +static const unsigned int pex_l1_rst_n_pw3_pins[] = { + TEGRA_PIN_PEX_L1_RST_N_PW3, +}; + +static const unsigned int pex_l2_clkreq_n_pw4_pins[] = { + TEGRA_PIN_PEX_L2_CLKREQ_N_PW4, +}; + +static const unsigned int pex_l2_rst_n_pw5_pins[] = { + TEGRA_PIN_PEX_L2_RST_N_PW5, +}; + +static const unsigned int pex_l3_clkreq_n_pw6_pins[] = { + TEGRA_PIN_PEX_L3_CLKREQ_N_PW6, +}; + +static const unsigned int pex_l3_rst_n_pw7_pins[] = { + TEGRA_PIN_PEX_L3_RST_N_PW7, +}; + +static const unsigned int pex_wake_n_px0_pins[] = { + TEGRA_PIN_PEX_WAKE_N_PX0, +}; + +static const unsigned int dp_aux_ch0_hpd_px1_pins[] = { + TEGRA_PIN_DP_AUX_CH0_HPD_PX1, +}; + +static const unsigned int bootv_ctl_n_paa0_pins[] = { + TEGRA_PIN_BOOTV_CTL_N_PAA0, +}; + +static const unsigned int soc_gpio00_paa1_pins[] = { + TEGRA_PIN_SOC_GPIO00_PAA1, +}; + +static const unsigned int vcomp_alert_paa2_pins[] = { + TEGRA_PIN_VCOMP_ALERT_PAA2, +}; + +static const unsigned int pwm1_paa3_pins[] = { + TEGRA_PIN_PWM1_PAA3, +}; + +static const unsigned int batt_oc_paa4_pins[] = { + TEGRA_PIN_BATT_OC_PAA4, +}; + +static const unsigned int soc_gpio04_paa5_pins[] = { + TEGRA_PIN_SOC_GPIO04_PAA5, +}; + +static const unsigned int soc_gpio25_paa6_pins[] = { + TEGRA_PIN_SOC_GPIO25_PAA6, +}; + +static const unsigned int soc_gpio26_paa7_pins[] = { + TEGRA_PIN_SOC_GPIO26_PAA7, +}; + +static const unsigned int hdmi_cec_pbb0_pins[] = { + TEGRA_PIN_HDMI_CEC_PBB0, +}; + +static const unsigned int spi2_sck_pcc0_pins[] = { + TEGRA_PIN_SPI2_SCK_PCC0, +}; + +static const unsigned int spi2_miso_pcc1_pins[] = { + TEGRA_PIN_SPI2_MISO_PCC1, +}; + +static const unsigned int spi2_mosi_pcc2_pins[] = { + TEGRA_PIN_SPI2_MOSI_PCC2, +}; + +static const unsigned int spi2_cs0_pcc3_pins[] = { + TEGRA_PIN_SPI2_CS0_PCC3, +}; + +static const unsigned int spi2_cs1_pcc4_pins[] = { + TEGRA_PIN_SPI2_CS1_PCC4, +}; + +static const unsigned int uart3_tx_pcc5_pins[] = { + TEGRA_PIN_UART3_TX_PCC5, +}; + +static const unsigned int uart3_rx_pcc6_pins[] = { + TEGRA_PIN_UART3_RX_PCC6, +}; + +static const unsigned int gen2_i2c_scl_pcc7_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PCC7, +}; + +static const unsigned int gen2_i2c_sda_pdd0_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PDD0, +}; + +static const unsigned int gen8_i2c_scl_pdd1_pins[] = { + TEGRA_PIN_GEN8_I2C_SCL_PDD1, +}; + +static const unsigned int gen8_i2c_sda_pdd2_pins[] = { + TEGRA_PIN_GEN8_I2C_SDA_PDD2, +}; + +static const unsigned int touch_clk_pdd3_pins[] = { + TEGRA_PIN_TOUCH_CLK_PDD3, +}; + +static const unsigned int dmic1_clk_pdd4_pins[] = { + TEGRA_PIN_DMIC1_CLK_PDD4, +}; + +static const unsigned int dmic1_dat_pdd5_pins[] = { + TEGRA_PIN_DMIC1_DAT_PDD5, +}; + +static const unsigned int soc_gpio19_pdd6_pins[] = { + TEGRA_PIN_SOC_GPIO19_PDD6, +}; + +static const unsigned int pwm2_pdd7_pins[] = { + TEGRA_PIN_PWM2_PDD7, +}; + +static const unsigned int pwm3_pee0_pins[] = { + TEGRA_PIN_PWM3_PEE0, +}; + +static const unsigned int pwm7_pee1_pins[] = { + TEGRA_PIN_PWM7_PEE1, +}; + +static const unsigned int soc_gpio49_pee2_pins[] = { + TEGRA_PIN_SOC_GPIO49_PEE2, +}; + +static const unsigned int soc_gpio82_pee3_pins[] = { + TEGRA_PIN_SOC_GPIO82_PEE3, +}; + +static const unsigned int soc_gpio50_pee4_pins[] = { + TEGRA_PIN_SOC_GPIO50_PEE4, +}; + +static const unsigned int soc_gpio83_pee5_pins[] = { + TEGRA_PIN_SOC_GPIO83_PEE5, +}; + +static const unsigned int soc_gpio69_pff0_pins[] = { + TEGRA_PIN_SOC_GPIO69_PFF0, +}; + +static const unsigned int soc_gpio70_pff1_pins[] = { + TEGRA_PIN_SOC_GPIO70_PFF1, +}; + +static const unsigned int soc_gpio71_pff2_pins[] = { + TEGRA_PIN_SOC_GPIO71_PFF2, +}; + +static const unsigned int soc_gpio72_pff3_pins[] = { + TEGRA_PIN_SOC_GPIO72_PFF3, +}; + +static const unsigned int soc_gpio73_pff4_pins[] = { + TEGRA_PIN_SOC_GPIO73_PFF4, +}; + +static const unsigned int soc_gpio74_pff5_pins[] = { + TEGRA_PIN_SOC_GPIO74_PFF5, +}; + +static const unsigned int soc_gpio80_pff6_pins[] = { + TEGRA_PIN_SOC_GPIO80_PFF6, +}; + +static const unsigned int soc_gpio76_pff7_pins[] = { + TEGRA_PIN_SOC_GPIO76_PFF7, +}; + +static const unsigned int soc_gpio77_pgg0_pins[] = { + TEGRA_PIN_SOC_GPIO77_PGG0, +}; + +static const unsigned int soc_gpio84_pgg1_pins[] = { + TEGRA_PIN_SOC_GPIO84_PGG1, +}; + +static const unsigned int uart2_tx_pgg2_pins[] = { + TEGRA_PIN_UART2_TX_PGG2, +}; + +static const unsigned int uart2_rx_pgg3_pins[] = { + TEGRA_PIN_UART2_RX_PGG3, +}; + +static const unsigned int uart2_rts_pgg4_pins[] = { + TEGRA_PIN_UART2_RTS_PGG4, +}; + +static const unsigned int uart2_cts_pgg5_pins[] = { + TEGRA_PIN_UART2_CTS_PGG5, +}; + +static const unsigned int soc_gpio85_pgg6_pins[] = { + TEGRA_PIN_SOC_GPIO85_PGG6, +}; + +static const unsigned int uart5_tx_pgg7_pins[] = { + TEGRA_PIN_UART5_TX_PGG7, +}; + +static const unsigned int uart5_rx_phh0_pins[] = { + TEGRA_PIN_UART5_RX_PHH0, +}; + +static const unsigned int uart5_rts_phh1_pins[] = { + TEGRA_PIN_UART5_RTS_PHH1, +}; + +static const unsigned int uart5_cts_phh2_pins[] = { + TEGRA_PIN_UART5_CTS_PHH2, +}; + +static const unsigned int soc_gpio86_phh3_pins[] = { + TEGRA_PIN_SOC_GPIO86_PHH3, +}; + +static const unsigned int sdmmc1_comp_pins[] = { + TEGRA_PIN_SDMMC1_COMP, +}; + +/* Define unique ID for each function */ +enum tegra_mux_dt { + TEGRA_MUX_DCA_VSYNC, + TEGRA_MUX_DCA_HSYNC, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_RSVD0, + TEGRA_MUX_I2C7_CLK, + TEGRA_MUX_I2C7_DAT, + TEGRA_MUX_I2C4_DAT, + TEGRA_MUX_I2C4_CLK, + TEGRA_MUX_I2C9_DAT, + TEGRA_MUX_I2C9_CLK, + TEGRA_MUX_USB_VBUS_EN0, + TEGRA_MUX_USB_VBUS_EN1, + TEGRA_MUX_SPI3_DIN, + TEGRA_MUX_SPI1_CS0, + TEGRA_MUX_SPI3_CS0, + TEGRA_MUX_SPI1_DIN, + TEGRA_MUX_SPI3_CS1, + TEGRA_MUX_SPI1_SCK, + TEGRA_MUX_SPI3_SCK, + TEGRA_MUX_SPI1_CS1, + TEGRA_MUX_SPI1_DOUT, + TEGRA_MUX_SPI3_DOUT, + TEGRA_MUX_GP_PWM5, + TEGRA_MUX_GP_PWM6, + TEGRA_MUX_EXTPERIPH2_CLK, + TEGRA_MUX_EXTPERIPH1_CLK, + TEGRA_MUX_I2C3_DAT, + TEGRA_MUX_I2C3_CLK, + TEGRA_MUX_EXTPERIPH4_CLK, + TEGRA_MUX_EXTPERIPH3_CLK, + TEGRA_MUX_DMIC2_DAT, + TEGRA_MUX_DMIC2_CLK, + TEGRA_MUX_UARTA_CTS, + TEGRA_MUX_UARTA_RTS, + TEGRA_MUX_UARTA_RXD, + TEGRA_MUX_UARTA_TXD, + TEGRA_MUX_I2C5_CLK, + TEGRA_MUX_I2C5_DAT, + TEGRA_MUX_UARTD_CTS, + TEGRA_MUX_UARTD_RTS, + TEGRA_MUX_UARTD_RXD, + TEGRA_MUX_UARTD_TXD, + TEGRA_MUX_I2C1_CLK, + TEGRA_MUX_I2C1_DAT, + TEGRA_MUX_SDMMC1_CD, + TEGRA_MUX_I2S2_SCLK, + TEGRA_MUX_I2S2_SDATA_OUT, + TEGRA_MUX_I2S2_SDATA_IN, + TEGRA_MUX_I2S2_LRCK, + TEGRA_MUX_I2S4_SCLK, + TEGRA_MUX_I2S4_SDATA_OUT, + TEGRA_MUX_I2S4_SDATA_IN, + TEGRA_MUX_I2S4_LRCK, + TEGRA_MUX_I2S1_SCLK, + TEGRA_MUX_I2S1_SDATA_OUT, + TEGRA_MUX_I2S1_SDATA_IN, + TEGRA_MUX_I2S1_LRCK, + TEGRA_MUX_AUD_MCLK, + TEGRA_MUX_I2S3_LRCK, + TEGRA_MUX_I2S3_SCLK, + TEGRA_MUX_I2S3_SDATA_IN, + TEGRA_MUX_I2S3_SDATA_OUT, + TEGRA_MUX_PE2_CLKREQ_L, + TEGRA_MUX_PE1_CLKREQ_L, + TEGRA_MUX_PE1_RST_L, + TEGRA_MUX_PE0_CLKREQ_L, + TEGRA_MUX_PE0_RST_L, + TEGRA_MUX_PE2_RST_L, + TEGRA_MUX_PE3_CLKREQ_L, + TEGRA_MUX_PE3_RST_L, + TEGRA_MUX_DP_AUX_CH0_HPD, + TEGRA_MUX_QSPI0_IO0, + TEGRA_MUX_QSPI0_IO1, + TEGRA_MUX_QSPI0_SCK, + TEGRA_MUX_QSPI0_CS_N, + TEGRA_MUX_UARTG_CTS, + TEGRA_MUX_UARTG_RTS, + TEGRA_MUX_UARTG_TXD, + TEGRA_MUX_UARTG_RXD, + TEGRA_MUX_SDMMC1_CLK, + TEGRA_MUX_SDMMC1_CMD, + TEGRA_MUX_SDMMC1_COMP, + TEGRA_MUX_SDMMC1_DAT3, + TEGRA_MUX_SDMMC1_DAT2, + TEGRA_MUX_SDMMC1_DAT1, + TEGRA_MUX_SDMMC1_DAT0, + TEGRA_MUX_UFS0, + TEGRA_MUX_SOC_THERM_OC1, + TEGRA_MUX_HDMI_CEC, + TEGRA_MUX_GP_PWM4, + TEGRA_MUX_UARTC_RXD, + TEGRA_MUX_UARTC_TXD, + TEGRA_MUX_I2C8_DAT, + TEGRA_MUX_I2C8_CLK, + TEGRA_MUX_SPI2_DOUT, + TEGRA_MUX_I2C2_CLK, + TEGRA_MUX_SPI2_CS0, + TEGRA_MUX_I2C2_DAT, + TEGRA_MUX_SPI2_SCK, + TEGRA_MUX_SPI2_DIN, + TEGRA_MUX_PPC_MODE_1, + TEGRA_MUX_PPC_READY, + TEGRA_MUX_PPC_MODE_2, + TEGRA_MUX_PPC_CC, + TEGRA_MUX_PPC_MODE_0, + TEGRA_MUX_PPC_INT_N, + TEGRA_MUX_UARTE_TXD, + TEGRA_MUX_UARTE_RXD, + TEGRA_MUX_UARTB_TXD, + TEGRA_MUX_UARTB_RXD, + TEGRA_MUX_UARTB_CTS, + TEGRA_MUX_UARTB_RTS, + TEGRA_MUX_UARTE_CTS, + TEGRA_MUX_UARTE_RTS, + TEGRA_MUX_GP_PWM7, + TEGRA_MUX_GP_PWM2, + TEGRA_MUX_GP_PWM3, + TEGRA_MUX_GP_PWM1, + TEGRA_MUX_SPI2_CS1, + TEGRA_MUX_DMIC1_CLK, + TEGRA_MUX_DMIC1_DAT, + TEGRA_MUX_RSVD1, + TEGRA_MUX_DCB_HSYNC, + TEGRA_MUX_DCB_VSYNC, + TEGRA_MUX_SOC_THERM_OC4, + TEGRA_MUX_GP_PWM8, + TEGRA_MUX_NV_THERM_FAN_TACH0, + TEGRA_MUX_WDT_RESET_OUTA, + TEGRA_MUX_CCLA_LA_TRIGGER_MUX, + TEGRA_MUX_DSPK1_DAT, + TEGRA_MUX_DSPK1_CLK, + TEGRA_MUX_NV_THERM_FAN_TACH1, + TEGRA_MUX_DSPK0_DAT, + TEGRA_MUX_DSPK0_CLK, + TEGRA_MUX_I2S5_SCLK, + TEGRA_MUX_I2S6_LRCK, + TEGRA_MUX_I2S6_SDATA_IN, + TEGRA_MUX_I2S6_SCLK, + TEGRA_MUX_I2S6_SDATA_OUT, + TEGRA_MUX_I2S5_LRCK, + TEGRA_MUX_I2S5_SDATA_OUT, + TEGRA_MUX_I2S5_SDATA_IN, + TEGRA_MUX_SDMMC1_PE3_RST_L, + TEGRA_MUX_SDMMC1_PE3_CLKREQ_L, + TEGRA_MUX_TOUCH_CLK, + TEGRA_MUX_PPC_I2C_DAT, + TEGRA_MUX_WDT_RESET_OUTB, + TEGRA_MUX_SPI5_CS1, + TEGRA_MUX_PPC_RST_N, + TEGRA_MUX_PPC_I2C_CLK, + TEGRA_MUX_SPI4_CS1, + TEGRA_MUX_SOC_THERM_OC3, + TEGRA_MUX_SPI5_SCK, + TEGRA_MUX_SPI5_MISO, + TEGRA_MUX_SPI4_SCK, + TEGRA_MUX_SPI4_MISO, + TEGRA_MUX_SPI4_CS0, + TEGRA_MUX_SPI4_MOSI, + TEGRA_MUX_SPI5_CS0, + TEGRA_MUX_SPI5_MOSI, + TEGRA_MUX_LED_BLINK, + TEGRA_MUX_RSVD2, + TEGRA_MUX_DMIC3_CLK, + TEGRA_MUX_DMIC3_DAT, + TEGRA_MUX_DMIC4_CLK, + TEGRA_MUX_DMIC4_DAT, + TEGRA_MUX_TSC_EDGE_OUT0, + TEGRA_MUX_TSC_EDGE_OUT3, + TEGRA_MUX_TSC_EDGE_OUT1, + TEGRA_MUX_TSC_EDGE_OUT2, + TEGRA_MUX_DMIC5_CLK, + TEGRA_MUX_DMIC5_DAT, + TEGRA_MUX_RSVD3, + TEGRA_MUX_SDMMC1_WP, + TEGRA_MUX_TSC_EDGE_OUT0A, + TEGRA_MUX_TSC_EDGE_OUT0D, + TEGRA_MUX_TSC_EDGE_OUT0B, + TEGRA_MUX_TSC_EDGE_OUT0C, + TEGRA_MUX_SOC_THERM_OC2, +}; + +/* Make list of each function name */ +#define TEGRA_PIN_FUNCTION(lid) #lid + +static const char * const tegra238_functions[] = { + TEGRA_PIN_FUNCTION(dca_vsync), + TEGRA_PIN_FUNCTION(dca_hsync), + TEGRA_PIN_FUNCTION(displaya), + TEGRA_PIN_FUNCTION(rsvd0), + TEGRA_PIN_FUNCTION(i2c7_clk), + TEGRA_PIN_FUNCTION(i2c7_dat), + TEGRA_PIN_FUNCTION(i2c4_dat), + TEGRA_PIN_FUNCTION(i2c4_clk), + TEGRA_PIN_FUNCTION(i2c9_dat), + TEGRA_PIN_FUNCTION(i2c9_clk), + TEGRA_PIN_FUNCTION(usb_vbus_en0), + TEGRA_PIN_FUNCTION(usb_vbus_en1), + TEGRA_PIN_FUNCTION(spi3_din), + TEGRA_PIN_FUNCTION(spi1_cs0), + TEGRA_PIN_FUNCTION(spi3_cs0), + TEGRA_PIN_FUNCTION(spi1_din), + TEGRA_PIN_FUNCTION(spi3_cs1), + TEGRA_PIN_FUNCTION(spi1_sck), + TEGRA_PIN_FUNCTION(spi3_sck), + TEGRA_PIN_FUNCTION(spi1_cs1), + TEGRA_PIN_FUNCTION(spi1_dout), + TEGRA_PIN_FUNCTION(spi3_dout), + TEGRA_PIN_FUNCTION(gp_pwm5), + TEGRA_PIN_FUNCTION(gp_pwm6), + TEGRA_PIN_FUNCTION(extperiph2_clk), + TEGRA_PIN_FUNCTION(extperiph1_clk), + TEGRA_PIN_FUNCTION(i2c3_dat), + TEGRA_PIN_FUNCTION(i2c3_clk), + TEGRA_PIN_FUNCTION(extperiph4_clk), + TEGRA_PIN_FUNCTION(extperiph3_clk), + TEGRA_PIN_FUNCTION(dmic2_dat), + TEGRA_PIN_FUNCTION(dmic2_clk), + TEGRA_PIN_FUNCTION(uarta_cts), + TEGRA_PIN_FUNCTION(uarta_rts), + TEGRA_PIN_FUNCTION(uarta_rxd), + TEGRA_PIN_FUNCTION(uarta_txd), + TEGRA_PIN_FUNCTION(i2c5_clk), + TEGRA_PIN_FUNCTION(i2c5_dat), + TEGRA_PIN_FUNCTION(uartd_cts), + TEGRA_PIN_FUNCTION(uartd_rts), + TEGRA_PIN_FUNCTION(uartd_rxd), + TEGRA_PIN_FUNCTION(uartd_txd), + TEGRA_PIN_FUNCTION(i2c1_clk), + TEGRA_PIN_FUNCTION(i2c1_dat), + TEGRA_PIN_FUNCTION(sdmmc1_cd), + TEGRA_PIN_FUNCTION(i2s2_sclk), + TEGRA_PIN_FUNCTION(i2s2_sdata_out), + TEGRA_PIN_FUNCTION(i2s2_sdata_in), + TEGRA_PIN_FUNCTION(i2s2_lrck), + TEGRA_PIN_FUNCTION(i2s4_sclk), + TEGRA_PIN_FUNCTION(i2s4_sdata_out), + TEGRA_PIN_FUNCTION(i2s4_sdata_in), + TEGRA_PIN_FUNCTION(i2s4_lrck), + TEGRA_PIN_FUNCTION(i2s1_sclk), + TEGRA_PIN_FUNCTION(i2s1_sdata_out), + TEGRA_PIN_FUNCTION(i2s1_sdata_in), + TEGRA_PIN_FUNCTION(i2s1_lrck), + TEGRA_PIN_FUNCTION(aud_mclk), + TEGRA_PIN_FUNCTION(i2s3_lrck), + TEGRA_PIN_FUNCTION(i2s3_sclk), + TEGRA_PIN_FUNCTION(i2s3_sdata_in), + TEGRA_PIN_FUNCTION(i2s3_sdata_out), + TEGRA_PIN_FUNCTION(pe2_clkreq_l), + TEGRA_PIN_FUNCTION(pe1_clkreq_l), + TEGRA_PIN_FUNCTION(pe1_rst_l), + TEGRA_PIN_FUNCTION(pe0_clkreq_l), + TEGRA_PIN_FUNCTION(pe0_rst_l), + TEGRA_PIN_FUNCTION(pe2_rst_l), + TEGRA_PIN_FUNCTION(pe3_clkreq_l), + TEGRA_PIN_FUNCTION(pe3_rst_l), + TEGRA_PIN_FUNCTION(dp_aux_ch0_hpd), + TEGRA_PIN_FUNCTION(qspi0_io0), + TEGRA_PIN_FUNCTION(qspi0_io1), + TEGRA_PIN_FUNCTION(qspi0_sck), + TEGRA_PIN_FUNCTION(qspi0_cs_n), + TEGRA_PIN_FUNCTION(uartg_cts), + TEGRA_PIN_FUNCTION(uartg_rts), + TEGRA_PIN_FUNCTION(uartg_txd), + TEGRA_PIN_FUNCTION(uartg_rxd), + TEGRA_PIN_FUNCTION(sdmmc1_clk), + TEGRA_PIN_FUNCTION(sdmmc1_cmd), + TEGRA_PIN_FUNCTION(sdmmc1_comp), + TEGRA_PIN_FUNCTION(sdmmc1_dat3), + TEGRA_PIN_FUNCTION(sdmmc1_dat2), + TEGRA_PIN_FUNCTION(sdmmc1_dat1), + TEGRA_PIN_FUNCTION(sdmmc1_dat0), + TEGRA_PIN_FUNCTION(ufs0), + TEGRA_PIN_FUNCTION(soc_therm_oc1), + TEGRA_PIN_FUNCTION(hdmi_cec), + TEGRA_PIN_FUNCTION(gp_pwm4), + TEGRA_PIN_FUNCTION(uartc_rxd), + TEGRA_PIN_FUNCTION(uartc_txd), + TEGRA_PIN_FUNCTION(i2c8_dat), + TEGRA_PIN_FUNCTION(i2c8_clk), + TEGRA_PIN_FUNCTION(spi2_dout), + TEGRA_PIN_FUNCTION(i2c2_clk), + TEGRA_PIN_FUNCTION(spi2_cs0), + TEGRA_PIN_FUNCTION(i2c2_dat), + TEGRA_PIN_FUNCTION(spi2_sck), + TEGRA_PIN_FUNCTION(spi2_din), + TEGRA_PIN_FUNCTION(ppc_mode_1), + TEGRA_PIN_FUNCTION(ppc_ready), + TEGRA_PIN_FUNCTION(ppc_mode_2), + TEGRA_PIN_FUNCTION(ppc_cc), + TEGRA_PIN_FUNCTION(ppc_mode_0), + TEGRA_PIN_FUNCTION(ppc_int_n), + TEGRA_PIN_FUNCTION(uarte_txd), + TEGRA_PIN_FUNCTION(uarte_rxd), + TEGRA_PIN_FUNCTION(uartb_txd), + TEGRA_PIN_FUNCTION(uartb_rxd), + TEGRA_PIN_FUNCTION(uartb_cts), + TEGRA_PIN_FUNCTION(uartb_rts), + TEGRA_PIN_FUNCTION(uarte_cts), + TEGRA_PIN_FUNCTION(uarte_rts), + TEGRA_PIN_FUNCTION(gp_pwm7), + TEGRA_PIN_FUNCTION(gp_pwm2), + TEGRA_PIN_FUNCTION(gp_pwm3), + TEGRA_PIN_FUNCTION(gp_pwm1), + TEGRA_PIN_FUNCTION(spi2_cs1), + TEGRA_PIN_FUNCTION(dmic1_clk), + TEGRA_PIN_FUNCTION(dmic1_dat), + TEGRA_PIN_FUNCTION(rsvd1), + TEGRA_PIN_FUNCTION(dcb_hsync), + TEGRA_PIN_FUNCTION(dcb_vsync), + TEGRA_PIN_FUNCTION(soc_therm_oc4), + TEGRA_PIN_FUNCTION(gp_pwm8), + TEGRA_PIN_FUNCTION(nv_therm_fan_tach0), + TEGRA_PIN_FUNCTION(wdt_reset_outa), + TEGRA_PIN_FUNCTION(ccla_la_trigger_mux), + TEGRA_PIN_FUNCTION(dspk1_dat), + TEGRA_PIN_FUNCTION(dspk1_clk), + TEGRA_PIN_FUNCTION(nv_therm_fan_tach1), + TEGRA_PIN_FUNCTION(dspk0_dat), + TEGRA_PIN_FUNCTION(dspk0_clk), + TEGRA_PIN_FUNCTION(i2s5_sclk), + TEGRA_PIN_FUNCTION(i2s6_lrck), + TEGRA_PIN_FUNCTION(i2s6_sdata_in), + TEGRA_PIN_FUNCTION(i2s6_sclk), + TEGRA_PIN_FUNCTION(i2s6_sdata_out), + TEGRA_PIN_FUNCTION(i2s5_lrck), + TEGRA_PIN_FUNCTION(i2s5_sdata_out), + TEGRA_PIN_FUNCTION(i2s5_sdata_in), + TEGRA_PIN_FUNCTION(sdmmc1_pe3_rst_l), + TEGRA_PIN_FUNCTION(sdmmc1_pe3_clkreq_l), + TEGRA_PIN_FUNCTION(touch_clk), + TEGRA_PIN_FUNCTION(ppc_i2c_dat), + TEGRA_PIN_FUNCTION(wdt_reset_outb), + TEGRA_PIN_FUNCTION(spi5_cs1), + TEGRA_PIN_FUNCTION(ppc_rst_n), + TEGRA_PIN_FUNCTION(ppc_i2c_clk), + TEGRA_PIN_FUNCTION(spi4_cs1), + TEGRA_PIN_FUNCTION(soc_therm_oc3), + TEGRA_PIN_FUNCTION(spi5_sck), + TEGRA_PIN_FUNCTION(spi5_miso), + TEGRA_PIN_FUNCTION(spi4_sck), + TEGRA_PIN_FUNCTION(spi4_miso), + TEGRA_PIN_FUNCTION(spi4_cs0), + TEGRA_PIN_FUNCTION(spi4_mosi), + TEGRA_PIN_FUNCTION(spi5_cs0), + TEGRA_PIN_FUNCTION(spi5_mosi), + TEGRA_PIN_FUNCTION(led_blink), + TEGRA_PIN_FUNCTION(rsvd2), + TEGRA_PIN_FUNCTION(dmic3_clk), + TEGRA_PIN_FUNCTION(dmic3_dat), + TEGRA_PIN_FUNCTION(dmic4_clk), + TEGRA_PIN_FUNCTION(dmic4_dat), + TEGRA_PIN_FUNCTION(tsc_edge_out0), + TEGRA_PIN_FUNCTION(tsc_edge_out3), + TEGRA_PIN_FUNCTION(tsc_edge_out1), + TEGRA_PIN_FUNCTION(tsc_edge_out2), + TEGRA_PIN_FUNCTION(dmic5_clk), + TEGRA_PIN_FUNCTION(dmic5_dat), + TEGRA_PIN_FUNCTION(rsvd3), + TEGRA_PIN_FUNCTION(sdmmc1_wp), + TEGRA_PIN_FUNCTION(tsc_edge_out0a), + TEGRA_PIN_FUNCTION(tsc_edge_out0d), + TEGRA_PIN_FUNCTION(tsc_edge_out0b), + TEGRA_PIN_FUNCTION(tsc_edge_out0c), + TEGRA_PIN_FUNCTION(soc_therm_oc2), +}; + +#define PINGROUP_REG_Y(r) ((r)) +#define PINGROUP_REG_N(r) -1 + +#define DRV_PINGROUP_Y(r) ((r)) + +#define DRV_PINGROUP_ENTRY_N \ + .drv_reg = -1, \ + .drv_bank = -1, \ + .drvdn_bit = -1, \ + .drvup_bit = -1, \ + .slwr_bit = -1, \ + .slwf_bit = -1 + +#define DRV_PINGROUP_ENTRY_Y(r, drvdn_b, drvdn_w, drvup_b, \ + drvup_w, slwr_b, slwr_w, slwf_b, \ + slwf_w, bank) \ + .drv_reg = DRV_PINGROUP_Y(r), \ + .drv_bank = bank, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w + +#define PIN_PINGROUP_ENTRY_N \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_bit = -1, \ + .e_io_hv_bit = -1, \ + .odrain_bit = -1, \ + .lock_bit = -1, \ + .parked_bit = -1, \ + .lpmd_bit = -1, \ + .drvtype_bit = -1, \ + .lpdr_bit = -1, \ + .pbias_buf_bit = -1, \ + .preemp_bit = -1, \ + .rfu_in_bit = -1 + +#define PIN_PINGROUP_ENTRY_Y(r, bank, pupd, e_io_hv, e_lpbk, e_input, \ + e_lpdr, e_pbias_buf, gpio_sfio_sel, \ + schmitt_b) \ + .mux_reg = PINGROUP_REG_Y(r), \ + .lpmd_bit = -1, \ + .lock_bit = -1, \ + .hsm_bit = -1, \ + .mux_bank = bank, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG_##pupd(r), \ + .pupd_bank = bank, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG_Y(r), \ + .tri_bank = bank, \ + .tri_bit = 4, \ + .einput_bit = e_input, \ + .sfsel_bit = gpio_sfio_sel, \ + .schmitt_bit = schmitt_b, \ + .drvtype_bit = 13, \ + .lpdr_bit = e_lpdr, + +#define drive_soc_gpio36_pt1 DRV_PINGROUP_ENTRY_Y(0x10004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio53_pt2 DRV_PINGROUP_ENTRY_Y(0x1000c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio38_pt3 DRV_PINGROUP_ENTRY_Y(0x1001c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio40_pt4 DRV_PINGROUP_ENTRY_Y(0x1002c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio75_ps2 DRV_PINGROUP_ENTRY_Y(0x10034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio81_pt0 DRV_PINGROUP_ENTRY_Y(0x1003c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio78_ps5 DRV_PINGROUP_ENTRY_Y(0x10044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio34_pt5 DRV_PINGROUP_ENTRY_Y(0x1004c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen7_i2c_scl_ps3 DRV_PINGROUP_ENTRY_Y(0x100a4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen7_i2c_sda_ps4 DRV_PINGROUP_ENTRY_Y(0x100ac, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen4_i2c_sda_ps1 DRV_PINGROUP_ENTRY_Y(0x100b4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen4_i2c_scl_ps0 DRV_PINGROUP_ENTRY_Y(0x100bc, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen9_i2c_sda_ps7 DRV_PINGROUP_ENTRY_Y(0x100c4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen9_i2c_scl_ps6 DRV_PINGROUP_ENTRY_Y(0x100cc, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_usb_vbus_en0_pt6 DRV_PINGROUP_ENTRY_Y(0x100d4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_usb_vbus_en1_pt7 DRV_PINGROUP_ENTRY_Y(0x100dc, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio61_pr0 DRV_PINGROUP_ENTRY_Y(0x1f004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio62_pr1 DRV_PINGROUP_ENTRY_Y(0x1f00c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio63_pr2 DRV_PINGROUP_ENTRY_Y(0x1f014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio64_pr3 DRV_PINGROUP_ENTRY_Y(0x1f01c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio65_pr4 DRV_PINGROUP_ENTRY_Y(0x1f024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio66_pr5 DRV_PINGROUP_ENTRY_Y(0x1f02c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio67_pr6 DRV_PINGROUP_ENTRY_Y(0x1f034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio68_pr7 DRV_PINGROUP_ENTRY_Y(0x1f03c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi3_miso_pa4 DRV_PINGROUP_ENTRY_Y(0xd004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi1_cs0_pb3 DRV_PINGROUP_ENTRY_Y(0xd00c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi3_cs0_pa6 DRV_PINGROUP_ENTRY_Y(0xd014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi1_miso_pb1 DRV_PINGROUP_ENTRY_Y(0xd01c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi3_cs1_pa7 DRV_PINGROUP_ENTRY_Y(0xd024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi1_sck_pb0 DRV_PINGROUP_ENTRY_Y(0xd02c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi3_sck_pa3 DRV_PINGROUP_ENTRY_Y(0xd034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi1_cs1_pb4 DRV_PINGROUP_ENTRY_Y(0xd03c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi1_mosi_pb2 DRV_PINGROUP_ENTRY_Y(0xd044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_spi3_mosi_pa5 DRV_PINGROUP_ENTRY_Y(0xd04c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gpu_pwr_req_pa0 DRV_PINGROUP_ENTRY_Y(0xd054, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gp_pwm5_pa1 DRV_PINGROUP_ENTRY_Y(0xd05c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gp_pwm6_pa2 DRV_PINGROUP_ENTRY_Y(0xd064, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_extperiph2_clk_pc3 DRV_PINGROUP_ENTRY_Y(0x4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_extperiph1_clk_pc2 DRV_PINGROUP_ENTRY_Y(0xc, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_cam_i2c_sda_pc5 DRV_PINGROUP_ENTRY_Y(0x14, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_cam_i2c_scl_pc4 DRV_PINGROUP_ENTRY_Y(0x1c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio23_pc6 DRV_PINGROUP_ENTRY_Y(0x24, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio24_pc7 DRV_PINGROUP_ENTRY_Y(0x2c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio27_pd0 DRV_PINGROUP_ENTRY_Y(0x44, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio29_pd2 DRV_PINGROUP_ENTRY_Y(0x54, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio32_pd4 DRV_PINGROUP_ENTRY_Y(0x6c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio33_pd3 DRV_PINGROUP_ENTRY_Y(0x74, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio35_pd5 DRV_PINGROUP_ENTRY_Y(0x7c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio37_pd6 DRV_PINGROUP_ENTRY_Y(0x84, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio56_pd7 DRV_PINGROUP_ENTRY_Y(0x8c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio55_pd1 DRV_PINGROUP_ENTRY_Y(0x94, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart1_cts_pe3 DRV_PINGROUP_ENTRY_Y(0x9c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart1_rts_pe2 DRV_PINGROUP_ENTRY_Y(0xa4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart1_rx_pe1 DRV_PINGROUP_ENTRY_Y(0xac, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart1_tx_pe0 DRV_PINGROUP_ENTRY_Y(0xb4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pwr_i2c_scl_pc0 DRV_PINGROUP_ENTRY_Y(0xbc, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pwr_i2c_sda_pc1 DRV_PINGROUP_ENTRY_Y(0xc4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_cpu_pwr_req_ph4 DRV_PINGROUP_ENTRY_Y(0x4004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart4_cts_pg5 DRV_PINGROUP_ENTRY_Y(0x400c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart4_rts_pg4 DRV_PINGROUP_ENTRY_Y(0x4014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart4_rx_pg3 DRV_PINGROUP_ENTRY_Y(0x401c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart4_tx_pg2 DRV_PINGROUP_ENTRY_Y(0x4024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen1_i2c_scl_ph2 DRV_PINGROUP_ENTRY_Y(0x402c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_gen1_i2c_sda_ph3 DRV_PINGROUP_ENTRY_Y(0x4034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio20_pf6 DRV_PINGROUP_ENTRY_Y(0x403c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio21_pf7 DRV_PINGROUP_ENTRY_Y(0x4044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio22_pg0 DRV_PINGROUP_ENTRY_Y(0x404c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio13_pf0 DRV_PINGROUP_ENTRY_Y(0x4054, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio14_pf1 DRV_PINGROUP_ENTRY_Y(0x405c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio15_pf2 DRV_PINGROUP_ENTRY_Y(0x4064, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio16_pf3 DRV_PINGROUP_ENTRY_Y(0x406c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio17_pf4 DRV_PINGROUP_ENTRY_Y(0x4074, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio18_pf5 DRV_PINGROUP_ENTRY_Y(0x407c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio41_pg6 DRV_PINGROUP_ENTRY_Y(0x408c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio42_pg7 DRV_PINGROUP_ENTRY_Y(0x4094, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio43_ph0 DRV_PINGROUP_ENTRY_Y(0x409c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio44_ph1 DRV_PINGROUP_ENTRY_Y(0x40a4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio06_pg1 DRV_PINGROUP_ENTRY_Y(0x40ac, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio07_ph5 DRV_PINGROUP_ENTRY_Y(0x40b4, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap4_sclk_pp4 DRV_PINGROUP_ENTRY_Y(0x2004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap4_dout_pp5 DRV_PINGROUP_ENTRY_Y(0x200c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap4_din_pp6 DRV_PINGROUP_ENTRY_Y(0x2014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap4_fs_pp7 DRV_PINGROUP_ENTRY_Y(0x201c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio167_pp0 DRV_PINGROUP_ENTRY_Y(0x2044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio168_pp1 DRV_PINGROUP_ENTRY_Y(0x204c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio169_pp2 DRV_PINGROUP_ENTRY_Y(0x2054, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio170_pp3 DRV_PINGROUP_ENTRY_Y(0x205c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio171_pq0 DRV_PINGROUP_ENTRY_Y(0x2064, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio172_pq1 DRV_PINGROUP_ENTRY_Y(0x206c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio173_pq2 DRV_PINGROUP_ENTRY_Y(0x2074, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio45_pk0 DRV_PINGROUP_ENTRY_Y(0x18004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio46_pk1 DRV_PINGROUP_ENTRY_Y(0x1800c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio47_pk2 DRV_PINGROUP_ENTRY_Y(0x18014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio48_pk3 DRV_PINGROUP_ENTRY_Y(0x1801c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio57_pj4 DRV_PINGROUP_ENTRY_Y(0x18024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio58_pj5 DRV_PINGROUP_ENTRY_Y(0x1802c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio59_pj6 DRV_PINGROUP_ENTRY_Y(0x18034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio60_pj7 DRV_PINGROUP_ENTRY_Y(0x1803c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap3_fs_pj3 DRV_PINGROUP_ENTRY_Y(0x18064, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap3_clk_pj0 DRV_PINGROUP_ENTRY_Y(0x1806c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap3_din_pj2 DRV_PINGROUP_ENTRY_Y(0x18074, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dap3_dout_pj1 DRV_PINGROUP_ENTRY_Y(0x1807c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l2_clkreq_n_pw4 DRV_PINGROUP_ENTRY_Y(0x7004, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_wake_n_px0 DRV_PINGROUP_ENTRY_Y(0x700c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l1_clkreq_n_pw2 DRV_PINGROUP_ENTRY_Y(0x7014, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l1_rst_n_pw3 DRV_PINGROUP_ENTRY_Y(0x701c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l0_clkreq_n_pw0 DRV_PINGROUP_ENTRY_Y(0x7024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l0_rst_n_pw1 DRV_PINGROUP_ENTRY_Y(0x702c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l2_rst_n_pw5 DRV_PINGROUP_ENTRY_Y(0x7034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l3_clkreq_n_pw6 DRV_PINGROUP_ENTRY_Y(0x703c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_pex_l3_rst_n_pw7 DRV_PINGROUP_ENTRY_Y(0x7044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_dp_aux_ch0_hpd_px1 DRV_PINGROUP_ENTRY_Y(0x704c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_qspi0_io0_pl1 DRV_PINGROUP_ENTRY_Y(0xb004, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_qspi0_io1_pl2 DRV_PINGROUP_ENTRY_Y(0xb00c, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_qspi0_sck_pl0 DRV_PINGROUP_ENTRY_Y(0xb014, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_qspi0_cs_n_pl3 DRV_PINGROUP_ENTRY_Y(0xb01c, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio156_pm0 DRV_PINGROUP_ENTRY_Y(0xb024, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio155_pl7 DRV_PINGROUP_ENTRY_Y(0xb02c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio160_pm4 DRV_PINGROUP_ENTRY_Y(0xb034, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio154_pl6 DRV_PINGROUP_ENTRY_Y(0xb03c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio152_pl4 DRV_PINGROUP_ENTRY_Y(0xb044, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio153_pl5 DRV_PINGROUP_ENTRY_Y(0xb04c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio161_pm5 DRV_PINGROUP_ENTRY_Y(0xb054, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio162_pm6 DRV_PINGROUP_ENTRY_Y(0xb05c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio159_pm3 DRV_PINGROUP_ENTRY_Y(0xb064, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio157_pm1 DRV_PINGROUP_ENTRY_Y(0xb06c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_soc_gpio158_pm2 DRV_PINGROUP_ENTRY_Y(0xb074, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart7_cts_pn2 DRV_PINGROUP_ENTRY_Y(0xb07c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart7_rts_pn1 DRV_PINGROUP_ENTRY_Y(0xb084, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart7_tx_pm7 DRV_PINGROUP_ENTRY_Y(0xb08c, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_uart7_rx_pn0 DRV_PINGROUP_ENTRY_Y(0xb094, 12, 5, 20, 5, -1, -1, -1, -1, 0) +#define drive_sdmmc1_clk_pu0 DRV_PINGROUP_ENTRY_Y(0x8004, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_cmd_pu1 DRV_PINGROUP_ENTRY_Y(0x800c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat3_pu5 DRV_PINGROUP_ENTRY_Y(0x801c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat2_pu4 DRV_PINGROUP_ENTRY_Y(0x8024, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat1_pu3 DRV_PINGROUP_ENTRY_Y(0x802c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat0_pu2 DRV_PINGROUP_ENTRY_Y(0x8034, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_ufs0_rst_n_pv1 DRV_PINGROUP_ENTRY_Y(0x11004, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_ufs0_ref_clk_pv0 DRV_PINGROUP_ENTRY_Y(0x1100c, 12, 5, 24, 5, -1, -1, -1, -1, 0) +#define drive_batt_oc_paa4 DRV_PINGROUP_ENTRY_Y(0x1024, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_bootv_ctl_n_paa0 DRV_PINGROUP_ENTRY_Y(0x102c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_vcomp_alert_paa2 DRV_PINGROUP_ENTRY_Y(0x105c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_hdmi_cec_pbb0 DRV_PINGROUP_ENTRY_Y(0x1064, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_touch_clk_pdd3 DRV_PINGROUP_ENTRY_Y(0x106c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart3_rx_pcc6 DRV_PINGROUP_ENTRY_Y(0x1074, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart3_tx_pcc5 DRV_PINGROUP_ENTRY_Y(0x107c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_gen8_i2c_sda_pdd2 DRV_PINGROUP_ENTRY_Y(0x1084, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_gen8_i2c_scl_pdd1 DRV_PINGROUP_ENTRY_Y(0x108c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_spi2_mosi_pcc2 DRV_PINGROUP_ENTRY_Y(0x1094, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_gen2_i2c_scl_pcc7 DRV_PINGROUP_ENTRY_Y(0x109c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_spi2_cs0_pcc3 DRV_PINGROUP_ENTRY_Y(0x10a4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_gen2_i2c_sda_pdd0 DRV_PINGROUP_ENTRY_Y(0x10ac, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_spi2_sck_pcc0 DRV_PINGROUP_ENTRY_Y(0x10b4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_spi2_miso_pcc1 DRV_PINGROUP_ENTRY_Y(0x10bc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio49_pee2 DRV_PINGROUP_ENTRY_Y(0x10c4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio50_pee4 DRV_PINGROUP_ENTRY_Y(0x10cc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio82_pee3 DRV_PINGROUP_ENTRY_Y(0x10d4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio71_pff2 DRV_PINGROUP_ENTRY_Y(0x10dc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio76_pff7 DRV_PINGROUP_ENTRY_Y(0x10e4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio74_pff5 DRV_PINGROUP_ENTRY_Y(0x10ec, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio00_paa1 DRV_PINGROUP_ENTRY_Y(0x10f4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio19_pdd6 DRV_PINGROUP_ENTRY_Y(0x10fc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio86_phh3 DRV_PINGROUP_ENTRY_Y(0x1104, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio72_pff3 DRV_PINGROUP_ENTRY_Y(0x110c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio77_pgg0 DRV_PINGROUP_ENTRY_Y(0x1114, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio80_pff6 DRV_PINGROUP_ENTRY_Y(0x111c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio84_pgg1 DRV_PINGROUP_ENTRY_Y(0x1124, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio83_pee5 DRV_PINGROUP_ENTRY_Y(0x112c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio73_pff4 DRV_PINGROUP_ENTRY_Y(0x1134, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio70_pff1 DRV_PINGROUP_ENTRY_Y(0x113c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio04_paa5 DRV_PINGROUP_ENTRY_Y(0x1144, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio85_pgg6 DRV_PINGROUP_ENTRY_Y(0x114c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio69_pff0 DRV_PINGROUP_ENTRY_Y(0x1154, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio25_paa6 DRV_PINGROUP_ENTRY_Y(0x115c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_soc_gpio26_paa7 DRV_PINGROUP_ENTRY_Y(0x1164, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart5_tx_pgg7 DRV_PINGROUP_ENTRY_Y(0x116c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart5_rx_phh0 DRV_PINGROUP_ENTRY_Y(0x1174, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart2_tx_pgg2 DRV_PINGROUP_ENTRY_Y(0x117c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart2_rx_pgg3 DRV_PINGROUP_ENTRY_Y(0x1184, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart2_cts_pgg5 DRV_PINGROUP_ENTRY_Y(0x118c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart2_rts_pgg4 DRV_PINGROUP_ENTRY_Y(0x1194, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart5_cts_phh2 DRV_PINGROUP_ENTRY_Y(0x119c, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_uart5_rts_phh1 DRV_PINGROUP_ENTRY_Y(0x11a4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_pwm7_pee1 DRV_PINGROUP_ENTRY_Y(0x11ac, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_pwm2_pdd7 DRV_PINGROUP_ENTRY_Y(0x11b4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_pwm3_pee0 DRV_PINGROUP_ENTRY_Y(0x11bc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_pwm1_paa3 DRV_PINGROUP_ENTRY_Y(0x11c4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_spi2_cs1_pcc4 DRV_PINGROUP_ENTRY_Y(0x11cc, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_dmic1_clk_pdd4 DRV_PINGROUP_ENTRY_Y(0x11d4, 12, 5, 20, 5, -1, -1, -1, -1, 1) +#define drive_dmic1_dat_pdd5 DRV_PINGROUP_ENTRY_Y(0x11dc, 12, 5, 20, 5, -1, -1, -1, -1, 1) + +#define drive_sdmmc1_comp DRV_PINGROUP_ENTRY_N + +#define PINGROUP(pg_name, f0, f1, f2, f3, r, bank, pupd, e_io_hv, e_lpbk, e_input, e_lpdr, e_pbias_buf, \ + gpio_sfio_sel, schmitt_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + PIN_PINGROUP_ENTRY_Y(r, bank, pupd, e_io_hv, e_lpbk, \ + e_input, e_lpdr, e_pbias_buf, \ + gpio_sfio_sel, schmitt_b) \ + drive_##pg_name, \ + } + +static const struct tegra_pingroup tegra238_groups[] = { + PINGROUP(soc_gpio36_pt1, DCA_VSYNC, RSVD1, RSVD2, RSVD3, 0x10000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio53_pt2, DCA_HSYNC, RSVD1, RSVD2, RSVD3, 0x10008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio38_pt3, DISPLAYA, DCB_HSYNC, RSVD2, RSVD3, 0x10018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio40_pt4, RSVD0, DCB_VSYNC, RSVD2, RSVD3, 0x10028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio75_ps2, RSVD0, RSVD1, RSVD2, RSVD3, 0x10030, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio81_pt0, RSVD0, RSVD1, RSVD2, RSVD3, 0x10038, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio78_ps5, RSVD0, RSVD1, RSVD2, RSVD3, 0x10040, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio34_pt5, RSVD0, RSVD1, RSVD2, RSVD3, 0x10048, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen7_i2c_scl_ps3, I2C7_CLK, RSVD1, RSVD2, RSVD3, 0x100a0, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen7_i2c_sda_ps4, I2C7_DAT, RSVD1, RSVD2, RSVD3, 0x100a8, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen4_i2c_sda_ps1, I2C4_DAT, RSVD1, RSVD2, RSVD3, 0x100b0, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen4_i2c_scl_ps0, I2C4_CLK, RSVD1, RSVD2, RSVD3, 0x100b8, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen9_i2c_sda_ps7, I2C9_DAT, RSVD1, RSVD2, RSVD3, 0x100c0, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen9_i2c_scl_ps6, I2C9_CLK, RSVD1, RSVD2, RSVD3, 0x100c8, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(usb_vbus_en0_pt6, USB_VBUS_EN0, RSVD1, RSVD2, RSVD3, 0x100d0, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(usb_vbus_en1_pt7, USB_VBUS_EN1, RSVD1, RSVD2, RSVD3, 0x100d8, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio61_pr0, RSVD0, SOC_THERM_OC4, RSVD2, RSVD3, 0x1f000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio62_pr1, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio63_pr2, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f010, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio64_pr3, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio65_pr4, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio66_pr5, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio67_pr6, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f030, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio68_pr7, RSVD0, RSVD1, RSVD2, RSVD3, 0x1f038, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi3_miso_pa4, SPI3_DIN, RSVD1, RSVD2, RSVD3, 0xd000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi1_cs0_pb3, SPI1_CS0, RSVD1, RSVD2, RSVD3, 0xd008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi3_cs0_pa6, SPI3_CS0, RSVD1, RSVD2, RSVD3, 0xd010, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi1_miso_pb1, SPI1_DIN, RSVD1, RSVD2, RSVD3, 0xd018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi3_cs1_pa7, SPI3_CS1, RSVD1, RSVD2, RSVD3, 0xd020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi1_sck_pb0, SPI1_SCK, RSVD1, RSVD2, RSVD3, 0xd028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi3_sck_pa3, SPI3_SCK, RSVD1, RSVD2, RSVD3, 0xd030, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi1_cs1_pb4, SPI1_CS1, RSVD1, RSVD2, RSVD3, 0xd038, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi1_mosi_pb2, SPI1_DOUT, RSVD1, RSVD2, RSVD3, 0xd040, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi3_mosi_pa5, SPI3_DOUT, RSVD1, RSVD2, RSVD3, 0xd048, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(gpu_pwr_req_pa0, RSVD0, RSVD1, RSVD2, RSVD3, 0xd050, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(gp_pwm5_pa1, GP_PWM5, RSVD1, RSVD2, RSVD3, 0xd058, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gp_pwm6_pa2, GP_PWM6, RSVD1, RSVD2, RSVD3, 0xd060, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(extperiph2_clk_pc3, EXTPERIPH2_CLK, RSVD1, RSVD2, RSVD3, 0x0000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(extperiph1_clk_pc2, EXTPERIPH1_CLK, RSVD1, RSVD2, RSVD3, 0x0008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(cam_i2c_sda_pc5, I2C3_DAT, RSVD1, RSVD2, RSVD3, 0x0010, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(cam_i2c_scl_pc4, I2C3_CLK, RSVD1, RSVD2, RSVD3, 0x0018, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio23_pc6, RSVD0, RSVD1, RSVD2, RSVD3, 0x0020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio24_pc7, RSVD0, RSVD1, RSVD2, RSVD3, 0x0028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio27_pd0, RSVD0, GP_PWM8, RSVD2, RSVD3, 0x0040, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio29_pd2, RSVD0, NV_THERM_FAN_TACH0, RSVD2, RSVD3, 0x0050, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio32_pd4, EXTPERIPH4_CLK, RSVD1, RSVD2, RSVD3, 0x0068, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio33_pd3, EXTPERIPH3_CLK, RSVD1, RSVD2, RSVD3, 0x0070, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio35_pd5, RSVD0, RSVD1, RSVD2, RSVD3, 0x0078, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio37_pd6, DMIC2_DAT, RSVD1, RSVD2, RSVD3, 0x0080, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio56_pd7, DMIC2_CLK, RSVD1, RSVD2, RSVD3, 0x0088, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio55_pd1, RSVD0, WDT_RESET_OUTA, RSVD2, RSVD3, 0x0090, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart1_cts_pe3, UARTA_CTS, RSVD1, RSVD2, RSVD3, 0x0098, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart1_rts_pe2, UARTA_RTS, RSVD1, RSVD2, RSVD3, 0x00a0, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart1_rx_pe1, UARTA_RXD, RSVD1, RSVD2, RSVD3, 0x00a8, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart1_tx_pe0, UARTA_TXD, RSVD1, RSVD2, RSVD3, 0x00b0, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(pwr_i2c_scl_pc0, I2C5_CLK, RSVD1, RSVD2, RSVD3, 0x00b8, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pwr_i2c_sda_pc1, I2C5_DAT, RSVD1, RSVD2, RSVD3, 0x00c0, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(cpu_pwr_req_ph4, RSVD0, RSVD1, RSVD2, RSVD3, 0x4000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart4_cts_pg5, UARTD_CTS, RSVD1, RSVD2, RSVD3, 0x4008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart4_rts_pg4, UARTD_RTS, RSVD1, RSVD2, RSVD3, 0x4010, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart4_rx_pg3, UARTD_RXD, RSVD1, RSVD2, RSVD3, 0x4018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart4_tx_pg2, UARTD_TXD, RSVD1, RSVD2, RSVD3, 0x4020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(gen1_i2c_scl_ph2, I2C1_CLK, RSVD1, RSVD2, RSVD3, 0x4028, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen1_i2c_sda_ph3, I2C1_DAT, RSVD1, RSVD2, RSVD3, 0x4030, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio20_pf6, SDMMC1_CD, RSVD1, RSVD2, RSVD3, 0x4038, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio21_pf7, RSVD0, RSVD1, RSVD2, RSVD3, 0x4040, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio22_pg0, RSVD0, RSVD1, RSVD2, RSVD3, 0x4048, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio13_pf0, RSVD0, RSVD1, RSVD2, RSVD3, 0x4050, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio14_pf1, RSVD0, RSVD1, RSVD2, RSVD3, 0x4058, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio15_pf2, RSVD0, RSVD1, RSVD2, RSVD3, 0x4060, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio16_pf3, RSVD0, RSVD1, RSVD2, RSVD3, 0x4068, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio17_pf4, RSVD0, CCLA_LA_TRIGGER_MUX, RSVD2, RSVD3, 0x4070, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio18_pf5, RSVD0, RSVD1, RSVD2, RSVD3, 0x4078, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio41_pg6, I2S2_SCLK, RSVD1, RSVD2, RSVD3, 0x4088, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio42_pg7, I2S2_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x4090, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio43_ph0, I2S2_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x4098, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio44_ph1, I2S2_LRCK, RSVD1, RSVD2, RSVD3, 0x40a0, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio06_pg1, RSVD0, RSVD1, RSVD2, RSVD3, 0x40a8, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio07_ph5, RSVD0, RSVD1, RSVD2, RSVD3, 0x40b0, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap4_sclk_pp4, I2S4_SCLK, RSVD1, RSVD2, RSVD3, 0x2000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap4_dout_pp5, I2S4_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x2008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap4_din_pp6, I2S4_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x2010, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap4_fs_pp7, I2S4_LRCK, RSVD1, RSVD2, RSVD3, 0x2018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio167_pp0, RSVD0, RSVD1, RSVD2, RSVD3, 0x2040, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio168_pp1, RSVD0, RSVD1, RSVD2, RSVD3, 0x2048, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio169_pp2, RSVD0, RSVD1, RSVD2, RSVD3, 0x2050, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio170_pp3, RSVD0, RSVD1, RSVD2, RSVD3, 0x2058, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio171_pq0, RSVD0, RSVD1, RSVD2, RSVD3, 0x2060, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio172_pq1, RSVD0, RSVD1, RSVD2, RSVD3, 0x2068, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio173_pq2, RSVD0, RSVD1, RSVD2, RSVD3, 0x2070, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio45_pk0, I2S1_SCLK, DSPK1_DAT, DMIC3_CLK, RSVD3, 0x18000, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio46_pk1, I2S1_SDATA_OUT, DSPK1_CLK, DMIC3_DAT, RSVD3, 0x18008, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio47_pk2, I2S1_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x18010, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio48_pk3, I2S1_LRCK, RSVD1, RSVD2, RSVD3, 0x18018, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio57_pj4, RSVD0, RSVD1, RSVD2, SDMMC1_WP, 0x18020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio58_pj5, RSVD0, RSVD1, RSVD2, RSVD3, 0x18028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio59_pj6, AUD_MCLK, RSVD1, RSVD2, RSVD3, 0x18030, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio60_pj7, RSVD0, NV_THERM_FAN_TACH1, RSVD2, RSVD3, 0x18038, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap3_fs_pj3, I2S3_LRCK, RSVD1, RSVD2, RSVD3, 0x18060, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap3_clk_pj0, I2S3_SCLK, DSPK0_DAT, DMIC4_CLK, RSVD3, 0x18068, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap3_din_pj2, I2S3_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x18070, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dap3_dout_pj1, I2S3_SDATA_OUT, DSPK0_CLK, DMIC4_DAT, RSVD3, 0x18078, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l2_clkreq_n_pw4, PE2_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x7000, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_wake_n_px0, RSVD0, RSVD1, RSVD2, RSVD3, 0x7008, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l1_clkreq_n_pw2, PE1_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x7010, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l1_rst_n_pw3, PE1_RST_L, RSVD1, RSVD2, RSVD3, 0x7018, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l0_clkreq_n_pw0, PE0_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x7020, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l0_rst_n_pw1, PE0_RST_L, RSVD1, RSVD2, RSVD3, 0x7028, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l2_rst_n_pw5, PE2_RST_L, RSVD1, RSVD2, RSVD3, 0x7030, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l3_clkreq_n_pw6, PE3_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x7038, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pex_l3_rst_n_pw7, PE3_RST_L, RSVD1, RSVD2, RSVD3, 0x7040, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(dp_aux_ch0_hpd_px1, DP_AUX_CH0_HPD, RSVD1, RSVD2, RSVD3, 0x7048, 0, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(qspi0_io0_pl1, QSPI0_IO0, RSVD1, RSVD2, RSVD3, 0xb000, 0, Y, -1, 5, 6, -1, -1, 10, 12), + PINGROUP(qspi0_io1_pl2, QSPI0_IO1, RSVD1, RSVD2, RSVD3, 0xb008, 0, Y, -1, 5, 6, -1, -1, 10, 12), + PINGROUP(qspi0_sck_pl0, QSPI0_SCK, RSVD1, RSVD2, RSVD3, 0xb010, 0, Y, -1, 5, 6, -1, -1, 10, 12), + PINGROUP(qspi0_cs_n_pl3, QSPI0_CS_N, RSVD1, RSVD2, RSVD3, 0xb018, 0, Y, -1, 5, 6, -1, -1, 10, 12), + PINGROUP(soc_gpio156_pm0, RSVD0, I2S5_SCLK, RSVD2, RSVD3, 0xb020, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio155_pl7, RSVD0, I2S6_LRCK, RSVD2, RSVD3, 0xb028, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio160_pm4, RSVD0, RSVD1, RSVD2, RSVD3, 0xb030, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio154_pl6, RSVD0, I2S6_SDATA_IN, RSVD2, RSVD3, 0xb038, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio152_pl4, RSVD0, I2S6_SCLK, RSVD2, RSVD3, 0xb040, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio153_pl5, RSVD0, I2S6_SDATA_OUT, RSVD2, RSVD3, 0xb048, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio161_pm5, RSVD0, RSVD1, RSVD2, RSVD3, 0xb050, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio162_pm6, RSVD0, RSVD1, RSVD2, RSVD3, 0xb058, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio159_pm3, RSVD0, I2S5_LRCK, RSVD2, RSVD3, 0xb060, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio157_pm1, RSVD0, I2S5_SDATA_OUT, RSVD2, RSVD3, 0xb068, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio158_pm2, RSVD0, I2S5_SDATA_IN, RSVD2, RSVD3, 0xb070, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart7_cts_pn2, UARTG_CTS, RSVD1, RSVD2, RSVD3, 0xb078, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart7_rts_pn1, UARTG_RTS, RSVD1, RSVD2, RSVD3, 0xb080, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart7_tx_pm7, UARTG_TXD, RSVD1, RSVD2, RSVD3, 0xb088, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart7_rx_pn0, UARTG_RXD, RSVD1, RSVD2, RSVD3, 0xb090, 0, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(sdmmc1_clk_pu0, SDMMC1_CLK, RSVD1, RSVD2, RSVD3, 0x8000, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(sdmmc1_cmd_pu1, SDMMC1_CMD, RSVD1, RSVD2, RSVD3, 0x8008, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(sdmmc1_comp, SDMMC1_COMP, RSVD1, RSVD2, RSVD3, 0x8010, 0, N, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat3_pu5, SDMMC1_DAT3, SDMMC1_PE3_RST_L, RSVD2, RSVD3, 0x8018, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(sdmmc1_dat2_pu4, SDMMC1_DAT2, SDMMC1_PE3_CLKREQ_L, RSVD2, RSVD3, 0x8020, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(sdmmc1_dat1_pu3, SDMMC1_DAT1, RSVD1, RSVD2, RSVD3, 0x8028, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(sdmmc1_dat0_pu2, SDMMC1_DAT0, RSVD1, RSVD2, RSVD3, 0x8030, 0, Y, -1, 5, 6, -1, 9, 10, 12), + PINGROUP(ufs0_rst_n_pv1, UFS0, RSVD1, RSVD2, RSVD3, 0x11000, 0, Y, -1, 5, 6, -1, -1, 10, 12), + PINGROUP(ufs0_ref_clk_pv0, UFS0, RSVD1, RSVD2, RSVD3, 0x11008, 0, Y, -1, 5, 6, -1, -1, 10, 12), + +}; + +static const struct tegra_pingroup tegra238_aon_groups[] = { + PINGROUP(bootv_ctl_n_paa0, RSVD0, RSVD1, RSVD2, RSVD3, 0x1028, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio00_paa1, RSVD0, RSVD1, RSVD2, RSVD3, 0x10f0, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(vcomp_alert_paa2, SOC_THERM_OC1, RSVD1, RSVD2, RSVD3, 0x1058, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pwm1_paa3, GP_PWM1, RSVD1, RSVD2, RSVD3, 0x11c0, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(batt_oc_paa4, SOC_THERM_OC2, RSVD1, RSVD2, RSVD3, 0x1020, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio04_paa5, RSVD0, RSVD1, RSVD2, RSVD3, 0x1140, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio25_paa6, RSVD0, RSVD1, RSVD2, RSVD3, 0x1158, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio26_paa7, RSVD0, SOC_THERM_OC3, RSVD2, RSVD3, 0x1160, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(hdmi_cec_pbb0, HDMI_CEC, RSVD1, RSVD2, RSVD3, 0x1060, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(spi2_sck_pcc0, SPI2_SCK, RSVD1, RSVD2, RSVD3, 0x10b0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi2_miso_pcc1, SPI2_DIN, RSVD1, RSVD2, RSVD3, 0x10b8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi2_mosi_pcc2, SPI2_DOUT, RSVD1, RSVD2, RSVD3, 0x1090, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi2_cs0_pcc3, SPI2_CS0, RSVD1, RSVD2, RSVD3, 0x10a0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(spi2_cs1_pcc4, SPI2_CS1, RSVD1, RSVD2, RSVD3, 0x11c8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(uart3_tx_pcc5, UARTC_TXD, RSVD1, RSVD2, RSVD3, 0x1078, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart3_rx_pcc6, UARTC_RXD, RSVD1, RSVD2, RSVD3, 0x1070, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen2_i2c_scl_pcc7, I2C2_CLK, RSVD1, RSVD2, RSVD3, 0x1098, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen2_i2c_sda_pdd0, I2C2_DAT, RSVD1, RSVD2, RSVD3, 0x10a8, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen8_i2c_scl_pdd1, I2C8_CLK, RSVD1, RSVD2, RSVD3, 0x1088, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(gen8_i2c_sda_pdd2, I2C8_DAT, RSVD1, RSVD2, RSVD3, 0x1080, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(touch_clk_pdd3, GP_PWM4, TOUCH_CLK, RSVD2, RSVD3, 0x1068, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(dmic1_clk_pdd4, DMIC1_CLK, RSVD1, DMIC5_CLK, RSVD3, 0x11d0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(dmic1_dat_pdd5, DMIC1_DAT, RSVD1, DMIC5_DAT, RSVD3, 0x11d8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio19_pdd6, RSVD0, WDT_RESET_OUTB, RSVD2, RSVD3, 0x10f8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(pwm2_pdd7, GP_PWM2, LED_BLINK, RSVD2, RSVD3, 0x11b0, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pwm3_pee0, GP_PWM3, RSVD1, RSVD2, RSVD3, 0x11b8, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(pwm7_pee1, GP_PWM7, RSVD1, RSVD2, RSVD3, 0x11a8, 1, Y, 5, 7, 6, 8, -1, 10, 12), +}; + +static const struct tegra_pinctrl_soc_data tegra238_pinctrl_aon = { + .pins = tegra238_aon_pins, + .npins = ARRAY_SIZE(tegra238_aon_pins), + .functions = tegra238_functions, + .nfunctions = ARRAY_SIZE(tegra238_functions), + .groups = tegra238_aon_groups, + .ngroups = ARRAY_SIZE(tegra238_aon_groups), + .hsm_in_mux = false, + .schmitt_in_mux = true, + .drvtype_in_mux = true, + .sfsel_in_mux = true, +}; + +static const struct tegra_pinctrl_soc_data tegra238_pinctrl = { + .pins = tegra238_pins, + .npins = ARRAY_SIZE(tegra238_pins), + .functions = tegra238_functions, + .nfunctions = ARRAY_SIZE(tegra238_functions), + .groups = tegra238_groups, + .ngroups = ARRAY_SIZE(tegra238_groups), + .hsm_in_mux = false, + .schmitt_in_mux = true, + .drvtype_in_mux = true, + .sfsel_in_mux = true, +}; + +static int tegra238_pinctrl_probe(struct platform_device *pdev) +{ + const struct tegra_pinctrl_soc_data *soc = device_get_match_data(&pdev->dev); + + return tegra_pinctrl_probe(pdev, soc); +} + +static const struct of_device_id tegra238_pinctrl_of_match[] = { + { .compatible = "nvidia,tegra238-pinmux", .data = &tegra238_pinctrl }, + { .compatible = "nvidia,tegra238-pinmux-aon", .data = &tegra238_pinctrl_aon }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra238_pinctrl_of_match); + +static struct platform_driver tegra238_pinctrl_driver = { + .driver = { + .name = "tegra238-pinctrl", + .of_match_table = tegra238_pinctrl_of_match, + }, + .probe = tegra238_pinctrl_probe, +}; + +static int __init tegra238_pinctrl_init(void) +{ + return platform_driver_register(&tegra238_pinctrl_driver); +} +module_init(tegra238_pinctrl_init); + +static void __exit tegra238_pinctrl_exit(void) +{ + platform_driver_unregister(&tegra238_pinctrl_driver); +} +module_exit(tegra238_pinctrl_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("NVIDIA Tegra238 pinctrl driver"); From 30a9d5162f25b4a67209677982ee745070f8b6d6 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 27 Apr 2026 13:42:29 +0000 Subject: [PATCH 1815/5214] dt-bindings: pinctrl: Document Tegra264 pin controllers Tegra264 contains three pin controllers. Document their compatible strings and describe the list of pins and functions that they provide. Signed-off-by: Prathamesh Shete Reviewed-by: Krzysztof Kozlowski Reviewed-by: Jon Hunter Signed-off-by: Linus Walleij --- .../pinctrl/nvidia,tegra264-pinmux-aon.yaml | 80 +++++++++ .../nvidia,tegra264-pinmux-common.yaml | 84 +++++++++ .../pinctrl/nvidia,tegra264-pinmux-main.yaml | 167 ++++++++++++++++++ .../pinctrl/nvidia,tegra264-pinmux-uphy.yaml | 78 ++++++++ 4 files changed, 409 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-aon.yaml create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-common.yaml create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-main.yaml create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-uphy.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-aon.yaml new file mode 100644 index 000000000000..682e6510ed45 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-aon.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra264-pinmux-aon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra264 AON Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra264-pinmux-aon + + reg: + maxItems: 1 + +patternProperties: + "^pinmux(-[a-z0-9-]+)?$": + type: object + + # pin groups + additionalProperties: + $ref: nvidia,tegra264-pinmux-common.yaml + + properties: + nvidia,pins: + items: + enum: [ soc_gpio00_paa0, vcomp_alert_paa1, ao_retention_n_paa2, + batt_oc_paa3, bootv_ctl_n_paa4, power_on_paa5, + hdmi_cec_paa6, soc_gpio07_paa7, soc_gpio08_pbb0, + soc_gpio09_pbb1, gen2_i2c_scl_pcc0, gen2_i2c_sda_pcc1, + gen3_i2c_scl_pcc2, gen3_i2c_sda_pcc3, gp_pwm4_pcc4, + uart0_tx_pcc5, uart0_rx_pcc6, spi2_sck_pcc7, + spi2_miso_pdd0, spi2_mosi_pdd1, spi2_cs0_n_pdd2, + soc_gpio21_pdd3, soc_gpio22_pdd4, soc_gpio23_pdd5, + soc_gpio24_pdd6, soc_gpio25_pdd7, soc_gpio26_pee0, + soc_gpio27_pee1, soc_gpio28_pee2, soc_gpio29_pee3, + drive_ao_retention_n_paa2, drive_batt_oc_paa3, + drive_power_on_paa5, drive_vcomp_alert_paa1, + drive_bootv_ctl_n_paa4, drive_soc_gpio00_paa0, + drive_soc_gpio07_paa7, drive_soc_gpio08_pbb0, + drive_soc_gpio09_pbb1, drive_hdmi_cec_paa6, + drive_gen2_i2c_scl_pcc0, drive_gen2_i2c_sda_pcc1, + drive_gen3_i2c_scl_pcc2, drive_gen3_i2c_sda_pcc3, + drive_gp_pwm4_pcc4, drive_uart0_tx_pcc5, + drive_uart0_rx_pcc6, drive_spi2_sck_pcc7, + drive_spi2_miso_pdd0, drive_spi2_mosi_pdd1, + drive_spi2_cs0_n_pdd2, drive_soc_gpio21_pdd3, + drive_soc_gpio22_pdd4, drive_soc_gpio23_pdd5, + drive_soc_gpio24_pdd6, drive_soc_gpio25_pdd7, + drive_soc_gpio26_pee0, drive_soc_gpio27_pee1, + drive_soc_gpio28_pee2, drive_soc_gpio29_pee3 ] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + pinmux@c7a2000 { + compatible = "nvidia,tegra264-pinmux-aon"; + reg = <0xc7a2000 0x2000>; + + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinmux-default { + uart0 { + nvidia,pins = "uart0_tx_pcc5"; + nvidia,function = "uarta_txd"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-common.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-common.yaml new file mode 100644 index 000000000000..d644c496d8a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-common.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra264-pinmux-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra264 Pinmux Common Properties + +maintainers: + - Thierry Reding + - Jon Hunter + +$ref: nvidia,tegra-pinmux-common.yaml + +properties: + nvidia,function: + enum: [ dca_vsync, dca_hsync, rsvd0, dp_aux_ch0_hpd, dp_aux_ch1_hpd, + dp_aux_ch2_hpd, dp_aux_ch3_hpd, gp_pwm2, gp_pwm3, i2c7_clk, + i2c7_dat, i2c9_clk, i2c9_dat, uartk_cts, uartk_rts, uartk_rxd, + uartk_txd, spi3_cs0, spi3_cs3, spi3_din, spi3_dout, spi3_sck, + uartf_cts, uartf_rts, uartf_rxd, uartf_txd, spi1_cs0, spi1_cs1, + spi1_din, spi1_dout, spi1_sck, extperiph2_clk, extperiph1_clk, + i2c12_clk, i2c12_dat, nv_therm_fan_tach0, gp_pwm9, uartj_cts, + uartj_rts, uartj_rxd, uartj_txd, i2c0_clk, i2c0_dat, i2c1_clk, + i2c1_dat, i2s2_lrck, i2s2_sclk, i2s2_sdata_out, i2s2_sdata_in, + gp_pwm10, uarte_cts, uarte_rts, uarte_rxd, uarte_txd, i2c5_dat, + i2c5_clk, i2s6_sdata_in, i2s6_sdata_out, i2s6_lrck, i2s6_sclk, + i2s4_sdata_out, i2s4_sclk, i2s4_sdata_in, i2s4_lrck, spi5_cs0, + spi5_din, spi5_dout, spi5_sck, aud_mclk, i2s1_sclk, i2s1_sdata_in, + i2s1_sdata_out, i2s1_lrck, i2c11_clk, i2c11_dat, xhalt_trig, + gp_pwm1, gp_pwm6, gp_pwm7, gp_pwm8, ufs0, pe1_clkreq_l, pe1_rst_l, + pe2_rst_l, pe2_clkreq_l, pe3_clkreq_l, pe3_rst_l, sgmii0_sma_mdio, + sgmii0_sma_mdc, usb_vbus_en0, usb_vbus_en1, eth1_mdio, pe4_clkreq_l, + pe4_rst_l, pe5_clkreq_l, pe5_rst_l, eth0_mdio, eth0_mdc, eth1_mdc, + eth2_mdio, eth2_mdc, eth3_mdio, eth3_mdc, qspi0_cs_n, qspi0_io0, + qspi0_io1, qspi0_io2, qspi0_io3, qspi0_sck, sdmmc1_clk, sdmmc1_cmd, + sdmmc1_comp, sdmmc1_dat3, sdmmc1_dat2, sdmmc1_dat1, sdmmc1_dat0, + qspi3_sck, qspi3_cs0, qspi3_io0, qspi3_io1, dcb_vsync, dcb_hsync, + dsa_lspii, dce_vsync, dce_hsync, dch_vsync, dch_hsync, bl_en, + bl_pwm_dim0, rsvd1, soc_therm_oc3, i2s5_sclk, i2s5_sdata_in, + extperiph3_clk, extperiph4_clk, i2s5_sdata_out, i2s5_lrck, + sdmmc1_cd, i2s7_sdata_in, spi4_sck, spi4_din, spi4_dout, spi4_cs0, + spi4_cs1, gp_pwm5, i2c14_clk, i2c14_dat, i2s8_sclk, i2s8_sdata_out, + i2s8_lrck, i2s8_sdata_in, i2c16_clk, i2c16_dat, i2s3_sclk, + i2s3_sdata_out, i2s3_sdata_in, i2s3_lrck, pm_trig1, pm_trig0, + qspi2_sck, qspi2_cs0, qspi2_io0, qspi2_io1, dcc_vsync, dcc_hsync, + rsvd2, dcf_vsync, dcf_hsync, soundwire1_clk, soundwire1_dat0, + soundwire1_dat1, soundwire1_dat2, dmic2_clk, dmic2_dat, + nv_therm_fan_tach1, i2c15_clk, i2c15_dat, i2s7_lrck, + ccla_la_trigger_mux, i2s7_sclk, i2s7_sdata_out, dmic1_dat, + dmic1_clk, dcd_vsync, dcd_hsync, rsvd3, dcg_vsync, dcg_hsync, + dspk1_clk, dspk1_dat, soc_therm_oc2, istctrl_ist_done_n, + soc_therm_oc1, tsc_edge_out0c, tsc_edge_out0d, tsc_edge_out0a, + tsc_edge_out0b, touch_clk, hdmi_cec, i2c2_clk, i2c2_dat, i2c3_clk, + i2c3_dat, gp_pwm4, uarta_txd, uarta_rxd, spi2_sck, spi2_din, + spi2_dout, spi2_cs0, tsc_sync1, tsc_edge_out3, tsc_edge_out0, + tsc_edge_out1, tsc_sync0, soundwire0_clk, soundwire0_dat0, + l0l1_rst_out_n, l2_rst_out_n, uartl_txd, uartl_rxd, i2s9_sclk, + i2s9_sdata_out, i2s9_sdata_in, i2s9_lrck, dmic5_dat, dmic5_clk, + tsc_edge_out2 ] + + # out of the common properties, only these are allowed for Tegra264 + nvidia,pins: true + nvidia,pull: true + nvidia,tristate: true + nvidia,schmitt: true + nvidia,enable-input: true + nvidia,open-drain: true + nvidia,lock: true + nvidia,drive-type: true + nvidia,io-hv: true + +required: + - nvidia,pins + +# We would typically use unevaluatedProperties here but that has the +# downside that all the properties in the common bindings become valid +# for all chip generations. In this case, however, we want the per-SoC +# bindings to be able to override which of the common properties are +# allowed, since not all pinmux generations support the same sets of +# properties. This way, the common bindings define the format of the +# properties but the per-SoC bindings define which of them apply to a +# given chip. +additionalProperties: false diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-main.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-main.yaml new file mode 100644 index 000000000000..c40409d3263c --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-main.yaml @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra264-pinmux-main.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra264 Main Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra264-pinmux-main + + reg: + maxItems: 1 + +patternProperties: + "^pinmux(-[a-z0-9-]+)?$": + type: object + + # pin groups + additionalProperties: + $ref: nvidia,tegra264-pinmux-common.yaml + + properties: + nvidia,pins: + items: + enum: [ pwm1_pa0, pwm6_pa1, pwm7_pa2, pwm8_pa3, ufs0_ref_clk_pa4, + ufs0_rst_n_pa5, soc_gpio250_pf0, soc_gpio251_pf1, + soc_gpio252_pf2, dp_aux_ch0_hpd_pf3, dp_aux_ch1_hpd_pf4, + dp_aux_ch2_hpd_pf5, dp_aux_ch3_hpd_pf6, pwm2_pf7, pwm3_pg0, + gen7_i2c_scl_pg1, gen7_i2c_sda_pg2, gen9_i2c_scl_pg3, + gen9_i2c_sda_pg4, sdmmc1_clk_px0, sdmmc1_cmd_px1, + sdmmc1_dat0_px2, sdmmc1_dat1_px3, sdmmc1_dat2_px4, + sdmmc1_dat3_px5, sdmmc1_comp, soc_gpio124_pl0, + soc_gpio125_pl1, fan_tach0_pl2, soc_gpio127_pl3, + soc_gpio128_pl4, soc_gpio129_pl5, soc_gpio130_pl6, + soc_gpio131_pl7, gp_pwm9_pm0, soc_gpio133_pm1, uart9_tx_pm2, + uart9_rx_pm3, uart9_rts_n_pm4, uart9_cts_n_pm5, + soc_gpio170_pu0, soc_gpio171_pu1, soc_gpio172_pu2, + soc_gpio173_pu3, soc_gpio174_pu4, soc_gpio175_pu5, + soc_gpio176_pu6, soc_gpio177_pu7, soc_gpio178_pv0, + pwm10_pv1, uart4_tx_pv2, uart4_rx_pv3, uart4_rts_n_pv4, + uart4_cts_n_pv5, dap2_clk_pv6, dap2_din_pv7, dap2_dout_pw0, + dap2_fs_pw1, gen1_i2c_scl_pw2, gen1_i2c_sda_pw3, + gen0_i2c_scl_pw4, gen0_i2c_sda_pw5, pwr_i2c_scl_pw6, + pwr_i2c_sda_pw7, soc_gpio138_pp0, soc_gpio139_pp1, + dap6_sclk_pp2, dap6_dout_pp3, dap6_din_pp4, dap6_fs_pp5, + dap4_sclk_pp6, dap4_dout_pp7, dap4_din_pq0, dap4_fs_pq1, + spi5_sck_pq2, spi5_miso_pq3, spi5_mosi_pq4, spi5_cs0_pq5, + soc_gpio152_pq6, soc_gpio153_pq7, aud_mclk_pr0, + soc_gpio155_pr1, dap1_sclk_pr2, dap1_out_pr3, dap1_in_pr4, + dap1_fs_pr5, gen11_i2c_scl_pr6, gen11_i2c_sda_pr7, + soc_gpio350_ps0, soc_gpio351_ps1, qspi0_sck_pt0, + qspi0_cs_n_pt1, qspi0_io0_pt2, qspi0_io1_pt3, qspi0_io2_pt4, + qspi0_io3_pt5, soc_gpio192_pt6, soc_gpio270_py0, + soc_gpio271_py1, soc_gpio272_py2, soc_gpio273_py3, + soc_gpio274_py4, soc_gpio275_py5, soc_gpio276_py6, + soc_gpio277_py7, soc_gpio278_pz0, soc_gpio279_pz1, + xhalt_trig_pz2, soc_gpio281_pz3, soc_gpio282_pz4, + soc_gpio283_pz5, soc_gpio284_pz6, soc_gpio285_pz7, + soc_gpio286_pal0, soc_gpio287_pal1, soc_gpio288_pal2, + cpu_pwr_req_ph0, gpu_pwr_req_ph1, uart10_tx_ph2, + uart10_rx_ph3, uart10_rts_n_ph4, uart10_cts_n_ph5, + spi3_sck_ph6, spi3_miso_ph7, spi3_mosi_pj0, spi3_cs0_pj1, + spi3_cs3_pj2, uart5_tx_pj3, uart5_rx_pj4, uart5_rts_n_pj5, + uart5_cts_n_pj6, spi1_sck_pj7, spi1_miso_pk0, spi1_mosi_pk1, + spi1_cs0_pk2, spi1_cs1_pk3, extperiph1_clk_pk4, + extperiph2_clk_pk5, gen12_i2c_scl_pk6, gen12_i2c_sda_pk7, + drive_cpu_pwr_req_ph0, drive_gpu_pwr_req_ph1, + drive_uart10_cts_n_ph5, drive_uart10_rts_n_ph4, + drive_uart10_rx_ph3, drive_uart10_tx_ph2, + drive_spi3_cs0_pj1, drive_spi3_cs3_pj2, + drive_spi3_miso_ph7, drive_spi3_mosi_pj0, + drive_spi3_sck_ph6, drive_uart5_cts_n_pj6, + drive_uart5_rts_n_pj5, drive_uart5_rx_pj4, + drive_uart5_tx_pj3, drive_spi1_cs0_pk2, + drive_spi1_cs1_pk3, drive_spi1_miso_pk0, + drive_spi1_mosi_pk1, drive_spi1_sck_pj7, + drive_extperiph2_clk_pk5, drive_extperiph1_clk_pk4, + drive_gen12_i2c_scl_pk6, drive_gen12_i2c_sda_pk7, + drive_soc_gpio124_pl0, drive_soc_gpio125_pl1, + drive_fan_tach0_pl2, drive_soc_gpio127_pl3, + drive_soc_gpio128_pl4, drive_soc_gpio129_pl5, + drive_soc_gpio130_pl6, drive_soc_gpio131_pl7, + drive_gp_pwm9_pm0, drive_soc_gpio133_pm1, + drive_uart9_cts_n_pm5, drive_uart9_rts_n_pm4, + drive_uart9_rx_pm3, drive_uart9_tx_pm2, + drive_sdmmc1_clk_px0, drive_sdmmc1_cmd_px1, + drive_sdmmc1_dat3_px5, drive_sdmmc1_dat2_px4, + drive_sdmmc1_dat1_px3, drive_sdmmc1_dat0_px2, + drive_qspi0_cs_n_pt1, drive_qspi0_io0_pt2, + drive_qspi0_io1_pt3, drive_qspi0_io2_pt4, + drive_qspi0_io3_pt5, drive_qspi0_sck_pt0, + drive_soc_gpio192_pt6, drive_soc_gpio138_pp0, + drive_soc_gpio139_pp1, drive_dap6_din_pp4, + drive_dap6_dout_pp3, drive_dap6_fs_pp5, + drive_dap6_sclk_pp2, drive_dap4_dout_pp7, + drive_dap4_sclk_pp6, drive_dap4_din_pq0, + drive_dap4_fs_pq1, drive_spi5_cs0_pq5, + drive_spi5_miso_pq3, drive_spi5_mosi_pq4, + drive_spi5_sck_pq2, drive_soc_gpio152_pq6, + drive_soc_gpio153_pq7, drive_soc_gpio155_pr1, + drive_aud_mclk_pr0, drive_dap1_sclk_pr2, + drive_dap1_in_pr4, drive_dap1_out_pr3, + drive_dap1_fs_pr5, drive_gen11_i2c_scl_pr6, + drive_gen11_i2c_sda_pr7, drive_soc_gpio350_ps0, + drive_soc_gpio351_ps1, drive_gen0_i2c_scl_pw4, + drive_gen0_i2c_sda_pw5, drive_gen1_i2c_scl_pw2, + drive_gen1_i2c_sda_pw3, drive_dap2_fs_pw1, + drive_dap2_clk_pv6, drive_dap2_din_pv7, + drive_dap2_dout_pw0, drive_pwm10_pv1, + drive_soc_gpio170_pu0, drive_soc_gpio171_pu1, + drive_soc_gpio172_pu2, drive_soc_gpio173_pu3, + drive_soc_gpio174_pu4, drive_soc_gpio175_pu5, + drive_soc_gpio176_pu6, drive_soc_gpio177_pu7, + drive_soc_gpio178_pv0, drive_uart4_cts_n_pv5, + drive_uart4_rts_n_pv4, drive_uart4_rx_pv3, + drive_uart4_tx_pv2, drive_pwr_i2c_sda_pw7, + drive_pwr_i2c_scl_pw6, drive_soc_gpio250_pf0, + drive_soc_gpio251_pf1, drive_soc_gpio252_pf2, + drive_dp_aux_ch0_hpd_pf3, drive_dp_aux_ch1_hpd_pf4, + drive_dp_aux_ch2_hpd_pf5, drive_dp_aux_ch3_hpd_pf6, + drive_pwm2_pf7, drive_pwm3_pg0, + drive_gen7_i2c_scl_pg1, drive_gen7_i2c_sda_pg2, + drive_gen9_i2c_scl_pg3, drive_gen9_i2c_sda_pg4, + drive_soc_gpio270_py0, drive_soc_gpio271_py1, + drive_soc_gpio272_py2, drive_soc_gpio273_py3, + drive_soc_gpio274_py4, drive_soc_gpio275_py5, + drive_soc_gpio276_py6, drive_soc_gpio277_py7, + drive_soc_gpio278_pz0, drive_soc_gpio279_pz1, + drive_soc_gpio282_pz4, drive_soc_gpio283_pz5, + drive_soc_gpio284_pz6, drive_soc_gpio285_pz7, + drive_soc_gpio286_pal0, drive_soc_gpio287_pal1, + drive_soc_gpio288_pal2, drive_xhalt_trig_pz2, + drive_soc_gpio281_pz3 ] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + pinmux@c281000 { + compatible = "nvidia,tegra264-pinmux-main"; + reg = <0xc281000 0xc000>; + + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinmux-default { + sdmmc1 { + nvidia,pins = "sdmmc1_clk_px0"; + nvidia,function = "sdmmc1_cd"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-uphy.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-uphy.yaml new file mode 100644 index 000000000000..9a54795d9cc5 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra264-pinmux-uphy.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/nvidia,tegra264-pinmux-uphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra264 UPHY Pinmux Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra264-pinmux-uphy + + reg: + maxItems: 1 + +patternProperties: + "^pinmux(-[a-z0-9-]+)?$": + type: object + + # pin groups + additionalProperties: + $ref: nvidia,tegra264-pinmux-common.yaml + + properties: + nvidia,pins: + items: + enum: [ eth1_mdio_pe0, pex_l4_clkreq_n_pd0, pex_l4_rst_n_pd1, + pex_l5_clkreq_n_pd2, pex_l5_rst_n_pd3, eth0_mdio_pd4, + eth0_mdc_pd5, eth1_mdc_pe1, eth2_mdio_pe2, eth2_mdc_pe3, + eth3_mdio_pd6, eth3_mdc_pd7, pex_l1_clkreq_n_pb0, + pex_l1_rst_n_pb1, pex_wake_n_pc2, pex_l2_rst_n_pb3, + pex_l2_clkreq_n_pb2, pex_l3_clkreq_n_pb4, pex_l3_rst_n_pb5, + sgmii0_sma_mdio_pc0, sgmii0_sma_mdc_pc1, soc_gpio113_pb6, + soc_gpio114_pb7, pwm1_pa0, pwm6_pa1, pwm7_pa2, pwm8_pa3, + ufs0_ref_clk_pa4, ufs0_rst_n_pa5, drive_eth1_mdio_pe0, + drive_pex_l4_clkreq_n_pd0, drive_pex_l4_rst_n_pd1, + drive_pex_l5_clkreq_n_pd2, drive_pex_l5_rst_n_pd3, + drive_eth0_mdio_pd4, drive_eth0_mdc_pd5, drive_eth1_mdc_pe1, + drive_eth2_mdio_pe2, drive_eth2_mdc_pe3, drive_eth3_mdio_pd6, + drive_eth3_mdc_pd7, drive_pex_l1_clkreq_n_pb0, + drive_pex_l1_rst_n_pb1, drive_pex_wake_n_pc2, + drive_pex_l2_rst_n_pb3, drive_pex_l2_clkreq_n_pb2, + drive_pex_l3_clkreq_n_pb4, drive_pex_l3_rst_n_pb5, + drive_sgmii0_sma_mdio_pc0, drive_sgmii0_sma_mdc_pc1, + drive_soc_gpio113_pb6, drive_soc_gpio114_pb7, + drive_pwm1_pa0, drive_pwm6_pa1, drive_pwm7_pa2, + drive_pwm8_pa3, drive_ufs0_ref_clk_pa4, drive_ufs0_rst_n_pa5 ] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + pinmux@82e0000 { + compatible = "nvidia,tegra264-pinmux-uphy"; + reg = <0x82e0000 0x4000>; + + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_default>; + + pinmux_default: pinmux-default { + pex { + nvidia,pins = "pex_l1_rst_n_pb1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + }; + }; From c98506206912dd0ddea94f844b65b0209a57e372 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 27 Apr 2026 13:42:30 +0000 Subject: [PATCH 1816/5214] pinctrl: tegra: Add Tegra264 pinmux driver Add support for the three pin controllers (MAIN, UPHY and AON) found on Tegra264. Signed-off-by: Prathamesh Shete Reviewed-by: Jon Hunter Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/Kconfig | 10 + drivers/pinctrl/tegra/Makefile | 1 + drivers/pinctrl/tegra/pinctrl-tegra264.c | 2216 ++++++++++++++++++++++ 3 files changed, 2227 insertions(+) create mode 100644 drivers/pinctrl/tegra/pinctrl-tegra264.c diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index 96b9f5c8ed51..c7507193044f 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -47,6 +47,16 @@ config PINCTRL_TEGRA238 and configuration for the MAIN and AON pin controllers found on Tegra238. +config PINCTRL_TEGRA264 + tristate "NVIDIA Tegra264 pinctrl driver" + default m if ARCH_TEGRA_264_SOC + select PINCTRL_TEGRA + help + Say Y or M here to enable support for the pinctrl driver for + NVIDIA Tegra264 SoC. This driver controls the pin multiplexing + and configuration for the MAIN, AON and UPHY pin controllers found + on Tegra264. + config PINCTRL_TEGRA_XUSB bool "NVIDIA Tegra XUSB pin controller" if COMPILE_TEST && !ARCH_TEGRA default y if ARCH_TEGRA diff --git a/drivers/pinctrl/tegra/Makefile b/drivers/pinctrl/tegra/Makefile index ce700bbcbf6e..71ade768bf9c 100644 --- a/drivers/pinctrl/tegra/Makefile +++ b/drivers/pinctrl/tegra/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_PINCTRL_TEGRA186) += pinctrl-tegra186.o obj-$(CONFIG_PINCTRL_TEGRA194) += pinctrl-tegra194.o obj-$(CONFIG_PINCTRL_TEGRA234) += pinctrl-tegra234.o obj-$(CONFIG_PINCTRL_TEGRA238) += pinctrl-tegra238.o +obj-$(CONFIG_PINCTRL_TEGRA264) += pinctrl-tegra264.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o diff --git a/drivers/pinctrl/tegra/pinctrl-tegra264.c b/drivers/pinctrl/tegra/pinctrl-tegra264.c new file mode 100644 index 000000000000..5a0c91aaba3a --- /dev/null +++ b/drivers/pinctrl/tegra/pinctrl-tegra264.c @@ -0,0 +1,2216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Pinctrl data for the NVIDIA Tegra264 pinmux + * + * Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "pinctrl-tegra.h" + +/* Define unique ID for each pins */ +enum { + TEGRA_PIN_PEX_L4_CLKREQ_N_PD0, + TEGRA_PIN_PEX_L4_RST_N_PD1, + TEGRA_PIN_PEX_L5_CLKREQ_N_PD2, + TEGRA_PIN_PEX_L5_RST_N_PD3, + TEGRA_PIN_ETH0_MDIO_PD4, + TEGRA_PIN_ETH0_MDC_PD5, + TEGRA_PIN_ETH3_MDIO_PD6, + TEGRA_PIN_ETH3_MDC_PD7, + TEGRA_PIN_ETH1_MDIO_PE0, + TEGRA_PIN_ETH1_MDC_PE1, + TEGRA_PIN_ETH2_MDIO_PE2, + TEGRA_PIN_ETH2_MDC_PE3, + TEGRA_PIN_PEX_L1_CLKREQ_N_PB0, + TEGRA_PIN_PEX_L1_RST_N_PB1, + TEGRA_PIN_PEX_L2_CLKREQ_N_PB2, + TEGRA_PIN_PEX_L2_RST_N_PB3, + TEGRA_PIN_PEX_L3_CLKREQ_N_PB4, + TEGRA_PIN_PEX_L3_RST_N_PB5, + TEGRA_PIN_SOC_GPIO113_PB6, + TEGRA_PIN_SOC_GPIO114_PB7, + TEGRA_PIN_SGMII0_SMA_MDIO_PC0, + TEGRA_PIN_SGMII0_SMA_MDC_PC1, + TEGRA_PIN_PEX_WAKE_N_PC2, + TEGRA_PIN_PWM1_PA0, + TEGRA_PIN_PWM6_PA1, + TEGRA_PIN_PWM7_PA2, + TEGRA_PIN_PWM8_PA3, + TEGRA_PIN_UFS0_REF_CLK_PA4, + TEGRA_PIN_UFS0_RST_N_PA5, +}; + +enum { + TEGRA_PIN_SOC_GPIO250_PF0, + TEGRA_PIN_SOC_GPIO251_PF1, + TEGRA_PIN_SOC_GPIO252_PF2, + TEGRA_PIN_DP_AUX_CH0_HPD_PF3, + TEGRA_PIN_DP_AUX_CH1_HPD_PF4, + TEGRA_PIN_DP_AUX_CH2_HPD_PF5, + TEGRA_PIN_DP_AUX_CH3_HPD_PF6, + TEGRA_PIN_PWM2_PF7, + TEGRA_PIN_PWM3_PG0, + TEGRA_PIN_GEN7_I2C_SCL_PG1, + TEGRA_PIN_GEN7_I2C_SDA_PG2, + TEGRA_PIN_GEN9_I2C_SCL_PG3, + TEGRA_PIN_GEN9_I2C_SDA_PG4, + TEGRA_PIN_SDMMC1_CLK_PX0, + TEGRA_PIN_SDMMC1_CMD_PX1, + TEGRA_PIN_SDMMC1_DAT0_PX2, + TEGRA_PIN_SDMMC1_DAT1_PX3, + TEGRA_PIN_SDMMC1_DAT2_PX4, + TEGRA_PIN_SDMMC1_DAT3_PX5, + TEGRA_PIN_SDMMC1_COMP, + TEGRA_PIN_SOC_GPIO124_PL0, + TEGRA_PIN_SOC_GPIO125_PL1, + TEGRA_PIN_FAN_TACH0_PL2, + TEGRA_PIN_SOC_GPIO127_PL3, + TEGRA_PIN_SOC_GPIO128_PL4, + TEGRA_PIN_SOC_GPIO129_PL5, + TEGRA_PIN_SOC_GPIO130_PL6, + TEGRA_PIN_SOC_GPIO131_PL7, + TEGRA_PIN_GP_PWM9_PM0, + TEGRA_PIN_SOC_GPIO133_PM1, + TEGRA_PIN_UART9_TX_PM2, + TEGRA_PIN_UART9_RX_PM3, + TEGRA_PIN_UART9_RTS_N_PM4, + TEGRA_PIN_UART9_CTS_N_PM5, + TEGRA_PIN_SOC_GPIO170_PU0, + TEGRA_PIN_SOC_GPIO171_PU1, + TEGRA_PIN_SOC_GPIO172_PU2, + TEGRA_PIN_SOC_GPIO173_PU3, + TEGRA_PIN_SOC_GPIO174_PU4, + TEGRA_PIN_SOC_GPIO175_PU5, + TEGRA_PIN_SOC_GPIO176_PU6, + TEGRA_PIN_SOC_GPIO177_PU7, + TEGRA_PIN_SOC_GPIO178_PV0, + TEGRA_PIN_PWM10_PV1, + TEGRA_PIN_UART4_TX_PV2, + TEGRA_PIN_UART4_RX_PV3, + TEGRA_PIN_UART4_RTS_N_PV4, + TEGRA_PIN_UART4_CTS_N_PV5, + TEGRA_PIN_DAP2_CLK_PV6, + TEGRA_PIN_DAP2_DIN_PV7, + TEGRA_PIN_DAP2_DOUT_PW0, + TEGRA_PIN_DAP2_FS_PW1, + TEGRA_PIN_GEN1_I2C_SCL_PW2, + TEGRA_PIN_GEN1_I2C_SDA_PW3, + TEGRA_PIN_GEN0_I2C_SCL_PW4, + TEGRA_PIN_GEN0_I2C_SDA_PW5, + TEGRA_PIN_PWR_I2C_SCL_PW6, + TEGRA_PIN_PWR_I2C_SDA_PW7, + TEGRA_PIN_SOC_GPIO138_PP0, + TEGRA_PIN_SOC_GPIO139_PP1, + TEGRA_PIN_DAP6_SCLK_PP2, + TEGRA_PIN_DAP6_DOUT_PP3, + TEGRA_PIN_DAP6_DIN_PP4, + TEGRA_PIN_DAP6_FS_PP5, + TEGRA_PIN_DAP4_SCLK_PP6, + TEGRA_PIN_DAP4_DOUT_PP7, + TEGRA_PIN_DAP4_DIN_PQ0, + TEGRA_PIN_DAP4_FS_PQ1, + TEGRA_PIN_SPI5_SCK_PQ2, + TEGRA_PIN_SPI5_MISO_PQ3, + TEGRA_PIN_SPI5_MOSI_PQ4, + TEGRA_PIN_SPI5_CS0_PQ5, + TEGRA_PIN_SOC_GPIO152_PQ6, + TEGRA_PIN_SOC_GPIO153_PQ7, + TEGRA_PIN_AUD_MCLK_PR0, + TEGRA_PIN_SOC_GPIO155_PR1, + TEGRA_PIN_DAP1_SCLK_PR2, + TEGRA_PIN_DAP1_OUT_PR3, + TEGRA_PIN_DAP1_IN_PR4, + TEGRA_PIN_DAP1_FS_PR5, + TEGRA_PIN_GEN11_I2C_SCL_PR6, + TEGRA_PIN_GEN11_I2C_SDA_PR7, + TEGRA_PIN_SOC_GPIO350_PS0, + TEGRA_PIN_SOC_GPIO351_PS1, + TEGRA_PIN_QSPI0_SCK_PT0, + TEGRA_PIN_QSPI0_CS_N_PT1, + TEGRA_PIN_QSPI0_IO0_PT2, + TEGRA_PIN_QSPI0_IO1_PT3, + TEGRA_PIN_QSPI0_IO2_PT4, + TEGRA_PIN_QSPI0_IO3_PT5, + TEGRA_PIN_SOC_GPIO192_PT6, + TEGRA_PIN_SOC_GPIO270_PY0, + TEGRA_PIN_SOC_GPIO271_PY1, + TEGRA_PIN_SOC_GPIO272_PY2, + TEGRA_PIN_SOC_GPIO273_PY3, + TEGRA_PIN_SOC_GPIO274_PY4, + TEGRA_PIN_SOC_GPIO275_PY5, + TEGRA_PIN_SOC_GPIO276_PY6, + TEGRA_PIN_SOC_GPIO277_PY7, + TEGRA_PIN_SOC_GPIO278_PZ0, + TEGRA_PIN_SOC_GPIO279_PZ1, + TEGRA_PIN_XHALT_TRIG_PZ2, + TEGRA_PIN_SOC_GPIO281_PZ3, + TEGRA_PIN_SOC_GPIO282_PZ4, + TEGRA_PIN_SOC_GPIO283_PZ5, + TEGRA_PIN_SOC_GPIO284_PZ6, + TEGRA_PIN_SOC_GPIO285_PZ7, + TEGRA_PIN_SOC_GPIO286_PAL0, + TEGRA_PIN_SOC_GPIO287_PAL1, + TEGRA_PIN_SOC_GPIO288_PAL2, + TEGRA_PIN_CPU_PWR_REQ_PH0, + TEGRA_PIN_GPU_PWR_REQ_PH1, + TEGRA_PIN_UART10_TX_PH2, + TEGRA_PIN_UART10_RX_PH3, + TEGRA_PIN_UART10_RTS_N_PH4, + TEGRA_PIN_UART10_CTS_N_PH5, + TEGRA_PIN_SPI3_SCK_PH6, + TEGRA_PIN_SPI3_MISO_PH7, + TEGRA_PIN_SPI3_MOSI_PJ0, + TEGRA_PIN_SPI3_CS0_PJ1, + TEGRA_PIN_SPI3_CS3_PJ2, + TEGRA_PIN_UART5_TX_PJ3, + TEGRA_PIN_UART5_RX_PJ4, + TEGRA_PIN_UART5_RTS_N_PJ5, + TEGRA_PIN_UART5_CTS_N_PJ6, + TEGRA_PIN_SPI1_SCK_PJ7, + TEGRA_PIN_SPI1_MISO_PK0, + TEGRA_PIN_SPI1_MOSI_PK1, + TEGRA_PIN_SPI1_CS0_PK2, + TEGRA_PIN_SPI1_CS1_PK3, + TEGRA_PIN_EXTPERIPH1_CLK_PK4, + TEGRA_PIN_EXTPERIPH2_CLK_PK5, + TEGRA_PIN_GEN12_I2C_SCL_PK6, + TEGRA_PIN_GEN12_I2C_SDA_PK7, +}; + +enum { + TEGRA_PIN_SOC_GPIO00_PAA0, + TEGRA_PIN_VCOMP_ALERT_PAA1, + TEGRA_PIN_AO_RETENTION_N_PAA2, + TEGRA_PIN_BATT_OC_PAA3, + TEGRA_PIN_BOOTV_CTL_N_PAA4, + TEGRA_PIN_POWER_ON_PAA5, + TEGRA_PIN_HDMI_CEC_PAA6, + TEGRA_PIN_SOC_GPIO07_PAA7, + TEGRA_PIN_SOC_GPIO08_PBB0, + TEGRA_PIN_SOC_GPIO09_PBB1, + TEGRA_PIN_GEN2_I2C_SCL_PCC0, + TEGRA_PIN_GEN2_I2C_SDA_PCC1, + TEGRA_PIN_GEN3_I2C_SCL_PCC2, + TEGRA_PIN_GEN3_I2C_SDA_PCC3, + TEGRA_PIN_GP_PWM4_PCC4, + TEGRA_PIN_UART0_TX_PCC5, + TEGRA_PIN_UART0_RX_PCC6, + TEGRA_PIN_SPI2_SCK_PCC7, + TEGRA_PIN_SPI2_MISO_PDD0, + TEGRA_PIN_SPI2_MOSI_PDD1, + TEGRA_PIN_SPI2_CS0_N_PDD2, + TEGRA_PIN_SOC_GPIO21_PDD3, + TEGRA_PIN_SOC_GPIO22_PDD4, + TEGRA_PIN_SOC_GPIO23_PDD5, + TEGRA_PIN_SOC_GPIO24_PDD6, + TEGRA_PIN_SOC_GPIO25_PDD7, + TEGRA_PIN_SOC_GPIO26_PEE0, + TEGRA_PIN_SOC_GPIO27_PEE1, + TEGRA_PIN_SOC_GPIO28_PEE2, + TEGRA_PIN_SOC_GPIO29_PEE3, +}; + +static const struct pinctrl_pin_desc tegra264_uphy_pins[] = { + PINCTRL_PIN(TEGRA_PIN_PEX_L4_CLKREQ_N_PD0, "PEX_L4_CLKREQ_N_PD0"), + PINCTRL_PIN(TEGRA_PIN_PEX_L4_RST_N_PD1, "PEX_L4_RST_N_PD1"), + PINCTRL_PIN(TEGRA_PIN_PEX_L5_CLKREQ_N_PD2, "PEX_L5_CLKREQ_N_PD2"), + PINCTRL_PIN(TEGRA_PIN_PEX_L5_RST_N_PD3, "PEX_L5_RST_N_PD3"), + PINCTRL_PIN(TEGRA_PIN_ETH0_MDIO_PD4, "ETH0_MDIO_PD4"), + PINCTRL_PIN(TEGRA_PIN_ETH0_MDC_PD5, "ETH0_MDC_PD5"), + PINCTRL_PIN(TEGRA_PIN_ETH3_MDIO_PD6, "ETH3_MDIO_PD6"), + PINCTRL_PIN(TEGRA_PIN_ETH3_MDC_PD7, "ETH3_MDC_PD7"), + PINCTRL_PIN(TEGRA_PIN_ETH1_MDIO_PE0, "ETH1_MDIO_PE0"), + PINCTRL_PIN(TEGRA_PIN_ETH1_MDC_PE1, "ETH1_MDC_PE1"), + PINCTRL_PIN(TEGRA_PIN_ETH2_MDIO_PE2, "ETH2_MDIO_PE2"), + PINCTRL_PIN(TEGRA_PIN_ETH2_MDC_PE3, "ETH2_MDC_PE3"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_CLKREQ_N_PB0, "PEX_L1_CLKREQ_N_PB0"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_RST_N_PB1, "PEX_L1_RST_N_PB1"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_CLKREQ_N_PB2, "PEX_L2_CLKREQ_N_PB2"), + PINCTRL_PIN(TEGRA_PIN_PEX_L2_RST_N_PB3, "PEX_L2_RST_N_PB3"), + PINCTRL_PIN(TEGRA_PIN_PEX_L3_CLKREQ_N_PB4, "PEX_L3_CLKREQ_N_PB4"), + PINCTRL_PIN(TEGRA_PIN_PEX_L3_RST_N_PB5, "PEX_L3_RST_N_PB5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO113_PB6, "SOC_GPIO113_PB6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO114_PB7, "SOC_GPIO114_PB7"), + PINCTRL_PIN(TEGRA_PIN_SGMII0_SMA_MDIO_PC0, "SGMII0_SMA_MDIO_PC0"), + PINCTRL_PIN(TEGRA_PIN_SGMII0_SMA_MDC_PC1, "SGMII0_SMA_MDC_PC1"), + PINCTRL_PIN(TEGRA_PIN_PEX_WAKE_N_PC2, "PEX_WAKE_N_PC2"), + PINCTRL_PIN(TEGRA_PIN_PWM1_PA0, "PWM1_PA0"), + PINCTRL_PIN(TEGRA_PIN_PWM6_PA1, "PWM6_PA1"), + PINCTRL_PIN(TEGRA_PIN_PWM7_PA2, "PWM7_PA2"), + PINCTRL_PIN(TEGRA_PIN_PWM8_PA3, "PWM8_PA3"), + PINCTRL_PIN(TEGRA_PIN_UFS0_REF_CLK_PA4, "UFS0_REF_CLK_PA4"), + PINCTRL_PIN(TEGRA_PIN_UFS0_RST_N_PA5, "UFS0_RST_N_PA5"), +}; + +static const struct pinctrl_pin_desc tegra264_main_pins[] = { + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO250_PF0, "SOC_GPIO250_PF0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO251_PF1, "SOC_GPIO251_PF1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO252_PF2, "SOC_GPIO252_PF2"), + PINCTRL_PIN(TEGRA_PIN_DP_AUX_CH0_HPD_PF3, "DP_AUX_CH0_HPD_PF3"), + PINCTRL_PIN(TEGRA_PIN_DP_AUX_CH1_HPD_PF4, "DP_AUX_CH1_HPD_PF4"), + PINCTRL_PIN(TEGRA_PIN_DP_AUX_CH2_HPD_PF5, "DP_AUX_CH2_HPD_PF5"), + PINCTRL_PIN(TEGRA_PIN_DP_AUX_CH3_HPD_PF6, "DP_AUX_CH3_HPD_PF6"), + PINCTRL_PIN(TEGRA_PIN_PWM2_PF7, "PWM2_PF7"), + PINCTRL_PIN(TEGRA_PIN_PWM3_PG0, "PWM3_PG0"), + PINCTRL_PIN(TEGRA_PIN_GEN7_I2C_SCL_PG1, "GEN7_I2C_SCL_PG1"), + PINCTRL_PIN(TEGRA_PIN_GEN7_I2C_SDA_PG2, "GEN7_I2C_SDA_PG2"), + PINCTRL_PIN(TEGRA_PIN_GEN9_I2C_SCL_PG3, "GEN9_I2C_SCL_PG3"), + PINCTRL_PIN(TEGRA_PIN_GEN9_I2C_SDA_PG4, "GEN9_I2C_SDA_PG4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PX0, "SDMMC1_CLK_PX0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PX1, "SDMMC1_CMD_PX1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PX2, "SDMMC1_DAT0_PX2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PX3, "SDMMC1_DAT1_PX3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PX4, "SDMMC1_DAT2_PX4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PX5, "SDMMC1_DAT3_PX5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_COMP, "SDMMC1_COMP"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ_PH0, "CPU_PWR_REQ_PH0"), + PINCTRL_PIN(TEGRA_PIN_GPU_PWR_REQ_PH1, "GPU_PWR_REQ_PH1"), + PINCTRL_PIN(TEGRA_PIN_UART10_TX_PH2, "UART10_TX_PH2"), + PINCTRL_PIN(TEGRA_PIN_UART10_RX_PH3, "UART10_RX_PH3"), + PINCTRL_PIN(TEGRA_PIN_UART10_RTS_N_PH4, "UART10_RTS_N_PH4"), + PINCTRL_PIN(TEGRA_PIN_UART10_CTS_N_PH5, "UART10_CTS_N_PH5"), + PINCTRL_PIN(TEGRA_PIN_SPI3_SCK_PH6, "SPI3_SCK_PH6"), + PINCTRL_PIN(TEGRA_PIN_SPI3_MISO_PH7, "SPI3_MISO_PH7"), + PINCTRL_PIN(TEGRA_PIN_SPI3_MOSI_PJ0, "SPI3_MOSI_PJ0"), + PINCTRL_PIN(TEGRA_PIN_SPI3_CS0_PJ1, "SPI3_CS0_PJ1"), + PINCTRL_PIN(TEGRA_PIN_SPI3_CS3_PJ2, "SPI3_CS3_PJ2"), + PINCTRL_PIN(TEGRA_PIN_UART5_TX_PJ3, "UART5_TX_PJ3"), + PINCTRL_PIN(TEGRA_PIN_UART5_RX_PJ4, "UART5_RX_PJ4"), + PINCTRL_PIN(TEGRA_PIN_UART5_RTS_N_PJ5, "UART5_RTS_N_PJ5"), + PINCTRL_PIN(TEGRA_PIN_UART5_CTS_N_PJ6, "UART5_CTS_N_PJ6"), + PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PJ7, "SPI1_SCK_PJ7"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PK0, "SPI1_MISO_PK0"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PK1, "SPI1_MOSI_PK1"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_PK2, "SPI1_CS0_PK2"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS1_PK3, "SPI1_CS1_PK3"), + PINCTRL_PIN(TEGRA_PIN_EXTPERIPH1_CLK_PK4, "EXTPERIPH1_CLK_PK4"), + PINCTRL_PIN(TEGRA_PIN_EXTPERIPH2_CLK_PK5, "EXTPERIPH2_CLK_PK5"), + PINCTRL_PIN(TEGRA_PIN_GEN12_I2C_SCL_PK6, "GEN12_I2C_SCL_PK6"), + PINCTRL_PIN(TEGRA_PIN_GEN12_I2C_SDA_PK7, "GEN12_I2C_SDA_PK7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO124_PL0, "SOC_GPIO124_PL0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO125_PL1, "SOC_GPIO125_PL1"), + PINCTRL_PIN(TEGRA_PIN_FAN_TACH0_PL2, "FAN_TACH0_PL2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO127_PL3, "SOC_GPIO127_PL3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO128_PL4, "SOC_GPIO128_PL4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO129_PL5, "SOC_GPIO129_PL5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO130_PL6, "SOC_GPIO130_PL6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO131_PL7, "SOC_GPIO131_PL7"), + PINCTRL_PIN(TEGRA_PIN_GP_PWM9_PM0, "GP_PWM9_PM0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO133_PM1, "SOC_GPIO133_PM1"), + PINCTRL_PIN(TEGRA_PIN_UART9_TX_PM2, "UART9_TX_PM2"), + PINCTRL_PIN(TEGRA_PIN_UART9_RX_PM3, "UART9_RX_PM3"), + PINCTRL_PIN(TEGRA_PIN_UART9_RTS_N_PM4, "UART9_RTS_N_PM4"), + PINCTRL_PIN(TEGRA_PIN_UART9_CTS_N_PM5, "UART9_CTS_N_PM5"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_SCK_PT0, "QSPI0_SCK_PT0"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_CS_N_PT1, "QSPI0_CS_N_PT1"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO0_PT2, "QSPI0_IO0_PT2"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO1_PT3, "QSPI0_IO1_PT3"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO2_PT4, "QSPI0_IO2_PT4"), + PINCTRL_PIN(TEGRA_PIN_QSPI0_IO3_PT5, "QSPI0_IO3_PT5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO192_PT6, "SOC_GPIO192_PT6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO170_PU0, "SOC_GPIO170_PU0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO171_PU1, "SOC_GPIO171_PU1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO172_PU2, "SOC_GPIO172_PU2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO173_PU3, "SOC_GPIO173_PU3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO174_PU4, "SOC_GPIO174_PU4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO175_PU5, "SOC_GPIO175_PU5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO176_PU6, "SOC_GPIO176_PU6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO177_PU7, "SOC_GPIO177_PU7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO178_PV0, "SOC_GPIO178_PV0"), + PINCTRL_PIN(TEGRA_PIN_PWM10_PV1, "PWM10_PV1"), + PINCTRL_PIN(TEGRA_PIN_UART4_TX_PV2, "UART4_TX_PV2"), + PINCTRL_PIN(TEGRA_PIN_UART4_RX_PV3, "UART4_RX_PV3"), + PINCTRL_PIN(TEGRA_PIN_UART4_RTS_N_PV4, "UART4_RTS_N_PV4"), + PINCTRL_PIN(TEGRA_PIN_UART4_CTS_N_PV5, "UART4_CTS_N_PV5"), + PINCTRL_PIN(TEGRA_PIN_DAP2_CLK_PV6, "DAP2_CLK_PV6"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PV7, "DAP2_DIN_PV7"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PW0, "DAP2_DOUT_PW0"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PW1, "DAP2_FS_PW1"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PW2, "GEN1_I2C_SCL_PW2"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PW3, "GEN1_I2C_SDA_PW3"), + PINCTRL_PIN(TEGRA_PIN_GEN0_I2C_SCL_PW4, "GEN0_I2C_SCL_PW4"), + PINCTRL_PIN(TEGRA_PIN_GEN0_I2C_SDA_PW5, "GEN0_I2C_SDA_PW5"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PW6, "PWR_I2C_SCL_PW6"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PW7, "PWR_I2C_SDA_PW7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO270_PY0, "SOC_GPIO270_PY0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO271_PY1, "SOC_GPIO271_PY1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO272_PY2, "SOC_GPIO272_PY2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO273_PY3, "SOC_GPIO273_PY3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO274_PY4, "SOC_GPIO274_PY4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO275_PY5, "SOC_GPIO275_PY5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO276_PY6, "SOC_GPIO276_PY6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO277_PY7, "SOC_GPIO277_PY7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO278_PZ0, "SOC_GPIO278_PZ0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO279_PZ1, "SOC_GPIO279_PZ1"), + PINCTRL_PIN(TEGRA_PIN_XHALT_TRIG_PZ2, "XHALT_TRIG_PZ2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO281_PZ3, "SOC_GPIO281_PZ3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO282_PZ4, "SOC_GPIO282_PZ4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO283_PZ5, "SOC_GPIO283_PZ5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO284_PZ6, "SOC_GPIO284_PZ6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO285_PZ7, "SOC_GPIO285_PZ7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO286_PAL0, "SOC_GPIO286_PAL0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO287_PAL1, "SOC_GPIO287_PAL1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO288_PAL2, "SOC_GPIO288_PAL2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO138_PP0, "SOC_GPIO138_PP0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO139_PP1, "SOC_GPIO139_PP1"), + PINCTRL_PIN(TEGRA_PIN_DAP6_SCLK_PP2, "DAP6_SCLK_PP2"), + PINCTRL_PIN(TEGRA_PIN_DAP6_DOUT_PP3, "DAP6_DOUT_PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP6_DIN_PP4, "DAP6_DIN_PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP6_FS_PP5, "DAP6_FS_PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP6, "DAP4_SCLK_PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP7, "DAP4_DOUT_PP7"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PQ0, "DAP4_DIN_PQ0"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PQ1, "DAP4_FS_PQ1"), + PINCTRL_PIN(TEGRA_PIN_SPI5_SCK_PQ2, "SPI5_SCK_PQ2"), + PINCTRL_PIN(TEGRA_PIN_SPI5_MISO_PQ3, "SPI5_MISO_PQ3"), + PINCTRL_PIN(TEGRA_PIN_SPI5_MOSI_PQ4, "SPI5_MOSI_PQ4"), + PINCTRL_PIN(TEGRA_PIN_SPI5_CS0_PQ5, "SPI5_CS0_PQ5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO152_PQ6, "SOC_GPIO152_PQ6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO153_PQ7, "SOC_GPIO153_PQ7"), + PINCTRL_PIN(TEGRA_PIN_AUD_MCLK_PR0, "AUD_MCLK_PR0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO155_PR1, "SOC_GPIO155_PR1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PR2, "DAP1_SCLK_PR2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_OUT_PR3, "DAP1_OUT_PR3"), + PINCTRL_PIN(TEGRA_PIN_DAP1_IN_PR4, "DAP1_IN_PR4"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PR5, "DAP1_FS_PR5"), + PINCTRL_PIN(TEGRA_PIN_GEN11_I2C_SCL_PR6, "GEN11_I2C_SCL_PR6"), + PINCTRL_PIN(TEGRA_PIN_GEN11_I2C_SDA_PR7, "GEN11_I2C_SDA_PR7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO350_PS0, "SOC_GPIO350_PS0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO351_PS1, "SOC_GPIO351_PS1"), + +}; + +static const struct pinctrl_pin_desc tegra264_aon_pins[] = { + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO00_PAA0, "SOC_GPIO00_PAA0"), + PINCTRL_PIN(TEGRA_PIN_VCOMP_ALERT_PAA1, "VCOMP_ALERT_PAA1"), + PINCTRL_PIN(TEGRA_PIN_AO_RETENTION_N_PAA2, "AO_RETENTION_N_PAA2"), + PINCTRL_PIN(TEGRA_PIN_BATT_OC_PAA3, "BATT_OC_PAA3"), + PINCTRL_PIN(TEGRA_PIN_BOOTV_CTL_N_PAA4, "BOOTV_CTL_N_PAA4"), + PINCTRL_PIN(TEGRA_PIN_POWER_ON_PAA5, "POWER_ON_PAA5"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PAA6, "HDMI_CEC_PAA6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO07_PAA7, "SOC_GPIO07_PAA7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO08_PBB0, "SOC_GPIO08_PBB0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO09_PBB1, "SOC_GPIO09_PBB1"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PCC0, "GEN2_I2C_SCL_PCC0"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PCC1, "GEN2_I2C_SDA_PCC1"), + PINCTRL_PIN(TEGRA_PIN_GEN3_I2C_SCL_PCC2, "GEN3_I2C_SCL_PCC2"), + PINCTRL_PIN(TEGRA_PIN_GEN3_I2C_SDA_PCC3, "GEN3_I2C_SDA_PCC3"), + PINCTRL_PIN(TEGRA_PIN_GP_PWM4_PCC4, "GP_PWM4_PCC4"), + PINCTRL_PIN(TEGRA_PIN_UART0_TX_PCC5, "UART0_TX_PCC5"), + PINCTRL_PIN(TEGRA_PIN_UART0_RX_PCC6, "UART0_RX_PCC6"), + PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PCC7, "SPI2_SCK_PCC7"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PDD0, "SPI2_MISO_PDD0"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PDD1, "SPI2_MOSI_PDD1"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_N_PDD2, "SPI2_CS0_N_PDD2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO21_PDD3, "SOC_GPIO21_PDD3"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO22_PDD4, "SOC_GPIO22_PDD4"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO23_PDD5, "SOC_GPIO23_PDD5"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO24_PDD6, "SOC_GPIO24_PDD6"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO25_PDD7, "SOC_GPIO25_PDD7"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO26_PEE0, "SOC_GPIO26_PEE0"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO27_PEE1, "SOC_GPIO27_PEE1"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO28_PEE2, "SOC_GPIO28_PEE2"), + PINCTRL_PIN(TEGRA_PIN_SOC_GPIO29_PEE3, "SOC_GPIO29_PEE3"), +}; + +static const unsigned int soc_gpio250_pf0_pins[] = { + TEGRA_PIN_SOC_GPIO250_PF0, +}; + +static const unsigned int soc_gpio251_pf1_pins[] = { + TEGRA_PIN_SOC_GPIO251_PF1, +}; + +static const unsigned int soc_gpio252_pf2_pins[] = { + TEGRA_PIN_SOC_GPIO252_PF2, +}; + +static const unsigned int dp_aux_ch0_hpd_pf3_pins[] = { + TEGRA_PIN_DP_AUX_CH0_HPD_PF3, +}; + +static const unsigned int dp_aux_ch1_hpd_pf4_pins[] = { + TEGRA_PIN_DP_AUX_CH1_HPD_PF4, +}; + +static const unsigned int dp_aux_ch2_hpd_pf5_pins[] = { + TEGRA_PIN_DP_AUX_CH2_HPD_PF5, +}; + +static const unsigned int dp_aux_ch3_hpd_pf6_pins[] = { + TEGRA_PIN_DP_AUX_CH3_HPD_PF6, +}; + +static const unsigned int pwm2_pf7_pins[] = { + TEGRA_PIN_PWM2_PF7, +}; + +static const unsigned int pwm3_pg0_pins[] = { + TEGRA_PIN_PWM3_PG0, +}; + +static const unsigned int gen7_i2c_scl_pg1_pins[] = { + TEGRA_PIN_GEN7_I2C_SCL_PG1, +}; + +static const unsigned int gen7_i2c_sda_pg2_pins[] = { + TEGRA_PIN_GEN7_I2C_SDA_PG2, +}; + +static const unsigned int gen9_i2c_scl_pg3_pins[] = { + TEGRA_PIN_GEN9_I2C_SCL_PG3, +}; + +static const unsigned int gen9_i2c_sda_pg4_pins[] = { + TEGRA_PIN_GEN9_I2C_SDA_PG4, +}; + +static const unsigned int pwm1_pa0_pins[] = { + TEGRA_PIN_PWM1_PA0, +}; + +static const unsigned int pwm6_pa1_pins[] = { + TEGRA_PIN_PWM6_PA1, +}; + +static const unsigned int pwm7_pa2_pins[] = { + TEGRA_PIN_PWM7_PA2, +}; + +static const unsigned int pwm8_pa3_pins[] = { + TEGRA_PIN_PWM8_PA3, +}; + +static const unsigned int ufs0_ref_clk_pa4_pins[] = { + TEGRA_PIN_UFS0_REF_CLK_PA4, +}; + +static const unsigned int ufs0_rst_n_pa5_pins[] = { + TEGRA_PIN_UFS0_RST_N_PA5, +}; + +static const unsigned int pex_l1_clkreq_n_pb0_pins[] = { + TEGRA_PIN_PEX_L1_CLKREQ_N_PB0, +}; + +static const unsigned int pex_l1_rst_n_pb1_pins[] = { + TEGRA_PIN_PEX_L1_RST_N_PB1, +}; + +static const unsigned int pex_l2_clkreq_n_pb2_pins[] = { + TEGRA_PIN_PEX_L2_CLKREQ_N_PB2, +}; + +static const unsigned int pex_l2_rst_n_pb3_pins[] = { + TEGRA_PIN_PEX_L2_RST_N_PB3, +}; + +static const unsigned int pex_l3_clkreq_n_pb4_pins[] = { + TEGRA_PIN_PEX_L3_CLKREQ_N_PB4, +}; + +static const unsigned int pex_l3_rst_n_pb5_pins[] = { + TEGRA_PIN_PEX_L3_RST_N_PB5, +}; + +static const unsigned int soc_gpio113_pb6_pins[] = { + TEGRA_PIN_SOC_GPIO113_PB6, +}; + +static const unsigned int soc_gpio114_pb7_pins[] = { + TEGRA_PIN_SOC_GPIO114_PB7, +}; + +static const unsigned int sgmii0_sma_mdio_pc0_pins[] = { + TEGRA_PIN_SGMII0_SMA_MDIO_PC0, +}; + +static const unsigned int sgmii0_sma_mdc_pc1_pins[] = { + TEGRA_PIN_SGMII0_SMA_MDC_PC1, +}; + +static const unsigned int pex_wake_n_pc2_pins[] = { + TEGRA_PIN_PEX_WAKE_N_PC2, +}; + +static const unsigned int pex_l4_clkreq_n_pd0_pins[] = { + TEGRA_PIN_PEX_L4_CLKREQ_N_PD0, +}; + +static const unsigned int pex_l4_rst_n_pd1_pins[] = { + TEGRA_PIN_PEX_L4_RST_N_PD1, +}; + +static const unsigned int pex_l5_clkreq_n_pd2_pins[] = { + TEGRA_PIN_PEX_L5_CLKREQ_N_PD2, +}; + +static const unsigned int pex_l5_rst_n_pd3_pins[] = { + TEGRA_PIN_PEX_L5_RST_N_PD3, +}; + +static const unsigned int eth0_mdio_pd4_pins[] = { + TEGRA_PIN_ETH0_MDIO_PD4, +}; + +static const unsigned int eth0_mdc_pd5_pins[] = { + TEGRA_PIN_ETH0_MDC_PD5, +}; + +static const unsigned int eth3_mdio_pd6_pins[] = { + TEGRA_PIN_ETH3_MDIO_PD6, +}; + +static const unsigned int eth3_mdc_pd7_pins[] = { + TEGRA_PIN_ETH3_MDC_PD7, +}; + +static const unsigned int eth1_mdio_pe0_pins[] = { + TEGRA_PIN_ETH1_MDIO_PE0, +}; + +static const unsigned int eth1_mdc_pe1_pins[] = { + TEGRA_PIN_ETH1_MDC_PE1, +}; + +static const unsigned int eth2_mdio_pe2_pins[] = { + TEGRA_PIN_ETH2_MDIO_PE2, +}; + +static const unsigned int eth2_mdc_pe3_pins[] = { + TEGRA_PIN_ETH2_MDC_PE3, +}; + +static const unsigned int sdmmc1_clk_px0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PX0, +}; + +static const unsigned int sdmmc1_cmd_px1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PX1, +}; + +static const unsigned int sdmmc1_dat0_px2_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PX2, +}; + +static const unsigned int sdmmc1_dat1_px3_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PX3, +}; + +static const unsigned int sdmmc1_dat2_px4_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PX4, +}; + +static const unsigned int sdmmc1_dat3_px5_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PX5, +}; + +static const unsigned int sdmmc1_comp_pins[] = { + TEGRA_PIN_SDMMC1_COMP, +}; + +static const unsigned int cpu_pwr_req_ph0_pins[] = { + TEGRA_PIN_CPU_PWR_REQ_PH0, +}; + +static const unsigned int gpu_pwr_req_ph1_pins[] = { + TEGRA_PIN_GPU_PWR_REQ_PH1, +}; + +static const unsigned int uart10_tx_ph2_pins[] = { + TEGRA_PIN_UART10_TX_PH2, +}; + +static const unsigned int uart10_rx_ph3_pins[] = { + TEGRA_PIN_UART10_RX_PH3, +}; + +static const unsigned int uart10_rts_n_ph4_pins[] = { + TEGRA_PIN_UART10_RTS_N_PH4, +}; + +static const unsigned int uart10_cts_n_ph5_pins[] = { + TEGRA_PIN_UART10_CTS_N_PH5, +}; + +static const unsigned int spi3_sck_ph6_pins[] = { + TEGRA_PIN_SPI3_SCK_PH6, +}; + +static const unsigned int spi3_miso_ph7_pins[] = { + TEGRA_PIN_SPI3_MISO_PH7, +}; + +static const unsigned int spi3_mosi_pj0_pins[] = { + TEGRA_PIN_SPI3_MOSI_PJ0, +}; + +static const unsigned int spi3_cs0_pj1_pins[] = { + TEGRA_PIN_SPI3_CS0_PJ1, +}; + +static const unsigned int spi3_cs3_pj2_pins[] = { + TEGRA_PIN_SPI3_CS3_PJ2, +}; + +static const unsigned int uart5_tx_pj3_pins[] = { + TEGRA_PIN_UART5_TX_PJ3, +}; + +static const unsigned int uart5_rx_pj4_pins[] = { + TEGRA_PIN_UART5_RX_PJ4, +}; + +static const unsigned int uart5_rts_n_pj5_pins[] = { + TEGRA_PIN_UART5_RTS_N_PJ5, +}; + +static const unsigned int uart5_cts_n_pj6_pins[] = { + TEGRA_PIN_UART5_CTS_N_PJ6, +}; + +static const unsigned int spi1_sck_pj7_pins[] = { + TEGRA_PIN_SPI1_SCK_PJ7, +}; + +static const unsigned int spi1_miso_pk0_pins[] = { + TEGRA_PIN_SPI1_MISO_PK0, +}; + +static const unsigned int spi1_mosi_pk1_pins[] = { + TEGRA_PIN_SPI1_MOSI_PK1, +}; + +static const unsigned int spi1_cs0_pk2_pins[] = { + TEGRA_PIN_SPI1_CS0_PK2, +}; + +static const unsigned int spi1_cs1_pk3_pins[] = { + TEGRA_PIN_SPI1_CS1_PK3, +}; + +static const unsigned int extperiph1_clk_pk4_pins[] = { + TEGRA_PIN_EXTPERIPH1_CLK_PK4, +}; + +static const unsigned int extperiph2_clk_pk5_pins[] = { + TEGRA_PIN_EXTPERIPH2_CLK_PK5, +}; + +static const unsigned int gen12_i2c_scl_pk6_pins[] = { + TEGRA_PIN_GEN12_I2C_SCL_PK6, +}; + +static const unsigned int gen12_i2c_sda_pk7_pins[] = { + TEGRA_PIN_GEN12_I2C_SDA_PK7, +}; + +static const unsigned int soc_gpio124_pl0_pins[] = { + TEGRA_PIN_SOC_GPIO124_PL0, +}; + +static const unsigned int soc_gpio125_pl1_pins[] = { + TEGRA_PIN_SOC_GPIO125_PL1, +}; + +static const unsigned int fan_tach0_pl2_pins[] = { + TEGRA_PIN_FAN_TACH0_PL2, +}; + +static const unsigned int soc_gpio127_pl3_pins[] = { + TEGRA_PIN_SOC_GPIO127_PL3, +}; + +static const unsigned int soc_gpio128_pl4_pins[] = { + TEGRA_PIN_SOC_GPIO128_PL4, +}; + +static const unsigned int soc_gpio129_pl5_pins[] = { + TEGRA_PIN_SOC_GPIO129_PL5, +}; + +static const unsigned int soc_gpio130_pl6_pins[] = { + TEGRA_PIN_SOC_GPIO130_PL6, +}; + +static const unsigned int soc_gpio131_pl7_pins[] = { + TEGRA_PIN_SOC_GPIO131_PL7, +}; + +static const unsigned int gp_pwm9_pm0_pins[] = { + TEGRA_PIN_GP_PWM9_PM0, +}; + +static const unsigned int soc_gpio133_pm1_pins[] = { + TEGRA_PIN_SOC_GPIO133_PM1, +}; + +static const unsigned int uart9_tx_pm2_pins[] = { + TEGRA_PIN_UART9_TX_PM2, +}; + +static const unsigned int uart9_rx_pm3_pins[] = { + TEGRA_PIN_UART9_RX_PM3, +}; + +static const unsigned int uart9_rts_n_pm4_pins[] = { + TEGRA_PIN_UART9_RTS_N_PM4, +}; + +static const unsigned int uart9_cts_n_pm5_pins[] = { + TEGRA_PIN_UART9_CTS_N_PM5, +}; + +static const unsigned int soc_gpio170_pu0_pins[] = { + TEGRA_PIN_SOC_GPIO170_PU0, +}; + +static const unsigned int soc_gpio171_pu1_pins[] = { + TEGRA_PIN_SOC_GPIO171_PU1, +}; + +static const unsigned int soc_gpio172_pu2_pins[] = { + TEGRA_PIN_SOC_GPIO172_PU2, +}; + +static const unsigned int soc_gpio173_pu3_pins[] = { + TEGRA_PIN_SOC_GPIO173_PU3, +}; + +static const unsigned int soc_gpio174_pu4_pins[] = { + TEGRA_PIN_SOC_GPIO174_PU4, +}; + +static const unsigned int soc_gpio175_pu5_pins[] = { + TEGRA_PIN_SOC_GPIO175_PU5, +}; + +static const unsigned int soc_gpio176_pu6_pins[] = { + TEGRA_PIN_SOC_GPIO176_PU6, +}; + +static const unsigned int soc_gpio177_pu7_pins[] = { + TEGRA_PIN_SOC_GPIO177_PU7, +}; + +static const unsigned int soc_gpio178_pv0_pins[] = { + TEGRA_PIN_SOC_GPIO178_PV0, +}; + +static const unsigned int pwm10_pv1_pins[] = { + TEGRA_PIN_PWM10_PV1, +}; + +static const unsigned int uart4_tx_pv2_pins[] = { + TEGRA_PIN_UART4_TX_PV2, +}; + +static const unsigned int uart4_rx_pv3_pins[] = { + TEGRA_PIN_UART4_RX_PV3, +}; + +static const unsigned int uart4_rts_n_pv4_pins[] = { + TEGRA_PIN_UART4_RTS_N_PV4, +}; + +static const unsigned int uart4_cts_n_pv5_pins[] = { + TEGRA_PIN_UART4_CTS_N_PV5, +}; + +static const unsigned int dap2_clk_pv6_pins[] = { + TEGRA_PIN_DAP2_CLK_PV6, +}; + +static const unsigned int dap2_din_pv7_pins[] = { + TEGRA_PIN_DAP2_DIN_PV7, +}; + +static const unsigned int dap2_dout_pw0_pins[] = { + TEGRA_PIN_DAP2_DOUT_PW0, +}; + +static const unsigned int dap2_fs_pw1_pins[] = { + TEGRA_PIN_DAP2_FS_PW1, +}; + +static const unsigned int gen1_i2c_scl_pw2_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PW2, +}; + +static const unsigned int gen1_i2c_sda_pw3_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PW3, +}; + +static const unsigned int gen0_i2c_scl_pw4_pins[] = { + TEGRA_PIN_GEN0_I2C_SCL_PW4, +}; + +static const unsigned int gen0_i2c_sda_pw5_pins[] = { + TEGRA_PIN_GEN0_I2C_SDA_PW5, +}; + +static const unsigned int pwr_i2c_scl_pw6_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PW6, +}; + +static const unsigned int pwr_i2c_sda_pw7_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PW7, +}; + +static const unsigned int qspi0_sck_pt0_pins[] = { + TEGRA_PIN_QSPI0_SCK_PT0, +}; + +static const unsigned int qspi0_cs_n_pt1_pins[] = { + TEGRA_PIN_QSPI0_CS_N_PT1, +}; + +static const unsigned int qspi0_io0_pt2_pins[] = { + TEGRA_PIN_QSPI0_IO0_PT2, +}; + +static const unsigned int qspi0_io1_pt3_pins[] = { + TEGRA_PIN_QSPI0_IO1_PT3, +}; + +static const unsigned int qspi0_io2_pt4_pins[] = { + TEGRA_PIN_QSPI0_IO2_PT4, +}; + +static const unsigned int qspi0_io3_pt5_pins[] = { + TEGRA_PIN_QSPI0_IO3_PT5, +}; + +static const unsigned int soc_gpio192_pt6_pins[] = { + TEGRA_PIN_SOC_GPIO192_PT6, +}; + +static const unsigned int soc_gpio138_pp0_pins[] = { + TEGRA_PIN_SOC_GPIO138_PP0, +}; + +static const unsigned int soc_gpio139_pp1_pins[] = { + TEGRA_PIN_SOC_GPIO139_PP1, +}; + +static const unsigned int dap6_sclk_pp2_pins[] = { + TEGRA_PIN_DAP6_SCLK_PP2, +}; + +static const unsigned int dap6_dout_pp3_pins[] = { + TEGRA_PIN_DAP6_DOUT_PP3, +}; + +static const unsigned int dap6_din_pp4_pins[] = { + TEGRA_PIN_DAP6_DIN_PP4, +}; + +static const unsigned int dap6_fs_pp5_pins[] = { + TEGRA_PIN_DAP6_FS_PP5, +}; + +static const unsigned int dap4_sclk_pp6_pins[] = { + TEGRA_PIN_DAP4_SCLK_PP6, +}; + +static const unsigned int dap4_dout_pp7_pins[] = { + TEGRA_PIN_DAP4_DOUT_PP7, +}; + +static const unsigned int dap4_din_pq0_pins[] = { + TEGRA_PIN_DAP4_DIN_PQ0, +}; + +static const unsigned int dap4_fs_pq1_pins[] = { + TEGRA_PIN_DAP4_FS_PQ1, +}; + +static const unsigned int spi5_sck_pq2_pins[] = { + TEGRA_PIN_SPI5_SCK_PQ2, +}; + +static const unsigned int spi5_miso_pq3_pins[] = { + TEGRA_PIN_SPI5_MISO_PQ3, +}; + +static const unsigned int spi5_mosi_pq4_pins[] = { + TEGRA_PIN_SPI5_MOSI_PQ4, +}; + +static const unsigned int spi5_cs0_pq5_pins[] = { + TEGRA_PIN_SPI5_CS0_PQ5, +}; + +static const unsigned int soc_gpio152_pq6_pins[] = { + TEGRA_PIN_SOC_GPIO152_PQ6, +}; + +static const unsigned int soc_gpio153_pq7_pins[] = { + TEGRA_PIN_SOC_GPIO153_PQ7, +}; + +static const unsigned int aud_mclk_pr0_pins[] = { + TEGRA_PIN_AUD_MCLK_PR0, +}; + +static const unsigned int soc_gpio155_pr1_pins[] = { + TEGRA_PIN_SOC_GPIO155_PR1, +}; + +static const unsigned int dap1_sclk_pr2_pins[] = { + TEGRA_PIN_DAP1_SCLK_PR2, +}; + +static const unsigned int dap1_out_pr3_pins[] = { + TEGRA_PIN_DAP1_OUT_PR3, +}; + +static const unsigned int dap1_in_pr4_pins[] = { + TEGRA_PIN_DAP1_IN_PR4, +}; + +static const unsigned int dap1_fs_pr5_pins[] = { + TEGRA_PIN_DAP1_FS_PR5, +}; + +static const unsigned int gen11_i2c_scl_pr6_pins[] = { + TEGRA_PIN_GEN11_I2C_SCL_PR6, +}; + +static const unsigned int gen11_i2c_sda_pr7_pins[] = { + TEGRA_PIN_GEN11_I2C_SDA_PR7, +}; + +static const unsigned int soc_gpio350_ps0_pins[] = { + TEGRA_PIN_SOC_GPIO350_PS0, +}; + +static const unsigned int soc_gpio351_ps1_pins[] = { + TEGRA_PIN_SOC_GPIO351_PS1, +}; + +static const unsigned int soc_gpio270_py0_pins[] = { + TEGRA_PIN_SOC_GPIO270_PY0, +}; + +static const unsigned int soc_gpio271_py1_pins[] = { + TEGRA_PIN_SOC_GPIO271_PY1, +}; + +static const unsigned int soc_gpio272_py2_pins[] = { + TEGRA_PIN_SOC_GPIO272_PY2, +}; + +static const unsigned int soc_gpio273_py3_pins[] = { + TEGRA_PIN_SOC_GPIO273_PY3, +}; + +static const unsigned int soc_gpio274_py4_pins[] = { + TEGRA_PIN_SOC_GPIO274_PY4, +}; + +static const unsigned int soc_gpio275_py5_pins[] = { + TEGRA_PIN_SOC_GPIO275_PY5, +}; + +static const unsigned int soc_gpio276_py6_pins[] = { + TEGRA_PIN_SOC_GPIO276_PY6, +}; + +static const unsigned int soc_gpio277_py7_pins[] = { + TEGRA_PIN_SOC_GPIO277_PY7, +}; + +static const unsigned int soc_gpio278_pz0_pins[] = { + TEGRA_PIN_SOC_GPIO278_PZ0, +}; + +static const unsigned int soc_gpio279_pz1_pins[] = { + TEGRA_PIN_SOC_GPIO279_PZ1, +}; + +static const unsigned int xhalt_trig_pz2_pins[] = { + TEGRA_PIN_XHALT_TRIG_PZ2, +}; + +static const unsigned int soc_gpio281_pz3_pins[] = { + TEGRA_PIN_SOC_GPIO281_PZ3, +}; + +static const unsigned int soc_gpio282_pz4_pins[] = { + TEGRA_PIN_SOC_GPIO282_PZ4, +}; + +static const unsigned int soc_gpio283_pz5_pins[] = { + TEGRA_PIN_SOC_GPIO283_PZ5, +}; + +static const unsigned int soc_gpio284_pz6_pins[] = { + TEGRA_PIN_SOC_GPIO284_PZ6, +}; + +static const unsigned int soc_gpio285_pz7_pins[] = { + TEGRA_PIN_SOC_GPIO285_PZ7, +}; + +static const unsigned int soc_gpio286_pal0_pins[] = { + TEGRA_PIN_SOC_GPIO286_PAL0, +}; + +static const unsigned int soc_gpio287_pal1_pins[] = { + TEGRA_PIN_SOC_GPIO287_PAL1, +}; + +static const unsigned int soc_gpio288_pal2_pins[] = { + TEGRA_PIN_SOC_GPIO288_PAL2, +}; + +static const unsigned int soc_gpio00_paa0_pins[] = { + TEGRA_PIN_SOC_GPIO00_PAA0, +}; + +static const unsigned int vcomp_alert_paa1_pins[] = { + TEGRA_PIN_VCOMP_ALERT_PAA1, +}; + +static const unsigned int ao_retention_n_paa2_pins[] = { + TEGRA_PIN_AO_RETENTION_N_PAA2, +}; + +static const unsigned int batt_oc_paa3_pins[] = { + TEGRA_PIN_BATT_OC_PAA3, +}; + +static const unsigned int bootv_ctl_n_paa4_pins[] = { + TEGRA_PIN_BOOTV_CTL_N_PAA4, +}; + +static const unsigned int power_on_paa5_pins[] = { + TEGRA_PIN_POWER_ON_PAA5, +}; + +static const unsigned int hdmi_cec_paa6_pins[] = { + TEGRA_PIN_HDMI_CEC_PAA6, +}; + +static const unsigned int soc_gpio07_paa7_pins[] = { + TEGRA_PIN_SOC_GPIO07_PAA7, +}; + +static const unsigned int soc_gpio08_pbb0_pins[] = { + TEGRA_PIN_SOC_GPIO08_PBB0, +}; + +static const unsigned int soc_gpio09_pbb1_pins[] = { + TEGRA_PIN_SOC_GPIO09_PBB1, +}; + +static const unsigned int gen2_i2c_scl_pcc0_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PCC0, +}; + +static const unsigned int gen2_i2c_sda_pcc1_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PCC1, +}; + +static const unsigned int gen3_i2c_scl_pcc2_pins[] = { + TEGRA_PIN_GEN3_I2C_SCL_PCC2, +}; + +static const unsigned int gen3_i2c_sda_pcc3_pins[] = { + TEGRA_PIN_GEN3_I2C_SDA_PCC3, +}; + +static const unsigned int gp_pwm4_pcc4_pins[] = { + TEGRA_PIN_GP_PWM4_PCC4, +}; + +static const unsigned int uart0_tx_pcc5_pins[] = { + TEGRA_PIN_UART0_TX_PCC5, +}; + +static const unsigned int uart0_rx_pcc6_pins[] = { + TEGRA_PIN_UART0_RX_PCC6, +}; + +static const unsigned int spi2_sck_pcc7_pins[] = { + TEGRA_PIN_SPI2_SCK_PCC7, +}; + +static const unsigned int spi2_miso_pdd0_pins[] = { + TEGRA_PIN_SPI2_MISO_PDD0, +}; + +static const unsigned int spi2_mosi_pdd1_pins[] = { + TEGRA_PIN_SPI2_MOSI_PDD1, +}; + +static const unsigned int spi2_cs0_n_pdd2_pins[] = { + TEGRA_PIN_SPI2_CS0_N_PDD2, +}; + +static const unsigned int soc_gpio21_pdd3_pins[] = { + TEGRA_PIN_SOC_GPIO21_PDD3, +}; + +static const unsigned int soc_gpio22_pdd4_pins[] = { + TEGRA_PIN_SOC_GPIO22_PDD4, +}; + +static const unsigned int soc_gpio23_pdd5_pins[] = { + TEGRA_PIN_SOC_GPIO23_PDD5, +}; + +static const unsigned int soc_gpio24_pdd6_pins[] = { + TEGRA_PIN_SOC_GPIO24_PDD6, +}; + +static const unsigned int soc_gpio25_pdd7_pins[] = { + TEGRA_PIN_SOC_GPIO25_PDD7, +}; + +static const unsigned int soc_gpio26_pee0_pins[] = { + TEGRA_PIN_SOC_GPIO26_PEE0, +}; + +static const unsigned int soc_gpio27_pee1_pins[] = { + TEGRA_PIN_SOC_GPIO27_PEE1, +}; + +static const unsigned int soc_gpio28_pee2_pins[] = { + TEGRA_PIN_SOC_GPIO28_PEE2, +}; + +static const unsigned int soc_gpio29_pee3_pins[] = { + TEGRA_PIN_SOC_GPIO29_PEE3, +}; + +enum tegra_mux_dt { + TEGRA_MUX_DCA_VSYNC, + TEGRA_MUX_DCA_HSYNC, + TEGRA_MUX_RSVD0, + TEGRA_MUX_DP_AUX_CH0_HPD, + TEGRA_MUX_DP_AUX_CH1_HPD, + TEGRA_MUX_DP_AUX_CH2_HPD, + TEGRA_MUX_DP_AUX_CH3_HPD, + TEGRA_MUX_GP_PWM2, + TEGRA_MUX_GP_PWM3, + TEGRA_MUX_I2C7_CLK, + TEGRA_MUX_I2C7_DAT, + TEGRA_MUX_I2C9_CLK, + TEGRA_MUX_I2C9_DAT, + TEGRA_MUX_UARTK_CTS, + TEGRA_MUX_UARTK_RTS, + TEGRA_MUX_UARTK_RXD, + TEGRA_MUX_UARTK_TXD, + TEGRA_MUX_SPI3_CS0, + TEGRA_MUX_SPI3_CS3, + TEGRA_MUX_SPI3_DIN, + TEGRA_MUX_SPI3_DOUT, + TEGRA_MUX_SPI3_SCK, + TEGRA_MUX_UARTF_CTS, + TEGRA_MUX_UARTF_RTS, + TEGRA_MUX_UARTF_RXD, + TEGRA_MUX_UARTF_TXD, + TEGRA_MUX_SPI1_CS0, + TEGRA_MUX_SPI1_CS1, + TEGRA_MUX_SPI1_DIN, + TEGRA_MUX_SPI1_DOUT, + TEGRA_MUX_SPI1_SCK, + TEGRA_MUX_EXTPERIPH2_CLK, + TEGRA_MUX_EXTPERIPH1_CLK, + TEGRA_MUX_I2C12_CLK, + TEGRA_MUX_I2C12_DAT, + TEGRA_MUX_NV_THERM_FAN_TACH0, + TEGRA_MUX_GP_PWM9, + TEGRA_MUX_UARTJ_CTS, + TEGRA_MUX_UARTJ_RTS, + TEGRA_MUX_UARTJ_RXD, + TEGRA_MUX_UARTJ_TXD, + TEGRA_MUX_I2C0_CLK, + TEGRA_MUX_I2C0_DAT, + TEGRA_MUX_I2C1_CLK, + TEGRA_MUX_I2C1_DAT, + TEGRA_MUX_I2S2_LRCK, + TEGRA_MUX_I2S2_SCLK, + TEGRA_MUX_I2S2_SDATA_OUT, + TEGRA_MUX_I2S2_SDATA_IN, + TEGRA_MUX_GP_PWM10, + TEGRA_MUX_UARTE_CTS, + TEGRA_MUX_UARTE_RTS, + TEGRA_MUX_UARTE_RXD, + TEGRA_MUX_UARTE_TXD, + TEGRA_MUX_I2C5_DAT, + TEGRA_MUX_I2C5_CLK, + TEGRA_MUX_I2S6_SDATA_IN, + TEGRA_MUX_I2S6_SDATA_OUT, + TEGRA_MUX_I2S6_LRCK, + TEGRA_MUX_I2S6_SCLK, + TEGRA_MUX_I2S4_SDATA_OUT, + TEGRA_MUX_I2S4_SCLK, + TEGRA_MUX_I2S4_SDATA_IN, + TEGRA_MUX_I2S4_LRCK, + TEGRA_MUX_SPI5_CS0, + TEGRA_MUX_SPI5_DIN, + TEGRA_MUX_SPI5_DOUT, + TEGRA_MUX_SPI5_SCK, + TEGRA_MUX_AUD_MCLK, + TEGRA_MUX_I2S1_SCLK, + TEGRA_MUX_I2S1_SDATA_IN, + TEGRA_MUX_I2S1_SDATA_OUT, + TEGRA_MUX_I2S1_LRCK, + TEGRA_MUX_I2C11_CLK, + TEGRA_MUX_I2C11_DAT, + TEGRA_MUX_XHALT_TRIG, + TEGRA_MUX_GP_PWM1, + TEGRA_MUX_GP_PWM6, + TEGRA_MUX_GP_PWM7, + TEGRA_MUX_GP_PWM8, + TEGRA_MUX_UFS0, + TEGRA_MUX_PE1_CLKREQ_L, + TEGRA_MUX_PE1_RST_L, + TEGRA_MUX_PE2_RST_L, + TEGRA_MUX_PE2_CLKREQ_L, + TEGRA_MUX_PE3_CLKREQ_L, + TEGRA_MUX_PE3_RST_L, + TEGRA_MUX_SGMII0_SMA_MDIO, + TEGRA_MUX_SGMII0_SMA_MDC, + TEGRA_MUX_USB_VBUS_EN0, + TEGRA_MUX_USB_VBUS_EN1, + TEGRA_MUX_ETH1_MDIO, + TEGRA_MUX_PE4_CLKREQ_L, + TEGRA_MUX_PE4_RST_L, + TEGRA_MUX_PE5_CLKREQ_L, + TEGRA_MUX_PE5_RST_L, + TEGRA_MUX_ETH0_MDIO, + TEGRA_MUX_ETH0_MDC, + TEGRA_MUX_ETH1_MDC, + TEGRA_MUX_ETH2_MDIO, + TEGRA_MUX_ETH2_MDC, + TEGRA_MUX_ETH3_MDIO, + TEGRA_MUX_ETH3_MDC, + TEGRA_MUX_QSPI0_CS_N, + TEGRA_MUX_QSPI0_IO0, + TEGRA_MUX_QSPI0_IO1, + TEGRA_MUX_QSPI0_IO2, + TEGRA_MUX_QSPI0_IO3, + TEGRA_MUX_QSPI0_SCK, + TEGRA_MUX_SDMMC1_CLK, + TEGRA_MUX_SDMMC1_CMD, + TEGRA_MUX_SDMMC1_COMP, + TEGRA_MUX_SDMMC1_DAT3, + TEGRA_MUX_SDMMC1_DAT2, + TEGRA_MUX_SDMMC1_DAT1, + TEGRA_MUX_SDMMC1_DAT0, + TEGRA_MUX_QSPI3_SCK, + TEGRA_MUX_QSPI3_CS0, + TEGRA_MUX_QSPI3_IO0, + TEGRA_MUX_QSPI3_IO1, + TEGRA_MUX_DCB_VSYNC, + TEGRA_MUX_DCB_HSYNC, + TEGRA_MUX_DSA_LSPII, + TEGRA_MUX_DCE_VSYNC, + TEGRA_MUX_DCE_HSYNC, + TEGRA_MUX_DCH_VSYNC, + TEGRA_MUX_DCH_HSYNC, + TEGRA_MUX_BL_EN, + TEGRA_MUX_BL_PWM_DIM0, + TEGRA_MUX_RSVD1, + TEGRA_MUX_SOC_THERM_OC3, + TEGRA_MUX_I2S5_SCLK, + TEGRA_MUX_I2S5_SDATA_IN, + TEGRA_MUX_EXTPERIPH3_CLK, + TEGRA_MUX_EXTPERIPH4_CLK, + TEGRA_MUX_I2S5_SDATA_OUT, + TEGRA_MUX_I2S5_LRCK, + TEGRA_MUX_SDMMC1_CD, + TEGRA_MUX_I2S7_SDATA_IN, + TEGRA_MUX_SPI4_SCK, + TEGRA_MUX_SPI4_DIN, + TEGRA_MUX_SPI4_DOUT, + TEGRA_MUX_SPI4_CS0, + TEGRA_MUX_SPI4_CS1, + TEGRA_MUX_GP_PWM5, + TEGRA_MUX_I2C14_CLK, + TEGRA_MUX_I2C14_DAT, + TEGRA_MUX_I2S8_SCLK, + TEGRA_MUX_I2S8_SDATA_OUT, + TEGRA_MUX_I2S8_LRCK, + TEGRA_MUX_I2S8_SDATA_IN, + TEGRA_MUX_I2C16_CLK, + TEGRA_MUX_I2C16_DAT, + TEGRA_MUX_I2S3_SCLK, + TEGRA_MUX_I2S3_SDATA_OUT, + TEGRA_MUX_I2S3_SDATA_IN, + TEGRA_MUX_I2S3_LRCK, + TEGRA_MUX_PM_TRIG1, + TEGRA_MUX_PM_TRIG0, + TEGRA_MUX_QSPI2_SCK, + TEGRA_MUX_QSPI2_CS0, + TEGRA_MUX_QSPI2_IO0, + TEGRA_MUX_QSPI2_IO1, + TEGRA_MUX_DCC_VSYNC, + TEGRA_MUX_DCC_HSYNC, + TEGRA_MUX_RSVD2, + TEGRA_MUX_DCF_VSYNC, + TEGRA_MUX_DCF_HSYNC, + TEGRA_MUX_SOUNDWIRE1_CLK, + TEGRA_MUX_SOUNDWIRE1_DAT0, + TEGRA_MUX_SOUNDWIRE1_DAT1, + TEGRA_MUX_SOUNDWIRE1_DAT2, + TEGRA_MUX_DMIC2_CLK, + TEGRA_MUX_DMIC2_DAT, + TEGRA_MUX_NV_THERM_FAN_TACH1, + TEGRA_MUX_I2C15_CLK, + TEGRA_MUX_I2C15_DAT, + TEGRA_MUX_I2S7_LRCK, + TEGRA_MUX_CCLA_LA_TRIGGER_MUX, + TEGRA_MUX_I2S7_SCLK, + TEGRA_MUX_I2S7_SDATA_OUT, + TEGRA_MUX_DMIC1_DAT, + TEGRA_MUX_DMIC1_CLK, + TEGRA_MUX_DCD_VSYNC, + TEGRA_MUX_DCD_HSYNC, + TEGRA_MUX_RSVD3, + TEGRA_MUX_DCG_VSYNC, + TEGRA_MUX_DCG_HSYNC, + TEGRA_MUX_DSPK1_CLK, + TEGRA_MUX_DSPK1_DAT, + TEGRA_MUX_SOC_THERM_OC2, + TEGRA_MUX_ISTCTRL_IST_DONE_N, + TEGRA_MUX_SOC_THERM_OC1, + TEGRA_MUX_TSC_EDGE_OUT0C, + TEGRA_MUX_TSC_EDGE_OUT0D, + TEGRA_MUX_TSC_EDGE_OUT0A, + TEGRA_MUX_TSC_EDGE_OUT0B, + TEGRA_MUX_TOUCH_CLK, + TEGRA_MUX_HDMI_CEC, + TEGRA_MUX_I2C2_CLK, + TEGRA_MUX_I2C2_DAT, + TEGRA_MUX_I2C3_CLK, + TEGRA_MUX_I2C3_DAT, + TEGRA_MUX_GP_PWM4, + TEGRA_MUX_UARTA_TXD, + TEGRA_MUX_UARTA_RXD, + TEGRA_MUX_SPI2_SCK, + TEGRA_MUX_SPI2_DIN, + TEGRA_MUX_SPI2_DOUT, + TEGRA_MUX_SPI2_CS0, + TEGRA_MUX_TSC_SYNC1, + TEGRA_MUX_TSC_EDGE_OUT3, + TEGRA_MUX_TSC_EDGE_OUT0, + TEGRA_MUX_TSC_EDGE_OUT1, + TEGRA_MUX_TSC_SYNC0, + TEGRA_MUX_SOUNDWIRE0_CLK, + TEGRA_MUX_SOUNDWIRE0_DAT0, + TEGRA_MUX_L0L1_RST_OUT_N, + TEGRA_MUX_L2_RST_OUT_N, + TEGRA_MUX_UARTL_TXD, + TEGRA_MUX_UARTL_RXD, + TEGRA_MUX_I2S9_SCLK, + TEGRA_MUX_I2S9_SDATA_OUT, + TEGRA_MUX_I2S9_SDATA_IN, + TEGRA_MUX_I2S9_LRCK, + TEGRA_MUX_DMIC5_DAT, + TEGRA_MUX_DMIC5_CLK, + TEGRA_MUX_TSC_EDGE_OUT2, +}; + +/* Make list of each function name */ +#define TEGRA_PIN_FUNCTION(lid) #lid + +static const char * const tegra264_functions[] = { + TEGRA_PIN_FUNCTION(dca_vsync), + TEGRA_PIN_FUNCTION(dca_hsync), + TEGRA_PIN_FUNCTION(rsvd0), + TEGRA_PIN_FUNCTION(dp_aux_ch0_hpd), + TEGRA_PIN_FUNCTION(dp_aux_ch1_hpd), + TEGRA_PIN_FUNCTION(dp_aux_ch2_hpd), + TEGRA_PIN_FUNCTION(dp_aux_ch3_hpd), + TEGRA_PIN_FUNCTION(gp_pwm2), + TEGRA_PIN_FUNCTION(gp_pwm3), + TEGRA_PIN_FUNCTION(i2c7_clk), + TEGRA_PIN_FUNCTION(i2c7_dat), + TEGRA_PIN_FUNCTION(i2c9_clk), + TEGRA_PIN_FUNCTION(i2c9_dat), + TEGRA_PIN_FUNCTION(uartk_cts), + TEGRA_PIN_FUNCTION(uartk_rts), + TEGRA_PIN_FUNCTION(uartk_rxd), + TEGRA_PIN_FUNCTION(uartk_txd), + TEGRA_PIN_FUNCTION(spi3_cs0), + TEGRA_PIN_FUNCTION(spi3_cs3), + TEGRA_PIN_FUNCTION(spi3_din), + TEGRA_PIN_FUNCTION(spi3_dout), + TEGRA_PIN_FUNCTION(spi3_sck), + TEGRA_PIN_FUNCTION(uartf_cts), + TEGRA_PIN_FUNCTION(uartf_rts), + TEGRA_PIN_FUNCTION(uartf_rxd), + TEGRA_PIN_FUNCTION(uartf_txd), + TEGRA_PIN_FUNCTION(spi1_cs0), + TEGRA_PIN_FUNCTION(spi1_cs1), + TEGRA_PIN_FUNCTION(spi1_din), + TEGRA_PIN_FUNCTION(spi1_dout), + TEGRA_PIN_FUNCTION(spi1_sck), + TEGRA_PIN_FUNCTION(extperiph2_clk), + TEGRA_PIN_FUNCTION(extperiph1_clk), + TEGRA_PIN_FUNCTION(i2c12_clk), + TEGRA_PIN_FUNCTION(i2c12_dat), + TEGRA_PIN_FUNCTION(nv_therm_fan_tach0), + TEGRA_PIN_FUNCTION(gp_pwm9), + TEGRA_PIN_FUNCTION(uartj_cts), + TEGRA_PIN_FUNCTION(uartj_rts), + TEGRA_PIN_FUNCTION(uartj_rxd), + TEGRA_PIN_FUNCTION(uartj_txd), + TEGRA_PIN_FUNCTION(i2c0_clk), + TEGRA_PIN_FUNCTION(i2c0_dat), + TEGRA_PIN_FUNCTION(i2c1_clk), + TEGRA_PIN_FUNCTION(i2c1_dat), + TEGRA_PIN_FUNCTION(i2s2_lrck), + TEGRA_PIN_FUNCTION(i2s2_sclk), + TEGRA_PIN_FUNCTION(i2s2_sdata_out), + TEGRA_PIN_FUNCTION(i2s2_sdata_in), + TEGRA_PIN_FUNCTION(gp_pwm10), + TEGRA_PIN_FUNCTION(uarte_cts), + TEGRA_PIN_FUNCTION(uarte_rts), + TEGRA_PIN_FUNCTION(uarte_rxd), + TEGRA_PIN_FUNCTION(uarte_txd), + TEGRA_PIN_FUNCTION(i2c5_dat), + TEGRA_PIN_FUNCTION(i2c5_clk), + TEGRA_PIN_FUNCTION(i2s6_sdata_in), + TEGRA_PIN_FUNCTION(i2s6_sdata_out), + TEGRA_PIN_FUNCTION(i2s6_lrck), + TEGRA_PIN_FUNCTION(i2s6_sclk), + TEGRA_PIN_FUNCTION(i2s4_sdata_out), + TEGRA_PIN_FUNCTION(i2s4_sclk), + TEGRA_PIN_FUNCTION(i2s4_sdata_in), + TEGRA_PIN_FUNCTION(i2s4_lrck), + TEGRA_PIN_FUNCTION(spi5_cs0), + TEGRA_PIN_FUNCTION(spi5_din), + TEGRA_PIN_FUNCTION(spi5_dout), + TEGRA_PIN_FUNCTION(spi5_sck), + TEGRA_PIN_FUNCTION(aud_mclk), + TEGRA_PIN_FUNCTION(i2s1_sclk), + TEGRA_PIN_FUNCTION(i2s1_sdata_in), + TEGRA_PIN_FUNCTION(i2s1_sdata_out), + TEGRA_PIN_FUNCTION(i2s1_lrck), + TEGRA_PIN_FUNCTION(i2c11_clk), + TEGRA_PIN_FUNCTION(i2c11_dat), + TEGRA_PIN_FUNCTION(xhalt_trig), + TEGRA_PIN_FUNCTION(gp_pwm1), + TEGRA_PIN_FUNCTION(gp_pwm6), + TEGRA_PIN_FUNCTION(gp_pwm7), + TEGRA_PIN_FUNCTION(gp_pwm8), + TEGRA_PIN_FUNCTION(ufs0), + TEGRA_PIN_FUNCTION(pe1_clkreq_l), + TEGRA_PIN_FUNCTION(pe1_rst_l), + TEGRA_PIN_FUNCTION(pe2_rst_l), + TEGRA_PIN_FUNCTION(pe2_clkreq_l), + TEGRA_PIN_FUNCTION(pe3_clkreq_l), + TEGRA_PIN_FUNCTION(pe3_rst_l), + TEGRA_PIN_FUNCTION(sgmii0_sma_mdio), + TEGRA_PIN_FUNCTION(sgmii0_sma_mdc), + TEGRA_PIN_FUNCTION(usb_vbus_en0), + TEGRA_PIN_FUNCTION(usb_vbus_en1), + TEGRA_PIN_FUNCTION(eth1_mdio), + TEGRA_PIN_FUNCTION(pe4_clkreq_l), + TEGRA_PIN_FUNCTION(pe4_rst_l), + TEGRA_PIN_FUNCTION(pe5_clkreq_l), + TEGRA_PIN_FUNCTION(pe5_rst_l), + TEGRA_PIN_FUNCTION(eth0_mdio), + TEGRA_PIN_FUNCTION(eth0_mdc), + TEGRA_PIN_FUNCTION(eth1_mdc), + TEGRA_PIN_FUNCTION(eth2_mdio), + TEGRA_PIN_FUNCTION(eth2_mdc), + TEGRA_PIN_FUNCTION(eth3_mdio), + TEGRA_PIN_FUNCTION(eth3_mdc), + TEGRA_PIN_FUNCTION(qspi0_cs_n), + TEGRA_PIN_FUNCTION(qspi0_io0), + TEGRA_PIN_FUNCTION(qspi0_io1), + TEGRA_PIN_FUNCTION(qspi0_io2), + TEGRA_PIN_FUNCTION(qspi0_io3), + TEGRA_PIN_FUNCTION(qspi0_sck), + TEGRA_PIN_FUNCTION(sdmmc1_clk), + TEGRA_PIN_FUNCTION(sdmmc1_cmd), + TEGRA_PIN_FUNCTION(sdmmc1_comp), + TEGRA_PIN_FUNCTION(sdmmc1_dat3), + TEGRA_PIN_FUNCTION(sdmmc1_dat2), + TEGRA_PIN_FUNCTION(sdmmc1_dat1), + TEGRA_PIN_FUNCTION(sdmmc1_dat0), + TEGRA_PIN_FUNCTION(qspi3_sck), + TEGRA_PIN_FUNCTION(qspi3_cs0), + TEGRA_PIN_FUNCTION(qspi3_io0), + TEGRA_PIN_FUNCTION(qspi3_io1), + TEGRA_PIN_FUNCTION(dcb_vsync), + TEGRA_PIN_FUNCTION(dcb_hsync), + TEGRA_PIN_FUNCTION(dsa_lspii), + TEGRA_PIN_FUNCTION(dce_vsync), + TEGRA_PIN_FUNCTION(dce_hsync), + TEGRA_PIN_FUNCTION(dch_vsync), + TEGRA_PIN_FUNCTION(dch_hsync), + TEGRA_PIN_FUNCTION(bl_en), + TEGRA_PIN_FUNCTION(bl_pwm_dim0), + TEGRA_PIN_FUNCTION(rsvd1), + TEGRA_PIN_FUNCTION(soc_therm_oc3), + TEGRA_PIN_FUNCTION(i2s5_sclk), + TEGRA_PIN_FUNCTION(i2s5_sdata_in), + TEGRA_PIN_FUNCTION(extperiph3_clk), + TEGRA_PIN_FUNCTION(extperiph4_clk), + TEGRA_PIN_FUNCTION(i2s5_sdata_out), + TEGRA_PIN_FUNCTION(i2s5_lrck), + TEGRA_PIN_FUNCTION(sdmmc1_cd), + TEGRA_PIN_FUNCTION(i2s7_sdata_in), + TEGRA_PIN_FUNCTION(spi4_sck), + TEGRA_PIN_FUNCTION(spi4_din), + TEGRA_PIN_FUNCTION(spi4_dout), + TEGRA_PIN_FUNCTION(spi4_cs0), + TEGRA_PIN_FUNCTION(spi4_cs1), + TEGRA_PIN_FUNCTION(gp_pwm5), + TEGRA_PIN_FUNCTION(i2c14_clk), + TEGRA_PIN_FUNCTION(i2c14_dat), + TEGRA_PIN_FUNCTION(i2s8_sclk), + TEGRA_PIN_FUNCTION(i2s8_sdata_out), + TEGRA_PIN_FUNCTION(i2s8_lrck), + TEGRA_PIN_FUNCTION(i2s8_sdata_in), + TEGRA_PIN_FUNCTION(i2c16_clk), + TEGRA_PIN_FUNCTION(i2c16_dat), + TEGRA_PIN_FUNCTION(i2s3_sclk), + TEGRA_PIN_FUNCTION(i2s3_sdata_out), + TEGRA_PIN_FUNCTION(i2s3_sdata_in), + TEGRA_PIN_FUNCTION(i2s3_lrck), + TEGRA_PIN_FUNCTION(pm_trig1), + TEGRA_PIN_FUNCTION(pm_trig0), + TEGRA_PIN_FUNCTION(qspi2_sck), + TEGRA_PIN_FUNCTION(qspi2_cs0), + TEGRA_PIN_FUNCTION(qspi2_io0), + TEGRA_PIN_FUNCTION(qspi2_io1), + TEGRA_PIN_FUNCTION(dcc_vsync), + TEGRA_PIN_FUNCTION(dcc_hsync), + TEGRA_PIN_FUNCTION(rsvd2), + TEGRA_PIN_FUNCTION(dcf_vsync), + TEGRA_PIN_FUNCTION(dcf_hsync), + TEGRA_PIN_FUNCTION(soundwire1_clk), + TEGRA_PIN_FUNCTION(soundwire1_dat0), + TEGRA_PIN_FUNCTION(soundwire1_dat1), + TEGRA_PIN_FUNCTION(soundwire1_dat2), + TEGRA_PIN_FUNCTION(dmic2_clk), + TEGRA_PIN_FUNCTION(dmic2_dat), + TEGRA_PIN_FUNCTION(nv_therm_fan_tach1), + TEGRA_PIN_FUNCTION(i2c15_clk), + TEGRA_PIN_FUNCTION(i2c15_dat), + TEGRA_PIN_FUNCTION(i2s7_lrck), + TEGRA_PIN_FUNCTION(ccla_la_trigger_mux), + TEGRA_PIN_FUNCTION(i2s7_sclk), + TEGRA_PIN_FUNCTION(i2s7_sdata_out), + TEGRA_PIN_FUNCTION(dmic1_dat), + TEGRA_PIN_FUNCTION(dmic1_clk), + TEGRA_PIN_FUNCTION(dcd_vsync), + TEGRA_PIN_FUNCTION(dcd_hsync), + TEGRA_PIN_FUNCTION(rsvd3), + TEGRA_PIN_FUNCTION(dcg_vsync), + TEGRA_PIN_FUNCTION(dcg_hsync), + TEGRA_PIN_FUNCTION(dspk1_clk), + TEGRA_PIN_FUNCTION(dspk1_dat), + TEGRA_PIN_FUNCTION(soc_therm_oc2), + TEGRA_PIN_FUNCTION(istctrl_ist_done_n), + TEGRA_PIN_FUNCTION(soc_therm_oc1), + TEGRA_PIN_FUNCTION(tsc_edge_out0c), + TEGRA_PIN_FUNCTION(tsc_edge_out0d), + TEGRA_PIN_FUNCTION(tsc_edge_out0a), + TEGRA_PIN_FUNCTION(tsc_edge_out0b), + TEGRA_PIN_FUNCTION(touch_clk), + TEGRA_PIN_FUNCTION(hdmi_cec), + TEGRA_PIN_FUNCTION(i2c2_clk), + TEGRA_PIN_FUNCTION(i2c2_dat), + TEGRA_PIN_FUNCTION(i2c3_clk), + TEGRA_PIN_FUNCTION(i2c3_dat), + TEGRA_PIN_FUNCTION(gp_pwm4), + TEGRA_PIN_FUNCTION(uarta_txd), + TEGRA_PIN_FUNCTION(uarta_rxd), + TEGRA_PIN_FUNCTION(spi2_sck), + TEGRA_PIN_FUNCTION(spi2_din), + TEGRA_PIN_FUNCTION(spi2_dout), + TEGRA_PIN_FUNCTION(spi2_cs0), + TEGRA_PIN_FUNCTION(tsc_sync1), + TEGRA_PIN_FUNCTION(tsc_edge_out3), + TEGRA_PIN_FUNCTION(tsc_edge_out0), + TEGRA_PIN_FUNCTION(tsc_edge_out1), + TEGRA_PIN_FUNCTION(tsc_sync0), + TEGRA_PIN_FUNCTION(soundwire0_clk), + TEGRA_PIN_FUNCTION(soundwire0_dat0), + TEGRA_PIN_FUNCTION(l0l1_rst_out_n), + TEGRA_PIN_FUNCTION(l2_rst_out_n), + TEGRA_PIN_FUNCTION(uartl_txd), + TEGRA_PIN_FUNCTION(uartl_rxd), + TEGRA_PIN_FUNCTION(i2s9_sclk), + TEGRA_PIN_FUNCTION(i2s9_sdata_out), + TEGRA_PIN_FUNCTION(i2s9_sdata_in), + TEGRA_PIN_FUNCTION(i2s9_lrck), + TEGRA_PIN_FUNCTION(dmic5_dat), + TEGRA_PIN_FUNCTION(dmic5_clk), + TEGRA_PIN_FUNCTION(tsc_edge_out2), +}; + +#define PINGROUP_REG_Y(r) ((r)) +#define PINGROUP_REG_N(r) -1 + +#define DRV_PINGROUP_Y(r) ((r)) + +#define DRV_PINGROUP_ENTRY_N \ + .drv_reg = -1, \ + .drv_bank = -1, \ + .drvdn_bit = -1, \ + .drvup_bit = -1, \ + .slwr_bit = -1, \ + .slwf_bit = -1 + +#define DRV_PINGROUP_ENTRY_Y(r, drvdn_b, drvdn_w, drvup_b, \ + drvup_w, slwr_b, slwr_w, slwf_b, \ + slwf_w, bank) \ + .drv_reg = DRV_PINGROUP_Y(r), \ + .drv_bank = bank, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w + +#define PIN_PINGROUP_ENTRY_N \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_bit = -1, \ + .e_io_hv_bit = -1, \ + .odrain_bit = -1, \ + .lock_bit = -1, \ + .parked_bit = -1, \ + .lpmd_bit = -1, \ + .drvtype_bit = -1, \ + .lpdr_bit = -1, \ + .pbias_buf_bit = -1, \ + .preemp_bit = -1, \ + .rfu_in_bit = -1 + +#define PIN_PINGROUP_ENTRY_Y(r, bank, pupd, e_io_hv, e_lpbk, e_input, \ + e_lpdr, e_pbias_buf, gpio_sfio_sel, \ + schmitt_b) \ + .mux_reg = PINGROUP_REG_Y(r), \ + .lpmd_bit = -1, \ + .lock_bit = -1, \ + .hsm_bit = -1, \ + .mux_bank = bank, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG_##pupd(r), \ + .pupd_bank = bank, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG_Y(r), \ + .tri_bank = bank, \ + .tri_bit = 4, \ + .einput_bit = e_input, \ + .sfsel_bit = gpio_sfio_sel, \ + .schmitt_bit = schmitt_b, \ + .drvtype_bit = 13, \ + .lpdr_bit = e_lpdr, + +#define drive_eth1_mdio_pe0 DRV_PINGROUP_ENTRY_Y(0x4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l4_clkreq_n_pd0 DRV_PINGROUP_ENTRY_Y(0xc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l4_rst_n_pd1 DRV_PINGROUP_ENTRY_Y(0x14, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l5_clkreq_n_pd2 DRV_PINGROUP_ENTRY_Y(0x1c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l5_rst_n_pd3 DRV_PINGROUP_ENTRY_Y(0x24, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth0_mdio_pd4 DRV_PINGROUP_ENTRY_Y(0x2c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth0_mdc_pd5 DRV_PINGROUP_ENTRY_Y(0x34, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth1_mdc_pe1 DRV_PINGROUP_ENTRY_Y(0x3c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth2_mdio_pe2 DRV_PINGROUP_ENTRY_Y(0x44, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth2_mdc_pe3 DRV_PINGROUP_ENTRY_Y(0x4c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth3_mdio_pd6 DRV_PINGROUP_ENTRY_Y(0x54, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_eth3_mdc_pd7 DRV_PINGROUP_ENTRY_Y(0x5c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l1_clkreq_n_pb0 DRV_PINGROUP_ENTRY_Y(0x2004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l1_rst_n_pb1 DRV_PINGROUP_ENTRY_Y(0x200c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_wake_n_pc2 DRV_PINGROUP_ENTRY_Y(0x2014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l2_rst_n_pb3 DRV_PINGROUP_ENTRY_Y(0x201c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l2_clkreq_n_pb2 DRV_PINGROUP_ENTRY_Y(0x2024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l3_clkreq_n_pb4 DRV_PINGROUP_ENTRY_Y(0x202c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pex_l3_rst_n_pb5 DRV_PINGROUP_ENTRY_Y(0x2034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_sgmii0_sma_mdio_pc0 DRV_PINGROUP_ENTRY_Y(0x203c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_sgmii0_sma_mdc_pc1 DRV_PINGROUP_ENTRY_Y(0x2044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio113_pb6 DRV_PINGROUP_ENTRY_Y(0x204c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio114_pb7 DRV_PINGROUP_ENTRY_Y(0x2054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm1_pa0 DRV_PINGROUP_ENTRY_Y(0x3004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm6_pa1 DRV_PINGROUP_ENTRY_Y(0x300c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm7_pa2 DRV_PINGROUP_ENTRY_Y(0x3014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm8_pa3 DRV_PINGROUP_ENTRY_Y(0x301c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_ufs0_ref_clk_pa4 DRV_PINGROUP_ENTRY_Y(0x3024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_ufs0_rst_n_pa5 DRV_PINGROUP_ENTRY_Y(0x302c, 12, 4, 20, 4, -1, -1, -1, -1, 0) + +#define drive_cpu_pwr_req_ph0 DRV_PINGROUP_ENTRY_Y(0x4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gpu_pwr_req_ph1 DRV_PINGROUP_ENTRY_Y(0xc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart10_cts_n_ph5 DRV_PINGROUP_ENTRY_Y(0x14, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart10_rts_n_ph4 DRV_PINGROUP_ENTRY_Y(0x1c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart10_rx_ph3 DRV_PINGROUP_ENTRY_Y(0x24, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart10_tx_ph2 DRV_PINGROUP_ENTRY_Y(0x2c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi3_cs0_pj1 DRV_PINGROUP_ENTRY_Y(0x34, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi3_cs3_pj2 DRV_PINGROUP_ENTRY_Y(0x3c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi3_miso_ph7 DRV_PINGROUP_ENTRY_Y(0x44, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi3_mosi_pj0 DRV_PINGROUP_ENTRY_Y(0x4c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi3_sck_ph6 DRV_PINGROUP_ENTRY_Y(0x54, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart5_cts_n_pj6 DRV_PINGROUP_ENTRY_Y(0x5c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart5_rts_n_pj5 DRV_PINGROUP_ENTRY_Y(0x64, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart5_rx_pj4 DRV_PINGROUP_ENTRY_Y(0x6c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart5_tx_pj3 DRV_PINGROUP_ENTRY_Y(0x74, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi1_cs0_pk2 DRV_PINGROUP_ENTRY_Y(0x7c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi1_cs1_pk3 DRV_PINGROUP_ENTRY_Y(0x84, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi1_miso_pk0 DRV_PINGROUP_ENTRY_Y(0x8c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi1_mosi_pk1 DRV_PINGROUP_ENTRY_Y(0x94, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi1_sck_pj7 DRV_PINGROUP_ENTRY_Y(0x9c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_extperiph2_clk_pk5 DRV_PINGROUP_ENTRY_Y(0xa4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_extperiph1_clk_pk4 DRV_PINGROUP_ENTRY_Y(0xac, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen12_i2c_scl_pk6 DRV_PINGROUP_ENTRY_Y(0xb4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen12_i2c_sda_pk7 DRV_PINGROUP_ENTRY_Y(0xbc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio124_pl0 DRV_PINGROUP_ENTRY_Y(0x1004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio125_pl1 DRV_PINGROUP_ENTRY_Y(0x100c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_fan_tach0_pl2 DRV_PINGROUP_ENTRY_Y(0x1014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio127_pl3 DRV_PINGROUP_ENTRY_Y(0x101c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio128_pl4 DRV_PINGROUP_ENTRY_Y(0x1024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio129_pl5 DRV_PINGROUP_ENTRY_Y(0x102c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio130_pl6 DRV_PINGROUP_ENTRY_Y(0x1034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio131_pl7 DRV_PINGROUP_ENTRY_Y(0x103c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gp_pwm9_pm0 DRV_PINGROUP_ENTRY_Y(0x1044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio133_pm1 DRV_PINGROUP_ENTRY_Y(0x104c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart9_cts_n_pm5 DRV_PINGROUP_ENTRY_Y(0x1054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart9_rts_n_pm4 DRV_PINGROUP_ENTRY_Y(0x105c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart9_rx_pm3 DRV_PINGROUP_ENTRY_Y(0x1064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart9_tx_pm2 DRV_PINGROUP_ENTRY_Y(0x106c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_sdmmc1_comp DRV_PINGROUP_ENTRY_N +#define drive_sdmmc1_clk_px0 DRV_PINGROUP_ENTRY_Y(0x2004, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_cmd_px1 DRV_PINGROUP_ENTRY_Y(0x200c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat3_px5 DRV_PINGROUP_ENTRY_Y(0x201c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat2_px4 DRV_PINGROUP_ENTRY_Y(0x2024, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat1_px3 DRV_PINGROUP_ENTRY_Y(0x202c, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_sdmmc1_dat0_px2 DRV_PINGROUP_ENTRY_Y(0x2034, 28, 2, 30, 2, -1, -1, -1, -1, 0) +#define drive_qspi0_cs_n_pt1 DRV_PINGROUP_ENTRY_Y(0x3004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_qspi0_io0_pt2 DRV_PINGROUP_ENTRY_Y(0x300c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_qspi0_io1_pt3 DRV_PINGROUP_ENTRY_Y(0x3014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_qspi0_io2_pt4 DRV_PINGROUP_ENTRY_Y(0x301c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_qspi0_io3_pt5 DRV_PINGROUP_ENTRY_Y(0x3024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_qspi0_sck_pt0 DRV_PINGROUP_ENTRY_Y(0x302c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio192_pt6 DRV_PINGROUP_ENTRY_Y(0x3034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio138_pp0 DRV_PINGROUP_ENTRY_Y(0x5004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio139_pp1 DRV_PINGROUP_ENTRY_Y(0x500c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap6_din_pp4 DRV_PINGROUP_ENTRY_Y(0x5014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap6_dout_pp3 DRV_PINGROUP_ENTRY_Y(0x501c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap6_fs_pp5 DRV_PINGROUP_ENTRY_Y(0x5024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap6_sclk_pp2 DRV_PINGROUP_ENTRY_Y(0x502c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap4_dout_pp7 DRV_PINGROUP_ENTRY_Y(0x5034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap4_sclk_pp6 DRV_PINGROUP_ENTRY_Y(0x503c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap4_din_pq0 DRV_PINGROUP_ENTRY_Y(0x5044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap4_fs_pq1 DRV_PINGROUP_ENTRY_Y(0x504c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi5_cs0_pq5 DRV_PINGROUP_ENTRY_Y(0x5054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi5_miso_pq3 DRV_PINGROUP_ENTRY_Y(0x505c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi5_mosi_pq4 DRV_PINGROUP_ENTRY_Y(0x5064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi5_sck_pq2 DRV_PINGROUP_ENTRY_Y(0x506c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio152_pq6 DRV_PINGROUP_ENTRY_Y(0x5074, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio153_pq7 DRV_PINGROUP_ENTRY_Y(0x507c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio155_pr1 DRV_PINGROUP_ENTRY_Y(0x5084, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_aud_mclk_pr0 DRV_PINGROUP_ENTRY_Y(0x508c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap1_sclk_pr2 DRV_PINGROUP_ENTRY_Y(0x5094, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap1_in_pr4 DRV_PINGROUP_ENTRY_Y(0x509c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap1_out_pr3 DRV_PINGROUP_ENTRY_Y(0x50a4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap1_fs_pr5 DRV_PINGROUP_ENTRY_Y(0x50ac, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen11_i2c_scl_pr6 DRV_PINGROUP_ENTRY_Y(0x50b4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen11_i2c_sda_pr7 DRV_PINGROUP_ENTRY_Y(0x50bc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio350_ps0 DRV_PINGROUP_ENTRY_Y(0x50c4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio351_ps1 DRV_PINGROUP_ENTRY_Y(0x50cc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen0_i2c_scl_pw4 DRV_PINGROUP_ENTRY_Y(0x6004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen0_i2c_sda_pw5 DRV_PINGROUP_ENTRY_Y(0x600c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen1_i2c_scl_pw2 DRV_PINGROUP_ENTRY_Y(0x6014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen1_i2c_sda_pw3 DRV_PINGROUP_ENTRY_Y(0x601c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap2_fs_pw1 DRV_PINGROUP_ENTRY_Y(0x6044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap2_clk_pv6 DRV_PINGROUP_ENTRY_Y(0x604c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap2_din_pv7 DRV_PINGROUP_ENTRY_Y(0x6054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dap2_dout_pw0 DRV_PINGROUP_ENTRY_Y(0x605c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm10_pv1 DRV_PINGROUP_ENTRY_Y(0x6064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio170_pu0 DRV_PINGROUP_ENTRY_Y(0x606c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio171_pu1 DRV_PINGROUP_ENTRY_Y(0x6074, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio172_pu2 DRV_PINGROUP_ENTRY_Y(0x607c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio173_pu3 DRV_PINGROUP_ENTRY_Y(0x6084, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio174_pu4 DRV_PINGROUP_ENTRY_Y(0x608c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio175_pu5 DRV_PINGROUP_ENTRY_Y(0x6094, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio176_pu6 DRV_PINGROUP_ENTRY_Y(0x609c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio177_pu7 DRV_PINGROUP_ENTRY_Y(0x60a4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio178_pv0 DRV_PINGROUP_ENTRY_Y(0x60ac, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart4_cts_n_pv5 DRV_PINGROUP_ENTRY_Y(0x60b4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart4_rts_n_pv4 DRV_PINGROUP_ENTRY_Y(0x60bc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart4_rx_pv3 DRV_PINGROUP_ENTRY_Y(0x60c4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart4_tx_pv2 DRV_PINGROUP_ENTRY_Y(0x60cc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwr_i2c_sda_pw7 DRV_PINGROUP_ENTRY_Y(0x60d4, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwr_i2c_scl_pw6 DRV_PINGROUP_ENTRY_Y(0x60dc, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio250_pf0 DRV_PINGROUP_ENTRY_Y(0x7004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio251_pf1 DRV_PINGROUP_ENTRY_Y(0x700c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio252_pf2 DRV_PINGROUP_ENTRY_Y(0x7014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dp_aux_ch0_hpd_pf3 DRV_PINGROUP_ENTRY_Y(0x701c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dp_aux_ch1_hpd_pf4 DRV_PINGROUP_ENTRY_Y(0x7024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dp_aux_ch2_hpd_pf5 DRV_PINGROUP_ENTRY_Y(0x702c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_dp_aux_ch3_hpd_pf6 DRV_PINGROUP_ENTRY_Y(0x7034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm2_pf7 DRV_PINGROUP_ENTRY_Y(0x703c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_pwm3_pg0 DRV_PINGROUP_ENTRY_Y(0x7044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen7_i2c_scl_pg1 DRV_PINGROUP_ENTRY_Y(0x704c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen7_i2c_sda_pg2 DRV_PINGROUP_ENTRY_Y(0x7054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen9_i2c_scl_pg3 DRV_PINGROUP_ENTRY_Y(0x705c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen9_i2c_sda_pg4 DRV_PINGROUP_ENTRY_Y(0x7064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio270_py0 DRV_PINGROUP_ENTRY_Y(0xa004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio271_py1 DRV_PINGROUP_ENTRY_Y(0xa00c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio272_py2 DRV_PINGROUP_ENTRY_Y(0xa014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio273_py3 DRV_PINGROUP_ENTRY_Y(0xa01c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio274_py4 DRV_PINGROUP_ENTRY_Y(0xa024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio275_py5 DRV_PINGROUP_ENTRY_Y(0xa02c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio276_py6 DRV_PINGROUP_ENTRY_Y(0xa034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio277_py7 DRV_PINGROUP_ENTRY_Y(0xa03c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio278_pz0 DRV_PINGROUP_ENTRY_Y(0xa044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio279_pz1 DRV_PINGROUP_ENTRY_Y(0xa04c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio282_pz4 DRV_PINGROUP_ENTRY_Y(0xa054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio283_pz5 DRV_PINGROUP_ENTRY_Y(0xa05c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio284_pz6 DRV_PINGROUP_ENTRY_Y(0xa064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio285_pz7 DRV_PINGROUP_ENTRY_Y(0xa06c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio286_pal0 DRV_PINGROUP_ENTRY_Y(0xa074, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio287_pal1 DRV_PINGROUP_ENTRY_Y(0xa07c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio288_pal2 DRV_PINGROUP_ENTRY_Y(0xa084, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_xhalt_trig_pz2 DRV_PINGROUP_ENTRY_Y(0xa08c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio281_pz3 DRV_PINGROUP_ENTRY_Y(0xa094, 12, 4, 20, 4, -1, -1, -1, -1, 0) + +#define drive_ao_retention_n_paa2 DRV_PINGROUP_ENTRY_Y(0x2c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_batt_oc_paa3 DRV_PINGROUP_ENTRY_Y(0x34, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_power_on_paa5 DRV_PINGROUP_ENTRY_Y(0x3c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_vcomp_alert_paa1 DRV_PINGROUP_ENTRY_Y(0x44, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_bootv_ctl_n_paa4 DRV_PINGROUP_ENTRY_Y(0x4c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio00_paa0 DRV_PINGROUP_ENTRY_Y(0x54, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio07_paa7 DRV_PINGROUP_ENTRY_Y(0x5c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio08_pbb0 DRV_PINGROUP_ENTRY_Y(0x64, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio09_pbb1 DRV_PINGROUP_ENTRY_Y(0x6c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_hdmi_cec_paa6 DRV_PINGROUP_ENTRY_Y(0x74, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen2_i2c_scl_pcc0 DRV_PINGROUP_ENTRY_Y(0x1004, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen2_i2c_sda_pcc1 DRV_PINGROUP_ENTRY_Y(0x100c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen3_i2c_scl_pcc2 DRV_PINGROUP_ENTRY_Y(0x1014, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gen3_i2c_sda_pcc3 DRV_PINGROUP_ENTRY_Y(0x101c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_gp_pwm4_pcc4 DRV_PINGROUP_ENTRY_Y(0x1024, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart0_tx_pcc5 DRV_PINGROUP_ENTRY_Y(0x102c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_uart0_rx_pcc6 DRV_PINGROUP_ENTRY_Y(0x1034, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi2_sck_pcc7 DRV_PINGROUP_ENTRY_Y(0x103c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi2_miso_pdd0 DRV_PINGROUP_ENTRY_Y(0x1044, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi2_mosi_pdd1 DRV_PINGROUP_ENTRY_Y(0x104c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_spi2_cs0_n_pdd2 DRV_PINGROUP_ENTRY_Y(0x1054, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio21_pdd3 DRV_PINGROUP_ENTRY_Y(0x105c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio22_pdd4 DRV_PINGROUP_ENTRY_Y(0x1064, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio23_pdd5 DRV_PINGROUP_ENTRY_Y(0x106c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio24_pdd6 DRV_PINGROUP_ENTRY_Y(0x1074, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio25_pdd7 DRV_PINGROUP_ENTRY_Y(0x107c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio26_pee0 DRV_PINGROUP_ENTRY_Y(0x1084, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio27_pee1 DRV_PINGROUP_ENTRY_Y(0x108c, 12, 4, 20, 4, -1, -1, -1, -1, 0) +#define drive_soc_gpio28_pee2 DRV_PINGROUP_ENTRY_N +#define drive_soc_gpio29_pee3 DRV_PINGROUP_ENTRY_N + +#define PINGROUP(pg_name, f0, f1, f2, f3, r, bank, pupd, e_io_hv, e_lpbk, e_input, e_lpdr, e_pbias_buf, \ + gpio_sfio_sel, schmitt_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + PIN_PINGROUP_ENTRY_Y(r, bank, pupd, e_io_hv, e_lpbk, \ + e_input, e_lpdr, e_pbias_buf, \ + gpio_sfio_sel, schmitt_b) \ + drive_##pg_name, \ + } + +static const struct tegra_pingroup tegra264_uphy_groups[] = { + PINGROUP(eth1_mdio_pe0, ETH1_MDIO, RSVD1, RSVD2, RSVD3, 0x0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l4_clkreq_n_pd0, PE4_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l4_rst_n_pd1, PE4_RST_L, RSVD1, RSVD2, RSVD3, 0x10, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l5_clkreq_n_pd2, PE5_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x18, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l5_rst_n_pd3, PE5_RST_L, RSVD1, RSVD2, RSVD3, 0x20, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth0_mdio_pd4, ETH0_MDIO, RSVD1, RSVD2, RSVD3, 0x28, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth0_mdc_pd5, ETH0_MDC, RSVD1, RSVD2, RSVD3, 0x30, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth1_mdc_pe1, ETH1_MDC, RSVD1, RSVD2, RSVD3, 0x38, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth2_mdio_pe2, ETH2_MDIO, RSVD1, RSVD2, RSVD3, 0x40, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth2_mdc_pe3, ETH2_MDC, RSVD1, RSVD2, RSVD3, 0x48, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth3_mdio_pd6, ETH3_MDIO, RSVD1, RSVD2, RSVD3, 0x50, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(eth3_mdc_pd7, ETH3_MDC, RSVD1, RSVD2, RSVD3, 0x58, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l1_clkreq_n_pb0, PE1_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x2000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l1_rst_n_pb1, PE1_RST_L, RSVD1, RSVD2, RSVD3, 0x2008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_wake_n_pc2, RSVD0, RSVD1, RSVD2, RSVD3, 0x2010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l2_rst_n_pb3, PE2_RST_L, RSVD1, RSVD2, RSVD3, 0x2018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l2_clkreq_n_pb2, PE2_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x2020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l3_clkreq_n_pb4, PE3_CLKREQ_L, RSVD1, RSVD2, RSVD3, 0x2028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pex_l3_rst_n_pb5, PE3_RST_L, RSVD1, RSVD2, RSVD3, 0x2030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(sgmii0_sma_mdio_pc0, SGMII0_SMA_MDIO, RSVD1, RSVD2, RSVD3, 0x2038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(sgmii0_sma_mdc_pc1, SGMII0_SMA_MDC, RSVD1, RSVD2, RSVD3, 0x2040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio113_pb6, USB_VBUS_EN0, RSVD1, RSVD2, RSVD3, 0x2048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio114_pb7, USB_VBUS_EN1, RSVD1, RSVD2, RSVD3, 0x2050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm1_pa0, GP_PWM1, RSVD1, RSVD2, RSVD3, 0x3000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm6_pa1, GP_PWM6, RSVD1, RSVD2, RSVD3, 0x3008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm7_pa2, GP_PWM7, RSVD1, RSVD2, RSVD3, 0x3010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm8_pa3, GP_PWM8, RSVD1, RSVD2, RSVD3, 0x3018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(ufs0_ref_clk_pa4, UFS0, RSVD1, RSVD2, RSVD3, 0x3020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(ufs0_rst_n_pa5, UFS0, RSVD1, RSVD2, RSVD3, 0x3028, 0, Y, 5, 7, 6, 8, -1, 10, 11), +}; + +static const struct tegra_pingroup tegra264_main_groups[] = { + PINGROUP(cpu_pwr_req_ph0, RSVD0, RSVD1, RSVD2, RSVD3, 0x0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gpu_pwr_req_ph1, RSVD0, RSVD1, RSVD2, RSVD3, 0x8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart10_cts_n_ph5, UARTK_CTS, RSVD1, RSVD2, RSVD3, 0x10, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart10_rts_n_ph4, UARTK_RTS, RSVD1, RSVD2, RSVD3, 0x18, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart10_rx_ph3, UARTK_RXD, RSVD1, RSVD2, RSVD3, 0x20, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart10_tx_ph2, UARTK_TXD, RSVD1, RSVD2, RSVD3, 0x28, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi3_cs0_pj1, SPI3_CS0, RSVD1, RSVD2, RSVD3, 0x30, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi3_cs3_pj2, SPI3_CS3, RSVD1, RSVD2, RSVD3, 0x38, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi3_miso_ph7, SPI3_DIN, RSVD1, RSVD2, RSVD3, 0x40, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi3_mosi_pj0, SPI3_DOUT, RSVD1, RSVD2, RSVD3, 0x48, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi3_sck_ph6, SPI3_SCK, RSVD1, RSVD2, RSVD3, 0x50, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart5_cts_n_pj6, UARTF_CTS, RSVD1, RSVD2, RSVD3, 0x58, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart5_rts_n_pj5, UARTF_RTS, RSVD1, RSVD2, RSVD3, 0x60, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart5_rx_pj4, UARTF_RXD, RSVD1, RSVD2, RSVD3, 0x68, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart5_tx_pj3, UARTF_TXD, RSVD1, RSVD2, RSVD3, 0x70, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi1_cs0_pk2, SPI1_CS0, RSVD1, RSVD2, RSVD3, 0x78, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi1_cs1_pk3, SPI1_CS1, RSVD1, RSVD2, RSVD3, 0x80, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi1_miso_pk0, SPI1_DIN, RSVD1, RSVD2, RSVD3, 0x88, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi1_mosi_pk1, SPI1_DOUT, RSVD1, RSVD2, RSVD3, 0x90, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi1_sck_pj7, SPI1_SCK, RSVD1, RSVD2, RSVD3, 0x98, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(extperiph2_clk_pk5, EXTPERIPH2_CLK, RSVD1, DMIC2_CLK, DSPK1_CLK, 0xa0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(extperiph1_clk_pk4, EXTPERIPH1_CLK, RSVD1, DMIC2_DAT, DSPK1_DAT, 0xa8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen12_i2c_scl_pk6, I2C12_CLK, RSVD1, RSVD2, RSVD3, 0xb0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen12_i2c_sda_pk7, I2C12_DAT, RSVD1, RSVD2, RSVD3, 0xb8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio124_pl0, RSVD0, SOC_THERM_OC3, RSVD2, RSVD3, 0x1000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio125_pl1, RSVD0, I2S5_SCLK, RSVD2, RSVD3, 0x1008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(fan_tach0_pl2, NV_THERM_FAN_TACH0, RSVD1, RSVD2, RSVD3, 0x1010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio127_pl3, RSVD0, RSVD1, NV_THERM_FAN_TACH1, RSVD3, 0x1018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio128_pl4, RSVD0, I2S5_SDATA_IN, RSVD2, RSVD3, 0x1020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio129_pl5, RSVD0, EXTPERIPH3_CLK, I2C15_CLK, RSVD3, 0x1028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio130_pl6, RSVD0, EXTPERIPH4_CLK, I2C15_DAT, RSVD3, 0x1030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio131_pl7, RSVD0, I2S5_SDATA_OUT, RSVD2, RSVD3, 0x1038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gp_pwm9_pm0, GP_PWM9, RSVD1, RSVD2, RSVD3, 0x1040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio133_pm1, RSVD0, I2S5_LRCK, RSVD2, RSVD3, 0x1048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart9_cts_n_pm5, UARTJ_CTS, RSVD1, RSVD2, RSVD3, 0x1050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart9_rts_n_pm4, UARTJ_RTS, RSVD1, RSVD2, RSVD3, 0x1058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart9_rx_pm3, UARTJ_RXD, RSVD1, RSVD2, RSVD3, 0x1060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart9_tx_pm2, UARTJ_TXD, RSVD1, RSVD2, RSVD3, 0x1068, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(sdmmc1_clk_px0, SDMMC1_CLK, RSVD1, RSVD2, RSVD3, 0x2000, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(sdmmc1_cmd_px1, SDMMC1_CMD, RSVD1, RSVD2, RSVD3, 0x2008, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(sdmmc1_comp, SDMMC1_COMP, RSVD1, RSVD2, RSVD3, 0x2010, 0, N, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat3_px5, SDMMC1_DAT3, RSVD1, RSVD2, RSVD3, 0x2018, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(sdmmc1_dat2_px4, SDMMC1_DAT2, RSVD1, RSVD2, RSVD3, 0x2020, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(sdmmc1_dat1_px3, SDMMC1_DAT1, RSVD1, RSVD2, RSVD3, 0x2028, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(sdmmc1_dat0_px2, SDMMC1_DAT0, RSVD1, RSVD2, RSVD3, 0x2030, 0, Y, -1, 5, 6, 13, -1, 10, -1), + PINGROUP(qspi0_cs_n_pt1, QSPI0_CS_N, RSVD1, RSVD2, RSVD3, 0x3000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(qspi0_io0_pt2, QSPI0_IO0, RSVD1, RSVD2, RSVD3, 0x3008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(qspi0_io1_pt3, QSPI0_IO1, RSVD1, RSVD2, RSVD3, 0x3010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(qspi0_io2_pt4, QSPI0_IO2, RSVD1, RSVD2, RSVD3, 0x3018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(qspi0_io3_pt5, QSPI0_IO3, RSVD1, RSVD2, RSVD3, 0x3020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(qspi0_sck_pt0, QSPI0_SCK, RSVD1, RSVD2, RSVD3, 0x3028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio192_pt6, RSVD0, RSVD1, RSVD2, RSVD3, 0x3030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio138_pp0, RSVD0, I2C14_CLK, DMIC1_DAT, RSVD3, 0x5000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio139_pp1, RSVD0, I2C14_DAT, DMIC1_CLK, RSVD3, 0x5008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap6_din_pp4, I2S6_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x5010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap6_dout_pp3, I2S6_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x5018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap6_fs_pp5, I2S6_LRCK, RSVD1, RSVD2, RSVD3, 0x5020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap6_sclk_pp2, I2S6_SCLK, RSVD1, RSVD2, RSVD3, 0x5028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap4_dout_pp7, I2S4_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x5030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap4_sclk_pp6, I2S4_SCLK, RSVD1, RSVD2, RSVD3, 0x5038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap4_din_pq0, I2S4_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x5040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap4_fs_pq1, I2S4_LRCK, RSVD1, RSVD2, RSVD3, 0x5048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi5_cs0_pq5, SPI5_CS0, RSVD1, RSVD2, RSVD3, 0x5050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi5_miso_pq3, SPI5_DIN, RSVD1, RSVD2, RSVD3, 0x5058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi5_mosi_pq4, SPI5_DOUT, RSVD1, RSVD2, RSVD3, 0x5060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi5_sck_pq2, SPI5_SCK, RSVD1, RSVD2, RSVD3, 0x5068, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio152_pq6, RSVD0, I2S8_SCLK, RSVD2, RSVD3, 0x5070, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio153_pq7, RSVD0, I2S8_SDATA_OUT, RSVD2, RSVD3, 0x5078, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio155_pr1, RSVD0, I2S8_LRCK, RSVD2, RSVD3, 0x5080, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(aud_mclk_pr0, AUD_MCLK, RSVD1, RSVD2, RSVD3, 0x5088, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap1_sclk_pr2, I2S1_SCLK, RSVD1, RSVD2, RSVD3, 0x5090, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap1_in_pr4, I2S1_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x5098, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap1_out_pr3, I2S1_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x50a0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap1_fs_pr5, I2S1_LRCK, RSVD1, RSVD2, RSVD3, 0x50a8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen11_i2c_scl_pr6, I2C11_CLK, RSVD1, RSVD2, RSVD3, 0x50b0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen11_i2c_sda_pr7, I2C11_DAT, RSVD1, RSVD2, RSVD3, 0x50b8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio350_ps0, RSVD0, I2S8_SDATA_IN, RSVD2, RSVD3, 0x50c0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio351_ps1, RSVD0, RSVD1, RSVD2, RSVD3, 0x50c8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen0_i2c_scl_pw4, I2C0_CLK, RSVD1, RSVD2, RSVD3, 0x6000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen0_i2c_sda_pw5, I2C0_DAT, RSVD1, RSVD2, RSVD3, 0x6008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen1_i2c_scl_pw2, I2C1_CLK, RSVD1, RSVD2, RSVD3, 0x6010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen1_i2c_sda_pw3, I2C1_DAT, RSVD1, RSVD2, RSVD3, 0x6018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap2_fs_pw1, I2S2_LRCK, RSVD1, RSVD2, RSVD3, 0x6040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap2_clk_pv6, I2S2_SCLK, RSVD1, RSVD2, RSVD3, 0x6048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap2_din_pv7, I2S2_SDATA_OUT, RSVD1, RSVD2, RSVD3, 0x6050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dap2_dout_pw0, I2S2_SDATA_IN, RSVD1, RSVD2, RSVD3, 0x6058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm10_pv1, GP_PWM10, SDMMC1_CD, I2S7_LRCK, RSVD3, 0x6060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio170_pu0, RSVD0, I2S7_SDATA_IN, CCLA_LA_TRIGGER_MUX, RSVD3, 0x6068, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio171_pu1, RSVD0, SPI4_SCK, RSVD2, RSVD3, 0x6070, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio172_pu2, RSVD0, SPI4_DIN, RSVD2, RSVD3, 0x6078, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio173_pu3, RSVD0, SPI4_DOUT, RSVD2, RSVD3, 0x6080, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio174_pu4, RSVD0, SPI4_CS0, RSVD2, RSVD3, 0x6088, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio175_pu5, RSVD0, SPI4_CS1, RSVD2, RSVD3, 0x6090, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio176_pu6, RSVD0, RSVD1, I2S7_SCLK, RSVD3, 0x6098, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio177_pu7, RSVD0, GP_PWM5, RSVD2, RSVD3, 0x60a0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio178_pv0, RSVD0, RSVD1, I2S7_SDATA_OUT, RSVD3, 0x60a8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart4_cts_n_pv5, UARTE_CTS, RSVD1, RSVD2, RSVD3, 0x60b0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart4_rts_n_pv4, UARTE_RTS, RSVD1, RSVD2, RSVD3, 0x60b8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart4_rx_pv3, UARTE_RXD, RSVD1, RSVD2, RSVD3, 0x60c0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart4_tx_pv2, UARTE_TXD, RSVD1, RSVD2, RSVD3, 0x60c8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwr_i2c_sda_pw7, I2C5_DAT, RSVD1, RSVD2, RSVD3, 0x60d0, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwr_i2c_scl_pw6, I2C5_CLK, RSVD1, RSVD2, RSVD3, 0x60d8, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio250_pf0, DCA_VSYNC, DCB_VSYNC, DCC_VSYNC, DCD_VSYNC, 0x7000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio251_pf1, DCA_HSYNC, DCB_HSYNC, DCC_HSYNC, DCD_HSYNC, 0x7008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio252_pf2, RSVD0, DSA_LSPII, RSVD2, RSVD3, 0x7010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dp_aux_ch0_hpd_pf3, DP_AUX_CH0_HPD, DCE_VSYNC, DCF_VSYNC, DCG_VSYNC, 0x7018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dp_aux_ch1_hpd_pf4, DP_AUX_CH1_HPD, DCE_HSYNC, DCF_HSYNC, DCG_HSYNC, 0x7020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dp_aux_ch2_hpd_pf5, DP_AUX_CH2_HPD, DCH_VSYNC, RSVD2, RSVD3, 0x7028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(dp_aux_ch3_hpd_pf6, DP_AUX_CH3_HPD, DCH_HSYNC, RSVD2, RSVD3, 0x7030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm2_pf7, GP_PWM2, BL_EN, RSVD2, RSVD3, 0x7038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(pwm3_pg0, GP_PWM3, BL_PWM_DIM0, RSVD2, RSVD3, 0x7040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen7_i2c_scl_pg1, I2C7_CLK, RSVD1, SOUNDWIRE1_CLK, RSVD3, 0x7048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen7_i2c_sda_pg2, I2C7_DAT, RSVD1, SOUNDWIRE1_DAT0, RSVD3, 0x7050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen9_i2c_scl_pg3, I2C9_CLK, RSVD1, SOUNDWIRE1_DAT1, RSVD3, 0x7058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen9_i2c_sda_pg4, I2C9_DAT, RSVD1, SOUNDWIRE1_DAT2, RSVD3, 0x7060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio270_py0, RSVD0, I2C16_CLK, RSVD2, RSVD3, 0xa000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio271_py1, RSVD0, I2C16_DAT, RSVD2, RSVD3, 0xa008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio272_py2, RSVD0, I2S3_SCLK, RSVD2, RSVD3, 0xa010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio273_py3, RSVD0, I2S3_SDATA_OUT, RSVD2, RSVD3, 0xa018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio274_py4, RSVD0, I2S3_SDATA_IN, RSVD2, RSVD3, 0xa020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio275_py5, RSVD0, I2S3_LRCK, RSVD2, RSVD3, 0xa028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio276_py6, RSVD0, RSVD1, RSVD2, RSVD3, 0xa030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio277_py7, RSVD0, RSVD1, RSVD2, RSVD3, 0xa038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio278_pz0, RSVD0, RSVD1, RSVD2, RSVD3, 0xa040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio279_pz1, RSVD0, RSVD1, RSVD2, RSVD3, 0xa048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio282_pz4, RSVD0, PM_TRIG1, RSVD2, RSVD3, 0xa050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio283_pz5, RSVD0, RSVD1, RSVD2, RSVD3, 0xa058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio284_pz6, RSVD0, RSVD1, RSVD2, RSVD3, 0xa060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio285_pz7, RSVD0, RSVD1, RSVD2, RSVD3, 0xa068, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio286_pal0, RSVD0, RSVD1, RSVD2, RSVD3, 0xa070, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio287_pal1, RSVD0, RSVD1, RSVD2, RSVD3, 0xa078, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio288_pal2, RSVD0, RSVD1, RSVD2, RSVD3, 0xa080, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(xhalt_trig_pz2, XHALT_TRIG, RSVD1, RSVD2, RSVD3, 0xa088, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio281_pz3, RSVD0, PM_TRIG0, RSVD2, RSVD3, 0xa090, 0, Y, 5, 7, 6, 8, -1, 10, 11), +}; + +static const struct tegra_pingroup tegra264_aon_groups[] = { + PINGROUP(ao_retention_n_paa2, RSVD0, RSVD1, RSVD2, ISTCTRL_IST_DONE_N, 0x28, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(batt_oc_paa3, SOC_THERM_OC2, RSVD1, RSVD2, RSVD3, 0x30, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(power_on_paa5, RSVD0, RSVD1, RSVD2, RSVD3, 0x38, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(vcomp_alert_paa1, SOC_THERM_OC1, RSVD1, RSVD2, RSVD3, 0x40, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(bootv_ctl_n_paa4, RSVD0, RSVD1, RSVD2, RSVD3, 0x48, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio00_paa0, RSVD0, RSVD1, RSVD2, RSVD3, 0x50, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio07_paa7, RSVD0, RSVD1, RSVD2, RSVD3, 0x58, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio08_pbb0, RSVD0, RSVD1, RSVD2, RSVD3, 0x60, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio09_pbb1, RSVD0, RSVD1, RSVD2, RSVD3, 0x68, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(hdmi_cec_paa6, HDMI_CEC, RSVD1, RSVD2, RSVD3, 0x70, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen2_i2c_scl_pcc0, I2C2_CLK, RSVD1, RSVD2, RSVD3, 0x1000, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen2_i2c_sda_pcc1, I2C2_DAT, RSVD1, RSVD2, RSVD3, 0x1008, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen3_i2c_scl_pcc2, I2C3_CLK, RSVD1, RSVD2, RSVD3, 0x1010, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gen3_i2c_sda_pcc3, I2C3_DAT, RSVD1, RSVD2, RSVD3, 0x1018, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(gp_pwm4_pcc4, GP_PWM4, TOUCH_CLK, RSVD2, RSVD3, 0x1020, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart0_tx_pcc5, UARTA_TXD, RSVD1, UARTL_TXD, RSVD3, 0x1028, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(uart0_rx_pcc6, UARTA_RXD, RSVD1, UARTL_RXD, RSVD3, 0x1030, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi2_sck_pcc7, SPI2_SCK, RSVD1, I2S9_SCLK, SOUNDWIRE0_CLK, 0x1038, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi2_miso_pdd0, SPI2_DIN, RSVD1, I2S9_SDATA_OUT, SOUNDWIRE0_DAT0, 0x1040, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi2_mosi_pdd1, SPI2_DOUT, RSVD1, I2S9_SDATA_IN, RSVD3, 0x1048, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(spi2_cs0_n_pdd2, SPI2_CS0, RSVD1, I2S9_LRCK, RSVD3, 0x1050, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio21_pdd3, RSVD0, TSC_SYNC1, DMIC5_DAT, RSVD3, 0x1058, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio22_pdd4, RSVD0, RSVD1, DMIC5_CLK, RSVD3, 0x1060, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio23_pdd5, RSVD0, RSVD1, TSC_EDGE_OUT2, TSC_EDGE_OUT0C, 0x1068, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio24_pdd6, RSVD0, TSC_EDGE_OUT3, RSVD2, TSC_EDGE_OUT0D, 0x1070, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio25_pdd7, RSVD0, TSC_EDGE_OUT0, RSVD2, TSC_EDGE_OUT0A, 0x1078, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio26_pee0, RSVD0, TSC_EDGE_OUT1, RSVD2, TSC_EDGE_OUT0B, 0x1080, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio27_pee1, RSVD0, TSC_SYNC0, RSVD2, RSVD3, 0x1088, 0, Y, 5, 7, 6, 8, -1, 10, 11), + PINGROUP(soc_gpio28_pee2, L0L1_RST_OUT_N, RSVD1, RSVD2, RSVD3, 0x1090, 0, N, -1, -1, -1, -1, -1, 10, -1), + PINGROUP(soc_gpio29_pee3, L2_RST_OUT_N, RSVD1, RSVD2, RSVD3, 0x1098, 0, N, -1, -1, -1, -1, -1, 10, -1), +}; + +static const struct tegra_pinctrl_soc_data tegra264_uphy_pinctrl = { + .pins = tegra264_uphy_pins, + .npins = ARRAY_SIZE(tegra264_uphy_pins), + .functions = tegra264_functions, + .nfunctions = ARRAY_SIZE(tegra264_functions), + .groups = tegra264_uphy_groups, + .ngroups = ARRAY_SIZE(tegra264_uphy_groups), + .hsm_in_mux = false, + .schmitt_in_mux = true, + .drvtype_in_mux = true, + .sfsel_in_mux = true, +}; + +static const struct tegra_pinctrl_soc_data tegra264_main_pinctrl = { + .pins = tegra264_main_pins, + .npins = ARRAY_SIZE(tegra264_main_pins), + .functions = tegra264_functions, + .nfunctions = ARRAY_SIZE(tegra264_functions), + .groups = tegra264_main_groups, + .ngroups = ARRAY_SIZE(tegra264_main_groups), + .hsm_in_mux = false, + .schmitt_in_mux = true, + .drvtype_in_mux = true, + .sfsel_in_mux = true, +}; + +static const struct tegra_pinctrl_soc_data tegra264_aon_pinctrl = { + .pins = tegra264_aon_pins, + .npins = ARRAY_SIZE(tegra264_aon_pins), + .functions = tegra264_functions, + .nfunctions = ARRAY_SIZE(tegra264_functions), + .groups = tegra264_aon_groups, + .ngroups = ARRAY_SIZE(tegra264_aon_groups), + .hsm_in_mux = false, + .schmitt_in_mux = true, + .drvtype_in_mux = true, + .sfsel_in_mux = true, +}; + +static int tegra264_pinctrl_probe(struct platform_device *pdev) +{ + const struct tegra_pinctrl_soc_data *soc = device_get_match_data(&pdev->dev); + + return tegra_pinctrl_probe(pdev, soc); +} + +static const struct of_device_id tegra264_pinctrl_of_match[] = { + { .compatible = "nvidia,tegra264-pinmux-uphy", .data = &tegra264_uphy_pinctrl}, + { .compatible = "nvidia,tegra264-pinmux-main", .data = &tegra264_main_pinctrl}, + { .compatible = "nvidia,tegra264-pinmux-aon", .data = &tegra264_aon_pinctrl}, + { } +}; +MODULE_DEVICE_TABLE(of, tegra264_pinctrl_of_match); + +static struct platform_driver tegra264_pinctrl_driver = { + .driver = { + .name = "tegra264-pinctrl", + .of_match_table = tegra264_pinctrl_of_match, + }, + .probe = tegra264_pinctrl_probe, +}; + +static int __init tegra264_pinctrl_init(void) +{ + return platform_driver_register(&tegra264_pinctrl_driver); +} +module_init(tegra264_pinctrl_init); + +static void __exit tegra264_pinctrl_exit(void) +{ + platform_driver_unregister(&tegra264_pinctrl_driver); +} +module_exit(tegra264_pinctrl_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("NVIDIA Tegra264 pinctrl driver"); From b54e0de9e2f290a8ad962697633000ea109f5c87 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 01:01:03 +0800 Subject: [PATCH 1817/5214] pinctrl: sophgo: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Linus Walleij --- drivers/pinctrl/sophgo/pinctrl-cv18xx.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/sophgo/pinctrl-cv18xx.c b/drivers/pinctrl/sophgo/pinctrl-cv18xx.c index 59318a42690f..36e3c4f9e90f 100644 --- a/drivers/pinctrl/sophgo/pinctrl-cv18xx.c +++ b/drivers/pinctrl/sophgo/pinctrl-cv18xx.c @@ -334,13 +334,11 @@ static int cv1800_pinconf_compute_config(struct sophgo_pinctrl *pctrl, switch (param) { case PIN_CONFIG_BIAS_PULL_DOWN: - v &= ~PIN_IO_PULLDOWN; - v |= FIELD_PREP(PIN_IO_PULLDOWN, arg); + FIELD_MODIFY(PIN_IO_PULLDOWN, &v, arg); m |= PIN_IO_PULLDOWN; break; case PIN_CONFIG_BIAS_PULL_UP: - v &= ~PIN_IO_PULLUP; - v |= FIELD_PREP(PIN_IO_PULLUP, arg); + FIELD_MODIFY(PIN_IO_PULLUP, &v, arg); m |= PIN_IO_PULLUP; break; case PIN_CONFIG_DRIVE_STRENGTH_UA: @@ -348,8 +346,7 @@ static int cv1800_pinconf_compute_config(struct sophgo_pinctrl *pctrl, priv->power_cfg, arg); if (ret < 0) return ret; - v &= ~PIN_IO_DRIVE; - v |= FIELD_PREP(PIN_IO_DRIVE, ret); + FIELD_MODIFY(PIN_IO_DRIVE, &v, ret); m |= PIN_IO_DRIVE; break; case PIN_CONFIG_INPUT_SCHMITT_UV: @@ -357,21 +354,18 @@ static int cv1800_pinconf_compute_config(struct sophgo_pinctrl *pctrl, priv->power_cfg, arg); if (ret < 0) return ret; - v &= ~PIN_IO_SCHMITT; - v |= FIELD_PREP(PIN_IO_SCHMITT, ret); + FIELD_MODIFY(PIN_IO_SCHMITT, &v, ret); m |= PIN_IO_SCHMITT; break; case PIN_CONFIG_POWER_SOURCE: /* Ignore power source as it is always fixed */ break; case PIN_CONFIG_SLEW_RATE: - v &= ~PIN_IO_OUT_FAST_SLEW; - v |= FIELD_PREP(PIN_IO_OUT_FAST_SLEW, arg); + FIELD_MODIFY(PIN_IO_OUT_FAST_SLEW, &v, arg); m |= PIN_IO_OUT_FAST_SLEW; break; case PIN_CONFIG_BIAS_BUS_HOLD: - v &= ~PIN_IO_BUS_HOLD; - v |= FIELD_PREP(PIN_IO_BUS_HOLD, arg); + FIELD_MODIFY(PIN_IO_BUS_HOLD, &v, arg); m |= PIN_IO_BUS_HOLD; break; default: From cecbdf69ba4333ea66e8c227d4c1011b517de82e Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 01:01:04 +0800 Subject: [PATCH 1818/5214] pinctrl: spacemit: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Linus Walleij --- drivers/pinctrl/spacemit/pinctrl-k1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index 67de499349bb..f0b5ebd9e223 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -622,9 +622,8 @@ static int spacemit_pinconf_generate_config(struct spacemit_pinctrl *pctrl, return -EINVAL; } } else { - v &= ~PAD_SLEW_RATE; slew_rate = slew_rate > 1 ? (slew_rate - 2) : 0; - v |= FIELD_PREP(PAD_SLEW_RATE, slew_rate); + FIELD_MODIFY(PAD_SLEW_RATE, &v, slew_rate); } } From ce4e27247ca643645c6d3043aad1c1c67bf7fdac Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Mon, 11 May 2026 11:17:49 +0800 Subject: [PATCH 1819/5214] pinctrl: nuvoton: ma35d1: fix MFP register offset and pin table Each GPIO bank has two 32-bit MFP registers: MFPL covering pins 0-7 at the bank base offset, and MFPH covering pins 8-15 at base offset+4. ma35_pinctrl_parse_groups() computed the register address without accounting for this split, so any pin with an index >= 8 within its bank was written to the wrong register. Also fix the pin descriptor table in pinctrl-ma35d1.c: switch from sequential to 16-per-bank pin numbering, add missing PC8-PC11 pins and their mux options, and remove the duplicate PN10-PN15 entries. Fixes: f805e356313b ("pinctrl: nuvoton: Add ma35d1 pinctrl and GPIO driver") Signed-off-by: Joey Lu Signed-off-by: Linus Walleij --- drivers/pinctrl/nuvoton/pinctrl-ma35.c | 3 +- drivers/pinctrl/nuvoton/pinctrl-ma35d1.c | 470 +++++++++++++---------- 2 files changed, 263 insertions(+), 210 deletions(-) diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35.c b/drivers/pinctrl/nuvoton/pinctrl-ma35.c index f01344201628..dafa85c105a1 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35.c @@ -1014,7 +1014,8 @@ static int ma35_pinctrl_parse_groups(struct fwnode_handle *fwnode, struct group_ grp->data = pin; for (i = 0, j = 0; i < count; i += 3, j++) { - pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE; + pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE + + (elems[i + 1] >= 8 ? 4 : 0); pin->shift = (elems[i + 1] * MA35_MFP_BITS_PER_PORT) % 32; pin->muxval = elems[i + 2]; pin->configs = configs; diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c index eafa06ca0879..9d4627c80a52 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c @@ -113,6 +113,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x0, "GPA14"), MA35_MUX(0x2, "UART7_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "NAND_nWP"), MA35_MUX(0x7, "EBI_AD14"), MA35_MUX(0x9, "EBI_ADR14")), @@ -123,6 +124,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x3, "UART6_RXD"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x5, "CAN2_RXD"), + MA35_MUX(0x6, "USBHL0_DM"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x9, "QEI0_A"), MA35_MUX(0xb, "TM1"), @@ -187,6 +189,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH5"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI0_MOSI"), MA35_MUX(0x6, "EBI_MCLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), @@ -202,6 +205,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_BRAKE1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI0_MISO"), MA35_MUX(0x6, "I2S1_MCLK"), MA35_MUX(0x7, "CCAP1_SFIELD"), @@ -220,6 +224,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x5, "CAN2_RXD"), MA35_MUX(0x6, "I2S1_LRCK"), + MA35_MUX(0x7, "USBHL1_DM"), MA35_MUX(0x8, "ADC0_CH4"), MA35_MUX(0x9, "EBI_ADR16"), MA35_MUX(0xe, "ECAP2_IC0")), @@ -231,6 +236,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x5, "CAN2_TXD"), MA35_MUX(0x6, "I2S1_BCLK"), + MA35_MUX(0x7, "USBHL1_DP"), MA35_MUX(0x8, "ADC0_CH5"), MA35_MUX(0x9, "EBI_ADR17"), MA35_MUX(0xe, "ECAP2_IC1")), @@ -239,6 +245,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "I2C4_SDA"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "ADC0_CH6"), @@ -249,6 +256,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "I2C4_SCL"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "ADC0_CH7"), @@ -264,10 +272,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(34, PC2, 0x90, 0x8, MA35_MUX(0x0, "GPC2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x6, "SD0_DAT0/eMMC0_DAT0")), MA35_PIN(35, PC3, 0x90, 0xc, MA35_MUX(0x0, "GPC3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x6, "SD0_DAT1/eMMC0_DAT1")), MA35_PIN(36, PC4, 0x90, 0x10, MA35_MUX(0x0, "GPC4"), @@ -280,65 +290,100 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(38, PC6, 0x90, 0x18, MA35_MUX(0x0, "GPC6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "SD0_nCD")), MA35_PIN(39, PC7, 0x90, 0x1c, MA35_MUX(0x0, "GPC7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "SD0_WP")), - MA35_PIN(40, PC12, 0x94, 0x10, + MA35_PIN(40, PC8, 0x94, 0x0, + MA35_MUX(0x0, "GPC8"), + MA35_MUX(0x1, "EPWM2_CH0"), + MA35_MUX(0x2, "UART10_nCTS"), + MA35_MUX(0x3, "UART9_RXD"), + MA35_MUX(0x4, "I2C0_SDA"), + MA35_MUX(0x5, "SPI1_SS0"), + MA35_MUX(0x6, "SD0_DAT4/eMMC0_DAT4")), + MA35_PIN(41, PC9, 0x94, 0x4, + MA35_MUX(0x0, "GPC9"), + MA35_MUX(0x1, "EPWM2_CH1"), + MA35_MUX(0x2, "UART10_nRTS"), + MA35_MUX(0x3, "UART9_TXD"), + MA35_MUX(0x4, "I2C0_SCL"), + MA35_MUX(0x5, "SPI1_CLK"), + MA35_MUX(0x6, "SD0_DAT5/eMMC0_DAT5")), + MA35_PIN(42, PC10, 0x94, 0x8, + MA35_MUX(0x0, "GPC10"), + MA35_MUX(0x1, "EPWM2_CH2"), + MA35_MUX(0x2, "UART10_RXD"), + MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), + MA35_MUX(0x5, "SPI1_MOSI"), + MA35_MUX(0x6, "SD0_DAT6/eMMC0_DAT6")), + MA35_PIN(43, PC11, 0x94, 0xc, + MA35_MUX(0x0, "GPC11"), + MA35_MUX(0x1, "EPWM2_CH3"), + MA35_MUX(0x2, "UART10_TXD"), + MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), + MA35_MUX(0x5, "SPI1_MISO"), + MA35_MUX(0x6, "SD0_DAT7/eMMC0_DAT7")), + MA35_PIN(44, PC12, 0x94, 0x10, MA35_MUX(0x0, "GPC12"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART11_RXD"), MA35_MUX(0x6, "LCM_DATA16")), - MA35_PIN(41, PC13, 0x94, 0x14, + MA35_PIN(45, PC13, 0x94, 0x14, MA35_MUX(0x0, "GPC13"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART11_TXD"), MA35_MUX(0x6, "LCM_DATA17")), - MA35_PIN(42, PC14, 0x94, 0x18, + MA35_PIN(46, PC14, 0x94, 0x18, MA35_MUX(0x0, "GPC14"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x6, "LCM_DATA18")), - MA35_PIN(43, PC15, 0x94, 0x1c, + MA35_PIN(47, PC15, 0x94, 0x1c, MA35_MUX(0x0, "GPC15"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x6, "LCM_DATA19"), MA35_MUX(0x7, "LCM_MPU_TE"), MA35_MUX(0x8, "LCM_MPU_VSYNC")), - MA35_PIN(44, PD0, 0x98, 0x0, + MA35_PIN(48, PD0, 0x98, 0x0, MA35_MUX(0x0, "GPD0"), MA35_MUX(0x2, "UART3_nCTS"), MA35_MUX(0x3, "UART4_RXD"), MA35_MUX(0x5, "QSPI0_SS0")), - MA35_PIN(45, PD1, 0x98, 0x4, + MA35_PIN(49, PD1, 0x98, 0x4, MA35_MUX(0x0, "GPD1"), MA35_MUX(0x2, "UART3_nRTS"), MA35_MUX(0x3, "UART4_TXD"), MA35_MUX(0x5, "QSPI0_CLK")), - MA35_PIN(46, PD2, 0x98, 0x8, + MA35_PIN(50, PD2, 0x98, 0x8, MA35_MUX(0x0, "GPD2"), MA35_MUX(0x2, "UART3_RXD"), MA35_MUX(0x5, "QSPI0_MOSI0")), - MA35_PIN(47, PD3, 0x98, 0xc, + MA35_PIN(51, PD3, 0x98, 0xc, MA35_MUX(0x0, "GPD3"), MA35_MUX(0x2, "UART3_TXD"), MA35_MUX(0x5, "QSPI0_MISO0")), - MA35_PIN(48, PD4, 0x98, 0x10, + MA35_PIN(52, PD4, 0x98, 0x10, MA35_MUX(0x0, "GPD4"), MA35_MUX(0x2, "UART1_nCTS"), MA35_MUX(0x3, "UART2_RXD"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x5, "QSPI0_MOSI1")), - MA35_PIN(49, PD5, 0x98, 0x14, + MA35_PIN(53, PD5, 0x98, 0x14, MA35_MUX(0x0, "GPD5"), MA35_MUX(0x2, "UART1_nRTS"), MA35_MUX(0x3, "UART2_TXD"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x5, "QSPI0_MISO1")), - MA35_PIN(50, PD6, 0x98, 0x18, + MA35_PIN(54, PD6, 0x98, 0x18, MA35_MUX(0x0, "GPD6"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "QSPI1_MOSI1"), MA35_MUX(0x6, "I2C0_SDA"), MA35_MUX(0x7, "I2S0_MCLK"), @@ -346,10 +391,11 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD5"), MA35_MUX(0xa, "SPI3_SS1"), MA35_MUX(0xb, "TRACE_CLK")), - MA35_PIN(51, PD7, 0x98, 0x1c, + MA35_PIN(55, PD7, 0x98, 0x1c, MA35_MUX(0x0, "GPD7"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "QSPI1_MISO1"), MA35_MUX(0x6, "I2C0_SCL"), MA35_MUX(0x7, "I2S1_MCLK"), @@ -357,7 +403,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD6"), MA35_MUX(0xa, "SC1_nCD"), MA35_MUX(0xb, "EADC0_ST")), - MA35_PIN(52, PD8, 0x9c, 0x0, + MA35_PIN(56, PD8, 0x9c, 0x0, MA35_MUX(0x0, "GPD8"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART16_nCTS"), @@ -368,7 +414,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD7"), MA35_MUX(0xa, "SC1_CLK"), MA35_MUX(0xb, "TM0")), - MA35_PIN(53, PD9, 0x9c, 0x4, + MA35_PIN(57, PD9, 0x9c, 0x4, MA35_MUX(0x0, "GPD9"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART16_nRTS"), @@ -379,7 +425,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD8"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM0_EXT")), - MA35_PIN(54, PD10, 0x9c, 0x8, + MA35_PIN(58, PD10, 0x9c, 0x8, MA35_MUX(0x0, "GPD10"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART16_RXD"), @@ -389,7 +435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD9"), MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM2")), - MA35_PIN(55, PD11, 0x9c, 0xc, + MA35_PIN(59, PD11, 0x9c, 0xc, MA35_MUX(0x0, "GPD11"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART16_TXD"), @@ -399,7 +445,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD10"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(56, PD12, 0x9c, 0x10, + MA35_PIN(60, PD12, 0x9c, 0x10, MA35_MUX(0x0, "GPD12"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART11_TXD"), @@ -412,7 +458,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "I2S1_LRCK"), MA35_MUX(0xd, "INT1")), - MA35_PIN(57, PD13, 0x9c, 0x14, + MA35_PIN(61, PD13, 0x9c, 0x14, MA35_MUX(0x0, "GPD13"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART11_RXD"), @@ -424,11 +470,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "ECAP0_IC0"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "I2S1_BCLK")), - MA35_PIN(58, PD14, 0x9c, 0x18, + MA35_PIN(62, PD14, 0x9c, 0x18, MA35_MUX(0x0, "GPD14"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "TRACE_DATA2"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0x8, "EBI_AD6"), @@ -436,116 +483,117 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM6"), MA35_MUX(0xc, "I2S1_DI"), MA35_MUX(0xd, "INT3")), - MA35_PIN(59, PD15, 0x9c, 0x1c, + MA35_PIN(63, PD15, 0x9c, 0x1c, MA35_MUX(0x0, "GPD15"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "TRACE_DATA3"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0x9, "ECAP0_IC2"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xc, "I2S1_DO")), - MA35_PIN(60, PE0, 0xa0, 0x0, + MA35_PIN(64, PE0, 0xa0, 0x0, MA35_MUX(0x0, "GPE0"), MA35_MUX(0x2, "UART9_nCTS"), MA35_MUX(0x3, "UART8_RXD"), MA35_MUX(0x7, "CCAP1_DATA0"), MA35_MUX(0x8, "RGMII0_MDC"), MA35_MUX(0x9, "RMII0_MDC")), - MA35_PIN(61, PE1, 0xa0, 0x4, + MA35_PIN(65, PE1, 0xa0, 0x4, MA35_MUX(0x0, "GPE1"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART8_TXD"), MA35_MUX(0x7, "CCAP1_DATA1"), MA35_MUX(0x8, "RGMII0_MDIO"), MA35_MUX(0x9, "RMII0_MDIO")), - MA35_PIN(62, PE2, 0xa0, 0x8, + MA35_PIN(66, PE2, 0xa0, 0x8, MA35_MUX(0x0, "GPE2"), MA35_MUX(0x2, "UART9_RXD"), MA35_MUX(0x7, "CCAP1_DATA2"), MA35_MUX(0x8, "RGMII0_TXCTL"), MA35_MUX(0x9, "RMII0_TXEN")), - MA35_PIN(63, PE3, 0xa0, 0xc, + MA35_PIN(67, PE3, 0xa0, 0xc, MA35_MUX(0x0, "GPE3"), MA35_MUX(0x2, "UART9_TXD"), MA35_MUX(0x7, "CCAP1_DATA3"), MA35_MUX(0x8, "RGMII0_TXD0"), MA35_MUX(0x9, "RMII0_TXD0")), - MA35_PIN(64, PE4, 0xa0, 0x10, + MA35_PIN(68, PE4, 0xa0, 0x10, MA35_MUX(0x0, "GPE4"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x7, "CCAP1_DATA4"), MA35_MUX(0x8, "RGMII0_TXD1"), MA35_MUX(0x9, "RMII0_TXD1")), - MA35_PIN(65, PE5, 0xa0, 0x14, + MA35_PIN(69, PE5, 0xa0, 0x14, MA35_MUX(0x0, "GPE5"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x7, "CCAP1_DATA5"), MA35_MUX(0x8, "RGMII0_RXCLK"), MA35_MUX(0x9, "RMII0_REFCLK")), - MA35_PIN(66, PE6, 0xa0, 0x18, + MA35_PIN(70, PE6, 0xa0, 0x18, MA35_MUX(0x0, "GPE6"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x7, "CCAP1_DATA6"), MA35_MUX(0x8, "RGMII0_RXCTL"), MA35_MUX(0x9, "RMII0_CRSDV")), - MA35_PIN(67, PE7, 0xa0, 0x1c, + MA35_PIN(71, PE7, 0xa0, 0x1c, MA35_MUX(0x0, "GPE7"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x7, "CCAP1_DATA7"), MA35_MUX(0x8, "RGMII0_RXD0"), MA35_MUX(0x9, "RMII0_RXD0")), - MA35_PIN(68, PE8, 0xa4, 0x0, + MA35_PIN(72, PE8, 0xa4, 0x0, MA35_MUX(0x0, "GPE8"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x3, "UART12_RXD"), MA35_MUX(0x7, "CCAP1_SCLK"), MA35_MUX(0x8, "RGMII0_RXD1"), MA35_MUX(0x9, "RMII0_RXD1")), - MA35_PIN(69, PE9, 0xa4, 0x4, + MA35_PIN(73, PE9, 0xa4, 0x4, MA35_MUX(0x0, "GPE9"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x3, "UART12_TXD"), MA35_MUX(0x7, "CCAP1_PIXCLK"), MA35_MUX(0x8, "RGMII0_RXD2"), MA35_MUX(0x9, "RMII0_RXERR")), - MA35_PIN(70, PE10, 0xa4, 0x8, + MA35_PIN(74, PE10, 0xa4, 0x8, MA35_MUX(0x0, "GPE10"), MA35_MUX(0x2, "UART15_nCTS"), MA35_MUX(0x3, "UART14_RXD"), MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x7, "CCAP1_HSYNC"), MA35_MUX(0x8, "RGMII0_RXD3")), - MA35_PIN(71, PE11, 0xa4, 0xc, + MA35_PIN(75, PE11, 0xa4, 0xc, MA35_MUX(0x0, "GPE11"), MA35_MUX(0x2, "UART15_nRTS"), MA35_MUX(0x3, "UART14_TXD"), MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), MA35_MUX(0x8, "RGMII0_TXCLK")), - MA35_PIN(72, PE12, 0xa4, 0x10, + MA35_PIN(76, PE12, 0xa4, 0x10, MA35_MUX(0x0, "GPE12"), MA35_MUX(0x2, "UART15_RXD"), MA35_MUX(0x5, "SPI1_MOSI"), MA35_MUX(0x7, "CCAP1_DATA8"), MA35_MUX(0x8, "RGMII0_TXD2")), - MA35_PIN(73, PE13, 0xa4, 0x14, + MA35_PIN(77, PE13, 0xa4, 0x14, MA35_MUX(0x0, "GPE13"), MA35_MUX(0x2, "UART15_TXD"), MA35_MUX(0x5, "SPI1_MISO"), MA35_MUX(0x7, "CCAP1_DATA9"), MA35_MUX(0x8, "RGMII0_TXD3")), - MA35_PIN(74, PE14, 0xa4, 0x18, + MA35_PIN(78, PE14, 0xa4, 0x18, MA35_MUX(0x0, "GPE14"), MA35_MUX(0x1, "UART0_TXD")), - MA35_PIN(75, PE15, 0xa4, 0x1c, + MA35_PIN(79, PE15, 0xa4, 0x1c, MA35_MUX(0x0, "GPE15"), MA35_MUX(0x1, "UART0_RXD")), - MA35_PIN(76, PF0, 0xa8, 0x0, + MA35_PIN(80, PF0, 0xa8, 0x0, MA35_MUX(0x0, "GPF0"), MA35_MUX(0x2, "UART2_nCTS"), MA35_MUX(0x3, "UART1_RXD"), @@ -553,7 +601,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDC"), MA35_MUX(0x9, "RMII1_MDC"), MA35_MUX(0xe, "KPI_COL0")), - MA35_PIN(77, PF1, 0xa8, 0x4, + MA35_PIN(81, PF1, 0xa8, 0x4, MA35_MUX(0x0, "GPF1"), MA35_MUX(0x2, "UART2_nRTS"), MA35_MUX(0x3, "UART1_TXD"), @@ -561,21 +609,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDIO"), MA35_MUX(0x9, "RMII1_MDIO"), MA35_MUX(0xe, "KPI_COL1")), - MA35_PIN(78, PF2, 0xa8, 0x8, + MA35_PIN(82, PF2, 0xa8, 0x8, MA35_MUX(0x0, "GPF2"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x6, "RGMII0_TXD2"), MA35_MUX(0x8, "RGMII1_TXCTL"), MA35_MUX(0x9, "RMII1_TXEN"), MA35_MUX(0xe, "KPI_COL2")), - MA35_PIN(79, PF3, 0xa8, 0xc, + MA35_PIN(83, PF3, 0xa8, 0xc, MA35_MUX(0x0, "GPF3"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x6, "RGMII0_TXD3"), MA35_MUX(0x8, "RGMII1_TXD0"), MA35_MUX(0x9, "RMII1_TXD0"), MA35_MUX(0xe, "KPI_COL3")), - MA35_PIN(80, PF4, 0xa8, 0x10, + MA35_PIN(84, PF4, 0xa8, 0x10, MA35_MUX(0x0, "GPF4"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "UART10_RXD"), @@ -583,9 +631,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x8, "RGMII1_TXD1"), MA35_MUX(0x9, "RMII1_TXD1"), + MA35_MUX(0xc, "USBHL0_DM"), MA35_MUX(0xd, "CAN2_RXD"), MA35_MUX(0xe, "KPI_ROW0")), - MA35_PIN(81, PF5, 0xa8, 0x14, + MA35_PIN(85, PF5, 0xa8, 0x14, MA35_MUX(0x0, "GPF5"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "UART10_TXD"), @@ -593,9 +642,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x8, "RGMII1_RXCLK"), MA35_MUX(0x9, "RMII1_REFCLK"), + MA35_MUX(0xc, "USBHL0_DP"), MA35_MUX(0xd, "CAN2_TXD"), MA35_MUX(0xe, "KPI_ROW1")), - MA35_PIN(82, PF6, 0xa8, 0x18, + MA35_PIN(86, PF6, 0xa8, 0x18, MA35_MUX(0x0, "GPF6"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x4, "I2S0_DI"), @@ -605,7 +655,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SDA"), MA35_MUX(0xd, "SC0_CLK"), MA35_MUX(0xe, "KPI_ROW2")), - MA35_PIN(83, PF7, 0xa8, 0x1c, + MA35_PIN(87, PF7, 0xa8, 0x1c, MA35_MUX(0x0, "GPF7"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x4, "I2S0_DO"), @@ -615,7 +665,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SCL"), MA35_MUX(0xd, "SC0_DAT"), MA35_MUX(0xe, "KPI_ROW3")), - MA35_PIN(84, PF8, 0xac, 0x0, + MA35_PIN(88, PF8, 0xac, 0x0, MA35_MUX(0x0, "GPF8"), MA35_MUX(0x2, "UART13_RXD"), MA35_MUX(0x4, "I2C5_SDA"), @@ -624,7 +674,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXD1"), MA35_MUX(0xd, "SC0_RST"), MA35_MUX(0xe, "KPI_COL4")), - MA35_PIN(85, PF9, 0xac, 0x4, + MA35_PIN(89, PF9, 0xac, 0x4, MA35_MUX(0x0, "GPF9"), MA35_MUX(0x2, "UART13_TXD"), MA35_MUX(0x4, "I2C5_SCL"), @@ -633,7 +683,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXERR"), MA35_MUX(0xd, "SC0_PWR"), MA35_MUX(0xe, "KPI_COL5")), - MA35_PIN(86, PF10, 0xac, 0x8, + MA35_PIN(90, PF10, 0xac, 0x8, MA35_MUX(0x0, "GPF10"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x5, "I2S0_LRCK"), @@ -641,7 +691,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_RXD3"), MA35_MUX(0x9, "SC0_CLK"), MA35_MUX(0xe, "KPI_COL6")), - MA35_PIN(87, PF11, 0xac, 0xc, + MA35_PIN(91, PF11, 0xac, 0xc, MA35_MUX(0x0, "GPF11"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x5, "I2S0_BCLK"), @@ -649,21 +699,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_TXCLK"), MA35_MUX(0x9, "SC0_DAT"), MA35_MUX(0xe, "KPI_COL7")), - MA35_PIN(88, PF12, 0xac, 0x10, + MA35_PIN(92, PF12, 0xac, 0x10, MA35_MUX(0x0, "GPF12"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "RGMII1_TXD2"), MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xe, "KPI_ROW4")), - MA35_PIN(89, PF13, 0xac, 0x14, + MA35_PIN(93, PF13, 0xac, 0x14, MA35_MUX(0x0, "GPF13"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "RGMII1_TXD3"), MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xe, "KPI_ROW5")), - MA35_PIN(90, PF14, 0xac, 0x18, + MA35_PIN(94, PF14, 0xac, 0x18, MA35_MUX(0x0, "GPF14"), MA35_MUX(0x1, "EPWM2_BRAKE0"), MA35_MUX(0x2, "EADC0_ST"), @@ -679,10 +729,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "SPI1_SS1"), MA35_MUX(0xe, "QEI2_INDEX"), MA35_MUX(0xf, "I2S0_MCLK")), - MA35_PIN(91, PF15, 0xac, 0x1c, + MA35_PIN(95, PF15, 0xac, 0x1c, MA35_MUX(0x0, "GPF15"), MA35_MUX(0x1, "HSUSB0_VBUSVLD")), - MA35_PIN(92, PG0, 0xb0, 0x0, + MA35_PIN(96, PG0, 0xb0, 0x0, MA35_MUX(0x0, "GPG0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART7_TXD"), @@ -696,19 +746,20 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xc, "CLKO"), MA35_MUX(0xd, "INT0"), MA35_MUX(0xf, "EBI_ADR15")), - MA35_PIN(93, PG1, 0xb0, 0x4, + MA35_PIN(97, PG1, 0xb0, 0x4, MA35_MUX(0x0, "GPG1"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART6_TXD"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "CAN2_TXD"), + MA35_MUX(0x6, "USBHL0_DP"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0x9, "QEI0_B"), MA35_MUX(0xb, "TM1_EXT"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(94, PG2, 0xb0, 0x8, + MA35_PIN(98, PG2, 0xb0, 0x8, MA35_MUX(0x0, "GPG2"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART9_RXD"), @@ -719,7 +770,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_A"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xd, "INT1")), - MA35_PIN(95, PG3, 0xb0, 0xc, + MA35_PIN(99, PG3, 0xb0, 0xc, MA35_MUX(0x0, "GPG3"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART9_TXD"), @@ -731,7 +782,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_B"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "I2S1_MCLK")), - MA35_PIN(96, PG4, 0xb0, 0x10, + MA35_PIN(100, PG4, 0xb0, 0x10, MA35_MUX(0x0, "GPG4"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART5_nCTS"), @@ -745,7 +796,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xd, "INT2"), MA35_MUX(0xe, "ECAP1_IC2")), - MA35_PIN(97, PG5, 0xb0, 0x14, + MA35_PIN(101, PG5, 0xb0, 0x14, MA35_MUX(0x0, "GPG5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART5_nRTS"), @@ -757,7 +808,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_DI"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM4_EXT")), - MA35_PIN(98, PG6, 0xb0, 0x18, + MA35_PIN(102, PG6, 0xb0, 0x18, MA35_MUX(0x0, "GPG6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART5_RXD"), @@ -769,7 +820,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xd, "INT3")), - MA35_PIN(99, PG7, 0xb0, 0x1c, + MA35_PIN(103, PG7, 0xb0, 0x1c, MA35_MUX(0x0, "GPG7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART5_TXD"), @@ -780,27 +831,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_LRCK"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT")), - MA35_PIN(100, PG8, 0xb4, 0x0, + MA35_PIN(104, PG8, 0xb4, 0x0, MA35_MUX(0x0, "GPG8"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "LCM_VSYNC"), MA35_MUX(0x7, "I2C3_SDA"), MA35_MUX(0xc, "EBI_AD7"), MA35_MUX(0xd, "EBI_nCS0")), - MA35_PIN(101, PG9, 0xb4, 0x4, + MA35_PIN(105, PG9, 0xb4, 0x4, MA35_MUX(0x0, "GPG9"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "LCM_HSYNC"), MA35_MUX(0x7, "I2C3_SCL"), MA35_MUX(0xc, "EBI_AD8"), MA35_MUX(0xd, "EBI_nCS1")), - MA35_PIN(102, PG10, 0xb4, 0x8, + MA35_PIN(106, PG10, 0xb4, 0x8, MA35_MUX(0x0, "GPG10"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART13_TXD"), @@ -808,7 +861,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_CLK"), MA35_MUX(0xc, "EBI_AD9"), MA35_MUX(0xd, "EBI_nWRH")), - MA35_PIN(103, PG11, 0xb4, 0xc, + MA35_PIN(107, PG11, 0xb4, 0xc, MA35_MUX(0x0, "GPG11"), MA35_MUX(0x3, "JTAG_TDO"), MA35_MUX(0x5, "I2S0_MCLK"), @@ -816,93 +869,93 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "EBI_nWRH"), MA35_MUX(0x8, "EBI_nCS1"), MA35_MUX(0xa, "EBI_AD0")), - MA35_PIN(104, PG12, 0xb4, 0x10, + MA35_PIN(108, PG12, 0xb4, 0x10, MA35_MUX(0x0, "GPG12"), MA35_MUX(0x3, "JTAG_TCK/SW_CLK"), MA35_MUX(0x5, "I2S0_LRCK"), MA35_MUX(0x7, "EBI_nWRL"), MA35_MUX(0xa, "EBI_AD1")), - MA35_PIN(105, PG13, 0xb4, 0x14, + MA35_PIN(109, PG13, 0xb4, 0x14, MA35_MUX(0x0, "GPG13"), MA35_MUX(0x3, "JTAG_TMS/SW_DIO"), MA35_MUX(0x5, "I2S0_BCLK"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0xa, "EBI_AD2")), - MA35_PIN(106, PG14, 0xb4, 0x18, + MA35_PIN(110, PG14, 0xb4, 0x18, MA35_MUX(0x0, "GPG14"), MA35_MUX(0x3, "JTAG_TDI"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "NAND_nCS1"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0xa, "EBI_AD3")), - MA35_PIN(107, PG15, 0xb4, 0x1c, + MA35_PIN(111, PG15, 0xb4, 0x1c, MA35_MUX(0x0, "GPG15"), MA35_MUX(0x3, "JTAG_nTRST"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0xa, "EBI_AD4")), - MA35_PIN(108, PH0, 0xb8, 0x0, + MA35_PIN(112, PH0, 0xb8, 0x0, MA35_MUX(0x0, "GPH0"), MA35_MUX(0x2, "UART8_nCTS"), MA35_MUX(0x3, "UART7_RXD"), MA35_MUX(0x6, "LCM_DATA8")), - MA35_PIN(109, PH1, 0xb8, 0x4, + MA35_PIN(113, PH1, 0xb8, 0x4, MA35_MUX(0x0, "GPH1"), MA35_MUX(0x2, "UART8_nRTS"), MA35_MUX(0x3, "UART7_TXD"), MA35_MUX(0x6, "LCM_DATA9")), - MA35_PIN(110, PH2, 0xb8, 0x8, + MA35_PIN(114, PH2, 0xb8, 0x8, MA35_MUX(0x0, "GPH2"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x6, "LCM_DATA10")), - MA35_PIN(111, PH3, 0xb8, 0xc, + MA35_PIN(115, PH3, 0xb8, 0xc, MA35_MUX(0x0, "GPH3"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x6, "LCM_DATA11")), - MA35_PIN(112, PH4, 0xb8, 0x10, + MA35_PIN(116, PH4, 0xb8, 0x10, MA35_MUX(0x0, "GPH4"), MA35_MUX(0x2, "UART10_nCTS"), MA35_MUX(0x3, "UART9_RXD"), MA35_MUX(0x6, "LCM_DATA12")), - MA35_PIN(113, PH5, 0xb8, 0x14, + MA35_PIN(117, PH5, 0xb8, 0x14, MA35_MUX(0x0, "GPH5"), MA35_MUX(0x2, "UART10_nRTS"), MA35_MUX(0x3, "UART9_TXD"), MA35_MUX(0x6, "LCM_DATA13")), - MA35_PIN(114, PH6, 0xb8, 0x18, + MA35_PIN(118, PH6, 0xb8, 0x18, MA35_MUX(0x0, "GPH6"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x6, "LCM_DATA14")), - MA35_PIN(115, PH7, 0xb8, 0x1c, + MA35_PIN(119, PH7, 0xb8, 0x1c, MA35_MUX(0x0, "GPH7"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x6, "LCM_DATA15")), - MA35_PIN(116, PH8, 0xbc, 0x0, + MA35_PIN(120, PH8, 0xbc, 0x0, MA35_MUX(0x0, "GPH8"), MA35_MUX(0x6, "TAMPER0")), - MA35_PIN(117, PH9, 0xbc, 0x4, + MA35_PIN(121, PH9, 0xbc, 0x4, MA35_MUX(0x0, "GPH9"), MA35_MUX(0x4, "CLK_32KOUT"), MA35_MUX(0x6, "TAMPER1")), - MA35_PIN(118, PH12, 0xbc, 0x10, + MA35_PIN(124, PH12, 0xbc, 0x10, MA35_MUX(0x0, "GPH12"), MA35_MUX(0x2, "UART14_nCTS"), MA35_MUX(0x3, "UART13_RXD"), MA35_MUX(0x6, "LCM_DATA20")), - MA35_PIN(119, PH13, 0xbc, 0x14, + MA35_PIN(125, PH13, 0xbc, 0x14, MA35_MUX(0x0, "GPH13"), MA35_MUX(0x2, "UART14_nRTS"), MA35_MUX(0x3, "UART13_TXD"), MA35_MUX(0x6, "LCM_DATA21")), - MA35_PIN(120, PH14, 0xbc, 0x18, + MA35_PIN(126, PH14, 0xbc, 0x18, MA35_MUX(0x0, "GPH14"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x6, "LCM_DATA22")), - MA35_PIN(121, PH15, 0xbc, 0x1c, + MA35_PIN(127, PH15, 0xbc, 0x1c, MA35_MUX(0x0, "GPH15"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x6, "LCM_DATA23")), - MA35_PIN(122, PI0, 0xc0, 0x0, + MA35_PIN(128, PI0, 0xc0, 0x0, MA35_MUX(0x0, "GPI0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART12_nCTS"), @@ -913,7 +966,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR0"), MA35_MUX(0xb, "TM0"), MA35_MUX(0xc, "ECAP1_IC0")), - MA35_PIN(123, PI1, 0xc0, 0x4, + MA35_PIN(129, PI1, 0xc0, 0x4, MA35_MUX(0x0, "GPI1"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -924,26 +977,28 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR1"), MA35_MUX(0xb, "TM0_EXT"), MA35_MUX(0xc, "ECAP1_IC1")), - MA35_PIN(124, PI2, 0xc0, 0x8, + MA35_PIN(130, PI2, 0xc0, 0x8, MA35_MUX(0x0, "GPI2"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x7, "SC0_DAT"), MA35_MUX(0x8, "EBI_ADR2"), MA35_MUX(0xb, "TM1"), MA35_MUX(0xc, "ECAP1_IC2")), - MA35_PIN(125, PI3, 0xc0, 0xc, + MA35_PIN(131, PI3, 0xc0, 0xc, MA35_MUX(0x0, "GPI3"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC0_RST"), MA35_MUX(0x8, "EBI_ADR3"), MA35_MUX(0xb, "TM1_EXT")), - MA35_PIN(126, PI4, 0xc0, 0x10, + MA35_PIN(132, PI4, 0xc0, 0x10, MA35_MUX(0x0, "GPI4"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_nCTS"), @@ -953,7 +1008,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_LRCK"), MA35_MUX(0x8, "EBI_ADR4"), MA35_MUX(0xd, "INT0")), - MA35_PIN(127, PI5, 0xc0, 0x14, + MA35_PIN(133, PI5, 0xc0, 0x14, MA35_MUX(0x0, "GPI5"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_nRTS"), @@ -962,65 +1017,67 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_BCLK"), MA35_MUX(0x8, "EBI_ADR5"), MA35_MUX(0xd, "INT1")), - MA35_PIN(128, PI6, 0xc0, 0x18, + MA35_PIN(134, PI6, 0xc0, 0x18, MA35_MUX(0x0, "GPI6"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "EBI_ADR6"), MA35_MUX(0xc, "QEI1_INDEX"), MA35_MUX(0xd, "INT2")), - MA35_PIN(129, PI7, 0xc0, 0x1c, + MA35_PIN(135, PI7, 0xc0, 0x1c, MA35_MUX(0x0, "GPI7"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "EBI_ADR7"), MA35_MUX(0xc, "ECAP0_IC0"), MA35_MUX(0xd, "INT3")), - MA35_PIN(130, PI8, 0xc4, 0x0, + MA35_PIN(136, PI8, 0xc4, 0x0, MA35_MUX(0x0, "GPI8"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x6, "LCM_DATA0"), MA35_MUX(0xc, "EBI_AD11")), - MA35_PIN(131, PI9, 0xc4, 0x4, + MA35_PIN(137, PI9, 0xc4, 0x4, MA35_MUX(0x0, "GPI9"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x6, "LCM_DATA1"), MA35_MUX(0xc, "EBI_AD12")), - MA35_PIN(132, PI10, 0xc4, 0x8, + MA35_PIN(138, PI10, 0xc4, 0x8, MA35_MUX(0x0, "GPI10"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x6, "LCM_DATA2"), MA35_MUX(0xc, "EBI_AD13")), - MA35_PIN(133, PI11, 0xC4, 0xc, + MA35_PIN(139, PI11, 0xC4, 0xc, MA35_MUX(0x0, "GPI11"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x6, "LCM_DATA3"), MA35_MUX(0xc, "EBI_AD14")), - MA35_PIN(134, PI12, 0xc4, 0x10, + MA35_PIN(140, PI12, 0xc4, 0x10, MA35_MUX(0x0, "GPI12"), MA35_MUX(0x2, "UART6_nCTS"), MA35_MUX(0x3, "UART5_RXD"), MA35_MUX(0x6, "LCM_DATA4")), - MA35_PIN(135, PI13, 0xc4, 0x14, + MA35_PIN(141, PI13, 0xc4, 0x14, MA35_MUX(0x0, "GPI13"), MA35_MUX(0x2, "UART6_nRTS"), MA35_MUX(0x3, "UART5_TXD"), MA35_MUX(0x6, "LCM_DATA5")), - MA35_PIN(136, PI14, 0xc4, 0x18, + MA35_PIN(142, PI14, 0xc4, 0x18, MA35_MUX(0x0, "GPI14"), MA35_MUX(0x2, "UART6_RXD"), MA35_MUX(0x6, "LCM_DATA6")), - MA35_PIN(137, PI15, 0xc4, 0x1c, + MA35_PIN(143, PI15, 0xc4, 0x1c, MA35_MUX(0x0, "GPI15"), MA35_MUX(0x2, "UART6_TXD"), MA35_MUX(0x6, "LCM_DATA7")), - MA35_PIN(138, PJ0, 0xc8, 0x0, + MA35_PIN(144, PJ0, 0xc8, 0x0, MA35_MUX(0x0, "GPJ0"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART8_nCTS"), @@ -1034,7 +1091,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR16"), MA35_MUX(0xb, "EBI_nCS0"), MA35_MUX(0xc, "EBI_AD7")), - MA35_PIN(139, PJ1, 0xc8, 0x4, + MA35_PIN(145, PJ1, 0xc8, 0x4, MA35_MUX(0x0, "GPJ1"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART8_nRTS"), @@ -1048,11 +1105,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR17"), MA35_MUX(0xb, "EBI_nCS1"), MA35_MUX(0xc, "EBI_AD8")), - MA35_PIN(140, PJ2, 0xc8, 0x8, + MA35_PIN(146, PJ2, 0xc8, 0x8, MA35_MUX(0x0, "GPJ2"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x6, "eMMC1_DAT6"), MA35_MUX(0x7, "I2S0_DI"), @@ -1061,11 +1119,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR18"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "EBI_AD9")), - MA35_PIN(141, PJ3, 0xc8, 0xc, + MA35_PIN(147, PJ3, 0xc8, 0xc, MA35_MUX(0x0, "GPJ3"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x6, "eMMC1_DAT7"), MA35_MUX(0x7, "I2S0_DO"), @@ -1074,39 +1133,43 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR19"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "EBI_AD10")), - MA35_PIN(142, PJ4, 0xc8, 0x10, + MA35_PIN(148, PJ4, 0xc8, 0x10, MA35_MUX(0x0, "GPJ4"), MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x6, "SD1_WP")), - MA35_PIN(143, PJ5, 0xc8, 0x14, + MA35_PIN(149, PJ5, 0xc8, 0x14, MA35_MUX(0x0, "GPJ5"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "SD1_nCD")), - MA35_PIN(144, PJ6, 0xc8, 0x18, + MA35_PIN(150, PJ6, 0xc8, 0x18, MA35_MUX(0x0, "GPJ6"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "SD1_CMD/eMMC1_CMD")), - MA35_PIN(145, PJ7, 0xc8, 0x1c, + MA35_PIN(151, PJ7, 0xc8, 0x1c, MA35_MUX(0x0, "GPJ7"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "SD1_CLK/eMMC1_CLK")), - MA35_PIN(146, PJ8, 0xcc, 0x0, + MA35_PIN(152, PJ8, 0xcc, 0x0, MA35_MUX(0x0, "GPJ8"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "SD1_DAT0/eMMC1_DAT0")), - MA35_PIN(147, PJ9, 0xcc, 0x4, + MA35_PIN(153, PJ9, 0xcc, 0x4, MA35_MUX(0x0, "GPJ9"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x6, "SD1_DAT1/eMMC1_DAT1")), - MA35_PIN(148, PJ10, 0xcc, 0x8, + MA35_PIN(154, PJ10, 0xcc, 0x8, MA35_MUX(0x0, "GPJ10"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "SD1_DAT2/eMMC1_DAT2")), - MA35_PIN(149, PJ11, 0xcc, 0xc, + MA35_PIN(155, PJ11, 0xcc, 0xc, MA35_MUX(0x0, "GPJ11"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "SD1_DAT3/eMMC1_DAT3")), - MA35_PIN(150, PJ12, 0xcc, 0x10, + MA35_PIN(156, PJ12, 0xcc, 0x10, MA35_MUX(0x0, "GPJ12"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1117,7 +1180,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR12"), MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(151, PJ13, 0xcc, 0x14, + MA35_PIN(157, PJ13, 0xcc, 0x14, MA35_MUX(0x0, "GPJ13"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1127,27 +1190,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "SC1_DAT"), MA35_MUX(0x8, "EBI_ADR13"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(152, PJ14, 0xcc, 0x18, + MA35_PIN(158, PJ14, 0xcc, 0x18, MA35_MUX(0x0, "GPJ14"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC1_RST"), MA35_MUX(0x8, "EBI_ADR14"), MA35_MUX(0xb, "TM3")), - MA35_PIN(153, PJ15, 0xcc, 0x1c, + MA35_PIN(159, PJ15, 0xcc, 0x1c, MA35_MUX(0x0, "GPJ15"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI3_CLK"), MA35_MUX(0x6, "EADC0_ST"), MA35_MUX(0x7, "SC1_PWR"), MA35_MUX(0x8, "EBI_ADR15"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(154, PK0, 0xd0, 0x0, + MA35_PIN(160, PK0, 0xd0, 0x0, MA35_MUX(0x0, "GPK0"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART16_nCTS"), @@ -1157,7 +1222,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR8"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(155, PK1, 0xd0, 0x4, + MA35_PIN(161, PK1, 0xd0, 0x4, MA35_MUX(0x0, "GPK1"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART16_nRTS"), @@ -1167,25 +1232,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR9"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(156, PK2, 0xd0, 0x8, + MA35_PIN(162, PK2, 0xd0, 0x8, MA35_MUX(0x0, "GPK2"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART16_RXD"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x7, "SC0_PWR"), MA35_MUX(0x8, "EBI_ADR10"), MA35_MUX(0xc, "QEI0_A")), - MA35_PIN(157, PK3, 0xd0, 0xc, + MA35_PIN(163, PK3, 0xd0, 0xc, MA35_MUX(0x0, "GPK3"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART16_TXD"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x7, "SC1_nCD"), MA35_MUX(0x8, "EBI_ADR11"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(158, PK4, 0xd0, 0x10, + MA35_PIN(164, PK4, 0xd0, 0x10, MA35_MUX(0x0, "GPK4"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART13_RXD"), @@ -1193,7 +1260,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_DEN"), MA35_MUX(0xc, "EBI_AD10"), MA35_MUX(0xd, "EBI_nWRL")), - MA35_PIN(159, PK5, 0xd0, 0x14, + MA35_PIN(165, PK5, 0xd0, 0x14, MA35_MUX(0x0, "GPK5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -1205,28 +1272,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EADC0_ST"), MA35_MUX(0xb, "TM8_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(160, PK6, 0xd0, 0x18, + MA35_PIN(166, PK6, 0xd0, 0x18, MA35_MUX(0x0, "GPK6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x7, "I2S1_BCLK"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xb, "TM6"), MA35_MUX(0xd, "INT2")), - MA35_PIN(161, PK7, 0xd0, 0x1c, + MA35_PIN(167, PK7, 0xd0, 0x1c, MA35_MUX(0x0, "GPK7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x7, "I2S1_LRCK"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0x9, "CLKO"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xd, "INT3")), - MA35_PIN(162, PK8, 0xd4, 0x0, + MA35_PIN(168, PK8, 0xd4, 0x0, MA35_MUX(0x0, "GPK8"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x4, "I2C3_SDA"), @@ -1237,25 +1306,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR15"), MA35_MUX(0xb, "TM8"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(163, PK9, 0xd4, 0x4, + MA35_PIN(169, PK9, 0xd4, 0x4, MA35_MUX(0x0, "GPK9"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "CCAP0_SCLK"), MA35_MUX(0x8, "EBI_AD0"), MA35_MUX(0xa, "EBI_ADR0")), - MA35_PIN(164, PK10, 0xd4, 0x8, + MA35_PIN(170, PK10, 0xd4, 0x8, MA35_MUX(0x0, "GPK10"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "CCAP0_PIXCLK"), MA35_MUX(0x8, "EBI_AD1"), MA35_MUX(0xa, "EBI_ADR1")), - MA35_PIN(165, PK11, 0xd4, 0xc, + MA35_PIN(171, PK11, 0xd4, 0xc, MA35_MUX(0x0, "GPK11"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "CCAP0_HSYNC"), MA35_MUX(0x8, "EBI_AD2"), MA35_MUX(0xa, "EBI_ADR2")), - MA35_PIN(166, PK12, 0xd4, 0x10, + MA35_PIN(172, PK12, 0xd4, 0x10, MA35_MUX(0x0, "GPK12"), MA35_MUX(0x1, "EPWM2_CH0"), MA35_MUX(0x2, "UART1_nCTS"), @@ -1266,7 +1337,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "SC0_CLK"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT2")), - MA35_PIN(167, PK13, 0xd4, 0x14, + MA35_PIN(173, PK13, 0xd4, 0x14, MA35_MUX(0x0, "GPK13"), MA35_MUX(0x1, "EPWM2_CH1"), MA35_MUX(0x2, "UART1_nRTS"), @@ -1276,28 +1347,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "SPI1_CLK"), MA35_MUX(0x8, "SC0_DAT"), MA35_MUX(0xb, "TM10_EXT")), - MA35_PIN(168, PK14, 0xd4, 0x18, + MA35_PIN(174, PK14, 0xd4, 0x18, MA35_MUX(0x0, "GPK14"), MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART1_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xa, "I2C5_SDA"), MA35_MUX(0xb, "TM11"), MA35_MUX(0xd, "INT3")), - MA35_PIN(169, PK15, 0xd4, 0x1c, + MA35_PIN(175, PK15, 0xd4, 0x1c, MA35_MUX(0x0, "GPK15"), MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART1_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0xa, "I2C5_SCL"), MA35_MUX(0xb, "TM11_EXT")), - MA35_PIN(170, PL0, 0xd8, 0x0, + MA35_PIN(176, PL0, 0xd8, 0x0, MA35_MUX(0x0, "GPL0"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART11_nCTS"), @@ -1310,7 +1383,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_CLK"), MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "QEI1_A")), - MA35_PIN(171, PL1, 0xd8, 0x4, + MA35_PIN(177, PL1, 0xd8, 0x4, MA35_MUX(0x0, "GPL1"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART11_nRTS"), @@ -1323,11 +1396,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_DAT"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "QEI1_B")), - MA35_PIN(172, PL2, 0xd8, 0x8, + MA35_PIN(178, PL2, 0xd8, 0x8, MA35_MUX(0x0, "GPL2"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "QSPI1_SS1"), MA35_MUX(0x7, "I2S0_DI"), @@ -1335,11 +1409,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(173, PL3, 0xd8, 0xc, + MA35_PIN(179, PL3, 0xd8, 0xc, MA35_MUX(0x0, "GPL3"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "QSPI1_CLK"), MA35_MUX(0x7, "I2S0_DO"), @@ -1347,7 +1422,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC0")), - MA35_PIN(174, PL4, 0xd8, 0x10, + MA35_PIN(180, PL4, 0xd8, 0x10, MA35_MUX(0x0, "GPL4"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1360,7 +1435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_nCD"), MA35_MUX(0xb, "TM9"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(175, PL5, 0xd8, 0x14, + MA35_PIN(181, PL5, 0xd8, 0x14, MA35_MUX(0x0, "GPL5"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1373,28 +1448,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_nCD"), MA35_MUX(0xb, "TM9_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(176, PL6, 0xd8, 0x18, + MA35_PIN(182, PL6, 0xd8, 0x18, MA35_MUX(0x0, "GPL6"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "QSPI1_MOSI1"), MA35_MUX(0x7, "TRACE_CLK"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xc, "ECAP1_IC0"), MA35_MUX(0xd, "INT0")), - MA35_PIN(177, PL7, 0xd8, 0x1c, + MA35_PIN(183, PL7, 0xd8, 0x1c, MA35_MUX(0x0, "GPL7"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "QSPI1_MISO1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "ECAP1_IC1"), MA35_MUX(0xd, "INT1")), - MA35_PIN(178, PL8, 0xdc, 0x0, + MA35_PIN(184, PL8, 0xdc, 0x0, MA35_MUX(0x0, "GPL8"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART14_nCTS"), @@ -1408,7 +1485,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xc, "ECAP1_IC2"), MA35_MUX(0xd, "INT2")), - MA35_PIN(179, PL9, 0xdc, 0x4, + MA35_PIN(185, PL9, 0xdc, 0x4, MA35_MUX(0x0, "GPL9"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART14_nRTS"), @@ -1422,11 +1499,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4_EXT"), MA35_MUX(0xc, "QEI0_A"), MA35_MUX(0xd, "INT3")), - MA35_PIN(180, PL10, 0xdc, 0x8, + MA35_PIN(186, PL10, 0xdc, 0x8, MA35_MUX(0x0, "GPL10"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x6, "EPWM0_CH5"), MA35_MUX(0x7, "I2S1_DI"), @@ -1434,11 +1512,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(181, PL11, 0xdc, 0xc, + MA35_PIN(187, PL11, 0xdc, 0xc, MA35_MUX(0x0, "GPL11"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x6, "EPWM1_CH5"), MA35_MUX(0x7, "I2S1_DO"), @@ -1446,7 +1525,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(182, PL12, 0xdc, 0x10, + MA35_PIN(188, PL12, 0xdc, 0x10, MA35_MUX(0x0, "GPL12"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART7_nCTS"), @@ -1463,7 +1542,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD11"), MA35_MUX(0xe, "RGMII0_PPS"), MA35_MUX(0xf, "RMII0_PPS")), - MA35_PIN(183, PL13, 0xdc, 0x14, + MA35_PIN(189, PL13, 0xdc, 0x14, MA35_MUX(0x0, "GPL13"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART7_nRTS"), @@ -1480,7 +1559,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD12"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(184, PL14, 0xdc, 0x18, + MA35_PIN(190, PL14, 0xdc, 0x18, MA35_MUX(0x0, "GPL14"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART7_RXD"), @@ -1492,7 +1571,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "INT0"), MA35_MUX(0xd, "EBI_AD13")), - MA35_PIN(185, PL15, 0xdc, 0x1c, + MA35_PIN(191, PL15, 0xdc, 0x1c, MA35_MUX(0x0, "GPL15"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART7_TXD"), @@ -1505,86 +1584,92 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2_EXT"), MA35_MUX(0xc, "INT2"), MA35_MUX(0xd, "EBI_AD14")), - MA35_PIN(186, PM0, 0xe0, 0x0, + MA35_PIN(192, PM0, 0xe0, 0x0, MA35_MUX(0x0, "GPM0"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "CCAP0_VSYNC"), MA35_MUX(0x8, "EBI_AD3"), MA35_MUX(0xa, "EBI_ADR3")), - MA35_PIN(187, PM1, 0xe0, 0x4, + MA35_PIN(193, PM1, 0xe0, 0x4, MA35_MUX(0x0, "GPM1"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x6, "CCAP0_SFIELD"), MA35_MUX(0x8, "EBI_AD4"), MA35_MUX(0xa, "EBI_ADR4")), - MA35_PIN(188, PM2, 0xe0, 0x8, + MA35_PIN(194, PM2, 0xe0, 0x8, MA35_MUX(0x0, "GPM2"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP0_DATA0"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xa, "EBI_ADR5")), - MA35_PIN(189, PM3, 0xe0, 0xc, + MA35_PIN(195, PM3, 0xe0, 0xc, MA35_MUX(0x0, "GPM3"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP0_DATA1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xa, "EBI_ADR6")), - MA35_PIN(190, PM4, 0xe0, 0x10, + MA35_PIN(196, PM4, 0xe0, 0x10, MA35_MUX(0x0, "GPM4"), MA35_MUX(0x4, "I2C5_SDA"), MA35_MUX(0x6, "CCAP0_DATA2"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0xa, "EBI_ADR7")), - MA35_PIN(191, PM5, 0xe0, 0x14, + MA35_PIN(197, PM5, 0xe0, 0x14, MA35_MUX(0x0, "GPM5"), MA35_MUX(0x4, "I2C5_SCL"), MA35_MUX(0x6, "CCAP0_DATA3"), MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0xa, "EBI_ADR8")), - MA35_PIN(192, PM6, 0xe0, 0x18, + MA35_PIN(198, PM6, 0xe0, 0x18, MA35_MUX(0x0, "GPM6"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP0_DATA4"), MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0xa, "EBI_ADR9")), - MA35_PIN(193, PM7, 0xe0, 0x1c, + MA35_PIN(199, PM7, 0xe0, 0x1c, MA35_MUX(0x0, "GPM7"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP0_DATA5"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0xa, "EBI_ADR10")), - MA35_PIN(194, PM8, 0xe4, 0x0, + MA35_PIN(200, PM8, 0xe4, 0x0, MA35_MUX(0x0, "GPM8"), MA35_MUX(0x4, "I2C0_SDA"), MA35_MUX(0x6, "CCAP0_DATA6"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0xa, "EBI_ADR11")), - MA35_PIN(195, PM9, 0xe4, 0x4, + MA35_PIN(201, PM9, 0xe4, 0x4, MA35_MUX(0x0, "GPM9"), MA35_MUX(0x4, "I2C0_SCL"), MA35_MUX(0x6, "CCAP0_DATA7"), MA35_MUX(0x8, "EBI_AD12"), MA35_MUX(0xa, "EBI_ADR12")), - MA35_PIN(196, PM10, 0xe4, 0x8, + MA35_PIN(202, PM10, 0xe4, 0x8, MA35_MUX(0x0, "GPM10"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_SS0"), MA35_MUX(0x6, "CCAP0_DATA8"), MA35_MUX(0x7, "SPI2_I2SMCLK"), MA35_MUX(0x8, "EBI_AD13"), MA35_MUX(0xa, "EBI_ADR13")), - MA35_PIN(197, PM11, 0xe4, 0xc, + MA35_PIN(203, PM11, 0xe4, 0xc, MA35_MUX(0x0, "GPM11"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x6, "CCAP0_DATA9"), MA35_MUX(0x7, "SPI2_SS1"), MA35_MUX(0x8, "EBI_AD14"), MA35_MUX(0xa, "EBI_ADR14")), - MA35_PIN(198, PM12, 0xe4, 0x10, + MA35_PIN(204, PM12, 0xe4, 0x10, MA35_MUX(0x0, "GPM12"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART10_nCTS"), @@ -1595,7 +1680,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0x9, "I2S1_MCLK"), MA35_MUX(0xb, "TM8")), - MA35_PIN(199, PM13, 0xe4, 0x14, + MA35_PIN(205, PM13, 0xe4, 0x14, MA35_MUX(0x0, "GPM13"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART10_nRTS"), @@ -1605,99 +1690,66 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0x9, "ECAP1_IC0"), MA35_MUX(0xb, "TM8_EXT")), - MA35_PIN(200, PM14, 0xe4, 0x18, + MA35_PIN(206, PM14, 0xe4, 0x18, MA35_MUX(0x0, "GPM14"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x3, "TRACE_DATA2"), MA35_MUX(0x4, "CAN2_RXD"), + MA35_MUX(0x5, "USBHL3_DM"), MA35_MUX(0x6, "I2C3_SDA"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0x9, "ECAP1_IC1"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT1")), - MA35_PIN(201, PM15, 0xe4, 0x1c, + MA35_PIN(207, PM15, 0xe4, 0x1c, MA35_MUX(0x0, "GPM15"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x3, "TRACE_DATA3"), MA35_MUX(0x4, "CAN2_TXD"), + MA35_MUX(0x5, "USBHL3_DP"), MA35_MUX(0x6, "I2C3_SCL"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0x9, "ECAP1_IC2"), MA35_MUX(0xb, "TM10_EXT"), MA35_MUX(0xd, "INT2")), - MA35_PIN(202, PN0, 0xe8, 0x0, + MA35_PIN(208, PN0, 0xe8, 0x0, MA35_MUX(0x0, "GPN0"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x6, "CCAP1_DATA0")), - MA35_PIN(203, PN1, 0xe8, 0x4, + MA35_PIN(209, PN1, 0xe8, 0x4, MA35_MUX(0x0, "GPN1"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x6, "CCAP1_DATA1")), - MA35_PIN(204, PN2, 0xe8, 0x8, + MA35_PIN(210, PN2, 0xe8, 0x8, MA35_MUX(0x0, "GPN2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP1_DATA2")), - MA35_PIN(205, PN3, 0xe8, 0xc, + MA35_PIN(211, PN3, 0xe8, 0xc, MA35_MUX(0x0, "GPN3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP1_DATA3")), - MA35_PIN(206, PN4, 0xe8, 0x10, + MA35_PIN(212, PN4, 0xe8, 0x10, MA35_MUX(0x0, "GPN4"), MA35_MUX(0x4, "I2C1_SDA"), MA35_MUX(0x6, "CCAP1_DATA4")), - MA35_PIN(207, PN5, 0xe8, 0x14, + MA35_PIN(213, PN5, 0xe8, 0x14, MA35_MUX(0x0, "GPN5"), MA35_MUX(0x4, "I2C1_SCL"), MA35_MUX(0x6, "CCAP1_DATA5")), - MA35_PIN(208, PN6, 0xe8, 0x18, + MA35_PIN(214, PN6, 0xe8, 0x18, MA35_MUX(0x0, "GPN6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP1_DATA6")), - MA35_PIN(209, PN7, 0xe8, 0x1c, + MA35_PIN(215, PN7, 0xe8, 0x1c, MA35_MUX(0x0, "GPN7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP1_DATA7")), - MA35_PIN(210, PN10, 0xec, 0x8, - MA35_MUX(0x0, "GPN10"), - MA35_MUX(0x3, "CAN2_RXD"), - MA35_MUX(0x6, "CCAP1_SCLK")), - MA35_PIN(211, PN11, 0xec, 0xc, - MA35_MUX(0x0, "GPN11"), - MA35_MUX(0x3, "CAN2_TXD"), - MA35_MUX(0x6, "CCAP1_PIXCLK")), - MA35_PIN(212, PN12, 0xec, 0x10, - MA35_MUX(0x0, "GPN12"), - MA35_MUX(0x2, "UART6_nCTS"), - MA35_MUX(0x3, "UART12_RXD"), - MA35_MUX(0x4, "I2C5_SDA"), - MA35_MUX(0x6, "CCAP1_HSYNC")), - MA35_PIN(213, PN13, 0xec, 0x14, - MA35_MUX(0x0, "GPN13"), - MA35_MUX(0x2, "UART6_nRTS"), - MA35_MUX(0x3, "UART12_TXD"), - MA35_MUX(0x4, "I2C5_SCL"), - MA35_MUX(0x6, "CCAP1_VSYNC")), - MA35_PIN(214, PN14, 0xec, 0x18, - MA35_MUX(0x0, "GPN14"), - MA35_MUX(0x2, "UART6_RXD"), - MA35_MUX(0x3, "CAN3_RXD"), - MA35_MUX(0x5, "SPI1_SS1"), - MA35_MUX(0x6, "CCAP1_SFIELD"), - MA35_MUX(0x7, "SPI1_I2SMCLK")), - MA35_PIN(215, PN15, 0xec, 0x1c, - MA35_MUX(0x0, "GPN15"), - MA35_MUX(0x1, "EPWM2_CH4"), - MA35_MUX(0x2, "UART6_TXD"), - MA35_MUX(0x3, "CAN3_TXD"), - MA35_MUX(0x5, "I2S0_MCLK"), - MA35_MUX(0x6, "SPI1_SS1"), - MA35_MUX(0x7, "SPI1_I2SMCLK"), - MA35_MUX(0x8, "SC0_nCD"), - MA35_MUX(0x9, "EADC0_ST"), - MA35_MUX(0xa, "CLKO"), - MA35_MUX(0xb, "TM6")), MA35_PIN(216, PN8, 0xec, 0x0, MA35_MUX(0x0, "GPN8"), MA35_MUX(0x1, "EPWM2_CH4"), From 1cb73b83ab6dec8159d0280345a46fbb282c378f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:52 +0100 Subject: [PATCH 1820/5214] pinctrl: cs42l43: Fix leaked pm reference on error path Returning directly if the regmap_update_bits() fails causes a pm runtime reference to be leaked, let things run to the end of the function instead. Fixes: e52c741907fb ("pinctrl: cirrus: cs42l43: use new GPIO line value setter callbacks") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 227c37c360e1..3cc183520607 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -499,12 +499,10 @@ static int cs42l43_gpio_set(struct gpio_chip *chip, unsigned int offset, ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1, BIT(shift), value << shift); - if (ret) - return ret; pm_runtime_put(priv->dev); - return 0; + return ret; } static int cs42l43_gpio_direction_out(struct gpio_chip *chip, From 9da52ee80aee3ab3c69208bd1cfbb4be01371214 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:53 +0100 Subject: [PATCH 1821/5214] pinctrl: cs42l43: Fix polarity on debounce The debounce bit sets a bypass on the debounce rather than enabling it, as such the current polarity of the debounce is set incorrectly. Invert the polarity to correct this. Fixes: d5282a539297 ("pinctrl: cs42l43: Add support for the cs42l43") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 3cc183520607..305233fc1987 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -343,7 +343,7 @@ static int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin, return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2, CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin, - !!us << pin); + !us << pin); } static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev, From 6000eab4515a2a381557a48c35e248a3418f2bc6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:44 +0200 Subject: [PATCH 1822/5214] mtd: spi-nor: Add steps for testing locking support As recently raised on the mailing list, it may be useful to propose a list of steps to go through in order to prove the devices have been described correctly, especially since all the block protection information is not stored in any kind of table and is instead filled manually by developers. Use the debugfs output to ease the comparison between expectations and reality. [rdunlap@infradead.org: fix build warning] Link: https://lore.kernel.org/linux-mtd/20260526172341.773398-1-rdunlap@infradead.org/ Signed-off-by: Miquel Raynal Signed-off-by: Randy Dunlap Signed-off-by: Pratyush Yadav --- Documentation/driver-api/mtd/spi-nor.rst | 133 +++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/Documentation/driver-api/mtd/spi-nor.rst b/Documentation/driver-api/mtd/spi-nor.rst index 148fa4288760..f817a9193731 100644 --- a/Documentation/driver-api/mtd/spi-nor.rst +++ b/Documentation/driver-api/mtd/spi-nor.rst @@ -203,3 +203,136 @@ section, after the ``---`` marker. mtd.writesize = 1 mtd.oobsize = 0 regions = 0 + +5) If your flash supports locking, please go through the following test + procedure to make sure it correctly behaves. The below example + expects the typical situation where eraseblocks and lock sectors have + the same size. In case you enabled MTD_SPI_NOR_USE_4K_SECTORS, you + must adapt `bs` accordingly. + + Warning: These tests may hard lock your device! Make sure: + + - The device is not hard locked already (#WP strapped to low and + SR_SRWD bit set) + - If you have a WPn pin, you may want to set `no-wp` in your DT for + the time of the test, to only make use of software protection. + Otherwise, clearing the locking state depends on the WPn + signal and if it is tied to low, the flash will be permanently + locked. + + Test full chip locking and make sure expectations, the MEMISLOCKED + ioctl output, the debugfs output and experimental results are all + aligned:: + + root@1:~# alias show_sectors='grep -A4 "locked sectors" /sys/kernel/debug/spi-nor/spi0.0/params' + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -i /dev/mtd0 + Device: /dev/mtd0 + Start: 0 + Len: 0x4000000 + Lock status: unlocked + Return code: 0 + root@1:~# mtd_debug erase /dev/mtd0 0 2097152 + Erased 2097152 bytes from address 0x00000000 in flash + root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test + Copied 2097152 bytes from spi_test to address 0x00000000 in flash + root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read + Copied 2097152 bytes from address 0x00000000 in flash to spi_read + root@1:~# sha256sum spi* + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03ffffff | unlocked | 1024 + + root@1:~# flash_lock -l /dev/mtd0 + root@1:~# flash_lock -i /dev/mtd0 + Device: /dev/mtd0 + Start: 0 + Len: 0x4000000 + Lock status: locked + Return code: 1 + root@1:~# mtd_debug erase /dev/mtd0 0 2097152 + Erased 2097152 bytes from address 0x00000000 in flash + root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read + Copied 2097152 bytes from address 0x00000000 in flash to spi_read + root@1:~# sha256sum spi* + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test + root@1:~# dd if=/dev/urandom of=./spi_test2 bs=1M count=2 + 2+0 records in + 2+0 records out + root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test2 + Copied 2097152 bytes from spi_test2 to address 0x00000000 in flash + root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read2 + Copied 2097152 bytes from address 0x00000000 in flash to spi_read2 + root@1:~# sha256sum spi* + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read2 + c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test + bea9334df51c620440f86751cba0799214a016329f1736f9456d40cf40efdc88 spi_test2 + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03ffffff | locked | 1024 + root@1:~# flash_lock -u /dev/mtd0 + + Once we trust the debugfs output we can use it to test various + situations. Check top locking/unlocking (end of the device):: + + root@1:~# size=$(cat /sys/class/mtd/mtd0/size) + root@1:~# bs=$(cat /sys/class/mtd/mtd0/erasesize) + root@1:~# nsectors=$(grep unlocked /sys/kernel/debug/spi-nor/spi0.0/params | sed -e 's/.*unlocked | //') + root@1:~# ss=$(($size / $nsectors)) + root@1:~# bps=$(($ss / $bs)) + + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -l /dev/mtd0 $(($size - (2 * $ss))) $((2 * $bps)) # last two + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03fdffff | unlocked | 1022 + 03fe0000-03ffffff | locked | 2 + root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $((1 * $bps)) # last one + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03feffff | unlocked | 1023 + 03ff0000-03ffffff | locked | 1 + + If the flash features 4 block protection bits (BP), we can protect + more than 4MB (typically 128 64kiB-blocks or more), with a finer + grain than locking the entire device:: + + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -l /dev/mtd0 $(($size - (2**7 * $ss))) $((2**7 * $bps)) + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-037fffff | unlocked | 896 + 03800000-03ffffff | locked | 128 + + If the flash features a Top/Bottom (TB) bit, we can protect the + beginning of the flash:: + + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -l /dev/mtd0 0 $((2 * $bps)) # first two + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-0001ffff | locked | 2 + 00020000-03ffffff | unlocked | 1022 + root@1:~# flash_lock -u /dev/mtd0 $ss $((1 * $bps)) # first one + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-0000ffff | locked | 1 + 00010000-03ffffff | unlocked | 1023 From e3fb31d8847fef2ce37c5f60bc77d3f731a2419b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:45 +0200 Subject: [PATCH 1823/5214] mtd: spi-nor: swp: Add support for the complement feature The current locking implementation allows to select a power of two number of blocks, which is going to be the protected amount, as well as telling whether this is the data at the top (end of the device) or the bottom (beginning of the device). This means at most we can cover half of the device or the entire device, but nothing in between. The complement feature allows a much finer grain of configuration, by allowing to invert what is considered locked and unlocked. Add support for this feature. The only known position for the CMP bit is bit 6 of the configuration register. The locking and unlocking logics are kept unchanged if the CMP bit is unavailable. Otherwise, once the regular logic has been applied, we check if we already found an optimal configuration. If not, we try with the CMP bit set. If the coverage is closer to the request, we use it. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.c | 3 + drivers/mtd/spi-nor/core.h | 4 + drivers/mtd/spi-nor/debugfs.c | 1 + drivers/mtd/spi-nor/swp.c | 214 ++++++++++++++++++++++++++++------ include/linux/mtd/spi-nor.h | 1 + 5 files changed, 190 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 1fc71d6e0fec..b25d1a870a22 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2974,6 +2974,9 @@ static void spi_nor_init_flags(struct spi_nor *nor) nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; } + if (flags & SPI_NOR_HAS_CMP) + nor->flags |= SNOR_F_HAS_SR2_CMP_BIT6; + if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 && !nor->controller_ops) nor->flags |= SNOR_F_RWW; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index ce46771ecdc8..ba2d1a862c9d 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -141,6 +141,7 @@ enum spi_nor_option_flags { SNOR_F_ECC = BIT(15), SNOR_F_NO_WP = BIT(16), SNOR_F_SWAP16 = BIT(17), + SNOR_F_HAS_SR2_CMP_BIT6 = BIT(18), }; struct spi_nor_read_command { @@ -491,6 +492,8 @@ struct spi_nor_id { * SPI_NOR_NO_ERASE: no erase command needed. * SPI_NOR_QUAD_PP: flash supports Quad Input Page Program. * SPI_NOR_RWW: flash supports reads while write. + * SPI_NOR_HAS_CMP: flash SR2 has complement (CMP) protect bit. Must + * be used with SPI_NOR_HAS_LOCK. * * @no_sfdp_flags: flags that indicate support that can be discovered via SFDP. * Used when SFDP tables are not defined in the flash. These @@ -539,6 +542,7 @@ struct flash_info { #define SPI_NOR_NO_ERASE BIT(6) #define SPI_NOR_QUAD_PP BIT(8) #define SPI_NOR_RWW BIT(9) +#define SPI_NOR_HAS_CMP BIT(10) u8 no_sfdp_flags; #define SPI_NOR_SKIP_SFDP BIT(0) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index ef710bd4f307..e80c03dd5461 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -30,6 +30,7 @@ static const char *const snor_f_names[] = { SNOR_F_NAME(ECC), SNOR_F_NAME(NO_WP), SNOR_F_NAME(SWAP16), + SNOR_F_NAME(HAS_SR2_CMP_BIT6), }; #undef SNOR_F_NAME diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 2411d8f1012d..235070b215d1 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -34,6 +34,15 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) return 0; } +static u8 spi_nor_get_sr_cmp_mask(struct spi_nor *nor) +{ + if (!(nor->flags & SNOR_F_NO_READ_CR) && + nor->flags & SNOR_F_HAS_SR2_CMP_BIT6) + return SR2_CMP_BIT6; + else + return 0; +} + u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) { unsigned int bp_slots, bp_slots_needed; @@ -61,8 +70,10 @@ void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 min_prot_len; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor); u8 bp, val = sr[0] & bp_mask; bool tb = (nor->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; + bool cmp = sr[1] & cmp_mask; if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; @@ -70,22 +81,37 @@ void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, bp = val >> SR_BP_SHIFT; if (!bp) { - /* No protection */ - *ofs = 0; - *len = 0; + if (!cmp) { + /* No protection */ + *ofs = 0; + *len = 0; + } else { + /* Full protection */ + *ofs = 0; + *len = nor->params->size; + } return; } min_prot_len = spi_nor_get_min_prot_length_sr(nor); *len = min_prot_len << (bp - 1); - if (*len > nor->params->size) *len = nor->params->size; - if (tb) - *ofs = 0; - else - *ofs = nor->params->size - *len; + if (cmp) + *len = nor->params->size - *len; + + if (!cmp) { + if (tb) + *ofs = 0; + else + *ofs = nor->params->size - *len; + } else { + if (tb) + *ofs = nor->params->size - *len; + else + *ofs = 0; + } } /* @@ -142,13 +168,15 @@ static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow) } static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, - u8 pow, bool use_top) + u8 pow, bool use_top, bool cmp) { u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor); int ret; new_sr[0] = old_sr[0] & ~bp_mask & ~tb_mask; + new_sr[1] = old_sr[1] & ~cmp_mask; /* Build BP field */ ret = spi_nor_sr_set_bp_mask(nor, &new_sr[0], pow); @@ -156,9 +184,13 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, return ret; /* Build TB field */ - if (!use_top) + if ((!cmp && !use_top) || (cmp && use_top)) new_sr[0] |= tb_mask; + /* Build CMP field */ + if (cmp) + new_sr[1] |= cmp_mask; + return 0; } @@ -170,15 +202,27 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) { u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor); + u8 sr_cr[2] = {}; + if (!sr) { if (spi_nor_read_sr(nor, nor->bouncebuf)) return; - sr = nor->bouncebuf; + sr_cr[0] = nor->bouncebuf[0]; + + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + if (spi_nor_read_cr(nor, nor->bouncebuf)) + return; + } + + sr_cr[1] = nor->bouncebuf[0]; + sr = sr_cr; } nor->dfs_sr_cache[0] = sr[0] & (bp_mask | tb_mask | SR_SRWD); + nor->dfs_sr_cache[1] = sr[1] & cmp_mask; } /* @@ -187,10 +231,11 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) * register * (SR). Does not support these features found in newer SR bitfields: * - SEC: sector/block protect - only handle SEC=0 (block protect) - * - CMP: complement protect - only support CMP=0 (range is not complemented) * * Support for the following is provided conditionally for some flash: * - TB: top/bottom protect + * - CMP: complement protect (BP and TP describe the unlocked part, while + * the reminder is locked) * * Sample table portion for 8MB flash (Winbond w25q64fw): * @@ -217,11 +262,13 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); - u8 status_old[1] = {}, status_new[1] = {}; - loff_t ofs_old, ofs_new; - u64 len_old, len_new; + u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; + u8 *best_status_new = status_new; + loff_t ofs_old, ofs_new, ofs_new_cmp; + u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; @@ -232,6 +279,14 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) status_old[0] = nor->bouncebuf[0]; + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + ret = spi_nor_read_cr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old[1] = nor->bouncebuf[0]; + } + /* If nothing in our range is unlocked, we don't need to do anything */ if (spi_nor_is_locked_sr(nor, ofs, len, status_old)) return 0; @@ -262,27 +317,60 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); + ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top, false); if (ret) return ret; + /* + * In case the region asked is not fully met, maybe we can try with the + * complement feature + */ + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + if (can_be_cmp && len_new != lock_len) { + pow = ilog2(nor->params->size - lock_len) - ilog2(min_prot_len) + 1; + ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true); + if (ret) + return ret; + + /* + * ilog2() "floors" the result, which means in some cases we may have to + * manually reduce the scope when the complement feature is used. + * The uAPI is to never lock more than what is requested, but less is accepted. + * Make sure we are not covering a too wide range, reduce it otherwise. + */ + spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp); + if (len_new_cmp > lock_len) { + pow++; + ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true); + if (ret) + return ret; + } + + /* Pick the CMP configuration if we cover a closer range */ + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp); + if (len_new_cmp <= lock_len && + (lock_len - len_new_cmp) < (lock_len - len_new)) + best_status_new = status_new_cmp; + } + /* * Disallow further writes if WP# pin is neither left floating nor * wrongly tied to GND (that includes internal pull-downs). * WP# pin hard strapped to GND can be a valid use case. */ if (!(nor->flags & SNOR_F_NO_WP)) - status_new[0] |= SR_SRWD; + best_status_new[0] |= SR_SRWD; spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); - spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + spi_nor_get_locked_range_sr(nor, best_status_new, &ofs_new, &len_new); /* Don't "lock" with no region! */ if (!len_new) return -EINVAL; /* Don't bother if they're the same */ - if (status_new[0] == status_old[0]) + if (best_status_new[0] == status_old[0] && best_status_new[1] == status_old[1]) return 0; /* Only modify protection if it will not unlock other areas */ @@ -290,11 +378,14 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old))) return -EINVAL; - ret = spi_nor_write_sr_and_check(nor, status_new[0]); + if (nor->flags & SNOR_F_NO_READ_CR) + ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); + else + ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); if (ret) return ret; - spi_nor_cache_sr_lock_bits(nor, status_new); + spi_nor_cache_sr_lock_bits(nor, best_status_new); return 0; } @@ -307,11 +398,13 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); - u8 status_old[1], status_new[1]; - loff_t ofs_old, ofs_new; - u64 len_old, len_new; + u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; + u8 *best_status_new = status_new; + loff_t ofs_old, ofs_new, ofs_new_cmp; + u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; @@ -322,6 +415,14 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) status_old[0] = nor->bouncebuf[0]; + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + ret = spi_nor_read_cr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old[1] = nor->bouncebuf[0]; + } + /* If nothing in our range is locked, we don't need to do anything */ if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old)) return 0; @@ -359,30 +460,66 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; - ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top); + ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top, false); if (ret) return ret; + /* + * In case the region asked is not fully met, maybe we can try with the + * complement feature + */ + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + if (can_be_cmp && len_new != lock_len) { + pow = ilog2(nor->params->size - lock_len) - ilog2(min_prot_len) + 1; + ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true); + if (ret) + return ret; + + /* + * ilog2() "floors" the result, which means in some cases we may have to + * manually reduce the scope when the complement feature is used. + * The uAPI is to never unlock more than what is requested, but less is accepted. + * Make sure we are not covering a too small range, increase it otherwise. + */ + spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp); + if (len_new_cmp < lock_len) { + pow--; + ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true); + if (ret) + return ret; + } + + /* Pick the CMP configuration if we cover a closer range */ + spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp); + if (len_new_cmp <= lock_len && + (lock_len - len_new_cmp) < (lock_len - len_new)) + best_status_new = status_new_cmp; + } + /* Don't protect status register if we're fully unlocked */ if (lock_len == 0) - status_new[0] &= ~SR_SRWD; + best_status_new[0] &= ~SR_SRWD; /* Don't bother if they're the same */ - if (status_new[0] == status_old[0]) + if (best_status_new[0] == status_old[0] && best_status_new[1] == status_old[1]) return 0; /* Only modify protection if it will not lock other areas */ spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); - spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new); + spi_nor_get_locked_range_sr(nor, best_status_new, &ofs_new, &len_new); if (len_old && len_new && (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new))) return -EINVAL; - ret = spi_nor_write_sr_and_check(nor, status_new[0]); + if (nor->flags & SNOR_F_NO_READ_CR) + ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); + else + ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); if (ret) return ret; - spi_nor_cache_sr_lock_bits(nor, status_new); + spi_nor_cache_sr_lock_bits(nor, best_status_new); return 0; } @@ -396,13 +533,24 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) */ static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) { + u8 sr_cr[2] = {}; int ret; ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) return ret; - return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf); + sr_cr[0] = nor->bouncebuf[0]; + + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + ret = spi_nor_read_cr(nor, nor->bouncebuf); + if (ret) + return ret; + + sr_cr[1] = nor->bouncebuf[0]; + } + + return spi_nor_is_locked_sr(nor, ofs, len, sr_cr); } static const struct spi_nor_locking_ops spi_nor_sr_locking_ops = { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 9ad77f9e76c2..4b92494827b1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -125,6 +125,7 @@ #define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */ #define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */ #define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */ +#define SR2_CMP_BIT6 BIT(6) #define SR2_QUAD_EN_BIT7 BIT(7) /* Supported SPI protocols */ From f5d81415a7e12cc73b510bfac181411fedfe6345 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:46 +0200 Subject: [PATCH 1824/5214] mtd: spi-nor: Add steps for testing locking with CMP Extend the test coverage by giving guidelines to verify the CMP bit acts according to our expectations. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- Documentation/driver-api/mtd/spi-nor.rst | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/driver-api/mtd/spi-nor.rst b/Documentation/driver-api/mtd/spi-nor.rst index f817a9193731..747a326fb6c0 100644 --- a/Documentation/driver-api/mtd/spi-nor.rst +++ b/Documentation/driver-api/mtd/spi-nor.rst @@ -336,3 +336,40 @@ section, after the ``---`` marker. ------------------+----------+--------- 00000000-0000ffff | locked | 1 00010000-03ffffff | unlocked | 1023 + + If the flash features a Complement (CMP) bit, we can protect with + more granularity above half of the capacity. Let's lock all but one + block, then unlock one more block:: + + root@1:~# all_but_one=$((($size / $bs) - ($ss / $bs))) + + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -l /dev/mtd0 $ss $all_but_one # all but the first + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-0000ffff | unlocked | 1 + 00010000-03ffffff | locked | 1023 + root@1:~# flash_lock -u /dev/mtd0 $ss $(($ss / $bs)) # all but the two first + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-0001ffff | unlocked | 2 + 00020000-03ffffff | locked | 1022 + root@1:~# flash_lock -u /dev/mtd0 + root@1:~# flash_lock -l /dev/mtd0 0 $all_but_one # same from the other side + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03feffff | locked | 1023 + 03ff0000-03ffffff | unlocked | 1 + root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $(($ss / $bs)) # all but two + root@1:~# show_sectors + software locked sectors + region (in hex) | status | #sectors + ------------------+----------+--------- + 00000000-03fdffff | locked | 1022 + 03fe0000-03ffffff | unlocked | 2 From 591be2ba7861be3ca3aabeb3fd2a3ce96ddb8601 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:47 +0200 Subject: [PATCH 1825/5214] mtd: spi-nor: winbond: Add W25H512NWxxAM CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index eaa547d36aad..ef73c855cedb 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -360,7 +360,8 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25H512NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x20), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { /* W25H01NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x21), From 855599425e1aefb499e7bf90c34f708ebbb63045 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:48 +0200 Subject: [PATCH 1826/5214] mtd: spi-nor: winbond: Add W25H01NWxxAM CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index ef73c855cedb..7628fa7fb64f 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -365,7 +365,8 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25H01NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x21), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { /* W25H02NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x22), From 751e4b02c469ac84e390c472fd30e2c512e3f587 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:49 +0200 Subject: [PATCH 1827/5214] mtd: spi-nor: winbond: Add W25H02NWxxAM CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Unfortunately, this chip also comes with an incorrect BFPT table, indicating the Control Register cannot be read back. This is wrong, reading back the register works and has no (observed) side effect. The datasheet clearly indicates supporting the 35h command and all bits from the CR are marked readable. QE and CMP bits are inside, and can be properly read back. Add a fixup for this, otherwise it would defeat the use of the CMP feature. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 7628fa7fb64f..2c48d6c4a0aa 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -73,6 +73,26 @@ static const struct spi_nor_fixups w25q256_fixups = { .post_bfpt = w25q256_post_bfpt_fixups, }; +static int +winbond_rdcr_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + /* + * W25H02NW, unlike its W25H512NW nor W25H01NW cousins, improperly sets + * the QE BFPT configuration bits, indicating a non readable CR. This is + * both incorrect and impractical, as the chip features a CMP bit for its + * locking scheme that lays in the Control Register, and needs to be read. + */ + nor->flags &= ~SNOR_F_NO_READ_CR; + + return 0; +} + +static const struct spi_nor_fixups winbond_rdcr_fixup = { + .post_bfpt = winbond_rdcr_post_bfpt_fixup, +}; + /** * winbond_nor_select_die() - Set active die. * @nor: pointer to 'struct spi_nor'. @@ -370,7 +390,9 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25H02NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x22), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + .fixups = &winbond_rdcr_fixup, }, }; From f468f05f0724cf7f7a993f9805185ea8cc72453c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:50 +0200 Subject: [PATCH 1828/5214] mtd: spi-nor: winbond: Add W25Q01NWxxIQ CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Unfortunately, this chip also comes with an incorrect BFPT table, indicating the Control Register cannot be read back. This is wrong, reading back the register works and has no (observed) side effect. The datasheet clearly indicates supporting the 35h command and all bits from the CR are marked readable. QE and CMP bits are inside, and can be properly read back. Add a fixup for this, otherwise it would defeat the use of the CMP feature. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 2c48d6c4a0aa..3eca7baa4d5a 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -368,7 +368,9 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25Q01NWxxIQ */ .id = SNOR_ID(0xef, 0x60, 0x21), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + .fixups = &winbond_rdcr_fixup, }, { /* W25Q01NWxxIM */ .id = SNOR_ID(0xef, 0x80, 0x21), From eb403cb56e13d7efd742511c1a92b89dc9db8658 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:51 +0200 Subject: [PATCH 1829/5214] mtd: spi-nor: winbond: Add W25Q01NWxxIM CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 3eca7baa4d5a..3a3b7f2f1659 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -374,7 +374,8 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25Q01NWxxIM */ .id = SNOR_ID(0xef, 0x80, 0x21), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { /* W25Q02NWxxIM */ .id = SNOR_ID(0xef, 0x80, 0x22), From 48007986232858f94fc1ba2af4b360008e3e146b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:52 +0200 Subject: [PATCH 1830/5214] mtd: spi-nor: winbond: Add W25Q02NWxxIM CMP locking support This chip has support for the locking complement (CMP) feature. Add the relevant bit to enable it. Unfortunately, this chip also comes with an incorrect BFPT table, indicating the Control Register cannot be read back. This is wrong, reading back the register works and has no (observed) side effect. The datasheet clearly indicates supporting the 35h command and all bits from the CR are marked readable. QE and CMP bits are inside, and can be properly read back. Add a fixup for this, otherwise it would defeat the use of the CMP feature. Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/winbond.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 3a3b7f2f1659..8ebdbcec0b3f 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -379,7 +379,9 @@ static const struct flash_info winbond_nor_parts[] = { }, { /* W25Q02NWxxIM */ .id = SNOR_ID(0xef, 0x80, 0x22), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + .fixups = &winbond_rdcr_fixup, }, { /* W25H512NWxxAM */ .id = SNOR_ID(0xef, 0xa0, 0x20), From 949c51bfda675a9921fa2cf7ecd7ccf4da4f2e7d Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Mon, 11 May 2026 23:59:43 +0530 Subject: [PATCH 1831/5214] pinctrl: bcm: fix SPDX comment style in header Header files should use the C-style '/*' block comment for SPDX license identifiers. Correct the style in pinctrl-bcm63xx.h to satisfy checkpatch requirements. Signed-off-by: Mayur Kumar Signed-off-by: Linus Walleij --- drivers/pinctrl/bcm/pinctrl-bcm63xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/bcm/pinctrl-bcm63xx.h b/drivers/pinctrl/bcm/pinctrl-bcm63xx.h index 95243027ecd9..3dcfabb94ece 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm63xx.h +++ b/drivers/pinctrl/bcm/pinctrl-bcm63xx.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2021 Álvaro Fernández Rojas * Copyright (C) 2016 Jonas Gorski From 00e79cfd64ded7526d3c754d99e35fd8cb6ef46d Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Tue, 12 May 2026 00:00:02 +0530 Subject: [PATCH 1832/5214] pinctrl: actions: fix SPDX comment style in header Header files should use the C-style '/*' block comment for SPDX license identifiers. Correct the style in pinctrl-owl.h to satisfy checkpatch requirements. Signed-off-by: Mayur Kumar Signed-off-by: Linus Walleij --- drivers/pinctrl/actions/pinctrl-owl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/actions/pinctrl-owl.h b/drivers/pinctrl/actions/pinctrl-owl.h index dae2e8363fd5..feee7ad7e27e 100644 --- a/drivers/pinctrl/actions/pinctrl-owl.h +++ b/drivers/pinctrl/actions/pinctrl-owl.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * OWL SoC's Pinctrl definitions * From ee238a4f28f959094c85413140df9b59d39afc53 Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Tue, 12 May 2026 00:00:17 +0530 Subject: [PATCH 1833/5214] pinctrl: mediatek: fix SPDX comment style in header Header files should use the C-style '/*' block comment for SPDX license identifiers. Correct the style in pinctrl-mtk-mt8365.h to satisfy checkpatch requirements. Signed-off-by: Mayur Kumar Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mtk-mt8365.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8365.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8365.h index 39e17532c460..3f519aa01cfd 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8365.h +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8365.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2018 MediaTek Inc. * Author: Zhiyong Tao From 214a821c1462924668644e167a9706564cba65ea Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 1 May 2026 12:21:45 +0100 Subject: [PATCH 1834/5214] KVM: arm64: Guard against NULL vcpu on VHE hyp panic path On VHE, __hyp_call_panic() unconditionally calls __deactivate_traps(vcpu) on the vcpu pointer read from host_ctxt->__hyp_running_vcpu. That pointer is cleared after every guest exit (and is never set when no guest is running), so an unexpected EL2 exception landing in _guest_exit_panic, e.g. via the el2t*_invalid / el2h_irq_invalid vectors - reaches this function with vcpu == NULL. __deactivate_traps() then dereferences vcpu via ___deactivate_traps() -> vserror_state_is_nested() -> vcpu_has_nv() -> vcpu->arch.features, faulting inside the panic handler and obscuring the original failure. The nVHE counterpart (hyp_panic() in arch/arm64/kvm/hyp/nvhe/switch.c) already guards its vcpu-using cleanup with "if (vcpu)"; mirror that here. sysreg_restore_host_state_vhe() does not depend on vcpu and continues to run unconditionally, preserving panic forensics. The trailing panic("...VCPU:%p", vcpu) prints "(null)" safely via printk's %p handling. Fixes: 6a0259ed29bb ("KVM: arm64: Remove hyp_panic arguments") Assisted-by: Gemini:gemini-3.1-pro review-prompts Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260501112149.2824881-3-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/vhe/switch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 9db3f11a4754..1e8995add14f 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -663,7 +663,8 @@ static void __noreturn __hyp_call_panic(u64 spsr, u64 elr, u64 par) host_ctxt = host_data_ptr(host_ctxt); vcpu = host_ctxt->__hyp_running_vcpu; - __deactivate_traps(vcpu); + if (vcpu) + __deactivate_traps(vcpu); sysreg_restore_host_state_vhe(host_ctxt); panic("HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n", From 1bdcdc84f9f91e702bb4410cb46016cde1d57d9b Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 1 May 2026 12:21:46 +0100 Subject: [PATCH 1835/5214] KVM: arm64: Fix __deactivate_fgt macro parameter typo __deactivate_fgt() declares its first parameter as "htcxt" but the body references "hctxt". The parameter is unused; the macro silently captures "hctxt" from the enclosing scope. Both existing callers (__deactivate_traps_hfgxtr() and __deactivate_traps_ich_hfgxtr()) happen to define a local "struct kvm_cpu_context *hctxt", so the macro works by coincidence. A future caller without an "hctxt" local in scope, or naming it differently, would compile but bind to the wrong context. Align the parameter name with the sibling __activate_fgt() macro. The "vcpu" parameter remains unused in the body, kept for API symmetry with __activate_fgt() (which uses it). Fixes: f5a5a406b4b8 ("KVM: arm64: Propagate and handle Fine-Grained UNDEF bits") Assisted-by: Gemini:gemini-3.1-pro review-prompts Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260501112149.2824881-4-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 98b2976837b1..bf0eb5e43427 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -245,7 +245,7 @@ static inline void __activate_traps_ich_hfgxtr(struct kvm_vcpu *vcpu) __activate_fgt(hctxt, vcpu, ICH_HFGITR_EL2); } -#define __deactivate_fgt(htcxt, vcpu, reg) \ +#define __deactivate_fgt(hctxt, vcpu, reg) \ do { \ write_sysreg_s(ctxt_sys_reg(hctxt, reg), \ SYS_ ## reg); \ From 3a4f5b96730cb40d5d9b31293fd34e11a10f2d6d Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 1 May 2026 12:21:47 +0100 Subject: [PATCH 1836/5214] KVM: arm64: Seed pkvm_ownership_selftest vcpu memcache The hypercall handlers call pkvm_refill_memcache() to top up the hyp_vcpu memcache before invoking __pkvm_host_{share,donate}_guest(). pkvm_ownership_selftest invokes those functions directly with a static selftest_vcpu that has an empty memcache. Seed selftest_vcpu's memcache from the prepopulated selftest pages, leaving the remainder for selftest_vm.pool. Required by the memcache-sufficiency pre-check added in the following patches. Assisted-by: Gemini:gemini-3.1-pro review-prompts Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260501112149.2824881-5-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/pkvm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index ff89b30f5c4a..3b2c4fbc34d8 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -752,16 +752,30 @@ static struct pkvm_hyp_vcpu selftest_vcpu = { struct pkvm_hyp_vcpu *init_selftest_vm(void *virt) { struct hyp_page *p = hyp_virt_to_page(virt); + unsigned long min_pages, seeded = 0; int i; selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr; WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt)); + /* + * Mirror pkvm_refill_memcache() for the share/donate pre-checks; + * the selftest invokes those functions directly and would + * otherwise see an empty memcache. + */ + min_pages = kvm_mmu_cache_min_pages(&selftest_vm.kvm.arch.mmu); + for (i = 0; i < pkvm_selftest_pages(); i++) { if (p[i].refcount) continue; p[i].refcount = 1; - hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i])); + if (seeded < min_pages) { + push_hyp_memcache(&selftest_vcpu.vcpu.arch.pkvm_memcache, + hyp_page_to_virt(&p[i]), hyp_virt_to_phys); + seeded++; + } else { + hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i])); + } } selftest_vm.kvm.arch.pkvm.handle = __pkvm_reserve_vm(); From 8ed0fbe5404616041f6daf1d2fa1824d75602f63 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 1 May 2026 12:21:48 +0100 Subject: [PATCH 1837/5214] KVM: arm64: Pre-check vcpu memcache for host->guest share __pkvm_host_share_guest() ends with kvm_pgtable_stage2_map() to install the guest stage-2 mapping, after a forward pass that mutates the host vmemmap (sets PKVM_PAGE_SHARED_OWNED and increments host_share_guest_count) for every page in the range. The map's return value is wrapped in WARN_ON() and otherwise discarded, asserting that the call cannot fail. WARN_ON() at nVHE EL2 panics, so this assertion is only correct if the call genuinely cannot fail. kvm_pgtable_stage2_map() can fail with -ENOMEM when the stage-2 walker exhausts the caller's memcache, and the host controls the vcpu memcache via the topup interface, so an under-provisioned share request would otherwise turn a recoverable -ENOMEM into a fatal hyp panic. Bound the worst-case walker allocation in the existing pre-check pass so that kvm_pgtable_stage2_map() cannot fail at the call site, using kvm_mmu_cache_min_pages() -- the same bound host EL1 uses for its own stage-2 maps. If the vcpu memcache holds fewer pages, return -ENOMEM before any state mutation. Fixes: d0bd3e6570ae ("KVM: arm64: Introduce __pkvm_host_share_guest()") Assisted-by: Gemini:gemini-3.1-pro review-prompts Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260501112149.2824881-6-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 3263f6d0a0a4..6af4a1c95aef 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -1384,6 +1384,22 @@ int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm) return ret && ret != -EHWPOISON ? ret : 0; } +/* + * share/donate install at most one stage-2 leaf (PAGE_SIZE, or one + * KVM_PGTABLE_LAST_LEVEL - 1 block for share). kvm_mmu_cache_min_pages() + * bounds the worst-case allocation: exact for the PAGE_SIZE leaf, + * conservative by one for the block. + */ +static int __guest_check_pgtable_memcache(struct pkvm_hyp_vcpu *vcpu) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + + if (vcpu->vcpu.arch.pkvm_memcache.nr_pages < kvm_mmu_cache_min_pages(vm->pgt.mmu)) + return -ENOMEM; + + return 0; +} + int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu) { struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); @@ -1468,6 +1484,10 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu } } + ret = __guest_check_pgtable_memcache(vcpu); + if (ret) + goto unlock; + for_each_hyp_page(page, phys, size) { set_host_state(page, PKVM_PAGE_SHARED_OWNED); page->host_share_guest_count++; From cada2549ca4c934e6fb3801f857c6b4b0c36490b Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 1 May 2026 12:21:49 +0100 Subject: [PATCH 1838/5214] KVM: arm64: Pre-check vcpu memcache for host->guest donate __pkvm_host_donate_guest() flips the host stage-2 PTE for the donated page to a non-valid annotation via host_stage2_set_owner_metadata_locked() and then calls kvm_pgtable_stage2_map() to install the matching guest stage-2 mapping. The map's return value is wrapped in WARN_ON() and otherwise discarded, asserting that the call cannot fail. WARN_ON() at nVHE EL2 panics, so this assertion is only correct if the call genuinely cannot fail. kvm_pgtable_stage2_map() can fail with -ENOMEM even at PAGE_SIZE granularity: the donate path verifies PKVM_NOPAGE for the guest IPA before the map, so the walker must allocate fresh page-table pages from the vcpu memcache, and the host controls the vcpu memcache via the topup interface. An under-provisioned donation request would otherwise turn a recoverable -ENOMEM into a fatal hyp panic. Bound the worst-case walker allocation alongside the existing __host_check_page_state_range() / __guest_check_page_state_range() pre-checks, using the helper introduced for host->guest share. If the vcpu memcache holds fewer pages than kvm_mmu_cache_min_pages(), return -ENOMEM before any state mutation. Fixes: 1e579adca177 ("KVM: arm64: Introduce __pkvm_host_donate_guest()") Assisted-by: Gemini:gemini-3.1-pro review-prompts Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260501112149.2824881-7-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 6af4a1c95aef..848c5b9dcfe6 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -1419,6 +1419,10 @@ int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu) if (ret) goto unlock; + ret = __guest_check_pgtable_memcache(vcpu); + if (ret) + goto unlock; + meta = host_stage2_encode_gfn_meta(vm, gfn); WARN_ON(host_stage2_set_owner_metadata_locked(phys, PAGE_SIZE, PKVM_ID_GUEST, meta)); From 16ec643e3b226739c3e770e062b8acdc8d3719be Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Fri, 8 May 2026 10:40:06 -0700 Subject: [PATCH 1839/5214] remoteproc: xlnx: Remove binding header dependency Bindings can be deprecated and driver should not include bindings headers directly. Instead define needed constants in the driver. Signed-off-by: Tanmay Shah Link: https://lore.kernel.org/r/20260508174006.3783082-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/xlnx_r5_remoteproc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 45a62cb98072..f5b736fa3cb4 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -4,7 +4,6 @@ * */ -#include #include #include #include @@ -19,6 +18,11 @@ #include "remoteproc_internal.h" +#define PD_R5_0_ATCM 15 +#define PD_R5_0_BTCM 16 +#define PD_R5_1_ATCM 17 +#define PD_R5_1_BTCM 18 + /* IPI buffer MAX length */ #define IPI_BUF_LEN_MAX 32U From 4cceeb8da363ac5127b147ee7345104743f53e9d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 18 May 2026 16:31:26 +0100 Subject: [PATCH 1840/5214] KVM: arm64: Don't populate TPIDR_EL2 in finalise_el2() Currently, it is not necessary for __finalise_el2() to configure TPIDR_EL2: * The hyp stub code does not consume the value of TPIDR_EL2. * On the boot cpu, TPIDR_EL1 is used for the percpu offset until the ARM64_HAS_VIRT_HOST_EXTN cpucap is detected and boot alternatives are patched. Before boot alternatives are patched, cpu_copy_el2regs() will copy TPIDR_EL1 into TPIDR_EL2. It is not necessary for __finalise_el2() to initialise TPIDR_EL2 before this. * Secondary CPUs are brought up after boot alternatives have been patched, and __secondary_switched() will initialize TPIDR_EL2 in 'init_cpu_task', after finalise_el2() calls __finalise_el2() * KVM hyp code which may consume TPIDR_EL2 is brought up after all secondaries have been booted, once TPIDR_El2 has been configured on all CPUs. Remove the redundant initialisation from __finalise_el2(). Cc: Oliver Upton Cc: Marc Zyngier Cc: Catalin Marinas Reviewed-by: Mark Rutland Signed-off-by: Will Deacon Reviewed-by: Marc Zyngier Link: https://patch.msgid.link/20260518153127.6078-1-will@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kernel/hyp-stub.S | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S index 634ddc904244..37c6976e44a4 100644 --- a/arch/arm64/kernel/hyp-stub.S +++ b/arch/arm64/kernel/hyp-stub.S @@ -104,11 +104,9 @@ SYM_CODE_START_LOCAL(__finalise_el2) mov_q x0, HCR_HOST_VHE_FLAGS msr_hcr_el2 x0 - // Use the EL1 allocated stack, per-cpu offset + // Use the EL1 allocated stack mrs x0, sp_el1 mov sp, x0 - mrs x0, tpidr_el1 - msr tpidr_el2, x0 // FP configuration, vectors mrs_s x0, SYS_CPACR_EL12 From 32845111e8ea6ef5e837b6d25080e69e99bd4561 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 27 May 2026 10:46:13 +0200 Subject: [PATCH 1841/5214] bus: mhi: host: pci_generic: Fix the physical function check Commit b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") added the check for physical function by checking for 'pdev->is_physfn. But 'pdev->is_physfn' is only set for the physical function of a SR-IOV capable device. But for the non-SR-IOV device this variable will be 0. So this check ended up breaking the health check functionality for all non-SR-IOV devices. Fix it by checking for '!pdev->is_virtfn' to make sure that the check is only skipped for virtual functions. Cc: stable@vger.kernel.org # 6.18 Reported-by: Slark Xiao Tested-by: Slark Xiao Fixes: b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pci_generic.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 5836ecb0ea32..0d0d9c7ffa4b 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1261,7 +1261,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) dev_warn(&pdev->dev, "device recovery started\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); pm_runtime_forbid(&pdev->dev); @@ -1291,7 +1291,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); return; @@ -1382,7 +1382,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl_config = info->config; /* Initialize health check monitor only for Physical functions */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_setup(&mhi_pdev->health_check_timer, health_check, 0); mhi_cntrl = &mhi_pdev->mhi_cntrl; @@ -1404,7 +1404,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->mru = info->mru_default; mhi_cntrl->name = info->name; - if (pdev->is_physfn) + if (!pdev->is_virtfn) mhi_pdev->reset_on_remove = info->reset_on_remove; if (info->edl_trigger) @@ -1453,7 +1453,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); /* start health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */ @@ -1482,7 +1482,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pci_disable_sriov(pdev); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete_sync(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1514,7 +1514,7 @@ static void mhi_pci_reset_prepare(struct pci_dev *pdev) dev_info(&pdev->dev, "reset\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); /* Clean up MHI state */ @@ -1560,7 +1560,7 @@ static void mhi_pci_reset_done(struct pci_dev *pdev) } set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); } @@ -1626,7 +1626,7 @@ static int __maybe_unused mhi_pci_runtime_suspend(struct device *dev) if (test_and_set_bit(MHI_PCI_DEV_SUSPENDED, &mhi_pdev->status)) return 0; - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1679,7 +1679,7 @@ static int __maybe_unused mhi_pci_runtime_resume(struct device *dev) } /* Resume health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* It can be a remote wakeup (no mhi runtime_get), update access time */ From 0524a508b2cf7a3819406cd10d43d6e30fa31004 Mon Sep 17 00:00:00 2001 From: Stanislav Zaikin Date: Fri, 22 May 2026 16:11:48 +0200 Subject: [PATCH 1842/5214] pinctrl: qcom: sm6115: Add egpio support This mirrors the egpio support added to sc7280/sm8450/sm8250/etc. This change is necessary for GPIOs 98-112 (15 GPIOs) to be used as normal GPIOs. Signed-off-by: Stanislav Zaikin Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-sm6115.c | 40 +++++++++++++++++---------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index e36716514f1f..0099c474c442 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -47,6 +47,8 @@ enum { .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ .oe_bit = 9, \ .in_bit = 0, \ .out_bit = 1, \ @@ -374,6 +376,7 @@ enum sm6115_functions { msm_mux_ddr_pxi1, msm_mux_ddr_pxi2, msm_mux_ddr_pxi3, + msm_mux_egpio, msm_mux_gcc_gp1, msm_mux_gcc_gp2, msm_mux_gcc_gp3, @@ -451,6 +454,11 @@ static const char * const gpio_groups[] = { "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", }; +static const char * const egpio_groups[] = { + "gpio98", "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", + "gpio104", "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", + "gpio110", "gpio111", "gpio112", +}; static const char * const ddr_bist_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3", }; @@ -681,6 +689,7 @@ static const struct pinfunction sm6115_functions[] = { MSM_PIN_FUNCTION(ddr_pxi1), MSM_PIN_FUNCTION(ddr_pxi2), MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(egpio), MSM_PIN_FUNCTION(gcc_gp1), MSM_PIN_FUNCTION(gcc_gp2), MSM_PIN_FUNCTION(gcc_gp3), @@ -839,21 +848,21 @@ static const struct msm_pingroup sm6115_groups[] = { [95] = PINGROUP(95, WEST, nav_gpio, gp_pdm0, qdss_gpio, wlan1_adc1, _, _, _, _, _), [96] = PINGROUP(96, WEST, qup4, nav_gpio, mdp_vsync, gp_pdm1, sd_write, jitter_bist, qdss_cti, qdss_cti, _), [97] = PINGROUP(97, WEST, qup4, nav_gpio, mdp_vsync, gp_pdm2, jitter_bist, qdss_cti, qdss_cti, _, _), - [98] = PINGROUP(98, SOUTH, _, _, _, _, _, _, _, _, _), - [99] = PINGROUP(99, SOUTH, _, _, _, _, _, _, _, _, _), - [100] = PINGROUP(100, SOUTH, atest, _, _, _, _, _, _, _, _), - [101] = PINGROUP(101, SOUTH, atest, _, _, _, _, _, _, _, _), - [102] = PINGROUP(102, SOUTH, _, phase_flag, dac_calib, ddr_pxi2, _, _, _, _, _), - [103] = PINGROUP(103, SOUTH, _, phase_flag, dac_calib, ddr_pxi2, _, _, _, _, _), - [104] = PINGROUP(104, SOUTH, _, phase_flag, qdss_gpio, dac_calib, ddr_pxi3, _, _, _, _), - [105] = PINGROUP(105, SOUTH, _, phase_flag, qdss_gpio, dac_calib, ddr_pxi3, _, _, _, _), - [106] = PINGROUP(106, SOUTH, nav_gpio, gcc_gp3, qdss_gpio, _, _, _, _, _, _), - [107] = PINGROUP(107, SOUTH, nav_gpio, gcc_gp2, qdss_gpio, _, _, _, _, _, _), - [108] = PINGROUP(108, SOUTH, nav_gpio, _, _, _, _, _, _, _, _), - [109] = PINGROUP(109, SOUTH, _, qdss_gpio, _, _, _, _, _, _, _), - [110] = PINGROUP(110, SOUTH, _, qdss_gpio, _, _, _, _, _, _, _), - [111] = PINGROUP(111, SOUTH, _, _, _, _, _, _, _, _, _), - [112] = PINGROUP(112, SOUTH, _, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, SOUTH, _, _, _, _, _, _, _, _, egpio), + [99] = PINGROUP(99, SOUTH, _, _, _, _, _, _, _, _, egpio), + [100] = PINGROUP(100, SOUTH, atest, _, _, _, _, _, _, _, egpio), + [101] = PINGROUP(101, SOUTH, atest, _, _, _, _, _, _, _, egpio), + [102] = PINGROUP(102, SOUTH, _, phase_flag, dac_calib, ddr_pxi2, _, _, _, _, egpio), + [103] = PINGROUP(103, SOUTH, _, phase_flag, dac_calib, ddr_pxi2, _, _, _, _, egpio), + [104] = PINGROUP(104, SOUTH, _, phase_flag, qdss_gpio, dac_calib, ddr_pxi3, _, _, _, egpio), + [105] = PINGROUP(105, SOUTH, _, phase_flag, qdss_gpio, dac_calib, ddr_pxi3, _, _, _, egpio), + [106] = PINGROUP(106, SOUTH, nav_gpio, gcc_gp3, qdss_gpio, _, _, _, _, _, egpio), + [107] = PINGROUP(107, SOUTH, nav_gpio, gcc_gp2, qdss_gpio, _, _, _, _, _, egpio), + [108] = PINGROUP(108, SOUTH, nav_gpio, _, _, _, _, _, _, _, egpio), + [109] = PINGROUP(109, SOUTH, _, qdss_gpio, _, _, _, _, _, _, egpio), + [110] = PINGROUP(110, SOUTH, _, qdss_gpio, _, _, _, _, _, _, egpio), + [111] = PINGROUP(111, SOUTH, _, _, _, _, _, _, _, _, egpio), + [112] = PINGROUP(112, SOUTH, _, _, _, _, _, _, _, _, egpio), [113] = UFS_RESET(ufs_reset, 0x78000), [114] = SDC_QDSD_PINGROUP(sdc1_rclk, WEST, 0x75000, 15, 0), [115] = SDC_QDSD_PINGROUP(sdc1_clk, WEST, 0x75000, 13, 6), @@ -886,6 +895,7 @@ static const struct msm_pinctrl_soc_data sm6115_tlmm = { .ntiles = ARRAY_SIZE(sm6115_tiles), .wakeirq_map = sm6115_mpm_map, .nwakeirq_map = ARRAY_SIZE(sm6115_mpm_map), + .egpio_func = 9, }; static int sm6115_tlmm_probe(struct platform_device *pdev) From cf7d65d1d6f50daadfe7c475f0c2b1bfdc288b56 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 27 May 2026 10:43:43 -0700 Subject: [PATCH 1843/5214] KVM: x86: Consolidate CPUID fault handling for emulator and interception logic Extract the logic for emulating CPUID faulting (where CPUID #GPs at CPL>0 outside of SMM) into a dedicated helper and use the helper for both the full emulator and the intercepted-CPUID paths. Opportunistically drop kvm_require_cpl(), as kvm_emulate_cpuid() was the one and only user. No functional change intended. [jim: Add EXPORT_STATIC_CALL_GPL(kvm_x86_get_cpl) so that KVM vendor modules can call kvm_is_cpuid_allowed(). Fix typo in commit message.] Signed-off-by: Jim Mattson Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260527174347.2356165-2-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/cpuid.c | 5 +++-- arch/x86/kvm/cpuid.h | 8 ++++++++ arch/x86/kvm/emulate.c | 6 +----- arch/x86/kvm/kvm_emulate.h | 1 + arch/x86/kvm/x86.c | 19 +++++++------------ 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index beb83eea65ef..f5a35136ebb8 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2320,7 +2320,6 @@ static inline void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, __kvm_inject_emulated_page_fault(vcpu, fault, false); } -bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl); bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr); static inline int __kvm_irq_line_state(unsigned long *irq_state, diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index e69156b54cff..1c95d1fa3ead 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -2161,9 +2161,10 @@ int kvm_emulate_cpuid(struct kvm_vcpu *vcpu) { u32 eax, ebx, ecx, edx; - if (!is_smm(vcpu) && cpuid_fault_enabled(vcpu) && - !kvm_require_cpl(vcpu, 0)) + if (!kvm_is_cpuid_allowed(vcpu)) { + kvm_queue_exception_e(vcpu, GP_VECTOR, 0); return 1; + } eax = kvm_rax_read(vcpu); ecx = kvm_rcx_read(vcpu); diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 039b8e6f40ba..bc4a8428b836 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -7,6 +7,8 @@ #include #include +#include "smm.h" + extern u32 kvm_cpu_caps[NR_KVM_CPU_CAPS] __read_mostly; extern bool kvm_is_configuring_cpu_caps __read_mostly; @@ -192,6 +194,12 @@ static inline bool cpuid_fault_enabled(struct kvm_vcpu *vcpu) MSR_MISC_FEATURES_ENABLES_CPUID_FAULT; } +static inline bool kvm_is_cpuid_allowed(struct kvm_vcpu *vcpu) +{ + return !cpuid_fault_enabled(vcpu) || is_smm(vcpu) || + !kvm_x86_call(get_cpl)(vcpu); +} + static __always_inline void kvm_cpu_cap_clear(unsigned int x86_feature) { unsigned int x86_leaf = __feature_leaf(x86_feature); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index dd0e19af4997..a367828ce869 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3594,12 +3594,8 @@ static int em_sti(struct x86_emulate_ctxt *ctxt) static int em_cpuid(struct x86_emulate_ctxt *ctxt) { u32 eax, ebx, ecx, edx; - u64 msr = 0; - ctxt->ops->get_msr(ctxt, MSR_MISC_FEATURES_ENABLES, &msr); - if (!ctxt->ops->is_smm(ctxt) && - (msr & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT) && - ctxt->ops->cpl(ctxt)) + if (!ctxt->ops->is_cpuid_allowed(ctxt)) return emulate_gp(ctxt, 0); eax = reg_read(ctxt, VCPU_REGS_RAX); diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index f5df31a52996..3e375af15c03 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -230,6 +230,7 @@ struct x86_emulate_ops { struct x86_instruction_info *info, enum x86_intercept_stage stage); + bool (*is_cpuid_allowed)(struct x86_emulate_ctxt *ctxt); bool (*get_cpuid)(struct x86_emulate_ctxt *ctxt, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, bool exact_only); bool (*guest_has_movbe)(struct x86_emulate_ctxt *ctxt); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c560c52b95f0..2bad3fa987df 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -151,6 +151,7 @@ struct kvm_x86_ops kvm_x86_ops __read_mostly; #include EXPORT_STATIC_CALL_GPL(kvm_x86_get_cs_db_l_bits); EXPORT_STATIC_CALL_GPL(kvm_x86_cache_reg); +EXPORT_STATIC_CALL_GPL(kvm_x86_get_cpl); static bool __read_mostly ignore_msrs = 0; module_param(ignore_msrs, bool, 0644); @@ -1022,18 +1023,6 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_queue_exception_e); -/* - * Checks if cpl <= required_cpl; if true, return true. Otherwise queue - * a #GP and return false. - */ -bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl) -{ - if (kvm_x86_call(get_cpl)(vcpu) <= required_cpl) - return true; - kvm_queue_exception_e(vcpu, GP_VECTOR, 0); - return false; -} - bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr) { if ((dr != 4 && dr != 5) || !kvm_is_cr4_bit_set(vcpu, X86_CR4_DE)) @@ -8824,6 +8813,11 @@ static int emulator_intercept(struct x86_emulate_ctxt *ctxt, &ctxt->exception); } +static bool emulator_is_cpuid_allowed(struct x86_emulate_ctxt *ctxt) +{ + return kvm_is_cpuid_allowed(emul_to_vcpu(ctxt)); +} + static bool emulator_get_cpuid(struct x86_emulate_ctxt *ctxt, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, bool exact_only) @@ -8961,6 +8955,7 @@ static const struct x86_emulate_ops emulate_ops = { .wbinvd = emulator_wbinvd, .fix_hypercall = emulator_fix_hypercall, .intercept = emulator_intercept, + .is_cpuid_allowed = emulator_is_cpuid_allowed, .get_cpuid = emulator_get_cpuid, .guest_has_movbe = emulator_guest_has_movbe, .guest_has_fxsr = emulator_guest_has_fxsr, From be7fd7c3e8bcfd3a1804567ce5cf9ca3b254c1ba Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 27 May 2026 10:43:44 -0700 Subject: [PATCH 1844/5214] KVM: x86: Prioritize CPUID faulting over CPUID VM-exits in nested VMX Per the Intel SDM, "Certain exceptions have priority over VM exits. These include invalid-opcode exceptions, faults based on privilege level, and general-protection exceptions that are based on checking I/O permission bits in the task-state segment (TSS)." Ensure that when L2 executes CPUID at CPL > 0 while L1 has enabled CPUID faulting, KVM intercepts the exit in L0 and queues #GP rather than forwarding the CPUID VM-exit to L1. Empirical testing confirms that this #GP has higher precedence than a CPUID VM-exit on Granite Rapids (F/M/S 6/0xad/1). Fixes: db2336a80489 ("KVM: x86: virtualize cpuid faulting") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260513224608.1859737-1-jmattson%40google.com?part=3 Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260527174347.2356165-3-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/nested.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index a78ce0080963..30dcabc899a2 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -6552,6 +6552,8 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, nested_evmcs_l2_tlb_flush_enabled(vcpu) && kvm_hv_is_tlb_flush_hcall(vcpu); #endif + case EXIT_REASON_CPUID: + return !kvm_is_cpuid_allowed(vcpu); default: break; } From d1bc99885a59d89e408f52f4c147b07bca9a686f Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 27 May 2026 10:43:45 -0700 Subject: [PATCH 1845/5214] KVM: x86: Remove supports_cpuid_fault() helper The function, supports_cpuid_fault(), tests specifically for guest support of Intel's CPUID faulting feature. It does not test for guest support of AMD's CPUID faulting feature. To avoid confusion, remove the helper. Suggested-by: Sean Christopherson Signed-off-by: Jim Mattson Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260527174347.2356165-4-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/cpuid.h | 5 ----- arch/x86/kvm/x86.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index bc4a8428b836..95d09ccbf951 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -183,11 +183,6 @@ static inline int guest_cpuid_stepping(struct kvm_vcpu *vcpu) return x86_stepping(best->eax); } -static inline bool supports_cpuid_fault(struct kvm_vcpu *vcpu) -{ - return vcpu->arch.msr_platform_info & MSR_PLATFORM_INFO_CPUID_FAULT; -} - static inline bool cpuid_fault_enabled(struct kvm_vcpu *vcpu) { return vcpu->arch.msr_misc_features_enables & diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 2bad3fa987df..d8e6877a7a43 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4264,7 +4264,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_MISC_FEATURES_ENABLES: if (data & ~MSR_MISC_FEATURES_ENABLES_CPUID_FAULT || (data & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT && - !supports_cpuid_fault(vcpu))) + !(vcpu->arch.msr_platform_info & MSR_PLATFORM_INFO_CPUID_FAULT))) return 1; vcpu->arch.msr_misc_features_enables = data; break; From e93a93f11490992a477546d38d3a37b9b8dc6a5f Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 27 May 2026 10:43:46 -0700 Subject: [PATCH 1846/5214] KVM: x86: Virtualize AMD CPUID faulting On AMD CPUs, CPUID faulting support is advertised via CPUID.80000021H:EAX.CpuidUserDis[bit 17] and enabled by setting HWCR.CpuidUserDis[bit 35]. Advertise the feature to userspace regardless of host CPU support. Allow writes to HWCR to set bit 35 when the guest CPUID advertises CpuidUserDis. Update cpuid_fault_enabled() to check HWCR.CpuidUserDis as well as MSR_FEATURE_ENABLES.CPUID_GP_ON_CPL_GT_0. Unlike VMX, SVM prioritizes the CPUID intercept over the #GP induced by CPUID faulting.[1] This behavior has been confirmed on a Turin CPU (F/M/S 1AH/2/1). Link: https://lore.kernel.org/r/DS7PR12MB82011943131DF5415365E19E940B2@DS7PR12MB8201.namprd12.prod.outlook.com [1] Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260527174347.2356165-5-jmattson@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/msr-index.h | 1 + arch/x86/kvm/cpuid.c | 2 +- arch/x86/kvm/cpuid.h | 5 +++-- arch/x86/kvm/x86.c | 18 ++++++++++++------ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index a14a0f43e04a..f534f150d1c5 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -898,6 +898,7 @@ #define MSR_K7_HWCR_IRPERF_EN_BIT 30 #define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) #define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35 +#define MSR_K7_HWCR_CPUID_USER_DIS BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT) #define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_STATUS 0xc0010042 #define MSR_K7_HWCR_CPB_DIS_BIT 25 diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 1c95d1fa3ead..8e5340dd2621 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1248,7 +1248,7 @@ void kvm_initialize_cpu_caps(void) F(AUTOIBRS), EMULATED_F(NO_SMM_CTL_MSR), /* PrefetchCtlMsr */ - /* GpOnUserCpuid */ + EMULATED_F(GP_ON_USER_CPUID), /* EPSF */ F(PREFETCHI), F(AVX512_BMM), diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 95d09ccbf951..fc96ba86c644 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -185,8 +185,9 @@ static inline int guest_cpuid_stepping(struct kvm_vcpu *vcpu) static inline bool cpuid_fault_enabled(struct kvm_vcpu *vcpu) { - return vcpu->arch.msr_misc_features_enables & - MSR_MISC_FEATURES_ENABLES_CPUID_FAULT; + return (vcpu->arch.msr_misc_features_enables & + MSR_MISC_FEATURES_ENABLES_CPUID_FAULT) || + (vcpu->arch.msr_hwcr & MSR_K7_HWCR_CPUID_USER_DIS); } static inline bool kvm_is_cpuid_allowed(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d8e6877a7a43..57ce0f1f1860 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4002,22 +4002,28 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_EFER: return set_efer(vcpu, msr_info); - case MSR_K7_HWCR: - data &= ~(u64)0x40; /* ignore flush filter disable */ - data &= ~(u64)0x100; /* ignore ignne emulation enable */ - data &= ~(u64)0x8; /* ignore TLB cache disable */ - + case MSR_K7_HWCR: { /* * Allow McStatusWrEn and TscFreqSel. (Linux guests from v3.2 * through at least v6.6 whine if TscFreqSel is clear, * depending on F/M/S. */ - if (data & ~(BIT_ULL(18) | BIT_ULL(24))) { + u64 valid = BIT_ULL(18) | BIT_ULL(24); + + data &= ~(u64)0x40; /* ignore flush filter disable */ + data &= ~(u64)0x100; /* ignore ignne emulation enable */ + data &= ~(u64)0x8; /* ignore TLB cache disable */ + + if (guest_cpu_cap_has(vcpu, X86_FEATURE_GP_ON_USER_CPUID)) + valid |= MSR_K7_HWCR_CPUID_USER_DIS; + + if (data & ~valid) { kvm_pr_unimpl_wrmsr(vcpu, msr, data); return 1; } vcpu->arch.msr_hwcr = data; break; + } case MSR_FAM10H_MMIO_CONF_BASE: if (data != 0) { kvm_pr_unimpl_wrmsr(vcpu, msr, data); From b16c2aca369d180dc94275343b34966194317528 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 27 May 2026 10:43:47 -0700 Subject: [PATCH 1847/5214] KVM: selftests: Update hwcr_msr_test for CPUID faulting bit Add BIT_ULL(35) (CpuidUserDis) to the valid mask in hwcr_msr_test, now that KVM accepts writes to this bit when the guest CPUID advertises CpuidUserDis. Signed-off-by: Jim Mattson Link: https://patch.msgid.link/20260527174347.2356165-6-jmattson@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/include/x86/processor.h | 1 + tools/testing/selftests/kvm/x86/hwcr_msr_test.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 06878e7c7347..513e4a1075fa 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -226,6 +226,7 @@ struct kvm_x86_cpu_feature { #define X86_FEATURE_SEV KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 1) #define X86_FEATURE_SEV_ES KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 3) #define X86_FEATURE_SEV_SNP KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 4) +#define X86_FEATURE_GP_ON_USER_CPUID KVM_X86_CPU_FEATURE(0x80000021, 0, EAX, 17) #define X86_FEATURE_PERFMON_V2 KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 0) #define X86_FEATURE_LBR_PMC_FREEZE KVM_X86_CPU_FEATURE(0x80000022, 0, EAX, 2) diff --git a/tools/testing/selftests/kvm/x86/hwcr_msr_test.c b/tools/testing/selftests/kvm/x86/hwcr_msr_test.c index 8e20a03b3329..53b7971aa072 100644 --- a/tools/testing/selftests/kvm/x86/hwcr_msr_test.c +++ b/tools/testing/selftests/kvm/x86/hwcr_msr_test.c @@ -11,12 +11,17 @@ void test_hwcr_bit(struct kvm_vcpu *vcpu, unsigned int bit) { const u64 ignored = BIT_ULL(3) | BIT_ULL(6) | BIT_ULL(8); - const u64 valid = BIT_ULL(18) | BIT_ULL(24); - const u64 legal = ignored | valid; + u64 valid = BIT_ULL(18) | BIT_ULL(24); u64 val = BIT_ULL(bit); u64 actual; + u64 legal; int r; + if (kvm_cpu_has(X86_FEATURE_GP_ON_USER_CPUID)) + valid |= BIT_ULL(35); + + legal = ignored | valid; + r = _vcpu_set_msr(vcpu, MSR_K7_HWCR, val); TEST_ASSERT(val & ~legal ? !r : r == 1, "Expected KVM_SET_MSRS(MSR_K7_HWCR) = 0x%lx to %s", From b400c076fe4b63576fa22fbb59078b030000970c Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 23 May 2026 23:43:51 +0500 Subject: [PATCH 1848/5214] counter: ftm-quaddec: use devm_mutex_init() ftm_quaddec_probe() calls mutex_init() but neither the cleanup action nor a remove callback issues a matching mutex_destroy(), so the lock debug state is leaked on driver unbind. Switch to devm_mutex_init() so the mutex is torn down in the same devm scope it was set up in. Signed-off-by: Stepan Ionichev Reviewed-by: Joshua Crofts Link: https://lore.kernel.org/r/20260523184351.7567-1-sozdayvek@gmail.com Signed-off-by: William Breathitt Gray --- drivers/counter/ftm-quaddec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index c47741292ae1..8455f16d62cb 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -292,7 +292,9 @@ static int ftm_quaddec_probe(struct platform_device *pdev) counter->signals = ftm_quaddec_signals; counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals); - mutex_init(&ftm->ftm_quaddec_mutex); + ret = devm_mutex_init(&pdev->dev, &ftm->ftm_quaddec_mutex); + if (ret) + return ret; ftm_quaddec_init(ftm); From 4d9a902be374aea023f2193f729c26612e56b542 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 23 May 2026 23:44:18 +0500 Subject: [PATCH 1849/5214] counter: interrupt-cnt: use devm_mutex_init() interrupt_cnt_probe() calls mutex_init() but neither this driver nor the counter core issues a matching mutex_destroy() on unbind, so the lock debug state is leaked. Switch to devm_mutex_init() so the mutex is torn down in the same devm scope it was set up in. Signed-off-by: Stepan Ionichev Reviewed-by: Joshua Crofts Link: https://lore.kernel.org/r/20260523184418.7586-1-sozdayvek@gmail.com Signed-off-by: William Breathitt Gray --- drivers/counter/interrupt-cnt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index e6100b5fb082..cd475382ab6a 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -233,7 +233,9 @@ static int interrupt_cnt_probe(struct platform_device *pdev) if (ret) return ret; - mutex_init(&priv->lock); + ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; ret = devm_counter_add(dev, counter); if (ret < 0) From ca815bb87064a8f68b00eaf8872ef0d4a33a5bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 20 May 2026 14:18:12 +0300 Subject: [PATCH 1850/5214] counter: intel-qep: Use devm_mutex_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_qep_probe() calls mutex_init() but lacks the pairing mutex_destroy() calls. Convert to devm_mutex_init() which handles cleanup automatically. Signed-off-by: Ilpo Järvinen Reviewed-by: Joshua Crofts Reviewed-by: Stepan Ionichev Link: https://lore.kernel.org/r/20260520111813.3934-1-ilpo.jarvinen@linux.intel.com Signed-off-by: William Breathitt Gray --- drivers/counter/intel-qep.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index 9c6536f75afd..3181da47e9d4 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -400,7 +400,9 @@ static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) qep->dev = dev; qep->regs = regs; - mutex_init(&qep->lock); + ret = devm_mutex_init(dev, &qep->lock); + if (ret) + return ret; intel_qep_init(qep); pci_set_drvdata(pci, qep); From 12d595795de758dbaab2da6dac14ebd354b0f57d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:53:57 +0800 Subject: [PATCH 1851/5214] KVM: TDX: Drop kvm_x86_ops.link_external_spt() Drop the dedicated .link_external_spt() for linking S-EPT pages, and instead funnel everything through .set_external_spte() for mapping S-EPT entries. Using separate hooks doesn't help prevent TDP MMU details from bleeding into TDX, and vice versa; to the contrary, dedicated callbacks will result in _more_ pollution when hugepage support is added, e.g. will require the TDP MMU to know details about the splitting rules for TDX that aren't all that relevant to the TDP MMU. Ideally, KVM would provide a single pair of hooks to set S-EPT entries, one hook for setting SPTEs under write-lock and another for setting SPTEs under read-lock (e.g. to ensure the entire operation is "atomic", to allow for failure, etc.). Sadly, TDX's requirement that all child S-EPT entries are removed before the parent makes that impractical: the TDP MMU deliberately prunes non-leaf SPTEs and _then_ processes its children, thus making it quite important for the TDP MMU to differentiate between zapping leaf and non-leaf S-EPT entries. However, that's the _only_ case that's truly special, and even that case could be shoehorned into a single hook; it just wouldn't be a net positive. Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075357.4113-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-ops.h | 1 - arch/x86/include/asm/kvm_host.h | 3 -- arch/x86/kvm/mmu/tdp_mmu.c | 29 +------------- arch/x86/kvm/vmx/tdx.c | 63 ++++++++++++++++++++---------- 4 files changed, 44 insertions(+), 52 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index b0269325646c..2cb393000ee9 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -96,7 +96,6 @@ KVM_X86_OP_OPTIONAL_RET0(set_identity_map_addr) KVM_X86_OP_OPTIONAL_RET0(get_mt_mask) KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep) KVM_X86_OP(load_mmu_pgd) -KVM_X86_OP_OPTIONAL_RET0(link_external_spt) KVM_X86_OP_OPTIONAL_RET0(set_external_spte) KVM_X86_OP_OPTIONAL_RET0(free_external_spt) KVM_X86_OP_OPTIONAL(remove_external_spte) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8a53ca619570..85339d43a9ff 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1911,9 +1911,6 @@ struct kvm_x86_ops { void (*load_mmu_pgd)(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level); - /* Update external mapping with page table link. */ - int (*link_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - void *external_spt); /* Update the external page table from spte getting set. */ int (*set_external_spte)(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index f98afc3422ce..0dc1b0597f8a 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -495,27 +495,12 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) call_rcu(&sp->rcu_head, tdp_mmu_free_sp_rcu_callback); } -static void *get_external_spt(gfn_t gfn, u64 new_spte, int level) -{ - if (is_shadow_present_pte(new_spte) && !is_last_spte(new_spte, level)) { - struct kvm_mmu_page *sp = spte_to_child_sp(new_spte); - - WARN_ON_ONCE(sp->role.level + 1 != level); - WARN_ON_ONCE(sp->gfn != gfn); - return sp->external_spt; - } - - return NULL; -} - static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sptep, gfn_t gfn, u64 *old_spte, u64 new_spte, int level) { bool was_present = is_shadow_present_pte(*old_spte); - bool is_present = is_shadow_present_pte(new_spte); - bool is_leaf = is_present && is_last_spte(new_spte, level); - int ret = 0; + int ret; KVM_BUG_ON(was_present, kvm); @@ -528,18 +513,8 @@ static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sp if (!try_cmpxchg64(rcu_dereference(sptep), old_spte, FROZEN_SPTE)) return -EBUSY; - /* - * Use different call to either set up middle level - * external page table, or leaf. - */ - if (is_leaf) { - ret = kvm_x86_call(set_external_spte)(kvm, gfn, level, new_spte); - } else { - void *external_spt = get_external_spt(gfn, new_spte, level); + ret = kvm_x86_call(set_external_spte)(kvm, gfn, level, new_spte); - KVM_BUG_ON(!external_spt, kvm); - ret = kvm_x86_call(link_external_spt)(kvm, gfn, level, external_spt); - } if (ret) __kvm_tdp_mmu_write_spte(sptep, *old_spte); else diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 3f956dde4a51..2dfc90c449a7 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1653,18 +1653,58 @@ static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, return 0; } +static struct page *tdx_spte_to_sept_pt(struct kvm *kvm, gfn_t gfn, + u64 new_spte, enum pg_level level) +{ + struct kvm_mmu_page *sp = spte_to_child_sp(new_spte); + + if (KVM_BUG_ON(!sp->external_spt, kvm) || + KVM_BUG_ON(sp->role.level + 1 != level, kvm) || + KVM_BUG_ON(sp->gfn != gfn, kvm)) + return NULL; + + return virt_to_page(sp->external_spt); +} + +static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, + enum pg_level level, u64 mirror_spte) +{ + gpa_t gpa = gfn_to_gpa(gfn); + u64 err, entry, level_state; + struct page *sept_pt; + + sept_pt = tdx_spte_to_sept_pt(kvm, gfn, mirror_spte, level); + if (!sept_pt) + return -EIO; + + err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, level, sept_pt, + &entry, &level_state); + if (unlikely(tdx_operand_busy(err))) + return -EBUSY; + + if (TDX_BUG_ON_2(err, TDH_MEM_SEPT_ADD, entry, level_state, kvm)) + return -EIO; + + return 0; +} + static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); kvm_pfn_t pfn = spte_to_pfn(mirror_spte); + if (KVM_BUG_ON(!is_shadow_present_pte(mirror_spte), kvm)) + return -EIO; + + if (!is_last_spte(mirror_spte, level)) + return tdx_sept_link_private_spt(kvm, gfn, level, mirror_spte); + /* TODO: handle large pages. */ if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm)) return -EIO; - WARN_ON_ONCE(!is_shadow_present_pte(mirror_spte) || - (mirror_spte & VMX_EPT_RWX_MASK) != VMX_EPT_RWX_MASK); + WARN_ON_ONCE((mirror_spte & VMX_EPT_RWX_MASK) != VMX_EPT_RWX_MASK); /* * Ensure pre_fault_allowed is read by kvm_arch_vcpu_pre_fault_memory() @@ -1684,24 +1724,6 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, return tdx_mem_page_aug(kvm, gfn, level, pfn); } -static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) -{ - gpa_t gpa = gfn_to_gpa(gfn); - struct page *page = virt_to_page(private_spt); - u64 err, entry, level_state; - - err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, level, page, &entry, - &level_state); - if (unlikely(tdx_operand_busy(err))) - return -EBUSY; - - if (TDX_BUG_ON_2(err, TDH_MEM_SEPT_ADD, entry, level_state, kvm)) - return -EIO; - - return 0; -} - /* * Ensure shared and private EPTs to be flushed on all vCPUs. * tdh_mem_track() is the only caller that increases TD epoch. An increase in @@ -3411,7 +3433,6 @@ int __init tdx_hardware_setup(void) vt_x86_ops.vm_size = max_t(unsigned int, vt_x86_ops.vm_size, sizeof(struct kvm_tdx)); - vt_x86_ops.link_external_spt = tdx_sept_link_private_spt; vt_x86_ops.set_external_spte = tdx_sept_set_private_spte; vt_x86_ops.free_external_spt = tdx_sept_free_private_spt; vt_x86_ops.remove_external_spte = tdx_sept_remove_private_spte; From 176dcd88f4b46ecf8df579f54b9168c105c87c60 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:55:00 +0800 Subject: [PATCH 1852/5214] KVM: TDX: Wrap mapping of leaf and non-leaf S-EPT entries into helpers Add a helper, tdx_sept_map_leaf_spte(), to wrap and isolate PAGE.ADD and PAGE.AUG operations. Rename tdx_sept_link_private_spt() to tdx_sept_map_nonleaf_spte() to wrap SEPT.ADD for symmetry. Thus, transition tdx_sept_set_private_spte() into a "dispatch" routine for setting/writing S-EPT entries. No functional change intended. Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075500.4157-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 2dfc90c449a7..965c1244e733 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1666,7 +1666,7 @@ static struct page *tdx_spte_to_sept_pt(struct kvm *kvm, gfn_t gfn, return virt_to_page(sp->external_spt); } -static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, +static int tdx_sept_map_nonleaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { gpa_t gpa = gfn_to_gpa(gfn); @@ -1688,18 +1688,12 @@ static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, return 0; } -static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, u64 mirror_spte) +static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, + u64 mirror_spte) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); kvm_pfn_t pfn = spte_to_pfn(mirror_spte); - if (KVM_BUG_ON(!is_shadow_present_pte(mirror_spte), kvm)) - return -EIO; - - if (!is_last_spte(mirror_spte, level)) - return tdx_sept_link_private_spt(kvm, gfn, level, mirror_spte); - /* TODO: handle large pages. */ if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm)) return -EIO; @@ -1724,6 +1718,18 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, return tdx_mem_page_aug(kvm, gfn, level, pfn); } +static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, + enum pg_level level, u64 mirror_spte) +{ + if (KVM_BUG_ON(!is_shadow_present_pte(mirror_spte), kvm)) + return -EIO; + + if (!is_last_spte(mirror_spte, level)) + return tdx_sept_map_nonleaf_spte(kvm, gfn, level, mirror_spte); + + return tdx_sept_map_leaf_spte(kvm, gfn, level, mirror_spte); +} + /* * Ensure shared and private EPTs to be flushed on all vCPUs. * tdh_mem_track() is the only caller that increases TD epoch. An increase in From 78d23e299bba68d12c212f729fc7cf47a66a5251 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:55:19 +0800 Subject: [PATCH 1853/5214] KVM: x86/mmu: Fold set_external_spte_present() into its sole caller Fold set_external_spte_present() into __tdp_mmu_set_spte_atomic() in anticipation of propagating *all* changes (like atomic zap) triggered by tdp_mmu_set_spte_atomic() to the external PTEs. No functional change intended. Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075520.4177-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 72 ++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 0dc1b0597f8a..128089571e34 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -495,33 +495,6 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) call_rcu(&sp->rcu_head, tdp_mmu_free_sp_rcu_callback); } -static int __must_check set_external_spte_present(struct kvm *kvm, tdp_ptep_t sptep, - gfn_t gfn, u64 *old_spte, - u64 new_spte, int level) -{ - bool was_present = is_shadow_present_pte(*old_spte); - int ret; - - KVM_BUG_ON(was_present, kvm); - - lockdep_assert_held(&kvm->mmu_lock); - /* - * We need to lock out other updates to the SPTE until the external - * page table has been modified. Use FROZEN_SPTE similar to - * the zapping case. - */ - if (!try_cmpxchg64(rcu_dereference(sptep), old_spte, FROZEN_SPTE)) - return -EBUSY; - - ret = kvm_x86_call(set_external_spte)(kvm, gfn, level, new_spte); - - if (ret) - __kvm_tdp_mmu_write_spte(sptep, *old_spte); - else - __kvm_tdp_mmu_write_spte(sptep, new_spte); - return ret; -} - /** * handle_changed_spte - handle bookkeeping associated with an SPTE change * @kvm: kvm instance @@ -626,6 +599,8 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, struct tdp_iter *iter, u64 new_spte) { + u64 *raw_sptep = rcu_dereference(iter->sptep); + /* * The caller is responsible for ensuring the old SPTE is not a FROZEN * SPTE. KVM should never attempt to zap or manipulate a FROZEN SPTE, @@ -635,8 +610,13 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, WARN_ON_ONCE(iter->yielded || is_frozen_spte(iter->old_spte)); if (is_mirror_sptep(iter->sptep) && !is_frozen_spte(new_spte)) { + bool was_present = is_shadow_present_pte(iter->old_spte); int ret; + KVM_BUG_ON(was_present, kvm); + + lockdep_assert_held(&kvm->mmu_lock); + /* * Users of atomic zapping don't operate on mirror roots, * so don't handle it and bug the VM if it's seen. @@ -644,25 +624,35 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) return -EBUSY; - ret = set_external_spte_present(kvm, iter->sptep, iter->gfn, - &iter->old_spte, new_spte, iter->level); - if (ret) - return ret; - } else { - u64 *sptep = rcu_dereference(iter->sptep); - /* - * Note, fast_pf_fix_direct_spte() can also modify TDP MMU SPTEs - * and does not hold the mmu_lock. On failure, i.e. if a - * different logical CPU modified the SPTE, try_cmpxchg64() - * updates iter->old_spte with the current value, so the caller - * operates on fresh data, e.g. if it retries - * tdp_mmu_set_spte_atomic() + * We need to lock out other updates to the SPTE until the external + * page table has been modified. Use FROZEN_SPTE similar to + * the zapping case. */ - if (!try_cmpxchg64(sptep, &iter->old_spte, new_spte)) + if (!try_cmpxchg64(raw_sptep, &iter->old_spte, FROZEN_SPTE)) return -EBUSY; + + ret = kvm_x86_call(set_external_spte)(kvm, iter->gfn, iter->level, + new_spte); + + if (ret) + __kvm_tdp_mmu_write_spte(iter->sptep, iter->old_spte); + else + __kvm_tdp_mmu_write_spte(iter->sptep, new_spte); + + return ret; } + /* + * Note, fast_pf_fix_direct_spte() can also modify TDP MMU SPTEs and + * does not hold the mmu_lock. On failure, i.e. if a different logical + * CPU modified the SPTE, try_cmpxchg64() updates iter->old_spte with + * the current value, so the caller operates on fresh data, e.g. if it + * retries tdp_mmu_set_spte_atomic(). + */ + if (!try_cmpxchg64(raw_sptep, &iter->old_spte, new_spte)) + return -EBUSY; + return 0; } From 291bcb3a75835233f6904b20c64b941108330f87 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:55:33 +0800 Subject: [PATCH 1854/5214] KVM: x86/mmu: Plumb param "old_spte" into kvm_x86_ops.set_external_spte() If tdp_mmu_set_spte_atomic() triggers an atomic zap on a mirror SPTE (though currently no paths trigger it), the change is propagated via the set_external_spte() op. Plumb the old SPTE into the set_external_spte() op, so TDX code rather than TDP MMU code can warn if the atomic zap isn't allowed, i.e. to let TDX enforce TDX's rules (inasmuch as possible). Rename mirror_spte to new_spte to follow the TDP MMU's naming, and to make it more obvious what value the parameter holds. Opportunistically tweak the ordering of parameters to match the pattern of most TDP MMU functions, which do "old, new, level". Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075533.4193-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 4 ++-- arch/x86/kvm/mmu/tdp_mmu.c | 4 ++-- arch/x86/kvm/vmx/tdx.c | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 85339d43a9ff..b8ca43241b11 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1912,8 +1912,8 @@ struct kvm_x86_ops { int root_level); /* Update the external page table from spte getting set. */ - int (*set_external_spte)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - u64 mirror_spte); + int (*set_external_spte)(struct kvm *kvm, gfn_t gfn, u64 old_spte, + u64 new_spte, enum pg_level level); /* Update external page tables for page table about to be freed. */ int (*free_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 128089571e34..4e961b331780 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -632,8 +632,8 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, if (!try_cmpxchg64(raw_sptep, &iter->old_spte, FROZEN_SPTE)) return -EBUSY; - ret = kvm_x86_call(set_external_spte)(kvm, iter->gfn, iter->level, - new_spte); + ret = kvm_x86_call(set_external_spte)(kvm, iter->gfn, iter->old_spte, + new_spte, iter->level); if (ret) __kvm_tdp_mmu_write_spte(iter->sptep, iter->old_spte); diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 965c1244e733..c7ca5c79ada2 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1667,13 +1667,13 @@ static struct page *tdx_spte_to_sept_pt(struct kvm *kvm, gfn_t gfn, } static int tdx_sept_map_nonleaf_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, u64 mirror_spte) + enum pg_level level, u64 new_spte) { gpa_t gpa = gfn_to_gpa(gfn); u64 err, entry, level_state; struct page *sept_pt; - sept_pt = tdx_spte_to_sept_pt(kvm, gfn, mirror_spte, level); + sept_pt = tdx_spte_to_sept_pt(kvm, gfn, new_spte, level); if (!sept_pt) return -EIO; @@ -1689,16 +1689,16 @@ static int tdx_sept_map_nonleaf_spte(struct kvm *kvm, gfn_t gfn, } static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, - u64 mirror_spte) + u64 new_spte) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - kvm_pfn_t pfn = spte_to_pfn(mirror_spte); + kvm_pfn_t pfn = spte_to_pfn(new_spte); /* TODO: handle large pages. */ if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm)) return -EIO; - WARN_ON_ONCE((mirror_spte & VMX_EPT_RWX_MASK) != VMX_EPT_RWX_MASK); + WARN_ON_ONCE((new_spte & VMX_EPT_RWX_MASK) != VMX_EPT_RWX_MASK); /* * Ensure pre_fault_allowed is read by kvm_arch_vcpu_pre_fault_memory() @@ -1718,16 +1718,16 @@ static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level leve return tdx_mem_page_aug(kvm, gfn, level, pfn); } -static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, u64 mirror_spte) +static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, + u64 new_spte, enum pg_level level) { - if (KVM_BUG_ON(!is_shadow_present_pte(mirror_spte), kvm)) + if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) return -EIO; - if (!is_last_spte(mirror_spte, level)) - return tdx_sept_map_nonleaf_spte(kvm, gfn, level, mirror_spte); + if (!is_last_spte(new_spte, level)) + return tdx_sept_map_nonleaf_spte(kvm, gfn, level, new_spte); - return tdx_sept_map_leaf_spte(kvm, gfn, level, mirror_spte); + return tdx_sept_map_leaf_spte(kvm, gfn, level, new_spte); } /* From 6b1280a738f92542a9c73ce190846f54f55ee021 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Sat, 9 May 2026 15:55:44 +0800 Subject: [PATCH 1855/5214] KVM: TDX: Move KVM_BUG_ON()s in __tdp_mmu_set_spte_atomic() to TDX code Drop some KVM_BUG_ON()s that are guarding against TDP MMU attempting to propagate unsupported changes to the external page table through __tdp_mmu_set_spte_atomic(). Have TDX code trigger them instead. Now that TDP MMU logically allows propagating atomic zapping operation to the external page table through the set_external_spte() op in __tdp_mmu_set_spte_atomic(), TDX code will trigger the KVM_BUG_ON() on the atomic zapping request instead. (Note: non-atomic zapping is not propagated via the set_external_spte() op yet). Despite the generic naming, external page table ops are designed completely around TDX. They hook the bare minimum of what is needed, and exclude the operations that are not supported by TDX. To help wrangle which operations are handleable by various operations, warnings and KVM_BUG_ON()s exist in the code. These warnings and KVM_BUG_ON()s put the burden of understanding which operations should be forwarded to TDX code on TDP MMU developers, who often read the code without TDX context. Future changes will transition the encapsulation of this domain knowledge to TDX code by funneling the external page table updates through a central update mechanism. In this paradigm, the central update mechanism can encapsulate the special knowledge, but will not have as much knowledge about what operation is in progress. Suggested-by: Sean Christopherson Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075544.4210-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 10 ---------- arch/x86/kvm/vmx/tdx.c | 3 +++ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 4e961b331780..ed806a4768c2 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -610,20 +610,10 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, WARN_ON_ONCE(iter->yielded || is_frozen_spte(iter->old_spte)); if (is_mirror_sptep(iter->sptep) && !is_frozen_spte(new_spte)) { - bool was_present = is_shadow_present_pte(iter->old_spte); int ret; - KVM_BUG_ON(was_present, kvm); - lockdep_assert_held(&kvm->mmu_lock); - /* - * Users of atomic zapping don't operate on mirror roots, - * so don't handle it and bug the VM if it's seen. - */ - if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) - return -EBUSY; - /* * We need to lock out other updates to the SPTE until the external * page table has been modified. Use FROZEN_SPTE similar to diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index c7ca5c79ada2..ea3ad913e750 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1721,6 +1721,9 @@ static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level leve static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, u64 new_spte, enum pg_level level) { + if (KVM_BUG_ON(is_shadow_present_pte(old_spte), kvm)) + return -EIO; + if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) return -EIO; From 40342185fd2b2791a11a0ea4ef9f78171b067dd5 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Sat, 9 May 2026 15:55:57 +0800 Subject: [PATCH 1856/5214] KVM: TDX: Move lockdep assert in __tdp_mmu_set_spte_atomic() to TDX code Move the MMU lockdep assert in __tdp_mmu_set_spte_atomic() into the TDX specific op because the assert is TDX specific in intention. The TDP MMU has many lockdep asserts for various scenarios, and in fact the callchains that are used for TDX already have a lockdep assert which covers the case in __tdp_mmu_set_spte_atomic(). However, these asserts are for management of the TDP root owned by KVM. In the __tdp_mmu_set_spte_atomic() assert case, it is helping with a scheme to avoid contention in the TDX module during zap operations. That is very TDX specific. One option would be to just remove the assert in __tdp_mmu_set_spte_atomic() and rely on the other ones in the TDP MMU. But that assert is for a different intention, and too far away from the SEAMCALL that needs it. So just move it to TDX code. Suggested-by: Sean Christopherson Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075557.4226-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 2 -- arch/x86/kvm/vmx/tdx.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index ed806a4768c2..dc455e6e7dc7 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -612,8 +612,6 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, if (is_mirror_sptep(iter->sptep) && !is_frozen_spte(new_spte)) { int ret; - lockdep_assert_held(&kvm->mmu_lock); - /* * We need to lock out other updates to the SPTE until the external * page table has been modified. Use FROZEN_SPTE similar to diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index ea3ad913e750..2e41bddfb80e 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1721,6 +1721,8 @@ static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level leve static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, u64 new_spte, enum pg_level level) { + lockdep_assert_held(&kvm->mmu_lock); + if (KVM_BUG_ON(is_shadow_present_pte(old_spte), kvm)) return -EIO; From c1d3af136fbf5a092785aa52f0a310008ccfad69 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Sat, 9 May 2026 15:56:09 +0800 Subject: [PATCH 1857/5214] KVM: x86/tdp_mmu: Morph !is_frozen_spte() check into a KVM_MMU_WARN_ON() Remove the conditional logic for handling the setting of mirror page table to frozen in __tdp_mmu_set_spte_atomic() and add it as a warning for both mirror and direct cases. The mirror page table needs to propagate PTE changes to the external page table. This presents a problem for atomic updates which can't update both page tables at once. So a special value, FROZEN_SPTE, is used as a temporary state during these updates to prevent concurrent operations on the PTE. If the TDP MMU tried to install FROZEN_SPTE as a long-term value, it would confuse these updates. On the other hand, it would also confuse other threads if FROZEN_SPTE is installed as a long-term value for direct page tables (e.g., causing another thread working on atomic zap to wait for a !FROZEN_SPTE value endlessly). Therefore, add the warning for installing FROZEN_SPTE as a long-term value in __tdp_mmu_set_spte_atomic() without differentiating whether it's a mirror or direct page table. Suggested-by: Sean Christopherson Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075609.4242-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index dc455e6e7dc7..b30e33dea265 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -609,7 +609,10 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, */ WARN_ON_ONCE(iter->yielded || is_frozen_spte(iter->old_spte)); - if (is_mirror_sptep(iter->sptep) && !is_frozen_spte(new_spte)) { + /* Should not set FROZEN_SPTE as a long-term value. */ + KVM_MMU_WARN_ON(is_frozen_spte(new_spte)); + + if (is_mirror_sptep(iter->sptep)) { int ret; /* From c1ec7f368d85329ab25407a0b9cf0ec99ff15e22 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:56:22 +0800 Subject: [PATCH 1858/5214] KVM: x86/mmu: Plumb "sp" _pointer_ into the TDP MMU's handle_changed_spte() Plumb the "sp" pointer into handle_changed_spte() to allow checking of is_mirror_sp(sp) in handle_changed_spte(). This will allow consolidating all S-EPT updates into a single kvm_x86_ops hook. [Yan: Remove unused "as_id" param in tdp_mmu_set_spte() ] Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075622.4258-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index b30e33dea265..4c68d5e03346 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -320,9 +320,9 @@ void kvm_tdp_mmu_alloc_root(struct kvm_vcpu *vcpu, bool mirror) } } -static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, - u64 old_spte, u64 new_spte, int level, - bool shared); +static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, + gfn_t gfn, u64 old_spte, u64 new_spte, + int level, bool shared); static void tdp_account_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) { @@ -471,8 +471,7 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) old_spte = kvm_tdp_mmu_write_spte(sptep, old_spte, FROZEN_SPTE, level); } - handle_changed_spte(kvm, kvm_mmu_page_as_id(sp), gfn, - old_spte, FROZEN_SPTE, level, shared); + handle_changed_spte(kvm, sp, gfn, old_spte, FROZEN_SPTE, level, shared); if (is_mirror_sp(sp)) { KVM_BUG_ON(shared, kvm); @@ -498,7 +497,7 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) /** * handle_changed_spte - handle bookkeeping associated with an SPTE change * @kvm: kvm instance - * @as_id: the address space of the paging structure the SPTE was a part of + * @sp: the page table in which the SPTE resides * @gfn: the base GFN that was mapped by the SPTE * @old_spte: The value of the SPTE before the change * @new_spte: The value of the SPTE after the change @@ -511,15 +510,16 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) * dirty logging updates are handled in common code, not here (see make_spte() * and fast_pf_fix_direct_spte()). */ -static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, - u64 old_spte, u64 new_spte, int level, - bool shared) +static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, + gfn_t gfn, u64 old_spte, u64 new_spte, + int level, bool shared) { bool was_present = is_shadow_present_pte(old_spte); bool is_present = is_shadow_present_pte(new_spte); bool was_leaf = was_present && is_last_spte(old_spte, level); bool is_leaf = is_present && is_last_spte(new_spte, level); bool pfn_changed = spte_to_pfn(old_spte) != spte_to_pfn(new_spte); + int as_id = kvm_mmu_page_as_id(sp); WARN_ON_ONCE(level > PT64_ROOT_MAX_LEVEL); WARN_ON_ONCE(level < PG_LEVEL_4K); @@ -668,6 +668,7 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, struct tdp_iter *iter, u64 new_spte) { + struct kvm_mmu_page *sp = sptep_to_sp(rcu_dereference(iter->sptep)); int ret; lockdep_assert_held_read(&kvm->mmu_lock); @@ -676,7 +677,7 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, if (ret) return ret; - handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte, + handle_changed_spte(kvm, sp, iter->gfn, iter->old_spte, new_spte, iter->level, true); return 0; @@ -685,7 +686,6 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, /* * tdp_mmu_set_spte - Set a TDP MMU SPTE and handle the associated bookkeeping * @kvm: KVM instance - * @as_id: Address space ID, i.e. regular vs. SMM * @sptep: Pointer to the SPTE * @old_spte: The current value of the SPTE * @new_spte: The new value that will be set for the SPTE @@ -695,9 +695,11 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, * Returns the old SPTE value, which _may_ be different than @old_spte if the * SPTE had voldatile bits. */ -static u64 tdp_mmu_set_spte(struct kvm *kvm, int as_id, tdp_ptep_t sptep, - u64 old_spte, u64 new_spte, gfn_t gfn, int level) +static u64 tdp_mmu_set_spte(struct kvm *kvm, tdp_ptep_t sptep, u64 old_spte, + u64 new_spte, gfn_t gfn, int level) { + struct kvm_mmu_page *sp = sptep_to_sp(rcu_dereference(sptep)); + lockdep_assert_held_write(&kvm->mmu_lock); /* @@ -711,7 +713,7 @@ static u64 tdp_mmu_set_spte(struct kvm *kvm, int as_id, tdp_ptep_t sptep, old_spte = kvm_tdp_mmu_write_spte(sptep, old_spte, new_spte, level); - handle_changed_spte(kvm, as_id, gfn, old_spte, new_spte, level, false); + handle_changed_spte(kvm, sp, gfn, old_spte, new_spte, level, false); /* * Users that do non-atomic setting of PTEs don't operate on mirror @@ -729,9 +731,8 @@ static inline void tdp_mmu_iter_set_spte(struct kvm *kvm, struct tdp_iter *iter, u64 new_spte) { WARN_ON_ONCE(iter->yielded); - iter->old_spte = tdp_mmu_set_spte(kvm, iter->as_id, iter->sptep, - iter->old_spte, new_spte, - iter->gfn, iter->level); + iter->old_spte = tdp_mmu_set_spte(kvm, iter->sptep, iter->old_spte, + new_spte, iter->gfn, iter->level); } #define tdp_root_for_each_pte(_iter, _kvm, _root, _start, _end) \ From ca674df13b195eb6d124ab059799d4e03fa40624 Mon Sep 17 00:00:00 2001 From: Xuanqing Shi <1356292400@qq.com> Date: Tue, 26 May 2026 19:26:17 -0700 Subject: [PATCH 1859/5214] KVM: VMX: Handle bad values on proxied writes to LBR MSRs Use the "safe" WRMSR API when writing LBRs on behalf of the guest (or host userspace), and propagate any errors back to the instigator, as the value being written is untrusted. E.g. if the guest (or host userspace) attempts to set reserved bits in LBR_SELECT, then KVM needs to return an error, and not WARN on the bad value. Continue using the "unsafe" version of RDMSR, as it should be impossible to reach the helper with a completely bogus MSR, i.e. WARNing on RDMSR failure is very desirable, e.g. to make KVM bugs more visible. unchecked MSR access error: WRMSR to 0x1c8 (tried to write 0x0000000000004000) Call Trace: intel_pmu_set_msr+0x4e0/0x7f0 [kvm_intel] kvm_pmu_set_msr+0x17e/0x1c0 [kvm] kvm_set_msr_common+0xc76/0x1440 [kvm] vmx_set_msr+0x5e6/0x1570 [kvm_intel] kvm_emulate_wrmsr+0x54/0x1d0 [kvm] vmx_handle_exit+0x7fc/0x970 [kvm_intel] Fixes: 1b5ac3226a1a ("KVM: vmx/pmu: Pass-through LBR msrs when the guest LBR event is ACTIVE") Cc: stable@vger.kernel.org Signed-off-by: Xuanqing Shi <1356292400@qq.com> [sean: rework changelog, only modify WRMSR path, tag for stable@] Link: https://patch.msgid.link/20260527022617.3973884-1-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/pmu_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 27eb76e6b6a0..22138c487216 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -308,13 +308,15 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu, */ local_irq_disable(); if (lbr_desc->event->state == PERF_EVENT_STATE_ACTIVE) { + int err = 0; + if (read) rdmsrq(index, msr_info->data); else - wrmsrq(index, msr_info->data); + err = wrmsrq_safe(index, msr_info->data); __set_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); - return true; + return !err; } clear_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); From 3e2dec1ede0a772c4d7cebd4130a7c4a12de2789 Mon Sep 17 00:00:00 2001 From: Vishal Annapurve Date: Thu, 5 Mar 2026 22:26:26 +0000 Subject: [PATCH 1860/5214] KVM: TDX: Allow userspace to return errors to guest for MAPGPA MAPGPA request from TDX VMs gets split into chunks by KVM using a loop of userspace exits until the complete range is handled. In some cases userspace VMM might decide to break the MAPGPA operation and continue it later. For example: in the case of intrahost migration userspace might decide to continue the MAPGPA operation after the migration is completed. Allow userspace to signal to TDX guests that the MAPGPA operation should be retried the next time the guest is scheduled. This is potentially a breaking change since if userspace sets hypercall.ret to a value other than EBUSY or EINVAL an EINVAL error code will be returned to userspace. As of now QEMU never sets hypercall.ret to a non-zero value after handling KVM_EXIT_HYPERCALL so this change should be safe. Reviewed-by: Michael Roth Signed-off-by: Vishal Annapurve Co-developed-by: Sagi Shahar Signed-off-by: Sagi Shahar Link: https://patch.msgid.link/20260305222627.4193305-2-sagis@google.com Signed-off-by: Sean Christopherson --- Documentation/virt/kvm/api.rst | 3 +++ arch/x86/kvm/vmx/tdx.c | 28 +++++++++++++++++++++------- arch/x86/kvm/x86.h | 6 ++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 52bbbb553ce1..1a5a82ab0d66 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8818,6 +8818,9 @@ block sizes is exposed in KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES as a This capability, if enabled, will cause KVM to exit to userspace with KVM_EXIT_HYPERCALL exit reason to process some hypercalls. +Userspace may fail the hypercall by setting hypercall.ret to EINVAL +or may request the hypercall to be retried the next time the guest run +by setting hypercall.ret to EAGAIN. Calling KVM_CHECK_EXTENSION for this capability will return a bitmask of hypercalls that can be configured to exit to userspace. diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 5fb3f606501b..738fd5ea9257 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1188,12 +1188,22 @@ static void __tdx_map_gpa(struct vcpu_tdx *tdx); static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu) { + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret); struct vcpu_tdx *tdx = to_tdx(vcpu); + long rc; - if (vcpu->run->hypercall.ret) { - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_INVALID_OPERAND); - tdx->vp_enter_args.r11 = tdx->map_gpa_next; - return 1; + switch (hypercall_ret) { + case 0: + break; + case EAGAIN: + rc = TDVMCALL_STATUS_RETRY; + goto propagate_error; + case EINVAL: + rc = TDVMCALL_STATUS_INVALID_OPERAND; + goto propagate_error; + default: + WARN_ON_ONCE(kvm_is_valid_map_gpa_range_ret(hypercall_ret)); + return -EINVAL; } tdx->map_gpa_next += TDX_MAP_GPA_MAX_LEN; @@ -1206,13 +1216,17 @@ static int tdx_complete_vmcall_map_gpa(struct kvm_vcpu *vcpu) * TDVMCALL_MAP_GPA, see comments in tdx_protected_apic_has_interrupt(). */ if (kvm_vcpu_has_events(vcpu)) { - tdvmcall_set_return_code(vcpu, TDVMCALL_STATUS_RETRY); - tdx->vp_enter_args.r11 = tdx->map_gpa_next; - return 1; + rc = TDVMCALL_STATUS_RETRY; + goto propagate_error; } __tdx_map_gpa(tdx); return 0; + +propagate_error: + tdvmcall_set_return_code(vcpu, rc); + tdx->vp_enter_args.r11 = tdx->map_gpa_next; + return 1; } static void __tdx_map_gpa(struct vcpu_tdx *tdx) diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 38a905fa86de..aa7d5b757fb5 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -754,6 +754,12 @@ static inline void kvm_prepare_emulated_mmio_exit(struct kvm_vcpu *vcpu, frag->data, vcpu->mmio_is_write); } +static inline bool kvm_is_valid_map_gpa_range_ret(u64 hypercall_ret) +{ + return !hypercall_ret || hypercall_ret == EINVAL || + hypercall_ret == EAGAIN; +} + static inline bool user_exit_on_hypercall(struct kvm *kvm, unsigned long hc_nr) { return kvm->arch.hypercall_exit_enabled & BIT(hc_nr); From 5d40e5b49442437fe9dfd2577f7b17c07dbefb92 Mon Sep 17 00:00:00 2001 From: Sagi Shahar Date: Thu, 5 Mar 2026 22:26:27 +0000 Subject: [PATCH 1861/5214] KVM: SEV: Restrict userspace return codes for KVM_HC_MAP_GPA_RANGE To align with the updated TDX api that allows userspace to request that guests retry MAP_GPA operations, make sure that userspace is only returning EINVAL or EAGAIN as possible error codes. Reviewed-by: Michael Roth Signed-off-by: Sagi Shahar Link: https://patch.msgid.link/20260305222627.4193305-3-sagis@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/sev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 940b97d4a852..2cb20fec9974 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3781,9 +3781,13 @@ static int snp_rmptable_psmash(kvm_pfn_t pfn) static int snp_complete_psc_msr(struct kvm_vcpu *vcpu) { + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret); struct vcpu_svm *svm = to_svm(vcpu); - if (vcpu->run->hypercall.ret) + if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret)) + return -EINVAL; + + if (hypercall_ret) set_ghcb_msr(svm, GHCB_MSR_PSC_RESP_ERROR); else set_ghcb_msr(svm, GHCB_MSR_PSC_RESP); @@ -3874,10 +3878,14 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm) static int snp_complete_one_psc(struct kvm_vcpu *vcpu) { + u64 hypercall_ret = READ_ONCE(vcpu->run->hypercall.ret); struct vcpu_svm *svm = to_svm(vcpu); struct psc_buffer *psc = svm->sev_es.ghcb_sa; - if (vcpu->run->hypercall.ret) { + if (!kvm_is_valid_map_gpa_range_ret(hypercall_ret)) + return -EINVAL; + + if (hypercall_ret) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); return 1; /* resume guest */ } From 28c77a0378044b96f9c82cdd889872ab88191857 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:56:34 +0800 Subject: [PATCH 1862/5214] KVM: x86/tdp_mmu: Centrally propagate to-present/atomic zap updates to external PTEs Move propagation of to-present changes and atomic zap changes to external PTEs from function __tdp_mmu_set_spte_atomic() to function __handle_changed_spte(), which centrally handles changes of SPTEs. When setting a PTE to present in the mirror page tables, the update needs to be propagated to the external page tables (in TDX parlance, the S-EPT). Today this is handled by special mirror page tables logic/branching in __tdp_mmu_set_spte_atomic(), which is the only place where present PTEs are set for TDX. The current approach obviously works, but is a bit hacked on. The hook for setting present leaf PTEs is added only where TDX happens to need it. For example, TDX does not support any of the operations that use the non-atomic variant, tdp_mmu_set_spte(), to set present PTEs. Since the hook is missing there, it is very hard to understand the code from a non-TDX lens. If the reader doesn't know the TDX specifics it could look like the external SPTE update is missing. In addition to being confusing, it also litters the TDP MMU with "external" update callbacks. This is especially unfortunate because there is already a central place to react to TDP updates, handle_changed_spte(). Begin the process of moving towards a model where all mirror page table updates are forwarded to TDX code where the TDX-specific logic can live with a more proper separation of concerns. Do this by adding a helper __handle_changed_spte() and teaching it how to return error codes, such that it can propagate the failures that may come from TDX external page table updates. Make the original handle_changed_spte() a no-fail version of __handle_changed_spte(), so it handles no-fail changes which are under exclusive mmu_lock or under the no-fail path handle_removed_pt(), triggering KVM_BUG_ON() on error returns. Instead of having __tdp_mmu_set_spte_atomic() do the frozen mirror SPTE dance and trigger propagation to external PTEs, make __tdp_mmu_set_spte_atomic() a simple helper of try_cmpxchg64() and hoist the frozen mirror SPTE dance up a level to tdp_mmu_set_spte_atomic(). Then, the propagation of changes to present to the external PTEs can be centralized to __handle_changed_spte(). Aging external SPTEs is not yet supported for the mirror page table, so just warn on mirror usage in kvm_tdp_mmu_age_spte() and invoke __tdp_mmu_set_spte_atomic() directly without frozen dance. No need to warn on installing FROZEN_SPTE as a long-term value in kvm_tdp_mmu_age_spte() since removing accessed bit is mutually exclusive with installing FROZEN_SPTE (FROZEN_SPTE is with accessed bit in all x86 platforms). Since tdp_mmu_set_spte_atomic() can also be invoked to atomically zap SPTEs (though there's no path to trigger atomic zap on the mirror page table up to now), also leverage set_external_spte() op to propagate the atomic zaps when tdp_mmu_set_spte_atomic() zaps leaf SPTEs directly. (When tdp_mmu_set_spte_atomic() zaps a non-leaf SPTE, zaps of the child leaf SPTEs are propagated via the remove_external_spte() op). Note: tdp_mmu_set_spte_atomic() invokes __handle_changed_spte() to handle changes to new_spte while the mirror SPTE is frozen, so (1) the update of the external PTEs and statistics, or (2) the update of child mirror SPTEs, child external PTEs and corresponding statistics, now occur before the mirror SPTE is actually set to new_spte. (1) is ok since if it fails, the mirror SPTE will be restored to its original value. (2) is also ok since handle_removed_pt() is no-fail. Link: https://lore.kernel.org/lkml/aYYn0nf2cayYu8e7@google.com [Rick: Based on a diff by Sean Chrisopherson] Co-developed-by: Rick Edgecombe Signed-off-by: Rick Edgecombe [Yan: added atomic zap case ] Co-developed-by: Yan Zhao Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075634.4274-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 126 ++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 4c68d5e03346..3c3e73ce8da9 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -495,7 +495,7 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) } /** - * handle_changed_spte - handle bookkeeping associated with an SPTE change + * __handle_changed_spte - handle bookkeeping associated with an SPTE change * @kvm: kvm instance * @sp: the page table in which the SPTE resides * @gfn: the base GFN that was mapped by the SPTE @@ -510,9 +510,9 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) * dirty logging updates are handled in common code, not here (see make_spte() * and fast_pf_fix_direct_spte()). */ -static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, - gfn_t gfn, u64 old_spte, u64 new_spte, - int level, bool shared) +static int __handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, + gfn_t gfn, u64 old_spte, u64 new_spte, + int level, bool shared) { bool was_present = is_shadow_present_pte(old_spte); bool is_present = is_shadow_present_pte(new_spte); @@ -549,9 +549,7 @@ static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, } if (old_spte == new_spte) - return; - - trace_kvm_tdp_mmu_spte_changed(as_id, gfn, level, old_spte, new_spte); + return 0; if (is_leaf) check_spte_writable_invariants(new_spte); @@ -578,29 +576,51 @@ static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, "a temporary frozen SPTE.\n" "as_id: %d gfn: %llx old_spte: %llx new_spte: %llx level: %d", as_id, gfn, old_spte, new_spte, level); - return; - } - if (is_leaf != was_leaf) - kvm_update_page_stats(kvm, level, is_leaf ? 1 : -1); + trace_kvm_tdp_mmu_spte_changed(as_id, gfn, level, old_spte, new_spte); + return 0; + } /* * Recursively handle child PTs if the change removed a subtree from * the paging structure. Note the WARN on the PFN changing without the * SPTE being converted to a hugepage (leaf) or being zapped. Shadow * pages are kernel allocations and should never be migrated. + * + * For the mirror page table, propagate changes to present or changes of + * leaf SPTEs to !present under shared mmu_lock to the external SPTE via + * set_external_spte() op. */ if (was_present && !was_leaf && - (is_leaf || !is_present || WARN_ON_ONCE(pfn_changed))) + (is_leaf || !is_present || WARN_ON_ONCE(pfn_changed))) { handle_removed_pt(kvm, spte_to_child_pt(old_spte, level), shared); + } else if (is_mirror_sp(sp) && (is_present || shared)) { + int r; + + r = kvm_x86_call(set_external_spte)(kvm, gfn, old_spte, new_spte, level); + if (r) + return r; + } + trace_kvm_tdp_mmu_spte_changed(as_id, gfn, level, old_spte, new_spte); + + if (is_leaf != was_leaf) + kvm_update_page_stats(kvm, level, is_leaf ? 1 : -1); + + return 0; +} + +static void handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, + gfn_t gfn, u64 old_spte, u64 new_spte, + int level, bool shared) +{ + KVM_BUG_ON(__handle_changed_spte(kvm, sp, gfn, old_spte, new_spte, + level, shared), kvm); } static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, struct tdp_iter *iter, u64 new_spte) { - u64 *raw_sptep = rcu_dereference(iter->sptep); - /* * The caller is responsible for ensuring the old SPTE is not a FROZEN * SPTE. KVM should never attempt to zap or manipulate a FROZEN SPTE, @@ -609,31 +629,6 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, */ WARN_ON_ONCE(iter->yielded || is_frozen_spte(iter->old_spte)); - /* Should not set FROZEN_SPTE as a long-term value. */ - KVM_MMU_WARN_ON(is_frozen_spte(new_spte)); - - if (is_mirror_sptep(iter->sptep)) { - int ret; - - /* - * We need to lock out other updates to the SPTE until the external - * page table has been modified. Use FROZEN_SPTE similar to - * the zapping case. - */ - if (!try_cmpxchg64(raw_sptep, &iter->old_spte, FROZEN_SPTE)) - return -EBUSY; - - ret = kvm_x86_call(set_external_spte)(kvm, iter->gfn, iter->old_spte, - new_spte, iter->level); - - if (ret) - __kvm_tdp_mmu_write_spte(iter->sptep, iter->old_spte); - else - __kvm_tdp_mmu_write_spte(iter->sptep, new_spte); - - return ret; - } - /* * Note, fast_pf_fix_direct_spte() can also modify TDP MMU SPTEs and * does not hold the mmu_lock. On failure, i.e. if a different logical @@ -641,7 +636,7 @@ static inline int __must_check __tdp_mmu_set_spte_atomic(struct kvm *kvm, * the current value, so the caller operates on fresh data, e.g. if it * retries tdp_mmu_set_spte_atomic(). */ - if (!try_cmpxchg64(raw_sptep, &iter->old_spte, new_spte)) + if (!try_cmpxchg64(rcu_dereference(iter->sptep), &iter->old_spte, new_spte)) return -EBUSY; return 0; @@ -673,14 +668,51 @@ static inline int __must_check tdp_mmu_set_spte_atomic(struct kvm *kvm, lockdep_assert_held_read(&kvm->mmu_lock); - ret = __tdp_mmu_set_spte_atomic(kvm, iter, new_spte); + /* Should not set FROZEN_SPTE as a long-term value. */ + KVM_MMU_WARN_ON(is_frozen_spte(new_spte)); + + /* + * Temporarily freeze the SPTE until the external PTE operation has + * completed, e.g. so that concurrent faults don't attempt to install a + * child PTE in the external page table before the parent PTE has been + * written. + */ + if (is_mirror_sptep(iter->sptep)) + ret = __tdp_mmu_set_spte_atomic(kvm, iter, FROZEN_SPTE); + else + ret = __tdp_mmu_set_spte_atomic(kvm, iter, new_spte); + if (ret) return ret; - handle_changed_spte(kvm, sp, iter->gfn, iter->old_spte, - new_spte, iter->level, true); - - return 0; + /* + * Handle the change from iter->old_spte to new_spte. + * + * Note: for mirror page table, this means the updates of the external + * PTE, statistics, or updates of child SPTEs, child external PTEs and + * corresponding statistics are performed while the mirror SPTE is in + * frozen state (i.e., before the mirror SPTE is set to new_spte). + */ + ret = __handle_changed_spte(kvm, sp, iter->gfn, iter->old_spte, + new_spte, iter->level, true); + /* + * Unfreeze the mirror SPTE. If updating the external SPTE failed, + * restore the old value so that the mirror SPTE isn't frozen in + * perpetuity, otherwise set the mirror SPTE to the new desired value. + */ + if (is_mirror_sptep(iter->sptep)) { + if (ret) + __kvm_tdp_mmu_write_spte(iter->sptep, iter->old_spte); + else + __kvm_tdp_mmu_write_spte(iter->sptep, new_spte); + } else { + /* + * Bug the VM if handling the change failed, as failure is only + * allowed if KVM couldn't update the external SPTE. + */ + KVM_BUG_ON(ret, kvm); + } + return ret; } /* @@ -1334,6 +1366,10 @@ static void kvm_tdp_mmu_age_spte(struct kvm *kvm, struct tdp_iter *iter) { u64 new_spte; + /* TODO: Add support for aging external SPTEs, if necessary. */ + if (WARN_ON_ONCE(is_mirror_sptep(iter->sptep))) + return; + if (spte_ad_enabled(iter->old_spte)) { iter->old_spte = tdp_mmu_clear_spte_bits_atomic(iter->sptep, shadow_accessed_mask); From 41367d77ee4eba4b062835723b1756b0b6a86324 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Sat, 9 May 2026 15:56:47 +0800 Subject: [PATCH 1863/5214] KVM: x86/mmu: Drop KVM_BUG_ON() on shared lock to zap child external PTEs Drop the KVM_BUG_ON() in the KVM MMU core before zapping child external PTEs, since requiring zapping PTEs to be protected by exclusive mmu_lock is TDX's specific requirement. No need to plumb the shared/exclusive info into the remove_external_spte() op or move the KVM_BUG_ON() to TDX, because - There's already an assertion of exclusive mmu_lock protection in TDX. - The KVM_BUG_ON() is a bit redundant given that if there's any bug causing zapping of leaf PTEs in S-EPT under shared mmu_lock, SEAMCALL failures due to contention would result in TDX_BUG_ON() in TDX. Link: https://lore.kernel.org/kvm/aYUarHf3KEwHGuJe@google.com/ Suggested-by: Sean Christopherson Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075647.4290-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 3c3e73ce8da9..3ba7556a8d2f 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -473,10 +473,8 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) } handle_changed_spte(kvm, sp, gfn, old_spte, FROZEN_SPTE, level, shared); - if (is_mirror_sp(sp)) { - KVM_BUG_ON(shared, kvm); + if (is_mirror_sp(sp)) remove_external_spte(kvm, gfn, old_spte, level); - } } if (is_mirror_sp(sp) && From c74893aa56f4e7515c0877c5230f9f42357ff044 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:56:58 +0800 Subject: [PATCH 1864/5214] KVM: TDX: Hoist tdx_sept_remove_private_spte() above set_private_spte() Arrange tdx_sept_remove_private_spte() (and its tdx_track() helper) to be above tdx_sept_set_private_spte() in anticipation of routing all S-EPT writes (with the exception of reclaiming non-leaf pages) through the "set" API. No functional change intended. Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075658.4306-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 80 +++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 2e41bddfb80e..8991f08ef81c 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1718,23 +1718,6 @@ static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level leve return tdx_mem_page_aug(kvm, gfn, level, pfn); } -static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, - u64 new_spte, enum pg_level level) -{ - lockdep_assert_held(&kvm->mmu_lock); - - if (KVM_BUG_ON(is_shadow_present_pte(old_spte), kvm)) - return -EIO; - - if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) - return -EIO; - - if (!is_last_spte(new_spte, level)) - return tdx_sept_map_nonleaf_spte(kvm, gfn, level, new_spte); - - return tdx_sept_map_leaf_spte(kvm, gfn, level, new_spte); -} - /* * Ensure shared and private EPTs to be flushed on all vCPUs. * tdh_mem_track() is the only caller that increases TD epoch. An increase in @@ -1781,29 +1764,6 @@ static void tdx_track(struct kvm *kvm) kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE); } -static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) -{ - struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - - /* - * free_external_spt() is only called after hkid is freed when TD is - * tearing down. - * KVM doesn't (yet) zap page table pages in mirror page table while - * TD is active, though guest pages mapped in mirror page table could be - * zapped during TD is active, e.g. for shared <-> private conversion - * and slot move/deletion. - */ - if (KVM_BUG_ON(is_hkid_assigned(kvm_tdx), kvm)) - return -EIO; - - /* - * The HKID assigned to this TD was already freed and cache was - * already flushed. We don't have to flush again. - */ - return tdx_reclaim_page(virt_to_page(private_spt)); -} - static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { @@ -1854,6 +1814,46 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, tdx_quirk_reset_paddr(PFN_PHYS(pfn), PAGE_SIZE); } +static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, + u64 new_spte, enum pg_level level) +{ + lockdep_assert_held(&kvm->mmu_lock); + + if (KVM_BUG_ON(is_shadow_present_pte(old_spte), kvm)) + return -EIO; + + if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) + return -EIO; + + if (!is_last_spte(new_spte, level)) + return tdx_sept_map_nonleaf_spte(kvm, gfn, level, new_spte); + + return tdx_sept_map_leaf_spte(kvm, gfn, level, new_spte); +} + +static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, + enum pg_level level, void *private_spt) +{ + struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); + + /* + * free_external_spt() is only called after hkid is freed when TD is + * tearing down. + * KVM doesn't (yet) zap page table pages in mirror page table while + * TD is active, though guest pages mapped in mirror page table could be + * zapped during TD is active, e.g. for shared <-> private conversion + * and slot move/deletion. + */ + if (KVM_BUG_ON(is_hkid_assigned(kvm_tdx), kvm)) + return -EIO; + + /* + * The HKID assigned to this TD was already freed and cache was + * already flushed. We don't have to flush again. + */ + return tdx_reclaim_page(virt_to_page(private_spt)); +} + void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, int trig_mode, int vector) { From 6eae9ce9461cc0ba16a779b49a70eaab2271a969 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:57:09 +0800 Subject: [PATCH 1865/5214] KVM: TDX: Drop kvm_x86_ops.remove_external_spte() Drop kvm_x86_ops.remove_external_spte(), and instead handle the removal of leaf SPTEs in the S-EPT (a.k.a. external page table) in kvm_x86_ops.set_external_spte(). This will also allow extending tdx_sept_set_private_spte() to support splitting a huge S-EPT entry without needing yet another kvm_x86_ops hook. Now all changes for removing leaf mirror SPTEs are propagated through kvm_x86_ops.set_external_spte(). - When removing leaf mirror SPTEs under shared mmu_lock (though currently no path can trigger this scenario and TDX does not support this scenario), tdx_sept_remove_private_spte() may produce a warning due to lockdep_assert_held_write() or may return -EIO and trigger TDX_BUG_ON() due to concurrent BLOCK, TRACK, REMOVE. - When removing leaf mirror SPTEs under exclusive mmu_lock, all errors are unexpected. If any error occurs in this scenario, tdx_sept_remove_private_spte() will return -EIO and trigger KVM_BUG_ON(). A redundant KVM_BUG_ON() call will also be triggered in TDP MMU core in handle_changed_spte(), which is benign (the WARN will fire if and only if the VM isn't already bugged). Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075709.4322-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-ops.h | 1 - arch/x86/include/asm/kvm_host.h | 3 --- arch/x86/kvm/mmu/tdp_mmu.c | 37 +++------------------------- arch/x86/kvm/vmx/tdx.c | 39 +++++++++++++++++++++--------- 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 2cb393000ee9..771d991562ca 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -98,7 +98,6 @@ KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep) KVM_X86_OP(load_mmu_pgd) KVM_X86_OP_OPTIONAL_RET0(set_external_spte) KVM_X86_OP_OPTIONAL_RET0(free_external_spt) -KVM_X86_OP_OPTIONAL(remove_external_spte) KVM_X86_OP(has_wbinvd_exit) KVM_X86_OP(get_l2_tsc_offset) KVM_X86_OP(get_l2_tsc_multiplier) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b8ca43241b11..dc5625950e5b 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1919,9 +1919,6 @@ struct kvm_x86_ops { int (*free_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, void *external_spt); - /* Update external page table from spte getting removed, and flush TLB. */ - void (*remove_external_spte)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - u64 mirror_spte); bool (*has_wbinvd_exit)(void); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 3ba7556a8d2f..72d323f2d0dc 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -359,25 +359,6 @@ static void tdp_mmu_unlink_sp(struct kvm *kvm, struct kvm_mmu_page *sp) spin_unlock(&kvm->arch.tdp_mmu_pages_lock); } -static void remove_external_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, - int level) -{ - /* - * External (TDX) SPTEs are limited to PG_LEVEL_4K, and external - * PTs are removed in a special order, involving free_external_spt(). - * But remove_external_spte() will be called on non-leaf PTEs via - * __tdp_mmu_zap_root(), so avoid the error the former would return - * in this case. - */ - if (!is_last_spte(old_spte, level)) - return; - - /* Zapping leaf spte is allowed only when write lock is held. */ - lockdep_assert_held_write(&kvm->mmu_lock); - - kvm_x86_call(remove_external_spte)(kvm, gfn, level, old_spte); -} - /** * handle_removed_pt() - handle a page table removed from the TDP structure * @@ -472,9 +453,6 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) FROZEN_SPTE, level); } handle_changed_spte(kvm, sp, gfn, old_spte, FROZEN_SPTE, level, shared); - - if (is_mirror_sp(sp)) - remove_external_spte(kvm, gfn, old_spte, level); } if (is_mirror_sp(sp) && @@ -585,14 +563,14 @@ static int __handle_changed_spte(struct kvm *kvm, struct kvm_mmu_page *sp, * SPTE being converted to a hugepage (leaf) or being zapped. Shadow * pages are kernel allocations and should never be migrated. * - * For the mirror page table, propagate changes to present or changes of - * leaf SPTEs to !present under shared mmu_lock to the external SPTE via + * For the mirror page table, propagate all changes to the external SPTE + * (except zapping/promotion of non-leaf SPTEs) via the * set_external_spte() op. */ if (was_present && !was_leaf && (is_leaf || !is_present || WARN_ON_ONCE(pfn_changed))) { handle_removed_pt(kvm, spte_to_child_pt(old_spte, level), shared); - } else if (is_mirror_sp(sp) && (is_present || shared)) { + } else if (is_mirror_sp(sp)) { int r; r = kvm_x86_call(set_external_spte)(kvm, gfn, old_spte, new_spte, level); @@ -745,15 +723,6 @@ static u64 tdp_mmu_set_spte(struct kvm *kvm, tdp_ptep_t sptep, u64 old_spte, handle_changed_spte(kvm, sp, gfn, old_spte, new_spte, level, false); - /* - * Users that do non-atomic setting of PTEs don't operate on mirror - * roots, so don't handle it and bug the VM if it's seen. - */ - if (is_mirror_sptep(sptep)) { - KVM_BUG_ON(is_shadow_present_pte(new_spte), kvm); - remove_external_spte(kvm, gfn, old_spte, level); - } - return old_spte; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 8991f08ef81c..54c85b81a09f 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1764,11 +1764,11 @@ static void tdx_track(struct kvm *kvm) kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE); } -static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, u64 mirror_spte) +static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, + enum pg_level level, u64 old_spte) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - kvm_pfn_t pfn = spte_to_pfn(mirror_spte); + kvm_pfn_t pfn = spte_to_pfn(old_spte); gpa_t gpa = gfn_to_gpa(gfn); u64 err, entry, level_state; @@ -1780,16 +1780,16 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, * there can't be anything populated in the private EPT. */ if (KVM_BUG_ON(!is_hkid_assigned(to_kvm_tdx(kvm)), kvm)) - return; + return -EIO; /* TODO: handle large pages. */ if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm)) - return; + return -EIO; err = tdh_do_no_vcpus(tdh_mem_range_block, kvm, &kvm_tdx->td, gpa, level, &entry, &level_state); if (TDX_BUG_ON_2(err, TDH_MEM_RANGE_BLOCK, entry, level_state, kvm)) - return; + return -EIO; /* * TDX requires TLB tracking before dropping private page. Do @@ -1805,22 +1805,40 @@ static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, err = tdh_do_no_vcpus(tdh_mem_page_remove, kvm, &kvm_tdx->td, gpa, level, &entry, &level_state); if (TDX_BUG_ON_2(err, TDH_MEM_PAGE_REMOVE, entry, level_state, kvm)) - return; + return -EIO; err = tdh_phymem_page_wbinvd_hkid((u16)kvm_tdx->hkid, pfn); if (TDX_BUG_ON(err, TDH_PHYMEM_PAGE_WBINVD, kvm)) - return; + return -EIO; tdx_quirk_reset_paddr(PFN_PHYS(pfn), PAGE_SIZE); + return 0; } +/* + * Handle changes for + * (1) leaf SPTEs from non-present to present + * (2) non-leaf SPTEs from non-present to present + * (3) leaf SPTEs from present to non-present + * + * - (1) and (2) must be under shared mmu_lock. If (1) and (2) are under + * exclusive mmu_lock (currently impossible), contention errors may lead to + * KVM_BUG_ON() in handle_changed_spte(), e.g., due to tdx_mem_page_aug(), + * tdx_mem_page_add(), or tdh_mem_sept_add() contending with tdh_vp_enter() + * due to zero-step mitigation or contending with TDCALLs. + * - (3) must be under write mmu_lock. If (3) is under shared mmu_lock + * (currently impossible), warnings will be generated due to + * lockdep_assert_held_write() or TDX_BUG_ON() caused by concurrent BLOCK, + * TRACK, REMOVE. + * - Promotion/demotion is not yet supported. + */ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, u64 new_spte, enum pg_level level) { lockdep_assert_held(&kvm->mmu_lock); - if (KVM_BUG_ON(is_shadow_present_pte(old_spte), kvm)) - return -EIO; + if (is_shadow_present_pte(old_spte)) + return tdx_sept_remove_private_spte(kvm, gfn, level, old_spte); if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) return -EIO; @@ -3446,7 +3464,6 @@ int __init tdx_hardware_setup(void) vt_x86_ops.set_external_spte = tdx_sept_set_private_spte; vt_x86_ops.free_external_spt = tdx_sept_free_private_spt; - vt_x86_ops.remove_external_spte = tdx_sept_remove_private_spte; vt_x86_ops.protected_apic_has_interrupt = tdx_protected_apic_has_interrupt; return 0; From 0cef26b537ffa963d719c529f8ff604c1db505fd Mon Sep 17 00:00:00 2001 From: Yan Zhao Date: Sat, 9 May 2026 15:57:19 +0800 Subject: [PATCH 1866/5214] KVM: TDX: Rename tdx_sept_remove_private_spte() to show it's for leaf SPTEs Rename tdx_sept_remove_private_spte() to tdx_sept_remove_leaf_spte() to clearly show that this function is for removal of leaf SPTEs. No functional change intended. Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075719.4338-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/tdx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 54c85b81a09f..b1c273dfbe4c 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1764,8 +1764,8 @@ static void tdx_track(struct kvm *kvm) kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE); } -static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, u64 old_spte) +static int tdx_sept_remove_leaf_spte(struct kvm *kvm, gfn_t gfn, + enum pg_level level, u64 old_spte) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); kvm_pfn_t pfn = spte_to_pfn(old_spte); @@ -1838,7 +1838,7 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, lockdep_assert_held(&kvm->mmu_lock); if (is_shadow_present_pte(old_spte)) - return tdx_sept_remove_private_spte(kvm, gfn, level, old_spte); + return tdx_sept_remove_leaf_spte(kvm, gfn, level, old_spte); if (KVM_BUG_ON(!is_shadow_present_pte(new_spte), kvm)) return -EIO; @@ -2832,7 +2832,7 @@ void tdx_flush_tlb_current(struct kvm_vcpu *vcpu) void tdx_flush_tlb_all(struct kvm_vcpu *vcpu) { /* - * TDX has called tdx_track() in tdx_sept_remove_private_spte() to + * TDX has called tdx_track() in tdx_sept_remove_leaf_spte() to * ensure that private EPT will be flushed on the next TD enter. No need * to call tdx_track() here again even when this callback is a result of * zapping private EPT. From b35bda696e4416a01a064ccb5e67bca03132d8ec Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:57:30 +0800 Subject: [PATCH 1867/5214] KVM: x86: Move error handling inside free_external_spt() Move the logic for TDX's specific need to leak pages when reclaim fails inside the free_external_spt() op, so this can be done in TDX specific code and not the generic MMU. Do this by passing in "sp" instead of the external page table pointer. This way, TDX code can set sp->external_spt to NULL. Since the error is now handled internally in TDX code (by triggering KVM_BUG_ON() or TDX_BUG_ON_3(), which warn and stop the VM on any error), change the op to return void. This way it also operates like a normal free in that success is guaranteed from the caller's perspective. Opportunistically, drop the unused level and gfn args while adjusting the sp arg. [ Rick: Re-wrote log and massaged op name ] Co-developed-by: Rick Edgecombe Signed-off-by: Rick Edgecombe [ Yan: Updated patch log/function comment, dropped unused param in op ] Co-developed-by: Yan Zhao Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075730.4354-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-ops.h | 2 +- arch/x86/include/asm/kvm_host.h | 3 +-- arch/x86/kvm/mmu/tdp_mmu.c | 13 ++----------- arch/x86/kvm/vmx/tdx.c | 28 ++++++++++++++-------------- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 771d991562ca..83dc5086138b 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -97,7 +97,7 @@ KVM_X86_OP_OPTIONAL_RET0(get_mt_mask) KVM_X86_OP_OPTIONAL_RET0(tdp_has_smep) KVM_X86_OP(load_mmu_pgd) KVM_X86_OP_OPTIONAL_RET0(set_external_spte) -KVM_X86_OP_OPTIONAL_RET0(free_external_spt) +KVM_X86_OP_OPTIONAL(free_external_spt) KVM_X86_OP(has_wbinvd_exit) KVM_X86_OP(get_l2_tsc_offset) KVM_X86_OP(get_l2_tsc_multiplier) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index dc5625950e5b..a2ae2d7ca00f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1916,8 +1916,7 @@ struct kvm_x86_ops { u64 new_spte, enum pg_level level); /* Update external page tables for page table about to be freed. */ - int (*free_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - void *external_spt); + void (*free_external_spt)(struct kvm *kvm, struct kvm_mmu_page *sp); bool (*has_wbinvd_exit)(void); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 72d323f2d0dc..74531e4bbee6 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -455,17 +455,8 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) handle_changed_spte(kvm, sp, gfn, old_spte, FROZEN_SPTE, level, shared); } - if (is_mirror_sp(sp) && - WARN_ON(kvm_x86_call(free_external_spt)(kvm, base_gfn, sp->role.level, - sp->external_spt))) { - /* - * Failed to free page table page in mirror page table and - * there is nothing to do further. - * Intentionally leak the page to prevent the kernel from - * accessing the encrypted page. - */ - sp->external_spt = NULL; - } + if (is_mirror_sp(sp)) + kvm_x86_call(free_external_spt)(kvm, sp); call_rcu(&sp->rcu_head, tdp_mmu_free_sp_rcu_callback); } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b1c273dfbe4c..b3185cd9c6d6 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1849,27 +1849,27 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, u64 old_spte, return tdx_sept_map_leaf_spte(kvm, gfn, level, new_spte); } -static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) +/* + * Handle changes for non-leaf SPTEs from present to non-present. + * Must be under exclusive mmu_lock and cannot fail. + */ +static void tdx_sept_free_private_spt(struct kvm *kvm, struct kvm_mmu_page *sp) { - struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - /* - * free_external_spt() is only called after hkid is freed when TD is - * tearing down. * KVM doesn't (yet) zap page table pages in mirror page table while * TD is active, though guest pages mapped in mirror page table could be * zapped during TD is active, e.g. for shared <-> private conversion * and slot move/deletion. + * + * In other words, KVM should only free mirror page tables after the + * TD's hkid is freed, when the TD is being torn down. + * + * If the S-EPT PTE can't be removed for any reason, intentionally leak + * the page to prevent the kernel from accessing the encrypted page. */ - if (KVM_BUG_ON(is_hkid_assigned(kvm_tdx), kvm)) - return -EIO; - - /* - * The HKID assigned to this TD was already freed and cache was - * already flushed. We don't have to flush again. - */ - return tdx_reclaim_page(virt_to_page(private_spt)); + if (KVM_BUG_ON(is_hkid_assigned(to_kvm_tdx(kvm)), kvm) || + tdx_reclaim_page(virt_to_page(sp->external_spt))) + sp->external_spt = NULL; } void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, From 110d4d263450e4172db2f71053d9382320de7e82 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Sat, 9 May 2026 15:57:40 +0800 Subject: [PATCH 1868/5214] KVM: TDX: Move external page table freeing to TDX code Move the freeing of external page tables into the reclaim operation that lives in TDX code. The TDP MMU supports traversing the TDP without holding locks. Page tables need to be freed via RCU to prevent walking one that gets freed. While none of these lockless walk operations actually happen for the mirror page table, the TDP MMU nonetheless frees the mirror page table in the same way, and (because it's a handy place to plug it in) the external page table as well. However, the external page table definitely can't be walked once the page table pages are reclaimed from the TDX module. The TDX module releases the page for the host VMM to use, so this RCU-time free is unnecessary for the external page table. So move the free_page() call to TDX code. Create an tdp_mmu_free_unused_sp() to allow for freeing external page tables that have never left the TDP MMU code (i.e. don't need to be freed in a special way). Link: https://lore.kernel.org/kvm/aYpjNrtGmogNzqwT@google.com [Based on a diff by Sean, added log] Co-developed-by: Rick Edgecombe Signed-off-by: Rick Edgecombe Signed-off-by: Yan Zhao Link: https://patch.msgid.link/20260509075740.4371-1-yan.y.zhao@intel.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/tdp_mmu.c | 16 +++++++++++----- arch/x86/kvm/vmx/tdx.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 74531e4bbee6..5b3041138301 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -53,13 +53,18 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm) rcu_barrier(); } -static void tdp_mmu_free_sp(struct kvm_mmu_page *sp) +static void __tdp_mmu_free_sp(struct kvm_mmu_page *sp) { - free_page((unsigned long)sp->external_spt); free_page((unsigned long)sp->spt); kmem_cache_free(mmu_page_header_cache, sp); } +static void tdp_mmu_free_unused_sp(struct kvm_mmu_page *sp) +{ + free_page((unsigned long)sp->external_spt); + __tdp_mmu_free_sp(sp); +} + /* * This is called through call_rcu in order to free TDP page table memory * safely with respect to other kernel threads that may be operating on @@ -73,7 +78,8 @@ static void tdp_mmu_free_sp_rcu_callback(struct rcu_head *head) struct kvm_mmu_page *sp = container_of(head, struct kvm_mmu_page, rcu_head); - tdp_mmu_free_sp(sp); + WARN_ON_ONCE(sp->external_spt); + __tdp_mmu_free_sp(sp); } void kvm_tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root) @@ -1268,7 +1274,7 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) * failed, e.g. because a different task modified the SPTE. */ if (r) { - tdp_mmu_free_sp(sp); + tdp_mmu_free_unused_sp(sp); goto retry; } @@ -1579,7 +1585,7 @@ static int tdp_mmu_split_huge_pages_root(struct kvm *kvm, * installs its own sp in place of the last sp we tried to split. */ if (sp) - tdp_mmu_free_sp(sp); + tdp_mmu_free_unused_sp(sp); return 0; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b3185cd9c6d6..54f9eb1a0202 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1869,7 +1869,16 @@ static void tdx_sept_free_private_spt(struct kvm *kvm, struct kvm_mmu_page *sp) */ if (KVM_BUG_ON(is_hkid_assigned(to_kvm_tdx(kvm)), kvm) || tdx_reclaim_page(virt_to_page(sp->external_spt))) - sp->external_spt = NULL; + goto out; + + /* + * Immediately free the S-EPT page because RCU-time free is unnecessary + * after TDH.PHYMEM.PAGE.RECLAIM ensures there are no outstanding + * readers. + */ + free_page((unsigned long)sp->external_spt); +out: + sp->external_spt = NULL; } void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, From 8862376260c4a19329c8ba8b31d2e12510d2401d Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:46:55 +0000 Subject: [PATCH 1869/5214] KVM: nSVM: Stop leaking single-stepping on VMRUN into L2 According to the APM, TF on VMRUN causes a #DB after VMRUN completes on the _host_ side. However, KVM injects a #DB in L2 context instead (or exits to userspace if KVM_GUESTDBG_SINGLESTEP is set) in kvm_skip_emulated_instruction(). Avoid single-step handling on VMRUN by open-coding the rest of kvm_skip_emulated_instruction() in nested_svm_vmrun(). This doesn't look pretty, but following changes will need to open-code kvm_pmu_instruction_retired() anyway, and will cleanup the code. This ignores TF on VMRUN instead of injecting a spurious exception into L2. Document this virtualization hole with a FIXME. Note that a failed VMRUN would have been correctly single-stepped, but now TF is always ignored for consistency and simplicity purposes. VMX does not support TF on a successful VMLAUNCH/VMRESUME, so it's unlikely that single-stepping VMRUN properly is important, especially if it's only for failed VMRUNs. Fixes: c8e16b78c614 ("x86: KVM: svm: eliminate hardcoded RIP advancement from vmrun_interception()") Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-2-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 18 +++++++++++++++--- arch/x86/kvm/svm/svm.c | 2 +- arch/x86/kvm/svm/svm.h | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index b326420ac883..a7ed06d88697 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -30,6 +30,7 @@ #include "lapic.h" #include "svm.h" #include "hyperv.h" +#include "pmu.h" #define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK @@ -1140,11 +1141,22 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) return kvm_handle_memory_failure(vcpu, X86EMUL_IO_NEEDED, NULL); /* Advance RIP past VMRUN as part of the nested #VMEXIT. */ - return kvm_skip_emulated_instruction(vcpu); + if (!svm_skip_emulated_instruction(vcpu)) + return 0; + + kvm_pmu_instruction_retired(vcpu); + return 1; } - /* At this point, VMRUN is guaranteed to not fault; advance RIP. */ - ret = kvm_skip_emulated_instruction(vcpu); + /* + * At this point, VMRUN is guaranteed to not fault; advance RIP. + * + * FIXME: If TF is set on VMRUN should inject a #DB (or handle guest + * debugging) right after #VMEXIT, right now it's just ignored. + */ + ret = svm_skip_emulated_instruction(vcpu); + if (ret) + kvm_pmu_instruction_retired(vcpu); /* * Since vmcb01 is not in use, we can use it to store some of the L1 diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 62d374ceffc1..ab1014986b91 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -333,7 +333,7 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, return 1; } -static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) +int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) { return __svm_skip_emulated_instruction(vcpu, EMULTYPE_SKIP, true); } diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 2b6733dffd76..e5d9984ef632 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -832,6 +832,8 @@ static inline void svm_enable_intercept_for_msr(struct kvm_vcpu *vcpu, svm_set_intercept_for_msr(vcpu, msr, type, true); } +int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu); + /* nested.c */ #define NESTED_EXIT_HOST 0 /* Exit handled on host level */ From 42ff88db18a5a42f619eab1d862e04e6505e8ee6 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:46:56 +0000 Subject: [PATCH 1870/5214] KVM: nSVM: Bail early out of VMRUN emulation if advancing RIP fails If svm_skip_emulation_instruction() fails, then RIP could not be advanced correctly (e.g. decode failure when NextRIP is not available). KVM will exit to userspace to handle the emulation failure, but only after stuffing the wrong RIP into vmcb01 and entering guest mode. Bail early and exit to userspace before committing any side-effects of emulating the VMRUN (e.g. entering guest mode). Fixes: c8e16b78c614 ("x86: KVM: svm: eliminate hardcoded RIP advancement from vmrun_interception()") Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-3-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index a7ed06d88697..6e26c8e1b771 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1154,9 +1154,10 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) * FIXME: If TF is set on VMRUN should inject a #DB (or handle guest * debugging) right after #VMEXIT, right now it's just ignored. */ - ret = svm_skip_emulated_instruction(vcpu); - if (ret) - kvm_pmu_instruction_retired(vcpu); + if (!svm_skip_emulated_instruction(vcpu)) + return 0; + + kvm_pmu_instruction_retired(vcpu); /* * Since vmcb01 is not in use, we can use it to store some of the L1 @@ -1186,7 +1187,7 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) nested_svm_vmexit(svm); } - return ret; + return 1; } /* Copy state save area fields which are handled by VMRUN */ From 0a35c2a051f34890f39befb5dc4f1cf41ec12b2a Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:46:57 +0000 Subject: [PATCH 1871/5214] KVM: nSVM: Unify RIP and PMU handling calls when emulating VMRUN The code paths for advancing RIP and retiring the instruction for RIP are very similar whether or not caching vmcb12 succeeds. The only difference is handling mapping failures (i.e. EFAULT). Pull the mapping failure handling out and unify the calls to svm_skip_emulated_instruction() and kvm_pmu_instruction_retired(), but return immediately after if copying and caching vmcb12 failed. A nice side effect of this is that the FIXME comment is now above the only code path calling svm_skip_emulated_instruction(). Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-4-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 6e26c8e1b771..2ef31efd4c9d 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1136,20 +1136,13 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) } ret = nested_svm_copy_vmcb12_to_cache(vcpu, vmcb12_gpa); - if (ret) { - if (ret == -EFAULT) - return kvm_handle_memory_failure(vcpu, X86EMUL_IO_NEEDED, NULL); - - /* Advance RIP past VMRUN as part of the nested #VMEXIT. */ - if (!svm_skip_emulated_instruction(vcpu)) - return 0; - - kvm_pmu_instruction_retired(vcpu); - return 1; - } + if (ret == -EFAULT) + return kvm_handle_memory_failure(vcpu, X86EMUL_IO_NEEDED, NULL); /* - * At this point, VMRUN is guaranteed to not fault; advance RIP. + * At this point, VMRUN is guaranteed to not fault; advance RIP. If + * caching vmcb12 failed for other reasons, return immediately afterward + * as a nested #VMEXIT was already set up. * * FIXME: If TF is set on VMRUN should inject a #DB (or handle guest * debugging) right after #VMEXIT, right now it's just ignored. @@ -1159,6 +1152,9 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) kvm_pmu_instruction_retired(vcpu); + if (ret) + return 1; + /* * Since vmcb01 is not in use, we can use it to store some of the L1 * state. From 9be579d2265185ec92d75e4540fc6c4e621f1667 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:46:58 +0000 Subject: [PATCH 1872/5214] KVM: nSVM: Move VMRUN instruction retirement after entering guest mode A successful VMRUN retires in guest mode and should be counted by the PMU as a guest instruction. Move the call to kvm_pmu_instruction_retired() after potentially entering guest mode, such that VMRUN is counted correctly. The PMU event will be matched against L2's CPL, but otherwise this does not change the behavior in terms of guest vs. host, because KVM does not virtualize Host-Only/Guest-Only PMC controls yet, so all instructions are counted regardless of the vCPU's host/guest state. But this change is needed for the incoming support for Host-Only/Guest-Only controls to count VMRUN correctly. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-5-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 2ef31efd4c9d..3a90b7f83d8e 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1150,10 +1150,8 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) if (!svm_skip_emulated_instruction(vcpu)) return 0; - kvm_pmu_instruction_retired(vcpu); - if (ret) - return 1; + goto insn_retired; /* * Since vmcb01 is not in use, we can use it to store some of the L1 @@ -1183,6 +1181,12 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) nested_svm_vmexit(svm); } +insn_retired: + /* + * A successful VMRUN is counted by the PMU in guest mode, so only + * retire the instruction after potentially entering guest mode. + */ + kvm_pmu_instruction_retired(vcpu); return 1; } From 277eb65b6cc3442a9cde58291d3f7b21ba79b770 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:46:59 +0000 Subject: [PATCH 1873/5214] KVM: x86: Move enable_pmu/enable_mediated_pmu to pmu.h and pmu.c The declaration and definition of enable_pmu/enable_mediated_pmu semantically belongs in pmu.h and pmu.c, and more importantly, pmu.h uses enable_mediated_pmu and relies on the caller including x86.h. There is already precedence for other module params defined outside of x86.c, so move enable_pmu/enable_mediated_pmu to pmu.c. No functional change intended. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-6-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/pmu.c | 10 ++++++++++ arch/x86/kvm/pmu.h | 3 +++ arch/x86/kvm/x86.c | 9 --------- arch/x86/kvm/x86.h | 3 --- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index e218352e3423..d6ac3c55fce5 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "x86.h" @@ -33,6 +34,15 @@ static struct x86_pmu_capability __read_mostly kvm_host_pmu; struct x86_pmu_capability __read_mostly kvm_pmu_cap; EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_pmu_cap); +/* Enable/disable PMU virtualization */ +bool __read_mostly enable_pmu = true; +EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_pmu); +module_param(enable_pmu, bool, 0444); + +/* Enable/disabled mediated PMU virtualization. */ +bool __read_mostly enable_mediated_pmu; +EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_mediated_pmu); + struct kvm_pmu_emulated_event_selectors { u64 INSTRUCTIONS_RETIRED; u64 BRANCH_INSTRUCTIONS_RETIRED; diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 0925246731cb..b1f2418e960a 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -53,6 +53,9 @@ struct kvm_pmu_ops { const u32 MSR_STRIDE; }; +extern bool enable_pmu; +extern bool enable_mediated_pmu; + void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); void kvm_handle_guest_mediated_pmi(void); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 48f259015ce4..3447186edfa3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -182,15 +182,6 @@ module_param(force_emulation_prefix, int, 0644); int __read_mostly pi_inject_timer = -1; module_param(pi_inject_timer, bint, 0644); -/* Enable/disable PMU virtualization */ -bool __read_mostly enable_pmu = true; -EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_pmu); -module_param(enable_pmu, bool, 0444); - -/* Enable/disabled mediated PMU virtualization. */ -bool __read_mostly enable_mediated_pmu; -EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_mediated_pmu); - bool __read_mostly eager_page_split = true; module_param(eager_page_split, bool, 0644); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 38a905fa86de..30a69effc81e 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -490,9 +490,6 @@ fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu); extern struct kvm_caps kvm_caps; extern struct kvm_host_values kvm_host; -extern bool enable_pmu; -extern bool enable_mediated_pmu; - void kvm_setup_xss_caps(void); /* From 103cd7deda589c47f10cdf0f65cae21dbca35b9c Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:00 +0000 Subject: [PATCH 1874/5214] KVM: x86/pmu: Rename reprogram_counters() to clarify usage Rename reprogram_counters() to kvm_pmu_request_counters_reprogram() clarifying that it is more similar to kvm_pmu_request_counter_reprogram(), and less similar to reprogram_counter(). The kvm_pmu_* prefix is also appropriate as the function is exposed in the header. Opportunistically rename the argument from 'diff' to 'counters'. No functional change intended. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-7-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/pmu.c | 2 +- arch/x86/kvm/pmu.h | 7 ++++--- arch/x86/kvm/vmx/pmu_intel.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index d6ac3c55fce5..afbc731e7217 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -889,7 +889,7 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (pmu->global_ctrl != data) { diff = pmu->global_ctrl ^ data; pmu->global_ctrl = data; - reprogram_counters(pmu, diff); + kvm_pmu_request_counters_reprogram(pmu, diff); } /* * Unconditionally forward writes to vendor code, i.e. to the diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index b1f2418e960a..f8286067722b 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -210,14 +210,15 @@ static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) kvm_make_request(KVM_REQ_PMU, pmc->vcpu); } -static inline void reprogram_counters(struct kvm_pmu *pmu, u64 diff) +static inline void kvm_pmu_request_counters_reprogram(struct kvm_pmu *pmu, + u64 counters) { int bit; - if (!diff) + if (!counters) return; - for_each_set_bit(bit, (unsigned long *)&diff, X86_PMC_IDX_MAX) + for_each_set_bit(bit, (unsigned long *)&counters, X86_PMC_IDX_MAX) set_bit(bit, pmu->reprogram_pmi); kvm_make_request(KVM_REQ_PMU, pmu_to_vcpu(pmu)); } diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 27eb76e6b6a0..9bd77843d8da 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -391,7 +391,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (pmu->pebs_enable != data) { diff = pmu->pebs_enable ^ data; pmu->pebs_enable = data; - reprogram_counters(pmu, diff); + kvm_pmu_request_counters_reprogram(pmu, diff); } break; case MSR_IA32_DS_AREA: From e48810e06f45251a56bc007485854112e6cfc430 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:01 +0000 Subject: [PATCH 1875/5214] KVM: x86/pmu: Do a single atomic OR when reprogramming counters Do a single atomic OR using the atomic overlay of reprogram_pmi bitmask, instead of one atomic set_bit() call per counter. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-8-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/pmu.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index f8286067722b..0e99022168a8 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -213,13 +213,10 @@ static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) static inline void kvm_pmu_request_counters_reprogram(struct kvm_pmu *pmu, u64 counters) { - int bit; - if (!counters) return; - for_each_set_bit(bit, (unsigned long *)&counters, X86_PMC_IDX_MAX) - set_bit(bit, pmu->reprogram_pmi); + atomic64_or(counters, &pmu->__reprogram_pmi); kvm_make_request(KVM_REQ_PMU, pmu_to_vcpu(pmu)); } From 232eadc3110b6b00eda84fb8670ddb20a64e1726 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:02 +0000 Subject: [PATCH 1876/5214] KVM: x86/pmu: Check mediated PMU counter enablement before event filters If the guest disables the counter (by clearing ARCH_PERFMON_EVENTSEL_ENABLE), KVM still performs the PMU filter lookup, even though it doesn't end up changing eventsel_hw. Check if the counter is enabled by the guest before doing the potentially expensive PMU filter lookup. Suggested-by: Sean Christopherson Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-9-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index afbc731e7217..67dbbd4c7303 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -532,7 +532,7 @@ static bool pmc_is_event_allowed(struct kvm_pmc *pmc) static void kvm_mediated_pmu_refresh_event_filter(struct kvm_pmc *pmc) { - bool allowed = pmc_is_event_allowed(pmc); + bool allowed = pmc_is_locally_enabled(pmc) && pmc_is_event_allowed(pmc); struct kvm_pmu *pmu = pmc_to_pmu(pmc); if (pmc_is_gp(pmc)) { From f98ef41a5af4c71a699df69399c9d932c8c1bf69 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:03 +0000 Subject: [PATCH 1877/5214] KVM: x86/pmu: Add support for KVM_X86_PMU_OP_OPTIONAL_RET0 Add definitions for KVM_X86_PMU_OP_OPTIONAL_RET0() to resolve to __static_call_return0, similar to KVM_X86_OP_OPTIONAL_RET0(). Move the definition of kvm_pmu_call() to pmu.h, and add declarations for the static PMU calls in the header to allow making callbacks from the header in following changes. Suggested-by: Sean Christopherson Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-10-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-pmu-ops.h | 4 +++- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/pmu.c | 4 ++++ arch/x86/kvm/pmu.h | 8 ++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h index d5452b3433b7..0616243c84cf 100644 --- a/arch/x86/include/asm/kvm-x86-pmu-ops.h +++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #if !defined(KVM_X86_PMU_OP) || \ - !defined(KVM_X86_PMU_OP_OPTIONAL) + !defined(KVM_X86_PMU_OP_OPTIONAL) || \ + !defined(KVM_X86_PMU_OP_OPTIONAL_RET0) #error Missing one or more KVM_X86_PMU_OP #defines #else @@ -31,3 +32,4 @@ KVM_X86_PMU_OP(mediated_put) #undef KVM_X86_PMU_OP #undef KVM_X86_PMU_OP_OPTIONAL +#undef KVM_X86_PMU_OP_OPTIONAL_RET0 diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index ef353daeacc9..dd761073d618 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2065,7 +2065,6 @@ extern bool __read_mostly enable_device_posted_irqs; extern struct kvm_x86_ops kvm_x86_ops; #define kvm_x86_call(func) static_call(kvm_x86_##func) -#define kvm_pmu_call(func) static_call(kvm_x86_pmu_##func) #define KVM_X86_OP(func) \ DECLARE_STATIC_CALL(kvm_x86_##func, *(((struct kvm_x86_ops *)0)->func)); diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 67dbbd4c7303..9b7e39610be2 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -98,6 +98,7 @@ static struct kvm_pmu_ops kvm_pmu_ops __read_mostly; DEFINE_STATIC_CALL_NULL(kvm_x86_pmu_##func, \ *(((struct kvm_pmu_ops *)0)->func)); #define KVM_X86_PMU_OP_OPTIONAL KVM_X86_PMU_OP +#define KVM_X86_PMU_OP_OPTIONAL_RET0 KVM_X86_PMU_OP #include void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) @@ -109,6 +110,9 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) #define KVM_X86_PMU_OP(func) \ WARN_ON(!kvm_pmu_ops.func); __KVM_X86_PMU_OP(func) #define KVM_X86_PMU_OP_OPTIONAL __KVM_X86_PMU_OP +#define KVM_X86_PMU_OP_OPTIONAL_RET0(func) \ + static_call_update(kvm_x86_pmu_##func, (void *)kvm_pmu_ops.func ? : \ + (void *)__static_call_return0); #include #undef __KVM_X86_PMU_OP } diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 0e99022168a8..a062f0bc3dbb 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -53,6 +53,14 @@ struct kvm_pmu_ops { const u32 MSR_STRIDE; }; +#define kvm_pmu_call(func) static_call(kvm_x86_pmu_##func) + +#define KVM_X86_PMU_OP(func) \ + DECLARE_STATIC_CALL(kvm_x86_pmu_##func, *(((struct kvm_pmu_ops *)0)->func)); +#define KVM_X86_PMU_OP_OPTIONAL KVM_X86_PMU_OP +#define KVM_X86_PMU_OP_OPTIONAL_RET0 KVM_X86_PMU_OP +#include + extern bool enable_pmu; extern bool enable_mediated_pmu; From 2ed72677a5c1025edb1398bbfda0f558ee19b4df Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:04 +0000 Subject: [PATCH 1878/5214] KVM: x86/pmu: Disable counters based on Host-Only/Guest-Only bits in SVM Introduce an optional per-vendor PMU callback for checking if a counter is disabled in the current mode, and register a callback on AMD to disable a counter based on the vCPU's setting of Host-Only or Guest-Only EVENT_SELECT bits with the mediated PMU. If EFER.SVME is set, all events are counted if both bits are set or cleared. If only one bit is set, the counter is disabled if the vCPU context does not match the set bit. If EFER.SVME is cleared, the counter is disabled if any of the bits is set, otherwise all events are counted. Note that a Linux guest correctly handles this and clears Host-Only when EFER.SVME is cleared, see commit 1018faa6cf23 ("perf/x86/kvm: Fix Host-Only/Guest-Only counting with SVM disabled"). The callback is made from pmc_is_locally_enabled(), which is used for the mediated PMU when updating eventsel_hw in kvm_mediated_pmu_refresh_eventsel_hw(), as well as when checking what PMCs count instructions/branches for emulation in kvm_pmu_recalc_pmc_emulation(). Host-Only and Guest-Only bits are currently reserved, so this change is a noop, but the bits will be allowed with mediated PMU in a following change when fully supported. Originally-by: Jim Mattson Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-11-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-pmu-ops.h | 1 + arch/x86/include/asm/perf_event.h | 2 ++ arch/x86/kvm/pmu.c | 1 + arch/x86/kvm/pmu.h | 4 +++- arch/x86/kvm/svm/pmu.c | 32 ++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h index 0616243c84cf..4a223c2793e3 100644 --- a/arch/x86/include/asm/kvm-x86-pmu-ops.h +++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h @@ -24,6 +24,7 @@ KVM_X86_PMU_OP(init) KVM_X86_PMU_OP_OPTIONAL(reset) KVM_X86_PMU_OP_OPTIONAL(deliver_pmi) KVM_X86_PMU_OP_OPTIONAL(cleanup) +KVM_X86_PMU_OP_OPTIONAL_RET0(pmc_is_disabled_in_current_mode) KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) KVM_X86_PMU_OP(mediated_load) diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 752cb319d5ea..1eb13673e889 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -60,6 +60,8 @@ #define AMD64_EVENTSEL_INT_CORE_ENABLE (1ULL << 36) #define AMD64_EVENTSEL_GUESTONLY (1ULL << 40) #define AMD64_EVENTSEL_HOSTONLY (1ULL << 41) +#define AMD64_EVENTSEL_HOST_GUEST_MASK \ + (AMD64_EVENTSEL_HOSTONLY | AMD64_EVENTSEL_GUESTONLY) #define AMD64_EVENTSEL_INT_CORE_SEL_SHIFT 37 #define AMD64_EVENTSEL_INT_CORE_SEL_MASK \ diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 9b7e39610be2..8159b07e9bc2 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -100,6 +100,7 @@ static struct kvm_pmu_ops kvm_pmu_ops __read_mostly; #define KVM_X86_PMU_OP_OPTIONAL KVM_X86_PMU_OP #define KVM_X86_PMU_OP_OPTIONAL_RET0 KVM_X86_PMU_OP #include +EXPORT_STATIC_CALL_GPL(kvm_x86_pmu_pmc_is_disabled_in_current_mode); void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) { diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index a062f0bc3dbb..71c7853e8ae5 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -36,6 +36,7 @@ struct kvm_pmu_ops { void (*reset)(struct kvm_vcpu *vcpu); void (*deliver_pmi)(struct kvm_vcpu *vcpu); void (*cleanup)(struct kvm_vcpu *vcpu); + bool (*pmc_is_disabled_in_current_mode)(struct kvm_pmc *pmc); bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); void (*mediated_load)(struct kvm_vcpu *vcpu); @@ -201,7 +202,8 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) pmc->idx - KVM_FIXED_PMC_BASE_IDX) & (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER); - return pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE; + return (pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE) && + !kvm_pmu_call(pmc_is_disabled_in_current_mode)(pmc); } extern struct x86_pmu_capability kvm_pmu_cap; diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c index 7aa298eeb072..41ee6532290e 100644 --- a/arch/x86/kvm/svm/pmu.c +++ b/arch/x86/kvm/svm/pmu.c @@ -260,6 +260,37 @@ static void amd_mediated_pmu_put(struct kvm_vcpu *vcpu) wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, pmu->global_status); } +static bool amd_pmc_is_disabled_in_current_mode(struct kvm_pmc *pmc) +{ + struct kvm_vcpu *vcpu = pmc->vcpu; + u64 host_guest_bits; + + if (!kvm_vcpu_has_mediated_pmu(vcpu)) + return false; + + /* Common code is supposed to check the common enable bit */ + if (WARN_ON_ONCE(!(pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE))) + return false; + + /* If both bits are cleared, the counter is always enabled */ + host_guest_bits = pmc->eventsel & AMD64_EVENTSEL_HOST_GUEST_MASK; + if (!host_guest_bits) + return false; + + /* If EFER.SVME=0 and either bit is set, the counter is disabled */ + if (!(vcpu->arch.efer & EFER_SVME)) + return true; + + /* + * If EFER.SVME=1, the counter is disabled iff only one of the bits is + * set AND the set bit doesn't match the vCPU mode. + */ + if (host_guest_bits == AMD64_EVENTSEL_HOST_GUEST_MASK) + return false; + + return !!(host_guest_bits & AMD64_EVENTSEL_GUESTONLY) != is_guest_mode(vcpu); +} + struct kvm_pmu_ops amd_pmu_ops __initdata = { .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, .msr_idx_to_pmc = amd_msr_idx_to_pmc, @@ -269,6 +300,7 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = { .set_msr = amd_pmu_set_msr, .refresh = amd_pmu_refresh, .init = amd_pmu_init, + .pmc_is_disabled_in_current_mode = amd_pmc_is_disabled_in_current_mode, .is_mediated_pmu_supported = amd_pmu_is_mediated_pmu_supported, .mediated_load = amd_mediated_pmu_load, From c0d4c9c54c256cea13e3bea82127cc34b833ff08 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:05 +0000 Subject: [PATCH 1879/5214] KVM: x86/pmu: Track mediated PMU counters with mode-specific enables Instead of always checking of a counter needs to be disabled for mode-specific reasons (e.g. Host-Only/Guest-Only bits in SVM), add a bitmap to track such counters. Set the bit for counters using either Host-Only or Guest-Only bits in EVENTSEL on SVM. This bitmap will also be reused in following changes to selectively apply changes to such counters. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-12-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/pmu.c | 1 + arch/x86/kvm/pmu.h | 9 +++++++-- arch/x86/kvm/svm/pmu.c | 6 ++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index dd761073d618..5934cc3994c5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -612,6 +612,8 @@ struct kvm_pmu { DECLARE_BITMAP(pmc_counting_instructions, X86_PMC_IDX_MAX); DECLARE_BITMAP(pmc_counting_branches, X86_PMC_IDX_MAX); + DECLARE_BITMAP(pmc_has_mode_specific_enables, X86_PMC_IDX_MAX); + u64 ds_area; u64 pebs_enable; u64 pebs_enable_rsvd; diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 8159b07e9bc2..84c834ad2cd4 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -936,6 +936,7 @@ static void kvm_pmu_reset(struct kvm_vcpu *vcpu) pmu->need_cleanup = false; bitmap_zero(pmu->reprogram_pmi, X86_PMC_IDX_MAX); + bitmap_zero(pmu->pmc_has_mode_specific_enables, X86_PMC_IDX_MAX); kvm_for_each_pmc(pmu, pmc, i, pmu->all_valid_pmc_idx) { pmc_stop_counter(pmc); diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 71c7853e8ae5..34c3c6913ef6 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -202,8 +202,13 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) pmc->idx - KVM_FIXED_PMC_BASE_IDX) & (INTEL_FIXED_0_KERNEL | INTEL_FIXED_0_USER); - return (pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE) && - !kvm_pmu_call(pmc_is_disabled_in_current_mode)(pmc); + if (!(pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE)) + return false; + + if (!test_bit(pmc->idx, pmu->pmc_has_mode_specific_enables)) + return true; + + return !kvm_pmu_call(pmc_is_disabled_in_current_mode)(pmc); } extern struct x86_pmu_capability kvm_pmu_cap; diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c index 41ee6532290e..b892a25ea4ca 100644 --- a/arch/x86/kvm/svm/pmu.c +++ b/arch/x86/kvm/svm/pmu.c @@ -168,6 +168,12 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) pmc->eventsel = data; pmc->eventsel_hw = (data & ~AMD64_EVENTSEL_HOSTONLY) | AMD64_EVENTSEL_GUESTONLY; + + if (data & AMD64_EVENTSEL_HOST_GUEST_MASK) + __set_bit(pmc->idx, pmu->pmc_has_mode_specific_enables); + else + __clear_bit(pmc->idx, pmu->pmc_has_mode_specific_enables); + kvm_pmu_request_counter_reprogram(pmc); } return 0; From a02a25a652468efb5f3d19426352484d77b6b4d4 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Wed, 27 May 2026 23:47:06 +0000 Subject: [PATCH 1880/5214] KVM: x86/pmu: Reprogram Host/Guest-Only counters on nested transitions Reprogram PMU counters on nested transitions for the mediated PMU, to re-evaluate Host-Only and Guest-Only bits and enable/disable the PMU counters accordingly. For example, if Host-Only is set and Guest-Only is cleared, a counter should be disabled when entering guest mode and enabled when exiting guest mode. According to the APM, when EFER.SVME is cleared, setting Host-Only or Guest-Only disables the counter, so also trigger counter reprogramming when EFER.SVME is toggled. Counters setting any of Host-Only and Guest-Only bits are already being tracked in pmc_has_mode_specific_enables, use the bitmap to reprogram these counters. Reprogram the counters synchronously on nested VMRUN/#VMEXIT and EFER.SVME toggling. This is necessary as these instructions are counted based on the new CPU state (after the instruction is retired in hardware). Hence, the PMU needs to be updated before instruction emulation is completed and kvm_pmu_instruction_retired() is called. Defer reprogramming the counters when force leaving guest mode through svm_leave_nested() to avoid potentially reading stale state (e.g. incorrect EFER). All flows force leaving nested are non-architectural, so accuracy is irrelevant. Refactor a helper out of kvm_pmu_request_reprogram_counters() that accepts a boolean allowing synchronous vs deferred reprogramming, and use that from SVM code to support both scenarios. Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-13-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/pmu.c | 1 + arch/x86/kvm/pmu.h | 18 ++++++++++++++---- arch/x86/kvm/svm/nested.c | 12 ++++++++++++ arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/svm/svm.h | 23 +++++++++++++++++++++++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 84c834ad2cd4..b92dd2e58335 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -685,6 +685,7 @@ void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) kvm_for_each_pmc(pmu, pmc, bit, bitmap) kvm_pmu_recalc_pmc_emulation(pmu, pmc); } +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_pmu_handle_event); int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx) { diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 34c3c6913ef6..a5821d7c87f9 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -216,6 +216,7 @@ extern struct x86_pmu_capability kvm_pmu_cap; void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops); void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc); +void kvm_pmu_handle_event(struct kvm_vcpu *vcpu); static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) { @@ -225,14 +226,24 @@ static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) kvm_make_request(KVM_REQ_PMU, pmc->vcpu); } -static inline void kvm_pmu_request_counters_reprogram(struct kvm_pmu *pmu, - u64 counters) +static inline void __kvm_pmu_reprogram_counters(struct kvm_pmu *pmu, + u64 counters, + bool defer) { if (!counters) return; atomic64_or(counters, &pmu->__reprogram_pmi); - kvm_make_request(KVM_REQ_PMU, pmu_to_vcpu(pmu)); + if (defer) + kvm_make_request(KVM_REQ_PMU, pmu_to_vcpu(pmu)); + else + kvm_pmu_handle_event(pmu_to_vcpu(pmu)); +} + +static inline void kvm_pmu_request_counters_reprogram(struct kvm_pmu *pmu, + u64 counters) +{ + __kvm_pmu_reprogram_counters(pmu, counters, true); } /* @@ -261,7 +272,6 @@ static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu) } void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu); -void kvm_pmu_handle_event(struct kvm_vcpu *vcpu); int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data); int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx); bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr); diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 3a90b7f83d8e..d1d7bb20ff35 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -837,6 +837,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) /* Enter Guest-Mode */ enter_guest_mode(vcpu); + svm_pmu_handle_nested_transition(svm); /* * Filled at exit: exit_code, exit_info_1, exit_info_2, exit_int_info, @@ -1320,6 +1321,8 @@ void nested_svm_vmexit(struct vcpu_svm *svm) /* Exit Guest-Mode */ leave_guest_mode(vcpu); + svm_pmu_handle_nested_transition(svm); + svm->nested.vmcb12_gpa = 0; kvm_warn_on_nested_run_pending(vcpu); @@ -1537,6 +1540,15 @@ void svm_leave_nested(struct kvm_vcpu *vcpu) leave_guest_mode(vcpu); + /* + * Force leaving nested is a non-architectural flow so precision + * isn't a priority. Defer updating the PMU until the next vCPU + * run, potentially tolerating some imprecision to avoid poking + * into PMU state from arbitrary contexts (e.g. to avoid using + * stale state). + */ + __svm_pmu_handle_nested_transition(svm, true); + svm_switch_vmcb(svm, &svm->vmcb01); nested_svm_uninit_mmu_context(vcpu); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ab1014986b91..502433c5b189 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -265,6 +265,7 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer) set_exception_intercept(svm, GP_VECTOR); } + svm_pmu_handle_nested_transition(svm); kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); } diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index e5d9984ef632..87c6b105deef 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -25,6 +25,7 @@ #include "cpuid.h" #include "kvm_cache_regs.h" #include "x86.h" +#include "pmu.h" /* * Helpers to convert to/from physical addresses for pages whose address is @@ -897,6 +898,28 @@ void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, void nested_sync_control_from_vmcb02(struct vcpu_svm *svm); void svm_switch_vmcb(struct vcpu_svm *svm, struct kvm_vmcb_info *target_vmcb); + +static inline void __svm_pmu_handle_nested_transition(struct vcpu_svm *svm, + bool defer) +{ + struct kvm_pmu *pmu = vcpu_to_pmu(&svm->vcpu); + u64 counters = *(u64 *)pmu->pmc_has_mode_specific_enables; + + __kvm_pmu_reprogram_counters(pmu, counters, defer); +} + +static inline void svm_pmu_handle_nested_transition(struct vcpu_svm *svm) +{ + /* + * Do NOT defer reprogramming the counters by default. Instructions + * causing a state change are counted based on the _new_ CPU state + * (e.g. a successful VMRUN is counted in guest mode). Hence, the + * counters should be reprogrammed with the new state _before_ the + * instruction is potentially counted upon emulation completion. + */ + __svm_pmu_handle_nested_transition(svm, false); +} + extern struct kvm_x86_nested_ops svm_nested_ops; /* avic.c */ From f4805dcf2d2107b83329b56f62204e3a846ff7d2 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 27 May 2026 23:47:07 +0000 Subject: [PATCH 1881/5214] KVM: x86/pmu: Allow Host-Only/Guest-Only bits with nSVM and mediated PMU Now that KVM correctly handles Host-Only and Guest-Only bits in the event selector MSRs, allow the guest to set them if the vCPU advertises SVM and uses the mediated PMU. Signed-off-by: Jim Mattson Signed-off-by: Yosry Ahmed Link: https://patch.msgid.link/20260527234711.4175166-14-yosry@kernel.org Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/pmu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c index b892a25ea4ca..c18286545a7a 100644 --- a/arch/x86/kvm/svm/pmu.c +++ b/arch/x86/kvm/svm/pmu.c @@ -213,7 +213,11 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu) } pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(48) - 1; + pmu->reserved_bits = 0xfffffff000280000ull; + if (guest_cpu_cap_has(vcpu, X86_FEATURE_SVM) && kvm_vcpu_has_mediated_pmu(vcpu)) + pmu->reserved_bits &= ~AMD64_EVENTSEL_HOST_GUEST_MASK; + pmu->raw_event_mask = AMD64_RAW_EVENT_MASK; /* not applicable to AMD; but clean them to prevent any fall out */ pmu->counter_bitmask[KVM_PMC_FIXED] = 0; From a878096e0e86b44e758aafc6b26af97e8f548673 Mon Sep 17 00:00:00 2001 From: Wei-Lin Chang Date: Tue, 14 Apr 2026 01:03:31 +0100 Subject: [PATCH 1882/5214] KVM: arm64: nv: Rename vtcr_to_walk_info() to setup_s2_walk() This rename aligns the stage-2 walker better with the stage-1 walker. Also set up other non-VTCR walk info in the function. Signed-off-by: Wei-Lin Chang Link: https://patch.msgid.link/20260414000334.3947257-2-weilin.chang@arm.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 883b6c1008fb..00e8bc939baf 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,9 +378,12 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, return 0; } -static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi) +static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) { - wi->t0sz = vtcr & TCR_EL2_T0SZ_MASK; + u64 vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2); + + wi->baddr = vcpu_read_sys_reg(vcpu, VTTBR_EL2); + wi->t0sz = vtcr & VTCR_EL2_T0SZ_MASK; switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { case VTCR_EL2_TG0_4K: @@ -398,12 +401,12 @@ static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi) ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false)); wi->ha = vtcr & VTCR_EL2_HA; + wi->be = vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; } int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa, struct kvm_s2_trans *result) { - u64 vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2); struct s2_walk_info wi; int ret; @@ -412,11 +415,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa, if (!vcpu_has_nv(vcpu)) return 0; - wi.baddr = vcpu_read_sys_reg(vcpu, VTTBR_EL2); - - vtcr_to_walk_info(vtcr, &wi); - - wi.be = vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; + setup_s2_walk(vcpu, &wi); ret = walk_nested_s2_pgd(vcpu, gipa, &wi, result); if (ret) From d7768b4f718503e79e7c626d29e9131b747148ee Mon Sep 17 00:00:00 2001 From: Wei-Lin Chang Date: Tue, 14 Apr 2026 01:03:32 +0100 Subject: [PATCH 1883/5214] KVM: arm64: Factor out TG0/1 decoding of VTCR and TCR The current code decodes TCR.TG0/TG1 and VTCR.TG0 inline at several places. Extract this logic into helpers so the granule size can be derived in one place. This enables us to alter the effective granule size in the same place, which we will do in a later patch. Signed-off-by: Wei-Lin Chang Link: https://patch.msgid.link/20260414000334.3947257-3-weilin.chang@arm.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 77 ++++++++++++++++++++++++++--------------- arch/arm64/kvm/nested.c | 29 +++++++++------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 9f8f0ae8e86e..6ebcf65b4ffa 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -136,14 +136,58 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi) wi->e0poe = (wi->regime != TR_EL2) && (val & TCR2_EL1_E0POE); } +static unsigned int tcr_to_tg0_pgshift(u64 tcr) +{ + u64 tg0 = tcr & TCR_TG0_MASK; + + switch (tg0) { + case TCR_TG0_4K: + return 12; + case TCR_TG0_16K: + return 14; + case TCR_TG0_64K: + default: /* IMPDEF: treat any other value as 64k */ + return 16; + } +} + +static unsigned int tcr_to_tg1_pgshift(u64 tcr) +{ + u64 tg1 = tcr & TCR_TG1_MASK; + + switch (tg1) { + case TCR_TG1_4K: + return 12; + case TCR_TG1_16K: + return 14; + case TCR_TG1_64K: + default: /* IMPDEF: treat any other value as 64k */ + return 16; + } +} + +static unsigned int tcr_tg_pgshift(u64 tcr, bool upper_range) +{ + unsigned int shift; + + /* Someone was silly enough to encode TG0/TG1 differently */ + if (upper_range) + shift = tcr_to_tg1_pgshift(tcr); + else + shift = tcr_to_tg0_pgshift(tcr); + + return shift; +} + static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va) { - u64 hcr, sctlr, tcr, tg, ps, ia_bits, ttbr; + u64 hcr, sctlr, tcr, ps, ia_bits, ttbr; unsigned int stride, x; - bool va55, tbi, lva; + bool va55, tbi, lva, upper_range; va55 = va & BIT(55); + upper_range = va55 && wi->regime != TR_EL2; if (vcpu_has_nv(vcpu)) { hcr = __vcpu_sys_reg(vcpu, HCR_EL2); @@ -174,35 +218,12 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, BUG(); } - /* Someone was silly enough to encode TG0/TG1 differently */ - if (va55 && wi->regime != TR_EL2) { + if (upper_range) wi->txsz = FIELD_GET(TCR_T1SZ_MASK, tcr); - tg = FIELD_GET(TCR_TG1_MASK, tcr); - - switch (tg << TCR_TG1_SHIFT) { - case TCR_TG1_4K: - wi->pgshift = 12; break; - case TCR_TG1_16K: - wi->pgshift = 14; break; - case TCR_TG1_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift = 16; break; - } - } else { + else wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr); - tg = FIELD_GET(TCR_TG0_MASK, tcr); - - switch (tg << TCR_TG0_SHIFT) { - case TCR_TG0_4K: - wi->pgshift = 12; break; - case TCR_TG0_16K: - wi->pgshift = 14; break; - case TCR_TG0_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift = 16; break; - } - } + wi->pgshift = tcr_tg_pgshift(tcr, upper_range); wi->pa52bit = has_52bit_pa(vcpu, wi, tcr); ia_bits = get_ia_size(wi); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 00e8bc939baf..a88e5dfddd2b 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,28 +378,33 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, return 0; } + +static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) +{ + u64 tg0 = FIELD_GET(VTCR_EL2_TG0_MASK, vtcr); + + switch (tg0) { + case VTCR_EL2_TG0_4K: + return 12; + case VTCR_EL2_TG0_16K: + return 14; + case VTCR_EL2_TG0_64K: + default: /* IMPDEF: treat any other value as 64k */ + return 16; + } +} + static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) { u64 vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2); wi->baddr = vcpu_read_sys_reg(vcpu, VTTBR_EL2); wi->t0sz = vtcr & VTCR_EL2_T0SZ_MASK; - - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { - case VTCR_EL2_TG0_4K: - wi->pgshift = 12; break; - case VTCR_EL2_TG0_16K: - wi->pgshift = 14; break; - case VTCR_EL2_TG0_64K: - default: /* IMPDEF: treat any other value as 64k */ - wi->pgshift = 16; break; - } - + wi->pgshift = vtcr_to_tg0_pgshift(vtcr); wi->sl = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); /* Global limit for now, should eventually be per-VM */ wi->max_oa_bits = min(get_kvm_ipa_limit(), ps_to_output_size(FIELD_GET(VTCR_EL2_PS_MASK, vtcr), false)); - wi->ha = vtcr & VTCR_EL2_HA; wi->be = vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_EE; } From b154da8288add1f6fb958797d0b3462800f9fc77 Mon Sep 17 00:00:00 2001 From: Wei-Lin Chang Date: Tue, 14 Apr 2026 01:03:33 +0100 Subject: [PATCH 1884/5214] KVM: arm64: nv: Use literal granule size in TLBI range calculation TLBI handling derives the invalidation range from guest VTCR_EL2.TG0 in get_guest_mapping_ttl() and compute_tlb_inval_range(). Switch these to use a helper that returns the decoded VTCR_EL2.TG0 granule size instead of decoding it inline. This keeps the granule size derivation in one place and prepares for following changes that adjust the effective granule size. Signed-off-by: Wei-Lin Chang Link: https://patch.msgid.link/20260414000334.3947257-4-weilin.chang@arm.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index a88e5dfddd2b..bc95e43c54dd 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -394,6 +394,11 @@ static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) } } +static size_t vtcr_to_tg0_pgsize(u64 vtcr) +{ + return BIT(vtcr_to_tg0_pgshift(vtcr)); +} + static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) { u64 vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2); @@ -516,20 +521,21 @@ static u8 pgshift_level_to_ttl(u16 shift, u8 level) */ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr) { - u64 tmp, sz = 0, vtcr = mmu->tlb_vtcr; + u64 tmp, sz = 0; kvm_pte_t pte; u8 ttl, level; + size_t tg0_size = vtcr_to_tg0_pgsize(mmu->tlb_vtcr); lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock); - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { - case VTCR_EL2_TG0_4K: + switch (tg0_size) { + case SZ_4K: ttl = (TLBI_TTL_TG_4K << 2); break; - case VTCR_EL2_TG0_16K: + case SZ_16K: ttl = (TLBI_TTL_TG_16K << 2); break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ ttl = (TLBI_TTL_TG_64K << 2); break; @@ -539,19 +545,19 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr) again: /* Iteratively compute the block sizes for a particular granule size */ - switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) { - case VTCR_EL2_TG0_4K: + switch (tg0_size) { + case SZ_4K: if (sz < SZ_4K) sz = SZ_4K; else if (sz < SZ_2M) sz = SZ_2M; else if (sz < SZ_1G) sz = SZ_1G; else sz = 0; break; - case VTCR_EL2_TG0_16K: + case SZ_16K: if (sz < SZ_16K) sz = SZ_16K; else if (sz < SZ_32M) sz = SZ_32M; else sz = 0; break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ if (sz < SZ_64K) sz = SZ_64K; else if (sz < SZ_512M) sz = SZ_512M; @@ -602,14 +608,14 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val) if (!max_size) { /* Compute the maximum extent of the invalidation */ - switch (FIELD_GET(VTCR_EL2_TG0_MASK, mmu->tlb_vtcr)) { - case VTCR_EL2_TG0_4K: + switch (vtcr_to_tg0_pgsize(mmu->tlb_vtcr)) { + case SZ_4K: max_size = SZ_1G; break; - case VTCR_EL2_TG0_16K: + case SZ_16K: max_size = SZ_32M; break; - case VTCR_EL2_TG0_64K: + case SZ_64K: default: /* IMPDEF: treat any other value as 64k */ /* * No, we do not support 52bit IPA in nested yet. Once From 8853566dfbab1a255ae72676ab5ec43e1631ddb7 Mon Sep 17 00:00:00 2001 From: Wei-Lin Chang Date: Tue, 14 Apr 2026 01:03:34 +0100 Subject: [PATCH 1885/5214] KVM: arm64: Fallback to a supported value for unsupported guest TGx When KVM derives the translation granule for emulated stage-1 and stage-2 walks, it decodes TCR/VTCR.TGx and treats the granule as-is. This is wrong when the guest programs a granule size that is not advertised in the guest's ID_AA64MMFR0_EL1.TGRAN* fields. Architecturally, such a value must be treated as an implemented granule size. Choose an available one while prioritizing PAGE_SIZE. Signed-off-by: Wei-Lin Chang Link: https://patch.msgid.link/20260414000334.3947257-5-weilin.chang@arm.com [maz: minor tidying up] Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 52 ++++++++++++++++++- arch/arm64/kvm/nested.c | 112 ++++++++++++++++++++++++++++------------ 2 files changed, 128 insertions(+), 36 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 6ebcf65b4ffa..60d51e98ccb0 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -136,6 +136,30 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi) wi->e0poe = (wi->regime != TR_EL2) && (val & TCR2_EL1_E0POE); } +#define _has_tgran(__r, __sz) \ + ({ \ + u64 _s1, _mmfr0 = __r; \ + \ + _s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz, _mmfr0); \ + \ + _s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI; \ + }) + +static bool has_tgran(u64 mmfr0, unsigned int shift) +{ + switch (shift) { + case 12: + return _has_tgran(mmfr0, 4); + case 14: + return _has_tgran(mmfr0, 16); + case 16: + return _has_tgran(mmfr0, 64); + default: + BUG(); + } +} + static unsigned int tcr_to_tg0_pgshift(u64 tcr) { u64 tg0 = tcr & TCR_TG0_MASK; @@ -166,8 +190,23 @@ static unsigned int tcr_to_tg1_pgshift(u64 tcr) } } -static unsigned int tcr_tg_pgshift(u64 tcr, bool upper_range) +static unsigned int fallback_tgran_shift(u64 mmfr0) { + if (has_tgran(mmfr0, PAGE_SHIFT)) + return PAGE_SHIFT; + else if (has_tgran(mmfr0, 12)) + return 12; + else if (has_tgran(mmfr0, 14)) + return 14; + else if (has_tgran(mmfr0, 16)) + return 16; + else /* Should be unreacheable */ + return PAGE_SHIFT; +} + +static unsigned int tcr_tg_pgshift(struct kvm *kvm, u64 tcr, bool upper_range) +{ + u64 mmfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64MMFR0_EL1); unsigned int shift; /* Someone was silly enough to encode TG0/TG1 differently */ @@ -176,6 +215,15 @@ static unsigned int tcr_tg_pgshift(u64 tcr, bool upper_range) else shift = tcr_to_tg0_pgshift(tcr); + /* + * If TGx is programmed to an unimplemented value (not advertised in + * ID_AA64MMFR0_EL1), we should treat it as if an implemented value is + * written, as per the architecture. Choose an available one while + * prioritizing PAGE_SIZE. + */ + if (!has_tgran(mmfr0, shift)) + return fallback_tgran_shift(mmfr0); + return shift; } @@ -223,7 +271,7 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, else wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr); - wi->pgshift = tcr_tg_pgshift(tcr, upper_range); + wi->pgshift = tcr_tg_pgshift(vcpu->kvm, tcr, upper_range); wi->pa52bit = has_52bit_pa(vcpu, wi, tcr); ia_bits = get_ia_size(wi); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index bc95e43c54dd..3204b3ef60dd 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -378,25 +378,84 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, return 0; } +#define _has_tgran_2(__r, __sz) \ + ({ \ + u64 _s1, _s2, _mmfr0 = __r; \ + \ + _s2 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz##_2, _mmfr0); \ + \ + _s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ + TGRAN##__sz, _mmfr0); \ + \ + ((_s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI && \ + _s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \ + (_s2 == ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \ + _s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI)); \ + }) -static unsigned int vtcr_to_tg0_pgshift(u64 vtcr) +static bool has_tgran_2(u64 mmfr0, unsigned int shift) { - u64 tg0 = FIELD_GET(VTCR_EL2_TG0_MASK, vtcr); - - switch (tg0) { - case VTCR_EL2_TG0_4K: - return 12; - case VTCR_EL2_TG0_16K: - return 14; - case VTCR_EL2_TG0_64K: - default: /* IMPDEF: treat any other value as 64k */ - return 16; + switch (shift) { + case 12: + return _has_tgran_2(mmfr0, 4); + case 14: + return _has_tgran_2(mmfr0, 16); + case 16: + return _has_tgran_2(mmfr0, 64); + default: + BUG(); } } -static size_t vtcr_to_tg0_pgsize(u64 vtcr) +static unsigned int fallback_tgran2_shift(u64 mmfr0) { - return BIT(vtcr_to_tg0_pgshift(vtcr)); + if (has_tgran_2(mmfr0, PAGE_SHIFT)) + return PAGE_SHIFT; + else if (has_tgran_2(mmfr0, 12)) + return 12; + else if (has_tgran_2(mmfr0, 14)) + return 14; + else if (has_tgran_2(mmfr0, 16)) + return 16; + else + return PAGE_SHIFT; +} + +static unsigned int vtcr_to_tg0_pgshift(struct kvm *kvm, u64 vtcr) +{ + u64 tg0 = FIELD_GET(VTCR_EL2_TG0_MASK, vtcr); + u64 mmfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64MMFR0_EL1); + unsigned int shift; + + switch (tg0) { + case VTCR_EL2_TG0_4K: + shift = 12; + break; + case VTCR_EL2_TG0_16K: + shift = 14; + break; + case VTCR_EL2_TG0_64K: + /* IMPDEF: treat any other value as 64k, subject to fallback */ + default: + shift = 16; + } + + /* + * If TGx is programmed to an unimplemented value (not advertised in + * ID_AA64MMFR0_EL1), we should treat it as if an implemented value is + * written, as per the architecture. Choose an available one while + * prioritizing PAGE_SIZE. + */ + if (!has_tgran_2(mmfr0, shift)) + return fallback_tgran2_shift(mmfr0); + + return shift; +} + +static size_t vtcr_to_tg0_pgsize(struct kvm *kvm, u64 vtcr) +{ + return BIT(vtcr_to_tg0_pgshift(kvm, vtcr)); } static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) @@ -405,7 +464,7 @@ static void setup_s2_walk(struct kvm_vcpu *vcpu, struct s2_walk_info *wi) wi->baddr = vcpu_read_sys_reg(vcpu, VTTBR_EL2); wi->t0sz = vtcr & VTCR_EL2_T0SZ_MASK; - wi->pgshift = vtcr_to_tg0_pgshift(vtcr); + wi->pgshift = vtcr_to_tg0_pgshift(vcpu->kvm, vtcr); wi->sl = FIELD_GET(VTCR_EL2_SL0_MASK, vtcr); /* Global limit for now, should eventually be per-VM */ wi->max_oa_bits = min(get_kvm_ipa_limit(), @@ -521,10 +580,10 @@ static u8 pgshift_level_to_ttl(u16 shift, u8 level) */ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr) { + size_t tg0_size = vtcr_to_tg0_pgsize(kvm_s2_mmu_to_kvm(mmu), mmu->tlb_vtcr); u64 tmp, sz = 0; kvm_pte_t pte; u8 ttl, level; - size_t tg0_size = vtcr_to_tg0_pgsize(mmu->tlb_vtcr); lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock); @@ -608,7 +667,7 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val) if (!max_size) { /* Compute the maximum extent of the invalidation */ - switch (vtcr_to_tg0_pgsize(mmu->tlb_vtcr)) { + switch (vtcr_to_tg0_pgsize(kvm, mmu->tlb_vtcr)) { case SZ_4K: max_size = SZ_1G; break; @@ -1508,21 +1567,6 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) } } -#define has_tgran_2(__r, __sz) \ - ({ \ - u64 _s1, _s2, _mmfr0 = __r; \ - \ - _s2 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ - TGRAN##__sz##_2, _mmfr0); \ - \ - _s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \ - TGRAN##__sz, _mmfr0); \ - \ - ((_s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_NI && \ - _s2 != ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz) || \ - (_s2 == ID_AA64MMFR0_EL1_TGRAN##__sz##_2_TGRAN##__sz && \ - _s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI)); \ - }) /* * Our emulated CPU doesn't support all the possible features. For the * sake of simplicity (and probably mental sanity), wipe out a number @@ -1609,15 +1653,15 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) */ switch (PAGE_SIZE) { case SZ_4K: - if (has_tgran_2(orig_val, 4)) + if (_has_tgran_2(orig_val, 4)) val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN4_2, IMP); fallthrough; case SZ_16K: - if (has_tgran_2(orig_val, 16)) + if (_has_tgran_2(orig_val, 16)) val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN16_2, IMP); fallthrough; case SZ_64K: - if (has_tgran_2(orig_val, 64)) + if (_has_tgran_2(orig_val, 64)) val |= SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, TGRAN64_2, IMP); break; } From 2c5d2d3c3f70cde2565d7b279b544893a2035842 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 07:46:04 -0400 Subject: [PATCH 1886/5214] thunderbolt: Prevent XDomain delayed work use-after-free on disconnect tb_xdp_handle_request() runs on system_wq and queues xd->state_work via queue_delayed_work() in three request handlers: PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake), and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues xd->properties_changed_work when local properties change. Concurrently, tb_xdomain_remove() calls stop_handshake() which does cancel_delayed_work_sync() on both delayed works. Later, tb_xdomain_unregister() calls device_unregister() which eventually frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue") moved the request handler off tb->wq, the handler and the remove path are no longer serialized. If queue_delayed_work() executes after cancel_delayed_work_sync() but before the xdomain is freed, the delayed work fires on a freed object. Add xd->removing that tb_xdomain_remove() sets under xd->lock before calling stop_handshake(). Each external queue site holds the same lock and checks removing before calling queue_delayed_work(). This provides the mutual exclusion needed: either the queue site acquires the lock first and queues work that the subsequent cancel will see, or the remove path acquires the lock first and the queue site observes removing == true and skips the queue. Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 41 ++++++++++++++++++++++++++--------- include/linux/thunderbolt.h | 3 +++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 781d88d06b93..fe6c5ac703f4 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -803,9 +803,13 @@ static void tb_xdp_handle_request(struct work_struct *work) * the xdomain related to this connection as well in * case there is a change in services it offers. */ - if (xd && device_is_registered(&xd->dev)) - queue_delayed_work(tb->wq, &xd->state_work, - msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + if (xd) { + mutex_lock(&xd->lock); + if (!xd->removing && device_is_registered(&xd->dev)) + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + mutex_unlock(&xd->lock); + } break; case UUID_REQUEST_OLD: @@ -818,8 +822,12 @@ static void tb_xdp_handle_request(struct work_struct *work) * received UUID request from the remote host. */ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) { - dev_dbg(&xd->dev, "restarting handshake\n"); - start_handshake(xd); + mutex_lock(&xd->lock); + if (!xd->removing) { + dev_dbg(&xd->dev, "restarting handshake\n"); + start_handshake(xd); + } + mutex_unlock(&xd->lock); } break; @@ -885,9 +893,13 @@ static void tb_xdp_handle_request(struct work_struct *work) ret = tb_xdp_link_state_change_response(ctl, route, sequence, 0); - xd->target_link_width = lsc->tlw; - queue_delayed_work(tb->wq, &xd->state_work, - msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + mutex_lock(&xd->lock); + if (!xd->removing) { + xd->target_link_width = lsc->tlw; + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + } + mutex_unlock(&xd->lock); } else { tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY); @@ -971,8 +983,12 @@ static int update_xdomain(struct device *dev, void *data) xd = tb_to_xdomain(dev); if (xd) { - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(50)); + mutex_lock(&xd->lock); + if (!xd->removing) + queue_delayed_work(xd->tb->wq, + &xd->properties_changed_work, + msecs_to_jiffies(50)); + mutex_unlock(&xd->lock); } return 0; @@ -2200,6 +2216,11 @@ static int unregister_service(struct device *dev, void *data) void tb_xdomain_remove(struct tb_xdomain *xd) { tb_xdomain_debugfs_remove(xd); + + mutex_lock(&xd->lock); + xd->removing = true; + mutex_unlock(&xd->lock); + stop_handshake(xd); tb_xdomain_link_exit(xd); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index b5659f883517..feb1af175cfd 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -213,6 +213,8 @@ enum tb_link_width { * @link_width: Width of the downstream facing link * @link_usb4: Downstream link is USB4 * @is_unplugged: The XDomain is unplugged + * @removing: Set by tb_xdomain_remove() under @lock to prevent + * concurrent delayed work queueing * @needs_uuid: If the XDomain does not have @remote_uuid it will be * queried first * @service_ids: Used to generate IDs for the services @@ -262,6 +264,7 @@ struct tb_xdomain { enum tb_link_width link_width; bool link_usb4; bool is_unplugged; + bool removing; bool needs_uuid; struct ida service_ids; struct ida in_hopids; From 4d86e384c164abeee17da09fe6cd24a37973e628 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:42 +0200 Subject: [PATCH 1887/5214] platform/x86: uniwill-laptop: Rework FN lock/super key suspend handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the suspend handling for the FN lock and super key enable features saves the whole values of the affected registers instead of the individual feature state. This duplicates the register access logic from the associated sysfs attributes. Rework the suspend handling to reuse said register access logic and only store the individual feature state as a boolean value. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-6-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 119 ++++++++++++-------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 8cc01bec77b9..c73e280a58bd 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -341,8 +341,8 @@ struct uniwill_data { struct acpi_battery_hook hook; unsigned int last_charge_ctrl; struct mutex battery_lock; /* Protects the list of currently registered batteries */ - unsigned int last_status; - unsigned int last_switch_status; + bool last_fn_lock_state; + bool last_super_key_enable_state; struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ struct list_head batteries; struct mutex led_lock; /* Protects writes to the lightbar registers */ @@ -619,11 +619,22 @@ static const struct regmap_config uniwill_ec_config = { .use_single_write = true, }; +static int uniwill_write_fn_lock(struct uniwill_data *data, bool status) +{ + unsigned int value; + + if (status) + value = FN_LOCK_STATUS; + else + value = 0; + + return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value); +} + static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct uniwill_data *data = dev_get_drvdata(dev); - unsigned int value; bool enable; int ret; @@ -631,21 +642,15 @@ static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - if (enable) - value = FN_LOCK_STATUS; - else - value = 0; - - ret = regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value); + ret = uniwill_write_fn_lock(data, enable); if (ret < 0) return ret; return count; } -static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf) +static int uniwill_read_fn_lock(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -653,23 +658,31 @@ static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, c if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !!(value & FN_LOCK_STATUS)); + *status = !!(value & FN_LOCK_STATUS); + + return 0; +} + +static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_fn_lock(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(fn_lock); -static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int uniwill_write_super_key_enable(struct uniwill_data *data, bool status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; - bool enable; int ret; - ret = kstrtobool(buf, &enable); - if (ret < 0) - return ret; - guard(mutex)(&data->super_key_lock); ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); @@ -680,20 +693,33 @@ static ssize_t super_key_enable_store(struct device *dev, struct device_attribut * We can only toggle the super key lock, so we return early if the setting * is already in the correct state. */ - if (enable == !(value & SUPER_KEY_LOCK_STATUS)) - return count; + if (status == !(value & SUPER_KEY_LOCK_STATUS)) + return 0; - ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, - TRIGGER_SUPER_KEY_LOCK); + return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, + TRIGGER_SUPER_KEY_LOCK); +} + +static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret < 0) + return ret; + + ret = uniwill_write_super_key_enable(data, enable); if (ret < 0) return ret; return count; } -static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +static int uniwill_read_super_key_enable(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -701,7 +727,22 @@ static ssize_t super_key_enable_show(struct device *dev, struct device_attribute if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !(value & SUPER_KEY_LOCK_STATUS)); + *status = !(value & SUPER_KEY_LOCK_STATUS); + + return 0; +} + +static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_super_key_enable(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(super_key_enable); @@ -1723,10 +1764,10 @@ static int uniwill_suspend_fn_lock(struct uniwill_data *data) return 0; /* - * The EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it + * EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it * ourselves. */ - return regmap_read(data->regmap, EC_ADDR_BIOS_OEM, &data->last_status); + return uniwill_read_fn_lock(data, &data->last_fn_lock_state); } static int uniwill_suspend_super_key(struct uniwill_data *data) @@ -1735,10 +1776,10 @@ static int uniwill_suspend_super_key(struct uniwill_data *data) return 0; /* - * The EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it + * EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it * ourselves. */ - return regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &data->last_switch_status); + return uniwill_read_super_key_enable(data, &data->last_super_key_enable_state); } static int uniwill_suspend_battery(struct uniwill_data *data) @@ -1795,27 +1836,15 @@ static int uniwill_resume_fn_lock(struct uniwill_data *data) if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return 0; - return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, - data->last_status); + return uniwill_write_fn_lock(data, data->last_fn_lock_state); } static int uniwill_resume_super_key(struct uniwill_data *data) { - unsigned int value; - int ret; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY)) return 0; - ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); - if (ret < 0) - return ret; - - if ((data->last_switch_status & SUPER_KEY_LOCK_STATUS) == (value & SUPER_KEY_LOCK_STATUS)) - return 0; - - return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, - TRIGGER_SUPER_KEY_LOCK); + return uniwill_write_super_key_enable(data, data->last_super_key_enable_state); } static int uniwill_resume_battery(struct uniwill_data *data) From f7bcba269143230923e40cd88b9169ed27f178ae Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:43 +0200 Subject: [PATCH 1888/5214] platform/x86: uniwill-laptop: Mark EC_ADDR_OEM_4 as volatile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out that EC_ADDR_OEM_4 also contains bits with a volatile nature. Mark the whole register as volatile to prepare for the usage of said bits. This also means that we now have to save/restore the touchpad toggle state ourself. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-7-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 72 +++++++++++++++++---- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index c73e280a58bd..b08aa3a3f89d 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -343,6 +343,7 @@ struct uniwill_data { struct mutex battery_lock; /* Protects the list of currently registered batteries */ bool last_fn_lock_state; bool last_super_key_enable_state; + bool last_touchpad_toggle_enable_state; struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ struct list_head batteries; struct mutex led_lock; /* Protects writes to the lightbar registers */ @@ -598,6 +599,7 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg) case EC_ADDR_PWM_2: case EC_ADDR_TRIGGER: case EC_ADDR_SWITCH_STATUS: + case EC_ADDR_OEM_4: case EC_ADDR_CHARGE_CTRL: case EC_ADDR_USB_C_POWER_PRIORITY: return true; @@ -747,11 +749,22 @@ static ssize_t super_key_enable_show(struct device *dev, struct device_attribute static DEVICE_ATTR_RW(super_key_enable); +static int uniwill_write_touchpad_toggle_enable(struct uniwill_data *data, bool status) +{ + unsigned int value; + + if (status) + value = 0; + else + value = TOUCHPAD_TOGGLE_OFF; + + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value); +} + static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct uniwill_data *data = dev_get_drvdata(dev); - unsigned int value; bool enable; int ret; @@ -759,22 +772,15 @@ static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_at if (ret < 0) return ret; - if (enable) - value = 0; - else - value = TOUCHPAD_TOGGLE_OFF; - - ret = regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value); + ret = uniwill_write_touchpad_toggle_enable(data, enable); if (ret < 0) return ret; return count; } -static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int uniwill_read_touchpad_toggle_enable(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -782,7 +788,23 @@ static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_att if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !(value & TOUCHPAD_TOGGLE_OFF)); + *status = !(value & TOUCHPAD_TOGGLE_OFF); + + return 0; +} + +static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_touchpad_toggle_enable(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(touchpad_toggle_enable); @@ -1782,6 +1804,18 @@ static int uniwill_suspend_super_key(struct uniwill_data *data) return uniwill_read_super_key_enable(data, &data->last_super_key_enable_state); } +static int uniwill_suspend_touchpad_toggle(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE)) + return 0; + + /* + * EC_ADDR_OEM_4 is marked as volatile, so we have to restore it + * ourselves. + */ + return uniwill_read_touchpad_toggle_enable(data, &data->last_touchpad_toggle_enable_state); +} + static int uniwill_suspend_battery(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) @@ -1817,6 +1851,10 @@ static int uniwill_suspend(struct device *dev) if (ret < 0) return ret; + ret = uniwill_suspend_touchpad_toggle(data); + if (ret < 0) + return ret; + ret = uniwill_suspend_battery(data); if (ret < 0) return ret; @@ -1847,6 +1885,14 @@ static int uniwill_resume_super_key(struct uniwill_data *data) return uniwill_write_super_key_enable(data, data->last_super_key_enable_state); } +static int uniwill_resume_touchpad_toggle(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE)) + return 0; + + return uniwill_write_touchpad_toggle_enable(data, data->last_touchpad_toggle_enable_state); +} + static int uniwill_resume_battery(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) @@ -1892,6 +1938,10 @@ static int uniwill_resume(struct device *dev) if (ret < 0) return ret; + ret = uniwill_resume_touchpad_toggle(data); + if (ret < 0) + return ret; + ret = uniwill_resume_battery(data); if (ret < 0) return ret; From 73fa957a05e9fd43f204225497d4c98d7c0d0490 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:44 +0200 Subject: [PATCH 1889/5214] platform/x86: uniwill-laptop: Add support for battery charge modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many Uniwill-based devices do not supports the already existing charge limit functionality, but instead support an alternative interface for controlling the battery charge algorithm. Add support for this interface and update the documentation. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-8-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- .../admin-guide/laptops/uniwill-laptop.rst | 17 +- drivers/platform/x86/uniwill/uniwill-acpi.c | 248 ++++++++++++++---- drivers/platform/x86/uniwill/uniwill-wmi.c | 5 +- 3 files changed, 216 insertions(+), 54 deletions(-) diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst index 1f3ca84c7d88..24b41dbab886 100644 --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst @@ -46,11 +46,20 @@ Battery Charging Control .. warning:: Some devices do not properly implement the charging threshold interface. Forcing the driver to enable access to said interface on such devices might damage the battery [1]_. Because of this the driver will not enable said feature even when - using the ``force`` module parameter. + using the ``force`` module parameter. The charging profile interface will be + available instead. -The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over -the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values -between 1 and 100 percent are supported. +The ``uniwill-laptop`` driver supports controlling the battery charge limit. This either happens +over the standard ``charge_control_end_threshold`` or ``charge_types`` power supply sysfs attribute, +depending on the device. When using the ``charge_control_end_threshold`` sysfs attribute, all values +between 1 and 100 percent are supported. When using the ``charge_types`` sysfs attribute, the driver +supports switching between the ``Standard``, ``Trickle`` and ``Long Life`` profiles. + +Keep in mind that when using the ``charge_types`` sysfs attribute, the EC firmware will hide the +true charging status of the battery from the operating system, potentially misleading users into +thinking that the charging profile does not work. Checking the ``current_now`` sysfs attribute +tells you the true charging status of the battery even when using the ``charge_types`` sysfs +attribute (0 means that the battery is currently not charging). Additionally the driver signals the presence of battery charging issues through the standard ``health`` power supply sysfs attribute. diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index b08aa3a3f89d..53a05a05c594 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -254,6 +254,10 @@ #define EC_ADDR_OEM_4 0x07A6 #define OVERBOOST_DYN_TEMP_OFF BIT(1) +#define CHARGING_PROFILE_MASK GENMASK(5, 4) +#define CHARGING_PROFILE_HIGH_CAPACITY 0x00 +#define CHARGING_PROFILE_BALANCED 0x01 +#define CHARGING_PROFILE_STATIONARY 0x02 #define TOUCHPAD_TOGGLE_OFF BIT(6) #define EC_ADDR_CHARGE_CTRL 0x07B9 @@ -320,13 +324,15 @@ #define UNIWILL_FEATURE_SUPER_KEY BIT(1) #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) #define UNIWILL_FEATURE_LIGHTBAR BIT(3) -#define UNIWILL_FEATURE_BATTERY BIT(4) -#define UNIWILL_FEATURE_CPU_TEMP BIT(5) -#define UNIWILL_FEATURE_GPU_TEMP BIT(6) -#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7) -#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8) -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9) -#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10) +#define UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT BIT(4) +/* Mutually exclusive with the charge limit feature */ +#define UNIWILL_FEATURE_BATTERY_CHARGE_MODES BIT(5) +#define UNIWILL_FEATURE_CPU_TEMP BIT(6) +#define UNIWILL_FEATURE_GPU_TEMP BIT(7) +#define UNIWILL_FEATURE_PRIMARY_FAN BIT(8) +#define UNIWILL_FEATURE_SECONDARY_FAN BIT(9) +#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(10) +#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11) enum usb_c_power_priority_options { USB_C_POWER_PRIORITY_CHARGING = 0, @@ -339,8 +345,15 @@ struct uniwill_data { struct regmap *regmap; unsigned int features; struct acpi_battery_hook hook; - unsigned int last_charge_ctrl; struct mutex battery_lock; /* Protects the list of currently registered batteries */ + union { + struct { + /* Protects writes to last_charge_type */ + struct mutex charge_type_lock; + enum power_supply_charge_type last_charge_type; + }; + unsigned int last_charge_ctrl; + }; bool last_fn_lock_state; bool last_super_key_enable_state; bool last_touchpad_toggle_enable_state; @@ -447,6 +460,12 @@ static inline bool uniwill_device_supports(const struct uniwill_data *data, return (data->features & features) == features; } +static inline bool uniwill_device_supports_any(const struct uniwill_data *data, + unsigned int features) +{ + return data->features & features; +} + static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val) { union acpi_object params[2] = { @@ -1432,6 +1451,30 @@ static unsigned int uniwill_sanitize_battery_threshold(unsigned int value) return min(value, 100); } +static int uniwill_read_charge_type(struct uniwill_data *data, enum power_supply_charge_type *type) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value); + if (ret < 0) + return ret; + + switch (FIELD_GET(CHARGING_PROFILE_MASK, value)) { + case CHARGING_PROFILE_HIGH_CAPACITY: + *type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + return 0; + case CHARGING_PROFILE_BALANCED: + *type = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + return 0; + case CHARGING_PROFILE_STATIONARY: + *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + return 0; + default: + return -EPROTO; + } +} + static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp, union power_supply_propval *val) @@ -1442,6 +1485,16 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup int ret; switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + /* + * We need to use the cached value here because the charging mode + * reported by the EC might temporarily change when a external power + * source has been connected. + */ + mutex_lock(&data->charge_type_lock); + val->intval = data->last_charge_type; + mutex_unlock(&data->charge_type_lock); + return 0; case POWER_SUPPLY_PROP_HEALTH: ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_PRESENT, &prop); if (ret < 0) @@ -1486,13 +1539,52 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup } } +static int uniwill_write_charge_type(struct uniwill_data *data, enum power_supply_charge_type type) +{ + unsigned int value; + + switch (type) { + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_STATIONARY); + break; + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_HIGH_CAPACITY); + break; + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_BALANCED); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, CHARGING_PROFILE_MASK, value); +} + +static int uniwill_restore_charge_type(struct uniwill_data *data) +{ + guard(mutex)(&data->charge_type_lock); + + return uniwill_write_charge_type(data, data->last_charge_type); +} + static int uniwill_set_property(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp, const union power_supply_propval *val) { struct uniwill_data *data = drvdata; + int ret; switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + mutex_lock(&data->charge_type_lock); + + ret = uniwill_write_charge_type(data, val->intval); + if (ret >= 0) + data->last_charge_type = val->intval; + + mutex_unlock(&data->charge_type_lock); + + return ret; case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: if (val->intval < 0 || val->intval > 100) return -EINVAL; @@ -1508,21 +1600,41 @@ static int uniwill_property_is_writeable(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp) { - if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: return true; - - return false; + default: + return false; + } } -static const enum power_supply_property uniwill_properties[] = { +static const enum power_supply_property uniwill_charge_limit_properties[] = { POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, }; -static const struct power_supply_ext uniwill_extension = { +static const struct power_supply_ext uniwill_charge_limit_extension = { .name = DRIVER_NAME, - .properties = uniwill_properties, - .num_properties = ARRAY_SIZE(uniwill_properties), + .properties = uniwill_charge_limit_properties, + .num_properties = ARRAY_SIZE(uniwill_charge_limit_properties), + .get_property = uniwill_get_property, + .set_property = uniwill_set_property, + .property_is_writeable = uniwill_property_is_writeable, +}; + +static const enum power_supply_property uniwill_charge_modes_properties[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, + POWER_SUPPLY_PROP_HEALTH, +}; + +static const struct power_supply_ext uniwill_charge_modes_extension = { + .name = DRIVER_NAME, + .charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) | + BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE), + .properties = uniwill_charge_modes_properties, + .num_properties = ARRAY_SIZE(uniwill_charge_modes_properties), .get_property = uniwill_get_property, .set_property = uniwill_set_property, .property_is_writeable = uniwill_property_is_writeable, @@ -1538,7 +1650,13 @@ static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery if (!entry) return -ENOMEM; - ret = power_supply_register_extension(battery, &uniwill_extension, data->dev, data); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + ret = power_supply_register_extension(battery, &uniwill_charge_limit_extension, + data->dev, data); + else + ret = power_supply_register_extension(battery, &uniwill_charge_modes_extension, + data->dev, data); + if (ret < 0) { kfree(entry); return ret; @@ -1567,7 +1685,10 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt } } - power_supply_unregister_extension(battery, &uniwill_extension); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + power_supply_unregister_extension(battery, &uniwill_charge_limit_extension); + else + power_supply_unregister_extension(battery, &uniwill_charge_modes_extension); return 0; } @@ -1577,28 +1698,37 @@ static int uniwill_battery_init(struct uniwill_data *data) unsigned int value, threshold, sanitized; int ret; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) - return 0; - - ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); - if (ret < 0) - return ret; - - /* - * The charge control threshold might be initialized with 0 by - * the EC to signal that said threshold is uninitialized. We thus - * need to replace this placeholder value with a valid one (100) - * to signal that we want to take control of battery charging. - * For the sake of completeness we also apply this to other - * invalid threshold values. - */ - threshold = FIELD_GET(CHARGE_CTRL_MASK, value); - sanitized = uniwill_sanitize_battery_threshold(threshold); - if (threshold != sanitized) { - FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized); - ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) { + ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); if (ret < 0) return ret; + + /* + * The charge control threshold might be initialized with 0 by + * the EC to signal that said threshold is uninitialized. We thus + * need to replace this placeholder value with a valid one (100) + * to signal that we want to take control of battery charging. + * For the sake of completeness we also apply this to other + * invalid threshold values. + */ + threshold = FIELD_GET(CHARGE_CTRL_MASK, value); + sanitized = uniwill_sanitize_battery_threshold(threshold); + if (threshold != sanitized) { + FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized); + ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); + if (ret < 0) + return ret; + } + } else if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { + ret = devm_mutex_init(data->dev, &data->charge_type_lock); + if (ret < 0) + return ret; + + ret = uniwill_read_charge_type(data, &data->last_charge_type); + if (ret < 0) + return ret; + } else { + return 0; } ret = devm_mutex_init(data->dev, &data->battery_lock); @@ -1617,10 +1747,13 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action { struct uniwill_data *data = container_of(nb, struct uniwill_data, nb); struct uniwill_battery_entry *entry; + int ret; switch (action) { case UNIWILL_OSD_BATTERY_ALERT: - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) + if (!uniwill_device_supports_any(data, + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) return NOTIFY_DONE; mutex_lock(&data->battery_lock); @@ -1631,10 +1764,24 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action return NOTIFY_OK; case UNIWILL_OSD_DC_ADAPTER_CHANGED: - if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + if (!uniwill_device_supports_any(data, + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) return NOTIFY_DONE; - return notifier_from_errno(usb_c_power_priority_restore(data)); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { + ret = uniwill_restore_charge_type(data); + if (ret < 0) + return notifier_from_errno(ret); + } + + if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) { + ret = usb_c_power_priority_restore(data); + if (ret < 0) + return notifier_from_errno(ret); + } + + return NOTIFY_OK; case UNIWILL_OSD_FN_LOCK: if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return NOTIFY_DONE; @@ -1818,7 +1965,7 @@ static int uniwill_suspend_touchpad_toggle(struct uniwill_data *data) static int uniwill_suspend_battery(struct uniwill_data *data) { - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) + if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) return 0; /* @@ -1895,11 +2042,14 @@ static int uniwill_resume_touchpad_toggle(struct uniwill_data *data) static int uniwill_resume_battery(struct uniwill_data *data) { - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) - return 0; + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) + return uniwill_restore_charge_type(data); - return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, - data->last_charge_ctrl); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, + data->last_charge_ctrl); + + return 0; } static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) @@ -1978,7 +2128,7 @@ static struct platform_driver uniwill_driver = { static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = { .features = UNIWILL_FEATURE_SUPER_KEY | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -1989,7 +2139,7 @@ static struct uniwill_device_descriptor lapac71h_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2001,7 +2151,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_LIGHTBAR | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2587,7 +2737,7 @@ static int __init uniwill_init(void) if (force) { /* Assume that the device supports all features except the charge limit */ - device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY; + device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT; pr_warn("Enabling potentially unsupported features\n"); } diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c index 097882f10b1e..e97aa988a90c 100644 --- a/drivers/platform/x86/uniwill/uniwill-wmi.c +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c @@ -48,6 +48,7 @@ int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) { u32 value; + int ret; if (obj->type != ACPI_TYPE_INTEGER) return; @@ -56,7 +57,9 @@ static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) dev_dbg(&wdev->dev, "Received WMI event %u\n", value); - blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); + ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); + if (notifier_to_errno(ret) < 0) + dev_err(&wdev->dev, "Failed to handle event %u\n", value); } /* From 2c5d91f35d0d564c8b4c062b67526e0d2d27fae9 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Wed, 13 May 2026 01:21:45 +0200 Subject: [PATCH 1890/5214] platform/x86: uniwill-laptop: Enable battery charge modes on supported devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable battery charge modes on supported TUXEDO devices by adding the feature bit to the respective device descriptors. Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260512232145.329260-9-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 53a05a05c594..ab063ead45b9 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -2168,6 +2168,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN | @@ -2177,6 +2178,7 @@ static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2187,6 +2189,7 @@ static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __ini static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2198,6 +2201,7 @@ static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __ini static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN, @@ -2206,6 +2210,7 @@ static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = static struct uniwill_device_descriptor tux_featureset_3_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2231,6 +2236,7 @@ static int phxtxx1_probe(struct uniwill_data *data) static struct uniwill_device_descriptor phxtxx1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_USB_C_POWER_PRIORITY, @@ -2256,6 +2262,7 @@ static int phxarx1_phxaqf1_probe(struct uniwill_data *data) static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN | From 1ab843135a7795b0ef37bd7eaf01056276873fd4 Mon Sep 17 00:00:00 2001 From: Jack Wu Date: Tue, 26 May 2026 18:36:37 +0800 Subject: [PATCH 1891/5214] platform/x86: dell-dw5826e: Add reset driver for DW5826e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the DW5826e is in a frozen state and unable to receive USB commands, this driver provides a method for the user to reset the DW5826e via ACPI. E.g: echo 1 > /sys/bus/platform/devices/PALC0001\:00/wwan_reset Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Armin Wolf Signed-off-by: Jack Wu Link: https://patch.msgid.link/20260526-dell-reset-v8-v8-1-d3a29cb4cf2f@compal.com [ij: removed default m] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../testing/sysfs-driver-dell-dw5826e-reset | 9 ++ drivers/platform/x86/dell/Kconfig | 5 + drivers/platform/x86/dell/Makefile | 1 + .../platform/x86/dell/dell-dw5826e-reset.c | 93 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset create mode 100644 drivers/platform/x86/dell/dell-dw5826e-reset.c diff --git a/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset b/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset new file mode 100644 index 000000000000..a665e265633f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset @@ -0,0 +1,9 @@ +What: /sys/bus/platform/devices//wwan_reset +Date: April 2026 +KernelVersion: 7.2 +Contact: Jackbb Wu +Description: + Writing to this file triggers a Platform Level Device Reset + (PLDR) of the Dell DW5826e WWAN module via an ACPI _DSM + method. This can be used to recover the modem when it is in + a frozen state and unable to respond to USB commands. diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 738c108c2163..9f0016056d7e 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -276,4 +276,9 @@ config DELL_WMI_SYSMAN To compile this driver as a module, choose M here: the module will be called dell-wmi-sysman. +config DELL_DW5826E_RESET + tristate "Dell DW5826e PLDR reset support" + depends on ACPI + help + This adds support for the Dell DW5826e PLDR reset via ACPI endif # X86_PLATFORM_DRIVERS_DELL diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index c7501c25e627..470874c6e6e6 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ +obj-$(CONFIG_DELL_DW5826E_RESET) += dell-dw5826e-reset.o diff --git a/drivers/platform/x86/dell/dell-dw5826e-reset.c b/drivers/platform/x86/dell/dell-dw5826e-reset.c new file mode 100644 index 000000000000..1ca7c3421bb5 --- /dev/null +++ b/drivers/platform/x86/dell/dell-dw5826e-reset.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * dell-dw5826e-reset.c - Dell DW5826e reset driver + * + * Copyright (C) 2026 Jackbb Wu + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PALC_DSM_FN_TRIGGER_PLDR BIT(1) + +static guid_t palc_dsm_guid = + GUID_INIT(0x5a1a4bba, 0x8006, 0x487e, 0xbe, 0x0a, 0xac, 0xf5, 0xd8, 0xfd, 0xfe, 0x59); + +static int trigger_palc_pldr(struct device *dev, acpi_handle handle) +{ + union acpi_object *obj; + int ret = 0; + + obj = acpi_evaluate_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR, NULL); + if (!obj) { + dev_err(dev, "Failed to evaluate _DSM\n"); + return -EIO; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(dev, "Unexpected _DSM return type: %d\n", obj->type); + ret = -EINVAL; + } + + ACPI_FREE(obj); + return ret; +} + +static ssize_t wwan_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + acpi_handle handle = ACPI_HANDLE(dev); + int ret; + + ret = trigger_palc_pldr(dev, handle); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(wwan_reset); + +static struct attribute *palc_attrs[] = { + &dev_attr_wwan_reset.attr, + NULL +}; +ATTRIBUTE_GROUPS(palc); + +static int palc_probe(struct platform_device *pdev) +{ + acpi_handle handle; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + if (!acpi_check_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR)) + return -ENODEV; + + return 0; +} + +static const struct acpi_device_id palc_acpi_ids[] = { + { "PALC0001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, palc_acpi_ids); + +static struct platform_driver palc_driver = { + .driver = { + .name = "dell-dw5826e-reset", + .acpi_match_table = palc_acpi_ids, + .dev_groups = palc_groups, + }, + .probe = palc_probe, +}; +module_platform_driver(palc_driver); + +MODULE_DESCRIPTION("Dell DW5826e reset driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("JackBB Wu"); From 499d2e4b75c44365a40a5fe623f3bd5bc1844197 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 27 May 2026 16:43:54 +0200 Subject: [PATCH 1892/5214] KVM: s390: Track page size in struct guest_fault Until now, the members of struct guest_fault are always accessed while holding the required locks, and thus the ptep and crstep pointers can be dereferenced safely. There will be some new cases where callers of kvm_s390_faultin_gfn() need to know the size of the page used to solve the fault, at which point no locks are held anymore, and dereferencing the crstep field is not possible. Introduce a new crste_region3 flag for struct guest_fault to indicate whether the crstep used to solve the fault was a region 3 entry with FC=1 (large pud). This allows to disambiguate all three possible scenarios: * If ptep is not NULL, the fault was solved with a pte. * If ptep is NULL and crste_region3 is 0, a segment entry with FC=1 (large pmd) was used. * If ptep is NULL and crste_region3 is 1, a region 3 entry with FC=1 (large pud) was used. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260527144358.186359-2-imbrenda@linux.ibm.com> --- arch/s390/kvm/dat.h | 1 + arch/s390/kvm/gmap.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/arch/s390/kvm/dat.h b/arch/s390/kvm/dat.h index 8f8278c44879..5d848e74e382 100644 --- a/arch/s390/kvm/dat.h +++ b/arch/s390/kvm/dat.h @@ -500,6 +500,7 @@ struct guest_fault { bool write_attempt; /* Write access attempted */ bool attempt_pfault; /* Attempt a pfault first */ bool valid; /* This entry contains valid data */ + bool crste_region3; /* Whether crstep refers to a region3 entry */ void (*callback)(struct guest_fault *f); void *priv; }; diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 3c26e35af0ef..fe138d17caaf 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -531,6 +531,7 @@ static int gmap_handle_minor_crste_fault(struct gmap *gmap, struct guest_fault * f->pfn = PHYS_PFN(large_crste_to_phys(oldcrste, f->gfn)); f->writable = oldcrste.s.fc1.w; + f->crste_region3 = is_pud(oldcrste); /* Appropriate permissions already (race with another handler), nothing to do. */ if (!oldcrste.h.i && !(f->write_attempt && oldcrste.h.p)) return 0; @@ -690,6 +691,7 @@ static int _gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, int leve if (oldval.val != _CRSTE_EMPTY(oldval.h.tt).val && crste_origin_large(oldval) != crste_origin_large(newval)) return -EAGAIN; + f->crste_region3 = is_pud(newval); } while (!gmap_crstep_xchg_atomic(gmap, f->crstep, oldval, newval, f->gfn)); if (f->callback) f->callback(f); From bea77e83c0b9e1d98482a0c24f9ee9eb8dd61edc Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 27 May 2026 16:43:55 +0200 Subject: [PATCH 1893/5214] KVM: s390: Implement KVM_PRE_FAULT_MEMORY Implement and enable the KVM_PRE_FAULT_MEMORY ioctl for s390. Faulted-in pages will be marked as accessed, unlike x86, otherwise they will trigger a minor fault when accessed. Avoiding such faults is one of the points of KVM_PRE_FAULT_MEMORY. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260527144358.186359-3-imbrenda@linux.ibm.com> --- arch/s390/kvm/Kconfig | 1 + arch/s390/kvm/kvm-s390.c | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig index 5b835bc6a194..8d3ee17a1bcb 100644 --- a/arch/s390/kvm/Kconfig +++ b/arch/s390/kvm/Kconfig @@ -30,6 +30,7 @@ config KVM select KVM_VFIO select VIRT_XFER_TO_GUEST_WORK select KVM_MMU_LOCKLESS_AGING + select KVM_GENERIC_PRE_FAULT_MEMORY help Support hosting paravirtualized guest machines using the SIE virtualization capability on the mainframe. This should work diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index e09960c2e6ed..f6521f16532a 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -630,6 +630,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_USER_OPEREXEC: case KVM_CAP_S390_KEYOP: case KVM_CAP_S390_VSIE_ESAMODE: + case KVM_CAP_PRE_FAULT_MEMORY: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -5736,6 +5737,50 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, return; } +/** + * kvm_arch_vcpu_pre_fault_memory() -- pre-fault and link gmap dat tables + * @vcpu: the vcpu that shall appear to have generated the fault-in. + * @range: the range that needs to be faulted in. + * + * The first page of the given range is faulted in and the corresponding gmap + * page tables are created, as if the given vCPU had performed a read + * operation. + * If the range starts outside any memslots, an error is returned. An error is + * also returned for UCONTROL VMs, which should instead use the + * KVM_S390_VCPU_FAULT ioctl. + * + * Return: + * * %-ENOENT if the range lies outside of a memslot. + * * %-EINVAL in case of invalid state (for example if the VM is UCONTROL). + * * %-EIO if errors happen while faulting-in the page (will trigger a warning + * in the caller). + * * other error codes < 0 in case of other errors. + * * otherwise a number > 0 of bytes that have been faulted in successfully. + */ +long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range) +{ + struct guest_fault f = { .gfn = gpa_to_gfn(range->gpa), }; + gpa_t end; + int rc; + + if (kvm_is_ucontrol(vcpu->kvm)) + return -EINVAL; + + rc = kvm_s390_faultin_gfn(vcpu, NULL, &f); + if (rc == PGM_ADDRESSING) + return -ENOENT; + if (rc > 0) + return -EIO; + if (rc < 0) + return rc; + + if (f.ptep) + return PAGE_SIZE; + + end = ALIGN(range->gpa + PAGE_SIZE, f.crste_region3 ? _REGION3_SIZE : HPAGE_SIZE); + return min(range->size, end - range->gpa); +} + /** * kvm_test_age_gfn() - test young * @kvm: the kvm instance From a5aa761e73074266fef95fd3ee2ff75ea0372f16 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 27 May 2026 16:43:56 +0200 Subject: [PATCH 1894/5214] KVM: s390: Update KVM_PRE_FAULT_MEMORY API documentation Update the API documentation for KVM_PRE_FAULT_MEMORY to account for its s390 implementation. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260527144358.186359-4-imbrenda@linux.ibm.com> --- Documentation/virt/kvm/api.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 52bbbb553ce1..e7998feaa940 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6471,7 +6471,8 @@ Errors: ========== =============================================================== EINVAL The specified `gpa` and `size` were invalid (e.g. not - page aligned, causes an overflow, or size is zero). + page aligned, causes an overflow, or size is zero), or the VM + is UCONTROL (s390). ENOENT The specified `gpa` is outside defined memslots. EINTR An unmasked signal is pending and no page was processed. EFAULT The parameter address was invalid. @@ -6494,7 +6495,7 @@ Errors: KVM_PRE_FAULT_MEMORY populates KVM's stage-2 page tables used to map memory for the current vCPU state. KVM maps memory as if the vCPU generated a stage-2 read page fault, e.g. faults in memory as needed, but doesn't break -CoW. However, KVM does not mark any newly created stage-2 PTE as Accessed. +CoW. On x86, KVM does not mark any newly created stage-2 PTE as Accessed. In the case of confidential VM types where there is an initial set up of private guest memory before the guest is 'finalized'/measured, this ioctl From c847704619da45d5a161887afcfc3701ae18f971 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 27 May 2026 16:43:57 +0200 Subject: [PATCH 1895/5214] KVM: selftests: Fix pre_fault_memory_test to run on s390 Add a missing #include which is needed and otherwise not included on s390. Remove the assertion vcpu->run->exit_reason == KVM_EXIT_IO since it is x86-specific and redundant anyway. Acked-by: Sean Christopherson Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260527144358.186359-5-imbrenda@linux.ibm.com> --- tools/testing/selftests/kvm/pre_fault_memory_test.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/pre_fault_memory_test.c b/tools/testing/selftests/kvm/pre_fault_memory_test.c index fcb57fd034e6..a0fcae3cb7a8 100644 --- a/tools/testing/selftests/kvm/pre_fault_memory_test.c +++ b/tools/testing/selftests/kvm/pre_fault_memory_test.c @@ -11,6 +11,7 @@ #include #include #include +#include /* Arbitrarily chosen values */ #define TEST_SIZE (SZ_2M + PAGE_SIZE) @@ -167,7 +168,6 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private) .type = vm_type, }; struct kvm_vcpu *vcpu; - struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; @@ -193,11 +193,6 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private) vcpu_args_set(vcpu, 1, gva); vcpu_run(vcpu); - run = vcpu->run; - TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, - "Wanted KVM_EXIT_IO, got exit reason: %u (%s)", - run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: REPORT_GUEST_ASSERT(uc); From 68f8c9565c4391343aa3b77f7af642a366991405 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 27 May 2026 16:43:58 +0200 Subject: [PATCH 1896/5214] KVM: selftests: Enable pre_fault_memory_test for s390 Enable the pre_fault_memory_test to run on s390. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260527144358.186359-6-imbrenda@linux.ibm.com> --- tools/testing/selftests/kvm/Makefile.kvm | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..fff939db89cd 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -210,6 +210,7 @@ TEST_GEN_PROGS_s390 += s390/keyop TEST_GEN_PROGS_s390 += rseq_test TEST_GEN_PROGS_s390 += s390/irq_routing TEST_GEN_PROGS_s390 += mmu_stress_test +TEST_GEN_PROGS_s390 += pre_fault_memory_test TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test From a2a893d5757a0d43c0a2ed351eb1e34ab718f2fe Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:34 +0000 Subject: [PATCH 1897/5214] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use an enum for all device ID's and CPU attribute feature ID's, add missing CPU attributes. Reviewed-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-2-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/lenovo-wmi-other.rst | 9 ++ drivers/platform/x86/lenovo/wmi-capdata.h | 5 +- drivers/platform/x86/lenovo/wmi-other.c | 99 ++++++++++++++++++- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst index 01d471156738..189dd4d31926 100644 --- a/Documentation/wmi/devices/lenovo-wmi-other.rst +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -68,9 +68,18 @@ Each attribute has the following properties: - type The following firmware-attributes are implemented: + - cpu_temp: CPU Thermal Load Limit + - ppt_cpu_cl: CPU Cross Loading Power Limit + - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit + - ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit + - ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking + - ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking + - ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking + - ppt_pl4_ipl: Platform Profile Tracking Instantaneous Power Limit + - ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit LENOVO_FAN_TEST_DATA ------------------------- diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index c3e760b8c3c3..c74a0e5294e7 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -18,7 +18,10 @@ #define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8) #define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0) -#define LWMI_DEVICE_ID_FAN 0x04 +enum lwmi_device_id { + LWMI_DEVICE_ID_CPU = 0x01, + LWMI_DEVICE_ID_FAN = 0x04, +}; #define LWMI_TYPE_ID_NONE 0x00 diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index d318ba432fdc..fe4cd7da017e 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -51,14 +51,21 @@ #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" -#define LWMI_DEVICE_ID_CPU 0x01 - -#define LWMI_FEATURE_ID_CPU_SPPT 0x01 -#define LWMI_FEATURE_ID_CPU_SPL 0x02 -#define LWMI_FEATURE_ID_CPU_FPPT 0x03 +enum lwmi_feature_id_cpu { + LWMI_FEATURE_ID_CPU_SPPT = 0x01, + LWMI_FEATURE_ID_CPU_SPL = 0x02, + LWMI_FEATURE_ID_CPU_FPPT = 0x03, + LWMI_FEATURE_ID_CPU_TEMP = 0x04, + LWMI_FEATURE_ID_CPU_APU = 0x05, + LWMI_FEATURE_ID_CPU_CL = 0x06, + LWMI_FEATURE_ID_CPU_TAU = 0x07, + LWMI_FEATURE_ID_CPU_IPL = 0x09, +}; #define LWMI_FEATURE_ID_FAN_RPM 0x03 +#define LWMI_TYPE_ID_CROSSLOAD 0x01 + #define LWMI_FEATURE_VALUE_GET 17 #define LWMI_FEATURE_VALUE_SET 18 @@ -566,18 +573,72 @@ static struct tunable_attr_01 ppt_pl1_spl = { .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl1_spl_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPL, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + static struct tunable_attr_01 ppt_pl2_sppt = { .device_id = LWMI_DEVICE_ID_CPU, .feature_id = LWMI_FEATURE_ID_CPU_SPPT, .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl2_sppt_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPPT, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + static struct tunable_attr_01 ppt_pl3_fppt = { .device_id = LWMI_DEVICE_ID_CPU, .feature_id = LWMI_FEATURE_ID_CPU_FPPT, .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl3_fppt_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_FPPT, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + +static struct tunable_attr_01 cpu_temp = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_TEMP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl1_apu_spl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_APU, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_cpu_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_CL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl1_tau = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_TAU, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl4_ipl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_IPL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl4_ipl_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_IPL, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + struct capdata01_attr_group { const struct attribute_group *attr_group; struct tunable_attr_01 *tunable_attr; @@ -913,17 +974,45 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) .name = _fsname, .attrs = _attrname##_attrs \ } +LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp", + "Set the CPU thermal load limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl", + "Set the CPU cross loading power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl", + "Set the APU sustained power limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl", "Set the CPU sustained power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl", + "Set the CPU cross loading sustained power limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt", "Set the CPU slow package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl", + "Set the CPU cross loading slow package power tracking limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt", "Set the CPU fast package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl", + "Set the CPU cross loading fast package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau", + "Set the CPU sustained power limit exceed duration"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl", + "Set the CPU instantaneous power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl", + "Set the CPU cross loading instantaneous power limit"); + static struct capdata01_attr_group cd01_attr_groups[] = { + { &cpu_temp_attr_group, &cpu_temp }, + { &ppt_cpu_cl_attr_group, &ppt_cpu_cl }, + { &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl }, { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, + { &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl }, + { &ppt_pl1_tau_attr_group, &ppt_pl1_tau }, { &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt }, + { &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl }, { &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt }, + { &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl }, + { &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl }, + { &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl }, {}, }; From b51fa3b00e0339bebeac7d0c1beb13f494f6fe41 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:35 +0000 Subject: [PATCH 1898/5214] platform/x86: lenovo-wmi-other: Add GPU tunable attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use an enum for all GPU attribute feature ID's and add GPU attributes. Reviewed-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-3-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/lenovo-wmi-other.rst | 10 ++ drivers/platform/x86/lenovo/wmi-capdata.h | 1 + drivers/platform/x86/lenovo/wmi-other.c | 105 ++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst index 189dd4d31926..011054d64eac 100644 --- a/Documentation/wmi/devices/lenovo-wmi-other.rst +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -69,6 +69,16 @@ Each attribute has the following properties: The following firmware-attributes are implemented: - cpu_temp: CPU Thermal Load Limit + - dgpu_boost_clk: Dedicated GPU Boost Clock + - dgpu_didvid: Dedicated GPU Device Identifier and Vendor Identifier + - dgpu_enable: Dedicated GPU Enabled Status + - gpu_mode: GPU Mode by Power Limit + - gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset + - gpu_nv_bpl: Nvidia GPU Base Power Limit + - gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit + - gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power + - gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit + - gpu_temp: GPU Thermal Load Limit - ppt_cpu_cl: CPU Cross Loading Power Limit - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index c74a0e5294e7..a7cfdeaa58f7 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -20,6 +20,7 @@ enum lwmi_device_id { LWMI_DEVICE_ID_CPU = 0x01, + LWMI_DEVICE_ID_GPU = 0x02, LWMI_DEVICE_ID_FAN = 0x04, }; diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index fe4cd7da017e..9a6183bbcc92 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -62,6 +62,19 @@ enum lwmi_feature_id_cpu { LWMI_FEATURE_ID_CPU_IPL = 0x09, }; +enum lwmi_feature_id_gpu { + LWMI_FEATURE_ID_GPU_NV_PPAB = 0x01, + LWMI_FEATURE_ID_GPU_NV_CTGP = 0x02, + LWMI_FEATURE_ID_GPU_TEMP = 0x03, + LWMI_FEATURE_ID_GPU_AC_OFFSET = 0x04, + LWMI_FEATURE_ID_DGPU_BOOST_CLK = 0x06, + LWMI_FEATURE_ID_DGPU_EN = 0x07, + LWMI_FEATURE_ID_GPU_MODE = 0x08, + LWMI_FEATURE_ID_DGPU_DIDVID = 0x09, + LWMI_FEATURE_ID_GPU_NV_BPL = 0x0a, + LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b, +}; + #define LWMI_FEATURE_ID_FAN_RPM 0x03 #define LWMI_TYPE_ID_CROSSLOAD 0x01 @@ -639,6 +652,66 @@ static struct tunable_attr_01 ppt_pl4_ipl_cl = { .type_id = LWMI_TYPE_ID_CROSSLOAD, }; +static struct tunable_attr_01 gpu_nv_ppab = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_ctgp = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_temp = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_TEMP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_ac_offset = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_boost_clk = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_enable = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_EN, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_mode = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_MODE, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_didvid = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_DIDVID, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_bpl = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_BPL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_cpu_boost = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST, + .type_id = LWMI_TYPE_ID_NONE, +}; + struct capdata01_attr_group { const struct attribute_group *attr_group; struct tunable_attr_01 *tunable_attr; @@ -974,6 +1047,7 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) .name = _fsname, .attrs = _attrname##_attrs \ } +/* CPU tunable attributes */ LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp", "Set the CPU thermal load limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl", @@ -999,9 +1073,40 @@ LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl", LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl", "Set the CPU cross loading instantaneous power limit"); +/* GPU tunable attributes */ +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "dgpu_boost_clk", + "Set the dedicated GPU boost clock"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "dgpu_didvid", + "Get the GPU device identifier and vendor identifier"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable", + "Set the dedicated Nvidia GPU enabled status"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode", + "Set the GPU mode by power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset", + "Set the Nvidia GPU AC total processing power baseline offset"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl", + "Set the Nvidia GPU base power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost", + "Set the Nvidia GPU to CPU dynamic boost limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp", + "Set the GPU configurable total graphics power"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab", + "Set the Nvidia GPU power performance aware boost limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp", + "Set the GPU thermal load limit"); static struct capdata01_attr_group cd01_attr_groups[] = { { &cpu_temp_attr_group, &cpu_temp }, + { &dgpu_boost_clk_attr_group, &dgpu_boost_clk }, + { &dgpu_didvid_attr_group, &dgpu_didvid }, + { &dgpu_enable_attr_group, &dgpu_enable }, + { &gpu_mode_attr_group, &gpu_mode }, + { &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset }, + { &gpu_nv_bpl_attr_group, &gpu_nv_bpl }, + { &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost }, + { &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp }, + { &gpu_nv_ppab_attr_group, &gpu_nv_ppab }, + { &gpu_temp_attr_group, &gpu_temp }, { &ppt_cpu_cl_attr_group, &ppt_cpu_cl }, { &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl }, { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, From 891d43749445fed6725acf9ee1c37e052dba8288 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:36 +0000 Subject: [PATCH 1899/5214] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the next patch a power supply extension is added which requires a name attribute. Instead of creating another const macro with the same information, rename LWMI_OM_FW_ATTR_BASE_PATH to LWMI_OM_SYSFS_NAME. Reviewed-by: Rong Zhang Tested-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-4-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-other.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index 9a6183bbcc92..beb92c7cff25 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -92,7 +92,7 @@ enum lwmi_feature_id_gpu { lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) -#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" +#define LWMI_OM_SYSFS_NAME "lenovo-wmi-other" #define LWMI_OM_HWMON_NAME "lenovo_wmi_other" static DEFINE_IDA(lwmi_om_ida); @@ -1138,8 +1138,7 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s-%u", - LWMI_OM_FW_ATTR_BASE_PATH, - priv->ida_id); + LWMI_OM_SYSFS_NAME, priv->ida_id); if (IS_ERR(priv->fw_attr_dev)) { err = PTR_ERR(priv->fw_attr_dev); goto err_free_ida; From 9ca8fc065b88b327acbfdc33454efea391639716 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:37 +0000 Subject: [PATCH 1900/5214] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add charge_behaviour and charge_types attributes through a power supply extension for devices that support WMI based charge enable & disable. Lenovo Legion devices that implement WMI function and capdata ID 0x03010001 in their BIOS are able to enable or disable charging at 80% through the lenovo-wmi-other interface. Add a charge_types attribute for BATX devices to expose this capability with Standard and Long_Life types enabled. Additionally, devices that support WMI function and capdata ID 0x03020000 are able to force discharge of the battery. Expose this capability with a charge_behaviour attribute in the power supply extension, with the auto and force-discharge behaviors enabled. The GET method for this attribute is bugged. After analyzing the DSDT, and some testing, it appears the method grabs bit(3) instead of bit(4) from the EC register that stores the current status, and will only report if charging has been inhibited or not. To work around this, store and report the last setting written to the attribute. As some devices only expose one attribute or the other, a bitmask is added with a lookup table and some helper macros to select the correct configuration for the hardware at runtime. The ideapad_laptop driver provides the charge_types attribute to provide similar functionality. When the WMI method is set this can corrupt the ACPI method return and cause hardware and driver errors. To avoid conflicts between the drivers, we get the acpi_handle and do the same check that ideapad_laptop does when it enables the feature. If the feature is supported in ideapad_laptop, abort adding the extension from lenovo-wmi-other. The ACPI method is more reliable when both are present from my testing, so we can prefer that implementation and do not need to worry about de-conflicting from inside that driver. Reviewed-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-5-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 1 + drivers/platform/x86/lenovo/wmi-capdata.h | 1 + drivers/platform/x86/lenovo/wmi-other.c | 391 ++++++++++++++++++++++ 3 files changed, 393 insertions(+) diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 09b1b055d2e0..b9a5d18caa1e 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -262,6 +262,7 @@ config LENOVO_WMI_GAMEZONE config LENOVO_WMI_TUNING tristate "Lenovo Other Mode WMI Driver" depends on ACPI_WMI + depends on ACPI_BATTERY select HWMON select FW_ATTR_CLASS select LENOVO_WMI_CAPDATA diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index a7cfdeaa58f7..a49229cec245 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -21,6 +21,7 @@ enum lwmi_device_id { LWMI_DEVICE_ID_CPU = 0x01, LWMI_DEVICE_ID_GPU = 0x02, + LWMI_DEVICE_ID_PSU = 0x03, LWMI_DEVICE_ID_FAN = 0x04, }; diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index beb92c7cff25..8331da864beb 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -41,9 +41,12 @@ #include #include #include +#include #include #include +#include + #include "wmi-capdata.h" #include "wmi-events.h" #include "wmi-helpers.h" @@ -75,9 +78,15 @@ enum lwmi_feature_id_gpu { LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b, }; +enum lwmi_feature_id_psu { + LWMI_FEATURE_ID_PSU_CHARGE_TYPES = 0x01, + LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR = 0x02, +}; + #define LWMI_FEATURE_ID_FAN_RPM 0x03 #define LWMI_TYPE_ID_CROSSLOAD 0x01 +#define LWMI_TYPE_ID_PSU_AC 0x01 #define LWMI_FEATURE_VALUE_GET 17 #define LWMI_FEATURE_VALUE_SET 18 @@ -88,10 +97,19 @@ enum lwmi_feature_id_gpu { #define LWMI_FAN_DIV 100 +#define LWMI_CHARGE_BEHAVIOR_DISCHARGE 0x00 +#define LWMI_CHARGE_BEHAVIOR_AUTO 0x01 +#define LWMI_CHARGE_TYPE_STANDARD 0x00 +#define LWMI_CHARGE_TYPE_LONGLIFE 0x01 + #define LWMI_ATTR_ID_FAN_RPM(x) \ lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) +#define LWMI_ATTR_ID_PSU(feat, type) \ + lwmi_attr_id(LWMI_DEVICE_ID_PSU, feat, \ + LWMI_GZ_THERMAL_MODE_NONE, type) + #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other" #define LWMI_OM_HWMON_NAME "lenovo_wmi_other" @@ -131,6 +149,11 @@ struct lwmi_om_priv { bool capdata00_collected : 1; bool capdata_fan_collected : 1; } fan_flags; + + enum power_supply_charge_behaviour charge_behaviour; + const struct power_supply_ext *battery_ext; + struct acpi_battery_hook battery_hook; + bool bh_registered; }; /* @@ -557,6 +580,371 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list * lwmi_om_hwmon_add(priv); } +/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */ + +/** + * lwmi_psy_ext_get_prop() - Get a power_supply_ext property + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer to the lwmi_om_priv drvdata + * @prop: The property to read + * @val: The value to return + * + * Reads the given value from the power_supply_ext property + * + * Return: 0 on success, or an error + */ +static int lwmi_psy_ext_get_prop(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct lwmi_om_priv *priv = ext_data; + struct wmi_method_args_32 args = {}; + u32 retval; + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + /* Reading from BIOS reads the wrong bit. Use cached value */ + val->intval = priv->charge_behaviour; + return 0; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + break; + default: + return -EINVAL; + } + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, + (u8 *)&args, sizeof(args), + &retval); + if (ret) + return ret; + + dev_dbg(&priv->wdev->dev, "Got return value %#x for property %#x\n", retval, prop); + + switch (retval) { + case LWMI_CHARGE_TYPE_LONGLIFE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + break; + case LWMI_CHARGE_TYPE_STANDARD: + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n", retval); + return -EINVAL; + } + + return 0; +} + +/** + * lwmi_psy_ext_set_prop() - Set a power_supply_ext property + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer to the lwmi_om_priv drvdata + * @prop: The property to write + * @val: The value to write + * + * Writes the given value to the power_supply_ext property + * + * Return: 0 on success, or an error + */ +static int lwmi_psy_ext_set_prop(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct lwmi_om_priv *priv = ext_data; + struct wmi_method_args_32 args = {}; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR, + LWMI_TYPE_ID_NONE); + switch (val->intval) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + args.arg1 = LWMI_CHARGE_BEHAVIOR_AUTO; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + args.arg1 = LWMI_CHARGE_BEHAVIOR_DISCHARGE; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge behavior value: %#x\n", + val->intval); + return -EINVAL; + } + priv->charge_behaviour = val->intval; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + args.arg1 = LWMI_CHARGE_TYPE_LONGLIFE; + break; + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + args.arg1 = LWMI_CHARGE_TYPE_STANDARD; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n", + val->intval); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + dev_dbg(&priv->wdev->dev, "Attempting to set %#010x for property %#x to %#x\n", + args.arg0, prop, args.arg1); + + return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, + (u8 *)&args, sizeof(args), NULL); +} + +/** lwmi_psy_prop_get_supported() - Gets the support level from capdata for a given property + * @priv: Pointer to the lwmi_om_priv drvdata + * @prop: The power supply property to be evaluated + * + * Return: bitmapped capability data support level + */ +static u32 lwmi_psy_prop_get_supported(struct lwmi_om_priv *priv, enum power_supply_property prop) +{ + struct capdata00 capdata; + u32 attribute_id; + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR, + LWMI_TYPE_ID_NONE); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + break; + default: + return 0; + } + + ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata); + if (ret) + return 0; + + dev_dbg(&priv->wdev->dev, "Battery charge feature (%#010x) support level: %#x\n", + attribute_id, capdata.supported); + + return capdata.supported; +} + +/** + * lwmi_psy_prop_is_supported() - Determine if the property is supported + * @priv: Pointer to the lwmi_om_priv drvdata + * @prop: The power supply property to be evaluated + * + * Checks capdata 00 to determine if the property is supported. + * + * Return: true if readable, or false + */ +static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv, enum power_supply_property prop) +{ + int ret; + + ret = lwmi_psy_prop_get_supported(priv, prop); + if (ret < 0) + return false; + + return (ret & LWMI_SUPP_VALID) && (ret & LWMI_SUPP_GET); +} + +/** + * lwmi_psy_prop_is_writeable() - Determine if the property is writeable + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer the lwmi_om_priv drvdata + * @prop: The property to check + * + * Checks capdata 00 to determine if the property is writable. + * + * Return: true if writable, or false + */ +static int lwmi_psy_prop_is_writeable(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop) +{ + struct lwmi_om_priv *priv = ext_data; + int ret; + + ret = lwmi_psy_prop_get_supported(priv, prop); + if (ret < 0) + return false; + + return !!(ret & LWMI_SUPP_SET); +} + +#define DEFINE_LWMI_POWER_SUPPLY_EXTENSION(_name, _props, _behaviours, _types) \ + static const struct power_supply_ext _name = { \ + .name = LWMI_OM_SYSFS_NAME, \ + .properties = _props, \ + .num_properties = ARRAY_SIZE(_props), \ + .charge_behaviours = _behaviours, \ + .charge_types = _types, \ + .get_property = lwmi_psy_ext_get_prop, \ + .set_property = lwmi_psy_ext_set_prop, \ + .property_is_writeable = lwmi_psy_prop_is_writeable, \ + } + +static const enum power_supply_property lwmi_psy_ext_props_all[] = { + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const enum power_supply_property lwmi_psy_ext_props_types[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const enum power_supply_property lwmi_psy_ext_props_behaviour[] = { + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, +}; + +#define LWMI_CHARGE_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) + +#define LWMI_CHARGE_TYPES (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | \ + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)) + +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_all, lwmi_psy_ext_props_all, + LWMI_CHARGE_BEHAVIOURS, LWMI_CHARGE_TYPES); +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_types, lwmi_psy_ext_props_types, + 0, LWMI_CHARGE_TYPES); +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_behaviour, lwmi_psy_ext_props_behaviour, + LWMI_CHARGE_BEHAVIOURS, 0); + +#define LWMI_PSY_PROP_BEHAVIOUR BIT(0) +#define LWMI_PSY_PROP_TYPES BIT(1) + +static const struct power_supply_ext *lwmi_psy_exts[] = { + [LWMI_PSY_PROP_BEHAVIOUR] = &lwmi_psy_ext_behaviour, + [LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_types, + [LWMI_PSY_PROP_BEHAVIOUR | LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_all, +}; + +/** + * lwmi_add_battery() - Connect the power_supply_ext + * @battery: The battery to extend + * @hook: The driver hook used to extend the battery + * + * Return: 0 on success, or an error. + */ +static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook); + + return power_supply_register_extension(battery, priv->battery_ext, &priv->wdev->dev, priv); +} + +/** + * lwmi_remove_battery() - Disconnect the power_supply_ext + * @battery: The battery that was extended + * @hook: The driver hook used to extend the battery + * + * Return: 0 on success, or an error. + */ +static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook); + + power_supply_unregister_extension(battery, priv->battery_ext); + return 0; +} + +/** + * lwmi_acpi_match() - Attempts to return the ideapad acpi handle + * @handle: The ACPI handle that manages battery charging + * @lvl: Unused + * @context: Void pointer to the acpi_handle object to return + * @retval: Unused + * + * Checks if the ideapad_laptop driver is going to manage charge_type first, + * then if not, hooks the battery to our WMI methods. + * + * Return: AE_CTRL_TERMINATE if found, AE_OK if not found. + */ +static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl, + void *context, void **retval) +{ + acpi_handle *ahand = context; + + if (!handle) + return AE_OK; + + *ahand = handle; + + return AE_CTRL_TERMINATE; +} + +/** + * lwmi_om_psy_ext_init() - Hooks power supply extension to device battery + * @priv: Pointer to the lwmi_om_priv drvdata. + * + * Checks if the ideapad_laptop driver is going to manage charge attributes first, + * then if not, hooks the battery to our WMI methods if they are supported. + */ +static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) +{ + static const char * const ideapad_hid = "VPC2004"; + acpi_handle handle = NULL; + unsigned int props = 0; + int ret; + + if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR)) + props |= LWMI_PSY_PROP_BEHAVIOUR; + if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_TYPES)) + props |= LWMI_PSY_PROP_TYPES; + if (!props) + return; + + /* Deconflict ideapad_laptop driver */ + ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL); + if (ret) + return; + + if (handle && acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { + dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device\n"); + return; + } + + /* Add battery hooks */ + priv->battery_ext = lwmi_psy_exts[props]; + priv->battery_hook.add_battery = lwmi_add_battery; + priv->battery_hook.remove_battery = lwmi_remove_battery; + priv->battery_hook.name = "Lenovo WMI Other Battery Extension"; + priv->bh_registered = true; + + battery_hook_register(&priv->battery_hook); +} + +/** + * lwmi_om_psy_remove() - Unregister battery hook + * @priv: Driver private data + * + * Unregisters the battery hook if applicable. + */ +static void lwmi_om_psy_remove(struct lwmi_om_priv *priv) +{ + if (!priv->bh_registered) + return; + + battery_hook_unregister(&priv->battery_hook); + priv->bh_registered = false; +} + /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */ struct tunable_attr_01 { @@ -1243,6 +1631,7 @@ static int lwmi_om_master_bind(struct device *dev) } lwmi_om_fan_info_collect_cd00(priv); + lwmi_om_psy_ext_init(priv); lwmi_om_fw_attr_add(priv); @@ -1265,6 +1654,8 @@ static void lwmi_om_master_unbind(struct device *dev) lwmi_om_hwmon_remove(priv); + lwmi_om_psy_remove(priv); + component_unbind_all(dev, NULL); } From fb8e9629efbab67135f801f5ec5e91307e7693c2 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:38 +0000 Subject: [PATCH 1901/5214] platform/x86: lenovo-wmi-other: Add force_load_psy_ext module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Lenovo BIOS have been shown to have incomplete and/or broken capability data and WMI attribute IDs. In some cases the capability data reports that a feature is not supported when the get/set methods are fully implemented. It is also possible that the ACPI methods from the ideapad_laptop driver we defer to could be bugged while the WMI method is fully working. To aid end users in submitting more complete bug reports in these situations, add an override to skip the ACPI and compatibility checks to force load the power supply extension as if it is fully supported and has no conflicts. Reviewed-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-6-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-other.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index 8331da864beb..fbb32bf404f2 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -185,6 +185,16 @@ MODULE_PARM_DESC(relax_fan_constraint, "Enabling this may results in HWMON attributes being out-of-sync, " "and setting a too low RPM stops the fan. Use with caution."); +/* Visibility of power supply extensions */ +static bool force_load_psy_ext; +module_param(force_load_psy_ext, bool, 0444); +MODULE_PARM_DESC(force_load_psy_ext, + "This option will skip checking if the ideapad_laptop driver will conflict " + "with adding an extension to set the battery charge behavior and battery charge " + "control end threshold. It will also skip checking if the BIOS reports that " + "those features are fully supported. It is recommended to blacklist the ideapad " + "driver before using this option."); + /* ======== HWMON (component: lenovo-wmi-capdata 00 & fan) ======== */ /** @@ -755,6 +765,9 @@ static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv, enum power_sup { int ret; + if (force_load_psy_ext) + return true; + ret = lwmi_psy_prop_get_supported(priv, prop); if (ret < 0) return false; @@ -781,6 +794,9 @@ static int lwmi_psy_prop_is_writeable(struct power_supply *ps, struct lwmi_om_priv *priv = ext_data; int ret; + if (force_load_psy_ext) + return true; + ret = lwmi_psy_prop_get_supported(priv, prop); if (ret < 0) return false; @@ -909,6 +925,8 @@ static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) props |= LWMI_PSY_PROP_TYPES; if (!props) return; + if (force_load_psy_ext) + goto load_psy_ext; /* Deconflict ideapad_laptop driver */ ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL); @@ -920,6 +938,7 @@ static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) return; } +load_psy_ext: /* Add battery hooks */ priv->battery_ext = lwmi_psy_exts[props]; priv->battery_hook.add_battery = lwmi_add_battery; From 229e575451e4e65db47bb1873be0ccab764d9f3d Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 20 May 2026 06:07:39 +0000 Subject: [PATCH 1902/5214] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to add debugfs support for lenovo-wmi-capdata. Let's setup a debugfs directory called "lenovo_wmi" for tidiness, so that any lenovo-wmi-* device can put its subdirectory under the directory. Subdirectories will be named after the corresponding WMI devices. Signed-off-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-7-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-helpers.c | 34 +++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-helpers.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c index 7a198259e393..8f5766c391eb 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.c +++ b/drivers/platform/x86/lenovo/wmi-helpers.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -185,6 +187,38 @@ int lwmi_tm_notifier_call(enum thermal_mode *mode) } EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS"); +static struct dentry *lwmi_debugfs_dir; + +/** + * lwmi_debugfs_create_dir() - Helper function for creating a debugfs directory + * for a device. + * @wdev: Pointer to the WMI device to be called. + * + * Caller must remove the directory with debugfs_remove_recursive() on device + * removal. + * + * Return: Pointer to the created directory. + */ +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev) +{ + return debugfs_create_dir(dev_name(&wdev->dev), lwmi_debugfs_dir); +} +EXPORT_SYMBOL_NS_GPL(lwmi_debugfs_create_dir, "LENOVO_WMI_HELPERS"); + +static int __init lwmi_helpers_init(void) +{ + lwmi_debugfs_dir = debugfs_create_dir("lenovo_wmi", NULL); + + return 0; +} +subsys_initcall(lwmi_helpers_init) + +static void __exit lwmi_helpers_exit(void) +{ + debugfs_remove_recursive(lwmi_debugfs_dir); +} +module_exit(lwmi_helpers_exit) + MODULE_AUTHOR("Derek J. Clark "); MODULE_DESCRIPTION("Lenovo WMI Helpers Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h index ed7db3ebba6c..039fe61003ce 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.h +++ b/drivers/platform/x86/lenovo/wmi-helpers.h @@ -16,6 +16,8 @@ struct wmi_method_args_32 { u32 arg1; }; +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev); + enum lwmi_event_type { LWMI_GZ_GET_THERMAL_MODE = 0x01, }; From 18a166d29aa12d361907a3aa724c8892d834592b Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 20 May 2026 06:07:40 +0000 Subject: [PATCH 1903/5214] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo GameZone/Other interfaces have some delicate divergences among different devices. When making a bug report or adding support for new devices/interfaces, capdata is the most important information to cross-check with. Add a debugfs file (lenovo_wmi//capdata), so that users can dump capdata and include it in their reports. Since `struct capdata01' is just an extension to `struct capdata00', also convert the former to include the latter anonymously (-fms-extensions, since v6.19). This is declared as a union in the capdata01 struct, with both the anonymous declaration and as a named member to avoid type casting when passing just the capdata00 struct pointer. Tested-by: Kurt Borja Signed-off-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-8-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 1 + drivers/platform/x86/lenovo/wmi-capdata.c | 120 ++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-capdata.h | 7 +- 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index b9a5d18caa1e..4443f40ef8aa 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -236,6 +236,7 @@ config YT2_1380 config LENOVO_WMI_CAPDATA tristate depends on ACPI_WMI + depends on LENOVO_WMI_HELPERS config LENOVO_WMI_EVENTS tristate diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c index 714aa6fd6f1f..d5e961566136 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.c +++ b/drivers/platform/x86/lenovo/wmi-capdata.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +89,7 @@ struct lwmi_cd_priv { struct notifier_block acpi_nb; /* ACPI events */ struct wmi_device *wdev; struct cd_list *list; + struct dentry *debugfs_dir; /* * A capdata device may be a component master of another capdata device. @@ -117,6 +120,8 @@ struct cd_list { static struct wmi_driver lwmi_cd_driver; +/* ======== Device components ======== */ + /** * lwmi_cd_match() - Match rule for the master driver. * @dev: Pointer to the capability data parent device. @@ -470,6 +475,116 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA"); DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan); EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA"); +/* ======== debugfs ======== */ + +/** + * lwmi_cd00_show() - Dump capdata00 + * @s: Pointer to seq_file where the capdata00 is dumped. + * @cd00: Pointer to a capdata00 struct to be dumped. + */ +static void lwmi_cd00_show(struct seq_file *s, struct capdata00 *cd00) +{ + u8 dev = FIELD_GET(LWMI_ATTR_DEV_ID_MASK, cd00->id); + u8 feat = FIELD_GET(LWMI_ATTR_FEAT_ID_MASK, cd00->id); + u8 mode = FIELD_GET(LWMI_ATTR_MODE_ID_MASK, cd00->id); + u8 type = FIELD_GET(LWMI_ATTR_TYPE_ID_MASK, cd00->id); + bool extra = cd00->supported & ~(LWMI_SUPP_GET | LWMI_SUPP_SET | LWMI_SUPP_VALID); + bool get = cd00->supported & LWMI_SUPP_GET; + bool set = cd00->supported & LWMI_SUPP_SET; + bool valid = cd00->supported & LWMI_SUPP_VALID; + + seq_printf(s, " id: 0x%08x [dev: %2u, feat: %2u, mode: %2u, type: %2u]\n", + cd00->id, dev, feat, mode, type); + + seq_printf(s, " supported: 0x%08x [%c%c%c%c]\n", cd00->supported, + extra ? '+' : ' ', + get ? 'R' : ' ', + set ? 'W' : ' ', + valid ? 'V' : ' '); + + seq_printf(s, " default_value: %u\n", cd00->default_value); +} + +/** + * lwmi_cd01_show() - Dump capdata01 + * @s: Pointer to seq_file where the capdata01 is dumped. + * @cd01: Pointer to a capdata01 struct to be dumped. + */ +static void lwmi_cd01_show(struct seq_file *s, struct capdata01 *cd01) +{ + /* capdata01 is an extension to capdata00. */ + lwmi_cd00_show(s, &cd01->cd00); + + seq_printf(s, " step: %u\n", cd01->step); + seq_printf(s, " min_value: %u\n", cd01->min_value); + seq_printf(s, " max_value: %u\n", cd01->max_value); +} + +/** + * lwmi_cd_fan_show() - Dump capdata_fan + * @s: Pointer to seq_file where the capdata_fan is dumped. + * @cd_fan: Pointer to a capdata_fan struct to be dumped. + */ +static void lwmi_cd_fan_show(struct seq_file *s, struct capdata_fan *cd_fan) +{ + seq_printf(s, " id: %u\n", cd_fan->id); + seq_printf(s, " min_rpm: %u\n", cd_fan->min_rpm); + seq_printf(s, " max_rpm: %u\n", cd_fan->max_rpm); +} + +/** + * lwmi_cd_debugfs_show() - Dump capability data to debugfs + * @s: Pointer to seq_file where the capability data is dumped. + * @data: unused. + * + * Return: 0 + */ +static int lwmi_cd_debugfs_show(struct seq_file *s, void *data) +{ + struct lwmi_cd_priv *priv = s->private; + u8 idx; + + guard(mutex)(&priv->list->list_mutex); + + /* lwmi_cd_alloc() ensured priv->list->type must be a valid type. */ + for (idx = 0; idx < priv->list->count; idx++) { + seq_printf(s, "%s[%u]:\n", lwmi_cd_table[priv->list->type].name, idx); + + if (priv->list->type == LENOVO_CAPABILITY_DATA_00) + lwmi_cd00_show(s, &priv->list->cd00[idx]); + else if (priv->list->type == LENOVO_CAPABILITY_DATA_01) + lwmi_cd01_show(s, &priv->list->cd01[idx]); + else if (priv->list->type == LENOVO_FAN_TEST_DATA) + lwmi_cd_fan_show(s, &priv->list->cd_fan[idx]); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(lwmi_cd_debugfs); + +/** + * lwmi_cd_debugfs_add() - Create debugfs directory and files for a device + * @priv: lenovo-wmi-capdata driver data. + */ +static void lwmi_cd_debugfs_add(struct lwmi_cd_priv *priv) +{ + priv->debugfs_dir = lwmi_debugfs_create_dir(priv->wdev); + + debugfs_create_file("capdata", 0444, priv->debugfs_dir, priv, &lwmi_cd_debugfs_fops); +} + +/** + * lwmi_cd_debugfs_remove() - Remove debugfs directory for a device + * @priv: lenovo-wmi-capdata driver data. + */ +static void lwmi_cd_debugfs_remove(struct lwmi_cd_priv *priv) +{ + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; +} + +/* ======== WMI interface ======== */ + /** * lwmi_cd_cache() - Cache all WMI data block information * @priv: lenovo-wmi-capdata driver data. @@ -772,6 +887,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context) dev_err(&wdev->dev, "failed to register %s: %d\n", info->name, ret); } else { + lwmi_cd_debugfs_add(priv); + dev_dbg(&wdev->dev, "registered %s with %u items\n", info->name, priv->list->count); } @@ -782,6 +899,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev) { struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev); + lwmi_cd_debugfs_remove(priv); + switch (priv->list->type) { case LENOVO_CAPABILITY_DATA_00: lwmi_cd_sub_master_del(priv); @@ -821,6 +940,7 @@ static struct wmi_driver lwmi_cd_driver = { module_wmi_driver(lwmi_cd_driver); +MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table); MODULE_AUTHOR("Derek J. Clark "); MODULE_AUTHOR("Rong Zhang "); diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index a49229cec245..92098aeeee84 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -38,9 +38,10 @@ struct capdata00 { }; struct capdata01 { - u32 id; - u32 supported; - u32 default_value; + union { + struct capdata00; + struct capdata00 cd00; + }; u32 step; u32 min_value; u32 max_value; From fbab18baaf9a3fe45c6fd75dab7f5b7f6992356f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 16:43:41 +0200 Subject: [PATCH 1904/5214] platform: arm64 Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. While touching all these arrays, unify usage of whitespace in the list terminator. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519144341.1589034-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/arm64/acer-aspire1-ec.c | 2 +- drivers/platform/arm64/huawei-gaokun-ec.c | 2 +- drivers/platform/arm64/lenovo-thinkpad-t14s.c | 4 ++-- drivers/platform/arm64/lenovo-yoga-c630.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/arm64/acer-aspire1-ec.c b/drivers/platform/arm64/acer-aspire1-ec.c index 438532a047e6..08d0b155a197 100644 --- a/drivers/platform/arm64/acer-aspire1-ec.c +++ b/drivers/platform/arm64/acer-aspire1-ec.c @@ -532,7 +532,7 @@ static int aspire_ec_resume(struct device *dev) } static const struct i2c_device_id aspire_ec_id[] = { - { "aspire1-ec", }, + { .name = "aspire1-ec" }, { } }; MODULE_DEVICE_TABLE(i2c, aspire_ec_id); diff --git a/drivers/platform/arm64/huawei-gaokun-ec.c b/drivers/platform/arm64/huawei-gaokun-ec.c index a83ddc20b5a3..80a8ba8b8dda 100644 --- a/drivers/platform/arm64/huawei-gaokun-ec.c +++ b/drivers/platform/arm64/huawei-gaokun-ec.c @@ -795,7 +795,7 @@ static int gaokun_ec_probe(struct i2c_client *client) } static const struct i2c_device_id gaokun_ec_id[] = { - { "gaokun-ec", }, + { .name = "gaokun-ec" }, { } }; MODULE_DEVICE_TABLE(i2c, gaokun_ec_id); diff --git a/drivers/platform/arm64/lenovo-thinkpad-t14s.c b/drivers/platform/arm64/lenovo-thinkpad-t14s.c index 5590302a5694..e7acb66b77f2 100644 --- a/drivers/platform/arm64/lenovo-thinkpad-t14s.c +++ b/drivers/platform/arm64/lenovo-thinkpad-t14s.c @@ -637,8 +637,8 @@ static const struct of_device_id t14s_ec_of_match[] = { MODULE_DEVICE_TABLE(of, t14s_ec_of_match); static const struct i2c_device_id t14s_ec_i2c_id_table[] = { - { "thinkpad-t14s-ec", }, - {} + { .name = "thinkpad-t14s-ec" }, + { } }; MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table); diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c index 75060c842b24..a8600a977fbc 100644 --- a/drivers/platform/arm64/lenovo-yoga-c630.c +++ b/drivers/platform/arm64/lenovo-yoga-c630.c @@ -238,8 +238,8 @@ static const struct of_device_id yoga_c630_ec_of_match[] = { MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match); static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = { - { "yoga-c630-ec", }, - {} + { .name = "yoga-c630-ec" }, + { } }; MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table); From 84b27a3cdd512f88ef837a913f8bb54c0b9d9290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 17:40:40 +0200 Subject: [PATCH 1905/5214] platform/x86: x86-android-tablets: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260519154040.1594878-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c index ebbedfe5f4e8..67dba9aa51f6 100644 --- a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c +++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c @@ -242,7 +242,7 @@ static int atla10_ec_probe(struct i2c_client *client) } static const struct i2c_device_id atla10_ec_id_table[] = { - { "vexia_atla10_ec" }, + { .name = "vexia_atla10_ec" }, { } }; MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table); From 5a3feefbcfa652e65b9259708222dd31d180eb33 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 15 May 2026 16:58:51 +0200 Subject: [PATCH 1906/5214] platform/x86: Move delayed work on system_dfl_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_wq, which will be deprecated soon and replaced by system_percpu_wq. commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") The function(s) mentioned earlier, end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_wq with system_dfl_wq so that the work may benefit from scheduler task placement. Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260515145851.318787-1-marco.crivellari@suse.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index f63bc00d9a9b..48292cc02ee2 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -2396,7 +2396,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) ret = hp_wmi_fan_speed_max_set(1); if (ret < 0) return ret; - mod_delayed_work(system_wq, &priv->keep_alive_dwork, + mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork, secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: @@ -2405,7 +2405,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv)); if (ret < 0) return ret; - mod_delayed_work(system_wq, &priv->keep_alive_dwork, + mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork, secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_AUTO: From 29f692985819f4089f02a86e151a72f6d4cdd90d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Apr 2026 17:39:34 +0800 Subject: [PATCH 1907/5214] PCI: qcom: Disable ASPM L0s for SA8775P Due to a hardware issue, L0s is not properly supported by the PCIe controller on the SA8775p SoC. If enabled, the L0s to L0 transition triggers below correctable AER errors and may also affect link stability: pcieport 0000:00:00.0: PME: Signaling with IRQ 332 pcieport 0000:00:00.0: AER: enabled with IRQ 332 pcieport 0000:00:00.0: AER: Correctable error message received from 0000:01:00.0 pci 0000:01:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pci 0000:01:00.0: device [17cb:1103] error status/mask=00001000/0000e000 pci 0000:01:00.0: [12] Timeout pcieport 0000:00:00.0: AER: Multiple Correctable error message received from 0000:01:00.0 pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pcieport 0000:00:00.0: device [17cb:0115] error status/mask=00001000/0000e000 pcieport 0000:00:00.0: [12] Timeout Hence, disable L0s for the SA8775p SoC to allow it to properly function by sacrificing a little bit of power saving. Fixes: 58d0d3e032b3 ("PCI: qcom-ep: Add support for SA8775P SOC") Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Shawn Guo [mani: commit log, corrected fixes tag] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260419093934.1223027-1-shengchao.guo@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 65688e28f10d..8cf0a27d166b 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1563,6 +1563,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = { static const struct qcom_pcie_cfg cfg_1_34_0 = { .ops = &ops_1_9_0, .override_no_snoop = true, + .no_l0s = true, }; static const struct qcom_pcie_cfg cfg_2_1_0 = { From f92d4f74de4fd6b233929a47f6bb89536c6274c8 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Wed, 27 May 2026 23:15:45 -0400 Subject: [PATCH 1908/5214] KVM: x86: Fix wrong return value type in guest_cpuid_has() The function guest_cpuid_has() is declared with a return type of 'bool'. However, when kvm_find_cpuid_entry_index() fails to locate a valid CPUID entry, the code incorrectly returns 'NULL' instead of 'false'. Signed-off-by: Li RongQing Link: https://patch.msgid.link/20260528031545.1879-1-lirongqing@baidu.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/cpuid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index fc96ba86c644..8d863f45585d 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -126,7 +126,7 @@ static __always_inline bool guest_cpuid_has(struct kvm_vcpu *vcpu, entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index); if (!entry) - return NULL; + return false; reg = __cpuid_entry_get_reg(entry, cpuid.reg); if (!reg) From 01f94d53947df35ba77cdb3992a4e3ef9d9dc1ad Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 22 May 2026 13:57:17 +0300 Subject: [PATCH 1909/5214] pinctrl: renesas: rzv2m: Use -ENOTSUPP instead of -EOPNOTSUPP The pinctrl and GPIO core code make exceptions for the -ENOTSUPP error code. One such example is gpio_set_config_with_argument_optional(), which returns success when gpio_set_config_with_argument() returns -ENOTSUPP, but reports failure for all other error codes. Returning -EOPNOTSUPP from the pinctrl driver on the unsupported pinctrl operation may lead to boot failures when pinctrl drivers implements struct gpio_chip::set_config, the system uses GPIO hogs, and the struct gpio_chip::set_config implementation returns -EOPNOTSUPP for the unsupported operations. Currently, the driver does not implement struct gpio_chip::set_config(). To avoid future failures, return -ENOTSUPP from rzv2m_pinctrl_pinconf_set(). rzv2m_pinctrl_pinconf_group_get() is used when dumping pinctrl configuration. pinconf_generic_dump_one(), which calls it, makes exceptions for the -EINVAL and -ENOTSUPP error codes. The documentation for struct pinconf_ops::pin_config_group_get states that it "should return -ENOTSUPP and -EINVAL using the same rules as pin_config_get()". The documentation for struct pinconf_ops::pin_config_get states: "get the config of a certain pin, if the requested config is not available on this controller this should return -ENOTSUPP and if it is available but disabled it should return -EINVAL". Return -ENOTSUPP for the unsupported pinctrl operation. Suggested-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260522105717.1727837-1-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzv2m.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzv2m.c b/drivers/pinctrl/renesas/pinctrl-rzv2m.c index 827b3e91a6cc..9029d1947bbb 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzv2m.c +++ b/drivers/pinctrl/renesas/pinctrl-rzv2m.c @@ -661,7 +661,7 @@ static int rzv2m_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, } default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } @@ -711,7 +711,7 @@ static int rzv2m_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, /* Check config matches previous pins */ if (i && prev_config != *config) - return -EOPNOTSUPP; + return -ENOTSUPP; prev_config = *config; } From b3ea1cac08b25aad9e0a360c694198b37e35706a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 28 May 2026 11:04:35 +0300 Subject: [PATCH 1910/5214] pinctrl: renesas: rzg2l: Keep member documentation aligned Keep the documentation for struct rzg2l_pinctrl_reg_cache members aligned with the struct member order. Reviewed-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260528080439.615958-4-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index ac42093fc579..e534684ee613 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -361,16 +361,16 @@ struct rzg2l_pinctrl_pin_settings { * @pmc: PMC registers cache * @pfc: PFC registers cache * @iolh: IOLH registers cache - * @pupd: PUPD registers cache * @ien: IEN registers cache + * @pupd: PUPD registers cache * @smt: SMT registers cache * @sr: SR registers cache * @nod: NOD registers cache * @clone: Clone register cache * @sd_ch: SD_CH registers cache * @eth_poc: ET_POC registers cache - * @other_poc: OTHER_POC register cache * @oen: Output Enable register cache + * @other_poc: OTHER_POC register cache * @qspi: QSPI registers cache */ struct rzg2l_pinctrl_reg_cache { From 80538a53978bb9788080caea6e5ee3393dfb6a72 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 28 May 2026 11:04:36 +0300 Subject: [PATCH 1911/5214] pinctrl: renesas: rzg2l: Use tab instead of spaces Use tab instead of spaces to follow the same coding style. Reviewed-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260528080439.615958-5-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index e534684ee613..83c61dcb24b1 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -388,7 +388,7 @@ struct rzg2l_pinctrl_reg_cache { u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; - u8 other_poc; + u8 other_poc; u8 qspi; }; From c3647e582e1563d27896c62686d88db76c6cd3a6 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 28 May 2026 08:40:43 -0400 Subject: [PATCH 1912/5214] KVM: x86: Use fls() instead of ffs() for rmaps histogram bucketing The kvm_mmu_rmaps_stat_show() function implements a logarithmic histogram to collect statistics on the distribution of pte_list_count sizes. However, it currently uses ffs() (Find First Set), which looks for the least significant set bit. Using ffs() leads to severe statistical distortion for any count that is not a power of two. For example, if a rmap has a pte_list_count of 6 (binary 0110), ffs(6) returns 2, forcing this entry to be erroneously counted towards the bucket representing a count of 2. In contrast, it conceptually belongs to the bucket spanning the 4 to 7 range. Replace ffs() with fls() (Find Last Set) to correctly identify the most significant set bit (effectively computing floor(log2(count)) + 1). This Ensure that intermediate counts are correctly categorized into their appropriate power-of-two order-of-magnitude buckets rather than being severely under-reported. Fixes: 3bcd0662d66f ("KVM: X86: Introduce mmu_rmaps_stat per-vm debugfs file") Signed-off-by: Li RongQing Link: https://patch.msgid.link/20260528124043.2153-1-lirongqing@baidu.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/debugfs.c b/arch/x86/kvm/debugfs.c index 999227fc7c66..0074a56e45b4 100644 --- a/arch/x86/kvm/debugfs.c +++ b/arch/x86/kvm/debugfs.c @@ -121,7 +121,7 @@ static int kvm_mmu_rmaps_stat_show(struct seq_file *m, void *v) lpage_size = kvm_mmu_slot_lpages(slot, k + 1); cur = log[k]; for (l = 0; l < lpage_size; l++) { - index = ffs(pte_list_count(&rmap[l])); + index = fls(pte_list_count(&rmap[l])); if (WARN_ON_ONCE(index >= RMAP_LOG_SIZE)) index = RMAP_LOG_SIZE - 1; cur[index]++; From f59e975f2f6dee121d8168436a205542a91c0f33 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 15 May 2026 12:09:26 +0300 Subject: [PATCH 1913/5214] clk: renesas: r8a779g0: Add DSC clock Add the DSC module clock for Renesas R-Car V4H (R8A779G0) SoC. Signed-off-by: Marek Vasut Signed-off-by: Tomi Valkeinen Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260515-rcar-du-dsc-v3-1-164157820498@ideasonboard.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a779g0-cpg-mssr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/renesas/r8a779g0-cpg-mssr.c b/drivers/clk/renesas/r8a779g0-cpg-mssr.c index 015b9773cc55..54ba76ff5ab0 100644 --- a/drivers/clk/renesas/r8a779g0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a779g0-cpg-mssr.c @@ -245,6 +245,7 @@ static const struct mssr_mod_clk r8a779g0_mod_clks[] __initconst = { DEF_MOD("fcpvx0", 1100, R8A779G0_CLK_S0D1_VIO), DEF_MOD("fcpvx1", 1101, R8A779G0_CLK_S0D1_VIO), DEF_MOD("tsn", 2723, R8A779G0_CLK_S0D4_HSC), + DEF_MOD("dsc", 2819, R8A779G0_CLK_VIOBUSD2), DEF_MOD("ssiu", 2926, R8A779G0_CLK_S0D6_PER), DEF_MOD("ssi", 2927, R8A779G0_CLK_S0D6_PER), }; From f5e45196023dd454dcf5dd8add1cf99d77336271 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 24 May 2026 09:26:50 +0100 Subject: [PATCH 1914/5214] clk: renesas: r9a08g045: Drop unused pm_domain header file The linux/pm_domain.h header is not used in this file. Remove it to keep the includes clean. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260524082657.19335-1-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r9a08g045-cpg.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 3e816c881a14..624fc5e6fb24 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -9,7 +9,6 @@ #include #include #include -#include #include From a167ae8eace52dd6c80438b77d92450fe12cd4be Mon Sep 17 00:00:00 2001 From: Harrison Vanderbyl Date: Fri, 15 May 2026 15:41:48 +1000 Subject: [PATCH 1915/5214] platform/surface: SAM: Add support for Surface Pro 12in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a SAM client device node group and registry entry for the Microsoft Surface Pro, 12-inch with Snapdragon. This set enables the use of the following devices. 1: cover keyboard 2: cover touchpad 3: pen stash events. The battery info and charger info devices have been purposefully omitted as they are also reported by other drivers and cause conflicts. Signed-off-by: Harrison Vanderbyl Link: https://patch.msgid.link/ab458aadea651396d9ea7629419a32dc7510c593.1778822464.git.harrison.vanderbyl@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../surface/surface_aggregator_registry.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index f0881edfb616..d37a7e4c6b02 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -420,6 +420,19 @@ static const struct software_node *ssam_node_group_sp11[] = { NULL, }; +/* Devices for Surface Pro 12" first edition (ARM/QCOM) */ +static const struct software_node *ssam_node_group_sp12in[] = { + &ssam_node_root, + &ssam_node_hub_kip, + &ssam_node_tmp_sensors, + &ssam_node_hid_kip_keyboard, + &ssam_node_hid_sam_penstash, + &ssam_node_hid_kip_touchpad, + &ssam_node_hid_kip_fwupd, + &ssam_node_pos_tablet_switch, + NULL, +}; + /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { @@ -498,6 +511,8 @@ static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = { { .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g }, /* Surface Pro 11 (ARM/QCOM) */ { .compatible = "microsoft,denali", (void *)ssam_node_group_sp11 }, + /* Surface Pro 12in First Edition (ARM/QCOM) */ + { .compatible = "microsoft,surface-pro-12in", (void *)ssam_node_group_sp12in }, /* Surface Laptop 7 */ { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 }, { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 }, From 306e443156b82a2a14a3f33da908c303be45529c Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Wed, 27 May 2026 18:05:39 +0900 Subject: [PATCH 1916/5214] mtd: spi-nor: spansion: use die erase for multi-die devices only Die erase opcode is supported in multi-die devices only. For single die devices, default chip erase opcode must be used. In s25hx_t_late_init(), die erase opcode is set only when the device is multi-die. Fixes: 461d0babb544 ("mtd: spi-nor: spansion: enable die erase for multi die flashes") Cc: stable@kernel.org Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus Reviewed-by: Miquel Raynal Signed-off-by: Takahiro Kuwano Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/spansion.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 8498c7003d88..b6023076903a 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -674,7 +674,9 @@ static int s25hx_t_late_init(struct spi_nor *nor) params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + if (params->n_dice > 1) + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + return 0; } From df415c5e1de0f1aeefacb4e6252ff98d38c04437 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Wed, 27 May 2026 18:05:40 +0900 Subject: [PATCH 1917/5214] mtd: spi-nor: spansion: add die erase support in s28hx-t S28Hx-T family has multi-die devices that support die erase opcode. Update die erase opcode when the device is multi-die. Reviewed-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Reviewed-by: Miquel Raynal Signed-off-by: Takahiro Kuwano Signed-off-by: Pratyush Yadav --- drivers/mtd/spi-nor/spansion.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index b6023076903a..65227d989de1 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -762,6 +762,9 @@ static int s28hx_t_late_init(struct spi_nor *nor) params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); + if (params->n_dice > 1) + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + return 0; } From e72b425f617edebf6f374fa39f2c763c9b3696ec Mon Sep 17 00:00:00 2001 From: "Zenghui Yu (Huawei)" Date: Wed, 18 Mar 2026 22:43:05 +0800 Subject: [PATCH 1918/5214] KVM: arm64: Remove @arch from __load_stage2() Since commit fe49fd940e22 ("KVM: arm64: Move VTCR_EL2 into struct s2_mmu"), @arch is no longer required to obtain the per-kvm_s2_mmu vtcr and can be removed from __load_stage2(). Signed-off-by: Zenghui Yu (Huawei) Reviewed-by: Anshuman Khandual Link: https://patch.msgid.link/20260318144305.56831-1-zenghui.yu@linux.dev Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_mmu.h | 3 +-- arch/arm64/kvm/at.c | 2 +- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 +- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 2 +- arch/arm64/kvm/hyp/nvhe/switch.c | 2 +- arch/arm64/kvm/hyp/nvhe/tlb.c | 4 ++-- arch/arm64/kvm/hyp/vhe/switch.c | 2 +- arch/arm64/kvm/hyp/vhe/tlb.c | 4 ++-- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 01e9c72d6aa7..6eae7e7e2a68 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -318,8 +318,7 @@ static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu) * Must be called from hyp code running at EL2 with an updated VTTBR * and interrupts disabled. */ -static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu, - struct kvm_arch *arch) +static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu) { write_sysreg(mmu->vtcr, vtcr_el2); write_sysreg(kvm_get_vttbr(mmu), vttbr_el2); diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 9f8f0ae8e86e..b91ef006919e 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1380,7 +1380,7 @@ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) } } write_sysreg_el1(vcpu_read_sys_reg(vcpu, SCTLR_EL1), SYS_SCTLR); - __load_stage2(mmu, mmu->arch); + __load_stage2(mmu); skip_mmu_switch: /* Temporarily switch back to guest context */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index 3cbfae0e3dda..aaeec6862215 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -67,7 +67,7 @@ int refill_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages, static __always_inline void __load_host_stage2(void) { if (static_branch_likely(&kvm_protected_mode_initialized)) - __load_stage2(&host_mmu.arch.mmu, &host_mmu.arch); + __load_stage2(&host_mmu.arch.mmu); else write_sysreg(0, vttbr_el2); } diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 28a471d1927c..888bd7e71d0c 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -337,7 +337,7 @@ int __pkvm_prot_finalize(void) kvm_flush_dcache_to_poc(params, sizeof(*params)); write_sysreg_hcr(params->hcr_el2); - __load_stage2(&host_mmu.arch.mmu, &host_mmu.arch); + __load_stage2(&host_mmu.arch.mmu); /* * Make sure to have an ISB before the TLB maintenance below but only diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 8d1df3d33595..7318e3e6a5f3 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -315,7 +315,7 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) __sysreg_restore_state_nvhe(guest_ctxt); mmu = kern_hyp_va(vcpu->arch.hw_mmu); - __load_stage2(mmu, kern_hyp_va(mmu->arch)); + __load_stage2(mmu); __activate_traps(vcpu); __hyp_vgic_restore_state(vcpu); diff --git a/arch/arm64/kvm/hyp/nvhe/tlb.c b/arch/arm64/kvm/hyp/nvhe/tlb.c index b29140995d48..fdb90483340c 100644 --- a/arch/arm64/kvm/hyp/nvhe/tlb.c +++ b/arch/arm64/kvm/hyp/nvhe/tlb.c @@ -110,7 +110,7 @@ static void enter_vmid_context(struct kvm_s2_mmu *mmu, if (vcpu) __load_host_stage2(); else - __load_stage2(mmu, kern_hyp_va(mmu->arch)); + __load_stage2(mmu); asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT)); } @@ -128,7 +128,7 @@ static void exit_vmid_context(struct tlb_inv_context *cxt) return; if (vcpu) - __load_stage2(mmu, kern_hyp_va(mmu->arch)); + __load_stage2(mmu); else __load_host_stage2(); diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 1e8995add14f..bbe9cebd3d9d 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -219,7 +219,7 @@ void kvm_vcpu_load_vhe(struct kvm_vcpu *vcpu) __vcpu_load_switch_sysregs(vcpu); __vcpu_load_activate_traps(vcpu); - __load_stage2(vcpu->arch.hw_mmu, vcpu->arch.hw_mmu->arch); + __load_stage2(vcpu->arch.hw_mmu); } void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/hyp/vhe/tlb.c b/arch/arm64/kvm/hyp/vhe/tlb.c index f7b9dfe3f3a5..c386d9f1c101 100644 --- a/arch/arm64/kvm/hyp/vhe/tlb.c +++ b/arch/arm64/kvm/hyp/vhe/tlb.c @@ -60,7 +60,7 @@ static void enter_vmid_context(struct kvm_s2_mmu *mmu, * place before clearing TGE. __load_stage2() already * has an ISB in order to deal with this. */ - __load_stage2(mmu, mmu->arch); + __load_stage2(mmu); val = read_sysreg(hcr_el2); val &= ~HCR_TGE; write_sysreg_hcr(val); @@ -78,7 +78,7 @@ static void exit_vmid_context(struct tlb_inv_context *cxt) /* ... and the stage-2 MMU context that we switched away from */ if (cxt->mmu) - __load_stage2(cxt->mmu, cxt->mmu->arch); + __load_stage2(cxt->mmu); if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) { /* Restore the registers to what they were */ From 8e2b43d2c10b1b5f42805810c6854470d8774e60 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Wed, 27 May 2026 10:05:29 -0700 Subject: [PATCH 1919/5214] media: i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS) Add driver for Intel Computer Vision Sensing (CVS) devices found on Intel Luna Lake (LNL), Panther Lake (PTL), and Arrow Lake (ARL) platforms. The CVS device acts as a V4L2 sub-device bridge that manages CSI-2 link ownership between the host (Linux) and firmware for camera sensors. It provides: - Query the device status via sysfs interface - CSI-2 link ownership arbitration between host and CVS firmware - MIPI CSI-2 configuration management - Privacy LED control coordination - Power management integration with runtime PM The driver consists of two main components: core.c: Core driver with probe, command transport, and power management v4l2.c: V4L2 sub-device and media framework integration Hardware Interface: - I2C for command/control communication with device firmware - GPIO signals for ownership handshaking (request/response) - Optional reset and wake interrupt for full-capability variants - Integration with Intel IPU via ipu_bridge The driver supports two hardware capability levels: - Light capability: Basic GPIO-based ownership (2 GPIOs) - Full capability: Enhanced with reset control and wake IRQ (4 GPIOs) Device-specific quirks are handled via a quirk table to accommodate variations across different CVS implementations (Lattice, Synaptics). In addition to I2C-based operation, the driver supports platform device instantiation for systems where CVS is exposed without I2C transport, falling back to GPIO-only ownership control. The CVS driver integrates with the IPU bridge for automatic device discovery via ACPI on supported platforms. PCI device IDs for Intel IPU7 (0x645d, shared by MTL and LNL) and IPU7.5 (0xb05d, shared by ARL and PTL) are included in the driver-local icvs_pci_tbl lookup table, enabling CVS to locate these IPU variants without modifying the shared ipu6-pci-table header. A PM runtime device link is established between IPU (consumer) and CVS (supplier) so that the PM framework automatically resumes CVS before IPU begins streaming, triggering cvs_runtime_resume() to claim CSI-2 link ownership. Ownership is released via cvs_runtime_suspend() after the autosuspend delay. Signed-off-by: Miguel Vadillo Tested-by: Mehdi Djait # Dell XPS 13 9350 + IPU7 Reviewed-by: Mehdi Djait Signed-off-by: Sakari Ailus --- MAINTAINERS | 6 + drivers/media/i2c/Kconfig | 2 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/cvs/Kconfig | 21 + drivers/media/i2c/cvs/Makefile | 4 + drivers/media/i2c/cvs/core.c | 1043 ++++++++++++++++++++++++++++++++ drivers/media/i2c/cvs/icvs.h | 495 +++++++++++++++ drivers/media/i2c/cvs/v4l2.c | 618 +++++++++++++++++++ 8 files changed, 2190 insertions(+) create mode 100644 drivers/media/i2c/cvs/Kconfig create mode 100644 drivers/media/i2c/cvs/Makefile create mode 100644 drivers/media/i2c/cvs/core.c create mode 100644 drivers/media/i2c/cvs/icvs.h create mode 100644 drivers/media/i2c/cvs/v4l2.c diff --git a/MAINTAINERS b/MAINTAINERS index 3124500ff432..efbf808063e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12848,6 +12848,12 @@ S: Orphan T: git git://git.code.sf.net/p/intel-sas/isci F: drivers/scsi/isci/ +INTEL COMPUTER VISION SENSING (CVS) DRIVER +M: Miguel Vadillo +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/cvs/ + INTEL CPU family model numbers M: Tony Luck M: x86@kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index a3ab48607dcf..5d173e0ecf42 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1657,6 +1657,8 @@ endmenu menu "Miscellaneous helper chips" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +source "drivers/media/i2c/cvs/Kconfig" + config VIDEO_I2C tristate "I2C transport video support" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 90b276a7417a..e45359efe0e4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -171,3 +171,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/ diff --git a/drivers/media/i2c/cvs/Kconfig b/drivers/media/i2c/cvs/Kconfig new file mode 100644 index 000000000000..4309d20dd726 --- /dev/null +++ b/drivers/media/i2c/cvs/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_INTEL_CVS + tristate "Intel CVS CSI-2 bridge support" + depends on I2C && ACPI && VIDEO_DEV + depends on IPU_BRIDGE || !IPU_BRIDGE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This adds support for the Intel Computer Vision Sensing (CVS). + + The driver registers a V4L2 sub-device to arbitrate CSI-2 link + ownership between the host and CVS firmware, and configures the + device for camera sensor streaming. + + The driver can operate with full I2C transport or in a reduced + platform (GPIO-only) mode when I2C is unavailable. + + Say Y to build into the kernel, or M to build as a module. + The module will be named intel_cvs. If unsure, say N. diff --git a/drivers/media/i2c/cvs/Makefile b/drivers/media/i2c/cvs/Makefile new file mode 100644 index 000000000000..0aeb2c2e3100 --- /dev/null +++ b/drivers/media/i2c/cvs/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +intel_cvs-y := core.o v4l2.o + +obj-$(CONFIG_VIDEO_INTEL_CVS) += intel_cvs.o diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c new file mode 100644 index 000000000000..4282f33c7295 --- /dev/null +++ b/drivers/media/i2c/cvs/core.c @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "icvs.h" + +/* Command timeouts determined experimentally */ +#define CMD_TIMEOUT (5 * HZ) +#define FW_READY_DELAY_MS 100 + +#define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ +#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ + +/* + * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. + * Once the IPU6 driver gains support for IPU7, this table can be dropped. + */ +static const struct pci_device_id icvs_ipu7_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { } +}; + +static const struct acpi_gpio_params gpio_wake = { 0, 0, false }; +static const struct acpi_gpio_params gpio_rst = { 1, 0, false }; +static const struct acpi_gpio_params gpio_req = { 2, 0, false }; +static const struct acpi_gpio_params gpio_resp = { 3, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_gpios[] = { + { "wake-gpio", &gpio_wake, 1 }, + { "rst-gpio", &gpio_rst, 1 }, + { "req-gpio", &gpio_req, 1 }, + { "resp-gpio", &gpio_resp, 1 }, + { } +}; + +static const struct acpi_gpio_params lgpio_req = { 0, 0, false }; +static const struct acpi_gpio_params lgpio_resp = { 1, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_lgpios[] = { + { "req-gpio", &lgpio_req, 1 }, + { "resp-gpio", &lgpio_resp, 1 }, + { } +}; + +/* Device quirk table */ +static const struct icvs_device_quirk cvs_quirk_table[] = { + { 0x2ac1, 0x20d0, ICVS_NO_MIPI_CONFIG | + ICVS_NO_CAPS | + ICVS_NO_FW_UPDATE + }, /* Lattice NX33 */ + { 0x06CB, 0x0701, ICVS_SKIP_FW_RESET | + ICVS_HOST_SENSOR_PWR_CTRL | + ICVS_HOST_PRIV_CTRL | + ICVS_FW_BUF_SIZE_256 | + ICVS_FW_HEADER_SIZE_256 + }, /* Synaptics SVP7xxx */ + { } +}; + +/** + * cvs_set_quirks - Match device VID/PID and set quirks + * @ctx: CVS device context + * @vid: Vendor ID + * @pid: Product ID + * + * Searches the quirk table for a matching VID/PID and populates ctx->quirks + * with the corresponding quirk flags. + * If no match is found, quirks is set to 0. + */ +static void cvs_set_quirks(struct icvs *ctx, u16 vid, u16 pid) +{ + ctx->quirks = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(cvs_quirk_table); i++) { + if (cvs_quirk_table[i].vid == vid && + cvs_quirk_table[i].pid == pid) { + ctx->quirks = cvs_quirk_table[i].quirks; + dev_info(cvs_dev(ctx), + "Quirks: 0x%lx (VID:0x%04x PID:0x%04x)\n", + ctx->quirks, vid, pid); + return; + } + } + + dev_info(cvs_dev(ctx), + "No quirks for device (VID:0x%04x PID:0x%04x)\n", vid, pid); +} + +/* I2C transport helpers */ + +/** + * cvs_read_i2c - Issue a read-type command and fetch device response + * @ctx: CVS device context + * @cmd_id: Command identifier (big endian) + * @resp: Destination buffer for response payload + * @size: Size of payload to read into @resp (without prefix) + * + * Sends @cmd_id and reads back the response in a single I2C transaction. + * When the device prepends a 4-byte protocol prefix, the combined + * prefix+payload is read into a temporary buffer and only the payload is + * copied to @resp, avoiding any dependency on the layout of the caller's + * buffer. + * + * Return: 0 on success or negative errno. + */ +static int cvs_read_i2c(struct icvs *ctx, __be16 cmd_id, void *resp, + size_t size) +{ + size_t prefix_size = ctx->prefix ? sizeof(u32) : 0; + size_t read_size = size + prefix_size; + struct i2c_client *i2c = ctx->i2c_client; + u8 *buf __free(kfree) = NULL; + int cnt; + + if (!resp || !size) + return -EINVAL; + + cnt = i2c_master_send(i2c, (const char *)&cmd_id, sizeof(cmd_id)); + if (cnt != sizeof(cmd_id)) + return cnt < 0 ? cnt : -EIO; + + buf = kmalloc(read_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cnt = i2c_master_recv(i2c, buf, read_size); + if (cnt != read_size) { + dev_dbg(cvs_dev(ctx), "recv cmd 0x%04x short read (%d/%zu)\n", + be16_to_cpu(cmd_id), cnt, read_size); + return cnt < 0 ? cnt : -EIO; + } + + memcpy(resp, buf + prefix_size, size); + + return 0; +} + +/** + * cvs_write_i2c - Write a raw command buffer to the device over I2C + * @ctx: CVS device context + * @data: Buffer containing command + payload + * @size: Total bytes to write + * + * Return: 0 on success or negative errno. + */ +static int cvs_write_i2c(struct icvs *ctx, const void *data, int size) +{ + struct i2c_client *i2c = ctx->i2c_client; + int cnt; + + if (size < 0 || !data) + return -EINVAL; + + cnt = i2c_master_send(i2c, data, size); + if (cnt != size) { + dev_dbg(cvs_dev(ctx), "send short (%d/%d)\n", cnt, size); + return cnt < 0 ? cnt : -EIO; + } + + return 0; +} + +/** + * cvs_checksum - Simple additive checksum helper + * @data: 32-bit aligned data buffer + * @len: Length in bytes (multiple of 4) + * + * Return: 32-bit additive checksum of the dwords in @data. + */ +static u32 cvs_checksum(const void *data, size_t len) +{ + const u32 *words = data; + u32 csum = 0; + + if (WARN_ON_ONCE(len % sizeof(u32))) + return 0; + + for (unsigned int i = 0; i < len / sizeof(u32); i++) + csum += words[i]; + + return csum; +} + +/** + * cvs_schedule_and_wait - Schedule polling work then wait for completion + * @ctx: CVS device context + * @work_delay_ms: Delay in milliseconds before polling work executes + * @wait_jiffies: Timeout (jiffies) to wait for cmd completion + * + * Queues ctx->work to run after @work_delay_ms and then waits up to + * @wait_jiffies for ctx->cmd_completion. + * + * Return: 0 on success, -ETIMEDOUT on timeout, negative errno on error. + */ +static int cvs_schedule_and_wait(struct icvs *ctx, unsigned int work_delay_ms, + unsigned long wait_jiffies) +{ + int ret; + + schedule_delayed_work(&ctx->work, msecs_to_jiffies(work_delay_ms)); + ret = wait_for_completion_killable_timeout(&ctx->cmd_completion, + wait_jiffies); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +/** + * cvs_wait_wake_or_sleep - Wait for wake IRQ or sleep fallback + * @ctx: CVS device context + * @timeout_jiffies: Timeout (jiffies) for wake event on full-cap devices + * @sleep_ms: Milliseconds to sleep on light-cap devices + * + * For full capability devices (ICVS_FULLCAP) waits interruptibly on + * ctx->hostwake_event until ctx->hostwake_event_arg becomes true or + * @timeout_jiffies elapses. The flag is cleared after the wait. + * For light capability devices performs a blocking msleep(@sleep_ms). + * + * Return codes normalized for caller switch handling: + * <0 : error / interrupted + * -ETIMEDOUT : timeout (wake event not observed) + * 0 : success (wake observed OR light-cap sleep elapsed) + * + * Return: negative errno, -ETIMEDOUT on timeout, 0 on success. + */ +static int cvs_wait_wake_or_sleep(struct icvs *ctx, + unsigned long timeout_jiffies, + unsigned int sleep_ms) +{ + int ret; + + if (ctx->res == ICVS_FULLCAP) { + ret = wait_event_interruptible_timeout(ctx->hostwake_event, + ctx->hostwake_event_arg, + timeout_jiffies); + ctx->hostwake_event_arg = false; + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + return 0; + } + + msleep(sleep_ms); + + return 0; /* treat sleep path as success */ +} + +/** + * cvs_config_mipi - Send a HOST_SET_MIPI_CONFIG command + * @ctx: CVS device context + * @c: Command container with conf field populated + * @len: Length of original command structure (unused except for symmetry) + * + * Packages @c->param.conf into a cvs_mipi_data_packet including size and + * checksum, then writes it to the device. + * + * Firmware note: the CVS firmware expects the MIPI configuration to be + * sent as a single transaction, including all relevant parameters and checksum. + * + * Return: 0 on success, NO_MIPI_CONFIG or negative errno from I2C write. + */ +static int cvs_config_mipi(struct icvs *ctx, struct icvs_cmd *c, size_t len) +{ + struct icvs_mipi_data_packet pkt = { + .cmd_id = c->cmd_id, + .size = sizeof(c->param.conf), + .crc = cvs_checksum(&c->param.conf, sizeof(c->param.conf)), + .conf = c->param.conf, + }; + + if (ctx->quirks & ICVS_NO_MIPI_CONFIG) + return 0; + + return cvs_write_i2c(ctx, &pkt, sizeof(pkt)); +} + +/** + * cvs_get_device_state - Query current device state bitfield + * @ctx: CVS device context + * @state: Returned state value + * + * Issues GET_DEV_STATE and fills @state. + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_state(struct icvs *ctx, u8 *state) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_STATE), + }; + int ret; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.state, sizeof(n.resp.state)); + if (ret) + return ret; + + *state = n.resp.state; + + return 0; +} + +/** + * cvs_get_device_caps - Read protocol capabilities + * @ctx: CVS device context + * @caps: Capability structure to populate + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_caps(struct icvs *ctx, + struct icvs_dev_capabilities *caps) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_CAPABILITY), + }; + int ret; + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.cap, sizeof(n.resp.cap)); + if (ret) + return ret; + + *caps = n.resp.cap; + + return 0; +} + +/** + * cvs_hw_init - Probe device for prefix support and apply quirks + * @ctx: CVS device context + * + * Sends GET_DEV_VID_PID and probes for a 32-bit prefix. + * If it matches ICVS_PREFIX_VAL, sets ctx->prefix for subsequent reads. + * Then reads VID/PID and applies matching quirks. + * GET_DEV_VID_PID is supported by all protocol versions. + * + * Return: 0 on success or negative errno. + */ +static int cvs_hw_init(struct icvs *ctx) +{ + struct icvs_resp n = { }; + __be16 cmd = cpu_to_be16(ICVS_GET_DEV_VID_PID); + u32 resp; + int ret; + + /* + * Clear prefix so cvs_read_i2c always reads exactly sizeof(u32) bytes + * here, regardless of any value left over from a previous call (e.g. + * on resume). + */ + ctx->prefix = false; + ret = cvs_read_i2c(ctx, cmd, &resp, sizeof(resp)); + if (ret) + return ret; + + ctx->prefix = resp == ICVS_PREFIX_VAL; + + /* Now read VID/PID to apply quirks */ + ret = cvs_read_i2c(ctx, cmd, + &n.resp.vid_pid, sizeof(n.resp.vid_pid)); + if (ret) + return ret; + + cvs_set_quirks(ctx, n.resp.vid_pid.v_id, n.resp.vid_pid.p_id); + + return 0; +} + +/** + * cvs_irq_handler - Wake IRQ handler (full capability devices) + * @irq: IRQ number + * @dev_id: Device context pointer + * + * Sets a waitqueue flag and wakes up sleeping waiters. + * + * Return: IRQ_HANDLED always. + */ +static irqreturn_t cvs_irq_handler(int irq, void *dev_id) +{ + struct icvs *ctx = dev_id; + + ctx->hostwake_event_arg = true; + wake_up_interruptible(&ctx->hostwake_event); + + return IRQ_HANDLED; +} + +/** + * cvs_reset - Toggle reset GPIO for full capability devices + * @ctx: CVS device context + * + * Drives reset low briefly then high if device resources indicate full + * capability. Light devices have no reset line. + */ +static void cvs_reset(struct icvs *ctx) +{ + if (ctx->quirks & ICVS_SKIP_FW_RESET) + return; + + if (ctx->res == ICVS_FULLCAP) { + gpiod_set_value(ctx->rst, 0); + fsleep(2000); + gpiod_set_value(ctx->rst, 1); + } +} + +/** + * cvs_recv - Delayed work handler polling for command completion + * @work: Embedded delayed_work member + * + * Re-reads device state; if device_busy remains set, re-schedules itself. + * Otherwise stores state into wq_resp and completes the command. + */ +static void cvs_recv(struct work_struct *work) +{ + struct icvs *ctx = container_of(work, struct icvs, work.work); + u8 state = 0; + int ret; + + ret = cvs_get_device_state(ctx, &state); + if (ret < 0) { + dev_dbg(cvs_dev(ctx), "state read failed: %d\n", ret); + return; + } + + if (state & ICVS_DEV_STATE_BUSY) { + dev_dbg(cvs_dev(ctx), "device busy, reschedule\n"); + schedule_delayed_work(&ctx->work, + msecs_to_jiffies(FW_READY_DELAY_MS)); + return; + } + + ctx->wq_resp.resp.state = state; + complete(&ctx->cmd_completion); +} + +/** + * cvs_send - Common command submission path + * @ctx: CVS device context + * @cmd: Command buffer (icvs_cmd) with cmd_id and param populated + * @len: Buffer length + * + * Dispatches a set of supported commands: + * - ICVS_SET_DEV_HOST_ID, + * - ICVS_HOST_SENSOR_OWNER, + * - ICVS_HOST_SET_MIPI_CONFIG + * - ICVS_FW_LOADER_* + * + * For I2C based commands it sets big-endian cmd ids, writes to the device + * and waits (via delayed work) for completion or timeout. + * GPIO based ownership toggles are handled locally. + * + * Caller must hold ctx->lock when invoking this function and check for i2c + * bus availability. + * + * Return: 0 on success, negative errno, -EINVAL for unsupported command + * or status from device in ctx->wq_resp. + */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len) +{ + int ret, status = 0; + + lockdep_assert_held(&ctx->lock); + + dev_dbg(cvs_dev(ctx), "send cmd = 0x%04x", be16_to_cpu(cmd->cmd_id)); + + reinit_completion(&ctx->cmd_completion); + + switch (be16_to_cpu(cmd->cmd_id)) { + case ICVS_SET_DEV_HOST_ID: + cmd->cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + if (ret < 0) + break; + + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_HOST_SENSOR_OWNER: + gpiod_set_value_cansleep(ctx->req, cmd->param.param); + fsleep(FW_READY_DELAY_MS * USEC_PER_MSEC); + ret = gpiod_get_value_cansleep(ctx->resp); + status = cmd->param.param == ret ? 0 : -EINVAL; + ret = 0; /* success */ + break; + case ICVS_HOST_SET_MIPI_CONFIG: + cmd->cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG); + ret = cvs_config_mipi(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR) ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_START: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_START); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + case ICVS_FW_LOADER_DATA: + /* Quirk for older protocols */ + if (ctx->caps.protocol_version_major >= 2 && + ctx->caps.protocol_version_minor >= 2) { + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_DATA); + ret = cvs_write_i2c(ctx, cmd, len); + } else { + ret = cvs_write_i2c(ctx, &cmd->param, + len - sizeof(cmd->cmd_id)); + } + + if (ret < 0) + return ret; + + ret = cvs_wait_wake_or_sleep(ctx, FW_READY_DELAY_MS, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_END: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_END); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = !(ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + return ret; + + return ctx->wq_resp.status = status; +} + +/** + * cvs_set_link_owner - Switch CSI-2 link ownership between host and device + * @ctx: CVS device context + * @owner: Desired owner (ICVS_CSI_LINK_HOST or ICVS_CSI_LINK_CVS) + * + * Called from runtime PM callbacks to claim or release the CSI-2 link. + * Also callable directly for error recovery paths. + * + * Return: 0 on success or negative errno. + */ +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SENSOR_OWNER), + .param.param = owner, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/** + * cvs_configure_dev_caps - Configure device capability ownership bits + * @ctx: CVS device context + * + * Tells the CVS device which of its features (privacy LED, RGB camera + * power-up, vision sensing) are controlled by the host, then sends + * SET_DEV_HOST_ID. + * + * Return: 0 on success or negative errno. + */ +static int cvs_configure_dev_caps(struct icvs *ctx) +{ + struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID) }; + size_t sz = sizeof(cmd.cmd_id) + sizeof(cmd.param.host_id); + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + if (ctx->quirks & ICVS_HOST_VISION_SENSING) + cmd.param.host_id |= ICVS_HOST_ID_VISION_SENSING; + if (ctx->quirks & ICVS_HOST_PRIV_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_PRIVACY_LED; + if (ctx->quirks & ICVS_HOST_SENSOR_PWR_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_RGBCAMERA_PWRUP; + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, sz); +} + +/** + * cvs_core_probe - Shared probe path for I2C & platform instantiation + * @dev: Parent device + * @i2c: I2C client (NULL for platform devices) + * + * Discovers IPU, parses ACPI resources, sets up GPIOs/IRQs, initializes + * sub-device (CSI) and host identifier, and exposes sysfs firmware interface. + * + * Return: 0 on success or negative errno. + */ +static int cvs_core_probe(struct device *dev, struct i2c_client *i2c) +{ + struct pci_dev *ipu = NULL; + struct icvs *ctx; + int ret; + + /* Locate IPU device */ + for (unsigned int i = 0; !ipu && ipu6_pci_tbl[i].vendor; i++) + ipu = pci_get_device(ipu6_pci_tbl[i].vendor, + ipu6_pci_tbl[i].device, NULL); + for (unsigned int i = 0; !ipu && icvs_ipu7_tbl[i].vendor; i++) + ipu = pci_get_device(icvs_ipu7_tbl[i].vendor, + icvs_ipu7_tbl[i].device, NULL); + if (!ipu) + return -ENODEV; + + ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb); + if (ret < 0) + goto err_put_ipu; + + if (!dev_fwnode(dev)) { + ret = -ENXIO; + goto err_put_ipu; + } + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err_put_ipu; + } + + ctx->i2c_client = i2c; + + ret = gpiod_count(dev, NULL); + switch (ret) { + case ICVS_GPIO_SYNC: + ctx->res = ICVS_LIGHTCAP; + break; + case ICVS_GPIO_ASYNC: + ctx->res = ICVS_FULLCAP; + break; + default: + dev_err(dev, "unexpected GPIO count %d\n", ret); + ret = -EINVAL; + goto err_put_ipu; + } + + ret = devm_acpi_dev_add_driver_gpios(dev, + ctx->res == ICVS_FULLCAP ? + icvs_acpi_gpios : + icvs_acpi_lgpios); + if (ret) { + dev_err_probe(dev, ret, "failed to add ACPI GPIOs\n"); + goto err_put_ipu; + } + + ctx->req = devm_gpiod_get(dev, "req", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->req)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->req), + "failed to get req GPIO\n"); + goto err_put_ipu; + } + + ctx->resp = devm_gpiod_get(dev, "resp", GPIOD_IN); + if (IS_ERR(ctx->resp)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->resp), + "failed to get resp GPIO\n"); + goto err_put_ipu; + } + + if (ctx->res == ICVS_FULLCAP) { + struct gpio_desc *wake; + + ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->rst)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->rst), + "failed to get rst GPIO\n"); + goto err_put_ipu; + } + + wake = devm_gpiod_get(dev, "wake", GPIOD_IN); + if (IS_ERR(wake)) { + ret = dev_err_probe(dev, PTR_ERR(wake), + "failed to get wake GPIO\n"); + goto err_put_ipu; + } + + ctx->irq = gpiod_to_irq(wake); + if (ctx->irq < 0) { + ret = dev_err_probe(dev, ctx->irq, + "failed to get wake IRQ\n"); + goto err_put_ipu; + } + + ret = devm_request_threaded_irq(dev, ctx->irq, NULL, + cvs_irq_handler, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "cvs_wake", ctx); + if (ret) { + dev_err_probe(dev, ret, "failed to request IRQ\n"); + goto err_put_ipu; + } + } + + ret = devm_mutex_init(dev, &ctx->lock); + if (ret) + goto err_put_ipu; + + init_completion(&ctx->cmd_completion); + init_waitqueue_head(&ctx->hostwake_event); + INIT_DELAYED_WORK(&ctx->work, cvs_recv); + + if (i2c) { + ret = cvs_hw_init(ctx); + if (ret) { + dev_err(dev, "HW init failed (%d)\n", ret); + /* + * Fallback to GPIO-only mode. + * Some BIOS show the device on the I2C bus, however, + * the device is not accessible via I2C. + */ + ctx->i2c_client = NULL; + goto fail_i2c; + } + + ret = cvs_get_device_caps(ctx, &ctx->caps); + if (ret) { + dev_err_probe(dev, ret, "get caps failed\n"); + goto err_put_ipu; + } + + ret = cvs_configure_dev_caps(ctx); + if (ret) { + dev_err_probe(dev, ret, + "configure dev caps failed\n"); + goto err_put_ipu; + } + } + +fail_i2c: + ret = cvs_csi_init(ctx, dev, i2c); + if (ret) { + dev_err_probe(dev, ret, "CSI init failed\n"); + goto err_put_ipu; + } + + dev_set_drvdata(dev, ctx); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + /* + * Create a PM runtime device link with IPU as consumer and CVS as + * supplier. When the IPU runtime-resumes to start streaming, the PM + * framework automatically resumes CVS first, triggering + * cvs_runtime_resume() which hands CSI-2 link ownership to the host. + */ + ctx->ipu_link = device_link_add(&ipu->dev, dev, + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE | + DL_FLAG_STATELESS); + if (!ctx->ipu_link) { + dev_err(dev, "IPU device link failed\n"); + ret = -ENODEV; + goto err_csi_remove; + } + + if (has_acpi_companion(dev)) + acpi_dev_clear_dependencies(ACPI_COMPANION(dev)); + + put_device(&ipu->dev); + + return 0; + +err_csi_remove: + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + cvs_csi_remove(ctx); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +err_put_ipu: + put_device(&ipu->dev); + + return ret; +} + +/** + * cvs_probe - I2C driver probe entry + * @i2c: I2C client + * + * Return: 0 on success or negative errno. + */ +static int cvs_probe(struct i2c_client *i2c) +{ + return cvs_core_probe(&i2c->dev, i2c); +} + +/** + * cvs_core_remove - Shared remove logic + * @dev: Device + */ +static void cvs_core_remove(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + cvs_csi_remove(ctx); + + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + cvs_reset(ctx); +} + +/** + * cvs_remove - I2C driver remove + * @client: I2C client + */ +static void cvs_remove(struct i2c_client *client) +{ + cvs_core_remove(&client->dev); +} + +/** + * cvs_suspend - System suspend callback + * @dev: Device + * + * Return: 0. + */ +static int __maybe_unused cvs_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + + return 0; +} + +/** + * cvs_resume - System resume callback + * @dev: Device + * + * Re-validates I2C link prefix and re-sends host id if transport available. + * + * Return: 0 on success or negative errno if I2C check fails. + */ +static int __maybe_unused cvs_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + int ret; + + if (ctx->i2c_client) { + ret = cvs_hw_init(ctx); + if (ret) + return ret; + return cvs_configure_dev_caps(ctx); + } + + return 0; +} + +/** + * cvs_runtime_resume - Runtime PM resume: claim CSI-2 link ownership + * @dev: Device + * + * Triggered automatically when the IPU (consumer) runtime-resumes, because + * a DL_FLAG_PM_RUNTIME device link makes CVS the supplier. Transfers CSI-2 + * link ownership to the host so the IPU can start receiving sensor frames. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_HOST); +} + +/** + * cvs_runtime_suspend - Runtime PM suspend: release CSI-2 link ownership + * @dev: Device + * + * Called when the streaming reference is dropped by cvs_csi_disable_streams + * via pm_runtime_put_autosuspend. Returns CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_CVS); +} + +static const struct dev_pm_ops __maybe_unused cvs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cvs_suspend, cvs_resume) + SET_RUNTIME_PM_OPS(cvs_runtime_suspend, cvs_runtime_resume, NULL) +}; + +static const struct acpi_device_id intel_cvs_acpi_match[] = { + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ + { } +}; +MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match); + +static struct i2c_driver cvs_driver = { + .driver = { + .name = "intel_cvs", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_probe, + .remove = cvs_remove, +}; + +static int cvs_platform_probe(struct platform_device *pdev) +{ + return cvs_core_probe(&pdev->dev, NULL); +} + +static void cvs_platform_remove(struct platform_device *pdev) +{ + cvs_core_remove(&pdev->dev); +} + +/* + * Platform driver structure. + * + * Some platforms may instantiate the CVS device as a platform device + * without I2C support. This driver binding allows such platforms to use the + * CVS core functionality (GPIOs, CSI sub-device) without I2C. + */ +static struct platform_driver cvs_platform_driver = { + .driver = { + .name = "cvs_platform", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_platform_probe, + .remove = cvs_platform_remove, +}; + +/** + * cvs_init - Module init registering I2C and platform drivers + * + * Return: 0 on success or negative errno. + */ +static int __init cvs_init(void) +{ + int ret; + + ret = i2c_add_driver(&cvs_driver); + if (ret) + return ret; + + ret = platform_driver_register(&cvs_platform_driver); + if (ret) { + i2c_del_driver(&cvs_driver); + return ret; + } + + return 0; +} + +/** + * cvs_exit - Module exit unregistering drivers + */ +static void __exit cvs_exit(void) +{ + platform_driver_unregister(&cvs_platform_driver); + i2c_del_driver(&cvs_driver); +} +module_init(cvs_init); +module_exit(cvs_exit); + +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +MODULE_AUTHOR("Miguel Vadillo "); +MODULE_DESCRIPTION("Intel Vision Sensing Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h new file mode 100644 index 000000000000..cfa8ef5d975c --- /dev/null +++ b/drivers/media/i2c/cvs/icvs.h @@ -0,0 +1,495 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Intel Corporation + */ + +#ifndef _ICVS_H +#define _ICVS_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * PCI device IDs for all IPU generations that co-exist with the CVS bridge. + * IPU7 (0x645d) is shared by MTL and LNL; IPU7P5 (0xb05d) is shared by + * ARL and PTL. These are not yet in the shared ipu6-pci-table so they are + * listed here alongside the IPU6 family for probe-time IPU discovery. + */ +struct gpio_desc; +struct i2c_client; + +/* + * GPIO resource counts (ACPI enumerated). + * + * 4-GPIO configuration has a wake IRQ and supports asynchronous messaging. + * 2-GPIO configuration has no IRQ, so communication is synchronous only. + */ +#define ICVS_GPIO_ASYNC 4 +#define ICVS_GPIO_SYNC 2 + +/* Firmware response prefix (optional, for protocol revision 2.x or newer) */ +#define ICVS_PREFIX_VAL 0xCAFEB0BA + +/* + * CSI bridge sub-device definitions + */ +/** + * enum icvs_csi_cmd_id - Low-level CSI bridge command identifiers + * + * These numeric IDs are part of the legacy CSI-side protocol not mapped + * directly to the higher level firmware opcodes in enum icvs_command. Only + * a minimal subset is presently issued/handled by the driver. Others are + * reserved for future expansion of the CSI bridge feature set. + * + * @ICVS_CSI_SET_OWNER: Set CSI sensor ownership between host and CVS + * @ICVS_CSI_SET_CONF: Apply CSI link configuration parameters + * @ICVS_CSI_PRIVACY_NOTIF: Notify host of a privacy state transition + */ +enum icvs_csi_cmd_id { + ICVS_CSI_SET_OWNER = 0, + ICVS_CSI_SET_CONF = 2, + ICVS_CSI_PRIVACY_NOTIF = 6, +}; + +/** + * enum icvs_csi_link_owner - CSI-2 link / sensor ownership + * + * Ownership reflects which endpoint currently controls the attached image + * sensor over the CSI-2 link. Transitions may gate streaming or reconfigure + * link parameters. The host requests ownership changes via protocol opcodes + * and may need to assert GPIO signals on platforms without full capability. + * + * @ICVS_CSI_LINK_HOST: Host (Linux) owns the sensor and may start streaming + * @ICVS_CSI_LINK_CVS: CVS firmware owns the sensor (host must not stream) + */ +enum icvs_csi_link_owner { + ICVS_CSI_LINK_HOST, + ICVS_CSI_LINK_CVS, +}; + +/** + * enum icvs_csi_privacy_status - Reported privacy state + * + * Reflects user privacy (e.g. camera LED assertion and stream gating). The + * MAX value is a sentinel used for bounds checking and is not a real state. + * + * @ICVS_CSI_PRIVACY_OFF: Privacy not asserted (LED off, streaming permitted) + * @ICVS_CSI_PRIVACY_ON: Privacy asserted (LED on and/or stream gated) + * @ICVS_CSI_PRIVACY_MAX: Sentinel; not a valid operational value + */ +enum icvs_csi_privacy_status { + ICVS_CSI_PRIVACY_OFF, + ICVS_CSI_PRIVACY_ON, + ICVS_CSI_PRIVACY_MAX +}; + +/** + * enum icvs_csi_pads - Media pads exposed by the CVS sub-device + * + * The bridge presents a single sink (from the remote sensor) and a single + * source (toward the rest of the media graph / consumers). NUM_PADS is used + * for sizing arrays and iteration; it is not a real pad index. + * + * @ICVS_CSI_PAD_SINK: Sink pad receiving frames from remote sensor + * @ICVS_CSI_PAD_SOURCE: Source pad emitting frames to downstream entities + * @ICVS_CSI_NUM_PADS: Count sentinel (array size / iteration bound) + */ +enum icvs_csi_pads { + ICVS_CSI_PAD_SINK, + ICVS_CSI_PAD_SOURCE, + ICVS_CSI_NUM_PADS +}; + +/* + * Core driver structures and functions used by both I2C and platform modes + */ + +/** + * DOC: CVS device quirk flags + * + * These bit flag macros describe per-device behavioral adjustments applied + * after VID/PID matching (see cvs_i2c_check() and cvs_apply_quirks()). They + * allow the driver to selectively alter logic paths for firmware / hardware + * variants without introducing hard-coded conditionals at each call site. + * + * @ICVS_NO_MIPI_CONFIG: Device firmware performs its own MIPI link setup; + * skip sending HOST_SET_MIPI_CONFIG. + * @ICVS_SKIP_FW_RESET: Skip issuing a post firmware-update reset sequence. + * @ICVS_NO_CAPS: Device firmware does not support GET_DEV_CAPABILITY; + * skip capability query and treat caps as unsupported. + * @ICVS_FW_BUF_SIZE_256: Firmware expects chunk transfer buffer size of 256 + * bytes (override defaults if they differ). + * @ICVS_FW_HEADER_SIZE_256: Firmware header size fixed at 256 bytes offset + * for start of payload data. + * @ICVS_HOST_SENSOR_PWR_CTRL: Host must control sensor power sequencing. + * @ICVS_HOST_PRIV_CTRL: Host owns privacy LED gating. + * @ICVS_HOST_VISION_SENSING: Host enables vision sensing capability bit + * @ICVS_NO_FW_UPDATE: Device does not support firmware update. + */ +#define ICVS_NO_MIPI_CONFIG BIT(0) +#define ICVS_SKIP_FW_RESET BIT(1) +#define ICVS_NO_CAPS BIT(2) +#define ICVS_FW_BUF_SIZE_256 BIT(3) +#define ICVS_FW_HEADER_SIZE_256 BIT(4) +#define ICVS_HOST_SENSOR_PWR_CTRL BIT(5) +#define ICVS_HOST_PRIV_CTRL BIT(6) +#define ICVS_HOST_VISION_SENSING BIT(7) +#define ICVS_NO_FW_UPDATE BIT(8) + +/** + * struct icvs_device_quirk - Device-specific quirk entry + * @vid: Vendor ID + * @pid: Product ID + * @quirks: Quirk flags for this device + */ +struct icvs_device_quirk { + u16 vid; + u16 pid; + unsigned long quirks; +}; + +/** + * struct icvs_dt_config - Data type configuration for a virtual channel + * @pixel_width: Pixel width in pixels + * @pixel_height: Pixel height in pixels + * @data_type: MIPI CSI-2 data type (RAW10, RAW12, etc.) + * @reserved: Reserved for future use + */ +struct icvs_dt_config { + u16 pixel_width; + u16 pixel_height; + u8 data_type; + u8 reserved[3]; +}; + +/** + * struct icvs_vc_config - Virtual channel configuration + * @vc: Virtual channel index (0-31) + * @dt_count: Number of data types configured + * @dt_configs: Array of data type configurations (up to 4) + * @reserved: Reserved for future use + */ +struct icvs_vc_config { + u8 vc; + u8 dt_count; + struct icvs_dt_config dt_configs[4]; + u8 reserved[6]; +}; + +/** + * struct icvs_link_cfg - Host to CVS CSI link configuration + * @fps: Frames per second + * @nr_of_lanes: Number of CSI-2 data lanes used + * @phy_mode: 0 = DPHY, 1 = CPHY + * @vc_count: Number of virtual channels enabled + * @vc_configs: Per-VC configuration + * @link_freq: Link frequency in Hz + * @reserved: Reserved for future use + */ +struct icvs_link_cfg { + u8 fps; + u8 nr_of_lanes; + u8 phy_mode; + u8 vc_count; + struct icvs_vc_config vc_configs[4]; + u32 link_freq; + u8 reserved[8]; +}; + +/** + * struct icvs_fw_version - Firmware version tuple + * @major: Major version + * @minor: Minor version + * @hotfix: Hotfix/patch level + * @build: Build number + */ +struct icvs_fw_version { + u32 major; + u32 minor; + u32 hotfix; + u32 build; +}; + +/** + * struct icvs_vid_pid - Device vendor/product IDs + * @v_id: Vendor ID + * @p_id: Product ID + */ +struct icvs_vid_pid { + u16 v_id; + u16 p_id; +}; + +/** + * struct icvs_mipi_data_packet - Encapsulated MIPI link configuration packet + * @cmd_id: Command identifier + * @size: Payload size (bytes) + * @crc: Checksum over payload + * @conf: CSI link configuration + * @reserved: Reserved for future use + */ +struct icvs_mipi_data_packet { + __be16 cmd_id; + u32 size; + u32 crc; + struct icvs_link_cfg conf; + u8 reserved[70]; +} __packed; + +/** + * struct icvs_mipi_read_packet - Read-back MIPI configuration + * @size: Payload size + * @crc: Payload checksum + * @conf: CSI link configuration + */ +struct icvs_mipi_read_packet { + u32 size; + u32 crc; + struct icvs_link_cfg conf; +}; + +/* Host identifier bitfield masks */ +#define ICVS_HOST_ID_RGBCAMERA_PWRUP BIT(31) +#define ICVS_HOST_ID_PRIVACY_LED BIT(30) +#define ICVS_HOST_ID_DEVICE_POWER GENMASK(29, 28) +#define ICVS_HOST_ID_VISION_SENSING BIT(27) + +/* Device state bitfield masks (u8) */ +#define ICVS_DEV_STATE_PRIVACY BIT(0) +#define ICVS_DEV_STATE_ON BIT(1) +#define ICVS_DEV_STATE_SENSOR_OWNER BIT(2) +#define ICVS_DEV_STATE_DOWNLOAD BIT(4) +#define ICVS_DEV_STATE_ERROR BIT(6) +#define ICVS_DEV_STATE_BUSY BIT(7) + +/* Device capability bitfield masks (u16) */ +#define ICVS_CAP_HOST_MIPI_REQUIRED BIT(15) +#define ICVS_CAP_FW_ANTIROLLBACK BIT(14) +#define ICVS_CAP_PRIVACY2VISIONDRIVER BIT(13) +#define ICVS_CAP_FWUPDATE_RESET_HOST BIT(12) +#define ICVS_CAP_NO_CAMERA_FWUPDATE BIT(11) +#define ICVS_CAP_POWER_DOMAIN_SUPPORT BIT(10) +#define ICVS_CAP_FW_FLASHED_IN_PLACE BIT(9) +#define ICVS_CAP_IO_CONTEXT_HOT BIT(8) + +/** + * struct icvs_dev_capabilities - Protocol capabilities reported by device + * @protocol_version_major: Major protocol version + * @protocol_version_minor: Minor protocol version + * @capability: Capability bitfield - use ICVS_CAP_* masks + * @max_packet_time: Max packet processing time (ms) + * @max_post_dl_time: Max post-download time (s) + */ +struct icvs_dev_capabilities { + u8 protocol_version_major; + u8 protocol_version_minor; + u16 capability; + u16 max_packet_time; + u16 max_post_dl_time; +}; + +/** + * struct icvs_cmd - Generic command container + * @cmd_id: Command identifier + * @param: Parameter union providing multiple variants + * @param.param: Raw parameter + * @param.host_id: Host identifier (ICVS_SET_DEV_HOST_ID) - use + * ICVS_HOST_ID_* masks + * @param.conf: CSI link configuration (ICVS_HOST_SET_MIPI_CONFIG) + */ +struct icvs_cmd { + __be16 cmd_id; + union { + u32 param; + u32 host_id; + struct icvs_link_cfg conf; + } param; +} __packed; + +/** + * struct icvs_resp - Firmware response container + * @status: Internal status code + * @cmd_id: Original command identifier + * @resp: Response union containing variant payload + * @resp.state: Device state response (u8, use ICVS_DEV_STATE_* masks) + * @resp.cap: Capability response + * @resp.conf: Link configuration response + * @resp.mipi_read: Raw link config read-back + * @resp.vid_pid: Vendor/product identifiers + * @resp.fw_version: Firmware version tuple + */ +struct icvs_resp { + u32 status; + __be16 cmd_id; + union { + u8 state; + struct icvs_dev_capabilities cap; + struct icvs_link_cfg conf; + struct icvs_mipi_read_packet mipi_read; + struct icvs_vid_pid vid_pid; + struct icvs_fw_version fw_version; + } resp; +}; + +/** + * enum icvs_resources - Device capability / resource category + * + * Categorizes hardware resource availability which influences protocol + * features (e.g. reset control, wake IRQ, GPIO mediated ownership). Light + * capability devices expose a reduced set of control GPIOs; full capability + * devices provide all optional signals and features. NOTSUP represents an + * unsupported or uninitialized state. + * + * @ICVS_NOTSUP: Capability not supported / not yet determined + * @ICVS_LIGHTCAP: Light capability (limited GPIO / no dedicated wake IRQ) + * @ICVS_FULLCAP: Full capability (reset GPIO, wake IRQ, extended protocol) + */ +enum icvs_resources { + ICVS_NOTSUP, + ICVS_LIGHTCAP, + ICVS_FULLCAP, +}; + +/** + * enum icvs_command - Protocol command opcodes (firmware space 0x0800+) + * @ICVS_GET_DEV_STATE: Query current device state bitfield + * @ICVS_GET_DEV_FW_VERSION: Retrieve firmware version tuple + * @ICVS_GET_DEV_VID_PID: Read vendor / product identifiers + * @ICVS_GET_DEV_ERR_CODE: Fetch last error code (if any) + * @ICVS_GET_DEV_CAPABILITY: Read protocol capability structure + * @ICVS_SET_DEV_HOST_ID: Set host identity / ownership bits + * @ICVS_GET_DEV_HOST_ID: Read back host identity + * @ICVS_FW_LOADER_START: Begin firmware download sequence + * @ICVS_FW_LOADER_DATA: Stream a chunk of firmware payload + * @ICVS_FW_LOADER_END: End firmware download / trigger flash + * @ICVS_HOST_GET_MIPI_CONFIG: Request current MIPI CSI link configuration + * @ICVS_HOST_SET_MIPI_CONFIG: Apply new MIPI CSI link configuration + * @ICVS_HOST_SENSOR_OWNER: Toggle CSI sensor ownership (GPIO assist) + */ +enum icvs_command { + ICVS_GET_DEV_STATE = 0x0800, + ICVS_GET_DEV_FW_VERSION = 0x0801, + ICVS_GET_DEV_VID_PID = 0x0802, + ICVS_GET_DEV_ERR_CODE = 0x0803, + ICVS_GET_DEV_CAPABILITY = 0x0804, + ICVS_SET_DEV_HOST_ID = 0x0805, + ICVS_GET_DEV_HOST_ID = 0x0806, + ICVS_FW_LOADER_START = 0x0820, + ICVS_FW_LOADER_DATA = 0x0821, + ICVS_FW_LOADER_END = 0x0822, + ICVS_HOST_GET_MIPI_CONFIG = 0x082F, + ICVS_HOST_SET_MIPI_CONFIG = 0x0830, + ICVS_HOST_SENSOR_OWNER = 0x0831, +}; + +/** + * enum icvs_state - Device state bitfield flags + * + * These flags correspond to bits in the device state byte returned via + * GET_DEV_STATE and decoded in union icvs_dev_state. Multiple bits may be + * asserted simultaneously. Reserved bits are omitted. + * + * @ICVS_DEVICE_OFF_STATE: Raw zero value; device is powered off or + * not yet ready + * @ICVS_DEVICE_PRIVACY_ON: Privacy mode active (LED asserted and/or + * stream gated) + * @ICVS_DEVICE_ON_STATE: Device powered and responsive to protocol commands + * @ICVS_DEVICE_SENSOR_OWNER: CVS currently owns the attached CSI sensor + * @ICVS_DEVICE_DWNLD_STATE: Firmware download / flash operation in progress + * @ICVS_DEVICE_ERROR_STATE: Device has reported an error (query + * ICVS_GET_DEV_ERR_CODE) + * @ICVS_DEVICE_BUSY_STATE: Device is busy processing a prior command + */ +enum icvs_state { + ICVS_DEVICE_OFF_STATE = 0x00, + ICVS_DEVICE_PRIVACY_ON = BIT(0), + ICVS_DEVICE_ON_STATE = BIT(1), + ICVS_DEVICE_SENSOR_OWNER = BIT(2), + ICVS_DEVICE_DWNLD_STATE = BIT(4), + ICVS_DEVICE_ERROR_STATE = BIT(6), + ICVS_DEVICE_BUSY_STATE = BIT(7), +}; + +/** + * struct icvs - Core CVS device context + * @i2c_client: I2C client (NULL in platform-only mode) + * @work: Delayed work for polling device state / completion + * @wq_resp: Last response container populated by workqueue + * @cmd_completion: Completion for command waiters + * @lock: Mutex protecting command submission & shared state + * @subdev: V4L2 sub-device representing the CSI bridge entity + * @remote: Remote media pad connected to upstream camera sensor + * @notifier: Async notifier for remote sensor discovery + * @ctrl_handler: V4L2 control handler + * @freq_ctrl: (future) frequency control pointer + * @pads: Local media pads (sink/source) + * @nr_of_lanes: Active CSI-2 lane count + * @link_freq: Current link frequency (Hz) + * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) + * @res: Resource capability (light/full) + * @caps: Reported device protocol capabilities + * @prefix: Firmware response prefix present + * @quirks: Device-specific quirk flags + * @rst: Reset GPIO (full capability only) + * @req: Request GPIO (ownership signaling) + * @resp: Response GPIO (ownership signaling) + * @irq: Wake IRQ (full capability) + * @hostwake_event: Waitqueue for wake events + * @hostwake_event_arg: Wake event flag + */ +struct icvs { + struct i2c_client *i2c_client; + struct delayed_work work; + struct icvs_resp wq_resp; + struct completion cmd_completion; + struct mutex lock; /* Protects command execution and device state */ + struct v4l2_subdev subdev; + struct media_pad *remote; + struct v4l2_async_notifier notifier; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *freq_ctrl; + struct media_pad pads[ICVS_CSI_NUM_PADS]; + u32 nr_of_lanes; + u64 link_freq; + struct device_link *ipu_link; + enum icvs_resources res; + struct icvs_dev_capabilities caps; + bool prefix; + unsigned long quirks; + struct gpio_desc *rst; + struct gpio_desc *req; + struct gpio_desc *resp; + int irq; + wait_queue_head_t hostwake_event; + bool hostwake_event_arg; +}; + +/** + * cvs_dev - Helper returning the struct device for a CVS context + * @ctx: CVS context + * + * Avoids repeating transport conditional logic at each call site when + * acquiring the device pointer for logging or PM operations. + * + * Return: Device pointer (never NULL if @ctx is valid). + */ +static inline struct device *cvs_dev(struct icvs *ctx) +{ + return ctx->i2c_client ? &ctx->i2c_client->dev : ctx->subdev.dev; +} + +/* Cross-unit interfaces */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len); +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner); +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c); +void cvs_csi_remove(struct icvs *ctx); + +#endif /* _ICVS_H */ diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c new file mode 100644 index 000000000000..3a1ec0059ef7 --- /dev/null +++ b/drivers/media/i2c/cvs/v4l2.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + * CVS driver - CSI/V4L2 subdev support + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "icvs.h" + +/* + * Helpers + */ +static inline struct icvs *notifier_to_csi(struct v4l2_async_notifier *n) +{ + return container_of(n, struct icvs, notifier); +} + +static inline struct icvs *sd_to_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct icvs, subdev); +} + +/* + * Default formats + */ +static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + +/** + * csi_set_link_cfg - Program default CSI-2 link parameters + * @ctx: CVS device context + * + * Populates a HOST_SET_MIPI_CONFIG command using current lane count and + * link frequency, then submits it to the device. + * Rest of the link parameters are left at firmware defaults. + * + * Return: 0 on success or negative errno. + */ +static int csi_set_link_cfg(struct icvs *ctx) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), + .param.conf.nr_of_lanes = ctx->nr_of_lanes, + .param.conf.link_freq = ctx->link_freq, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/* + * Streaming + */ + +/** + * cvs_csi_enable_streams - Start streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to enable (bit 0 supported) + * + * Runtime-resumes the bridge (triggering cvs_runtime_resume() to claim CSI-2 + * link ownership), fetches the link frequency, programs the MIPI configuration, + * and forwards the enable request downstream. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + s64 freq; + int ret; + + /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) { + ret = freq; + goto err_rpm_put; + } + ctx->link_freq = freq; + + if (ctx->i2c_client) { + ret = csi_set_link_cfg(ctx); + if (ret < 0) + goto err_rpm_put_sync; + } + + ret = v4l2_subdev_enable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + goto err_rpm_put_sync; + + return 0; + +err_rpm_put_sync: + /* Bypass autosuspend to immediately release ownership on error */ + pm_runtime_put_sync(dev); + return ret; +err_rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +/** + * cvs_csi_disable_streams - Stop streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to disable (bit 0 supported) + * + * Disables the remote sensor stream then drops the PM reference acquired + * during enable. After the autosuspend delay, cvs_runtime_suspend() will + * return CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + int ret; + + ret = v4l2_subdev_disable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + dev_err(dev, "disable streams failed: %d\n", ret); + + /* cvs_set_link_owner(ICVS_CSI_LINK_CVS) */ + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Pad operations / formats + */ +/** + * cvs_csi_init_state - Initialize pad formats in subdev state + * @sd: Sub-device + * @state: State container + * + * Sets all pad formats to a minimal 1x1 default. + * + * Return: 0. + */ +static int cvs_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + for (unsigned int i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = + cvs_csi_format_mbus_default; + + return 0; +} + +/** + * cvs_csi_set_fmt - Negotiate pad format + * @sd: Sub-device + * @state: State + * @format: Desired / returned format + * + * Mirrors sink format onto source pad. Accepts many media bus codes, falling + * back to Y8 if unsupported. Normalizes field setting. + * + * Return: 0. + */ +static int cvs_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *src = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SOURCE); + struct v4l2_mbus_framefmt *sink = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SINK); + + if (format->pad == ICVS_CSI_PAD_SOURCE) { /* source pad mirrors sink */ + *src = *sink; + return 0; + } + + v4l_bound_align_image(&format->format.width, 1, 65536, 0, + &format->format.height, 1, 65536, 0, 0); + + switch (format->format.code) { + /* Accept a large list; default fallback to Y8 */ + case MEDIA_BUS_FMT_RGB444_1X12: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_BGR565_2X8_BE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_GBR888_1X24: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_ARGB8888_1X32: + case MEDIA_BUS_FMT_RGB888_1X32_PADHI: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_UV8_1X8: + case MEDIA_BUS_FMT_UYVY8_1_5X8: + case MEDIA_BUS_FMT_VYUY8_1_5X8: + case MEDIA_BUS_FMT_YUYV8_1_5X8: + case MEDIA_BUS_FMT_YVYU8_1_5X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_VYUY10_2X10: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_YVYU10_2X10: + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_VYUY12_2X12: + case MEDIA_BUS_FMT_YUYV12_2X12: + case MEDIA_BUS_FMT_YVYU12_2X12: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YDYUYDYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_AYUV8_1X32: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + case MEDIA_BUS_FMT_JPEG_1X8: + case MEDIA_BUS_FMT_AHSV8888_1X32: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SRGGB16_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + + if (format->format.field == V4L2_FIELD_ANY) + format->format.field = V4L2_FIELD_NONE; + + *sink = format->format; + *src = *sink; + + return 0; +} + +/** + * cvs_csi_get_mbus_config - Provide current CSI-2 bus configuration + * @sd: Sub-device + * @pad: Pad index + * @cfg: Returned bus config + * + * Fills lane ordering and number of lanes; retrieves link frequency from + * remote entity. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct icvs *ctx = sd_to_csi(sd); + s64 freq; + + cfg->type = V4L2_MBUS_CSI2_DPHY; + for (unsigned int i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++) + cfg->bus.mipi_csi2.data_lanes[i] = i + 1; + cfg->bus.mipi_csi2.num_data_lanes = ctx->nr_of_lanes; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) + return -EINVAL; + + ctx->link_freq = freq; + cfg->link_freq = freq; + + return 0; +} + +static const struct v4l2_subdev_core_ops cvs_csi_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops cvs_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops cvs_csi_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = cvs_csi_set_fmt, + .get_mbus_config = cvs_csi_get_mbus_config, + .enable_streams = cvs_csi_enable_streams, + .disable_streams = cvs_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops cvs_csi_subdev_ops = { + .core = &cvs_csi_subdev_core_ops, + .video = &cvs_csi_video_ops, + .pad = &cvs_csi_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops cvs_csi_internal_ops = { + .init_state = cvs_csi_init_state, +}; + +static const struct media_entity_operations cvs_csi_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Async notifier + */ +/** + * cvs_csi_notify_bound - Remote sensor bound callback + * @notifier: Async notifier + * @sd: Remote subdev + * @asc: Async match connection + * + * Locates the source pad of the remote sensor and creates a media link to + * the CVS bridge sink pad enabling it by default. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) + return pad; + + ctx->remote = &sd->entity.pads[pad]; + + return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, + ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +/** + * cvs_csi_notify_unbind - Remote sensor unbind callback + * @notifier: Notifier + * @sd: Remote subdev + * @asc: Connection + */ +static void cvs_csi_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + + ctx->remote = NULL; +} + +static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = { + .bound = cvs_csi_notify_bound, + .unbind = cvs_csi_notify_unbind, +}; + +/* + * Controls + */ +/** + * cvs_csi_init_controls - Initialize V4L2 controls + * @ctx: CVS context + * + * Currently sets up a read-only privacy control placeholder. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_init_controls(struct icvs *ctx) +{ + struct v4l2_ctrl *privacy_ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + privacy_ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, NULL, + V4L2_CID_PRIVACY, 0, 1, 1, 0); + if (privacy_ctrl) + privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctx->ctrl_handler.error) { + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ctx->ctrl_handler.error; + } + + ctx->subdev.ctrl_handler = &ctx->ctrl_handler; + + return 0; +} + +/* + * Firmware (graph) parsing + */ +/** + * cvs_csi_parse_firmware - Parse firmware (ACPI graph) endpoints + * @ctx: CVS context + * + * Discovers sink and remote source endpoints, validates lane counts and + * registers an async notifier for the remote sensor. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_parse_firmware(struct icvs *ctx) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct device *dev = cvs_dev(ctx); + struct fwnode_handle *sink_ep, *source_ep; + struct v4l2_async_connection *asc; + int ret; + + sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + v4l2_async_subdev_nf_init(&ctx->notifier, &ctx->subdev); + ctx->notifier.ops = &cvs_csi_notify_ops; + + ret = v4l2_fwnode_endpoint_parse(sink_ep, &ep); + if (ret) + goto err_nf_cleanup; + + ctx->nr_of_lanes = ep.bus.mipi_csi2.num_data_lanes; + source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0); + ret = v4l2_fwnode_endpoint_parse(source_ep, &ep); + fwnode_handle_put(source_ep); + if (ret) + goto err_nf_cleanup; + + if (ctx->nr_of_lanes != ep.bus.mipi_csi2.num_data_lanes) { + ret = -EINVAL; + goto err_nf_cleanup; + } + + asc = v4l2_async_nf_add_fwnode_remote(&ctx->notifier, sink_ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err_nf_cleanup; + } + + ret = v4l2_async_nf_register(&ctx->notifier); + if (ret) + goto err_nf_cleanup; + + fwnode_handle_put(sink_ep); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&ctx->notifier); + fwnode_handle_put(sink_ep); + + return ret; +} + +/* + * Public CSI init/cleanup used by core probe/remove + */ +/** + * cvs_csi_init - Initialize CSI/V4L2 sub-device side of bridge + * @ctx: CVS context + * @dev: Parent device + * @i2c: I2C client (may be NULL for platform mode) + * + * Sets up sub-device, media entity pads, async notifier, controls and + * registers with the V4L2 framework. + * + * Return: 0 on success or negative errno. + */ +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c) +{ + int ret; + + if (i2c) { + v4l2_i2c_subdev_init(&ctx->subdev, i2c, &cvs_csi_subdev_ops); + } else { + v4l2_subdev_init(&ctx->subdev, &cvs_csi_subdev_ops); + ctx->subdev.dev = dev; + } + + ctx->subdev.internal_ops = &cvs_csi_internal_ops; + v4l2_set_subdevdata(&ctx->subdev, ctx); + ctx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + ctx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + ctx->subdev.entity.ops = &cvs_csi_entity_ops; + snprintf(ctx->subdev.name, sizeof(ctx->subdev.name), "Intel CVS"); + + ret = cvs_csi_parse_firmware(ctx); + if (ret) + return ret; + + ret = cvs_csi_init_controls(ctx); + if (ret) + goto err_nf_unreg; + + ctx->pads[ICVS_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ctx->pads[ICVS_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&ctx->subdev.entity, ICVS_CSI_NUM_PADS, + ctx->pads); + if (ret) + goto err_ctrl_cleanup; + + ctx->subdev.state_lock = ctx->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_async_register_subdev(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&ctx->subdev.entity); + +err_ctrl_cleanup: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + +err_nf_unreg: + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + + return ret; +} + +/** + * cvs_csi_remove - Cleanup CSI/V4L2 sub-device + * @ctx: CVS context + */ +void cvs_csi_remove(struct icvs *ctx) +{ + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_async_unregister_subdev(&ctx->subdev); + v4l2_subdev_cleanup(&ctx->subdev); + media_entity_cleanup(&ctx->subdev.entity); +} + +MODULE_AUTHOR("Miguel Vadillo "); +MODULE_DESCRIPTION("CSI/V4L2 support for Intel Vision Sensing Controller"); +MODULE_LICENSE("GPL"); From c6b1b34b509032c7e7cef9efc63cab55c2ad309e Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Wed, 27 May 2026 10:05:30 -0700 Subject: [PATCH 1920/5214] media: pci: intel: Add CVS support for IPU bridge driver CVS is located between IPU device and sensors and is available in existing commercial platforms from multiple OEMs. The connection information between them in firmware is not enough to build a V4L2 connection graph. This patch parses the connection properties from the SSDB buffer in DSDT and builds the connection using software nodes. From the IPU bridge point of view, CVS is just like IVSC. Signed-off-by: Miguel Vadillo Tested-by: Mehdi Djait # Dell XPS 13 9350 + IPU7 Reviewed-by: Mehdi Djait Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index fc6608e33de4..88581a4c081d 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -168,6 +168,9 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC1095" }, { "INTC100A" }, { "INTC10CF" }, + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ }; static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) @@ -221,7 +224,13 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) return csi_dev; } - return NULL; + /* Try to locate CVS device on the I2C bus */ + csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); + if (csi_dev) + return csi_dev; + + /* Fallback to platform bus for CVS device */ + return bus_find_device_by_acpi_dev(&platform_bus_type, adev); } static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, @@ -235,7 +244,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); if (!csi_dev) { acpi_dev_put(adev); - dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n"); + dev_err(ADEV_DEV(adev), "Failed to find MEI or CVS CSI dev\n"); return -ENODEV; } From 6767653b4e633d0ceaaaa734a4692306a9970573 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 19:27:05 +0530 Subject: [PATCH 1921/5214] media: ti: j721e-csi2rx: Minor cleanup of loop variables Replace open-coded `i--; for (; i >= 0; i--)` patterns with the idiomatic `while (i--)` in the error unwind paths of csi_async_notifier_complete() and ti_csi2rx_probe(). Also scope loop variables directly in the for statement instead of declaring them at the top of the function in ti_csi2rx_suspend(), ti_csi2rx_resume() and ti_csi2rx_remove(). Change the type to unsigned int in the first two to match csi->num_ctx. Signed-off-by: Rishikesh Donadkar Reviewed-by: Jai Luthra Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 21388284cbaa..ef74e2da19b6 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -485,8 +485,7 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) return 0; unregister_dev: - i--; - for (; i >= 0; i--) { + while (i--) { media_entity_remove_links(&csi->ctx[i].vdev.entity); video_unregister_device(&csi->ctx[i].vdev); } @@ -1552,7 +1551,7 @@ static int ti_csi2rx_suspend(struct device *dev) struct ti_csi2rx_ctx *ctx; struct ti_csi2rx_dma *dma; unsigned long flags = 0; - int i, ret = 0; + int ret = 0; /* If device was not in use we can simply suspend */ if (pm_runtime_status_suspended(dev)) @@ -1564,7 +1563,7 @@ static int ti_csi2rx_suspend(struct device *dev) */ writel(0, csi->shim + SHIM_CNTL); - for (i = 0; i < csi->num_ctx; i++) { + for (unsigned int i = 0; i < csi->num_ctx; i++) { ctx = &csi->ctx[i]; dma = &ctx->dma; @@ -1604,7 +1603,7 @@ static int ti_csi2rx_resume(struct device *dev) struct ti_csi2rx_buffer *buf; unsigned long flags = 0; unsigned int reg; - int i, ret = 0; + int ret = 0; /* If device was not in use, we can simply wakeup */ if (pm_runtime_status_suspended(dev)) @@ -1614,7 +1613,7 @@ static int ti_csi2rx_resume(struct device *dev) reg = SHIM_CNTL_PIX_RST; writel(reg, csi->shim + SHIM_CNTL); - for (i = 0; i < csi->num_ctx; i++) { + for (unsigned int i = 0; i < csi->num_ctx; i++) { ctx = &csi->ctx[i]; dma = &ctx->dma; spin_lock_irqsave(&dma->lock, flags); @@ -1755,8 +1754,7 @@ static int ti_csi2rx_probe(struct platform_device *pdev) err_notifier: ti_csi2rx_cleanup_notifier(csi); err_ctx: - i--; - for (; i >= 0; i--) + while (i--) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); err_dma_chan: @@ -1768,12 +1766,11 @@ static int ti_csi2rx_probe(struct platform_device *pdev) static void ti_csi2rx_remove(struct platform_device *pdev) { struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); - unsigned int i; if (!pm_runtime_status_suspended(&pdev->dev)) pm_runtime_set_suspended(&pdev->dev); - for (i = 0; i < csi->num_ctx; i++) + for (unsigned int i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); From f368ded872292b01d29d6f1e4a98746f3432c991 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Fri, 22 May 2026 09:57:34 +0800 Subject: [PATCH 1922/5214] media: ti: j721e-csi2rx: Fix error handling for media_entity_remote_source_pad_unique() The media_entity_remote_source_pad_unique() function returns an error pointer on failure, not NULL. Fix the check to use IS_ERR() and return PTR_ERR() to correctly handle allocation failures. Fixes: 982135c0eac6 ("media: ti: j721e-csi2rx: add a subdev for the core device") Fixes: 3ed9c0a1fdba ("media: ti: j721e-csi2rx: add multistream support") Signed-off-by: Chen Ni Signed-off-by: Sakari Ailus --- .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index ef74e2da19b6..4769931b1930 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -887,9 +887,9 @@ static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) /* Get the source pad connected to this ctx */ pad = media_entity_remote_source_pad_unique(ctx->pad.entity); - if (!pad) { + if (IS_ERR(pad)) { dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx); - return -ENODEV; + return PTR_ERR(pad); } state = v4l2_subdev_get_locked_active_state(&csi->subdev); @@ -1183,8 +1183,8 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, spin_unlock_irqrestore(&dma->lock, flags); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); - if (!remote_pad) - return -ENODEV; + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); sink_streams = v4l2_subdev_state_xlate_streams(state, pad, TI_CSI2RX_PAD_SINK, &streams_mask); @@ -1218,8 +1218,8 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, writel(0, csi->shim + SHIM_CNTL); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); - if (!remote_pad) - return -ENODEV; + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); sink_streams = v4l2_subdev_state_xlate_streams(state, pad, TI_CSI2RX_PAD_SINK, &streams_mask); From 804ed52335e93d8e94dc44ae7d4c31f54efbb5fa Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:07 +0200 Subject: [PATCH 1923/5214] Documentation: admin-guide: media: add rk3588 vicap Add a section that describes the Rockchip RK3588 VICAP. Reviewed-by: Mehdi Djait Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../admin-guide/media/rkcif-rk3588-vicap.dot | 29 +++++++++++++++++ Documentation/admin-guide/media/rkcif.rst | 32 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Documentation/admin-guide/media/rkcif-rk3588-vicap.dot diff --git a/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot new file mode 100644 index 000000000000..f6d3404920b5 --- /dev/null +++ b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot @@ -0,0 +1,29 @@ +digraph board { + rankdir=TB + n00000007 [label="{{ 0} | rkcif-mipi2\n/dev/v4l-subdev0 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a + n00000007:port1 -> n00000010 [style=dashed] + n00000007:port1 -> n00000016 [style=dashed] + n00000007:port1 -> n0000001c [style=dashed] + n0000000a [label="rkcif-mipi2-id0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000010 [label="rkcif-mipi2-id1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000016 [label="rkcif-mipi2-id2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001c [label="rkcif-mipi2-id3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000025 [label="{{ 0} | rkcif-mipi4\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000025:port1 -> n00000028 + n00000025:port1 -> n0000002e [style=dashed] + n00000025:port1 -> n00000034 [style=dashed] + n00000025:port1 -> n0000003a [style=dashed] + n00000028 [label="rkcif-mipi4-id0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000002e [label="rkcif-mipi4-id1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000034 [label="rkcif-mipi4-id2\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n0000003a [label="rkcif-mipi4-id3\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n00000043 [label="{{ 0} | dw-mipi-csi2rx fdd30000.csi\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000043:port1 -> n00000007:port0 + n00000048 [label="{{ 0} | dw-mipi-csi2rx fdd50000.csi\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000048:port1 -> n00000025:port0 + n0000004d [label="{{} | imx415 3-001a\n/dev/v4l-subdev4 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000004d:port0 -> n00000043:port0 + n00000051 [label="{{} | imx415 4-001a\n/dev/v4l-subdev5 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000051:port0 -> n00000048:port0 +} diff --git a/Documentation/admin-guide/media/rkcif.rst b/Documentation/admin-guide/media/rkcif.rst index 2558c121abc4..313a0ea45d16 100644 --- a/Documentation/admin-guide/media/rkcif.rst +++ b/Documentation/admin-guide/media/rkcif.rst @@ -77,3 +77,35 @@ and the following video devices: .. kernel-figure:: rkcif-rk3568-vicap.dot :alt: Topology of the RK3568 Video Capture (VICAP) unit :align: center + +Rockchip RK3588 Video Capture (VICAP) +------------------------------------- + +The RK3588 Video Capture (VICAP) unit features a digital video port and six +MIPI CSI-2 capture interfaces that can receive video data independently. +The DVP accepts parallel video data, BT.656 and BT.1120. +Since the BT.1120 protocol may feature more than one stream, the RK3588 VICAP +DVP features four DMA engines that can capture different streams. +Similarly, the RK3588 VICAP MIPI CSI-2 receivers feature four DMA engines each +to handle different Virtual Channels (VCs). + +The rkcif driver represents this hardware variant by exposing the following +V4L2 subdevices: + +* dw-mipi-csi2rx fdd30000.csi: MIPI CSI-2 receiver connected to MIPI DPHY0 +* dw-mipi-csi2rx fdd50000.csi: MIPI CSI-2 receiver connected to MIPI DPHY1 +* rkcif-mipi2: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY0 +* rkcif-mipi4: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY1 + +and the following video devices: + +* rkcif-mipi2-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi2 + INTERFACE/CROP block. +* rkcif-mipi4-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi4 + INTERFACE/CROP block. + +.. kernel-figure:: rkcif-rk3588-vicap.dot + :alt: Topology of the RK3588 Video Capture (VICAP) unit + :align: center From 4a6b7bf1de948251ca5f09ca1a632f036e52085c Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:08 +0200 Subject: [PATCH 1924/5214] media: dt-bindings: add rockchip rk3588 vicap Add documentation for the Rockchip RK3588 Video Capture (VICAP) unit. To that end, make the existing rockchip,rk3568-vicap documentation more general and introduce variant specific constraints. Acked-by: Conor Dooley Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../bindings/media/rockchip,rk3568-vicap.yaml | 173 ++++++++++++++++-- 1 file changed, 154 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml index 18cd0a5a5318..080b64503b1b 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml @@ -15,9 +15,15 @@ description: the data from camera sensors, video decoders, or other companion ICs and transfers it into system main memory by AXI bus. + The Rockchip RK3588 Video Capture (VICAP) is similar to its RK3568 + counterpart, but features six MIPI CSI-2 ports and additional connections + to the image signal processor (ISP) blocks. + properties: compatible: - const: rockchip,rk3568-vicap + enum: + - rockchip,rk3568-vicap + - rockchip,rk3588-vicap reg: maxItems: 1 @@ -26,11 +32,8 @@ properties: maxItems: 1 clocks: - items: - - description: ACLK - - description: HCLK - - description: DCLK - - description: ICLK + minItems: 4 + maxItems: 5 clock-names: items: @@ -38,25 +41,19 @@ properties: - const: hclk - const: dclk - const: iclk + - const: iclk1 + minItems: 4 iommus: maxItems: 1 resets: - items: - - description: ARST - - description: HRST - - description: DRST - - description: PRST - - description: IRST + minItems: 5 + maxItems: 9 reset-names: - items: - - const: arst - - const: hrst - - const: drst - - const: prst - - const: irst + minItems: 5 + maxItems: 9 rockchip,grf: $ref: /schemas/types.yaml#/definitions/phandle @@ -67,8 +64,15 @@ properties: ports: $ref: /schemas/graph.yaml#/properties/ports + additionalProperties: false properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + port@0: $ref: /schemas/graph.yaml#/$defs/port-base unevaluatedProperties: false @@ -100,13 +104,75 @@ properties: port@1: $ref: /schemas/graph.yaml#/properties/port - description: Port connected to the MIPI CSI-2 receiver output. + description: Port connected to the MIPI CSI-2 receiver 0 output. properties: endpoint: $ref: video-interfaces.yaml# unevaluatedProperties: false + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 1 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 2 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 3 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@5: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 4 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@6: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 5 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@10: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP0 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@11: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP1 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false required: - compatible - reg @@ -114,6 +180,75 @@ required: - clocks - ports +allOf: + - if: + properties: + compatible: + contains: + const: rockchip,rk3568-vicap + then: + properties: + clocks: + maxItems: 4 + + clock-names: + maxItems: 4 + + resets: + maxItems: 5 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: prst + - const: irst + + ports: + properties: + port@2: false + + port@3: false + + port@4: false + + port@5: false + + port@6: false + + port@10: false + + port@11: false + + - if: + properties: + compatible: + contains: + const: rockchip,rk3588-vicap + then: + properties: + clocks: + minItems: 5 + + clock-names: + minItems: 5 + + resets: + minItems: 9 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: irst0 + - const: irst1 + - const: irst2 + - const: irst3 + - const: irst4 + - const: irst5 + additionalProperties: false examples: From 2175323fdf820ebf2d861283d1de7ef394b048c5 Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:09 +0200 Subject: [PATCH 1925/5214] media: rockchip: rkcif: add support for rk3588 vicap mipi capture The RK3588 Video Capture (VICAP) unit features a Digital Video Port (DVP) and six MIPI CSI-2 capture interfaces. Add initial support for this variant to the rkcif driver and enable the MIPI CSI-2 capture interfaces. Reviewed-by: Mehdi Djait Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../rockchip/rkcif/rkcif-capture-mipi.c | 148 +++++++++++++++++- .../rockchip/rkcif/rkcif-capture-mipi.h | 1 + .../platform/rockchip/rkcif/rkcif-common.h | 2 +- .../media/platform/rockchip/rkcif/rkcif-dev.c | 18 +++ 4 files changed, 163 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c index 9e67160a16e4..bc9518f8db50 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c @@ -30,6 +30,14 @@ #define RK3568_MIPI_CTRL0_CROP_EN BIT(5) #define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1) +#define RK3588_MIPI_CTRL0_DMA_EN BIT(28) +#define RK3588_MIPI_CTRL0_HIGH_ALIGN BIT(27) +#define RK3588_MIPI_CTRL0_WRDDR(type) ((type) << 5) +#define RK3588_MIPI_CTRL0_CROP_EN BIT(4) +#define RK3588_MIPI_CTRL0_PARSE(type) ((type) << 1) + +#define RK3588_MIPI_CTRL_CAP_EN BIT(0) + #define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10) #define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8) #define RKCIF_MIPI_CTRL0_CAP_EN BIT(0) @@ -375,11 +383,8 @@ static u32 rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream, const struct rkcif_output_fmt *active_out_fmt) { - u32 ctrl0 = 0; - - ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); - ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; - ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN; + u32 ctrl0 = RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt) | + RKCIF_MIPI_CTRL0_CAP_EN | RK3568_MIPI_CTRL0_CROP_EN; if (active_out_fmt->mipi.compact) ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN; @@ -481,6 +486,132 @@ const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = { }, }; +static u32 +rkcif_rk3588_mipi_ctrl0(struct rkcif_stream *stream, + const struct rkcif_output_fmt *active_out_fmt) +{ + u32 ctrl0 = 0; + + ctrl0 |= RK3588_MIPI_CTRL0_DMA_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); + ctrl0 |= RK3588_MIPI_CTRL0_CROP_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; + + switch (active_out_fmt->mipi.type) { + case RKCIF_MIPI_TYPE_RAW8: + break; + case RKCIF_MIPI_TYPE_RAW10: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x1); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RAW12: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x2); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RGB888: + break; + case RKCIF_MIPI_TYPE_YUV422SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x4); + break; + case RKCIF_MIPI_TYPE_YUV420SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x5); + break; + case RKCIF_MIPI_TYPE_YUV400: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x3); + break; + default: + break; + } + + return ctrl0; +} + +const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data = { + .mipi_num = 6, + .mipi_ctrl0 = rkcif_rk3588_mipi_ctrl0, + .regs = { + [RKCIF_MIPI_CTRL] = 0x20, + [RKCIF_MIPI_INTEN] = 0x74, + [RKCIF_MIPI_INTSTAT] = 0x78, + }, + .regs_id = { + [RKCIF_ID0] = { + [RKCIF_MIPI_CTRL0] = 0x00, + [RKCIF_MIPI_CTRL1] = 0x04, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x8c, + }, + [RKCIF_ID1] = { + [RKCIF_MIPI_CTRL0] = 0x08, + [RKCIF_MIPI_CTRL1] = 0x0c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x38, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x40, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x48, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x3c, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x44, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x90, + }, + [RKCIF_ID2] = { + [RKCIF_MIPI_CTRL0] = 0x10, + [RKCIF_MIPI_CTRL1] = 0x14, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x4c, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x54, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x5c, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x50, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x58, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x94, + }, + [RKCIF_ID3] = { + [RKCIF_MIPI_CTRL0] = 0x18, + [RKCIF_MIPI_CTRL1] = 0x1c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x60, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x68, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x70, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x64, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x6c, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x98, + }, + }, + .blocks = { + { + .offset = 0x100, + }, + { + .offset = 0x200, + }, + { + .offset = 0x300, + }, + { + .offset = 0x400, + }, + { + .offset = 0x500, + }, + { + .offset = 0x600, + }, + }, +}; + static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface, unsigned int index) { @@ -631,6 +762,13 @@ static int rkcif_mipi_start_streaming(struct rkcif_stream *stream) rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1); rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0); + /* + * TODO: This bit has a different meaning on the RK3568, but it is + * set there by default anyway. While correct, this is not exactly + * nice and shall be reworked during the next refactoring. + */ + rkcif_mipi_write(interface, RKCIF_MIPI_CTRL, RK3588_MIPI_CTRL_CAP_EN); + ret = 0; out: diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h index 7f16eadc474c..7edaca44f653 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h @@ -13,6 +13,7 @@ #include "rkcif-common.h" extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data; +extern const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data; int rkcif_mipi_register(struct rkcif_device *rkcif); diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h index dd92cfbc879f..4d9211ba9bda 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h @@ -27,7 +27,7 @@ #include "rkcif-regs.h" #define RKCIF_DRIVER_NAME "rockchip-cif" -#define RKCIF_CLK_MAX 4 +#define RKCIF_CLK_MAX 5 enum rkcif_format_type { RKCIF_FMT_TYPE_INVALID, diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c index b4cf1146f131..be3a174b9aab 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c @@ -53,6 +53,20 @@ static const struct rkcif_match_data rk3568_vicap_match_data = { .mipi = &rkcif_rk3568_vicap_mipi_match_data, }; +static const char *const rk3588_vicap_clks[] = { + "aclk", + "hclk", + "dclk", + "iclk", + "iclk1", +}; + +static const struct rkcif_match_data rk3588_vicap_match_data = { + .clks = rk3588_vicap_clks, + .clks_num = ARRAY_SIZE(rk3588_vicap_clks), + .mipi = &rkcif_rk3588_vicap_mipi_match_data, +}; + static const struct of_device_id rkcif_plat_of_match[] = { { .compatible = "rockchip,px30-vip", @@ -62,6 +76,10 @@ static const struct of_device_id rkcif_plat_of_match[] = { .compatible = "rockchip,rk3568-vicap", .data = &rk3568_vicap_match_data, }, + { + .compatible = "rockchip,rk3588-vicap", + .data = &rk3588_vicap_match_data, + }, {} }; MODULE_DEVICE_TABLE(of, rkcif_plat_of_match); From bdf5eb306c4d07a3bfdd4e2ab8f2acc7ff824940 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:12 -0800 Subject: [PATCH 1926/5214] x86/topology: Add missing struct declaration and attribute dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prototypes for get_topology_cpu_type_name() and get_topology_cpu_type() take a pointer to struct cpuinfo_x86, but asm/topology.h neither includes nor forward-declares the structure. Including asm/topology.h, directly or indirectly, without including asm/processor.h triggers a warning: ./arch/x86/include/asm/topology.h:159:47: error: ‘struct cpuinfo_x86’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror] 159 | const char *get_topology_cpu_type_name(struct cpuinfo_x86 *c); | ^~~~~~~~~~~ Since only a pointer is needed, add a forward declaration of struct cpuinfo_x86. Additionally, sysctl_sched_itmt_enabled is declared in asm/topology.h with the __read_mostly attribute, but the header does not include linux/cache.h. This causes a build failure when including asm/topology.h but not linux/ cache.h: ./arch/x86/include/asm/topology.h:264:27: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘sysctl_sched_itmt_enabled’ 264 | extern bool __read_mostly sysctl_sched_itmt_enabled; | ^~~~~~~~~~~~~~~~~~~~~~~~~ Include the required header. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511181954.UMxCeTV1-lkp@intel.com/ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511190008.AA0NTn3G-lkp@intel.com/ Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 0ba9bdb99871..8fb61d2465eb 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -157,6 +157,8 @@ extern unsigned int __num_threads_per_package; extern unsigned int __num_cores_per_package; extern unsigned int __num_nodes_per_package; +struct cpuinfo_x86; + const char *get_topology_cpu_type_name(struct cpuinfo_x86 *c); enum x86_topology_cpu_type get_topology_cpu_type(struct cpuinfo_x86 *c); @@ -265,6 +267,7 @@ extern bool x86_topology_update; #ifdef CONFIG_SCHED_MC_PRIO #include +#include DECLARE_PER_CPU_READ_MOSTLY(int, sched_core_priority); extern bool __read_mostly sysctl_sched_itmt_enabled; From a746607df24b4b01fef380390c0b499b84fc7863 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:13 -0800 Subject: [PATCH 1927/5214] x86/acpi: Add functions to setup and access the wakeup mailbox Systems that describe hardware using DeviceTree graphs may enumerate and implement the wakeup mailbox as defined in the ACPI specification but do not otherwise depend on ACPI. Expose functions to setup and access the location of the wakeup mailbox from outside ACPI code. The function acpi_setup_mp_wakeup_mailbox() stores the physical address of the mailbox and updates the wakeup_secondary_cpu_64() APIC callback. The function acpi_madt_multiproc_wakeup_mailbox() returns a pointer to the mailbox. Acked-by: Rafael J. Wysocki (Intel) Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/acpi.h | 10 ++++++++++ arch/x86/kernel/acpi/madt_wakeup.c | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index a03aa6f999d1..820df375df79 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -182,6 +182,9 @@ void __iomem *x86_acpi_os_ioremap(acpi_physical_address phys, acpi_size size); #define acpi_os_ioremap acpi_os_ioremap #endif +void acpi_setup_mp_wakeup_mailbox(u64 addr); +struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void); + #else /* !CONFIG_ACPI */ #define acpi_lapic 0 @@ -200,6 +203,13 @@ static inline u64 x86_default_get_root_pointer(void) return 0; } +static inline void acpi_setup_mp_wakeup_mailbox(u64 addr) { } + +static inline struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) +{ + return NULL; +} + #endif /* !CONFIG_ACPI */ #define ARCH_HAS_POWER_INIT 1 diff --git a/arch/x86/kernel/acpi/madt_wakeup.c b/arch/x86/kernel/acpi/madt_wakeup.c index 6d7603511f52..82caf44b45e3 100644 --- a/arch/x86/kernel/acpi/madt_wakeup.c +++ b/arch/x86/kernel/acpi/madt_wakeup.c @@ -247,3 +247,14 @@ int __init acpi_parse_mp_wake(union acpi_subtable_headers *header, return 0; } + +void __init acpi_setup_mp_wakeup_mailbox(u64 mailbox_paddr) +{ + acpi_mp_wake_mailbox_paddr = mailbox_paddr; + apic_update_callback(wakeup_secondary_cpu_64, acpi_wakeup_cpu); +} + +struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) +{ + return acpi_mp_wake_mailbox; +} From b7c8992aea5e0aca6f5c3d1e57ed568eeddfe9a0 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:14 -0800 Subject: [PATCH 1928/5214] dt-bindings: reserved-memory: Wakeup Mailbox for Intel processors Add DeviceTree bindings to enumerate the wakeup mailbox used in platform firmware for Intel processors. x86 platforms commonly boot secondary CPUs using an INIT assert, de-assert followed by Start-Up IPI messages. The wakeup mailbox can be used when this mechanism is unavailable. The wakeup mailbox offers more control to the operating system to boot secondary CPUs than a spin-table. It allows the reuse of the same wakeup vector for all CPUs while maintaining control over which CPUs to boot and when. While it is possible to achieve the same level of control using a spin-table, it would require specifying a separate `cpu-release-addr` for each secondary CPU. The operation and structure of the mailbox are described in the Multiprocessor Wakeup Structure defined in the ACPI specification. Note that this structure does not specify how to publish the mailbox to the operating system (ACPI-based platform firmware uses a separate table). No ACPI table is needed in DeviceTree-based firmware to enumerate the mailbox. Nodes that want to refer to the reserved memory usually define a `memory-region` property. /cpus/cpu* nodes would want to refer to the mailbox, but they do not have such property defined in the DeviceTree specification. Moreover, it would imply that there is a memory region per CPU. Instead, add a `compatible` property that the operating system can use to discover the mailbox. Reviewed-by: Dexuan Cui Reviewed-by: Rob Herring (Arm) Acked-by: Rafael J. Wysocki (Intel) Co-developed-by: Yunhong Jiang Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- .../reserved-memory/intel,wakeup-mailbox.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml diff --git a/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml b/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml new file mode 100644 index 000000000000..4362bc058df4 --- /dev/null +++ b/Documentation/devicetree/bindings/reserved-memory/intel,wakeup-mailbox.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reserved-memory/intel,wakeup-mailbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wakeup Mailbox for Intel processors + +description: | + The Wakeup Mailbox provides a mechanism for the operating system to wake up + secondary CPUs on Intel processors. It is an alternative to the INIT-!INIT- + SIPI sequence used on most x86 systems. + + The structure and operation of the mailbox is described in the Multiprocessor + Wakeup Structure of the ACPI specification version 6.6 section 5.2.12.19 [1]. + + The implementation of the mailbox in platform firmware is described in the + Intel TDX Virtual Firmware Design Guide section 4.3.5 [2]. + + 1: https://uefi.org/specs/ACPI/6.6/05_ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure + 2: https://www.intel.com/content/www/us/en/content-details/733585/intel-tdx-virtual-firmware-design-guide.html + +maintainers: + - Ricardo Neri + +allOf: + - $ref: reserved-memory.yaml + +properties: + compatible: + const: intel,wakeup-mailbox + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + + wakeup-mailbox@ffff0000 { + compatible = "intel,wakeup-mailbox"; + reg = <0x0 0xffff0000 0x1000>; + }; + }; From 12d58799c19572b98ec84c12ec34a228fe3e8c5c Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:15 -0800 Subject: [PATCH 1929/5214] x86/dt: Parse the Wakeup Mailbox for Intel processors The Wakeup Mailbox is a mechanism to boot secondary CPUs on systems that do not want or cannot use the INIT + StartUp IPI messages. The platform firmware is expected to implement the mailbox as described in the Multiprocessor Wakeup Structure of the ACPI specification. It is also expected to publish the mailbox to the operating system as described in the corresponding DeviceTree schema that accompanies the documentation of the Linux kernel. Reuse the existing functionality to set the memory location of the mailbox and update the wakeup_secondary_cpu_64() APIC callback. Make this functionality available to DeviceTree-based systems by making CONFIG_X86_ MAILBOX_WAKEUP depend on either CONFIG_OF or CONFIG_ACPI_MADT_WAKEUP. do_boot_cpu() uses wakeup_secondary_cpu_64() when set. It will be set if a wakeup mailbox is enumerated via an ACPI table or a DeviceTree node. For cases in which this behavior is not desired, this APIC callback can be updated later during boot using platform-specific hooks. Reviewed-by: Dexuan Cui Co-developed-by: Yunhong Jiang Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/kernel/devicetree.c | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index dd8748c45529..318acaecb5ca 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -125,6 +126,51 @@ static void __init dtb_setup_hpet(void) #endif } +#if defined(CONFIG_X86_64) && defined(CONFIG_SMP) + +#define WAKEUP_MAILBOX_SIZE 0x1000 +#define WAKEUP_MAILBOX_ALIGN 0x1000 + +/** dtb_wakeup_mailbox_setup() - Parse the wakeup mailbox from the device tree + * + * Look for the presence of a wakeup mailbox in the DeviceTree. The mailbox is + * expected to follow the structure and operation described in the Multiprocessor + * Wakeup Structure of the ACPI specification. + */ +static void __init dtb_wakeup_mailbox_setup(void) +{ + struct device_node *node; + struct resource res; + + node = of_find_compatible_node(NULL, NULL, "intel,wakeup-mailbox"); + if (!node) + return; + + if (of_address_to_resource(node, 0, &res)) + goto done; + + /* The mailbox is a 4KB-aligned region.*/ + if (res.start & (WAKEUP_MAILBOX_ALIGN - 1)) + goto done; + + /* The mailbox has a size of 4KB. */ + if (res.end - res.start + 1 != WAKEUP_MAILBOX_SIZE) + goto done; + + /* Not supported when the mailbox is used. */ + cpu_hotplug_disable_offlining(); + + acpi_setup_mp_wakeup_mailbox(res.start); +done: + of_node_put(node); +} +#else /* !CONFIG_X86_64 || !CONFIG_SMP */ +static inline int dtb_wakeup_mailbox_setup(void) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_X86_64 && CONFIG_SMP */ + #ifdef CONFIG_X86_LOCAL_APIC static void __init dtb_cpu_setup(void) @@ -287,6 +333,7 @@ static void __init x86_dtb_parse_smp_config(void) dtb_setup_hpet(); dtb_apic_setup(); + dtb_wakeup_mailbox_setup(); } void __init x86_flattree_get_config(void) From 7e4c083ece42ed6c90e4071772847db595081e0e Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:16 -0800 Subject: [PATCH 1930/5214] x86/hyperv/vtl: Set real_mode_header in hv_vtl_init_platform() Hyper-V VTL clears x86_platform.realmode_{init(), reserve()} in hv_vtl_init_platform() whereas it sets real_mode_header later in hv_vtl_early_init(). There is no need to deal with the settings of real mode memory in two places. Also, both functions are called much earlier than x86_platform.realmode_init() (via an early_initcall), where the real_mode_header is needed. Set real_mode_header in hv_vtl_init_platform() to keep all code dealing with memory for the real mode trampoline in one place. Besides making the code more readable, it prepares it for a subsequent changeset in which the behavior needs to change to support Hyper-V VTL guests in a TDX environment. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Suggested-by: Thomas Gleixner Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 9b6a9bc4ab76..e045bf3a2c59 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -70,6 +70,7 @@ void __init hv_vtl_init_platform(void) x86_platform.realmode_reserve = x86_init_noop; x86_platform.realmode_init = x86_init_noop; + real_mode_header = &hv_vtl_real_mode_header; x86_init.irqs.pre_vector_init = x86_init_noop; x86_init.timers.timer_init = x86_init_noop; x86_init.resources.probe_roms = x86_init_noop; @@ -251,7 +252,6 @@ int __init hv_vtl_early_init(void) panic("XSAVE has to be disabled as it is not supported by this module.\n" "Please add 'noxsave' to the kernel command line.\n"); - real_mode_header = &hv_vtl_real_mode_header; apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); return 0; From a7ac1ea1f06314f6690ac772e9a31d6e2db977fa Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:17 -0800 Subject: [PATCH 1931/5214] x86/realmode: Make the location of the trampoline configurable x86 CPUs boot in real mode. This mode uses a 1MB address space. The trampoline must reside below this 1MB memory boundary. There are platforms in which the firmware boots the secondary CPUs, switches them to long mode and transfers control to the kernel. An example of such a mechanism is the ACPI Multiprocessor Wakeup Structure. In this scenario there is no restriction on locating the trampoline under 1MB memory. Moreover, certain platforms (for example, Hyper-V VTL guests) may not have memory available for allocation below 1MB. Add a new member to struct x86_init_resources to specify the upper bound for the location of the trampoline memory. Preserve the default upper bound of 1MB to conserve the current behavior. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Originally-by: Thomas Gleixner Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/x86_init.h | 3 +++ arch/x86/kernel/x86_init.c | 3 +++ arch/x86/realmode/init.c | 7 +++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 6c8a6ead84f6..953d3199408a 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -31,12 +31,15 @@ struct x86_init_mpparse { * platform * @memory_setup: platform specific memory setup * @dmi_setup: platform specific DMI setup + * @realmode_limit: platform specific address limit for the real mode trampoline + * (default 1M) */ struct x86_init_resources { void (*probe_roms)(void); void (*reserve_resources)(void); char *(*memory_setup)(void); void (*dmi_setup)(void); + unsigned long realmode_limit; }; /** diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index ebefb77c37bb..252c5827d063 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,8 @@ struct x86_init_ops x86_init __initdata = { .reserve_resources = reserve_standard_io_resources, .memory_setup = e820__memory_setup_default, .dmi_setup = dmi_setup, + /* Has to be under 1M so we can execute real-mode AP code. */ + .realmode_limit = SZ_1M, }, .mpparse = { diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c index 88be32026768..694d80a5c68e 100644 --- a/arch/x86/realmode/init.c +++ b/arch/x86/realmode/init.c @@ -46,7 +46,7 @@ void load_trampoline_pgtable(void) void __init reserve_real_mode(void) { - phys_addr_t mem; + phys_addr_t mem, limit = x86_init.resources.realmode_limit; size_t size = real_mode_size_needed(); if (!size) @@ -54,10 +54,9 @@ void __init reserve_real_mode(void) WARN_ON(slab_is_available()); - /* Has to be under 1M so we can execute real-mode AP code. */ - mem = memblock_phys_alloc_range(size, PAGE_SIZE, 0, 1<<20); + mem = memblock_phys_alloc_range(size, PAGE_SIZE, 0, limit); if (!mem) - pr_info("No sub-1M memory is available for the trampoline\n"); + pr_info("No memory below %pa for the real-mode trampoline\n", &limit); else set_real_mode_mem(mem); From aa7719039bd9e378c8c134b8ce5fa5f4e13cda73 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:18 -0800 Subject: [PATCH 1932/5214] x86/hyperv/vtl: Setup the 64-bit trampoline for TDX guests The hypervisor is an untrusted entity for TDX guests. It cannot be used to boot secondary CPUs - neither via hypercalls nor the INIT assert, de-assert, plus Start-Up IPI messages. Instead, the platform virtual firmware boots the secondary CPUs and puts them in a state to transfer control to the kernel. This mechanism uses the wakeup mailbox described in the Multiprocessor Wakeup Structure of the ACPI specification. The entry point to the kernel is trampoline_start64. Allocate and setup the trampoline using the default x86_platform callbacks. The platform firmware configures the secondary CPUs in long mode. It is no longer necessary to locate the trampoline under 1MB memory. After handoff from firmware, the trampoline code switches briefly to 32-bit addressing mode, which has an addressing limit of 4GB. Set the upper bound of the trampoline memory accordingly. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index e045bf3a2c59..b94fffa67312 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -68,9 +68,14 @@ void __init hv_vtl_init_platform(void) */ pr_info("Linux runs in Hyper-V Virtual Trust Level %d\n", ms_hyperv.vtl); - x86_platform.realmode_reserve = x86_init_noop; - x86_platform.realmode_init = x86_init_noop; - real_mode_header = &hv_vtl_real_mode_header; + /* There is no paravisor present if we are here. */ + if (hv_isolation_type_tdx()) { + x86_init.resources.realmode_limit = SZ_4G; + } else { + x86_platform.realmode_reserve = x86_init_noop; + x86_platform.realmode_init = x86_init_noop; + real_mode_header = &hv_vtl_real_mode_header; + } x86_init.irqs.pre_vector_init = x86_init_noop; x86_init.timers.timer_init = x86_init_noop; x86_init.resources.probe_roms = x86_init_noop; From 12584a89c9172ed5c2718612314a2d6f87260896 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:19 -0800 Subject: [PATCH 1933/5214] x86/acpi: Add a helper to get the address of the wakeup mailbox A Hyper-V VTL level 2 guest in a TDX environment needs to map the physical page of the ACPI Multiprocessor Wakeup Structure as private (encrypted). It needs to know the physical address of this structure. Add a helper function to retrieve the address. Suggested-by: Michael Kelley Acked-by: Rafael J. Wysocki (Intel) Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/include/asm/acpi.h | 6 ++++++ arch/x86/kernel/acpi/madt_wakeup.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 820df375df79..c4e6459bd56b 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -184,6 +184,7 @@ void __iomem *x86_acpi_os_ioremap(acpi_physical_address phys, acpi_size size); void acpi_setup_mp_wakeup_mailbox(u64 addr); struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void); +u64 acpi_get_mp_wakeup_mailbox_paddr(void); #else /* !CONFIG_ACPI */ @@ -210,6 +211,11 @@ static inline struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mail return NULL; } +static inline u64 acpi_get_mp_wakeup_mailbox_paddr(void) +{ + return 0; +} + #endif /* !CONFIG_ACPI */ #define ARCH_HAS_POWER_INIT 1 diff --git a/arch/x86/kernel/acpi/madt_wakeup.c b/arch/x86/kernel/acpi/madt_wakeup.c index 82caf44b45e3..48734e4a6e8f 100644 --- a/arch/x86/kernel/acpi/madt_wakeup.c +++ b/arch/x86/kernel/acpi/madt_wakeup.c @@ -258,3 +258,8 @@ struct acpi_madt_multiproc_wakeup_mailbox *acpi_get_mp_wakeup_mailbox(void) { return acpi_mp_wake_mailbox; } + +u64 acpi_get_mp_wakeup_mailbox_paddr(void) +{ + return acpi_mp_wake_mailbox_paddr; +} From 80200341197a799fe39da4e9da61bde4d23ec7ec Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 4 Mar 2026 15:41:20 -0800 Subject: [PATCH 1934/5214] x86/hyperv/vtl: Mark the wakeup mailbox page as private The current code maps MMIO devices as shared (decrypted) by default in a confidential computing VM. In a TDX environment, secondary CPUs are booted using the Multiprocessor Wakeup Structure defined in the ACPI specification. The virtual firmware and the operating system function in the guest context, without intervention from the VMM. Map the physical memory of the mailbox as private. Use the is_private_mmio() callback. Signed-off-by: Yunhong Jiang Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index b94fffa67312..1e2f5b3ea772 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -6,6 +6,9 @@ * Saurabh Sengar */ +#include + +#include #include #include #include @@ -59,6 +62,18 @@ static void __noreturn hv_vtl_restart(char __maybe_unused *cmd) hv_vtl_emergency_restart(); } +static inline bool within_page(u64 addr, u64 start) +{ + return addr >= start && addr < (start + PAGE_SIZE); +} + +static bool hv_vtl_is_private_mmio_tdx(u64 addr) +{ + u64 mb_addr = acpi_get_mp_wakeup_mailbox_paddr(); + + return mb_addr && within_page(addr, mb_addr); +} + void __init hv_vtl_init_platform(void) { /* @@ -71,6 +86,8 @@ void __init hv_vtl_init_platform(void) /* There is no paravisor present if we are here. */ if (hv_isolation_type_tdx()) { x86_init.resources.realmode_limit = SZ_4G; + x86_platform.hyper.is_private_mmio = hv_vtl_is_private_mmio_tdx; + } else { x86_platform.realmode_reserve = x86_init_noop; x86_platform.realmode_init = x86_init_noop; From 7a035678fc2bdee81881170764ef08a91a076147 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Wed, 4 Mar 2026 15:41:21 -0800 Subject: [PATCH 1935/5214] x86/hyperv/vtl: Use the wakeup mailbox to boot secondary CPUs The hypervisor is an untrusted entity for TDX guests. It cannot be used to boot secondary CPUs. The function hv_vtl_wakeup_secondary_cpu() cannot be used. Instead, the virtual firmware boots the secondary CPUs and places them in a state to transfer control to the kernel using the wakeup mailbox. The firmware enumerates the mailbox via either an ACPI table or a DeviceTree node. If the wakeup mailbox is present, the kernel updates the APIC callback wakeup_secondary_cpu_64() to use it. Reviewed-by: Dexuan Cui Reviewed-by: Michael Kelley Signed-off-by: Ricardo Neri Signed-off-by: Dexuan Cui --- arch/x86/hyperv/hv_vtl.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 1e2f5b3ea772..07fac3d687c3 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -274,7 +274,15 @@ int __init hv_vtl_early_init(void) panic("XSAVE has to be disabled as it is not supported by this module.\n" "Please add 'noxsave' to the kernel command line.\n"); - apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); + /* + * TDX confidential VMs do not trust the hypervisor and cannot use it to + * boot secondary CPUs. Instead, they will be booted using the wakeup + * mailbox if detected during boot. See setup_arch(). + * + * There is no paravisor present if we are here. + */ + if (!hv_isolation_type_tdx()) + apic_update_callback(wakeup_secondary_cpu_64, hv_vtl_wakeup_secondary_cpu); return 0; } From 1f9d9b62cfd54fb06df9b5f2f2868324bee3aab4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 27 May 2026 13:19:33 -0300 Subject: [PATCH 1936/5214] perf tests hwmon_pmu: Use PRIu64 + (uint64_t) cast for a __u64 field to work more widely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While testing perf with an updated Debian experimental cross compiler (gcc version 14.2.0 (Debian 14.2.0-13)) this started failing: In file included from tests/hwmon_pmu.c:12: tests/hwmon_pmu.c: In function 'do_test': tests/hwmon_pmu.c:199:34: error: format '%lld' expects argument of type 'long long int', but argument 7 has type '__u64' {aka 'long unsigned int'} [-Werror=format=] 199 | pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /git/perf-7.1.0-rc5/tools/perf/util/debug.h:20:21: note: in definition of macro 'pr_fmt' 20 | #define pr_fmt(fmt) fmt | ^~~ tests/hwmon_pmu.c:199:25: note: in expansion of macro 'pr_debug' 199 | pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", | ^~~~~~~~ tests/hwmon_pmu.c:199:79: note: format string is defined here 199 | pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", | ~~~^ | | | long long int | %ld LD /tmp/build/perf/util/intel-pt-decoder/perf-util-in.o The usual make that %lld a PRIu64 (since arg7 is evsel->core.attr.config, which is a __u64) but then on Fedora 44 (gcc version 16.1.1 20260515 (Red Hat 16.1.1-2)) it ends up with: In file included from tests/hwmon_pmu.c:13: tests/hwmon_pmu.c: In function ‘do_test’: tests/hwmon_pmu.c:200:34: error: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 7 has type ‘__u64’ {aka ‘long long unsigned int’} [-Werror=format=] 200 | pr_debug("FAILED %s:%d Unexpected config for '%s', %" PRIu64 " != %ld\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/acme/git/perf-tools-next2/tools/perf/util/debug.h:20:21: note: in definition of macro ‘pr_fmt’ 20 | #define pr_fmt(fmt) fmt | ^~~ tests/hwmon_pmu.c:200:25: note: in expansion of macro ‘pr_debug’ 200 | pr_debug("FAILED %s:%d Unexpected config for '%s', %" PRIu64 " != %ld\n", | ^~~~~~~~ cc1: all warnings being treated as errors So the way to satisfy both compilers is to also add a (u64) cast to arg7. Cc: Adrian Hunter Cc: Ian Rogers Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hwmon_pmu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c index ada6e445c4c4..62e0841a6c31 100644 --- a/tools/perf/tests/hwmon_pmu.c +++ b/tools/perf/tests/hwmon_pmu.c @@ -2,6 +2,7 @@ #include "hwmon_pmu.h" #include +#include #include #include @@ -196,9 +197,9 @@ static int do_test(size_t i, bool with_pmu, bool with_alias) continue; if (evsel->core.attr.config != (u64)test_events[i].key.type_and_num) { - pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n", + pr_debug("FAILED %s:%d Unexpected config for '%s', %" PRIu64 " != %ld\n", __FILE__, __LINE__, str, - evsel->core.attr.config, + (uint64_t)evsel->core.attr.config, test_events[i].key.type_and_num); ret = TEST_FAIL; goto out; From 9ac747ebc893a9c58099e60de8cd82cbeefede99 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Sun, 19 Apr 2026 19:25:52 -0700 Subject: [PATCH 1937/5214] dt-bindings: spmi: glymur-spmi-pmic-arb: Add compatible for Qualcomm Hawi SoC The PMIC arbiter in the Qualcomm Hawi SoC is version v8.5, which introduces parity and CRC checks for data received from the PMIC, as well as NACK checks for command sequences except for read. All other features in PMIC arbiter remain the same as the one in the Qualcomm Glymur SoC, with the only differences being some additional error status checks. Therefore, add a string for "qcom,hawi-spmi-pmic-arb" as a compatible entry for "qcom,glymur-spmi-pmic-arb". Signed-off-by: Fenglin Wu Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- .../devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml index 3b5005b96c6d..1593a1183a36 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml +++ b/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml @@ -25,6 +25,7 @@ properties: oneOf: - items: - enum: + - qcom,hawi-spmi-pmic-arb - qcom,kaanapali-spmi-pmic-arb - const: qcom,glymur-spmi-pmic-arb - enum: From e08ee0e3b521aeffefcb0438a48e17e81af66eae Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Sun, 19 Apr 2026 19:25:53 -0700 Subject: [PATCH 1938/5214] spmi: spmi-pmic-arb: add support for PMIC arbiter v8.5 PMIC arbiter v8.5 is an extension of PMIC arbiter v8 that updated the definition of the channel status register bit fields. Add support to handle this difference. Signed-off-by: Fenglin Wu Reviewed-by: Dmitry Baryshkov Signed-off-by: Stephen Boyd --- drivers/spmi/spmi-pmic-arb.c | 142 +++++++++++++++++++++++++++-------- 1 file changed, 112 insertions(+), 30 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 69f8d456324a..2e2cb4774103 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -28,6 +28,7 @@ #define PMIC_ARB_VERSION_V5_MIN 0x50000000 #define PMIC_ARB_VERSION_V7_MIN 0x70000000 #define PMIC_ARB_VERSION_V8_MIN 0x80000000 +#define PMIC_ARB_VERSION_V8P5_MIN 0x80050000 #define PMIC_ARB_INT_EN 0x0004 #define PMIC_ARB_FEATURES 0x0004 @@ -62,14 +63,6 @@ /* Ownership Table */ #define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) -/* Channel Status fields */ -enum pmic_arb_chnl_status { - PMIC_ARB_STATUS_DONE = BIT(0), - PMIC_ARB_STATUS_FAILURE = BIT(1), - PMIC_ARB_STATUS_DENIED = BIT(2), - PMIC_ARB_STATUS_DROPPED = BIT(3), -}; - /* Command register fields */ #define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 @@ -239,6 +232,7 @@ struct spmi_pmic_arb { * on v2 address of SPMI_PIC_IRQ_CLEARn. * @apid_map_offset: offset of PMIC_ARB_REG_CHNLn * @apid_owner: on v2 and later address of SPMI_PERIPHn_2OWNER_TABLE_REG + * @check_chnl_status: checks channel status and returns error code if any */ struct pmic_arb_ver_ops { const char *ver_str; @@ -261,6 +255,8 @@ struct pmic_arb_ver_ops { void __iomem *(*irq_clear)(struct spmi_pmic_arb_bus *bus, u16 n); u32 (*apid_map_offset)(u16 n); void __iomem *(*apid_owner)(struct spmi_pmic_arb_bus *bus, u16 n); + int (*check_chnl_status)(struct spmi_controller *ctrl, u32 status, + u8 sid, u16 addr, u32 offset); }; static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb, @@ -306,6 +302,84 @@ static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf, __raw_writel(data, pmic_arb->wr_base + reg); } +static int pmic_arb_check_chnl_status_v1(struct spmi_controller *ctrl, + u32 status, u8 sid, u16 addr, + u32 offset) +{ + /* Check if DONE bit is set */ + if (!(status & BIT(0))) + return -EAGAIN; + + if (status & BIT(1)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", + __func__, sid, addr, status, offset); + WARN_ON(1); + return -EIO; + } + + if (status & BIT(2)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", + __func__, sid, addr, status); + return -EPERM; + } + + if (status & BIT(3)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + return 0; +} + +static int pmic_arb_check_chnl_status_v8p5(struct spmi_controller *ctrl, + u32 status, u8 sid, u16 addr, + u32 offset) +{ + /* Check if DONE bit is set */ + if (!(status & BIT(0))) + return -EAGAIN; + + if (status & BIT(1)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", + __func__, sid, addr, status, offset); + WARN_ON(1); + return -EIO; + } + + if (status & BIT(2)) { + dev_err(&ctrl->dev, "%s: %#x %#x: CRC error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(3)) { + dev_err(&ctrl->dev, "%s: %#x %#x: parity error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(4)) { + dev_err(&ctrl->dev, "%s: %#x %#x: NACK error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(5)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", + __func__, sid, addr, status); + return -EPERM; + } + + if (status & BIT(6)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + return 0; +} + static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, void __iomem *base, u8 sid, u16 addr, enum pmic_arb_channel ch_type) @@ -327,28 +401,10 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, while (timeout--) { status = readl_relaxed(base + offset); - if (status & PMIC_ARB_STATUS_DONE) { - if (status & PMIC_ARB_STATUS_DENIED) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", - __func__, sid, addr, status); - return -EPERM; - } + rc = pmic_arb->ver_ops->check_chnl_status(ctrl, status, sid, addr, offset); + if (rc != -EAGAIN) + return rc; - if (status & PMIC_ARB_STATUS_FAILURE) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", - __func__, sid, addr, status, offset); - WARN_ON(1); - return -EIO; - } - - if (status & PMIC_ARB_STATUS_DROPPED) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", - __func__, sid, addr, status); - return -EIO; - } - - return 0; - } udelay(1); } @@ -1768,6 +1824,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = { .irq_clear = pmic_arb_irq_clear_v1, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v2 = { @@ -1784,6 +1841,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = { .irq_clear = pmic_arb_irq_clear_v2, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v3 = { @@ -1800,6 +1858,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = { .irq_clear = pmic_arb_irq_clear_v2, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v5 = { @@ -1816,6 +1875,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { .irq_clear = pmic_arb_irq_clear_v5, .apid_map_offset = pmic_arb_apid_map_offset_v5, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v7 = { @@ -1832,6 +1892,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v7 = { .irq_clear = pmic_arb_irq_clear_v7, .apid_map_offset = pmic_arb_apid_map_offset_v7, .apid_owner = pmic_arb_apid_owner_v7, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v8 = { @@ -1849,6 +1910,25 @@ static const struct pmic_arb_ver_ops pmic_arb_v8 = { .irq_clear = pmic_arb_irq_clear_v8, .apid_map_offset = pmic_arb_apid_map_offset_v8, .apid_owner = pmic_arb_apid_owner_v8, + .check_chnl_status = pmic_arb_check_chnl_status_v1, +}; + +static const struct pmic_arb_ver_ops pmic_arb_v8p5 = { + .ver_str = "v8.5", + .get_core_resources = pmic_arb_get_core_resources_v8, + .get_bus_resources = pmic_arb_get_bus_resources_v8, + .init_apid = pmic_arb_init_apid_v8, + .ppid_to_apid = pmic_arb_ppid_to_apid_v5, + .non_data_cmd = pmic_arb_non_data_cmd_v2, + .offset = pmic_arb_offset_v8, + .fmt_cmd = pmic_arb_fmt_cmd_v2, + .owner_acc_status = pmic_arb_owner_acc_status_v7, + .acc_enable = pmic_arb_acc_enable_v8, + .irq_status = pmic_arb_irq_status_v8, + .irq_clear = pmic_arb_irq_clear_v8, + .apid_map_offset = pmic_arb_apid_map_offset_v8, + .apid_owner = pmic_arb_apid_owner_v8, + .check_chnl_status = pmic_arb_check_chnl_status_v8p5, }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { @@ -2030,8 +2110,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pmic_arb->ver_ops = &pmic_arb_v5; else if (hw_ver < PMIC_ARB_VERSION_V8_MIN) pmic_arb->ver_ops = &pmic_arb_v7; - else + else if (hw_ver < PMIC_ARB_VERSION_V8P5_MIN) pmic_arb->ver_ops = &pmic_arb_v8; + else + pmic_arb->ver_ops = &pmic_arb_v8p5; err = pmic_arb->ver_ops->get_core_resources(pdev, core); if (err) From 0c766f0f08ea681396814fd82eddd299da188625 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 16 Apr 2026 15:48:33 -0700 Subject: [PATCH 1939/5214] spmi: clean up kernel-doc in spmi.h Fix all kernel-doc warnings in spmi.h: Warning: include/linux/spmi.h:114 function parameter 'ctrl' not described in 'spmi_controller_put' Warning: include/linux/spmi.h:144 struct member 'shutdown' not described in 'spmi_driver' Signed-off-by: Randy Dunlap Signed-off-by: Stephen Boyd --- include/linux/spmi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/spmi.h b/include/linux/spmi.h index 28e8c8bd3944..671ce1b3ee8e 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -109,7 +109,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, /** * spmi_controller_put() - decrement controller refcount - * @ctrl SPMI controller. + * @ctrl: SPMI controller. */ static inline void spmi_controller_put(struct spmi_controller *ctrl) { @@ -129,6 +129,7 @@ int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl * this structure. * @probe: binds this driver to a SPMI device. * @remove: unbinds this driver from the SPMI device. + * @shutdown: shuts down this driver. * * If PM runtime support is desired for a slave, a device driver can call * pm_runtime_put() from their probe() routine (and a balancing From 3443eec9c55d128064c83225a9111f1a1a37277a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 26 Mar 2026 19:41:43 -0700 Subject: [PATCH 1940/5214] spmi: use kzalloc_flex in main allocation Add a flexible array member to avoid indexing past the struct. Signed-off-by: Rosen Penev Signed-off-by: Stephen Boyd --- drivers/spmi/spmi.c | 4 ++-- include/linux/spmi.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index e889b129f3ac..57b7c0cb4240 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -450,7 +450,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, if (WARN_ON(!parent)) return ERR_PTR(-EINVAL); - ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); + ctrl = kzalloc_flex(*ctrl, priv, size); if (!ctrl) return ERR_PTR(-ENOMEM); @@ -459,7 +459,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, ctrl->dev.bus = &spmi_bus_type; ctrl->dev.parent = parent; ctrl->dev.of_node = parent->of_node; - spmi_controller_set_drvdata(ctrl, &ctrl[1]); + spmi_controller_set_drvdata(ctrl, ctrl->priv); id = ida_alloc(&ctrl_ida, GFP_KERNEL); if (id < 0) { diff --git a/include/linux/spmi.h b/include/linux/spmi.h index 671ce1b3ee8e..4eb9564a7fb3 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -76,6 +76,7 @@ void spmi_device_remove(struct spmi_device *sdev); * @cmd: sends a non-data command sequence on the SPMI bus. * @read_cmd: sends a register read command sequence on the SPMI bus. * @write_cmd: sends a register write command sequence on the SPMI bus. + * @priv: array of private data. */ struct spmi_controller { struct device dev; @@ -85,6 +86,7 @@ struct spmi_controller { u8 sid, u16 addr, u8 *buf, size_t len); int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid, u16 addr, const u8 *buf, size_t len); + u8 priv[]; }; static inline struct spmi_controller *to_spmi_controller(struct device *d) From 38ba49580e875786b85bad1d3895aa9b9f4b3431 Mon Sep 17 00:00:00 2001 From: Aayush Patil Date: Sun, 10 May 2026 23:58:56 +0530 Subject: [PATCH 1941/5214] docs/filesystems/9p: fix broken external links The xcpu.org links for xcpu-talk, kvmfs, and cellfs-talk are dead with no archived snapshots available on the Wayback Machine, so remove them. The PROSE I/O link redirects to a dead server; replace it with an archived version from web.archive.org. Signed-off-by: Aayush Patil Message-ID: <20260510182856.17569-1-aayushpatilsch@gmail.com> Signed-off-by: Dominique Martinet --- Documentation/filesystems/9p.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/filesystems/9p.rst b/Documentation/filesystems/9p.rst index be3504ca034a..65809a1dad21 100644 --- a/Documentation/filesystems/9p.rst +++ b/Documentation/filesystems/9p.rst @@ -23,13 +23,10 @@ the 9p client is available in the form of a USENIX paper: Other applications are described in the following papers: * XCPU & Clustering - http://xcpu.org/papers/xcpu-talk.pdf * KVMFS: control file system for KVM - http://xcpu.org/papers/kvmfs.pdf * CellFS: A New Programming Model for the Cell BE - http://xcpu.org/papers/cellfs-talk.pdf * PROSE I/O: Using 9p to enable Application Partitions - http://plan9.escet.urjc.es/iwp9/cready/PROSE_iwp9_2006.pdf + http://web.archive.org/web/20110101152020/http://plan9.escet.urjc.es/iwp9/cready/PROSE_iwp9_2006.pdf * VirtFS: A Virtualization Aware File System pass-through https://kernel.org/doc/ols/2010/ols2010-pages-109-120.pdf From 6b4f48728faa8bb514368f7eacda05565dea8696 Mon Sep 17 00:00:00 2001 From: Vasiliy Kovalev Date: Wed, 15 Apr 2026 18:52:37 +0300 Subject: [PATCH 1942/5214] net/9p: fix infinite loop in p9_client_rpc on fatal signal When p9_client_rpc() is called with type P9_TFLUSH and the transport has no peer (e.g. fd transport backed by pipes with no 9p server), a fatal signal causes an infinite loop: again: err = io_wait_event_killable(req->wq, ...) /* SIGKILL wakes the task, returns -ERESTARTSYS */ if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; } clear_thread_flag() clears TIF_SIGPENDING before jumping back to io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING, finds it zero, and the task goes to sleep again. The task can only wake on the next signal delivery that calls signal_wake_up() and sets TIF_SIGPENDING again. When that happens the loop repeats, clears TIF_SIGPENDING, and sleeps again indefinitely. This is triggered in practice by coredump_wait(): when a thread in a multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall User Dispatch), coredump_wait() sends SIGKILL to all other threads and waits for them to call mm_release(). If one of those threads is blocked in p9_client_rpc() over an fd transport with no peer, it enters the P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls forever: INFO: task syz.0.18:676 blocked for more than 143 seconds. Not tainted 6.12.77+ #1 task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004 Call Trace: context_switch kernel/sched/core.c:5344 [inline] __schedule+0xcb4/0x5d50 kernel/sched/core.c:6724 __schedule_loop kernel/sched/core.c:6801 [inline] schedule+0xe5/0x350 kernel/sched/core.c:6816 schedule_timeout+0x253/0x290 kernel/time/timer.c:2593 do_wait_for_common kernel/sched/completion.c:95 [inline] __wait_for_common+0x409/0x600 kernel/sched/completion.c:116 wait_for_common kernel/sched/completion.c:127 [inline] wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264 coredump_wait fs/coredump.c:448 [inline] do_coredump+0x854/0x4350 fs/coredump.c:629 get_signal+0x1425/0x2730 kernel/signal.c:2903 arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop kernel/entry/common.c:111 [inline] exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline] __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline] syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218 do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so fatal_signal_pending() works correctly. If a fatal signal is pending, jump to recalc_sigpending to restore TIF_SIGPENDING and return -ERESTARTSYS to the caller. The same defect is present in stable kernels back to 5.4. On those kernels the infinite loop is broken earlier by a second SIGKILL from the parent process (e.g. kill_and_wait() retrying after a timeout), resulting in a zombie process and a shutdown delay rather than a permanent D-state hang, but the underlying flaw is the same. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code") Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7 Cc: stable@vger.kernel.org Signed-off-by: Vasiliy Kovalev Message-ID: <20260415155237.182891-1-kovalev@altlinux.org> Signed-off-by: Dominique Martinet --- net/9p/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/9p/client.c b/net/9p/client.c index b7d947910037..69d3efd340c0 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -600,6 +600,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { + if (fatal_signal_pending(current)) + goto recalc_sigpending; sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; From eb3bd277b37cd435d26a44a40d8f7c87ff16feb6 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:08:58 -0400 Subject: [PATCH 1943/5214] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer Skip invalid sub-buffers when validating the persistent ring buffer instead of discarding the entire ring buffer. Only skipped buffers are invalidated (cleared). If the cache data in memory fails to be synchronized during a reboot, the persistent ring buffer may become partially corrupted, but other sub-buffers may still contain readable event data. Only discard the subbuffers that are found to be corrupted. Link: https://lore.kernel.org/all/20260520185018.051228084@kernel.org/ Link: https://patch.msgid.link/20260522171050.914418536@kernel.org Signed-off-by: Masami Hiramatsu (Google) [SDR: Fixed max_loops in rb_iter_peek() as well ] Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 120 ++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7b07d2004cc6..6afd8ea5081a 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -370,6 +370,12 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) return local_read(&bpage->page->commit); } +/* Size is determined by what has been committed */ +static __always_inline unsigned int rb_page_size(struct buffer_page *bpage) +{ + return rb_page_commit(bpage) & ~RB_MISSED_MASK; +} + static void free_buffer_page(struct buffer_page *bpage) { /* Range pages are not to be freed */ @@ -1762,7 +1768,6 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, unsigned long *subbuf_mask) { int subbuf_size = PAGE_SIZE; - struct buffer_data_page *subbuf; unsigned long buffers_start; unsigned long buffers_end; int i; @@ -1770,6 +1775,11 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, if (!subbuf_mask) return false; + if (meta->subbuf_size != PAGE_SIZE) { + pr_info("Ring buffer boot meta [%d] invalid subbuf_size\n", cpu); + return false; + } + buffers_start = meta->first_buffer; buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs); @@ -1786,11 +1796,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, return false; } - subbuf = rb_subbufs_from_meta(meta); - bitmap_clear(subbuf_mask, 0, meta->nr_subbufs); - /* Is the meta buffers and the subbufs themselves have correct data? */ + /* + * Ensure the meta::buffers array has correct data. The data in each subbufs + * are checked later in rb_meta_validate_events(). + */ for (i = 0; i < meta->nr_subbufs; i++) { if (meta->buffers[i] < 0 || meta->buffers[i] >= meta->nr_subbufs) { @@ -1798,18 +1809,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, return false; } - if ((unsigned)local_read(&subbuf->commit) > subbuf_size) { - pr_info("Ring buffer boot meta [%d] buffer invalid commit\n", cpu); - return false; - } - if (test_bit(meta->buffers[i], subbuf_mask)) { pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu); return false; } set_bit(meta->buffers[i], subbuf_mask); - subbuf = (void *)subbuf + subbuf_size; } return true; @@ -1873,13 +1878,22 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu return events; } -static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu) +static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu, + struct ring_buffer_cpu_meta *meta) { unsigned long long ts; + unsigned long tail; u64 delta; - int tail; - tail = local_read(&dpage->commit); + /* + * When a sub-buffer is recovered from a read, the commit value may + * have RB_MISSED_* bits set, as these bits are reset on reuse. + * Even after clearing these bits, a commit value greater than the + * subbuf_size is considered invalid. + */ + tail = local_read(&dpage->commit) & ~RB_MISSED_MASK; + if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE) + return -1; return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); } @@ -1890,6 +1904,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) struct buffer_page *head_page, *orig_head, *orig_reader; unsigned long entry_bytes = 0; unsigned long entries = 0; + int discarded = 0; int ret; u64 ts; int i; @@ -1901,14 +1916,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) orig_reader = cpu_buffer->reader_page; /* Do the reader page first */ - ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu); + ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu, meta); if (ret < 0) { - pr_info("Ring buffer reader page is invalid\n"); - goto invalid; + pr_info("Ring buffer meta [%d] invalid reader page detected\n", + cpu_buffer->cpu); + discarded++; + /* Instead of discard whole ring buffer, discard only this sub-buffer. */ + local_set(&orig_reader->entries, 0); + local_set(&orig_reader->page->commit, 0); + } else { + entries += ret; + entry_bytes += rb_page_size(orig_reader); + local_set(&orig_reader->entries, ret); } - entries += ret; - entry_bytes += local_read(&orig_reader->page->commit); - local_set(&orig_reader->entries, ret); ts = head_page->page->time_stamp; @@ -1936,7 +1956,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) break; /* Stop rewind if the page is invalid. */ - ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu); + ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta); if (ret < 0) break; @@ -1945,7 +1965,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (ret) local_inc(&cpu_buffer->pages_touched); entries += ret; - entry_bytes += rb_page_commit(head_page); + entry_bytes += rb_page_size(head_page); } if (i) pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i); @@ -2015,21 +2035,24 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (head_page == orig_reader) continue; - ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu); + ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta); if (ret < 0) { - pr_info("Ring buffer meta [%d] invalid buffer page\n", - cpu_buffer->cpu); - goto invalid; + if (!discarded) + pr_info("Ring buffer meta [%d] invalid buffer page detected\n", + cpu_buffer->cpu); + discarded++; + /* Instead of discard whole ring buffer, discard only this sub-buffer. */ + local_set(&head_page->entries, 0); + local_set(&head_page->page->commit, 0); + } else { + /* If the buffer has content, update pages_touched */ + if (ret) + local_inc(&cpu_buffer->pages_touched); + + entries += ret; + entry_bytes += rb_page_size(head_page); + local_set(&head_page->entries, ret); } - - /* If the buffer has content, update pages_touched */ - if (ret) - local_inc(&cpu_buffer->pages_touched); - - entries += ret; - entry_bytes += local_read(&head_page->page->commit); - local_set(&head_page->entries, ret); - if (head_page == cpu_buffer->commit_page) break; } @@ -2043,7 +2066,10 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) local_set(&cpu_buffer->entries, entries); local_set(&cpu_buffer->entries_bytes, entry_bytes); - pr_info("Ring buffer meta [%d] is from previous boot!\n", cpu_buffer->cpu); + pr_info("Ring buffer meta [%d] is from previous boot!", cpu_buffer->cpu); + if (discarded) + pr_cont(" (%d pages discarded)", discarded); + pr_cont("\n"); return; invalid: @@ -3330,12 +3356,6 @@ rb_iter_head_event(struct ring_buffer_iter *iter) return NULL; } -/* Size is determined by what has been committed */ -static __always_inline unsigned rb_page_size(struct buffer_page *bpage) -{ - return rb_page_commit(bpage) & ~RB_MISSED_MASK; -} - static __always_inline unsigned rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer) { @@ -5636,8 +5656,9 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) static struct buffer_page * __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) { - struct buffer_page *reader = NULL; + int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3; unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size); + struct buffer_page *reader = NULL; unsigned long overwrite; unsigned long flags; int nr_loops = 0; @@ -5649,11 +5670,14 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) again: /* * This should normally only loop twice. But because the - * start of the reader inserts an empty page, it causes - * a case where we will loop three times. There should be no - * reason to loop four times (that I know of). + * start of the reader inserts an empty page, it causes a + * case where we will loop three times. There should be no + * reason to loop four times unless the ring buffer is a + * recovered persistent ring buffer. For persistent ring buffers, + * invalid pages are reset during recovery, so there may be more + * than 3 contiguous pages can be empty, but less than nr_pages. */ - if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) { + if (RB_WARN_ON(cpu_buffer, ++nr_loops > max_loops)) { reader = NULL; goto out; } @@ -5950,12 +5974,14 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts) struct ring_buffer_per_cpu *cpu_buffer; struct ring_buffer_event *event; int nr_loops = 0; + int max_loops; if (ts) *ts = 0; cpu_buffer = iter->cpu_buffer; buffer = cpu_buffer->buffer; + max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3; /* * Check if someone performed a consuming read to the buffer @@ -5978,7 +6004,7 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts) * the ring buffer with an active write as the consumer is. * Do not warn if the three failures is reached. */ - if (++nr_loops > 3) + if (++nr_loops > max_loops) return NULL; if (rb_per_cpu_empty(cpu_buffer)) From c8a7d4b4723a21e7464efe86dcf80627e0b4df33 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:08:59 -0400 Subject: [PATCH 1944/5214] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer Skip invalid sub-buffers when rewinding the persistent ring buffer instead of stopping the rewinding the ring buffer. The skipped buffers are cleared. To ensure the rewinding stops at the unused page, this also clears buffer_data_page::time_stamp when tracing resets the buffer. This allows us to identify unused pages and empty pages. Link: https://patch.msgid.link/20260522171051.091265852@kernel.org Signed-off-by: Masami Hiramatsu (Google) [ SDR: Have reader_page still get evaluated if header_page fails ] Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 107 ++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 6afd8ea5081a..c10cf4ba91d6 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -363,6 +363,7 @@ struct buffer_page { static void rb_init_page(struct buffer_data_page *bpage) { local_set(&bpage->commit, 0); + bpage->time_stamp = 0; } static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) @@ -1878,12 +1879,14 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu return events; } -static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu, - struct ring_buffer_cpu_meta *meta) +static int rb_validate_buffer(struct buffer_page *bpage, int cpu, + struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts) { + struct buffer_data_page *dpage = bpage->page; unsigned long long ts; unsigned long tail; u64 delta; + int ret; /* * When a sub-buffer is recovered from a read, the commit value may @@ -1892,9 +1895,27 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu, * subbuf_size is considered invalid. */ tail = local_read(&dpage->commit) & ~RB_MISSED_MASK; - if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE) - return -1; - return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); + if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE) + ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); + else + ret = -1; + + /* + * The timestamp must be greater than @prev_ts and smaller than @next_ts. + * Since this function works in both forward (verify) and reverse (unwind) + * loop, we don't know both @prev_ts and @next_ts at the same time. + * So use the known boundary as the boundary. + */ + if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) { + local_set(&bpage->entries, 0); + local_set(&dpage->commit, 0); + dpage->time_stamp = prev_ts ? prev_ts : next_ts; + ret = -1; + } else { + local_set(&bpage->entries, ret); + } + + return ret; } /* If the meta data has been validated, now validate the events */ @@ -1905,6 +1926,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) unsigned long entry_bytes = 0; unsigned long entries = 0; int discarded = 0; + bool skip = false; int ret; u64 ts; int i; @@ -1915,25 +1937,35 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) orig_head = head_page = cpu_buffer->head_page; orig_reader = cpu_buffer->reader_page; - /* Do the reader page first */ - ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu, meta); + /* Do the head page first */ + ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0); + if (ret < 0) { + pr_info("Ring buffer meta [%d] invalid head page detected\n", + cpu_buffer->cpu); + /* Don't bother rewinding */ + skip = true; + ts = 0; + } else { + ts = head_page->page->time_stamp; + } + + /* Do the reader page - reader must be previous to head. */ + ret = rb_validate_buffer(orig_reader, cpu_buffer->cpu, meta, 0, ts); if (ret < 0) { pr_info("Ring buffer meta [%d] invalid reader page detected\n", cpu_buffer->cpu); discarded++; - /* Instead of discard whole ring buffer, discard only this sub-buffer. */ - local_set(&orig_reader->entries, 0); - local_set(&orig_reader->page->commit, 0); } else { entries += ret; entry_bytes += rb_page_size(orig_reader); - local_set(&orig_reader->entries, ret); + ts = orig_reader->page->time_stamp; } - ts = head_page->page->time_stamp; + if (skip) + goto skip_rewind; /* - * Try to rewind the head so that we can read the pages which already + * Try to rewind the head so that we can read the pages which are already * read in the previous boot. */ if (head_page == cpu_buffer->tail_page) @@ -1946,26 +1978,27 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (head_page == cpu_buffer->tail_page) break; - /* Ensure the page has older data than head. */ - if (ts < head_page->page->time_stamp) + /* Rewind until unused page (no timestamp, no commit). */ + if (!head_page->page->time_stamp && rb_page_commit(head_page) == 0) break; - ts = head_page->page->time_stamp; - /* Ensure the page has correct timestamp and some data. */ - if (!ts || rb_page_commit(head_page) == 0) - break; - - /* Stop rewind if the page is invalid. */ - ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta); - if (ret < 0) - break; - - /* Recover the number of entries and update stats. */ - local_set(&head_page->entries, ret); - if (ret) - local_inc(&cpu_buffer->pages_touched); - entries += ret; - entry_bytes += rb_page_size(head_page); + /* + * Skip if the page is invalid, or its timestamp is newer than the + * previous valid page. + */ + ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, ts); + if (ret < 0) { + if (!discarded) + pr_info("Ring buffer meta [%d] invalid buffer page detected\n", + cpu_buffer->cpu); + discarded++; + } else { + entries += ret; + entry_bytes += rb_page_size(head_page); + if (ret > 0) + local_inc(&cpu_buffer->pages_touched); + ts = head_page->page->time_stamp; + } } if (i) pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i); @@ -2027,6 +2060,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Nothing more to do, the only page is the reader page */ goto done; } + ts = head_page->page->time_stamp; /* Iterate until finding the commit page */ for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) { @@ -2035,15 +2069,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (head_page == orig_reader) continue; - ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta); + ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, ts, 0); if (ret < 0) { if (!discarded) pr_info("Ring buffer meta [%d] invalid buffer page detected\n", cpu_buffer->cpu); discarded++; - /* Instead of discard whole ring buffer, discard only this sub-buffer. */ - local_set(&head_page->entries, 0); - local_set(&head_page->page->commit, 0); } else { /* If the buffer has content, update pages_touched */ if (ret) @@ -2051,7 +2082,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) entries += ret; entry_bytes += rb_page_size(head_page); - local_set(&head_page->entries, ret); + ts = head_page->page->time_stamp; } if (head_page == cpu_buffer->commit_page) break; @@ -2079,12 +2110,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Reset the reader page */ local_set(&cpu_buffer->reader_page->entries, 0); - local_set(&cpu_buffer->reader_page->page->commit, 0); + rb_init_page(cpu_buffer->reader_page->page); /* Reset all the subbuffers */ for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) { local_set(&head_page->entries, 0); - local_set(&head_page->page->commit, 0); + rb_init_page(head_page->page); } } From 68413a36f0051bd7ba7ea37ae2167951aeae5bd2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:09:00 -0400 Subject: [PATCH 1945/5214] ring-buffer: Add persistent ring buffer invalid-page inject test Add a self-corrupting test for the persistent ring buffer. This will inject an erroneous value to some sub-buffer pages (where the index is even or multiples of 5) in the persistent ring buffer when the kernel panics, and checks whether the number of detected invalid pages and the total entry_bytes are the same as the recorded values after reboot. This ensures that the kernel can correctly recover a partially corrupted persistent ring buffer after a reboot or panic. The test only runs on the persistent ring buffer whose name is "ptracingtest". The user has to fill it with events before a kernel panic. To run the test, enable CONFIG_RING_BUFFER_PERSISTENT_INJECT and add the following kernel cmdline: reserve_mem=20M:2M:trace trace_instance=ptracingtest^traceoff@trace panic=1 Run the following commands after the 1st boot: cd /sys/kernel/tracing/instances/ptracingtest echo 1 > tracing_on echo 1 > events/enable sleep 3 echo c > /proc/sysrq-trigger After panic message, the kernel will reboot and run the verification on the persistent ring buffer, e.g. Ring buffer meta [2] invalid buffer page detected Ring buffer meta [2] is from previous boot! (318 pages discarded) Ring buffer testing [2] invalid pages: PASSED (318/318) Ring buffer testing [2] entry_bytes: PASSED (1300476/1300476) Link: https://patch.msgid.link/20260522171051.260140328@kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- include/linux/ring_buffer.h | 1 + kernel/trace/Kconfig | 34 ++++++++++++++++ kernel/trace/ring_buffer.c | 79 +++++++++++++++++++++++++++++++++++++ kernel/trace/trace.c | 4 ++ 4 files changed, 118 insertions(+) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 994f52b34344..0670742b2d60 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -238,6 +238,7 @@ int ring_buffer_subbuf_size_get(struct trace_buffer *buffer); enum ring_buffer_flags { RB_FL_OVERWRITE = 1 << 0, + RB_FL_TESTING = 1 << 1, }; #ifdef CONFIG_RING_BUFFER diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index e130da35808f..084f34dc6c9f 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -1202,6 +1202,40 @@ config RING_BUFFER_VALIDATE_TIME_DELTAS Only say Y if you understand what this does, and you still want it enabled. Otherwise say N +config RING_BUFFER_PERSISTENT_INJECT + bool "Enable persistent ring buffer error injection test" + depends on RING_BUFFER + help + This option will have the kernel check if the persistent ring + buffer is named "ptracingtest". and if so, it will corrupt some + of its pages on a kernel panic. This is used to test if the + persistent ring buffer can recover from some of its sub-buffers + being corrupted. + To use this, boot a kernel with a "ptracingtest" persistent + ring buffer, e.g. + + reserve_mem=20M:2M:trace trace_instance=ptracingtest@trace panic=1 + + And after the 1st boot, run the following commands: + + cd /sys/kernel/tracing/instances/ptracingtest + echo 1 > events/enable + echo 1 > tracing_on + sleep 3 + echo c > /proc/sysrq-trigger + + After the panic message, the kernel will reboot and will show + the test results in the console output. + + Note that events for the test ring buffer needs to be enabled + prior to crashing the kernel so that the ring buffer has content + that the test will corrupt. + As the test will corrupt events in the "ptracingtest" persistent + ring buffer, it should not be used for any other purpose other + than this test. + + If unsure, say N + config MMIOTRACE_TEST tristate "Test module for mmiotrace" depends on MMIOTRACE && m diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index c10cf4ba91d6..dc603d9c9414 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -64,6 +64,10 @@ struct ring_buffer_cpu_meta { unsigned long commit_buffer; __u32 subbuf_size; __u32 nr_subbufs; +#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT + __u32 nr_invalid; + __u32 entry_bytes; +#endif int buffers[]; }; @@ -2101,6 +2105,21 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (discarded) pr_cont(" (%d pages discarded)", discarded); pr_cont("\n"); + +#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT + if (meta->nr_invalid) + pr_warn("Ring buffer testing [%d] invalid pages: %s (%d/%d)\n", + cpu_buffer->cpu, + (discarded == meta->nr_invalid) ? "PASSED" : "FAILED", + discarded, meta->nr_invalid); + if (meta->entry_bytes) + pr_warn("Ring buffer testing [%d] entry_bytes: %s (%ld/%ld)\n", + cpu_buffer->cpu, + (entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED", + (long)entry_bytes, (long)meta->entry_bytes); + meta->nr_invalid = 0; + meta->entry_bytes = 0; +#endif return; invalid: @@ -2581,12 +2600,72 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) kfree(cpu_buffer); } +#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT +static void rb_test_inject_invalid_pages(struct trace_buffer *buffer) +{ + struct ring_buffer_per_cpu *cpu_buffer; + struct ring_buffer_cpu_meta *meta; + struct buffer_data_page *dpage; + unsigned long entry_bytes = 0; + unsigned long ptr; + int subbuf_size; + int invalid = 0; + int cpu; + int i; + + if (!(buffer->flags & RB_FL_TESTING)) + return; + + guard(preempt)(); + cpu = smp_processor_id(); + + cpu_buffer = buffer->buffers[cpu]; + if (!cpu_buffer) + return; + meta = cpu_buffer->ring_meta; + if (!meta) + return; + + ptr = (unsigned long)rb_subbufs_from_meta(meta); + subbuf_size = meta->subbuf_size; + + for (i = 0; i < meta->nr_subbufs; i++) { + unsigned long idx = meta->buffers[i]; + + dpage = (void *)(ptr + idx * subbuf_size); + /* Skip unused pages */ + if (!local_read(&dpage->commit)) + continue; + + /* + * Invalidate even pages or multiples of 5. This will cause 3 + * contiguous invalidated(empty) pages. + */ + if (!(i & 0x1) || !(i % 5)) { + local_add(subbuf_size + 1, &dpage->commit); + invalid++; + } else { + /* Count total commit bytes. */ + entry_bytes += local_read(&dpage->commit) & ~RB_MISSED_MASK; + } + } + + pr_info("Inject invalidated %d pages on CPU%d, total size: %ld\n", + invalid, cpu, (long)entry_bytes); + meta->nr_invalid = invalid; + meta->entry_bytes = entry_bytes; +} +#else /* !CONFIG_RING_BUFFER_PERSISTENT_INJECT */ +#define rb_test_inject_invalid_pages(buffer) do { } while (0) +#endif + /* Stop recording on a persistent buffer and flush cache if needed. */ static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data) { struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb); ring_buffer_record_off(buffer); + rb_test_inject_invalid_pages(buffer); arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end); return NOTIFY_DONE; } diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6eb4d3097a4d..4573f65d68ce 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -8383,6 +8383,8 @@ static void setup_trace_scratch(struct trace_array *tr, memset(tscratch, 0, size); } +#define TRACE_TEST_PTRACING_NAME "ptracingtest" + int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size) { enum ring_buffer_flags rb_flags; @@ -8394,6 +8396,8 @@ int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int buf->tr = tr; if (tr->range_addr_start && tr->range_addr_size) { + if (tr->name && !strcmp(tr->name, TRACE_TEST_PTRACING_NAME)) + rb_flags |= RB_FL_TESTING; /* Add scratch buffer to handle 128 modules */ buf->buffer = ring_buffer_alloc_range(size, rb_flags, 0, tr->range_addr_start, From e500acc34b556df7774213d95ff130801b9d6512 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:09:01 -0400 Subject: [PATCH 1946/5214] ring-buffer: Show commit numbers in buffer_meta file In addition to the index number, show the commit numbers of each data page in the per_cpu buffer_meta file. This is useful for understanding the current status of the persistent ring buffer. (Note that this file is shown only for persistent ring buffer and its backup instance) Link: https://patch.msgid.link/20260522171051.424411323@kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index dc603d9c9414..88e613e78632 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2231,6 +2231,7 @@ static int rbm_show(struct seq_file *m, void *v) struct ring_buffer_per_cpu *cpu_buffer = m->private; struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta; unsigned long val = (unsigned long)v; + struct buffer_data_page *dpage; if (val == 1) { seq_printf(m, "head_buffer: %d\n", @@ -2243,7 +2244,9 @@ static int rbm_show(struct seq_file *m, void *v) } val -= 2; - seq_printf(m, "buffer[%ld]: %d\n", val, meta->buffers[val]); + dpage = rb_range_buffer(cpu_buffer, val); + seq_printf(m, "buffer[%ld]: %d (commit: %ld)\n", + val, meta->buffers[val], dpage ? local_read(&dpage->commit) : -1); return 0; } From ea250b7ef9ff2af4241b4d7f078594e51d155b81 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:09:02 -0400 Subject: [PATCH 1947/5214] ring-buffer: Cleanup persistent ring buffer validation Cleanup rb_meta_validate_events() function to make it easier to read. This includes the following cleanups: - Introduce rb_validatation_state to hold working variables in validation. - Move repleated validation state updates into rb_validate_buffer(). - Move reader_page injection code outside of rb_meta_validate_events(). Link: https://patch.msgid.link/20260522171051.577231395@kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 200 ++++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 92 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 88e613e78632..73f453ba12ce 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1883,8 +1883,16 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu return events; } -static int rb_validate_buffer(struct buffer_page *bpage, int cpu, - struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts) +struct rb_validation_state { + unsigned long entries; + unsigned long entry_bytes; + int discarded; + u64 ts; +}; + +static int __rb_validate_buffer(struct buffer_page *bpage, int cpu, + struct ring_buffer_cpu_meta *meta, + u64 prev_ts, u64 next_ts) { struct buffer_data_page *dpage = bpage->page; unsigned long long ts; @@ -1922,17 +1930,95 @@ static int rb_validate_buffer(struct buffer_page *bpage, int cpu, return ret; } +/** + * rb_validate_buffer - validates a single buffer page and updates the state. + * @bpage: buffer page to validate + * @cpu_buffer: cpu_buffer this page belongs to + * @meta: meta of the cpu_buffer + * @state: validation state + * @prev_ts: previous buffer's timestamp (optional) + * @next_ts: next buffer's timestamp (optional) + * + * If the page is invalid (wrong event length or timestamp), it increments the + * discarded counter and warns it. Otherwise, it updates the validation state. + */ +static void rb_validate_buffer(struct buffer_page *bpage, + struct ring_buffer_per_cpu *cpu_buffer, + struct ring_buffer_cpu_meta *meta, + struct rb_validation_state *state, + u64 prev_ts, u64 next_ts) +{ + int ret; + + ret = __rb_validate_buffer(bpage, cpu_buffer->cpu, meta, prev_ts, next_ts); + if (ret < 0) { + if (!state->discarded) + pr_info("Ring buffer meta [%d] invalid buffer page detected\n", + cpu_buffer->cpu); + state->discarded++; + } else { + /* If the buffer has content, update pages_touched */ + if (ret) + local_inc(&cpu_buffer->pages_touched); + + state->entries += ret; + state->entry_bytes += rb_page_size(bpage); + state->ts = bpage->page->time_stamp; + } +} + +static void rb_meta_inject_reader_page(struct ring_buffer_per_cpu *cpu_buffer, + struct ring_buffer_cpu_meta *meta, + struct buffer_page *orig_head, + struct buffer_page *head_page) +{ + struct buffer_page *bpage = orig_head; + int i; + + rb_dec_page(&bpage); + /* + * Insert the reader_page before the original head page. + * Since the list encode RB_PAGE flags, general list + * operations should be avoided. + */ + cpu_buffer->reader_page->list.next = &orig_head->list; + cpu_buffer->reader_page->list.prev = orig_head->list.prev; + orig_head->list.prev = &cpu_buffer->reader_page->list; + bpage->list.next = &cpu_buffer->reader_page->list; + + /* Make the head_page the reader page */ + cpu_buffer->reader_page = head_page; + bpage = head_page; + rb_inc_page(&head_page); + head_page->list.prev = bpage->list.prev; + rb_dec_page(&bpage); + bpage->list.next = &head_page->list; + rb_set_list_to_head(&bpage->list); + cpu_buffer->pages = &head_page->list; + + cpu_buffer->head_page = head_page; + meta->head_buffer = (unsigned long)head_page->page; + + /* Reset all the indexes */ + bpage = cpu_buffer->reader_page; + meta->buffers[0] = rb_meta_subbuf_idx(meta, bpage->page); + bpage->id = 0; + + for (i = 1, bpage = head_page; i < meta->nr_subbufs; + i++, rb_inc_page(&bpage)) { + meta->buffers[i] = rb_meta_subbuf_idx(meta, bpage->page); + bpage->id = i; + } +} + /* If the meta data has been validated, now validate the events */ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) { struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta; struct buffer_page *head_page, *orig_head, *orig_reader; - unsigned long entry_bytes = 0; - unsigned long entries = 0; - int discarded = 0; + struct rb_validation_state state = { 0 }; bool skip = false; int ret; - u64 ts; int i; if (!meta || !meta->head_buffer) @@ -1942,28 +2028,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) orig_reader = cpu_buffer->reader_page; /* Do the head page first */ - ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0); + ret = __rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0); if (ret < 0) { pr_info("Ring buffer meta [%d] invalid head page detected\n", cpu_buffer->cpu); /* Don't bother rewinding */ skip = true; - ts = 0; + state.ts = 0; } else { - ts = head_page->page->time_stamp; + state.ts = head_page->page->time_stamp; } /* Do the reader page - reader must be previous to head. */ - ret = rb_validate_buffer(orig_reader, cpu_buffer->cpu, meta, 0, ts); - if (ret < 0) { - pr_info("Ring buffer meta [%d] invalid reader page detected\n", - cpu_buffer->cpu); - discarded++; - } else { - entries += ret; - entry_bytes += rb_page_size(orig_reader); - ts = orig_reader->page->time_stamp; - } + rb_validate_buffer(orig_reader, cpu_buffer, meta, &state, 0, state.ts); if (skip) goto skip_rewind; @@ -1990,19 +2067,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) * Skip if the page is invalid, or its timestamp is newer than the * previous valid page. */ - ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, ts); - if (ret < 0) { - if (!discarded) - pr_info("Ring buffer meta [%d] invalid buffer page detected\n", - cpu_buffer->cpu); - discarded++; - } else { - entries += ret; - entry_bytes += rb_page_size(head_page); - if (ret > 0) - local_inc(&cpu_buffer->pages_touched); - ts = head_page->page->time_stamp; - } + rb_validate_buffer(head_page, cpu_buffer, meta, &state, 0, state.ts); } if (i) pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i); @@ -2016,43 +2081,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) * into the location just before the original head page. */ if (head_page != orig_head) { - struct buffer_page *bpage = orig_head; - - rb_dec_page(&bpage); - /* - * Insert the reader_page before the original head page. - * Since the list encode RB_PAGE flags, general list - * operations should be avoided. - */ - cpu_buffer->reader_page->list.next = &orig_head->list; - cpu_buffer->reader_page->list.prev = orig_head->list.prev; - orig_head->list.prev = &cpu_buffer->reader_page->list; - bpage->list.next = &cpu_buffer->reader_page->list; - - /* Make the head_page the reader page */ - cpu_buffer->reader_page = head_page; - bpage = head_page; - rb_inc_page(&head_page); - head_page->list.prev = bpage->list.prev; - rb_dec_page(&bpage); - bpage->list.next = &head_page->list; - rb_set_list_to_head(&bpage->list); - cpu_buffer->pages = &head_page->list; - - cpu_buffer->head_page = head_page; - meta->head_buffer = (unsigned long)head_page->page; - - /* Reset all the indexes */ - bpage = cpu_buffer->reader_page; - meta->buffers[0] = rb_meta_subbuf_idx(meta, bpage->page); - bpage->id = 0; - - for (i = 1, bpage = head_page; i < meta->nr_subbufs; - i++, rb_inc_page(&bpage)) { - meta->buffers[i] = rb_meta_subbuf_idx(meta, bpage->page); - bpage->id = i; - } - + rb_meta_inject_reader_page(cpu_buffer, meta, orig_head, head_page); /* We'll restart verifying from orig_head */ head_page = orig_head; } @@ -2064,7 +2093,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Nothing more to do, the only page is the reader page */ goto done; } - ts = head_page->page->time_stamp; + state.ts = head_page->page->time_stamp; /* Iterate until finding the commit page */ for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) { @@ -2073,21 +2102,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) if (head_page == orig_reader) continue; - ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, ts, 0); - if (ret < 0) { - if (!discarded) - pr_info("Ring buffer meta [%d] invalid buffer page detected\n", - cpu_buffer->cpu); - discarded++; - } else { - /* If the buffer has content, update pages_touched */ - if (ret) - local_inc(&cpu_buffer->pages_touched); + rb_validate_buffer(head_page, cpu_buffer, meta, &state, state.ts, 0); - entries += ret; - entry_bytes += rb_page_size(head_page); - ts = head_page->page->time_stamp; - } if (head_page == cpu_buffer->commit_page) break; } @@ -2098,25 +2114,25 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) goto invalid; } done: - local_set(&cpu_buffer->entries, entries); - local_set(&cpu_buffer->entries_bytes, entry_bytes); + local_set(&cpu_buffer->entries, state.entries); + local_set(&cpu_buffer->entries_bytes, state.entry_bytes); pr_info("Ring buffer meta [%d] is from previous boot!", cpu_buffer->cpu); - if (discarded) - pr_cont(" (%d pages discarded)", discarded); + if (state.discarded) + pr_cont(" (%d pages discarded)", state.discarded); pr_cont("\n"); #ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT if (meta->nr_invalid) pr_warn("Ring buffer testing [%d] invalid pages: %s (%d/%d)\n", cpu_buffer->cpu, - (discarded == meta->nr_invalid) ? "PASSED" : "FAILED", - discarded, meta->nr_invalid); + (state.discarded == meta->nr_invalid) ? "PASSED" : "FAILED", + state.discarded, meta->nr_invalid); if (meta->entry_bytes) pr_warn("Ring buffer testing [%d] entry_bytes: %s (%ld/%ld)\n", cpu_buffer->cpu, - (entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED", - (long)entry_bytes, (long)meta->entry_bytes); + (state.entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED", + (long)state.entry_bytes, (long)meta->entry_bytes); meta->nr_invalid = 0; meta->entry_bytes = 0; #endif From 7cf02d0aa6bd4a36f784f6e5dabf225e10b31f3a Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 22 May 2026 13:09:03 -0400 Subject: [PATCH 1948/5214] ring-buffer: Cleanup buffer_data_page related code Code cleanup related to buffer_data_page for readability, which includes: - Introduce rb_data_page_commit() and rb_data_page_size() - Use 'dpage' for buffer_data_page, instead of 'bpage' because 'bpage' is used for buffer_page. Link: https://patch.msgid.link/20260522171051.722645963@kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 114 ++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 73f453ba12ce..fd4a8dc5484e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -364,21 +364,30 @@ struct buffer_page { #define RB_WRITE_MASK 0xfffff #define RB_WRITE_INTCNT (1 << 20) -static void rb_init_page(struct buffer_data_page *bpage) +static void rb_init_data_page(struct buffer_data_page *bpage) { local_set(&bpage->commit, 0); bpage->time_stamp = 0; } -static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) +static __always_inline long rb_data_page_commit(struct buffer_data_page *dpage) { - return local_read(&bpage->page->commit); + return local_read(&dpage->commit); +} + +static __always_inline long rb_data_page_size(struct buffer_data_page *dpage) +{ + return rb_data_page_commit(dpage) & ~RB_MISSED_MASK; +} + +static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) +{ + return rb_data_page_commit(bpage->page); } -/* Size is determined by what has been committed */ static __always_inline unsigned int rb_page_size(struct buffer_page *bpage) { - return rb_page_commit(bpage) & ~RB_MISSED_MASK; + return rb_data_page_size(bpage->page); } static void free_buffer_page(struct buffer_page *bpage) @@ -419,7 +428,7 @@ static struct buffer_data_page *alloc_cpu_data(int cpu, int order) return NULL; dpage = page_address(page); - rb_init_page(dpage); + rb_init_data_page(dpage); return dpage; } @@ -659,7 +668,7 @@ static void verify_event(struct ring_buffer_per_cpu *cpu_buffer, do { if (page == tail_page || WARN_ON_ONCE(stop++ > 100)) done = true; - commit = local_read(&page->page->commit); + commit = rb_page_commit(page); write = local_read(&page->write); if (addr >= (unsigned long)&page->page->data[commit] && addr < (unsigned long)&page->page->data[write]) @@ -1906,7 +1915,7 @@ static int __rb_validate_buffer(struct buffer_page *bpage, int cpu, * Even after clearing these bits, a commit value greater than the * subbuf_size is considered invalid. */ - tail = local_read(&dpage->commit) & ~RB_MISSED_MASK; + tail = rb_data_page_size(dpage); if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE) ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); else @@ -2145,12 +2154,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Reset the reader page */ local_set(&cpu_buffer->reader_page->entries, 0); - rb_init_page(cpu_buffer->reader_page->page); + rb_init_data_page(cpu_buffer->reader_page->page); /* Reset all the subbuffers */ for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) { local_set(&head_page->entries, 0); - rb_init_page(head_page->page); + rb_init_data_page(head_page->page); } } @@ -2210,7 +2219,7 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages, int sc */ for (i = 0; i < meta->nr_subbufs; i++) { meta->buffers[i] = i; - rb_init_page(subbuf); + rb_init_data_page(subbuf); subbuf += meta->subbuf_size; } } @@ -2262,7 +2271,7 @@ static int rbm_show(struct seq_file *m, void *v) val -= 2; dpage = rb_range_buffer(cpu_buffer, val); seq_printf(m, "buffer[%ld]: %d (commit: %ld)\n", - val, meta->buffers[val], dpage ? local_read(&dpage->commit) : -1); + val, meta->buffers[val], dpage ? rb_data_page_commit(dpage) : -1); return 0; } @@ -2653,7 +2662,7 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer) dpage = (void *)(ptr + idx * subbuf_size); /* Skip unused pages */ - if (!local_read(&dpage->commit)) + if (!rb_data_page_commit(dpage)) continue; /* @@ -2665,7 +2674,7 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer) invalid++; } else { /* Count total commit bytes. */ - entry_bytes += local_read(&dpage->commit) & ~RB_MISSED_MASK; + entry_bytes += rb_data_page_size(dpage); } } @@ -4194,8 +4203,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) local_set(&cpu_buffer->commit_page->page->commit, rb_page_write(cpu_buffer->commit_page)); RB_WARN_ON(cpu_buffer, - local_read(&cpu_buffer->commit_page->page->commit) & - ~RB_WRITE_MASK); + rb_page_commit(cpu_buffer->commit_page) & ~RB_WRITE_MASK); barrier(); } @@ -4567,7 +4575,7 @@ static const char *show_interrupt_level(void) return show_irq_str(level); } -static void dump_buffer_page(struct buffer_data_page *bpage, +static void dump_buffer_page(struct buffer_data_page *dpage, struct rb_event_info *info, unsigned long tail) { @@ -4575,12 +4583,12 @@ static void dump_buffer_page(struct buffer_data_page *bpage, u64 ts, delta; int e; - ts = bpage->time_stamp; + ts = dpage->time_stamp; pr_warn(" [%lld] PAGE TIME STAMP\n", ts); for (e = 0; e < tail; e += rb_event_length(event)) { - event = (struct ring_buffer_event *)(bpage->data + e); + event = (struct ring_buffer_event *)(dpage->data + e); switch (event->type_len) { @@ -4630,7 +4638,7 @@ static atomic_t ts_dump; } \ atomic_inc(&cpu_buffer->record_disabled); \ pr_warn(fmt, ##__VA_ARGS__); \ - dump_buffer_page(bpage, info, tail); \ + dump_buffer_page(dpage, info, tail); \ atomic_dec(&ts_dump); \ /* There's some cases in boot up that this can happen */ \ if (WARN_ON_ONCE(system_state != SYSTEM_BOOTING)) \ @@ -4646,16 +4654,16 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, struct rb_event_info *info, unsigned long tail) { - struct buffer_data_page *bpage; + struct buffer_data_page *dpage; u64 ts, delta; bool full = false; int ret; - bpage = info->tail_page->page; + dpage = info->tail_page->page; if (tail == CHECK_FULL_PAGE) { full = true; - tail = local_read(&bpage->commit); + tail = rb_data_page_commit(dpage); } else if (info->add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)) { /* Ignore events with absolute time stamps */ @@ -4666,7 +4674,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, * Do not check the first event (skip possible extends too). * Also do not check if previous events have not been committed. */ - if (tail <= 8 || tail > local_read(&bpage->commit)) + if (tail <= 8 || tail > rb_data_page_commit(dpage)) return; /* @@ -4675,7 +4683,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, if (atomic_inc_return(this_cpu_ptr(&checking)) != 1) goto out; - ret = rb_read_data_buffer(bpage, tail, cpu_buffer->cpu, &ts, &delta); + ret = rb_read_data_buffer(dpage, tail, cpu_buffer->cpu, &ts, &delta); if (ret < 0) { if (delta < ts) { buffer_warn_return("[CPU: %d]ABSOLUTE TIME WENT BACKWARDS: last ts: %lld absolute ts: %lld clock:%pS\n", @@ -6466,7 +6474,7 @@ static void rb_clear_buffer_page(struct buffer_page *page) { local_set(&page->write, 0); local_set(&page->entries, 0); - rb_init_page(page->page); + rb_init_data_page(page->page); page->read = 0; } @@ -6951,7 +6959,7 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu) local_irq_restore(flags); if (bpage->data) { - rb_init_page(bpage->data); + rb_init_data_page(bpage->data); } else { bpage->data = alloc_cpu_data(cpu, cpu_buffer->buffer->subbuf_order); if (!bpage->data) { @@ -6976,8 +6984,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, struct buffer_data_read_page *data_page) { struct ring_buffer_per_cpu *cpu_buffer; - struct buffer_data_page *bpage = data_page->data; - struct page *page = virt_to_page(bpage); + struct buffer_data_page *dpage = data_page->data; + struct page *page = virt_to_page(dpage); unsigned long flags; if (!buffer || !buffer->buffers || !buffer->buffers[cpu]) @@ -6997,15 +7005,15 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, arch_spin_lock(&cpu_buffer->lock); if (!cpu_buffer->free_page) { - cpu_buffer->free_page = bpage; - bpage = NULL; + cpu_buffer->free_page = dpage; + dpage = NULL; } arch_spin_unlock(&cpu_buffer->lock); local_irq_restore(flags); out: - free_pages((unsigned long)bpage, data_page->order); + free_pages((unsigned long)dpage, data_page->order); kfree(data_page); } EXPORT_SYMBOL_GPL(ring_buffer_free_read_page); @@ -7050,7 +7058,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, { struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu]; struct ring_buffer_event *event; - struct buffer_data_page *bpage; + struct buffer_data_page *dpage; struct buffer_page *reader; unsigned long missed_events; unsigned int commit; @@ -7076,8 +7084,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (data_page->order != buffer->subbuf_order) return -1; - bpage = data_page->data; - if (!bpage) + dpage = data_page->data; + if (!dpage) return -1; guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); @@ -7143,7 +7151,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * We have already ensured there's enough space if this * is a time extend. */ size = rb_event_length(event); - memcpy(bpage->data + pos, rpage->data + rpos, size); + memcpy(dpage->data + pos, rpage->data + rpos, size); len -= size; @@ -7159,9 +7167,9 @@ int ring_buffer_read_page(struct trace_buffer *buffer, size = rb_event_ts_length(event); } while (len >= size); - /* update bpage */ - local_set(&bpage->commit, pos); - bpage->time_stamp = save_timestamp; + /* update dpage */ + local_set(&dpage->commit, pos); + dpage->time_stamp = save_timestamp; /* we copied everything to the beginning */ read = 0; @@ -7171,13 +7179,13 @@ int ring_buffer_read_page(struct trace_buffer *buffer, cpu_buffer->read_bytes += rb_page_size(reader); /* swap the pages */ - rb_init_page(bpage); - bpage = reader->page; + rb_init_data_page(dpage); + dpage = reader->page; reader->page = data_page->data; local_set(&reader->write, 0); local_set(&reader->entries, 0); reader->read = 0; - data_page->data = bpage; + data_page->data = dpage; /* * Use the real_end for the data size, @@ -7185,12 +7193,12 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * on the page. */ if (reader->real_end) - local_set(&bpage->commit, reader->real_end); + local_set(&dpage->commit, reader->real_end); } cpu_buffer->lost_events = 0; - commit = local_read(&bpage->commit); + commit = rb_data_page_commit(dpage); /* * Set a flag in the commit field if we lost events */ @@ -7199,19 +7207,19 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * missed events, then record it there. */ if (buffer->subbuf_size - commit >= sizeof(missed_events)) { - memcpy(&bpage->data[commit], &missed_events, + memcpy(&dpage->data[commit], &missed_events, sizeof(missed_events)); - local_add(RB_MISSED_STORED, &bpage->commit); + local_add(RB_MISSED_STORED, &dpage->commit); commit += sizeof(missed_events); } - local_add(RB_MISSED_EVENTS, &bpage->commit); + local_add(RB_MISSED_EVENTS, &dpage->commit); } /* * This page may be off to user land. Zero it out here. */ if (commit < buffer->subbuf_size) - memset(&bpage->data[commit], 0, buffer->subbuf_size - commit); + memset(&dpage->data[commit], 0, buffer->subbuf_size - commit); return read; } @@ -7842,7 +7850,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) if (missed_events) { if (cpu_buffer->reader_page != cpu_buffer->commit_page) { - struct buffer_data_page *bpage = reader->page; + struct buffer_data_page *dpage = reader->page; unsigned int commit; /* * Use the real_end for the data size, @@ -7850,18 +7858,18 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) * on the page. */ if (reader->real_end) - local_set(&bpage->commit, reader->real_end); + local_set(&dpage->commit, reader->real_end); /* * If there is room at the end of the page to save the * missed events, then record it there. */ commit = rb_page_size(reader); if (buffer->subbuf_size - commit >= sizeof(missed_events)) { - memcpy(&bpage->data[commit], &missed_events, + memcpy(&dpage->data[commit], &missed_events, sizeof(missed_events)); - local_add(RB_MISSED_STORED, &bpage->commit); + local_add(RB_MISSED_STORED, &dpage->commit); } - local_add(RB_MISSED_EVENTS, &bpage->commit); + local_add(RB_MISSED_EVENTS, &dpage->commit); } else if (!WARN_ONCE(cpu_buffer->reader_page == cpu_buffer->tail_page, "Reader on commit with %ld missed events", missed_events)) { From 8913e2a48b8d3c1ef9c99481e5725afca841762e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 22 May 2026 13:09:04 -0400 Subject: [PATCH 1949/5214] ring-buffer: Have dropped subbuffers be persistent across reboots When the persistent ring buffer detects a corrupted subbuffer, it will zero its size and report dropped pages in the dmesg, then it continues normally. But if a reboot happens without clearing or restarting tracing on the persistent ring buffer, the next boot will show no pages are dropped. If the persistent ring buffer is still the same, then it should still report dropped pages so the user knows that the buffer has missing events. Add the RB_MISSED_EVENTS flag to the commit value of the subbuffer so that the next boot will still show that pages were dropped. Link: https://patch.msgid.link/20260522171051.860780286@kernel.org Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index fd4a8dc5484e..db72969aefa9 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1915,7 +1915,7 @@ static int __rb_validate_buffer(struct buffer_page *bpage, int cpu, * Even after clearing these bits, a commit value greater than the * subbuf_size is considered invalid. */ - tail = rb_data_page_size(dpage); + tail = rb_data_page_commit(dpage); if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE) ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); else @@ -1929,7 +1929,7 @@ static int __rb_validate_buffer(struct buffer_page *bpage, int cpu, */ if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) { local_set(&bpage->entries, 0); - local_set(&dpage->commit, 0); + local_set(&dpage->commit, RB_MISSED_EVENTS); dpage->time_stamp = prev_ts ? prev_ts : next_ts; ret = -1; } else { @@ -3451,7 +3451,7 @@ rb_iter_head_event(struct ring_buffer_iter *iter) * is a mb(), which will synchronize with the rmb here. * (see rb_tail_page_update() and __rb_reserve_next()) */ - commit = rb_page_commit(iter_head_page); + commit = rb_page_size(iter_head_page); smp_rmb(); /* An event needs to be at least 8 bytes in size */ @@ -3480,7 +3480,7 @@ rb_iter_head_event(struct ring_buffer_iter *iter) /* Make sure the page didn't change since we read this */ if (iter->page_stamp != iter_head_page->page->time_stamp || - commit > rb_page_commit(iter_head_page)) + commit > rb_page_size(iter_head_page)) goto reset; iter->next_event = iter->head + length; @@ -5651,7 +5651,7 @@ int ring_buffer_iter_empty(struct ring_buffer_iter *iter) * (see rb_tail_page_update()) */ smp_rmb(); - commit = rb_page_commit(commit_page); + commit = rb_page_size(commit_page); /* We want to make sure that the commit page doesn't change */ smp_rmb(); @@ -5844,6 +5844,7 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) */ local_set(&cpu_buffer->reader_page->write, 0); local_set(&cpu_buffer->reader_page->entries, 0); + rb_init_data_page(cpu_buffer->reader_page->page); cpu_buffer->reader_page->real_end = 0; spin: From 97628ff073a8235b401b99e8619c121040f39251 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 22 May 2026 13:09:05 -0400 Subject: [PATCH 1950/5214] ring-buffer: Show persistent buffer dropped events in trace file When the persistent ring buffer is validated on boot up, if a subbuffer is deemed invalid, it resets the buffer and continues. Currently, these lost events are not shown in the trace file output. Have the trace iterator look for subbuffers that have the RB_MISSED_EVENTS set and set the iter->missed_events flag when it is detected. This will then have the trace file shows "LOST EVENTS" when it reads across a subbuffer that was corrupted and invalidated. For example: <...>-1016 [005] ...1. 6230.660403: preempt_disable: caller=__mod_memcg_state+0x1c8/0x200 parent=__mod_memcg_state+0x1c8/0x200 CPU:5 [LOST EVENTS] <...>-1016 [005] ..... 6230.660673: kmem_cache_alloc: call_site=__anon_vma_prepare+0x1ad/0x1e0 ptr=000000006e40294c name=anon_vma bytes_req=200 bytes_alloc=208 gfp_flags=GFP_KERNEL node=-1 accounted=true Link: https://patch.msgid.link/20260522171052.006276604@kernel.org Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index db72969aefa9..988915f035c7 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -3525,6 +3525,9 @@ static void rb_inc_iter(struct ring_buffer_iter *iter) else rb_inc_page(&iter->head_page); + if (rb_page_commit(iter->head_page) & RB_MISSED_EVENTS) + iter->missed_events = -1; + iter->page_stamp = iter->read_stamp = iter->head_page->page->time_stamp; iter->head = 0; iter->next_event = 0; @@ -7061,7 +7064,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, struct ring_buffer_event *event; struct buffer_data_page *dpage; struct buffer_page *reader; - unsigned long missed_events; + long missed_events; unsigned int commit; unsigned int read; u64 save_timestamp; @@ -7187,6 +7190,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, local_set(&reader->entries, 0); reader->read = 0; data_page->data = dpage; + if (!missed_events && rb_data_page_commit(dpage) & RB_MISSED_EVENTS) + missed_events = -1; /* * Use the real_end for the data size, @@ -7204,10 +7209,12 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * Set a flag in the commit field if we lost events */ if (missed_events) { - /* If there is room at the end of the page to save the + /* + * If there is room at the end of the page to save the * missed events, then record it there. */ - if (buffer->subbuf_size - commit >= sizeof(missed_events)) { + if (missed_events > 0 && + buffer->subbuf_size - commit >= sizeof(missed_events)) { memcpy(&dpage->data[commit], &missed_events, sizeof(missed_events)); local_add(RB_MISSED_STORED, &dpage->commit); From 8928e4a3be34bf053f9ef1cad67263604bf4f05e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 22 May 2026 13:09:06 -0400 Subject: [PATCH 1951/5214] ring-buffer: Show persistent buffer dropped events in trace_pipe file When the persistent ring buffer is validated on boot up, if a subbuffer is deemed invalid, it resets the buffer and continues. Have the code preserve the RB_MISSED_EVENTS flag in the commit portion of the subbuffer header and pass that back so that the trace_pipe file can show the missed events like the trace file does. For example: <...>-1242 [005] d.... 4429.120116: page_fault_user: address=0x7ffaebb6e728 ip=0x7ffaeb9d4960 error_code=0x7 <...>-1242 [005] ..... 4429.120124: mm_page_alloc: page=00000000055254f3 pfn=0x1373bd order=0 migratetype=1 gfp_flags=GFP_HIGHUSER_MOVABLE|__GFP_COMP <...>-1242 [005] d..2. 4429.120132: tlb_flush: pages:1 reason:local MM shootdown (3) CPU:5 [LOST EVENTS] <...>-1242 [005] d.... 4429.120661: page_fault_user: address=0x55ba7c2d0944 ip=0x55ba7c20cd02 error_code=0x7 <...>-1242 [005] ..... 4429.120669: mm_page_alloc: page=0000000005a02500 pfn=0x12b6e4 order=0 migratetype=1 gfp_flags=GFP_HIGHUSER_MOVABLE|__GFP_COMP <...>-1242 [005] d..2. 4429.120680: tlb_flush: pages:1 reason:local MM shootdown (3) Link: https://patch.msgid.link/20260522171052.156419479@kernel.org Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 56 +++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 988915f035c7..910f6b3adf74 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5801,6 +5801,7 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) struct buffer_page *reader = NULL; unsigned long overwrite; unsigned long flags; + int missed_events = 0; int nr_loops = 0; bool ret; @@ -5901,6 +5902,9 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) if (!ret) goto spin; + if (rb_page_commit(reader) & RB_MISSED_EVENTS) + missed_events = -1; + if (cpu_buffer->ring_meta) rb_update_meta_reader(cpu_buffer, reader); @@ -5965,6 +5969,8 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) */ smp_rmb(); + if (!cpu_buffer->lost_events) + cpu_buffer->lost_events = missed_events; return reader; } @@ -7066,6 +7072,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, struct buffer_page *reader; long missed_events; unsigned int commit; + unsigned int size; unsigned int read; u64 save_timestamp; bool force_memcpy; @@ -7101,7 +7108,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, event = rb_reader_event(cpu_buffer); read = reader->read; - commit = rb_page_size(reader); + commit = rb_page_commit(reader); + size = rb_page_size(reader); /* Check if any events were dropped */ missed_events = cpu_buffer->lost_events; @@ -7115,13 +7123,14 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * we must copy the data from the page to the buffer. * Otherwise, we can simply swap the page with the one passed in. */ - if (read || (len < (commit - read)) || + if (read || (len < (size - read)) || cpu_buffer->reader_page == cpu_buffer->commit_page || force_memcpy) { struct buffer_data_page *rpage = cpu_buffer->reader_page->page; unsigned int rpos = read; unsigned int pos = 0; - unsigned int size; + unsigned int event_size; + unsigned int flags = 0; /* * If a full page is expected, this can still be returned @@ -7130,19 +7139,22 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * the reader page. */ if (full && - (!read || (len < (commit - read)) || + (!read || (len < (size - read)) || cpu_buffer->reader_page == cpu_buffer->commit_page)) return -1; - if (len > (commit - read)) - len = (commit - read); + if (len > (size - read)) + len = (size - read); /* Always keep the time extend and data together */ - size = rb_event_ts_length(event); + event_size = rb_event_ts_length(event); - if (len < size) + if (len < event_size) return -1; + if (commit & RB_MISSED_EVENTS) + flags = RB_MISSED_EVENTS; + /* save the current timestamp, since the user will need it */ save_timestamp = cpu_buffer->read_stamp; @@ -7154,25 +7166,25 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * one or two events. * We have already ensured there's enough space if this * is a time extend. */ - size = rb_event_length(event); - memcpy(dpage->data + pos, rpage->data + rpos, size); + event_size = rb_event_length(event); + memcpy(dpage->data + pos, rpage->data + rpos, event_size); - len -= size; + len -= event_size; rb_advance_reader(cpu_buffer); rpos = reader->read; - pos += size; + pos += event_size; - if (rpos >= commit) + if (rpos >= event_size) break; event = rb_reader_event(cpu_buffer); /* Always keep the time extend and data together */ - size = rb_event_ts_length(event); - } while (len >= size); + event_size = rb_event_ts_length(event); + } while (len >= event_size); /* update dpage */ - local_set(&dpage->commit, pos); + local_set(&dpage->commit, pos | flags); dpage->time_stamp = save_timestamp; /* we copied everything to the beginning */ @@ -7204,7 +7216,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, cpu_buffer->lost_events = 0; - commit = rb_data_page_commit(dpage); + size = rb_data_page_size(dpage); /* * Set a flag in the commit field if we lost events */ @@ -7214,11 +7226,11 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * missed events, then record it there. */ if (missed_events > 0 && - buffer->subbuf_size - commit >= sizeof(missed_events)) { - memcpy(&dpage->data[commit], &missed_events, + buffer->subbuf_size - size >= sizeof(missed_events)) { + memcpy(&dpage->data[size], &missed_events, sizeof(missed_events)); local_add(RB_MISSED_STORED, &dpage->commit); - commit += sizeof(missed_events); + size += sizeof(missed_events); } local_add(RB_MISSED_EVENTS, &dpage->commit); } @@ -7226,8 +7238,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, /* * This page may be off to user land. Zero it out here. */ - if (commit < buffer->subbuf_size) - memset(&dpage->data[commit], 0, buffer->subbuf_size - commit); + if (size < buffer->subbuf_size) + memset(&dpage->data[size], 0, buffer->subbuf_size - size); return read; } From 1d224e7cbeac00292f15564310cdfa3976785b0e Mon Sep 17 00:00:00 2001 From: Li Wang Date: Wed, 22 Apr 2026 16:04:45 +0800 Subject: [PATCH 1952/5214] selftests/mm: respect build verbosity settings for 32/64-bit targets Patch series "selftests/mm: clean up build output and verbosity", v3. Currently, the build process for the mm selftests is unnecessarily noisy. First, it leaks raw compiler errors during the liburing feature probe if the headers are missing, which is confusing since the build system already handles this gracefully with a clear warning. Second, the specific 32-bit and 64-bit compilation targets ignore the standard kbuild verbosity settings, always printing their full compiler commands even during a default quiet build. This patch (of 2): The 32-bit and 64-bit compilation rules invoke $(CC) directly, bypassing the $(Q) quiet prefix and $(call msg,...) helper used by the rest of the selftests build system. This causes these rules to always print the full compiler command line, even when V=0 (the default). Wrap the commands with $(Q) and $(call msg,CC,,$@) to match the convention used by lib.mk, so that quiet and verbose builds behave consistently across all targets. ==== Build logs ==== ... CC merge CC rmap CC soft-dirty gcc -Wall -O2 -I /usr/src/25/tools/testing/selftests/../../.. -isystem /usr/src/25/tools/testing/selftests/../../../usr/include -isystem /usr/src/25/tools/testing/selftests/../../../tools/include/uapi -Wunreachable-code -U_FORTIFY_SOURCE -no-pie -D_GNU_SOURCE= -I/usr/src/25/tools/testing/selftests/../../../tools/testing/selftests -m32 -mxsave protection_keys.c vm_util.c thp_settings.c pkey_util.c -lrt -lpthread -lm -lrt -ldl -lm -o /usr/src/25/tools/testing/selftests/mm/protection_keys_32 gcc -Wall -O2 -I /usr/src/25/tools/testing/selftests/../../.. -isystem /usr/src/25/tools/testing/selftests/../../../usr/include -isystem /usr/src/25/tools/testing/selftests/../../../tools/include/uapi -Wunreachable-code -U_FORTIFY_SOURCE -no-pie -D_GNU_SOURCE= -I/usr/src/25/tools/testing/selftests/../../../tools/testing/selftests -m32 -mxsave pkey_sighandler_tests.c vm_util.c thp_settings.c pkey_util.c -lrt -lpthread -lm -lrt -ldl -lm -o /usr/src/25/tools/testing/selftests/mm/pkey_sighandler_tests_32 ... Link: https://lore.kernel.org/20260422080446.26020-1-wangli.ahau@gmail.com Link: https://lore.kernel.org/20260422080446.26020-2-wangli.ahau@gmail.com Signed-off-by: Li Wang Reported-by: Andrew Morton Tested-by: Andrew Morton Tested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index cd24596cdd27..6195770eba6e 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -216,7 +216,8 @@ ifeq ($(CAN_BUILD_I386),1) $(BINARIES_32): CFLAGS += -m32 -mxsave $(BINARIES_32): LDLIBS += -lrt -ldl -lm $(BINARIES_32): $(OUTPUT)/%_32: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(call msg,CC,,$@) + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-32,$(t)))) endif @@ -224,7 +225,8 @@ ifeq ($(CAN_BUILD_X86_64),1) $(BINARIES_64): CFLAGS += -m64 -mxsave $(BINARIES_64): LDLIBS += -lrt -ldl $(BINARIES_64): $(OUTPUT)/%_64: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(call msg,CC,,$@) + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-64,$(t)))) endif From 04cf82a741e096bf74e77bb8cf11e66481b4dcdf Mon Sep 17 00:00:00 2001 From: Li Wang Date: Wed, 22 Apr 2026 16:04:46 +0800 Subject: [PATCH 1953/5214] selftests/mm: suppress compiler error in liburing check When building the mm selftests on a system without liburing development headers, check_config.sh leaks a raw compiler error: /tmp/tmp.kIIOIqwe3n.c:2:10: fatal error: liburing.h: No such file or directory 2 | #include | ^~~~~~~~~~~~ Since this is an expected failure during the configuration probe, redirect the compiler output to /dev/null to hide it. And the build system prints a clear warning when this occurs: Warning: missing liburing support. Some tests will be skipped. Because the user is properly notified about the missing dependency, the raw compiler error is redundant and only confuse users. Additionally, update the Makefile to use $(Q) and $(call msg,...) for the check_config.sh execution. This aligns the probe with standard kbuild output formatting, providing a clean "CHK" message instead of printing the raw command during the build. Link: https://lore.kernel.org/20260422080446.26020-3-wangli.ahau@gmail.com Signed-off-by: Li Wang Tested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 3 ++- tools/testing/selftests/mm/check_config.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 6195770eba6e..18779045b7f6 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -263,7 +263,8 @@ $(OUTPUT)/migration: LDLIBS += -lnuma $(OUTPUT)/rmap: LDLIBS += -lnuma local_config.mk local_config.h: check_config.sh - CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh + $(call msg,CHK,config,$@) + $(Q)CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh EXTRA_CLEAN += local_config.mk local_config.h diff --git a/tools/testing/selftests/mm/check_config.sh b/tools/testing/selftests/mm/check_config.sh index b84c82bbf875..32beaefe279e 100755 --- a/tools/testing/selftests/mm/check_config.sh +++ b/tools/testing/selftests/mm/check_config.sh @@ -16,7 +16,7 @@ echo "#include " > $tmpfile_c echo "#include " >> $tmpfile_c echo "int func(void) { return 0; }" >> $tmpfile_c -$CC $CFLAGS -c $tmpfile_c -o $tmpfile_o +$CC $CFLAGS -c $tmpfile_c -o $tmpfile_o >/dev/null 2>&1 if [ -f $tmpfile_o ]; then echo "#define LOCAL_CONFIG_HAVE_LIBURING 1" > $OUTPUT_H_FILE From b001cf7d16dd18f14bd372a8018ecbf48197289d Mon Sep 17 00:00:00 2001 From: Hrushikesh Salunke Date: Wed, 22 Apr 2026 10:26:58 +0000 Subject: [PATCH 1954/5214] mm/page_alloc: replace kernel_init_pages() with batch page clearing When init_on_alloc is enabled, kernel_init_pages() clears every page one at a time via clear_highpage_kasan_tagged(), which incurs per-page kmap_local_page()/kunmap_local() overhead and prevents the architecture clearing primitive from operating on contiguous ranges. Introduce clear_highpages_kasan_tagged() as a static batch clearing helper in page_alloc.c that calls clear_pages() for the full contiguous range on !HIGHMEM systems, bypassing the per-page kmap overhead and allowing a single invocation of the arch clearing primitive across the entire allocation. The HIGHMEM path falls back to per-page clearing since those pages require kmap. Replace kernel_init_pages() with direct calls to the new helper, as it becomes a trivial wrapper. Allocating 8192 x 2MB HugeTLB pages (16GB) with init_on_alloc=1: Before: 0.445s After: 0.166s (-62.7%, 2.68x faster) Kernel time (sys) reduction per workload with init_on_alloc=1: Workload Before After Change Graph500 64C128T 30m 41.8s 15m 14.8s -50.3% Graph500 16C32T 15m 56.7s 9m 43.7s -39.0% Pagerank 32T 1m 58.5s 1m 12.8s -38.5% Pagerank 128T 2m 36.3s 1m 40.4s -35.7% [hsalunke@amd.com: move clear_highpages_kasan_tagged() to page_alloc.c] Link: https://lore.kernel.org/20260504063942.553438-1-hsalunke@amd.com Link: https://lore.kernel.org/20260422102729.166599-1-hsalunke@amd.com Signed-off-by: Hrushikesh Salunke Acked-by: Vlastimil Babka (SUSE) Acked-by: Zi Yan Acked-by: Pankaj Gupta Acked-by: David Hildenbrand (Arm) Acked-by: Lorenzo Stoakes Cc: Ankur Arora Cc: Bharata B Rao Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shivank Garg Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d49c254174da..bf53242d3db7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1211,14 +1211,18 @@ static inline bool should_skip_kasan_poison(struct page *page) return page_kasan_tag(page) == KASAN_TAG_KERNEL; } -static void kernel_init_pages(struct page *page, int numpages) +static void clear_highpages_kasan_tagged(struct page *page, int numpages) { - int i; - /* s390's use of memset() could override KASAN redzones. */ kasan_disable_current(); - for (i = 0; i < numpages; i++) - clear_highpage_kasan_tagged(page + i); + if (!IS_ENABLED(CONFIG_HIGHMEM)) { + clear_pages(kasan_reset_tag(page_address(page)), numpages); + } else { + int i; + + for (i = 0; i < numpages; i++) + clear_highpage_kasan_tagged(page + i); + } kasan_enable_current(); } @@ -1423,7 +1427,7 @@ __always_inline bool __free_pages_prepare(struct page *page, init = false; } if (init) - kernel_init_pages(page, 1 << order); + clear_highpages_kasan_tagged(page, 1 << order); /* * arch_free_page() can make the page's contents inaccessible. s390 @@ -1848,7 +1852,7 @@ inline void post_alloc_hook(struct page *page, unsigned int order, } /* If memory is still not initialized, initialize it now. */ if (init) - kernel_init_pages(page, 1 << order); + clear_highpages_kasan_tagged(page, 1 << order); set_page_owner(page, order, gfp_flags); page_table_check_alloc(page, order); From eb92d97f7e6a325607b9c3981131067c0469bfcf Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Thu, 23 Apr 2026 09:41:42 +0800 Subject: [PATCH 1955/5214] Revert "tmpfs: don't enable large folios if not supported" This reverts commit 5a90c155defa684f3a21f68c3f8e40c056e6114c. Currently, when shmem mounts are initialized, they only use 'sbinfo->huge' to determine whether the shmem mount supports large folios. However, for anonymous shmem, whether it supports large folios can be dynamically configured via sysfs interfaces, so setting or not setting mapping_set_large_folios() during initialization cannot accurately reflect whether anonymous shmem actually supports large folios, which has already caused some confusion[1]. Moreover, for tmpfs mounts, relying on 'sbinfo->huge' cannot keep the mapping_set_large_folios() setting consistent across all mappings in the entire tmpfs mount. In other words, under the same tmpfs mount, after remount, we might end up with some mappings supporting large folios (calling mapping_set_large_folios()) while others don't. After some investigation, I found that the write performance regression addressed by commit 5a90c155defa has already been fixed by the following commit 665575cff098b ("filemap: move prefaulting out of hot write path"). See the following test data: Base: dd if=/dev/zero of=/mnt/tmpfs/test bs=400K count=10485 (3.2 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=800K count=5242 (3.2 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=1600K count=2621 (3.1 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=2200K count=1906 (3.0 GB/s ) dd if=/dev/zero of=/mnt/tmpfs/test bs=3000K count=1398 (3.0 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=4500K count=932 (3.1 GB/s) Base + revert 5a90c155defa: dd if=/dev/zero of=/mnt/tmpfs/test bs=400K count=10485 (3.3 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=800K count=5242 (3.3 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=1600K count=2621 (3.2 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=2200K count=1906 (3.1 GB/s) dd if=/dev/zero of=/mnt/tmpfs/testbs=3000K count=1398 (3.0 GB/s) dd if=/dev/zero of=/mnt/tmpfs/test bs=4500K count=932 (3.1 GB/s) The data is basically consistent with minor fluctuation noise. So we can now safely revert commit 5a90c155defa to set mapping_set_large_folios() for all shmem mounts unconditionally. Link: https://lore.kernel.org/b2c7deee259a94b0d00a7c320d8d24d2c421f761.1776908112.git.baolin.wang@linux.alibaba.com Link: https://lore.kernel.org/all/ec927492-4577-4192-8fad-85eb1bb43121@linux.alibaba.com/ [1] Link: https://lore.kernel.org/all/116df9f9-4db7-40d4-a4a4-30a87c0feffa@linux.alibaba.com/ Fixes: 5a90c155defa ("tmpfs: don't enable large folios if not supported") Signed-off-by: Baolin Wang Acked-by: Zi Yan Reviewed-by: Kefeng Wang Reviewed-by: Lance Yang Acked-by: David Hildenbrand (Arm) Acked-by: Lorenzo Stoakes Cc: Hugh Dickins Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/shmem.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 3b5dc21b323c..bab3529af23c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3101,10 +3101,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap, cache_no_acl(inode); if (sbinfo->noswap) mapping_set_unevictable(inode->i_mapping); - - /* Don't consider 'deny' for emergencies and 'force' for testing */ - if (sbinfo->huge) - mapping_set_large_folios(inode->i_mapping); + mapping_set_large_folios(inode->i_mapping); switch (mode & S_IFMT) { default: From 085a7acf732f6040fd36b002e6a49c90b76db41c Mon Sep 17 00:00:00 2001 From: "Barry Song (Xiaomi)" Date: Thu, 23 Apr 2026 11:49:17 +0800 Subject: [PATCH 1956/5214] mm/huge_memory: fix outdated comment about freeing subpages in __folio_split The comment appears to be outdated. add_to_swap() no longer exists, and the explanation of why we need to call put_page() after splitting could be made more general. Link: https://lore.kernel.org/20260423034917.8234-1-baohua@kernel.org Signed-off-by: Barry Song (Xiaomi) Acked-by: David Hildenbrand (Arm) Acked-by: Zi Yan Cc: Lorenzo Stoakes Cc: Baolin Wang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Dev Jain Cc: Lance Yang Cc: Chris Li Cc: Kairui Song Cc: Kemeng Shi Cc: Nhat Pham Cc: Baoquan He Cc: Youngjun Park Signed-off-by: Andrew Morton --- mm/huge_memory.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 970e077019b7..4586f3ccb133 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4190,11 +4190,10 @@ static int __folio_split(struct folio *folio, unsigned int new_order, folio_unlock(new_folio); /* - * Subpages may be freed if there wasn't any mapping - * like if add_to_swap() is running on a lru page that - * had its mapping zapped. And freeing these pages - * requires taking the lru_lock so we do the put_page - * of the tail pages after the split is complete. + * Subpages whose mapping has been zapped may be freed + * earlier, but freeing them requires taking the + * lru_lock, so we defer put_page() on tail pages until + * after the split completes. */ free_folio_and_swap_cache(new_folio); } From 8613803cf5d532316aa886f17066c5e5968ea21e Mon Sep 17 00:00:00 2001 From: Chengkaitao Date: Thu, 23 Apr 2026 18:14:41 +0800 Subject: [PATCH 1957/5214] mm: convert vmemmap_p?d_populate() to static functions Since the vmemmap_p?d_populate functions are unused outside the mm subsystem, we can remove their external declarations and convert them to static functions. Link: https://lore.kernel.org/20260423101441.7089-1-kaitao.cheng@linux.dev Signed-off-by: Chengkaitao Acked-by: David Hildenbrand (arm) Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador Cc: David Hildenbrand Cc: Liam Howlett Cc: Lorenzo Stoakes (Oracle) Cc: Michal Hocko Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- include/linux/mm.h | 7 ------- mm/sparse-vmemmap.c | 10 +++++----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 06bbe9eba636..e3b6112a8d79 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4860,13 +4860,6 @@ unsigned long section_map_size(void); struct page * __populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap); -pgd_t *vmemmap_pgd_populate(unsigned long addr, int node); -p4d_t *vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node); -pud_t *vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node); -pmd_t *vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node); -pte_t *vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, - struct vmem_altmap *altmap, unsigned long ptpfn, - unsigned long flags); void *vmemmap_alloc_block(unsigned long size, int node); struct vmem_altmap; void *vmemmap_alloc_block_buf(unsigned long size, int node, diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 6eadb9d116e4..3c35d2303a61 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -151,7 +151,7 @@ void __meminit vmemmap_verify(pte_t *pte, int node, start, end - 1); } -pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, +static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, struct vmem_altmap *altmap, unsigned long ptpfn, unsigned long flags) { @@ -195,7 +195,7 @@ static void * __meminit vmemmap_alloc_block_zero(unsigned long size, int node) return p; } -pmd_t * __meminit vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node) +static pmd_t * __meminit vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node) { pmd_t *pmd = pmd_offset(pud, addr); if (pmd_none(*pmd)) { @@ -208,7 +208,7 @@ pmd_t * __meminit vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node) return pmd; } -pud_t * __meminit vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node) +static pud_t * __meminit vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node) { pud_t *pud = pud_offset(p4d, addr); if (pud_none(*pud)) { @@ -221,7 +221,7 @@ pud_t * __meminit vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node) return pud; } -p4d_t * __meminit vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node) +static p4d_t * __meminit vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node) { p4d_t *p4d = p4d_offset(pgd, addr); if (p4d_none(*p4d)) { @@ -234,7 +234,7 @@ p4d_t * __meminit vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node) return p4d; } -pgd_t * __meminit vmemmap_pgd_populate(unsigned long addr, int node) +static pgd_t * __meminit vmemmap_pgd_populate(unsigned long addr, int node) { pgd_t *pgd = pgd_offset_k(addr); if (pgd_none(*pgd)) { From 4221aadd720bef7df1268391d6eb1ea1f0476b38 Mon Sep 17 00:00:00 2001 From: Bunyod Suvonov Date: Thu, 23 Apr 2026 18:37:53 +0800 Subject: [PATCH 1958/5214] mm/vmscan: add balance_pgdat begin/end tracepoints Vmscan has six main reclaim entry points: try_to_free_pages() for direct reclaim, try_to_free_mem_cgroup_pages() for memcg reclaim, mem_cgroup_shrink_node() for memcg soft limit reclaim, node_reclaim() for node reclaim, shrink_all_memory() for hibernation reclaim, and balance_pgdat() for kswapd reclaim. All of them, except for shrink_all_memory() and balance_pgdat(), already have begin/end tracepoints. This makes it harder to trace which reclaim path is responsible for memory reclaim activity, because kswapd reclaim cannot be identified as cleanly as other reclaim entry points, even though it is the main background reclaim path under memory pressure. There may be no need to trace shrink_all_memory() as it is primarily used during hibernation. So this patch adds the missing tracepoint pair for balance_pgdat(). The begin tracepoint records the node id, requested reclaim order, and the requested classzone bound (highest_zoneidx). The end tracepoint records the node id, the reclaim order that balance_pgdat() finished with, the requested classzone bound, and nr_reclaimed. Together, they show the requested reclaim order and classzone bound, whether reclaim fell back to a lower order, and how much reclaim work was done. The end tracepoint also records highest_zoneidx even though it does not change within a balance_pgdat() invocation. This keeps the end event self-contained, so users can analyze reclaim results directly from end events without depending on begin/end correlation, which is less convenient when tracing is filtered or records are dropped. It also makes it straightforward to relate nr_reclaimed and the final reclaim order to the requested classzone bound. Link: https://lore.kernel.org/20260424031418.174597-1-b.suvonov@sjtu.edu.cn Link: https://lore.kernel.org/20260423103753.546582-1-b.suvonov@sjtu.edu.cn Signed-off-by: Bunyod Suvonov Acked-by: Shakeel Butt Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Lorenzo Stoakes Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Qi Zheng Cc: Steven Rostedt Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- include/trace/events/vmscan.h | 52 +++++++++++++++++++++++++++++++++++ mm/vmscan.c | 5 ++++ 2 files changed, 57 insertions(+) diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index 4445a8d9218d..b4bf7b8def1f 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -96,6 +96,58 @@ TRACE_EVENT(mm_vmscan_kswapd_wake, __entry->order) ); +TRACE_EVENT(mm_vmscan_balance_pgdat_begin, + + TP_PROTO(int nid, int order, int highest_zoneidx), + + TP_ARGS(nid, order, highest_zoneidx), + + TP_STRUCT__entry( + __field(int, nid) + __field(int, order) + __field(int, highest_zoneidx) + ), + + TP_fast_assign( + __entry->nid = nid; + __entry->order = order; + __entry->highest_zoneidx = highest_zoneidx; + ), + + TP_printk("nid=%d order=%d highest_zoneidx=%-8s", + __entry->nid, + __entry->order, + __print_symbolic(__entry->highest_zoneidx, ZONE_TYPE)) +); + +TRACE_EVENT(mm_vmscan_balance_pgdat_end, + + TP_PROTO(int nid, int order, int highest_zoneidx, + unsigned long nr_reclaimed), + + TP_ARGS(nid, order, highest_zoneidx, nr_reclaimed), + + TP_STRUCT__entry( + __field(int, nid) + __field(int, order) + __field(int, highest_zoneidx) + __field(unsigned long, nr_reclaimed) + ), + + TP_fast_assign( + __entry->nid = nid; + __entry->order = order; + __entry->highest_zoneidx = highest_zoneidx; + __entry->nr_reclaimed = nr_reclaimed; + ), + + TP_printk("nid=%d order=%d highest_zoneidx=%-8s nr_reclaimed=%lu", + __entry->nid, + __entry->order, + __print_symbolic(__entry->highest_zoneidx, ZONE_TYPE), + __entry->nr_reclaimed) +); + TRACE_EVENT(mm_vmscan_wakeup_kswapd, TP_PROTO(int nid, int zid, int order, gfp_t gfp_flags), diff --git a/mm/vmscan.c b/mm/vmscan.c index bd1b1aa12581..b2d89ed69d22 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -7121,6 +7121,8 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx) .may_unmap = 1, }; + trace_mm_vmscan_balance_pgdat_begin(pgdat->node_id, order, + highest_zoneidx); set_task_reclaim_state(current, &sc.reclaim_state); psi_memstall_enter(&pflags); __fs_reclaim_acquire(_THIS_IP_); @@ -7314,6 +7316,9 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx) psi_memstall_leave(&pflags); set_task_reclaim_state(current, NULL); + trace_mm_vmscan_balance_pgdat_end(pgdat->node_id, sc.order, + highest_zoneidx, sc.nr_reclaimed); + /* * Return the order kswapd stopped reclaiming at as * prepare_kswapd_sleep() takes it into account. If another caller From 4aa4abf1f14bd6d0748b7d35a803cc2376a8e20b Mon Sep 17 00:00:00 2001 From: Ryan Roberts Date: Wed, 1 Apr 2026 11:16:19 +0100 Subject: [PATCH 1959/5214] mm/page_alloc: optimize free_contig_range() Patch series "mm: Free contiguous order-0 pages efficiently", v6. A recent change to vmalloc caused some performance benchmark regressions (see [1]). I'm attempting to fix that (and at the same time significantly improve beyond the baseline) by freeing a contiguous set of order-0 pages as a batch. At the same time I observed that free_contig_range() was essentially doing the same thing as vfree() so I've fixed it there too. While at it, optimize the __free_contig_frozen_range() as well. Check that the contiguous range falls in the same section. If they aren't enabled, the if conditions get optimized out by the compiler as memdesc_section() returns 0. See num_pages_contiguous() for more details about it. This patch (of 3): Decompose the range of order-0 pages to be freed into the set of largest possible power-of-2 size and aligned chunks and free them to the pcp or buddy. This improves on the previous approach which freed each order-0 page individually in a loop. Testing shows performance to be improved by more than 10x in some cases. Since each page is order-0, we must decrement each page's reference count individually and only consider the page for freeing as part of a high order chunk if the reference count goes to zero. Additionally free_pages_prepare() must be called for each individual order-0 page too, so that the struct page state and global accounting state can be appropriately managed. But once this is done, the resulting high order chunks can be freed as a unit to the pcp or buddy. This significantly speeds up the free operation but also has the side benefit that high order blocks are added to the pcp instead of each page ending up on the pcp order-0 list; memory remains more readily available in high orders. vmalloc will shortly become a user of this new optimized free_contig_range() since it aggressively allocates high order non-compound pages, but then calls split_page() to end up with contiguous order-0 pages. These can now be freed much more efficiently. The execution time of the following function was measured in a server class arm64 machine: static int page_alloc_high_order_test(void) { unsigned int order = HPAGE_PMD_ORDER; struct page *page; int i; for (i = 0; i < 100000; i++) { page = alloc_pages(GFP_KERNEL, order); if (!page) return -1; split_page(page, order); free_contig_range(page_to_pfn(page), 1UL << order); } return 0; } Execution time before: 4097358 usec Execution time after: 729831 usec Perf trace before: 99.63% 0.00% kthreadd [kernel.kallsyms] [.] kthread | ---kthread 0xffffb33c12a26af8 | |--98.13%--0xffffb33c12a26060 | | | |--97.37%--free_contig_range | | | | | |--94.93%--___free_pages | | | | | | | |--55.42%--__free_frozen_pages | | | | | | | | | --43.20%--free_frozen_page_commit | | | | | | | | | --35.37%--_raw_spin_unlock_irqrestore | | | | | | | |--11.53%--_raw_spin_trylock | | | | | | | |--8.19%--__preempt_count_dec_and_test | | | | | | | |--5.64%--_raw_spin_unlock | | | | | | | |--2.37%--__get_pfnblock_flags_mask.isra.0 | | | | | | | --1.07%--free_frozen_page_commit | | | | | --1.54%--__free_frozen_pages | | | --0.77%--___free_pages | --0.98%--0xffffb33c12a26078 alloc_pages_noprof Perf trace after: 8.42% 2.90% kthreadd [kernel.kallsyms] [k] __free_contig_range | |--5.52%--__free_contig_range | | | |--5.00%--free_prepared_contig_range | | | | | |--1.43%--__free_frozen_pages | | | | | | | --0.51%--free_frozen_page_commit | | | | | |--1.08%--_raw_spin_trylock | | | | | --0.89%--_raw_spin_unlock | | | --0.52%--free_pages_prepare | --2.90%--ret_from_fork kthread 0xffffae1c12abeaf8 0xffffae1c12abe7a0 | --2.69%--vfree __free_contig_range Link: https://lore.kernel.org/20260401101634.2868165-1-usama.anjum@arm.com Link: https://lore.kernel.org/20260401101634.2868165-2-usama.anjum@arm.com Link: https://lore.kernel.org/all/66919a28-bc81-49c9-b68f-dd7c73395a0d@arm.com [1] Signed-off-by: Ryan Roberts Co-developed-by: Muhammad Usama Anjum Signed-off-by: Muhammad Usama Anjum Acked-by: David Hildenbrand (Arm) Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Zi Yan Cc: Brendan Jackman Cc: David Sterba Cc: Johannes Weiner Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nick Terrell Cc: Suren Baghdasaryan Cc: "Uladzislau Rezki (Sony)" Cc: Vishal Moola (Oracle) Signed-off-by: Andrew Morton --- include/linux/gfp.h | 2 + mm/page_alloc.c | 112 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 51ef13ed756e..87259e309dee 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -467,6 +467,8 @@ void free_contig_frozen_range(unsigned long pfn, unsigned long nr_pages); void free_contig_range(unsigned long pfn, unsigned long nr_pages); #endif +void __free_contig_range(unsigned long pfn, unsigned long nr_pages); + DEFINE_FREE(free_page, void *, free_page((unsigned long)_T)) #endif /* __LINUX_GFP_H */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index bf53242d3db7..9d4fb1ea084a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -90,6 +90,9 @@ typedef int __bitwise fpi_t; /* Free the page without taking locks. Rely on trylock only. */ #define FPI_TRYLOCK ((__force fpi_t)BIT(2)) +/* free_pages_prepare() has already been called for page(s) being freed. */ +#define FPI_PREPARED ((__force fpi_t)BIT(3)) + /* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */ static DEFINE_MUTEX(pcp_batch_high_lock); #define MIN_PERCPU_PAGELIST_HIGH_FRACTION (8) @@ -1307,8 +1310,8 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) #endif /* CONFIG_MEM_ALLOC_PROFILING */ -__always_inline bool __free_pages_prepare(struct page *page, - unsigned int order, fpi_t fpi_flags) +static __always_inline bool __free_pages_prepare(struct page *page, + unsigned int order, fpi_t fpi_flags) { int bad = 0; bool skip_kasan_poison = should_skip_kasan_poison(page); @@ -1316,6 +1319,9 @@ __always_inline bool __free_pages_prepare(struct page *page, bool compound = PageCompound(page); struct folio *folio = page_folio(page); + if (fpi_flags & FPI_PREPARED) + return true; + VM_BUG_ON_PAGE(PageTail(page), page); trace_mm_page_free(page, order); @@ -6762,6 +6768,105 @@ void __init page_alloc_sysctl_init(void) register_sysctl_init("vm", page_alloc_sysctl_table); } +static void free_prepared_contig_range(struct page *page, + unsigned long nr_pages) +{ + unsigned long pfn = page_to_pfn(page); + + while (nr_pages) { + unsigned int order; + + /* We are limited by the largest buddy order. */ + order = pfn ? __ffs(pfn) : MAX_PAGE_ORDER; + /* Don't exceed the number of pages to free. */ + order = min_t(unsigned int, order, ilog2(nr_pages)); + order = min_t(unsigned int, order, MAX_PAGE_ORDER); + + /* + * Free the chunk as a single block. Our caller has already + * called free_pages_prepare() for each order-0 page. + */ + __free_frozen_pages(page, order, FPI_PREPARED); + + pfn += 1UL << order; + page += 1UL << order; + nr_pages -= 1UL << order; + } +} + +static void __free_contig_range_common(unsigned long pfn, unsigned long nr_pages, + bool is_frozen) +{ + struct page *page, *start = NULL; + unsigned long nr_start = 0; + unsigned long start_sec; + unsigned long i; + + for (i = 0; i < nr_pages; i++) { + bool can_free = true; + + /* + * Contiguous PFNs might not have contiguous "struct pages" + * in some kernel configs: page++ across a section boundary + * is undefined. Use pfn_to_page() for each PFN. + */ + page = pfn_to_page(pfn + i); + + VM_WARN_ON_ONCE(PageHead(page)); + VM_WARN_ON_ONCE(PageTail(page)); + + if (!is_frozen) + can_free = put_page_testzero(page); + + if (can_free) + can_free = free_pages_prepare(page, 0); + + if (!can_free) { + if (start) { + free_prepared_contig_range(start, i - nr_start); + start = NULL; + } + continue; + } + + if (start && memdesc_section(page->flags) != start_sec) { + free_prepared_contig_range(start, i - nr_start); + start = page; + nr_start = i; + start_sec = memdesc_section(page->flags); + } else if (!start) { + start = page; + nr_start = i; + start_sec = memdesc_section(page->flags); + } + } + + if (start) + free_prepared_contig_range(start, nr_pages - nr_start); +} + +/** + * __free_contig_range - Free contiguous range of order-0 pages. + * @pfn: Page frame number of the first page in the range. + * @nr_pages: Number of pages to free. + * + * For each order-0 struct page in the physically contiguous range, put a + * reference. Free any page who's reference count falls to zero. The + * implementation is functionally equivalent to, but significantly faster than + * calling __free_page() for each struct page in a loop. + * + * Memory allocated with alloc_pages(order>=1) then subsequently split to + * order-0 with split_page() is an example of appropriate contiguous pages that + * can be freed with this API. + * + * Context: May be called in interrupt context or while holding a normal + * spinlock, but not in NMI context or while holding a raw spinlock. + */ +void __free_contig_range(unsigned long pfn, unsigned long nr_pages) +{ + __free_contig_range_common(pfn, nr_pages, /* is_frozen= */ false); +} + #ifdef CONFIG_CONTIG_ALLOC /* Usage: See admin-guide/dynamic-debug-howto.rst */ static void alloc_contig_dump_pages(struct list_head *page_list) @@ -7308,8 +7413,7 @@ void free_contig_range(unsigned long pfn, unsigned long nr_pages) if (WARN_ON_ONCE(PageHead(pfn_to_page(pfn)))) return; - for (; nr_pages--; pfn++) - __free_page(pfn_to_page(pfn)); + __free_contig_range(pfn, nr_pages); } EXPORT_SYMBOL(free_contig_range); #endif /* CONFIG_CONTIG_ALLOC */ From 60ced5818f64ac356620d1ad3e0d473c457dbf5b Mon Sep 17 00:00:00 2001 From: Ryan Roberts Date: Wed, 1 Apr 2026 11:16:20 +0100 Subject: [PATCH 1960/5214] vmalloc: optimize vfree with free_pages_bulk() Whenever vmalloc allocates high order pages (e.g. for a huge mapping) it must immediately split_page() to order-0 so that it remains compatible with users that want to access the underlying struct page. Commit a06157804399 ("mm/vmalloc: request large order pages from buddy allocator") recently made it much more likely for vmalloc to allocate high order pages which are subsequently split to order-0. Unfortunately this had the side effect of causing performance regressions for tight vmalloc/vfree loops (e.g. test_vmalloc.ko benchmarks). See Closes: tag. This happens because the high order pages must be gotten from the buddy but then because they are split to order-0, when they are freed they are freed to the order-0 pcp. Previously allocation was for order-0 pages so they were recycled from the pcp. It would be preferable if when vmalloc allocates an (e.g.) order-3 page that it also frees that order-3 page to the order-3 pcp, then the regression could be removed. So let's do exactly that; update stats separately first as coalescing is hard to do correctly without complexity. Use free_pages_bulk() which uses the new __free_contig_range() API to batch-free contiguous ranges of pfns. This not only removes the regression, but significantly improves performance of vfree beyond the baseline. A selection of test_vmalloc benchmarks running on arm64 server class system. mm-new is the baseline. Commit a06157804399 ("mm/vmalloc: request large order pages from buddy allocator") was added in v6.19-rc1 where we see regressions. Then with this change performance is much better. (>0 is faster, <0 is slower, (R)/(I) = statistically significant Regression/Improvement): +-----------------+----------------------------------------------------------+-------------------+--------------------+ | Benchmark | Result Class | mm-new | this series | +=================+==========================================================+===================+====================+ | micromm/vmalloc | fix_align_alloc_test: p:1, h:0, l:500000 (usec) | 1331843.33 | (I) 67.17% | | | fix_size_alloc_test: p:1, h:0, l:500000 (usec) | 415907.33 | -5.14% | | | fix_size_alloc_test: p:4, h:0, l:500000 (usec) | 755448.00 | (I) 53.55% | | | fix_size_alloc_test: p:16, h:0, l:500000 (usec) | 1591331.33 | (I) 57.26% | | | fix_size_alloc_test: p:16, h:1, l:500000 (usec) | 1594345.67 | (I) 68.46% | | | fix_size_alloc_test: p:64, h:0, l:100000 (usec) | 1071826.00 | (I) 79.27% | | | fix_size_alloc_test: p:64, h:1, l:100000 (usec) | 1018385.00 | (I) 84.17% | | | fix_size_alloc_test: p:256, h:0, l:100000 (usec) | 3970899.67 | (I) 77.01% | | | fix_size_alloc_test: p:256, h:1, l:100000 (usec) | 3821788.67 | (I) 89.44% | | | fix_size_alloc_test: p:512, h:0, l:100000 (usec) | 7795968.00 | (I) 82.67% | | | fix_size_alloc_test: p:512, h:1, l:100000 (usec) | 6530169.67 | (I) 118.09% | | | full_fit_alloc_test: p:1, h:0, l:500000 (usec) | 626808.33 | -0.98% | | | kvfree_rcu_1_arg_vmalloc_test: p:1, h:0, l:500000 (usec) | 532145.67 | -1.68% | | | kvfree_rcu_2_arg_vmalloc_test: p:1, h:0, l:500000 (usec) | 537032.67 | -0.96% | | | long_busy_list_alloc_test: p:1, h:0, l:500000 (usec) | 8805069.00 | (I) 74.58% | | | pcpu_alloc_test: p:1, h:0, l:500000 (usec) | 500824.67 | 4.35% | | | random_size_align_alloc_test: p:1, h:0, l:500000 (usec) | 1637554.67 | (I) 76.99% | | | random_size_alloc_test: p:1, h:0, l:500000 (usec) | 4556288.67 | (I) 72.23% | | | vm_map_ram_test: p:1, h:0, l:500000 (usec) | 107371.00 | -0.70% | +-----------------+----------------------------------------------------------+-------------------+--------------------+ Link: https://lore.kernel.org/20260401101634.2868165-3-usama.anjum@arm.com Fixes: a06157804399 ("mm/vmalloc: request large order pages from buddy allocator") Closes: https://lore.kernel.org/all/66919a28-bc81-49c9-b68f-dd7c73395a0d@arm.com/ Signed-off-by: Ryan Roberts Co-developed-by: Muhammad Usama Anjum Signed-off-by: Muhammad Usama Anjum Acked-by: Vlastimil Babka (SUSE) Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Reviewed-by: Uladzislau Rezki (Sony) Cc: Brendan Jackman Cc: David Sterba Cc: Johannes Weiner Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nick Terrell Cc: Suren Baghdasaryan Cc: Vishal Moola (Oracle) Signed-off-by: Andrew Morton --- include/linux/gfp.h | 2 ++ mm/page_alloc.c | 28 ++++++++++++++++++++++++++++ mm/vmalloc.c | 16 +++++----------- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 87259e309dee..cdf95a9f0b87 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -239,6 +239,8 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid, struct page **page_array); #define __alloc_pages_bulk(...) alloc_hooks(alloc_pages_bulk_noprof(__VA_ARGS__)) +void free_pages_bulk(struct page **page_array, unsigned long nr_pages); + unsigned long alloc_pages_bulk_mempolicy_noprof(gfp_t gfp, unsigned long nr_pages, struct page **page_array); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9d4fb1ea084a..91bef811a771 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5189,6 +5189,34 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid, } EXPORT_SYMBOL_GPL(alloc_pages_bulk_noprof); +/* + * free_pages_bulk - Free an array of order-0 pages + * @page_array: Array of pages to free + * @nr_pages: The number of pages in the array + * + * Free the order-0 pages. Adjacent entries whose PFNs form a contiguous + * run are released with a single __free_contig_range() call. + * + * This assumes page_array is sorted in ascending PFN order. Without that, + * the function still frees all pages, but contiguous runs may not be + * detected and the freeing pattern can degrade to freeing one page at a + * time. + * + * Context: Sleepable process context only; calls cond_resched() + */ +void free_pages_bulk(struct page **page_array, unsigned long nr_pages) +{ + while (nr_pages) { + unsigned long nr_contig = num_pages_contiguous(page_array, nr_pages); + + __free_contig_range(page_to_pfn(*page_array), nr_contig); + + nr_pages -= nr_contig; + page_array += nr_contig; + cond_resched(); + } +} + /* * This is the 'heart' of the zoned buddy allocator. */ diff --git a/mm/vmalloc.c b/mm/vmalloc.c index bb6ae08d18f5..99fce4f9f6e4 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3459,19 +3459,13 @@ void vfree(const void *addr) if (unlikely(vm->flags & VM_FLUSH_RESET_PERMS)) vm_reset_perms(vm); - for (i = 0; i < vm->nr_pages; i++) { - struct page *page = vm->pages[i]; - BUG_ON(!page); - /* - * High-order allocs for huge vmallocs are split, so - * can be freed as an array of order-0 allocations - */ - if (!(vm->flags & VM_MAP_PUT_PAGES)) - mod_lruvec_page_state(page, NR_VMALLOC, -1); - __free_page(page); - cond_resched(); + if (!(vm->flags & VM_MAP_PUT_PAGES)) { + for (i = 0; i < vm->nr_pages; i++) + mod_lruvec_page_state(vm->pages[i], NR_VMALLOC, -1); } + free_pages_bulk(vm->pages, vm->nr_pages); + kvfree(vm->pages); kfree(vm); } From b971e47fd98f97d66ab3b1c0864916d844fa0104 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Wed, 1 Apr 2026 11:16:21 +0100 Subject: [PATCH 1961/5214] mm/page_alloc: optimize __free_contig_frozen_range() Apply the same batch-freeing optimization from free_contig_range() to the frozen page path. The previous __free_contig_frozen_range() freed each order-0 page individually via free_frozen_pages(), which is slow for the same reason the old free_contig_range() was: each page goes to the order-0 pcp list rather than being coalesced into higher-order blocks. Rewrite __free_contig_frozen_range() to call free_pages_prepare() for each order-0 page, then batch the prepared pages into the largest possible power-of-2 aligned chunks via free_prepared_contig_range(). If free_pages_prepare() fails (e.g. HWPoison, bad page) the page is deliberately not freed; it should not be returned to the allocator. I've tested CMA through debugfs. The test allocates 16384 pages per allocation for several iterations. There is 3.5x improvement. Before: 1406 usec per iteration After: 402 usec per iteration Before: 70.89% 0.69% cma [kernel.kallsyms] [.] free_contig_frozen_range | |--70.20%--free_contig_frozen_range | | | |--46.41%--__free_frozen_pages | | | | | --36.18%--free_frozen_page_commit | | | | | --29.63%--_raw_spin_unlock_irqrestore | | | |--8.76%--_raw_spin_trylock | | | |--7.03%--__preempt_count_dec_and_test | | | |--4.57%--_raw_spin_unlock | | | |--1.96%--__get_pfnblock_flags_mask.isra.0 | | | --1.15%--free_frozen_page_commit | --0.69%--el0t_64_sync After: 23.57% 0.00% cma [kernel.kallsyms] [.] free_contig_frozen_range | ---free_contig_frozen_range | |--20.45%--__free_contig_frozen_range | | | |--17.77%--free_pages_prepare | | | --0.72%--free_prepared_contig_range | | | --0.55%--__free_frozen_pages | --3.12%--free_pages_prepare Link: https://lore.kernel.org/20260401101634.2868165-4-usama.anjum@arm.com Signed-off-by: Muhammad Usama Anjum Acked-by: David Hildenbrand (Arm) Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Zi Yan Suggested-by: Zi Yan Cc: Brendan Jackman Cc: David Sterba Cc: Johannes Weiner Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nick Terrell Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: "Uladzislau Rezki (Sony)" Cc: Vishal Moola (Oracle) Signed-off-by: Andrew Morton --- mm/page_alloc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 91bef811a771..a81ae5781036 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7032,8 +7032,7 @@ static int __alloc_contig_verify_gfp_mask(gfp_t gfp_mask, gfp_t *gfp_cc_mask) static void __free_contig_frozen_range(unsigned long pfn, unsigned long nr_pages) { - for (; nr_pages--; pfn++) - free_frozen_pages(pfn_to_page(pfn), 0); + __free_contig_range_common(pfn, nr_pages, /* is_frozen= */ true); } /** From 5c5bc5e326fe4bcfe1c6f5c69a0b8df809bdc2e4 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Tue, 21 Apr 2026 07:17:54 +0200 Subject: [PATCH 1962/5214] mm/gup: cleanup pgtable entry accessors PMD and PUD entries revalidation has the same semantics as PTE entry revalidation. Convert the remaining direct entry dereferences to the corresponding accessors. The PTE validation in gup_fast_pte_range() is inconsistent with the prior value acquisition in the sense that it drops the lockless access semantics. Use the lockless accessor not only for the PTE, but also for the PMD validation, which is likewise inconsistent with the prior value acquisition in gup_fast_pmd_range(). Link: https://lore.kernel.org/20260421051754.1691221-1-agordeev@linux.ibm.com Signed-off-by: Alexander Gordeev Acked-by: David Hildenbrand (Arm) Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Jason Gunthorpe Cc: John Hubbard Cc: Kevin Brodsky Cc: Peter Xu Cc: Ryan Roberts Cc: Vasily Gorbik Signed-off-by: Andrew Morton --- mm/gup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/gup.c b/mm/gup.c index ad9ded39609c..0692119b7904 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -2865,8 +2865,8 @@ static int gup_fast_pte_range(pmd_t pmd, pmd_t *pmdp, unsigned long addr, if (!folio) goto pte_unmap; - if (unlikely(pmd_val(pmd) != pmd_val(*pmdp)) || - unlikely(pte_val(pte) != pte_val(ptep_get(ptep)))) { + if (unlikely(pmd_val(pmd) != pmd_val(pmdp_get_lockless(pmdp))) || + unlikely(pte_val(pte) != pte_val(ptep_get_lockless(ptep)))) { gup_put_folio(folio, 1, flags); goto pte_unmap; } @@ -2942,7 +2942,7 @@ static int gup_fast_pmd_leaf(pmd_t orig, pmd_t *pmdp, unsigned long addr, if (!folio) return 0; - if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) { + if (unlikely(pmd_val(orig) != pmd_val(pmdp_get_lockless(pmdp)))) { gup_put_folio(folio, refs, flags); return 0; } @@ -2985,7 +2985,7 @@ static int gup_fast_pud_leaf(pud_t orig, pud_t *pudp, unsigned long addr, if (!folio) return 0; - if (unlikely(pud_val(orig) != pud_val(*pudp))) { + if (unlikely(pud_val(orig) != pud_val(pudp_get(pudp)))) { gup_put_folio(folio, refs, flags); return 0; } From 214f9ab72ce6e16120c20ad670389656f059e685 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Fri, 24 Apr 2026 14:52:17 +0530 Subject: [PATCH 1963/5214] mm/memory: update stale locking comments for fault handlers Update the comments for wp_page_copy(), do_wp_page(), do_swap_page(), do_anonymous_page(), __do_fault(), do_fault(), handle_pte_fault(), __handle_mm_fault(), and handle_mm_fault() to concisely clarify that they can be entered holding either the mmap_lock or the VMA lock, and that the lock may be released upon returning VM_FAULT_RETRY. Additionally, make the following corrections: - In do_anonymous_page(), correct the outdated claim that the function is entered with the PTE "mapped but not yet locked". Since handle_pte_fault() unmaps the empty PTE before routing to do_pte_missing(), the comment now correctly states it is entered with the PTE unmapped and unlocked. - In __do_fault(), update the stale reference from __lock_page_retry() to __folio_lock_or_retry(). Link: https://lore.kernel.org/20260424092217.263648-1-adi.sharma@zohomail.in Signed-off-by: Aditya Sharma Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- mm/memory.c | 55 ++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 86a973119bd4..02ec74a1273f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3837,8 +3837,8 @@ vm_fault_t __vmf_anon_prepare(struct vm_fault *vmf) * Handle the case of a page which we actually need to copy to a new page, * either due to COW or unsharing. * - * Called with mmap_lock locked and the old page referenced, but - * without the ptl held. + * Called with either the VMA lock or the mmap_lock held (see FAULT_FLAG_VMA_LOCK) + * and the old page referenced, but without the ptl held. * * High level logic flow: * @@ -4237,9 +4237,9 @@ static bool wp_can_reuse_anon_folio(struct folio *folio, * though the page will change only once the write actually happens. This * avoids a few races, and potentially makes it more efficient. * - * We enter with non-exclusive mmap_lock (to exclude vma changes, - * but allow concurrent faults), with pte both mapped and locked. - * We return with mmap_lock still held, but pte unmapped and unlocked. + * We enter with either the VMA lock or the mmap_lock held (see + * FAULT_FLAG_VMA_LOCK) and pte both mapped and locked. We return with + * the same lock still held, but pte unmapped and unlocked. */ static vm_fault_t do_wp_page(struct vm_fault *vmf) __releases(vmf->ptl) @@ -4785,12 +4785,12 @@ static void check_swap_exclusive(struct folio *folio, swp_entry_t entry, } /* - * We enter with non-exclusive mmap_lock (to exclude vma changes, - * but allow concurrent faults), and pte mapped but not yet locked. + * We enter with either the VMA lock or the mmap_lock held (see + * FAULT_FLAG_VMA_LOCK), and pte mapped but not yet locked. * We return with pte unmapped and unlocked. * - * We return with the mmap_lock locked or unlocked in the same cases - * as does filemap_fault(). + * When returning, the lock may have been released in the same cases + * as done by filemap_fault(). */ vm_fault_t do_swap_page(struct vm_fault *vmf) { @@ -5330,9 +5330,10 @@ static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte, } /* - * We enter with non-exclusive mmap_lock (to exclude vma changes, - * but allow concurrent faults), and pte mapped but not yet locked. - * We return with mmap_lock still held, but pte unmapped and unlocked. + * We enter with either the VMA lock or the mmap_lock held (see + * FAULT_FLAG_VMA_LOCK), and pte unmapped and unlocked. + * We return with the lock still held, but pte unmapped and unlocked. + * If VM_FAULT_RETRY is returned, the lock may have been released. */ static vm_fault_t do_anonymous_page(struct vm_fault *vmf) { @@ -5440,9 +5441,10 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf) } /* - * The mmap_lock must have been held on entry, and may have been - * released depending on flags and vma->vm_ops->fault() return value. - * See filemap_fault() and __lock_page_retry(). + * Either the VMA lock or the mmap_lock must have been held on entry + * (see FAULT_FLAG_VMA_LOCK) and may have been released depending on + * flags and vma->vm_ops->fault() return value. + * See filemap_fault() and __folio_lock_or_retry(). */ static vm_fault_t __do_fault(struct vm_fault *vmf) { @@ -6003,11 +6005,11 @@ static vm_fault_t do_shared_fault(struct vm_fault *vmf) } /* - * We enter with non-exclusive mmap_lock (to exclude vma changes, - * but allow concurrent faults). - * The mmap_lock may have been released depending on flags and our + * We enter with either the VMA lock or the mmap_lock held (see + * FAULT_FLAG_VMA_LOCK). + * The lock may have been released depending on flags and our * return value. See filemap_fault() and __folio_lock_or_retry(). - * If mmap_lock is released, vma may become invalid (for example + * If the lock is released, vma may become invalid (for example * by other thread calling munmap()). */ static vm_fault_t do_fault(struct vm_fault *vmf) @@ -6374,10 +6376,11 @@ static void fix_spurious_fault(struct vm_fault *vmf, * with external mmu caches can use to update those (ie the Sparc or * PowerPC hashed page tables that act as extended TLBs). * - * We enter with non-exclusive mmap_lock (to exclude vma changes, but allow - * concurrent faults). + * On entry, we hold either the VMA lock or the mmap_lock + * (see FAULT_FLAG_VMA_LOCK). * - * The mmap_lock may have been released depending on flags and our return value. + * The mmap_lock or VMA lock may have been released depending on flags + * and our return value. * See filemap_fault() and __folio_lock_or_retry(). */ static vm_fault_t handle_pte_fault(struct vm_fault *vmf) @@ -6458,8 +6461,8 @@ static vm_fault_t handle_pte_fault(struct vm_fault *vmf) /* * On entry, we hold either the VMA lock or the mmap_lock - * (FAULT_FLAG_VMA_LOCK tells you which). If VM_FAULT_RETRY is set in - * the result, the mmap_lock is not held on exit. See filemap_fault() + * (see FAULT_FLAG_VMA_LOCK). If VM_FAULT_RETRY is set in + * the result, the lock is not held on exit. See filemap_fault() * and __folio_lock_or_retry(). */ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma, @@ -6691,9 +6694,9 @@ static vm_fault_t sanitize_fault_flags(struct vm_area_struct *vma, /* * By the time we get here, we already hold either the VMA lock or the - * mmap_lock (FAULT_FLAG_VMA_LOCK tells you which). + * mmap_lock (see FAULT_FLAG_VMA_LOCK). * - * The mmap_lock may have been released depending on flags and our + * The lock may have been released depending on flags and our * return value. See filemap_fault() and __folio_lock_or_retry(). */ vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address, From 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 21:29:40 -0700 Subject: [PATCH 1964/5214] mm/damon/core: make charge_addr_from aware of end-address exclusivity DAMON region end address is exclusive one, but charge_addr_from is assigned assuming the end address is inclusive. As a result, DAMOS action to next up to min_region_sz memory can be skipped. This is quite negligible user impact. But, the bug is a bug that can be very simply fixed. Fix the wrong assignment to respect the exclusiveness of the address. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260428042942.118230-1-sj@kernel.org Link: https://lore.kernel.org/20260428032324.115663-1-sj@kernel.org [1] Fixes: 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton --- mm/damon/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 3dbbbfdeff71..901ffdaefb7f 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2106,7 +2106,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, if (damos_quota_is_set(quota) && quota->charged_sz >= quota->esz) { quota->charge_target_from = t; - quota->charge_addr_from = r->ar.end + 1; + quota->charge_addr_from = r->ar.end; } } if (s->action != DAMOS_STAT) From 9138e27a3bc380cd88475546688f23d5eda1ad23 Mon Sep 17 00:00:00 2001 From: Ravi Jonnalagadda Date: Mon, 27 Apr 2026 20:05:20 -0700 Subject: [PATCH 1965/5214] mm/damon: add node_eligible_mem_bp goal metric Background and Motivation ========================= In heterogeneous memory systems, controlling memory distribution across NUMA nodes is essential for performance optimization. This patch enables system-wide page distribution with target-state goals such as "maintain 60% of scheme-eligible memory on DRAM" using PA-mode DAMON schemes. Rather than using absolute thresholds, this metric tracks the ratio of memory that matches each scheme's access pattern filters on a target node, enabling the quota system to automatically adjust migration aggressiveness to maintain the desired distribution. What This Metric Measures ========================= node_eligible_mem_bp: scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000 Two-Scheme Setup for Hot Page Distribution ========================================== For maintaining 60% of hot memory on DRAM (node 0) and 40% on CXL (node 1): PULL scheme: migrate_hot to node 0 goal: node_eligible_mem_bp, nid=0, target=6000 addr filter: node 1 address range (only migrate FROM CXL) "Move hot pages to DRAM if less than 60% of hot data is in DRAM" PUSH scheme: migrate_hot to node 1 goal: node_eligible_mem_bp, nid=1, target=4000 addr filter: node 0 address range (only migrate FROM DRAM) "Move hot pages to CXL if less than 40% of hot data is in CXL" Each scheme independently measures its own eligible memory and adjusts its quota to achieve its target ratio. The schemes work in concert through DAMON's unified monitoring context, with the quota autotuner balancing their relative aggressiveness. Implementation Details ====================== The implementation adds a new quota goal metric type DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP to the existing DAMOS quota goal framework. When this metric is configured for a scheme: 1. During each quota adjustment cycle, damos_get_node_eligible_mem_bp() is called to calculate the current memory distribution. 2. The function iterates through all regions that match the scheme's access pattern (via __damos_valid_target()) and calculates: - Total eligible bytes across all nodes - Eligible bytes specifically on the target node (goal->nid) 3. For each eligible region, damos_calc_eligible_bytes() walks through the physical address range, using damon_get_folio() to look up each folio and determine its NUMA node via folio_nid(). 4. Large folios are handled by calculating the exact overlap between the region boundaries and folio boundaries, ensuring accurate byte counts even when regions partially span folios. 5. The ratio (node_eligible / total_eligible * 10000) is returned as basis points, which the quota autotuner uses to adjust the scheme's effective quota size (esz). The implementation requires CONFIG_DAMON_PADDR since damon_get_folio() is only available for physical address space monitoring. Testing Results =============== Functionally tested on a two-node heterogeneous memory system with DRAM (node 0) and CXL memory (node 1). A PUSH+PULL scheme configuration using migrate_hot actions was used to reach a target hot memory ratio between the two tiers. With the TEMPORAL tuner, the system converges quickly to the target distribution. The tuner drives esz to maximum when under goal and to zero once the goal is met, forming a simple on/off feedback loop that stabilizes at the desired ratio. With the CONSIST tuner, the scheme still converges but more slowly, as it migrates and then throttles itself based on quota feedback. The time to reach the goal varies depending on workload intensity. Note: This metric works with both TEMPORAL and CONSIST goal tuners. Link: https://lore.kernel.org/20260428030520.701-1-ravis.opensrc@gmail.com Signed-off-by: Ravi Jonnalagadda Suggested-by: SeongJae Park Reviewed-by: SeongJae Park Cc: Honggyu Kim Cc: Jonathan Corbet Cc: Yunjeong Mun Signed-off-by: Andrew Morton --- include/linux/damon.h | 3 + mm/damon/core.c | 172 +++++++++++++++++++++++++++++++++++---- mm/damon/sysfs-schemes.c | 7 ++ 3 files changed, 167 insertions(+), 15 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index f2cdb7c3f5e6..986b8c902585 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -159,6 +159,8 @@ enum damos_action { * @DAMOS_QUOTA_NODE_MEMCG_FREE_BP: MemFree ratio of a node for a cgroup. * @DAMOS_QUOTA_ACTIVE_MEM_BP: Active to total LRU memory ratio. * @DAMOS_QUOTA_INACTIVE_MEM_BP: Inactive to total LRU memory ratio. + * @DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP: Scheme-eligible memory ratio of a + * node in basis points (0-10000). * @NR_DAMOS_QUOTA_GOAL_METRICS: Number of DAMOS quota goal metrics. * * Metrics equal to larger than @NR_DAMOS_QUOTA_GOAL_METRICS are unsupported. @@ -172,6 +174,7 @@ enum damos_quota_goal_metric { DAMOS_QUOTA_NODE_MEMCG_FREE_BP, DAMOS_QUOTA_ACTIVE_MEM_BP, DAMOS_QUOTA_INACTIVE_MEM_BP, + DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP, NR_DAMOS_QUOTA_GOAL_METRICS, }; diff --git a/mm/damon/core.c b/mm/damon/core.c index 901ffdaefb7f..e4229294353e 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -13,10 +13,14 @@ #include #include #include +#include #include #include #include +/* for damon_get_folio() used by node eligible memory metrics */ +#include "ops-common.h" + #define CREATE_TRACE_POINTS #include @@ -1326,11 +1330,26 @@ static int damon_commit_targets( int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) { int err; + struct damos *scheme; + struct damos_quota_goal *goal; dst->maybe_corrupted = true; if (!is_power_of_2(src->min_region_sz)) return -EINVAL; + /* node_eligible_mem_bp metric requires PADDR ops */ + if (src->ops.id != DAMON_OPS_PADDR) { + damon_for_each_scheme(scheme, src) { + struct damos_quota *quota = &scheme->quota; + + damos_for_each_quota_goal(goal, quota) { + if (goal->metric == + DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP) + return -EINVAL; + } + } + } + err = damon_commit_schemes(dst, src); if (err) return err; @@ -2287,7 +2306,115 @@ static unsigned long damos_get_node_memcg_used_bp( numerator = i.totalram - used_pages; return mult_frac(numerator, 10000, i.totalram); } -#else + +#ifdef CONFIG_DAMON_PADDR +/* + * damos_calc_eligible_bytes() - Calculate raw eligible bytes per node. + * @c: The DAMON context. + * @s: The scheme. + * @nid: The target NUMA node id. + * @total: Output for total eligible bytes across all nodes. + * + * Iterates through each folio in eligible regions to accurately determine + * which node the memory resides on. Returns eligible bytes on the specified + * node and sets *total to the sum across all nodes. + * + * Note: This function requires damon_get_folio() from ops-common.c, which is + * only available when CONFIG_DAMON_PADDR is enabled. It also requires the + * context to be using PADDR operations for meaningful results. + */ +static phys_addr_t damos_calc_eligible_bytes(struct damon_ctx *c, + struct damos *s, int nid, phys_addr_t *total) +{ + struct damon_target *t; + struct damon_region *r; + phys_addr_t total_eligible = 0; + phys_addr_t node_eligible = 0; + + damon_for_each_target(t, c) { + damon_for_each_region(r, t) { + phys_addr_t addr, end_addr; + + if (!__damos_valid_target(r, s)) + continue; + + /* Convert from core address units to physical bytes */ + addr = (phys_addr_t)r->ar.start * c->addr_unit; + end_addr = (phys_addr_t)r->ar.end * c->addr_unit; + while (addr < end_addr) { + struct folio *folio; + phys_addr_t folio_start, folio_end; + phys_addr_t overlap_start, overlap_end; + phys_addr_t counted; + + folio = damon_get_folio(PHYS_PFN(addr)); + if (!folio) { + addr = PAGE_ALIGN_DOWN(addr + + PAGE_SIZE); + if (!addr) + break; + continue; + } + + /* + * Calculate exact overlap between the region + * [addr, end_addr) and the folio range. + * The folio may start before addr if addr is + * in the middle of a large folio. + */ + folio_start = PFN_PHYS(folio_pfn(folio)); + folio_end = folio_start + folio_size(folio); + + overlap_start = max(addr, folio_start); + overlap_end = min(end_addr, folio_end); + + if (overlap_end > overlap_start) { + counted = overlap_end - overlap_start; + total_eligible += counted; + if (folio_nid(folio) == nid) + node_eligible += counted; + } + + /* Advance past the entire folio */ + addr = folio_end; + folio_put(folio); + } + cond_resched(); + } + } + + *total = total_eligible; + return node_eligible; +} + +static unsigned long damos_get_node_eligible_mem_bp(struct damon_ctx *c, + struct damos *s, int nid) +{ + phys_addr_t total_eligible = 0; + phys_addr_t node_eligible; + + if (c->ops.id != DAMON_OPS_PADDR) + return 0; + + if (nid < 0 || nid >= MAX_NUMNODES || !node_online(nid)) + return 0; + + node_eligible = damos_calc_eligible_bytes(c, s, nid, &total_eligible); + + if (!(unsigned long)total_eligible) + return 0; + + return mult_frac((unsigned long)node_eligible, 10000, + (unsigned long)total_eligible); +} +#else /* CONFIG_DAMON_PADDR */ +static unsigned long damos_get_node_eligible_mem_bp(struct damon_ctx *c, + struct damos *s, int nid) +{ + return 0; +} +#endif /* CONFIG_DAMON_PADDR */ +#else /* CONFIG_NUMA */ static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { @@ -2299,7 +2426,13 @@ static unsigned long damos_get_node_memcg_used_bp( { return 0; } -#endif + +static unsigned long damos_get_node_eligible_mem_bp(struct damon_ctx *c, + struct damos *s, int nid) +{ + return 0; +} +#endif /* CONFIG_NUMA */ /* * Returns LRU-active or inactive memory to total LRU memory size ratio. @@ -2319,7 +2452,8 @@ static unsigned int damos_get_in_active_mem_bp(bool active_ratio) return mult_frac(inactive, 10000, total); } -static void damos_set_quota_goal_current_value(struct damos_quota_goal *goal) +static void damos_set_quota_goal_current_value(struct damon_ctx *c, + struct damos *s, struct damos_quota_goal *goal) { u64 now_psi_total; @@ -2345,19 +2479,24 @@ static void damos_set_quota_goal_current_value(struct damos_quota_goal *goal) goal->current_value = damos_get_in_active_mem_bp( goal->metric == DAMOS_QUOTA_ACTIVE_MEM_BP); break; + case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP: + goal->current_value = damos_get_node_eligible_mem_bp(c, s, + goal->nid); + break; default: break; } } /* Return the highest score since it makes schemes least aggressive */ -static unsigned long damos_quota_score(struct damos_quota *quota) +static unsigned long damos_quota_score(struct damon_ctx *c, struct damos *s) { struct damos_quota_goal *goal; + struct damos_quota *quota = &s->quota; unsigned long highest_score = 0; damos_for_each_quota_goal(goal, quota) { - damos_set_quota_goal_current_value(goal); + damos_set_quota_goal_current_value(c, s, goal); highest_score = max(highest_score, mult_frac(goal->current_value, 10000, goal->target_value)); @@ -2366,17 +2505,20 @@ static unsigned long damos_quota_score(struct damos_quota *quota) return highest_score; } -static void damos_goal_tune_esz_bp_consist(struct damos_quota *quota) +static void damos_goal_tune_esz_bp_consist(struct damon_ctx *c, struct damos *s) { - unsigned long score = damos_quota_score(quota); + struct damos_quota *quota = &s->quota; + unsigned long score = damos_quota_score(c, s); quota->esz_bp = damon_feed_loop_next_input( max(quota->esz_bp, 10000UL), score); } -static void damos_goal_tune_esz_bp_temporal(struct damos_quota *quota) +static void damos_goal_tune_esz_bp_temporal(struct damon_ctx *c, + struct damos *s) { - unsigned long score = damos_quota_score(quota); + struct damos_quota *quota = &s->quota; + unsigned long score = damos_quota_score(c, s); if (score >= 10000) quota->esz_bp = 0; @@ -2389,9 +2531,9 @@ static void damos_goal_tune_esz_bp_temporal(struct damos_quota *quota) /* * Called only if quota->ms, or quota->sz are set, or quota->goals is not empty */ -static void damos_set_effective_quota(struct damos_quota *quota, - struct damon_ctx *ctx) +static void damos_set_effective_quota(struct damon_ctx *ctx, struct damos *s) { + struct damos_quota *quota = &s->quota; unsigned long throughput; unsigned long esz = ULONG_MAX; @@ -2402,9 +2544,9 @@ static void damos_set_effective_quota(struct damos_quota *quota, if (!list_empty("a->goals)) { if (quota->goal_tuner == DAMOS_QUOTA_GOAL_TUNER_CONSIST) - damos_goal_tune_esz_bp_consist(quota); + damos_goal_tune_esz_bp_consist(ctx, s); else if (quota->goal_tuner == DAMOS_QUOTA_GOAL_TUNER_TEMPORAL) - damos_goal_tune_esz_bp_temporal(quota); + damos_goal_tune_esz_bp_temporal(ctx, s); esz = quota->esz_bp / 10000; } @@ -2452,7 +2594,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) /* First charge window */ if (!quota->total_charged_sz && !quota->charged_from) { quota->charged_from = jiffies; - damos_set_effective_quota(quota, c); + damos_set_effective_quota(c, s); } /* New charge window starts */ @@ -2467,7 +2609,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) quota->charged_sz = 0; if (trace_damos_esz_enabled()) cached_esz = quota->esz; - damos_set_effective_quota(quota, c); + damos_set_effective_quota(c, s); if (trace_damos_esz_enabled() && quota->esz != cached_esz) damos_trace_esz(c, s, quota); } diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index a8014780edae..d12e741a47ec 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1093,6 +1093,10 @@ struct damos_sysfs_qgoal_metric_name damos_sysfs_qgoal_metric_names[] = { .metric = DAMOS_QUOTA_INACTIVE_MEM_BP, .name = "inactive_mem_bp", }, + { + .metric = DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP, + .name = "node_eligible_mem_bp", + }, }; static ssize_t target_metric_show(struct kobject *kobj, @@ -2685,6 +2689,9 @@ static int damos_sysfs_add_quota_score( } goal->nid = sysfs_goal->nid; break; + case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP: + goal->nid = sysfs_goal->nid; + break; default: break; } From c7ec7d5f6b3d1fc36d04baaabd8d2756a5e937b1 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:50 -0700 Subject: [PATCH 1966/5214] mm/damon/core: handle Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/core.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index e4229294353e..df51a5661d46 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2065,6 +2065,20 @@ static void damos_walk_cancel(struct damon_ctx *ctx) mutex_unlock(&ctx->walk_control_lock); } +static bool damos_quota_is_full(struct damos_quota *quota, + unsigned long min_region_sz) +{ + if (!damos_quota_is_set(quota)) + return false; + if (quota->charged_sz >= quota->esz) + return true; + /* + * DAMOS action is applied per region, so esz - quota->charged_sz < min_region_sz; +} + static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, struct damon_region *r, struct damos *s) { @@ -2122,8 +2136,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, quota->total_charged_ns += timespec64_to_ns(&end) - timespec64_to_ns(&begin); quota->charged_sz += sz; - if (damos_quota_is_set(quota) && - quota->charged_sz >= quota->esz) { + if (damos_quota_is_full(quota, c->min_region_sz)) { quota->charge_target_from = t; quota->charge_addr_from = r->ar.end; } @@ -2151,8 +2164,7 @@ static void damon_do_apply_schemes(struct damon_ctx *c, continue; /* Check the quota */ - if (damos_quota_is_set(quota) && - quota->charged_sz >= quota->esz) + if (damos_quota_is_full(quota, c->min_region_sz)) continue; if (damos_skip_charged_region(t, r, s, c->min_region_sz)) @@ -2601,8 +2613,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) if (!time_in_range_open(jiffies, quota->charged_from, quota->charged_from + msecs_to_jiffies(quota->reset_interval))) { - if (damos_quota_is_set(quota) && - quota->charged_sz >= quota->esz) + if (damos_quota_is_full(quota, c->min_region_sz)) s->stat.qt_exceeds++; quota->total_charged_sz += quota->charged_sz; quota->charged_from = jiffies; From 2423bb5fbe81f842cef10e076aeeb04004a6e15f Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:51 -0700 Subject: [PATCH 1967/5214] mm/damon/core: merge regions after applying DAMOS schemes damos_apply_scheme() could split the given region if applying the scheme's action to the entire region can result in violating the quota-set upper limit. Keeping regions that are created by such split operations is unnecessary overhead. The overhead would be negligible in the common case because such split operations could happen only up to the number of installed schemes per scheme apply interval. The following commit could make the impact larger, though. The following commit will allow the action-failed region to be charged in a different ratio. If both the ratio and the remaining quota is quite small while the region to apply the scheme is quite large and the action is nearly always failing, a high number of split operations could happen. Remove the unnecessary overhead by merging regions after applying schemes is done for each region. The merge operation is made only if it will not lose monitoring information and keep min_nr_regions constraint. In the worst case, the max_nr_regions could still be violated until the next per-aggregation interval merge operation is made. Link: https://lore.kernel.org/20260428013402.115171-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/core.c | 59 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index df51a5661d46..e59f4031d24b 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2182,6 +2182,58 @@ static void damon_do_apply_schemes(struct damon_ctx *c, } } +/* + * damos_apply_target() - Apply DAMOS schemes to a given target. + * @c: monitoring context to apply its DAMOS schemes to.. + * @t: monitoring target to apply the schemes to. + * @max_region_sz: maximum region size for @c. + * + * This function could split regions for keeping the quota. To minimize + * overhead from the split operations increased number of regions, this + * function will also merge regions after the schemes applying attempt is done, + * for each region. The merge operation is made only when it doesn't lose the + * monitoring information and not violating @max_region_sz. + * + * Hence, after this function is called, the total number of regions could + * be increased or reduced. The increase could make max_nr_regions temporarily + * be violated, until the next per-aggregation interval regions merge operation + * is executed. The decrease will not violate min_nr_regions though, since it + * keeps @max_region_sz. + */ +static void damos_apply_target(struct damon_ctx *c, struct damon_target *t, + unsigned long max_region_sz) +{ + struct damon_region *r; + + damon_for_each_region(r, t) { + struct damon_region *prev_r; + + damon_do_apply_schemes(c, t, r); + /* + * damon_do_apply_scheems() could split the region for the + * quota. Keeping the new slices is an overhead. Merge back + * the slices into the previous region if it doesn't lose any + * information and not violating the max_region_sz. + */ + if (damon_first_region(t) == r) + continue; + prev_r = damon_prev_region(r); + if (prev_r->ar.end != r->ar.start) + continue; + if (prev_r->age != r->age) + continue; + if (prev_r->last_nr_accesses != r->last_nr_accesses) + continue; + if (prev_r->nr_accesses != r->nr_accesses) + continue; + if (r->ar.end - prev_r->ar.start > max_region_sz) + continue; + prev_r->ar.end = r->ar.end; + damon_destroy_region(r, t); + r = prev_r; + } +} + /* * damon_feed_loop_next_input() - get next input to achieve a target score. * @last_input The last input. @@ -2674,9 +2726,9 @@ static void damos_trace_stat(struct damon_ctx *c, struct damos *s) static void kdamond_apply_schemes(struct damon_ctx *c) { struct damon_target *t; - struct damon_region *r; struct damos *s; bool has_schemes_to_apply = false; + unsigned long max_region_sz; damon_for_each_scheme(s, c) { if (time_before(c->passed_sample_intervals, s->next_apply_sis)) @@ -2693,13 +2745,12 @@ static void kdamond_apply_schemes(struct damon_ctx *c) if (!has_schemes_to_apply) return; + max_region_sz = damon_region_sz_limit(c); mutex_lock(&c->walk_control_lock); damon_for_each_target(t, c) { if (c->ops.target_valid && c->ops.target_valid(t) == false) continue; - - damon_for_each_region(r, t) - damon_do_apply_schemes(c, t, r); + damos_apply_target(c, t, max_region_sz); } damon_for_each_scheme(s, c) { From 4ee4fb3214a8aadf5e8d253f8a34b76baff7f37d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:52 -0700 Subject: [PATCH 1968/5214] mm/damon/core: introduce failed region quota charge ratio DAMOS quota is charged to all DAMOS action application attempted memory, regardless of how much of the memory the action was successful and failed. This makes understanding quota behavior without DAMOS stat but only with end level metrics (e.g., increased amount of free memory for DAMOS_PAGEOUT action) difficult. Also, charging action-failed memory same as action-successful memory is somewhat unfair, as successful action application will induce more overhead in most cases. Introduce DAMON core API for setting the charge ratio for such action-failed memory. It allows API callers to specify the ratio in a flexible way, by setting the numerator and the denominator. Link: https://lore.kernel.org/20260428013402.115171-4-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 9 +++++++++ mm/damon/core.c | 21 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 986b8c902585..2bb43910e22e 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -236,6 +236,8 @@ enum damos_quota_goal_tuner { * @goals: Head of quota tuning goals (&damos_quota_goal) list. * @goal_tuner: Goal-based @esz tuning algorithm to use. * @esz: Effective size quota in bytes. + * @fail_charge_num: Failed regions charge rate numerator. + * @fail_charge_denom: Failed regions charge rate denominator. * * @weight_sz: Weight of the region's size for prioritization. * @weight_nr_accesses: Weight of the region's nr_accesses for prioritization. @@ -265,6 +267,10 @@ enum damos_quota_goal_tuner { * * The resulting effective size quota in bytes is set to @esz. * + * For DAMOS action applying failed amount of regions, charging those same to + * those that the action has successfully applied may be unfair. For the + * reason, 'the size * @fail_charge_num / @fail_charge_denom' is charged. + * * For selecting regions within the quota, DAMON prioritizes current scheme's * target memory regions using the &struct damon_operations->get_scheme_score. * You could customize the prioritization logic by setting &weight_sz, @@ -279,6 +285,9 @@ struct damos_quota { enum damos_quota_goal_tuner goal_tuner; unsigned long esz; + unsigned int fail_charge_num; + unsigned int fail_charge_denom; + unsigned int weight_sz; unsigned int weight_nr_accesses; unsigned int weight_age; diff --git a/mm/damon/core.c b/mm/damon/core.c index e59f4031d24b..7aeaf319a18a 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -922,6 +922,8 @@ static int damos_commit_quota(struct damos_quota *dst, struct damos_quota *src) if (err) return err; dst->goal_tuner = src->goal_tuner; + dst->fail_charge_num = src->fail_charge_num; + dst->fail_charge_denom = src->fail_charge_denom; dst->weight_sz = src->weight_sz; dst->weight_nr_accesses = src->weight_nr_accesses; dst->weight_age = src->weight_age; @@ -2065,6 +2067,23 @@ static void damos_walk_cancel(struct damon_ctx *ctx) mutex_unlock(&ctx->walk_control_lock); } +static void damos_charge_quota(struct damos_quota *quota, + unsigned long sz_region, unsigned long sz_applied) +{ + /* + * sz_applied could be bigger than sz_region, depending on ops + * implementation of the action, e.g., damos_pa_pageout(). Charge only + * the region size in the case. + */ + if (!quota->fail_charge_denom || sz_applied > sz_region) + quota->charged_sz += sz_region; + else + quota->charged_sz += sz_applied + mult_frac( + (sz_region - sz_applied), + quota->fail_charge_num, + quota->fail_charge_denom); +} + static bool damos_quota_is_full(struct damos_quota *quota, unsigned long min_region_sz) { @@ -2135,7 +2154,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, ktime_get_coarse_ts64(&end); quota->total_charged_ns += timespec64_to_ns(&end) - timespec64_to_ns(&begin); - quota->charged_sz += sz; + damos_charge_quota(quota, sz, sz_applied); if (damos_quota_is_full(quota, c->min_region_sz)) { quota->charge_target_from = t; quota->charge_addr_from = r->ar.end; From fad1124120d61d2c6781c9d0fcace0fdb6e24df4 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:53 -0700 Subject: [PATCH 1969/5214] mm/damon/sysfs-schemes: implement fail_charge_{num,denom} files Implement the user-space ABI for the DAMOS action failed region quota-charge ratio setup. For this, add two new sysfs files under the DAMON sysfs interface for DAMOS quotas. Names of the files are fail_charge_num and fail_charge_denom, and work for reading and setting the numerator and denominator of the failed regions charge ratio. Link: https://lore.kernel.org/20260428013402.115171-5-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index d12e741a47ec..be2b5eda84e0 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1512,6 +1512,8 @@ struct damon_sysfs_quotas { unsigned long reset_interval_ms; unsigned long effective_sz; /* Effective size quota in bytes */ enum damos_quota_goal_tuner goal_tuner; + unsigned int fail_charge_num; + unsigned int fail_charge_denom; }; static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void) @@ -1686,6 +1688,48 @@ static ssize_t goal_tuner_store(struct kobject *kobj, return -EINVAL; } +static ssize_t fail_charge_num_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_quotas *quotas = container_of(kobj, + struct damon_sysfs_quotas, kobj); + + return sysfs_emit(buf, "%u\n", quotas->fail_charge_num); +} + +static ssize_t fail_charge_num_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_quotas *quotas = container_of(kobj, + struct damon_sysfs_quotas, kobj); + int err = kstrtouint(buf, 0, "as->fail_charge_num); + + if (err) + return -EINVAL; + return count; +} + +static ssize_t fail_charge_denom_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_quotas *quotas = container_of(kobj, + struct damon_sysfs_quotas, kobj); + + return sysfs_emit(buf, "%u\n", quotas->fail_charge_denom); +} + +static ssize_t fail_charge_denom_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_quotas *quotas = container_of(kobj, + struct damon_sysfs_quotas, kobj); + int err = kstrtouint(buf, 0, "as->fail_charge_denom); + + if (err) + return -EINVAL; + return count; +} + static void damon_sysfs_quotas_release(struct kobject *kobj) { kfree(container_of(kobj, struct damon_sysfs_quotas, kobj)); @@ -1706,12 +1750,20 @@ static struct kobj_attribute damon_sysfs_quotas_effective_bytes_attr = static struct kobj_attribute damon_sysfs_quotas_goal_tuner_attr = __ATTR_RW_MODE(goal_tuner, 0600); +static struct kobj_attribute damon_sysfs_quotas_fail_charge_num_attr = + __ATTR_RW_MODE(fail_charge_num, 0600); + +static struct kobj_attribute damon_sysfs_quotas_fail_charge_denom_attr = + __ATTR_RW_MODE(fail_charge_denom, 0600); + static struct attribute *damon_sysfs_quotas_attrs[] = { &damon_sysfs_quotas_ms_attr.attr, &damon_sysfs_quotas_sz_attr.attr, &damon_sysfs_quotas_reset_interval_ms_attr.attr, &damon_sysfs_quotas_effective_bytes_attr.attr, &damon_sysfs_quotas_goal_tuner_attr.attr, + &damon_sysfs_quotas_fail_charge_num_attr.attr, + &damon_sysfs_quotas_fail_charge_denom_attr.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_quotas); @@ -2803,6 +2855,8 @@ static struct damos *damon_sysfs_mk_scheme( .weight_nr_accesses = sysfs_weights->nr_accesses, .weight_age = sysfs_weights->age, .goal_tuner = sysfs_quotas->goal_tuner, + .fail_charge_num = sysfs_quotas->fail_charge_num, + .fail_charge_denom = sysfs_quotas->fail_charge_denom, }; struct damos_watermarks wmarks = { .metric = sysfs_wmarks->metric, From 776270536d9d2111aec3db54cfccae4ed5a3c5f6 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:54 -0700 Subject: [PATCH 1970/5214] Docs/mm/damon/design: document fail_charge_{num,denom} Update DAMON design document for the DAMOS action failed region quota charge ratio. Link: https://lore.kernel.org/20260428013402.115171-6-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index afc7d52bda2f..bacb457f553a 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -565,6 +565,28 @@ interface `, refer to :ref:`weights ` part of the documentation. +.. _damon_design_damos_quotas_failed_memory_charging_ratio: + +Action-failed Memory Charging Ratio +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DAMOS action to a given region can fail for some subsets of the memory of the +region. For example, if the action is ``pageout`` and the region has some +unreclaimable pages, applying the action to the pages will fail. The amount of +system resource that is taken for such failed action applications is usually +different from that for successful action applications. For such cases, users +can set different charging ratio for such failed memory. The ratio can be +specified using ``fail_charge_num`` and ``fail_charge_denom`` parameters. The +two parameters represent the numerator and denominator of the ratio. The +feature is enabled only if ``fail_charge_denom`` is not zero. + +For example, let's suppose a DAMOS action is applied to a region of 1,000 MiB +size. The action is successfully applied to only 700 MiB of the region. +``fail_charge_num`` and ``fail_charge_denom`` are set to ``1`` and ``1024``, +respectively. Then only 700 MiB and 300 KiB of size (``700 MiB + 300 MiB * 1 / +1024``) will be charged. + + .. _damon_design_damos_quotas_auto_tuning: Aim-oriented Feedback-driven Auto-tuning From 59ebdeedb595116bc2d2d0bcc408994908cb3b9d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:55 -0700 Subject: [PATCH 1971/5214] Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom} files Update DAMON usage document for the DAMOS action failed regions quota charge ratio control sysfs files. Link: https://lore.kernel.org/20260428013402.115171-7-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index 534e1199cf09..e84b58731f7e 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -83,7 +83,9 @@ comma (","). │ │ │ │ │ │ │ │ sz/min,max │ │ │ │ │ │ │ │ nr_accesses/min,max │ │ │ │ │ │ │ │ age/min,max - │ │ │ │ │ │ │ :ref:`quotas `/ms,bytes,reset_interval_ms,effective_bytes,goal_tuner + │ │ │ │ │ │ │ :ref:`quotas `/ms,bytes,reset_interval_ms, + │ │ │ │ │ │ │ effective_bytes,goal_tuner, + │ │ │ │ │ │ │ fail_charge_num,fail_charge_denom │ │ │ │ │ │ │ │ weights/sz_permil,nr_accesses_permil,age_permil │ │ │ │ │ │ │ │ :ref:`goals `/nr_goals │ │ │ │ │ │ │ │ │ 0/target_metric,target_value,current_value,nid,path @@ -377,9 +379,10 @@ schemes//quotas/ The directory for the :ref:`quotas ` of the given DAMON-based operation scheme. -Under ``quotas`` directory, five files (``ms``, ``bytes``, -``reset_interval_ms``, ``effective_bytes`` and ``goal_tuner``) and two -directories (``weights`` and ``goals``) exist. +Under ``quotas`` directory, seven files (``ms``, ``bytes``, +``reset_interval_ms``, ``effective_bytes``, ``goal_tuner``, ``fail_charge_num`` +and ``fail_charge_denom``) and two directories (``weights`` and ``goals``) +exist. You can set the ``time quota`` in milliseconds, ``size quota`` in bytes, and ``reset interval`` in milliseconds by writing the values to the three files, @@ -398,6 +401,13 @@ the background design of the feature and the name of the selectable algorithms. Refer to :ref:`goals directory ` for the goals setup. +You can set the action-failed memory quota charging ratio by writing the +numerator and the denominator for the ratio to ``fail_charge_num`` and +``fail_charge_denom`` files, respectively. Reading those files will return the +current set values. Refer to :ref:`design +` for more details of +the ratio feature. + The time quota is internally transformed to a size quota. Between the transformed size quota and user-specified size quota, smaller one is applied. Based on the user-specified :ref:`goal `, the From 1d6b8e92da39413b7780908ea3d896c4a75b9bed Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:56 -0700 Subject: [PATCH 1972/5214] Docs/ABI/damon: document fail_charge_{num,denom} Update DAMON ABI document for the DAMOS action failed regions quota charge ratio control sysfs files. Link: https://lore.kernel.org/20260428013402.115171-8-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/ABI/testing/sysfs-kernel-mm-damon | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index 2424237ebb10..213eb87392d8 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -322,6 +322,18 @@ Contact: SeongJae Park Description: Writing to and reading from this file sets and gets the goal-based effective quota auto-tuning algorithm to use. +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/fail_charge_num +Date: Mar 2026 +Contact: SeongJae Park +Description: Writing to and reading from this file sets and gets the + action-failed memory quota charging ratio numerator. + +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/fail_charge_denom +Date: Mar 2026 +Contact: SeongJae Park +Description: Writing to and reading from this file sets and gets the + action-failed memory quota charging ratio denominator. + What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/weights/sz_permil Date: Mar 2022 Contact: SeongJae Park From 0a605b4b673b46c78b43b5f5e557cfdd06856267 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:57 -0700 Subject: [PATCH 1973/5214] mm/damon/tests/core-kunit: test fail_charge_{num,denom} committing Extend damos_test_commit_quotas() kunit test to ensure damos_commit_quota() handles fail_charge_{num,denom} parameters. Link: https://lore.kernel.org/20260428013402.115171-9-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/tests/core-kunit.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 9e5904c2beeb..6de622a2fd79 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -694,6 +694,8 @@ static void damos_test_commit_quota(struct kunit *test) .ms = 2, .sz = 3, .goal_tuner = DAMOS_QUOTA_GOAL_TUNER_CONSIST, + .fail_charge_num = 2, + .fail_charge_denom = 3, .weight_sz = 4, .weight_nr_accesses = 5, .weight_age = 6, @@ -703,6 +705,8 @@ static void damos_test_commit_quota(struct kunit *test) .ms = 8, .sz = 9, .goal_tuner = DAMOS_QUOTA_GOAL_TUNER_TEMPORAL, + .fail_charge_num = 1, + .fail_charge_denom = 1024, .weight_sz = 10, .weight_nr_accesses = 11, .weight_age = 12, @@ -717,6 +721,8 @@ static void damos_test_commit_quota(struct kunit *test) KUNIT_EXPECT_EQ(test, dst.ms, src.ms); KUNIT_EXPECT_EQ(test, dst.sz, src.sz); KUNIT_EXPECT_EQ(test, dst.goal_tuner, src.goal_tuner); + KUNIT_EXPECT_EQ(test, dst.fail_charge_num, src.fail_charge_num); + KUNIT_EXPECT_EQ(test, dst.fail_charge_denom, src.fail_charge_denom); KUNIT_EXPECT_EQ(test, dst.weight_sz, src.weight_sz); KUNIT_EXPECT_EQ(test, dst.weight_nr_accesses, src.weight_nr_accesses); KUNIT_EXPECT_EQ(test, dst.weight_age, src.weight_age); From 588f08518fa2bb3b9ef20b5fbb20e27b39e5a257 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:58 -0700 Subject: [PATCH 1974/5214] selftests/damon/_damon_sysfs: support failed region quota charge ratio Extend _damon_sysfs.py for DAMOS action failed regions quota charge ratio setup, so that we can add kselftest for the new feature. Link: https://lore.kernel.org/20260428013402.115171-10-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/_damon_sysfs.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 2b4df655d9fd..0f13512fa5e6 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -132,14 +132,17 @@ class DamosQuota: goals = None # quota goals goal_tuner = None # quota goal tuner reset_interval_ms = None # quota reset interval + fail_charge_num = None + fail_charge_denom = None weight_sz_permil = None weight_nr_accesses_permil = None weight_age_permil = None scheme = None # owner scheme def __init__(self, sz=0, ms=0, goals=None, goal_tuner='consist', - reset_interval_ms=0, weight_sz_permil=0, - weight_nr_accesses_permil=0, weight_age_permil=0): + reset_interval_ms=0, fail_charge_num=0, fail_charge_denom=0, + weight_sz_permil=0, weight_nr_accesses_permil=0, + weight_age_permil=0): self.sz = sz self.ms = ms self.reset_interval_ms = reset_interval_ms @@ -151,6 +154,8 @@ class DamosQuota: for idx, goal in enumerate(self.goals): goal.idx = idx goal.quota = self + self.fail_charge_num = fail_charge_num + self.fail_charge_denom = fail_charge_denom def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'quotas') @@ -197,6 +202,18 @@ class DamosQuota: os.path.join(self.sysfs_dir(), 'goal_tuner'), self.goal_tuner) if err is not None: return err + + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_num'), + self.fail_charge_num) + if err is not None: + return err + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_denom'), + self.fail_charge_denom) + if err is not None: + return err + return None class DamosWatermarks: From bcd8d68c6ba1ef918294d96ab64726eeef00b37c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:59 -0700 Subject: [PATCH 1975/5214] selftests/damon/drgn_dump_damon_status: support failed region quota charge ratio Extend drgn_dump_damon_status.py to dump DAMON internal state for DAMOS action failed regions quota charge ratio, to be able to show if the internal state for the feature is working, with future DAMON selftests. Link: https://lore.kernel.org/20260428013402.115171-11-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/drgn_dump_damon_status.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index af99b07a4f56..b5c56233a923 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -112,6 +112,8 @@ def damos_quota_to_dict(quota): ['goals', damos_quota_goals_to_list], ['goal_tuner', int], ['esz', int], + ['fail_charge_num', int], + ['fail_charge_denom', int], ['weight_sz', int], ['weight_nr_accesses', int], ['weight_age', int], From 8d21446a6c7feb2d93b3ea4f54ffd7f4eb64f2bc Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:34:00 -0700 Subject: [PATCH 1976/5214] selftests/damon/sysfs.py: test failed region quota charge ratio Extend sysfs.py DAMON selftest to setup DAMOS action failed region quota charge ratio and assert the setup is made into DAMON internal state. Link: https://lore.kernel.org/20260428013402.115171-12-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 3aa5c91548a5..9067945f16ca 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -73,6 +73,10 @@ def assert_quota_committed(quota, dump): } assert_true(dump['goal_tuner'] == tuner_val[quota.goal_tuner], 'goal_tuner', dump) + assert_true(dump['fail_charge_num'] == quota.fail_charge_num, + 'fail_charge_num', dump) + assert_true(dump['fail_charge_denom'] == quota.fail_charge_denom, + 'fail_charge_denom', dump) assert_true(dump['weight_sz'] == quota.weight_sz_permil, 'weight_sz', dump) assert_true(dump['weight_nr_accesses'] == quota.weight_nr_accesses_permil, 'weight_nr_accesses', dump) @@ -239,6 +243,8 @@ def main(): nid=1)], goal_tuner='temporal', reset_interval_ms=1500, + fail_charge_num=1, + fail_charge_denom=4096, weight_sz_permil=20, weight_nr_accesses_permil=200, weight_age_permil=1000), From 9f40c3cdf0fa3011c3a15f8acc0b9ffb3ed11171 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:52 +0800 Subject: [PATCH 1977/5214] selftests/cgroup: skip test_zswap if zswap is globally disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "selftests/cgroup: improve zswap tests robustness and support large page sizes", v7. This patchset aims to fix various spurious failures and improve the overall robustness of the cgroup zswap selftests. The primary motivation is to make the tests compatible with architectures that use non-4K page sizes (such as 64K on ppc64le and arm64). Currently, the tests rely heavily on hardcoded 4K page sizes and fixed memory limits. On 64K page size systems, these hardcoded values lead to sub-page granularity accesses, incorrect page count calculations, and insufficient memory pressure to trigger zswap writeback, ultimately causing the tests to fail. Additionally, this series addresses OOM kills occurring in test_swapin_nozswap by dynamically scaling memory limits, and prevents spurious test failures when zswap is built into the kernel but globally disabled. This patch (of 8): test_zswap currently only checks whether zswap is present by testing /sys/module/zswap. This misses the runtime global state exposed in /sys/module/zswap/parameters/enabled. When zswap is built/loaded but globally disabled, the zswap cgroup selftests run in an invalid environment and may fail spuriously. Check the runtime enabled state before running the tests: - skip if zswap is not configured, - fail if the enabled knob cannot be read, - skip if zswap is globally disabled. Also print a hint in the skip message on how to enable zswap. Link: https://lore.kernel.org/20260424040059.12940-1-li.wang@linux.dev Link: https://lore.kernel.org/20260424040059.12940-2-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index a7bdcdd09d62..a94238a2e048 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -15,6 +15,9 @@ #include "kselftest.h" #include "cgroup_util.h" +#define PATH_ZSWAP "/sys/module/zswap" +#define PATH_ZSWAP_ENABLED "/sys/module/zswap/parameters/enabled" + static int read_int(const char *path, size_t *value) { FILE *file; @@ -725,9 +728,18 @@ struct zswap_test { }; #undef T -static bool zswap_configured(void) +static void check_zswap_enabled(void) { - return access("/sys/module/zswap", F_OK) == 0; + char value[2]; + + if (access(PATH_ZSWAP, F_OK)) + ksft_exit_skip("zswap isn't configured\n"); + + if (read_text(PATH_ZSWAP_ENABLED, value, sizeof(value)) <= 0) + ksft_exit_fail_msg("Failed to read " PATH_ZSWAP_ENABLED "\n"); + + if (value[0] == 'N') + ksft_exit_skip("zswap is disabled (hint: echo 1 > " PATH_ZSWAP_ENABLED ")\n"); } int main(int argc, char **argv) @@ -740,8 +752,7 @@ int main(int argc, char **argv) if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); - if (!zswap_configured()) - ksft_exit_skip("zswap isn't configured\n"); + check_zswap_enabled(); /* * Check that memory controller is available: From 0d38cded3c6294b0dfa38e3fc92077b5d381951e Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:53 +0800 Subject: [PATCH 1978/5214] selftests/cgroup: avoid OOM in test_swapin_nozswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_swapin_nozswap can hit OOM before reaching its assertions on some setups. The test currently sets memory.max=8M and then allocates/reads 32M with memory.zswap.max=0, which may over-constrain reclaim and kill the workload process. Replace hardcoded sizes with PAGE_SIZE-based values: - control_allocation_size = PAGE_SIZE * 512 - memory.max = control_allocation_size * 3 / 4 - minimum expected swap = control_allocation_size / 4 This keeps the test pressure model intact (allocate/read beyond memory.max to force swap-in/out) while making it more robust across different environments. The test intent is unchanged: confirm that swapping occurs while zswap remains unused when memory.zswap.max=0. === Error Logs === # ./test_zswap TAP version 13 1..7 ok 1 test_zswap_usage not ok 2 test_swapin_nozswap ... # dmesg [271641.879153] test_zswap invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0 [271641.879168] CPU: 1 UID: 0 PID: 177372 Comm: test_zswap Kdump: loaded Not tainted 6.12.0-211.el10.ppc64le #1 VOLUNTARY [271641.879171] Hardware name: IBM,9009-41A POWER9 (architected) 0x4e0202 0xf000005 of:IBM,FW940.02 (UL940_041) hv:phyp pSeries [271641.879173] Call Trace: [271641.879174] [c00000037540f730] [c00000000127ec44] dump_stack_lvl+0x88/0xc4 (unreliable) [271641.879184] [c00000037540f760] [c0000000005cc594] dump_header+0x5c/0x1e4 [271641.879188] [c00000037540f7e0] [c0000000005cb464] oom_kill_process+0x324/0x3b0 [271641.879192] [c00000037540f860] [c0000000005cbe48] out_of_memory+0x118/0x420 [271641.879196] [c00000037540f8f0] [c00000000070d8ec] mem_cgroup_out_of_memory+0x18c/0x1b0 [271641.879200] [c00000037540f990] [c000000000713888] try_charge_memcg+0x598/0x890 [271641.879204] [c00000037540fa70] [c000000000713dbc] charge_memcg+0x5c/0x110 [271641.879207] [c00000037540faa0] [c0000000007159f8] __mem_cgroup_charge+0x48/0x120 [271641.879211] [c00000037540fae0] [c000000000641914] alloc_anon_folio+0x2b4/0x5a0 [271641.879215] [c00000037540fb60] [c000000000641d58] do_anonymous_page+0x158/0x6b0 [271641.879218] [c00000037540fbd0] [c000000000642f8c] __handle_mm_fault+0x4bc/0x910 [271641.879221] [c00000037540fcf0] [c000000000643500] handle_mm_fault+0x120/0x3c0 [271641.879224] [c00000037540fd40] [c00000000014bba0] ___do_page_fault+0x1c0/0x980 [271641.879228] [c00000037540fdf0] [c00000000014c44c] hash__do_page_fault+0x2c/0xc0 [271641.879232] [c00000037540fe20] [c0000000001565d8] do_hash_fault+0x128/0x1d0 [271641.879236] [c00000037540fe50] [c000000000008be0] data_access_common_virt+0x210/0x220 [271641.879548] Tasks state (memory values in pages): ... [271641.879550] [ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name [271641.879555] [ 177372] 0 177372 571 0 0 0 0 51200 96 0 test_zswap [271641.879562] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=/,mems_allowed=0,oom_memcg=/no_zswap_test,task_memcg=/no_zswap_test,task=test_zswap,pid=177372,uid=0 [271641.879578] Memory cgroup out of memory: Killed process 177372 (test_zswap) total-vm:36544kB, anon-rss:0kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:50kB oom_score_adj:0 Link: https://lore.kernel.org/20260424040059.12940-3-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index a94238a2e048..47709cbdcdf1 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -165,21 +165,25 @@ static int test_zswap_usage(const char *root) static int test_swapin_nozswap(const char *root) { int ret = KSFT_FAIL; - char *test_group; - long swap_peak, zswpout; + char *test_group, mem_max_buf[32]; + long swap_peak, zswpout, min_swap; + size_t allocation_size = sysconf(_SC_PAGESIZE) * 512; + + min_swap = allocation_size / 4; + snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size * 3/4); test_group = cg_name(root, "no_zswap_test"); if (!test_group) goto out; if (cg_create(test_group)) goto out; - if (cg_write(test_group, "memory.max", "8M")) + if (cg_write(test_group, "memory.max", mem_max_buf)) goto out; if (cg_write(test_group, "memory.zswap.max", "0")) goto out; /* Allocate and read more than memory.max to trigger swapin */ - if (cg_run(test_group, allocate_and_read_bytes, (void *)MB(32))) + if (cg_run(test_group, allocate_and_read_bytes, (void *)allocation_size)) goto out; /* Verify that pages are swapped out, but no zswap happened */ @@ -189,8 +193,9 @@ static int test_swapin_nozswap(const char *root) goto out; } - if (swap_peak < MB(24)) { - ksft_print_msg("at least 24MB of memory should be swapped out\n"); + if (swap_peak < min_swap) { + ksft_print_msg("at least %ldKB of memory should be swapped out\n", + min_swap / 1024); goto out; } From b19ee588e159c71f0d314246a944dcfc3e2a6009 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:54 +0800 Subject: [PATCH 1979/5214] selftests/cgroup: use runtime page size for zswpin check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_zswapin compares memory.stat:zswpin (counted in pages) against a byte threshold converted with PAGE_SIZE. In cgroup selftests, PAGE_SIZE is hardcoded to 4096, which makes the conversion wrong on systems with non-4K base pages (e.g. 64K). As a result, the test requires too many pages to pass and fails spuriously even when zswap is working. Use sysconf(_SC_PAGESIZE) for the zswpin threshold conversion so the check matches the actual system page size. Link: https://lore.kernel.org/20260424040059.12940-4-li.wang@linux.dev Signed-off-by: Li Wang Reviewed-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 47709cbdcdf1..37aa83c2f1bf 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -245,7 +245,7 @@ static int test_zswapin(const char *root) goto out; } - if (zswpin < MB(24) / PAGE_SIZE) { + if (zswpin < MB(24) / sysconf(_SC_PAGESIZE)) { ksft_print_msg("at least 24MB should be brought back from zswap\n"); goto out; } From 6e9f5c2eecd107cf9a10fd22d311b2c49026f474 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:55 +0800 Subject: [PATCH 1980/5214] selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cgroup utility code defines a local PAGE_SIZE macro hardcoded to 4096, which is used primarily as a generic buffer size for reading cgroup and proc files. This naming is misleading because the value has nothing to do with the actual page size of the system. On architectures with larger pages (e.g., 64K on arm64 or ppc64), the name suggests a relationship that does not exist. Additionally, the name can shadow or conflict with PAGE_SIZE definitions from system headers, leading to confusion or subtle bugs. To resolve this, rename the macro to BUF_SIZE to accurately reflect its purpose as a general I/O buffer size. Furthermore, test_memcontrol currently relies on this hardcoded 4K value to stride through memory and trigger page faults. Update this logic to use the actual system page size dynamically. This micro-optimizes the memory faulting process by ensuring it iterates correctly and efficiently based on the underlying architecture's true page size. (This part from Waiman) Link: https://lore.kernel.org/20260424040059.12940-5-li.wang@linux.dev Signed-off-by: Li Wang Signed-off-by: Waiman Long Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- .../selftests/cgroup/lib/cgroup_util.c | 18 +++++++++--------- .../cgroup/lib/include/cgroup_util.h | 4 ++-- tools/testing/selftests/cgroup/test_core.c | 2 +- tools/testing/selftests/cgroup/test_freezer.c | 2 +- .../selftests/cgroup/test_memcontrol.c | 19 ++++++++++++------- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index 42f54936f4bb..f1ec7de58ae3 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -141,7 +141,7 @@ int cg_read_strcmp_wait(const char *cgroup, const char *control, int cg_read_strstr(const char *cgroup, const char *control, const char *needle) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; if (cg_read(cgroup, control, buf, sizeof(buf))) return -1; @@ -171,7 +171,7 @@ long cg_read_long_fd(int fd) long cg_read_key_long(const char *cgroup, const char *control, const char *key) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; char *ptr; if (cg_read(cgroup, control, buf, sizeof(buf))) @@ -207,7 +207,7 @@ long cg_read_key_long_poll(const char *cgroup, const char *control, long cg_read_lc(const char *cgroup, const char *control) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; const char delim[] = "\n"; char *line; long cnt = 0; @@ -259,7 +259,7 @@ int cg_write_numeric(const char *cgroup, const char *control, long value) static int cg_find_root(char *root, size_t len, const char *controller, bool *nsdelegate) { - char buf[10 * PAGE_SIZE]; + char buf[10 * BUF_SIZE]; char *fs, *mount, *type, *options; const char delim[] = "\n\t "; @@ -314,7 +314,7 @@ int cg_create(const char *cgroup) int cg_wait_for_proc_count(const char *cgroup, int count) { - char buf[10 * PAGE_SIZE] = {0}; + char buf[10 * BUF_SIZE] = {0}; int attempts; char *ptr; @@ -339,7 +339,7 @@ int cg_wait_for_proc_count(const char *cgroup, int count) int cg_killall(const char *cgroup) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; char *ptr = buf; /* If cgroup.kill exists use it. */ @@ -549,7 +549,7 @@ int cg_run_nowait(const char *cgroup, int proc_mount_contains(const char *option) { - char buf[4 * PAGE_SIZE]; + char buf[4 * BUF_SIZE]; ssize_t read; read = read_text("/proc/mounts", buf, sizeof(buf)); @@ -561,7 +561,7 @@ int proc_mount_contains(const char *option) int cgroup_feature(const char *feature) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; ssize_t read; read = read_text("/sys/kernel/cgroup/features", buf, sizeof(buf)); @@ -588,7 +588,7 @@ ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) return -1; diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h index 567b1082974c..febc1723d090 100644 --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h @@ -2,8 +2,8 @@ #include #include -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 +#ifndef BUF_SIZE +#define BUF_SIZE 4096 #endif #define MB(x) (x << 20) diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index 7b83c7e7c9d4..88ca832d4fc1 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -87,7 +87,7 @@ static int test_cgcore_destroy(const char *root) int ret = KSFT_FAIL; char *cg_test = NULL; int child_pid; - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; cg_test = cg_name(root, "cg_test"); diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c index 97fae92c8387..160a9e6ad277 100644 --- a/tools/testing/selftests/cgroup/test_freezer.c +++ b/tools/testing/selftests/cgroup/test_freezer.c @@ -642,7 +642,7 @@ static int test_cgfreezer_ptrace(const char *root) */ static int proc_check_stopped(int pid) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; int len; len = proc_read_text(pid, 0, "stat", buf, sizeof(buf)); diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c index b43da9bc20c4..44338dbaee81 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c @@ -26,6 +26,7 @@ static bool has_localevents; static bool has_recursiveprot; +static int page_size; int get_temp_fd(void) { @@ -34,7 +35,7 @@ int get_temp_fd(void) int alloc_pagecache(int fd, size_t size) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; struct stat st; int i; @@ -61,7 +62,7 @@ int alloc_anon(const char *cgroup, void *arg) char *buf, *ptr; buf = malloc(size); - for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) + for (ptr = buf; ptr < buf + size; ptr += page_size) *ptr = 0; free(buf); @@ -70,7 +71,7 @@ int alloc_anon(const char *cgroup, void *arg) int is_swap_enabled(void) { - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; const char delim[] = "\n"; int cnt = 0; char *line; @@ -113,7 +114,7 @@ static int test_memcg_subtree_control(const char *root) { char *parent, *child, *parent2 = NULL, *child2 = NULL; int ret = KSFT_FAIL; - char buf[PAGE_SIZE]; + char buf[BUF_SIZE]; /* Create two nested cgroups with the memory controller enabled */ parent = cg_name(root, "memcg_test_0"); @@ -184,7 +185,7 @@ static int alloc_anon_50M_check(const char *cgroup, void *arg) return -1; } - for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) + for (ptr = buf; ptr < buf + size; ptr += page_size) *ptr = 0; current = cg_read_long(cgroup, "memory.current"); @@ -414,7 +415,7 @@ static int alloc_anon_noexit(const char *cgroup, void *arg) return -1; } - for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) + for (ptr = buf; ptr < buf + size; ptr += page_size) *ptr = 0; while (getppid() == ppid) @@ -1000,7 +1001,7 @@ static int alloc_anon_50M_check_swap(const char *cgroup, void *arg) return -1; } - for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) + for (ptr = buf; ptr < buf + size; ptr += page_size) *ptr = 0; mem_current = cg_read_long(cgroup, "memory.current"); @@ -1791,6 +1792,10 @@ int main(int argc, char **argv) char root[PATH_MAX]; int i, proc_status; + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size <= 0) + page_size = BUF_SIZE; + ksft_print_header(); ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) From 43743cc516684e40faf15afbb123eccd3d90e244 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:56 +0800 Subject: [PATCH 1981/5214] selftests/cgroup: replace hardcoded page size values in test_zswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_zswap uses hardcoded values of 4095 and 4096 throughout as page stride and page size, which are only correct on systems with a 4K page size. On architectures with larger pages (e.g., 64K on arm64 or ppc64), these constants cause memory to be touched at sub-page granularity, leading to inefficient access patterns and incorrect page count calculations, which can cause test failures. Replace all hardcoded 4095 and 4096 values with a global pagesize variable initialized from sysconf(_SC_PAGESIZE) at startup, and remove the redundant local sysconf() calls scattered across individual functions. No functional change on 4K page size systems. Link: https://lore.kernel.org/20260424040059.12940-6-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Reviewed-by: Jiayuan Chen Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Nhat Pham Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 45 ++++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 37aa83c2f1bf..23ff11390a33 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -15,6 +15,8 @@ #include "kselftest.h" #include "cgroup_util.h" +static int page_size; + #define PATH_ZSWAP "/sys/module/zswap" #define PATH_ZSWAP_ENABLED "/sys/module/zswap/parameters/enabled" @@ -73,11 +75,11 @@ static int allocate_and_read_bytes(const char *cgroup, void *arg) if (!mem) return -1; - for (int i = 0; i < size; i += 4095) + for (int i = 0; i < size; i += page_size) mem[i] = 'a'; /* Go through the allocated memory to (z)swap in and out pages */ - for (int i = 0; i < size; i += 4095) { + for (int i = 0; i < size; i += page_size) { if (mem[i] != 'a') ret = -1; } @@ -93,7 +95,7 @@ static int allocate_bytes(const char *cgroup, void *arg) if (!mem) return -1; - for (int i = 0; i < size; i += 4095) + for (int i = 0; i < size; i += page_size) mem[i] = 'a'; free(mem); return 0; @@ -167,7 +169,7 @@ static int test_swapin_nozswap(const char *root) int ret = KSFT_FAIL; char *test_group, mem_max_buf[32]; long swap_peak, zswpout, min_swap; - size_t allocation_size = sysconf(_SC_PAGESIZE) * 512; + size_t allocation_size = page_size * 512; min_swap = allocation_size / 4; snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size * 3/4); @@ -245,7 +247,7 @@ static int test_zswapin(const char *root) goto out; } - if (zswpin < MB(24) / sysconf(_SC_PAGESIZE)) { + if (zswpin < MB(24) / page_size) { ksft_print_msg("at least 24MB should be brought back from zswap\n"); goto out; } @@ -272,9 +274,8 @@ static int test_zswapin(const char *root) */ static int attempt_writeback(const char *cgroup, void *arg) { - long pagesize = sysconf(_SC_PAGESIZE); size_t memsize = MB(4); - char buf[pagesize]; + char buf[page_size]; long zswap_usage; bool wb_enabled = *(bool *) arg; int ret = -1; @@ -289,11 +290,11 @@ static int attempt_writeback(const char *cgroup, void *arg) * half empty, this will result in data that is still compressible * and ends up in zswap, with material zswap usage. */ - for (int i = 0; i < pagesize; i++) - buf[i] = i < pagesize/2 ? (char) i : 0; + for (int i = 0; i < page_size; i++) + buf[i] = i < page_size/2 ? (char) i : 0; - for (int i = 0; i < memsize; i += pagesize) - memcpy(&mem[i], buf, pagesize); + for (int i = 0; i < memsize; i += page_size) + memcpy(&mem[i], buf, page_size); /* Try and reclaim allocated memory */ if (cg_write_numeric(cgroup, "memory.reclaim", memsize)) { @@ -304,8 +305,8 @@ static int attempt_writeback(const char *cgroup, void *arg) zswap_usage = cg_read_long(cgroup, "memory.zswap.current"); /* zswpin */ - for (int i = 0; i < memsize; i += pagesize) { - if (memcmp(&mem[i], buf, pagesize)) { + for (int i = 0; i < memsize; i += page_size) { + if (memcmp(&mem[i], buf, page_size)) { ksft_print_msg("invalid memory\n"); goto out; } @@ -441,7 +442,7 @@ static int test_no_invasive_cgroup_shrink(const char *root) if (cg_enter_current(control_group)) goto out; control_allocation = malloc(control_allocation_size); - for (int i = 0; i < control_allocation_size; i += 4095) + for (int i = 0; i < control_allocation_size; i += page_size) control_allocation[i] = 'a'; if (cg_read_key_long(control_group, "memory.stat", "zswapped") < 1) goto out; @@ -481,7 +482,7 @@ static int no_kmem_bypass_child(const char *cgroup, void *arg) values->child_allocated = true; return -1; } - for (long i = 0; i < values->target_alloc_bytes; i += 4095) + for (long i = 0; i < values->target_alloc_bytes; i += page_size) ((char *)allocation)[i] = 'a'; values->child_allocated = true; pause(); @@ -529,7 +530,7 @@ static int test_no_kmem_bypass(const char *root) min_free_kb_low = sys_info.totalram / 500000; values->target_alloc_bytes = (sys_info.totalram - min_free_kb_high * 1000) + sys_info.totalram * 5 / 100; - stored_pages_threshold = sys_info.totalram / 5 / 4096; + stored_pages_threshold = sys_info.totalram / 5 / page_size; trigger_allocation_size = sys_info.totalram / 20; /* Set up test memcg */ @@ -556,7 +557,7 @@ static int test_no_kmem_bypass(const char *root) if (!trigger_allocation) break; - for (int i = 0; i < trigger_allocation_size; i += 4095) + for (int i = 0; i < trigger_allocation_size; i += page_size) trigger_allocation[i] = 'b'; usleep(100000); free(trigger_allocation); @@ -567,8 +568,8 @@ static int test_no_kmem_bypass(const char *root) /* If memory was pushed to zswap, verify it belongs to memcg */ if (stored_pages > stored_pages_threshold) { int zswapped = cg_read_key_long(test_group, "memory.stat", "zswapped "); - int delta = stored_pages * 4096 - zswapped; - int result_ok = delta < stored_pages * 4096 / 4; + int delta = stored_pages * page_size - zswapped; + int result_ok = delta < stored_pages * page_size / 4; ret = result_ok ? KSFT_PASS : KSFT_FAIL; break; @@ -622,7 +623,7 @@ static int allocate_random_and_wait(const char *cgroup, void *arg) close(fd); /* Touch all pages to ensure they're faulted in */ - for (size_t i = 0; i < size; i += PAGE_SIZE) + for (size_t i = 0; i < size; i += page_size) mem[i] = mem[i]; /* Use MADV_PAGEOUT to push pages into zswap */ @@ -752,6 +753,10 @@ int main(int argc, char **argv) char root[PATH_MAX]; int i; + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size <= 0) + page_size = BUF_SIZE; + ksft_print_header(); ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) From a19b474927519c8822193f8bdc010641ec6ba404 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:57 +0800 Subject: [PATCH 1982/5214] selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on large pagesize system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_no_invasive_cgroup_shrink sets up two cgroups: wb_group, which is expected to trigger zswap writeback, and a control group (renamed to zw_group), which should only have pages sitting in zswap without any writeback. There are two problems with the current test: 1) The data patterns are reversed. wb_group uses allocate_bytes(), which writes only a single byte per page — trivially compressible, especially by zstd — so compressed pages fit within zswap.max and writeback is never triggered. Meanwhile, the control group uses getrandom() to produce hard-to-compress data, but it is the group that does *not* need writeback. 2) The test uses fixed sizes (10K zswap.max, 10MB allocation) that are too small on systems with large PAGE_SIZE (e.g. 64K), failing to build enough memory pressure to trigger writeback reliably. Fix both issues by: - Swapping the data patterns: fill wb_group pages with partially random data (getrandom for page_size/4 bytes) to resist compression and trigger writeback, and fill zw_group pages with simple repeated data to stay compressed in zswap. - Making all size parameters PAGE_SIZE-aware: set allocation size to PAGE_SIZE * 1024, memory.zswap.max to PAGE_SIZE, and memory.max to allocation_size / 2 for both cgroups. - Allocating memory inline instead of via cg_run() so the pages remain resident throughout the test. === Error Log === # getconf PAGESIZE 65536 # ./test_zswap TAP version 13 ... ok 5 test_zswap_writeback_disabled ok 6 # SKIP test_no_kmem_bypass not ok 7 test_no_invasive_cgroup_shrink Link: https://lore.kernel.org/20260424040059.12940-7-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 70 ++++++++++++++------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 23ff11390a33..8f0478923bd0 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "kselftest.h" #include "cgroup_util.h" @@ -426,44 +427,71 @@ static int test_zswap_writeback_disabled(const char *root) static int test_no_invasive_cgroup_shrink(const char *root) { int ret = KSFT_FAIL; - size_t control_allocation_size = MB(10); - char *control_allocation = NULL, *wb_group = NULL, *control_group = NULL; + unsigned int off; + size_t allocation_size = page_size * 1024; + unsigned int nr_pages = allocation_size / page_size; + char zswap_max_buf[32], mem_max_buf[32]; + char *zw_allocation = NULL, *wb_allocation = NULL; + char *zw_group = NULL, *wb_group = NULL; + + snprintf(zswap_max_buf, sizeof(zswap_max_buf), "%d", page_size); + snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size / 2); wb_group = setup_test_group_1M(root, "per_memcg_wb_test1"); if (!wb_group) return KSFT_FAIL; - if (cg_write(wb_group, "memory.zswap.max", "10K")) + if (cg_write(wb_group, "memory.zswap.max", zswap_max_buf)) goto out; - control_group = setup_test_group_1M(root, "per_memcg_wb_test2"); - if (!control_group) + if (cg_write(wb_group, "memory.max", mem_max_buf)) goto out; - /* Push some test_group2 memory into zswap */ - if (cg_enter_current(control_group)) + zw_group = setup_test_group_1M(root, "per_memcg_wb_test2"); + if (!zw_group) goto out; - control_allocation = malloc(control_allocation_size); - for (int i = 0; i < control_allocation_size; i += page_size) - control_allocation[i] = 'a'; - if (cg_read_key_long(control_group, "memory.stat", "zswapped") < 1) + if (cg_write(zw_group, "memory.max", mem_max_buf)) goto out; - /* Allocate 10x memory.max to push wb_group memory into zswap and trigger wb */ - if (cg_run(wb_group, allocate_bytes, (void *)MB(10))) + /* Push some zw_group memory into zswap (simple data, easy to compress) */ + if (cg_enter_current(zw_group)) goto out; + zw_allocation = malloc(allocation_size); + for (int i = 0; i < nr_pages; i++) { + off = (unsigned long)i * page_size; + memset(&zw_allocation[off], 0, page_size); + memset(&zw_allocation[off], 'a', page_size/4); + } + if (cg_read_key_long(zw_group, "memory.stat", "zswapped") < 1) + goto out; + + /* Push wb_group memory into zswap with hard-to-compress data to trigger wb */ + if (cg_enter_current(wb_group)) + goto out; + wb_allocation = malloc(allocation_size); + if (!wb_allocation) + goto out; + for (int i = 0; i < nr_pages; i++) { + off = (unsigned long)i * page_size; + memset(&wb_allocation[off], 0, page_size); + getrandom(&wb_allocation[off], page_size/4, 0); + } /* Verify that only zswapped memory from gwb_group has been written back */ - if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(control_group) == 0) + if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(zw_group) == 0) ret = KSFT_PASS; out: cg_enter_current(root); - if (control_group) { - cg_destroy(control_group); - free(control_group); + if (zw_group) { + cg_destroy(zw_group); + free(zw_group); } - cg_destroy(wb_group); - free(wb_group); - if (control_allocation) - free(control_allocation); + if (wb_group) { + cg_destroy(wb_group); + free(wb_group); + } + if (zw_allocation) + free(zw_allocation); + if (wb_allocation) + free(wb_allocation); return ret; } From 883015a9c328eaeac48395db36f9e5f864f6473d Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:58 +0800 Subject: [PATCH 1983/5214] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In attempt_writeback(), a memsize of 4M only covers 64 pages on 64K page size systems. When memory.reclaim is called, the kernel prefers reclaiming clean file pages (binary, libc, linker, etc.) over swapping anonymous pages. With only 64 pages of anonymous memory, the reclaim target can be largely or entirely satisfied by dropping file pages, resulting in very few or zero anonymous pages being pushed into zswap. This causes zswap_usage to be extremely small or zero, making zswap_usage/4 insufficient to create meaningful writeback pressure. The test then fails because no writeback is triggered. On 4K page size systems this is not an issue because 4M covers 1024 pages, and file pages are a small fraction of the reclaim target. Fix this by: - Always allocating 1024 pages regardless of page size. This ensures enough anonymous pages to reliably populate zswap and trigger writeback, while keeping the original 4M allocation on 4K systems. - Setting zswap.max to zswap_usage/4 instead of zswap_usage/2 to create stronger writeback pressure, ensuring reclaim reliably triggers writeback even on large page size systems. === Error Log === # uname -rm 6.12.0-211.el10.ppc64le ppc64le # getconf PAGESIZE 65536 # ./test_zswap TAP version 13 1..7 ok 1 test_zswap_usage ok 2 test_swapin_nozswap ok 3 test_zswapin not ok 4 test_zswap_writeback_enabled ... Link: https://lore.kernel.org/20260424040059.12940-8-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 8f0478923bd0..5fe0cffb5575 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -268,14 +268,14 @@ static int test_zswapin(const char *root) This will move it into zswap. * 3. Save current zswap usage. * 4. Move the memory allocated in step 1 back in from zswap. - * 5. Set zswap.max to half the amount that was recorded in step 3. + * 5. Set zswap.max to 1/4 of the amount that was recorded in step 3. * 6. Attempt to reclaim memory equal to the amount that was allocated, this will either trigger writeback if it's enabled, or reclamation will fail if writeback is disabled as there isn't enough zswap space. */ static int attempt_writeback(const char *cgroup, void *arg) { - size_t memsize = MB(4); + size_t memsize = page_size * 1024; char buf[page_size]; long zswap_usage; bool wb_enabled = *(bool *) arg; @@ -313,12 +313,12 @@ static int attempt_writeback(const char *cgroup, void *arg) } } - if (cg_write_numeric(cgroup, "memory.zswap.max", zswap_usage/2)) + if (cg_write_numeric(cgroup, "memory.zswap.max", zswap_usage/4)) goto out; /* * If writeback is enabled, trying to reclaim memory now will trigger a - * writeback as zswap.max is half of what was needed when reclaim ran the first time. + * writeback as zswap.max is 1/4 of what was needed when reclaim ran the first time. * If writeback is disabled, memory reclaim will fail as zswap is limited and * it can't writeback to swap. */ From e5ab892d05ca1a6b032dbc4c9795372daf226415 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:59 +0800 Subject: [PATCH 1984/5214] selftests/cgroup: test_zswap: wait for asynchronous writeback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zswap writeback is asynchronous, but test_zswap.c checks writeback counters immediately after reclaim/trigger paths. On some platforms (e.g. ppc64le), this can race with background writeback and cause spurious failures even when behavior is correct. Add wait_for_writeback() to poll get_cg_wb_count() with a bounded timeout, and use it in: test_zswap_writeback_one() when writeback is expected test_no_invasive_cgroup_shrink() for the wb_group check This keeps the original before/after assertion style while making the tests robust against writeback completion latency. No test behavior change, selftest stability improvement only. Link: https://lore.kernel.org/20260424040059.12940-9-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 28 +++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 5fe0cffb5575..49b36ee79160 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -120,6 +120,27 @@ static char *setup_test_group_1M(const char *root, const char *name) return NULL; } +/* + * Writeback is asynchronous; poll until at least one writeback has + * been recorded for @cg, or until @timeout_ms has elapsed. + */ +static long wait_for_writeback(const char *cg, int timeout_ms) +{ + long elapsed, count; + for (elapsed = 0; elapsed < timeout_ms; elapsed += 100) { + count = get_cg_wb_count(cg); + + if (count < 0) + return -1; + if (count > 0) + return count; + + usleep(100000); + } + + return 0; +} + /* * Sanity test to check that pages are written into zswap. */ @@ -345,7 +366,10 @@ static int test_zswap_writeback_one(const char *cgroup, bool wb) return -1; /* Verify that zswap writeback occurred only if writeback was enabled */ - zswpwb_after = get_cg_wb_count(cgroup); + if (wb) + zswpwb_after = wait_for_writeback(cgroup, 5000); + else + zswpwb_after = get_cg_wb_count(cgroup); if (zswpwb_after < 0) return -1; @@ -476,7 +500,7 @@ static int test_no_invasive_cgroup_shrink(const char *root) } /* Verify that only zswapped memory from gwb_group has been written back */ - if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(zw_group) == 0) + if (wait_for_writeback(wb_group, 5000) > 0 && get_cg_wb_count(zw_group) == 0) ret = KSFT_PASS; out: cg_enter_current(root); From 13fe5736560d6635592b77b1b490fd018af33075 Mon Sep 17 00:00:00 2001 From: Sunny Patel Date: Sun, 19 Apr 2026 23:17:43 +0530 Subject: [PATCH 1985/5214] mm/migrate_device: cleanup up PMD Checks and warnings Remove the odd VM_WARN_ON_FOLIO(!folio, folio) usage and replace it with a simpler VM_WARN_ON_ONCE(!folio) check. Drop the redundant VM_WARN_ON_ONCE(!pmd_none(*pmdp) && !is_huge_zero_pmd(*pmdp)). Refactor the PMD checks, making the control flow clearer and avoiding duplicate condition checks. Link: https://lore.kernel.org/20260419174747.10701-1-nueralspacetech@gmail.com Signed-off-by: Sunny Patel Acked-by: Zi Yan Reviewed-by: Huang Ying Acked-by: David Hildenbrand (Arm) Cc: Alistair Popple Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Signed-off-by: Andrew Morton --- mm/migrate_device.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/migrate_device.c b/mm/migrate_device.c index 19cd14b34114..554754eb26ff 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -801,8 +801,7 @@ static int migrate_vma_insert_huge_pmd_page(struct migrate_vma *migrate, bool flush = false; unsigned long i; - VM_WARN_ON_FOLIO(!folio, folio); - VM_WARN_ON_ONCE(!pmd_none(*pmdp) && !is_huge_zero_pmd(*pmdp)); + VM_WARN_ON_ONCE(!folio); if (!thp_vma_suitable_order(vma, addr, HPAGE_PMD_ORDER)) return -EINVAL; @@ -859,11 +858,9 @@ static int migrate_vma_insert_huge_pmd_page(struct migrate_vma *migrate, if (userfaultfd_missing(vma)) goto unlock_abort; - if (!pmd_none(*pmdp)) { - if (!is_huge_zero_pmd(*pmdp)) - goto unlock_abort; + if (is_huge_zero_pmd(*pmdp)) flush = true; - } else if (!pmd_none(*pmdp)) + else if (!pmd_none(*pmdp)) goto unlock_abort; add_mm_counter(vma->vm_mm, MM_ANONPAGES, HPAGE_PMD_NR); From e0974347f5bbf5f869d616779684a5ed8337c27b Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 19 Apr 2026 23:42:25 +0900 Subject: [PATCH 1986/5214] mm/sparse: remove unnecessary NULL check before allocating mem_section Commit 850ed20539a4 ("mm: move array mem_section init code out of memory_present()") moved mem_section allocation logic into memblocks_present(). Before that move, memory_present() could be called multiple times, so unlikely() matched the common case, where most calls found mem_section already allocated. After that move, memblocks_present() is called exactly once from sparse_init(). Under CONFIG_SPARSEMEM_EXTREME, mem_section is always NULL when it is called. So remove unnecessary NULL check before allocating mem_section. No functional change. Link: https://lore.kernel.org/20260419144225.2875654-1-ekffu200098@gmail.com Signed-off-by: Sang-Heon Jeon Acked-by: Mike Rapoport (Microsoft) Reviewed by: Donet Tom Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- mm/sparse.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mm/sparse.c b/mm/sparse.c index effdac6b0ab1..e13f9f5fa090 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -201,13 +201,11 @@ static void __init memblocks_present(void) int i, nid; #ifdef CONFIG_SPARSEMEM_EXTREME - if (unlikely(!mem_section)) { - unsigned long size, align; + unsigned long size, align; - size = sizeof(struct mem_section *) * NR_SECTION_ROOTS; - align = 1 << (INTERNODE_CACHE_SHIFT); - mem_section = memblock_alloc_or_panic(size, align); - } + size = sizeof(struct mem_section *) * NR_SECTION_ROOTS; + align = 1 << (INTERNODE_CACHE_SHIFT); + mem_section = memblock_alloc_or_panic(size, align); #endif for_each_mem_pfn_range(i, MAX_NUMNODES, &start, &end, &nid) From eb8fc9d285f95cd14697ef3df2b0c2e41c76cbdd Mon Sep 17 00:00:00 2001 From: Xiang Gao Date: Thu, 16 Apr 2026 14:23:02 +0800 Subject: [PATCH 1987/5214] mm/vmscan: fix typos in comments Fix three typos in comments: - Line 112: "zome_reclaim_mode" -> "zone_reclaim_mode" - Line 6208: "prioities" -> "priorities" - Line 7067: "that that high" -> "that the high" (duplicated word) Link: https://lore.kernel.org/20260416062302.727468-1-gxxa03070307@gmail.com Signed-off-by: Xiang Gao Reviewed-by: Barry Song Reviewed-by: Donet Tom Signed-off-by: Andrew Morton --- mm/vmscan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index b2d89ed69d22..a9fd43b23a58 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -109,7 +109,7 @@ struct scan_control { /* zone_reclaim_mode */ unsigned int may_unmap:1; - /* zome_reclaim_mode, boost reclaim, cgroup restrictions */ + /* zone_reclaim_mode, boost reclaim, cgroup restrictions */ unsigned int may_swap:1; /* Not allow cache_trim_mode to be turned on as part of reclaim? */ @@ -6359,7 +6359,7 @@ static void consider_reclaim_throttle(pg_data_t *pgdat, struct scan_control *sc) if (current_is_kswapd() || cgroup_reclaim(sc)) return; - /* Throttle if making no progress at high prioities. */ + /* Throttle if making no progress at high priorities. */ if (sc->priority == 1 && !sc->nr_reclaimed) reclaim_throttle(pgdat, VMSCAN_THROTTLE_NOPROGRESS); } @@ -7224,7 +7224,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx) /* * There should be no need to raise the scanning priority if - * enough pages are already being scanned that that high + * enough pages are already being scanned that the high * watermark would be met at 100% efficiency. */ if (kswapd_shrink_node(pgdat, &sc)) From d86c9e971af2315119a78c564a802fafcebf1b6b Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:37 -0700 Subject: [PATCH 1988/5214] mm: fix mmap errno value when MAP_DROPPABLE is not supported Patch series "fix MAP_DROPPABLE not supported errno", v4. Mark Brown reported seeing a regression in -next on 32 bit arm with the mlock selftests. Before exiting and marking the tests failed, the following message was logged after an attempt to create a MAP_DROPPABLE mapping: Bail out! mmap error: Unknown error 524 It turns out error 524 is ENOTSUPP which is an error that userspace is not supposed to see, but it indicates in this instance that MAP_DROPPABLE is not supported. The first patch changes the errno returned to EOPNOTSUPP. The second patch is a second version of a prior patch to introduce selftests to verify locking behavior with droppable mappings with the additional change to skip the tests when MAP_DROPPABLE is not supported. The third patch fixes the MAP_DROPPABLE selftest so that it is run by the framework and skips if MAP_DROPPABLE is not supported. This patch (of 3): On configs where MAP_DROPPABLE is not supported (currently any 32-bit config except for PPC32), mmap fails with errno set to ENOTSUPP. However, ENOTSUPP is not a standard error value that userspace knows about. The acceptable userspace-visible errno to use is EOPNOTSUPP. checkpatch.pl has a warning to this effect. Link: https://lore.kernel.org/20260416033939.49981-1-anthony.yznaga@oracle.com Link: https://lore.kernel.org/20260416033939.49981-2-anthony.yznaga@oracle.com Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Acked-by: Vlastimil Babka (SUSE) Reported-by: Mark Brown Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes (Oracle) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton --- mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index 5754d1c36462..2311ae7c2ff4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -504,7 +504,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, break; case MAP_DROPPABLE: if (VM_DROPPABLE == VM_NONE) - return -ENOTSUPP; + return -EOPNOTSUPP; /* * A locked or stack area makes no sense to be droppable. * From c02dd57c57a6ae7dd05fdf8b861f1a76e1e4f8bc Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:38 -0700 Subject: [PATCH 1989/5214] selftests/mm: verify droppable mappings cannot be locked For configs that support MAP_DROPPABLE verify that a mapping created with MAP_DROPPABLE cannot be locked via mlock(), and that it will not be locked if it's created after mlockall(MCL_FUTURE). Link: https://lore.kernel.org/20260416033939.49981-3-anthony.yznaga@oracle.com Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/mlock2-tests.c | 86 ++++++++++++++++++++--- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/mm/mlock2-tests.c b/tools/testing/selftests/mm/mlock2-tests.c index b474f2b20def..e16e288cc7c1 100644 --- a/tools/testing/selftests/mm/mlock2-tests.c +++ b/tools/testing/selftests/mm/mlock2-tests.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE #include +#include #include #include #include @@ -163,14 +164,17 @@ static int lock_check(unsigned long addr) return (vma_rss == vma_size); } -static int unlock_lock_check(char *map) +static int unlock_lock_check(char *map, bool mlock_supported) { - if (is_vmflag_set((unsigned long)map, LOCKED)) { - ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED); - return 1; - } + if (!is_vmflag_set((unsigned long)map, LOCKED)) + return 0; - return 0; + if (mlock_supported) + ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED); + else + ksft_print_msg("VMA flag %s is present on an unsupported VMA\n", LOCKED); + + return 1; } static void test_mlock_lock(void) @@ -196,7 +200,7 @@ static void test_mlock_lock(void) ksft_exit_fail_msg("munlock(): %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: Unlocked\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: Unlocked\n", __func__); munmap(map, 2 * page_size); } @@ -296,7 +300,7 @@ static void test_munlockall0(void) ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: No locked memory\n", __func__); munmap(map, 2 * page_size); } @@ -336,7 +340,67 @@ static void test_munlockall1(void) ksft_exit_fail_msg("munlockall() %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: No locked memory\n", __func__); + munmap(map, 2 * page_size); +} + +/* Droppable memory should not be lockable. */ +static void test_mlock_droppable(void) +{ + char *map; + unsigned long page_size = getpagesize(); + + /* Ensure MCL_FUTURE is not set. */ + if (munlockall()) { + ksft_test_result_fail("munlockall() %s\n", strerror(errno)); + return; + } + + map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + if (map == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) + ksft_test_result_skip("%s: MAP_DROPPABLE not supported\n", __func__); + else + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + return; + } + + if (mlock2_(map, 2 * page_size, 0)) + ksft_test_result_fail("mlock2(0): %s\n", strerror(errno)); + else + ksft_test_result(!unlock_lock_check(map, false), + "%s: droppable memory not locked\n", __func__); + + munmap(map, 2 * page_size); +} + +static void test_mlockall_future_droppable(void) +{ + char *map; + unsigned long page_size = getpagesize(); + + if (mlockall(MCL_CURRENT | MCL_FUTURE)) { + ksft_test_result_fail("mlockall(MCL_CURRENT | MCL_FUTURE): %s\n", strerror(errno)); + return; + } + + map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + + if (map == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) + ksft_test_result_skip("%s: MAP_DROPPABLE not supported\n", __func__); + else + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + munlockall(); + return; + } + + ksft_test_result(!unlock_lock_check(map, false), "%s: droppable memory not locked\n", + __func__); + + munlockall(); munmap(map, 2 * page_size); } @@ -442,7 +506,7 @@ int main(int argc, char **argv) munmap(map, size); - ksft_set_plan(13); + ksft_set_plan(15); test_mlock_lock(); test_mlock_onfault(); @@ -451,6 +515,8 @@ int main(int argc, char **argv) test_lock_onfault_of_present(); test_vma_management(true); test_mlockall(); + test_mlock_droppable(); + test_mlockall_future_droppable(); ksft_finished(); } From 303c6bdfe7cb51658fe632e31ee5a5d526c88435 Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:39 -0700 Subject: [PATCH 1990/5214] selftests/mm: run the MAP_DROPPABLE selftest The test was not being run by the selftest framework so it was never noticed that it would fail with an assertion failure on configs without support for MAP_DROPPABLE. Update the test so that it is skipped instead when MAP_DROPPABLE is not supported, and add it to the mmap category so that the test is run by the framework. Link: https://lore.kernel.org/20260416033939.49981-4-anthony.yznaga@oracle.com Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Mark Brown Cc: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/droppable.c | 9 ++++++++- tools/testing/selftests/mm/run_vmtests.sh | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/droppable.c b/tools/testing/selftests/mm/droppable.c index 44940f75c461..30c8be37fcb9 100644 --- a/tools/testing/selftests/mm/droppable.c +++ b/tools/testing/selftests/mm/droppable.c @@ -26,7 +26,14 @@ int main(int argc, char *argv[]) ksft_set_plan(1); alloc = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); - assert(alloc != MAP_FAILED); + if (alloc == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { + ksft_test_result_skip("MAP_DROPPABLE not supported\n"); + exit(KSFT_SKIP); + } + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + exit(KSFT_FAIL); + } memset(alloc, 'A', alloc_size); for (size_t i = 0; i < alloc_size; i += page_size) assert(*(uint8_t *)(alloc + i)); diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index c17b133a81d2..3b61677fe984 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -382,6 +382,7 @@ else fi CATEGORY="mmap" run_test ./map_populate +CATEGORY="mmap" run_test ./droppable CATEGORY="mlock" run_test ./mlock-random-test From 781b0e74748f14b0e732eb736370bbbed181fe4d Mon Sep 17 00:00:00 2001 From: Zhen Ni Date: Tue, 14 Apr 2026 15:58:13 +0800 Subject: [PATCH 1991/5214] mm/page_owner: fix %pGp format specifier argument type The %pGp format specifier expects an argument of type 'unsigned long *', but page->flags is now of type 'memdesc_flags_t' (a struct containing an unsigned long member 'f') after the introduction of memdesc_flags_t. Fix the type mismatch by passing &page->flags.f instead of &page->flags, which matches the expected type. Link: https://lore.kernel.org/20260414075813.3425968-1-zhen.ni@easystack.cn Fixes: 53fbef56e07d ("mm: introduce memdesc_flags_t") Signed-off-by: Zhen Ni Acked-by: Vlastimil Babka (SUSE) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_owner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_owner.c b/mm/page_owner.c index 8178e0be557f..2dddcb6510aa 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c @@ -573,7 +573,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn, migratetype_names[page_mt], pfn >> pageblock_order, migratetype_names[pageblock_mt], - &page->flags); + &page->flags.f); ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0); if (ret >= count) From 8c2c7df58b5433f614d603bbdffd85f2a392b74a Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Sun, 12 Apr 2026 14:19:31 -0700 Subject: [PATCH 1992/5214] Docs/mm/damon/maintainer-profile: add AI review usage guideline DAMON is opted-in for DAMON patches scanning [1] and email delivery [2]. Clarify how that could be used on DAMON maintainer profile. Link: https://lore.kernel.org/20260412211932.89038-1-sj@kernel.org Link: https://github.com/sashiko-dev/sashiko/commit/ad9f4a98f958 [1] Link: https://github.com/sashiko-dev/sashiko/commit/b554c7b6e733 [2] Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Randy Dunlap Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- Documentation/mm/damon/maintainer-profile.rst | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/mm/damon/maintainer-profile.rst b/Documentation/mm/damon/maintainer-profile.rst index bcb9798a27a8..fb2fa00cc9aa 100644 --- a/Documentation/mm/damon/maintainer-profile.rst +++ b/Documentation/mm/damon/maintainer-profile.rst @@ -100,3 +100,24 @@ There is also a public Google `calendar `_ that has the events. Anyone can subscribe to it. DAMON maintainer will also provide periodic reminders to the mailing list (damon@lists.linux.dev). + +AI Review +--------- + +For patches that are publicly posted to DAMON mailing list +(damon@lists.linux.dev), AI reviews of the patches will be available at +sashiko.dev. The reviews could also be sent as mails to the author of the +patch. + +Patch authors are encouraged to check the AI reviews and share their opinions. +The sharing could be done as a reply to the mail thread. Consider reducing the +recipients list for such sharing, since some people are not really interested +in AI reviews. As a rule of thumb, drop stable@vger.kernel.org and individuals +except DAMON maintainer. + +`hkml` also provides a `feature +`_ +for such sharing. Please feel free to use the feature. + +It is only an optional recommendation. DAMON maintainer could also ask any +question about the AI reviews, though. From ffe55393137c01aa01940b528afcea8c5a108ed7 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Fri, 10 Apr 2026 17:24:19 +0800 Subject: [PATCH 1993/5214] mm/sparse: remove sparse buffer pre-allocation mechanism Commit 9bdac9142407 ("sparsemem: Put mem map for one node together.") introduced a mechanism to pre-allocate a large memory block to hold all memmaps for a NUMA node upfront. However, the original commit message did not clearly state the actual benefits or the necessity of explicitly pre-allocating a single chunk for all memmap areas of a given node. One of the concerns about removing this pre-allocation is that the subsequent per-section memmap allocations could become scattered around, and might turn too many memory blocks/sections into an "un-offlinable" state. However, tests show that even without the explicit node-wide pre-allocation, memblock still allocates memory closely and back-to-back. When tracing vmemmap_set_pmd allocations, the physical chunks allocated by memblock are strictly adjacent to each other in a single contiguous physical range (mapped top-down). Because they are packed tightly together naturally, they will at most consume or pollute the exact same number of memory blocks as the explicit pre-allocation did. Another concern is the boot performance impact of calling memmap_alloc() multiple times compared to one large node-wide allocation. Tests on a 256GB VM showed that memmap allocation time increased from 199,555 ns to 741,292 ns. Even though it is 3.7x slower, on a 1TB machine, the entire memory allocation time would only take a few milliseconds. This boot performance difference is completely negligible. Since no negative impact on memory offlining behavior or noticeable boot performance regression was found, this patch proposes removing the explicit node-wide memmap pre-allocation mechanism to reduce the maintenance burden. Link: https://lore.kernel.org/20260410092419.2446420-1-songmuchun@bytedance.com Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/mm.h | 1 - mm/sparse-vmemmap.c | 7 +----- mm/sparse.c | 58 +-------------------------------------------- 3 files changed, 2 insertions(+), 64 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index e3b6112a8d79..8a0078a4dc78 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4855,7 +4855,6 @@ static inline void print_vma_addr(char *prefix, unsigned long rip) } #endif -void *sparse_buffer_alloc(unsigned long size); unsigned long section_map_size(void); struct page * __populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 3c35d2303a61..43f82621dd92 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -87,15 +87,10 @@ static void * __meminit altmap_alloc_block_buf(unsigned long size, void * __meminit vmemmap_alloc_block_buf(unsigned long size, int node, struct vmem_altmap *altmap) { - void *ptr; - if (altmap) return altmap_alloc_block_buf(size, altmap); - ptr = sparse_buffer_alloc(size); - if (!ptr) - ptr = vmemmap_alloc_block(size, node); - return ptr; + return vmemmap_alloc_block(size, node); } static unsigned long __meminit vmem_altmap_next_pfn(struct vmem_altmap *altmap) diff --git a/mm/sparse.c b/mm/sparse.c index e13f9f5fa090..16ac6df3c89f 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -239,12 +239,9 @@ struct page __init *__populate_section_memmap(unsigned long pfn, struct dev_pagemap *pgmap) { unsigned long size = section_map_size(); - struct page *map = sparse_buffer_alloc(size); + struct page *map; phys_addr_t addr = __pa(MAX_DMA_ADDRESS); - if (map) - return map; - map = memmap_alloc(size, size, addr, nid, false); if (!map) panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%pa\n", @@ -254,55 +251,6 @@ struct page __init *__populate_section_memmap(unsigned long pfn, } #endif /* !CONFIG_SPARSEMEM_VMEMMAP */ -static void *sparsemap_buf __meminitdata; -static void *sparsemap_buf_end __meminitdata; - -static inline void __meminit sparse_buffer_free(unsigned long size) -{ - WARN_ON(!sparsemap_buf || size == 0); - memblock_free(sparsemap_buf, size); -} - -static void __init sparse_buffer_init(unsigned long size, int nid) -{ - phys_addr_t addr = __pa(MAX_DMA_ADDRESS); - WARN_ON(sparsemap_buf); /* forgot to call sparse_buffer_fini()? */ - /* - * Pre-allocated buffer is mainly used by __populate_section_memmap - * and we want it to be properly aligned to the section size - this is - * especially the case for VMEMMAP which maps memmap to PMDs - */ - sparsemap_buf = memmap_alloc(size, section_map_size(), addr, nid, true); - sparsemap_buf_end = sparsemap_buf + size; -} - -static void __init sparse_buffer_fini(void) -{ - unsigned long size = sparsemap_buf_end - sparsemap_buf; - - if (sparsemap_buf && size > 0) - sparse_buffer_free(size); - sparsemap_buf = NULL; -} - -void * __meminit sparse_buffer_alloc(unsigned long size) -{ - void *ptr = NULL; - - if (sparsemap_buf) { - ptr = (void *) roundup((unsigned long)sparsemap_buf, size); - if (ptr + size > sparsemap_buf_end) - ptr = NULL; - else { - /* Free redundant aligned space */ - if ((unsigned long)(ptr - sparsemap_buf) > 0) - sparse_buffer_free((unsigned long)(ptr - sparsemap_buf)); - sparsemap_buf = ptr + size; - } - } - return ptr; -} - void __weak __meminit vmemmap_populate_print_last(void) { } @@ -360,8 +308,6 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin, goto failed; } - sparse_buffer_init(map_count * section_map_size(), nid); - sparse_vmemmap_init_nid_early(nid); for_each_present_section_nr(pnum_begin, pnum) { @@ -379,7 +325,6 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin, __func__, nid); pnum_begin = pnum; sparse_usage_fini(); - sparse_buffer_fini(); goto failed; } memmap_boot_pages_add(DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), @@ -388,7 +333,6 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin, } } sparse_usage_fini(); - sparse_buffer_fini(); return; failed: /* From db5e2c01ca3a8fba1c0687d7eec3ac701387a31f Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Fri, 10 Apr 2026 15:47:39 +0800 Subject: [PATCH 1994/5214] mm/memory-failure: use bool for forcekill state 'forcekill' is used as a boolean flag to control whether processes should be forcibly killed. It is only assigned from boolean expressions and never used in arithmetic or bitmask operations. Convert it from int to bool. No functional change intended. Link: https://lore.kernel.org/20260410074740.2524718-1-ye.liu@linux.dev Signed-off-by: Ye Liu Reviewed-by: SeongJae Park Acked-by: Miaohe Lin Cc: Liu Ye Cc: Naoya Horiguchi Signed-off-by: Andrew Morton --- mm/memory-failure.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ee42d4361309..62b547c168fc 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -459,7 +459,7 @@ void add_to_kill_ksm(struct task_struct *tsk, const struct page *p, * Only do anything when FORCEKILL is set, otherwise just free the * list (this is used for clean pages which do not need killing) */ -static void kill_procs(struct list_head *to_kill, int forcekill, +static void kill_procs(struct list_head *to_kill, bool forcekill, unsigned long pfn, int flags) { struct to_kill *tk, *next; @@ -1582,7 +1582,7 @@ static bool hwpoison_user_mappings(struct folio *folio, struct page *p, { LIST_HEAD(tokill); bool unmap_success; - int forcekill; + bool forcekill; bool mlocked = folio_test_mlocked(folio); /* @@ -1703,7 +1703,7 @@ static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn, unmap_mapping_range(mapping, start, size, 0); } - kill_procs(to_kill, flags & MF_MUST_KILL, pfn, flags); + kill_procs(to_kill, !!(flags & MF_MUST_KILL), pfn, flags); } /* From 98e09ce7bb67902c452d22a2a10baf3f0951f3d2 Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Thu, 9 Apr 2026 09:43:22 +0800 Subject: [PATCH 1995/5214] mm/khugepaged: use ALIGN helpers for PMD alignment PMD alignment in khugepaged is currently implemented using a mix of rounding helpers and open-coded bitmask operations. Use ALIGN() and ALIGN_DOWN() consistently for PMD-sized address range alignment, matching the preferred style for address and size handling. No functional change intended. Link: https://lore.kernel.org/20260409014323.2385982-1-ye.liu@linux.dev Signed-off-by: Ye Liu Reviewed-by: Zi Yan Reviewed-by: Barry Song Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: SeongJae Park Cc: Baolin Wang Cc: Dev Jain Cc: Lance Yang Cc: Liam Howlett Cc: Liu Ye Cc: Nico Pache Cc: Ryan Roberts Signed-off-by: Andrew Morton --- mm/khugepaged.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index b8452dbdb043..5f4e009593e0 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2528,8 +2528,8 @@ static void collapse_scan_mm_slot(unsigned int progress_max, cc->progress++; continue; } - hstart = round_up(vma->vm_start, HPAGE_PMD_SIZE); - hend = round_down(vma->vm_end, HPAGE_PMD_SIZE); + hstart = ALIGN(vma->vm_start, HPAGE_PMD_SIZE); + hend = ALIGN_DOWN(vma->vm_end, HPAGE_PMD_SIZE); if (khugepaged_scan.address > hend) { cc->progress++; continue; @@ -2845,8 +2845,8 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, mmgrab(mm); lru_add_drain_all(); - hstart = (start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; - hend = end & HPAGE_PMD_MASK; + hstart = ALIGN(start, HPAGE_PMD_SIZE); + hend = ALIGN_DOWN(end, HPAGE_PMD_SIZE); for (addr = hstart; addr < hend; addr += HPAGE_PMD_SIZE) { enum scan_result result = SCAN_FAIL; From b0f3d00e15e82242d08791fea00807cb01eb1235 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 8 Apr 2026 08:47:00 -0700 Subject: [PATCH 1996/5214] mm: huge_memory: use sysfs_match_string() in defrag_store() Patch series "mm: huge_memory: clean up defrag sysfs with shared", v2. Refactor defrag_store() and defrag_show() to use shared data tables instead of duplicated if/else chains. Patch 1 introduces an enum defrag_mode, a defrag_mode_strings[] table, and a defrag_flags[] mapping array, then rewrites defrag_store() to use sysfs_match_string() with a loop over defrag_flags[]. Patch 2 refactors defrag_show() to use the same arrays, replacing its hardcoded if/else chain of test_bit() calls and string literals. This follows the same pattern applied to anon_enabled_store() in commit 522dfb4ba71f ("mm: huge_memory: refactor anon_enabled_store() with change_anon_orders()"). This patch (of 2): Replace the if/else chain of sysfs_streq() calls in defrag_store() with sysfs_match_string() and a defrag_mode_strings[] table. Introduce enum defrag_mode and defrag_flags[] array mapping each mode to its corresponding transparent_hugepage_flag. The store function now loops over defrag_flags[], setting the bit for the selected mode and clearing the others. When mode is DEFRAG_NEVER (index 4), no index in the 4-element defrag_flags[] matches, so all flags are cleared. Note that the enum ordering (always, defer, defer+madvise, madvise, never) differs from the original if/else chain order in defrag_store() (always, defer+madvise, defer, madvise, never). This is intentional to match the display order used by defrag_show(). This is a follow-up cleanup to commit 522dfb4ba71f ("mm: huge_memory: refactor anon_enabled_store() with change_anon_orders()") which applied the same sysfs_match_string() pattern to anon_enabled_store(). Link: https://lore.kernel.org/20260408-thp_defrag-v2-0-bc544c1bde4e@debian.org Link: https://lore.kernel.org/20260408-thp_defrag-v2-1-bc544c1bde4e@debian.org Signed-off-by: Breno Leitao Acked-by: David Hildenbrand (Arm) Tested-by: Lance Yang Reviewed-by: Lance Yang Reviewed-by: Barry Song Reviewed-by: Lorenzo Stoakes Tested-by: Zi Yan Acked-by: Zi Yan Cc: Baolin Wang Cc: Dev Jain Cc: Liam Howlett Cc: Nico Pache Cc: Ryan Roberts Signed-off-by: Andrew Morton --- mm/huge_memory.c | 60 +++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 4586f3ccb133..62e00b21fdf4 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -429,6 +429,29 @@ ssize_t single_hugepage_flag_store(struct kobject *kobj, return count; } +enum defrag_mode { + DEFRAG_ALWAYS = 0, + DEFRAG_DEFER, + DEFRAG_DEFER_MADVISE, + DEFRAG_MADVISE, + DEFRAG_NEVER, +}; + +static const char * const defrag_mode_strings[] = { + [DEFRAG_ALWAYS] = "always", + [DEFRAG_DEFER] = "defer", + [DEFRAG_DEFER_MADVISE] = "defer+madvise", + [DEFRAG_MADVISE] = "madvise", + [DEFRAG_NEVER] = "never", +}; + +static const enum transparent_hugepage_flag defrag_flags[] = { + [DEFRAG_ALWAYS] = TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, + [DEFRAG_DEFER] = TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, + [DEFRAG_DEFER_MADVISE] = TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, + [DEFRAG_MADVISE] = TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, +}; + static ssize_t defrag_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -456,34 +479,19 @@ static ssize_t defrag_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - if (sysfs_streq(buf, "always")) { - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - } else if (sysfs_streq(buf, "defer+madvise")) { - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - } else if (sysfs_streq(buf, "defer")) { - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - } else if (sysfs_streq(buf, "madvise")) { - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - } else if (sysfs_streq(buf, "never")) { - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - } else + int mode, m; + + mode = sysfs_match_string(defrag_mode_strings, buf); + if (mode < 0) return -EINVAL; + for (m = 0; m < ARRAY_SIZE(defrag_flags); m++) { + if (m == mode) + set_bit(defrag_flags[m], &transparent_hugepage_flags); + else + clear_bit(defrag_flags[m], &transparent_hugepage_flags); + } + return count; } static struct kobj_attribute defrag_attr = __ATTR_RW(defrag); From 1d8274b82cd1870eba883fd20204bcd8601c3527 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 8 Apr 2026 08:47:01 -0700 Subject: [PATCH 1997/5214] mm: huge_memory: refactor defrag_show() to use defrag_flags[] Replace the hardcoded if/else chain of test_bit() calls and string literals in defrag_show() with a loop over defrag_flags[] and defrag_mode_strings[] arrays introduced in the previous commit. This makes defrag_show() consistent with defrag_store() and eliminates the duplicated mode name strings. Link: https://lore.kernel.org/20260408-thp_defrag-v2-2-bc544c1bde4e@debian.org Signed-off-by: Breno Leitao Acked-by: David Hildenbrand (Arm) Tested-by: Lance Yang Reviewed-by: Lance Yang Reviewed-by: Barry Song Reviewed-by: Lorenzo Stoakes Tested-by: Zi Yan Acked-by: Zi Yan Cc: Baolin Wang Cc: Dev Jain Cc: Liam Howlett Cc: Nico Pache Cc: Ryan Roberts Signed-off-by: Andrew Morton --- mm/huge_memory.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 62e00b21fdf4..e9d499da0ac7 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -455,24 +455,30 @@ static const enum transparent_hugepage_flag defrag_flags[] = { static ssize_t defrag_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - const char *output; + int active = DEFRAG_NEVER; + int len = 0; + int i; - if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, - &transparent_hugepage_flags)) - output = "[always] defer defer+madvise madvise never"; - else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, - &transparent_hugepage_flags)) - output = "always [defer] defer+madvise madvise never"; - else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, - &transparent_hugepage_flags)) - output = "always defer [defer+madvise] madvise never"; - else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, - &transparent_hugepage_flags)) - output = "always defer defer+madvise [madvise] never"; - else - output = "always defer defer+madvise madvise [never]"; + for (i = 0; i < ARRAY_SIZE(defrag_flags); i++) { + if (test_bit(defrag_flags[i], &transparent_hugepage_flags)) { + active = i; + break; + } + } - return sysfs_emit(buf, "%s\n", output); + for (i = 0; i < ARRAY_SIZE(defrag_mode_strings); i++) { + if (i == active) + len += sysfs_emit_at(buf, len, "[%s] ", + defrag_mode_strings[i]); + else + len += sysfs_emit_at(buf, len, "%s ", + defrag_mode_strings[i]); + } + + /* Replace trailing space with newline */ + buf[len - 1] = '\n'; + + return len; } static ssize_t defrag_store(struct kobject *kobj, From f2a950170f7a78761c2b2e5e535716fb0f8c0813 Mon Sep 17 00:00:00 2001 From: "JP Kobryn (Meta)" Date: Mon, 6 Apr 2026 12:50:14 -0700 Subject: [PATCH 1998/5214] mm/vmpressure: skip socket pressure for costly order reclaim When reclaim is triggered by high order allocations on a fragmented system, vmpressure() can report poor reclaim efficiency even though the system has plenty of free memory. This is because many pages are scanned, but few are found to actually reclaim - the pages are actively in use and don't need to be freed. The resulting scan:reclaim ratio causes vmpressure() to assert socket pressure, throttling TCP throughput unnecessarily. Costly order allocations (above PAGE_ALLOC_COSTLY_ORDER) rely heavily on compaction to succeed, so poor reclaim efficiency at these orders does not necessarily indicate memory pressure. The kernel already treats this order as the boundary where reclaim is no longer expected to succeed and compaction may take over. Make vmpressure() order-aware through an additional parameter sourced from scan_control at existing call sites. Socket pressure is now only asserted when order <= PAGE_ALLOC_COSTLY_ORDER. Memcg reclaim is unaffected since try_to_free_mem_cgroup_pages() always uses order 0, which passes the filter unconditionally. Similarly, vmpressure_prio() now passes order 0 internally when calling vmpressure(), ensuring critical pressure from low reclaim priority is not suppressed by the order filter. The patch was motivated by a case of impacted net throughput in production. On one affected host, the memory state at the time showed ~15GB available, zero cgroup pressure, and the following buddyinfo state: Order FreePages 0: 133,970 1: 29,230 2: 17,351 3: 18,984 7+: 0 Using bpf, it was found that 94% of vmpressure calls on this host were from order-7 kswapd reclaim. TCP minimum recv window is rcv_ssthresh:19712. Before patch: 723 out of 3,843 (19%) TCP connections stuck at minimum recv window After live-patching and ~30min elapsed: 0 out of 3,470 TCP connections stuck at minimum recv window Link: https://lore.kernel.org/20260406195014.112521-1-jp.kobryn@linux.dev Signed-off-by: JP Kobryn (Meta) Reviewed-by: Rik van Riel Acked-by: Johannes Weiner Acked-by: Shakeel Butt Acked-by: Jakub Kicinski Reviewed-by: Barry Song Acked-by: Vlastimil Babka (SUSE) Cc: Axel Rasmussen Cc: David Hildenbrand Cc: Eric Dumazet Cc: Kairui Song Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Qi Zheng Cc: Suren Baghdasaryan Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- include/linux/vmpressure.h | 9 +++++---- mm/vmpressure.c | 15 ++++++++++++--- mm/vmscan.c | 8 ++++---- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/linux/vmpressure.h b/include/linux/vmpressure.h index 6a2f51ebbfd3..faecd5522401 100644 --- a/include/linux/vmpressure.h +++ b/include/linux/vmpressure.h @@ -30,8 +30,8 @@ struct vmpressure { struct mem_cgroup; #ifdef CONFIG_MEMCG -extern void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree, - unsigned long scanned, unsigned long reclaimed); +void vmpressure(gfp_t gfp, int order, struct mem_cgroup *memcg, bool tree, + unsigned long scanned, unsigned long reclaimed); extern void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio); extern void vmpressure_init(struct vmpressure *vmpr); @@ -44,8 +44,9 @@ extern int vmpressure_register_event(struct mem_cgroup *memcg, extern void vmpressure_unregister_event(struct mem_cgroup *memcg, struct eventfd_ctx *eventfd); #else -static inline void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree, - unsigned long scanned, unsigned long reclaimed) {} +static inline void vmpressure(gfp_t gfp, int order, struct mem_cgroup *memcg, + bool tree, unsigned long scanned, + unsigned long reclaimed) {} static inline void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio) {} #endif /* CONFIG_MEMCG */ diff --git a/mm/vmpressure.c b/mm/vmpressure.c index 3fbb86996c4d..f053554e5826 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -218,6 +218,7 @@ static void vmpressure_work_fn(struct work_struct *work) /** * vmpressure() - Account memory pressure through scanned/reclaimed ratio * @gfp: reclaimer's gfp mask + * @order: allocation order being reclaimed for * @memcg: cgroup memory controller handle * @tree: legacy subtree mode * @scanned: number of pages scanned @@ -236,7 +237,7 @@ static void vmpressure_work_fn(struct work_struct *work) * * This function does not return any value. */ -void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree, +void vmpressure(gfp_t gfp, int order, struct mem_cgroup *memcg, bool tree, unsigned long scanned, unsigned long reclaimed) { struct vmpressure *vmpr; @@ -307,7 +308,15 @@ void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree, level = vmpressure_calc_level(scanned, reclaimed); - if (level > VMPRESSURE_LOW) { + /* + * Once we go above COSTLY_ORDER, reclaim relies heavily on + * compaction to make progress. Reclaim efficiency was never a + * great proxy for pressure to begin with, but it's outright + * misleading with these high orders. Don't throttle sockets + * because somebody is attempting something crazy like an order-7 + * and predictably struggling. + */ + if (level > VMPRESSURE_LOW && order <= PAGE_ALLOC_COSTLY_ORDER) { /* * Let the socket buffer allocator know that * we are having trouble reclaiming LRU pages. @@ -348,7 +357,7 @@ void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio) * to the vmpressure() basically means that we signal 'critical' * level. */ - vmpressure(gfp, memcg, true, vmpressure_win, 0); + vmpressure(gfp, 0, memcg, true, vmpressure_win, 0); } #define MAX_VMPRESSURE_ARGS_LEN (strlen("critical") + strlen("hierarchy") + 2) diff --git a/mm/vmscan.c b/mm/vmscan.c index a9fd43b23a58..4b0984387658 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -5071,8 +5071,8 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority); if (!sc->proactive) - vmpressure(sc->gfp_mask, memcg, false, sc->nr_scanned - scanned, - sc->nr_reclaimed - reclaimed); + vmpressure(sc->gfp_mask, sc->order, memcg, false, + sc->nr_scanned - scanned, sc->nr_reclaimed - reclaimed); flush_reclaim_state(sc); @@ -6175,7 +6175,7 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc) /* Record the group's reclaim efficiency */ if (!sc->proactive) - vmpressure(sc->gfp_mask, memcg, false, + vmpressure(sc->gfp_mask, sc->order, memcg, false, sc->nr_scanned - scanned, sc->nr_reclaimed - reclaimed); @@ -6220,7 +6220,7 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc) /* Record the subtree's reclaim efficiency */ if (!sc->proactive) - vmpressure(sc->gfp_mask, sc->target_mem_cgroup, true, + vmpressure(sc->gfp_mask, sc->order, sc->target_mem_cgroup, true, sc->nr_scanned - nr_scanned, nr_node_reclaimed); if (nr_node_reclaimed) From d590df11be0f18cdf817fcd20e9f3c51962df5d7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 2 Apr 2026 08:26:50 +0100 Subject: [PATCH 1999/5214] mm/page_io: rename swap_iocb fields for clarity swap_iocb->pages tracks the number of bvec entries (folios), not base pages. Rename the array from bvec to bvecs and the counter from pages to nr_bvecs to accurately reflect their purpose. Link: https://lore.kernel.org/20260402072650.48811-1-devnexen@gmail.com Signed-off-by: David Carlier Suggested-by: Matthew Wilcox (Oracle) Suggested-by: David Hildenbrand Acked-by: David Hildenbrand (Arm) Acked-by: Chris Li Cc: Baoquan He Cc: Kairui Song Cc: Kemeng Shi Cc: NeilBrown Cc: Nhat Pham Signed-off-by: Andrew Morton --- mm/page_io.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/mm/page_io.c b/mm/page_io.c index 70cea9e24d2f..7ed76592e20d 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -326,8 +326,8 @@ static void bio_associate_blkg_from_page(struct bio *bio, struct folio *folio) struct swap_iocb { struct kiocb iocb; - struct bio_vec bvec[SWAP_CLUSTER_MAX]; - int pages; + struct bio_vec bvecs[SWAP_CLUSTER_MAX]; + int nr_bvecs; int len; }; static mempool_t *sio_pool; @@ -348,7 +348,7 @@ int sio_pool_init(void) static void sio_write_complete(struct kiocb *iocb, long ret) { struct swap_iocb *sio = container_of(iocb, struct swap_iocb, iocb); - struct page *page = sio->bvec[0].bv_page; + struct page *page = sio->bvecs[0].bv_page; int p; if (ret != sio->len) { @@ -362,15 +362,15 @@ static void sio_write_complete(struct kiocb *iocb, long ret) */ pr_err_ratelimited("Write error %ld on dio swapfile (%llu)\n", ret, swap_dev_pos(page_swap_entry(page))); - for (p = 0; p < sio->pages; p++) { - page = sio->bvec[p].bv_page; + for (p = 0; p < sio->nr_bvecs; p++) { + page = sio->bvecs[p].bv_page; set_page_dirty(page); ClearPageReclaim(page); } } - for (p = 0; p < sio->pages; p++) - end_page_writeback(sio->bvec[p].bv_page); + for (p = 0; p < sio->nr_bvecs; p++) + end_page_writeback(sio->bvecs[p].bv_page); mempool_free(sio, sio_pool); } @@ -397,13 +397,13 @@ static void swap_writepage_fs(struct folio *folio, struct swap_iocb **swap_plug) init_sync_kiocb(&sio->iocb, swap_file); sio->iocb.ki_complete = sio_write_complete; sio->iocb.ki_pos = pos; - sio->pages = 0; + sio->nr_bvecs = 0; sio->len = 0; } - bvec_set_folio(&sio->bvec[sio->pages], folio, folio_size(folio), 0); + bvec_set_folio(&sio->bvecs[sio->nr_bvecs], folio, folio_size(folio), 0); sio->len += folio_size(folio); - sio->pages += 1; - if (sio->pages == ARRAY_SIZE(sio->bvec) || !swap_plug) { + sio->nr_bvecs += 1; + if (sio->nr_bvecs == ARRAY_SIZE(sio->bvecs) || !swap_plug) { swap_write_unplug(sio); sio = NULL; } @@ -477,7 +477,7 @@ void swap_write_unplug(struct swap_iocb *sio) struct address_space *mapping = sio->iocb.ki_filp->f_mapping; int ret; - iov_iter_bvec(&from, ITER_SOURCE, sio->bvec, sio->pages, sio->len); + iov_iter_bvec(&from, ITER_SOURCE, sio->bvecs, sio->nr_bvecs, sio->len); ret = mapping->a_ops->swap_rw(&sio->iocb, &from); if (ret != -EIOCBQUEUED) sio_write_complete(&sio->iocb, ret); @@ -489,8 +489,8 @@ static void sio_read_complete(struct kiocb *iocb, long ret) int p; if (ret == sio->len) { - for (p = 0; p < sio->pages; p++) { - struct folio *folio = page_folio(sio->bvec[p].bv_page); + for (p = 0; p < sio->nr_bvecs; p++) { + struct folio *folio = page_folio(sio->bvecs[p].bv_page); count_mthp_stat(folio_order(folio), MTHP_STAT_SWPIN); count_memcg_folio_events(folio, PSWPIN, folio_nr_pages(folio)); @@ -499,8 +499,8 @@ static void sio_read_complete(struct kiocb *iocb, long ret) } count_vm_events(PSWPIN, sio->len >> PAGE_SHIFT); } else { - for (p = 0; p < sio->pages; p++) { - struct folio *folio = page_folio(sio->bvec[p].bv_page); + for (p = 0; p < sio->nr_bvecs; p++) { + struct folio *folio = page_folio(sio->bvecs[p].bv_page); folio_unlock(folio); } @@ -559,13 +559,13 @@ static void swap_read_folio_fs(struct folio *folio, struct swap_iocb **plug) init_sync_kiocb(&sio->iocb, sis->swap_file); sio->iocb.ki_pos = pos; sio->iocb.ki_complete = sio_read_complete; - sio->pages = 0; + sio->nr_bvecs = 0; sio->len = 0; } - bvec_set_folio(&sio->bvec[sio->pages], folio, folio_size(folio), 0); + bvec_set_folio(&sio->bvecs[sio->nr_bvecs], folio, folio_size(folio), 0); sio->len += folio_size(folio); - sio->pages += 1; - if (sio->pages == ARRAY_SIZE(sio->bvec) || !plug) { + sio->nr_bvecs += 1; + if (sio->nr_bvecs == ARRAY_SIZE(sio->bvecs) || !plug) { swap_read_unplug(sio); sio = NULL; } @@ -666,7 +666,7 @@ void __swap_read_unplug(struct swap_iocb *sio) struct address_space *mapping = sio->iocb.ki_filp->f_mapping; int ret; - iov_iter_bvec(&from, ITER_DEST, sio->bvec, sio->pages, sio->len); + iov_iter_bvec(&from, ITER_DEST, sio->bvecs, sio->nr_bvecs, sio->len); ret = mapping->a_ops->swap_rw(&sio->iocb, &from); if (ret != -EIOCBQUEUED) sio_read_complete(&sio->iocb, ret); From c70a9f639bfd662b95b5e3e64f4b62b13c237eca Mon Sep 17 00:00:00 2001 From: wangxuewen Date: Thu, 2 Apr 2026 14:49:46 +0800 Subject: [PATCH 2000/5214] mm/memory-failure: replace magic number 3 with GET_PAGE_MAX_RETRY_NUM Replace the hardcoded magic number 3 in get_any_page() with the existing GET_PAGE_MAX_RETRY_NUM macro for code consistency and maintainability. This change has no functional impact, only improves code readability and unifies the retry limit configuration. Link: https://lore.kernel.org/20260402064946.1124250-1-18810879172@163.com Signed-off-by: wangxuewen Acked-by: SeongJae Park Acked-by: Miaohe Lin Cc: Naoya Horiguchi Signed-off-by: Andrew Morton --- mm/memory-failure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 62b547c168fc..866c4428ac7e 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1418,7 +1418,7 @@ static int get_any_page(struct page *p, unsigned long flags) * We raced with (possibly temporary) unhandlable * page, retry. */ - if (pass++ < 3) { + if (pass++ < GET_PAGE_MAX_RETRY_NUM) { shake_page(p); goto try_again; } From 1d05e1f6ac26263dc27cfb2796ef4e5e24e070f4 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Tue, 31 Mar 2026 10:52:13 +0000 Subject: [PATCH 2001/5214] mm/page_alloc: cleanup flag vars in alloc_pages_bulk_noprof() These two variables are redundant, squash them to align alloc_pages_bulk_noprof() with the style used in alloc_frozen_pages_nolock_noprof(). Link: https://lore.kernel.org/20260331-b4-prepare_alloc_pages-flags-v1-1-ea2416def698@google.com Signed-off-by: Brendan Jackman Reviewed-by: Suren Baghdasaryan Reviewed-by: Vishal Moola Reviewed-by: Vlastimil Babka (SUSE) Reviewed-by: Anshuman Khandual Cc: Johannes Weiner Cc: Michal Hocko Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a81ae5781036..baf41005f90e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5054,7 +5054,6 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid, struct per_cpu_pages *pcp; struct list_head *pcp_list; struct alloc_context ac; - gfp_t alloc_gfp; unsigned int alloc_flags = ALLOC_WMARK_LOW; int nr_populated = 0, nr_account = 0; @@ -5095,10 +5094,8 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid, /* May set ALLOC_NOFRAGMENT, fragmentation will return 1 page. */ gfp &= gfp_allowed_mask; - alloc_gfp = gfp; - if (!prepare_alloc_pages(gfp, 0, preferred_nid, nodemask, &ac, &alloc_gfp, &alloc_flags)) + if (!prepare_alloc_pages(gfp, 0, preferred_nid, nodemask, &ac, &gfp, &alloc_flags)) goto out; - gfp = alloc_gfp; /* Find an allowed local zone that meets the low watermark. */ z = ac.preferred_zoneref; From b9fe373e7d3c5a0814fe45d8cb41f649ed5e244d Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Tue, 31 Mar 2026 08:07:30 +0100 Subject: [PATCH 2002/5214] mm/thp: dead code cleanup in Kconfig There is already an 'if TRANSPARENT_HUGEPAGE' condition wrapping several config options e.g. 'READ_ONLY_THP_FOR_FS', making the 'depends on' statement for each of these a duplicate dependency (dead code). I propose leaving the outer 'if TRANSPARENT_HUGEPAGE...endif' and removing the individual 'depends on TRANSPARENT_HUGEPAGE' statement from each option. This dead code was found by kconfirm, a static analysis tool for Kconfig. Link: https://lore.kernel.org/20260331070730.33915-1-julianbraha@gmail.com Signed-off-by: Julian Braha Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Anshuman Khandual Cc: Johannes Weiner Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/Kconfig b/mm/Kconfig index e8bf1e9e6ad9..e221fa1dc54d 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -810,7 +810,6 @@ if TRANSPARENT_HUGEPAGE choice prompt "Transparent Hugepage Support sysfs defaults" - depends on TRANSPARENT_HUGEPAGE default TRANSPARENT_HUGEPAGE_ALWAYS help Selects the sysfs defaults for Transparent Hugepage Support. @@ -840,7 +839,6 @@ endchoice choice prompt "Shmem hugepage allocation defaults" - depends on TRANSPARENT_HUGEPAGE default TRANSPARENT_HUGEPAGE_SHMEM_HUGE_NEVER help Selects the hugepage allocation policy defaults for @@ -886,7 +884,6 @@ endchoice choice prompt "Tmpfs hugepage allocation defaults" - depends on TRANSPARENT_HUGEPAGE default TRANSPARENT_HUGEPAGE_TMPFS_HUGE_NEVER help Selects the hugepage allocation policy defaults for @@ -931,7 +928,7 @@ endchoice config THP_SWAP def_bool y - depends on TRANSPARENT_HUGEPAGE && ARCH_WANTS_THP_SWAP && SWAP && 64BIT + depends on ARCH_WANTS_THP_SWAP && SWAP && 64BIT help Swap transparent huge pages in one piece, without splitting. XXX: For now, swap cluster backing transparent huge page From 94e0bcde055ee1bd758218ec3a4ff098874123ac Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Mon, 30 Mar 2026 18:20:57 -0700 Subject: [PATCH 2003/5214] mm, page_alloc: reintroduce page allocation stall warning Previously, we had warnings when a single page allocation took longer than reasonably expected. This was introduced in commit 63f53dea0c98 ("mm: warn about allocations which stall for too long"). The warning was subsequently reverted in commit 400e22499dd9 ("mm: don't warn about allocations which stall for too long") because it was possible to generate memory pressure that would effectively stall further progress through printk execution. Page allocation stalls in excess of 10 seconds are always useful to debug because they can result in severe userspace unresponsiveness. Adding this artifact can be used to correlate with userspace going out to lunch and to understand the state of memory at the time. There should be a reasonable expectation that this warning will never trigger given it is very passive, it will only be emitted when a page allocation takes longer than 10 seconds. If it does trigger, this reveals an issue that should be fixed: a single page allocation should never loop for more than 10 seconds without oom killing to make memory available. Unlike the original implementation, this implementation only reports stalls once for the system every 10 seconds. Otherwise, many concurrent reclaimers could spam the kernel log unnecessarily. Stalls are only reported when calling into direct reclaim. Link: https://lore.kernel.org/371c86c8-1d47-bd70-b74c-769842718b1f@google.com Signed-off-by: David Rientjes Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Shakeel Butt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: Johannes Weiner Cc: Petr Mladek Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index baf41005f90e..d9c6313e69f3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -285,6 +285,14 @@ EXPORT_SYMBOL(nr_node_ids); EXPORT_SYMBOL(nr_online_nodes); #endif +/* + * When page allocations stall for longer than a threshold, + * ALLOC_STALL_WARN_MSECS, leave a warning in the kernel log. Only one warning + * will be printed during this duration for the entire system. + */ +#define ALLOC_STALL_WARN_MSECS (10 * 1000UL) +static unsigned long alloc_stall_warn_jiffies = INITIAL_JIFFIES; + static bool page_contains_unaccepted(struct page *page, unsigned int order); static bool cond_accept_memory(struct zone *zone, unsigned int order, int alloc_flags); @@ -4688,6 +4696,40 @@ check_retry_cpuset(int cpuset_mems_cookie, struct alloc_context *ac) return false; } +static void check_alloc_stall_warn(gfp_t gfp_mask, nodemask_t *nodemask, + unsigned int order, unsigned long alloc_start_time) +{ + static DEFINE_SPINLOCK(alloc_stall_lock); + unsigned long stall_msecs = jiffies_to_msecs(jiffies - alloc_start_time); + + if (likely(stall_msecs < ALLOC_STALL_WARN_MSECS)) + return; + if (time_is_after_jiffies(READ_ONCE(alloc_stall_warn_jiffies))) + return; + if (gfp_mask & __GFP_NOWARN) + return; + + if (!spin_trylock(&alloc_stall_lock)) + return; + + /* Check again, this time under the lock */ + if (time_is_after_jiffies(alloc_stall_warn_jiffies)) { + spin_unlock(&alloc_stall_lock); + return; + } + + WRITE_ONCE(alloc_stall_warn_jiffies, jiffies + msecs_to_jiffies(ALLOC_STALL_WARN_MSECS)); + spin_unlock(&alloc_stall_lock); + + pr_warn("%s: page allocation stall for %lu secs: order:%d, mode:%#x(%pGg) nodemask=%*pbl", + current->comm, stall_msecs / MSEC_PER_SEC, order, gfp_mask, &gfp_mask, + nodemask_pr_args(nodemask)); + cpuset_print_current_mems_allowed(); + pr_cont("\n"); + dump_stack(); + warn_alloc_show_mem(gfp_mask, nodemask); +} + static inline struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct alloc_context *ac) @@ -4708,6 +4750,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, int reserve_flags; bool compact_first = false; bool can_retry_reserves = true; + unsigned long alloc_start_time = jiffies; if (unlikely(nofail)) { /* @@ -4823,6 +4866,9 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, if (current->flags & PF_MEMALLOC) goto nopage; + /* If allocation has taken excessively long, warn about it */ + check_alloc_stall_warn(gfp_mask, ac->nodemask, order, alloc_start_time); + /* Try direct reclaim and then allocating */ if (!compact_first) { page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, From 9669b87065a6fe96198f3df2c3d125c5f5c1f210 Mon Sep 17 00:00:00 2001 From: "JP Kobryn (Meta)" Date: Fri, 24 Apr 2026 22:34:17 -0700 Subject: [PATCH 2004/5214] mm/lruvec: preemptively free dead folios during lru_add drain Of all observable lruvec lock contention in our fleet, we find that ~24% occurs when dead folios are present in lru_add batches at drain time. This is wasteful in the sense that the folio is added to the LRU just to be immediately removed via folios_put_refs(), incurring two unnecessary lock acquisitions. Eliminate this overhead by preemptively cleaning up dead folios before they make it into the LRU. Use folio_ref_freeze() to filter folios whose only remaining refcount is the batch ref. When dead folios are found, move them off the add batch and onto a temporary batch to be freed. PG_active may be set on a batched folio as well as PG_unevictable (via migration path). Since filtered folios bypass the normal lru_add() cleanup, both flags must be cleared before freeing. During A/B testing on one of our prod instagram workloads (high-frequency short-lived requests), the patch intercepted almost all dead folios before they entered the LRU. Data collected using the mm_lru_insertion tracepoint shows the effectiveness of the patch: Per-host LRU add averages at 95% CPU load (60 hosts each side, 3 x 60s intervals) dead folios/min total folios/min dead % unpatched: 1,297,785 19,341,986 6.7097% patched: 14 19,039,996 0.0001% Within this workload, we save ~2.6M lock acquisitions per minute per host as a result. System-wide memory stats improved on the patched side also at 95% CPU load: - direct reclaim scanning reduced 7% - allocation stalls reduced 5.2% - compaction stalls reduced 12.3% - page frees reduced 4.9% No regressions were observed in requests served per second or request tail latency (p99). Both metrics showed directional improvement at higher CPU utilization (comparing 85% to 95%). Note that tests were performed using classic LRU. Link: https://lore.kernel.org/20260425053417.351146-1-jp.kobryn@linux.dev Signed-off-by: JP Kobryn (Meta) Reviewed-by: Matthew Wilcox (Oracle) Acked-by: Shakeel Butt Acked-by: Michal Hocko Cc: Axel Rasmussen Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: Johannes Weiner Cc: Kairui Song Cc: Kemeng Shi Cc: Nhat Pham Cc: Rik van Riel Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- mm/swap.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/mm/swap.c b/mm/swap.c index 5cc44f0de987..2dd84813f4dd 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -160,14 +160,42 @@ static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn) int i; struct lruvec *lruvec = NULL; unsigned long flags = 0; + struct folio_batch free_fbatch; + bool is_lru_add = (move_fn == lru_add); + + /* + * If we're adding to the LRU, preemptively filter dead folios. Use + * this dedicated folio batch for temp storage and deferred cleanup. + */ + if (is_lru_add) + folio_batch_init(&free_fbatch); for (i = 0; i < folio_batch_count(fbatch); i++) { struct folio *folio = fbatch->folios[i]; /* block memcg migration while the folio moves between lru */ - if (move_fn != lru_add && !folio_test_clear_lru(folio)) + if (!is_lru_add && !folio_test_clear_lru(folio)) continue; + /* + * Filter dead folios by moving them from the add batch to the temp + * batch for freeing after this loop. + * + * We're bypassing normal cleanup. Clear flags that are not + * applicable to dead folios. + * + * Since the folio may be part of a huge page, unqueue from + * deferred split list to avoid a dangling list entry. + */ + if (is_lru_add && folio_ref_freeze(folio, 1)) { + __folio_clear_active(folio); + __folio_clear_unevictable(folio); + folio_unqueue_deferred_split(folio); + fbatch->folios[i] = NULL; + folio_batch_add(&free_fbatch, folio); + continue; + } + folio_lruvec_relock_irqsave(folio, &lruvec, &flags); move_fn(lruvec, folio); @@ -176,6 +204,13 @@ static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn) if (lruvec) lruvec_unlock_irqrestore(lruvec, flags); + + /* Cleanup filtered dead folios. */ + if (is_lru_add) { + mem_cgroup_uncharge_folios(&free_fbatch); + free_unref_folios(&free_fbatch); + } + folios_put(fbatch); } @@ -964,6 +999,10 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs) struct folio *folio = folios->folios[i]; unsigned int nr_refs = refs ? refs[i] : 1; + /* Folio batch entry may have been preemptively removed during drain. */ + if (!folio) + continue; + if (is_huge_zero_folio(folio)) continue; From 15807d0ddde37407af72859426b654f3d1972b00 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sat, 28 Mar 2026 12:25:34 +0530 Subject: [PATCH 2005/5214] mm/hugetlb: fix hugetlb cgroup rsvd charge/uncharge mismatch In alloc_hugetlb_folio(), a single h_cg pointer is used for both the rsvd and non-rsvd hugetlb cgroup charges. When map_chg is set, hugetlb_cgroup_charge_cgroup_rsvd() stores the charged cgroup in h_cg, but the immediately following hugetlb_cgroup_charge_cgroup() overwrites h_cg with the non-rsvd cgroup pointer. As a result, hugetlb_cgroup_commit_charge_rsvd() stores the wrong (non-rsvd) cgroup pointer into the folio's rsvd slot. When the folio is later freed, free_huge_folio() unconditionally calls both hugetlb_cgroup_uncharge_folio() and hugetlb_cgroup_uncharge_folio_rsvd(). The rsvd uncharge reads back the wrong cgroup from the folio and decrements a counter that was never charged for that cgroup, causing a page_counter underflow: page_counter underflow: -512 nr_pages=512 WARNING: mm/page_counter.c:61 at page_counter_cancel Fix this by introducing a separate h_cg_rsvd pointer exclusively for the rsvd charge path, keeping the rsvd and non-rsvd charges fully independent through their charge, commit, and error uncharge paths. Link: https://lore.kernel.org/20260328065534.346053-1-kartikey406@gmail.com Fixes: 08cf9faf7558 ("hugetlb_cgroup: support noreserve mappings") Reported-by: syzbot+226c1f947186f8fef796@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=226c1f947186f8fef796 Signed-off-by: Deepanshu Kartikey Reviewed-by: Muchun Song Cc: David Hildenbrand Cc: Oscar Salvador Cc: Mina Almasry Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4b80b167cc9c..bcc657abbe35 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2859,6 +2859,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, map_chg_state map_chg; int ret, idx; struct hugetlb_cgroup *h_cg = NULL; + struct hugetlb_cgroup *h_cg_rsvd = NULL; gfp_t gfp = htlb_alloc_mask(h) | __GFP_RETRY_MAYFAIL; idx = hstate_index(h); @@ -2909,7 +2910,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { ret = hugetlb_cgroup_charge_cgroup_rsvd( - idx, pages_per_huge_page(h), &h_cg); + idx, pages_per_huge_page(h), &h_cg_rsvd); if (ret) goto out_subpool_put; } @@ -2951,7 +2952,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { hugetlb_cgroup_commit_charge_rsvd(idx, pages_per_huge_page(h), - h_cg, folio); + h_cg_rsvd, folio); } spin_unlock_irq(&hugetlb_lock); @@ -3003,7 +3004,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, out_uncharge_cgroup_reservation: if (map_chg) hugetlb_cgroup_uncharge_cgroup_rsvd(idx, pages_per_huge_page(h), - h_cg); + h_cg_rsvd); out_subpool_put: /* * put page to subpool iff the quota of subpool's rsv_hpages is used From faae0ca3628b99119c3ad9780259d25b02ddff93 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 26 Mar 2026 12:31:57 +0000 Subject: [PATCH 2006/5214] drm/managed: use special gfp_t format specifier Patch series "treewide: fixup gfp_t printks", v2. Use vprintf()'s special gfp_t conversion in a few places. This patch (of 3): %pGg produces nice readable output and decouples the format string from the size of gfp_t. Link: https://lore.kernel.org/20260326-gfp64-v2-0-d916021cecdf@google.com Link: https://lore.kernel.org/20260326-gfp64-v2-1-d916021cecdf@google.com Signed-off-by: Brendan Jackman Cc: Alexander Potapenko Cc: Allison Collins Cc: Allison Henderson Cc: Dave Airlie Cc: David S. Miller Cc: Dmitry Vyukov Cc: Eric Dumazet Cc: Jakub Kacinski Cc: Maarten Lankhorst Cc: Marco Elver Cc: Maxime Ripard Cc: Paolo Abeni Cc: Simon Horman Cc: Stanislaw Gruszka Cc: Thomas Zimemrmann Signed-off-by: Andrew Morton --- drivers/gpu/drm/drm_managed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 247f468731de..a9da94319b05 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -232,8 +232,8 @@ void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) dr = alloc_dr(NULL, size, gfp, dev_to_node(dev->dev)); if (!dr) { - drm_dbg_drmres(dev, "failed to allocate %zu bytes, %u flags\n", - size, gfp); + drm_dbg_drmres(dev, "failed to allocate %zu bytes, %pGg\n", + size, &gfp); return NULL; } dr->node.name = kstrdup_const("kmalloc", gfp); From d36102a5f55321b9bdf3e40fbb7b5c482e6dfb12 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 26 Mar 2026 12:31:59 +0000 Subject: [PATCH 2007/5214] mm/kfence: use special gfp_t format specifier %pGg produces nice readable output and decouples the format string from the size of gfp_t. Link: https://lore.kernel.org/20260326-gfp64-v2-3-d916021cecdf@google.com Signed-off-by: Brendan Jackman Cc: Alexander Potapenko Cc: Allison Collins Cc: Allison Henderson Cc: Dave Airlie Cc: David S. Miller Cc: Dmitry Vyukov Cc: Eric Dumazet Cc: Jakub Kacinski Cc: Jakub Kicinski Cc: Maarten Lankhorst Cc: Marco Elver Cc: Maxime Ripard Cc: Paolo Abeni Cc: Simon Horman Cc: Stanislaw Gruszka Cc: Thomas Zimemrmann Signed-off-by: Andrew Morton --- mm/kfence/kfence_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/kfence/kfence_test.c b/mm/kfence/kfence_test.c index 5725a367246d..10424cd25e5a 100644 --- a/mm/kfence/kfence_test.c +++ b/mm/kfence/kfence_test.c @@ -263,7 +263,7 @@ static void *test_alloc(struct kunit *test, size_t size, gfp_t gfp, enum allocat break; } - kunit_info(test, "%s: size=%zu, gfp=%x, policy=%s, cache=%i\n", __func__, size, gfp, + kunit_info(test, "%s: size=%zu, gfp=%pGg, policy=%s, cache=%i\n", __func__, size, &gfp, policy_name, !!test_cache); /* From 3f994146201563c5374a5ea17486b80323120d6d Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 26 Mar 2026 12:32:00 +0000 Subject: [PATCH 2008/5214] net/rds: use special gfp_t format specifier %pGg produces nice readable output and decouples the format string from the size of gfp_t. Link: https://lore.kernel.org/20260326-gfp64-v2-4-d916021cecdf@google.com Signed-off-by: Brendan Jackman Acked-by: Allison Henderson Cc: Alexander Potapenko Cc: Allison Collins Cc: Dave Airlie Cc: David S. Miller Cc: Dmitry Vyukov Cc: Eric Dumazet Cc: Jakub Kacinski Cc: Maarten Lankhorst Cc: Marco Elver Cc: Maxime Ripard Cc: Paolo Abeni Cc: Simon Horman Cc: Stanislaw Gruszka Cc: Thomas Zimemrmann Signed-off-by: Andrew Morton --- net/rds/tcp_recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c index 49f96ee0c40f..ffe843ca219c 100644 --- a/net/rds/tcp_recv.c +++ b/net/rds/tcp_recv.c @@ -275,7 +275,7 @@ static int rds_tcp_read_sock(struct rds_conn_path *cp, gfp_t gfp) desc.count = 1; /* give more than one skb per call */ tcp_read_sock(sock->sk, &desc, rds_tcp_data_recv); - rdsdebug("tcp_read_sock for tc %p gfp 0x%x returned %d\n", tc, gfp, + rdsdebug("tcp_read_sock for tc %p gfp %pGg returned %d\n", tc, &gfp, desc.error); if (skb_queue_empty_lockless(&sock->sk->sk_receive_queue) && From 8aa442cfce79e2d69e72fc8e0c0864ac2971149d Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Feb 2026 12:15:16 -0800 Subject: [PATCH 2009/5214] dax/kmem: account for partial discontiguous resource upon removal When dev_dax_kmem_probe() partially succeeds (at least one range is mapped) but a subsequent range fails request_mem_region() or add_memory_driver_managed(), the probe silently continues, ultimately returning success, but with the corresponding range resource NULL'ed out. dev_dax_kmem_remove() iterates over all dax_device ranges regardless of if the underlying resource exists. When remove_memory() is called later, it returns 0 because the memory was never added which causes dev_dax_kmem_remove() to incorrectly assume the (nonexistent) resource can be removed and attempts cleanup on a NULL pointer. Fix this by skipping these ranges altogether, noting that these cases are considered success, such that the cleanup is still reached when all actually-added ranges are successfully removed. Link: https://lore.kernel.org/20260223201516.1517657-1-dave@stgolabs.net Fixes: 60e93dc097f7 ("device-dax: add dis-contiguous resource support") Signed-off-by: Davidlohr Bueso Reviewed-by: Ben Cheatham Reviewed-by: Alison Schofield Reviewed-by: Jonathan Cameron Cc: Dan Williams Cc: Dave Jiang Cc: Vishal Verma Signed-off-by: Andrew Morton --- drivers/dax/kmem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index 2cc8749bc871..a18e2b968e4d 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -227,6 +227,12 @@ static void dev_dax_kmem_remove(struct dev_dax *dev_dax) if (rc) continue; + /* range was never added during probe */ + if (!data->res[i]) { + success++; + continue; + } + rc = remove_memory(range.start, range_len(&range)); if (rc == 0) { remove_resource(data->res[i]); From 7e8983f317ab0efd13aa573e166d7ad69e36a429 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Wed, 15 Apr 2026 10:15:09 +0530 Subject: [PATCH 2010/5214] selftests/mm: simplify byte pattern checking in mremap_test The original version of mremap_test (7df666253f26: "kselftests: vm: add mremap tests") validated remapped contents byte-by-byte and printed a mismatch index in case the bytes streams didn't match. That was rather inefficient, especially also if the test passed. Later, commit 7033c6cc9620 ("selftests/mm: mremap_test: optimize execution time from minutes to seconds using chunkwise memcmp") used memcmp() on bigger chunks, to fallback to byte-wise scanning to detect the problematic index only if it discovered a problem. However, the implementation is overly complicated (e.g., get_sqrt() is currently not optimal) and we don't really have to report the exact index: whoever debugs the failing test can figure that out. Let's simplify by just comparing both byte streams with memcmp() and not detecting the exact failed index. Link: https://lore.kernel.org/20260415044509.579428-1-dev.jain@arm.com Signed-off-by: Dev Jain Reported-by: Sarthak Sharma Tested-by: Sarthak Sharma Acked-by: Mike Rapoport (Microsoft) Acked-by: David Hildenbrand (Arm) Cc: Anshuman Khandual Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: David Laight Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/mremap_test.c | 109 +++-------------------- 1 file changed, 10 insertions(+), 99 deletions(-) diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c index 308576437228..131d9d6db867 100644 --- a/tools/testing/selftests/mm/mremap_test.c +++ b/tools/testing/selftests/mm/mremap_test.c @@ -76,27 +76,6 @@ enum { .expect_failure = should_fail \ } -/* compute square root using binary search */ -static unsigned long get_sqrt(unsigned long val) -{ - unsigned long low = 1; - - /* assuming rand_size is less than 1TB */ - unsigned long high = (1UL << 20); - - while (low <= high) { - unsigned long mid = low + (high - low) / 2; - unsigned long temp = mid * mid; - - if (temp == val) - return mid; - if (temp < val) - low = mid + 1; - high = mid - 1; - } - return low; -} - /* * Returns false if the requested remap region overlaps with an * existing mapping (e.g text, stack) else returns true. @@ -995,11 +974,9 @@ static long long remap_region(struct config c, unsigned int threshold_mb, char *rand_addr) { void *addr, *tmp_addr, *src_addr, *dest_addr, *dest_preamble_addr = NULL; - unsigned long long t, d; struct timespec t_start = {0, 0}, t_end = {0, 0}; long long start_ns, end_ns, align_mask, ret, offset; unsigned long long threshold; - unsigned long num_chunks; if (threshold_mb == VALIDATION_NO_THRESHOLD) threshold = c.region_size; @@ -1068,87 +1045,21 @@ static long long remap_region(struct config c, unsigned int threshold_mb, goto clean_up_dest_preamble; } - /* - * Verify byte pattern after remapping. Employ an algorithm with a - * square root time complexity in threshold: divide the range into - * chunks, if memcmp() returns non-zero, only then perform an - * iteration in that chunk to find the mismatch index. - */ - num_chunks = get_sqrt(threshold); - for (unsigned long i = 0; i < num_chunks; ++i) { - size_t chunk_size = threshold / num_chunks; - unsigned long shift = i * chunk_size; - - if (!memcmp(dest_addr + shift, rand_addr + shift, chunk_size)) - continue; - - /* brute force iteration only over mismatch segment */ - for (t = shift; t < shift + chunk_size; ++t) { - if (((char *) dest_addr)[t] != rand_addr[t]) { - ksft_print_msg("Data after remap doesn't match at offset %llu\n", - t); - ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[t] & 0xff, - ((char *) dest_addr)[t] & 0xff); - ret = -1; - goto clean_up_dest; - } - } - } - - /* - * if threshold is not divisible by num_chunks, then check the - * last chunk - */ - for (t = num_chunks * (threshold / num_chunks); t < threshold; ++t) { - if (((char *) dest_addr)[t] != rand_addr[t]) { - ksft_print_msg("Data after remap doesn't match at offset %llu\n", - t); - ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[t] & 0xff, - ((char *) dest_addr)[t] & 0xff); - ret = -1; - goto clean_up_dest; - } + /* Verify byte pattern after remapping */ + if (memcmp(dest_addr, rand_addr, threshold)) { + ksft_print_msg("Data after remap doesn't match\n"); + ret = -1; + goto clean_up_dest; } /* Verify the dest preamble byte pattern after remapping */ - if (!c.dest_preamble_size) - goto no_preamble; - - num_chunks = get_sqrt(c.dest_preamble_size); - - for (unsigned long i = 0; i < num_chunks; ++i) { - size_t chunk_size = c.dest_preamble_size / num_chunks; - unsigned long shift = i * chunk_size; - - if (!memcmp(dest_preamble_addr + shift, rand_addr + shift, - chunk_size)) - continue; - - /* brute force iteration only over mismatched segment */ - for (d = shift; d < shift + chunk_size; ++d) { - if (((char *) dest_preamble_addr)[d] != rand_addr[d]) { - ksft_print_msg("Preamble data after remap doesn't match at offset %llu\n", - d); - ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[d] & 0xff, - ((char *) dest_preamble_addr)[d] & 0xff); - ret = -1; - goto clean_up_dest; - } - } + if (c.dest_preamble_size && + memcmp(dest_preamble_addr, rand_addr, c.dest_preamble_size)) { + ksft_print_msg("Preamble data after remap doesn't match\n"); + ret = -1; + goto clean_up_dest; } - for (d = num_chunks * (c.dest_preamble_size / num_chunks); d < c.dest_preamble_size; ++d) { - if (((char *) dest_preamble_addr)[d] != rand_addr[d]) { - ksft_print_msg("Preamble data after remap doesn't match at offset %llu\n", - d); - ksft_print_msg("Expected: %#x\t Got: %#x\n", rand_addr[d] & 0xff, - ((char *) dest_preamble_addr)[d] & 0xff); - ret = -1; - goto clean_up_dest; - } - } - -no_preamble: start_ns = t_start.tv_sec * NS_PER_SEC + t_start.tv_nsec; end_ns = t_end.tv_sec * NS_PER_SEC + t_end.tv_nsec; ret = end_ns - start_ns; From c373f7f98e6ad591c85d40548cf8b6443be69311 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:50 +0800 Subject: [PATCH 2011/5214] mm/sparse-vmemmap: fix vmemmap accounting underflow Patch series "mm: Fix vmemmap optimization accounting and initialization", v8. The series fixes several bugs in vmemmap optimization, mainly around incorrect page accounting and memmap initialization in DAX and memory hotplug paths. It also fixes pageblock migratetype initialization and struct page initialization for ZONE_DEVICE compound pages. Patches 1-4 fix vmemmap accounting issues. Patch 1 fixes an accounting underflow in the section activation failure path by moving vmemmap page accounting into the lower-level allocation and freeing helpers. Patch 2 fixes incorrect altmap passing in the memory hotplug error path. Patch 3 passes pgmap through memory deactivation paths so the teardown side can determine whether vmemmap optimization was in effect. Patch 4 uses that information to account the optimized DAX vmemmap size correctly. Patches 5-6 fix initialization issues in mm/mm_init. One makes sure all pageblocks in ZONE_DEVICE compound pages get their migratetype initialized. The other fixes a case where DAX memory hotplug reuses an unoptimized early-section memmap while compound_nr_pages() still assumes vmemmap optimization, leaving tail struct pages uninitialized. This patch (of 6): In section_activate(), if populate_section_memmap() fails, the error handling path calls section_deactivate() to roll back the state. This causes a vmemmap accounting imbalance. Since commit c3576889d87b ("mm: fix accounting of memmap pages"), memmap pages are accounted for only after populate_section_memmap() succeeds. However, the failure path unconditionally calls section_deactivate(), which decreases the vmemmap count. Consequently, a failure in populate_section_memmap() leads to an accounting underflow, incorrectly reducing the system's tracked vmemmap usage. Fix this more thoroughly by moving all accounting calls into the lower level functions that actually perform the vmemmap allocation and freeing: - populate_section_memmap() accounts for newly allocated vmemmap pages - depopulate_section_memmap() unaccounts when vmemmap is freed This ensures proper accounting in all code paths, including error handling and early section cases. Link: https://lore.kernel.org/20260428081855.1249045-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260428081855.1249045-2-songmuchun@bytedance.com Fixes: c3576889d87b ("mm: fix accounting of memmap pages") Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/sparse-vmemmap.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 43f82621dd92..60e55e78d7ff 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -651,7 +651,12 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - return __populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); + struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap, + pgmap); + + memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); + + return page; } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, @@ -660,13 +665,17 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); + memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); vmemmap_free(start, end, altmap); } + static void free_map_bootmem(struct page *memmap) { unsigned long start = (unsigned long)memmap; unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION); + memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), + PAGE_SIZE))); vmemmap_free(start, end, NULL); } @@ -769,14 +778,10 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, * The memmap of early sections is always fully populated. See * section_activate() and pfn_valid() . */ - if (!section_is_early) { - memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); + if (!section_is_early) depopulate_section_memmap(pfn, nr_pages, altmap); - } else if (memmap) { - memmap_boot_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), - PAGE_SIZE))); + else if (memmap) free_map_bootmem(memmap); - } if (empty) ms->section_mem_map = (unsigned long)NULL; @@ -821,7 +826,6 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, section_deactivate(pfn, nr_pages, altmap); return ERR_PTR(-ENOMEM); } - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); return memmap; } From 2fac4afa0e2e68841334c78c1821e49f74fbc66a Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:51 +0800 Subject: [PATCH 2012/5214] mm/memory_hotplug: fix incorrect altmap passing in error path In create_altmaps_and_memory_blocks(), when arch_add_memory() succeeds with memmap_on_memory enabled, the vmemmap pages are allocated from params.altmap. If create_memory_block_devices() subsequently fails, the error path calls arch_remove_memory() with a NULL altmap instead of params.altmap. This is a bug that could lead to memory corruption. Since altmap is NULL, vmemmap_free() falls back to freeing the vmemmap pages into the system buddy allocator via free_pages() instead of the altmap. arch_remove_memory() then immediately destroys the physical linear mapping for this memory. This injects unowned pages into the buddy allocator, causing machine checks or memory corruption if the system later attempts to allocate and use those freed pages. Fix this by passing params.altmap to arch_remove_memory() in the error path. Link: https://lore.kernel.org/20260428081855.1249045-3-songmuchun@bytedance.com Fixes: 6b8f0798b85a ("mm/memory_hotplug: split memmap_on_memory requests across memblocks") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Reviewed-by: Georgi Djakov Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/memory_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 40c7915dabe0..cf4f77108c43 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1470,7 +1470,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret = create_memory_block_devices(cur_start, memblock_size, nid, params.altmap, group); if (ret) { - arch_remove_memory(cur_start, memblock_size, NULL); + arch_remove_memory(cur_start, memblock_size, params.altmap); kfree(params.altmap); goto out; } From 3bbc54dd1b62f1a4b218c70aafbeceeba7c90c5d Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:52 +0800 Subject: [PATCH 2013/5214] mm/sparse-vmemmap: pass @pgmap argument to memory deactivation paths Currently, the memory hot-remove call chain -- arch_remove_memory(), __remove_pages(), sparse_remove_section() and section_deactivate() -- does not carry the struct dev_pagemap pointer. This prevents the lower levels from knowing whether the section was originally populated with vmemmap optimizations (e.g., DAX with vmemmap optimization enabled). Without this information, we cannot call vmemmap_can_optimize() to determine if the vmemmap pages were optimized. As a result, the vmemmap page accounting during teardown will mistakenly assume a non-optimized allocation, leading to incorrect memmap statistics. To lay the groundwork for fixing the vmemmap page accounting, we need to pass the @pgmap pointer down to the deactivation location. Plumb the @pgmap argument through the APIs of arch_remove_memory(), __remove_pages() and sparse_remove_section(), mirroring the corresponding *_activate() paths. Link: https://lore.kernel.org/20260428081855.1249045-4-songmuchun@bytedance.com Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- arch/arm64/mm/mmu.c | 5 +++-- arch/loongarch/mm/init.c | 5 +++-- arch/powerpc/mm/mem.c | 5 +++-- arch/riscv/mm/init.c | 5 +++-- arch/s390/mm/init.c | 5 +++-- arch/x86/mm/init_64.c | 5 +++-- include/linux/memory_hotplug.h | 8 +++++--- mm/memory_hotplug.c | 13 +++++++------ mm/memremap.c | 4 ++-- mm/sparse-vmemmap.c | 12 ++++++------ 10 files changed, 38 insertions(+), 29 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index dd85e093ffdb..e5a42b7a0160 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -2024,12 +2024,13 @@ int arch_add_memory(int nid, u64 start, u64 size, return ret; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap, pgmap); __remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size); } diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 031b39eb081c..687980b6e91f 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -119,12 +119,13 @@ int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) return ret; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap, pgmap); } #endif diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 648d0c5602ec..4c1afab91996 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -158,12 +158,13 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, return rc; } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap, pgmap); arch_remove_linear_mapping(start, size); } #endif diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index fa8d2f6f554b..885f1db4e9bf 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1742,9 +1742,10 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *param return ret; } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { - __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap); + __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap, pgmap); remove_linear_mapping(start, size); flush_tlb_all(); } diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 1f72efc2a579..11a689423440 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -276,12 +276,13 @@ int arch_add_memory(int nid, u64 start, u64 size, return rc; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap, pgmap); vmem_remove_mapping(start, size); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index df2261fa4f98..77b889b71cf3 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1288,12 +1288,13 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) remove_pagetable(start, end, true, NULL); } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap, pgmap); kernel_physical_mapping_remove(start, start + size); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 815e908c4135..7c9d66729c60 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -135,9 +135,10 @@ static inline bool movable_node_is_enabled(void) return movable_node_enabled; } -extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap); +extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, + struct dev_pagemap *pgmap); extern void __remove_pages(unsigned long start_pfn, unsigned long nr_pages, - struct vmem_altmap *altmap); + struct vmem_altmap *altmap, struct dev_pagemap *pgmap); /* reasonably generic interface to expand the physical pages */ extern int __add_pages(int nid, unsigned long start_pfn, unsigned long nr_pages, @@ -307,7 +308,8 @@ extern int sparse_add_section(int nid, unsigned long pfn, unsigned long nr_pages, struct vmem_altmap *altmap, struct dev_pagemap *pgmap); extern void sparse_remove_section(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap); + struct vmem_altmap *altmap, + struct dev_pagemap *pgmap); extern struct zone *zone_for_pfn_range(enum mmop online_type, int nid, struct memory_group *group, unsigned long start_pfn, unsigned long nr_pages); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index cf4f77108c43..462d8dcd636d 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -576,6 +576,7 @@ void remove_pfn_range_from_zone(struct zone *zone, * @pfn: starting pageframe (must be aligned to start of a section) * @nr_pages: number of pages to remove (must be multiple of section size) * @altmap: alternative device page map or %NULL if default memmap is used + * @pgmap: device page map or %NULL if not ZONE_DEVICE * * Generic helper function to remove section mappings and sysfs entries * for the section of the memory we are removing. Caller needs to make @@ -583,7 +584,7 @@ void remove_pfn_range_from_zone(struct zone *zone, * calling offline_pages(). */ void __remove_pages(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap) + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { const unsigned long end_pfn = pfn + nr_pages; unsigned long cur_nr_pages; @@ -598,7 +599,7 @@ void __remove_pages(unsigned long pfn, unsigned long nr_pages, /* Select all remaining pages up to the next section boundary */ cur_nr_pages = min(end_pfn - pfn, SECTION_ALIGN_UP(pfn + 1) - pfn); - sparse_remove_section(pfn, cur_nr_pages, altmap); + sparse_remove_section(pfn, cur_nr_pages, altmap, pgmap); } } @@ -1427,7 +1428,7 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size) remove_memory_block_devices(cur_start, memblock_size); - arch_remove_memory(cur_start, memblock_size, altmap); + arch_remove_memory(cur_start, memblock_size, altmap, NULL); /* Verify that all vmemmap pages have actually been freed. */ WARN(altmap->alloc, "Altmap not fully unmapped"); @@ -1470,7 +1471,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret = create_memory_block_devices(cur_start, memblock_size, nid, params.altmap, group); if (ret) { - arch_remove_memory(cur_start, memblock_size, params.altmap); + arch_remove_memory(cur_start, memblock_size, params.altmap, NULL); kfree(params.altmap); goto out; } @@ -1556,7 +1557,7 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags) /* create memory block devices after memory was added */ ret = create_memory_block_devices(start, size, nid, NULL, group); if (ret) { - arch_remove_memory(start, size, params.altmap); + arch_remove_memory(start, size, params.altmap, NULL); goto error; } } @@ -2268,7 +2269,7 @@ static int try_remove_memory(u64 start, u64 size) * No altmaps present, do the removal directly */ remove_memory_block_devices(start, size); - arch_remove_memory(start, size, NULL); + arch_remove_memory(start, size, NULL, NULL); } else { /* all memblocks in the range have altmaps */ remove_memory_blocks_and_altmaps(start, size); diff --git a/mm/memremap.c b/mm/memremap.c index 053842d45cb1..81766d822400 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -97,10 +97,10 @@ static void pageunmap_range(struct dev_pagemap *pgmap, int range_id) PHYS_PFN(range_len(range))); if (pgmap->type == MEMORY_DEVICE_PRIVATE) { __remove_pages(PHYS_PFN(range->start), - PHYS_PFN(range_len(range)), NULL); + PHYS_PFN(range_len(range)), NULL, pgmap); } else { arch_remove_memory(range->start, range_len(range), - pgmap_altmap(pgmap)); + pgmap_altmap(pgmap), pgmap); kasan_remove_zero_shadow(__va(range->start), range_len(range)); } mem_hotplug_done(); diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 60e55e78d7ff..eafb7c6eb71e 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -660,7 +660,7 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap) + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); @@ -741,7 +741,7 @@ static int fill_subsection_map(unsigned long pfn, unsigned long nr_pages) * usage map, but still need to free the vmemmap range. */ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap) + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { struct mem_section *ms = __pfn_to_section(pfn); bool section_is_early = early_section(ms); @@ -779,7 +779,7 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, * section_activate() and pfn_valid() . */ if (!section_is_early) - depopulate_section_memmap(pfn, nr_pages, altmap); + depopulate_section_memmap(pfn, nr_pages, altmap, pgmap); else if (memmap) free_map_bootmem(memmap); @@ -823,7 +823,7 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, memmap = populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); if (!memmap) { - section_deactivate(pfn, nr_pages, altmap); + section_deactivate(pfn, nr_pages, altmap, pgmap); return ERR_PTR(-ENOMEM); } @@ -884,13 +884,13 @@ int __meminit sparse_add_section(int nid, unsigned long start_pfn, } void sparse_remove_section(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap) + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { struct mem_section *ms = __pfn_to_section(pfn); if (WARN_ON_ONCE(!valid_section(ms))) return; - section_deactivate(pfn, nr_pages, altmap); + section_deactivate(pfn, nr_pages, altmap, pgmap); } #endif /* CONFIG_MEMORY_HOTPLUG */ From 721a73e30c9e3e8fcffe1725bcede1bbd20b4918 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:53 +0800 Subject: [PATCH 2014/5214] mm/sparse-vmemmap: fix DAX vmemmap accounting with optimization When vmemmap optimization is enabled for DAX, the nr_memmap_pages counter in /proc/vmstat is incorrect. The current code always accounts for the full, non-optimized vmemmap size, but vmemmap optimization reduces the actual number of vmemmap pages by reusing tail pages. This causes the system to overcount vmemmap usage, leading to inaccurate page statistics in /proc/vmstat. Fix this by introducing section_nr_vmemmap_pages(), which returns the exact vmemmap page count for a given pfn range based on whether optimization is in effect. Link: https://lore.kernel.org/20260428081855.1249045-5-songmuchun@bytedance.com Fixes: 15995a352474 ("mm: report per-page metadata information") Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/sparse-vmemmap.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index eafb7c6eb71e..112ccf9c71ca 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -647,6 +647,31 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn) } } +static int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages, + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) +{ + const unsigned int order = pgmap ? pgmap->vmemmap_shift : 0; + const unsigned long pages_per_compound = 1UL << order; + + VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SUBSECTION)); + VM_WARN_ON_ONCE(nr_pages > PAGES_PER_SECTION); + + if (!vmemmap_can_optimize(altmap, pgmap)) + return DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE); + + if (order < PFN_SECTION_SHIFT) { + VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, pages_per_compound)); + return VMEMMAP_RESERVE_NR * nr_pages / pages_per_compound; + } + + VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SECTION)); + + if (IS_ALIGNED(pfn, pages_per_compound)) + return VMEMMAP_RESERVE_NR; + + return 0; +} + static struct page * __meminit populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap) @@ -654,7 +679,7 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); + memmap_pages_add(section_nr_vmemmap_pages(pfn, nr_pages, altmap, pgmap)); return page; } @@ -665,7 +690,7 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); - memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); + memmap_pages_add(-section_nr_vmemmap_pages(pfn, nr_pages, altmap, pgmap)); vmemmap_free(start, end, altmap); } @@ -673,9 +698,10 @@ static void free_map_bootmem(struct page *memmap) { unsigned long start = (unsigned long)memmap; unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION); + unsigned long pfn = page_to_pfn(memmap); - memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), - PAGE_SIZE))); + memmap_boot_pages_add(-section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION, + NULL, NULL)); vmemmap_free(start, end, NULL); } From 94405c6136839f7c462249c8b4b957bcb9527a9d Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:54 +0800 Subject: [PATCH 2015/5214] mm/mm_init: fix pageblock migratetype for ZONE_DEVICE compound pages The memmap_init_zone_device() function only initializes the migratetype of the first pageblock of a compound page. If the compound page size exceeds pageblock_nr_pages (e.g., 1GB hugepages with 2MB pageblocks), subsequent pageblocks in the compound page remain uninitialized. Move the migratetype initialization out of __init_zone_device_page() and into a separate pageblock_migratetype_init_range() function. This iterates over the entire PFN range of the memory, ensuring that all pageblocks are correctly initialized. Also remove the stale confusing comment about MEMINIT_HOTPLUG above the migratetype setting since it is an obsolete relic from commit 966cf44f637e ("mm: defer ZONE_DEVICE page initialization to the point where we init pgmap") and no longer makes sense here. Link: https://lore.kernel.org/20260428081855.1249045-6-songmuchun@bytedance.com Fixes: c4386bd8ee3a ("mm/memremap: add ZONE_DEVICE support for compound pages") Signed-off-by: Muchun Song Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/mm_init.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index f9f8e1af921c..cfc76953e249 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -674,6 +674,20 @@ static inline void fixup_hashdist(void) static inline void fixup_hashdist(void) {} #endif /* CONFIG_NUMA */ +#ifdef CONFIG_ZONE_DEVICE +static __meminit void pageblock_migratetype_init_range(unsigned long pfn, + unsigned long nr_pages, int migratetype) +{ + const unsigned long end = pfn + nr_pages; + + for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) { + init_pageblock_migratetype(pfn_to_page(pfn), migratetype, false); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + } +} +#endif + /* * Initialize a reserved page unconditionally, finding its zone first. */ @@ -1011,21 +1025,6 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, page_folio(page)->pgmap = pgmap; page->zone_device_data = NULL; - /* - * Mark the block movable so that blocks are reserved for - * movable at startup. This will force kernel allocations - * to reserve their blocks rather than leaking throughout - * the address space during boot when many long-lived - * kernel allocations are made. - * - * Please note that MEMINIT_HOTPLUG path doesn't clear memmap - * because this is done early in section_activate() - */ - if (pageblock_aligned(pfn)) { - init_pageblock_migratetype(page, MIGRATE_MOVABLE, false); - cond_resched(); - } - /* * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released * directly to the driver page allocator which will set the page count @@ -1122,6 +1121,9 @@ void __ref memmap_init_zone_device(struct zone *zone, __init_zone_device_page(page, pfn, zone_idx, nid, pgmap); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + if (pfns_per_compound == 1) continue; @@ -1129,6 +1131,8 @@ void __ref memmap_init_zone_device(struct zone *zone, compound_nr_pages(altmap, pgmap)); } + pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); + pr_debug("%s initialised %lu pages in %ums\n", __func__, nr_pages, jiffies_to_msecs(jiffies - start)); } From cd681403a87085562499d60325b7b45d3be11217 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:55 +0800 Subject: [PATCH 2016/5214] mm/mm_init: fix uninitialized struct pages for ZONE_DEVICE If DAX memory is hotplugged into an unoccupied subsection of an early section, section_activate() reuses the unoptimized boot memmap. However, compound_nr_pages() still assumes that vmemmap optimization is in effect and initializes only the reduced number of struct pages. As a result, the remaining tail struct pages are left uninitialized, which can later lead to unexpected behavior or crashes. Fix this by treating early sections as unoptimized when calculating how many struct pages to initialize. Link: https://lore.kernel.org/20260428081855.1249045-7-songmuchun@bytedance.com Fixes: 6fd3620b3428 ("mm/page_alloc: reuse tail struct pages for compound devmaps") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Mike Rapoport (Microsoft) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/mm_init.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index cfc76953e249..bd466a3c10c8 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1055,10 +1055,17 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, * of how the sparse_vmemmap internals handle compound pages in the lack * of an altmap. See vmemmap_populate_compound_pages(). */ -static inline unsigned long compound_nr_pages(struct vmem_altmap *altmap, +static inline unsigned long compound_nr_pages(unsigned long pfn, + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - if (!vmemmap_can_optimize(altmap, pgmap)) + /* + * If DAX memory is hot-plugged into an unoccupied subsection + * of an early section, the unoptimized boot memmap is reused. + * See section_activate(). + */ + if (early_section(__pfn_to_section(pfn)) || + !vmemmap_can_optimize(altmap, pgmap)) return pgmap_vmemmap_nr(pgmap); return VMEMMAP_RESERVE_NR * (PAGE_SIZE / sizeof(struct page)); @@ -1128,7 +1135,7 @@ void __ref memmap_init_zone_device(struct zone *zone, continue; memmap_init_compound(page, pfn, zone_idx, nid, pgmap, - compound_nr_pages(altmap, pgmap)); + compound_nr_pages(pfn, altmap, pgmap)); } pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); From 1d258000b0f28ca34faa7ce699e873666724aaa3 Mon Sep 17 00:00:00 2001 From: Liew Rui Yan Date: Sun, 26 Apr 2026 16:16:14 -0700 Subject: [PATCH 2017/5214] mm/damon/ops-common: optimize damon_hot_score() using ilog2() Patch series "mm/damon: repost non-hotfix reviewed patches in damon/next tree", v2. The first patch from Liew Rui Yan add a minor performance optimization using ilog2() instead of inefficient manual implementation of the functionality. The second patch from Cheng-Han Wu fixes a minor typo: s/parametrs/parameters/. The third patch from Liew Rui Yan make commit_inputs operation of DAMON_RECLAIM and DAMON_LRU_SORT synchronous to improve the user experience. The fourth patch from Asier Gutierrez adds a new DAMOS action, DAMOS_COLLAPSE for deterministic DAMOS-based access-aware THP system. This patch (of 4): The current implementation of damon_hot_score() uses a manual for-loop to calculate the value of 'age_in_log'. This can be efficiently replaced by ilog2(), which is semantically more appropriate for calculating the logarithmic value of age. In a simulated-kernel-module performance test with 10,000,000 iterations, this optimization showed a significant reduction in latency (average latency reduced from ~12ns to ~1ns). Test results from the simulated-kernel-module: - ilog2: DAMON Perf Test: Starting 10000000 iterations ============================================= Total Iterations : 10000000 Average Latency : 1 ns P95 Latency : 41 ns P99 Latency : 41 ns --------------------------------------------- Range (ns) | Count | Percent --------------------------------------------- 0-19 | 0 | 0% 20-39 | 2625000 | 26% 40-59 | 7374000 | 73% 60-79 | 0 | 0% 80-99 | 0 | 0% 100+ | 1000 | 0% ============================================= - for-loop: DAMON Perf Test: Starting 10000000 iterations ============================================= Total Iterations : 10000000 Average Latency : 12 ns P95 Latency : 51 ns P99 Latency : 60 ns --------------------------------------------- Range (ns) | Count | Percent --------------------------------------------- 0-19 | 0 | 0% 20-39 | 0 | 0% 40-59 | 9862000 | 98% 60-79 | 135000 | 1% 80-99 | 1000 | 0% 100+ | 2000 | 0% ============================================= Full raw benchmark results can be found at [1]. Link: https://lore.kernel.org/20260426231619.107231-1-sj@kernel.org Link: https://lore.kernel.org/20260426231619.107231-2-sj@kernel.org Link: https://github.com/aethernet65535/damon-hot-score-fls-optimize/tree/master/result-raw [1] Signed-off-by: SeongJae Park Signed-off-by: Liew Rui Yan Reviewed-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Asier Gutierrez Cc: Cheng-Han Wu Signed-off-by: Andrew Morton --- mm/damon/ops-common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 8c6d613425c1..3a0ddc3ac719 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -117,9 +117,12 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, damon_max_nr_accesses(&c->attrs); age_in_sec = (unsigned long)r->age * c->attrs.aggr_interval / 1000000; - for (age_in_log = 0; age_in_log < DAMON_MAX_AGE_IN_LOG && age_in_sec; - age_in_log++, age_in_sec >>= 1) - ; + if (age_in_sec) + age_in_log = min_t(int, ilog2(age_in_sec) + 1, + DAMON_MAX_AGE_IN_LOG); + else + age_in_log = 0; + /* If frequency is 0, higher age means it's colder */ if (freq_subscore == 0) From abdca14655fe4ec821791c031d5764fdd1e9484d Mon Sep 17 00:00:00 2001 From: Cheng-Han Wu Date: Sun, 26 Apr 2026 16:16:15 -0700 Subject: [PATCH 2018/5214] Docs/admin-guide/mm/damon: fix 'parametrs' typo Fix the misspelling of "parametrs" as "parameters" in reclaim.rst and lru_sort.rst. Link: https://lore.kernel.org/20260426231619.107231-3-sj@kernel.org Signed-off-by: Cheng-Han Wu Signed-off-by: SeongJae Park Reviewed-by: SeongJae Park Cc: Asier Gutierrez Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Liew Rui Yan Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/lru_sort.rst | 2 +- Documentation/admin-guide/mm/damon/reclaim.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/lru_sort.rst b/Documentation/admin-guide/mm/damon/lru_sort.rst index 14cc6b2db897..25e2f042a383 100644 --- a/Documentation/admin-guide/mm/damon/lru_sort.rst +++ b/Documentation/admin-guide/mm/damon/lru_sort.rst @@ -75,7 +75,7 @@ Make DAMON_LRU_SORT reads the input parameters again, except ``enabled``. Input parameters that updated while DAMON_LRU_SORT is running are not applied by default. Once this parameter is set as ``Y``, DAMON_LRU_SORT reads values -of parametrs except ``enabled`` again. Once the re-reading is done, this +of parameters except ``enabled`` again. Once the re-reading is done, this parameter is set as ``N``. If invalid parameters are found while the re-reading, DAMON_LRU_SORT will be disabled. diff --git a/Documentation/admin-guide/mm/damon/reclaim.rst b/Documentation/admin-guide/mm/damon/reclaim.rst index d7a0225b4950..01a34c215b66 100644 --- a/Documentation/admin-guide/mm/damon/reclaim.rst +++ b/Documentation/admin-guide/mm/damon/reclaim.rst @@ -67,7 +67,7 @@ Make DAMON_RECLAIM reads the input parameters again, except ``enabled``. Input parameters that updated while DAMON_RECLAIM is running are not applied by default. Once this parameter is set as ``Y``, DAMON_RECLAIM reads values -of parametrs except ``enabled`` again. Once the re-reading is done, this +of parameters except ``enabled`` again. Once the re-reading is done, this parameter is set as ``N``. If invalid parameters are found while the re-reading, DAMON_RECLAIM will be disabled. From de3c60e1c8314f3408a72836483772e17f279aca Mon Sep 17 00:00:00 2001 From: Liew Rui Yan Date: Sun, 26 Apr 2026 16:16:16 -0700 Subject: [PATCH 2019/5214] mm/damon: add synchronous commit for commit_inputs Problem ======= Writing invalid parameters to sysfs followed by 'commit_inputs=Y' fails silently (no error returned to shell), because the validation happens asynchronously in the kdamond. Solution ======== To fix this, the commit_inputs_store() callback now uses damon_call() to synchronously commit parameters in the kdamond thread's safe context. This ensures that validation errors are returned immediately to userspace, following the pattern used by DAMON_SYSFS. Changes ======= 1. Added commit_inputs_store() and commit_inputs_fn() to commit synchronously. 2. Removed handle_commit_inputs(). This change is motivated from another discussion [1]. Link: https://lore.kernel.org/20260426231619.107231-4-sj@kernel.org Link: https://lore.kernel.org/20260318153731.97470-1-aethernet65535@gmail.com [1] Signed-off-by: Liew Rui Yan Reviewed-by: SeongJae Park Signed-off-by: SeongJae Park Cc: Asier Gutierrez Cc: Cheng-Han Wu Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 48 +++++++++++++++++++++++++++++++++++++-------- mm/damon/reclaim.c | 48 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 8494040b1ee4..7569e471160a 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -39,7 +39,6 @@ static bool enabled __read_mostly; * the re-reading, DAMON_LRU_SORT will be disabled. */ static bool commit_inputs __read_mostly; -module_param(commit_inputs, bool, 0600); /* * Desired active to [in]active memory ratio in bp (1/10,000). @@ -340,18 +339,51 @@ static int damon_lru_sort_apply_parameters(void) return err; } -static int damon_lru_sort_handle_commit_inputs(void) +static int damon_lru_sort_commit_inputs_fn(void *arg) { - int err; + return damon_lru_sort_apply_parameters(); +} - if (!commit_inputs) +static int damon_lru_sort_commit_inputs_store(const char *val, + const struct kernel_param *kp) +{ + bool commit_inputs_request; + int err; + struct damon_call_control control = { + .fn = damon_lru_sort_commit_inputs_fn, + }; + + if (!val) { + commit_inputs_request = true; + } else { + err = kstrtobool(val, &commit_inputs_request); + if (err) + return err; + } + + if (!commit_inputs_request) return 0; - err = damon_lru_sort_apply_parameters(); - commit_inputs = false; - return err; + /* + * Skip damon_call() if ctx is not initialized to avoid + * NULL pointer dereference. + */ + if (!ctx) + return -EINVAL; + + err = damon_call(ctx, &control); + + return err ? err : control.return_code; } +static const struct kernel_param_ops commit_inputs_param_ops = { + .flags = KERNEL_PARAM_OPS_FL_NOARG, + .set = damon_lru_sort_commit_inputs_store, + .get = param_get_bool, +}; + +module_param_cb(commit_inputs, &commit_inputs_param_ops, &commit_inputs, 0600); + static int damon_lru_sort_damon_call_fn(void *arg) { struct damon_ctx *c = arg; @@ -365,7 +397,7 @@ static int damon_lru_sort_damon_call_fn(void *arg) damon_lru_sort_cold_stat = s->stat; } - return damon_lru_sort_handle_commit_inputs(); + return 0; } static struct damon_call_control call_control = { diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index fe7fce26cf6c..b330ff169590 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -39,7 +39,6 @@ static bool enabled __read_mostly; * re-reading, DAMON_RECLAIM will be disabled. */ static bool commit_inputs __read_mostly; -module_param(commit_inputs, bool, 0600); /* * Time threshold for cold memory regions identification in microseconds. @@ -246,18 +245,51 @@ static int damon_reclaim_apply_parameters(void) return err; } -static int damon_reclaim_handle_commit_inputs(void) +static int damon_reclaim_commit_inputs_fn(void *arg) { - int err; + return damon_reclaim_apply_parameters(); +} - if (!commit_inputs) +static int damon_reclaim_commit_inputs_store(const char *val, + const struct kernel_param *kp) +{ + bool commit_inputs_request; + int err; + struct damon_call_control control = { + .fn = damon_reclaim_commit_inputs_fn, + }; + + if (!val) { + commit_inputs_request = true; + } else { + err = kstrtobool(val, &commit_inputs_request); + if (err) + return err; + } + + if (!commit_inputs_request) return 0; - err = damon_reclaim_apply_parameters(); - commit_inputs = false; - return err; + /* + * Skip damon_call() if ctx is not initialized to avoid + * NULL pointer dereference. + */ + if (!ctx) + return -EINVAL; + + err = damon_call(ctx, &control); + + return err ? err : control.return_code; } +static const struct kernel_param_ops commit_inputs_param_ops = { + .flags = KERNEL_PARAM_OPS_FL_NOARG, + .set = damon_reclaim_commit_inputs_store, + .get = param_get_bool, +}; + +module_param_cb(commit_inputs, &commit_inputs_param_ops, &commit_inputs, 0600); + static int damon_reclaim_damon_call_fn(void *arg) { struct damon_ctx *c = arg; @@ -267,7 +299,7 @@ static int damon_reclaim_damon_call_fn(void *arg) damon_for_each_scheme(s, c) damon_reclaim_stat = s->stat; - return damon_reclaim_handle_commit_inputs(); + return 0; } static struct damon_call_control call_control = { From 58996503b631adc6a268a42f4624a34513c16199 Mon Sep 17 00:00:00 2001 From: Asier Gutierrez Date: Sun, 26 Apr 2026 16:16:17 -0700 Subject: [PATCH 2020/5214] mm/damon: support MADV_COLLAPSE via DAMOS_COLLAPSE scheme action This patch set introces a new action: DAMOS_COLLAPSE. For DAMOS_HUGEPAGE and DAMOS_NOHUGEPAGE to work, khugepaged should be working, since it relies on hugepage_madvise to add a new slot. This slot should be picked up by khugepaged and eventually collapse (or not, if we are using DAMOS_NOHUGEPAGE) the pages. If THP is not enabled, khugepaged will not be working, and therefore no collapse will happen. DAMOS_COLLAPSE eventually calls madvise_collapse, which will collapse the address range synchronously. In cases where there is a large VMA (databases, for example), DAMOS_COLLAPSE allows us to collapse only the hot region, and not the entire VMA. This new action may be required to support autotuning with hugepage as a goal[1]. ========= Benchmarks: ========= MySQL ===== Tests were performed in an ARM physical server with MariaDB 10.5 and sysbench. Read only benchmark was perform with gaussian row hitting, which follows a normal distribution. T n, D h: THP set to never, DAMON action set to hugepage T m, D h: THP set to madvise, DAMON action set to hugepage T n, D c: THP set to never, DAMON action set to collapse Memory consumption. Lower is better. +------------------+----------+----------+----------+ | | T n, D h | T m, D h | T n, D c | +------------------+----------+----------+----------+ | Total memory use | 2.13 | 2.20 | 2.20 | | Huge pages | 0 | 1.3 | 1.27 | +------------------+----------+----------+----------+ Performance in TPS (Transactions Per Second). Higher is better. T n, D h: 18225.58 T m, D h 18252.93 T n, D c: 18270.21 Performance counter I got the number of L1 D/I TLB accesses and the number a D/I TLB accesses that triggered a page walk. I divided the second by the first to get the percentage of page walkes per TLB access. The lower the better. +---------------+--------------+--------------+--------------+ | | T n, D h | T m, D h | T n, D c | +---------------+--------------+--------------+--------------+ | L1 DTLB | 127248242753 | 125431020479 | 125327001821 | | L1 ITLB | 80332558619 | 79346759071 | 79298139590 | | DTLB walk | 75011087 | 52800418 | 55895794 | | ITLB walk | 71577076 | 71505137 | 67262140 | | DTLB % misses | 0.058948623 | 0.042095183 | 0.044599961 | | ITLB % misses | 0.089100954 | 0.090117275 | 0.084821839 | +---------------+--------------+--------------+--------------+ Masim ===== I used masim with the "demo" configuration, but changing the times to 100 seconds for the initial phase and 50 seconds for the rest of the phases. Memory consumption: +------------------+----------+----------+----------+ | | T n, D h | T m, D h | T n, D c | +------------------+----------+----------+----------+ | Total memory use | 2.38 GB | 2.36 GB | 2.37 GB | | Huge pages | 0 | 190 MB | 188 MB | +------------------+----------+----------+----------+ Performance: THP never, DAMOS_HUGEPAGE initial phase: 40,491 accesses/msec, 100001 msecs run low phase 0: 39,658 accesses/msec, 50002 msecs run high phase 0: 41,678 accesses/msec, 50000 msecs run low phase 1: 39,625 accesses/msec, 50003 msecs run high phase 1: 41,658 accesses/msec, 50002 msecs run low phase 2: 39,642 accesses/msec, 50002 msecs run high phase 2: 41,640 accesses/msec, 50001 msecs run THP madvise, DAMOS_HUGEPAGE initial phase: 51,977 accesses/msec, 100000 msecs run low phase 0: 86,953 accesses/msec, 50000 msecs run high phase 0: 94,812 accesses/msec, 50000 msecs run low phase 1: 101,017 accesses/msec, 50000 msecs run high phase 1: 94,841 accesses/msec, 50000 msecs run low phase 2: 100,993 accesses/msec, 50000 msecs run high phase 2: 94,791 accesses/msec, 50001 msecs run THP never, DAMOS_COLLAPSE initial phase: 93,678 accesses/msec, 100001 msecs run low phase 0: 101,475 accesses/msec, 50000 msecs run high phase 0: 98,589 accesses/msec, 50000 msecs run low phase 1: 101,531 accesses/msec, 50001 msecs run high phase 1: 98,506 accesses/msec, 50001 msecs run low phase 2: 101,458 accesses/msec, 50001 msecs run high phase 2: 98,555 accesses/msec, 50000 msecs run Memory consumption dynamic (how quickly collapses occur): It shows in seconds how many huge pages are allocated. +----+----------+----------+ | | T m, D h | T n, D c | +----+----------+----------+ | 5 | 32 | 188 | | 10 | 48 | 188 | | 15 | 64 | 188 | | 20 | 96 | 188 | | 30 | 112 | 188 | | 35 | 144 | 188 | | 40 | 160 | 188 | | 45 | 190 | 188 | | 50 | 190 | 188 | | 55 | 190 | 188 | | 60 | 190 | 188 | +----+----------+----------+ ========= - We can see that DAMOS "hugepage" action works only when THP is set to madvise. "collapse" action works even when THP is set to never. - Performance for "collapse" action is slightly lower than "hugepage" action and THP madvise. This is due to the fact that collapases occur synchronously. With "hugepage" they may occur during page faults. - Memory consumption is slighly lower for "collapse" than "hugepage" with THP madvise. This is due to the khugepage collapses all VMAs, while "collapse" action only collapses the VMAs in the hot region. - There is an improvement in TLB utilization when collapse through "hugepage" or "collapse" actions are triggered. The amount of TLB misses is lower. - "collapse" action is performance synchronously, which means that page collapses happen earlier and more rapidly. This can be useful or not, depending on the scenario. - "hugepage" action may trigger a VMA split in some scenarios, since it needs to change the flag of the VMA to THP enabled. This may lead to additional overhead. Collapse action just adds a new option to chose the correct system balance. Link: https://lore.kernel.org/20260426231619.107231-5-sj@kernel.org Link: https://lore.kernel.org/damon/20260313000816.79933-1-sj@kernel.org/ [1] Signed-off-by: Asier Gutierrez Signed-off-by: SeongJae Park Reviewed-by: SeongJae Park Cc: Cheng-Han Wu Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Liew Rui Yan Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 4 ++++ include/linux/damon.h | 2 ++ mm/damon/sysfs-schemes.c | 4 ++++ mm/damon/vaddr.c | 3 +++ tools/testing/selftests/damon/sysfs.py | 11 ++++++----- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index bacb457f553a..da74ab20e289 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -474,6 +474,10 @@ that supports each action are as below. Supported by ``vaddr`` and ``fvaddr`` operations set. When TRANSPARENT_HUGEPAGE is disabled, the application of the action will just fail. + - ``collapse``: Call ``madvise()`` for the region with ``MADV_COLLAPSE``. + Supported by ``vaddr`` and ``fvaddr`` operations set. When + TRANSPARENT_HUGEPAGE is disabled, the application of the action will just + fail. - ``lru_prio``: Prioritize the region on its LRU lists. Supported by ``paddr`` operations set. - ``lru_deprio``: Deprioritize the region on its LRU lists. diff --git a/include/linux/damon.h b/include/linux/damon.h index 2bb43910e22e..d3a231275c23 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -121,6 +121,7 @@ struct damon_target { * @DAMOS_PAGEOUT: Reclaim the region. * @DAMOS_HUGEPAGE: Call ``madvise()`` for the region with MADV_HUGEPAGE. * @DAMOS_NOHUGEPAGE: Call ``madvise()`` for the region with MADV_NOHUGEPAGE. + * @DAMOS_COLLAPSE: Call ``madvise()`` for the region with MADV_COLLAPSE. * @DAMOS_LRU_PRIO: Prioritize the region on its LRU lists. * @DAMOS_LRU_DEPRIO: Deprioritize the region on its LRU lists. * @DAMOS_MIGRATE_HOT: Migrate the regions prioritizing warmer regions. @@ -140,6 +141,7 @@ enum damos_action { DAMOS_PAGEOUT, DAMOS_HUGEPAGE, DAMOS_NOHUGEPAGE, + DAMOS_COLLAPSE, DAMOS_LRU_PRIO, DAMOS_LRU_DEPRIO, DAMOS_MIGRATE_HOT, diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index be2b5eda84e0..ab2153fff9a8 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2116,6 +2116,10 @@ static struct damos_sysfs_action_name damos_sysfs_action_names[] = { .action = DAMOS_NOHUGEPAGE, .name = "nohugepage", }, + { + .action = DAMOS_COLLAPSE, + .name = "collapse", + }, { .action = DAMOS_LRU_PRIO, .name = "lru_prio", diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index b069dbc7e3d2..dd5f2d7027ac 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -903,6 +903,9 @@ static unsigned long damon_va_apply_scheme(struct damon_ctx *ctx, case DAMOS_NOHUGEPAGE: madv_action = MADV_NOHUGEPAGE; break; + case DAMOS_COLLAPSE: + madv_action = MADV_COLLAPSE; + break; case DAMOS_MIGRATE_HOT: case DAMOS_MIGRATE_COLD: return damos_va_migrate(t, r, scheme, sz_filter_passed); diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 9067945f16ca..7e93584ff02b 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -127,11 +127,12 @@ def assert_scheme_committed(scheme, dump): 'pageout': 2, 'hugepage': 3, 'nohugeapge': 4, - 'lru_prio': 5, - 'lru_deprio': 6, - 'migrate_hot': 7, - 'migrate_cold': 8, - 'stat': 9, + 'collapse': 5, + 'lru_prio': 6, + 'lru_deprio': 7, + 'migrate_hot': 8, + 'migrate_cold': 9, + 'stat': 10, } assert_true(dump['action'] == action_val[scheme.action], 'action', dump) assert_true(dump['apply_interval_us'] == scheme. apply_interval_us, From 8803f883310a886e701fa282eaae3a6658b10091 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 27 Apr 2026 13:43:14 +0200 Subject: [PATCH 2021/5214] sh: use folio_mapped() instead of page_mapped() in sh4_flush_cache_page() Patch series "mm: remove page_mapped()". While preparing my slides for an LSF/MM talk, I realized that I did not yet remove page_mapped(). So let's do that. In the BPF arena code it's unclear which memdesc we would want to allocate in the future: certainly something with a refcount, but likely none with a mapcount. So let's just rely on the page refcount instead to decide whether we want to try zapping the page from user page tables. This patch (of 3): We already have the folio in our hands, so let's just use folio_mapped(). Link: https://lore.kernel.org/20260427-page_mapped-v1-0-e89c3592c74c@kernel.org Link: https://lore.kernel.org/20260427-page_mapped-v1-1-e89c3592c74c@kernel.org Signed-off-by: David Hildenbrand (Arm) Reviewed-by: Matthew Wilcox (Oracle) Cc: Alexei Starovoitov Cc: Andrii Nakryiko Cc: Eduard Zingerman Cc: Harry Yoo Cc: Jann Horn Cc: Jiri Olsa Cc: John Paul Adrian Glaubitz Cc: Kumar Kartikeya Dwivedi Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Martin KaFai Lau Cc: Michal Hocko Cc: Mike Rapoport Cc: Rich Felker Cc: Rik van Riel Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Yonghong Song Cc: Yoshinori Sato Signed-off-by: Andrew Morton --- arch/sh/mm/cache-sh4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 83fb34b39ca7..8bc9ce541c14 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c @@ -248,7 +248,7 @@ static void sh4_flush_cache_page(void *args) */ map_coherent = (current_cpu_data.dcache.n_aliases && test_bit(PG_dcache_clean, folio_flags(folio, 0)) && - page_mapped(page)); + folio_mapped(folio)); if (map_coherent) vaddr = kmap_coherent(page, address); else From 88692f0c33a788072abfa1888b28bc6d7d7d1165 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 27 Apr 2026 13:43:15 +0200 Subject: [PATCH 2022/5214] bpf: arena: use page_ref_count() instead of page_mapped() in arena_free_pages() Pages that BPF arena code maps are allocated through bpf_map_alloc_pages(), which does not allocate folios but pages. In the future, pages will not have a mapcount, only folios will. Converting the code to use folios and rely on folio_mapped() sounds like the wrong approach. Should BPF arena code allocate folios and use folio_mapped() here? But likely we would not want to use folios here longterm, as we don't really need folio information. Hard to tell. But in the meantime, we can simply use the page refcount instead, as a heuristic whether the page might be mapped to user space and we would want to try zapping it, so we can get rid of page_mapped(). Page allocation will give us a page with a refcount of 1. Any user space mapping adds a page reference. While there can be references from other subsystems (e.g., GUP), in the common case for this test here relying on the page count is good enough. Link: https://lore.kernel.org/20260427-page_mapped-v1-2-e89c3592c74c@kernel.org Signed-off-by: David Hildenbrand (Arm) Reviewed-by: Matthew Wilcox (Oracle) Cc: Alexei Starovoitov Cc: Andrii Nakryiko Cc: Eduard Zingerman Cc: Harry Yoo Cc: Jann Horn Cc: Jiri Olsa Cc: John Paul Adrian Glaubitz Cc: Kumar Kartikeya Dwivedi Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Martin KaFai Lau Cc: Michal Hocko Cc: Mike Rapoport Cc: Rich Felker Cc: Rik van Riel Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Yonghong Song Cc: Yoshinori Sato Signed-off-by: Andrew Morton --- kernel/bpf/arena.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 49a8f7b1beef..a497c5913bd4 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -729,7 +729,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) { page = llist_entry(pos, struct page, pcp_llist); - if (page_cnt == 1 && page_mapped(page)) /* mapped by some user process */ + if (page_cnt == 1 && page_ref_count(page) > 1) /* maybe mapped by user space */ /* Optimization for the common case of page_cnt==1: * If page wasn't mapped into some user vma there * is no need to call zap_pages which is slow. When From 90f01f5d6ba57d93363289b3247314b7fd5e8d49 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 27 Apr 2026 13:43:16 +0200 Subject: [PATCH 2023/5214] mm: remove page_mapped() Let's replace the last user of page_mapped() by folio_mapped() so we can get rid of page_mapped(). Replace the remaining occurrences of page_mapped() in rmap documentation by folio_mapped(). Link: https://lore.kernel.org/20260427-page_mapped-v1-3-e89c3592c74c@kernel.org Signed-off-by: David Hildenbrand (Arm) Reviewed-by: Matthew Wilcox (Oracle) Cc: Alexei Starovoitov Cc: Andrii Nakryiko Cc: Eduard Zingerman Cc: Harry Yoo Cc: Jann Horn Cc: Jiri Olsa Cc: John Paul Adrian Glaubitz Cc: Kumar Kartikeya Dwivedi Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Martin KaFai Lau Cc: Michal Hocko Cc: Mike Rapoport Cc: Rich Felker Cc: Rik van Riel Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Yonghong Song Cc: Yoshinori Sato Signed-off-by: Andrew Morton --- include/linux/mm.h | 10 ---------- mm/memory.c | 2 +- mm/rmap.c | 8 ++++---- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 8a0078a4dc78..9cedc5e75aa9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1888,16 +1888,6 @@ static inline bool folio_mapped(const struct folio *folio) return folio_mapcount(folio) >= 1; } -/* - * Return true if this page is mapped into pagetables. - * For compound page it returns true if any sub-page of compound page is mapped, - * even if this particular sub-page is not itself mapped by any PTE or PMD. - */ -static inline bool page_mapped(const struct page *page) -{ - return folio_mapped(page_folio(page)); -} - static inline struct page *virt_to_head_page(const void *x) { struct page *page = virt_to_page(x); diff --git a/mm/memory.c b/mm/memory.c index 02ec74a1273f..0c9d9c2cbf0e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5482,7 +5482,7 @@ static vm_fault_t __do_fault(struct vm_fault *vmf) if (unlikely(PageHWPoison(vmf->page))) { vm_fault_t poisonret = VM_FAULT_HWPOISON; if (ret & VM_FAULT_LOCKED) { - if (page_mapped(vmf->page)) + if (folio_mapped(folio)) unmap_mapping_folio(folio); /* Retry if a clean folio was removed from the cache. */ if (mapping_evict_folio(folio->mapping, folio)) diff --git a/mm/rmap.c b/mm/rmap.c index 99e1b3dc390b..1c77d5dc06e9 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -571,7 +571,7 @@ void __init anon_vma_init(void) * In case it was remapped to a different anon_vma, the new anon_vma will be a * child of the old anon_vma, and the anon_vma lifetime rules will therefore * ensure that any anon_vma obtained from the page will still be valid for as - * long as we observe page_mapped() [ hence all those page_mapped() tests ]. + * long as we observe folio_mapped() [ hence all those folio_mapped() tests ]. * * All users of this function must be very careful when walking the anon_vma * chain and verify that the page in question is indeed mapped in it @@ -1999,7 +1999,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, /* * When racing against e.g. zap_pte_range() on another cpu, * in between its ptep_get_and_clear_full() and folio_remove_rmap_*(), - * try_to_unmap() may return before page_mapped() has become false, + * try_to_unmap() may return before folio_mapped() has become false, * if page table locking is skipped: use TTU_SYNC to wait for that. */ if (flags & TTU_SYNC) @@ -2428,7 +2428,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, /* * When racing against e.g. zap_pte_range() on another cpu, * in between its ptep_get_and_clear_full() and folio_remove_rmap_*(), - * try_to_migrate() may return before page_mapped() has become false, + * try_to_migrate() may return before folio_mapped() has become false, * if page table locking is skipped: use TTU_SYNC to wait for that. */ if (flags & TTU_SYNC) @@ -2929,7 +2929,7 @@ static struct anon_vma *rmap_walk_anon_lock(const struct folio *folio, /* * Note: remove_migration_ptes() cannot use folio_lock_anon_vma_read() - * because that depends on page_mapped(); but not all its usages + * because that depends on folio_mapped(); but not all its usages * are holding mmap_lock. Users without mmap_lock are required to * take a reference count to prevent the anon_vma disappearing */ From 0b20c36c118d2122f57982c644e526c0fcd4a947 Mon Sep 17 00:00:00 2001 From: fujunjie Date: Mon, 4 May 2026 10:39:57 +0000 Subject: [PATCH 2024/5214] mm/madvise: reject invalid process_madvise() advice for zero-length vectors process_madvise() used to validate the advice while walking each imported iovec. If the vector has zero total length, vector_madvise() does not enter the loop and can return success without checking whether the advice value is valid. For a local mm, such as process_madvise(PIDFD_SELF, ...), the remote-only process_madvise_remote_valid() check is skipped. As a result, an invalid advice can be reported as success when the vector has zero total length. This differs from madvise(), which rejects an invalid advice before returning success for a zero-length range. Validate the generic madvise behavior at the syscall-facing entry points before any vector walk. In process_madvise(), do this before the remote-only advice restriction so unsupported advice is rejected with the same priority for local and remote mm. Use an errno-returning helper for address/length validation, and handle zero-length ranges explicitly at the call sites. Requests with valid advice and zero total length remain a noop and continue to return 0. Add a selftest that covers invalid advice with a zero-length iovec and an empty vector, while also checking that a request with valid advice and zero length still succeeds. Link: https://lore.kernel.org/tencent_C3AEB0E769C5F4F9370F9411B69B7F8B2907@qq.com Fixes: 021781b01275 ("mm/madvise: unrestrict process_madvise() for current process") Signed-off-by: fujunjie Acked-by: David Hildenbrand (Arm) Reviewed-by: SeongJae Park Cc: Christian Brauner Cc: Jann Horn Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Shuah Khan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/madvise.c | 60 ++++++++++------------- tools/testing/selftests/mm/process_madv.c | 28 +++++++++++ 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/mm/madvise.c b/mm/madvise.c index 69708e953cf5..cd9bb077072c 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -1834,50 +1834,29 @@ static void madvise_finish_tlb(struct madvise_behavior *madv_behavior) tlb_finish_mmu(madv_behavior->tlb); } -static bool is_valid_madvise(unsigned long start, size_t len_in, int behavior) +/** + * check_input_range() - Check if the requested range is valid. + * @start: Start address of madvise-requested address range. + * @len_in: Length of madvise-requested address range. + * + * Returns: 0 if the input range is valid, otherwise an error code. + */ +static int check_input_range(unsigned long start, size_t len_in) { size_t len; - if (!madvise_behavior_valid(behavior)) - return false; - if (!PAGE_ALIGNED(start)) - return false; + return -EINVAL; len = PAGE_ALIGN(len_in); /* Check to see whether len was rounded up from small -ve to zero */ if (len_in && !len) - return false; + return -EINVAL; if (start + len < start) - return false; + return -EINVAL; - return true; -} - -/* - * madvise_should_skip() - Return if the request is invalid or nothing. - * @start: Start address of madvise-requested address range. - * @len_in: Length of madvise-requested address range. - * @behavior: Requested madvise behavior. - * @err: Pointer to store an error code from the check. - * - * If the specified behaviour is invalid or nothing would occur, we skip the - * operation. This function returns true in the cases, otherwise false. In - * the former case we store an error on @err. - */ -static bool madvise_should_skip(unsigned long start, size_t len_in, - int behavior, int *err) -{ - if (!is_valid_madvise(start, len_in, behavior)) { - *err = -EINVAL; - return true; - } - if (start + PAGE_ALIGN(len_in) == start) { - *err = 0; - return true; - } - return false; + return 0; } static bool is_madvise_populate(struct madvise_behavior *madv_behavior) @@ -2013,8 +1992,13 @@ int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int beh .tlb = &tlb, }; - if (madvise_should_skip(start, len_in, behavior, &error)) + if (!madvise_behavior_valid(behavior)) + return -EINVAL; + + error = check_input_range(start, len_in); + if (error || !len_in) return error; + error = madvise_lock(&madv_behavior); if (error) return error; @@ -2056,7 +2040,8 @@ static ssize_t vector_madvise(struct mm_struct *mm, struct iov_iter *iter, size_t len_in = iter_iov_len(iter); int error; - if (madvise_should_skip(start, len_in, behavior, &error)) + error = check_input_range(start, len_in); + if (error || !len_in) ret = error; else ret = madvise_do_behavior(start, len_in, &madv_behavior); @@ -2131,6 +2116,11 @@ SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec, goto release_task; } + if (!madvise_behavior_valid(behavior)) { + ret = -EINVAL; + goto release_mm; + } + /* * We need only perform this check if we are attempting to manipulate a * remote process's address space. diff --git a/tools/testing/selftests/mm/process_madv.c b/tools/testing/selftests/mm/process_madv.c index cd4610baf5d7..3fffd5f7e6fb 100644 --- a/tools/testing/selftests/mm/process_madv.c +++ b/tools/testing/selftests/mm/process_madv.c @@ -309,6 +309,34 @@ TEST_F(process_madvise, invalid_vlen) ASSERT_EQ(munmap(map, pagesize), 0); } +/* + * Test that invalid advice is rejected even when the iovec has zero total + * length. A request with valid advice and zero length is a noop, but + * invalid advice should still fail with EINVAL. + */ +TEST_F(process_madvise, invalid_advice_zero_length) +{ + struct iovec vec = { + .iov_base = NULL, + .iov_len = 0, + }; + int pidfd = self->pidfd; + ssize_t ret; + + errno = 0; + ret = sys_process_madvise(pidfd, &vec, 1, -1, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); + + errno = 0; + ret = sys_process_madvise(pidfd, &vec, 1, MADV_DONTNEED, 0); + ASSERT_EQ(ret, 0); + + ret = sys_process_madvise(pidfd, NULL, 0, -1, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); +} + /* * Test process_madvise() with an invalid flag value. Currently, only a flag * value of 0 is supported. This test is reserved for the future, e.g., if From 7b32f64bc512b40b268776c5ac4d354b325b3197 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Sun, 26 Apr 2026 20:01:47 -0700 Subject: [PATCH 2025/5214] mm: limit filemap_fault readahead to VMA boundaries When a file mapping covers a strict subset of a file, an access to the mapping can trigger readahead of file pages outside the mapped region. Readahead is meant to prefetch pages likely to be accessed soon, but these pages aren't accessible via the same means, so it fair to say we don't have a good indicator they'll be accessed soon. Take an ELF file for example: an access to the end of a program's read-only segment isn't a sign that nearby file contents will be accessed next (they are likely to be mapped discontiguously, or not at all). The pressure from loading these pages into the cache can evict more useful pages. To improve the behavior, make three changes: * Introduce a new readahead_control field, max_index, as a hard limit on the readahead. The existing file_ra_state->size can't be used as a limit, it is more of a hint and can be increased by various heuristics. * Set readahead_control->max_index to the end of the VMA in all of the readahead paths that can be triggered from a fault on a file mapping (both "sync" and "async" readahead). * Limit the read-around range start to the VMA's start. Note that these changes only affect readahead triggered in the context of a fault, they do not affect readahead triggered by read syscalls. If a user mixes the two types of accesses, the behavior is expected to be the following: if a fault causes readahead and places a PG_readahead marker and then a read(2) syscall hits the PG_readahead marker, the resulting async readahead *will not* be limited to the VMA end. Conversely, if a read(2) syscall places a PG_readahead marker and then a fault hits the marker, the async readahead *will* be limited to the VMA end. There is an edge case that the above motivation glosses over: A single file mapping might be backed by multiple VMAs. For example, a whole file could be mapped RW, then part of the mapping made RO using mprotect. This patch would hurt performance of a sequential faulted read of such a mapping, the degree depending on how fragmented the VMAs are. A usage pattern like that is likely rare and already suffering from sub-optimal performance because, e.g., the fragmented VMAs limit the fault-around, so each VMA boundary in a sequential faulted read would cause a minor fault. Still, this patch would make it worse. See a previous discussion of this topic at [1]. Tested by mapping and reading a small subset of a large file, then using the cachestat syscall to verify the number of cached pages didn't exceed the mapping size. In practical scenarios, the effect depends on the specific file and usage. Sometimes there is no effect at all, but, for some ELF files in Android, we see ~20% fewer pages pulled into the cache. A comprehensive performance evaluation hasn't been done, but, in addition to the anecdontal memory savings mentioned above, a benchmark was run with fio 3.38, showing neutral looking results: /data/local/tmp/fio --version fio --name=mmap_test --ioengine=mmap --rw=read --bs=4k \ --offset=1G --size=1G --filesize=3G --numjobs=1 \ --filename=testfile.bin Before: 4366.6 MiB/s (avg of 3459, 4592, 4613, 4697, 4472) After: 4444.0 MiB/s (avg of 4633, 4655, 4511, 4571, 3850) +1.7% Same, with --ioengine=mmap --rw=randread Before: 445.6 MiB/s (avg of 446, 447, 442, 452, 441) After: 447.0 MiB/s (avg of 447, 446, 446, 451, 445) +0.3% Same, with --ioengine=psync --rw=read Before: 3086.6 MiB/s (avg of 3122, 3094, 3066, 3094, 3057) After: 3084.6 MiB/s (avg of 3039, 3103, 3103, 3084, 3094) -0.06% Same, with --ioengine=psync --rw=randread Before: 2226.4 MiB/s (avg of 2256, 2183, 2207, 2265, 2221) After: 2231.4 MiB/s (avg of 2236, 2241, 2236, 2193, 2251) +0.2% Link: https://lore.kernel.org/20260427030148.653228-1-fmayle@google.com Link: https://lore.kernel.org/all/ivnv2crd3et76p2nx7oszuqhzzah756oecn5yuykzqfkqzoygw@yvnlkhjjssoz/ [1] Signed-off-by: Frederick Mayle Reviewed-by: Jan Kara Reviewed-by: Kalesh Singh Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Matthew Wilcox Cc: Suren Baghdasaryan Signed-off-by: Andrew Morton --- include/linux/pagemap.h | 2 ++ mm/filemap.c | 4 ++++ mm/readahead.c | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 31a848485ad9..1f50991b43e3 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -1350,6 +1350,7 @@ struct readahead_control { struct file_ra_state *ra; /* private: use the readahead_* accessors instead */ pgoff_t _index; + pgoff_t _max_index; /* limit readahead to _max_index, inclusive */ unsigned int _nr_pages; unsigned int _batch_count; bool dropbehind; @@ -1363,6 +1364,7 @@ struct readahead_control { .mapping = m, \ .ra = r, \ ._index = i, \ + ._max_index = ULONG_MAX, \ } #define VM_READAHEAD_PAGES (SZ_128K / PAGE_SIZE) diff --git a/mm/filemap.c b/mm/filemap.c index 4e636647100c..97772a05a18e 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3314,6 +3314,8 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) bool force_thp_readahead = false; unsigned short mmap_miss; + ractl._max_index = vmf->vma->vm_pgoff + vma_pages(vmf->vma) - 1; + /* Use the readahead code, even if readahead is disabled */ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE) && HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER) @@ -3396,6 +3398,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) * mmap read-around */ ra->start = max_t(long, 0, vmf->pgoff - ra->ra_pages / 2); + ra->start = max(ra->start, vmf->vma->vm_pgoff); ra->size = ra->ra_pages; ra->async_size = ra->ra_pages / 4; ra->order = 0; @@ -3438,6 +3441,7 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf, } if (folio_test_readahead(folio)) { + ractl._max_index = vmf->vma->vm_pgoff + vma_pages(vmf->vma) - 1; fpin = maybe_unlock_mmap_for_io(vmf, fpin); page_cache_async_ra(&ractl, folio, ra->ra_pages); } diff --git a/mm/readahead.c b/mm/readahead.c index 7b05082c89ea..8c12b63ccd4a 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -324,6 +324,8 @@ static void do_page_cache_ra(struct readahead_control *ractl, return; end_index = (isize - 1) >> PAGE_SHIFT; + if (end_index > ractl->_max_index) + end_index = ractl->_max_index; if (index > end_index) return; /* Don't read past the page containing the last byte of the file */ @@ -471,7 +473,7 @@ void page_cache_ra_order(struct readahead_control *ractl, pgoff_t start = readahead_index(ractl); pgoff_t index = start; unsigned int min_order = mapping_min_folio_order(mapping); - pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT; + pgoff_t limit; pgoff_t mark = index + ra->size - ra->async_size; unsigned int nofs; int err = 0; @@ -484,6 +486,8 @@ void page_cache_ra_order(struct readahead_control *ractl, goto fallback; } + limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT; + limit = min(limit, ractl->_max_index); limit = min(limit, index + ra->size - 1); new_order = min(mapping_max_folio_order(mapping), new_order); From 3b9e3cc0405b422db884054ea2417b7b85220c56 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:20 -0700 Subject: [PATCH 2026/5214] mm/damon/core: introduce damon_ctx->paused Patch series "mm/damon: let DAMON be paused and resumed", v2. DAMON utilizes a few mechanisms that enhance itself over time. Adaptive regions adjustment, goal-based DAMOS quota auto-tuning and monitoring intervals auto-tuning like self-training mechanisms are such examples. It also adds access frequency stability information (age) to the monitoring results, which makes it enhanced over time. Sometimes users have to stop DAMON. In this case, DAMON internal state that enhanced over the time of the last execution simply goes away. Restarted DAMON have to train itself and enhance its output from the scratch. This makes DAMON less useful in such cases. Introducing three such use cases below. Investigation of DAMON. It is best to do the investigation online, especially when it is a production environment. DAMON therefore provides features for such online investigations, including DAMOS stats, monitoring result snapshot exposure, and multiple tracepoints. When those are insufficient, and there are additional clues that could be interfered by DAMON, users have to temporarily stop DAMON to collect the additional clues. It is not very useful since many of DAMON internal clues are gone when DAMON is stopped. The loss of the monitoring results that improved over time is also problematic, especially in production environments. Monitoring of workloads that have different user-known phases. For example, in Android, applications are known to have very different access patterns and behaviors when they are running on the foreground and the background. It can therefore be useful to separate monitoring of apps based on whether they are running on the foreground and on the background. Having two DAMON threads per application that paused and resumed for the apps foreground/background switches can be useful for the purpose. But such pause/resume of the execution is not supported. Tests of DAMON. A few DAMON selftests are using drgn to dump the internal DAMON status. The tests show if the dumped status is the same as what the test code expected. Because DAMON keeps running and modifying its internal status, there are chances of data races that can cause false test results. Stopping DAMON can avoid the race. But, since the internal state of DAMON is dropped, the test coverage will be limited. Let DAMON execution be paused and resumed without loss of the internal state, to overhaul the limitations. For this, introduce a new DAMON context parameter, namely 'pause'. API callers can update it while the context is running, using the online parameters update functions (damon_commit_ctx() and damon_call()). Once it is set, kdamond_fn() main loop will do only limited works excluding the monitoring and DAMOS works, while sleeping sampling intervals per the work. The limited works include handling of the online parameters update. Hence users can unset the 'pause' parameter again. Once it is unset, kdamond_fn() main loop will do all the work again (resumed). Under the paused state, it also does stop condition checks and handling of it, so that paused DAMON can also be stopped if needed. Expose the feature to the user space via DAMON sysfs interface. Also, update existing drgn-based tests to test and use the feature. Tests ===== I confirmed the feature functionality using real time tracing ('perf trace' or 'trace-cmd stream') of damon:damon_aggregated DAMON tracepoint. By pausing and resuming the DAMON execution, I was able to see the trace stops and continued as expected. Note that the pause feature support is added to DAMON user-space tool (damo) after v3.1.9. Users can use '--pause_ctx' command line option of damo for that, and I actually used it for my test. The extended drgn-based selftests are also testing a part of the functionality. Patches Sequence ================ Patch 1 introduces the new core API for the pause feature. Patch 2 extend DAMON sysfs interface for the new parameter. Patches 3-5 update design, usage and ABI documents for the new sysfs file, respectively. The following five patches are for tests. Patch 6 implements a new kunit test for the pause parameter online commitment. Patches 7 and 8 extend DAMON selftest helpers to support the new feature. Patch 9 extends selftest to test the commitment of the feature. Finally, patch 10 updates existing selftest to be safe from the race condition using the pause/resume feature. This patch (of 10): DAMON supports only start and stop of the execution. When it is stopped, its internal data that it self-trained goes away. It will be useful if the execution can be paused and resumed with the previous self-trained data. Introduce per-context API parameter, 'paused', for the purpose. The parameter can be set and unset while DAMON is running and paused, using the online parameters commit helper functions (damon_commit_ctx() and damon_call()). Once 'paused' is set, the kdamond_fn() main loop does only limited works with sampling interval sleep during the works. The limited works include the handling of the online parameters update, so that users can unset the 'pause' and resume the execution when they want. It also keep checking DAMON stop conditions and handling of it, so that DAMON can be stopped while paused if needed. Link: https://lore.kernel.org/20260427151231.113429-1-sj@kernel.org Link: https://lore.kernel.org/20260427151231.113429-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 2 ++ mm/damon/core.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index d3a231275c23..f2370a3a4a9a 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -801,6 +801,7 @@ struct damon_attrs { * @ops: Set of monitoring operations for given use cases. * @addr_unit: Scale factor for core to ops address conversion. * @min_region_sz: Minimum region size. + * @pause: Pause kdamond main loop. * @adaptive_targets: Head of monitoring targets (&damon_target) list. * @schemes: Head of schemes (&damos) list. */ @@ -854,6 +855,7 @@ struct damon_ctx { struct damon_operations ops; unsigned long addr_unit; unsigned long min_region_sz; + bool pause; struct list_head adaptive_targets; struct list_head schemes; diff --git a/mm/damon/core.c b/mm/damon/core.c index 7aeaf319a18a..05e4bef367db 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1370,6 +1370,7 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) if (err) return err; } + dst->pause = src->pause; dst->ops = src->ops; dst->addr_unit = src->addr_unit; dst->min_region_sz = src->min_region_sz; @@ -3237,6 +3238,14 @@ static int kdamond_fn(void *data) kdamond_call(ctx, false); if (ctx->maybe_corrupted) break; + while (ctx->pause) { + damos_walk_cancel(ctx); + kdamond_usleep(ctx->attrs.sample_interval); + /* allow caller unset pause via damon_call() */ + kdamond_call(ctx, false); + if (kdamond_need_stop(ctx) || ctx->maybe_corrupted) + goto done; + } if (!list_empty(&ctx->schemes)) kdamond_apply_schemes(ctx); else From 3375284944ead898236652bd68a8dac66b65792d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:21 -0700 Subject: [PATCH 2027/5214] mm/damon/sysfs: add pause file under context dir Add pause DAMON sysfs file under the context directory. It exposes the damon_ctx->pause API parameter to the users so that they can use the pause/resume feature. Link: https://lore.kernel.org/20260427151231.113429-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index eefa959aa30a..d5863cc33d23 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -866,6 +866,7 @@ struct damon_sysfs_context { struct damon_sysfs_attrs *attrs; struct damon_sysfs_targets *targets; struct damon_sysfs_schemes *schemes; + bool pause; }; static struct damon_sysfs_context *damon_sysfs_context_alloc( @@ -878,6 +879,7 @@ static struct damon_sysfs_context *damon_sysfs_context_alloc( context->kobj = (struct kobject){}; context->ops_id = ops_id; context->addr_unit = 1; + context->pause = false; return context; } @@ -1053,6 +1055,30 @@ static ssize_t addr_unit_store(struct kobject *kobj, return count; } +static ssize_t pause_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct damon_sysfs_context *context = container_of(kobj, + struct damon_sysfs_context, kobj); + + return sysfs_emit(buf, "%c\n", context->pause ? 'Y' : 'N'); +} + +static ssize_t pause_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct damon_sysfs_context *context = container_of(kobj, + struct damon_sysfs_context, kobj); + bool pause; + int err = kstrtobool(buf, &pause); + + if (err) + return err; + context->pause = pause; + return count; +} + + static void damon_sysfs_context_release(struct kobject *kobj) { kfree(container_of(kobj, struct damon_sysfs_context, kobj)); @@ -1067,10 +1093,14 @@ static struct kobj_attribute damon_sysfs_context_operations_attr = static struct kobj_attribute damon_sysfs_context_addr_unit_attr = __ATTR_RW_MODE(addr_unit, 0600); +static struct kobj_attribute damon_sysfs_context_pause_attr = + __ATTR_RW_MODE(pause, 0600); + static struct attribute *damon_sysfs_context_attrs[] = { &damon_sysfs_context_avail_operations_attr.attr, &damon_sysfs_context_operations_attr.attr, &damon_sysfs_context_addr_unit_attr.attr, + &damon_sysfs_context_pause_attr.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_context); @@ -1470,6 +1500,7 @@ static int damon_sysfs_apply_inputs(struct damon_ctx *ctx, if (sys_ctx->ops_id == DAMON_OPS_PADDR) ctx->min_region_sz = max( DAMON_MIN_REGION_SZ / sys_ctx->addr_unit, 1); + ctx->pause = sys_ctx->pause; err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs); if (err) return err; From 60bee40e30d047356a118bd637ba4960baadcd46 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:22 -0700 Subject: [PATCH 2028/5214] Docs/mm/damon/design: update for context pause/resume feature Update DAMON design document for the context execution pause/resume feature. Link: https://lore.kernel.org/20260427151231.113429-4-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index da74ab20e289..fa7392b5a331 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -19,6 +19,13 @@ types of monitoring. To know how user-space can do the configurations and start/stop DAMON, refer to :ref:`DAMON sysfs interface ` documentation. +Users can also request each context execution to be paused and resumed. When +it is paused, the kdamond does nothing other than applying online parameter +update. + +To know how user-space can pause/resume each context, refer to :ref:`DAMON +sysfs context ` usage documentation. + Overall Architecture ==================== From ade1a22a8bf612c4e9fd8fabd5b103dae4d6a0c6 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:23 -0700 Subject: [PATCH 2029/5214] Docs/admin-guide/mm/damon/usage: update for pause file Update DAMON usage document for the DAMON context execution pause/resume feature. Link: https://lore.kernel.org/20260427151231.113429-5-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index e84b58731f7e..d5548e460857 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -66,7 +66,8 @@ comma (","). │ :ref:`kdamonds `/nr_kdamonds │ │ :ref:`0 `/state,pid,refresh_ms │ │ │ :ref:`contexts `/nr_contexts - │ │ │ │ :ref:`0 `/avail_operations,operations,addr_unit + │ │ │ │ :ref:`0 `/avail_operations,operations,addr_unit, + │ │ │ │ pause │ │ │ │ │ :ref:`monitoring_attrs `/ │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us │ │ │ │ │ │ │ intervals_goal/access_bp,aggrs,min_sample_us,max_sample_us @@ -196,9 +197,9 @@ details). At the moment, only one context per kdamond is supported, so only contexts// ------------- -In each context directory, three files (``avail_operations``, ``operations`` -and ``addr_unit``) and three directories (``monitoring_attrs``, ``targets``, -and ``schemes``) exist. +In each context directory, four files (``avail_operations``, ``operations``, +``addr_unit`` and ``pause``) and three directories (``monitoring_attrs``, +``targets``, and ``schemes``) exist. DAMON supports multiple types of :ref:`monitoring operations `, including those for virtual address @@ -216,6 +217,9 @@ reading from the ``operations`` file. ``addr_unit`` file is for setting and getting the :ref:`address unit ` parameter of the operations set. +``pause`` file is for setting and getting the :ref:`pause request +` parameter of the context. + .. _sysfs_monitoring_attrs: contexts//monitoring_attrs/ From f0cefc367686a5fb1de0b9b0a3bcd179ef5e67ee Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:24 -0700 Subject: [PATCH 2030/5214] Docs/ABI/damon: update for pause sysfs file Update DAMON ABI document for the DAMON context execution pause/resume feature. Link: https://lore.kernel.org/20260427151231.113429-6-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/ABI/testing/sysfs-kernel-mm-damon | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index 213eb87392d8..971c22e34e72 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -84,6 +84,13 @@ Description: Writing an integer to this file sets the 'address unit' parameter of the given operations set of the context. Reading the file returns the last-written 'address unit' value. +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//pause +Date: Mar 2026 +Contact: SeongJae Park +Description: Writing a boolean keyword to this file sets the 'pause' request + parameter for the context. Reading the file returns the + last-written 'pause' value. + What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/sample_us Date: Mar 2022 Contact: SeongJae Park From eb1ae61075f3c9e4e395f23993b5f3593a2e8ff1 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:25 -0700 Subject: [PATCH 2031/5214] mm/damon/tests/core-kunit: test pause commitment Add a kunit test for commitment of damon_ctx->pause parameter that can be done using damon_commit_ctx(). Link: https://lore.kernel.org/20260427151231.113429-7-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/tests/core-kunit.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 6de622a2fd79..1b23a22ac04c 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -1083,6 +1083,10 @@ static void damon_test_commit_ctx(struct kunit *test) KUNIT_EXPECT_EQ(test, damon_commit_ctx(dst, src), 0); src->min_region_sz = 4095; KUNIT_EXPECT_EQ(test, damon_commit_ctx(dst, src), -EINVAL); + src->min_region_sz = 4096; + src->pause = true; + KUNIT_EXPECT_EQ(test, damon_commit_ctx(dst, src), 0); + KUNIT_EXPECT_TRUE(test, dst->pause); damon_destroy_ctx(src); damon_destroy_ctx(dst); } From 5d8585a1d7f689a6fee5a497d83017c5a8a4acfc Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:26 -0700 Subject: [PATCH 2032/5214] selftests/damon/_damon_sysfs: support pause file staging DAMON test-purpose sysfs interface control Python module, _damon_sysfs, is not supporting the newly added pause file. Add the support of the file, for future test and use of the feature. Link: https://lore.kernel.org/20260427151231.113429-8-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/_damon_sysfs.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 0f13512fa5e6..8b12cc048440 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -621,10 +621,11 @@ class DamonCtx: targets = None schemes = None kdamond = None + pause = None idx = None def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[], - schemes=[]): + schemes=[], pause=False): self.ops = ops self.monitoring_attrs = monitoring_attrs self.monitoring_attrs.context = self @@ -639,6 +640,8 @@ class DamonCtx: scheme.idx = idx scheme.context = self + self.pause=pause + def sysfs_dir(self): return os.path.join(self.kdamond.sysfs_dir(), 'contexts', '%d' % self.idx) @@ -679,6 +682,11 @@ class DamonCtx: err = scheme.stage() if err is not None: return err + + err = write_file(os.path.join(self.sysfs_dir(), 'pause'), self.pause) + if err is not None: + return err + return None class Kdamond: From d0e3f902aef881dab99111b59897dd045d932e47 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:27 -0700 Subject: [PATCH 2033/5214] selftests/damon/drgn_dump_damon_status: dump pause drgn_dump_damon_status is not dumping the damon_ctx->pause parameter value, so it cannot be tested. Dump it for future tests. Link: https://lore.kernel.org/20260427151231.113429-9-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/drgn_dump_damon_status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index b5c56233a923..972948e6215f 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -202,6 +202,7 @@ def damon_ctx_to_dict(ctx): ['attrs', attrs_to_dict], ['adaptive_targets', targets_to_list], ['schemes', schemes_to_list], + ['pause', bool], ]) def main(): From e88be73275e9bff727977499066606e35fa8db13 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:28 -0700 Subject: [PATCH 2034/5214] selftests/damon/sysfs.py: check pause on assert_ctx_committed() Extend sysfs.py tests to confirm damon_ctx->pause can be set using the pause sysfs file. Link: https://lore.kernel.org/20260427151231.113429-10-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 7e93584ff02b..eb56c19cd3f9 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -195,6 +195,7 @@ def assert_ctx_committed(ctx, dump): assert_monitoring_attrs_committed(ctx.monitoring_attrs, dump['attrs']) assert_monitoring_targets_committed(ctx.targets, dump['adaptive_targets']) assert_schemes_committed(ctx.schemes, dump['schemes']) + assert_true(dump['pause'] == ctx.pause, 'pause', dump) def assert_ctxs_committed(kdamonds): status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) From cb1a7622c90c169b1dabdd680711f85b6fde7319 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 08:12:29 -0700 Subject: [PATCH 2035/5214] selftests/damon/sysfs.py: pause DAMON before dumping status The sysfs.py test commits DAMON parameters, dump the internal DAMON state, and show if the parameters are committed as expected using the dumped state. While the dumping is ongoing, DAMON is alive. It can make internal changes including addition and removal of regions. It can therefore make a race that can result in false test results. Pause DAMON execution during the state dumping to avoid such races. Link: https://lore.kernel.org/20260427151231.113429-11-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.py | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index eb56c19cd3f9..cd4d82c85211 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -198,18 +198,55 @@ def assert_ctx_committed(ctx, dump): assert_true(dump['pause'] == ctx.pause, 'pause', dump) def assert_ctxs_committed(kdamonds): + ctxs_paused_for_dump = [] + kdamonds_paused_for_dump = [] + # pause for safe state dumping + for kd in kdamonds.kdamonds: + for ctx in kd.contexts: + if ctx.pause is False: + ctx.pause = True + ctxs_paused_for_dump.append(ctx) + if not kd in kdamonds_paused_for_dump: + kdamonds_paused_for_dump.append(kd) + if kd in kdamonds_paused_for_dump: + err = kd.commit() + if err is not None: + print('pause fail (%s)' % err) + kdamonds.stop() + exit(1) + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) if err is not None: print(err) kdamonds.stop() exit(1) + # resume contexts paused for safe state dumping + for ctx in ctxs_paused_for_dump: + ctx.pause = False + for kd in kdamonds_paused_for_dump: + err = kd.commit() + if err is not None: + print('resume fail (%s)' % err) + kdamonds.stop() + exit(1) + + # restore for comparison + for ctx in ctxs_paused_for_dump: + ctx.pause = True + ctxs = kdamonds.kdamonds[0].contexts dump = status['contexts'] assert_true(len(ctxs) == len(dump), 'ctxs length', dump) for idx, ctx in enumerate(ctxs): assert_ctx_committed(ctx, dump[idx]) + # restore for the caller + for kd in kdamonds.kdamonds: + for ctx in kd.contexts: + if ctx in ctxs_paused_for_dump: + ctx.pause = False + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -309,6 +346,7 @@ def main(): print('kdamond start failed: %s' % err) exit(1) kdamonds.kdamonds[0].contexts[0].targets[1].obsolete = True + kdamonds.kdamonds[0].contexts[0].pause = True kdamonds.kdamonds[0].commit() del kdamonds.kdamonds[0].contexts[0].targets[1] assert_ctxs_committed(kdamonds) From d94d0f9c153f8d9a234171d1ff1c48e513254e7a Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Tue, 24 Mar 2026 19:07:09 +0000 Subject: [PATCH 2036/5214] mm/migrate: rename PAGE_ migration flags to FOLIO_ These flags only track folio-specific state during migration and are not used for movable_ops pages. Rename the enum values and the old_page_state variable to match. No functional change. Link: https://lore.kernel.org/20260324190706.964555-4-shivankg@amd.com Signed-off-by: Shivank Garg Suggested-by: David Hildenbrand Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Reviewed-by: Huang Ying Cc: Alistair Popple Cc: Byungchul Park Cc: Gregory Price Cc: Joshua Hahn Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Rakie Kim Cc: Shivank Garg Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/migrate.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/mm/migrate.c b/mm/migrate.c index 8a64291ab5b4..0c6a0ab6ecce 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1135,26 +1135,24 @@ static int move_to_new_folio(struct folio *dst, struct folio *src, * This is safe because nobody is using it except us. */ enum { - PAGE_WAS_MAPPED = BIT(0), - PAGE_WAS_MLOCKED = BIT(1), - PAGE_OLD_STATES = PAGE_WAS_MAPPED | PAGE_WAS_MLOCKED, + FOLIO_WAS_MAPPED = BIT(0), + FOLIO_WAS_MLOCKED = BIT(1), + FOLIO_OLD_STATES = FOLIO_WAS_MAPPED | FOLIO_WAS_MLOCKED, }; static void __migrate_folio_record(struct folio *dst, - int old_page_state, - struct anon_vma *anon_vma) + int old_folio_state, struct anon_vma *anon_vma) { - dst->private = (void *)anon_vma + old_page_state; + dst->private = (void *)anon_vma + old_folio_state; } static void __migrate_folio_extract(struct folio *dst, - int *old_page_state, - struct anon_vma **anon_vmap) + int *old_folio_state, struct anon_vma **anon_vmap) { unsigned long private = (unsigned long)dst->private; - *anon_vmap = (struct anon_vma *)(private & ~PAGE_OLD_STATES); - *old_page_state = private & PAGE_OLD_STATES; + *anon_vmap = (struct anon_vma *)(private & ~FOLIO_OLD_STATES); + *old_folio_state = private & FOLIO_OLD_STATES; dst->private = NULL; } @@ -1209,7 +1207,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, { struct folio *dst; int rc = -EAGAIN; - int old_page_state = 0; + int old_folio_state = 0; struct anon_vma *anon_vma = NULL; bool locked = false; bool dst_locked = false; @@ -1253,7 +1251,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, } locked = true; if (folio_test_mlocked(src)) - old_page_state |= PAGE_WAS_MLOCKED; + old_folio_state |= FOLIO_WAS_MLOCKED; if (folio_test_writeback(src)) { /* @@ -1302,7 +1300,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, dst_locked = true; if (unlikely(page_has_movable_ops(&src->page))) { - __migrate_folio_record(dst, old_page_state, anon_vma); + __migrate_folio_record(dst, old_folio_state, anon_vma); return 0; } @@ -1328,11 +1326,11 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, VM_BUG_ON_FOLIO(folio_test_anon(src) && !folio_test_ksm(src) && !anon_vma, src); try_to_migrate(src, mode == MIGRATE_ASYNC ? TTU_BATCH_FLUSH : 0); - old_page_state |= PAGE_WAS_MAPPED; + old_folio_state |= FOLIO_WAS_MAPPED; } if (!folio_mapped(src)) { - __migrate_folio_record(dst, old_page_state, anon_vma); + __migrate_folio_record(dst, old_folio_state, anon_vma); return 0; } @@ -1344,7 +1342,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, if (rc == -EAGAIN) ret = NULL; - migrate_folio_undo_src(src, old_page_state & PAGE_WAS_MAPPED, + migrate_folio_undo_src(src, old_folio_state & FOLIO_WAS_MAPPED, anon_vma, locked, ret); migrate_folio_undo_dst(dst, dst_locked, put_new_folio, private); @@ -1358,13 +1356,13 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private, struct list_head *ret) { int rc; - int old_page_state = 0; + int old_folio_state = 0; struct anon_vma *anon_vma = NULL; bool src_deferred_split = false; bool src_partially_mapped = false; struct list_head *prev; - __migrate_folio_extract(dst, &old_page_state, &anon_vma); + __migrate_folio_extract(dst, &old_folio_state, &anon_vma); prev = dst->lru.prev; list_del(&dst->lru); @@ -1404,10 +1402,10 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private, * isolated from the unevictable LRU: but this case is the easiest. */ folio_add_lru(dst); - if (old_page_state & PAGE_WAS_MLOCKED) + if (old_folio_state & FOLIO_WAS_MLOCKED) lru_add_drain(); - if (old_page_state & PAGE_WAS_MAPPED) + if (old_folio_state & FOLIO_WAS_MAPPED) remove_migration_ptes(src, dst, 0); out_unlock_both: @@ -1439,11 +1437,11 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private, */ if (rc == -EAGAIN) { list_add(&dst->lru, prev); - __migrate_folio_record(dst, old_page_state, anon_vma); + __migrate_folio_record(dst, old_folio_state, anon_vma); return rc; } - migrate_folio_undo_src(src, old_page_state & PAGE_WAS_MAPPED, + migrate_folio_undo_src(src, old_folio_state & FOLIO_WAS_MAPPED, anon_vma, true, ret); migrate_folio_undo_dst(dst, true, put_new_folio, private); @@ -1777,11 +1775,11 @@ static void migrate_folios_undo(struct list_head *src_folios, dst = list_first_entry(dst_folios, struct folio, lru); dst2 = list_next_entry(dst, lru); list_for_each_entry_safe(folio, folio2, src_folios, lru) { - int old_page_state = 0; + int old_folio_state = 0; struct anon_vma *anon_vma = NULL; - __migrate_folio_extract(dst, &old_page_state, &anon_vma); - migrate_folio_undo_src(folio, old_page_state & PAGE_WAS_MAPPED, + __migrate_folio_extract(dst, &old_folio_state, &anon_vma); + migrate_folio_undo_src(folio, old_folio_state & FOLIO_WAS_MAPPED, anon_vma, true, ret_folios); list_del(&dst->lru); migrate_folio_undo_dst(dst, true, put_new_folio, private); From 838376c60df0f28b5b3659a3ef649f07d0eeadf6 Mon Sep 17 00:00:00 2001 From: Hui Zhu Date: Wed, 29 Apr 2026 16:42:16 +0800 Subject: [PATCH 2037/5214] mm/memcontrol: hoist pstatc_pcpu assignment out of CPU loop In mem_cgroup_alloc(), the assignment of pstatc_pcpu is invariant with respect to the for_each_possible_cpu() loop: both the 'parent' pointer and 'parent->vmstats_percpu' remain constant throughout all iterations. The original code redundantly re-evaluated the 'if (parent)' condition and reassigned pstatc_pcpu on every CPU iteration, then repeated the same ternary check 'parent ? pstatc_pcpu : NULL' when storing into statc->parent_pcpu. Move the single conditional assignment of pstatc_pcpu to before the loop, resolving both the loop-invariant placement issue and the duplicated null check. On systems with a large number of possible CPUs, this eliminates repeated branch evaluation with no functional change. No functional change intended. Link: https://lore.kernel.org/20260429084216.186238-1-hui.zhu@linux.dev Signed-off-by: Hui Zhu Reviewed-by: SeongJae Park Acked-by: Shakeel Butt Cc: Johannes Weiner Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/memcontrol.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 177732fef010..2bc9a7238939 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4002,11 +4002,10 @@ static struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent) if (!memcg1_alloc_events(memcg)) goto fail; + pstatc_pcpu = parent ? parent->vmstats_percpu : NULL; for_each_possible_cpu(cpu) { - if (parent) - pstatc_pcpu = parent->vmstats_percpu; statc = per_cpu_ptr(memcg->vmstats_percpu, cpu); - statc->parent_pcpu = parent ? pstatc_pcpu : NULL; + statc->parent_pcpu = pstatc_pcpu; statc->vmstats = memcg->vmstats; } From b56ca146a2b2750172f91f6db960a37a1a546efd Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Wed, 29 Apr 2026 15:57:02 +0530 Subject: [PATCH 2038/5214] vmalloc: add __GFP_SKIP_KASAN support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "kasan: hw_tags: Disable tagging for stack and page-tables", v4. Stacks and page tables are always accessed with the match-all tag, so assigning a new random tag every time at allocation and setting invalid tag at deallocation time, just adds overhead without improving the detection. With __GFP_SKIP_KASAN the page keeps its poison tag and KASAN_TAG_KERNEL (match-all tag) is stored in the page flags while keeping the poison tag in the hardware. The benefit of it is that 256 tag setting instruction per 4 kB page aren't needed at allocation and deallocation time. Thus match-all pointers still work, while non-match tags (other than poison tag) still fault. __GFP_SKIP_KASAN only skips for KASAN_HW_TAGS mode, so coverage is unchanged. Benchmark: The benchmark has two modes. In thread mode, the child process forks and creates N threads. In pgtable mode, the parent maps and faults a specified memory size and then forks repeatedly with children exiting immediately. Thread benchmark: 2000 iterations, 2000 threads: 2.575 s → 2.229 s (~13.4% faster) The pgtable samples: - 2048 MB, 2000 iters 19.08 s → 17.62 s (~7.6% faster) This patch (of 3): For allocations that will be accessed only with match-all pointers (e.g., kernel stacks), setting tags is wasted work. If the caller already set __GFP_SKIP_KASAN, skip tag setting of vmalloc pages. Before this patch, __GFP_SKIP_KASAN wasn't being used with vmalloc APIs. So it wasn't being checked. Now its being checked and acted upon. Other KASAN modes are unchanged because __GFP_SKIP_KASAN is ignored for them in the page allocator, and in vmalloc too we ignore this flag for them. This is a preparatory patch for optimizing kernel stack allocations. Link: https://lore.kernel.org/20260429102704.680174-1-dev.jain@arm.com Link: https://lore.kernel.org/20260429102704.680174-2-dev.jain@arm.com Signed-off-by: Muhammad Usama Anjum Co-developed-by: Ryan Roberts Signed-off-by: Ryan Roberts Co-developed-by: Dev Jain Signed-off-by: Dev Jain Reviewed-by: Catalin Marinas Cc: Arnd Bergmann Cc: Ben Segall Cc: David Hildenbrand Cc: Dietmar Eggemann Cc: Ingo Molnar Cc: Juri Lelli Cc: Kees Cook Cc: K Prateek Nayak Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mathieu Desnoyers Cc: Mel Gorman Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: "Uladzislau Rezki (Sony)" Cc: Valentin Schneider Cc: Vincent Guittot Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/gfp_types.h | 6 +++--- mm/vmalloc.c | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/linux/gfp_types.h b/include/linux/gfp_types.h index cd4972a7c97c..54ca0c88bab6 100644 --- a/include/linux/gfp_types.h +++ b/include/linux/gfp_types.h @@ -281,9 +281,9 @@ enum { * * %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation. * Used for userspace and vmalloc pages; the latter are unpoisoned by - * kasan_unpoison_vmalloc instead. For userspace pages, results in - * poisoning being skipped as well, see should_skip_kasan_poison for - * details. Only effective in HW_TAGS mode. + * kasan_unpoison_vmalloc instead. If passed to vmalloc, kasan_unpoison_vmalloc + * is skipped too. For userspace pages, results in poisoning being skipped as + * well, see should_skip_kasan_poison for details. Only effective in HW_TAGS mode. */ #define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) #define __GFP_COMP ((__force gfp_t)___GFP_COMP) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 99fce4f9f6e4..eabb86b13b7e 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3933,7 +3933,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, __GFP_NOFAIL | __GFP_ZERO |\ __GFP_NORETRY | __GFP_RETRY_MAYFAIL |\ GFP_NOFS | GFP_NOIO | GFP_KERNEL_ACCOUNT |\ - GFP_USER | __GFP_NOLOCKDEP) + GFP_USER | __GFP_NOLOCKDEP | __GFP_SKIP_KASAN) static gfp_t vmalloc_fix_flags(gfp_t flags) { @@ -3974,6 +3974,9 @@ static gfp_t vmalloc_fix_flags(gfp_t flags) * * %__GFP_NOWARN can be used to suppress failure messages. * + * %__GFP_SKIP_KASAN can be used to skip unpoisoning of mapped pages + * (when prot=%PAGE_KERNEL). + * * Can not be called from interrupt nor NMI contexts. * Return: the address of the area or %NULL on failure */ @@ -3987,6 +3990,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align, kasan_vmalloc_flags_t kasan_flags = KASAN_VMALLOC_NONE; unsigned long original_align = align; unsigned int shift = PAGE_SHIFT; + bool skip_vmalloc_kasan = kasan_hw_tags_enabled() && (gfp_mask & __GFP_SKIP_KASAN); if (WARN_ON_ONCE(!size)) return NULL; @@ -4017,7 +4021,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align, again: area = __get_vm_area_node(size, align, shift, VM_ALLOC | VM_UNINITIALIZED | vm_flags, start, end, node, - gfp_mask, caller); + gfp_mask & ~__GFP_SKIP_KASAN, caller); if (!area) { bool nofail = gfp_mask & __GFP_NOFAIL; warn_alloc(gfp_mask, NULL, @@ -4035,7 +4039,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align, * kasan_unpoison_vmalloc(). */ if (pgprot_val(prot) == pgprot_val(PAGE_KERNEL)) { - if (kasan_hw_tags_enabled()) { + if (kasan_hw_tags_enabled() && !skip_vmalloc_kasan) { /* * Modify protection bits to allow tagging. * This must be done before mapping. @@ -4072,7 +4076,8 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align, (gfp_mask & __GFP_SKIP_ZERO)) kasan_flags |= KASAN_VMALLOC_INIT; /* KASAN_VMALLOC_PROT_NORMAL already set if required. */ - area->addr = kasan_unpoison_vmalloc(area->addr, size, kasan_flags); + if (!skip_vmalloc_kasan) + area->addr = kasan_unpoison_vmalloc(area->addr, size, kasan_flags); /* * In this function, newly allocated vm_struct has VM_UNINITIALIZED From 6ae51adb084a9d87a8b9501d2231e20271dece87 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Wed, 29 Apr 2026 15:57:03 +0530 Subject: [PATCH 2039/5214] kasan: skip HW tagging for all kernel thread stacks HW-tag KASAN never checks kernel stacks because stack pointers carry the match-all tag, so setting/poisoning tags is pure overhead. - Add __GFP_SKIP_KASAN to THREADINFO_GFP so every stack allocator that uses it skips tagging (fork path plus arch users) - Add __GFP_SKIP_KASAN to GFP_VMAP_STACK for the fork-specific vmap stacks. - When reusing cached vmap stacks, skip kasan_unpoison_range() if HW tags are enabled. Software KASAN is unchanged; this only affects tag-based KASAN. Link: https://lore.kernel.org/20260429102704.680174-3-dev.jain@arm.com Signed-off-by: Muhammad Usama Anjum Signed-off-by: Dev Jain Reviewed-by: Catalin Marinas Cc: Arnd Bergmann Cc: Ben Segall Cc: David Hildenbrand (Arm) Cc: Dietmar Eggemann Cc: Ingo Molnar Cc: Juri Lelli Cc: Kees Cook Cc: K Prateek Nayak Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mathieu Desnoyers Cc: Mel Gorman Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Zijlstra Cc: Ryan Roberts Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: "Uladzislau Rezki (Sony)" Cc: Valentin Schneider Cc: Vincent Guittot Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/thread_info.h | 2 +- kernel/fork.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index 051e42902690..307b8390fc67 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -92,7 +92,7 @@ static inline long set_restart_fn(struct restart_block *restart, #define THREAD_ALIGN THREAD_SIZE #endif -#define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_ZERO) +#define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_ZERO | __GFP_SKIP_KASAN) /* * flag set/clear/test wrappers diff --git a/kernel/fork.c b/kernel/fork.c index 8ac38beae360..ec6a120291e5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -204,7 +204,7 @@ static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]); * accounting is performed by the code assigning/releasing stacks to tasks. * We need a zeroed memory without __GFP_ACCOUNT. */ -#define GFP_VMAP_STACK (GFP_KERNEL | __GFP_ZERO) +#define GFP_VMAP_STACK (GFP_KERNEL | __GFP_ZERO | __GFP_SKIP_KASAN) struct vm_stack { struct rcu_head rcu; @@ -342,7 +342,8 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) } /* Reset stack metadata. */ - kasan_unpoison_range(vm_area->addr, THREAD_SIZE); + if (!kasan_hw_tags_enabled()) + kasan_unpoison_range(vm_area->addr, THREAD_SIZE); stack = kasan_reset_tag(vm_area->addr); From d46644af7636c4cb876110c8ff7f1efbbb815bfe Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Wed, 29 Apr 2026 15:57:04 +0530 Subject: [PATCH 2040/5214] mm: skip KASAN tagging for page-allocated page tables Page tables are always accessed via the linear mapping with a match-all tag, so HW-tag KASAN never checks them. For page-allocated tables (PTEs and PGDs etc), avoid the tag setup and poisoning overhead by using __GFP_SKIP_KASAN. SLUB-backed page tables are unchanged for now. (They aren't widely used and require more SLUB related skip logic. Leave it later.) Link: https://lore.kernel.org/20260429102704.680174-4-dev.jain@arm.com Signed-off-by: Muhammad Usama Anjum Signed-off-by: Dev Jain Reviewed-by: Ryan Roberts Reviewed-by: Catalin Marinas Acked-by: David Hildenbrand (Arm) Cc: Arnd Bergmann Cc: Ben Segall Cc: Dietmar Eggemann Cc: Ingo Molnar Cc: Juri Lelli Cc: Kees Cook Cc: K Prateek Nayak Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mathieu Desnoyers Cc: Mel Gorman Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: "Uladzislau Rezki (Sony)" Cc: Valentin Schneider Cc: Vincent Guittot Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/asm-generic/pgalloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/asm-generic/pgalloc.h b/include/asm-generic/pgalloc.h index 57137d3ac159..051aa1331051 100644 --- a/include/asm-generic/pgalloc.h +++ b/include/asm-generic/pgalloc.h @@ -4,7 +4,7 @@ #ifdef CONFIG_MMU -#define GFP_PGTABLE_KERNEL (GFP_KERNEL | __GFP_ZERO) +#define GFP_PGTABLE_KERNEL (GFP_KERNEL | __GFP_ZERO | __GFP_SKIP_KASAN) #define GFP_PGTABLE_USER (GFP_PGTABLE_KERNEL | __GFP_ACCOUNT) /** From 70d8797c15d640982365e96e34e93a3aa38e82da Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:23 -0700 Subject: [PATCH 2041/5214] mm/damon: introduce damon_set_region_system_rams_default() Patch series "mm/damon/reclaim,lru_sort: monitor all system rams by default". DAMON_RECLAIM and DAMON_LRU_SORT set the biggest 'System RAM' resource of the system as the default monitoring target address range. The main intention behind the design is to minimize the overhead coming from monitoring of non-System RAM areas. This could result in an odd setup when there are multiple discrete System RAMs of considerable sizes. For example, there are System RAMs each having 500 GiB size. In this case, only the first 500 GiB will be set as the monitoring region by default. This is particularly common on NUMA systems. Hence the modules allow users to set the monitoring target address range using the module parameters if the default setup doesn't work for them. In other words, the current design trades ease of setup for lower overhead. However, because DAMON utilizes the sampling based access check and the adaptive regions adjustment mechanisms, the overhead from the monitoring of non-System RAM areas should be negligible in most setups. Meanwhile, the setup complexity is causing real headaches for users who need to run those modules on various types of systems. That is, the current tradeoff is not a good deal. Set the physical address range that can cover all System RAM areas of the system as the default monitoring regions for DAMON_RECLAIM and DAMON_LRU_SORT. Technically speaking, this is changing documented behavior. However, it makes no sense to believe there is a real use case that really depends on the old weird default behavior. If the old default behavior was working for them in the reasonable way, this change will only add a negligible amount of monitoring overhead. If it didn't work, the users may already be using manual monitoring regions setup, and they will not be affected by this change. Patches Sequence ================ Patch 1 introduces a new core function that will be used for the new default monitoring target region setup. Patch 2 and 3 update DAMON_RECLAIM and DAMON_LRU_SORT to use the new function instead of the old one, respectively. Patch 4 removes the old core function that was replaced by the new one, as there is no more user of it. Patch 5 updates DAMON_STAT to use the new one instead of its in-house nearly-duplicate self implementation of the functionality. Finally patches 6 and 7 update the DAMON_RECLAIM and DAMON_LRU_SORT user documentation for the new behaviors, respectively. This patch (of 7): damon_set_region_biggest_system_ram_default() sets the monitoring target region as the caller requested. If the caller didn't specify the region, it finds the biggest System RAM of the system and sets it as the target region. When there are more than one considerable size of System RAM resources in the system, the default target setup makes no sense. Introduce a variant, namely damon_set_region_system_rams_default(). It sets a physical address range that covers all System RAM resources as the default target region. Link: https://lore.kernel.org/20260429041232.90257-1-sj@kernel.org Link: https://lore.kernel.org/20260429041232.90257-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 5 +++ mm/damon/core.c | 79 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index f2370a3a4a9a..f656908b2d38 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1010,6 +1010,11 @@ int damon_kdamond_pid(struct damon_ctx *ctx); int damon_call(struct damon_ctx *ctx, struct damon_call_control *control); int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control); +int damon_set_region_system_rams_default(struct damon_target *t, + unsigned long *start, unsigned long *end, + unsigned long addr_unit, + unsigned long min_region_sz); + int damon_set_region_biggest_system_ram_default(struct damon_target *t, unsigned long *start, unsigned long *end, unsigned long addr_unit, diff --git a/mm/damon/core.c b/mm/damon/core.c index 05e4bef367db..980a31cd3498 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -3328,14 +3328,20 @@ static int kdamond_fn(void *data) return 0; } -static int walk_system_ram(struct resource *res, void *arg) -{ - struct resource *a = arg; +struct damon_system_ram_range_walk_arg { + bool walked; + struct resource res; +}; - if (resource_size(a) < resource_size(res)) { - a->start = res->start; - a->end = res->end; +static int damon_system_ram_walk_fn(struct resource *res, void *arg) +{ + struct damon_system_ram_range_walk_arg *a = arg; + + if (!a->walked) { + a->walked = true; + a->res.start = res->start; } + a->res.end = res->end; return 0; } @@ -3352,6 +3358,67 @@ static unsigned long damon_res_to_core_addr(resource_size_t ra, return ra / addr_unit; } +static bool damon_find_system_rams_range(unsigned long *start, + unsigned long *end, unsigned long addr_unit) +{ + struct damon_system_ram_range_walk_arg arg = {}; + + walk_system_ram_res(0, -1, &arg, damon_system_ram_walk_fn); + if (!arg.walked) + return false; + *start = damon_res_to_core_addr(arg.res.start, addr_unit); + *end = damon_res_to_core_addr(arg.res.end + 1, addr_unit); + if (*end <= *start) + return false; + return true; +} + +/** + * damon_set_region_system_rams_default() - Set the region of the given + * monitoring target as requested, or to cover all 'System RAM' resources. + * @t: The monitoring target to set the region. + * @start: The pointer to the start address of the region. + * @end: The pointer to the end address of the region. + * @addr_unit: The address unit for the damon_ctx of @t. + * @min_region_sz: Minimum region size. + * + * This function sets the region of @t as requested by @start and @end. If the + * values of @start and @end are zero, however, this function finds 'System + * RAM' resources and sets the region to cover all the resource. In the latter + * case, this function saves the start and the end addresseses of the first and + * the last resources in @start and @end, respectively. + * + * Return: 0 on success, negative error code otherwise. + */ +int damon_set_region_system_rams_default(struct damon_target *t, + unsigned long *start, unsigned long *end, + unsigned long addr_unit, unsigned long min_region_sz) +{ + struct damon_addr_range addr_range; + + if (*start > *end) + return -EINVAL; + + if (!*start && !*end && + !damon_find_system_rams_range(start, end, addr_unit)) + return -EINVAL; + + addr_range.start = *start; + addr_range.end = *end; + return damon_set_regions(t, &addr_range, 1, min_region_sz); +} + +static int walk_system_ram(struct resource *res, void *arg) +{ + struct resource *a = arg; + + if (resource_size(a) < resource_size(res)) { + a->start = res->start; + a->end = res->end; + } + return 0; +} + /* * Find biggest 'System RAM' resource and store its start and end address in * @start and @end, respectively. If no System RAM is found, returns false. From 99976875c9e59b975c85d73386d76944ce74f598 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:24 -0700 Subject: [PATCH 2042/5214] mm/damon/reclaim: cover all system rams DAMON_RECLAIM allows users to set the physical address range to monitor and do the work on. When users don't explicitly set the range, the biggest System RAM resource of the system is selected as the monitoring target address range. The intention was to reduce the overhead from monitoring non-System RAM areas because monitoring of non-System RAM may be meaningless. However, because of the sampling based access check and adaptive regions adjustment, the overhead should be negligible. It makes more sense to just cover all system rams of the system. Do so. Link: https://lore.kernel.org/20260429041232.90257-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/reclaim.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index b330ff169590..a60ee800d63e 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -113,7 +113,8 @@ DEFINE_DAMON_MODULES_MON_ATTRS_PARAMS(damon_reclaim_mon_attrs); * Start of the target memory region in physical address. * * The start physical address of memory region that DAMON_RECLAIM will do work - * against. By default, biggest System RAM is used as the region. + * against. By default, the system's entire physical memory is used as the + * region. */ static unsigned long monitor_region_start __read_mostly; module_param(monitor_region_start, ulong, 0600); @@ -122,7 +123,8 @@ module_param(monitor_region_start, ulong, 0600); * End of the target memory region in physical address. * * The end physical address of memory region that DAMON_RECLAIM will do work - * against. By default, biggest System RAM is used as the region. + * against. By default, the system's entire physical memory is used as the + * region. */ static unsigned long monitor_region_end __read_mostly; module_param(monitor_region_end, ulong, 0600); @@ -232,11 +234,9 @@ static int damon_reclaim_apply_parameters(void) damos_add_filter(scheme, filter); } - err = damon_set_region_biggest_system_ram_default(param_target, - &monitor_region_start, - &monitor_region_end, - param_ctx->addr_unit, - param_ctx->min_region_sz); + err = damon_set_region_system_rams_default(param_target, + &monitor_region_start, &monitor_region_end, + param_ctx->addr_unit, param_ctx->min_region_sz); if (err) goto out; err = damon_commit_ctx(ctx, param_ctx); From e17741ad08451e652924abe6277362d2ae19dd4a Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:25 -0700 Subject: [PATCH 2043/5214] mm/damon/lru_sort: cover all system rams DAMON_LRU_SORT allows users to set the physical address range to monitor and do the work on. When users don't explicitly set the range, the biggest system ram resource of the system is selected as the monitoring target address range. The intention was to reduce the overhead from monitoring non-System RAM areas because monitoring non-System RAM may be meaningless. However, because of the sampling based access check and adaptive regions adjustment, the overhead should be negligible. It makes more sense to just cover all system rams of the system. Do so. Link: https://lore.kernel.org/20260429041232.90257-4-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 7569e471160a..2eb559d913b6 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -139,7 +139,8 @@ DEFINE_DAMON_MODULES_MON_ATTRS_PARAMS(damon_lru_sort_mon_attrs); * Start of the target memory region in physical address. * * The start physical address of memory region that DAMON_LRU_SORT will do work - * against. By default, biggest System RAM is used as the region. + * against. By default, the system's entire physical memory is used as the + * region. */ static unsigned long monitor_region_start __read_mostly; module_param(monitor_region_start, ulong, 0600); @@ -148,7 +149,8 @@ module_param(monitor_region_start, ulong, 0600); * End of the target memory region in physical address. * * The end physical address of memory region that DAMON_LRU_SORT will do work - * against. By default, biggest System RAM is used as the region. + * against. By default, the system's entire physical memory is used as the + * region. */ static unsigned long monitor_region_end __read_mostly; module_param(monitor_region_end, ulong, 0600); @@ -326,7 +328,7 @@ static int damon_lru_sort_apply_parameters(void) if (err) goto out; - err = damon_set_region_biggest_system_ram_default(param_target, + err = damon_set_region_system_rams_default(param_target, &monitor_region_start, &monitor_region_end, param_ctx->addr_unit, From 3a870b43776c0c9740a087eb0d831cd6cb8016f7 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:26 -0700 Subject: [PATCH 2044/5214] mm/damon/core: remove damon_set_region_biggest_system_ram_default() Now nobody is using damon_set_region_biggest_system_ram_default(). Remove it. Link: https://lore.kernel.org/20260429041232.90257-5-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 5 ---- mm/damon/core.c | 64 ------------------------------------------- 2 files changed, 69 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index f656908b2d38..c7a31572689b 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1015,11 +1015,6 @@ int damon_set_region_system_rams_default(struct damon_target *t, unsigned long addr_unit, unsigned long min_region_sz); -int damon_set_region_biggest_system_ram_default(struct damon_target *t, - unsigned long *start, unsigned long *end, - unsigned long addr_unit, - unsigned long min_region_sz); - #endif /* CONFIG_DAMON */ #endif /* _DAMON_H */ diff --git a/mm/damon/core.c b/mm/damon/core.c index 980a31cd3498..9f38deddcb30 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -3408,70 +3408,6 @@ int damon_set_region_system_rams_default(struct damon_target *t, return damon_set_regions(t, &addr_range, 1, min_region_sz); } -static int walk_system_ram(struct resource *res, void *arg) -{ - struct resource *a = arg; - - if (resource_size(a) < resource_size(res)) { - a->start = res->start; - a->end = res->end; - } - return 0; -} - -/* - * Find biggest 'System RAM' resource and store its start and end address in - * @start and @end, respectively. If no System RAM is found, returns false. - */ -static bool damon_find_biggest_system_ram(unsigned long *start, - unsigned long *end, unsigned long addr_unit) - -{ - struct resource res = {}; - - walk_system_ram_res(0, -1, &res, walk_system_ram); - *start = damon_res_to_core_addr(res.start, addr_unit); - *end = damon_res_to_core_addr(res.end + 1, addr_unit); - if (*end <= *start) - return false; - return true; -} - -/** - * damon_set_region_biggest_system_ram_default() - Set the region of the given - * monitoring target as requested, or biggest 'System RAM'. - * @t: The monitoring target to set the region. - * @start: The pointer to the start address of the region. - * @end: The pointer to the end address of the region. - * @addr_unit: The address unit for the damon_ctx of @t. - * @min_region_sz: Minimum region size. - * - * This function sets the region of @t as requested by @start and @end. If the - * values of @start and @end are zero, however, this function finds the biggest - * 'System RAM' resource and sets the region to cover the resource. In the - * latter case, this function saves the start and end addresses of the resource - * in @start and @end, respectively. - * - * Return: 0 on success, negative error code otherwise. - */ -int damon_set_region_biggest_system_ram_default(struct damon_target *t, - unsigned long *start, unsigned long *end, - unsigned long addr_unit, unsigned long min_region_sz) -{ - struct damon_addr_range addr_range; - - if (*start > *end) - return -EINVAL; - - if (!*start && !*end && - !damon_find_biggest_system_ram(start, end, addr_unit)) - return -EINVAL; - - addr_range.start = *start; - addr_range.end = *end; - return damon_set_regions(t, &addr_range, 1, min_region_sz); -} - /* * damon_moving_sum() - Calculate an inferred moving sum value. * @mvsum: Inferred sum of the last @len_window values. From 122dff8c22eafcdb3adeaf7bdf1c63adeb9457e2 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:27 -0700 Subject: [PATCH 2045/5214] mm/damon/stat: use damon_set_region_system_rams_default() damon_stat_set_moniotirng_region() is nearly a duplicate of the core function, damon_set_region_system_rams_default(). Use the core implementation. Link: https://lore.kernel.org/20260429041232.90257-6-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/stat.c | 53 +++---------------------------------------------- 1 file changed, 3 insertions(+), 50 deletions(-) diff --git a/mm/damon/stat.c b/mm/damon/stat.c index 3951b762cbdd..f4d3203e9263 100644 --- a/mm/damon/stat.c +++ b/mm/damon/stat.c @@ -148,59 +148,12 @@ static int damon_stat_damon_call_fn(void *data) return 0; } -struct damon_stat_system_ram_range_walk_arg { - bool walked; - struct resource res; -}; - -static int damon_stat_system_ram_walk_fn(struct resource *res, void *arg) -{ - struct damon_stat_system_ram_range_walk_arg *a = arg; - - if (!a->walked) { - a->walked = true; - a->res.start = res->start; - } - a->res.end = res->end; - return 0; -} - -static unsigned long damon_stat_res_to_core_addr(resource_size_t ra, - unsigned long addr_unit) -{ - /* - * Use div_u64() for avoiding linking errors related with __udivdi3, - * __aeabi_uldivmod, or similar problems. This should also improve the - * performance optimization (read div_u64() comment for the detail). - */ - if (sizeof(ra) == 8 && sizeof(addr_unit) == 4) - return div_u64(ra, addr_unit); - return ra / addr_unit; -} - -static int damon_stat_set_monitoring_region(struct damon_target *t, - unsigned long addr_unit, unsigned long min_region_sz) -{ - struct damon_addr_range addr_range; - struct damon_stat_system_ram_range_walk_arg arg = {}; - - walk_system_ram_res(0, -1, &arg, damon_stat_system_ram_walk_fn); - if (!arg.walked) - return -EINVAL; - addr_range.start = damon_stat_res_to_core_addr( - arg.res.start, addr_unit); - addr_range.end = damon_stat_res_to_core_addr( - arg.res.end + 1, addr_unit); - if (addr_range.end <= addr_range.start) - return -EINVAL; - return damon_set_regions(t, &addr_range, 1, min_region_sz); -} - static struct damon_ctx *damon_stat_build_ctx(void) { struct damon_ctx *ctx; struct damon_attrs attrs; struct damon_target *target; + unsigned long start = 0, end = 0; ctx = damon_new_ctx(); if (!ctx) @@ -230,8 +183,8 @@ static struct damon_ctx *damon_stat_build_ctx(void) if (!target) goto free_out; damon_add_target(ctx, target); - if (damon_stat_set_monitoring_region(target, ctx->addr_unit, - ctx->min_region_sz)) + if (damon_set_region_system_rams_default(target, &start, &end, + ctx->addr_unit, ctx->min_region_sz)) goto free_out; return ctx; free_out: From 2262a915615ba308a87e8cf05acf1b16c01ca04b Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:28 -0700 Subject: [PATCH 2046/5214] Docs/admin-guide/mm/damon/reclaim: update for entire memory monitoring Update DAMON_RECLAIM usage document for the changed default monitoring target region selection. Link: https://lore.kernel.org/20260429041232.90257-7-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/reclaim.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/reclaim.rst b/Documentation/admin-guide/mm/damon/reclaim.rst index 01a34c215b66..57ab8b187650 100644 --- a/Documentation/admin-guide/mm/damon/reclaim.rst +++ b/Documentation/admin-guide/mm/damon/reclaim.rst @@ -229,7 +229,8 @@ Start of target memory region in physical address. The start physical address of memory region that DAMON_RECLAIM will do work against. That is, DAMON_RECLAIM will find cold memory regions in this region -and reclaims. By default, biggest System RAM is used as the region. +and reclaims. By default, the system's entire physical memory is used as the +region. monitor_region_end ------------------ @@ -238,7 +239,8 @@ End of target memory region in physical address. The end physical address of memory region that DAMON_RECLAIM will do work against. That is, DAMON_RECLAIM will find cold memory regions in this region -and reclaims. By default, biggest System RAM is used as the region. +and reclaims. By default, the system's entire physical memory is used as the +region. addr_unit --------- From 77289dcfa973d4a9984abaa2093e739038e1d94d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 28 Apr 2026 21:12:29 -0700 Subject: [PATCH 2047/5214] Docs/admin-guide/mm/damon/lru_sort: update for entire memory monitoring Update DAMON_LRU_SORT usage document for the changed default monitoring target region selection. Link: https://lore.kernel.org/20260429041232.90257-8-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/lru_sort.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/lru_sort.rst b/Documentation/admin-guide/mm/damon/lru_sort.rst index 25e2f042a383..b93ca9b0853d 100644 --- a/Documentation/admin-guide/mm/damon/lru_sort.rst +++ b/Documentation/admin-guide/mm/damon/lru_sort.rst @@ -246,7 +246,8 @@ monitor_region_start Start of target memory region in physical address. The start physical address of memory region that DAMON_LRU_SORT will do work -against. By default, biggest System RAM is used as the region. +against. By default, the system's entire physical memory is used as the +region. monitor_region_end ------------------ @@ -254,7 +255,8 @@ monitor_region_end End of target memory region in physical address. The end physical address of memory region that DAMON_LRU_SORT will do work -against. By default, biggest System RAM is used as the region. +against. By default, the system's entire physical memory is used as the +region. addr_unit --------- From 9f7ff45e99d322077af7f53f4a0a2b0907816531 Mon Sep 17 00:00:00 2001 From: Vineet Agarwal Date: Wed, 29 Apr 2026 17:28:16 +0530 Subject: [PATCH 2048/5214] selftests/mm: khugepaged: initialize file contents via mmap file_setup_area() currently allocates anonymous memory, fills it, and writes it into the backing file used for collapse testing. Instead of copying data through write(), resize the file with ftruncate(), map it directly with MAP_SHARED, and initialize the mapped area in place. This simplifies the setup path and avoids the need for explicit partial write handling. Link: https://lore.kernel.org/20260429115816.98824-1-agarwal.vineet2006@gmail.com Signed-off-by: Vineet Agarwal Reviewed-by: Zi Yan Tested-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/khugepaged.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 3fe7ef04ac62..c8393ca52cab 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -373,7 +373,7 @@ static void *file_setup_area(int nr_hpages) unlink(finfo.path); /* Cleanup from previous failed tests */ printf("Creating %s for collapse%s...", finfo.path, finfo.type == VMA_SHMEM ? " (tmpfs)" : ""); - fd = open(finfo.path, O_DSYNC | O_CREAT | O_RDWR | O_TRUNC | O_EXCL, + fd = open(finfo.path, O_CREAT | O_RDWR | O_TRUNC | O_EXCL, 777); if (fd < 0) { perror("open()"); @@ -381,9 +381,21 @@ static void *file_setup_area(int nr_hpages) } size = nr_hpages * hpage_pmd_size; - p = alloc_mapping(nr_hpages); + if (ftruncate(fd, size)) { + perror("ftruncate()"); + exit(EXIT_FAILURE); + } + p = mmap(BASE_ADDR, size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (p != BASE_ADDR) { + perror("mmap()"); + exit(EXIT_FAILURE); + } fill_memory(p, 0, size); - write(fd, p, size); + if (msync(p, size, MS_SYNC)) { + perror("msync()"); + exit(EXIT_FAILURE); + } close(fd); munmap(p, size); success("OK"); From ab3fad1b1cdc7aab95c49f389642c4fb88a4f35e Mon Sep 17 00:00:00 2001 From: Vineet Agarwal Date: Wed, 29 Apr 2026 19:34:34 +0530 Subject: [PATCH 2049/5214] mm/khugepaged: return -EAGAIN for SCAN_PAGE_HAS_PRIVATE in MADV_COLLAPSE MADV_COLLAPSE uses errno values to provide actionable feedback to userspace. Temporary resource constraints are mapped to -EAGAIN so the caller may retry, while intrinsic failures of the specified range are mapped to -EINVAL. collapse_file() returns SCAN_PAGE_HAS_PRIVATE when filemap_release_folio() fails while isolating file-backed folios for collapse. This currently falls through the default case in madvise_collapse_errno() and is reported to userspace as -EINVAL. However, filemap_release_folio() failure commonly reflects temporary folio state rather than a permanently uncollapsible range. For example, ext4 returns false when a folio still has dirty journalled data, btrfs returns false for dirty or writeback folios before extent state release, and NFS may return false while reclaiming filesystem-private folio state. In such cases, retrying MADV_COLLAPSE after writeback, reclaim or journal progress may succeed. This matches the existing -EAGAIN handling for SCAN_PAGE_DIRTY_OR_WRITEBACK and other transient collapse failures more closely than -EINVAL. Therefore, map SCAN_PAGE_HAS_PRIVATE to -EAGAIN so userspace receives retryable feedback for this temporary failure path. Link: https://lore.kernel.org/20260429140434.439456-1-agarwal.vineet2006@gmail.com Signed-off-by: Vineet Agarwal Reviewed-by: Dev Jain Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Lance Yang Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Nico Pache Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 5f4e009593e0..28a843f30b32 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2808,6 +2808,7 @@ static int madvise_collapse_errno(enum scan_result r) case SCAN_PAGE_LRU: case SCAN_DEL_PAGE_LRU: case SCAN_PAGE_FILLED: + case SCAN_PAGE_HAS_PRIVATE: case SCAN_PAGE_DIRTY_OR_WRITEBACK: return -EAGAIN; /* From 7e6cc9f954aa3455cd6ef4dfcbd4102265c30884 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 29 Apr 2026 08:03:05 -0700 Subject: [PATCH 2050/5214] Docs/admin-guide/mm/damon/usage: mark scheme filters sysfs dir as deprecated Patch series "mm/damon/sysfs: document filters/ directory as deprecated". Commit ab71d2d30121 ("mm/damon/sysfs-schemes: let damon_sysfs_scheme_set_filters() be used for different named directories") introduced alternatives of 'filters' directory, namely core_filters/ and 'ops_filters/ directories. Now the alternatives are well stabilized and ready for all users. All filters/ directory use cases are expected to be able to be migrated to the alternatives. An LTS kernel having the alternatives, namely 6.18.y, is also released. Existence of filters/ directory is only confusing. It would be better not immediately removing the directory, though. There could be users that need time before migrating to the alternatives. There might be unexpected use cases that the alternatives cannot support. Doing the deprecation step by step across multiple years like DAMON debugfs deprecation would be safer. Start the deprecation changes by announcing the deprecation on the documents. Every year, one more action for completely removing the directory will be followed, like DAMON debugfs deprecation did. Following yearly actions are currently expected. In 2027, deprecation warning kernel messages will be printed once, for use of filters/ directory. In 2028, filters/ directory will be renamed to filters_DEPRECATED/. In 2029, filters_DEPRECATED/ directory will be removed. This patch (of 2): The alternatives of 'filters/' directory, namely 'core_filters/' and 'ops_filters/', can fully support all the features 'filters/' directory can do, and provide better user experience. Having 'filters/' directory is only confusing to users. Announce it as deprecated on the usage document. Link: https://lore.kernel.org/20260429150309.82282-1-sj@kernel.org Link: https://lore.kernel.org/20260429150309.82282-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index d5548e460857..11c75a598393 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -485,10 +485,10 @@ directory can be used for installing filters regardless of their handled layers. Filters that requested by ``core_filters`` and ``ops_filters`` will be installed before those of ``filters``. All three directories have same files. -Use of ``filters`` directory can make expecting evaluation orders of given -filters with the files under directory bit confusing. Users are hence -recommended to use ``core_filters`` and ``ops_filters`` directories. The -``filters`` directory could be deprecated in future. +Use of ``filters`` directory can make filters evaluation orders confusing to +expect. For this reason, ``filters`` directory is deprecated. It is still +functioning, but is scheduled for removal in the near future. Users should use +``core_filters`` and ``ops_filters`` directories instead. In the beginning, the directory has only one file, ``nr_filters``. Writing a number (``N``) to the file creates the number of child directories named ``0`` From 4c53a9fdb6f83f261a6e2d433602ed0189408f82 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 29 Apr 2026 08:03:06 -0700 Subject: [PATCH 2051/5214] Docs/ABI/damon: mark schemes//filters/ deprecated Now the 'filters/' directory is deprecated. Update ABI document to also announce the fact. Also update the descriptions of the files to be based on 'core_filter/' directory, to make the old descriptions ready to be removed when the time arrives. Link: https://lore.kernel.org/20260429150309.82282-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .../ABI/testing/sysfs-kernel-mm-damon | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index 971c22e34e72..ee29d4e204ff 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -396,15 +396,20 @@ Contact: SeongJae Park Description: Writing to and reading from this file sets and gets the low watermark of the scheme in permil. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters/nr_filters -Date: Dec 2022 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters +Date: Feb 2025 +Contact: SeongJae Park +Description: Directory for DAMON core layer-handled DAMOS filters. + +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters/nr_filters +Date: Feb 2025 Contact: SeongJae Park Description: Writing a number 'N' to this file creates the number of directories for setting filters of the scheme named '0' to - 'N-1' under the filters/ directory. + 'N-1' under the core_filters/ directory. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//type -Date: Dec 2022 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//type +Date: Feb 2025 Contact: SeongJae Park Description: Writing to and reading from this file sets and gets the type of the memory of the interest. 'anon' for anonymous pages, @@ -412,77 +417,78 @@ Description: Writing to and reading from this file sets and gets the type of 'addr' for address range (an open-ended interval), or 'target' for DAMON monitoring target can be written and read. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//memcg_path -Date: Dec 2022 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//memcg_path +Date: Feb 2025 Contact: SeongJae Park Description: If 'memcg' is written to the 'type' file, writing to and reading from this file sets and gets the path to the memory cgroup of the interest. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//addr_start -Date: Jul 2023 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//addr_start +Date: Feb 2025 Contact: SeongJae Park Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the start address of the address range for the filter. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//addr_end -Date: Jul 2023 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//addr_end +Date: Feb 2025 Contact: SeongJae Park Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the end address of the address range for the filter. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//min +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//min Date: Feb 2025 Contact: SeongJae Park Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the minimum size of the hugepage for the filter. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//max +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//max Date: Feb 2025 Contact: SeongJae Park Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the maximum size of the hugepage for the filter. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//target_idx -Date: Dec 2022 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//target_idx +Date: Feb 2025 Contact: SeongJae Park Description: If 'target' is written to the 'type' file, writing to or reading from this file sets or gets the index of the DAMON monitoring target of the interest. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//matching -Date: Dec 2022 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//matching +Date: Feb 2025 Contact: SeongJae Park Description: Writing 'Y' or 'N' to this file sets whether the filter is for the memory of the 'type', or all except the 'type'. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters//allow -Date: Jan 2025 +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//allow +Date: Feb 2025 Contact: SeongJae Park Description: Writing 'Y' or 'N' to this file sets whether to allow or reject applying the scheme's action to the memory that satisfies the 'type' and the 'matching' of the directory. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters -Date: Feb 2025 -Contact: SeongJae Park -Description: Directory for DAMON core layer-handled DAMOS filters. Files - under this directory works same to those of - /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters - directory. - What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//ops_filters Date: Feb 2025 Contact: SeongJae Park Description: Directory for DAMON operations set layer-handled DAMOS filters. Files under this directory works same to those of - /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters + /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters directory. +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters +Date: Dec 2022 +Contact: SeongJae Park +Description: Directory for DAMOS filters. Files under this directory works + same to those of + /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//{core,ops}_filters + directory. This is deprecated. Use the core_filters and + ops_filters instead. + What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests/nr_dests Date: Jul 2025 Contact: SeongJae Park From 5ebb2064da361ca860c052bca9ae37962adef3f7 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:06 +0000 Subject: [PATCH 2052/5214] mm: use zone lock guard in reserve_highatomic_pageblock() Patch series "mm: use spinlock guards for zone lock", v3. This series uses spinlock guard for zone lock across several mm functions to replace explicit lock/unlock patterns with automatic scope-based cleanup. This simplifies the control flow by removing 'flags' variables, goto labels, and redundant unlock calls. Patches are ordered by decreasing value. The first six patches simplify the control flow by removing gotos, multiple unlock paths, or 'ret' variables. The last two are simpler lock/unlock pair conversions that only remove 'flags' and can be dropped if considered unnecessary churn. Binary size increase is +39 bytes, with Peter Zijlstra's fix for guards [1] applied. This is due to the compiler not being able to deduplicate epilogue and eliminate redundant NULL check. See discussion [2] for more details. I proposed a patch [3] that fixes this, but until it is merged we need to assume +39 bytes will stay (though it is compiler dependent). This patch (of 8): Use the spinlock_irqsave zone lock guard in reserve_highatomic_pageblock() to replace the explicit lock/unlock and goto out_unlock pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/cover.1777462630.git.d@ilvokhin.com Link: https://lore.kernel.org/3657e1144e2ffc1ca0eb57d57d89bfec4073d8c6.1777462630.git.d@ilvokhin.com Link: https://lore.kernel.org/all/20260309164516.GE606826@noisy.programming.kicks-ass.net/ [1] Link: https://lore.kernel.org/all/afC5C6fylF4AsITV@shell.ilvokhin.com/ [2] Link: https://lore.kernel.org/all/20260427165037.205337-1-d@ilvokhin.com/ [3] Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: Johannes Weiner Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Peter Zijlstra Signed-off-by: Andrew Morton --- mm/page_alloc.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d9c6313e69f3..36d37e9ff3b9 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3442,7 +3442,7 @@ static void reserve_highatomic_pageblock(struct page *page, int order, struct zone *zone) { int mt; - unsigned long max_managed, flags; + unsigned long max_managed; /* * The number reserved as: minimum is 1 pageblock, maximum is @@ -3456,29 +3456,26 @@ static void reserve_highatomic_pageblock(struct page *page, int order, if (zone->nr_reserved_highatomic >= max_managed) return; - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); /* Recheck the nr_reserved_highatomic limit under the lock */ if (zone->nr_reserved_highatomic >= max_managed) - goto out_unlock; + return; /* Yoink! */ mt = get_pageblock_migratetype(page); /* Only reserve normal pageblocks (i.e., they can merge with others) */ if (!migratetype_is_mergeable(mt)) - goto out_unlock; + return; if (order < pageblock_order) { if (move_freepages_block(zone, page, mt, MIGRATE_HIGHATOMIC) == -1) - goto out_unlock; + return; zone->nr_reserved_highatomic += pageblock_nr_pages; } else { change_pageblock_range(page, order, MIGRATE_HIGHATOMIC); zone->nr_reserved_highatomic += 1 << order; } - -out_unlock: - spin_unlock_irqrestore(&zone->lock, flags); } /* From 3a92b4e99b7429f98625a08f3dd2aea92754aa99 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:07 +0000 Subject: [PATCH 2053/5214] mm: use zone lock guard in unset_migratetype_isolate() Use spinlock_irqsave zone lock guard in unset_migratetype_isolate() to replace the explicit lock/unlock and goto pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/815c0905ea77828ed32bf56ff0a6d3c6548eb3a2.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Acked-by: Zi Yan Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/page_isolation.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index c48ff5c00244..9d606052dd80 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -223,15 +223,14 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode, static void unset_migratetype_isolate(struct page *page) { struct zone *zone; - unsigned long flags; bool isolated_page = false; unsigned int order; struct page *buddy; zone = page_zone(page); - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); if (!is_migrate_isolate_page(page)) - goto out; + return; /* * Because freepage with more than pageblock_order on isolated @@ -279,8 +278,6 @@ static void unset_migratetype_isolate(struct page *page) __putback_isolated_page(page, order, get_pageblock_migratetype(page)); } zone->nr_isolate_pageblock--; -out: - spin_unlock_irqrestore(&zone->lock, flags); } static inline struct page * From 055526c21e2dc802389435bc684cf17cdf507909 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:08 +0000 Subject: [PATCH 2054/5214] mm: use zone lock guard in unreserve_highatomic_pageblock() Use spinlock_irqsave zone lock guard in unreserve_highatomic_pageblock() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/69db814cd178915cb5615334a29304678f960963.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 36d37e9ff3b9..56ba22e1a816 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3491,7 +3491,6 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, bool force) { struct zonelist *zonelist = ac->zonelist; - unsigned long flags; struct zoneref *z; struct zone *zone; struct page *page; @@ -3508,7 +3507,7 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, pageblock_nr_pages) continue; - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); for (order = 0; order < NR_PAGE_ORDERS; order++) { struct free_area *area = &(zone->free_area[order]); unsigned long size; @@ -3555,12 +3554,9 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * so this should not fail on zone boundaries. */ WARN_ON_ONCE(ret == -1); - if (ret > 0) { - spin_unlock_irqrestore(&zone->lock, flags); + if (ret > 0) return ret; - } } - spin_unlock_irqrestore(&zone->lock, flags); } return false; From feb0df835fde31ba6af7ab2b7b05751cadc97472 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:09 +0000 Subject: [PATCH 2055/5214] mm: use zone lock guard in set_migratetype_isolate() Use spinlock_irqsave scoped lock guard in set_migratetype_isolate() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. The scoped variant is used to keep dump_page() outside the locked section to avoid a lockdep splat. Link: https://lore.kernel.org/6883351ad7f74d20875fff30e0e3214a089cea97.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Acked-by: Zi Yan Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/page_isolation.c | 62 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 9d606052dd80..7a9d631945a3 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -167,48 +167,40 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode, { struct zone *zone = page_zone(page); struct page *unmovable; - unsigned long flags; unsigned long check_unmovable_start, check_unmovable_end; if (PageUnaccepted(page)) accept_page(page); - spin_lock_irqsave(&zone->lock, flags); - - /* - * We assume the caller intended to SET migrate type to isolate. - * If it is already set, then someone else must have raced and - * set it before us. - */ - if (is_migrate_isolate_page(page)) { - spin_unlock_irqrestore(&zone->lock, flags); - return -EBUSY; - } - - /* - * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. - * We just check MOVABLE pages. - * - * Pass the intersection of [start_pfn, end_pfn) and the page's pageblock - * to avoid redundant checks. - */ - check_unmovable_start = max(page_to_pfn(page), start_pfn); - check_unmovable_end = min(pageblock_end_pfn(page_to_pfn(page)), - end_pfn); - - unmovable = has_unmovable_pages(check_unmovable_start, check_unmovable_end, - mode); - if (!unmovable) { - if (!pageblock_isolate_and_move_free_pages(zone, page)) { - spin_unlock_irqrestore(&zone->lock, flags); + scoped_guard(spinlock_irqsave, &zone->lock) { + /* + * We assume the caller intended to SET migrate type to + * isolate. If it is already set, then someone else must have + * raced and set it before us. + */ + if (is_migrate_isolate_page(page)) return -EBUSY; - } - zone->nr_isolate_pageblock++; - spin_unlock_irqrestore(&zone->lock, flags); - return 0; - } - spin_unlock_irqrestore(&zone->lock, flags); + /* + * FIXME: Now, memory hotplug doesn't call shrink_slab() by + * itself. We just check MOVABLE pages. + * + * Pass the intersection of [start_pfn, end_pfn) and the page's + * pageblock to avoid redundant checks. + */ + check_unmovable_start = max(page_to_pfn(page), start_pfn); + check_unmovable_end = min(pageblock_end_pfn(page_to_pfn(page)), + end_pfn); + + unmovable = has_unmovable_pages(check_unmovable_start, + check_unmovable_end, mode); + if (!unmovable) { + if (!pageblock_isolate_and_move_free_pages(zone, page)) + return -EBUSY; + zone->nr_isolate_pageblock++; + return 0; + } + } if (mode == PB_ISOLATE_MODE_MEM_OFFLINE) { /* * printk() with zone->lock held will likely trigger a From ee8a0c15c26f6cf67e4e5207cb18e6262d7e886e Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:10 +0000 Subject: [PATCH 2056/5214] mm: use zone lock guard in take_page_off_buddy() Use spinlock_irqsave zone lock guard in take_page_off_buddy() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. This also allows to return directly from the loop, removing the 'ret' variable. Link: https://lore.kernel.org/a981721632a981f148c63e3f7df3d1116a0c3f6d.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 56ba22e1a816..f5ad74490c5d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7644,11 +7644,9 @@ bool take_page_off_buddy(struct page *page) { struct zone *zone = page_zone(page); unsigned long pfn = page_to_pfn(page); - unsigned long flags; unsigned int order; - bool ret = false; - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); for (order = 0; order < NR_PAGE_ORDERS; order++) { struct page *page_head = page - (pfn & ((1 << order) - 1)); int page_order = buddy_order(page_head); @@ -7663,14 +7661,12 @@ bool take_page_off_buddy(struct page *page) break_down_buddy_pages(zone, page_head, page, 0, page_order, migratetype); SetPageHWPoisonTakenOff(page); - ret = true; - break; + return true; } if (page_count(page_head) > 0) break; } - spin_unlock_irqrestore(&zone->lock, flags); - return ret; + return false; } /* From 5e22451096cd65ded8a7550fb324c8e6dc3b2b22 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:11 +0000 Subject: [PATCH 2057/5214] mm: use zone lock guard in put_page_back_buddy() Use spinlock_irqsave zone lock guard in put_page_back_buddy() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/b0fceedca37139da36aa626ac72eb9840b641021.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f5ad74490c5d..49711916703e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7675,23 +7675,19 @@ bool take_page_off_buddy(struct page *page) bool put_page_back_buddy(struct page *page) { struct zone *zone = page_zone(page); - unsigned long flags; - bool ret = false; - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); if (put_page_testzero(page)) { unsigned long pfn = page_to_pfn(page); int migratetype = get_pfnblock_migratetype(page, pfn); ClearPageHWPoisonTakenOff(page); __free_one_page(page, pfn, zone, 0, migratetype, FPI_NONE); - if (TestClearPageHWPoison(page)) { - ret = true; - } + if (TestClearPageHWPoison(page)) + return true; } - spin_unlock_irqrestore(&zone->lock, flags); - return ret; + return false; } #endif From 5ad64655dde8e5416fc0fff51a189879fe3235fd Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:12 +0000 Subject: [PATCH 2058/5214] mm: use zone lock guard in free_pcppages_bulk() Use spinlock_irqsave zone lock guard in free_pcppages_bulk() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/aafc2d660057a91eb40417f8ff4645b0a8c525e2.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 49711916703e..8835064aaa8c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1469,7 +1469,6 @@ static void free_pcppages_bulk(struct zone *zone, int count, struct per_cpu_pages *pcp, int pindex) { - unsigned long flags; unsigned int order; struct page *page; @@ -1482,7 +1481,7 @@ static void free_pcppages_bulk(struct zone *zone, int count, /* Ensure requested pindex is drained first. */ pindex = pindex - 1; - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); while (count > 0) { struct list_head *list; @@ -1514,8 +1513,6 @@ static void free_pcppages_bulk(struct zone *zone, int count, trace_mm_page_pcpu_drain(page, order, mt); } while (count > 0 && !list_empty(list)); } - - spin_unlock_irqrestore(&zone->lock, flags); } /* Split a multi-block free page into its individual pageblocks. */ From 95b8e432265f61bd9ecdce07d76be6182289ac2a Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 29 Apr 2026 12:02:13 +0000 Subject: [PATCH 2059/5214] mm: use zone lock guard in __offline_isolated_pages() Use spinlock_irqsave zone lock guard in __offline_isolated_pages() to replace the explicit lock/unlock pattern with automatic scope-based cleanup. Link: https://lore.kernel.org/13149be4f8151e18eb5f1eb4f3241ab3cffb373e.1777462630.git.d@ilvokhin.com Signed-off-by: Dmitry Ilvokhin Suggested-by: Steven Rostedt Acked-by: Michal Hocko Cc: Brendan Jackman Cc: David Hildenbrand Cc: Johannes Weiner Cc: Lorenzo Stoakes Cc: Peter Zijlstra Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8835064aaa8c..69a99af77777 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7531,7 +7531,7 @@ void zone_pcp_reset(struct zone *zone) unsigned long __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn) { - unsigned long already_offline = 0, flags; + unsigned long already_offline = 0; unsigned long pfn = start_pfn; struct page *page; struct zone *zone; @@ -7539,7 +7539,7 @@ unsigned long __offline_isolated_pages(unsigned long start_pfn, offline_mem_sections(pfn, end_pfn); zone = page_zone(pfn_to_page(pfn)); - spin_lock_irqsave(&zone->lock, flags); + guard(spinlock_irqsave)(&zone->lock); while (pfn < end_pfn) { page = pfn_to_page(pfn); /* @@ -7569,7 +7569,6 @@ unsigned long __offline_isolated_pages(unsigned long start_pfn, del_page_from_free_list(page, zone, order, MIGRATE_ISOLATE); pfn += (1 << order); } - spin_unlock_irqrestore(&zone->lock, flags); return end_pfn - start_pfn - already_offline; } From 0b9c0aeba938aad9964f855df00bf929b83a484d Mon Sep 17 00:00:00 2001 From: fujunjie Date: Tue, 28 Apr 2026 01:59:43 +0000 Subject: [PATCH 2060/5214] mm/filemap: count only the faulting address as a mmap hit Patch series "mm/filemap: tighten mmap_miss hit accounting", v3. mmap_miss is increased when synchronous mmap readahead is needed, and decreased when filemap_map_pages() maps folios that are already in the page cache. The decrease side can over-credit hits in two cases: - fault-around installs nearby PTEs even though the fault only proves that the faulting address was accessed; - after synchronous mmap readahead returns VM_FAULT_RETRY, the retry can find the folio brought in by the same miss and immediately cancel that miss. Current evidence comes from a local KVM/data-disk microbenchmark using mmap_miss_probe, with an 8 GiB guest, 2 vCPUs, 8192 KiB read_ahead_kb, cold page cache before each run, 1% of the file accessed, and medians of 3 runs. mmap_miss_probe mmap()s a prepared file with MADV_NORMAL and then touches one byte at selected base-page offsets. The access order is random, sequential, or a fixed page stride. The harness drops caches before each run and samples /proc/vmstat around that access loop. The 20 GiB case below is a larger-than-memory file case in an 8 GiB guest. No separate memory hog was used. The 4 GiB case uses the same 8 GiB guest but keeps the file fit-in-memory. Each case used a fresh temporary qcow2 data disk, seen by the guest as /dev/vda, formatted as ext4 and mounted at /mnt/mmap-matrix. Each result is "pgpgin GiB / elapsed seconds". "pgpgin GiB" is the delta of the guest /proc/vmstat pgpgin counter, converted from KiB to GiB; it is used here as an approximate block input counter, not as resident memory or exact application IO. "Elapsed seconds" is the wall-clock runtime of the whole mmap_miss_probe access pass, not per-access latency. For the 20 GiB larger-than-memory case: workload before after random 223.377 GiB/101.293s 1.010 GiB/4.790s stride1021 204.214 GiB/97.557s 204.208 GiB/108.086s stride2053 409.584 GiB/193.700s 0.970 GiB/3.685s stride4099 406.452 GiB/134.241s 0.975 GiB/3.499s sequential 0.212 GiB/0.050s 0.212 GiB/0.057s For the 4 GiB fit-in-memory case: workload before after random 3.987 GiB/1.960s 0.980 GiB/1.221s stride1021 4.002 GiB/1.838s 4.002 GiB/1.851s stride2053 3.991 GiB/1.835s 0.811 GiB/0.985s stride4099 4.001 GiB/1.836s 0.819 GiB/1.037s sequential 0.056 GiB/0.013s 0.056 GiB/0.018s The 20 GiB setup also has an ablation. P1 is only the faulting-address hit accounting change. P2-only is only the FAULT_FLAG_TRIED retry filter. P1+P2 is the combined accounting change: workload variant result random baseline 223.377 GiB/101.293s random P1 223.268 GiB/98.481s random P2-only 223.257 GiB/100.091s random P1+P2 1.010 GiB/4.790s stride2053 baseline 409.584 GiB/193.700s stride2053 P1 409.584 GiB/197.645s stride2053 P2-only 15.722 GiB/5.485s stride2053 P1+P2 0.970 GiB/3.685s sequential baseline 0.212 GiB/0.050s sequential P1 0.212 GiB/0.046s sequential P2-only 0.212 GiB/0.050s sequential P1+P2 0.212 GiB/0.057s After the v2 implementation refactor, only the final P1+P2 shape was rerun in the same setup. The numbers stayed in line with the v1 P1+P2 rows above: workload larger-than-memory case fit-in-memory case 20 GiB file, 1% access 4 GiB file, 1% access random 1.010 GiB/4.383s 0.980 GiB/1.088s stride1021 204.216 GiB/105.601s 4.001 GiB/1.783s stride2053 0.970 GiB/3.760s 0.810 GiB/0.908s stride4099 0.975 GiB/3.410s 0.818 GiB/0.870s sequential 0.212 GiB/0.060s 0.056 GiB/0.016s This does not claim to solve every sparse pattern. The stride1021 rows are intentionally shown as a boundary: with 8192 KiB read_ahead_kb, file->f_ra.ra_pages is 2048 base pages, and synchronous mmap read-around uses a 2048-page window centered around the fault, roughly [index - 1024, index + 1023]. stride1021 is 1021 * 4 KiB = 4084 KiB, so the next access lands inside the previous read-around window. About every other access can be a real faulting-address page-cache hit, and the other half can each read about 8 MiB. For about 52k accesses in the 20 GiB/1% run, half of them times 8 MiB is about 205 GiB, matching the observed 204 GiB. This patch (of 2): filemap_map_pages() reduces file->f_ra.mmap_miss when fault-around maps folios that are already present in the page cache. That hit accounting is too generous because fault-around can install PTEs around the faulting address even though the fault only proves that the faulting address was accessed. Move the mmap_miss update back into filemap_map_pages(), drop the mmap_miss argument from the helper functions, and decrement mmap_miss only when the helper return value shows that the faulting address was mapped. Keep the existing workingset-folio behavior unchanged. Link: https://lore.kernel.org/tencent_AA501E9A238337BD167E5C2ACF948A1AF308@qq.com Link: https://lore.kernel.org/tencent_756F151FE66F3D80479A6F982C0AB8569F09@qq.com Signed-off-by: fujunjie Reviewed-by: Jan Kara Reviewed-by: Vishal Moola Cc: Matthew Wilcox (Oracle) Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/filemap.c | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 97772a05a18e..816eabb22e19 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3751,8 +3751,7 @@ static struct folio *next_uptodate_folio(struct xa_state *xas, static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, struct folio *folio, unsigned long start, unsigned long addr, unsigned int nr_pages, - unsigned long *rss, unsigned short *mmap_miss, - pgoff_t file_end) + unsigned long *rss, pgoff_t file_end) { struct address_space *mapping = folio->mapping; unsigned int ref_from_caller = 1; @@ -3784,16 +3783,6 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, if (PageHWPoison(page + count)) goto skip; - /* - * If there are too many folios that are recently evicted - * in a file, they will probably continue to be evicted. - * In such situation, read-ahead is only a waste of IO. - * Don't decrease mmap_miss in this scenario to make sure - * we can stop read-ahead. - */ - if (!folio_test_workingset(folio)) - (*mmap_miss)++; - /* * NOTE: If there're PTE markers, we'll leave them to be * handled in the specific fault path, and it'll prohibit the @@ -3840,7 +3829,7 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, static vm_fault_t filemap_map_order0_folio(struct vm_fault *vmf, struct folio *folio, unsigned long addr, - unsigned long *rss, unsigned short *mmap_miss) + unsigned long *rss) { vm_fault_t ret = 0; struct page *page = &folio->page; @@ -3848,10 +3837,6 @@ static vm_fault_t filemap_map_order0_folio(struct vm_fault *vmf, if (PageHWPoison(page)) goto out; - /* See comment of filemap_map_folio_range() */ - if (!folio_test_workingset(folio)) - (*mmap_miss)++; - /* * NOTE: If there're PTE markers, we'll leave them to be * handled in the specific fault path, and it'll prohibit @@ -3886,7 +3871,6 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, vm_fault_t ret = 0; unsigned long rss = 0; unsigned int nr_pages = 0, folio_type; - unsigned short mmap_miss = 0, mmap_miss_saved; /* * Recalculate end_pgoff based on file_end before calling @@ -3925,6 +3909,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, folio_type = mm_counter_file(folio); do { unsigned long end; + vm_fault_t map_ret; addr += (xas.xa_index - last_pgoff) << PAGE_SHIFT; vmf->pte += xas.xa_index - last_pgoff; @@ -3932,13 +3917,34 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, end = folio_next_index(folio) - 1; nr_pages = min(end, end_pgoff) - xas.xa_index + 1; - if (!folio_test_large(folio)) - ret |= filemap_map_order0_folio(vmf, - folio, addr, &rss, &mmap_miss); - else - ret |= filemap_map_folio_range(vmf, folio, - xas.xa_index - folio->index, addr, - nr_pages, &rss, &mmap_miss, file_end); + if (!folio_test_large(folio)) { + map_ret = filemap_map_order0_folio(vmf, folio, addr, + &rss); + } else { + unsigned long start = xas.xa_index - folio->index; + + map_ret = filemap_map_folio_range(vmf, folio, start, + addr, nr_pages, &rss, + file_end); + } + ret |= map_ret; + + /* + * If there are too many folios that are recently evicted + * in a file, they will probably continue to be evicted. + * In such situation, read-ahead is only a waste of IO. + * Don't decrease mmap_miss in this scenario to make sure + * we can stop read-ahead. + */ + if ((map_ret & VM_FAULT_NOPAGE) && + !folio_test_workingset(folio)) { + unsigned short mmap_miss; + + mmap_miss = READ_ONCE(file->f_ra.mmap_miss); + if (mmap_miss) + WRITE_ONCE(file->f_ra.mmap_miss, + mmap_miss - 1); + } folio_unlock(folio); } while ((folio = next_uptodate_folio(&xas, mapping, end_pgoff)) != NULL); @@ -3948,12 +3954,6 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, out: rcu_read_unlock(); - mmap_miss_saved = READ_ONCE(file->f_ra.mmap_miss); - if (mmap_miss >= mmap_miss_saved) - WRITE_ONCE(file->f_ra.mmap_miss, 0); - else - WRITE_ONCE(file->f_ra.mmap_miss, mmap_miss_saved - mmap_miss); - return ret; } EXPORT_SYMBOL(filemap_map_pages); From 9b0fcac3cfe7ffeb3da78bfee765072861c81ce2 Mon Sep 17 00:00:00 2001 From: fujunjie Date: Tue, 28 Apr 2026 01:59:44 +0000 Subject: [PATCH 2061/5214] mm/filemap: do not count FAULT_FLAG_TRIED retries as mmap hits A fault that starts synchronous mmap readahead can return VM_FAULT_RETRY after dropping mmap_lock. The retry may then map the folio brought in by that same miss. Do not let this retry decrement mmap_miss. The retry still maps the folio from the page cache; it just does not count as a useful mmap readahead hit. Link: https://lore.kernel.org/tencent_22E6B8849EC1141FE7773C64467E6F1E2C09@qq.com Signed-off-by: fujunjie Reviewed-by: Jan Kara Reviewed-by: Vishal Moola Cc: Matthew Wilcox (Oracle) Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/filemap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/filemap.c b/mm/filemap.c index 816eabb22e19..ab34cab2416a 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3937,6 +3937,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, * we can stop read-ahead. */ if ((map_ret & VM_FAULT_NOPAGE) && + !(vmf->flags & FAULT_FLAG_TRIED) && !folio_test_workingset(folio)) { unsigned short mmap_miss; From ca9caa098f70e25c0edd812a640c6367e711c886 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 1 May 2026 10:20:57 +0800 Subject: [PATCH 2062/5214] selftests/cgroup: fix hardcoded page size in test_percpu_basic Patch series "selftests/cgroup: Fix false positive failures in test_percpu_basic", v2. This patch series addresses two separate issues that cause false positive failures in the test_percpu_basic test within the cgroup kmem selftests. The first issue stems from a hardcoded assumption about the system page size, which breaks the test on architectures with larger page sizes. The second issue is an overly strict memory check that fails to account for the slab metadata allocated during cgroup creation. This patch (of 2): MAX_VMSTAT_ERROR uses a hardcoded page size of 4096, which assumes 4K pages. This causes test_percpu_basic to fail on systems where the kernel is configured with a larger page size, such as aarch64 systems using 16K or 64K pages, where the maximum permissible discrepancy between memory.current and percpu charges is proportionally larger. Replace the hardcoded 4096 with sysconf(_SC_PAGESIZE) to correctly derive the page size at runtime regardless of the underlying architecture or kernel configuration. Link: https://lore.kernel.org/20260501022058.18024-1-li.wang@linux.dev Link: https://lore.kernel.org/20260501022058.18024-2-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Waiman Long Reviewed-by: Sayali Patil Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Shakeel Butt Cc: Tejun Heo Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_kmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index 12f59925500b..69cb1b50988c 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -24,7 +24,7 @@ * the maximum discrepancy between charge and vmstat entries is number * of cpus multiplied by 64 pages. */ -#define MAX_VMSTAT_ERROR (4096 * 64 * get_nprocs()) +#define MAX_VMSTAT_ERROR (sysconf(_SC_PAGESIZE) * 64 * get_nprocs()) #define KMEM_DEAD_WAIT_RETRIES 80 From 5de852b6e7cddc75d7182d5503e54d3ad50d109a Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 1 May 2026 10:20:58 +0800 Subject: [PATCH 2063/5214] selftests/cgroup: include slab in test_percpu_basic memory check test_percpu_basic() currently compares memory.current against only memory.stat:percpu after creating 1000 child cgroups. Observed failure: #./test_kmem ok 1 test_kmem_basic ok 2 test_kmem_memcg_deletion ok 3 test_kmem_proc_kpagecgroup ok 4 test_kmem_kernel_stacks ok 5 test_kmem_dead_cgroups memory.current 11530240 percpu 8440000 not ok 6 test_percpu_basic That assumption is too strict: child cgroup creation also allocates slab-backed metadata, so memory.current is expected to be larger than percpu alone. One visible path is: cgroup_mkdir() cgroup_create() cgroup_addrm_file() cgroup_add_file() __kernfs_create_file() __kernfs_new_node() kmem_cache_zalloc() These kernfs allocations are charged as slab and show up in memory.stat:slab. Update the check to compare memory.current against (percpu + slab) within MAX_VMSTAT_ERROR, and print slab/delta in the failure message to improve diagnostics. Link: https://lore.kernel.org/20260501022058.18024-3-li.wang@linux.dev Signed-off-by: Li Wang Reviewed-by: Waiman Long Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Shakeel Butt Cc: Tejun Heo Cc: Vlastimil Babka Cc: Sayali Patil Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_kmem.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index 69cb1b50988c..1db0ba1226b9 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -353,7 +353,7 @@ static int test_percpu_basic(const char *root) { int ret = KSFT_FAIL; char *parent, *child; - long current, percpu; + long current, percpu, slab; int i; parent = cg_name(root, "percpu_basic_test"); @@ -383,13 +383,14 @@ static int test_percpu_basic(const char *root) current = cg_read_long(parent, "memory.current"); percpu = cg_read_key_long(parent, "memory.stat", "percpu "); + slab = cg_read_key_long(parent, "memory.stat", "slab "); - if (current > 0 && percpu > 0 && labs(current - percpu) < - MAX_VMSTAT_ERROR) + if (current > 0 && percpu > 0 && slab >= 0 && + labs(current - (percpu + slab)) < MAX_VMSTAT_ERROR) ret = KSFT_PASS; else - printf("memory.current %ld\npercpu %ld\n", - current, percpu); + printf("memory.current %ld\npercpu %ld\nslab %ld\ndelta %ld\n", + current, percpu, slab, current - (percpu + slab)); cleanup_children: for (i = 0; i < 1000; i++) { From 0453f857eb32c11d8cc48988911fc5905d054319 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 30 Apr 2026 18:17:38 -0700 Subject: [PATCH 2064/5214] mm/damon/reclaim: add autotune_monitoring_intervals parameter Patch series "mm/damon/reclaim: support monitoring intervals auto-tuning". The monitoring intervals auto-tuning feature of DAMON has proven to be useful in multiple environments. Add a new DAMON_RECLAIM parameter for supporting the feature, and update the document for the new parameter. This patch (of 2): DAMON's monitoring intervals auto-tuning feature has proven to be useful in multiple environments. DAMON_RECLAIM is still asking users to do the manual tuning of the intervals. Add a module parameter for utilizing the auto-tuning feature with the suggested default setup. Note that use of the auto-tuning overrides the manually entered monitoring intervals. Also, note that the 'min_age' will dynamically changed proportional to auto-tuned intervals. It is recommended to use 'min_age' short enough and use 'quota_mem_pressure_us' like coldness threshold auto-tuning features together. Link: https://lore.kernel.org/20260501011740.81988-1-sj@kernel.org Link: https://lore.kernel.org/20260501011740.81988-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/reclaim.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index a60ee800d63e..7126d47fb8b2 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -91,6 +91,20 @@ module_param(quota_mem_pressure_us, ulong, 0600); static unsigned long quota_autotune_feedback __read_mostly; module_param(quota_autotune_feedback, ulong, 0600); +/* + * Auto-tune monitoring intervals. + * + * If this parameter is set as ``Y``, DAMON_RECLAIM automatically tunes DAMON's + * sampling and aggregation intervals. The auto-tuning aims to capture + * meaningful amount of access events in each DAMON-snapshot, while keeping the + * sampling intervals 5 milliseconds in minimum, and 10 seconds in maximum. + * Setting this as ``N`` disables the auto-tuning. + * + * Disabled by default. + */ +static bool autotune_monitoring_intervals __read_mostly; +module_param(autotune_monitoring_intervals, bool, 0600); + static struct damos_watermarks damon_reclaim_wmarks = { .metric = DAMOS_WMARK_FREE_MEM_RATE, .interval = 5000000, /* 5 seconds */ @@ -152,7 +166,7 @@ DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_reclaim_stat, static struct damon_ctx *ctx; static struct damon_target *target; -static struct damos *damon_reclaim_new_scheme(void) +static struct damos *damon_reclaim_new_scheme(unsigned long aggr_interval) { struct damos_access_pattern pattern = { /* Find regions having PAGE_SIZE or larger size */ @@ -162,8 +176,7 @@ static struct damos *damon_reclaim_new_scheme(void) .min_nr_accesses = 0, .max_nr_accesses = 0, /* for min_age or more micro-seconds */ - .min_age_region = min_age / - damon_reclaim_mon_attrs.aggr_interval, + .min_age_region = min_age / aggr_interval, .max_age_region = UINT_MAX, }; @@ -184,6 +197,7 @@ static int damon_reclaim_apply_parameters(void) { struct damon_ctx *param_ctx; struct damon_target *param_target; + struct damon_attrs attrs; struct damos *scheme; struct damos_quota_goal *goal; struct damos_filter *filter; @@ -201,12 +215,21 @@ static int damon_reclaim_apply_parameters(void) goto out; } - err = damon_set_attrs(param_ctx, &damon_reclaim_mon_attrs); + attrs = damon_reclaim_mon_attrs; + if (autotune_monitoring_intervals) { + attrs.sample_interval = 5000; + attrs.aggr_interval = 100000; + attrs.intervals_goal.access_bp = 40; + attrs.intervals_goal.aggrs = 3; + attrs.intervals_goal.min_sample_us = 5000; + attrs.intervals_goal.max_sample_us = 10 * 1000 * 1000; + } + err = damon_set_attrs(param_ctx, &attrs); if (err) goto out; err = -ENOMEM; - scheme = damon_reclaim_new_scheme(); + scheme = damon_reclaim_new_scheme(attrs.aggr_interval); if (!scheme) goto out; damon_set_schemes(param_ctx, &scheme, 1); From 1794454a3bf66974f806301fa2952aed719780fb Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 30 Apr 2026 18:17:39 -0700 Subject: [PATCH 2065/5214] Docs/admin-guide/mm/damon/reclaim: update for autotune_monitoring_intervals Update DAMON_RECLAIM usage document for the newly added monitoring intervals auto-tuning enablement parameter. Link: https://lore.kernel.org/20260501011740.81988-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/reclaim.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/admin-guide/mm/damon/reclaim.rst b/Documentation/admin-guide/mm/damon/reclaim.rst index 57ab8b187650..ec7e3e32b4ac 100644 --- a/Documentation/admin-guide/mm/damon/reclaim.rst +++ b/Documentation/admin-guide/mm/damon/reclaim.rst @@ -85,6 +85,17 @@ identifies the region as cold, and reclaims it. 120 seconds by default. +autotune_monitoring_intervals +----------------------------- + +If this parameter is set as ``Y``, DAMON_RECLAIM automatically tunes DAMON's +sampling and aggregation intervals. The auto-tuning aims to capture meaningful +amount of access events in each DAMON-snapshot, while keeping the sampling +interval 5 milliseconds in minimum, and 10 seconds in maximum. Setting this as +``N`` disables the auto-tuning. + +Disabled by default. + quota_ms -------- From 3a0bc9568c354357546557d8b969785bc27fd260 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 1 May 2026 19:05:03 -0700 Subject: [PATCH 2066/5214] mm/damon/stat: add a parameter for reading kdamond pid Patch series "mm/damon/stat: add kdamond_pid parameter". DAMON_STAT doesn't provide the pid of its kdamond, unlike DAMON_RECLAIM and DAMON_LRU_SORT. This makes user-space management of DAMON_STAT unnecessarily complicated. Provide the information via a new parameter, namely kdamond_pid, and document it. This patch (of 2): Knowing the pid of the kdamonds can help user-space management including monitoring of DAMON's system resource consumption. To make it easier, DAMON_SYSFS, DAMON_RECLAIM and DAMON_LRU_SORT provide the pid information. DAMON_STAT is not providing it, though. Expose the pid of DAMON_STAT kdamond via a new read-only module parameter, namely kdamond_pid. This also makes DAMON modules usage more standardized, because DAMON_RECLAIM and DAMON_LRU_SORT also provide the information via their read-only parameters of the same name. Link: https://lore.kernel.org/20260502020505.80822-1-sj@kernel.org Link: https://lore.kernel.org/20260502020505.80822-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/stat.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/mm/damon/stat.c b/mm/damon/stat.c index f4d3203e9263..0e14f5bb8f75 100644 --- a/mm/damon/stat.c +++ b/mm/damon/stat.c @@ -266,6 +266,45 @@ static int damon_stat_enabled_load(char *buffer, const struct kernel_param *kp) return sprintf(buffer, "%c\n", damon_stat_enabled() ? 'Y' : 'N'); } +static int damon_stat_kdamond_pid_store( + const char *val, const struct kernel_param *kp) +{ + /* + * kdamond_pid is read-only, but kernel command line could write it. + * Do nothing here. + */ + return 0; +} + +static int damon_stat_kdamond_pid_load( + char *buffer, const struct kernel_param *kp) +{ + int pid; + + if (!damon_stat_context) { + pid = -1; + } else { + pid = damon_kdamond_pid(damon_stat_context); + if (pid < 1) + pid = -1; + } + return sprintf(buffer, "%d\n", pid); +} + +static const struct kernel_param_ops kdamond_pid_param_ops = { + .set = damon_stat_kdamond_pid_store, + .get = damon_stat_kdamond_pid_load, +}; + +/* + * PID of the DAMON thread + * + * If DAMON_STAT is enabled, this becomes the PID of the worker thread. + * Else, -1. + */ +module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400); +MODULE_PARM_DESC(kdamond_pid, "pid of the kdamond"); + static int __init damon_stat_init(void) { int err = 0; From f27d56b4f2aa0ffeda7113df3443448bc907acaf Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 1 May 2026 19:05:04 -0700 Subject: [PATCH 2067/5214] Docs/admin-guide/mm/damon/stat: document kdamond_pid parameter Update DAMON_STAT usage document for newly added kdamond_pid parameter. Link: https://lore.kernel.org/20260502020505.80822-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/stat.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/admin-guide/mm/damon/stat.rst b/Documentation/admin-guide/mm/damon/stat.rst index c4b14daeb2dd..46c5dd96aa2e 100644 --- a/Documentation/admin-guide/mm/damon/stat.rst +++ b/Documentation/admin-guide/mm/damon/stat.rst @@ -89,3 +89,10 @@ percentiles of the idle time values via this read-only parameter. Reading the parameter returns 101 idle time values in milliseconds, separated by comma. Each value represents 0-th, 1st, 2nd, 3rd, ..., 99th and 100th percentile idle times. + +kdamond_pid +----------- + +PID of the DAMON thread. + +If DAMON_STAT is enabled, this becomes the PID of the worker thread. Else, -1. From 9d5685286aa8c5ef70d8e1a34cef5daf518ae237 Mon Sep 17 00:00:00 2001 From: Zhouyi Zhou Date: Tue, 5 May 2026 02:11:25 +0000 Subject: [PATCH 2068/5214] highmem-internal.h: fix typo in the comment for kunmap_atomic() Replace `PREEMP_RT` with `PREEMPT_RT` in the header comment to match the correct kernel configuration name. Link: https://lore.kernel.org/20260505021125.1941691-1-zhouzhouyi@gmail.com Signed-off-by: Zhouyi Zhou Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Andrew Morton --- include/linux/highmem-internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/highmem-internal.h b/include/linux/highmem-internal.h index 0574c21ca45d..bb71e7dba4f7 100644 --- a/include/linux/highmem-internal.h +++ b/include/linux/highmem-internal.h @@ -262,7 +262,7 @@ static inline bool is_kmap_addr(const void *x) * @__addr: Virtual address to be unmapped * * Unmaps an address previously mapped by kmap_atomic() and re-enables - * pagefaults. Depending on PREEMP_RT configuration, re-enables also + * pagefaults. Depending on PREEMPT_RT configuration, re-enables also * migration and preemption. Users should not count on these side effects. * * Mappings should be unmapped in the reverse order that they were mapped. From 66366d291f666ddeda5f8c84f253e308de3e6b55 Mon Sep 17 00:00:00 2001 From: Zijiang Huang Date: Wed, 6 May 2026 21:09:19 +0800 Subject: [PATCH 2069/5214] mm/swap: add cond_resched() in swap_reclaim_full_clusters to prevent softlockup We hit a real softlockup in an internal stress test environment. The workload was LTP memory/swap stress on a large arm64 machine, with 320 CPUs, about 1TB memory and an 8.6GB swap device. The system was under heavy load and the swap device had a large number of full clusters. The softlockup was triggered during a stress test after about 3 days. So, add periodic cond_resched() calls during large full_clusters reclaim operations to prevent softlockup issues. Detailed call trace as follow: PID: 3817773 TASK: ffff0883bb28b780 CPU: 48 COMMAND: "kworker/48:7" #0 [ffff800080183d10] __crash_kexec at ffffa4c1361e5de4 #1 [ffff800080183d90] panic at ffffa4c1360d5e9c #2 [ffff800080183e20] watchdog_timer_fn at ffffa4c136231fa8 ... #16 [ffff8000c4ad3cb0] swap_cache_del_folio at ffffa4c1363e1614 #17 [ffff8000c4ad3ce0] __try_to_reclaim_swap at ffffa4c1363e4bfc #18 [ffff8000c4ad3d40] swap_reclaim_full_clusters at ffffa4c1363e5474 #19 [ffff8000c4ad3da0] swap_reclaim_work at ffffa4c1363e550c #20 [ffff8000c4ad3dc0] process_one_work at ffffa4c136102edc #21 [ffff8000c4ad3e10] worker_thread at ffffa4c136103398 #22 [ffff8000c4ad3e70] kthread at ffffa4c13610d95c Link: https://lore.kernel.org/20260506130919.2298807-1-kerayhuang@tencent.com Fixes: 5168a68eb78f ("mm, swap: avoid over reclaim of full clusters") Signed-off-by: Zijiang Huang Reviewed-by: Kairui Song Reviewed-by: Hao Peng Reviewed-by: albinwyang Reviewed-by: Baoquan He Acked-by: Chris Li Cc: Barry Song Cc: Kairui Song Cc: Kemeng Shi Cc: Nhat Pham Cc: Youngjun Park Cc: Signed-off-by: Andrew Morton --- mm/swapfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/swapfile.c b/mm/swapfile.c index 9174f1eeffb0..74a1e324449d 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1054,6 +1054,7 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force) swap_cluster_unlock(ci); if (to_scan <= 0) break; + cond_resched(); } } From 77d100d11c87e62010fe65a9a4d117ca0a05f8d0 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 6 May 2026 05:58:24 -0700 Subject: [PATCH 2070/5214] mm/kmemleak: dedupe verbose scan output by allocation backtrace Patch series "mm/kmemleak: dedupe verbose scan output", v3. I am starting to run with kmemleak in verbose enabled in some "probe points" across the my employers fleet so that suspected leaks land in dmesg without needing a separate read of /sys/kernel/debug/kmemleak. The downside is that workloads which leak many objects from a single allocation site flood the console with byte-for-byte identical backtraces. Hundreds of duplicates per scan are common, drowning out distinct leaks and unrelated kernel messages, while adding no signal beyond the first occurrence. This series collapses those duplicates inside kmemleak itself. Each unique stackdepot trace_handle prints once per scan, followed by a short summary line when more than one object shares it: kmemleak: unreferenced object 0xff110001083beb00 (size 192): kmemleak: comm "modprobe", pid 974, jiffies 4294754196 kmemleak: ... kmemleak: backtrace (crc 6f361828): kmemleak: __kmalloc_cache_noprof+0x1af/0x650 kmemleak: ... kmemleak: ... and 71 more object(s) with the same backtrace The "N new suspected memory leaks" tally and the contents of /sys/kernel/debug/kmemleak are unchanged - the per-object detail is still available on demand, only the verbose (dmesg) output is collapsed. Patch 1 is the kmemleak change. Patch 2 adds a selftest that loads samples/kmemleak's CONFIG_SAMPLE kmemleak-test module to generate ten leaks sharing one call site and checks that the printed count is strictly less than the reported leak total. Not sure if Patch 2 is useful or not, if not, it is easier to discard. This patch (of 2): In kmemleak's verbose mode, every unreferenced object found during a scan is logged with its full header, hex dump and 16-frame backtrace. Workloads that leak many objects from a single allocation site flood dmesg with byte-for-byte identical backtraces, drowning out distinct leaks and other kernel messages. Dedupe within each scan using stackdepot's trace_handle as the key: for every leaked object with a recorded stack trace, look up the representative kmemleak_object in a per-scan xarray keyed by trace_handle. The first sighting stores the object pointer (with a get_object() reference) and sets object->dup_count to 1; later sightings just bump dup_count on the representative. After the scan, walk the xarray once and emit each unique backtrace, followed by a single summary line when more than one object shares it. Leaks whose trace_handle is 0 (early-boot allocations tracked before kmemleak_init() set up object_cache, or stack_depot_save() failures under memory pressure) cannot be deduped, so they are still printed inline via the same locked OBJECT_ALLOCATED-checked helper. The contents of /sys/kernel/debug/kmemleak are unchanged - only the verbose console output is collapsed. Safety notes: - The xarray store happens outside object->lock: object->lock is a raw spinlock, while xa_store() may grab xa_node slab locks at a higher wait-context level which lockdep flags as invalid. trace_handle is captured under object->lock (which serialises with kmemleak_update_trace()'s writer), so it is safe to use after dropping the lock. - get_object() pins the kmemleak_object metadata across rcu_read_unlock(), but the underlying tracked allocation can still be freed concurrently. The deferred print path therefore re-acquires object->lock and re-checks OBJECT_ALLOCATED via print_leak_locked() before touching object->pointer; __delete_object() clears that flag under the same lock before the user memory goes away. The same helper is used by the trace_handle == 0 and xa_store() failure fallbacks, so every printer in the new path has identical safety guarantees. - If get_object() fails after we set OBJECT_REPORTED, the object is already being torn down (use_count hit zero); the leak count is still accurate but the verbose line is dropped, which is correct - the memory was freed concurrently and is no longer a leak. - If xa_store() fails to allocate an xa_node under memory pressure, we fall back to printing inline via print_leak_locked() instead of silently dropping the leak. - The hex dump is skipped for coalesced entries (dup_count > 1): bytes would differ across objects sharing a backtrace anyway, and skipping it removes the only remaining read of object->pointer's contents in the deferred path. The representative's reported size may also differ from the coalesced objects' sizes; the printed trace_handle reflects the representative's current value rather than the value used as the dedup key, which is normally - but not strictly - identical. Link: https://lore.kernel.org/20260506-kmemleak_dedup-v3-0-2d36aafc34da@debian.org Link: https://lore.kernel.org/20260506-kmemleak_dedup-v3-1-2d36aafc34da@debian.org Signed-off-by: Breno Leitao Reviewed-by: Catalin Marinas Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/kmemleak.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 8 deletions(-) diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 2eff0d6b622b..7c7ba17ce7af 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -92,6 +92,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,8 @@ struct kmemleak_object { struct hlist_head area_list; unsigned long jiffies; /* creation timestamp */ pid_t pid; /* pid of the current task */ + /* per-scan dedup count, valid only while in scan-local dedup xarray */ + unsigned int dup_count; char comm[TASK_COMM_LEN]; /* executable name */ }; @@ -360,8 +363,9 @@ static const char *__object_type_str(struct kmemleak_object *object) * Printing of the unreferenced objects information to the seq file. The * print_unreferenced function must be called with the object->lock held. */ -static void print_unreferenced(struct seq_file *seq, - struct kmemleak_object *object) +static void __print_unreferenced(struct seq_file *seq, + struct kmemleak_object *object, + bool hex_dump) { int i; unsigned long *entries; @@ -373,7 +377,8 @@ static void print_unreferenced(struct seq_file *seq, object->pointer, object->size); warn_or_seq_printf(seq, " comm \"%s\", pid %d, jiffies %lu\n", object->comm, object->pid, object->jiffies); - hex_dump_object(seq, object); + if (hex_dump) + hex_dump_object(seq, object); warn_or_seq_printf(seq, " backtrace (crc %x):\n", object->checksum); for (i = 0; i < nr_entries; i++) { @@ -382,6 +387,12 @@ static void print_unreferenced(struct seq_file *seq, } } +static void print_unreferenced(struct seq_file *seq, + struct kmemleak_object *object) +{ + __print_unreferenced(seq, object, true); +} + /* * Print the kmemleak_object information. This function is used mainly for * debugging special cases when kmemleak operations. It must be called with @@ -1684,6 +1695,103 @@ static void kmemleak_cond_resched(struct kmemleak_object *object) put_object(object); } +/* + * Print one leak inline. The hex dump is gated on OBJECT_ALLOCATED so it + * does not touch user memory that was freed concurrently; the rest of the + * report (backtrace, comm, pid) is always emitted since the kmemleak_object + * metadata is pinned by the caller. + */ +static void print_leak_locked(struct kmemleak_object *object, bool hex_dump) +{ + raw_spin_lock_irq(&object->lock); + __print_unreferenced(NULL, object, + hex_dump && (object->flags & OBJECT_ALLOCATED)); + raw_spin_unlock_irq(&object->lock); +} + +/* + * Per-scan dedup table for verbose leak printing. The xarray is keyed by + * stackdepot trace_handle and stores a pointer to the representative + * kmemleak_object. The per-scan repeat count lives in object->dup_count. + * + * dedup_record() must run outside object->lock: xa_store() may take + * mutexes (xa_node slab allocation) which lockdep would flag against the + * raw spinlock object->lock. + */ +static void dedup_record(struct xarray *dedup, struct kmemleak_object *object, + depot_stack_handle_t trace_handle) +{ + struct kmemleak_object *rep; + void *old; + + /* + * No stack trace to dedup against: early-boot allocation tracked + * before kmemleak_init() set up object_cache, or stack_depot_save() + * failure under memory pressure. + */ + if (!trace_handle) { + print_leak_locked(object, true); + return; + } + + /* stack is available, now we can de-dup */ + rep = xa_load(dedup, trace_handle); + if (rep) { + rep->dup_count++; + return; + } + + /* + * Object is being torn down (use_count already hit zero); the + * tracked memory at object->pointer is unsafe to read, so skip. + */ + if (!get_object(object)) + return; + + object->dup_count = 1; + old = xa_store(dedup, trace_handle, object, GFP_ATOMIC); + if (xa_is_err(old)) { + /* xa_node allocation failed; fall back to inline print. */ + print_leak_locked(object, true); + put_object(object); + return; + } + /* + * scan_mutex serialises all writers to the dedup xarray, so xa_store() + * after a NULL xa_load() must always overwrite an empty slot. + */ + WARN_ON_ONCE(old); +} + +/* + * Drain the dedup table. Re-acquires object->lock and re-checks + * OBJECT_ALLOCATED before printing: while get_object() pins the + * kmemleak_object metadata, the underlying tracked allocation may have + * been freed since the scan walked it (kmemleak_free clears + * OBJECT_ALLOCATED under object->lock before the user memory goes away). + * The hex dump is skipped for coalesced entries since the bytes would + * differ across objects anyway. + */ +static void dedup_flush(struct xarray *dedup) +{ + struct kmemleak_object *object; + unsigned long idx; + unsigned int dup; + bool coalesced; + + xa_for_each(dedup, idx, object) { + dup = object->dup_count; + coalesced = dup > 1; + + print_leak_locked(object, !coalesced); + if (coalesced) + pr_warn(" ... and %u more object(s) with the same backtrace\n", + dup - 1); + put_object(object); + xa_erase(dedup, idx); + } +} + /* * Scan data sections and all the referenced memory blocks allocated via the * kernel's standard allocators. This function must be called with the @@ -1694,6 +1802,7 @@ static void kmemleak_scan(void) struct kmemleak_object *object; struct zone *zone; int __maybe_unused i; + struct xarray dedup; int new_leaks = 0; jiffies_last_scan = jiffies; @@ -1834,10 +1943,18 @@ static void kmemleak_scan(void) return; /* - * Scanning result reporting. + * Scanning result reporting. When verbose printing is enabled, dedupe + * by stackdepot trace_handle so each unique backtrace is logged once + * per scan, annotated with the number of objects that share it. The + * per-leak count below still reflects every object, and + * /sys/kernel/debug/kmemleak still lists them individually. */ + xa_init(&dedup); rcu_read_lock(); list_for_each_entry_rcu(object, &object_list, object_list) { + depot_stack_handle_t trace_handle; + bool dedup_print; + if (need_resched()) kmemleak_cond_resched(object); @@ -1849,18 +1966,33 @@ static void kmemleak_scan(void) if (!color_white(object)) continue; raw_spin_lock_irq(&object->lock); + trace_handle = 0; + dedup_print = false; if (unreferenced_object(object) && !(object->flags & OBJECT_REPORTED)) { object->flags |= OBJECT_REPORTED; - - if (kmemleak_verbose) - print_unreferenced(NULL, object); - + if (kmemleak_verbose) { + trace_handle = object->trace_handle; + dedup_print = true; + } new_leaks++; } raw_spin_unlock_irq(&object->lock); + + /* + * Defer the verbose print outside object->lock: xa_store() + * may take xa_node slab locks at a higher wait-context level + * which lockdep would flag against the raw_spinlock_t + * object->lock. rcu_read_lock() keeps the kmemleak_object + * alive across the call. + */ + if (dedup_print) + dedup_record(&dedup, object, trace_handle); } rcu_read_unlock(); + /* Flush'em all */ + dedup_flush(&dedup); + xa_destroy(&dedup); if (new_leaks) { kmemleak_found_leaks = true; From cfaef29c20e86738aec28641b6de1e078298999e Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 6 May 2026 05:58:25 -0700 Subject: [PATCH 2071/5214] selftests/mm: add kmemleak verbose dedup test Add a regression test for the per-scan verbose dedup added in the preceding commit. The test loads samples/kmemleak's helper module (CONFIG_SAMPLE_KMEMLEAK=m) to generate orphan allocations, several of which share an allocation backtrace, runs four kmemleak scans with verbose printing enabled, then walks dmesg looking for two "unreferenced object" reports within a single scan that share an identical backtrace - which would mean dedup failed to collapse them. The test is intentionally permissive on detection but strict on regressions: - PASS when no duplicates are observed, regardless of whether the dedup summary line ("... and N more object(s) with the same backtrace") was actually emitted. Per-CPU chunk reuse, slab freelist pointers, kernel stack residue and CONFIG_DEBUG_KMEMLEAK_ AUTO_SCAN can all keep most of the orphans "still referenced" or reported across many separate scans, so the dedup path may have nothing to fold within one scan. That is not a regression. - PASS reports whether dedup actually fired, so a passing run on a well-behaved environment is still informative. - FAIL when two same-backtrace reports land in a single scan (clear dedup regression). - FAIL when kmemleak's own per-scan tally counts leaks but the verbose path emits zero "unreferenced object" lines - that catches a regression in the verbose printer itself, which would otherwise pass the duplicate check trivially. - SKIP when kmemleak is absent, disabled at runtime, or the helper module is not built. The dmesg parser anchors stack-frame matching to the indentation kmemleak uses for them (4+ spaces under "kmemleak: ") so unrelated kmemleak warnings landing between reports do not get lumped into the backtrace key and mask a duplicate. Link: https://lore.kernel.org/20260506-kmemleak_dedup-v3-2-2d36aafc34da@debian.org Signed-off-by: Breno Leitao Cc: Catalin Marinas Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 1 + .../selftests/mm/ksft_kmemleak_dedup.sh | 222 ++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100755 tools/testing/selftests/mm/ksft_kmemleak_dedup.sh diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 18779045b7f6..41053fdaad88 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -151,6 +151,7 @@ TEST_PROGS += ksft_gup_test.sh TEST_PROGS += ksft_hmm.sh TEST_PROGS += ksft_hugetlb.sh TEST_PROGS += ksft_hugevm.sh +TEST_PROGS += ksft_kmemleak_dedup.sh TEST_PROGS += ksft_ksm.sh TEST_PROGS += ksft_ksm_numa.sh TEST_PROGS += ksft_madv_guard.sh diff --git a/tools/testing/selftests/mm/ksft_kmemleak_dedup.sh b/tools/testing/selftests/mm/ksft_kmemleak_dedup.sh new file mode 100755 index 000000000000..d01950244490 --- /dev/null +++ b/tools/testing/selftests/mm/ksft_kmemleak_dedup.sh @@ -0,0 +1,222 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Regression test for kmemleak's per-scan verbose dedup. +# +# Loads samples/kmemleak's helper module to generate orphan allocations +# (some of which share an allocation backtrace), runs a few kmemleak +# scans with verbose printing enabled, and verifies that no two +# "unreferenced object" reports within a single scan share the same +# backtrace - which would mean dedup failed to collapse them. +# +# This test is intentionally permissive: the kmemleak-test module's +# leaks frequently get reported across many separate scans (per-CPU +# chunk reuse, slab freelist pointers, kernel stack residue), so dedup +# may never have anything to fold within one scan. That is not a +# regression. The test only fails when it actually catches dedup not +# happening on input that should have triggered it - i.e. two reports +# with identical backtraces in the same scan. +# +# Author: Breno Leitao + +ksft_skip=4 +KMEMLEAK=/sys/kernel/debug/kmemleak +VERBOSE_PARAM=/sys/module/kmemleak/parameters/verbose +MODULE=kmemleak-test + +skip() { + echo "SKIP: $*" + exit $ksft_skip +} + +fail() { + echo "FAIL: $*" + exit 1 +} + +pass() { + echo "PASS: $*" + exit 0 +} + +[ "$(id -u)" -eq 0 ] || skip "must run as root" +[ -r "$KMEMLEAK" ] || skip "no kmemleak debugfs (CONFIG_DEBUG_KMEMLEAK)" +[ -w "$VERBOSE_PARAM" ] || skip "kmemleak verbose param missing" +modinfo "$MODULE" >/dev/null 2>&1 || + skip "$MODULE not built (CONFIG_SAMPLE_KMEMLEAK)" + +# The verdict depends entirely on dmesg contents, so a silently-empty +# dmesg (dmesg_restrict=1 with CAP_SYSLOG dropped, restricted container, +# etc.) would let the script report PASS without parsing anything. Probe +# both read and clear up front and skip cleanly if either is denied. +dmesg >/dev/null 2>&1 || + skip "cannot read dmesg (need CAP_SYSLOG or dmesg_restrict=0)" +dmesg -C >/dev/null 2>&1 || + skip "cannot clear dmesg (need CAP_SYSLOG or dmesg_restrict=0)" + +# kmemleak can be present but disabled at runtime (boot arg kmemleak=off, +# or it self-disabled after an internal error). In that state writes other +# than "clear" return EPERM, so probe once and skip if so. +if ! echo scan > "$KMEMLEAK" 2>/dev/null; then + skip "kmemleak is disabled (check dmesg or kmemleak= boot arg)" +fi + +prev_verbose=$(cat "$VERBOSE_PARAM") +# shellcheck disable=SC2317 # invoked indirectly via trap +cleanup() { + echo "$prev_verbose" > "$VERBOSE_PARAM" 2>/dev/null + rmmod "$MODULE" 2>/dev/null + # Drain the leak set we generated. Subsequent selftests (e.g. + # tools/testing/selftests/net/netfilter/nft_interface_stress.sh) + # fail on any non-empty kmemleak report, so leaving the helper + # module's intentional leaks behind would poison the rest of a + # kselftest run. + # + # Caveat: kmemleak_clear() only greys objects that have already + # been reported (OBJECT_REPORTED && unreferenced_object()). Helper + # allocations that stayed "still referenced" throughout the test + # (stale pointers in per-CPU chunks, slab freelists, kernel stacks) + # were never reported and are therefore not greyed by this clear - + # they remain tracked and a later scan can still surface them. Such + # leftovers are inherent to the kmemleak-test sample module and are + # not specific to this test; consumers that fail on any kmemleak + # output (rather than on the test-specific backtraces) need to be + # robust to that, or this test should be excluded from the run. + echo clear > "$KMEMLEAK" 2>/dev/null +} +trap cleanup EXIT + +echo 1 > "$VERBOSE_PARAM" + +# Drain the existing leak set so the next scan only reports our objects. +echo clear > "$KMEMLEAK" + +# Re-clear dmesg now (the up-front probe also cleared it, but anything +# logged between then and here - module unload chatter, the probe scan, +# the verbose-param write - would otherwise pollute the parse window). +dmesg -C >/dev/null + +# If the module was left loaded by a previous aborted run, modprobe would +# be a no-op and the init function would not run, so no new leaks would be +# generated. Force a clean state first. +rmmod "$MODULE" 2>/dev/null +modprobe "$MODULE" || skip "failed to load $MODULE" +# Removing the module orphans the list elements without freeing them. +rmmod "$MODULE" || skip "failed to unload $MODULE" + +# Run a handful of scans so kmemleak has the chance to age and report +# the orphans. We do not require any particular number to be reported: +# the regression check below operates on whatever lands in dmesg. +# +# Note: with CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=y the kernel's own scan +# thread can report and mark these orphans (OBJECT_REPORTED) before our +# manual scans run, after which our scans will see nothing. The +# lower-bound check below catches the case where that happens and the +# manual scans also produce nothing. +SCAN_COUNT=4 +SCAN_SLEEP=6 +for _ in $(seq 1 "$SCAN_COUNT"); do + echo scan > "$KMEMLEAK" + sleep "$SCAN_SLEEP" +done + +# Strip the leading "[ nnn.nnnnnn] " dmesg timestamp prefix. Without +# this, two identical stack frames printed from two reports in the same +# scan would produce different per-frame strings (different timestamps) +# and the duplicate-backtrace check below would not match them, silently +# passing a real dedup regression. Doing the strip here makes the rest +# of the parser timestamp-agnostic regardless of what dmesg defaults to. +log=$(dmesg | sed 's/^\[[^]]*\] //') + +# After running the workload (modprobe + scans), dmesg should contain at +# least the helper module's pr_info lines and our manual-scan output. An +# empty capture here means dmesg succeeded earlier but is now denying us +# the buffer (race with dmesg_restrict toggling, etc.); refuse to give a +# verdict on no evidence. +[ -n "$log" ] || skip "dmesg returned empty after running workload" + +# Lower bound: if kmemleak's own per-scan tally counted leaks but the +# verbose path emitted no "unreferenced object" line, the verbose printer +# itself is regressed - fail rather than silently passing on no input. +new_leaks=$(echo "$log" | + sed -n 's/.*kmemleak: \([0-9]\+\) new suspected.*/\1/p' | + awk '{s+=$1} END{print s+0}') +printed=$(echo "$log" | grep -c 'kmemleak: unreferenced object') +if [ "$new_leaks" -gt 0 ] && [ "$printed" -eq 0 ]; then + fail "verbose path broken: $new_leaks leaks counted, 0 printed in $SCAN_COUNT scans" +fi + +# Walk the log: split into per-scan chunks at "N new suspected memory +# leaks" boundaries; within each chunk, capture each "unreferenced +# object" report's backtrace and check that no backtrace is reported +# more than once. A duplicate within a single scan means dedup failed +# to collapse two leaks that share an allocation site. +violations=$(echo "$log" | awk ' + function flush_block() { + if (in_block) { + # Skip empty backtraces: leaks with trace_handle == 0 + # (early-boot allocations or stack_depot_save() failures + # under memory pressure) are intentionally not deduped, + # so multiple such reports in one scan are expected and + # must not be flagged as a regression. + if (bt != "") + seen[bt]++ + in_block = 0 + collecting = 0 + bt = "" + } + } + function check_and_reset( b) { + for (b in seen) + if (seen[b] > 1) + printf("backtrace seen %d times in one scan:\n%s\n", + seen[b], b) + delete seen + } + # Scan boundary: the per-scan summary line. + /kmemleak: [0-9]+ new suspected memory leaks/ { + flush_block() + check_and_reset() + next + } + # Start of a new "unreferenced object" report. + /kmemleak: unreferenced object/ { + flush_block() + in_block = 1 + next + } + # Inside a report, the "backtrace (crc ...):" line switches us to + # backtrace-collecting mode. + in_block && /kmemleak:[[:space:]]+backtrace \(crc/ { + collecting = 1 + next + } + # Once collecting, capture only deeply-indented "kmemleak: " lines + # (stack frames have 4+ spaces of indentation under "kmemleak: "; + # headers and the "... and N more" tail line have less). This stops + # unrelated kmemleak warns landing between reports from being lumped + # into the backtrace key, which would mask a genuine duplicate. + in_block && collecting && /kmemleak:[[:space:]]{4,}/ { + bt = bt $0 "\n" + next + } + END { + flush_block() + check_and_reset() + } +') + +if [ -n "$violations" ]; then + echo "$violations" + fail "kmemleak dedup regression: same backtrace reported more than once in a single scan" +fi + +# Count the dedup summary lines so the report distinguishes "dedup +# actually fired" from "no same-backtrace leaks turned up to dedup". +dedup_lines=$(echo "$log" | grep -c 'more object(s) with the same backtrace') + +if [ "$dedup_lines" -gt 0 ]; then + pass "no dedup violations across $SCAN_COUNT scans; dedup fired ($dedup_lines summary line(s) observed)" +else + pass "no dedup violations across $SCAN_COUNT scans; dedup had nothing to collapse" +fi From 9794de4500e7dc8686f4ae5f2d11d76e43f299c5 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 22 Apr 2026 22:17:44 +0300 Subject: [PATCH 2072/5214] proc: add tgid_iter.pid_ns member next_tgid() accepts pid namespace as an argument, but it never changes during readdir (which would be unthinkable thing to do anyway). Move it inside iterator type and hide from direct usage. Link: https://lore.kernel.org/20260422191745.435556-1-adobriyan@gmail.com Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton --- fs/proc/base.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index d9acfa89c894..f2db455dbbfd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3543,8 +3543,10 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags) struct tgid_iter { unsigned int tgid; struct task_struct *task; + struct pid_namespace *pid_ns; }; -static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter) + +static struct tgid_iter next_tgid(struct tgid_iter iter) { struct pid *pid; @@ -3553,9 +3555,9 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite rcu_read_lock(); retry: iter.task = NULL; - pid = find_ge_pid(iter.tgid, ns); + pid = find_ge_pid(iter.tgid, iter.pid_ns); if (pid) { - iter.tgid = pid_nr_ns(pid, ns); + iter.tgid = pid_nr_ns(pid, iter.pid_ns); iter.task = pid_task(pid, PIDTYPE_TGID); if (!iter.task) { iter.tgid += 1; @@ -3574,7 +3576,7 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) { struct tgid_iter iter; struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb); - struct pid_namespace *ns = proc_pid_ns(file_inode(file)->i_sb); + struct pid_namespace *pid_ns = proc_pid_ns(file_inode(file)->i_sb); loff_t pos = ctx->pos; if (pos >= PID_MAX_LIMIT + TGID_OFFSET) @@ -3592,9 +3594,10 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) } iter.tgid = pos - TGID_OFFSET; iter.task = NULL; - for (iter = next_tgid(ns, iter); + iter.pid_ns = pid_ns; + for (iter = next_tgid(iter); iter.task; - iter.tgid += 1, iter = next_tgid(ns, iter)) { + iter.tgid += 1, iter = next_tgid(iter)) { char name[10 + 1]; unsigned int len; From f7027ed76dfcf4905225272029a83cc74fd1816d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 22 Apr 2026 22:17:45 +0300 Subject: [PATCH 2073/5214] proc: rewrite next_tgid() * deduplicate "iter.tgid += 1" line, Right now it is done once inside next_tgid() itself and second time inside "for" loop. * deduplicate next_tgid() call itself with different loop style: auto it = make_tgid_iter(); while (next_tgid(&it)) { ... } gcc seems to inline it twice: $ ./scripts/bloat-o-meter ../vmlinux-000 ../obj/vmlinux add/remove: 0/1 grow/shrink: 1/0 up/down: 100/-245 (-145) Function old new delta proc_pid_readdir 531 631 +100 next_tgid 245 - -245 * make tgid_iter.pid_ns const it never changes during readdir anyway [akpm@linux-foundation.org: remove newline] Link: https://lore.kernel.org/20260422191745.435556-2-adobriyan@gmail.com Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton --- fs/proc/base.c | 60 ++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index f2db455dbbfd..49344de10158 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3543,30 +3543,42 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags) struct tgid_iter { unsigned int tgid; struct task_struct *task; - struct pid_namespace *pid_ns; + struct pid_namespace *const pid_ns; }; -static struct tgid_iter next_tgid(struct tgid_iter iter) +static struct tgid_iter +make_tgid_iter(unsigned int init_tgid, struct pid_namespace *pid_ns) { - struct pid *pid; + return (struct tgid_iter){ + .tgid = init_tgid - 1, + .pid_ns = pid_ns, + }; +} - if (iter.task) - put_task_struct(iter.task); - rcu_read_lock(); -retry: - iter.task = NULL; - pid = find_ge_pid(iter.tgid, iter.pid_ns); - if (pid) { - iter.tgid = pid_nr_ns(pid, iter.pid_ns); - iter.task = pid_task(pid, PIDTYPE_TGID); - if (!iter.task) { - iter.tgid += 1; - goto retry; - } - get_task_struct(iter.task); +static bool next_tgid(struct tgid_iter *it) +{ + if (it->task) { + put_task_struct(it->task); + it->task = NULL; + } + + rcu_read_lock(); + while (1) { + it->tgid += 1; + const auto pid = find_ge_pid(it->tgid, it->pid_ns); + if (pid) { + it->tgid = pid_nr_ns(pid, it->pid_ns); + it->task = pid_task(pid, PIDTYPE_TGID); + if (it->task) { + get_task_struct(it->task); + rcu_read_unlock(); + return true; + } + } else { + rcu_read_unlock(); + return false; + } } - rcu_read_unlock(); - return iter; } #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) @@ -3574,7 +3586,6 @@ static struct tgid_iter next_tgid(struct tgid_iter iter) /* for the /proc/ directory itself, after non-process stuff has been done */ int proc_pid_readdir(struct file *file, struct dir_context *ctx) { - struct tgid_iter iter; struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb); struct pid_namespace *pid_ns = proc_pid_ns(file_inode(file)->i_sb); loff_t pos = ctx->pos; @@ -3592,12 +3603,9 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) return 0; ctx->pos = pos = pos + 1; } - iter.tgid = pos - TGID_OFFSET; - iter.task = NULL; - iter.pid_ns = pid_ns; - for (iter = next_tgid(iter); - iter.task; - iter.tgid += 1, iter = next_tgid(iter)) { + + auto iter = make_tgid_iter(pos - TGID_OFFSET, pid_ns); + while (next_tgid(&iter)) { char name[10 + 1]; unsigned int len; From 2ddfdfe1ad31ba5be5a1c3b4b2b950cf6b6d7c35 Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Tue, 21 Apr 2026 23:14:06 +0200 Subject: [PATCH 2074/5214] checkpatch: allow passing config directory checkpatch.pl searches for .checkpatch.conf in $CWD, $HOME and $CWD/.scripts. Allow passing a single directory via CHECKPATCH_CONFIG_DIR environment variable (empty value is ignored). This allows to directly use project configuration file for projects which vendored checkpatch.pl (e.g. LTP or u-boot). Although it'd be more convenient for user to have --conf-dir option (instead of using environment variable), code would get ugly because options from the configuration file needs to be read before processing command line options with Getopt::Long. While at it, document directories and environment variable in -h help and HTML doc. Link: https://lore.kernel.org/20260421211408.383972-1-pvorel@suse.cz Signed-off-by: Petr Vorel Reviewed-by: Simon Glass Acked-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- Documentation/dev-tools/checkpatch.rst | 7 +++++++ scripts/checkpatch.pl | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index dccede68698c..010dfcd615af 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -210,6 +210,13 @@ Available options: Display the help text. +Configuration file +================== + +Default configuration options can be stored in ``.checkpatch.conf``, search +path: ``.:$HOME:.scripts`` or in a directory specified by ``$CHECKPATCH_CONFIG_DIR`` +environment variable (falling back to the default search path). + Message Levels ============== diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 0492d6afc9a1..8b44b3a6a4dd 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -57,6 +57,9 @@ my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; +my $def_configuration_dirs_help = '.:$HOME:.scripts'; +(my $def_configuration_dirs = $def_configuration_dirs_help) =~ s/\$(\w+)/$ENV{$1}/g; +my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; @@ -146,6 +149,11 @@ Options: -h, --help, --version display this help and exit When FILE is - read standard input. + +CONFIGURATION FILE +Default configuration options can be stored in $configuration_file, +search path: '$def_configuration_dirs_help' or in a directory specified by +\$$env_config_dir environment variable (fallback to the default search path). EOM exit($exitcode); @@ -237,7 +245,7 @@ sub list_types { exit($exitcode); } -my $conf = which_conf($configuration_file); +my $conf = which_conf($configuration_file, $env_config_dir, $def_configuration_dirs); if (-f $conf) { my @conf_args; open(my $conffile, '<', "$conf") @@ -1531,9 +1539,15 @@ sub which { } sub which_conf { - my ($conf) = @_; + my ($conf, $env_key, $paths) = @_; + my $env_dir = $ENV{$env_key}; - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (defined($env_dir) && $env_dir ne "") { + return "$env_dir/$conf" if (-e "$env_dir/$conf"); + warn "$P: Can't find a readable $conf in '$env_dir', falling back to default search paths\n"; + } + + foreach my $path (split(/:/, $paths)) { if (-e "$path/$conf") { return "$path/$conf"; } From b8979a02f9c56d489e27690981d2aa29a3e79542 Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Tue, 21 Apr 2026 23:14:07 +0200 Subject: [PATCH 2075/5214] checkpatch: add option to not force /* */ for SPDX Add option --spdx-cxx-comments to not force C comments (/* */) for SPDX, but allow also C++ comments (//). As documented in aa19a176df95d6, this is required for some old toolchains still have older assembler tools which cannot handle C++ style comments. This avoids forcing this for projects which vendored checkpatch.pl (e.g. LTP or u-boot). Link: https://lore.kernel.org/20260421211408.383972-2-pvorel@suse.cz Signed-off-by: Petr Vorel Reviewed-by: Simon Glass Acked-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- Documentation/dev-tools/checkpatch.rst | 7 +++++++ scripts/checkpatch.pl | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index 010dfcd615af..6139a08c34cd 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -184,6 +184,13 @@ Available options: Override checking of perl version. Runtime errors may be encountered after enabling this flag if the perl version does not meet the minimum specified. + - --spdx-cxx-comments + + Don't force C comments ``/* */`` for SPDX license (required by old + toolchains), allow also C++ comments ``//``. + + NOTE: it should *not* be used for Linux mainline. + - --codespell Use the codespell dictionary for checking spelling errors. diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8b44b3a6a4dd..0d18771f1b01 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -62,6 +62,7 @@ my $def_configuration_dirs_help = '.:$HOME:.scripts'; my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; +my $spdx_cxx_comments = 0; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; @@ -138,6 +139,10 @@ Options: file. It's your fault if there's no backup or git --ignore-perl-version override checking of perl version. expect runtime errors. + --spdx-cxx-comments don't force C comments (/* */) for SPDX license + (required by old toolchains), allow also C++ + comments (//). + NOTE: it should *not* be used for Linux mainline. --codespell Use the codespell dictionary for spelling/typos (default:$codespellfile) --codespellfile Use this codespell dictionary @@ -347,6 +352,7 @@ GetOptions( 'fix!' => \$fix, 'fix-inplace!' => \$fix_inplace, 'ignore-perl-version!' => \$ignore_perl_version, + 'spdx-cxx-comments!' => \$spdx_cxx_comments, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, @@ -3815,26 +3821,33 @@ sub process { $checklicenseline = 2; } elsif ($rawline =~ /^\+/) { my $comment = ""; - if ($realfile =~ /\.(h|s|S)$/) { - $comment = '/*'; - } elsif ($realfile =~ /\.(c|rs|dts|dtsi)$/) { + if ($realfile =~ /\.(c|rs|dts|dtsi)$/) { $comment = '//'; } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { $comment = '#'; } elsif ($realfile =~ /\.rst$/) { $comment = '..'; } + my $pattern = qr{\Q$comment\E}; + if ($realfile =~ /\.(h|s|S)$/) { + $comment = '/*'; + $pattern = qr{/\*}; + if ($spdx_cxx_comments) { + $comment = '// or /*'; + $pattern = qr{//|/\*}; + } + } # check SPDX comment style for .[chsS] files if ($realfile =~ /\.[chsS]$/ && $rawline =~ /SPDX-License-Identifier:/ && - $rawline !~ m@^\+\s*\Q$comment\E\s*@) { + $rawline !~ m@^\+\s*$pattern\s*@) { WARN("SPDX_LICENSE_TAG", "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); } if ($comment !~ /^$/ && - $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { + $rawline !~ m@^\+$pattern SPDX-License-Identifier: @) { WARN("SPDX_LICENSE_TAG", "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { From de5d0652d0633daaaa1f8f322721f600e65b1fb1 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 21 Apr 2026 14:26:47 +0200 Subject: [PATCH 2076/5214] proc: use strnlen() for name validation in __proc_create Replace strlen(fn) with strnlen(fn, NAME_MAX + 1) when validating the final path component in __proc_create(). This preserves the existing name limit while bounding the length scan to one byte past the maximum name length. Handle empty names separately, and treat names longer than NAME_MAX as too long. Link: https://lore.kernel.org/20260421122648.56723-2-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Reviewed-by: Jan Kara Cc: Alexey Dobriyan Cc: Al Viro Cc: Christian Brauner Cc: Thorsten Blum Cc: wangzijie Signed-off-by: Andrew Morton --- fs/proc/generic.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8bb81e58c9d8..3063080f3bb2 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -427,9 +427,13 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, if (xlate_proc_name(name, parent, &fn) != 0) goto out; qstr.name = fn; - qstr.len = strlen(fn); - if (qstr.len == 0 || qstr.len >= 256) { - WARN(1, "name len %u\n", qstr.len); + qstr.len = strnlen(fn, NAME_MAX + 1); + if (qstr.len == 0) { + WARN(1, "empty name\n"); + return NULL; + } + if (qstr.len > NAME_MAX) { + WARN(1, "name too long\n"); return NULL; } if (qstr.len == 1 && fn[0] == '.') { From 0e75190fe40093d85c0c698d9f8dca66df842605 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 23 Apr 2026 23:11:39 +0800 Subject: [PATCH 2077/5214] tools/accounting/getdelays: fix -Wformat-truncation warning in format_timespec Reproduce with GCC 13.3.0: $ cd tools/accounting $ make This emits: getdelays.c: In function `format_timespec': getdelays.c:218:67: warning: `:' directive output may be truncated writing 1 byte into a region of size between 0 and 16 [-Wformat-truncation=] 218 | snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d", | getdelays.c:218:9: note: `snprintf' output between 20 and 72 bytes into a destination of size 32 The problem is that %04d and %02d specify minimum field widths only. GCC cannot prove that formatting tm_year + 1900 and the other struct tm fields will always fit in the fixed 32-byte buffer, so it warns about possible truncation. Fix this by replacing the manual snprintf() formatting with strftime("%Y-%m-%dT%H:%M:%S", ...). That matches the data we already have in struct tm, keeps the intended timestamp format, and avoids the warning when building tools/accounting with GCC. Link: https://lore.kernel.org/87d9723e0b59d816ee2e4bd7cddd58a54c6c9f91.1776956545.git.cyyzero16@gmail.com Signed-off-by: Yiyang Chen Cc: Fan Yu Cc: Wang Yaxin Signed-off-by: Andrew Morton --- tools/accounting/getdelays.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c index 368a622ca027..caa5fe9dd573 100644 --- a/tools/accounting/getdelays.c +++ b/tools/accounting/getdelays.c @@ -241,13 +241,7 @@ static const char *format_timespec(struct __kernel_timespec *ts) if (localtime_r(&time_sec, &tm_info) == NULL) return "N/A"; - snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d", - tm_info.tm_year + 1900, - tm_info.tm_mon + 1, - tm_info.tm_mday, - tm_info.tm_hour, - tm_info.tm_min, - tm_info.tm_sec); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", &tm_info); return buffer; } From 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:10:48 +0000 Subject: [PATCH 2078/5214] ocfs2: use kzalloc for quota recovery bitmap allocation ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not fully initialize it. This can lead to use of uninitialized bits during quota recovery from a corrupted filesystem image. Use kzalloc instead to ensure the bitmap is zero-initialized. Link: https://lore.kernel.org/20260418131048.1052507-1-tristmd@gmail.com Reported-by: syzbot+7ea0b96c4ddb49fd1a70@syzkaller.appspotmail.com Signed-off-by: Tristan Madani Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/quota_local.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 12cbb4fccda0..f55810c59b1b 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb, if (!rc) return -ENOMEM; rc->rc_chunk = chunk; - rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); + rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS); if (!rc->rc_bitmap) { kfree(rc); return -ENOMEM; From 1877a09b64f89278ce3dc83e0cf6a8b1516660cb Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 18 Apr 2026 10:34:59 -0700 Subject: [PATCH 2079/5214] checkpatch: add check for function pointer arrays in declarations checkpatch did not allow function pointer arrays when testing declaration blocks. Add it. Link: https://lore.kernel.org/eb62763085eb42193a611bca00a62d6f0ae72e1e.1776530118.git.joe@perches.com Signed-off-by: Joe Perches Cc: Andy Whitcroft Cc: Dan Carpenter Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 0d18771f1b01..3727156e4cca 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -4183,7 +4183,7 @@ sub process { $pl =~ s/\b(?:$Attribute|$Sparse)\b//g; if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident(?:\s*\[\s*(?:$Ident|$Constant)?\s*\])?\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros @@ -4197,7 +4197,7 @@ sub process { # looks like a declaration !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident(?:\s*\[\s*(?:$Ident|$Constant)?\s*\])?\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros From 410002f8139cbf902f1567f0a8cbf0c5b6f43da2 Mon Sep 17 00:00:00 2001 From: Adi Nata Date: Sun, 5 Apr 2026 09:19:20 +0800 Subject: [PATCH 2080/5214] kunit: fat: test cluster and directory i_pos layout helpers Add KUnit coverage for fat_clus_to_blknr() and fat_get_blknr_offset() using stub msdos_sb_info values so cluster-to-sector and i_pos split math stays correct. Link: https://lore.kernel.org/20260405011920.28622-1-adinata.softwareengineer@gmail.com Signed-off-by: Adi Nata Acked-by: OGAWA Hirofumi Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/fat/fat_test.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 886bf044a9f1..4eeed9dca549 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -20,6 +20,37 @@ static void fat_checksum_test(struct kunit *test) KUNIT_EXPECT_EQ(test, fat_checksum("ABCDEFGHA "), (u8)98); } +static void fat_clus_to_blknr_test(struct kunit *test) +{ + struct msdos_sb_info sbi = { + .sec_per_clus = 4, + .data_start = 100, + }; + + KUNIT_EXPECT_EQ(test, (sector_t)100, + fat_clus_to_blknr(&sbi, FAT_START_ENT)); + KUNIT_EXPECT_EQ(test, (sector_t)112, fat_clus_to_blknr(&sbi, 5)); +} + +static void fat_get_blknr_offset_test(struct kunit *test) +{ + struct msdos_sb_info sbi = { + .dir_per_block = 16, + .dir_per_block_bits = 4, + }; + + sector_t blknr; + int offset; + + fat_get_blknr_offset(&sbi, 0, &blknr, &offset); + KUNIT_EXPECT_EQ(test, (sector_t)0, blknr); + KUNIT_EXPECT_EQ(test, 0, offset); + + fat_get_blknr_offset(&sbi, (10 << 4) | 7, &blknr, &offset); + KUNIT_EXPECT_EQ(test, (sector_t)10, blknr); + KUNIT_EXPECT_EQ(test, 7, offset); +} + struct fat_timestamp_testcase { const char *name; struct timespec64 ts; @@ -341,6 +372,8 @@ static void fat_truncate_atime_test(struct kunit *test) static struct kunit_case fat_test_cases[] = { KUNIT_CASE(fat_checksum_test), + KUNIT_CASE(fat_clus_to_blknr_test), + KUNIT_CASE(fat_get_blknr_offset_test), KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, From 99df2a8eba347e901e5355ee66bdb2ade76ff847 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 13 Apr 2026 11:23:48 -0700 Subject: [PATCH 2081/5214] clang-format: fix formatting of guard() and scoped_guard() statements Without this patch clang-format formats guard() and scoped_guard() statements as follows: guard(...)(...) { ... } With this patch clang-format formats guard() and scoped_guard() statements as follows: guard(...)(...) { ... } Link: https://lore.kernel.org/20260413182348.1865138-1-bvanassche@acm.org Signed-off-by: Bart Van Assche Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Andrew Morton --- .clang-format | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-format b/.clang-format index 1cc151e2adcc..6a3de86ab27a 100644 --- a/.clang-format +++ b/.clang-format @@ -481,6 +481,7 @@ ForEachMacros: - 'genradix_for_each' - 'genradix_for_each_from' - 'genradix_for_each_reverse' + - 'guard' - 'hash_for_each' - 'hash_for_each_possible' - 'hash_for_each_possible_rcu' @@ -674,6 +675,7 @@ ForEachMacros: - 'rq_list_for_each' - 'rq_list_for_each_safe' - 'sample_read_group__for_each' + - 'scoped_guard' - 'scsi_for_each_prot_sg' - 'scsi_for_each_sg' - 'sctp_for_each_hentry' From b3e4fbb04220efc3bc022bcf31b5689d39c6b111 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:44 +0800 Subject: [PATCH 2082/5214] taskstats: retain dead thread stats in TGID queries Patch series "taskstats: fix TGID dead-thread stat retention", v3. This series fixes a taskstats TGID aggregation bug where fields added in the TGID query path were not preserved after thread exit, and adds a kselftest covering the regression. The first patch keeps the cached TGID aggregate used for dead threads in step with the fields already accumulated for live threads, and also fixes the final TGID exit notification emitted when group_dead is true. The second patch adds a kselftest that verifies TGID CPU stats do not regress after a worker thread exits and has been reaped. This patch (of 2): fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group. However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads. This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit(): - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path. - commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path. As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields. Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries. Link: https://lore.kernel.org/cover.1776094300.git.cyyzero16@gmail.com Link: https://lore.kernel.org/abd2a15d33343636ab5ba43d540bcfe508bd66c7.1776094300.git.cyyzero16@gmail.com Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") Fixes: b663a79c1915 ("taskstats: add context-switch counters") Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Cc: Signed-off-by: Andrew Morton --- kernel/taskstats.c | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 73bd6a6a7893..2cd0172d0516 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) return 0; } +static void tgid_stats_add_task(struct taskstats *stats, + struct task_struct *tsk, u64 now_ns) +{ + u64 delta, utime, stime; + + /* + * Each accounting subsystem calls its functions here to + * accumulate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + /* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime += delta; + + task_cputime(tsk, &utime, &stime); + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} + static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) { struct task_struct *tsk, *first; unsigned long flags; int rc = -ESRCH; - u64 delta, utime, stime; - u64 start_time; + u64 now_ns; /* * Add additional stats from live tasks except zombie thread group @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) else memset(stats, 0, sizeof(*stats)); - start_time = ktime_get_ns(); + now_ns = ktime_get_ns(); for_each_thread(first, tsk) { if (tsk->exit_state) continue; - /* - * Accounting subsystem can call its functions here to - * fill in relevant parts of struct taskstsats as follows - * - * per-task-foo(stats, tsk); - */ - delayacct_add_tsk(stats, tsk); - /* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; - /* Convert to micro seconds */ - do_div(delta, NSEC_PER_USEC); - stats->ac_etime += delta; - - task_cputime(tsk, &utime, &stime); - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); - - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; + tgid_stats_add_task(stats, tsk, now_ns); } unlock_task_sighand(first, &flags); @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) static void fill_tgid_exit(struct task_struct *tsk) { unsigned long flags; + u64 now_ns; spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->stats) goto ret; - /* - * Each accounting subsystem calls its functions here to - * accumalate its per-task stats for tsk, into the per-tgid structure - * - * per-task-foo(tsk->signal->stats, tsk); - */ - delayacct_add_tsk(tsk->signal->stats, tsk); + now_ns = ktime_get_ns(); + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); ret: spin_unlock_irqrestore(&tsk->sighand->siglock, flags); return; From 20e4b31f8e837d923eee91cd5458f823a11cca9a Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:45 +0800 Subject: [PATCH 2083/5214] selftests/acct: add taskstats TGID retention test Add a kselftest for the taskstats TGID aggregation fix. The test creates a worker thread, snapshots TGID taskstats while the worker is still alive, lets the worker exit, and then verifies that the TGID CPU total does not regress after the thread has been reaped. The pass/fail check intentionally keys off ac_utime + ac_stime only, which is the primary user-visible regression fixed by the taskstats change and is less sensitive to scheduling noise than context-switch counters. Link: https://lore.kernel.org/0d55354911c54cd1b9f10a09f6fd378af85c8d43.1776094300.git.cyyzero16@gmail.com Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/acct/.gitignore | 3 +- tools/testing/selftests/acct/Makefile | 7 +- .../acct/taskstats_fill_stats_tgid.c | 375 ++++++++++++++++++ 3 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/acct/taskstats_fill_stats_tgid.c diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore index 7e78aac19038..9e9c61c5bfd6 100644 --- a/tools/testing/selftests/acct/.gitignore +++ b/tools/testing/selftests/acct/.gitignore @@ -1,3 +1,4 @@ acct_syscall +taskstats_fill_stats_tgid config -process_log \ No newline at end of file +process_log diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile index 7e025099cf65..083cab5ddb72 100644 --- a/tools/testing/selftests/acct/Makefile +++ b/tools/testing/selftests/acct/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 TEST_GEN_PROGS := acct_syscall -CFLAGS += -Wall +TEST_GEN_PROGS += taskstats_fill_stats_tgid -include ../lib.mk \ No newline at end of file +CFLAGS += -Wall +LDLIBS += -lpthread + +include ../lib.mk diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c new file mode 100644 index 000000000000..d6cab4ae26f2 --- /dev/null +++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest.h" + +#ifndef NLA_ALIGN +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) +#endif + +#define BUSY_NS (200ULL * 1000 * 1000) + +struct worker_ctx { + pthread_mutex_t lock; + pthread_cond_t cond; + bool ready; + bool release; +}; + +static unsigned long busy_sink; + +static void *taskstats_nla_data(const struct nlattr *na) +{ + return (void *)((char *)na + NLA_HDRLEN); +} + +static bool taskstats_nla_ok(const struct nlattr *na, int remaining) +{ + return remaining >= (int)sizeof(*na) && + na->nla_len >= sizeof(*na) && + na->nla_len <= remaining; +} + +static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining) +{ + int aligned_len = NLA_ALIGN(na->nla_len); + + *remaining -= aligned_len; + return (struct nlattr *)((char *)na + aligned_len); +} + +static uint64_t timespec_diff_ns(const struct timespec *start, + const struct timespec *end) +{ + return (uint64_t)(end->tv_sec - start->tv_sec) * 1000000000ULL + + (uint64_t)(end->tv_nsec - start->tv_nsec); +} + +static void burn_cpu_for_ns(uint64_t runtime_ns) +{ + struct timespec start, now; + unsigned long acc = 0; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + do { + for (int i = 0; i < 100000; i++) + acc += i; + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + } while (timespec_diff_ns(&start, &now) < runtime_ns); + + busy_sink = acc; +} + +static int netlink_open(void) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = getpid(), + }; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd < 0) + return -errno; + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + int err = -errno; + + close(fd); + return err; + } + + return fd; +} + +static int send_request(int fd, void *buf, size_t len) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + + if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) + return -errno; + + return 0; +} + +static int get_family_id(int fd, const char *name) +{ + struct { + struct nlmsghdr nlh; + struct genlmsghdr genl; + char buf[256]; + } req = { 0 }; + char resp[8192]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + struct nlattr *na; + int len; + int rem; + int ret; + + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.nlh.nlmsg_type = GENL_ID_CTRL; + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_seq = 1; + req.nlh.nlmsg_pid = getpid(); + + req.genl.cmd = CTRL_CMD_GETFAMILY; + req.genl.version = 1; + + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); + na->nla_type = CTRL_ATTR_FAMILY_NAME; + na->nla_len = NLA_HDRLEN + strlen(name) + 1; + memcpy(taskstats_nla_data(na), name, strlen(name) + 1); + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); + + ret = send_request(fd, &req, req.nlh.nlmsg_len); + if (ret) + return ret; + + len = recv(fd, resp, sizeof(resp), 0); + if (len < 0) + return -errno; + + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nlh); + + return err->error ? err->error : -ENOENT; + } + + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); + while (taskstats_nla_ok(na, rem)) { + if (na->nla_type == CTRL_ATTR_FAMILY_ID) + return *(uint16_t *)taskstats_nla_data(na); + na = taskstats_nla_next(na, &rem); + } + } + + return -ENOENT; +} + +static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id, + struct taskstats *stats) +{ + struct { + struct nlmsghdr nlh; + struct genlmsghdr genl; + char buf[256]; + } req = { 0 }; + char resp[16384]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + struct nlattr *na; + struct nlattr *nested; + int len; + int rem; + int nrem; + int ret; + + memset(stats, 0, sizeof(*stats)); + + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.nlh.nlmsg_type = family_id; + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_seq = 2; + req.nlh.nlmsg_pid = getpid(); + + req.genl.cmd = TASKSTATS_CMD_GET; + req.genl.version = 1; + + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); + na->nla_type = attr_type; + na->nla_len = NLA_HDRLEN + sizeof(id); + memcpy(taskstats_nla_data(na), &id, sizeof(id)); + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); + + ret = send_request(fd, &req, req.nlh.nlmsg_len); + if (ret) + return ret; + + len = recv(fd, resp, sizeof(resp), 0); + if (len < 0) + return -errno; + + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nlh); + + return err->error ? err->error : -ENOENT; + } + + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); + while (taskstats_nla_ok(na, rem)) { + if (na->nla_type == TASKSTATS_TYPE_AGGR_PID || + na->nla_type == TASKSTATS_TYPE_AGGR_TGID) { + nested = (struct nlattr *)taskstats_nla_data(na); + nrem = na->nla_len - NLA_HDRLEN; + while (taskstats_nla_ok(nested, nrem)) { + if (nested->nla_type == TASKSTATS_TYPE_STATS) { + memcpy(stats, taskstats_nla_data(nested), + sizeof(*stats)); + return 0; + } + nested = taskstats_nla_next(nested, &nrem); + } + } + na = taskstats_nla_next(na, &rem); + } + } + + return -ENOENT; +} + +static uint64_t cpu_total(const struct taskstats *stats) +{ + return (uint64_t)stats->ac_utime + (uint64_t)stats->ac_stime; +} + +static void print_stats(const char *label, const struct taskstats *stats) +{ + ksft_print_msg("%s: cpu_total=%llu nvcsw=%llu nivcsw=%llu\n", + label, (unsigned long long)cpu_total(stats), + (unsigned long long)stats->nvcsw, + (unsigned long long)stats->nivcsw); +} + +static void *worker_thread(void *arg) +{ + struct worker_ctx *ctx = arg; + + burn_cpu_for_ns(BUSY_NS); + + pthread_mutex_lock(&ctx->lock); + ctx->ready = true; + pthread_cond_broadcast(&ctx->cond); + while (!ctx->release) + pthread_cond_wait(&ctx->cond, &ctx->lock); + pthread_mutex_unlock(&ctx->lock); + + return NULL; +} + +int main(void) +{ + struct worker_ctx ctx = { + .lock = PTHREAD_MUTEX_INITIALIZER, + .cond = PTHREAD_COND_INITIALIZER, + }; + struct taskstats before, after; + pthread_t thread; + pid_t tgid = getpid(); + int family_id; + int fd; + int ret; + + ksft_print_header(); + ksft_set_plan(1); + + if (geteuid()) + ksft_exit_skip("taskstats_fill_stats_tgid needs root\n"); + + fd = netlink_open(); + if (fd < 0) + ksft_exit_skip("failed to open generic netlink socket: %s\n", + strerror(-fd)); + + family_id = get_family_id(fd, TASKSTATS_GENL_NAME); + if (family_id < 0) + ksft_exit_skip("taskstats generic netlink family unavailable: %s\n", + strerror(-family_id)); + + /* Create worker thread that burns 200ms of CPU */ + if (pthread_create(&thread, NULL, worker_thread, &ctx) != 0) + ksft_exit_fail_msg("pthread_create failed: %s\n", strerror(errno)); + + /* Wait for worker to finish generating activity */ + pthread_mutex_lock(&ctx.lock); + while (!ctx.ready) + pthread_cond_wait(&ctx.cond, &ctx.lock); + pthread_mutex_unlock(&ctx.lock); + + /* + * Snapshot A: TGID stats while worker is alive and sleeping. + * Contains main thread + worker contributions. + */ + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &before); + if (ret) + ksft_exit_fail_msg("TGID query before exit failed: %s\n", + strerror(-ret)); + + /* Release worker so it can exit, then join (deterministic wait). + * + * Kernel exit path ordering guarantees: + * do_exit() + * taskstats_exit() -> fill_tgid_exit() (accumulates worker into signal->stats) + * exit_notify() (releases the thread) + * do_task_dead() -> __schedule() (wakes joiner) + * + * So pthread_join() returns only after fill_tgid_exit() has completed. + */ + pthread_mutex_lock(&ctx.lock); + ctx.release = true; + pthread_cond_broadcast(&ctx.cond); + pthread_mutex_unlock(&ctx.lock); + + pthread_join(thread, NULL); + + /* + * Snapshot B: TGID stats after worker has exited. + * fill_stats_for_tgid() does: + * memcpy(signal->stats) <- includes fill_tgid_exit accumulation + * + scan live threads <- only main thread now + */ + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &after); + if (ret) + ksft_exit_fail_msg("TGID query after exit failed: %s\n", + strerror(-ret)); + + print_stats("TGID before worker exit", &before); + print_stats("TGID after worker exit", &after); + + /* + * The worker burned 200ms of CPU before the first snapshot. + * If the kernel correctly retained its contribution via + * fill_tgid_exit(), then the TGID CPU total after exit must be at + * least as large as the TGID CPU total before exit. + */ + ksft_test_result(cpu_total(&after) >= cpu_total(&before), + "TGID CPU stats should not regress after thread exit\n"); + + close(fd); + ksft_finished(); + return ksft_get_fail_cnt() ? KSFT_FAIL : KSFT_PASS; +} From f13f1b0cb56491547243e502988282e5be50bc44 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Thu, 9 Apr 2026 02:08:51 +0545 Subject: [PATCH 2084/5214] seq_buf: export seq_buf_putmem_hex() and add KUnit tests The seq_buf KUnit suite does not exercise seq_buf_putmem_hex(). Add one test for the len > 8 chunking path and one overflow test where a later chunk no longer fits in the buffer. Export seq_buf_putmem_hex() as well so SEQ_BUF_KUNIT_TEST=m links cleanly. Without the export, modpost reports seq_buf_putmem_hex as undefined when seq_buf_kunit is built as a module. Link: https://lore.kernel.org/20260408202351.21829-1-shuvampandey1@gmail.com Signed-off-by: Shuvam Pandey Acked-by: Steven Rostedt (Google) Cc: David Gow Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Signed-off-by: Andrew Morton --- lib/seq_buf.c | 1 + lib/tests/seq_buf_kunit.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/seq_buf.c b/lib/seq_buf.c index f3f3436d60a9..b59488fa8135 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -298,6 +298,7 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, } return 0; } +EXPORT_SYMBOL_GPL(seq_buf_putmem_hex); /** * seq_buf_path - copy a path into the sequence buffer diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c index 8a01579a978e..eb466386bbef 100644 --- a/lib/tests/seq_buf_kunit.c +++ b/lib/tests/seq_buf_kunit.c @@ -184,6 +184,38 @@ static void seq_buf_get_buf_commit_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); } +static void seq_buf_putmem_hex_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 24); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 0809 "; +#else + const char *expected = "0706050403020100 0908 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), 0); + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), strlen(expected)); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + +static void seq_buf_putmem_hex_overflow_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 20); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 "; +#else + const char *expected = "0706050403020100 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1); + KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 20); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_init_test), KUNIT_CASE(seq_buf_declare_test), @@ -194,6 +226,8 @@ static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_printf_test), KUNIT_CASE(seq_buf_printf_overflow_test), KUNIT_CASE(seq_buf_get_buf_commit_test), + KUNIT_CASE(seq_buf_putmem_hex_test), + KUNIT_CASE(seq_buf_putmem_hex_overflow_test), {} }; From 09d8b39563e84fc245d642182715a7e0e024b0f2 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 8 Apr 2026 15:45:42 -0400 Subject: [PATCH 2085/5214] get_maintainer: add --json output mode Add a --json flag to get_maintainer.pl that emits structured JSON output, making results machine-parseable for CI systems, IDE integrations, and AI-assisted development tools. The JSON output includes a maintainers array with structured name, email, and role fields, plus optional arrays for scm, status, subsystem, web, and bug information when those flags are enabled. Normal text output behavior is completely unchanged when --json is not specified. Assisted-by: Claude:claude-opus-4-6 Link: https://lore.kernel.org/20260408194542.1354549-1-sashal@kernel.org Signed-off-by: Sasha Levin Acked-by: Joe Perches Signed-off-by: Andrew Morton --- scripts/get_maintainer.pl | 71 +++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index f0ca0db6ddc2..16b80a700d4a 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -21,6 +21,7 @@ use Cwd; use File::Find; use File::Spec::Functions; use open qw(:std :encoding(UTF-8)); +use JSON::PP; my $cur_path = fastgetcwd() . '/'; my $lk_path = "./"; @@ -68,6 +69,7 @@ my $pattern_depth = 0; my $self_test = undef; my $version = 0; my $help = 0; +my $json = 0; my $find_maintainer_files = 0; my $maintainer_path; my $vcs_used = 0; @@ -285,6 +287,7 @@ if (!GetOptions( 'find-maintainer-files' => \$find_maintainer_files, 'mpath|maintainer-path=s' => \$maintainer_path, 'self-test:s' => \$self_test, + 'json!' => \$json, 'v|version' => \$version, 'h|help|usage' => \$help, )) { @@ -650,39 +653,48 @@ my %deduplicate_name_hash = (); my %deduplicate_address_hash = (); my @maintainers = get_maintainers(); -if (@maintainers) { - @maintainers = merge_email(@maintainers); - output(@maintainers); -} -if ($scm) { - @scm = uniq(@scm); - output(@scm); -} +@maintainers = merge_email(@maintainers) if (@maintainers); +@scm = uniq(@scm) if ($scm); +@substatus = uniq(@substatus) if ($output_substatus); +@status = uniq(@status) if ($status); +@subsystem = uniq(@subsystem) if ($subsystem); +@web = uniq(@web) if ($web); +@bug = uniq(@bug) if ($bug); -if ($output_substatus) { - @substatus = uniq(@substatus); - output(@substatus); -} +if ($json) { + my @json_maintainers; + for my $m (@maintainers) { + my ($addr, $role); + if ($output_roles && $m =~ /^(.*?)\s+\((.+)\)\s*$/) { + $addr = $1; + $role = $2; + } else { + $addr = $m; + } + my ($name, $email_addr) = parse_email($addr); + my %entry = (name => $name, email => $email_addr); + $entry{role} = $role if (defined $role && $role ne ''); + push(@json_maintainers, \%entry); + } -if ($status) { - @status = uniq(@status); - output(@status); -} + my %result = (maintainers => \@json_maintainers); + $result{scm} = \@scm if ($scm); + $result{status} = \@status if ($status); + $result{subsystem} = \@subsystem if ($subsystem); + $result{web} = \@web if ($web); + $result{bug} = \@bug if ($bug); -if ($subsystem) { - @subsystem = uniq(@subsystem); - output(@subsystem); -} - -if ($web) { - @web = uniq(@web); - output(@web); -} - -if ($bug) { - @bug = uniq(@bug); - output(@bug); + my $json_encoder = JSON::PP->new->canonical->utf8; + print($json_encoder->encode(\%result) . "\n"); +} else { + output(@maintainers) if (@maintainers); + output(@scm) if ($scm); + output(@substatus) if ($output_substatus); + output(@status) if ($status); + output(@subsystem) if ($subsystem); + output(@web) if ($web); + output(@bug) if ($bug); } exit($exit); @@ -1104,6 +1116,7 @@ Output type options: --separator [, ] => separator for multiple entries on 1 line using --separator also sets --nomultiline if --separator is not [, ] --multiline => print 1 entry per line + --json => output results as JSON Other options: --pattern-depth => Number of pattern directory traversals (default: 0 (all)) From 97bd80a91858ea72be5aa4565af2721fb732eba6 Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Tue, 7 Apr 2026 11:09:34 +0530 Subject: [PATCH 2086/5214] treewide: fix indentation and whitespace in Kconfig files Clean up inconsistent indentation (mixing tabs and spaces) and remove extraneous whitespace in several Kconfig files across the tree. This is a purely cosmetic change to improve readability. Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Link: https://lore.kernel.org/20260407053945.14116-1-linux.amoon@gmail.com Signed-off-by: Anand Moon Reviewed-by: Jan Kara [fs] Reviewed-by: Liam R. Howlett [mm] Reviewed-by: Lorenzo Stoakes [mm] Reviewed-by: Christian Brauner Signed-off-by: Andrew Morton --- certs/Kconfig | 14 +++++++------- fs/Kconfig | 6 +++--- init/Kconfig | 24 ++++++++++++------------ lib/Kconfig | 2 +- mm/Kconfig | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/certs/Kconfig b/certs/Kconfig index 8e39a80c7abe..9d2bf7fb5b9e 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -6,14 +6,14 @@ config MODULE_SIG_KEY default "certs/signing_key.pem" depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES) help - Provide the file name of a private key/certificate in PEM format, - or a PKCS#11 URI according to RFC7512. The file should contain, or - the URI should identify, both the certificate and its corresponding - private key. + Provide the file name of a private key/certificate in PEM format, + or a PKCS#11 URI according to RFC7512. The file should contain, or + the URI should identify, both the certificate and its corresponding + private key. - If this option is unchanged from its default "certs/signing_key.pem", - then the kernel will automatically generate the private key and - certificate as described in Documentation/admin-guide/module-signing.rst + If this option is unchanged from its default "certs/signing_key.pem", + then the kernel will automatically generate the private key and + certificate as described in Documentation/admin-guide/module-signing.rst choice prompt "Type of module signing key to be generated" diff --git a/fs/Kconfig b/fs/Kconfig index 43cb06de297f..cf6ae64776e6 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -78,7 +78,7 @@ config FS_DAX --map=mem: https://docs.pmem.io/ndctl-user-guide/ndctl-man-pages/ndctl-create-namespace - For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most + For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most file systems DAX support needs to be manually enabled globally or per-inode using a mount option as well. See the file documentation in Documentation/filesystems/dax.rst for details. @@ -116,8 +116,8 @@ config FILE_LOCKING default y help This option enables standard file locking support, required - for filesystems like NFS and for the flock() system - call. Disabling this option saves about 11k. + for filesystems like NFS and for the flock() system + call. Disabling this option saves about 11k. source "fs/crypto/Kconfig" diff --git a/init/Kconfig b/init/Kconfig index 2937c4d308ae..624d93506190 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1050,14 +1050,14 @@ config PAGE_COUNTER bool config CGROUP_FAVOR_DYNMODS - bool "Favor dynamic modification latency reduction by default" - help - This option enables the "favordynmods" mount option by default - which reduces the latencies of dynamic cgroup modifications such - as task migrations and controller on/offs at the cost of making - hot path operations such as forks and exits more expensive. + bool "Favor dynamic modification latency reduction by default" + help + This option enables the "favordynmods" mount option by default + which reduces the latencies of dynamic cgroup modifications such + as task migrations and controller on/offs at the cost of making + hot path operations such as forks and exits more expensive. - Say N if unsure. + Say N if unsure. config MEMCG bool "Memory controller" @@ -1139,7 +1139,7 @@ config GROUP_SCHED_WEIGHT def_bool n config GROUP_SCHED_BANDWIDTH - def_bool n + def_bool n config FAIR_GROUP_SCHED bool "Group scheduling for SCHED_OTHER" @@ -1645,10 +1645,10 @@ config LD_ORPHAN_WARN depends on $(ld-option,--orphan-handling=error) config LD_ORPHAN_WARN_LEVEL - string - depends on LD_ORPHAN_WARN - default "error" if WERROR - default "warn" + string + depends on LD_ORPHAN_WARN + default "error" if WERROR + default "warn" config SYSCTL bool diff --git a/lib/Kconfig b/lib/Kconfig index 00a9509636c1..ed31919a5a0b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -590,7 +590,7 @@ config OBJAGG config LWQ_TEST bool "Boot-time test for lwq queuing" help - Run boot-time test of light-weight queuing. + Run boot-time test of light-weight queuing. endmenu diff --git a/mm/Kconfig b/mm/Kconfig index e8bf1e9e6ad9..6c2217ea3523 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1303,7 +1303,7 @@ config ARCH_HAS_PTE_SPECIAL bool config MAPPING_DIRTY_HELPERS - bool + bool config KMAP_LOCAL bool @@ -1434,7 +1434,7 @@ config ARCH_HAS_USER_SHADOW_STACK bool help The architecture has hardware support for userspace shadow call - stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss). + stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss). config HAVE_ARCH_TLB_REMOVE_TABLE def_bool n From f53718d5a23b72149c9bb7287c9ca078ff4d4aab Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Apr 2026 21:32:47 +0200 Subject: [PATCH 2087/5214] lib/tests: string_helpers: decouple unescape and escape cases Patch series "lib/tests: string_helpers: Slight improvements". Two ad-hoc patches to improve the test module. It was induced by another patch that poorly tried to add (existing) test cases and make me revisit string_helpers_kunit.c. This patch (of 2): Currently the escape and unescape test cases go in one step. Decouple them for the better granularity and understanding test coverage in the results. Link: https://lore.kernel.org/20260406193425.1534197-1-andriy.shevchenko@linux.intel.com Link: https://lore.kernel.org/20260406193425.1534197-2-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/string_helpers_kunit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/tests/string_helpers_kunit.c b/lib/tests/string_helpers_kunit.c index c853046183d2..cd08e79a857d 100644 --- a/lib/tests/string_helpers_kunit.c +++ b/lib/tests/string_helpers_kunit.c @@ -601,6 +601,11 @@ static void test_unescape(struct kunit *test) test_string_unescape(test, "unescape", i, false); test_string_unescape(test, "unescape inplace", get_random_u32_below(UNESCAPE_ALL_MASK + 1), true); +} + +static void test_escape(struct kunit *test) +{ + unsigned int i; /* Without dictionary */ for (i = 0; i < ESCAPE_ALL_MASK + 1; i++) @@ -615,6 +620,7 @@ static struct kunit_case string_helpers_test_cases[] = { KUNIT_CASE(test_get_size), KUNIT_CASE(test_upper_lower), KUNIT_CASE(test_unescape), + KUNIT_CASE(test_escape), {} }; From fe495c4e2ee34f84d856c06d524d43512e1f4f98 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Apr 2026 21:32:48 +0200 Subject: [PATCH 2088/5214] lib/tests: string_helpers: don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Link: https://lore.kernel.org/20260406193425.1534197-3-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/string_helpers_kunit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/tests/string_helpers_kunit.c b/lib/tests/string_helpers_kunit.c index cd08e79a857d..9fbe91079c7e 100644 --- a/lib/tests/string_helpers_kunit.c +++ b/lib/tests/string_helpers_kunit.c @@ -5,11 +5,16 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include + #include -#include +#include +#include +#include #include -#include +#include +#include #include +#include static void test_string_check_buf(struct kunit *test, const char *name, unsigned int flags, From cd2464a059d6e88eecda87d71d3dbc87175289f0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 31 Mar 2026 16:28:38 +0200 Subject: [PATCH 2089/5214] init.h: discard exitcall symbols early Any __exitcall() and built-in module_exit() handler is marked as __used, which leads to the code being included in the object file and later discarded at link time. As far as I can tell, this was originally added at the same time as initcalls were marked the same way, to prevent them from getting dropped with gcc-3.4, but it was never actaully necessary to keep exit functions around. Mark them as __maybe_unused instead, which lets the compiler treat the exitcalls as entirely unused, and make better decisions about dropping specializing static functions called from these. Link: https://lore.kernel.org/all/acruxMNdnUlyRHiy@google.com/ Link: https://lore.kernel.org/20260331142846.3187706-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Acked-by: Nicolas Schier Cc: Andriy Shevchenko Cc: Dmitry Torokhov Cc: Josh Poimboeuf Cc: Kees Cook Cc: Marco Elver Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: Petr Mladek Signed-off-by: Andrew Morton --- include/linux/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/init.h b/include/linux/init.h index 40331923b9f4..6326c61e2332 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -47,7 +47,7 @@ #define __initdata __section(".init.data") #define __initconst __section(".init.rodata") #define __exitdata __section(".exit.data") -#define __exit_call __used __section(".exitcall.exit") +#define __exit_call __maybe_unused __section(".exitcall.exit") /* * modpost check for section mismatches during the kernel build. From cae29a5787e38ce7ec7727a87ac7e393a85cb1ef Mon Sep 17 00:00:00 2001 From: Josh Law Date: Tue, 24 Mar 2026 22:32:09 +0000 Subject: [PATCH 2090/5214] lib/base64: validate before writing in decode tail path Patch series "lib/base64: decode fixes", v2. Two small fixes for lib/base64.c: 1. base64_decode() writes a decoded byte to the output buffer before validating the input in the trailing-bytes path. Move the validity checks before any writes so dst is untouched on invalid input. 2. The @padding kernel-doc for base64_decode() was copy-pasted from base64_encode() and describes the wrong direction. This patch (of 2): The trailing-bytes path in base64_decode() writes a decoded byte to the output buffer before checking whether the input characters are valid. If the input is malformed, garbage is written to dst before the function returns -1. Move the validity checks before any writes so the output buffer is left untouched on invalid input. Link: https://lore.kernel.org/20260324223210.47676-1-objecting@objecting.org Link: https://lore.kernel.org/20260324223210.47676-2-objecting@objecting.org Fixes: 9c7d3cf94d33 ("lib/base64: rework encode/decode for speed and stricter validation") Signed-off-by: Josh Law Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/base64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/base64.c b/lib/base64.c index 41961a444028..20dacee25f65 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -168,15 +168,16 @@ int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base6 return -1; val = (base64_rev_tables[s[0]] << 12) | (base64_rev_tables[s[1]] << 6); - *bp++ = val >> 10; if (srclen == 2) { if (val & 0x800003ff) return -1; + *bp++ = val >> 10; } else { val |= base64_rev_tables[s[2]]; if (val & 0x80000003) return -1; + *bp++ = val >> 10; *bp++ = val >> 2; } return bp - dst; From f424800c2a23d9fdc1355bdaf46ad2bc89741436 Mon Sep 17 00:00:00 2001 From: Josh Law Date: Tue, 24 Mar 2026 22:32:10 +0000 Subject: [PATCH 2091/5214] lib/base64: fix copy-pasted @padding doc in base64_decode() The @padding kernel-doc for base64_decode() says "whether to append '=' padding characters", which was copy-pasted from base64_encode(). In the decode context, it controls whether the input is expected to include padding, not whether to append it. Link: https://lore.kernel.org/20260324223210.47676-3-objecting@objecting.org Signed-off-by: Josh Law Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/base64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base64.c b/lib/base64.c index 20dacee25f65..325c7332b049 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -122,7 +122,7 @@ EXPORT_SYMBOL_GPL(base64_encode); * @src: the string to decode. Doesn't need to be NUL-terminated. * @srclen: the length of @src in bytes * @dst: (output) the decoded binary data - * @padding: whether to append '=' padding characters + * @padding: whether the input is expected to include '=' padding characters * @variant: which base64 variant to use * * Decodes a string using the selected Base64 variant. From 47d020e2337461f8a2991a0dc301f10a71903710 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:25 +0000 Subject: [PATCH 2092/5214] kselftest/filelock: use ksft_perror() Patch series "selftests/filelock: Make output more kselftestish", v4. This series makes the output from the ofdlocks test a bit easier for tooling to work with, and also ignores the generated file while we're here. This patch (of 3): The ofdlocks test reports some errors via perror() which does not produce KTAP output, convert to ksft_perror() which does. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-0-db8ae192ff42@kernel.org Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-1-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/ofdlocks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/filelock/ofdlocks.c b/tools/testing/selftests/filelock/ofdlocks.c index ff8d47fc373a..2d3b06ce5e5e 100644 --- a/tools/testing/selftests/filelock/ofdlocks.c +++ b/tools/testing/selftests/filelock/ofdlocks.c @@ -16,7 +16,7 @@ static int lock_set(int fd, struct flock *fl) fl->l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_SETLK, fl); if (ret) - perror("fcntl()"); + ksft_perror("fcntl()"); return ret; } @@ -28,7 +28,7 @@ static int lock_get(int fd, struct flock *fl) fl->l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_GETLK, fl); if (ret) - perror("fcntl()"); + ksft_perror("fcntl()"); return ret; } From 33d5b13098fb8db5ff120d4e2124c8b214696859 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:26 +0000 Subject: [PATCH 2093/5214] kselftest/filelock: report each test in oftlocks separately The filelock test checks four different things but only reports an overall status, convert to use ksft_test_result() for these individual tests. Each test depends on the previous ones so we still bail out if any of them fail but we get a bit more information from UIs parsing the results. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-2-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/ofdlocks.c | 90 +++++++++------------ 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/tools/testing/selftests/filelock/ofdlocks.c b/tools/testing/selftests/filelock/ofdlocks.c index 2d3b06ce5e5e..68bac28b234b 100644 --- a/tools/testing/selftests/filelock/ofdlocks.c +++ b/tools/testing/selftests/filelock/ofdlocks.c @@ -39,94 +39,82 @@ int main(void) int fd = open("/tmp/aa", O_RDWR | O_CREAT | O_EXCL, 0600); int fd2 = open("/tmp/aa", O_RDONLY); + ksft_print_header(); + ksft_set_plan(4); + unlink("/tmp/aa"); assert(fd != -1); assert(fd2 != -1); - ksft_print_msg("[INFO] opened fds %i %i\n", fd, fd2); + ksft_print_msg("opened fds %i %i\n", fd, fd2); /* Set some read lock */ fl.l_type = F_RDLCK; fl.l_start = 5; fl.l_len = 3; rc = lock_set(fd, &fl); - if (rc == 0) { - ksft_print_msg - ("[SUCCESS] set OFD read lock on first fd\n"); - } else { - ksft_print_msg("[FAIL] to set OFD read lock on first fd\n"); - return -1; - } + ksft_test_result(rc == 0, "set OFD read lock on first fd\n"); + if (rc != 0) + ksft_finished(); + /* Make sure read locks do not conflict on different fds. */ fl.l_type = F_RDLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd2, &fl); if (rc != 0) - return -1; - if (fl.l_type != F_UNLCK) { - ksft_print_msg("[FAIL] read locks conflicted\n"); - return -1; - } + ksft_finished(); + if (fl.l_type != F_UNLCK) + ksft_exit_fail_msg("read locks conflicted\n"); + /* Make sure read/write locks do conflict on different fds. */ fl.l_type = F_WRLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd2, &fl); if (rc != 0) - return -1; - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[SUCCESS] read and write locks conflicted\n"); - } else { - ksft_print_msg - ("[SUCCESS] read and write locks not conflicted\n"); - return -1; - } + ksft_finished(); + ksft_test_result(fl.l_type != F_UNLCK, + "read and write locks conflicted\n"); + if (fl.l_type == F_UNLCK) + ksft_finished(); + /* Get info about the lock on first fd. */ fl.l_type = F_UNLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd, &fl); - if (rc != 0) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK not supported\n"); - return -1; - } - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[SUCCESS] F_UNLCK test returns: locked, type %i pid %i len %zi\n", - fl.l_type, fl.l_pid, fl.l_len); - } else { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK did not return lock info\n"); - return -1; - } + if (rc != 0) + ksft_exit_fail_msg("F_OFD_GETLK with F_UNLCK not supported\n"); + ksft_test_result(fl.l_type != F_UNLCK, + "F_OFD_GETLK with F_UNLCK returned lock info\n"); + if (fl.l_type == F_UNLCK) + ksft_exit_fail(); + ksft_print_msg("F_UNLCK test returns: locked, type %i pid %i len %zi\n", + fl.l_type, fl.l_pid, fl.l_len); + /* Try the same but by locking everything by len==0. */ fl2.l_type = F_UNLCK; fl2.l_start = 0; fl2.l_len = 0; rc = lock_get(fd, &fl2); - if (rc != 0) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK not supported\n"); - return -1; - } + if (rc != 0) + ksft_exit_fail_msg + ("F_OFD_GETLK with F_UNLCK not supported\n"); + ksft_test_result(memcmp(&fl, &fl2, sizeof(fl)) == 0, + "F_UNLCK with len==0 returned the same\n"); if (memcmp(&fl, &fl2, sizeof(fl))) { - ksft_print_msg - ("[FAIL] F_UNLCK test returns: locked, type %i pid %i len %zi\n", + ksft_exit_fail_msg + ("F_UNLCK test returns: locked, type %i pid %i len %zi\n", fl.l_type, fl.l_pid, fl.l_len); - return -1; } - ksft_print_msg("[SUCCESS] F_UNLCK with len==0 returned the same\n"); + /* Get info about the lock on second fd - no locks on it. */ fl.l_type = F_UNLCK; fl.l_start = 0; fl.l_len = 0; lock_get(fd2, &fl); - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK return lock info from another fd\n"); - return -1; - } - return 0; + ksft_test_result(fl.l_type == F_UNLCK, + "F_OFD_GETLK with F_UNLCK return lock info from another fd\n"); + + ksft_finished(); } From b3c726f9f46600868bdbe4bb7f1d770fd7ebb2e6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:27 +0000 Subject: [PATCH 2094/5214] kselftest/filelock: add a .gitignore file Tell git to ignore the generated binary for the test. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-3-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/filelock/.gitignore diff --git a/tools/testing/selftests/filelock/.gitignore b/tools/testing/selftests/filelock/.gitignore new file mode 100644 index 000000000000..825e899a121b --- /dev/null +++ b/tools/testing/selftests/filelock/.gitignore @@ -0,0 +1 @@ +ofdlocks From f829d4d911cc296b32d14436a8a517e907228475 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:55 -0400 Subject: [PATCH 2095/5214] rust: uaccess: use INLINE_COPY_TO_USER to guard copy_to_user() Patch series "uaccess: unify inline vs outline copy_{from,to}_user() selection", v2. The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. The first patch of the series fixes rust/uaccess coppy_to_user() wrapper guarded with INLINE_COPY_FROM_USER. The 2nd patch switches codebase to the unified INLINE_COPY_USER. And the last patch cleans up ifdefery in the include/linux/uaccess.h This patch (of 3): The copy_to_user() rust helper is only needed when the main kernel inlines the function. It is controlled by INLINE_COPY_TO_USER, but the rust helper is protected with INLINE_COPY_FROM_USER. Fix that. Link: https://lore.kernel.org/20260425020857.356850-1-ynorov@nvidia.com Link: https://lore.kernel.org/20260425020857.356850-2-ynorov@nvidia.com Fixes: d99dc586ca7c7 ("uaccess: decouple INLINE_COPY_FROM_USER and CONFIG_RUST") Signed-off-by: Yury Norov Reported-by: Christophe Leroy (CS GROUP) Closes: https://lore.kernel.org/all/746c9c50-20c4-4dc9-a539-bf1310ff9414@kernel.org/ Cc: Alice Ryhl Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Cc: Arnd Bergmann Signed-off-by: Andrew Morton --- rust/helpers/uaccess.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index d9625b9ee046..aff22f16ab38 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -20,7 +20,9 @@ unsigned long rust_helper__copy_from_user(void *to, const void __user *from, uns { return _inline_copy_from_user(to, from, n); } +#endif +#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From c02be2ad2b88c67c5d7c06b6aa7083b5b40e1077 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:56 -0400 Subject: [PATCH 2096/5214] uaccess: unify inline vs outline copy_{from,to}_user() selection The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. Switch the codebase to the single unified INLINE_COPY_USER control. Link: https://lore.kernel.org/20260425020857.356850-3-ynorov@nvidia.com Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton --- arch/arc/include/asm/uaccess.h | 3 +-- arch/arm/include/asm/uaccess.h | 3 +-- arch/arm64/include/asm/uaccess.h | 3 +-- arch/hexagon/include/asm/uaccess.h | 3 +-- arch/loongarch/include/asm/uaccess.h | 3 +-- arch/m68k/include/asm/uaccess.h | 3 +-- arch/microblaze/include/asm/uaccess.h | 3 +-- arch/mips/include/asm/uaccess.h | 3 +-- arch/nios2/include/asm/uaccess.h | 3 +-- arch/openrisc/include/asm/uaccess.h | 3 +-- arch/parisc/include/asm/uaccess.h | 3 +-- arch/s390/include/asm/uaccess.h | 3 +-- arch/sh/include/asm/uaccess.h | 3 +-- arch/sparc/include/asm/uaccess_32.h | 3 +-- arch/sparc/include/asm/uaccess_64.h | 3 +-- arch/um/include/asm/uaccess.h | 3 +-- arch/xtensa/include/asm/uaccess.h | 3 +-- include/asm-generic/uaccess.h | 3 +-- include/linux/uaccess.h | 12 ++++++------ lib/usercopy.c | 4 +--- rust/helpers/uaccess.c | 4 +--- 21 files changed, 26 insertions(+), 48 deletions(-) diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index 1e8809ea000a..6df2209541ac 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -628,8 +628,7 @@ static inline unsigned long __clear_user(void __user *to, unsigned long n) return res; } -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #define __clear_user __clear_user diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index d6ae80b5df36..1593cf3b9800 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -616,8 +616,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) } #define __clear_user(addr, n) (memset((void __force *)addr, 0, n), 0) #endif -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER static inline unsigned long __must_check clear_user(void __user *to, unsigned long n) { diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index b0c83a08dda9..9f5bd9c69c24 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -456,8 +456,7 @@ do { \ unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0) -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n); static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n) diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h index bff77efc0d9a..1aecf60ec4f5 100644 --- a/arch/hexagon/include/asm/uaccess.h +++ b/arch/hexagon/include/asm/uaccess.h @@ -26,8 +26,7 @@ unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER __kernel_size_t __clear_user_hexagon(void __user *dest, unsigned long count); #define __clear_user(a, s) __clear_user_hexagon((a), (s)) diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 438269313e78..428f373feabf 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -292,8 +292,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * __clear_user: - Zero a block of memory in user space, with less checking. diff --git a/arch/m68k/include/asm/uaccess.h b/arch/m68k/include/asm/uaccess.h index 64914872a5c9..31d133faa45e 100644 --- a/arch/m68k/include/asm/uaccess.h +++ b/arch/m68k/include/asm/uaccess.h @@ -377,8 +377,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __constant_copy_to_user(to, from, n); return __generic_copy_to_user(to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #define __get_kernel_nofault(dst, src, type, err_label) \ do { \ diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 3aab2f17e046..afa0dd8d013f 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -250,8 +250,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_tofrom_user(to, (__force const void __user *)from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Copy a null terminated string from userspace. diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index c0cede273c7c..f00c36676b73 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -433,8 +433,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __cu_len_r; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern __kernel_size_t __bzero(void __user *addr, __kernel_size_t size); diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index 6ccc9a232c23..5e6e05cc6efc 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -57,8 +57,7 @@ extern unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); extern unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern long strncpy_from_user(char *__to, const char __user *__from, long __len); diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h index d6500a374e18..db934ebc0069 100644 --- a/arch/openrisc/include/asm/uaccess.h +++ b/arch/openrisc/include/asm/uaccess.h @@ -218,8 +218,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size) { return __copy_tofrom_user((__force void *)to, from, size); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern unsigned long __clear_user(void __user *addr, unsigned long size); diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 6c531d2c847e..0d17f81c8b27 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -197,7 +197,6 @@ unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src, unsigned long len); unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src, unsigned long len); -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #endif /* __PARISC_UACCESS_H */ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index dff035372601..a9f32c53f699 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -30,8 +30,7 @@ void debug_user_asce(int exit); #define uaccess_kmsan_or_inline __always_inline #endif -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static uaccess_kmsan_or_inline __must_check unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long size) diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h index a79609eb14be..02e7a066538e 100644 --- a/arch/sh/include/asm/uaccess.h +++ b/arch/sh/include/asm/uaccess.h @@ -95,8 +95,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Clear the area and return remaining number of bytes diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index 43284b6ec46a..5542d5b32994 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -190,8 +190,7 @@ static inline unsigned long raw_copy_from_user(void *to, const void __user *from return __copy_user((__force void __user *) to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static inline unsigned long __clear_user(void __user *addr, unsigned long size) { diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index b825a5dd0210..e2989cfba626 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -231,8 +231,7 @@ unsigned long __must_check raw_copy_from_user(void *to, unsigned long __must_check raw_copy_to_user(void __user *to, const void *from, unsigned long size); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER unsigned long __must_check raw_copy_in_user(void __user *to, const void __user *from, diff --git a/arch/um/include/asm/uaccess.h b/arch/um/include/asm/uaccess.h index 0df9ea4abda8..4417c8b1d37a 100644 --- a/arch/um/include/asm/uaccess.h +++ b/arch/um/include/asm/uaccess.h @@ -27,8 +27,7 @@ static inline int __access_ok(const void __user *ptr, unsigned long size); #define __access_ok __access_ok #define __clear_user __clear_user -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #include diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 56aec6d504fe..6538a29a2bbd 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -237,8 +237,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) prefetch(from); return __xtensa_copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * We need to return the number of bytes not cleared. Our memset() diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index b276f783494c..4569045e7139 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h @@ -91,8 +91,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) memcpy((void __force *)to, from, n); return 0; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #endif /* CONFIG_UACCESS_MEMCPY */ /* diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 56328601218c..6100f1046546 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -84,7 +84,7 @@ * the 6 functions (copy_{to,from}_user(), __copy_{to,from}_user_inatomic()) * that are used instead. Out of those, __... ones are inlined. Plain * copy_{to,from}_user() might or might not be inlined. If you want them - * inlined, have asm/uaccess.h define INLINE_COPY_{TO,FROM}_USER. + * inlined, have asm/uaccess.h define INLINE_COPY_USER. * * NOTE: only copy_from_user() zero-pads the destination in case of short copy. * Neither __copy_from_user() nor __copy_from_user_inatomic() zero anything @@ -157,7 +157,7 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) } /* - * Architectures that #define INLINE_COPY_TO_USER use this function + * Architectures that #define INLINE_COPY_USER use this function * directly in the normal copy_to/from_user(), the other ones go * through an extern _copy_to/from_user(), which expands the same code * here. @@ -190,7 +190,7 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_FROM_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_from_user(void *, const void __user *, unsigned long); #endif @@ -207,7 +207,7 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_TO_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,7 +217,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER return _inline_copy_from_user(to, from, n); #else return _copy_from_user(to, from, n); @@ -230,7 +230,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) if (!check_copy_size(from, n, true)) return n; -#ifdef INLINE_COPY_TO_USER +#ifdef INLINE_COPY_USER return _inline_copy_to_user(to, from, n); #else return _copy_to_user(to, from, n); diff --git a/lib/usercopy.c b/lib/usercopy.c index b00a3a957de6..e2f0bf104a59 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c @@ -12,15 +12,13 @@ /* out-of-line parts */ -#if !defined(INLINE_COPY_FROM_USER) +#if !defined(INLINE_COPY_USER) unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } EXPORT_SYMBOL(_copy_from_user); -#endif -#if !defined(INLINE_COPY_TO_USER) unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n) { return _inline_copy_to_user(to, from, n); diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index aff22f16ab38..6e59cc9c665c 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -14,15 +14,13 @@ rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) return copy_to_user(to, from, n); } -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER __rust_helper unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } -#endif -#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From bd99fcfc6219ebe36ae4d0bf5333b5ecc17b53df Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:57 -0400 Subject: [PATCH 2097/5214] uaccess: minimize INLINE_COPY_USER-related ifdefery Now that we've got the same config selecting inline vs outline copy_to_user() and copy_from_user(), we can simplify the corresponding logic in the uaccess.h. Link: https://lore.kernel.org/20260425020857.356850-4-ynorov@nvidia.com Fixes: 1f9a8286bc0c ("uaccess: always export _copy_[from|to]_user with CONFIG_RUST") Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton --- include/linux/uaccess.h | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 6100f1046546..e0c3d6e29301 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -190,10 +190,6 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_USER -extern __must_check unsigned long -_copy_from_user(void *, const void __user *, unsigned long); -#endif static inline __must_check unsigned long _inline_copy_to_user(void __user *to, const void *from, unsigned long n) @@ -207,7 +203,13 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_USER +#ifdef INLINE_COPY_USER +# define _copy_to_user _inline_copy_to_user +# define _copy_from_user _inline_copy_from_user +#else +extern __must_check unsigned long +_copy_from_user(void *, const void __user *, unsigned long); + extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,11 +219,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_USER - return _inline_copy_from_user(to, from, n); -#else return _copy_from_user(to, from, n); -#endif } static __always_inline unsigned long __must_check @@ -229,12 +227,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) { if (!check_copy_size(from, n, true)) return n; - -#ifdef INLINE_COPY_USER - return _inline_copy_to_user(to, from, n); -#else return _copy_to_user(to, from, n); -#endif } #ifndef copy_mc_to_kernel From 5a13e296a3e32a70db2a520d8611a53d7bde10e8 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Thu, 30 Apr 2026 16:15:33 +0200 Subject: [PATCH 2098/5214] kcov: refactor common handle ID into kcov_common_handle_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store common handle IDs in "struct kcov_common_handle_id", which consumes no space in non-KCOV builds. This cleanup removes #ifdef boilerplate code from subsystems that integrate with KCOV (in particular in usbip_common.h and skbuff.h, see the diffstat). This should also make it easier to add KCOV remote coverage to more subsystems in the future. Link: https://lore.kernel.org/20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com Signed-off-by: Jann Horn Acked-by: Greg Kroah-Hartman Reviewed-by: Dmitry Vyukov Acked-by: Jakub Kicinski Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Eugenio Pérez Cc: Hongren (Zenithal) Zheng Cc: Jann Horn Cc: Jason Wang Cc: "Michael S. Tsirkin" Cc: Shuah Khan Cc: Valentina Manea Signed-off-by: Andrew Morton --- drivers/usb/usbip/usbip_common.h | 29 +---------------------------- drivers/usb/usbip/vhci_rx.c | 4 ++-- drivers/usb/usbip/vhci_sysfs.c | 2 +- drivers/vhost/vhost.h | 2 +- include/linux/kcov.h | 12 ++++++------ include/linux/skbuff.h | 14 +++----------- include/linux/types.h | 6 ++++++ kernel/kcov.c | 6 +++--- 8 files changed, 23 insertions(+), 52 deletions(-) diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 282efca64a01..be4c5e65a7f8 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -282,9 +282,7 @@ struct usbip_device { void (*unusable)(struct usbip_device *); } eh_ops; -#ifdef CONFIG_KCOV - u64 kcov_handle; -#endif + struct kcov_common_handle_id kcov_handle; }; #define kthread_get_run(threadfn, data, namefmt, ...) \ @@ -339,29 +337,4 @@ static inline int interface_to_devnum(struct usb_interface *interface) return udev->devnum; } -#ifdef CONFIG_KCOV - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) -{ - ud->kcov_handle = kcov_common_handle(); -} - -static inline void usbip_kcov_remote_start(struct usbip_device *ud) -{ - kcov_remote_start_common(ud->kcov_handle); -} - -static inline void usbip_kcov_remote_stop(void) -{ - kcov_remote_stop(); -} - -#else /* CONFIG_KCOV */ - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_start(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_stop(void) { } - -#endif /* CONFIG_KCOV */ - #endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index a75f4a898a41..a678e7c89837 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -261,9 +261,9 @@ int vhci_rx_loop(void *data) if (usbip_event_happened(ud)) break; - usbip_kcov_remote_start(ud); + kcov_remote_start_common(ud->kcov_handle); vhci_rx_pdu(ud); - usbip_kcov_remote_stop(); + kcov_remote_stop(); } return 0; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 5bc8c47788d4..b98d14c43d13 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -425,7 +425,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr, vdev->ud.tcp_rx = tcp_rx; vdev->ud.tcp_tx = tcp_tx; vdev->ud.status = VDEV_ST_NOTASSIGNED; - usbip_kcov_handle_init(&vdev->ud); + vdev->ud.kcov_handle = kcov_common_handle(); spin_unlock(&vdev->ud.lock); spin_unlock_irqrestore(&vhci->lock, flags); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 4fe99765c5c7..0192ade6e749 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -44,7 +44,7 @@ struct vhost_worker { /* Used to serialize device wide flushing with worker swapping. */ struct mutex mutex; struct llist_head work_list; - u64 kcov_handle; + struct kcov_common_handle_id kcov_handle; u32 id; int attachment_cnt; bool killed; diff --git a/include/linux/kcov.h b/include/linux/kcov.h index 0143358874b0..cdb72b3859d8 100644 --- a/include/linux/kcov.h +++ b/include/linux/kcov.h @@ -43,11 +43,11 @@ do { \ /* See Documentation/dev-tools/kcov.rst for usage details. */ void kcov_remote_start(u64 handle); void kcov_remote_stop(void); -u64 kcov_common_handle(void); +struct kcov_common_handle_id kcov_common_handle(void); -static inline void kcov_remote_start_common(u64 id) +static inline void kcov_remote_start_common(struct kcov_common_handle_id id) { - kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id)); + kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id.val)); } static inline void kcov_remote_start_usb(u64 id) @@ -99,11 +99,11 @@ static inline void kcov_prepare_switch(struct task_struct *t) {} static inline void kcov_finish_switch(struct task_struct *t) {} static inline void kcov_remote_start(u64 handle) {} static inline void kcov_remote_stop(void) {} -static inline u64 kcov_common_handle(void) +static inline struct kcov_common_handle_id kcov_common_handle(void) { - return 0; + return (struct kcov_common_handle_id){}; } -static inline void kcov_remote_start_common(u64 id) {} +static inline void kcov_remote_start_common(struct kcov_common_handle_id id) {} static inline void kcov_remote_start_usb(u64 id) {} static inline void kcov_remote_start_usb_softirq(u64 id) {} static inline void kcov_remote_stop_softirq(void) {} diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2bcf78a4de7b..a3fe418f7ced 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1082,9 +1082,7 @@ struct sk_buff { __u16 network_header; __u16 mac_header; -#ifdef CONFIG_KCOV - u64 kcov_handle; -#endif + struct kcov_common_handle_id kcov_handle; ); /* end headers group */ @@ -5437,20 +5435,14 @@ static inline void skb_reset_csum_not_inet(struct sk_buff *skb) } static inline void skb_set_kcov_handle(struct sk_buff *skb, - const u64 kcov_handle) + struct kcov_common_handle_id kcov_handle) { -#ifdef CONFIG_KCOV skb->kcov_handle = kcov_handle; -#endif } -static inline u64 skb_get_kcov_handle(struct sk_buff *skb) +static inline struct kcov_common_handle_id skb_get_kcov_handle(struct sk_buff *skb) { -#ifdef CONFIG_KCOV return skb->kcov_handle; -#else - return 0; -#endif } static inline void skb_mark_for_recycle(struct sk_buff *skb) diff --git a/include/linux/types.h b/include/linux/types.h index 608050dbca6a..93166b0b0617 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -224,6 +224,12 @@ struct ustat { char f_fpack[6]; }; +struct kcov_common_handle_id { +#ifdef CONFIG_KCOV + u64 val; +#endif +}; + /** * struct callback_head - callback structure for use with RCU and task_work * @next: next update requests in a list diff --git a/kernel/kcov.c b/kernel/kcov.c index 0b369e88c7c9..a43e33a28adb 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1083,11 +1083,11 @@ void kcov_remote_stop(void) EXPORT_SYMBOL(kcov_remote_stop); /* See the comment before kcov_remote_start() for usage details. */ -u64 kcov_common_handle(void) +struct kcov_common_handle_id kcov_common_handle(void) { if (!in_task()) - return 0; - return current->kcov_handle; + return (struct kcov_common_handle_id){ .val = 0 }; + return (struct kcov_common_handle_id){ .val = current->kcov_handle }; } EXPORT_SYMBOL(kcov_common_handle); From fc15e3a30ddd950f009c76765331783b9af94a87 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2026 10:51:56 +0300 Subject: [PATCH 2099/5214] rapidio/tsi721: prevent a bad dereference in tsi721_db_dpc() With a list_for_each() loop, if we don't find the item we are looking for in the list, then the loop exits with the iterator, which is "dbell" in this loop, pointing to invalid memory. This code uses the "found" variable to determine if we have found the doorbell we are looking for or not. However, the problem that the "found" variable needs to be set to false at the start of each iteration, otherwise after the first correct doorbell, then everything is marked as found. Reset the "found" to false at the start of the iteration and move the variable inside the loop. Link: https://lore.kernel.org/af2WHMZiqMwdYveO@stanley.mountain Fixes: 48618fb4e522 ("RapidIO: add mport driver for Tsi721 bridge") Signed-off-by: Dan Carpenter Cc: Alexandre Bounine Cc: Chul Kim Cc: Matt Porter Signed-off-by: Andrew Morton --- drivers/rapidio/devices/tsi721.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 66331e67cf4e..71b87bf8c31d 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -394,7 +394,6 @@ static void tsi721_db_dpc(struct work_struct *work) idb_work); struct rio_mport *mport; struct rio_dbell *dbell; - int found = 0; u32 wr_ptr, rd_ptr; u64 *idb_entry; u32 regval; @@ -412,6 +411,8 @@ static void tsi721_db_dpc(struct work_struct *work) rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE; while (wr_ptr != rd_ptr) { + int found = 0; + idb_entry = (u64 *)(priv->idb_base + (TSI721_IDB_ENTRY_SIZE * rd_ptr)); rd_ptr++; From 83544c68f71da543f59fdc9b30c1f5ff66de1a25 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 7 May 2026 10:41:46 -0700 Subject: [PATCH 2100/5214] MAINTAINERS/CREDITS: remove inactive checkpatch reviewers Dwaipayan Ray and Lukas Bulwahn have not commented on checkpatch in several years. Lukas is still active on MAINTAINERS. Create an entry in CREDITS for Dwaipayan. Link: https://lore.kernel.org/64f057d1d7f247583eb616337b89b3ff7bcc627f.camel@perches.com Signed-off-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- CREDITS | 4 ++++ MAINTAINERS | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CREDITS b/CREDITS index 17962bdd6dbd..102a20dbd03f 100644 --- a/CREDITS +++ b/CREDITS @@ -3368,6 +3368,10 @@ N: Anil Ravindranath E: anil_ravindranath@pmc-sierra.com D: PMC-Sierra MaxRAID driver +N: Dwaipayan Ray +E: dwaipayanray1@gmail.com +D: checkpatch improvements + N: Eric S. Raymond E: esr@thyrsus.com W: http://www.tuxedo.org/~esr/ diff --git a/MAINTAINERS b/MAINTAINERS index 461a3eed6129..236b0785bf3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5969,8 +5969,6 @@ F: drivers/input/keyboard/charlieplex_keypad.c CHECKPATCH M: Andy Whitcroft M: Joe Perches -R: Dwaipayan Ray -R: Lukas Bulwahn S: Maintained F: scripts/checkpatch.pl From e4f5f6f3c199ae7fbe142da6b79a97a504ac7e55 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 7 May 2026 11:52:37 +0200 Subject: [PATCH 2101/5214] kfence: fix KASAN HW tags bypass via runtime sample_interval change If a user writes a non-zero value to the sample_interval module parameter at runtime, the missing KASAN HW tags check in the late init path allows KFENCE to be enabled alongside KASAN HW tags, bypassing the boot restriction. This patch adds the missing check to param_set_sample_interval() to reject the parameter change if KASAN HW tags are enabled. Link: https://lore.kernel.org/20260507095237.741017-1-glider@google.com Fixes: 09833d99db36 ("mm/kfence: disable KFENCE upon KASAN HW tags enablement") Signed-off-by: Alexander Potapenko Cc: Marco Elver Cc: Greg Thelen Cc: Roman Gushchin Cc: Pimyn Girgis Signed-off-by: Andrew Morton --- mm/kfence/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 655dc5ce3240..ee6ae01de5ae 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -77,6 +77,11 @@ static int param_set_sample_interval(const char *val, const struct kernel_param WRITE_ONCE(kfence_enabled, false); } + if (num && kasan_hw_tags_enabled()) { + pr_info("disabled as KASAN HW tags are enabled\n"); + return -EINVAL; + } + *((unsigned long *)kp->arg) = num; if (num && !READ_ONCE(kfence_enabled) && system_state != SYSTEM_BOOTING) From 25c786a9f7f8b5e882367f569020f1f37ac9fea7 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 7 May 2026 11:49:19 +0200 Subject: [PATCH 2102/5214] llist: make locking comments consistent llist's locking requirement table has a legend which claims that all operations not needing a lock a marked with '-', whereas in truth for some table entries just a whitespace is used. Add the '-' to all appropriate places. Link: https://lore.kernel.org/20260507094918.23910-2-phasta@kernel.org Signed-off-by: Philipp Stanner Cc: Jens Axboe Cc: "Paul E . McKenney" Cc: Shakeel Butt Cc: Tejun Heo Signed-off-by: Andrew Morton --- include/linux/llist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/llist.h b/include/linux/llist.h index 607b2360c938..8846b7709669 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -26,8 +26,8 @@ * * | add | del_first | del_all * add | - | - | - - * del_first | | L | L - * del_all | | | - + * del_first | - | L | L + * del_all | - | - | - * * Where, a particular row's operation can happen concurrently with a column's * operation, with "-" being no lock needed, while "L" being lock is needed. From 56cb9b7d96b28a1173a510ab25354b6599ad3a33 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Mon, 11 May 2026 12:50:52 +0200 Subject: [PATCH 2103/5214] gcov: use atomic counter updates to fix concurrent access crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC's GCOV instrumentation can merge global branch counters with loop induction variables as an optimization. In inflate_fast(), the inner copy loops get transformed so that the GCOV counter value is loaded multiple times to compute the loop base address, start index, and end bound. Since GCOV counters are global (not per-CPU), concurrent execution on different CPUs causes the counter to change between loads, producing inconsistent values and out-of-bounds memory writes. The crash manifests during IPComp (IP Payload Compression) processing when inflate_fast() runs concurrently on multiple CPUs: BUG: unable to handle page fault for address: ffffd0a3c0902ffa RIP: inflate_fast+1431 Call Trace: zlib_inflate __deflate_decompress crypto_comp_decompress ipcomp_decompress [xfrm_ipcomp] ipcomp_input [xfrm_ipcomp] xfrm_input At the crash point, the compiler generated three loads from the same global GCOV counter (__gcov0.inflate_fast+216) to compute base, start, and end for an indexed loop. Another CPU modified the counter between loads, making the values inconsistent - the write went 3.4 MB past a 65 KB buffer. Add -fprofile-update=prefer-atomic to CFLAGS_GCOV at the global level in the top-level Makefile, guarded by a try-run compile test. The test compiles a minimal program with and without -fprofile-update=prefer-atomic using the full KBUILD_CFLAGS, then compares undefined symbols in the resulting object files. If prefer-atomic introduces new undefined references (such as __atomic_fetch_add_8 on i386 or __aarch64_ldadd8_relax on arm64 with outline-atomics), the flag is not added -- the kernel does not link against libatomic. On architectures where GCC inlines 64-bit atomic counter updates (x86_64, s390, ...) the test passes and the flag is enabled, preventing the compiler from merging counters with loop induction variables and fixing the observed concurrent-access crash. On architectures where the flag would introduce libatomic dependencies, it is silently omitted and behaviour is no worse than before this patch. Move the CFLAGS_GCOV block from its original position (before the arch Makefile include) to after the core KBUILD_CFLAGS assignments but before the scripts/Makefile.gcc-plugins include. This placement ensures the try-run test sees arch-specific flags (-m32, -march=, -mno-outline-atomics) while avoiding GCC plugin flags (-fplugin=) that would break the test on clean builds when plugin shared objects do not yet exist. Link: https://lore.kernel.org/20260511105052.417187-2-khorenko@virtuozzo.com Signed-off-by: Konstantin Khorenko Tested-by: Arnd Bergmann Tested-by: Peter Oberparleiter Reviewed-by: Peter Oberparleiter Cc: Masahiro Yamada Cc: Miguel Ojeda Cc: Mikhail Zaslonko Cc: Nathan Chancellor Cc: Pavel Tikhomirov Cc: Thomas Weißschuh Cc: Signed-off-by: Andrew Morton --- Makefile | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f056c921ea9c..8813f24b1ff4 100644 --- a/Makefile +++ b/Makefile @@ -826,12 +826,6 @@ endif # KBUILD_EXTMOD # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -endif -export CFLAGS_GCOV - # The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later. ifdef CONFIG_FUNCTION_TRACER CC_FLAGS_FTRACE := -pg @@ -1149,6 +1143,27 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_GCOV += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_GCOV += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_GCOV + # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree KBUILD_CPPFLAGS += -fmacro-prefix-map=$(srcroot)/= From 738ce4979c802ba0f1f8249a893aa7db1fbb2cf8 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:16:00 +0800 Subject: [PATCH 2104/5214] ocfs2: reject inconsistent inode size before truncate [BUG] openat(..., O_WRONLY|O_CREAT|O_TRUNC) can hit: kernel BUG at fs/ocfs2/file.c:454! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_truncate_file+0x1204/0x13c0 fs/ocfs2/file.c:454 Call Trace: ocfs2_setattr+0xa6d/0x1fd0 fs/ocfs2/file.c:1212 notify_change+0x4b5/0x1030 fs/attr.c:546 do_truncate+0x1d2/0x230 fs/open.c:68 handle_truncate fs/namei.c:3596 [inline] do_open fs/namei.c:3979 [inline] path_openat+0x260f/0x2ce0 fs/namei.c:4134 do_filp_open+0x1f6/0x430 fs/namei.c:4161 do_sys_openat2+0x117/0x1c0 fs/open.c:1437 do_sys_open fs/open.c:1452 [inline] __do_sys_openat fs/open.c:1468 [inline] __se_sys_openat fs/open.c:1463 [inline] __x64_sys_openat+0x15b/0x220 fs/open.c:1463 ... [CAUSE] ocfs2_truncate_file() treats di_bh->i_size matching inode->i_size as an internal code invariant and BUGs if it is broken. That assumption is too strong for corrupted metadata. The dinode block can still be structurally valid enough to pass ocfs2_read_inode_block() while no longer matching an already-instantiated VFS inode. On local mounts, ocfs2_inode_lock_update() skips refresh entirely, so truncate can observe the mismatch directly and crash instead of rejecting the corruption. [FIX] Turn the BUG_ON into normal OCFS2 corruption handling. If truncate sees di_bh->i_size disagree with inode->i_size, report it with ocfs2_error() and abort before touching truncate state. This keeps the fix at the first boundary that actually requires the sizes to match and avoids widening checks into hotter generic inode-lock paths Link: https://lore.kernel.org/20260512021601.3936417-1-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/file.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 7df9921c1a38..d6e977ba6565 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -444,21 +444,26 @@ int ocfs2_truncate_file(struct inode *inode, struct ocfs2_dinode *fe = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - /* We trust di_bh because it comes from ocfs2_inode_lock(), which - * already validated it */ + /* + * On local mounts ocfs2_inode_lock_update() skips the inode + * refresh path, so truncation still needs to reject an inode + * state that no longer matches di_bh. + */ fe = (struct ocfs2_dinode *) di_bh->b_data; trace_ocfs2_truncate_file((unsigned long long)OCFS2_I(inode)->ip_blkno, (unsigned long long)le64_to_cpu(fe->i_size), (unsigned long long)new_i_size); - mlog_bug_on_msg(le64_to_cpu(fe->i_size) != i_size_read(inode), - "Inode %llu, inode i_size = %lld != di " - "i_size = %llu, i_flags = 0x%x\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - i_size_read(inode), - (unsigned long long)le64_to_cpu(fe->i_size), - le32_to_cpu(fe->i_flags)); + if (unlikely(le64_to_cpu(fe->i_size) != i_size_read(inode))) { + status = ocfs2_error(inode->i_sb, + "Inode %llu has inconsistent i_size: inode = %lld, dinode = %llu, i_flags = 0x%x\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + i_size_read(inode), + (unsigned long long)le64_to_cpu(fe->i_size), + le32_to_cpu(fe->i_flags)); + goto bail; + } if (new_i_size > le64_to_cpu(fe->i_size)) { trace_ocfs2_truncate_file_error( From c0438198c28b1d22c272751af5e717c11d9fa8dd Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:41:15 +0800 Subject: [PATCH 2105/5214] ocfs2: don't BUG_ON an invalid journal dinode [BUG] A fuzzed OCFS2 image can corrupt the current slot journal dinode while mount is still in progress. The mount path first reports the invalid journal block and then crashes in shutdown: kernel BUG at fs/ocfs2/journal.c:1034! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_journal_toggle_dirty+0x2d6/0x340 fs/ocfs2/journal.c:1034 Call Trace: ocfs2_journal_shutdown+0x414/0xc30 fs/ocfs2/journal.c:1116 ocfs2_mount_volume fs/ocfs2/super.c:1785 [inline] ocfs2_fill_super+0x30a9/0x3cd0 fs/ocfs2/super.c:1083 get_tree_bdev_flags+0x38b/0x640 fs/super.c:1698 get_tree_bdev+0x24/0x40 fs/super.c:1721 ocfs2_get_tree+0x21/0x30 fs/ocfs2/super.c:1184 vfs_get_tree+0x9a/0x370 fs/super.c:1758 fc_mount fs/namespace.c:1199 [inline] do_new_mount_fc fs/namespace.c:3642 [inline] do_new_mount fs/namespace.c:3718 [inline] path_mount+0x5b8/0x1ea0 fs/namespace.c:4028 do_mount fs/namespace.c:4041 [inline] __do_sys_mount fs/namespace.c:4229 [inline] __se_sys_mount fs/namespace.c:4206 [inline] __x64_sys_mount+0x282/0x320 fs/namespace.c:4206 ... [CAUSE] ocfs2_journal_toggle_dirty() used to return -EIO when journal->j_bh no longer contained a valid dinode, because the startup and shutdown paths already handled that failure. Commit 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") changed the check to a BUG_ON() under the assumption that the journal dinode had already been validated. That turns an unexpected invalid journal dinode during mount teardown into a kernel crash instead of a normal mount failure. [FIX] Replace the BUG_ON() with WARN_ON() and return -EIO. This keeps the invariant warning for debugging, but restores the original behavior of failing startup or shutdown cleanly instead of panicking the kernel. Link: https://lore.kernel.org/20260512024115.4036371-1-gality369@gmail.com Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/journal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index f9bf3bac085d..fc54cc798ce3 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1022,11 +1022,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, struct ocfs2_dinode *fe; fe = (struct ocfs2_dinode *)bh->b_data; - - /* The journal bh on the osb always comes from ocfs2_journal_init() - * and was validated there inside ocfs2_inode_lock_full(). It's a - * code bug if we mess it up. */ - BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); + if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe))) + return -EIO; flags = le32_to_cpu(fe->id1.journal1.ij_flags); if (dirty) From bef1006da49c91e8e154223d3005829a394f8f78 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:10 +0800 Subject: [PATCH 2106/5214] ocfs2: validate inline xattr header before ibody lookups Patch series "ocfs2: validate inline xattr header consumers". Corrupt i_xattr_inline_size can move the computed inode-body xattr header outside the dinode block. Several OCFS2 paths then trust xh_count or xattr entry geometry from that unchecked header. The reported KASAN splat hits the ibody lookup path: BUG: KASAN: use-after-free in ocfs2_xattr_find_entry+0x37b/0x3a0 ocfs2_xattr_ibody_get() ocfs2_xattr_get_nolock() ocfs2_calc_xattr_init() The same unchecked header derivation also exists in the outside-value probe, ibody remove, inline refcount attach, and inline reflink paths. This series factors the existing ibody list validation into a shared helper and then converts the remaining inline-header consumers one at a time. Patch layout: 1. validate ibody get/find and reuse the helper in ibody list 2. validate the outside-value probe 3. validate ibody remove 4. validate inline refcount attach 5. validate inline reflink This patch (of 5): [BUG] mknodat() can read past the end of a dinode block when ACL inheritance walks a corrupted inode-body xattr header. Another report shows the same unchecked lookup later faulting in the VFS open path after create returns a garbage status. KASAN: use-after-free in ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078 Read of size 2 at addr ffff88801c520300 by task syz.0.10/360 Trace: ... ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078 ocfs2_xattr_ibody_get fs/ocfs2/xattr.c:1178 [inline] ocfs2_xattr_get_nolock+0x2ee/0x1110 fs/ocfs2/xattr.c:1309 ocfs2_calc_xattr_init+0x716/0xac0 fs/ocfs2/xattr.c:628 ocfs2_mknod+0x935/0x2400 fs/ocfs2/namei.c:333 ocfs2_create+0x158/0x390 fs/ocfs2/namei.c:676 vfs_create fs/namei.c:3493 [inline] vfs_create+0x445/0x6f0 fs/namei.c:3477 do_mknodat+0x2d8/0x5e0 fs/namei.c:4372 __do_sys_mknodat fs/namei.c:4400 [inline] __se_sys_mknodat fs/namei.c:4397 [inline] __x64_sys_mknodat+0xb6/0xf0 fs/namei.c:4397 ... Another report: BUG: unable to handle page fault for address: fffffbfff3e40ec0 RIP: 0010:__d_entry_type include/linux/dcache.h:414 [inline] RIP: 0010:d_can_lookup include/linux/dcache.h:429 [inline] RIP: 0010:d_is_dir include/linux/dcache.h:439 [inline] RIP: 0010:path_openat+0xe2f/0x2ce0 fs/namei.c:4134 Trace: ... do_filp_open+0x1f6/0x430 fs/namei.c:4161 do_sys_openat2+0x117/0x1c0 fs/open.c:1437 __x64_sys_openat+0x15b/0x220 fs/open.c:1463 ... [CAUSE] ocfs2_xattr_ibody_list() already validates the inline xattr size and entry count, but ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find() still derive the inline header directly from di->i_xattr_inline_size and then trust xh_count. A corrupted inline size or entry count can therefore move the computed header outside the dinode block before get/find start walking it. That can either make ocfs2_xattr_find_entry() dereference xs->header->xh_count outside the block or make ocfs2_xattr_get_nolock() bubble a garbage status back through ocfs2_calc_xattr_init() into the create/open path. [FIX] Factor the existing ibody header geometry checks into a shared helper. Use it in ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find(), and have ocfs2_xattr_ibody_list() reuse the same helper instead of open-coding the validation. Reject corrupt ibody metadata with -EFSCORRUPTED before the lookup path can walk bogus xattr geometry or return a garbage status. Link: https://lore.kernel.org/20260508085914.61647-1-gality369@gmail.com Link: https://lore.kernel.org/20260508085914.61647-2-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Jia-Ju Bai Cc: Zixuan Fu Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 82 +++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 86cfd4c2adf9..3a5a17cdcf7e 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -950,6 +950,41 @@ static int ocfs2_xattr_list_entries(struct inode *inode, return result; } +static int ocfs2_xattr_ibody_lookup_header(struct inode *inode, + struct ocfs2_dinode *di, + struct ocfs2_xattr_header **header) +{ + u16 xattr_count; + size_t max_entries; + u16 inline_size = le16_to_cpu(di->i_xattr_inline_size); + + if (inline_size > inode->i_sb->s_blocksize || + inline_size < sizeof(struct ocfs2_xattr_header)) { + ocfs2_error(inode->i_sb, + "Invalid xattr inline size %u in inode %llu\n", + inline_size, + (unsigned long long)OCFS2_I(inode)->ip_blkno); + return -EFSCORRUPTED; + } + + *header = (struct ocfs2_xattr_header *) + ((void *)di + inode->i_sb->s_blocksize - inline_size); + + xattr_count = le16_to_cpu((*header)->xh_count); + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) / + sizeof(struct ocfs2_xattr_entry); + + if (xattr_count > max_entries) { + ocfs2_error(inode->i_sb, + "xattr entry count %u exceeds maximum %zu in inode %llu\n", + xattr_count, max_entries, + (unsigned long long)OCFS2_I(inode)->ip_blkno); + return -EFSCORRUPTED; + } + + return 0; +} + int ocfs2_has_inline_xattr_value_outside(struct inode *inode, struct ocfs2_dinode *di) { @@ -975,39 +1010,13 @@ static int ocfs2_xattr_ibody_list(struct inode *inode, struct ocfs2_xattr_header *header = NULL; struct ocfs2_inode_info *oi = OCFS2_I(inode); int ret = 0; - u16 xattr_count; - size_t max_entries; - u16 inline_size; if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) return ret; - inline_size = le16_to_cpu(di->i_xattr_inline_size); - - /* Validate inline size is reasonable */ - if (inline_size > inode->i_sb->s_blocksize || - inline_size < sizeof(struct ocfs2_xattr_header)) { - ocfs2_error(inode->i_sb, - "Invalid xattr inline size %u in inode %llu\n", - inline_size, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - return -EFSCORRUPTED; - } - - header = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - inline_size); - - xattr_count = le16_to_cpu(header->xh_count); - max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) / - sizeof(struct ocfs2_xattr_entry); - - if (xattr_count > max_entries) { - ocfs2_error(inode->i_sb, - "xattr entry count %u exceeds maximum %zu in inode %llu\n", - xattr_count, max_entries, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - return -EFSCORRUPTED; - } + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); @@ -1200,8 +1209,9 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, return -ENODATA; xs->end = (void *)di + inode->i_sb->s_blocksize; - xs->header = (struct ocfs2_xattr_header *) - (xs->end - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header); + if (ret) + return ret; xs->base = (void *)xs->header; xs->here = xs->header->xh_entries; @@ -2726,12 +2736,14 @@ static int ocfs2_xattr_ibody_find(struct inode *inode, xs->xattr_bh = xs->inode_bh; xs->end = (void *)di + inode->i_sb->s_blocksize; - if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) - xs->header = (struct ocfs2_xattr_header *) - (xs->end - le16_to_cpu(di->i_xattr_inline_size)); - else + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header); + if (ret) + return ret; + } else { xs->header = (struct ocfs2_xattr_header *) (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size); + } xs->base = (void *)xs->header; xs->here = xs->header->xh_entries; From a61b83dd83ed44e937de7aead2b4ddd3ad32e3f8 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:11 +0800 Subject: [PATCH 2107/5214] ocfs2: validate inline xattr header before checking outside values [BUG] A corrupt inline xattr header can make ocfs2_has_inline_xattr_value_outside() walk xh_count from an unchecked header while refcount-tree teardown decides whether inline xattrs still point outside the inode body. [CAUSE] ocfs2_has_inline_xattr_value_outside() still computed the inline header directly from di->i_xattr_inline_size and immediately iterated xh_count. That is the same unchecked metadata boundary as the ibody lookup bug. [FIX] Reuse the shared inline-header helper before iterating xh_count. Because this helper returns a boolean-style answer to its caller, treat a corrupt header conservatively as "has outside values" instead of walking it. Link: https://lore.kernel.org/20260508085914.61647-3-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 3a5a17cdcf7e..05f6f0a886cf 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -989,11 +989,12 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode, struct ocfs2_dinode *di) { struct ocfs2_xattr_header *xh; + int ret; int i; - xh = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xh); + if (ret) + return 1; for (i = 0; i < le16_to_cpu(xh->xh_count); i++) if (!ocfs2_xattr_is_local(&xh->xh_entries[i])) From b8ba8bbe69ad8a37e2f9bc2792c1b825f1964c91 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:12 +0800 Subject: [PATCH 2108/5214] ocfs2: validate inline xattr header before ibody remove [BUG] A corrupt inline xattr header can make ocfs2_xattr_ibody_remove() pass an unchecked header into ocfs2_remove_value_outside() during inode xattr teardown. [CAUSE] ocfs2_xattr_ibody_remove() still rebuilt the ibody xattr header directly from di->i_xattr_inline_size and then handed it to code that iterates xh_count and entry geometry. [FIX] Validate the inline xattr header with the shared helper before handing it to the outside-value removal path, and propagate -EFSCORRUPTED on bad metadata instead of traversing the unchecked header. Link: https://lore.kernel.org/20260508085914.61647-4-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 05f6f0a886cf..bbb25a01b097 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -2476,9 +2476,9 @@ static int ocfs2_xattr_ibody_remove(struct inode *inode, .vb_access = ocfs2_journal_access_di, }; - header = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; ret = ocfs2_remove_value_outside(inode, &vb, header, ref_ci, ref_root_bh); From 4523ba0ee2e9ab6ee9c4b20b2867c3e4aa01f503 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:13 +0800 Subject: [PATCH 2109/5214] ocfs2: validate inline xattr header before inline refcount attach [BUG] A corrupt inline xattr header can make ocfs2_xattr_inline_attach_refcount() feed an unchecked header into the refcount-attachment walk for inline xattr values. [CAUSE] The inline refcount-attach path still derived the header directly from di->i_xattr_inline_size and then passed it to code that iterates xh_count and xattr entries. [FIX] Use the shared ibody header helper before attaching refcounts to inline xattr values so corrupt header geometry is rejected with -EFSCORRUPTED instead of being traversed. Link: https://lore.kernel.org/20260508085914.61647-5-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index bbb25a01b097..4877406a83ce 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6016,14 +6016,17 @@ static int ocfs2_xattr_inline_attach_refcount(struct inode *inode, struct ocfs2_cached_dealloc_ctxt *dealloc) { struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data; - struct ocfs2_xattr_header *header = (struct ocfs2_xattr_header *) - (fe_bh->b_data + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + struct ocfs2_xattr_header *header; + int ret; struct ocfs2_xattr_value_buf vb = { .vb_bh = fe_bh, .vb_access = ocfs2_journal_access_di, }; + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; + return ocfs2_xattr_attach_refcount_normal(inode, &vb, header, ref_ci, ref_root_bh, dealloc); } From bda614e6b4f27d3535ee86a96a6bab3b9b4f5e87 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:14 +0800 Subject: [PATCH 2110/5214] ocfs2: validate inline xattr header before reflinking inline xattrs [BUG] A corrupt inline xattr header can make ocfs2_reflink_xattr_inline() lock, copy, and reflink xattr state from an unchecked ibody xattr header. [CAUSE] The inline reflink path still trusted di->i_xattr_inline_size to compute header_off, xh, and new_xh before handing the source header to the reflink allocator and copy logic. [FIX] Validate the source inode's inline xattr header with the shared helper first, then derive the reflink copy offsets from the validated inline size/header. This keeps the reflink path from traversing corrupt ibody xattr geometry. Link: https://lore.kernel.org/20260508085914.61647-6-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 4877406a83ce..fcddd3c13acd 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6511,12 +6511,10 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args) handle_t *handle; struct ocfs2_super *osb = OCFS2_SB(args->old_inode->i_sb); struct ocfs2_dinode *di = (struct ocfs2_dinode *)args->old_bh->b_data; - int inline_size = le16_to_cpu(di->i_xattr_inline_size); - int header_off = osb->sb->s_blocksize - inline_size; - struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *) - (args->old_bh->b_data + header_off); - struct ocfs2_xattr_header *new_xh = (struct ocfs2_xattr_header *) - (args->new_bh->b_data + header_off); + int inline_size; + int header_off; + struct ocfs2_xattr_header *xh; + struct ocfs2_xattr_header *new_xh; struct ocfs2_alloc_context *meta_ac = NULL; struct ocfs2_inode_info *new_oi; struct ocfs2_dinode *new_di; @@ -6525,6 +6523,15 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args) .vb_access = ocfs2_journal_access_di, }; + ret = ocfs2_xattr_ibody_lookup_header(args->old_inode, di, &xh); + if (ret) + goto out; + + inline_size = le16_to_cpu(di->i_xattr_inline_size); + header_off = osb->sb->s_blocksize - inline_size; + new_xh = (struct ocfs2_xattr_header *) + (args->new_bh->b_data + header_off); + ret = ocfs2_reflink_lock_xattr_allocators(osb, xh, args->ref_root_bh, &credits, &meta_ac); if (ret) { From c210dfaa2720fcad9cbc8ec30fb45ddbabee4bf8 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Mon, 4 May 2026 16:36:05 -0400 Subject: [PATCH 2111/5214] scripts/bloat-o-meter: ignore _sdata _sdata is a linker symbol, but bloat-o-meter may consider it as a real variable: $ scripts/bloat-o-meter vmlinux.orig vmlinux add/remove: 7/1 grow/shrink: 0/0 up/down: 3437/-4096 (-659) Function old new delta crc32table_le - 1024 +1024 crc32table_be - 1024 +1024 crc32ctable_le - 1024 +1024 byte_rev_table - 256 +256 crc32_be - 39 +39 crc32c - 35 +35 crc32_le - 35 +35 _sdata 4096 - -4096 Total: Before=8592564398, After=8592563739, chg -0.00% With the patch: $ scripts/bloat-o-meter vmlinux.orig vmlinux add/remove: 7/0 grow/shrink: 0/0 up/down: 3437/0 (3437) Function old new delta crc32table_le - 1024 +1024 crc32table_be - 1024 +1024 crc32ctable_le - 1024 +1024 byte_rev_table - 256 +256 crc32_be - 39 +39 crc32c - 35 +35 crc32_le - 35 +35 Total: Before=8592560302, After=8592563739, chg +0.00% Link: https://lore.kernel.org/20260504203606.427972-1-ynorov@nvidia.com Signed-off-by: Yury Norov Cc: Valtteri Koskivuori Cc: Eric Dumazet Signed-off-by: Andrew Morton --- scripts/bloat-o-meter | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index 9b4fb996d95b..5868a8b11b0f 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -43,6 +43,7 @@ def getsizes(file, format): if name.startswith("__se_compat_sys"): continue if name.startswith("__addressable_"): continue if name.startswith("__noinstr_text_start"): continue + if name.startswith("_sdata"): continue if name == "linux_banner": continue if name == "vermagic": continue # statics and some other optimizations adds random .NUMBER From 1c56b9cb489e7aa820dd61860e3dd892bdebe80c Mon Sep 17 00:00:00 2001 From: Lucas Poupeau Date: Mon, 4 May 2026 22:16:07 +0200 Subject: [PATCH 2112/5214] lib/bug: cleanup comment style, types and modernize logging Improve the overall code quality of lib/bug.c by: - Reformatting the main documentation block to follow the standard kernel multi-line comment style. - Replacing 'unsigned' with the preferred 'unsigned int'. - Converting legacy printk() calls to modern pr_warn() and pr_info() macros to include proper facility levels and satisfy checkpatch. Link: https://lore.kernel.org/20260504201607.56932-1-lucasp.linux@gmail.com Signed-off-by: Lucas Poupeau Signed-off-by: Andrew Morton --- lib/bug.c | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/bug.c b/lib/bug.c index 224f4cfa4aa3..f4a4df3991b0 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -1,41 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 /* - Generic support for BUG() - - This respects the following config options: - - CONFIG_BUG - emit BUG traps. Nothing happens without this. - CONFIG_GENERIC_BUG - enable this code. - CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit relative pointers for bug_addr and file - CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG - - CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable - (though they're generally always on). - - CONFIG_GENERIC_BUG is set by each architecture using this code. - - To use this, your architecture must: - - 1. Set up the config options: - - Enable CONFIG_GENERIC_BUG if CONFIG_BUG - - 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) - - Define HAVE_ARCH_BUG - - Implement BUG() to generate a faulting instruction - - NOTE: struct bug_entry does not have "file" or "line" entries - when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate - the values accordingly. - - 3. Implement the trap - - In the illegal instruction trap handler (typically), verify - that the fault was in kernel mode, and call report_bug() - - report_bug() will return whether it was a false alarm, a warning, - or an actual bug. - - You must implement the is_valid_bugaddr(bugaddr) callback which - returns true if the eip is a real kernel address, and it points - to the expected BUG trap instruction. - - Jeremy Fitzhardinge 2006 + * Generic support for BUG() + * + * This respects the following config options: + * + * CONFIG_BUG - emit BUG traps. Nothing happens without this. + * CONFIG_GENERIC_BUG - enable this code. + * CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit relative pointers for bug_addr and file + * CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG + * + * CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable + * (though they're generally always on). + * + * CONFIG_GENERIC_BUG is set by each architecture using this code. + * + * To use this, your architecture must: + * + * 1. Set up the config options: + * - Enable CONFIG_GENERIC_BUG if CONFIG_BUG + * + * 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) + * - Define HAVE_ARCH_BUG + * - Implement BUG() to generate a faulting instruction + * - NOTE: struct bug_entry does not have "file" or "line" entries + * when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate + * the values accordingly. + * + * 3. Implement the trap + * - In the illegal instruction trap handler (typically), verify + * that the fault was in kernel mode, and call report_bug() + * - report_bug() will return whether it was a false alarm, a warning, + * or an actual bug. + * - You must implement the is_valid_bugaddr(bugaddr) callback which + * returns true if the eip is a real kernel address, and it points + * to the expected BUG trap instruction. + * + * Jeremy Fitzhardinge 2006 */ #define pr_fmt(fmt) fmt @@ -71,7 +71,7 @@ static struct bug_entry *module_find_bug(unsigned long bugaddr) guard(rcu)(); list_for_each_entry_rcu(mod, &module_bug_list, bug_list) { - unsigned i; + unsigned int i; bug = mod->bug_table; for (i = 0; i < mod->num_bugs; ++i, ++bug) @@ -191,14 +191,14 @@ void __warn_printf(const char *fmt, struct pt_regs *regs) } #endif - printk("%s", fmt); + pr_warn("%s", fmt); } static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long bugaddr, struct pt_regs *regs) { bool warning, once, done, no_cut, has_args; const char *file, *fmt; - unsigned line; + unsigned int line; if (!bug) { if (!is_valid_bugaddr(bugaddr)) @@ -237,7 +237,7 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga * extra debugging message it writes before triggering the handler. */ if (!no_cut) { - printk(KERN_DEFAULT CUT_HERE); + pr_info(CUT_HERE); __warn_printf(fmt, has_args ? regs : NULL); } From 2f5026b8d4fe34601d692ae7f7cd27367e002266 Mon Sep 17 00:00:00 2001 From: Hongfu Li Date: Wed, 13 May 2026 10:58:38 +0800 Subject: [PATCH 2113/5214] selftests/perf_events: fix mmap() error check in sigtrap_threads In sigtrap_threads(), the return value of mmap() is checked against NULL. mmap() returns MAP_FAILED, which is (void *)-1, not NULL, when it fails. Since MAP_FAILED is non-zero and non-NULL, the condition "p == NULL" will never be true on failure, causing the program to proceed with an invalid pointer and segfault if mmap() actually fails under memory pressure. Link: https://lore.kernel.org/20260513025838.594945-1-lihongfu@kylinos.cn Signed-off-by: Hongfu Li Reviewed-by: Andrew Morton Cc: Mickael Salaun Cc: SeongJae Park Cc: Shuah Khan Cc: Wei Yang Cc: Kyle Huey Cc: Ingo Molnar Signed-off-by: Andrew Morton --- tools/testing/selftests/perf_events/watermark_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/perf_events/watermark_signal.c b/tools/testing/selftests/perf_events/watermark_signal.c index 0f64b9b17081..a84709cabd8b 100644 --- a/tools/testing/selftests/perf_events/watermark_signal.c +++ b/tools/testing/selftests/perf_events/watermark_signal.c @@ -102,7 +102,7 @@ TEST(watermark_signal) } p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (p == NULL) { + if (p == MAP_FAILED) { perror("mmap"); goto cleanup; } From dcc8a57815534e3844f342b28ecfb6e626a816a0 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 16 Mar 2026 15:57:27 +0545 Subject: [PATCH 2114/5214] lib/tests: extend cmdline KUnit with next_arg() tests The cmdline KUnit suite covers get_option() and get_options(), but it does not exercise next_arg(). Extend the suite with one test for a quoted value containing spaces and one regression test for a bare quote token after a normal parameter. The regression test covers the bare quote token path fixed by commit 9847f21225c4 ("lib/cmdline: avoid page fault in next_arg"). [shuvampandey1@gmail.com: extend cmdline next_arg() coverage with mixed tokens] Link: https://lore.kernel.org/20260316211249.88601-1-shuvampandey1@gmail.com Link: https://lore.kernel.org/20260316101227.15807-1-shuvampandey1@gmail.com Signed-off-by: Shuvam Pandey Reviewed-by: Andy Shevchenko Cc: Neel Natu Cc: Dmitry Antipov Cc: "Darrick J. Wong" Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/cmdline_kunit.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/lib/tests/cmdline_kunit.c b/lib/tests/cmdline_kunit.c index c1602f797637..af44ae9e92c3 100644 --- a/lib/tests/cmdline_kunit.c +++ b/lib/tests/cmdline_kunit.c @@ -139,11 +139,73 @@ static void cmdline_test_range(struct kunit *test) } while (++i < ARRAY_SIZE(cmdline_test_range_strings)); } +static void cmdline_test_next_arg_quoted_value(struct kunit *test) +{ + char in[] = "foo=\"bar baz\" qux=1"; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "foo"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "bar baz"); + KUNIT_EXPECT_STREQ(test, next, "qux=1"); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "qux"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "1"); + KUNIT_EXPECT_STREQ(test, next, ""); +} + +static void cmdline_test_next_arg_bare_quote_regression(struct kunit *test) +{ + char in[] = "foo=bar \""; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "foo"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "bar"); + KUNIT_EXPECT_STREQ(test, next, "\""); + + /* This hits the i == 0 quoted-token case fixed by 9847f21225c4. */ + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, ""); + KUNIT_EXPECT_PTR_EQ(test, val, NULL); + KUNIT_EXPECT_STREQ(test, next, ""); +} + +static void cmdline_test_next_arg_mixed_tokens(struct kunit *test) +{ + char in[] = "bbb= jjj kkk=\"a=b\""; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "bbb"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, ""); + KUNIT_EXPECT_STREQ(test, next, "jjj kkk=\"a=b\""); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "jjj"); + KUNIT_EXPECT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, next, "kkk=\"a=b\""); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "kkk"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "a=b"); + KUNIT_EXPECT_STREQ(test, next, ""); +} + static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_lead_int), KUNIT_CASE(cmdline_test_tail_int), KUNIT_CASE(cmdline_test_range), + KUNIT_CASE(cmdline_test_next_arg_quoted_value), + KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression), + KUNIT_CASE(cmdline_test_next_arg_mixed_tokens), {} }; From 6e30111dbb4075e3c26c230417b32bc4a1c66831 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:52 +0300 Subject: [PATCH 2115/5214] lib: fix _parse_integer_limit() to handle overflow Patch series "lib and lib/cmdline enhancements", v11. This series is a merge of the recently posted [1] and [2]. The first one is intended to adjust '_parse_integer_limit()' and 'memparse()' to not ignore overflows, extend string to 64-bit integer conversion tests, add KUnit-based test for 'memparse()' and fix kernel-doc glitches found in lib/cmdline.c. The second one was originated from RISCV-specific build fixes needed to integrate the former and now aims to provide platform-specific double-word shifts and corresponding KUnit test. Getting feedback from RISCV core maintainers would be very helpful. Special thanks to Andy Shevchenko, Charlie Jenkins, and Andrew Morton. This patch (of 8): In '_parse_integer_limit()', adjust native integer arithmetic with near-to-overflow branch where 'check_mul_overflow()' and 'check_add_overflow()' are used to check whether an intermediate result goes out of range, and denote such a case with ULLONG_MAX, thus making the function more similar to standard C library's 'strtoull()'. Adjust comment to kernel-doc style as well. Link: https://lore.kernel.org/20260519172259.908980-1-dmantipov@yandex.ru Link: https://lore.kernel.org/20260519172259.908980-2-dmantipov@yandex.ru Link: https://lore.kernel.org/linux-riscv/20260403103338.1122415-1-dmantipov@yandex.ru [1] Link: https://lore.kernel.org/linux-riscv/20260427090105.705529-1-dmantipov@yandex.ru [2] Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Ard Biesheuvel Cc: Palmer Dabbelt Cc: Charlie Jenkins Signed-off-by: Andrew Morton --- lib/kstrtox.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 97be2a39f537..edc4eb7c1bca 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -39,25 +39,30 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) return s; } -/* - * Convert non-negative integer string representation in explicitly given radix - * to an integer. A maximum of max_chars characters will be converted. +/** + * _parse_integer_limit - Convert integer string representation to an integer + * @s: Integer string representation + * @base: Radix + * @p: Where to store result + * @max_chars: Maximum amount of characters to convert * - * Return number of characters consumed maybe or-ed with overflow bit. - * If overflow occurs, result integer (incorrect) is still returned. + * Convert non-negative integer string representation in explicitly given + * radix to an integer. If overflow occurs, value at @p is set to ULLONG_MAX. * - * Don't you dare use this function. + * This function is the workhorse of other string conversion functions and it + * is discouraged to use it explicitly. Consider kstrto*() family instead. + * + * Return: Number of characters consumed, maybe ORed with overflow bit */ noinline unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p, size_t max_chars) { + unsigned int rv, overflow = 0; unsigned long long res; - unsigned int rv; res = 0; - rv = 0; - while (max_chars--) { + for (rv = 0; rv < max_chars; rv++, s++) { unsigned int c = *s; unsigned int lc = _tolower(c); unsigned int val; @@ -76,15 +81,17 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon * it in the max base we support (16) */ if (unlikely(res & (~0ull << 60))) { - if (res > div_u64(ULLONG_MAX - val, base)) - rv |= KSTRTOX_OVERFLOW; + if (check_mul_overflow(res, base, &res) || + check_add_overflow(res, val, &res)) { + res = ULLONG_MAX; + overflow = KSTRTOX_OVERFLOW; + } + } else { + res = res * base + val; } - res = res * base + val; - rv++; - s++; } *p = res; - return rv; + return rv | overflow; } noinline From 9a4580db6e9f83428813f671a79486469684069f Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:53 +0300 Subject: [PATCH 2116/5214] lib: fix memparse() to handle overflow Since '_parse_integer_limit()' (and so 'simple_strtoull()') is now capable to handle overflow, adjust 'memparse()' to handle overflow (denoted by ULLONG_MAX) returned from 'simple_strtoull()'. Also use 'check_shl_overflow()' to catch an overflow possibly caused by processing size suffix and denote it with ULLONG_MAX as well. Link: https://lore.kernel.org/20260519172259.908980-3-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/cmdline.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/cmdline.c b/lib/cmdline.c index 90ed997d9570..f6e4b113ca9f 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -150,39 +150,46 @@ EXPORT_SYMBOL(get_options); unsigned long long memparse(const char *ptr, char **retptr) { char *endptr; /* local pointer to end of parsed string */ - unsigned long long ret = simple_strtoull(ptr, &endptr, 0); + unsigned int shl = 0; + /* Consume valid suffix even in case of overflow. */ switch (*endptr) { case 'E': case 'e': - ret <<= 10; + shl += 10; fallthrough; case 'P': case 'p': - ret <<= 10; + shl += 10; fallthrough; case 'T': case 't': - ret <<= 10; + shl += 10; fallthrough; case 'G': case 'g': - ret <<= 10; + shl += 10; fallthrough; case 'M': case 'm': - ret <<= 10; + shl += 10; fallthrough; case 'K': case 'k': - ret <<= 10; - endptr++; + shl += 10; fallthrough; default: break; } + if (shl && likely(ptr != endptr)) { + /* Have valid suffix with preceding number. */ + if (unlikely(check_shl_overflow(ret, shl, &ret))) + ret = ULLONG_MAX; + endptr++; + } + if (retptr) *retptr = endptr; From ab90fc1007245380988c500ddf7eff6da1b10056 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:54 +0300 Subject: [PATCH 2117/5214] lib: add more string to 64-bit integer conversion overflow tests Add a few more string to 64-bit integer conversion tests to check whether 'kstrtoull()', 'kstrtoll()', 'kstrtou64()' and 'kstrtos64()' can handle overflows reported by '_parse_integer_limit()'. Link: https://lore.kernel.org/20260519172259.908980-4-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/test-kstrtox.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c index ee87fef66cb5..811128d0df16 100644 --- a/lib/test-kstrtox.c +++ b/lib/test-kstrtox.c @@ -198,6 +198,7 @@ static void __init test_kstrtoull_fail(void) {"10000000000000000000000000000000000000000000000000000000000000000", 2}, {"2000000000000000000000", 8}, {"18446744073709551616", 10}, + {"569202370375329612767", 10}, {"10000000000000000", 16}, /* negative */ {"-0", 0}, @@ -275,9 +276,11 @@ static void __init test_kstrtoll_fail(void) {"9223372036854775809", 10}, {"18446744073709551614", 10}, {"18446744073709551615", 10}, + {"569202370375329612767", 10}, {"-9223372036854775809", 10}, {"-18446744073709551614", 10}, {"-18446744073709551615", 10}, + {"-569202370375329612767", 10}, /* sign is first character if any */ {"-+1", 0}, {"-+1", 8}, @@ -334,6 +337,7 @@ static void __init test_kstrtou64_fail(void) {"-1", 10}, {"18446744073709551616", 10}, {"18446744073709551617", 10}, + {"569202370375329612767", 10}, }; TEST_FAIL(kstrtou64, u64, "%llu", test_u64_fail); } @@ -386,6 +390,8 @@ static void __init test_kstrtos64_fail(void) {"18446744073709551615", 10}, {"18446744073709551616", 10}, {"18446744073709551617", 10}, + {"569202370375329612767", 10}, + {"-569202370375329612767", 10}, }; TEST_FAIL(kstrtos64, s64, "%lld", test_s64_fail); } From a535853b664988aba11771deed44673765df581e Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:55 +0300 Subject: [PATCH 2118/5214] lib/cmdline_kunit: add test case for memparse() Better late than never, now there is a long-awaited basic test for 'memparse()' which is provided by cmdline.c. Link: https://lore.kernel.org/20260519172259.908980-5-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/tests/cmdline_kunit.c | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/tests/cmdline_kunit.c b/lib/tests/cmdline_kunit.c index af44ae9e92c3..3f61ff8d3178 100644 --- a/lib/tests/cmdline_kunit.c +++ b/lib/tests/cmdline_kunit.c @@ -6,6 +6,7 @@ #include #include #include +#include #include static const char *cmdline_test_strings[] = { @@ -198,6 +199,60 @@ static void cmdline_test_next_arg_mixed_tokens(struct kunit *test) KUNIT_EXPECT_STREQ(test, next, ""); } +struct cmdline_test_memparse_entry { + const char *input; + const char *unrecognized; + unsigned long long result; +}; + +static const struct cmdline_test_memparse_entry testdata[] = { + { "0", "", 0ULL }, + { "1", "", 1ULL }, + { "a", "a", 0ULL }, + { "k", "k", 0ULL }, + { "E", "E", 0ULL }, + { "0xb", "", 11ULL }, + { "0xz", "x", 0ULL }, + { "1234", "", 1234ULL }, + { "04567", "", 2423ULL }, + { "0x9876", "", 39030LL }, + { "05678", "8", 375ULL }, + { "0xabcdefz", "z", 11259375ULL }, + { "0cdba", "c", 0ULL }, + { "4K", "", SZ_4K }, + { "0x10k@0xaaaabbbb", "@", SZ_16K }, + { "32M", "", SZ_32M }, + { "067m:foo", ":", 55 * SZ_1M }, + { "2G;bar=baz", ";", SZ_2G }, + { "07gz", "z", 7ULL * SZ_1G }, + { "3T+data", "+", 3 * SZ_1T }, + { "04t,ro", ",", SZ_4T }, + { "012p", "", 11258999068426240ULL }, + { "7P,sync", ",", 7881299347898368ULL }, + { "0x2e", "", 46ULL }, + { "2E and more", " ", 2305843009213693952ULL }, + { "18446744073709551615", "", ULLONG_MAX }, + { "0xffffffffffffffff0", "", ULLONG_MAX }, + { "1111111111111111111T", "", ULLONG_MAX }, + { "222222222222222222222G", "", ULLONG_MAX }, + { "3333333333333333333333M", "", ULLONG_MAX }, +}; + +static void cmdline_test_memparse(struct kunit *test) +{ + const struct cmdline_test_memparse_entry *e; + unsigned long long ret; + char *retptr; + + for (e = testdata; e < testdata + ARRAY_SIZE(testdata); e++) { + ret = memparse(e->input, &retptr); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when parsing '%s'", e->input); + KUNIT_EXPECT_EQ_MSG(test, *retptr, *e->unrecognized, + " when parsing '%s'", e->input); + } +} + static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_lead_int), @@ -206,6 +261,7 @@ static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_next_arg_quoted_value), KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression), KUNIT_CASE(cmdline_test_next_arg_mixed_tokens), + KUNIT_CASE(cmdline_test_memparse), {} }; From 117d2bfa0ab1bec88d600c50884000334f034338 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:56 +0300 Subject: [PATCH 2119/5214] lib/cmdline: adjust a few comments to fix kernel-doc -Wreturn warnings Fix 'get_option()', 'memparse()' and 'parse_option_str()' comments to match the commonly used style as suggested by kernel-doc -Wreturn. Link: https://lore.kernel.org/20260519172259.908980-6-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/cmdline.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cmdline.c b/lib/cmdline.c index f6e4b113ca9f..16cce6621cec 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -43,7 +43,7 @@ static int get_range(char **str, int *pint, int n) * When @pint is NULL the function can be used as a validator of * the current option in the string. * - * Return values: + * Return: * 0 - no int in string * 1 - int found, no subsequent comma * 2 - int found including a subsequent comma @@ -145,6 +145,9 @@ EXPORT_SYMBOL(get_options); * * Parses a string into a number. The number stored at @ptr is * potentially suffixed with K, M, G, T, P, E. + * + * Return: The value as recognized by simple_strtoull() multiplied + * by the value as specified by suffix, if any. */ unsigned long long memparse(const char *ptr, char **retptr) @@ -205,7 +208,7 @@ EXPORT_SYMBOL(memparse); * This function parses a string containing a comma-separated list of * strings like a=b,c. * - * Return true if there's such option in the string, or return false. + * Return: True if there's such option in the string or false otherwise. */ bool parse_option_str(const char *str, const char *option) { From a354b8de9ad607c0a11419a402df46b46503f921 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:57 +0300 Subject: [PATCH 2120/5214] riscv: add platform-specific double word shifts for riscv32 Add riscv32-specific '__ashldi3()', '__ashrdi3()', and '__lshrdi3()'. Initially it was intended to fix the following link error observed when building EFI-enabled kernel with CONFIG_EFI_STUB=y and CONFIG_EFI_GENERIC_STUB=y: riscv32-linux-gnu-ld: ./drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L49': __efistub_cmdline.c:(.init.text+0x1f2): undefined reference to `__efistub___ashldi3' riscv32-linux-gnu-ld: __efistub_cmdline.c:(.init.text+0x202): undefined reference to `__efistub___lshrdi3' Reported at [1] trying to build https://patchew.org/linux/20260212164413.889625-1-dmantipov@yandex.ru, tested with 'qemu-system-riscv32 -M virt' only. Link: https://lore.kernel.org/20260519172259.908980-7-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603041925.KLKqpK6N-lkp@intel.com [1] Suggested-by: Ard Biesheuvel Tested-by: Charlie Jenkins Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- arch/riscv/Kconfig | 3 --- arch/riscv/include/asm/asm-prototypes.h | 4 +++ arch/riscv/kernel/image-vars.h | 9 +++++++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/ashldi3.S | 36 +++++++++++++++++++++++++ arch/riscv/lib/ashrdi3.S | 36 +++++++++++++++++++++++++ arch/riscv/lib/lshrdi3.S | 36 +++++++++++++++++++++++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 arch/riscv/lib/ashldi3.S create mode 100644 arch/riscv/lib/ashrdi3.S create mode 100644 arch/riscv/lib/lshrdi3.S diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5754942cf85..0d10b299bad8 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -404,9 +404,6 @@ config ARCH_RV32I bool "RV32I" depends on NONPORTABLE select 32BIT - select GENERIC_LIB_ASHLDI3 - select GENERIC_LIB_ASHRDI3 - select GENERIC_LIB_LSHRDI3 select GENERIC_LIB_UCMPDI2 config ARCH_RV64I diff --git a/arch/riscv/include/asm/asm-prototypes.h b/arch/riscv/include/asm/asm-prototypes.h index 5b90ba5314ee..a0ca9efff267 100644 --- a/arch/riscv/include/asm/asm-prototypes.h +++ b/arch/riscv/include/asm/asm-prototypes.h @@ -5,6 +5,10 @@ #include #include +long long __lshrdi3(long long a, int b); +long long __ashrdi3(long long a, int b); +long long __ashldi3(long long a, int b); + long long __lshrti3(long long a, int b); long long __ashrti3(long long a, int b); long long __ashlti3(long long a, int b); diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 3bd9d06a8b8f..7b44b94f1283 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -32,6 +32,15 @@ __efistub___init_text_end = __init_text_end; __efistub_sysfb_primary_display = sysfb_primary_display; #endif +/* + * These double-word integer shifts are used by the library code, and + * the first two of them are required to link EFI stub. Note __ashrdi3() + * is not actually used by the stub but this may change in the future. + */ +PROVIDE(__efistub___lshrdi3 = __lshrdi3); +PROVIDE(__efistub___ashldi3 = __ashldi3); +PROVIDE(__efistub___ashrdi3 = __ashrdi3); + #endif #endif /* __RISCV_KERNEL_IMAGE_VARS_H */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 6f767b2a349d..f668b98970bd 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -16,6 +16,7 @@ ifeq ($(CONFIG_MMU), y) lib-$(CONFIG_RISCV_ISA_V) += uaccess_vector.o endif lib-$(CONFIG_MMU) += uaccess.o +lib-$(CONFIG_32BIT) += ashldi3.o ashrdi3.o lshrdi3.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/riscv/lib/ashldi3.S b/arch/riscv/lib/ashldi3.S new file mode 100644 index 000000000000..c3408862e2f6 --- /dev/null +++ b/arch/riscv/lib/ashldi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Shift left + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__ashldi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a1, a0 + mv a0, zero +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + srl t2, a0, t1 + sll a0, a0, a2 + sll a1, a1, a2 + or a1, a1, t2 +2: ret + +SYM_FUNC_END(__ashldi3) +EXPORT_SYMBOL(__ashldi3) diff --git a/arch/riscv/lib/ashrdi3.S b/arch/riscv/lib/ashrdi3.S new file mode 100644 index 000000000000..426de0946606 --- /dev/null +++ b/arch/riscv/lib/ashrdi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Arithmetic shift right + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__ashrdi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a0, a1 + srai a1, a1, 31 +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + sll t2, a1, t1 + sra a1, a1, a2 + srl a0, a0, a2 + or a0, a0, t2 +2: ret + +SYM_FUNC_END(__ashrdi3) +EXPORT_SYMBOL(__ashrdi3) diff --git a/arch/riscv/lib/lshrdi3.S b/arch/riscv/lib/lshrdi3.S new file mode 100644 index 000000000000..1af03985ccb7 --- /dev/null +++ b/arch/riscv/lib/lshrdi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Logical shift right + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__lshrdi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a0, a1 + mv a1, zero +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + sll t2, a1, t1 + srl a1, a1, a2 + srl a0, a0, a2 + or a0, a0, t2 +2: ret + +SYM_FUNC_END(__lshrdi3) +EXPORT_SYMBOL(__lshrdi3) From 6bba2ae43e8f379d3bed4c3eb258ea6d81baae80 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:58 +0300 Subject: [PATCH 2121/5214] lib: kunit: add tests for __ashldi3(), __ashrdi3(), and __lshrdi3() Add KUnit tests for '__ashldi3()', '__ashrdi3()', and '__lshrdi3()' helper functions used to implement 64-bit arithmetic shift left, arithmetic shift right and logical shift right, respectively, on a 32-bit CPUs. Tested with 'qemu-system-riscv32 -M virt' and 'qemu-system-arm -M virt'. Link: https://lore.kernel.org/20260519172259.908980-8-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Tested-by: Charlie Jenkins Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/Kconfig.debug | 10 +++ lib/tests/Makefile | 1 + lib/tests/shdi3_kunit.c | 175 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 lib/tests/shdi3_kunit.c diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8ff5adcfe1e0..7ca468c7d81d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2971,6 +2971,16 @@ config BITS_TEST If unsure, say N. +config SHDI3_KUNIT_TEST + tristate "KUnit test for __ashldi3(), __ashrdi3(), and __lshrdi3()" + depends on KUNIT + depends on ARM || XTENSA || MICROBLAZE || ((RISCV || SPARC) && !64BIT) + help + This builds the unit test for __ashldi3(), __ashrdi3(), and + __lshrdi3() helper functions used to implement 64-bit arithmetic + shift left, arithmetic shift right and logical shift right, + respectively, on a 32-bit CPUs. + config SLUB_KUNIT_TEST tristate "KUnit test for SLUB cache error detection" if !KUNIT_ALL_TESTS depends on SLUB_DEBUG && KUNIT diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 7e9c2fa52e35..4ead57602eac 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BASE64_KUNIT) += base64_kunit.o obj-$(CONFIG_BITOPS_KUNIT) += bitops_kunit.o obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_BITS_TEST) += test_bits.o +obj-$(CONFIG_SHDI3_KUNIT_TEST) += shdi3_kunit.o obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o diff --git a/lib/tests/shdi3_kunit.c b/lib/tests/shdi3_kunit.c new file mode 100644 index 000000000000..44f65e66512b --- /dev/null +++ b/lib/tests/shdi3_kunit.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0 +/* + * Test cases for __ashldi3(), __ashrdi3(), and __lshrdi3(). + */ + +#include +#include +#include +#include + +struct shdi3_test_entry { + long long input; + int shift; + long long result; +}; + +static const struct shdi3_test_entry ashldi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/ashldi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x2468ACF13579BDELL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BCLL }, + { 0x0123456789ABCDEFLL, 3, 0x91A2B3C4D5E6F78LL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDEF0LL }, + { 0x0123456789ABCDEFLL, 28, 0x789ABCDEF0000000LL }, + { 0x0123456789ABCDEFLL, 29, 0xF13579BDE0000000LL }, + { 0x0123456789ABCDEFLL, 30, 0xE26AF37BC0000000LL }, + { 0x0123456789ABCDEFLL, 31, 0xC4D5E6F780000000LL }, + { 0x0123456789ABCDEFLL, 32, 0x89ABCDEF00000000LL }, + { 0x0123456789ABCDEFLL, 33, 0x13579BDE00000000LL }, + { 0x0123456789ABCDEFLL, 34, 0x26AF37BC00000000LL }, + { 0x0123456789ABCDEFLL, 35, 0x4D5E6F7800000000LL }, + { 0x0123456789ABCDEFLL, 36, 0x9ABCDEF000000000LL }, + { 0x0123456789ABCDEFLL, 60, 0xF000000000000000LL }, + { 0x0123456789ABCDEFLL, 61, 0xE000000000000000LL }, + { 0x0123456789ABCDEFLL, 62, 0xC000000000000000LL }, + { 0x0123456789ABCDEFLL, 63, 0x8000000000000000LL }, +}; + +static void shdi3_test_ashldi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = ashldi3_testdata; + e < ashldi3_testdata + ARRAY_SIZE(ashldi3_testdata); e++) { + ret = __ashldi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __ashldi3(%lld, %d)", + e->input, e->shift); + } +} + +static const struct shdi3_test_entry ashrdi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/ashrdi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x91A2B3C4D5E6F7LL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BLL }, + { 0x0123456789ABCDEFLL, 3, 0x2468ACF13579BDLL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDELL }, + { 0x0123456789ABCDEFLL, 28, 0x12345678LL }, + { 0x0123456789ABCDEFLL, 29, 0x91A2B3CLL }, + { 0x0123456789ABCDEFLL, 30, 0x48D159ELL }, + { 0x0123456789ABCDEFLL, 31, 0x2468ACFLL }, + { 0x0123456789ABCDEFLL, 32, 0x1234567LL }, + { 0x0123456789ABCDEFLL, 33, 0x91A2B3LL }, + { 0x0123456789ABCDEFLL, 34, 0x48D159LL }, + { 0x0123456789ABCDEFLL, 35, 0x2468ACLL }, + { 0x0123456789ABCDEFLL, 36, 0x123456LL }, + { 0x0123456789ABCDEFLL, 60, 0 }, + { 0x0123456789ABCDEFLL, 61, 0 }, + { 0x0123456789ABCDEFLL, 62, 0 }, + { 0x0123456789ABCDEFLL, 63, 0 }, + { 0xFEDCBA9876543210LL, 0, 0xFEDCBA9876543210LL }, + { 0xFEDCBA9876543210LL, 1, 0xFF6E5D4C3B2A1908LL }, + { 0xFEDCBA9876543210LL, 2, 0xFFB72EA61D950C84LL }, + { 0xFEDCBA9876543210LL, 3, 0xFFDB97530ECA8642LL }, + { 0xFEDCBA9876543210LL, 4, 0xFFEDCBA987654321LL }, + { 0xFEDCBA9876543210LL, 28, 0xFFFFFFFFEDCBA987LL }, + { 0xFEDCBA9876543210LL, 29, 0xFFFFFFFFF6E5D4C3LL }, + { 0xFEDCBA9876543210LL, 30, 0xFFFFFFFFFB72EA61LL }, + { 0xFEDCBA9876543210LL, 31, 0xFFFFFFFFFDB97530LL }, + { 0xFEDCBA9876543210LL, 32, 0xFFFFFFFFFEDCBA98LL }, + { 0xFEDCBA9876543210LL, 33, 0xFFFFFFFFFF6E5D4CLL }, + { 0xFEDCBA9876543210LL, 34, 0xFFFFFFFFFFB72EA6LL }, + { 0xFEDCBA9876543210LL, 35, 0xFFFFFFFFFFDB9753LL }, + { 0xFEDCBA9876543210LL, 36, 0xFFFFFFFFFFEDCBA9LL }, + { 0xAEDCBA9876543210LL, 60, 0xFFFFFFFFFFFFFFFALL }, + { 0xAEDCBA9876543210LL, 61, 0xFFFFFFFFFFFFFFFDLL }, + { 0xAEDCBA9876543210LL, 62, 0xFFFFFFFFFFFFFFFELL }, + { 0xAEDCBA9876543210LL, 63, 0xFFFFFFFFFFFFFFFFLL }, +}; + +static void shdi3_test_ashrdi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = ashrdi3_testdata; + e < ashrdi3_testdata + ARRAY_SIZE(ashrdi3_testdata); e++) { + ret = __ashrdi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __ashrdi3(%lld, %d)", + e->input, e->shift); + } +} + +static const struct shdi3_test_entry lshrdi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/lshrdi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x91A2B3C4D5E6F7LL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BLL }, + { 0x0123456789ABCDEFLL, 3, 0x2468ACF13579BDLL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDELL }, + { 0x0123456789ABCDEFLL, 28, 0x12345678LL }, + { 0x0123456789ABCDEFLL, 29, 0x91A2B3CLL }, + { 0x0123456789ABCDEFLL, 30, 0x48D159ELL }, + { 0x0123456789ABCDEFLL, 31, 0x2468ACFLL }, + { 0x0123456789ABCDEFLL, 32, 0x1234567LL }, + { 0x0123456789ABCDEFLL, 33, 0x91A2B3LL }, + { 0x0123456789ABCDEFLL, 34, 0x48D159LL }, + { 0x0123456789ABCDEFLL, 35, 0x2468ACLL }, + { 0x0123456789ABCDEFLL, 36, 0x123456LL }, + { 0x0123456789ABCDEFLL, 60, 0 }, + { 0x0123456789ABCDEFLL, 61, 0 }, + { 0x0123456789ABCDEFLL, 62, 0 }, + { 0x0123456789ABCDEFLL, 63, 0 }, + { 0xFEDCBA9876543210LL, 0, 0xFEDCBA9876543210LL }, + { 0xFEDCBA9876543210LL, 1, 0x7F6E5D4C3B2A1908LL }, + { 0xFEDCBA9876543210LL, 2, 0x3FB72EA61D950C84LL }, + { 0xFEDCBA9876543210LL, 3, 0x1FDB97530ECA8642LL }, + { 0xFEDCBA9876543210LL, 4, 0xFEDCBA987654321LL }, + { 0xFEDCBA9876543210LL, 28, 0xFEDCBA987LL }, + { 0xFEDCBA9876543210LL, 29, 0x7F6E5D4C3LL }, + { 0xFEDCBA9876543210LL, 30, 0x3FB72EA61LL }, + { 0xFEDCBA9876543210LL, 31, 0x1FDB97530LL }, + { 0xFEDCBA9876543210LL, 32, 0xFEDCBA98LL }, + { 0xFEDCBA9876543210LL, 33, 0x7F6E5D4CLL }, + { 0xFEDCBA9876543210LL, 34, 0x3FB72EA6LL }, + { 0xFEDCBA9876543210LL, 35, 0x1FDB9753LL }, + { 0xFEDCBA9876543210LL, 36, 0xFEDCBA9LL }, + { 0xAEDCBA9876543210LL, 60, 0xALL }, + { 0xAEDCBA9876543210LL, 61, 0x5LL }, + { 0xAEDCBA9876543210LL, 62, 0x2LL }, + { 0xAEDCBA9876543210LL, 63, 0x1LL }, +}; + +static void shdi3_test_lshrdi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = lshrdi3_testdata; + e < lshrdi3_testdata + ARRAY_SIZE(lshrdi3_testdata); e++) { + ret = __lshrdi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __lshrdi3(%lld, %d)", + e->input, e->shift); + } +} + +static struct kunit_case shdi3_test_cases[] = { + KUNIT_CASE(shdi3_test_ashldi3), + KUNIT_CASE(shdi3_test_ashrdi3), + KUNIT_CASE(shdi3_test_lshrdi3), + {} +}; + +static struct kunit_suite shdi3_test_suite = { + .name = "shdi3", + .test_cases = shdi3_test_cases, +}; +kunit_test_suite(shdi3_test_suite); + +MODULE_DESCRIPTION("Test cases for __ashldi3(), __ashrdi3(), and __lshrdi3()"); +MODULE_LICENSE("GPL"); From 442082297e3d38f3a59754ff56fc07d72a93fdbd Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:59 +0300 Subject: [PATCH 2122/5214] riscv: fix building compressed EFI image When building vmlinuz.efi with CONFIG_EFI_ZBOOT enabled, '__lshrdi3()' is also needed to fix yet another link error observed when building riscv32 and loongarch32 images: riscv32-linux-gnu-ld: drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L49': __efistub_cmdline.c:(.init.text+0x202): undefined reference to `__efistub___lshrdi3' /usr/bin/loongarch32-linux-gnu-ld: ./drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L47': __efistub_cmdline.c:(.init.text+0x26c): undefined reference to `__efistub___lshrdi3' And since both riscv64 and loongarch64 can have CONFIG_EFI_ZBOOT but doesn't need these library routines, rely on CONFIG_32BIT to manage linking of lib-ashldi3.o and lib-lshrdi3.o on 32-bit variants only. [dmantipov@yandex.ru: fix loongarch32] Link: https://lore.kernel.org/8095016e47aceab4830c2523ce78af968ec0497e.camel@yandex.ru Link: https://lore.kernel.org/20260519172259.908980-9-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reported-by: Charlie Jenkins Closes: https://lore.kernel.org/linux-riscv/20260409050018.GA371560@inky.localdomain Tested-by: Charlie Jenkins Suggested-by: Ard Biesheuvel Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Tested-by: Charlie Jenkins Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- drivers/firmware/efi/libstub/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index cfedb3025c26..77a2b2d74f3f 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -95,8 +95,10 @@ CFLAGS_zboot-decompress-gzip.o += -I$(srctree)/lib/zlib_inflate zboot-obj-$(CONFIG_KERNEL_ZSTD) := zboot-decompress-zstd.o lib-xxhash.o CFLAGS_zboot-decompress-zstd.o += -I$(srctree)/lib/zstd -zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o lib-ashldi3.o -zboot-obj-$(CONFIG_LOONGARCH) += lib-clz_ctz.o lib-ashldi3.o +zboot-riscv-obj-$(CONFIG_32BIT) := lib-ashldi3.o lib-lshrdi3.o +zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o $(zboot-riscv-obj-y) +zboot-loongarch-obj-$(CONFIG_32BIT) := lib-ashldi3.o lib-lshrdi3.o +zboot-obj-$(CONFIG_LOONGARCH) += lib-clz_ctz.o $(zboot-loongarch-obj-y) lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y) lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o From 1d9c9493692eccd16b47610f1d21cc7a100199e9 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 5 May 2026 11:00:46 +0200 Subject: [PATCH 2123/5214] kcov: allow simultaneous KCOV_ENABLE/KCOV_REMOTE_ENABLE Allow the same userspace thread to simultaneously collect normal coverage in syscall context (KCOV_ENABLE) and remote coverage of asynchronous work created by the thread (KCOV_REMOTE_ENABLE). With this, remote KCOV coverage becomes useful for generic fuzzing and not just fuzzing of specific data injection interfaces. This requires that the task_struct::kcov_* fields are separated into ones that are used by the task that generates coverage, and ones that are used by the task that requested remote coverage. To split this up: - Split task_struct::kcov into kcov and kcov_remote. kcov_task_exit() now has to clean up both separately. - Only use task_struct::kcov_mode on the task that generates coverage. - Only reset task_struct::kcov_handle on the task that requested remote coverage. After this change, fields used by the task that generates coverage are: - kcov_mode - kcov_size - kcov_area - kcov - kcov_sequence - kcov_softirq Fields used by the task that requested remote coverage are: - kcov_remote - kcov_handle [jannh@google.com: remove unused constant KCOV_MODE_REMOTE, per Dmitry] Link: https://lore.kernel.org/20260515-kcov-simultaneous-remote-v2-1-56fde1cfa509@google.com [jannh@google.com: update documentation on remote coverage collection] Link: https://lore.kernel.org/20260519-kcov-docs-v1-1-5bb22f4cb20c@google.com [jannh@google.com: move and reword sentence on simultaneous normal/remote collection Link: https://lore.kernel.org/20260520-kcov-docs-v2-1-819f78778763@google.com Link: https://lore.kernel.org/20260505-kcov-simultaneous-remote-v1-1-a670ba7cefd2@google.com Signed-off-by: Jann Horn Reviewed-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Marco Elver Signed-off-by: Andrew Morton --- Documentation/dev-tools/kcov.rst | 6 ++ include/linux/kcov.h | 2 - include/linux/sched.h | 3 + kernel/kcov.c | 94 ++++++++++++++++++-------------- 4 files changed, 61 insertions(+), 44 deletions(-) diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst index 8127849d40f5..1a739290c8ec 100644 --- a/Documentation/dev-tools/kcov.rst +++ b/Documentation/dev-tools/kcov.rst @@ -237,6 +237,9 @@ Both ``kcov_remote_start`` and ``kcov_remote_stop`` annotations and the collection sections. The way a handle is used depends on the context where the matching code section executes. +A thread can use two separate KCOV instances to collect remote coverage and +normal coverage at the same time. + KCOV supports collecting remote coverage from the following contexts: 1. Global kernel background tasks. These are the tasks that are spawned during @@ -262,6 +265,9 @@ gets saved to the ``kcov_handle`` field in the current ``task_struct`` and needs to be passed to the newly spawned local tasks via custom kernel code modifications. Those tasks should in turn use the passed handle in their ``kcov_remote_start`` and ``kcov_remote_stop`` annotations. +In the kernel, common handles are wrapped in a ``kcov_common_handle_id``, which +consumes no space in builds without ``CONFIG_KCOV``; subsystems that integrate +with this mechanism should not need to use any ``#ifdef CONFIG_KCOV`` or such. KCOV follows a predefined format for both global and common handles. Each handle is a ``u64`` integer. Currently, only the one top and the lower 4 bytes diff --git a/include/linux/kcov.h b/include/linux/kcov.h index cdb72b3859d8..895b761b2db1 100644 --- a/include/linux/kcov.h +++ b/include/linux/kcov.h @@ -21,8 +21,6 @@ enum kcov_mode { KCOV_MODE_TRACE_PC = 2, /* Collecting comparison operands mode. */ KCOV_MODE_TRACE_CMP = 3, - /* The process owns a KCOV remote reference. */ - KCOV_MODE_REMOTE = 4, }; #define KCOV_IN_CTXSW (1 << 30) diff --git a/include/linux/sched.h b/include/linux/sched.h index ee06cba5c6f5..d71cec884a5d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1517,6 +1517,9 @@ struct task_struct { /* KCOV descriptor wired with this task or NULL: */ struct kcov *kcov; + /* KCOV descriptor for remote coverage collection from other tasks: */ + struct kcov *kcov_remote; + /* KCOV common handle for remote coverage collection: */ u64 kcov_handle; diff --git a/kernel/kcov.c b/kernel/kcov.c index a43e33a28adb..fd2503030729 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -368,6 +368,7 @@ static void kcov_start(struct task_struct *t, struct kcov *kcov, WRITE_ONCE(t->kcov_mode, mode); } +/* operates on coverage-generator-owned fields */ static void kcov_stop(struct task_struct *t) { WRITE_ONCE(t->kcov_mode, KCOV_MODE_DISABLED); @@ -377,16 +378,17 @@ static void kcov_stop(struct task_struct *t) t->kcov_area = NULL; } +/* operates on coverage-generator-owned fields */ static void kcov_task_reset(struct task_struct *t) { kcov_stop(t); t->kcov_sequence = 0; - t->kcov_handle = 0; } void kcov_task_init(struct task_struct *t) { kcov_task_reset(t); + t->kcov_remote = NULL; t->kcov_handle = current->kcov_handle; } @@ -423,11 +425,14 @@ static void kcov_remote_reset(struct kcov *kcov) static void kcov_disable(struct task_struct *t, struct kcov *kcov) __must_hold(&kcov->lock) { - kcov_task_reset(t); - if (kcov->remote) + if (kcov->remote) { + t->kcov_handle = 0; + t->kcov_remote = NULL; kcov_remote_reset(kcov); - else + } else { + kcov_task_reset(t); kcov_reset(kcov); + } } static void kcov_get(struct kcov *kcov) @@ -453,41 +458,47 @@ void kcov_task_exit(struct task_struct *t) unsigned long flags; kcov = t->kcov; - if (kcov == NULL) - return; - - spin_lock_irqsave(&kcov->lock, flags); - kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); - /* - * For KCOV_ENABLE devices we want to make sure that t->kcov->t == t, - * which comes down to: - * WARN_ON(!kcov->remote && kcov->t != t); - * - * For KCOV_REMOTE_ENABLE devices, the exiting task is either: - * - * 1. A remote task between kcov_remote_start() and kcov_remote_stop(). - * In this case we should print a warning right away, since a task - * shouldn't be exiting when it's in a kcov coverage collection - * section. Here t points to the task that is collecting remote - * coverage, and t->kcov->t points to the thread that created the - * kcov device. Which means that to detect this case we need to - * check that t != t->kcov->t, and this gives us the following: - * WARN_ON(kcov->remote && kcov->t != t); - * - * 2. The task that created kcov exiting without calling KCOV_DISABLE, - * and then again we make sure that t->kcov->t == t: - * WARN_ON(kcov->remote && kcov->t != t); - * - * By combining all three checks into one we get: - */ - if (WARN_ON(kcov->t != t)) { + if (kcov) { + spin_lock_irqsave(&kcov->lock, flags); + kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); + /* + * This could be a remote task between kcov_remote_start() and + * kcov_remote_stop(). + * In this case we should print a warning right away, since a + * task shouldn't be exiting when it's in a kcov coverage + * collection section. + * + * Otherwise, this should be a task that created a local + * kcov instance and hasn't called KCOV_DISABLE. + * Make sure that t->kcov->t is consistent. + */ + if (WARN_ON(kcov->remote) || WARN_ON(kcov->t != t)) { + spin_unlock_irqrestore(&kcov->lock, flags); + return; + } + /* Just to not leave dangling references behind. */ + kcov_disable(t, kcov); spin_unlock_irqrestore(&kcov->lock, flags); - return; + kcov_put(kcov); + } + kcov = t->kcov_remote; + if (kcov) { + spin_lock_irqsave(&kcov->lock, flags); + kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); + /* + * This is a KCOV_REMOTE_ENABLE device, and the task is the + * user task which has requested remote coverage collection. + * Make sure that t->kcov->t is consistent. + */ + if (WARN_ON(!kcov->remote) || WARN_ON(kcov->t != t)) { + spin_unlock_irqrestore(&kcov->lock, flags); + return; + } + /* Just to not leave dangling references behind. */ + kcov_disable(t, kcov); + spin_unlock_irqrestore(&kcov->lock, flags); + kcov_put(kcov); } - /* Just to not leave dangling references behind. */ - kcov_disable(t, kcov); - spin_unlock_irqrestore(&kcov->lock, flags); - kcov_put(kcov); } static int kcov_mmap(struct file *filep, struct vm_area_struct *vma) @@ -629,9 +640,9 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, case KCOV_DISABLE: /* Disable coverage for the current task. */ unused = arg; - if (unused != 0 || current->kcov != kcov) - return -EINVAL; t = current; + if (unused != 0 || (kcov != t->kcov && kcov != t->kcov_remote)) + return -EINVAL; if (WARN_ON(kcov->t != t)) return -EINVAL; kcov_disable(t, kcov); @@ -641,7 +652,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, if (kcov->mode != KCOV_MODE_INIT || !kcov->area) return -EINVAL; t = current; - if (kcov->t != NULL || t->kcov != NULL) + if (kcov->t != NULL || t->kcov_remote != NULL) return -EBUSY; remote_arg = (struct kcov_remote_arg *)arg; mode = kcov_get_mode(remote_arg->trace_mode); @@ -651,8 +662,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, LONG_MAX / sizeof(unsigned long)) return -EINVAL; kcov->mode = mode; - t->kcov = kcov; - t->kcov_mode = KCOV_MODE_REMOTE; + t->kcov_remote = kcov; kcov->t = t; kcov->remote = true; kcov->remote_size = remote_arg->area_size; From 91e11432d7db690dfa3e6d2e6f89f5cd865123f5 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 18 May 2026 11:10:05 +0200 Subject: [PATCH 2124/5214] mailmap: update Jorge Ramirez-Ortiz email address Map old Linaro address to the new Qualcomm OSS address. Link: https://lore.kernel.org/20260518091019.3926371-1-jorge.ramirez@oss.qualcomm.com Signed-off-by: Jorge Ramirez-Ortiz Cc: Jakub Kacinski Cc: Konrad Dybcio Cc: Martin Kepplinger Cc: Shannon Nelson Signed-off-by: Andrew Morton --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index a009f73d7ea5..cf67173dcd51 100644 --- a/.mailmap +++ b/.mailmap @@ -435,6 +435,7 @@ John Stultz Jonas Gorski Jonathan Cameron Jordan Crouse +Jorge Ramirez-Ortiz From c4697486fc232e821a33191d809d0ac3bb600027 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:44 +0200 Subject: [PATCH 2125/5214] raid6: turn the userspace test harness into a kunit test Patch series "cleanup the RAID6 P/Q library", v3. This series cleans up the RAID6 P/Q library to match the recent updates to the RAID 5 XOR library and other CRC/crypto libraries. This includes providing properly documented external interfaces, hiding the internals, using static_call instead of indirect calls and turning the user space test suite into an in-kernel kunit test which is also extended to improve coverage. Note that this changes registration so that non-priority algorithms are not registered, which greatly helps with the benchmark time at boot time. I'd like to encourage all architecture maintainers to see if they can further optimized this by registering as few as possible algorithms when there is a clear benefit in optimized or more unrolled implementations. This patch (of 18): Currently the raid6 code can be compiled as userspace code to run the test suite. Convert that to be a kunit case with minimal changes to avoid mutating global state so that we can drop this requirement. Note that this is not a good kunit test case yet and will need a lot more work, but that is deferred until the raid6 code is moved to it's new place, which is easier if the userspace makefile doesn't need adjustments for the new location first. Link: https://lore.kernel.org/20260518051804.462141-1-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-2-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 3 - lib/Kconfig | 11 +++ lib/raid6/Makefile | 2 +- lib/raid6/algos.c | 5 +- lib/raid6/recov.c | 34 --------- lib/raid6/test/Makefile | 155 +-------------------------------------- lib/raid6/test/test.c | 156 +++++++++++++++++++++------------------- 7 files changed, 100 insertions(+), 266 deletions(-) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 2467b3be15c9..08c5995ea980 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -144,7 +144,6 @@ extern const struct raid6_calls raid6_neonx8; /* Algorithm list */ extern const struct raid6_calls * const raid6_algos[]; extern const struct raid6_recov_calls *const raid6_recov_algos[]; -int raid6_select_algo(void); /* Return values from chk_syndrome */ #define RAID6_OK 0 @@ -165,8 +164,6 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, void **ptrs); extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, void **ptrs); -void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, - void **ptrs); /* Some definitions to allow code to be compiled for testing in userspace */ #ifndef __KERNEL__ diff --git a/lib/Kconfig b/lib/Kconfig index ed31919a5a0b..f882f7f0a68f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -11,6 +11,17 @@ menu "Library routines" config RAID6_PQ tristate +config RAID6_PQ_KUNIT_TEST + tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on RAID6_PQ + default KUNIT_ALL_TESTS + help + Unit tests for the RAID6 PQ library functions. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + config RAID6_PQ_BENCHMARK bool "Automatically choose fastest RAID6 PQ functions" depends on RAID6_PQ diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 5be0a4e60ab1..6fd048c127b6 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RAID6_PQ) += raid6_pq.o +obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ int8.o diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 799e0e5eac26..5a9f4882e18d 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -19,6 +19,7 @@ #include #include #endif +#include struct raid6_calls raid6_call; EXPORT_SYMBOL_GPL(raid6_call); @@ -86,6 +87,7 @@ const struct raid6_calls * const raid6_algos[] = { &raid6_intx1, NULL }; +EXPORT_SYMBOL_IF_KUNIT(raid6_algos); void (*raid6_2data_recov)(int, size_t, int, int, void **); EXPORT_SYMBOL_GPL(raid6_2data_recov); @@ -119,6 +121,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = { &raid6_recov_intx1, NULL }; +EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); #ifdef __KERNEL__ #define RAID6_TIME_JIFFIES_LG2 4 @@ -239,7 +242,7 @@ static inline const struct raid6_calls *raid6_choose_gen( /* Try to pick the best algorithm */ /* This code uses the gfmul table as convenient data set to abuse */ -int __init raid6_select_algo(void) +static int __init raid6_select_algo(void) { const int disks = RAID6_TEST_DISKS; diff --git a/lib/raid6/recov.c b/lib/raid6/recov.c index b5e47c008b41..8d113196632e 100644 --- a/lib/raid6/recov.c +++ b/lib/raid6/recov.c @@ -99,37 +99,3 @@ const struct raid6_recov_calls raid6_recov_intx1 = { .name = "intx1", .priority = 0, }; - -#ifndef __KERNEL__ -/* Testing only */ - -/* Recover two failed blocks. */ -void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs) -{ - if ( faila > failb ) { - int tmp = faila; - faila = failb; - failb = tmp; - } - - if ( failb == disks-1 ) { - if ( faila == disks-2 ) { - /* P+Q failure. Just rebuild the syndrome. */ - raid6_call.gen_syndrome(disks, bytes, ptrs); - } else { - /* data+Q failure. Reconstruct data from P, - then rebuild syndrome. */ - /* NOT IMPLEMENTED - equivalent to RAID-5 */ - } - } else { - if ( failb == disks-2 ) { - /* data+P failure. */ - raid6_datap_recov(disks, bytes, faila, ptrs); - } else { - /* data+data failure. */ - raid6_2data_recov(disks, bytes, faila, failb, ptrs); - } - } -} - -#endif diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile index 09bbe2b14cce..520381ea71d7 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid6/test/Makefile @@ -1,156 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# -# This is a simple Makefile to test some of the RAID-6 code -# from userspace. -# -pound := \# +obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o -# Adjust as desired -CC = gcc -OPTFLAGS = -O2 -CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS) -LD = ld -AWK = awk -f -AR = ar -RANLIB = ranlib -OBJS = int1.o int2.o int4.o int8.o int16.o int32.o recov.o algos.o tables.o - -ARCH := $(shell uname -m 2>/dev/null | sed -e /s/i.86/i386/) -ifeq ($(ARCH),i386) - CFLAGS += -DCONFIG_X86_32 - IS_X86 = yes -endif -ifeq ($(ARCH),x86_64) - CFLAGS += -DCONFIG_X86_64 - IS_X86 = yes -endif - -ifeq ($(ARCH),arm) - CFLAGS += -I../../../arch/arm/include -mfpu=neon - HAS_NEON = yes -endif -ifeq ($(ARCH),aarch64) - CFLAGS += -I../../../arch/arm64/include - HAS_NEON = yes -endif - -ifeq ($(findstring riscv,$(ARCH)),riscv) - CFLAGS += -I../../../arch/riscv/include -DCONFIG_RISCV=1 - HAS_RVV = yes -endif - -ifeq ($(findstring ppc,$(ARCH)),ppc) - CFLAGS += -I../../../arch/powerpc/include - HAS_ALTIVEC := $(shell printf '$(pound)include \nvector int a;\n' |\ - gcc -c -x c - >/dev/null && rm ./-.o && echo yes) -endif - -ifeq ($(ARCH),loongarch64) - CFLAGS += -I../../../arch/loongarch/include -DCONFIG_LOONGARCH=1 - CFLAGS += $(shell echo 'vld $$vr0, $$zero, 0' | \ - gcc -c -x assembler - >/dev/null 2>&1 && \ - rm ./-.o && echo -DCONFIG_CPU_HAS_LSX=1) - CFLAGS += $(shell echo 'xvld $$xr0, $$zero, 0' | \ - gcc -c -x assembler - >/dev/null 2>&1 && \ - rm ./-.o && echo -DCONFIG_CPU_HAS_LASX=1) -endif - -ifeq ($(IS_X86),yes) - OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o avx512.o recov_avx512.o - CFLAGS += -DCONFIG_X86 -else ifeq ($(HAS_NEON),yes) - OBJS += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o - CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1 -else ifeq ($(HAS_ALTIVEC),yes) - CFLAGS += -DCONFIG_ALTIVEC - OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \ - vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o -else ifeq ($(ARCH),loongarch64) - OBJS += loongarch_simd.o recov_loongarch_simd.o -else ifeq ($(HAS_RVV),yes) - OBJS += rvv.o recov_rvv.o - CFLAGS += -DCONFIG_RISCV_ISA_V=1 -endif - -.c.o: - $(CC) $(CFLAGS) -c -o $@ $< - -%.c: ../%.c - cp -f $< $@ - -%.uc: ../%.uc - cp -f $< $@ - -all: raid6.a raid6test - -raid6.a: $(OBJS) - rm -f $@ - $(AR) cq $@ $^ - $(RANLIB) $@ - -raid6test: test.c raid6.a - $(CC) $(CFLAGS) -o raid6test $^ - -neon1.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < neon.uc > $@ - -neon2.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < neon.uc > $@ - -neon4.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < neon.uc > $@ - -neon8.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < neon.uc > $@ - -altivec1.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < altivec.uc > $@ - -altivec2.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < altivec.uc > $@ - -altivec4.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < altivec.uc > $@ - -altivec8.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < altivec.uc > $@ - -vpermxor1.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < vpermxor.uc > $@ - -vpermxor2.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < vpermxor.uc > $@ - -vpermxor4.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < vpermxor.uc > $@ - -vpermxor8.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < vpermxor.uc > $@ - -int1.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < int.uc > $@ - -int2.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < int.uc > $@ - -int4.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < int.uc > $@ - -int8.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < int.uc > $@ - -int16.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=16 < int.uc > $@ - -int32.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=32 < int.uc > $@ - -tables.c: mktables - ./mktables > tables.c - -clean: - rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c vpermxor*.c neon*.c tables.c raid6test - -spotless: clean - rm -f *~ +raid6_kunit-y += test.o diff --git a/lib/raid6/test/test.c b/lib/raid6/test/test.c index 841a55242aba..9db287b4a48f 100644 --- a/lib/raid6/test/test.c +++ b/lib/raid6/test/test.c @@ -1,43 +1,37 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6test.c + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * Test RAID-6 recovery with various algorithms + * Test RAID-6 recovery algorithms. */ -#include -#include -#include +#include +#include #include +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +#define RAID6_KUNIT_SEED 42 + #define NDISKS 16 /* Including P and Q */ -const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); - -char *dataptrs[NDISKS]; -char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static struct rnd_state rng; +static void *dataptrs[NDISKS]; +static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static void makedata(int start, int stop) { - int i, j; + int i; for (i = start; i <= stop; i++) { - for (j = 0; j < PAGE_SIZE; j++) - data[i][j] = rand(); - + prandom_bytes_state(&rng, data[i], PAGE_SIZE); dataptrs[i] = data[i]; } } -static char disk_type(int d) +static char member_type(int d) { switch (d) { case NDISKS-2: @@ -49,104 +43,118 @@ static char disk_type(int d) } } -static int test_disks(int i, int j) +static void test_disks(struct kunit *test, const struct raid6_calls *calls, + const struct raid6_recov_calls *ra, int faila, int failb) { - int erra, errb; - memset(recovi, 0xf0, PAGE_SIZE); memset(recovj, 0xba, PAGE_SIZE); - dataptrs[i] = recovi; - dataptrs[j] = recovj; + dataptrs[faila] = recovi; + dataptrs[failb] = recovj; - raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs); + if (failb == NDISKS - 1) { + /* + * We don't implement the data+Q failure scenario, since it + * is equivalent to a RAID-5 failure (XOR, then recompute Q). + */ + if (faila != NDISKS - 2) + goto skip; - erra = memcmp(data[i], recovi, PAGE_SIZE); - errb = memcmp(data[j], recovj, PAGE_SIZE); - - if (i < NDISKS-2 && j == NDISKS-1) { - /* We don't implement the DQ failure scenario, since it's - equivalent to a RAID-5 failure (XOR, then recompute Q) */ - erra = errb = 0; + /* P+Q failure. Just rebuild the syndrome. */ + calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); + } else if (failb == NDISKS - 2) { + /* data+P failure. */ + ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs); } else { - printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n", - raid6_call.name, - i, disk_type(i), - j, disk_type(j), - (!erra && !errb) ? "OK" : - !erra ? "ERRB" : - !errb ? "ERRA" : "ERRAB"); + /* data+data failure. */ + ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } - dataptrs[i] = data[i]; - dataptrs[j] = data[j]; + KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, + "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n", + calls->name, ra->name, + faila, member_type(faila), + failb, member_type(failb)); + KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, + "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n", + calls->name, ra->name, + failb, member_type(failb), + faila, member_type(faila)); - return erra || errb; +skip: + dataptrs[faila] = data[faila]; + dataptrs[failb] = data[failb]; } -int main(int argc, char *argv[]) +static void raid6_test(struct kunit *test) { const struct raid6_calls *const *algo; const struct raid6_recov_calls *const *ra; int i, j, p1, p2; - int err = 0; - - makedata(0, NDISKS-1); for (ra = raid6_recov_algos; *ra; ra++) { if ((*ra)->valid && !(*ra)->valid()) continue; - raid6_2data_recov = (*ra)->data2; - raid6_datap_recov = (*ra)->datap; - - printf("using recovery %s\n", (*ra)->name); - for (algo = raid6_algos; *algo; algo++) { - if ((*algo)->valid && !(*algo)->valid()) + const struct raid6_calls *calls = *algo; + + if (calls->valid && !calls->valid()) continue; - raid6_call = **algo; - /* Nuke syndromes */ - memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE); + memset(data[NDISKS - 2], 0xee, PAGE_SIZE); + memset(data[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ - raid6_call.gen_syndrome(NDISKS, PAGE_SIZE, + calls->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - err += test_disks(i, j); + test_disks(test, calls, *ra, i, j); - if (!raid6_call.xor_syndrome) + if (!calls->xor_syndrome) continue; for (p1 = 0; p1 < NDISKS-2; p1++) for (p2 = p1; p2 < NDISKS-2; p2++) { /* Simulate rmw run */ - raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, (void **)&dataptrs); makedata(p1, p2); - raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, (void **)&dataptrs); for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - err += test_disks(i, j); + test_disks(test, calls, + *ra, i, j); } } - printf("\n"); } - - printf("\n"); - /* Pick the best algorithm test */ - raid6_select_algo(); - - if (err) - printf("\n*** ERRORS FOUND ***\n"); - - return err; } + +static struct kunit_case raid6_test_cases[] = { + KUNIT_CASE(raid6_test), + {}, +}; + +static int raid6_suite_init(struct kunit_suite *suite) +{ + prandom_seed_state(&rng, RAID6_KUNIT_SEED); + makedata(0, NDISKS - 1); + return 0; +} + +static struct kunit_suite raid6_test_suite = { + .name = "raid6", + .test_cases = raid6_test_cases, + .suite_init = raid6_suite_init, +}; +kunit_test_suite(raid6_test_suite); + +MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); +MODULE_LICENSE("GPL"); From 3d6beb659ddf0664612bafacb0bd9030ba6ec7e6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:45 +0200 Subject: [PATCH 2126/5214] raid6: remove __KERNEL__ ifdefs With the test code ported to kernel space, none of this is required. Link: https://lore.kernel.org/20260518051804.462141-3-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 90 -------------------------------- lib/raid6/algos.c | 12 ----- lib/raid6/altivec.uc | 10 +--- lib/raid6/avx2.c | 2 +- lib/raid6/avx512.c | 2 +- lib/raid6/loongarch.h | 38 -------------- lib/raid6/loongarch_simd.c | 3 +- lib/raid6/mktables.c | 14 ----- lib/raid6/mmx.c | 2 +- lib/raid6/neon.c | 6 --- lib/raid6/recov_avx2.c | 2 +- lib/raid6/recov_avx512.c | 2 +- lib/raid6/recov_loongarch_simd.c | 3 +- lib/raid6/recov_neon.c | 6 --- lib/raid6/recov_ssse3.c | 2 +- lib/raid6/rvv.h | 11 +--- lib/raid6/sse1.c | 2 +- lib/raid6/sse2.c | 2 +- lib/raid6/vpermxor.uc | 7 --- lib/raid6/x86.h | 75 -------------------------- 20 files changed, 15 insertions(+), 276 deletions(-) delete mode 100644 lib/raid6/loongarch.h delete mode 100644 lib/raid6/x86.h diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 08c5995ea980..d26788fada58 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -8,8 +8,6 @@ #ifndef LINUX_RAID_RAID6_H #define LINUX_RAID_RAID6_H -#ifdef __KERNEL__ - #include #include @@ -19,59 +17,6 @@ static inline void *raid6_get_zero_page(void) return page_address(ZERO_PAGE(0)); } -#else /* ! __KERNEL__ */ -/* Used for testing in user space */ - -#include -#include -#include -#include -#include -#include -#include - -/* Not standard, but glibc defines it */ -#define BITS_PER_LONG __WORDSIZE - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -#ifndef PAGE_SIZE -# define PAGE_SIZE 4096 -#endif -#ifndef PAGE_SHIFT -# define PAGE_SHIFT 12 -#endif -extern const char raid6_empty_zero_page[PAGE_SIZE]; - -#define __init -#define __exit -#ifndef __attribute_const__ -# define __attribute_const__ __attribute__((const)) -#endif -#define noinline __attribute__((noinline)) - -#define preempt_enable() -#define preempt_disable() -#define cpu_has_feature(x) 1 -#define enable_kernel_altivec() -#define disable_kernel_altivec() - -#undef EXPORT_SYMBOL -#define EXPORT_SYMBOL(sym) -#undef EXPORT_SYMBOL_GPL -#define EXPORT_SYMBOL_GPL(sym) -#define MODULE_LICENSE(licence) -#define MODULE_DESCRIPTION(desc) -#define subsys_initcall(x) -#define module_exit(x) - -#define IS_ENABLED(x) (x) -#define CONFIG_RAID6_PQ_BENCHMARK 1 -#endif /* __KERNEL__ */ - /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); @@ -165,39 +110,4 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, void **ptrs); -/* Some definitions to allow code to be compiled for testing in userspace */ -#ifndef __KERNEL__ - -# define jiffies raid6_jiffies() -# define printk printf -# define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__) -# define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__) -# define GFP_KERNEL 0 -# define __get_free_pages(x, y) ((unsigned long)mmap(NULL, PAGE_SIZE << (y), \ - PROT_READ|PROT_WRITE, \ - MAP_PRIVATE|MAP_ANONYMOUS,\ - 0, 0)) -# define free_pages(x, y) munmap((void *)(x), PAGE_SIZE << (y)) - -static inline void cpu_relax(void) -{ - /* Nothing */ -} - -#undef HZ -#define HZ 1000 -static inline uint32_t raid6_jiffies(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec*1000 + tv.tv_usec/1000; -} - -static inline void *raid6_get_zero_page(void) -{ - return raid6_empty_zero_page; -} - -#endif /* ! __KERNEL__ */ - #endif /* LINUX_RAID_RAID6_H */ diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 5a9f4882e18d..985c60bb00a4 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -12,13 +12,8 @@ */ #include -#ifndef __KERNEL__ -#include -#include -#else #include #include -#endif #include struct raid6_calls raid6_call; @@ -123,14 +118,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = { }; EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); -#ifdef __KERNEL__ #define RAID6_TIME_JIFFIES_LG2 4 -#else -/* Need more time to be stable in userspace */ -#define RAID6_TIME_JIFFIES_LG2 9 -#define time_before(x, y) ((x) < (y)) -#endif - #define RAID6_TEST_DISKS 8 #define RAID6_TEST_DISKS_ORDER 3 diff --git a/lib/raid6/altivec.uc b/lib/raid6/altivec.uc index d20ed0d11411..2c59963e58f9 100644 --- a/lib/raid6/altivec.uc +++ b/lib/raid6/altivec.uc @@ -27,10 +27,8 @@ #ifdef CONFIG_ALTIVEC #include -#ifdef __KERNEL__ -# include -# include -#endif /* __KERNEL__ */ +#include +#include /* * This is the C data type to use. We use a vector of @@ -113,11 +111,7 @@ int raid6_have_altivec(void); int raid6_have_altivec(void) { /* This assumes either all CPUs have Altivec or none does */ -# ifdef __KERNEL__ return cpu_has_feature(CPU_FTR_ALTIVEC); -# else - return 1; -# endif } #endif diff --git a/lib/raid6/avx2.c b/lib/raid6/avx2.c index 059024234dce..a1a5213918af 100644 --- a/lib/raid6/avx2.c +++ b/lib/raid6/avx2.c @@ -14,7 +14,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_avx2_constants { u64 x1d[4]; diff --git a/lib/raid6/avx512.c b/lib/raid6/avx512.c index 009bd0adeebf..874998bcd7d7 100644 --- a/lib/raid6/avx512.c +++ b/lib/raid6/avx512.c @@ -18,7 +18,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_avx512_constants { u64 x1d[8]; diff --git a/lib/raid6/loongarch.h b/lib/raid6/loongarch.h deleted file mode 100644 index acfc33ce7056..000000000000 --- a/lib/raid6/loongarch.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2023 WANG Xuerui - * - * raid6/loongarch.h - * - * Definitions common to LoongArch RAID-6 code only - */ - -#ifndef _LIB_RAID6_LOONGARCH_H -#define _LIB_RAID6_LOONGARCH_H - -#ifdef __KERNEL__ - -#include -#include - -#else /* for user-space testing */ - -#include - -/* have to supply these defines for glibc 2.37- and musl */ -#ifndef HWCAP_LOONGARCH_LSX -#define HWCAP_LOONGARCH_LSX (1 << 4) -#endif -#ifndef HWCAP_LOONGARCH_LASX -#define HWCAP_LOONGARCH_LASX (1 << 5) -#endif - -#define kernel_fpu_begin() -#define kernel_fpu_end() - -#define cpu_has_lsx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LSX) -#define cpu_has_lasx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LASX) - -#endif /* __KERNEL__ */ - -#endif /* _LIB_RAID6_LOONGARCH_H */ diff --git a/lib/raid6/loongarch_simd.c b/lib/raid6/loongarch_simd.c index aa5d9f924ca3..72f4d92d4876 100644 --- a/lib/raid6/loongarch_simd.c +++ b/lib/raid6/loongarch_simd.c @@ -10,7 +10,8 @@ */ #include -#include "loongarch.h" +#include +#include /* * The vector algorithms are currently priority 0, which means the generic diff --git a/lib/raid6/mktables.c b/lib/raid6/mktables.c index 3be03793237c..3de1dbf6846c 100644 --- a/lib/raid6/mktables.c +++ b/lib/raid6/mktables.c @@ -56,9 +56,7 @@ int main(int argc, char *argv[]) uint8_t v; uint8_t exptbl[256], invtbl[256]; - printf("#ifdef __KERNEL__\n"); printf("#include \n"); - printf("#endif\n"); printf("#include \n"); /* Compute multiplication table */ @@ -76,9 +74,7 @@ int main(int argc, char *argv[]) printf("\t},\n"); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfmul);\n"); - printf("#endif\n"); /* Compute vector multiplication table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -101,9 +97,7 @@ int main(int argc, char *argv[]) printf("\t},\n"); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_vgfmul);\n"); - printf("#endif\n"); /* Compute power-of-2 table (exponent) */ v = 1; @@ -120,9 +114,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfexp);\n"); - printf("#endif\n"); /* Compute log-of-2 table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -140,9 +132,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gflog);\n"); - printf("#endif\n"); /* Compute inverse table x^-1 == x^254 */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -155,9 +145,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfinv);\n"); - printf("#endif\n"); /* Compute inv(2^x + 1) (exponent-xor-inverse) table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -169,9 +157,7 @@ int main(int argc, char *argv[]) (j == 7) ? '\n' : ' '); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfexi);\n"); - printf("#endif\n"); return 0; } diff --git a/lib/raid6/mmx.c b/lib/raid6/mmx.c index 3a5bf53a297b..e411f0cfbd95 100644 --- a/lib/raid6/mmx.c +++ b/lib/raid6/mmx.c @@ -14,7 +14,7 @@ #ifdef CONFIG_X86_32 #include -#include "x86.h" +#include /* Shared with raid6/sse1.c */ const struct raid6_mmx_constants { diff --git a/lib/raid6/neon.c b/lib/raid6/neon.c index 6d9474ce6da9..47b8bb0afc65 100644 --- a/lib/raid6/neon.c +++ b/lib/raid6/neon.c @@ -6,13 +6,7 @@ */ #include - -#ifdef __KERNEL__ #include -#else -#define scoped_ksimd() -#define cpu_has_neon() (1) -#endif /* * There are 2 reasons these wrappers are kept in a separate compilation unit diff --git a/lib/raid6/recov_avx2.c b/lib/raid6/recov_avx2.c index 97d598d2535c..19fbd9c4dce6 100644 --- a/lib/raid6/recov_avx2.c +++ b/lib/raid6/recov_avx2.c @@ -5,7 +5,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_avx2(void) { diff --git a/lib/raid6/recov_avx512.c b/lib/raid6/recov_avx512.c index 7986120ca444..143f4976b2ad 100644 --- a/lib/raid6/recov_avx512.c +++ b/lib/raid6/recov_avx512.c @@ -7,7 +7,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_avx512(void) { diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid6/recov_loongarch_simd.c index 93dc515997a1..eb3a1e79f01f 100644 --- a/lib/raid6/recov_loongarch_simd.c +++ b/lib/raid6/recov_loongarch_simd.c @@ -11,7 +11,8 @@ */ #include -#include "loongarch.h" +#include +#include /* * Unlike with the syndrome calculation algorithms, there's no boot-time diff --git a/lib/raid6/recov_neon.c b/lib/raid6/recov_neon.c index 9d99aeabd31a..13d5df718c15 100644 --- a/lib/raid6/recov_neon.c +++ b/lib/raid6/recov_neon.c @@ -5,14 +5,8 @@ */ #include - -#ifdef __KERNEL__ #include #include "neon.h" -#else -#define scoped_ksimd() -#define cpu_has_neon() (1) -#endif static int raid6_has_neon(void) { diff --git a/lib/raid6/recov_ssse3.c b/lib/raid6/recov_ssse3.c index 2e849185c32b..146cdbf465bd 100644 --- a/lib/raid6/recov_ssse3.c +++ b/lib/raid6/recov_ssse3.c @@ -4,7 +4,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_ssse3(void) { diff --git a/lib/raid6/rvv.h b/lib/raid6/rvv.h index 6d0708a2c8a4..b0a71b375962 100644 --- a/lib/raid6/rvv.h +++ b/lib/raid6/rvv.h @@ -7,17 +7,8 @@ * Definitions for RISC-V RAID-6 code */ -#ifdef __KERNEL__ -#include -#else -#define kernel_vector_begin() -#define kernel_vector_end() -#include -#include -#define has_vector() (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) -#endif - #include +#include static int rvv_has_vector(void) { diff --git a/lib/raid6/sse1.c b/lib/raid6/sse1.c index 692fa3a93bf0..794d5cfa0306 100644 --- a/lib/raid6/sse1.c +++ b/lib/raid6/sse1.c @@ -19,7 +19,7 @@ #ifdef CONFIG_X86_32 #include -#include "x86.h" +#include /* Defined in raid6/mmx.c */ extern const struct raid6_mmx_constants { diff --git a/lib/raid6/sse2.c b/lib/raid6/sse2.c index 2930220249c9..f9edf8a8d1c4 100644 --- a/lib/raid6/sse2.c +++ b/lib/raid6/sse2.c @@ -13,7 +13,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_sse_constants { u64 x1d[2]; diff --git a/lib/raid6/vpermxor.uc b/lib/raid6/vpermxor.uc index 1bfb127fbfe8..a8e76b1c956e 100644 --- a/lib/raid6/vpermxor.uc +++ b/lib/raid6/vpermxor.uc @@ -25,10 +25,8 @@ #include #include -#ifdef __KERNEL__ #include #include -#endif typedef vector unsigned char unative_t; #define NSIZE sizeof(unative_t) @@ -85,13 +83,8 @@ int raid6_have_altivec_vpermxor(void); int raid6_have_altivec_vpermxor(void) { /* Check if arch has both altivec and the vpermxor instructions */ -# ifdef __KERNEL__ return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && cpu_has_feature(CPU_FTR_ARCH_207S)); -# else - return 1; -#endif - } #endif diff --git a/lib/raid6/x86.h b/lib/raid6/x86.h deleted file mode 100644 index 9a6ff37115e7..000000000000 --- a/lib/raid6/x86.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* ----------------------------------------------------------------------- * - * - * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - -/* - * raid6/x86.h - * - * Definitions common to x86 and x86-64 RAID-6 code only - */ - -#ifndef LINUX_RAID_RAID6X86_H -#define LINUX_RAID_RAID6X86_H - -#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__) - -#ifdef __KERNEL__ /* Real code */ - -#include - -#else /* Dummy code for user space testing */ - -static inline void kernel_fpu_begin(void) -{ -} - -static inline void kernel_fpu_end(void) -{ -} - -#define __aligned(x) __attribute__((aligned(x))) - -#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ -#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions - * (fast save and restore) */ -#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ -#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ -#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */ -#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */ -#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */ -#define X86_FEATURE_AVX2 (9*32+ 5) /* AVX2 instructions */ -#define X86_FEATURE_AVX512F (9*32+16) /* AVX-512 Foundation */ -#define X86_FEATURE_AVX512DQ (9*32+17) /* AVX-512 DQ (Double/Quad granular) - * Instructions - */ -#define X86_FEATURE_AVX512BW (9*32+30) /* AVX-512 BW (Byte/Word granular) - * Instructions - */ -#define X86_FEATURE_AVX512VL (9*32+31) /* AVX-512 VL (128/256 Vector Length) - * Extensions - */ -#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ - -/* Should work well enough on modern CPUs for testing */ -static inline int boot_cpu_has(int flag) -{ - u32 eax, ebx, ecx, edx; - - eax = (flag & 0x100) ? 7 : - (flag & 0x20) ? 0x80000001 : 1; - ecx = 0; - - asm volatile("cpuid" - : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx)); - - return ((flag & 0x100 ? ebx : - (flag & 0x80) ? ecx : edx) >> (flag & 31)) & 1; -} - -#endif /* ndef __KERNEL__ */ - -#endif -#endif From 3626738bc7147d52cb49f3994a9846aa2d34810a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:46 +0200 Subject: [PATCH 2127/5214] raid6: move to lib/raid/ Move the raid6 code to live in lib/raid/ with the XOR code, and change the internal organization so that each architecture has a subdirectory similar to the CRC, crypto and XOR libraries, and fix up the Makefile to only build files actually needed. Also move the kunit test case from the history test/ subdirectory to tests/ and use the normal naming scheme for it. Link: https://lore.kernel.org/20260518051804.462141-4-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- MAINTAINERS | 2 +- lib/Kconfig | 22 ---- lib/Makefile | 1 - lib/raid/Kconfig | 22 ++++ lib/raid/Makefile | 2 +- lib/{ => raid}/raid6/.gitignore | 0 lib/raid/raid6/Makefile | 122 ++++++++++++++++++ lib/{ => raid}/raid6/algos.c | 0 lib/{raid6 => raid/raid6/arm}/neon.c | 0 lib/{raid6 => raid/raid6/arm}/neon.h | 0 lib/{raid6 => raid/raid6/arm}/neon.uc | 2 +- lib/{raid6 => raid/raid6/arm}/recov_neon.c | 2 +- .../raid6/arm}/recov_neon_inner.c | 2 +- lib/{ => raid}/raid6/int.uc | 0 .../raid6/loongarch}/loongarch_simd.c | 0 .../raid6/loongarch}/recov_loongarch_simd.c | 0 lib/{ => raid}/raid6/mktables.c | 0 lib/{raid6 => raid/raid6/powerpc}/altivec.uc | 4 - lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc | 3 - lib/{ => raid}/raid6/recov.c | 0 lib/{raid6 => raid/raid6/riscv}/recov_rvv.c | 0 lib/{raid6 => raid/raid6/riscv}/rvv.c | 0 lib/{raid6 => raid/raid6/riscv}/rvv.h | 0 lib/{raid6 => raid/raid6/s390}/recov_s390xc.c | 0 lib/{raid6 => raid/raid6/s390}/s390vx.uc | 0 lib/{raid6/test => raid/raid6/tests}/Makefile | 2 - .../test.c => raid/raid6/tests/raid6_kunit.c} | 0 lib/{ => raid}/raid6/unroll.awk | 0 lib/{raid6 => raid/raid6/x86}/avx2.c | 0 lib/{raid6 => raid/raid6/x86}/avx512.c | 0 lib/{raid6 => raid/raid6/x86}/mmx.c | 4 - lib/{raid6 => raid/raid6/x86}/recov_avx2.c | 0 lib/{raid6 => raid/raid6/x86}/recov_avx512.c | 0 lib/{raid6 => raid/raid6/x86}/recov_ssse3.c | 0 lib/{raid6 => raid/raid6/x86}/sse1.c | 4 - lib/{raid6 => raid/raid6/x86}/sse2.c | 0 lib/raid6/Makefile | 83 ------------ lib/raid6/test/.gitignore | 3 - 38 files changed, 149 insertions(+), 131 deletions(-) rename lib/{ => raid}/raid6/.gitignore (100%) create mode 100644 lib/raid/raid6/Makefile rename lib/{ => raid}/raid6/algos.c (100%) rename lib/{raid6 => raid/raid6/arm}/neon.c (100%) rename lib/{raid6 => raid/raid6/arm}/neon.h (100%) rename lib/{raid6 => raid/raid6/arm}/neon.uc (99%) rename lib/{raid6 => raid/raid6/arm}/recov_neon.c (99%) rename lib/{raid6 => raid/raid6/arm}/recov_neon_inner.c (99%) rename lib/{ => raid}/raid6/int.uc (100%) rename lib/{raid6 => raid/raid6/loongarch}/loongarch_simd.c (100%) rename lib/{raid6 => raid/raid6/loongarch}/recov_loongarch_simd.c (100%) rename lib/{ => raid}/raid6/mktables.c (100%) rename lib/{raid6 => raid/raid6/powerpc}/altivec.uc (98%) rename lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc (98%) rename lib/{ => raid}/raid6/recov.c (100%) rename lib/{raid6 => raid/raid6/riscv}/recov_rvv.c (100%) rename lib/{raid6 => raid/raid6/riscv}/rvv.c (100%) rename lib/{raid6 => raid/raid6/riscv}/rvv.h (100%) rename lib/{raid6 => raid/raid6/s390}/recov_s390xc.c (100%) rename lib/{raid6 => raid/raid6/s390}/s390vx.uc (100%) rename lib/{raid6/test => raid/raid6/tests}/Makefile (77%) rename lib/{raid6/test/test.c => raid/raid6/tests/raid6_kunit.c} (100%) rename lib/{ => raid}/raid6/unroll.awk (100%) rename lib/{raid6 => raid/raid6/x86}/avx2.c (100%) rename lib/{raid6 => raid/raid6/x86}/avx512.c (100%) rename lib/{raid6 => raid/raid6/x86}/mmx.c (99%) rename lib/{raid6 => raid/raid6/x86}/recov_avx2.c (100%) rename lib/{raid6 => raid/raid6/x86}/recov_avx512.c (100%) rename lib/{raid6 => raid/raid6/x86}/recov_ssse3.c (100%) rename lib/{raid6 => raid/raid6/x86}/sse1.c (99%) rename lib/{raid6 => raid/raid6/x86}/sse2.c (100%) delete mode 100644 lib/raid6/Makefile delete mode 100644 lib/raid6/test/.gitignore diff --git a/MAINTAINERS b/MAINTAINERS index 236b0785bf3d..49a264b999e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24823,7 +24823,7 @@ F: drivers/md/md* F: drivers/md/raid* F: include/linux/raid/ F: include/uapi/linux/raid/ -F: lib/raid6/ +F: lib/raid/raid6/ SOLIDRUN CLEARFOG SUPPORT M: Russell King diff --git a/lib/Kconfig b/lib/Kconfig index f882f7f0a68f..f01a33e521e1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -8,28 +8,6 @@ config BINARY_PRINTF menu "Library routines" -config RAID6_PQ - tristate - -config RAID6_PQ_KUNIT_TEST - tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS - depends on KUNIT - depends on RAID6_PQ - default KUNIT_ALL_TESTS - help - Unit tests for the RAID6 PQ library functions. - - This is intended to help people writing architecture-specific - optimized versions. If unsure, say N. - -config RAID6_PQ_BENCHMARK - bool "Automatically choose fastest RAID6 PQ functions" - depends on RAID6_PQ - default y - help - Benchmark all available RAID6 PQ functions on init and choose the - fastest one. - config LINEAR_RANGES tristate diff --git a/lib/Makefile b/lib/Makefile index f33a24bf1c19..6e72d2c1cce7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -167,7 +167,6 @@ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ obj-$(CONFIG_XZ_DEC) += xz/ -obj-$(CONFIG_RAID6_PQ) += raid6/ lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index 5ab2b0a7be4c..e39f6d667792 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -28,3 +28,25 @@ config XOR_KUNIT_TEST This is intended to help people writing architecture-specific optimized versions. If unsure, say N. + +config RAID6_PQ + tristate + +config RAID6_PQ_KUNIT_TEST + tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on RAID6_PQ + default KUNIT_ALL_TESTS + help + Unit tests for the RAID6 PQ library functions. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + +config RAID6_PQ_BENCHMARK + bool "Automatically choose fastest RAID6 PQ functions" + depends on RAID6_PQ + default y + help + Benchmark all available RAID6 PQ functions on init and choose the + fastest one. diff --git a/lib/raid/Makefile b/lib/raid/Makefile index 3540fe846dc4..6fc5eeb53df0 100644 --- a/lib/raid/Makefile +++ b/lib/raid/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y += xor/ +obj-y += xor/ raid6/ diff --git a/lib/raid6/.gitignore b/lib/raid/raid6/.gitignore similarity index 100% rename from lib/raid6/.gitignore rename to lib/raid/raid6/.gitignore diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile new file mode 100644 index 000000000000..7cb31b8a5c17 --- /dev/null +++ b/lib/raid/raid6/Makefile @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I $(src) + +obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/ + +raid6_pq-y += algos.o tables.o + +# generic integer generation and recovery implementation +raid6_pq-y += int1.o int2.o int4.o int8.o +raid6_pq-y += recov.o + +# architecture-specific generation and recovery implementations: +raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += arm/neon.o \ + arm/neon1.o \ + arm/neon2.o \ + arm/neon4.o \ + arm/neon8.o \ + arm/recov_neon.o \ + arm/recov_neon_inner.o +raid6_pq-$(CONFIG_LOONGARCH) += loongarch/loongarch_simd.o \ + loongarch/recov_loongarch_simd.o +raid6_pq-$(CONFIG_ALTIVEC) += powerpc/altivec1.o \ + powerpc/altivec2.o \ + powerpc/altivec4.o \ + powerpc/altivec8.o \ + powerpc/vpermxor1.o \ + powerpc/vpermxor2.o \ + powerpc/vpermxor4.o \ + powerpc/vpermxor8.o +raid6_pq-$(CONFIG_RISCV_ISA_V) += riscv/rvv.o \ + riscv/recov_rvv.o +raid6_pq-$(CONFIG_S390) += s390/s390vx8.o \ + s390/recov_s390xc.o +ifeq ($(CONFIG_X86),y) +raid6_pq-$(CONFIG_X86_32) += x86/mmx.o \ + x86/sse1.o +endif +raid6_pq-$(CONFIG_X86) += x86/sse2.o \ + x86/avx2.o \ + x86/avx512.o \ + x86/recov_ssse3.o \ + x86/recov_avx2.o \ + x86/recov_avx512.o + +hostprogs += mktables + +CFLAGS_arm/neon1.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon2.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon4.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon8.o += $(CC_FLAGS_FPU) +CFLAGS_arm/recov_neon_inner.o += $(CC_FLAGS_FPU) +CFLAGS_REMOVE_arm/neon1.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon2.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon4.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon8.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/recov_neon_inner.o += $(CC_FLAGS_NO_FPU) + +ifeq ($(CONFIG_ALTIVEC),y) +altivec_flags := -maltivec $(call cc-option,-mabi=altivec) +# Enable +altivec_flags += -isystem $(shell $(CC) -print-file-name=include) + +CFLAGS_powerpc/altivec1.o += $(altivec_flags) +CFLAGS_powerpc/altivec2.o += $(altivec_flags) +CFLAGS_powerpc/altivec4.o += $(altivec_flags) +CFLAGS_powerpc/altivec8.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor1.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor2.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor4.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor8.o += $(altivec_flags) + +ifdef CONFIG_CC_IS_CLANG +# clang ppc port does not yet support -maltivec when -msoft-float is +# enabled. A future release of clang will resolve this +# https://llvm.org/pr31177 +CFLAGS_REMOVE_powerpc/altivec1.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec2.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec4.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec8.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor1.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor2.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor4.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor8.o += -msoft-float +endif # CONFIG_CC_IS_CLANG +endif # CONFIG_ALTIVEC + +quiet_cmd_mktable = TABLE $@ + cmd_mktable = $(obj)/mktables > $@ + +targets += tables.c +$(obj)/tables.c: $(obj)/mktables FORCE + $(call if_changed,mktable) + +quiet_cmd_unroll = UNROLL $@ + cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@ + +targets += int1.c int2.c int4.c int8.c +$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += arm/neon1.c arm/neon2.c arm/neon4.c arm/neon8.c +$(obj)/arm/neon%.c: $(src)/arm/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += powerpc/altivec1.c \ + powerpc/altivec2.c \ + powerpc/altivec4.c \ + powerpc/altivec8.c +$(obj)/powerpc/altivec%.c: $(src)/powerpc/altivec.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += powerpc/vpermxor1.c \ + powerpc/vpermxor2.c \ + powerpc/vpermxor4.c \ + powerpc/vpermxor8.c +$(obj)/powerpc/vpermxor%.c: $(src)/powerpc/vpermxor.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += s390/s390vx8.c +$(obj)/s390/s390vx%.c: $(src)/s390/s390vx.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) diff --git a/lib/raid6/algos.c b/lib/raid/raid6/algos.c similarity index 100% rename from lib/raid6/algos.c rename to lib/raid/raid6/algos.c diff --git a/lib/raid6/neon.c b/lib/raid/raid6/arm/neon.c similarity index 100% rename from lib/raid6/neon.c rename to lib/raid/raid6/arm/neon.c diff --git a/lib/raid6/neon.h b/lib/raid/raid6/arm/neon.h similarity index 100% rename from lib/raid6/neon.h rename to lib/raid/raid6/arm/neon.h diff --git a/lib/raid6/neon.uc b/lib/raid/raid6/arm/neon.uc similarity index 99% rename from lib/raid6/neon.uc rename to lib/raid/raid6/arm/neon.uc index 355270af0cd6..14a9fc2c60fa 100644 --- a/lib/raid6/neon.uc +++ b/lib/raid/raid6/arm/neon.uc @@ -25,7 +25,7 @@ */ #include -#include "neon.h" +#include "arm/neon.h" typedef uint8x16_t unative_t; diff --git a/lib/raid6/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c similarity index 99% rename from lib/raid6/recov_neon.c rename to lib/raid/raid6/arm/recov_neon.c index 13d5df718c15..5a48fcc762e8 100644 --- a/lib/raid6/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -6,7 +6,7 @@ #include #include -#include "neon.h" +#include "arm/neon.h" static int raid6_has_neon(void) { diff --git a/lib/raid6/recov_neon_inner.c b/lib/raid/raid6/arm/recov_neon_inner.c similarity index 99% rename from lib/raid6/recov_neon_inner.c rename to lib/raid/raid6/arm/recov_neon_inner.c index f9e7e8f5a151..53c355efa7ff 100644 --- a/lib/raid6/recov_neon_inner.c +++ b/lib/raid/raid6/arm/recov_neon_inner.c @@ -5,7 +5,7 @@ */ #include -#include "neon.h" +#include "arm/neon.h" #ifdef CONFIG_ARM /* diff --git a/lib/raid6/int.uc b/lib/raid/raid6/int.uc similarity index 100% rename from lib/raid6/int.uc rename to lib/raid/raid6/int.uc diff --git a/lib/raid6/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c similarity index 100% rename from lib/raid6/loongarch_simd.c rename to lib/raid/raid6/loongarch/loongarch_simd.c diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c similarity index 100% rename from lib/raid6/recov_loongarch_simd.c rename to lib/raid/raid6/loongarch/recov_loongarch_simd.c diff --git a/lib/raid6/mktables.c b/lib/raid/raid6/mktables.c similarity index 100% rename from lib/raid6/mktables.c rename to lib/raid/raid6/mktables.c diff --git a/lib/raid6/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc similarity index 98% rename from lib/raid6/altivec.uc rename to lib/raid/raid6/powerpc/altivec.uc index 2c59963e58f9..130d3d3dd42c 100644 --- a/lib/raid6/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -24,8 +24,6 @@ #include -#ifdef CONFIG_ALTIVEC - #include #include #include @@ -122,5 +120,3 @@ const struct raid6_calls raid6_altivec$# = { "altivecx$#", 0 }; - -#endif /* CONFIG_ALTIVEC */ diff --git a/lib/raid6/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc similarity index 98% rename from lib/raid6/vpermxor.uc rename to lib/raid/raid6/powerpc/vpermxor.uc index a8e76b1c956e..595f20aaf4cf 100644 --- a/lib/raid6/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -21,8 +21,6 @@ */ #include -#ifdef CONFIG_ALTIVEC - #include #include #include @@ -95,4 +93,3 @@ const struct raid6_calls raid6_vpermxor$# = { "vpermxor$#", 0 }; -#endif diff --git a/lib/raid6/recov.c b/lib/raid/raid6/recov.c similarity index 100% rename from lib/raid6/recov.c rename to lib/raid/raid6/recov.c diff --git a/lib/raid6/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c similarity index 100% rename from lib/raid6/recov_rvv.c rename to lib/raid/raid6/riscv/recov_rvv.c diff --git a/lib/raid6/rvv.c b/lib/raid/raid6/riscv/rvv.c similarity index 100% rename from lib/raid6/rvv.c rename to lib/raid/raid6/riscv/rvv.c diff --git a/lib/raid6/rvv.h b/lib/raid/raid6/riscv/rvv.h similarity index 100% rename from lib/raid6/rvv.h rename to lib/raid/raid6/riscv/rvv.h diff --git a/lib/raid6/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c similarity index 100% rename from lib/raid6/recov_s390xc.c rename to lib/raid/raid6/s390/recov_s390xc.c diff --git a/lib/raid6/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc similarity index 100% rename from lib/raid6/s390vx.uc rename to lib/raid/raid6/s390/s390vx.uc diff --git a/lib/raid6/test/Makefile b/lib/raid/raid6/tests/Makefile similarity index 77% rename from lib/raid6/test/Makefile rename to lib/raid/raid6/tests/Makefile index 520381ea71d7..87a001b22847 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid/raid6/tests/Makefile @@ -1,5 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o - -raid6_kunit-y += test.o diff --git a/lib/raid6/test/test.c b/lib/raid/raid6/tests/raid6_kunit.c similarity index 100% rename from lib/raid6/test/test.c rename to lib/raid/raid6/tests/raid6_kunit.c diff --git a/lib/raid6/unroll.awk b/lib/raid/raid6/unroll.awk similarity index 100% rename from lib/raid6/unroll.awk rename to lib/raid/raid6/unroll.awk diff --git a/lib/raid6/avx2.c b/lib/raid/raid6/x86/avx2.c similarity index 100% rename from lib/raid6/avx2.c rename to lib/raid/raid6/x86/avx2.c diff --git a/lib/raid6/avx512.c b/lib/raid/raid6/x86/avx512.c similarity index 100% rename from lib/raid6/avx512.c rename to lib/raid/raid6/x86/avx512.c diff --git a/lib/raid6/mmx.c b/lib/raid/raid6/x86/mmx.c similarity index 99% rename from lib/raid6/mmx.c rename to lib/raid/raid6/x86/mmx.c index e411f0cfbd95..7e9810669347 100644 --- a/lib/raid6/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -11,8 +11,6 @@ * MMX implementation of RAID-6 syndrome functions */ -#ifdef CONFIG_X86_32 - #include #include @@ -135,5 +133,3 @@ const struct raid6_calls raid6_mmxx2 = { "mmxx2", 0 }; - -#endif diff --git a/lib/raid6/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c similarity index 100% rename from lib/raid6/recov_avx2.c rename to lib/raid/raid6/x86/recov_avx2.c diff --git a/lib/raid6/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c similarity index 100% rename from lib/raid6/recov_avx512.c rename to lib/raid/raid6/x86/recov_avx512.c diff --git a/lib/raid6/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c similarity index 100% rename from lib/raid6/recov_ssse3.c rename to lib/raid/raid6/x86/recov_ssse3.c diff --git a/lib/raid6/sse1.c b/lib/raid/raid6/x86/sse1.c similarity index 99% rename from lib/raid6/sse1.c rename to lib/raid/raid6/x86/sse1.c index 794d5cfa0306..deecdd72ceec 100644 --- a/lib/raid6/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -16,8 +16,6 @@ * worthwhile as a separate implementation. */ -#ifdef CONFIG_X86_32 - #include #include @@ -155,5 +153,3 @@ const struct raid6_calls raid6_sse1x2 = { "sse1x2", 1 /* Has cache hints */ }; - -#endif diff --git a/lib/raid6/sse2.c b/lib/raid/raid6/x86/sse2.c similarity index 100% rename from lib/raid6/sse2.c rename to lib/raid/raid6/x86/sse2.c diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile deleted file mode 100644 index 6fd048c127b6..000000000000 --- a/lib/raid6/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/ - -raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ - int8.o - -raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o avx512.o recov_avx512.o -raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o \ - vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o -raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o -raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o -raid6_pq-$(CONFIG_LOONGARCH) += loongarch_simd.o recov_loongarch_simd.o -raid6_pq-$(CONFIG_RISCV_ISA_V) += rvv.o recov_rvv.o - -hostprogs += mktables - -ifeq ($(CONFIG_ALTIVEC),y) -altivec_flags := -maltivec $(call cc-option,-mabi=altivec) -# Enable -altivec_flags += -isystem $(shell $(CC) -print-file-name=include) - -ifdef CONFIG_CC_IS_CLANG -# clang ppc port does not yet support -maltivec when -msoft-float is -# enabled. A future release of clang will resolve this -# https://llvm.org/pr31177 -CFLAGS_REMOVE_altivec1.o += -msoft-float -CFLAGS_REMOVE_altivec2.o += -msoft-float -CFLAGS_REMOVE_altivec4.o += -msoft-float -CFLAGS_REMOVE_altivec8.o += -msoft-float -CFLAGS_REMOVE_vpermxor1.o += -msoft-float -CFLAGS_REMOVE_vpermxor2.o += -msoft-float -CFLAGS_REMOVE_vpermxor4.o += -msoft-float -CFLAGS_REMOVE_vpermxor8.o += -msoft-float -endif -endif - -quiet_cmd_unroll = UNROLL $@ - cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@ - -targets += int1.c int2.c int4.c int8.c -$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_altivec1.o += $(altivec_flags) -CFLAGS_altivec2.o += $(altivec_flags) -CFLAGS_altivec4.o += $(altivec_flags) -CFLAGS_altivec8.o += $(altivec_flags) -targets += altivec1.c altivec2.c altivec4.c altivec8.c -$(obj)/altivec%.c: $(src)/altivec.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_vpermxor1.o += $(altivec_flags) -CFLAGS_vpermxor2.o += $(altivec_flags) -CFLAGS_vpermxor4.o += $(altivec_flags) -CFLAGS_vpermxor8.o += $(altivec_flags) -targets += vpermxor1.c vpermxor2.c vpermxor4.c vpermxor8.c -$(obj)/vpermxor%.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_neon1.o += $(CC_FLAGS_FPU) -CFLAGS_neon2.o += $(CC_FLAGS_FPU) -CFLAGS_neon4.o += $(CC_FLAGS_FPU) -CFLAGS_neon8.o += $(CC_FLAGS_FPU) -CFLAGS_recov_neon_inner.o += $(CC_FLAGS_FPU) -CFLAGS_REMOVE_neon1.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon2.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon4.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon8.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_recov_neon_inner.o += $(CC_FLAGS_NO_FPU) -targets += neon1.c neon2.c neon4.c neon8.c -$(obj)/neon%.c: $(src)/neon.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += s390vx8.c -$(obj)/s390vx%.c: $(src)/s390vx.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -quiet_cmd_mktable = TABLE $@ - cmd_mktable = $(obj)/mktables > $@ - -targets += tables.c -$(obj)/tables.c: $(obj)/mktables FORCE - $(call if_changed,mktable) diff --git a/lib/raid6/test/.gitignore b/lib/raid6/test/.gitignore deleted file mode 100644 index 1b68a77f348f..000000000000 --- a/lib/raid6/test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/int.uc -/neon.uc -/raid6test From 06d2a66fb7c0b33ce893edb2f695add88291bff2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:47 +0200 Subject: [PATCH 2128/5214] raid6: remove unused defines in pq.h These are not used anywhere in the kernel. Link: https://lore.kernel.org/20260518051804.462141-5-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index d26788fada58..5e7e743b83f5 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -90,12 +90,6 @@ extern const struct raid6_calls raid6_neonx8; extern const struct raid6_calls * const raid6_algos[]; extern const struct raid6_recov_calls *const raid6_recov_algos[]; -/* Return values from chk_syndrome */ -#define RAID6_OK 0 -#define RAID6_P_BAD 1 -#define RAID6_Q_BAD 2 -#define RAID6_PQ_BAD 3 - /* Galois field tables */ extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); From 885d314231837a58258edc2757def1ee7fa0138c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:48 +0200 Subject: [PATCH 2129/5214] raid6: remove raid6_get_zero_page Just open code it as in other places in the kernel. Link: https://lore.kernel.org/20260518051804.462141-6-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- crypto/async_tx/async_pq.c | 2 +- crypto/async_tx/async_raid6_recov.c | 4 ++-- include/linux/raid/pq.h | 6 ------ lib/raid/raid6/arm/recov_neon.c | 6 +++--- lib/raid/raid6/loongarch/recov_loongarch_simd.c | 12 ++++++------ lib/raid/raid6/recov.c | 6 +++--- lib/raid/raid6/riscv/recov_rvv.c | 6 +++--- lib/raid/raid6/s390/recov_s390xc.c | 6 +++--- lib/raid/raid6/x86/recov_avx2.c | 6 +++--- lib/raid/raid6/x86/recov_avx512.c | 6 +++--- lib/raid/raid6/x86/recov_ssse3.c | 6 +++--- 11 files changed, 30 insertions(+), 36 deletions(-) diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index 9e4bb7fbde25..0ce6f07b4e0d 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -119,7 +119,7 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, for (i = 0; i < disks; i++) { if (blocks[i] == NULL) { BUG_ON(i > disks - 3); /* P or Q can't be zero */ - srcs[i] = raid6_get_zero_page(); + srcs[i] = page_address(ZERO_PAGE(0)); } else { srcs[i] = page_address(blocks[i]) + offsets[i]; diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index 539ea5b378dc..f2dc6af6e6a7 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -414,7 +414,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = raid6_get_zero_page(); + ptrs[i] = page_address(ZERO_PAGE(0)); else ptrs[i] = page_address(blocks[i]) + offs[i]; @@ -497,7 +497,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = raid6_get_zero_page(); + ptrs[i] = page_address(ZERO_PAGE(0)); else ptrs[i] = page_address(blocks[i]) + offs[i]; diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 5e7e743b83f5..f27a866c287f 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,12 +11,6 @@ #include #include -/* This should be const but the raid6 code is too convoluted for that. */ -static inline void *raid6_get_zero_page(void) -{ - return page_address(ZERO_PAGE(0)); -} - /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 5a48fcc762e8..9993bda5d3a6 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -29,10 +29,10 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -66,7 +66,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index eb3a1e79f01f..4d4563209647 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -43,10 +43,10 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -198,7 +198,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -317,10 +317,10 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -437,7 +437,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 8d113196632e..211e1df28963 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -31,10 +31,10 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -72,7 +72,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 40c393206b6a..f77d9c430687 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -158,10 +158,10 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -196,7 +196,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 487018f81192..0f32217b7123 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -34,10 +34,10 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -81,7 +81,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 19fbd9c4dce6..325310c81e1c 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -28,10 +28,10 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -196,7 +196,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 143f4976b2ad..08de77fcb8bd 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -37,10 +37,10 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -238,7 +238,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 146cdbf465bd..002bef1e0847 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -30,10 +30,10 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -203,7 +203,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); From 7e91f76a96686b3341c96e1e7f3e86c0f51e2cff Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:49 +0200 Subject: [PATCH 2130/5214] raid6: use named initializers for struct raid6_calls Link: https://lore.kernel.org/20260518051804.462141-7-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/arm/neon.c | 9 +++---- lib/raid/raid6/int.uc | 8 +++--- lib/raid/raid6/loongarch/loongarch_simd.c | 18 ++++++------- lib/raid/raid6/powerpc/altivec.uc | 8 +++--- lib/raid/raid6/powerpc/vpermxor.uc | 8 +++--- lib/raid/raid6/riscv/rvv.h | 9 +++---- lib/raid/raid6/s390/s390vx.uc | 10 +++---- lib/raid/raid6/x86/avx2.c | 33 ++++++++++++----------- lib/raid/raid6/x86/avx512.c | 33 ++++++++++++----------- lib/raid/raid6/x86/mmx.c | 16 +++++------ lib/raid/raid6/x86/sse1.c | 18 ++++++------- lib/raid/raid6/x86/sse2.c | 30 ++++++++++----------- 12 files changed, 95 insertions(+), 105 deletions(-) diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index 47b8bb0afc65..c21da59ab48f 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -40,11 +40,10 @@ start, stop, (unsigned long)bytes, ptrs);\ } \ struct raid6_calls const raid6_neonx ## _n = { \ - raid6_neon ## _n ## _gen_syndrome, \ - raid6_neon ## _n ## _xor_syndrome, \ - raid6_have_neon, \ - "neonx" #_n, \ - 0 \ + .gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \ + .xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \ + .valid = raid6_have_neon, \ + .name = "neonx" #_n, \ } static int raid6_have_neon(void) diff --git a/lib/raid/raid6/int.uc b/lib/raid/raid6/int.uc index 1ba56c3fa482..4f5f2869e21e 100644 --- a/lib/raid/raid6/int.uc +++ b/lib/raid/raid6/int.uc @@ -139,9 +139,7 @@ static void raid6_int$#_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_intx$# = { - raid6_int$#_gen_syndrome, - raid6_int$#_xor_syndrome, - NULL, /* always valid */ - "int" NSTRING "x$#", - 0 + .gen_syndrome = raid6_int$#_gen_syndrome, + .xor_syndrome = raid6_int$#_xor_syndrome, + .name = "int" NSTRING "x$#", }; diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index 72f4d92d4876..1b4cd1512d05 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -244,11 +244,10 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_lsx = { - raid6_lsx_gen_syndrome, - raid6_lsx_xor_syndrome, - raid6_has_lsx, - "lsx", - .priority = 0 /* see the comment near the top of the file for reason */ + .gen_syndrome = raid6_lsx_gen_syndrome, + .xor_syndrome = raid6_lsx_xor_syndrome, + .valid = raid6_has_lsx, + .name = "lsx", }; #undef NSIZE @@ -413,11 +412,10 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_lasx = { - raid6_lasx_gen_syndrome, - raid6_lasx_xor_syndrome, - raid6_has_lasx, - "lasx", - .priority = 0 /* see the comment near the top of the file for reason */ + .gen_syndrome = raid6_lasx_gen_syndrome, + .xor_syndrome = raid6_lasx_xor_syndrome, + .valid = raid6_has_lasx, + .name = "lasx", }; #undef NSIZE #endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index 130d3d3dd42c..084ead768ddb 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -114,9 +114,7 @@ int raid6_have_altivec(void) #endif const struct raid6_calls raid6_altivec$# = { - raid6_altivec$#_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_altivec, - "altivecx$#", - 0 + .gen_syndrome = raid6_altivec$#_gen_syndrome, + .valid = raid6_have_altivec, + .name = "altivecx$#", }; diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index 595f20aaf4cf..bb2c3a316ae8 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -87,9 +87,7 @@ int raid6_have_altivec_vpermxor(void) #endif const struct raid6_calls raid6_vpermxor$# = { - raid6_vpermxor$#_gen_syndrome, - NULL, - raid6_have_altivec_vpermxor, - "vpermxor$#", - 0 + .gen_syndrome = raid6_vpermxor$#_gen_syndrome, + .valid = raid6_have_altivec_vpermxor, + .name = "vpermxor$#", }; diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index b0a71b375962..0d430a4c5f08 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -39,9 +39,8 @@ static int rvv_has_vector(void) kernel_vector_end(); \ } \ struct raid6_calls const raid6_rvvx ## _n = { \ - raid6_rvv ## _n ## _gen_syndrome, \ - raid6_rvv ## _n ## _xor_syndrome, \ - rvv_has_vector, \ - "rvvx" #_n, \ - 0 \ + .gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \ + .xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \ + .valid = rvv_has_vector, \ + .name = "rvvx" #_n, \ } diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index 8aa53eb2f395..97c5d5d9dcf9 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -127,9 +127,9 @@ static int raid6_s390vx$#_valid(void) } const struct raid6_calls raid6_s390vx$# = { - raid6_s390vx$#_gen_syndrome, - raid6_s390vx$#_xor_syndrome, - raid6_s390vx$#_valid, - "vx128x$#", - 1 + .gen_syndrome = raid6_s390vx$#_gen_syndrome, + .xor_syndrome = raid6_s390vx$#_xor_syndrome, + .valid = raid6_s390vx$#_valid, + .name = "vx128x$#", + .priority = 1, }; diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index a1a5213918af..aab8b624c635 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -128,11 +128,12 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x1 = { - raid6_avx21_gen_syndrome, - raid6_avx21_xor_syndrome, - raid6_have_avx2, - "avx2x1", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx21_gen_syndrome, + .xor_syndrome = raid6_avx21_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x1", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; /* @@ -258,11 +259,12 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x2 = { - raid6_avx22_gen_syndrome, - raid6_avx22_xor_syndrome, - raid6_have_avx2, - "avx2x2", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx22_gen_syndrome, + .xor_syndrome = raid6_avx22_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x2", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; #ifdef CONFIG_X86_64 @@ -461,10 +463,11 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x4 = { - raid6_avx24_gen_syndrome, - raid6_avx24_xor_syndrome, - raid6_have_avx2, - "avx2x4", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx24_gen_syndrome, + .xor_syndrome = raid6_avx24_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x4", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; #endif /* CONFIG_X86_64 */ diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 874998bcd7d7..47636b16632f 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -156,11 +156,12 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx512x1 = { - raid6_avx5121_gen_syndrome, - raid6_avx5121_xor_syndrome, - raid6_have_avx512, - "avx512x1", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5121_gen_syndrome, + .xor_syndrome = raid6_avx5121_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x1", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; /* @@ -313,11 +314,12 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx512x2 = { - raid6_avx5122_gen_syndrome, - raid6_avx5122_xor_syndrome, - raid6_have_avx512, - "avx512x2", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5122_gen_syndrome, + .xor_syndrome = raid6_avx5122_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x2", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; #ifdef CONFIG_X86_64 @@ -551,10 +553,11 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, kernel_fpu_end(); } const struct raid6_calls raid6_avx512x4 = { - raid6_avx5124_gen_syndrome, - raid6_avx5124_xor_syndrome, - raid6_have_avx512, - "avx512x4", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5124_gen_syndrome, + .xor_syndrome = raid6_avx5124_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x4", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; #endif diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 7e9810669347..22b9fdaa705f 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -68,11 +68,9 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_mmxx1 = { - raid6_mmx1_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_mmx, - "mmxx1", - 0 + .gen_syndrome = raid6_mmx1_gen_syndrome, + .valid = raid6_have_mmx, + .name = "mmxx1", }; /* @@ -127,9 +125,7 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_mmxx2 = { - raid6_mmx2_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_mmx, - "mmxx2", - 0 + .gen_syndrome = raid6_mmx2_gen_syndrome, + .valid = raid6_have_mmx, + .name = "mmxx2", }; diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index deecdd72ceec..fad214a430d8 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -84,11 +84,10 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_sse1x1 = { - raid6_sse11_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_sse1_or_mmxext, - "sse1x1", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse11_gen_syndrome, + .valid = raid6_have_sse1_or_mmxext, + .name = "sse1x1", + .priority = 1, /* Has cache hints */ }; /* @@ -147,9 +146,8 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_sse1x2 = { - raid6_sse12_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_sse1_or_mmxext, - "sse1x2", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse12_gen_syndrome, + .valid = raid6_have_sse1_or_mmxext, + .name = "sse1x2", + .priority = 1, /* Has cache hints */ }; diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index f9edf8a8d1c4..1b28e858a1d4 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -133,11 +133,11 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_sse2x1 = { - raid6_sse21_gen_syndrome, - raid6_sse21_xor_syndrome, - raid6_have_sse2, - "sse2x1", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse21_gen_syndrome, + .xor_syndrome = raid6_sse21_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x1", + .priority = 1, /* Has cache hints */ }; /* @@ -263,11 +263,11 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_sse2x2 = { - raid6_sse22_gen_syndrome, - raid6_sse22_xor_syndrome, - raid6_have_sse2, - "sse2x2", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse22_gen_syndrome, + .xor_syndrome = raid6_sse22_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x2", + .priority = 1, /* Has cache hints */ }; #ifdef CONFIG_X86_64 @@ -470,11 +470,11 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x4 = { - raid6_sse24_gen_syndrome, - raid6_sse24_xor_syndrome, - raid6_have_sse2, - "sse2x4", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse24_gen_syndrome, + .xor_syndrome = raid6_sse24_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x4", + .priority = 1, /* Has cache hints */ }; #endif /* CONFIG_X86_64 */ From 35472bc6f31b64e58d1fe560340aa4f1684b8d6d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:50 +0200 Subject: [PATCH 2131/5214] raid6: improve the public interface Stop directly calling into function pointers from users of the RAID6 PQ API, and provide exported functions with proper documentation and API guarantees asserts where applicable instead. Link: https://lore.kernel.org/20260518051804.462141-8-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- Documentation/crypto/async-tx-api.rst | 4 +- crypto/async_tx/async_pq.c | 6 +- crypto/async_tx/async_raid6_recov.c | 4 +- drivers/md/raid5.c | 4 +- fs/btrfs/raid56.c | 8 +- include/linux/raid/pq.h | 19 +-- lib/raid/raid6/algos.c | 137 +++++++++++++++++- lib/raid/raid6/arm/recov_neon.c | 4 +- .../raid6/loongarch/recov_loongarch_simd.c | 8 +- lib/raid/raid6/recov.c | 4 +- lib/raid/raid6/riscv/recov_rvv.c | 4 +- lib/raid/raid6/s390/recov_s390xc.c | 4 +- lib/raid/raid6/x86/recov_avx2.c | 4 +- lib/raid/raid6/x86/recov_avx512.c | 4 +- lib/raid/raid6/x86/recov_ssse3.c | 4 +- 15 files changed, 170 insertions(+), 48 deletions(-) diff --git a/Documentation/crypto/async-tx-api.rst b/Documentation/crypto/async-tx-api.rst index f88a7809385e..49fcfc66314a 100644 --- a/Documentation/crypto/async-tx-api.rst +++ b/Documentation/crypto/async-tx-api.rst @@ -82,9 +82,9 @@ xor_val xor a series of source buffers and set a flag if the pq generate the p+q (raid6 syndrome) from a series of source buffers pq_val validate that a p and or q buffer are in sync with a given series of sources -datap (raid6_datap_recov) recover a raid6 data block and the p block +datap (raid6_recov_datap) recover a raid6 data block and the p block from the given sources -2data (raid6_2data_recov) recover 2 raid6 data blocks from the given +2data (raid6_recov_2data) recover 2 raid6 data blocks from the given sources ======== ==================================================================== diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index 0ce6f07b4e0d..f3574f80d1df 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -131,11 +131,11 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, } } if (submit->flags & ASYNC_TX_PQ_XOR_DST) { - BUG_ON(!raid6_call.xor_syndrome); + BUG_ON(!raid6_can_xor_syndrome()); if (start >= 0) - raid6_call.xor_syndrome(disks, start, stop, len, srcs); + raid6_xor_syndrome(disks, start, stop, len, srcs); } else - raid6_call.gen_syndrome(disks, len, srcs); + raid6_gen_syndrome(disks, len, srcs); async_tx_sync_epilog(submit); } diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index f2dc6af6e6a7..305ea1421a3e 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -418,7 +418,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, else ptrs[i] = page_address(blocks[i]) + offs[i]; - raid6_2data_recov(disks, bytes, faila, failb, ptrs); + raid6_recov_2data(disks, bytes, faila, failb, ptrs); async_tx_sync_epilog(submit); @@ -501,7 +501,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, else ptrs[i] = page_address(blocks[i]) + offs[i]; - raid6_datap_recov(disks, bytes, faila, ptrs); + raid6_recov_datap(disks, bytes, faila, ptrs); async_tx_sync_epilog(submit); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0d76e82f4506..ebcb19317670 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6955,7 +6955,7 @@ raid5_store_rmw_level(struct mddev *mddev, const char *page, size_t len) if (kstrtoul(page, 10, &new)) return -EINVAL; - if (new != PARITY_DISABLE_RMW && !raid6_call.xor_syndrome) + if (new != PARITY_DISABLE_RMW && !raid6_can_xor_syndrome()) return -EINVAL; if (new != PARITY_DISABLE_RMW && @@ -7646,7 +7646,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) conf->level = mddev->new_level; if (conf->level == 6) { conf->max_degraded = 2; - if (raid6_call.xor_syndrome) + if (raid6_can_xor_syndrome()) conf->rmw_level = PARITY_ENABLE_RMW; else conf->rmw_level = PARITY_DISABLE_RMW; diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 08ee8f316d96..dabc9522e881 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1410,7 +1410,7 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int rbio_qstripe_paddr(rbio, sector_nr, step_nr)); assert_rbio(rbio); - raid6_call.gen_syndrome(rbio->real_stripes, step, pointers); + raid6_gen_syndrome(rbio->real_stripes, step, pointers); } else { /* raid5 */ memcpy(pointers[rbio->nr_data], pointers[0], step); @@ -1987,10 +1987,10 @@ static void recover_vertical_step(struct btrfs_raid_bio *rbio, } if (failb == rbio->real_stripes - 2) { - raid6_datap_recov(rbio->real_stripes, step, + raid6_recov_datap(rbio->real_stripes, step, faila, pointers); } else { - raid6_2data_recov(rbio->real_stripes, step, + raid6_recov_2data(rbio->real_stripes, step, faila, failb, pointers); } } else { @@ -2644,7 +2644,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio, if (has_qstripe) { assert_rbio(rbio); /* RAID6, call the library function to fill in our P/Q. */ - raid6_call.gen_syndrome(rbio->real_stripes, step, pointers); + raid6_gen_syndrome(rbio->real_stripes, step, pointers); } else { /* RAID5. */ memcpy(pointers[nr_data], pointers[0], step); diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index f27a866c287f..425a227591c0 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,6 +11,16 @@ #include #include +void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs); +void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, + void **ptrs); +bool raid6_can_xor_syndrome(void); + +void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, + void **ptrs); +void raid6_recov_datap(int disks, size_t bytes, int faila, + void **ptrs); + /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); @@ -20,9 +30,6 @@ struct raid6_calls { int priority; /* Relative priority ranking if non-zero */ }; -/* Selected algorithm */ -extern struct raid6_calls raid6_call; - /* Various routine sets */ extern const struct raid6_calls raid6_intx1; extern const struct raid6_calls raid6_intx2; @@ -92,10 +99,4 @@ extern const u8 raid6_gflog[256] __attribute__((aligned(256))); extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); -/* Recovery routines */ -extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, - void **ptrs); -extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, - void **ptrs); - #endif /* LINUX_RAID_RAID6_H */ diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 985c60bb00a4..b0ba31f6d48e 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -16,8 +16,83 @@ #include #include -struct raid6_calls raid6_call; -EXPORT_SYMBOL_GPL(raid6_call); +static const struct raid6_recov_calls *raid6_recov_algo; + +/* Selected algorithm */ +static struct raid6_calls raid6_call; + +/** + * raid6_gen_syndrome - generate RAID6 P/Q parity + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @ptrs: @disks size array of memory pointers + * + * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and + * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to + * @ptrs[@disks - 3]. + * + * @disks must be at least 4, and the memory pointed to by each member of @ptrs + * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of + * 512. + * + * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying + * algorithm. + */ +void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + + raid6_call.gen_syndrome(disks, bytes, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_gen_syndrome); + +/** + * raid6_xor_syndrome - update RAID6 P/Q parity + * @disks: number of "disks" to operate on including parity + * @start: first index into @disk to update + * @stop: last index into @disk to update + * @bytes: length in bytes of each vector + * @ptrs: @disks size array of memory pointers + * + * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and + * @ptrs[@disks - 1] respectively for the memory pointed to by + * @ptrs[@start..@stop]. + * + * This is used to update parity in place using the following sequence: + * + * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data. + * 2) update the the data in @ptrs[@start..@stop]. + * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data. + * + * Data between @start and @stop that is not changed should be filled + * with a pointer to the kernel zero page. + * + * @disks must be at least 4, and the memory pointed to by each member of @ptrs + * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of + * 512. @stop must be larger or equal to @start. + */ +void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, + void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(stop < start); + + raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_xor_syndrome); + +/* + * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used + * + * Returns %true if raid6_can_xor_syndrome() can be used, else %false. + */ +bool raid6_can_xor_syndrome(void) +{ + return !!raid6_call.xor_syndrome; +} +EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); const struct raid6_calls * const raid6_algos[] = { #if defined(__i386__) && !defined(__arch_um__) @@ -84,11 +159,58 @@ const struct raid6_calls * const raid6_algos[] = { }; EXPORT_SYMBOL_IF_KUNIT(raid6_algos); -void (*raid6_2data_recov)(int, size_t, int, int, void **); -EXPORT_SYMBOL_GPL(raid6_2data_recov); +/** + * raid6_recov_2data - recover two missing data disks + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @faila: first failed data disk index + * @failb: second failed data disk index + * @ptrs: @disks size array of memory pointers + * + * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the + * data in the remaining disks and the two parities pointed to by the other + * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks + * and the two parities. @faila must be smaller than @failb. + * + * Memory pointed to by each pointer in @ptrs must be page aligned and is + * limited to %PAGE_SIZE. + */ +void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, + void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(bytes > PAGE_SIZE); + WARN_ON_ONCE(failb <= faila); -void (*raid6_datap_recov)(int, size_t, int, void **); -EXPORT_SYMBOL_GPL(raid6_datap_recov); + raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_recov_2data); + +/** + * raid6_recov_datap - recover a missing data disk and missing P-parity + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @faila: failed data disk index + * @ptrs: @disks size array of memory pointers + * + * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in + * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity + * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks + * includes the data disks and the two parities. + * + * Memory pointed to by each pointer in @ptrs must be page aligned and is + * limited to %PAGE_SIZE. + */ +void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(bytes > PAGE_SIZE); + + raid6_recov_algo->datap(disks, bytes, faila, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_recov_datap); const struct raid6_recov_calls *const raid6_recov_algos[] = { #ifdef CONFIG_X86 @@ -133,8 +255,7 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void) best = *algo; if (best) { - raid6_2data_recov = best->data2; - raid6_datap_recov = best->datap; + raid6_recov_algo = best; pr_info("raid6: using %s recovery algorithm\n", best->name); } else diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 9993bda5d3a6..4eb0efb44750 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -35,7 +35,7 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -69,7 +69,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 4d4563209647..7d4d349322b3 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -49,7 +49,7 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -201,7 +201,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; @@ -323,7 +323,7 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -440,7 +440,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 211e1df28963..cc7e4dc1eaa6 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -37,7 +37,7 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -75,7 +75,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index f77d9c430687..3ff39826e33f 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -164,7 +164,7 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -199,7 +199,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 0f32217b7123..2bc4c85174de 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -40,7 +40,7 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -84,7 +84,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 325310c81e1c..bef82a38d8eb 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -34,7 +34,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -199,7 +199,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 08de77fcb8bd..06c70e771eaa 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -43,7 +43,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -241,7 +241,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 002bef1e0847..5ca7d56f23d8 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -36,7 +36,7 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -206,7 +206,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; From 2790045a62eb52a5052eed06ddcc10b03b007a39 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:51 +0200 Subject: [PATCH 2132/5214] raid6: warn when using less than four devices Quoting H. Peter Anvin who came up with the RAID6 P/Q algorithm, and who wrote the initial implementation, then still part of the md driver: The RAID-6 code has *never* supported only 3 units, and if it ever worked for *any* of the implementations it was purely by accident. Speaking as the original author I should know; this was deliberate as in some cases the degenerate case (3) would have required extra trays in the code to no user benefit. While md never allowed less than 4 devices, btrfs does. This new warning will trigger for such file systems, but given how it already causes havoc that is a good thing. If btrfs wants to fix third, it should switch to transparently use three-way mirroring underneath, which will work as P and Q are copies of the single data device by the definition of the Linux RAID 6 P/Q algorithm. Link: https://lore.kernel.org/20260518051804.462141-9-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 2 ++ lib/raid/raid6/algos.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 425a227591c0..87e3cb55bf07 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,6 +11,8 @@ #include #include +#define RAID6_MIN_DISKS 4 + void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs); void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, void **ptrs); diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index b0ba31f6d48e..63d1945ba63c 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -42,6 +42,7 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) { WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(disks < RAID6_MIN_DISKS); raid6_call.gen_syndrome(disks, bytes, ptrs); } @@ -77,6 +78,7 @@ void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, { WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(disks < RAID6_MIN_DISKS); WARN_ON_ONCE(stop < start); raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); From 769d603fc44f896e7f61de7f0cdb8b78d46bc8c8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:52 +0200 Subject: [PATCH 2133/5214] raid6: hide internals Split out two new headers from the public pq.h: - lib/raid/raid6/algos.h contains the algorithm lists private to lib/raid/raid6 - include/linux/raid/pq_tables.h contains the tables also used by async_tx providers. The public include/linux/pq.h is now limited to the public interface for the consumers of the RAID6 PQ API. [hch@lst.de: remove duplicate ccflags-y line] Link: https://lore.kernel.org/20260527074539.2292913-2-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-10-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- crypto/async_tx/async_pq.c | 1 + crypto/async_tx/async_raid6_recov.c | 1 + drivers/dma/bcm-sba-raid.c | 1 + include/linux/raid/pq.h | 96 ++----------------- include/linux/raid/pq_tables.h | 19 ++++ lib/raid/raid6/algos.c | 3 +- lib/raid/raid6/algos.h | 82 ++++++++++++++++ lib/raid/raid6/arm/neon.c | 2 +- lib/raid/raid6/arm/recov_neon.c | 2 + lib/raid/raid6/int.uc | 2 +- lib/raid/raid6/loongarch/loongarch_simd.c | 2 +- .../raid6/loongarch/recov_loongarch_simd.c | 2 + lib/raid/raid6/mktables.c | 2 +- lib/raid/raid6/powerpc/altivec.uc | 2 +- lib/raid/raid6/powerpc/vpermxor.uc | 2 +- lib/raid/raid6/recov.c | 2 + lib/raid/raid6/riscv/recov_rvv.c | 2 + lib/raid/raid6/riscv/rvv.h | 2 +- lib/raid/raid6/s390/recov_s390xc.c | 2 + lib/raid/raid6/s390/s390vx.uc | 2 +- lib/raid/raid6/tests/raid6_kunit.c | 2 +- lib/raid/raid6/x86/avx2.c | 3 +- lib/raid/raid6/x86/avx512.c | 3 +- lib/raid/raid6/x86/mmx.c | 3 +- lib/raid/raid6/x86/recov_avx2.c | 2 + lib/raid/raid6/x86/recov_avx512.c | 2 + lib/raid/raid6/x86/recov_ssse3.c | 2 + lib/raid/raid6/x86/sse1.c | 3 +- lib/raid/raid6/x86/sse2.c | 3 +- 29 files changed, 149 insertions(+), 103 deletions(-) create mode 100644 include/linux/raid/pq_tables.h create mode 100644 lib/raid/raid6/algos.h diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index f3574f80d1df..27f99349e310 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index 305ea1421a3e..e53870d84bc5 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index ed037fa883f6..0de03611252e 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "dmaengine.h" diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 87e3cb55bf07..b3bf9339cd86 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -1,15 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* -*- linux-c -*- ------------------------------------------------------- * +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved * - * Copyright 2003 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ + * Public interface to the RAID6 P/Q calculation and recovery library. + */ +#ifndef LINUX_RAID_PQ_H +#define LINUX_RAID_PQ_H -#ifndef LINUX_RAID_RAID6_H -#define LINUX_RAID_RAID6_H - -#include -#include +#include #define RAID6_MIN_DISKS 4 @@ -23,82 +21,4 @@ void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs); -/* Routine choices */ -struct raid6_calls { - void (*gen_syndrome)(int, size_t, void **); - void (*xor_syndrome)(int, int, int, size_t, void **); - int (*valid)(void); /* Returns 1 if this routine set is usable */ - const char *name; /* Name of this routine set */ - int priority; /* Relative priority ranking if non-zero */ -}; - -/* Various routine sets */ -extern const struct raid6_calls raid6_intx1; -extern const struct raid6_calls raid6_intx2; -extern const struct raid6_calls raid6_intx4; -extern const struct raid6_calls raid6_intx8; -extern const struct raid6_calls raid6_mmxx1; -extern const struct raid6_calls raid6_mmxx2; -extern const struct raid6_calls raid6_sse1x1; -extern const struct raid6_calls raid6_sse1x2; -extern const struct raid6_calls raid6_sse2x1; -extern const struct raid6_calls raid6_sse2x2; -extern const struct raid6_calls raid6_sse2x4; -extern const struct raid6_calls raid6_altivec1; -extern const struct raid6_calls raid6_altivec2; -extern const struct raid6_calls raid6_altivec4; -extern const struct raid6_calls raid6_altivec8; -extern const struct raid6_calls raid6_avx2x1; -extern const struct raid6_calls raid6_avx2x2; -extern const struct raid6_calls raid6_avx2x4; -extern const struct raid6_calls raid6_avx512x1; -extern const struct raid6_calls raid6_avx512x2; -extern const struct raid6_calls raid6_avx512x4; -extern const struct raid6_calls raid6_s390vx8; -extern const struct raid6_calls raid6_vpermxor1; -extern const struct raid6_calls raid6_vpermxor2; -extern const struct raid6_calls raid6_vpermxor4; -extern const struct raid6_calls raid6_vpermxor8; -extern const struct raid6_calls raid6_lsx; -extern const struct raid6_calls raid6_lasx; -extern const struct raid6_calls raid6_rvvx1; -extern const struct raid6_calls raid6_rvvx2; -extern const struct raid6_calls raid6_rvvx4; -extern const struct raid6_calls raid6_rvvx8; - -struct raid6_recov_calls { - void (*data2)(int, size_t, int, int, void **); - void (*datap)(int, size_t, int, void **); - int (*valid)(void); - const char *name; - int priority; -}; - -extern const struct raid6_recov_calls raid6_recov_intx1; -extern const struct raid6_recov_calls raid6_recov_ssse3; -extern const struct raid6_recov_calls raid6_recov_avx2; -extern const struct raid6_recov_calls raid6_recov_avx512; -extern const struct raid6_recov_calls raid6_recov_s390xc; -extern const struct raid6_recov_calls raid6_recov_neon; -extern const struct raid6_recov_calls raid6_recov_lsx; -extern const struct raid6_recov_calls raid6_recov_lasx; -extern const struct raid6_recov_calls raid6_recov_rvv; - -extern const struct raid6_calls raid6_neonx1; -extern const struct raid6_calls raid6_neonx2; -extern const struct raid6_calls raid6_neonx4; -extern const struct raid6_calls raid6_neonx8; - -/* Algorithm list */ -extern const struct raid6_calls * const raid6_algos[]; -extern const struct raid6_recov_calls *const raid6_recov_algos[]; - -/* Galois field tables */ -extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); -extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); -extern const u8 raid6_gfexp[256] __attribute__((aligned(256))); -extern const u8 raid6_gflog[256] __attribute__((aligned(256))); -extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); -extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); - -#endif /* LINUX_RAID_RAID6_H */ +#endif /* LINUX_RAID_PQ_H */ diff --git a/include/linux/raid/pq_tables.h b/include/linux/raid/pq_tables.h new file mode 100644 index 000000000000..7b1ebe675677 --- /dev/null +++ b/include/linux/raid/pq_tables.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved + * + * Galois field tables for the Linux RAID6 P/Q parity algorithm. + */ +#ifndef _LINUX_RAID_PQ_TABLES_H +#define _LINUX_RAID_PQ_TABLES_H + +#include + +extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); +extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); +extern const u8 raid6_gfexp[256] __attribute__((aligned(256))); +extern const u8 raid6_gflog[256] __attribute__((aligned(256))); +extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); +extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); + +#endif /* _LINUX_RAID_PQ_TABLES_H */ diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 63d1945ba63c..d83ca4dac864 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -11,10 +11,11 @@ * Algorithm list and algorithm selection for RAID-6 */ -#include #include #include +#include #include +#include "algos.h" static const struct raid6_recov_calls *raid6_recov_algo; diff --git a/lib/raid/raid6/algos.h b/lib/raid/raid6/algos.h new file mode 100644 index 000000000000..e5f1098d2179 --- /dev/null +++ b/lib/raid/raid6/algos.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved + */ +#ifndef _PQ_IMPL_H +#define _PQ_IMPL_H + +#include + +/* Routine choices */ +struct raid6_calls { + const char *name; + void (*gen_syndrome)(int disks, size_t bytes, void **ptrs); + void (*xor_syndrome)(int disks, int start, int stop, size_t bytes, + void **ptrs); + int (*valid)(void); /* Returns 1 if this routine set is usable */ + int priority; /* Relative priority ranking if non-zero */ +}; + +/* Various routine sets */ +extern const struct raid6_calls raid6_intx1; +extern const struct raid6_calls raid6_intx2; +extern const struct raid6_calls raid6_intx4; +extern const struct raid6_calls raid6_intx8; +extern const struct raid6_calls raid6_mmxx1; +extern const struct raid6_calls raid6_mmxx2; +extern const struct raid6_calls raid6_sse1x1; +extern const struct raid6_calls raid6_sse1x2; +extern const struct raid6_calls raid6_sse2x1; +extern const struct raid6_calls raid6_sse2x2; +extern const struct raid6_calls raid6_sse2x4; +extern const struct raid6_calls raid6_altivec1; +extern const struct raid6_calls raid6_altivec2; +extern const struct raid6_calls raid6_altivec4; +extern const struct raid6_calls raid6_altivec8; +extern const struct raid6_calls raid6_avx2x1; +extern const struct raid6_calls raid6_avx2x2; +extern const struct raid6_calls raid6_avx2x4; +extern const struct raid6_calls raid6_avx512x1; +extern const struct raid6_calls raid6_avx512x2; +extern const struct raid6_calls raid6_avx512x4; +extern const struct raid6_calls raid6_s390vx8; +extern const struct raid6_calls raid6_vpermxor1; +extern const struct raid6_calls raid6_vpermxor2; +extern const struct raid6_calls raid6_vpermxor4; +extern const struct raid6_calls raid6_vpermxor8; +extern const struct raid6_calls raid6_lsx; +extern const struct raid6_calls raid6_lasx; +extern const struct raid6_calls raid6_rvvx1; +extern const struct raid6_calls raid6_rvvx2; +extern const struct raid6_calls raid6_rvvx4; +extern const struct raid6_calls raid6_rvvx8; + +struct raid6_recov_calls { + const char *name; + void (*data2)(int disks, size_t bytes, int faila, int failb, + void **ptrs); + void (*datap)(int disks, size_t bytes, int faila, void **ptrs); + int (*valid)(void); + int priority; +}; + +extern const struct raid6_recov_calls raid6_recov_intx1; +extern const struct raid6_recov_calls raid6_recov_ssse3; +extern const struct raid6_recov_calls raid6_recov_avx2; +extern const struct raid6_recov_calls raid6_recov_avx512; +extern const struct raid6_recov_calls raid6_recov_s390xc; +extern const struct raid6_recov_calls raid6_recov_neon; +extern const struct raid6_recov_calls raid6_recov_lsx; +extern const struct raid6_recov_calls raid6_recov_lasx; +extern const struct raid6_recov_calls raid6_recov_rvv; + +extern const struct raid6_calls raid6_neonx1; +extern const struct raid6_calls raid6_neonx2; +extern const struct raid6_calls raid6_neonx4; +extern const struct raid6_calls raid6_neonx8; + +/* Algorithm list */ +extern const struct raid6_calls * const raid6_algos[]; +extern const struct raid6_recov_calls *const raid6_recov_algos[]; + +#endif /* _PQ_IMPL_H */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index c21da59ab48f..bd4ec4c86ee8 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -5,8 +5,8 @@ * Copyright (C) 2013 Linaro Ltd */ -#include #include +#include "algos.h" /* * There are 2 reasons these wrappers are kept in a separate compilation unit diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 4eb0efb44750..e1d1d19fc9a8 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -4,8 +4,10 @@ * Copyright (C) 2017 Linaro Ltd. */ +#include #include #include +#include "algos.h" #include "arm/neon.h" static int raid6_has_neon(void) diff --git a/lib/raid/raid6/int.uc b/lib/raid/raid6/int.uc index 4f5f2869e21e..e63bd5a9c2ed 100644 --- a/lib/raid/raid6/int.uc +++ b/lib/raid/raid6/int.uc @@ -18,7 +18,7 @@ * This file is postprocessed using unroll.awk */ -#include +#include "algos.h" /* * This is the C data type to use diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index 1b4cd1512d05..f77d11ce676e 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -9,9 +9,9 @@ * Copyright 2002-2004 H. Peter Anvin */ -#include #include #include +#include "algos.h" /* * The vector algorithms are currently priority 0, which means the generic diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 7d4d349322b3..0bbdc8b5c2e7 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -10,9 +10,11 @@ * Author: Jim Kukunas */ +#include #include #include #include +#include "algos.h" /* * Unlike with the syndrome calculation algorithms, there's no boot-time diff --git a/lib/raid/raid6/mktables.c b/lib/raid/raid6/mktables.c index 3de1dbf6846c..97a17493bbd8 100644 --- a/lib/raid/raid6/mktables.c +++ b/lib/raid/raid6/mktables.c @@ -57,7 +57,7 @@ int main(int argc, char *argv[]) uint8_t exptbl[256], invtbl[256]; printf("#include \n"); - printf("#include \n"); + printf("#include \"algos.h\"\n"); /* Compute multiplication table */ printf("\nconst u8 __attribute__((aligned(256)))\n" diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index 084ead768ddb..eb4a448cc88e 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -22,7 +22,7 @@ * bracked this with preempt_disable/enable or in a lock) */ -#include +#include "algos.h" #include #include diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index bb2c3a316ae8..ec61f30bec11 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -20,11 +20,11 @@ * This instruction was introduced in POWER8 - ISA v2.07. */ -#include #include #include #include #include +#include "algos.h" typedef vector unsigned char unative_t; #define NSIZE sizeof(unative_t) diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index cc7e4dc1eaa6..735ab4013771 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -13,7 +13,9 @@ * the syndrome.) */ +#include #include +#include "algos.h" /* Recover two failed data blocks. */ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 3ff39826e33f..02120d245e22 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -4,7 +4,9 @@ * Author: Chunyan Zhang */ +#include #include +#include "algos.h" #include "rvv.h" static void __raid6_2data_recov_rvv(int bytes, u8 *p, u8 *q, u8 *dp, diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index 0d430a4c5f08..c293130d798b 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -7,8 +7,8 @@ * Definitions for RISC-V RAID-6 code */ -#include #include +#include "algos.h" static int rvv_has_vector(void) { diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 2bc4c85174de..e7b3409f21e2 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -6,7 +6,9 @@ * Author(s): Martin Schwidefsky */ +#include #include +#include "algos.h" static inline void xor_block(u8 *p1, u8 *p2) { diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index 97c5d5d9dcf9..aba3515eacac 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -12,8 +12,8 @@ */ #include -#include #include +#include "algos.h" #define NSIZE 16 diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 9db287b4a48f..de6a866953c5 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -7,7 +7,7 @@ #include #include -#include +#include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index aab8b624c635..0bf831799082 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -13,8 +13,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_avx2_constants { u64 x1d[4]; diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 47636b16632f..98ed42fb0a46 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -17,8 +17,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_avx512_constants { u64 x1d[8]; diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 22b9fdaa705f..052d9f010bfe 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -11,8 +11,9 @@ * MMX implementation of RAID-6 syndrome functions */ -#include +#include #include +#include "algos.h" /* Shared with raid6/sse1.c */ const struct raid6_mmx_constants { diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index bef82a38d8eb..06c6e05763bc 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -4,8 +4,10 @@ * Author: Jim Kukunas */ +#include #include #include +#include "algos.h" static int raid6_has_avx2(void) { diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 06c70e771eaa..850bb962b514 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -6,8 +6,10 @@ * Author: Megha Dey */ +#include #include #include +#include "algos.h" static int raid6_has_avx512(void) { diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 5ca7d56f23d8..95589c33003a 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -3,8 +3,10 @@ * Copyright (C) 2012 Intel Corporation */ +#include #include #include +#include "algos.h" static int raid6_has_ssse3(void) { diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index fad214a430d8..7004255a0bb1 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -16,8 +16,9 @@ * worthwhile as a separate implementation. */ -#include +#include #include +#include "algos.h" /* Defined in raid6/mmx.c */ extern const struct raid6_mmx_constants { diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index 1b28e858a1d4..f30be4ee14d0 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -12,8 +12,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_sse_constants { u64 x1d[2]; From adfcf6e89bc1322c4a6cc37ad32e411cf0500622 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:53 +0200 Subject: [PATCH 2134/5214] raid6: rework registration of optimized algorithms Replace the static array of algorithms with a call to an architecture helper to register algorithms. This serves two purposes: it avoid having to register all algorithms in a single central place, and it removes the need for the priority field by just registering the algorithms that the architecture considers suitable for the currently running CPUs. [hch@lst.de: register avx512 after avx2] Link: https://lore.kernel.org/20260527074539.2292913-3-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-11-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/Kconfig | 11 + lib/raid/raid6/Makefile | 4 + lib/raid/raid6/algos.c | 305 ++++++++---------- lib/raid/raid6/algos.h | 69 +--- lib/raid/raid6/arm/neon.c | 6 - lib/raid/raid6/arm/pq_arch.h | 21 ++ lib/raid/raid6/arm/recov_neon.c | 7 - lib/raid/raid6/arm64/pq_arch.h | 1 + lib/raid/raid6/loongarch/loongarch_simd.c | 12 - lib/raid/raid6/loongarch/pq_arch.h | 23 ++ .../raid6/loongarch/recov_loongarch_simd.c | 14 - lib/raid/raid6/powerpc/altivec.uc | 10 - lib/raid/raid6/powerpc/pq_arch.h | 32 ++ lib/raid/raid6/powerpc/vpermxor.uc | 11 - lib/raid/raid6/recov.c | 2 - lib/raid/raid6/riscv/pq_arch.h | 21 ++ lib/raid/raid6/riscv/recov_rvv.c | 2 - lib/raid/raid6/riscv/rvv.h | 6 - lib/raid/raid6/s390/pq_arch.h | 15 + lib/raid/raid6/s390/recov_s390xc.c | 2 - lib/raid/raid6/s390/s390vx.uc | 7 - lib/raid/raid6/tests/raid6_kunit.c | 23 +- lib/raid/raid6/x86/avx2.c | 14 - lib/raid/raid6/x86/avx512.c | 19 -- lib/raid/raid6/x86/mmx.c | 8 - lib/raid/raid6/x86/pq_arch.h | 96 ++++++ lib/raid/raid6/x86/recov_avx2.c | 8 - lib/raid/raid6/x86/recov_avx512.c | 12 - lib/raid/raid6/x86/recov_ssse3.c | 9 - lib/raid/raid6/x86/sse1.c | 12 - lib/raid/raid6/x86/sse2.c | 15 - 31 files changed, 386 insertions(+), 411 deletions(-) create mode 100644 lib/raid/raid6/arm/pq_arch.h create mode 100644 lib/raid/raid6/arm64/pq_arch.h create mode 100644 lib/raid/raid6/loongarch/pq_arch.h create mode 100644 lib/raid/raid6/powerpc/pq_arch.h create mode 100644 lib/raid/raid6/riscv/pq_arch.h create mode 100644 lib/raid/raid6/s390/pq_arch.h create mode 100644 lib/raid/raid6/x86/pq_arch.h diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index e39f6d667792..978cd6ba08ac 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -32,6 +32,17 @@ config XOR_KUNIT_TEST config RAID6_PQ tristate +# selected by architectures that provide an optimized PQ implementation +config RAID6_PQ_ARCH + depends on RAID6_PQ + default y if KERNEL_MODE_NEON # arm32/arm64 + default y if LOONGARCH + default y if ALTIVEC # powerpc + default y if RISCV_ISA_V + default y if S390 + default y if X86 + bool + config RAID6_PQ_KUNIT_TEST tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile index 7cb31b8a5c17..038d6c74d1ba 100644 --- a/lib/raid/raid6/Makefile +++ b/lib/raid/raid6/Makefile @@ -2,6 +2,10 @@ ccflags-y += -I $(src) +ifeq ($(CONFIG_RAID6_PQ_ARCH),y) +CFLAGS_algos.o += -I$(src)/$(SRCARCH) +endif + obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/ raid6_pq-y += algos.o tables.o diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index d83ca4dac864..30d7cb34874f 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -17,6 +17,9 @@ #include #include "algos.h" +#define RAID6_MAX_ALGOS 16 +static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS]; +static unsigned int raid6_nr_algos; static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ @@ -97,71 +100,6 @@ bool raid6_can_xor_syndrome(void) } EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); -const struct raid6_calls * const raid6_algos[] = { -#if defined(__i386__) && !defined(__arch_um__) - &raid6_avx512x2, - &raid6_avx512x1, - &raid6_avx2x2, - &raid6_avx2x1, - &raid6_sse2x2, - &raid6_sse2x1, - &raid6_sse1x2, - &raid6_sse1x1, - &raid6_mmxx2, - &raid6_mmxx1, -#endif -#if defined(__x86_64__) && !defined(__arch_um__) - &raid6_avx512x4, - &raid6_avx512x2, - &raid6_avx512x1, - &raid6_avx2x4, - &raid6_avx2x2, - &raid6_avx2x1, - &raid6_sse2x4, - &raid6_sse2x2, - &raid6_sse2x1, -#endif -#ifdef CONFIG_ALTIVEC - &raid6_vpermxor8, - &raid6_vpermxor4, - &raid6_vpermxor2, - &raid6_vpermxor1, - &raid6_altivec8, - &raid6_altivec4, - &raid6_altivec2, - &raid6_altivec1, -#endif -#if defined(CONFIG_S390) - &raid6_s390vx8, -#endif -#ifdef CONFIG_KERNEL_MODE_NEON - &raid6_neonx8, - &raid6_neonx4, - &raid6_neonx2, - &raid6_neonx1, -#endif -#ifdef CONFIG_LOONGARCH -#ifdef CONFIG_CPU_HAS_LASX - &raid6_lasx, -#endif -#ifdef CONFIG_CPU_HAS_LSX - &raid6_lsx, -#endif -#endif -#ifdef CONFIG_RISCV_ISA_V - &raid6_rvvx1, - &raid6_rvvx2, - &raid6_rvvx4, - &raid6_rvvx8, -#endif - &raid6_intx8, - &raid6_intx4, - &raid6_intx2, - &raid6_intx1, - NULL -}; -EXPORT_SYMBOL_IF_KUNIT(raid6_algos); - /** * raid6_recov_2data - recover two missing data disks * @disks: number of "disks" to operate on including parity @@ -215,119 +153,57 @@ void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) } EXPORT_SYMBOL_GPL(raid6_recov_datap); -const struct raid6_recov_calls *const raid6_recov_algos[] = { -#ifdef CONFIG_X86 - &raid6_recov_avx512, - &raid6_recov_avx2, - &raid6_recov_ssse3, -#endif -#ifdef CONFIG_S390 - &raid6_recov_s390xc, -#endif -#if defined(CONFIG_KERNEL_MODE_NEON) - &raid6_recov_neon, -#endif -#ifdef CONFIG_LOONGARCH -#ifdef CONFIG_CPU_HAS_LASX - &raid6_recov_lasx, -#endif -#ifdef CONFIG_CPU_HAS_LSX - &raid6_recov_lsx, -#endif -#endif -#ifdef CONFIG_RISCV_ISA_V - &raid6_recov_rvv, -#endif - &raid6_recov_intx1, - NULL -}; -EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); - #define RAID6_TIME_JIFFIES_LG2 4 #define RAID6_TEST_DISKS 8 #define RAID6_TEST_DISKS_ORDER 3 -static inline const struct raid6_recov_calls *raid6_choose_recov(void) +static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], + const int disks) { - const struct raid6_recov_calls *const *algo; - const struct raid6_recov_calls *best; + /* work on the second half of the disks */ + int start = (disks >> 1) - 1, stop = disks - 3; + const struct raid6_calls *best = NULL; + unsigned long bestgenperf = 0; + unsigned int i; - for (best = NULL, algo = raid6_recov_algos; *algo; algo++) - if (!best || (*algo)->priority > best->priority) - if (!(*algo)->valid || (*algo)->valid()) - best = *algo; + for (i = 0; i < raid6_nr_algos; i++) { + const struct raid6_calls *algo = raid6_algos[i]; + unsigned long perf = 0, j0, j1; - if (best) { - raid6_recov_algo = best; - - pr_info("raid6: using %s recovery algorithm\n", best->name); - } else - pr_err("raid6: Yikes! No recovery algorithm found!\n"); - - return best; -} - -static inline const struct raid6_calls *raid6_choose_gen( - void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) -{ - unsigned long perf, bestgenperf, j0, j1; - int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */ - const struct raid6_calls *const *algo; - const struct raid6_calls *best; - - for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { - if (!best || (*algo)->priority >= best->priority) { - if ((*algo)->valid && !(*algo)->valid()) - continue; - - if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { - best = *algo; - break; - } - - perf = 0; - - preempt_disable(); - j0 = jiffies; - while ((j1 = jiffies) == j0) - cpu_relax(); - while (time_before(jiffies, - j1 + (1<gen_syndrome(disks, PAGE_SIZE, *dptrs); - perf++; - } - preempt_enable(); - - if (perf > bestgenperf) { - bestgenperf = perf; - best = *algo; - } - pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name, - (perf * HZ * (disks-2)) >> - (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); + preempt_disable(); + j0 = jiffies; + while ((j1 = jiffies) == j0) + cpu_relax(); + while (time_before(jiffies, + j1 + (1<gen_syndrome(disks, PAGE_SIZE, *dptrs); + perf++; } + preempt_enable(); + + if (perf > bestgenperf) { + bestgenperf = perf; + best = algo; + } + pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name, + (perf * HZ * (disks-2)) >> + (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); } if (!best) { pr_err("raid6: Yikes! No algorithm found!\n"); - goto out; + return -EINVAL; } raid6_call = *best; - if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { - pr_info("raid6: skipped pq benchmark and selected %s\n", - best->name); - goto out; - } - pr_info("raid6: using algorithm %s gen() %ld MB/s\n", best->name, (bestgenperf * HZ * (disks - 2)) >> (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); if (best->xor_syndrome) { - perf = 0; + unsigned long perf = 0, j0, j1; preempt_disable(); j0 = jiffies; @@ -346,8 +222,7 @@ static inline const struct raid6_calls *raid6_choose_gen( (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); } -out: - return best; + return 0; } @@ -357,12 +232,17 @@ static inline const struct raid6_calls *raid6_choose_gen( static int __init raid6_select_algo(void) { const int disks = RAID6_TEST_DISKS; - - const struct raid6_calls *gen_best; - const struct raid6_recov_calls *rec_best; char *disk_ptr, *p; void *dptrs[RAID6_TEST_DISKS]; int i, cycle; + int error; + + if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { + pr_info("raid6: skipped pq benchmark and selected %s\n", + raid6_algos[raid6_nr_algos - 1]->name); + raid6_call = *raid6_algos[raid6_nr_algos - 1]; + return 0; + } /* prepare the buffer and fill it circularly with gfmul table */ disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); @@ -385,22 +265,109 @@ static int __init raid6_select_algo(void) memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536); /* select raid gen_syndrome function */ - gen_best = raid6_choose_gen(&dptrs, disks); - - /* select raid recover functions */ - rec_best = raid6_choose_recov(); + error = raid6_choose_gen(&dptrs, disks); free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); - return gen_best && rec_best ? 0 : -EINVAL; + return error; } -static void raid6_exit(void) +/* + * Register a RAID6 P/Q generation algorithm. The most optimized/unrolled + * implementation should be registered last so it will be selected when the + * boot-time benchmark is disabled. + */ +void __init raid6_algo_add(const struct raid6_calls *algo) { - do { } while (0); + if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS)) + return; + raid6_algos[raid6_nr_algos++] = algo; } -subsys_initcall(raid6_select_algo); +void __init raid6_algo_add_default(void) +{ + raid6_algo_add(&raid6_intx1); + raid6_algo_add(&raid6_intx2); + raid6_algo_add(&raid6_intx4); + raid6_algo_add(&raid6_intx8); +} + +void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo) +{ + if (WARN_ON_ONCE(raid6_recov_algo)) + return; + raid6_recov_algo = algo; +} + +#ifdef CONFIG_RAID6_PQ_ARCH +#include "pq_arch.h" +#else +static inline void arch_raid6_init(void) +{ + raid6_algo_add_default(); +} +#endif /* CONFIG_RAID6_PQ_ARCH */ + +static int __init raid6_init(void) +{ + /* + * Architectures providing arch_raid6_init must add all PQ generation + * algorithms they want to consider in arch_raid6_init(), including + * the generic ones using raid6_algo_add_default() if wanted. + */ + arch_raid6_init(); + + /* + * Architectures don't have to set a recovery algorithm, we'll just pick + * the generic integer one if none was set. + */ + if (!raid6_recov_algo) + raid6_recov_algo = &raid6_recov_intx1; + pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); + + return raid6_select_algo(); +} + +static void __exit raid6_exit(void) +{ +} + +subsys_initcall(raid6_init); module_exit(raid6_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RAID6 Q-syndrome calculations"); + +#if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST) +const struct raid6_calls *raid6_algo_find(unsigned int idx) +{ + if (idx >= raid6_nr_algos) { + /* + * Always include the simplest generic integer implementation in + * the unit tests as a baseline. + */ + if (idx == raid6_nr_algos && + raid6_algos[0] != &raid6_intx1) + return &raid6_intx1; + return NULL; + } + return raid6_algos[idx]; +} +EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find); + +const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx) +{ + switch (idx) { + case 0: + /* always test the generic integer implementation */ + return &raid6_recov_intx1; + case 1: + /* test the optimized implementation if there is one */ + if (raid6_recov_algo != &raid6_recov_intx1) + return raid6_recov_algo; + return NULL; + default: + return NULL; + } +} +EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find); +#endif /* CONFIG_RAID6_PQ_KUNIT_TEST */ diff --git a/lib/raid/raid6/algos.h b/lib/raid/raid6/algos.h index e5f1098d2179..43f636be183f 100644 --- a/lib/raid/raid6/algos.h +++ b/lib/raid/raid6/algos.h @@ -5,6 +5,7 @@ #ifndef _PQ_IMPL_H #define _PQ_IMPL_H +#include #include /* Routine choices */ @@ -13,70 +14,28 @@ struct raid6_calls { void (*gen_syndrome)(int disks, size_t bytes, void **ptrs); void (*xor_syndrome)(int disks, int start, int stop, size_t bytes, void **ptrs); - int (*valid)(void); /* Returns 1 if this routine set is usable */ - int priority; /* Relative priority ranking if non-zero */ }; -/* Various routine sets */ -extern const struct raid6_calls raid6_intx1; -extern const struct raid6_calls raid6_intx2; -extern const struct raid6_calls raid6_intx4; -extern const struct raid6_calls raid6_intx8; -extern const struct raid6_calls raid6_mmxx1; -extern const struct raid6_calls raid6_mmxx2; -extern const struct raid6_calls raid6_sse1x1; -extern const struct raid6_calls raid6_sse1x2; -extern const struct raid6_calls raid6_sse2x1; -extern const struct raid6_calls raid6_sse2x2; -extern const struct raid6_calls raid6_sse2x4; -extern const struct raid6_calls raid6_altivec1; -extern const struct raid6_calls raid6_altivec2; -extern const struct raid6_calls raid6_altivec4; -extern const struct raid6_calls raid6_altivec8; -extern const struct raid6_calls raid6_avx2x1; -extern const struct raid6_calls raid6_avx2x2; -extern const struct raid6_calls raid6_avx2x4; -extern const struct raid6_calls raid6_avx512x1; -extern const struct raid6_calls raid6_avx512x2; -extern const struct raid6_calls raid6_avx512x4; -extern const struct raid6_calls raid6_s390vx8; -extern const struct raid6_calls raid6_vpermxor1; -extern const struct raid6_calls raid6_vpermxor2; -extern const struct raid6_calls raid6_vpermxor4; -extern const struct raid6_calls raid6_vpermxor8; -extern const struct raid6_calls raid6_lsx; -extern const struct raid6_calls raid6_lasx; -extern const struct raid6_calls raid6_rvvx1; -extern const struct raid6_calls raid6_rvvx2; -extern const struct raid6_calls raid6_rvvx4; -extern const struct raid6_calls raid6_rvvx8; - struct raid6_recov_calls { const char *name; void (*data2)(int disks, size_t bytes, int faila, int failb, void **ptrs); void (*datap)(int disks, size_t bytes, int faila, void **ptrs); - int (*valid)(void); - int priority; }; +void __init raid6_algo_add(const struct raid6_calls *algo); +void __init raid6_algo_add_default(void); +void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo); + +/* for the kunit test */ +const struct raid6_calls *raid6_algo_find(unsigned int idx); +const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx); + +/* generic implementations */ +extern const struct raid6_calls raid6_intx1; +extern const struct raid6_calls raid6_intx2; +extern const struct raid6_calls raid6_intx4; +extern const struct raid6_calls raid6_intx8; extern const struct raid6_recov_calls raid6_recov_intx1; -extern const struct raid6_recov_calls raid6_recov_ssse3; -extern const struct raid6_recov_calls raid6_recov_avx2; -extern const struct raid6_recov_calls raid6_recov_avx512; -extern const struct raid6_recov_calls raid6_recov_s390xc; -extern const struct raid6_recov_calls raid6_recov_neon; -extern const struct raid6_recov_calls raid6_recov_lsx; -extern const struct raid6_recov_calls raid6_recov_lasx; -extern const struct raid6_recov_calls raid6_recov_rvv; - -extern const struct raid6_calls raid6_neonx1; -extern const struct raid6_calls raid6_neonx2; -extern const struct raid6_calls raid6_neonx4; -extern const struct raid6_calls raid6_neonx8; - -/* Algorithm list */ -extern const struct raid6_calls * const raid6_algos[]; -extern const struct raid6_recov_calls *const raid6_recov_algos[]; #endif /* _PQ_IMPL_H */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index bd4ec4c86ee8..341c61af675e 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -42,15 +42,9 @@ struct raid6_calls const raid6_neonx ## _n = { \ .gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \ .xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \ - .valid = raid6_have_neon, \ .name = "neonx" #_n, \ } -static int raid6_have_neon(void) -{ - return cpu_has_neon(); -} - RAID6_NEON_WRAPPER(1); RAID6_NEON_WRAPPER(2); RAID6_NEON_WRAPPER(4); diff --git a/lib/raid/raid6/arm/pq_arch.h b/lib/raid/raid6/arm/pq_arch.h new file mode 100644 index 000000000000..3f876ea6749c --- /dev/null +++ b/lib/raid/raid6/arm/pq_arch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_neonx1; +extern const struct raid6_calls raid6_neonx2; +extern const struct raid6_calls raid6_neonx4; +extern const struct raid6_calls raid6_neonx8; +extern const struct raid6_recov_calls raid6_recov_neon; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (cpu_has_neon()) { + raid6_algo_add(&raid6_neonx1); + raid6_algo_add(&raid6_neonx2); + raid6_algo_add(&raid6_neonx4); + raid6_algo_add(&raid6_neonx8); + raid6_recov_algo_add(&raid6_recov_neon); + } +} diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index e1d1d19fc9a8..1524050d09b7 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -10,11 +10,6 @@ #include "algos.h" #include "arm/neon.h" -static int raid6_has_neon(void) -{ - return cpu_has_neon(); -} - static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -87,7 +82,5 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_neon = { .data2 = raid6_2data_recov_neon, .datap = raid6_datap_recov_neon, - .valid = raid6_has_neon, .name = "neon", - .priority = 10, }; diff --git a/lib/raid/raid6/arm64/pq_arch.h b/lib/raid/raid6/arm64/pq_arch.h new file mode 100644 index 000000000000..27ff564d7594 --- /dev/null +++ b/lib/raid/raid6/arm64/pq_arch.h @@ -0,0 +1 @@ +#include "arm/pq_arch.h" diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index f77d11ce676e..c1eb53fafd27 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -26,11 +26,6 @@ #ifdef CONFIG_CPU_HAS_LSX #define NSIZE 16 -static int raid6_has_lsx(void) -{ - return cpu_has_lsx; -} - static void raid6_lsx_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -246,7 +241,6 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_lsx = { .gen_syndrome = raid6_lsx_gen_syndrome, .xor_syndrome = raid6_lsx_xor_syndrome, - .valid = raid6_has_lsx, .name = "lsx", }; @@ -256,11 +250,6 @@ const struct raid6_calls raid6_lsx = { #ifdef CONFIG_CPU_HAS_LASX #define NSIZE 32 -static int raid6_has_lasx(void) -{ - return cpu_has_lasx; -} - static void raid6_lasx_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -414,7 +403,6 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_lasx = { .gen_syndrome = raid6_lasx_gen_syndrome, .xor_syndrome = raid6_lasx_xor_syndrome, - .valid = raid6_has_lasx, .name = "lasx", }; #undef NSIZE diff --git a/lib/raid/raid6/loongarch/pq_arch.h b/lib/raid/raid6/loongarch/pq_arch.h new file mode 100644 index 000000000000..ae443a4d7b69 --- /dev/null +++ b/lib/raid/raid6/loongarch/pq_arch.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_lsx; +extern const struct raid6_calls raid6_lasx; + +extern const struct raid6_recov_calls raid6_recov_lsx; +extern const struct raid6_recov_calls raid6_recov_lasx; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx) + raid6_algo_add(&raid6_lsx); + if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx) + raid6_algo_add(&raid6_lasx); + + if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx) + raid6_recov_algo_add(&raid6_recov_lasx); + else if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx) + raid6_recov_algo_add(&raid6_recov_lsx); +} diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 0bbdc8b5c2e7..87a2313bbb4f 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -24,11 +24,6 @@ */ #ifdef CONFIG_CPU_HAS_LSX -static int raid6_has_lsx(void) -{ - return cpu_has_lsx; -} - static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -291,18 +286,11 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_lsx = { .data2 = raid6_2data_recov_lsx, .datap = raid6_datap_recov_lsx, - .valid = raid6_has_lsx, .name = "lsx", - .priority = 1, }; #endif /* CONFIG_CPU_HAS_LSX */ #ifdef CONFIG_CPU_HAS_LASX -static int raid6_has_lasx(void) -{ - return cpu_has_lasx; -} - static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -509,8 +497,6 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_lasx = { .data2 = raid6_2data_recov_lasx, .datap = raid6_datap_recov_lasx, - .valid = raid6_has_lasx, .name = "lasx", - .priority = 2, }; #endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index eb4a448cc88e..c5429fb71dd6 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -104,17 +104,7 @@ static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs) preempt_enable(); } -int raid6_have_altivec(void); -#if $# == 1 -int raid6_have_altivec(void) -{ - /* This assumes either all CPUs have Altivec or none does */ - return cpu_has_feature(CPU_FTR_ALTIVEC); -} -#endif - const struct raid6_calls raid6_altivec$# = { .gen_syndrome = raid6_altivec$#_gen_syndrome, - .valid = raid6_have_altivec, .name = "altivecx$#", }; diff --git a/lib/raid/raid6/powerpc/pq_arch.h b/lib/raid/raid6/powerpc/pq_arch.h new file mode 100644 index 000000000000..ea1878777ff2 --- /dev/null +++ b/lib/raid/raid6/powerpc/pq_arch.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_altivec1; +extern const struct raid6_calls raid6_altivec2; +extern const struct raid6_calls raid6_altivec4; +extern const struct raid6_calls raid6_altivec8; +extern const struct raid6_calls raid6_vpermxor1; +extern const struct raid6_calls raid6_vpermxor2; +extern const struct raid6_calls raid6_vpermxor4; +extern const struct raid6_calls raid6_vpermxor8; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + + /* This assumes either all CPUs have Altivec or none does */ + if (cpu_has_feature(CPU_FTR_ALTIVEC)) { + raid6_algo_add(&raid6_altivec1); + raid6_algo_add(&raid6_altivec2); + raid6_algo_add(&raid6_altivec4); + raid6_algo_add(&raid6_altivec8); + } + if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && + cpu_has_feature(CPU_FTR_ARCH_207S)) { + raid6_algo_add(&raid6_vpermxor1); + raid6_algo_add(&raid6_vpermxor2); + raid6_algo_add(&raid6_vpermxor4); + raid6_algo_add(&raid6_vpermxor8); + } +} diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index ec61f30bec11..e8964361aaef 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -76,18 +76,7 @@ static void raid6_vpermxor$#_gen_syndrome(int disks, size_t bytes, void **ptrs) preempt_enable(); } -int raid6_have_altivec_vpermxor(void); -#if $# == 1 -int raid6_have_altivec_vpermxor(void) -{ - /* Check if arch has both altivec and the vpermxor instructions */ - return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && - cpu_has_feature(CPU_FTR_ARCH_207S)); -} -#endif - const struct raid6_calls raid6_vpermxor$# = { .gen_syndrome = raid6_vpermxor$#_gen_syndrome, - .valid = raid6_have_altivec_vpermxor, .name = "vpermxor$#", }; diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 735ab4013771..76eb2aef3667 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -97,7 +97,5 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_intx1 = { .data2 = raid6_2data_recov_intx1, .datap = raid6_datap_recov_intx1, - .valid = NULL, .name = "intx1", - .priority = 0, }; diff --git a/lib/raid/raid6/riscv/pq_arch.h b/lib/raid/raid6/riscv/pq_arch.h new file mode 100644 index 000000000000..82f1a188f8c4 --- /dev/null +++ b/lib/raid/raid6/riscv/pq_arch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_rvvx1; +extern const struct raid6_calls raid6_rvvx2; +extern const struct raid6_calls raid6_rvvx4; +extern const struct raid6_calls raid6_rvvx8; +extern const struct raid6_recov_calls raid6_recov_rvv; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (has_vector()) { + raid6_algo_add(&raid6_rvvx1); + raid6_algo_add(&raid6_rvvx2); + raid6_algo_add(&raid6_rvvx4); + raid6_algo_add(&raid6_rvvx8); + raid6_recov_algo_add(&raid6_recov_rvv); + } +} diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 02120d245e22..2305940276dd 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -218,7 +218,5 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_rvv = { .data2 = raid6_2data_recov_rvv, .datap = raid6_datap_recov_rvv, - .valid = rvv_has_vector, .name = "rvv", - .priority = 1, }; diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index c293130d798b..3a7c2468b1ea 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -10,11 +10,6 @@ #include #include "algos.h" -static int rvv_has_vector(void) -{ - return has_vector(); -} - #define RAID6_RVV_WRAPPER(_n) \ static void raid6_rvv ## _n ## _gen_syndrome(int disks, \ size_t bytes, void **ptrs) \ @@ -41,6 +36,5 @@ static int rvv_has_vector(void) struct raid6_calls const raid6_rvvx ## _n = { \ .gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \ .xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \ - .valid = rvv_has_vector, \ .name = "rvvx" #_n, \ } diff --git a/lib/raid/raid6/s390/pq_arch.h b/lib/raid/raid6/s390/pq_arch.h new file mode 100644 index 000000000000..95d14c342306 --- /dev/null +++ b/lib/raid/raid6/s390/pq_arch.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_s390vx8; +extern const struct raid6_recov_calls raid6_recov_s390xc; + +static __always_inline void __init arch_raid6_init(void) +{ + if (cpu_has_vx()) + raid6_algo_add(&raid6_s390vx8); + else + raid6_algo_add_default(); + raid6_recov_algo_add(&raid6_recov_s390xc); +} diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index e7b3409f21e2..08d56896e5ea 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -112,7 +112,5 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_s390xc = { .data2 = raid6_2data_recov_s390xc, .datap = raid6_datap_recov_s390xc, - .valid = NULL, .name = "s390xc", - .priority = 1, }; diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index aba3515eacac..e5cf9054be2a 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -121,15 +121,8 @@ static void raid6_s390vx$#_xor_syndrome(int disks, int start, int stop, kernel_fpu_end(&vxstate, KERNEL_VXR); } -static int raid6_s390vx$#_valid(void) -{ - return cpu_has_vx(); -} - const struct raid6_calls raid6_s390vx$# = { .gen_syndrome = raid6_s390vx$#_gen_syndrome, .xor_syndrome = raid6_s390vx$#_xor_syndrome, - .valid = raid6_s390vx$#_valid, .name = "vx128x$#", - .priority = 1, }; diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index de6a866953c5..4dea1c5acc96 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -88,19 +88,20 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, static void raid6_test(struct kunit *test) { - const struct raid6_calls *const *algo; - const struct raid6_recov_calls *const *ra; int i, j, p1, p2; + unsigned int r, g; - for (ra = raid6_recov_algos; *ra; ra++) { - if ((*ra)->valid && !(*ra)->valid()) - continue; + for (r = 0; ; r++) { + const struct raid6_recov_calls *ra = raid6_recov_algo_find(r); - for (algo = raid6_algos; *algo; algo++) { - const struct raid6_calls *calls = *algo; + if (!ra) + break; - if (calls->valid && !calls->valid()) - continue; + for (g = 0; ; g++) { + const struct raid6_calls *calls = raid6_algo_find(g); + + if (!calls) + break; /* Nuke syndromes */ memset(data[NDISKS - 2], 0xee, PAGE_SIZE); @@ -112,7 +113,7 @@ static void raid6_test(struct kunit *test) for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, *ra, i, j); + test_disks(test, calls, ra, i, j); if (!calls->xor_syndrome) continue; @@ -130,7 +131,7 @@ static void raid6_test(struct kunit *test) for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) test_disks(test, calls, - *ra, i, j); + ra, i, j); } } diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 0bf831799082..7efd94e6a87a 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -24,11 +24,6 @@ static const struct raid6_avx2_constants { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,}, }; -static int raid6_have_avx2(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX); -} - /* * Plain AVX2 implementation */ @@ -131,10 +126,7 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x1 = { .gen_syndrome = raid6_avx21_gen_syndrome, .xor_syndrome = raid6_avx21_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x1", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; /* @@ -262,10 +254,7 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x2 = { .gen_syndrome = raid6_avx22_gen_syndrome, .xor_syndrome = raid6_avx22_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x2", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; #ifdef CONFIG_X86_64 @@ -466,9 +455,6 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x4 = { .gen_syndrome = raid6_avx24_gen_syndrome, .xor_syndrome = raid6_avx24_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x4", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; #endif /* CONFIG_X86_64 */ diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 98ed42fb0a46..0772e798b742 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -30,16 +30,6 @@ static const struct raid6_avx512_constants { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,}, }; -static int raid6_have_avx512(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX) && - boot_cpu_has(X86_FEATURE_AVX512F) && - boot_cpu_has(X86_FEATURE_AVX512BW) && - boot_cpu_has(X86_FEATURE_AVX512VL) && - boot_cpu_has(X86_FEATURE_AVX512DQ); -} - static void raid6_avx5121_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -159,10 +149,7 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x1 = { .gen_syndrome = raid6_avx5121_gen_syndrome, .xor_syndrome = raid6_avx5121_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x1", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; /* @@ -317,10 +304,7 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x2 = { .gen_syndrome = raid6_avx5122_gen_syndrome, .xor_syndrome = raid6_avx5122_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x2", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; #ifdef CONFIG_X86_64 @@ -556,9 +540,6 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x4 = { .gen_syndrome = raid6_avx5124_gen_syndrome, .xor_syndrome = raid6_avx5124_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x4", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; #endif diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 052d9f010bfe..3228c335965a 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -22,12 +22,6 @@ const struct raid6_mmx_constants { 0x1d1d1d1d1d1d1d1dULL, }; -static int raid6_have_mmx(void) -{ - /* Not really "boot_cpu" but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX); -} - /* * Plain MMX implementation */ @@ -70,7 +64,6 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_mmxx1 = { .gen_syndrome = raid6_mmx1_gen_syndrome, - .valid = raid6_have_mmx, .name = "mmxx1", }; @@ -127,6 +120,5 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_mmxx2 = { .gen_syndrome = raid6_mmx2_gen_syndrome, - .valid = raid6_have_mmx, .name = "mmxx2", }; diff --git a/lib/raid/raid6/x86/pq_arch.h b/lib/raid/raid6/x86/pq_arch.h new file mode 100644 index 000000000000..02f8843b0537 --- /dev/null +++ b/lib/raid/raid6/x86/pq_arch.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_mmxx1; +extern const struct raid6_calls raid6_mmxx2; +extern const struct raid6_calls raid6_sse1x1; +extern const struct raid6_calls raid6_sse1x2; +extern const struct raid6_calls raid6_sse2x1; +extern const struct raid6_calls raid6_sse2x2; +extern const struct raid6_calls raid6_sse2x4; +extern const struct raid6_calls raid6_avx2x1; +extern const struct raid6_calls raid6_avx2x2; +extern const struct raid6_calls raid6_avx2x4; +extern const struct raid6_calls raid6_avx512x1; +extern const struct raid6_calls raid6_avx512x2; +extern const struct raid6_calls raid6_avx512x4; + +extern const struct raid6_recov_calls raid6_recov_ssse3; +extern const struct raid6_recov_calls raid6_recov_avx2; +extern const struct raid6_recov_calls raid6_recov_avx512; + +static inline int raid6_has_avx512(void) +{ + return boot_cpu_has(X86_FEATURE_AVX2) && + boot_cpu_has(X86_FEATURE_AVX) && + boot_cpu_has(X86_FEATURE_AVX512F) && + boot_cpu_has(X86_FEATURE_AVX512BW) && + boot_cpu_has(X86_FEATURE_AVX512VL) && + boot_cpu_has(X86_FEATURE_AVX512DQ); +} + +static inline bool raid6_has_avx2(void) +{ + return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX); +} + +static inline bool raid6_has_ssse3(void) +{ + return boot_cpu_has(X86_FEATURE_XMM) && + boot_cpu_has(X86_FEATURE_XMM2) && + boot_cpu_has(X86_FEATURE_SSSE3); +} + +static inline bool raid6_has_sse2(void) +{ + return boot_cpu_has(X86_FEATURE_MMX) && + boot_cpu_has(X86_FEATURE_FXSR) && + boot_cpu_has(X86_FEATURE_XMM) && + boot_cpu_has(X86_FEATURE_XMM2); +} + +static inline bool raid6_has_sse1_or_mmxext(void) +{ + return boot_cpu_has(X86_FEATURE_MMX) && + (boot_cpu_has(X86_FEATURE_XMM) || + boot_cpu_has(X86_FEATURE_MMXEXT)); +} + +static __always_inline void __init arch_raid6_init(void) +{ + if (raid6_has_avx2()) { + raid6_algo_add(&raid6_avx2x1); + raid6_algo_add(&raid6_avx2x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_avx2x4); + if (raid6_has_avx512()) { + raid6_algo_add(&raid6_avx512x1); + raid6_algo_add(&raid6_avx512x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_avx512x4); + } + } else if (IS_ENABLED(CONFIG_X86_64) || raid6_has_sse2()) { + /* x86_64 can assume SSE2 as baseline */ + raid6_algo_add(&raid6_sse2x1); + raid6_algo_add(&raid6_sse2x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_sse2x4); + } else { + raid6_algo_add_default(); + if (raid6_has_sse1_or_mmxext()) { + raid6_algo_add(&raid6_sse1x1); + raid6_algo_add(&raid6_sse1x2); + } else if (boot_cpu_has(X86_FEATURE_MMX)) { + raid6_algo_add(&raid6_mmxx1); + raid6_algo_add(&raid6_mmxx2); + } + } + + if (raid6_has_avx512()) + raid6_recov_algo_add(&raid6_recov_avx512); + else if (raid6_has_avx2()) + raid6_recov_algo_add(&raid6_recov_avx2); + else if (raid6_has_ssse3()) + raid6_recov_algo_add(&raid6_recov_ssse3); +} diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 06c6e05763bc..a714a780a2d8 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -9,12 +9,6 @@ #include #include "algos.h" -static int raid6_has_avx2(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX); -} - static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -305,11 +299,9 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_avx2 = { .data2 = raid6_2data_recov_avx2, .datap = raid6_datap_recov_avx2, - .valid = raid6_has_avx2, #ifdef CONFIG_X86_64 .name = "avx2x2", #else .name = "avx2x1", #endif - .priority = 2, }; diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 850bb962b514..ec72d5a30c01 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -11,16 +11,6 @@ #include #include "algos.h" -static int raid6_has_avx512(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX) && - boot_cpu_has(X86_FEATURE_AVX512F) && - boot_cpu_has(X86_FEATURE_AVX512BW) && - boot_cpu_has(X86_FEATURE_AVX512VL) && - boot_cpu_has(X86_FEATURE_AVX512DQ); -} - static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -369,11 +359,9 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_avx512 = { .data2 = raid6_2data_recov_avx512, .datap = raid6_datap_recov_avx512, - .valid = raid6_has_avx512, #ifdef CONFIG_X86_64 .name = "avx512x2", #else .name = "avx512x1", #endif - .priority = 3, }; diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 95589c33003a..700bd2c865ec 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -8,13 +8,6 @@ #include #include "algos.h" -static int raid6_has_ssse3(void) -{ - return boot_cpu_has(X86_FEATURE_XMM) && - boot_cpu_has(X86_FEATURE_XMM2) && - boot_cpu_has(X86_FEATURE_SSSE3); -} - static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -320,11 +313,9 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_ssse3 = { .data2 = raid6_2data_recov_ssse3, .datap = raid6_datap_recov_ssse3, - .valid = raid6_has_ssse3, #ifdef CONFIG_X86_64 .name = "ssse3x2", #else .name = "ssse3x1", #endif - .priority = 1, }; diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index 7004255a0bb1..6ebdcf824e00 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -25,14 +25,6 @@ extern const struct raid6_mmx_constants { u64 x1d; } raid6_mmx_constants; -static int raid6_have_sse1_or_mmxext(void) -{ - /* Not really boot_cpu but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX) && - (boot_cpu_has(X86_FEATURE_XMM) || - boot_cpu_has(X86_FEATURE_MMXEXT)); -} - /* * Plain SSE1 implementation */ @@ -86,9 +78,7 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_sse1x1 = { .gen_syndrome = raid6_sse11_gen_syndrome, - .valid = raid6_have_sse1_or_mmxext, .name = "sse1x1", - .priority = 1, /* Has cache hints */ }; /* @@ -148,7 +138,5 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_sse1x2 = { .gen_syndrome = raid6_sse12_gen_syndrome, - .valid = raid6_have_sse1_or_mmxext, .name = "sse1x2", - .priority = 1, /* Has cache hints */ }; diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index f30be4ee14d0..7049c8512f35 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -22,15 +22,6 @@ static const struct raid6_sse_constants { { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL }, }; -static int raid6_have_sse2(void) -{ - /* Not really boot_cpu but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX) && - boot_cpu_has(X86_FEATURE_FXSR) && - boot_cpu_has(X86_FEATURE_XMM) && - boot_cpu_has(X86_FEATURE_XMM2); -} - /* * Plain SSE2 implementation */ @@ -136,9 +127,7 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x1 = { .gen_syndrome = raid6_sse21_gen_syndrome, .xor_syndrome = raid6_sse21_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x1", - .priority = 1, /* Has cache hints */ }; /* @@ -266,9 +255,7 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x2 = { .gen_syndrome = raid6_sse22_gen_syndrome, .xor_syndrome = raid6_sse22_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x2", - .priority = 1, /* Has cache hints */ }; #ifdef CONFIG_X86_64 @@ -473,9 +460,7 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x4 = { .gen_syndrome = raid6_sse24_gen_syndrome, .xor_syndrome = raid6_sse24_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x4", - .priority = 1, /* Has cache hints */ }; #endif /* CONFIG_X86_64 */ From 10f4b8e2a16452a7ead747c91a27ba3fabfe948d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:54 +0200 Subject: [PATCH 2135/5214] raid6: use static_call for gen_syndrom and xor_syndrom Avoid indirect calls for P/Q parity generation. Link: https://lore.kernel.org/20260518051804.462141-12-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 30d7cb34874f..ff99202cad46 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "algos.h" @@ -23,7 +24,8 @@ static unsigned int raid6_nr_algos; static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ -static struct raid6_calls raid6_call; +DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); +DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); /** * raid6_gen_syndrome - generate RAID6 P/Q parity @@ -48,7 +50,7 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) WARN_ON_ONCE(bytes & 511); WARN_ON_ONCE(disks < RAID6_MIN_DISKS); - raid6_call.gen_syndrome(disks, bytes, ptrs); + static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs); } EXPORT_SYMBOL_GPL(raid6_gen_syndrome); @@ -85,7 +87,7 @@ void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, WARN_ON_ONCE(disks < RAID6_MIN_DISKS); WARN_ON_ONCE(stop < start); - raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); + static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs); } EXPORT_SYMBOL_GPL(raid6_xor_syndrome); @@ -96,7 +98,7 @@ EXPORT_SYMBOL_GPL(raid6_xor_syndrome); */ bool raid6_can_xor_syndrome(void) { - return !!raid6_call.xor_syndrome; + return !!static_call_query(raid6_xor_syndrome_impl); } EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); @@ -195,7 +197,8 @@ static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], return -EINVAL; } - raid6_call = *best; + static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome); + static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome); pr_info("raid6: using algorithm %s gen() %ld MB/s\n", best->name, @@ -240,7 +243,10 @@ static int __init raid6_select_algo(void) if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { pr_info("raid6: skipped pq benchmark and selected %s\n", raid6_algos[raid6_nr_algos - 1]->name); - raid6_call = *raid6_algos[raid6_nr_algos - 1]; + static_call_update(raid6_gen_syndrome_impl, + raid6_algos[raid6_nr_algos - 1]->gen_syndrome); + static_call_update(raid6_xor_syndrome_impl, + raid6_algos[raid6_nr_algos - 1]->xor_syndrome); return 0; } From dd83de0341da9979d6e584cd10e44361a183c938 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:55 +0200 Subject: [PATCH 2136/5214] raid6: use static_call for raid6_recov_2data and raid6_recov_datap Avoid expensive indirect calls for the recovery routines as well. Link: https://lore.kernel.org/20260518051804.462141-13-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index ff99202cad46..43e3309b5306 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -26,6 +26,8 @@ static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); +DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2); +DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap); /** * raid6_gen_syndrome - generate RAID6 P/Q parity @@ -126,7 +128,7 @@ void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, WARN_ON_ONCE(bytes > PAGE_SIZE); WARN_ON_ONCE(failb <= faila); - raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs); + static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs); } EXPORT_SYMBOL_GPL(raid6_recov_2data); @@ -151,7 +153,7 @@ void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) WARN_ON_ONCE(bytes & 511); WARN_ON_ONCE(bytes > PAGE_SIZE); - raid6_recov_algo->datap(disks, bytes, faila, ptrs); + static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs); } EXPORT_SYMBOL_GPL(raid6_recov_datap); @@ -329,6 +331,8 @@ static int __init raid6_init(void) */ if (!raid6_recov_algo) raid6_recov_algo = &raid6_recov_intx1; + static_call_update(raid6_recov_2data_impl, raid6_recov_algo->data2); + static_call_update(raid6_recov_datap_impl, raid6_recov_algo->datap); pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); return raid6_select_algo(); From 30bf04bd13a58cd9b877589569aa0abd06f04e52 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:56 +0200 Subject: [PATCH 2137/5214] raid6: update top of file comments Drop the pointless mention of the file name, and use standard formatting for the top of file comments. Link: https://lore.kernel.org/20260518051804.462141-14-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 8 +------- lib/raid/raid6/arm/neon.c | 2 +- lib/raid/raid6/mktables.c | 12 +++--------- lib/raid/raid6/recov.c | 14 ++++---------- lib/raid/raid6/riscv/rvv.h | 2 -- lib/raid/raid6/x86/avx2.c | 15 +++++---------- lib/raid/raid6/x86/avx512.c | 22 ++++++++-------------- lib/raid/raid6/x86/mmx.c | 10 ++-------- lib/raid/raid6/x86/sse1.c | 18 ++++++------------ lib/raid/raid6/x86/sse2.c | 9 +-------- 10 files changed, 31 insertions(+), 81 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 43e3309b5306..a600d5853672 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -1,12 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/algos.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * * Algorithm list and algorithm selection for RAID-6 */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index 341c61af675e..af90869aaffc 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * linux/lib/raid6/neon.c - RAID6 syndrome calculation using ARM NEON intrinsics + * RAID6 syndrome calculation using ARM NEON intrinsics * * Copyright (C) 2013 Linaro Ltd */ diff --git a/lib/raid/raid6/mktables.c b/lib/raid/raid6/mktables.c index 97a17493bbd8..b6327b562fdb 100644 --- a/lib/raid/raid6/mktables.c +++ b/lib/raid/raid6/mktables.c @@ -1,15 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * mktables.c + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * Make RAID-6 tables. This is a host user space program to be run at - * compile time. + * Make RAID-6 tables. This is a host user space program to be run at compile + * time. */ #include diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 76eb2aef3667..3fa53bc3fde4 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -1,16 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/recov.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * RAID-6 data recovery in dual failure mode. In single failure mode, - * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct - * the syndrome.) + * RAID-6 data recovery in dual failure mode. In single failure mode, use the + * RAID-5 algorithm (or, in the case of Q failure, just reconstruct the + * syndrome.) */ #include diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index 3a7c2468b1ea..df0e3637cae8 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -2,8 +2,6 @@ /* * Copyright 2024 Institute of Software, CAS. * - * raid6/rvv.h - * * Definitions for RISC-V RAID-6 code */ diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 7efd94e6a87a..7d829c669ea7 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -1,16 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright (C) 2012 Intel Corporation - * Author: Yuanhan Liu - * - * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * AVX2 implementation of RAID-6 syndrome functions + * Copyright (C) 2012 Intel Corporation + * Author: Yuanhan Liu * + * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved + * + * AVX2 implementation of RAID-6 syndrome functions */ #include diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 0772e798b742..e671eb5bde63 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -1,20 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- -------------------------------------------------------- - * - * Copyright (C) 2016 Intel Corporation - * - * Author: Gayatri Kammela - * Author: Megha Dey - * - * Based on avx2.c: Copyright 2012 Yuanhan Liu All Rights Reserved - * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- - */ - /* - * AVX512 implementation of RAID-6 syndrome functions + * Copyright (C) 2016 Intel Corporation * + * Author: Gayatri Kammela + * Author: Megha Dey + * + * Based on avx2.c: Copyright 2012 Yuanhan Liu All Rights Reserved + * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved + * + * AVX512 implementation of RAID-6 syndrome functions */ #include diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 3228c335965a..afa82536142d 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -1,14 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/mmx.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * MMX implementation of RAID-6 syndrome functions + * MMX implementation of RAID-6 syndrome functions. */ #include diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index 6ebdcf824e00..f4b260df522a 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -1,19 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/sse1.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * SSE-1/MMXEXT implementation of RAID-6 syndrome functions + * SSE-1/MMXEXT implementation of RAID-6 syndrome functions. * - * This is really an MMX implementation, but it requires SSE-1 or - * AMD MMXEXT for prefetch support and a few other features. The - * support for nontemporal memory accesses is enough to make this - * worthwhile as a separate implementation. + * This is really an MMX implementation, but it requires SSE-1 or AMD MMXEXT for + * prefetch support and a few other features. The support for nontemporal + * memory accesses is enough to make this worthwhile as a separate + * implementation. */ #include diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index 7049c8512f35..43b09ce58270 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -1,15 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/sse2.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * * SSE-2 implementation of RAID-6 syndrome functions - * */ #include From 2175395f76c3eb3b19f1ce9be54d9dc9b0e4e61e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:57 +0200 Subject: [PATCH 2138/5214] raid6_kunit: use KUNIT_CASE_PARAM The raid6 test combines various generation and recovery algorithms. Use KUNIT_CASE_PARAM and provide a generator that iterates over the possible combinations instead of looping inside a single test instance. Link: https://lore.kernel.org/20260518051804.462141-15-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 122 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 4dea1c5acc96..9d06ed9b90e4 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -21,6 +21,15 @@ static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +struct test_args { + unsigned int recov_idx; + const struct raid6_recov_calls *recov; + unsigned int gen_idx; + const struct raid6_calls *gen; +}; + +static struct test_args args; + static void makedata(int start, int stop) { int i; @@ -43,9 +52,10 @@ static char member_type(int d) } } -static void test_disks(struct kunit *test, const struct raid6_calls *calls, - const struct raid6_recov_calls *ra, int faila, int failb) +static void test_recover(struct kunit *test, int faila, int failb) { + const struct test_args *ta = test->param_value; + memset(recovi, 0xf0, PAGE_SIZE); memset(recovj, 0xba, PAGE_SIZE); @@ -61,25 +71,23 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, goto skip; /* P+Q failure. Just rebuild the syndrome. */ - calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); } else if (failb == NDISKS - 2) { /* data+P failure. */ - ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs); + ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); } else { /* data+data failure. */ - ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); + ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, - "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n", - calls->name, ra->name, - faila, member_type(faila), - failb, member_type(failb)); + "faila miscompared: %3d[%c] (failb=%3d[%c])\n", + faila, member_type(faila), + failb, member_type(failb)); KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, - "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n", - calls->name, ra->name, - failb, member_type(failb), - faila, member_type(faila)); + "failb miscompared: %3d[%c] (faila=%3d[%c])\n", + failb, member_type(failb), + faila, member_type(faila)); skip: dataptrs[faila] = data[faila]; @@ -88,58 +96,66 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, static void raid6_test(struct kunit *test) { + const struct test_args *ta = test->param_value; int i, j, p1, p2; - unsigned int r, g; - for (r = 0; ; r++) { - const struct raid6_recov_calls *ra = raid6_recov_algo_find(r); + /* Nuke syndromes */ + memset(data[NDISKS - 2], 0xee, PAGE_SIZE); + memset(data[NDISKS - 1], 0xee, PAGE_SIZE); - if (!ra) - break; + /* Generate assumed good syndrome */ + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); - for (g = 0; ; g++) { - const struct raid6_calls *calls = raid6_algo_find(g); + for (i = 0; i < NDISKS - 1; i++) + for (j = i + 1; j < NDISKS; j++) + test_recover(test, i, j); - if (!calls) - break; + if (!ta->gen->xor_syndrome) + return; - /* Nuke syndromes */ - memset(data[NDISKS - 2], 0xee, PAGE_SIZE); - memset(data[NDISKS - 1], 0xee, PAGE_SIZE); - - /* Generate assumed good syndrome */ - calls->gen_syndrome(NDISKS, PAGE_SIZE, - (void **)&dataptrs); - - for (i = 0; i < NDISKS-1; i++) - for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, ra, i, j); - - if (!calls->xor_syndrome) - continue; - - for (p1 = 0; p1 < NDISKS-2; p1++) - for (p2 = p1; p2 < NDISKS-2; p2++) { - - /* Simulate rmw run */ - calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); - makedata(p1, p2); - calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); - - for (i = 0; i < NDISKS-1; i++) - for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, - ra, i, j); - } + for (p1 = 0; p1 < NDISKS - 2; p1++) { + for (p2 = p1; p2 < NDISKS - 2; p2++) { + /* Simulate rmw run */ + ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + (void **)&dataptrs); + makedata(p1, p2); + ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + (void **)&dataptrs); + for (i = 0; i < NDISKS - 1; i++) + for (j = i + 1; j < NDISKS; j++) + test_recover(test, i, j); } } } +static const void *raid6_gen_params(struct kunit *test, const void *prev, + char *desc) +{ + if (!prev) { + memset(&args, 0, sizeof(args)); +next_algo: + args.recov_idx = 0; + args.gen = raid6_algo_find(args.gen_idx); + if (!args.gen) + return NULL; + } + + if (args.recov) + args.recov_idx++; + args.recov = raid6_recov_algo_find(args.recov_idx); + if (!args.recov) { + args.gen_idx++; + goto next_algo; + } + + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", + args.gen->name, args.recov->name); + return &args; +} + static struct kunit_case raid6_test_cases[] = { - KUNIT_CASE(raid6_test), + KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), {}, }; From d67c25712fe3c5d78fea03827771c49debb89c80 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:58 +0200 Subject: [PATCH 2139/5214] raid6_kunit: dynamically allocate data buffers using vmalloc Use vmalloc for the data buffers instead of using static .data allocations. This provides for better out of bounds checking and avoids wasting kernel memory after the test has run. vmalloc is used instead of kmalloc to provide for better out of bounds access checking as in other kunit tests. Link: https://lore.kernel.org/20260518051804.462141-16-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 77 ++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 9d06ed9b90e4..72c78834df7f 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -7,19 +7,20 @@ #include #include +#include #include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define RAID6_KUNIT_SEED 42 +#define RAID6_KUNIT_MAX_FAILURES 2 #define NDISKS 16 /* Including P and Q */ static struct rnd_state rng; static void *dataptrs[NDISKS]; -static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static void *test_buffers[NDISKS]; +static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; struct test_args { unsigned int recov_idx; @@ -35,8 +36,8 @@ static void makedata(int start, int stop) int i; for (i = start; i <= stop; i++) { - prandom_bytes_state(&rng, data[i], PAGE_SIZE); - dataptrs[i] = data[i]; + prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); + dataptrs[i] = test_buffers[i]; } } @@ -55,12 +56,13 @@ static char member_type(int d) static void test_recover(struct kunit *test, int faila, int failb) { const struct test_args *ta = test->param_value; + int i; - memset(recovi, 0xf0, PAGE_SIZE); - memset(recovj, 0xba, PAGE_SIZE); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); - dataptrs[faila] = recovi; - dataptrs[failb] = recovj; + dataptrs[faila] = test_recov_buffers[0]; + dataptrs[failb] = test_recov_buffers[1]; if (failb == NDISKS - 1) { /* @@ -80,18 +82,20 @@ static void test_recover(struct kunit *test, int faila, int failb) ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } - KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, + KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], + PAGE_SIZE, "faila miscompared: %3d[%c] (failb=%3d[%c])\n", faila, member_type(faila), failb, member_type(failb)); - KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, + KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], + PAGE_SIZE, "failb miscompared: %3d[%c] (faila=%3d[%c])\n", failb, member_type(failb), faila, member_type(faila)); skip: - dataptrs[faila] = data[faila]; - dataptrs[failb] = data[failb]; + dataptrs[faila] = test_buffers[faila]; + dataptrs[failb] = test_buffers[failb]; } static void raid6_test(struct kunit *test) @@ -100,8 +104,8 @@ static void raid6_test(struct kunit *test) int i, j, p1, p2; /* Nuke syndromes */ - memset(data[NDISKS - 2], 0xee, PAGE_SIZE); - memset(data[NDISKS - 1], 0xee, PAGE_SIZE); + memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); + memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); @@ -161,15 +165,58 @@ static struct kunit_case raid6_test_cases[] = { static int raid6_suite_init(struct kunit_suite *suite) { + int i; + prandom_seed_state(&rng, RAID6_KUNIT_SEED); + + /* + * Allocate the test buffer using vmalloc() with a page-aligned length + * so that it is immediately followed by a guard page. This allows + * buffer overreads to be detected, even in assembly code. + */ + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { + test_recov_buffers[i] = vmalloc(PAGE_SIZE); + if (!test_recov_buffers[i]) + goto out_free_recov_buffers; + } + for (i = 0; i < NDISKS; i++) { + test_buffers[i] = vmalloc(PAGE_SIZE); + if (!test_buffers[i]) + goto out_free_buffers; + } + makedata(0, NDISKS - 1); + return 0; + +out_free_buffers: + for (i = 0; i < NDISKS; i++) + vfree(test_buffers[i]); + memset(test_buffers, 0, sizeof(test_buffers)); +out_free_recov_buffers: + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + vfree(test_recov_buffers[i]); + memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); + return -ENOMEM; +} + +static void raid6_suite_exit(struct kunit_suite *suite) +{ + int i; + + for (i = 0; i < NDISKS; i++) + vfree(test_buffers[i]); + memset(test_buffers, 0, sizeof(test_buffers)); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + vfree(test_recov_buffers[i]); + memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); } static struct kunit_suite raid6_test_suite = { .name = "raid6", .test_cases = raid6_test_cases, .suite_init = raid6_suite_init, + .suite_exit = raid6_suite_exit, }; kunit_test_suite(raid6_test_suite); From 562bcbfcb99b2eb4fd17d6551d760450e128afe1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:59 +0200 Subject: [PATCH 2140/5214] raid6_kunit: cleanup dataptr handling Move the global dataptr array into test_recover() as all sites that fill data or parity can use test_buffers directly, and this localized the override for the failed slots to the recovery testing routine. Link: https://lore.kernel.org/20260518051804.462141-17-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 72c78834df7f..152d5d1c3a88 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -18,7 +18,6 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define NDISKS 16 /* Including P and Q */ static struct rnd_state rng; -static void *dataptrs[NDISKS]; static void *test_buffers[NDISKS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; @@ -35,10 +34,8 @@ static void makedata(int start, int stop) { int i; - for (i = start; i <= stop; i++) { + for (i = start; i <= stop; i++) prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); - dataptrs[i] = test_buffers[i]; - } } static char member_type(int d) @@ -56,11 +53,13 @@ static char member_type(int d) static void test_recover(struct kunit *test, int faila, int failb) { const struct test_args *ta = test->param_value; + void *dataptrs[NDISKS]; int i; for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); + memcpy(dataptrs, test_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; @@ -70,7 +69,7 @@ static void test_recover(struct kunit *test, int faila, int failb) * is equivalent to a RAID-5 failure (XOR, then recompute Q). */ if (faila != NDISKS - 2) - goto skip; + return; /* P+Q failure. Just rebuild the syndrome. */ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); @@ -92,10 +91,6 @@ static void test_recover(struct kunit *test, int faila, int failb) "failb miscompared: %3d[%c] (faila=%3d[%c])\n", failb, member_type(failb), faila, member_type(faila)); - -skip: - dataptrs[faila] = test_buffers[faila]; - dataptrs[failb] = test_buffers[failb]; } static void raid6_test(struct kunit *test) @@ -108,7 +103,7 @@ static void raid6_test(struct kunit *test) memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers); for (i = 0; i < NDISKS - 1; i++) for (j = i + 1; j < NDISKS; j++) @@ -121,10 +116,10 @@ static void raid6_test(struct kunit *test) for (p2 = p1; p2 < NDISKS - 2; p2++) { /* Simulate rmw run */ ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); + test_buffers); makedata(p1, p2); ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); + test_buffers); for (i = 0; i < NDISKS - 1; i++) for (j = i + 1; j < NDISKS; j++) From fa0c812c0aa58d4375317d804dd54e44b8bcf5e3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:18:00 +0200 Subject: [PATCH 2141/5214] raid6_kunit: randomize parameters and increase limits The current test has double-quadratic behavior in the selection for the updated ("XORed") disks, and in the selection of updated pointers, which makes scaling it to more tests difficult. At the same time it only ever tests with the maximum number of disks, which leaves a coverage hole for smaller ones. Fix this by randomizing the total number, failed disks and regions to update, and increasing the upper number of tests disks. Link: https://lore.kernel.org/20260518051804.462141-18-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 201 ++++++++++++++++++++--------- 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 152d5d1c3a88..71adf8932e93 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -8,18 +8,21 @@ #include #include #include +#include #include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define RAID6_KUNIT_SEED 42 +#define RAID6_KUNIT_NUM_TEST_ITERS 10 +#define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */ #define RAID6_KUNIT_MAX_FAILURES 2 - -#define NDISKS 16 /* Including P and Q */ +#define RAID6_KUNIT_MAX_BYTES PAGE_SIZE static struct rnd_state rng; -static void *test_buffers[NDISKS]; +static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; +static size_t test_buflen; struct test_args { unsigned int recov_idx; @@ -30,102 +33,171 @@ struct test_args { static struct test_args args; +static u32 rand32(void) +{ + return prandom_u32_state(&rng); +} + +/* Generate a random length that is a multiple of 512. */ +static unsigned int random_length(unsigned int max_length) +{ + return round_up((rand32() % max_length) + 1, 512); +} + +static unsigned int random_nr_buffers(void) +{ + return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) + + RAID6_MIN_DISKS; +} + static void makedata(int start, int stop) { int i; for (i = start; i <= stop; i++) - prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); + prandom_bytes_state(&rng, test_buffers[i], test_buflen); } -static char member_type(int d) +static char member_type(unsigned int nr_buffers, int d) { - switch (d) { - case NDISKS-2: + if (d == nr_buffers - 2) return 'P'; - case NDISKS-1: + if (d == nr_buffers - 1) return 'Q'; - default: - return 'D'; - } + return 'D'; } -static void test_recover(struct kunit *test, int faila, int failb) +static void test_recover_one(struct kunit *test, unsigned int nr_buffers, + unsigned int len, int faila, int failb) { const struct test_args *ta = test->param_value; - void *dataptrs[NDISKS]; + void *dataptrs[RAID6_KUNIT_MAX_BUFFERS]; int i; + if (faila > failb) + swap(faila, failb); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) - memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); + memset(test_recov_buffers[i], 0xf0, test_buflen); memcpy(dataptrs, test_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; - if (failb == NDISKS - 1) { + if (failb == nr_buffers - 1) { /* * We don't implement the data+Q failure scenario, since it * is equivalent to a RAID-5 failure (XOR, then recompute Q). */ - if (faila != NDISKS - 2) + if (WARN_ON_ONCE(faila != nr_buffers - 2)) return; /* P+Q failure. Just rebuild the syndrome. */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); - } else if (failb == NDISKS - 2) { + ta->gen->gen_syndrome(nr_buffers, len, dataptrs); + } else if (failb == nr_buffers - 2) { /* data+P failure. */ - ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); + ta->recov->datap(nr_buffers, len, faila, dataptrs); } else { /* data+data failure. */ - ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); + ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); } KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], - PAGE_SIZE, - "faila miscompared: %3d[%c] (failb=%3d[%c])\n", - faila, member_type(faila), - failb, member_type(failb)); + len, + "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", + faila, member_type(nr_buffers, faila), + nr_buffers, len, + failb, member_type(nr_buffers, failb)); KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], - PAGE_SIZE, - "failb miscompared: %3d[%c] (faila=%3d[%c])\n", - failb, member_type(failb), - faila, member_type(faila)); + len, + "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", + failb, member_type(nr_buffers, failb), + nr_buffers, len, + faila, member_type(nr_buffers, faila)); +} + +static void test_recover(struct kunit *test, unsigned int nr_buffers, + unsigned int len) +{ + unsigned int nr_data = nr_buffers - 2; + int iterations, i; + + /* Test P+Q recovery */ + test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1); + + /* Test data+P recovery */ + for (i = 0; i < nr_buffers - 2; i++) + test_recover_one(test, nr_buffers, len, i, nr_data); + + /* Double data failure is impossible with a single data disk */ + if (nr_data == 1) + return; + + /* Test data+data recovery using random sampling */ + iterations = nr_buffers * 2; /* should provide good enough coverage */ + for (i = 0; i < iterations; i++) { + int faila = rand32() % nr_data, failb; + + do { + failb = rand32() % nr_data; + } while (failb == faila); + + test_recover_one(test, nr_buffers, len, faila, failb); + } +} + +/* Simulate rmw run */ +static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, + unsigned int len, int p1, int p2) +{ + const struct test_args *ta = test->param_value; + + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + makedata(p1, p2); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + test_recover(test, nr_buffers, len); +} + +static void test_rmw(struct kunit *test, unsigned int nr_buffers, + unsigned int len) +{ + int iterations = nr_buffers / 2, i; + + for (i = 0; i < iterations; i++) { + int p1 = rand32() % (nr_buffers - 2); + int p2 = rand32() % (nr_buffers - 2); + + if (p2 < p1) + swap(p1, p2); + test_rmw_one(test, nr_buffers, len, p1, p2); + } +} + +static void raid6_test_one(struct kunit *test) +{ + const struct test_args *ta = test->param_value; + unsigned int nr_buffers = random_nr_buffers(); + unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); + + /* Nuke syndromes */ + memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); + memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); + + /* Generate assumed good syndrome */ + ta->gen->gen_syndrome(nr_buffers, len, test_buffers); + + test_recover(test, nr_buffers, len); + + if (ta->gen->xor_syndrome) + test_rmw(test, nr_buffers, len); } static void raid6_test(struct kunit *test) { - const struct test_args *ta = test->param_value; - int i, j, p1, p2; + int i; - /* Nuke syndromes */ - memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); - memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); - - /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers); - - for (i = 0; i < NDISKS - 1; i++) - for (j = i + 1; j < NDISKS; j++) - test_recover(test, i, j); - - if (!ta->gen->xor_syndrome) - return; - - for (p1 = 0; p1 < NDISKS - 2; p1++) { - for (p2 = p1; p2 < NDISKS - 2; p2++) { - /* Simulate rmw run */ - ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - test_buffers); - makedata(p1, p2); - ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - test_buffers); - - for (i = 0; i < NDISKS - 1; i++) - for (j = i + 1; j < NDISKS; j++) - test_recover(test, i, j); - } - } + for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++) + raid6_test_one(test); } static const void *raid6_gen_params(struct kunit *test, const void *prev, @@ -169,23 +241,24 @@ static int raid6_suite_init(struct kunit_suite *suite) * so that it is immediately followed by a guard page. This allows * buffer overreads to be detected, even in assembly code. */ + test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE); for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { - test_recov_buffers[i] = vmalloc(PAGE_SIZE); + test_recov_buffers[i] = vmalloc(test_buflen); if (!test_recov_buffers[i]) goto out_free_recov_buffers; } - for (i = 0; i < NDISKS; i++) { - test_buffers[i] = vmalloc(PAGE_SIZE); + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) { + test_buffers[i] = vmalloc(test_buflen); if (!test_buffers[i]) goto out_free_buffers; } - makedata(0, NDISKS - 1); + makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1); return 0; out_free_buffers: - for (i = 0; i < NDISKS; i++) + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) vfree(test_buffers[i]); memset(test_buffers, 0, sizeof(test_buffers)); out_free_recov_buffers: @@ -199,7 +272,7 @@ static void raid6_suite_exit(struct kunit_suite *suite) { int i; - for (i = 0; i < NDISKS; i++) + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) vfree(test_buffers[i]); memset(test_buffers, 0, sizeof(test_buffers)); for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) From 8cf0a6c4bb9e09a2d280376dba723b218c139e58 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:18:01 +0200 Subject: [PATCH 2142/5214] raid6_kunit: randomize buffer alignment Add code to add random alignment to the buffers to test the case where they are not page aligned, and to move the buffers to the end of the allocation so that they are next to the vmalloc guard page. This does not include the recovery buffers as the recovery requires page alignment. Link: https://lore.kernel.org/20260518051804.462141-19-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 41 +++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 71adf8932e93..9f3e671a1224 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -21,6 +21,7 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); static struct rnd_state rng; static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; +static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; static size_t test_buflen; @@ -50,6 +51,14 @@ static unsigned int random_nr_buffers(void) RAID6_MIN_DISKS; } +/* Generate a random alignment that is a multiple of 64. */ +static unsigned int random_alignment(unsigned int max_alignment) +{ + if (max_alignment == 0) + return 0; + return (rand32() % (max_alignment + 1)) & ~63; +} + static void makedata(int start, int stop) { int i; @@ -80,7 +89,7 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers, for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) memset(test_recov_buffers[i], 0xf0, test_buflen); - memcpy(dataptrs, test_buffers, sizeof(dataptrs)); + memcpy(dataptrs, aligned_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; @@ -102,13 +111,13 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers, ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); } - KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], + KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila], len, "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", faila, member_type(nr_buffers, faila), nr_buffers, len, failb, member_type(nr_buffers, failb)); - KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], + KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb], len, "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", failb, member_type(nr_buffers, failb), @@ -152,9 +161,9 @@ static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, { const struct test_args *ta = test->param_value; - ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); makedata(p1, p2); - ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); test_recover(test, nr_buffers, len); } @@ -178,13 +187,33 @@ static void raid6_test_one(struct kunit *test) const struct test_args *ta = test->param_value; unsigned int nr_buffers = random_nr_buffers(); unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); + unsigned int max_alignment; + int i; /* Nuke syndromes */ memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); + /* + * If we're not using the entire buffer size, inject randomize alignment + * into the buffer. + */ + max_alignment = RAID6_KUNIT_MAX_BYTES - len; + if (rand32() % 2 == 0) { + /* Use random alignments mod 64 */ + for (i = 0; i < nr_buffers; i++) + aligned_buffers[i] = test_buffers[i] + + random_alignment(max_alignment); + } else { + /* Go up to the guard page, to catch buffer overreads */ + unsigned int align = test_buflen - len; + + for (i = 0; i < nr_buffers; i++) + aligned_buffers[i] = test_buffers[i] + align; + } + /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(nr_buffers, len, test_buffers); + ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers); test_recover(test, nr_buffers, len); From f7ea0d13735ef812f7c777d5c0a79ff6a4b369c3 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 15 May 2026 21:34:24 +0300 Subject: [PATCH 2143/5214] include: remove unused cnt32_to_63.h All users have been removed over time as ARM and other architectures switched to generic sched_clock. The last user was microblaze, removed in commit 839396ab88e4 ("microblaze: timer: Use generic sched_clock implementation"). Assisted-by: Claude:claude-opus-4-6 Link: https://lore.kernel.org/20260515183429.1503740-1-costa.shul@redhat.com Signed-off-by: Costa Shulyupin Reviewed-by: Andrew Morton Cc: Nicolas Pitre Cc: Nicolas Pitre Signed-off-by: Andrew Morton --- include/linux/cnt32_to_63.h | 104 ------------------------------------ 1 file changed, 104 deletions(-) delete mode 100644 include/linux/cnt32_to_63.h diff --git a/include/linux/cnt32_to_63.h b/include/linux/cnt32_to_63.h deleted file mode 100644 index 064428479f2d..000000000000 --- a/include/linux/cnt32_to_63.h +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Extend a 32-bit counter to 63 bits - * - * Author: Nicolas Pitre - * Created: December 3, 2006 - * Copyright: MontaVista Software, Inc. - */ - -#ifndef __LINUX_CNT32_TO_63_H__ -#define __LINUX_CNT32_TO_63_H__ - -#include -#include -#include - -/* this is used only to give gcc a clue about good code generation */ -union cnt32_to_63 { - struct { -#if defined(__LITTLE_ENDIAN) - u32 lo, hi; -#elif defined(__BIG_ENDIAN) - u32 hi, lo; -#endif - }; - u64 val; -}; - - -/** - * cnt32_to_63 - Expand a 32-bit counter to a 63-bit counter - * @cnt_lo: The low part of the counter - * - * Many hardware clock counters are only 32 bits wide and therefore have - * a relatively short period making wrap-arounds rather frequent. This - * is a problem when implementing sched_clock() for example, where a 64-bit - * non-wrapping monotonic value is expected to be returned. - * - * To overcome that limitation, let's extend a 32-bit counter to 63 bits - * in a completely lock free fashion. Bits 0 to 31 of the clock are provided - * by the hardware while bits 32 to 62 are stored in memory. The top bit in - * memory is used to synchronize with the hardware clock half-period. When - * the top bit of both counters (hardware and in memory) differ then the - * memory is updated with a new value, incrementing it when the hardware - * counter wraps around. - * - * Because a word store in memory is atomic then the incremented value will - * always be in synch with the top bit indicating to any potential concurrent - * reader if the value in memory is up to date or not with regards to the - * needed increment. And any race in updating the value in memory is harmless - * as the same value would simply be stored more than once. - * - * The restrictions for the algorithm to work properly are: - * - * 1) this code must be called at least once per each half period of the - * 32-bit counter; - * - * 2) this code must not be preempted for a duration longer than the - * 32-bit counter half period minus the longest period between two - * calls to this code; - * - * Those requirements ensure proper update to the state bit in memory. - * This is usually not a problem in practice, but if it is then a kernel - * timer should be scheduled to manage for this code to be executed often - * enough. - * - * And finally: - * - * 3) the cnt_lo argument must be seen as a globally incrementing value, - * meaning that it should be a direct reference to the counter data which - * can be evaluated according to a specific ordering within the macro, - * and not the result of a previous evaluation stored in a variable. - * - * For example, this is wrong: - * - * u32 partial = get_hw_count(); - * u64 full = cnt32_to_63(partial); - * return full; - * - * This is fine: - * - * u64 full = cnt32_to_63(get_hw_count()); - * return full; - * - * Note that the top bit (bit 63) in the returned value should be considered - * as garbage. It is not cleared here because callers are likely to use a - * multiplier on the returned value which can get rid of the top bit - * implicitly by making the multiplier even, therefore saving on a runtime - * clear-bit instruction. Otherwise caller must remember to clear the top - * bit explicitly. - */ -#define cnt32_to_63(cnt_lo) \ -({ \ - static u32 __m_cnt_hi; \ - union cnt32_to_63 __x; \ - __x.hi = __m_cnt_hi; \ - smp_rmb(); \ - __x.lo = (cnt_lo); \ - if (unlikely((s32)(__x.hi ^ __x.lo) < 0)) \ - __m_cnt_hi = __x.hi = (__x.hi ^ 0x80000000) + (__x.hi >> 31); \ - __x.val; \ -}) - -#endif From f5b1910e23f1233c8d4185268b2e659df2bc5dbf Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 18 May 2026 13:23:40 +0900 Subject: [PATCH 2144/5214] ocfs2: kill osb->system_file_mutex lock Commit 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") tried to avoid a refcount leak caused by allowing multiple threads to call igrab(inode). But addition of osb->system_file_mutex made locking dependency complicated and is causing lockdep to warn about possibility of AB-BA deadlock. Since _ocfs2_get_system_file_inode() returns the same inode for the same input arguments, we don't need to serialize _ocfs2_get_system_file_inode(). What we need to make sure is that igrab(inode) is called for only once(). Therefore, replace osb->system_file_mutex with cmpxchg()-based locking. Link: https://lore.kernel.org/fea8d1fd-afb0-4302-a560-c202e2ef7afd@I-love.SAKURA.ne.jp Fixes: 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") Signed-off-by: Tetsuo Handa Reviewed-by: Heming Zhao Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton --- fs/ocfs2/ocfs2.h | 2 -- fs/ocfs2/super.c | 2 -- fs/ocfs2/sysfile.c | 9 +++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 7b50e03dfa66..62cad6522c7a 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -494,8 +494,6 @@ struct ocfs2_super struct rb_root osb_rf_lock_tree; struct ocfs2_refcount_tree *osb_ref_tree_lru; - struct mutex system_file_mutex; - /* * OCFS2 needs to schedule several different types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b875f01c9756..6dd45c2153f8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1997,8 +1997,6 @@ static int ocfs2_initialize_super(struct super_block *sb, spin_lock_init(&osb->osb_xattr_lock); ocfs2_init_steal_slots(osb); - mutex_init(&osb->system_file_mutex); - atomic_set(&osb->alloc_stats.moves, 0); atomic_set(&osb->alloc_stats.local_data, 0); atomic_set(&osb->alloc_stats.bitmap_data, 0); diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index d53a6cc866be..67e492f4b828 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, } else arr = get_local_system_inode(osb, type, slot); - mutex_lock(&osb->system_file_mutex); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ inode = igrab(inode); - mutex_unlock(&osb->system_file_mutex); BUG_ON(!inode); return inode; @@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, inode = _ocfs2_get_system_file_inode(osb, type, slot); /* add one more if putting into array for first time */ - if (arr && inode) { - *arr = igrab(inode); - BUG_ON(!*arr); + if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) { + inode = igrab(inode); + BUG_ON(!inode); } - mutex_unlock(&osb->system_file_mutex); return inode; } From 4d80db59de9c7b52f75d8f32005dafc8d64658b0 Mon Sep 17 00:00:00 2001 From: Ingyu Jang Date: Fri, 15 May 2026 04:32:14 +0900 Subject: [PATCH 2145/5214] error-inject: use IS_ERR() check for debugfs_create_file() debugfs_create_file() returns an error pointer on failure, never NULL, so the !file check in ei_debugfs_init() never triggers and the debugfs_remove() cleanup cannot run. Use IS_ERR() and propagate the actual error via PTR_ERR(). Link: https://lore.kernel.org/20260514193214.2432769-1-ingyujang25@korea.ac.kr Signed-off-by: Ingyu Jang Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/error-inject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/error-inject.c b/lib/error-inject.c index f3d1b70be605..32f3d1ca9ea2 100644 --- a/lib/error-inject.c +++ b/lib/error-inject.c @@ -219,9 +219,9 @@ static int __init ei_debugfs_init(void) dir = debugfs_create_dir("error_injection", NULL); file = debugfs_create_file("list", 0444, dir, NULL, &ei_fops); - if (!file) { + if (IS_ERR(file)) { debugfs_remove(dir); - return -ENOMEM; + return PTR_ERR(file); } return 0; From 5366a017099c6a3c443be908a05f26fd72af12a1 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:02 -0400 Subject: [PATCH 2146/5214] ocfs2: reject dinodes with non-canonical i_mode type Patch series "ocfs2: harden inode validators against forged metadata", v2. This series adds three structural checks to OCFS2 dinode validation so malformed on-disk fields are rejected before ocfs2_populate_inode() copies them into the in-core inode. The checks cover: - i_mode values whose type bits do not name a canonical POSIX file type; - non-device dinodes whose id1.dev1.i_rdev field is non-zero; and - non-inline dinodes that claim non-zero i_size while i_clusters is zero, covering directories unconditionally and regular files on non-sparse volumes. The normal read path reports these through ocfs2_error(), matching the existing suballoc-slot, inline-data, chain-list, and refcount checks. The online filecheck path uses the same structural predicates but keeps its own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(). This patch (of 3): ocfs2_validate_inode_block() currently accepts any non-zero i_mode value. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; an unrecognised type falls through to ocfs2_special_file_iops and init_special_inode(). Reject dinodes whose type bits do not name one of the seven canonical POSIX file types. Use fs_umode_to_ftype(), the same generic file-type conversion helper OCFS2 already uses for directory entries, so the accepted inode type set matches the kernel file-type vocabulary instead of open-coding a local switch. Apply the same structural check to the online filecheck read path. filecheck keeps its own error namespace, so it reports malformed i_mode through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(), but it must not allow a malformed dinode to proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/20260519110404.1803902-2-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index a510a0eb1adc..e149ccbdc03c 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -64,7 +65,12 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode, static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, struct buffer_head *bh); static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, - struct buffer_head *bh); + struct buffer_head *bh); + +static bool ocfs2_valid_inode_mode(umode_t mode) +{ + return fs_umode_to_ftype(mode) != FT_UNKNOWN; +} void ocfs2_set_inode_flags(struct inode *inode) { @@ -1494,6 +1500,24 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Reject dinodes whose i_mode does not name one of the seven + * canonical POSIX file types. ocfs2_populate_inode() copies + * i_mode verbatim into inode->i_mode and then dispatches via + * switch (mode & S_IFMT) to file/dir/symlink/special_file iops; + * an unrecognised type falls into ocfs2_special_file_iops with + * init_special_inode(), which interprets i_rdev. Constrain the + * type here so the dispatch only ever sees a value mkfs.ocfs2 / + * VFS can produce. + */ + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1624,6 +1648,15 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le32_to_cpu(di->i_fs_generation)); rc = -OCFS2_FILECHECK_ERR_GENERATION; + goto bail; + } + + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: @@ -1812,4 +1845,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = { .co_io_lock = ocfs2_inode_cache_io_lock, .co_io_unlock = ocfs2_inode_cache_io_unlock, }; - From 51407c2d249987a466416890a5c931a2354a46aa Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:03 -0400 Subject: [PATCH 2147/5214] ocfs2: reject dinodes whose i_rdev disagrees with the file type id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union. It is only meaningful for character and block device inodes. For any other user-visible file type the on-disk value must be zero. ocfs2_populate_inode() currently copies id1.dev1.i_rdev into inode->i_rdev before the S_IFMT switch decides whether the inode is a special file. A non-device inode with a non-zero i_rdev can therefore publish stale or attacker-controlled device state into the in-core inode. System inodes legitimately use other arms of the same union, so keep the cross-check restricted to non-system inodes. Factor that predicate into a helper and use it in both the normal validator and online filecheck path; filecheck reports the malformed dinode through OCFS2_FILECHECK_ERR_INVALIDINO instead of ocfs2_error(). Link: https://lore.kernel.org/20260519110404.1803902-3-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index e149ccbdc03c..992980ea9804 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -72,6 +72,16 @@ static bool ocfs2_valid_inode_mode(umode_t mode) return fs_umode_to_ftype(mode) != FT_UNKNOWN; } +static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + + return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1518,6 +1528,41 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * id1.dev1.i_rdev is the device-number arm of the id1 union and + * is only meaningful for character and block device inodes. For + * any other regular user-visible file type the on-disk value + * must be zero. ocfs2_populate_inode() currently runs + * + * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); + * + * unconditionally, before the S_IFMT switch decides whether the + * inode is a special file. As a result, an i_rdev value present + * on a non-device inode is silently published into the in-core + * inode; a subsequent forced re-read or in-core mode mutation + * (cluster peer with raw write access to the shared LUN, + * on-disk corruption, or a separately forged dinode) can then + * expose the attacker-controlled device number to + * init_special_inode() without ever showing an unusual i_mode + * at validation time. + * + * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 + * and journal1 arms of the same union (allocator i_used / + * i_total counters and the journal ij_flags / + * ij_recovery_generation pair); those bytes are not an i_rdev + * and must not be checked here. Restrict the cross-check to + * non-system inodes, which is the full attacker-controllable + * surface. + */ + if (ocfs2_dinode_has_unexpected_rdev(di)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1657,6 +1702,16 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_mode)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_unexpected_rdev(di)) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:04 -0400 Subject: [PATCH 2148/5214] ocfs2: reject non-inline dinodes with i_size and zero i_clusters On a volume mounted without OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a non-inline regular file with non-zero i_size and zero i_clusters is structurally malformed: the extent map declares no allocated clusters yet the size header claims content exists. Keep rejecting that shape, but express it through a shared predicate so the same invariant is available to normal inode reads and online filecheck. The same zero-cluster shape is also malformed for non-inline directories. ocfs2 directory growth allocates backing storage before advancing i_size, and ocfs2_dir_foreach_blk_el() later walks until ctx->pos reaches i_size_read(inode). A forged directory dinode with a huge i_size and no clusters would repeatedly fail on holes while advancing through the claimed size. Sparse regular files remain exempt: on sparse-alloc volumes, truncate can legitimately grow i_size without allocating clusters. System inodes and inline-data dinodes also retain their separate storage rules. Mirror the check in ocfs2_filecheck_validate_inode_block() as well. filecheck reports through its own error namespace, so malformed size/cluster state is logged as a filecheck invalid-inode result rather than via ocfs2_error(), but it must not proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-4-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 992980ea9804..432eac01c176 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -82,6 +82,24 @@ static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; } +static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb, + struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) + return false; + if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters)) + return false; + + if (S_ISDIR(mode)) + return true; + + return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode); +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1563,6 +1581,33 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Non-inline directories must not have i_size without allocated + * clusters: directory growth adds storage before advancing i_size, + * and readdir walks i_size block-by-block. A forged directory + * with zero clusters and a huge i_size would repeatedly fault on + * holes while advancing through the claimed size. + * + * Non-inline regular files have the same invariant on non-sparse + * volumes. Sparse regular files are different: truncate can + * legitimately grow i_size without allocating clusters, so keep + * the sparse-alloc carveout for S_IFREG only. System inodes and + * inline-data dinodes have their own storage rules. + */ + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + rc = ocfs2_error(sb, + "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + rc = ocfs2_error(sb, + "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1712,6 +1757,21 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, le16_to_cpu(di->i_mode), (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 2e73ea35a5dbe7e258b7fd396bcb100fa154f029 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 16 May 2026 17:09:15 +0500 Subject: [PATCH 2149/5214] lib/uuid_kunit: add tests for the four random UUID/GUID generators uuid_kunit currently exercises only guid_parse() and uuid_parse() (plus their invalid-input paths). The four random generators exported from lib/uuid.c -- generate_random_uuid(), generate_random_guid(), uuid_gen() and guid_gen() -- have no direct kunit coverage. Random output cannot be compared against a fixed expected value, but RFC 4122 section 4.4 specifies two invariants that any version-4 random UUID/GUID must satisfy: - version 4 in the high nibble of the version byte (byte 6 in the wire uuid_t layout, byte 7 in the byte-swapped guid_t layout); - variant DCE 1.1 (binary 10x) in the high bits of byte 8. Add four test cases that invoke each generator several times and verify these bit patterns hold. The same checks catch a regression in either the mask/OR sequence in the generators or the layout constants. Run the loop a handful of times to cover the small but non-zero chance that an unmasked random byte happens to satisfy the version/variant pattern by accident on a single call. Link: https://lore.kernel.org/20260516120915.40544-1-sozdayvek@gmail.com Signed-off-by: Stepan Ionichev Acked-by: Andy Shevchenko Cc: Brendan Higgins Cc: David Gow Cc: Greg Kroah-Hartman Signed-off-by: Andrew Morton --- lib/tests/uuid_kunit.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/tests/uuid_kunit.c b/lib/tests/uuid_kunit.c index de71b2649dac..2ef64fbe67d6 100644 --- a/lib/tests/uuid_kunit.c +++ b/lib/tests/uuid_kunit.c @@ -86,11 +86,67 @@ static void uuid_test_uuid_invalid(struct kunit *test) } } +/* + * RFC 4122 section 4.4 says random UUIDs/GUIDs (version 4) must have: + * - version 4 in the high nibble of the version byte, + * - variant DCE 1.1 (binary 10x) in the high bits of byte 8. + * + * The version byte is byte 6 in the "wire" uuid_t layout and byte 7 in + * the byte-swapped guid_t layout. + */ +static void uuid_test_uuid_gen(struct kunit *test) +{ + uuid_t u; + + for (unsigned int i = 0; i < 8; i++) { + uuid_gen(&u); + KUNIT_EXPECT_EQ(test, u.b[6] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, u.b[8] & 0xc0, 0x80); + } +} + +static void uuid_test_guid_gen(struct kunit *test) +{ + guid_t g; + + for (unsigned int i = 0; i < 8; i++) { + guid_gen(&g); + KUNIT_EXPECT_EQ(test, g.b[7] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, g.b[8] & 0xc0, 0x80); + } +} + +static void uuid_test_generate_random_uuid(struct kunit *test) +{ + unsigned char buf[16]; + + for (unsigned int i = 0; i < 8; i++) { + generate_random_uuid(buf); + KUNIT_EXPECT_EQ(test, buf[6] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, buf[8] & 0xc0, 0x80); + } +} + +static void uuid_test_generate_random_guid(struct kunit *test) +{ + unsigned char buf[16]; + + for (unsigned int i = 0; i < 8; i++) { + generate_random_guid(buf); + KUNIT_EXPECT_EQ(test, buf[7] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, buf[8] & 0xc0, 0x80); + } +} + static struct kunit_case uuid_test_cases[] = { KUNIT_CASE(uuid_test_guid_valid), KUNIT_CASE(uuid_test_uuid_valid), KUNIT_CASE(uuid_test_guid_invalid), KUNIT_CASE(uuid_test_uuid_invalid), + KUNIT_CASE(uuid_test_uuid_gen), + KUNIT_CASE(uuid_test_guid_gen), + KUNIT_CASE(uuid_test_generate_random_uuid), + KUNIT_CASE(uuid_test_generate_random_guid), {}, }; From 685568777c5a18fbc40bd0b64527fd9444c255be Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 14 May 2026 18:56:03 +0200 Subject: [PATCH 2150/5214] string: use min in sized_strscpy Use min() and drop the limit variable to simplify sized_strscpy(). Link: https://lore.kernel.org/20260514165601.527883-3-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Cc: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/string.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/string.c b/lib/string.c index b632c71df1a5..1f9297e9776a 100644 --- a/lib/string.c +++ b/lib/string.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -125,11 +126,8 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count) * If src is unaligned, don't cross a page boundary, * since we don't know if the next page is mapped. */ - if ((long)src & (sizeof(long) - 1)) { - size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)); - if (limit < max) - max = limit; - } + if ((long)src & (sizeof(long) - 1)) + max = min(PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)), max); #else /* If src or dest is unaligned, don't do word-at-a-time. */ if (((long) dest | (long) src) & (sizeof(long) - 1)) From c60ffec33ddf24577f6f4da18fe825b2058c5f78 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 21 May 2026 11:03:36 +0800 Subject: [PATCH 2151/5214] lib/nmi_backtrace: print out the CPUs which fail to respond to NMI When debugging RCU stall cases, usually all CPUs will respond to the NMI and print out the backtrace. But in some nasty or hardware related cases, some CPUs may fail to respond in 10 seconds, and very likely this is sign of severe issues. Paul McKenney has implemented the NMI backtrace stall check for x86, and for other architectures, it should be also helpful to at least print out those CPUs which failed to repond to the NMI, so that users can get an early heads-up for possible CPU hard stall. [feng.tang@linux.alibaba.com: avoid hard-coding "10" in two places and in a comment] Link: https://lore.kernel.org/ag-1ciG0FSomBf7q@U-2FWC9VHC-2323.local [akpm@linux-foundation.org: use __stringify()] Link: https://lore.kernel.org/20260521030336.92172-1-feng.tang@linux.alibaba.com Signed-off-by: Feng Tang Reviewed-by: Petr Mladek Cc: "Paul E . McKenney" Signed-off-by: Andrew Morton --- lib/nmi_backtrace.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c index 33c154264bfe..a3bfa9360b23 100644 --- a/lib/nmi_backtrace.c +++ b/lib/nmi_backtrace.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,8 @@ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; /* "in progress" flag of arch_trigger_cpumask_backtrace */ static unsigned long backtrace_flag; +#define NMI_BT_TIMEOUT_SEC 10 + /* * When raise() is called it will be passed a pointer to the * backtrace_mask. Architectures that call nmi_cpu_backtrace() @@ -68,14 +71,20 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask, raise(to_cpumask(backtrace_mask)); } - /* Wait for up to 10 seconds for all CPUs to do the backtrace */ - for (i = 0; i < 10 * 1000; i++) { + /* Wait for up to NMI_BT_TIMEOUT_SEC seconds for all CPUs to do the backtrace */ + for (i = 0; i < NMI_BT_TIMEOUT_SEC * 1000; i++) { if (cpumask_empty(to_cpumask(backtrace_mask))) break; mdelay(1); touch_softlockup_watchdog(); } - nmi_backtrace_stall_check(to_cpumask(backtrace_mask)); + + if (!cpumask_empty(to_cpumask(backtrace_mask))) { + pr_warn("After " __stringify(NMI_BT_TIMEOUT_SEC) " seconds, these CPUS still haven't responded to the NMI: %*pbl\n", + cpumask_pr_args(to_cpumask(backtrace_mask))); + + nmi_backtrace_stall_check(to_cpumask(backtrace_mask)); + } /* * Force flush any remote buffers that might be stuck in IRQ context From 38cd651ebce7065a81c7e950d9e2ea1572304605 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 20 May 2026 10:57:20 +0800 Subject: [PATCH 2152/5214] soundwire: only handle alert events when the peripheral is attached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't make sense to handle an alert event when the peripheral is not attached. The slave->status could be SDW_SLAVE_ATTACHED or SDW_SLAVE_ALERT when it is attached on the bus. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260520025720.1999367-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fe5316d93fef..0490777fa406 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1958,6 +1958,10 @@ int sdw_handle_slave_status(struct sdw_bus *bus, break; case SDW_SLAVE_ALERT: + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + ret = sdw_handle_slave_alerts(slave); if (ret < 0) dev_err(&slave->dev, From 4dab2b904414fac53535c4e4cdad808132f4cdc2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 20 May 2026 17:36:31 +0100 Subject: [PATCH 2153/5214] soundwire: dmi-quirks: Disable ghost Realtek devices Many systems ship with a Realtek audio codec in the ACPI that doesn't physically exist in the system. This confuses the newer function topology system that creates the soundcard, as it builds the card based on the ACPI information. Whilst we are working with the laptop vendors to try and stop this happening there are quite a few systems where this has shipped. Add a quirk to disable this "ghost" device. Currently this patch should cover: - Asus UX5406AA - Lenovo Yoga Pro 9i (83SF) - Lenovo Yoga Slim 7 Ultra (83QK) Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260520163631.3300102-4-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul --- drivers/soundwire/dmi-quirks.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 5854218e1a27..32a46a2d90f7 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -90,6 +90,19 @@ static const struct adr_remap intel_rooks_county[] = { {} }; +/* + * Many platforms have ghost realtek devices in the ACPI that don't physically + * exist, remove those devices. + */ +static const struct adr_remap ghost_realtek[] = { + /* rt722 on link3 */ + { + 0x000330025d072201ull, + 0x0000000000000000ull + }, + {} +}; + static const struct dmi_system_id adr_remap_quirk_table[] = { /* TGL devices */ { @@ -164,6 +177,28 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)hp_omen_16, }, + /* PTL devices */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUS"), + DMI_MATCH(DMI_BOARD_NAME, "UX5406AA"), + }, + .driver_data = (void *)ghost_realtek, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + }, + .driver_data = (void *)ghost_realtek, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83SF"), + }, + .driver_data = (void *)ghost_realtek, + }, {} }; From c72da0688575e5ef39c36bb44fed53aa18f8ae65 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:38 +0300 Subject: [PATCH 2154/5214] iio: adc: ad_sigma_delta: fix CS held asserted and state leaks In ad_sigma_delta_single_conversion(), set_mode(AD_SD_MODE_IDLE) and disable_one() were called from the out: block while keep_cs_asserted was still true. This caused any SPI transfer issued by those callbacks to carry cs_change=1, leaving CS permanently asserted after the conversion. Fix by moving both calls into the out_unlock: block, after keep_cs_asserted is cleared, matching the pattern already used in ad_sd_calibrate(). In the error path of ad_sd_buffer_postenable(), if an operation fails after set_mode(AD_SD_MODE_CONTINUOUS) has already succeeded (e.g. spi_offload_trigger_enable()), the device is left in continuous conversion mode with CS physically asserted. Additionally, bus_locked remaining true after spi_bus_unlock() causes subsequent SPI operations to call spi_sync_locked() without the bus lock actually held, allowing concurrent SPI access. Fix the error path by clearing keep_cs_asserted first, then calling set_mode(AD_SD_MODE_IDLE) to revert the device mode and deassert CS, then clearing bus_locked before releasing the bus. For devices that implement neither set_mode nor disable_one (such as MAX11205, which has no physical CS pin), no SPI transfer is issued during cleanup and the cs_change flag has no effect on any physical line. Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a955556f9ec8..651ade67ad2e 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -441,11 +441,10 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, out: ad_sd_disable_irq(sigma_delta); - ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); - ad_sigma_delta_disable_one(sigma_delta, chan->address); - out_unlock: sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); out_release: @@ -578,6 +577,9 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return 0; err_unlock: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); spi_unoptimize_message(&sigma_delta->sample_msg); From 91bc6767a4f55dc470d8a56b55b9f2ea09094efe Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:39 +0300 Subject: [PATCH 2155/5214] iio: adc: ad_sigma_delta: fix clear_pending_event for registerless devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ad_sigma_delta_clear_pending_event() falls through to the status register read path for devices with has_registers = false and no rdy_gpiod. For such devices, ad_sd_read_reg() skips the address byte entirely and clocks raw MISO bytes with no address phase — making it byte-for-byte identical to reading conversion data. If a pending conversion result is present, this partially consumes it and corrupts the data stream for the subsequent ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Furthermore, with num_resetclks = 0 on these devices, data_read_len evaluates to 0. If the clocked byte has bit 7 clear, pending_event is set and the code attempts memset(data + 2, 0xff, 0 - 1), overflowing to SIZE_MAX and corrupting the heap. Fix by returning 0 immediately when neither rdy_gpiod nor has_registers is set. This is safe for all current registerless devices: ad7191 and ad7780 (with powerdown GPIO) are reset between conversions by CS deassertion, so there is no stale result to drain; ad7780 (without powerdown GPIO) and max11205 are continuously-converting and cycle ~DRDY at the output data rate regardless of whether the previous result was read, so the next falling edge fires naturally. A future registerless device that holds ~DRDY asserted until data is read would be broken by this early return and would require either num_resetclks set or a rdy-gpio. The same heap corruption is reachable on any device with rdy_gpiod set but num_resetclks = 0: if the GPIO indicates a pending event, the drain path executes memset(data + 2, 0xff, 0 - 1) regardless of has_registers. Add an explicit data_read_len == 0 guard after the pending event check; the stale result is then consumed by the first ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 651ade67ad2e..1b410291da53 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -262,11 +262,25 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta /* * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an - * old event. + * old event. For devices with neither an RDY GPIO nor registers, + * ad_sd_read_reg() transmits no address byte and clocks raw MISO bytes, + * which is indistinguishable from reading conversion data and would + * partially consume a pending result. Skip the check for such devices. + * + * This is safe for all current registerless devices: ad7191 and ad7780 + * (with powerdown GPIO) are reset between conversions by CS deassertion, + * so there is no stale result to drain; ad7780 (without powerdown GPIO) + * and max11205 are continuously-converting and cycle ~DRDY at the output + * data rate regardless of whether the previous result was read, so the + * next falling edge fires naturally. + * + * A future registerless device that holds ~DRDY asserted until data is + * read would be broken by this early return and would need either + * num_resetclks set or a rdy-gpio. */ if (sigma_delta->rdy_gpiod) { pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); - } else { + } else if (sigma_delta->info->has_registers) { unsigned int status_reg; ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); @@ -274,11 +288,24 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta return ret; pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } else { + return 0; } if (!pending_event) return 0; + /* + * With num_resetclks = 0, data_read_len is 0 and the drain sequence + * below would compute memset(data + 2, 0xff, 0 - 1), underflowing to + * SIZE_MAX and corrupting the heap. There is no safe way to drain the + * stale result without knowing the data register size; it will be + * consumed by the first ad_sd_read_reg() call in + * ad_sigma_delta_single_conversion(). + */ + if (!data_read_len) + return 0; + /* * In general the size of the data register is unknown. It varies from * device to device, might be one byte longer if CONTROL.DATA_STATUS is From 70008e22ab3fd619fb2a50dcfa80b9dfa26c5d8a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Sat, 23 May 2026 12:12:37 +0200 Subject: [PATCH 2156/5214] gfs2: Remove unused fallocate_chunk argument The mode argument of fallocate_chunk() became unused in commit 1885867b84d5 ("GFS2: Update i_size properly on fallocate"), so remove it. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/file.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 9704f1ef6ad1..48ebda5ba808 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1173,8 +1173,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) return ret; } -static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, - int mode) +static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len) { struct super_block *sb = inode->i_sb; struct gfs2_inode *ip = GFS2_I(inode); @@ -1336,7 +1335,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t if (error) goto out_trans_fail; - error = fallocate_chunk(inode, offset, max_bytes, mode); + error = fallocate_chunk(inode, offset, max_bytes); gfs2_trans_end(sdp); if (error) From 4982e58669b11c43644efb5fb7435975848b716e Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 27 May 2026 21:15:04 +0200 Subject: [PATCH 2157/5214] gfs2: page poisoning fix Processes can write to the last page of a file using mmap, and when the file size is not a multiple of the page size, this can be used to write beyond the end of the file. This is sometimes referred to as page poisoning, and it is not a problem in itself because the data beyond eof will be ignored. However, we currently fail to clear out any space beyond the end of the file that we skip over when the file size is increased, so that "poison" can end up getting exposed. Fix that. Fixes xfstest generic/363. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/bmap.c | 19 +++++++++++++++++++ fs/gfs2/bmap.h | 1 + fs/gfs2/file.c | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index b3d7fcd95f03..95a64819fe2c 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1321,6 +1321,19 @@ static int gfs2_block_zero_range(struct inode *inode, loff_t from, loff_t length &gfs2_iomap_write_ops, NULL); } +int gfs2_clear_beyond_eof(struct inode *inode, loff_t end) +{ + loff_t isize = i_size_read(inode); + unsigned int len = isize & ~PAGE_MASK; + + if (!len || isize >= end) + return 0; + len = PAGE_SIZE - len; + if (end - isize < len) + len = end - isize; + return gfs2_block_zero_range(inode, isize, len); +} + #define GFS2_JTRUNC_REVOKES 8192 /** @@ -2096,6 +2109,12 @@ static int do_grow(struct inode *inode, u64 size) unstuff = 1; } + if (!unstuff) { + error = gfs2_clear_beyond_eof(inode, size); + if (error) + goto do_grow_qunlock; + } + error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT + (unstuff && gfs2_is_jdata(ip) ? RES_JDATA : 0) + diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 6cdc72dd55a3..e3d6efdfd890 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -58,6 +58,7 @@ int gfs2_get_extent(struct inode *inode, u64 lblock, u64 *dblock, unsigned int *extlen); int gfs2_alloc_extent(struct inode *inode, u64 lblock, u64 *dblock, unsigned *extlen, bool *new); +int gfs2_clear_beyond_eof(struct inode *inode, loff_t end); int gfs2_setattr_size(struct inode *inode, u64 size); int gfs2_truncatei_resume(struct gfs2_inode *ip); int gfs2_file_dealloc(struct gfs2_inode *ip); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 48ebda5ba808..b8c10de113ba 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1057,6 +1057,10 @@ static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, goto out_unlock; } + ret = gfs2_clear_beyond_eof(inode, iocb->ki_pos); + if (ret) + goto out_unlock; + pagefault_disable(); ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops, &gfs2_iomap_write_ops, NULL); @@ -1265,6 +1269,12 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t next = (next + 1) << sdp->sd_sb.sb_bsize_shift; + if (!(mode & FALLOC_FL_KEEP_SIZE)) { + error = gfs2_clear_beyond_eof(inode, offset + len); + if (error) + return error; + } + offset &= bsize_mask; len = next - offset; From fa09f08ede3db3050ae16ae1ed92c902d0cada23 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 29 May 2026 00:52:01 +0800 Subject: [PATCH 2158/5214] coresight: etb10: restore atomic_t for shared reading state The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate for userspace buffer access. etb_open() claims that gate with local_cmpxchg(), and etb_release() clears it with local_set(). That gate is shared per-device state rather than CPU-local state. A running system can reach it whenever /dev/ is opened, closed, and reopened by different tasks while the device remains registered, so the same drvdata->reading variable may be claimed on one CPU and later cleared on another. This code used to use atomic_t for the same gate, but commit 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") changed it to local_t even though the access pattern remained cross-task and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and atomic_set() so the exclusivity gate again uses a primitive intended for shared state. The issue was found on Linux v6.18.21 by our static analysis tool while scanning surviving local_t-on-shared-state sites, and then manually reviewed against the live etb10 file-op path. It was runtime-validated with a reproducible QEMU no-device KCSAN PoC that kept the same report-local contract: 1. use one shared struct etb_drvdata carrier and its drvdata->reading gate; 2. call etb_open() and etb_release() sequentially on that gate to confirm the original claim/clear path; 3. bind the open side to CPU0 and the release side to CPU1 for the same gate to show cross-CPU ownership; 4. run bound workers that repeatedly race etb_open() and etb_release() on the same gate until KCSAN reports a target hit. The harness recorded: L1 passed open=1 release=1 reading_after_open=1 reading_after_release=0 L2 passed open_cpu=0 release_cpu=1 cross_cpu_release=1 reading_after=0 open_ret=0 Representative KCSAN excerpt from the no-device validation run: BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv] write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1: etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0: etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 value changed: 0x00000000 -> 0x00000001 Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2 This no-device harness is not a real ETB10 hardware end-to-end run, but it preserves the same shared drvdata->reading gate and the same etb_open()/etb_release() claim/clear contract. No real ETB10 hardware was available for runtime testing. Build-tested with: make olddefconfig make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o Fixes: 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260528165201.319452-1-runyu.xiao@seu.edu.cn --- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b952a1d47f12..a827f76b8144 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -83,7 +83,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; raw_spinlock_t spinlock; - local_t reading; + atomic_t reading; pid_t pid; u8 *buf; u32 buffer_depth; @@ -601,7 +601,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (local_cmpxchg(&drvdata->reading, 0, 1)) + if (atomic_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); @@ -639,7 +639,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - local_set(&drvdata->reading, 0); + atomic_set(&drvdata->reading, 0); dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); return 0; From da27c4379ce6b2b7d5d3359e9b10c829b9e5dbbe Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 5 May 2026 12:36:18 -0300 Subject: [PATCH 2159/5214] perf session: Add minimum event size and alignment validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a per-type minimum size table (perf_event__min_size[]) and enforce it before swap and processing, so that both cross-endian and native-endian paths are protected from accessing fields past the event boundary. The table uses offsetof() for types with trailing variable-length fields (filenames, strings, msg arrays) and sizeof() for fixed-size types. Zero entries mean no minimum beyond the 8-byte header already enforced by the reader. Undersized events are skipped with a warning in process_event and rejected in peek_event — both checked before the swap handler runs, preventing OOB access on crafted event fields. Also reject events whose header.size is not 8-byte aligned. The kernel aligns all event sizes to sizeof(u64) — see perf_event_comm_event() (ALIGN), perf_event_mmap_event(), perf_event_cgroup(), perf_event_ksymbol() (IS_ALIGNED loops), and perf_event_text_poke() (ALIGN) in kernel/events/core.c. An unaligned size means the file is corrupted or crafted; reject early so downstream code that divides by sizeof(u64) to compute array element counts gets exact results. Three legacy user events are exempted from the alignment check: TRACING_DATA (66) had a 12-byte struct before commit b39c915a4f36 ("libperf event: Ensure tracing data is multiple of 8 sized") added padding, COMPRESSED (81) carries raw ZSTD output (already superseded by COMPRESSED2 with PERF_ALIGN), and HEADER_FEATURE (80) uses do_write_string() with a 4-byte length prefix. Also guard event_swap() against crafted event types >= PERF_RECORD_HEADER_MAX to prevent OOB reads on the perf_event__swap_ops[] array. Changes in v2: - Fix double-skip for unsupported event types: return 0 instead of event->header.size in perf_session__process_event() for HEADER_MAX, since reader__read_event() already advances by event->header.size (Reported-by: sashiko-bot@kernel.org) - Exempt TRACING_DATA, COMPRESSED, and HEADER_FEATURE from the alignment check — these legacy user events predate the 8-byte alignment rule (Reported-by: sashiko-bot@kernel.org) - peek_event: return 0 (skip) for unknown event types instead of -1 (error), consistent with process_event which already skips unsupported types gracefully (Reported-by: sashiko-bot@kernel.org) Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 263 ++++++++++++++++++++++++++++++++------ 1 file changed, 225 insertions(+), 38 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1e25892963b7..0523fd243e02 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1759,15 +1759,121 @@ int perf_session__deliver_synth_attr_event(struct perf_session *session, return perf_session__deliver_synth_event(session, &ev.ev, NULL); } +/* + * Minimum event sizes indexed by type. Checked before swap and + * processing so that both cross-endian and native-endian paths + * are protected from accessing fields past the event boundary. + * Zero means no minimum beyond the 8-byte header (already + * enforced by the reader). + */ +static const u32 perf_event__min_size[PERF_RECORD_HEADER_MAX] = { + /* + * offsetof() + 1 for types with a trailing variable-length + * string (filename, comm, path, name, msg): the +1 ensures + * room for at least a null terminator. Full null-termination + * within the event boundary is checked separately. + * + * PERF_RECORD_SAMPLE is omitted: all64_swap is bounded by + * header.size, and the internal layout varies by sample_type + * so a fixed minimum is not meaningful. + */ + [PERF_RECORD_MMAP] = offsetof(struct perf_record_mmap, filename) + 1, + [PERF_RECORD_LOST] = sizeof(struct perf_record_lost), + [PERF_RECORD_COMM] = offsetof(struct perf_record_comm, comm) + 1, + [PERF_RECORD_EXIT] = sizeof(struct perf_record_fork), + [PERF_RECORD_THROTTLE] = sizeof(struct perf_record_throttle), + [PERF_RECORD_UNTHROTTLE] = sizeof(struct perf_record_throttle), + [PERF_RECORD_FORK] = sizeof(struct perf_record_fork), + /* + * The kernel dynamically sizes PERF_RECORD_READ based on + * attr.read_format — the minimum has just pid + tid + value. + */ + [PERF_RECORD_READ] = offsetof(struct perf_record_read, time_enabled), + [PERF_RECORD_MMAP2] = offsetof(struct perf_record_mmap2, filename) + 1, + [PERF_RECORD_LOST_SAMPLES] = sizeof(struct perf_record_lost_samples), + [PERF_RECORD_AUX] = sizeof(struct perf_record_aux), + [PERF_RECORD_ITRACE_START] = sizeof(struct perf_record_itrace_start), + [PERF_RECORD_SWITCH] = sizeof(struct perf_event_header), + [PERF_RECORD_SWITCH_CPU_WIDE] = sizeof(struct perf_record_switch), + [PERF_RECORD_NAMESPACES] = sizeof(struct perf_record_namespaces), + [PERF_RECORD_CGROUP] = offsetof(struct perf_record_cgroup, path) + 1, + [PERF_RECORD_TEXT_POKE] = sizeof(struct perf_record_text_poke_event), + [PERF_RECORD_KSYMBOL] = offsetof(struct perf_record_ksymbol, name) + 1, + [PERF_RECORD_BPF_EVENT] = sizeof(struct perf_record_bpf_event), + [PERF_RECORD_HEADER_ATTR] = sizeof(struct perf_event_header) + PERF_ATTR_SIZE_VER0, + [PERF_RECORD_HEADER_EVENT_TYPE] = sizeof(struct perf_record_header_event_type), + /* Legacy events predate the __u32 pad field, accept 12-byte records */ + [PERF_RECORD_HEADER_TRACING_DATA] = offsetof(struct perf_record_header_tracing_data, pad), + [PERF_RECORD_AUX_OUTPUT_HW_ID] = sizeof(struct perf_record_aux_output_hw_id), + [PERF_RECORD_AUXTRACE_INFO] = sizeof(struct perf_record_auxtrace_info), + [PERF_RECORD_AUXTRACE] = sizeof(struct perf_record_auxtrace), + [PERF_RECORD_AUXTRACE_ERROR] = offsetof(struct perf_record_auxtrace_error, msg) + 1, + [PERF_RECORD_THREAD_MAP] = sizeof(struct perf_record_thread_map), + /* Smallest valid variant is RANGE_CPUS: header(8) + type(2) + range(6) */ + [PERF_RECORD_CPU_MAP] = sizeof(struct perf_event_header) + + sizeof(__u16) + + sizeof(struct perf_record_range_cpu_map), + [PERF_RECORD_STAT_CONFIG] = sizeof(struct perf_record_stat_config), + [PERF_RECORD_STAT] = sizeof(struct perf_record_stat), + [PERF_RECORD_STAT_ROUND] = sizeof(struct perf_record_stat_round), + /* Union inflates sizeof; use fixed header fields as minimum */ + [PERF_RECORD_EVENT_UPDATE] = offsetof(struct perf_record_event_update, scale), + [PERF_RECORD_TIME_CONV] = offsetof(struct perf_record_time_conv, time_cycles), + [PERF_RECORD_ID_INDEX] = sizeof(struct perf_record_id_index), + [PERF_RECORD_HEADER_BUILD_ID] = sizeof(struct perf_record_header_build_id), + [PERF_RECORD_HEADER_FEATURE] = sizeof(struct perf_record_header_feature), + [PERF_RECORD_COMPRESSED2] = sizeof(struct perf_record_compressed2), + [PERF_RECORD_BPF_METADATA] = sizeof(struct perf_record_bpf_metadata), + [PERF_RECORD_CALLCHAIN_DEFERRED] = sizeof(struct perf_event_header) + sizeof(__u64), + /* + * SCHEDSTAT events have a version-dependent union after the + * fixed header fields; the minimum is the base (pre-union) + * portion so old and new versions both pass. + */ + [PERF_RECORD_SCHEDSTAT_CPU] = offsetof(struct perf_record_schedstat_cpu, v15), + [PERF_RECORD_SCHEDSTAT_DOMAIN] = offsetof(struct perf_record_schedstat_domain, v15), +}; + +/* + * Return true if the event is too small for its declared type. + * Caller must ensure event->header.type < PERF_RECORD_HEADER_MAX. + * If min is non-NULL, stores the required minimum on failure. + */ +static bool perf_event__too_small(const union perf_event *event, u32 *min) +{ + u32 min_sz = perf_event__min_size[event->header.type]; + + if (min_sz && event->header.size < min_sz) { + if (min) + *min = min_sz; + return true; + } + + return false; +} + +/* Caller must ensure event->header.type < PERF_RECORD_HEADER_MAX */ static void event_swap(union perf_event *event, bool sample_id_all) { - perf_event__swap_op swap; - - swap = perf_event__swap_ops[event->header.type]; + perf_event__swap_op swap = perf_event__swap_ops[event->header.type]; if (swap) swap(event, sample_id_all); } +/* + * Read and validate the event at @file_offset. + * + * Returns: + * 0 — success: *event_ptr is set and safe to access. + * -1 — error; check *event_ptr to decide whether to advance or abort: + * *event_ptr set — event header was read but the event is + * malformed (too small for its type, or byte-swap + * failed). header.size is still valid, so the + * caller can advance past the event. + * *event_ptr NULL — fatal: couldn't read the header at all + * (I/O error, offset out of range, pipe mode). + * Caller must abort. + */ int perf_session__peek_event(struct perf_session *session, off_t file_offset, void *buf, size_t buf_sz, union perf_event **event_ptr, @@ -1775,52 +1881,85 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, { union perf_event *event; size_t hdr_sz, rest; + u32 min_sz; int fd; + *event_ptr = NULL; + if (session->one_mmap && !session->header.needs_swap) { event = file_offset - session->one_mmap_offset + session->one_mmap_addr; - goto out_parse_sample; + + /* Every event must at least contain its own header */ + if (event->header.size < sizeof(struct perf_event_header)) + return -1; + } else { + if (perf_data__is_pipe(session->data)) + return -1; + + fd = perf_data__fd(session->data); + hdr_sz = sizeof(struct perf_event_header); + + if (buf_sz < hdr_sz) + return -1; + + if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || + readn(fd, buf, hdr_sz) != (ssize_t)hdr_sz) + return -1; + + event = (union perf_event *)buf; + + if (session->header.needs_swap) + perf_event_header__bswap(&event->header); + + if (event->header.size < hdr_sz || event->header.size > buf_sz) + return -1; + + buf += hdr_sz; + rest = event->header.size - hdr_sz; + + if (readn(fd, buf, rest) != (ssize_t)rest) + return -1; } - if (perf_data__is_pipe(session->data)) + /* Event data is fully loaded — expose so callers can advance */ + *event_ptr = event; + + /* + * Check alignment before type: an unaligned size misaligns the + * stream for all subsequent reads regardless of event type. + * Three legacy user events predate the 8-byte rule — exempt them. + */ + if (event->header.size % sizeof(u64) && + event->header.type != PERF_RECORD_HEADER_TRACING_DATA && + event->header.type != PERF_RECORD_COMPRESSED && + event->header.type != PERF_RECORD_HEADER_FEATURE) { + pr_warning("WARNING: peek_event: event type %u size %u not aligned to %zu\n", + event->header.type, + event->header.size, sizeof(u64)); return -1; + } - fd = perf_data__fd(session->data); - hdr_sz = sizeof(struct perf_event_header); + if (event->header.type >= PERF_RECORD_HEADER_MAX) { + pr_warning("WARNING: peek_event: unsupported event type %u, skipping\n", + event->header.type); + return 0; + } - if (buf_sz < hdr_sz) - return -1; - - if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || - readn(fd, buf, hdr_sz) != (ssize_t)hdr_sz) - return -1; - - event = (union perf_event *)buf; - - if (session->header.needs_swap) - perf_event_header__bswap(&event->header); - - if (event->header.size < hdr_sz || event->header.size > buf_sz) - return -1; - - buf += hdr_sz; - rest = event->header.size - hdr_sz; - - if (readn(fd, buf, rest) != (ssize_t)rest) + if (perf_event__too_small(event, &min_sz)) { + pr_warning("WARNING: peek_event: %s event size %u too small (min %u)\n", + perf_event__name(event->header.type), + event->header.size, min_sz); return -1; + } if (session->header.needs_swap) event_swap(event, evlist__sample_id_all(session->evlist)); -out_parse_sample: - if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && evlist__parse_sample(session->evlist, event, sample)) return -1; - *event_ptr = event; - return 0; } @@ -1858,23 +1997,71 @@ static s64 perf_session__process_event(struct perf_session *session, { struct evlist *evlist = session->evlist; const struct perf_tool *tool = session->tool; + u32 min_sz; int ret; - if (session->header.needs_swap) - event_swap(event, evlist__sample_id_all(evlist)); + /* + * The kernel aligns all event sizes to sizeof(u64) — see + * perf_event_comm_event() (ALIGN), perf_event_mmap_event(), + * perf_event_cgroup(), perf_event_ksymbol() (IS_ALIGNED loops), + * and perf_event_text_poke() (ALIGN) in kernel/events/core.c. + * + * An unaligned size means the file is corrupted or crafted. + * Abort: there is no point continuing to read unaligned records + * because the caller advances rd->head by event->header.size, + * so every subsequent read would start at a misaligned offset, + * producing garbage headers for the rest of the file. + * + * Exempt three legacy user events that predate the alignment rule: + * + * TRACING_DATA (66): struct tracing_data_event was 12 bytes before + * b39c915a4f36 ("libperf event: Ensure tracing data is multiple + * of 8 sized") added __u32 pad; old perf.data files still contain + * 12-byte records. + * TODO: introduce HEADER_TRACING_DATA2 with guaranteed alignment. + * + * COMPRESSED (81): raw ZSTD output, arbitrary length. Already + * superseded by COMPRESSED2 (83) with PERF_ALIGN. + * + * HEADER_FEATURE (80): do_write_string() uses a 4-byte length + * prefix with no padding to 8-byte total. + * TODO: introduce HEADER_FEATURE2 with guaranteed alignment. + */ + if (event->header.size % sizeof(u64) && + event->header.type != PERF_RECORD_HEADER_TRACING_DATA && + event->header.type != PERF_RECORD_COMPRESSED && + event->header.type != PERF_RECORD_HEADER_FEATURE) { + pr_err("ERROR: %s event size %u is not 8-byte aligned, aborting\n", + perf_event__name(event->header.type), + event->header.size); + return -EINVAL; + } if (event->header.type >= PERF_RECORD_HEADER_MAX) { - /* perf should not support unaligned event, stop here. */ - if (event->header.size % sizeof(u64)) - return -EINVAL; - /* This perf is outdated and does not support the latest event type. */ ui__warning("Unsupported header type %u, please consider updating perf.\n", event->header.type); - /* Skip unsupported event by returning its size. */ - return event->header.size; + /* + * Return 0 to skip: the caller (reader__read_event) + * already advances by event->header.size. + */ + return 0; } + /* + * Skip rather than abort: a too-small-but-aligned event + * can be safely stepped over without misaligning the stream. + */ + if (perf_event__too_small(event, &min_sz)) { + pr_warning("WARNING: %s event size %u too small (min %u), skipping\n", + perf_event__name(event->header.type), + event->header.size, min_sz); + return 0; + } + + if (session->header.needs_swap) + event_swap(event, evlist__sample_id_all(evlist)); + events_stats__inc(&evlist->stats, event->header.type); if (event->header.type >= PERF_RECORD_USER_TYPE_START) From 1e389b7639f17e947ab9ed77bcba9f2ab7420f74 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 23 May 2026 08:06:35 -0300 Subject: [PATCH 2160/5214] perf session: Bounds-check one_mmap event pointer in peek_event perf_session__peek_event() computes an event pointer directly from file_offset when one_mmap is active, without verifying that file_offset and the subsequent event->header.size fall within the mapped region. A corrupted perf.data file could cause out-of-bounds memory reads. Add one_mmap_size to the session struct and validate both the header and full event fit within the mmap before dereferencing. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 29 ++++++++++++++++++++++++++--- tools/perf/util/session.h | 2 ++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0523fd243e02..c4cd8ad6d810 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1887,12 +1887,27 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, *event_ptr = NULL; if (session->one_mmap && !session->header.needs_swap) { - event = file_offset - session->one_mmap_offset + - session->one_mmap_addr; + u64 offset_in_mmap; + + /* Validate offset with integer arithmetic to avoid pointer UB */ + if ((u64)file_offset < session->one_mmap_offset) + return -1; + + offset_in_mmap = (u64)file_offset - session->one_mmap_offset; + + /* Use subtraction to avoid addition overflow */ + if (offset_in_mmap >= session->one_mmap_size || + session->one_mmap_size - offset_in_mmap < sizeof(struct perf_event_header)) + return -1; + + event = session->one_mmap_addr + offset_in_mmap; - /* Every event must at least contain its own header */ if (event->header.size < sizeof(struct perf_event_header)) return -1; + + /* Ensure full event is within the mmap region */ + if (session->one_mmap_size - offset_in_mmap < event->header.size) + return -1; } else { if (perf_data__is_pipe(session->data)) return -1; @@ -2560,6 +2575,14 @@ reader__mmap(struct reader *rd, struct perf_session *session) if (session->one_mmap) { session->one_mmap_addr = buf; session->one_mmap_offset = rd->file_offset; + /* + * mmap_size was set to the full file extent (data_offset + + * data_size) but file_offset was shifted forward by + * page_offset for page alignment. Reduce by page_offset + * so the bounds check reflects the file-backed portion + * of the mapping — pages beyond the file cause SIGBUS. + */ + session->one_mmap_size = rd->mmap_size - page_offset; } return 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f05f0d4a6c23..d554e2a1a50e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -71,6 +71,8 @@ struct perf_session { void *one_mmap_addr; /** @one_mmap_offset: File offset in perf.data file when mapped. */ u64 one_mmap_offset; + /** @one_mmap_size: Size of the single mmap in bytes. */ + u64 one_mmap_size; /** @ordered_events: Used to turn unordered events into ordered ones. */ struct ordered_events ordered_events; /** @data: Optional perf data file being read from. */ From 1a646a28a0063f1fb31589f7190f2b4fb613e413 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 4 May 2026 07:37:22 -0300 Subject: [PATCH 2161/5214] perf tools: Fix event_contains() macro to verify full field extent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit event_contains() checked whether a field's start offset was within the event (header.size > offsetof), but not whether the full field fit. A crafted event with header.size = offsetof(field) + 1 would pass the check, but an 8-byte access (bswap_64, direct read) would overrun the event boundary by up to 7 bytes. Fix the macro to verify the complete field: header.size >= offsetof(field) + sizeof(field) Also update all callers that check event_contains(time_cycles) but access later fields (time_mask, cap_user_time_zero, cap_user_time_short) to check for cap_user_time_short — the last field accessed — so the entire extended block is verified: tsc.c, arm-spe.c, cs-etm.c, jitdump.c. Note: session.c's perf_event__time_conv_swap() also guards on time_cycles but accesses time_mask — a pre-existing issue not introduced by this macro change. It is fixed by a later patch in this series ("perf session: Add validated swap infrastructure with null-termination checks"), which decouples time_cycles and time_mask into independent per-field event_contains() checks. The struct assignment overread (session->time_conv = event->time_conv copies sizeof on a potentially shorter event) is separately fixed by "perf session: Use bounded copy for PERF_RECORD_TIME_CONV". Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/perf/event.h | 9 ++++++++- tools/perf/util/arm-spe.c | 2 +- tools/perf/util/cs-etm.c | 2 +- tools/perf/util/jitdump.c | 2 +- tools/perf/util/tsc.c | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index 9043dc72b5d6..fdced574c889 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -8,7 +8,14 @@ #include #include /* pid_t */ -#define event_contains(obj, mem) ((obj).header.size > offsetof(typeof(obj), mem)) +/* + * Verify the full field fits within the event, not just its start offset. + * Only valid for fixed-size scalar fields — for trailing arrays like + * filename[PATH_MAX], sizeof() evaluates to the declared maximum, not + * the actual string length, so this would spuriously return false. + */ +#define event_contains(obj, mem) \ + ((obj).header.size >= offsetof(typeof(obj), mem) + sizeof((obj).mem)) struct perf_record_mmap { struct perf_event_header header; diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 31f05f467810..552f063f126e 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -2002,7 +2002,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event, spe->tc.time_mult = tc->time_mult; spe->tc.time_zero = tc->time_zero; - if (event_contains(*tc, time_cycles)) { + if (event_contains(*tc, cap_user_time_short)) { spe->tc.time_cycles = tc->time_cycles; spe->tc.time_mask = tc->time_mask; spe->tc.cap_user_time_zero = tc->cap_user_time_zero; diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 6ec48de29441..40c6ddfa8c8d 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -3514,7 +3514,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, etm->tc.time_shift = tc->time_shift; etm->tc.time_mult = tc->time_mult; etm->tc.time_zero = tc->time_zero; - if (event_contains(*tc, time_cycles)) { + if (event_contains(*tc, cap_user_time_short)) { etm->tc.time_cycles = tc->time_cycles; etm->tc.time_mask = tc->time_mask; etm->tc.cap_user_time_zero = tc->cap_user_time_zero; diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 52e6ffac2b3e..18fd84a82153 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -409,7 +409,7 @@ static uint64_t convert_timestamp(struct jit_buf_desc *jd, uint64_t timestamp) * checks the event size and assigns these extended fields if these * fields are contained in the event. */ - if (event_contains(*time_conv, time_cycles)) { + if (event_contains(*time_conv, cap_user_time_short)) { tc.time_cycles = time_conv->time_cycles; tc.time_mask = time_conv->time_mask; tc.cap_user_time_zero = time_conv->cap_user_time_zero; diff --git a/tools/perf/util/tsc.c b/tools/perf/util/tsc.c index 511a517ce613..ebf289bf6b9d 100644 --- a/tools/perf/util/tsc.c +++ b/tools/perf/util/tsc.c @@ -127,7 +127,7 @@ size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp) * when supported cap_user_time_short, for backward compatibility, * prints the extended fields only if they are contained in the event. */ - if (event_contains(*tc, time_cycles)) { + if (event_contains(*tc, cap_user_time_short)) { ret += fprintf(fp, "... Time Cycles %" PRI_lu64 "\n", tc->time_cycles); ret += fprintf(fp, "... Time Mask %#" PRI_lx64 "\n", From a18908b5056b8fdb2c44505f0c57ff05865740a3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 4 May 2026 18:26:07 -0300 Subject: [PATCH 2162/5214] perf zstd: Fix compression error path in zstd_compress_stream_to_records() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error fallback does memcpy(dst, src, src_size) intending to store uncompressed data when compression fails, but this has three bugs: 1. dst has been advanced past the record header (and potentially past earlier compressed records), so the copy writes to the wrong offset in the output buffer. 2. src still points to the start of the input, not to the remaining uncompressed data at src + input.pos. On a second or later iteration, previously compressed data would be duplicated. 3. No check that dst_size >= src_size — if the remaining output space is smaller, this is an out-of-bounds write. Replace with return -1 after resetting the ZSTD compression context via ZSTD_initCStream(). The -1 propagates through zstd_compress() -> record__pushfn() -> perf_mmap__push() to the recording loop, which breaks out and terminates recording. Add an out_child_no_flush label in __cmd_record() so the mmap-read failure path skips the final record__mmap_read_all() flush — retrying the same read that just failed would just fail again, and the flush is only useful when the mmap data is intact but the control path (auxtrace, switch_output) had an error. Consolidate all error paths through a single 'reset' label to ensure the compression context is always reset on failure — including the output-buffer-full path, where a bare return without resetting would leave stale stream state that corrupts output if the caller retries. Also guard against process_header() writing the event header before the buffer-full check: add a sizeof(perf_event_header) pre-check so the callback never writes past the output buffer. Guard against ZSTD making no progress: if output.pos is zero after ZSTD_compressStream(), calling process_header(record, 0) would re-trigger header initialization, double-subtracting the header size from dst_size and underflowing the unsigned counter. Also fix two pre-existing issues in the same function: - Add a dst_size guard before subtracting the record header size: if the output buffer is nearly full, the unsigned dst_size -= size underflows to a huge value, causing ZSTD_compressStream to write past the buffer boundary. - Check the ZSTD_initCStream() return value and log an error if the context reset itself fails. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 6 +++++- tools/perf/util/zstd.c | 27 +++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4a30688699d5..a33c78f030d9 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -2744,7 +2744,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) trigger_error(&auxtrace_snapshot_trigger); trigger_error(&switch_output_trigger); err = -1; - goto out_child; + goto out_child_no_flush; } if (auxtrace_record__snapshot_started) { @@ -2891,6 +2891,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) out_child: record__stop_threads(rec); record__mmap_read_all(rec, true); + goto out_free_threads; +out_child_no_flush: + /* mmap read already failed — retrying would just fail again */ + record__stop_threads(rec); out_free_threads: record__free_thread_data(rec); evlist__finalize_ctlfd(rec->evlist); diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c index 57027e0ac7b6..ecda9deb53b7 100644 --- a/tools/perf/util/zstd.c +++ b/tools/perf/util/zstd.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include "util/compress.h" #include "util/debug.h" @@ -54,7 +55,13 @@ ssize_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_ while (input.pos < input.size) { record = dst; + /* process_header writes the event header into record */ + if (dst_size < sizeof(struct perf_event_header)) + goto reset; size = process_header(record, 0); + /* Output buffer full — cannot fit even the record header */ + if (size > dst_size) + goto reset; compressed += size; dst += size; dst_size -= size; @@ -65,10 +72,18 @@ ssize_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_ if (ZSTD_isError(ret)) { pr_err("failed to compress %ld bytes: %s\n", (long)src_size, ZSTD_getErrorName(ret)); - memcpy(dst, src, src_size); - return src_size; + goto reset; } size = output.pos; + /* + * No progress: ZSTD couldn't emit any bytes into the + * remaining output buffer. Calling process_header + * with size=0 would re-trigger header initialization, + * double-subtracting the header size from dst_size and + * underflowing the unsigned counter. + */ + if (size == 0) + goto reset; size = process_header(record, size); compressed += size; dst += size; @@ -76,6 +91,14 @@ ssize_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_ } return compressed; + +reset: + /* Reset so the context is usable if the caller retries */ + ret = ZSTD_initCStream(data->cstream, data->comp_level); + if (ZSTD_isError(ret)) + pr_err("failed to reset compression context: %s\n", + ZSTD_getErrorName(ret)); + return -1; } size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size, From 3ec93875fda94e7f4c466855825c860a2e0ceac2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 19:46:21 -0300 Subject: [PATCH 2163/5214] perf zstd: Fix multi-iteration decompression and error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zstd_decompress_stream() has two bugs in its multi-iteration loop: 1. After each ZSTD_decompressStream() call, the code advances output.dst by output.pos but doesn't reset output.pos to 0. ZSTD interprets output.pos relative to output.dst, so the next iteration writes at (dst + pos) + pos = dst + 2*pos, skipping a gap and potentially writing out of bounds. 2. On ZSTD_decompressStream() error, the loop executes break and returns output.pos (which is > 0 if some bytes were decompressed before the error). The caller checks !decomp_size and skips the error, silently accepting truncated or corrupted data. Fix both by removing the output buffer adjustment — ZSTD correctly accumulates output.pos across calls without it. Return 0 on decompression error so the caller detects it. Add a no-progress guard to prevent infinite loops if the output buffer fills before all input is consumed. Note: the compressed event data_size is validated against header.size by a subsequent patch in this series ("perf tools: Harden compressed event processing"). Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/zstd.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c index ecda9deb53b7..21a0eb58597c 100644 --- a/tools/perf/util/zstd.c +++ b/tools/perf/util/zstd.c @@ -123,14 +123,26 @@ size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size } } while (input.pos < input.size) { + size_t prev_in = input.pos; + size_t prev_out = output.pos; + ret = ZSTD_decompressStream(data->dstream, &output, &input); if (ZSTD_isError(ret)) { pr_err("failed to decompress (B): %zd -> %zd, dst_size %zd : %s\n", - src_size, output.size, dst_size, ZSTD_getErrorName(ret)); - break; + src_size, output.pos, dst_size, ZSTD_getErrorName(ret)); + return 0; } - output.dst = dst + output.pos; - output.size = dst_size - output.pos; + /* + * Neither stream advanced — decompression is stuck. + * Return 0 (error) rather than partial output: perf + * uses ZSTD_flushStream (not ZSTD_endStream), so the + * stream is continuous across compressed events. + * Discarding unconsumed input would desynchronize the + * decompressor, causing the next call to produce + * garbage that could be misinterpreted as valid events. + */ + if (input.pos == prev_in && output.pos == prev_out) + return 0; } return output.pos; From fe84caf425f9d404833dc165eb60efbd0fc54e43 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 12:57:48 -0300 Subject: [PATCH 2164/5214] perf session: Fix PERF_RECORD_READ swap and dump for variable-length events The kernel dynamically sizes PERF_RECORD_READ based on attr.read_format: only the fields enabled by PERF_FORMAT_TOTAL_TIME_ENABLED, PERF_FORMAT_TOTAL_TIME_RUNNING, PERF_FORMAT_ID, and PERF_FORMAT_LOST are emitted, packed with no gaps. perf_event__read_swap() unconditionally byte-swapped time_enabled, time_running, and id at their fixed struct offsets, causing out-of-bounds access on smaller events and swapping the wrong bytes when not all format fields are present. It also swapped sample_id_all at a fixed offset past the full struct, which is wrong for shorter events. Replace the individual field swaps with a single mem_bswap_64() over the entire tail from value onward. Since every field after pid/tid is u64 regardless of which combination is present, this correctly handles any read_format combination and any trailing sample_id_all fields. Similarly, dump_read() accessed optional fields via fixed struct offsets, displaying values from wrong positions when not all format bits are set. Walk the packed u64 array sequentially instead, with bounds checks against event->header.size. Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 61 ++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c4cd8ad6d810..24f2ba599b80 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -354,17 +354,24 @@ static void perf_event__task_swap(union perf_event *event, bool sample_id_all) swap_sample_id_all(event, &event->fork + 1); } -static void perf_event__read_swap(union perf_event *event, bool sample_id_all) +static void perf_event__read_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { + size_t tail; + event->read.pid = bswap_32(event->read.pid); event->read.tid = bswap_32(event->read.tid); - event->read.value = bswap_64(event->read.value); - event->read.time_enabled = bswap_64(event->read.time_enabled); - event->read.time_running = bswap_64(event->read.time_running); - event->read.id = bswap_64(event->read.id); - - if (sample_id_all) - swap_sample_id_all(event, &event->read + 1); + /* + * Everything after pid/tid is u64: the read values (variable + * set determined by attr.read_format, which we don't have + * here) optionally followed by sample_id_all fields. + * Since all are u64, swap the entire remaining tail at once. + */ + tail = event->header.size - offsetof(struct perf_record_read, value); + /* mem_bswap_64 rounds up to 8-byte chunks — unaligned tail overruns the buffer */ + if (tail % sizeof(u64)) + return; + mem_bswap_64(&event->read.value, tail); } static void perf_event__aux_swap(union perf_event *event, bool sample_id_all) @@ -1200,8 +1207,9 @@ static void dump_deferred_callchain(union perf_event *event, struct perf_sample static void dump_read(struct evsel *evsel, union perf_event *event) { - struct perf_record_read *read_event = &event->read; u64 read_format; + __u64 *array; + void *end; if (!dump_trace) return; @@ -1213,18 +1221,37 @@ static void dump_read(struct evsel *evsel, union perf_event *event) return; read_format = evsel->core.attr.read_format; + /* + * The kernel packs only the enabled read_format fields + * after value, with no gaps. Walk the packed array + * instead of using fixed struct offsets. + */ + array = &event->read.value + 1; + end = (void *)event + event->header.size; - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - printf("... time enabled : %" PRI_lu64 "\n", read_event->time_enabled); + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { + if ((void *)(array + 1) > end) + return; + printf("... time enabled : %" PRI_lu64 "\n", *array++); + } - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - printf("... time running : %" PRI_lu64 "\n", read_event->time_running); + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { + if ((void *)(array + 1) > end) + return; + printf("... time running : %" PRI_lu64 "\n", *array++); + } - if (read_format & PERF_FORMAT_ID) - printf("... id : %" PRI_lu64 "\n", read_event->id); + if (read_format & PERF_FORMAT_ID) { + if ((void *)(array + 1) > end) + return; + printf("... id : %" PRI_lu64 "\n", *array++); + } - if (read_format & PERF_FORMAT_LOST) - printf("... lost : %" PRI_lu64 "\n", read_event->lost); + if (read_format & PERF_FORMAT_LOST) { + if ((void *)(array + 1) > end) + return; + printf("... lost : %" PRI_lu64 "\n", *array++); + } } static struct machine *machines__find_for_cpumode(struct machines *machines, From 315c81b4b971b6e92cb47c990fb4fbaa14f0386c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:01:34 -0300 Subject: [PATCH 2165/5214] perf session: Fix swap_sample_id_all() crash on crafted events swap_sample_id_all() calls BUG_ON(size % sizeof(u64)) which kills perf on any event where the sample_id_all tail is not 8-byte aligned. A crafted perf.data can trigger this trivially. Replace BUG_ON with a bounds check: skip the swap if the data pointer is past the end of the event, and only swap when there are bytes remaining. Note: the strlen calls in string-field swap handlers (comm, mmap, mmap2, cgroup) are replaced with bounded strnlen by the next patch in this series ("perf session: Add validated swap infrastructure with null-termination checks"). Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 24f2ba599b80..37544a357418 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -276,10 +276,18 @@ void perf_session__delete(struct perf_session *session) static void swap_sample_id_all(union perf_event *event, void *data) { void *end = (void *) event + event->header.size; - int size = end - data; + int size; - BUG_ON(size % sizeof(u64)); - mem_bswap_64(data, size); + if (data >= end) + return; + + size = end - data; + if (size % sizeof(u64)) { + pr_warning("swap_sample_id_all: unaligned sample_id_all remainder (%d), skipping swap\n", size); + return; + } + if (size > 0) + mem_bswap_64(data, size); } static void perf_event__all64_swap(union perf_event *event, From 981f13ae6255fe8e9e68ca6435cd8194446460ec Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 12:47:46 -0300 Subject: [PATCH 2166/5214] perf session: Add validated swap infrastructure with null-termination checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change swap callbacks from void to int return so handlers can propagate errors. All 28 existing handlers are converted to return 0 on success, -1 on error. Three new handlers (KSYMBOL, BPF_EVENT, HEADER_FEATURE) are added returning int from the start, with sample_id_all handling for the kernel event types. event_swap() propagates the return to its callers (process_event and peek_event), which skip events that fail to swap. Add perf_event__check_nul() for null-termination enforcement on the common event delivery path for MMAP, MMAP2, COMM, CGROUP, and KSYMBOL events. Events with unterminated strings are skipped — native-endian files are mapped read-only, so writing a NUL byte in place would segfault. Swap handler hardening: - Use strnlen bounded by event size (instead of strlen) in COMM/MMAP/MMAP2/CGROUP swap handlers, returning -1 on unterminated strings. - Bounds check text_poke old_len+new_len before computing the sample_id offset, returning -1 on overflow. Use offsetof() for the native-path check in machines__deliver_event() since sizeof() includes struct padding past the flexible array. - Fix PERF_RECORD_SWITCH sample_id_all: non-CPU_WIDE SWITCH events have sample_id immediately after the 8-byte header, not at sizeof(struct perf_record_switch) which is the CPU_WIDE variant size. - Fix perf_event__time_conv_swap(): decouple time_cycles and time_mask into independent per-field event_contains() checks, so each field is only swapped when the event is large enough to contain it. The original code guarded both fields under a single time_cycles check, which would swap time_mask on a short event that contains time_cycles but not time_mask. - Handle ABI0 (attr.size == 0) in perf_event__attr_swap() by substituting PERF_ATTR_SIZE_VER0, so bswap_safe() correctly swaps VER0 fields instead of skipping everything. - peek_events: on swap failure, advance past the malformed entry instead of aborting the loop. Note: the nr-field bounds checks for namespaces, thread_map, cpu_map, and stat_config arrays are added by a subsequent patch ("perf session: Validate nr fields against event size on both swap and common paths"). The HEADER_ATTR attr.size validation is added by ("perf session: Validate HEADER_ATTR attr.size before swapping"). By establishing the int-returning swap infrastructure first, all subsequent hardening patches can use direct error returns from day one — no poison values, no workarounds for void return. Changes in v2: - peek_events: abort instead of skip for AUXTRACE events on validation failure — skipping only header.size would land inside the raw trace payload, causing subsequent iterations to misparse data as events (Reported-by: sashiko-bot@kernel.org) Fixes: 9aa0bfa370b2 ("perf tools: Handle PERF_RECORD_KSYMBOL") Fixes: 45178a928a4b ("perf tools: Handle PERF_RECORD_BPF_EVENT") Fixes: e9def1b2e74e ("perf tools: Add feature header record to pipe-mode") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: David Carrillo-Cisneros Cc: Jiri Olsa Cc: Namhyung Kim Cc: Song Liu Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 406 ++++++++++++++++++++++++++++++-------- 1 file changed, 325 insertions(+), 81 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 37544a357418..d5864e380c1b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -290,28 +290,44 @@ static void swap_sample_id_all(union perf_event *event, void *data) mem_bswap_64(data, size); } -static void perf_event__all64_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__all64_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { struct perf_event_header *hdr = &event->header; - mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); + size_t size = event->header.size - sizeof(*hdr); + + /* mem_bswap_64 rounds up to 8-byte chunks — unaligned size overruns the buffer */ + if (size % sizeof(u64)) + return -1; + mem_bswap_64(hdr + 1, size); + return 0; } -static void perf_event__comm_swap(union perf_event *event, bool sample_id_all) +static int perf_event__comm_swap(union perf_event *event, bool sample_id_all) { event->comm.pid = bswap_32(event->comm.pid); event->comm.tid = bswap_32(event->comm.tid); if (sample_id_all) { void *data = &event->comm.comm; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); - data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); + /* + * No NUL within the event boundary — can't locate where + * sample_id_all starts. Reject so the event is skipped + * rather than swapping garbage. + */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); swap_sample_id_all(event, data); } + return 0; } -static void perf_event__mmap_swap(union perf_event *event, - bool sample_id_all) +static int perf_event__mmap_swap(union perf_event *event, + bool sample_id_all) { event->mmap.pid = bswap_32(event->mmap.pid); event->mmap.tid = bswap_32(event->mmap.tid); @@ -321,13 +337,19 @@ static void perf_event__mmap_swap(union perf_event *event, if (sample_id_all) { void *data = &event->mmap.filename; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); - data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); + /* See comment in perf_event__comm_swap() */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); swap_sample_id_all(event, data); } + return 0; } -static void perf_event__mmap2_swap(union perf_event *event, +static int perf_event__mmap2_swap(union perf_event *event, bool sample_id_all) { event->mmap2.pid = bswap_32(event->mmap2.pid); @@ -345,12 +367,19 @@ static void perf_event__mmap2_swap(union perf_event *event, if (sample_id_all) { void *data = &event->mmap2.filename; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); - data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); + /* See comment in perf_event__comm_swap() */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); swap_sample_id_all(event, data); } + return 0; } -static void perf_event__task_swap(union perf_event *event, bool sample_id_all) + +static int perf_event__task_swap(union perf_event *event, bool sample_id_all) { event->fork.pid = bswap_32(event->fork.pid); event->fork.tid = bswap_32(event->fork.tid); @@ -360,10 +389,11 @@ static void perf_event__task_swap(union perf_event *event, bool sample_id_all) if (sample_id_all) swap_sample_id_all(event, &event->fork + 1); + return 0; } -static void perf_event__read_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__read_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { size_t tail; @@ -378,11 +408,12 @@ static void perf_event__read_swap(union perf_event *event, tail = event->header.size - offsetof(struct perf_record_read, value); /* mem_bswap_64 rounds up to 8-byte chunks — unaligned tail overruns the buffer */ if (tail % sizeof(u64)) - return; + return -1; mem_bswap_64(&event->read.value, tail); + return 0; } -static void perf_event__aux_swap(union perf_event *event, bool sample_id_all) +static int perf_event__aux_swap(union perf_event *event, bool sample_id_all) { event->aux.aux_offset = bswap_64(event->aux.aux_offset); event->aux.aux_size = bswap_64(event->aux.aux_size); @@ -390,19 +421,21 @@ static void perf_event__aux_swap(union perf_event *event, bool sample_id_all) if (sample_id_all) swap_sample_id_all(event, &event->aux + 1); + return 0; } -static void perf_event__itrace_start_swap(union perf_event *event, - bool sample_id_all) +static int perf_event__itrace_start_swap(union perf_event *event, + bool sample_id_all) { event->itrace_start.pid = bswap_32(event->itrace_start.pid); event->itrace_start.tid = bswap_32(event->itrace_start.tid); if (sample_id_all) swap_sample_id_all(event, &event->itrace_start + 1); + return 0; } -static void perf_event__switch_swap(union perf_event *event, bool sample_id_all) +static int perf_event__switch_swap(union perf_event *event, bool sample_id_all) { if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) { event->context_switch.next_prev_pid = @@ -411,30 +444,45 @@ static void perf_event__switch_swap(union perf_event *event, bool sample_id_all) bswap_32(event->context_switch.next_prev_tid); } - if (sample_id_all) - swap_sample_id_all(event, &event->context_switch + 1); + if (sample_id_all) { + /* + * PERF_RECORD_SWITCH has no fields beyond the header; + * SWITCH_CPU_WIDE adds pid/tid. Use the right offset + * so sample_id starts at the correct position. + */ + if (event->header.type == PERF_RECORD_SWITCH) + swap_sample_id_all(event, (void *)event + sizeof(event->header)); + else + swap_sample_id_all(event, &event->context_switch + 1); + } + return 0; } -static void perf_event__text_poke_swap(union perf_event *event, bool sample_id_all) +static int perf_event__text_poke_swap(union perf_event *event, bool sample_id_all) { event->text_poke.addr = bswap_64(event->text_poke.addr); event->text_poke.old_len = bswap_16(event->text_poke.old_len); event->text_poke.new_len = bswap_16(event->text_poke.new_len); if (sample_id_all) { + void *data = &event->text_poke.old_len; + void *end = (void *)event + event->header.size; size_t len = sizeof(event->text_poke.old_len) + sizeof(event->text_poke.new_len) + event->text_poke.old_len + event->text_poke.new_len; - void *data = &event->text_poke.old_len; + /* old_len + new_len exceeds event — can't find sample_id_all */ + if (data + len > end) + return -1; data += PERF_ALIGN(len, sizeof(u64)); swap_sample_id_all(event, data); } + return 0; } -static void perf_event__throttle_swap(union perf_event *event, - bool sample_id_all) +static int perf_event__throttle_swap(union perf_event *event, + bool sample_id_all) { event->throttle.time = bswap_64(event->throttle.time); event->throttle.id = bswap_64(event->throttle.id); @@ -442,10 +490,11 @@ static void perf_event__throttle_swap(union perf_event *event, if (sample_id_all) swap_sample_id_all(event, &event->throttle + 1); + return 0; } -static void perf_event__namespaces_swap(union perf_event *event, - bool sample_id_all) +static int perf_event__namespaces_swap(union perf_event *event, + bool sample_id_all) { u64 i; @@ -462,18 +511,25 @@ static void perf_event__namespaces_swap(union perf_event *event, if (sample_id_all) swap_sample_id_all(event, &event->namespaces.link_info[i]); + return 0; } -static void perf_event__cgroup_swap(union perf_event *event, bool sample_id_all) +static int perf_event__cgroup_swap(union perf_event *event, bool sample_id_all) { event->cgroup.id = bswap_64(event->cgroup.id); if (sample_id_all) { void *data = &event->cgroup.path; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); - data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); + /* See comment in perf_event__comm_swap() */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); swap_sample_id_all(event, data); } + return 0; } static u8 revbyte(u8 b) @@ -514,9 +570,19 @@ void perf_event__attr_swap(struct perf_event_attr *attr) attr->type = bswap_32(attr->type); attr->size = bswap_32(attr->size); -#define bswap_safe(f, n) \ - (attr->size > (offsetof(struct perf_event_attr, f) + \ - sizeof(attr->f) * (n))) + /* + * ABI0: size == 0 means the producer didn't set it. + * Assume PERF_ATTR_SIZE_VER0 so bswap_safe() below + * correctly swaps the VER0 fields instead of skipping + * everything. Same convention as read_attr(). + */ + if (!attr->size) + attr->size = PERF_ATTR_SIZE_VER0; + +/* Verify the full field extent fits, not just its start offset */ +#define bswap_safe(f, n) \ + (attr->size >= (offsetof(struct perf_event_attr, f) + \ + sizeof(attr->f) * ((n) + 1))) #define bswap_field(f, sz) \ do { \ if (bswap_safe(f, 0)) \ @@ -554,8 +620,8 @@ do { \ #undef bswap_safe } -static void perf_event__hdr_attr_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__hdr_attr_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { size_t size; @@ -564,30 +630,34 @@ static void perf_event__hdr_attr_swap(union perf_event *event, size = event->header.size; size -= perf_record_header_attr_id(event) - (void *)event; mem_bswap_64(perf_record_header_attr_id(event), size); + return 0; } -static void perf_event__event_update_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__event_update_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->event_update.type = bswap_64(event->event_update.type); event->event_update.id = bswap_64(event->event_update.id); + return 0; } -static void perf_event__event_type_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__event_type_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->event_type.event_type.event_id = bswap_64(event->event_type.event_type.event_id); + return 0; } -static void perf_event__tracing_data_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__tracing_data_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->tracing_data.size = bswap_32(event->tracing_data.size); + return 0; } -static void perf_event__auxtrace_info_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__auxtrace_info_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { size_t size; @@ -596,10 +666,11 @@ static void perf_event__auxtrace_info_swap(union perf_event *event, size = event->header.size; size -= (void *)&event->auxtrace_info.priv - (void *)event; mem_bswap_64(event->auxtrace_info.priv, size); + return 0; } -static void perf_event__auxtrace_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__auxtrace_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->auxtrace.size = bswap_64(event->auxtrace.size); event->auxtrace.offset = bswap_64(event->auxtrace.offset); @@ -607,10 +678,11 @@ static void perf_event__auxtrace_swap(union perf_event *event, event->auxtrace.idx = bswap_32(event->auxtrace.idx); event->auxtrace.tid = bswap_32(event->auxtrace.tid); event->auxtrace.cpu = bswap_32(event->auxtrace.cpu); + return 0; } -static void perf_event__auxtrace_error_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__auxtrace_error_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->auxtrace_error.type = bswap_32(event->auxtrace_error.type); event->auxtrace_error.code = bswap_32(event->auxtrace_error.code); @@ -625,10 +697,11 @@ static void perf_event__auxtrace_error_swap(union perf_event *event, event->auxtrace_error.machine_pid = bswap_32(event->auxtrace_error.machine_pid); event->auxtrace_error.vcpu = bswap_32(event->auxtrace_error.vcpu); } + return 0; } -static void perf_event__thread_map_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__thread_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { unsigned i; @@ -636,10 +709,11 @@ static void perf_event__thread_map_swap(union perf_event *event, for (i = 0; i < event->thread_map.nr; i++) event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); + return 0; } -static void perf_event__cpu_map_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__cpu_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { struct perf_record_cpu_map_data *data = &event->cpu_map.data; @@ -677,20 +751,22 @@ static void perf_event__cpu_map_swap(union perf_event *event, default: break; } + return 0; } -static void perf_event__stat_config_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__stat_config_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { u64 size; size = bswap_64(event->stat_config.nr) * sizeof(event->stat_config.data[0]); size += 1; /* nr item itself */ mem_bswap_64(&event->stat_config.nr, size); + return 0; } -static void perf_event__stat_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__stat_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->stat.id = bswap_64(event->stat.id); event->stat.thread = bswap_32(event->stat.thread); @@ -698,44 +774,90 @@ static void perf_event__stat_swap(union perf_event *event, event->stat.val = bswap_64(event->stat.val); event->stat.ena = bswap_64(event->stat.ena); event->stat.run = bswap_64(event->stat.run); + return 0; } -static void perf_event__stat_round_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__stat_round_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->stat_round.type = bswap_64(event->stat_round.type); event->stat_round.time = bswap_64(event->stat_round.time); + return 0; } -static void perf_event__time_conv_swap(union perf_event *event, - bool sample_id_all __maybe_unused) +static int perf_event__time_conv_swap(union perf_event *event, + bool sample_id_all __maybe_unused) { event->time_conv.time_shift = bswap_64(event->time_conv.time_shift); event->time_conv.time_mult = bswap_64(event->time_conv.time_mult); event->time_conv.time_zero = bswap_64(event->time_conv.time_zero); - if (event_contains(event->time_conv, time_cycles)) { + if (event_contains(event->time_conv, time_cycles)) event->time_conv.time_cycles = bswap_64(event->time_conv.time_cycles); + if (event_contains(event->time_conv, time_mask)) event->time_conv.time_mask = bswap_64(event->time_conv.time_mask); - } + return 0; } -static void +static int perf_event__schedstat_cpu_swap(union perf_event *event __maybe_unused, bool sample_id_all __maybe_unused) { /* FIXME */ + return 0; } -static void +static int perf_event__schedstat_domain_swap(union perf_event *event __maybe_unused, bool sample_id_all __maybe_unused) { /* FIXME */ + return 0; } -typedef void (*perf_event__swap_op)(union perf_event *event, - bool sample_id_all); +static int perf_event__ksymbol_swap(union perf_event *event, + bool sample_id_all) +{ + event->ksymbol.addr = bswap_64(event->ksymbol.addr); + event->ksymbol.len = bswap_32(event->ksymbol.len); + event->ksymbol.ksym_type = bswap_16(event->ksymbol.ksym_type); + event->ksymbol.flags = bswap_16(event->ksymbol.flags); + + if (sample_id_all) { + void *data = &event->ksymbol.name; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); + + /* See comment in perf_event__comm_swap() */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); + swap_sample_id_all(event, data); + } + return 0; +} + +static int perf_event__bpf_event_swap(union perf_event *event, + bool sample_id_all) +{ + event->bpf.type = bswap_16(event->bpf.type); + event->bpf.flags = bswap_16(event->bpf.flags); + event->bpf.id = bswap_32(event->bpf.id); + + if (sample_id_all) + swap_sample_id_all(event, &event->bpf + 1); + return 0; +} + +static int perf_event__header_feature_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->feat.feat_id = bswap_64(event->feat.feat_id); + return 0; +} + +typedef int (*perf_event__swap_op)(union perf_event *event, + bool sample_id_all); static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_MMAP] = perf_event__mmap_swap, @@ -755,6 +877,8 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_SWITCH_CPU_WIDE] = perf_event__switch_swap, [PERF_RECORD_NAMESPACES] = perf_event__namespaces_swap, [PERF_RECORD_CGROUP] = perf_event__cgroup_swap, + [PERF_RECORD_KSYMBOL] = perf_event__ksymbol_swap, + [PERF_RECORD_BPF_EVENT] = perf_event__bpf_event_swap, [PERF_RECORD_TEXT_POKE] = perf_event__text_poke_swap, [PERF_RECORD_AUX_OUTPUT_HW_ID] = perf_event__all64_swap, [PERF_RECORD_CALLCHAIN_DEFERRED] = perf_event__all64_swap, @@ -762,6 +886,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, [PERF_RECORD_HEADER_BUILD_ID] = NULL, + [PERF_RECORD_HEADER_FEATURE] = perf_event__header_feature_swap, [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, @@ -1488,6 +1613,25 @@ static int session__flush_deferred_samples(struct perf_session *session, return ret; } +/* + * Return true if the string field is properly null-terminated + * within the event boundary. Native-endian files are mapped + * read-only (MAP_SHARED + PROT_READ) so we cannot write a + * null byte in place; skip the event instead. + */ +static bool perf_event__check_nul(const char *str, const void *end, const char *event_name) +{ + size_t max_len = (const char *)end - str; + + if (max_len == 0 || strnlen(str, max_len) == max_len) { + pr_warning("WARNING: PERF_RECORD_%s: string not null-terminated, skipping event\n", + event_name); + return false; + } + + return true; +} + static int machines__deliver_event(struct machines *machines, struct evlist *evlist, union perf_event *event, @@ -1536,16 +1680,32 @@ static int machines__deliver_event(struct machines *machines, } return evlist__deliver_sample(evlist, tool, event, sample, machine); case PERF_RECORD_MMAP: + if (!perf_event__check_nul(event->mmap.filename, + (void *)event + event->header.size, + "MMAP")) + return 0; return tool->mmap(tool, event, sample, machine); case PERF_RECORD_MMAP2: if (event->header.misc & PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT) ++evlist->stats.nr_proc_map_timeout; + if (!perf_event__check_nul(event->mmap2.filename, + (void *)event + event->header.size, + "MMAP2")) + return 0; return tool->mmap2(tool, event, sample, machine); case PERF_RECORD_COMM: + if (!perf_event__check_nul(event->comm.comm, + (void *)event + event->header.size, + "COMM")) + return 0; return tool->comm(tool, event, sample, machine); case PERF_RECORD_NAMESPACES: return tool->namespaces(tool, event, sample, machine); case PERF_RECORD_CGROUP: + if (!perf_event__check_nul(event->cgroup.path, + (void *)event + event->header.size, + "CGROUP")) + return 0; return tool->cgroup(tool, event, sample, machine); case PERF_RECORD_FORK: return tool->fork(tool, event, sample, machine); @@ -1584,11 +1744,25 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_SWITCH_CPU_WIDE: return tool->context_switch(tool, event, sample, machine); case PERF_RECORD_KSYMBOL: + if (!perf_event__check_nul(event->ksymbol.name, + (void *)event + event->header.size, + "KSYMBOL")) + return 0; return tool->ksymbol(tool, event, sample, machine); case PERF_RECORD_BPF_EVENT: return tool->bpf(tool, event, sample, machine); - case PERF_RECORD_TEXT_POKE: + case PERF_RECORD_TEXT_POKE: { + /* offsetof(bytes), not sizeof — sizeof includes padding past the flexible array */ + size_t text_poke_len = offsetof(struct perf_record_text_poke_event, bytes) + + event->text_poke.old_len + + event->text_poke.new_len; + + if (event->header.size < text_poke_len) { + pr_warning("WARNING: PERF_RECORD_TEXT_POKE: old_len+new_len exceeds event, skipping\n"); + return 0; + } return tool->text_poke(tool, event, sample, machine); + } case PERF_RECORD_AUX_OUTPUT_HW_ID: return tool->aux_output_hw_id(tool, event, sample, machine); case PERF_RECORD_CALLCHAIN_DEFERRED: @@ -1794,12 +1968,28 @@ int perf_session__deliver_synth_attr_event(struct perf_session *session, return perf_session__deliver_synth_event(session, &ev.ev, NULL); } +/* Caller must ensure event->header.type < PERF_RECORD_HEADER_MAX */ +static int event_swap(union perf_event *event, bool sample_id_all) +{ + perf_event__swap_op swap = perf_event__swap_ops[event->header.type]; + + if (swap) + return swap(event, sample_id_all); + return 0; +} + /* * Minimum event sizes indexed by type. Checked before swap and * processing so that both cross-endian and native-endian paths * are protected from accessing fields past the event boundary. * Zero means no minimum beyond the 8-byte header (already * enforced by the reader). + * + * These values represent the smallest event the kernel has ever + * emitted for each type, so they do not reject legitimate legacy + * perf.data files from older kernels. Variable-length events + * use offsetof() to the first variable field; the variable + * content is validated separately (e.g., perf_event__check_nul). */ static const u32 perf_event__min_size[PERF_RECORD_HEADER_MAX] = { /* @@ -1821,7 +2011,9 @@ static const u32 perf_event__min_size[PERF_RECORD_HEADER_MAX] = { [PERF_RECORD_FORK] = sizeof(struct perf_record_fork), /* * The kernel dynamically sizes PERF_RECORD_READ based on - * attr.read_format — the minimum has just pid + tid + value. + * attr.read_format — only the enabled fields are emitted, + * packed with no gaps. The minimum valid event has just + * pid + tid + one u64 value (no optional fields). */ [PERF_RECORD_READ] = offsetof(struct perf_record_read, time_enabled), [PERF_RECORD_MMAP2] = offsetof(struct perf_record_mmap2, filename) + 1, @@ -1844,14 +2036,25 @@ static const u32 perf_event__min_size[PERF_RECORD_HEADER_MAX] = { [PERF_RECORD_AUXTRACE] = sizeof(struct perf_record_auxtrace), [PERF_RECORD_AUXTRACE_ERROR] = offsetof(struct perf_record_auxtrace_error, msg) + 1, [PERF_RECORD_THREAD_MAP] = sizeof(struct perf_record_thread_map), - /* Smallest valid variant is RANGE_CPUS: header(8) + type(2) + range(6) */ + /* + * sizeof(perf_record_cpu_map) is 20 because the outer struct + * isn't packed and GCC adds 2 bytes of trailing padding. + * The smallest valid variant (RANGE_CPUS) is only 16 bytes: + * header(8) + type(2) + range_cpu_data(6). Per-variant + * bounds are checked in the swap handler via payload. + */ [PERF_RECORD_CPU_MAP] = sizeof(struct perf_event_header) + sizeof(__u16) + sizeof(struct perf_record_range_cpu_map), [PERF_RECORD_STAT_CONFIG] = sizeof(struct perf_record_stat_config), [PERF_RECORD_STAT] = sizeof(struct perf_record_stat), [PERF_RECORD_STAT_ROUND] = sizeof(struct perf_record_stat_round), - /* Union inflates sizeof; use fixed header fields as minimum */ + /* + * EVENT_UPDATE has a union whose largest member (cpus) + * inflates sizeof to 40, but SCALE events are only 32 + * and UNIT/NAME events can be even smaller. Use the + * fixed header fields (header + type + id) as minimum. + */ [PERF_RECORD_EVENT_UPDATE] = offsetof(struct perf_record_event_update, scale), [PERF_RECORD_TIME_CONV] = offsetof(struct perf_record_time_conv, time_cycles), [PERF_RECORD_ID_INDEX] = sizeof(struct perf_record_id_index), @@ -1887,14 +2090,6 @@ static bool perf_event__too_small(const union perf_event *event, u32 *min) return false; } -/* Caller must ensure event->header.type < PERF_RECORD_HEADER_MAX */ -static void event_swap(union perf_event *event, bool sample_id_all) -{ - perf_event__swap_op swap = perf_event__swap_ops[event->header.type]; - if (swap) - swap(event, sample_id_all); -} - /* * Read and validate the event at @file_offset. * @@ -2003,8 +2198,16 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, return -1; } - if (session->header.needs_swap) - event_swap(event, evlist__sample_id_all(session->evlist)); + if (session->header.needs_swap && + event_swap(event, evlist__sample_id_all(session->evlist))) { + /* + * The header was already swapped so header.size is + * valid — expose the event so callers can advance + * past this malformed entry instead of aborting. + */ + *event_ptr = event; + return -1; + } if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && evlist__parse_sample(session->evlist, event, sample)) @@ -2022,11 +2225,37 @@ int perf_session__peek_events(struct perf_session *session, u64 offset, int err; do { + event = NULL; err = perf_session__peek_event(session, offset, buf, PERF_SAMPLE_MAX_SIZE, &event, NULL); - if (err) - return err; + if (err) { + /* + * Recoverable error: peek_event returns -1 but + * sets event_ptr when the header was read + * successfully but the event is malformed (too + * small or swap failed). Skip past it using + * header.size — don't invoke the callback since + * type-specific fields may be truncated. + * + * Must abort if: event_ptr is NULL (I/O error), + * size is 0 (can't advance), type is AUXTRACE + * (payload extends beyond header.size), or size + * is unaligned (would misalign all subsequent reads). + * + * Direct callers (auxtrace, cs-etm) treat any + * non-zero return as fatal — only this loop skips. + */ + if (event && event->header.size && + event->header.type != PERF_RECORD_AUXTRACE && + event->header.size % sizeof(u64) == 0) { + offset += event->header.size; + err = 0; + } else { + return err; + } + continue; + } err = cb(session, event, offset, data); if (err) @@ -2109,8 +2338,12 @@ static s64 perf_session__process_event(struct perf_session *session, return 0; } - if (session->header.needs_swap) - event_swap(event, evlist__sample_id_all(evlist)); + if (session->header.needs_swap && + event_swap(event, evlist__sample_id_all(evlist))) { + pr_warning("WARNING: swap failed for %s event, skipping\n", + perf_event__name(event->header.type)); + return 0; + } events_stats__inc(&evlist->stats, event->header.type); @@ -2579,6 +2812,17 @@ reader__mmap(struct reader *rd, struct perf_session *session) char *buf, **mmaps = rd->mmaps; u64 page_offset; + /* + * Native-endian: MAP_SHARED + PROT_READ — the kernel + * guarantees page-level coherence but a concurrent writer + * could modify the file between validation and use. This + * is a theoretical TOCTOU that affects the entire perf.data + * processing pipeline; fixing it would require copying each + * event to a private buffer before processing. + * + * Cross-endian: MAP_PRIVATE + PROT_WRITE — swap handlers + * get a copy-on-write snapshot immune to concurrent writes. + */ mmap_prot = PROT_READ; mmap_flags = MAP_SHARED; From 3ade633da7aa58e2a643c9eb0686c91d7dfa4f4e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 6 May 2026 17:00:50 -0300 Subject: [PATCH 2167/5214] perf session: Use bounded copy for PERF_RECORD_TIME_CONV session->time_conv = event->time_conv copies sizeof(struct perf_record_time_conv) bytes unconditionally, but older kernels emit shorter TIME_CONV events without the time_cycles, time_mask, cap_user_time_zero, and cap_user_time_short fields. For a 32-byte event (the original format), this reads 24 bytes past the event boundary into adjacent mmap'd data. The garbage values end up in session->time_conv and can cause incorrect TSC conversion if cap_user_time_zero happens to be non-zero. Replace the struct assignment with a bounded memcpy capped at event->header.size, zeroing the remainder so extended fields default to off when absent. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d5864e380c1b..3f72b80aac56 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1897,7 +1897,14 @@ static s64 perf_session__process_user_event(struct perf_session *session, err = tool->stat_round(tool, session, event); break; case PERF_RECORD_TIME_CONV: - session->time_conv = event->time_conv; + /* + * Bounded copy: older kernels emit a shorter struct + * without time_cycles/time_mask/cap_user_time_*. + * Zero the rest so extended fields default to off. + */ + memset(&session->time_conv, 0, sizeof(session->time_conv)); + memcpy(&session->time_conv, &event->time_conv, + min((size_t)event->header.size, sizeof(session->time_conv))); err = tool->time_conv(tool, session, event); break; case PERF_RECORD_HEADER_FEATURE: From 6a38b515a5ea7101e8a9e14acf248d14083c632f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:07:27 -0300 Subject: [PATCH 2168/5214] perf session: Validate HEADER_ATTR attr.size before swapping Harden PERF_RECORD_HEADER_ATTR handling against crafted perf.data: - Validate attr.size: must be >= PERF_ATTR_SIZE_VER0, a multiple of sizeof(u64), and fit within the event payload. - Copy only min(attr.size, sizeof(struct perf_event_attr)) bytes into a local attr, zeroing the rest so legacy files don't leak adjacent event data into new fields. - Keep the original attr.size so perf_event__synthesize_attr() uses it for both allocation and ID-array placement. Fix perf_event__synthesize_attr() to use attr->size (not the compiled sizeof) for event allocation and layout, so perf inject correctly re-synthesizes attrs from files recorded by a different perf version. Without this, the ID array destination pointer (computed via perf_record_header_attr_id()) would be inconsistent with the allocation when attr->size differs from sizeof. Also fix the parse-no-sample-id-all test to set attr.size, which is now validated, and improve error handling in read_attr() for short reads and invalid attr sizes. Handle ABI0 pipe/inject events where attr.size is 0: use a local attr_size variable set to PERF_ATTR_SIZE_VER0 for both the bounded copy and ID array position, instead of writing back to the event. Native-endian files may be MAP_SHARED (read-only mmap), so writing to the event buffer would SIGSEGV. The swap path handles ABI0 in perf_event__attr_swap() which writes to the MAP_PRIVATE copy. header.size alignment is now validated centrally in perf_session__process_event() (see "Add minimum event size and alignment validation"). Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 23 ++++-- tools/perf/tests/parse-no-sample-id-all.c | 6 ++ tools/perf/util/header.c | 96 +++++++++++++++++++++-- tools/perf/util/session.c | 31 ++++++++ tools/perf/util/synthetic-events.c | 25 +++++- 5 files changed, 166 insertions(+), 15 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 41a3721a194d..d8cb1f562f69 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -229,6 +229,7 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, struct perf_inject *inject = container_of(tool, struct perf_inject, tool); struct perf_event_attr attr; + u32 raw_attr_size, attr_size; size_t n_ids; u64 *ids; int ret; @@ -244,24 +245,34 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, if (!inject->itrace_synth_opts.set) return perf_event__repipe_synth(tool, event); - if (event->header.size < sizeof(struct perf_event_header) + sizeof(u64)) { + if (event->header.size < sizeof(struct perf_event_header) + PERF_ATTR_SIZE_VER0) { pr_err("Attribute event size %u is too small\n", event->header.size); return -EINVAL; } - if (event->header.size - sizeof(event->header) < event->attr.attr.size) { + /* + * ABI0 pipe/inject events have attr.size == 0; default to + * PERF_ATTR_SIZE_VER0 (the ABI0 footprint) for the bounded + * copy and ID array position. Same pattern as + * perf_event__process_attr() in header.c. + */ + raw_attr_size = event->attr.attr.size; + attr_size = raw_attr_size ?: PERF_ATTR_SIZE_VER0; + + if (raw_attr_size && (raw_attr_size < PERF_ATTR_SIZE_VER0 || + raw_attr_size > event->header.size - sizeof(event->header))) { pr_err("Attribute event size %u is too small for attr.size %u\n", - event->header.size, event->attr.attr.size); + event->header.size, raw_attr_size); return -EINVAL; } memset(&attr, 0, sizeof(attr)); memcpy(&attr, &event->attr.attr, - min_t(size_t, sizeof(attr), (size_t)event->attr.attr.size)); + min_t(size_t, sizeof(attr), attr_size)); - n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size; + n_ids = event->header.size - sizeof(event->header) - attr_size; n_ids /= sizeof(u64); - ids = perf_record_header_attr_id(event); + ids = (void *)&event->attr.attr + attr_size; attr.size = sizeof(struct perf_event_attr); attr.sample_type &= ~PERF_SAMPLE_AUX; diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index 50e68b7d43aa..8ac862c94879 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -82,6 +82,9 @@ static int test__parse_no_sample_id_all(struct test_suite *test __maybe_unused, .type = PERF_RECORD_HEADER_ATTR, .size = sizeof(struct test_attr_event), }, + .attr = { + .size = sizeof(struct perf_event_attr), + }, .id = 1, }; struct test_attr_event event2 = { @@ -89,6 +92,9 @@ static int test__parse_no_sample_id_all(struct test_suite *test __maybe_unused, .type = PERF_RECORD_HEADER_ATTR, .size = sizeof(struct test_attr_event), }, + .attr = { + .size = sizeof(struct perf_event_attr), + }, .id = 2, }; struct perf_record_mmap event3 = { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f30e48eb3fc3..967c3d8ff12c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4770,9 +4770,15 @@ static int read_attr(int fd, struct perf_header *ph, if (sz == 0) { /* assume ABI0 */ sz = PERF_ATTR_SIZE_VER0; + } else if (sz < PERF_ATTR_SIZE_VER0) { + pr_debug("bad attr size %zu, expected at least %d\n", + sz, PERF_ATTR_SIZE_VER0); + errno = EINVAL; + return -1; } else if (sz > our_sz) { pr_debug("file uses a more recent and unsupported ABI" " (%zu bytes extra)\n", sz - our_sz); + errno = EINVAL; return -1; } /* what we have not yet read and that we know about */ @@ -4782,11 +4788,21 @@ static int read_attr(int fd, struct perf_header *ph, ptr += PERF_ATTR_SIZE_VER0; ret = readn(fd, ptr, left); + if (ret <= 0) { + if (ret == 0) + errno = EIO; + return -1; + } } /* read perf_file_section, ids are read in caller */ ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids)); + if (ret <= 0) { + if (ret == 0) + errno = EIO; + return -1; + } - return ret <= 0 ? -1 : 0; + return 0; } #ifdef HAVE_LIBTRACEEVENT @@ -5094,11 +5110,42 @@ int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) { - u32 i, n_ids; + struct perf_event_attr attr; + u32 i, n_ids, raw_attr_size; u64 *ids; + size_t attr_size, copy_size; struct evsel *evsel; struct evlist *evlist = *pevlist; + /* + * HEADER_ATTR event layout (pipe/inject mode): + * + * [header (8 bytes)] [attr (attr_size bytes)] [id0 id1 ... idN] + * |<------------------ header.size --------------------------->| + * + * attr_size varies across perf versions: VER0 = 64 bytes, + * current sizeof(struct perf_event_attr) = larger. A newer + * producer may emit a larger attr than we understand. + * + * attr.size == 0 (ABI0) means the producer didn't set it + * (e.g., bench/inject-buildid, older perf). Treat as VER0. + * + * Require 8-byte alignment so the u64 ID array is aligned + * and attr.size fits cleanly within the payload. + * + * Read attr.size once — the event may be on a shared mmap + * and re-reading could yield a different value. + */ + raw_attr_size = event->attr.attr.size; + if (event->header.size < sizeof(event->header) + PERF_ATTR_SIZE_VER0 || + (raw_attr_size && (raw_attr_size < PERF_ATTR_SIZE_VER0 || + raw_attr_size % sizeof(u64) || + raw_attr_size > event->header.size - sizeof(event->header)))) { + pr_err("PERF_RECORD_HEADER_ATTR: invalid attr.size %u (event size %u, min %d)\n", + raw_attr_size, event->header.size, PERF_ATTR_SIZE_VER0); + return -EINVAL; + } + if (dump_trace) perf_event__fprintf_attr(event, stdout); @@ -5108,13 +5155,46 @@ int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, return -ENOMEM; } - evsel = evsel__new(&event->attr.attr); + /* + * attr_size = footprint of the attr in the event — determines + * where the ID array starts. For ABI0, assume VER0 (64 bytes). + * + * copy_size = how much we copy into our local struct, capped at + * sizeof(attr) so a newer producer's larger attr doesn't + * overflow. Fields beyond copy_size are zeroed. + * + * Do NOT write attr_size back to the event — native-endian + * files use MAP_SHARED (read-only), writing would SIGSEGV. + * The swap path handles ABI0 in perf_event__attr_swap() + * which writes to the writable MAP_PRIVATE copy instead. + */ + attr_size = raw_attr_size ?: PERF_ATTR_SIZE_VER0; + copy_size = min(attr_size, sizeof(attr)); + memcpy(&attr, &event->attr.attr, copy_size); + if (copy_size < sizeof(attr)) + memset((void *)&attr + copy_size, 0, sizeof(attr) - copy_size); + + /* + * Normalize ABI0: the swap path sets attr.size = VER0 on the + * event, but the native path leaves it as 0. Set it on the + * local copy so perf inject re-synthesizes with consistent + * layout regardless of endianness. + */ + attr.size = attr_size; + + evsel = evsel__new(&attr); if (evsel == NULL) return -ENOMEM; evlist__add(evlist, evsel); - n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size; + /* + * IDs occupy the remainder after header + attr. Use attr_size + * (not copy_size) — even if the producer's attr is larger than + * our struct, the IDs start after attr_size bytes in the event. + * Validation above guarantees attr_size <= payload size. + */ + n_ids = event->header.size - sizeof(event->header) - attr_size; n_ids = n_ids / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so @@ -5124,7 +5204,13 @@ int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, if (perf_evsel__alloc_id(&evsel->core, 1, n_ids)) return -ENOMEM; - ids = perf_record_header_attr_id(event); + /* + * Locate IDs at attr_size bytes past the attr start in the + * event. Cannot use perf_record_header_attr_id() — that + * macro reads event->attr.attr.size, which is 0 for ABI0 + * on the native-endian path (no swap handler to fix it up). + */ + ids = (void *)&event->attr.attr + attr_size; for (i = 0; i < n_ids; i++) { perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]); } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3f72b80aac56..aef10d42be35 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -623,8 +623,39 @@ do { \ static int perf_event__hdr_attr_swap(union perf_event *event, bool sample_id_all __maybe_unused) { + u32 attr_size, payload_size; size_t size; + /* + * Validate attr.size (still foreign-endian) before calling + * perf_event__attr_swap(), which uses it via bswap_safe() + * to decide which fields to swap. A crafted attr.size + * larger than the event payload would swap past the event + * boundary and corrupt adjacent memory. + * + * header.size alignment is already validated by + * perf_session__process_event(). The min_size table + * guarantees header.size >= sizeof(header) + + * PERF_ATTR_SIZE_VER0, so attr.size is safe to access. + */ + attr_size = bswap_32(event->attr.attr.size); + /* + * ABI0: size field not set. This only happens in pipe/inject + * mode where HEADER_ATTR events carry their own attr. For + * regular perf.data files, read_attr() uses f_header.attr_size + * from the file header instead. Assume PERF_ATTR_SIZE_VER0. + */ + if (!attr_size) + attr_size = PERF_ATTR_SIZE_VER0; + payload_size = event->header.size - sizeof(event->header); + + if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size % sizeof(u64) || + attr_size > payload_size) { + pr_err("PERF_RECORD_HEADER_ATTR: invalid attr.size %u (min: %d, max: %u, 8-byte aligned)\n", + attr_size, PERF_ATTR_SIZE_VER0, payload_size); + return -1; + } + perf_event__attr_swap(&event->attr.attr); size = event->header.size; diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index d665b0f94b32..5307d707711d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2181,11 +2181,21 @@ int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_ u32 ids, u64 *id, perf_event__handler_t process) { union perf_event *ev; - size_t size; + size_t attr_size, size; int err; - size = sizeof(struct perf_event_attr); - size = PERF_ALIGN(size, sizeof(u64)); + /* + * Use attr->size for the event layout, not the compiled + * sizeof(struct perf_event_attr), so that synthesized events + * match the source perf.data layout. This matters for perf + * inject, which re-synthesizes attrs from a file that may + * have been recorded by a different version of perf. + * perf_record_header_attr_id() locates the ID array at + * attr->size bytes past the attr. + */ + attr_size = attr->size ?: sizeof(struct perf_event_attr); + + size = PERF_ALIGN(attr_size, sizeof(u64)); size += sizeof(struct perf_event_header); size += ids * sizeof(u64); @@ -2194,7 +2204,14 @@ int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_ if (ev == NULL) return -ENOMEM; - ev->attr.attr = *attr; + /* + * Copy only the bytes we understand; zalloc ensures that any + * extra bytes between sizeof(struct perf_event_attr) and + * attr_size are zero when the source file uses a newer, larger + * struct. + */ + memcpy(&ev->attr.attr, attr, min(sizeof(struct perf_event_attr), attr_size)); + ev->attr.attr.size = attr_size; memcpy(perf_record_header_attr_id(ev), id, ids * sizeof(u64)); ev->attr.header.type = PERF_RECORD_HEADER_ATTR; From 5682175baf39cddefe6697ee3faed05928c26364 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:14:06 -0300 Subject: [PATCH 2169/5214] perf session: Validate nr fields against event size on both swap and common paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several event types use an nr field to control iteration over variable-length arrays. The swap handlers byte-swap and loop using these fields without bounds checks, and the native processing path trusts them as well. Add bounds checks on both paths for: - PERF_RECORD_THREAD_MAP: validate nr against payload, return -1 on the swap path. On the native path, reject with -EINVAL. - PERF_RECORD_NAMESPACES: clamp nr on the swap path (safe because each entry is indexed by type; missing entries just won't be resolved). Skip the event on the native path. - PERF_RECORD_CPU_MAP: clamp nr for CPUS and MASK sub-types on the swap path. Add bounds checks for mask64 which previously had no nr validation. Skip the event on the native path. - PERF_RECORD_STAT_CONFIG: clamp nr on the swap path (safe because each config entry is self-describing via its tag). Skip the event on the native path. The swap path (cross-endian, writable MAP_PRIVATE mapping) can safely clamp by writing back to the event. The native path (read-only MAP_SHARED mapping) must skip instead of clamping because writing to the mmap'd event would segfault. Also fix stat_config swap range: change size += 1 to size += sizeof(event->stat_config.nr) for clarity. The old +1 happened to work because mem_bswap_64 processes 8-byte chunks, but the intent is to include the 8-byte nr field in the swap range. Changes in v2: - Document that PERF_RECORD_NAMESPACES max_nr includes trailing sample_id space when sample_id_all is present — harmless on the swap path because both per-element bswap_64 and swap_sample_id_all() perform the same u64 byte swap (Reported-by: sashiko-bot@kernel.org) Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 253 +++++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 19 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index aef10d42be35..8588e12f110f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -496,13 +496,35 @@ static int perf_event__throttle_swap(union perf_event *event, static int perf_event__namespaces_swap(union perf_event *event, bool sample_id_all) { - u64 i; + u64 i, nr, max_nr; event->namespaces.pid = bswap_32(event->namespaces.pid); event->namespaces.tid = bswap_32(event->namespaces.tid); event->namespaces.nr_namespaces = bswap_64(event->namespaces.nr_namespaces); - for (i = 0; i < event->namespaces.nr_namespaces; i++) { + nr = event->namespaces.nr_namespaces; + /* + * Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof. + * When sample_id_all is present max_nr slightly overestimates the + * array space because header.size includes the trailing sample_id. + * Harmless: both the per-element bswap_64 loop and swap_sample_id_all() + * perform the same u64 byte swap, so the result is correct regardless + * of where the boundary between array and sample_id falls. + */ + max_nr = (event->header.size - sizeof(event->namespaces)) / + sizeof(event->namespaces.link_info[0]); + /* + * Safe to clamp: each namespace entry is indexed by type; + * missing entries just won't be resolved. + */ + if (nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), clamping\n", + nr, max_nr); + nr = max_nr; + event->namespaces.nr_namespaces = nr; + } + + for (i = 0; i < nr; i++) { struct perf_ns_link_info *ns = &event->namespaces.link_info[i]; ns->dev = bswap_64(ns->dev); @@ -734,11 +756,23 @@ static int perf_event__auxtrace_error_swap(union perf_event *event, static int perf_event__thread_map_swap(union perf_event *event, bool sample_id_all __maybe_unused) { - unsigned i; + unsigned int i; + u64 nr; event->thread_map.nr = bswap_64(event->thread_map.nr); - for (i = 0; i < event->thread_map.nr; i++) + /* + * Reject rather than clamp: unlike namespaces (indexed by type) + * or stat_config (self-describing tags), a truncated thread map + * is structurally broken — downstream would get a wrong map. + */ + /* Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof */ + nr = event->thread_map.nr; + if (nr > (event->header.size - sizeof(event->thread_map)) / + sizeof(event->thread_map.entries[0])) + return -1; + + for (i = 0; i < nr; i++) event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); return 0; } @@ -747,32 +781,80 @@ static int perf_event__cpu_map_swap(union perf_event *event, bool sample_id_all __maybe_unused) { struct perf_record_cpu_map_data *data = &event->cpu_map.data; + u32 payload = event->header.size - sizeof(event->header); data->type = bswap_16(data->type); + /* + * Safe to clamp: a shorter CPU map just means some CPUs + * are absent; tools process the CPUs that are present. + */ switch (data->type) { - case PERF_CPU_MAP__CPUS: - data->cpus_data.nr = bswap_16(data->cpus_data.nr); + case PERF_CPU_MAP__CPUS: { + u16 nr, max_nr; - for (unsigned i = 0; i < data->cpus_data.nr; i++) + data->cpus_data.nr = bswap_16(data->cpus_data.nr); + nr = data->cpus_data.nr; + max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + cpus_data.cpu)) / + sizeof(data->cpus_data.cpu[0]); + if (nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), clamping\n", + nr, max_nr); + nr = max_nr; + data->cpus_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) data->cpus_data.cpu[i] = bswap_16(data->cpus_data.cpu[i]); break; + } case PERF_CPU_MAP__MASK: data->mask32_data.long_size = bswap_16(data->mask32_data.long_size); switch (data->mask32_data.long_size) { - case 4: + case 4: { + u16 nr, max_nr; + data->mask32_data.nr = bswap_16(data->mask32_data.nr); - for (unsigned i = 0; i < data->mask32_data.nr; i++) + nr = data->mask32_data.nr; + max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + mask32_data.mask)) / + sizeof(data->mask32_data.mask[0]); + if (nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), clamping\n", + nr, max_nr); + nr = max_nr; + data->mask32_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) data->mask32_data.mask[i] = bswap_32(data->mask32_data.mask[i]); break; - case 8: + } + case 8: { + u16 nr, max_nr; + data->mask64_data.nr = bswap_16(data->mask64_data.nr); - for (unsigned i = 0; i < data->mask64_data.nr; i++) + nr = data->mask64_data.nr; + if (payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) { + data->mask64_data.nr = 0; + break; + } + max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + mask64_data.mask)) / + sizeof(data->mask64_data.mask[0]); + if (nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), clamping\n", + nr, max_nr); + nr = max_nr; + data->mask64_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) data->mask64_data.mask[i] = bswap_64(data->mask64_data.mask[i]); break; + } default: - pr_err("cpu_map swap: unsupported long size\n"); + pr_err("cpu_map swap: unsupported long size %u\n", + data->mask32_data.long_size); } break; case PERF_CPU_MAP__RANGE_CPUS: @@ -788,11 +870,27 @@ static int perf_event__cpu_map_swap(union perf_event *event, static int perf_event__stat_config_swap(union perf_event *event, bool sample_id_all __maybe_unused) { - u64 size; + u64 nr, max_nr, size; - size = bswap_64(event->stat_config.nr) * sizeof(event->stat_config.data[0]); - size += 1; /* nr item itself */ + nr = bswap_64(event->stat_config.nr); + /* Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof */ + max_nr = (event->header.size - sizeof(event->stat_config)) / + sizeof(event->stat_config.data[0]); + /* + * Safe to clamp: each config entry is self-describing + * via its tag; missing entries keep their defaults. + */ + if (nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), clamping\n", + nr, max_nr); + nr = max_nr; + } + size = nr * sizeof(event->stat_config.data[0]); + /* The swap starts at &nr, so add its size to cover the full range */ + size += sizeof(event->stat_config.nr); mem_bswap_64(&event->stat_config.nr, size); + /* Persist the clamped value in native byte order */ + event->stat_config.nr = nr; return 0; } @@ -1730,8 +1828,27 @@ static int machines__deliver_event(struct machines *machines, "COMM")) return 0; return tool->comm(tool, event, sample, machine); - case PERF_RECORD_NAMESPACES: + case PERF_RECORD_NAMESPACES: { + /* + * Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof. + * Includes trailing sample_id space when present, but prevents OOB. + */ + u64 max_nr = (event->header.size - sizeof(event->namespaces)) / + sizeof(event->namespaces.link_info[0]); + + /* + * Native-endian events are mmap'd read-only, so we + * cannot clamp nr in place. Skip the event instead. + * The swap handler already clamps on the writable + * cross-endian path. + */ + if (event->namespaces.nr_namespaces > max_nr) { + pr_warning("WARNING: PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", + (u64)event->namespaces.nr_namespaces, max_nr); + return 0; + } return tool->namespaces(tool, event, sample, machine); + } case PERF_RECORD_CGROUP: if (!perf_event__check_nul(event->cgroup.path, (void *)event + event->header.size, @@ -1912,15 +2029,112 @@ static s64 perf_session__process_user_event(struct perf_session *session, perf_session__auxtrace_error_inc(session, event); err = tool->auxtrace_error(tool, session, event); break; - case PERF_RECORD_THREAD_MAP: + case PERF_RECORD_THREAD_MAP: { + u64 max_nr; + + if (event->header.size < sizeof(event->thread_map)) { + pr_err("PERF_RECORD_THREAD_MAP: header.size (%u) too small\n", + event->header.size); + err = -EINVAL; + break; + } + + max_nr = (event->header.size - sizeof(event->thread_map)) / + sizeof(event->thread_map.entries[0]); + if (event->thread_map.nr > max_nr) { + pr_err("PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n", + (u64)event->thread_map.nr, max_nr); + err = -EINVAL; + break; + } + err = tool->thread_map(tool, session, event); break; - case PERF_RECORD_CPU_MAP: + } + case PERF_RECORD_CPU_MAP: { + struct perf_record_cpu_map_data *data = &event->cpu_map.data; + u32 payload = event->header.size - sizeof(event->header); + + /* + * Native-endian events are mmap'd read-only, so we + * cannot clamp nr fields in place. Skip the event + * if any variant overflows. + */ + switch (data->type) { + case PERF_CPU_MAP__CPUS: { + u16 max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + cpus_data.cpu)) / + sizeof(data->cpus_data.cpu[0]); + + if (data->cpus_data.nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), skipping\n", + data->cpus_data.nr, max_nr); + err = 0; + goto out; + } + break; + } + case PERF_CPU_MAP__MASK: + if (data->mask32_data.long_size == 4) { + u16 max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + mask32_data.mask)) / + sizeof(data->mask32_data.mask[0]); + + if (data->mask32_data.nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), skipping\n", + data->mask32_data.nr, max_nr); + err = 0; + goto out; + } + } else if (data->mask64_data.long_size == 8) { + u16 max_nr; + + if (payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) { + err = 0; + goto out; + } + max_nr = (payload - offsetof(struct perf_record_cpu_map_data, + mask64_data.mask)) / + sizeof(data->mask64_data.mask[0]); + if (data->mask64_data.nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), skipping\n", + data->mask64_data.nr, max_nr); + err = 0; + goto out; + } + } else { + pr_warning("WARNING: PERF_RECORD_CPU_MAP: unsupported long_size %u, skipping\n", + data->mask32_data.long_size); + err = 0; + goto out; + } + break; + default: + break; + } + err = tool->cpu_map(tool, session, event); break; - case PERF_RECORD_STAT_CONFIG: + } + case PERF_RECORD_STAT_CONFIG: { + /* Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof */ + u64 max_nr = (event->header.size - sizeof(event->stat_config)) / + sizeof(event->stat_config.data[0]); + + /* + * Native-endian events are mmap'd read-only, so we + * cannot clamp nr in place. Skip the event instead. + */ + if (event->stat_config.nr > max_nr) { + pr_warning("WARNING: PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", + (u64)event->stat_config.nr, max_nr); + err = 0; + goto out; + } + err = tool->stat_config(tool, session, event); break; + } case PERF_RECORD_STAT: err = tool->stat(tool, session, event); break; @@ -1963,6 +2177,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, err = -EINVAL; break; } +out: perf_sample__exit(&sample); return err; } From 23d6be944edd59f2e0a9a2a186388c0c58e85416 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 12:53:52 -0300 Subject: [PATCH 2170/5214] perf header: Byte-swap build ID event pid and bounds check section entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf_header__read_build_ids() swaps the event header fields for cross-endian perf.data files but not bev.pid. This causes perf_session__findnew_machine() to look up the wrong machine for guest VM build IDs, misattributing them. Swap bev.pid alongside the header fields. Also add a build_id_swap callback for stream-mode build ID events, and validate NUL-termination of build_id.filename on the native-endian delivery path (perf_session__process_user_event) — events with unterminated filenames are skipped. Harden perf_header__read_build_ids() against crafted perf.data files: - Add overflow check on offset + size to prevent wrap past ULLONG_MAX. - Reject bev.header.size == 0 which would loop forever. - Reject bev.header.size > remaining section to prevent reading past the section boundary. - Guard memcmp(filename, "nel.kallsyms]", 13) with len >= 13 to avoid reading uninitialized stack memory on short filenames. - Force NUL-termination of filename before passing it to functions like machine__findnew_dso() that use strlen/strcmp. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 50 +++++++++++++++++++++++++++++++++++---- tools/perf/util/session.c | 27 ++++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 967c3d8ff12c..c0b5c99f462a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include "string2.h" #include #include @@ -2578,7 +2579,13 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, } old_bev; struct perf_record_header_build_id bev; char filename[PATH_MAX]; - u64 limit = offset + size; + u64 limit; + + /* Prevent offset + size from wrapping past ULLONG_MAX */ + if (size > ULLONG_MAX - offset) + return -1; + + limit = offset + size; while (offset < limit) { ssize_t len; @@ -2589,6 +2596,10 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, if (header->needs_swap) perf_event_header__bswap(&old_bev.header); + /* size == 0 loops forever; size > remaining reads past section */ + if (old_bev.header.size == 0 || old_bev.header.size > limit - offset) + return -1; + len = old_bev.header.size - sizeof(old_bev); if (len < 0 || len >= PATH_MAX) { pr_warning("invalid build_id filename length %zd\n", len); @@ -2597,6 +2608,13 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, if (readn(input, filename, len) != len) return -1; + /* + * The file data may lack a null terminator, which could + * indicate a corrupt or crafted perf.data file. Ensure + * filename is always a valid C string before passing it + * to functions like machine__findnew_dso(). + */ + filename[len] = '\0'; bev.header = old_bev.header; @@ -2624,17 +2642,32 @@ static int perf_header__read_build_ids(struct perf_header *header, struct perf_session *session = container_of(header, struct perf_session, header); struct perf_record_header_build_id bev; char filename[PATH_MAX]; - u64 limit = offset + size, orig_offset = offset; + u64 limit, orig_offset = offset; int err = -1; + /* Prevent offset + size from wrapping past ULLONG_MAX */ + if (size > ULLONG_MAX - offset) + return -1; + + limit = offset + size; + while (offset < limit) { ssize_t len; if (readn(input, &bev, sizeof(bev)) != sizeof(bev)) goto out; - if (header->needs_swap) + if (header->needs_swap) { perf_event_header__bswap(&bev.header); + bev.pid = bswap_32(bev.pid); + } + + /* + * size == 0 would loop forever (offset never advances); + * size > remaining would read past the section boundary. + */ + if (bev.header.size == 0 || bev.header.size > limit - offset) + goto out; len = bev.header.size - sizeof(bev); if (len < 0 || len >= PATH_MAX) { @@ -2644,6 +2677,13 @@ static int perf_header__read_build_ids(struct perf_header *header, if (readn(input, filename, len) != len) goto out; + /* + * The file data may lack a null terminator, which could + * indicate a corrupt or crafted perf.data file. Ensure + * filename is always a valid C string before passing it + * to functions like machine__findnew_dso(). + */ + filename[len] = '\0'; /* * The a1645ce1 changeset: * @@ -2657,7 +2697,9 @@ static int perf_header__read_build_ids(struct perf_header *header, * '[kernel.kallsyms]' string for the kernel build-id has the * first 4 characters chopped off (where the pid_t sits). */ - if (memcmp(filename, "nel.kallsyms]", 13) == 0) { + /* Guard short filenames against memcmp reading past the buffer */ + if (len >= (ssize_t)sizeof("nel.kallsyms]") - 1 && + memcmp(filename, "nel.kallsyms]", sizeof("nel.kallsyms]") - 1) == 0) { if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) return -1; return perf_header__read_build_ids_abi_quirk(header, input, offset, size); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8588e12f110f..0fac8f4e0e22 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -686,6 +686,25 @@ static int perf_event__hdr_attr_swap(union perf_event *event, return 0; } +static int perf_event__build_id_swap(union perf_event *event, + bool sample_id_all) +{ + event->build_id.pid = bswap_32(event->build_id.pid); + + if (sample_id_all) { + void *data = &event->build_id.filename; + void *end = (void *)event + event->header.size; + size_t len = strnlen(data, end - data); + + /* See comment in perf_event__comm_swap() */ + if (len == (size_t)(end - data)) + return -1; + data += PERF_ALIGN(len + 1, sizeof(u64)); + swap_sample_id_all(event, data); + } + return 0; +} + static int perf_event__event_update_swap(union perf_event *event, bool sample_id_all __maybe_unused) { @@ -1014,7 +1033,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, - [PERF_RECORD_HEADER_BUILD_ID] = NULL, + [PERF_RECORD_HEADER_BUILD_ID] = perf_event__build_id_swap, [PERF_RECORD_HEADER_FEATURE] = perf_event__header_feature_swap, [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, @@ -2004,6 +2023,12 @@ static s64 perf_session__process_user_event(struct perf_session *session, err = tool->tracing_data(tool, session, event); break; case PERF_RECORD_HEADER_BUILD_ID: + if (!perf_event__check_nul(event->build_id.filename, + (void *)event + event->header.size, + "HEADER_BUILD_ID")) { + err = 0; + break; + } err = tool->build_id(tool, session, event); break; case PERF_RECORD_FINISHED_ROUND: From f11a797cb6a8707be7e18603519406076c90efe3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 4 May 2026 11:17:19 -0300 Subject: [PATCH 2171/5214] perf cpumap: Reject RANGE_CPUS with start_cpu > end_cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_map__from_range() computes nr_cpus as end_cpu - start_cpu + 1. When a crafted perf.data has start_cpu > end_cpu, this wraps to a huge value, causing perf_cpu_map__empty_new() to attempt a massive allocation. Return NULL when the range is inverted. Also clamp any_cpu to boolean (0 or 1) since it is added to the allocation count — a crafted value > 1 would inflate the map size. Harden cpu_map__from_mask() to reject unsupported long_size values (anything other than 4 or 8), preventing misinterpretation of the mask data layout. Snapshot mmap'd fields via READ_ONCE() into locals to prevent TOCTOU re-reads — the data pointer references MAP_SHARED mmap'd memory that could theoretically change between reads on a FUSE-backed file: - cpu_map__from_range(): snapshot start_cpu, end_cpu, any_cpu - cpu_map__from_entries(): snapshot nr and each cpu[i] element - cpu_map__from_mask(): snapshot long_size (before validation, closing the check-then-read gap), mask_nr - perf_record_cpu_map_data__read_one_mask(): add u16 long_size parameter so callers pass the validated copy instead of re-reading data->mask32_data.long_size from mmap'd memory Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 62 +++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 11922e1ded84..b1e5c29c6e3e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -10,6 +10,7 @@ #include #include "asm/bug.h" +#include #include #include #include @@ -40,15 +41,16 @@ bool perf_record_cpu_map_data__test_bit(int i, /* Read ith mask value from data into the given 64-bit sized bitmap */ static void perf_record_cpu_map_data__read_one_mask(const struct perf_record_cpu_map_data *data, - int i, unsigned long *bitmap) + int i, unsigned long *bitmap, + u16 long_size) { #if __SIZEOF_LONG__ == 8 - if (data->mask32_data.long_size == 4) + if (long_size == 4) bitmap[0] = data->mask32_data.mask[i]; else bitmap[0] = data->mask64_data.mask[i]; #else - if (data->mask32_data.long_size == 4) { + if (long_size == 4) { bitmap[0] = data->mask32_data.mask[i]; bitmap[1] = 0; } else { @@ -64,24 +66,27 @@ static void perf_record_cpu_map_data__read_one_mask(const struct perf_record_cpu } static struct perf_cpu_map *cpu_map__from_entries(const struct perf_record_cpu_map_data *data) { + /* Snapshot nr — data is mmap'd and could change between reads */ + u16 nr = READ_ONCE(data->cpus_data.nr); struct perf_cpu_map *map; - map = perf_cpu_map__empty_new(data->cpus_data.nr); + map = perf_cpu_map__empty_new(nr); if (!map) return NULL; - for (unsigned int i = 0; i < data->cpus_data.nr; i++) { + for (unsigned int i = 0; i < nr; i++) { + u16 cpu = READ_ONCE(data->cpus_data.cpu[i]); /* * Special treatment for -1, which is not real cpu number, * and we need to use (int) -1 to initialize map[i], * otherwise it would become 65535. */ - if (data->cpus_data.cpu[i] == (u16) -1) { + if (cpu == (u16) -1) { RC_CHK_ACCESS(map)->map[i].cpu = -1; - } else if (data->cpus_data.cpu[i] < INT16_MAX) { - RC_CHK_ACCESS(map)->map[i].cpu = (int16_t) data->cpus_data.cpu[i]; + } else if (cpu < INT16_MAX) { + RC_CHK_ACCESS(map)->map[i].cpu = (int16_t) cpu; } else { - pr_err("Invalid cpumap entry %u\n", data->cpus_data.cpu[i]); + pr_err("Invalid cpumap entry %u\n", cpu); perf_cpu_map__put(map); return NULL; } @@ -93,11 +98,21 @@ static struct perf_cpu_map *cpu_map__from_entries(const struct perf_record_cpu_m static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_data *data) { DECLARE_BITMAP(local_copy, 64); - int weight = 0, mask_nr = data->mask32_data.nr; + int weight = 0, mask_nr; + /* Snapshot before validation — data is mmap'd and could change */ + u16 long_size = READ_ONCE(data->mask32_data.long_size); struct perf_cpu_map *map; + /* long_size must be 4 or 8; other values overflow cpus_per_i below */ + if (long_size != 4 && long_size != 8) { + pr_warning("WARNING: cpu_map mask: unsupported long_size %u\n", long_size); + return NULL; + } + + mask_nr = READ_ONCE(data->mask32_data.nr); + for (int i = 0; i < mask_nr; i++) { - perf_record_cpu_map_data__read_one_mask(data, i, local_copy); + perf_record_cpu_map_data__read_one_mask(data, i, local_copy, long_size); weight += bitmap_weight(local_copy, 64); } @@ -106,11 +121,14 @@ static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_ return NULL; for (int i = 0, j = 0; i < mask_nr; i++) { - int cpus_per_i = (i * data->mask32_data.long_size * BITS_PER_BYTE); + int cpus_per_i = (i * long_size * BITS_PER_BYTE); int cpu; - perf_record_cpu_map_data__read_one_mask(data, i, local_copy); + perf_record_cpu_map_data__read_one_mask(data, i, local_copy, long_size); for_each_set_bit(cpu, local_copy, 64) { + /* Guard against more set bits than the first pass counted */ + if (j >= weight) + break; if (cpu + cpus_per_i < INT16_MAX) { RC_CHK_ACCESS(map)->map[j++].cpu = cpu + cpus_per_i; } else { @@ -126,18 +144,28 @@ static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_ static struct perf_cpu_map *cpu_map__from_range(const struct perf_record_cpu_map_data *data) { + /* Snapshot fields — data is mmap'd and could change between reads */ + u16 start_cpu = READ_ONCE(data->range_cpu_data.start_cpu); + u16 end_cpu = READ_ONCE(data->range_cpu_data.end_cpu); + u16 any_cpu = READ_ONCE(data->range_cpu_data.any_cpu); struct perf_cpu_map *map; unsigned int i = 0; - map = perf_cpu_map__empty_new(data->range_cpu_data.end_cpu - - data->range_cpu_data.start_cpu + 1 + data->range_cpu_data.any_cpu); + if (end_cpu < start_cpu) { + pr_warning("WARNING: cpu_map range: end_cpu %u < start_cpu %u\n", + end_cpu, start_cpu); + return NULL; + } + + /* any_cpu is boolean (0 or 1), not a count — clamp to avoid inflated nr */ + map = perf_cpu_map__empty_new(end_cpu - start_cpu + 1 + !!any_cpu); if (!map) return NULL; - if (data->range_cpu_data.any_cpu) + if (any_cpu) RC_CHK_ACCESS(map)->map[i++].cpu = -1; - for (int cpu = data->range_cpu_data.start_cpu; cpu <= data->range_cpu_data.end_cpu; + for (int cpu = start_cpu; cpu <= end_cpu; i++, cpu++) { if (cpu < INT16_MAX) { RC_CHK_ACCESS(map)->map[i].cpu = cpu; From 97cd22555c6dd3c5124ff16417fa66dd7bb844be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:18:45 -0300 Subject: [PATCH 2172/5214] perf auxtrace: Harden auxtrace_error event handling Fix four issues in PERF_RECORD_AUXTRACE_ERROR handling: 1. auxtrace_error_name() takes a signed int parameter, but e->type is __u32. A crafted value like 0xFFFFFFFF converts to -1, passes the bounds check, and causes a negative array index. Fix by changing the parameter to unsigned int. 2. The msg field is printed via %s without a length bound. The min_size table only guarantees fields up to msg (offset 48), so a truncated event has zero msg bytes within the event boundary. Compute the available msg length from header.size, cap at sizeof(e->msg), and use %.*s. 3. fmt >= 2 adds machine_pid and vcpu fields after msg[64]. Older files may have fmt >= 2 but an event size that doesn't include these fields. Add a size check in the swap handler to downgrade fmt before the conditional field access, and a matching size guard in the fprintf path for native-endian events (which are mmap'd read-only and can't be modified in place). 4. python_process_auxtrace_error() had the same issues: msg was passed to tuple_set_string() unbounded, and machine_pid/vcpu were accessed unconditionally without checking fmt or event size. Apply the same bounds checks. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/auxtrace.c | 24 +++++++++++++--- .../scripting-engines/trace-event-python.c | 28 +++++++++++++++++-- tools/perf/util/session.c | 18 ++++++++++-- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index a9f007d47c0b..fcf564e0d777 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1774,7 +1774,7 @@ static const char * const auxtrace_error_type_name[] = { [PERF_AUXTRACE_ERROR_ITRACE] = "instruction trace", }; -static const char *auxtrace_error_name(int type) +static const char *auxtrace_error_name(unsigned int type) { const char *error_type_name = NULL; @@ -1790,6 +1790,7 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp) struct perf_record_auxtrace_error *e = &event->auxtrace_error; unsigned long long nsecs = e->time; const char *msg = e->msg; + int msg_max; int ret; ret = fprintf(fp, " %s error type %u", @@ -1807,11 +1808,26 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp) if (!e->fmt) msg = (const char *)&e->time; - if (e->fmt >= 2 && e->machine_pid) + /* Bound msg to the bytes actually within the event, capped at the array size */ + msg_max = (int)((void *)event + event->header.size - (void *)msg); + if (msg_max < 0) + msg_max = 0; + if (msg_max > (int)sizeof(e->msg)) + msg_max = sizeof(e->msg); + + /* + * Unlike the swap path which downgrades fmt in place, + * native-endian events are mmap'd read-only — check size + * instead to avoid accessing machine_pid/vcpu OOB. + */ + if (e->fmt >= 2 && + event->header.size >= offsetof(typeof(event->auxtrace_error), vcpu) + + sizeof(event->auxtrace_error.vcpu) && + e->machine_pid) ret += fprintf(fp, " machine_pid %d vcpu %d", e->machine_pid, e->vcpu); - ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %s\n", - e->cpu, e->pid, e->tid, e->ip, e->code, msg); + ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %.*s\n", + e->cpu, e->pid, e->tid, e->ip, e->code, msg_max, msg); return ret; } diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 8edd2f36e5a9..cee1f32d7022 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1607,6 +1607,9 @@ static void python_process_auxtrace_error(struct perf_session *session __maybe_u const char *handler_name = "auxtrace_error"; unsigned long long tm = e->time; const char *msg = e->msg; + s32 machine_pid = 0, vcpu = 0; + char msg_buf[MAX_AUXTRACE_ERROR_MSG + 1]; + int msg_max; PyObject *handler, *t; handler = get_handler(handler_name); @@ -1618,6 +1621,25 @@ static void python_process_auxtrace_error(struct perf_session *session __maybe_u msg = (const char *)&e->time; } + /* Bound msg to the bytes within the event, ensure NUL-termination */ + msg_max = (int)((void *)event + event->header.size - (void *)msg); + if (msg_max <= 0) { + msg_buf[0] = '\0'; + } else { + if (msg_max > (int)sizeof(msg_buf) - 1) + msg_max = sizeof(msg_buf) - 1; + memcpy(msg_buf, msg, msg_max); + msg_buf[msg_max] = '\0'; + } + + /* Only access fmt >= 2 fields if the event is large enough */ + if (e->fmt >= 2 && + event->header.size >= offsetof(typeof(event->auxtrace_error), vcpu) + + sizeof(event->auxtrace_error.vcpu)) { + machine_pid = e->machine_pid; + vcpu = e->vcpu; + } + t = tuple_new(11); tuple_set_u32(t, 0, e->type); @@ -1627,10 +1649,10 @@ static void python_process_auxtrace_error(struct perf_session *session __maybe_u tuple_set_s32(t, 4, e->tid); tuple_set_u64(t, 5, e->ip); tuple_set_u64(t, 6, tm); - tuple_set_string(t, 7, msg); + tuple_set_string(t, 7, msg_buf); tuple_set_u32(t, 8, cpumode); - tuple_set_s32(t, 9, e->machine_pid); - tuple_set_s32(t, 10, e->vcpu); + tuple_set_s32(t, 9, machine_pid); + tuple_set_s32(t, 10, vcpu); call_object(handler, t, handler_name); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0fac8f4e0e22..092fccbea8f8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -766,8 +766,22 @@ static int perf_event__auxtrace_error_swap(union perf_event *event, if (event->auxtrace_error.fmt) event->auxtrace_error.time = bswap_64(event->auxtrace_error.time); if (event->auxtrace_error.fmt >= 2) { - event->auxtrace_error.machine_pid = bswap_32(event->auxtrace_error.machine_pid); - event->auxtrace_error.vcpu = bswap_32(event->auxtrace_error.vcpu); + /* + * fmt >= 2 adds machine_pid and vcpu after msg[64]. + * Older files may have fmt >= 2 but an event size + * that doesn't include these fields — downgrade to + * avoid swapping out of bounds. + */ + if (event->header.size < offsetof(typeof(event->auxtrace_error), vcpu) + + sizeof(event->auxtrace_error.vcpu)) { + pr_warning("WARNING: PERF_RECORD_AUXTRACE_ERROR: fmt %u but event too small for machine_pid/vcpu (%u bytes), downgrading fmt\n", + event->auxtrace_error.fmt, + event->header.size); + event->auxtrace_error.fmt = 1; + } else { + event->auxtrace_error.machine_pid = bswap_32(event->auxtrace_error.machine_pid); + event->auxtrace_error.vcpu = bswap_32(event->auxtrace_error.vcpu); + } } return 0; } From 355ca55e3e96ea272b330f3fb80401472f60d18c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:26:51 -0300 Subject: [PATCH 2173/5214] perf session: Add byte-swap and bounds check for PERF_RECORD_BPF_METADATA events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PERF_RECORD_BPF_METADATA has no entry in perf_event__swap_ops[], so its nr_entries field is never byte-swapped when reading a cross-endian perf.data file. Downstream processing in perf_event__fprintf_bpf_metadata() loops over nr_entries, so a foreign-endian value causes out-of-bounds reads. Add a swap handler that byte-swaps nr_entries after validating that header.size is large enough. The entries[] array contains only char arrays (key/value strings), so no per-entry swap is needed — but ensure NUL-termination on the writable cross-endian path. Validate header.size, nr_entries, and string NUL-termination in the common event delivery path so that native-endian files with malicious values are also rejected. Snapshot nr_entries via READ_ONCE() before validation — the event is on a MAP_SHARED mmap that could theoretically change between the bounds check and the loop. Changes in v2: - Snapshot event->header.size via READ_ONCE() into a local variable to prevent a double-fetch underflow in the max_entries calculation (Reported-by: sashiko-bot@kernel.org) - Write back clamped nr_entries to the event on the swap path, consistent with NAMESPACES and STAT_CONFIG handlers — without writeback the native path sees the inflated nr and skips the event entirely (Reported-by: sashiko-bot@kernel.org) Fixes: ab38e84ba9a8 ("perf record: collect BPF metadata from existing BPF programs") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Blake Jones Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 89 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 092fccbea8f8..95eb793026de 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -961,6 +961,48 @@ static int perf_event__time_conv_swap(union perf_event *event, return 0; } +static int perf_event__bpf_metadata_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + u64 i, nr, max_nr; + + /* Fixed header must fit before accessing nr_entries or prog_name */ + if (event->header.size < sizeof(event->bpf_metadata)) + return -1; + + event->bpf_metadata.nr_entries = bswap_64(event->bpf_metadata.nr_entries); + + /* + * Ensure NUL-termination on the cross-endian path where the + * mapping is writable (MAP_PRIVATE + PROT_WRITE). Fixing + * the string in place is preferred over rejecting because it + * preserves the event for downstream processing — only the + * last byte is lost. + * + * The native-endian path (MAP_SHARED + PROT_READ) cannot + * write, so it validates and skips unterminated events in + * perf_session__process_user_event() instead. The two + * strategies produce different outcomes for the same + * malformed input (fix vs skip), which is inherent in the + * writable-vs-read-only mapping model. + */ + event->bpf_metadata.prog_name[BPF_PROG_NAME_LEN - 1] = '\0'; + + nr = event->bpf_metadata.nr_entries; + max_nr = (event->header.size - sizeof(event->bpf_metadata)) / + sizeof(event->bpf_metadata.entries[0]); + if (nr > max_nr) { + /* Persist clamped value so the native path processes entries, not skips */ + nr = max_nr; + event->bpf_metadata.nr_entries = nr; + } + + for (i = 0; i < nr; i++) { + event->bpf_metadata.entries[i].key[BPF_METADATA_KEY_LEN - 1] = '\0'; + event->bpf_metadata.entries[i].value[BPF_METADATA_VALUE_LEN - 1] = '\0'; + } + return 0; +} static int perf_event__schedstat_cpu_swap(union perf_event *event __maybe_unused, bool sample_id_all __maybe_unused) @@ -1060,6 +1102,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, [PERF_RECORD_TIME_CONV] = perf_event__time_conv_swap, + [PERF_RECORD_BPF_METADATA] = perf_event__bpf_metadata_swap, [PERF_RECORD_SCHEDSTAT_CPU] = perf_event__schedstat_cpu_swap, [PERF_RECORD_SCHEDSTAT_DOMAIN] = perf_event__schedstat_domain_swap, [PERF_RECORD_HEADER_MAX] = NULL, @@ -2203,9 +2246,53 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_FINISHED_INIT: err = tool->finished_init(tool, session, event); break; - case PERF_RECORD_BPF_METADATA: + case PERF_RECORD_BPF_METADATA: { + u64 nr_entries, max_entries; + u32 hdr_size = READ_ONCE(event->header.size); + + if (hdr_size < sizeof(event->bpf_metadata)) { + pr_warning("WARNING: PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n", + hdr_size); + err = 0; + break; + } + + /* + * Native-endian files are mmap'd read-only — validate + * NUL-termination instead of writing. + */ + if (strnlen(event->bpf_metadata.prog_name, + BPF_PROG_NAME_LEN) == BPF_PROG_NAME_LEN) { + pr_warning("WARNING: PERF_RECORD_BPF_METADATA: prog_name not null-terminated, skipping\n"); + err = 0; + break; + } + + /* Snapshot — event is mmap'd and could change between reads */ + nr_entries = READ_ONCE(event->bpf_metadata.nr_entries); + max_entries = (hdr_size - sizeof(event->bpf_metadata)) / + sizeof(event->bpf_metadata.entries[0]); + if (nr_entries > max_entries) { + pr_warning("WARNING: PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n", + nr_entries, max_entries); + err = 0; + break; + } + + for (u64 i = 0; i < nr_entries; i++) { + if (strnlen(event->bpf_metadata.entries[i].key, + BPF_METADATA_KEY_LEN) == BPF_METADATA_KEY_LEN || + strnlen(event->bpf_metadata.entries[i].value, + BPF_METADATA_VALUE_LEN) == BPF_METADATA_VALUE_LEN) { + pr_warning("WARNING: PERF_RECORD_BPF_METADATA: entry %" PRIu64 " key/value not null-terminated, skipping\n", i); + err = 0; + goto out; + } + } + err = tool->bpf_metadata(tool, session, event); break; + } case PERF_RECORD_SCHEDSTAT_CPU: err = tool->schedstat_cpu(tool, session, event); break; From 3c18544fb18b18fa824d072fc6a22e8d9f6dc070 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:31:40 -0300 Subject: [PATCH 2174/5214] perf header: Validate null-termination in PERF_RECORD_EVENT_UPDATE string fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strdup(ev->unit) and strdup(ev->name) read until '\0' with no guarantee the string is null-terminated within event->header.size. The dump_trace fprintf path has the same problem with %s. Validate before either path runs — same class of bug fixed for MMAP/MMAP2/COMM/CGROUP by perf_event__check_nul(). Also harden the event_update swap handler to: - Validate SCALE event size before swapping the double at offset 24, which exceeds the 24-byte min_size. - Validate CPUS event size before accessing the cpu_map type/nr/long_size fields, which also start at the min_size boundary. - Swap CPUS variant fields (type, nr, long_size) so the processing path sees native byte order. Add validation in perf_event__process_event_update() for all event update variants (UNIT, NAME, SCALE, CPUS) before dump_trace or processing. Validate CPUS nr against payload size for both PERF_CPU_MAP__CPUS and PERF_CPU_MAP__MASK types on the fprintf (dump_trace) path: - CPUS: check nr does not exceed available cpu entries - MASK: check nr does not exceed available mask entries for both mask32 (long_size == 4) and mask64 (long_size == 8) layouts, with underflow guards on the offsetof subtraction Fix a missing break before the default case in the CPUS switch path. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 150 ++++++++++++++++++++++++++++++++++++-- tools/perf/util/session.c | 99 ++++++++++++++++++++++++- 2 files changed, 242 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c0b5c99f462a..9e3a08b1f8ae 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -5117,15 +5117,76 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) switch (ev->type) { case PERF_EVENT_UPDATE__SCALE: + if (event->header.size < offsetof(struct perf_record_event_update, scale) + + sizeof(ev->scale)) { + ret += fprintf(fp, "... scale: (truncated)\n"); + break; + } ret += fprintf(fp, "... scale: %f\n", ev->scale.scale); break; case PERF_EVENT_UPDATE__UNIT: - ret += fprintf(fp, "... unit: %s\n", ev->unit); + case PERF_EVENT_UPDATE__NAME: { + size_t str_off = offsetof(struct perf_record_event_update, unit); + size_t max_len = event->header.size > str_off ? + event->header.size - str_off : 0; + + if (max_len == 0 || strnlen(ev->unit, max_len) == max_len) { + ret += fprintf(fp, "... %s: (unterminated)\n", + ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name"); + break; + } + ret += fprintf(fp, "... %s: %s\n", + ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name", + ev->unit); break; - case PERF_EVENT_UPDATE__NAME: - ret += fprintf(fp, "... name: %s\n", ev->name); - break; - case PERF_EVENT_UPDATE__CPUS: + } + case PERF_EVENT_UPDATE__CPUS: { + size_t cpus_off = offsetof(struct perf_record_event_update, cpus); + u32 cpus_payload; + + if (event->header.size < cpus_off + sizeof(__u16) + + sizeof(struct perf_record_range_cpu_map)) { + ret += fprintf(fp, "... cpus: (truncated)\n"); + break; + } + + /* + * Validate nr against payload — this function may be + * called from the stub handler (dump_trace path) which + * bypasses perf_event__process_event_update() validation. + */ + cpus_payload = event->header.size - cpus_off; + if (ev->cpus.cpus.type == PERF_CPU_MAP__CPUS) { + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, cpus_data.cpu) || + ev->cpus.cpus.cpus_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, cpus_data.cpu)) / + sizeof(ev->cpus.cpus.cpus_data.cpu[0])) { + ret += fprintf(fp, "... cpus: nr %u exceeds payload\n", + ev->cpus.cpus.cpus_data.nr); + break; + } + } else if (ev->cpus.cpus.type == PERF_CPU_MAP__MASK) { + if (ev->cpus.cpus.mask32_data.long_size == 4) { + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask32_data.mask) || + ev->cpus.cpus.mask32_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask32_data.mask)) / + sizeof(ev->cpus.cpus.mask32_data.mask[0])) { + ret += fprintf(fp, "... cpus: mask nr %u exceeds payload\n", + ev->cpus.cpus.mask32_data.nr); + break; + } + } else if (ev->cpus.cpus.mask64_data.long_size == 8) { + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask) || + ev->cpus.cpus.mask64_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) / + sizeof(ev->cpus.cpus.mask64_data.mask[0])) { + ret += fprintf(fp, "... cpus: mask nr %u exceeds payload\n", + ev->cpus.cpus.mask64_data.nr); + break; + } + } + } + ret += fprintf(fp, "... "); map = cpu_map__new_data(&ev->cpus.cpus); @@ -5135,6 +5196,7 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) } else ret += fprintf(fp, "failed to get cpus\n"); break; + } default: ret += fprintf(fp, "... unknown type\n"); break; @@ -5269,6 +5331,83 @@ int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused struct evsel *evsel; struct perf_cpu_map *map; + /* + * Validate payload before dump_trace or processing — both + * paths access variant-specific fields without further checks. + */ + if (ev->type == PERF_EVENT_UPDATE__UNIT || + ev->type == PERF_EVENT_UPDATE__NAME) { + size_t str_off = offsetof(struct perf_record_event_update, unit); + size_t max_len = event->header.size > str_off ? + event->header.size - str_off : 0; + + if (max_len == 0 || strnlen(ev->unit, max_len) == max_len) { + pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: %s not null-terminated, skipping\n", + ev->type == PERF_EVENT_UPDATE__UNIT ? "unit" : "name"); + return 0; + } + } else if (ev->type == PERF_EVENT_UPDATE__SCALE) { + if (event->header.size < offsetof(struct perf_record_event_update, scale) + + sizeof(ev->scale)) { + pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: SCALE payload too small, skipping\n"); + return 0; + } + } else if (ev->type == PERF_EVENT_UPDATE__CPUS) { + size_t cpus_off = offsetof(struct perf_record_event_update, cpus); + size_t min_cpus = sizeof(__u16) + + sizeof(struct perf_record_range_cpu_map); + u32 cpus_payload; + + if (event->header.size < cpus_off + min_cpus) { + pr_warning("WARNING: PERF_RECORD_EVENT_UPDATE: CPUS payload too small, skipping\n"); + return 0; + } + + /* + * Validate per-variant nr against the remaining + * payload on the native path — the swap path clamps + * nr in perf_event__event_update_swap(), but native + * events are read-only and cannot be clamped in place. + * cpu_map__new_data() trusts nr for allocation and + * iteration, so unchecked values cause OOB reads. + */ + cpus_payload = event->header.size - cpus_off; + switch (ev->cpus.cpus.type) { + case PERF_CPU_MAP__CPUS: + if (ev->cpus.cpus.cpus_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, cpus_data.cpu)) / + sizeof(ev->cpus.cpus.cpus_data.cpu[0])) { + pr_warning("WARNING: EVENT_UPDATE CPUS: nr %u exceeds payload, skipping\n", + ev->cpus.cpus.cpus_data.nr); + return 0; + } + break; + case PERF_CPU_MAP__MASK: + if (ev->cpus.cpus.mask32_data.long_size == 4) { + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask32_data.mask) || + ev->cpus.cpus.mask32_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask32_data.mask)) / + sizeof(ev->cpus.cpus.mask32_data.mask[0])) { + pr_warning("WARNING: EVENT_UPDATE MASK: nr %u exceeds payload, skipping\n", + ev->cpus.cpus.mask32_data.nr); + return 0; + } + } else if (ev->cpus.cpus.mask64_data.long_size == 8) { + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask) || + ev->cpus.cpus.mask64_data.nr > + (cpus_payload - offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) / + sizeof(ev->cpus.cpus.mask64_data.mask[0])) { + pr_warning("WARNING: EVENT_UPDATE MASK: nr %u exceeds payload, skipping\n", + ev->cpus.cpus.mask64_data.nr); + return 0; + } + } + break; + default: + break; + } + } + if (dump_trace) perf_event__fprintf_event_update(event, stdout); @@ -5300,6 +5439,7 @@ int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused evsel->core.pmu_cpus = map; } else pr_err("failed to get event_update cpus\n"); + break; default: break; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 95eb793026de..8280413f4528 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -708,8 +708,103 @@ static int perf_event__build_id_swap(union perf_event *event, static int perf_event__event_update_swap(union perf_event *event, bool sample_id_all __maybe_unused) { - event->event_update.type = bswap_64(event->event_update.type); - event->event_update.id = bswap_64(event->event_update.id); + struct perf_record_event_update *ev = &event->event_update; + + ev->type = bswap_64(ev->type); + ev->id = bswap_64(ev->id); + + /* + * Swap variant-specific fields so the processing path + * sees native byte order. + */ + if (ev->type == PERF_EVENT_UPDATE__SCALE) { + if (event->header.size < offsetof(struct perf_record_event_update, scale) + + sizeof(ev->scale)) + return -1; + mem_bswap_64(&ev->scale.scale, sizeof(ev->scale.scale)); + } else if (ev->type == PERF_EVENT_UPDATE__CPUS) { + u32 cpus_payload; + struct perf_record_cpu_map_data *data = &ev->cpus.cpus; + + /* CPUS fields start at the same offset as scale (union) */ + if (event->header.size < offsetof(struct perf_record_event_update, cpus) + + sizeof(__u16) + sizeof(struct perf_record_range_cpu_map)) + return -1; + cpus_payload = event->header.size - offsetof(struct perf_record_event_update, cpus); + data->type = bswap_16(data->type); + /* + * Full swap including array elements — same logic as + * perf_event__cpu_map_swap() but scoped to the + * embedded cpu_map_data within EVENT_UPDATE. + */ + switch (data->type) { + case PERF_CPU_MAP__CPUS: { + u16 nr, max_nr; + + data->cpus_data.nr = bswap_16(data->cpus_data.nr); + nr = data->cpus_data.nr; + max_nr = (cpus_payload - offsetof(struct perf_record_cpu_map_data, + cpus_data.cpu)) / + sizeof(data->cpus_data.cpu[0]); + if (nr > max_nr) { + nr = max_nr; + data->cpus_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) + data->cpus_data.cpu[i] = bswap_16(data->cpus_data.cpu[i]); + break; + } + case PERF_CPU_MAP__MASK: + data->mask32_data.long_size = bswap_16(data->mask32_data.long_size); + switch (data->mask32_data.long_size) { + case 4: { + u16 nr, max_nr; + + data->mask32_data.nr = bswap_16(data->mask32_data.nr); + nr = data->mask32_data.nr; + max_nr = (cpus_payload - offsetof(struct perf_record_cpu_map_data, + mask32_data.mask)) / + sizeof(data->mask32_data.mask[0]); + if (nr > max_nr) { + nr = max_nr; + data->mask32_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) + data->mask32_data.mask[i] = bswap_32(data->mask32_data.mask[i]); + break; + } + case 8: { + u16 nr, max_nr; + + data->mask64_data.nr = bswap_16(data->mask64_data.nr); + nr = data->mask64_data.nr; + if (cpus_payload < offsetof(struct perf_record_cpu_map_data, mask64_data.mask)) { + data->mask64_data.nr = 0; + break; + } + max_nr = (cpus_payload - offsetof(struct perf_record_cpu_map_data, + mask64_data.mask)) / + sizeof(data->mask64_data.mask[0]); + if (nr > max_nr) { + nr = max_nr; + data->mask64_data.nr = nr; + } + for (unsigned int i = 0; i < nr; i++) + data->mask64_data.mask[i] = bswap_64(data->mask64_data.mask[i]); + break; + } + default: + break; + } + break; + case PERF_CPU_MAP__RANGE_CPUS: + data->range_cpu_data.start_cpu = bswap_16(data->range_cpu_data.start_cpu); + data->range_cpu_data.end_cpu = bswap_16(data->range_cpu_data.end_cpu); + break; + default: + break; + } + } return 0; } From 2bd03e3054a4e21895e6aa20dd8a14c08e37edee Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:20:14 -0300 Subject: [PATCH 2175/5214] perf tools: Bounds check perf_event_attr fields against attr.size before printing perf_event_attr__fprintf() accessed all struct fields unconditionally, but attrs from older perf.data files or BPF-captured syscall payloads may have a smaller size than the current struct. Fields beyond the recorded size contain uninitialized or zero-filled data. Add size-guarded macros (PRINT_ATTRn, PRINT_ATTRn_bf) that compare each field's offset against attr->size before accessing it. Guard the bitfield block (disabled, inherit, ... defer_output) with attr_size >= 48. These bitfields share a single __u64 at offset 40, which is within PERF_ATTR_SIZE_VER0 for validated perf.data attrs, but BPF-captured attrs from perf trace can have a smaller size when the tracee passes a minimal struct to sys_perf_event_open. Also fix the BPF trace path: when perf trace intercepts sys_perf_event_open via BPF, the program copies PERF_ATTR_SIZE_VER0 bytes when the tracee passes size=0, but leaves the size field as 0. Set attr->size to PERF_ATTR_SIZE_VER0 in the augmented syscall handler so the bounds checks match the actual copied size. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/perf_event_open.c | 23 +++- tools/perf/util/perf_event_attr_fprintf.c | 141 ++++++++++++++-------- 2 files changed, 114 insertions(+), 50 deletions(-) diff --git a/tools/perf/trace/beauty/perf_event_open.c b/tools/perf/trace/beauty/perf_event_open.c index c1c7445dcff9..6315b46bcdf0 100644 --- a/tools/perf/trace/beauty/perf_event_open.c +++ b/tools/perf/trace/beauty/perf_event_open.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: LGPL-2.1 +#include #include "trace/beauty/beauty.h" #include "util/evsel_fprintf.h" #include @@ -80,7 +81,27 @@ static size_t perf_event_attr___scnprintf(struct perf_event_attr *attr, char *bf static size_t syscall_arg__scnprintf_augmented_perf_event_attr(struct syscall_arg *arg, char *bf, size_t size) { - return perf_event_attr___scnprintf((void *)arg->augmented.args->value, bf, size, + struct perf_event_attr *attr = (void *)arg->augmented.args->value; + struct perf_event_attr local_attr; + + /* + * augmented_raw_syscalls.bpf.c (shipped with perf) copies + * PERF_ATTR_SIZE_VER0 bytes when the tracee passes size=0, + * but leaves the size field as 0. The payload size is + * guaranteed by perf's own BPF program, not externally + * controllable. Copy to a local so we can fix up size + * without writing to the potentially read-only augmented + * args buffer. + */ + if (!attr->size) { + memcpy(&local_attr, attr, PERF_ATTR_SIZE_VER0); + memset((void *)&local_attr + PERF_ATTR_SIZE_VER0, 0, + sizeof(local_attr) - PERF_ATTR_SIZE_VER0); + local_attr.size = PERF_ATTR_SIZE_VER0; + attr = &local_attr; + } + + return perf_event_attr___scnprintf(attr, bf, size, trace__show_zeros(arg->trace)); } diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c index 741c3d657a8b..3933639d76c5 100644 --- a/tools/perf/util/perf_event_attr_fprintf.c +++ b/tools/perf/util/perf_event_attr_fprintf.c @@ -275,24 +275,56 @@ static void __p_config_id(struct perf_pmu *pmu, char *buf, size_t size, u32 type #define p_type_id(val) __p_type_id(buf, BUF_SIZE, pmu, val) #define p_config_id(val) __p_config_id(pmu, buf, BUF_SIZE, attr->type, val) -#define PRINT_ATTRn(_n, _f, _p, _a) \ -do { \ - if (_a || attr->_f) { \ - _p(attr->_f); \ - ret += attr__fprintf(fp, _n, buf, priv);\ - } \ +#define PRINT_ATTRn(_n, _f, _p, _a) \ +do { \ + if (attr_size >= offsetof(struct perf_event_attr, _f) + \ + sizeof(attr->_f) && \ + (_a || attr->_f)) { \ + _p(attr->_f); \ + ret += attr__fprintf(fp, _n, buf, priv); \ + } \ +} while (0) + +/* bitfield members share an offset; most are within PERF_ATTR_SIZE_VER0 */ +#define PRINT_ATTRn_bf(_n, _f, _p, _a) \ +do { \ + if (_a || attr->_f) { \ + _p(attr->_f); \ + ret += attr__fprintf(fp, _n, buf, priv); \ + } \ } while (0) #define PRINT_ATTRf(_f, _p) PRINT_ATTRn(#_f, _f, _p, false) +#define PRINT_ATTRf_bf(_f, _p) PRINT_ATTRn_bf(#_f, _f, _p, false) int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, attr__fprintf_f attr__fprintf, void *priv) { struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); + /* + * size == 0 means ABI0 — the producer didn't set attr.size. + * perf_event__fprintf_attr() may pass the raw mmap'd event + * before the local copy, so default to PERF_ATTR_SIZE_VER0 + * (the ABI0 footprint) to avoid reading past the attr into + * the ID array that follows it in HEADER_ATTR events. + */ + u32 attr_size = attr->size ?: PERF_ATTR_SIZE_VER0; char buf[BUF_SIZE]; int ret = 0; - if (!pmu && (attr->type == PERF_TYPE_HARDWARE || attr->type == PERF_TYPE_HW_CACHE)) { + /* + * Cap to what we understand: all callers store the attr in a + * buffer of sizeof(*attr) bytes (perf.data read path copies + * min(attr.size, sizeof), BPF augmented path copies into a + * fixed-size value[] array). A spoofed attr->size larger + * than sizeof would cause PRINT_ATTRn to read past the + * actual buffer. + */ + if (attr_size > sizeof(*attr)) + attr_size = sizeof(*attr); + + if (!pmu && attr_size >= offsetof(struct perf_event_attr, config) + sizeof(attr->config) && + (attr->type == PERF_TYPE_HARDWARE || attr->type == PERF_TYPE_HW_CACHE)) { u32 extended_type = attr->config >> PERF_PMU_TYPE_SHIFT; if (extended_type) @@ -306,45 +338,53 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, PRINT_ATTRf(sample_type, p_sample_type); PRINT_ATTRf(read_format, p_read_format); - PRINT_ATTRf(disabled, p_unsigned); - PRINT_ATTRf(inherit, p_unsigned); - PRINT_ATTRf(pinned, p_unsigned); - PRINT_ATTRf(exclusive, p_unsigned); - PRINT_ATTRf(exclude_user, p_unsigned); - PRINT_ATTRf(exclude_kernel, p_unsigned); - PRINT_ATTRf(exclude_hv, p_unsigned); - PRINT_ATTRf(exclude_idle, p_unsigned); - PRINT_ATTRf(mmap, p_unsigned); - PRINT_ATTRf(comm, p_unsigned); - PRINT_ATTRf(freq, p_unsigned); - PRINT_ATTRf(inherit_stat, p_unsigned); - PRINT_ATTRf(enable_on_exec, p_unsigned); - PRINT_ATTRf(task, p_unsigned); - PRINT_ATTRf(watermark, p_unsigned); - PRINT_ATTRf(precise_ip, p_unsigned); - PRINT_ATTRf(mmap_data, p_unsigned); - PRINT_ATTRf(sample_id_all, p_unsigned); - PRINT_ATTRf(exclude_host, p_unsigned); - PRINT_ATTRf(exclude_guest, p_unsigned); - PRINT_ATTRf(exclude_callchain_kernel, p_unsigned); - PRINT_ATTRf(exclude_callchain_user, p_unsigned); - PRINT_ATTRf(mmap2, p_unsigned); - PRINT_ATTRf(comm_exec, p_unsigned); - PRINT_ATTRf(use_clockid, p_unsigned); - PRINT_ATTRf(context_switch, p_unsigned); - PRINT_ATTRf(write_backward, p_unsigned); - PRINT_ATTRf(namespaces, p_unsigned); - PRINT_ATTRf(ksymbol, p_unsigned); - PRINT_ATTRf(bpf_event, p_unsigned); - PRINT_ATTRf(aux_output, p_unsigned); - PRINT_ATTRf(cgroup, p_unsigned); - PRINT_ATTRf(text_poke, p_unsigned); - PRINT_ATTRf(build_id, p_unsigned); - PRINT_ATTRf(inherit_thread, p_unsigned); - PRINT_ATTRf(remove_on_exec, p_unsigned); - PRINT_ATTRf(sigtrap, p_unsigned); - PRINT_ATTRf(defer_callchain, p_unsigned); - PRINT_ATTRf(defer_output, p_unsigned); + /* + * All bitfields share a single __u64 right after read_format. + * BPF-captured attrs from perf trace may have a small size + * when the tracee passes a minimal struct, so skip the + * entire block when it's not covered. + */ + if (attr_size >= offsetof(struct perf_event_attr, wakeup_events)) { + PRINT_ATTRf_bf(disabled, p_unsigned); + PRINT_ATTRf_bf(inherit, p_unsigned); + PRINT_ATTRf_bf(pinned, p_unsigned); + PRINT_ATTRf_bf(exclusive, p_unsigned); + PRINT_ATTRf_bf(exclude_user, p_unsigned); + PRINT_ATTRf_bf(exclude_kernel, p_unsigned); + PRINT_ATTRf_bf(exclude_hv, p_unsigned); + PRINT_ATTRf_bf(exclude_idle, p_unsigned); + PRINT_ATTRf_bf(mmap, p_unsigned); + PRINT_ATTRf_bf(comm, p_unsigned); + PRINT_ATTRf_bf(freq, p_unsigned); + PRINT_ATTRf_bf(inherit_stat, p_unsigned); + PRINT_ATTRf_bf(enable_on_exec, p_unsigned); + PRINT_ATTRf_bf(task, p_unsigned); + PRINT_ATTRf_bf(watermark, p_unsigned); + PRINT_ATTRf_bf(precise_ip, p_unsigned); + PRINT_ATTRf_bf(mmap_data, p_unsigned); + PRINT_ATTRf_bf(sample_id_all, p_unsigned); + PRINT_ATTRf_bf(exclude_host, p_unsigned); + PRINT_ATTRf_bf(exclude_guest, p_unsigned); + PRINT_ATTRf_bf(exclude_callchain_kernel, p_unsigned); + PRINT_ATTRf_bf(exclude_callchain_user, p_unsigned); + PRINT_ATTRf_bf(mmap2, p_unsigned); + PRINT_ATTRf_bf(comm_exec, p_unsigned); + PRINT_ATTRf_bf(use_clockid, p_unsigned); + PRINT_ATTRf_bf(context_switch, p_unsigned); + PRINT_ATTRf_bf(write_backward, p_unsigned); + PRINT_ATTRf_bf(namespaces, p_unsigned); + PRINT_ATTRf_bf(ksymbol, p_unsigned); + PRINT_ATTRf_bf(bpf_event, p_unsigned); + PRINT_ATTRf_bf(aux_output, p_unsigned); + PRINT_ATTRf_bf(cgroup, p_unsigned); + PRINT_ATTRf_bf(text_poke, p_unsigned); + PRINT_ATTRf_bf(build_id, p_unsigned); + PRINT_ATTRf_bf(inherit_thread, p_unsigned); + PRINT_ATTRf_bf(remove_on_exec, p_unsigned); + PRINT_ATTRf_bf(sigtrap, p_unsigned); + PRINT_ATTRf_bf(defer_callchain, p_unsigned); + PRINT_ATTRf_bf(defer_output, p_unsigned); + } PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false); PRINT_ATTRf(bp_type, p_unsigned); @@ -359,9 +399,12 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, PRINT_ATTRf(sample_max_stack, p_unsigned); PRINT_ATTRf(aux_sample_size, p_unsigned); PRINT_ATTRf(sig_data, p_unsigned); - PRINT_ATTRf(aux_start_paused, p_unsigned); - PRINT_ATTRf(aux_pause, p_unsigned); - PRINT_ATTRf(aux_resume, p_unsigned); + /* aux_{start_paused,pause,resume} are at byte 116, past VER0 */ + if (attr_size >= offsetof(struct perf_event_attr, sig_data)) { + PRINT_ATTRf_bf(aux_start_paused, p_unsigned); + PRINT_ATTRf_bf(aux_pause, p_unsigned); + PRINT_ATTRf_bf(aux_resume, p_unsigned); + } return ret; } From 7d19f2008abf4b5440bfd9962f46f605beab69df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:24:02 -0300 Subject: [PATCH 2176/5214] perf header: Propagate feature section processing errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf_session__read_header() discards the return value from perf_header__process_sections(), so any error from a feature section processor (process_nrcpus, process_compressed, etc.) is silently ignored and the session opens as if nothing went wrong. This defeats the validation added by subsequent commits in this series: a crafted perf.data that fails a feature section check would still be processed with partially-initialized state. Check the return value and fail the session if any feature section processor returns an error. For truncated files (data.size == 0, i.e. recording was interrupted before the header was finalized), skip feature section processing entirely and clear the feature bitmap so tools use their "feature not present" fallbacks instead of accessing uninitialized env fields. Change the feature processor stubs for optional libraries (libtraceevent, libbpf) from returning -1 to returning 0, so that perf.data files containing these features can still be opened on builds without the optional library — the feature is simply skipped rather than causing a fatal error. Also propagate evlist__prepare_tracepoint_events() failure as -ENOMEM, since the function can fail due to strdup() allocation failure inside evsel__prepare_tracepoint_event(). Fixes: 1c0b04d12ae9 ("perf tools: Add perf_session__read_header function") Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 51 ++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 9e3a08b1f8ae..f4e0e257ff72 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2748,8 +2748,9 @@ static int process_tracing_data(struct feat_fd *ff __maybe_unused, void *data __ return ret < 0 ? -1 : 0; #else - pr_err("ERROR: Trying to read tracing data without libtraceevent support.\n"); - return -1; + /* Not an error — the feature is simply unsupported in this build */ + pr_debug("Tracing data present but libtraceevent not available, skipping.\n"); + return 0; #endif } @@ -3643,8 +3644,9 @@ static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data _ up_write(&env->bpf_progs.lock); return err; #else - pr_err("ERROR: Trying to read bpf_prog_info without libbpf support.\n"); - return -1; + /* Not an error — the feature is simply unsupported in this build */ + pr_debug("BPF prog info present but libbpf not available, skipping.\n"); + return 0; #endif // HAVE_LIBBPF_SUPPORT } @@ -3712,8 +3714,9 @@ static int process_bpf_btf(struct feat_fd *ff __maybe_unused, void *data __mayb free(node); return err; #else - pr_err("ERROR: Trying to read btf data without libbpf support.\n"); - return -1; + /* Not an error — the feature is simply unsupported in this build */ + pr_debug("BTF data present but libbpf not available, skipping.\n"); + return 0; #endif // HAVE_LIBBPF_SUPPORT } @@ -4900,7 +4903,7 @@ int perf_session__read_header(struct perf_session *session) struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; - int nr_attrs, nr_ids, i, j, err; + int nr_attrs, nr_ids, i, j, err = -ENOMEM; int fd = perf_data__fd(data); session->evlist = evlist__new(); @@ -4920,6 +4923,7 @@ int perf_session__read_header(struct perf_session *session) return err; } + err = -ENOMEM; if (perf_file_header__read(&f_header, header, fd) < 0) return -EINVAL; @@ -4997,15 +5001,36 @@ int perf_session__read_header(struct perf_session *session) lseek(fd, tmp, SEEK_SET); } + /* + * Skip feature section processing for truncated files + * (data.size == 0 means recording was interrupted). The + * section table is unreliable in that case, and the event + * data can still be processed without the feature headers. + * Clear the bitmap so has_feat() returns false and tools + * use their "feature not present" fallbacks instead of + * accessing uninitialized env fields. + */ + if (f_header.data.size == 0) { + bitmap_zero(header->adds_features, HEADER_FEAT_BITS); + } else { #ifdef HAVE_LIBTRACEEVENT - perf_header__process_sections(header, fd, &session->tevent, - perf_file_section__process); + err = perf_header__process_sections(header, fd, &session->tevent, + perf_file_section__process); + if (err < 0) + goto out_delete_evlist; - if (evlist__prepare_tracepoint_events(session->evlist, session->tevent.pevent)) - goto out_delete_evlist; + if (evlist__prepare_tracepoint_events(session->evlist, + session->tevent.pevent)) { + err = -ENOMEM; + goto out_delete_evlist; + } #else - perf_header__process_sections(header, fd, NULL, perf_file_section__process); + err = perf_header__process_sections(header, fd, NULL, + perf_file_section__process); + if (err < 0) + goto out_delete_evlist; #endif + } return 0; out_errno: @@ -5014,7 +5039,7 @@ int perf_session__read_header(struct perf_session *session) out_delete_evlist: evlist__delete(session->evlist); session->evlist = NULL; - return -ENOMEM; + return err; } int perf_event__process_feature(const struct perf_tool *tool __maybe_unused, From 7685cc0f2ea34b341524453badf1933142a0961d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:28:06 -0300 Subject: [PATCH 2177/5214] perf header: Validate f_attr.ids section before use in perf_session__read_header() perf_session__read_header() reads f_attr.ids.size from the perf.data file and divides it by sizeof(u64) to compute nr_ids, which is declared as int. No validation is performed on the value before it is used to allocate arrays and drive a read loop. On 32-bit architectures, a crafted f_attr.ids.size of 0x100000000 (4 GB) produces nr_ids = 0x20000000, but the allocation size 1 * 0x20000000 * 8 overflows size_t to 0, so zalloc(0) returns a valid pointer. The subsequent loop writes 0x20000000 IDs into that zero-length buffer, corrupting the heap. On 64-bit, the u64-to-int truncation silently drops high bits, processing fewer IDs than the file claims. While not exploitable, this is a data integrity issue. Add validation before using f_attr.ids: - Cap nr_attrs (attrs.size / attr_size) to MAX_NR_ATTRS (1 << 16) with overflow-safe u64 comparison before assigning to int - Reject ids.size not aligned to sizeof(u64) - Cap ids.size / sizeof(u64) to MAX_IDS_PER_ATTR (1 << 24) to prevent int truncation and size_t overflow on 32-bit - Reject ids sections that extend past the end of the file, guarded by S_ISREG() so non-regular files (block devices, pipes) are not falsely rejected Also fix perf_header__getbuffer64() to set errno = EIO when readn() returns 0 (EOF). Without this, the out_errno path in perf_session__read_header() returns -errno which is 0 (success) on truncated files, causing downstream NULL dereferences. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 77 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f4e0e257ff72..fe23bbd8370c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -64,6 +64,25 @@ #include #endif +/* + * nr_ids * sizeof(struct perf_sample_id) must not overflow + * size_t on 32-bit; the struct is ~104 bytes (32-bit) or + * ~184 bytes (64-bit), so 1<<24 (16M) keeps the product + * under 2 GB on 32-bit. + * + * This is a per-attribute cap only — the total across all + * attributes is not capped because legitimate high-core-count + * workloads (e.g. 5000 tracepoints × 4096 CPUs) can exceed + * a single-attribute limit. + */ +#define MAX_IDS_PER_ATTR (1 << 24) +/* + * Cap nr_attrs to prevent resource exhaustion from crafted + * files. 65536 is well beyond any real workload (perf stat + * typically uses < 100 events) but prevents u64-to-int + * truncation on the attr count. + */ +#define MAX_NR_ATTRS (1 << 16) #define MAX_BPF_DATA_LEN (256 * 1024 * 1024) #define MAX_BPF_PROGS 131072 #define MAX_CACHE_ENTRIES 32768 @@ -4468,8 +4487,13 @@ int perf_session__inject_header(struct perf_session *session, static int perf_header__getbuffer64(struct perf_header *header, int fd, void *buf, size_t size) { - if (readn(fd, buf, size) <= 0) + ssize_t n = readn(fd, buf, size); + + if (n <= 0) { + if (n == 0) + errno = EIO; return -1; + } if (header->needs_swap) mem_bswap_64(buf, size); @@ -4803,6 +4827,8 @@ static int read_attr(int fd, struct perf_header *ph, if (ret <= 0) { pr_debug("cannot read %d bytes of header attr\n", PERF_ATTR_SIZE_VER0); + if (ret == 0) + errno = EIO; return -1; } @@ -4903,6 +4929,7 @@ int perf_session__read_header(struct perf_session *session) struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; + struct stat input_stat; int nr_attrs, nr_ids, i, j, err = -ENOMEM; int fd = perf_data__fd(data); @@ -4951,6 +4978,15 @@ int perf_session__read_header(struct perf_session *session) return -EINVAL; } + if (fstat(fd, &input_stat) < 0) + return -errno; + + /* Check before assigning to int to avoid u64-to-int truncation */ + if (f_header.attrs.size / f_header.attr_size > MAX_NR_ATTRS) { + pr_err("Too many attributes: %" PRIu64 " (max %d)\n", + f_header.attrs.size / f_header.attr_size, MAX_NR_ATTRS); + return -EINVAL; + } nr_attrs = f_header.attrs.size / f_header.attr_size; lseek(fd, f_header.attrs.offset, SEEK_SET); @@ -4967,6 +5003,45 @@ int perf_session__read_header(struct perf_session *session) perf_event__attr_swap(&f_attr.attr); } + /* + * Validate ids section: must be aligned to u64, and + * the count must fit in an int to avoid truncation in + * nr_ids and size_t overflow in perf_evsel__alloc_id() + * on 32-bit architectures. + */ + if (f_attr.ids.size % sizeof(u64)) { + pr_err("Invalid ids section size %" PRIu64 " for attr %d, not aligned to u64\n", + f_attr.ids.size, i); + err = -EINVAL; + goto out_delete_evlist; + } + + /* + * Cap the ID count to avoid int truncation of nr_ids + * on 64-bit and size_t overflow in the allocation + * paths (nr_ids * sizeof(u64), nr_ids * + * sizeof(struct perf_sample_id)) on 32-bit. + */ + if (f_attr.ids.size / sizeof(u64) > MAX_IDS_PER_ATTR) { + pr_err("Invalid ids section size %" PRIu64 " for attr %d, too many IDs\n", + f_attr.ids.size, i); + err = -EINVAL; + goto out_delete_evlist; + } + + /* + * FIXME: see perf_header__process_sections() — block + * devices bypass this check because st_size is 0. + */ + if (S_ISREG(input_stat.st_mode) && + (f_attr.ids.offset > (u64)input_stat.st_size || + f_attr.ids.size > (u64)input_stat.st_size - f_attr.ids.offset)) { + pr_err("Invalid ids section for attr %d: offset=%" PRIu64 " size=%" PRIu64 " exceeds file size %" PRIu64 "\n", + i, f_attr.ids.offset, f_attr.ids.size, (u64)input_stat.st_size); + err = -EINVAL; + goto out_delete_evlist; + } + tmp = lseek(fd, 0, SEEK_CUR); evsel = evsel__new(&f_attr.attr); From b8deecd753441eea76e67f08989189ce6ddf8011 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:32:58 -0300 Subject: [PATCH 2178/5214] perf header: Validate feature section size and add read path bounds checking Harden feature section parsing against crafted perf.data files: 1. perf_header__process_sections() reads the feature section table and passes each section's offset and size directly to the processing callbacks without validating them against the actual file size. A crafted section size would make all downstream bounds checks against ff->size ineffective since they compare against the untrusted, inflated bound. Add an fstat() check with S_ISREG() guard and verify that each section's offset + size does not extend past EOF. 2. __do_read_buf() validates reads against ff->size (section size), but __do_read_fd() had no such check, so a malformed perf.data with an understated section size could cause reads past the end of the current section into the next section's data. Add the bounds check in __do_read(), the common caller of both helpers, so it is enforced uniformly for both the fd and buf paths. Track the section-relative offset in __do_read_fd() so the check works for the fd path. Reject negative sizes which on 32-bit can occur when a u32 >= 0x80000000 is passed as ssize_t. 3. do_read_string() relied on file data being null-padded. Add explicit null-termination (buf[len-1] = '\0') after reading and validate length (>= 1, fits within section) before allocating, so callers like process_cpu_topology() never receive an unterminated string. 4. Initialize feat_fd.offset to 0 (section-relative) instead of section->offset (file-absolute) so the bounds tracking is consistent with __do_read()'s section-relative comparison. Adjust process_build_id() to use lseek() for its file-absolute offset needs since it cannot rely on ff->offset for that. 5. Propagate ff->size to perf_file_section__fprintf_info() so its reads are also bounded. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: David Carrillo-Cisneros Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 66 ++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fe23bbd8370c..90417a478c8d 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -233,23 +233,32 @@ static int __do_read_fd(struct feat_fd *ff, void *addr, ssize_t size) if (ret != size) return ret < 0 ? (int)ret : -1; + ff->offset += size; return 0; } static int __do_read_buf(struct feat_fd *ff, void *addr, ssize_t size) { - if (size > (ssize_t)ff->size - ff->offset) - return -1; - memcpy(addr, ff->buf + ff->offset, size); ff->offset += size; return 0; - } static int __do_read(struct feat_fd *ff, void *addr, ssize_t size) { + /* + * Reject negative sizes, which on 32-bit can occur when a + * u32 >= 0x80000000 is passed as ssize_t. The cast to + * ssize_t is safe because perf_header__process_sections() + * validates that each section fits within the file size + * before any feature callback reaches here, and only + * feature sections (metadata like build IDs, topology, etc.) + * use this path — these cannot legitimately approach 2GB. + */ + if (size < 0 || size > (ssize_t)ff->size - ff->offset) + return -1; + if (!ff->buf) return __do_read_fd(ff, addr, size); return __do_read_buf(ff, addr, size); @@ -289,16 +298,25 @@ static char *do_read_string(struct feat_fd *ff) if (do_read_u32(ff, &len)) return NULL; + /* At least the null terminator. */ + if (len < 1 || len > ff->size - ff->offset) { + pr_debug("do_read_string: invalid length %u (remaining %zu)\n", + len, (size_t)(ff->size - ff->offset)); + return NULL; + } + buf = malloc(len); if (!buf) return NULL; if (!__do_read(ff, buf, len)) { /* - * strings are padded by zeroes - * thus the actual strlen of buf - * may be less than len + * do_write_string() writes len including the null + * terminator, padded to NAME_ALIGN. Ensure the + * string is always null-terminated even if the file + * data has been tampered with. */ + buf[len - 1] = '\0'; return buf; } @@ -2775,7 +2793,13 @@ static int process_tracing_data(struct feat_fd *ff __maybe_unused, void *data __ static int process_build_id(struct feat_fd *ff, void *data __maybe_unused) { - if (perf_header__read_build_ids(ff->ph, ff->fd, ff->offset, ff->size)) + /* lseek fails in pipe mode — fall back to ff->offset */ + off_t offset = lseek(ff->fd, 0, SEEK_CUR); + + if (offset == (off_t)-1) + offset = ff->offset; + + if (perf_header__read_build_ids(ff->ph, ff->fd, offset, ff->size)) pr_debug("Failed to read buildids, continuing...\n"); return 0; } @@ -4152,6 +4176,7 @@ static int perf_file_section__fprintf_info(struct perf_file_section *section, ff = (struct feat_fd) { .fd = fd, .ph = ph, + .size = section->size, }; if (!feat_ops[feat].full_only || hd->full) @@ -4512,6 +4537,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, int sec_size; int feat; int err; + struct stat st; nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); if (!nr_sections) @@ -4529,7 +4555,29 @@ int perf_header__process_sections(struct perf_header *header, int fd, if (err < 0) goto out_free; + if (fstat(fd, &st) < 0) { + pr_err("Failed to stat the perf data file\n"); + err = -1; + goto out_free; + } + for_each_set_bit(feat, header->adds_features, header->last_feat) { + /* + * FIXME: block devices have st_size == 0, so we skip + * bounds checking entirely. Historically perf never + * prevented using a block device as input, but it + * probably should — there's no valid use case for it + * and it bypasses all file-size validation. + */ + if (S_ISREG(st.st_mode) && + (sec->offset > (u64)st.st_size || + sec->size > (u64)st.st_size - sec->offset)) { + pr_err("Feature %s (%d) section extends past EOF (offset=%" PRIu64 ", size=%" PRIu64 ", file=%" PRIu64 ")\n", + header_feat__name(feat), feat, + sec->offset, sec->size, (u64)st.st_size); + err = -1; + goto out_free; + } err = process(sec++, header, feat, fd, data); if (err < 0) goto out_free; @@ -4756,7 +4804,7 @@ static int perf_file_section__process(struct perf_file_section *section, .fd = fd, .ph = ph, .size = section->size, - .offset = section->offset, + .offset = 0, }; if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { From 944f65c8b8231d26d4db6be67bcb641142603cb4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:37:39 -0300 Subject: [PATCH 2179/5214] perf header: Sanity check HEADER_EVENT_DESC attr.size before swap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read_event_desc() reads nre (event count), sz (attr size), and nr (IDs per event) from the file and uses them to control allocations and loops without validating them against the section size. A crafted perf.data could trigger large allocations or many loop iterations before __do_read() eventually rejects the reads. Add bounds checks in read_event_desc(): - Reject sz smaller than PERF_ATTR_SIZE_VER0. - Require at least one event (nre > 0). - Check that nre events fit in the remaining section, using the minimum per-event footprint of sz + sizeof(u32). - Pre-swap attr->size to native byte order, then reject values below PERF_ATTR_SIZE_VER0 or above sz before calling perf_event__attr_swap() to prevent heap out-of-bounds access. - Handle ABI0 (attr.size == 0): substitute PERF_ATTR_SIZE_VER0, and on native-endian files write the value back so free_event_desc() does not treat the zero as its end-of-array sentinel (it iterates while attr.size != 0). The swap path skips the write-back — perf_event__attr_swap() has its own ABI0 fallback that sets VER0 after swapping. - Check that nr IDs fit in the remaining section before allocating. Fixes: b30b61729246 ("perf tools: Fix a problem when opening old perf.data with different byte order") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 90417a478c8d..37d7c9849e0e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2173,9 +2173,28 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (do_read_u32(ff, &nre)) goto error; + /* Size of each of the nre attributes. */ if (do_read_u32(ff, &sz)) goto error; + /* + * Require at least one event with an attr no smaller than the + * first published struct, and reject sz values where + * sz + sizeof(u32) would overflow size_t (possible on 32-bit) + * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. + * + * The minimum section footprint per event is sz bytes for the + * attr plus a u32 for the id count, check that nre events fit. + */ + if (!nre || sz < PERF_ATTR_SIZE_VER0 || + sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || + nre == UINT32_MAX || + nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { + pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", + nre, sz, PERF_ATTR_SIZE_VER0); + goto error; + } + /* buffer to hold on file attr struct */ buf = malloc(sz); if (!buf) @@ -2191,6 +2210,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff) msz = sz; for (i = 0, evsel = events; i < nre; evsel++, i++) { + struct perf_event_attr *attr = buf; + u32 attr_size; + evsel->core.idx = i; /* @@ -2200,6 +2222,32 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (__do_read(ff, buf, sz)) goto error; + /* Reject before attr_swap to prevent OOB via bswap_safe() */ + attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; + /* ABI0: size == 0 means the producer didn't set it */ + if (!attr_size) { + attr_size = PERF_ATTR_SIZE_VER0; + /* + * Write back so free_event_desc() doesn't + * treat this event as the end-of-array sentinel + * (it iterates while attr.size != 0). + * + * Only for native — the swap path must NOT + * write native-endian VER0 here because + * perf_event__attr_swap() would re-swap it + * to 0x40000000, defeating bswap_safe() bounds. + * perf_event__attr_swap() has its own ABI0 + * fallback that sets VER0 after swapping. + */ + if (!ff->ph->needs_swap) + attr->size = attr_size; + } + if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { + pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", + i, attr_size, PERF_ATTR_SIZE_VER0, sz); + goto error; + } + if (ff->ph->needs_swap) perf_event__attr_swap(buf); @@ -2221,6 +2269,12 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (!nr) continue; + /* Prevent oversized allocation from crafted nr */ + if (nr > (ff->size - ff->offset) / sizeof(*id)) { + pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); + goto error; + } + id = calloc(nr, sizeof(*id)); if (!id) goto error; From 3669697bda41c562d90eb38f54881cc02ef3d51c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:41:46 -0300 Subject: [PATCH 2180/5214] perf header: Validate bitmap size before allocating in do_read_bitmap() do_read_bitmap() reads a u64 bit count from the file and passes it to bitmap_zalloc() without checking it against the remaining section size. A crafted perf.data could trigger a large allocation that would only fail later when the per-element reads exceed section bounds. Additionally, bitmap_zalloc() takes an int parameter, so a crafted size with bits set above bit 31 (e.g. 0x100000040) would pass the section bounds check but truncate when passed to bitmap_zalloc(), allocating a much smaller buffer than the subsequent read loop expects. Reject size values that exceed INT_MAX, and check that the data needed (BITS_TO_U64(size) u64 values) fits in the remaining section before allocating. Switch from bitmap_zalloc() to calloc() of u64 units so the allocation size matches the u64 read/write granularity and avoids unsigned long vs u64 mismatch on 32-bit architectures. Fix do_write_bitmap() to use memcpy to read u64-sized chunks from the unsigned long bitmap, preventing out-of-bounds reads on 32-bit systems where sizeof(unsigned long) is 4 but the bitmap is stored in u64 units. Fix process_mem_topology() minimum section size: the check used nr * 2 * sizeof(u64) per node, but do_read_bitmap() reads an additional u64 for the bitmap size, so the minimum is 3 * sizeof(u64). Fix memory leak in process_mem_topology() error paths: replace free(nodes) with memory_node__delete_nodes() to free per-node bitmaps allocated by do_read_bitmap(). Currently used by process_mem_topology() for HEADER_MEM_TOPOLOGY. Fixes: a881fc56038a ("perf header: Sanity check HEADER_MEM_TOPOLOGY") Closes: https://lore.kernel.org/linux-perf-users/20260414224622.2AE69C19425@smtp.kernel.org/ Closes: https://lore.kernel.org/linux-perf-users/20260410223242.DD76FC19421@smtp.kernel.org/ Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 37d7c9849e0e..2fea0172140e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -178,15 +178,25 @@ int do_write(struct feat_fd *ff, const void *buf, size_t size) /* Return: 0 if succeeded, -ERR if failed. */ static int do_write_bitmap(struct feat_fd *ff, unsigned long *set, u64 size) { - u64 *p = (u64 *) set; + size_t byte_size = BITS_TO_LONGS(size) * sizeof(unsigned long); int i, ret; ret = do_write(ff, &size, sizeof(size)); if (ret < 0) return ret; + /* + * The on-disk format uses u64 elements, but the in-memory bitmap + * uses unsigned long, which is only 4 bytes on 32-bit architectures. + * Copy with bounded size so the last element doesn't read past the + * bitmap allocation when BITS_TO_LONGS(size) is odd. + */ for (i = 0; (u64) i < BITS_TO_U64(size); i++) { - ret = do_write(ff, p + i, sizeof(*p)); + u64 val = 0; + size_t off = i * sizeof(val); + + memcpy(&val, (char *)set + off, min(sizeof(val), byte_size - off)); + ret = do_write(ff, &val, sizeof(val)); if (ret < 0) return ret; } @@ -335,7 +345,20 @@ static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize) if (ret) return ret; - set = bitmap_zalloc(size); + /* Bitmap APIs use int for nbits; reject u64 values that truncate. */ + if (size > INT_MAX || + BITS_TO_U64(size) > (ff->size - ff->offset) / sizeof(u64)) { + pr_debug("do_read_bitmap: size %" PRIu64 " exceeds section bounds\n", size); + return -1; + } + + /* + * bitmap_zalloc() allocates in unsigned long units, which are only + * 4 bytes on 32-bit architectures. The read loop below casts the + * buffer to u64 * and writes 8-byte elements, so allocate in u64 + * units to ensure the buffer is large enough. + */ + set = calloc(BITS_TO_U64(size), sizeof(u64)); if (!set) return -ENOMEM; @@ -3497,7 +3520,8 @@ static int process_mem_topology(struct feat_fd *ff, return -1; } - if (ff->size < 3 * sizeof(u64) + nr * 2 * sizeof(u64)) { + /* Per node: node_id(u64) + mem_size(u64) + bitmap_nr_bits(u64) */ + if (ff->size < 3 * sizeof(u64) + nr * 3 * sizeof(u64)) { pr_err("Invalid HEADER_MEM_TOPOLOGY: section too small (%zu) for %llu nodes\n", ff->size, (unsigned long long)nr); return -1; @@ -3532,7 +3556,7 @@ static int process_mem_topology(struct feat_fd *ff, out: if (ret) - free(nodes); + memory_node__delete_nodes(nodes, nr); return ret; } From 1c9d2ac2ead70d4d7c70e7f1276d68ca86e15fa4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 13:22:33 -0300 Subject: [PATCH 2181/5214] perf session: Add byte-swap handler for PERF_RECORD_COMPRESSED2 PERF_RECORD_COMPRESSED2 events carry a data_size field that must be byte-swapped when reading cross-endian perf.data files. Without a swap handler, reading COMPRESSED2 events on a different-endian machine would misinterpret data_size as a garbage value, causing the decompression path to read the wrong number of bytes. The compressed payload itself is a raw byte stream and needs no swapping. Fixes: 208c0e16834472bb ("perf record: Add 8-byte aligned event type PERF_RECORD_COMPRESSED2") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Chun-Tse Shao Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8280413f4528..9271885e3920 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1056,6 +1056,14 @@ static int perf_event__time_conv_swap(union perf_event *event, return 0; } +static int perf_event__compressed2_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + /* Only data_size needs swapping — compressed payload is a raw byte stream */ + event->pack2.data_size = bswap_64(event->pack2.data_size); + return 0; +} + static int perf_event__bpf_metadata_swap(union perf_event *event, bool sample_id_all __maybe_unused) { @@ -1197,6 +1205,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, [PERF_RECORD_TIME_CONV] = perf_event__time_conv_swap, + [PERF_RECORD_COMPRESSED2] = perf_event__compressed2_swap, [PERF_RECORD_BPF_METADATA] = perf_event__bpf_metadata_swap, [PERF_RECORD_SCHEDSTAT_CPU] = perf_event__schedstat_cpu_swap, [PERF_RECORD_SCHEDSTAT_DOMAIN] = perf_event__schedstat_domain_swap, From 6e803f1e0198f436d512f9b0eebd89ec2a85ac85 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:47:38 -0300 Subject: [PATCH 2182/5214] perf tools: Harden compressed event processing Add several hardening checks to the compressed event decompression pipeline: 1. Guard against decomp_last_rem underflow: check that decomp_last->head does not exceed decomp_last->size before subtracting. A u64 underflow here would produce a huge decomp_len, causing an oversized mmap allocation. 2. Validate comp_mmap_len from the HEADER_COMPRESSED feature section: reject values that are not 4K-aligned or smaller than 4096. The downstream decompression path checks allocation sizes against SIZE_MAX, which handles 32-bit safety. 3. Validate COMPRESSED event header size: reject events where header.size is too small to contain the fixed struct fields, preventing underflow in the payload size calculation. 4. Validate COMPRESSED2 event data_size: check that data_size does not exceed the available payload (header.size minus the fixed struct fields) for the newer compressed format. 5. Reject compressed events when the HEADER_COMPRESSED feature is missing from the file header, which means no decompression context was initialized. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 17 +++++++++++++++++ tools/perf/util/tool.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2fea0172140e..f771a76321c1 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -3861,6 +3861,23 @@ static int process_compressed(struct feat_fd *ff, if (do_read_u32(ff, &(env->comp_mmap_len))) return -1; + /* + * FIXME: perf.data should record the recording system's page + * size — it affects mmap buffer alignment, sample addresses, + * and data_page_size/code_page_size interpretation. Without + * it we assume 4K (the smallest Linux page size) as a safe + * minimum alignment for comp_mmap_len validation. + * + * No upper-bound cap: perf_session__process_compressed_event() + * checks decomp_len + sizeof(struct decomp) against SIZE_MAX + * before allocating, which handles 32-bit safety. + */ + if (env->comp_mmap_len < 4096 || env->comp_mmap_len % 4096) { + pr_err("Invalid HEADER_COMPRESSED: comp_mmap_len (%u) must be a 4K-aligned value >= 4096\n", + env->comp_mmap_len); + return -1; + } + return 0; } diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 225a77d530ce..18641919473a 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -24,7 +24,15 @@ static int perf_session__process_compressed_event(const struct perf_tool *tool _ size_t mmap_len, decomp_len = perf_session__env(session)->comp_mmap_len; struct decomp *decomp, *decomp_last = session->active_decomp->decomp_last; + if (!decomp_len) { + pr_err("Compressed events found but HEADER_COMPRESSED not set\n"); + return -1; + } + if (decomp_last) { + /* Prevent u64 underflow in decomp_last_rem */ + if (decomp_last->head > decomp_last->size) + return -1; decomp_last_rem = decomp_last->size - decomp_last->head; decomp_len += decomp_last_rem; } @@ -47,14 +55,37 @@ static int perf_session__process_compressed_event(const struct perf_tool *tool _ decomp->size = decomp_last_rem; } + /* + * Events are read directly from the mmap'd file; fields could + * theoretically change via a FUSE-backed file, but that applies + * to the entire event processing pipeline, not just here. + */ if (event->header.type == PERF_RECORD_COMPRESSED) { + if (event->header.size < sizeof(struct perf_record_compressed)) + goto err_decomp; src = (void *)event + sizeof(struct perf_record_compressed); src_size = event->pack.header.size - sizeof(struct perf_record_compressed); } else if (event->header.type == PERF_RECORD_COMPRESSED2) { + /* + * prefetch_event() only guarantees that the 8-byte + * event header fits; validate that header.size covers + * the data_size field before accessing it, otherwise a + * crafted event reads data_size from adjacent memory. + */ + if (event->header.size < sizeof(struct perf_record_compressed2)) + goto err_decomp; src = (void *)event + sizeof(struct perf_record_compressed2); src_size = event->pack2.data_size; + /* + * data_size is independent of header.size (which + * includes padding); verify it doesn't exceed the + * actual payload to prevent out-of-bounds reads in + * zstd_decompress_stream(). + */ + if (src_size > event->header.size - sizeof(struct perf_record_compressed2)) + goto err_decomp; } else { - return -1; + goto err_decomp; } decomp_size = zstd_decompress_stream(session->active_decomp->zstd_decomp, src, src_size, @@ -77,6 +108,11 @@ static int perf_session__process_compressed_event(const struct perf_tool *tool _ pr_debug("decomp (B): %zd to %zd\n", src_size, decomp_size); return 0; + +err_decomp: + munmap(decomp, mmap_len); + pr_err("Couldn't decompress data\n"); + return -1; } #endif From d88e954c8271c9c47d6d50dcfdd864e6b031d166 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:51:05 -0300 Subject: [PATCH 2183/5214] perf session: Check for decompression buffer size overflow On 32-bit systems, sizeof(struct decomp) + decomp_len can wrap size_t when comp_mmap_len is large. The preceding patch validates comp_mmap_len alignment but does not cap the upper bound, so two additions can still overflow: 1. decomp_len += decomp_last_rem: on 32-bit, adding a u64 to size_t silently truncates, producing a corrupted decomp_len that would bypass the subsequent overflow check and result in an undersized buffer allocation. 2. sizeof(struct decomp) + decomp_len: the final addition could overflow on systems with small size_t. Add explicit overflow checks before each addition as defense-in-depth. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/tool.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 18641919473a..25c9b378aa16 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -34,9 +34,22 @@ static int perf_session__process_compressed_event(const struct perf_tool *tool _ if (decomp_last->head > decomp_last->size) return -1; decomp_last_rem = decomp_last->size - decomp_last->head; + /* + * Check before adding: on 32-bit, size_t += u64 + * silently truncates, bypassing the overflow check + * below and producing an undersized buffer. + */ + if (decomp_last_rem > SIZE_MAX - decomp_len - sizeof(struct decomp)) { + pr_err("Decompression buffer size overflow\n"); + return -1; + } decomp_len += decomp_last_rem; } + if (decomp_len > SIZE_MAX - sizeof(struct decomp)) { + pr_err("Decompression buffer size overflow\n"); + return -1; + } mmap_len = sizeof(struct decomp) + decomp_len; decomp = mmap(NULL, mmap_len, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); From a2e59fb79f449fb43ca277a413f1e1de3c3a6326 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:55:43 -0300 Subject: [PATCH 2184/5214] perf session: Bound nr_cpus_avail and validate sample CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several downstream consumers (timechart, kwork, sched) use fixed-size arrays indexed by CPU. A crafted perf.data can supply arbitrary CPU values that index past these arrays, causing out-of-bounds access. Validate sample.cpu against min(nr_cpus_avail, MAX_NR_CPUS) in perf_session__deliver_event() before any tool callback runs. The cap at MAX_NR_CPUS protects fixed-size downstream arrays; the true nr_cpus_avail is preserved in env for header parsing (e.g. process_cpu_topology) which needs the real count. Fall back to MAX_NR_CPUS when HEADER_NRCPUS is missing (truncated files, pipe mode, pre-2017 perf). Only validate when PERF_SAMPLE_CPU is set in sample_type — when absent, evsel__parse_sample() leaves sample.cpu as (u32)-1, a sentinel that downstream tools (script, inject) check to identify events without CPU info. Clamping it to 0 would break those checks. Inline evlist__parse_sample() into perf_session__deliver_event() so the evsel lookup needed for sample_type checking reuses the same evsel that parsed the sample, avoiding a second evlist__event2evsel() call on every event. For pipe-mode streams where HEADER_NRCPUS may arrive late or not at all, the MAX_NR_CPUS fallback ensures the bounds check is still effective against the fixed-size downstream arrays. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 30 +++++++++++++ tools/perf/util/session.c | 88 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f771a76321c1..5b1fa1653d2a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -48,6 +48,7 @@ #include #include "asm/bug.h" #include "tool.h" +#include "../perf.h" #include "time-utils.h" #include "units.h" #include "util/util.h" // perf_exe() @@ -2895,6 +2896,17 @@ static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused) if (ret) return ret; + /* + * Cap at 1M CPUs — generous for any real system but prevents + * stack overflow from VLA allocations sized by nr_cpus_avail + * (e.g. DECLARE_BITMAP in builtin-c2c.c node_entry()). + */ + if (nr_cpus_avail > (1U << 20)) { + pr_err("Invalid HEADER_NRCPUS: nr_cpus_avail (%u) exceeds maximum (%u)\n", + nr_cpus_avail, 1U << 20); + return -1; + } + if (nr_cpus_online > nr_cpus_avail) { pr_err("Invalid HEADER_NRCPUS: nr_cpus_online (%u) > nr_cpus_avail (%u)\n", nr_cpus_online, nr_cpus_avail); @@ -5250,6 +5262,24 @@ int perf_session__read_header(struct perf_session *session) #endif } + /* + * Without nr_cpus_avail the sample CPU bounds check in + * perf_session__deliver_event() is bypassed, allowing crafted + * CPU IDs to reach downstream consumers that index fixed-size + * arrays (timechart, kwork, sched — all sized MAX_NR_CPUS). + * + * This can happen with truncated files (interrupted recording + * loses all feature sections), very old files that predate + * HEADER_NRCPUS, or crafted files that omit it. Fall back to + * MAX_NR_CPUS so the bounds check is still effective — any + * CPU ID below that limit is safe for all downstream arrays. + */ + if (header->env.nr_cpus_avail == 0) { + header->env.nr_cpus_avail = MAX_NR_CPUS; + pr_warning("WARNING: perf.data is missing HEADER_NRCPUS, using MAX_NR_CPUS (%d) as CPU bound\n", + MAX_NR_CPUS); + } + return 0; out_errno: return -errno; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9271885e3920..6de665d3c905 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2110,14 +2110,100 @@ static int perf_session__deliver_event(struct perf_session *session, const char *file_path) { struct perf_sample sample; + struct evsel *evsel; int ret; perf_sample__init(&sample, /*all=*/false); - ret = evlist__parse_sample(session->evlist, event, &sample); + evsel = evlist__event2evsel(session->evlist, event); + if (!evsel) { + pr_err("No evsel found for event type %u\n", + event->header.type); + ret = -EFAULT; + goto out; + } + ret = evsel__parse_sample(evsel, event, &sample); if (ret) { pr_err("Can't parse sample, err = %d\n", ret); goto out; } + /* + * evsel__parse_sample() doesn't populate machine_pid/vcpu, + * which are needed by machines__find_for_cpumode() to + * attribute samples to guest VMs. The SID table maps + * sample IDs to the guest that owns the event. + */ + if (perf_guest && sample.id) { + struct perf_sample_id *sid = evlist__id2sid(session->evlist, sample.id); + + if (sid) { + sample.machine_pid = sid->machine_pid; + sample.vcpu = sid->vcpu.cpu; + } + } + + /* + * Validate sample.cpu before any callback can use it as an + * array index (kwork cpus_runtime, timechart cpus_cstate_*, + * sched cpu_last_switched). + * + * When PERF_SAMPLE_CPU is absent, evsel__parse_sample() leaves + * sample.cpu as (u32)-1 — a sentinel that downstream tools + * (script, inject) check to identify events without CPU info. + * Only check when sample.cpu was actually populated from event + * data: PERF_RECORD_SAMPLE always has it when PERF_SAMPLE_CPU + * is set; non-sample events only have it when sample_id_all is + * enabled. Otherwise sample.cpu is the (u32)-1 sentinel from + * evsel__parse_sample() and must not be validated or clamped. + */ + if ((evsel->core.attr.sample_type & PERF_SAMPLE_CPU) && + (event->header.type == PERF_RECORD_SAMPLE || + evsel->core.attr.sample_id_all)) { + int nr_cpus_avail = perf_session__env(session)->nr_cpus_avail; + + /* + * For perf.data files the MAX_NR_CPUS fallback in + * perf_session__read_header() guarantees this is set. + * For pipe mode, HEADER_NRCPUS may arrive late or not + * at all (pre-2017 perf, third-party tools). Fall + * back to MAX_NR_CPUS so the bounds check still works + * against fixed-size downstream arrays. + * + * Do NOT write back to env: this function runs during + * recording (synthesized events) when nr_cpus_avail is + * legitimately 0. Writing MAX_NR_CPUS would cause + * write_cpu_topology() to emit 4096 core_id/socket_id + * pairs instead of the real CPU count, corrupting the + * topology section in the generated perf.data. + */ + if (nr_cpus_avail <= 0) + nr_cpus_avail = MAX_NR_CPUS; + /* + * Cap at MAX_NR_CPUS for the bounds check — downstream + * consumers use fixed-size arrays of that size. Keep + * the true nr_cpus_avail in env for header parsing + * (e.g. process_cpu_topology) which needs the real count. + */ + if (nr_cpus_avail > MAX_NR_CPUS) + nr_cpus_avail = MAX_NR_CPUS; + if (sample.cpu >= (u32)nr_cpus_avail && + sample.cpu != (u32)-1) { + /* + * Warn rather than abort: synthesized events + * (MMAP, COMM) lack sample_id_all data, so + * parse_id_sample reads garbage from the event + * payload. Clamping to 0 protects downstream + * array indexing while keeping the session alive. + * + * Preserve (u32)-1: perf script and perf inject + * use it as a sentinel for "CPU not applicable." + * Downstream array users (timechart, kwork) have + * their own per-callback bounds checks. + */ + pr_warning_once("WARNING: sample CPU %u >= nr_cpus_avail %u, clamping to 0\n", + sample.cpu, nr_cpus_avail); + sample.cpu = 0; + } + } ret = auxtrace__process_event(session, event, &sample, tool); if (ret < 0) From 48d004c0742dcbc940ffb3b7390540c5a200729e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 3 May 2026 13:05:51 -0300 Subject: [PATCH 2185/5214] perf kwork: Bounds check work->cpu before indexing cpus_runtime[] work->cpu comes from sample->cpu which is (u32)-1 when PERF_SAMPLE_CPU is absent. Stored as int, this becomes -1 which passes the signed BUG_ON(work->cpu >= MAX_NR_CPUS) but causes an out-of-bounds access on cpus_runtime[-1]. Replace the BUG_ON in top_calc_total_runtime() with an unsigned bounds check that skips entries with invalid CPU values, counting them for a summary warning. Guard the same index in profile_event_match() (bitmap OOB), top_calc_idle_time(), top_calc_irq_runtime(), top_calc_cpu_usage(), and top_calc_load_runtime(). Also guard against division by zero in top_calc_cpu_usage() when no runtime was accumulated. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Yang Jihong Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kwork.c | 45 +++++++++++++++++++++++++++++++++----- tools/perf/util/kwork.h | 1 + 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index f793ea578515..99dc293a0744 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -467,7 +467,9 @@ static bool profile_event_match(struct perf_kwork *kwork, u64 time = sample->time; struct perf_time_interval *ptime = &kwork->ptime; - if ((kwork->cpu_list != NULL) && !test_bit(cpu, kwork->cpu_bitmap)) + /* Guard test_bit: cpu == -1 (absent PERF_SAMPLE_CPU) would index past the bitmap */ + if ((kwork->cpu_list != NULL) && + ((unsigned int)cpu >= MAX_NR_CPUS || !test_bit(cpu, kwork->cpu_bitmap))) return false; if (((ptime->start != 0) && (ptime->start > time)) || @@ -2041,7 +2043,18 @@ static void top_calc_total_runtime(struct perf_kwork *kwork) next = rb_first_cached(&class->work_root); while (next) { work = rb_entry(next, struct kwork_work, node); - BUG_ON(work->cpu >= MAX_NR_CPUS); + /* + * work->cpu comes from sample->cpu which is -1 when + * PERF_SAMPLE_CPU is absent. As int that's -1, but as + * unsigned it exceeds MAX_NR_CPUS — skip to avoid OOB + * on cpus_runtime[]. + */ + /* Counted and reported in perf_kwork__top_report() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) { + stat->nr_skipped_cpu++; + next = rb_next(next); + continue; + } stat->cpus_runtime[work->cpu].total += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].total += work->total_runtime; next = rb_next(next); @@ -2053,7 +2066,8 @@ static void top_calc_idle_time(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; - if (work->id == 0) { + /* See comment in top_calc_total_runtime() */ + if (work->id == 0 && (unsigned int)work->cpu < MAX_NR_CPUS) { stat->cpus_runtime[work->cpu].idle += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].idle += work->total_runtime; } @@ -2065,6 +2079,10 @@ static void top_calc_irq_runtime(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; + /* See comment in top_calc_total_runtime() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) + return; + if (type == KWORK_CLASS_IRQ) { stat->cpus_runtime[work->cpu].irq += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].irq += work->total_runtime; @@ -2117,12 +2135,19 @@ static void top_calc_cpu_usage(struct perf_kwork *kwork) if (work->total_runtime == 0) goto next; + /* See comment in top_calc_total_runtime() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) + goto next; + __set_bit(work->cpu, stat->all_cpus_bitmap); top_subtract_irq_runtime(kwork, work); - work->cpu_usage = work->total_runtime * 10000 / - stat->cpus_runtime[work->cpu].total; + /* Guard against division by zero if no runtime was accumulated */ + if (stat->cpus_runtime[work->cpu].total) { + work->cpu_usage = work->total_runtime * 10000 / + stat->cpus_runtime[work->cpu].total; + } top_calc_idle_time(kwork, work); next: @@ -2135,7 +2160,8 @@ static void top_calc_load_runtime(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; - if (work->id != 0) { + /* See comment in top_calc_total_runtime() */ + if (work->id != 0 && (unsigned int)work->cpu < MAX_NR_CPUS) { stat->cpus_runtime[work->cpu].load += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].load += work->total_runtime; } @@ -2211,6 +2237,13 @@ static void perf_kwork__top_report(struct perf_kwork *kwork) next = rb_next(next); } + if (kwork->top_stat.nr_skipped_cpu) { + printf(" Warning: %u work entries with invalid CPU were excluded from totals.\n" + " Task runtimes may appear inflated (IRQ time not subtracted).\n" + " Consider re-recording with PERF_SAMPLE_CPU enabled.\n", + kwork->top_stat.nr_skipped_cpu); + } + printf("\n"); } diff --git a/tools/perf/util/kwork.h b/tools/perf/util/kwork.h index 81d39a7f78c8..6ec70dcc4157 100644 --- a/tools/perf/util/kwork.h +++ b/tools/perf/util/kwork.h @@ -202,6 +202,7 @@ struct __top_cpus_runtime { struct kwork_top_stat { DECLARE_BITMAP(all_cpus_bitmap, MAX_NR_CPUS); struct __top_cpus_runtime *cpus_runtime; + unsigned int nr_skipped_cpu; }; struct perf_kwork { From f6ded4d1e8b5e29278605e89c893197fb484c2a6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 23 May 2026 15:30:15 -0300 Subject: [PATCH 2186/5214] perf session: Snapshot event->header.size in process_user_event() On native-endian files, events are read from MAP_SHARED memory. Multiple reads of event->header.size can return different values if the file is concurrently modified, allowing an attacker to bypass bounds checks performed on an earlier read. Snapshot header.size into a local variable at function entry using READ_ONCE() to prevent compiler rematerialization, and use it for all size-dependent arithmetic within the function. This ensures every bounds calculation uses the same value that was validated by the reader. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 6de665d3c905..e2e821b77766 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2230,6 +2230,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, { struct ordered_events *oe = &session->ordered_events; const struct perf_tool *tool = session->tool; + const u32 event_size = READ_ONCE(event->header.size); struct perf_sample sample; int fd = perf_data__fd(session->data); s64 err; @@ -2271,7 +2272,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, break; case PERF_RECORD_HEADER_BUILD_ID: if (!perf_event__check_nul(event->build_id.filename, - (void *)event + event->header.size, + (void *)event + event_size, "HEADER_BUILD_ID")) { err = 0; break; @@ -2294,7 +2295,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, * place already. */ if (!perf_data__is_pipe(session->data)) - lseek(fd, file_offset + event->header.size, SEEK_SET); + lseek(fd, file_offset + event_size, SEEK_SET); err = tool->auxtrace(tool, session, event); break; case PERF_RECORD_AUXTRACE_ERROR: @@ -2304,14 +2305,14 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_THREAD_MAP: { u64 max_nr; - if (event->header.size < sizeof(event->thread_map)) { + if (event_size < sizeof(event->thread_map)) { pr_err("PERF_RECORD_THREAD_MAP: header.size (%u) too small\n", - event->header.size); + event_size); err = -EINVAL; break; } - max_nr = (event->header.size - sizeof(event->thread_map)) / + max_nr = (event_size - sizeof(event->thread_map)) / sizeof(event->thread_map.entries[0]); if (event->thread_map.nr > max_nr) { pr_err("PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n", @@ -2325,7 +2326,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, } case PERF_RECORD_CPU_MAP: { struct perf_record_cpu_map_data *data = &event->cpu_map.data; - u32 payload = event->header.size - sizeof(event->header); + u32 payload = event_size - sizeof(event->header); /* * Native-endian events are mmap'd read-only, so we @@ -2389,8 +2390,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, break; } case PERF_RECORD_STAT_CONFIG: { - /* Cannot underflow: perf_event__min_size[] guarantees header.size >= sizeof */ - u64 max_nr = (event->header.size - sizeof(event->stat_config)) / + /* Cannot underflow: perf_event__min_size[] guarantees event_size >= sizeof */ + u64 max_nr = (event_size - sizeof(event->stat_config)) / sizeof(event->stat_config.data[0]); /* @@ -2421,7 +2422,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, */ memset(&session->time_conv, 0, sizeof(session->time_conv)); memcpy(&session->time_conv, &event->time_conv, - min((size_t)event->header.size, sizeof(session->time_conv))); + min((size_t)event_size, sizeof(session->time_conv))); err = tool->time_conv(tool, session, event); break; case PERF_RECORD_HEADER_FEATURE: @@ -2438,11 +2439,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, break; case PERF_RECORD_BPF_METADATA: { u64 nr_entries, max_entries; - u32 hdr_size = READ_ONCE(event->header.size); - if (hdr_size < sizeof(event->bpf_metadata)) { + if (event_size < sizeof(event->bpf_metadata)) { pr_warning("WARNING: PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n", - hdr_size); + event_size); err = 0; break; } @@ -2458,9 +2458,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, break; } - /* Snapshot — event is mmap'd and could change between reads */ nr_entries = READ_ONCE(event->bpf_metadata.nr_entries); - max_entries = (hdr_size - sizeof(event->bpf_metadata)) / + max_entries = (event_size - sizeof(event->bpf_metadata)) / sizeof(event->bpf_metadata.entries[0]); if (nr_entries > max_entries) { pr_warning("WARNING: PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n", From f86446669053125485a4721a722b446e1c3e5efa Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 9 May 2026 23:55:07 -0300 Subject: [PATCH 2187/5214] perf test: Add truncated perf.data robustness test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a shell test that verifies perf report handles truncated perf.data files gracefully — exiting with an error code rather than crashing with SIGSEGV or SIGABRT. The test records a simple workload, then truncates the resulting perf.data at four offsets that exercise different parsing stages: 8 bytes — file header magic only 64 bytes — partial file header (attr section incomplete) 256 bytes — into the first events (partial event headers) 75% size — mid-stream truncation (partial event data) For each truncation, perf report is run and the exit code is checked: - Exit code 0 (success) fails the test — a truncated file should never parse without error. - Crash signals are detected portably via kill -l, which maps the signal number to a name on the running system. This handles architectures where signal numbers differ (e.g. SIGBUS is 7 on x86/ARM but 10 on MIPS/SPARC). Core-dump and fatal signals (KILL, ILL, ABRT, BUS, FPE, SEGV, TRAP, SYS) fail the test. - Higher exit codes (200+) are perf's own negative-errno returns (e.g. -EINVAL = 234) and are expected. This exercises the bounds checking, minimum-size validation, and error propagation added by the preceding patches in this series. Testing it: root@number:~# perf test truncat 84: Test that perf report handles truncated perf.data gracefully (no crash, no segfault — clean error exit).: Ok root@number:~# perf test -vv truncat 84: Test that perf report handles truncated perf.data gracefully (no crash, no segfault — clean error exit).: --- start --- test child forked, pid 62890 ---- end(0) ---- 84: Test that perf report handles truncated perf.data gracefully (no crash, no segfault — clean error exit).: Ok root@number:~# Changes in v2: - Add SIGKILL to the list of fatal signals so OOM kills from resource exhaustion bugs are detected (Reported-by: sashiko-bot@kernel.org) Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m [ Fixed the SPDX on the line where 'perf test' expects the test description, reviewed by Ian Rogers ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/data_validation.sh | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100755 tools/perf/tests/shell/data_validation.sh diff --git a/tools/perf/tests/shell/data_validation.sh b/tools/perf/tests/shell/data_validation.sh new file mode 100755 index 000000000000..bf16b2f2b911 --- /dev/null +++ b/tools/perf/tests/shell/data_validation.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# Test that perf report handles truncated perf.data gracefully (no crash, no segfault — clean error exit). +# SPDX-License-Identifier: GPL-2.0 +# +# Exercises the bounds checking and minimum-size validation added +# by the perf-data-validation hardening series. + +err=0 + +cleanup() { + [ -n "${perfdata}" ] && rm -f "${perfdata}" "${perfdata}.old" + rm -f "${truncated}" "${stderrfile}" + trap - EXIT TERM INT +} +trap 'cleanup; exit 1' TERM INT +trap cleanup EXIT + +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 +truncated=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 +stderrfile=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 + +# Record a simple workload +if ! perf record -o "${perfdata}" -- perf test -w noploop 2>/dev/null; then + echo "Skip: perf record failed" + cleanup + exit 2 +fi + +file_size=$(wc -c < "${perfdata}") +if [ "${file_size}" -lt 512 ]; then + echo "Skip: perf.data too small (${file_size} bytes)" + cleanup + exit 2 +fi + +# Test truncation at various offsets that exercise different +# parsing stages: +# 8 — file header magic only, no attrs or data +# 64 — partial file header (attr section incomplete) +# 256 — into the first events (partial event headers) +# 75% — mid-stream truncation (partial event data) +for cut_at in 8 64 256 $((file_size * 3 / 4)); do + if [ "${cut_at}" -ge "${file_size}" ]; then + continue + fi + dd if="${perfdata}" of="${truncated}" bs="${cut_at}" count=1 2>/dev/null + + # perf report should exit with an error, not crash. + # Capture stderr to detect sanitizer violations. + perf report -i "${truncated}" --stdio > /dev/null 2> "${stderrfile}" + exit_code=$? + + # A truncated file should never parse successfully + if [ ${exit_code} -eq 0 ]; then + echo "FAIL: perf report exited 0 (success) on ${cut_at}-byte truncated file — expected an error" + err=1 + continue + fi + + # Detect sanitizer violations — ASAN/MSAN/TSAN/UBSAN exit + # with code 1 by default, which would otherwise look like a + # clean error exit. Check stderr for their markers. + if grep -qE "^(==[0-9]+==ERROR:|SUMMARY: [A-Za-z]*Sanitizer)" "${stderrfile}" 2>/dev/null; then + sanitizer=$(grep -oE "(Address|Memory|Thread|UndefinedBehavior)Sanitizer" "${stderrfile}" | head -1) + echo "FAIL: perf report triggered ${sanitizer:-sanitizer} on ${cut_at}-byte truncated file" + err=1 + continue + fi + + # Detect crash signals portably — signal numbers differ + # across architectures (e.g. SIGBUS is 7 on x86/ARM but + # 10 on MIPS/SPARC). Use kill -l to map the number to a + # name on the running system. + if [ ${exit_code} -gt 128 ] && [ ${exit_code} -lt 200 ]; then + sig_name=$(kill -l $((exit_code - 128)) 2>/dev/null) + case ${sig_name} in + KILL|ILL|ABRT|BUS|FPE|SEGV|TRAP|SYS) + echo "FAIL: perf report crashed (SIG${sig_name}) on ${cut_at}-byte truncated file" + err=1 + ;; + esac + fi +done + +cleanup +exit ${err} From 6f31bb737ef8c519e0d58d39d93a5352b920b9b7 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 11 May 2026 10:19:35 +0100 Subject: [PATCH 2188/5214] perf test: Make leafloop workload immune to compiler options Since the leafloop test program was moved into the main Perf binary as a workload, it inherited the same compiler options as Perf. In this case the -fstack-protector option broke the assumption that simple leaf frames don't have a stack frame on Arm. This causes test_arm_callgraph_fp.sh to pass even if the stack isn't augmented with the link register, making the test useless. Fix it by rewriting the leaf function in assembly seeing as it's so simple. Adding -fno-stack-protector would also work, but wouldn't be robust against other future compiler option additions. The local variables and 'a' variable were never needed so remove them to simplify. Reviewed-by: Ian Rogers Assisted-by: GitHub-Copilot:GPT-5.5 Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/workloads/leafloop.c | 40 +++++++++++++++++++++------ 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/workloads/leafloop.c b/tools/perf/tests/workloads/leafloop.c index f7561767e32c..c20c75f7ba49 100644 --- a/tools/perf/tests/workloads/leafloop.c +++ b/tools/perf/tests/workloads/leafloop.c @@ -6,26 +6,48 @@ #include "../tests.h" /* We want to check these symbols in perf script */ -noinline void leaf(volatile int b); -noinline void parent(volatile int b); +noinline void leaf(void); +noinline void parent(void); -static volatile int a; -static volatile sig_atomic_t done; +static volatile sig_atomic_t done asm("leafloop_done"); static void sighandler(int sig __maybe_unused) { done = 1; } -noinline void leaf(volatile int b) +#if defined(__aarch64__) +/* + * Write leaf() in assembly so it stays as a minimal leaf function with no + * stack frame and won't get silently broken in the future by any Perf wide + * compilation options like -fstack-protector-all. + */ +asm( + ".pushsection .text,\"ax\",%progbits\n" + ".global leaf\n" + ".type leaf, %function\n" + "leaf:\n" + " adrp x1, leafloop_done\n" + " ldr w2, [x1, #:lo12:leafloop_done]\n" + " cbz w2, leaf\n" + " ret\n" + ".size leaf, .-leaf\n" + ".popsection\n" +); + +#else + +noinline void leaf(void) { while (!done) - a += b; + ; } -noinline void parent(volatile int b) +#endif + +noinline void parent(void) { - leaf(b); + leaf(); } static int leafloop(int argc, const char **argv) @@ -39,7 +61,7 @@ static int leafloop(int argc, const char **argv) signal(SIGALRM, sighandler); alarm(sec); - parent(sec); + parent(); return 0; } From edfbf3a61b9f9defa337377e2904677a41edfbab Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Tue, 26 May 2026 22:16:10 -0700 Subject: [PATCH 2189/5214] dt-bindings: remoteproc: xlnx: Add firmware-name property The firmware-name property indicates which firmware to load on RPU during the Linux boot time. It is possible to stop the RPU after boot and load different firmware and start RPU. Signed-off-by: Tanmay Shah Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260527051611.194844-2-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- .../devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml b/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml index ee63c03949c9..ae63c3e39ced 100644 --- a/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml +++ b/Documentation/devicetree/bindings/remoteproc/xlnx,zynqmp-r5fss.yaml @@ -135,6 +135,10 @@ patternProperties: - description: vring1 additionalItems: true + firmware-name: + maxItems: 1 + description: default firmware to load + required: - compatible - reg From a16272779b8a4194fd90c767ff1b347dcd55ddac Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Tue, 26 May 2026 22:16:11 -0700 Subject: [PATCH 2190/5214] remoteproc: xlnx: Enable auto boot feature The remoteproc framework has capability to start (or attach to) the remote processor automatically if auto boot flag is set by the driver during probe. If the 'firmware-name' property is available for the remoteproc node, then that firmware will be loaded and started during auto boot. If the remote core is started by the bootloader then during auto-boot remoteproc framework will try to attach to the remote processor. The current architecture allocates and adds the remoteproc instance before all the hardware such as sram, mbox, TCM is initialized. This design has to be changed for auto boot to work. So, rename zynqmp_r5_rproc_add() function to zynqmp_r5_rproc_alloc() and move adding the remoteproc instance at the end of cluster initialization. This makes sure that all the required hardware is initialized before starting the remote processor. Signed-off-by: Tanmay Shah Link: https://lore.kernel.org/r/20260527051611.194844-3-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/xlnx_r5_remoteproc.c | 48 +++++++++++++++++-------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index f5b736fa3cb4..3349d1877751 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -903,17 +903,18 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { }; /** - * zynqmp_r5_add_rproc_core() - Add core data to framework. - * Allocate and add struct rproc object for each r5f core + * zynqmp_r5_alloc_rproc_core() - alloc rproc core data structure + * Allocate struct rproc object for each r5f core * This is called for each individual r5f core * * @cdev: Device node of each r5 core * * Return: zynqmp_r5_core object for success else error code pointer */ -static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) +static struct zynqmp_r5_core *zynqmp_r5_alloc_rproc_core(struct device *cdev) { struct zynqmp_r5_core *r5_core; + const char *fw_name = NULL; struct rproc *r5_rproc; int ret; @@ -922,10 +923,15 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) if (ret) return ERR_PTR(ret); + ret = rproc_of_parse_firmware(cdev, 0, &fw_name); + if (ret < 0 && ret != -EINVAL) + return ERR_PTR(dev_err_probe(cdev, ret, + "failed to parse firmware-name\n")); + /* Allocate remoteproc instance */ r5_rproc = rproc_alloc(cdev, dev_name(cdev), &zynqmp_r5_rproc_ops, - NULL, sizeof(struct zynqmp_r5_core)); + fw_name, sizeof(struct zynqmp_r5_core)); if (!r5_rproc) { dev_err(cdev, "failed to allocate memory for rproc instance\n"); return ERR_PTR(-ENOMEM); @@ -936,6 +942,11 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) r5_rproc->recovery_disabled = true; r5_rproc->has_iommu = false; r5_rproc->auto_boot = false; + + /* attempt to boot automatically if the firmware-name is provided */ + if (fw_name) + r5_rproc->auto_boot = true; + r5_core = r5_rproc->priv; r5_core->dev = cdev; r5_core->np = dev_of_node(cdev); @@ -945,13 +956,6 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } - /* Add R5 remoteproc core */ - ret = rproc_add(r5_rproc); - if (ret) { - dev_err(cdev, "failed to add r5 remoteproc\n"); - goto free_rproc; - } - r5_core->rproc = r5_rproc; return r5_core; @@ -1284,6 +1288,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, if (zynqmp_r5_get_rsc_table_va(r5_core)) dev_dbg(r5_core->dev, "rsc tbl not found\n"); r5_core->rproc->state = RPROC_DETACHED; + r5_core->rproc->auto_boot = true; } } @@ -1308,7 +1313,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) enum rpu_oper_mode fw_reg_val; struct device **child_devs; enum rpu_tcm_comb tcm_mode; - int core_count, ret, i; + int core_count, ret, i, j; struct mbox_info *ipi; ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode); @@ -1394,7 +1399,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) child_devs[i] = &child_pdev->dev; /* create and add remoteproc instance of type struct rproc */ - r5_cores[i] = zynqmp_r5_add_rproc_core(&child_pdev->dev); + r5_cores[i] = zynqmp_r5_alloc_rproc_core(&child_pdev->dev); if (IS_ERR(r5_cores[i])) { ret = PTR_ERR(r5_cores[i]); r5_cores[i] = NULL; @@ -1439,16 +1444,31 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) goto release_r5_cores; } + for (j = 0; j < cluster->core_count; j++) { + /* Add R5 remoteproc core */ + ret = rproc_add(r5_cores[j]->rproc); + if (ret) { + dev_err_probe(r5_cores[j]->dev, ret, + "failed to add remoteproc\n"); + goto delete_r5_cores; + } + } + kfree(child_devs); return 0; +delete_r5_cores: + i = core_count - 1; + /* delete previous added rproc */ + while (--j >= 0) + rproc_del(r5_cores[j]->rproc); + release_r5_cores: while (i >= 0) { put_device(child_devs[i]); if (r5_cores[i]) { zynqmp_r5_free_mbox(r5_cores[i]->ipi); of_reserved_mem_device_release(r5_cores[i]->dev); - rproc_del(r5_cores[i]->rproc); rproc_free(r5_cores[i]->rproc); } i--; From bfda3b9424e02c6b2afac74410457c9c1743ba4a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 22 May 2026 14:44:07 -0700 Subject: [PATCH 2191/5214] tracing: Turn hist_elt_data field_var_str into a flexible array The field_var_str array was allocated separately via kcalloc() with its length already known at elt_data allocation time. Convert it to a flexible array member and fold the two allocations into a single kzalloc_flex(), reordering hist_trigger_elt_data_alloc() so n_str is computed and bounds-checked before the struct allocation. hist_elt_data is only reached through tracing_map_elt::private_data (a void *), never embedded, so adding a FAM imposes no tail-position constraint on any enclosing struct. Added __counted_by for extra runtime analysis. Link: https://patch.msgid.link/20260522214407.18120-1-rosenp@gmail.com Assisted-by: Claude:Opus-4.7 Signed-off-by: Rosen Penev Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_hist.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 9701650c89b2..82ce492ab268 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -683,8 +683,8 @@ struct track_data { struct hist_elt_data { char *comm; u64 *var_ref_vals; - char **field_var_str; int n_field_var_str; + char *field_var_str[] __counted_by(n_field_var_str); }; struct snapshot_context { @@ -1629,8 +1629,6 @@ static void hist_elt_data_free(struct hist_elt_data *elt_data) for (i = 0; i < elt_data->n_field_var_str; i++) kfree(elt_data->field_var_str[i]); - kfree(elt_data->field_var_str); - kfree(elt_data->comm); kfree(elt_data); } @@ -1650,10 +1648,19 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) struct hist_field *hist_field; unsigned int i, n_str; - elt_data = kzalloc_obj(*elt_data); + BUILD_BUG_ON(STR_VAR_LEN_MAX & (sizeof(u64) - 1)); + + n_str = hist_data->n_field_var_str + hist_data->n_save_var_str + + hist_data->n_var_str; + if (n_str > SYNTH_FIELDS_MAX) + return -EINVAL; + + elt_data = kzalloc_flex(*elt_data, field_var_str, n_str); if (!elt_data) return -ENOMEM; + elt_data->n_field_var_str = n_str; + for_each_hist_field(i, hist_data) { hist_field = hist_data->fields[i]; @@ -1667,24 +1674,8 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt) } } - n_str = hist_data->n_field_var_str + hist_data->n_save_var_str + - hist_data->n_var_str; - if (n_str > SYNTH_FIELDS_MAX) { - hist_elt_data_free(elt_data); - return -EINVAL; - } - - BUILD_BUG_ON(STR_VAR_LEN_MAX & (sizeof(u64) - 1)); - size = STR_VAR_LEN_MAX; - elt_data->field_var_str = kcalloc(n_str, sizeof(char *), GFP_KERNEL); - if (!elt_data->field_var_str) { - hist_elt_data_free(elt_data); - return -EINVAL; - } - elt_data->n_field_var_str = n_str; - for (i = 0; i < n_str; i++) { elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL); if (!elt_data->field_var_str[i]) { From 01046072880b654dbadf71be2f645aad4a7b5d87 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Mon, 25 May 2026 19:04:28 +0200 Subject: [PATCH 2192/5214] tracing: Disable KCOV instrumentation for trace_irqsoff.o When KCOV runs its boot selftest with whole-kernel instrumentation enabled, it sets current->kcov_mode to KCOV_MODE_TRACE_PC without installing a coverage area. Any instrumented code accepted as task-context coverage in that window dereferences current->kcov_area and crashes. On ARMv5 Versatile PB with CONFIG_KCOV_SELFTEST=y, CONFIG_KCOV_INSTRUMENT_ALL=y and CONFIG_IRQSOFF_TRACER=y, boot hits a NULL pointer fault during the selftest: kcov: running self test Internal error: Oops: 5 [#1] ARM PC is at __sanitizer_cov_trace_pc+0x4c/0x90 Kernel panic - not syncing: Fatal exception A diagnostic run showed the unwanted coverage comes from the IRQs-off tracer callbacks reached from ARM IRQ entry before hardirq context is visible to KCOV: __sanitizer_cov_trace_pc from tracer_hardirqs_off+0x18/0x1cc tracer_hardirqs_off from trace_hardirqs_off+0x34/0x54 trace_hardirqs_off from __irq_svc+0x58/0xb0 __irq_svc from kcov_init+0x7c/0xdc and similarly through tracer_hardirqs_on(). trace_preemptirq.o is already excluded because this tracing path can run from early interrupt code and produce coverage unrelated to syscall inputs. Exclude trace_irqsoff.o as well, instead of requiring users to turn off CONFIG_KCOV_INSTRUMENT_ALL=y, which is the default whole-kernel KCOV mode. With the exclusion in place, the same ARMv5 Versatile PB QEMU test boots through the KCOV selftest and reaches userspace. Tested on ARMv5 Versatile PB QEMU with CONFIG_KCOV_SELFTEST=y, CONFIG_KCOV_INSTRUMENT_ALL=y and CONFIG_IRQSOFF_TRACER=y. Link: https://patch.msgid.link/20260525170428.67211-1-kmehltretter@gmail.com Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Signed-off-by: Steven Rostedt --- kernel/trace/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 9b0834134cae..660675e1d426 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -48,9 +48,10 @@ ifdef CONFIG_GCOV_PROFILE_FTRACE GCOV_PROFILE := y endif -# Functions in this file could be invoked from early interrupt -# code and produce random code coverage. +# Functions in these files can run from IRQ entry before hardirq context +# is visible to KCOV, and produce coverage unrelated to syscall inputs. KCOV_INSTRUMENT_trace_preemptirq.o := n +KCOV_INSTRUMENT_trace_irqsoff.o := n CFLAGS_bpf_trace.o := -I$(src) From 9581123304b23049437324038698af9fb56ee663 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Wed, 27 May 2026 11:13:01 -0400 Subject: [PATCH 2193/5214] perf/ftrace: Fix WARNING in __unregister_ftrace_function perf_ftrace_function_unregister() unconditionally calls unregister_ftrace_function() without checking whether the ftrace_ops was ever successfully registered. This triggers a WARN_ON in __unregister_ftrace_function() when the ops doesn't have FTRACE_OPS_FL_ENABLED set. This can happen during perf_event_alloc() error cleanup when perf_trace_destroy() is called via __free_event() on an event whose ftrace_ops registration failed or was already torn down by perf_try_init_event()'s err_destroy path. The call path is: perf_event_alloc() error cleanup -> __free_event() -> event->destroy() [tp_perf_event_destroy] -> perf_trace_destroy() -> perf_trace_event_close() -> TRACE_REG_PERF_CLOSE -> perf_ftrace_function_unregister() -> unregister_ftrace_function() -> __unregister_ftrace_function() -> WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED)) Fix this by checking FTRACE_OPS_FL_ENABLED before attempting to unregister. If the ops is not enabled, just free the filter and return success. Link: https://patch.msgid.link/20260527111301.2d0d8256@fangorn Signed-off-by: Rik van Riel Signed-off-by: Steven Rostedt --- kernel/trace/trace_event_perf.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index a6bb7577e8c5..5b272856e5ab 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -497,7 +497,17 @@ static int perf_ftrace_function_register(struct perf_event *event) static int perf_ftrace_function_unregister(struct perf_event *event) { struct ftrace_ops *ops = &event->ftrace_ops; - int ret = unregister_ftrace_function(ops); + int ret = 0; + + /* + * Perf will call this unconditionally even if the ops is not + * enabled. The unregister_ftrace_function() will warn if called + * when not enabled. Just bypass the unregistering if ops isn't + * enabled here. + */ + if (ops->flags & FTRACE_OPS_FL_ENABLED) + ret = unregister_ftrace_function(ops); + ftrace_free_filter(ops); return ret; } From 1aec36f2f362e4e6d25413113fc363c2c57a9aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 13:03:39 +0200 Subject: [PATCH 2194/5214] ipmi:ssif: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly set the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignments. While touching this array use a named initializer for assigning .name. Signed-off-by: Uwe Kleine-König (The Capable Hub) Message-ID: <5966a65daf432613a58af373af79c9c4421b3985.1780052427.git.u.kleine-koenig@baylibre.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_ssif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index add043b812ea..07f1d2327bb7 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -2127,7 +2127,7 @@ static void ssif_platform_remove(struct platform_device *dev) } static const struct platform_device_id ssif_plat_ids[] = { - { "dmi-ipmi-ssif", 0 }, + { .name = "dmi-ipmi-ssif" }, { } }; MODULE_DEVICE_TABLE(platform, ssif_plat_ids); From e1620c2add523868f3655feb5e4727ddeee83c59 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:43 -0700 Subject: [PATCH 2195/5214] perf vendor events intel: Update alderlake events from 1.37 to 1.39 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events were published in: https://github.com/intel/perfmon/commit/e55693d19f4dfe6b09c0ee9eb2b4e93781e16dd9 https://github.com/intel/perfmon/commit/25a1cd4847c1ed9159b5c79d1f7afe24ec965269 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/x86/alderlake/cache.json | 85 +++++++++++++++++++ .../pmu-events/arch/x86/alderlake/memory.json | 64 ++++++++++++++ .../arch/x86/alderlake/pipeline.json | 54 ++++++++++++ .../arch/x86/alderlake/virtual-memory.json | 9 ++ tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 5 files changed, 213 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/alderlake/cache.json b/tools/perf/pmu-events/arch/x86/alderlake/cache.json index 5d0d824f3e7e..e44e6b651d55 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/cache.json @@ -150,6 +150,16 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf", + "Unit": "cpu_core" + }, { "BriefDescription": "Counts the number of L2 Cache accesses that resulted in a miss. Counts on a per core basis.", "Counter": "0,1,2,3,4,5", @@ -259,6 +269,16 @@ "UMask": "0x21", "Unit": "cpu_core" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf", + "Unit": "cpu_core" + }, { "BriefDescription": "L2_RQSTS.HWPF_MISS", "Counter": "0,1,2,3", @@ -338,6 +358,16 @@ "UMask": "0x40", "Unit": "cpu_core" }, + { + "BriefDescription": "Cycles when L1D is locked", + "Counter": "0,1,2,3", + "EventCode": "0x42", + "EventName": "LOCK_CYCLES.CACHE_LOCK_DURATION", + "PublicDescription": "This event counts the number of cycles when the L1D is locked. It is a superset of the 0x1 mask (BUS_LOCK_CLOCKS.BUS_LOCK_DURATION).", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, { "BriefDescription": "Counts the number of cacheable memory requests that miss in the LLC. Counts on a per core basis.", "Counter": "0,1,2,3,4,5", @@ -853,6 +883,17 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of memory uops retired.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL", + "PublicDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST)", + "SampleAfterValue": "200003", + "UMask": "0x83", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load uops retired.", "Counter": "0,1,2,3,4,5", @@ -875,6 +916,18 @@ "UMask": "0x82", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", @@ -899,6 +952,18 @@ "UMask": "0x5", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", @@ -981,6 +1046,16 @@ "UMask": "0x21", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of memory uops retired that were splits.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT", + "SampleAfterValue": "200003", + "UMask": "0x43", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of retired split load uops.", "Counter": "0,1,2,3,4,5", @@ -991,6 +1066,16 @@ "UMask": "0x41", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of retired split store uops.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT_STORES", + "SampleAfterValue": "200003", + "UMask": "0x42", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the total number of load and store uops retired that missed in the second level TLB.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlake/memory.json b/tools/perf/pmu-events/arch/x86/alderlake/memory.json index a0260d5b8619..f482c06ac728 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/memory.json @@ -9,6 +9,15 @@ "UMask": "0x6", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.ANY", + "SampleAfterValue": "1000003", + "UMask": "0x7f", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block, on a load that retires.", "Counter": "0,1,2,3,4,5", @@ -27,6 +36,15 @@ "UMask": "0xf4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", "Counter": "0,1,2,3,4,5", @@ -36,6 +54,16 @@ "UMask": "0x81", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", "Counter": "0,1,2,3,4,5", @@ -46,6 +74,15 @@ "UMask": "0xc0", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a pagewalk.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.PGWALK", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a pagewalk.", "Counter": "0,1,2,3,4,5", @@ -55,6 +92,15 @@ "UMask": "0xa0", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a store data forward block.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.ST_ADDR", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a store address match.", "Counter": "0,1,2,3,4,5", @@ -64,6 +110,24 @@ "UMask": "0x84", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json index 80cad3c49d20..1c292f29b0aa 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json @@ -349,6 +349,15 @@ "UMask": "0xfd", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of near relative JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.REL_JMP", + "SampleAfterValue": "200003", + "UMask": "0xdf", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_RETURN", "Counter": "0,1,2,3,4,5", @@ -359,6 +368,15 @@ "UMask": "0xf7", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of taken branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND_TAKEN", "Counter": "0,1,2,3,4,5", @@ -560,6 +578,15 @@ "UMask": "0xfe", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the total number of BTCLEARS.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe8", + "EventName": "BTCLEAR.ANY", + "PublicDescription": "Counts the total number of BTCLEARS which occurs when the Branch Target Buffer (BTB) predicts a taken branch.", + "SampleAfterValue": "200003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Core clocks when the thread is in the C0.1 light-weight slower wakeup time but more power saving optimized state.", "Counter": "0,1,2,3,4,5,6,7", @@ -1214,6 +1241,24 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FAST", + "SampleAfterValue": "20003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "20003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machines clears due to memory renaming.", "Counter": "0,1,2,3,4,5", @@ -1410,6 +1455,15 @@ "UMask": "0x4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json index 132ce48af6d9..115bbc000a45 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/virtual-memory.json @@ -250,6 +250,15 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DTLB miss.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.DTLB_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DTLB miss.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 8a9e1735e21e..2f542283202a 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -1,5 +1,5 @@ Family-model,Version,Filename,EventType -GenuineIntel-6-(97|9A|B7|BA|BF),v1.37,alderlake,core +GenuineIntel-6-(97|9A|B7|BA|BF),v1.39,alderlake,core GenuineIntel-6-BE,v1.37,alderlaken,core GenuineIntel-6-C[56],v1.16,arrowlake,core GenuineIntel-6-(1C|26|27|35|36),v5,bonnell,core From 9996a23d319dd5db39bb8f89debeead52658b70c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:44 -0700 Subject: [PATCH 2196/5214] perf vendor events intel: Update alderlaken events from 1.37 to 1.39 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events were published in: https://github.com/intel/perfmon/commit/e55693d19f4dfe6b09c0ee9eb2b4e93781e16dd9 https://github.com/intel/perfmon/commit/25a1cd4847c1ed9159b5c79d1f7afe24ec965269 Signed-off-by: Ian Rogers Reviewed-by: Dapeng Mi Link: https://lore.kernel.org/r/20260529045155.311805-3-irogers@google.com Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Thomas Falcon Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: James Clark Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Andreas Färber Cc: linux-kernel@vger.kernel.org Cc: linux-perf-users@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/x86/alderlaken/cache.json | 50 ++++++++++++++++ .../arch/x86/alderlaken/memory.json | 57 +++++++++++++++++++ .../arch/x86/alderlaken/pipeline.json | 48 ++++++++++++++++ .../arch/x86/alderlaken/virtual-memory.json | 8 +++ tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 5 files changed, 164 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json index 1f97a4dc6fb1..0ffad953e752 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json @@ -225,6 +225,16 @@ "SampleAfterValue": "20003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of memory uops retired.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL", + "PublicDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST)", + "SampleAfterValue": "200003", + "UMask": "0x83" + }, { "BriefDescription": "Counts the number of load uops retired.", "Counter": "0,1,2,3,4,5", @@ -245,6 +255,17 @@ "SampleAfterValue": "200003", "UMask": "0x82" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "SampleAfterValue": "1000003", + "UMask": "0x5" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", @@ -267,6 +288,17 @@ "SampleAfterValue": "1000003", "UMask": "0x5" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "SampleAfterValue": "1000003", + "UMask": "0x5" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", @@ -342,6 +374,15 @@ "SampleAfterValue": "200003", "UMask": "0x21" }, + { + "BriefDescription": "Counts the number of memory uops retired that were splits.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT", + "SampleAfterValue": "200003", + "UMask": "0x43" + }, { "BriefDescription": "Counts the number of retired split load uops.", "Counter": "0,1,2,3,4,5", @@ -351,6 +392,15 @@ "SampleAfterValue": "200003", "UMask": "0x41" }, + { + "BriefDescription": "Counts the number of retired split store uops.", + "Counter": "0,1,2,3,4,5", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT_STORES", + "SampleAfterValue": "200003", + "UMask": "0x42" + }, { "BriefDescription": "Counts the total number of load and store uops retired that missed in the second level TLB.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/memory.json b/tools/perf/pmu-events/arch/x86/alderlaken/memory.json index 049c5e2630d7..a0dd82ddd58a 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/memory.json @@ -1,4 +1,12 @@ [ + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.ANY", + "SampleAfterValue": "1000003", + "UMask": "0x7f" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block, on a load that retires.", "Counter": "0,1,2,3,4,5", @@ -15,6 +23,14 @@ "SampleAfterValue": "1000003", "UMask": "0xf4" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", "Counter": "0,1,2,3,4,5", @@ -23,6 +39,15 @@ "SampleAfterValue": "1000003", "UMask": "0x81" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", "Counter": "0,1,2,3,4,5", @@ -32,6 +57,14 @@ "SampleAfterValue": "1000003", "UMask": "0xc0" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a pagewalk.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.PGWALK", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a pagewalk.", "Counter": "0,1,2,3,4,5", @@ -40,6 +73,14 @@ "SampleAfterValue": "1000003", "UMask": "0xa0" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a store data forward block.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.ST_ADDR", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a store address match.", "Counter": "0,1,2,3,4,5", @@ -48,6 +89,22 @@ "SampleAfterValue": "1000003", "UMask": "0x84" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json index a13851071624..ad3acb109bd8 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json @@ -175,6 +175,14 @@ "SampleAfterValue": "200003", "UMask": "0xfd" }, + { + "BriefDescription": "Counts the number of near relative JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.REL_JMP", + "SampleAfterValue": "200003", + "UMask": "0xdf" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.NEAR_RETURN", "Counter": "0,1,2,3,4,5", @@ -184,6 +192,14 @@ "SampleAfterValue": "200003", "UMask": "0xf7" }, + { + "BriefDescription": "Counts the number of taken branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.COND_TAKEN", "Counter": "0,1,2,3,4,5", @@ -293,6 +309,14 @@ "SampleAfterValue": "200003", "UMask": "0xfe" }, + { + "BriefDescription": "Counts the total number of BTCLEARS.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe8", + "EventName": "BTCLEAR.ANY", + "PublicDescription": "Counts the total number of BTCLEARS which occurs when the Branch Target Buffer (BTB) predicts a taken branch.", + "SampleAfterValue": "200003" + }, { "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", @@ -400,6 +424,22 @@ "SampleAfterValue": "20003", "UMask": "0x8" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FAST", + "SampleAfterValue": "20003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "20003", + "UMask": "0x40" + }, { "BriefDescription": "Counts the number of machines clears due to memory renaming.", "Counter": "0,1,2,3,4,5", @@ -482,6 +522,14 @@ "SampleAfterValue": "200003", "UMask": "0x4" }, + { + "BriefDescription": "Counts the number issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "200003", + "UMask": "0x8" + }, { "BriefDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json b/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json index d9c737a17df0..56c1bfc38024 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/virtual-memory.json @@ -42,6 +42,14 @@ "SampleAfterValue": "200003", "UMask": "0xe" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DTLB miss.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0x05", + "EventName": "LD_HEAD.DTLB_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DTLB miss.", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 2f542283202a..c076dbed1611 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -1,6 +1,6 @@ Family-model,Version,Filename,EventType GenuineIntel-6-(97|9A|B7|BA|BF),v1.39,alderlake,core -GenuineIntel-6-BE,v1.37,alderlaken,core +GenuineIntel-6-BE,v1.39,alderlaken,core GenuineIntel-6-C[56],v1.16,arrowlake,core GenuineIntel-6-(1C|26|27|35|36),v5,bonnell,core GenuineIntel-6-(3D|47),v30,broadwell,core From 119de2c33d96d2b4e79f8fbe8ec0c5b5939e0a52 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 May 2026 11:41:20 +0200 Subject: [PATCH 2197/5214] pinctrl: tegra238: remove unused entries The -Wunused-const-variable check points out a number of added entries that are currently not referenced: drivers/pinctrl/tegra/pinctrl-tegra238.c:1169:27: error: 'soc_gpio86_phh3_pins' defined but not used [-Werror=unused-const-variable=] 1169 | static const unsigned int soc_gpio86_phh3_pins[] = { | ^~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/tegra/pinctrl-tegra238.c:1165:27: error: 'uart5_cts_phh2_pins' defined but not used [-Werror=unused-const-variable=] 1165 | static const unsigned int uart5_cts_phh2_pins[] = { | ^~~~~~~~~~~~~~~~~~~ drivers/pinctrl/tegra/pinctrl-tegra238.c:1161:27: error: 'uart5_rts_phh1_pins' defined but not used [-Werror=unused-const-variable=] 1161 | static const unsigned int uart5_rts_phh1_pins[] = { | ^~~~~~~~~~~~~~~~~~~ Remove them for now, they can just be added back if they get used in the future. Fixes: 25cac7292d49 ("pinctrl: tegra: Add Tegra238 pinmux driver") Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/pinctrl-tegra238.c | 96 ------------------------ 1 file changed, 96 deletions(-) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra238.c b/drivers/pinctrl/tegra/pinctrl-tegra238.c index 421da334151c..c765b6b880e5 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra238.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra238.c @@ -1074,102 +1074,6 @@ static const unsigned int pwm7_pee1_pins[] = { TEGRA_PIN_PWM7_PEE1, }; -static const unsigned int soc_gpio49_pee2_pins[] = { - TEGRA_PIN_SOC_GPIO49_PEE2, -}; - -static const unsigned int soc_gpio82_pee3_pins[] = { - TEGRA_PIN_SOC_GPIO82_PEE3, -}; - -static const unsigned int soc_gpio50_pee4_pins[] = { - TEGRA_PIN_SOC_GPIO50_PEE4, -}; - -static const unsigned int soc_gpio83_pee5_pins[] = { - TEGRA_PIN_SOC_GPIO83_PEE5, -}; - -static const unsigned int soc_gpio69_pff0_pins[] = { - TEGRA_PIN_SOC_GPIO69_PFF0, -}; - -static const unsigned int soc_gpio70_pff1_pins[] = { - TEGRA_PIN_SOC_GPIO70_PFF1, -}; - -static const unsigned int soc_gpio71_pff2_pins[] = { - TEGRA_PIN_SOC_GPIO71_PFF2, -}; - -static const unsigned int soc_gpio72_pff3_pins[] = { - TEGRA_PIN_SOC_GPIO72_PFF3, -}; - -static const unsigned int soc_gpio73_pff4_pins[] = { - TEGRA_PIN_SOC_GPIO73_PFF4, -}; - -static const unsigned int soc_gpio74_pff5_pins[] = { - TEGRA_PIN_SOC_GPIO74_PFF5, -}; - -static const unsigned int soc_gpio80_pff6_pins[] = { - TEGRA_PIN_SOC_GPIO80_PFF6, -}; - -static const unsigned int soc_gpio76_pff7_pins[] = { - TEGRA_PIN_SOC_GPIO76_PFF7, -}; - -static const unsigned int soc_gpio77_pgg0_pins[] = { - TEGRA_PIN_SOC_GPIO77_PGG0, -}; - -static const unsigned int soc_gpio84_pgg1_pins[] = { - TEGRA_PIN_SOC_GPIO84_PGG1, -}; - -static const unsigned int uart2_tx_pgg2_pins[] = { - TEGRA_PIN_UART2_TX_PGG2, -}; - -static const unsigned int uart2_rx_pgg3_pins[] = { - TEGRA_PIN_UART2_RX_PGG3, -}; - -static const unsigned int uart2_rts_pgg4_pins[] = { - TEGRA_PIN_UART2_RTS_PGG4, -}; - -static const unsigned int uart2_cts_pgg5_pins[] = { - TEGRA_PIN_UART2_CTS_PGG5, -}; - -static const unsigned int soc_gpio85_pgg6_pins[] = { - TEGRA_PIN_SOC_GPIO85_PGG6, -}; - -static const unsigned int uart5_tx_pgg7_pins[] = { - TEGRA_PIN_UART5_TX_PGG7, -}; - -static const unsigned int uart5_rx_phh0_pins[] = { - TEGRA_PIN_UART5_RX_PHH0, -}; - -static const unsigned int uart5_rts_phh1_pins[] = { - TEGRA_PIN_UART5_RTS_PHH1, -}; - -static const unsigned int uart5_cts_phh2_pins[] = { - TEGRA_PIN_UART5_CTS_PHH2, -}; - -static const unsigned int soc_gpio86_phh3_pins[] = { - TEGRA_PIN_SOC_GPIO86_PHH3, -}; - static const unsigned int sdmmc1_comp_pins[] = { TEGRA_PIN_SDMMC1_COMP, }; From 55290f5983540e537239bfaf02a050502a570383 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 27 May 2026 22:08:45 +0100 Subject: [PATCH 2198/5214] MAINTAINERS: add myself as co-maintainer for Samsung pinctrl drivers As Google Tensor gs101 is based off a Samsung Exynos design I've been working on the Samsung pinctrl drivers and have an interest in helping maintain this code. Signed-off-by: Peter Griffin Acked-by: Sylwester Nawrocki Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..47e8f12480b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21061,6 +21061,7 @@ F: drivers/pinctrl/renesas/ PIN CONTROLLER - SAMSUNG M: Krzysztof Kozlowski M: Sylwester Nawrocki +M: Peter Griffin R: Alim Akhtar L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-samsung-soc@vger.kernel.org From 8c63821f227f7fd8b9e0adc3cd4b26e5b8eeb71e Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 7 May 2026 08:21:06 +0000 Subject: [PATCH 2199/5214] dt-bindings: pinctl: amlogic,pinctrl-a4: Add compatible string for A9 Update dt-binding document for pinctrl of Amlogic A9. In Amlogic A9 SoC, a bank mux register reuse other banks. The multiplexed part requires special processing and is therefore incompatible with the previous SoCs. Signed-off-by: Xianwei Zhao Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml b/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml index 6ba66c2033b4..b69db1b95345 100644 --- a/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml +++ b/Documentation/devicetree/bindings/pinctrl/amlogic,pinctrl-a4.yaml @@ -17,6 +17,7 @@ properties: oneOf: - enum: - amlogic,pinctrl-a4 + - amlogic,pinctrl-a9 - amlogic,pinctrl-s6 - amlogic,pinctrl-s7 - items: From e1147e8a002af4997bd90b8210e02bab9b50d4c0 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 7 May 2026 08:21:07 +0000 Subject: [PATCH 2200/5214] pinctrl: meson: support amlogic A9 SoC In Amlogic A9 SoC, subordinate bank reuse other master bank is not from bit0, and subordinate bank reuse multi master banks. This submission implements this situation. Signed-off-by: Xianwei Zhao Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 61 ++++++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index 4c88ea1de909..88c5408f51cf 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -55,14 +55,18 @@ struct aml_pio_control { * partial bank(subordinate) pins mux config use other bank(main) mux registgers * m_bank_id: the main bank which pin_id from 0, but register bit not from bit 0 * m_bit_offs: bit offset the main bank mux register + * s_bit_offs: start bit that subordinate bank use mux register * sid: start pin_id of subordinate bank * eid: end pin_id of subordinate bank + * next: subordinate bank reused multiple other bank groups. */ struct multi_mux { unsigned int m_bank_id; unsigned int m_bit_offs; + unsigned int s_bit_offs; unsigned int sid; unsigned int eid; + const struct multi_mux *next; }; struct aml_pctl_data { @@ -124,12 +128,51 @@ static const char *aml_bank_name[31] = { "GPIOCC", "TEST_N", "ANALOG" }; +static const struct multi_mux multi_mux_a9[] = { + { + .m_bank_id = AMLOGIC_GPIO_C, + .m_bit_offs = 4, + .s_bit_offs = 0, + .sid = (AMLOGIC_GPIO_D << 8) + 16, + .eid = (AMLOGIC_GPIO_D << 8) + 16, + .next = &multi_mux_a9[1], + }, { + .m_bank_id = AMLOGIC_GPIO_AO, + .m_bit_offs = 0, + .s_bit_offs = 52, + .sid = (AMLOGIC_GPIO_D << 8) + 17, + .eid = (AMLOGIC_GPIO_D << 8) + 17, + .next = NULL, + }, { + .m_bank_id = AMLOGIC_GPIO_A, + .m_bit_offs = 0, + .s_bit_offs = 80, + .sid = (AMLOGIC_GPIO_Y << 8) + 8, + .eid = (AMLOGIC_GPIO_Y << 8) + 9, + .next = NULL, + }, { + .m_bank_id = AMLOGIC_GPIO_CC, + .m_bit_offs = 24, + .s_bit_offs = 0, + .sid = (AMLOGIC_GPIO_X << 8) + 16, + .eid = (AMLOGIC_GPIO_X << 8) + 17, + .next = NULL, + }, +}; + +static const struct aml_pctl_data a9_priv_data = { + .number = ARRAY_SIZE(multi_mux_a9), + .p_mux = multi_mux_a9, +}; + static const struct multi_mux multi_mux_s7[] = { { .m_bank_id = AMLOGIC_GPIO_CC, .m_bit_offs = 24, + .s_bit_offs = 0, .sid = (AMLOGIC_GPIO_X << 8) + 16, .eid = (AMLOGIC_GPIO_X << 8) + 19, + .next = NULL, }, }; @@ -142,13 +185,17 @@ static const struct multi_mux multi_mux_s6[] = { { .m_bank_id = AMLOGIC_GPIO_CC, .m_bit_offs = 24, + .s_bit_offs = 0, .sid = (AMLOGIC_GPIO_X << 8) + 16, .eid = (AMLOGIC_GPIO_X << 8) + 19, + .next = NULL, }, { .m_bank_id = AMLOGIC_GPIO_F, .m_bit_offs = 4, + .s_bit_offs = 0, .sid = (AMLOGIC_GPIO_D << 8) + 6, .eid = (AMLOGIC_GPIO_D << 8) + 6, + .next = NULL, }, }; @@ -177,31 +224,34 @@ static int aml_pctl_set_function(struct aml_pinctrl *info, struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int shift; int reg; - int i; + int i, loop_count; unsigned int offset = bank->mux_bit_offs; const struct multi_mux *p_mux; /* peculiar mux reg set */ - if (bank->p_mux) { - p_mux = bank->p_mux; + loop_count = 10; + p_mux = bank->p_mux; + while (p_mux && loop_count) { if (pin_id >= p_mux->sid && pin_id <= p_mux->eid) { bank = NULL; for (i = 0; i < info->nbanks; i++) { if (info->banks[i].bank_id == p_mux->m_bank_id) { bank = &info->banks[i]; - break; + break; } } if (!bank || !bank->reg_mux) return -EINVAL; - shift = (pin_id - p_mux->sid) << 2; + shift = ((pin_id - p_mux->sid) << 2) + p_mux->s_bit_offs; reg = (shift / 32) * 4; offset = shift % 32; return regmap_update_bits(bank->reg_mux, reg, 0xf << offset, (func & 0xf) << offset); } + p_mux = p_mux->next; + loop_count--; } /* normal mux reg set */ @@ -1158,6 +1208,7 @@ static int aml_pctl_probe(struct platform_device *pdev) static const struct of_device_id aml_pctl_of_match[] = { { .compatible = "amlogic,pinctrl-a4", }, + { .compatible = "amlogic,pinctrl-a9", .data = &a9_priv_data, }, { .compatible = "amlogic,pinctrl-s7", .data = &s7_priv_data, }, { .compatible = "amlogic,pinctrl-s6", .data = &s6_priv_data, }, { /* sentinel */ } From 446fa334d186316e76cbdc4e94e42af7d040a79c Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 29 May 2026 13:39:11 +0530 Subject: [PATCH 2201/5214] pinctrl: qcom: Replace open coded eoi call with irq_chip_eoi_parent() Before commit 14dbe186b9d4 ("pinctrl: msmgpio: Make the irqchip immutable") msm gpio irqchip conditionally initialized pctrl->irq_chip.irq_eoi to irq_chip_eoi_parent() only for the GPIO irqs having a wakeup capable irq. In order to make gpio irqchip immutable pctrl->irq_chip.irq_eoi is initialized to msm_gpio_irq_eoi() which now gets invoked for both wake up and non-wakeup capable GPIO IRQs. Replace open coded eoi call to parent irqchip with irq_chip_eoi_parent(). Since the irq_chip_*_parent() APIs internally do not check the valid parent data is present to ensure irq_chip_eoi_parent() is only invoked for wakeup capable GPIOs validate d->parent_data within msm_gpio_irq_eoi(). For non wakeup capable GPIOs d->parent_data will be NULL since parent irqchip diconnects hierarchy using irq_domain_disconnect_hierarchy() and later irq framework trims hierarchy using irq_domain_trim_hierarchy() which makes d->parent_data as NULL. No functional impact. Reviewed-by: Bjorn Andersson Signed-off-by: Maulik Shah Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-msm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 45b3a2763eb8..6771f5eb29e4 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1012,10 +1012,8 @@ static void msm_gpio_irq_ack(struct irq_data *d) static void msm_gpio_irq_eoi(struct irq_data *d) { - d = d->parent_data; - - if (d) - d->chip->irq_eoi(d); + if (d->parent_data) + irq_chip_eoi_parent(d); } static bool msm_gpio_needs_dual_edge_parent_workaround(struct irq_data *d, From 9b97162c7757463bf3ea4afa1d68722c9a3e6bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 17:43:00 +0200 Subject: [PATCH 2202/5214] pinctrl: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous unit. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Acked-by: Mika Westerberg Reviewed-by: Geert Uytterhoeven # renesas Acked-by: Geert Uytterhoeven # renesas Signed-off-by: Linus Walleij --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 4 ++-- drivers/pinctrl/intel/pinctrl-broxton.c | 4 ++-- drivers/pinctrl/intel/pinctrl-denverton.c | 2 +- drivers/pinctrl/pinctrl-tps6594.c | 4 ++-- drivers/pinctrl/renesas/core.c | 24 +++++++++++------------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 305233fc1987..8990fab0446c 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -602,8 +602,8 @@ static int cs42l43_pin_probe(struct platform_device *pdev) } static const struct platform_device_id cs42l43_pin_id_table[] = { - { "cs42l43-pinctrl", }, - {} + { .name = "cs42l43-pinctrl" }, + { } }; MODULE_DEVICE_TABLE(platform, cs42l43_pin_id_table); diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c index 3d3c1706928a..a33100f28488 100644 --- a/drivers/pinctrl/intel/pinctrl-broxton.c +++ b/drivers/pinctrl/intel/pinctrl-broxton.c @@ -995,8 +995,8 @@ static const struct acpi_device_id bxt_pinctrl_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bxt_pinctrl_acpi_match); static const struct platform_device_id bxt_pinctrl_platform_ids[] = { - { "apollolake-pinctrl", (kernel_ulong_t)apl_pinctrl_soc_data }, - { "broxton-pinctrl", (kernel_ulong_t)bxt_pinctrl_soc_data }, + { .name = "apollolake-pinctrl", .driver_data = (kernel_ulong_t)apl_pinctrl_soc_data }, + { .name = "broxton-pinctrl", .driver_data = (kernel_ulong_t)bxt_pinctrl_soc_data }, { } }; MODULE_DEVICE_TABLE(platform, bxt_pinctrl_platform_ids); diff --git a/drivers/pinctrl/intel/pinctrl-denverton.c b/drivers/pinctrl/intel/pinctrl-denverton.c index 4a1d346fb30c..09aee90dee82 100644 --- a/drivers/pinctrl/intel/pinctrl-denverton.c +++ b/drivers/pinctrl/intel/pinctrl-denverton.c @@ -250,7 +250,7 @@ static const struct acpi_device_id dnv_pinctrl_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, dnv_pinctrl_acpi_match); static const struct platform_device_id dnv_pinctrl_platform_ids[] = { - { "denverton-pinctrl", (kernel_ulong_t)&dnv_soc_data }, + { .name = "denverton-pinctrl", .driver_data = (kernel_ulong_t)&dnv_soc_data }, { } }; MODULE_DEVICE_TABLE(platform, dnv_pinctrl_platform_ids); diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c index 6726853110d1..55dfa843e35e 100644 --- a/drivers/pinctrl/pinctrl-tps6594.c +++ b/drivers/pinctrl/pinctrl-tps6594.c @@ -562,8 +562,8 @@ static int tps6594_pinctrl_probe(struct platform_device *pdev) } static const struct platform_device_id tps6594_pinctrl_id_table[] = { - { "tps6594-pinctrl", }, - {} + { .name = "tps6594-pinctrl" }, + { } }; MODULE_DEVICE_TABLE(platform, tps6594_pinctrl_id_table); diff --git a/drivers/pinctrl/renesas/core.c b/drivers/pinctrl/renesas/core.c index 0840668638d9..a466ebf99593 100644 --- a/drivers/pinctrl/renesas/core.c +++ b/drivers/pinctrl/renesas/core.c @@ -1380,40 +1380,40 @@ static int sh_pfc_probe(struct platform_device *pdev) static const struct platform_device_id sh_pfc_id_table[] = { #ifdef CONFIG_PINCTRL_PFC_SH7203 - { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info }, + { .name = "pfc-sh7203", .driver_data = (kernel_ulong_t)&sh7203_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7264 - { "pfc-sh7264", (kernel_ulong_t)&sh7264_pinmux_info }, + { .name = "pfc-sh7264", .driver_data = (kernel_ulong_t)&sh7264_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7269 - { "pfc-sh7269", (kernel_ulong_t)&sh7269_pinmux_info }, + { .name = "pfc-sh7269", .driver_data = (kernel_ulong_t)&sh7269_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7720 - { "pfc-sh7720", (kernel_ulong_t)&sh7720_pinmux_info }, + { .name = "pfc-sh7720", .driver_data = (kernel_ulong_t)&sh7720_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7722 - { "pfc-sh7722", (kernel_ulong_t)&sh7722_pinmux_info }, + { .name = "pfc-sh7722", .driver_data = (kernel_ulong_t)&sh7722_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7723 - { "pfc-sh7723", (kernel_ulong_t)&sh7723_pinmux_info }, + { .name = "pfc-sh7723", .driver_data = (kernel_ulong_t)&sh7723_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7724 - { "pfc-sh7724", (kernel_ulong_t)&sh7724_pinmux_info }, + { .name = "pfc-sh7724", .driver_data = (kernel_ulong_t)&sh7724_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7734 - { "pfc-sh7734", (kernel_ulong_t)&sh7734_pinmux_info }, + { .name = "pfc-sh7734", .driver_data = (kernel_ulong_t)&sh7734_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7757 - { "pfc-sh7757", (kernel_ulong_t)&sh7757_pinmux_info }, + { .name = "pfc-sh7757", .driver_data = (kernel_ulong_t)&sh7757_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7785 - { "pfc-sh7785", (kernel_ulong_t)&sh7785_pinmux_info }, + { .name = "pfc-sh7785", .driver_data = (kernel_ulong_t)&sh7785_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SH7786 - { "pfc-sh7786", (kernel_ulong_t)&sh7786_pinmux_info }, + { .name = "pfc-sh7786", .driver_data = (kernel_ulong_t)&sh7786_pinmux_info }, #endif #ifdef CONFIG_PINCTRL_PFC_SHX3 - { "pfc-shx3", (kernel_ulong_t)&shx3_pinmux_info }, + { .name = "pfc-shx3", .driver_data = (kernel_ulong_t)&shx3_pinmux_info }, #endif { /* sentinel */ } }; From 3a7a1e491bac52a42693b2613533035f0eff85eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 17:43:01 +0200 Subject: [PATCH 2203/5214] pinctrl: max77620: Unify usage of space and comma in platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The most accepted style for the array terminator is to use a single space between the curly braces and no trailing comma. Also don't use a comma directly before a closing brace in the other entries. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-max77620.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/pinctrl-max77620.c b/drivers/pinctrl/pinctrl-max77620.c index acb945a25743..c47eccce7dc0 100644 --- a/drivers/pinctrl/pinctrl-max77620.c +++ b/drivers/pinctrl/pinctrl-max77620.c @@ -645,9 +645,9 @@ static const struct dev_pm_ops max77620_pinctrl_pm_ops = { }; static const struct platform_device_id max77620_pinctrl_devtype[] = { - { .name = "max77620-pinctrl", }, - { .name = "max20024-pinctrl", }, - {}, + { .name = "max77620-pinctrl" }, + { .name = "max20024-pinctrl" }, + { } }; MODULE_DEVICE_TABLE(platform, max77620_pinctrl_devtype); From 555aa0e83b16b55f47f1940f89ae30595de4f414 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Wed, 27 May 2026 23:16:24 -0400 Subject: [PATCH 2204/5214] KVM: x86: ioapic: Use old_dest_mode consistently in ioapic_write_indirect() When reconstructing a temporary IRQ state from the historical/old IOAPIC redirection table configuration in ioapic_write_indirect(), the code previously assigned 'irq.dest_id' from 'old_dest_id', but incorrectly queried the live/new 'e->fields.dest_mode' to populate 'irq.dest_mode'. Mixing the old destination ID with the new destination mode creates an inconsistent, hybrid IRQ state. This discrepancy leads to an incorrect vCPU bitmap calculation via kvm_bitmap_or_dest_vcpus(), causing subsequent interrupt routing updates (such as RTC interrupt handling) to target the wrong set of virtual processors if both fields were modified simultaneously. Fix this by using 'old_dest_mode' consistently alongside 'old_dest_id' to ensure the historical IRQ structure is reconstructed accurately. Fixes: c96001c5702e ("KVM: X86: Use APIC_DEST_* macros properly in kvm_lapic_irq.dest_mode") Signed-off-by: Li RongQing Link: https://patch.msgid.link/20260528031624.1929-1-lirongqing@baidu.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/ioapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index eed96ff6e722..f3f4a483ca15 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c @@ -441,7 +441,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) irq.dest_id = old_dest_id; irq.dest_mode = kvm_lapic_irq_dest_mode( - !!e->fields.dest_mode); + !!old_dest_mode); kvm_bitmap_or_dest_vcpus(ioapic->kvm, &irq, vcpu_bitmap); } From 8a990b123bf524205484516339e469fbe887a000 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:45 -0700 Subject: [PATCH 2205/5214] perf vendor events intel: Update arrowlake events from 1.16 to 1.17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events were published in: https://github.com/intel/perfmon/commit/90c505bcd9b10fd9ce692a670c23074ab743aa87 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/x86/arrowlake/cache.json | 53 +++++++++++++++++++ .../arch/x86/arrowlake/floating-point.json | 9 ++++ .../arch/x86/arrowlake/pipeline.json | 48 ++++++++++++++++- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json index 4c3aa1fab5a8..fe6b9ad68f87 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json @@ -339,6 +339,16 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Counts the number of L2 Cache Accesses that resulted in a Hit from a front door request only (does not include rejects or recycles), per core event", "Counter": "0,1,2,3,4,5,6,7", @@ -464,6 +474,16 @@ "UMask": "0x21", "Unit": "cpu_core" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Read requests with true-miss in L2 cache [This event is alias to L2_REQUEST.MISS]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -1126,6 +1146,15 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of retired load ops that hit in the L3 cache in which a snoop was required and modified data was forwarded", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_HITM", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of retired load ops with an unknown source", "Counter": "0,1,2,3,4,5,6,7", @@ -1393,6 +1422,18 @@ "UMask": "0x82", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "SampleAfterValue": "200003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", "Counter": "0,1", @@ -1453,6 +1494,18 @@ "UMask": "0x5", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048.", + "Counter": "0,1", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "SampleAfterValue": "200003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", "Counter": "0,1", diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/floating-point.json b/tools/perf/pmu-events/arch/x86/arrowlake/floating-point.json index 3e68c2468f11..c54fc201a6ca 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/floating-point.json @@ -564,6 +564,15 @@ "UMask": "0x1", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer store data port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.STD", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of floating point operations retired that required microcode assist.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json b/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json index fb973c75be57..a0fd63cace22 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json @@ -376,7 +376,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of taken branch instructions retired", + "BriefDescription": "Counts the number of near taken branch instructions retired", "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_TAKEN", @@ -422,6 +422,15 @@ "UMask": "0xfd", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of relative JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.REL_JMP", + "SampleAfterValue": "200003", + "UMask": "0xdf", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of near relative JMP branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7", @@ -431,6 +440,25 @@ "UMask": "0xdf", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of taken branch instructions retired", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of taken branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "Errata": "ARL011", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -1663,6 +1691,15 @@ "UMask": "0x88", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts number of virtual trap actually taken (e.g. highest priority event during retirement). It can count virtual trap from FPC port 0 or port 1 (x87/SSE) equally in a single counter.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "20003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of nukes due to memory renaming", "Counter": "0,1,2,3,4,5,6,7", @@ -1672,6 +1709,15 @@ "UMask": "0x10", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of machines clears due to memory renaming.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index c076dbed1611..85f41cab56c7 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -1,7 +1,7 @@ Family-model,Version,Filename,EventType GenuineIntel-6-(97|9A|B7|BA|BF),v1.39,alderlake,core GenuineIntel-6-BE,v1.39,alderlaken,core -GenuineIntel-6-C[56],v1.16,arrowlake,core +GenuineIntel-6-C[56],v1.17,arrowlake,core GenuineIntel-6-(1C|26|27|35|36),v5,bonnell,core GenuineIntel-6-(3D|47),v30,broadwell,core GenuineIntel-6-56,v12,broadwellde,core From 1a5c515eae7c2234b86b35b3b0f2ab94887f43d7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:46 -0700 Subject: [PATCH 2206/5214] perf vendor events intel: Update clearwaterforest events and metrics from 1.00 to 1.02 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/6de6be144b5ea747690a74106f180269c3d647b0 https://github.com/intel/perfmon/commit/e7f5e6092c3e4c11f350c0468ddecb0e1e818b76 https://github.com/intel/perfmon/commit/9c1a1baa5156efbe9325392194d2bf5d1c8bcb92 https://github.com/intel/perfmon/commit/6055edb3c30634fc913250b562c0fc42ac4eb523 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/x86/clearwaterforest/cache.json | 525 ++- .../arch/x86/clearwaterforest/counter.json | 79 +- .../x86/clearwaterforest/cwf-metrics.json | 310 ++ .../x86/clearwaterforest/floating-point.json | 191 + .../arch/x86/clearwaterforest/frontend.json | 226 ++ .../arch/x86/clearwaterforest/memory.json | 92 + .../arch/x86/clearwaterforest/other.json | 20 + .../arch/x86/clearwaterforest/pipeline.json | 829 +++- .../x86/clearwaterforest/uncore-cache.json | 3413 +++++++++++++++++ .../arch/x86/clearwaterforest/uncore-cxl.json | 31 + .../clearwaterforest/uncore-interconnect.json | 1625 ++++++++ .../arch/x86/clearwaterforest/uncore-io.json | 1925 ++++++++++ .../x86/clearwaterforest/uncore-memory.json | 883 +++++ .../x86/clearwaterforest/uncore-power.json | 109 + .../x86/clearwaterforest/virtual-memory.json | 94 + tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 16 files changed, 10307 insertions(+), 47 deletions(-) create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/cwf-metrics.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/floating-point.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/other.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cache.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cxl.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-interconnect.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-io.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-memory.json create mode 100644 tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-power.json diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/cache.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/cache.json index ecb7dc252208..480874ccbd09 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/cache.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/cache.json @@ -1,4 +1,140 @@ [ + { + "BriefDescription": "Counts the number of requests that were not accepted into the L2Q because the L2Q is FULL.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x31", + "EventName": "CORE_REJECT_L2Q.ANY", + "PublicDescription": "Counts the number of (demand and L1 prefetchers) core requests rejected by the L2Q due to a full or nearly full condition which likely indicates back pressure from L2Q. It also counts requests that would have gone directly to the XQ, but are rejected due to a full or nearly full condition, indicating back pressure from the IDI link. The L2Q may also reject transactions from a core to ensure fairness between cores, or to delay a core's dirty eviction when the address conflicts with incoming external snoops. Note that L2 prefetcher requests that are dropped are not counted by this event. Counts on a per core basis.", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x30", + "EventName": "L2_REJECT_XQ.ANY", + "PublicDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition which likely indicates back pressure from the IDI link. The IDI link is a central on-die protocol for memory and coherence traffic between the core and the uncore. It is highly optimized for efficiency, bandwidth, and coherency, using multiple channels and flow control mechanisms to manage high-throughput, low-latency data and protocol communication within the chip. The XQ may reject transactions from the L2Q (non-cacheable requests), BBL (L2 misses) and WOB (L2 write-back victims).", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door requests for Code Read, Data Read, RFO, ITOM, and L2 Prefetches. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x1ff" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD", + "SampleAfterValue": "1000003", + "UMask": "0xc4" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x84" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x44" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD", + "SampleAfterValue": "1000003", + "UMask": "0xc1" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x81" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x41" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO", + "SampleAfterValue": "1000003", + "UMask": "0xc2" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x82" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x42" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door requests that resulted in a Hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "SampleAfterValue": "1000003", + "UMask": "0x1bf" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests, including hits and misses. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF", + "SampleAfterValue": "1000003", + "UMask": "0xc8" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests that result in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x88" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests that result in a miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x48" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.MISS", + "SampleAfterValue": "1000003", + "UMask": "0x17f" + }, { "BriefDescription": "Counts the number of cacheable memory requests that miss in the LLC. Counts on a per core basis.", "Counter": "0,1,2,3,4,5,6,7", @@ -17,6 +153,231 @@ "SampleAfterValue": "1000003", "UMask": "0x4f" }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to a demand load which hit in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.L2_HIT", + "PublicDescription": "Counts the number of cycles a core is stalled due to a demand load which hit in the L2 cache.", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x6" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, no snoop was required. LLC provided data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, a snoop was required, the snoop misses or the snoop hits but no fwd. LLC provides the data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the caches. DRAM, MMIO or other LOCAL memory type provides the data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the caches, a snoop was required, and hits in other core or module on same die. Another core provides the data with a fwd, no fwd, or hitM.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_OTHERMOD", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the total number of load ops retired that miss the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.ALL", + "PublicDescription": "Counts the total number of load ops retired that miss the L3 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM", + "PublicDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote DRAM", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM_OR_NOFWD", + "PublicDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote DRAM, OR had a Remote snoop miss/no fwd and hit in the Local DRAM. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote Cache and modified data was forwarded", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD_HITM", + "PublicDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote Cache and modified data was forwarded Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote Cache and non-modified data was forwarded", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD_NONM", + "PublicDescription": "Counts the number of load ops retired that miss the L3 cache and hit in a Remote Cache and non-modified data was forwarded Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_HITM", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and modified data was forwarded. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and no data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_NO_FWD", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and no data was forwarded. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and non-modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_WITH_FWD", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and non-modified data was forwarded. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit the L1 data cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L1_HIT", + "PublicDescription": "Counts the number of load ops retired that hit the L1 data cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss in the L1 data cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L1_MISS", + "PublicDescription": "Counts the number of load ops retired that miss in the L1 data cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L2_HIT", + "PublicDescription": "Counts the number of load ops retired that hit in the L2 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of load ops retired that miss in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L2_MISS", + "PublicDescription": "Counts the number of load ops retired that miss in the L2 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1c" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which no snoop was required.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT_NO_SNOOP", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which no snoop was required. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and it hit and forwarded data, it hit and did not forward data, or it hit and the forwarded data was modified.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT_SNOOP_HIT", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and it hit and forwarded data, it hit and did not forward data, or it hit and the forwarded data was modified. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of cycles that uops are blocked for any of the following reasons: load buffer, store buffer or RSV full.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x04", + "EventName": "MEM_SCHEDULER_BLOCK.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x7" + }, + { + "BriefDescription": "Counts the number of cycles that uops are blocked due to a load buffer full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x04", + "EventName": "MEM_SCHEDULER_BLOCK.LD_BUF", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of cycles that uops are blocked due to an RSV full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x04", + "EventName": "MEM_SCHEDULER_BLOCK.RSV", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of cycles that uops are blocked due to a store buffer full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x04", + "EventName": "MEM_SCHEDULER_BLOCK.ST_BUF", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL", + "PublicDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST). Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x83" + }, { "BriefDescription": "Counts the number of load ops retired.", "Counter": "0,1,2,3,4,5,6,7", @@ -36,121 +397,213 @@ "UMask": "0x82" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", "MSRIndex": "0x3F6", "MSRValue": "0x400", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", "MSRIndex": "0x3F6", "MSRValue": "0x80", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", "MSRIndex": "0x3F6", "MSRValue": "0x10", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", "MSRIndex": "0x3F6", "MSRValue": "0x800", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", "MSRIndex": "0x3F6", "MSRValue": "0x100", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", "MSRIndex": "0x3F6", "MSRValue": "0x20", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", "MSRIndex": "0x3F6", "MSRValue": "0x4", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", "MSRIndex": "0x3F6", "MSRValue": "0x200", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", "MSRIndex": "0x3F6", "MSRValue": "0x40", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", "MSRIndex": "0x3F6", "MSRValue": "0x8", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of load uops retired that performed one or more locks", "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOCK_LOADS", + "PublicDescription": "Counts the number of load uops retired that performed one or more locks Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x21" + }, + { + "BriefDescription": "Counts the number of load uops retired that were correctly predicated by the memory renaming (MRN) feature.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.MRN_LOADS", + "PublicDescription": "Counts the number of load uops retired that were correctly predicated by the memory renaming (MRN) feature. MRN loads are a part of the memory renaming process that optimizes data forwarding between stores and loads, reducing latency and improving performance. This involves tagging stores that forward to loads and copying the physical source (the actual data) directly from the store to the load. The MRN'd load can complete without accessing the memory, thus reducing load-to-use latency. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x9" + }, + { + "BriefDescription": "Counts the number of store uops retired that were correctly tagged by the memory renaming (MRN) feature.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.MRN_STORES", + "PublicDescription": "Counts the number of store uops retired that were correctly tagged by the memory renaming (MRN) feature. MRN stores are a part of the memory renaming process that optimizes data forwarding between stores and loads, reducing latency and improving performance. This involves tagging stores that forward to loads and copying the physical source (the actual data) directly from the store to the load. The MRN store ensures that it's data is accurately forwarded to matching subsequent loads. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xa" + }, + { + "BriefDescription": "Counts the number of memory uops retired that were splits.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT", + "PublicDescription": "Counts the number of memory uops retired that were splits. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x43" + }, + { + "BriefDescription": "Counts the number of retired split load uops.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT_LOADS", + "PublicDescription": "Counts the number of retired split load uops. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x41" + }, + { + "BriefDescription": "Counts the number of retired split store uops.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.SPLIT_STORES", + "PublicDescription": "Counts the number of retired split store uops. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x42" + }, + { + "BriefDescription": "Counts the number of memory uops retired that missed in the second level TLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.STLB_MISS", + "PublicDescription": "Counts the number of memory uops retired that missed in the second level TLB. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x13" + }, + { + "BriefDescription": "Counts the number of load uops retired that miss in the second Level TLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.STLB_MISS_LOADS", + "PublicDescription": "Counts the number of load uops retired that miss in the second Level TLB. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x11" + }, + { + "BriefDescription": "Counts the number of store uops retired that miss in the second level TLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.STLB_MISS_STORES", + "PublicDescription": "Counts the number of store uops retired that miss in the second level TLB. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x12" + }, + { + "BriefDescription": "Counts the number of stores uops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", + "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", - "PublicDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of stores uops retired. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x6" }, @@ -175,5 +628,13 @@ "PublicDescription": "Counts demand read for ownership (RFO) requests and software prefetches for exclusive ownership (PREFETCHW) that have any type of response. Available PDIST counters: 0", "SampleAfterValue": "100003", "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to an icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.ICACHE", + "SampleAfterValue": "1000003", + "UMask": "0x20" } ] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/counter.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/counter.json index a0eaf5b6f2bc..03c2e5f77204 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/counter.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/counter.json @@ -1,7 +1,82 @@ [ { "Unit": "core", - "CountersNumFixed": "3", - "CountersNumGeneric": "39" + "CountersNumFixed": "7", + "CountersNumGeneric": "8" + }, + { + "Unit": "B2CMI", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "CHA", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "IMC", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "B2HOT", + "CountersNumFixed": "0", + "CountersNumGeneric": 4 + }, + { + "Unit": "IIO", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "IRP", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "UPI", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "B2UPI", + "CountersNumFixed": "0", + "CountersNumGeneric": 4 + }, + { + "Unit": "B2CXL", + "CountersNumFixed": "0", + "CountersNumGeneric": 4 + }, + { + "Unit": "UBOX", + "CountersNumFixed": "0", + "CountersNumGeneric": "2" + }, + { + "Unit": "PCU", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "CHACMS", + "CountersNumFixed": "0", + "CountersNumGeneric": "4" + }, + { + "Unit": "MDF", + "CountersNumFixed": "0", + "CountersNumGeneric": 4 + }, + { + "Unit": "CXLCM", + "CountersNumFixed": "0", + "CountersNumGeneric": 8 + }, + { + "Unit": "CXLDP", + "CountersNumFixed": "0", + "CountersNumGeneric": 4 } ] \ No newline at end of file diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/cwf-metrics.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/cwf-metrics.json new file mode 100644 index 000000000000..bff8841096cd --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/cwf-metrics.json @@ -0,0 +1,310 @@ +[ + { + "BriefDescription": "C1 residency percent per core", + "MetricExpr": "cstate_core@c1\\-residency@ / msr@tsc@", + "MetricGroup": "Power", + "MetricName": "C1_Core_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C2 residency percent per package", + "MetricExpr": "cstate_pkg@c2\\-residency@ / msr@tsc@", + "MetricGroup": "Power", + "MetricName": "C2_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C6 residency percent per core", + "MetricExpr": "cstate_core@c6\\-residency@ / msr@tsc@", + "MetricGroup": "Power", + "MetricName": "C6_Core_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C6 residency percent per package", + "MetricExpr": "cstate_pkg@c6\\-residency@ / msr@tsc@", + "MetricGroup": "Power", + "MetricName": "C6_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Cycles per instruction retired; indicating how much time each executed instruction took; in units of cycles", + "MetricExpr": "CPU_CLK_UNHALTED.THREAD / INST_RETIRED.ANY", + "MetricName": "cpi", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "The average number of cores that are in cstate C0 as observed by the power control unit (PCU)", + "MetricExpr": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C0 / pcu_0@UNC_P_CLOCKTICKS@ * #num_packages", + "MetricName": "cpu_cstate_c0" + }, + { + "BriefDescription": "The average number of cores are in cstate C6 as observed by the power control unit (PCU)", + "MetricExpr": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C6 / pcu_0@UNC_P_CLOCKTICKS@ * #num_packages", + "MetricName": "cpu_cstate_c6" + }, + { + "BriefDescription": "CPU operating frequency (in GHz)", + "MetricExpr": "CPU_CLK_UNHALTED.THREAD / CPU_CLK_UNHALTED.REF_TSC * #SYSTEM_TSC_FREQ / 1e9", + "MetricName": "cpu_operating_frequency", + "ScaleUnit": "1GHz" + }, + { + "BriefDescription": "Percentage of time spent in the active CPU power state C0", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC / msr@tsc@", + "MetricName": "cpu_utilization", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Ratio of number of completed page walks (for all page sizes) caused by demand data loads to the total number of completed instructions", + "MetricExpr": "DTLB_LOAD_MISSES.WALK_COMPLETED / INST_RETIRED.ANY", + "MetricName": "dtlb_2nd_level_load_mpi", + "PublicDescription": "Ratio of number of completed page walks (for all page sizes) caused by demand data loads to the total number of completed instructions. This implies it missed in the DTLB and further levels of TLB", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of completed page walks (for all page sizes) caused by demand data stores to the total number of completed instructions", + "MetricExpr": "DTLB_STORE_MISSES.WALK_COMPLETED / INST_RETIRED.ANY", + "MetricName": "dtlb_2nd_level_store_mpi", + "PublicDescription": "Ratio of number of completed page walks (for all page sizes) caused by demand data stores to the total number of completed instructions. This implies it missed in the DTLB and further levels of TLB", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Bandwidth observed by the integrated I/O traffic controller (IIO) of IO reads that are initiated by end device controllers that are requesting memory from the CPU", + "MetricExpr": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.ALL_PARTS * 4 / 1e6 / duration_time", + "MetricName": "iio_bandwidth_read", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth observed by the integrated I/O traffic controller (IIO) of IO writes that are initiated by end device controllers that are writing memory to the CPU", + "MetricExpr": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.ALL_PARTS * 4 / 1e6 / duration_time", + "MetricName": "iio_bandwidth_write", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO reads that are initiated by end device controllers that are requesting memory from the CPU", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of inbound IO reads that are initiated by end device controllers that are requesting memory from the CPU and miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read_l3_miss", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO reads that are initiated by end device controllers that are requesting memory from the local CPU socket", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_LOCAL * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read_local", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO reads that are initiated by end device controllers that are requesting memory from a remote CPU socket", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_REMOTE * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_read_remote", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO writes that are initiated by end device controllers that are writing memory to the CPU", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOM + UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of inbound IO writes that are initiated by end device controllers that are writing memory to the CPU", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOM + UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write_l3_miss", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO writes that are initiated by end device controllers that are writing memory to the local CPU socket", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOM_LOCAL + UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_LOCAL) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write_local", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth of IO writes that are initiated by end device controllers that are writing memory to a remote CPU socket", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOM_REMOTE + UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_REMOTE) * 64 / 1e6 / duration_time", + "MetricName": "io_bandwidth_write_remote", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "The percent of inbound full cache line writes initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM / UNC_CHA_TOR_INSERTS.IO_ITOM", + "MetricName": "io_full_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "The number of times per second that ownership of a cacheline was stolen from the integrated IO controller before it was able to write back the modified line", + "MetricExpr": "(UNC_I_MISC1.LOST_FWD + UNC_I_MISC1.SEC_RCVD_INVLD) / duration_time", + "MetricName": "io_lost_fwd", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "Message Signaled Interrupts (MSI) per second sent by the integrated I/O traffic controller (IIO) to System Configuration Controller (Ubox)", + "MetricExpr": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED / duration_time", + "MetricName": "io_msi", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_RFO) / duration_time", + "MetricName": "io_number_of_partial_pci_writes", + "ScaleUnit": "1per_sec" + }, + { + "BriefDescription": "The percent of inbound partial writes initiated by IO that miss the L3 cache", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_MISS_RFO) / (UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR + UNC_CHA_TOR_INSERTS.IO_RFO)", + "MetricName": "io_partial_write_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "The percent of inbound reads initiated by IO that miss the L3 cache", + "MetricExpr": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR / UNC_CHA_TOR_INSERTS.IO_PCIRDCUR", + "MetricName": "io_read_l3_miss", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Ratio of number of completed page walks (for all page sizes) caused by a code fetch to the total number of completed instructions", + "MetricExpr": "ITLB_MISSES.WALK_COMPLETED / INST_RETIRED.ANY", + "MetricName": "itlb_2nd_level_mpi", + "PublicDescription": "Ratio of number of completed page walks (for all page sizes) caused by a code fetch to the total number of completed instructions. This implies it missed in the ITLB (Instruction TLB) and further levels of TLB", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of code read requests missing in L1 instruction cache (includes prefetches) to the total number of completed instructions", + "MetricExpr": "ICACHE.MISSES / INST_RETIRED.ANY", + "MetricName": "l1_i_code_read_misses_with_prefetches_per_instr", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of demand load requests hitting in L1 data cache to the total number of completed instructions", + "MetricExpr": "MEM_LOAD_UOPS_RETIRED.L1_HIT / INST_RETIRED.ANY", + "MetricName": "l1d_demand_data_read_hits_per_instr", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of completed demand load requests hitting in L2 cache to the total number of completed instructions", + "MetricExpr": "MEM_LOAD_UOPS_RETIRED.L2_HIT / INST_RETIRED.ANY", + "MetricName": "l2_demand_data_read_hits_per_instr", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of completed data read request missing L2 cache to the total number of completed instructions", + "MetricExpr": "MEM_LOAD_UOPS_RETIRED.L2_MISS / INST_RETIRED.ANY", + "MetricName": "l2_demand_data_read_mpi", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of code read requests missing last level core cache (includes demand w/ prefetches) to the total number of completed instructions", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IA_MISS_CRD + UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF) / INST_RETIRED.ANY", + "MetricName": "llc_code_read_mpi_demand_plus_prefetch", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Ratio of number of data read requests missing last level core cache (includes demand w/ prefetches) to the total number of completed instructions", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF + UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA) / INST_RETIRED.ANY", + "MetricName": "llc_data_read_mpi_demand_plus_prefetch", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Average latency of a last level cache (LLC) demand data read miss (read memory access) in nano seconds", + "MetricExpr": "1e9 * (UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT / UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT) / (UNC_CHA_CLOCKTICKS / (source_count(UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT) * #num_packages)) * duration_time", + "MetricName": "llc_demand_data_read_miss_latency", + "ScaleUnit": "1ns" + }, + { + "BriefDescription": "Bandwidth (MB/sec) of read requests that miss the last level cache (LLC) and go to local memory", + "MetricExpr": "UNC_CHA_REQUESTS.READS_LOCAL * 64 / 1e6 / duration_time", + "MetricName": "llc_miss_local_memory_bandwidth_read", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth (MB/sec) of write requests that miss the last level cache (LLC) and go to local memory", + "MetricExpr": "UNC_CHA_REQUESTS.WRITES_LOCAL * 64 / 1e6 / duration_time", + "MetricName": "llc_miss_local_memory_bandwidth_write", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth (MB/sec) of read requests that miss the last level cache (LLC) and go to remote memory", + "MetricExpr": "UNC_CHA_REQUESTS.READS_REMOTE * 64 / 1e6 / duration_time", + "MetricName": "llc_miss_remote_memory_bandwidth_read", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Bandwidth (MB/sec) of write requests that miss the last level cache (LLC) and go to remote memory", + "MetricExpr": "UNC_CHA_REQUESTS.WRITES_REMOTE * 64 / 1e6 / duration_time", + "MetricName": "llc_miss_remote_memory_bandwidth_write", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "", + "MetricExpr": "MEM_UOPS_RETIRED.ALL_LOADS / INST_RETIRED.ANY", + "MetricName": "loads_retired_per_instr", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "DDR memory read bandwidth (MB/sec)", + "MetricExpr": "(UNC_M_CAS_COUNT_SCH0.RD + UNC_M_CAS_COUNT_SCH1.RD) * 64 / 1e6 / duration_time", + "MetricName": "memory_bandwidth_read", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "DDR memory bandwidth (MB/sec)", + "MetricExpr": "(UNC_M_CAS_COUNT_SCH0.RD + UNC_M_CAS_COUNT_SCH1.RD + UNC_M_CAS_COUNT_SCH0.WR + UNC_M_CAS_COUNT_SCH1.WR) * 64 / 1e6 / duration_time", + "MetricName": "memory_bandwidth_total", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "DDR memory write bandwidth (MB/sec)", + "MetricExpr": "(UNC_M_CAS_COUNT_SCH0.WR + UNC_M_CAS_COUNT_SCH1.WR) * 64 / 1e6 / duration_time", + "MetricName": "memory_bandwidth_write", + "ScaleUnit": "1MB/s" + }, + { + "BriefDescription": "Memory read that miss the last level cache (LLC) addressed to local DRAM as a percentage of total memory read accesses, does not include LLC prefetches", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_LOCAL) / (UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_REMOTE + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_REMOTE)", + "MetricName": "numa_reads_addressed_to_local_dram", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Memory reads that miss the last level cache (LLC) addressed to remote DRAM as a percentage of total memory read accesses, does not include LLC prefetches", + "MetricExpr": "(UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_REMOTE + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_REMOTE) / (UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_REMOTE + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_REMOTE)", + "MetricName": "numa_reads_addressed_to_remote_dram", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Percentage of cycles spent in System Management Interrupts.", + "MetricExpr": "((msr@aperf@ - cycles) / msr@aperf@ if msr@smi@ > 0 else 0)", + "MetricGroup": "smi", + "MetricName": "smi_cycles", + "MetricThreshold": "smi_cycles > 0.1", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Number of SMI interrupts.", + "MetricExpr": "msr@smi@", + "MetricGroup": "smi", + "MetricName": "smi_num", + "ScaleUnit": "1SMI#" + }, + { + "BriefDescription": "", + "MetricExpr": "MEM_UOPS_RETIRED.ALL_STORES / INST_RETIRED.ANY", + "MetricName": "stores_retired_per_instr", + "ScaleUnit": "1per_instr" + }, + { + "BriefDescription": "Uncore operating frequency in GHz", + "MetricExpr": "UNC_CHA_CLOCKTICKS / (source_count(UNC_CHA_CLOCKTICKS) * #num_packages) / 1e9 / duration_time", + "MetricName": "uncore_frequency", + "ScaleUnit": "1GHz" + }, + { + "BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data transmit bandwidth (MB/sec)", + "MetricExpr": "UNC_UPI_TxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time", + "MetricName": "upi_data_transmit_bw", + "ScaleUnit": "1MB/s" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/floating-point.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/floating-point.json new file mode 100644 index 000000000000..34d7a50d2fd8 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/floating-point.json @@ -0,0 +1,191 @@ +[ + { + "BriefDescription": "Counts the number of cycles when any of the floating point dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of floating point dividers per cycle in the loop stage.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of floating point divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of all types of floating point operations per uop with all default weighting", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc8", + "EventName": "FP_FLOPS_RETIRED.ALL", + "PublicDescription": "Counts the number of all types of floating point operations per uop with all default weighting Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x3" + }, + { + "BriefDescription": "Counts the number of floating point operations that produce 32 bit single precision results", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc8", + "EventName": "FP_FLOPS_RETIRED.FP32", + "PublicDescription": "Counts the number of floating point operations that produce 32 bit single precision results Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of floating point operations that produce 64 bit double precision results", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc8", + "EventName": "FP_FLOPS_RETIRED.FP64", + "PublicDescription": "Counts the number of floating point operations that produce 64 bit double precision results Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a packed 128 bit double precision floating point. This may be SSE or AVX.128 operations.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.128B_DP", + "PublicDescription": "Counts the number of retired instructions whose sources are a packed 128 bit double precision floating point. This may be SSE or AVX.128 operations. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a packed 128 bit single precision floating point. This may be SSE or AVX.128 operations.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.128B_SP", + "PublicDescription": "Counts the number of retired instructions whose sources are a packed 128 bit single precision floating point. This may be SSE or AVX.128 operations. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a packed 256 bit double precision floating point.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.256B_DP", + "PublicDescription": "Counts the number of retired instructions whose sources are a packed 256 bit double precision floating point. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a packed 256 bit single precision floating point.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.256B_SP", + "PublicDescription": "Counts the number of retired instructions whose sources are a packed 256 bit single precision floating point. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a scalar 32bit single precision floating point.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.32B_SP", + "PublicDescription": "Counts the number of retired instructions whose sources are a scalar 32bit single precision floating point. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of retired instructions whose sources are a scalar 64 bit double precision floating point.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.64B_DP", + "PublicDescription": "Counts the number of retired instructions whose sources are a scalar 64 bit double precision floating point. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the total number of floating point retired instructions.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc7", + "EventName": "FP_INST_RETIRED.ALL", + "PublicDescription": "Counts the total number of floating point retired instructions. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x3f" + }, + { + "BriefDescription": "Counts the number of uops executed on all floating point ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x1f" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P3", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0, 1, 2, 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0x1e" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer store data port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.STD", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of floating point operations retired that required microcode assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FP_ASSIST", + "PublicDescription": "Counts the number of floating point operations retired that required microcode assist, which is not a reflection of the number of FP operations, instructions or uops.", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of floating point divide uops retired (x87 and sse, including x87 sqrt).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.FPDIV", + "PublicDescription": "Counts the number of floating point divide uops retired (x87 and sse, including x87 sqrt). Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/frontend.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/frontend.json index 7a9250e5c8f2..b71d6ae1abe8 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/frontend.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/frontend.json @@ -1,4 +1,206 @@ [ + { + "BriefDescription": "Counts the total number of BACLEARS due to all branch types including conditional and unconditional jumps, returns, and indirect branches.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe6", + "EventName": "BACLEARS.ANY", + "PublicDescription": "Counts the total number of BACLEARS, which occur when the Branch Target Buffer (BTB) prediction or lack thereof, was corrected by a later branch predictor in the frontend. Includes BACLEARS due to all branch types including conditional and unconditional jumps, returns, and indirect branches.", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of BACLEARS due to a conditional jump.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe6", + "EventName": "BACLEARS.COND", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of BACLEARS due to an indirect branch.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe6", + "EventName": "BACLEARS.INDIRECT", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of BACLEARS due to a return branch.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe6", + "EventName": "BACLEARS.RETURN", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of BACLEARS due to a direct, unconditional jump.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe6", + "EventName": "BACLEARS.UNCOND", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "PublicDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior Available PDIST counters: 0,1", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "PublicDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "PublicDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "PublicDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ITLB_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "PublicDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "PublicDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L2_HIT", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L2 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L2_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xe" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x6" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and did not have to snoop another core.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT_NO_SNOOP", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and did not have to snoop another core. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and required a snoop (that resulted in a snoop miss, snoop hitm, snoop hit with fwd, or snoop hit with no fwd).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT_WITH_SNOOP", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and required a snoop (that resulted in a snoop miss, snoop hitm, snoop hit with fwd, or snoop hit with no fwd). Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 and L3 caches.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 and L3 caches. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that hit in the second level TLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ITLB_STLB_HIT", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that hit in the second level TLB. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that also missed the second level TLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ITLB_STLB_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that also missed the second level TLB. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of instructions retired that were preceded by empty issue slots at allocation due to an Instruction L1 cache miss, that matched a current outstanding prefetch request initiated by a Code SWPF stream.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.LATE_SWPF", + "PublicDescription": "Counts the number of instructions retired that were preceded by empty issue slots at allocation due to an Instruction L1 cache miss, that matched a current outstanding prefetch request initiated by a Code SWPF stream. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, { "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump.", "Counter": "0,1,2,3,4,5,6,7", @@ -7,6 +209,14 @@ "SampleAfterValue": "1000003", "UMask": "0x3" }, + { + "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump and the instruction cache registers bytes are present.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x80", + "EventName": "ICACHE.HIT", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump and the instruction cache registers bytes are not present. -", "Counter": "0,1,2,3,4,5,6,7", @@ -14,5 +224,21 @@ "EventName": "ICACHE.MISSES", "SampleAfterValue": "1000003", "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of cycles that the micro-sequencer is busy.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe7", + "EventName": "MS_DECODED.MS_BUSY", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of times entered into a ucode flow in the FEC. Includes inserted flows due to front-end detected faults or assists.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe7", + "EventName": "MS_DECODED.MS_ENTRY", + "SampleAfterValue": "1000003", + "UMask": "0x1" } ] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/memory.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/memory.json index 58e543550279..ae9095a090e5 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/memory.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/memory.json @@ -1,4 +1,96 @@ [ + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a core bound stall including a store address match, a DTLB miss or a page walk that detains the load from retiring.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_BOUND_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0xf4" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x81" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER_AT_RET", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0xc0" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82" + }, + { + "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MEMORY_ORDERING", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MEMORY_ORDERING_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8002" + }, + { + "BriefDescription": "Counts misaligned loads that are 4K page splits.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x13", + "EventName": "MISALIGN_MEM_REF.LOAD_PAGE_SPLIT", + "PublicDescription": "Counts misaligned loads that are 4K page splits. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts misaligned stores that are 4K page splits.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x13", + "EventName": "MISALIGN_MEM_REF.STORE_PAGE_SPLIT", + "PublicDescription": "Counts misaligned stores that are 4K page splits. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts demand data reads that were not supplied by the L3 cache.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/other.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/other.json new file mode 100644 index 000000000000..2c8721903610 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/other.json @@ -0,0 +1,20 @@ +[ + { + "BriefDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. [This event is alias to MISC_RETIRED.LBR_INSERTS]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe4", + "EventName": "LBR_INSERTS.ANY", + "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. [This event is alias to MISC_RETIRED.LBR_INSERTS] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the total number of BTCLEARS.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe8", + "EventName": "PREDICTION.BTCLEAR", + "PublicDescription": "Counts the total number of BTCLEARS which occurs when the Branch Target Buffer (BTB) predicts a taken branch.", + "SampleAfterValue": "1000003", + "UMask": "0x1" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/pipeline.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/pipeline.json index 26bd12fefa3d..3d10dcffb8fc 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/pipeline.json @@ -1,4 +1,38 @@ [ + { + "BriefDescription": "Counts the number of cycles when any of the floating point or integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.DIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x3" + }, + { + "BriefDescription": "Counts the number of cycles when any of the integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts number of active integer dividers per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of integer divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the total number of branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -7,6 +41,211 @@ "PublicDescription": "Counts the total number of instructions in which the instruction pointer (IP) of the processor is resteered due to a branch instruction and the branch instruction successfully retires. All branch type instructions are accounted for. Available PDIST counters: 0,1", "SampleAfterValue": "1000003" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_NEAR_IND", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_NEAR_IND_OR_RET", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x58" + }, + { + "BriefDescription": "Counts the number of conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND", + "PublicDescription": "Counts the number of conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x7" + }, + { + "BriefDescription": "Counts the number of not taken conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_NTAKEN", + "PublicDescription": "Counts the number of not taken conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of taken conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN", + "PublicDescription": "Counts the number of taken conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x3" + }, + { + "BriefDescription": "Counts the number of taken backward conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN_BWD", + "PublicDescription": "Counts the number of taken backward conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of taken forward conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN_FWD", + "PublicDescription": "Counts the number of taken forward conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of near CALL branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_CALL", + "PublicDescription": "Counts the number of near CALL branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x30" + }, + { + "BriefDescription": "Counts the number of near direct CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIRECT_CALL", + "PublicDescription": "Counts the number of near direct CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of near direct JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIRECT_JMP", + "PublicDescription": "Counts the number of near direct JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIR_CALL", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIR_JMP", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT", + "PublicDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, + { + "BriefDescription": "Counts the number of near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_CALL", + "PublicDescription": "Counts the number of near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of near indirect JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_JMP", + "PublicDescription": "Counts the number of near indirect JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of near indirect JMP, near indirect CALL, and RET branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND_OR_RET]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN", + "PublicDescription": "Counts the number of near indirect JMP, near indirect CALL, and RET branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND_OR_RET] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x58" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_IND_CALL", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_IND_JMP", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of near JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_JMP", + "PublicDescription": "Counts the number of near JMP branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xc0" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_RETURN]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_RET", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_RETURN] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of near RET branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_RET]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_RETURN", + "PublicDescription": "Counts the number of near RET branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_RET] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of near taken branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_TAKEN", + "PublicDescription": "Counts the number of near taken branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xfb" + }, { "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -16,10 +255,47 @@ "SampleAfterValue": "1000003" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "This event is deprecated. [This event is alias to BR_MISP_RETIRED.NEAR_INDIRECT]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.ALL_NEAR_IND", + "PublicDescription": "This event is deprecated. [This event is alias to BR_MISP_RETIRED.NEAR_INDIRECT] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, + { + "BriefDescription": "Counts the number of mispredicted not taken conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.COND_NTAKEN", + "PublicDescription": "Counts the number of mispredicted not taken conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of mispredicted taken conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.COND_TAKEN", + "PublicDescription": "Counts the number of mispredicted taken conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x3" + }, + { + "BriefDescription": "Counts the number of mispredicted near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_MISP_RETIRED.ALL_NEAR_IND]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.NEAR_INDIRECT", + "PublicDescription": "Counts the number of mispredicted near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_MISP_RETIRED.ALL_NEAR_IND] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, + { + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", - "SampleAfterValue": "1000003", + "SampleAfterValue": "2000003", "UMask": "0x2" }, { @@ -27,13 +303,13 @@ "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.CORE_P", - "SampleAfterValue": "1000003" + "SampleAfterValue": "2000003" }, { "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", - "SampleAfterValue": "1000003", + "SampleAfterValue": "2000003", "UMask": "0x3" }, { @@ -42,14 +318,14 @@ "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.REF_TSC_P", "PublicDescription": "Counts the number of reference cycles that the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. This event is not affected by core frequency changes and increments at a fixed frequency that is also used for the Time Stamp Counter (TSC). This event uses a programmable general purpose performance counter.", - "SampleAfterValue": "1000003", + "SampleAfterValue": "2000003", "UMask": "0x1" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", - "SampleAfterValue": "1000003", + "SampleAfterValue": "2000003", "UMask": "0x2" }, { @@ -57,14 +333,14 @@ "Counter": "0,1,2,3,4,5,6,7", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.THREAD_P", - "SampleAfterValue": "1000003" + "SampleAfterValue": "2000003" }, { "BriefDescription": "Fixed Counter: Counts the number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", "PublicDescription": "Fixed Counter: Counts the number of instructions retired. Available PDIST counters: 32", - "SampleAfterValue": "1000003", + "SampleAfterValue": "2000003", "UMask": "0x1" }, { @@ -73,15 +349,339 @@ "EventCode": "0xc0", "EventName": "INST_RETIRED.ANY_P", "PublicDescription": "Counts the number of instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "2000003" + }, + { + "BriefDescription": "Counts the number of uops executed on secondary integer ports 0,1,2,3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.2ND", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of uops executed on all Integer ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of uops executed on a load port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.LD", + "PublicDescription": "Counts the number of uops executed on a load port. This event counts for integer uops even if the destination is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P3", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0,1, 2, 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0x78" + }, + { + "BriefDescription": "Counts the number of uops executed on a Store address port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STA", + "PublicDescription": "Counts the number of uops executed on a Store address port. This event counts integer uops even if the data source is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of uops executed on an integer store data and jump port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STD_JMP", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked because it initially appears to be store forward blocked, but subsequently is shown not to be blocked based on 4K alias check.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.ADDRESS_ALIAS", + "PublicDescription": "Counts the number of retired loads that are blocked because it initially appears to be store forward blocked, but subsequently is shown not to be blocked based on 4K alias check. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked for any of the following reasons: DTLB miss, address alias, store forward or data unknown (includes memory disambiguation blocks and ESP consuming load blocks) and others.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.ALL", + "PublicDescription": "Counts the number of retired loads that are blocked for any of the following reasons: DTLB miss, address alias, store forward or data unknown (includes memory disambiguation blocks and ESP consuming load blocks) and others. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1f" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked because its address exactly matches an older store whose data is not ready.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.DATA_UNKNOWN", + "PublicDescription": "Counts the number of retired loads that are blocked because its address exactly matches an older store whose data is not ready. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked because its address partially overlapped with an older store.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.STORE_FORWARD", + "PublicDescription": "Counts the number of retired loads that are blocked because its address partially overlapped with an older store. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the total number of machine clears for any reason including, but not limited to, memory ordering, memory disambiguation, SMC, and FP assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY", "SampleAfterValue": "1000003" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x80ff" + }, + { + "BriefDescription": "Counts the number of machine clears due to memory ordering in which an internal load passes an older store within the same CPU.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.DISAMBIGUATION", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.DISAMBIGUATION_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8008" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of machine clears due to memory renaming.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8010" + }, + { + "BriefDescription": "Counts the number of machine clears due to a page fault. Counts both I-Side and D-Side (Loads/Stores) page faults. A page fault occurs when either the page is not present, or an access violation occurs.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.PAGE_FAULT", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of machine clears due to program modifying data (self modifying code) within 1K of a recently fetched code page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.SMC", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of machine clears due to program modifying data (cross-core modifying code) within 1K of a recently fetched code page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.XMC", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to LBR_INSERTS.ANY]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xe4", + "EventName": "MISC_RETIRED.LBR_INSERTS", + "PublicDescription": "This event is deprecated. [This event is alias to LBR_INSERTS.ANY] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "PublicDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "PublicDescription": "Counts the number of LFENCE instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "PublicDescription": "Counts the number of accesses to KeyLocker cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "PublicDescription": "Counts the number of misses to KeyLocker cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x11" + }, + { + "BriefDescription": "Counts the number of issue slots in a UMWAIT or TPAUSE instruction where no uop issues due to the instruction putting the CPU into the C0.1 activity state. For Tremont, UMWAIT and TPAUSE will only put the CPU into C0.1 activity state (not C0.2 activity state).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.C01_MS_SCB", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of issue slots where no uop could issue due to an IQ scoreboard that stalls allocation until a specified older uop retires or (in the case of jump scoreboard) executes. Commonly executed instructions with IQ scoreboards include LFENCE and MFENCE.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.IQ_JEU_SCB", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.NON_C01_MS_SCB", + "PublicDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires. The most commonly executed instruction with an MS scoreboard is PAUSE.", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, { "BriefDescription": "Fixed Counter: Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear.", - "Counter": "36", + "Counter": "Fixed counter 4", "EventName": "TOPDOWN_BAD_SPECULATION.ALL", "SampleAfterValue": "1000003", "UMask": "0x5" }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.ALL_P", + "PublicDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear. Only issue slots wasted due to fast nukes such as memory ordering nukes are counted. Other nukes are not accounted for. Counts all issue slots blocked during this recovery window, including relevant microcode flows, and while uops are not yet available in the instruction queue (IQ) or until an FE_BOUND event occurs besides OTHER and CISC. Also includes the issue slots that were consumed by the backend but were thrown away because they were younger than the mispredict or machine clear.", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to Fast Nukes such as Memory Ordering Machine clears and MRN nukes", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.FASTNUKE", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to a branch mispredict that resulted in LSD exit.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.LSD_MISPREDICT", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a machine clear (nuke) of any kind including memory ordering and memory disambiguation.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.MACHINE_CLEARS", + "SampleAfterValue": "1000003", + "UMask": "0x3" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to Branch Mispredict", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.MISPREDICT", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to a machine clear (nuke).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of retirement slots not consumed due to backend stalls. [This event is alias to TOPDOWN_BE_BOUND.ALL_P]", "Counter": "0,1,2,3,4,5,6,7", @@ -90,6 +690,14 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to due to certain allocation restrictions.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.ALLOC_RESTRICTIONS", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of retirement slots not consumed due to backend stalls. [This event is alias to TOPDOWN_BE_BOUND.ALL]", "Counter": "0,1,2,3,4,5,6,7", @@ -98,18 +706,215 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to LSD entry.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.LSD", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to memory reservation stall (scheduler not being able to accept another uop). This could be caused by RSV full or load/store buffer block.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.MEM_SCHEDULER", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to IEC and FPC RAT stalls - which can be due to the FIQ and IEC reservation station stall (integer, FP and SIMD scheduler not being able to accept another uop).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.NON_MEM_SCHEDULER", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to mrbl stall. A marble refers to a physical register file entry, also known as the physical destination (PDST).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.REGISTER", + "PublicDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to mrbl stall. A 'marble' refers to a physical register file entry, also known as the physical destination (PDST).", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to ROB full", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.REORDER_BUFFER", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to iq/jeu scoreboards or ms scb", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.SERIALIZATION", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, { "BriefDescription": "Fixed Counter: Counts the number of retirement slots not consumed due to front end stalls.", - "Counter": "37", + "Counter": "Fixed counter 5", "EventName": "TOPDOWN_FE_BOUND.ALL", "SampleAfterValue": "1000003", "UMask": "0x6" }, + { + "BriefDescription": "Counts the number of retirement slots not consumed due to front end stalls.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x9c", + "EventName": "TOPDOWN_FE_BOUND.ALL_P", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to BAClear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to BTClear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to ms", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.CISC", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to decode stall", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to frontend bandwidth restrictions due to decode, predecode, cisc, and other limitations.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.FRONTEND_BANDWIDTH", + "SampleAfterValue": "1000003", + "UMask": "0x8d" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to latency related stalls including BACLEARs, BTCLEARs, ITLB misses, and ICache misses.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.FRONTEND_LATENCY", + "SampleAfterValue": "1000003", + "UMask": "0x72" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to itlb miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.ITLB_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend that do not categorize into any other common frontend stall", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to predecode wrong", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Fixed Counter: Counts the number of consumed retirement slots.", - "Counter": "38", + "Counter": "Fixed counter 6", "EventName": "TOPDOWN_RETIRING.ALL", "SampleAfterValue": "1000003", "UMask": "0x7" + }, + { + "BriefDescription": "Counts the number of consumed retirement slots.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "TOPDOWN_RETIRING.ALL_P", + "PublicDescription": "Counts the number of consumed retirement slots. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of uops issued by the front end every cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x0e", + "EventName": "UOPS_ISSUED.ANY", + "PublicDescription": "Counts the number of uops issued by the front end every cycle. When 4-uops are requested and only 2-uops are delivered, the event counts 2. Uops_issued correlates to the number of ROB entries. If uop takes 2 ROB slots it counts as 2 uops_issued.", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of uops retired that were dual destination.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.DUAL_DEST", + "PublicDescription": "Counts the number of uops retired that were dual destination. Dual destination (dual dest) micro-operations require writing to two separate destination registers or memory addresses upon execution. These uops consume two entries in the ROB and tracked separately because they involve more complex operations that necessitate multiple results. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of uops retired that are the last uop of a macro-instruction.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.EOM", + "PublicDescription": "Counts the number of uops retired that are the last uop of a macro-instruction. EOM uops indicate the 'end of a macro-instruction' and play a crucial role in the processor's control flow and recovery mechanisms.", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of integer divide uops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.IDIV", + "PublicDescription": "Counts the number of integer divide uops retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of uops retired that originated from a loop stream detector.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.LSD", + "PublicDescription": "Counts the number of uops retired that originated from a loop stream detector. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of uops that are from the complex flows issued by the micro-sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.MS", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of x87 uops retired, includes those in ms flows.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.X87", + "SampleAfterValue": "1000003" } ] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cache.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cache.json new file mode 100644 index 000000000000..278837f2068a --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cache.json @@ -0,0 +1,3413 @@ +[ + { + "BriefDescription": "Clockticks for CMS units attached to CHA", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_CHACMS_CLOCKTICKS", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "CHACMS" + }, + { + "BriefDescription": "UNC_CHACMS_DISTRESS_ASSERTED", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHACMS_DISTRESS_ASSERTED", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "CHACMS" + }, + { + "BriefDescription": "Counts the number of cycles FAST trigger is received from the global FAST distress wire.", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHACMS_RING_SRC_THRTL", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "CHACMS" + }, + { + "BriefDescription": "Number of CHA clock cycles while the event is enabled", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_CHA_CLOCKTICKS", + "PerPkg": "1", + "PublicDescription": "Clockticks of the uncore caching and home agent (CHA)", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts transactions that looked into the multi-socket cacheline Directory state, and therefore did not send a snoop because the Directory indicated it was not needed.", + "Counter": "0,1,2,3", + "EventCode": "0x53", + "EventName": "UNC_CHA_DIR_LOOKUP.NO_SNP", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts transactions that looked into the multi-socket cacheline Directory state, and sent one or more snoops, because the Directory indicated it was needed.", + "Counter": "0,1,2,3", + "EventCode": "0x53", + "EventName": "UNC_CHA_DIR_LOOKUP.SNP", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts only multi-socket cacheline Directory state updates memory writes issued from the HA pipe. This does not include memory write requests which are for I (Invalid) or E (Exclusive) cachelines.", + "Counter": "0,1,2,3", + "EventCode": "0x54", + "EventName": "UNC_CHA_DIR_UPDATE.HA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts only multi-socket cacheline Directory state updates due to memory writes issued from the TOR pipe which are the result of remote transaction hitting the SF/LLC and returning data Core2Core. This does not include memory write requests which are for I (Invalid) or E (Exclusive) cachelines.", + "Counter": "0,1,2,3", + "EventCode": "0x54", + "EventName": "UNC_CHA_DIR_UPDATE.TOR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Distress signal assertion for dynamic prefetch throttle (DPT). Threshold for distress signal assertion reached in TOR or IRQ (immediate cause for triggering).", + "Counter": "0,1,2,3", + "EventCode": "0x59", + "EventName": "UNC_CHA_DISTRESS_ASSERTED.DPT_ANY", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x3", + "Unit": "CHA" + }, + { + "BriefDescription": "Distress signal assertion for dynamic prefetch throttle (DPT). Threshold for distress signal assertion reached in IRQ (immediate cause for triggering).", + "Counter": "0,1,2,3", + "EventCode": "0x59", + "EventName": "UNC_CHA_DISTRESS_ASSERTED.DPT_IRQ", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Distress signal assertion for dynamic prefetch throttle (DPT). Threshold for distress signal assertion reached in TOR (immediate cause for triggering).", + "Counter": "0,1,2,3", + "EventCode": "0x59", + "EventName": "UNC_CHA_DISTRESS_ASSERTED.DPT_TOR", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts when a normal (Non-Isochronous) full line write is issued from the CHA to the any of the memory controller channels.", + "Counter": "0,1,2,3", + "EventCode": "0x5b", + "EventName": "UNC_CHA_IMC_WRITES_COUNT.FULL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "CHA to iMC Full Line Writes Issued : ISOCH Full Line : Counts the total number of full line writes issued from the HA into the memory controller.", + "Counter": "0,1,2,3", + "EventCode": "0x5b", + "EventName": "UNC_CHA_IMC_WRITES_COUNT.FULL_PRIORITY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "CHA to iMC Full Line Writes Issued : Partial Non-ISOCH : Counts the total number of full line writes issued from the HA into the memory controller.", + "Counter": "0,1,2,3", + "EventCode": "0x5b", + "EventName": "UNC_CHA_IMC_WRITES_COUNT.PARTIAL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "CHA to iMC Full Line Writes Issued : ISOCH Partial : Counts the total number of full line writes issued from the HA into the memory controller.", + "Counter": "0,1,2,3", + "EventCode": "0x5b", + "EventName": "UNC_CHA_IMC_WRITES_COUNT.PARTIAL_PRIORITY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: All Requests to Remotely Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.ALL_REMOTE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : All transactions from Remote Agents", + "UMask": "0x17e0ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: CRd Requests", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.CODE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : CRd Requests", + "UMask": "0x1bd0ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests and Read Prefetches", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.DATA_RD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x1bc1ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests, Read Prefetches, and Snoops", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.DATA_READ_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Data Reads", + "UMask": "0x1fc1ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.DATA_READ_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Demand Data Reads, Core and LLC prefetches", + "UMask": "0x841ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests, Read Prefetches, and Snoops which miss the Cache", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.DATA_READ_MISS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Data Read Misses", + "UMask": "0x1fc101", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: All Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCALLY_HOMED_ADDRESS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Transactions homed locally", + "UMask": "0xbdfff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Code Read Requests and Code Read Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_CODE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : CRd Requests", + "UMask": "0x19d0ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests and Read Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_DATA_RD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x19c1ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Code Read Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_DMND_CODE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : CRd Requests", + "UMask": "0x1850ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_DMND_DATA_RD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x1841ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: RFO Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_DMND_RFO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : RFO Requests", + "UMask": "0x1848ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: LLC Prefetch Requests to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_LLC_PF", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x189dff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: All Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_PF", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x199dff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Code Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_PF_CODE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : CRd Requests", + "UMask": "0x1910ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Read Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_PF_DATA_RD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x1981ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: RFO Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_PF_RFO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : RFO Requests", + "UMask": "0x1908ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: RFO Requests and RFO Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.LOCAL_RFO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : RFO Requests", + "UMask": "0x19c8ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: All Requests to Remotely Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.REMOTELY_HOMED_ADDRESS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Transactions homed remotely : Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. : Transaction whose address resides in a remote MC", + "UMask": "0x15dfff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Code Read/Prefetch Requests from a Remote Socket", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.REMOTE_CODE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : CRd Requests", + "UMask": "0x1a10ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Data Read/Prefetch Requests from a Remote Socket", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.REMOTE_DATA_RD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed - this includes code, data, prefetches and hints coming from L2. This has numerous filters available. Note the non-standard filtering equation. This event will count requests that lookup the cache multiple times with multiple increments. One must ALWAYS set umask bit 0 and select a state or states to match. Otherwise, the event will count nothing. CHAFilter0[24:21,17] bits correspond to [FMESI] state. Read transactions", + "UMask": "0x1a01ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: RFO Requests/Prefetches from a Remote Socket", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.REMOTE_RFO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : RFO Requests", + "UMask": "0x1a08ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Snoop Requests from a Remote Socket", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.REMOTE_SNP", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Counts the number of times the LLC was accessed", + "UMask": "0x1c19ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: All RFO and RFO Prefetches", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.RFO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : All RFOs - Demand and Prefetches", + "UMask": "0x1bc8ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: RFO Requests and RFO Prefetches to Locally Homed Memory", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.RFO_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Locally HOMed RFOs - Demand and Prefetches", + "UMask": "0x9c8ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Writes to Locally Homed Memory (includes writebacks from L1/L2)", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.WRITE_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Writes", + "UMask": "0x842ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Cache Lookups: Writes to Remotely Homed Memory (includes writebacks from L1/L2)", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_CHA_LLC_LOOKUP.WRITE_REMOTE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cache Lookups : Remote Writes", + "UMask": "0x17c2ff", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : All Lines Victimized", + "UMask": "0xf", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : IA traffic : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.IA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : IO traffic : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.IO", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.LOCAL_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Local - All Lines", + "UMask": "0x200f", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.LOCAL_E", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Local - Lines in E State", + "UMask": "0x2002", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.LOCAL_F", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Local - Lines in F State", + "UMask": "0x2008", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.LOCAL_M", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Local - Lines in M State", + "UMask": "0x2001", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.LOCAL_S", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Local - Lines in S State", + "UMask": "0x2004", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.REMOTE_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Remote - All Lines", + "UMask": "0x800f", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.REMOTE_E", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Remote - Lines in E State", + "UMask": "0x8002", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.REMOTE_M", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Remote - Lines in M State", + "UMask": "0x8001", + "Unit": "CHA" + }, + { + "BriefDescription": "Lines Victimized : Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.REMOTE_S", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Remote - Lines in S State", + "UMask": "0x8004", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.TOTAL_E", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Lines in E state", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.TOTAL_M", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Lines in M state", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the number of lines that were victimized on a fill. This can be filtered by the state that the line was in.", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_CHA_LLC_VICTIMS.TOTAL_S", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Lines Victimized : Lines in S State", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts when a RFO (the Read for Ownership issued before a write) request hit a cacheline in the S (Shared) state.", + "Counter": "0,1,2,3", + "EventCode": "0x39", + "EventName": "UNC_CHA_MISC.RFO_HIT_S", + "PerPkg": "1", + "PublicDescription": "Cbo Misc : RFO HitS", + "UMask": "0x8", + "Unit": "CHA" + }, + { + "BriefDescription": "OSB Snoop Broadcast : Local InvItoE : Count of OSB snoop broadcasts. Counts by 1 per request causing OSB snoops to be broadcast. Does not count all the snoops generated by OSB.", + "Counter": "0,1,2,3", + "EventCode": "0x55", + "EventName": "UNC_CHA_OSB.LOCAL_INVITOE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "OSB Snoop Broadcast : Local Rd : Count of OSB snoop broadcasts. Counts by 1 per request causing OSB snoops to be broadcast. Does not count all the snoops generated by OSB.", + "Counter": "0,1,2,3", + "EventCode": "0x55", + "EventName": "UNC_CHA_OSB.LOCAL_READ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "OSB Snoop Broadcast : Off : Count of OSB snoop broadcasts. Counts by 1 per request causing OSB snoops to be broadcast. Does not count all the snoops generated by OSB.", + "Counter": "0,1,2,3", + "EventCode": "0x55", + "EventName": "UNC_CHA_OSB.OFF_PWRHEURISTIC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "CHA" + }, + { + "BriefDescription": "OSB Snoop Broadcast : Remote Rd : Count of OSB snoop broadcasts. Counts by 1 per request causing OSB snoops to be broadcast. Does not count all the snoops generated by OSB.", + "Counter": "0,1,2,3", + "EventCode": "0x55", + "EventName": "UNC_CHA_OSB.REMOTE_READ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "OSB Snoop Broadcast : RFO HitS Snoop Broadcast : Count of OSB snoop broadcasts. Counts by 1 per request causing OSB snoops to be broadcast. Does not count all the snoops generated by OSB.", + "Counter": "0,1,2,3", + "EventCode": "0x55", + "EventName": "UNC_CHA_OSB.RFO_HITS_SNP_BCAST", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.ALLOC_EXCLUSIVE", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.ALLOC_EXCLUSIVE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.ALLOC_SHARED", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.ALLOC_SHARED", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.DEALLOC_EVCTCLN", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.DEALLOC_EVCTCLN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.DIRBACKED_ONLY", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.DIRBACKED_ONLY", + "Experimental": "1", + "PerPkg": "1", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.HIT_EXCLUSIVE", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.HIT_EXCLUSIVE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.HIT_SHARED", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.HIT_SHARED", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.INCLUSIVE_ONLY", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.INCLUSIVE_ONLY", + "Experimental": "1", + "PerPkg": "1", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.MISS", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.MISS", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.UPDATE_EXCLUSIVE", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.UPDATE_EXCLUSIVE", + "Experimental": "1", + "PerPkg": "1", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.UPDATE_SHARED", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.UPDATE_SHARED", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.VICTIM_EXCLUSIVE", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.VICTIM_EXCLUSIVE", + "Experimental": "1", + "PerPkg": "1", + "Unit": "CHA" + }, + { + "BriefDescription": "UNC_CHA_REMOTE_SF.VICTIM_SHARED", + "Counter": "0,1,2,3", + "EventCode": "0x69", + "EventName": "UNC_CHA_REMOTE_SF.VICTIM_SHARED", + "Experimental": "1", + "PerPkg": "1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the total number of requests coming from a unit on this socket for exclusive ownership of a cache line without receiving data (INVITOE) to the CHA.", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.INVITOE", + "PerPkg": "1", + "PublicDescription": "HA Read and Write Requests : InvalItoE", + "UMask": "0x30", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the total number of requests coming from a unit on this socket for exclusive ownership of a cache line without receiving data (INVITOE) to the CHA.", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.INVITOE_LOCAL", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the total number of requests coming from a remote socket for exclusive ownership of a cache line without receiving data (INVITOE) to the CHA.", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.INVITOE_REMOTE", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts read requests made into this CHA. Reads include all read opcodes (including RFO: the Read for Ownership issued before a write) .", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.READS", + "PerPkg": "1", + "PublicDescription": "HA Read and Write Requests : Reads", + "UMask": "0x3", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts read requests coming from a unit on this socket made into this CHA. Reads include all read opcodes (including RFO: the Read for Ownership issued before a write).", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.READS_LOCAL", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts read requests coming from a remote socket made into the CHA. Reads include all read opcodes (including RFO: the Read for Ownership issued before a write).", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.READS_REMOTE", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts write requests made into the CHA, including streaming, evictions, HitM (Reads from another core to a Modified cacheline), etc.", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.WRITES", + "PerPkg": "1", + "PublicDescription": "HA Read and Write Requests : Writes", + "UMask": "0xc", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts write requests coming from a unit on this socket made into this CHA, including streaming, evictions, HitM (Reads from another core to a Modified cacheline), etc.", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.WRITES_LOCAL", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts the total number of read requests made into the Home Agent. Reads include all read opcodes (including RFO). Writes include all writes (streaming, evictions, HitM, etc).", + "Counter": "0,1,2,3", + "EventCode": "0x50", + "EventName": "UNC_CHA_REQUESTS.WRITES_REMOTE", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "CHA" + }, + { + "BriefDescription": "Ingress (from CMS) Allocations : IRQ : Counts number of allocations per cycle into the specified Ingress queue.", + "Counter": "0,1,2,3", + "EventCode": "0x13", + "EventName": "UNC_CHA_RxC_INSERTS.IRQ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Ingress (from CMS) Occupancy : IRQ : Counts number of entries in the specified Ingress queue in each cycle.", + "Counter": "0", + "EventCode": "0x11", + "EventName": "UNC_CHA_RxC_OCCUPANCY.IRQ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking exclusive lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", + "Counter": "0,1,2,3", + "EventCode": "0x3d", + "EventName": "UNC_CHA_SF_EVICTION.E_STATE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Snoop Filter Capacity Evictions : E state", + "UMask": "0x2", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking modified lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", + "Counter": "0,1,2,3", + "EventCode": "0x3d", + "EventName": "UNC_CHA_SF_EVICTION.M_STATE", + "PerPkg": "1", + "PublicDescription": "Snoop Filter Capacity Evictions : M state", + "UMask": "0x1", + "Unit": "CHA" + }, + { + "BriefDescription": "Counts snoop filter capacity evictions for entries tracking shared lines in the core's cache. Snoop filter capacity evictions occur when the snoop filter is full and evicts an existing entry to track a new entry. Does not count clean evictions such as when a core's cache replaces a tracked cacheline with a new cacheline.", + "Counter": "0,1,2,3", + "EventCode": "0x3d", + "EventName": "UNC_CHA_SF_EVICTION.S_STATE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Snoop Filter Capacity Evictions : S state", + "UMask": "0x4", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR Inserts", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All", + "UMask": "0xc001ffff", + "Unit": "CHA" + }, + { + "BriefDescription": "CLFlush transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_CLFLUSH", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8c7fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "FsRdCur transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_FSRDCUR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8effd20", + "Unit": "CHA" + }, + { + "BriefDescription": "FsRdCurPtl transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_FSRDCURPTL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c9effd20", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_ITOM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc47fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMWr transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_ITOMWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc4ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "MemPushWr transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_MEMPUSHWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc6ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "WCiL transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_WCIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c86ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "WcilF transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_WCILF", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c867fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "WiL transactions from a CXL device which hit in the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_HIT_WIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c87ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "CLFlush transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_CLFLUSH", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8c7fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "FsRdCur transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_FSRDCUR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8effe20", + "Unit": "CHA" + }, + { + "BriefDescription": "FsRdCurPtl transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_FSRDCURPTL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c9effe20", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_ITOM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc47fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMWr transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_ITOMWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc4ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "MemPushWr transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_MEMPUSHWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc6ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "WCiL transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_WCIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c86ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "WcilF transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_WCILF", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c867fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "WiL transactions from a CXL device which miss the L3.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.CXL_MISS_WIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c87ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "All locally initiated requests from IA Cores", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from iA Cores", + "UMask": "0xc001ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "CLFlush events that are initiated from the Core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_CLFLUSH", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CLFlushes issued by iA Cores", + "UMask": "0xc8c7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "CLFlushOpt events that are initiated from the Core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_CLFLUSHOPT", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CLFlushOpts issued by iA Cores", + "UMask": "0xc8d7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRDs issued by iA Cores", + "UMask": "0xc80fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read prefetch from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts; Code read prefetch from local IA that misses in the snoop filter", + "UMask": "0xc88fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opts issued by iA Cores", + "UMask": "0xc827ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt prefetch from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_DRD_OPT_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opt_Prefs issued by iA Cores", + "UMask": "0xc8a7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "All locally initiated requests from IA Cores which hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from iA Cores that Hit the LLC", + "UMask": "0xc001fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Hit the LLC", + "UMask": "0xc80ffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read prefetch from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd_Prefs issued by iA Cores that hit the LLC", + "UMask": "0xc88ffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "All requests issued from IA cores to CXL accelerator memory regions that hit the LLC.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c0018101", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opts issued by iA Cores that hit the LLC", + "UMask": "0xc827fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt prefetch from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_DRD_OPT_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opt_Prefs issued by iA Cores that hit the LLC", + "UMask": "0xc8a7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM requests from local IA cores that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by iA Cores that Hit LLC", + "UMask": "0xcc47fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch code read from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefCode issued by iA Cores that hit the LLC", + "UMask": "0xcccffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch data read from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefData issued by iA Cores that hit the LLC", + "UMask": "0xccd7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch read for ownership from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that hit the LLC", + "UMask": "0xccc7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Hit the LLC", + "UMask": "0xc807fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership prefetch from local IA that hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFO_Prefs issued by iA Cores that Hit the LLC", + "UMask": "0xc887fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM events that are initiated from the Core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by iA Cores", + "UMask": "0xcc47ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNear requests from local IA cores", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears issued by iA Cores", + "UMask": "0xcd47ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch code read from local IA.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefCode issued by iA Cores", + "UMask": "0xcccfff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch data read from local IA.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefData issued by iA Cores", + "UMask": "0xccd7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch read for ownership from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores", + "UMask": "0xccc7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "All locally initiated requests from IA Cores which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC", + "UMask": "0xc001fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Missed the LLC", + "UMask": "0xc80ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "CRDs from local IA cores to locally homed memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc80efe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Code read prefetch from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd_Prefs issued by iA Cores that Missed the LLC", + "UMask": "0xc88ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "CRD Prefetches from local IA cores to locally homed memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd_Prefs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc88efe01", + "Unit": "CHA" + }, + { + "BriefDescription": "CRD Prefetches from local IA cores to remotely homed memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd_Prefs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc88f7e01", + "Unit": "CHA" + }, + { + "BriefDescription": "CRDs from local IA cores to remotely homed memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CRd issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc80f7e01", + "Unit": "CHA" + }, + { + "BriefDescription": "All requests issued from IA cores to CXL accelerator memory regions that miss the LLC.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c0018201", + "Unit": "CHA" + }, + { + "BriefDescription": "DRds and equivalent opcodes issued from an IA core which miss the L3 and target memory in a CXL type 2 memory expander card.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_CXL_ACC", + "PerPkg": "1", + "PublicDescription": "DRds issued from an IA core which miss the L3 and target memory in a CXL type 2 memory expander card.", + "UMask": "0x10c8178201", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opt issued by iA Cores that missed the LLC", + "UMask": "0xc827fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Inserts into the TOR from local IA cores which miss the LLC and snoop filter with the opcode DRd_Opt, and which target local memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opt issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc826fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Data read opt prefetch from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : DRd_Opt_Prefs issued by iA Cores that missed the LLC", + "UMask": "0xc8a7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Inserts into the TOR from local IA cores which miss the LLC and snoop filter with the opcode DRD_PREF_OPT, and target local memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : Data read opt prefetch from local iA that missed the LLC targeting local memory", + "UMask": "0xc8a6fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Inserts into the TOR from local IA cores which miss the LLC and snoop filter with the opcode DRD_PREF_OPT, and target remote memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : Data read opt prefetch from local iA that missed the LLC targeting remote memory", + "UMask": "0xc8a77e01", + "Unit": "CHA" + }, + { + "BriefDescription": "Inserts into the TOR from local IA cores which miss the LLC and snoop filter with the opcode DRd_Opt, and target remote memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : Data read opt from local iA that missed the LLC targeting remote memory", + "UMask": "0xc8277e01", + "Unit": "CHA" + }, + { + "BriefDescription": "L2 data prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8978201", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM requests from local IA cores that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by iA Cores that Missed LLC", + "UMask": "0xcc47fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch code read from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefCode issued by iA Cores that missed the LLC", + "UMask": "0xcccffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch data read from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefData issued by iA Cores that missed the LLC", + "UMask": "0xccd7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "LLC data prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10ccd78201", + "Unit": "CHA" + }, + { + "BriefDescription": "Last level cache prefetch read for ownership from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that missed the LLC", + "UMask": "0xccc7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "L2 RFO prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFRFO_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8878201", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to locally homed DDR addresses that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LOCAL_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting DDR that missed the LLC - HOMed locally", + "UMask": "0xc8668601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to locally homed PMM addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LOCAL_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting PMM that missed the LLC - HOMed locally", + "UMask": "0xc8668a01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from local IA cores to locally homed DDR addresses that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LOCAL_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting DDR that missed the LLC - HOMed locally", + "UMask": "0xc86e8601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from local IA cores to locally homed PMM addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LOCAL_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting PMM that missed the LLC - HOMed locally", + "UMask": "0xc86e8a01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to remotely homed DDR addresses that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_REMOTE_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting DDR that missed the LLC - HOMed remotely", + "UMask": "0xc8670601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to remotely homed PMM addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_REMOTE_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting PMM that missed the LLC - HOMed remotely", + "UMask": "0xc8670a01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from local IA cores to remotely homed DDR addresses that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_REMOTE_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting DDR that missed the LLC - HOMed remotely", + "UMask": "0xc86f0601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from local IA cores to remotely homed PMM addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_REMOTE_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting PMM that missed the LLC - HOMed remotely", + "UMask": "0xc86f0a01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Missed the LLC", + "UMask": "0xc807fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "RFOs issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8078201", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership from local IA that miss the LLC targeting local memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc806fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership prefetch from local IA that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFO_Prefs issued by iA Cores that Missed the LLC", + "UMask": "0xc887fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "LLC RFO prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10ccc78201", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership prefetch from local IA that miss the LLC targeting local memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFO_Prefs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc886fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership prefetch from local IA that miss the LLC targeting remote memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFO_Prefs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc8877e01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership from local IA that miss the LLC targeting remote memory", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc8077e01", + "Unit": "CHA" + }, + { + "BriefDescription": "UCRDF requests from local IA cores that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_UCRDF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : UCRdFs issued by iA Cores that Missed LLC", + "UMask": "0xc877de01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from a local IA core that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCIL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores that Missed the LLC", + "UMask": "0xc86ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA core that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCILF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLF issued by iA Cores that Missed the LLC", + "UMask": "0xc867fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to DDR homed addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting DDR that missed the LLC", + "UMask": "0xc8678601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA cores to PMM homed addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLFs issued by iA Cores targeting PMM that missed the LLC", + "UMask": "0xc8678a01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from local IA cores to DDR homed addresses which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting DDR that missed the LLC", + "UMask": "0xc86f8601", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from a local IA core to PMM homed addresses that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores targeting PMM that missed the LLC", + "UMask": "0xc86f8a01", + "Unit": "CHA" + }, + { + "BriefDescription": "WIL requests from local IA cores that miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WIL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WiLs issued by iA Cores that Missed LLC", + "UMask": "0xc87fde01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by iA Cores", + "UMask": "0xc807ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "Read for ownership prefetch from local IA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFO_Prefs issued by iA Cores", + "UMask": "0xc887ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "SpecItoM events that are initiated from the Core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_SPECITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : SpecItoMs issued by iA Cores", + "UMask": "0xcc57ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WbEFtoEs issued by iA Cores. (Non Modified Write Backs)", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WBEFTOE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc3fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WbEFtoIs issued by iA Cores . (Non Modified Write Backs)", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WBEFTOI", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc37ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WbMtoEs issued by iA Cores . (Modified Write Backs)", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WBMTOE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc2fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WbMtoI requests from local IA cores", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WBMTOI", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WbMtoIs issued by iA Cores", + "UMask": "0xcc27ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WbStoIs issued by iA Cores . (Non Modified Write Backs)", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WBSTOI", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc67ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCIL requests from a local IA core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WCIL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLs issued by iA Cores", + "UMask": "0xc86fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "WCILF requests from local IA core", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IA_WCILF", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WCiLF issued by iA Cores", + "UMask": "0xc867ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR inserts from local IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from IO Devices", + "UMask": "0xc001ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "CLFlush requests from IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_CLFLUSH", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : CLFlushes issued by IO Devices", + "UMask": "0xc8c3ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR inserts from local IO devices which hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_HIT", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from IO Devices that hit the LLC", + "UMask": "0xc001fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMs from local IO devices which hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_HIT_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc43fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNears, indicating a partial write request, from IO Devices that hit the LLC", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_HIT_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears, indicating a partial write request, from IO Devices that hit the LLC", + "UMask": "0xcd43fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "PCIRDCURs issued by IO devices which hit the LLC", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_HIT_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : PCIRdCurs issued by IO Devices that hit the LLC", + "UMask": "0xc8f3fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "RFOs from local IO devices which hit the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_HIT_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by IO Devices that hit the LLC", + "UMask": "0xc803fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR ItoM inserts from local IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices", + "UMask": "0xcc43ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNears, indicating a partial write request, from IO Devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears, indicating a partial write request, from IO Devices", + "UMask": "0xcd43ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNear (partial write) transactions from an IO device that addresses memory on the local socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears, indicating a partial write request, from IO Devices that address memory on the local socket", + "UMask": "0xcd42ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNear (partial write) transactions from an IO device that addresses memory on a remote socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears, indicating a partial write request, from IO Devices that address memory on a remote socket", + "UMask": "0xcd437f04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM (write) transactions from an IO device that addresses memory on the local socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOM_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoM, indicating a write request, from IO Devices that address memory on the local socket", + "UMask": "0xcc42ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoM (write) transactions from an IO device that addresses memory on a remote socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_ITOM_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoM, indicating a write request, from IO Devices that address memory on a remote socket", + "UMask": "0xcc437f04", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR inserts from local IO devices which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All requests from IO Devices that missed the LLC", + "UMask": "0xc001fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR ItoM inserts from local IO devices which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMs issued by IO Devices that missed the LLC", + "UMask": "0xcc43fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "UMask": "0xcd43fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "PCIRDCURs issued by IO devices which miss the LLC", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : PCIRdCurs issued by IO Devices that missed the LLC", + "UMask": "0xc8f3fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "All TOR RFO inserts from local IO devices which miss the cache", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by IO Devices that missed the LLC", + "UMask": "0xc803fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "PCIRDCURs issued by IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : PCIRdCurs issued by IO Devices", + "UMask": "0xc8f3ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "PCIRDCUR (read) transactions from an IO device that addresses memory on the local socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : PCIRdCurs issued by IO Devices that addresses memory on the local socket", + "UMask": "0xc8f2ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "PCIRDCUR (read) transactions from an IO device that addresses memory on a remote socket", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_PCIRDCUR_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : PCIRdCurs issued by IO Devices that addresses memory on a remote socket", + "UMask": "0xc8f37f04", + "Unit": "CHA" + }, + { + "BriefDescription": "RFOs from local IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : RFOs issued by IO Devices", + "UMask": "0xc803ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "WBMtoI requests from IO devices", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.IO_WBMTOI", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : WbMtoIs issued by IO Devices", + "UMask": "0xcc23ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Inserts for SF or LLC Evictions", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.LLC_OR_SF_EVICTIONS", + "PerPkg": "1", + "PublicDescription": "TOR allocation occurred as a result of SF/LLC evictions (came from the ISMQ)", + "UMask": "0xc001ff02", + "Unit": "CHA" + }, + { + "BriefDescription": "All locally initiated requests", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.LOC_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All from Local iA and IO", + "UMask": "0xc000ff05", + "Unit": "CHA" + }, + { + "BriefDescription": "All from Local iA", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.LOC_IA", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All from Local iA", + "UMask": "0xc000ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "All from Local IO", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.LOC_IO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All from Local IO", + "UMask": "0xc000ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "All remote requests (e.g. snoops, writebacks) that came from remote sockets", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.REM_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All Remote Requests", + "UMask": "0xc001ffc8", + "Unit": "CHA" + }, + { + "BriefDescription": "All snoops to this LLC that came from remote sockets", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_CHA_TOR_INSERTS.REM_SNPS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Inserts : All Snoops from Remote", + "UMask": "0xc001ff08", + "Unit": "CHA" + }, + { + "BriefDescription": "Occupancy for all TOR entries", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All", + "UMask": "0xc001ffff", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CLFlush transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_CLFLUSH", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8c7fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for FsRdCur transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_FSRDCUR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8effd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for FsRdCurPtl transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_FSRDCURPTL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c9effd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_ITOM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc47fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMWr transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_ITOMWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc4ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for MemPushWr transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_MEMPUSHWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc6ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCiL transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_WCIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c86ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WcilF transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_WCILF", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c867fd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WiL transactions from a CXL device which hit in the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_HIT_WIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c87ffd20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CLFlush transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_CLFLUSH", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8c7fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for FsRdCur transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_FSRDCUR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c8effe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for FsRdCurPtl transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_FSRDCURPTL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c9effe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_ITOM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc47fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMWr transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_ITOMWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc4ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for MemPushWr transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_MEMPUSHWR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78cc6ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCiL transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_WCIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c86ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WcilF transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_WCILF", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c867fe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WiL transactions from a CXL device which miss the L3.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.CXL_MISS_WIL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78c87ffe20", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All locally initiated requests from IA Cores", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from iA Cores", + "UMask": "0xc001ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CLFlush events that are initiated from the Core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_CLFLUSH", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CLFlushes issued by iA Cores", + "UMask": "0xc8c7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CLFlushOpt events that are initiated from the Core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_CLFLUSHOPT", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CLFlushOpts issued by iA Cores", + "UMask": "0xc8d7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRDs issued by iA Cores", + "UMask": "0xc80fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy; Code read prefetch from local IA that misses in the snoop filter", + "UMask": "0xc88fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opts issued by iA Cores", + "UMask": "0xc827ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_DRD_OPT_PREF", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opt_Prefs issued by iA Cores", + "UMask": "0xc8a7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All locally initiated requests from IA Cores which hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from iA Cores that Hit the LLC", + "UMask": "0xc001fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Hit the LLC", + "UMask": "0xc80ffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read prefetch from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd_Prefs issued by iA Cores that hit the LLC", + "UMask": "0xc88ffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All requests issued from IA cores to CXL accelerator memory regions that hit the LLC.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c0018101", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opts issued by iA Cores that hit the LLC", + "UMask": "0xc827fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt prefetch from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_DRD_OPT_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opt_Prefs issued by iA Cores that hit the LLC", + "UMask": "0xc8a7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM requests from local IA cores that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by iA Cores that Hit LLC", + "UMask": "0xcc47fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch code read from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefCode issued by iA Cores that hit the LLC", + "UMask": "0xcccffd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch data read from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefData issued by iA Cores that hit the LLC", + "UMask": "0xccd7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch read for ownership from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that hit the LLC", + "UMask": "0xccc7fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Hit the LLC", + "UMask": "0xc807fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership prefetch from local IA that hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFO_Prefs issued by iA Cores that Hit the LLC", + "UMask": "0xc887fd01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM events that are initiated from the Core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by iA Cores", + "UMask": "0xcc47ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNear requests from local IA cores", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears issued by iA Cores", + "UMask": "0xcd47ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch code read from local IA.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefCode issued by iA Cores", + "UMask": "0xcccfff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch data read from local IA.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefData issued by iA Cores", + "UMask": "0xccd7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores", + "UMask": "0xccc7ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All locally initiated requests from IA Cores which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from iA Cores that Missed the LLC", + "UMask": "0xc001fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Missed the LLC", + "UMask": "0xc80ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CRDs from local IA cores to locally homed memory", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc80efe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Code read prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd_Prefs issued by iA Cores that Missed the LLC", + "UMask": "0xc88ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CRD Prefetches from local IA cores to locally homed memory", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD_PREF_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd_Prefs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc88efe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CRD Prefetches from local IA cores to remotely homed memory", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD_PREF_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd_Prefs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc88f7e01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CRDs from local IA cores to remotely homed memory", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CRd issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc80f7e01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All requests issued from IA cores to CXL accelerator memory regions that miss the LLC.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c0018201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for DRds and equivalent opcodes issued from an IA core which miss the L3 and target memory in a CXL type 2 memory expander card.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8178201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opt issued by iA Cores that missed the LLC", + "UMask": "0xc827fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Data read opt prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_OPT_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : DRd_Opt_Prefs issued by iA Cores that missed the LLC", + "UMask": "0xc8a7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for L2 data prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_PREF_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8978201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM requests from local IA cores that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by iA Cores that Missed LLC", + "UMask": "0xcc47fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch code read from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LLCPREFCODE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefCode issued by iA Cores that missed the LLC", + "UMask": "0xcccffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch data read from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LLCPREFDATA", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefData issued by iA Cores that missed the LLC", + "UMask": "0xccd7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for LLC data prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LLCPREFDATA_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10ccd78201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Last level cache prefetch read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LLCPREFRFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that missed the LLC", + "UMask": "0xccc7fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for L2 RFO prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LLCPREFRFO_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8878201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to locally homed DDR addresses that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LOCAL_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting DDR that missed the LLC - HOMed locally", + "UMask": "0xc8668601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to locally homed PMM addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LOCAL_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting PMM that missed the LLC - HOMed locally", + "UMask": "0xc8668a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from local IA cores to locally homed DDR addresses that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LOCAL_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting DDR that missed the LLC - HOMed locally", + "UMask": "0xc86e8601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from local IA cores to locally homed PMM addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LOCAL_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting PMM that missed the LLC - HOMed locally", + "UMask": "0xc86e8a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to remotely homed DDR addresses that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_REMOTE_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting DDR that missed the LLC - HOMed remotely", + "UMask": "0xc8670601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to remotely homed PMM addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_REMOTE_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting PMM that missed the LLC - HOMed remotely", + "UMask": "0xc8670a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from local IA cores to remotely homed DDR addresses that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_REMOTE_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting DDR that missed the LLC - HOMed remotely", + "UMask": "0xc86f0601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from local IA cores to remotely homed PMM addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_REMOTE_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting PMM that missed the LLC - HOMed remotely", + "UMask": "0xc86f0a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Missed the LLC", + "UMask": "0xc807fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for RFOs issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10c8078201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc806fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFO_Prefs issued by iA Cores that Missed the LLC", + "UMask": "0xc887fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for LLC RFO prefetches issued from an IA core which miss the L3 and target memory in a CXL type 2 accelerator.", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_PREF_CXL_ACC", + "PerPkg": "1", + "UMask": "0x10ccc78201", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_PREF_LOCAL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFO_Prefs issued by iA Cores that Missed the LLC - HOMed locally", + "UMask": "0xc886fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_PREF_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFO_Prefs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc8877e01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO_REMOTE", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Missed the LLC - HOMed remotely", + "UMask": "0xc8077e01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for UCRDF requests from local IA cores that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_UCRDF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : UCRdFs issued by iA Cores that Missed LLC", + "UMask": "0xc877de01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from a local IA core that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCIL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores that Missed the LLC", + "UMask": "0xc86ffe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA core that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCILF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLF issued by iA Cores that Missed the LLC", + "UMask": "0xc867fe01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to DDR homed addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCILF_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting DDR that missed the LLC", + "UMask": "0xc8678601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA cores to PMM homed addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCILF_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLFs issued by iA Cores targeting PMM that missed the LLC", + "UMask": "0xc8678a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from local IA cores to DDR homed addresses which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCIL_DDR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting DDR that missed the LLC", + "UMask": "0xc86f8601", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from a local IA core to PMM homed addresses that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WCIL_PMM", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores targeting PMM that missed the LLC", + "UMask": "0xc86f8a01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WIL requests from local IA cores that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_WIL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WiLs issued by iA Cores that Missed LLC", + "UMask": "0xc87fde01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores", + "UMask": "0xc807ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for Read for ownership prefetch from local IA that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_RFO_PREF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFO_Prefs issued by iA Cores", + "UMask": "0xc887ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for SpecItoM events that are initiated from the Core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_SPECITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : SpecItoMs issued by iA Cores", + "UMask": "0xcc57ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WbMtoI requests from local IA cores", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_WBMTOI", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WbMtoIs issued by iA Cores", + "UMask": "0xcc27ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCIL requests from a local IA core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_WCIL", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLs issued by iA Cores", + "UMask": "0xc86fff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WCILF requests from local IA core", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_WCILF", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WCiLF issued by iA Cores", + "UMask": "0xc867ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR inserts from local IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from IO Devices", + "UMask": "0xc001ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for CLFlush requests from IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_CLFLUSH", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : CLFlushes issued by IO Devices", + "UMask": "0xc8c3ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR inserts from local IO devices which hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_HIT", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from IO Devices that hit the LLC", + "UMask": "0xc001fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMs from local IO devices which hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_HIT_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by IO Devices that Hit the LLC", + "UMask": "0xcc43fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNears, indicating a partial write request, from IO Devices that hit the LLC", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_HIT_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears, indicating a partial write request, from IO Devices that hit the LLC", + "UMask": "0xcd43fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for PCIRDCURs issued by IO devices which hit the LLC", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_HIT_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : PCIRdCurs issued by IO Devices that hit the LLC", + "UMask": "0xc8f3fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for RFOs from local IO devices which hit the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_HIT_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by IO Devices that hit the LLC", + "UMask": "0xc803fd04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR ItoM inserts from local IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by IO Devices", + "UMask": "0xcc43ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNears, indicating a partial write request, from IO Devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears, indicating a partial write request, from IO Devices", + "UMask": "0xcd43ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR inserts from local IO devices which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All requests from IO Devices that missed the LLC", + "UMask": "0xc001fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR ItoM inserts from local IO devices which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOM", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by IO Devices that missed the LLC", + "UMask": "0xcc43fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOMCACHENEAR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "UMask": "0xcd43fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNear transactions from an IO device on the local socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOMCACHENEAR_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "UMask": "0xcd42fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoMCacheNear transactions from an IO device on a remote socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOMCACHENEAR_REMOTE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMCacheNears, indicating a partial write request, from IO Devices that missed the LLC", + "UMask": "0xcd437e04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM transactions from an IO device on the local socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOM_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by IO Devices that missed the LLC", + "UMask": "0xcc42fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for ItoM transactions from an IO device on a remote socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOM_REMOTE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : ItoMs issued by IO Devices that missed the LLC", + "UMask": "0xcc437e04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for PCIRDCURs issued by IO devices which miss the LLC", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : PCIRdCurs issued by IO Devices that missed the LLC", + "UMask": "0xc8f3fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for PCIRDCUR transactions from an IO device on the local socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_PCIRDCUR_LOCAL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : PCIRdCurs issued by IO Devices that missed the LLC", + "UMask": "0xc8f2fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for PCIRDCUR transactions from an IO device on a remote socket that miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_PCIRDCUR_REMOTE", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : PCIRdCurs issued by IO Devices that missed the LLC", + "UMask": "0xc8f37e04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All TOR RFO inserts from local IO devices which miss the cache", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by IO Devices that missed the LLC", + "UMask": "0xc803fe04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for PCIRDCURs issued by IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_PCIRDCUR", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : PCIRdCurs issued by IO Devices", + "UMask": "0xc8f3ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for RFOs from local IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_RFO", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : RFOs issued by IO Devices", + "UMask": "0xc803ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for WBMtoI requests from IO devices", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_WBMTOI", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : WbMtoIs issued by IO Devices", + "UMask": "0xcc23ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All locally initiated requests", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.LOC_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All from Local iA and IO", + "UMask": "0xc000ff05", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All from Local iA", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.LOC_IA", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All from Local iA", + "UMask": "0xc000ff01", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All from Local IO", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.LOC_IO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All from Local IO", + "UMask": "0xc000ff04", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All remote requests (e.g. snoops, writebacks) that came from remote sockets", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.REM_ALL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All Remote Requests", + "UMask": "0xc001ffc8", + "Unit": "CHA" + }, + { + "BriefDescription": "TOR Occupancy for All snoops to this LLC that came from remote sockets", + "Counter": "0", + "EventCode": "0x36", + "EventName": "UNC_CHA_TOR_OCCUPANCY.REM_SNPS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "TOR Occupancy : All Snoops from Remote", + "UMask": "0xc001ff08", + "Unit": "CHA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cxl.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cxl.json new file mode 100644 index 000000000000..383a5ba5a697 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-cxl.json @@ -0,0 +1,31 @@ +[ + { + "BriefDescription": "B2CXL Clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_B2CXL_CLOCKTICKS", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "B2CXL" + }, + { + "BriefDescription": "Number of Allocation to Mem Data Packing buffer", + "Counter": "4,5,6,7", + "EventCode": "0x41", + "EventName": "UNC_CXLCM_RxC_PACK_BUF_INSERTS.MEM_DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "CXLCM" + }, + { + "BriefDescription": "Number of Allocation to M2S Data AGF", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_CXLDP_TxC_AGF_INSERTS.M2S_DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "CXLDP" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-interconnect.json new file mode 100644 index 000000000000..251e5d20fefe --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-interconnect.json @@ -0,0 +1,1625 @@ +[ + { + "BriefDescription": "Clockticks of the mesh to memory (B2CMI)", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_B2CMI_CLOCKTICKS", + "PerPkg": "1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of time D2C was not honoured by egress due to directory state constraints", + "Counter": "0,1,2,3", + "EventCode": "0x17", + "EventName": "UNC_B2CMI_DIRECT2CORE_NOT_TAKEN_DIRSTATE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of times B2CMI egress did D2C (direct to core)", + "Counter": "0,1,2,3", + "EventCode": "0x16", + "EventName": "UNC_B2CMI_DIRECT2CORE_TAKEN", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of times D2C wasn't honoured even though the incoming request had d2c set for non cisgress txn", + "Counter": "0,1,2,3", + "EventCode": "0x18", + "EventName": "UNC_B2CMI_DIRECT2CORE_TXN_OVERRIDE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of d2k wasn't done due to credit constraints", + "Counter": "0,1,2,3", + "EventCode": "0x1B", + "EventName": "UNC_B2CMI_DIRECT2UPI_NOT_TAKEN_CREDITS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Direct to UPI Transactions - Ignored due to lack of credits : All : Counts the number of d2k wasn't done due to credit constraints", + "Counter": "0,1,2,3", + "EventCode": "0x1B", + "EventName": "UNC_B2CMI_DIRECT2UPI_NOT_TAKEN_CREDITS.EGRESS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of time D2K was not honoured by egress due to directory state constraints", + "Counter": "0,1,2,3", + "EventCode": "0x1A", + "EventName": "UNC_B2CMI_DIRECT2UPI_NOT_TAKEN_DIRSTATE", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Cycles when Direct2UPI was Disabled : Egress Ignored D2U : Counts the number of time D2K was not honoured by egress due to directory state constraints", + "Counter": "0,1,2,3", + "EventCode": "0x1A", + "EventName": "UNC_B2CMI_DIRECT2UPI_NOT_TAKEN_DIRSTATE.EGRESS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of times egress did D2K (Direct to KTI)", + "Counter": "0,1,2,3", + "EventCode": "0x19", + "EventName": "UNC_B2CMI_DIRECT2UPI_TAKEN", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of times D2K wasn't honoured even though the incoming request had d2k set for non cisgress txn", + "Counter": "0,1,2,3", + "EventCode": "0x1C", + "EventName": "UNC_B2CMI_DIRECT2UPI_TXN_OVERRIDE", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit Clean", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x38", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On NonDirty Line in A State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.CLEAN_A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On NonDirty Line in I State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.CLEAN_I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On NonDirty Line in S State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.CLEAN_S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit Dirty (modified)", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x7", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On Dirty Line in A State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.DIRTY_A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On Dirty Line in I State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.DIRTY_I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Hit : On Dirty Line in S State", + "Counter": "0,1,2,3", + "EventCode": "0x1D", + "EventName": "UNC_B2CMI_DIRECTORY_HIT.DIRTY_S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of 1lm or 2lm hit read data returns to egress with any directory to non persistent memory", + "Counter": "0,1,2,3", + "EventCode": "0x20", + "EventName": "UNC_B2CMI_DIRECTORY_LOOKUP.ANY", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of 1lm or 2lm hit read data returns to egress with directory A to non persistent memory", + "Counter": "0,1,2,3", + "EventCode": "0x20", + "EventName": "UNC_B2CMI_DIRECTORY_LOOKUP.STATE_A", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of 1lm or 2lm hit read data returns to egress with directory I to non persistent memory", + "Counter": "0,1,2,3", + "EventCode": "0x20", + "EventName": "UNC_B2CMI_DIRECTORY_LOOKUP.STATE_I", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the number of 1lm or 2lm hit read data returns to egress with directory S to non persistent memory", + "Counter": "0,1,2,3", + "EventCode": "0x20", + "EventName": "UNC_B2CMI_DIRECTORY_LOOKUP.STATE_S", + "PerPkg": "1", + "PublicDescription": "Counts the number of 1lm or 2lm hit read data returns to egress with directory S to non persistent memory", + "UMask": "0x4", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss Clean", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x38", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On NonDirty Line in A State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.CLEAN_A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On NonDirty Line in I State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.CLEAN_I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On NonDirty Line in S State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.CLEAN_S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss Dirty (modified)", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x7", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On Dirty Line in A State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.DIRTY_A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On Dirty Line in I State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.DIRTY_I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory Miss : On Dirty Line in S State", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_B2CMI_DIRECTORY_MISS.DIRTY_S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any A2I Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.A2I", + "PerPkg": "1", + "UMask": "0x320", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any A2S Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.A2S", + "PerPkg": "1", + "UMask": "0x340", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts cisgress directory updates", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.ANY", + "PerPkg": "1", + "UMask": "0x301", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts any 1lm or 2lm hit data return that would result in directory update to non persistent memory (DRAM)", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.HIT_ANY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x101", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in near memory to the A state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.HIT_X2A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x114", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in near memory to the I state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.HIT_X2I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x128", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in near memory to the S state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.HIT_X2S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x142", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any I2A Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.I2A", + "PerPkg": "1", + "UMask": "0x304", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any I2S Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.I2S", + "PerPkg": "1", + "UMask": "0x302", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in far memory to the A state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.MISS_X2A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x214", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in far memory to the I state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.MISS_X2I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x228", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update in far memory to the S state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.MISS_X2S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x242", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any S2A Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.S2A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x310", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Any S2I Transition", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.S2I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x308", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update to the A state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.X2A", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x314", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update to the I state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.X2I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x328", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Directory update to the S state", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_B2CMI_DIRECTORY_UPDATE.X2S", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x342", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts any read", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "UNC_B2CMI_IMC_READS.ALL", + "PerPkg": "1", + "UMask": "0x104", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts normal reads issue to CMI", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "UNC_B2CMI_IMC_READS.NORMAL", + "PerPkg": "1", + "UMask": "0x101", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Count reads to NM region", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "UNC_B2CMI_IMC_READS.TO_DDR_AS_CACHE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x110", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts reads to 1lm non persistent memory regions", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "UNC_B2CMI_IMC_READS.TO_DDR_AS_MEM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x108", + "Unit": "B2CMI" + }, + { + "BriefDescription": "All Writes - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.ALL", + "PerPkg": "1", + "UMask": "0x110", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Full Non-ISOCH - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.FULL", + "PerPkg": "1", + "UMask": "0x101", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Non-Inclusive - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.NI", + "Experimental": "1", + "PerPkg": "1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Non-Inclusive Miss - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.NI_MISS", + "Experimental": "1", + "PerPkg": "1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Partial Non-ISOCH - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.PARTIAL", + "PerPkg": "1", + "UMask": "0x102", + "Unit": "B2CMI" + }, + { + "BriefDescription": "DDR, acting as Cache - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.TO_DDR_AS_CACHE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x140", + "Unit": "B2CMI" + }, + { + "BriefDescription": "DDR - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x25", + "EventName": "UNC_B2CMI_IMC_WRITES.TO_DDR_AS_MEM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x120", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Prefetch CAM Inserts : UPI - Ch 0", + "Counter": "0,1,2,3", + "EventCode": "0x56", + "EventName": "UNC_B2CMI_PREFCAM_INSERTS.CH0_UPI", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Prefetch CAM Inserts : XPT - Ch 0", + "Counter": "0,1,2,3", + "EventCode": "0x56", + "EventName": "UNC_B2CMI_PREFCAM_INSERTS.CH0_XPT", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Prefetch CAM Inserts : UPI - All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x56", + "EventName": "UNC_B2CMI_PREFCAM_INSERTS.UPI_ALLCH", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Prefetch CAM Inserts : XPT -All Channels", + "Counter": "0,1,2,3", + "EventCode": "0x56", + "EventName": "UNC_B2CMI_PREFCAM_INSERTS.XPT_ALLCH", + "PerPkg": "1", + "PublicDescription": "Prefetch CAM Inserts : XPT - All Channels", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Prefetch CAM Occupancy : Channel 0", + "Counter": "0,1,2,3", + "EventCode": "0x54", + "EventName": "UNC_B2CMI_PREFCAM_OCCUPANCY.CH0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm reads and WRNI which were a hit", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_B2CMI_TAG_HIT.ALL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm reads which were a hit clean", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_B2CMI_TAG_HIT.RD_CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm reads which were a hit dirty", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_B2CMI_TAG_HIT.RD_DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm WRNI which were a hit clean", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_B2CMI_TAG_HIT.WR_CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm WRNI which were a hit dirty", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_B2CMI_TAG_HIT.WR_DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm second way read miss for a WrNI", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x5", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm second way read miss for a WrNI", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xa", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm second way read miss for a Rd", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.RD_2WAY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm reads which were a miss and the cache line is unmodified", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.RD_CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm reads which were a miss and the cache line is modified", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.RD_DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm second way read miss for a WrNI", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.WR_2WAY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm WRNI which were a miss and the cache line is unmodified", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.WR_CLEAN", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Counts the 2lm WRNI which were a miss and the cache line is modified", + "Counter": "0,1,2,3", + "EventCode": "0x4B", + "EventName": "UNC_B2CMI_TAG_MISS.WR_DIRTY", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Tracker Inserts : Channel 0", + "Counter": "0,1,2,3", + "EventCode": "0x32", + "EventName": "UNC_B2CMI_TRACKER_INSERTS.CH0", + "PerPkg": "1", + "UMask": "0x104", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Tracker Occupancy : Channel 0", + "Counter": "0,1,2,3", + "EventCode": "0x33", + "EventName": "UNC_B2CMI_TRACKER_OCCUPANCY.CH0", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "Write Tracker Inserts : Channel 0", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_B2CMI_WR_TRACKER_INSERTS.CH0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2CMI" + }, + { + "BriefDescription": "UNC_B2HOT_CLOCKTICKS", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_B2HOT_CLOCKTICKS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "B2HOT" + }, + { + "BriefDescription": "Number of uclks in domain", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_B2UPI_CLOCKTICKS", + "PerPkg": "1", + "Unit": "B2UPI" + }, + { + "BriefDescription": "Total Write Cache Occupancy : Mem", + "Counter": "0,1,2,3", + "EventCode": "0x0F", + "EventName": "UNC_I_CACHE_TOTAL_OCCUPANCY.MEM", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "IRP" + }, + { + "BriefDescription": "IRP Clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_I_CLOCKTICKS", + "PerPkg": "1", + "Unit": "IRP" + }, + { + "BriefDescription": "Inbound read requests received by the IRP and inserted into the FAF queue", + "Counter": "0,1,2,3", + "EventCode": "0x18", + "EventName": "UNC_I_FAF_INSERTS", + "PerPkg": "1", + "Unit": "IRP" + }, + { + "BriefDescription": "FAF occupancy", + "Counter": "0,1,2,3", + "EventCode": "0x19", + "EventName": "UNC_I_FAF_OCCUPANCY", + "Experimental": "1", + "PerPkg": "1", + "Unit": "IRP" + }, + { + "BriefDescription": "Counts Timeouts - Set 0 : Fastpath Rejects", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_I_MISC0.FAST_REJ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "IRP" + }, + { + "BriefDescription": "Counts Timeouts - Set 0 : Fastpath Requests", + "Counter": "0,1,2,3", + "EventCode": "0x1E", + "EventName": "UNC_I_MISC0.FAST_REQ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "IRP" + }, + { + "BriefDescription": "Misc Events - Set 1 : Lost Forward : Snoop pulled away ownership before a write was committed", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_I_MISC1.LOST_FWD", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "IRP" + }, + { + "BriefDescription": "Misc Events - Set 1 : Received Invalid : Secondary received a transfer that did not have sufficient MESI state", + "Counter": "0,1,2,3", + "EventCode": "0x1F", + "EventName": "UNC_I_MISC1.SEC_RCVD_INVLD", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "IRP" + }, + { + "BriefDescription": "Snoop Hit E/S responses", + "Counter": "0,1,2,3", + "EventCode": "0x12", + "EventName": "UNC_I_SNOOP_RESP.ALL_HIT_ES", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x74", + "Unit": "IRP" + }, + { + "BriefDescription": "Snoop Hit I responses", + "Counter": "0,1,2,3", + "EventCode": "0x12", + "EventName": "UNC_I_SNOOP_RESP.ALL_HIT_I", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x72", + "Unit": "IRP" + }, + { + "BriefDescription": "Snoop Hit M responses", + "Counter": "0,1,2,3", + "EventCode": "0x12", + "EventName": "UNC_I_SNOOP_RESP.ALL_HIT_M", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x78", + "Unit": "IRP" + }, + { + "BriefDescription": "Snoop miss responses", + "Counter": "0,1,2,3", + "EventCode": "0x12", + "EventName": "UNC_I_SNOOP_RESP.ALL_MISS", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x71", + "Unit": "IRP" + }, + { + "BriefDescription": "Inbound write (fast path) requests to coherent memory, received by the IRP resulting in write ownership requests issued by IRP to the mesh.", + "Counter": "0,1,2,3", + "EventCode": "0x11", + "EventName": "UNC_I_TRANSACTIONS.WR_PREF", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "IRP" + }, + { + "BriefDescription": "MDF Clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_MDF_CLOCKTICKS", + "PerPkg": "1", + "Unit": "MDF" + }, + { + "BriefDescription": "Number of UPI LL clock cycles while the event is enabled", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_UPI_CLOCKTICKS", + "PerPkg": "1", + "PublicDescription": "Number of kfclks", + "Unit": "UPI" + }, + { + "BriefDescription": "Cycles in L1 : Number of UPI qfclk cycles spent in L1 power mode. L1 is a mode that totally shuts down a UPI link. Use edge detect to count the number of instances when the UPI link entered L1. Link power states are per link and per direction, so for example the Tx direction could be in one state while Rx was in another. Because L1 totally shuts down the link, it takes a good amount of time to exit this mode.", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_UPI_L1_POWER_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Non-Coherent Bypass", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.NCB", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xe", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Non-Coherent Bypass, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.NCB_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10e", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Non-Coherent Standard", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.NCS", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Non-Coherent Standard, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.NCS_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10f", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Request", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.REQ", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Request, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.REQ_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x108", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - Conflict", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSPCNFLT", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1aa", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - Invalid", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSPI", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x12a", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - Data", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSP_DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xc", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - Data, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSP_DATA_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10c", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - No Data", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSP_NODATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xa", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Response - No Data, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.RSP_NODATA_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10a", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Snoop", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.SNP", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x9", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Snoop, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.SNP_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x109", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Writeback", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.WB", + "PerPkg": "1", + "UMask": "0xd", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Receive path of a UPI Port : Writeback, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_UPI_RxL_BASIC_HDR_MATCH.WB_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10d", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : All Data : Shows legal flit time (hides impact of L0p and L0c).", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.ALL_DATA", + "PerPkg": "1", + "UMask": "0xf", + "Unit": "UPI" + }, + { + "BriefDescription": "Null FLITs received from any slot", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.ALL_NULL", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Valid Flits Received : Null FLITs received from any slot", + "UMask": "0x27", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Data : Shows legal flit time (hides impact of L0p and L0c). : Count Data Flits (which consume all slots), but how much to count is based on Slot0-2 mask, so count can be 0-3 depending on which slots are enabled for counting..", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Idle : Shows legal flit time (hides impact of L0p and L0c).", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.IDLE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x47", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : LLCRD Not Empty : Shows legal flit time (hides impact of L0p and L0c). : Enables counting of LLCRD (with non-zero payload). This only applies to slot 2 since LLCRD is only allowed in slot 2", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.LLCRD", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : LLCTRL : Shows legal flit time (hides impact of L0p and L0c). : Equivalent to an idle packet. Enables counting of slot 0 LLCTRL messages.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.LLCTRL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : All Non Data : Shows legal flit time (hides impact of L0p and L0c).", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.NON_DATA", + "PerPkg": "1", + "UMask": "0x97", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Slot NULL or LLCRD Empty : Shows legal flit time (hides impact of L0p and L0c). : LLCRD with all zeros is treated as NULL. Slot 1 is not treated as NULL if slot 0 is a dual slot. This can apply to slot 0,1, or 2.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.NULL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Protocol Header : Shows legal flit time (hides impact of L0p and L0c). : Enables count of protocol headers in slot 0,1,2 (depending on slot uMask bits)", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.PROTHDR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Slot 0 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 0 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Slot 1 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 1 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Received : Slot 2 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 2 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_UPI_RxL_FLITS.SLOT2", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Flit Buffer Allocations : Slot 0 : Number of allocations into the UPI Rx Flit Buffer. Generally, when data is transmitted across UPI, it will bypass the RxQ and pass directly to the ring interface. If things back up getting transmitted onto the ring, however, it may need to allocate into this buffer, thus increasing the latency. This event can be used in conjunction with the Flit Buffer Occupancy event in order to calculate the average flit buffer lifetime.", + "Counter": "0,1,2,3", + "EventCode": "0x30", + "EventName": "UNC_UPI_RxL_INSERTS.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Flit Buffer Allocations : Slot 1 : Number of allocations into the UPI Rx Flit Buffer. Generally, when data is transmitted across UPI, it will bypass the RxQ and pass directly to the ring interface. If things back up getting transmitted onto the ring, however, it may need to allocate into this buffer, thus increasing the latency. This event can be used in conjunction with the Flit Buffer Occupancy event in order to calculate the average flit buffer lifetime.", + "Counter": "0,1,2,3", + "EventCode": "0x30", + "EventName": "UNC_UPI_RxL_INSERTS.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Flit Buffer Allocations : Slot 2 : Number of allocations into the UPI Rx Flit Buffer. Generally, when data is transmitted across UPI, it will bypass the RxQ and pass directly to the ring interface. If things back up getting transmitted onto the ring, however, it may need to allocate into this buffer, thus increasing the latency. This event can be used in conjunction with the Flit Buffer Occupancy event in order to calculate the average flit buffer lifetime.", + "Counter": "0,1,2,3", + "EventCode": "0x30", + "EventName": "UNC_UPI_RxL_INSERTS.SLOT2", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Occupancy - All Packets : Slot 0", + "Counter": "0,1,2,3", + "EventCode": "0x32", + "EventName": "UNC_UPI_RxL_OCCUPANCY.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Occupancy - All Packets : Slot 1", + "Counter": "0,1,2,3", + "EventCode": "0x32", + "EventName": "UNC_UPI_RxL_OCCUPANCY.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "UPI" + }, + { + "BriefDescription": "RxQ Occupancy - All Packets : Slot 2", + "Counter": "0,1,2,3", + "EventCode": "0x32", + "EventName": "UNC_UPI_RxL_OCCUPANCY.SLOT2", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "UPI" + }, + { + "BriefDescription": "Cycles in L0p", + "Counter": "0,1,2,3", + "EventCode": "0x27", + "EventName": "UNC_UPI_TxL0P_POWER_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "Unit": "UPI" + }, + { + "BriefDescription": "UNC_UPI_TxL0P_POWER_CYCLES_LL_ENTER", + "Counter": "0,1,2,3", + "EventCode": "0x28", + "EventName": "UNC_UPI_TxL0P_POWER_CYCLES_LL_ENTER", + "Experimental": "1", + "PerPkg": "1", + "Unit": "UPI" + }, + { + "BriefDescription": "UNC_UPI_TxL0P_POWER_CYCLES_M3_EXIT", + "Counter": "0,1,2,3", + "EventCode": "0x29", + "EventName": "UNC_UPI_TxL0P_POWER_CYCLES_M3_EXIT", + "Experimental": "1", + "PerPkg": "1", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Non-Coherent Bypass", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.NCB", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xe", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Non-Coherent Bypass, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.NCB_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10e", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Non-Coherent Standard", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.NCS", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Non-Coherent Standard, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.NCS_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10f", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Request", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.REQ", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Request, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.REQ_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x108", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - Conflict", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSPCNFLT", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1aa", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - Invalid", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSPI", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x12a", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - Data", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSP_DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xc", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - Data, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSP_DATA_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10c", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - No Data", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSP_NODATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xa", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Response - No Data, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.RSP_NODATA_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10a", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Snoop", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.SNP", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x9", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Snoop, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.SNP_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x109", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Writeback", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.WB", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xd", + "Unit": "UPI" + }, + { + "BriefDescription": "Matches on Transmit path of a UPI Port : Writeback, Match Opcode", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_UPI_TxL_BASIC_HDR_MATCH.WB_OPC", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10d", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : All Data : Counts number of data flits across this UPI link.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.ALL_DATA", + "PerPkg": "1", + "UMask": "0xf", + "Unit": "UPI" + }, + { + "BriefDescription": "All Null Flits", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.ALL_NULL", + "PerPkg": "1", + "PublicDescription": "Valid Flits Sent : Idle", + "UMask": "0x27", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Data : Shows legal flit time (hides impact of L0p and L0c). : Count Data Flits (which consume all slots), but how much to count is based on Slot0-2 mask, so count can be 0-3 depending on which slots are enabled for counting..", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.DATA", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Idle : Shows legal flit time (hides impact of L0p and L0c).", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.IDLE", + "PerPkg": "1", + "UMask": "0x47", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : LLCRD Not Empty : Shows legal flit time (hides impact of L0p and L0c). : Enables counting of LLCRD (with non-zero payload). This only applies to slot 2 since LLCRD is only allowed in slot 2", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.LLCRD", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : LLCTRL : Shows legal flit time (hides impact of L0p and L0c). : Equivalent to an idle packet. Enables counting of slot 0 LLCTRL messages.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.LLCTRL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : All Non Data : Shows legal flit time (hides impact of L0p and L0c).", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.NON_DATA", + "PerPkg": "1", + "PublicDescription": "Valid Flits Sent : Null FLITs transmitted to any slot", + "UMask": "0x97", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Slot NULL or LLCRD Empty : Shows legal flit time (hides impact of L0p and L0c). : LLCRD with all zeros is treated as NULL. Slot 1 is not treated as NULL if slot 0 is a dual slot. This can apply to slot 0,1, or 2.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.NULL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Protocol Header : Shows legal flit time (hides impact of L0p and L0c). : Enables count of protocol headers in slot 0,1,2 (depending on slot uMask bits)", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.PROTHDR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Slot 0 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 0 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Slot 1 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 1 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "UPI" + }, + { + "BriefDescription": "Valid Flits Sent : Slot 2 : Shows legal flit time (hides impact of L0p and L0c). : Count Slot 2 - Other mask bits determine types of headers to count.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_UPI_TxL_FLITS.SLOT2", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "UPI" + }, + { + "BriefDescription": "Message Received : Doorbell", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.DOORBELL_RCVD", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : Interrupt", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.INT_PRIO", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Message Received : Interrupt : Interrupts", + "UMask": "0x10", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : IPI", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.IPI_RCVD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Message Received : IPI : Inter Processor Interrupts", + "UMask": "0x4", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : MSI", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.MSI_RCVD", + "PerPkg": "1", + "PublicDescription": "Message Received : MSI : Message Signaled Interrupts - interrupts sent by devices (including PCIe via IOxAPIC) (Socket Mode only)", + "UMask": "0x2", + "Unit": "UBOX" + }, + { + "BriefDescription": "Message Received : VLW", + "Counter": "0,1", + "EventCode": "0x42", + "EventName": "UNC_U_EVENT_MSG.VLW_RCVD", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Message Received : VLW : Virtual Logical Wire (legacy) message were received from Uncore.", + "UMask": "0x1", + "Unit": "UBOX" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-io.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-io.json new file mode 100644 index 000000000000..77f9c4cee952 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-io.json @@ -0,0 +1,1925 @@ +[ + { + "BriefDescription": "IIO Clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_IIO_CLOCKTICKS", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.ALL_PARTS", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PCIE Completion Buffer Inserts. Counts once per 64 byte read issued from this PCIE device.", + "Counter": "0,1,2,3", + "EventCode": "0xC2", + "EventName": "UNC_IIO_COMP_BUF_INSERTS.CMPD.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.ALL_PARTS", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0xff", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x40", + "Unit": "IIO" + }, + { + "BriefDescription": "Count of allocations in the completion buffer", + "Counter": "2,3", + "EventCode": "0xD5", + "EventName": "UNC_IIO_COMP_BUF_OCCUPANCY.CMPD.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x80", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.ALL_PARTS", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core reporting completion of Card read from Core DRAM", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_READ.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.ALL_PARTS", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.MEM_WRITE.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Another card (different IIO stack) reading from this card.", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.PEER_READ.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested by the CPU : Another card (different IIO stack) writing to this card.", + "Counter": "2,3", + "EventCode": "0xC0", + "EventName": "UNC_IIO_DATA_REQ_BY_CPU.PEER_WRITE.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Counts once for every 4 bytes read from this card to memory. This event does include reads to IO.", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x02", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x04", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x08", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x10", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x20", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x40", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x80", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Counts once for every 4 bytes written from this card to memory. This event does include writes to IO.", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x02", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x04", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x08", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x10", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x20", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x40", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Four byte data request of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x80", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_READ.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Counts once for every 4 bytes written from this card to a peer device's IO space.", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.ALL_PARTS", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Data requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x83", + "EventName": "UNC_IIO_DATA_REQ_OF_CPU.PEER_WRITE.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB Hits to a 1G Page", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.1G_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB Hits to a 2M Page", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.2M_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB Hits to a 4K Page", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.4K_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB lookups all", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.ALL_LOOKUPS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Context cache hits", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.CTXT_CACHE_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x80", + "Unit": "IIO" + }, + { + "BriefDescription": "Context cache lookups", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.CTXT_CACHE_LOOKUPS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x40", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB lookups first", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.FIRST_LOOKUPS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB Fills (same as IOTLB miss)", + "Counter": "0,1,2,3", + "EventCode": "0x40", + "EventName": "UNC_IIO_IOMMU0.MISSES", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "IOMMU memory access (both low and high priority)", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.NUM_MEM_ACCESSES", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0xc0", + "Unit": "IIO" + }, + { + "BriefDescription": "IOMMU high priority memory access", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.NUM_MEM_ACCESSES_HIGH", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x80", + "Unit": "IIO" + }, + { + "BriefDescription": "IOMMU low priority memory access", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.NUM_MEM_ACCESSES_LOW", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x40", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache Hit to a 1G page", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_1G_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache Hit to a 256T page", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_256T_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache Hit to a 2M page", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_2M_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache Hit to a 512G page", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_512G_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache fill", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_CACHE_FILLS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "Second Level Page Walk Cache lookup", + "Counter": "0,1,2,3", + "EventCode": "0x41", + "EventName": "UNC_IIO_IOMMU1.SLPWC_CACHE_LOOKUPS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Cycles PWT full", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.CYC_PWT_FULL", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Interrupt Entry cache hit", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.INT_CACHE_HITS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x80", + "Unit": "IIO" + }, + { + "BriefDescription": "Interrupt Entry cache lookup", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.INT_CACHE_LOOKUPS", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x40", + "Unit": "IIO" + }, + { + "BriefDescription": "Context Cache invalidation events", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.NUM_INVAL_CTXT_CACHE", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Interrupt Entry Cache invalidation events", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.NUM_INVAL_INT_CACHE", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "IOTLB invalidation events", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.NUM_INVAL_IOTLB", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "PASID Cache invalidation events", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_IIO_IOMMU3.NUM_INVAL_PASID_CACHE", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO]", + "Counter": "2,3", + "Deprecated": "1", + "EventCode": "0xc5", + "EventName": "UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Occupancy of outbound request queue : To device : Counts number of outbound requests/completions IIO is currently processing [This event is alias to UNC_IIO_NUM_OUSTANDING_REQ_FROM_CPU.TO_IO]", + "Counter": "2,3", + "EventCode": "0xc5", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_FROM_CPU.TO_IO", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Passing data to be written", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.DATA", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "Issuing final read or write of line", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.FINAL_RD_WR", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Processing response from IOMMU", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.IOMMU_HIT", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Issuing to IOMMU", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.IOMMU_REQ", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Request Ownership", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.REQ_OWN", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Writing line", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_IIO_NUM_OUTSTANDING_REQ_OF_CPU.WR", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.ABORT", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.ABORT", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x80", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.CONFINED_P2P", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.CONFINED_P2P", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x40", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.LOC_P2P", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.LOC_P2P", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x20", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MCAST", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MCAST", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MEM", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MEM", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MSGB", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MSGB", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.REM_P2P", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.REM_P2P", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x10", + "Unit": "IIO" + }, + { + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Posted requests sent by the integrated IO (IIO) controller to the Ubox, useful for counting message signaled interrupts (MSI).", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED", + "FCMask": "0x01", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "All 9 bits of Page Walk Tracker Occupancy", + "Counter": "0,1,2,3", + "EventCode": "0x42", + "EventName": "UNC_IIO_PWT_OCCUPANCY", + "Experimental": "1", + "PerPkg": "1", + "PortMask": "0x000", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core reading from Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_READ.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Core writing to Cards MMIO space", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.MEM_WRITE.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Another card (different IIO stack) reading from this card.", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.PEER_READ.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested by the CPU : Another card (different IIO stack) writing to this card.", + "Counter": "2,3", + "EventCode": "0xC1", + "EventName": "UNC_IIO_TXN_REQ_BY_CPU.PEER_WRITE.ALL_PARTS", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x0FF", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_READ.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x4", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART0", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART2", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART3", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART4", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART5", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART6", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to DRAM", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.MEM_WRITE.PART7", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x1", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card reading from another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_READ.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x8", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART0", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x001", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART1", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x002", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART2", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x004", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART3", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x008", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART4", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x010", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART5", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x020", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART6", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x040", + "UMask": "0x2", + "Unit": "IIO" + }, + { + "BriefDescription": "Number Transactions requested of the CPU : Card writing to another Card (same or different stack)", + "Counter": "0,1", + "EventCode": "0x84", + "EventName": "UNC_IIO_TXN_REQ_OF_CPU.PEER_WRITE.PART7", + "Experimental": "1", + "FCMask": "0x07", + "PerPkg": "1", + "PortMask": "0x080", + "UMask": "0x2", + "Unit": "IIO" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-memory.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-memory.json new file mode 100644 index 000000000000..8184c2b6b861 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-memory.json @@ -0,0 +1,883 @@ +[ + { + "BriefDescription": "DRAM Activate Count : Counts the number of DRAM Activate commands sent on this channel. Activate commands are issued to open up a page on the DRAM devices so that it can be read or written to with a CAS. One can calculate the number of Page Misses by subtracting the number of Page Miss precharges from the number of Activates.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_M_ACT_COUNT.ALL", + "PerPkg": "1", + "UMask": "0xf7", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Activate Count : Read transaction on Page Empty or Page Miss : Counts the number of DRAM Activate commands sent on this channel. Activate commands are issued to open up a page on the DRAM devices so that it can be read or written to with a CAS. One can calculate the number of Page Misses by subtracting the number of Page Miss precharges from the number of Activates.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_M_ACT_COUNT.RD", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf1", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Activate Count : Underfill Read transaction on Page Empty or Page Miss : Counts the number of DRAM Activate commands sent on this channel. Activate commands are issued to open up a page on the DRAM devices so that it can be read or written to with a CAS. One can calculate the number of Page Misses by subtracting the number of Page Miss precharges from the number of Activates.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_M_ACT_COUNT.UFILL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf4", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Activate Count : Write transaction on Page Empty or Page Miss : Counts the number of DRAM Activate commands sent on this channel. Activate commands are issued to open up a page on the DRAM devices so that it can be read or written to with a CAS. One can calculate the number of Page Misses by subtracting the number of Page Miss precharges from the number of Activates.", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_M_ACT_COUNT.WR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf2", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0, all CAS operations", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.ALL", + "PerPkg": "1", + "UMask": "0xff", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0, all reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD", + "PerPkg": "1", + "UMask": "0xcf", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 regular reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_NON_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc3", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 auto-precharge reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_PRE_REG", + "PerPkg": "1", + "UMask": "0xc2", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 auto-precharge underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_PRE_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc8", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 regular reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_REG", + "PerPkg": "1", + "UMask": "0xc1", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc4", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.RD_UNDERFILL_ALL", + "PerPkg": "1", + "UMask": "0xcc", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0, all writes", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.WR", + "PerPkg": "1", + "UMask": "0xf0", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 regular writes", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.WR_NONPRE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xd0", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 0 auto-precharge writes", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_M_CAS_COUNT_SCH0.WR_PRE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xe0", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1, all CAS operations", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.ALL", + "PerPkg": "1", + "UMask": "0xff", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1, all reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD", + "PerPkg": "1", + "UMask": "0xcf", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 regular reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_NON_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc3", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 auto-precharge reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_PRE_REG", + "PerPkg": "1", + "UMask": "0xc2", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 auto-precharge underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_PRE_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc8", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 regular reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_REG", + "PerPkg": "1", + "UMask": "0xc1", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_UNDERFILL", + "PerPkg": "1", + "UMask": "0xc4", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 underfill reads", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.RD_UNDERFILL_ALL", + "PerPkg": "1", + "UMask": "0xcc", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1, all writes", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.WR", + "PerPkg": "1", + "UMask": "0xf0", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 regular writes", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.WR_NONPRE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xd0", + "Unit": "IMC" + }, + { + "BriefDescription": "CAS count for SubChannel 1 auto-precharge writes", + "Counter": "0,1,2,3", + "EventCode": "0x06", + "EventName": "UNC_M_CAS_COUNT_SCH1.WR_PRE", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xe0", + "Unit": "IMC" + }, + { + "BriefDescription": "Number of DRAM DCLK clock cycles while the event is enabled. DCLK is 1/4 of DRAM data rate.", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "PublicDescription": "DRAM Clockticks", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "Number of DRAM HCLK clock cycles while the event is enabled", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_M_HCLOCKTICKS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "DRAM Clockticks", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 temp readings forced 2x refresh", + "Counter": "0,1,2,3", + "EventCode": "0xA7", + "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 temp readings forced 2x refresh", + "Counter": "0,1,2,3", + "EventCode": "0xA7", + "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 temp readings forced 2x refresh", + "Counter": "0,1,2,3", + "EventCode": "0xA7", + "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM0", + "UMask": "0x4", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 temp readings forced 2x refresh", + "Counter": "0,1,2,3", + "EventCode": "0xA7", + "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM1", + "UMask": "0x8", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 MRRs was triggered/running", + "Counter": "0,1,2,3", + "EventCode": "0xA6", + "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 MRRs was triggered/running", + "Counter": "0,1,2,3", + "EventCode": "0xA6", + "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 MRRs was triggered/running", + "Counter": "0,1,2,3", + "EventCode": "0xA6", + "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM0", + "UMask": "0x4", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles MR4 MRRs was triggered/running", + "Counter": "0,1,2,3", + "EventCode": "0xA6", + "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM1", + "UMask": "0x8", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH0_RANK0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH0_RANK1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH0_RANK2", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x4", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH0_RANK3", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x8", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH1_RANK0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x10", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH1_RANK1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x20", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH1_RANK2", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x40", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode", + "Counter": "0,1,2,3", + "EventCode": "0x47", + "EventName": "UNC_M_POWERDOWN_CYCLES.SCH1_RANK3", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x80", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles a given rank is in Power Down Mode and all pages are closed", + "Counter": "0,1,2,3", + "EventCode": "0x88", + "EventName": "UNC_M_POWER_CHANNEL_PPD_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Critical level on specified DIMM and throttle level is zero.", + "Counter": "0,1,2,3", + "EventCode": "0x89", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Critical level on specified DIMM and throttle level is zero.", + "Counter": "0,1,2,3", + "EventCode": "0x89", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "UNC_M_POWER_THROTTLE_CYCLES.BW_SLOT0", + "Counter": "0,1,2,3", + "EventCode": "0x46", + "EventName": "UNC_M_POWER_THROTTLE_CYCLES.BW_SLOT0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "UNC_M_POWER_THROTTLE_CYCLES.BW_SLOT1", + "Counter": "0,1,2,3", + "EventCode": "0x46", + "EventName": "UNC_M_POWER_THROTTLE_CYCLES.BW_SLOT1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "MR4 temp reading is throttling", + "Counter": "0,1,2,3", + "EventCode": "0x46", + "EventName": "UNC_M_POWER_THROTTLE_CYCLES.MR4BLKEN", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x8", + "Unit": "IMC" + }, + { + "BriefDescription": "RAPL is throttling", + "Counter": "0,1,2,3", + "EventCode": "0x46", + "EventName": "UNC_M_POWER_THROTTLE_CYCLES.RAPLBLK", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x4", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Precharge commands. : Counts the number of DRAM Precharge commands sent on this channel.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_PRE_COUNT.ALL", + "PerPkg": "1", + "UMask": "0xff", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Precharge commands. : Precharge due to (?) : Counts the number of DRAM Precharge commands sent on this channel.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_PRE_COUNT.PGT", + "PerPkg": "1", + "UMask": "0xf8", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Precharge commands. : Counts the number of DRAM Precharge commands sent on this channel.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_PRE_COUNT.RD", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf1", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Precharge commands. : Counts the number of DRAM Precharge commands sent on this channel.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_PRE_COUNT.UFILL", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf4", + "Unit": "IMC" + }, + { + "BriefDescription": "DRAM Precharge commands. : Counts the number of DRAM Precharge commands sent on this channel.", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_PRE_COUNT.WR", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xf2", + "Unit": "IMC" + }, + { + "BriefDescription": "Read buffer inserts on subchannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x17", + "EventName": "UNC_M_RDB_INSERTS.SCH0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "IMC" + }, + { + "BriefDescription": "Read buffer inserts on subchannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x17", + "EventName": "UNC_M_RDB_INSERTS.SCH1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "IMC" + }, + { + "BriefDescription": "Read buffer occupancy on subchannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x1a", + "EventName": "UNC_M_RDB_OCCUPANCY_SCH0", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Read buffer occupancy on subchannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x1b", + "EventName": "UNC_M_RDB_OCCUPANCY_SCH1", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue Allocations : Counts the number of allocations into the Read Pending Queue. This queue is used to schedule reads out to the memory controller and to track the requests. Requests allocate into the RPQ soon after they enter the memory controller, and need credits for an entry in this buffer before being sent from the HA to the iMC. They deallocate after the CAS command has been issued to memory. This includes both ISOCH and non-ISOCH requests.", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.PCH0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x50", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue Allocations : Counts the number of allocations into the Read Pending Queue. This queue is used to schedule reads out to the memory controller and to track the requests. Requests allocate into the RPQ soon after they enter the memory controller, and need credits for an entry in this buffer before being sent from the HA to the iMC. They deallocate after the CAS command has been issued to memory. This includes both ISOCH and non-ISOCH requests.", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.PCH1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xa0", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue inserts for subchannel 0, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.SCH0_PCH0", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue inserts for subchannel 0, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.SCH0_PCH1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue inserts for subchannel 1, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.SCH1_PCH0", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "IMC" + }, + { + "BriefDescription": "Read Pending Queue inserts for subchannel 1, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_M_RPQ_INSERTS.SCH1_PCH1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "IMC" + }, + { + "BriefDescription": "Read pending queue occupancy for subchannel 0, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_M_RPQ_OCCUPANCY_SCH0_PCH0", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Read pending queue occupancy for subchannel 0, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x81", + "EventName": "UNC_M_RPQ_OCCUPANCY_SCH0_PCH1", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Read pending queue occupancy for subchannel 1, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x82", + "EventName": "UNC_M_RPQ_OCCUPANCY_SCH1_PCH0", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Read pending queue occupancy for subchannel 1, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "UNC_M_RPQ_OCCUPANCY_SCH1_PCH1", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "subevent0 - # of cycles all ranks were in SR subevent1 - # of times all ranks went into SR subevent2 -# of times ps_sr_active asserted (SRE) subevent3 - # of times ps_sr_active deasserted (SRX) subevent4 - # of times PS-&>Refresh ps_sr_req asserted (SRE) subevent5 - # of times PS-&>Refresh ps_sr_req deasserted (SRX) subevent6 - # of cycles PSCtrlr FSM was in FATAL", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_SELF_REFRESH.ENTER_SUCCESS", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_SELF_REFRESH.ENTER_SUCCESS", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles all ranks were in SR", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_SELF_REFRESH.ENTER_SUCCESS_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "-", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Critical level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Critical level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8e", + "EventName": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at High level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8d", + "EventName": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at High level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8d", + "EventName": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Normal level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8b", + "EventName": "UNC_M_THROTTLE_LOW_CYCLES.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_LOW_CYCLES.SLOT0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Normal level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8b", + "EventName": "UNC_M_THROTTLE_LOW_CYCLES.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_LOW_CYCLES.SLOT1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Mid level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8c", + "EventName": "UNC_M_THROTTLE_MID_CYCLES.SLOT0", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_MID_CYCLES.SLOT0", + "UMask": "0x1", + "Unit": "IMC" + }, + { + "BriefDescription": "# of cycles Throttling at Mid level on specified DIMM", + "Counter": "0,1,2,3", + "EventCode": "0x8c", + "EventName": "UNC_M_THROTTLE_MID_CYCLES.SLOT1", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "UNC_M_THROTTLE_MID_CYCLES.SLOT1", + "UMask": "0x2", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue Allocations", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.PCH0", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0x50", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue Allocations", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.PCH1", + "Experimental": "1", + "PerPkg": "1", + "UMask": "0xa0", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue inserts for subchannel 0, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.SCH0_PCH0", + "PerPkg": "1", + "UMask": "0x10", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue inserts for subchannel 0, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.SCH0_PCH1", + "PerPkg": "1", + "UMask": "0x20", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue inserts for subchannel 1, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.SCH1_PCH0", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "IMC" + }, + { + "BriefDescription": "Write Pending Queue inserts for subchannel 1, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x22", + "EventName": "UNC_M_WPQ_INSERTS.SCH1_PCH1", + "PerPkg": "1", + "UMask": "0x80", + "Unit": "IMC" + }, + { + "BriefDescription": "Write pending queue occupancy for subchannel 0, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x84", + "EventName": "UNC_M_WPQ_OCCUPANCY_SCH0_PCH0", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Write pending queue occupancy for subchannel 0, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_WPQ_OCCUPANCY_SCH0_PCH1", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Write pending queue occupancy for subchannel 1, pseudochannel 0", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_WPQ_OCCUPANCY_SCH1_PCH0", + "PerPkg": "1", + "Unit": "IMC" + }, + { + "BriefDescription": "Write pending queue occupancy for subchannel 1, pseudochannel 1", + "Counter": "0,1,2,3", + "EventCode": "0x87", + "EventName": "UNC_M_WPQ_OCCUPANCY_SCH1_PCH1", + "PerPkg": "1", + "Unit": "IMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-power.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-power.json new file mode 100644 index 000000000000..9ea852ef190e --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/uncore-power.json @@ -0,0 +1,109 @@ +[ + { + "BriefDescription": "PCU Clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "PublicDescription": "PCU Clockticks: The PCU runs off a fixed 1 GHz clock. This event counts the number of pclk cycles measured while the counter was enabled. The pclk, like the Memory Controller's dclk, counts at a constant rate making it a good measure of actual wall time.", + "Unit": "PCU" + }, + { + "BriefDescription": "Thermal Strongest Upper Limit Cycles", + "Counter": "0,1,2,3", + "EventCode": "0x04", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Thermal Strongest Upper Limit Cycles : Number of cycles any frequency is reduced due to a thermal limit. Count only if throttling is occurring.", + "Unit": "PCU" + }, + { + "BriefDescription": "Power Strongest Upper Limit Cycles", + "Counter": "0,1,2,3", + "EventCode": "0x05", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Power Strongest Upper Limit Cycles : Counts the number of cycles when power is the upper limit on frequency.", + "Unit": "PCU" + }, + { + "BriefDescription": "Cycles spent changing Frequency", + "Counter": "0,1,2,3", + "EventCode": "0x74", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Cycles spent changing Frequency : Counts the number of cycles when the system is changing frequency. This can not be filtered by thread ID. One can also use it with the occupancy counter that monitors number of threads in C0 to estimate the performance impact that frequency transitions had on the system.", + "Unit": "PCU" + }, + { + "BriefDescription": "Package C State Residency - C2E", + "Counter": "0,1,2,3", + "EventCode": "0x2b", + "EventName": "UNC_P_PKG_RESIDENCY_C2E_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Package C State Residency - C2E : Counts the number of cycles when the package was in C2E. This event can be used in conjunction with edge detect to count C2E entrances (or exits using invert). Residency events do not include transition times.", + "Unit": "PCU" + }, + { + "BriefDescription": "Package C State Residency - C6", + "Counter": "0,1,2,3", + "EventCode": "0x2d", + "EventName": "UNC_P_PKG_RESIDENCY_C6_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Package C State Residency - C6 : Counts the number of cycles when the package was in C6. This event can be used in conjunction with edge detect to count C6 entrances (or exits using invert). Residency events do not include transition times.", + "Unit": "PCU" + }, + { + "BriefDescription": "Number of cores in C0", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C0", + "PerPkg": "1", + "PublicDescription": "Number of cores in C0 : This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with thresholding to generate histograms, or with other PCU events and occupancy triggering to capture other details.", + "Unit": "PCU" + }, + { + "BriefDescription": "Number of cores in C3", + "Counter": "0,1,2,3", + "EventCode": "0x36", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C3", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Number of cores in C3 : This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with thresholding to generate histograms, or with other PCU events and occupancy triggering to capture other details.", + "Unit": "PCU" + }, + { + "BriefDescription": "Number of cores in C6", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY_CORES_C6", + "PerPkg": "1", + "PublicDescription": "Number of cores in C6 : This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with thresholding to generate histograms, or with other PCU events and occupancy triggering to capture other details.", + "Unit": "PCU" + }, + { + "BriefDescription": "External Prochot", + "Counter": "0,1,2,3", + "EventCode": "0x0a", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "External Prochot : Counts the number of cycles that we are in external PROCHOT mode. This mode is triggered when a sensor off the die determines that something off-die (like DRAM) is too hot and must throttle to avoid damaging the chip.", + "Unit": "PCU" + }, + { + "BriefDescription": "Internal Prochot", + "Counter": "0,1,2,3", + "EventCode": "0x09", + "EventName": "UNC_P_PROCHOT_INTERNAL_CYCLES", + "Experimental": "1", + "PerPkg": "1", + "PublicDescription": "Internal Prochot : Counts the number of cycles that we are in Internal PROCHOT mode. This mode is triggered when a sensor on the die determines that we are too hot and must throttle to avoid damaging the chip.", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/clearwaterforest/virtual-memory.json b/tools/perf/pmu-events/arch/x86/clearwaterforest/virtual-memory.json index 78f2b835c1fa..a6a8733de235 100644 --- a/tools/perf/pmu-events/arch/x86/clearwaterforest/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/clearwaterforest/virtual-memory.json @@ -1,4 +1,20 @@ [ + { + "BriefDescription": "Counts the number of page walks initiated by a demand load that missed the first and second level TLBs.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x08", + "EventName": "DTLB_LOAD_MISSES.MISS_CAUSED_WALK", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of first level TLB misses but second level hits due to a demand load that did not start a page walk. Accounts for all page sizes. Will result in a DTLB write from STLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x08", + "EventName": "DTLB_LOAD_MISSES.STLB_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of page walks completed due to load DTLB misses to any page size.", "Counter": "0,1,2,3,4,5,6,7", @@ -8,6 +24,32 @@ "SampleAfterValue": "1000003", "UMask": "0xe" }, + { + "BriefDescription": "Counts the number of page walks completed due to load DTLB misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x08", + "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to loads (including SW prefetches) whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of page walks initiated by a store that missed the first and second level TLBs.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x49", + "EventName": "DTLB_STORE_MISSES.MISS_CAUSED_WALK", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of first level TLB misses but second level hits due to stores that did not start a page walk. Accounts for all page sizes. Will result in a DTLB write from STLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x49", + "EventName": "DTLB_STORE_MISSES.STLB_HIT", + "PublicDescription": "Counts the number of first level TLB misses but second level hits due to a demand load that did not start a page walk. Accounts for all page sizes. Will result in a DTLB write from STLB.", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of page walks completed due to store DTLB misses to any page size.", "Counter": "0,1,2,3,4,5,6,7", @@ -17,6 +59,31 @@ "SampleAfterValue": "1000003", "UMask": "0xe" }, + { + "BriefDescription": "Counts the number of page walks completed due to store DTLB misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x49", + "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to stores whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of page walks initiated by a instruction fetch that missed the first and second level TLBs.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.MISS_CAUSED_WALK", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of first level TLB misses but second level hits due to an instruction fetch that did not start a page walk. Account for all pages sizes. Will result in an ITLB write from STLB.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.STLB_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of page walks completed due to instruction fetch misses to any page size.", "Counter": "0,1,2,3,4,5,6,7", @@ -25,5 +92,32 @@ "PublicDescription": "Counts the number of page walks completed due to instruction fetches whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to any page size. Includes page walks that page fault.", "SampleAfterValue": "1000003", "UMask": "0xe" + }, + { + "BriefDescription": "Counts the number of page walks completed due to instruction fetch misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to instruction fetches whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of page walks outstanding for iside in PMH every cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.WALK_PENDING", + "PublicDescription": "Counts the number of page walks outstanding for iside in PMH every cycle. A PMH page walk is outstanding from page walk start till PMH becomes idle again (ready to serve next walk). Includes EPT-walk intervals. Walks could be counted by edge detecting on this event, but would count restarted suspended walks.", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of retired loads that are blocked due to a first level TLB miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.DTLB_MISS", + "PublicDescription": "Counts the number of retired loads that are blocked due to a first level TLB miss. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8" } ] diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 85f41cab56c7..a52909e212b4 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -7,7 +7,7 @@ GenuineIntel-6-(3D|47),v30,broadwell,core GenuineIntel-6-56,v12,broadwellde,core GenuineIntel-6-4F,v23,broadwellx,core GenuineIntel-6-55-[56789ABCDEF],v1.25,cascadelakex,core -GenuineIntel-6-DD,v1.00,clearwaterforest,core +GenuineIntel-6-DD,v1.02,clearwaterforest,core GenuineIntel-6-9[6C],v1.05,elkhartlake,core GenuineIntel-6-CF,v1.21,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core From 91a914e8b9bf937d7a700005dc9c0b83b7dfbc2f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:47 -0700 Subject: [PATCH 2207/5214] perf vendor events intel: Update emeraldrapids events from 1.21 to 1.23 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/526f1bf0ad6a42d275d1bb115cd337b71c561f92 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/x86/emeraldrapids/cache.json | 18 ++++++++++++++++++ tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json index b2f8947f6741..ff6071d7728e 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json @@ -181,6 +181,15 @@ "SampleAfterValue": "200003", "UMask": "0xff" }, + { + "BriefDescription": "All requests that hit L2 cache [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf" + }, { "BriefDescription": "Read requests with true-miss in L2 cache. [This event is alias to L2_RQSTS.MISS]", "Counter": "0,1,2,3", @@ -279,6 +288,15 @@ "SampleAfterValue": "200003", "UMask": "0x21" }, + { + "BriefDescription": "All requests that hit L2 cache [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf" + }, { "BriefDescription": "L2_RQSTS.HWPF_MISS", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index a52909e212b4..b6d4d37bcf99 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -9,7 +9,7 @@ GenuineIntel-6-4F,v23,broadwellx,core GenuineIntel-6-55-[56789ABCDEF],v1.25,cascadelakex,core GenuineIntel-6-DD,v1.02,clearwaterforest,core GenuineIntel-6-9[6C],v1.05,elkhartlake,core -GenuineIntel-6-CF,v1.21,emeraldrapids,core +GenuineIntel-6-CF,v1.23,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core GenuineIntel-6-7A,v1.01,goldmontplus,core GenuineIntel-6-B6,v1.11,grandridge,core From 409c04ab6b94ce0380034f416c515b19dee54b19 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:48 -0700 Subject: [PATCH 2208/5214] perf vendor events intel: Update grandridge events from 1.11 to 1.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/50159a77124571c633adc2625fa7b566010d5001 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/x86/grandridge/cache.json | 56 ++++++++++++++++ .../arch/x86/grandridge/floating-point.json | 8 +++ .../arch/x86/grandridge/frontend.json | 64 +++++++++++++++++++ .../arch/x86/grandridge/pipeline.json | 15 +++++ .../arch/x86/grandridge/uncore-io.json | 15 ++--- .../arch/x86/grandridge/uncore-memory.json | 32 +++++----- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 7 files changed, 167 insertions(+), 25 deletions(-) diff --git a/tools/perf/pmu-events/arch/x86/grandridge/cache.json b/tools/perf/pmu-events/arch/x86/grandridge/cache.json index 0aa921ba89b4..393ce9421c12 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/cache.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/cache.json @@ -121,6 +121,14 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to an instruction cache or TLB miss which missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.L2_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x7e" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which hit in the LLC. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", "Counter": "0,1,2,3,4,5,6,7", @@ -129,6 +137,14 @@ "SampleAfterValue": "1000003", "UMask": "0x6" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which hit in the LLC, no snoop was required. LLC provides the data. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which missed all the caches. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -137,6 +153,14 @@ "SampleAfterValue": "1000003", "UMask": "0x78" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which missed all the caches. DRAM, MMIO or other LOCAL memory type provides the data. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an L1 demand load miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -154,6 +178,14 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to a demand load which missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.L2_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x7e" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", "Counter": "0,1,2,3,4,5,6,7", @@ -162,6 +194,14 @@ "SampleAfterValue": "1000003", "UMask": "0x6" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, a snoop was required, the snoop misses or the snoop hits but NO_FWD. LLC provides the data. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the local caches. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -170,6 +210,14 @@ "SampleAfterValue": "1000003", "UMask": "0x78" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a demand load miss and the data was provided from an unknown source. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a store buffer full condition", "Counter": "0,1,2,3,4,5,6,7", @@ -178,6 +226,14 @@ "SampleAfterValue": "1000003", "UMask": "0x80" }, + { + "BriefDescription": "Counts the total number of load ops retired that miss the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, { "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/floating-point.json b/tools/perf/pmu-events/arch/x86/grandridge/floating-point.json index 5266eed969be..c567f073713c 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/floating-point.json @@ -90,6 +90,14 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer store data port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.STD", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of floating point operations retired that required microcode assist.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/frontend.json b/tools/perf/pmu-events/arch/x86/grandridge/frontend.json index fef5cba533bb..8a591e31d331 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/frontend.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/frontend.json @@ -8,6 +8,54 @@ "SampleAfterValue": "200003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged with having preceded with frontend bound behavior", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ALL", + "SampleAfterValue": "1000003" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a baclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_DETECT", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles /empty issue slots due to a btclear", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.BRANCH_RESTEER", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.CISC", + "PublicDescription": "Counts the number of instructions retired that were tagged following an ms flow due to the bubble/wasted issue slot from exiting long ms flow", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged every cycle the decoder is unable to send 3 uops per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DECODE", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", "Counter": "0,1,2,3,4,5,6,7", @@ -16,6 +64,22 @@ "SampleAfterValue": "1000003", "UMask": "0x10" }, + { + "BriefDescription": "Counts the number of instruction retired tagged after a wasted issue slot if none of the previous events occurred", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.OTHER", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, + { + "BriefDescription": "Counts the number of instruction retired that are tagged after a branch instruction causes bubbles/empty issue slots due to a predecode wrong.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.PREDECODE", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts every time the code stream enters into a new cache line by walking sequential from the previous line or being redirected by a jump.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json b/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json index 20986b987e18..0a8f7d327150 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json @@ -260,6 +260,13 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the total number of machine clears for any reason including, but not limited to, memory ordering, memory disambiguation, SMC, and FP assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY", + "SampleAfterValue": "20003" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering in which an internal load passes an older store within the same CPU.", "Counter": "0,1,2,3,4,5,6,7", @@ -268,6 +275,14 @@ "SampleAfterValue": "20003", "UMask": "0x8" }, + { + "BriefDescription": "Counts the number of machines clears due to memory renaming.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, { "BriefDescription": "Counts the number of machine clears due to a page fault. Counts both I-Side and D-Side (Loads/Stores) page faults. A page fault occurs when either the page is not present, or an access violation occurs.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json b/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json index 764cf2f0b4a8..1a495a89ed57 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/uncore-io.json @@ -824,7 +824,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.ABORT", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.ABORT", @@ -836,7 +836,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.CONFINED_P2P", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.CONFINED_P2P", @@ -848,7 +848,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.LOC_P2P", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.LOC_P2P", @@ -860,7 +860,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MCAST", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MCAST", @@ -872,7 +872,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MEM", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MEM", @@ -884,7 +884,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MSGB", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.MSGB", @@ -896,7 +896,7 @@ "Unit": "IIO" }, { - "BriefDescription": "-", + "BriefDescription": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX", "Counter": "0,1,2,3", "EventCode": "0x8e", "EventName": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX", @@ -915,7 +915,6 @@ "FCMask": "0x01", "PerPkg": "1", "PortMask": "0x0FF", - "PublicDescription": "-", "UMask": "0x4", "Unit": "IIO" }, diff --git a/tools/perf/pmu-events/arch/x86/grandridge/uncore-memory.json b/tools/perf/pmu-events/arch/x86/grandridge/uncore-memory.json index 6a11e5505957..8413391d752c 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/uncore-memory.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/uncore-memory.json @@ -195,7 +195,7 @@ "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM0", "UMask": "0x1", "Unit": "IMC" }, @@ -206,7 +206,7 @@ "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH0_DIMM1", "UMask": "0x2", "Unit": "IMC" }, @@ -217,7 +217,7 @@ "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM0", "UMask": "0x4", "Unit": "IMC" }, @@ -228,7 +228,7 @@ "EventName": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_MR4_2XREF_CYCLES.SCH1_DIMM1", "UMask": "0x8", "Unit": "IMC" }, @@ -239,7 +239,7 @@ "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM0", "UMask": "0x1", "Unit": "IMC" }, @@ -250,7 +250,7 @@ "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH0_DIMM1", "UMask": "0x2", "Unit": "IMC" }, @@ -261,7 +261,7 @@ "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM0", "UMask": "0x4", "Unit": "IMC" }, @@ -272,7 +272,7 @@ "EventName": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_PDC_MR4ACTIVE_CYCLES.SCH1_DIMM1", "UMask": "0x8", "Unit": "IMC" }, @@ -617,7 +617,7 @@ "EventName": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT0", "UMask": "0x1", "Unit": "IMC" }, @@ -628,7 +628,7 @@ "EventName": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_CRIT_CYCLES.SLOT1", "UMask": "0x2", "Unit": "IMC" }, @@ -639,7 +639,7 @@ "EventName": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT0", "UMask": "0x1", "Unit": "IMC" }, @@ -650,7 +650,7 @@ "EventName": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_HIGH_CYCLES.SLOT1", "UMask": "0x2", "Unit": "IMC" }, @@ -661,7 +661,7 @@ "EventName": "UNC_M_THROTTLE_LOW_CYCLES.SLOT0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_LOW_CYCLES.SLOT0", "UMask": "0x1", "Unit": "IMC" }, @@ -672,7 +672,7 @@ "EventName": "UNC_M_THROTTLE_LOW_CYCLES.SLOT1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_LOW_CYCLES.SLOT1", "UMask": "0x2", "Unit": "IMC" }, @@ -683,7 +683,7 @@ "EventName": "UNC_M_THROTTLE_MID_CYCLES.SLOT0", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_MID_CYCLES.SLOT0", "UMask": "0x1", "Unit": "IMC" }, @@ -694,7 +694,7 @@ "EventName": "UNC_M_THROTTLE_MID_CYCLES.SLOT1", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "-", + "PublicDescription": "UNC_M_THROTTLE_MID_CYCLES.SLOT1", "UMask": "0x2", "Unit": "IMC" }, diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index b6d4d37bcf99..a1548f1306a6 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -12,7 +12,7 @@ GenuineIntel-6-9[6C],v1.05,elkhartlake,core GenuineIntel-6-CF,v1.23,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core GenuineIntel-6-7A,v1.01,goldmontplus,core -GenuineIntel-6-B6,v1.11,grandridge,core +GenuineIntel-6-B6,v1.12,grandridge,core GenuineIntel-6-A[DE],v1.17,graniterapids,core GenuineIntel-6-(3C|45|46),v36,haswell,core GenuineIntel-6-3F,v29,haswellx,core From 8036f156851c4f35e180ecd27d56bdb8144bc1e8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:49 -0700 Subject: [PATCH 2209/5214] perf vendor events intel: Update graniterapids events from 1.17 to 1.18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/5b93c8c750300a15b29a9a718511869b280c91f4 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/graniterapids/cache.json | 9 +++++++++ tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/cache.json b/tools/perf/pmu-events/arch/x86/graniterapids/cache.json index db28866444b6..95d6b46f3445 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/cache.json @@ -296,6 +296,15 @@ "SampleAfterValue": "200003", "UMask": "0x40" }, + { + "BriefDescription": "Cycles when L1D is locked", + "Counter": "0,1,2,3", + "EventCode": "0x42", + "EventName": "LOCK_CYCLES.CACHE_LOCK_DURATION", + "PublicDescription": "This event counts the number of cycles when the L1D is locked. It is a superset of the 0x1 mask (BUS_LOCK_CLOCKS.BUS_LOCK_DURATION).", + "SampleAfterValue": "2000003", + "UMask": "0x2" + }, { "BriefDescription": "Core-originated cacheable requests that missed L3 (Except hardware prefetches to the L3)", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index a1548f1306a6..b97d19ae4264 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -13,7 +13,7 @@ GenuineIntel-6-CF,v1.23,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core GenuineIntel-6-7A,v1.01,goldmontplus,core GenuineIntel-6-B6,v1.12,grandridge,core -GenuineIntel-6-A[DE],v1.17,graniterapids,core +GenuineIntel-6-A[DE],v1.18,graniterapids,core GenuineIntel-6-(3C|45|46),v36,haswell,core GenuineIntel-6-3F,v29,haswellx,core GenuineIntel-6-7[DE],v1.24,icelake,core From 988e264ae93f3e1a6059c13e6036c1fd73a585e2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:50 -0700 Subject: [PATCH 2210/5214] perf vendor events intel: Update lunarlake events from 1.21 to 1.22 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/fae822a0f9318e602902eeb2166b966a28c715f8 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/x86/lunarlake/cache.json | 20 +++++++++++++++++++ .../arch/x86/lunarlake/pipeline.json | 9 +++++++++ tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json index 2db3e8a51fbd..92a3667b4520 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json @@ -289,6 +289,16 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Counts the number of total L2 Cache Accesses that resulted in a Miss from a front door request only (does not include rejects or recycles), per core event", "Counter": "0,1,2,3,4,5,6,7", @@ -387,6 +397,16 @@ "UMask": "0x21", "Unit": "cpu_core" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Read requests with true-miss in L2 cache [This event is alias to L2_REQUEST.MISS]", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json index d98723b3cd78..d66eafccebbb 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json @@ -315,6 +315,15 @@ "UMask": "0xfd", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of relative JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.REL_JMP", + "SampleAfterValue": "200003", + "UMask": "0xdf", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index b97d19ae4264..4176d22da1a7 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -22,7 +22,7 @@ GenuineIntel-6-3A,v24,ivybridge,core GenuineIntel-6-3E,v24,ivytown,core GenuineIntel-6-2D,v24,jaketown,core GenuineIntel-6-(57|85),v16,knightslanding,core -GenuineIntel-6-BD,v1.21,lunarlake,core +GenuineIntel-6-BD,v1.22,lunarlake,core GenuineIntel-6-(AA|AC|B5),v1.20,meteorlake,core GenuineIntel-6-1[AEF],v4,nehalemep,core GenuineIntel-6-2E,v4,nehalemex,core From 8dee02f7397e7725f7a6e3f0f74349333af8a832 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:51 -0700 Subject: [PATCH 2211/5214] perf vendor events intel: Update meteorlake events from 1.20 to 1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/419a6600ad2019d4acbf0f79cc54cde85164afc1 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- .../pmu-events/arch/x86/meteorlake/cache.json | 55 ++++++ .../arch/x86/meteorlake/floating-point.json | 63 +++++++ .../arch/x86/meteorlake/memory.json | 64 +++++++ .../arch/x86/meteorlake/pipeline.json | 158 ++++++++++++++++++ .../arch/x86/meteorlake/virtual-memory.json | 9 + 6 files changed, 350 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 4176d22da1a7..1a6bb8597cbe 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -23,7 +23,7 @@ GenuineIntel-6-3E,v24,ivytown,core GenuineIntel-6-2D,v24,jaketown,core GenuineIntel-6-(57|85),v16,knightslanding,core GenuineIntel-6-BD,v1.22,lunarlake,core -GenuineIntel-6-(AA|AC|B5),v1.20,meteorlake,core +GenuineIntel-6-(AA|AC|B5),v1.21,meteorlake,core GenuineIntel-6-1[AEF],v4,nehalemep,core GenuineIntel-6-2E,v4,nehalemex,core GenuineIntel-6-CC,v1.04,pantherlake,core diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json index 4c1220c19456..6419bc36f249 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json @@ -1,4 +1,13 @@ [ + { + "BriefDescription": "Counts the number of request that were not accepted into the L2Q because the L2Q is FULL.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x31", + "EventName": "CORE_REJECT_L2Q.ANY", + "PublicDescription": "Counts the number of (demand and L1 prefetchers) core requests rejected by the L2Q due to a full or nearly full w condition which likely indicates back pressure from L2Q. It also counts requests that would have gone directly to the XQ, but are rejected due to a full or nearly full condition, indicating back pressure from the IDI link. The L2Q may also reject transactions from a core to insure fairness between cores, or to delay a cores dirty eviction when the address conflicts incoming external snoops. (Note that L2 prefetcher requests that are dropped are not counted by this event.) Counts on a per core basis.", + "SampleAfterValue": "200003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L1D cacheline (dirty) evictions caused by load misses, stores, and prefetches.", "Counter": "0,1,2,3,4,5,6,7", @@ -181,6 +190,15 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x30", + "EventName": "L2_REJECT_XQ.ANY", + "PublicDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition which likely indicates back pressure from the IDI link. The XQ may reject transactions from the L2Q (non-cacheable requests), BBL (L2 misses) and WOB (L2 write-back victims).", + "SampleAfterValue": "200003", + "Unit": "cpu_atom" + }, { "BriefDescription": "All accesses to L2 cache [This event is alias to L2_RQSTS.REFERENCES]", "Counter": "0,1,2,3", @@ -885,6 +903,33 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_HITM", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and no data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_NO_FWD", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and non-modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_WITH_FWD", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM", "Counter": "0,1,2,3,4,5,6,7", @@ -993,6 +1038,16 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST)", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL", + "SampleAfterValue": "200003", + "UMask": "0x83", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load ops retired.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json b/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json index 28dc5e06ee31..1ccbd54904c5 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json @@ -19,6 +19,24 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of active floating point dividers per cycle in the loop stage.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of floating point divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts all microcode FP assists.", "Counter": "0,1,2,3,4,5,6,7", @@ -274,6 +292,51 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of uops executed on all floating point ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xf", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0, 1, 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of uops executed on floating point and vector integer store data port.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/memory.json b/tools/perf/pmu-events/arch/x86/meteorlake/memory.json index f0cbeda4d5ca..7cdd5cb39009 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/memory.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/memory.json @@ -19,6 +19,15 @@ "UMask": "0x6", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.ANY", + "SampleAfterValue": "1000003", + "UMask": "0x7f", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block, on a load that retires.", "Counter": "0,1,2,3,4,5,6,7", @@ -37,6 +46,15 @@ "UMask": "0xf4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -46,6 +64,16 @@ "UMask": "0x81", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", "Counter": "0,1,2,3,4,5,6,7", @@ -56,6 +84,15 @@ "UMask": "0xc0", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a pagewalk.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.PGWALK", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a pagewalk.", "Counter": "0,1,2,3,4,5,6,7", @@ -65,6 +102,15 @@ "UMask": "0xa0", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a store address match.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.ST_ADDR", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a store address match.", "Counter": "0,1,2,3,4,5,6,7", @@ -74,6 +120,24 @@ "UMask": "0x84", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json index 7662846745bd..09e1147c4733 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json @@ -20,6 +20,16 @@ "UMask": "0x9", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles when any of the integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event counts the cycles the integer divider is busy.", "Counter": "0,1,2,3,4,5,6,7", @@ -30,6 +40,24 @@ "UMask": "0x8", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of active integer dividers per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of integer divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Number of occurrences where a microcode assist is invoked by hardware.", "Counter": "0,1,2,3,4,5,6,7", @@ -255,6 +283,16 @@ "UMask": "0xdf", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of taken branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "Errata": "MTL013", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -929,6 +967,89 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of uops executed on all Integer ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on a load port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.LD", + "PublicDescription": "Counts the number of uops executed on a load port. This event counts for integer uops even if the destination is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P3", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0,1, 2, 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0x78", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on a Store address port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STA", + "PublicDescription": "Counts the number of uops executed on a Store address port. This event counts integer uops even if the data source is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of uops executed on an integer store data and jump port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STD_JMP", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "INT_VEC_RETIRED.128BIT", "Counter": "0,1,2,3,4,5,6,7", @@ -1131,6 +1252,24 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FAST", + "SampleAfterValue": "20003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "20003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machines clears due to memory renaming.", "Counter": "0,1,2,3,4,5,6,7", @@ -1303,6 +1442,25 @@ "UMask": "0x4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.NON_C01_MS_SCB", + "PublicDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires. The most commonly executed instruction with an MS scoreboard is PAUSE.", + "SampleAfterValue": "200003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event counts a subset of the Topdown Slots event that were not consumed by the back-end pipeline due to lack of back-end resources, as a result of memory subsystem delays, execution units limitations, or other conditions.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/meteorlake/virtual-memory.json index 305b96b26a4e..04396c7b3e08 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/virtual-memory.json @@ -356,6 +356,15 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DTLB miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.DTLB_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DTLB miss.", "Counter": "0,1,2,3,4,5,6,7", From 4aa006c38dca636519241667175f9125ff630838 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:52 -0700 Subject: [PATCH 2212/5214] perf vendor events intel: Update pantherlake events from 1.04 to 1.05 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/f5593317a6dee0d11bb2db9a5895db1f231267a9 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- .../arch/x86/pantherlake/cache.json | 152 ++++++++++-- .../arch/x86/pantherlake/frontend.json | 60 +++++ .../arch/x86/pantherlake/memory.json | 29 +++ .../arch/x86/pantherlake/other.json | 10 + .../arch/x86/pantherlake/pipeline.json | 231 +++++++++++++++++- 6 files changed, 462 insertions(+), 22 deletions(-) diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 1a6bb8597cbe..70ba1af93822 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -26,7 +26,7 @@ GenuineIntel-6-BD,v1.22,lunarlake,core GenuineIntel-6-(AA|AC|B5),v1.21,meteorlake,core GenuineIntel-6-1[AEF],v4,nehalemep,core GenuineIntel-6-2E,v4,nehalemex,core -GenuineIntel-6-CC,v1.04,pantherlake,core +GenuineIntel-6-(CC|D5),v1.05,pantherlake,core GenuineIntel-6-A7,v1.04,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core GenuineIntel-6-8F,v1.36,sapphirerapids,core diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/cache.json b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json index e5323093eec0..165e955b870c 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json @@ -1,4 +1,13 @@ [ + { + "BriefDescription": "Counts the number of requests that were not accepted into the L2Q because the L2Q is FULL.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x31", + "EventName": "CORE_REJECT_L2Q.ANY", + "PublicDescription": "Counts the number of (demand and L1 prefetchers) core requests rejected by the L2Q due to a full or nearly full condition which likely indicates back pressure from L2Q. It also counts requests that would have gone directly to the XQ, but are rejected due to a full or nearly full condition, indicating back pressure from the IDI link. The L2Q may also reject transactions from a core to ensure fairness between cores, or to delay a core's dirty eviction when the address conflicts with incoming external snoops. Note that L2 prefetcher requests that are dropped are not counted by this event. Counts on a per core basis.", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cache lines replaced in L0 data cache.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -130,6 +139,15 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x30", + "EventName": "L2_REJECT_XQ.ANY", + "PublicDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition which likely indicates back pressure from the IDI link. The IDI link is a central on-die protocol for memory and coherence traffic between the core and the uncore. It is highly optimized for efficiency, bandwidth, and coherency, using multiple channels and flow control mechanisms to manage high-throughput, low-latency data and protocol communication within the chip. The XQ may reject transactions from the L2Q (non-cacheable requests), BBL (L2 misses) and WOB (L2 write-back victims).", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door requests for Code Read, Data Read, RFO, ITOM, and L2 Prefetches. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -158,6 +176,15 @@ "UMask": "0xc4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x84", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -176,6 +203,15 @@ "UMask": "0xc1", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x81", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -194,6 +230,15 @@ "UMask": "0xc2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests that resulted in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests that resulted in a Miss. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -212,6 +257,16 @@ "UMask": "0x1bf", "Unit": "cpu_atom" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -221,6 +276,24 @@ "UMask": "0xc8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests that result in a hit. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF_HIT", + "SampleAfterValue": "1000003", + "UMask": "0x88", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests that result in a miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x48", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door requests that resulted in a Miss. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -310,6 +383,16 @@ "UMask": "0x21", "Unit": "cpu_core" }, + { + "BriefDescription": "All requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0x5f", + "Unit": "cpu_core" + }, { "BriefDescription": "Read requests with true-miss in L2 cache [This event is alias to L2_REQUEST.MISS]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -1019,6 +1102,19 @@ "UMask": "0x82", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of tagged load uops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY", + "MSRIndex": "0x3F6", + "MSRValue": "0x1", + "PublicDescription": "Counts the number of tagged load uops retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x5", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024.", "Counter": "0,1,2,3,4,5,6,7", @@ -1033,27 +1129,27 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", "MSRIndex": "0x3F6", "MSRValue": "0x80", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", "MSRIndex": "0x3F6", "MSRValue": "0x10", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" @@ -1072,79 +1168,79 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", "MSRIndex": "0x3F6", "MSRValue": "0x100", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", "MSRIndex": "0x3F6", "MSRValue": "0x20", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", "MSRIndex": "0x3F6", "MSRValue": "0x4", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", "MSRIndex": "0x3F6", "MSRValue": "0x200", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", "MSRIndex": "0x3F6", "MSRValue": "0x40", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", "MSRIndex": "0x3F6", "MSRValue": "0x8", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" @@ -1159,6 +1255,26 @@ "UMask": "0x21", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of load uops retired that were correctly predicated by the memory renaming (MRN) feature.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.MRN_LOADS", + "PublicDescription": "Counts the number of load uops retired that were correctly predicated by the memory renaming (MRN) feature. MRN loads are a part of the memory renaming process that optimizes data forwarding between stores and loads, reducing latency and improving performance. This involves tagging stores that forward to loads and copying the physical source (the actual data) directly from the store to the load. The MRN'd load can complete without accessing the memory, thus reducing load-to-use latency. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x9", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of store uops retired that were correctly tagged by the memory renaming (MRN) feature.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.MRN_STORES", + "PublicDescription": "Counts the number of store uops retired that were correctly tagged by the memory renaming (MRN) feature. MRN stores are a part of the memory renaming process that optimizes data forwarding between stores and loads, reducing latency and improving performance. This involves tagging stores that forward to loads and copying the physical source (the actual data) directly from the store to the load. The MRN store ensures that it's data is accurately forwarded to matching subsequent loads. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xa", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of memory uops retired that were splits.", "Counter": "0,1,2,3,4,5,6,7", @@ -1220,12 +1336,12 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", - "PublicDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of stores uops retired. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x6", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json index 5e69b81742f5..ef490c38569d 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json @@ -120,6 +120,16 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ICACHE", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to icache miss Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", "Counter": "0,1,2,3,4,5,6,7", @@ -344,6 +354,56 @@ "UMask": "0x1", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L2_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x6", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and did not have to snoop another core.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT_NO_SNOOP", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and did not have to snoop another core. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and required a snoop (that resulted in a snoop miss, snoop hitm, snoop hit with fwd, or snoop hit with no fwd).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_HIT_WITH_SNOOP", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that hit in the L3 cache, and required a snoop (that resulted in a snoop miss, snoop hitm, snoop hit with fwd, or snoop hit with no fwd). Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 and L3 caches.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L3_MISS", + "PublicDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that also missed in the L2 and L3 caches. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that hit in the second level TLB.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/memory.json b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json index 4248cc101391..ac0446359de3 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/memory.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json @@ -26,6 +26,26 @@ "UMask": "0x81", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER_AT_RET", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0xc0", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", "Counter": "0,1,2,3,4,5,6,7", @@ -63,6 +83,15 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MEMORY_ORDERING_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8002", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 1024 cycles.", "Counter": "2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/other.json b/tools/perf/pmu-events/arch/x86/pantherlake/other.json index 915c52f5abd1..91dd2d25ebaf 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/other.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/other.json @@ -18,6 +18,16 @@ "UMask": "0x8", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. [This event is alias to MISC_RETIRED.LBR_INSERTS]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe4", + "EventName": "LBR_INSERTS.ANY", + "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. [This event is alias to MISC_RETIRED.LBR_INSERTS] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts streaming stores that have any type of response.", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json index 86009237df2f..d476bad5e2a7 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json @@ -258,6 +258,16 @@ "UMask": "0x30", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near direct CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIRECT_CALL", + "PublicDescription": "Counts the number of near direct CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "near relative call instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_REL_CALL]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -268,6 +278,16 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near direct JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIRECT_JMP", + "PublicDescription": "Counts the number of near direct JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_DIR_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, { "BriefDescription": "near relative jump instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_REL_JMP]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -278,6 +298,28 @@ "UMask": "0x80", "Unit": "cpu_core" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIR_CALL", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_DIR_JMP", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_DIRECT_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND]", "Counter": "0,1,2,3,4,5,6,7", @@ -298,6 +340,16 @@ "UMask": "0x50", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_CALL", + "PublicDescription": "Counts the number of near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Indirect near call instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_CALL]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -308,6 +360,16 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near indirect JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_JMP", + "PublicDescription": "Counts the number of near indirect JMP branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Indirect near jump instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_IND_JMP]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -328,6 +390,17 @@ "UMask": "0x58", "Unit": "cpu_atom" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_IND_CALL", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -339,6 +412,17 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_JMP]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_IND_JMP", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_JMP] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_JMP]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -350,6 +434,16 @@ "UMask": "0x40", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_JMP", + "PublicDescription": "Counts the number of near JMP branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xc0", + "Unit": "cpu_atom" + }, { "BriefDescription": "Indirect and Direct Relative near jump instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -382,6 +476,27 @@ "UMask": "0x80", "Unit": "cpu_core" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_RETURN]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_RET", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_RETURN] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of near RET branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_RET]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_RETURN", + "PublicDescription": "Counts the number of near RET branch instructions retired. [This event is alias to BR_INST_RETIRED.NEAR_RET] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Return instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -966,7 +1081,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -1554,6 +1669,23 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the total number of machine clears for any reason including, but not limited to, memory ordering, memory disambiguation, SMC, and FP assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x80ff", + "Unit": "cpu_atom" + }, { "BriefDescription": "Number of machine clears (nukes) of any type.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -1575,6 +1707,42 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.DISAMBIGUATION_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8008", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machines clears due to memory renaming.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE_FAST", + "SampleAfterValue": "1000003", + "UMask": "0x8010", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machine clears due to a page fault. Counts both I-Side and D-Side (Loads/Stores) page faults. A page fault occurs when either the page is not present, or an access violation occurs.", "Counter": "0,1,2,3,4,5,6,7", @@ -1603,6 +1771,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of machine clears due to program modifying data (cross-core modifying code) within 1K of a recently fetched code page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.XMC", + "PublicDescription": "Counts the number of machine clears due to program modifying data (self modifying code) within 1K of a recently fetched code page.", + "SampleAfterValue": "1000003", + "UMask": "0x80", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts cycles where no execution is happening due to loads waiting for L1 cache (that is: no execution & load in flight & no load missed L1 cache)", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -1650,11 +1828,12 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL.", + "BriefDescription": "This event is deprecated. [This event is alias to LBR_INSERTS.ANY]", "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", "EventCode": "0xe4", "EventName": "MISC_RETIRED.LBR_INSERTS", - "PublicDescription": "Counts the number of LBR entries recorded. Requires LBRs to be enabled in IA32_LBR_CTL. Available PDIST counters: 0,1", + "PublicDescription": "This event is deprecated. [This event is alias to LBR_INSERTS.ANY] Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_atom" @@ -1943,6 +2122,25 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to IEC and FPC RAT stalls - which can be due to the FIQ and IEC reservation station stall (integer, FP and SIMD scheduler not being able to accept another uop).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.NON_MEM_SCHEDULER", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to mrbl stall. A marble refers to a physical register file entry, also known as the physical destination (PDST).", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.REGISTER", + "PublicDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to mrbl stall. A 'marble' refers to a physical register file entry, also known as the physical destination (PDST).", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to ROB full", "Counter": "0,1,2,3,4,5,6,7", @@ -2014,6 +2212,15 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to frontend bandwidth restrictions due to decode, predecode, cisc, and other limitations.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x71", + "EventName": "TOPDOWN_FE_BOUND.FRONTEND_BANDWIDTH", + "SampleAfterValue": "1000003", + "UMask": "0x8d", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to latency related stalls including BACLEARs, BTCLEARs, ITLB misses, and ICache misses.", "Counter": "0,1,2,3,4,5,6,7", @@ -2262,6 +2469,14 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the total number of uops retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.ALL", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles with retired uop(s).", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -2273,6 +2488,16 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of uops retired that were dual destination.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.DUAL_DEST", + "PublicDescription": "Counts the number of uops retired that were dual destination. Dual destination (dual dest) micro-operations require writing to two separate destination registers or memory addresses upon execution. These uops consume two entries in the ROB and tracked separately because they involve more complex operations that necessitate multiple results. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of uops retired that are the last uop of a macro-instruction.", "Counter": "0,1,2,3,4,5,6,7", From ab85585e388caf9556f70bf9361b7cbe15af85ac Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:53 -0700 Subject: [PATCH 2213/5214] perf vendor events intel: Update sapphirerapids events from 1.36 to 1.39 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/0718b785554ba9bb7f87ad2b838cf25bab5bfa9c https://github.com/intel/perfmon/commit/42fe96774f8bda1d67c6ad7ef7f45b27fae7c696 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- .../arch/x86/sapphirerapids/cache.json | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 70ba1af93822..dbe1fe5a68f6 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -29,7 +29,7 @@ GenuineIntel-6-2E,v4,nehalemex,core GenuineIntel-6-(CC|D5),v1.05,pantherlake,core GenuineIntel-6-A7,v1.04,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core -GenuineIntel-6-8F,v1.36,sapphirerapids,core +GenuineIntel-6-8F,v1.39,sapphirerapids,core GenuineIntel-6-AF,v1.15,sierraforest,core GenuineIntel-6-(37|4A|4C|4D|5A),v15,silvermont,core GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v59,skylake,core diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json index 373b26c84448..4c096b5e6766 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json @@ -181,6 +181,15 @@ "SampleAfterValue": "200003", "UMask": "0xff" }, + { + "BriefDescription": "All requests that hit L2 cache [This event is alias to L2_RQSTS.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_RQSTS.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf" + }, { "BriefDescription": "Read requests with true-miss in L2 cache. [This event is alias to L2_RQSTS.MISS]", "Counter": "0,1,2,3", @@ -279,6 +288,15 @@ "SampleAfterValue": "200003", "UMask": "0x21" }, + { + "BriefDescription": "All requests that hit L2 cache [This event is alias to L2_REQUEST.HIT]", + "Counter": "0,1,2,3", + "EventCode": "0x24", + "EventName": "L2_RQSTS.HIT", + "PublicDescription": "Counts all requests that hit L2 cache. [This event is alias to L2_REQUEST.HIT]", + "SampleAfterValue": "200003", + "UMask": "0xdf" + }, { "BriefDescription": "L2_RQSTS.HWPF_MISS", "Counter": "0,1,2,3", @@ -350,6 +368,15 @@ "SampleAfterValue": "200003", "UMask": "0x40" }, + { + "BriefDescription": "Cycles when L1D is locked", + "Counter": "0,1,2,3", + "EventCode": "0x42", + "EventName": "LOCK_CYCLES.CACHE_LOCK_DURATION", + "PublicDescription": "This event counts the number of cycles when the L1D is locked. It is a superset of the 0x1 mask (BUS_LOCK_CLOCKS.BUS_LOCK_DURATION).", + "SampleAfterValue": "2000003", + "UMask": "0x2" + }, { "BriefDescription": "Core-originated cacheable requests that missed L3 (Except hardware prefetches to the L3)", "Counter": "0,1,2,3,4,5,6,7", From 33518bdc96c49c7d213f081feefbcb62bc956035 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 28 May 2026 21:51:54 -0700 Subject: [PATCH 2214/5214] perf vendor events intel: Update sierraforest events from 1.15 to 1.17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated events and metrics were published in: https://github.com/intel/perfmon/commit/efa15280e0577982744642a77af18208aab3635b https://github.com/intel/perfmon/commit/5cdba6c2ccfde2ec13e0e701bc2a374849ce9a44 Reviewed-by: Dapeng Mi Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andreas Färber Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Manivannan Sadhasivam Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/mapfile.csv | 2 +- .../arch/x86/sierraforest/cache.json | 49 ++++++ .../arch/x86/sierraforest/floating-point.json | 64 +++++++ .../arch/x86/sierraforest/memory.json | 57 +++++++ .../arch/x86/sierraforest/pipeline.json | 156 ++++++++++++++++++ .../arch/x86/sierraforest/virtual-memory.json | 8 + 6 files changed, 335 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index dbe1fe5a68f6..50a403b429b1 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -30,7 +30,7 @@ GenuineIntel-6-(CC|D5),v1.05,pantherlake,core GenuineIntel-6-A7,v1.04,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core GenuineIntel-6-8F,v1.39,sapphirerapids,core -GenuineIntel-6-AF,v1.15,sierraforest,core +GenuineIntel-6-AF,v1.17,sierraforest,core GenuineIntel-6-(37|4A|4C|4D|5A),v15,silvermont,core GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v59,skylake,core GenuineIntel-6-55-[01234],v1.37,skylakex,core diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/cache.json b/tools/perf/pmu-events/arch/x86/sierraforest/cache.json index 168f43557a0e..71acb49b2ab9 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/cache.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/cache.json @@ -1,4 +1,12 @@ [ + { + "BriefDescription": "Counts the number of requests that were not accepted into the L2Q because the L2Q is FULL.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x31", + "EventName": "CORE_REJECT_L2Q.ANY", + "PublicDescription": "Counts the number of (demand and L1 prefetchers) core requests rejected by the L2Q due to a full or nearly full w condition which likely indicates back pressure from L2Q. It also counts requests that would have gone directly to the XQ, but are rejected due to a full or nearly full condition, indicating back pressure from the IDI link. The L2Q may also reject transactions from a core to insure fairness between cores, or to delay a cores dirty eviction when the address conflicts incoming external snoops. (Note that L2 prefetcher requests that are dropped are not counted by this event.) Counts on a per core basis.", + "SampleAfterValue": "200003" + }, { "BriefDescription": "Counts the number of L1D cacheline (dirty) evictions caused by load misses, stores, and prefetches.", "Counter": "0,1,2,3,4,5,6,7", @@ -62,6 +70,14 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x30", + "EventName": "L2_REJECT_XQ.ANY", + "PublicDescription": "Counts the number of demand and prefetch transactions that the External Queue (XQ) rejects due to a full or near full condition which likely indicates back pressure from the IDI link. The XQ may reject transactions from the L2Q (non-cacheable requests), BBL (L2 misses) and WOB (L2 write-back victims).", + "SampleAfterValue": "200003" + }, { "BriefDescription": "Counts the number of L2 Cache Accesses that resulted in a Hit from a front door request only (does not include rejects or recycles), per core event", "Counter": "0,1,2,3,4,5,6,7", @@ -154,6 +170,14 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to a demand load which missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.L2_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x7e" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", "Counter": "0,1,2,3,4,5,6,7", @@ -178,6 +202,14 @@ "SampleAfterValue": "1000003", "UMask": "0x78" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the caches, a snoop was required, and hits in other core or module on the same die. Another core provides the data with a FWD, NO_FWD, or HITM. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_OTHERMOD", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a store buffer full condition", "Counter": "0,1,2,3,4,5,6,7", @@ -227,6 +259,14 @@ "SampleAfterValue": "1000003", "UMask": "0x4" }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_HITM", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, { "BriefDescription": "Counts the number of load ops retired that hit the L1 data cache.", "Counter": "0,1,2,3,4,5,6,7", @@ -307,6 +347,15 @@ "SampleAfterValue": "20003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of memory uops retired. A single uop that performs both a load AND a store will be counted as 1, not 2 (e.g. ADD [mem], CONST)", + "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", + "EventCode": "0xd0", + "EventName": "MEM_UOPS_RETIRED.ALL", + "SampleAfterValue": "200003", + "UMask": "0x83" + }, { "BriefDescription": "Counts the number of load ops retired.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/floating-point.json b/tools/perf/pmu-events/arch/x86/sierraforest/floating-point.json index 5266eed969be..67d52477aad6 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/floating-point.json @@ -8,6 +8,22 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the number of active floating point dividers per cycle in the loop stage.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of floating point divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, { "BriefDescription": "Counts the number of all types of floating point operations per uop with all default weighting", "Counter": "0,1,2,3,4,5,6,7", @@ -90,6 +106,54 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the number of uops executed on all floating point ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xf" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer port 0, 1, 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0xe" + }, + { + "BriefDescription": "Counts the number of uops executed on floating point and vector integer store data port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb2", + "EventName": "FP_VINT_UOPS_EXECUTED.STD", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of floating point operations retired that required microcode assist.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/memory.json b/tools/perf/pmu-events/arch/x86/sierraforest/memory.json index dc850a179517..01e07f90e76b 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/memory.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/memory.json @@ -1,4 +1,12 @@ [ + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.ANY", + "SampleAfterValue": "1000003", + "UMask": "0x7f" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to any number of reasons, including an L1 miss, WCB full, pagewalk, store address block or store data block, on a load that retires.", "Counter": "0,1,2,3,4,5,6,7", @@ -15,6 +23,14 @@ "SampleAfterValue": "1000003", "UMask": "0xf4" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -23,6 +39,15 @@ "SampleAfterValue": "1000003", "UMask": "0x81" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.OTHER", + "PublicDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to other block cases such as pipeline conflicts, fences, etc.", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to other block cases.", "Counter": "0,1,2,3,4,5,6,7", @@ -32,6 +57,14 @@ "SampleAfterValue": "1000003", "UMask": "0xc0" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a pagewalk.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.PGWALK", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a pagewalk.", "Counter": "0,1,2,3,4,5,6,7", @@ -40,6 +73,14 @@ "SampleAfterValue": "1000003", "UMask": "0xa0" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a store address match.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.ST_ADDR", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a store address match.", "Counter": "0,1,2,3,4,5,6,7", @@ -48,6 +89,22 @@ "SampleAfterValue": "1000003", "UMask": "0x84" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json index cf67ff6135e0..e7ffb0042477 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json @@ -8,6 +8,31 @@ "SampleAfterValue": "1000003", "UMask": "0x3" }, + { + "BriefDescription": "Counts the number of cycles when any of the integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of active integer dividers per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of integer divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the total number of branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -121,6 +146,15 @@ "SampleAfterValue": "200003", "UMask": "0xdf" }, + { + "BriefDescription": "Counts the number of taken branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "Errata": "SRF7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.TAKEN", + "SampleAfterValue": "200003", + "UMask": "0x80" + }, { "BriefDescription": "Counts the total number of mispredicted branch instructions retired for all branch types.", "Counter": "0,1,2,3,4,5,6,7", @@ -244,6 +278,80 @@ "EventName": "INST_RETIRED.ANY_P", "SampleAfterValue": "2000003" }, + { + "BriefDescription": "Counts the number of uops executed on all Integer ports.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.ALL", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of uops executed on a load port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.LD", + "PublicDescription": "Counts the number of uops executed on a load port. This event counts for integer uops even if the destination is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P0", + "SampleAfterValue": "1000003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 1.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P1", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 2.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P2", + "SampleAfterValue": "1000003", + "UMask": "0x20" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.P3", + "SampleAfterValue": "1000003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of uops executed on integer port 0,1, 2, 3.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.PRIMARY", + "SampleAfterValue": "1000003", + "UMask": "0x78" + }, + { + "BriefDescription": "Counts the number of uops executed on a Store address port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STA", + "PublicDescription": "Counts the number of uops executed on a Store address port. This event counts integer uops even if the data source is FP/vector", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of uops executed on an integer store data and jump port.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xb3", + "EventName": "INT_UOPS_EXECUTED.STD_JMP", + "SampleAfterValue": "1000003", + "UMask": "0x4" + }, { "BriefDescription": "Counts the number of retired loads that are blocked because it initially appears to be store forward blocked, but subsequently is shown not to be blocked based on 4K alias check.", "Counter": "0,1,2,3,4,5,6,7", @@ -268,6 +376,13 @@ "SampleAfterValue": "1000003", "UMask": "0x2" }, + { + "BriefDescription": "Counts the total number of machine clears for any reason including, but not limited to, memory ordering, memory disambiguation, SMC, and FP assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY", + "SampleAfterValue": "20003" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering in which an internal load passes an older store within the same CPU.", "Counter": "0,1,2,3,4,5,6,7", @@ -276,6 +391,30 @@ "SampleAfterValue": "20003", "UMask": "0x8" }, + { + "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FAST", + "SampleAfterValue": "20003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of virtual traps taken.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.FPC_VIRTUAL_TRAP", + "SampleAfterValue": "20003", + "UMask": "0x40" + }, + { + "BriefDescription": "Counts the number of machines clears due to memory renaming.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MRN_NUKE", + "SampleAfterValue": "1000003", + "UMask": "0x80" + }, { "BriefDescription": "Counts the number of machine clears due to a page fault. Counts both I-Side and D-Side (Loads/Stores) page faults. A page fault occurs when either the page is not present, or an access violation occurs.", "Counter": "0,1,2,3,4,5,6,7", @@ -349,6 +488,23 @@ "SampleAfterValue": "200003", "UMask": "0x4" }, + { + "BriefDescription": "Counts the number of issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "200003", + "UMask": "0x8" + }, + { + "BriefDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.NON_C01_MS_SCB", + "PublicDescription": "Counts the number of issue slots not consumed by the backend due to a micro-sequencer (MS) scoreboard, which stalls the front-end from issuing from the UROM until a specified older uop retires. The most commonly executed instruction with an MS scoreboard is PAUSE.", + "SampleAfterValue": "200003", + "UMask": "0x2" + }, { "BriefDescription": "Counts the number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear. [This event is alias to TOPDOWN_BAD_SPECULATION.ALL_P]", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/virtual-memory.json b/tools/perf/pmu-events/arch/x86/sierraforest/virtual-memory.json index 35cc5b6d41f2..88996fdac15f 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/virtual-memory.json @@ -137,6 +137,14 @@ "SampleAfterValue": "200003", "UMask": "0x10" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DTLB miss", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.DTLB_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DTLB miss.", "Counter": "0,1,2,3,4,5,6,7", From a43581b5dd1f05dd42f9a7b43e3bdfa09daed440 Mon Sep 17 00:00:00 2001 From: Chun-Tse Shao Date: Thu, 28 May 2026 16:44:54 -0700 Subject: [PATCH 2215/5214] perf jevents: Add IOMMU metrics for AMD Add IOMMU Translation Lookaside Buffer (TLB) and interrupt cache metrics to perf jevents for AMD platforms. This enhances I/O performance observability, allowing fleet-wide monitoring of IOMMU overhead. These metrics are supported on Zen 2 and newer processors (Rome, Milan, Genoa, Turin) and are implemented using the standard `amd_iommu` PMU events. The implementation uses the existing `_zen_model` helper to ensure these are only generated for Zen 2+. Note that the pde events on AMD cover both 2M and 1G pages, so 1G pages are implicitly included in the total hits/misses metrics (sum of pte and pde events). The following metrics are added: - iotlb_total_hit: Total IOTLB hits (4K, 2M, 1G pages). - iotlb_total_miss: Total IOTLB misses. - iotlb_miss_rate: IOTLB miss rate. - iotlb_interrupt_cache_hit: Interrupt cache hits. - iotlb_interrupt_cache_miss: Interrupt cache misses. - iotlb_interrupt_cache_lookup: Interrupt cache lookups. - iotlb_interrupt_cache_miss_rate: Interrupt cache miss rate. Tested: # perf stat -M \ iotlb_total_hit,iotlb_total_miss,iotlb_miss_rate \ --per-socket --metric-only -a -j -- sleep 10 {"socket" : "S0", "counters" : 10, "hits iotlb_total_hit" : "3579249.0", "% iotlb_miss_rate" : "0.0", "misses iotlb_total_miss" : "3.0"} {"socket" : "S1", "counters" : 10, "hits iotlb_total_hit" : "0.0", "% iotlb_miss_rate" : "0.0", "misses iotlb_total_miss" : "0.0"} Reviewed-by: Ian Rogers Reviewed-by: Sandipan Das Signed-off-by: Chun-Tse Shao Assisted-by: Gemini:gemini-3.1-pro-preview Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/amd_metrics.py | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tools/perf/pmu-events/amd_metrics.py b/tools/perf/pmu-events/amd_metrics.py index 971f6e7af1f8..dccfcacaf148 100755 --- a/tools/perf/pmu-events/amd_metrics.py +++ b/tools/perf/pmu-events/amd_metrics.py @@ -265,6 +265,62 @@ def AmdDtlb() -> Optional[MetricGroup]: ], description="Data TLB metrics") +def AmdIotlb() -> Optional[MetricGroup]: + global _zen_model + if _zen_model < 2: + return None + + # On AMD, the pde events cover both 2M and 1G pages. + total_hit = Event("amd_iommu/mem_iommu_tlb_pte_hit/") + Event( + "amd_iommu/mem_iommu_tlb_pde_hit/" + ) + total_miss = Event("amd_iommu/mem_iommu_tlb_pte_mis/") + Event( + "amd_iommu/mem_iommu_tlb_pde_mis/" + ) + miss_rate = d_ratio(total_miss, total_miss + total_hit) + + interrupt_cache_hit = Event("amd_iommu/int_dte_hit/") + interrupt_cache_miss = Event("amd_iommu/int_dte_mis/") + interrupt_cache_lookup = interrupt_cache_hit + interrupt_cache_miss + interrupt_cache_miss_rate = d_ratio( + interrupt_cache_miss, interrupt_cache_miss + interrupt_cache_hit + ) + + return MetricGroup( + "iotlb", + [ + Metric("iotlb_total_hit", "IOTLB total hit", total_hit, "hits"), + Metric("iotlb_total_miss", "IOTLB total miss", total_miss, "misses"), + Metric("iotlb_miss_rate", "IOTLB miss rate", miss_rate, "100%"), + Metric( + "iotlb_interrupt_cache_hit", + "IOTLB interrupt cache hit", + interrupt_cache_hit, + "hits", + ), + Metric( + "iotlb_interrupt_cache_miss", + "IOTLB interrupt cache miss", + interrupt_cache_miss, + "misses", + ), + Metric( + "iotlb_interrupt_cache_lookup", + "IOTLB interrupt cache lookup", + interrupt_cache_lookup, + "lookups", + ), + Metric( + "iotlb_interrupt_cache_miss_rate", + "IOTLB interrupt cache miss rate", + interrupt_cache_miss_rate, + "100%", + ), + ], + description="IOMMU TLB metrics", + ) + + def AmdItlb(): global _zen_model l2h = Event("bp_l1_tlb_miss_l2_tlb_hit", "bp_l1_tlb_miss_l2_hit") @@ -473,6 +529,7 @@ def main() -> None: AmdBr(), AmdCtxSw(), AmdDtlb(), + AmdIotlb(), AmdItlb(), AmdLdSt(), AmdUpc(), From 3dfaf02e554f63f9b4e68b69deda9617a1948993 Mon Sep 17 00:00:00 2001 From: Chun-Tse Shao Date: Thu, 28 May 2026 16:44:55 -0700 Subject: [PATCH 2216/5214] perf jevents: Add IOMMU metrics for Intel Add IOMMU Translation Lookaside Buffer (TLB) and interrupt cache metrics to perf jevents for Intel platforms. This enhances I/O performance observability, allowing fleet-wide monitoring of IOMMU overhead. These metrics are supported on platforms that expose the required uncore IIO IOMMU events (such as Emerald Rapids and Granite Rapids). The Intel implementation dynamically detects event availability at generation time. It requires at least the TLB events to expose the metric group, while the interrupt cache events are optional. This allows platforms like Emerald Rapids, which lack IOMMU interrupt cache events, to still expose the IOMMU TLB metrics. The following metrics are added: - iotlb_total_hit: Total IOTLB hits (4K, 2M, 1G pages). - iotlb_total_miss: Total IOTLB misses. - iotlb_miss_rate: IOTLB miss rate. - iotlb_interrupt_cache_hit: Interrupt cache hits. - iotlb_interrupt_cache_miss: Interrupt cache misses (calculated as lookup - hit, clamped to zero). - iotlb_interrupt_cache_lookup: Interrupt cache lookups. - iotlb_interrupt_cache_miss_rate: Interrupt cache miss rate. Tested: # perf stat -M \ iotlb_total_hit,iotlb_total_miss,iotlb_miss_rate \ --per-socket --metric-only -a -j -- sleep 10 {"socket" : "S0", "counters" : 10, "hits iotlb_total_hit" : "3579249.0", "% iotlb_miss_rate" : "0.0", "misses iotlb_total_miss" : "3.0"} {"socket" : "S1", "counters" : 10, "hits iotlb_total_hit" : "0.0", "% iotlb_miss_rate" : "0.0", "misses iotlb_total_miss" : "0.0"} Reviewed-by: Ian Rogers Signed-off-by: Chun-Tse Shao Assisted-by: Gemini:gemini-3.1-pro-preview Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Sandipan Das Link: https://lore.kernel.org/r/20260528234455.434027-3-ctshao@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/intel_metrics.py | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index 52035433b505..c3a5c2965f74 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -457,6 +457,67 @@ def IntelIlp() -> MetricGroup: ]) +def IntelIotlb() -> Optional[MetricGroup]: + try: + total_hit = ( + Event("UNC_IIO_IOMMU0.4K_HITS") + + Event("UNC_IIO_IOMMU0.2M_HITS") + + Event("UNC_IIO_IOMMU0.1G_HITS") + ) + total_miss = Event("UNC_IIO_IOMMU0.MISSES") + except: + return None + + miss_rate = d_ratio(total_miss, total_miss + total_hit) + metrics = [ + Metric("iotlb_total_hit", "IOTLB total hit", total_hit, "hits"), + Metric("iotlb_total_miss", "IOTLB total miss", total_miss, "misses"), + Metric("iotlb_miss_rate", "IOTLB miss rate", miss_rate, "100%"), + ] + + try: + interrupt_cache_hit = Event("UNC_IIO_IOMMU3.INT_CACHE_HITS") + interrupt_cache_lookup = Event("UNC_IIO_IOMMU3.INT_CACHE_LOOKUPS") + interrupt_cache_miss = max(interrupt_cache_lookup - interrupt_cache_hit, 0) + interrupt_cache_miss_rate = d_ratio( + interrupt_cache_miss, interrupt_cache_miss + interrupt_cache_hit + ) + metrics += [ + Metric( + "iotlb_interrupt_cache_hit", + "IOTLB interrupt cache hit", + interrupt_cache_hit, + "hits", + ), + Metric( + "iotlb_interrupt_cache_miss", + "IOTLB interrupt cache miss", + interrupt_cache_miss, + "misses", + ), + Metric( + "iotlb_interrupt_cache_lookup", + "IOTLB interrupt cache lookup", + interrupt_cache_lookup, + "lookups", + ), + Metric( + "iotlb_interrupt_cache_miss_rate", + "IOTLB interrupt cache miss rate", + interrupt_cache_miss_rate, + "100%", + ), + ] + except: + pass + + return MetricGroup( + "iotlb", + metrics, + description="IOMMU TLB metrics", + ) + + def IntelL2() -> Optional[MetricGroup]: try: DC_HIT = Event("L2_RQSTS.DEMAND_DATA_RD_HIT") @@ -1105,6 +1166,7 @@ def main() -> None: IntelCtxSw(), IntelFpu(), IntelIlp(), + IntelIotlb(), IntelL2(), IntelLdSt(), IntelMissLat(), From 167bef4df68635f8bfa2af9108351ee78536d7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADn=20Gil?= Date: Tue, 26 May 2026 13:08:52 +0200 Subject: [PATCH 2217/5214] perf util: Fix perf_exe() buffer write past end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf_exe() passes len to readlink() and then unconditionally writes a trailing NUL at buf[n]. If readlink() returns len, the write lands one byte past the buffer. Read at most len - 1 bytes and keep the existing NUL termination. Also guard the fallback path for tiny buffers so copying "perf" cannot overflow. Reviewed-by: Ian Rogers Signed-off-by: Miguel Martín Gil Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 25849434f0a4..2c2a5c449ffd 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -419,11 +419,21 @@ int perf_tip(char **strp, const char *dirpath) char *perf_exe(char *buf, int len) { - int n = readlink("/proc/self/exe", buf, len); + int n; + + if (len <= 0) + return buf; + + n = readlink("/proc/self/exe", buf, len - 1); if (n > 0) { buf[n] = 0; return buf; } + if (len < (int)sizeof("perf")) { + buf[0] = '\0'; + return buf; + } + return strcpy(buf, "perf"); } From 83eff458a690e650b68cd6467aae8959755c5388 Mon Sep 17 00:00:00 2001 From: James Clark Date: Fri, 10 Apr 2026 12:05:12 +0100 Subject: [PATCH 2218/5214] perf arm-spe: Don't warn about the discard bit if it doesn't exist Opening an SPE event shows a warning that doesn't concern the user: $ perf record -e arm_spe Unknown/empty format name: discard Perf only wants to know if the discard bit is set for configuring the event, not in response to anything the user has done. Fix it by adding another helper that returns if a config bit exists without warning. We should probably keep the warning in evsel__get_config_val() to avoid having every caller having to do it, and most format bits should never be missing. Add a test for the new helper. Rename the parent test function to be more generic rather than adding a new one as it requires a lot of boilerplate. Reviewed-by: Ian Rogers Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/util/arm-spe.c | 3 ++- tools/perf/tests/pmu.c | 11 +++++++++-- tools/perf/util/evsel.c | 6 ++++++ tools/perf/util/evsel.h | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index f00d72d087fc..91bb28cad79a 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -428,7 +428,8 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, evlist__for_each_entry_safe(evlist, tmp, evsel) { if (evsel__is_aux_event(evsel)) { arm_spe_setup_evsel(evsel, cpus); - if (!evsel__get_config_val(evsel, "discard", &discard_bit)) + if (evsel__config_exists(evsel, "discard") && + !evsel__get_config_val(evsel, "discard", &discard_bit)) discard = !!discard_bit; } } diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 0ebf2d7b2cb4..d7be9d1c6f52 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -201,7 +201,8 @@ static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest return ret; } -static int test__pmu_usr_chgs(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +static int test__pmu_config_helpers(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) { const char *event = "perf-pmu-test/config=15,config1=4,krava02=170," "krava03=1,krava11=27,krava12=1/"; @@ -236,6 +237,12 @@ static int test__pmu_usr_chgs(struct test_suite *test __maybe_unused, int subtes } evsel = evlist__first(evlist); + /* Test evsel__config_exists() */ + TEST_ASSERT_EQUAL("krava01 should exist", + evsel__config_exists(evsel, "krava01"), true); + TEST_ASSERT_EQUAL("krava99 should not exist", + evsel__config_exists(evsel, "krava99"), false); + /* * Set via config=15, krava01 bits 0-1 * Set via config1=4, krava11 bit 1 @@ -629,7 +636,7 @@ static struct test_case tests__pmu[] = { TEST_CASE("PMU name combining", name_len), TEST_CASE("PMU name comparison", name_cmp), TEST_CASE("PMU cmdline match", pmu_match), - TEST_CASE("PMU user config changes", pmu_usr_chgs), + TEST_CASE("PMU config helpers", pmu_config_helpers), { .name = NULL, } }; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ac92f9e0e5b4..34c03f47a913 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1399,6 +1399,12 @@ void evsel__set_config_if_unset(struct evsel *evsel, const char *config_name, perf_pmu__format_pack(format->bits, val, vp, /*zero=*/true); } +bool evsel__config_exists(const struct evsel *evsel, const char *config_name) +{ + struct perf_pmu_format *format = pmu_find_format(&evsel->pmu->format, config_name); + + return format && !bitmap_empty(format->bits, PERF_PMU_FORMAT_BITS); +} int evsel__get_config_val(const struct evsel *evsel, const char *config_name, u64 *val) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 3b12d99f0aa9..8178858d168a 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -589,6 +589,7 @@ void evsel__uniquify_counter(struct evsel *counter); ((((src) >> (pos)) & ((1ull << (size)) - 1)) << (63 - ((pos) + (size) - 1))) u64 evsel__bitfield_swap_branch_flags(u64 value); +bool evsel__config_exists(const struct evsel *evsel, const char *config_name); int evsel__get_config_val(const struct evsel *evsel, const char *config_name, u64 *val); void evsel__set_config_if_unset(struct evsel *evsel, const char *config_name, From 6539aef6347ee57301c7e47a518bbc9403dba6fa Mon Sep 17 00:00:00 2001 From: Dapeng Mi Date: Thu, 28 May 2026 10:36:36 -0500 Subject: [PATCH 2219/5214] perf script: Fix missing '+' indicator when branch counter reaches upper limit When displaying branch counter (br_cntr) information, a "+" suffix represents that event occurrences may have been lost due to branch counter saturation. However, this indicator was missing in perf script. Add it back. Before: # Branch counter abbr list: # cpu_core/event=0xc4,umask=0x20/ppp = A # cpu_core/instructions/ = B # cpu_core/MEM_INST_RETIRED.ALL_LOADS/ = C # cpu_core/MEM_LOAD_RETIRED.L2_MISS/ = D # '-' No event occurs # '+' Event occurrences may be lost due to branch counter saturated ... datasym+190: 00005567f9951676 jz 0x5567f995162dr_cntr: BBBC # PRED 1 cycles [1] ... After: ... datasym+190: 00005567f9951676 jz 0x5567f995162dr_cntr: BBB+C # PRED 1 cycles [1] ... Reviewed-by: Dapeng Mi Signed-off-by: Dapeng Mi Signed-off-by: Thomas Falcon Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index e330ae7f725e..5124edf2b7a6 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1291,8 +1291,12 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, if (!verbose) { for (j = 0; j < num; j++) printed += fprintf(fp, "%s", pos->abbr_name); - } else - printed += fprintf(fp, "%s %d ", pos->name, num); + if (mask && (num == mask)) + printed += fprintf(fp, "+"); + } else { + printed += fprintf(fp, "%s %d%s", pos->name, + num, mask && (num == mask) ? "+ " : " "); + } } if (numprinted == 0 && !verbose) printed += fprintf(fp, "-"); From bb4832101b0969d7d3faf7dd6095274db288cd0f Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Thu, 28 May 2026 10:36:37 -0500 Subject: [PATCH 2220/5214] perf annotate: Fix missing branch counter column in TUI mode 'perf annotate' checks that evlist->nr_br_cntr has been incremented to determine whether to show branch counter information. However, this data is not populated until after the check when events are processed. Therefore, this counter will always be less than zero and the Branch Count column is never shown. Do this check after events have been processed and branch counter data is updated. Reviewed-by: Dapeng Mi Signed-off-by: Thomas Falcon Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 719b36d4eed5..5f450c8093c0 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -559,6 +559,10 @@ static int __cmd_annotate(struct perf_annotate *ann) if (ret) goto out; + if ((use_browser == 1 || ann->use_stdio2) && ann->has_br_stack) + if (session->evlist->nr_br_cntr > 0) + annotate_opts.show_br_cntr = true; + if (dump_trace) { perf_session__fprintf_nr_events(session, stdout); evlist__fprintf_nr_events(session->evlist, stdout); @@ -922,11 +926,8 @@ int cmd_annotate(int argc, const char **argv) * branch counters, if the corresponding branch info is available * in the perf data in the TUI mode. */ - if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) { + if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) sort__mode = SORT_MODE__BRANCH; - if (annotate.session->evlist->nr_br_cntr > 0) - annotate_opts.show_br_cntr = true; - } if (setup_sorting(/*evlist=*/NULL, perf_session__env(annotate.session)) < 0) usage_with_options(annotate_usage, options); From 185cd1d8cb7f0fd30d868fa12d294bba5997b7c7 Mon Sep 17 00:00:00 2001 From: Hendrik Noack Date: Thu, 28 May 2026 22:05:25 -0700 Subject: [PATCH 2221/5214] dt-bindings: Input: Add Wacom W9000-series penabled touchscreens Add bindings for Wacom W9002 and two Wacom W9007 variants which can be found in tablets. W9002, W9007A LT03, and W9007A V1 differ in the length of the return message containing coordinates, distance, pressure and button status. Co-developed-by: Ferass El Hafidi Signed-off-by: Ferass El Hafidi Signed-off-by: Hendrik Noack Acked-by: Conor Dooley Link: https://patch.msgid.link/20260528074818.12151-2-hendrik-noack@gmx.de Signed-off-by: Dmitry Torokhov --- .../input/touchscreen/wacom,w9007a-lt03.yaml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml diff --git a/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml b/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml new file mode 100644 index 000000000000..6d1da6a435d3 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/wacom,w9007a-lt03.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wacom W9000-series penabled I2C touchscreen + +maintainers: + - Hendrik Noack + +description: | + The W9000-series are penabled touchscreen controllers by Wacom. + + The firmware of controllers in different devices may differ. This can also + affect the controller's behavior. + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + enum: + - wacom,w9002 + - wacom,w9007a-lt03 + - wacom,w9007a-v1 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + + flash-mode-gpios: + maxItems: 1 + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + digitizer@56 { + compatible = "wacom,w9007a-lt03"; + reg = <0x56>; + interrupt-parent = <&gpd1>; + interrupts = <1 IRQ_TYPE_EDGE_RISING>; + + vdd-supply = <&stylus_reg>; + + flash-mode-gpios = <&gpd1 3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpx0 1 GPIO_ACTIVE_LOW>; + + touchscreen-x-mm = <216>; + touchscreen-y-mm = <135>; + touchscreen-inverted-x; + }; + }; From acaefbacc997ae79527a5ade8720221dfe17407e Mon Sep 17 00:00:00 2001 From: Hendrik Noack Date: Thu, 28 May 2026 21:28:48 -0700 Subject: [PATCH 2222/5214] Input: Add support for Wacom W9000-series penabled touchscreens Add driver for Wacom W9002 and two Wacom W9007A variants. These are penabled touchscreens supporting passive Wacom Pens and use I2C. Co-developed-by: Ferass El Hafidi Signed-off-by: Ferass El Hafidi Signed-off-by: Hendrik Noack Link: https://patch.msgid.link/20260528074818.12151-3-hendrik-noack@gmx.de Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wacom_w9000.c | 444 ++++++++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 drivers/input/touchscreen/wacom_w9000.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 484522d8d675..9b9ae8ac3f7f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -610,6 +610,18 @@ config TOUCHSCREEN_WACOM_I2C To compile this driver as a module, choose M here: the module will be called wacom_i2c. +config TOUCHSCREEN_WACOM_W9000 + tristate "Wacom W9000-series penabled touchscreen (I2C)" + depends on I2C + help + Say Y here if you have a Wacom W9000-series penabled I2C touchscreen. + This driver supports models W9002 and W9007A. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module + will be called wacom_w9000. + config TOUCHSCREEN_LPC32XX tristate "LPC32XX touchscreen controller" depends on ARCH_LPC32XX diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6d397268d2e3..bfd9de83389d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -100,6 +100,7 @@ tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO) += tsc2007_iio.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W9000) += wacom_w9000.o obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C) += wdt87xx_i2c.o obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o diff --git a/drivers/input/touchscreen/wacom_w9000.c b/drivers/input/touchscreen/wacom_w9000.c new file mode 100644 index 000000000000..4a2629a54167 --- /dev/null +++ b/drivers/input/touchscreen/wacom_w9000.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Wacom W9000-series penabled I2C touchscreen driver + * + * Copyright (c) 2026 Hendrik Noack + * + * Partially based on vendor driver: + * Copyright (C) 2012, Samsung Electronics Co. Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Some chips have flaky firmware that requires many retries before responding. */ +#define CMD_QUERY_RETRIES 8 + +/* Message length */ +#define CMD_QUERY_NUM_MAX 9 +#define MSG_COORD_NUM_MAX 12 + +/* Commands */ +#define CMD_QUERY 0x2a + +struct wacom_w9000_variant { + const u8 cmd_query_num; + const u8 msg_coord_num; + const char *name; +}; + +struct wacom_w9000_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct wacom_w9000_variant *variant; + u16 fw_version; + + struct touchscreen_properties prop; + u16 max_pressure; + + struct regulator *regulator; + + struct gpio_desc *flash_mode_gpio; + struct gpio_desc *reset_gpio; + + unsigned int irq; + + bool pen_proximity; +}; + +static int wacom_w9000_read(struct i2c_client *client, u8 command, u8 len, u8 *data) +{ + int error, res; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = &command, + .len = sizeof(command), + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = data, + .len = len, + } + }; + + res = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (res != ARRAY_SIZE(msg)) { + error = res < 0 ? res : -EIO; + dev_err(&client->dev, "%s: i2c transfer failed: %d (%d)\n", + __func__, error, res); + return error; + } + + return 0; +} + +static int wacom_w9000_query(struct wacom_w9000_data *wacom_data) +{ + struct i2c_client *client = wacom_data->client; + struct device *dev = &wacom_data->client->dev; + u8 data[CMD_QUERY_NUM_MAX]; + int retry; + int error; + + for (retry = 0; retry < CMD_QUERY_RETRIES; retry++) { + error = wacom_w9000_read(client, CMD_QUERY, + wacom_data->variant->cmd_query_num, + data); + if (!error && data[0] == 0x0f) + break; + } + + if (error) + return error; + + if (data[0] != 0x0f) + return -EIO; + + dev_dbg(dev, "query: %*ph, %d\n", + wacom_data->variant->cmd_query_num, data, retry); + + wacom_data->prop.max_x = get_unaligned_be16(&data[1]); + wacom_data->prop.max_y = get_unaligned_be16(&data[3]); + wacom_data->max_pressure = get_unaligned_be16(&data[5]); + wacom_data->fw_version = get_unaligned_be16(&data[7]); + + dev_dbg(dev, "max_x:%d, max_y:%d, max_pressure:%d, fw:%#x\n", + wacom_data->prop.max_x, wacom_data->prop.max_y, + wacom_data->max_pressure, wacom_data->fw_version); + + return 0; +} + +static int wacom_w9000_power_on(struct wacom_w9000_data *wacom_data) +{ + int error; + + lockdep_assert_held(&wacom_data->input_dev->mutex); + + error = regulator_enable(wacom_data->regulator); + if (error) { + dev_err(&wacom_data->client->dev, "Failed to enable regulators: %d\n", error); + return error; + } + + msleep(200); + + gpiod_set_value_cansleep(wacom_data->reset_gpio, 0); + enable_irq(wacom_data->irq); + + return 0; +} + +static int wacom_w9000_power_off(struct wacom_w9000_data *wacom_data) +{ + lockdep_assert_held(&wacom_data->input_dev->mutex); + + disable_irq(wacom_data->irq); + gpiod_set_value_cansleep(wacom_data->reset_gpio, 1); + regulator_disable(wacom_data->regulator); + + return 0; +} + +static void wacom_w9000_coord(struct wacom_w9000_data *wacom_data) +{ + struct i2c_client *client = wacom_data->client; + struct device *dev = &wacom_data->client->dev; + u8 data[MSG_COORD_NUM_MAX]; + bool touch, rubber, side_button; + u16 x, y, pressure; + u8 distance = 0; + int error; + + error = i2c_master_recv(client, data, wacom_data->variant->msg_coord_num); + if (error != wacom_data->variant->msg_coord_num) { + if (error >= 0) + error = -EIO; + dev_err_ratelimited(dev, "%s: i2c receive failed (%d)\n", __func__, error); + return; + } + + dev_dbg(dev, "data: %*ph\n", wacom_data->variant->msg_coord_num, data); + + if (data[0] & BIT(7)) { + wacom_data->pen_proximity = true; + + touch = !!(data[0] & BIT(4)); + side_button = !!(data[0] & BIT(5)); + rubber = !!(data[0] & BIT(6)); + + x = get_unaligned_be16(&data[1]); + y = get_unaligned_be16(&data[3]); + pressure = get_unaligned_be16(&data[5]); + + if (wacom_data->variant->msg_coord_num > 7) + distance = data[7]; + + if (x > wacom_data->prop.max_x || y > wacom_data->prop.max_y) { + dev_warn_ratelimited(dev, "Coordinates out of range x=%d, y=%d\n", x, y); + return; + } + + if (pressure > wacom_data->max_pressure) { + dev_warn_ratelimited(dev, "Pressure out of range %d\n", pressure); + return; + } + + touchscreen_report_pos(wacom_data->input_dev, &wacom_data->prop, x, y, false); + input_report_abs(wacom_data->input_dev, ABS_PRESSURE, pressure); + + if (wacom_data->variant->msg_coord_num > 7) + input_report_abs(wacom_data->input_dev, ABS_DISTANCE, distance); + + input_report_key(wacom_data->input_dev, BTN_STYLUS, side_button); + input_report_key(wacom_data->input_dev, BTN_TOUCH, touch); + input_report_key(wacom_data->input_dev, BTN_TOOL_PEN, !rubber); + input_report_key(wacom_data->input_dev, BTN_TOOL_RUBBER, rubber); + input_sync(wacom_data->input_dev); + } else if (wacom_data->pen_proximity) { + input_report_abs(wacom_data->input_dev, ABS_PRESSURE, 0); + + if (wacom_data->variant->msg_coord_num > 7) + input_report_abs(wacom_data->input_dev, ABS_DISTANCE, 255); + + input_report_key(wacom_data->input_dev, BTN_STYLUS, 0); + input_report_key(wacom_data->input_dev, BTN_TOUCH, 0); + input_report_key(wacom_data->input_dev, BTN_TOOL_PEN, 0); + input_report_key(wacom_data->input_dev, BTN_TOOL_RUBBER, 0); + input_sync(wacom_data->input_dev); + + wacom_data->pen_proximity = false; + } +} + +static irqreturn_t wacom_w9000_interrupt(int irq, void *dev_id) +{ + struct wacom_w9000_data *wacom_data = dev_id; + + wacom_w9000_coord(wacom_data); + + return IRQ_HANDLED; +} + +static int wacom_w9000_open(struct input_dev *dev) +{ + struct wacom_w9000_data *wacom_data = input_get_drvdata(dev); + + return wacom_w9000_power_on(wacom_data); +} + +static void wacom_w9000_close(struct input_dev *dev) +{ + struct wacom_w9000_data *wacom_data = input_get_drvdata(dev); + + wacom_w9000_power_off(wacom_data); +} + +static int wacom_w9000_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct wacom_w9000_data *wacom_data; + struct input_dev *input_dev; + int error; + u32 val; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "i2c_check_functionality error\n"); + return -EIO; + } + + wacom_data = devm_kzalloc(dev, sizeof(*wacom_data), GFP_KERNEL); + if (!wacom_data) + return -ENOMEM; + + wacom_data->variant = i2c_get_match_data(client); + if (!wacom_data->variant) { + dev_err(dev, "No i2c match_data available\n"); + return -EINVAL; + } + + if (wacom_data->variant->cmd_query_num > CMD_QUERY_NUM_MAX || + wacom_data->variant->msg_coord_num > MSG_COORD_NUM_MAX) { + dev_err(dev, "Length of message for %s exceeds the maximum\n", + wacom_data->variant->name); + return -EINVAL; + } + + if (wacom_data->variant->msg_coord_num < 7) { + dev_err(dev, "Length of coordinates message for %s too short\n", + wacom_data->variant->name); + return -EINVAL; + } + + wacom_data->client = client; + wacom_data->irq = client->irq; + i2c_set_clientdata(client, wacom_data); + + wacom_data->regulator = devm_regulator_get(dev, "vdd"); + if (IS_ERR(wacom_data->regulator)) + return dev_err_probe(dev, PTR_ERR(wacom_data->regulator), + "Failed to get regulators\n"); + + wacom_data->flash_mode_gpio = devm_gpiod_get_optional(dev, "flash-mode", GPIOD_OUT_LOW); + if (IS_ERR(wacom_data->flash_mode_gpio)) + return dev_err_probe(dev, PTR_ERR(wacom_data->flash_mode_gpio), + "Failed to get flash-mode gpio\n"); + + wacom_data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(wacom_data->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wacom_data->reset_gpio), + "Failed to get reset gpio\n"); + + error = regulator_enable(wacom_data->regulator); + if (error) + return dev_err_probe(dev, error, "Failed to enable regulators\n"); + + msleep(200); + + gpiod_set_value_cansleep(wacom_data->reset_gpio, 0); + + error = wacom_w9000_query(wacom_data); + + gpiod_set_value_cansleep(wacom_data->reset_gpio, 1); + regulator_disable(wacom_data->regulator); + + if (error) + return dev_err_probe(dev, error, "Failed to query\n"); + + error = devm_request_threaded_irq(dev, wacom_data->irq, NULL, wacom_w9000_interrupt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, client->name, wacom_data); + if (error) + return dev_err_probe(dev, error, "Failed to register interrupt\n"); + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return -ENOMEM; + + wacom_data->input_dev = input_dev; + input_set_drvdata(input_dev, wacom_data); + + input_dev->name = wacom_data->variant->name; + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x56a; + input_dev->id.version = wacom_data->fw_version; + input_dev->open = wacom_w9000_open; + input_dev->close = wacom_w9000_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_capability(input_dev, EV_KEY, BTN_TOOL_PEN); + input_set_capability(input_dev, EV_KEY, BTN_TOOL_RUBBER); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + + input_set_abs_params(input_dev, ABS_X, 0, wacom_data->prop.max_x, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, wacom_data->prop.max_y, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_data->max_pressure, 0, 0); + + if (wacom_data->variant->msg_coord_num > 7) + input_set_abs_params(input_dev, ABS_DISTANCE, 0, 255, 0, 0); + + touchscreen_parse_properties(input_dev, false, &wacom_data->prop); + + dev_info(dev, "%s size X%uY%u\n", wacom_data->variant->name, + wacom_data->prop.max_x, wacom_data->prop.max_y); + + error = device_property_read_u32(dev, "touchscreen-x-mm", &val); + if (!error && val) + input_abs_set_res(input_dev, wacom_data->prop.swap_x_y ? ABS_Y : ABS_X, + wacom_data->prop.max_x / val); + error = device_property_read_u32(dev, "touchscreen-y-mm", &val); + if (!error && val) + input_abs_set_res(input_dev, wacom_data->prop.swap_x_y ? ABS_X : ABS_Y, + wacom_data->prop.max_y / val); + + error = input_register_device(wacom_data->input_dev); + if (error) + return dev_err_probe(dev, error, "Failed to register input device\n"); + + return 0; +} + +static int wacom_w9000_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct wacom_w9000_data *wacom_data = i2c_get_clientdata(client); + + guard(mutex)(&wacom_data->input_dev->mutex); + + if (input_device_enabled(wacom_data->input_dev)) + return wacom_w9000_power_off(wacom_data); + + return 0; +} + +static int wacom_w9000_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct wacom_w9000_data *wacom_data = i2c_get_clientdata(client); + + guard(mutex)(&wacom_data->input_dev->mutex); + + if (input_device_enabled(wacom_data->input_dev)) + return wacom_w9000_power_on(wacom_data); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(wacom_w9000_pm, wacom_w9000_suspend, wacom_w9000_resume); + +static const struct wacom_w9000_variant w9002 = { + .cmd_query_num = 9, + .msg_coord_num = 7, + .name = "Wacom W9002 Digitizer", +}; + +static const struct wacom_w9000_variant w9007a_lt03 = { + .cmd_query_num = 9, + .msg_coord_num = 8, + .name = "Wacom W9007A LT03 Digitizer", +}; + +static const struct wacom_w9000_variant w9007a_v1 = { + .cmd_query_num = 9, + .msg_coord_num = 12, + .name = "Wacom W9007A V1 Digitizer", +}; + +static const struct of_device_id wacom_w9000_of_match[] = { + { .compatible = "wacom,w9002", .data = &w9002 }, + { .compatible = "wacom,w9007a-lt03", .data = &w9007a_lt03, }, + { .compatible = "wacom,w9007a-v1", .data = &w9007a_v1, }, + { } +}; +MODULE_DEVICE_TABLE(of, wacom_w9000_of_match); + +static const struct i2c_device_id wacom_w9000_id[] = { + { .name = "w9002", .driver_data = (kernel_ulong_t)&w9002 }, + { .name = "w9007a-lt03", .driver_data = (kernel_ulong_t)&w9007a_lt03 }, + { .name = "w9007a-v1", .driver_data = (kernel_ulong_t)&w9007a_v1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wacom_w9000_id); + +static struct i2c_driver wacom_w9000_driver = { + .driver = { + .name = "wacom_w9000", + .of_match_table = wacom_w9000_of_match, + .pm = pm_sleep_ptr(&wacom_w9000_pm), + }, + .probe = wacom_w9000_probe, + .id_table = wacom_w9000_id, +}; +module_i2c_driver(wacom_w9000_driver); + +MODULE_AUTHOR("Hendrik Noack "); +MODULE_DESCRIPTION("Wacom W9000-series penabled touchscreen driver"); +MODULE_LICENSE("GPL"); From 3eb9ba0da0ab73e135f93ffccf07381ba11f100e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 7 Apr 2026 01:15:52 +0300 Subject: [PATCH 2223/5214] Revert "media: venus: hfi_platform: Correct supported codecs for sc7280" This reverts commit c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280"). The codecs might be deprecated, but they still work (somewhat) perfectly and don't cause any issues with the rest of the system. Reenable VP8 codecs by reverting the offending commit. Tested with fluster: |Test|FFmpeg-VP8-v4l2m2m|GStreamer-VP8-V4L2| |TOTAL|50/61|50/61| |TOTAL TIME|12.171s|11.824s| Fixes: c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280") Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/venus/hfi_parser.c | 6 ++--- .../media/platform/qcom/venus/hfi_platform.c | 24 ------------------- .../media/platform/qcom/venus/hfi_platform.h | 2 -- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 92765f9c8873..c4cf6cd50a9a 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); - if (ret) - return ret; + if (plat->codecs) + plat->codecs(core, &enc_codecs, &dec_codecs, &count); if (plat->capabilities) caps = plat->capabilities(core, &entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index cde7f93045ac..f19572ab1d16 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,9 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include #include "hfi_platform.h" -#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core, return freq; } - -int -hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count) -{ - const struct hfi_platform *plat; - - plat = hfi_platform_get(core->res->hfi_version); - if (!plat) - return -EINVAL; - - if (plat->codecs) - plat->codecs(core, enc_codecs, dec_codecs, count); - - if (IS_IRIS2_1(core)) { - *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; - *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; - } - - return 0; -} - diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5e4f8013a6b1..a0b6d19f3e1a 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core, unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core, enum hfi_version version, u32 codec, u32 session_type); -int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count); #endif From 631cc1c72475bcf8365fc1772699b54249ca01e1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 May 2026 14:59:25 +0300 Subject: [PATCH 2224/5214] media: dt-bindings: qcom,qcm2290-venus: add Venus on SM6115 The Qualcomm SM6115 platform contains the AR50_Lite core similar to the one found on the QCM2290. Define new platform-specific compatible, while using QCM2290 as a fallback. Signed-off-by: Dmitry Baryshkov [bod: Fixed indentation of compats] Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,qcm2290-venus.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml index 7e6dc410c2d2..5977e7d0a71b 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml @@ -18,7 +18,11 @@ allOf: properties: compatible: - const: qcom,qcm2290-venus + oneOf: + - items: + - const: qcom,sm6115-venus + - const: qcom,qcm2290-venus + - const: qcom,qcm2290-venus power-domains: maxItems: 3 From e49686fff1f11ed3085b7b9b56a20ba0ebd9a509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 26 May 2026 16:24:24 +0300 Subject: [PATCH 2225/5214] media: dt-bindings: venus: Add qcom,msm8939 schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a schema description for the Venus video decoder/encoder IP in MSM8939. Signed-off-by: André Apitzsch Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Erikas Bitovtas Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,msm8939-venus.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml new file mode 100644 index 000000000000..10a50a410748 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,msm8939-venus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8939 Venus video encode and decode accelerators + +maintainers: + - André Apitzsch + - Erikas Bitovtas + +description: + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +allOf: + - $ref: qcom,venus-common.yaml# + +properties: + compatible: + const: qcom,msm8939-venus + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: vcodec1 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec1_core + + iommus: + maxItems: 1 + +required: + - compatible + - iommus + - power-domain-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + + video-codec@1d00000 { + compatible = "qcom,msm8939-venus"; + reg = <0x01d00000 0xff000>; + interrupts = ; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>, + <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>; + clock-names = "core", + "iface", + "bus", + "vcodec0_core", + "vcodec1_core"; + power-domains = <&gcc VENUS_GDSC>, + <&gcc VENUS_CORE0_GDSC>, + <&gcc VENUS_CORE1_GDSC>; + power-domain-names = "venus", "vcodec0", "vcodec1"; + iommus = <&apps_iommu 5>; + memory-region = <&venus_mem>; + }; From e1c9adabb268cc5d56723b7df1da49e59070f309 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:07 +0530 Subject: [PATCH 2226/5214] media: qcom: venus: drop extra padding in NV12 raw size calculation get_framesize_raw_nv12() currently adds SZ_4K to the UV plane size and an additional SZ_8K to the total buffer size. This inflates the calculated sizeimage and leads userspace to over-allocate buffers without a clear requirement. Remove the extra SZ_4K/SZ_8K padding and compute the NV12 size as the sum of Y and UV planes, keeping the final ALIGN(size, SZ_4K) intact. Fixes: e1cb72de702ad ("media: venus: helpers: move frame size calculations on common place") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 747c388fe25f..59eee3dd9e06 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height) uv_sclines = ALIGN(((height + 1) >> 1), 16); y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; + uv_plane = uv_stride * uv_sclines; + size = y_plane + uv_plane; return ALIGN(size, SZ_4K); } From 35428ae3a6a40912f1f7b47bb6c65f1a63d0b8f8 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:08 +0530 Subject: [PATCH 2227/5214] media: qcom: venus: relax encoder frame/blur dimension steps on v4 Encoder HFI capabilities on v4 advertise a 16-pixel step for frame and blur dimensions. This is overly restrictive and can cause userspace caps negotiation to fail even for valid resolutions. Relax the advertised step size to 1 and keep alignment enforcement in buffer layout and size calculations. Fixes: 8b88cabef404e ("media: venus: hfi_plat_v4: Populate codecs and capabilities for v4") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/venus/hfi_platform_v4.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index cda888b56b5d..e0b3652bb440 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From c53e0550288b2e08b984b24035c471941b7820c7 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:09 +0530 Subject: [PATCH 2228/5214] media: qcom: venus: relax encoder frame/blur step size on v6 Encoder HFI capabilities on v6 enforce a 16-pixel step for frame and blur dimensions, which does not reflect actual hardware requirements and can reject valid userspace configurations. Relax the step size to 1 while leaving min/max limits unchanged. Fixes: 869d77e706290 ("media: venus: hfi_plat_v6: Populate capabilities for v6") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/venus/hfi_platform_v6.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index d8568c08cc36..fb8d10ab3404 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From 86a366c02ddbee289d0f04c63a3ebd940404cd07 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 26 May 2026 16:24:26 +0300 Subject: [PATCH 2229/5214] media: qcom: venus: add power domain enable logic for Venus cores Attach power domains for vdec and venc cores and power them up if a vdec or venc session is started. Vcodec clocks are added and enabled to the core Venus device both for vcodec0 and vcodec1. To ensure they are added only once, introduce a new property "vcodec_clks", which is an array of clocks which are enabled both during decode and encode and is retrieved from the device tree only once. Reviewed-by: Bryan O'Donoghue Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.h | 3 + .../media/platform/qcom/venus/pm_helpers.c | 150 +++++++++++++++++- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 03804c30808e..c1603eebd757 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -79,6 +79,7 @@ struct venus_resources { const struct hfi_ubwc_config *ubwc_conf; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; unsigned int vcodec_clks_num; @@ -143,6 +144,7 @@ struct venus_format { * @aon_base: AON base address * @irq: Venus irq * @clks: an array of struct clk pointers + * @vcodec_clks: an array of vcodec struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path @@ -197,6 +199,7 @@ struct venus_core { void __iomem *aon_base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; + struct clk *vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 14a4e8311a64..be1cbd5cfe84 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -89,12 +89,23 @@ static void core_clks_disable(struct venus_core *core) static int core_clks_set_rate(struct venus_core *core, unsigned long freq) { - int ret; + const struct venus_resources *res = core->res; + int ret, i; ret = dev_pm_opp_set_rate(core->dev, freq); if (ret) return ret; + if (!res->vcodec_clks_num) + goto set_rates; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_set_rate(core->vcodec_clks[i], freq); + if (ret) + return ret; + } + +set_rates: ret = clk_set_rate(core->vcodec0_clks[0], freq); if (ret) return ret; @@ -297,10 +308,33 @@ static int load_scale_v1(struct venus_inst *inst) return ret; } +static int vcodec_domains_get_v1(struct venus_core *core) +{ + struct device *dev = core->dev; + const struct venus_resources *res = core->res; + const struct dev_pm_domain_attach_data vcodec_data = { + .pd_names = res->vcodec_pmdomains, + .num_pd_names = res->vcodec_pmdomains_num, + .pd_flags = PD_FLAG_NO_DEV_LINK, + }; + + if (!res->vcodec_pmdomains) + return 0; + + return devm_pm_domain_attach_list(dev, &vcodec_data, + &core->pmdomains); +} + static int core_get_v1(struct venus_core *core) { + const struct venus_resources *res = core->res; + struct device *dev = core->dev; int ret; + ret = vcodec_domains_get_v1(core); + if (ret < 0) + return ret; + ret = core_clks_get(core); if (ret) return ret; @@ -309,9 +343,79 @@ static int core_get_v1(struct venus_core *core) if (ret) return ret; + if (!res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_get(core, dev, core->vcodec_clks, + res->vcodec_clks); + if (ret) + return ret; + return 0; } +static int vcodec_domains_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + if (!res->vcodec_pmdomains) + return 0; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = pm_runtime_resume_and_get(pd_dev); + if (ret) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } + return ret; +} + +static void vcodec_domains_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = res->vcodec_pmdomains_num; + + if (!res->vcodec_pmdomains) + return; + + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } +} + +static int vcodec_domains_set_hw(struct venus_core *core, bool is_hw) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = dev_pm_genpd_set_hwmode(pd_dev, is_hw); + if (ret && ret != -EOPNOTSUPP) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + dev_pm_genpd_set_hwmode(pd_dev, !is_hw); + } + return ret; +} + static void core_put_v1(struct venus_core *core) { } @@ -320,11 +424,43 @@ static int core_power_v1(struct venus_core *core, int on) { int ret = 0; - if (on == POWER_ON) - ret = core_clks_enable(core); - else - core_clks_disable(core); + if (on == POWER_ON) { + ret = vcodec_domains_enable(core); + if (ret) + return ret; + ret = core_clks_enable(core); + if (ret) + goto fail_pmdomains; + + if (!core->res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_enable(core, core->vcodec_clks); + if (ret) + goto fail_core_clks; + + ret = vcodec_domains_set_hw(core, true); + if (ret) + goto fail_vcodec_clks; + + } else { + if (core->res->vcodec_pmdomains) { + vcodec_domains_set_hw(core, false); + vcodec_clks_disable(core, core->vcodec_clks); + } + core_clks_disable(core); + vcodec_domains_disable(core); + } + + return 0; + +fail_vcodec_clks: + vcodec_clks_disable(core, core->vcodec_clks); +fail_core_clks: + core_clks_disable(core); +fail_pmdomains: + vcodec_domains_disable(core); return ret; } @@ -875,7 +1011,7 @@ static int venc_power_v4(struct device *dev, int on) return ret; } -static int vcodec_domains_get(struct venus_core *core) +static int vcodec_domains_get_v4(struct venus_core *core) { int ret; struct device *dev = core->dev; @@ -999,7 +1135,7 @@ static int core_get_v4(struct venus_core *core) if (ret) return ret; - ret = vcodec_domains_get(core); + ret = vcodec_domains_get_v4(core); if (ret) return ret; From cc41341d1209169d7928794ae90386c17827cc76 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 26 May 2026 16:24:27 +0300 Subject: [PATCH 2230/5214] media: qcom: venus: add codec blacklist mechanism Add decode and encode blacklist properties to allow disabling different codecs per Venus device, instead of doing it per HFI version. Reviewed-by: Bryan O'Donoghue Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 5 +++++ drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/hfi_parser.c | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index a87e8afb23df..bd88e2b484a5 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,8 @@ static void venus_sys_error_handler(struct work_struct *work) static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { + case HFI_VIDEO_CODEC_HEVC: + return V4L2_PIX_FMT_HEVC; case HFI_VIDEO_CODEC_H264: return V4L2_PIX_FMT_H264; case HFI_VIDEO_CODEC_H263: @@ -684,6 +687,8 @@ static const struct venus_resources msm8916_res = { .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_HEVC | HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index c1603eebd757..46705a666776 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -88,6 +88,8 @@ struct venus_resources { const char **opp_pmdomain; unsigned int opp_pmdomain_num; unsigned int vcodec_num; + const u32 dec_codec_blacklist; + const u32 enc_codec_blacklist; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; enum hfi_version hfi_version; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index c4cf6cd50a9a..b1657443f23f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -206,11 +206,11 @@ static int parse_codecs(struct venus_core *core, void *data) core->dec_codecs = codecs->dec_codecs; core->enc_codecs = codecs->enc_codecs; - if (IS_V1(core)) { - core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; - core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; - core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; - } + if (core->res->dec_codec_blacklist) + core->dec_codecs &= ~core->res->dec_codec_blacklist; + + if (core->res->enc_codec_blacklist) + core->enc_codecs &= ~core->res->enc_codec_blacklist; return sizeof(*codecs); } From 197789c60a11eaf18b4a2e5d2dbf8d490e10395e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 26 May 2026 16:24:28 +0300 Subject: [PATCH 2231/5214] media: qcom: venus: Add msm8939 resource struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add msm8939 configuration data and related compatible. Reviewed-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Signed-off-by: André Apitzsch Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index bd88e2b484a5..243e342b0ae7 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -693,6 +693,45 @@ static const struct venus_resources msm8916_res = { .enc_nodename = "video-encoder", }; +static const struct freq_tbl msm8939_freq_table[] = { + { 489600, 266670000 }, /* 1080p @ 60 */ + { 244800, 133330000 }, /* 1080p @ 30 */ + { 220800, 133330000 }, /* 720p @ 60 */ + { 108000, 133330000 }, /* 720p @ 30 */ + { 72000, 133330000 }, /* VGA @ 60 */ + { 36000, 133330000 }, /* VGA @ 30 */ +}; + +static const struct reg_val msm8939_reg_preset[] = { + { 0xe0020, 0x0aaaaaaa }, + { 0xe0024, 0x0aaaaaaa }, + { 0x80124, 0x00000003 }, +}; + +static const struct venus_resources msm8939_res = { + .freq_tbl = msm8939_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8939_freq_table), + .reg_tbl = msm8939_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset), + .clks = { "core", "iface", "bus", }, + .clks_num = 3, + .vcodec_clks = { "vcodec0_core", "vcodec1_core" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .max_load = 489600, /* 1080p@30 + 1080p@30 */ + .hfi_version = HFI_VERSION_1XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", +}; + static const struct freq_tbl msm8996_freq_table[] = { { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */ { 972000, 520000000 }, /* 4k UHD @ 30 */ @@ -1133,6 +1172,7 @@ static const struct venus_resources qcm2290_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, + { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, From 45ac2230f929b9f0fae899c4a5c6bf763250fc13 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 29 May 2026 14:27:10 +0300 Subject: [PATCH 2232/5214] media: iris: Fix use IRQF_NO_AUTOEN when requesting the IRQ Requesting the IRQ and then immediately disabling it is fragile as it leaves a window when the IRQ is still enabled although the underlying device might be not completely setup for IRQ handling. Pass IRQF_NO_AUTOEN instead of calling disable_irq_nosync(). Fixes: fb583a214337 ("media: iris: introduce host firmware interface with necessary hooks") Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov [bod: Appended Fix to patch title for -stable clarity] [bod: Added cc stable for backporting] Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index d36f0c0e785b..a755a6f73ea6 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -259,12 +259,12 @@ static int iris_probe(struct platform_device *pdev) return PTR_ERR(core->ubwc_cfg); ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, - iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); + iris_hfi_isr_handler, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "iris", core); if (ret) return ret; - disable_irq_nosync(core->irq); - iris_init_ops(core); core->iris_firmware_data->init_hfi_ops(core); From 96789be88f987d733a07b37279d4ebc8efd6207a Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:34:58 +0800 Subject: [PATCH 2233/5214] media: dt-bindings: qcom,sm8550-iris: Add X1P42100 compatible Document the new compatible string "qcom,x1p42100-iris". Unlike SM8550 where the BSE (Bitstream Engine) is clocked implicitly via vcodec0_core, x1p42100 exposes a dedicated BSE clock vcodec0_bse that requires explicit enable/disable and frequency configuration. The SM8550 driver has no knowledge of this clock and therefore cannot operate x1p42100 hardware correctly. Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sm8550-iris.yaml | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index 9c4b760508b5..0400ca1bff05 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -26,6 +26,7 @@ properties: - qcom,qcs8300-iris - qcom,sm8550-iris - qcom,sm8650-iris + - qcom,x1p42100-iris reg: maxItems: 1 @@ -41,13 +42,16 @@ properties: - const: mmcx clocks: - maxItems: 3 + minItems: 3 + maxItems: 4 clock-names: + minItems: 3 items: - const: iface - const: core - const: vcodec0_core + - const: vcodec0_bse firmware-name: maxItems: 1 @@ -115,6 +119,23 @@ allOf: maxItems: 1 reset-names: maxItems: 1 + - if: + properties: + compatible: + enum: + - qcom,x1p42100-iris + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + else: + properties: + clocks: + maxItems: 3 + clock-names: + maxItems: 3 unevaluatedProperties: false From 58cbdcfb3533f30952ee44e4244ff358fbff9656 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 May 2026 13:56:35 +0300 Subject: [PATCH 2234/5214] media: dt-bindings: Document SC8280XP/SM8350 Iris The Iris block on SM8350 and SC8280XP is compatible with the Iris (Venus) on SM8250. Describing in the bindings that the block is Iris v2 and not Venus. Document SM8350 and SC8280XP IP cores, using qcom,sm8250-venus as a fallback compatible. Signed-off-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sm8250-venus.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml index 43a10d9f664e..aca748e42aca 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml @@ -10,15 +10,21 @@ maintainers: - Stanimir Varbanov description: | - The Venus IP is a video encode and decode accelerator present - on Qualcomm platforms + The Iris v2.xx IP is a video encode and decode accelerator present on + Qualcomm platforms allOf: - $ref: qcom,venus-common.yaml# properties: compatible: - const: qcom,sm8250-venus + oneOf: + - const: qcom,sm8250-venus + - items: + - enum: + - qcom,sc8280xp-iris + - qcom,sm8350-iris + - const: qcom,sm8250-venus power-domains: minItems: 2 From 897972882725db9d0d417e168b0f756ddf348a23 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:10 +0800 Subject: [PATCH 2235/5214] media: qcom: iris: Add intra refresh support for gen1 encoder Add support for intra refresh configuration on gen1 encoder by enabling V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD and V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE controls. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 39 ++++++++++++++++++- drivers/media/platform/qcom/iris/iris_ctrls.h | 3 +- .../media/platform/qcom/iris/iris_hfi_gen1.c | 19 +++++++++ .../qcom/iris/iris_hfi_gen1_command.c | 8 ++++ .../qcom/iris/iris_hfi_gen1_defines.h | 13 +++++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 2 +- 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index ef7adac3764d..287cfc532941 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -970,7 +970,44 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) &hfi_val, sizeof(u32)); } -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct v4l2_pix_format_mplane *fmt = &inst->fmt_dst->fmt.pix_mp; + u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + u32 ir_period = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_refresh hfi_val; + + if (!ir_period) + return -EINVAL; + + if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) { + hfi_val.mode = HFI_INTRA_REFRESH_RANDOM; + } else if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) { + hfi_val.mode = HFI_INTRA_REFRESH_CYCLIC; + } else { + return -EINVAL; + } + + /* + * Calculate the number of macroblocks in a frame, + * then determine how many macroblocks need to be + * refreshed within one ir_period. + */ + hfi_val.mbs = (fmt->width / codec_align) * (fmt->height / codec_align); + hfi_val.mbs /= ir_period; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 9518803577bc..a0d5338bdc91 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -34,7 +34,8 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 60f51a1ba941..9344d20042fd 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -224,6 +224,25 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_qp_range, }, + { + .cap_id = IR_TYPE, + .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, + .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) | + BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC), + .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = IR_PERIOD, + .min = 0, + .max = ((4096 * 2304) >> 8), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ir_period_gen1, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 83373862655f..051ba0d157c7 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -687,6 +687,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info); break; } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + struct hfi_intra_refresh *in = pdata, *intra_refresh = prop_data; + + intra_refresh->mode = in->mode; + intra_refresh->mbs = in->mbs; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 42226ccee3d9..04c79ee0463d 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -139,6 +139,14 @@ #define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003 #define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004 #define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009 + +#define HFI_INTRA_REFRESH_NONE 0x1 +#define HFI_INTRA_REFRESH_CYCLIC 0x2 +#define HFI_INTRA_REFRESH_ADAPTIVE 0x3 +#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4 +#define HFI_INTRA_REFRESH_RANDOM 0x5 + +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 @@ -447,6 +455,11 @@ struct hfi_framerate { u32 framerate; }; +struct hfi_intra_refresh { + u32 mode; + u32 mbs; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index ce8490d64854..401519fef0e2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -713,7 +713,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .value = 0, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_ir_period, + .set = iris_set_ir_period_gen2, }, }; From 6870381285b8b8161235d54456a7c4fe0e94f32e Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:11 +0800 Subject: [PATCH 2236/5214] media: qcom: iris: Add Long-Term Reference support for encoder Add Long-Term Reference(LTR) frame support for both gen1 and gen2 encoders by enabling the following V4L2 controls: V4L2_CID_MPEG_VIDEO_LTR_COUNT V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX Tested-by: Neil Armstrong # on SM8650-HDK Reviewed-by: Dikshita Agarwal Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 128 ++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 5 + .../media/platform/qcom/iris/iris_hfi_gen1.c | 30 ++++ .../qcom/iris/iris_hfi_gen1_command.c | 25 ++++ .../qcom/iris/iris_hfi_gen1_defines.h | 24 ++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 30 ++++ .../qcom/iris/iris_hfi_gen2_defines.h | 3 + .../platform/qcom/iris/iris_platform_common.h | 6 + .../platform/qcom/iris/iris_vpu_buffer.c | 20 ++- 9 files changed, 267 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 287cfc532941..87d10ce1a9a5 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -112,6 +112,12 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return IR_TYPE; case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return IR_PERIOD; + case V4L2_CID_MPEG_VIDEO_LTR_COUNT: + return LTR_COUNT; + case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: + return USE_LTR; + case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: + return MARK_LTR; default: return INST_FW_CAP_MAX; } @@ -213,6 +219,12 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE; case IR_PERIOD: return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD; + case LTR_COUNT: + return V4L2_CID_MPEG_VIDEO_LTR_COUNT; + case USE_LTR: + return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES; + case MARK_LTR: + return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; default: return 0; } @@ -1033,6 +1045,122 @@ int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ty &ir_period, sizeof(u32)); } +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mode ltr_mode; + + if (!ltr_count) + return -EINVAL; + + ltr_mode.count = ltr_count; + ltr_mode.mode = HFI_LTR_MODE_MANUAL; + ltr_mode.trust_mode = 1; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mode, sizeof(ltr_mode)); +} + +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_use ltr_use; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_use.ref_ltr = inst->fw_caps[cap_id].value; + ltr_use.use_constrnt = true; + ltr_use.frames = 0; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_use, sizeof(ltr_use)); +} + +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mark ltr_mark; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_mark.mark_frame = inst->fw_caps[cap_id].value; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mark, sizeof(ltr_mark)); +} + +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!ltr_count) + return -EINVAL; + + if (inst->hfi_rc_type == HFI_RC_CBR_VFR || + inst->hfi_rc_type == HFI_RC_CBR_CFR || + inst->hfi_rc_type == HFI_RC_OFF) { + inst->fw_caps[LTR_COUNT].value = 0; + return -EINVAL; + } + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + <r_count, sizeof(u32)); +} + +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_val = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count || hfi_val == INVALID_DEFAULT_MARK_OR_USE_LTR) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &hfi_val, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index a0d5338bdc91..996c83fdc6f4 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -36,6 +36,11 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 9344d20042fd..6db693a602ac 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -243,6 +243,36 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_ir_period_gen1, }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_LTRMODE, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen1, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN1) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_ltr, + }, + { + .cap_id = MARK_LTR, + .min = 0, + .max = (MAX_LTR_FRAME_COUNT_GEN1 - 1), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_mark_ltr, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 051ba0d157c7..a441c897aaab 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -695,6 +695,31 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); break; } + case HFI_PROPERTY_PARAM_VENC_LTRMODE: { + struct hfi_ltr_mode *in = pdata, *ltr_mode = prop_data; + + ltr_mode->mode = in->mode; + ltr_mode->count = in->count; + ltr_mode->trust_mode = in->trust_mode; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mode); + break; + } + case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { + struct hfi_ltr_use *in = pdata, *ltr_use = prop_data; + + ltr_use->frames = in->frames; + ltr_use->ref_ltr = in->ref_ltr; + ltr_use->use_constrnt = in->use_constrnt; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { + struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; + + ltr_mark->mark_frame = in->mark_frame; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 04c79ee0463d..34249fc0d047 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -147,8 +147,16 @@ #define HFI_INTRA_REFRESH_RANDOM 0x5 #define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 +#define HFI_LTR_MODE_PERIODIC 0x2 + +#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 struct hfi_pkt_hdr { @@ -460,6 +468,22 @@ struct hfi_intra_refresh { u32 mbs; }; +struct hfi_ltr_mode { + u32 mode; + u32 count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 401519fef0e2..495327160ec2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -715,6 +715,36 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_ir_period_gen2, }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN2, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LTR_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen2, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROP_LTR_USE, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = MARK_LTR, + .min = INVALID_DEFAULT_MARK_OR_USE_LTR, + .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1), + .step_or_mask = 1, + .value = INVALID_DEFAULT_MARK_OR_USE_LTR, + .hfi_id = HFI_PROP_LTR_MARK, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index cecf771c55dd..aec19efc41a5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -71,6 +71,9 @@ enum hfi_rate_control { #define HFI_PROP_MIN_QP_PACKED 0x0300012f #define HFI_PROP_MAX_QP_PACKED 0x03000130 #define HFI_PROP_IR_RANDOM_PERIOD 0x03000131 +#define HFI_PROP_LTR_COUNT 0x03000134 +#define HFI_PROP_LTR_MARK 0x03000135 +#define HFI_PROP_LTR_USE 0x03000136 #define HFI_PROP_TOTAL_BITRATE 0x0300013b #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6a108173be35..2f4392e6a42e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -28,6 +28,9 @@ struct iris_inst; #define MAX_QP_HEVC 63 #define DEFAULT_QP 20 #define BITRATE_DEFAULT 20000000 +#define INVALID_DEFAULT_MARK_OR_USE_LTR -1 +#define MAX_LTR_FRAME_COUNT_GEN1 4 +#define MAX_LTR_FRAME_COUNT_GEN2 2 enum stage_type { STAGE_1 = 1, @@ -151,6 +154,9 @@ enum platform_inst_fw_cap_type { VFLIP, IR_TYPE, IR_PERIOD, + LTR_COUNT, + USE_LTR, + MARK_LTR, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 9270422c1601..891aed5091c7 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -934,6 +934,19 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) num_vpp_pipes, inst->hfi_rc_type); } +static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) +{ + u32 num_ref = 1; + u32 ltr_count; + + ltr_count = inst->fw_caps[LTR_COUNT].value; + + if (ltr_count) + num_ref = num_ref + ltr_count; + + return num_ref; +} + static u32 iris_vpu_dec_partial_size(struct iris_inst *inst) { struct v4l2_format *f = inst->fmt_src; @@ -968,7 +981,7 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) { u32 height = iris_vpu_enc_get_bitstream_height(inst); u32 width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_recon = 1; + u32 num_recon = hfi_buffer_get_recon_count(inst); u32 lcu_size = 16; if (inst->codec == V4L2_PIX_FMT_HEVC) { @@ -1677,10 +1690,9 @@ static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst) { u32 frame_height = iris_vpu_enc_get_bitstream_height(inst); u32 frame_width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_ref = 1; + u32 num_ref = hfi_buffer_get_recon_count(inst); - return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, - false); + return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, false); } static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst) From e4d067d5e991b96d86d9becc5fc9c9cfc4cacc59 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:12 +0800 Subject: [PATCH 2237/5214] media: qcom: iris: Add B frames support for encoder Add support for B-frame configuration on both gen1 and gen2 encoders by enabling V4L2_CID_MPEG_VIDEO_B_FRAMES control. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 30 +++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 1 + .../media/platform/qcom/iris/iris_hfi_gen1.c | 18 +++++++++++ .../qcom/iris/iris_hfi_gen1_command.c | 8 +++++ .../qcom/iris/iris_hfi_gen1_defines.h | 10 +++++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 10 +++++++ .../platform/qcom/iris/iris_platform_common.h | 2 ++ .../platform/qcom/iris/iris_vpu_buffer.c | 6 +++- 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 87d10ce1a9a5..a6bd2eada52b 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -118,6 +118,8 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return USE_LTR; case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: return MARK_LTR; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return B_FRAME; default: return INST_FW_CAP_MAX; } @@ -225,6 +227,8 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES; case MARK_LTR: return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; + case B_FRAME: + return V4L2_CID_MPEG_VIDEO_B_FRAMES; default: return 0; } @@ -1161,6 +1165,32 @@ int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_ &hfi_val, sizeof(u32)); } +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 gop_size = inst->fw_caps[GOP_SIZE].value; + u32 b_frame = inst->fw_caps[B_FRAME].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_period intra_period; + + if (!gop_size || b_frame >= gop_size) + return -EINVAL; + + /* + * intra_period represents the length of a GOP, which includes both P-frames + * and B-frames. The counts of P-frames and B-frames within a GOP must be + * communicated to the firmware. + */ + intra_period.pframes = (gop_size - 1) / (b_frame + 1); + intra_period.bframes = b_frame; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &intra_period, sizeof(intra_period)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 996c83fdc6f4..609258c81517 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -41,6 +41,7 @@ int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ty int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 6db693a602ac..792441463b4b 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -273,6 +273,24 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_mark_ltr, }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 3, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = INTRA_PERIOD, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_intra_period, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index a441c897aaab..05e7b5ff2b92 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -720,6 +720,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); break; } + case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { + struct hfi_intra_period *in = pdata, *intra_period = prop_data; + + intra_period->pframes = in->pframes; + intra_period->bframes = in->bframes; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 34249fc0d047..4343661e8606 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -155,6 +155,7 @@ #define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 #define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 @@ -484,6 +485,15 @@ struct hfi_ltr_mark { u32 mark_frame; }; +struct hfi_max_num_b_frames { + u32 max_num_b_frames; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 495327160ec2..27878b70e516 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -745,6 +745,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_use_and_mark_ltr, }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_MAX_B_FRAMES, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_u32, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 2f4392e6a42e..422e83ae0788 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -157,6 +157,8 @@ enum platform_inst_fw_cap_type { LTR_COUNT, USE_LTR, MARK_LTR, + B_FRAME, + INTRA_PERIOD, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 891aed5091c7..0ed82dc2b8af 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -936,11 +936,15 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) { + u32 bframe_count, ltr_count; u32 num_ref = 1; - u32 ltr_count; + bframe_count = inst->fw_caps[B_FRAME].value; ltr_count = inst->fw_caps[LTR_COUNT].value; + if (bframe_count) + num_ref = 2; + if (ltr_count) num_ref = num_ref + ltr_count; From e0507d45b3c171296248b4382271a003c6c2e058 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:13 +0800 Subject: [PATCH 2238/5214] media: qcom: iris: Add hierarchical coding support for encoder Add hierarchical coding support for both gen1 and gen2 encoders by enabling the following V4L2 controls: H264: V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING, V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE, V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER HEVC(gen2 only): V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE, V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER Reviewed-by: Vikash Garodia Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 288 +++++++++++++++++- drivers/media/platform/qcom/iris/iris_ctrls.h | 7 +- .../media/platform/qcom/iris/iris_hfi_gen1.c | 94 +++++- .../qcom/iris/iris_hfi_gen1_command.c | 21 +- .../qcom/iris/iris_hfi_gen1_defines.h | 2 + .../media/platform/qcom/iris/iris_hfi_gen2.c | 184 ++++++++++- .../qcom/iris/iris_hfi_gen2_defines.h | 15 + .../media/platform/qcom/iris/iris_instance.h | 4 + .../platform/qcom/iris/iris_platform_common.h | 23 ++ .../platform/qcom/iris/iris_vpu_buffer.c | 28 ++ 10 files changed, 658 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index a6bd2eada52b..10e33b8a73f6 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -120,6 +120,40 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return MARK_LTR; case V4L2_CID_MPEG_VIDEO_B_FRAMES: return B_FRAME; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING: + return LAYER_ENABLE; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE: + return LAYER_TYPE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + return LAYER_TYPE_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER: + return LAYER_COUNT_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: + return LAYER_COUNT_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR: + return LAYER0_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR: + return LAYER1_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR: + return LAYER2_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR: + return LAYER3_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR: + return LAYER4_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR: + return LAYER5_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: + return LAYER0_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: + return LAYER1_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: + return LAYER2_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: + return LAYER3_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: + return LAYER4_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: + return LAYER5_BITRATE_HEVC; default: return INST_FW_CAP_MAX; } @@ -229,6 +263,40 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; case B_FRAME: return V4L2_CID_MPEG_VIDEO_B_FRAMES; + case LAYER_ENABLE: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING; + case LAYER_TYPE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE; + case LAYER_TYPE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE; + case LAYER_COUNT_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER; + case LAYER_COUNT_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER; + case LAYER0_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR; + case LAYER1_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR; + case LAYER2_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR; + case LAYER3_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR; + case LAYER4_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR; + case LAYER5_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR; + case LAYER0_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR; + case LAYER1_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR; + case LAYER2_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR; + case LAYER3_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR; + case LAYER4_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR; + case LAYER5_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR; default: return 0; } @@ -575,7 +643,64 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ &hfi_val, sizeof(u32)); } -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; + u32 bitrate = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_bitrate hfi_val; + u32 max_bitrate; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET) && cap_id != BITRATE) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_HEVC) { + max_bitrate = CABAC_MAX_BITRATE; + } else { + if (entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + max_bitrate = CABAC_MAX_BITRATE; + else + max_bitrate = CAVLC_MAX_BITRATE; + } + + hfi_val.bitrate = min(bitrate, max_bitrate); + + switch (cap_id) { + case BITRATE: + case LAYER0_BITRATE_H264: + hfi_val.layer_id = 0; + break; + case LAYER1_BITRATE_H264: + hfi_val.layer_id = 1; + break; + case LAYER2_BITRATE_H264: + hfi_val.layer_id = 2; + break; + case LAYER3_BITRATE_H264: + hfi_val.layer_id = 3; + break; + case LAYER4_BITRATE_H264: + hfi_val.layer_id = 4; + break; + case LAYER5_BITRATE_H264: + hfi_val.layer_id = 5; + break; + default: + return -EINVAL; + } + + if (hfi_val.layer_id > 0 && !inst->fw_caps[LAYER_ENABLE].value) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; @@ -1191,6 +1316,167 @@ int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type &intra_period, sizeof(intra_period)); } +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type; + + if (inst->hfi_rc_type == HFI_RATE_CONTROL_CQ || + inst->hfi_rc_type == HFI_RATE_CONTROL_OFF) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_H264) { + if (!layer_enable || !inst->fw_caps[LAYER_COUNT_H264].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_P_HYBRID_LTR; + else + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else if (inst->codec == V4L2_PIX_FMT_HEVC) { + if (!inst->fw_caps[LAYER_COUNT_HEVC].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P) { + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + + inst->hfi_layer_type = layer_type; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32_ENUM, + &layer_type, sizeof(u32)); +} + +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 layer_count = inst->fw_caps[cap_id].value; + u32 hfi_id, ret; + + if (!layer_enable || !layer_count) + return -EINVAL; + + inst->hfi_layer_count = layer_count; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) { + hfi_id = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + ret = hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); + if (ret) + return ret; + } + + hfi_id = inst->fw_caps[cap_id].hfi_id; + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type = inst->hfi_layer_type; + u32 layer_count, layer_count_max; + + layer_count = (inst->codec == V4L2_PIX_FMT_H264) ? + inst->fw_caps[LAYER_COUNT_H264].value : + inst->fw_caps[LAYER_COUNT_HEVC].value; + + if (!layer_count) + return -EINVAL; + + if (layer_type == HFI_HIER_B) { + layer_count_max = MAX_LAYER_HB; + } else if (layer_type == HFI_HIER_P_HYBRID_LTR) { + layer_count_max = MAX_AVC_LAYER_HP_HYBRID_LTR; + } else if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_H264) { + layer_count_max = MAX_AVC_LAYER_HP_SLIDING_WINDOW; + } else { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_count_max = MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW; + else + layer_count_max = MAX_HEVC_LAYER_HP_SLIDING_WINDOW; + } + } else { + return -EINVAL; + } + + if (layer_count > layer_count_max) + layer_count = layer_count_max; + + layer_count += 1; /* base layer */ + inst->hfi_layer_count = layer_count; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 bitrate = inst->fw_caps[cap_id].value; + + /* ignore layer bitrate when total bitrate is set */ + if (inst->fw_caps[BITRATE].flags & CAP_FLAG_CLIENT_SET) + return 0; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET)) + return -EINVAL; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &bitrate, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 609258c81517..3c462ec9190b 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -22,7 +22,8 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); @@ -42,6 +43,10 @@ int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 792441463b4b..ca1545d28b53 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -136,7 +136,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen1, }, { .cap_id = BITRATE_MODE, @@ -291,6 +291,98 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_intra_period, }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = MAX_HIER_CODING_LAYER_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_layer_count_gen1, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 05e7b5ff2b92..4cb54e5e4f1f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -604,11 +604,10 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p break; } case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { - struct hfi_bitrate *brate = prop_data; - u32 *in = pdata; + struct hfi_bitrate *in = pdata, *brate = prop_data; - brate->bitrate = *in; - brate->layer_id = 0; + brate->bitrate = in->bitrate; + brate->layer_id = in->layer_id; packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate); break; } @@ -728,6 +727,20 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); break; } + case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + break; + } + case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 4343661e8606..0e4dee192384 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -154,11 +154,13 @@ #define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 #define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 #define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b struct hfi_pkt_hdr { u32 size; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 27878b70e516..7a85c1d4e5e6 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -391,7 +391,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .hfi_id = HFI_PROP_TOTAL_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen2, }, { .cap_id = BITRATE_PEAK, @@ -755,6 +755,188 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_u32, }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_TYPE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER_COUNT_HEVC, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER0_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + } }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index aec19efc41a5..d09096a9d5f9 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -74,7 +74,22 @@ enum hfi_rate_control { #define HFI_PROP_LTR_COUNT 0x03000134 #define HFI_PROP_LTR_MARK 0x03000135 #define HFI_PROP_LTR_USE 0x03000136 + +enum hfi_layer_encoding_type { + HFI_HIER_P_SLIDING_WINDOW = 0x1, + HFI_HIER_P_HYBRID_LTR = 0x2, + HFI_HIER_B = 0x3, +}; + +#define HFI_PROP_LAYER_ENCODING_TYPE 0x03000138 +#define HFI_PROP_LAYER_COUNT 0x03000139 #define HFI_PROP_TOTAL_BITRATE 0x0300013b +#define HFI_PROP_BITRATE_LAYER1 0x0300013c +#define HFI_PROP_BITRATE_LAYER2 0x0300013d +#define HFI_PROP_BITRATE_LAYER3 0x0300013e +#define HFI_PROP_BITRATE_LAYER4 0x0300013f +#define HFI_PROP_BITRATE_LAYER5 0x03000140 +#define HFI_PROP_BITRATE_LAYER6 0x03000141 #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 #define HFI_PROP_QUALITY_MODE 0x03000148 diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 352af99699dd..0041b0cc4001 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -77,6 +77,8 @@ struct iris_fmt { * @enc_raw_height: source image height for encoder instance * @enc_scale_width: scale width for encoder instance * @enc_scale_height: scale height for encoder instance + * @hfi_layer_type: hierarchical coding layer type + * @hfi_layer_count: hierarchical coding layer count */ struct iris_inst { @@ -120,6 +122,8 @@ struct iris_inst { u32 enc_raw_height; u32 enc_scale_width; u32 enc_scale_height; + u32 hfi_layer_type; + u32 hfi_layer_count; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 422e83ae0788..7ca37ca2dcca 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -31,6 +31,12 @@ struct iris_inst; #define INVALID_DEFAULT_MARK_OR_USE_LTR -1 #define MAX_LTR_FRAME_COUNT_GEN1 4 #define MAX_LTR_FRAME_COUNT_GEN2 2 +#define MAX_LAYER_HB 3 +#define MAX_AVC_LAYER_HP_HYBRID_LTR 5 +#define MAX_AVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW 5 +#define MAX_HIER_CODING_LAYER_GEN1 6 enum stage_type { STAGE_1 = 1, @@ -159,6 +165,23 @@ enum platform_inst_fw_cap_type { MARK_LTR, B_FRAME, INTRA_PERIOD, + LAYER_ENABLE, + LAYER_TYPE_H264, + LAYER_TYPE_HEVC, + LAYER_COUNT_H264, + LAYER_COUNT_HEVC, + LAYER0_BITRATE_H264, + LAYER1_BITRATE_H264, + LAYER2_BITRATE_H264, + LAYER3_BITRATE_H264, + LAYER4_BITRATE_H264, + LAYER5_BITRATE_H264, + LAYER0_BITRATE_HEVC, + LAYER1_BITRATE_HEVC, + LAYER2_BITRATE_HEVC, + LAYER3_BITRATE_HEVC, + LAYER4_BITRATE_HEVC, + LAYER5_BITRATE_HEVC, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 0ed82dc2b8af..c2cd4adc0823 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -936,6 +936,8 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) { + u32 layer_count = inst->hfi_layer_count; + u32 layer_type = inst->hfi_layer_type; u32 bframe_count, ltr_count; u32 num_ref = 1; @@ -945,9 +947,35 @@ static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) if (bframe_count) num_ref = 2; + /* The shift operation here is rounding logic, similar to [(x+1)/2]. */ + if (layer_type == HFI_HIER_P_HYBRID_LTR) + num_ref = (layer_count + 1) >> 1; + + if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = (layer_count + 1) >> 1; + else if (inst->codec == V4L2_PIX_FMT_H264 && layer_count < 4) + num_ref = (layer_count - 1); + else + num_ref = layer_count; + } + if (ltr_count) num_ref = num_ref + ltr_count; + /* + * The expression (1 << layers - 2) + 1 accounts for the number of reference + * frames in the Adaptive Hierarchical B-frame encoding case. In this scheme, + * the number of frames in a sub-GOP is related to (2^(number of layers) - 1), + * hence the use of the shift operation. + */ + if (layer_type == HFI_HIER_B) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = layer_count; + else + num_ref = (1 << (layer_count - 2)) + 1; + } + return num_ref; } From df5418d5f96c88430661a17143f4b74666dcc471 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:14 +0800 Subject: [PATCH 2239/5214] media: qcom: iris: Optimize iris_hfi_gen1_packet_session_set_property Modify iris_hfi_gen1_packet_session_set_property to simplify size calculations and remove redundant code patterns. Previously, packet->shdr.hdr.size was incremented by sizeof(u32) in every switch case, resulting in repetitive and less maintainable logic. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen1_command.c | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 4cb54e5e4f1f..7674b47ad6c4 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -485,7 +485,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p { void *prop_data = &packet->data[1]; - packet->shdr.hdr.size = sizeof(*packet); + packet->shdr.hdr.size = sizeof(*packet) + sizeof(ptype); packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; packet->shdr.session_id = inst->session_id; packet->num_properties = 1; @@ -498,14 +498,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p fsize->buffer_type = in->buffer_type; fsize->height = in->height; fsize->width = in->width; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize); + packet->shdr.hdr.size += sizeof(*fsize); break; } case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: { struct hfi_videocores_usage_type *in = pdata, *cu = prop_data; cu->video_core_enable_mask = in->video_core_enable_mask; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + packet->shdr.hdr.size += sizeof(*cu); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: { @@ -514,7 +514,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p hfi->buffer_type = in->buffer_type; hfi->format = in->format; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + packet->shdr.hdr.size += sizeof(*hfi); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: { @@ -533,7 +533,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p info->plane_format[1].buffer_alignment = 256; } - packet->shdr.hdr.size += sizeof(u32) + sizeof(*info); + packet->shdr.hdr.size += sizeof(*info); break; } case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { @@ -543,7 +543,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p count->type = in->type; count->count_actual = in->count_actual; count->count_min_host = in->count_min_host; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*count); + packet->shdr.hdr.size += sizeof(*count); break; } case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: { @@ -552,7 +552,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p multi->buffer_type = in->buffer_type; multi->enable = in->enable; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + packet->shdr.hdr.size += sizeof(*multi); break; } case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: { @@ -560,7 +560,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p sz->size = in->size; sz->type = in->type; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz); + packet->shdr.hdr.size += sizeof(*sz); break; } case HFI_PROPERTY_PARAM_WORK_ROUTE: { @@ -568,7 +568,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wr->video_work_route = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr); + packet->shdr.hdr.size += sizeof(*wr); break; } case HFI_PROPERTY_PARAM_WORK_MODE: { @@ -576,7 +576,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wm->video_work_mode = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + packet->shdr.hdr.size += sizeof(*wm); break; } case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: { @@ -592,7 +592,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p /* Level not supported, falling back to 1 */ pl->level = 1; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*pl); + packet->shdr.hdr.size += sizeof(*pl); break; } case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: { @@ -600,7 +600,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; en->enable = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*en); + packet->shdr.hdr.size += sizeof(*en); break; } case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { @@ -608,7 +608,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p brate->bitrate = in->bitrate; brate->layer_id = in->layer_id; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate); + packet->shdr.hdr.size += sizeof(*brate); break; } case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: { @@ -627,7 +627,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p } packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) * 2; + packet->shdr.hdr.size += sizeof(u32); break; } case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: { @@ -637,7 +637,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p entropy->entropy_mode = *in; if (entropy->entropy_mode == HFI_H264_ENTROPY_CABAC) entropy->cabac_model = HFI_H264_CABAC_MODEL_0; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*entropy); + packet->shdr.hdr.size += sizeof(*entropy); break; } case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2: { @@ -662,7 +662,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ((max_qp & 0xFF) << 16); range->min_qp.enable = 7; range->max_qp.enable = 7; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*range); + packet->shdr.hdr.size += sizeof(*range); break; } case HFI_PROPERTY_CONFIG_FRAME_RATE: { @@ -671,7 +671,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p frate->buffer_type = in->buffer_type; frate->framerate = in->framerate; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*frate); + packet->shdr.hdr.size += sizeof(*frate); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { @@ -683,7 +683,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p plane_actual_info->plane_format[0] = in->plane_format[0]; if (in->num_planes > 1) plane_actual_info->plane_format[1] = in->plane_format[1]; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info); + packet->shdr.hdr.size += sizeof(*plane_actual_info); break; } case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { @@ -691,7 +691,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p intra_refresh->mode = in->mode; intra_refresh->mbs = in->mbs; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); + packet->shdr.hdr.size += sizeof(*intra_refresh); break; } case HFI_PROPERTY_PARAM_VENC_LTRMODE: { @@ -700,7 +700,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ltr_mode->mode = in->mode; ltr_mode->count = in->count; ltr_mode->trust_mode = in->trust_mode; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mode); + packet->shdr.hdr.size += sizeof(*ltr_mode); break; } case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { @@ -709,14 +709,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ltr_use->frames = in->frames; ltr_use->ref_ltr = in->ref_ltr; ltr_use->use_constrnt = in->use_constrnt; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use); + packet->shdr.hdr.size += sizeof(*ltr_use); break; } case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; ltr_mark->mark_frame = in->mark_frame; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); + packet->shdr.hdr.size += sizeof(*ltr_mark); break; } case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { @@ -724,21 +724,21 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p intra_period->pframes = in->pframes; intra_period->bframes = in->bframes; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); + packet->shdr.hdr.size += sizeof(*intra_period); break; } case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { u32 *in = pdata; packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + packet->shdr.hdr.size += sizeof(u32); break; } case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { u32 *in = pdata; packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + packet->shdr.hdr.size += sizeof(u32); break; } default: From d5e3f2b961da5048a4e588bfa81d8c599956d12c Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:15 +0800 Subject: [PATCH 2240/5214] media: qcom: iris: Simplify COMV size calculation Unify AVC/HEVC handling by computing codec and lcu_size upfront. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_buffer.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index c2cd4adc0823..f55db869fee4 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -1014,16 +1014,13 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) u32 height = iris_vpu_enc_get_bitstream_height(inst); u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 num_recon = hfi_buffer_get_recon_count(inst); - u32 lcu_size = 16; + u32 codec, lcu_size; - if (inst->codec == V4L2_PIX_FMT_HEVC) { - lcu_size = 32; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_HEVC); - } + codec = (inst->codec == V4L2_PIX_FMT_HEVC) ? + HFI_CODEC_ENCODE_HEVC : HFI_CODEC_ENCODE_AVC; + lcu_size = (inst->codec == V4L2_PIX_FMT_HEVC) ? 32 : 16; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_AVC); + return hfi_buffer_comv_enc(width, height, lcu_size, num_recon + 1, codec); } static inline From 5c66647a5c3e005f9b6a96fe4aa7ec82d2701b4f Mon Sep 17 00:00:00 2001 From: Vishnu Reddy Date: Wed, 1 Apr 2026 19:19:29 +0530 Subject: [PATCH 2241/5214] media: iris: add FPS calculation and VPP FW overhead in frequency formula MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver was using a fixed default FPS value when calculating the VPU frequency. This caused wrong frequency requests for high‑frame‑rate streams, for example 4K at 240 FPS. Because of this, the hardware was running at a lower frequency than needed. Add the FPS measurement based on the decoder input buffer arrival rate. The measured FPS is stored per instance and used in frequency calculation instead of the fixed default FPS. The value is clamped so that it does not exceed platform limits. Add a VPP firmware overhead when running in STAGE_2. Reviewed-by: Vikash Garodia Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_instance.h | 4 ++++ drivers/media/platform/qcom/iris/iris_vdec.c | 20 +++++++++++++++++++ drivers/media/platform/qcom/iris/iris_vpu2.c | 2 +- .../platform/qcom/iris/iris_vpu_common.c | 6 +++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 0041b0cc4001..1d8a22ea4809 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -70,6 +70,8 @@ struct iris_fmt { * @metadata_idx: index for metadata buffer * @codec: codec type * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver + * @last_buf_ns: start time of received input buffer for current one second FPS window + * @frame_counter: input buffer counter for current one second FPS window * @frame_rate: frame rate of current instance * @operating_rate: operating rate of current instance * @hfi_rc_type: rate control type @@ -115,6 +117,8 @@ struct iris_inst { u32 metadata_idx; u32 codec; bool last_buffer_dequeued; + u64 last_buf_ns; + u32 frame_counter; u32 frame_rate; u32 operating_rate; u32 hfi_rc_type; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index ccda3b9fb845..1d34c7bcf8f8 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -54,6 +54,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage; + inst->frame_rate = MAXIMUM_FPS; memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0], INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap)); @@ -369,6 +370,8 @@ int iris_vdec_streamon_input(struct iris_inst *inst) if (ret) return ret; + inst->frame_counter = 0; + return iris_process_streamon_input(inst); } @@ -411,6 +414,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) { struct iris_buffer *buf = to_iris_buffer(vbuf); struct vb2_buffer *vb2 = &vbuf->vb2_buf; + u64 cur_buf_ns, delta_ns; struct vb2_queue *q; int ret; @@ -427,6 +431,22 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } + if (buf->type == BUF_INPUT) { + cur_buf_ns = ktime_get_ns(); + + if (!inst->frame_counter) + inst->last_buf_ns = cur_buf_ns; + + inst->frame_counter++; + delta_ns = cur_buf_ns - inst->last_buf_ns; + + if (delta_ns >= NSEC_PER_SEC) { + inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS, + MAXIMUM_FPS); + inst->frame_counter = 0; + } + } + iris_scale_power(inst); return iris_queue_buffer(inst, buf); diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index 9c103a2e4e4e..73c201f4f338 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -18,7 +18,7 @@ static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size) struct v4l2_format *inp_f = inst->fmt_src; u32 mbs_per_second, mbpf, height, width; unsigned long vpp_freq, vsp_freq; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index c6cfc1d9fd9e..3872c4f37987 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -416,7 +416,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz u32 height, width, mbs_per_second, mbpf; u64 fw_cycles, fw_vpp_cycles; u64 vsp_cycles, vpp_cycles; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); @@ -435,6 +435,10 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz if (inst->fw_caps[PIPE].value > 1) vpp_cycles += div_u64(vpp_cycles * 59, 1000); + /* 1.05 is VPP FW overhead */ + if (inst->fw_caps[STAGE].value == STAGE_2) + vpp_cycles += mult_frac(vpp_cycles, 5, 100); + vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); /* VSP FW overhead 1.05 */ From d1c9f40f764ed2a91d2903c25d1962cf43afef89 Mon Sep 17 00:00:00 2001 From: Vishnu Reddy Date: Thu, 14 May 2026 00:28:22 +0530 Subject: [PATCH 2242/5214] media: iris: optimize COMV buffer allocation for VPU3x and VPU4x The existing iris_vpu_dec_comv_size() used VIDEO_MAX_FRAME (32) as num_comv count unconditionally when calculating the co-located motion vector (COMV) buffer size. This resulted in an oversized COMV buffer allocation throughout decode session, wasting memory regardless of actual number of buffers required. For VPU3x and VPU4x platforms, introduce iris_vpu3x_4x_dec_comv_size() to replace iris_vpu_dec_comv_size(). These derive num_comv dynamically, it uses inst->fw_min_count once the firmware has reported its buffer requirements, and fallback to output count during initialization before firmware has communicated its requirements. This aligns the COMV buffer size to the actual count needed rather than always allocating with fixed VIDEO_MAX_FRAME value. Additionally, during iris_vdec_inst_init(), fw_min_count was initialized to MIN_BUFFERS instead of 0. This masked the fallback logic and caused the COMV size calculation to use MIN_BUFFERS even before firmware had reported its actual requirements. Fix this by initializing fw_min_count to 0. During testing of 1080p AVC, it reduces the COMV buffer size from 32.89MB to 6.16MB per decode session, significantly reducing memory consumption. Reviewed-by: Vikash Garodia Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_command.c | 15 +++---------- .../platform/qcom/iris/iris_platform_common.h | 1 - .../qcom/iris/iris_platform_qcs8300.h | 1 - .../platform/qcom/iris/iris_platform_sm8550.h | 1 - drivers/media/platform/qcom/iris/iris_vdec.c | 3 ++- .../platform/qcom/iris/iris_vpu_buffer.c | 22 +++++++++++++++++-- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index c90b22a75bc5..7ac69af63ead 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -10,7 +10,6 @@ #define UNSPECIFIED_COLOR_FORMAT 5 #define NUM_SYS_INIT_PACKETS 8 -#define NUM_COMV_AV1 18 #define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32))) @@ -1207,18 +1206,10 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst) { - struct platform_inst_caps *caps; - struct iris_core *core = inst->core; - u32 num_comv; + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; - caps = core->iris_platform_data->inst_caps; - - /* - * AV1 needs more comv buffers than other codecs. - * Update accordingly. - */ - num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? - NUM_COMV_AV1 : caps->num_comv; + if (inst->fw_min_count) + num_comv = inst->fw_min_count; return iris_hfi_gen2_session_set_property(inst, HFI_PROP_COMV_BUFFER_COUNT, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 7ca37ca2dcca..c522dd0a528b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -96,7 +96,6 @@ struct platform_inst_caps { u32 mb_cycles_vpp; u32 mb_cycles_fw; u32 mb_cycles_fw_vpp; - u32 num_comv; u32 max_frame_rate; u32 max_operating_rate; }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h index 61025f1e965b..3cfecae80d1e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -15,7 +15,6 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = { .mb_cycles_vpp = 200, .mb_cycles_fw = 326389, .mb_cycles_fw_vpp = 44156, - .num_comv = 0, .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h index a9d9709c2e35..3c9dae995bb2 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h @@ -23,7 +23,6 @@ static struct platform_inst_caps platform_inst_cap_sm8550 = { .mb_cycles_vpp = 200, .mb_cycles_fw = 489583, .mb_cycles_fw_vpp = 66234, - .num_comv = 0, .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 1d34c7bcf8f8..dc7b051f2d01 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -24,7 +24,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) inst->fmt_src = kzalloc_obj(*inst->fmt_src); inst->fmt_dst = kzalloc_obj(*inst->fmt_dst); - inst->fw_min_count = MIN_BUFFERS; + inst->fw_min_count = 0; f = inst->fmt_src; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -259,6 +259,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) /* Update capture format based on new ip w/h */ output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT); inst->crop.left = 0; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index f55db869fee4..fb6f1016415e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -731,6 +731,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_comv_h264d(width, height, num_comv); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_comv_h265d(width, height, num_comv); + + return 0; +} + +static u32 iris_vpu3x_4x_dec_comv_size(struct iris_inst *inst) +{ + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; + struct v4l2_format *f = inst->fmt_src; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + + if (inst->fw_min_count) + num_comv = inst->fw_min_count; + if (inst->codec == V4L2_PIX_FMT_H264) return hfi_buffer_comv_h264d(width, height, num_comv); else if (inst->codec == V4L2_PIX_FMT_HEVC) @@ -2066,7 +2084,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu_dec_line_size }, {BUF_PERSIST, iris_vpu_dec_persist_size }, @@ -2139,7 +2157,7 @@ u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu4x_dec_line_size }, {BUF_PERSIST, iris_vpu4x_dec_persist_size }, From 4147ffa3d96dde43bd4f3607db75f261fafaade2 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:34:59 +0800 Subject: [PATCH 2243/5214] media: iris: Add hardware power on/off ops for X1P42100 On X1P42100 the Iris block has an extra BSE clock. Wire this clock into the power on/off sequence. The BSE clock is used to drive the Bin Stream Engine, which is a sub-block of the video codec hardware responsible for bitstream-level processing. It is required to be enabled separately from the core clock to ensure proper codec operation. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 3872c4f37987..5a85568c5ee1 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -224,6 +224,7 @@ void iris_vpu_power_off_hw(struct iris_core *core) { dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); } @@ -292,12 +293,18 @@ int iris_vpu_power_on_hw(struct iris_core *core) if (ret && ret != -ENOENT) goto err_disable_hw_clock; + ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK); + if (ret && ret != -ENOENT) + goto err_disable_hw_ahb_clock; + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); if (ret) - goto err_disable_hw_ahb_clock; + goto err_disable_bse_hw_clock; return 0; +err_disable_bse_hw_clock: + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); err_disable_hw_ahb_clock: iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); err_disable_hw_clock: From f014f5bec088e7c084343642a83725c77a24af39 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:35:00 +0800 Subject: [PATCH 2244/5214] media: iris: Add platform data for X1P42100 Introduce platform data for X1P42100, derived from SM8550 but using a different clock configuration and a dedicated OPP setup. Reviewed-by: Vikash Garodia Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_platform_common.h | 1 + .../platform/qcom/iris/iris_platform_vpu3x.c | 42 +++++++++++++++++++ .../qcom/iris/iris_platform_x1p42100.h | 22 ++++++++++ drivers/media/platform/qcom/iris/iris_probe.c | 4 ++ 4 files changed, 69 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_x1p42100.h diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index c522dd0a528b..982767faa51c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -58,6 +58,7 @@ extern const struct iris_platform_data sm8250_data; extern const struct iris_platform_data sm8550_data; extern const struct iris_platform_data sm8650_data; extern const struct iris_platform_data sm8750_data; +extern const struct iris_platform_data x1p42100_data; enum platform_clk_type { IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */ diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index c249ff827541..541ddc40e3ae 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -16,6 +16,7 @@ #include "iris_platform_sm8550.h" #include "iris_platform_sm8650.h" #include "iris_platform_sm8750.h" +#include "iris_platform_x1p42100.h" static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = { .firmware_data = &iris_hfi_gen2_data, @@ -29,6 +30,12 @@ static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = { .fwname = "qcom/vpu/vpu30_p4.mbn", }; +static const struct iris_firmware_desc iris_vpu30_p1_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p1_s7.mbn", +}; + static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = { .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, @@ -217,3 +224,38 @@ const struct iris_platform_data sm8750_data = { .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, }; + +/* + * Shares most of SM8550 data except: + * - clk_tbl and opp_clk_tbl for x1p42100 + * - different firmware + * - different num_vpp_pipe + */ +const struct iris_platform_data x1p42100_data = { + .firmware_desc = &iris_vpu30_p1_gen2_desc, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = x1p42100_clk_table, + .clk_tbl_size = ARRAY_SIZE(x1p42100_clk_table), + .opp_clk_tbl = x1p42100_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 1, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h new file mode 100644 index 000000000000..d89acfbc1233 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_X1P42100_H__ +#define __IRIS_PLATFORM_X1P42100_H__ + +static const struct platform_clk_data x1p42100_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, + {IRIS_BSE_HW_CLK, "vcodec0_bse" }, +}; + +static const char *const x1p42100_opp_clk_table[] = { + "vcodec0_core", + "vcodec0_bse", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index a755a6f73ea6..a53423399854 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -385,6 +385,10 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8750-iris", .data = &sm8750_data, }, + { + .compatible = "qcom,x1p42100-iris", + .data = &x1p42100_data, + }, { }, }; MODULE_DEVICE_TABLE(of, iris_dt_match); From 56f93da11a7c310a83dded5bee28d6661a04da13 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 29 May 2026 17:26:11 +0300 Subject: [PATCH 2245/5214] media: iris: drop struct iris_fmt The struct iris_fmt unites pixfmt with the plane type, however the type from the struct is not actually used. Drop the struct completely and use u32 pixfmt in all the callsites. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_instance.h | 5 - .../platform/qcom/iris/iris_platform_common.h | 2 +- .../platform/qcom/iris/iris_platform_vpu2.c | 17 +--- .../platform/qcom/iris/iris_platform_vpu3x.c | 22 +---- drivers/media/platform/qcom/iris/iris_vdec.c | 78 +++++++-------- drivers/media/platform/qcom/iris/iris_venc.c | 96 ++++++++----------- 6 files changed, 80 insertions(+), 140 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 1d8a22ea4809..c54d8ec8562a 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -29,11 +29,6 @@ enum iris_fmt_type_cap { IRIS_FMT_QC08C, }; -struct iris_fmt { - u32 pixfmt; - u32 type; -}; - /** * struct iris_inst - holds per video instance parameters * diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 982767faa51c..6d69a1e3dcd3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -311,7 +311,7 @@ struct iris_platform_data { const char * const *controller_rst_tbl; unsigned int controller_rst_tbl_size; u64 dma_mask; - struct iris_fmt *inst_iris_fmts; + const u32 *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; const struct tz_cp_config *tz_cp_config_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 41986af8313b..6e06a32822bb 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -28,19 +28,10 @@ static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { .fwname = "qcom/vpu/vpu20_p4.mbn", }; -static struct iris_fmt iris_fmts_vpu2_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_fmts_vpu2_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, }; static struct platform_inst_caps platform_inst_cap_vpu2 = { diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index 541ddc40e3ae..2c63adbc5579 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -48,23 +48,11 @@ static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = { .fwname = "qcom/vpu/vpu35_p4.mbn", }; -static struct iris_fmt iris_fmts_vpu3x_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_AV1] = { - .pixfmt = V4L2_PIX_FMT_AV1, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_fmts_vpu3x_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, + [IRIS_FMT_AV1] = V4L2_PIX_FMT_AV1, }; static const struct icc_info iris_icc_info_vpu3x[] = { diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index dc7b051f2d01..b65832042dc8 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -68,23 +68,16 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_vdec_formats_cap[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_vdec_formats_cap[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = inst->core->iris_platform_data->inst_iris_fmts; @@ -95,25 +88,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) - break; + if (fmt[i] == pixfmt) + return true; } - if (i == size || fmt[i].type != type) - return NULL; - - return &fmt[i]; + return false; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -125,18 +114,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -144,14 +133,14 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; default: return -EINVAL; @@ -164,15 +153,15 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; struct vb2_queue *src_q; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -180,7 +169,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; @@ -229,7 +218,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -268,7 +257,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) inst->crop.height = f->fmt.pix_mp.height; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_dst; @@ -297,16 +286,13 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub) diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index aeed756ee9ca..2398992d0596 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -85,34 +85,21 @@ void iris_venc_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_venc_formats_cap[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_venc_formats_cap[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, }; -static const struct iris_fmt iris_venc_formats_out[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_venc_formats_out[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = iris_venc_formats_out; @@ -123,25 +110,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) - break; + if (fmt[i] == pixfmt) + return true; } - if (i == size || fmt[i].type != type) - return NULL; - - return &fmt[i]; + return false; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -153,18 +136,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -172,14 +155,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL; break; default: @@ -192,14 +175,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -207,7 +190,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -228,17 +211,17 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f) { - const struct iris_fmt *venc_fmt; struct v4l2_format *fmt; u32 codec_align; + bool supported; iris_venc_try_fmt(inst, f); - venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type); - if (!venc_fmt) + supported = check_format(inst, f->fmt.pix_mp.pixelformat, f->type); + if (!supported) return -EINVAL; - codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16; + codec_align = (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) ? 32 : 16; fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -281,7 +264,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) iris_venc_try_fmt(inst, f); - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -350,16 +333,13 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_venc_subscribe_event(struct iris_inst *inst, From 3395d7526386dccad1d9ff101a6e1f5d9c47b9ba Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Fri, 29 May 2026 17:36:54 +0300 Subject: [PATCH 2246/5214] media: iris: Initialize HFI ops after firmware load in core init The HFI sys ops were previously initialized in probe() but, we don't have firmware loaded at probe time. Since HFI is tightly coupled to firmware, initialize the HFI sys ops after firmware has been successfully loaded and booted. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dmitry Baryshkov Signed-off-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.c | 2 ++ drivers/media/platform/qcom/iris/iris_probe.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index e6141012cd3d..a1823ded46e8 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -74,6 +74,8 @@ int iris_core_init(struct iris_core *core) if (ret) goto error_unload_fw; + core->iris_firmware_data->init_hfi_ops(core); + ret = iris_hfi_core_init(core); if (ret) goto error_unload_fw; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index a53423399854..c2dcb50a2782 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -266,7 +266,6 @@ static int iris_probe(struct platform_device *pdev) return ret; iris_init_ops(core); - core->iris_firmware_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) From 285ecb7d9e1b2401c2eccca4d0f26615f4d651eb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 May 2026 12:29:07 +0200 Subject: [PATCH 2247/5214] Revert "gpib: cb7210: Fix region leak when request_irq fails" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5ad28496055858166eb2268344c8fda2c26d3561. Turns out not to be correct. Link: https://lore.kernel.org/r/PpNUbGhrvT8I_KayoDvQYI2PYjmMw1QEkuVBDZz2PwBsVVgPkBXJarc2mBM0IhiH3AQG0GtgqEsDRXNj3yUKEDBaZa25u73pAjvcE6vfRsg=@protonmail.com Reported-by: Dominik Karol Piątkowski Cc: Mark Brown Cc: Hongling Zeng Cc: Hongling Zeng Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/cb7210/cb7210.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 05058bf2cd50..6dd8637c5964 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1062,7 +1062,6 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); - release_region(nec7210_iobase(cb_priv), cb7210_iosize); return -EBUSY; } cb_priv->irq = config->ibirq; From e11560b050ce867bd7d3ccea138231db54e2250a Mon Sep 17 00:00:00 2001 From: Denzeel Oliva Date: Thu, 28 May 2026 15:09:01 -0500 Subject: [PATCH 2248/5214] clk: samsung: exynos990: Fix PERIC0/1 USI clock types Use nMUX() for USI and UART user muxes to allow reparenting between OSC and CMU IP output when changing rates, and use DIV_F() with CLK_SET_RATE_PARENT on their dividers and gates so rate requests propagate upward. Consolidate identical USI parent arrays into shared mout_peric0_nonbususer_p and mout_peric1_nonbususer_p. Signed-off-by: Denzeel Oliva Link: https://patch.msgid.link/20260528-perics-usi-v1-1-13a6ee4d1a6f@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynos990.c | 307 +++++++++++++--------------- 1 file changed, 143 insertions(+), 164 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos990.c b/drivers/clk/samsung/clk-exynos990.c index 6277dd557fab..4385c3b76dd6 100644 --- a/drivers/clk/samsung/clk-exynos990.c +++ b/drivers/clk/samsung/clk-exynos990.c @@ -1546,54 +1546,44 @@ static const unsigned long peric0_clk_regs[] __initconst = { /* Parent clock list for CMU_PERIC0 muxes */ PNAME(mout_peric0_bus_user_p) = { "oscclk", "dout_cmu_peric0_bus" }; -PNAME(mout_peric0_uart_dbg_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi00_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi01_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi02_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi03_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi04_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi05_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi13_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi14_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi15_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi_i2c_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; +PNAME(mout_peric0_nonbususer_p) = { "oscclk", "dout_cmu_peric0_ip" }; static const struct samsung_mux_clock peric0_mux_clks[] __initconst = { MUX(CLK_MOUT_PERIC0_BUS_USER, "mout_peric0_bus_user", mout_peric0_bus_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_BUS_USER, 4, 1), - MUX(CLK_MOUT_PERIC0_UART_DBG, "mout_peric0_uart_dbg", - mout_peric0_uart_dbg_p, PLL_CON0_MUX_CLKCMU_PERIC0_UART_DBG, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI00_USI_USER, "mout_peric0_usi00_usi_user", - mout_peric0_usi00_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI00_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI01_USI_USER, "mout_peric0_usi01_usi_user", - mout_peric0_usi01_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI01_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI02_USI_USER, "mout_peric0_usi02_usi_user", - mout_peric0_usi02_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI02_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI03_USI_USER, "mout_peric0_usi03_usi_user", - mout_peric0_usi03_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI03_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI04_USI_USER, "mout_peric0_usi04_usi_user", - mout_peric0_usi04_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI04_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI05_USI_USER, "mout_peric0_usi05_usi_user", - mout_peric0_usi05_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI05_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI13_USI_USER, "mout_peric0_usi13_usi_user", - mout_peric0_usi13_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI13_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI14_USI_USER, "mout_peric0_usi14_usi_user", - mout_peric0_usi14_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI14_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI15_USI_USER, "mout_peric0_usi15_usi_user", - mout_peric0_usi15_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI15_USI_USER, - 4, 1), + nMUX(CLK_MOUT_PERIC0_UART_DBG, "mout_peric0_uart_dbg", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_UART_DBG, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI00_USI_USER, "mout_peric0_usi00_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI00_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI01_USI_USER, "mout_peric0_usi01_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI01_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI02_USI_USER, "mout_peric0_usi02_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI02_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI03_USI_USER, "mout_peric0_usi03_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI03_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI04_USI_USER, "mout_peric0_usi04_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI04_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI05_USI_USER, "mout_peric0_usi05_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI05_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI13_USI_USER, "mout_peric0_usi13_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI13_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI14_USI_USER, "mout_peric0_usi14_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI14_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI15_USI_USER, "mout_peric0_usi15_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI15_USI_USER, + 4, 1), MUX(CLK_MOUT_PERIC0_USI_I2C_USER, "mout_peric0_usi_i2c_user", - mout_peric0_usi_i2c_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI_I2C_USER, + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI_I2C_USER, 4, 1), }; @@ -1602,42 +1592,42 @@ static const struct samsung_div_clock peric0_div_clks[] __initconst = { "mout_peric0_uart_dbg", CLK_CON_DIV_DIV_CLK_PERIC0_UART_DBG, 0, 4), - DIV(CLK_DOUT_PERIC0_USI00_USI, "dout_peric0_usi00_usi", - "mout_peric0_usi00_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI00_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI01_USI, "dout_peric0_usi01_usi", - "mout_peric0_usi01_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI01_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI02_USI, "dout_peric0_usi02_usi", - "mout_peric0_usi02_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI02_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI03_USI, "dout_peric0_usi03_usi", - "mout_peric0_usi03_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI03_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI04_USI, "dout_peric0_usi04_usi", - "mout_peric0_usi04_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI04_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI05_USI, "dout_peric0_usi05_usi", - "mout_peric0_usi05_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI05_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI13_USI, "dout_peric0_usi13_usi", - "mout_peric0_usi13_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI13_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI14_USI, "dout_peric0_usi14_usi", - "mout_peric0_usi14_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI14_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI15_USI, "dout_peric0_usi15_usi", - "mout_peric0_usi15_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI15_USI, - 0, 4), + DIV_F(CLK_DOUT_PERIC0_USI00_USI, "dout_peric0_usi00_usi", + "mout_peric0_usi00_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI00_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI01_USI, "dout_peric0_usi01_usi", + "mout_peric0_usi01_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI01_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI02_USI, "dout_peric0_usi02_usi", + "mout_peric0_usi02_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI02_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI03_USI, "dout_peric0_usi03_usi", + "mout_peric0_usi03_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI03_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI04_USI, "dout_peric0_usi04_usi", + "mout_peric0_usi04_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI04_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI05_USI, "dout_peric0_usi05_usi", + "mout_peric0_usi05_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI05_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI13_USI, "dout_peric0_usi13_usi", + "mout_peric0_usi13_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI13_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI14_USI, "dout_peric0_usi14_usi", + "mout_peric0_usi14_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI14_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI15_USI, "dout_peric0_usi15_usi", + "mout_peric0_usi15_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI15_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(CLK_DOUT_PERIC0_USI_I2C, "dout_peric0_usi_i2c", "mout_peric0_usi_i2c_user", CLK_CON_DIV_DIV_CLK_PERIC0_USI_I2C, @@ -2107,58 +2097,47 @@ static const unsigned long peric1_clk_regs[] __initconst = { /* Parent clock list for CMU_PERIC1 muxes */ PNAME(mout_peric1_bus_user_p) = { "oscclk", "dout_cmu_peric1_bus" }; -PNAME(mout_peric1_uart_bt_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi06_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi07_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi08_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi09_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi10_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi11_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi12_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi18_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi16_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi17_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi_i2c_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; +PNAME(mout_peric1_nonbususer_p) = { "oscclk", "dout_cmu_peric1_ip" }; static const struct samsung_mux_clock peric1_mux_clks[] __initconst = { MUX(CLK_MOUT_PERIC1_BUS_USER, "mout_peric1_bus_user", mout_peric1_bus_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_BUS_USER, 4, 1), - MUX(CLK_MOUT_PERIC1_UART_BT_USER, "mout_peric1_uart_bt_user", - mout_peric1_uart_bt_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_UART_BT_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI06_USI_USER, "mout_peric1_usi06_usi_user", - mout_peric1_usi06_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI06_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI07_USI_USER, "mout_peric1_usi07_usi_user", - mout_peric1_usi07_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI07_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI08_USI_USER, "mout_peric1_usi08_usi_user", - mout_peric1_usi08_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI08_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI09_USI_USER, "mout_peric1_usi09_usi_user", - mout_peric1_usi09_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI09_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI10_USI_USER, "mout_peric1_usi10_usi_user", - mout_peric1_usi10_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI10_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI11_USI_USER, "mout_peric1_usi11_usi_user", - mout_peric1_usi11_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI11_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI12_USI_USER, "mout_peric1_usi12_usi_user", - mout_peric1_usi12_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI12_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI18_USI_USER, "mout_peric1_usi18_usi_user", - mout_peric1_usi18_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI18_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI16_USI_USER, "mout_peric1_usi16_usi_user", - mout_peric1_usi16_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI16_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI17_USI_USER, "mout_peric1_usi17_usi_user", - mout_peric1_usi17_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI17_USI_USER, - 4, 1), + nMUX(CLK_MOUT_PERIC1_UART_BT_USER, "mout_peric1_uart_bt_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_UART_BT_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI06_USI_USER, "mout_peric1_usi06_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI06_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI07_USI_USER, "mout_peric1_usi07_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI07_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI08_USI_USER, "mout_peric1_usi08_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI08_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI09_USI_USER, "mout_peric1_usi09_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI09_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI10_USI_USER, "mout_peric1_usi10_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI10_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI11_USI_USER, "mout_peric1_usi11_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI11_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI12_USI_USER, "mout_peric1_usi12_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI12_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI18_USI_USER, "mout_peric1_usi18_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI18_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI16_USI_USER, "mout_peric1_usi16_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI16_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI17_USI_USER, "mout_peric1_usi17_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI17_USI_USER, + 4, 1), MUX(CLK_MOUT_PERIC1_USI_I2C_USER, "mout_peric1_usi_i2c_user", - mout_peric1_usi_i2c_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI_I2C_USER, + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI_I2C_USER, 4, 1), }; @@ -2167,46 +2146,46 @@ static const struct samsung_div_clock peric1_div_clks[] __initconst = { "mout_peric1_uart_bt_user", CLK_CON_DIV_DIV_CLK_PERIC1_UART_BT, 0, 4), - DIV(CLK_DOUT_PERIC1_USI06_USI, "dout_peric1_usi06_usi", - "mout_peric1_usi06_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI06_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI07_USI, "dout_peric1_usi07_usi", - "mout_peric1_usi07_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI07_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI08_USI, "dout_peric1_usi08_usi", - "mout_peric1_usi08_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI08_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI18_USI, "dout_peric1_usi18_usi", - "mout_peric1_usi18_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI18_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI12_USI, "dout_peric1_usi12_usi", - "mout_peric1_usi12_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI12_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI09_USI, "dout_peric1_usi09_usi", - "mout_peric1_usi09_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI09_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI10_USI, "dout_peric1_usi10_usi", - "mout_peric1_usi10_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI10_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI11_USI, "dout_peric1_usi11_usi", - "mout_peric1_usi11_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI11_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI16_USI, "dout_peric1_usi16_usi", - "mout_peric1_usi16_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI16_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI17_USI, "dout_peric1_usi17_usi", - "mout_peric1_usi17_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI17_USI, - 0, 4), + DIV_F(CLK_DOUT_PERIC1_USI06_USI, "dout_peric1_usi06_usi", + "mout_peric1_usi06_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI06_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI07_USI, "dout_peric1_usi07_usi", + "mout_peric1_usi07_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI07_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI08_USI, "dout_peric1_usi08_usi", + "mout_peric1_usi08_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI08_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI18_USI, "dout_peric1_usi18_usi", + "mout_peric1_usi18_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI18_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI12_USI, "dout_peric1_usi12_usi", + "mout_peric1_usi12_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI12_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI09_USI, "dout_peric1_usi09_usi", + "mout_peric1_usi09_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI09_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI10_USI, "dout_peric1_usi10_usi", + "mout_peric1_usi10_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI10_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI11_USI, "dout_peric1_usi11_usi", + "mout_peric1_usi11_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI11_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI16_USI, "dout_peric1_usi16_usi", + "mout_peric1_usi16_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI16_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI17_USI, "dout_peric1_usi17_usi", + "mout_peric1_usi17_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI17_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(CLK_DOUT_PERIC1_USI_I2C, "dout_peric1_usi_i2c", "mout_peric1_usi_i2c_user", CLK_CON_DIV_DIV_CLK_PERIC1_USI_I2C, From c3029c38175b66b8ed8713f6c6c028a1de998b0d Mon Sep 17 00:00:00 2001 From: "Chang S. Bae" Date: Fri, 15 May 2026 13:27:32 -0400 Subject: [PATCH 2249/5214] KVM: VMX: Macrofy GPR swapping in __vmx_vcpu_run() Convert the repeated register save/restore sequences into macros, trading some level of implementation trickiness for conciseness (more than one register can be saved/restored with one invocation) and a smaller chance of cut and paste errors. This is particularly useful in preparation for extended GPR support; upcoming support for APX would need to add 32 lines to the VM entry/exit paths. No functional change intended. Suggested-by: Paolo Bonzini Signed-off-by: Chang S. Bae Link: https://lore.kernel.org/6e67df0e-e5f0-43f5-aa86-22e8b01b75d2@redhat.com Link: https://patch.msgid.link/20260512011502.53072-2-chang.seok.bae@intel.com/ [Keep kvm_vcpu_regs.h and put the macros in there. -Paolo] Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_vcpu_regs.h | 104 +++++++++++++++++++++++++++ arch/x86/kvm/svm/vmenter.S | 3 - arch/x86/kvm/vmenter.h | 31 ++++++++ arch/x86/kvm/vmx/vmenter.S | 89 ++++++----------------- 4 files changed, 155 insertions(+), 72 deletions(-) diff --git a/arch/x86/include/asm/kvm_vcpu_regs.h b/arch/x86/include/asm/kvm_vcpu_regs.h index 1af2cb59233b..650fabf5871d 100644 --- a/arch/x86/include/asm/kvm_vcpu_regs.h +++ b/arch/x86/include/asm/kvm_vcpu_regs.h @@ -22,4 +22,108 @@ #define __VCPU_REGS_R15 15 #endif +#ifdef __ASSEMBLER__ + +#define REG_NUM_INVALID 100 + + # convert the 32-bit register operand \r32 into a register number; + # store it in the value whose name is in \opd. + # only for !CONFIG_X86_64 (does not support r8d-r15d) + .macro R32_NUM opd r32 + \opd = REG_NUM_INVALID + .ifc \r32,%eax + \opd = __VCPU_REGS_RAX + .endif + .ifc \r32,%ecx + \opd = __VCPU_REGS_RCX + .endif + .ifc \r32,%edx + \opd = __VCPU_REGS_RDX + .endif + .ifc \r32,%ebx + \opd = __VCPU_REGS_RBX + .endif + .ifc \r32,%esp + \opd = __VCPU_REGS_RSP + .endif + .ifc \r32,%ebp + \opd = __VCPU_REGS_RBP + .endif + .ifc \r32,%esi + \opd = __VCPU_REGS_RSI + .endif + .ifc \r32,%edi + \opd = __VCPU_REGS_RDI + .endif + .endm + + # convert the 64-bit register operand \r64 into a register number; + # store it in the value whose name is in \opd. + .macro R64_NUM opd r64 + \opd = REG_NUM_INVALID +#ifdef CONFIG_X86_64 + .ifc \r64,%rax + \opd = __VCPU_REGS_RAX + .endif + .ifc \r64,%rcx + \opd = __VCPU_REGS_RCX + .endif + .ifc \r64,%rdx + \opd = __VCPU_REGS_RDX + .endif + .ifc \r64,%rbx + \opd = __VCPU_REGS_RBX + .endif + .ifc \r64,%rsp + \opd = __VCPU_REGS_RSP + .endif + .ifc \r64,%rbp + \opd = __VCPU_REGS_RBP + .endif + .ifc \r64,%rsi + \opd = __VCPU_REGS_RSI + .endif + .ifc \r64,%rdi + \opd = __VCPU_REGS_RDI + .endif + .ifc \r64,%r8 + \opd = 8 + .endif + .ifc \r64,%r9 + \opd = 9 + .endif + .ifc \r64,%r10 + \opd = 10 + .endif + .ifc \r64,%r11 + \opd = 11 + .endif + .ifc \r64,%r12 + \opd = 12 + .endif + .ifc \r64,%r13 + \opd = 13 + .endif + .ifc \r64,%r14 + \opd = 14 + .endif + .ifc \r64,%r15 + \opd = 15 + .endif +#endif + .endm + +.macro REG_NUM reg_num reg +#ifdef CONFIG_X86_64 + R64_NUM \reg_num \reg +#else + R32_NUM \reg_num \reg +#endif + .if \reg_num == REG_NUM_INVALID + .error "invalid register" + .endif +.endm + +#endif + #endif /* _ASM_X86_KVM_VCPU_REGS_H */ diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index f523d9e49839..6a91e1383e8f 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -4,13 +4,10 @@ #include #include #include -#include #include #include "kvm-asm-offsets.h" #include "vmenter.h" -#define WORD_SIZE (BITS_PER_LONG / 8) - /* Intentionally omit RAX as it's context switched by hardware */ #define VCPU_RCX (SVM_vcpu_arch_regs + __VCPU_REGS_RCX * WORD_SIZE) #define VCPU_RDX (SVM_vcpu_arch_regs + __VCPU_REGS_RDX * WORD_SIZE) diff --git a/arch/x86/kvm/vmenter.h b/arch/x86/kvm/vmenter.h index ba3f71449c62..77376812c81b 100644 --- a/arch/x86/kvm/vmenter.h +++ b/arch/x86/kvm/vmenter.h @@ -2,6 +2,8 @@ #ifndef __KVM_X86_VMENTER_H #define __KVM_X86_VMENTER_H +#include + #define KVM_ENTER_VMRESUME BIT(0) #define KVM_ENTER_SAVE_SPEC_CTRL BIT(1) #define KVM_ENTER_CLEAR_CPU_BUFFERS_FOR_MMIO BIT(2) @@ -76,5 +78,34 @@ wrmsr .endm +#define WORD_SIZE (BITS_PER_LONG / 8) + +.macro LOAD_REGS src:req, regs_ofs:req, regs:vararg +.irp reg, \regs + REG_NUM reg_num \reg + mov (\regs_ofs + reg_num * WORD_SIZE)(\src), \reg +.endr +.endm + +.macro STORE_REGS dst:req, regs_ofs:req, regs:vararg +.irp reg, \regs + REG_NUM reg_num \reg + mov \reg, (\regs_ofs + reg_num * WORD_SIZE)(\dst) +.endr +.endm + +.macro POP_REGS dst:req, regs_ofs:req, regs:vararg +.irp reg, \regs + REG_NUM reg_num \reg + pop (\regs_ofs + reg_num * WORD_SIZE)(\dst) +.endr +.endm + +.macro CLEAR_REGS regs:vararg +.irp reg, \regs + xorl \reg, \reg +.endr +.endm + #endif /* __ASSEMBLER__ */ #endif /* __KVM_X86_VMENTER_H */ diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 7e4dc17fc0b8..4b7aaa7430fb 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -2,35 +2,12 @@ #include #include #include -#include #include #include #include #include "kvm-asm-offsets.h" #include "vmenter.h" -#define WORD_SIZE (BITS_PER_LONG / 8) - -#define VCPU_RAX (VMX_vcpu_arch_regs + __VCPU_REGS_RAX * WORD_SIZE) -#define VCPU_RCX (VMX_vcpu_arch_regs + __VCPU_REGS_RCX * WORD_SIZE) -#define VCPU_RDX (VMX_vcpu_arch_regs + __VCPU_REGS_RDX * WORD_SIZE) -#define VCPU_RBX (VMX_vcpu_arch_regs + __VCPU_REGS_RBX * WORD_SIZE) -/* Intentionally omit RSP as it's context switched by hardware */ -#define VCPU_RBP (VMX_vcpu_arch_regs + __VCPU_REGS_RBP * WORD_SIZE) -#define VCPU_RSI (VMX_vcpu_arch_regs + __VCPU_REGS_RSI * WORD_SIZE) -#define VCPU_RDI (VMX_vcpu_arch_regs + __VCPU_REGS_RDI * WORD_SIZE) - -#ifdef CONFIG_X86_64 -#define VCPU_R8 (VMX_vcpu_arch_regs + __VCPU_REGS_R8 * WORD_SIZE) -#define VCPU_R9 (VMX_vcpu_arch_regs + __VCPU_REGS_R9 * WORD_SIZE) -#define VCPU_R10 (VMX_vcpu_arch_regs + __VCPU_REGS_R10 * WORD_SIZE) -#define VCPU_R11 (VMX_vcpu_arch_regs + __VCPU_REGS_R11 * WORD_SIZE) -#define VCPU_R12 (VMX_vcpu_arch_regs + __VCPU_REGS_R12 * WORD_SIZE) -#define VCPU_R13 (VMX_vcpu_arch_regs + __VCPU_REGS_R13 * WORD_SIZE) -#define VCPU_R14 (VMX_vcpu_arch_regs + __VCPU_REGS_R14 * WORD_SIZE) -#define VCPU_R15 (VMX_vcpu_arch_regs + __VCPU_REGS_R15 * WORD_SIZE) -#endif - .macro VMX_DO_EVENT_IRQOFF call_insn call_target /* * Unconditionally create a stack frame, getting the correct RSP on the @@ -114,25 +91,18 @@ SYM_FUNC_START(__vmx_vcpu_run) * an LFENCE to stop speculation from skipping the wrmsr. */ - /* Load guest registers. Don't clobber flags. */ - mov VCPU_RAX(%_ASM_DI), %_ASM_AX - mov VCPU_RCX(%_ASM_DI), %_ASM_CX - mov VCPU_RDX(%_ASM_DI), %_ASM_DX - mov VCPU_RBX(%_ASM_DI), %_ASM_BX - mov VCPU_RBP(%_ASM_DI), %_ASM_BP - mov VCPU_RSI(%_ASM_DI), %_ASM_SI + /* + * Load guest registers. Don't clobber flags. Intentionally omit + * %_ASM_SP as it's context switched by hardware + */ + LOAD_REGS %_ASM_DI, VMX_vcpu_arch_regs, \ + %_ASM_AX, %_ASM_CX, %_ASM_DX, %_ASM_BX, %_ASM_BP, %_ASM_SI #ifdef CONFIG_X86_64 - mov VCPU_R8 (%_ASM_DI), %r8 - mov VCPU_R9 (%_ASM_DI), %r9 - mov VCPU_R10(%_ASM_DI), %r10 - mov VCPU_R11(%_ASM_DI), %r11 - mov VCPU_R12(%_ASM_DI), %r12 - mov VCPU_R13(%_ASM_DI), %r13 - mov VCPU_R14(%_ASM_DI), %r14 - mov VCPU_R15(%_ASM_DI), %r15 + LOAD_REGS %_ASM_DI, VMX_vcpu_arch_regs, \ + %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15 #endif /* Load guest RDI. This kills the @vmx pointer! */ - mov VCPU_RDI(%_ASM_DI), %_ASM_DI + LOAD_REGS %_ASM_DI, VMX_vcpu_arch_regs, %_ASM_DI /* * Note, ALTERNATIVE_2 works in reverse order. If CLEAR_CPU_BUF_VM is @@ -187,23 +157,16 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) /* Reload @vmx to RDI. */ mov 2*WORD_SIZE(%_ASM_SP), %_ASM_DI - /* Save all guest registers, including RDI from the stack */ - mov %_ASM_AX, VCPU_RAX(%_ASM_DI) - mov %_ASM_CX, VCPU_RCX(%_ASM_DI) - mov %_ASM_DX, VCPU_RDX(%_ASM_DI) - mov %_ASM_BX, VCPU_RBX(%_ASM_DI) - mov %_ASM_BP, VCPU_RBP(%_ASM_DI) - mov %_ASM_SI, VCPU_RSI(%_ASM_DI) - pop VCPU_RDI(%_ASM_DI) + /* + * Save all guest registers, including RDI from the stack. Intentionally + * omit %_ASM_SP as it's context switched by hardware + */ + STORE_REGS %_ASM_DI, VMX_vcpu_arch_regs, \ + %_ASM_AX, %_ASM_CX, %_ASM_DX, %_ASM_BX, %_ASM_BP, %_ASM_SI + POP_REGS %_ASM_DI, VMX_vcpu_arch_regs, %_ASM_DI #ifdef CONFIG_X86_64 - mov %r8, VCPU_R8 (%_ASM_DI) - mov %r9, VCPU_R9 (%_ASM_DI) - mov %r10, VCPU_R10(%_ASM_DI) - mov %r11, VCPU_R11(%_ASM_DI) - mov %r12, VCPU_R12(%_ASM_DI) - mov %r13, VCPU_R13(%_ASM_DI) - mov %r14, VCPU_R14(%_ASM_DI) - mov %r15, VCPU_R15(%_ASM_DI) + STORE_REGS %_ASM_DI, VMX_vcpu_arch_regs, \ + %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15 #endif /* Clear return value to indicate VM-Exit (as opposed to VM-Fail). */ @@ -220,21 +183,9 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) * VM-Exit and RBX is explicitly loaded with 0 or 1 to hold the return * value. */ - xor %eax, %eax - xor %ecx, %ecx - xor %edx, %edx - xor %ebp, %ebp - xor %esi, %esi - xor %edi, %edi + CLEAR_REGS %eax, %ecx, %edx, %ebp, %esi, %edi #ifdef CONFIG_X86_64 - xor %r8d, %r8d - xor %r9d, %r9d - xor %r10d, %r10d - xor %r11d, %r11d - xor %r12d, %r12d - xor %r13d, %r13d - xor %r14d, %r14d - xor %r15d, %r15d + CLEAR_REGS %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d #endif /* From 8d4fb56794aa5c614574a678db679b2d407d8d3c Mon Sep 17 00:00:00 2001 From: "Chang S. Bae" Date: Fri, 15 May 2026 13:27:33 -0400 Subject: [PATCH 2250/5214] KVM: SVM: Macrofy GPR swapping in __svm_vcpu_run() Convert the register save/restore sequences in the SVM entry into macros, following the VMX code. Drop the now-redundant register offset defines. No functional change intended. Signed-off-by: Chang S. Bae Link: https://patch.msgid.link/20260512011502.53072-3-chang.seok.bae@intel.com/ Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/vmenter.S | 83 +++++++++----------------------------- 1 file changed, 19 insertions(+), 64 deletions(-) diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 6a91e1383e8f..06a8cb58f187 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -8,26 +8,6 @@ #include "kvm-asm-offsets.h" #include "vmenter.h" -/* Intentionally omit RAX as it's context switched by hardware */ -#define VCPU_RCX (SVM_vcpu_arch_regs + __VCPU_REGS_RCX * WORD_SIZE) -#define VCPU_RDX (SVM_vcpu_arch_regs + __VCPU_REGS_RDX * WORD_SIZE) -#define VCPU_RBX (SVM_vcpu_arch_regs + __VCPU_REGS_RBX * WORD_SIZE) -/* Intentionally omit RSP as it's context switched by hardware */ -#define VCPU_RBP (SVM_vcpu_arch_regs + __VCPU_REGS_RBP * WORD_SIZE) -#define VCPU_RSI (SVM_vcpu_arch_regs + __VCPU_REGS_RSI * WORD_SIZE) -#define VCPU_RDI (SVM_vcpu_arch_regs + __VCPU_REGS_RDI * WORD_SIZE) - -#ifdef CONFIG_X86_64 -#define VCPU_R8 (SVM_vcpu_arch_regs + __VCPU_REGS_R8 * WORD_SIZE) -#define VCPU_R9 (SVM_vcpu_arch_regs + __VCPU_REGS_R9 * WORD_SIZE) -#define VCPU_R10 (SVM_vcpu_arch_regs + __VCPU_REGS_R10 * WORD_SIZE) -#define VCPU_R11 (SVM_vcpu_arch_regs + __VCPU_REGS_R11 * WORD_SIZE) -#define VCPU_R12 (SVM_vcpu_arch_regs + __VCPU_REGS_R12 * WORD_SIZE) -#define VCPU_R13 (SVM_vcpu_arch_regs + __VCPU_REGS_R13 * WORD_SIZE) -#define VCPU_R14 (SVM_vcpu_arch_regs + __VCPU_REGS_R14 * WORD_SIZE) -#define VCPU_R15 (SVM_vcpu_arch_regs + __VCPU_REGS_R15 * WORD_SIZE) -#endif - #define SVM_vmcb01_pa (SVM_vmcb01 + KVM_VMCB_pa) .section .noinstr.text, "ax" @@ -108,23 +88,17 @@ SYM_FUNC_START(__svm_vcpu_run) mov SVM_current_vmcb(%_ASM_DI), %_ASM_AX mov KVM_VMCB_pa(%_ASM_AX), %_ASM_AX - /* Load guest registers. */ - mov VCPU_RCX(%_ASM_DI), %_ASM_CX - mov VCPU_RDX(%_ASM_DI), %_ASM_DX - mov VCPU_RBX(%_ASM_DI), %_ASM_BX - mov VCPU_RBP(%_ASM_DI), %_ASM_BP - mov VCPU_RSI(%_ASM_DI), %_ASM_SI + /* + * Load guest registers. Intentionally omit %_ASM_AX and %_ASM_SP as + * context switched by hardware + */ + LOAD_REGS %_ASM_DI, SVM_vcpu_arch_regs, \ + %_ASM_CX, %_ASM_DX, %_ASM_BX, %_ASM_BP, %_ASM_SI #ifdef CONFIG_X86_64 - mov VCPU_R8 (%_ASM_DI), %r8 - mov VCPU_R9 (%_ASM_DI), %r9 - mov VCPU_R10(%_ASM_DI), %r10 - mov VCPU_R11(%_ASM_DI), %r11 - mov VCPU_R12(%_ASM_DI), %r12 - mov VCPU_R13(%_ASM_DI), %r13 - mov VCPU_R14(%_ASM_DI), %r14 - mov VCPU_R15(%_ASM_DI), %r15 + LOAD_REGS %_ASM_DI, SVM_vcpu_arch_regs, \ + %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15 #endif - mov VCPU_RDI(%_ASM_DI), %_ASM_DI + LOAD_REGS %_ASM_DI, SVM_vcpu_arch_regs, %_ASM_DI /* Clobbers EFLAGS.ZF */ SVM_CLEAR_CPU_BUFFERS @@ -135,22 +109,15 @@ SYM_FUNC_START(__svm_vcpu_run) /* Pop @svm to RAX while it's the only available register. */ pop %_ASM_AX - /* Save all guest registers. */ - mov %_ASM_CX, VCPU_RCX(%_ASM_AX) - mov %_ASM_DX, VCPU_RDX(%_ASM_AX) - mov %_ASM_BX, VCPU_RBX(%_ASM_AX) - mov %_ASM_BP, VCPU_RBP(%_ASM_AX) - mov %_ASM_SI, VCPU_RSI(%_ASM_AX) - mov %_ASM_DI, VCPU_RDI(%_ASM_AX) + /* + * Save all guest registers. Intentionally omit %_ASM_AX and %_ASM_SP as + * context switched by hardware + */ + STORE_REGS %_ASM_AX, SVM_vcpu_arch_regs, \ + %_ASM_CX, %_ASM_DX, %_ASM_BX, %_ASM_BP, %_ASM_SI, %_ASM_DI #ifdef CONFIG_X86_64 - mov %r8, VCPU_R8 (%_ASM_AX) - mov %r9, VCPU_R9 (%_ASM_AX) - mov %r10, VCPU_R10(%_ASM_AX) - mov %r11, VCPU_R11(%_ASM_AX) - mov %r12, VCPU_R12(%_ASM_AX) - mov %r13, VCPU_R13(%_ASM_AX) - mov %r14, VCPU_R14(%_ASM_AX) - mov %r15, VCPU_R15(%_ASM_AX) + STORE_REGS %_ASM_AX, SVM_vcpu_arch_regs, \ + %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15 #endif /* @svm can stay in RDI from now on. */ @@ -193,21 +160,9 @@ SYM_FUNC_START(__svm_vcpu_run) * free. RSP and RAX are exempt as they are restored by hardware * during VM-Exit. */ - xor %ecx, %ecx - xor %edx, %edx - xor %ebx, %ebx - xor %ebp, %ebp - xor %esi, %esi - xor %edi, %edi + CLEAR_REGS %ecx, %edx, %ebx, %ebp, %esi, %edi #ifdef CONFIG_X86_64 - xor %r8d, %r8d - xor %r9d, %r9d - xor %r10d, %r10d - xor %r11d, %r11d - xor %r12d, %r12d - xor %r13d, %r13d - xor %r14d, %r14d - xor %r15d, %r15d + CLEAR_REGS %r8d, %r9d, %r10d, %r11d, %r12d, %r13d, %r14d, %r15d #endif /* "Pop" @enter_flags. */ From e2e664812f7558e5a999271db7d17a391862d701 Mon Sep 17 00:00:00 2001 From: "Chang S. Bae" Date: Fri, 15 May 2026 13:27:34 -0400 Subject: [PATCH 2251/5214] KVM: SEV: Macrofy GPR swapping in __svm_sev_es_vcpu_run() Convert the SEV-ES entry code to use macros for saving guest GPRs, following VMX/SVM paths. Drop now-unused register offsets and __VCPU_REGS_* defines. No functional change intended. Signed-off-by: Chang S. Bae Link: https://patch.msgid.link/20260512011502.53072-4-chang.seok.bae@intel.com/ Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 16 ++++++++-------- arch/x86/include/asm/kvm_vcpu_regs.h | 11 ----------- arch/x86/kvm/svm/vmenter.S | 21 ++------------------- 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c470e40a00aa..aa77ccb7aaa5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -182,14 +182,14 @@ enum kvm_reg { VCPU_REGS_RSI = __VCPU_REGS_RSI, VCPU_REGS_RDI = __VCPU_REGS_RDI, #ifdef CONFIG_X86_64 - VCPU_REGS_R8 = __VCPU_REGS_R8, - VCPU_REGS_R9 = __VCPU_REGS_R9, - VCPU_REGS_R10 = __VCPU_REGS_R10, - VCPU_REGS_R11 = __VCPU_REGS_R11, - VCPU_REGS_R12 = __VCPU_REGS_R12, - VCPU_REGS_R13 = __VCPU_REGS_R13, - VCPU_REGS_R14 = __VCPU_REGS_R14, - VCPU_REGS_R15 = __VCPU_REGS_R15, + VCPU_REGS_R8 = 8, + VCPU_REGS_R9, + VCPU_REGS_R10, + VCPU_REGS_R11, + VCPU_REGS_R12, + VCPU_REGS_R13, + VCPU_REGS_R14, + VCPU_REGS_R15, #endif VCPU_REGS_RIP, NR_VCPU_REGS, diff --git a/arch/x86/include/asm/kvm_vcpu_regs.h b/arch/x86/include/asm/kvm_vcpu_regs.h index 650fabf5871d..d9f1528fab2b 100644 --- a/arch/x86/include/asm/kvm_vcpu_regs.h +++ b/arch/x86/include/asm/kvm_vcpu_regs.h @@ -11,17 +11,6 @@ #define __VCPU_REGS_RSI 6 #define __VCPU_REGS_RDI 7 -#ifdef CONFIG_X86_64 -#define __VCPU_REGS_R8 8 -#define __VCPU_REGS_R9 9 -#define __VCPU_REGS_R10 10 -#define __VCPU_REGS_R11 11 -#define __VCPU_REGS_R12 12 -#define __VCPU_REGS_R13 13 -#define __VCPU_REGS_R14 14 -#define __VCPU_REGS_R15 15 -#endif - #ifdef __ASSEMBLER__ #define REG_NUM_INVALID 100 diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 06a8cb58f187..99701892f8ec 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -211,18 +211,7 @@ SYM_FUNC_END(__svm_vcpu_run) #ifdef CONFIG_KVM_AMD_SEV - -#ifdef CONFIG_X86_64 #define SEV_ES_GPRS_BASE 0x300 -#define SEV_ES_RBX (SEV_ES_GPRS_BASE + __VCPU_REGS_RBX * WORD_SIZE) -#define SEV_ES_RBP (SEV_ES_GPRS_BASE + __VCPU_REGS_RBP * WORD_SIZE) -#define SEV_ES_RSI (SEV_ES_GPRS_BASE + __VCPU_REGS_RSI * WORD_SIZE) -#define SEV_ES_RDI (SEV_ES_GPRS_BASE + __VCPU_REGS_RDI * WORD_SIZE) -#define SEV_ES_R12 (SEV_ES_GPRS_BASE + __VCPU_REGS_R12 * WORD_SIZE) -#define SEV_ES_R13 (SEV_ES_GPRS_BASE + __VCPU_REGS_R13 * WORD_SIZE) -#define SEV_ES_R14 (SEV_ES_GPRS_BASE + __VCPU_REGS_R14 * WORD_SIZE) -#define SEV_ES_R15 (SEV_ES_GPRS_BASE + __VCPU_REGS_R15 * WORD_SIZE) -#endif /** * __svm_sev_es_vcpu_run - Run a SEV-ES vCPU via a transition to SVM guest mode @@ -237,19 +226,13 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) * Except for RAX and RSP, all GPRs are restored on #VMEXIT, but not * saved on VMRUN. */ - mov %rbp, SEV_ES_RBP (%rdx) - mov %r15, SEV_ES_R15 (%rdx) - mov %r14, SEV_ES_R14 (%rdx) - mov %r13, SEV_ES_R13 (%rdx) - mov %r12, SEV_ES_R12 (%rdx) - mov %rbx, SEV_ES_RBX (%rdx) + STORE_REGS %rdx, SEV_ES_GPRS_BASE, %rbp, %r15, %r14, %r13, %r12, %rbx /* * Save volatile registers that hold arguments that are needed after * #VMEXIT (RDI=@svm and RSI=@enter_flags). */ - mov %rdi, SEV_ES_RDI (%rdx) - mov %rsi, SEV_ES_RSI (%rdx) + STORE_REGS %rdx, SEV_ES_GPRS_BASE, %rdi, %rsi /* Clobbers RAX, RCX, and RDX (@hostsa), consumes RDI (@svm). */ RESTORE_GUEST_SPEC_CTRL From 47ceab218c827cd5861bd1615763433a870a2d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 18:48:47 +0200 Subject: [PATCH 2252/5214] Input: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260515164848.497608-2-u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/adafruit-seesaw.c | 2 +- drivers/input/joystick/as5011.c | 2 +- drivers/input/joystick/qwiic-joystick.c | 2 +- drivers/input/keyboard/adp5588-keys.c | 4 ++-- drivers/input/keyboard/cap11xx.c | 14 +++++++------- drivers/input/keyboard/cypress-sf.c | 2 +- drivers/input/keyboard/dlink-dir685-touchkeys.c | 2 +- drivers/input/keyboard/lm8323.c | 2 +- drivers/input/keyboard/lm8333.c | 2 +- drivers/input/keyboard/max7359_keypad.c | 2 +- drivers/input/keyboard/mpr121_touchkey.c | 2 +- drivers/input/keyboard/qt1070.c | 2 +- drivers/input/keyboard/qt2160.c | 2 +- drivers/input/keyboard/tca8418_keypad.c | 2 +- drivers/input/keyboard/tm2-touchkey.c | 2 +- drivers/input/misc/ad714x-i2c.c | 10 +++++----- drivers/input/misc/adxl34x-i2c.c | 2 +- drivers/input/misc/apanel.c | 2 +- drivers/input/misc/atmel_captouch.c | 2 +- drivers/input/misc/bma150.c | 6 +++--- drivers/input/misc/cma3000_d0x_i2c.c | 2 +- drivers/input/misc/da7280.c | 2 +- drivers/input/misc/drv260x.c | 8 ++++---- drivers/input/misc/drv2665.c | 2 +- drivers/input/misc/drv2667.c | 2 +- drivers/input/misc/kxtj9.c | 2 +- drivers/input/misc/mma8450.c | 2 +- drivers/input/misc/pcf8574_keypad.c | 2 +- drivers/input/mouse/cyapa.c | 2 +- drivers/input/mouse/elan_i2c_core.c | 2 +- drivers/input/mouse/synaptics_i2c.c | 2 +- drivers/input/rmi4/rmi_i2c.c | 2 +- drivers/input/rmi4/rmi_smbus.c | 2 +- drivers/input/touchscreen/ad7879-i2c.c | 4 ++-- drivers/input/touchscreen/ar1021_i2c.c | 2 +- drivers/input/touchscreen/atmel_mxt_ts.c | 10 +++++----- drivers/input/touchscreen/auo-pixcir-ts.c | 2 +- drivers/input/touchscreen/bu21013_ts.c | 2 +- drivers/input/touchscreen/bu21029_ts.c | 2 +- drivers/input/touchscreen/cy8ctma140.c | 2 +- drivers/input/touchscreen/cy8ctmg110_ts.c | 2 +- drivers/input/touchscreen/cyttsp5.c | 2 +- drivers/input/touchscreen/cyttsp_i2c.c | 2 +- drivers/input/touchscreen/eeti_ts.c | 2 +- drivers/input/touchscreen/egalax_ts.c | 2 +- drivers/input/touchscreen/elants_i2c.c | 6 +++--- drivers/input/touchscreen/exc3000.c | 8 ++++---- drivers/input/touchscreen/goodix.c | 2 +- drivers/input/touchscreen/hideep.c | 2 +- drivers/input/touchscreen/himax_hx83112b.c | 4 ++-- drivers/input/touchscreen/hynitron-cst816x.c | 2 +- drivers/input/touchscreen/ili210x.c | 8 ++++---- drivers/input/touchscreen/ilitek_ts_i2c.c | 2 +- drivers/input/touchscreen/max11801_ts.c | 2 +- drivers/input/touchscreen/melfas_mip4.c | 2 +- drivers/input/touchscreen/migor_ts.c | 2 +- drivers/input/touchscreen/mms114.c | 2 +- drivers/input/touchscreen/novatek-nvt-ts.c | 4 ++-- drivers/input/touchscreen/pixcir_i2c_ts.c | 4 ++-- drivers/input/touchscreen/raydium_i2c_ts.c | 4 ++-- drivers/input/touchscreen/rohm_bu21023.c | 2 +- drivers/input/touchscreen/s6sy761.c | 2 +- drivers/input/touchscreen/silead.c | 12 ++++++------ drivers/input/touchscreen/sis_i2c.c | 4 ++-- drivers/input/touchscreen/st1232.c | 4 ++-- drivers/input/touchscreen/stmfts.c | 2 +- drivers/input/touchscreen/tsc2004.c | 2 +- drivers/input/touchscreen/tsc2007_core.c | 2 +- drivers/input/touchscreen/wacom_i2c.c | 2 +- drivers/input/touchscreen/wdt87xx_i2c.c | 2 +- drivers/input/touchscreen/zet6223.c | 2 +- drivers/input/touchscreen/zforce_ts.c | 2 +- 72 files changed, 112 insertions(+), 112 deletions(-) diff --git a/drivers/input/joystick/adafruit-seesaw.c b/drivers/input/joystick/adafruit-seesaw.c index c248c15b849d..5835ed1e29e1 100644 --- a/drivers/input/joystick/adafruit-seesaw.c +++ b/drivers/input/joystick/adafruit-seesaw.c @@ -304,7 +304,7 @@ static int seesaw_probe(struct i2c_client *client) } static const struct i2c_device_id seesaw_id_table[] = { - { SEESAW_DEVICE_NAME }, + { .name = SEESAW_DEVICE_NAME }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(i2c, seesaw_id_table); diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 7b4d61626898..5fca92bf9df0 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -337,7 +337,7 @@ static void as5011_remove(struct i2c_client *client) } static const struct i2c_device_id as5011_id[] = { - { MODULE_DEVICE_ALIAS }, + { .name = MODULE_DEVICE_ALIAS }, { } }; MODULE_DEVICE_TABLE(i2c, as5011_id); diff --git a/drivers/input/joystick/qwiic-joystick.c b/drivers/input/joystick/qwiic-joystick.c index 6f989d00d3ec..c471afc7862b 100644 --- a/drivers/input/joystick/qwiic-joystick.c +++ b/drivers/input/joystick/qwiic-joystick.c @@ -126,7 +126,7 @@ MODULE_DEVICE_TABLE(of, of_qwiic_match); #endif /* CONFIG_OF */ static const struct i2c_device_id qwiic_id_table[] = { - { KBUILD_MODNAME }, + { .name = KBUILD_MODNAME }, { } }; MODULE_DEVICE_TABLE(i2c, qwiic_id_table); diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 414fbef4abf9..8d14d0f69d4e 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -842,8 +842,8 @@ static int adp5588_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume); static const struct i2c_device_id adp5588_id[] = { - { "adp5588-keys" }, - { "adp5587-keys" }, + { .name = "adp5588-keys" }, + { .name = "adp5587-keys" }, { } }; MODULE_DEVICE_TABLE(i2c, adp5588_id); diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 0c17cbaa3d27..2447c1ae2166 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -651,13 +651,13 @@ static const struct of_device_id cap11xx_dt_ids[] = { MODULE_DEVICE_TABLE(of, cap11xx_dt_ids); static const struct i2c_device_id cap11xx_i2c_ids[] = { - { "cap1106", (kernel_ulong_t)&cap1106_model }, - { "cap1126", (kernel_ulong_t)&cap1126_model }, - { "cap1188", (kernel_ulong_t)&cap1188_model }, - { "cap1203", (kernel_ulong_t)&cap1203_model }, - { "cap1206", (kernel_ulong_t)&cap1206_model }, - { "cap1293", (kernel_ulong_t)&cap1293_model }, - { "cap1298", (kernel_ulong_t)&cap1298_model }, + { .name = "cap1106", .driver_data = (kernel_ulong_t)&cap1106_model }, + { .name = "cap1126", .driver_data = (kernel_ulong_t)&cap1126_model }, + { .name = "cap1188", .driver_data = (kernel_ulong_t)&cap1188_model }, + { .name = "cap1203", .driver_data = (kernel_ulong_t)&cap1203_model }, + { .name = "cap1206", .driver_data = (kernel_ulong_t)&cap1206_model }, + { .name = "cap1293", .driver_data = (kernel_ulong_t)&cap1293_model }, + { .name = "cap1298", .driver_data = (kernel_ulong_t)&cap1298_model }, { } }; MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids); diff --git a/drivers/input/keyboard/cypress-sf.c b/drivers/input/keyboard/cypress-sf.c index 335b72efc5aa..4b2ae07f315c 100644 --- a/drivers/input/keyboard/cypress-sf.c +++ b/drivers/input/keyboard/cypress-sf.c @@ -209,7 +209,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops, cypress_sf_suspend, cypress_sf_resume); static const struct i2c_device_id cypress_sf_id_table[] = { - { CYPRESS_SF_DEV_NAME }, + { .name = CYPRESS_SF_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table); diff --git a/drivers/input/keyboard/dlink-dir685-touchkeys.c b/drivers/input/keyboard/dlink-dir685-touchkeys.c index 4184dd2eaeeb..bf94367f6b64 100644 --- a/drivers/input/keyboard/dlink-dir685-touchkeys.c +++ b/drivers/input/keyboard/dlink-dir685-touchkeys.c @@ -128,7 +128,7 @@ static int dir685_tk_probe(struct i2c_client *client) } static const struct i2c_device_id dir685_tk_id[] = { - { "dir685tk" }, + { .name = "dir685tk" }, { } }; MODULE_DEVICE_TABLE(i2c, dir685_tk_id); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index d9df10484755..8eac63b2d304 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -788,7 +788,7 @@ static int lm8323_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume); static const struct i2c_device_id lm8323_id[] = { - { "lm8323" }, + { .name = "lm8323" }, { } }; diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index 384baabf9924..34be5b2e476d 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -194,7 +194,7 @@ static int lm8333_probe(struct i2c_client *client) } static const struct i2c_device_id lm8333_id[] = { - { "lm8333" }, + { .name = "lm8333" }, { } }; MODULE_DEVICE_TABLE(i2c, lm8333_id); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index c10726b5e4d1..f8fb44bc4915 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -270,7 +270,7 @@ static int max7359_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume); static const struct i2c_device_id max7359_ids[] = { - { "max7359" }, + { .name = "max7359" }, { } }; MODULE_DEVICE_TABLE(i2c, max7359_ids); diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 47edc161ec77..a1a102855590 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -322,7 +322,7 @@ static int mpr_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); static const struct i2c_device_id mpr121_id[] = { - { "mpr121_touchkey" }, + { .name = "mpr121_touchkey" }, { } }; MODULE_DEVICE_TABLE(i2c, mpr121_id); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index b255b997e279..e31cb115a954 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -233,7 +233,7 @@ static int qt1070_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(qt1070_pm_ops, qt1070_suspend, qt1070_resume); static const struct i2c_device_id qt1070_id[] = { - { "qt1070" }, + { .name = "qt1070" }, { } }; MODULE_DEVICE_TABLE(i2c, qt1070_id); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 53f5255fd19d..e64adaca10ff 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -393,7 +393,7 @@ static int qt2160_probe(struct i2c_client *client) } static const struct i2c_device_id qt2160_idtable[] = { - { "qt2160" }, + { .name = "qt2160" }, { } }; diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 68c0afafee7b..eb5be644f236 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -354,7 +354,7 @@ static int tca8418_keypad_probe(struct i2c_client *client) } static const struct i2c_device_id tca8418_id[] = { - { "tca8418", 8418, }, + { .name = "tca8418", .driver_data = 8418 }, { } }; MODULE_DEVICE_TABLE(i2c, tca8418_id); diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 55d699d9037d..967bbe553c06 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -326,7 +326,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops, tm2_touchkey_suspend, tm2_touchkey_resume); static const struct i2c_device_id tm2_touchkey_id_table[] = { - { TM2_TOUCHKEY_DEV_NAME }, + { .name = TM2_TOUCHKEY_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table); diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 2adb7a058362..c3cb561512ed 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -72,11 +72,11 @@ static int ad714x_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ad714x_id[] = { - { "ad7142_captouch" }, - { "ad7143_captouch" }, - { "ad7147_captouch" }, - { "ad7147a_captouch" }, - { "ad7148_captouch" }, + { .name = "ad7142_captouch" }, + { .name = "ad7143_captouch" }, + { .name = "ad7147_captouch" }, + { .name = "ad7147a_captouch" }, + { .name = "ad7148_captouch" }, { } }; MODULE_DEVICE_TABLE(i2c, ad714x_id); diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 5ea0ce42a507..84665c612f3c 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -96,7 +96,7 @@ static int adxl34x_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl34x_id[] = { - { "adxl34x" }, + { .name = "adxl34x" }, { } }; diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c index d43aebd785b7..3eb02a64acfb 100644 --- a/drivers/input/misc/apanel.c +++ b/drivers/input/misc/apanel.c @@ -192,7 +192,7 @@ static void apanel_shutdown(struct i2c_client *client) } static const struct i2c_device_id apanel_id[] = { - { "fujitsu_apanel" }, + { .name = "fujitsu_apanel" }, { } }; MODULE_DEVICE_TABLE(i2c, apanel_id); diff --git a/drivers/input/misc/atmel_captouch.c b/drivers/input/misc/atmel_captouch.c index f9744cf0ca80..fc4abea68479 100644 --- a/drivers/input/misc/atmel_captouch.c +++ b/drivers/input/misc/atmel_captouch.c @@ -257,7 +257,7 @@ static const struct of_device_id atmel_captouch_of_id[] = { MODULE_DEVICE_TABLE(of, atmel_captouch_of_id); static const struct i2c_device_id atmel_captouch_id[] = { - { "atmel_captouch" }, + { .name = "atmel_captouch" }, { } }; MODULE_DEVICE_TABLE(i2c, atmel_captouch_id); diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 4cc2a0dcaa75..0ef282932095 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -536,9 +536,9 @@ static int __maybe_unused bma150_resume(struct device *dev) static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); static const struct i2c_device_id bma150_id[] = { - { "bma150" }, - { "smb380" }, - { "bma023" }, + { .name = "bma150" }, + { .name = "smb380" }, + { .name = "bma023" }, { } }; diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index f892c5b1e4bd..14be6d4682d0 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -90,7 +90,7 @@ static const struct dev_pm_ops cma3000_i2c_pm_ops = { }; static const struct i2c_device_id cma3000_i2c_id[] = { - { "cma3000_d01" }, + { .name = "cma3000_d01" }, { } }; diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c index e4a605c6af15..77fcf868dca2 100644 --- a/drivers/input/misc/da7280.c +++ b/drivers/input/misc/da7280.c @@ -1305,7 +1305,7 @@ MODULE_DEVICE_TABLE(of, da7280_of_match); #endif static const struct i2c_device_id da7280_i2c_id[] = { - { "da7280", }, + { .name = "da7280" }, { } }; MODULE_DEVICE_TABLE(i2c, da7280_i2c_id); diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index e2089d6ac850..6c5c4c53753b 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -630,10 +630,10 @@ static int drv260x_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); static const struct i2c_device_id drv260x_id[] = { - { "drv2604" }, - { "drv2604l" }, - { "drv2605" }, - { "drv2605l" }, + { .name = "drv2604" }, + { .name = "drv2604l" }, + { .name = "drv2605" }, + { .name = "drv2605l" }, { } }; MODULE_DEVICE_TABLE(i2c, drv260x_id); diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index 46842cb8ddd7..4f8c1f69aa63 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -281,7 +281,7 @@ static int drv2665_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume); static const struct i2c_device_id drv2665_id[] = { - { "drv2665" }, + { .name = "drv2665" }, { } }; MODULE_DEVICE_TABLE(i2c, drv2665_id); diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index f952a24ec595..97309984fa7c 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -458,7 +458,7 @@ static int drv2667_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume); static const struct i2c_device_id drv2667_id[] = { - { "drv2667" }, + { .name = "drv2667" }, { } }; MODULE_DEVICE_TABLE(i2c, drv2667_id); diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index eb9788ea5215..6b3495ba5763 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -525,7 +525,7 @@ static int kxtj9_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume); static const struct i2c_device_id kxtj9_id[] = { - { NAME }, + { .name = NAME }, { } }; diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 0c661140fb88..a2888d1ff58f 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -200,7 +200,7 @@ static int mma8450_probe(struct i2c_client *c) } static const struct i2c_device_id mma8450_id[] = { - { MMA8450_DRV_NAME }, + { .name = MMA8450_DRV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, mma8450_id); diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 14fc6c6cf699..a831fcc851f7 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -189,7 +189,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(pcf8574_kp_pm_ops, pcf8574_kp_suspend, pcf8574_kp_resume); static const struct i2c_device_id pcf8574_kp_id[] = { - { DRV_NAME }, + { .name = DRV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id); diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 6e0d956617a1..47000c30e4d8 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -1456,7 +1456,7 @@ static const struct dev_pm_ops cyapa_pm_ops = { }; static const struct i2c_device_id cyapa_id_table[] = { - { "cyapa" }, + { .name = "cyapa" }, { } }; MODULE_DEVICE_TABLE(i2c, cyapa_id_table); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index fee1796da3d0..294e775510d5 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1399,7 +1399,7 @@ static int elan_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume); static const struct i2c_device_id elan_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, elan_id); diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 6f3d5c33b807..d4cf982f1263 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -607,7 +607,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend, synaptics_i2c_resume); static const struct i2c_device_id synaptics_i2c_id_table[] = { - { "synaptics_i2c" }, + { .name = "synaptics_i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table); diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index 3c0c5fd44702..e11d0acb9b96 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -365,7 +365,7 @@ static const struct dev_pm_ops rmi_i2c_pm = { }; static const struct i2c_device_id rmi_id[] = { - { "rmi4_i2c" }, + { .name = "rmi4_i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, rmi_id); diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c index f3d0b40721df..6de68c602558 100644 --- a/drivers/input/rmi4/rmi_smbus.c +++ b/drivers/input/rmi4/rmi_smbus.c @@ -413,7 +413,7 @@ static const struct dev_pm_ops rmi_smb_pm = { }; static const struct i2c_device_id rmi_id[] = { - { "rmi4_smbus" }, + { .name = "rmi4_smbus" }, { } }; MODULE_DEVICE_TABLE(i2c, rmi_id); diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index e5b99312c3d9..c1ccdf86b74c 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -42,8 +42,8 @@ static int ad7879_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ad7879_id[] = { - { "ad7879" }, - { "ad7889" }, + { .name = "ad7879" }, + { .name = "ad7889" }, { } }; MODULE_DEVICE_TABLE(i2c, ad7879_id); diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index 8a588202447d..aa43fc1549df 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -164,7 +164,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume); static const struct i2c_device_id ar1021_i2c_id[] = { - { "ar1021" }, + { .name = "ar1021" }, { } }; MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a9e86ad7ed5e..74de06ef1894 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3410,11 +3410,11 @@ MODULE_DEVICE_TABLE(acpi, mxt_acpi_id); #endif static const struct i2c_device_id mxt_id[] = { - { "qt602240_ts" }, - { "atmel_mxt_ts" }, - { "atmel_mxt_tp" }, - { "maxtouch" }, - { "mXT224" }, + { .name = "qt602240_ts" }, + { .name = "atmel_mxt_ts" }, + { .name = "atmel_mxt_tp" }, + { .name = "maxtouch" }, + { .name = "mXT224" }, { } }; MODULE_DEVICE_TABLE(i2c, mxt_id); diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 401b2264f585..6c6ebebacba9 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -618,7 +618,7 @@ static int auo_pixcir_probe(struct i2c_client *client) } static const struct i2c_device_id auo_pixcir_idtable[] = { - { "auo_pixcir_ts" }, + { .name = "auo_pixcir_ts" }, { } }; MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable); diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 6baebb7ec089..d1bdccc56fc8 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -597,7 +597,7 @@ static int bu21013_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(bu21013_dev_pm_ops, bu21013_suspend, bu21013_resume); static const struct i2c_device_id bu21013_id[] = { - { DRIVER_TP }, + { .name = DRIVER_TP }, { } }; MODULE_DEVICE_TABLE(i2c, bu21013_id); diff --git a/drivers/input/touchscreen/bu21029_ts.c b/drivers/input/touchscreen/bu21029_ts.c index 68d9cd55ceeb..339fee1622fb 100644 --- a/drivers/input/touchscreen/bu21029_ts.c +++ b/drivers/input/touchscreen/bu21029_ts.c @@ -442,7 +442,7 @@ static int bu21029_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(bu21029_pm_ops, bu21029_suspend, bu21029_resume); static const struct i2c_device_id bu21029_ids[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, bu21029_ids); diff --git a/drivers/input/touchscreen/cy8ctma140.c b/drivers/input/touchscreen/cy8ctma140.c index 2d4b6e343203..0ac48eff3376 100644 --- a/drivers/input/touchscreen/cy8ctma140.c +++ b/drivers/input/touchscreen/cy8ctma140.c @@ -322,7 +322,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cy8ctma140_pm, cy8ctma140_suspend, cy8ctma140_resume); static const struct i2c_device_id cy8ctma140_idtable[] = { - { CY8CTMA140_NAME }, + { .name = CY8CTMA140_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, cy8ctma140_idtable); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index 54d6c4869eb0..33ad4f0512a0 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -267,7 +267,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); static const struct i2c_device_id cy8ctmg110_idtable[] = { - { CY8CTMG110_DRIVER_NAME, 1 }, + { .name = CY8CTMG110_DRIVER_NAME, .driver_data = 1 }, { } }; diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c index 47f4271395a6..73c397e44da4 100644 --- a/drivers/input/touchscreen/cyttsp5.c +++ b/drivers/input/touchscreen/cyttsp5.c @@ -938,7 +938,7 @@ static const struct of_device_id cyttsp5_of_match[] = { MODULE_DEVICE_TABLE(of, cyttsp5_of_match); static const struct i2c_device_id cyttsp5_i2c_id[] = { - { CYTTSP5_NAME }, + { .name = CYTTSP5_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index cb15600549cd..17524f7e944d 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -103,7 +103,7 @@ static int cyttsp_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id cyttsp_i2c_id[] = { - { CY_I2C_NAME }, + { .name = CY_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 492e19a28a74..b12602bc368e 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -266,7 +266,7 @@ static int eeti_ts_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); static const struct i2c_device_id eeti_ts_id[] = { - { "eeti_ts" }, + { .name = "eeti_ts" }, { } }; MODULE_DEVICE_TABLE(i2c, eeti_ts_id); diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index eb3cc2befcdf..76cf8085553f 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -219,7 +219,7 @@ static int egalax_ts_probe(struct i2c_client *client) } static const struct i2c_device_id egalax_ts_id[] = { - { "egalax_ts" }, + { .name = "egalax_ts" }, { } }; MODULE_DEVICE_TABLE(i2c, egalax_ts_id); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 835d91dff0a4..17175adcaebb 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1615,9 +1615,9 @@ static DEFINE_SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops, elants_i2c_suspend, elants_i2c_resume); static const struct i2c_device_id elants_i2c_id[] = { - { DEVICE_NAME, EKTH3500 }, - { "ekth3500", EKTH3500 }, - { "ektf3624", EKTF3624 }, + { .name = DEVICE_NAME, .driver_data = EKTH3500 }, + { .name = "ekth3500", .driver_data = EKTH3500 }, + { .name = "ektf3624", .driver_data = EKTF3624 }, { } }; MODULE_DEVICE_TABLE(i2c, elants_i2c_id); diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index 78c0911ba6e2..037bb35238a3 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -432,10 +432,10 @@ static int exc3000_probe(struct i2c_client *client) } static const struct i2c_device_id exc3000_id[] = { - { "exc3000", EETI_EXC3000 }, - { "exc80h60", EETI_EXC80H60 }, - { "exc80h84", EETI_EXC80H84 }, - { "exc81w32", EETI_EXC81W32 }, + { .name = "exc3000", .driver_data = EETI_EXC3000 }, + { .name = "exc80h60", .driver_data = EETI_EXC80H60 }, + { .name = "exc80h84", .driver_data = EETI_EXC80H84 }, + { .name = "exc81w32", .driver_data = EETI_EXC81W32 }, { } }; MODULE_DEVICE_TABLE(i2c, exc3000_id); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f8798d11ec03..2c48ad8ee2e6 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -1523,7 +1523,7 @@ static int goodix_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume); static const struct i2c_device_id goodix_ts_id[] = { - { "GDIX1001:00" }, + { .name = "GDIX1001:00" }, { } }; MODULE_DEVICE_TABLE(i2c, goodix_ts_id); diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c index 62041bcca83d..58e00c314ace 100644 --- a/drivers/input/touchscreen/hideep.c +++ b/drivers/input/touchscreen/hideep.c @@ -1081,7 +1081,7 @@ static int hideep_probe(struct i2c_client *client) } static const struct i2c_device_id hideep_i2c_id[] = { - { HIDEEP_I2C_NAME }, + { .name = HIDEEP_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, hideep_i2c_id); diff --git a/drivers/input/touchscreen/himax_hx83112b.c b/drivers/input/touchscreen/himax_hx83112b.c index 896a145ddb2b..0aa67dfacbad 100644 --- a/drivers/input/touchscreen/himax_hx83112b.c +++ b/drivers/input/touchscreen/himax_hx83112b.c @@ -406,8 +406,8 @@ static const struct himax_chip hx83112b_chip = { }; static const struct i2c_device_id himax_ts_id[] = { - { "hx83100a", (kernel_ulong_t)&hx83100a_chip }, - { "hx83112b", (kernel_ulong_t)&hx83112b_chip }, + { .name = "hx83100a", .driver_data = (kernel_ulong_t)&hx83100a_chip }, + { .name = "hx83112b", .driver_data = (kernel_ulong_t)&hx83112b_chip }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, himax_ts_id); diff --git a/drivers/input/touchscreen/hynitron-cst816x.c b/drivers/input/touchscreen/hynitron-cst816x.c index b64d7928e18f..47d9cd7412d1 100644 --- a/drivers/input/touchscreen/hynitron-cst816x.c +++ b/drivers/input/touchscreen/hynitron-cst816x.c @@ -226,7 +226,7 @@ static int cst816x_probe(struct i2c_client *client) } static const struct i2c_device_id cst816x_id[] = { - { .name = "cst816s", 0 }, + { .name = "cst816s" }, { } }; MODULE_DEVICE_TABLE(i2c, cst816x_id); diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 3bf524a6ee20..66ada7ffbc80 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -1049,10 +1049,10 @@ static int ili210x_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", (long)&ili210x_chip }, - { "ili2117", (long)&ili211x_chip }, - { "ili2120", (long)&ili212x_chip }, - { "ili251x", (long)&ili251x_chip }, + { .name = "ili210x", .driver_data = (long)&ili210x_chip }, + { .name = "ili2117", .driver_data = (long)&ili211x_chip }, + { .name = "ili2120", .driver_data = (long)&ili212x_chip }, + { .name = "ili251x", .driver_data = (long)&ili251x_chip }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c index 0706443792ba..3de0fbf8da38 100644 --- a/drivers/input/touchscreen/ilitek_ts_i2c.c +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c @@ -639,7 +639,7 @@ static int ilitek_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_suspend, ilitek_resume); static const struct i2c_device_id ilitek_ts_i2c_id[] = { - { ILITEK_TS_NAME }, + { .name = ILITEK_TS_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, ilitek_ts_i2c_id); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index f39633fc8dc2..3996e4a61ff7 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -213,7 +213,7 @@ static int max11801_ts_probe(struct i2c_client *client) } static const struct i2c_device_id max11801_ts_id[] = { - { "max11801" }, + { .name = "max11801" }, { } }; MODULE_DEVICE_TABLE(i2c, max11801_ts_id); diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 10fccf4e5bb1..fd6c0ac301a2 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -1536,7 +1536,7 @@ MODULE_DEVICE_TABLE(acpi, mip4_acpi_match); #endif static const struct i2c_device_id mip4_i2c_ids[] = { - { MIP4_DEVICE_NAME }, + { .name = MIP4_DEVICE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids); diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index 993d945dcd23..9d221e340404 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -211,7 +211,7 @@ static int migor_ts_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume); static const struct i2c_device_id migor_ts_id[] = { - { "migor_ts" }, + { .name = "migor_ts" }, { } }; MODULE_DEVICE_TABLE(i2c, migor_ts_id); diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index af462086a65c..9597214d9d3c 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -667,7 +667,7 @@ static int mms114_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); static const struct i2c_device_id mms114_id[] = { - { "mms114" }, + { .name = "mms114" }, { } }; MODULE_DEVICE_TABLE(i2c, mms114_id); diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c index 708bfb933ddd..0f771f681952 100644 --- a/drivers/input/touchscreen/novatek-nvt-ts.c +++ b/drivers/input/touchscreen/novatek-nvt-ts.c @@ -326,8 +326,8 @@ static const struct of_device_id nvt_ts_of_match[] = { MODULE_DEVICE_TABLE(of, nvt_ts_of_match); static const struct i2c_device_id nvt_ts_i2c_id[] = { - { "nt11205-ts", (unsigned long) &nvt_nt11205_ts_data }, - { "nt36672a-ts", (unsigned long) &nvt_nt36672a_ts_data }, + { .name = "nt11205-ts", .driver_data = (unsigned long)&nvt_nt11205_ts_data }, + { .name = "nt36672a-ts", .driver_data = (unsigned long)&nvt_nt36672a_ts_data }, { } }; MODULE_DEVICE_TABLE(i2c, nvt_ts_i2c_id); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index c6b3615c8775..1ca5920846df 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -580,8 +580,8 @@ static const struct pixcir_i2c_chip_data pixcir_tangoc_data = { }; static const struct i2c_device_id pixcir_i2c_ts_id[] = { - { "pixcir_ts", (unsigned long) &pixcir_ts_data }, - { "pixcir_tangoc", (unsigned long) &pixcir_tangoc_data }, + { .name = "pixcir_ts", .driver_data = (unsigned long)&pixcir_ts_data }, + { .name = "pixcir_tangoc", .driver_data = (unsigned long)&pixcir_tangoc_data }, { } }; MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index f2d33ad86fd2..0256055abcef 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -1219,8 +1219,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops, raydium_i2c_suspend, raydium_i2c_resume); static const struct i2c_device_id raydium_i2c_id[] = { - { "raydium_i2c" }, - { "rm32380" }, + { .name = "raydium_i2c" }, + { .name = "rm32380" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, raydium_i2c_id); diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index 295d8d75ba32..a5c06b7423c0 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -1144,7 +1144,7 @@ static int rohm_bu21023_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id rohm_bu21023_i2c_id[] = { - { BU21023_NAME }, + { .name = BU21023_NAME }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id); diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c index e1518a75a51b..0f24a9b73063 100644 --- a/drivers/input/touchscreen/s6sy761.c +++ b/drivers/input/touchscreen/s6sy761.c @@ -520,7 +520,7 @@ MODULE_DEVICE_TABLE(of, s6sy761_of_match); #endif static const struct i2c_device_id s6sy761_id[] = { - { "s6sy761" }, + { .name = "s6sy761" }, { } }; MODULE_DEVICE_TABLE(i2c, s6sy761_id); diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index 5ccc96764742..44d7141103f4 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -776,12 +776,12 @@ static int silead_ts_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(silead_ts_pm, silead_ts_suspend, silead_ts_resume); static const struct i2c_device_id silead_ts_id[] = { - { "gsl1680" }, - { "gsl1688" }, - { "gsl3670" }, - { "gsl3675" }, - { "gsl3692" }, - { "mssl1680" }, + { .name = "gsl1680" }, + { .name = "gsl1688" }, + { .name = "gsl3670" }, + { .name = "gsl3675" }, + { .name = "gsl3692" }, + { .name = "mssl1680" }, { } }; MODULE_DEVICE_TABLE(i2c, silead_ts_id); diff --git a/drivers/input/touchscreen/sis_i2c.c b/drivers/input/touchscreen/sis_i2c.c index a625f2ad809d..4fa93cd31ee4 100644 --- a/drivers/input/touchscreen/sis_i2c.c +++ b/drivers/input/touchscreen/sis_i2c.c @@ -374,8 +374,8 @@ MODULE_DEVICE_TABLE(of, sis_ts_dt_ids); #endif static const struct i2c_device_id sis_ts_id[] = { - { SIS_I2C_NAME }, - { "9200-ts" }, + { .name = SIS_I2C_NAME }, + { .name = "9200-ts" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, sis_ts_id); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 9b266927b7fe..c2dcfce5003d 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -453,8 +453,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, st1232_ts_suspend, st1232_ts_resume); static const struct i2c_device_id st1232_ts_id[] = { - { ST1232_TS_NAME, (unsigned long)&st1232_chip_info }, - { ST1633_TS_NAME, (unsigned long)&st1633_chip_info }, + { .name = ST1232_TS_NAME, .driver_data = (unsigned long)&st1232_chip_info }, + { .name = ST1633_TS_NAME, .driver_data = (unsigned long)&st1633_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, st1232_ts_id); diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 454f7e822e9c..972687797f82 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -837,7 +837,7 @@ MODULE_DEVICE_TABLE(of, stmfts_of_match); #endif static const struct i2c_device_id stmfts_id[] = { - { "stmfts" }, + { .name = "stmfts" }, { } }; MODULE_DEVICE_TABLE(i2c, stmfts_id); diff --git a/drivers/input/touchscreen/tsc2004.c b/drivers/input/touchscreen/tsc2004.c index 787f2caf4f73..130aeb735b65 100644 --- a/drivers/input/touchscreen/tsc2004.c +++ b/drivers/input/touchscreen/tsc2004.c @@ -43,7 +43,7 @@ static int tsc2004_probe(struct i2c_client *i2c) } static const struct i2c_device_id tsc2004_idtable[] = { - { "tsc2004" }, + { .name = "tsc2004" }, { } }; MODULE_DEVICE_TABLE(i2c, tsc2004_idtable); diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index f9b3d2598933..4a775c5df0ea 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -405,7 +405,7 @@ static int tsc2007_probe(struct i2c_client *client) } static const struct i2c_device_id tsc2007_idtable[] = { - { "tsc2007" }, + { .name = "tsc2007" }, { } }; diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index fd97a83f5664..dd7c2a8a831b 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -253,7 +253,7 @@ static int wacom_i2c_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume); static const struct i2c_device_id wacom_i2c_id[] = { - { "WAC_I2C_EMR" }, + { .name = "WAC_I2C_EMR" }, { } }; MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index bdaabb14dc8c..1af309252092 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -1140,7 +1140,7 @@ static int wdt87xx_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume); static const struct i2c_device_id wdt87xx_dev_id[] = { - { WDT87XX_NAME }, + { .name = WDT87XX_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id); diff --git a/drivers/input/touchscreen/zet6223.c b/drivers/input/touchscreen/zet6223.c index 943634ba9cd9..a341142f3708 100644 --- a/drivers/input/touchscreen/zet6223.c +++ b/drivers/input/touchscreen/zet6223.c @@ -236,7 +236,7 @@ static const struct of_device_id zet6223_of_match[] = { MODULE_DEVICE_TABLE(of, zet6223_of_match); static const struct i2c_device_id zet6223_id[] = { - { "zet6223" }, + { .name = "zet6223" }, { } }; MODULE_DEVICE_TABLE(i2c, zet6223_id); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index a360749fa076..4bfc9560bd6c 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -831,7 +831,7 @@ static int zforce_probe(struct i2c_client *client) } static const struct i2c_device_id zforce_idtable[] = { - { "zforce-ts" }, + { .name = "zforce-ts" }, { } }; MODULE_DEVICE_TABLE(i2c, zforce_idtable); From 332fbc03e1a8cb4c0ce6891cae2c286ddf031aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 18:51:34 +0200 Subject: [PATCH 2253/5214] Input: iqs5xx - drop unused i2c driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't make use of the value that was explicitly assigned to the .driver_data members. Drop the assignment. While touching the array, convert it to use named initialization which is easier to understand. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260515165135.498505-2-u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/iqs5xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 587665e4e6fd..b9bbe8b3eab8 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -1049,9 +1049,9 @@ static int iqs5xx_probe(struct i2c_client *client) } static const struct i2c_device_id iqs5xx_id[] = { - { "iqs550", 0 }, - { "iqs572", 1 }, - { "iqs525", 2 }, + { .name = "iqs550" }, + { .name = "iqs572" }, + { .name = "iqs525" }, { } }; MODULE_DEVICE_TABLE(i2c, iqs5xx_id); From 83fcf113ea9a03255d065c27b328ed3c1e816678 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:51:58 +0200 Subject: [PATCH 2254/5214] media: qcom: camss: csid-340: Switch to generic CSID_CFG/CTRL registers The former RDI-specific register definitions (CSID_RDI_CFG0/CTRL) are renamed to unified CSID_CFG0/CSID_CTRL variants, as their layout is interface agnostic. This refactoring provides the foundation for extending csid-340 with missing PIX interface/path support. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 0231985746ed..9eee23bd81c2 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -41,19 +41,20 @@ #define CSI2_RX_CFG1_MISR_EN BIT(6) #define CSI2_RX_CFG1_CGC_MODE BIT(7) -#define CSID_RDI_CFG0(rdi) (0x300 + 0x100 * (rdi)) -#define CSID_RDI_CFG0_BYTE_CNTR_EN BIT(0) -#define CSID_RDI_CFG0_TIMESTAMP_EN BIT(1) -#define CSID_RDI_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) -#define CSID_RDI_CFG0_DECODE_FORMAT_NOP CSID_RDI_CFG0_DECODE_FORMAT_MASK -#define CSID_RDI_CFG0_DT_MASK GENMASK(21, 16) -#define CSID_RDI_CFG0_VC_MASK GENMASK(23, 22) -#define CSID_RDI_CFG0_DTID_MASK GENMASK(28, 27) -#define CSID_RDI_CFG0_ENABLE BIT(31) +#define CSID_CFG0(iface) (0x300 + 0x100 * (iface)) +#define CSID_CFG0_BYTE_CNTR_EN BIT(0) +#define CSID_CFG0_TIMESTAMP_EN BIT(1) +#define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) +#define CSID_CFG0_DECODE_FORMAT_NOP CSID_CFG0_DECODE_FORMAT_MASK +#define CSID_CFG0_DT_MASK GENMASK(21, 16) +#define CSID_CFG0_VC_MASK GENMASK(23, 22) +#define CSID_CFG0_DTID_MASK GENMASK(28, 27) +#define CSID_CFG0_ENABLE BIT(31) + +#define CSID_CTRL(iface) (0x308 + 0x100 * (iface)) +#define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 +#define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -#define CSID_RDI_CTRL(rdi) (0x308 + 0x100 * (rdi)) -#define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0 -#define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1 static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { @@ -71,7 +72,7 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) { - writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); + writel_relaxed(!!enable, csid->base + CSID_CTRL(rdi)); } static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) @@ -88,7 +89,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(port) + * CSID_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 @@ -97,19 +98,19 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 */ dt_id = port & 0x03; - val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ - val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); - val |= FIELD_PREP(CSID_RDI_CFG0_VC_MASK, vc); - val |= FIELD_PREP(CSID_RDI_CFG0_DTID_MASK, dt_id); + val = CSID_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ + val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type); + val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc); + val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id); if (enable) - val |= CSID_RDI_CFG0_ENABLE; + val |= CSID_CFG0_ENABLE; dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", csid->id, enable ? "enable" : "disable", format->data_type, port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); + writel_relaxed(val, csid->base + CSID_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) From 189c9ef362be65ea587074f0b73e74ac5855cf51 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:51:59 +0200 Subject: [PATCH 2255/5214] media: qcom: camss: csid-340: Add port-to-interface mapping The CSID-340 block uses different register offsets for the PIX and RDI interfaces, but the driver previously indexed these registers directly with the camss port number. This happened to work for RDI because the port index matches the RDI register layout, but this assumption breaks with upcoming PIX interface support Introduce an explicit port-to-interface mapping and use the mapped iface index when programming CSID_CFG0 and CSID_CTRL. This replaces the standalone __csid_ctrl_rdi() helper and simplifies the RDI stream setup path. Also correct the CSID_CFG0/CTRL base offsets and clean up the code in preparation for full PIX path support. Like RDI, PIX outputs Bayer frames but can also achieve some image processing such as scaling, cropping and generating statitics (e.g. histogram), it also offer more flexebility in term of image alignment and stride. All of that can then later be leveraged to improve software or hardware frames post-processing. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 9eee23bd81c2..183d40d59afb 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -41,7 +41,7 @@ #define CSI2_RX_CFG1_MISR_EN BIT(6) #define CSI2_RX_CFG1_CGC_MODE BIT(7) -#define CSID_CFG0(iface) (0x300 + 0x100 * (iface)) +#define CSID_CFG0(iface) (0x200 + 0x100 * (iface)) #define CSID_CFG0_BYTE_CNTR_EN BIT(0) #define CSID_CFG0_TIMESTAMP_EN BIT(1) #define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) @@ -51,10 +51,24 @@ #define CSID_CFG0_DTID_MASK GENMASK(28, 27) #define CSID_CFG0_ENABLE BIT(31) -#define CSID_CTRL(iface) (0x308 + 0x100 * (iface)) +#define CSID_CTRL(iface) (0x208 + 0x100 * (iface)) #define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 +#define CSID_MAX_RDI_SRC_STREAMS (MSM_CSID_MAX_SRC_STREAMS - 1) + +enum csid_iface { + CSID_IFACE_PIX, + CSID_IFACE_RDI0, + CSID_IFACE_RDI1, + CSID_IFACE_RDI2, +}; + +static enum csid_iface csid_port_iface_map[CSID_MAX_RDI_SRC_STREAMS] = { + [0] = CSID_IFACE_RDI0, + [1] = CSID_IFACE_RDI1, + [2] = CSID_IFACE_RDI2, +}; static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { @@ -70,17 +84,13 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); } -static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) -{ - writel_relaxed(!!enable, csid->base + CSID_CTRL(rdi)); -} - static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); + enum csid_iface iface = csid_port_iface_map[port]; u8 dt_id; u32 val; @@ -110,7 +120,8 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 csid->id, enable ? "enable" : "disable", format->data_type, port, vc); - writel_relaxed(val, csid->base + CSID_CFG0(port)); + writel_relaxed(val, csid->base + CSID_CFG0(iface)); + writel_relaxed(enable, csid->base + CSID_CTRL(iface)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -119,12 +130,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_rx(csid, &csid->phy); - /* Loop through all enabled ports and configure a stream for each */ - for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { - if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i, 0); - __csid_ctrl_rdi(csid, enable, i); - } + /* RDIs */ + for (i = 0; i < CSID_MAX_RDI_SRC_STREAMS; i++) { + if (csid->phy.en_vc & BIT(i)) + __csid_configure_rdi_stream(csid, !!enable, i, 0); } } From ad543f3842f8e8998590f704fb09d160adcc610d Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:00 +0200 Subject: [PATCH 2256/5214] media: qcom: camss: csid-340: Enable PIX interface routing Add PIX path support to the CSID-340 driver. The hardware exposes a dedicated PIX interface in addition to the existing RDI paths, but the driver only supported RDI stream configuration so far. The PIX path is configured similarly to RDI but requires decode-format to be specified. The PIX pipeline can subsequently perform further processing, including scaling, cropping, and statistics. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 183d40d59afb..eca3f07b8a8a 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -55,8 +55,6 @@ #define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -#define CSID_MAX_RDI_SRC_STREAMS (MSM_CSID_MAX_SRC_STREAMS - 1) - enum csid_iface { CSID_IFACE_PIX, CSID_IFACE_RDI0, @@ -64,10 +62,11 @@ enum csid_iface { CSID_IFACE_RDI2, }; -static enum csid_iface csid_port_iface_map[CSID_MAX_RDI_SRC_STREAMS] = { +static enum csid_iface csid_port_iface_map[MSM_CSID_MAX_SRC_STREAMS] = { [0] = CSID_IFACE_RDI0, [1] = CSID_IFACE_RDI1, [2] = CSID_IFACE_RDI2, + [3] = CSID_IFACE_PIX, }; static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) @@ -84,13 +83,14 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) +static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); enum csid_iface iface = csid_port_iface_map[port]; + u8 dt_id; u32 val; @@ -108,7 +108,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 */ dt_id = port & 0x03; - val = CSID_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ + if (iface == CSID_IFACE_PIX) + val = FIELD_PREP(CSID_CFG0_DECODE_FORMAT_MASK, format->decode_format); + else /* RDI is raw, no decoding */ + val = CSID_CFG0_DECODE_FORMAT_NOP; + val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type); val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc); val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id); @@ -116,24 +120,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (enable) val |= CSID_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x df=0x%x port=%u vc=%u)\n", csid->id, enable ? "enable" : "disable", format->data_type, - port, vc); + format->decode_format, port, vc); writel_relaxed(val, csid->base + CSID_CFG0(iface)); writel_relaxed(enable, csid->base + CSID_CTRL(iface)); } -static void csid_configure_stream(struct csid_device *csid, u8 enable) +static void csid_configure_streams(struct csid_device *csid, u8 enable) { int i; __csid_configure_rx(csid, &csid->phy); - /* RDIs */ - for (i = 0; i < CSID_MAX_RDI_SRC_STREAMS; i++) { + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) - __csid_configure_rdi_stream(csid, !!enable, i, 0); + __csid_configure_stream(csid, !!enable, i, 0); } } @@ -189,7 +192,7 @@ static void csid_subdev_init(struct csid_device *csid) {} const struct csid_hw_ops csid_ops_340 = { .configure_testgen_pattern = csid_configure_testgen_pattern, - .configure_stream = csid_configure_stream, + .configure_stream = csid_configure_streams, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, From 880a3e40337e5ddf18ab8d8e07fe963de8789cbf Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:01 +0200 Subject: [PATCH 2257/5214] media: qcom: camss: vfe-340: Proper client handling We need to properly map camss WM index to our internal WM client instance. Today we only support RDI interfaces with the RDI_WM macro, introduce a __wm_to_client helper to support any interface. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-vfe-340.c | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c index 30d7630b3e8b..d129b0d3a6ed 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-340.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c @@ -69,24 +69,19 @@ #define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100) -/* - * TODO: differentiate the port id based on requested type of RDI, BHIST etc - * - * TFE write master IDs (clients) - * - * BAYER 0 - * IDEAL_RAW 1 - * STATS_TINTLESS_BG 2 - * STATS_BHIST 3 - * STATS_AWB_BG 4 - * STATS_AEC_BG 5 - * STATS_BAF 6 - * RDI0 7 - * RDI1 8 - * RDI2 9 - */ -#define RDI_WM(n) (7 + (n)) -#define TFE_WM_NUM 10 +enum tfe_client { + TFE_CLI_BAYER, + TFE_CLI_IDEAL_RAW, + TFE_CLI_STATS_TINTLESS_BG, + TFE_CLI_STATS_BHIST, + TFE_CLI_STATS_AWB_BG, + TFE_CLI_STATS_AEC_BG, + TFE_CLI_STATS_BAF, + TFE_CLI_RDI0, + TFE_CLI_RDI1, + TFE_CLI_RDI2, + TFE_CLI_NUM +}; enum tfe_iface { TFE_IFACE_PIX, @@ -108,6 +103,13 @@ enum tfe_subgroups { TFE_SUBGROUP_NUM }; +static enum tfe_client tfe_wm_client_map[VFE_LINE_NUM_MAX] = { + [VFE_LINE_RDI0] = TFE_CLI_RDI0, + [VFE_LINE_RDI1] = TFE_CLI_RDI1, + [VFE_LINE_RDI2] = TFE_CLI_RDI2, + [VFE_LINE_PIX] = TFE_CLI_BAYER, +}; + static enum tfe_iface tfe_line_iface_map[VFE_LINE_NUM_MAX] = { [VFE_LINE_RDI0] = TFE_IFACE_RDI0, [VFE_LINE_RDI1] = TFE_IFACE_RDI1, @@ -209,10 +211,10 @@ static irqreturn_t vfe_isr(int irq, void *dev) status = readl_relaxed(vfe->base + TFE_BUS_OVERFLOW_STATUS); if (status) { writel_relaxed(status, vfe->base + TFE_BUS_STATUS_CLEAR); - for (i = 0; i < TFE_WM_NUM; i++) { + for (i = 0; i < TFE_CLI_NUM; i++) { if (status & BIT(i)) dev_err_ratelimited(vfe->camss->dev, - "VFE%u: bus overflow for wm %u\n", + "VFE%u: bus overflow for client %u\n", vfe->id, i); } } @@ -235,49 +237,49 @@ static void vfe_enable_irq(struct vfe_device *vfe) TFE_BUS_IRQ_MASK_0_IMG_VIOL, vfe->base + TFE_BUS_IRQ_MASK_0); } -static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, +static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, struct vfe_line *line) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; - writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(wm)); + writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client)); } -static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) +static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; /* Configuration for plain RDI frames */ - writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(wm)); - writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(wm)); - writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(wm)); - writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(wm)); - writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(wm)); + writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(client)); /* No dropped frames, one irq per frame */ - writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(wm)); - writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(wm)); + writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(client)); + writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(client)); vfe_enable_irq(vfe); writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME, - vfe->base + TFE_BUS_CLIENT_CFG(wm)); + vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Started RDI%u width %u height %u stride %u\n", - vfe->id, rdi, pix->width, pix->height, stride); + dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n", + vfe->id, client, pix->width, pix->height, client); } -static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) +static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; - writel(0, vfe->base + TFE_BUS_CLIENT_CFG(wm)); + writel(0, vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Stopped RDI%u\n", vfe->id, rdi); + dev_dbg(vfe->camss->dev, "VFE%u: Stopped client %u\n", vfe->id, client); } static const struct camss_video_ops vfe_video_ops_520 = { From 793e056d44b76e284afa33d47de4c5ac75c8941a Mon Sep 17 00:00:00 2001 From: Vishwas Rajashekar Date: Sat, 18 Apr 2026 00:11:09 +0530 Subject: [PATCH 2258/5214] dt-bindings: iio: gyroscope: add mount-matrix for bmg160 The mount-matrix property supplies a 3x3 matrix that is used to transform the values from the gyroscope to get vector values that are relative to the way the sensor has been mounted on the device. When the property is not specified, the identity matrix is used. This change adds mount-matrix as an optional property to the dt-bindings for the bmg160 gyroscope. Signed-off-by: Vishwas Rajashekar Reviewed-by: Rob Herring (Arm) Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml index fcbd4b430e48..8e094651381c 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml @@ -26,6 +26,9 @@ properties: vdd-supply: true vddio-supply: true + mount-matrix: + description: an optional 3x3 mounting rotation matrix. + spi-max-frequency: maximum: 10000000 @@ -56,6 +59,9 @@ examples: reg = <0x69>; interrupt-parent = <&gpio6>; interrupts = <18 IRQ_TYPE_EDGE_RISING>; + mount-matrix = "0", "1", "0", + "1", "0", "0", + "0", "0", "1"; }; }; ... From 129c5499819bee611ea1a05f8307692ff75161ce Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 20 Apr 2026 13:12:23 +0300 Subject: [PATCH 2259/5214] iio: backend: add devm_iio_backend_get_by_index() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new function to get an IIO backend by its index in the io-backends device tree property. This is useful for multi-channel devices that have multiple backends, where looking up by index is more straightforward than using named backends. Extract __devm_iio_backend_fwnode_get_by_index() from the existing __devm_iio_backend_fwnode_get(), taking the index directly as a parameter. The new public API devm_iio_backend_get_by_index() uses the index to find the backend reference in the io-backends property, avoiding the need for io-backend-names. Reviewed-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 53 +++++++++++++++++++++--------- include/linux/iio/backend.h | 1 + 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 10e689f49441..138ebebc9c0d 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -964,23 +964,13 @@ int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address) } EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, "IIO_BACKEND"); -static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, - struct fwnode_handle *fwnode) +static struct iio_backend *__devm_iio_backend_fwnode_get_by_index(struct device *dev, + struct fwnode_handle *fwnode, + unsigned int index) { struct iio_backend *back; - unsigned int index; int ret; - if (name) { - ret = device_property_match_string(dev, "io-backend-names", - name); - if (ret < 0) - return ERR_PTR(ret); - index = ret; - } else { - index = 0; - } - struct fwnode_handle *fwnode_back __free(fwnode_handle) = fwnode_find_reference(fwnode, "io-backends", index); if (IS_ERR(fwnode_back)) @@ -996,8 +986,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con if (ret) return ERR_PTR(ret); - if (name) - back->idx = index; + back->idx = index; return back; } @@ -1005,6 +994,24 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con return ERR_PTR(-EPROBE_DEFER); } +static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, + struct fwnode_handle *fwnode) +{ + unsigned int index; + int ret; + + if (name) { + ret = device_property_match_string(dev, "io-backend-names", name); + if (ret < 0) + return ERR_PTR(ret); + index = ret; + } else { + index = 0; + } + + return __devm_iio_backend_fwnode_get_by_index(dev, fwnode, index); +} + /** * devm_iio_backend_get - Device managed backend device get * @dev: Consumer device for the backend @@ -1021,6 +1028,22 @@ struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) } EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, "IIO_BACKEND"); +/** + * devm_iio_backend_get_by_index - Device managed backend device get by index + * @dev: Consumer device for the backend + * @index: Index of the backend in the io-backends property + * + * Gets the backend at @index associated with @dev. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend *devm_iio_backend_get_by_index(struct device *dev, unsigned int index) +{ + return __devm_iio_backend_fwnode_get_by_index(dev, dev_fwnode(dev), index); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); + /** * devm_iio_backend_fwnode_get - Device managed backend firmware node get * @dev: Consumer device for the backend diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 4d15c2a9802c..3f95ed1fdf9e 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -261,6 +261,7 @@ int iio_backend_extend_chan_spec(struct iio_backend *back, bool iio_backend_has_caps(struct iio_backend *back, u32 caps); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); +struct iio_backend *devm_iio_backend_get_by_index(struct device *dev, unsigned int index); struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, const char *name, struct fwnode_handle *fwnode); From 80cc6d13d16d5c78cc088cea8a3d33ec853e3e22 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 20 Apr 2026 13:12:24 +0300 Subject: [PATCH 2260/5214] dt-bindings: iio: adc: ad4080: add AD4880 support Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with integrated fully differential amplifiers (FDA). The AD4880 has two independent ADC channels, each with its own SPI configuration interface. This requires: - Two entries in reg property for primary and secondary channel chip selects - Two io-backends entries for the two data channels Acked-by: Conor Dooley Reviewed-by: David Lechner Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ad4080.yaml | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index 79df2696ef24..9c6a56c7c8ef 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -18,7 +18,11 @@ description: | service a wide variety of precision, wide bandwidth data acquisition applications. + The AD4880 is a dual-channel variant with two independent ADC channels, + each with its own SPI configuration interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4080.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4880.pdf $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -34,9 +38,15 @@ properties: - adi,ad4086 - adi,ad4087 - adi,ad4088 + - adi,ad4880 reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + description: + SPI chip select(s). For single-channel devices, one chip select. + For multi-channel devices like AD4880, two chip selects are required + as each channel has its own SPI configuration interface. spi-max-frequency: description: Configuration of the SPI bus. @@ -60,7 +70,10 @@ properties: vrefin-supply: true io-backends: - maxItems: 1 + minItems: 1 + items: + - description: Backend for channel A (primary) + - description: Backend for channel B (secondary) adi,lvds-cnv-enable: description: Enable the LVDS signal type on the CNV pin. Default is CMOS. @@ -81,6 +94,25 @@ required: - vdd33-supply - vrefin-supply +allOf: + - if: + properties: + compatible: + contains: + const: adi,ad4880 + then: + properties: + reg: + minItems: 2 + io-backends: + minItems: 2 + else: + properties: + reg: + maxItems: 1 + io-backends: + maxItems: 1 + additionalProperties: false examples: @@ -101,4 +133,21 @@ examples: io-backends = <&iio_backend>; }; }; + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4880"; + reg = <0>, <1>; + spi-max-frequency = <10000000>; + vdd33-supply = <&vdd33>; + vddldo-supply = <&vddldo>; + vrefin-supply = <&vrefin>; + clocks = <&cnv>; + clock-names = "cnv"; + io-backends = <&iio_backend_cha>, <&iio_backend_chb>; + }; + }; ... From 370461ac8d51c530e7194efd4764dcb738dc52d7 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Mon, 20 Apr 2026 13:12:25 +0300 Subject: [PATCH 2261/5214] iio: adc: ad4080: add support for AD4880 dual-channel ADC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with integrated fully differential amplifiers (FDA). The AD4880 has two independent ADC channels, each with its own SPI configuration interface. The driver uses spi_new_ancillary_device() to create an additional SPI device for the second channel, allowing both channels to share the same SPI bus with different chip selects. Reviewed-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 257 +++++++++++++++++++++++++++++---------- 1 file changed, 195 insertions(+), 62 deletions(-) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 204ad198342b..265d85ac171a 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,9 @@ #define AD4086_CHIP_ID 0x0056 #define AD4087_CHIP_ID 0x0057 #define AD4088_CHIP_ID 0x0058 +#define AD4880_CHIP_ID 0x0750 + +#define AD4080_MAX_CHANNELS 2 #define AD4080_LVDS_CNV_CLK_CNT_MAX 7 @@ -179,8 +183,9 @@ struct ad4080_chip_info { }; struct ad4080_state { - struct regmap *regmap; - struct iio_backend *back; + struct spi_device *spi[AD4080_MAX_CHANNELS]; + struct regmap *regmap[AD4080_MAX_CHANNELS]; + struct iio_backend *back[AD4080_MAX_CHANNELS]; const struct ad4080_chip_info *info; /* * Synchronize access to members the of driver state, and ensure @@ -189,7 +194,7 @@ struct ad4080_state { struct mutex lock; unsigned int num_lanes; unsigned long clk_rate; - enum ad4080_filter_type filter_type; + enum ad4080_filter_type filter_type[AD4080_MAX_CHANNELS]; bool lvds_cnv_en; }; @@ -206,9 +211,9 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, struct ad4080_state *st = iio_priv(indio_dev); if (readval) - return regmap_read(st->regmap, reg, readval); + return regmap_read(st->regmap[0], reg, readval); - return regmap_write(st->regmap, reg, writeval); + return regmap_write(st->regmap[0], reg, writeval); } static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) @@ -229,8 +234,9 @@ static unsigned int ad4080_get_dec_rate(struct iio_dev *dev, struct ad4080_state *st = iio_priv(dev); int ret; unsigned int data; + unsigned int ch = chan->channel; - ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); if (ret) return ret; @@ -242,13 +248,14 @@ static int ad4080_set_dec_rate(struct iio_dev *dev, unsigned int mode) { struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; guard(mutex)(&st->lock); - if ((st->filter_type >= SINC_5 && mode >= 512) || mode < 2) + if ((st->filter_type[ch] >= SINC_5 && mode >= 512) || mode < 2) return -EINVAL; - return regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + return regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, (ilog2(mode) - 1))); @@ -268,15 +275,15 @@ static int ad4080_read_raw(struct iio_dev *indio_dev, dec_rate = ad4080_get_dec_rate(indio_dev, chan); if (dec_rate < 0) return dec_rate; - if (st->filter_type == SINC_5_COMP) + if (st->filter_type[chan->channel] == SINC_5_COMP) dec_rate *= 2; - if (st->filter_type) + if (st->filter_type[chan->channel]) *val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate); else *val = st->clk_rate; return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - if (st->filter_type == FILTER_NONE) { + if (st->filter_type[chan->channel] == FILTER_NONE) { *val = 1; } else { *val = ad4080_get_dec_rate(indio_dev, chan); @@ -297,7 +304,7 @@ static int ad4080_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - if (st->filter_type == FILTER_NONE && val > 1) + if (st->filter_type[chan->channel] == FILTER_NONE && val > 1) return -EINVAL; return ad4080_set_dec_rate(indio_dev, chan, val); @@ -306,23 +313,23 @@ static int ad4080_write_raw(struct iio_dev *indio_dev, } } -static int ad4080_lvds_sync_write(struct ad4080_state *st) +static int ad4080_lvds_sync_write(struct ad4080_state *st, unsigned int ch) { - struct device *dev = regmap_get_device(st->regmap); + struct device *dev = regmap_get_device(st->regmap[ch]); int ret; - ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); if (ret) return ret; - ret = iio_backend_interface_data_align(st->back, 10000); + ret = iio_backend_interface_data_align(st->back[ch], 10000); if (ret) return dev_err_probe(dev, ret, "Data alignment process failed\n"); dev_dbg(dev, "Success: Pattern correct and Locked!\n"); - return regmap_clear_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + return regmap_clear_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); } @@ -331,9 +338,10 @@ static int ad4080_get_filter_type(struct iio_dev *dev, { struct ad4080_state *st = iio_priv(dev); unsigned int data; + unsigned int ch = chan->channel; int ret; - ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); if (ret) return ret; @@ -345,6 +353,7 @@ static int ad4080_set_filter_type(struct iio_dev *dev, unsigned int mode) { struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; int dec_rate; int ret; @@ -357,18 +366,18 @@ static int ad4080_set_filter_type(struct iio_dev *dev, if (mode >= SINC_5 && dec_rate >= 512) return -EINVAL; - ret = iio_backend_filter_type_set(st->back, mode); + ret = iio_backend_filter_type_set(st->back[ch], mode); if (ret) return ret; - ret = regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + ret = regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, AD4080_FILTER_CONFIG_FILTER_SEL_MSK, FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, mode)); if (ret) return ret; - st->filter_type = mode; + st->filter_type[ch] = mode; return 0; } @@ -382,14 +391,14 @@ static int ad4080_read_avail(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - switch (st->filter_type) { + switch (st->filter_type[chan->channel]) { case FILTER_NONE: *vals = ad4080_dec_rate_none; *length = ARRAY_SIZE(ad4080_dec_rate_none); break; default: *vals = ad4080_dec_rate_avail; - *length = st->filter_type >= SINC_5 ? + *length = st->filter_type[chan->channel] >= SINC_5 ? (ARRAY_SIZE(ad4080_dec_rate_avail) - 2) : ARRAY_SIZE(ad4080_dec_rate_avail); break; @@ -401,6 +410,28 @@ static int ad4080_read_avail(struct iio_dev *indio_dev, } } +static int ad4880_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + /* + * Each backend has a single channel (channel 0 from the + * backend's perspective), so always use channel index 0. + */ + if (test_bit(ch, scan_mask)) + ret = iio_backend_chan_enable(st->back[ch], 0); + else + ret = iio_backend_chan_disable(st->back[ch], 0); + if (ret) + return ret; + } + + return 0; +} + static const struct iio_info ad4080_iio_info = { .debugfs_reg_access = ad4080_reg_access, .read_raw = ad4080_read_raw, @@ -408,6 +439,19 @@ static const struct iio_info ad4080_iio_info = { .read_avail = ad4080_read_avail, }; +/* + * AD4880 needs update_scan_mode to enable/disable individual backend channels. + * Single-channel devices don't need this as their backends may not implement + * chan_enable/chan_disable operations. + */ +static const struct iio_info ad4880_iio_info = { + .debugfs_reg_access = ad4080_reg_access, + .read_raw = ad4080_read_raw, + .write_raw = ad4080_write_raw, + .read_avail = ad4080_read_avail, + .update_scan_mode = ad4880_update_scan_mode, +}; + static const struct iio_enum ad4080_filter_type_enum = { .items = ad4080_filter_type_iio_enum, .num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum), @@ -422,17 +466,28 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = { { } }; -#define AD4080_CHANNEL_DEFINE(bits, storage) { \ +/* + * AD4880 needs per-channel filter configuration since each channel has + * its own independent ADC with separate SPI interface. + */ +static struct iio_chan_spec_ext_info ad4880_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, + &ad4080_filter_type_enum), + { } +}; + +#define AD4080_CHANNEL_DEFINE(bits, storage, idx) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ - .channel = 0, \ + .channel = (idx), \ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .ext_info = ad4080_ext_info, \ - .scan_index = 0, \ + .scan_index = (idx), \ .scan_type = { \ .sign = 's', \ .realbits = (bits), \ @@ -440,23 +495,51 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = { }, \ } -static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32); +/* + * AD4880 has per-channel attributes (filter_type, oversampling_ratio, + * sampling_frequency) since each channel has its own independent ADC + * with separate SPI configuration interface. + */ +#define AD4880_CHANNEL_DEFINE(bits, storage, idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = ad4880_ext_info, \ + .scan_index = (idx), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = (storage), \ + }, \ +} -static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32); +static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); -static const struct iio_chan_spec ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32); +static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); -static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); -static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); -static const struct iio_chan_spec ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); -static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); -static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); -static const struct iio_chan_spec ad4088_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4088_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4880_channels[] = { + AD4880_CHANNEL_DEFINE(20, 32, 0), + AD4880_CHANNEL_DEFINE(20, 32, 1), +}; static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", @@ -548,25 +631,34 @@ static const struct ad4080_chip_info ad4088_chip_info = { .lvds_cnv_clk_cnt_max = 8, }; -static int ad4080_setup(struct iio_dev *indio_dev) +static const struct ad4080_chip_info ad4880_chip_info = { + .name = "ad4880", + .product_id = AD4880_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4880_channels, + .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, +}; + +static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) { - struct ad4080_state *st = iio_priv(indio_dev); - struct device *dev = regmap_get_device(st->regmap); + struct device *dev = regmap_get_device(st->regmap[ch]); __le16 id_le; u16 id; int ret; - ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, AD4080_INTERFACE_CONFIG_A_SW_RESET); if (ret) return ret; - ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, AD4080_INTERFACE_CONFIG_A_SDO_ENABLE); if (ret) return ret; - ret = regmap_bulk_read(st->regmap, AD4080_REG_PRODUCT_ID_L, &id_le, + ret = regmap_bulk_read(st->regmap[ch], AD4080_REG_PRODUCT_ID_L, &id_le, sizeof(id_le)); if (ret) return ret; @@ -575,18 +667,18 @@ static int ad4080_setup(struct iio_dev *indio_dev) if (id != st->info->product_id) dev_info(dev, "Unrecognized CHIP_ID 0x%X\n", id); - ret = regmap_set_bits(st->regmap, AD4080_REG_GPIO_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_GPIO_CONFIG_A, AD4080_GPIO_CONFIG_A_GPO_1_EN); if (ret) return ret; - ret = regmap_write(st->regmap, AD4080_REG_GPIO_CONFIG_B, + ret = regmap_write(st->regmap[ch], AD4080_REG_GPIO_CONFIG_B, FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK, AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY)); if (ret) return ret; - ret = iio_backend_num_lanes_set(st->back, st->num_lanes); + ret = iio_backend_num_lanes_set(st->back[ch], st->num_lanes); if (ret) return ret; @@ -594,7 +686,7 @@ static int ad4080_setup(struct iio_dev *indio_dev) return 0; /* Set maximum LVDS Data Transfer Latency */ - ret = regmap_update_bits(st->regmap, + ret = regmap_update_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_B, AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, @@ -603,24 +695,38 @@ static int ad4080_setup(struct iio_dev *indio_dev) return ret; if (st->num_lanes > 1) { - ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES); if (ret) return ret; } - ret = regmap_set_bits(st->regmap, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_B, AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN); if (ret) return ret; - return ad4080_lvds_sync_write(st); + return ad4080_lvds_sync_write(st, ch); } -static int ad4080_properties_parse(struct ad4080_state *st) +static int ad4080_setup(struct iio_dev *indio_dev) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + ret = ad4080_setup_channel(st, ch); + if (ret) + return ret; + } + + return 0; +} + +static int ad4080_properties_parse(struct ad4080_state *st, + struct device *dev) { - struct device *dev = regmap_get_device(st->regmap); st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable"); @@ -655,14 +761,28 @@ static int ad4080_probe(struct spi_device *spi) return dev_err_probe(dev, ret, "failed to get and enable supplies\n"); - st->regmap = devm_regmap_init_spi(spi, &ad4080_regmap_config); - if (IS_ERR(st->regmap)) - return PTR_ERR(st->regmap); + /* Setup primary SPI device (channel 0) */ + st->spi[0] = spi; + st->regmap[0] = devm_regmap_init_spi(spi, &ad4080_regmap_config); + if (IS_ERR(st->regmap[0])) + return PTR_ERR(st->regmap[0]); st->info = spi_get_device_match_data(spi); if (!st->info) return -ENODEV; + /* Setup ancillary SPI devices for additional channels */ + for (unsigned int ch = 1; ch < st->info->num_channels; ch++) { + st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch)); + if (IS_ERR(st->spi[ch])) + return dev_err_probe(dev, PTR_ERR(st->spi[ch]), + "failed to register ancillary device\n"); + + st->regmap[ch] = devm_regmap_init_spi(st->spi[ch], &ad4080_regmap_config); + if (IS_ERR(st->regmap[ch])) + return PTR_ERR(st->regmap[ch]); + } + ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; @@ -670,9 +790,10 @@ static int ad4080_probe(struct spi_device *spi) indio_dev->name = st->info->name; indio_dev->channels = st->info->channels; indio_dev->num_channels = st->info->num_channels; - indio_dev->info = &ad4080_iio_info; + indio_dev->info = st->info->num_channels > 1 ? + &ad4880_iio_info : &ad4080_iio_info; - ret = ad4080_properties_parse(st); + ret = ad4080_properties_parse(st, dev); if (ret) return ret; @@ -682,15 +803,25 @@ static int ad4080_probe(struct spi_device *spi) st->clk_rate = clk_get_rate(clk); - st->back = devm_iio_backend_get(dev, NULL); - if (IS_ERR(st->back)) - return PTR_ERR(st->back); + /* Get backends for all channels */ + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + st->back[ch] = devm_iio_backend_get_by_index(dev, ch); + if (IS_ERR(st->back[ch])) + return PTR_ERR(st->back[ch]); - ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); - if (ret) - return ret; + ret = devm_iio_backend_enable(dev, st->back[ch]); + if (ret) + return ret; + } - ret = devm_iio_backend_enable(dev, st->back); + /* + * Request buffer from the first backend only. For multi-channel + * devices (e.g., AD4880), the FPGA uses two axi_ad408x IP instances + * (one per ADC channel) whose outputs are combined by a packer block + * that interleaves all channel data into a single DMA stream routed + * through the first backend's clock domain. + */ + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev); if (ret) return ret; @@ -711,6 +842,7 @@ static const struct spi_device_id ad4080_id[] = { { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, + { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -725,6 +857,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4086", &ad4086_chip_info }, { .compatible = "adi,ad4087", &ad4087_chip_info }, { .compatible = "adi,ad4088", &ad4088_chip_info }, + { .compatible = "adi,ad4880", &ad4880_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); From 74c3923344c6ad4b7199948d54dc947504c39483 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Sun, 26 Apr 2026 14:47:02 +0530 Subject: [PATCH 2262/5214] iio: ssp_sensors: cleanup codestyle warning Reported by checkpatch: FILE: drivers/iio/common/ssp_sensors/ssp_dev.c WARNING: Prefer __packed over __attribute__((__packed__)) +} __attribute__((__packed__)); Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index da09c9f3ceb6..7d07fae295fd 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -28,7 +28,7 @@ struct ssp_instruction { __le32 a; __le32 b; u8 c; -} __attribute__((__packed__)); +} __packed__; static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67, 208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171, From c09f950fd87ad6505cc7bdbac4e0a125b6ed313c Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Sat, 25 Apr 2026 11:16:16 +0400 Subject: [PATCH 2263/5214] iio: adc: ad7625: fix type mismatch in clamp() macro clamp() expects compatible operand types. The period calculation uses nanosecond constants, while the local target variable was narrower than the upper bound expression. Make target unsigned long and use unsigned long bounds, including NSEC_PER_USEC for the upper limit. This keeps the operands naturally aligned without adding casts. Suggested-by: Andy Shevchenko Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7625.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c index 0466c0c7eae4..f1ee29f35fa8 100644 --- a/drivers/iio/adc/ad7625.c +++ b/drivers/iio/adc/ad7625.c @@ -175,12 +175,12 @@ enum ad7960_mode { static int ad7625_set_sampling_freq(struct ad7625_state *st, u32 freq) { - u32 target; struct pwm_waveform clk_gate_wf = { }, cnv_wf = { }; + unsigned long target; int ret; target = DIV_ROUND_UP(NSEC_PER_SEC, freq); - cnv_wf.period_length_ns = clamp(target, 100, 10 * KILO); + cnv_wf.period_length_ns = clamp(target, 100UL, 10UL * NSEC_PER_USEC); /* * Use the maximum conversion time t_CNVH from the datasheet as From 2e43816512876d41a7ce1771ba949b846f5bb7dd Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Mon, 27 Apr 2026 14:23:16 +0800 Subject: [PATCH 2264/5214] dt-bindings: iio: dac: Add ADI AD5706R Add device tree binding documentation for the Analog Devices AD5706R 4-channel 16-bit current output digital-to-analog converter. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexis Czezar Torreno Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5706r.yaml | 105 ++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml new file mode 100644 index 000000000000..19cc744a9f0f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad5706r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5706R 4-Channel Current Output DAC + +maintainers: + - Alexis Czezar Torreno + +description: | + The AD5706R is a 4-channel, 16-bit resolution, current output + digital-to-analog converter (DAC) with programmable output current + ranges (50mA, 150mA, 200mA, 300mA), an integrated 2.5V voltage + reference, and load DAC, A/B toggle, and dither functions. + + Datasheet: + https://www.analog.com/en/products/ad5706r.html + +properties: + compatible: + enum: + - adi,ad5706r + + reg: + maxItems: 1 + + avdd-supply: + description: Analog power supply (2.9V to 3.6V). + + iovdd-supply: + description: Logic power supply (1.14V to 1.89V). + + pvdd0-supply: + description: Power supply for IDAC0 channel (1.65V to AVDD). + + pvdd1-supply: + description: Power supply for IDAC1 channel (1.65V to AVDD). + + pvdd2-supply: + description: Power supply for IDAC2 channel (1.65V to AVDD). + + pvdd3-supply: + description: Power supply for IDAC3 channel (1.65V to AVDD). + + vref-supply: + description: + Optional external 2.5V voltage reference. If not provided, the + internal 2.5V reference is used. + + pwms: + maxItems: 1 + description: + Optional PWM connected to the LDAC/TGP/DCK pin for hardware + triggered DAC updates, toggle, or dither clock generation. + + reset-gpios: + maxItems: 1 + description: + GPIO connected to the active low RESET pin. If not provided, + software reset is used. + + enable-gpios: + maxItems: 1 + description: + GPIO connected to the active low OUT_EN pin. Controls whether + the current outputs are enabled or in high-Z/ground state. + +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "adi,ad5706r"; + reg = <0>; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + pvdd0-supply = <&pvdd>; + pvdd1-supply = <&pvdd>; + pvdd2-supply = <&pvdd>; + pvdd3-supply = <&pvdd>; + vref-supply = <&vref>; + spi-max-frequency = <50000000>; + pwms = <&pwm0 0 1000000 0>; + reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; + enable-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..fff2bf304357 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1498,6 +1498,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml F: drivers/iio/adc/ad4851.c +ANALOG DEVICES INC AD5706R DRIVER +M: Alexis Czezar Torreno +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml + ANALOG DEVICES INC AD7091R DRIVER M: Marcelo Schmitt L: linux-iio@vger.kernel.org From 081ce276d801d7c0a3dc41aa9a02ff73eb96c94a Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Mon, 27 Apr 2026 14:23:17 +0800 Subject: [PATCH 2265/5214] iio: dac: ad5706r: Add support for AD5706R DAC Add support for the Analog Devices AD5706R, a 4-channel 16-bit current output digital-to-analog converter with SPI interface. Reviewed-by: Andy Shevchenko Signed-off-by: Alexis Czezar Torreno Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 11 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5706r.c | 253 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 drivers/iio/dac/ad5706r.c diff --git a/MAINTAINERS b/MAINTAINERS index fff2bf304357..31c4db07e99b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1504,6 +1504,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad5706r.yaml +F: drivers/iio/dac/ad5706r.c ANALOG DEVICES INC AD7091R DRIVER M: Marcelo Schmitt diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index cd4870b65415..657c68e75542 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -178,6 +178,17 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config AD5706R + tristate "Analog Devices AD5706R DAC driver" + depends on SPI + select REGMAP + help + Say yes here to build support for Analog Devices AD5706R 4-channel, + 16-bit current output DAC. + + To compile this driver as a module, choose M here: the + module will be called ad5706r. + config AD9739A tristate "Analog Devices AD9739A RF DAC spi driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 2a80bbf4e80a..003431798498 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o obj-$(CONFIG_AD5592R) += ad5592r.o obj-$(CONFIG_AD5593R) += ad5593r.o +obj-$(CONFIG_AD5706R) += ad5706r.o obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5758) += ad5758.o obj-$(CONFIG_AD5761) += ad5761.o diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c new file mode 100644 index 000000000000..f7872e92dc01 --- /dev/null +++ b/drivers/iio/dac/ad5706r.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD5706R 16-bit Current Output Digital to Analog Converter + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI frame layout */ +#define AD5706R_RD_MASK BIT(15) +#define AD5706R_ADDR_MASK GENMASK(11, 0) + +/* Registers */ +#define AD5706R_REG_DAC_INPUT_A_CH(x) (0x60 + ((x) * 2)) +#define AD5706R_REG_DAC_DATA_READBACK_CH(x) (0x68 + ((x) * 2)) + +#define AD5706R_DAC_RESOLUTION 16 +#define AD5706R_DAC_MAX_CODE GENMASK(15, 0) +#define AD5706R_MULTIBYTE_REG_START 0x14 +#define AD5706R_MULTIBYTE_REG_END 0x71 +#define AD5706R_MAX_REG 0x77 + +struct ad5706r_state { + struct spi_device *spi; + struct regmap *regmap; + + u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[4]; +}; + +static int ad5706r_reg_len(unsigned int reg) +{ + if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) + return 2; + + return 1; +} + +static int ad5706r_regmap_write(void *context, const void *data, size_t count) +{ + struct ad5706r_state *st = context; + unsigned int num_bytes; + u16 reg, val; + + if (count != 4) + return -EINVAL; + + reg = get_unaligned_be16(data); + val = get_unaligned_be16(data + 2); + num_bytes = ad5706r_reg_len(reg); + + struct spi_transfer xfer = { + .tx_buf = st->tx_buf, + .len = num_bytes + 2, + }; + + put_unaligned_be16(reg, &st->tx_buf[0]); + + if (num_bytes == 1) + st->tx_buf[2] = (u8)val; + else if (num_bytes == 2) + put_unaligned_be16(val, &st->tx_buf[2]); + else + return -EINVAL; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad5706r_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct ad5706r_state *st = context; + unsigned int num_bytes; + u16 reg, cmd, val; + int ret; + + if (reg_size != 2 || val_size != 2) + return -EINVAL; + + reg = get_unaligned_be16(reg_buf); + num_bytes = ad5706r_reg_len(reg); + + /* Full duplex, device responds immediately after command */ + struct spi_transfer xfer = { + .tx_buf = st->tx_buf, + .rx_buf = st->rx_buf, + .len = 2 + num_bytes, + }; + + cmd = AD5706R_RD_MASK | (reg & AD5706R_ADDR_MASK); + put_unaligned_be16(cmd, &st->tx_buf[0]); + put_unaligned_be16(0, &st->tx_buf[2]); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + /* Extract value from response (skip 2-byte command echo) */ + if (num_bytes == 1) + val = st->rx_buf[2]; + else if (num_bytes == 2) + val = get_unaligned_be16(&st->rx_buf[2]); + else + return -EINVAL; + + put_unaligned_be16(val, val_buf); + + return 0; +} + +static int ad5706r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg, reg_val; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + reg = AD5706R_REG_DAC_DATA_READBACK_CH(chan->channel); + ret = regmap_read(st->regmap, reg, ®_val); + if (ret) + return ret; + + *val = reg_val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 50; + *val2 = AD5706R_DAC_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad5706r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val > AD5706R_DAC_MAX_CODE) + return -EINVAL; + + reg = AD5706R_REG_DAC_INPUT_A_CH(chan->channel); + return regmap_write(st->regmap, reg, val); + default: + return -EINVAL; + } +} + +static const struct regmap_bus ad5706r_regmap_bus = { + .write = ad5706r_regmap_write, + .read = ad5706r_regmap_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ad5706r_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .max_register = AD5706R_MAX_REG, +}; + +static const struct iio_info ad5706r_info = { + .read_raw = ad5706r_read_raw, + .write_raw = ad5706r_write_raw, +}; + +#define AD5706R_CHAN(_channel) { \ + .type = IIO_CURRENT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ +} + +static const struct iio_chan_spec ad5706r_channels[] = { + AD5706R_CHAN(0), + AD5706R_CHAN(1), + AD5706R_CHAN(2), + AD5706R_CHAN(3), +}; + +static int ad5706r_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad5706r_state *st; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->regmap = devm_regmap_init(dev, &ad5706r_regmap_bus, + st, &ad5706r_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to init regmap\n"); + + indio_dev->name = "ad5706r"; + indio_dev->info = &ad5706r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad5706r_channels; + indio_dev->num_channels = ARRAY_SIZE(ad5706r_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad5706r_of_match[] = { + { .compatible = "adi,ad5706r" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5706r_of_match); + +static const struct spi_device_id ad5706r_id[] = { + { "ad5706r" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5706r_id); + +static struct spi_driver ad5706r_driver = { + .driver = { + .name = "ad5706r", + .of_match_table = ad5706r_of_match, + }, + .probe = ad5706r_probe, + .id_table = ad5706r_id, +}; +module_spi_driver(ad5706r_driver); + +MODULE_AUTHOR("Alexis Czezar Torreno "); +MODULE_DESCRIPTION("AD5706R 16-bit Current Output DAC driver"); +MODULE_LICENSE("GPL"); From 095ae2f2e72f56d442ec0e476f873fd4d92c81de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rahman=20Mahmutovi=C4=87?= Date: Mon, 27 Apr 2026 22:20:16 +0200 Subject: [PATCH 2266/5214] iio: temperature: maxim_thermocouple: Fix indentation in of_match table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace leading spaces with tabs in the of_device_id table entries to comply with kernel coding style. Signed-off-by: Rahman Mahmutović Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/maxim_thermocouple.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 205939680fd4..e898f56d1196 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -277,8 +277,8 @@ static const struct spi_device_id maxim_thermocouple_id[] = { MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); static const struct of_device_id maxim_thermocouple_of_match[] = { - { .compatible = "maxim,max6675" }, - { .compatible = "maxim,max31855" }, + { .compatible = "maxim,max6675" }, + { .compatible = "maxim,max31855" }, { .compatible = "maxim,max31855k" }, { .compatible = "maxim,max31855j" }, { .compatible = "maxim,max31855n" }, @@ -286,7 +286,7 @@ static const struct of_device_id maxim_thermocouple_of_match[] = { { .compatible = "maxim,max31855t" }, { .compatible = "maxim,max31855e" }, { .compatible = "maxim,max31855r" }, - { }, + { }, }; MODULE_DEVICE_TABLE(of, maxim_thermocouple_of_match); From c0536d61f30417b0aeac7ea8734525ab7a01a93b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Apr 2026 20:51:47 +0200 Subject: [PATCH 2267/5214] iio: buffer: Move from int64_t to s64 for timestamp iio_push_to_buffers_with_ts_unaligned() uses int64_t for timestamp. Move it from int64_t to s64 to make consistent with: - iio_push_to_buffers_with_ts() - all current users that supply s64 anyway This will reduce potential of wrong type being chosen when using this API. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- include/linux/iio/buffer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 252ce4a6f913..9d66510a1d49 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -2444,7 +2444,7 @@ EXPORT_SYMBOL_GPL(iio_push_to_buffers); int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, const void *data, size_t data_sz, - int64_t timestamp) + s64 timestamp) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 8fd0d57d238f..745c98ef4e04 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -79,7 +79,7 @@ static inline int iio_push_to_buffers_with_ts(struct iio_dev *indio_dev, int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, const void *data, size_t data_sz, - int64_t timestamp); + s64 timestamp); bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, const unsigned long *mask); From 5b382bf01a377ea96612089b1d68d9547882359f Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 19 Apr 2026 20:36:23 +0200 Subject: [PATCH 2268/5214] iio: frequency: ad9832: simplify bitwise math MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the ad9832_calc_freqreg by removing the redundant u64 casts and 1L bitwise left shift and replacing the multiplication by a bit shift, as multiplying integers by a power of two is identical to a bitwise left shift. Signed-off-by: Joshua Crofts Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index c0b7852f1c84..659821a1e2cb 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -117,8 +117,8 @@ struct ad9832_state { static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout) { - unsigned long long freqreg = (u64)fout * - (u64)((u64)1L << AD9832_FREQ_BITS); + u64 freqreg = (u64)fout << AD9832_FREQ_BITS; + do_div(freqreg, mclk); return freqreg; } From 31b9486cd91e5d1054d508166ed5e5ae38c38198 Mon Sep 17 00:00:00 2001 From: "Rafael G. Dias" Date: Tue, 28 Apr 2026 13:13:38 -0300 Subject: [PATCH 2269/5214] iio: light: stk3310: Sort headers alphabetically Sort the included headers alphabetically and group the headers separately from the generic headers. Co-developed-by: Felipe Khoury Dayoub Signed-off-by: Felipe Khoury Dayoub Signed-off-by: Rafael G. Dias Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index a75a83594a7e..1fad9367c2d4 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -10,9 +10,10 @@ #include #include #include -#include #include +#include #include + #include #include #include From 9cba9d97ac7453ce4f0d589fdc05f5e1abfd6eb5 Mon Sep 17 00:00:00 2001 From: "Rafael G. Dias" Date: Tue, 28 Apr 2026 13:13:39 -0300 Subject: [PATCH 2270/5214] iio: light: stk3310: Update includes to match IWYU Clean up the included headers in stk3310.c according to the Include-What-You-Use (IWYU) tool. Remove the generic header and add explicit dependencies to improve compilation accuracy. Co-developed-by: Felipe Khoury Dayoub Signed-off-by: Felipe Khoury Dayoub Signed-off-by: Rafael G. Dias Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 1fad9367c2d4..63920fe134bc 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -7,16 +7,28 @@ * IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48. */ +#include +#include +#include +#include #include #include -#include #include #include +#include +#include +#include #include +#include +#include +#include #include #include #include +#include + +#include #define STK3310_REG_STATE 0x00 #define STK3310_REG_PSCTRL 0x01 From b4e9ede52a6094b4a7fcf584f7348542d0619632 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 29 Apr 2026 21:27:23 +0200 Subject: [PATCH 2271/5214] iio: adc: rcar: Fix up Marek Vasut MAINTAINERS entry Use up to date address. No functional change. Signed-off-by: Marek Vasut Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 31c4db07e99b..c316e3b44e19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22602,7 +22602,7 @@ F: Documentation/devicetree/bindings/mtd/renesas-nandc.yaml F: drivers/mtd/nand/raw/renesas-nand-controller.c RENESAS R-CAR GYROADC DRIVER -M: Marek Vasut +M: Marek Vasut L: linux-iio@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/iio/adc/renesas,rcar-gyroadc.yaml From 19f2c27251d86cf1272d475c52a190d01e9f996b Mon Sep 17 00:00:00 2001 From: Angus Gardner Date: Mon, 4 May 2026 19:20:57 +1000 Subject: [PATCH 2272/5214] staging: iio: ad9834: simplify -ENOMEM return in probe devm_iio_device_alloc() failure returns -ENOMEM via a local variable unnecessarily. Return -ENOMEM directly instead. Signed-off-by: Angus Gardner Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 330db78fe766..e7aee3307061 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -392,10 +392,8 @@ static int ad9834_probe(struct spi_device *spi) return dev_err_probe(&spi->dev, ret, "Failed to enable specified AVDD supply\n"); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) { - ret = -ENOMEM; - return ret; - } + if (!indio_dev) + return -ENOMEM; st = iio_priv(indio_dev); mutex_init(&st->lock); st->mclk = devm_clk_get_enabled(&spi->dev, NULL); From 8b8e850eada20e96ef22621b87f58a72b69566cf Mon Sep 17 00:00:00 2001 From: Angus Gardner Date: Mon, 4 May 2026 19:20:58 +1000 Subject: [PATCH 2273/5214] staging: iio: ad9834: use dev_err_probe() in probe function Replace open-coded dev_err() + return sequences with dev_err_probe(), which is the preferred pattern for probe error paths as it handles deferred probing correctly and reduces boilerplate. Convert all three remaining instances in ad9834_probe(): - master clock enable failure - device init SPI sync failure The avdd regulator path already used dev_err_probe(). Signed-off-by: Angus Gardner Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index e7aee3307061..8c0699460ae1 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -397,10 +397,9 @@ static int ad9834_probe(struct spi_device *spi) st = iio_priv(indio_dev); mutex_init(&st->lock); st->mclk = devm_clk_get_enabled(&spi->dev, NULL); - if (IS_ERR(st->mclk)) { - dev_err(&spi->dev, "Failed to enable master clock\n"); - return PTR_ERR(st->mclk); - } + if (IS_ERR(st->mclk)) + return dev_err_probe(&spi->dev, PTR_ERR(st->mclk), + "Failed to enable master clock\n"); st->spi = spi; st->devid = spi_get_device_id(spi)->driver_data; @@ -442,10 +441,9 @@ static int ad9834_probe(struct spi_device *spi) st->data = cpu_to_be16(AD9834_REG_CMD | st->control); ret = spi_sync(st->spi, &st->msg); - if (ret) { - dev_err(&spi->dev, "device init failed\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, + "device init failed\n"); ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, 1000000); if (ret) From 11aa529f27c7c8656ab57a6d79111b2e525f7b19 Mon Sep 17 00:00:00 2001 From: Angus Gardner Date: Mon, 4 May 2026 19:20:59 +1000 Subject: [PATCH 2274/5214] staging: iio: ad9834: fix chip name typo in comments Two comments incorrectly refer to 'AD9843' instead of 'AD9834'. Fix the copy-paste typo. Signed-off-by: Angus Gardner Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 8c0699460ae1..4359b358e0e5 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -167,7 +167,7 @@ static ssize_t ad9834_write(struct device *dev, break; case AD9834_OPBITEN: if (st->control & AD9834_MODE) { - ret = -EINVAL; /* AD9843 reserved mode */ + ret = -EINVAL; /* AD9834 reserved mode */ break; } @@ -242,7 +242,7 @@ static ssize_t ad9834_store_wavetype(struct device *dev, st->control &= ~AD9834_OPBITEN; st->control |= AD9834_MODE; } else if (st->control & AD9834_OPBITEN) { - ret = -EINVAL; /* AD9843 reserved mode */ + ret = -EINVAL; /* AD9834 reserved mode */ } else { st->control |= AD9834_MODE; } From 9c1d639e90cf42f5c1401f91f38ffd89af6dd970 Mon Sep 17 00:00:00 2001 From: Miao Li Date: Mon, 4 May 2026 11:04:06 +0800 Subject: [PATCH 2275/5214] iio: light: stk3310: Deal with the ps interrupt issue in PM On the Inspur HS326 laptop(which integrated with HiSilicon M900 processor), if the STK3311-X chip's PS interrupt is configured in "Recommended interrupt mode", the interrupt cannot be triggered normally after waking from suspend or hibernation. In this case, neither disabling and re-enabling the interrupt nor resetting the PS threshold register can restore the interrupt to normal operation. If the interrupt is disabled in suspend() then reset the PS threshold register and enable the interrupt in resume(). This resolves the issue. Signed-off-by: Miao Li Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 76 +++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 63920fe134bc..21118b746789 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -130,6 +130,9 @@ struct stk3310_data { struct mutex lock; bool als_enabled; bool ps_enabled; + bool ps_int_enabled; + uint32_t ps_thdl; + uint32_t ps_thdh; uint32_t ps_near_level; u64 timestamp; struct regmap *regmap; @@ -309,10 +312,17 @@ static int stk3310_write_event(struct iio_dev *indio_dev, buf = cpu_to_be16(val); ret = regmap_bulk_write(data->regmap, reg, &buf, 2); - if (ret < 0) + if (ret < 0) { dev_err(&client->dev, "failed to set PS threshold!\n"); + return ret; + } - return ret; + if (reg == STK3310_REG_THDH_PS) + data->ps_thdh = val; + else + data->ps_thdl = val; + + return 0; } static int stk3310_read_event_config(struct iio_dev *indio_dev, @@ -344,11 +354,17 @@ static int stk3310_write_event_config(struct iio_dev *indio_dev, /* Set INT_PS value */ mutex_lock(&data->lock); ret = regmap_field_write(data->reg_int_ps, state); - if (ret < 0) + if (ret < 0) { dev_err(&client->dev, "failed to set interrupt mode\n"); + mutex_unlock(&data->lock); + return ret; + } + + data->ps_int_enabled = state; + mutex_unlock(&data->lock); - return ret; + return 0; } static int stk3310_read_raw(struct iio_dev *indio_dev, @@ -517,10 +533,15 @@ static int stk3310_init(struct iio_dev *indio_dev) /* Enable PS interrupts */ ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN); - if (ret < 0) + if (ret < 0) { dev_err(&client->dev, "failed to enable interrupts!\n"); + return ret; + } - return ret; + data->ps_int_enabled = true; + data->ps_thdh = STK3310_PS_MAX_VAL; + + return 0; } static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg) @@ -684,9 +705,18 @@ static void stk3310_remove(struct i2c_client *client) static int stk3310_suspend(struct device *dev) { struct stk3310_data *data; + int ret; data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + if (data->ps_int_enabled) { + ret = regmap_field_write(data->reg_int_ps, 0x0); + if (ret < 0) { + dev_err(dev, "failed to disable ps int at suspend.\n"); + return ret; + } + } + return stk3310_set_state(data, STK3310_STATE_STANDBY); } @@ -694,6 +724,8 @@ static int stk3310_resume(struct device *dev) { u8 state = 0; struct stk3310_data *data; + __be16 buf; + int ret; data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); if (data->ps_enabled) @@ -701,7 +733,37 @@ static int stk3310_resume(struct device *dev) if (data->als_enabled) state |= STK3310_STATE_EN_ALS; - return stk3310_set_state(data, state); + ret = stk3310_set_state(data, state); + if (ret < 0) + return ret; + + if (data->ps_thdl != 0x0) { + buf = cpu_to_be16(data->ps_thdl); + ret = regmap_bulk_write(data->regmap, STK3310_REG_THDL_PS, &buf, 2); + if (ret < 0) { + dev_err(dev, "failed to set reg THDL_PS at resume.\n"); + return ret; + } + } + + if (data->ps_thdh != STK3310_PS_MAX_VAL) { + buf = cpu_to_be16(data->ps_thdh); + ret = regmap_bulk_write(data->regmap, STK3310_REG_THDH_PS, &buf, 2); + if (ret < 0) { + dev_err(dev, "failed to set reg THDH_PS at resume.\n"); + return ret; + } + } + + if (data->ps_int_enabled) { + ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN); + if (ret < 0) { + dev_err(dev, "failed to enable ps int at resume.\n"); + return ret; + } + } + + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, From ac4129478a2416eecdb09e78f7d7b5d7286b9ac6 Mon Sep 17 00:00:00 2001 From: Miao Li Date: Mon, 4 May 2026 11:04:07 +0800 Subject: [PATCH 2276/5214] iio: light: stk3310: Replace uint32_t with u32 and reorder members to eliminate padding Replace the uint32_t type members in struct stk3310_data with u32 to adhere to the unified kernel coding style, and reorder the member variables to eliminate memory padding holes. Suggested-by: Andy Shevchenko Signed-off-by: Miao Li Reviewed-by: Joshua Crofts Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 21118b746789..fb3d9821edee 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -128,12 +128,6 @@ static const int stk3310_it_table[][2] = { struct stk3310_data { struct i2c_client *client; struct mutex lock; - bool als_enabled; - bool ps_enabled; - bool ps_int_enabled; - uint32_t ps_thdl; - uint32_t ps_thdh; - uint32_t ps_near_level; u64 timestamp; struct regmap *regmap; struct regmap_field *reg_state; @@ -144,6 +138,12 @@ struct stk3310_data { struct regmap_field *reg_int_ps; struct regmap_field *reg_flag_psint; struct regmap_field *reg_flag_nf; + u32 ps_thdl; + u32 ps_thdh; + u32 ps_near_level; + bool als_enabled; + bool ps_enabled; + bool ps_int_enabled; }; static const struct iio_event_spec stk3310_events[] = { From a4c18ea894e5587bcc6be787fcd77c0c19beaa1d Mon Sep 17 00:00:00 2001 From: Miao Li Date: Mon, 4 May 2026 11:04:08 +0800 Subject: [PATCH 2277/5214] iio: light: stk3310: Use sizeof() for regmap_bulk_read/write count parameter Convert the hardcoded count parameter to sizeof(buf) for all regmap_bulk_write() and regmap_bulk_read() calls in this driver to improve code maintainability. For details, see [1]. Link: https://lore.kernel.org/all/20260428192213.7c5c80e5@jic23-huawei/ #[1] Signed-off-by: Miao Li Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index fb3d9821edee..60407bc87ccf 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -271,7 +271,7 @@ static int stk3310_read_event(struct iio_dev *indio_dev, return -EINVAL; mutex_lock(&data->lock); - ret = regmap_bulk_read(data->regmap, reg, &buf, 2); + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); mutex_unlock(&data->lock); if (ret < 0) { dev_err(&data->client->dev, "register read failed\n"); @@ -311,7 +311,7 @@ static int stk3310_write_event(struct iio_dev *indio_dev, return -EINVAL; buf = cpu_to_be16(val); - ret = regmap_bulk_write(data->regmap, reg, &buf, 2); + ret = regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "failed to set PS threshold!\n"); return ret; @@ -389,7 +389,7 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, reg = STK3310_REG_PS_DATA_MSB; mutex_lock(&data->lock); - ret = regmap_bulk_read(data->regmap, reg, &buf, 2); + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "register read failed\n"); mutex_unlock(&data->lock); @@ -739,7 +739,7 @@ static int stk3310_resume(struct device *dev) if (data->ps_thdl != 0x0) { buf = cpu_to_be16(data->ps_thdl); - ret = regmap_bulk_write(data->regmap, STK3310_REG_THDL_PS, &buf, 2); + ret = regmap_bulk_write(data->regmap, STK3310_REG_THDL_PS, &buf, sizeof(buf)); if (ret < 0) { dev_err(dev, "failed to set reg THDL_PS at resume.\n"); return ret; @@ -748,7 +748,7 @@ static int stk3310_resume(struct device *dev) if (data->ps_thdh != STK3310_PS_MAX_VAL) { buf = cpu_to_be16(data->ps_thdh); - ret = regmap_bulk_write(data->regmap, STK3310_REG_THDH_PS, &buf, 2); + ret = regmap_bulk_write(data->regmap, STK3310_REG_THDH_PS, &buf, sizeof(buf)); if (ret < 0) { dev_err(dev, "failed to set reg THDH_PS at resume.\n"); return ret; From c49965ae6b0c1a6362737fbce1e06617882f51fc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:29:04 +0200 Subject: [PATCH 2278/5214] iio: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the drivers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sgp30.c | 3 +-- drivers/iio/light/cm3232.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index 21730d62b5c8..d42b62b7081e 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -498,6 +498,7 @@ static const struct of_device_id sgp_dt_ids[] = { { .compatible = "sensirion,sgpc3", .data = &sgp_devices[SGPC3] }, { } }; +MODULE_DEVICE_TABLE(of, sgp_dt_ids); static int sgp_probe(struct i2c_client *client) { @@ -566,9 +567,7 @@ static const struct i2c_device_id sgp_id[] = { { "sgpc3", (kernel_ulong_t)&sgp_devices[SGPC3] }, { } }; - MODULE_DEVICE_TABLE(i2c, sgp_id); -MODULE_DEVICE_TABLE(of, sgp_dt_ids); static struct i2c_driver sgp_driver = { .driver = { diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index 3a3ad6b4c468..90aa77e3a03b 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -369,6 +369,7 @@ static const struct i2c_device_id cm3232_id[] = { { "cm3232" }, { } }; +MODULE_DEVICE_TABLE(i2c, cm3232_id); static int cm3232_suspend(struct device *dev) { @@ -400,8 +401,6 @@ static int cm3232_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume); -MODULE_DEVICE_TABLE(i2c, cm3232_id); - static const struct of_device_id cm3232_of_match[] = { {.compatible = "capella,cm3232"}, { } From ccf300c36bd6c79eeef4f4122fd2e58acfa46dbb Mon Sep 17 00:00:00 2001 From: Guilherme Dias Date: Mon, 4 May 2026 16:04:25 -0300 Subject: [PATCH 2279/5214] iio: gyro: adxrs290: Use guard(mutex) in lieu of manual lock+unlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use guard(mutex)() to automatically release the lock on scope exit, simplifying the error handling path and removing the need for explicit unlock and goto-based cleanup. Signed-off-by: Guilherme Dias Co-developed-by: João Paulo Menezes Linaris Signed-off-by: João Paulo Menezes Linaris Reviewed-by: Andy Shevchenko Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adxrs290.c | 75 +++++++++++++++---------------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 3efe385ebedc..35928383d4f2 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -115,65 +116,53 @@ static const int adxrs290_hpf_3db_freq_hz_table[][2] = { static int adxrs290_get_rate_data(struct iio_dev *indio_dev, const u8 cmd, int *val) { struct adxrs290_state *st = iio_priv(indio_dev); - int ret = 0; int temp; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + temp = spi_w8r16(st->spi, cmd); - if (temp < 0) { - ret = temp; - goto err_unlock; - } + if (temp < 0) + return temp; *val = sign_extend32(temp, 15); -err_unlock: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adxrs290_get_temp_data(struct iio_dev *indio_dev, int *val) { const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_TEMP0); struct adxrs290_state *st = iio_priv(indio_dev); - int ret = 0; int temp; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + temp = spi_w8r16(st->spi, cmd); - if (temp < 0) { - ret = temp; - goto err_unlock; - } + if (temp < 0) + return temp; /* extract lower 12 bits temperature reading */ *val = sign_extend32(temp, 11); -err_unlock: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adxrs290_get_3db_freq(struct iio_dev *indio_dev, u8 *val, u8 *val2) { const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_FILTER); struct adxrs290_state *st = iio_priv(indio_dev); - int ret = 0; short temp; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); + temp = spi_w8r8(st->spi, cmd); - if (temp < 0) { - ret = temp; - goto err_unlock; - } + if (temp < 0) + return temp; *val = FIELD_GET(ADXRS290_LPF_MASK, temp); *val2 = FIELD_GET(ADXRS290_HPF_MASK, temp); -err_unlock: - mutex_unlock(&st->lock); - return ret; + return 0; } static int adxrs290_spi_write_reg(struct spi_device *spi, const u8 reg, @@ -220,11 +209,11 @@ static int adxrs290_set_mode(struct iio_dev *indio_dev, enum adxrs290_mode mode) if (st->mode == mode) return 0; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = spi_w8r8(st->spi, ADXRS290_READ_REG(ADXRS290_REG_POWER_CTL)); if (ret < 0) - goto out_unlock; + return ret; val = ret; @@ -236,21 +225,18 @@ static int adxrs290_set_mode(struct iio_dev *indio_dev, enum adxrs290_mode mode) val |= ADXRS290_MEASUREMENT; break; default: - ret = -EINVAL; - goto out_unlock; + return -EINVAL; } ret = adxrs290_spi_write_reg(st->spi, ADXRS290_REG_POWER_CTL, val); if (ret < 0) { dev_err(&st->spi->dev, "unable to set mode: %d\n", ret); - goto out_unlock; + return ret; } /* update cached mode */ st->mode = mode; -out_unlock: - mutex_unlock(&st->lock); return ret; } @@ -506,19 +492,20 @@ static irqreturn_t adxrs290_trigger_handler(int irq, void *p) u8 tx = ADXRS290_READ_REG(ADXRS290_REG_DATAX0); int ret; - mutex_lock(&st->lock); + do { + guard(mutex)(&st->lock); - /* exercise a bulk data capture starting from reg DATAX0... */ - ret = spi_write_then_read(st->spi, &tx, sizeof(tx), st->buffer.channels, - sizeof(st->buffer.channels)); - if (ret < 0) - goto out_unlock_notify; + /* exercise a bulk data capture starting from reg DATAX0... */ + ret = spi_write_then_read(st->spi, &tx, sizeof(tx), + st->buffer.channels, + sizeof(st->buffer.channels)); + if (ret < 0) + break; - iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer, - pf->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer, + pf->timestamp); + } while (0); -out_unlock_notify: - mutex_unlock(&st->lock); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; From 0a5f45ed2342aabae1e32c72558d15be28940a95 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:26 +0200 Subject: [PATCH 2280/5214] iio: light: si1133: reset counter to prevent race condition Sashiko reported a potential race condition happening when the driver returns an errno after a timeout in the si1133_command() function. The premature exit causes the hardware and software counters to become out of sync by not updating data->rsp_seq, therefore the internal hardware counter keeps incrementing. Fix this by adding a call to si1133_cmd_reset_counter() before returning from timeout. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 44fa152dbd24..c88c79202be2 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -427,6 +427,11 @@ static int si1133_command(struct si1133_data *data, u8 cmd) dev_warn(dev, "Failed to read command 0x%02x, ret=%d\n", cmd, err); + /* + * Reset counter on err to prevent software and hardware + * counters being out of sync. + */ + si1133_cmd_reset_counter(data); goto out; } } From 8c50a95ceb230d17801758a9e41ffbbbe46f8b4d Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:27 +0200 Subject: [PATCH 2281/5214] iio: light: si1133: prevent race condition on timeout Sashiko reported a bug where the si1133_command exits on timeout without halting the sensor or masking the interrupt. If the sensor completes the command later, any subsequent command to the sensor will cause the IRQ handler to complete immediately, returning stale data to the driver all while the command hasn't finished yet, shifting all potential reads in the future. Fix this by masking the IRQ if wait_for_completion_timeout() fails. When initiating a new command, do a dummy read of the IRQ_STATUS register and turn the IRQ back on. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index c88c79202be2..bf7bf0f1631d 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -395,8 +395,14 @@ static int si1133_command(struct si1133_data *data, u8 cmd) expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR; - if (cmd == SI1133_CMD_FORCE) + if (cmd == SI1133_CMD_FORCE) { + /* Flush pending IRQs from a previous timeout. */ + regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &resp); + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, + SI1133_IRQ_CHANNEL_ENABLE); + reinit_completion(&data->completion); + } err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd); if (err) { @@ -409,6 +415,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) /* wait for irq */ if (!wait_for_completion_timeout(&data->completion, msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) { + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0); err = -ETIMEDOUT; goto out; } From ff032cc038d57bf91986be60dfabf4f5262c4198 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:28 +0200 Subject: [PATCH 2282/5214] iio: light: si1133: remove unused macros Remove unused macros unrelated to hardware definition. No functional change. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index bf7bf0f1631d..ed9b3e0a12d6 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -86,13 +86,9 @@ #define SI1133_CMD_MINSLEEP_US_LOW 5000 #define SI1133_CMD_MINSLEEP_US_HIGH 7500 #define SI1133_CMD_TIMEOUT_MS 25 -#define SI1133_CMD_LUX_TIMEOUT_MS 5000 -#define SI1133_CMD_TIMEOUT_US SI1133_CMD_TIMEOUT_MS * 1000 #define SI1133_REG_HOSTOUT(x) (x) + 0x13 -#define SI1133_MEASUREMENT_FREQUENCY 1250 - #define SI1133_X_ORDER_MASK 0x0070 #define SI1133_Y_ORDER_MASK 0x0007 #define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 4 From a3bed7bfc42d647f527361ffb5ed64eadfefc76b Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:29 +0200 Subject: [PATCH 2283/5214] iio: light: si1133: prefer complex macros enclosed in parenthesis Enclose complex macros in parenthesis per checkpatch.pl error to improve code style. Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index ed9b3e0a12d6..9abc0a7e2ecf 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -50,23 +50,23 @@ #define SI1133_MAX_CMD_CTR 0xF #define SI1133_PARAM_REG_CHAN_LIST 0x01 -#define SI1133_PARAM_REG_ADCCONFIG(x) ((x) * 4) + 2 -#define SI1133_PARAM_REG_ADCSENS(x) ((x) * 4) + 3 -#define SI1133_PARAM_REG_ADCPOST(x) ((x) * 4) + 4 +#define SI1133_PARAM_REG_ADCCONFIG(x) (((x) * 4) + 2) +#define SI1133_PARAM_REG_ADCSENS(x) (((x) * 4) + 3) +#define SI1133_PARAM_REG_ADCPOST(x) (((x) * 4) + 4) #define SI1133_ADCMUX_MASK 0x1F -#define SI1133_ADCCONFIG_DECIM_RATE(x) (x) << 5 +#define SI1133_ADCCONFIG_DECIM_RATE(x) ((x) << 5) #define SI1133_ADCSENS_SCALE_MASK 0x70 #define SI1133_ADCSENS_SCALE_SHIFT 4 #define SI1133_ADCSENS_HSIG_MASK BIT(7) #define SI1133_ADCSENS_HSIG_SHIFT 7 #define SI1133_ADCSENS_HW_GAIN_MASK 0xF -#define SI1133_ADCSENS_NB_MEAS(x) fls(x) << SI1133_ADCSENS_SCALE_SHIFT +#define SI1133_ADCSENS_NB_MEAS(x) (fls(x) << SI1133_ADCSENS_SCALE_SHIFT) #define SI1133_ADCPOST_24BIT_EN BIT(6) -#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (x & GENMASK(2, 0)) << 3 +#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (((x) & GENMASK(2, 0)) << 3) #define SI1133_PARAM_ADCMUX_SMALL_IR 0x0 #define SI1133_PARAM_ADCMUX_MED_IR 0x1 @@ -87,11 +87,11 @@ #define SI1133_CMD_MINSLEEP_US_HIGH 7500 #define SI1133_CMD_TIMEOUT_MS 25 -#define SI1133_REG_HOSTOUT(x) (x) + 0x13 +#define SI1133_REG_HOSTOUT(x) ((x) + 0x13) #define SI1133_X_ORDER_MASK 0x0070 #define SI1133_Y_ORDER_MASK 0x0007 -#define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 4 +#define si1133_get_x_order(m) (((m) & SI1133_X_ORDER_MASK) >> 4) #define si1133_get_y_order(m) ((m) & SI1133_Y_ORDER_MASK) #define SI1133_LUX_ADC_MASK 0xE From dd2701f0b2a38f416332605e7fbb0470521591f6 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:30 +0200 Subject: [PATCH 2284/5214] iio: light: si1133: add missing include headers Add missing include headers to prevent compilation relying on transient dependencies (array_size.h, bitops.h, completion.h, dev_printk.h, err.h, jiffies.h, math.h, mod_devicetable.h, mutex.h, types.h). Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 9abc0a7e2ecf..ab66a5a9ffb4 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -6,11 +6,21 @@ * Copyright 2018 Maxime Roussin-Belanger */ +#include +#include +#include #include +#include +#include #include #include +#include +#include +#include #include +#include #include +#include #include #include From fb94aeb67415cbeb3de944e6e570c55f75c0d62c Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:31 +0200 Subject: [PATCH 2285/5214] iio: light: si1133: group generic headers Group generic include headers to improve code style. No functional change. Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index ab66a5a9ffb4..fdffdee16e49 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -21,14 +21,12 @@ #include #include #include +#include +#include #include #include -#include - -#include - #define SI1133_REG_PART_ID 0x00 #define SI1133_REG_REV_ID 0x01 #define SI1133_REG_MFR_ID 0x02 From 82108871f44483ab7e0291f1c70b5461ed233ab8 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:32 +0200 Subject: [PATCH 2286/5214] iio: light: si1133: add local variable for timeout Add local variable for timeout to improve readability. No functional change. Suggested-by: Jonathan Cameron Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index fdffdee16e49..ef5c38e303a6 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -390,6 +390,7 @@ static int si1133_cmd_reset_counter(struct si1133_data *data) static int si1133_command(struct si1133_data *data, u8 cmd) { + unsigned long timeout; struct device *dev = &data->client->dev; u32 resp; int err; @@ -417,8 +418,8 @@ static int si1133_command(struct si1133_data *data, u8 cmd) if (cmd == SI1133_CMD_FORCE) { /* wait for irq */ - if (!wait_for_completion_timeout(&data->completion, - msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) { + timeout = msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS); + if (!wait_for_completion_timeout(&data->completion, timeout)) { regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0); err = -ETIMEDOUT; goto out; From d0b396cab2f7d56126a9b36dba9d6c52292aa7bc Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:33 +0200 Subject: [PATCH 2287/5214] iio: light: si1133: use guard(mutex)() macro Remove mutex_lock()/mutex_unlock() and goto instances and add guard(mutex)() macro to modernize driver and improve mutex handling. Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index ef5c38e303a6..655fa778b4a6 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -396,7 +397,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) int err; int expected_seq; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR; @@ -413,7 +414,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) if (err) { dev_warn(dev, "Failed to write command 0x%02x, ret=%d\n", cmd, err); - goto out; + return err; } if (cmd == SI1133_CMD_FORCE) { @@ -421,12 +422,11 @@ static int si1133_command(struct si1133_data *data, u8 cmd) timeout = msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS); if (!wait_for_completion_timeout(&data->completion, timeout)) { regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0); - err = -ETIMEDOUT; - goto out; + return -ETIMEDOUT; } err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp); if (err) - goto out; + return err; } else { err = regmap_read_poll_timeout(data->regmap, SI1133_REG_RESPONSE0, resp, @@ -444,7 +444,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) * counters being out of sync. */ si1133_cmd_reset_counter(data); - goto out; + return err; } } @@ -455,9 +455,6 @@ static int si1133_command(struct si1133_data *data, u8 cmd) data->rsp_seq = expected_seq; } -out: - mutex_unlock(&data->mutex); - return err; } From 5a6249110e0f845bfe181ccc0c275995f26469cf Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 30 Apr 2026 07:41:47 -0500 Subject: [PATCH 2288/5214] iio: magnetometer: rm3100: Modernize locking and refactor control flow Replace mutex_lock() and mutex_unlock() calls in rm3100-core.c with the more modern guard(mutex)() family. This will help modernize the driver and bring it up-to-date with modern available macros/functions. While replacing mutex_lock() and mutex_unlock(), the critical sections of rm3100_read_mag() and rm3100_get_samp_freq() have been extended to include negligible operations for cleaner logic. Add new helper-wrapper function rm3100_regmap_bulk_read_locked() to help keep rm3100_trigger_handler() switch-cases clean while maintaining mutex locking and avoiding re-entrancy risks from potential callbacks. While at it, remove redundant gotos where applicable, and use direct returns instead. In addition, remove regmap variable in rm3100_trigger_handler() as its references have been replaced with variable data. Suggested-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Signed-off-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/rm3100-core.c | 94 ++++++++++++++------------ 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index 2b2884425746..ac3f9f7fc808 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -204,27 +204,23 @@ static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val) u8 buffer[3]; int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + ret = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx)); if (ret < 0) - goto unlock_return; + return ret; ret = rm3100_wait_measurement(data); if (ret < 0) - goto unlock_return; + return ret; ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3); if (ret < 0) - goto unlock_return; - mutex_unlock(&data->lock); + return ret; *val = sign_extend32(get_unaligned_be24(&buffer[0]), 23); return IIO_VAL_INT; - -unlock_return: - mutex_unlock(&data->lock); - return ret; } #define RM3100_CHANNEL(axis, idx) \ @@ -284,11 +280,12 @@ static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2) unsigned int tmp; int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp); - mutex_unlock(&data->lock); if (ret < 0) return ret; + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0]; *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1]; @@ -338,56 +335,50 @@ static int rm3100_set_samp_freq(struct iio_dev *indio_dev, int val, int val2) int ret; int i; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + /* All cycle count registers use the same value. */ ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count); if (ret < 0) - goto unlock_return; + return ret; for (i = 0; i < RM3100_SAMP_NUM; i++) { if (val == rm3100_samp_rates[i][0] && val2 == rm3100_samp_rates[i][1]) break; } - if (i == RM3100_SAMP_NUM) { - ret = -EINVAL; - goto unlock_return; - } + if (i == RM3100_SAMP_NUM) + return -EINVAL; ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET); if (ret < 0) - goto unlock_return; + return ret; /* Checking if cycle count registers need changing. */ if (val == 600 && cycle_count == 200) { ret = rm3100_set_cycle_count(data, 100); if (ret < 0) - goto unlock_return; + return ret; } else if (val != 600 && cycle_count == 100) { ret = rm3100_set_cycle_count(data, 200); if (ret < 0) - goto unlock_return; + return ret; } if (iio_buffer_enabled(indio_dev)) { /* Writing TMRC registers requires CMM reset. */ ret = regmap_write(regmap, RM3100_REG_CMM, 0); if (ret < 0) - goto unlock_return; + return ret; ret = regmap_write(data->regmap, RM3100_REG_CMM, (*indio_dev->active_scan_mask & 0x7) << RM3100_CMM_AXIS_SHIFT | RM3100_CMM_START); if (ret < 0) - goto unlock_return; + return ret; } - mutex_unlock(&data->lock); data->conversion_time = rm3100_samp_rates[i][2] * 2; return 0; - -unlock_return: - mutex_unlock(&data->lock); - return ret; } static int rm3100_read_raw(struct iio_dev *indio_dev, @@ -458,6 +449,27 @@ static const struct iio_buffer_setup_ops rm3100_buffer_ops = { .postdisable = rm3100_buffer_postdisable, }; +/** + * rm3100_regmap_bulk_read_locked() - Wrapper around regmap_bulk_read() with a mutex + * + * @data: Data structure containing regmap and mutex + * @reg: First register to be read from, passed to regmap_bulk_read() + * @val: Pointer to store read value, in native register size for device, + * passed to regmap_bulk_read() + * @val_count: Number of registers to read, passed to regmap_bulk_read() + * + * Intended for use only in rm3100_trigger_handler(). + * + * Return: + * A value of zero on success, a negative errno in error cases. + */ +static int rm3100_regmap_bulk_read_locked(struct rm3100_data *data, unsigned int reg, + void *val, size_t val_count) +{ + guard(mutex)(&data->lock); + return regmap_bulk_read(data->regmap, reg, val, val_count); +} + static irqreturn_t rm3100_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -465,14 +477,12 @@ static irqreturn_t rm3100_trigger_handler(int irq, void *p) unsigned long scan_mask = *indio_dev->active_scan_mask; unsigned int mask_len = iio_get_masklength(indio_dev); struct rm3100_data *data = iio_priv(indio_dev); - struct regmap *regmap = data->regmap; int ret, i, bit; - mutex_lock(&data->lock); switch (scan_mask) { case BIT(0) | BIT(1) | BIT(2): - ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9); - mutex_unlock(&data->lock); + ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2, + data->buffer, 9); if (ret < 0) goto done; /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */ @@ -480,36 +490,34 @@ static irqreturn_t rm3100_trigger_handler(int irq, void *p) memmove(data->buffer + i * 4, data->buffer + i * 3, 3); break; case BIT(0) | BIT(1): - ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6); - mutex_unlock(&data->lock); + ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2, + data->buffer, 6); if (ret < 0) goto done; memmove(data->buffer + 4, data->buffer + 3, 3); break; case BIT(1) | BIT(2): - ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6); - mutex_unlock(&data->lock); + ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MY2, + data->buffer, 6); if (ret < 0) goto done; memmove(data->buffer + 4, data->buffer + 3, 3); break; case BIT(0) | BIT(2): - ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9); - mutex_unlock(&data->lock); + ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2, + data->buffer, 9); if (ret < 0) goto done; memmove(data->buffer + 4, data->buffer + 6, 3); break; default: for_each_set_bit(bit, &scan_mask, mask_len) { - ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit, - data->buffer, 3); - if (ret < 0) { - mutex_unlock(&data->lock); + ret = rm3100_regmap_bulk_read_locked(data, + RM3100_REG_MX2 + 3 * bit, + data->buffer, 3); + if (ret < 0) goto done; - } } - mutex_unlock(&data->lock); } /* * Always using the same buffer so that we wouldn't need to set the From 0c794147f2450a7948f22820698607b4e1b7c49e Mon Sep 17 00:00:00 2001 From: Marcelo Machado Lage Date: Wed, 29 Apr 2026 19:44:00 -0300 Subject: [PATCH 2289/5214] iio: adc: mcp3422: rewrite mask macros with help of bits.h APIs Rewrite MCP3422_CHANNEL_MASK, MCP3422_SRATE_MASK, MCP3422_PGA_MASK and MCP3422_CONT_SAMPLING using GENMASK() and BIT() macros from bits.h. The other macros MCP3422_SRATE_{240, 60, 15, 3} were not changed because they are also used as array indices. Signed-off-by: Marcelo Machado Lage Co-developed-by: Vinicius Lira Signed-off-by: Vinicius Lira Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3422.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 50834fdcf738..0cd0e7d39e39 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -13,6 +13,7 @@ * voltage unit is nV. */ +#include #include #include #include @@ -24,10 +25,10 @@ #include #include -/* Masks */ -#define MCP3422_CHANNEL_MASK 0x60 -#define MCP3422_PGA_MASK 0x03 -#define MCP3422_SRATE_MASK 0x0C +#define MCP3422_CHANNEL_MASK GENMASK(6, 5) +#define MCP3422_SRATE_MASK GENMASK(3, 2) +#define MCP3422_PGA_MASK GENMASK(1, 0) + #define MCP3422_SRATE_240 0x0 #define MCP3422_SRATE_60 0x1 #define MCP3422_SRATE_15 0x2 @@ -36,7 +37,7 @@ #define MCP3422_PGA_2 1 #define MCP3422_PGA_4 2 #define MCP3422_PGA_8 3 -#define MCP3422_CONT_SAMPLING 0x10 +#define MCP3422_CONT_SAMPLING BIT(4) #define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5) #define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK) From 484f68c753373d5f5c02a6014bdc8aa28fb820a3 Mon Sep 17 00:00:00 2001 From: Marcelo Machado Lage Date: Wed, 29 Apr 2026 19:44:01 -0300 Subject: [PATCH 2290/5214] iio: adc: mcp3422: write bit operations using bitfield.h APIs Replace manual bit manipulations with FIELD_GET(), FIELD_PREP() and FIELD_MODIFY() calls. The resulting code is more readable and maintainable, and 6 macros previously defined in the header are not needed anymore. Signed-off-by: Marcelo Machado Lage Co-developed-by: Vinicius Lira Signed-off-by: Vinicius Lira Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3422.c | 54 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 0cd0e7d39e39..0cbd7a874798 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -13,6 +13,7 @@ * voltage unit is nV. */ +#include #include #include #include @@ -39,14 +40,6 @@ #define MCP3422_PGA_8 3 #define MCP3422_CONT_SAMPLING BIT(4) -#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5) -#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK) -#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2) - -#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK) -#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK) -#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK) - #define MCP3422_CHAN(_index) \ { \ .type = IIO_VOLTAGE, \ @@ -109,7 +102,7 @@ static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig) static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config) { int ret = 0; - u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); u8 buf[4] = {0, 0, 0, 0}; u32 temp; @@ -137,18 +130,18 @@ static int mcp3422_read_channel(struct mcp3422 *adc, mutex_lock(&adc->lock); - if (req_channel != MCP3422_CHANNEL(adc->config)) { + if (req_channel != FIELD_GET(MCP3422_CHANNEL_MASK, adc->config)) { config = adc->config; - config &= ~MCP3422_CHANNEL_MASK; - config |= MCP3422_CHANNEL_VALUE(req_channel); - config &= ~MCP3422_PGA_MASK; - config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); + + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_PGA_MASK, &config, adc->pga[req_channel]); + ret = mcp3422_update_config(adc, config); if (ret < 0) { mutex_unlock(&adc->lock); return ret; } - msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]); + msleep(mcp3422_read_times[FIELD_GET(MCP3422_SRATE_MASK, adc->config)]); } ret = mcp3422_read(adc, value, &config); @@ -164,9 +157,8 @@ static int mcp3422_read_raw(struct iio_dev *iio, { struct mcp3422 *adc = iio_priv(iio); int err; - - u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); - u8 pga = MCP3422_PGA(adc->config); + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); + u8 pga = FIELD_GET(MCP3422_PGA_MASK, adc->config); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -182,7 +174,7 @@ static int mcp3422_read_raw(struct iio_dev *iio, return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_SAMP_FREQ: - *val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)]; + *val1 = mcp3422_sample_rates[FIELD_GET(MCP3422_SRATE_MASK, adc->config)]; return IIO_VAL_INT; default: @@ -200,7 +192,7 @@ static int mcp3422_write_raw(struct iio_dev *iio, u8 temp; u8 config = adc->config; u8 req_channel = channel->channel; - u8 sample_rate = MCP3422_SAMPLE_RATE(config); + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, config); u8 i; switch (mask) { @@ -212,10 +204,8 @@ static int mcp3422_write_raw(struct iio_dev *iio, if (val2 == mcp3422_scales[sample_rate][i]) { adc->pga[req_channel] = i; - config &= ~MCP3422_CHANNEL_MASK; - config |= MCP3422_CHANNEL_VALUE(req_channel); - config &= ~MCP3422_PGA_MASK; - config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_PGA_MASK, &config, adc->pga[req_channel]); return mcp3422_update_config(adc, config); } @@ -242,10 +232,8 @@ static int mcp3422_write_raw(struct iio_dev *iio, return -EINVAL; } - config &= ~MCP3422_CHANNEL_MASK; - config |= MCP3422_CHANNEL_VALUE(req_channel); - config &= ~MCP3422_SRATE_MASK; - config |= MCP3422_SAMPLE_RATE_VALUE(temp); + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_SRATE_MASK, &config, temp); return mcp3422_update_config(adc, config); @@ -284,7 +272,7 @@ static ssize_t mcp3422_show_scales(struct device *dev, struct device_attribute *attr, char *buf) { struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); - u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n", mcp3422_scales[sample_rate][0], @@ -377,10 +365,10 @@ static int mcp3422_probe(struct i2c_client *client) } /* meaningful default configuration */ - config = (MCP3422_CONT_SAMPLING - | MCP3422_CHANNEL_VALUE(0) - | MCP3422_PGA_VALUE(MCP3422_PGA_1) - | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); + config = MCP3422_CONT_SAMPLING | + FIELD_PREP(MCP3422_CHANNEL_MASK, 0) | + FIELD_PREP(MCP3422_PGA_MASK, MCP3422_PGA_1) | + FIELD_PREP(MCP3422_SRATE_MASK, MCP3422_SRATE_240); err = mcp3422_update_config(adc, config); if (err < 0) return err; From 4bb316664b7b857a1e57280164813f95a7e05a6a Mon Sep 17 00:00:00 2001 From: Pedro Barletta Gennari Date: Tue, 28 Apr 2026 22:29:54 -0300 Subject: [PATCH 2291/5214] iio: light: iqs621-als: use lock guards Use guard(mutex)() for handling mutex lock instead of manually locking and unlocking the mutex. This prevents forgotten locks due to early exits and removes the need of gotos. Signed-off-by: Pedro Barletta Gennari Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/iqs621-als.c | 93 ++++++++++++---------------------- 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c index b9f230210f07..25a655b328fc 100644 --- a/drivers/iio/light/iqs621-als.c +++ b/drivers/iio/light/iqs621-als.c @@ -5,6 +5,7 @@ * Copyright (C) 2019 Jeff LaBundy */ +#include #include #include #include @@ -107,26 +108,20 @@ static int iqs621_als_notifier(struct notifier_block *notifier, indio_dev = iqs621_als->indio_dev; timestamp = iio_get_time_ns(indio_dev); - mutex_lock(&iqs621_als->lock); + guard(mutex)(&iqs621_als->lock); if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) { ret = iqs621_als_init(iqs621_als); if (ret) { dev_err(indio_dev->dev.parent, "Failed to re-initialize device: %d\n", ret); - ret = NOTIFY_BAD; - } else { - ret = NOTIFY_OK; + return NOTIFY_BAD; } - - goto err_mutex; + return NOTIFY_OK; } - if (!iqs621_als->light_en && !iqs621_als->range_en && - !iqs621_als->prox_en) { - ret = NOTIFY_DONE; - goto err_mutex; - } + if (!iqs621_als->light_en && !iqs621_als->range_en && !iqs621_als->prox_en) + return NOTIFY_DONE; /* IQS621 only */ light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT; @@ -181,12 +176,7 @@ static int iqs621_als_notifier(struct notifier_block *notifier, iqs621_als->als_flags = event_data->als_flags; iqs621_als->ir_flags = event_data->ir_flags; - ret = NOTIFY_OK; - -err_mutex: - mutex_unlock(&iqs621_als->lock); - - return ret; + return NOTIFY_OK; } static void iqs621_als_notifier_unregister(void *context) @@ -241,30 +231,22 @@ static int iqs621_als_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); - int ret; - mutex_lock(&iqs621_als->lock); + guard(mutex)(&iqs621_als->lock); switch (chan->type) { case IIO_LIGHT: - ret = iqs621_als->light_en; - break; + return iqs621_als->light_en; case IIO_INTENSITY: - ret = iqs621_als->range_en; - break; + return iqs621_als->range_en; case IIO_PROXIMITY: - ret = iqs621_als->prox_en; - break; + return iqs621_als->prox_en; default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&iqs621_als->lock); - - return ret; } static int iqs621_als_write_event_config(struct iio_dev *indio_dev, @@ -278,11 +260,11 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, unsigned int val; int ret; - mutex_lock(&iqs621_als->lock); + guard(mutex)(&iqs621_als->lock); ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val); if (ret) - goto err_mutex; + return ret; iqs621_als->als_flags = val; switch (chan->type) { @@ -293,7 +275,7 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, 0xFF); if (!ret) iqs621_als->light_en = state; - break; + return ret; case IIO_INTENSITY: ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, @@ -302,12 +284,12 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, 0xFF); if (!ret) iqs621_als->range_en = state; - break; + return ret; case IIO_PROXIMITY: ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val); if (ret) - goto err_mutex; + return ret; iqs621_als->ir_flags = val; ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, @@ -315,16 +297,11 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, state ? 0 : 0xFF); if (!ret) iqs621_als->prox_en = state; - break; + return ret; default: - ret = -EINVAL; + return -EINVAL; } - -err_mutex: - mutex_unlock(&iqs621_als->lock); - - return ret; } static int iqs621_als_read_event_value(struct iio_dev *indio_dev, @@ -335,33 +312,28 @@ static int iqs621_als_read_event_value(struct iio_dev *indio_dev, int *val, int *val2) { struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); - int ret = IIO_VAL_INT; - mutex_lock(&iqs621_als->lock); + guard(mutex)(&iqs621_als->lock); switch (dir) { case IIO_EV_DIR_RISING: *val = iqs621_als->thresh_light * 16; - break; + return IIO_VAL_INT; case IIO_EV_DIR_FALLING: *val = iqs621_als->thresh_dark * 4; - break; + return IIO_VAL_INT; case IIO_EV_DIR_EITHER: if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH) *val = iqs621_als->thresh_prox * 4; else *val = iqs621_als->thresh_prox; - break; + return IIO_VAL_INT; default: - ret = -EINVAL; + return -EINVAL; } - - mutex_unlock(&iqs621_als->lock); - - return ret; } static int iqs621_als_write_event_value(struct iio_dev *indio_dev, @@ -375,9 +347,9 @@ static int iqs621_als_write_event_value(struct iio_dev *indio_dev, struct iqs62x_core *iqs62x = iqs621_als->iqs62x; unsigned int thresh_reg, thresh_val; u8 ir_flags_mask, *thresh_cache; - int ret = -EINVAL; + int ret; - mutex_lock(&iqs621_als->lock); + guard(mutex)(&iqs621_als->lock); switch (dir) { case IIO_EV_DIR_RISING: @@ -426,30 +398,27 @@ static int iqs621_als_write_event_value(struct iio_dev *indio_dev, break; default: - goto err_mutex; + return -EINVAL; } thresh_cache = &iqs621_als->thresh_prox; break; default: - goto err_mutex; + return -EINVAL; } if (thresh_val > 0xFF) - goto err_mutex; + return -EINVAL; ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val); if (ret) - goto err_mutex; + return ret; *thresh_cache = thresh_val; iqs621_als->ir_flags_mask = ir_flags_mask; -err_mutex: - mutex_unlock(&iqs621_als->lock); - - return ret; + return 0; } static const struct iio_info iqs621_als_info = { From f326d8f8f00676f07a40a5f3fbd1c89d854b89bd Mon Sep 17 00:00:00 2001 From: Pedro Barletta Gennari Date: Tue, 28 Apr 2026 22:29:55 -0300 Subject: [PATCH 2292/5214] iio: light: iqs621-als: prefer early error handling over if (!ret) Handle errors as early as possible by replacing 'if (!ret)' with the more common form 'if (ret)'. This makes the code easier to read. Signed-off-by: Pedro Barletta Gennari Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/iqs621-als.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c index 25a655b328fc..cd5843e3e2c3 100644 --- a/drivers/iio/light/iqs621-als.c +++ b/drivers/iio/light/iqs621-als.c @@ -273,18 +273,22 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, iqs62x->dev_desc->als_mask, iqs621_als->range_en || state ? 0 : 0xFF); - if (!ret) - iqs621_als->light_en = state; - return ret; + if (ret) + return ret; + iqs621_als->light_en = state; + + return 0; case IIO_INTENSITY: ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, iqs62x->dev_desc->als_mask, iqs621_als->light_en || state ? 0 : 0xFF); - if (!ret) - iqs621_als->range_en = state; - return ret; + if (ret) + return ret; + iqs621_als->range_en = state; + + return 0; case IIO_PROXIMITY: ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val); @@ -295,9 +299,11 @@ static int iqs621_als_write_event_config(struct iio_dev *indio_dev, ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, iqs62x->dev_desc->ir_mask, state ? 0 : 0xFF); - if (!ret) - iqs621_als->prox_en = state; - return ret; + if (ret) + return ret; + iqs621_als->prox_en = state; + + return 0; default: return -EINVAL; From f2c7187e266c16c7a0034731ee468d76c0492644 Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Tue, 5 May 2026 17:22:43 +0800 Subject: [PATCH 2293/5214] iio: adxl313: fix typos in documentation Add missing space in "ADXL313is" and improve grammar for "a single types of channels" to "multiple channels of a single type" as suggested by Jonathan Cameron. Wrap long line as suggested by Andy Shevchenko. Signed-off-by: Wang Zihan Signed-off-by: Jonathan Cameron --- Documentation/iio/adxl313.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst index 966e72c0109a..cc0829be0447 100644 --- a/Documentation/iio/adxl313.rst +++ b/Documentation/iio/adxl313.rst @@ -11,7 +11,7 @@ This driver supports Analog Device's ADXL313 on SPI/I2C bus. * `ADXL313 `_ -The ADXL313is a low noise density, low power, 3-axis accelerometer with +The ADXL313 is a low noise density, low power, 3-axis accelerometer with selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and ±4 g ranges. @@ -112,9 +112,9 @@ apply the following formula: Where _offset and _scale are device attributes. If no _offset attribute is present, simply assume its value is 0. -The ADXL313 driver offers data for a single types of channels, the table below -shows the measurement units for the processed value, which are defined by the -IIO framework: +The ADXL313 driver offers data for multiple channels of a single type. +The table below shows the measurement units for the processed value, +which are defined by the IIO framework: +-------------------------------------+---------------------------+ | Channel type | Measurement unit | From 67f4f2b31da5120dcdb409b2fe33383437a4bad7 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Tue, 5 May 2026 12:34:31 +0800 Subject: [PATCH 2294/5214] iio: ABI: Add DAC 500ohm, 3.85kohm, and 16kohm powerdown modes Add powerdown mode entries for DACs with 500 Ohm, 3.85 kOhm, and 16 kOhm resistor to ground output impedance states. These are used by the AD3531/AD3531R 4-channel DAC. Signed-off-by: Kim Seer Paller Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 60dfeff8f2c9..925a33fd309a 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -764,10 +764,13 @@ Contact: linux-iio@vger.kernel.org Description: Specifies the output powerdown mode. DAC output stage is disconnected from the amplifier and + 500ohm_to_gnd: connected to ground via a 500Ohm resistor, 1kohm_to_gnd: connected to ground via an 1kOhm resistor, 2.5kohm_to_gnd: connected to ground via a 2.5kOhm resistor, + 3.85kohm_to_gnd: connected to ground via a 3.85kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor, + 16kohm_to_gnd: connected to ground via a 16kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, 32kohm_to_gnd: connected to ground via a 32kOhm resistor, 42kohm_to_gnd: connected to ground via a 42kOhm resistor, From e94944d7364d3ddb273539492f9bd9c9a622549a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:27:55 +0200 Subject: [PATCH 2295/5214] iio: magnetometer: ak8975: Add missed pm_runtime_put_autosuspend() call On the failure in the ak8975_read_axis() the PM runtime gets unbalanced. Balance it by calling pm_runtime_put_autosuspend() on error path as well. Fixes: cde4cb5dd422 ("iio: magn: ak8975: deploy runtime and system PM") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260505-magnetometer-fixes-v5-0-831b9b5550fc%40gmail.com Signed-off-by: Andy Shevchenko Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 44782c26698b..e70101abfcd3 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -784,6 +784,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) exit: mutex_unlock(&data->lock); + pm_runtime_put_autosuspend(&data->client->dev); dev_err(&client->dev, "Error in reading axis\n"); return ret; } From bde5858a2773dcc588b1b9a61943a645d5f1115f Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:45:57 +0200 Subject: [PATCH 2296/5214] iio: magnetometer: ak8975: sort headers alphabetically Sort include headers alphabetically to improve coding style and readability. No functional change. Signed-off-by: Joshua Crofts Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index e70101abfcd3..e8d9151dc330 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -7,23 +7,23 @@ * Copyright (c) 2010, NVIDIA Corporation. */ -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include #include -#include -#include -#include -#include #include +#include +#include +#include #include #include -#include #include #include #include From 9d59def370123edfb32b96b590dcba78a5bd6bf1 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:45:58 +0200 Subject: [PATCH 2297/5214] iio: magnetometer: ak8975: update headers per IWYU principle Remove kernel.h proxy header and unused headers (slab.h, iio/sysfs.h, iio/trigger.h). Add missing headers to ensure atomicity (array_size.h, dev_printk.h, asm/byteorder.h, irqreturn.h, minmax.h, property.h, types.h, wait.h). Audited using the include-what-you-use tool. Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index e8d9151dc330..c401120a5f8b 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -7,24 +7,29 @@ * Copyright (c) 2010, NVIDIA Corporation. */ +#include #include #include +#include #include #include #include #include -#include +#include +#include #include #include #include #include +#include #include -#include +#include +#include + +#include #include #include -#include -#include #include #include From 1fcd1fef65145caa465f2fe967ad7d90ddcfbc95 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:45:59 +0200 Subject: [PATCH 2298/5214] iio: magnetometer: ak8975: replace usleep_range() with fsleep() Replace usleep_range() calls with fsleep(), passing the minimum value required by the sensor for hardware delays. fsleep() automatically selects the optimal sleep mechanism, simplifying driver code and time management. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index c401120a5f8b..b15ac73b37ed 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -461,7 +461,8 @@ static int ak8975_power_on(const struct ak8975_data *data) * and the minimum wait time before mode setting is 100us, in * total 300us. Add some margin and say minimum 500us here. */ - usleep_range(500, 1000); + fsleep(500); + return 0; } @@ -551,7 +552,7 @@ static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode) data->cntl_cache = regval; /* After mode change wait at least 100us */ - usleep_range(100, 500); + fsleep(100); return 0; } From 833ca882b76425a7ec12cc8090a390959d0b190f Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:46:00 +0200 Subject: [PATCH 2299/5214] iio: magnetometer: ak8975: change 'u8*' to 'u8 *' in cast Change 'u8*' cast to 'u8 *' as the former triggers a checkpatch error. Also fix the indentation of parameters in i2c_smbus_read_i2c_block_data_or_emulated() function. No functional change. Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b15ac73b37ed..159c619bd26a 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -759,9 +759,10 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) if (ret) goto exit; - ret = i2c_smbus_read_i2c_block_data_or_emulated( - client, def->data_regs[index], - sizeof(rval), (u8*)&rval); + ret = i2c_smbus_read_i2c_block_data_or_emulated(client, + def->data_regs[index], + sizeof(rval), + (u8 *)&rval); if (ret < 0) goto exit; From acea3560f387a749990567e151e6003ccc249e46 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:46:01 +0200 Subject: [PATCH 2300/5214] iio: magnetometer: ak8975: fix wrong errno on return The driver currently returns -EINVAL on polling timeout instead of -ETIMEDOUT. Replace return code for -ETIMEDOUT and remove unnecessary error message as -ETIMEDOUT is a standard POSIX error. Also replace instances of -EINVAL in comments. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 159c619bd26a..9511528cdd7b 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -662,10 +662,8 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data) break; timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; } - if (!timeout_ms) { - dev_err(&client->dev, "Conversion timeout happened\n"); - return -EINVAL; - } + if (!timeout_ms) + return -ETIMEDOUT; ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]); if (ret < 0) @@ -695,15 +693,13 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) break; timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; } - if (!timeout_ms) { - dev_err(&client->dev, "Conversion timeout happened\n"); - return -EINVAL; - } + if (!timeout_ms) + return -ETIMEDOUT; return read_status; } -/* Returns 0 if the end of conversion interrupt occurred or -ETIME otherwise */ +/* Returns 0 if the end of conversion interrupt occurred or -ETIMEDOUT otherwise */ static int wait_conversion_complete_interrupt(struct ak8975_data *data) { int ret; @@ -713,7 +709,7 @@ static int wait_conversion_complete_interrupt(struct ak8975_data *data) AK8975_DATA_READY_TIMEOUT); clear_bit(0, &data->flags); - return ret > 0 ? 0 : -ETIME; + return ret > 0 ? 0 : -ETIMEDOUT; } static int ak8975_start_read_axis(struct ak8975_data *data, From c0b3561c8e4a1a7c03177fa21376c04f554b3a57 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 13:46:02 +0200 Subject: [PATCH 2301/5214] iio: magnetometer: ak8975: pass conversion timeouts as arguments Refactor wait_conversion_complete*_() helper function to accept poll and timeout values directly as parameters. This improves the readability of the code and does not rely on hardcoded macros. Besides that, fix the home grown and obviously wrong in some cases the jiffy-based timeout. Co-developed-by: Andy Shevchenko Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 37 ++++++++++++++----------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 9511528cdd7b..dcb281149a9d 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -132,13 +133,6 @@ #define AK09912_MAX_REGS AK09912_REG_ASAZ -/* - * Miscellaneous values. - */ -#define AK8975_MAX_CONVERSION_TIMEOUT 500 -#define AK8975_CONVERSION_DONE_POLL_TIME 10 -#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000) - /* * Precalculate scale factor (in Gauss units) for each axis and * store in the device data. @@ -649,18 +643,19 @@ static int ak8975_setup(struct i2c_client *client) return 0; } -static int wait_conversion_complete_gpio(struct ak8975_data *data) +static int wait_conversion_complete_gpio(struct ak8975_data *data, + unsigned int poll_ms, + unsigned int timeout_ms) { struct i2c_client *client = data->client; - u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT; int ret; /* Wait for the conversion to complete. */ while (timeout_ms) { - msleep(AK8975_CONVERSION_DONE_POLL_TIME); + msleep(poll_ms); if (gpiod_get_value(data->eoc_gpiod)) break; - timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; + timeout_ms -= poll_ms; } if (!timeout_ms) return -ETIMEDOUT; @@ -672,16 +667,17 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data) return ret; } -static int wait_conversion_complete_polled(struct ak8975_data *data) +static int wait_conversion_complete_polled(struct ak8975_data *data, + unsigned int poll_ms, + unsigned int timeout_ms) { struct i2c_client *client = data->client; u8 read_status; - u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT; int ret; /* Wait for the conversion to complete. */ while (timeout_ms) { - msleep(AK8975_CONVERSION_DONE_POLL_TIME); + msleep(poll_ms); ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]); if (ret < 0) { @@ -691,7 +687,7 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) read_status = ret; if (read_status) break; - timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME; + timeout_ms -= poll_ms; } if (!timeout_ms) return -ETIMEDOUT; @@ -700,13 +696,14 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) } /* Returns 0 if the end of conversion interrupt occurred or -ETIMEDOUT otherwise */ -static int wait_conversion_complete_interrupt(struct ak8975_data *data) +static int wait_conversion_complete_interrupt(struct ak8975_data *data, + unsigned int timeout_ms) { int ret; ret = wait_event_timeout(data->data_ready_queue, test_bit(0, &data->flags), - AK8975_DATA_READY_TIMEOUT); + msecs_to_jiffies(timeout_ms)); clear_bit(0, &data->flags); return ret > 0 ? 0 : -ETIMEDOUT; @@ -725,11 +722,11 @@ static int ak8975_start_read_axis(struct ak8975_data *data, /* Wait for the conversion to complete. */ if (data->eoc_irq) - ret = wait_conversion_complete_interrupt(data); + ret = wait_conversion_complete_interrupt(data, 100); else if (data->eoc_gpiod) - ret = wait_conversion_complete_gpio(data); + ret = wait_conversion_complete_gpio(data, 10, 500); else - ret = wait_conversion_complete_polled(data); + ret = wait_conversion_complete_polled(data, 10, 500); if (ret < 0) return ret; From 4cff98838322d488016c1b0edfa980fc336de630 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 May 2026 13:46:05 +0200 Subject: [PATCH 2302/5214] iio: magnetometer: ak8975: avoid using temporary variable Avoid using temporary variable in ak8975_read_axis(). With that being done, the clamp_t() call becomes idiomatic in the driver and can be factored out to a helper later on (and if needed). Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index dcb281149a9d..b9fe2d7eeef8 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -741,7 +741,6 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) const struct i2c_client *client = data->client; const struct ak_def *def = data->def; __le16 rval; - u16 buff; int ret; pm_runtime_get_sync(&data->client->dev); @@ -778,8 +777,8 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) pm_runtime_put_autosuspend(&data->client->dev); /* Swap bytes and convert to valid range. */ - buff = le16_to_cpu(rval); - *val = clamp_t(s16, buff, -def->range, def->range); + *val = clamp_t(s16, le16_to_cpu(rval), -def->range, def->range); + return IIO_VAL_INT; exit: From 6982743df9e70280caf337671db22c9b4afd4b45 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 May 2026 13:46:06 +0200 Subject: [PATCH 2303/5214] iio: magnetometer: ak8975: drop duplicate NULL check The gpiod_set_consumer_name() is NULL-aware, no need to perform the same check in the caller. Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b9fe2d7eeef8..00ac19325da6 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -919,8 +919,7 @@ static int ak8975_probe(struct i2c_client *client) eoc_gpiod = devm_gpiod_get_optional(&client->dev, NULL, GPIOD_IN); if (IS_ERR(eoc_gpiod)) return PTR_ERR(eoc_gpiod); - if (eoc_gpiod) - gpiod_set_consumer_name(eoc_gpiod, "ak_8975"); + gpiod_set_consumer_name(eoc_gpiod, "ak_8975"); /* * According to AK09911 datasheet, if reset GPIO is provided then From 77e75249448a9b40b5a1a21756fd5a4c9573aa5b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 May 2026 13:46:07 +0200 Subject: [PATCH 2304/5214] iio: magnetometer: ak8975: remove duplicate error message The devm_request_irq() already prints an error message. Remove the duplicate. Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 00ac19325da6..31e9c7c52c18 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -583,17 +583,14 @@ static int ak8975_setup_irq(struct ak8975_data *data) rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler, IRQF_TRIGGER_RISING, dev_name(&client->dev), data); - if (rc < 0) { - dev_err(&client->dev, "irq %d request failed: %d\n", irq, rc); + if (rc < 0) return rc; - } data->eoc_irq = irq; return rc; } - /* * Perform some start-of-day setup, including reading the asa calibration * values and caching them. From dbb1f2fa2bba8df30927419d8cb7c56016452a5c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 May 2026 13:46:08 +0200 Subject: [PATCH 2305/5214] iio: magnetometer: ak8975: reduce usage of magic lengths of the buffer Reduce usage of magic lengths of the supplied buffer by replacing them with the corresponding sizeof():s. Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 31e9c7c52c18..d51028b447f6 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -489,8 +489,10 @@ static int ak8975_who_i_am(struct i2c_client *client, * AK8975 | DEVICE_ID | NA * AK8963 | DEVICE_ID | NA */ - ret = i2c_smbus_read_i2c_block_data_or_emulated( - client, AK09912_REG_WIA1, 2, wia_val); + ret = i2c_smbus_read_i2c_block_data_or_emulated(client, + AK09912_REG_WIA1, + sizeof(wia_val), + wia_val); if (ret < 0) { dev_err(&client->dev, "Error reading WIA\n"); return ret; @@ -609,9 +611,10 @@ static int ak8975_setup(struct i2c_client *client) } /* Get asa data and store in the device data. */ - ret = i2c_smbus_read_i2c_block_data_or_emulated( - client, data->def->ctrl_regs[ASA_BASE], - 3, data->asa); + ret = i2c_smbus_read_i2c_block_data_or_emulated(client, + data->def->ctrl_regs[ASA_BASE], + sizeof(data->asa), + data->asa); if (ret < 0) { dev_err(&client->dev, "Not able to read asa data\n"); return ret; @@ -866,7 +869,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) */ ret = i2c_smbus_read_i2c_block_data_or_emulated(client, def->data_regs[0], - 3 * sizeof(fval[0]), + sizeof(fval), (u8 *)fval); if (ret < 0) goto unlock; From a474d5cbdf306e65dc3a219c480a55b5a07893ed Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 May 2026 13:46:09 +0200 Subject: [PATCH 2306/5214] iio: magnetometer: ak8975: unify return code variable name In one case 'rc' is used in the other 'err', the most use 'ret'. Make the latter use the former, id est 'ret'. While at it, drop unneeded ' < 0' checks. Signed-off-by: Andy Shevchenko Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d51028b447f6..6be72e1cc0a8 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -572,8 +572,8 @@ static irqreturn_t ak8975_irq_handler(int irq, void *data) static int ak8975_setup_irq(struct ak8975_data *data) { struct i2c_client *client = data->client; - int rc; int irq; + int ret; init_waitqueue_head(&data->data_ready_queue); clear_bit(0, &data->flags); @@ -582,15 +582,15 @@ static int ak8975_setup_irq(struct ak8975_data *data) else irq = gpiod_to_irq(data->eoc_gpiod); - rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler, - IRQF_TRIGGER_RISING, - dev_name(&client->dev), data); - if (rc < 0) - return rc; + ret = devm_request_irq(&client->dev, irq, ak8975_irq_handler, + IRQF_TRIGGER_RISING, + dev_name(&client->dev), data); + if (ret) + return ret; data->eoc_irq = irq; - return rc; + return 0; } /* @@ -908,8 +908,8 @@ static int ak8975_probe(struct i2c_client *client) struct iio_dev *indio_dev; struct gpio_desc *eoc_gpiod; struct gpio_desc *reset_gpiod; - int err; const char *name = NULL; + int ret; /* * Grab and set up the supplied GPIO. @@ -944,9 +944,9 @@ static int ak8975_probe(struct i2c_client *client) data->reset_gpiod = reset_gpiod; data->eoc_irq = 0; - err = iio_read_mount_matrix(&client->dev, &data->orientation); - if (err) - return err; + ret = iio_read_mount_matrix(&client->dev, &data->orientation); + if (ret) + return ret; /* id will be NULL when enumerated via ACPI */ data->def = i2c_get_match_data(client); @@ -967,20 +967,20 @@ static int ak8975_probe(struct i2c_client *client) if (IS_ERR(data->vid)) return PTR_ERR(data->vid); - err = ak8975_power_on(data); - if (err) - return err; + ret = ak8975_power_on(data); + if (ret) + return ret; - err = ak8975_who_i_am(client, data->def->type); - if (err < 0) { + ret = ak8975_who_i_am(client, data->def->type); + if (ret) { dev_err(&client->dev, "Unexpected device\n"); goto power_off; } dev_dbg(&client->dev, "Asahi compass chip %s\n", name); /* Perform some basic start-of-day setup of the device. */ - err = ak8975_setup(client); - if (err < 0) { + ret = ak8975_setup(client); + if (ret) { dev_err(&client->dev, "%s initialization fails\n", name); goto power_off; } @@ -993,15 +993,15 @@ static int ak8975_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = name; - err = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger, + ret = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger, NULL); - if (err) { + if (ret) { dev_err(&client->dev, "triggered buffer setup failed\n"); goto power_off; } - err = iio_device_register(indio_dev); - if (err) { + ret = iio_device_register(indio_dev); + if (ret) { dev_err(&client->dev, "device register failed\n"); goto cleanup_buffer; } @@ -1024,7 +1024,7 @@ static int ak8975_probe(struct i2c_client *client) iio_triggered_buffer_cleanup(indio_dev); power_off: ak8975_power_off(data); - return err; + return ret; } static void ak8975_remove(struct i2c_client *client) From a3c9f6785954f0919cb9d9ed7ef2fe76972eaa68 Mon Sep 17 00:00:00 2001 From: Felipe Ribeiro de Souza Date: Tue, 5 May 2026 23:24:28 -0300 Subject: [PATCH 2307/5214] iio: adc: ingenic-adc: rename ingenic_adc_enable_unlocked() function Rename ingenic_adc_enable_unlocked() to __ingenic_adc_enable() to better reflect that this helper must be called with the lock held. Signed-off-by: Felipe Ribeiro de Souza Co-developed-by: Lucas Ivars Cadima Ciziks Signed-off-by: Lucas Ivars Cadima Ciziks Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ingenic-adc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index 1e802c8779a4..231ff8d584c2 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -181,9 +181,8 @@ static void ingenic_adc_set_config(struct ingenic_adc *adc, mutex_unlock(&adc->lock); } -static void ingenic_adc_enable_unlocked(struct ingenic_adc *adc, - int engine, - bool enabled) +static void __ingenic_adc_enable(struct ingenic_adc *adc, int engine, + bool enabled) { u8 val; @@ -202,7 +201,7 @@ static void ingenic_adc_enable(struct ingenic_adc *adc, bool enabled) { mutex_lock(&adc->lock); - ingenic_adc_enable_unlocked(adc, engine, enabled); + __ingenic_adc_enable(adc, engine, enabled); mutex_unlock(&adc->lock); } @@ -222,11 +221,11 @@ static int ingenic_adc_capture(struct ingenic_adc *adc, cfg = readl(adc->base + JZ_ADC_REG_CFG); writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); - ingenic_adc_enable_unlocked(adc, engine, true); + __ingenic_adc_enable(adc, engine, true); ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, !(val & BIT(engine)), 250, 1000); if (ret) - ingenic_adc_enable_unlocked(adc, engine, false); + __ingenic_adc_enable(adc, engine, false); writel(cfg, adc->base + JZ_ADC_REG_CFG); mutex_unlock(&adc->lock); From 7ea43bebc7966cc7c5c36bfaa9a3a1a404b32908 Mon Sep 17 00:00:00 2001 From: Felipe Ribeiro de Souza Date: Tue, 5 May 2026 23:24:29 -0300 Subject: [PATCH 2308/5214] iio: adc: ingenic-adc: refactor ingenic_adc_read_chan_info_raw() Extract the sample logic from ingenic_adc_read_chan_info_raw() into a new helper function __ingenic_adc_read_chan() to improve code readability and modularity. The helper handles the mutex-protected section for sampling channels, while the main function manages mutex and clock enabling/disabling. Signed-off-by: Felipe Ribeiro de Souza Co-developed-by: Lucas Ivars Cadima Ciziks Signed-off-by: Lucas Ivars Cadima Ciziks Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ingenic-adc.c | 40 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index 231ff8d584c2..91e3ea661594 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -627,22 +627,12 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev, } } -static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int *val) +static int __ingenic_adc_read_chan(struct ingenic_adc *adc, + struct iio_chan_spec const *chan, + int *val) { int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); - struct ingenic_adc *adc = iio_priv(iio_dev); - ret = clk_enable(adc->clk); - if (ret) { - dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", - ret); - return ret; - } - - /* We cannot sample the aux channels in parallel. */ - mutex_lock(&adc->aux_lock); if (adc->soc_data->has_aux_md && engine == 0) { switch (chan->channel) { case INGENIC_ADC_AUX0: @@ -661,7 +651,7 @@ static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, ret = ingenic_adc_capture(adc, engine); if (ret) - goto out; + return ret; switch (chan->channel) { case INGENIC_ADC_AUX0: @@ -674,9 +664,27 @@ static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, break; } - ret = IIO_VAL_INT; -out: + return IIO_VAL_INT; +} + +static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", ret); + return ret; + } + + /* We cannot sample the aux channels in parallel. */ + mutex_lock(&adc->aux_lock); + ret = __ingenic_adc_read_chan(adc, chan, val); mutex_unlock(&adc->aux_lock); + clk_disable(adc->clk); return ret; From a42fea859692c424fce81b830fbd4ff258d390a6 Mon Sep 17 00:00:00 2001 From: Felipe Ribeiro de Souza Date: Tue, 5 May 2026 23:24:30 -0300 Subject: [PATCH 2309/5214] iio: adc: ingenic-adc: use guard()() and scoped_guard() to handle synchronisation Replace mutex_lock() and mutex_unlock() calls with guard()() in functions ingenic_adc_set_adcmd(), ingenic_adc_set_config(), ingenic_adc_enable(), ingenic_adc_capture(), and with scoped_guard() in function ingenic_adc_read_chan_info_raw(). This removes the need to call the unlock function, as the lock is automatically released when the function return or the scope exits for any other case. Signed-off-by: Felipe Ribeiro de Souza Co-developed-by: Lucas Ivars Cadima Ciziks Signed-off-by: Lucas Ivars Cadima Ciziks Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ingenic-adc.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index 91e3ea661594..414f69acab7b 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -7,6 +7,8 @@ */ #include + +#include #include #include #include @@ -115,7 +117,7 @@ static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask) { struct ingenic_adc *adc = iio_priv(iio_dev); - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); /* Init ADCMD */ readl(adc->base + JZ_ADC_REG_ADCMD); @@ -162,8 +164,6 @@ static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask) /* We're done */ writel(0, adc->base + JZ_ADC_REG_ADCMD); - - mutex_unlock(&adc->lock); } static void ingenic_adc_set_config(struct ingenic_adc *adc, @@ -172,13 +172,11 @@ static void ingenic_adc_set_config(struct ingenic_adc *adc, { uint32_t cfg; - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; cfg |= val; writel(cfg, adc->base + JZ_ADC_REG_CFG); - - mutex_unlock(&adc->lock); } static void __ingenic_adc_enable(struct ingenic_adc *adc, int engine, @@ -200,9 +198,8 @@ static void ingenic_adc_enable(struct ingenic_adc *adc, int engine, bool enabled) { - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); __ingenic_adc_enable(adc, engine, enabled); - mutex_unlock(&adc->lock); } static int ingenic_adc_capture(struct ingenic_adc *adc, @@ -217,7 +214,7 @@ static int ingenic_adc_capture(struct ingenic_adc *adc, * probably due to the switch of VREF. We must keep the lock here to * avoid races with the buffer enable/disable functions. */ - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); cfg = readl(adc->base + JZ_ADC_REG_CFG); writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); @@ -228,7 +225,6 @@ static int ingenic_adc_capture(struct ingenic_adc *adc, __ingenic_adc_enable(adc, engine, false); writel(cfg, adc->base + JZ_ADC_REG_CFG); - mutex_unlock(&adc->lock); return ret; } @@ -681,9 +677,8 @@ static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, } /* We cannot sample the aux channels in parallel. */ - mutex_lock(&adc->aux_lock); - ret = __ingenic_adc_read_chan(adc, chan, val); - mutex_unlock(&adc->aux_lock); + scoped_guard(mutex, &adc->aux_lock) + ret = __ingenic_adc_read_chan(adc, chan, val); clk_disable(adc->clk); From 4452b868d669fbf6d5333cd03ab2bd8fbadb91a3 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 7 May 2026 12:24:04 -0500 Subject: [PATCH 2310/5214] MAINTAINERS: Add myself as SCD30 maintainer The current maintainer's email is no longer valid and the driver is being orphaned. Replace his entry with mine, as I am volunteering to take over. Link: https://lore.kernel.org/linux-iio/20260507170950.37a46820@jic23-huawei/T/#t Signed-off-by: Maxwell Doose Reviewed-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c316e3b44e19..80b1577cf9d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24193,7 +24193,7 @@ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml F: drivers/iio/chemical/sunrise_co2.c SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER -M: Tomasz Duszynski +M: Maxwell Doose S: Maintained F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml F: drivers/iio/chemical/scd30.h From 10ecf78420e647f1d5d52c4d5f852885624cf928 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 08:08:33 +0200 Subject: [PATCH 2311/5214] iio: magnetometer: yamaha-yas530: Get rid of i2c_client_get_device_id() Instead of relying on the name from ID table, which might be ambiguous in some cases, use explicit product label in the driver data. With that being done, get rid of i2c_client_get_device_id() call. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 140c422773f6..5acd23c6b97c 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -168,6 +168,7 @@ struct yas5xx; /** * struct yas5xx_chip_info - device-specific data and function pointers * @devid: device ID number + * @product_label: product label used in Linux * @product_name: product name of the YAS variant * @version_names: version letters or namings * @volatile_reg: device-specific volatile registers @@ -189,6 +190,7 @@ struct yas5xx; */ struct yas5xx_chip_info { unsigned int devid; + const char *product_label; const char *product_name; const char *version_names[2]; const int *volatile_reg; @@ -1323,6 +1325,7 @@ static int yas537_power_on(struct yas5xx *yas5xx) static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { [yas530] = { .devid = YAS530_DEVICE_ID, + .product_label = "yas530", .product_name = "YAS530 MS-3E", .version_names = { "A", "B" }, .volatile_reg = yas530_volatile_reg, @@ -1338,6 +1341,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { }, [yas532] = { .devid = YAS532_DEVICE_ID, + .product_label = "yas532", .product_name = "YAS532 MS-3R", .version_names = { "AB", "AC" }, .volatile_reg = yas530_volatile_reg, @@ -1353,6 +1357,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { }, [yas533] = { .devid = YAS532_DEVICE_ID, + .product_label = "yas533", .product_name = "YAS533 MS-3F", .version_names = { "AB", "AC" }, .volatile_reg = yas530_volatile_reg, @@ -1368,6 +1373,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { }, [yas537] = { .devid = YAS537_DEVICE_ID, + .product_label = "yas537", .product_name = "YAS537 MS-3T", .version_names = { "v0", "v1" }, /* version naming unknown */ .volatile_reg = yas537_volatile_reg, @@ -1385,7 +1391,6 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { static int yas5xx_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct iio_dev *indio_dev; struct device *dev = &i2c->dev; struct yas5xx *yas5xx; @@ -1443,7 +1448,7 @@ static int yas5xx_probe(struct i2c_client *i2c) if (id_check != ci->devid) { ret = dev_err_probe(dev, -ENODEV, "device ID %02x doesn't match %s\n", - id_check, id->name); + id_check, ci->product_label); goto assert_reset; } @@ -1469,7 +1474,7 @@ static int yas5xx_probe(struct i2c_client *i2c) indio_dev->info = &yas5xx_info; indio_dev->available_scan_masks = yas5xx_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = id->name; + indio_dev->name = ci->product_label; indio_dev->channels = yas5xx_channels; indio_dev->num_channels = ARRAY_SIZE(yas5xx_channels); From 6a0814f6f97de97fd109f5749a7dcd90fbdd574c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 08:08:34 +0200 Subject: [PATCH 2312/5214] iio: magnetometer: yamaha-yas530: Use devm_mutex_init() for mutex initialization Use devm_mutex_init() since it brings some benefits when CONFIG_DEBUG_MUTEXES is enabled. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 5acd23c6b97c..b3ee4351a0bf 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1405,7 +1405,10 @@ static int yas5xx_probe(struct i2c_client *i2c) yas5xx = iio_priv(indio_dev); i2c_set_clientdata(i2c, indio_dev); yas5xx->dev = dev; - mutex_init(&yas5xx->lock); + + ret = devm_mutex_init(dev, &yas5xx->lock); + if (ret) + return ret; ret = iio_read_mount_matrix(dev, &yas5xx->orientation); if (ret) From 49d7edc9bd8a2e9a50c918008338273a7c2fc071 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 May 2026 08:08:35 +0200 Subject: [PATCH 2313/5214] iio: magnetometer: yamaha-yas530: replace usleep_range() with fsleep() Replace usleep_range() with fsleep() to allow the kernel to select the most appropriate delay mechanism based on duration. Using USEC_PER_MSEC makes the unit conversion explicit. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index b3ee4351a0bf..6bd0535e5eb1 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1317,7 +1317,7 @@ static int yas537_power_on(struct yas5xx *yas5xx) return ret; /* Wait until the coil has ramped up */ - usleep_range(YAS537_MAG_RCOIL_TIME_US, YAS537_MAG_RCOIL_TIME_US + 100); + fsleep(YAS537_MAG_RCOIL_TIME_US); return 0; } @@ -1426,7 +1426,7 @@ static int yas5xx_probe(struct i2c_client *i2c) return dev_err_probe(dev, ret, "cannot enable regulators\n"); /* See comment in runtime resume callback */ - usleep_range(31000, 40000); + fsleep(31 * USEC_PER_MSEC); /* This will take the device out of reset if need be */ yas5xx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); @@ -1565,7 +1565,7 @@ static int yas5xx_runtime_resume(struct device *dev) * for all voltages to settle. The YAS532 is 10ms then 4ms for the * I2C to come online. Let's keep it safe and put this at 31ms. */ - usleep_range(31000, 40000); + fsleep(31 * USEC_PER_MSEC); gpiod_set_value_cansleep(yas5xx->reset, 0); ret = ci->power_on(yas5xx); From ae008f65398c2071b0b5510998d86f51d285a77d Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Fri, 8 May 2026 17:39:17 +0400 Subject: [PATCH 2314/5214] iio: chemical: scd30: make command lookup table const scd30_i2c_cmd_lookup_tbl contains fixed opcodes and is only read by scd30_i2c_command(). Make it const to document that it's immutable and allow it to be placed in read-only memory. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Stepan Ionichev Acked-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index 436df9c61a71..bf465cc71be7 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -20,7 +20,7 @@ #define SCD30_I2C_MAX_BUF_SIZE 18 #define SCD30_I2C_CRC8_POLYNOMIAL 0x31 -static u16 scd30_i2c_cmd_lookup_tbl[] = { +static const u16 scd30_i2c_cmd_lookup_tbl[] = { [CMD_START_MEAS] = 0x0010, [CMD_STOP_MEAS] = 0x0104, [CMD_MEAS_INTERVAL] = 0x4600, From 8e9c394520844fb11404b4f9f16749f90e1d8627 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 7 May 2026 20:28:00 +0500 Subject: [PATCH 2315/5214] iio: chemical: scd30: reject (response=NULL, size>0) in scd30_i2c_command() scd30_i2c_command() takes an opaque "response" buffer plus its size. At the start of the function the code already checks if response is NULL (via the rsp local), but the response-decoding loop after the i2c transfer always dereferences rsp without re-checking. With the current callers in scd30_core.c this is harmless, since write commands pass response=NULL together with size=0 (so the loop body is never entered). The (response=NULL, size>0) combination has no useful meaning: there is nowhere to put the bytes that come back from the chip. Treat it as an invalid argument and bail out at the top of the function with -EINVAL, instead of silently doing the i2c transfer and dereferencing a NULL pointer in the decode loop. smatch flagged the inconsistency: drivers/iio/chemical/scd30_i2c.c:104 scd30_i2c_command() error: we previously assumed rsp could be null (see line 77) No functional change for the existing callers, which only ever use (response=NULL, size=0) for writes and (response!=NULL, size>0) for reads. Signed-off-by: Stepan Ionichev Acked-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_i2c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c index bf465cc71be7..9e841f565149 100644 --- a/drivers/iio/chemical/scd30_i2c.c +++ b/drivers/iio/chemical/scd30_i2c.c @@ -71,6 +71,9 @@ static int scd30_i2c_command(struct scd30_state *state, enum scd30_cmd cmd, u16 int i, ret; char crc; + if (!response && size != 0) + return -EINVAL; + put_unaligned_be16(scd30_i2c_cmd_lookup_tbl[cmd], buf); i = 2; From a67d263922b7a4a8c30863ee4d8d20a482117f37 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 10:30:43 +0500 Subject: [PATCH 2316/5214] iio: adc: ad7793: replace usleep_range() with fsleep() The AD7792/AD7793 datasheet (Rev. B, page 25, RESET section) says: "When a reset is initiated, the user must allow a period of 500 us before accessing any of the on-chip registers." Use fsleep(500) instead of usleep_range(500, 2000). The 500 us minimum stays the same; fsleep() picks the upper slack itself (about +25% on a default config -- narrower than the original 2000 us). Add a code comment with the datasheet reference so the "why" of the wait is visible at the call site. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 624530cce938..ea1c192e2142 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -268,7 +268,12 @@ static int ad7793_setup(struct iio_dev *indio_dev, ret = ad_sd_reset(&st->sd); if (ret < 0) goto out; - usleep_range(500, 2000); /* Wait for at least 500us */ + + /* + * Per AD7792/AD7793 datasheet (Rev. B, page 25, RESET section), + * allow 500 us after a reset before accessing on-chip registers. + */ + fsleep(500); /* write/read test for device presence */ ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); From af74304c9fe93c32e9ab2d0fc7c2717ba99a45a1 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 10:44:41 +0500 Subject: [PATCH 2317/5214] iio: frequency: adrf6780: replace usleep_range() with fsleep() The ADRF6780 datasheet (Rev. D, page 23, ADC section) says: "Wait approximately 200 us for the ADC to be ready." fsleep(200) expands to the same usleep_range(200, 250). Use the flexible sleep helper, which picks the right primitive for the given microsecond delay. Replace the generic "Recommended delay for the ADC to be ready" comment with the datasheet reference so the "why" of the wait is visible at the call site. No functional change. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adrf6780.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index 257fd31a0b0e..9911b5273b22 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -188,8 +188,11 @@ static int adrf6780_read_adc_raw(struct adrf6780_state *st, unsigned int *read_v if (ret) goto exit; - /* Recommended delay for the ADC to be ready*/ - usleep_range(200, 250); + /* + * Per ADRF6780 datasheet (Rev. D, page 23, ADC section), + * wait approximately 200 us for the ADC to be ready. + */ + fsleep(200); ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val); if (ret) From d0f23d8a9091f43329e79bcd29bcac6bf81205e5 Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Sun, 10 May 2026 22:34:35 +0300 Subject: [PATCH 2318/5214] iio: adc: ti-ads1298: Fix incorrect timeout comment At the lowest supported data rate of 250Hz, one conversion period is 4ms, not 40ms. The 50ms timeout is deliberately conservative to allow for kernel scheduling latency, which can be significant under load or on slow machines. Fix the comment to state the correct conversion time, use "lowest sample rate" for clarity, and explain that the extra margin exists to absorb scheduling latency so that no one is tempted to shrink the timeout to match the conversion period. Also drop the redundant ret variable assignment by using the return value of wait_for_completion_timeout() directly in the if() condition. Signed-off-by: Md Shofiqul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1298.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index ae30b47e4514..25261163d3e2 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -210,9 +210,11 @@ static int ads1298_read_one(struct ads1298_private *priv, int chan_index) return ret; } - /* Cannot take longer than 40ms (250Hz) */ - ret = wait_for_completion_timeout(&priv->completion, msecs_to_jiffies(50)); - if (!ret) + /* + * One conversion takes at most 4ms at the lowest sample rate (250Hz). + * Use 50ms to allow for kernel scheduling latency. + */ + if (!wait_for_completion_timeout(&priv->completion, msecs_to_jiffies(50))) return -ETIMEDOUT; return 0; From 5d7b3df5c1be8f46c2d3eabd1c1fd7b27294cbd3 Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Sat, 9 May 2026 18:19:57 +0300 Subject: [PATCH 2319/5214] iio: adc: ti-ads1298: Add parentheses around macro parameter ADS1298_REG_CHnSET() is missing parentheses around the parameter 'n'. Add them to follow kernel macro coding style and prevent potential operator precedence issues if the argument is an expression. Signed-off-by: Md Shofiqul Islam Reviewed-by: Stepan Ionichev Acked-by: Mike Looijmans Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1298.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 25261163d3e2..69911b0cde84 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -66,7 +66,7 @@ #define ADS1298_MASK_CONFIG3_VREF_4V BIT(5) #define ADS1298_REG_LOFF 0x04 -#define ADS1298_REG_CHnSET(n) (0x05 + n) +#define ADS1298_REG_CHnSET(n) (0x05 + (n)) #define ADS1298_MASK_CH_PD BIT(7) #define ADS1298_MASK_CH_PGA GENMASK(6, 4) #define ADS1298_MASK_CH_MUX GENMASK(2, 0) From 01437ab5111f57005be54005f5b575dc06cb682e Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Sat, 9 May 2026 18:19:59 +0300 Subject: [PATCH 2320/5214] iio: adc: ti-ads1298: Remove unnecessary CONFIG2 write during init The driver was enabling the internal test signal (INT_TEST), double amplitude (TEST_AMP), and fast frequency (TEST_FREQ_FAST) bits in CONFIG2 during initialization. These bits activate an internal square wave generator intended for device testing and calibration, not normal ECG operation. CONFIG2 defaults to having only the RESERVED bit set after reset, which is the correct value for normal operation. Remove the write entirely since it would just be writing the reset default value. Suggested-by: Mike Looijmans Signed-off-by: Md Shofiqul Islam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1298.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 69911b0cde84..579200e06cbd 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -617,15 +617,6 @@ static int ads1298_init(struct iio_dev *indio_dev) if (!indio_dev->name) return -ENOMEM; - /* Enable internal test signal, double amplitude, double frequency */ - ret = regmap_write(priv->regmap, ADS1298_REG_CONFIG2, - ADS1298_MASK_CONFIG2_RESERVED | - ADS1298_MASK_CONFIG2_INT_TEST | - ADS1298_MASK_CONFIG2_TEST_AMP | - ADS1298_MASK_CONFIG2_TEST_FREQ_FAST); - if (ret) - return ret; - val = ADS1298_MASK_CONFIG3_RESERVED; /* Must write 1 always */ if (!priv->reg_vref) { /* Enable internal reference */ From 7e6a73eda92ac7aeda7587c65a1acd32b31a5c3b Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 07:55:44 +0500 Subject: [PATCH 2321/5214] iio: adc: ad7192: replace usleep_range() with fsleep() The AD7192 datasheet (Rev. A, page 34, RESET section) says: "When a reset is initiated, the user must allow a period of 500 us before accessing any of the on-chip registers." Use fsleep(500) instead of usleep_range(500, 1000). Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 8b1664f6b102..ba27614c89b4 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -576,7 +576,12 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; - usleep_range(500, 1000); /* Wait for at least 500us */ + + /* + * Per AD7192 datasheet (Rev. A, page 34, RESET section), allow + * 500 us after a reset before accessing on-chip registers. + */ + fsleep(500); /* write/read test for device presence */ ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id); From 01b7517513e884ca2fa42852427ccb1ff6b849d7 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 10 May 2026 16:38:52 +0500 Subject: [PATCH 2322/5214] iio: accel: adxl355: replace usleep_range() with fsleep() The "at least 5ms" wait after software reset has no specific upper bound. Use fsleep() with 5 * USEC_PER_MSEC to make the unit explicit at the call site. No functional change. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index 03e5744d9667..89ac62ff4d04 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -349,7 +349,7 @@ static int adxl355_setup(struct adxl355_data *data) return ret; /* Wait at least 5ms after software reset */ - usleep_range(5000, 10000); + fsleep(5 * USEC_PER_MSEC); /* Read shadow registers for comparison */ ret = regmap_bulk_read(data->regmap, From fcec89dfec1cb8fcd5ee7fb2b7d458633c4a8457 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Soemardi Date: Sun, 10 May 2026 07:01:41 +0000 Subject: [PATCH 2323/5214] iio: adc: qcom-pm8xxx-xoadc: remove redundant error logs when reading values Drop dev_err() logging for -EINVAL and -ETIMEDOUT cases and rely on return values to report errors, reducing unnecessary log noise. Reviewed-by: Dmitry Baryshkov Reviewed-by: Andy Shevchenko Signed-off-by: Antony Kurniawan Soemardi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 31f88cf7f7f1..282a67b46a5e 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -535,10 +535,7 @@ static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc, goto unlock; /* Next the interrupt occurs */ - ret = wait_for_completion_timeout(&adc->complete, - VADC_CONV_TIME_MAX_US); - if (!ret) { - dev_err(adc->dev, "conversion timed out\n"); + if (!wait_for_completion_timeout(&adc->complete, VADC_CONV_TIME_MAX_US)) { ret = -ETIMEDOUT; goto unlock; } @@ -657,11 +654,8 @@ static int pm8xxx_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_PROCESSED: ch = pm8xxx_get_channel(adc, chan->address); - if (!ch) { - dev_err(adc->dev, "no such channel %lu\n", - chan->address); + if (!ch) return -EINVAL; - } ret = pm8xxx_read_channel(adc, ch, &adc_code); if (ret) return ret; @@ -677,11 +671,8 @@ static int pm8xxx_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: ch = pm8xxx_get_channel(adc, chan->address); - if (!ch) { - dev_err(adc->dev, "no such channel %lu\n", - chan->address); + if (!ch) return -EINVAL; - } ret = pm8xxx_read_channel(adc, ch, &adc_code); if (ret) return ret; From 70a1012793056852df6eda5a89cbf0476e0590e9 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Soemardi Date: Sun, 10 May 2026 07:01:45 +0000 Subject: [PATCH 2324/5214] iio: adc: qcom-pm8xxx-xoadc: add support for reading channel labels Implement the .read_label callback to allow userspace to identify ADC channels via the "label" property in the device tree. The name field in pm8xxx_chan_info is renamed to label to better reflect its purpose. If no label is provided in the device tree, it defaults to the hardware datasheet name. The change has been tested on Sony Xperia SP (PM8921). Reviewed-by: Dmitry Baryshkov Signed-off-by: Antony Kurniawan Soemardi Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 282a67b46a5e..4a1a0cfb4699 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -369,7 +369,7 @@ static const struct xoadc_channel pm8921_xoadc_channels[] = { /** * struct pm8xxx_chan_info - ADC channel information - * @name: name of this channel + * @label: label of this channel from device tree (defaults to datasheet name if not specified) * @hwchan: pointer to hardware channel information (muxing & scaling settings) * @calibration: whether to use absolute or ratiometric calibration * @decimation: 0,1,2,3 @@ -377,7 +377,7 @@ static const struct xoadc_channel pm8921_xoadc_channels[] = { * calibration: 0, 1, 2, 4, 5. */ struct pm8xxx_chan_info { - const char *name; + const char *label; const struct xoadc_channel *hwchan; enum vadc_calibration calibration; u8 decimation:2; @@ -446,7 +446,7 @@ static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc, u8 lsb, msb; dev_dbg(adc->dev, "read channel \"%s\", amux %d, prescale/mux: %d, rsv %d\n", - ch->name, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv); + ch->label, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv); mutex_lock(&adc->lock); @@ -716,8 +716,21 @@ static int pm8xxx_fwnode_xlate(struct iio_dev *indio_dev, return -EINVAL; } +static int pm8xxx_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + const struct pm8xxx_chan_info *ch; + + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) + return -EINVAL; + return sysfs_emit(label, "%s\n", ch->label); +} + static const struct iio_info pm8xxx_xoadc_info = { .fwnode_xlate = pm8xxx_fwnode_xlate, + .read_label = pm8xxx_read_label, .read_raw = pm8xxx_read_raw, }; @@ -761,7 +774,8 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, pre_scale_mux, amux_channel); return -EINVAL; } - ch->name = name; + ch->label = hwchan->datasheet_name; + fwnode_property_read_string(fwnode, "label", &ch->label); ch->hwchan = hwchan; /* Everyone seems to use absolute calibration except in special cases */ ch->calibration = VADC_CALIB_ABSOLUTE; @@ -803,7 +817,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, dev_dbg(dev, "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" ref voltage: %d, decimation %d prescale %d/%d, scale function %d\n", - hwchan->pre_scale_mux, hwchan->amux_channel, ch->name, + hwchan->pre_scale_mux, hwchan->amux_channel, ch->label, ch->amux_ip_rsv, ch->decimation, hwchan->prescale.numerator, hwchan->prescale.denominator, hwchan->scale_fn_type); From 92767f9f574c985fdf9ff9c25e59624a74db58a1 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Soemardi Date: Sun, 10 May 2026 07:01:33 +0000 Subject: [PATCH 2325/5214] dt-bindings: iio: adc: qcom,pm8018-adc: add label property for ADC channels Add a new optional label property for ADC channels to help users identify each channel when reading values from the sysfs interface. Signed-off-by: Antony Kurniawan Soemardi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/qcom,pm8018-adc.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml index c978c3a3e31a..63aac8de22ad 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml @@ -78,6 +78,10 @@ patternProperties: reg: maxItems: 1 + label: + description: | + Unique name to identify which channel this is. + qcom,decimation: $ref: /schemas/types.yaml#/definitions/uint32 description: | @@ -130,36 +134,47 @@ examples: vcoin: adc-channel@0 { reg = <0x00 0x00>; + label = "vcoin"; }; vbat: adc-channel@1 { reg = <0x00 0x01>; + label = "vbat"; }; dcin: adc-channel@2 { reg = <0x00 0x02>; + label = "dcin"; }; ichg: adc-channel@3 { reg = <0x00 0x03>; + label = "ichg"; }; vph_pwr: adc-channel@4 { reg = <0x00 0x04>; + label = "vph_pwr"; }; usb_vbus: adc-channel@a { reg = <0x00 0x0a>; + label = "usb_vbus"; }; die_temp: adc-channel@b { reg = <0x00 0x0b>; + label = "die_temp"; }; ref_625mv: adc-channel@c { reg = <0x00 0x0c>; + label = "ref_625mv"; }; ref_1250mv: adc-channel@d { reg = <0x00 0x0d>; + label = "ref_1250mv"; }; ref_325mv: adc-channel@e { reg = <0x00 0x0e>; + label = "ref_325mv"; }; ref_muxoff: adc-channel@f { reg = <0x00 0x0f>; + label = "ref_muxoff"; }; }; }; From d20605e18129079876485d652992c7ab48aaecde Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Mon, 11 May 2026 23:13:26 +0530 Subject: [PATCH 2326/5214] dt-bindings: iio: adc: hx711: clean up existing binding text Rewrite the binding description and property text so it describes the existing HX711 hardware behavior directly instead of documenting old driver implementation details. Also clarify that clock-frequency controls the SCK bit-bang timing. No functional change. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/avia-hx711.yaml | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml index 9c57eb13f892..1ea60dff98d5 100644 --- a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml @@ -10,14 +10,9 @@ maintainers: - Andreas Klinger description: | - Bit-banging driver using two GPIOs: - - sck-gpio gives a clock to the sensor with 24 cycles for data retrieval - and up to 3 cycles for selection of the input channel and gain for the - next measurement - - dout-gpio is the sensor data the sensor responds to the clock - - Specifications about the driver can be found at: - http://www.aviaic.com/ENProducts.aspx + The HX711 is a 24-bit ADC with selectable gain (32/64/128) and two + differential input channels. Channel A supports gain 64 and 128; + channel B supports gain 32. properties: compatible: @@ -26,23 +21,23 @@ properties: sck-gpios: description: - Definition of the GPIO for the clock (output). In the datasheet it is - named PD_SCK + GPIO for the clock output (PD_SCK in the datasheet). maxItems: 1 dout-gpios: description: - Definition of the GPIO for the data-out sent by the sensor in - response to the clock (input). - See Documentation/devicetree/bindings/gpio/gpio.txt for information - on how to specify a consumer gpio. + GPIO for the data output from the sensor (DOUT in the datasheet). maxItems: 1 avdd-supply: description: - Definition of the regulator used as analog supply + Analog supply voltage (AVDD). clock-frequency: + description: + Controls the SCK bit-bang timing. The value is used to derive the + delay between SCK edges; keep the SCK high time below 60 us to + avoid triggering chip power-down mode. minimum: 20000 maximum: 2500000 default: 400000 From 0ce8e3b445a3fec83a023f11512f63ff6da0d460 Mon Sep 17 00:00:00 2001 From: Piyush Patle Date: Mon, 11 May 2026 23:13:30 +0530 Subject: [PATCH 2327/5214] iio: adc: hx711: move scale computation to per-device storage The gain-to-scale table is global today, so probe-time scale updates for one device overwrite the values used by any earlier device instance. Fix this by making the gain table const and storing the computed scale values per device in hx711_data. No functional change for single-sensor configurations. Signed-off-by: Piyush Patle Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 1db8b68a8f64..86d2a70dd3de 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -28,22 +28,20 @@ struct hx711_gain_to_scale { int gain; int gain_pulse; - int scale; int channel; }; /* * .scale depends on AVDD which in turn is known as soon as the regulator - * is available - * therefore we set .scale in hx711_probe() + * is available; it is stored per device in hx711_data.gain_scale[] * * channel A in documentation is channel 0 in source code * channel B in documentation is channel 1 in source code */ -static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = { - { 128, 1, 0, 0 }, - { 32, 2, 0, 1 }, - { 64, 3, 0, 0 } +static const struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = { + { 128, 1, 0 }, + { 32, 2, 1 }, + { 64, 3, 0 }, }; static int hx711_get_gain_to_pulse(int gain) @@ -56,22 +54,22 @@ static int hx711_get_gain_to_pulse(int gain) return 1; } -static int hx711_get_gain_to_scale(int gain) +static int hx711_get_gain_to_scale(const int *gain_scale, int gain) { int i; for (i = 0; i < HX711_GAIN_MAX; i++) if (hx711_gain_to_scale[i].gain == gain) - return hx711_gain_to_scale[i].scale; + return gain_scale[i]; return 0; } -static int hx711_get_scale_to_gain(int scale) +static int hx711_get_scale_to_gain(const int *gain_scale, int scale) { int i; for (i = 0; i < HX711_GAIN_MAX; i++) - if (hx711_gain_to_scale[i].scale == scale) + if (gain_scale[i] == scale) return hx711_gain_to_scale[i].gain; return -EINVAL; } @@ -82,6 +80,7 @@ struct hx711_data { struct gpio_desc *gpiod_dout; int gain_set; /* gain set on device */ int gain_chan_a; /* gain for channel A */ + int gain_scale[HX711_GAIN_MAX]; struct mutex lock; /* * triggered buffer @@ -290,7 +289,8 @@ static int hx711_read_raw(struct iio_dev *indio_dev, *val = 0; mutex_lock(&hx711_data->lock); - *val2 = hx711_get_gain_to_scale(hx711_data->gain_set); + *val2 = hx711_get_gain_to_scale(hx711_data->gain_scale, + hx711_data->gain_set); mutex_unlock(&hx711_data->lock); @@ -321,7 +321,7 @@ static int hx711_write_raw(struct iio_dev *indio_dev, mutex_lock(&hx711_data->lock); - gain = hx711_get_scale_to_gain(val2); + gain = hx711_get_scale_to_gain(hx711_data->gain_scale, val2); if (gain < 0) { mutex_unlock(&hx711_data->lock); return gain; @@ -386,6 +386,7 @@ static ssize_t hx711_scale_available_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct hx711_data *hx711_data = iio_priv(dev_to_iio_dev(dev)); struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); int channel = iio_attr->address; int i, len = 0; @@ -393,7 +394,7 @@ static ssize_t hx711_scale_available_show(struct device *dev, for (i = 0; i < HX711_GAIN_MAX; i++) if (hx711_gain_to_scale[i].channel == channel) len += sprintf(buf + len, "0.%09d ", - hx711_gain_to_scale[i].scale); + hx711_data->gain_scale[i]); len += sprintf(buf + len, "\n"); @@ -511,7 +512,7 @@ static int hx711_probe(struct platform_device *pdev) ret *= 100; for (i = 0; i < HX711_GAIN_MAX; i++) - hx711_gain_to_scale[i].scale = + hx711_data->gain_scale[i] = ret / hx711_gain_to_scale[i].gain / 1678; hx711_data->gain_set = 128; From 3f439a2a5b37e9d72a8f4eb6974adbe7c34e1a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 12 May 2026 14:50:34 +0200 Subject: [PATCH 2328/5214] iio: Drop unused driver_data in four i2c drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the four drivers the .driver_data member of i2c_device_id is write-only. Drop the explicit assignment. While touching these arrays use a named initializer to assign the .name member, which is easier to parse for a human. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/pac1921.c | 2 +- drivers/iio/light/apds9160.c | 2 +- drivers/iio/light/tsl2563.c | 8 ++++---- drivers/iio/light/tsl2583.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index bce7185953ec..68bdd2f30bad 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -1310,7 +1310,7 @@ static int pac1921_probe(struct i2c_client *client) } static const struct i2c_device_id pac1921_id[] = { - { .name = "pac1921", 0 }, + { .name = "pac1921" }, { } }; MODULE_DEVICE_TABLE(i2c, pac1921_id); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 3da0bdac04cf..8dacb1730429 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -1572,7 +1572,7 @@ static const struct of_device_id apds9160_of_match[] = { MODULE_DEVICE_TABLE(of, apds9160_of_match); static const struct i2c_device_id apds9160_id[] = { - { "apds9160", 0 }, + { .name = "apds9160" }, { } }; MODULE_DEVICE_TABLE(i2c, apds9160_id); diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index f2af1cd7c2d1..7e277bc6a8b1 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -839,10 +839,10 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume); static const struct i2c_device_id tsl2563_id[] = { - { "tsl2560", 0 }, - { "tsl2561", 1 }, - { "tsl2562", 2 }, - { "tsl2563", 3 }, + { .name = "tsl2560" }, + { .name = "tsl2561" }, + { .name = "tsl2562" }, + { .name = "tsl2563" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2563_id); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index 8801a491de77..a0dd122af2cf 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -913,9 +913,9 @@ static DEFINE_RUNTIME_DEV_PM_OPS(tsl2583_pm_ops, tsl2583_suspend, tsl2583_resume, NULL); static const struct i2c_device_id tsl2583_idtable[] = { - { "tsl2580", 0 }, - { "tsl2581", 1 }, - { "tsl2583", 2 }, + { .name = "tsl2580" }, + { .name = "tsl2581" }, + { .name = "tsl2583" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2583_idtable); From d350cb2b23aee0f9a5107e87dc80929f93a04b00 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 11 May 2026 22:46:42 +0530 Subject: [PATCH 2329/5214] MAINTAINERS: Update Analog Devices IIO drivers entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lars-Peter Clausen is currently busy and is therefore removed from the ANALOG DEVICES INC IIO DRIVERS entry, with input from Jonathan. Add Analog Devices mailing list as contact and Nuno Sá as maintainer for the ANALOG DEVICES INC IIO DRIVERS entry for coverage support. Suggested-by: Jonathan Cameron Acked-by: Nuno Sá Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 80b1577cf9d1..0139dda4e2f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1852,8 +1852,9 @@ W: https://ez.analog.com/linux-software-drivers F: drivers/dma/dma-axi-dmac.c ANALOG DEVICES INC IIO DRIVERS -M: Lars-Peter Clausen +M: Nuno Sá M: Michael Hennerich +L: linux@analog.com S: Supported W: http://wiki.analog.com/ W: https://ez.analog.com/linux-software-drivers From bdc573d5c33b90a21c3799c1b3f08dc8092188af Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Mon, 11 May 2026 22:46:43 +0530 Subject: [PATCH 2330/5214] MAINTAINERS: Update maintainer for IIO drivers The listed Analog Devices domain email for Cosmin Tanislav is no longer valid, and he is no longer maintaining these IIO drivers. Add Marcelo Schmitt as the maintainer from Analog Devices to continue support and maintenance of the affected drivers. Signed-off-by: Sanjay Chitroda Reviewed-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0139dda4e2f8..d28e54838e7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -633,7 +633,7 @@ F: drivers/iio/accel/adxl355_i2c.c F: drivers/iio/accel/adxl355_spi.c ADXL367 THREE-AXIS DIGITAL ACCELEROMETER DRIVER -M: Cosmin Tanislav +M: Marcelo Schmitt L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1452,7 +1452,7 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml F: drivers/iio/adc/ad4080.c ANALOG DEVICES INC AD4130 DRIVER -M: Cosmin Tanislav +M: Marcelo Schmitt L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1548,7 +1548,7 @@ F: Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml F: drivers/iio/dac/ad7293.c ANALOG DEVICES INC AD74115 DRIVER -M: Cosmin Tanislav +M: Marcelo Schmitt L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers @@ -1556,7 +1556,7 @@ F: Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml F: drivers/iio/addac/ad74115.c ANALOG DEVICES INC AD74413R DRIVER -M: Cosmin Tanislav +M: Marcelo Schmitt L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers From cfa30b4330677e351d6d26a0a12bfe505bc9f31d Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 11 May 2026 13:26:10 +0200 Subject: [PATCH 2331/5214] iio: magnetometer: ak8975: modernize polling loops with iopoll() macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver currently uses while loops and msleep() for polling during conversion waits. Replace the custom polling loops with readx_poll_timeout() and read_poll_timeout() macros from . This reduces boilerplate, standardizes timeout handling and improves overall code readability, keeping the original timing and error behaviour. Add for USEC_PER_MSEC macro instead of using magic numbers. Assisted-by: Gemini:3.1-Pro Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 43 +++++++++++++------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 6be72e1cc0a8..b990c123e280 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -649,16 +650,14 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data, { struct i2c_client *client = data->client; int ret; + int val; /* Wait for the conversion to complete. */ - while (timeout_ms) { - msleep(poll_ms); - if (gpiod_get_value(data->eoc_gpiod)) - break; - timeout_ms -= poll_ms; - } - if (!timeout_ms) - return -ETIMEDOUT; + ret = readx_poll_timeout(gpiod_get_value, data->eoc_gpiod, val, val != 0, + poll_ms * USEC_PER_MSEC, + timeout_ms * USEC_PER_MSEC); + if (ret) + return ret; ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]); if (ret < 0) @@ -672,27 +671,21 @@ static int wait_conversion_complete_polled(struct ak8975_data *data, unsigned int timeout_ms) { struct i2c_client *client = data->client; - u8 read_status; int ret; + int val; /* Wait for the conversion to complete. */ - while (timeout_ms) { - msleep(poll_ms); - ret = i2c_smbus_read_byte_data(client, - data->def->ctrl_regs[ST1]); - if (ret < 0) { - dev_err(&client->dev, "Error in reading ST1\n"); - return ret; - } - read_status = ret; - if (read_status) - break; - timeout_ms -= poll_ms; - } - if (!timeout_ms) - return -ETIMEDOUT; + ret = read_poll_timeout(i2c_smbus_read_byte_data, val, val != 0, + poll_ms * USEC_PER_MSEC, + timeout_ms * USEC_PER_MSEC, + true, + client, data->def->ctrl_regs[ST1]); + if (ret) + return ret; + if (val < 0) + dev_err(&client->dev, "Error in reading ST1\n"); - return read_status; + return val; } /* Returns 0 if the end of conversion interrupt occurred or -ETIMEDOUT otherwise */ From 46dd701a39f0b6b1796e75a9eb87822b8b3a98f1 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 11 May 2026 13:26:11 +0200 Subject: [PATCH 2332/5214] iio: magnetometer: ak8975: check if gpiod read was successful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a check that ensures that valid data has been read from GPIOD. If not, log an error and return the negative read value. Suggested-by: Jonathan Cameron Reviewed-by: Nuno Sá Signed-off-by: Joshua Crofts Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index b990c123e280..63b6e8465f5f 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -658,6 +658,10 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data, timeout_ms * USEC_PER_MSEC); if (ret) return ret; + if (val < 0) { + dev_err(&client->dev, "Error in reading GPIOD\n"); + return val; + } ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]); if (ret < 0) From 79ab48ae4f9e06512263377c81fad1ab1adace4c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 May 2026 13:26:13 +0200 Subject: [PATCH 2333/5214] iio: magnetometer: ak8975: consistently use 'data' parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the functions use 'client', some use 'data', and some use both. Refactor the driver to consistently use 'data' in all cases. Signed-off-by: Andy Shevchenko Reviewed-by: Nuno Sá Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 63b6e8465f5f..dbab4d0bba34 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -474,9 +474,10 @@ static void ak8975_power_off(const struct ak8975_data *data) * Return 0 if the i2c device is the one we expect. * return a negative error number otherwise */ -static int ak8975_who_i_am(struct i2c_client *client, +static int ak8975_who_i_am(const struct ak8975_data *data, enum asahi_compass_chipset type) { + struct i2c_client *client = data->client; u8 wia_val[2]; int ret; @@ -598,10 +599,9 @@ static int ak8975_setup_irq(struct ak8975_data *data) * Perform some start-of-day setup, including reading the asa calibration * values and caching them. */ -static int ak8975_setup(struct i2c_client *client) +static int ak8975_setup(struct ak8975_data *data) { - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ak8975_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; int ret; /* Write the fused rom access mode. */ @@ -706,12 +706,13 @@ static int wait_conversion_complete_interrupt(struct ak8975_data *data, return ret > 0 ? 0 : -ETIMEDOUT; } -static int ak8975_start_read_axis(struct ak8975_data *data, - const struct i2c_client *client) +static int ak8975_start_read_axis(struct ak8975_data *data) { - /* Set up the device for taking a sample. */ - int ret = ak8975_set_mode(data, MODE_ONCE); + struct i2c_client *client = data->client; + int ret; + /* Set up the device for taking a sample. */ + ret = ak8975_set_mode(data, MODE_ONCE); if (ret < 0) { dev_err(&client->dev, "Error in setting operating mode\n"); return ret; @@ -744,7 +745,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) mutex_lock(&data->lock); - ret = ak8975_start_read_axis(data, client); + ret = ak8975_start_read_axis(data); if (ret) goto exit; @@ -856,7 +857,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) mutex_lock(&data->lock); - ret = ak8975_start_read_axis(data, client); + ret = ak8975_start_read_axis(data); if (ret) goto unlock; @@ -968,7 +969,7 @@ static int ak8975_probe(struct i2c_client *client) if (ret) return ret; - ret = ak8975_who_i_am(client, data->def->type); + ret = ak8975_who_i_am(data, data->def->type); if (ret) { dev_err(&client->dev, "Unexpected device\n"); goto power_off; @@ -976,7 +977,7 @@ static int ak8975_probe(struct i2c_client *client) dev_dbg(&client->dev, "Asahi compass chip %s\n", name); /* Perform some basic start-of-day setup of the device. */ - ret = ak8975_setup(client); + ret = ak8975_setup(data); if (ret) { dev_err(&client->dev, "%s initialization fails\n", name); goto power_off; From 8bf3e7a9defcfa889976258919d99fa2e8465189 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 11 May 2026 14:06:41 +0000 Subject: [PATCH 2334/5214] staging: iio: addac: adt7316: document SPI interface switching sequence The device powers up in I2C mode. Switching to SPI mode requires sending a sequence of SPI writes as described in the datasheet. During this sequence, the device may still be in I2C mode, so SPI transactions may not be recognized and can fail. Such errors are therefore ignored. Add a comment to clarify this behavior. Datasheet: https://www.analog.com/en/products/adt7316.html Reviewed-by: Maxwell Doose Reviewed-by: Andy Shevchenko Signed-off-by: Hungyu Lin Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316-spi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c index f91325d11394..1debcc36c1af 100644 --- a/drivers/staging/iio/addac/adt7316-spi.c +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -106,7 +106,18 @@ static int adt7316_spi_probe(struct spi_device *spi_dev) return -EINVAL; } - /* switch from default I2C protocol to SPI protocol */ + /* + * The device powers up in I2C mode. Switching to SPI mode + * requires sending a sequence of SPI writes as described in + * the datasheet "ADT7316/ADT7317/ADT7318", Rev. B, + * in the "Serial Interface Selection" section. + * + * During this sequence, the device may still be in I2C mode, + * so SPI transactions may not be recognized and can fail. + * Such errors are therefore ignored. + * + * TL;DR: Do not change this! + */ adt7316_spi_write(spi_dev, 0, 0); adt7316_spi_write(spi_dev, 0, 0); adt7316_spi_write(spi_dev, 0, 0); From eaead586b3d95890832449f2887283b067426ecc Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 13 May 2026 16:35:52 +0200 Subject: [PATCH 2335/5214] iio: magnetometer: ak8975: ensure device is awake for buffered capture Currently, the ak8975_start_read_axis() can be called while the device is autosuspended, causing two issues: 1. I2C transfers in the aforementioned function will fail or timeout because ak8975_runtime_suspend() disables the device regulators. 2. Since ak8975_fill_buffer() does not hold runtime references, ak8975_runtime_suspend() can run concurrently, and since PM callbacks do not use a locking mechanism, it may cause a race accessing the control register via the I2C bus. Fix this issue by adding struct iio_buffer_setup_ops that contains preenable and postdisable functions to ensure correct that device is powered on when running a buffered capture. Fixes: bc11ca4a0b84 ("iio:magnetometer:ak8975: triggered buffer support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260511-magnetometer-fixes-post-pickup-v7-0-9d910faa28b6%40gmail.com Cc: Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index dbab4d0bba34..0fb2fd03d11c 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -899,6 +899,28 @@ static irqreturn_t ak8975_handle_trigger(int irq, void *p) return IRQ_HANDLED; } +static int ak8975_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ak8975_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int ak8975_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ak8975_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct iio_buffer_setup_ops ak8975_buffer_setup_ops = { + .preenable = ak8975_buffer_preenable, + .postdisable = ak8975_buffer_postdisable, +}; static int ak8975_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -992,7 +1014,7 @@ static int ak8975_probe(struct i2c_client *client) indio_dev->name = name; ret = iio_triggered_buffer_setup(indio_dev, NULL, ak8975_handle_trigger, - NULL); + &ak8975_buffer_setup_ops); if (ret) { dev_err(&client->dev, "triggered buffer setup failed\n"); goto power_off; From a9a00d727b7bbc5e913a919530a9dd468935bf95 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Fri, 15 May 2026 12:28:23 +0200 Subject: [PATCH 2336/5214] iio: magnetometer: ak8975: fix potential kernel stack memory leak Currently in the AK8975 driver there are four instances where potential uninitialized kernel stack memory leaks can occur. If i2c_smbus_read_i2c_block_data_or_emulated() returns a value less than the size of the buffer, uninitialized bytes are retained in the buffer and later the buffer is passed on to IIO buffers, potentially leaking memory to userspace. Fix this by adding checks whether the return value of the function is equal to the size of the buffer and subsequently if the value is lesser than zero to distinguish from a returned error code. Fixes: bc11ca4a0b84 ("iio:magnetometer:ak8975: triggered buffer support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260513-ak8975-fix-v1-1-104ea605dd54%40gmail.com Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 0fb2fd03d11c..bb74abb648f1 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -499,6 +499,10 @@ static int ak8975_who_i_am(const struct ak8975_data *data, dev_err(&client->dev, "Error reading WIA\n"); return ret; } + if (ret != sizeof(wia_val)) { + dev_err(&client->dev, "Error reading WIA\n"); + return -EIO; + } if (wia_val[0] != AK8975_DEVICE_ID) return -ENODEV; @@ -620,6 +624,10 @@ static int ak8975_setup(struct ak8975_data *data) dev_err(&client->dev, "Not able to read asa data\n"); return ret; } + if (ret != sizeof(data->asa)) { + dev_err(&client->dev, "Error reading asa data\n"); + return -EIO; + } /* After reading fuse ROM data set power-down mode */ ret = ak8975_set_mode(data, POWER_DOWN); @@ -755,6 +763,10 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) (u8 *)&rval); if (ret < 0) goto exit; + if (ret != sizeof(rval)) { + ret = -EIO; + goto exit; + } /* Read out ST2 for release lock on measurement data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); @@ -871,6 +883,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) (u8 *)fval); if (ret < 0) goto unlock; + if (ret != sizeof(fval)) + goto unlock; mutex_unlock(&data->lock); From 29bf2693c9e31616244cad88fac06a3a3046b069 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 14 May 2026 20:16:39 -0500 Subject: [PATCH 2337/5214] dt-bindings: iio: chemical: sensirion,scd30: Update maintainers field Tomasz Duszynski is no longer the maintainer of the SCD30 driver. Replace his entry with mine. Link: https://lore.kernel.org/linux-iio/20260507172404.80435-1-m32285159@gmail.com/ Signed-off-by: Maxwell Doose Acked-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/chemical/sensirion,scd30.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml index 40d87346ff4c..a5b0debe85b1 100644 --- a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Sensirion SCD30 carbon dioxide sensor maintainers: - - Tomasz Duszynski + - Maxwell Doose description: | Air quality sensor capable of measuring co2 concentration, temperature From 469ad4d50fb34dc9abc9a47f879f7c07be614ae1 Mon Sep 17 00:00:00 2001 From: Raffael Raiel Trindade Date: Thu, 14 May 2026 17:11:49 -0300 Subject: [PATCH 2338/5214] iio: light: vcnl4000: use lock guard() Use guard() and scoped_guard() for handling mutex lock instead of manually locking and unlocking. Remove gotos in error handling logic. This prevents forgotten locks on early exits. Signed-off-by: Raffael Raiel Trindade Co-developed-by: Kim Carvalho Signed-off-by: Kim Carvalho Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 152 +++++++++++------------------------ 1 file changed, 45 insertions(+), 107 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index c08927e34b4e..88fc7424ae35 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -268,46 +269,36 @@ static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) { int ret; - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); if (ret < 0) - goto out; + return ret; if (en) ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; else ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); - -out: - mutex_unlock(&data->vcnl4000_lock); - - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); } static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) { int ret; - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); if (ret < 0) - goto out; + return ret; if (en) ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; else ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); - -out: - mutex_unlock(&data->vcnl4000_lock); - - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); } static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) @@ -442,18 +433,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, int tries = 20; int ret; - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, req_mask); if (ret < 0) - goto fail; + return ret; /* wait for data to become ready */ while (tries--) { ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); if (ret < 0) - goto fail; + return ret; if (ret & rdy_mask) break; msleep(20); /* measurement takes up to 100 ms */ @@ -462,21 +453,10 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, if (tries < 0) { dev_err(&data->client->dev, "vcnl4000_measure() failed, data not ready\n"); - ret = -EIO; - goto fail; + return -EIO; } - ret = vcnl4000_read_data(data, data_reg, val); - if (ret < 0) - goto fail; - - mutex_unlock(&data->vcnl4000_lock); - - return 0; - -fail: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return vcnl4000_read_data(data, data_reg, val); } static int vcnl4200_measure(struct vcnl4000_data *data, @@ -486,16 +466,14 @@ static int vcnl4200_measure(struct vcnl4000_data *data, s64 delta; ktime_t next_measurement; - mutex_lock(&chan->lock); - - next_measurement = ktime_add(chan->last_measurement, - chan->sampling_rate); - delta = ktime_us_delta(next_measurement, ktime_get()); - if (delta > 0) - usleep_range(delta, delta + 500); - chan->last_measurement = ktime_get(); - - mutex_unlock(&chan->lock); + scoped_guard(mutex, &chan->lock) { + next_measurement = ktime_add(chan->last_measurement, + chan->sampling_rate); + delta = ktime_us_delta(next_measurement, ktime_get()); + if (delta > 0) + usleep_range(delta, delta + 500); + chan->last_measurement = ktime_get(); + } ret = i2c_smbus_read_word_data(data->client, chan->reg); if (ret < 0) @@ -606,21 +584,15 @@ static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) (*data->chip_spec->als_it_times)[0][1]), val); - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); if (ret < 0) - goto out_unlock; + return ret; regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); regval |= (ret & ~VCNL4040_ALS_CONF_IT); - ret = i2c_smbus_write_word_data(data->client, - VCNL4200_AL_CONF, - regval); - -out_unlock: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, regval); } static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) @@ -660,20 +632,15 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); if (ret < 0) - goto out; + return ret; regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, - regval); - -out: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, regval); } static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2) @@ -721,20 +688,15 @@ static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, in break; } - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); if (ret < 0) - goto out_unlock; + return ret; regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i); regval |= (ret & ~VCNL4040_ALS_CONF_PERS); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, - regval); - -out_unlock: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, regval); } static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2) @@ -783,20 +745,15 @@ static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int } } - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); if (ret < 0) - goto out_unlock; + return ret; regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i); regval |= (ret & ~VCNL4040_CONF1_PS_PERS); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, - regval); - -out_unlock: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, regval); } static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val) @@ -830,20 +787,15 @@ static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) return -EINVAL; - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); if (ret < 0) - goto out_unlock; + return ret; regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i); regval |= (ret & ~VCNL4040_PS_CONF3_MPS); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, - regval); - -out_unlock: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, regval); } static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2) @@ -878,20 +830,15 @@ static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val) if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) return -EINVAL; - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); if (ret < 0) - goto out_unlock; + return ret; regval = (ret & ~VCNL4040_PS_MS_LED_I); regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, - regval); - -out_unlock: - mutex_unlock(&data->vcnl4000_lock); - return ret; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, regval); } static int vcnl4000_read_raw(struct iio_dev *indio_dev, @@ -1476,17 +1423,17 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir, bool state) { - int ret = -EINVAL; + int ret; u16 val, mask; struct vcnl4000_data *data = iio_priv(indio_dev); - mutex_lock(&data->vcnl4000_lock); + guard(mutex)(&data->vcnl4000_lock); switch (chan->type) { case IIO_LIGHT: ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); if (ret < 0) - goto out; + return ret; mask = VCNL4040_ALS_CONF_INT_EN; if (state) @@ -1495,13 +1442,11 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev, val = (ret & ~mask); data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, - val); - break; + return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); case IIO_PROXIMITY: ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); if (ret < 0) - goto out; + return ret; if (dir == IIO_EV_DIR_RISING) mask = VCNL4040_PS_IF_AWAY; @@ -1511,17 +1456,10 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev, val = state ? (ret | mask) : (ret & ~mask); data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, - val); - break; + return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); default: - break; + return -EINVAL; } - -out: - mutex_unlock(&data->vcnl4000_lock); - - return ret; } static irqreturn_t vcnl4040_irq_thread(int irq, void *p) From b049fb4081527c8d52834c46cef38c0bb65468ed Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 13 May 2026 15:13:32 +0500 Subject: [PATCH 2339/5214] iio: adc: ad7192: fix GPOCON register access annotation The comment next to AD7192_REG_GPOCON marks the register as RO, but the AD7192 datasheet (Rev. A, page 24, GPOCON REGISTER) says: "The GPOCON register is an 8-bit register from which data can be read or to which data can be written." The driver itself uses ad_sd_write_reg() against this register in ad7192_show_scale() / write paths to control the bridge power-down switch and digital outputs, which matches the RW datasheet description. Update the comment to RW so it does not mislead future readers. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7192.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index ba27614c89b4..caf4473ad643 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -39,7 +39,7 @@ #define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */ #define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */ #define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */ -#define AD7192_REG_GPOCON 5 /* GPOCON Register (RO, 8-bit) */ +#define AD7192_REG_GPOCON 5 /* GPOCON Register (RW, 8-bit) */ #define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit */ /* (AD7792)/24-bit (AD7192)) */ #define AD7192_REG_FULLSALE 7 /* Full-Scale Register */ From 2f856ad3f729e09a1a046272c68437d058c50621 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 13:51:57 +0500 Subject: [PATCH 2340/5214] Documentation: iio: fix typo in triggered-buffers example In the "IIO triggered buffer setup" example, iio_triggered_buffer_setup() is called with "sensor_iio_polfunc" (single 'l') while the function is defined and later referenced as "sensor_iio_pollfunc" (double 'l'). Fix the misspelling so the example is consistent. Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/triggered-buffers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/iio/triggered-buffers.rst b/Documentation/driver-api/iio/triggered-buffers.rst index 417555dbbdf4..23b82357eba6 100644 --- a/Documentation/driver-api/iio/triggered-buffers.rst +++ b/Documentation/driver-api/iio/triggered-buffers.rst @@ -43,7 +43,7 @@ A typical triggered buffer setup looks like this:: } /* setup triggered buffer, usually in probe function */ - iio_triggered_buffer_setup(indio_dev, sensor_iio_polfunc, + iio_triggered_buffer_setup(indio_dev, sensor_iio_pollfunc, sensor_trigger_handler, sensor_buffer_setup_ops); From f608e9c032dac51d1e6410fddcbcddeddb261cdf Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 13 May 2026 15:07:52 +0500 Subject: [PATCH 2341/5214] Documentation: iio: make ADXL Y-axis calibbias description consistent The Y-axis calibbias rows in adxl345.rst, adxl313.rst and adxl380.rst use a different wording than the matching X-axis and Z-axis rows in the same tables: the X/Z rows say "Calibration offset for the X/Z-axis accelerometer channel." while the Y row says "Y-axis (or y-axis) acceleration offset correction". Make the Y-axis row match the other two so each driver's sysfs table has consistent capitalisation and wording. Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- Documentation/iio/adxl313.rst | 2 +- Documentation/iio/adxl345.rst | 2 +- Documentation/iio/adxl380.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst index cc0829be0447..3641193bf647 100644 --- a/Documentation/iio/adxl313.rst +++ b/Documentation/iio/adxl313.rst @@ -38,7 +38,7 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +---------------------------------------------------+----------------------------------------------------------+ | in_accel_x_raw | Raw X-axis accelerometer channel value. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_y_calibbias | y-axis acceleration offset correction | +| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. | +---------------------------------------------------+----------------------------------------------------------+ | in_accel_y_raw | Raw Y-axis accelerometer channel value. | +---------------------------------------------------+----------------------------------------------------------+ diff --git a/Documentation/iio/adxl345.rst b/Documentation/iio/adxl345.rst index 978f746a8198..0aa33a852412 100644 --- a/Documentation/iio/adxl345.rst +++ b/Documentation/iio/adxl345.rst @@ -47,7 +47,7 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +-------------------------------------------+----------------------------------------------------------+ | in_accel_x_raw | Raw X-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ -| in_accel_y_calibbias | Y-axis acceleration offset correction | +| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. | +-------------------------------------------+----------------------------------------------------------+ | in_accel_y_raw | Raw Y-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ diff --git a/Documentation/iio/adxl380.rst b/Documentation/iio/adxl380.rst index 61cafa2f98bf..654d4c0e84a1 100644 --- a/Documentation/iio/adxl380.rst +++ b/Documentation/iio/adxl380.rst @@ -51,7 +51,7 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +---------------------------------------------------+----------------------------------------------------------+ | in_accel_x_raw | Raw X-axis accelerometer channel value. | +---------------------------------------------------+----------------------------------------------------------+ -| in_accel_y_calibbias | y-axis acceleration offset correction | +| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. | +---------------------------------------------------+----------------------------------------------------------+ | in_accel_y_raw | Raw Y-axis accelerometer channel value. | +---------------------------------------------------+----------------------------------------------------------+ From ecedfa46a2e5db329925bac8cab9ac8cbad55755 Mon Sep 17 00:00:00 2001 From: Vladislav Kulikov Date: Mon, 11 May 2026 19:11:34 +0000 Subject: [PATCH 2342/5214] dt-bindings: iio: magnetometer: add MEMSIC MMC5983MA Add a Devicetree binding for the MEMSIC MMC5983MA 3-axis magnetometer. MMC5983MA is not register-compatible with the existing MEMSIC magnetometer drivers. It has a different register map, 18-bit output data format, and I2C/SPI transport support. Reviewed-by: David Lechner Acked-by: Conor Dooley Signed-off-by: Vladislav Kulikov Signed-off-by: Jonathan Cameron --- .../iio/magnetometer/memsic,mmc5983.yaml | 63 +++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml diff --git a/Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml b/Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml new file mode 100644 index 000000000000..4a4df6bb70fe --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/memsic,mmc5983.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MEMSIC MMC5983MA 3-axis magnetic sensor + +maintainers: + - Vladislav Kulikov + +properties: + compatible: + const: memsic,mmc5983 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor + + vddio-supply: + description: Regulator that provides power to the digital interface and INT pin + +required: + - compatible + - reg + - vdd-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@30 { + compatible = "memsic,mmc5983"; + reg = <0x30>; + vdd-supply = <&vdd_3v3_reg>; + vddio-supply = <&vdd_3v3_reg>; + }; + }; + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@0 { + compatible = "memsic,mmc5983"; + reg = <0>; + spi-max-frequency = <10000000>; + vdd-supply = <&vdd_3v3_reg>; + vddio-supply = <&vdd_3v3_reg>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index d28e54838e7a..a80dfbbee2ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17179,6 +17179,12 @@ F: drivers/mtd/ F: include/linux/mtd/ F: include/uapi/mtd/ +MEMSIC MMC5983 MAGNETOMETER DRIVER +M: Vladislav Kulikov +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml + MEN A21 WATCHDOG DRIVER M: Johannes Thumshirn L: linux-watchdog@vger.kernel.org From 243a738ccdabb8f997a5c6678e22f60761dbadf1 Mon Sep 17 00:00:00 2001 From: Vladislav Kulikov Date: Mon, 11 May 2026 19:11:35 +0000 Subject: [PATCH 2343/5214] iio: magnetometer: add driver for MEMSIC MMC5983MA Add support for the MEMSIC MMC5983MA 3-axis magnetometer. The driver provides raw magnetic field readings via IIO sysfs with SET/RESET offset cancellation for each measurement. Reviewed-by: David Lechner Signed-off-by: Vladislav Kulikov Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/magnetometer/Kconfig | 11 + drivers/iio/magnetometer/Makefile | 1 + drivers/iio/magnetometer/mmc5983.c | 348 +++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 drivers/iio/magnetometer/mmc5983.c diff --git a/MAINTAINERS b/MAINTAINERS index a80dfbbee2ef..640192835bd3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17184,6 +17184,7 @@ M: Vladislav Kulikov L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/magnetometer/memsic,mmc5983.yaml +F: drivers/iio/magnetometer/mmc5983.c MEN A21 WATCHDOG DRIVER M: Johannes Thumshirn diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index fb313e591e85..ea2697fb5ab6 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -151,6 +151,17 @@ config MMC5633 To compile this driver as a module, choose M here: the module will be called mmc5633 +config MMC5983 + tristate "MEMSIC MMC5983 3-axis magnetic sensor" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for the MEMSIC MMC5983 3-axis + magnetic sensor. + + To compile this driver as a module, choose M here: the module + will be called mmc5983 + config IIO_ST_MAGN_3AXIS tristate "STMicroelectronics magnetometers 3-Axis Driver" depends on (I2C || SPI_MASTER) && SYSFS diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 5bd227f8c120..7fd9b3fd914e 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MAG3110) += mag3110.o obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o obj-$(CONFIG_MMC35240) += mmc35240.o obj-$(CONFIG_MMC5633) += mmc5633.o +obj-$(CONFIG_MMC5983) += mmc5983.o obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o st_magn-y := st_magn_core.o diff --git a/drivers/iio/magnetometer/mmc5983.c b/drivers/iio/magnetometer/mmc5983.c new file mode 100644 index 000000000000..a67b13393b6b --- /dev/null +++ b/drivers/iio/magnetometer/mmc5983.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MMC5983 - MEMSIC 3-axis Magnetic Sensor + * + * Copyright (c) 2026, Vlad Kulikov + * + * IIO driver for MMC5983 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MMC5983_REG_XOUT0 0x00 +#define MMC5983_REG_XOUT1 0x01 +#define MMC5983_REG_YOUT0 0x02 +#define MMC5983_REG_YOUT1 0x03 +#define MMC5983_REG_ZOUT0 0x04 +#define MMC5983_REG_ZOUT1 0x05 +#define MMC5983_REG_XYZOUT2 0x06 + +#define MMC5983_REG_STATUS 0x08 + +#define MMC5983_REG_CTRL0 0x09 +#define MMC5983_REG_CTRL1 0x0A +#define MMC5983_REG_CTRL2 0x0B +#define MMC5983_REG_CTRL3 0x0C + +#define MMC5983_REG_ID 0x2F + +#define MMC5983_PRODUCT_ID 0x30 + +#define MMC5983_STATUS_MEAS_M_DONE_BIT BIT(0) +#define MMC5983_STATUS_OTP_RD_DONE_BIT BIT(4) + +#define MMC5983_CTRL0_TM_M_BIT BIT(0) +#define MMC5983_CTRL0_SET_BIT BIT(3) +#define MMC5983_CTRL0_RESET_BIT BIT(4) +#define MMC5983_CTRL0_OTP_RD_BIT BIT(6) + +#define MMC5983_CTRL1_SW_RST_BIT BIT(7) + +enum mmc5983_axis { + MMC5983_AXIS_X, + MMC5983_AXIS_Y, + MMC5983_AXIS_Z, +}; + +struct mmc5983_data { + struct regmap *regmap; + /* Protects chip access during SET/RESET measurement sequence */ + struct mutex mutex; +}; + +#define MMC5983_CHANNEL(_axis) { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = MMC5983_AXIS_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mmc5983_channels[] = { + MMC5983_CHANNEL(X), + MMC5983_CHANNEL(Y), + MMC5983_CHANNEL(Z), +}; + +static int mmc5983_pulse_coil(struct mmc5983_data *data, unsigned int coil_bit) +{ + int ret; + + ret = regmap_write(data->regmap, MMC5983_REG_CTRL0, coil_bit); + if (ret) + return ret; + + /* + * Datasheet page 15: SET/RESET coil pulse is 500 ns. + * Vendor sample code waits 500 us before the next operation. + */ + fsleep(500); + + return 0; +} + +static int mmc5983_take_measurement(struct mmc5983_data *data, int m[3]) +{ + unsigned int status; + u8 buf[7]; + int ret; + + ret = regmap_write(data->regmap, MMC5983_REG_CTRL0, + MMC5983_CTRL0_TM_M_BIT); + if (ret) + return ret; + + /* + * Datasheet page 15: measurement time is 8 ms at BW=00 (default, + * slowest setting). Use a 50 ms timeout for margin. + */ + ret = regmap_read_poll_timeout(data->regmap, MMC5983_REG_STATUS, + status, + status & MMC5983_STATUS_MEAS_M_DONE_BIT, + 10 * USEC_PER_MSEC, + 50 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, MMC5983_REG_XOUT0, buf, + sizeof(buf)); + if (ret) + return ret; + + m[0] = (buf[0] << 10) | (buf[1] << 2) | ((buf[6] >> 6) & 0x3); + m[1] = (buf[2] << 10) | (buf[3] << 2) | ((buf[6] >> 4) & 0x3); + m[2] = (buf[4] << 10) | (buf[5] << 2) | ((buf[6] >> 2) & 0x3); + + return 0; +} + +static int mmc5983_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct mmc5983_data *data = iio_priv(indio_dev); + int m1[3], m2[3]; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&data->mutex); + + /* SET: magnetize sensor elements in forward direction */ + ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_SET_BIT); + if (ret) + return ret; + + ret = mmc5983_take_measurement(data, m1); + if (ret) + return ret; + + /* RESET: magnetize sensor elements in reverse direction */ + ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_RESET_BIT); + if (ret) + return ret; + + ret = mmc5983_take_measurement(data, m2); + if (ret) + return ret; + + *val = (m1[chan->address] - m2[chan->address]) / 2; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 61035; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static const struct iio_info mmc5983_info = { + .read_raw = mmc5983_read_raw, +}; + +static bool mmc5983_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MMC5983_REG_CTRL0: + case MMC5983_REG_CTRL1: + case MMC5983_REG_CTRL2: + case MMC5983_REG_CTRL3: + return true; + default: + return false; + } +} + +static bool mmc5983_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MMC5983_REG_XOUT0: + case MMC5983_REG_XOUT1: + case MMC5983_REG_YOUT0: + case MMC5983_REG_YOUT1: + case MMC5983_REG_ZOUT0: + case MMC5983_REG_ZOUT1: + case MMC5983_REG_XYZOUT2: + case MMC5983_REG_STATUS: + case MMC5983_REG_CTRL0: + case MMC5983_REG_CTRL1: + case MMC5983_REG_CTRL2: + case MMC5983_REG_CTRL3: + case MMC5983_REG_ID: + return true; + default: + return false; + } +} + +static bool mmc5983_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MMC5983_REG_XOUT0: + case MMC5983_REG_XOUT1: + case MMC5983_REG_YOUT0: + case MMC5983_REG_YOUT1: + case MMC5983_REG_ZOUT0: + case MMC5983_REG_ZOUT1: + case MMC5983_REG_XYZOUT2: + case MMC5983_REG_STATUS: + case MMC5983_REG_CTRL0: + case MMC5983_REG_CTRL1: + return true; + default: + return false; + } +} + +static const struct regmap_config mmc5983_regmap_config = { + .name = "mmc5983_regmap", + .reg_bits = 8, + .val_bits = 8, + .max_register = MMC5983_REG_ID, + .writeable_reg = mmc5983_is_writeable_reg, + .readable_reg = mmc5983_is_readable_reg, + .volatile_reg = mmc5983_is_volatile_reg, +}; + +static int mmc5983_init(struct mmc5983_data *data) +{ + struct regmap *regmap = data->regmap; + struct device *dev = regmap_get_device(regmap); + unsigned int reg_id, status; + int ret; + + ret = regmap_read(regmap, MMC5983_REG_ID, ®_id); + if (ret) + return dev_err_probe(dev, ret, "Error reading product id\n"); + + if (reg_id != MMC5983_PRODUCT_ID) + dev_info(dev, "unexpected product id 0x%02x\n", reg_id); + + ret = regmap_write(regmap, MMC5983_REG_CTRL1, MMC5983_CTRL1_SW_RST_BIT); + if (ret) + return ret; + + /* Datasheet page 15: power-on time after SW_RST is 10 ms */ + fsleep(10 * USEC_PER_MSEC); + + ret = regmap_write(regmap, MMC5983_REG_CTRL0, MMC5983_CTRL0_OTP_RD_BIT); + if (ret) + return ret; + + /* + * Datasheet page 15: OTP read completes and self-clears. No separate + * OTP refresh timeout is specified, so use the 10 ms power-on time as + * a conservative upper bound. + */ + ret = regmap_read_poll_timeout(regmap, MMC5983_REG_STATUS, status, + status & MMC5983_STATUS_OTP_RD_DONE_BIT, + USEC_PER_MSEC, + 10 * USEC_PER_MSEC); + if (ret) + return ret; + + /* SET: magnetize sensor elements in forward direction */ + ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_SET_BIT); + if (ret) + return ret; + + /* RESET: magnetize sensor elements in reverse direction */ + return mmc5983_pulse_coil(data, MMC5983_CTRL0_RESET_BIT); +} + +static int mmc5983_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct mmc5983_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + data->regmap = devm_regmap_init_i2c(i2c, &mmc5983_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to allocate register map\n"); + + indio_dev->info = &mmc5983_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = "mmc5983"; + indio_dev->channels = mmc5983_channels; + indio_dev->num_channels = ARRAY_SIZE(mmc5983_channels); + + ret = mmc5983_init(data); + if (ret) + return dev_err_probe(dev, ret, "mmc5983 chip init failed\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mmc5983_of_match[] = { + { .compatible = "memsic,mmc5983" }, + { } +}; +MODULE_DEVICE_TABLE(of, mmc5983_of_match); + +static const struct i2c_device_id mmc5983_id[] = { + { "mmc5983" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mmc5983_id); + +static struct i2c_driver mmc5983_driver = { + .driver = { + .name = "mmc5983", + .of_match_table = mmc5983_of_match, + }, + .probe = mmc5983_probe, + .id_table = mmc5983_id, +}; +module_i2c_driver(mmc5983_driver); + +MODULE_AUTHOR("Vladislav Kulikov "); +MODULE_DESCRIPTION("MEMSIC MMC5983 magnetic sensor driver"); +MODULE_LICENSE("GPL"); From b622051675183c37c4b1d4699233528f5744eb8c Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 12 May 2026 12:57:22 +0200 Subject: [PATCH 2344/5214] iio: light: opt3001: make headers conform to iwyu Remove kernel.h proxy header, device.h, irq.h, slab.h as they are unnecessary and add missing headers (array_size.h, dev_printk.h, errno.h, jiffies.h, wait.h) to enforce IWYU principle and reduce transitive dependencies. Also, replace bitops.h with bits.h as only the BIT() macro is used. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 53bc455b7bad..e6c3665f46be 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -8,18 +8,19 @@ * Based on previous work from: Felipe Balbi */ -#include +#include +#include #include -#include +#include +#include #include #include -#include -#include -#include +#include #include +#include #include -#include #include +#include #include #include From 8402b3266173e3c282244246354b9863bca92ffd Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 12 May 2026 12:57:23 +0200 Subject: [PATCH 2345/5214] iio: light: opt3001: use macros from bits.h header Use GENMASK() and BIT() macros from bits.h header where it makes sense. While at it, remove unused macro. No functional change. Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index e6c3665f46be..dac3d3818d0a 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -33,17 +33,16 @@ #define OPT3001_MANUFACTURER_ID 0x7e #define OPT3001_DEVICE_ID 0x7f -#define OPT3001_CONFIGURATION_RN_MASK (0xf << 12) +#define OPT3001_CONFIGURATION_RN_MASK GENMASK(15, 12) #define OPT3001_CONFIGURATION_RN_AUTO (0xc << 12) #define OPT3001_CONFIGURATION_CT BIT(11) -#define OPT3001_CONFIGURATION_M_MASK (3 << 9) +#define OPT3001_CONFIGURATION_M_MASK GENMASK(10, 9) #define OPT3001_CONFIGURATION_M_SHUTDOWN (0 << 9) #define OPT3001_CONFIGURATION_M_SINGLE (1 << 9) #define OPT3001_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */ -#define OPT3001_CONFIGURATION_OVF BIT(8) #define OPT3001_CONFIGURATION_CRF BIT(7) #define OPT3001_CONFIGURATION_FH BIT(6) #define OPT3001_CONFIGURATION_FL BIT(5) @@ -51,7 +50,7 @@ #define OPT3001_CONFIGURATION_POL BIT(3) #define OPT3001_CONFIGURATION_ME BIT(2) -#define OPT3001_CONFIGURATION_FC_MASK (3 << 0) +#define OPT3001_CONFIGURATION_FC_MASK GENMASK(1, 0) /* The end-of-conversion enable is located in the low-limit register */ #define OPT3001_LOW_LIMIT_EOC_ENABLE 0xc000 From 947eb6f0a274f8b15a0248051a65b069effd5057 Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Tue, 12 May 2026 21:31:50 -0300 Subject: [PATCH 2346/5214] iio: adc: xilinx-ams: fix out-of-bounds channel lookup in event handling ams_event_to_channel() may return a pointer past the end of dev->channels when no matching scan_index is found. This can lead to invalid memory access in ams_handle_event(). Add a bounds check in ams_event_to_channel() and return NULL when no channel is found. Also guard the caller to safely handle this case. Fixes: d5c70627a794 ("iio: adc: Add Xilinx AMS driver") Signed-off-by: Guilherme Ivo Bozi Reviewed-by: Salih Erim Tested-by: Salih Erim Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-ams.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 124470c92529..6191cd1b29a5 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -871,6 +871,9 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, if (dev->channels[i].scan_index == scan_index) break; + if (i == dev->num_channels) + return NULL; + return &dev->channels[i]; } @@ -1012,6 +1015,8 @@ static void ams_handle_event(struct iio_dev *indio_dev, u32 event) const struct iio_chan_spec *chan; chan = ams_event_to_channel(indio_dev, event); + if (!chan) + return; if (chan->type == IIO_TEMP) { /* From f3c1ef2bca5c128b62b5b804a50ad3f6f64a3098 Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Tue, 12 May 2026 21:31:51 -0300 Subject: [PATCH 2347/5214] iio: adc: xilinx-ams: use guard(mutex) for automatic locking Replace open-coded mutex_lock()/mutex_unlock() pairs with guard(mutex) to simplify locking and ensure proper unlock on all control flow paths. This removes explicit unlock handling, reduces boilerplate, and avoids potential mistakes in error paths while keeping the behavior unchanged. Signed-off-by: Guilherme Ivo Bozi Reviewed-by: Andy Shevchenko Reviewed-by: Salih Erim Tested-by: Salih Erim Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-ams.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 6191cd1b29a5..70a1f97f6dad 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -720,22 +720,20 @@ static int ams_read_raw(struct iio_dev *indio_dev, int ret; switch (mask) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&ams->lock); + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&ams->lock); if (chan->scan_index >= AMS_CTRL_SEQ_BASE) { ret = ams_read_vcc_reg(ams, chan->address, val); if (ret) - goto unlock_mutex; + return ret; ams_enable_channel_sequence(indio_dev); } else if (chan->scan_index >= AMS_PS_SEQ_MAX) *val = readl(ams->pl_base + chan->address); else *val = readl(ams->ps_base + chan->address); - ret = IIO_VAL_INT; -unlock_mutex: - mutex_unlock(&ams->lock); - return ret; + return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_VOLTAGE: @@ -939,7 +937,7 @@ static int ams_write_event_config(struct iio_dev *indio_dev, alarm = ams_get_alarm_mask(chan->scan_index); - mutex_lock(&ams->lock); + guard(mutex)(&ams->lock); if (state) ams->alarm_mask |= alarm; @@ -948,8 +946,6 @@ static int ams_write_event_config(struct iio_dev *indio_dev, ams_update_alarm(ams, ams->alarm_mask); - mutex_unlock(&ams->lock); - return 0; } @@ -962,15 +958,13 @@ static int ams_read_event_value(struct iio_dev *indio_dev, struct ams *ams = iio_priv(indio_dev); unsigned int offset = ams_get_alarm_offset(chan->scan_index, dir); - mutex_lock(&ams->lock); + guard(mutex)(&ams->lock); if (chan->scan_index >= AMS_PS_SEQ_MAX) *val = readl(ams->pl_base + offset); else *val = readl(ams->ps_base + offset); - mutex_unlock(&ams->lock); - return IIO_VAL_INT; } @@ -983,7 +977,7 @@ static int ams_write_event_value(struct iio_dev *indio_dev, struct ams *ams = iio_priv(indio_dev); unsigned int offset; - mutex_lock(&ams->lock); + guard(mutex)(&ams->lock); /* Set temperature channel threshold to direct threshold */ if (chan->type == IIO_TEMP) { @@ -1005,8 +999,6 @@ static int ams_write_event_value(struct iio_dev *indio_dev, else writel(val, ams->ps_base + offset); - mutex_unlock(&ams->lock); - return 0; } From 43a66cae2e9dc97b309a296deb141a9b3446eb1a Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Tue, 12 May 2026 21:31:52 -0300 Subject: [PATCH 2348/5214] iio: adc: xilinx-ams: refactor alarm mapping to table-driven approach Replace multiple open-coded switch statements that map between scan_index, alarm bits, and register offsets with a centralized table-driven approach. Introduce a struct-based alarm_map to describe the relationship between scan indices and alarm offsets, and add a helper to translate scan_index to event IDs. This removes duplicated logic across ams_get_alarm_offset(), ams_event_to_channel(), and ams_get_alarm_mask(). The new approach improves maintainability, reduces code size, and makes it easier to extend or modify alarm mappings in the future, while preserving existing behavior. Signed-off-by: Guilherme Ivo Bozi Reviewed-by: Salih Erim Tested-by: Salih Erim Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-ams.c | 161 +++++++++++++---------------------- 1 file changed, 58 insertions(+), 103 deletions(-) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 70a1f97f6dad..d38c4401dfce 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -102,6 +102,7 @@ #define AMS_PS_SEQ_MASK GENMASK(21, 0) #define AMS_PL_SEQ_MASK GENMASK_ULL(59, 22) +#define AMS_ALARM_NONE 0x000 /* not a real offset */ #define AMS_ALARM_TEMP 0x140 #define AMS_ALARM_SUPPLY1 0x144 #define AMS_ALARM_SUPPLY2 0x148 @@ -763,9 +764,49 @@ static int ams_read_raw(struct iio_dev *indio_dev, } } +struct ams_alarm_map { + enum ams_ps_pl_seq scan_index; + unsigned int base_offset; +}; + +/* + * Array index matches enum ams_alarm_bit. + * Entries with base_offset == AMS_ALARM_NONE are unused/invalid + * (e.g. RESERVED) and must be skipped. + */ +static const struct ams_alarm_map alarm_map[] = { + [AMS_ALARM_BIT_TEMP] = { AMS_SEQ_TEMP, AMS_ALARM_TEMP }, + [AMS_ALARM_BIT_SUPPLY1] = { AMS_SEQ_SUPPLY1, AMS_ALARM_SUPPLY1 }, + [AMS_ALARM_BIT_SUPPLY2] = { AMS_SEQ_SUPPLY2, AMS_ALARM_SUPPLY2 }, + [AMS_ALARM_BIT_SUPPLY3] = { AMS_SEQ_SUPPLY3, AMS_ALARM_SUPPLY3 }, + [AMS_ALARM_BIT_SUPPLY4] = { AMS_SEQ_SUPPLY4, AMS_ALARM_SUPPLY4 }, + [AMS_ALARM_BIT_SUPPLY5] = { AMS_SEQ_SUPPLY5, AMS_ALARM_SUPPLY5 }, + [AMS_ALARM_BIT_SUPPLY6] = { AMS_SEQ_SUPPLY6, AMS_ALARM_SUPPLY6 }, + [AMS_ALARM_BIT_RESERVED] = { 0, AMS_ALARM_NONE }, + [AMS_ALARM_BIT_SUPPLY7] = { AMS_SEQ_SUPPLY7, AMS_ALARM_SUPPLY7 }, + [AMS_ALARM_BIT_SUPPLY8] = { AMS_SEQ_SUPPLY8, AMS_ALARM_SUPPLY8 }, + [AMS_ALARM_BIT_SUPPLY9] = { AMS_SEQ_SUPPLY9, AMS_ALARM_SUPPLY9 }, + [AMS_ALARM_BIT_SUPPLY10] = { AMS_SEQ_SUPPLY10, AMS_ALARM_SUPPLY10 }, + [AMS_ALARM_BIT_VCCAMS] = { AMS_SEQ_VCCAMS, AMS_ALARM_VCCAMS }, + [AMS_ALARM_BIT_TEMP_REMOTE] = { AMS_SEQ_TEMP_REMOTE, AMS_ALARM_TEMP_REMOTE }, +}; + +static int ams_scan_index_to_event(int scan_index) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(alarm_map); i++) { + if (alarm_map[i].base_offset == AMS_ALARM_NONE) + continue; + + if (alarm_map[i].scan_index == scan_index) + return i; + } + + return -EINVAL; +} + static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir) { - int offset; + int offset, event; if (scan_index >= AMS_PS_SEQ_MAX) scan_index -= AMS_PS_SEQ_MAX; @@ -779,36 +820,11 @@ static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir) offset = 0; } - switch (scan_index) { - case AMS_SEQ_TEMP: - return AMS_ALARM_TEMP + offset; - case AMS_SEQ_SUPPLY1: - return AMS_ALARM_SUPPLY1 + offset; - case AMS_SEQ_SUPPLY2: - return AMS_ALARM_SUPPLY2 + offset; - case AMS_SEQ_SUPPLY3: - return AMS_ALARM_SUPPLY3 + offset; - case AMS_SEQ_SUPPLY4: - return AMS_ALARM_SUPPLY4 + offset; - case AMS_SEQ_SUPPLY5: - return AMS_ALARM_SUPPLY5 + offset; - case AMS_SEQ_SUPPLY6: - return AMS_ALARM_SUPPLY6 + offset; - case AMS_SEQ_SUPPLY7: - return AMS_ALARM_SUPPLY7 + offset; - case AMS_SEQ_SUPPLY8: - return AMS_ALARM_SUPPLY8 + offset; - case AMS_SEQ_SUPPLY9: - return AMS_ALARM_SUPPLY9 + offset; - case AMS_SEQ_SUPPLY10: - return AMS_ALARM_SUPPLY10 + offset; - case AMS_SEQ_VCCAMS: - return AMS_ALARM_VCCAMS + offset; - case AMS_SEQ_TEMP_REMOTE: - return AMS_ALARM_TEMP_REMOTE + offset; - default: + event = ams_scan_index_to_event(scan_index); + if (event < 0 || alarm_map[event].base_offset == AMS_ALARM_NONE) return 0; - } + + return alarm_map[event].base_offset + offset; } static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, @@ -821,49 +837,13 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, scan_index = AMS_PS_SEQ_MAX; } - switch (event) { - case AMS_ALARM_BIT_TEMP: - scan_index += AMS_SEQ_TEMP; - break; - case AMS_ALARM_BIT_SUPPLY1: - scan_index += AMS_SEQ_SUPPLY1; - break; - case AMS_ALARM_BIT_SUPPLY2: - scan_index += AMS_SEQ_SUPPLY2; - break; - case AMS_ALARM_BIT_SUPPLY3: - scan_index += AMS_SEQ_SUPPLY3; - break; - case AMS_ALARM_BIT_SUPPLY4: - scan_index += AMS_SEQ_SUPPLY4; - break; - case AMS_ALARM_BIT_SUPPLY5: - scan_index += AMS_SEQ_SUPPLY5; - break; - case AMS_ALARM_BIT_SUPPLY6: - scan_index += AMS_SEQ_SUPPLY6; - break; - case AMS_ALARM_BIT_SUPPLY7: - scan_index += AMS_SEQ_SUPPLY7; - break; - case AMS_ALARM_BIT_SUPPLY8: - scan_index += AMS_SEQ_SUPPLY8; - break; - case AMS_ALARM_BIT_SUPPLY9: - scan_index += AMS_SEQ_SUPPLY9; - break; - case AMS_ALARM_BIT_SUPPLY10: - scan_index += AMS_SEQ_SUPPLY10; - break; - case AMS_ALARM_BIT_VCCAMS: - scan_index += AMS_SEQ_VCCAMS; - break; - case AMS_ALARM_BIT_TEMP_REMOTE: - scan_index += AMS_SEQ_TEMP_REMOTE; - break; - default: - break; - } + if (event >= ARRAY_SIZE(alarm_map)) + return NULL; + + if (alarm_map[event].base_offset == AMS_ALARM_NONE) + return NULL; + + scan_index += alarm_map[event].scan_index; for (i = 0; i < dev->num_channels; i++) if (dev->channels[i].scan_index == scan_index) @@ -877,43 +857,18 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, static int ams_get_alarm_mask(int scan_index) { - int bit = 0; + int bit = 0, event; if (scan_index >= AMS_PS_SEQ_MAX) { bit = AMS_PL_ALARM_START; scan_index -= AMS_PS_SEQ_MAX; } - switch (scan_index) { - case AMS_SEQ_TEMP: - return BIT(AMS_ALARM_BIT_TEMP + bit); - case AMS_SEQ_SUPPLY1: - return BIT(AMS_ALARM_BIT_SUPPLY1 + bit); - case AMS_SEQ_SUPPLY2: - return BIT(AMS_ALARM_BIT_SUPPLY2 + bit); - case AMS_SEQ_SUPPLY3: - return BIT(AMS_ALARM_BIT_SUPPLY3 + bit); - case AMS_SEQ_SUPPLY4: - return BIT(AMS_ALARM_BIT_SUPPLY4 + bit); - case AMS_SEQ_SUPPLY5: - return BIT(AMS_ALARM_BIT_SUPPLY5 + bit); - case AMS_SEQ_SUPPLY6: - return BIT(AMS_ALARM_BIT_SUPPLY6 + bit); - case AMS_SEQ_SUPPLY7: - return BIT(AMS_ALARM_BIT_SUPPLY7 + bit); - case AMS_SEQ_SUPPLY8: - return BIT(AMS_ALARM_BIT_SUPPLY8 + bit); - case AMS_SEQ_SUPPLY9: - return BIT(AMS_ALARM_BIT_SUPPLY9 + bit); - case AMS_SEQ_SUPPLY10: - return BIT(AMS_ALARM_BIT_SUPPLY10 + bit); - case AMS_SEQ_VCCAMS: - return BIT(AMS_ALARM_BIT_VCCAMS + bit); - case AMS_SEQ_TEMP_REMOTE: - return BIT(AMS_ALARM_BIT_TEMP_REMOTE + bit); - default: + event = ams_scan_index_to_event(scan_index); + if (event < 0) return 0; - } + + return BIT(event + bit); } static int ams_read_event_config(struct iio_dev *indio_dev, From 9ad241c71106d3dfb5d194e66b83462a18412cba Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 May 2026 14:01:12 +1300 Subject: [PATCH 2349/5214] iio: light: veml6030: remove unnecessary read of IT index This is dead code as the IT index is not used by gts to set the new scale. In its current form, the value is read but not used afterward. Remove the dead code. Signed-off-by: Javier Carrasco Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6030.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 6bcacae3863c..745cf3ad7092 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -521,13 +521,9 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev, static int veml6030_set_scale(struct iio_dev *indio_dev, int val, int val2) { - int ret, gain_sel, it_idx, it_sel; + int ret, gain_sel, it_sel; struct veml6030_data *data = iio_priv(indio_dev); - ret = regmap_field_read(data->rf.it, &it_idx); - if (ret) - return ret; - ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2, &gain_sel, &it_sel); if (ret) From 24245b93873bfef91657658775be3918e052d52f Mon Sep 17 00:00:00 2001 From: Michal Piekos Date: Sat, 16 May 2026 07:34:14 +0200 Subject: [PATCH 2350/5214] dt-bindings: iio: adc: Add GPADC for Allwinner A523 Add support for the GPADC for the Allwinner A523. It differs from the D1/T113s/R329/T507 by having two clocks. Acked-by: Conor Dooley Signed-off-by: Michal Piekos Signed-off-by: Jonathan Cameron --- .../iio/adc/allwinner,sun20i-d1-gpadc.yaml | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml index da605a051b94..6467800d30e2 100644 --- a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml @@ -14,6 +14,7 @@ properties: oneOf: - enum: - allwinner,sun20i-d1-gpadc + - allwinner,sun55i-a523-gpadc - items: - enum: - allwinner,sun50i-h616-gpadc @@ -29,7 +30,12 @@ properties: const: 0 clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 interrupts: maxItems: 1 @@ -40,6 +46,30 @@ properties: resets: maxItems: 1 +allOf: + - if: + properties: + compatible: + enum: + - allwinner,sun55i-a523-gpadc + then: + properties: + clocks: + items: + - description: Bus clock + - description: Module clock + clock-names: + items: + - const: bus + - const: mod + required: + - clock-names + else: + properties: + clocks: + maxItems: 1 + clock-names: false + patternProperties: "^channel@[0-9a-f]+$": $ref: adc.yaml From 8114f4420360260576cbd6d0633b28a607321f0b Mon Sep 17 00:00:00 2001 From: Michal Piekos Date: Sat, 16 May 2026 07:34:15 +0200 Subject: [PATCH 2351/5214] iio: adc: sun20i-gpadc: add A523 gpadc support A523 differs from existing sun20i-gpadc-iio by having two clocks; bus clock and module clock. Change driver to enable all clocks. Signed-off-by: Michal Piekos Signed-off-by: Jonathan Cameron --- drivers/iio/adc/sun20i-gpadc-iio.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index 861c14da75ad..f5fd2240b808 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -177,10 +177,10 @@ static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, static int sun20i_gpadc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct iio_dev *indio_dev; struct sun20i_gpadc_iio *info; + struct clk_bulk_data *clks; struct reset_control *rst; - struct clk *clk; + struct iio_dev *indio_dev; int irq; int ret; @@ -205,9 +205,11 @@ static int sun20i_gpadc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); - clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) - return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock\n"); + ret = devm_clk_bulk_get_all_enabled(dev, &clks); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to enable clocks\n"); + if (ret == 0) + return dev_err_probe(dev, -ENODEV, "needs at least one clocks\n"); rst = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(rst)) @@ -243,6 +245,7 @@ static int sun20i_gpadc_probe(struct platform_device *pdev) static const struct of_device_id sun20i_gpadc_of_id[] = { { .compatible = "allwinner,sun20i-d1-gpadc" }, + { .compatible = "allwinner,sun55i-a523-gpadc" }, { } }; MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id); From 44f38b46298dc2b0e007e88381d46fb92172a81d Mon Sep 17 00:00:00 2001 From: Michal Piekos Date: Sat, 16 May 2026 07:48:37 +0200 Subject: [PATCH 2352/5214] iio: adc: sun20i-gpadc: support non-contiguous channel lookups Using consumer driver like iio-hwmon which resolve channels through io-channels phandles will fail for sparse channels because IIO core by default treats phandle argument as index into channel array. eg. <&gpadc 1> will fail if there is only channel@1 specified Add .fwnode_xlate() which maps DT phandle to the registered channel whose chan->channel matches the hardware channel number. It allows sparse channel maps to be consumed by drivers like iio-hwmon. Tested on Radxa Cubie A5E. Reviewed-by: Andy Shevchenko Signed-off-by: Michal Piekos Signed-off-by: Jonathan Cameron --- drivers/iio/adc/sun20i-gpadc-iio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index f5fd2240b808..81fc4610e15e 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -139,8 +139,23 @@ static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int +sun20i_gpadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + if (iiospec->nargs != 1) + return -EINVAL; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + static const struct iio_info sun20i_gpadc_iio_info = { .read_raw = sun20i_gpadc_read_raw, + .fwnode_xlate = sun20i_gpadc_fwnode_xlate, }; static void sun20i_gpadc_reset_assert(void *data) From 20c598e82cd0b37334dc3e95fbd460f594460c3c Mon Sep 17 00:00:00 2001 From: Nikhil Gautam Date: Mon, 20 Apr 2026 13:54:36 +0530 Subject: [PATCH 2353/5214] iio: chemical: bme680: use BME680_NUM_CHANNELS for scan buffer Use BME680_NUM_CHANNELS instead of the hardcoded channel count in the scan buffer. This avoids use of a magic number and improves code readability and maintainability. Suggested-by: Jonathan Cameron Signed-off-by: Nikhil Gautam Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 6aeac132394c..ac9a737481dc 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -128,7 +128,7 @@ struct bme680_data { u16 heater_temp; struct { - s32 chan[4]; + s32 chan[BME680_NUM_CHANNELS]; aligned_s64 ts; } scan; From cc116c10f7874be109bd956a9080df113c246feb Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Sat, 9 May 2026 15:20:40 +0100 Subject: [PATCH 2354/5214] iio: dac: ad5504: sort headers alphabetically Rearrange the include headers in alphabetical order to follow the standard kernel coding style. This is a preparatory cleanup with no functional changes. Reviewed-by: Andy Shevchenko Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5504.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 355bcb6a8ba0..03ce37e2c616 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -5,21 +5,21 @@ * Copyright 2011 Analog Devices Inc. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include #define AD5504_RES_MASK GENMASK(11, 0) #define AD5504_CMD_READ BIT(15) From 97d30982aa5ae2b2f1778fdc91cc8f6e26e15453 Mon Sep 17 00:00:00 2001 From: Taha Ed-Dafili <0rayn.dev@gmail.com> Date: Sat, 9 May 2026 15:20:42 +0100 Subject: [PATCH 2355/5214] iio: dac: ad5504: introduce local dev pointer Replace &spi->dev with a local dev pointer to shorten lines, fix alignment, and improve overall readability in the probe function. Signed-off-by: Taha Ed-Dafili <0rayn.dev@gmail.com> Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5504.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 03ce37e2c616..5e586185d857 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -270,25 +270,26 @@ static const struct iio_chan_spec ad5504_channels[] = { static int ad5504_probe(struct spi_device *spi) { - const struct ad5504_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device *dev = &spi->dev; + const struct ad5504_platform_data *pdata = dev_get_platdata(dev); struct iio_dev *indio_dev; struct ad5504_state *st; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc"); + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); if (ret < 0 && ret != -ENODEV) return ret; if (ret == -ENODEV) { if (pdata->vref_mv) st->vref_mv = pdata->vref_mv; else - dev_warn(&spi->dev, "reference voltage unspecified\n"); + dev_warn(dev, "reference voltage unspecified\n"); } else { st->vref_mv = ret / 1000; } @@ -304,17 +305,17 @@ static int ad5504_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; if (spi->irq) { - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - NULL, - &ad5504_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - spi_get_device_id(st->spi)->name, - indio_dev); + ret = devm_request_threaded_irq(dev, spi->irq, + NULL, + &ad5504_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi_get_device_id(st->spi)->name, + indio_dev); if (ret) return ret; } - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad5504_id[] = { From 5bdff291d20c31b365d9ddfe9c426fbfb41da5bb Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:31 +0530 Subject: [PATCH 2356/5214] iio: accel: mma8452: handle I2C read error(s) in mma8452_read() Currently, If i2c_smbus_read_i2c_block_data() fails but mma8452_set_runtime_pm_state() succeeds, mma8452_read() returns 0. As a result, the caller mma8452_read_raw() assumes the read was successful and proceeds to use a buffer containing uninitialized stack memory. Add proper checking of the I2C read return value and propagate errors to the caller. Fixes: 96c0cb2bbfe0 ("iio: mma8452: add support for runtime power management") Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 15172ba2972c..cefc7cf4bd83 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -252,6 +252,8 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3]) ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *)buf); + if (ret < 0) + return ret; ret = mma8452_set_runtime_pm_state(data->client, false); From 0a6726ec20cd4c0101f2de0ca485a11676224dea Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:32 +0530 Subject: [PATCH 2357/5214] iio: accel: mma8452: switch to non-devm request_threaded_irq() Avoid using devm_request_threaded_irq() as the driver requires explicit error-handling path(s). Using devm_* API together with goto-based unwinding breaks the expected LIFO resource release model. Add explicit IRQ cleanup in the driver teardown paths to follow kernel resource management conventions. Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index cefc7cf4bd83..279a9b364886 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1682,18 +1682,16 @@ static int mma8452_probe(struct i2c_client *client) goto trigger_cleanup; if (client->irq) { - ret = devm_request_threaded_irq(&client->dev, - client->irq, - NULL, mma8452_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - client->name, indio_dev); + ret = request_threaded_irq(client->irq, NULL, mma8452_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, indio_dev); if (ret) goto buffer_cleanup; } ret = pm_runtime_set_active(&client->dev); if (ret < 0) - goto buffer_cleanup; + goto free_irq; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, @@ -1702,7 +1700,7 @@ static int mma8452_probe(struct i2c_client *client) ret = iio_device_register(indio_dev); if (ret < 0) - goto buffer_cleanup; + goto free_irq; ret = mma8452_set_freefall_mode(data, false); if (ret < 0) @@ -1713,6 +1711,10 @@ static int mma8452_probe(struct i2c_client *client) unregister_device: iio_device_unregister(indio_dev); +free_irq: + if (client->irq) + free_irq(client->irq, indio_dev); + buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); @@ -1738,6 +1740,9 @@ static void mma8452_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); + if (client->irq) + free_irq(client->irq, indio_dev); + iio_triggered_buffer_cleanup(indio_dev); mma8452_trigger_cleanup(indio_dev); mma8452_standby(iio_priv(indio_dev)); From b4f6b124467f5d770e170d93e6e12a2fe3977927 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:33 +0530 Subject: [PATCH 2358/5214] iio: accel: mma8452: cleanup codestyle warning Reported by checkpatch: FILE: drivers/iio/accel/mma8452.c CHECK: Alignment should match open parenthesis Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 279a9b364886..916631519d3f 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -706,8 +706,8 @@ static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, } static int __mma8452_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct mma8452_data *data = iio_priv(indio_dev); int i, j, ret; @@ -788,8 +788,9 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, } static int mma8452_get_event_regs(struct mma8452_data *data, - const struct iio_chan_spec *chan, enum iio_event_direction dir, - const struct mma8452_event_regs **ev_reg) + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + const struct mma8452_event_regs **ev_reg) { if (!chan) return -EINVAL; @@ -818,11 +819,11 @@ static int mma8452_get_event_regs(struct mma8452_data *data, } static int mma8452_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); int ret, us, power_mode; @@ -881,11 +882,11 @@ static int mma8452_read_event_value(struct iio_dev *indio_dev, } static int mma8452_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) { struct mma8452_data *data = iio_priv(indio_dev); int ret, reg, steps; @@ -955,8 +956,7 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev, case IIO_EV_DIR_FALLING: return mma8452_freefall_mode_enabled(data); case IIO_EV_DIR_RISING: - ret = i2c_smbus_read_byte_data(data->client, - ev_regs->ev_cfg); + ret = i2c_smbus_read_byte_data(data->client, ev_regs->ev_cfg); if (ret < 0) return ret; @@ -1193,7 +1193,7 @@ static const struct attribute_group mma8452_event_attribute_group = { static const struct iio_mount_matrix * mma8452_get_mount_matrix(const struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) + const struct iio_chan_spec *chan) { struct mma8452_data *data = iio_priv(indio_dev); @@ -1516,8 +1516,9 @@ static int mma8452_reset(struct i2c_client *client) * The following code will read the reset register, and check whether * this reset works. */ - i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2, - MMA8452_CTRL_REG2_RST); + i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG2, + MMA8452_CTRL_REG2_RST); for (i = 0; i < 10; i++) { usleep_range(100, 200); @@ -1647,8 +1648,8 @@ static int mma8452_probe(struct i2c_client *client) dev_dbg(&client->dev, "using interrupt line INT2\n"); } else { ret = i2c_smbus_write_byte_data(client, - MMA8452_CTRL_REG5, - data->chip_info->all_events); + MMA8452_CTRL_REG5, + data->chip_info->all_events); if (ret < 0) goto disable_regulators; @@ -1656,8 +1657,8 @@ static int mma8452_probe(struct i2c_client *client) } ret = i2c_smbus_write_byte_data(client, - MMA8452_CTRL_REG4, - data->chip_info->enabled_events); + MMA8452_CTRL_REG4, + data->chip_info->enabled_events); if (ret < 0) goto disable_regulators; From e9f143941584ae27e9981649a3f9916c322ee01d Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:34 +0530 Subject: [PATCH 2359/5214] iio: accel: mma8452: sort headers alphabetically Sort include headers alphabetically to improve readability. Signed-off-by: Sanjay Chitroda Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 916631519d3f..d227ce3d5f67 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -18,21 +18,22 @@ * TODO: orientation events */ -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include -#include #include #include #include -#include -#include -#include -#include -#include #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) From 32a5c04d457540af67507494f30261580213df94 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:35 +0530 Subject: [PATCH 2360/5214] iio: accel: mma8452: Use dev_err_probe() dev_err_probe() makes error code handling simpler and handle deferred probe nicely (avoid spamming logs). Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index d227ce3d5f67..b49949792190 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1548,6 +1548,7 @@ MODULE_DEVICE_TABLE(of, mma8452_dt_ids); static int mma8452_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct mma8452_data *data; struct iio_dev *indio_dev; int ret; @@ -1580,14 +1581,12 @@ static int mma8452_probe(struct i2c_client *client) "failed to get VDDIO regulator!\n"); ret = regulator_enable(data->vdd_reg); - if (ret) { - dev_err(&client->dev, "failed to enable VDD regulator!\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to enable VDD regulator!\n"); ret = regulator_enable(data->vddio_reg); if (ret) { - dev_err(&client->dev, "failed to enable VDDIO regulator!\n"); + dev_err_probe(dev, ret, "failed to enable VDDIO regulator!\n"); goto disable_regulator_vdd; } From db95bd7933c6c5c033f468ce70332e6e7bb9f7c5 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 16 May 2026 12:32:19 +0100 Subject: [PATCH 2361/5214] iio: trigger: drop generic interrupt trigger. There is no way to bind to this driver from firmware and no explicit platform device creation calls exist in mainline. As such it is dead code. So drop it rather than potentially wasting time modernizing it (see #1) Link: https://lore.kernel.org/all/20260511063229.1433-1-sozdayvek@gmail.com/ #1 Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/Kconfig | 9 -- drivers/iio/trigger/Makefile | 1 - drivers/iio/trigger/iio-trig-interrupt.c | 109 ----------------------- 3 files changed, 119 deletions(-) delete mode 100644 drivers/iio/trigger/iio-trig-interrupt.c diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 7ecb69725b1d..52899c22d57c 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -16,15 +16,6 @@ config IIO_HRTIMER_TRIGGER To compile this driver as a module, choose M here: the module will be called iio-trig-hrtimer. -config IIO_INTERRUPT_TRIGGER - tristate "Generic interrupt trigger" - help - Provides support for using an interrupt of any type as an IIO - trigger. This may be provided by a gpio driver for example. - - To compile this driver as a module, choose M here: the - module will be called iio-trig-interrupt. - config IIO_STM32_LPTIMER_TRIGGER tristate "STM32 Low-Power Timer Trigger" depends on MFD_STM32_LPTIMER || COMPILE_TEST diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile index f3d11acb8a0b..fc1fd5661c94 100644 --- a/drivers/iio/trigger/Makefile +++ b/drivers/iio/trigger/Makefile @@ -6,7 +6,6 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o -obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c deleted file mode 100644 index a100c2e3a0d9..000000000000 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Industrial I/O - generic interrupt based trigger support - * - * Copyright (c) 2008-2013 Jonathan Cameron - */ - -#include -#include -#include -#include -#include - -#include -#include - - -struct iio_interrupt_trigger_info { - unsigned int irq; -}; - -static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) -{ - iio_trigger_poll(private); - return IRQ_HANDLED; -} - -static int iio_interrupt_trigger_probe(struct platform_device *pdev) -{ - struct iio_interrupt_trigger_info *trig_info; - struct iio_trigger *trig; - unsigned long irqflags; - struct resource *irq_res; - int irq, ret = 0; - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - - if (irq_res == NULL) - return -ENODEV; - - irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; - - irq = irq_res->start; - - trig = iio_trigger_alloc(NULL, "irqtrig%d", irq); - if (!trig) { - ret = -ENOMEM; - goto error_ret; - } - - trig_info = kzalloc_obj(*trig_info); - if (!trig_info) { - ret = -ENOMEM; - goto error_free_trigger; - } - iio_trigger_set_drvdata(trig, trig_info); - trig_info->irq = irq; - ret = request_irq(irq, iio_interrupt_trigger_poll, - irqflags, trig->name, trig); - if (ret) { - dev_err(&pdev->dev, - "request IRQ-%d failed", irq); - goto error_free_trig_info; - } - - ret = iio_trigger_register(trig); - if (ret) - goto error_release_irq; - platform_set_drvdata(pdev, trig); - - return 0; - -/* First clean up the partly allocated trigger */ -error_release_irq: - free_irq(irq, trig); -error_free_trig_info: - kfree(trig_info); -error_free_trigger: - iio_trigger_free(trig); -error_ret: - return ret; -} - -static void iio_interrupt_trigger_remove(struct platform_device *pdev) -{ - struct iio_trigger *trig; - struct iio_interrupt_trigger_info *trig_info; - - trig = platform_get_drvdata(pdev); - trig_info = iio_trigger_get_drvdata(trig); - iio_trigger_unregister(trig); - free_irq(trig_info->irq, trig); - kfree(trig_info); - iio_trigger_free(trig); -} - -static struct platform_driver iio_interrupt_trigger_driver = { - .probe = iio_interrupt_trigger_probe, - .remove = iio_interrupt_trigger_remove, - .driver = { - .name = "iio_interrupt_trigger", - }, -}; - -module_platform_driver(iio_interrupt_trigger_driver); - -MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("Interrupt trigger for the iio subsystem"); -MODULE_LICENSE("GPL v2"); From 8b44f074a76c310e4b311fb4509586254f251dcb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 May 2026 12:00:58 -0500 Subject: [PATCH 2362/5214] MAINTAINERS: add match for IIO API docs Add a match for Documentation/driver-api/iio/ to the IIO subsystem in MAINTAINERS. Any changes to the IIO API documentation should be reviewed IIO folks. Signed-off-by: David Lechner Reviewed-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 640192835bd3..1533337900da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12503,6 +12503,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git F: Documentation/ABI/testing/configfs-iio* F: Documentation/ABI/testing/sysfs-bus-iio* F: Documentation/devicetree/bindings/iio/ +F: Documentation/driver-api/iio/ F: Documentation/iio/ F: drivers/iio/ F: drivers/staging/iio/ From 6bce70d0a9bc907cedb12f498ebc4cd5f7ac7cee Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 May 2026 12:00:59 -0500 Subject: [PATCH 2363/5214] docs: iio: triggered-buffers: use new helpers in example Update the "typical" triggered buffer example to use various new helpers that have been added in the last year or so. This reflects current expectations of how similar code should be written. Also zero-initialize the buffer so we don't leak stack data. And fix a missing semicolon while we're at it. Signed-off-by: David Lechner Reviewed-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/triggered-buffers.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/driver-api/iio/triggered-buffers.rst b/Documentation/driver-api/iio/triggered-buffers.rst index 23b82357eba6..23762b06fdc6 100644 --- a/Documentation/driver-api/iio/triggered-buffers.rst +++ b/Documentation/driver-api/iio/triggered-buffers.rst @@ -29,14 +29,14 @@ A typical triggered buffer setup looks like this:: irqreturn_t sensor_trigger_handler(int irq, void *p) { - u16 buf[8]; + IIO_DECLARE_BUFFER_WITH_TS(u16, buf, 3) = { }; int i = 0; /* read data for each active channel */ - for_each_set_bit(bit, active_scan_mask, masklength) - buf[i++] = sensor_get_data(bit) + iio_for_each_active_channel(indio_dev, bit) + buf[i++] = sensor_get_data(bit); - iio_push_to_buffers_with_timestamp(indio_dev, buf, timestamp); + iio_push_to_buffers_with_ts(indio_dev, buf, sizeof(buf), timestamp); iio_trigger_notify_done(trigger); return IRQ_HANDLED; From 97caa67b1c68674cef84b43cba0bd7973b401240 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 May 2026 12:29:35 -0500 Subject: [PATCH 2364/5214] iio: common: ssp: remove SSP_CHAN_TIMESTAMP() macro Remove the SSP_CHAN_TIMESTAMP() macro and replace users with the IIO_CHAN_SOFT_TIMESTAMP() macro. The SSP_CHAN_TIMESTAMP() macro is identical to the IIO_CHAN_SOFT_TIMESTAMP() macro, so we don't need a separate macro for it. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/ssp_accel_sensor.c | 2 +- drivers/iio/common/ssp_sensors/ssp_iio_sensor.h | 12 ------------ drivers/iio/gyro/ssp_gyro_sensor.c | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 3e572af2ec03..d1687cdd33ea 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -76,7 +76,7 @@ static const struct iio_chan_spec ssp_acc_channels[] = { SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X), SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y), SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z), - SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME), + IIO_CHAN_SOFT_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME), }; static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf, diff --git a/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h index 4528ab55eb68..05fcad61c848 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h +++ b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h @@ -18,18 +18,6 @@ },\ } -/* It is defined here as it is a mixed timestamp */ -#define SSP_CHAN_TIMESTAMP(_si) { \ - .type = IIO_TIMESTAMP, \ - .channel = -1, \ - .scan_index = _si, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 64, \ - .storagebits = 64, \ - }, \ -} - #define SSP_MS_PER_S 1000 #define SSP_INVERTED_SCALING_FACTOR 1000000U diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index d9b41cf8d799..1acbbc1eeec3 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -76,7 +76,7 @@ static const struct iio_chan_spec ssp_gyro_channels[] = { SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X), SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y), SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z), - SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME), + IIO_CHAN_SOFT_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME), }; static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf, From e50856dc41e8353237863d618a55d7496072c325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:04 +0200 Subject: [PATCH 2365/5214] iio: accel: bmc150: Explicitly set .driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is one id entry that has an explicit assignment to .driver_data. To make the intention clearer, assign BOSCH_UNKNOWN (which is also 0) for all previously ids that had .driver_data = 0 implicitly before. While touching all entries in this array, convert to named initializers. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-i2c.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index b4604f441553..3315172742d0 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -245,16 +245,16 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct i2c_device_id bmc150_accel_id[] = { - {"bma222"}, - {"bma222e"}, - {"bma250e"}, - {"bma253"}, - {"bma254"}, - {"bma255"}, - {"bma280"}, - {"bmc150_accel"}, - {"bmc156_accel", BOSCH_BMC156}, - {"bmi055_accel"}, + { .name = "bma222", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma222e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma250e", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma253", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma254", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma255", .driver_data = BOSCH_UNKNOWN }, + { .name = "bma280", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc150_accel", .driver_data = BOSCH_UNKNOWN }, + { .name = "bmc156_accel", .driver_data = BOSCH_BMC156 }, + { .name = "bmi055_accel", .driver_data = BOSCH_UNKNOWN }, { } }; From abd69c09a73bb9a74f899526b4c7a60169dcbb27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:05 +0200 Subject: [PATCH 2366/5214] iio: adc: ad7091r5: Simplify driver_data handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver only supports a single device type. So the return value of i2c_get_match_data() is already known and can just be hardcoded without type casting. While at it also remove the unused .data of the single of_device_id entry. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7091r5.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index bd4877268689..7bf7d538abd9 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -101,23 +101,17 @@ static const struct ad7091r_init_info ad7091r5_init_info = { static int ad7091r5_i2c_probe(struct i2c_client *i2c) { - const struct ad7091r_init_info *init_info; - - init_info = i2c_get_match_data(i2c); - if (!init_info) - return -EINVAL; - - return ad7091r_probe(&i2c->dev, init_info, i2c->irq); + return ad7091r_probe(&i2c->dev, &ad7091r5_init_info, i2c->irq); } static const struct of_device_id ad7091r5_dt_ids[] = { - { .compatible = "adi,ad7091r5", .data = &ad7091r5_init_info }, + { .compatible = "adi,ad7091r5" }, { } }; MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); static const struct i2c_device_id ad7091r5_i2c_ids[] = { - { "ad7091r5", (kernel_ulong_t)&ad7091r5_init_info }, + { .name = "ad7091r5" }, { } }; MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); From a7ee2d07013bd369285a0b87f219bd81a1f103ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:06 +0200 Subject: [PATCH 2367/5214] iio: dac: max5821: Drop unused i2c driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the single i2c_device_id array entry is unused in the driver. Drop it and also the then unused definition. While at it, convert the i2c_device_id array to a better readable named initializer. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5821.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index e7e29359f8fe..f3ebbdff5487 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -26,10 +26,6 @@ #define MAX5821_EXTENDED_DAC_A 0x04 #define MAX5821_EXTENDED_DAC_B 0x08 -enum max5821_device_ids { - ID_MAX5821, -}; - struct max5821_data { struct i2c_client *client; unsigned short vref_mv; @@ -332,7 +328,7 @@ static int max5821_probe(struct i2c_client *client) } static const struct i2c_device_id max5821_id[] = { - { "max5821", ID_MAX5821 }, + { .name = "max5821" }, { } }; MODULE_DEVICE_TABLE(i2c, max5821_id); From eb3fdf3d9bcc555a535dfa771129ad048abf042e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:07 +0200 Subject: [PATCH 2368/5214] iio: proximity: sx9324: Drop unused driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value assigned in the three device_id entries (ACPI, of and i2c) are unused, directly in the driver and also the sx_common support lib. Drop them and while touching these arrays, convert to named initializers. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9324.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index f61eff39751d..36c45d101336 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -1123,19 +1123,19 @@ static int sx9324_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); static const struct acpi_device_id sx9324_acpi_match[] = { - { "STH9324", SX9324_WHOAMI_VALUE }, + { .id = "STH9324" }, { } }; MODULE_DEVICE_TABLE(acpi, sx9324_acpi_match); static const struct of_device_id sx9324_of_match[] = { - { .compatible = "semtech,sx9324", (void *)SX9324_WHOAMI_VALUE }, + { .compatible = "semtech,sx9324" }, { } }; MODULE_DEVICE_TABLE(of, sx9324_of_match); static const struct i2c_device_id sx9324_id[] = { - { "sx9324", SX9324_WHOAMI_VALUE }, + { .name = "sx9324" }, { } }; MODULE_DEVICE_TABLE(i2c, sx9324_id); From ab233e4f9a9578faa30c1870f5436591d86c37b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:08 +0200 Subject: [PATCH 2369/5214] iio: proximity: sx9360: Drop unused driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value assigned in the three device_id entries (ACPI, of and i2c) are unused, directly in the driver and also the sx_common support lib. Drop them and while touching these arrays, convert to named initializers. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9360.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index 4448988d4e7e..4b9498022b22 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -832,20 +832,20 @@ static int sx9360_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); static const struct acpi_device_id sx9360_acpi_match[] = { - { "STH9360", SX9360_WHOAMI_VALUE }, - { "SAMM0208", SX9360_WHOAMI_VALUE }, + { .id = "STH9360" }, + { .id = "SAMM0208" }, { } }; MODULE_DEVICE_TABLE(acpi, sx9360_acpi_match); static const struct of_device_id sx9360_of_match[] = { - { .compatible = "semtech,sx9360", (void *)SX9360_WHOAMI_VALUE }, + { .compatible = "semtech,sx9360" }, { } }; MODULE_DEVICE_TABLE(of, sx9360_of_match); static const struct i2c_device_id sx9360_id[] = { - {"sx9360", SX9360_WHOAMI_VALUE }, + { .name = "sx9360" }, { } }; MODULE_DEVICE_TABLE(i2c, sx9360_id); From c4657858d43ada628e2f4eaf591c43efb3649933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 16:06:20 +0200 Subject: [PATCH 2370/5214] staging: iio: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Stepan Ionichev Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316-i2c.c | 12 ++++++------ drivers/staging/iio/impedance-analyzer/ad5933.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c index 3bdaee925dee..98f2e23c6a4b 100644 --- a/drivers/staging/iio/addac/adt7316-i2c.c +++ b/drivers/staging/iio/addac/adt7316-i2c.c @@ -109,12 +109,12 @@ static int adt7316_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adt7316_i2c_id[] = { - { "adt7316" }, - { "adt7317" }, - { "adt7318" }, - { "adt7516" }, - { "adt7517" }, - { "adt7519" }, + { .name = "adt7316" }, + { .name = "adt7317" }, + { .name = "adt7318" }, + { .name = "adt7516" }, + { .name = "adt7517" }, + { .name = "adt7519" }, { } }; diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index dde2ec9d1f6a..798e62bae29b 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -726,8 +726,8 @@ static int ad5933_probe(struct i2c_client *client) } static const struct i2c_device_id ad5933_id[] = { - { "ad5933" }, - { "ad5934" }, + { .name = "ad5933" }, + { .name = "ad5934" }, { } }; From f68afce8e8a74f52a879f56f607dfedf552b5ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 10:13:09 +0200 Subject: [PATCH 2371/5214] iio: Initialize i2c_device_id arrays using member names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Siratul Islam Reviewed-by: Matti Vaittinen Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_i2c.c | 4 +- drivers/iio/accel/adxl355_i2c.c | 4 +- drivers/iio/accel/adxl367_i2c.c | 2 +- drivers/iio/accel/adxl372_i2c.c | 4 +- drivers/iio/accel/adxl380_i2c.c | 8 +- drivers/iio/accel/bma180.c | 10 +- drivers/iio/accel/bma220_i2c.c | 2 +- drivers/iio/accel/bma400_i2c.c | 2 +- drivers/iio/accel/bmi088-accel-i2c.c | 6 +- drivers/iio/accel/da280.c | 6 +- drivers/iio/accel/da311.c | 2 +- drivers/iio/accel/dmard06.c | 6 +- drivers/iio/accel/dmard09.c | 2 +- drivers/iio/accel/dmard10.c | 2 +- drivers/iio/accel/fxls8962af-i2c.c | 8 +- drivers/iio/accel/kxcjk-1013.c | 10 +- drivers/iio/accel/kxsd9-i2c.c | 2 +- drivers/iio/accel/mc3230.c | 4 +- drivers/iio/accel/mma7455_i2c.c | 4 +- drivers/iio/accel/mma7660.c | 2 +- drivers/iio/accel/mma8452.c | 12 +-- drivers/iio/accel/mma9551.c | 2 +- drivers/iio/accel/mma9553.c | 2 +- drivers/iio/accel/mxc4005.c | 4 +- drivers/iio/accel/mxc6255.c | 4 +- drivers/iio/accel/st_accel_i2c.c | 52 +++++----- drivers/iio/accel/stk8312.c | 4 +- drivers/iio/accel/stk8ba50.c | 2 +- drivers/iio/adc/ad7291.c | 2 +- drivers/iio/adc/ad799x.c | 16 ++-- drivers/iio/adc/gehc-pmc-adc.c | 2 +- drivers/iio/adc/ina2xx-adc.c | 12 +-- drivers/iio/adc/ltc2309.c | 4 +- drivers/iio/adc/ltc2471.c | 4 +- drivers/iio/adc/ltc2485.c | 2 +- drivers/iio/adc/ltc2497.c | 4 +- drivers/iio/adc/max34408.c | 4 +- drivers/iio/adc/mcp3422.c | 16 ++-- drivers/iio/adc/nau7802.c | 2 +- drivers/iio/adc/rohm-bd79124.c | 2 +- drivers/iio/adc/ti-adc081c.c | 6 +- drivers/iio/adc/ti-ads1015.c | 6 +- drivers/iio/adc/ti-ads1100.c | 4 +- drivers/iio/adc/ti-ads1119.c | 2 +- drivers/iio/adc/ti-ads7138.c | 4 +- drivers/iio/adc/ti-ads7924.c | 2 +- drivers/iio/cdc/ad7150.c | 6 +- drivers/iio/cdc/ad7746.c | 6 +- drivers/iio/chemical/ags02ma.c | 2 +- drivers/iio/chemical/ams-iaq-core.c | 2 +- drivers/iio/chemical/atlas-ezo-sensor.c | 6 +- drivers/iio/chemical/atlas-sensor.c | 10 +- drivers/iio/chemical/bme680_i2c.c | 2 +- drivers/iio/chemical/ccs811.c | 4 +- drivers/iio/chemical/ens160_i2c.c | 2 +- drivers/iio/chemical/sgp30.c | 4 +- drivers/iio/chemical/sgp40.c | 2 +- drivers/iio/chemical/sps30_i2c.c | 2 +- drivers/iio/chemical/vz89x.c | 4 +- drivers/iio/dac/ad5064.c | 94 +++++++++---------- drivers/iio/dac/ad5380.c | 32 +++---- drivers/iio/dac/ad5446-i2c.c | 12 +-- drivers/iio/dac/ad5696-i2c.c | 32 +++---- drivers/iio/dac/ds4424.c | 8 +- drivers/iio/dac/m62332.c | 2 +- drivers/iio/dac/max517.c | 10 +- drivers/iio/dac/mcp4725.c | 4 +- drivers/iio/dac/mcp4728.c | 2 +- drivers/iio/dac/mcp47feb02.c | 48 +++++----- drivers/iio/dac/ti-dac5571.c | 22 ++--- drivers/iio/gyro/bmg160_i2c.c | 6 +- drivers/iio/gyro/fxas21002c_i2c.c | 2 +- drivers/iio/gyro/itg3200_core.c | 2 +- drivers/iio/gyro/mpu3050-i2c.c | 2 +- drivers/iio/gyro/st_gyro_i2c.c | 18 ++-- drivers/iio/health/afe4404.c | 2 +- drivers/iio/health/max30100.c | 2 +- drivers/iio/health/max30102.c | 6 +- drivers/iio/humidity/am2315.c | 2 +- drivers/iio/humidity/ens210.c | 12 +-- drivers/iio/humidity/hdc100x.c | 12 +-- drivers/iio/humidity/hdc2010.c | 4 +- drivers/iio/humidity/hdc3020.c | 6 +- drivers/iio/humidity/hts221_i2c.c | 2 +- drivers/iio/humidity/htu21.c | 4 +- drivers/iio/humidity/si7005.c | 4 +- drivers/iio/humidity/si7020.c | 4 +- drivers/iio/imu/bmi160/bmi160_i2c.c | 4 +- drivers/iio/imu/bmi270/bmi270_i2c.c | 4 +- drivers/iio/imu/bmi323/bmi323_i2c.c | 2 +- drivers/iio/imu/bno055/bno055_i2c.c | 2 +- drivers/iio/imu/fxos8700_i2c.c | 2 +- .../iio/imu/inv_icm42600/inv_icm42600_i2c.c | 14 +-- .../iio/imu/inv_icm45600/inv_icm45600_i2c.c | 16 ++-- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 36 +++---- drivers/iio/imu/kmx61.c | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 48 +++++----- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c | 4 +- drivers/iio/light/adjd_s311.c | 2 +- drivers/iio/light/adux1020.c | 2 +- drivers/iio/light/al3000a.c | 2 +- drivers/iio/light/al3010.c | 2 +- drivers/iio/light/al3320a.c | 2 +- drivers/iio/light/apds9300.c | 2 +- drivers/iio/light/apds9960.c | 2 +- drivers/iio/light/as73211.c | 4 +- drivers/iio/light/bh1745.c | 2 +- drivers/iio/light/bh1750.c | 10 +- drivers/iio/light/bh1780.c | 2 +- drivers/iio/light/cm3232.c | 2 +- drivers/iio/light/cm3323.c | 2 +- drivers/iio/light/cm36651.c | 2 +- drivers/iio/light/gp2ap002.c | 2 +- drivers/iio/light/gp2ap020a00f.c | 2 +- drivers/iio/light/isl29018.c | 6 +- drivers/iio/light/isl29028.c | 4 +- drivers/iio/light/isl29125.c | 2 +- drivers/iio/light/isl76682.c | 2 +- drivers/iio/light/jsa1212.c | 2 +- drivers/iio/light/ltr390.c | 2 +- drivers/iio/light/ltr501.c | 8 +- drivers/iio/light/ltrf216a.c | 4 +- drivers/iio/light/lv0104cs.c | 2 +- drivers/iio/light/max44000.c | 2 +- drivers/iio/light/max44009.c | 2 +- drivers/iio/light/noa1305.c | 2 +- drivers/iio/light/opt3001.c | 4 +- drivers/iio/light/opt4001.c | 4 +- drivers/iio/light/opt4060.c | 2 +- drivers/iio/light/pa12203001.c | 2 +- drivers/iio/light/rpr0521.c | 2 +- drivers/iio/light/si1133.c | 2 +- drivers/iio/light/si1145.c | 14 +-- drivers/iio/light/st_uvis25_i2c.c | 2 +- drivers/iio/light/stk3310.c | 8 +- drivers/iio/light/tcs3414.c | 2 +- drivers/iio/light/tcs3472.c | 2 +- drivers/iio/light/tsl2772.c | 26 ++--- drivers/iio/light/tsl4531.c | 2 +- drivers/iio/light/us5182d.c | 2 +- drivers/iio/light/vcnl4000.c | 14 +-- drivers/iio/light/vcnl4035.c | 2 +- drivers/iio/light/veml3235.c | 2 +- drivers/iio/light/veml6030.c | 6 +- drivers/iio/light/veml6040.c | 2 +- drivers/iio/light/veml6046x00.c | 2 +- drivers/iio/light/veml6070.c | 2 +- drivers/iio/light/veml6075.c | 2 +- drivers/iio/light/vl6180.c | 2 +- drivers/iio/light/zopt2201.c | 2 +- drivers/iio/magnetometer/af8133j.c | 2 +- drivers/iio/magnetometer/ak8974.c | 8 +- drivers/iio/magnetometer/ak8975.c | 14 +-- drivers/iio/magnetometer/bmc150_magn_i2c.c | 6 +- drivers/iio/magnetometer/hmc5843_i2c.c | 8 +- drivers/iio/magnetometer/mag3110.c | 2 +- drivers/iio/magnetometer/mmc35240.c | 2 +- drivers/iio/magnetometer/mmc5633.c | 4 +- drivers/iio/magnetometer/si7210.c | 2 +- drivers/iio/magnetometer/st_magn_i2c.c | 18 ++-- drivers/iio/magnetometer/tlv493d.c | 2 +- drivers/iio/magnetometer/tmag5273.c | 2 +- drivers/iio/magnetometer/yamaha-yas530.c | 8 +- drivers/iio/potentiometer/ad5272.c | 10 +- drivers/iio/potentiometer/ds1803.c | 8 +- drivers/iio/potentiometer/tpl0102.c | 8 +- drivers/iio/potentiostat/lmp91000.c | 4 +- drivers/iio/pressure/abp060mg.c | 90 +++++++++++------- drivers/iio/pressure/abp2030pa_i2c.c | 2 +- drivers/iio/pressure/adp810.c | 2 +- drivers/iio/pressure/bmp280-i2c.c | 12 +-- drivers/iio/pressure/dlhl60d.c | 4 +- drivers/iio/pressure/dps310.c | 2 +- drivers/iio/pressure/hp03.c | 2 +- drivers/iio/pressure/hp206c.c | 2 +- drivers/iio/pressure/hsc030pa_i2c.c | 2 +- drivers/iio/pressure/icp10100.c | 2 +- drivers/iio/pressure/mpl115_i2c.c | 2 +- drivers/iio/pressure/mpl3115.c | 2 +- drivers/iio/pressure/mprls0025pa_i2c.c | 2 +- drivers/iio/pressure/ms5611_i2c.c | 4 +- drivers/iio/pressure/ms5637.c | 8 +- drivers/iio/pressure/rohm-bm1390.c | 2 +- drivers/iio/pressure/sdp500.c | 2 +- drivers/iio/pressure/st_pressure_i2c.c | 16 ++-- drivers/iio/pressure/t5403.c | 2 +- drivers/iio/pressure/zpa2326_i2c.c | 2 +- drivers/iio/proximity/aw96103.c | 4 +- drivers/iio/proximity/hx9023s.c | 2 +- drivers/iio/proximity/isl29501.c | 2 +- drivers/iio/proximity/mb1232.c | 14 +-- .../iio/proximity/pulsedlight-lidar-lite-v2.c | 4 +- drivers/iio/proximity/rfd77402.c | 2 +- drivers/iio/proximity/srf08.c | 6 +- drivers/iio/proximity/sx9310.c | 4 +- drivers/iio/proximity/sx9500.c | 2 +- drivers/iio/proximity/vl53l0x-i2c.c | 2 +- drivers/iio/proximity/vl53l1x-i2c.c | 2 +- drivers/iio/temperature/max30208.c | 2 +- drivers/iio/temperature/mcp9600.c | 4 +- drivers/iio/temperature/mlx90614.c | 4 +- drivers/iio/temperature/mlx90632.c | 2 +- drivers/iio/temperature/mlx90635.c | 2 +- drivers/iio/temperature/tmp006.c | 2 +- drivers/iio/temperature/tmp007.c | 2 +- drivers/iio/temperature/tmp117.c | 4 +- drivers/iio/temperature/tsys01.c | 2 +- drivers/iio/temperature/tsys02d.c | 2 +- 208 files changed, 680 insertions(+), 658 deletions(-) diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index af84c0043c6c..511fb610436c 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -43,8 +43,8 @@ static const struct adxl345_chip_info adxl375_i2c_info = { }; static const struct i2c_device_id adxl345_i2c_id[] = { - { "adxl345", (kernel_ulong_t)&adxl345_i2c_info }, - { "adxl375", (kernel_ulong_t)&adxl375_i2c_info }, + { .name = "adxl345", .driver_data = (kernel_ulong_t)&adxl345_i2c_info }, + { .name = "adxl375", .driver_data = (kernel_ulong_t)&adxl375_i2c_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c index 19490a6e1f35..0b6b016bd358 100644 --- a/drivers/iio/accel/adxl355_i2c.c +++ b/drivers/iio/accel/adxl355_i2c.c @@ -38,8 +38,8 @@ static int adxl355_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl355_i2c_id[] = { - { "adxl355", (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, - { "adxl359", (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, + { .name = "adxl355", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL355] }, + { .name = "adxl359", .driver_data = (kernel_ulong_t)&adxl35x_chip_info[ADXL359] }, { } }; MODULE_DEVICE_TABLE(i2c, adxl355_i2c_id); diff --git a/drivers/iio/accel/adxl367_i2c.c b/drivers/iio/accel/adxl367_i2c.c index 1c7d2eb054a2..fb50ded68bae 100644 --- a/drivers/iio/accel/adxl367_i2c.c +++ b/drivers/iio/accel/adxl367_i2c.c @@ -61,7 +61,7 @@ static int adxl367_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl367_i2c_id[] = { - { "adxl367" }, + { .name = "adxl367" }, { } }; MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id); diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index ca2cabf24938..ddb125075778 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -44,8 +44,8 @@ static int adxl372_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl372_i2c_id[] = { - { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, - { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, + { .name = "adxl371", .driver_data = (kernel_ulong_t)&adxl371_chip_info }, + { .name = "adxl372", .driver_data = (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); diff --git a/drivers/iio/accel/adxl380_i2c.c b/drivers/iio/accel/adxl380_i2c.c index bd8782d08c7d..367a29298047 100644 --- a/drivers/iio/accel/adxl380_i2c.c +++ b/drivers/iio/accel/adxl380_i2c.c @@ -33,10 +33,10 @@ static int adxl380_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adxl380_i2c_id[] = { - { "adxl318", (kernel_ulong_t)&adxl318_chip_info }, - { "adxl319", (kernel_ulong_t)&adxl319_chip_info }, - { "adxl380", (kernel_ulong_t)&adxl380_chip_info }, - { "adxl382", (kernel_ulong_t)&adxl382_chip_info }, + { .name = "adxl318", .driver_data = (kernel_ulong_t)&adxl318_chip_info }, + { .name = "adxl319", .driver_data = (kernel_ulong_t)&adxl319_chip_info }, + { .name = "adxl380", .driver_data = (kernel_ulong_t)&adxl380_chip_info }, + { .name = "adxl382", .driver_data = (kernel_ulong_t)&adxl382_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl380_i2c_id); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 7bc6761f5135..e643bc73eefe 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -1083,11 +1083,11 @@ static int bma180_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); static const struct i2c_device_id bma180_ids[] = { - { "bma023", (kernel_ulong_t)&bma180_part_info[BMA023] }, - { "bma150", (kernel_ulong_t)&bma180_part_info[BMA150] }, - { "bma180", (kernel_ulong_t)&bma180_part_info[BMA180] }, - { "bma250", (kernel_ulong_t)&bma180_part_info[BMA250] }, - { "smb380", (kernel_ulong_t)&bma180_part_info[BMA150] }, + { .name = "bma023", .driver_data = (kernel_ulong_t)&bma180_part_info[BMA023] }, + { .name = "bma150", .driver_data = (kernel_ulong_t)&bma180_part_info[BMA150] }, + { .name = "bma180", .driver_data = (kernel_ulong_t)&bma180_part_info[BMA180] }, + { .name = "bma250", .driver_data = (kernel_ulong_t)&bma180_part_info[BMA250] }, + { .name = "smb380", .driver_data = (kernel_ulong_t)&bma180_part_info[BMA150] }, { } }; diff --git a/drivers/iio/accel/bma220_i2c.c b/drivers/iio/accel/bma220_i2c.c index 8b6f8e305c8c..b058e97bc6a6 100644 --- a/drivers/iio/accel/bma220_i2c.c +++ b/drivers/iio/accel/bma220_i2c.c @@ -47,7 +47,7 @@ static const struct of_device_id bma220_i2c_match[] = { MODULE_DEVICE_TABLE(of, bma220_i2c_match); static const struct i2c_device_id bma220_i2c_id[] = { - { "bma220" }, + { .name = "bma220" }, { } }; MODULE_DEVICE_TABLE(i2c, bma220_i2c_id); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c index 24a390c3ae66..23d394c5a791 100644 --- a/drivers/iio/accel/bma400_i2c.c +++ b/drivers/iio/accel/bma400_i2c.c @@ -28,7 +28,7 @@ static int bma400_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id bma400_i2c_ids[] = { - { "bma400" }, + { .name = "bma400" }, { } }; MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids); diff --git a/drivers/iio/accel/bmi088-accel-i2c.c b/drivers/iio/accel/bmi088-accel-i2c.c index 310f863029bb..d9468b1c5aee 100644 --- a/drivers/iio/accel/bmi088-accel-i2c.c +++ b/drivers/iio/accel/bmi088-accel-i2c.c @@ -45,9 +45,9 @@ static const struct of_device_id bmi088_of_match[] = { MODULE_DEVICE_TABLE(of, bmi088_of_match); static const struct i2c_device_id bmi088_accel_id[] = { - { "bmi085-accel", BOSCH_BMI085 }, - { "bmi088-accel", BOSCH_BMI088 }, - { "bmi090l-accel", BOSCH_BMI090L }, + { .name = "bmi085-accel", .driver_data = BOSCH_BMI085 }, + { .name = "bmi088-accel", .driver_data = BOSCH_BMI088 }, + { .name = "bmi090l-accel", .driver_data = BOSCH_BMI090L }, { } }; MODULE_DEVICE_TABLE(i2c, bmi088_accel_id); diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index c2dd123b9021..15acbe1c2cbd 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -162,9 +162,9 @@ static const struct acpi_device_id da280_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, da280_acpi_match); static const struct i2c_device_id da280_i2c_id[] = { - { "da217", (kernel_ulong_t)&da217_match_data }, - { "da226", (kernel_ulong_t)&da226_match_data }, - { "da280", (kernel_ulong_t)&da280_match_data }, + { .name = "da217", .driver_data = (kernel_ulong_t)&da217_match_data }, + { .name = "da226", .driver_data = (kernel_ulong_t)&da226_match_data }, + { .name = "da280", .driver_data = (kernel_ulong_t)&da280_match_data }, { } }; MODULE_DEVICE_TABLE(i2c, da280_i2c_id); diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index e1df7b009d89..dcb69c8eaf09 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -268,7 +268,7 @@ static int da311_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); static const struct i2c_device_id da311_i2c_id[] = { - { "da311" }, + { .name = "da311" }, { } }; MODULE_DEVICE_TABLE(i2c, da311_i2c_id); diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c index 33f225d73e7b..2957bf55d110 100644 --- a/drivers/iio/accel/dmard06.c +++ b/drivers/iio/accel/dmard06.c @@ -199,9 +199,9 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); static const struct i2c_device_id dmard06_id[] = { - { "dmard05" }, - { "dmard06" }, - { "dmard07" }, + { .name = "dmard05" }, + { .name = "dmard06" }, + { .name = "dmard07" }, { } }; MODULE_DEVICE_TABLE(i2c, dmard06_id); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c index d9290e3b9c46..fe35a1270786 100644 --- a/drivers/iio/accel/dmard09.c +++ b/drivers/iio/accel/dmard09.c @@ -123,7 +123,7 @@ static int dmard09_probe(struct i2c_client *client) } static const struct i2c_device_id dmard09_id[] = { - { "dmard09" }, + { .name = "dmard09" }, { } }; diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index 575e8510e1bd..8ba00fd57901 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -229,7 +229,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume); static const struct i2c_device_id dmard10_i2c_id[] = { - { "dmard10" }, + { .name = "dmard10" }, { } }; MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id); diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c index 106198a12474..fa46f5aa34f7 100644 --- a/drivers/iio/accel/fxls8962af-i2c.c +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -28,10 +28,10 @@ static int fxls8962af_probe(struct i2c_client *client) } static const struct i2c_device_id fxls8962af_id[] = { - { "fxls8962af", fxls8962af }, - { "fxls8964af", fxls8964af }, - { "fxls8967af", fxls8967af }, - { "fxls8974cf", fxls8974cf }, + { .name = "fxls8962af", .driver_data = fxls8962af }, + { .name = "fxls8964af", .driver_data = fxls8964af }, + { .name = "fxls8967af", .driver_data = fxls8967af }, + { .name = "fxls8974cf", .driver_data = fxls8974cf }, { } }; MODULE_DEVICE_TABLE(i2c, fxls8962af_id); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 2823ddde4bf2..8a082ff034dd 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1630,11 +1630,11 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = { }; static const struct i2c_device_id kxcjk1013_id[] = { - { "kxcjk1013", (kernel_ulong_t)&kxcjk1013_info }, - { "kxcj91008", (kernel_ulong_t)&kxcj91008_info }, - { "kxtj21009", (kernel_ulong_t)&kxtj21009_info }, - { "kxtf9", (kernel_ulong_t)&kxtf9_info }, - { "kx023-1025", (kernel_ulong_t)&kx0231025_info }, + { .name = "kxcjk1013", .driver_data = (kernel_ulong_t)&kxcjk1013_info }, + { .name = "kxcj91008", .driver_data = (kernel_ulong_t)&kxcj91008_info }, + { .name = "kxtj21009", .driver_data = (kernel_ulong_t)&kxtj21009_info }, + { .name = "kxtf9", .driver_data = (kernel_ulong_t)&kxtf9_info }, + { .name = "kx023-1025", .driver_data = (kernel_ulong_t)&kx0231025_info }, { } }; MODULE_DEVICE_TABLE(i2c, kxcjk1013_id); diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 1fa88b99149e..8f3314db82d2 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -43,7 +43,7 @@ static const struct of_device_id kxsd9_of_match[] = { MODULE_DEVICE_TABLE(of, kxsd9_of_match); static const struct i2c_device_id kxsd9_i2c_id[] = { - { "kxsd9" }, + { .name = "kxsd9" }, { } }; MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id); diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index 3e494f9ddc56..b45897210a62 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -230,8 +230,8 @@ static int mc3230_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); static const struct i2c_device_id mc3230_i2c_id[] = { - { "mc3230", (kernel_ulong_t)&mc3230_chip_info }, - { "mc3510c", (kernel_ulong_t)&mc3510c_chip_info }, + { .name = "mc3230", .driver_data = (kernel_ulong_t)&mc3230_chip_info }, + { .name = "mc3510c", .driver_data = (kernel_ulong_t)&mc3510c_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c index 2ff8eb1f9ce9..9588fd133b1e 100644 --- a/drivers/iio/accel/mma7455_i2c.c +++ b/drivers/iio/accel/mma7455_i2c.c @@ -32,8 +32,8 @@ static void mma7455_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id mma7455_i2c_ids[] = { - { "mma7455" }, - { "mma7456" }, + { .name = "mma7455" }, + { .name = "mma7456" }, { } }; MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids); diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index be3213600cf4..0ecf6c06dcc6 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -259,7 +259,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); static const struct i2c_device_id mma7660_i2c_id[] = { - { "mma7660" }, + { .name = "mma7660" }, { } }; MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index b49949792190..1403b32e2b21 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1829,12 +1829,12 @@ static const struct dev_pm_ops mma8452_pm_ops = { }; static const struct i2c_device_id mma8452_id[] = { - { "fxls8471", (kernel_ulong_t)&mma_chip_info_table[fxls8471] }, - { "mma8451", (kernel_ulong_t)&mma_chip_info_table[mma8451] }, - { "mma8452", (kernel_ulong_t)&mma_chip_info_table[mma8452] }, - { "mma8453", (kernel_ulong_t)&mma_chip_info_table[mma8453] }, - { "mma8652", (kernel_ulong_t)&mma_chip_info_table[mma8652] }, - { "mma8653", (kernel_ulong_t)&mma_chip_info_table[mma8653] }, + { .name = "fxls8471", .driver_data = (kernel_ulong_t)&mma_chip_info_table[fxls8471] }, + { .name = "mma8451", .driver_data = (kernel_ulong_t)&mma_chip_info_table[mma8451] }, + { .name = "mma8452", .driver_data = (kernel_ulong_t)&mma_chip_info_table[mma8452] }, + { .name = "mma8453", .driver_data = (kernel_ulong_t)&mma_chip_info_table[mma8453] }, + { .name = "mma8652", .driver_data = (kernel_ulong_t)&mma_chip_info_table[mma8652] }, + { .name = "mma8653", .driver_data = (kernel_ulong_t)&mma_chip_info_table[mma8653] }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 02195deada49..020370b0ec07 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -582,7 +582,7 @@ static const struct acpi_device_id mma9551_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match); static const struct i2c_device_id mma9551_id[] = { - { "mma9551" }, + { .name = "mma9551" }, { } }; diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index f85384a7908f..90ce86244ee8 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1219,7 +1219,7 @@ static const struct acpi_device_id mma9553_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match); static const struct i2c_device_id mma9553_id[] = { - { "mma9553" }, + { .name = "mma9553" }, { } }; diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index a2c3cf13d098..434971fbfb12 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -580,8 +580,8 @@ static const struct of_device_id mxc4005_of_match[] = { MODULE_DEVICE_TABLE(of, mxc4005_of_match); static const struct i2c_device_id mxc4005_id[] = { - { "mxc4005" }, - { "mxc6655" }, + { .name = "mxc4005" }, + { .name = "mxc6655" }, { } }; MODULE_DEVICE_TABLE(i2c, mxc4005_id); diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c index fc3ed84d1933..901f2b9f16a2 100644 --- a/drivers/iio/accel/mxc6255.c +++ b/drivers/iio/accel/mxc6255.c @@ -171,8 +171,8 @@ static const struct acpi_device_id mxc6255_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match); static const struct i2c_device_id mxc6255_id[] = { - { "mxc6225" }, - { "mxc6255" }, + { .name = "mxc6225" }, + { .name = "mxc6255" }, { } }; MODULE_DEVICE_TABLE(i2c, mxc6255_id); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index f24449500533..eecc7fcdb06e 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -138,32 +138,32 @@ static const struct acpi_device_id st_accel_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); static const struct i2c_device_id st_accel_id_table[] = { - { LSM303DLH_ACCEL_DEV_NAME }, - { LSM303DLHC_ACCEL_DEV_NAME }, - { LIS3DH_ACCEL_DEV_NAME }, - { LSM330D_ACCEL_DEV_NAME }, - { LSM330DL_ACCEL_DEV_NAME }, - { LSM330DLC_ACCEL_DEV_NAME }, - { LIS331DLH_ACCEL_DEV_NAME }, - { LSM303DL_ACCEL_DEV_NAME }, - { LSM303DLM_ACCEL_DEV_NAME }, - { LSM330_ACCEL_DEV_NAME }, - { LSM303AGR_ACCEL_DEV_NAME }, - { LIS2DH12_ACCEL_DEV_NAME }, - { LIS3L02DQ_ACCEL_DEV_NAME }, - { LNG2DM_ACCEL_DEV_NAME }, - { H3LIS331DL_ACCEL_DEV_NAME }, - { LIS331DL_ACCEL_DEV_NAME }, - { LIS3LV02DL_ACCEL_DEV_NAME }, - { LIS2DW12_ACCEL_DEV_NAME }, - { LIS3DE_ACCEL_DEV_NAME }, - { LIS2DE12_ACCEL_DEV_NAME }, - { LIS2DS12_ACCEL_DEV_NAME }, - { LIS2HH12_ACCEL_DEV_NAME }, - { LIS302DL_ACCEL_DEV_NAME }, - { LSM303C_ACCEL_DEV_NAME }, - { SC7A20_ACCEL_DEV_NAME }, - { IIS328DQ_ACCEL_DEV_NAME }, + { .name = LSM303DLH_ACCEL_DEV_NAME }, + { .name = LSM303DLHC_ACCEL_DEV_NAME }, + { .name = LIS3DH_ACCEL_DEV_NAME }, + { .name = LSM330D_ACCEL_DEV_NAME }, + { .name = LSM330DL_ACCEL_DEV_NAME }, + { .name = LSM330DLC_ACCEL_DEV_NAME }, + { .name = LIS331DLH_ACCEL_DEV_NAME }, + { .name = LSM303DL_ACCEL_DEV_NAME }, + { .name = LSM303DLM_ACCEL_DEV_NAME }, + { .name = LSM330_ACCEL_DEV_NAME }, + { .name = LSM303AGR_ACCEL_DEV_NAME }, + { .name = LIS2DH12_ACCEL_DEV_NAME }, + { .name = LIS3L02DQ_ACCEL_DEV_NAME }, + { .name = LNG2DM_ACCEL_DEV_NAME }, + { .name = H3LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS331DL_ACCEL_DEV_NAME }, + { .name = LIS3LV02DL_ACCEL_DEV_NAME }, + { .name = LIS2DW12_ACCEL_DEV_NAME }, + { .name = LIS3DE_ACCEL_DEV_NAME }, + { .name = LIS2DE12_ACCEL_DEV_NAME }, + { .name = LIS2DS12_ACCEL_DEV_NAME }, + { .name = LIS2HH12_ACCEL_DEV_NAME }, + { .name = LIS302DL_ACCEL_DEV_NAME }, + { .name = LSM303C_ACCEL_DEV_NAME }, + { .name = SC7A20_ACCEL_DEV_NAME }, + { .name = IIS328DQ_ACCEL_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index f31c6ab3392d..8b5a3e0489e9 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -630,8 +630,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, static const struct i2c_device_id stk8312_i2c_id[] = { /* Deprecated in favour of lowercase form */ - { "STK8312" }, - { "stk8312" }, + { .name = "STK8312" }, + { .name = "stk8312" }, { } }; MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index a9ff2a273fe1..ccea1331cafc 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -519,7 +519,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume); static const struct i2c_device_id stk8ba50_i2c_id[] = { - { "stk8ba50" }, + { .name = "stk8ba50" }, { } }; MODULE_DEVICE_TABLE(i2c, stk8ba50_i2c_id); diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 60e12faa3207..6a1493e27a30 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -536,7 +536,7 @@ static int ad7291_probe(struct i2c_client *client) } static const struct i2c_device_id ad7291_id[] = { - { "ad7291" }, + { .name = "ad7291" }, { } }; diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 0ceedea5df0b..bb076d8f5dd5 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -940,14 +940,14 @@ static int ad799x_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume); static const struct i2c_device_id ad799x_id[] = { - { "ad7991", ad7991 }, - { "ad7995", ad7995 }, - { "ad7999", ad7999 }, - { "ad7992", ad7992 }, - { "ad7993", ad7993 }, - { "ad7994", ad7994 }, - { "ad7997", ad7997 }, - { "ad7998", ad7998 }, + { .name = "ad7991", .driver_data = ad7991 }, + { .name = "ad7995", .driver_data = ad7995 }, + { .name = "ad7999", .driver_data = ad7999 }, + { .name = "ad7992", .driver_data = ad7992 }, + { .name = "ad7993", .driver_data = ad7993 }, + { .name = "ad7994", .driver_data = ad7994 }, + { .name = "ad7997", .driver_data = ad7997 }, + { .name = "ad7998", .driver_data = ad7998 }, { } }; diff --git a/drivers/iio/adc/gehc-pmc-adc.c b/drivers/iio/adc/gehc-pmc-adc.c index d1167818b17d..24193e941494 100644 --- a/drivers/iio/adc/gehc-pmc-adc.c +++ b/drivers/iio/adc/gehc-pmc-adc.c @@ -207,7 +207,7 @@ static const struct of_device_id pmc_adc_of_match[] = { MODULE_DEVICE_TABLE(of, pmc_adc_of_match); static const struct i2c_device_id pmc_adc_id_table[] = { - { "pmc-adc" }, + { .name = "pmc-adc" }, { } }; MODULE_DEVICE_TABLE(i2c, pmc_adc_id_table); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 7c04e3ec1cbc..c412f5ed46a1 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -1078,12 +1078,12 @@ static void ina2xx_remove(struct i2c_client *client) } static const struct i2c_device_id ina2xx_id[] = { - { "ina219", ina219 }, - { "ina220", ina219 }, - { "ina226", ina226 }, - { "ina230", ina226 }, - { "ina231", ina226 }, - { "ina236", ina236 }, + { .name = "ina219", .driver_data = ina219 }, + { .name = "ina220", .driver_data = ina219 }, + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, + { .name = "ina236", .driver_data = ina236 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 87b78d0353f1..1ad533270a39 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -243,8 +243,8 @@ static const struct of_device_id ltc2309_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2309_of_match); static const struct i2c_device_id ltc2309_id[] = { - { "ltc2305", (kernel_ulong_t)<c2305_chip_info }, - { "ltc2309", (kernel_ulong_t)<c2309_chip_info }, + { .name = "ltc2305", .driver_data = (kernel_ulong_t)<c2305_chip_info }, + { .name = "ltc2309", .driver_data = (kernel_ulong_t)<c2309_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2309_id); diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c index a579107fd5c9..369af700969f 100644 --- a/drivers/iio/adc/ltc2471.c +++ b/drivers/iio/adc/ltc2471.c @@ -136,8 +136,8 @@ static int ltc2471_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ltc2471_i2c_id[] = { - { "ltc2471", ltc2471 }, - { "ltc2473", ltc2473 }, + { .name = "ltc2471", .driver_data = ltc2471 }, + { .name = "ltc2473", .driver_data = ltc2473 }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id); diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c index 060651dd4130..8c3806e061dd 100644 --- a/drivers/iio/adc/ltc2485.c +++ b/drivers/iio/adc/ltc2485.c @@ -124,7 +124,7 @@ static int ltc2485_probe(struct i2c_client *client) } static const struct i2c_device_id ltc2485_id[] = { - { "ltc2485" }, + { .name = "ltc2485" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2485_id); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index eb9d521e86e5..8e899d6ffcfa 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -142,8 +142,8 @@ static const struct ltc2497_chip_info ltc2497_info[] = { }; static const struct i2c_device_id ltc2497_id[] = { - { "ltc2497", (kernel_ulong_t)<c2497_info[TYPE_LTC2497] }, - { "ltc2499", (kernel_ulong_t)<c2497_info[TYPE_LTC2499] }, + { .name = "ltc2497", .driver_data = (kernel_ulong_t)<c2497_info[TYPE_LTC2497] }, + { .name = "ltc2499", .driver_data = (kernel_ulong_t)<c2497_info[TYPE_LTC2499] }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2497_id); diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c index 4f45fd22a90c..da847eaed84e 100644 --- a/drivers/iio/adc/max34408.c +++ b/drivers/iio/adc/max34408.c @@ -256,8 +256,8 @@ static const struct of_device_id max34408_of_match[] = { MODULE_DEVICE_TABLE(of, max34408_of_match); static const struct i2c_device_id max34408_id[] = { - { "max34408", (kernel_ulong_t)&max34408_model_data }, - { "max34409", (kernel_ulong_t)&max34409_model_data }, + { .name = "max34408", .driver_data = (kernel_ulong_t)&max34408_model_data }, + { .name = "max34409", .driver_data = (kernel_ulong_t)&max34409_model_data }, { } }; MODULE_DEVICE_TABLE(i2c, max34408_id); diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 0cbd7a874798..f49cde672958 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -383,14 +383,14 @@ static int mcp3422_probe(struct i2c_client *client) } static const struct i2c_device_id mcp3422_id[] = { - { "mcp3421", 1 }, - { "mcp3422", 2 }, - { "mcp3423", 3 }, - { "mcp3424", 4 }, - { "mcp3425", 5 }, - { "mcp3426", 6 }, - { "mcp3427", 7 }, - { "mcp3428", 8 }, + { .name = "mcp3421", .driver_data = 1 }, + { .name = "mcp3422", .driver_data = 2 }, + { .name = "mcp3423", .driver_data = 3 }, + { .name = "mcp3424", .driver_data = 4 }, + { .name = "mcp3425", .driver_data = 5 }, + { .name = "mcp3426", .driver_data = 6 }, + { .name = "mcp3427", .driver_data = 7 }, + { .name = "mcp3428", .driver_data = 8 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3422_id); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 970afb27b839..836c9b49c721 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -531,7 +531,7 @@ static int nau7802_probe(struct i2c_client *client) } static const struct i2c_device_id nau7802_i2c_id[] = { - { "nau7802" }, + { .name = "nau7802" }, { } }; MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id); diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c index 40d00bd0cc9d..864f3b1366b5 100644 --- a/drivers/iio/adc/rohm-bd79124.c +++ b/drivers/iio/adc/rohm-bd79124.c @@ -1104,7 +1104,7 @@ static const struct of_device_id bd79124_of_match[] = { MODULE_DEVICE_TABLE(of, bd79124_of_match); static const struct i2c_device_id bd79124_id[] = { - { "bd79124" }, + { .name = "bd79124" }, { } }; MODULE_DEVICE_TABLE(i2c, bd79124_id); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 8ef51c57912d..33f82bdfeb94 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -199,9 +199,9 @@ static int adc081c_probe(struct i2c_client *client) } static const struct i2c_device_id adc081c_id[] = { - { "adc081c", (kernel_ulong_t)&adc081c_model }, - { "adc101c", (kernel_ulong_t)&adc101c_model }, - { "adc121c", (kernel_ulong_t)&adc121c_model }, + { .name = "adc081c", .driver_data = (kernel_ulong_t)&adc081c_model }, + { .name = "adc101c", .driver_data = (kernel_ulong_t)&adc101c_model }, + { .name = "adc121c", .driver_data = (kernel_ulong_t)&adc121c_model }, { } }; MODULE_DEVICE_TABLE(i2c, adc081c_id); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index c7ffe47449e2..8a272af69f7d 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -1128,9 +1128,9 @@ static const struct ads1015_chip_data tla2024_data = { }; static const struct i2c_device_id ads1015_id[] = { - { "ads1015", (kernel_ulong_t)&ads1015_data }, - { "ads1115", (kernel_ulong_t)&ads1115_data }, - { "tla2024", (kernel_ulong_t)&tla2024_data }, + { .name = "ads1015", .driver_data = (kernel_ulong_t)&ads1015_data }, + { .name = "ads1115", .driver_data = (kernel_ulong_t)&ads1115_data }, + { .name = "tla2024", .driver_data = (kernel_ulong_t)&tla2024_data }, { } }; MODULE_DEVICE_TABLE(i2c, ads1015_id); diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c index aa8946063c7d..9fe8d54cce83 100644 --- a/drivers/iio/adc/ti-ads1100.c +++ b/drivers/iio/adc/ti-ads1100.c @@ -400,8 +400,8 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops, NULL); static const struct i2c_device_id ads1100_id[] = { - { "ads1100" }, - { "ads1000" }, + { .name = "ads1100" }, + { .name = "ads1000" }, { } }; diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 2320be0efb50..d31f3d6eb781 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -804,7 +804,7 @@ static const struct of_device_id __maybe_unused ads1119_of_match[] = { MODULE_DEVICE_TABLE(of, ads1119_of_match); static const struct i2c_device_id ads1119_id[] = { - { "ads1119" }, + { .name = "ads1119" }, { } }; MODULE_DEVICE_TABLE(i2c, ads1119_id); diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c index ee5c1b8e3a8e..af87f5f19a0f 100644 --- a/drivers/iio/adc/ti-ads7138.c +++ b/drivers/iio/adc/ti-ads7138.c @@ -727,8 +727,8 @@ static const struct of_device_id ads7138_of_match[] = { MODULE_DEVICE_TABLE(of, ads7138_of_match); static const struct i2c_device_id ads7138_device_ids[] = { - { "ads7128", (kernel_ulong_t)&ads7128_data }, - { "ads7138", (kernel_ulong_t)&ads7138_data }, + { .name = "ads7128", .driver_data = (kernel_ulong_t)&ads7128_data }, + { .name = "ads7138", .driver_data = (kernel_ulong_t)&ads7138_data }, { } }; MODULE_DEVICE_TABLE(i2c, ads7138_device_ids); diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c index 5f294595a415..ff30a52d2850 100644 --- a/drivers/iio/adc/ti-ads7924.c +++ b/drivers/iio/adc/ti-ads7924.c @@ -442,7 +442,7 @@ static int ads7924_probe(struct i2c_client *client) } static const struct i2c_device_id ads7924_id[] = { - { "ads7924" }, + { .name = "ads7924" }, { } }; MODULE_DEVICE_TABLE(i2c, ads7924_id); diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index 8106a6a83561..cb9fff3bd67f 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -628,9 +628,9 @@ static int ad7150_probe(struct i2c_client *client) } static const struct i2c_device_id ad7150_id[] = { - { "ad7150", AD7150 }, - { "ad7151", AD7151 }, - { "ad7156", AD7150 }, + { .name = "ad7150", .driver_data = AD7150 }, + { .name = "ad7151", .driver_data = AD7151 }, + { .name = "ad7156", .driver_data = AD7150 }, { } }; diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index cb97e3c978d8..cf68b882bc49 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -789,9 +789,9 @@ static int ad7746_probe(struct i2c_client *client) } static const struct i2c_device_id ad7746_id[] = { - { "ad7745", 7745 }, - { "ad7746", 7746 }, - { "ad7747", 7747 }, + { .name = "ad7745", .driver_data = 7745 }, + { .name = "ad7746", .driver_data = 7746 }, + { .name = "ad7747", .driver_data = 7747 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7746_id); diff --git a/drivers/iio/chemical/ags02ma.c b/drivers/iio/chemical/ags02ma.c index 151178d4e8f4..8b97ea4a9515 100644 --- a/drivers/iio/chemical/ags02ma.c +++ b/drivers/iio/chemical/ags02ma.c @@ -139,7 +139,7 @@ static int ags02ma_probe(struct i2c_client *client) } static const struct i2c_device_id ags02ma_id_table[] = { - { "ags02ma" }, + { .name = "ags02ma" }, { } }; MODULE_DEVICE_TABLE(i2c, ags02ma_id_table); diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c index 10156d794092..7aa7841c530e 100644 --- a/drivers/iio/chemical/ams-iaq-core.c +++ b/drivers/iio/chemical/ams-iaq-core.c @@ -163,7 +163,7 @@ static int ams_iaqcore_probe(struct i2c_client *client) } static const struct i2c_device_id ams_iaqcore_id[] = { - { "ams-iaq-core" }, + { .name = "ams-iaq-core" }, { } }; MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id); diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c index 59f3a4fa9e9f..05da3b8a92ab 100644 --- a/drivers/iio/chemical/atlas-ezo-sensor.c +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -186,9 +186,9 @@ static const struct iio_info atlas_info = { }; static const struct i2c_device_id atlas_ezo_id[] = { - { "atlas-co2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] }, - { "atlas-o2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] }, - { "atlas-hum-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] }, + { .name = "atlas-co2-ezo", .driver_data = (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] }, + { .name = "atlas-o2-ezo", .driver_data = (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] }, + { .name = "atlas-hum-ezo", .driver_data = (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] }, { } }; MODULE_DEVICE_TABLE(i2c, atlas_ezo_id); diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 8bbba85af699..0e2edcff63f9 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -586,11 +586,11 @@ static const struct iio_info atlas_info = { }; static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", (kernel_ulong_t)&atlas_devices[ATLAS_PH_SM] }, - { "atlas-ec-sm", (kernel_ulong_t)&atlas_devices[ATLAS_EC_SM] }, - { "atlas-orp-sm", (kernel_ulong_t)&atlas_devices[ATLAS_ORP_SM] }, - { "atlas-do-sm", (kernel_ulong_t)&atlas_devices[ATLAS_DO_SM] }, - { "atlas-rtd-sm", (kernel_ulong_t)&atlas_devices[ATLAS_RTD_SM] }, + { .name = "atlas-ph-sm", .driver_data = (kernel_ulong_t)&atlas_devices[ATLAS_PH_SM] }, + { .name = "atlas-ec-sm", .driver_data = (kernel_ulong_t)&atlas_devices[ATLAS_EC_SM] }, + { .name = "atlas-orp-sm", .driver_data = (kernel_ulong_t)&atlas_devices[ATLAS_ORP_SM] }, + { .name = "atlas-do-sm", .driver_data = (kernel_ulong_t)&atlas_devices[ATLAS_DO_SM] }, + { .name = "atlas-rtd-sm", .driver_data = (kernel_ulong_t)&atlas_devices[ATLAS_RTD_SM] }, { } }; MODULE_DEVICE_TABLE(i2c, atlas_id); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 5560ea708b36..4cc0e491621a 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -36,7 +36,7 @@ static int bme680_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id bme680_i2c_id[] = { - { "bme680" }, + { .name = "bme680" }, { } }; MODULE_DEVICE_TABLE(i2c, bme680_i2c_id); diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 998c9239c4c7..ce7187ccd706 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -552,8 +552,8 @@ static void ccs811_remove(struct i2c_client *client) } static const struct i2c_device_id ccs811_id[] = { - { "ccs811" }, - { } + { .name = "ccs811" }, + { } }; MODULE_DEVICE_TABLE(i2c, ccs811_id); diff --git a/drivers/iio/chemical/ens160_i2c.c b/drivers/iio/chemical/ens160_i2c.c index aa0dfe639245..ab0e8dcce011 100644 --- a/drivers/iio/chemical/ens160_i2c.c +++ b/drivers/iio/chemical/ens160_i2c.c @@ -34,7 +34,7 @@ static int ens160_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ens160_i2c_id[] = { - { "ens160" }, + { .name = "ens160" }, { } }; MODULE_DEVICE_TABLE(i2c, ens160_i2c_id); diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index d42b62b7081e..8b88be85602c 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -563,8 +563,8 @@ static void sgp_remove(struct i2c_client *client) } static const struct i2c_device_id sgp_id[] = { - { "sgp30", (kernel_ulong_t)&sgp_devices[SGP30] }, - { "sgpc3", (kernel_ulong_t)&sgp_devices[SGPC3] }, + { .name = "sgp30", .driver_data = (kernel_ulong_t)&sgp_devices[SGP30] }, + { .name = "sgpc3", .driver_data = (kernel_ulong_t)&sgp_devices[SGPC3] }, { } }; MODULE_DEVICE_TABLE(i2c, sgp_id); diff --git a/drivers/iio/chemical/sgp40.c b/drivers/iio/chemical/sgp40.c index 07d8ab830211..b2b5e32a9eb2 100644 --- a/drivers/iio/chemical/sgp40.c +++ b/drivers/iio/chemical/sgp40.c @@ -355,7 +355,7 @@ static int sgp40_probe(struct i2c_client *client) } static const struct i2c_device_id sgp40_id[] = { - { "sgp40" }, + { .name = "sgp40" }, { } }; diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c index c92f04990c34..61781aaabd85 100644 --- a/drivers/iio/chemical/sps30_i2c.c +++ b/drivers/iio/chemical/sps30_i2c.c @@ -232,7 +232,7 @@ static int sps30_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id sps30_i2c_id[] = { - { "sps30" }, + { .name = "sps30" }, { } }; MODULE_DEVICE_TABLE(i2c, sps30_i2c_id); diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 5b358bcd311b..4deacf10b6ef 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -385,8 +385,8 @@ static int vz89x_probe(struct i2c_client *client) } static const struct i2c_device_id vz89x_id[] = { - { "vz89x", (kernel_ulong_t)&vz89x_chips[VZ89X] }, - { "vz89te", (kernel_ulong_t)&vz89x_chips[VZ89TE] }, + { .name = "vz89x", .driver_data = (kernel_ulong_t)&vz89x_chips[VZ89X] }, + { .name = "vz89te", .driver_data = (kernel_ulong_t)&vz89x_chips[VZ89TE] }, { } }; MODULE_DEVICE_TABLE(i2c, vz89x_id); diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 84be5174babd..b5ec33f5c317 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -1001,53 +1001,53 @@ static int ad5064_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ad5064_i2c_ids[] = { - {"ad5625", ID_AD5625 }, - {"ad5625r-1v25", ID_AD5625R_1V25 }, - {"ad5625r-2v5", ID_AD5625R_2V5 }, - {"ad5627", ID_AD5627 }, - {"ad5627r-1v25", ID_AD5627R_1V25 }, - {"ad5627r-2v5", ID_AD5627R_2V5 }, - {"ad5629-1", ID_AD5629_1}, - {"ad5629-2", ID_AD5629_2}, - {"ad5629-3", ID_AD5629_2}, /* similar enough to ad5629-2 */ - {"ad5645r-1v25", ID_AD5645R_1V25 }, - {"ad5645r-2v5", ID_AD5645R_2V5 }, - {"ad5665", ID_AD5665 }, - {"ad5665r-1v25", ID_AD5665R_1V25 }, - {"ad5665r-2v5", ID_AD5665R_2V5 }, - {"ad5667", ID_AD5667 }, - {"ad5667r-1v25", ID_AD5667R_1V25 }, - {"ad5667r-2v5", ID_AD5667R_2V5 }, - {"ad5669-1", ID_AD5669_1}, - {"ad5669-2", ID_AD5669_2}, - {"ad5669-3", ID_AD5669_2}, /* similar enough to ad5669-2 */ - {"ltc2606", ID_LTC2606}, - {"ltc2607", ID_LTC2607}, - {"ltc2609", ID_LTC2609}, - {"ltc2616", ID_LTC2616}, - {"ltc2617", ID_LTC2617}, - {"ltc2619", ID_LTC2619}, - {"ltc2626", ID_LTC2626}, - {"ltc2627", ID_LTC2627}, - {"ltc2629", ID_LTC2629}, - {"ltc2631-l12", ID_LTC2631_L12}, - {"ltc2631-h12", ID_LTC2631_H12}, - {"ltc2631-l10", ID_LTC2631_L10}, - {"ltc2631-h10", ID_LTC2631_H10}, - {"ltc2631-l8", ID_LTC2631_L8}, - {"ltc2631-h8", ID_LTC2631_H8}, - {"ltc2633-l12", ID_LTC2633_L12}, - {"ltc2633-h12", ID_LTC2633_H12}, - {"ltc2633-l10", ID_LTC2633_L10}, - {"ltc2633-h10", ID_LTC2633_H10}, - {"ltc2633-l8", ID_LTC2633_L8}, - {"ltc2633-h8", ID_LTC2633_H8}, - {"ltc2635-l12", ID_LTC2635_L12}, - {"ltc2635-h12", ID_LTC2635_H12}, - {"ltc2635-l10", ID_LTC2635_L10}, - {"ltc2635-h10", ID_LTC2635_H10}, - {"ltc2635-l8", ID_LTC2635_L8}, - {"ltc2635-h8", ID_LTC2635_H8}, + { .name = "ad5625", .driver_data = ID_AD5625 }, + { .name = "ad5625r-1v25", .driver_data = ID_AD5625R_1V25 }, + { .name = "ad5625r-2v5", .driver_data = ID_AD5625R_2V5 }, + { .name = "ad5627", .driver_data = ID_AD5627 }, + { .name = "ad5627r-1v25", .driver_data = ID_AD5627R_1V25 }, + { .name = "ad5627r-2v5", .driver_data = ID_AD5627R_2V5 }, + { .name = "ad5629-1", .driver_data = ID_AD5629_1 }, + { .name = "ad5629-2", .driver_data = ID_AD5629_2 }, + { .name = "ad5629-3", .driver_data = ID_AD5629_2 }, /* similar enough to ad5629-2 */ + { .name = "ad5645r-1v25", .driver_data = ID_AD5645R_1V25 }, + { .name = "ad5645r-2v5", .driver_data = ID_AD5645R_2V5 }, + { .name = "ad5665", .driver_data = ID_AD5665 }, + { .name = "ad5665r-1v25", .driver_data = ID_AD5665R_1V25 }, + { .name = "ad5665r-2v5", .driver_data = ID_AD5665R_2V5 }, + { .name = "ad5667", .driver_data = ID_AD5667 }, + { .name = "ad5667r-1v25", .driver_data = ID_AD5667R_1V25 }, + { .name = "ad5667r-2v5", .driver_data = ID_AD5667R_2V5 }, + { .name = "ad5669-1", .driver_data = ID_AD5669_1 }, + { .name = "ad5669-2", .driver_data = ID_AD5669_2 }, + { .name = "ad5669-3", .driver_data = ID_AD5669_2 }, /* similar enough to ad5669-2 */ + { .name = "ltc2606", .driver_data = ID_LTC2606 }, + { .name = "ltc2607", .driver_data = ID_LTC2607 }, + { .name = "ltc2609", .driver_data = ID_LTC2609 }, + { .name = "ltc2616", .driver_data = ID_LTC2616 }, + { .name = "ltc2617", .driver_data = ID_LTC2617 }, + { .name = "ltc2619", .driver_data = ID_LTC2619 }, + { .name = "ltc2626", .driver_data = ID_LTC2626 }, + { .name = "ltc2627", .driver_data = ID_LTC2627 }, + { .name = "ltc2629", .driver_data = ID_LTC2629 }, + { .name = "ltc2631-l12", .driver_data = ID_LTC2631_L12 }, + { .name = "ltc2631-h12", .driver_data = ID_LTC2631_H12 }, + { .name = "ltc2631-l10", .driver_data = ID_LTC2631_L10 }, + { .name = "ltc2631-h10", .driver_data = ID_LTC2631_H10 }, + { .name = "ltc2631-l8", .driver_data = ID_LTC2631_L8 }, + { .name = "ltc2631-h8", .driver_data = ID_LTC2631_H8 }, + { .name = "ltc2633-l12", .driver_data = ID_LTC2633_L12 }, + { .name = "ltc2633-h12", .driver_data = ID_LTC2633_H12 }, + { .name = "ltc2633-l10", .driver_data = ID_LTC2633_L10 }, + { .name = "ltc2633-h10", .driver_data = ID_LTC2633_H10 }, + { .name = "ltc2633-l8", .driver_data = ID_LTC2633_L8 }, + { .name = "ltc2633-h8", .driver_data = ID_LTC2633_H8 }, + { .name = "ltc2635-l12", .driver_data = ID_LTC2635_L12 }, + { .name = "ltc2635-h12", .driver_data = ID_LTC2635_H12 }, + { .name = "ltc2635-l10", .driver_data = ID_LTC2635_L10 }, + { .name = "ltc2635-h10", .driver_data = ID_LTC2635_H10 }, + { .name = "ltc2635-l8", .driver_data = ID_LTC2635_L8 }, + { .name = "ltc2635-h8", .driver_data = ID_LTC2635_H8 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 8b813cee7625..2e587bdd3214 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -513,22 +513,22 @@ static int ad5380_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ad5380_i2c_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, + { .name = "ad5380-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5380-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5381-3", .driver_data = ID_AD5381_3 }, + { .name = "ad5381-5", .driver_data = ID_AD5381_5 }, + { .name = "ad5382-3", .driver_data = ID_AD5382_3 }, + { .name = "ad5382-5", .driver_data = ID_AD5382_5 }, + { .name = "ad5383-3", .driver_data = ID_AD5383_3 }, + { .name = "ad5383-5", .driver_data = ID_AD5383_5 }, + { .name = "ad5384-3", .driver_data = ID_AD5380_3 }, + { .name = "ad5384-5", .driver_data = ID_AD5380_5 }, + { .name = "ad5390-3", .driver_data = ID_AD5390_3 }, + { .name = "ad5390-5", .driver_data = ID_AD5390_5 }, + { .name = "ad5391-3", .driver_data = ID_AD5391_3 }, + { .name = "ad5391-5", .driver_data = ID_AD5391_5 }, + { .name = "ad5392-3", .driver_data = ID_AD5392_3 }, + { .name = "ad5392-5", .driver_data = ID_AD5392_5 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); diff --git a/drivers/iio/dac/ad5446-i2c.c b/drivers/iio/dac/ad5446-i2c.c index 40fe7e17fce4..2d4c8908d91e 100644 --- a/drivers/iio/dac/ad5446-i2c.c +++ b/drivers/iio/dac/ad5446-i2c.c @@ -65,12 +65,12 @@ static const struct ad5446_chip_info ad5622_chip_info = { }; static const struct i2c_device_id ad5446_i2c_ids[] = { - {"ad5301", (kernel_ulong_t)&ad5602_chip_info}, - {"ad5311", (kernel_ulong_t)&ad5612_chip_info}, - {"ad5321", (kernel_ulong_t)&ad5622_chip_info}, - {"ad5602", (kernel_ulong_t)&ad5602_chip_info}, - {"ad5612", (kernel_ulong_t)&ad5612_chip_info}, - {"ad5622", (kernel_ulong_t)&ad5622_chip_info}, + { .name = "ad5301", .driver_data = (kernel_ulong_t)&ad5602_chip_info }, + { .name = "ad5311", .driver_data = (kernel_ulong_t)&ad5612_chip_info }, + { .name = "ad5321", .driver_data = (kernel_ulong_t)&ad5622_chip_info }, + { .name = "ad5602", .driver_data = (kernel_ulong_t)&ad5602_chip_info }, + { .name = "ad5612", .driver_data = (kernel_ulong_t)&ad5612_chip_info }, + { .name = "ad5622", .driver_data = (kernel_ulong_t)&ad5622_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids); diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index d3327bca0e07..56625e1da817 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -66,22 +66,22 @@ static int ad5686_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ad5686_i2c_id[] = { - {"ad5311r", ID_AD5311R}, - {"ad5337r", ID_AD5337R}, - {"ad5338r", ID_AD5338R}, - {"ad5671r", ID_AD5671R}, - {"ad5673r", ID_AD5673R}, - {"ad5675r", ID_AD5675R}, - {"ad5677r", ID_AD5677R}, - {"ad5691r", ID_AD5691R}, - {"ad5692r", ID_AD5692R}, - {"ad5693", ID_AD5693}, - {"ad5693r", ID_AD5693R}, - {"ad5694", ID_AD5694}, - {"ad5694r", ID_AD5694R}, - {"ad5695r", ID_AD5695R}, - {"ad5696", ID_AD5696}, - {"ad5696r", ID_AD5696R}, + { .name = "ad5311r", .driver_data = ID_AD5311R }, + { .name = "ad5337r", .driver_data = ID_AD5337R }, + { .name = "ad5338r", .driver_data = ID_AD5338R }, + { .name = "ad5671r", .driver_data = ID_AD5671R }, + { .name = "ad5673r", .driver_data = ID_AD5673R }, + { .name = "ad5675r", .driver_data = ID_AD5675R }, + { .name = "ad5677r", .driver_data = ID_AD5677R }, + { .name = "ad5691r", .driver_data = ID_AD5691R }, + { .name = "ad5692r", .driver_data = ID_AD5692R }, + { .name = "ad5693", .driver_data = ID_AD5693 }, + { .name = "ad5693r", .driver_data = ID_AD5693R }, + { .name = "ad5694", .driver_data = ID_AD5694 }, + { .name = "ad5694r", .driver_data = ID_AD5694R }, + { .name = "ad5695r", .driver_data = ID_AD5695R }, + { .name = "ad5696", .driver_data = ID_AD5696 }, + { .name = "ad5696r", .driver_data = ID_AD5696R }, { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 085f73de3f02..b6430b832cf0 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -401,10 +401,10 @@ static void ds4424_remove(struct i2c_client *client) } static const struct i2c_device_id ds4424_id[] = { - { "ds4402", (kernel_ulong_t)&ds4402_info }, - { "ds4404", (kernel_ulong_t)&ds4404_info }, - { "ds4422", (kernel_ulong_t)&ds4422_info }, - { "ds4424", (kernel_ulong_t)&ds4424_info }, + { .name = "ds4402", .driver_data = (kernel_ulong_t)&ds4402_info }, + { .name = "ds4404", .driver_data = (kernel_ulong_t)&ds4404_info }, + { .name = "ds4422", .driver_data = (kernel_ulong_t)&ds4422_info }, + { .name = "ds4424", .driver_data = (kernel_ulong_t)&ds4424_info }, { } }; diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 3497513854d7..7e80c0eb5cc1 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -228,7 +228,7 @@ static void m62332_remove(struct i2c_client *client) } static const struct i2c_device_id m62332_id[] = { - { "m62332", }, + { .name = "m62332" }, { } }; MODULE_DEVICE_TABLE(i2c, m62332_id); diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index d334c67821ad..d2ddc72e0e50 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -187,11 +187,11 @@ static int max517_probe(struct i2c_client *client) } static const struct i2c_device_id max517_id[] = { - { "max517", ID_MAX517 }, - { "max518", ID_MAX518 }, - { "max519", ID_MAX519 }, - { "max520", ID_MAX520 }, - { "max521", ID_MAX521 }, + { .name = "max517", .driver_data = ID_MAX517 }, + { .name = "max518", .driver_data = ID_MAX518 }, + { .name = "max519", .driver_data = ID_MAX519 }, + { .name = "max520", .driver_data = ID_MAX520 }, + { .name = "max521", .driver_data = ID_MAX521 }, { } }; MODULE_DEVICE_TABLE(i2c, max517_id); diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 23b9e3a09ec8..2d6bcfd5deaa 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -523,8 +523,8 @@ static const struct mcp4725_chip_info mcp4726 = { }; static const struct i2c_device_id mcp4725_id[] = { - { "mcp4725", (kernel_ulong_t)&mcp4725 }, - { "mcp4726", (kernel_ulong_t)&mcp4726 }, + { .name = "mcp4725", .driver_data = (kernel_ulong_t)&mcp4725 }, + { .name = "mcp4726", .driver_data = (kernel_ulong_t)&mcp4726 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp4725_id); diff --git a/drivers/iio/dac/mcp4728.c b/drivers/iio/dac/mcp4728.c index 4f30b99110b7..64bd9490fc19 100644 --- a/drivers/iio/dac/mcp4728.c +++ b/drivers/iio/dac/mcp4728.c @@ -572,7 +572,7 @@ static int mcp4728_probe(struct i2c_client *client) } static const struct i2c_device_id mcp4728_id[] = { - { "mcp4728" }, + { .name = "mcp4728" }, { } }; MODULE_DEVICE_TABLE(i2c, mcp4728_id); diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c index faccb804a5ed..217f78e44af1 100644 --- a/drivers/iio/dac/mcp47feb02.c +++ b/drivers/iio/dac/mcp47feb02.c @@ -1170,30 +1170,30 @@ static int mcp47feb02_probe(struct i2c_client *client) } static const struct i2c_device_id mcp47feb02_id[] = { - { "mcp47feb01", (kernel_ulong_t)&mcp47feb01_chip_features }, - { "mcp47feb02", (kernel_ulong_t)&mcp47feb02_chip_features }, - { "mcp47feb04", (kernel_ulong_t)&mcp47feb04_chip_features }, - { "mcp47feb08", (kernel_ulong_t)&mcp47feb08_chip_features }, - { "mcp47feb11", (kernel_ulong_t)&mcp47feb11_chip_features }, - { "mcp47feb12", (kernel_ulong_t)&mcp47feb12_chip_features }, - { "mcp47feb14", (kernel_ulong_t)&mcp47feb14_chip_features }, - { "mcp47feb18", (kernel_ulong_t)&mcp47feb18_chip_features }, - { "mcp47feb21", (kernel_ulong_t)&mcp47feb21_chip_features }, - { "mcp47feb22", (kernel_ulong_t)&mcp47feb22_chip_features }, - { "mcp47feb24", (kernel_ulong_t)&mcp47feb24_chip_features }, - { "mcp47feb28", (kernel_ulong_t)&mcp47feb28_chip_features }, - { "mcp47fvb01", (kernel_ulong_t)&mcp47fvb01_chip_features }, - { "mcp47fvb02", (kernel_ulong_t)&mcp47fvb02_chip_features }, - { "mcp47fvb04", (kernel_ulong_t)&mcp47fvb04_chip_features }, - { "mcp47fvb08", (kernel_ulong_t)&mcp47fvb08_chip_features }, - { "mcp47fvb11", (kernel_ulong_t)&mcp47fvb11_chip_features }, - { "mcp47fvb12", (kernel_ulong_t)&mcp47fvb12_chip_features }, - { "mcp47fvb14", (kernel_ulong_t)&mcp47fvb14_chip_features }, - { "mcp47fvb18", (kernel_ulong_t)&mcp47fvb18_chip_features }, - { "mcp47fvb21", (kernel_ulong_t)&mcp47fvb21_chip_features }, - { "mcp47fvb22", (kernel_ulong_t)&mcp47fvb22_chip_features }, - { "mcp47fvb24", (kernel_ulong_t)&mcp47fvb24_chip_features }, - { "mcp47fvb28", (kernel_ulong_t)&mcp47fvb28_chip_features }, + { .name = "mcp47feb01", .driver_data = (kernel_ulong_t)&mcp47feb01_chip_features }, + { .name = "mcp47feb02", .driver_data = (kernel_ulong_t)&mcp47feb02_chip_features }, + { .name = "mcp47feb04", .driver_data = (kernel_ulong_t)&mcp47feb04_chip_features }, + { .name = "mcp47feb08", .driver_data = (kernel_ulong_t)&mcp47feb08_chip_features }, + { .name = "mcp47feb11", .driver_data = (kernel_ulong_t)&mcp47feb11_chip_features }, + { .name = "mcp47feb12", .driver_data = (kernel_ulong_t)&mcp47feb12_chip_features }, + { .name = "mcp47feb14", .driver_data = (kernel_ulong_t)&mcp47feb14_chip_features }, + { .name = "mcp47feb18", .driver_data = (kernel_ulong_t)&mcp47feb18_chip_features }, + { .name = "mcp47feb21", .driver_data = (kernel_ulong_t)&mcp47feb21_chip_features }, + { .name = "mcp47feb22", .driver_data = (kernel_ulong_t)&mcp47feb22_chip_features }, + { .name = "mcp47feb24", .driver_data = (kernel_ulong_t)&mcp47feb24_chip_features }, + { .name = "mcp47feb28", .driver_data = (kernel_ulong_t)&mcp47feb28_chip_features }, + { .name = "mcp47fvb01", .driver_data = (kernel_ulong_t)&mcp47fvb01_chip_features }, + { .name = "mcp47fvb02", .driver_data = (kernel_ulong_t)&mcp47fvb02_chip_features }, + { .name = "mcp47fvb04", .driver_data = (kernel_ulong_t)&mcp47fvb04_chip_features }, + { .name = "mcp47fvb08", .driver_data = (kernel_ulong_t)&mcp47fvb08_chip_features }, + { .name = "mcp47fvb11", .driver_data = (kernel_ulong_t)&mcp47fvb11_chip_features }, + { .name = "mcp47fvb12", .driver_data = (kernel_ulong_t)&mcp47fvb12_chip_features }, + { .name = "mcp47fvb14", .driver_data = (kernel_ulong_t)&mcp47fvb14_chip_features }, + { .name = "mcp47fvb18", .driver_data = (kernel_ulong_t)&mcp47fvb18_chip_features }, + { .name = "mcp47fvb21", .driver_data = (kernel_ulong_t)&mcp47fvb21_chip_features }, + { .name = "mcp47fvb22", .driver_data = (kernel_ulong_t)&mcp47fvb22_chip_features }, + { .name = "mcp47fvb24", .driver_data = (kernel_ulong_t)&mcp47fvb24_chip_features }, + { .name = "mcp47fvb28", .driver_data = (kernel_ulong_t)&mcp47fvb28_chip_features }, { } }; MODULE_DEVICE_TABLE(i2c, mcp47feb02_id); diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index 455d61fc3f13..b9efd704e996 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -402,17 +402,17 @@ static const struct of_device_id dac5571_of_id[] = { MODULE_DEVICE_TABLE(of, dac5571_of_id); static const struct i2c_device_id dac5571_id[] = { - {"dac081c081", (kernel_ulong_t)&dac5571_spec[single_8bit] }, - {"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] }, - {"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] }, - {"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] }, - {"dac7571", (kernel_ulong_t)&dac5571_spec[single_12bit] }, - {"dac5574", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, - {"dac6574", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, - {"dac7574", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, - {"dac5573", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, - {"dac6573", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, - {"dac7573", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, + { .name = "dac081c081", .driver_data = (kernel_ulong_t)&dac5571_spec[single_8bit] }, + { .name = "dac121c081", .driver_data = (kernel_ulong_t)&dac5571_spec[single_12bit] }, + { .name = "dac5571", .driver_data = (kernel_ulong_t)&dac5571_spec[single_8bit] }, + { .name = "dac6571", .driver_data = (kernel_ulong_t)&dac5571_spec[single_10bit] }, + { .name = "dac7571", .driver_data = (kernel_ulong_t)&dac5571_spec[single_12bit] }, + { .name = "dac5574", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_8bit] }, + { .name = "dac6574", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_10bit] }, + { .name = "dac7574", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_12bit] }, + { .name = "dac5573", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_8bit] }, + { .name = "dac6573", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_10bit] }, + { .name = "dac7573", .driver_data = (kernel_ulong_t)&dac5571_spec[quad_12bit] }, { } }; MODULE_DEVICE_TABLE(i2c, dac5571_id); diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c index 1fb8a7969c25..028e5e29c6a1 100644 --- a/drivers/iio/gyro/bmg160_i2c.c +++ b/drivers/iio/gyro/bmg160_i2c.c @@ -47,9 +47,9 @@ static const struct acpi_device_id bmg160_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); static const struct i2c_device_id bmg160_i2c_id[] = { - { "bmg160" }, - { "bmi055_gyro" }, - { "bmi088_gyro" }, + { .name = "bmg160" }, + { .name = "bmi055_gyro" }, + { .name = "bmi088_gyro" }, { } }; diff --git a/drivers/iio/gyro/fxas21002c_i2c.c b/drivers/iio/gyro/fxas21002c_i2c.c index 43c6b3079487..d537e91caaaf 100644 --- a/drivers/iio/gyro/fxas21002c_i2c.c +++ b/drivers/iio/gyro/fxas21002c_i2c.c @@ -39,7 +39,7 @@ static void fxas21002c_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id fxas21002c_i2c_id[] = { - { "fxas21002c" }, + { .name = "fxas21002c" }, { } }; MODULE_DEVICE_TABLE(i2c, fxas21002c_i2c_id); diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index bfe95ec1abda..6b13d54d9b01 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -389,7 +389,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend, itg3200_resume); static const struct i2c_device_id itg3200_id[] = { - { "itg3200" }, + { .name = "itg3200" }, { } }; MODULE_DEVICE_TABLE(i2c, itg3200_id); diff --git a/drivers/iio/gyro/mpu3050-i2c.c b/drivers/iio/gyro/mpu3050-i2c.c index 6549b22e643d..2a5bcec7272c 100644 --- a/drivers/iio/gyro/mpu3050-i2c.c +++ b/drivers/iio/gyro/mpu3050-i2c.c @@ -92,7 +92,7 @@ static void mpu3050_i2c_remove(struct i2c_client *client) * supported by this driver */ static const struct i2c_device_id mpu3050_i2c_id[] = { - { "mpu3050" }, + { .name = "mpu3050" }, { } }; MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index aef5ec8f9dee..b07cb39051b3 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -93,15 +93,15 @@ static int st_gyro_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id st_gyro_id_table[] = { - { L3G4200D_GYRO_DEV_NAME }, - { LSM330D_GYRO_DEV_NAME }, - { LSM330DL_GYRO_DEV_NAME }, - { LSM330DLC_GYRO_DEV_NAME }, - { L3GD20_GYRO_DEV_NAME }, - { L3GD20H_GYRO_DEV_NAME }, - { L3G4IS_GYRO_DEV_NAME }, - { LSM330_GYRO_DEV_NAME }, - { LSM9DS0_GYRO_DEV_NAME }, + { .name = L3G4200D_GYRO_DEV_NAME }, + { .name = LSM330D_GYRO_DEV_NAME }, + { .name = LSM330DL_GYRO_DEV_NAME }, + { .name = LSM330DLC_GYRO_DEV_NAME }, + { .name = L3GD20_GYRO_DEV_NAME }, + { .name = L3GD20H_GYRO_DEV_NAME }, + { .name = L3G4IS_GYRO_DEV_NAME }, + { .name = LSM330_GYRO_DEV_NAME }, + { .name = LSM9DS0_GYRO_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, st_gyro_id_table); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 032da52a96d0..2357e2dd7017 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -575,7 +575,7 @@ static int afe4404_probe(struct i2c_client *client) } static const struct i2c_device_id afe4404_ids[] = { - { "afe4404" }, + { .name = "afe4404" }, { } }; MODULE_DEVICE_TABLE(i2c, afe4404_ids); diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 7dfdb5eb305e..97352485f5e2 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -507,7 +507,7 @@ static void max30100_remove(struct i2c_client *client) } static const struct i2c_device_id max30100_id[] = { - { "max30100" }, + { .name = "max30100" }, { } }; MODULE_DEVICE_TABLE(i2c, max30100_id); diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 47da44efd68b..c830eaf286f7 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -596,9 +596,9 @@ static void max30102_remove(struct i2c_client *client) } static const struct i2c_device_id max30102_id[] = { - { "max30101", max30105 }, - { "max30102", max30102 }, - { "max30105", max30105 }, + { .name = "max30101", .driver_data = max30105 }, + { .name = "max30102", .driver_data = max30102 }, + { .name = "max30105", .driver_data = max30105 }, { } }; MODULE_DEVICE_TABLE(i2c, max30102_id); diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 02ca23eb8991..f29baa251f9f 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -250,7 +250,7 @@ static int am2315_probe(struct i2c_client *client) } static const struct i2c_device_id am2315_i2c_id[] = { - { "am2315" }, + { .name = "am2315" }, { } }; MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); diff --git a/drivers/iio/humidity/ens210.c b/drivers/iio/humidity/ens210.c index 77418d97f30d..22ad208e6aa6 100644 --- a/drivers/iio/humidity/ens210.c +++ b/drivers/iio/humidity/ens210.c @@ -314,12 +314,12 @@ static const struct of_device_id ens210_of_match[] = { MODULE_DEVICE_TABLE(of, ens210_of_match); static const struct i2c_device_id ens210_id_table[] = { - { "ens210", (kernel_ulong_t)&ens210_chip_info_data }, - { "ens210a", (kernel_ulong_t)&ens210a_chip_info_data }, - { "ens211", (kernel_ulong_t)&ens211_chip_info_data }, - { "ens212", (kernel_ulong_t)&ens212_chip_info_data }, - { "ens213a", (kernel_ulong_t)&ens213a_chip_info_data }, - { "ens215", (kernel_ulong_t)&ens215_chip_info_data }, + { .name = "ens210", .driver_data = (kernel_ulong_t)&ens210_chip_info_data }, + { .name = "ens210a", .driver_data = (kernel_ulong_t)&ens210a_chip_info_data }, + { .name = "ens211", .driver_data = (kernel_ulong_t)&ens211_chip_info_data }, + { .name = "ens212", .driver_data = (kernel_ulong_t)&ens212_chip_info_data }, + { .name = "ens213a", .driver_data = (kernel_ulong_t)&ens213a_chip_info_data }, + { .name = "ens215", .driver_data = (kernel_ulong_t)&ens215_chip_info_data }, { } }; MODULE_DEVICE_TABLE(i2c, ens210_id_table); diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index c2b36e682e06..87194802cc4f 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -380,12 +380,12 @@ static int hdc100x_probe(struct i2c_client *client) } static const struct i2c_device_id hdc100x_id[] = { - { "hdc100x" }, - { "hdc1000" }, - { "hdc1008" }, - { "hdc1010" }, - { "hdc1050" }, - { "hdc1080" }, + { .name = "hdc100x" }, + { .name = "hdc1000" }, + { .name = "hdc1008" }, + { .name = "hdc1010" }, + { .name = "hdc1050" }, + { .name = "hdc1080" }, { } }; MODULE_DEVICE_TABLE(i2c, hdc100x_id); diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index 1a0f18251381..82d68987556d 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -317,8 +317,8 @@ static void hdc2010_remove(struct i2c_client *client) } static const struct i2c_device_id hdc2010_id[] = { - { "hdc2010" }, - { "hdc2080" }, + { .name = "hdc2010" }, + { .name = "hdc2080" }, { } }; MODULE_DEVICE_TABLE(i2c, hdc2010_id); diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 78b2c171c8da..1ae8702ada54 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -873,9 +873,9 @@ static int hdc3020_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume); static const struct i2c_device_id hdc3020_id[] = { - { "hdc3020" }, - { "hdc3021" }, - { "hdc3022" }, + { .name = "hdc3020" }, + { .name = "hdc3021" }, + { .name = "hdc3022" }, { } }; MODULE_DEVICE_TABLE(i2c, hdc3020_id); diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c index cbaa7d1af6c4..e823d37384d7 100644 --- a/drivers/iio/humidity/hts221_i2c.c +++ b/drivers/iio/humidity/hts221_i2c.c @@ -53,7 +53,7 @@ static const struct of_device_id hts221_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, hts221_i2c_of_match); static const struct i2c_device_id hts221_i2c_id_table[] = { - { HTS221_DEV_NAME }, + { .name = HTS221_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table); diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 7f1775bd26fd..9ba7507f105e 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -230,8 +230,8 @@ static int htu21_probe(struct i2c_client *client) } static const struct i2c_device_id htu21_id[] = { - {"htu21", HTU21}, - {"ms8607-humidity", MS8607}, + { .name = "htu21", .driver_data = HTU21 }, + { .name = "ms8607-humidity", .driver_data = MS8607 }, { } }; MODULE_DEVICE_TABLE(i2c, htu21_id); diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c index 0797ece1fcba..0c11d0d3afe0 100644 --- a/drivers/iio/humidity/si7005.c +++ b/drivers/iio/humidity/si7005.c @@ -163,8 +163,8 @@ static int si7005_probe(struct i2c_client *client) } static const struct i2c_device_id si7005_id[] = { - { "si7005" }, - { "th02" }, + { .name = "si7005" }, + { .name = "th02" }, { } }; MODULE_DEVICE_TABLE(i2c, si7005_id); diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index ff2dba50c0a5..9fb1e3ede3ff 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -267,8 +267,8 @@ static int si7020_probe(struct i2c_client *client) } static const struct i2c_device_id si7020_id[] = { - { "si7020" }, - { "th06" }, + { .name = "si7020" }, + { .name = "th06" }, { } }; MODULE_DEVICE_TABLE(i2c, si7020_id); diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c index 3e2758f4e0d3..29f3c4acb123 100644 --- a/drivers/iio/imu/bmi160/bmi160_i2c.c +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c @@ -38,8 +38,8 @@ static int bmi160_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id bmi160_i2c_id[] = { - { "bmi120" }, - { "bmi160" }, + { .name = "bmi120" }, + { .name = "bmi160" }, { } }; MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id); diff --git a/drivers/iio/imu/bmi270/bmi270_i2c.c b/drivers/iio/imu/bmi270/bmi270_i2c.c index b92da4e0776f..1e6839f9669e 100644 --- a/drivers/iio/imu/bmi270/bmi270_i2c.c +++ b/drivers/iio/imu/bmi270/bmi270_i2c.c @@ -33,8 +33,8 @@ static int bmi270_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id bmi270_i2c_id[] = { - { "bmi260", (kernel_ulong_t)&bmi260_chip_info }, - { "bmi270", (kernel_ulong_t)&bmi270_chip_info }, + { .name = "bmi260", .driver_data = (kernel_ulong_t)&bmi260_chip_info }, + { .name = "bmi270", .driver_data = (kernel_ulong_t)&bmi270_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, bmi270_i2c_id); diff --git a/drivers/iio/imu/bmi323/bmi323_i2c.c b/drivers/iio/imu/bmi323/bmi323_i2c.c index 8457fe304db8..328733ddeed7 100644 --- a/drivers/iio/imu/bmi323/bmi323_i2c.c +++ b/drivers/iio/imu/bmi323/bmi323_i2c.c @@ -114,7 +114,7 @@ static const struct acpi_device_id bmi323_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, bmi323_acpi_match); static const struct i2c_device_id bmi323_i2c_ids[] = { - { "bmi323" }, + { .name = "bmi323" }, { } }; MODULE_DEVICE_TABLE(i2c, bmi323_i2c_ids); diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c index f49d0905ee33..000bc9392480 100644 --- a/drivers/iio/imu/bno055/bno055_i2c.c +++ b/drivers/iio/imu/bno055/bno055_i2c.c @@ -30,7 +30,7 @@ static int bno055_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id bno055_i2c_id[] = { - { "bno055" }, + { .name = "bno055" }, { } }; MODULE_DEVICE_TABLE(i2c, bno055_i2c_id); diff --git a/drivers/iio/imu/fxos8700_i2c.c b/drivers/iio/imu/fxos8700_i2c.c index 2cc4a27a4527..c81e48c9d8e2 100644 --- a/drivers/iio/imu/fxos8700_i2c.c +++ b/drivers/iio/imu/fxos8700_i2c.c @@ -36,7 +36,7 @@ static int fxos8700_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id fxos8700_i2c_id[] = { - { "fxos8700" }, + { .name = "fxos8700" }, { } }; MODULE_DEVICE_TABLE(i2c, fxos8700_i2c_id); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 7e4d3ea68721..99d37ac53bbe 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -75,13 +75,13 @@ static int inv_icm42600_probe(struct i2c_client *client) * supported by this driver */ static const struct i2c_device_id inv_icm42600_id[] = { - { "icm42600", INV_CHIP_ICM42600 }, - { "icm42602", INV_CHIP_ICM42602 }, - { "icm42605", INV_CHIP_ICM42605 }, - { "icm42686", INV_CHIP_ICM42686 }, - { "icm42622", INV_CHIP_ICM42622 }, - { "icm42688", INV_CHIP_ICM42688 }, - { "icm42631", INV_CHIP_ICM42631 }, + { .name = "icm42600", .driver_data = INV_CHIP_ICM42600 }, + { .name = "icm42602", .driver_data = INV_CHIP_ICM42602 }, + { .name = "icm42605", .driver_data = INV_CHIP_ICM42605 }, + { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 }, + { .name = "icm42622", .driver_data = INV_CHIP_ICM42622 }, + { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 }, + { .name = "icm42631", .driver_data = INV_CHIP_ICM42631 }, { } }; MODULE_DEVICE_TABLE(i2c, inv_icm42600_id); diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c index 5ebc18121a11..26fba538a3cf 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -39,14 +39,14 @@ static int inv_icm45600_probe(struct i2c_client *client) * supported by this driver. */ static const struct i2c_device_id inv_icm45600_id[] = { - { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, - { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, - { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, - { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, - { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, - { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, - { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, - { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { .name = "icm45605", .driver_data = (kernel_ulong_t)&inv_icm45605_chip_info }, + { .name = "icm45606", .driver_data = (kernel_ulong_t)&inv_icm45606_chip_info }, + { .name = "icm45608", .driver_data = (kernel_ulong_t)&inv_icm45608_chip_info }, + { .name = "icm45634", .driver_data = (kernel_ulong_t)&inv_icm45634_chip_info }, + { .name = "icm45686", .driver_data = (kernel_ulong_t)&inv_icm45686_chip_info }, + { .name = "icm45687", .driver_data = (kernel_ulong_t)&inv_icm45687_chip_info }, + { .name = "icm45688p", .driver_data = (kernel_ulong_t)&inv_icm45688p_chip_info }, + { .name = "icm45689", .driver_data = (kernel_ulong_t)&inv_icm45689_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, inv_icm45600_id); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 8dc61812a8fc..4868e1576cee 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -174,24 +174,24 @@ static void inv_mpu_remove(struct i2c_client *client) * supported by this driver */ static const struct i2c_device_id inv_mpu_id[] = { - {"mpu6050", INV_MPU6050}, - {"mpu6500", INV_MPU6500}, - {"mpu6515", INV_MPU6515}, - {"mpu6880", INV_MPU6880}, - {"mpu9150", INV_MPU9150}, - {"mpu9250", INV_MPU9250}, - {"mpu9255", INV_MPU9255}, - {"icm20608", INV_ICM20608}, - {"icm20608d", INV_ICM20608D}, - {"icm20609", INV_ICM20609}, - {"icm20689", INV_ICM20689}, - {"icm20600", INV_ICM20600}, - {"icm20602", INV_ICM20602}, - {"icm20690", INV_ICM20690}, - {"iam20380", INV_IAM20380}, - {"iam20680", INV_IAM20680}, - {"iam20680hp", INV_IAM20680HP}, - {"iam20680ht", INV_IAM20680HT}, + { .name = "mpu6050", .driver_data = INV_MPU6050 }, + { .name = "mpu6500", .driver_data = INV_MPU6500 }, + { .name = "mpu6515", .driver_data = INV_MPU6515 }, + { .name = "mpu6880", .driver_data = INV_MPU6880 }, + { .name = "mpu9150", .driver_data = INV_MPU9150 }, + { .name = "mpu9250", .driver_data = INV_MPU9250 }, + { .name = "mpu9255", .driver_data = INV_MPU9255 }, + { .name = "icm20608", .driver_data = INV_ICM20608 }, + { .name = "icm20608d", .driver_data = INV_ICM20608D }, + { .name = "icm20609", .driver_data = INV_ICM20609 }, + { .name = "icm20689", .driver_data = INV_ICM20689 }, + { .name = "icm20600", .driver_data = INV_ICM20600 }, + { .name = "icm20602", .driver_data = INV_ICM20602 }, + { .name = "icm20690", .driver_data = INV_ICM20690 }, + { .name = "iam20380", .driver_data = INV_IAM20380 }, + { .name = "iam20680", .driver_data = INV_IAM20680 }, + { .name = "iam20680hp", .driver_data = INV_IAM20680HP }, + { .name = "iam20680ht", .driver_data = INV_IAM20680HT }, { } }; diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 3cd91d8a89ee..a4d176f81f0e 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1481,7 +1481,7 @@ static const struct dev_pm_ops kmx61_pm_ops = { }; static const struct i2c_device_id kmx61_id[] = { - { "kmx611021" }, + { .name = "kmx611021" }, { } }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index b2a7c2eaf50d..edec898cb11f 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -150,30 +150,30 @@ static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { - { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, - { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, - { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, - { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, - { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, - { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, - { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, - { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, - { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, - { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, - { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, - { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, - { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, - { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, - { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, - { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, - { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, - { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, - { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, - { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, - { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, - { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, - { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, - { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, + { .name = ST_LSM6DS3_DEV_NAME, .driver_data = ST_LSM6DS3_ID }, + { .name = ST_LSM6DS3H_DEV_NAME, .driver_data = ST_LSM6DS3H_ID }, + { .name = ST_LSM6DSL_DEV_NAME, .driver_data = ST_LSM6DSL_ID }, + { .name = ST_LSM6DSM_DEV_NAME, .driver_data = ST_LSM6DSM_ID }, + { .name = ST_ISM330DLC_DEV_NAME, .driver_data = ST_ISM330DLC_ID }, + { .name = ST_LSM6DSO_DEV_NAME, .driver_data = ST_LSM6DSO_ID }, + { .name = ST_ASM330LHH_DEV_NAME, .driver_data = ST_ASM330LHH_ID }, + { .name = ST_LSM6DSOX_DEV_NAME, .driver_data = ST_LSM6DSOX_ID }, + { .name = ST_LSM6DSR_DEV_NAME, .driver_data = ST_LSM6DSR_ID }, + { .name = ST_LSM6DS3TRC_DEV_NAME, .driver_data = ST_LSM6DS3TRC_ID }, + { .name = ST_ISM330DHCX_DEV_NAME, .driver_data = ST_ISM330DHCX_ID }, + { .name = ST_LSM9DS1_DEV_NAME, .driver_data = ST_LSM9DS1_ID }, + { .name = ST_LSM6DS0_DEV_NAME, .driver_data = ST_LSM6DS0_ID }, + { .name = ST_LSM6DSRX_DEV_NAME, .driver_data = ST_LSM6DSRX_ID }, + { .name = ST_LSM6DST_DEV_NAME, .driver_data = ST_LSM6DST_ID }, + { .name = ST_LSM6DSOP_DEV_NAME, .driver_data = ST_LSM6DSOP_ID }, + { .name = ST_ASM330LHHX_DEV_NAME, .driver_data = ST_ASM330LHHX_ID }, + { .name = ST_LSM6DSTX_DEV_NAME, .driver_data = ST_LSM6DSTX_ID }, + { .name = ST_LSM6DSV_DEV_NAME, .driver_data = ST_LSM6DSV_ID }, + { .name = ST_LSM6DSV16X_DEV_NAME, .driver_data = ST_LSM6DSV16X_ID }, + { .name = ST_LSM6DSO16IS_DEV_NAME, .driver_data = ST_LSM6DSO16IS_ID }, + { .name = ST_ISM330IS_DEV_NAME, .driver_data = ST_ISM330IS_ID }, + { .name = ST_ASM330LHB_DEV_NAME, .driver_data = ST_ASM330LHB_ID }, + { .name = ST_ASM330LHHXG1_DEV_NAME, .driver_data = ST_ASM330LHHXG1_ID }, { } }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 4232a9d800fc..f71ae7a59a22 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -33,8 +33,8 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct i2c_device_id st_lsm9ds0_id_table[] = { - { LSM303D_IMU_DEV_NAME }, - { LSM9DS0_IMU_DEV_NAME }, + { .name = LSM303D_IMU_DEV_NAME }, + { .name = LSM9DS0_IMU_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table); diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index edb3d9dc8bed..088b9431d10a 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -260,7 +260,7 @@ static int adjd_s311_probe(struct i2c_client *client) } static const struct i2c_device_id adjd_s311_id[] = { - { "adjd_s311" }, + { .name = "adjd_s311" }, { } }; MODULE_DEVICE_TABLE(i2c, adjd_s311_id); diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 66ff9c5fb66a..633a105fd7f0 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -818,7 +818,7 @@ static int adux1020_probe(struct i2c_client *client) } static const struct i2c_device_id adux1020_id[] = { - { "adux1020" }, + { .name = "adux1020" }, { } }; MODULE_DEVICE_TABLE(i2c, adux1020_id); diff --git a/drivers/iio/light/al3000a.c b/drivers/iio/light/al3000a.c index 9871096cbab3..d4e6fedf3d9e 100644 --- a/drivers/iio/light/al3000a.c +++ b/drivers/iio/light/al3000a.c @@ -183,7 +183,7 @@ static int al3000a_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(al3000a_pm_ops, al3000a_suspend, al3000a_resume); static const struct i2c_device_id al3000a_id[] = { - { "al3000a" }, + { .name = "al3000a" }, { } }; MODULE_DEVICE_TABLE(i2c, al3000a_id); diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 0932fa2b49fa..f48420135320 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -218,7 +218,7 @@ static int al3010_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume); static const struct i2c_device_id al3010_id[] = { - {"al3010", }, + { .name = "al3010" }, { } }; MODULE_DEVICE_TABLE(i2c, al3010_id); diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 63f5a85912fc..617b4f15ea3f 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -246,7 +246,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume); static const struct i2c_device_id al3320a_id[] = { - { "al3320a" }, + { .name = "al3320a" }, { } }; MODULE_DEVICE_TABLE(i2c, al3320a_id); diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index 05ba21675063..d60ade1209f3 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -492,7 +492,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend, apds9300_resume); static const struct i2c_device_id apds9300_id[] = { - { APDS9300_DRV_NAME }, + { .name = APDS9300_DRV_NAME }, { } }; diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 785c5dbe2d08..2686c3b0c03b 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1154,7 +1154,7 @@ static const struct dev_pm_ops apds9960_pm_ops = { }; static const struct i2c_device_id apds9960_id[] = { - { "apds9960" }, + { .name = "apds9960" }, { } }; MODULE_DEVICE_TABLE(i2c, apds9960_id); diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c index 9fe830dac679..67f33bbe1c13 100644 --- a/drivers/iio/light/as73211.c +++ b/drivers/iio/light/as73211.c @@ -875,8 +875,8 @@ static const struct of_device_id as73211_of_match[] = { MODULE_DEVICE_TABLE(of, as73211_of_match); static const struct i2c_device_id as73211_id[] = { - { "as73211", (kernel_ulong_t)&as73211_spec }, - { "as7331", (kernel_ulong_t)&as7331_spec }, + { .name = "as73211", .driver_data = (kernel_ulong_t)&as73211_spec }, + { .name = "as7331", .driver_data = (kernel_ulong_t)&as7331_spec }, { } }; MODULE_DEVICE_TABLE(i2c, as73211_id); diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 10b00344bbed..0aa8e5cc6c56 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -874,7 +874,7 @@ static int bh1745_probe(struct i2c_client *client) } static const struct i2c_device_id bh1745_idtable[] = { - { "bh1745" }, + { .name = "bh1745" }, { } }; MODULE_DEVICE_TABLE(i2c, bh1745_idtable); diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index 764f88826fcb..a51ac98c83c8 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -319,11 +319,11 @@ static int bh1750_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); static const struct i2c_device_id bh1750_id[] = { - { "bh1710", BH1710 }, - { "bh1715", BH1750 }, - { "bh1721", BH1721 }, - { "bh1750", BH1750 }, - { "bh1751", BH1750 }, + { .name = "bh1710", .driver_data = BH1710 }, + { .name = "bh1715", .driver_data = BH1750 }, + { .name = "bh1721", .driver_data = BH1721 }, + { .name = "bh1750", .driver_data = BH1750 }, + { .name = "bh1751", .driver_data = BH1750 }, { } }; MODULE_DEVICE_TABLE(i2c, bh1750_id); diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c index a740d1f992a8..ead98fb82af9 100644 --- a/drivers/iio/light/bh1780.c +++ b/drivers/iio/light/bh1780.c @@ -255,7 +255,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend, bh1780_runtime_resume, NULL); static const struct i2c_device_id bh1780_id[] = { - { "bh1780" }, + { .name = "bh1780" }, { } }; diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index 90aa77e3a03b..fec233d06602 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -366,7 +366,7 @@ static void cm3232_remove(struct i2c_client *client) } static const struct i2c_device_id cm3232_id[] = { - { "cm3232" }, + { .name = "cm3232" }, { } }; MODULE_DEVICE_TABLE(i2c, cm3232_id); diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index 79ad6e2209ca..0fd4ddb69c06 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -250,7 +250,7 @@ static int cm3323_probe(struct i2c_client *client) } static const struct i2c_device_id cm3323_id[] = { - { "cm3323" }, + { .name = "cm3323" }, { } }; MODULE_DEVICE_TABLE(i2c, cm3323_id); diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index 446dd54d5037..6ebd8ce4ca12 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -713,7 +713,7 @@ static void cm36651_remove(struct i2c_client *client) } static const struct i2c_device_id cm36651_id[] = { - { "cm36651" }, + { .name = "cm36651" }, { } }; diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a0d8a58f2704..c83f67ff2464 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -690,7 +690,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend, gp2ap002_runtime_resume, NULL); static const struct i2c_device_id gp2ap002_id_table[] = { - { "gp2ap002" }, + { .name = "gp2ap002" }, { } }; MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 7e388319ee2e..c218bb3519df 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1525,7 +1525,7 @@ static void gp2ap020a00f_remove(struct i2c_client *client) } static const struct i2c_device_id gp2ap020a00f_id[] = { - { "gp2ap020a00f" }, + { .name = "gp2ap020a00f" }, { } }; MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id); diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index b6ab726d1dae..8a39afaa2a37 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -829,9 +829,9 @@ static const struct acpi_device_id isl29018_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match); static const struct i2c_device_id isl29018_id[] = { - {"isl29018", isl29018}, - {"isl29023", isl29023}, - {"isl29035", isl29035}, + { .name = "isl29018", .driver_data = isl29018 }, + { .name = "isl29023", .driver_data = isl29023 }, + { .name = "isl29035", .driver_data = isl29035 }, { } }; MODULE_DEVICE_TABLE(i2c, isl29018_id); diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 374bccad9119..b88e7c4eae3e 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -673,8 +673,8 @@ static DEFINE_RUNTIME_DEV_PM_OPS(isl29028_pm_ops, isl29028_suspend, isl29028_resume, NULL); static const struct i2c_device_id isl29028_id[] = { - { "isl29028" }, - { "isl29030" }, + { .name = "isl29028" }, + { .name = "isl29030" }, { } }; MODULE_DEVICE_TABLE(i2c, isl29028_id); diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index 3acb8a4f1d12..9e0b7e6a3ecf 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -325,7 +325,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, isl29125_resume); static const struct i2c_device_id isl29125_id[] = { - { "isl29125" }, + { .name = "isl29125" }, { } }; MODULE_DEVICE_TABLE(i2c, isl29125_id); diff --git a/drivers/iio/light/isl76682.c b/drivers/iio/light/isl76682.c index b6f2fc9978f6..9b9052d08e41 100644 --- a/drivers/iio/light/isl76682.c +++ b/drivers/iio/light/isl76682.c @@ -319,7 +319,7 @@ static int isl76682_probe(struct i2c_client *client) } static const struct i2c_device_id isl76682_id[] = { - { "isl76682" }, + { .name = "isl76682" }, { } }; MODULE_DEVICE_TABLE(i2c, isl76682_id); diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index 6978d02a4df5..cc0a7c4a33dd 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -428,7 +428,7 @@ static const struct acpi_device_id jsa1212_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match); static const struct i2c_device_id jsa1212_id[] = { - { JSA1212_DRIVER_NAME }, + { .name = JSA1212_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, jsa1212_id); diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index f1702aca582d..bdc74b8226c8 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -889,7 +889,7 @@ static const struct dev_pm_ops ltr390_pm_ops = { }; static const struct i2c_device_id ltr390_id[] = { - { "ltr390" }, + { .name = "ltr390" }, { } }; MODULE_DEVICE_TABLE(i2c, ltr390_id); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 4d99ae336f61..15dd82ecf745 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1601,10 +1601,10 @@ static const struct acpi_device_id ltr_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); static const struct i2c_device_id ltr501_id[] = { - { "ltr501", ltr501 }, - { "ltr559", ltr559 }, - { "ltr301", ltr301 }, - { "ltr303", ltr303 }, + { .name = "ltr501", .driver_data = ltr501 }, + { .name = "ltr559", .driver_data = ltr559 }, + { .name = "ltr301", .driver_data = ltr301 }, + { .name = "ltr303", .driver_data = ltr303 }, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 5f27f754fe1c..aad96fc91565 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -551,8 +551,8 @@ static const struct ltr_chip_info ltrf216a_chip_info = { }; static const struct i2c_device_id ltrf216a_id[] = { - { "ltr308", .driver_data = (kernel_ulong_t)<r308_chip_info }, - { "ltrf216a", .driver_data = (kernel_ulong_t)<rf216a_chip_info }, + { .name = "ltr308", .driver_data = (kernel_ulong_t)<r308_chip_info }, + { .name = "ltrf216a", .driver_data = (kernel_ulong_t)<rf216a_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltrf216a_id); diff --git a/drivers/iio/light/lv0104cs.c b/drivers/iio/light/lv0104cs.c index 916109ec3217..eba82c334d70 100644 --- a/drivers/iio/light/lv0104cs.c +++ b/drivers/iio/light/lv0104cs.c @@ -510,7 +510,7 @@ static int lv0104cs_probe(struct i2c_client *client) } static const struct i2c_device_id lv0104cs_id[] = { - { "lv0104cs" }, + { .name = "lv0104cs" }, { } }; MODULE_DEVICE_TABLE(i2c, lv0104cs_id); diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index 039d45af3a7f..6594054c40c7 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -598,7 +598,7 @@ static int max44000_probe(struct i2c_client *client) } static const struct i2c_device_id max44000_id[] = { - { "max44000" }, + { .name = "max44000" }, { } }; MODULE_DEVICE_TABLE(i2c, max44000_id); diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 8cd7f5664e5b..82b8a806c5fa 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -534,7 +534,7 @@ static const struct of_device_id max44009_of_match[] = { MODULE_DEVICE_TABLE(of, max44009_of_match); static const struct i2c_device_id max44009_id[] = { - { "max44009" }, + { .name = "max44009" }, { } }; MODULE_DEVICE_TABLE(i2c, max44009_id); diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c index 25f63da70297..6731f1d9ec1c 100644 --- a/drivers/iio/light/noa1305.c +++ b/drivers/iio/light/noa1305.c @@ -315,7 +315,7 @@ static const struct of_device_id noa1305_of_match[] = { MODULE_DEVICE_TABLE(of, noa1305_of_match); static const struct i2c_device_id noa1305_ids[] = { - { "noa1305" }, + { .name = "noa1305" }, { } }; MODULE_DEVICE_TABLE(i2c, noa1305_ids); diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index dac3d3818d0a..03c7a87b4a8e 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -948,8 +948,8 @@ static const struct opt3001_chip_info opt3002_chip_information = { }; static const struct i2c_device_id opt3001_id[] = { - { "opt3001", (kernel_ulong_t)&opt3001_chip_information }, - { "opt3002", (kernel_ulong_t)&opt3002_chip_information }, + { .name = "opt3001", .driver_data = (kernel_ulong_t)&opt3001_chip_information }, + { .name = "opt3002", .driver_data = (kernel_ulong_t)&opt3002_chip_information }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(i2c, opt3001_id); diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 95167273bb90..dd152d921b48 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -438,8 +438,8 @@ static int opt4001_probe(struct i2c_client *client) * opt4001 packaging */ static const struct i2c_device_id opt4001_id[] = { - { "opt4001-sot-5x3", (kernel_ulong_t)&opt4001_sot_5x3_info }, - { "opt4001-picostar", (kernel_ulong_t)&opt4001_picostar_info }, + { .name = "opt4001-sot-5x3", .driver_data = (kernel_ulong_t)&opt4001_sot_5x3_info }, + { .name = "opt4001-picostar", .driver_data = (kernel_ulong_t)&opt4001_picostar_info }, { } }; MODULE_DEVICE_TABLE(i2c, opt4001_id); diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index d6e915ab355d..c391ad3271c6 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -1297,7 +1297,7 @@ static int opt4060_probe(struct i2c_client *client) } static const struct i2c_device_id opt4060_id[] = { - { "opt4060", }, + { .name = "opt4060" }, { } }; MODULE_DEVICE_TABLE(i2c, opt4060_id); diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index 98a1f1624c75..c0f4db8d95f7 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -457,7 +457,7 @@ static const struct acpi_device_id pa12203001_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match); static const struct i2c_device_id pa12203001_id[] = { - { "txcpa122" }, + { .name = "txcpa122" }, { } }; diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 9341c1d58cbe..2ac06dad6d19 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -1102,7 +1102,7 @@ static const struct acpi_device_id rpr0521_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rpr0521_acpi_match); static const struct i2c_device_id rpr0521_id[] = { - { "rpr0521" }, + { .name = "rpr0521" }, { } }; diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 655fa778b4a6..2812a2be99dd 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -1069,7 +1069,7 @@ static int si1133_probe(struct i2c_client *client) } static const struct i2c_device_id si1133_ids[] = { - { "si1133" }, + { .name = "si1133" }, { } }; MODULE_DEVICE_TABLE(i2c, si1133_ids); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index ef0abc4499b7..4601ae5d2009 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1334,13 +1334,13 @@ static int si1145_probe(struct i2c_client *client) } static const struct i2c_device_id si1145_ids[] = { - { "si1132", SI1132 }, - { "si1141", SI1141 }, - { "si1142", SI1142 }, - { "si1143", SI1143 }, - { "si1145", SI1145 }, - { "si1146", SI1146 }, - { "si1147", SI1147 }, + { .name = "si1132", .driver_data = SI1132 }, + { .name = "si1141", .driver_data = SI1141 }, + { .name = "si1142", .driver_data = SI1142 }, + { .name = "si1143", .driver_data = SI1143 }, + { .name = "si1145", .driver_data = SI1145 }, + { .name = "si1146", .driver_data = SI1146 }, + { .name = "si1147", .driver_data = SI1147 }, { } }; MODULE_DEVICE_TABLE(i2c, si1145_ids); diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index 5d9bb4d9be63..ed8cac5b8766 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -46,7 +46,7 @@ static const struct of_device_id st_uvis25_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, st_uvis25_i2c_of_match); static const struct i2c_device_id st_uvis25_i2c_id_table[] = { - { ST_UVIS25_DEV_NAME }, + { .name = ST_UVIS25_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 60407bc87ccf..8380df7ffa98 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -770,10 +770,10 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); static const struct i2c_device_id stk3310_i2c_id[] = { - { "STK3013" }, - { "STK3310" }, - { "STK3311" }, - { "STK3335" }, + { .name = "STK3013" }, + { .name = "STK3310" }, + { .name = "STK3311" }, + { .name = "STK3335" }, { } }; MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 5be461e6dbdb..458178fb629f 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -362,7 +362,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend, tcs3414_resume); static const struct i2c_device_id tcs3414_id[] = { - { "tcs3414" }, + { .name = "tcs3414" }, { } }; MODULE_DEVICE_TABLE(i2c, tcs3414_id); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 12429a3261b3..5a14f8d39aa4 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -597,7 +597,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, tcs3472_resume); static const struct i2c_device_id tcs3472_id[] = { - { "tcs3472" }, + { .name = "tcs3472" }, { } }; MODULE_DEVICE_TABLE(i2c, tcs3472_id); diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 9ba8140c8bc1..244f44379c36 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1900,19 +1900,19 @@ static int tsl2772_resume(struct device *dev) } static const struct i2c_device_id tsl2772_idtable[] = { - { "tsl2571", tsl2571 }, - { "tsl2671", tsl2671 }, - { "tmd2671", tmd2671 }, - { "tsl2771", tsl2771 }, - { "tmd2771", tmd2771 }, - { "tsl2572", tsl2572 }, - { "tsl2672", tsl2672 }, - { "tmd2672", tmd2672 }, - { "tsl2772", tsl2772 }, - { "tmd2772", tmd2772 }, - { "apds9900", apds9900 }, - { "apds9901", apds9900 }, - { "apds9930", apds9930 }, + { .name = "tsl2571", .driver_data = tsl2571 }, + { .name = "tsl2671", .driver_data = tsl2671 }, + { .name = "tmd2671", .driver_data = tmd2671 }, + { .name = "tsl2771", .driver_data = tsl2771 }, + { .name = "tmd2771", .driver_data = tmd2771 }, + { .name = "tsl2572", .driver_data = tsl2572 }, + { .name = "tsl2672", .driver_data = tsl2672 }, + { .name = "tmd2672", .driver_data = tmd2672 }, + { .name = "tsl2772", .driver_data = tsl2772 }, + { .name = "tmd2772", .driver_data = tmd2772 }, + { .name = "apds9900", .driver_data = apds9900 }, + { .name = "apds9901", .driver_data = apds9900 }, + { .name = "apds9930", .driver_data = apds9930 }, { } }; diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c index a5788c09ad02..2f91ed8a9876 100644 --- a/drivers/iio/light/tsl4531.c +++ b/drivers/iio/light/tsl4531.c @@ -227,7 +227,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume); static const struct i2c_device_id tsl4531_id[] = { - { "tsl4531" }, + { .name = "tsl4531" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl4531_id); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index d2f5a44892a8..d335e5e551f1 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -949,7 +949,7 @@ static const struct acpi_device_id us5182d_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match); static const struct i2c_device_id us5182d_id[] = { - { "usd5182" }, + { .name = "usd5182" }, { } }; diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 88fc7424ae35..128ae3f94074 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -2040,13 +2040,13 @@ static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, vcnl4000_runtime_resume, NULL); static const struct i2c_device_id vcnl4000_id[] = { - { "cm36672p", (kernel_ulong_t)&cm36672p_spec }, - { "cm36686", (kernel_ulong_t)&vcnl4040_spec }, - { "vcnl4000", (kernel_ulong_t)&vcnl4000_spec }, - { "vcnl4010", (kernel_ulong_t)&vcnl4010_spec }, - { "vcnl4020", (kernel_ulong_t)&vcnl4010_spec }, - { "vcnl4040", (kernel_ulong_t)&vcnl4040_spec }, - { "vcnl4200", (kernel_ulong_t)&vcnl4200_spec }, + { .name = "cm36672p", .driver_data = (kernel_ulong_t)&cm36672p_spec }, + { .name = "cm36686", .driver_data = (kernel_ulong_t)&vcnl4040_spec }, + { .name = "vcnl4000", .driver_data = (kernel_ulong_t)&vcnl4000_spec }, + { .name = "vcnl4010", .driver_data = (kernel_ulong_t)&vcnl4010_spec }, + { .name = "vcnl4020", .driver_data = (kernel_ulong_t)&vcnl4010_spec }, + { .name = "vcnl4040", .driver_data = (kernel_ulong_t)&vcnl4040_spec }, + { .name = "vcnl4200", .driver_data = (kernel_ulong_t)&vcnl4200_spec }, { } }; MODULE_DEVICE_TABLE(i2c, vcnl4000_id); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 16aeb17067bc..bf3a49b4351d 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -662,7 +662,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4035_pm_ops, vcnl4035_runtime_suspend, vcnl4035_runtime_resume, NULL); static const struct i2c_device_id vcnl4035_id[] = { - { "vcnl4035" }, + { .name = "vcnl4035" }, { } }; MODULE_DEVICE_TABLE(i2c, vcnl4035_id); diff --git a/drivers/iio/light/veml3235.c b/drivers/iio/light/veml3235.c index 9309ad83ca9e..d8a39223009b 100644 --- a/drivers/iio/light/veml3235.c +++ b/drivers/iio/light/veml3235.c @@ -525,7 +525,7 @@ static const struct of_device_id veml3235_of_match[] = { MODULE_DEVICE_TABLE(of, veml3235_of_match); static const struct i2c_device_id veml3235_id[] = { - { "veml3235" }, + { .name = "veml3235" }, { } }; MODULE_DEVICE_TABLE(i2c, veml3235_id); diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 745cf3ad7092..a1b4e7fadf80 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -1214,9 +1214,9 @@ static const struct of_device_id veml6030_of_match[] = { MODULE_DEVICE_TABLE(of, veml6030_of_match); static const struct i2c_device_id veml6030_id[] = { - { "veml6030", (kernel_ulong_t)&veml6030_chip}, - { "veml6035", (kernel_ulong_t)&veml6035_chip}, - { "veml7700", (kernel_ulong_t)&veml7700_chip}, + { .name = "veml6030", .driver_data = (kernel_ulong_t)&veml6030_chip }, + { .name = "veml6035", .driver_data = (kernel_ulong_t)&veml6035_chip }, + { .name = "veml7700", .driver_data = (kernel_ulong_t)&veml7700_chip }, { } }; MODULE_DEVICE_TABLE(i2c, veml6030_id); diff --git a/drivers/iio/light/veml6040.c b/drivers/iio/light/veml6040.c index f563f9f0ee67..0960784b8866 100644 --- a/drivers/iio/light/veml6040.c +++ b/drivers/iio/light/veml6040.c @@ -254,7 +254,7 @@ static int veml6040_probe(struct i2c_client *client) } static const struct i2c_device_id veml6040_id_table[] = { - {"veml6040"}, + { .name = "veml6040" }, { } }; MODULE_DEVICE_TABLE(i2c, veml6040_id_table); diff --git a/drivers/iio/light/veml6046x00.c b/drivers/iio/light/veml6046x00.c index e60f24d46e7b..f23d63291f73 100644 --- a/drivers/iio/light/veml6046x00.c +++ b/drivers/iio/light/veml6046x00.c @@ -1009,7 +1009,7 @@ static const struct of_device_id veml6046x00_of_match[] = { MODULE_DEVICE_TABLE(of, veml6046x00_of_match); static const struct i2c_device_id veml6046x00_id[] = { - { "veml6046x00" }, + { .name = "veml6046x00" }, { } }; MODULE_DEVICE_TABLE(i2c, veml6046x00_id); diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 74d7246e5225..aa7e52628d2a 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -300,7 +300,7 @@ static int veml6070_probe(struct i2c_client *client) } static const struct i2c_device_id veml6070_id[] = { - { "veml6070" }, + { .name = "veml6070" }, { } }; MODULE_DEVICE_TABLE(i2c, veml6070_id); diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c index edbb43407054..105bae7be899 100644 --- a/drivers/iio/light/veml6075.c +++ b/drivers/iio/light/veml6075.c @@ -451,7 +451,7 @@ static int veml6075_probe(struct i2c_client *client) } static const struct i2c_device_id veml6075_id[] = { - { "veml6075" }, + { .name = "veml6075" }, { } }; MODULE_DEVICE_TABLE(i2c, veml6075_id); diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index c1314b144367..6cb965418dba 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -750,7 +750,7 @@ static const struct of_device_id vl6180_of_match[] = { MODULE_DEVICE_TABLE(of, vl6180_of_match); static const struct i2c_device_id vl6180_id[] = { - { "vl6180" }, + { .name = "vl6180" }, { } }; MODULE_DEVICE_TABLE(i2c, vl6180_id); diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index 0990e4d266eb..b53be5f7354e 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -516,7 +516,7 @@ static int zopt2201_probe(struct i2c_client *client) } static const struct i2c_device_id zopt2201_id[] = { - { "zopt2201" }, + { .name = "zopt2201" }, { } }; MODULE_DEVICE_TABLE(i2c, zopt2201_id); diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c index b1768c3aa8f3..352f53edad9a 100644 --- a/drivers/iio/magnetometer/af8133j.c +++ b/drivers/iio/magnetometer/af8133j.c @@ -504,7 +504,7 @@ static const struct of_device_id af8133j_of_match[] = { MODULE_DEVICE_TABLE(of, af8133j_of_match); static const struct i2c_device_id af8133j_id[] = { - { "af8133j" }, + { .name = "af8133j" }, { } }; MODULE_DEVICE_TABLE(i2c, af8133j_id); diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 817b18257608..18dc36945a97 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -1017,10 +1017,10 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ak8974_dev_pm_ops, ak8974_runtime_suspend, ak8974_runtime_resume, NULL); static const struct i2c_device_id ak8974_id[] = { - { "ami305" }, - { "ami306" }, - { "ak8974" }, - { "hscdtd008a" }, + { .name = "ami305" }, + { .name = "ami306" }, + { .name = "ak8974" }, + { .name = "hscdtd008a" }, { } }; MODULE_DEVICE_TABLE(i2c, ak8974_id); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index bb74abb648f1..d045ea091205 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -1132,13 +1132,13 @@ static const struct acpi_device_id ak_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, ak_acpi_match); static const struct i2c_device_id ak8975_id[] = { - {"AK8963", (kernel_ulong_t)&ak_def_array[AK8963] }, - {"ak8963", (kernel_ulong_t)&ak_def_array[AK8963] }, - {"ak8975", (kernel_ulong_t)&ak_def_array[AK8975] }, - {"ak09911", (kernel_ulong_t)&ak_def_array[AK09911] }, - {"ak09912", (kernel_ulong_t)&ak_def_array[AK09912] }, - {"ak09916", (kernel_ulong_t)&ak_def_array[AK09916] }, - {"ak09918", (kernel_ulong_t)&ak_def_array[AK09918] }, + { .name = "AK8963", .driver_data = (kernel_ulong_t)&ak_def_array[AK8963] }, + { .name = "ak8963", .driver_data = (kernel_ulong_t)&ak_def_array[AK8963] }, + { .name = "ak8975", .driver_data = (kernel_ulong_t)&ak_def_array[AK8975] }, + { .name = "ak09911", .driver_data = (kernel_ulong_t)&ak_def_array[AK09911] }, + { .name = "ak09912", .driver_data = (kernel_ulong_t)&ak_def_array[AK09912] }, + { .name = "ak09916", .driver_data = (kernel_ulong_t)&ak_def_array[AK09916] }, + { .name = "ak09918", .driver_data = (kernel_ulong_t)&ak_def_array[AK09918] }, { } }; MODULE_DEVICE_TABLE(i2c, ak8975_id); diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index b110791f688a..7d3cca8cedbe 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -39,9 +39,9 @@ static void bmc150_magn_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id bmc150_magn_i2c_id[] = { - { "bmc150_magn" }, - { "bmc156_magn" }, - { "bmm150_magn" }, + { .name = "bmc150_magn" }, + { .name = "bmc156_magn" }, + { .name = "bmm150_magn" }, { } }; MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id); diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index b41709959e2b..4c454b0057b1 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -71,10 +71,10 @@ static void hmc5843_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id hmc5843_id[] = { - { "hmc5843", HMC5843_ID }, - { "hmc5883", HMC5883_ID }, - { "hmc5883l", HMC5883L_ID }, - { "hmc5983", HMC5983_ID }, + { .name = "hmc5843", .driver_data = HMC5843_ID }, + { .name = "hmc5883", .driver_data = HMC5883_ID }, + { .name = "hmc5883l", .driver_data = HMC5883L_ID }, + { .name = "hmc5983", .driver_data = HMC5983_ID }, { } }; MODULE_DEVICE_TABLE(i2c, hmc5843_id); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index ff09250a06e7..479a12ece8f6 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -619,7 +619,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume); static const struct i2c_device_id mag3110_id[] = { - { "mag3110" }, + { .name = "mag3110" }, { } }; MODULE_DEVICE_TABLE(i2c, mag3110_id); diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index f3d48d03f7c3..bad36c8dd598 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -560,7 +560,7 @@ static const struct acpi_device_id mmc35240_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match); static const struct i2c_device_id mmc35240_id[] = { - { "mmc35240" }, + { .name = "mmc35240" }, { } }; MODULE_DEVICE_TABLE(i2c, mmc35240_id); diff --git a/drivers/iio/magnetometer/mmc5633.c b/drivers/iio/magnetometer/mmc5633.c index 9d8e27486963..f82cb68f9c57 100644 --- a/drivers/iio/magnetometer/mmc5633.c +++ b/drivers/iio/magnetometer/mmc5633.c @@ -532,8 +532,8 @@ static const struct of_device_id mmc5633_of_match[] = { MODULE_DEVICE_TABLE(of, mmc5633_of_match); static const struct i2c_device_id mmc5633_i2c_id[] = { - { "mmc5603" }, - { "mmc5633" }, + { .name = "mmc5603" }, + { .name = "mmc5633" }, { } }; MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id); diff --git a/drivers/iio/magnetometer/si7210.c b/drivers/iio/magnetometer/si7210.c index 2a36abd1c99d..5b3fc3030703 100644 --- a/drivers/iio/magnetometer/si7210.c +++ b/drivers/iio/magnetometer/si7210.c @@ -413,7 +413,7 @@ static int si7210_probe(struct i2c_client *client) } static const struct i2c_device_id si7210_id[] = { - { "si7210" }, + { .name = "si7210" }, { } }; MODULE_DEVICE_TABLE(i2c, si7210_id); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index ed70e782af5e..26d1edd1e779 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -93,15 +93,15 @@ static int st_magn_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id st_magn_id_table[] = { - { LSM303DLH_MAGN_DEV_NAME }, - { LSM303DLHC_MAGN_DEV_NAME }, - { LSM303DLM_MAGN_DEV_NAME }, - { LIS3MDL_MAGN_DEV_NAME }, - { LSM303AGR_MAGN_DEV_NAME }, - { LIS2MDL_MAGN_DEV_NAME }, - { LSM9DS1_MAGN_DEV_NAME }, - { IIS2MDC_MAGN_DEV_NAME }, - { LSM303C_MAGN_DEV_NAME }, + { .name = LSM303DLH_MAGN_DEV_NAME }, + { .name = LSM303DLHC_MAGN_DEV_NAME }, + { .name = LSM303DLM_MAGN_DEV_NAME }, + { .name = LIS3MDL_MAGN_DEV_NAME }, + { .name = LSM303AGR_MAGN_DEV_NAME }, + { .name = LIS2MDL_MAGN_DEV_NAME }, + { .name = LSM9DS1_MAGN_DEV_NAME }, + { .name = IIS2MDC_MAGN_DEV_NAME }, + { .name = LSM303C_MAGN_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/tlv493d.c b/drivers/iio/magnetometer/tlv493d.c index e5e050af2b74..c8eb136cea6f 100644 --- a/drivers/iio/magnetometer/tlv493d.c +++ b/drivers/iio/magnetometer/tlv493d.c @@ -499,7 +499,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(tlv493d_pm_ops, tlv493d_runtime_suspend, tlv493d_runtime_resume, NULL); static const struct i2c_device_id tlv493d_id[] = { - { "tlv493d" }, + { .name = "tlv493d" }, { } }; MODULE_DEVICE_TABLE(i2c, tlv493d_id); diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c index 2adc3c036ab4..155294b66924 100644 --- a/drivers/iio/magnetometer/tmag5273.c +++ b/drivers/iio/magnetometer/tmag5273.c @@ -708,7 +708,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops, NULL); static const struct i2c_device_id tmag5273_id[] = { - { "tmag5273" }, + { .name = "tmag5273" }, { } }; MODULE_DEVICE_TABLE(i2c, tmag5273_id); diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 6bd0535e5eb1..fec084c16b17 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1587,10 +1587,10 @@ static DEFINE_RUNTIME_DEV_PM_OPS(yas5xx_dev_pm_ops, yas5xx_runtime_suspend, yas5xx_runtime_resume, NULL); static const struct i2c_device_id yas5xx_id[] = { - {"yas530", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas530] }, - {"yas532", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] }, - {"yas533", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] }, - {"yas537", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] }, + { .name = "yas530", .driver_data = (kernel_ulong_t)&yas5xx_chip_info_tbl[yas530] }, + { .name = "yas532", .driver_data = (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] }, + { .name = "yas533", .driver_data = (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] }, + { .name = "yas537", .driver_data = (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] }, { } }; MODULE_DEVICE_TABLE(i2c, yas5xx_id); diff --git a/drivers/iio/potentiometer/ad5272.c b/drivers/iio/potentiometer/ad5272.c index 672b1ca3a920..ac342127d59e 100644 --- a/drivers/iio/potentiometer/ad5272.c +++ b/drivers/iio/potentiometer/ad5272.c @@ -204,11 +204,11 @@ static const struct of_device_id ad5272_dt_ids[] = { MODULE_DEVICE_TABLE(of, ad5272_dt_ids); static const struct i2c_device_id ad5272_id[] = { - { "ad5272-020", AD5272_020 }, - { "ad5272-050", AD5272_050 }, - { "ad5272-100", AD5272_100 }, - { "ad5274-020", AD5274_020 }, - { "ad5274-100", AD5274_100 }, + { .name = "ad5272-020", .driver_data = AD5272_020 }, + { .name = "ad5272-050", .driver_data = AD5272_050 }, + { .name = "ad5272-100", .driver_data = AD5272_100 }, + { .name = "ad5274-020", .driver_data = AD5274_020 }, + { .name = "ad5274-100", .driver_data = AD5274_100 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5272_id); diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c index 8a64d93f7e7b..42394343b5a9 100644 --- a/drivers/iio/potentiometer/ds1803.c +++ b/drivers/iio/potentiometer/ds1803.c @@ -235,10 +235,10 @@ static const struct of_device_id ds1803_dt_ids[] = { MODULE_DEVICE_TABLE(of, ds1803_dt_ids); static const struct i2c_device_id ds1803_id[] = { - { "ds1803-010", (kernel_ulong_t)&ds1803_cfg[DS1803_010] }, - { "ds1803-050", (kernel_ulong_t)&ds1803_cfg[DS1803_050] }, - { "ds1803-100", (kernel_ulong_t)&ds1803_cfg[DS1803_100] }, - { "ds3502", (kernel_ulong_t)&ds1803_cfg[DS3502] }, + { .name = "ds1803-010", .driver_data = (kernel_ulong_t)&ds1803_cfg[DS1803_010] }, + { .name = "ds1803-050", .driver_data = (kernel_ulong_t)&ds1803_cfg[DS1803_050] }, + { .name = "ds1803-100", .driver_data = (kernel_ulong_t)&ds1803_cfg[DS1803_100] }, + { .name = "ds3502", .driver_data = (kernel_ulong_t)&ds1803_cfg[DS3502] }, { } }; MODULE_DEVICE_TABLE(i2c, ds1803_id); diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index a42b57733363..77149908e1bf 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -149,10 +149,10 @@ static int tpl0102_probe(struct i2c_client *client) } static const struct i2c_device_id tpl0102_id[] = { - { "cat5140-503", CAT5140_503 }, - { "cat5140-104", CAT5140_104 }, - { "tpl0102-104", TPL0102_104 }, - { "tpl0401-103", TPL0401_103 }, + { .name = "cat5140-503", .driver_data = CAT5140_503 }, + { .name = "cat5140-104", .driver_data = CAT5140_104 }, + { .name = "tpl0102-104", .driver_data = TPL0102_104 }, + { .name = "tpl0401-103", .driver_data = TPL0401_103 }, { } }; MODULE_DEVICE_TABLE(i2c, tpl0102_id); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index eccc2a34358f..359dffa47091 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -403,8 +403,8 @@ static const struct of_device_id lmp91000_of_match[] = { MODULE_DEVICE_TABLE(of, lmp91000_of_match); static const struct i2c_device_id lmp91000_id[] = { - { "lmp91000" }, - { "lmp91002" }, + { .name = "lmp91000" }, + { .name = "lmp91002" }, { } }; MODULE_DEVICE_TABLE(i2c, lmp91000_id); diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c index 699b0fd64985..fd48bed35088 100644 --- a/drivers/iio/pressure/abp060mg.c +++ b/drivers/iio/pressure/abp060mg.c @@ -209,44 +209,66 @@ static int abp060mg_probe(struct i2c_client *client) static const struct i2c_device_id abp060mg_id_table[] = { /* mbar & kPa variants (abp060m [60 mbar] == abp006k [6 kPa]) */ /* gage: */ - { "abp060mg", ABP006KG }, { "abp006kg", ABP006KG }, - { "abp100mg", ABP010KG }, { "abp010kg", ABP010KG }, - { "abp160mg", ABP016KG }, { "abp016kg", ABP016KG }, - { "abp250mg", ABP025KG }, { "abp025kg", ABP025KG }, - { "abp400mg", ABP040KG }, { "abp040kg", ABP040KG }, - { "abp600mg", ABP060KG }, { "abp060kg", ABP060KG }, - { "abp001bg", ABP100KG }, { "abp100kg", ABP100KG }, - { "abp1_6bg", ABP160KG }, { "abp160kg", ABP160KG }, - { "abp2_5bg", ABP250KG }, { "abp250kg", ABP250KG }, - { "abp004bg", ABP400KG }, { "abp400kg", ABP400KG }, - { "abp006bg", ABP600KG }, { "abp600kg", ABP600KG }, - { "abp010bg", ABP001GG }, { "abp001gg", ABP001GG }, + { .name = "abp060mg", .driver_data = ABP006KG }, + { .name = "abp006kg", .driver_data = ABP006KG }, + { .name = "abp100mg", .driver_data = ABP010KG }, + { .name = "abp010kg", .driver_data = ABP010KG }, + { .name = "abp160mg", .driver_data = ABP016KG }, + { .name = "abp016kg", .driver_data = ABP016KG }, + { .name = "abp250mg", .driver_data = ABP025KG }, + { .name = "abp025kg", .driver_data = ABP025KG }, + { .name = "abp400mg", .driver_data = ABP040KG }, + { .name = "abp040kg", .driver_data = ABP040KG }, + { .name = "abp600mg", .driver_data = ABP060KG }, + { .name = "abp060kg", .driver_data = ABP060KG }, + { .name = "abp001bg", .driver_data = ABP100KG }, + { .name = "abp100kg", .driver_data = ABP100KG }, + { .name = "abp1_6bg", .driver_data = ABP160KG }, + { .name = "abp160kg", .driver_data = ABP160KG }, + { .name = "abp2_5bg", .driver_data = ABP250KG }, + { .name = "abp250kg", .driver_data = ABP250KG }, + { .name = "abp004bg", .driver_data = ABP400KG }, + { .name = "abp400kg", .driver_data = ABP400KG }, + { .name = "abp006bg", .driver_data = ABP600KG }, + { .name = "abp600kg", .driver_data = ABP600KG }, + { .name = "abp010bg", .driver_data = ABP001GG }, + { .name = "abp001gg", .driver_data = ABP001GG }, /* differential: */ - { "abp060md", ABP006KD }, { "abp006kd", ABP006KD }, - { "abp100md", ABP010KD }, { "abp010kd", ABP010KD }, - { "abp160md", ABP016KD }, { "abp016kd", ABP016KD }, - { "abp250md", ABP025KD }, { "abp025kd", ABP025KD }, - { "abp400md", ABP040KD }, { "abp040kd", ABP040KD }, - { "abp600md", ABP060KD }, { "abp060kd", ABP060KD }, - { "abp001bd", ABP100KD }, { "abp100kd", ABP100KD }, - { "abp1_6bd", ABP160KD }, { "abp160kd", ABP160KD }, - { "abp2_5bd", ABP250KD }, { "abp250kd", ABP250KD }, - { "abp004bd", ABP400KD }, { "abp400kd", ABP400KD }, + { .name = "abp060md", .driver_data = ABP006KD }, + { .name = "abp006kd", .driver_data = ABP006KD }, + { .name = "abp100md", .driver_data = ABP010KD }, + { .name = "abp010kd", .driver_data = ABP010KD }, + { .name = "abp160md", .driver_data = ABP016KD }, + { .name = "abp016kd", .driver_data = ABP016KD }, + { .name = "abp250md", .driver_data = ABP025KD }, + { .name = "abp025kd", .driver_data = ABP025KD }, + { .name = "abp400md", .driver_data = ABP040KD }, + { .name = "abp040kd", .driver_data = ABP040KD }, + { .name = "abp600md", .driver_data = ABP060KD }, + { .name = "abp060kd", .driver_data = ABP060KD }, + { .name = "abp001bd", .driver_data = ABP100KD }, + { .name = "abp100kd", .driver_data = ABP100KD }, + { .name = "abp1_6bd", .driver_data = ABP160KD }, + { .name = "abp160kd", .driver_data = ABP160KD }, + { .name = "abp2_5bd", .driver_data = ABP250KD }, + { .name = "abp250kd", .driver_data = ABP250KD }, + { .name = "abp004bd", .driver_data = ABP400KD }, + { .name = "abp400kd", .driver_data = ABP400KD }, /* psi variants */ /* gage: */ - { "abp001pg", ABP001PG }, - { "abp005pg", ABP005PG }, - { "abp015pg", ABP015PG }, - { "abp030pg", ABP030PG }, - { "abp060pg", ABP060PG }, - { "abp100pg", ABP100PG }, - { "abp150pg", ABP150PG }, + { .name = "abp001pg", .driver_data = ABP001PG }, + { .name = "abp005pg", .driver_data = ABP005PG }, + { .name = "abp015pg", .driver_data = ABP015PG }, + { .name = "abp030pg", .driver_data = ABP030PG }, + { .name = "abp060pg", .driver_data = ABP060PG }, + { .name = "abp100pg", .driver_data = ABP100PG }, + { .name = "abp150pg", .driver_data = ABP150PG }, /* differential: */ - { "abp001pd", ABP001PD }, - { "abp005pd", ABP005PD }, - { "abp015pd", ABP015PD }, - { "abp030pd", ABP030PD }, - { "abp060pd", ABP060PD }, + { .name = "abp001pd", .driver_data = ABP001PD }, + { .name = "abp005pd", .driver_data = ABP005PD }, + { .name = "abp015pd", .driver_data = ABP015PD }, + { .name = "abp030pd", .driver_data = ABP030PD }, + { .name = "abp060pd", .driver_data = ABP060PD }, { } }; MODULE_DEVICE_TABLE(i2c, abp060mg_id_table); diff --git a/drivers/iio/pressure/abp2030pa_i2c.c b/drivers/iio/pressure/abp2030pa_i2c.c index 9f1c1c8a9afb..e71dc8e8e957 100644 --- a/drivers/iio/pressure/abp2030pa_i2c.c +++ b/drivers/iio/pressure/abp2030pa_i2c.c @@ -69,7 +69,7 @@ static const struct of_device_id abp2_i2c_match[] = { MODULE_DEVICE_TABLE(of, abp2_i2c_match); static const struct i2c_device_id abp2_i2c_id[] = { - { "abp2030pa" }, + { .name = "abp2030pa" }, { } }; MODULE_DEVICE_TABLE(i2c, abp2_i2c_id); diff --git a/drivers/iio/pressure/adp810.c b/drivers/iio/pressure/adp810.c index 5282612d1309..47c5ad564c7f 100644 --- a/drivers/iio/pressure/adp810.c +++ b/drivers/iio/pressure/adp810.c @@ -199,7 +199,7 @@ static int adp810_probe(struct i2c_client *client) } static const struct i2c_device_id adp810_id_table[] = { - { "adp810" }, + { .name = "adp810" }, { } }; MODULE_DEVICE_TABLE(i2c, adp810_id_table); diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 8e459b6c97ff..3f6e0723a9d7 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -38,12 +38,12 @@ static const struct of_device_id bmp280_of_i2c_match[] = { MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp085", (kernel_ulong_t)&bmp085_chip_info }, - {"bmp180", (kernel_ulong_t)&bmp180_chip_info }, - {"bmp280", (kernel_ulong_t)&bmp280_chip_info }, - {"bme280", (kernel_ulong_t)&bme280_chip_info }, - {"bmp380", (kernel_ulong_t)&bmp380_chip_info }, - {"bmp580", (kernel_ulong_t)&bmp580_chip_info }, + { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info }, + { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info }, + { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info }, + { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info }, + { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info }, + { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 46feb27fe632..01a873165923 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -340,8 +340,8 @@ static const struct of_device_id dlh_of_match[] = { MODULE_DEVICE_TABLE(of, dlh_of_match); static const struct i2c_device_id dlh_id[] = { - { "dlhl60d", (kernel_ulong_t)&dlhl60d_info }, - { "dlhl60g", (kernel_ulong_t)&dlhl60g_info }, + { .name = "dlhl60d", .driver_data = (kernel_ulong_t)&dlhl60d_info }, + { .name = "dlhl60g", .driver_data = (kernel_ulong_t)&dlhl60g_info }, { } }; MODULE_DEVICE_TABLE(i2c, dlh_id); diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 8edaa4d10a70..f45af72a0554 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -887,7 +887,7 @@ static int dps310_probe(struct i2c_client *client) } static const struct i2c_device_id dps310_id[] = { - { DPS310_DEV_NAME }, + { .name = DPS310_DEV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, dps310_id); diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c index cbb4aaf45e2c..424523345060 100644 --- a/drivers/iio/pressure/hp03.c +++ b/drivers/iio/pressure/hp03.c @@ -266,7 +266,7 @@ static int hp03_probe(struct i2c_client *client) } static const struct i2c_device_id hp03_id[] = { - { "hp03" }, + { .name = "hp03" }, { } }; MODULE_DEVICE_TABLE(i2c, hp03_id); diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c index abe10ccb6770..be14202855cf 100644 --- a/drivers/iio/pressure/hp206c.c +++ b/drivers/iio/pressure/hp206c.c @@ -395,7 +395,7 @@ static int hp206c_probe(struct i2c_client *client) } static const struct i2c_device_id hp206c_id[] = { - {"hp206c"}, + { .name = "hp206c" }, { } }; MODULE_DEVICE_TABLE(i2c, hp206c_id); diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c index 3500bda03d75..f4ea30b2980d 100644 --- a/drivers/iio/pressure/hsc030pa_i2c.c +++ b/drivers/iio/pressure/hsc030pa_i2c.c @@ -58,7 +58,7 @@ static const struct of_device_id hsc_i2c_match[] = { MODULE_DEVICE_TABLE(of, hsc_i2c_match); static const struct i2c_device_id hsc_i2c_id[] = { - { "hsc030pa" }, + { .name = "hsc030pa" }, { } }; MODULE_DEVICE_TABLE(i2c, hsc_i2c_id); diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c index 3d83d0098a57..02b363c5c45b 100644 --- a/drivers/iio/pressure/icp10100.c +++ b/drivers/iio/pressure/icp10100.c @@ -633,7 +633,7 @@ static const struct of_device_id icp10100_of_match[] = { MODULE_DEVICE_TABLE(of, icp10100_of_match); static const struct i2c_device_id icp10100_id[] = { - { "icp10100" }, + { .name = "icp10100" }, { } }; MODULE_DEVICE_TABLE(i2c, icp10100_id); diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c index 3db9ef4e2770..4a43eba078a8 100644 --- a/drivers/iio/pressure/mpl115_i2c.c +++ b/drivers/iio/pressure/mpl115_i2c.c @@ -45,7 +45,7 @@ static int mpl115_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id mpl115_i2c_id[] = { - { "mpl115" }, + { .name = "mpl115" }, { } }; MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index aeac1586f12e..f20fa6ad16fe 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -783,7 +783,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume); static const struct i2c_device_id mpl3115_id[] = { - { "mpl3115" }, + { .name = "mpl3115" }, { } }; MODULE_DEVICE_TABLE(i2c, mpl3115_id); diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c index 0fe8cfe0d7e7..92edaf3005eb 100644 --- a/drivers/iio/pressure/mprls0025pa_i2c.c +++ b/drivers/iio/pressure/mprls0025pa_i2c.c @@ -69,7 +69,7 @@ static const struct of_device_id mpr_i2c_match[] = { MODULE_DEVICE_TABLE(of, mpr_i2c_match); static const struct i2c_device_id mpr_i2c_id[] = { - { "mprls0025pa" }, + { .name = "mprls0025pa" }, { } }; MODULE_DEVICE_TABLE(i2c, mpr_i2c_id); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 1c041b9085fb..b5be6a6daf02 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -113,8 +113,8 @@ static const struct of_device_id ms5611_i2c_matches[] = { MODULE_DEVICE_TABLE(of, ms5611_i2c_matches); static const struct i2c_device_id ms5611_id[] = { - { "ms5611", MS5611 }, - { "ms5607", MS5607 }, + { .name = "ms5611", .driver_data = MS5611 }, + { .name = "ms5607", .driver_data = MS5607 }, { } }; MODULE_DEVICE_TABLE(i2c, ms5611_id); diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index 59705a666979..03945a4fc718 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -215,10 +215,10 @@ static const struct ms_tp_data ms8607_data = { }; static const struct i2c_device_id ms5637_id[] = { - {"ms5637", (kernel_ulong_t)&ms5637_data }, - {"ms5805", (kernel_ulong_t)&ms5805_data }, - {"ms5837", (kernel_ulong_t)&ms5837_data }, - {"ms8607-temppressure", (kernel_ulong_t)&ms8607_data }, + { .name = "ms5637", .driver_data = (kernel_ulong_t)&ms5637_data }, + { .name = "ms5805", .driver_data = (kernel_ulong_t)&ms5805_data }, + { .name = "ms5837", .driver_data = (kernel_ulong_t)&ms5837_data }, + { .name = "ms8607-temppressure", .driver_data = (kernel_ulong_t)&ms8607_data }, { } }; MODULE_DEVICE_TABLE(i2c, ms5637_id); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index 08146ca0f91d..b3be9de03678 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -888,7 +888,7 @@ static const struct of_device_id bm1390_of_match[] = { MODULE_DEVICE_TABLE(of, bm1390_of_match); static const struct i2c_device_id bm1390_id[] = { - { "bm1390glv-z", }, + { .name = "bm1390glv-z" }, { } }; MODULE_DEVICE_TABLE(i2c, bm1390_id); diff --git a/drivers/iio/pressure/sdp500.c b/drivers/iio/pressure/sdp500.c index 9828c73c4855..ba80dc21faad 100644 --- a/drivers/iio/pressure/sdp500.c +++ b/drivers/iio/pressure/sdp500.c @@ -130,7 +130,7 @@ static int sdp500_probe(struct i2c_client *client) } static const struct i2c_device_id sdp500_id[] = { - { "sdp500" }, + { .name = "sdp500" }, { } }; MODULE_DEVICE_TABLE(i2c, sdp500_id); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 0f50bac1fb4d..816bfcfd62ae 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -61,14 +61,14 @@ static const struct acpi_device_id st_press_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, st_press_acpi_match); static const struct i2c_device_id st_press_id_table[] = { - { LPS001WP_PRESS_DEV_NAME, LPS001WP }, - { LPS25H_PRESS_DEV_NAME, LPS25H }, - { LPS331AP_PRESS_DEV_NAME, LPS331AP }, - { LPS22HB_PRESS_DEV_NAME, LPS22HB }, - { LPS33HW_PRESS_DEV_NAME, LPS33HW }, - { LPS35HW_PRESS_DEV_NAME, LPS35HW }, - { LPS22HH_PRESS_DEV_NAME, LPS22HH }, - { LPS22DF_PRESS_DEV_NAME, LPS22DF }, + { .name = LPS001WP_PRESS_DEV_NAME, .driver_data = LPS001WP }, + { .name = LPS25H_PRESS_DEV_NAME, .driver_data = LPS25H }, + { .name = LPS331AP_PRESS_DEV_NAME, .driver_data = LPS331AP }, + { .name = LPS22HB_PRESS_DEV_NAME, .driver_data = LPS22HB }, + { .name = LPS33HW_PRESS_DEV_NAME, .driver_data = LPS33HW }, + { .name = LPS35HW_PRESS_DEV_NAME, .driver_data = LPS35HW }, + { .name = LPS22HH_PRESS_DEV_NAME, .driver_data = LPS22HH }, + { .name = LPS22DF_PRESS_DEV_NAME, .driver_data = LPS22DF }, { } }; MODULE_DEVICE_TABLE(i2c, st_press_id_table); diff --git a/drivers/iio/pressure/t5403.c b/drivers/iio/pressure/t5403.c index c7cb0fd816d3..ab30f6745181 100644 --- a/drivers/iio/pressure/t5403.c +++ b/drivers/iio/pressure/t5403.c @@ -251,7 +251,7 @@ static int t5403_probe(struct i2c_client *client) } static const struct i2c_device_id t5403_id[] = { - { "t5403" }, + { .name = "t5403" }, { } }; MODULE_DEVICE_TABLE(i2c, t5403_id); diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c index a6034bf05d97..2d8af33f6a29 100644 --- a/drivers/iio/pressure/zpa2326_i2c.c +++ b/drivers/iio/pressure/zpa2326_i2c.c @@ -58,7 +58,7 @@ static void zpa2326_remove_i2c(struct i2c_client *client) } static const struct i2c_device_id zpa2326_i2c_ids[] = { - { "zpa2326" }, + { .name = "zpa2326" }, { } }; MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids); diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index 3472a2c36e44..8fbb755dcae0 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -825,8 +825,8 @@ static const struct of_device_id aw96103_dt_match[] = { MODULE_DEVICE_TABLE(of, aw96103_dt_match); static const struct i2c_device_id aw96103_i2c_id[] = { - { "aw96103", (kernel_ulong_t)&aw_chip_info_tbl[AW96103_VAL] }, - { "aw96105", (kernel_ulong_t)&aw_chip_info_tbl[AW96105_VAL] }, + { .name = "aw96103", .driver_data = (kernel_ulong_t)&aw_chip_info_tbl[AW96103_VAL] }, + { .name = "aw96105", .driver_data = (kernel_ulong_t)&aw_chip_info_tbl[AW96105_VAL] }, { } }; MODULE_DEVICE_TABLE(i2c, aw96103_i2c_id); diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index 9efaa5b6b5bd..c3a93c0e2b64 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1199,7 +1199,7 @@ static const struct of_device_id hx9023s_of_match[] = { MODULE_DEVICE_TABLE(of, hx9023s_of_match); static const struct i2c_device_id hx9023s_id[] = { - { "hx9023s" }, + { .name = "hx9023s" }, { } }; MODULE_DEVICE_TABLE(i2c, hx9023s_id); diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index f69db6f2f380..016626f21218 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -995,7 +995,7 @@ static int isl29501_probe(struct i2c_client *client) } static const struct i2c_device_id isl29501_id[] = { - { "isl29501" }, + { .name = "isl29501" }, { } }; diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index 34b49c54e68b..1e8ecb9e9c56 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -244,13 +244,13 @@ static const struct of_device_id of_mb1232_match[] = { MODULE_DEVICE_TABLE(of, of_mb1232_match); static const struct i2c_device_id mb1232_id[] = { - { "maxbotix-mb1202", }, - { "maxbotix-mb1212", }, - { "maxbotix-mb1222", }, - { "maxbotix-mb1232", }, - { "maxbotix-mb1242", }, - { "maxbotix-mb7040", }, - { "maxbotix-mb7137", }, + { .name = "maxbotix-mb1202" }, + { .name = "maxbotix-mb1212" }, + { .name = "maxbotix-mb1222" }, + { .name = "maxbotix-mb1232" }, + { .name = "maxbotix-mb1242" }, + { .name = "maxbotix-mb7040" }, + { .name = "maxbotix-mb7137" }, { } }; MODULE_DEVICE_TABLE(i2c, mb1232_id); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 21336b8f122a..5e9e04540393 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -319,8 +319,8 @@ static void lidar_remove(struct i2c_client *client) } static const struct i2c_device_id lidar_id[] = { - { "lidar-lite-v2" }, - { "lidar-lite-v3" }, + { .name = "lidar-lite-v2" }, + { .name = "lidar-lite-v3" }, { } }; MODULE_DEVICE_TABLE(i2c, lidar_id); diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c index 81b8daf17a54..db1e94fc5658 100644 --- a/drivers/iio/proximity/rfd77402.c +++ b/drivers/iio/proximity/rfd77402.c @@ -433,7 +433,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume); static const struct i2c_device_id rfd77402_id[] = { - { "rfd77402" }, + { .name = "rfd77402" }, { } }; MODULE_DEVICE_TABLE(i2c, rfd77402_id); diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index 92a37ba331f6..35eb3fd7e700 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -530,9 +530,9 @@ static const struct of_device_id of_srf08_match[] = { MODULE_DEVICE_TABLE(of, of_srf08_match); static const struct i2c_device_id srf08_id[] = { - { "srf02", SRF02 }, - { "srf08", SRF08 }, - { "srf10", SRF10 }, + { .name = "srf02", .driver_data = SRF02 }, + { .name = "srf08", .driver_data = SRF08 }, + { .name = "srf10", .driver_data = SRF10 }, { } }; MODULE_DEVICE_TABLE(i2c, srf08_id); diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index fb02eac78ed4..602f7b95c83e 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -1007,8 +1007,8 @@ static const struct of_device_id sx9310_of_match[] = { MODULE_DEVICE_TABLE(of, sx9310_of_match); static const struct i2c_device_id sx9310_id[] = { - { "sx9310", (kernel_ulong_t)&sx9310_info }, - { "sx9311", (kernel_ulong_t)&sx9311_info }, + { .name = "sx9310", .driver_data = (kernel_ulong_t)&sx9310_info }, + { .name = "sx9311", .driver_data = (kernel_ulong_t)&sx9311_info }, { } }; MODULE_DEVICE_TABLE(i2c, sx9310_id); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 6c67bae7488c..dadce9b44227 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -1025,7 +1025,7 @@ static const struct of_device_id sx9500_of_match[] = { MODULE_DEVICE_TABLE(of, sx9500_of_match); static const struct i2c_device_id sx9500_id[] = { - { "sx9500" }, + { .name = "sx9500" }, { } }; MODULE_DEVICE_TABLE(i2c, sx9500_id); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index ad3e46d47fa8..21579331c6c3 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -393,7 +393,7 @@ static int vl53l0x_probe(struct i2c_client *client) } static const struct i2c_device_id vl53l0x_id[] = { - { "vl53l0x" }, + { .name = "vl53l0x" }, { } }; MODULE_DEVICE_TABLE(i2c, vl53l0x_id); diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c index 4d9cb3983dba..ff56bfcf8bd2 100644 --- a/drivers/iio/proximity/vl53l1x-i2c.c +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -730,7 +730,7 @@ static int vl53l1x_probe(struct i2c_client *client) } static const struct i2c_device_id vl53l1x_id[] = { - { "vl53l1x" }, + { .name = "vl53l1x" }, { } }; MODULE_DEVICE_TABLE(i2c, vl53l1x_id); diff --git a/drivers/iio/temperature/max30208.c b/drivers/iio/temperature/max30208.c index 720469f9dc36..6172abc6d533 100644 --- a/drivers/iio/temperature/max30208.c +++ b/drivers/iio/temperature/max30208.c @@ -218,7 +218,7 @@ static int max30208_probe(struct i2c_client *i2c) } static const struct i2c_device_id max30208_id_table[] = { - { "max30208" }, + { .name = "max30208" }, { } }; MODULE_DEVICE_TABLE(i2c, max30208_id_table); diff --git a/drivers/iio/temperature/mcp9600.c b/drivers/iio/temperature/mcp9600.c index aa42c2b1a369..2091733738b7 100644 --- a/drivers/iio/temperature/mcp9600.c +++ b/drivers/iio/temperature/mcp9600.c @@ -551,8 +551,8 @@ static const struct mcp_chip_info mcp9601_chip_info = { }; static const struct i2c_device_id mcp9600_id[] = { - { "mcp9600", .driver_data = (kernel_ulong_t)&mcp9600_chip_info }, - { "mcp9601", .driver_data = (kernel_ulong_t)&mcp9601_chip_info }, + { .name = "mcp9600", .driver_data = (kernel_ulong_t)&mcp9600_chip_info }, + { .name = "mcp9601", .driver_data = (kernel_ulong_t)&mcp9601_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, mcp9600_id); diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index 1ad21b73e1b4..342f2e4f80d1 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -699,8 +699,8 @@ static const struct mlx_chip_info mlx90615_chip_info = { }; static const struct i2c_device_id mlx90614_id[] = { - { "mlx90614", .driver_data = (kernel_ulong_t)&mlx90614_chip_info }, - { "mlx90615", .driver_data = (kernel_ulong_t)&mlx90615_chip_info }, + { .name = "mlx90614", .driver_data = (kernel_ulong_t)&mlx90614_chip_info }, + { .name = "mlx90615", .driver_data = (kernel_ulong_t)&mlx90615_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, mlx90614_id); diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c index b44f7036c2cc..3ab7687c4146 100644 --- a/drivers/iio/temperature/mlx90632.c +++ b/drivers/iio/temperature/mlx90632.c @@ -1276,7 +1276,7 @@ static int mlx90632_probe(struct i2c_client *client) } static const struct i2c_device_id mlx90632_id[] = { - { "mlx90632" }, + { .name = "mlx90632" }, { } }; MODULE_DEVICE_TABLE(i2c, mlx90632_id); diff --git a/drivers/iio/temperature/mlx90635.c b/drivers/iio/temperature/mlx90635.c index 1c8948ca54df..8c8bdab106dd 100644 --- a/drivers/iio/temperature/mlx90635.c +++ b/drivers/iio/temperature/mlx90635.c @@ -1026,7 +1026,7 @@ static int mlx90635_probe(struct i2c_client *client) } static const struct i2c_device_id mlx90635_id[] = { - { "mlx90635" }, + { .name = "mlx90635" }, { } }; MODULE_DEVICE_TABLE(i2c, mlx90635_id); diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index d8d8c8936d17..9939b9cd4796 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -391,7 +391,7 @@ static const struct of_device_id tmp006_of_match[] = { MODULE_DEVICE_TABLE(of, tmp006_of_match); static const struct i2c_device_id tmp006_id[] = { - { "tmp006" }, + { .name = "tmp006" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp006_id); diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index 043283b02c4d..d9eea06ff540 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -563,7 +563,7 @@ static const struct of_device_id tmp007_of_match[] = { MODULE_DEVICE_TABLE(of, tmp007_of_match); static const struct i2c_device_id tmp007_id[] = { - { "tmp007" }, + { .name = "tmp007" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp007_id); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c index 8972083d903a..6bc18616ad15 100644 --- a/drivers/iio/temperature/tmp117.c +++ b/drivers/iio/temperature/tmp117.c @@ -209,8 +209,8 @@ static const struct of_device_id tmp117_of_match[] = { MODULE_DEVICE_TABLE(of, tmp117_of_match); static const struct i2c_device_id tmp117_id[] = { - { "tmp116", (kernel_ulong_t)&tmp116_channels_info }, - { "tmp117", (kernel_ulong_t)&tmp117_channels_info }, + { .name = "tmp116", .driver_data = (kernel_ulong_t)&tmp116_channels_info }, + { .name = "tmp117", .driver_data = (kernel_ulong_t)&tmp117_channels_info }, { } }; MODULE_DEVICE_TABLE(i2c, tmp117_id); diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 334bba6fdae6..92c73369b06f 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -206,7 +206,7 @@ static int tsys01_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id tsys01_id[] = { - { "tsys01" }, + { .name = "tsys01" }, { } }; MODULE_DEVICE_TABLE(i2c, tsys01_id); diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index 0cad27205667..3ef72347456e 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -168,7 +168,7 @@ static int tsys02d_probe(struct i2c_client *client) } static const struct i2c_device_id tsys02d_id[] = { - { "tsys02d" }, + { .name = "tsys02d" }, { } }; MODULE_DEVICE_TABLE(i2c, tsys02d_id); From f9f99197a88260046b56cf4d24e51aff849bd45b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 21 May 2026 20:25:29 -0500 Subject: [PATCH 2372/5214] iio: humidity: ens210: remove compiler warning workaround Rewrite IIO_CHAN_INFO_RAW case to avoid needing to add unreachable code to work around a compiler warning. When scoped_guard() was first introduced, compilers could not see when it returned unconditionally from inside the hidden for loop. This has since been fixed in the macro definition. So removing the `return -EINVAL` should be enough. Still, we can improve readability, decrease indentation and avoid the hidden for loop by rewriting the case without scoped_guard(). Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/ens210.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/iio/humidity/ens210.c b/drivers/iio/humidity/ens210.c index 22ad208e6aa6..49543fc389bf 100644 --- a/drivers/iio/humidity/ens210.c +++ b/drivers/iio/humidity/ens210.c @@ -149,15 +149,16 @@ static int ens210_read_raw(struct iio_dev *indio_dev, int ret; switch (mask) { - case IIO_CHAN_INFO_RAW: - scoped_guard(mutex, &data->lock) { - ret = ens210_get_measurement( - indio_dev, channel->type == IIO_TEMP, val); - if (ret) - return ret; - return IIO_VAL_INT; - } - return -EINVAL; /* compiler warning workaround */ + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&data->lock); + + ret = ens210_get_measurement(indio_dev, channel->type == IIO_TEMP, + val); + if (ret) + return ret; + + return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: if (channel->type == IIO_TEMP) { *val = 15; From f9242e31d16dec9958d0b65d8071823e2efdd9f6 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 21 May 2026 17:30:43 -0500 Subject: [PATCH 2373/5214] iio: imu: kmx61: Use guard(mutex)() over manual locking Include linux/cleanup.h to take advantage of new macros. Replace manual mutex_lock() and mutex_unlock() calls across the file with guard(mutex)() and scoped_guard() where appropriate to simplify error paths and eliminate manual locking calls. Add new helper function kmx61_read_for_each_active_channel() to mitigate certain style issues and to prevent notifying that the IRQ is finished whilst holding the lock. Update certain returns, and add default case to return -EINVAL in kmx61_read_raw(). Remove now-redundant gotos and ret variables, as the new RAII macros make them unneeded. Signed-off-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/imu/kmx61.c | 129 +++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index a4d176f81f0e..b8a8297b39af 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -7,6 +7,7 @@ * IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F). */ +#include #include #include #include @@ -783,7 +784,7 @@ static int kmx61_read_raw(struct iio_dev *indio_dev, struct kmx61_data *data = kmx61_get_data(indio_dev); switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { switch (chan->type) { case IIO_ACCEL: base_reg = KMX61_ACC_XOUT_L; @@ -794,28 +795,24 @@ static int kmx61_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = kmx61_set_power_state(data, true, chan->address); - if (ret) { - mutex_unlock(&data->lock); + if (ret) return ret; - } ret = kmx61_read_measurement(data, base_reg, chan->scan_index); if (ret < 0) { kmx61_set_power_state(data, false, chan->address); - mutex_unlock(&data->lock); return ret; } *val = sign_extend32(ret >> chan->scan_type.shift, chan->scan_type.realbits - 1); ret = kmx61_set_power_state(data, false, chan->address); - - mutex_unlock(&data->lock); if (ret) return ret; return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL: @@ -830,45 +827,46 @@ static int kmx61_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_SAMP_FREQ: { if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) return -EINVAL; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + ret = kmx61_get_odr(data, val, val2, chan->address); - mutex_unlock(&data->lock); if (ret) return -EINVAL; return IIO_VAL_INT_PLUS_MICRO; } - return -EINVAL; + default: + return -EINVAL; + } } static int kmx61_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int ret; struct kmx61_data *data = kmx61_get_data(indio_dev); switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_SAMP_FREQ: { if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN) return -EINVAL; - mutex_lock(&data->lock); - ret = kmx61_set_odr(data, val, val2, chan->address); - mutex_unlock(&data->lock); - return ret; + guard(mutex)(&data->lock); + + return kmx61_set_odr(data, val, val2, chan->address); + } case IIO_CHAN_INFO_SCALE: switch (chan->type) { - case IIO_ACCEL: + case IIO_ACCEL: { if (val != 0) return -EINVAL; - mutex_lock(&data->lock); - ret = kmx61_set_scale(data, val2); - mutex_unlock(&data->lock); - return ret; + guard(mutex)(&data->lock); + + return kmx61_set_scale(data, val2); + } default: return -EINVAL; } @@ -945,29 +943,26 @@ static int kmx61_write_event_config(struct iio_dev *indio_dev, if (state && data->ev_enable_state) return 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); if (!state && data->motion_trig_on) { data->ev_enable_state = false; - goto err_unlock; + return ret; } ret = kmx61_set_power_state(data, state, KMX61_ACC); if (ret < 0) - goto err_unlock; + return ret; ret = kmx61_setup_any_motion_interrupt(data, state); if (ret < 0) { kmx61_set_power_state(data, false, KMX61_ACC); - goto err_unlock; + return ret; } data->ev_enable_state = state; -err_unlock: - mutex_unlock(&data->lock); - - return ret; + return 0; } static int kmx61_acc_validate_trigger(struct iio_dev *indio_dev, @@ -1020,11 +1015,11 @@ static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig, struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct kmx61_data *data = kmx61_get_data(indio_dev); - mutex_lock(&data->lock); + guard(mutex)(&data->lock); if (!state && data->ev_enable_state && data->motion_trig_on) { data->motion_trig_on = false; - goto err_unlock; + return ret; } if (data->acc_dready_trig == trig || data->motion_trig == trig) @@ -1034,7 +1029,7 @@ static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig, ret = kmx61_set_power_state(data, state, device); if (ret < 0) - goto err_unlock; + return ret; if (data->acc_dready_trig == trig || data->mag_dready_trig == trig) ret = kmx61_setup_new_data_interrupt(data, state, device); @@ -1042,7 +1037,7 @@ static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig, ret = kmx61_setup_any_motion_interrupt(data, state); if (ret < 0) { kmx61_set_power_state(data, false, device); - goto err_unlock; + return ret; } if (data->acc_dready_trig == trig) @@ -1051,10 +1046,8 @@ static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig, data->mag_dready_trig_on = state; else data->motion_trig_on = state; -err_unlock: - mutex_unlock(&data->lock); - return ret; + return 0; } static void kmx61_trig_reenable(struct iio_trigger *trig) @@ -1181,30 +1174,49 @@ static irqreturn_t kmx61_data_rdy_trig_poll(int irq, void *private) return IRQ_HANDLED; } -static irqreturn_t kmx61_trigger_handler(int irq, void *p) +/** + * kmx61_read_for_each_active_channel() - Read each active channel into a buffer + * + * @indio_dev: IIO Device struct to read from + * @buffer: Destination buffer to write to, the array must be of at least size 8 + * + * Return: + * 0 on success, negative errno on failure. + */ +static int kmx61_read_for_each_active_channel(struct iio_dev *indio_dev, s16 *buffer) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; struct kmx61_data *data = kmx61_get_data(indio_dev); - int bit, ret, i = 0; u8 base; - s16 buffer[8] = { }; + int ret, bit; + int i = 0; if (indio_dev == data->acc_indio_dev) base = KMX61_ACC_XOUT_L; else base = KMX61_MAG_XOUT_L; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + iio_for_each_active_channel(indio_dev, bit) { ret = kmx61_read_measurement(data, base, bit); - if (ret < 0) { - mutex_unlock(&data->lock); - goto err; - } + if (ret < 0) + return ret; buffer[i++] = ret; } - mutex_unlock(&data->lock); + + return 0; +} + +static irqreturn_t kmx61_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + int ret; + s16 buffer[8] = { }; + + ret = kmx61_read_for_each_active_channel(indio_dev, buffer); + if (ret < 0) + goto err; iio_push_to_buffers(indio_dev, buffer); err: @@ -1419,22 +1431,18 @@ static void kmx61_remove(struct i2c_client *client) iio_trigger_unregister(data->motion_trig); } - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); - mutex_unlock(&data->lock); } static int kmx61_suspend(struct device *dev) { - int ret; struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->lock); - ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, - false); - mutex_unlock(&data->lock); + guard(mutex)(&data->lock); - return ret; + return kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, false); } static int kmx61_resume(struct device *dev) @@ -1453,13 +1461,10 @@ static int kmx61_resume(struct device *dev) static int kmx61_runtime_suspend(struct device *dev) { struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); - int ret; - mutex_lock(&data->lock); - ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); - mutex_unlock(&data->lock); + guard(mutex)(&data->lock); - return ret; + return kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); } static int kmx61_runtime_resume(struct device *dev) From 94574dff234074addb0066e39005b8eac8351627 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 21 May 2026 00:08:50 +0500 Subject: [PATCH 2374/5214] iio: imu: bno055: terminate dev_err() strings with a newline Two dev_err() calls in bno055_ser_write_reg() and bno055_ser_read_reg() are missing the trailing newline, so the kernel log buffer continues the next message on the same line. Add the missing \n. Signed-off-by: Stepan Ionichev Reviewed-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/bno055_ser_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c index 48669dabb37b..733f9112de06 100644 --- a/drivers/iio/imu/bno055/bno055_ser_core.c +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -288,7 +288,7 @@ static int bno055_ser_write_reg(void *context, const void *_data, size_t count) struct bno055_ser_priv *priv = context; if (count < 2) { - dev_err(&priv->serdev->dev, "Invalid write count %zu", count); + dev_err(&priv->serdev->dev, "Invalid write count %zu\n", count); return -EINVAL; } @@ -306,7 +306,7 @@ static int bno055_ser_read_reg(void *context, struct bno055_ser_priv *priv = context; if (val_size > 128) { - dev_err(&priv->serdev->dev, "Invalid read valsize %zu", val_size); + dev_err(&priv->serdev->dev, "Invalid read valsize %zu\n", val_size); return -EINVAL; } From ab568cb755600dda283541179e9338e1f0faed9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:43 -0300 Subject: [PATCH 2375/5214] iio: accel: HID: hid-sensor-accel-3d: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the channel initialization loop and replace the local accel_3d_adjust_channel_bit_mask() function with a compound literal to improve code readability. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 27 +++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 2ff591b3458f..2bf05ab5235e 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ +#include #include #include #include @@ -119,17 +120,6 @@ static const struct iio_chan_spec gravity_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP), }; -/* Adjust channel real bits based on report descriptor */ -static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int accel_3d_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -297,19 +287,20 @@ static int accel_3d_parse_report(struct platform_device *pdev, struct accel_3d_state *st) { int ret; - int i; - for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { + for (unsigned int ch = CHANNEL_SCAN_INDEX_X; ch <= CHANNEL_SCAN_INDEX_Z; ch++) { ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, usage_id, - HID_USAGE_SENSOR_ACCEL_X_AXIS + i, - &st->accel[CHANNEL_SCAN_INDEX_X + i]); + HID_USAGE_SENSOR_ACCEL_X_AXIS + ch, + &st->accel[ch]); if (ret < 0) break; - accel_3d_adjust_channel_bit_mask(channels, - CHANNEL_SCAN_INDEX_X + i, - st->accel[CHANNEL_SCAN_INDEX_X + i].size); + channels[ch].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->accel[ch].size), + .storagebits = BITS_PER_TYPE(u32), + }; } dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", st->accel[0].index, From 3a7f08f66ec31b23822f107444e7c6816909ec1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:44 -0300 Subject: [PATCH 2376/5214] iio: gyro: HID: hid-sensor-gyro-3d: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the local gyro_3d_adjust_channel_bit_mask() function with a compound literal for scan_type initialization to improve code readability. Additionaly, clean up the channel initialization loop by iterating directly over the channel scan indices. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/hid-sensor-gyro-3d.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index c340cc899a7c..e48c25c87b6d 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ +#include #include #include #include @@ -82,17 +83,6 @@ static const struct iio_chan_spec gyro_3d_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; -/* Adjust channel real bits based on report descriptor */ -static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int gyro_3d_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -248,19 +238,20 @@ static int gyro_3d_parse_report(struct platform_device *pdev, struct gyro_3d_state *st) { int ret; - int i; - for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { + for (unsigned int ch = CHANNEL_SCAN_INDEX_X; ch <= CHANNEL_SCAN_INDEX_Z; ch++) { ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, usage_id, - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i, - &st->gyro[CHANNEL_SCAN_INDEX_X + i]); + HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + ch, + &st->gyro[ch]); if (ret < 0) break; - gyro_3d_adjust_channel_bit_mask(channels, - CHANNEL_SCAN_INDEX_X + i, - st->gyro[CHANNEL_SCAN_INDEX_X + i].size); + channels[ch].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->gyro[ch].size), + .storagebits = BITS_PER_TYPE(u32), + }; } dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", st->gyro[0].index, From cded217233845577e58b0720a61ebce556196f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:45 -0300 Subject: [PATCH 2377/5214] iio: light: HID: hid-sensor-als: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the local als_adjust_channel_bit_mask() function with a compound literal for scan_type initialization to improve code readability. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 384572844162..d72e260b8266 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ +#include #include #include #include @@ -117,17 +118,6 @@ static const struct iio_chan_spec als_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) }; -/* Adjust channel real bits based on report descriptor */ -static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int als_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -335,7 +325,11 @@ static int als_parse_report(struct platform_device *pdev, channels[index] = als_channels[i]; st->als_scan_mask[0] |= BIT(i); - als_adjust_channel_bit_mask(channels, index, st->als[i].size); + channels[index].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->als[i].size), + .storagebits = BITS_PER_TYPE(u32), + }; ++index; dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, From e6cd87745a456548e1a2f9ae90927e18a10b57fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:46 -0300 Subject: [PATCH 2378/5214] iio: light: HID: hid-sensor-prox: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the local prox_adjust_channel_bit_mask() function with a compound literal for scan_type initialization to improve code readability. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-prox.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index efa904a70d0e..edc9274a2c07 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2014, Intel Corporation. */ +#include #include #include #include @@ -67,17 +68,6 @@ static const struct iio_chan_spec prox_channels[] = { PROX_CHANNEL(false, 0), }; -/* Adjust channel real bits based on report descriptor */ -static void prox_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int prox_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -250,8 +240,11 @@ static int prox_parse_report(struct platform_device *pdev, st->scan_mask[0] |= BIT(index); channels[index] = prox_channels[i]; channels[index].scan_index = index; - prox_adjust_channel_bit_mask(channels, index, - st->prox_attr[index].size); + channels[index].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->prox_attr[index].size), + .storagebits = BITS_PER_TYPE(u32), + }; dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr[index].index, st->prox_attr[index].report_id); st->scale_precision[index] = From 339002636932e56191ba810975a876fe4a3e223f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:47 -0300 Subject: [PATCH 2379/5214] iio: magnetometer: HID: hid-sensor-magn-3d: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the local magn_3d_adjust_channel_bit_mask() function with a compound literal for scan_type initialization to improve code readability. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index b01dd53eb100..23884825eb00 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ +#include #include #include #include @@ -132,17 +133,6 @@ static const struct iio_chan_spec magn_3d_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(7) }; -/* Adjust channel real bits based on report descriptor */ -static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int magn_3d_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -418,9 +408,11 @@ static int magn_3d_parse_report(struct platform_device *pdev, if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) { /* Set magn_val_addr to iio value address */ st->magn_val_addr[i] = &st->iio_vals[*chan_count]; - magn_3d_adjust_channel_bit_mask(_channels, - *chan_count, - st->magn[i].size); + _channels[*chan_count].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->magn[i].size), + .storagebits = BITS_PER_TYPE(u32), + }; } (*chan_count)++; } From f1cb3d3afd1024269ec1887a36b28efd9ea849a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nat=C3=A1lia=20Salvino=20Andr=C3=A9?= Date: Tue, 19 May 2026 20:40:48 -0300 Subject: [PATCH 2380/5214] iio: pressure: HID: hid-sensor-press: Refactor channel initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the local press_adjust_channel_bit_mask() function with a compound literal for scan_type initialization to improve code readability. Signed-off-by: Natália Salvino André Co-developed-by: Pietro Di Consolo Gregorio Signed-off-by: Pietro Di Consolo Gregorio Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/hid-sensor-press.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 5f1d6abda3e4..a039b99d9851 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -3,6 +3,7 @@ * HID Sensors Driver * Copyright (c) 2014, Intel Corporation. */ +#include #include #include #include @@ -53,17 +54,6 @@ static const struct iio_chan_spec press_channels[] = { }; -/* Adjust channel real bits based on report descriptor */ -static void press_adjust_channel_bit_mask(struct iio_chan_spec *channels, - int channel, int size) -{ - channels[channel].scan_type.sign = 's'; - /* Real storage bits will change based on the report desc. */ - channels[channel].scan_type.realbits = size * 8; - /* Maximum size of a sample to capture is u32 */ - channels[channel].scan_type.storagebits = sizeof(u32) * 8; -} - /* Channel read_raw handler */ static int press_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -225,8 +215,11 @@ static int press_parse_report(struct platform_device *pdev, &st->press_attr); if (ret < 0) return ret; - press_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESSURE, - st->press_attr.size); + channels[CHANNEL_SCAN_INDEX_PRESSURE].scan_type = (struct iio_scan_type) { + .format = 's', + .realbits = BYTES_TO_BITS(st->press_attr.size), + .storagebits = BITS_PER_TYPE(u32), + }; dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, st->press_attr.report_id); From b39f3bb9f5580d19bc0c10dea9224d75fed45f1d Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:12 +0200 Subject: [PATCH 2381/5214] iio: tcs3472: power down chip on probe failure If tcs3472_probe() fails after enabling the chip (by writing PON | AEN to the ENABLE register), the error paths return without powering down the device. Add an 'error_powerdown' label at the end of the cleanup chain that calls tcs3472_powerdown() to power down the chip. The existing label cascade is rerouted to fall through to the new label. Move tcs3472_powerdown() above tcs3472_probe() so the probe can call it without a forward declaration. Found by code inspection while reviewing the probe error paths in preparation for the devm_ conversion. Fixes: eb869ade30a6 ("iio: Add tcs3472 color light sensor driver") Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 5a14f8d39aa4..6f055b0967a9 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -440,6 +440,23 @@ static const struct iio_info tcs3472_info = { .attrs = &tcs3472_attribute_group, }; +static int tcs3472_powerdown(struct tcs3472_data *data) +{ + int ret; + u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + data->enable & ~enable_mask); + if (!ret) + data->enable &= ~enable_mask; + + mutex_unlock(&data->lock); + + return ret; +} + static int tcs3472_probe(struct i2c_client *client) { struct tcs3472_data *data; @@ -513,7 +530,7 @@ static int tcs3472_probe(struct i2c_client *client) ret = iio_triggered_buffer_setup(indio_dev, NULL, tcs3472_trigger_handler, NULL); if (ret < 0) - return ret; + goto error_powerdown; if (client->irq) { ret = request_threaded_irq(client->irq, NULL, @@ -536,23 +553,8 @@ static int tcs3472_probe(struct i2c_client *client) free_irq(client->irq, indio_dev); buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); - return ret; -} - -static int tcs3472_powerdown(struct tcs3472_data *data) -{ - int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; - - mutex_lock(&data->lock); - - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); - if (!ret) - data->enable &= ~enable_mask; - - mutex_unlock(&data->lock); - +error_powerdown: + tcs3472_powerdown(data); return ret; } From 6283163e2a5c70e8d9d8d34df8b8a6d81507e966 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:13 +0200 Subject: [PATCH 2382/5214] iio: tcs3472: sort headers alphabetically Sort the #include directives in alphabetical order in preparation for adding new headers in upcoming patches. No functional change. Suggested-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 6f055b0967a9..68d9032973dc 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -13,16 +13,16 @@ * TODO: wait time */ -#include -#include #include +#include +#include #include +#include +#include #include #include -#include #include -#include #include #define TCS3472_DRV_NAME "tcs3472" From 0e6a62767a1235b1b52e9faaf6c916a4c786a3df Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:14 +0200 Subject: [PATCH 2383/5214] iio: tcs3472: convert remaining locking to guard(mutex) Convert several functions to use guard(mutex)() This avoids manual unlock calls on each return path, drops the goto in tcs3472_write_event(), and removes 'ret' variables only needed to return after the unlock. While the conversion is in progress, take the opportunity to make a few small cleanups that guard() enables: - In tcs3472_read_event_config(), replace '!!(...)' with '(...) ? 1 : 0' for readability. - In tcs3472_write_event_config(), tcs3472_powerdown() and tcs3472_resume() use an early return on the I2C write failure path. No functional change. Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 78 ++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 68d9032973dc..8956ed058a20 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -13,6 +13,7 @@ * TODO: wait time */ +#include #include #include #include @@ -220,32 +221,24 @@ static int tcs3472_read_event(struct iio_dev *indio_dev, int *val2) { struct tcs3472_data *data = iio_priv(indio_dev); - int ret; unsigned int period; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (info) { case IIO_EV_INFO_VALUE: *val = (dir == IIO_EV_DIR_RISING) ? data->high_thresh : data->low_thresh; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: period = (256 - data->atime) * 2400 * tcs3472_intr_pers[data->apers]; *val = period / USEC_PER_SEC; *val2 = period % USEC_PER_SEC; - ret = IIO_VAL_INT_PLUS_MICRO; - break; + return IIO_VAL_INT_PLUS_MICRO; default: - ret = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return ret; } static int tcs3472_write_event(struct iio_dev *indio_dev, @@ -259,7 +252,8 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, int period; int i; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); + switch (info) { case IIO_EV_INFO_VALUE: switch (dir) { @@ -270,18 +264,18 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, command = TCS3472_AILT; break; default: - ret = -EINVAL; - goto error; + return -EINVAL; } ret = i2c_smbus_write_word_data(data->client, command, val); if (ret) - goto error; + return ret; if (dir == IIO_EV_DIR_RISING) data->high_thresh = val; else data->low_thresh = val; - break; + + return 0; case IIO_EV_INFO_PERIOD: period = val * USEC_PER_SEC + val2; for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { @@ -291,18 +285,14 @@ static int tcs3472_write_event(struct iio_dev *indio_dev, } ret = i2c_smbus_write_byte_data(data->client, TCS3472_PERS, i); if (ret) - goto error; + return ret; data->apers = i; - break; - default: - ret = -EINVAL; - break; - } -error: - mutex_unlock(&data->lock); - return ret; + return 0; + default: + return -EINVAL; + } } static int tcs3472_read_event_config(struct iio_dev *indio_dev, @@ -310,13 +300,10 @@ static int tcs3472_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct tcs3472_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->lock); - ret = !!(data->enable & TCS3472_ENABLE_AIEN); - mutex_unlock(&data->lock); + guard(mutex)(&data->lock); - return ret; + return (data->enable & TCS3472_ENABLE_AIEN) ? 1 : 0; } static int tcs3472_write_event_config(struct iio_dev *indio_dev, @@ -327,7 +314,7 @@ static int tcs3472_write_event_config(struct iio_dev *indio_dev, int ret = 0; u8 enable_old; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); enable_old = data->enable; @@ -339,12 +326,13 @@ static int tcs3472_write_event_config(struct iio_dev *indio_dev, if (enable_old != data->enable) { ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, data->enable); - if (ret) + if (ret) { data->enable = enable_old; + return ret; + } } - mutex_unlock(&data->lock); - return ret; + return 0; } static irqreturn_t tcs3472_event_handler(int irq, void *priv) @@ -445,16 +433,16 @@ static int tcs3472_powerdown(struct tcs3472_data *data) int ret; u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, data->enable & ~enable_mask); - if (!ret) - data->enable &= ~enable_mask; + if (ret) + return ret; - mutex_unlock(&data->lock); + data->enable &= ~enable_mask; - return ret; + return 0; } static int tcs3472_probe(struct i2c_client *client) @@ -583,16 +571,16 @@ static int tcs3472_resume(struct device *dev) int ret; u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, data->enable | enable_mask); - if (!ret) - data->enable |= enable_mask; + if (ret) + return ret; - mutex_unlock(&data->lock); + data->enable |= enable_mask; - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, From 2e84e69c92233b601e3892887aec7c56fd4fe13b Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:15 +0200 Subject: [PATCH 2384/5214] iio: tcs3472: use ! instead of explicit NULL check Replace 'if (indio_dev == NULL)' with 'if (!indio_dev)' in tcs3472_probe() to follow the preferred kernel style. No functional change. Suggested-by: Joshua Crofts Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 8956ed058a20..36d3c106e70d 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -452,7 +452,7 @@ static int tcs3472_probe(struct i2c_client *client) int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); - if (indio_dev == NULL) + if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); From df94df71711516298719a4507a792f0bb9fd0837 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:16 +0200 Subject: [PATCH 2385/5214] iio: tcs3472: use devm for resource management Convert the driver to use device-managed resource allocation: - Add tcs3472_powerdown_action() and register it with devm_add_action_or_reset() to ensure the device is powered down on cleanup. - Replace iio_triggered_buffer_setup() with devm_iio_triggered_buffer_setup(). - Replace request_threaded_irq() with devm_request_threaded_irq(). - Replace iio_device_register() with devm_iio_device_register(). - Replace mutex_init() with devm_mutex_init(). - Remove tcs3472_remove() as all cleanup is now handled by devm. Use a local 'dev = &client->dev' in tcs3472_probe() to keep the devm calls compact. Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 63 +++++++++++++++---------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 36d3c106e70d..b0e8d5661567 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -445,20 +446,28 @@ static int tcs3472_powerdown(struct tcs3472_data *data) return 0; } +static void tcs3472_powerdown_action(void *data) +{ + tcs3472_powerdown(data); +} + static int tcs3472_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct tcs3472_data *data; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - mutex_init(&data->lock); + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; indio_dev->info = &tcs3472_info; indio_dev->name = TCS3472_DRV_NAME; @@ -515,46 +524,27 @@ static int tcs3472_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, - tcs3472_trigger_handler, NULL); + ret = devm_add_action_or_reset(dev, tcs3472_powerdown_action, data); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + tcs3472_trigger_handler, NULL); if (ret < 0) - goto error_powerdown; + return ret; if (client->irq) { - ret = request_threaded_irq(client->irq, NULL, - tcs3472_event_handler, - IRQF_TRIGGER_FALLING | IRQF_SHARED | - IRQF_ONESHOT, - client->name, indio_dev); + ret = devm_request_threaded_irq(dev, client->irq, NULL, + tcs3472_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_SHARED | + IRQF_ONESHOT, + client->name, indio_dev); if (ret) - goto buffer_cleanup; + return ret; } - ret = iio_device_register(indio_dev); - if (ret < 0) - goto free_irq; - - return 0; - -free_irq: - if (client->irq) - free_irq(client->irq, indio_dev); -buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); -error_powerdown: - tcs3472_powerdown(data); - return ret; -} - -static void tcs3472_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); - if (client->irq) - free_irq(client->irq, indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - tcs3472_powerdown(iio_priv(indio_dev)); + return devm_iio_device_register(dev, indio_dev); } static int tcs3472_suspend(struct device *dev) @@ -598,7 +588,6 @@ static struct i2c_driver tcs3472_driver = { .pm = pm_sleep_ptr(&tcs3472_pm_ops), }, .probe = tcs3472_probe, - .remove = tcs3472_remove, .id_table = tcs3472_id, }; module_i2c_driver(tcs3472_driver); From c9dc65030e5705d17c9605a17732d15b71898d0b Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:17 +0200 Subject: [PATCH 2386/5214] iio: tcs3472: use local struct device * for remaining cases Use the local 'struct device *dev' variable introduced for the devm calls also for the dev_info() calls in tcs3472_probe(), to keep the probe function consistent. No functional change. Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index b0e8d5661567..24b71d4543c5 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -480,9 +480,9 @@ static int tcs3472_probe(struct i2c_client *client) return ret; if (ret == 0x44) - dev_info(&client->dev, "TCS34721/34725 found\n"); + dev_info(dev, "TCS34721/34725 found\n"); else if (ret == 0x4d) - dev_info(&client->dev, "TCS34723/34727 found\n"); + dev_info(dev, "TCS34723/34727 found\n"); else return -ENODEV; From 1ccae88c0a1023e0c9f757dc6b1fbca463b32b36 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:18 +0200 Subject: [PATCH 2387/5214] iio: tcs3472: move standalone return to default case Move the trailing 'return -EINVAL' statements at the end of tcs3472_read_raw() and tcs3472_write_raw() into explicit default: cases inside the respective switch statements. This removes the need for a separate return statement after the switch. No functional change. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Reviewed-by: Joshua Crofts Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 24b71d4543c5..b61442c5c1be 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -166,8 +166,9 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev, *val = 0; *val2 = (256 - data->atime) * 2400; return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; } - return -EINVAL; } static int tcs3472_write_raw(struct iio_dev *indio_dev, @@ -204,8 +205,9 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev, } return -EINVAL; + default: + return -EINVAL; } - return -EINVAL; } /* From d2b3d461a09e0db6a6a8237db4081a32f71a04b9 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Sat, 23 May 2026 13:15:36 -0500 Subject: [PATCH 2388/5214] iio: chemical: sps30: Replace manual locking with RAII locking Replace manual mutex_lock() and mutex_unlock() calls with the much newer guard(mutex)() macro to enable RAII patterns, modernize the driver, and to increase readability. Move mutex locking into sps30_do_meas() and tune it up to use guard()(), as every caller takes the lock anyways. Signed-off-by: Maxwell Doose Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sps30.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index a934bf0298dd..8e15baa31423 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -5,6 +5,7 @@ * Copyright (c) Tomasz Duszynski */ +#include #include #include #include @@ -69,6 +70,8 @@ static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) { int i, ret; + guard(mutex)(&state->lock); + if (state->state == RESET) { ret = state->ops->start_meas(state); if (ret) @@ -111,9 +114,7 @@ static irqreturn_t sps30_trigger_handler(int irq, void *p) aligned_s64 ts; } scan; - mutex_lock(&state->lock); ret = sps30_do_meas(state, scan.data, ARRAY_SIZE(scan.data)); - mutex_unlock(&state->lock); if (ret) goto err; @@ -136,7 +137,6 @@ static int sps30_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { case IIO_MASSCONCENTRATION: - mutex_lock(&state->lock); /* read up to the number of bytes actually needed */ switch (chan->channel2) { case IIO_MOD_PM1: @@ -152,7 +152,6 @@ static int sps30_read_raw(struct iio_dev *indio_dev, ret = sps30_do_meas(state, data, 4); break; } - mutex_unlock(&state->lock); if (ret) return ret; @@ -197,9 +196,9 @@ static ssize_t start_cleaning_store(struct device *dev, if (kstrtoint(buf, 0, &val) || val != 1) return -EINVAL; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); + ret = state->ops->clean_fan(state); - mutex_unlock(&state->lock); if (ret) return ret; @@ -215,9 +214,9 @@ static ssize_t cleaning_period_show(struct device *dev, __be32 val; int ret; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); + ret = state->ops->read_cleaning_period(state, &val); - mutex_unlock(&state->lock); if (ret) return ret; @@ -238,12 +237,11 @@ static ssize_t cleaning_period_store(struct device *dev, struct device_attribute (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) return -EINVAL; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); + ret = state->ops->write_cleaning_period(state, cpu_to_be32(val)); - if (ret) { - mutex_unlock(&state->lock); + if (ret) return ret; - } msleep(20); @@ -256,8 +254,6 @@ static ssize_t cleaning_period_store(struct device *dev, struct device_attribute dev_warn(dev, "period changed but reads will return the old value\n"); - mutex_unlock(&state->lock); - return len; } From 8a2f9c41b9cc3b7570e4b8476220db11bd6a1030 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 24 May 2026 20:38:33 -0500 Subject: [PATCH 2389/5214] iio: Convert IIO_CHAN_SOFT_TIMESTAMP() to be compound literal Currently IIO_CHAN_SOFT_TIMESTAMP() can only be used to fill the static data. In some cases it would be convenient to use it as right value in the assignment operation. But it can't be done as is, because compiler has no clue about the data layout. Converting it to be a compound literal allows the above mentioned usage. While at it, tidy up the indentation. We also have to change existing uses of compound literal at the same time to avoid compiler errors. Signed-off-by: Andy Shevchenko Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.c | 2 +- drivers/iio/adc/max11410.c | 2 +- include/linux/iio/iio.h | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index d9271894f091..cebb8ed8dcb1 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -1475,7 +1475,7 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev) } if (slow_bus) - channels[i] = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(i); + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); indio_dev->channels = channels; diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index 69351f4f10bb..dc1b96356592 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -804,7 +804,7 @@ static int max11410_parse_channels(struct max11410_state *st, chan_idx++; } - channels[chan_idx] = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(chan_idx); + channels[chan_idx] = IIO_CHAN_SOFT_TIMESTAMP(chan_idx); indio_dev->num_channels = chan_idx + 1; indio_dev->channels = channels; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 96b05c86c325..711c00f67371 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -353,15 +353,15 @@ static inline bool iio_channel_has_available(const struct iio_chan_spec *chan, (chan->info_mask_shared_by_all_available & BIT(type)); } -#define IIO_CHAN_SOFT_TIMESTAMP(_si) { \ +#define IIO_CHAN_SOFT_TIMESTAMP(_si) (struct iio_chan_spec) { \ .type = IIO_TIMESTAMP, \ .channel = -1, \ .scan_index = _si, \ .scan_type = { \ .sign = 's', \ - .realbits = 64, \ + .realbits = 64, \ .storagebits = 64, \ - }, \ + }, \ } s64 iio_get_time_ns(const struct iio_dev *indio_dev); From c73b6d7f0b7f2b2279134746cbacbb880222f374 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:34 -0500 Subject: [PATCH 2390/5214] iio: common: scmi_sensors: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. In fact, there was an error here as the sign should be 's' instead of 'u' which is now changed to 's' by using IIO_CHAN_SOFT_TIMESTAMP(). If we find that this breaks userspace, we will have to revert this change, but seems unlikely since the timestamp channel is well-known to be a signed 64-bit integer globally. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/common/scmi_sensors/scmi_iio.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 5136ad9ada04..442b40ef27cf 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -419,17 +419,6 @@ static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = { { } }; -static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan, - int scan_index) -{ - iio_chan->type = IIO_TIMESTAMP; - iio_chan->channel = -1; - iio_chan->scan_index = scan_index; - iio_chan->scan_type.sign = 'u'; - iio_chan->scan_type.realbits = 64; - iio_chan->scan_type.storagebits = 64; -} - static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan, enum iio_chan_type type, enum iio_modifier mod, int scan_index) @@ -629,7 +618,7 @@ scmi_alloc_iiodev(struct scmi_device *sdev, "Error in registering sensor update notifier for sensor %s\n", sensor->sensor_info->name); - scmi_iio_set_timestamp_channel(&iio_channels[i], i); + iio_channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); iiodev->channels = iio_channels; return iiodev; } From 98f548bb911908dd2a60073c006ccebf9ce7c60a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:35 -0500 Subject: [PATCH 2391/5214] iio: adc: dln2-adc: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/dln2-adc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index eb902a946efe..b01c6f5a73b1 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -444,16 +444,6 @@ static int dln2_update_scan_mode(struct iio_dev *indio_dev, lval.scan_type.endianness = IIO_LE; \ } -/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */ -#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) { \ - lval.type = IIO_TIMESTAMP; \ - lval.channel = -1; \ - lval.scan_index = _si; \ - lval.scan_type.sign = 's'; \ - lval.scan_type.realbits = 64; \ - lval.scan_type.storagebits = 64; \ -} - static const struct iio_info dln2_adc_info = { .read_raw = dln2_adc_read_raw, .write_raw = dln2_adc_write_raw, @@ -614,7 +604,7 @@ static int dln2_adc_probe(struct platform_device *pdev) for (i = 0; i < chans; ++i) DLN2_ADC_CHAN(dln2->iio_channels[i], i) - IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i); + dln2->iio_channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); indio_dev->name = DLN2_ADC_MOD_NAME; indio_dev->info = &dln2_adc_info; From f97661c4c713fa2bdf2649c76b181772e27cf325 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:36 -0500 Subject: [PATCH 2392/5214] iio: adc: at91_adc: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 6e1930f7c65d..f610ad729bf3 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -481,7 +481,7 @@ static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private) static int at91_adc_channel_init(struct iio_dev *idev) { struct at91_adc_state *st = iio_priv(idev); - struct iio_chan_spec *chan_array, *timestamp; + struct iio_chan_spec *chan_array; int bit, idx = 0; unsigned long rsvd_mask = 0; @@ -519,14 +519,8 @@ static int at91_adc_channel_init(struct iio_dev *idev) chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); idx++; } - timestamp = chan_array + idx; - timestamp->type = IIO_TIMESTAMP; - timestamp->channel = -1; - timestamp->scan_index = idx; - timestamp->scan_type.sign = 's'; - timestamp->scan_type.realbits = 64; - timestamp->scan_type.storagebits = 64; + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx); idev->channels = chan_array; return idev->num_channels; From f5b317d043cd73aaa806121f763fb2aa93afc7aa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:37 -0500 Subject: [PATCH 2393/5214] iio: adc: cc10001_adc: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/cc10001_adc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c index 2c51b90b7101..d42b747325aa 100644 --- a/drivers/iio/adc/cc10001_adc.c +++ b/drivers/iio/adc/cc10001_adc.c @@ -262,7 +262,7 @@ static const struct iio_info cc10001_adc_info = { static int cc10001_adc_channel_init(struct iio_dev *indio_dev, unsigned long channel_map) { - struct iio_chan_spec *chan_array, *timestamp; + struct iio_chan_spec *chan_array; unsigned int bit, idx = 0; indio_dev->num_channels = bitmap_weight(&channel_map, @@ -289,13 +289,7 @@ static int cc10001_adc_channel_init(struct iio_dev *indio_dev, idx++; } - timestamp = &chan_array[idx]; - timestamp->type = IIO_TIMESTAMP; - timestamp->channel = -1; - timestamp->scan_index = idx; - timestamp->scan_type.sign = 's'; - timestamp->scan_type.realbits = 64; - timestamp->scan_type.storagebits = 64; + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx); indio_dev->channels = chan_array; From 404edaf1e885a7f047f3d2090d29bdf4c5cf77a2 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:38 -0500 Subject: [PATCH 2394/5214] iio: adc: stm32-adc: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 46106200bb86..5c5170b19b56 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2443,15 +2443,7 @@ static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping) scan_index = ret; if (timestamping) { - struct iio_chan_spec *timestamp = &channels[scan_index]; - - timestamp->type = IIO_TIMESTAMP; - timestamp->channel = -1; - timestamp->scan_index = scan_index; - timestamp->scan_type.sign = 's'; - timestamp->scan_type.realbits = 64; - timestamp->scan_type.storagebits = 64; - + channels[scan_index] = IIO_CHAN_SOFT_TIMESTAMP(scan_index); scan_index++; } From b3405ec8496272d42dcded5633cb547e586bad6e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:39 -0500 Subject: [PATCH 2395/5214] iio: common: cros_ec_sensors: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Also drop obvious comment while we're at it. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/common/cros_ec_sensors/cros_ec_activity.c | 8 +------- drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c index 6e38d115b6fe..6762685e6876 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c @@ -279,13 +279,7 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) channel++; } - /* Timestamp */ - channel->scan_index = index; - channel->type = IIO_TIMESTAMP; - channel->channel = -1; - channel->scan_type.sign = 's'; - channel->scan_type.realbits = 64; - channel->scan_type.storagebits = 64; + *channel = IIO_CHAN_SOFT_TIMESTAMP(index); indio_dev->channels = st->channels; indio_dev->num_channels = index + 1; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index f34e2bbba2d1..651632ccfe0d 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -279,13 +279,7 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) } } - /* Timestamp */ - channel->type = IIO_TIMESTAMP; - channel->channel = -1; - channel->scan_index = CROS_EC_SENSOR_MAX_AXIS; - channel->scan_type.sign = 's'; - channel->scan_type.realbits = 64; - channel->scan_type.storagebits = 64; + *channel = IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS); indio_dev->channels = state->channels; indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS; From 6a0861ce6f725891e8f31971512915a7374a541e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:38:40 -0500 Subject: [PATCH 2396/5214] iio: light: cros_ec_light_prox: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Also drop obvious comment while we're at it. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/light/cros_ec_light_prox.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 815806ceb5c8..d09dea9c0782 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -223,14 +223,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) return -EINVAL; } - /* Timestamp */ channel++; - channel->type = IIO_TIMESTAMP; - channel->channel = -1; - channel->scan_index = 1; - channel->scan_type.sign = 's'; - channel->scan_type.realbits = 64; - channel->scan_type.storagebits = 64; + *channel = IIO_CHAN_SOFT_TIMESTAMP(1); indio_dev->channels = state->channels; From 391c3ec1b15e9f5880812cb045f470ac45c8ba8f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 24 May 2026 20:46:52 -0500 Subject: [PATCH 2397/5214] iio: pressure: cros_ec_baro: simplify timestamp channel definition Use IIO_CHAN_SOFT_TIMESTAMP() to define the timestamp channel instead of manually filling in the struct iio_chan_spec fields. This makes the code less verbose and mistake-prone. Also drop obvious comment while we're at it. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/cros_ec_baro.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index c6b950c596c1..6cbde48d5be3 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -170,14 +170,8 @@ static int cros_ec_baro_probe(struct platform_device *pdev) return -EINVAL; } - /* Timestamp */ channel++; - channel->type = IIO_TIMESTAMP; - channel->channel = -1; - channel->scan_index = 1; - channel->scan_type.sign = 's'; - channel->scan_type.realbits = 64; - channel->scan_type.storagebits = 64; + *channel = IIO_CHAN_SOFT_TIMESTAMP(1); indio_dev->channels = state->channels; indio_dev->num_channels = CROS_EC_BARO_MAX_CHANNELS; From 2fab7499006ddc87c8937b149b769b8eb4a65217 Mon Sep 17 00:00:00 2001 From: Rodrigo Gobbi Date: Tue, 26 May 2026 22:36:39 -0300 Subject: [PATCH 2398/5214] iio: adc: spear_adc: sort includes alphabetically Sort includes alphabetically, no functional change Signed-off-by: Rodrigo Gobbi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 91995489bb1c..efde6afc54fd 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -5,19 +5,19 @@ * Copyright 2012 Stefan Roese */ +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include #include #include From f6ae81a98a7b72674d4ffef8daa87f4c958dd92b Mon Sep 17 00:00:00 2001 From: Rodrigo Gobbi Date: Tue, 26 May 2026 22:36:40 -0300 Subject: [PATCH 2399/5214] iio: adc: spear_adc: align headers with IWYU principle Remove unused includes and add what is being used: #include // for ARRAY_SIZE #include // for GENMASKxx #include // for dev_err_probe, dev_info #include // for DIV_ROUND_UP #include // for struct mutex #include // for uXX definitions #include // for IIO_CHAN_INFO_* Signed-off-by: Rodrigo Gobbi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index efde6afc54fd..4be722406bb5 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -5,22 +5,25 @@ * Copyright 2012 Stefan Roese */ +#include #include +#include #include #include -#include +#include #include #include #include -#include +#include #include #include +#include #include #include -#include +#include #include -#include +#include /* SPEAR registers definitions */ #define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF) From 46968e058cbc77a87f0771739eeea31aba7e08c1 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Tue, 26 May 2026 09:55:15 +0200 Subject: [PATCH 2400/5214] dt-bindings: iio: light: add Broadcom APDS9999 Add Device Tree binding for the Broadcom APDS9999 ambient light and proximity sensor. A separate binding file is used rather than merging with avago,apds9300.yaml because the APDS9999 has an additional vcsel-supply for the VCSEL. The APDS9999 features individual R, G, B, and IR channels with a green channel that uses optical coating to approximate the human eye spectral response for ALS/lux measurements. Calibrated RGB color sensing is not yet implemented in the driver. Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/brcm,apds9999.yaml | 54 +++++++++++++++++++ MAINTAINERS | 6 +++ 2 files changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml diff --git a/Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml b/Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml new file mode 100644 index 000000000000..9f5b3b294c2c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/brcm,apds9999.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# +title: Broadcom APDS-9999 Digital Proximity and RGB Sensor + +maintainers: + - Jose A. Perez de Azpillaga + +description: | + Broadcom APDS-9999 is a digital proximity and RGB sensor with + ambient light sensing (ALS) capability. The device uses individual + R, G, B, and IR channels plus a Vertical Cavity Surface Emitting + Laser (VCSEL) for proximity detection. + + Datasheet: https://docs.broadcom.com/docs/APDS-9999-DS + +properties: + compatible: + enum: + - brcm,apds9999 + + reg: + maxItems: 1 + + vdd-supply: true + + vcsel-supply: + description: VCSEL power supply (VVCSEL pin) + + interrupts: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@52 { + compatible = "brcm,apds9999"; + reg = <0x52>; + vdd-supply = <&vdd_reg>; + vcsel-supply = <&vcsel_reg>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 1533337900da..44b84c8857ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4992,6 +4992,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/brcm,apds9160.yaml F: drivers/iio/light/apds9160.c +BROADCOM APDS9999 AMBIENT LIGHT SENSOR DRIVER +M: Jose A. Perez de Azpillaga +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml + BROADCOM ASP 2.0 ETHERNET DRIVER M: Justin Chen M: Florian Fainelli From 5f9363e523000f05bfbd5440fecfa08ec5d08094 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Tue, 26 May 2026 09:55:46 +0200 Subject: [PATCH 2401/5214] iio: light: add support for APDS9999 sensor Add IIO driver for Broadcom APDS9999 ambient light sensor. The APDS9999 is a digital proximity and RGB sensor with ALS capability. The driver implements the ALS/Lux functionality using the green channel, which uses optical coating technology to approximate the human eye spectral response. Raw IIO_INTENSITY channels are exposed for red, green, blue, and IR so userspace can compute its own weighted lux. Proximity (PS) support is not yet implemented. Signed-off-by: Jose A. Perez de Azpillaga Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/light/Kconfig | 14 ++ drivers/iio/light/Makefile | 1 + drivers/iio/light/apds9999.c | 333 +++++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 drivers/iio/light/apds9999.c diff --git a/MAINTAINERS b/MAINTAINERS index 44b84c8857ca..9e69cc754cf5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4997,6 +4997,7 @@ M: Jose A. Perez de Azpillaga L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/light/brcm,apds9999.yaml +F: drivers/iio/light/apds9999.c BROADCOM ASP 2.0 ETHERNET DRIVER M: Justin Chen diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index eff33e456c70..da4807a3fd3d 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -119,6 +119,20 @@ config APDS9960 To compile this driver as a module, choose M here: the module will be called apds9960 +config APDS9999 + tristate "Broadcom APDS9999 ALS, RGB and proximity sensor" + depends on I2C + help + Say Y here if you want to build support for the Broadcom APDS9999 + ALS, RGB and proximity sensor with I2C interface. + + This driver provides ambient light sensing (ALS/Lux), raw + intensity data for red, green, blue and IR channels, plus + proximity detection support. + + To compile this driver as a module, choose M here: the + module will be called apds9999. + config AS73211 tristate "AMS AS73211 XYZ color sensor and AMS AS7331 UV sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c0048e0d5ca8..39e62dfc10c7 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_APDS9160) += apds9160.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9306) += apds9306.o obj-$(CONFIG_APDS9960) += apds9960.o +obj-$(CONFIG_APDS9999) += apds9999.o obj-$(CONFIG_AS73211) += as73211.o obj-$(CONFIG_BH1745) += bh1745.o obj-$(CONFIG_BH1750) += bh1750.o diff --git a/drivers/iio/light/apds9999.c b/drivers/iio/light/apds9999.c new file mode 100644 index 000000000000..7a0df5252078 --- /dev/null +++ b/drivers/iio/light/apds9999.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for Broadcom APDS9999 Lux Light Sensor + * + * Copyright (C) 2026 + * Author: Jose A. Perez de Azpillaga + * + * TODO: proximity sensor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APDS9999_REG_MAIN_CTRL 0x00 +#define APDS9999_MAIN_CTRL_LS_EN BIT(1) +#define APDS9999_REG_LS_MEAS_RATE 0x04 +#define APDS9999_LS_RES_MASK GENMASK(6, 4) +#define APDS9999_LS_RATE_MASK GENMASK(2, 0) +#define APDS9999_REG_LS_GAIN 0x05 +#define APDS9999_REG_PART_ID 0x06 +#define APDS9999_REG_MAIN_STATUS 0x07 +#define APDS9999_MAIN_STATUS_LS_DATA BIT(3) +#define APDS9999_REG_LS_DATA_IR_0 0x0A +#define APDS9999_REG_LS_DATA_GREEN_0 0x0D +#define APDS9999_REG_LS_DATA_BLUE_0 0x10 +#define APDS9999_REG_LS_DATA_RED_0 0x13 + +#define APDS9999_PART_ID 0xC2 + +#define APDS9999_GAIN_1X 0 +#define APDS9999_GAIN_3X 1 +#define APDS9999_GAIN_6X 2 +#define APDS9999_GAIN_9X 3 +#define APDS9999_GAIN_18X 4 + +static const int apds9999_gains[] = { + [APDS9999_GAIN_1X] = 1, + [APDS9999_GAIN_3X] = 3, + [APDS9999_GAIN_6X] = 6, + [APDS9999_GAIN_9X] = 9, + [APDS9999_GAIN_18X] = 18, +}; + +#define APDS9999_RES_20BIT 0 +#define APDS9999_RES_19BIT 1 +#define APDS9999_RES_18BIT 2 +#define APDS9999_RES_17BIT 3 +#define APDS9999_RES_16BIT 4 +#define APDS9999_RES_13BIT 5 + +static const int apds9999_itimes_us[] = { + [APDS9999_RES_20BIT] = 400 * USEC_PER_MSEC, + [APDS9999_RES_19BIT] = 200 * USEC_PER_MSEC, + [APDS9999_RES_18BIT] = 100 * USEC_PER_MSEC, + [APDS9999_RES_17BIT] = 50 * USEC_PER_MSEC, + [APDS9999_RES_16BIT] = 25 * USEC_PER_MSEC, + [APDS9999_RES_13BIT] = 3125, +}; + +#define APDS9999_RATE_25_MS 0 +#define APDS9999_RATE_50_MS 1 +#define APDS9999_RATE_100_MS 2 +#define APDS9999_RATE_200_MS 3 +#define APDS9999_RATE_500_MS 4 +#define APDS9999_RATE_1000_MS 5 +#define APDS9999_RATE_2000_MS 6 + +struct apds9999_data { + struct i2c_client *client; + /* lock: serializes access to device registers and cached values */ + struct mutex lock; + int als_gain_idx; + int als_res; + int als_rate; +}; + +static void apds9999_standby(void *client) +{ + i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL, 0); +} + +/* + * Apply power-on defaults: 18-bit / 100 ms resolution and rate, + * 3x gain. These match the datasheet reset values. + */ +static int apds9999_init(struct apds9999_data *data) +{ + struct device *dev = &data->client->dev; + struct i2c_client *client = data->client; + u8 regval; + int ret; + + ret = devm_add_action_or_reset(dev, apds9999_standby, client); + if (ret) + return ret; + + guard(mutex)(&data->lock); + + regval = FIELD_PREP(APDS9999_LS_RES_MASK, APDS9999_RES_18BIT) | + FIELD_PREP(APDS9999_LS_RATE_MASK, APDS9999_RATE_100_MS); + ret = i2c_smbus_write_byte_data(client, APDS9999_REG_LS_MEAS_RATE, + regval); + if (ret) + return ret; + data->als_res = APDS9999_RES_18BIT; + data->als_rate = APDS9999_RATE_100_MS; + + ret = i2c_smbus_write_byte_data(client, APDS9999_REG_LS_GAIN, + APDS9999_GAIN_3X); + if (ret) + return ret; + data->als_gain_idx = APDS9999_GAIN_3X; + + return i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL, + APDS9999_MAIN_CTRL_LS_EN); +} + +static int apds9999_read_channel(struct apds9999_data *data, u8 reg, + u32 *counts) +{ + struct i2c_client *client = data->client; + u8 buf[3]; + int ret, tries; + + guard(mutex)(&data->lock); + + /* + * Poll MAIN_STATUS for new data. Timeout: ~2 integration periods + * plus margin. Each try sleeps 20 ms. + */ + tries = max(2, (apds9999_itimes_us[data->als_res] * 2) / 20000); + + while (tries--) { + ret = i2c_smbus_read_byte_data(client, + APDS9999_REG_MAIN_STATUS); + if (ret < 0) + return ret; + if (ret & APDS9999_MAIN_STATUS_LS_DATA) + break; + fsleep(20000); + } + + if (tries < 0) + return -ETIMEDOUT; + + ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + *counts = get_unaligned_le24(buf) & GENMASK(19, 0); + return 0; +} + +static int apds9999_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct apds9999_data *data = iio_priv(indio_dev); + int gain, itime_us; + u64 scale_nano; + u32 counts; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = apds9999_read_channel(data, chan->address, &counts); + if (ret) + return ret; + *val = (int)counts; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: { + u32 remainder; + + /* + * Scale (lux per count) = 54 / (gain * integration_time_ms) + * + * The constant 54 is derived from the datasheet table: + * at gain = 3x, itime = 100 ms -> 0.180 lux/count + * -> C = 0.180 * 3 * 100 = 54 + * + * Expressed as IIO_VAL_INT_PLUS_NANO. + */ + gain = apds9999_gains[data->als_gain_idx]; + itime_us = apds9999_itimes_us[data->als_res]; + + /* scale_nano = 54 * 1e12 / (gain * itime_us) nano-lux/count */ + scale_nano = div_u64(54ULL * NSEC_PER_SEC * USEC_PER_MSEC, (u32)(gain * itime_us)); + *val = (int)div_u64_rem(scale_nano, NSEC_PER_SEC, &remainder); + *val2 = (int)remainder; + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = apds9999_itimes_us[data->als_res]; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static const struct iio_info apds9999_info = { + .read_raw = apds9999_read_raw, +}; + +/* + * The green channel uses optical coating to approximate the human eye + * spectral response. IIO_INTENSITY channels provide raw ADC data for + * red, green, blue, and IR so userspace can compute weighted lux. + */ +static const struct iio_chan_spec apds9999_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_GREEN_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_RED, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_RED_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_GREEN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_GREEN_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BLUE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_BLUE_0, + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .address = APDS9999_REG_LS_DATA_IR_0, + }, +}; + +static int apds9999_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct apds9999_data *data; + struct iio_dev *indio_dev; + int ret, part_id; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + part_id = i2c_smbus_read_byte_data(client, APDS9999_REG_PART_ID); + if (part_id < 0) + return dev_err_probe(dev, part_id, "failed to read PART_ID\n"); + if (part_id != APDS9999_PART_ID) + dev_info(dev, "unexpected PART_ID 0x%02x (expected 0x%02x)\n", + part_id, APDS9999_PART_ID); + + ret = apds9999_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize device\n"); + + indio_dev->name = "apds9999"; + indio_dev->info = &apds9999_info; + indio_dev->channels = apds9999_channels; + indio_dev->num_channels = ARRAY_SIZE(apds9999_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id apds9999_id[] = { + { .name = "apds9999" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, apds9999_id); + +static const struct of_device_id apds9999_of_match[] = { + { .compatible = "brcm,apds9999" }, + { } +}; +MODULE_DEVICE_TABLE(of, apds9999_of_match); + +static struct i2c_driver apds9999_driver = { + .driver = { + .name = "apds9999", + .of_match_table = apds9999_of_match, + }, + .probe = apds9999_probe, + .id_table = apds9999_id, +}; +module_i2c_driver(apds9999_driver); + +MODULE_AUTHOR("Jose A. Perez de Azpillaga "); +MODULE_DESCRIPTION("APDS-9999 Lux Light Sensor IIO Driver"); +MODULE_LICENSE("GPL"); From 434c150752675f44dc52c384a7fa22e5176bc35a Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:28 +0300 Subject: [PATCH 2402/5214] iio: temperature: ltc2983: Fix n_wires default bypassing rotation check When adi,number-of-wires is absent, n_wires is left at 0. The binding documents a default of 2 wires, matching the hardware default. However the current-rotate validation checks n_wires == 2 || n_wires == 3, so with n_wires = 0 the guard is bypassed and adi,current-rotate is accepted for a 2-wire RTD. Initialize n_wires = 2 to match the binding default and ensure the rotation check fires correctly when the property is absent. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 38e6f8dfd3b8..1f835e326b93 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -741,7 +741,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, struct ltc2983_rtd *rtd; int ret = 0; struct device *dev = &st->spi->dev; - u32 excitation_current = 0, n_wires = 0; + u32 excitation_current = 0, n_wires = 2; rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); if (!rtd) From 5cb9fdb446bfc3ae0524496f53fb68e67051701b Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:29 +0300 Subject: [PATCH 2403/5214] iio: temperature: ltc2983: Fix reinit_completion() called after conversion start reinit_completion() was called after regmap_write() initiated the hardware conversion, creating a race window where the interrupt could fire and call complete() before reinit_completion() reset the completion. Move reinit_completion() before the regmap_write() to close the race. ltc2983_eeprom_cmd() already does it in the correct order. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 1f835e326b93..2bc5cd46a72f 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1177,12 +1177,11 @@ static int ltc2983_chan_read(struct ltc2983_data *st, start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n", sensor->chan, start_conversion); + reinit_completion(&st->completion); /* start conversion */ ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion); if (ret) return ret; - - reinit_completion(&st->completion); /* * wait for conversion to complete. * 300 ms should be more than enough to complete the conversion. From 53f4fda3c0efa77bb239dc5e357ae80644fcb400 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:30 +0300 Subject: [PATCH 2404/5214] iio: temperature: ltc2983: Fix macro parenthesization and rename Wrap the 'chan' parameter in LTC2983_CHAN_START_ADDR() and LTC2983_CHAN_RES_ADDR() with parentheses to prevent potential macro argument expansion issues. Also rename LTC2983_CHAN_START_ADDR to LTC2983_CHAN_ASSIGN_ADDR and LTC2983_CHAN_RES_ADDR to LTC2983_RESULT_ADDR, to better reflect the datasheet names and avoid them being confused as related. Reviewed-by: Joshua Crofts Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 2bc5cd46a72f..4bae90f03002 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -56,10 +56,10 @@ #define LTC2983_EEPROM_WRITE_TIME_MS 2600 #define LTC2983_EEPROM_READ_TIME_MS 20 -#define LTC2983_CHAN_START_ADDR(chan) \ - (((chan - 1) * 4) + LTC2983_CHAN_ASSIGN_START_REG) -#define LTC2983_CHAN_RES_ADDR(chan) \ - (((chan - 1) * 4) + LTC2983_TEMP_RES_START_REG) +#define LTC2983_CHAN_ASSIGN_ADDR(chan) \ + ((((chan) - 1) * 4) + LTC2983_CHAN_ASSIGN_START_REG) +#define LTC2983_RESULT_ADDR(chan) \ + ((((chan) - 1) * 4) + LTC2983_TEMP_RES_START_REG) #define LTC2983_THERMOCOUPLE_DIFF_MASK BIT(3) #define LTC2983_THERMOCOUPLE_SGL(x) \ FIELD_PREP(LTC2983_THERMOCOUPLE_DIFF_MASK, x) @@ -351,7 +351,7 @@ static int __ltc2983_chan_assign_common(struct ltc2983_data *st, const struct ltc2983_sensor *sensor, u32 chan_val) { - u32 reg = LTC2983_CHAN_START_ADDR(sensor->chan); + u32 reg = LTC2983_CHAN_ASSIGN_ADDR(sensor->chan); chan_val |= LTC2983_CHAN_TYPE(sensor->type); dev_dbg(&st->spi->dev, "Assign reg:0x%04X, val:0x%08X\n", reg, @@ -1196,7 +1196,7 @@ static int ltc2983_chan_read(struct ltc2983_data *st, } /* read the converted data */ - ret = regmap_bulk_read(st->regmap, LTC2983_CHAN_RES_ADDR(sensor->chan), + ret = regmap_bulk_read(st->regmap, LTC2983_RESULT_ADDR(sensor->chan), &st->temp, sizeof(st->temp)); if (ret) return ret; From 6b047d099703c9b4f6846dcb98e2e3ca7a0ff773 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:31 +0300 Subject: [PATCH 2405/5214] iio: temperature: ltc2983: Use local device pointer consistently Some functions define a local 'dev' pointer but still use bare '&st->spi->dev' in some code paths, and some don't have it at all. Replace bare references with the local pointer for consistency and collapse some wrapped lines that now fit within 80 characters. Reviewed-by: Joshua Crofts Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 87 +++++++++++++++++-------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 4bae90f03002..8b0b6b4884f6 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -351,11 +351,11 @@ static int __ltc2983_chan_assign_common(struct ltc2983_data *st, const struct ltc2983_sensor *sensor, u32 chan_val) { + struct device *dev = &st->spi->dev; u32 reg = LTC2983_CHAN_ASSIGN_ADDR(sensor->chan); chan_val |= LTC2983_CHAN_TYPE(sensor->type); - dev_dbg(&st->spi->dev, "Assign reg:0x%04X, val:0x%08X\n", reg, - chan_val); + dev_dbg(dev, "Assign reg:0x%04X, val:0x%08X\n", reg, chan_val); st->chan_val = cpu_to_be32(chan_val); return regmap_bulk_write(st->regmap, reg, &st->chan_val, sizeof(st->chan_val)); @@ -656,11 +656,12 @@ static struct ltc2983_sensor * ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { + struct device *dev = &st->spi->dev; struct ltc2983_thermocouple *thermo; u32 oc_current; int ret; - thermo = devm_kzalloc(&st->spi->dev, sizeof(*thermo), GFP_KERNEL); + thermo = devm_kzalloc(dev, sizeof(*thermo), GFP_KERNEL); if (!thermo) return ERR_PTR(-ENOMEM); @@ -687,7 +688,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data LTC2983_THERMOCOUPLE_OC_CURR(3); break; default: - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid open circuit current:%u\n", oc_current); } @@ -697,7 +698,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data /* validate channel index */ if (!(thermo->sensor_config & LTC2983_THERMOCOUPLE_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for differential thermocouple\n", sensor->chan); @@ -712,7 +713,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data * This would be caught later but we can just return * the error right away. */ - return dev_err_ptr_probe(&st->spi->dev, ret, + return dev_err_ptr_probe(dev, ret, "Property reg must be given\n"); } @@ -823,7 +824,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, } else { /* same as differential case */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for RTD\n", sensor->chan); } @@ -873,7 +874,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, rtd->excitation_current = 0x08; break; default: - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid value for excitation current(%u)\n", excitation_current); } @@ -922,7 +923,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s /* validate channel index */ if (!(thermistor->sensor_config & LTC2983_THERMISTOR_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for differential thermistor\n", sensor->chan); @@ -964,7 +965,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s case 0: /* auto range */ if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Auto Range not allowed for custom sensors\n"); thermistor->excitation_current = 0x0c; @@ -1003,7 +1004,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s thermistor->excitation_current = 0x0b; break; default: - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid value for excitation current(%u)\n", excitation_current); } @@ -1016,11 +1017,12 @@ static struct ltc2983_sensor * ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { + struct device *dev = &st->spi->dev; struct ltc2983_diode *diode; u32 temp = 0, excitation_current = 0; int ret; - diode = devm_kzalloc(&st->spi->dev, sizeof(*diode), GFP_KERNEL); + diode = devm_kzalloc(dev, sizeof(*diode), GFP_KERNEL); if (!diode) return ERR_PTR(-ENOMEM); @@ -1036,7 +1038,7 @@ ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data * /* validate channel index */ if (!(diode->sensor_config & LTC2983_DIODE_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for differential thermistor\n", sensor->chan); @@ -1061,7 +1063,7 @@ ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data * diode->excitation_current = 0x03; break; default: - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid value for excitation current(%u)\n", excitation_current); } @@ -1079,23 +1081,24 @@ static struct ltc2983_sensor *ltc2983_r_sense_new(struct fwnode_handle *child, struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { + struct device *dev = &st->spi->dev; struct ltc2983_rsense *rsense; int ret; u32 temp; - rsense = devm_kzalloc(&st->spi->dev, sizeof(*rsense), GFP_KERNEL); + rsense = devm_kzalloc(dev, sizeof(*rsense), GFP_KERNEL); if (!rsense) return ERR_PTR(-ENOMEM); /* validate channel index */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chann:%d for r_sense\n", sensor->chan); ret = fwnode_property_read_u32(child, "adi,rsense-val-milli-ohms", &temp); if (ret) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Property adi,rsense-val-milli-ohms missing\n"); /* * Times 1000 because we have milli-ohms and __convert_to_raw @@ -1115,9 +1118,10 @@ static struct ltc2983_sensor *ltc2983_adc_new(struct fwnode_handle *child, struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { + struct device *dev = &st->spi->dev; struct ltc2983_adc *adc; - adc = devm_kzalloc(&st->spi->dev, sizeof(*adc), GFP_KERNEL); + adc = devm_kzalloc(dev, sizeof(*adc), GFP_KERNEL); if (!adc) return ERR_PTR(-ENOMEM); @@ -1125,7 +1129,7 @@ static struct ltc2983_sensor *ltc2983_adc_new(struct fwnode_handle *child, adc->single_ended = true; if (!adc->single_ended && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chan:%d for differential adc\n", sensor->chan); @@ -1140,9 +1144,10 @@ static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child, struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { + struct device *dev = &st->spi->dev; struct ltc2983_temp *temp; - temp = devm_kzalloc(&st->spi->dev, sizeof(*temp), GFP_KERNEL); + temp = devm_kzalloc(dev, sizeof(*temp), GFP_KERNEL); if (!temp) return ERR_PTR(-ENOMEM); @@ -1150,7 +1155,7 @@ static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child, temp->single_ended = true; if (!temp->single_ended && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) - return dev_err_ptr_probe(&st->spi->dev, -EINVAL, + return dev_err_ptr_probe(dev, -EINVAL, "Invalid chan:%d for differential temp\n", sensor->chan); @@ -1169,13 +1174,14 @@ static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child, static int ltc2983_chan_read(struct ltc2983_data *st, const struct ltc2983_sensor *sensor, int *val) { + struct device *dev = &st->spi->dev; u32 start_conversion = 0; int ret; unsigned long time; start_conversion = LTC2983_STATUS_START(true); start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); - dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n", + dev_dbg(dev, "Start conversion on chan:%d, status:%02X\n", sensor->chan, start_conversion); reinit_completion(&st->completion); /* start conversion */ @@ -1191,7 +1197,7 @@ static int ltc2983_chan_read(struct ltc2983_data *st, time = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(300)); if (!time) { - dev_warn(&st->spi->dev, "Conversion timed out\n"); + dev_warn(dev, "Conversion timed out\n"); return -ETIMEDOUT; } @@ -1204,7 +1210,7 @@ static int ltc2983_chan_read(struct ltc2983_data *st, *val = __be32_to_cpu(st->temp); if (!(LTC2983_RES_VALID_MASK & *val)) { - dev_err(&st->spi->dev, "Invalid conversion detected\n"); + dev_err(dev, "Invalid conversion detected\n"); return -EIO; } @@ -1221,12 +1227,12 @@ static int ltc2983_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct ltc2983_data *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; int ret; /* sanity check */ if (chan->address >= st->num_channels) { - dev_err(&st->spi->dev, "Invalid chan address:%ld", - chan->address); + dev_err(dev, "Invalid chan address:%ld", chan->address); return -EINVAL; } @@ -1302,7 +1308,7 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) st->num_channels = device_get_child_node_count(dev); if (!st->num_channels) - return dev_err_probe(&st->spi->dev, -EINVAL, + return dev_err_probe(dev, -EINVAL, "At least one channel must be given!\n"); st->sensors = devm_kcalloc(dev, st->num_channels, sizeof(*st->sensors), @@ -1390,6 +1396,7 @@ static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, unsigned int wait_time, unsigned int status_reg, unsigned long status_fail_mask) { + struct device *dev = &st->spi->dev; unsigned long time; unsigned int val; int ret; @@ -1409,7 +1416,7 @@ static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, time = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(wait_time)); if (!time) - return dev_err_probe(&st->spi->dev, -ETIMEDOUT, + return dev_err_probe(dev, -ETIMEDOUT, "EEPROM command timed out\n"); ret = regmap_read(st->regmap, status_reg, &val); @@ -1417,7 +1424,7 @@ static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, return ret; if (val & status_fail_mask) - return dev_err_probe(&st->spi->dev, -EINVAL, + return dev_err_probe(dev, -EINVAL, "EEPROM command failed: 0x%02X\n", val); return 0; @@ -1426,6 +1433,7 @@ static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) { u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0, status; + struct device *dev = &st->spi->dev; int ret; /* make sure the device is up: start bit (7) is 0 and done bit (6) is 1 */ @@ -1433,8 +1441,7 @@ static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) LTC2983_STATUS_UP(status) == 1, 25000, 25000 * 10); if (ret) - return dev_err_probe(&st->spi->dev, ret, - "Device startup timed out\n"); + return dev_err_probe(dev, ret, "Device startup timed out\n"); ret = regmap_update_bits(st->regmap, LTC2983_GLOBAL_CONFIG_REG, LTC2983_NOTCH_FREQ_MASK, @@ -1534,12 +1541,13 @@ static const struct iio_info ltc2983_iio_info = { static int ltc2983_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct ltc2983_data *st; struct iio_dev *indio_dev; struct gpio_desc *gpio; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1551,7 +1559,7 @@ static int ltc2983_probe(struct spi_device *spi) st->regmap = devm_regmap_init_spi(spi, <c2983_regmap_config); if (IS_ERR(st->regmap)) - return dev_err_probe(&spi->dev, PTR_ERR(st->regmap), + return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); mutex_init(&st->lock); @@ -1564,11 +1572,11 @@ static int ltc2983_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_regulator_get_enable(&spi->dev, "vdd"); + ret = devm_regulator_get_enable(dev, "vdd"); if (ret) return ret; - gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(gpio)) return PTR_ERR(gpio); @@ -1578,7 +1586,7 @@ static int ltc2983_probe(struct spi_device *spi) gpiod_set_value_cansleep(gpio, 0); } - st->iio_chan = devm_kzalloc(&spi->dev, + st->iio_chan = devm_kzalloc(dev, st->iio_channels * sizeof(*st->iio_chan), GFP_KERNEL); if (!st->iio_chan) @@ -1588,11 +1596,10 @@ static int ltc2983_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler, + ret = devm_request_irq(dev, spi->irq, ltc2983_irq_handler, IRQF_TRIGGER_RISING, st->info->name, st); if (ret) - return dev_err_probe(&spi->dev, ret, - "failed to request an irq\n"); + return dev_err_probe(dev, ret, "failed to request an irq\n"); if (st->info->has_eeprom) { ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD, @@ -1609,7 +1616,7 @@ static int ltc2983_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = <c2983_iio_info; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static int ltc2983_resume(struct device *dev) From ae81c43f6c01a441ea8ce77f37903853595b6bb3 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:32 +0300 Subject: [PATCH 2406/5214] iio: temperature: ltc2983: Fix inconsistent channel wording in messages Replace occurrences of the abbreviated 'chann' and 'chan' with 'channel' in error and debug messages throughout the driver. Also changed the diode invalid channel error message from "thermistor" to "diode". Reviewed-by: Joshua Crofts Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 8b0b6b4884f6..fc904c0a42b4 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -699,7 +699,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data if (!(thermo->sensor_config & LTC2983_THERMOCOUPLE_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for differential thermocouple\n", + "Invalid channel %d for differential thermocouple\n", sensor->chan); struct fwnode_handle *ref __free(fwnode_handle) = @@ -797,7 +797,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, /* * rtd channel indexes are a bit more complicated to validate. * For 4wire RTD with rotation, the channel selection cannot be - * >=19 since the chann + 1 is used in this configuration. + * >=19 since the channel + 1 is used in this configuration. * For 4wire RTDs with kelvin rsense, the rsense channel cannot be * <=1 since channel - 1 and channel - 2 are used. */ @@ -814,18 +814,18 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, (rtd->r_sense_chan <= min)) /* kelvin rsense*/ return dev_err_ptr_probe(dev, -EINVAL, - "Invalid rsense chann:%d to use in kelvin rsense\n", + "Invalid channel %d for kelvin rsense\n", rtd->r_sense_chan); if (sensor->chan < min || sensor->chan > max) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for the rtd config\n", + "Invalid channel %d for RTD config\n", sensor->chan); } else { /* same as differential case */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for RTD\n", + "Invalid channel %d for RTD\n", sensor->chan); } @@ -924,7 +924,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s if (!(thermistor->sensor_config & LTC2983_THERMISTOR_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for differential thermistor\n", + "Invalid channel %d for differential thermistor\n", sensor->chan); /* check custom sensor */ @@ -1039,7 +1039,7 @@ ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data * if (!(diode->sensor_config & LTC2983_DIODE_DIFF_MASK) && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for differential thermistor\n", + "Invalid channel %d for differential diode\n", sensor->chan); /* set common parameters */ @@ -1093,7 +1093,7 @@ static struct ltc2983_sensor *ltc2983_r_sense_new(struct fwnode_handle *child, /* validate channel index */ if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chann:%d for r_sense\n", + "Invalid channel %d for r_sense\n", sensor->chan); ret = fwnode_property_read_u32(child, "adi,rsense-val-milli-ohms", &temp); @@ -1130,7 +1130,7 @@ static struct ltc2983_sensor *ltc2983_adc_new(struct fwnode_handle *child, if (!adc->single_ended && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chan:%d for differential adc\n", + "Invalid channel %d for differential ADC\n", sensor->chan); /* set common parameters */ @@ -1156,7 +1156,7 @@ static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child, if (!temp->single_ended && sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) return dev_err_ptr_probe(dev, -EINVAL, - "Invalid chan:%d for differential temp\n", + "Invalid channel %d for differential temp\n", sensor->chan); temp->custom = __ltc2983_custom_sensor_new(st, child, "adi,custom-temp", @@ -1181,7 +1181,7 @@ static int ltc2983_chan_read(struct ltc2983_data *st, start_conversion = LTC2983_STATUS_START(true); start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); - dev_dbg(dev, "Start conversion on chan:%d, status:%02X\n", + dev_dbg(dev, "Start conversion on channel:%d, status:%02X\n", sensor->chan, start_conversion); reinit_completion(&st->completion); /* start conversion */ @@ -1232,7 +1232,7 @@ static int ltc2983_read_raw(struct iio_dev *indio_dev, /* sanity check */ if (chan->address >= st->num_channels) { - dev_err(dev, "Invalid chan address:%ld", chan->address); + dev_err(dev, "Invalid channel address: %ld\n", chan->address); return -EINVAL; } @@ -1329,14 +1329,14 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) if (sensor.chan < LTC2983_MIN_CHANNELS_NR || sensor.chan > st->info->max_channels_nr) return dev_err_probe(dev, -EINVAL, - "chan:%d must be from %u to %u\n", + "channel:%d must be from %u to %u\n", sensor.chan, LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr); if (channel_avail_mask & BIT(sensor.chan)) return dev_err_probe(dev, -EINVAL, - "chan:%d already in use\n", + "channel:%d already in use\n", sensor.chan); ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type); @@ -1344,7 +1344,7 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) return dev_err_probe(dev, ret, "adi,sensor-type property must given for child nodes\n"); - dev_dbg(dev, "Create new sensor, type %u, chann %u", + dev_dbg(dev, "Create new sensor, type %u, channel %u", sensor.type, sensor.chan); if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE && From b46696afbceffd926adbeb62180554c2096cee78 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:33 +0300 Subject: [PATCH 2407/5214] iio: temperature: ltc2983: Use fwnode_property_present() for optional properties Checking fwnode_property_read_u32() return value with if (!ret) silently swallows meaningful error codes when a property is present but malformed. Use fwnode_property_present() first so that absence uses the default while a present but unreadable property returns a proper error. Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 84 +++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index fc904c0a42b4..130ab7fddc2f 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -668,8 +668,14 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data if (fwnode_property_read_bool(child, "adi,single-ended")) thermo->sensor_config = LTC2983_THERMOCOUPLE_SGL(1); - ret = fwnode_property_read_u32(child, "adi,sensor-oc-current-microamp", &oc_current); - if (!ret) { + if (fwnode_property_present(child, "adi,sensor-oc-current-microamp")) { + ret = fwnode_property_read_u32(child, + "adi,sensor-oc-current-microamp", + &oc_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,sensor-oc-current-microamp\n"); + switch (oc_current) { case 10: thermo->sensor_config |= @@ -759,8 +765,12 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, return dev_err_ptr_probe(dev, ret, "Property reg must be given\n"); - ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires); - if (!ret) { + if (fwnode_property_present(child, "adi,number-of-wires")) { + ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,number-of-wires\n"); + switch (n_wires) { case 2: rtd->sensor_config = LTC2983_RTD_N_WIRES(0); @@ -842,12 +852,13 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, rtd->sensor.fault_handler = ltc2983_common_fault_handler; rtd->sensor.assign_chan = ltc2983_rtd_assign_chan; - ret = fwnode_property_read_u32(child, "adi,excitation-current-microamp", - &excitation_current); - if (ret) { - /* default to 5uA */ - rtd->excitation_current = 1; - } else { + if (fwnode_property_present(child, "adi,excitation-current-microamp")) { + ret = fwnode_property_read_u32(child, "adi,excitation-current-microamp", + &excitation_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,excitation-current-microamp\n"); + switch (excitation_current) { case 5: rtd->excitation_current = 0x01; @@ -878,9 +889,17 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, "Invalid value for excitation current(%u)\n", excitation_current); } + } else { + /* default to 5uA */ + rtd->excitation_current = 1; } - fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve); + if (fwnode_property_present(child, "adi,rtd-curve")) { + ret = fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,rtd-curve\n"); + } return &rtd->sensor; } @@ -950,17 +969,13 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s thermistor->sensor.fault_handler = ltc2983_common_fault_handler; thermistor->sensor.assign_chan = ltc2983_thermistor_assign_chan; - ret = fwnode_property_read_u32(child, "adi,excitation-current-nanoamp", - &excitation_current); - if (ret) { - /* Auto range is not allowed for custom sensors */ - if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) - /* default to 1uA */ - thermistor->excitation_current = 0x03; - else - /* default to auto-range */ - thermistor->excitation_current = 0x0c; - } else { + if (fwnode_property_present(child, "adi,excitation-current-nanoamp")) { + ret = fwnode_property_read_u32(child, "adi,excitation-current-nanoamp", + &excitation_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,excitation-current-nanoamp\n"); + switch (excitation_current) { case 0: /* auto range */ @@ -1008,6 +1023,14 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s "Invalid value for excitation current(%u)\n", excitation_current); } + } else { + /* Auto range is not allowed for custom sensors */ + if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) + /* default to 1uA */ + thermistor->excitation_current = 0x03; + else + /* default to auto-range */ + thermistor->excitation_current = 0x0c; } return &thermistor->sensor; @@ -1046,9 +1069,13 @@ ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data * diode->sensor.fault_handler = ltc2983_common_fault_handler; diode->sensor.assign_chan = ltc2983_diode_assign_chan; - ret = fwnode_property_read_u32(child, "adi,excitation-current-microamp", - &excitation_current); - if (!ret) { + if (fwnode_property_present(child, "adi,excitation-current-microamp")) { + ret = fwnode_property_read_u32(child, "adi,excitation-current-microamp", + &excitation_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,excitation-current-microamp\n"); + switch (excitation_current) { case 10: diode->excitation_current = 0x00; @@ -1069,7 +1096,12 @@ ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data * } } - fwnode_property_read_u32(child, "adi,ideal-factor-value", &temp); + if (fwnode_property_present(child, "adi,ideal-factor-value")) { + ret = fwnode_property_read_u32(child, "adi,ideal-factor-value", &temp); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,ideal-factor-value\n"); + } /* 2^20 resolution */ diode->ideal_factor_value = __convert_to_raw(temp, 1048576); From 622775dbc56a6349fc98b368041e3224bfeeb9de Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:34 +0300 Subject: [PATCH 2408/5214] iio: core: Add IIO_COVERAGE channel type Add a new channel type for sensors that report fractional coverage as a percentage. The sysfs attribute is in_coverageY_raw; after applying in_coverageY_scale the value is in percent. The first user is the ADT7604 leak detector, where the value represents the portion of the sensing element that is wetted. Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 17 +++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 4 files changed, 21 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 925a33fd309a..d8d6d85235b0 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1980,6 +1980,23 @@ Description: Raw (unscaled no offset etc.) resistance reading. Units after application of scale and offset are ohms. +What: /sys/bus/iio/devices/iio:deviceX/in_coverageY_raw +KernelVersion: 7.2 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) coverage reading. Used for sensors + that report fractional coverage as a percentage, such as leak + detectors where the value represents what portion of the sensing + element is wetted. Units after application of scale and offset are + percent. + +What: /sys/bus/iio/devices/iio:deviceX/in_coverageY_scale +KernelVersion: 7.2 +Contact: linux-iio@vger.kernel.org +Description: + Scale to be applied to in_coverageY_raw to obtain coverage + in percent. + What: /sys/bus/iio/devices/iio:deviceX/heater_enable KernelVersion: 4.1.0 Contact: linux-iio@vger.kernel.org diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index bd6f4f9f4533..ffe0dc49c4b9 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -98,6 +98,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CHROMATICITY] = "chromaticity", [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", + [IIO_COVERAGE] = "coverage", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index d7c2bb223651..c9295c707041 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -53,6 +53,7 @@ enum iio_chan_type { IIO_CHROMATICITY, IIO_ATTENTION, IIO_ALTCURRENT, + IIO_COVERAGE, }; enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index df6c43d7738d..bc3ef4c77c2b 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -65,6 +65,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_CHROMATICITY] = "chromaticity", [IIO_ATTENTION] = "attention", [IIO_ALTCURRENT] = "altcurrent", + [IIO_COVERAGE] = "coverage", }; static const char * const iio_ev_type_text[] = { @@ -194,6 +195,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_CHROMATICITY: case IIO_ATTENTION: case IIO_ALTCURRENT: + case IIO_COVERAGE: break; default: return false; From a496ba27dd68a5830cd57ec6c5c6e735fd113b1b Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:35 +0300 Subject: [PATCH 2409/5214] dt-bindings: iio: temperature: Add ADT7604 support to adi,ltc2983 The ADT7604 shares the same die as the LTC2984. It repurposes the custom RTD sensor type (18) as a copper trace resistance sensor and the custom thermistor type (27) as a leak detector, and removes thermocouple, diode and direct ADC sensor types. Add adi,adt7604 to the compatible list and introduce two new sensor node types specific to this device: - copper-trace@: maps to the custom RTD sensor type (18). Two variants: sub-ohm (< 1 ohm, adi,copper-trace-sub-ohm boolean, no custom table and excitation current) and standard (> 1 ohm, required adi,custom-copper-trace table, optional excitation current defaulting to the datasheet recommended value). Primary output is resistance in ohms. For > 1 ohm copper traces with a custom table, the chip also outputs temperature in millidegrees Celsius. - leak-detector@: maps to the custom thermistor sensor type (27). Takes a required adi,custom-leak-detector lookup table encoding resistance (uOhm) against coverage data (%). Two outputs: resistance in ohms and coverage in percent. Separate node types are used rather than extending the existing rtd@ and thermistor@ nodes because adi,custom-rtd is required for sensor type 18, and several properties (adi,number-of-wires, adi,rtd-curve, adi,rsense-share, adi,single-ended, adi,current-rotate) have no meaning for the new sensor types, since the configuration is hardcoded, and would need to be explicitly forbidden or ignored in the driver. allOf conditions are added to restrict thermocouple, diode, direct ADC and active temperature nodes to non-ADT7604 devices, and to restrict copper-trace and leak-detector nodes to the ADT7604 (some parts only). Signed-off-by: Liviu Stan Acked-by: Conor Dooley Signed-off-by: Jonathan Cameron --- .../bindings/iio/temperature/adi,ltc2983.yaml | 217 +++++++++++++++++- 1 file changed, 214 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml index a22725f7619b..13e5f29f0588 100644 --- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -4,14 +4,18 @@ $id: http://devicetree.org/schemas/iio/temperature/adi,ltc2983.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices LTC2983, LTC2986, LTM2985 Multi-sensor Temperature system +title: Analog Devices LTC2983 and similar Multi-sensor Temperature systems maintainers: - Nuno Sá description: | - Analog Devices LTC2983, LTC2984, LTC2986, LTM2985 Multi-Sensor Digital - Temperature Measurement Systems + Analog Devices Multi-Sensor Digital Temperature Measurement Systems: + - ADT7604 + - LTC2983 + - LTC2984 + - LTC2986 + - LTM2985 https://www.analog.com/media/en/technical-documentation/data-sheets/2983fc.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/2984fb.pdf @@ -43,6 +47,7 @@ properties: compatible: oneOf: - enum: + - adi,adt7604 - adi,ltc2983 - adi,ltc2986 - adi,ltm2985 @@ -436,6 +441,121 @@ patternProperties: required: - adi,custom-temp + '^copper-trace@': + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false + description: | + Copper trace resistance sensor (some parts only). Two variants exist: + sub-ohm (< 1 ohm, no custom table allowed) and standard (> 1 ohm, + required custom table). + + properties: + reg: + minimum: 2 + maximum: 20 + + adi,sensor-type: + description: Sensor type for copper trace sensors. + $ref: /schemas/types.yaml#/definitions/uint32 + const: 32 + + adi,rsense-handle: + description: Associated sense resistor sensor. + $ref: /schemas/types.yaml#/definitions/phandle + + adi,copper-trace-sub-ohm: + description: + Select the sub-ohm (< 1 ohm) copper trace variant. Custom table + and excitation current are not allowed in this mode. + type: boolean + + adi,excitation-current-microamp: + description: + Excitation current applied to the copper trace. Not used in + sub-ohm mode. The datasheet recommends 1mA for copper trace + sensors due to their typically small resistance. + enum: [5, 10, 25, 50, 100, 250, 500, 1000] + default: 1000 + + adi,custom-copper-trace: + description: + Resistance-to-temperature table for copper trace sensors with + resistance > 1 ohm. Required when adi,copper-trace-sub-ohm is not + set. See Page 36 of the datasheet. + $ref: /schemas/types.yaml#/definitions/uint64-matrix + minItems: 3 + maxItems: 64 + items: + items: + - description: Resistance point in uOhms. + - description: Temperature point in uK. + + required: + - adi,rsense-handle + + allOf: + - if: + required: + - adi,copper-trace-sub-ohm + then: + properties: + adi,custom-copper-trace: false + adi,excitation-current-microamp: false + - if: + not: + required: + - adi,copper-trace-sub-ohm + then: + required: + - adi,custom-copper-trace + + '^leak-detector@': + $ref: '#/$defs/sensor-node' + unevaluatedProperties: false + description: | + Leak detector sensor (some parts only). Outputs resistance in ohms and + a coverage percentage via IIO_COVERAGE (raw/1024 = coverage %). + + properties: + reg: + minimum: 2 + maximum: 20 + + adi,sensor-type: + description: Sensor type for leak detector sensors. + $ref: /schemas/types.yaml#/definitions/uint32 + const: 33 + + adi,rsense-handle: + description: Associated sense resistor sensor. + $ref: /schemas/types.yaml#/definitions/phandle + + adi,excitation-current-nanoamp: + description: + Excitation current applied to the leak detector. The correct value + depends on the electrical characteristics of the liquid being sensed. + For example, 10000 (10µA) is recommended for PG25 (see datasheet + Table 39). + enum: [250, 500, 1000, 5000, 10000, 25000, 50000, 100000, 250000, + 500000, 1000000] + + adi,custom-leak-detector: + description: | + Lookup table mapping resistance to coverage percentage. Entries must + be in ascending resistance order. + $ref: /schemas/types.yaml#/definitions/uint64-matrix + minItems: 3 + maxItems: 64 + items: + items: + - description: Resistance point in uOhms. + - description: Coverage data percentage (0 to 100). + + required: + - adi,rsense-handle + - adi,excitation-current-nanoamp + - adi,custom-leak-detector + '^rsense@': $ref: '#/$defs/sensor-node' unevaluatedProperties: false @@ -477,6 +597,32 @@ allOf: patternProperties: '^temp@': false + - if: + properties: + compatible: + contains: + const: adi,adt7604 + then: + patternProperties: + '^thermocouple@': false + '^diode@': false + '^adc@': false + '^temp@': false + '^rtd@': + properties: + adi,sensor-type: + not: + const: 18 + '^thermistor@': + properties: + adi,sensor-type: + not: + const: 27 + else: + patternProperties: + '^copper-trace@': false + '^leak-detector@': false + examples: - | #include @@ -556,4 +702,69 @@ examples: }; }; }; + + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@0 { + compatible = "adi,adt7604"; + reg = <0>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + + #address-cells = <1>; + #size-cells = <0>; + vdd-supply = <&supply>; + + trace_rsense: rsense@2 { + reg = <2>; + adi,sensor-type = <29>; + adi,rsense-val-milli-ohms = <100000>; // 100 ohm + }; + + copper-trace@4 { + reg = <4>; + adi,sensor-type = <32>; + adi,rsense-handle = <&trace_rsense>; + adi,copper-trace-sub-ohm; + }; + + r_sense: rsense@12 { + reg = <12>; + adi,sensor-type = <29>; + adi,rsense-val-milli-ohms = <1000000>; // 1 kohm + }; + + leak-detector@14 { + reg = <14>; + adi,sensor-type = <33>; + adi,rsense-handle = <&r_sense>; + adi,excitation-current-nanoamp = <10000>; + adi,custom-leak-detector = + /bits/ 64 < 0 100>, + /bits/ 64 < 202020000 99>, + /bits/ 64 < 285710000 70>, + /bits/ 64 < 333330000 60>, + /bits/ 64 < 400000000 50>, + /bits/ 64 < 500000000 40>, + /bits/ 64 < 666670000 30>, + /bits/ 64 < 1000000000 20>, + /bits/ 64 < 2000000000 10>, + /bits/ 64 <1000000000000 0>; + }; + + rtd@18 { + reg = <18>; + adi,sensor-type = <12>; // PT100 + adi,rsense-handle = <&r_sense>; + adi,number-of-wires = <2>; + adi,rsense-share; + adi,excitation-current-microamp = <500>; + adi,rtd-curve = <0>; + }; + }; + }; ... From 3dd0c048409e335320418c632966f149be628fd4 Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:36 +0300 Subject: [PATCH 2410/5214] iio: temperature: ltc2983: Add support for ADT7604 The ADT7604 shares the same die as the LTC2984. It repurposes the custom RTD sensor type (18) as a copper trace resistance sensor and the custom thermistor type (27) as a leak detector, and removes thermocouple, diode and direct ADC sensor types. Two new software sensor type values are introduced (LTC2983_SENSOR_COPPER_TRACE = 32, LTC2983_SENSOR_LEAK_DETECTOR = 33) that map to the hardware register values 18 and 27 respectively. Dedicated structs (ltc2983_copper_trace, ltc2983_leak_detector) and parser functions are added rather than extending the existing RTD and thermistor paths, as the hardware configuration bits are fully hardcoded and several RTD/thermistor properties would need to be explicitly forbidden or ignored. Custom RTD (type 18) becomes the copper trace sensor. Sensor configuration bits are hardcoded to 0b1001 per the datasheet. Two variants are supported via the adi,copper-trace-sub-ohm DT property: sub-ohm traces (< 1 ohm) have bits 17:0 cleared with no excitation current or custom table; standard traces (> 1 ohm) have a required resistance-to-temperature table. Custom thermistor (type 27) becomes the leak detector. Sensor configuration bits are hardcoded to 0b001. The custom table uses a resolution of 16 instead of 64, and is specified via the required adi,custom-leak-detector DT property. Both sensor types expose an IIO_RESISTANCE channel reading from the resistance result register bank (0x0060-0x00AF). Added a "base" parameter to the LTC2983_RESULT_ADDR macro and a "base_reg" parameter to the ltc2983_chan_read function so we can read from both result register banks. The resistance register encodes the measured resistance with 10 fractional bits, so dividing by 1024 gives ohms. Since the sense resistor is specified in ohms, the output is in ohms for both sensor types and a single 1/1024 scale applies to both. For > 1 ohm copper traces and for leak detectors, a secondary channel also appears: IIO_TEMP (millidegrees Celsius) for copper trace and IIO_COVERAGE (percent) for leak detector. The ltc2983_chip_info struct is extended with a u64 supported_sensors bitmask using BIT_ULL() to safely represent the new sensor type bits 32 and 33 on 32-bit builds. A LTC2983_SENSOR_NUM sentinel is added to the enum so that the bounds check uses >= LTC2983_SENSOR_NUM rather than hardcoding the last sensor type. Tested on EVAL-ADT7604-AZ connected to Raspberry Pi 5 via SPI. Signed-off-by: Liviu Stan Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 413 ++++++++++++++++++++++++++++-- 1 file changed, 394 insertions(+), 19 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 130ab7fddc2f..fc65d8352d12 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -28,6 +28,8 @@ #define LTC2983_STATUS_REG 0x0000 #define LTC2983_TEMP_RES_START_REG 0x0010 #define LTC2983_TEMP_RES_END_REG 0x005F +#define ADT7604_RES_RES_START_REG 0x0060 +#define ADT7604_RES_RES_END_REG 0x00AF #define LTC2983_EEPROM_KEY_REG 0x00B0 #define LTC2983_EEPROM_READ_STATUS_REG 0x00D0 #define LTC2983_GLOBAL_CONFIG_REG 0x00F0 @@ -58,8 +60,8 @@ #define LTC2983_CHAN_ASSIGN_ADDR(chan) \ ((((chan) - 1) * 4) + LTC2983_CHAN_ASSIGN_START_REG) -#define LTC2983_RESULT_ADDR(chan) \ - ((((chan) - 1) * 4) + LTC2983_TEMP_RES_START_REG) +#define LTC2983_RESULT_ADDR(chan, base) \ + ((((chan) - 1) * 4) + (base)) #define LTC2983_THERMOCOUPLE_DIFF_MASK BIT(3) #define LTC2983_THERMOCOUPLE_SGL(x) \ FIELD_PREP(LTC2983_THERMOCOUPLE_DIFF_MASK, x) @@ -186,17 +188,44 @@ enum { LTC2983_SENSOR_SENSE_RESISTOR = 29, LTC2983_SENSOR_DIRECT_ADC = 30, LTC2983_SENSOR_ACTIVE_TEMP = 31, + /* Sensor types for some parts only; map to RTD_CUSTOM/THERMISTOR_CUSTOM in HW */ + LTC2983_SENSOR_COPPER_TRACE = 32, + LTC2983_SENSOR_LEAK_DETECTOR = 33, + LTC2983_SENSOR_NUM }; +/* Bitmask of sensor types supported by LTC2983/LTC2984 and derivatives */ +#define LTC2983_COMMON_SENSORS \ + (GENMASK_ULL(LTC2983_SENSOR_THERMOCOUPLE_CUSTOM, LTC2983_SENSOR_THERMOCOUPLE) | \ + GENMASK_ULL(LTC2983_SENSOR_RTD_CUSTOM, LTC2983_SENSOR_RTD) | \ + GENMASK_ULL(LTC2983_SENSOR_THERMISTOR_CUSTOM, LTC2983_SENSOR_THERMISTOR) | \ + BIT_ULL(LTC2983_SENSOR_DIODE) | \ + BIT_ULL(LTC2983_SENSOR_SENSE_RESISTOR) | \ + BIT_ULL(LTC2983_SENSOR_DIRECT_ADC)) + +/* Bitmask of sensor types supported by ADT7604 */ +#define ADT7604_SENSORS \ + (GENMASK_ULL(LTC2983_SENSOR_RTD_CUSTOM - 1, LTC2983_SENSOR_RTD) | \ + GENMASK_ULL(LTC2983_SENSOR_THERMISTOR_CUSTOM - 1, LTC2983_SENSOR_THERMISTOR) | \ + BIT_ULL(LTC2983_SENSOR_SENSE_RESISTOR) | \ + BIT_ULL(LTC2983_SENSOR_COPPER_TRACE) | \ + BIT_ULL(LTC2983_SENSOR_LEAK_DETECTOR)) + #define to_thermocouple(_sensor) \ container_of(_sensor, struct ltc2983_thermocouple, sensor) #define to_rtd(_sensor) \ container_of(_sensor, struct ltc2983_rtd, sensor) +#define to_copper_trace(_sensor) \ + container_of(_sensor, struct ltc2983_copper_trace, sensor) + #define to_thermistor(_sensor) \ container_of(_sensor, struct ltc2983_thermistor, sensor) +#define to_leak_detector(_sensor) \ + container_of(_sensor, struct ltc2983_leak_detector, sensor) + #define to_diode(_sensor) \ container_of(_sensor, struct ltc2983_diode, sensor) @@ -212,7 +241,7 @@ enum { struct ltc2983_chip_info { const char *name; unsigned int max_channels_nr; - bool has_temp; + u64 supported_sensors; bool has_eeprom; }; @@ -247,6 +276,8 @@ struct ltc2983_sensor { u32 chan; /* sensor type */ u32 type; + /* number of IIO channels this sensor produces */ + u8 n_iio_chan; }; struct ltc2983_custom_sensor { @@ -274,6 +305,25 @@ struct ltc2983_rtd { u32 rtd_curve; }; +struct ltc2983_copper_trace { + struct ltc2983_sensor sensor; + struct ltc2983_custom_sensor *custom; + u32 r_sense_chan; + u32 excitation_current; + /* selects the <1Ω variant: bits 17:0 of the channel word are zeroed, + * disabling excitation current and custom table fields (ADT7604 + * datasheet Table 26) + */ + bool is_sub_ohm; +}; + +struct ltc2983_leak_detector { + struct ltc2983_sensor sensor; + struct ltc2983_custom_sensor *custom; + u32 r_sense_chan; + u32 excitation_current; +}; + struct ltc2983_thermistor { struct ltc2983_sensor sensor; struct ltc2983_custom_sensor *custom; @@ -353,8 +403,14 @@ static int __ltc2983_chan_assign_common(struct ltc2983_data *st, { struct device *dev = &st->spi->dev; u32 reg = LTC2983_CHAN_ASSIGN_ADDR(sensor->chan); + u32 hw_type = sensor->type; - chan_val |= LTC2983_CHAN_TYPE(sensor->type); + if (hw_type == LTC2983_SENSOR_COPPER_TRACE) + hw_type = LTC2983_SENSOR_RTD_CUSTOM; + else if (hw_type == LTC2983_SENSOR_LEAK_DETECTOR) + hw_type = LTC2983_SENSOR_THERMISTOR_CUSTOM; + + chan_val |= LTC2983_CHAN_TYPE(hw_type); dev_dbg(dev, "Assign reg:0x%04X, val:0x%08X\n", reg, chan_val); st->chan_val = cpu_to_be32(chan_val); return regmap_bulk_write(st->regmap, reg, &st->chan_val, @@ -485,6 +541,14 @@ __ltc2983_custom_sensor_new(struct ltc2983_data *st, const struct fwnode_handle for (index = 0; index < n_entries; index++) { u64 temp = ((u64 *)new_custom->table)[index]; + /* + * Users specify plain coverage percentage (0-100). Convert + * to µK so __convert_to_raw() produces the correct hardware + * encoding: P + 273.15 K. + */ + if ((index % 2) != 0 && !strcmp(propname, "adi,custom-leak-detector")) + temp = temp * 1000000 + 273150000; + if ((index % 2) != 0) temp = __convert_to_raw(temp, 1024); else if (has_signed && (s64)temp < 0) @@ -578,6 +642,31 @@ static int ltc2983_rtd_assign_chan(struct ltc2983_data *st, return __ltc2983_chan_assign_common(st, sensor, chan_val); } +static int ltc2983_copper_trace_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_copper_trace *ct = to_copper_trace(sensor); + u32 chan_val; + + chan_val = LTC2983_CHAN_ASSIGN(ct->r_sense_chan); + /* Sensor config bits 21:18 must be 0b1001 (ADT7604 datasheet Table 26) */ + chan_val |= LTC2983_RTD_CFG(0x9); + + if (ct->is_sub_ohm) { + chan_val &= ~GENMASK(17, 0); + } else { + int ret; + + chan_val |= LTC2983_RTD_EXC_CURRENT(ct->excitation_current); + ret = __ltc2983_chan_custom_sensor_assign(st, ct->custom, + &chan_val); + if (ret) + return ret; + } + + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + static int ltc2983_thermistor_assign_chan(struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { @@ -601,6 +690,25 @@ static int ltc2983_thermistor_assign_chan(struct ltc2983_data *st, return __ltc2983_chan_assign_common(st, sensor, chan_val); } +static int ltc2983_leak_detector_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_leak_detector *ld = to_leak_detector(sensor); + u32 chan_val; + int ret; + + chan_val = LTC2983_CHAN_ASSIGN(ld->r_sense_chan); + /* bits 21:19 must be 0b001 (ADT7604 datasheet Table 38) */ + chan_val |= LTC2983_THERMISTOR_CFG(1); + chan_val |= LTC2983_THERMISTOR_EXC_CURRENT(ld->excitation_current); + + ret = __ltc2983_chan_custom_sensor_assign(st, ld->custom, &chan_val); + if (ret) + return ret; + + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + static int ltc2983_diode_assign_chan(struct ltc2983_data *st, const struct ltc2983_sensor *sensor) { @@ -1036,6 +1144,195 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s return &thermistor->sensor; } +static struct ltc2983_sensor * +ltc2983_copper_trace_new(const struct fwnode_handle *child, struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct device *dev = &st->spi->dev; + struct ltc2983_copper_trace *ct; + int ret; + + if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) + return dev_err_ptr_probe(dev, -EINVAL, + "Invalid channel %d for copper trace\n", + sensor->chan); + + ct = devm_kzalloc(dev, sizeof(*ct), GFP_KERNEL); + if (!ct) + return ERR_PTR(-ENOMEM); + + struct fwnode_handle *ref __free(fwnode_handle) = + fwnode_find_reference(child, "adi,rsense-handle", 0); + if (IS_ERR(ref)) + return dev_err_cast_probe(dev, ref, + "Property adi,rsense-handle missing or invalid\n"); + + ret = fwnode_property_read_u32(ref, "reg", &ct->r_sense_chan); + if (ret) + return dev_err_ptr_probe(dev, ret, "Property reg must be given\n"); + + ct->is_sub_ohm = fwnode_property_read_bool(child, "adi,copper-trace-sub-ohm"); + + if (ct->is_sub_ohm && fwnode_property_present(child, "adi,custom-copper-trace")) + return dev_err_ptr_probe(dev, -EINVAL, + "sub-ohm copper trace cannot have a custom table\n"); + + if (!ct->is_sub_ohm) { + u32 excitation_current = 0; + + if (!fwnode_property_present(child, "adi,custom-copper-trace")) + return dev_err_ptr_probe(dev, -EINVAL, + "adi,custom-copper-trace is required for >1 ohm copper trace\n"); + + ct->custom = __ltc2983_custom_sensor_new(st, child, "adi,custom-copper-trace", + false, 2048, false); + if (IS_ERR(ct->custom)) + return ERR_CAST(ct->custom); + + if (fwnode_property_present(child, "adi,excitation-current-microamp")) { + ret = fwnode_property_read_u32(child, "adi,excitation-current-microamp", + &excitation_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to read adi,excitation-current-microamp\n"); + + switch (excitation_current) { + case 5: + ct->excitation_current = 0x01; + break; + case 10: + ct->excitation_current = 0x02; + break; + case 25: + ct->excitation_current = 0x03; + break; + case 50: + ct->excitation_current = 0x04; + break; + case 100: + ct->excitation_current = 0x05; + break; + case 250: + ct->excitation_current = 0x06; + break; + case 500: + ct->excitation_current = 0x07; + break; + case 1000: + ct->excitation_current = 0x08; + break; + default: + return dev_err_ptr_probe(dev, -EINVAL, + "Invalid value for excitation current(%u)\n", + excitation_current); + } + } else { + /* default to 1mA per datasheet recommendation for copper trace */ + ct->excitation_current = 0x08; + } + } + + ct->sensor.fault_handler = ltc2983_common_fault_handler; + ct->sensor.assign_chan = ltc2983_copper_trace_assign_chan; + if (ct->is_sub_ohm) + ct->sensor.n_iio_chan = 1; + else + ct->sensor.n_iio_chan = 2; + + return &ct->sensor; +} + +static struct ltc2983_sensor * +ltc2983_leak_detector_new(const struct fwnode_handle *child, struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct device *dev = &st->spi->dev; + struct ltc2983_leak_detector *ld; + int ret; + u32 excitation_current = 0; + + if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) + return dev_err_ptr_probe(dev, -EINVAL, + "Invalid channel %d for leak detector\n", + sensor->chan); + + ld = devm_kzalloc(dev, sizeof(*ld), GFP_KERNEL); + if (!ld) + return ERR_PTR(-ENOMEM); + + struct fwnode_handle *ref __free(fwnode_handle) = + fwnode_find_reference(child, "adi,rsense-handle", 0); + if (IS_ERR(ref)) + return dev_err_cast_probe(dev, ref, + "Property adi,rsense-handle missing or invalid\n"); + + ret = fwnode_property_read_u32(ref, "reg", &ld->r_sense_chan); + if (ret) + return dev_err_ptr_probe(dev, ret, + "rsense channel must be configured\n"); + + if (!fwnode_property_present(child, "adi,custom-leak-detector")) + return dev_err_ptr_probe(dev, -EINVAL, + "adi,custom-leak-detector is required for leak detectors\n"); + + ld->custom = __ltc2983_custom_sensor_new(st, child, "adi,custom-leak-detector", + false, 16, false); + if (IS_ERR(ld->custom)) + return ERR_CAST(ld->custom); + + ret = fwnode_property_read_u32(child, "adi,excitation-current-nanoamp", + &excitation_current); + if (ret) + return dev_err_ptr_probe(dev, ret, + "adi,excitation-current-nanoamp is required for leak detectors\n"); + + switch (excitation_current) { + case 250: + ld->excitation_current = 0x01; + break; + case 500: + ld->excitation_current = 0x02; + break; + case 1000: + ld->excitation_current = 0x03; + break; + case 5000: + ld->excitation_current = 0x04; + break; + case 10000: + ld->excitation_current = 0x05; + break; + case 25000: + ld->excitation_current = 0x06; + break; + case 50000: + ld->excitation_current = 0x07; + break; + case 100000: + ld->excitation_current = 0x08; + break; + case 250000: + ld->excitation_current = 0x09; + break; + case 500000: + ld->excitation_current = 0x0a; + break; + case 1000000: + ld->excitation_current = 0x0b; + break; + default: + return dev_err_ptr_probe(dev, -EINVAL, + "Invalid value for excitation current(%u)\n", + excitation_current); + } + + ld->sensor.fault_handler = ltc2983_common_fault_handler; + ld->sensor.assign_chan = ltc2983_leak_detector_assign_chan; + ld->sensor.n_iio_chan = 2; + + return &ld->sensor; +} + static struct ltc2983_sensor * ltc2983_diode_new(const struct fwnode_handle *child, const struct ltc2983_data *st, const struct ltc2983_sensor *sensor) @@ -1204,7 +1501,8 @@ static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child, } static int ltc2983_chan_read(struct ltc2983_data *st, - const struct ltc2983_sensor *sensor, int *val) + const struct ltc2983_sensor *sensor, + u32 base_reg, int *val) { struct device *dev = &st->spi->dev; u32 start_conversion = 0; @@ -1234,13 +1532,23 @@ static int ltc2983_chan_read(struct ltc2983_data *st, } /* read the converted data */ - ret = regmap_bulk_read(st->regmap, LTC2983_RESULT_ADDR(sensor->chan), + ret = regmap_bulk_read(st->regmap, LTC2983_RESULT_ADDR(sensor->chan, base_reg), &st->temp, sizeof(st->temp)); if (ret) return ret; *val = __be32_to_cpu(st->temp); + if (base_reg == ADT7604_RES_RES_START_REG) { + /* + * Resistance result register gives a plain unsigned value, + * D31 is always 0, no valid bit, no fault bits. Read bits[30:0] + * directly — the temperature result format does not apply here. + */ + *val &= GENMASK(30, 0); + return 0; + } + if (!(LTC2983_RES_VALID_MASK & *val)) { dev_err(dev, "Invalid conversion detected\n"); return -EIO; @@ -1271,7 +1579,16 @@ static int ltc2983_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: mutex_lock(&st->lock); - ret = ltc2983_chan_read(st, st->sensors[chan->address], val); + switch (chan->type) { + case IIO_RESISTANCE: + ret = ltc2983_chan_read(st, st->sensors[chan->address], + ADT7604_RES_RES_START_REG, val); + break; + default: + ret = ltc2983_chan_read(st, st->sensors[chan->address], + LTC2983_TEMP_RES_START_REG, val); + break; + } mutex_unlock(&st->lock); return ret ?: IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -1288,6 +1605,13 @@ static int ltc2983_read_raw(struct iio_dev *indio_dev, /* 2^21 */ *val2 = 2097152; return IIO_VAL_FRACTIONAL; + case IIO_RESISTANCE: + case IIO_COVERAGE: + /* value in ohm/percent */ + *val = 1; + /* 2^10 */ + *val2 = 1024; + return IIO_VAL_FRACTIONAL; default: return -EINVAL; } @@ -1348,7 +1672,7 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) if (!st->sensors) return -ENOMEM; - st->iio_channels = st->num_channels; + st->iio_channels = 0; device_for_each_child_node_scoped(dev, child) { struct ltc2983_sensor sensor; @@ -1376,6 +1700,12 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) return dev_err_probe(dev, ret, "adi,sensor-type property must given for child nodes\n"); + if (sensor.type >= LTC2983_SENSOR_NUM || + !(st->info->supported_sensors & BIT_ULL(sensor.type))) + return dev_err_probe(dev, -EINVAL, + "sensor type %d not supported on %s\n", + sensor.type, st->info->name); + dev_dbg(dev, "Create new sensor, type %u, channel %u", sensor.type, sensor.chan); @@ -1396,13 +1726,14 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) } else if (sensor.type == LTC2983_SENSOR_SENSE_RESISTOR) { st->sensors[chan] = ltc2983_r_sense_new(child, st, &sensor); - /* don't add rsense to iio */ - st->iio_channels--; } else if (sensor.type == LTC2983_SENSOR_DIRECT_ADC) { st->sensors[chan] = ltc2983_adc_new(child, st, &sensor); - } else if (st->info->has_temp && - sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) { + } else if (sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) { st->sensors[chan] = ltc2983_temp_new(child, st, &sensor); + } else if (sensor.type == LTC2983_SENSOR_COPPER_TRACE) { + st->sensors[chan] = ltc2983_copper_trace_new(child, st, &sensor); + } else if (sensor.type == LTC2983_SENSOR_LEAK_DETECTOR) { + st->sensors[chan] = ltc2983_leak_detector_new(child, st, &sensor); } else { return dev_err_probe(dev, -EINVAL, "Unknown sensor type %d\n", @@ -1417,6 +1748,16 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) st->sensors[chan]->chan = sensor.chan; st->sensors[chan]->type = sensor.type; + /* + * Dedicated functions set n_iio_chan themselves; for all other + * sensor types rsense produces 0 channels, everything else 1. + */ + if (!st->sensors[chan]->n_iio_chan) { + if (sensor.type != LTC2983_SENSOR_SENSE_RESISTOR) + st->sensors[chan]->n_iio_chan = 1; + } + st->iio_channels += st->sensors[chan]->n_iio_chan; + channel_avail_mask |= BIT(sensor.chan); chan++; } @@ -1464,8 +1805,9 @@ static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) { - u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0, status; struct device *dev = &st->spi->dev; + u32 iio_chan_t = 0, iio_chan_v = 0, iio_chan_r = 0, iio_chan_c = 0; + u32 chan, iio_idx = 0, status; int ret; /* make sure the device is up: start bit (7) is 0 and done bit (6) is 1 */ @@ -1512,12 +1854,33 @@ static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) continue; /* assign iio channel */ - if (st->sensors[chan]->type != LTC2983_SENSOR_DIRECT_ADC) { - chan_type = IIO_TEMP; - iio_chan = &iio_chan_t; - } else { + switch (st->sensors[chan]->type) { + case LTC2983_SENSOR_COPPER_TRACE: + if (st->sensors[chan]->n_iio_chan == 1) { + /* sub-ohm copper traces produce only a resistance result */ + st->iio_chan[iio_idx++] = + LTC2983_CHAN(IIO_RESISTANCE, iio_chan_r++, chan); + } else { + st->iio_chan[iio_idx++] = + LTC2983_CHAN(IIO_TEMP, iio_chan_t++, chan); + st->iio_chan[iio_idx++] = + LTC2983_CHAN(IIO_RESISTANCE, iio_chan_r++, chan); + } + continue; + case LTC2983_SENSOR_LEAK_DETECTOR: + st->iio_chan[iio_idx++] = + LTC2983_CHAN(IIO_COVERAGE, iio_chan_c++, chan); + st->iio_chan[iio_idx++] = + LTC2983_CHAN(IIO_RESISTANCE, iio_chan_r++, chan); + continue; + case LTC2983_SENSOR_DIRECT_ADC: chan_type = IIO_VOLTAGE; iio_chan = &iio_chan_v; + break; + default: + chan_type = IIO_TEMP; + iio_chan = &iio_chan_t; + break; } /* @@ -1534,6 +1897,7 @@ static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) static const struct regmap_range ltc2983_reg_ranges[] = { regmap_reg_range(LTC2983_STATUS_REG, LTC2983_STATUS_REG), regmap_reg_range(LTC2983_TEMP_RES_START_REG, LTC2983_TEMP_RES_END_REG), + regmap_reg_range(ADT7604_RES_RES_START_REG, ADT7604_RES_RES_END_REG), regmap_reg_range(LTC2983_EEPROM_KEY_REG, LTC2983_EEPROM_KEY_REG), regmap_reg_range(LTC2983_EEPROM_READ_STATUS_REG, LTC2983_EEPROM_READ_STATUS_REG), @@ -1672,32 +2036,42 @@ static int ltc2983_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(ltc2983_pm_ops, ltc2983_suspend, ltc2983_resume); +static const struct ltc2983_chip_info adt7604_chip_info_data = { + .name = "adt7604", + .max_channels_nr = 20, + .has_eeprom = true, + .supported_sensors = ADT7604_SENSORS, +}; + static const struct ltc2983_chip_info ltc2983_chip_info_data = { .name = "ltc2983", .max_channels_nr = 20, + .supported_sensors = LTC2983_COMMON_SENSORS, }; static const struct ltc2983_chip_info ltc2984_chip_info_data = { .name = "ltc2984", .max_channels_nr = 20, .has_eeprom = true, + .supported_sensors = LTC2983_COMMON_SENSORS, }; static const struct ltc2983_chip_info ltc2986_chip_info_data = { .name = "ltc2986", .max_channels_nr = 10, - .has_temp = true, .has_eeprom = true, + .supported_sensors = LTC2983_COMMON_SENSORS | BIT_ULL(LTC2983_SENSOR_ACTIVE_TEMP), }; static const struct ltc2983_chip_info ltm2985_chip_info_data = { .name = "ltm2985", .max_channels_nr = 10, - .has_temp = true, .has_eeprom = true, + .supported_sensors = LTC2983_COMMON_SENSORS | BIT_ULL(LTC2983_SENSOR_ACTIVE_TEMP), }; static const struct spi_device_id ltc2983_id_table[] = { + { "adt7604", (kernel_ulong_t)&adt7604_chip_info_data }, { "ltc2983", (kernel_ulong_t)<c2983_chip_info_data }, { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, @@ -1707,6 +2081,7 @@ static const struct spi_device_id ltc2983_id_table[] = { MODULE_DEVICE_TABLE(spi, ltc2983_id_table); static const struct of_device_id ltc2983_of_match[] = { + { .compatible = "adi,adt7604", .data = &adt7604_chip_info_data }, { .compatible = "adi,ltc2983", .data = <c2983_chip_info_data }, { .compatible = "adi,ltc2984", .data = <c2984_chip_info_data }, { .compatible = "adi,ltc2986", .data = <c2986_chip_info_data }, From 36862727e0079f8edcb2869d994a0d394513d857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 12:16:49 +0200 Subject: [PATCH 2411/5214] iio: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Jonathan Cameron --- drivers/iio/adc/88pm886-gpadc.c | 2 +- drivers/iio/adc/max77541-adc.c | 2 +- drivers/iio/adc/sun4i-gpadc-iio.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c index cffe35136685..4435f3d5e2b8 100644 --- a/drivers/iio/adc/88pm886-gpadc.c +++ b/drivers/iio/adc/88pm886-gpadc.c @@ -373,7 +373,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops, pm886_gpadc_runtime_resume, NULL); static const struct platform_device_id pm886_gpadc_id[] = { - { "88pm886-gpadc" }, + { .name = "88pm886-gpadc" }, { } }; MODULE_DEVICE_TABLE(platform, pm886_gpadc_id); diff --git a/drivers/iio/adc/max77541-adc.c b/drivers/iio/adc/max77541-adc.c index 0aa04d143ad4..013da014bccd 100644 --- a/drivers/iio/adc/max77541-adc.c +++ b/drivers/iio/adc/max77541-adc.c @@ -175,7 +175,7 @@ static int max77541_adc_probe(struct platform_device *pdev) } static const struct platform_device_id max77541_adc_platform_id[] = { - { "max77541-adc" }, + { .name = "max77541-adc" }, { } }; MODULE_DEVICE_TABLE(platform, max77541_adc_platform_id); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 479115ea50bf..203459ca9907 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -679,9 +679,9 @@ static void sun4i_gpadc_remove(struct platform_device *pdev) } static const struct platform_device_id sun4i_gpadc_id[] = { - { "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data }, - { "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data }, - { "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data }, + { .name = "sun4i-a10-gpadc-iio", .driver_data = (kernel_ulong_t)&sun4i_gpadc_data }, + { .name = "sun5i-a13-gpadc-iio", .driver_data = (kernel_ulong_t)&sun5i_gpadc_data }, + { .name = "sun6i-a31-gpadc-iio", .driver_data = (kernel_ulong_t)&sun6i_gpadc_data }, { } }; MODULE_DEVICE_TABLE(platform, sun4i_gpadc_id); From 40d7dc96cf14e2e37fb9501e7d25fde10b858596 Mon Sep 17 00:00:00 2001 From: Matheus Silveira Date: Thu, 28 May 2026 15:55:12 -0300 Subject: [PATCH 2412/5214] light: tsl2591: simplify tsl2591_persist functions via lookup table Replace switch statements with an indexed lookup table for persist cycle conversions. Both functions contain redundant switch statements. This reduces code duplication and makes future updates to TSL2591_PRST_ALS_INT_CYCLE_* definitions easier to maintain by keeping the mapping in a single place. Signed-off-by: Matheus Silveira Co-developed-by: Lucas Rabaquim Signed-off-by: Lucas Rabaquim Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2591.c | 94 +++++++++++-------------------------- 1 file changed, 28 insertions(+), 66 deletions(-) diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index c5557867ea43..ac5e1822b993 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -192,6 +192,24 @@ static const int tsl2591_calibscale_available[] = { 1, 25, 428, 9876, }; +static const u8 tsl2591_persist_table[] = { + [TSL2591_PRST_ALS_INT_CYCLE_ANY] = 1, + [TSL2591_PRST_ALS_INT_CYCLE_2] = 2, + [TSL2591_PRST_ALS_INT_CYCLE_3] = 3, + [TSL2591_PRST_ALS_INT_CYCLE_5] = 5, + [TSL2591_PRST_ALS_INT_CYCLE_10] = 10, + [TSL2591_PRST_ALS_INT_CYCLE_15] = 15, + [TSL2591_PRST_ALS_INT_CYCLE_20] = 20, + [TSL2591_PRST_ALS_INT_CYCLE_25] = 25, + [TSL2591_PRST_ALS_INT_CYCLE_30] = 30, + [TSL2591_PRST_ALS_INT_CYCLE_35] = 35, + [TSL2591_PRST_ALS_INT_CYCLE_40] = 40, + [TSL2591_PRST_ALS_INT_CYCLE_45] = 45, + [TSL2591_PRST_ALS_INT_CYCLE_50] = 50, + [TSL2591_PRST_ALS_INT_CYCLE_55] = 55, + [TSL2591_PRST_ALS_INT_CYCLE_60] = 60, +}; + static int tsl2591_set_als_lower_threshold(struct tsl2591_chip *chip, u16 als_lower_threshold); static int tsl2591_set_als_upper_threshold(struct tsl2591_chip *chip, @@ -231,78 +249,22 @@ static int tsl2591_multiplier_to_gain(const u32 multiplier) static int tsl2591_persist_cycle_to_lit(const u8 als_persist) { - switch (als_persist) { - case TSL2591_PRST_ALS_INT_CYCLE_ANY: - return 1; - case TSL2591_PRST_ALS_INT_CYCLE_2: - return 2; - case TSL2591_PRST_ALS_INT_CYCLE_3: - return 3; - case TSL2591_PRST_ALS_INT_CYCLE_5: - return 5; - case TSL2591_PRST_ALS_INT_CYCLE_10: - return 10; - case TSL2591_PRST_ALS_INT_CYCLE_15: - return 15; - case TSL2591_PRST_ALS_INT_CYCLE_20: - return 20; - case TSL2591_PRST_ALS_INT_CYCLE_25: - return 25; - case TSL2591_PRST_ALS_INT_CYCLE_30: - return 30; - case TSL2591_PRST_ALS_INT_CYCLE_35: - return 35; - case TSL2591_PRST_ALS_INT_CYCLE_40: - return 40; - case TSL2591_PRST_ALS_INT_CYCLE_45: - return 45; - case TSL2591_PRST_ALS_INT_CYCLE_50: - return 50; - case TSL2591_PRST_ALS_INT_CYCLE_55: - return 55; - case TSL2591_PRST_ALS_INT_CYCLE_60: - return 60; - default: + if (als_persist >= ARRAY_SIZE(tsl2591_persist_table) || + !tsl2591_persist_table[als_persist]) return -EINVAL; - } + + return tsl2591_persist_table[als_persist]; } static int tsl2591_persist_lit_to_cycle(const u8 als_persist) { - switch (als_persist) { - case 1: - return TSL2591_PRST_ALS_INT_CYCLE_ANY; - case 2: - return TSL2591_PRST_ALS_INT_CYCLE_2; - case 3: - return TSL2591_PRST_ALS_INT_CYCLE_3; - case 5: - return TSL2591_PRST_ALS_INT_CYCLE_5; - case 10: - return TSL2591_PRST_ALS_INT_CYCLE_10; - case 15: - return TSL2591_PRST_ALS_INT_CYCLE_15; - case 20: - return TSL2591_PRST_ALS_INT_CYCLE_20; - case 25: - return TSL2591_PRST_ALS_INT_CYCLE_25; - case 30: - return TSL2591_PRST_ALS_INT_CYCLE_30; - case 35: - return TSL2591_PRST_ALS_INT_CYCLE_35; - case 40: - return TSL2591_PRST_ALS_INT_CYCLE_40; - case 45: - return TSL2591_PRST_ALS_INT_CYCLE_45; - case 50: - return TSL2591_PRST_ALS_INT_CYCLE_50; - case 55: - return TSL2591_PRST_ALS_INT_CYCLE_55; - case 60: - return TSL2591_PRST_ALS_INT_CYCLE_60; - default: - return -EINVAL; + for (unsigned int i = TSL2591_PRST_ALS_INT_CYCLE_ANY; + i < ARRAY_SIZE(tsl2591_persist_table); i++) { + if (als_persist == tsl2591_persist_table[i]) + return i; } + + return -EINVAL; } static int tsl2591_compatible_int_time(struct tsl2591_chip *chip, From 4bff2ca299740182d2b05f425c02295e23177390 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 29 May 2026 17:13:52 +0300 Subject: [PATCH 2413/5214] dt-bindings: iio: adc: ad4080: add AD4884 support Add AD4884 compatible string to the AD4080 devicetree binding. The AD4884 is a dual-channel, 16-bit, 40 MSPS SAR ADC, sharing the same register map and interface as the AD4080 family. Like the AD4880, it requires two SPI chip selects and two io-backends for its independent ADC channels. The AD4884 differs from the AD4880 in resolution (16-bit vs 20-bit), which requires distinct channel configuration in the driver, precluding a fallback compatible. Acked-by: Conor Dooley Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index 9c6a56c7c8ef..4a3f7d3e05c3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -39,6 +39,7 @@ properties: - adi,ad4087 - adi,ad4088 - adi,ad4880 + - adi,ad4884 reg: minItems: 1 @@ -99,7 +100,9 @@ allOf: properties: compatible: contains: - const: adi,ad4880 + enum: + - adi,ad4880 + - adi,ad4884 then: properties: reg: From 7853cf5b65d2b70834cf49c7fb1ff6810120a8e7 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 29 May 2026 17:13:53 +0300 Subject: [PATCH 2414/5214] iio: adc: ad4080: add support for AD4884 Add support for the AD4884, a dual-channel, 16-bit, 40 MSPS SAR ADC. The AD4884 is the dual-channel variant of the AD4084, sharing the same register map and SPI interface as the rest of the AD4080 family. Like the AD4880, it uses two independent ADC channels, each with its own SPI configuration interface. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 265d85ac171a..764d49eca9e0 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -136,6 +136,7 @@ #define AD4087_CHIP_ID 0x0057 #define AD4088_CHIP_ID 0x0058 #define AD4880_CHIP_ID 0x0750 +#define AD4884_CHIP_ID 0x005C #define AD4080_MAX_CHANNELS 2 @@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = { AD4880_CHANNEL_DEFINE(20, 32, 1), }; +static const struct iio_chan_spec ad4884_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", .product_id = AD4080_CHIP_ID, @@ -641,6 +647,16 @@ static const struct ad4080_chip_info ad4880_chip_info = { .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, }; +static const struct ad4080_chip_info ad4884_chip_info = { + .name = "ad4884", + .product_id = AD4884_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4884_channels, + .lvds_cnv_clk_cnt_max = 2, +}; + static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) { struct device *dev = regmap_get_device(st->regmap[ch]); @@ -843,6 +859,7 @@ static const struct spi_device_id ad4080_id[] = { { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, + { "ad4884", (kernel_ulong_t)&ad4884_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -858,6 +875,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4087", &ad4087_chip_info }, { .compatible = "adi,ad4088", &ad4088_chip_info }, { .compatible = "adi,ad4880", &ad4880_chip_info }, + { .compatible = "adi,ad4884", &ad4884_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); From 1ea5792ad7c75e8d6ec62fcaccfbf38fa82ef978 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:00 +0300 Subject: [PATCH 2415/5214] dt-bindings: iio: adc: add AD4691 family Add DT bindings for the Analog Devices AD4691 family of multichannel SAR ADCs (AD4691, AD4692, AD4693, AD4694). The four variants are not compatible with each other: AD4691/AD4692 have 16 analog input channels while AD4693/AD4694 have 8, and AD4691/AD4693 top out at 500 kSPS while AD4692/AD4694 reach 1 MSPS. These differences in channel count and maximum sample rate require distinct compatible strings so the driver can select the correct channel configuration and rate limits. Acked-by: Conor Dooley Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ad4691.yaml | 180 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 187 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml new file mode 100644 index 000000000000..af28a0c1cfa9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4691.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4691 Family Multichannel SAR ADCs + +maintainers: + - Radu Sabau + +description: | + The AD4691 family are high-speed, low-power, multichannel successive + approximation register (SAR) analog-to-digital converters (ADCs) with + an SPI-compatible serial interface. The ADC supports CNV Burst Mode, + where an external PWM drives the CNV pin, and Manual Mode, where CNV + is directly tied to the SPI chip-select. + + Datasheets: + * https://www.analog.com/en/products/ad4691.html + * https://www.analog.com/en/products/ad4692.html + * https://www.analog.com/en/products/ad4693.html + * https://www.analog.com/en/products/ad4694.html + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,ad4691 + - adi,ad4692 + - adi,ad4693 + - adi,ad4694 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 40000000 + + spi-cpol: true + spi-cpha: true + + avdd-supply: + description: Analog power supply (4.5V to 5.5V). + + vdd-supply: + description: + External 1.8V digital core supply. When present, the internal LDO is + disabled (LDO_EN = 0). Mutually exclusive with ldo-in-supply. + + ldo-in-supply: + description: + LDO input supply (2.4V to 5.5V). When present and vdd-supply is absent, + the internal LDO generates 1.8V VDD from this input (LDO_EN = 1). + Mutually exclusive with vdd-supply. + + vio-supply: + description: I/O voltage supply (1.71V to 1.89V or VDD). + + ref-supply: + description: External reference voltage supply (2.4V to 5.25V). + + refin-supply: + description: Internal reference buffer input supply. + + reset-gpios: + description: + GPIO line controlling the hardware reset pin (active-low). + maxItems: 1 + + pwms: + description: + PWM connected to the CNV pin. When present, selects CNV Burst Mode where + the PWM drives the conversion rate. When absent, Manual Mode is used + (CNV tied to SPI CS). + maxItems: 1 + + interrupts: + description: + Interrupt lines connected to the ADC GP pins. Each GP pin can be + physically wired to an interrupt-capable input on the SoC. + maxItems: 4 + + interrupt-names: + description: Names of the interrupt lines, matching the GP pin names. + minItems: 1 + maxItems: 4 + items: + enum: + - gp0 + - gp1 + - gp2 + - gp3 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + '#trigger-source-cells': + description: + This node can act as a trigger source. The single cell in a consumer + reference specifies the GP pin number (0-3) used as the trigger output. + const: 1 + +required: + - compatible + - reg + - avdd-supply + - vio-supply + +allOf: + # vdd-supply and ldo-in-supply are mutually exclusive, one is required: + # either an external 1.8V VDD is provided or the internal LDO is fed from + # ldo-in-supply to generate VDD. + - oneOf: + - required: + - vdd-supply + - required: + - ldo-in-supply + # ref-supply and refin-supply are mutually exclusive, one is required + - oneOf: + - required: + - ref-supply + - required: + - refin-supply + +unevaluatedProperties: false + +examples: + - | + #include + /* AD4692 in CNV Burst Mode with SPI offload */ + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4692"; + reg = <0>; + spi-cpol; + spi-cpha; + spi-max-frequency = <40000000>; + + avdd-supply = <&avdd_supply>; + ldo-in-supply = <&avdd_supply>; + vio-supply = <&vio_supply>; + ref-supply = <&ref_5v>; + + reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; + + pwms = <&pwm_gen 0 0>; + + #trigger-source-cells = <1>; + }; + }; + + - | + #include + /* AD4692 in Manual Mode (CNV tied to SPI CS) */ + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4692"; + reg = <0>; + spi-cpol; + spi-cpha; + spi-max-frequency = <31250000>; + + avdd-supply = <&avdd_supply>; + ldo-in-supply = <&avdd_supply>; + vio-supply = <&vio_supply>; + refin-supply = <&refin_supply>; + + reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 9e69cc754cf5..95ba426ef56e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1476,6 +1476,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml F: drivers/iio/adc/ad4170-4.c +ANALOG DEVICES INC AD4691 DRIVER +M: Radu Sabau +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml + ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich M: Nuno Sá From 40850443aa1239f71fc65b870a0f0ecd54e42c9d Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:01 +0300 Subject: [PATCH 2416/5214] iio: adc: ad4691: add initial driver for AD4691 family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the Analog Devices AD4691 family of high-speed, low-power multichannel SAR ADCs: AD4691 (16-ch, 500 kSPS), AD4692 (16-ch, 1 MSPS), AD4693 (8-ch, 500 kSPS) and AD4694 (8-ch, 1 MSPS). The driver implements a custom regmap layer over raw SPI to handle the device's mixed 1/2/3/4-byte register widths and uses the standard IIO read_raw/write_raw interface for single-channel reads. The chip idles in Autonomous Mode so that single-shot read_raw can use the internal oscillator without disturbing the hardware configuration. Three voltage supply domains are managed: avdd (required), vio, and a reference supply on either the REF pin (ref-supply, external buffer) or the REFIN pin (refin-supply, uses the on-chip reference buffer; REFBUF_EN is set accordingly). Hardware reset is performed by asserting then deasserting the reset-gpios GPIO line (tRESETL minimum pulse width is 10 ns, satisfied by function-call overhead); the driver then waits 300 µs for the chip to complete its internal reset sequence. A software reset via SPI_CONFIG_A is used as fallback when no reset GPIO is provided. Accumulator channel masking for single-shot reads uses ACC_MASK_REG via an ADDR_DESCENDING SPI write, which covers both mask bytes in a single 16-bit transfer. Reviewed-by: David Lechner Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4691.c | 783 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 797 insertions(+) create mode 100644 drivers/iio/adc/ad4691.c diff --git a/MAINTAINERS b/MAINTAINERS index 95ba426ef56e..4cb938531c64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1482,6 +1482,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml +F: drivers/iio/adc/ad4691.c ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1115e81ac45b..d07e4d839f25 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -144,6 +144,18 @@ config AD4170_4 To compile this driver as a module, choose M here: the module will be called ad4170-4. +config AD4691 + tristate "Analog Devices AD4691 Family ADC Driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + select REGMAP + help + Say yes here to build support for Analog Devices AD4691 Family MuxSAR + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4691. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 097357d146ba..707dd708912f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_AD4080) += ad4080.o obj-$(CONFIG_AD4130) += ad4130.o obj-$(CONFIG_AD4134) += ad4134.o obj-$(CONFIG_AD4170_4) += ad4170-4.o +obj-$(CONFIG_AD4691) += ad4691.o obj-$(CONFIG_AD4695) += ad4695.o obj-$(CONFIG_AD4851) += ad4851.o obj-$(CONFIG_AD7091R) += ad7091r-base.o diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c new file mode 100644 index 000000000000..e1febf80f21d --- /dev/null +++ b/drivers/iio/adc/ad4691.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024-2026 Analog Devices, Inc. + * Author: Radu Sabau + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD4691_VREF_uV_MIN 2400000 +#define AD4691_VREF_uV_MAX 5250000 +#define AD4691_VREF_2P5_uV_MAX 2750000 +#define AD4691_VREF_3P0_uV_MAX 3250000 +#define AD4691_VREF_3P3_uV_MAX 3750000 +#define AD4691_VREF_4P096_uV_MAX 4500000 + +#define AD4691_CNV_DUTY_CYCLE_NS 380 +#define AD4691_CNV_HIGH_TIME_NS 430 + +#define AD4691_SPI_CONFIG_A_REG 0x000 +#define AD4691_SW_RESET (BIT(7) | BIT(0)) + +#define AD4691_STATUS_REG 0x014 +#define AD4691_CLAMP_STATUS1_REG 0x01A +#define AD4691_CLAMP_STATUS2_REG 0x01B +#define AD4691_DEVICE_SETUP 0x020 +#define AD4691_MANUAL_MODE BIT(2) +#define AD4691_LDO_EN BIT(4) +#define AD4691_REF_CTRL 0x021 +#define AD4691_REF_CTRL_MASK GENMASK(4, 2) +#define AD4691_REFBUF_EN BIT(0) +#define AD4691_OSC_FREQ_REG 0x023 +#define AD4691_OSC_FREQ_MASK GENMASK(3, 0) +#define AD4691_STD_SEQ_CONFIG 0x025 +#define AD4691_SEQ_ALL_CHANNELS_OFF 0x00 +#define AD4691_SPARE_CONTROL 0x02A + +#define AD4691_MAX_CHANNELS 16 + +#define AD4691_NOOP 0x00 +#define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3) + +#define AD4691_OSC_EN_REG 0x180 +#define AD4691_STATE_RESET_REG 0x181 +#define AD4691_STATE_RESET_ALL BIT(0) +#define AD4691_ADC_SETUP 0x182 +#define AD4691_ADC_MODE_MASK GENMASK(1, 0) +#define AD4691_CNV_BURST_MODE 0x01 +#define AD4691_AUTONOMOUS_MODE 0x02 +/* + * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a + * 16-bit BE value to 0x185 auto-decrements to 0x184 for the second byte. + */ +#define AD4691_ACC_MASK_REG 0x185 +#define AD4691_ACC_DEPTH_IN(n) (0x186 + (n)) +#define AD4691_GPIO_MODE1_REG 0x196 +#define AD4691_GPIO_MODE2_REG 0x197 +#define AD4691_GP_MODE_MASK GENMASK(3, 0) +#define AD4691_GP_MODE_DATA_READY 0x06 +#define AD4691_GPIO_READ 0x1A0 +#define AD4691_ACC_STATUS_FULL1_REG 0x1B0 +#define AD4691_ACC_STATUS_FULL2_REG 0x1B1 +#define AD4691_ACC_STATUS_OVERRUN1_REG 0x1B2 +#define AD4691_ACC_STATUS_OVERRUN2_REG 0x1B3 +#define AD4691_ACC_STATUS_SAT1_REG 0x1B4 +#define AD4691_ACC_STATUS_SAT2_REG 0x1BE +#define AD4691_ACC_SAT_OVR_REG(n) (0x1C0 + (n)) +#define AD4691_AVG_IN(n) (0x201 + (2 * (n))) +#define AD4691_AVG_STS_IN(n) (0x222 + (3 * (n))) +#define AD4691_ACC_IN(n) (0x252 + (3 * (n))) +#define AD4691_ACC_STS_DATA(n) (0x283 + (4 * (n))) + + +static const char * const ad4691_supplies[] = { "avdd", "vio" }; + +enum ad4691_ref_ctrl { + AD4691_VREF_2P5, + AD4691_VREF_3P0, + AD4691_VREF_3P3, + AD4691_VREF_4P096, + AD4691_VREF_5P0 +}; + +struct ad4691_channel_info { + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + unsigned int num_channels; +}; + +struct ad4691_chip_info { + const char *name; + unsigned int max_rate; + const struct ad4691_channel_info *sw_info; +}; + +#define AD4691_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .channel = ch, \ + .scan_type = { \ + .realbits = 16, \ + }, \ + } + +static const struct iio_chan_spec ad4691_channels[] = { + AD4691_CHANNEL(0), + AD4691_CHANNEL(1), + AD4691_CHANNEL(2), + AD4691_CHANNEL(3), + AD4691_CHANNEL(4), + AD4691_CHANNEL(5), + AD4691_CHANNEL(6), + AD4691_CHANNEL(7), + AD4691_CHANNEL(8), + AD4691_CHANNEL(9), + AD4691_CHANNEL(10), + AD4691_CHANNEL(11), + AD4691_CHANNEL(12), + AD4691_CHANNEL(13), + AD4691_CHANNEL(14), + AD4691_CHANNEL(15), +}; + +static const struct iio_chan_spec ad4693_channels[] = { + AD4691_CHANNEL(0), + AD4691_CHANNEL(1), + AD4691_CHANNEL(2), + AD4691_CHANNEL(3), + AD4691_CHANNEL(4), + AD4691_CHANNEL(5), + AD4691_CHANNEL(6), + AD4691_CHANNEL(7), +}; + +static const struct ad4691_channel_info ad4691_sw_info = { + .channels = ad4691_channels, + .num_channels = ARRAY_SIZE(ad4691_channels), +}; + +static const struct ad4691_channel_info ad4693_sw_info = { + .channels = ad4693_channels, + .num_channels = ARRAY_SIZE(ad4693_channels), +}; + +/* + * Internal oscillator frequency table. Index is the OSC_FREQ_REG[3:0] value. + * Index 0 (1 MHz) is only valid for AD4692/AD4694; AD4691/AD4693 support + * up to 500 kHz and use index 1 as their highest valid rate. + */ +static const int ad4691_osc_freqs_Hz[] = { + [0x0] = 1000000, + [0x1] = 500000, + [0x2] = 400000, + [0x3] = 250000, + [0x4] = 200000, + [0x5] = 167000, + [0x6] = 133000, + [0x7] = 125000, + [0x8] = 100000, + [0x9] = 50000, + [0xA] = 25000, + [0xB] = 12500, + [0xC] = 10000, + [0xD] = 5000, + [0xE] = 2500, + [0xF] = 1250, +}; + +static const struct ad4691_chip_info ad4691_chip_info = { + .name = "ad4691", + .max_rate = 500 * HZ_PER_KHZ, + .sw_info = &ad4691_sw_info, +}; + +static const struct ad4691_chip_info ad4692_chip_info = { + .name = "ad4692", + .max_rate = 1 * HZ_PER_MHZ, + .sw_info = &ad4691_sw_info, +}; + +static const struct ad4691_chip_info ad4693_chip_info = { + .name = "ad4693", + .max_rate = 500 * HZ_PER_KHZ, + .sw_info = &ad4693_sw_info, +}; + +static const struct ad4691_chip_info ad4694_chip_info = { + .name = "ad4694", + .max_rate = 1 * HZ_PER_MHZ, + .sw_info = &ad4693_sw_info, +}; + +struct ad4691_state { + const struct ad4691_chip_info *info; + struct regmap *regmap; + struct spi_device *spi; + + int vref_uV; + + bool refbuf_en; + bool ldo_en; + /* + * Synchronize access to members of the driver state, and ensure + * atomicity of consecutive SPI operations. + */ + struct mutex lock; +}; + +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct spi_device *spi = context; + u8 tx[2], rx[4]; + int ret; + + /* Set bit 15 to mark the operation as READ. */ + put_unaligned_be16(0x8000 | reg, tx); + + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1: + case AD4691_ACC_MASK_REG + 1 ... AD4691_ACC_SAT_OVR_REG(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 1); + if (ret) + return ret; + *val = rx[0]; + return 0; + case AD4691_ACC_MASK_REG: + case AD4691_STD_SEQ_CONFIG: + case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 2); + if (ret) + return ret; + *val = get_unaligned_be16(rx); + return 0; + case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15): + case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 3); + if (ret) + return ret; + *val = get_unaligned_be24(rx); + return 0; + case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 4); + if (ret) + return ret; + *val = get_unaligned_be32(rx); + return 0; + default: + return -EINVAL; + } +} + +static int ad4691_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct spi_device *spi = context; + u8 tx[4]; + + put_unaligned_be16(reg, tx); + + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1: + case AD4691_ACC_MASK_REG + 1 ... AD4691_GPIO_MODE2_REG: + if (val > U8_MAX) + return -EINVAL; + tx[2] = val; + return spi_write_then_read(spi, tx, 3, NULL, 0); + case AD4691_ACC_MASK_REG: + case AD4691_STD_SEQ_CONFIG: + if (val > U16_MAX) + return -EINVAL; + put_unaligned_be16(val, &tx[2]); + return spi_write_then_read(spi, tx, 4, NULL, 0); + default: + return -EINVAL; + } +} + +static bool ad4691_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD4691_STATUS_REG: + case AD4691_CLAMP_STATUS1_REG: + case AD4691_CLAMP_STATUS2_REG: + case AD4691_GPIO_READ: + case AD4691_ACC_STATUS_FULL1_REG ... AD4691_ACC_STATUS_SAT2_REG: + case AD4691_ACC_SAT_OVR_REG(0) ... AD4691_ACC_SAT_OVR_REG(15): + case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15): + case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15): + case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15): + case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15): + return true; + default: + return false; + } +} + +static bool ad4691_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15): + case AD4691_STD_SEQ_CONFIG: + return true; + default: + break; + } + + /* + * Multi-byte result registers have non-unit strides; only the base + * address of each entry is a valid single-register read. + */ + if (reg >= AD4691_AVG_IN(0) && reg <= AD4691_AVG_IN(15)) + return (reg - AD4691_AVG_IN(0)) % 2 == 0; + if (reg >= AD4691_AVG_STS_IN(0) && reg <= AD4691_AVG_STS_IN(15)) + return (reg - AD4691_AVG_STS_IN(0)) % 3 == 0; + if (reg >= AD4691_ACC_IN(0) && reg <= AD4691_ACC_IN(15)) + return (reg - AD4691_ACC_IN(0)) % 3 == 0; + if (reg >= AD4691_ACC_STS_DATA(0) && reg <= AD4691_ACC_STS_DATA(15)) + return (reg - AD4691_ACC_STS_DATA(0)) % 4 == 0; + + return false; +} + +static bool ad4691_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_STD_SEQ_CONFIG: + case AD4691_SPARE_CONTROL ... AD4691_GPIO_MODE2_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ad4691_regmap_config = { + .reg_bits = 16, + .val_bits = 32, + .reg_read = ad4691_reg_read, + .reg_write = ad4691_reg_write, + .volatile_reg = ad4691_volatile_reg, + .readable_reg = ad4691_readable_reg, + .writeable_reg = ad4691_writeable_reg, + .max_register = AD4691_ACC_STS_DATA(15), + .cache_type = REGCACHE_MAPLE, +}; + +/* + * Index 0 in ad4691_osc_freqs_Hz is 1 MHz — valid only for AD4692/AD4694 + * (max_rate == 1 MHz). AD4691/AD4693 cap at 500 kHz so their valid range + * starts at index 1. + */ +static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info) +{ + return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1; +} + +static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val); + if (ret) + return ret; + + *val = ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)]; + return IIO_VAL_INT; +} + +static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq) +{ + unsigned int start = ad4691_samp_freq_start(st->info); + + guard(mutex)(&st->lock); + + for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { + if (ad4691_osc_freqs_Hz[i] != freq) + continue; + return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG, + AD4691_OSC_FREQ_MASK, i); + } + + return -EINVAL; +} + +static int ad4691_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int start = ad4691_samp_freq_start(st->info); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = &ad4691_osc_freqs_Hz[start]; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4691_single_shot_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int reg_val, osc_idx, period_us; + int ret; + + guard(mutex)(&st->lock); + + /* Use AUTONOMOUS mode for single-shot reads. */ + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG, AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, + BIT(chan->channel)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, + ~BIT(chan->channel) & GENMASK(15, 0)); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 1); + if (ret) + return ret; + + osc_idx = FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val); + /* Wait 2 oscillator periods for the conversion to complete. */ + period_us = DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_idx]); + fsleep(period_us); + + ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4691_AVG_IN(chan->channel), ®_val); + if (ret) + return ret; + + *val = reg_val; + + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG, AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4691_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + return ad4691_single_shot_read(indio_dev, chan, val); + } + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4691_get_sampling_freq(st, val); + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uV / (MICRO / MILLI); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad4691_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4691_set_sampling_freq(st, val); + default: + return -EINVAL; + } +} + +static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_info ad4691_info = { + .read_raw = ad4691_read_raw, + .write_raw = ad4691_write_raw, + .read_avail = ad4691_read_avail, + .debugfs_reg_access = ad4691_reg_access, +}; + +static int ad4691_regulator_setup(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4691_supplies), + ad4691_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); + + /* + * vdd-supply and ldo-in-supply are mutually exclusive: + * vdd-supply present → external 1.8V VDD; disable internal LDO. + * vdd-supply absent → enable internal LDO fed from ldo-in-supply. + * Having both simultaneously is strongly inadvisable per the datasheet. + */ + if (device_property_present(dev, "vdd-supply")) { + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable VDD\n"); + } else if (device_property_present(dev, "ldo-in-supply")) { + ret = devm_regulator_get_enable(dev, "ldo-in"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable LDO-IN\n"); + st->ldo_en = true; + } else { + return dev_err_probe(dev, -EINVAL, + "missing one of vdd-supply, ldo-in-supply\n"); + } + + if (device_property_present(dev, "ref-supply")) { + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (st->vref_uV < 0) + return dev_err_probe(dev, st->vref_uV, + "Failed to get REF supply voltage\n"); + } else if (device_property_present(dev, "refin-supply")) { + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (st->vref_uV < 0) + return dev_err_probe(dev, st->vref_uV, + "Failed to get REFIN supply voltage\n"); + st->refbuf_en = true; + } else { + return dev_err_probe(dev, -EINVAL, + "missing one of ref-supply, refin-supply\n"); + } + + if (st->vref_uV < AD4691_VREF_uV_MIN || st->vref_uV > AD4691_VREF_uV_MAX) + return dev_err_probe(dev, -EINVAL, + "vref(%d) must be in the range [%u...%u]\n", + st->vref_uV, AD4691_VREF_uV_MIN, + AD4691_VREF_uV_MAX); + + return 0; +} + +static int ad4691_reset(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct reset_control *rst; + int ret; + + rst = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n"); + + if (rst) { + /* + * Assert the reset line to guarantee a clean reset pulse on + * every probe, including driver reloads where the line may + * already be deasserted (reset_control_put() does not + * re-assert on release). tRESETL (minimum pulse width) = 10 ns + * (Table 5); kernel function-call overhead alone exceeds this, + * so no explicit delay is needed between assert and deassert. + */ + reset_control_assert(rst); + ret = reset_control_deassert(rst); + if (ret) + return ret; + } else { + /* No hardware reset available, fall back to software reset. */ + ret = regmap_write(st->regmap, AD4691_SPI_CONFIG_A_REG, + AD4691_SW_RESET); + if (ret) + return ret; + } + + /* + * Wait 300 µs (Table 5) for the device to complete its internal reset + * sequence before accepting SPI commands. + */ + fsleep(300); + return 0; +} + +static int ad4691_config(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + enum ad4691_ref_ctrl ref_val; + unsigned int val; + int ret; + + switch (st->vref_uV) { + case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX: + ref_val = AD4691_VREF_2P5; + break; + case AD4691_VREF_2P5_uV_MAX + 1 ... AD4691_VREF_3P0_uV_MAX: + ref_val = AD4691_VREF_3P0; + break; + case AD4691_VREF_3P0_uV_MAX + 1 ... AD4691_VREF_3P3_uV_MAX: + ref_val = AD4691_VREF_3P3; + break; + case AD4691_VREF_3P3_uV_MAX + 1 ... AD4691_VREF_4P096_uV_MAX: + ref_val = AD4691_VREF_4P096; + break; + case AD4691_VREF_4P096_uV_MAX + 1 ... AD4691_VREF_uV_MAX: + ref_val = AD4691_VREF_5P0; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Unsupported vref voltage: %d uV\n", + st->vref_uV); + } + + val = FIELD_PREP(AD4691_REF_CTRL_MASK, ref_val); + if (st->refbuf_en) + val |= AD4691_REFBUF_EN; + + ret = regmap_write(st->regmap, AD4691_REF_CTRL, val); + if (ret) + return dev_err_probe(dev, ret, "Failed to write REF_CTRL\n"); + + ret = regmap_assign_bits(st->regmap, AD4691_DEVICE_SETUP, + AD4691_LDO_EN, st->ldo_en); + if (ret) + return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n"); + + /* + * Set the internal oscillator to the highest rate this chip supports. + * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those + * chips start at index 1 (500 kHz). + */ + ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG, + ad4691_samp_freq_start(st->info)); + if (ret) + return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n"); + + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE); + if (ret) + return dev_err_probe(dev, ret, "Failed to write ADC_SETUP\n"); + + return 0; +} + +static int ad4691_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4691_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->regmap = devm_regmap_init(dev, NULL, spi, &ad4691_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + ret = ad4691_regulator_setup(st); + if (ret) + return ret; + + ret = ad4691_reset(st); + if (ret) + return ret; + + ret = ad4691_config(st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->info = &ad4691_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = st->info->sw_info->channels; + indio_dev->num_channels = st->info->sw_info->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4691_of_match[] = { + { .compatible = "adi,ad4691", .data = &ad4691_chip_info }, + { .compatible = "adi,ad4692", .data = &ad4692_chip_info }, + { .compatible = "adi,ad4693", .data = &ad4693_chip_info }, + { .compatible = "adi,ad4694", .data = &ad4694_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4691_of_match); + +static const struct spi_device_id ad4691_id[] = { + { .name = "ad4691", .driver_data = (kernel_ulong_t)&ad4691_chip_info }, + { .name = "ad4692", .driver_data = (kernel_ulong_t)&ad4692_chip_info }, + { .name = "ad4693", .driver_data = (kernel_ulong_t)&ad4693_chip_info }, + { .name = "ad4694", .driver_data = (kernel_ulong_t)&ad4694_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4691_id); + +static struct spi_driver ad4691_driver = { + .driver = { + .name = "ad4691", + .of_match_table = ad4691_of_match, + }, + .probe = ad4691_probe, + .id_table = ad4691_id, +}; +module_spi_driver(ad4691_driver); + +MODULE_AUTHOR("Radu Sabau "); +MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver"); +MODULE_LICENSE("GPL"); From 41297c6bd8dd04c0b23bd7b14e00632cc806688b Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:02 +0300 Subject: [PATCH 2417/5214] iio: adc: ad4691: add triggered buffer support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add buffered capture support using the IIO triggered buffer framework. CNV Burst Mode: the GP pin identified by interrupt-names in the device tree is configured as DATA_READY output. The IRQ handler stops conversions and fires the IIO trigger; the trigger handler executes a pre-built SPI message that reads all active channels from the AVG_IN accumulator registers and then resets accumulator state and restarts conversions for the next cycle. Manual Mode: CNV is tied to SPI CS so each transfer simultaneously reads the previous result and starts the next conversion (pipelined N+1 scheme). At preenable time a pre-built, optimised SPI message of N+1 transfers is constructed (N channel reads plus one NOOP to drain the pipeline). The trigger handler executes the message in a single spi_sync() call and collects the results. An external trigger (e.g. iio-trig-hrtimer) is required to drive the trigger at the desired sample rate. Both modes share the same trigger handler and push a complete scan — one big-endian 16-bit (__be16) slot per active channel, densely packed in scan_index order, followed by a timestamp. The CNV Burst Mode sampling frequency (PWM period) is exposed as a buffer-level attribute via IIO_DEVICE_ATTR. Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ad4691.c | 574 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 572 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d07e4d839f25..a0b7a4eaa36e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -148,6 +148,8 @@ config AD4691 tristate "Analog Devices AD4691 Family ADC Driver" depends on SPI depends on REGULATOR || COMPILE_TEST + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select REGMAP help Say yes here to build support for Analog Devices AD4691 Family MuxSAR diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index e1febf80f21d..0b737cf6d795 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -11,19 +11,29 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include +#include #include #include #include #include +#include #include +#include +#include +#include +#include #define AD4691_VREF_uV_MIN 2400000 #define AD4691_VREF_uV_MAX 5250000 @@ -57,6 +67,7 @@ #define AD4691_NOOP 0x00 #define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3) +#define AD4691_EXIT_COMMAND 0x5000 #define AD4691_OSC_EN_REG 0x180 #define AD4691_STATE_RESET_REG 0x181 @@ -120,8 +131,12 @@ struct ad4691_chip_info { .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .channel = ch, \ + .scan_index = ch, \ .scan_type = { \ + .format = 'u', \ .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ }, \ } @@ -142,6 +157,7 @@ static const struct iio_chan_spec ad4691_channels[] = { AD4691_CHANNEL(13), AD4691_CHANNEL(14), AD4691_CHANNEL(15), + IIO_CHAN_SOFT_TIMESTAMP(16), }; static const struct iio_chan_spec ad4693_channels[] = { @@ -153,6 +169,7 @@ static const struct iio_chan_spec ad4693_channels[] = { AD4691_CHANNEL(5), AD4691_CHANNEL(6), AD4691_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), }; static const struct ad4691_channel_info ad4691_sw_info = { @@ -189,6 +206,8 @@ static const int ad4691_osc_freqs_Hz[] = { [0xF] = 1250, }; +static const char * const ad4691_gp_names[] = { "gp0", "gp1", "gp2", "gp3" }; + static const struct ad4691_chip_info ad4691_chip_info = { .name = "ad4691", .max_rate = 500 * HZ_PER_KHZ, @@ -218,8 +237,13 @@ struct ad4691_state { struct regmap *regmap; struct spi_device *spi; + struct pwm_device *conv_trigger; + int irq; int vref_uV; + u32 cnv_period_ns; + bool manual_mode; + bool irq_enabled; bool refbuf_en; bool ldo_en; /* @@ -227,8 +251,56 @@ struct ad4691_state { * atomicity of consecutive SPI operations. */ struct mutex lock; + /* + * Per-buffer-enable lifetime resources: + * Manual Mode - a pre-built SPI message that clocks out N+1 + * transfers in one go. + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N + * transfers in one go. + */ + struct spi_message scan_msg; + /* + * max 16 + 1 NOOP (manual) or 2*16 + 1 state-reset (CNV burst). + */ + struct spi_transfer scan_xfers[34]; + /* + * CNV burst: 16 AVG_IN addresses = 16. Manual: 16 channel cmds + + * 1 NOOP = 17. Stored as native u16; put_unaligned_be16() fills each + * slot so the SPI controller (bits_per_word=8) sends bytes MSB-first. + */ + u16 scan_tx[17] __aligned(IIO_DMA_MINALIGN); + /* + * CNV burst state-reset: 4-byte write [addr_hi, addr_lo, + * STATE_RESET_ALL, OSC_EN=1]. CS is asserted throughout, so + * ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as a + * deliberate side-write, keeping the oscillator enabled. Shared + * with the offload path (mutually exclusive at probe). + */ + u8 scan_tx_reset[4] __aligned(IIO_DMA_MINALIGN); + /* + * Scan buffer: one BE16 slot per active channel, plus timestamp. + * DMA-aligned because scan_xfers point rx_buf directly into vals[]. + */ + IIO_DECLARE_DMA_BUFFER_WITH_TS(__be16, vals, 16); }; +/* + * Configure the given GP pin (0-3) as DATA_READY output. + * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG. + * Even pins occupy bits [3:0], odd pins bits [7:4]. + */ +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num) +{ + unsigned int bit_off = gp_num % 2; + unsigned int reg_off = gp_num / 2; + unsigned int shift = 4 * bit_off; + + return regmap_update_bits(st->regmap, + AD4691_GPIO_MODE1_REG + reg_off, + AD4691_GP_MODE_MASK << shift, + AD4691_GP_MODE_DATA_READY << shift); +} + static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val) { struct spi_device *spi = context; @@ -539,13 +611,411 @@ static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg, return regmap_write(st->regmap, reg, writeval); } -static const struct iio_info ad4691_info = { +static int ad4691_set_pwm_freq(struct ad4691_state *st, unsigned int freq) +{ + if (!freq) + return -EINVAL; + + st->cnv_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, freq); + return 0; +} + +static int ad4691_sampling_enable(struct ad4691_state *st, bool enable) +{ + struct pwm_state conv_state = { + .period = st->cnv_period_ns, + .duty_cycle = AD4691_CNV_DUTY_CYCLE_NS, + .polarity = PWM_POLARITY_NORMAL, + .enabled = enable, + }; + + return pwm_apply_might_sleep(st->conv_trigger, &conv_state); +} + +/* + * ad4691_enter_conversion_mode - Switch the chip to its buffer conversion mode. + * + * Configures the ADC hardware registers for the mode selected at probe + * (CNV_BURST or MANUAL). Called from buffer preenable before starting + * sampling. The chip is in AUTONOMOUS mode during idle (for read_raw). + */ +static int ad4691_enter_conversion_mode(struct ad4691_state *st) +{ + int ret; + + if (st->manual_mode) + return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP, + AD4691_MANUAL_MODE, AD4691_MANUAL_MODE); + + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4691_STATE_RESET_REG, + AD4691_STATE_RESET_ALL); +} + +static int ad4691_transfer(struct ad4691_state *st, u16 cmd) +{ + u8 buf[2]; + + put_unaligned_be16(cmd, buf); + + return spi_write_then_read(st->spi, buf, sizeof(buf), NULL, 0); +} + +/* + * ad4691_exit_conversion_mode - Return the chip to AUTONOMOUS mode. + * + * Called from buffer postdisable to restore the chip to the + * idle state used by read_raw. Clears the sequencer and resets state. + */ +static int ad4691_exit_conversion_mode(struct ad4691_state *st) +{ + if (st->manual_mode) + return ad4691_transfer(st, AD4691_EXIT_COMMAND); + + return regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE); +} + +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int k, i; + int ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + spi_message_init(&st->scan_msg); + + k = 0; + iio_for_each_active_channel(indio_dev, i) { + /* + * Channel-select command occupies the first (high) byte of the + * 16-bit DIN frame; the second byte is a don't-care zero pad. + * put_unaligned_be16() writes [cmd, 0x00] in memory so the + * SPI controller sends the command byte first on the wire. + */ + put_unaligned_be16((u16)(AD4691_ADC_CHAN(i) << 8), &st->scan_tx[k]); + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; + /* + * The pipeline means xfer[0] receives the residual from the + * previous sequence, not a valid sample. Discard it (rx_buf=NULL) + * to avoid aliasing vals[0] across two concurrent DMA mappings. + * xfer[1] (or the NOOP when only one channel is active) writes + * the real ch[0] result to vals[0]. Subsequent transfers write + * into vals[k-1] so each result lands at the next dense slot. + */ + st->scan_xfers[k].rx_buf = (k == 0) ? NULL : &st->vals[k - 1]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].cs_change = 1; + st->scan_xfers[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS; + st->scan_xfers[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg); + k++; + } + + /* Final NOOP transfer retrieves the last channel's result. */ + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; /* scan_tx[k] == 0 == NOOP */ + st->scan_xfers[k].rx_buf = &st->vals[k - 1]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg); + + ret = spi_optimize_message(st->spi, &st->scan_msg); + if (ret) + return ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) { + spi_unoptimize_message(&st->scan_msg); + return ret; + } + + return 0; +} + +static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4691_exit_conversion_mode(st); + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = { + .preenable = ad4691_manual_buffer_preenable, + .postdisable = ad4691_manual_buffer_postdisable, +}; + +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int acc_mask, std_seq_config; + unsigned int k, i; + int ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + spi_message_init(&st->scan_msg); + + /* + * Each AVG_IN read needs two transfers: a 2-byte address write phase + * followed by a 2-byte data read phase. CS toggles between channels + * (cs_change=1 on the read phase of all but the last channel). + */ + k = 0; + iio_for_each_active_channel(indio_dev, i) { + put_unaligned_be16(0x8000 | AD4691_AVG_IN(i), &st->scan_tx[k]); + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[2 * k].len = sizeof(*st->scan_tx); + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg); + st->scan_xfers[2 * k + 1].rx_buf = &st->vals[k]; + st->scan_xfers[2 * k + 1].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k + 1].cs_change = 1; + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg); + k++; + } + + /* + * Append a 4-byte state-reset transfer [addr_hi, addr_lo, + * STATE_RESET_ALL, OSC_EN=1]. CS is asserted throughout, so + * ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as a + * deliberate side-write, keeping the oscillator enabled. + * STATE_RESET_ALL starts the next burst; the hardware does not + * accumulate new conversions until after a STATE_RESET pulse, so + * no in-progress data is lost. No cs_change here — CS must + * deassert normally at end of message to frame the next command. + */ + put_unaligned_be16(AD4691_STATE_RESET_REG, st->scan_tx_reset); + st->scan_tx_reset[2] = AD4691_STATE_RESET_ALL; + st->scan_tx_reset[3] = 1; + st->scan_xfers[2 * k].tx_buf = st->scan_tx_reset; + st->scan_xfers[2 * k].len = sizeof(st->scan_tx_reset); + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg); + + ret = spi_optimize_message(st->spi, &st->scan_msg); + if (ret) + return ret; + + std_seq_config = bitmap_read(indio_dev->active_scan_mask, 0, + iio_get_masklength(indio_dev)) & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, std_seq_config); + if (ret) + goto err_unoptimize; + + acc_mask = ~std_seq_config & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask); + if (ret) + goto err_unoptimize; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + goto err_unoptimize; + + return 0; + +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static int ad4691_cnv_burst_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + /* + * Start the PWM and unmask the IRQ here in postenable, not in + * preenable. The IIO core attaches the trigger poll function between + * preenable and postenable; enabling sampling or unmasking the IRQ + * before that point risks a DATA_READY assertion landing before the + * poll function is registered. iio_trigger_poll() would drop the + * event, disable_irq_nosync() would fire, and enable_irq() would + * never be called, leaving the IRQ permanently masked. + */ + ret = ad4691_sampling_enable(st, true); + if (ret) + return ret; + + enable_irq(st->irq); + st->irq_enabled = true; + return 0; +} + +static int ad4691_cnv_burst_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + if (st->irq_enabled) { + disable_irq(st->irq); + st->irq_enabled = false; + } + return ad4691_sampling_enable(st, false); +} + +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4691_exit_conversion_mode(st); + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = { + .preenable = ad4691_cnv_burst_buffer_preenable, + .postenable = ad4691_cnv_burst_buffer_postenable, + .predisable = ad4691_cnv_burst_buffer_predisable, + .postdisable = ad4691_cnv_burst_buffer_postdisable, +}; + +static ssize_t sampling_frequency_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4691_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns); +} + +static ssize_t sampling_frequency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ad4691_set_pwm_freq(st, freq); + if (ret) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0); + +static const struct iio_dev_attr *ad4691_buffer_attrs[] = { + &iio_dev_attr_sampling_frequency, + NULL +}; + +static irqreturn_t ad4691_irq(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad4691_state *st = iio_priv(indio_dev); + + /* + * Disable the IRQ before calling iio_trigger_poll(). The IRQ is + * re-enabled via the trigger .reenable callback, which the IIO core + * calls inside iio_trigger_notify_done() once use_count reaches zero. + * Re-enabling here (before notify_done) would race: a DATA_READY + * between enable_irq() and notify_done() calls iio_trigger_poll() + * while use_count > 0, dropping the event and permanently masking + * the IRQ. + */ + disable_irq_nosync(st->irq); + iio_trigger_poll(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad4691_trigger_reenable(struct iio_trigger *trig) +{ + struct ad4691_state *st = iio_trigger_get_drvdata(trig); + + enable_irq(st->irq); +} + +static const struct iio_trigger_ops ad4691_trigger_ops = { + .reenable = ad4691_trigger_reenable, + .validate_device = iio_trigger_validate_own_device, +}; + +static void ad4691_read_scan(struct iio_dev *indio_dev, s64 ts) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + ret = spi_sync(st->spi, &st->scan_msg); + if (ret) { + dev_err_ratelimited(regmap_get_device(st->regmap), + "SPI scan failed: %d\n", ret); + return; + } + + /* + * rx_buf pointers in scan_xfers point directly into scan.vals, so no + * copy is needed. The scan_msg already includes a STATE_RESET at the + * end (appended in preenable), so no explicit reset is needed here. + */ + iio_push_to_buffers_with_ts(indio_dev, st->vals, sizeof(st->vals), ts); +} + +static irqreturn_t ad4691_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ad4691_read_scan(indio_dev, pf->timestamp); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +/* + * CNV burst mode: only allow our own trigger (driven by DATA_READY IRQ). + * Manual mode: external triggers (e.g. iio-trig-hrtimer) must be allowed + * because manual mode has no DATA_READY IRQ to fire the internal trigger. + * iio_trigger_ops.validate_device = iio_trigger_validate_own_device is + * correct in both modes — it prevents other devices from hijacking our + * internal trigger; the distinction here is only for iio_info.validate_trigger. + */ +static const struct iio_info ad4691_cnv_burst_info = { + .read_raw = ad4691_read_raw, + .write_raw = ad4691_write_raw, + .read_avail = ad4691_read_avail, + .debugfs_reg_access = ad4691_reg_access, + .validate_trigger = iio_validate_own_trigger, +}; + +static const struct iio_info ad4691_manual_info = { .read_raw = ad4691_read_raw, .write_raw = ad4691_write_raw, .read_avail = ad4691_read_avail, .debugfs_reg_access = ad4691_reg_access, }; +static int ad4691_pwm_setup(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + + st->conv_trigger = devm_pwm_get(dev, "cnv"); + if (IS_ERR(st->conv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->conv_trigger), + "Failed to get CNV PWM\n"); + + return ad4691_set_pwm_freq(st, st->info->max_rate); +} + static int ad4691_regulator_setup(struct ad4691_state *st) { struct device *dev = regmap_get_device(st->regmap); @@ -649,6 +1119,22 @@ static int ad4691_config(struct ad4691_state *st) unsigned int val; int ret; + /* + * Determine buffer conversion mode from DT: if a PWM is provided it + * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS + * and each SPI transfer triggers a conversion (MANUAL_MODE). + * Both modes idle in AUTONOMOUS mode so that read_raw can use the + * internal oscillator without disturbing the hardware configuration. + */ + if (device_property_present(dev, "pwms")) { + st->manual_mode = false; + ret = ad4691_pwm_setup(st); + if (ret) + return ret; + } else { + st->manual_mode = true; + } + switch (st->vref_uV) { case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX: ref_val = AD4691_VREF_2P5; @@ -702,6 +1188,86 @@ static int ad4691_config(struct ad4691_state *st) return 0; } +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, + struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct iio_trigger *trig; + unsigned int i; + int irq, ret; + + indio_dev->channels = st->info->sw_info->channels; + indio_dev->num_channels = st->info->sw_info->num_channels; + indio_dev->info = st->manual_mode ? &ad4691_manual_info : &ad4691_cnv_burst_info; + + /* + * Manual mode relies on an external trigger (e.g. iio-trig-hrtimer); + * no internal trigger is needed or registered. + */ + if (st->manual_mode) + return devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4691_trigger_handler, + &ad4691_manual_buffer_setup_ops); + + /* + * CNV burst mode: allocate an internal trigger driven by the + * DATA_READY IRQ on the GP pin. + */ + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trig) + return -ENOMEM; + + trig->ops = &ad4691_trigger_ops; + iio_trigger_set_drvdata(trig, st); + + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return dev_err_probe(dev, ret, "IIO trigger register failed\n"); + + indio_dev->trig = iio_trigger_get(trig); + + /* + * The GP pin named in interrupt-names asserts at end-of-conversion. + * The IRQ handler fires the IIO trigger so the trigger handler can + * read and push the sample to the buffer. The IRQ is kept disabled + * until the buffer is enabled. + */ + irq = -ENXIO; + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) { + irq = fwnode_irq_get_byname(dev_fwnode(dev), + ad4691_gp_names[i]); + if (irq > 0 || irq == -EPROBE_DEFER) + break; + } + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get GP interrupt\n"); + + st->irq = irq; + + ret = ad4691_gpio_setup(st, i); + if (ret) + return ret; + + /* + * The handler only calls disable_irq_nosync() and iio_trigger_poll(), + * both safe in hardirq context, so register as a hard IRQ handler. + * IRQF_NO_AUTOEN keeps it disabled until the buffer is enabled. + */ + ret = devm_request_irq(dev, irq, ad4691_irq, IRQF_NO_AUTOEN, + indio_dev->name, indio_dev); + if (ret) + return ret; + + return devm_iio_triggered_buffer_setup_ext(dev, indio_dev, + iio_pollfunc_store_time, + ad4691_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &ad4691_cnv_burst_buffer_setup_ops, + ad4691_buffer_attrs); +} + static int ad4691_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -741,11 +1307,11 @@ static int ad4691_probe(struct spi_device *spi) return ret; indio_dev->name = st->info->name; - indio_dev->info = &ad4691_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->info->sw_info->channels; - indio_dev->num_channels = st->info->sw_info->num_channels; + ret = ad4691_setup_triggered_buffer(indio_dev, st); + if (ret) + return ret; return devm_iio_device_register(dev, indio_dev); } From ad4f8513aa4ab949bb2986d64720a0b56f882cbc Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:03 +0300 Subject: [PATCH 2418/5214] iio: adc: ad4691: add SPI offload support Add SPI offload support to enable DMA-based, CPU-independent data acquisition using the SPI Engine offload framework. When an SPI offload is available (devm_spi_offload_get() succeeds), the driver registers a DMA engine IIO buffer and uses dedicated buffer setup operations. If no offload is available the existing software triggered buffer path is used unchanged. Both CNV Burst Mode and Manual Mode support offload, but use different trigger mechanisms: CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY signal on the GP pin specified by the trigger-source consumer reference in the device tree (one cell = GP pin number 0-3). For this mode the driver acts as both an SPI offload consumer (DMA RX stream, message optimization) and a trigger source provider: it registers the GP/DATA_READY output via devm_spi_offload_trigger_register() so the offload framework can match the '#trigger-source-cells' phandle and automatically fire the SPI Engine DMA transfer at end-of-conversion. Manual Mode: the SPI Engine is triggered by a periodic trigger at the configured sampling frequency. The pre-built SPI message uses the pipelined CNV-on-CS protocol: N+1 16-bit transfers are issued for N active channels (the first result is discarded as garbage from the pipeline flush) and the remaining N results are captured by DMA. All offload transfers use 16-bit frames (bits_per_word=16, len=2). The SPI Engine assembles received bits into native 16-bit words before DMA, so offload samples land in CPU-native byte order (IIO_CPU). Dedicated channel arrays (AD4691_OFFLOAD_CHANNEL) reflect this: they omit IIO_BE and carry no soft timestamp (DMA delivers data directly to userspace). The software triggered-buffer path retains its IIO_BE channels because bits_per_word=8 causes SPI to deliver bytes MSB-first into memory, making the on-disk layout big-endian. Both paths use storagebits=16 as transfers are 16 bits wide in both cases. IIO_BUFFER_DMAENGINE is selected because the offload path uses devm_iio_dmaengine_buffer_setup_with_handle() to allocate and attach the DMA RX buffer to the IIO device. Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ad4691.c | 444 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 443 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a0b7a4eaa36e..a3a93a47b43d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -149,8 +149,10 @@ config AD4691 depends on SPI depends on REGULATOR || COMPILE_TEST select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER select REGMAP + select SPI_OFFLOAD help Say yes here to build support for Analog Devices AD4691 Family MuxSAR SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index 0b737cf6d795..aa4263fa17bf 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -24,11 +25,15 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -44,6 +49,11 @@ #define AD4691_CNV_DUTY_CYCLE_NS 380 #define AD4691_CNV_HIGH_TIME_NS 430 +/* + * Conservative default for the manual offload periodic trigger. Low enough + * to work safely out of the box across all OSR and channel count combinations. + */ +#define AD4691_OFFLOAD_INITIAL_TRIGGER_HZ (100 * HZ_PER_KHZ) #define AD4691_SPI_CONFIG_A_REG 0x000 #define AD4691_SW_RESET (BIT(7) | BIT(0)) @@ -119,6 +129,7 @@ struct ad4691_chip_info { const char *name; unsigned int max_rate; const struct ad4691_channel_info *sw_info; + const struct ad4691_channel_info *offload_info; }; #define AD4691_CHANNEL(ch) \ @@ -140,6 +151,30 @@ struct ad4691_chip_info { }, \ } +/* + * Offload path (bits_per_word=16): the SPI Engine assembles received + * bits into native 16-bit words before DMA, so samples are in + * CPU-native byte order (IIO_CPU). storagebits=16 matches the 16-bit + * DMA word size. + */ +#define AD4691_OFFLOAD_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + } + static const struct iio_chan_spec ad4691_channels[] = { AD4691_CHANNEL(0), AD4691_CHANNEL(1), @@ -172,6 +207,40 @@ static const struct iio_chan_spec ad4693_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), }; +/* + * Offload channel arrays: no IIO_CHAN_SOFT_TIMESTAMP because DMA delivers + * data directly to userspace without a software timestamp. + */ +static const struct iio_chan_spec ad4691_offload_channels[] = { + AD4691_OFFLOAD_CHANNEL(0), + AD4691_OFFLOAD_CHANNEL(1), + AD4691_OFFLOAD_CHANNEL(2), + AD4691_OFFLOAD_CHANNEL(3), + AD4691_OFFLOAD_CHANNEL(4), + AD4691_OFFLOAD_CHANNEL(5), + AD4691_OFFLOAD_CHANNEL(6), + AD4691_OFFLOAD_CHANNEL(7), + AD4691_OFFLOAD_CHANNEL(8), + AD4691_OFFLOAD_CHANNEL(9), + AD4691_OFFLOAD_CHANNEL(10), + AD4691_OFFLOAD_CHANNEL(11), + AD4691_OFFLOAD_CHANNEL(12), + AD4691_OFFLOAD_CHANNEL(13), + AD4691_OFFLOAD_CHANNEL(14), + AD4691_OFFLOAD_CHANNEL(15), +}; + +static const struct iio_chan_spec ad4693_offload_channels[] = { + AD4691_OFFLOAD_CHANNEL(0), + AD4691_OFFLOAD_CHANNEL(1), + AD4691_OFFLOAD_CHANNEL(2), + AD4691_OFFLOAD_CHANNEL(3), + AD4691_OFFLOAD_CHANNEL(4), + AD4691_OFFLOAD_CHANNEL(5), + AD4691_OFFLOAD_CHANNEL(6), + AD4691_OFFLOAD_CHANNEL(7), +}; + static const struct ad4691_channel_info ad4691_sw_info = { .channels = ad4691_channels, .num_channels = ARRAY_SIZE(ad4691_channels), @@ -182,6 +251,16 @@ static const struct ad4691_channel_info ad4693_sw_info = { .num_channels = ARRAY_SIZE(ad4693_channels), }; +static const struct ad4691_channel_info ad4691_offload_info = { + .channels = ad4691_offload_channels, + .num_channels = ARRAY_SIZE(ad4691_offload_channels), +}; + +static const struct ad4691_channel_info ad4693_offload_info = { + .channels = ad4693_offload_channels, + .num_channels = ARRAY_SIZE(ad4693_offload_channels), +}; + /* * Internal oscillator frequency table. Index is the OSC_FREQ_REG[3:0] value. * Index 0 (1 MHz) is only valid for AD4692/AD4694; AD4691/AD4693 support @@ -212,24 +291,28 @@ static const struct ad4691_chip_info ad4691_chip_info = { .name = "ad4691", .max_rate = 500 * HZ_PER_KHZ, .sw_info = &ad4691_sw_info, + .offload_info = &ad4691_offload_info, }; static const struct ad4691_chip_info ad4692_chip_info = { .name = "ad4692", .max_rate = 1 * HZ_PER_MHZ, .sw_info = &ad4691_sw_info, + .offload_info = &ad4691_offload_info, }; static const struct ad4691_chip_info ad4693_chip_info = { .name = "ad4693", .max_rate = 500 * HZ_PER_KHZ, .sw_info = &ad4693_sw_info, + .offload_info = &ad4693_offload_info, }; static const struct ad4691_chip_info ad4694_chip_info = { .name = "ad4694", .max_rate = 1 * HZ_PER_MHZ, .sw_info = &ad4693_sw_info, + .offload_info = &ad4693_offload_info, }; struct ad4691_state { @@ -251,6 +334,10 @@ struct ad4691_state { * atomicity of consecutive SPI operations. */ struct mutex lock; + /* NULL when no SPI offload hardware is present. */ + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + u64 trigger_hz; /* * Per-buffer-enable lifetime resources: * Manual Mode - a pre-built SPI message that clocks out N+1 @@ -265,8 +352,11 @@ struct ad4691_state { struct spi_transfer scan_xfers[34]; /* * CNV burst: 16 AVG_IN addresses = 16. Manual: 16 channel cmds + - * 1 NOOP = 17. Stored as native u16; put_unaligned_be16() fills each - * slot so the SPI controller (bits_per_word=8) sends bytes MSB-first. + * 1 NOOP = 17. Stored as native u16. The non-offload path fills slots + * with put_unaligned_be16() (bits_per_word=8, bytes go out in memory + * order). The offload path assigns native values directly + * (bits_per_word=bpw, SPI reads each slot as a native 16-bit word and + * shifts it out MSB-first). */ u16 scan_tx[17] __aligned(IIO_DMA_MINALIGN); /* @@ -301,6 +391,45 @@ static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num) AD4691_GP_MODE_DATA_READY << shift); } +static const struct spi_offload_config ad4691_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 1 && args[0] <= 3; +} + +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + struct ad4691_state *st = spi_offload_trigger_get_priv(trigger); + + if (nargs != 1 || args[0] > 3) + return -EINVAL; + + return ad4691_gpio_setup(st, args[0]); +} + +static int ad4691_offload_trigger_validate(struct spi_offload_trigger *trigger, + struct spi_offload_trigger_config *config) +{ + if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad4691_offload_trigger_ops = { + .match = ad4691_offload_trigger_match, + .request = ad4691_offload_trigger_request, + .validate = ad4691_offload_trigger_validate, +}; + static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val) { struct spi_device *spi = context; @@ -876,6 +1005,218 @@ static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = { .postdisable = ad4691_cnv_burst_buffer_postdisable, }; +static int ad4691_manual_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + struct spi_device *spi = to_spi_device(dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + }; + unsigned int bpw = indio_dev->channels[0].scan_type.realbits; + unsigned int bit, k; + int ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + return ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + /* + * N+1 transfers for N channels. Each CS-low period triggers + * a conversion AND returns the previous result (pipelined). + * TX: [AD4691_ADC_CHAN(n), 0x00] + * RX: [data_hi, data_lo] (storagebits=16, shift=0) + * Transfer 0 RX is garbage; transfers 1..N carry real data. + * scan_tx is reused for TX commands (mutually exclusive with the + * non-offload triggered-buffer path). + * + * bits_per_word=bpw: the SPI controller reads tx_buf as a native + * 16-bit word and shifts it out MSB-first. Store the exact 16-bit + * value we want on the wire as a plain native u16 — no endianness + * macro — so the wire bytes are correct on both LE and BE hosts. + * The channel-select command is a single byte; shift it to the MSB + * position so SPI sends it first, with a zero pad in the LSB. + */ + k = 0; + iio_for_each_active_channel(indio_dev, bit) { + st->scan_tx[k] = AD4691_ADC_CHAN(bit) << 8; + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].bits_per_word = bpw; + st->scan_xfers[k].cs_change = 1; + st->scan_xfers[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS; + st->scan_xfers[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + /* First transfer RX is garbage — skip it. */ + if (k > 0) + st->scan_xfers[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + k++; + } + + /* Final NOOP transfer retrieves the last channel's result. */ + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; /* scan_tx[k] == 0 == NOOP */ + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].bits_per_word = bpw; + st->scan_xfers[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + k++; + + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, k); + st->scan_msg.offload = st->offload; + + ret = spi_optimize_message(spi, &st->scan_msg); + if (ret) + goto err_exit_conversion; + + config.periodic.frequency_hz = st->trigger_hz; + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + goto err_unoptimize; + + return 0; + +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); +err_exit_conversion: + ad4691_exit_conversion_mode(st); + return ret; +} + +static int ad4691_manual_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->scan_msg); + + return ad4691_exit_conversion_mode(st); +} + +static const struct iio_buffer_setup_ops ad4691_manual_offload_buffer_setup_ops = { + .postenable = ad4691_manual_offload_buffer_postenable, + .predisable = ad4691_manual_offload_buffer_predisable, +}; + +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + struct spi_device *spi = to_spi_device(dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + unsigned int bpw = indio_dev->channels[0].scan_type.realbits; + unsigned int acc_mask, std_seq_config; + unsigned int bit, k; + int ret; + + std_seq_config = bitmap_read(indio_dev->active_scan_mask, 0, + iio_get_masklength(indio_dev)) & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, std_seq_config); + if (ret) + return ret; + + acc_mask = ~std_seq_config & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask); + if (ret) + return ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + return ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + /* + * Each AVG_IN register read uses two transfers: + * TX: [reg_hi | 0x80, reg_lo] (address phase, CS stays asserted) + * RX: [data_hi, data_lo] (bpw-wide data phase, storagebits=16) + * Both TX and RX use bits_per_word=bpw: the SPI controller reads tx_buf + * as a native 16-bit word and shifts it out MSB-first. Store the exact + * 16-bit wire value as a plain native u16 — no endianness macro — so the + * wire bytes are correct on both LE and BE hosts. The read-address + * (0x8000 | reg) is already the 16-bit value we want on the wire. + * scan_tx is reused for TX addresses (mutually exclusive with the + * non-offload triggered-buffer path). + */ + k = 0; + iio_for_each_active_channel(indio_dev, bit) { + st->scan_tx[k] = 0x8000 | AD4691_AVG_IN(bit); + + /* TX: address phase, CS stays asserted into data phase */ + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[2 * k].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k].bits_per_word = bpw; + + /* RX: data phase, CS toggles after to delimit the next register op */ + st->scan_xfers[2 * k + 1].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k + 1].bits_per_word = bpw; + st->scan_xfers[2 * k + 1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + st->scan_xfers[2 * k + 1].cs_change = 1; + k++; + } + + /* + * State reset: single 4-byte write [addr_hi, addr_lo, STATE_RESET_ALL, + * OSC_EN=1]. ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as + * a deliberate side-write, keeping the oscillator enabled. + * scan_tx_reset is shared with the non-offload path (len=4 here vs + * len=3 there) since the two paths are mutually exclusive at probe. + */ + put_unaligned_be16(AD4691_STATE_RESET_REG, st->scan_tx_reset); + st->scan_tx_reset[2] = AD4691_STATE_RESET_ALL; + st->scan_tx_reset[3] = 1; + st->scan_xfers[2 * k].tx_buf = st->scan_tx_reset; + st->scan_xfers[2 * k].len = sizeof(st->scan_tx_reset); + /* + * 4-byte u8 buffer assembled with put_unaligned_be16(); leave + * bits_per_word at the default (8) so bytes go out in memory order. + */ + + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, 2 * k + 1); + st->scan_msg.offload = st->offload; + + ret = spi_optimize_message(spi, &st->scan_msg); + if (ret) + goto err_exit_conversion; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + goto err_unoptimize; + + ret = ad4691_sampling_enable(st, true); + if (ret) + goto err_disable_trigger; + + return 0; + +err_disable_trigger: + spi_offload_trigger_disable(st->offload, st->offload_trigger); +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); +err_exit_conversion: + ad4691_exit_conversion_mode(st); + return ret; +} + +static int ad4691_cnv_burst_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + ad4691_sampling_enable(st, false); + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->scan_msg); + + return ad4691_exit_conversion_mode(st); +} + +static const struct iio_buffer_setup_ops ad4691_cnv_burst_offload_buffer_setup_ops = { + .postenable = ad4691_cnv_burst_offload_buffer_postenable, + .predisable = ad4691_cnv_burst_offload_buffer_predisable, +}; + static ssize_t sampling_frequency_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -883,6 +1224,9 @@ static ssize_t sampling_frequency_show(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad4691_state *st = iio_priv(indio_dev); + if (st->manual_mode && st->offload) + return sysfs_emit(buf, "%llu\n", READ_ONCE(st->trigger_hz)); + return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns); } @@ -903,6 +1247,20 @@ static ssize_t sampling_frequency_store(struct device *dev, if (IIO_DEV_ACQUIRE_FAILED(claim)) return -EBUSY; + if (st->manual_mode && st->offload) { + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { .frequency_hz = freq }, + }; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + WRITE_ONCE(st->trigger_hz, config.periodic.frequency_hz); + return len; + } + ret = ad4691_set_pwm_freq(st, freq); if (ret) return ret; @@ -1268,9 +1626,77 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, ad4691_buffer_attrs); } +static int ad4691_setup_offload(struct iio_dev *indio_dev, + struct ad4691_state *st, + struct spi_offload *spi_offload) +{ + struct device *dev = regmap_get_device(st->regmap); + struct dma_chan *rx_dma; + int ret; + + st->offload = spi_offload; + + indio_dev->channels = st->info->offload_info->channels; + indio_dev->num_channels = st->info->offload_info->num_channels; + /* + * Offload path uses DMA directly; no IIO trigger is involved, so + * external triggers are not restricted (no validate_trigger). + */ + indio_dev->info = &ad4691_manual_info; + + if (st->manual_mode) { + st->offload_trigger = + devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "Failed to get periodic offload trigger\n"); + + st->trigger_hz = AD4691_OFFLOAD_INITIAL_TRIGGER_HZ; + } else { + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad4691_offload_trigger_ops, + .priv = st, + }; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register offload trigger\n"); + + st->offload_trigger = + devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "Failed to get DATA_READY offload trigger\n"); + } + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get offload RX DMA channel\n"); + + if (st->manual_mode) + indio_dev->setup_ops = &ad4691_manual_offload_buffer_setup_ops; + else + indio_dev->setup_ops = &ad4691_cnv_burst_offload_buffer_setup_ops; + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return ret; + + indio_dev->buffer->attrs = ad4691_buffer_attrs; + + return 0; +} + static int ad4691_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + struct spi_offload *spi_offload; struct iio_dev *indio_dev; struct ad4691_state *st; int ret; @@ -1306,10 +1732,20 @@ static int ad4691_probe(struct spi_device *spi) if (ret) return ret; + spi_offload = devm_spi_offload_get(dev, spi, &ad4691_offload_config); + ret = PTR_ERR_OR_ZERO(spi_offload); + if (ret == -ENODEV) + spi_offload = NULL; + else if (ret) + return dev_err_probe(dev, ret, "Failed to get SPI offload\n"); + indio_dev->name = st->info->name; indio_dev->modes = INDIO_DIRECT_MODE; - ret = ad4691_setup_triggered_buffer(indio_dev, st); + if (spi_offload) + ret = ad4691_setup_offload(indio_dev, st, spi_offload); + else + ret = ad4691_setup_triggered_buffer(indio_dev, st); if (ret) return ret; @@ -1347,3 +1783,5 @@ module_spi_driver(ad4691_driver); MODULE_AUTHOR("Radu Sabau "); MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMA_BUFFER"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); From 6d9e7161bfbcce8960e946280fc92e6f5dc70569 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:04 +0300 Subject: [PATCH 2419/5214] iio: adc: ad4691: add oversampling support Add oversampling ratio (OSR) support for CNV burst mode. The accumulator depth register (ACC_DEPTH_IN(0)) is programmed with the selected OSR at buffer enable time and before each single-shot read. Supported OSR values: 1, 2, 4, 8, 16, 32. Introduce AD4691_MANUAL_CHANNEL() for manual mode channels, which do not expose the oversampling_ratio attribute since OSR is not applicable in that mode. A separate manual_channels array is added to struct ad4691_channel_info and selected at probe time. The OSR is shared across all channels (in_voltage_sampling_frequency and in_voltage_oversampling_ratio are info_mask_shared_by_all) because the chip has one internal oscillator and a single accumulator depth register (ACC_DEPTH_IN(0)) for all channels. in_voltage_sampling_frequency represents the effective output rate, defined as osc_freq / osr. Writing it computes needed_osc = freq * osr and snaps down to the largest oscillator table entry that satisfies both osc <= needed_osc and osc % osr == 0, guaranteeing an exact integer read-back. The result is stored in target_osc_freq_Hz and written to OSC_FREQ_REG at buffer enable and single-shot time, so sampling_frequency and oversampling_ratio can be set in any order. in_voltage_sampling_frequency_available is precomputed at probe for each OSR value, listing only oscillator table entries that divide evenly by that OSR, expressed as effective rates (osc_freq / osr). The list becomes sparser as OSR increases, capping at max_rate / osr. read_avail picks the precomputed list for the current OSR, making the returned pointer stable and race-free. Writing oversampling_ratio stores the new shared OSR and snaps target_osc_freq_Hz to the largest oscillator table entry that is both <= old_effective_rate * new_osr and evenly divisible by new_osr. This preserves an integer read-back of in_voltage_sampling_frequency after the OSR change while keeping the oscillator as close as possible to the previous effective rate. OSR defaults to 1 (no accumulation). Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4691.c | 357 +++++++++++++++++++++++++++++++++++---- 1 file changed, 327 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index aa4263fa17bf..548678adc2a4 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -122,6 +122,7 @@ enum ad4691_ref_ctrl { struct ad4691_channel_info { const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + const struct iio_chan_spec *manual_channels __counted_by_ptr(num_channels); unsigned int num_channels; }; @@ -132,7 +133,34 @@ struct ad4691_chip_info { const struct ad4691_channel_info *offload_info; }; +/* CNV burst mode channel — exposes oversampling ratio. */ #define AD4691_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +/* + * Manual mode channel — no oversampling ratio attribute. OSR is not + * supported in manual mode; ACC_DEPTH_IN is not configured during manual + * buffer enable. + */ +#define AD4691_MANUAL_CHANNEL(ch) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -156,8 +184,33 @@ struct ad4691_chip_info { * bits into native 16-bit words before DMA, so samples are in * CPU-native byte order (IIO_CPU). storagebits=16 matches the 16-bit * DMA word size. + * + * CNV burst offload configures ACC_DEPTH_IN per channel, so the + * oversampling_ratio attribute is exposed. Manual offload does not; + * use AD4691_OFFLOAD_MANUAL_CHANNEL for that path. */ #define AD4691_OFFLOAD_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + } + +/* Manual offload — same IIO_CPU layout but no oversampling_ratio attribute. */ +#define AD4691_OFFLOAD_MANUAL_CHANNEL(ch) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -241,23 +294,91 @@ static const struct iio_chan_spec ad4693_offload_channels[] = { AD4691_OFFLOAD_CHANNEL(7), }; +static const struct iio_chan_spec ad4691_manual_channels[] = { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + AD4691_MANUAL_CHANNEL(8), + AD4691_MANUAL_CHANNEL(9), + AD4691_MANUAL_CHANNEL(10), + AD4691_MANUAL_CHANNEL(11), + AD4691_MANUAL_CHANNEL(12), + AD4691_MANUAL_CHANNEL(13), + AD4691_MANUAL_CHANNEL(14), + AD4691_MANUAL_CHANNEL(15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static const struct iio_chan_spec ad4693_manual_channels[] = { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad4691_offload_manual_channels[] = { + AD4691_OFFLOAD_MANUAL_CHANNEL(0), + AD4691_OFFLOAD_MANUAL_CHANNEL(1), + AD4691_OFFLOAD_MANUAL_CHANNEL(2), + AD4691_OFFLOAD_MANUAL_CHANNEL(3), + AD4691_OFFLOAD_MANUAL_CHANNEL(4), + AD4691_OFFLOAD_MANUAL_CHANNEL(5), + AD4691_OFFLOAD_MANUAL_CHANNEL(6), + AD4691_OFFLOAD_MANUAL_CHANNEL(7), + AD4691_OFFLOAD_MANUAL_CHANNEL(8), + AD4691_OFFLOAD_MANUAL_CHANNEL(9), + AD4691_OFFLOAD_MANUAL_CHANNEL(10), + AD4691_OFFLOAD_MANUAL_CHANNEL(11), + AD4691_OFFLOAD_MANUAL_CHANNEL(12), + AD4691_OFFLOAD_MANUAL_CHANNEL(13), + AD4691_OFFLOAD_MANUAL_CHANNEL(14), + AD4691_OFFLOAD_MANUAL_CHANNEL(15), +}; + +static const struct iio_chan_spec ad4693_offload_manual_channels[] = { + AD4691_OFFLOAD_MANUAL_CHANNEL(0), + AD4691_OFFLOAD_MANUAL_CHANNEL(1), + AD4691_OFFLOAD_MANUAL_CHANNEL(2), + AD4691_OFFLOAD_MANUAL_CHANNEL(3), + AD4691_OFFLOAD_MANUAL_CHANNEL(4), + AD4691_OFFLOAD_MANUAL_CHANNEL(5), + AD4691_OFFLOAD_MANUAL_CHANNEL(6), + AD4691_OFFLOAD_MANUAL_CHANNEL(7), +}; + +static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 }; + static const struct ad4691_channel_info ad4691_sw_info = { .channels = ad4691_channels, + .manual_channels = ad4691_manual_channels, .num_channels = ARRAY_SIZE(ad4691_channels), }; static const struct ad4691_channel_info ad4693_sw_info = { .channels = ad4693_channels, + .manual_channels = ad4693_manual_channels, .num_channels = ARRAY_SIZE(ad4693_channels), }; static const struct ad4691_channel_info ad4691_offload_info = { .channels = ad4691_offload_channels, + .manual_channels = ad4691_offload_manual_channels, .num_channels = ARRAY_SIZE(ad4691_offload_channels), }; static const struct ad4691_channel_info ad4693_offload_info = { .channels = ad4693_offload_channels, + .manual_channels = ad4693_offload_manual_channels, .num_channels = ARRAY_SIZE(ad4693_offload_channels), }; @@ -324,6 +445,25 @@ struct ad4691_state { int irq; int vref_uV; u32 cnv_period_ns; + /* + * Snapped oscillator frequency (Hz) shared by all channels. Set when + * sampling_frequency or oversampling_ratio is written; written to + * OSC_FREQ_REG at buffer enable and single-shot time so both attributes + * can be set in any order. Reading in_voltage_sampling_frequency + * returns target_osc_freq_Hz / osr — the effective rate given the + * shared oversampling ratio. + */ + u32 target_osc_freq_Hz; + /* Shared oversampling ratio across all channels; always 1 in manual mode. */ + unsigned int osr; + /* + * Precomputed effective-rate lists, one row per entry in + * ad4691_oversampling_ratios[]. Populated at probe; read_avail picks + * the row for the current shared OSR. The tables are stable after + * probe so returning a pointer into them from read_avail is race-free. + */ + int samp_freq_avail[ARRAY_SIZE(ad4691_oversampling_ratios)][ARRAY_SIZE(ad4691_osc_freqs_Hz)]; + int samp_freq_avail_len[ARRAY_SIZE(ad4691_oversampling_ratios)]; bool manual_mode; bool irq_enabled; @@ -580,35 +720,95 @@ static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info) return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1; } -static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val) +/* + * Find the largest oscillator table entry that is both <= needed_osc and + * evenly divisible by osr (guaranteeing an integer effective rate on + * read-back). Returns 0 if no such entry exists in the chip's valid range. + */ +static unsigned int ad4691_find_osc_freq(struct ad4691_state *st, + unsigned int needed_osc, + unsigned int osr) { - unsigned int reg_val; - int ret; + unsigned int start = ad4691_samp_freq_start(st->info); - guard(mutex)(&st->lock); + for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { + if ((unsigned int)ad4691_osc_freqs_Hz[i] > needed_osc) + continue; + if (ad4691_osc_freqs_Hz[i] % osr) + continue; + return ad4691_osc_freqs_Hz[i]; + } + return 0; +} - ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val); - if (ret) - return ret; +/* Write target_osc_freq_Hz to OSC_FREQ_REG. Called at use time. */ +static int ad4691_write_osc_freq(struct ad4691_state *st) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { + if (ad4691_osc_freqs_Hz[i] == st->target_osc_freq_Hz) + return regmap_write(st->regmap, AD4691_OSC_FREQ_REG, i); + } + return -EINVAL; +} - *val = ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)]; - return IIO_VAL_INT; +/* Return the index of osr in ad4691_oversampling_ratios[], defaulting to 0. */ +static unsigned int ad4691_osr_index(unsigned int osr) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios) - 1; i++) { + if ((unsigned int)ad4691_oversampling_ratios[i] == osr) + return i; + } + return ARRAY_SIZE(ad4691_oversampling_ratios) - 1; +} + +/* + * Precompute samp_freq_avail[][]: for each OSR value, list the oscillator + * table entries that divide evenly by that OSR, expressed as effective rates + * (osc_freq / osr). Called once at probe after st->info is set. + */ +static void ad4691_precompute_samp_freq_avail(struct ad4691_state *st) +{ + unsigned int start = ad4691_samp_freq_start(st->info); + + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios); i++) { + unsigned int osr = ad4691_oversampling_ratios[i]; + int n = 0; + + for (unsigned int j = start; j < ARRAY_SIZE(ad4691_osc_freqs_Hz); j++) { + if (ad4691_osc_freqs_Hz[j] % osr) + continue; + st->samp_freq_avail[i][n++] = ad4691_osc_freqs_Hz[j] / osr; + } + st->samp_freq_avail_len[i] = n; + } } static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq) { - unsigned int start = ad4691_samp_freq_start(st->info); + unsigned int osr, found; + /* + * Read osr under st->lock: osr and target_osc_freq_Hz are modified + * together under the lock; reading after acquiring it ensures we see + * a consistent snapshot with no concurrent write racing us. + */ guard(mutex)(&st->lock); + osr = st->osr; - for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { - if (ad4691_osc_freqs_Hz[i] != freq) - continue; - return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG, - AD4691_OSC_FREQ_MASK, i); - } + if (freq <= 0 || (unsigned int)freq > st->info->max_rate / osr) + return -EINVAL; - return -EINVAL; + found = ad4691_find_osc_freq(st, (unsigned int)freq * osr, osr); + if (!found) + return -EINVAL; + + /* + * Store the snapped oscillator frequency; OSC_FREQ_REG is written at + * buffer enable and single-shot time so that sampling_frequency and + * oversampling_ratio can be set in any order. + */ + st->target_osc_freq_Hz = found; + return 0; } static int ad4691_read_avail(struct iio_dev *indio_dev, @@ -617,13 +817,27 @@ static int ad4691_read_avail(struct iio_dev *indio_dev, int *length, long mask) { struct ad4691_state *st = iio_priv(indio_dev); - unsigned int start = ad4691_samp_freq_start(st->info); switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: - *vals = &ad4691_osc_freqs_Hz[start]; + case IIO_CHAN_INFO_SAMP_FREQ: { + unsigned int osr_idx; + + /* + * The precomputed tables are stable after probe; only the + * current OSR needs to be read under the lock to pick the + * right row atomically. + */ + guard(mutex)(&st->lock); + osr_idx = ad4691_osr_index(st->osr); + *vals = st->samp_freq_avail[osr_idx]; *type = IIO_VAL_INT; - *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start; + *length = st->samp_freq_avail_len[osr_idx]; + return IIO_AVAIL_LIST; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4691_oversampling_ratios; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ad4691_oversampling_ratios); return IIO_AVAIL_LIST; default: return -EINVAL; @@ -634,7 +848,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct ad4691_state *st = iio_priv(indio_dev); - unsigned int reg_val, osc_idx, period_us; + unsigned int reg_val, period_us; int ret; guard(mutex)(&st->lock); @@ -654,7 +868,11 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev, if (ret) return ret; - ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val); + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + return ret; + + ret = ad4691_write_osc_freq(st); if (ret) return ret; @@ -662,9 +880,12 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev, if (ret) return ret; - osc_idx = FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val); - /* Wait 2 oscillator periods for the conversion to complete. */ - period_us = DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_idx]); + /* + * Wait osr + 1 oscillator periods: osr for accumulation, +1 for the + * pipeline margin (one extra period ensures the final result is ready). + */ + period_us = DIV_ROUND_UP((st->osr + 1) * USEC_PER_SEC, + st->target_osc_freq_Hz); fsleep(period_us); ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0); @@ -698,8 +919,22 @@ static int ad4691_read_raw(struct iio_dev *indio_dev, return ad4691_single_shot_read(indio_dev, chan, val); } - case IIO_CHAN_INFO_SAMP_FREQ: - return ad4691_get_sampling_freq(st, val); + case IIO_CHAN_INFO_SAMP_FREQ: { + /* + * Read target_osc_freq_Hz and osr under st->lock to get a + * consistent snapshot: write_raw for SAMP_FREQ or OSR modifies + * both fields under the lock, so a concurrent read without the + * lock could observe a new oscillator frequency with the old OSR. + */ + guard(mutex)(&st->lock); + *val = st->target_osc_freq_Hz / st->osr; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&st->lock); + *val = st->osr; + return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: *val = st->vref_uV / (MICRO / MILLI); *val2 = chan->scan_type.realbits; @@ -722,6 +957,33 @@ static int ad4691_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: return ad4691_set_sampling_freq(st, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + unsigned int old_effective, found, osr_idx; + + osr_idx = ad4691_osr_index(val); + if (ad4691_oversampling_ratios[osr_idx] != val) + return -EINVAL; + + /* + * Hold st->lock while computing the new oscillator frequency + * and updating both target_osc_freq_Hz and osr atomically: + * read_raw for SAMP_FREQ reads both fields under the lock and + * must see a consistent pair (new osc ↔ new osr). + * + * Snap target_osc_freq_Hz to the largest table entry that is + * both <= old_effective * new_osr and evenly divisible by + * new_osr, preserving an integer read-back of + * in_voltage_sampling_frequency after the OSR change. + */ + guard(mutex)(&st->lock); + old_effective = st->target_osc_freq_Hz / st->osr; + found = ad4691_find_osc_freq(st, old_effective * (unsigned int)val, val); + if (!found) + return -EINVAL; + st->target_osc_freq_Hz = found; + st->osr = val; + return 0; + } default: return -EINVAL; } @@ -776,6 +1038,10 @@ static int ad4691_enter_conversion_mode(struct ad4691_state *st) return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP, AD4691_MANUAL_MODE, AD4691_MANUAL_MODE); + ret = ad4691_write_osc_freq(st); + if (ret) + return ret; + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE); if (ret) @@ -943,6 +1209,10 @@ static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev) if (ret) goto err_unoptimize; + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + goto err_unoptimize; + ret = ad4691_enter_conversion_mode(st); if (ret) goto err_unoptimize; @@ -1122,6 +1392,10 @@ static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev) if (ret) return ret; + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + return ret; + ret = ad4691_enter_conversion_mode(st); if (ret) return ret; @@ -1538,11 +1812,15 @@ static int ad4691_config(struct ad4691_state *st) if (ret) return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n"); + st->target_osc_freq_Hz = ad4691_osc_freqs_Hz[ad4691_samp_freq_start(st->info)]; + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE); if (ret) return dev_err_probe(dev, ret, "Failed to write ADC_SETUP\n"); + ad4691_precompute_samp_freq_avail(st); + return 0; } @@ -1554,7 +1832,14 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, unsigned int i; int irq, ret; - indio_dev->channels = st->info->sw_info->channels; + /* + * Manual mode exposes channels without the oversampling_ratio attribute + * because ACC_DEPTH_IN is not configured in manual mode. + */ + if (st->manual_mode) + indio_dev->channels = st->info->sw_info->manual_channels; + else + indio_dev->channels = st->info->sw_info->channels; indio_dev->num_channels = st->info->sw_info->num_channels; indio_dev->info = st->manual_mode ? &ad4691_manual_info : &ad4691_cnv_burst_info; @@ -1636,7 +1921,18 @@ static int ad4691_setup_offload(struct iio_dev *indio_dev, st->offload = spi_offload; - indio_dev->channels = st->info->offload_info->channels; + /* + * CNV burst offload exposes oversampling_ratio (ACC_DEPTH_IN is + * configured per channel at buffer enable). Manual offload does not + * configure ACC_DEPTH_IN, so it uses a separate channel array + * without the oversampling_ratio attribute. Both paths use IIO_CPU + * (no .endianness annotation) because bits_per_word=16 causes the + * SPI Engine to produce native 16-bit DMA words. + */ + if (st->manual_mode) + indio_dev->channels = st->info->offload_info->manual_channels; + else + indio_dev->channels = st->info->offload_info->channels; indio_dev->num_channels = st->info->offload_info->num_channels; /* * Offload path uses DMA directly; no IIO trigger is involved, so @@ -1710,6 +2006,7 @@ static int ad4691_probe(struct spi_device *spi) st->info = spi_get_device_match_data(spi); if (!st->info) return -ENODEV; + st->osr = 1; ret = devm_mutex_init(dev, &st->lock); if (ret) From 4e8327ccb9098a8d71dfb000a02e844f4a002c00 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 29 May 2026 13:15:05 +0300 Subject: [PATCH 2420/5214] docs: iio: adc: ad4691: add driver documentation Add RST documentation for the AD4691 family ADC driver covering supported devices, IIO channels, operating modes, oversampling, reference voltage, LDO supply, reset, GP pins, SPI offload support, and buffer data format. Signed-off-by: Radu Sabau Signed-off-by: Jonathan Cameron --- Documentation/iio/ad4691.rst | 227 +++++++++++++++++++++++++++++++++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 229 insertions(+) create mode 100644 Documentation/iio/ad4691.rst diff --git a/Documentation/iio/ad4691.rst b/Documentation/iio/ad4691.rst new file mode 100644 index 000000000000..e45733341a4b --- /dev/null +++ b/Documentation/iio/ad4691.rst @@ -0,0 +1,227 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============= +AD4691 driver +============= + +ADC driver for Analog Devices Inc. AD4691 family of multichannel SAR ADCs. +The module name is ``ad4691``. + + +Supported devices +================= + +The following chips are supported by this driver: + +* `AD4691 `_ — 16-channel, 500 kSPS +* `AD4692 `_ — 16-channel, 1 MSPS +* `AD4693 `_ — 8-channel, 500 kSPS +* `AD4694 `_ — 8-channel, 1 MSPS + + +IIO channels +============ + +Each physical ADC input maps to one IIO voltage channel. The AD4691 and AD4692 +expose 16 channels (``voltage0`` through ``voltage15``); the AD4693 and AD4694 +expose 8 channels (``voltage0`` through ``voltage7``). + +All channels share a common scale (``in_voltage_scale``), derived from the +reference voltage. Each channel exposes: + +* ``in_voltageN_raw`` — single-shot ADC result + +The following attributes are shared across all channels: + +* ``in_voltage_sampling_frequency`` — effective output rate, defined as the + internal oscillator frequency divided by the oversampling ratio. Writing this + attribute selects the nearest achievable rate for the current OSR; the value + read back reflects the actual rate after snapping to the closest valid + oscillator entry. +* ``in_voltage_sampling_frequency_available`` — list of achievable effective + rates for the current oversampling ratio. The list updates dynamically when + the oversampling ratio changes. + +The following attributes are shared across all channels and only available in +CNV Burst Mode: + +* ``in_voltage_oversampling_ratio`` — hardware oversampling depth applied to + all channels; see `Oversampling`_ below. +* ``in_voltage_oversampling_ratio_available`` — valid ratios: 1, 2, 4, 8, 16, + 32. + + +Operating modes +=============== + +The driver supports two operating modes, selected automatically from the +device tree at probe time. + +Manual Mode +----------- + +Selected when no ``pwms`` property is present in the device tree. The CNV pin +is tied to the SPI chip-select: every CS assertion triggers a conversion and +returns the previous result. A user-defined IIO trigger (e.g. hrtimer trigger) +drives the buffer. + +Oversampling is not supported in Manual Mode. + +CNV Burst Mode +-------------- + +Selected when a ``pwms`` property is present in the device tree. A PWM drives +the CNV pin at the configured conversion rate. A GP pin wired to the SoC and +declared in the device tree signals DATA_READY at the end of each burst, +triggering a readout of all active channel results into the IIO buffer. + +The buffer output rate is controlled by the ``sampling_frequency`` attribute +on the IIO buffer. In practice the PWM rate should be set low enough to allow +the SPI readout to complete before the next conversion burst begins. + +Autonomous Mode (idle / single-shot) +------------------------------------- + +When the IIO buffer is disabled, ``in_voltageN_raw`` reads perform a single +conversion on the requested channel using the internal oscillator. The +oscillator is started and stopped around each read to save power. + + +Oversampling +============ + +In CNV Burst Mode a shared hardware accumulator averages a configurable number +of successive conversions across all active channels. The result is always a +16-bit mean, so the buffer data type (shown in ``buffer0/in_voltageN_type``) +is unaffected by the oversampling ratio. Valid ratios are 1, 2, 4, 8, 16 and +32; the default is 1 (no averaging). Oversampling is not supported in Manual +Mode. + +.. code-block:: bash + + # Set oversampling ratio to 16 (shared across all channels) + echo 16 > /sys/bus/iio/devices/iio:device0/in_voltage_oversampling_ratio + + # Read the resulting effective sampling frequency + cat /sys/bus/iio/devices/iio:device0/in_voltage_sampling_frequency + +Writing ``in_voltage_oversampling_ratio`` stores the new shared depth and snaps +the internal oscillator to the largest valid table entry that is both less than +or equal to ``old_effective_rate × new_osr`` and evenly divisible by +``new_osr``. This preserves an integer read-back of +``in_voltage_sampling_frequency`` after the change and keeps the oscillator as +close as possible to the previous effective rate. + + +Reference voltage +================= + +The driver supports two reference configurations, mutually exclusive: + +* **External reference** (``ref-supply``): a voltage between 2.4 V and 5.25 V + supplied externally. +* **Buffered internal reference** (``refin-supply``): an internal reference + buffer is enabled by the driver. + +Exactly one of ``ref-supply`` or ``refin-supply`` must be present in the +device tree. The reference voltage determines the full-scale range reported +via ``in_voltage_scale``. + + +LDO supply +========== + +The chip contains an internal LDO that powers part of the analog front-end. +The supply configuration is mutually exclusive: + +* **External VDD** (``vdd-supply``): an external 1.8 V supply is used directly; + the internal LDO is disabled. +* **Internal LDO** (``ldo-in-supply``): the internal LDO is enabled and fed + from the ``ldo-in`` regulator. Use this when no external 1.8 V VDD is present. + +Exactly one of ``vdd-supply`` or ``ldo-in-supply`` must be provided. + + +Reset +===== + +The driver supports two reset mechanisms: + +* **Hardware reset** (``reset-gpios`` in device tree): the GPIO line is + asserted then deasserted at probe; the driver waits 300 µs for the chip + to complete its internal reset sequence before accepting SPI commands. +* **Software reset** (fallback when ``reset-gpios`` is absent): written + automatically at probe. + + +GP pins and interrupts +====================== + +The chip exposes up to four general-purpose (GP) pins. In CNV Burst Mode +(non-offload), one GP pin must be wired to an interrupt-capable SoC input and +declared in the device tree using the ``interrupts`` and ``interrupt-names`` +properties. The ``interrupt-names`` value identifies which GP pin is used +(``"gp0"`` through ``"gp3"``). + +Example device tree fragment:: + + adc@0 { + compatible = "adi,ad4692"; + ... + interrupt-parent = <&gpio0>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "gp0"; + }; + + +SPI offload support +=================== + +When a SPI offload engine (e.g. the AXI SPI Engine) is present, the driver +uses DMA-backed transfers for CPU-independent, high-throughput data capture. +SPI offload is detected automatically at probe; if no offload hardware is +available the driver falls back to the software triggered-buffer path. + +Two SPI offload sub-modes exist: + +CNV Burst offload +----------------- + +Used when a ``pwms`` property is present and SPI offload is available. The PWM +drives CNV at the configured rate; on DATA_READY the offload engine reads all +active channel results and streams them directly to the IIO DMA buffer with no +CPU involvement. The GP pin used as DATA_READY trigger is supplied by the +trigger-source consumer at buffer enable time; no ``interrupt-names`` entry is +required. + +Manual offload +-------------- + +Used when no ``pwms`` property is present and SPI offload is available. A +periodic SPI offload trigger controls the conversion rate and the offload engine +streams results directly to the IIO DMA buffer. + +The ``sampling_frequency`` attribute on the IIO buffer controls the trigger +rate (in Hz). The initial rate is 100 kHz. + +Oversampling is not supported in Manual Mode. + + +Buffer data format +================== + +The sample format in the IIO buffer depends on whether SPI offload is in use. + +Software triggered-buffer path (no SPI offload) +------------------------------------------------ + +Each active channel occupies one 16-bit big-endian slot (``storagebits=16``, +``endianness=be``). Active channels are packed densely in scan-index order, +followed by a 64-bit software timestamp appended by the IIO core. + +SPI offload path +---------------- + +Each active channel occupies one 16-bit CPU-native slot (``storagebits=16``, +``endianness=cpu``). The SPI offload engine streams 16-bit words directly from +the SPI Engine into the DMA buffer; no software timestamp is appended. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index ba3e609c6a13..007e0a1fcc5a 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -23,6 +23,7 @@ Industrial I/O Kernel Drivers ad4000 ad4030 ad4062 + ad4691 ad4695 ad7191 ad7380 diff --git a/MAINTAINERS b/MAINTAINERS index 4cb938531c64..9e9457c7bba6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1482,6 +1482,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml +F: Documentation/iio/ad4691.rst F: drivers/iio/adc/ad4691.c ANALOG DEVICES INC AD4695 DRIVER From 6a75e3d4f6428b90f398354212e3a2e0172851d6 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:02 +0200 Subject: [PATCH 2421/5214] media: qcom: camss: vfe-340: Support for PIX client Add support for the vfe-340 PIX write engine, enabling frame capture through the PIX video device (e.g. msm_vfe0_pix). The PIX path requires a separate configuration flow from RDI, including cropping setup, line- based write engine configuration, and the correct packer format based on the input pixel format. In contrast to RDI, the PIX interface embeds a lightweight processing engine we can use for cropping, configuring custom stride/alignment, and, in the future, extracting frame statistics. The functionality has been validated on Arduino-Uno-Q with: media-ctl -d /dev/media0 --reset media-ctl -d /dev/media0 -l '"msm_csiphy0":1->"msm_csid0":0[1],"msm_csid0":4->"msm_vfe0_pix":0[1]' media-ctl -d /dev/media0 -V '"imx219 1-0010":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_csiphy0":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_csid0":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_vfe0_pix":0[fmt:SRGGB8_1X8/640x480 field:none]' yavta -B capture-mplane --capture=3 -n 3 -f SRGGB8 -s 640x480 /dev/video3 Signed-off-by: Loic Poulain [bod: Squash down fix for bpp unused in vfe_packer_format] Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-vfe-340.c | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c index d129b0d3a6ed..2526d568686d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-340.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c @@ -54,6 +54,7 @@ #define TFE_BUS_CLIENT_CFG(c) BUS_REG(0x200 + (c) * 0x100) #define TFE_BUS_CLIENT_CFG_EN BIT(0) +#define TFE_BUS_CLIENT_CFG_AUTORECOVER BIT(4) #define TFE_BUS_CLIENT_CFG_MODE_FRAME BIT(16) #define TFE_BUS_IMAGE_ADDR(c) BUS_REG(0x204 + (c) * 0x100) #define TFE_BUS_FRAME_INCR(c) BUS_REG(0x208 + (c) * 0x100) @@ -63,12 +64,23 @@ #define TFE_BUS_IMAGE_CFG_2(c) BUS_REG(0x214 + (c) * 0x100) #define TFE_BUS_IMAGE_CFG_2_DEFAULT 0xffff #define TFE_BUS_PACKER_CFG(c) BUS_REG(0x218 + (c) * 0x100) +#define TFE_BUS_PACKER_CFG_FMT_PLAIN8 0x1 #define TFE_BUS_PACKER_CFG_FMT_PLAIN64 0xa +#define TFE_BUS_PACKER_CFG_FMT_MIPI10 0xc +#define TFE_BUS_PACKER_CFG_FMT_MIPI12 0xd #define TFE_BUS_IRQ_SUBSAMPLE_CFG_0(c) BUS_REG(0x230 + (c) * 0x100) #define TFE_BUS_IRQ_SUBSAMPLE_CFG_1(c) BUS_REG(0x234 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100) +#define PP_CROP_REG(a) (0x2800 + (a)) +#define TFE_PP_CROP_CFG PP_CROP_REG(0x60) +#define TFE_PP_CROP_CFG_EN (BIT(0) | BIT(9)) +#define TFE_PP_CROP_LINE_CFG PP_CROP_REG(0x68) +#define TFE_PP_CROP_FIRST GENMASK(29, 16) +#define TFE_PP_CROP_LAST GENMASK(13, 0) +#define TFE_PP_CROP_PIX_CFG PP_CROP_REG(0x6C) + enum tfe_client { TFE_CLI_BAYER, TFE_CLI_IDEAL_RAW, @@ -245,18 +257,69 @@ static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client)); } +static u32 vfe_packer_format(struct vfe_device *vfe, u32 pixelformat) +{ + const struct camss_formats *fmt = vfe->res->formats_rdi; + unsigned int bpp = 0; + int i; + + for (i = 0; i < fmt->nformats; i++) { + if (fmt->formats[i].pixelformat == pixelformat) { + bpp = fmt->formats[i].mbus_bpp; + break; + } + } + + switch (bpp) { + case 10: + return TFE_BUS_PACKER_CFG_FMT_MIPI10; + case 12: + return TFE_BUS_PACKER_CFG_FMT_MIPI12; + default: + return TFE_BUS_PACKER_CFG_FMT_PLAIN8; + } +} + static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; u8 client = tfe_wm_client_map[wm]; + u32 cfg = TFE_BUS_CLIENT_CFG_EN; - /* Configuration for plain RDI frames */ - writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(client)); - writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); - writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); - writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); - writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(client)); + if (client == TFE_CLI_BAYER) { /* PIX - Line based */ + struct v4l2_rect *crop = &line->crop; + + /* Cropping */ + writel_relaxed(TFE_PP_CROP_CFG_EN, vfe->base + TFE_PP_CROP_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->top) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->top + crop->height - 1), + vfe->base + TFE_PP_CROP_LINE_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->left) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->left + crop->width - 1), + vfe->base + TFE_PP_CROP_PIX_CFG); + + /* Write Engine */ + writel_relaxed(pix->width | (pix->height << 16), + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(stride, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(vfe_packer_format(vfe, pix->pixelformat), + vfe->base + TFE_BUS_PACKER_CFG(client)); + + cfg |= TFE_BUS_CLIENT_CFG_AUTORECOVER; + } else { /* RDI - Frame based */ + writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, + vfe->base + TFE_BUS_PACKER_CFG(client)); + cfg |= TFE_BUS_CLIENT_CFG_MODE_FRAME; + } /* No dropped frames, one irq per frame */ writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client)); @@ -266,11 +329,10 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) vfe_enable_irq(vfe); - writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME, - vfe->base + TFE_BUS_CLIENT_CFG(client)); + writel(cfg, vfe->base + TFE_BUS_CLIENT_CFG(client)); dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n", - vfe->id, client, pix->width, pix->height, client); + vfe->id, client, pix->width, pix->height, stride); } static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) From 22fa1c39ba6fe726b547c877c924379b7fee260a Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Fri, 29 May 2026 00:20:51 -0400 Subject: [PATCH 2422/5214] clk: at91: keep securam node alive while mapping it pmc_register_ops() gets an owned reference to the "atmel,sama5d2-securam" node with of_find_compatible_node(). The success path dropped that reference before passing the node to of_iomap(), leaving of_iomap() to consume a node pointer after the caller had released its reference. Move of_node_put() after of_iomap() so the node remains referenced for the mapping operation. The unavailable-node error path already releases the reference. Fixes: 4d21be864092 ("clk: at91: pmc: execute suspend/resume only for backup mode") Signed-off-by: Yuho Choi Reviewed-by: Alexandre Belloni Link: https://patch.msgid.link/20260529042051.1626978-1-dbgh9129@gmail.com Signed-off-by: Claudiu Beznea --- drivers/clk/at91/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index b618a5e00b00..03a6c31d6aa8 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -180,9 +180,9 @@ static int __init pmc_register_ops(void) of_node_put(np); return -ENODEV; } - of_node_put(np); at91_pmc_backup_suspend = of_iomap(np, 0); + of_node_put(np); if (!at91_pmc_backup_suspend) { pr_warn("%s(): unable to map securam\n", __func__); return -ENOMEM; From 60aa6734f542a6c4b63cfbaabc544c3e67412ce9 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 12 Apr 2026 10:38:22 +0800 Subject: [PATCH 2423/5214] RISC-V: KVM: Batch stage-2 TLB flushes KVM RISC-V triggers a TLB flush for every single stage-2 PTE modification (unmap or write-protect) now. Although KVM coalesces the hardware IPIs, the software overhead of executing the flush work for every page is large, especially during dirty page tracking. Following the approach used in x86 and arm64, this patch optimizes the MMU logic by making the PTE manipulation functions return a boolean indicating if a leaf PTE was actually changed. The outer MMU functions bubble up this flag to batch the remote TLB flushes. Consequently, the flush operation is executed only once per batch. Moving it outside of the `mmu_lock` also reduces lock contention. Tested with tools/testing/selftests/kvm on a 4-vCPU guest (Host environment: QEMU 10.2.1 RISC-V) 1. demand_paging_test (1GB memory) time ./demand_paging_test -b 1G -v 4 - Total execution time reduced from ~2m39s to ~2m31s 2. dirty_log_perf_test (1GB memory) ./dirty_log_perf_test -b 1G -v 4 - "Clear dirty log time" per iteration dropped significantly from ~3.40s to ~0.18s Reviewed-by: Nutty Liu Reviewed-by: Anup Patel Signed-off-by: Jinyu Tang Link: https://lore.kernel.org/r/20260412023822.83341-1-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_gstage.h | 6 ++-- arch/riscv/kvm/gstage.c | 35 ++++++++++++++--------- arch/riscv/kvm/mmu.c | 44 +++++++++++++++++++++++------ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h index 9c908432bc17..f820c6783e16 100644 --- a/arch/riscv/include/asm/kvm_gstage.h +++ b/arch/riscv/include/asm/kvm_gstage.h @@ -70,13 +70,13 @@ enum kvm_riscv_gstage_op { GSTAGE_OP_WP, /* Write-protect */ }; -void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, +bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op); -void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, +bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, gpa_t start, gpa_t size, bool may_block); -void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end); +bool kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end); void kvm_riscv_gstage_mode_detect(void); diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index d9fe8be2a151..e020b334ae6f 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -337,35 +337,36 @@ int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage, return 0; } -void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, +bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op) { int i, ret; pte_t old_pte, *next_ptep; u32 next_ptep_level; unsigned long next_page_size, page_size; + bool flush = false; ret = gstage_level_to_page_size(gstage, ptep_level, &page_size); if (ret) - return; + return false; WARN_ON(addr & (page_size - 1)); if (!pte_val(ptep_get(ptep))) - return; + return false; if (ptep_level && !gstage_pte_leaf(ptep)) { next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); next_ptep_level = ptep_level - 1; ret = gstage_level_to_page_size(gstage, next_ptep_level, &next_page_size); if (ret) - return; + return false; if (op == GSTAGE_OP_CLEAR) set_pte(ptep, __pte(0)); for (i = 0; i < PTRS_PER_PTE; i++) - kvm_riscv_gstage_op_pte(gstage, addr + i * next_page_size, - &next_ptep[i], next_ptep_level, op); + flush |= kvm_riscv_gstage_op_pte(gstage, addr + i * next_page_size, + &next_ptep[i], next_ptep_level, op); if (op == GSTAGE_OP_CLEAR) put_page(virt_to_page(next_ptep)); } else { @@ -375,11 +376,13 @@ void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, else if (op == GSTAGE_OP_WP) set_pte(ptep, __pte(pte_val(ptep_get(ptep)) & ~_PAGE_WRITE)); if (pte_val(*ptep) != pte_val(old_pte)) - gstage_tlb_flush(gstage, ptep_level, addr); + flush = true; } + + return flush; } -void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, +bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, gpa_t start, gpa_t size, bool may_block) { int ret; @@ -388,6 +391,7 @@ void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, bool found_leaf; unsigned long page_size; gpa_t addr = start, end = start + size; + bool flush = false; while (addr < end) { found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level); @@ -399,8 +403,8 @@ void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, goto next; if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) - kvm_riscv_gstage_op_pte(gstage, addr, ptep, - ptep_level, GSTAGE_OP_CLEAR); + flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, + ptep_level, GSTAGE_OP_CLEAR); next: addr += page_size; @@ -412,9 +416,11 @@ void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, if (!(gstage->flags & KVM_GSTAGE_FLAGS_LOCAL) && may_block && addr < end) cond_resched_lock(&gstage->kvm->mmu_lock); } + + return flush; } -void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end) +bool kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end) { int ret; pte_t *ptep; @@ -422,6 +428,7 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end bool found_leaf; gpa_t addr = start; unsigned long page_size; + bool flush = false; while (addr < end) { found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level); @@ -433,11 +440,13 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end goto next; addr = ALIGN_DOWN(addr, page_size); - kvm_riscv_gstage_op_pte(gstage, addr, ptep, - ptep_level, GSTAGE_OP_WP); + flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, + ptep_level, GSTAGE_OP_WP); next: addr += page_size; } + + return flush; } void __init kvm_riscv_gstage_mode_detect(void) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 2d3def024270..8469ed932421 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -23,13 +23,15 @@ static void mmu_wp_memory_region(struct kvm *kvm, int slot) phys_addr_t start = memslot->base_gfn << PAGE_SHIFT; phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT; struct kvm_gstage gstage; + bool flush; kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); - kvm_riscv_gstage_wp_range(&gstage, start, end); + flush = kvm_riscv_gstage_wp_range(&gstage, start, end); spin_unlock(&kvm->mmu_lock); - kvm_flush_remote_tlbs_memslot(kvm, memslot); + if (flush) + kvm_flush_remote_tlbs_memslot(kvm, memslot); } int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, @@ -82,12 +84,17 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) { struct kvm_gstage gstage; + bool flush; kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); - kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); + flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); spin_unlock(&kvm->mmu_lock); + + if (flush) + kvm_flush_remote_tlbs_range(kvm, gpa >> PAGE_SHIFT, + size >> PAGE_SHIFT); } void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, @@ -99,10 +106,14 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, phys_addr_t start = (base_gfn + __ffs(mask)) << PAGE_SHIFT; phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT; struct kvm_gstage gstage; + bool flush; kvm_riscv_gstage_init(&gstage, kvm); - kvm_riscv_gstage_wp_range(&gstage, start, end); + flush = kvm_riscv_gstage_wp_range(&gstage, start, end); + if (flush) + kvm_flush_remote_tlbs_range(kvm, start >> PAGE_SHIFT, + (end - start) >> PAGE_SHIFT); } void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) @@ -128,12 +139,16 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, gpa_t gpa = slot->base_gfn << PAGE_SHIFT; phys_addr_t size = slot->npages << PAGE_SHIFT; struct kvm_gstage gstage; + bool flush; kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); - kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); + flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); spin_unlock(&kvm->mmu_lock); + if (flush) + kvm_flush_remote_tlbs_range(kvm, gpa >> PAGE_SHIFT, + size >> PAGE_SHIFT); } void kvm_arch_commit_memory_region(struct kvm *kvm, @@ -231,17 +246,24 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) { struct kvm_gstage gstage; bool mmu_locked; + bool flush; if (!kvm->arch.pgd) return false; kvm_riscv_gstage_init(&gstage, kvm); mmu_locked = spin_trylock(&kvm->mmu_lock); - kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT, - (range->end - range->start) << PAGE_SHIFT, - range->may_block); + + flush = kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT, + (range->end - range->start) << PAGE_SHIFT, + range->may_block); + if (mmu_locked) spin_unlock(&kvm->mmu_lock); + + if (flush) + kvm_flush_remote_tlbs_range(kvm, range->start, + range->end - range->start); return false; } @@ -557,11 +579,12 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) { struct kvm_gstage gstage; void *pgd = NULL; + bool flush = false; spin_lock(&kvm->mmu_lock); if (kvm->arch.pgd) { kvm_riscv_gstage_init(&gstage, kvm); - kvm_riscv_gstage_unmap_range(&gstage, 0UL, + flush = kvm_riscv_gstage_unmap_range(&gstage, 0UL, kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels), false); pgd = READ_ONCE(kvm->arch.pgd); kvm->arch.pgd = NULL; @@ -570,6 +593,9 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) } spin_unlock(&kvm->mmu_lock); + if (flush) + kvm_flush_remote_tlbs(kvm); + if (pgd) free_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size)); } From bf480c6133461a60e8a4486e560550a20e40ac11 Mon Sep 17 00:00:00 2001 From: Michal Clapinski Date: Thu, 23 Apr 2026 14:25:36 +0200 Subject: [PATCH 2424/5214] kho: fix deferred initialization of scratch areas Currently, if CONFIG_DEFERRED_STRUCT_PAGE_INIT is enabled, kho_release_scratch() will initialize the struct pages and set migratetype of KHO scratch. Unless the whole scratch fits below first_deferred_pfn, some of that will be overwritten either by deferred_init_pages() or memmap_init_reserved_range(). To fix it, make memmap_init_range(), deferred_init_memmap_chunk() and __init_page_from_nid() recognize KHO scratch regions and set migratetype of pageblocks in those regions to MIGRATE_CMA. Co-developed-by: Mike Rapoport (Microsoft) Signed-off-by: Michal Clapinski Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260423122538.140993-2-mclapinski@google.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/memblock.h | 21 +++++++++-- kernel/liveupdate/kexec_handover.c | 25 ------------- mm/memblock.c | 56 ++++++++++++------------------ mm/mm_init.c | 30 +++++++++------- 4 files changed, 58 insertions(+), 74 deletions(-) diff --git a/include/linux/memblock.h b/include/linux/memblock.h index b0f750d22a7b..5afcd99aa8c1 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -613,11 +613,28 @@ static inline void memtest_report_meminfo(struct seq_file *m) { } #ifdef CONFIG_MEMBLOCK_KHO_SCRATCH void memblock_set_kho_scratch_only(void); void memblock_clear_kho_scratch_only(void); -void memmap_init_kho_scratch_pages(void); +bool memblock_is_kho_scratch_memory(phys_addr_t addr); + +static inline enum migratetype kho_scratch_migratetype(unsigned long pfn, + enum migratetype mt) +{ + if (memblock_is_kho_scratch_memory(PFN_PHYS(pfn))) + return MIGRATE_CMA; + return mt; +} #else static inline void memblock_set_kho_scratch_only(void) { } static inline void memblock_clear_kho_scratch_only(void) { } -static inline void memmap_init_kho_scratch_pages(void) {} +static inline bool memblock_is_kho_scratch_memory(phys_addr_t addr) +{ + return false; +} + +static inline enum migratetype kho_scratch_migratetype(unsigned long pfn, + enum migratetype mt) +{ + return mt; +} #endif #endif /* _LINUX_MEMBLOCK_H */ diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 1b592d86dc48..a0aa8281dba1 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -1584,35 +1584,10 @@ static __init int kho_init(void) } fs_initcall(kho_init); -static void __init kho_release_scratch(void) -{ - phys_addr_t start, end; - u64 i; - - memmap_init_kho_scratch_pages(); - - /* - * Mark scratch mem as CMA before we return it. That way we - * ensure that no kernel allocations happen on it. That means - * we can reuse it as scratch memory again later. - */ - __for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, - MEMBLOCK_KHO_SCRATCH, &start, &end, NULL) { - ulong start_pfn = pageblock_start_pfn(PFN_DOWN(start)); - ulong end_pfn = pageblock_align(PFN_UP(end)); - ulong pfn; - - for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) - init_pageblock_migratetype(pfn_to_page(pfn), - MIGRATE_CMA, false); - } -} - void __init kho_memory_init(void) { if (kho_in.scratch_phys) { kho_scratch = phys_to_virt(kho_in.scratch_phys); - kho_release_scratch(); if (kho_mem_retrieve(kho_get_fdt())) kho_in.fdt_phys = 0; diff --git a/mm/memblock.c b/mm/memblock.c index ccd43f3abb82..6349c48154f4 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -1028,40 +1028,6 @@ int __init_memblock memblock_physmem_add(phys_addr_t base, phys_addr_t size) } #endif -#ifdef CONFIG_MEMBLOCK_KHO_SCRATCH -__init void memblock_set_kho_scratch_only(void) -{ - kho_scratch_only = true; -} - -__init void memblock_clear_kho_scratch_only(void) -{ - kho_scratch_only = false; -} - -__init void memmap_init_kho_scratch_pages(void) -{ - phys_addr_t start, end; - unsigned long pfn; - int nid; - u64 i; - - if (!IS_ENABLED(CONFIG_DEFERRED_STRUCT_PAGE_INIT)) - return; - - /* - * Initialize struct pages for free scratch memory. - * The struct pages for reserved scratch memory will be set up in - * memmap_init_reserved_pages() - */ - __for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, - MEMBLOCK_KHO_SCRATCH, &start, &end, &nid) { - for (pfn = PFN_UP(start); pfn < PFN_DOWN(end); pfn++) - init_deferred_page(pfn, nid); - } -} -#endif - /** * memblock_setclr_flag - set or clear flag for a memory region * @type: memblock type to set/clear flag for @@ -2535,6 +2501,28 @@ int reserve_mem_release_by_name(const char *name) return 1; } +#ifdef CONFIG_MEMBLOCK_KHO_SCRATCH +__init void memblock_set_kho_scratch_only(void) +{ + kho_scratch_only = true; +} + +__init void memblock_clear_kho_scratch_only(void) +{ + kho_scratch_only = false; +} + +bool __init_memblock memblock_is_kho_scratch_memory(phys_addr_t addr) +{ + int i = memblock_search(&memblock.memory, addr); + + if (i == -1) + return false; + + return memblock_is_kho_scratch(&memblock.memory.regions[i]); +} +#endif + #ifdef CONFIG_KEXEC_HANDOVER static int __init reserved_mem_preserve(void) diff --git a/mm/mm_init.c b/mm/mm_init.c index f9f8e1af921c..eddc0f03a779 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -692,9 +692,11 @@ void __meminit __init_page_from_nid(unsigned long pfn, int nid) } __init_single_page(pfn_to_page(pfn), pfn, zid, nid); - if (pageblock_aligned(pfn)) - init_pageblock_migratetype(pfn_to_page(pfn), MIGRATE_MOVABLE, - false); + if (pageblock_aligned(pfn)) { + enum migratetype mt = + kho_scratch_migratetype(pfn, MIGRATE_MOVABLE); + init_pageblock_migratetype(pfn_to_page(pfn), mt, false); + } } #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT @@ -927,7 +929,8 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone static void __init memmap_init_zone_range(struct zone *zone, unsigned long start_pfn, unsigned long end_pfn, - unsigned long *hole_pfn) + unsigned long *hole_pfn, + enum migratetype mt) { unsigned long zone_start_pfn = zone->zone_start_pfn; unsigned long zone_end_pfn = zone_start_pfn + zone->spanned_pages; @@ -940,8 +943,7 @@ static void __init memmap_init_zone_range(struct zone *zone, return; memmap_init_range(end_pfn - start_pfn, nid, zone_id, start_pfn, - zone_end_pfn, MEMINIT_EARLY, NULL, MIGRATE_MOVABLE, - false); + zone_end_pfn, MEMINIT_EARLY, NULL, mt, false); if (*hole_pfn < start_pfn) init_unavailable_range(*hole_pfn, start_pfn, zone_id, nid); @@ -957,6 +959,8 @@ static void __init memmap_init(void) for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid) { struct pglist_data *node = NODE_DATA(nid); + enum migratetype mt = + kho_scratch_migratetype(start_pfn, MIGRATE_MOVABLE); for (j = 0; j < MAX_NR_ZONES; j++) { struct zone *zone = node->node_zones + j; @@ -965,7 +969,7 @@ static void __init memmap_init(void) continue; memmap_init_zone_range(zone, start_pfn, end_pfn, - &hole_pfn); + &hole_pfn, mt); zone_id = j; } } @@ -1970,7 +1974,7 @@ unsigned long __init node_map_pfn_alignment(void) #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT static void __init deferred_free_pages(unsigned long pfn, - unsigned long nr_pages) + unsigned long nr_pages, enum migratetype mt) { struct page *page; unsigned long i; @@ -1983,8 +1987,7 @@ static void __init deferred_free_pages(unsigned long pfn, /* Free a large naturally-aligned chunk if possible */ if (nr_pages == MAX_ORDER_NR_PAGES && IS_MAX_ORDER_ALIGNED(pfn)) { for (i = 0; i < nr_pages; i += pageblock_nr_pages) - init_pageblock_migratetype(page + i, MIGRATE_MOVABLE, - false); + init_pageblock_migratetype(page + i, mt, false); __free_pages_core(page, MAX_PAGE_ORDER, MEMINIT_EARLY); return; } @@ -1994,8 +1997,7 @@ static void __init deferred_free_pages(unsigned long pfn, for (i = 0; i < nr_pages; i++, page++, pfn++) { if (pageblock_aligned(pfn)) - init_pageblock_migratetype(page, MIGRATE_MOVABLE, - false); + init_pageblock_migratetype(page, mt, false); __free_pages_core(page, 0, MEMINIT_EARLY); } } @@ -2053,6 +2055,8 @@ deferred_init_memmap_chunk(unsigned long start_pfn, unsigned long end_pfn, for_each_free_mem_range(i, nid, 0, &start, &end, NULL) { unsigned long spfn = PFN_UP(start); unsigned long epfn = PFN_DOWN(end); + enum migratetype mt = + kho_scratch_migratetype(spfn, MIGRATE_MOVABLE); if (spfn >= end_pfn) break; @@ -2065,7 +2069,7 @@ deferred_init_memmap_chunk(unsigned long start_pfn, unsigned long end_pfn, unsigned long chunk_end = min(mo_pfn, epfn); nr_pages += deferred_init_pages(zone, spfn, chunk_end); - deferred_free_pages(spfn, chunk_end - spfn); + deferred_free_pages(spfn, chunk_end - spfn, mt); spfn = chunk_end; From c6073743d0c7f011461bc542c9112180471affad Mon Sep 17 00:00:00 2001 From: Evangelos Petrongonas Date: Thu, 23 Apr 2026 14:25:37 +0200 Subject: [PATCH 2425/5214] kho: make preserved pages compatible with deferred struct page init When CONFIG_DEFERRED_STRUCT_PAGE_INIT is enabled, struct page initialization is deferred to parallel kthreads that run later in the boot process. During KHO restoration, kho_preserved_memory_reserve() writes metadata for each preserved memory region. However, if the struct page has not been initialized, this write targets uninitialized memory, potentially leading to errors like: BUG: unable to handle page fault for address: ... Fix this by introducing kho_get_preserved_page(), which ensures all struct pages in a preserved region are initialized by calling init_deferred_page() which is a no-op when the struct page is already initialized. Signed-off-by: Evangelos Petrongonas Co-developed-by: Michal Clapinski Signed-off-by: Michal Clapinski Reviewed-by: Pratyush Yadav (Google) Reviewed-by: Pasha Tatashin Reviewed-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260423122538.140993-3-mclapinski@google.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/Kconfig | 2 -- kernel/liveupdate/kexec_handover.c | 27 ++++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index 1a8513f16ef7..c13af38ba23a 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -1,12 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Live Update and Kexec HandOver" - depends on !DEFERRED_STRUCT_PAGE_INIT config KEXEC_HANDOVER bool "kexec handover" depends on ARCH_SUPPORTS_KEXEC_HANDOVER && ARCH_SUPPORTS_KEXEC_FILE - depends on !DEFERRED_STRUCT_PAGE_INIT select MEMBLOCK_KHO_SCRATCH select KEXEC_FILE select LIBFDT diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index a0aa8281dba1..eb8abd04e7ff 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -459,6 +459,31 @@ struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages) } EXPORT_SYMBOL_GPL(kho_restore_pages); +/* + * With CONFIG_DEFERRED_STRUCT_PAGE_INIT, struct pages in higher memory regions + * may not be initialized yet at the time KHO deserializes preserved memory. + * KHO uses the struct page to store metadata and a later initialization would + * overwrite it. + * Ensure all the struct pages in the preservation are + * initialized. kho_preserved_memory_reserve() marks the reservation as noinit + * to make sure they don't get re-initialized later. + */ +static struct page *__init kho_get_preserved_page(phys_addr_t phys, + unsigned int order) +{ + unsigned long pfn = PHYS_PFN(phys); + int nid; + + if (!IS_ENABLED(CONFIG_DEFERRED_STRUCT_PAGE_INIT)) + return pfn_to_page(pfn); + + nid = early_pfn_to_nid(pfn); + for (unsigned long i = 0; i < (1UL << order); i++) + init_deferred_page(pfn + i, nid); + + return pfn_to_page(pfn); +} + static int __init kho_preserved_memory_reserve(phys_addr_t phys, unsigned int order) { @@ -467,7 +492,7 @@ static int __init kho_preserved_memory_reserve(phys_addr_t phys, u64 sz; sz = 1 << (order + PAGE_SHIFT); - page = phys_to_page(phys); + page = kho_get_preserved_page(phys, order); /* Reserve the memory preserved in KHO in memblock */ memblock_reserve(phys, sz); From 1ab207e9b0926082963482f133403df0b3fcf59b Mon Sep 17 00:00:00 2001 From: Michal Clapinski Date: Thu, 23 Apr 2026 14:25:38 +0200 Subject: [PATCH 2426/5214] selftests: kho: test with deferred struct page init Enable DEFERRED_STRUCT_PAGE_INIT which depends on SMP. Also enable additional debugging options. Signed-off-by: Michal Clapinski Reviewed-by: Mike Rapoport (Microsoft) Acked-by: Pasha Tatashin Reviewed-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260423122538.140993-4-mclapinski@google.com Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/selftests/kho/vmtest.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/kho/vmtest.sh b/tools/testing/selftests/kho/vmtest.sh index 49fdac8e8b15..0014bd76e88d 100755 --- a/tools/testing/selftests/kho/vmtest.sh +++ b/tools/testing/selftests/kho/vmtest.sh @@ -59,10 +59,14 @@ function build_kernel() { tee "$kconfig" > "$kho_config" < Date: Wed, 29 Apr 2026 22:21:14 +0100 Subject: [PATCH 2427/5214] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length A session name must not be an empty string, and must not exceed the maximum size define in the uapi header, including null termination. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Cc: stable@vger.kernel.org Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Acked-by: Mike Rapoport (Microsoft) Link: https://lore.kernel.org/r/20260429212221.814107-2-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 7a42385dabe2..ec7aebc15a80 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -382,9 +382,13 @@ static int luo_session_getfile(struct luo_session *session, struct file **filep) int luo_session_create(const char *name, struct file **filep) { + size_t len = strnlen(name, LIVEUPDATE_SESSION_NAME_LENGTH); struct luo_session *session; int err; + if (len == 0 || len > LIVEUPDATE_SESSION_NAME_LENGTH - 1) + return -EINVAL; + session = luo_session_alloc(name); if (IS_ERR(session)) return PTR_ERR(session); From dab2b4c66aa0f44ccb6a0096906e5680c604fe39 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 29 Apr 2026 22:21:15 +0100 Subject: [PATCH 2428/5214] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length Verify that LIVEUPDATE_IOCTL_CREATE_SESSION ioctl which provide a name that is an empty string or too long are not allowed. Cc: stable@vger.kernel.org Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20260429212221.814107-3-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/liveupdate/liveupdate.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index 37c808fbe1e9..90268d86684f 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -386,4 +386,46 @@ TEST_F(liveupdate_device, prevent_double_preservation) ASSERT_EQ(close(session_fd2), 0); } +/* + * Test Case: Create Session with No Null Termination + * + * Verifies that filling the entire 64-byte name field with non-null characters + * (no '\0' terminator) is rejected by the kernel with EINVAL. + */ +TEST_F(liveupdate_device, create_session_no_null_termination) +{ + struct liveupdate_ioctl_create_session args = {}; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + /* Fill entire name field with 'X', no null terminator */ + args.size = sizeof(args); + memset(args.name, 'X', sizeof(args.name)); + + EXPECT_LT(ioctl(self->fd1, LIVEUPDATE_IOCTL_CREATE_SESSION, &args), 0); + EXPECT_EQ(errno, EINVAL); +} + +/* + * Test Case: Create Session with Empty Name + * + * Verifies that creating a session with an empty string name fails + * with EINVAL. + */ +TEST_F(liveupdate_device, create_session_empty_name) +{ + int session_fd; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd = create_session(self->fd1, ""); + EXPECT_EQ(session_fd, -EINVAL); +} + TEST_HARNESS_MAIN From 4c08a9f38c50c0df7091b5567be7a3bbac92de0b Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 29 Apr 2026 22:21:16 +0100 Subject: [PATCH 2429/5214] liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl Userspace when requesting a session via the ioctl specifies a name and gets a FD, but then there is no ioctl to go back the other way and get the name given a LUO session FD. This is problematic especially when there is a userspace orchestrator that wants to check what FDs it is handling for clients without having to do manual string scraping of procfs, or without procfs at all. Add a ioctl to simply get the name from an FD. Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20260429212221.814107-4-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- include/uapi/linux/liveupdate.h | 21 +++++++++++++++++++++ kernel/liveupdate/luo_session.c | 16 ++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h index 30bc66ee9436..3a9ff53b02e0 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -59,6 +59,7 @@ enum { LIVEUPDATE_CMD_SESSION_PRESERVE_FD = LIVEUPDATE_CMD_SESSION_BASE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41, LIVEUPDATE_CMD_SESSION_FINISH = 0x42, + LIVEUPDATE_CMD_SESSION_GET_NAME = 0x43, }; /** @@ -213,4 +214,24 @@ struct liveupdate_session_finish { #define LIVEUPDATE_SESSION_FINISH \ _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH) +/** + * struct liveupdate_session_get_name - ioctl(LIVEUPDATE_SESSION_GET_NAME) + * @size: Input; sizeof(struct liveupdate_session_get_name) + * @reserved: Input; Must be zero. Reserved for future use. + * @name: Output; A null-terminated string with the full session name. + * + * Retrieves the full name of the session associated with this file descriptor. + * This is useful because the kernel may truncate the name shown in /proc. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_session_get_name { + __u32 size; + __u32 reserved; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_SESSION_GET_NAME \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_GET_NAME) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index ec7aebc15a80..e7f27815c051 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -289,10 +289,24 @@ static int luo_session_finish(struct luo_session *session, return luo_ucmd_respond(ucmd, sizeof(*argp)); } +static int luo_session_get_name(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_get_name *argp = ucmd->cmd; + + if (argp->reserved != 0) + return -EINVAL; + + strscpy((char *)argp->name, session->name, sizeof(argp->name)); + + return luo_ucmd_respond(ucmd, sizeof(*argp)); +} + union ucmd_buffer { struct liveupdate_session_finish finish; struct liveupdate_session_preserve_fd preserve; struct liveupdate_session_retrieve_fd retrieve; + struct liveupdate_session_get_name get_name; }; struct luo_ioctl_op { @@ -319,6 +333,8 @@ static const struct luo_ioctl_op luo_session_ioctl_ops[] = { struct liveupdate_session_preserve_fd, token), IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, struct liveupdate_session_retrieve_fd, token), + IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name, + struct liveupdate_session_get_name, name), }; static long luo_session_ioctl(struct file *filep, unsigned int cmd, From a1f8ed3451ff64f6907fbbd6eb5ca4dff1e2a503 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 29 Apr 2026 22:21:17 +0100 Subject: [PATCH 2430/5214] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME Verify that the new LIVEUPDATE_SESSION_GET_NAME ioctl works as expected via new test cases in the existing liveupdate selftest. Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20260429212221.814107-5-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/liveupdate/liveupdate.c | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index 90268d86684f..c7d94b9181e1 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -102,6 +102,22 @@ static int create_session(int lu_fd, const char *name) return args.fd; } +/* Helper function to get a session name via ioctl. */ +static int get_session_name(int session_fd, char *name, size_t name_len) +{ + struct liveupdate_session_get_name args = {}; + + args.size = sizeof(args); + + if (ioctl(session_fd, LIVEUPDATE_SESSION_GET_NAME, &args)) + return -errno; + + strncpy(name, (char *)args.name, name_len - 1); + name[name_len - 1] = '\0'; + + return 0; +} + /* * Test Case: Create Duplicate Session * @@ -428,4 +444,59 @@ TEST_F(liveupdate_device, create_session_empty_name) EXPECT_EQ(session_fd, -EINVAL); } +/* + * Test Case: Get Session Name + * + * Verifies that the full session name can be retrieved from a session file + * descriptor via ioctl. + */ +TEST_F(liveupdate_device, get_session_name) +{ + char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; + const char *session_name = "get-name-test-session"; + int session_fd; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd = create_session(self->fd1, session_name); + ASSERT_GE(session_fd, 0); + + ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); + ASSERT_STREQ(name_buf, session_name); + + ASSERT_EQ(close(session_fd), 0); +} + +/* + * Test Case: Get Session Name at Maximum Length + * + * Verifies that a session name using the full LIVEUPDATE_SESSION_NAME_LENGTH + * (minus the null terminator) can be correctly retrieved. + */ +TEST_F(liveupdate_device, get_session_name_max_length) +{ + char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; + char long_name[LIVEUPDATE_SESSION_NAME_LENGTH]; + int session_fd; + + memset(long_name, 'A', sizeof(long_name) - 1); + long_name[sizeof(long_name) - 1] = '\0'; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd = create_session(self->fd1, long_name); + ASSERT_GE(session_fd, 0); + + ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); + ASSERT_STREQ(name_buf, long_name); + + ASSERT_EQ(close(session_fd), 0); +} + TEST_HARNESS_MAIN From d2850ff2f8c5acda4c1097aa53c006a75b091f2c Mon Sep 17 00:00:00 2001 From: David Matlack Date: Thu, 23 Apr 2026 17:40:28 +0000 Subject: [PATCH 2431/5214] liveupdate: Use refcount_t for FLB reference counts Use refcount_t instead of a raw integer to keep track of references on incoming and outgoing FLBs. Using refcount_t provides protection from overflow, underflow, and other issues. Fixes: cab056f2aae7 ("liveupdate: luo_flb: introduce File-Lifecycle-Bound global state") Signed-off-by: David Matlack Reviewed-by: Samiullah Khawaja Reviewed-by: Pasha Tatashin Link: https://lore.kernel.org/r/20260423174032.3140399-2-dmatlack@google.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/liveupdate.h | 3 ++- kernel/liveupdate/luo_flb.c | 22 ++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 30c5a39ff9e9..8d3bbc35c828 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -175,7 +176,7 @@ struct liveupdate_flb_ops { * @retrieved: True once the FLB's retrieve() callback has run. */ struct luo_flb_private_state { - long count; + refcount_t count; u64 data; void *obj; struct mutex lock; diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 00f5494812c4..59c5f31ab767 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -111,7 +111,7 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) struct luo_flb_private *private = luo_flb_get_private(flb); scoped_guard(mutex, &private->outgoing.lock) { - if (!private->outgoing.count) { + if (!refcount_read(&private->outgoing.count)) { struct liveupdate_flb_op_args args = {0}; int err; @@ -126,8 +126,10 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) } private->outgoing.data = args.data; private->outgoing.obj = args.obj; + refcount_set(&private->outgoing.count, 1); + } else { + refcount_inc(&private->outgoing.count); } - private->outgoing.count++; } return 0; @@ -138,8 +140,7 @@ static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb) struct luo_flb_private *private = luo_flb_get_private(flb); scoped_guard(mutex, &private->outgoing.lock) { - private->outgoing.count--; - if (!private->outgoing.count) { + if (refcount_dec_and_test(&private->outgoing.count)) { struct liveupdate_flb_op_args args = {0}; args.flb = flb; @@ -178,7 +179,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) for (int i = 0; i < fh->header_ser->count; i++) { if (!strcmp(fh->ser[i].name, flb->compatible)) { private->incoming.data = fh->ser[i].data; - private->incoming.count = fh->ser[i].count; + refcount_set(&private->incoming.count, fh->ser[i].count); found = true; break; } @@ -208,12 +209,8 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) static void luo_flb_file_finish_one(struct liveupdate_flb *flb) { struct luo_flb_private *private = luo_flb_get_private(flb); - u64 count; - scoped_guard(mutex, &private->incoming.lock) - count = --private->incoming.count; - - if (!count) { + if (refcount_dec_and_test(&private->incoming.count)) { struct liveupdate_flb_op_args args = {0}; if (!private->incoming.retrieved) { @@ -652,12 +649,13 @@ void luo_flb_serialize(void) guard(rwsem_read)(&luo_register_rwlock); list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) { struct luo_flb_private *private = luo_flb_get_private(gflb); + long count = refcount_read(&private->outgoing.count); - if (private->outgoing.count > 0) { + if (count > 0) { strscpy(fh->ser[i].name, gflb->compatible, sizeof(fh->ser[i].name)); fh->ser[i].data = private->outgoing.data; - fh->ser[i].count = private->outgoing.count; + fh->ser[i].count = count; i++; } } From d8e47bd066d7e626f9f45d416182d585b7e18b9b Mon Sep 17 00:00:00 2001 From: David Matlack Date: Thu, 23 Apr 2026 17:40:29 +0000 Subject: [PATCH 2432/5214] liveupdate: Reference count incoming FLB data Increment the incoming FLB refcount in liveupdate_flb_get_incoming() so that the FLB structure cannot be freed while the caller is actively using it. Add an additional liveupdate_flb_put_incoming() function so the caller can explicitly indicate when it is done using the FLB data. During a Live Update, a subsystem might need to hold onto the incoming File-Lifecycle-Bound (FLB) data for an extended period, such as during device enumeration. Incrementing the reference count guarantees that the data remains valid and accessible until the subsystem releases it, preventing future use-after-free bugs. Fixes: cab056f2aae7 ("liveupdate: luo_flb: introduce File-Lifecycle-Bound global state") Signed-off-by: David Matlack Reviewed-by: Samiullah Khawaja Reviewed-by: Pasha Tatashin Link: https://lore.kernel.org/r/20260423174032.3140399-3-dmatlack@google.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/liveupdate.h | 6 ++++++ kernel/liveupdate/luo_flb.c | 32 +++++++++++++++++--------------- lib/tests/liveupdate.c | 3 +++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 8d3bbc35c828..88722e5caf02 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -240,6 +240,8 @@ void liveupdate_unregister_flb(struct liveupdate_file_handler *fh, struct liveupdate_flb *flb); int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp); +void liveupdate_flb_put_incoming(struct liveupdate_flb *flb); + int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp); #else /* CONFIG_LIVEUPDATE */ @@ -280,6 +282,10 @@ static inline int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, return -EOPNOTSUPP; } +static inline void liveupdate_flb_put_incoming(struct liveupdate_flb *flb) +{ +} + static inline int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp) { diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 59c5f31ab767..8f5c5dd01cd0 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -165,7 +165,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) bool found = false; int err; - guard(mutex)(&private->incoming.lock); + lockdep_assert_held(&private->incoming.lock); if (private->incoming.finished) return -ENODATA; @@ -206,12 +206,14 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) return 0; } -static void luo_flb_file_finish_one(struct liveupdate_flb *flb) +void liveupdate_flb_put_incoming(struct liveupdate_flb *flb) { struct luo_flb_private *private = luo_flb_get_private(flb); + struct liveupdate_flb_op_args args = {0}; - if (refcount_dec_and_test(&private->incoming.count)) { - struct liveupdate_flb_op_args args = {0}; + scoped_guard(mutex, &private->incoming.lock) { + if (!refcount_dec_and_test(&private->incoming.count)) + return; if (!private->incoming.retrieved) { int err = luo_flb_retrieve_one(flb); @@ -220,16 +222,14 @@ static void luo_flb_file_finish_one(struct liveupdate_flb *flb) return; } - scoped_guard(mutex, &private->incoming.lock) { - args.flb = flb; - args.obj = private->incoming.obj; - flb->ops->finish(&args); + args.flb = flb; + args.obj = private->incoming.obj; + flb->ops->finish(&args); - private->incoming.data = 0; - private->incoming.obj = NULL; - private->incoming.finished = true; - module_put(flb->ops->owner); - } + private->incoming.data = 0; + private->incoming.obj = NULL; + private->incoming.finished = true; + module_put(flb->ops->owner); } } @@ -312,7 +312,7 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh) guard(rwsem_read)(&luo_register_rwlock); list_for_each_entry_reverse(iter, flb_list, list) - luo_flb_file_finish_one(iter->flb); + liveupdate_flb_put_incoming(iter->flb); } static void luo_flb_unregister_one(struct liveupdate_file_handler *fh, @@ -509,6 +509,8 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp) if (!liveupdate_enabled()) return -EOPNOTSUPP; + guard(mutex)(&private->incoming.lock); + if (!private->incoming.obj) { int err = luo_flb_retrieve_one(flb); @@ -516,7 +518,7 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp) return err; } - guard(mutex)(&private->incoming.lock); + refcount_inc(&private->incoming.count); *objp = private->incoming.obj; return 0; diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c index e4b0ecbee32f..4c08a7c6fb78 100644 --- a/lib/tests/liveupdate.c +++ b/lib/tests/liveupdate.c @@ -105,6 +105,9 @@ static void liveupdate_test_init(void) pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n", flb->compatible, ERR_PTR(err)); } + + if (!err) + liveupdate_flb_put_incoming(flb); } initialized = true; } From 051a224c4933e58a0592c5528e89831099c65d6b Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Mon, 4 May 2026 12:27:40 +0200 Subject: [PATCH 2433/5214] memblock tests: define MIGRATE_CMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kho_scratch_migratetype(), defined in include/linux/memblock.h uses enum migratetype. This breaks build for memblock tests with: ./linux/memblock.h:634:73: error: parameter 2 (‘mt’) has incomplete type 634 | enum migratetype mt) Fix it by defining enum migratetype and MIGRATE_CMA. As is the case with the other headers in tools/testing/memblock, do not bring in the whole thing, only what is needed. Reported-by: Mike Rapoport Closes: https://lore.kernel.org/linux-mm/afcdDm4aAJvNaQqH@kernel.org/ Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260504102742.3833159-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/memblock/linux/mmzone.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/memblock/linux/mmzone.h b/tools/testing/memblock/linux/mmzone.h index bb682659a12d..8d934ff5b080 100644 --- a/tools/testing/memblock/linux/mmzone.h +++ b/tools/testing/memblock/linux/mmzone.h @@ -35,4 +35,8 @@ typedef struct pglist_data { } pg_data_t; +enum migratetype { + MIGRATE_CMA, +}; + #endif From 4e8729e6598ba0d10021dbf48b308cd53a06bbc4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 28 May 2026 22:37:38 -0400 Subject: [PATCH 2434/5214] ring-buffer: Better comment the use of RB_MISSED_EVENTS If the persistent ring buffer is detected on boot up to have a corrupted sub-buffer, that sub-buffer is cleared to zero and its commit value has the RB_MISSED_EVENTS bit set. That bit is to allow the "trace", "trace_pipe" and "trace_pipe_raw" files know that events were dropped by outputting "[LOST EVENTS]". Only in this case does that bit get set in the writeable portion of the ring buffer. When events are dropped in the normal ring buffer, that information is stored in the cpu_buffer descriptor and the RB_MISSED_EVENTS is set in the buffer page at the time the page is consumed. It is never set in the writeable portion of the buffer. Add comments to describe this better as it can be confusing to know when the RB_MISSED_EVENTS are set in the commit portion of the buffer page. Link: https://lore.kernel.org/all/20260529001500.14178455a046a5cbc6180861@kernel.org/ Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260528223738.41276c0e@fedora Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 910f6b3adf74..06fb365bb86e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1929,6 +1929,12 @@ static int __rb_validate_buffer(struct buffer_page *bpage, int cpu, */ if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) { local_set(&bpage->entries, 0); + /* + * Note, the RB_MISSED_EVENTS is only set inside the main write + * buffer by this verification logic. The normal ring buffer + * has this bit set when the page is read and passed to the + * consumers. + */ local_set(&dpage->commit, RB_MISSED_EVENTS); dpage->time_stamp = prev_ts ? prev_ts : next_ts; ret = -1; @@ -7232,6 +7238,14 @@ int ring_buffer_read_page(struct trace_buffer *buffer, local_add(RB_MISSED_STORED, &dpage->commit); size += sizeof(missed_events); } + /* + * Note, for the persistent ring buffer, the RB_MISSED_EVENTS + * may have been set in the main buffer via the verification code. + * But here, dpage is a copy of that page and has not yet had + * the RB_MISSED_EVENTS set. As for the normal buffers, + * the main write buffer does not set these bits and it needs + * to be set here. + */ local_add(RB_MISSED_EVENTS, &dpage->commit); } From 9e8bc49f91f3f81d957c4f1c1f09fe94e2f88f6a Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:40 -0600 Subject: [PATCH 2435/5214] fpga: dfl: add bounds check in dfh_get_param_size() dfh_get_param_size() can return a parameter size larger than the feature region because the loop bounds check is evaluated before incrementing size. If the EOP (End of Parameters) bit is set in the same iteration, the inflated size is returned without re-validation against max. This can cause create_feature_instance() to call memcpy_fromio() with a size exceeding the ioremap'd region when a malicious FPGA device provides crafted DFHv1 parameter headers. Add a bounds check after the size increment to ensure the accumulated size never exceeds the feature boundary. Fixes: 4747ab89b4a6 ("fpga: dfl: add basic support for DFHv1") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-2-sebasjosue84@gmail.com Signed-off-by: Xu Yilun --- drivers/fpga/dfl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 4087a36a0571..4c63c7c8579b 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -1132,6 +1132,8 @@ static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) return -EINVAL; size += next * sizeof(u64); + if (size > max) + return -EINVAL; if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) return size; From fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:41 -0600 Subject: [PATCH 2436/5214] fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region() afu_ioctl_dma_map() accepts a 64-bit length from userspace via DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value is passed to afu_dma_pin_pages() where npages is derived as length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes int nr_pages, causing implicit truncation if length is very large. Validate map.length at the ioctl entry point before calling afu_dma_map_region(), rejecting values whose page count exceeds INT_MAX. Fixes: fa8dda1edef9 ("fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-3-sebasjosue84@gmail.com Signed-off-by: Xu Yilun --- drivers/fpga/dfl-afu-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 3bf8e7338dbe..097a97eeea66 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg) if (map.argsz < minsz || map.flags) return -EINVAL; + if (map.length >> PAGE_SHIFT > (u64)INT_MAX) + return -EINVAL; + ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova); if (ret) return ret; From 43a1974da6bc7ce8f4d1dc1d03d56997428c29c3 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:42 -0600 Subject: [PATCH 2437/5214] fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header() mpf_ops_parse_header() reads header_size from the bitstream at MPF_HEADER_SIZE_OFFSET (24). When header_size is zero, the expression *(buf + header_size - 1) reads one byte before the buffer start. Since initial_header_size is set to 71 in mpf_ops, the fpga-mgr core guarantees the buffer is large enough to reach MPF_HEADER_SIZE_OFFSET. The only real gap is the zero header_size case, which cannot be resolved by providing a larger buffer, so return -EINVAL. Fixes: 5f8d4a900830 ("fpga: microchip-spi: add Microchip MPF FPGA manager") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-4-sebasjosue84@gmail.com Signed-off-by: Xu Yilun --- drivers/fpga/microchip-spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 6134cea86ac8..cc8f6d7bb978 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -116,6 +116,9 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, } header_size = *(buf + MPF_HEADER_SIZE_OFFSET); + if (!header_size) + return -EINVAL; + if (header_size > count) { info->header_size = header_size; return -EAGAIN; From 507e3b479f9c6d85135eb5e1a77fb3fddb259ad8 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 19 May 2026 14:24:26 +0200 Subject: [PATCH 2438/5214] liveupdate: validate session type before performing operation The sessions ioctls are not applicable to all session types. PRESERVE_FD is only applicable to outgoing sessions. RETRIEVE_FD and FINISH are only valid for incoming session. Calling a incoming ioctl on an outgoing session is invalid and can cause file handlers to run into unexpected errors. For example, a user can create a (outgoing) session, preserve a memfd, and then immediately do a retrieve without doing a kexec in between. This would result in memfd's retrieve handler to run. The handlers expects to be called from a post-kexec context, and will try to do a kho_restore_vmalloc() or kho_restore_folio() to try and restore memory. KHO catches this (thanks to KHO_PAGE_MAGIC) and returns an error, but since this is considered an internal error and KHO throws out a bunch of WARN()s. Associate a type with each ioctl op and validate the type in luo_session_ioctl() before dispatching the ioctl handler to make sure the op is being called for the right session type. Fixes: 16cec0d26521 ("liveupdate: luo_session: add ioctls for file preservation") Cc: stable@vger.kernel.org Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260519122428.2378446-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 39 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index e7f27815c051..099db679bdc5 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -309,34 +309,60 @@ union ucmd_buffer { struct liveupdate_session_get_name get_name; }; +/* Type of sessions the ioctl applies to. */ +enum luo_ioctl_type { + LUO_IOCTL_INCOMING, + LUO_IOCTL_OUTGOING, + LUO_IOCTL_ALL, +}; + struct luo_ioctl_op { unsigned int size; unsigned int min_size; unsigned int ioctl_num; + enum luo_ioctl_type type; int (*execute)(struct luo_session *session, struct luo_ucmd *ucmd); }; -#define IOCTL_OP(_ioctl, _fn, _struct, _last) \ +#define IOCTL_OP(_ioctl, _fn, _struct, _last, _type) \ [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_SESSION_BASE] = { \ .size = sizeof(_struct) + \ BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ sizeof(_struct)), \ .min_size = offsetofend(_struct, _last), \ .ioctl_num = _ioctl, \ + .type = _type, \ .execute = _fn, \ } static const struct luo_ioctl_op luo_session_ioctl_ops[] = { IOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish, - struct liveupdate_session_finish, reserved), + struct liveupdate_session_finish, reserved, LUO_IOCTL_INCOMING), IOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd, - struct liveupdate_session_preserve_fd, token), + struct liveupdate_session_preserve_fd, token, LUO_IOCTL_OUTGOING), IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, - struct liveupdate_session_retrieve_fd, token), + struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING), IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name, - struct liveupdate_session_get_name, name), + struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_ALL), }; +static bool luo_ioctl_type_valid(struct luo_session *session, + const struct luo_ioctl_op *op) +{ + switch (op->type) { + case LUO_IOCTL_INCOMING: + /* Retrieved is only set on incoming sessions */ + return session->retrieved; + case LUO_IOCTL_OUTGOING: + return !session->retrieved; + case LUO_IOCTL_ALL: + return true; + } + + /* Catch-all. */ + return false; +} + static long luo_session_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -361,6 +387,8 @@ static long luo_session_ioctl(struct file *filep, unsigned int cmd, op = &luo_session_ioctl_ops[nr - LIVEUPDATE_CMD_SESSION_BASE]; if (op->ioctl_num != cmd) return -ENOIOCTLCMD; + if (!luo_ioctl_type_valid(session, op)) + return -EINVAL; if (ucmd.user_size < op->min_size) return -EINVAL; @@ -631,4 +659,3 @@ int luo_session_serialize(void) return err; } - From 42897b76de43e0686c18120e92ac95026ab13940 Mon Sep 17 00:00:00 2001 From: LongWei Date: Sat, 16 May 2026 16:56:53 +0800 Subject: [PATCH 2439/5214] kho: docs: fix typo in ABI documentation Replace "Indentifies" with "Identifies". Signed-off-by: Long Wei Link: https://patch.msgid.link/20260516085653.2193872-1-longwei27@huawei.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/kexec_handover.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h index db9bda6dd310..fb2d37417ad9 100644 --- a/include/linux/kho/abi/kexec_handover.h +++ b/include/linux/kho/abi/kexec_handover.h @@ -64,7 +64,7 @@ * Root KHO Node (/): * - compatible: "kho-v3" * - * Indentifies the overall KHO ABI version. + * Identifies the overall KHO ABI version. * * - preserved-memory-map: u64 * From 6bd280c7feebd922144fd74869ee222c22ddd042 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 19 May 2026 14:57:06 +0200 Subject: [PATCH 2440/5214] liveupdate: document liveupdate=on While the liveupdate= parameter is documented in kernel-parameters.txt, it is not listed in LUO's user facing documentation. This can make it hard for users to figure out how to enable the subsystem, since enabling just the config isn't enough. Note the need for the kernel parameter in LUO core documentation, which gets exported to Documentation/core-api/liveupdate.rst. Suggested-by: Luca Boccassi Signed-off-by: Pratyush Yadav (Google) Acked-by: Luca Boccassi Link: https://patch.msgid.link/20260519125714.2435640-1-pratyush@kernel.org Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 803f51c84275..5d5827ced73c 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -36,6 +36,10 @@ * * LUO uses Kexec Handover to transfer memory state from the current kernel to * the next kernel. For more details see Documentation/core-api/kho/index.rst. + * + * .. note:: + * To enable LUO, boot the kernel with the ``liveupdate=on`` command line + * parameter. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 0e39380a7316122e1b00012b3f3cd3e318b3e7d3 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 19 May 2026 18:05:49 +0200 Subject: [PATCH 2441/5214] kho: make sure scratch size is always aligned by CMA_MIN_ALIGNMENT_BYTES When using scratch_scale, the scratch sizes are rounded up to CMA_MIN_ALIGNMENT_BYTES since they will be released as MIGRATE_CMA. This is not done when using fixed scratch sizes via command line. This can result in user specifying a size which is not aligned, and thus kernel releasing a pageblock that is only partially scratch. Do the rounding up for both cases in scratch_size_update(). Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers") Cc: stable@kernel.org Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260519160554.2713361-1-pratyush@kernel.org Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/kexec_handover.c | 32 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index eb8abd04e7ff..4834a809985a 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -618,20 +618,30 @@ early_param("kho_scratch", kho_parse_scratch_size); static void __init scratch_size_update(void) { - phys_addr_t size; + /* + * If fixed sizes are not provided via command line, calculate them + * now. + */ + if (scratch_scale) { + phys_addr_t size; - if (!scratch_scale) - return; + size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, + NUMA_NO_NODE); + size = size * scratch_scale / 100; + scratch_size_lowmem = size; - size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, - NUMA_NO_NODE); - size = size * scratch_scale / 100; - scratch_size_lowmem = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, + NUMA_NO_NODE); + size = size * scratch_scale / 100 - scratch_size_lowmem; + scratch_size_global = size; + } - size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, - NUMA_NO_NODE); - size = size * scratch_scale / 100 - scratch_size_lowmem; - scratch_size_global = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + /* + * Scratch areas are released as MIGRATE_CMA. Round them up to the right + * size. + */ + scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES); + scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES); } static phys_addr_t __init scratch_size_node(int nid) From 608d1642f20077abff382f04bcd74a4a6cec3018 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 19 May 2026 16:28:39 +0100 Subject: [PATCH 2442/5214] liveupdate: document systemd support systemd v261 will add native support for LUO via the file descriptor store mechanism. Add a brief paragraph in the LUO userspace documentation page to inform users. Signed-off-by: Luca Boccassi Reviewed-by: Pasha Tatashin Acked-by: Pratyush Yadav Link: https://patch.msgid.link/20260519152957.548119-1-luca.boccassi@gmail.com Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/userspace-api/liveupdate.rst | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/userspace-api/liveupdate.rst b/Documentation/userspace-api/liveupdate.rst index 41c0473e4f16..f6efc32fb40b 100644 --- a/Documentation/userspace-api/liveupdate.rst +++ b/Documentation/userspace-api/liveupdate.rst @@ -14,6 +14,29 @@ ioctl uAPI =========== .. kernel-doc:: include/uapi/linux/liveupdate.h +Userspace Integration +===================== + +systemd (since version v261) uses LUO to preserve its per-service file +descriptor store across a kexec-based live update. Services opt in by setting +``FileDescriptorStoreMax=`` and ``FileDescriptorStorePreserve=`` in their unit, +and push file descriptors with a name into the store via +``sd_pid_notify_with_fds(... "FDSTORE=1\nFDNAME=foo")``. + +Services may also create their own LUO sessions (via ``/dev/liveupdate``) and +push the resulting session fds into their file descriptor store like any other +fd. systemd detects such session fds and handles them accordingly, and +hands the re-retrieved session fd back to the service after kexec, using the +existing file descriptor store service interface. + +For details, see: + +- `File Descriptor Store `_ +- `systemd.service(5) FileDescriptorStorePreserve= + `_ +- `sd_pid_notify_with_fds(3) + `_ + See Also ======== From eb2a73624ed07e4b69adeaec1ce3a62a7236f1ff Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 27 May 2026 16:37:12 +0100 Subject: [PATCH 2443/5214] liveupdate: document current compatibility status We currently do not guarantee compatibility due to ongoing development, so mention this explicitly in the user-facing documentation. Signed-off-by: Luca Boccassi Acked-by: Pratyush Yadav Link: https://patch.msgid.link/20260527153725.1770394-1-luca.boccassi@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/userspace-api/liveupdate.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/userspace-api/liveupdate.rst b/Documentation/userspace-api/liveupdate.rst index f6efc32fb40b..cdd74627ab75 100644 --- a/Documentation/userspace-api/liveupdate.rst +++ b/Documentation/userspace-api/liveupdate.rst @@ -14,6 +14,14 @@ ioctl uAPI =========== .. kernel-doc:: include/uapi/linux/liveupdate.h +Note on Compatibility +===================== + +Note that the Live Update feature is still under development and subject to +change, so compatibility is not guaranteed when kexec rebooting between +different kernel versions. This is expected to change and stabilize in a future +version. + Userspace Integration ===================== From 5eff62b051fbdb686e885c1468301d964f2e3d66 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:33 +0000 Subject: [PATCH 2444/5214] liveupdate: skip serialization for context-preserving kexec A preserve_context kexec returns to the current kernel, which is unrelated to live update where the state is passed to the next kernel. Skip liveupdate_reboot() in this case to avoid serialization and prevent sessions from being left in a frozen state upon return. Fixes: db8bed8082dc ("kexec: call liveupdate_reboot() before kexec") Reported-by: Oskar Gerlicz Kowalczuk Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-2-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/kexec_core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index a43d2da0fe3e..dc770b9a6d05 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -1146,9 +1146,11 @@ int kernel_kexec(void) goto Unlock; } - error = liveupdate_reboot(); - if (error) - goto Unlock; + if (!kexec_image->preserve_context) { + error = liveupdate_reboot(); + if (error) + goto Unlock; + } #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { From d3ae9e7fddb4036f50003d7fa1ef52801fdb961b Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:34 +0000 Subject: [PATCH 2445/5214] liveupdate: fix TOCTOU race in luo_session_retrieve() Extend the scope of the rwsem_read lock in luo_session_retrieve() to overlap with the acquisition of the session mutex. This prevents a concurrent thread from releasing and freeing the session between the lookup and the mutex lock. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-3-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 099db679bdc5..a1c742eeb444 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -463,12 +463,11 @@ int luo_session_retrieve(const char *name, struct file **filep) struct luo_session *it; int err; - scoped_guard(rwsem_read, &sh->rwsem) { - list_for_each_entry(it, &sh->list, list) { - if (!strncmp(it->name, name, sizeof(it->name))) { - session = it; - break; - } + guard(rwsem_read)(&sh->rwsem); + list_for_each_entry(it, &sh->list, list) { + if (!strncmp(it->name, name, sizeof(it->name))) { + session = it; + break; } } From bb1328be35bf43c88288c5c31ceb45181b574c0c Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:35 +0000 Subject: [PATCH 2446/5214] liveupdate: block session mutations during reboot During the reboot() syscall, user processes may still be running concurrently and attempting to mutate sessions (e.g., creating, retrieving, or releasing sessions). To prevent this, introduce luo_session_serialize_rwsem to synchronize mutations with the serialization process. All session mutation operations (create, retrieve, release, ioctl) take the read lock. The serialization process (luo_session_serialize) takes the write lock and holds it indefinitely on success. This effectively freezes the LUO session subsystem during the transition to the new kernel. If serialization fails, the lock is released to allow recovery. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Reported-by: Oskar Gerlicz Kowalczuk Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Reviewed-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260527202737.1345192-4-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 51 +++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index a1c742eeb444..5c6cebc6e326 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -46,6 +46,38 @@ * 4. Retrieval: A userspace agent in the new kernel can then call * `luo_session_retrieve()` with a session name to get a new file * descriptor and access the preserved state. + * + * Locking: + * + * The LUO session subsystem uses a three-tier locking hierarchy to ensure thread + * safety and prevent deadlocks during concurrent session mutations and kexec + * serialization: + * + * 1. `luo_session_serialize_rwsem` (global rwsem): + * Protects session mutations (creation, retrieval, release, and ioctls) + * against the serialization process during reboot. + * + * - Readers: Taken by any path modifying or accessing session state (e.g., + * `luo_session_create()`, `luo_session_retrieve()`, `luo_session_release()`, + * and `luo_session_ioctl()`). + * - Writer: Taken by the serialization process (`luo_session_serialize()`) + * during reboot. On success, the write lock is held indefinitely to freeze + * the subsystem. On failure, it is released to allow recovery. + * + * 2. `luo_session_header->rwsem` (per-list rwsem): + * Synchronizes list-level operations for the incoming and outgoing session headers. + * + * - Writer: Taken during list mutation operations (inserting or removing a + * session from the list). + * - Reader: Taken when traversing the list (e.g., retrieving a session by name). + * + * 3. `luo_session->mutex` (per-session mutex): + * Protects the internal state and file sets of an individual session. It is + * acquired during per-session operations such as preserving, retrieving, + * or freezing files. + * + * Lock Hierarchy: + * `luo_session_serialize_rwsem` -> `luo_session_header->rwsem` -> `luo_session->mutex` */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -75,6 +107,8 @@ sizeof(struct luo_session_header_ser)) / \ sizeof(struct luo_session_ser)) +static DECLARE_RWSEM(luo_session_serialize_rwsem); + /** * struct luo_session_header - Header struct for managing LUO sessions. * @count: The number of sessions currently tracked in the @list. @@ -205,6 +239,7 @@ static int luo_session_release(struct inode *inodep, struct file *filep) struct luo_session *session = filep->private_data; struct luo_session_header *sh; + guard(rwsem_read)(&luo_session_serialize_rwsem); /* If retrieved is set, it means this session is from incoming list */ if (session->retrieved) { int err = luo_session_finish_one(session); @@ -398,6 +433,7 @@ static long luo_session_ioctl(struct file *filep, unsigned int cmd, if (ret) return ret; + guard(rwsem_read)(&luo_session_serialize_rwsem); return op->execute(session, &ucmd); } @@ -437,14 +473,17 @@ int luo_session_create(const char *name, struct file **filep) if (IS_ERR(session)) return PTR_ERR(session); + down_read(&luo_session_serialize_rwsem); err = luo_session_insert(&luo_session_global.outgoing, session); if (err) goto err_free; - scoped_guard(mutex, &session->mutex) - err = luo_session_getfile(session, filep); + mutex_lock(&session->mutex); + err = luo_session_getfile(session, filep); + mutex_unlock(&session->mutex); if (err) goto err_remove; + up_read(&luo_session_serialize_rwsem); return 0; @@ -452,6 +491,7 @@ int luo_session_create(const char *name, struct file **filep) luo_session_remove(&luo_session_global.outgoing, session); err_free: luo_session_free(session); + up_read(&luo_session_serialize_rwsem); return err; } @@ -463,6 +503,7 @@ int luo_session_retrieve(const char *name, struct file **filep) struct luo_session *it; int err; + guard(rwsem_read)(&luo_session_serialize_rwsem); guard(rwsem_read)(&sh->rwsem); list_for_each_entry(it, &sh->list, list) { if (!strncmp(it->name, name, sizeof(it->name))) { @@ -635,7 +676,8 @@ int luo_session_serialize(void) int i = 0; int err; - guard(rwsem_write)(&sh->rwsem); + down_write(&luo_session_serialize_rwsem); + down_write(&sh->rwsem); list_for_each_entry(session, &sh->list, list) { err = luo_session_freeze_one(session, &sh->ser[i]); if (err) @@ -646,6 +688,7 @@ int luo_session_serialize(void) i++; } sh->header_ser->count = sh->count; + up_write(&sh->rwsem); return 0; @@ -655,6 +698,8 @@ int luo_session_serialize(void) luo_session_unfreeze_one(session, &sh->ser[i]); memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name)); } + up_write(&sh->rwsem); + up_write(&luo_session_serialize_rwsem); return err; } From 291dcd37c8c8f8f8e1bccc92228f44bf371762a8 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:36 +0000 Subject: [PATCH 2447/5214] liveupdate: fix u-a-f in luo_file_unpreserve_files() and luo_file_finish() In luo_file_unpreserve_files() and luo_file_finish(), reorder module_put() and xa_erase() to ensure the file handler module remains pinned while its operations are being accessed. Specifically, luo_get_id() dereferences fh->ops->get_id, so the module reference must be held until after xa_erase() (which calls luo_get_id) completes. For luo_file_finish(), this requires moving the module_put() call out of the luo_file_finish_one() helper and into the main loop of luo_file_finish() itself. Fixes: 00d0b372374f ("liveupdate: prevent double management of files") Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-5-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index a0a419085e28..208987502f73 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -385,10 +385,11 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set) args.private_data = luo_file->private_data; luo_file->fh->ops->unpreserve(&args); luo_flb_file_unpreserve(luo_file->fh); - module_put(luo_file->fh->ops->owner); xa_erase(&luo_preserved_files, luo_get_id(luo_file->fh, luo_file->file)); + module_put(luo_file->fh->ops->owner); + list_del(&luo_file->list); file_set->count--; @@ -677,7 +678,6 @@ static void luo_file_finish_one(struct luo_file_set *file_set, luo_file->fh->ops->finish(&args); luo_flb_file_finish(luo_file->fh); - module_put(luo_file->fh->ops->owner); } /** @@ -738,6 +738,7 @@ int luo_file_finish(struct luo_file_set *file_set) luo_get_id(luo_file->fh, luo_file->file)); fput(luo_file->file); } + module_put(luo_file->fh->ops->owner); list_del(&luo_file->list); file_set->count--; mutex_destroy(&luo_file->mutex); From 2935777b418d2bfcbfe96705bb2c0fa6c0d94e18 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 27 May 2026 20:27:37 +0000 Subject: [PATCH 2448/5214] liveupdate: Remove unused ser field from struct luo_session The ser field in struct luo_session was intended to point to the serialized data for a session, but it was never actually utilized in the implementation. All serialization and deserialization logic consistently uses the pointers maintained in struct luo_session_header. Remove the dead field to clean up the structure. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260527202737.1345192-6-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_internal.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index 875844d7a41d..dd53d4a7277e 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -59,7 +59,6 @@ struct luo_file_set { * struct luo_session - Represents an active or incoming Live Update session. * @name: A unique name for this session, used for identification and * retrieval. - * @ser: Pointer to the serialized data for this session. * @list: A list_head member used to link this session into a global list * of either outgoing (to be preserved) or incoming (restored from * previous kernel) sessions. @@ -70,7 +69,6 @@ struct luo_file_set { */ struct luo_session { char name[LIVEUPDATE_SESSION_NAME_LENGTH]; - struct luo_session_ser *ser; struct list_head list; bool retrieved; struct luo_file_set file_set; From c70faabb6ddd06327ae778bf4f9b5b977749b315 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 10 May 2026 20:21:44 -0700 Subject: [PATCH 2449/5214] RISC-V: KVM: Use flexible array for APLIC IRQ state Store the per-source APLIC IRQ state in the APLIC allocation instead of allocating it separately. This ties the IRQ state lifetime directly to the APLIC state, removes a separate allocation failure path, and lets __counted_by() describe the array bounds. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260511032144.361520-1-rosenp@gmail.com Signed-off-by: Anup Patel --- arch/riscv/kvm/aia_aplic.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/arch/riscv/kvm/aia_aplic.c b/arch/riscv/kvm/aia_aplic.c index 3464f3351df7..748107c347d9 100644 --- a/arch/riscv/kvm/aia_aplic.c +++ b/arch/riscv/kvm/aia_aplic.c @@ -35,7 +35,7 @@ struct aplic { u32 nr_irqs; u32 nr_words; - struct aplic_irq *irqs; + struct aplic_irq irqs[] __counted_by(nr_irqs); }; static u32 aplic_read_sourcecfg(struct aplic *aplic, u32 irq) @@ -581,7 +581,7 @@ int kvm_riscv_aia_aplic_init(struct kvm *kvm) return 0; /* Allocate APLIC global state */ - aplic = kzalloc_obj(*aplic); + aplic = kzalloc_flex(*aplic, irqs, kvm->arch.aia.nr_sources + 1); if (!aplic) return -ENOMEM; kvm->arch.aia.aplic_state = aplic; @@ -589,11 +589,6 @@ int kvm_riscv_aia_aplic_init(struct kvm *kvm) /* Setup APLIC IRQs */ aplic->nr_irqs = kvm->arch.aia.nr_sources + 1; aplic->nr_words = DIV_ROUND_UP(aplic->nr_irqs, 32); - aplic->irqs = kzalloc_objs(*aplic->irqs, aplic->nr_irqs); - if (!aplic->irqs) { - ret = -ENOMEM; - goto fail_free_aplic; - } for (i = 0; i < aplic->nr_irqs; i++) raw_spin_lock_init(&aplic->irqs[i].lock); @@ -606,7 +601,7 @@ int kvm_riscv_aia_aplic_init(struct kvm *kvm) &aplic->iodev); mutex_unlock(&kvm->slots_lock); if (ret) - goto fail_free_aplic_irqs; + goto fail_free_aplic; /* Setup default IRQ routing */ ret = kvm_riscv_setup_default_irq_routing(kvm, aplic->nr_irqs); @@ -619,8 +614,6 @@ int kvm_riscv_aia_aplic_init(struct kvm *kvm) mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &aplic->iodev); mutex_unlock(&kvm->slots_lock); -fail_free_aplic_irqs: - kfree(aplic->irqs); fail_free_aplic: kvm->arch.aia.aplic_state = NULL; kfree(aplic); @@ -638,8 +631,6 @@ void kvm_riscv_aia_aplic_cleanup(struct kvm *kvm) kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &aplic->iodev); mutex_unlock(&kvm->slots_lock); - kfree(aplic->irqs); - kvm->arch.aia.aplic_state = NULL; kfree(aplic); } From 278b9d0fce2f462e8870d0effed0dc83c3cc7314 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 31 May 2026 18:56:35 +0200 Subject: [PATCH 2450/5214] dt-bindings: iio: light: veml6030: add veml3328 The Vishay VEML3328 is an RGBCIR light sensor that shares similar devicetree properties as other existing VEMLxxxx sensors in the kernel. Acked-by: Krzysztof Kozlowski Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/vishay,veml6030.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml index 4ea69f1fdd63..0041e1db6838 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/light/vishay,veml6030.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: VEML3235, VEML6030, VEML6035 and VEML7700 Ambient Light Sensors (ALS) +title: VEML3235, VEML3328, VEML6030, VEML6035 and VEML7700 Ambient Light Sensors (ALS) maintainers: - Rishi Gupta @@ -21,6 +21,7 @@ description: | Specifications about the sensors can be found at: https://www.vishay.com/docs/80131/veml3235.pdf + https://www.vishay.com/docs/84968/veml3328.pdf https://www.vishay.com/docs/84366/veml6030.pdf https://www.vishay.com/docs/84889/veml6035.pdf https://www.vishay.com/docs/84286/veml7700.pdf @@ -29,6 +30,7 @@ properties: compatible: enum: - vishay,veml3235 + - vishay,veml3328 - vishay,veml6030 - vishay,veml6035 - vishay,veml7700 @@ -79,6 +81,7 @@ allOf: compatible: enum: - vishay,veml3235 + - vishay,veml3328 - vishay,veml7700 then: properties: From 168479c9bf07ee28c372c48eebbf66f2d01911dd Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 1 Jun 2026 14:28:54 +0800 Subject: [PATCH 2451/5214] thunderbolt: test: Release third DP tunnel tb_test_tunnel_3dp() allocates three DisplayPort tunnels but only releases the first two before returning. Release the third tunnel as well to keep the test cleanup balanced. Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg --- drivers/thunderbolt/test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 463186a1abf9..05652ee82fbf 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -1661,6 +1661,7 @@ static void tb_test_tunnel_3dp(struct kunit *test) KUNIT_ASSERT_EQ(test, tunnel3->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel3->paths[0]->path_length, 3); + tb_tunnel_put(tunnel3); tb_tunnel_put(tunnel2); tb_tunnel_put(tunnel1); } From 8d4b989d9c9afe5f185aa5853b666fc4617afe9e Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Wed, 27 May 2026 19:16:22 -0700 Subject: [PATCH 2452/5214] nvdimm/btt: Handle preemption in BTT lane acquisition BTT lanes serialize access to per-lane metadata and workspace state during BTT I/O. The btt-check unit test reports data mismatches during BTT writes due to a race in lane acquisition that can lead to silent data corruption. The existing lane model uses a spinlock together with a per-CPU recursion count. That recursion model stopped being valid after BTT lanes became preemptible: another task can run on the same CPU, observe a non-zero recursion count, bypass locking, and use the same lane concurrently. BTT lanes are also held across arena_write_bytes() calls. That path reaches nsio_rw_bytes(), which flushes writes with nvdimm_flush(). Some provider flush callbacks can sleep, making a spinlock the wrong primitive for the lane lifetime. Replace the spinlock-based recursion model with a dynamically allocated per-lane mutex array and take the lane lock unconditionally. Add might_sleep() to catch any future atomic-context caller. Found with the ndctl unit test btt-check.sh. Fixes: 36c75ce3bd29 ("nd_btt: Make BTT lanes preemptible") Assisted-by: Claude-Sonnet:4.5 Tested-by: Aboorva Devarajan Reviewed-by: Aboorva Devarajan Reviewed-by: Vishal Verma Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260528021625.618462-1-alison.schofield@intel.com Signed-off-by: Alison Schofield --- Documentation/driver-api/nvdimm/btt.rst | 5 +- drivers/nvdimm/nd.h | 11 ++--- drivers/nvdimm/region_devs.c | 66 +++++++++---------------- 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/Documentation/driver-api/nvdimm/btt.rst b/Documentation/driver-api/nvdimm/btt.rst index 2d8269f834bd..d29fab95f149 100644 --- a/Documentation/driver-api/nvdimm/btt.rst +++ b/Documentation/driver-api/nvdimm/btt.rst @@ -161,9 +161,8 @@ process:: nlanes = min(nfree, num_cpus) A lane number is obtained at the start of any IO, and is used for indexing into -all the on-disk and in-memory data structures for the duration of the IO. If -there are more CPUs than the max number of available lanes, than lanes are -protected by spinlocks. +all the on-disk and in-memory data structures for the duration of the IO. Lanes +are protected by mutexes. d. In-memory data structure: Read Tracking Table (RTT) diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index b199eea3260e..197e5368c0a4 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -365,11 +365,6 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd); for (res = (ndd)->dpa.child, next = res ? res->sibling : NULL; \ res; res = next, next = next ? next->sibling : NULL) -struct nd_percpu_lane { - int count; - spinlock_t lock; -}; - enum nd_label_flags { ND_LABEL_REAP, }; @@ -400,6 +395,10 @@ struct nd_mapping { struct nvdimm_drvdata *ndd; }; +struct nd_lane { + struct mutex lock; /* serialize lane access */ +} ____cacheline_aligned_in_smp; + struct nd_region { struct device dev; struct ida ns_ida; @@ -420,7 +419,7 @@ struct nd_region { struct kernfs_node *bb_state; struct badblocks bb; struct nd_interleave_set *nd_set; - struct nd_percpu_lane __percpu *lane; + struct nd_lane *lane; int (*flush)(struct nd_region *nd_region, struct bio *bio); struct nd_mapping mapping[] __counted_by(ndr_mappings); }; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e35c2e18518f..5e079d61cbaa 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -192,7 +192,9 @@ static void nd_region_release(struct device *dev) put_device(&nvdimm->dev); } - free_percpu(nd_region->lane); + for (i = 0; i < nd_region->num_lanes; i++) + mutex_destroy(&nd_region->lane[i].lock); + kfree(nd_region->lane); if (!test_bit(ND_REGION_CXL, &nd_region->flags)) memregion_free(nd_region->id); kfree(nd_region); @@ -904,52 +906,30 @@ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) * nd_region_acquire_lane - allocate and lock a lane * @nd_region: region id and number of lanes possible * - * A lane correlates to a BLK-data-window and/or a log slot in the BTT. - * We optimize for the common case where there are 256 lanes, one - * per-cpu. For larger systems we need to lock to share lanes. For now - * this implementation assumes the cost of maintaining an allocator for - * free lanes is on the order of the lock hold time, so it implements a - * static lane = cpu % num_lanes mapping. + * A lane correlates to a log slot in the BTT. Lanes are shared across + * CPUs using a static lane = cpu % num_lanes mapping, with a per-lane + * mutex to serialize access. * - * In the case of a BTT instance on top of a BLK namespace a lane may be - * acquired recursively. We lock on the first instance. - * - * In the case of a BTT instance on top of PMEM, we only acquire a lane - * for the BTT metadata updates. + * Callers must be in sleepable context. The only in-tree caller is + * BTT's ->submit_bio handler (btt_read_pg / btt_write_pg). */ unsigned int nd_region_acquire_lane(struct nd_region *nd_region) + __acquires(&nd_region->lane[lane].lock) { - unsigned int cpu, lane; + unsigned int lane; - migrate_disable(); - cpu = smp_processor_id(); - if (nd_region->num_lanes < nr_cpu_ids) { - struct nd_percpu_lane *ndl_lock, *ndl_count; - - lane = cpu % nd_region->num_lanes; - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (ndl_count->count++ == 0) - spin_lock(&ndl_lock->lock); - } else - lane = cpu; + might_sleep(); + lane = raw_smp_processor_id() % nd_region->num_lanes; + mutex_lock(&nd_region->lane[lane].lock); return lane; } EXPORT_SYMBOL(nd_region_acquire_lane); void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane) + __releases(&nd_region->lane[lane].lock) { - if (nd_region->num_lanes < nr_cpu_ids) { - unsigned int cpu = smp_processor_id(); - struct nd_percpu_lane *ndl_lock, *ndl_count; - - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (--ndl_count->count == 0) - spin_unlock(&ndl_lock->lock); - } - migrate_enable(); + mutex_unlock(&nd_region->lane[lane].lock); } EXPORT_SYMBOL(nd_region_release_lane); @@ -1019,17 +999,16 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, goto err_id; } - nd_region->lane = alloc_percpu(struct nd_percpu_lane); + nd_region->num_lanes = ndr_desc->num_lanes; + if (!nd_region->num_lanes) + goto err_percpu; + nd_region->lane = kcalloc(nd_region->num_lanes, + sizeof(*nd_region->lane), GFP_KERNEL); if (!nd_region->lane) goto err_percpu; - for (i = 0; i < nr_cpu_ids; i++) { - struct nd_percpu_lane *ndl; - - ndl = per_cpu_ptr(nd_region->lane, i); - spin_lock_init(&ndl->lock); - ndl->count = 0; - } + for (i = 0; i < nd_region->num_lanes; i++) + mutex_init(&nd_region->lane[i].lock); for (i = 0; i < ndr_desc->num_mappings; i++) { struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; @@ -1046,7 +1025,6 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, } nd_region->provider_data = ndr_desc->provider_data; nd_region->nd_set = ndr_desc->nd_set; - nd_region->num_lanes = ndr_desc->num_lanes; nd_region->flags = ndr_desc->flags; nd_region->ro = ro; nd_region->numa_node = ndr_desc->numa_node; From 75a0e1e0b86078687c3c6a05107a98c7e59a65a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 12:18:16 +0200 Subject: [PATCH 2453/5214] power: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly set the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignments. While touching this array unify spacing, use a named initializer for .name and drop trailing commas after the list terminators. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/ba3589f74a12d86fb02ecb9fa2e89532188c22a0.1780048925.git.u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/reset/qemu-virt-ctrl.c | 2 +- drivers/power/supply/charger-manager.c | 4 ++-- drivers/power/supply/cros_charge-control.c | 4 ++-- drivers/power/supply/cros_peripheral_charger.c | 4 ++-- drivers/power/supply/cros_usbpd-charger.c | 4 ++-- drivers/power/supply/max77693_charger.c | 2 +- drivers/power/supply/max8997_charger.c | 2 +- drivers/power/supply/mt6360_charger.c | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/power/reset/qemu-virt-ctrl.c b/drivers/power/reset/qemu-virt-ctrl.c index 01409dfe2265..aa8355270b2c 100644 --- a/drivers/power/reset/qemu-virt-ctrl.c +++ b/drivers/power/reset/qemu-virt-ctrl.c @@ -103,7 +103,7 @@ static int qemu_virt_ctrl_probe(struct platform_device *pdev) } static const struct platform_device_id qemu_virt_ctrl_id[] = { - { "qemu-virt-ctrl", 0 }, + { .name = "qemu-virt-ctrl" }, { } }; MODULE_DEVICE_TABLE(platform, qemu_virt_ctrl_id); diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index c49e0e4d02f7..5dc9cd37d697 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1653,8 +1653,8 @@ static void charger_manager_remove(struct platform_device *pdev) } static const struct platform_device_id charger_manager_id[] = { - { "charger-manager", 0 }, - { }, + { .name = "charger-manager" }, + { } }; MODULE_DEVICE_TABLE(platform, charger_manager_id); diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index 53e6a77e03fc..b489325be816 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -320,8 +320,8 @@ static int cros_chctl_probe(struct platform_device *pdev) } static const struct platform_device_id cros_chctl_id[] = { - { "cros-charge-control", 0 }, - {} + { .name = "cros-charge-control" }, + { } }; static struct platform_driver cros_chctl_driver = { diff --git a/drivers/power/supply/cros_peripheral_charger.c b/drivers/power/supply/cros_peripheral_charger.c index f132fad288cb..9f67a6dbd94e 100644 --- a/drivers/power/supply/cros_peripheral_charger.c +++ b/drivers/power/supply/cros_peripheral_charger.c @@ -369,8 +369,8 @@ static int __maybe_unused cros_pchg_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cros_pchg_pm_ops, NULL, cros_pchg_resume); static const struct platform_device_id cros_pchg_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(platform, cros_pchg_id); diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c index 7d3e676a951c..308e1d4e6dd8 100644 --- a/drivers/power/supply/cros_usbpd-charger.c +++ b/drivers/power/supply/cros_usbpd-charger.c @@ -707,8 +707,8 @@ static SIMPLE_DEV_PM_OPS(cros_usbpd_charger_pm_ops, NULL, cros_usbpd_charger_resume); static const struct platform_device_id cros_usbpd_charger_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(platform, cros_usbpd_charger_id); diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c index 027d6a539b65..76c58b32a374 100644 --- a/drivers/power/supply/max77693_charger.c +++ b/drivers/power/supply/max77693_charger.c @@ -788,7 +788,7 @@ static void max77693_charger_remove(struct platform_device *pdev) } static const struct platform_device_id max77693_charger_id[] = { - { "max77693-charger", 0, }, + { .name = "max77693-charger" }, { } }; MODULE_DEVICE_TABLE(platform, max77693_charger_id); diff --git a/drivers/power/supply/max8997_charger.c b/drivers/power/supply/max8997_charger.c index 1ec3535a257d..19da9eff0372 100644 --- a/drivers/power/supply/max8997_charger.c +++ b/drivers/power/supply/max8997_charger.c @@ -268,7 +268,7 @@ static int max8997_battery_probe(struct platform_device *pdev) } static const struct platform_device_id max8997_battery_id[] = { - { "max8997-battery", 0 }, + { .name = "max8997-battery" }, { } }; MODULE_DEVICE_TABLE(platform, max8997_battery_id); diff --git a/drivers/power/supply/mt6360_charger.c b/drivers/power/supply/mt6360_charger.c index 77747eb51667..69955f255091 100644 --- a/drivers/power/supply/mt6360_charger.c +++ b/drivers/power/supply/mt6360_charger.c @@ -841,8 +841,8 @@ static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = { MODULE_DEVICE_TABLE(of, mt6360_charger_of_id); static const struct platform_device_id mt6360_charger_id[] = { - { "mt6360-chg", 0 }, - {}, + { .name = "mt6360-chg" }, + { } }; MODULE_DEVICE_TABLE(platform, mt6360_charger_id); From 37258ad1f3a52ea442c32b3c92ad7146e74050c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 12:18:17 +0200 Subject: [PATCH 2454/5214] power: supply: max14577: Drop driver data in of and platform device id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These values are not used, the relevant distinction happens in the mfd parent driver. So they can be dropped. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/62e1b01a6591dd59406a78f2bbca619d734a9150.1780048925.git.u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max14577_charger.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c index 63077d38ea30..a15d94942184 100644 --- a/drivers/power/supply/max14577_charger.c +++ b/drivers/power/supply/max14577_charger.c @@ -613,18 +613,16 @@ static void max14577_charger_remove(struct platform_device *pdev) } static const struct platform_device_id max14577_charger_id[] = { - { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, }, - { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, }, + { .name = "max14577-charger" }, + { .name = "max77836-charger" }, { } }; MODULE_DEVICE_TABLE(platform, max14577_charger_id); static const struct of_device_id of_max14577_charger_dt_match[] = { - { .compatible = "maxim,max14577-charger", - .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, }, - { .compatible = "maxim,max77836-charger", - .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, }, - { }, + { .compatible = "maxim,max14577-charger" }, + { .compatible = "maxim,max77836-charger" }, + { } }; MODULE_DEVICE_TABLE(of, of_max14577_charger_dt_match); From e28f7498dd819878b8acacb89c4c073a646feea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 12:18:19 +0200 Subject: [PATCH 2455/5214] power: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Matti Vaittinen Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/1ceacf4f9c3f827bcad85b378aa04cdca1c04635.1780048925.git.u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/reset/spacemit-p1-reboot.c | 4 ++-- drivers/power/reset/tps65086-restart.c | 2 +- drivers/power/supply/bd71828-power.c | 8 ++++---- drivers/power/supply/macsmc-power.c | 2 +- drivers/power/supply/max77759_charger.c | 2 +- drivers/power/supply/max8998_charger.c | 2 +- drivers/power/supply/pf1550-charger.c | 2 +- drivers/power/supply/rt5033_charger.c | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/power/reset/spacemit-p1-reboot.c b/drivers/power/reset/spacemit-p1-reboot.c index 9ec3d1fff8f3..84026b042ea2 100644 --- a/drivers/power/reset/spacemit-p1-reboot.c +++ b/drivers/power/reset/spacemit-p1-reboot.c @@ -70,8 +70,8 @@ static int spacemit_p1_reboot_probe(struct platform_device *pdev) } static const struct platform_device_id spacemit_p1_reboot_id_table[] = { - { "spacemit-p1-reboot", }, - { /* sentinel */ }, + { .name = "spacemit-p1-reboot" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, spacemit_p1_reboot_id_table); diff --git a/drivers/power/reset/tps65086-restart.c b/drivers/power/reset/tps65086-restart.c index 6976dbcac74f..37d248a9df17 100644 --- a/drivers/power/reset/tps65086-restart.c +++ b/drivers/power/reset/tps65086-restart.c @@ -41,7 +41,7 @@ static int tps65086_restart_probe(struct platform_device *pdev) } static const struct platform_device_id tps65086_restart_id_table[] = { - { "tps65086-reset", }, + { .name = "tps65086-reset" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table); diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c index 5e78faa0a4aa..5ab514ff5c65 100644 --- a/drivers/power/supply/bd71828-power.c +++ b/drivers/power/supply/bd71828-power.c @@ -1184,10 +1184,10 @@ static int bd71828_power_probe(struct platform_device *pdev) } static const struct platform_device_id bd71828_charger_id[] = { - { "bd71815-power", ROHM_CHIP_TYPE_BD71815 }, - { "bd71828-power", ROHM_CHIP_TYPE_BD71828 }, - { "bd72720-power", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71815-power", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd71828-power", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd72720-power", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd71828_charger_id); diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c index 33ca07460f3a..ced07f71e0a8 100644 --- a/drivers/power/supply/macsmc-power.c +++ b/drivers/power/supply/macsmc-power.c @@ -834,7 +834,7 @@ static void macsmc_power_remove(struct platform_device *pdev) } static const struct platform_device_id macsmc_power_id[] = { - { "macsmc-power" }, + { .name = "macsmc-power" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, macsmc_power_id); diff --git a/drivers/power/supply/max77759_charger.c b/drivers/power/supply/max77759_charger.c index 9bb414599f16..c606d7bafcb8 100644 --- a/drivers/power/supply/max77759_charger.c +++ b/drivers/power/supply/max77759_charger.c @@ -754,7 +754,7 @@ static int max77759_charger_probe(struct platform_device *pdev) } static const struct platform_device_id max77759_charger_id[] = { - { .name = "max77759-charger", }, + { .name = "max77759-charger" }, { } }; MODULE_DEVICE_TABLE(platform, max77759_charger_id); diff --git a/drivers/power/supply/max8998_charger.c b/drivers/power/supply/max8998_charger.c index 418b882b163d..b0eda2b51e7f 100644 --- a/drivers/power/supply/max8998_charger.c +++ b/drivers/power/supply/max8998_charger.c @@ -188,7 +188,7 @@ static int max8998_battery_probe(struct platform_device *pdev) } static const struct platform_device_id max8998_battery_id[] = { - { "max8998-battery", TYPE_MAX8998 }, + { .name = "max8998-battery" }, { } }; MODULE_DEVICE_TABLE(platform, max8998_battery_id); diff --git a/drivers/power/supply/pf1550-charger.c b/drivers/power/supply/pf1550-charger.c index a457862ef461..41036f4cb64a 100644 --- a/drivers/power/supply/pf1550-charger.c +++ b/drivers/power/supply/pf1550-charger.c @@ -622,7 +622,7 @@ static int pf1550_charger_probe(struct platform_device *pdev) } static const struct platform_device_id pf1550_charger_id[] = { - { "pf1550-charger", }, + { .name = "pf1550-charger" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, pf1550_charger_id); diff --git a/drivers/power/supply/rt5033_charger.c b/drivers/power/supply/rt5033_charger.c index de724f23e453..536ab29b657d 100644 --- a/drivers/power/supply/rt5033_charger.c +++ b/drivers/power/supply/rt5033_charger.c @@ -727,7 +727,7 @@ static int rt5033_charger_probe(struct platform_device *pdev) } static const struct platform_device_id rt5033_charger_id[] = { - { "rt5033-charger", }, + { .name = "rt5033-charger" }, { } }; MODULE_DEVICE_TABLE(platform, rt5033_charger_id); From eb7ed650e5960fc303130704d1e29d18a7d0e1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 12:18:20 +0200 Subject: [PATCH 2456/5214] power: supply: mt6360_charger: Use of match table unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mt6360_charger_of_id is defined unconditionally, so it doesn't make sense to not use it for the driver's .of_match_table member. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/ff94de5fb3ee6aeb1c0256e1a00c1c5ac350b430.1780048925.git.u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/mt6360_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/mt6360_charger.c b/drivers/power/supply/mt6360_charger.c index 69955f255091..d3b0731f6562 100644 --- a/drivers/power/supply/mt6360_charger.c +++ b/drivers/power/supply/mt6360_charger.c @@ -849,7 +849,7 @@ MODULE_DEVICE_TABLE(platform, mt6360_charger_id); static struct platform_driver mt6360_charger_driver = { .driver = { .name = "mt6360-chg", - .of_match_table = of_match_ptr(mt6360_charger_of_id), + .of_match_table = mt6360_charger_of_id, }, .probe = mt6360_charger_probe, .id_table = mt6360_charger_id, From e3f669bed32287ae72c05a4ddab4a6687b0e62ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 29 May 2026 12:18:21 +0200 Subject: [PATCH 2457/5214] power: Unify code style for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a single space in the list terminator and remove the trailing comma. Signed-off-by: Uwe Kleine-König (The Capable Hub) Acked-by: Chen-Yu Tsai Link: https://patch.msgid.link/d840a6f83e3736510d7d859bf48c9bce04876b0c.1780048925.git.u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 2 +- drivers/power/supply/axp288_fuel_gauge.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index ea0f5caee8f0..24a8b1ee2fec 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -955,7 +955,7 @@ static int axp288_charger_probe(struct platform_device *pdev) static const struct platform_device_id axp288_charger_id_table[] = { { .name = "axp288_charger" }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, axp288_charger_id_table); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index a3d71fc72064..5af334c0a980 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -799,7 +799,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) static const struct platform_device_id axp288_fg_id_table[] = { { .name = DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(platform, axp288_fg_id_table); From b6c7feeb604262f31a4279bd66aa300ad0903255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Pfl=C3=BCger?= Date: Thu, 28 May 2026 18:18:24 +0200 Subject: [PATCH 2458/5214] power: reset: sc27xx: Add platform_device_id table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the poweroff driver for SC27xx-series PMICs probe automatically. Since the device representing the poweroff functionality of the SC27xx PMIC is not supposed to have a dedicated device tree node without any corresponding DT resources [1], an of_device_id table cannot be used here. Instead, use a platform_device_id table to match the poweroff sub-device instantiated by the parent MFD driver. [1]: https://lore.kernel.org/all/20251002025344.GA2958334-robh@kernel.org/ Signed-off-by: Otto Pflüger Link: https://patch.msgid.link/20260528-sc27xx-mfd-cells-v3-2-25cd685d2743@abscue.de Signed-off-by: Sebastian Reichel --- drivers/power/reset/sc27xx-poweroff.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c index 393bd1c33b73..6376706bf561 100644 --- a/drivers/power/reset/sc27xx-poweroff.c +++ b/drivers/power/reset/sc27xx-poweroff.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -70,11 +71,18 @@ static int sc27xx_poweroff_probe(struct platform_device *pdev) return 0; } +static const struct platform_device_id sc27xx_poweroff_id_table[] = { + { "sc2731-poweroff" }, + { } +}; +MODULE_DEVICE_TABLE(platform, sc27xx_poweroff_id_table); + static struct platform_driver sc27xx_poweroff_driver = { .probe = sc27xx_poweroff_probe, .driver = { .name = "sc27xx-poweroff", }, + .id_table = sc27xx_poweroff_id_table, }; module_platform_driver(sc27xx_poweroff_driver); From 13fe4cd9ddd0aacb7777812328be525a11ea3fea Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:12 +0530 Subject: [PATCH 2459/5214] nvdimm/btt: Free arena sub-allocations on discover_arenas() error path Memory allocated by btt_freelist_init(), btt_rtt_init(), and btt_maplocks_init() is not freed on some discover_arenas() error paths. This leaks memory when arena discovery fails. Add the missing kfree() calls to release the allocations before returning an error. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-1-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield --- drivers/nvdimm/btt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index fdcb080a4314..e0b6a85a8124 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -919,6 +919,9 @@ static int discover_arenas(struct btt *btt) return ret; out: + kfree(arena->freelist); + kfree(arena->rtt); + kfree(arena->map_locks); kfree(arena); free_arenas(btt); return ret; From 1a6b6442a982d0ca5fb6a1a39b6f6dfd760eda57 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:13 +0530 Subject: [PATCH 2460/5214] nvdimm/btt: Free arenas on btt_init() error paths The arenas allocated by discover_arenas() or create_arenas() are not freed on some error paths in btt_init(). This leaks memory when BTT initialization fails. Call free_arenas() from the affected error paths to release the allocations. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-2-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield --- drivers/nvdimm/btt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index e0b6a85a8124..7e1112960d7f 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1592,7 +1592,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, if (btt->init_state != INIT_READY && nd_region->ro) { dev_warn(dev, "%s is read-only, unable to init btt metadata\n", dev_name(&nd_region->dev)); - return NULL; + goto err; } else if (btt->init_state != INIT_READY) { btt->num_arenas = (rawsize / ARENA_MAX_SIZE) + ((rawsize % ARENA_MAX_SIZE) ? 1 : 0); @@ -1602,25 +1602,28 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, ret = create_arenas(btt); if (ret) { dev_info(dev, "init: create_arenas: %d\n", ret); - return NULL; + goto err; } ret = btt_meta_init(btt); if (ret) { dev_err(dev, "init: error in meta_init: %d\n", ret); - return NULL; + goto err; } } ret = btt_blk_init(btt); if (ret) { dev_err(dev, "init: error in blk_init: %d\n", ret); - return NULL; + goto err; } btt_debugfs_init(btt); return btt; +err: + free_arenas(btt); + return NULL; } /** From cd1a8d788763bb6c0af3c53fbbd9abb555e18953 Mon Sep 17 00:00:00 2001 From: Tomasz Wolski Date: Thu, 28 May 2026 08:45:46 +0200 Subject: [PATCH 2461/5214] dax/bus: Upgrade resource conflict message to dev_err() in alloc_dax_region() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dax_region resource conflict in alloc_dax_region() indicates a serious configuration problem — two subsystems (e.g. dax_hmem and dax_cxl) are attempting to register overlapping address ranges. This is not a transient or debug-level condition; it represents a genuine resource conflict that an administrator needs to be aware of. Promote the log level from dev_dbg() to dev_err() so that the conflict is visible by default without requiring dynamic debug to be enabled. Suggested-by: Dan Williams Link: https://lore.kernel.org/linux-cxl/69c1a8d1c0fa9_7ee3100a1@dwillia2-mobl4.notmuch/ Signed-off-by: Tomasz Wolski Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260528064546.23362-1-tomasz.wolski@fujitsu.com Signed-off-by: Alison Schofield --- drivers/dax/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index ccfe65004888..b809e1a264af 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -670,7 +670,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, rc = request_resource(&dax_regions, &dax_region->res); if (rc) { - dev_dbg(parent, "dax_region resource conflict for %pR\n", + dev_err(parent, "dax_region resource conflict for %pR\n", &dax_region->res); goto err_res; } From 1e7afc906f2ffb0ef1ee58c44510f8e9e263048e Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Thu, 28 May 2026 14:36:18 -0400 Subject: [PATCH 2462/5214] nvdimm: Use sysfs_emit() for cpumask show callback nvdimm_pmu_cpumask_show() is a sysfs show callback. Use sysfs_emit() and cpumask_pr_args() to emit the mask. This prepares for removing cpumap_print_to_pagebuf(). Signed-off-by: Yury Norov Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260528183625.870813-12-ynorov@nvidia.com Signed-off-by: Alison Schofield --- drivers/nvdimm/nd_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvdimm/nd_perf.c b/drivers/nvdimm/nd_perf.c index e0b51438dc9b..9e497cae65b3 100644 --- a/drivers/nvdimm/nd_perf.c +++ b/drivers/nvdimm/nd_perf.c @@ -123,7 +123,7 @@ static ssize_t nvdimm_pmu_cpumask_show(struct device *dev, nd_pmu = container_of(pmu, struct nvdimm_pmu, pmu); - return cpumap_print_to_pagebuf(true, buf, cpumask_of(nd_pmu->cpu)); + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(nd_pmu->cpu))); } static int nvdimm_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) From e3fc08f4ab66da6226f5e34b87bcbcf35ddebe56 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Wed, 22 Apr 2026 17:10:03 -0700 Subject: [PATCH 2463/5214] MAINTAINERS: Add maintainer info for libnvdimm and DAX Add Alison Schofield to libnvdimm and DAX maintainer. Cc: Alison Schofield Cc: Ira Ira Weiny Cc: Dan Williams Signed-off-by: Dave Jiang Acked-by: Vishal Verma Acked-by: Ira Weiny Link: https://patch.msgid.link/20260423001003.2887295-1-dave.jiang@intel.com Signed-off-by: Alison Schofield --- MAINTAINERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9ec290e38b44..6e65a68d3eeb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7299,6 +7299,7 @@ DEVICE DIRECT ACCESS (DAX) M: Dan Williams M: Vishal Verma M: Dave Jiang +M: Alison Schofield L: nvdimm@lists.linux.dev L: linux-cxl@vger.kernel.org S: Supported @@ -14673,6 +14674,7 @@ LIBNVDIMM BTT: BLOCK TRANSLATION TABLE M: Vishal Verma M: Dan Williams M: Dave Jiang +M: Alison Schofield L: nvdimm@lists.linux.dev S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ @@ -14683,6 +14685,7 @@ LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER M: Dan Williams M: Vishal Verma M: Dave Jiang +M: Alison Schofield L: nvdimm@lists.linux.dev S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ @@ -14702,6 +14705,7 @@ M: Dan Williams M: Vishal Verma M: Dave Jiang M: Ira Weiny +M: Alison Schofield L: nvdimm@lists.linux.dev S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ From a6e0072cfa82f84557961dc13d552de69fff85b1 Mon Sep 17 00:00:00 2001 From: Ira Weiny Date: Mon, 4 May 2026 17:38:10 -0500 Subject: [PATCH 2464/5214] MAINTAINERS: Update address for Ira Weiny Update MAINTAINERS and .mailmap to point to my kernel.org address: iweiny@kernel.org Downgrade from maintainer to reviewer whilst doing so. Signed-off-by: Ira Weiny Acked-by: Jonathan Cameron Acked-by: Dave Jiang Link: https://patch.msgid.link/20260504-change-maintain-file-v1-1-6679b030d3e0@intel.com Signed-off-by: Alison Schofield --- .mailmap | 1 + MAINTAINERS | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index a009f73d7ea5..afdb363752e8 100644 --- a/.mailmap +++ b/.mailmap @@ -448,6 +448,7 @@ Juha Yrjola Juha Yrjola Julien Thierry Justin Iurman +Ira Weiny Iskren Chernev Kalle Valo Kalle Valo diff --git a/MAINTAINERS b/MAINTAINERS index 6e65a68d3eeb..4a49d718123d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4265,7 +4265,7 @@ M: Greg Kroah-Hartman M: "Rafael J. Wysocki" M: Danilo Krummrich R: Dave Ertman -R: Ira Weiny +R: Ira Weiny R: Leon Romanovsky L: driver-core@lists.linux.dev S: Supported @@ -6435,8 +6435,8 @@ M: Jonathan Cameron M: Dave Jiang M: Alison Schofield M: Vishal Verma -M: Ira Weiny M: Dan Williams +R: Ira Weiny L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/driver-api/cxl @@ -14704,8 +14704,8 @@ LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM M: Dan Williams M: Vishal Verma M: Dave Jiang -M: Ira Weiny M: Alison Schofield +R: Ira Weiny L: nvdimm@lists.linux.dev S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ From 86e411b6ec277dbb8ac1f1d855dc337181a62a29 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 18 May 2026 12:43:07 +0200 Subject: [PATCH 2465/5214] MAINTAINERS: nvdimm: Include maintainer profile No dedicated NVDIMM maintainers are returned by get_maintainers.pl for the subsystem maintainer profile, thus patches changing that file miss the actual owners of the file. Signed-off-by: Krzysztof Kozlowski Acked-by: Dave Jiang Link: https://patch.msgid.link/20260518104306.39289-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Alison Schofield --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4a49d718123d..54090d8f7c1d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14711,6 +14711,7 @@ S: Supported Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ P: Documentation/nvdimm/maintainer-entry-profile.rst T: git git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git +F: Documentation/nvdimm/maintainer-entry-profile.rst F: drivers/acpi/nfit/* F: drivers/nvdimm/* F: include/linux/libnvdimm.h From 57db1307afb1f83d45f5ff53b93f8d040100d13e Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:35 +0200 Subject: [PATCH 2466/5214] scsi: smartpqi: Use shost_to_hba() in pqi_scan_finished() shost_to_hba() is used everywhere except to obtain pqi_ctrl_info from shosti, except in pqi_scan_finished(), where shost_priv() is used. This causes one pointer dereference to be missed, as shost->hostdata is a pointer in smartpqi. Fix it. Fixes: 6c223761eb54 ("smartpqi: initial commit of Microsemi smartpqi driver") Signed-off-by: Martin Wilck Reviewed-by: Don Brace Cc: Don Brace Cc: storagedev@microchip.com Cc: stable@vger.kernel.org Reviewed-by: Hannes Reinecke Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-2-mwilck@suse.com Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index b4ed991976d0..65ff50982978 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2642,7 +2642,7 @@ static int pqi_scan_finished(struct Scsi_Host *shost, { struct pqi_ctrl_info *ctrl_info; - ctrl_info = shost_priv(shost); + ctrl_info = shost_to_hba(shost); return !mutex_is_locked(&ctrl_info->scan_mutex); } From 8c292e89bd831c8a13e92f3429ef66bbe0b83677 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:36 +0200 Subject: [PATCH 2467/5214] scsi: Revert "scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans" This reverts commit 37c4e72b0651e7697eb338cd1fb09feef472cc1a. Said commit causes excessive resource usage and even system freeze with some controllers, e.g. smartpqi and hisi_sas. The justification provided by the patch authors [1] was supporting a special mode of the mpi3mr and mpt3sas, so-called "Tri-mode", in which NVMe drives are exposed as SCSI devices on a separate channel. While that's useful for these drivers, it seems wrong to cause major breakage for other drivers for the sake of this feature. [1] https://lore.kernel.org/linux-scsi/CAFdVvOwjy+2ORJ6uJkspiLTPF05481U7gcS4QohFOFGPqAs8ig@mail.gmail.com/ Fixes: 37c4e72b0651 ("scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans") Signed-off-by: Martin Wilck Cc: Don Brace Cc: storagedev@microchip.com Cc: Ranjan Kumar Cc: Sathya Prakash Veerichetty Cc: Kashyap Desai Cc: Sumit Saxena Cc: mpi3mr-linuxdrv.pdl@broadcom.com Cc: MPT-FusionLinux.pdl@broadcom.com Cc: Yihang Li Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-3-mwilck@suse.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 2 +- drivers/scsi/scsi_transport_sas.c | 60 +++++++------------------------ 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 7e60e3a4bca6..e27da038603a 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1921,7 +1921,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return 0; } -EXPORT_SYMBOL(scsi_scan_host_selected); + static void scsi_sysfs_add_devices(struct Scsi_Host *shost) { struct scsi_device *sdev; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 13412702188e..d8f2377b017f 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -40,8 +40,6 @@ #include #include "scsi_sas_internal.h" -#include "scsi_priv.h" - struct sas_host_attrs { struct list_head rphy_list; struct mutex lock; @@ -1685,22 +1683,6 @@ int scsi_is_sas_rphy(const struct device *dev) } EXPORT_SYMBOL(scsi_is_sas_rphy); -static void scan_channel_zero(struct Scsi_Host *shost, uint id, u64 lun) -{ - struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct sas_rphy *rphy; - - list_for_each_entry(rphy, &sas_host->rphy_list, list) { - if (rphy->identify.device_type != SAS_END_DEVICE || - rphy->scsi_target_id == -1) - continue; - - if (id == SCAN_WILD_CARD || id == rphy->scsi_target_id) { - scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, - lun, SCSI_SCAN_MANUAL); - } - } -} /* * SCSI scan helper @@ -1710,41 +1692,23 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun) { struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - int res = 0; - int i; + struct sas_rphy *rphy; - switch (channel) { - case 0: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); - break; + mutex_lock(&sas_host->lock); + list_for_each_entry(rphy, &sas_host->rphy_list, list) { + if (rphy->identify.device_type != SAS_END_DEVICE || + rphy->scsi_target_id == -1) + continue; - case SCAN_WILD_CARD: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); - - for (i = 1; i <= shost->max_channel; i++) { - res = scsi_scan_host_selected(shost, i, id, lun, - SCSI_SCAN_MANUAL); - if (res) - goto exit_scan; + if ((channel == SCAN_WILD_CARD || channel == 0) && + (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) { + scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, + lun, SCSI_SCAN_MANUAL); } - break; - - default: - if (channel <= shost->max_channel) { - res = scsi_scan_host_selected(shost, channel, id, lun, - SCSI_SCAN_MANUAL); - } else { - res = -EINVAL; - } - break; } + mutex_unlock(&sas_host->lock); -exit_scan: - return res; + return 0; } From be8fcd4a8217a916344c88a4b1b84f5736dda17e Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Tue, 19 May 2026 16:52:33 +0300 Subject: [PATCH 2468/5214] scsi: sas: Skip opt_sectors when DMA reports no real optimization hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sas_host_setup() unconditionally sets shost->opt_sectors from dma_opt_mapping_size(). When the IOMMU is disabled or in passthrough mode and no DMA ops provide an opt_mapping_size callback, dma_opt_mapping_size() returns min(dma_max_mapping_size(), SIZE_MAX) which equals dma_max_mapping_size() — a hard upper bound, not an optimization hint. On a Dell PowerEdge R750 with mpt3sas (Broadcom SAS3816, FW 33.15.00.00) and intel_iommu=off the following values are observed: dma_opt_mapping_size() = dma_max_mapping_size() (no real hint) shost->max_sectors = 32767 opt_sectors = min(32767, huge >> 9) = 32767 optimal_io_size = 32767 << 9 = 16776704 → round_down(16776704, 4096) = 16773120 The SAS disk (SAMSUNG MZILT800HBHQ0D3) does not report an Optimal Transfer Length in VPD page B0, so sdkp->opt_xfer_blocks remains 0. sd_revalidate_disk() then uses min_not_zero(0, opt_sectors) = opt_sectors, propagating the bogus value into the block device's optimal_io_size (visible as OPT-IO = 16773120 in lsblk --topology). mkfs.xfs picks up optimal_io_size and minimum_io_size and computes: swidth = 16773120 / 4096 = 4095 sunit = 8192 / 4096 = 2 Since 4095 % 2 != 0, XFS rejects the geometry: SB stripe unit sanity check failed This makes it impossible to create XFS filesystems (e.g. for /var/lib/docker) during system bootstrap. Fix this by introducing a sas_dma_setup_opt_sectors() helper that sets opt_sectors only when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), indicating a genuine DMA optimization constraint. The helper computes min(opt_sectors, max_sectors) first, then rounds down to a power of two so that filesystem geometry calculations always produce clean results. When the two DMA values are equal, no backend provided a real hint, so opt_sectors stays at 0 ("no preference"). [mkp: implemented hch's suggestion] Fixes: 4cbfca5f7750 ("scsi: scsi_transport_sas: cap shost opt_sectors according to DMA optimal limit") Cc: stable@vger.kernel.org Reviewed-by: John Garry Signed-off-by: Ionut Nechita Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260519135238.373784-2-ionut.nechita@windriver.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_sas.c | 41 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index d8f2377b017f..d689b9ed08a6 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -220,12 +221,45 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) * SAS host attributes */ +/* + * Set shost->opt_sectors from the DMA optimal mapping size, but only + * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), + * indicating a genuine optimization hint from an IOMMU or DMA backend. + * When the two are equal (e.g. IOMMU disabled / passthrough), no real + * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size + * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment). + */ +static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost) +{ + struct device *dma_dev = shost->dma_dev; + size_t opt = dma_opt_mapping_size(dma_dev); + size_t max = dma_max_mapping_size(dma_dev); + unsigned int opt_sectors; + + /* opt >= max means no real hint was provided by the DMA layer */ + if (opt >= max) + return; + + /* Clamp to max_sectors to avoid overflow in sector arithmetic */ + opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT, + shost->max_sectors); + + /* Guard against zero before rounddown_pow_of_two() */ + if (!opt_sectors) + return; + + /* + * Round down to power-of-two so filesystem geometry calculations + * (e.g. XFS stripe width/unit) always produce clean divisors. + */ + shost->opt_sectors = rounddown_pow_of_two(opt_sectors); +} + static int sas_host_setup(struct transport_container *tc, struct device *dev, struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct device *dma_dev = shost->dma_dev; INIT_LIST_HEAD(&sas_host->rphy_list); mutex_init(&sas_host->lock); @@ -237,10 +271,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n", shost->host_no); - if (dma_dev->dma_mask) { - shost->opt_sectors = min_t(unsigned int, shost->max_sectors, - dma_opt_mapping_size(dma_dev) >> SECTOR_SHIFT); - } + sas_dma_setup_opt_sectors(shost); return 0; } From 06a34d9c1f47b923bb554de25406a5251b047379 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Wed, 20 May 2026 16:00:09 +0900 Subject: [PATCH 2469/5214] scsi: ufs: core: Skip link param validation when lanes_per_direction is unset ufshcd_validate_link_params(), added by commit e72323f3b09f ("scsi: ufs: core: Configure only active lanes during link"), is called unconditionally from ufshcd_link_startup() and fails link startup with -ENOLINK when the connected lane count read from the device differs from hba->lanes_per_direction. lanes_per_direction is only set by ufshcd-pltfrm (default 2, or the "lanes-per-direction" devicetree property); ufshcd-pci controllers (e.g. Intel) leave it 0. As the device always reports >= 1 connected lanes, the check can never match and link startup always fails. Reproduced with QEMU's UFS device. Skip the check when lanes_per_direction is unset: with no expected value to validate against, restore the behaviour from before that commit. Fixes: e72323f3b09f ("scsi: ufs: core: Configure only active lanes during link") Signed-off-by: Daejun Park Reviewed-by: Bart Van Assche Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260520070009epcms2p6542f3abb7660839e9d8140b3f2f145c3@epcms2p6 Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index c1a0a79e386d..1061e20786fa 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -5200,6 +5200,16 @@ static int ufshcd_validate_link_params(struct ufs_hba *hba) { int ret, val; + /* + * lanes_per_direction is only populated by the platform glue (it + * defaults to 2 or is read from the "lanes-per-direction" devicetree + * property). Controllers probed via ufshcd-pci leave it unset (0), in + * which case there is no expected lane count to validate the connected + * lanes against. Skip the check instead of failing link startup. + */ + if (!hba->lanes_per_direction) + return 0; + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &val); if (ret) From 056fca1f276fa41792838d5eb88e316dd81c982d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 22 May 2026 22:02:41 -0700 Subject: [PATCH 2470/5214] scsi: lpfc: Turn lpfc_queue q_pgs into a flexible array The q_pgs pointer was assigned to point at the trailing memory allocated past the struct. Convert it to a proper C99 flexible array member and use struct_size() for the allocation. Assisted-by: Claude:Opus-4.7 Signed-off-by: Rosen Penev Reviewed-by: Justin Tee Link: https://patch.msgid.link/20260523050241.190239-1-rosenp@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_sli.c | 3 +-- drivers/scsi/lpfc/lpfc_sli4.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d38fb374b379..0e56e7034566 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -15875,7 +15875,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size, if (pgcnt > phba->sli4_hba.pc_sli4_params.wqpcnt) pgcnt = phba->sli4_hba.pc_sli4_params.wqpcnt; - queue = kzalloc_node(sizeof(*queue) + (sizeof(void *) * pgcnt), + queue = kzalloc_node(struct_size(queue, q_pgs, pgcnt), GFP_KERNEL, cpu_to_node(cpu)); if (!queue) return NULL; @@ -15892,7 +15892,6 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size, * resources, the free routine needs to know what was allocated. */ queue->page_count = pgcnt; - queue->q_pgs = (void **)&queue[1]; queue->entry_cnt_per_pg = hw_page_size / entry_size; queue->entry_size = entry_size; queue->entry_count = entry_count; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 2744786d9c94..e2b95fb50d55 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -280,9 +280,10 @@ struct lpfc_queue { uint64_t isr_timestamp; struct lpfc_queue *assoc_qp; struct list_head _poll_list; - void **q_pgs; /* array to index entries per page */ enum lpfc_poll_mode poll_mode; + + void *q_pgs[]; /* array to index entries per page */ }; struct lpfc_sli4_link { From 1b6f03b7ae9ee27054c55bb55a69d05555a78516 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 30 May 2026 22:45:48 +0300 Subject: [PATCH 2471/5214] scsi: pm8001: Fix error code in non_fatal_log_show() The non_fatal_log_show() function is supposed to return negative error codes on failure. But because the error codes are saved in a u32 and then cast to signed long, they end up being high positive values instead of negative. Remove the intermediary u32 variable to fix this bug. Fixes: dba2cc03b9db ("scsi: pm80xx: sysfs attribute for non fatal dump") Signed-off-by: Dan Carpenter Acked-by: Jack Wang Link: https://patch.msgid.link/ahs-bEsBJH0KhnsX@stanley.mountain Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_ctl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index bb38b2d63acb..a27f3287748e 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -588,10 +588,7 @@ static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL); static ssize_t non_fatal_log_show(struct device *cdev, struct device_attribute *attr, char *buf) { - u32 count; - - count = pm80xx_get_non_fatal_dump(cdev, attr, buf); - return count; + return pm80xx_get_non_fatal_dump(cdev, attr, buf); } static DEVICE_ATTR_RO(non_fatal_log); From c39a9a02bc5d841c116dc03c264eb9ceecde806e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 1 Jun 2026 23:02:04 +0200 Subject: [PATCH 2472/5214] scsi: megaraid_mbox: Avoid double kfree() Smatch found a double-free after my recent change: drivers/scsi/megaraid/megaraid_mbox.c:3474 megaraid_cmm_register() error: double free of 'adp' (line 3468) Since the object is no longer allocated in megaraid_cmm_register(), remove the kfree() as well. Fixes: c1f7275b613b ("scsi: megaraid_mbox: Reduce stack usage in megaraid_cmm_register()") Reported-by: Dan Carpenter Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260601210216.846809-1-arnd@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_mm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index 60db48dc8f3a..e572665903d2 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -998,8 +998,6 @@ mraid_mm_register_adp(mraid_mmadp_t *adapter) dma_pool_destroy(adapter->pthru_dma_pool); - kfree(adapter); - return rval; } From 4cf752f6b99ab63506cde5a611d4219e97adbd84 Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Fri, 29 May 2026 10:07:39 +0900 Subject: [PATCH 2473/5214] scsi: ufs: core: Fix NULL pointer dereference in scsi_cmd_priv() calls ufshcd_tag_to_cmd() may return NULL if no command is associated with the given tag. However, several callers dereference the returned cmd pointer via scsi_cmd_priv() without checking for NULL first, leading to a potential NULL pointer dereference. Fix this by adding NULL checks for cmd before calling scsi_cmd_priv() and moving the lrbp initialization after the NULL check. Signed-off-by: Chanwoo Lee Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260529010739.295391-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-mcq.c | 7 ++++++- drivers/ufs/core/ufshcd.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index c1b1d67a1ddc..13b60a2d06db 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -637,7 +637,7 @@ static bool ufshcd_mcq_sqe_search(struct ufs_hba *hba, struct ufs_hw_queue *hwq, int task_tag) { struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, task_tag); - struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd); + struct ufshcd_lrb *lrbp; struct utp_transfer_req_desc *utrd; __le64 cmd_desc_base_addr; bool ret = false; @@ -647,6 +647,11 @@ static bool ufshcd_mcq_sqe_search(struct ufs_hba *hba, if (hba->quirks & UFSHCD_QUIRK_MCQ_BROKEN_RTC) return true; + if (!cmd) + return false; + + lrbp = scsi_cmd_priv(cmd); + mutex_lock(&hwq->sq_mutex); ufshcd_mcq_sq_stop(hba, hwq); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 1061e20786fa..9b6cb6b569bc 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -7903,8 +7903,12 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) for_each_set_bit(tag, &bitmap, hba->nutrs) { struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, tag); - struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd); + struct ufshcd_lrb *lrbp; + if (!cmd) + continue; + + lrbp = scsi_cmd_priv(cmd); lrbp->req_abort_skip = true; } } @@ -7925,11 +7929,16 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) { struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, tag); - struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd); + struct ufshcd_lrb *lrbp; int err; int poll_cnt; u8 resp = 0xF; + if (!cmd) + return -EINVAL; + + lrbp = scsi_cmd_priv(cmd); + for (poll_cnt = 100; poll_cnt; poll_cnt--) { err = ufshcd_issue_tm_cmd(hba, lrbp->lun, tag, UFS_QUERY_TASK, &resp); From 2483ae0a56231a915c706411421c6c002a2bf83e Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Wed, 27 May 2026 18:21:34 +0900 Subject: [PATCH 2474/5214] scsi: ufs: Fix wrong value printed in unexpected UPIU response case In ufshcd_transfer_rsp_status(), the default case of the inner switch statement prints the UPIU response code when an unexpected response is received. However, the code was printing 'result' variable which is always 0 at that point, making the error message useless for debugging. Fix this by printing the actual UPIU response code returned by ufshcd_get_req_rsp(). Fixes: 08108d31129a ("scsi: ufs: Improve type safety") Signed-off-by: Chanwoo Lee Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260527092134.275887-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 9b6cb6b569bc..3441e874eacc 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -5716,7 +5716,7 @@ static inline int ufshcd_transfer_rsp_status(struct ufs_hba *hba, default: dev_err(hba->dev, "Unexpected request response code = %x\n", - result); + ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr)); result = DID_ERROR << 16; break; } From 6bfc4bfd041d7ddeab9791f0592284585e960be4 Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Fri, 29 May 2026 15:15:00 +0900 Subject: [PATCH 2475/5214] scsi: ufs: Remove unnecessary return in void vops wrappers ufshcd_vops_exit(), ufshcd_vops_setup_task_mgmt(), and ufshcd_vops_hibern8_notify() use 'return hba->vops->xxx()' while other void vops wrappers call without return. Remove the unnecessary return keywords for consistency. Signed-off-by: Chanwoo Lee Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260529061503.301182-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd-priv.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 70f90d97f217..e55c2a02c1f5 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -132,7 +132,7 @@ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) static inline void ufshcd_vops_exit(struct ufs_hba *hba) { if (hba->vops && hba->vops->exit) - return hba->vops->exit(hba); + hba->vops->exit(hba); } static inline u32 ufshcd_vops_get_ufs_hci_version(struct ufs_hba *hba) @@ -211,7 +211,7 @@ static inline void ufshcd_vops_setup_task_mgmt(struct ufs_hba *hba, int tag, u8 tm_function) { if (hba->vops && hba->vops->setup_task_mgmt) - return hba->vops->setup_task_mgmt(hba, tag, tm_function); + hba->vops->setup_task_mgmt(hba, tag, tm_function); } static inline void ufshcd_vops_hibern8_notify(struct ufs_hba *hba, @@ -219,7 +219,7 @@ static inline void ufshcd_vops_hibern8_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) { if (hba->vops && hba->vops->hibern8_notify) - return hba->vops->hibern8_notify(hba, cmd, status); + hba->vops->hibern8_notify(hba, cmd, status); } static inline int ufshcd_vops_apply_dev_quirks(struct ufs_hba *hba) From 0600eec09ad6cc5ba3ca78aceb6fa8dcbad010bb Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Fri, 29 May 2026 15:16:19 +0900 Subject: [PATCH 2476/5214] scsi: ufs: Remove redundant vops NULL check and trivial wrapper ufshcd_variant_hba_init/exit() check 'if (!hba->vops)' before calling vops wrappers, but the wrappers already do NULL check internally. Remove the redundant checks. Also remove ufshcd_variant_hba_exit() entirely since it only wraps ufshcd_vops_exit() with no added value. Signed-off-by: Chanwoo Lee Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260529061623.301291-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 3441e874eacc..60ec2c63c2d8 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9854,28 +9854,15 @@ static int ufshcd_init_clocks(struct ufs_hba *hba) static int ufshcd_variant_hba_init(struct ufs_hba *hba) { - int err = 0; + int err = ufshcd_vops_init(hba); - if (!hba->vops) - goto out; - - err = ufshcd_vops_init(hba); if (err) dev_err_probe(hba->dev, err, "%s: variant %s init failed with err %d\n", __func__, ufshcd_get_var_name(hba), err); -out: return err; } -static void ufshcd_variant_hba_exit(struct ufs_hba *hba) -{ - if (!hba->vops) - return; - - ufshcd_vops_exit(hba); -} - static int ufshcd_hba_init(struct ufs_hba *hba) { int err; @@ -9943,7 +9930,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) if (hba->eh_wq) destroy_workqueue(hba->eh_wq); ufs_debugfs_hba_exit(hba); - ufshcd_variant_hba_exit(hba); + ufshcd_vops_exit(hba); ufshcd_setup_vreg(hba, false); ufshcd_setup_clocks(hba, false); ufshcd_setup_hba_vreg(hba, false); From 37340ac2c7be914ff76fdc6bb505b204a4140268 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 May 2026 18:50:39 -0700 Subject: [PATCH 2477/5214] Input: userio - update maintainer name She's been committing under the name Lyude Paul for a while Signed-off-by: Vicki Pfau Link: https://patch.msgid.link/20260522015040.3953472-1-vi@endrift.com Signed-off-by: Dmitry Torokhov --- Documentation/input/userio.rst | 2 +- MAINTAINERS | 2 +- drivers/input/serio/userio.c | 4 ++-- include/uapi/linux/userio.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/input/userio.rst b/Documentation/input/userio.rst index f780c77931fe..415962152815 100644 --- a/Documentation/input/userio.rst +++ b/Documentation/input/userio.rst @@ -5,7 +5,7 @@ The userio Protocol =================== -:Copyright: |copy| 2015 Stephen Chandler Paul +:Copyright: |copy| 2015 Lyude Paul Sponsored by Red Hat diff --git a/MAINTAINERS b/MAINTAINERS index 9ec290e38b44..ba67a2668231 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28403,7 +28403,7 @@ F: sound/drivers/pcmtest.c F: tools/testing/selftests/alsa/test-pcmtest-driver.c VIRTUAL SERIO DEVICE DRIVER -M: Stephen Chandler Paul +M: Lyude Paul S: Maintained F: drivers/input/serio/userio.c F: include/uapi/linux/userio.h diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c index 91cb7a177b2d..abca8cb6aca5 100644 --- a/drivers/input/serio/userio.c +++ b/drivers/input/serio/userio.c @@ -1,7 +1,7 @@ /* * userio kernel serio device emulation module * Copyright (C) 2015 Red Hat - * Copyright (C) 2015 Stephen Chandler Paul + * Copyright (C) 2015 Lyude Paul * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by @@ -278,6 +278,6 @@ module_driver(userio_misc, misc_register, misc_deregister); MODULE_ALIAS_MISCDEV(USERIO_MINOR); MODULE_ALIAS("devname:" USERIO_NAME); -MODULE_AUTHOR("Stephen Chandler Paul "); +MODULE_AUTHOR("Lyude Paul "); MODULE_DESCRIPTION("Virtual Serio Device Support"); MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/userio.h b/include/uapi/linux/userio.h index 74c9951d2cd0..98fe7e9089c4 100644 --- a/include/uapi/linux/userio.h +++ b/include/uapi/linux/userio.h @@ -2,7 +2,7 @@ /* * userio: virtual serio device support * Copyright (C) 2015 Red Hat - * Copyright (C) 2015 Lyude (Stephen Chandler Paul) + * Copyright (C) 2015 Lyude Paul * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the From c6f1363abbff69e5bd1cd1fcf060e1510e8d6d31 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 May 2026 18:50:40 -0700 Subject: [PATCH 2478/5214] Input: userio - allow setting other id values Previously, only the type value was settable. The proto value is used internally for choosing the right drivers, so we should expose it. The other values make sense to expose as well. Signed-off-by: Vicki Pfau Link: https://patch.msgid.link/20260522015040.3953472-2-vi@endrift.com Signed-off-by: Dmitry Torokhov --- Documentation/input/userio.rst | 23 +++++++++++++++++++++-- drivers/input/serio/userio.c | 30 ++++++++++++++++++++++++++++++ include/uapi/linux/userio.h | 5 ++++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Documentation/input/userio.rst b/Documentation/input/userio.rst index 415962152815..7aaaa629bde0 100644 --- a/Documentation/input/userio.rst +++ b/Documentation/input/userio.rst @@ -66,8 +66,27 @@ USERIO_CMD_SET_PORT_TYPE ~~~~~~~~~~~~~~~~~~~~~~~~ Sets the type of port we're emulating, where ``data`` is the port type being -set. Can be any of the macros from . For example: SERIO_8042 -would set the port type to be a normal PS/2 port. +set. Can be any of the serio type macros from . For example: +SERIO_8042 would set the port type to be a normal PS/2 port. + +USERIO_CMD_SET_PORT_PROTO +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the protocol of port we're emulating, where ``data`` is the protocol being +set. Can be any of the serio proto macros from . For example: +SERIO_IFORCE would set the port type to be an I-Force serial joystick. + +USERIO_CMD_SET_PORT_ID +~~~~~~~~~~~~~~~~~~~~~~ + +Sets the ``id`` value on the identification of port we're emulating, where +``data`` is the value being set. + +USERIO_CMD_SET_PORT_EXTRA +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets the ``extra`` value on the identification of port we're emulating, where +``data`` is the value being set. USERIO_CMD_SEND_INTERRUPT ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c index abca8cb6aca5..8c19975c84bf 100644 --- a/drivers/input/serio/userio.c +++ b/drivers/input/serio/userio.c @@ -206,6 +206,36 @@ static int userio_execute_cmd(struct userio_device *userio, userio->serio->id.type = cmd->data; break; + case USERIO_CMD_SET_PORT_EXTRA: + if (userio->running) { + dev_warn(userio_misc.this_device, + "Can't change port extra on an already running userio instance\n"); + return -EBUSY; + } + + userio->serio->id.extra = cmd->data; + break; + + case USERIO_CMD_SET_PORT_ID: + if (userio->running) { + dev_warn(userio_misc.this_device, + "Can't change port id on an already running userio instance\n"); + return -EBUSY; + } + + userio->serio->id.id = cmd->data; + break; + + case USERIO_CMD_SET_PORT_PROTO: + if (userio->running) { + dev_warn(userio_misc.this_device, + "Can't change port proto on an already running userio instance\n"); + return -EBUSY; + } + + userio->serio->id.proto = cmd->data; + break; + case USERIO_CMD_SEND_INTERRUPT: if (!userio->running) { dev_warn(userio_misc.this_device, diff --git a/include/uapi/linux/userio.h b/include/uapi/linux/userio.h index 98fe7e9089c4..550c7465af1f 100644 --- a/include/uapi/linux/userio.h +++ b/include/uapi/linux/userio.h @@ -27,7 +27,10 @@ enum userio_cmd_type { USERIO_CMD_REGISTER = 0, USERIO_CMD_SET_PORT_TYPE = 1, - USERIO_CMD_SEND_INTERRUPT = 2 + USERIO_CMD_SEND_INTERRUPT = 2, + USERIO_CMD_SET_PORT_EXTRA = 3, + USERIO_CMD_SET_PORT_ID = 4, + USERIO_CMD_SET_PORT_PROTO = 5, }; /* From d16cb0a9a42f7478c039ecaeba241872bc0b567a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 13 Mar 2026 11:14:23 +0100 Subject: [PATCH 2479/5214] powerpc: dts: mpc8315erdb: Add missing model property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mpc8315erdb devicetree did thus far not have a /model property, even though it is required by the devicetree specification. Signed-off-by: J. Neuschäfer Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260313-ppc-model-v1-1-bf19b3d1b65d@posteo.net --- arch/powerpc/boot/dts/mpc8315erdb.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index 7ba1159f8803..4757f0d24a2e 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8315erdb"; + model = "MPC8315E-RDB"; #address-cells = <1>; #size-cells = <1>; From ec07a2f1d337dd5a816431724a876026296369a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 13 Mar 2026 11:14:24 +0100 Subject: [PATCH 2480/5214] powerpc: dts: Add missing model properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are the remaining PowerPC devicetrees that do not have a /model property, even though it is required by the devicetree specification. Fix them by simply copying the compatible string. Signed-off-by: J. Neuschäfer Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260313-ppc-model-v1-2-bf19b3d1b65d@posteo.net --- arch/powerpc/boot/dts/mpc8308_p1m.dts | 1 + arch/powerpc/boot/dts/mpc8308rdb.dts | 1 + arch/powerpc/boot/dts/mpc836x_rdk.dts | 1 + arch/powerpc/boot/dts/mpc8377_rdb.dts | 1 + arch/powerpc/boot/dts/mpc8377_wlan.dts | 1 + arch/powerpc/boot/dts/mpc8378_rdb.dts | 1 + arch/powerpc/boot/dts/mpc8379_rdb.dts | 1 + 7 files changed, 7 insertions(+) diff --git a/arch/powerpc/boot/dts/mpc8308_p1m.dts b/arch/powerpc/boot/dts/mpc8308_p1m.dts index 41f917f97dab..5af945e1743c 100644 --- a/arch/powerpc/boot/dts/mpc8308_p1m.dts +++ b/arch/powerpc/boot/dts/mpc8308_p1m.dts @@ -9,6 +9,7 @@ / { compatible = "denx,mpc8308_p1m"; + model = "denx,mpc8308_p1m"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8308rdb.dts b/arch/powerpc/boot/dts/mpc8308rdb.dts index 39ed26fba410..7b6fd58ffafa 100644 --- a/arch/powerpc/boot/dts/mpc8308rdb.dts +++ b/arch/powerpc/boot/dts/mpc8308rdb.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8308rdb"; + model = "fsl,mpc8308rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts index 4ff38e1a2185..d3d36fbf4e86 100644 --- a/arch/powerpc/boot/dts/mpc836x_rdk.dts +++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts @@ -14,6 +14,7 @@ / { #address-cells = <1>; #size-cells = <1>; compatible = "fsl,mpc8360rdk"; + model = "fsl,mpc8360rdk"; aliases { serial0 = &serial0; diff --git a/arch/powerpc/boot/dts/mpc8377_rdb.dts b/arch/powerpc/boot/dts/mpc8377_rdb.dts index fb311a7eb9f2..f4244a7a3189 100644 --- a/arch/powerpc/boot/dts/mpc8377_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8377_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8377rdb"; + model = "fsl,mpc8377rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8377_wlan.dts b/arch/powerpc/boot/dts/mpc8377_wlan.dts index f736a15cceff..77ee5f8db14e 100644 --- a/arch/powerpc/boot/dts/mpc8377_wlan.dts +++ b/arch/powerpc/boot/dts/mpc8377_wlan.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8377wlan"; + model = "fsl,mpc8377wlan"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8378_rdb.dts b/arch/powerpc/boot/dts/mpc8378_rdb.dts index 32c49622b404..9ae4ed5ffcf4 100644 --- a/arch/powerpc/boot/dts/mpc8378_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8378_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8378rdb"; + model = "fsl,mpc8378rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8379_rdb.dts b/arch/powerpc/boot/dts/mpc8379_rdb.dts index 07deb89c5a9b..214611dc6da9 100644 --- a/arch/powerpc/boot/dts/mpc8379_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8379_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8379rdb"; + model = "fsl,mpc8379rdb"; #address-cells = <1>; #size-cells = <1>; From 2442f10c6eae63392452ab84a813dbc1f87c724d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Sun, 15 Mar 2026 22:45:13 +0100 Subject: [PATCH 2481/5214] powerpc: dts: Build devicetrees of enabled platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the same approach as other architectures such as Arm or RISC-V, and build devicetrees based on platforms selected in Kconfig. This makes it unnecessary to use CONFIG_OF_ALL_DTBS on PowerPC in order to build DTB files. This makes it easier to use other build and test infrastructure such as `make dtbs_check`, and is a first step towards generating FIT images that include all the relevant DTBs with `make image.fit`. Signed-off-by: J. Neuschäfer Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260315-mpc83xx-dtb-v4-1-243849be4280@posteo.net --- arch/powerpc/boot/dts/Makefile | 73 ++++++++++++++++++++++++++++++ arch/powerpc/boot/dts/fsl/Makefile | 43 ++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/arch/powerpc/boot/dts/Makefile b/arch/powerpc/boot/dts/Makefile index 0cd0d8558b47..1e61a951cebe 100644 --- a/arch/powerpc/boot/dts/Makefile +++ b/arch/powerpc/boot/dts/Makefile @@ -3,3 +3,76 @@ subdir-y += fsl dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(src)/%.dts,%.dtb, $(wildcard $(src)/*.dts)) + +# PPC44x platforms +dtb-$(CONFIG_PPC44x_SIMPLE) += arches.dtb bamboo.dtb bluestone.dtb glacier.dtb +dtb-$(CONFIG_PPC44x_SIMPLE) += eiger.dtb katmai.dtb rainier.dtb redwood.dtb +dtb-$(CONFIG_PPC44x_SIMPLE) += sequoia.dtb taishan.dtb yosemite.dtb icon.dtb +dtb-$(CONFIG_EBONY) += ebony.dtb +dtb-$(CONFIG_SAM440EP) += sam440ep.dtb +dtb-$(CONFIG_WARP) += warp.dtb +dtb-$(CONFIG_ISS4xx) += iss4xx.dtb iss4xx-mpic.dtb +dtb-$(CONFIG_CANYONLANDS) += canyonlands.dtb +dtb-$(CONFIG_CURRITUCK) += currituck.dtb +dtb-$(CONFIG_AKEBONO) += akebono.dtb +dtb-$(CONFIG_FSP2) += fsp2.dtb + +# Embedded 6xx platforms +dtb-$(CONFIG_LINKSTATION) += kuroboxHG.dtb kuroboxHD.dtb +dtb-$(CONFIG_STORCENTER) += storcenter.dtb +dtb-$(CONFIG_PPC_HOLLY) += holly.dtb +dtb-$(CONFIG_GAMECUBE) += gamecube.dtb +dtb-$(CONFIG_WII) += wii.dtb +dtb-$(CONFIG_MVME5100) += mvme5100.dtb + +# MPC8xx platforms +dtb-$(CONFIG_MPC885ADS) += mpc885ads.dtb +dtb-$(CONFIG_MPC86XADS) += mpc866ads.dtb +dtb-$(CONFIG_PPC_EP88XC) += ep88xc.dtb +dtb-$(CONFIG_PPC_ADDER875) += adder875-redboot.dtb adder875-uboot.dtb +dtb-$(CONFIG_TQM8XX) += tqm8xx.dtb + +# MPC512x platforms +dtb-$(CONFIG_MPC5121_ADS) += mpc5121ads.dtb +dtb-$(CONFIG_MPC512x_GENERIC) += mpc5125twr.dtb ac14xx.dts +dtb-$(CONFIG_PDM360NG) += pdm360ng.dtb + +# MPC5200 platforms +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += a3m071.dtb a4m072.dtb charon.dtb cm5200.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += digsy_mtc.dtb motionpro.dtb mucmc52.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += o2d.dtb o2d300.dtb o2dnt2.dtb o2i.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += o2mnt.dtb o3dnt.dtb pcm030.dtb pcm032.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += tqm5200.dtb uc101.dtb +dtb-$(CONFIG_PPC_LITE5200) += lite5200.dtb lite5200b.dtb +dtb-$(CONFIG_PPC_MEDIA5200) += media5200.dtb + +# MPC82xx platforms +dtb-$(CONFIG_EP8248E) += ep8248e.dtb +dtb-$(CONFIG_MGCOGE) += mgcoge.dtb + +# MPC83xx platforms +dtb-$(CONFIG_MPC830x_RDB) += mpc8308rdb.dtb mpc8308_p1m.dtb +dtb-$(CONFIG_MPC831x_RDB) += mpc8313erdb.dtb mpc8315erdb.dtb +dtb-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.dtb +dtb-$(CONFIG_MPC834x_ITX) += mpc8349emitx.dtb mpc8349emitxgp.dtb +dtb-$(CONFIG_ASP834x) += asp834x-redboot.dtb +dtb-$(CONFIG_MPC836x_RDK) += mpc836x_rdk.dtb +dtb-$(CONFIG_KMETER1) += kmeter1.dtb +dtb-$(CONFIG_MPC837x_RDB) += mpc8377_rdb.dtb mpc8378_rdb.dtb mpc8379_rdb.dtb +dtb-$(CONFIG_MPC837x_RDB) += mpc8377_wlan.dtb + +# MPC85xx platforms +dtb-$(CONFIG_STX_GP3) += stx_gp3_8560.dtb stxssa8555.dtb +dtb-$(CONFIG_TQM85xx) += tqm8540.dtb tqm8541.dtb tqm8548.dtb +dtb-$(CONFIG_TQM85xx) += tqm8548-bigflash.dtb tqm8555.dtb tqm8560.dtb +dtb-$(CONFIG_SOCRATES) += socrates.dtb +dtb-$(CONFIG_KSI8560) += ksi8560.dtb +dtb-$(CONFIG_XES_MPC85xx) += xcalibur1501.dtb xpedite5200.dtb +dtb-$(CONFIG_XES_MPC85xx) += xpedite5200_xmon.dtb xpedite5301.dtb +dtb-$(CONFIG_XES_MPC85xx) += xpedite5330.dtb xpedite5370.dtb +dtb-$(CONFIG_PPC_P2020) += turris1x.dtb + +# Misc. platforms +dtb-$(CONFIG_PPC_MICROWATT) += microwatt.dtb +dtb-$(CONFIG_AMIGAONE) += amigaone.dtb +dtb-$(CONFIG_PPC_PS3) += ps3.dtb diff --git a/arch/powerpc/boot/dts/fsl/Makefile b/arch/powerpc/boot/dts/fsl/Makefile index d3ecdf14bc42..d2cc8e1f007e 100644 --- a/arch/powerpc/boot/dts/fsl/Makefile +++ b/arch/powerpc/boot/dts/fsl/Makefile @@ -1,3 +1,46 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(src)/%.dts,%.dtb, $(wildcard $(src)/*.dts)) + +# MPC85xx platforms +dtb-$(CONFIG_BSC9131_RDB) += bsc9131rdb.dtb +dtb-$(CONFIG_BSC9132_QDS) += bsc9132qds.dtb +dtb-$(CONFIG_C293_PCIE) += c293pcie.dtb +dtb-$(CONFIG_MPC8536_DS) += mpc8536ds.dtb mpc8536ds_36b.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8544ds.dtb mpc8572ds_camp_core0.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8572ds_camp_core1.dtb mpc8572ds_36b.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8572ds.dtb +dtb-$(CONFIG_MPC85xx_MDS) += mpc8568mds.dtb mpc8569mds.dtb p1021mds.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020mbg-pc_32b.dtb p1020mbg-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb_36b.dtb p1020rdb.dtb p1020rdb-pc_32b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb-pc_36b.dtb p1020rdb-pc_camp_core0.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb-pc_camp_core1.dtb p1020rdb-pd.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020utm-pc_32b.dtb p1020utm-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1021rdb-pc_32b.dtb p1021rdb-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1024rdb_32b.dtb p1024rdb_36b.dtb p1025rdb_32b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1025rdb_36b.dtb +dtb-$(CONFIG_P1010_RDB) += p1010rdb-pa_36b.dtb p1010rdb-pa.dtb +dtb-$(CONFIG_P1010_RDB) += p1010rdb-pb_36b.dtb p1010rdb-pb.dtb +dtb-$(CONFIG_P1022_DS) += p1022ds_32b.dtb p1022ds_36b.dtb +dtb-$(CONFIG_P1022_RDK) += p1022rdk.dtb +dtb-$(CONFIG_P1023_RDB) += p1023rdb.dtb +dtb-$(CONFIG_PPC_P2020) += p2020ds.dtb +dtb-$(CONFIG_TWR_P102x) += p1025twr.dtb +dtb-$(CONFIG_CORENET_GENERIC) += b4420qds.dtb b4860qds.dtb cyrus_p5020.dtb +dtb-$(CONFIG_CORENET_GENERIC) += kmcent2.dtb kmcoge4.dtb oca4080.dtb +dtb-$(CONFIG_CORENET_GENERIC) += p2041rdb.dtb p3041ds.dtb p4080ds.dtb +dtb-$(CONFIG_CORENET_GENERIC) += p5020ds.dtb p5040ds.dtb t1023rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1024qds.dtb t1024rdb.dtb t1040d4rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1040qds.dtb t1040rdb.dtb t1040rdb-rev-a.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1042d4rdb.dtb t1042qds.dtb t1042rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1042rdb_pi.dtb t2080qds.dtb t2080rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t2081qds.dtb t4240qds.dtb t4240rdb.dtb +dtb-$(CONFIG_PPA8548) += ppa8548.dtb +dtb-$(CONFIG_GE_IMP3A) += ge_imp3a.dtb +dtb-$(CONFIG_MVME2500) += mvme2500.dtb + +# MPC86xx platforms +dtb-$(CONFIG_GEF_SBC310) += gef_sbc310.dtb +dtb-$(CONFIG_GEF_SBC610) += gef_sbc610.dtb +dtb-$(CONFIG_GEF_PPC9A) += gef_ppc9a.dtb +dtb-$(CONFIG_MVME7100) += mvme7100.dtb From b55b6b9ad76cdb82123c62a15c6a4ebe4abb8bfa Mon Sep 17 00:00:00 2001 From: Saket Kumar Bhaskar Date: Fri, 3 Apr 2026 16:20:16 +0530 Subject: [PATCH 2482/5214] powerpc64/bpf: Add powerpc64 JIT support for timed may_goto When verifier sees a timed may_goto instruction, it emits a call to arch_bpf_timed_may_goto() with a stack offset in BPF_REG_AX (powerpc64 R12) and expects the refreshed count value to be returned in the same register. The verifier doesn't save or restore any registers before emitting this call. arch_bpf_timed_may_goto() should act as a trampoline to call bpf_check_timed_may_goto() with powerpc64 ELF ABI calling convention. To support this custom calling convention, implement arch_bpf_timed_may_goto() in assembly and make sure BPF caller saved registers are preserved, then call bpf_check_timed_may_goto with the powerpc64 ABI calling convention where first argument and return value both are in R3. Finally, move the result back into BPF_REG_AX(R12) before returning. Also, introduce bpf_jit_emit_func_call() that computes the offset from kernel_toc_addr(), validates that the target and emits the ADDIS/ADDI sequence to load the function address before performing the indirect branch via MTCTR/BCTRL. The existing code in bpf_jit_emit_func_call_rel() is refactored to use this function. Acked-by: Hari Bathini Signed-off-by: Saket Kumar Bhaskar Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260403105016.74775-1-skb99@linux.ibm.com --- arch/powerpc/net/Makefile | 4 ++ arch/powerpc/net/bpf_jit_comp.c | 5 +++ arch/powerpc/net/bpf_jit_comp64.c | 62 ++++++++++++++++++++++----- arch/powerpc/net/bpf_timed_may_goto.S | 57 ++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 arch/powerpc/net/bpf_timed_may_goto.S diff --git a/arch/powerpc/net/Makefile b/arch/powerpc/net/Makefile index 8e60af32e51e..7f9339bacef1 100644 --- a/arch/powerpc/net/Makefile +++ b/arch/powerpc/net/Makefile @@ -3,3 +3,7 @@ # Arch-specific network modules # obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_jit_comp$(BITS).o + +ifdef CONFIG_PPC64 +obj-$(CONFIG_BPF_JIT) += bpf_timed_may_goto.o +endif diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 53ab97ad6074..fcbda446bc26 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -517,6 +517,11 @@ bool bpf_jit_supports_subprog_tailcalls(void) return IS_ENABLED(CONFIG_PPC64); } +bool bpf_jit_supports_timed_may_goto(void) +{ + return IS_ENABLED(CONFIG_PPC64); +} + bool bpf_jit_supports_kfunc_call(void) { return IS_ENABLED(CONFIG_PPC64); diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index db364d9083e7..dab106cae22b 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -451,10 +451,28 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *, u64, u64, u64), void *cookie } } +static int bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func_addr, int reg) +{ + long reladdr = func_addr - kernel_toc_addr(); + + if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { + pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func_addr); + return -ERANGE; + } + + EMIT(PPC_RAW_ADDIS(reg, _R2, PPC_HA(reladdr))); + EMIT(PPC_RAW_ADDI(reg, reg, PPC_LO(reladdr))); + EMIT(PPC_RAW_MTCTR(reg)); + EMIT(PPC_RAW_BCTRL()); + + return 0; +} + int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *ctx, u64 func) { unsigned long func_addr = func ? ppc_function_entry((void *)func) : 0; - long reladdr; + long __maybe_unused reladdr; + int ret; /* bpf to bpf call, func is not known in the initial pass. Emit 5 nops as a placeholder */ if (!func) { @@ -507,16 +525,9 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context * EMIT(PPC_RAW_BCTRL()); #else if (core_kernel_text(func_addr)) { - reladdr = func_addr - kernel_toc_addr(); - if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { - pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func); - return -ERANGE; - } - - EMIT(PPC_RAW_ADDIS(_R12, _R2, PPC_HA(reladdr))); - EMIT(PPC_RAW_ADDI(_R12, _R12, PPC_LO(reladdr))); - EMIT(PPC_RAW_MTCTR(_R12)); - EMIT(PPC_RAW_BCTRL()); + ret = bpf_jit_emit_func_call(image, ctx, func_addr, _R12); + if (ret) + return ret; } else { if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V1)) { /* func points to the function descriptor */ @@ -1755,6 +1766,35 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code if (ret < 0) return ret; + /* + * Call to arch_bpf_timed_may_goto() is emitted by the + * verifier and called with custom calling convention with + * first argument and return value in BPF_REG_AX (_R12). + * + * The generic helper or bpf function call emission path + * may use the same scratch register as BPF_REG_AX to + * materialize the target address. This would clobber AX + * and break timed may_goto semantics. + * + * Emit a minimal indirect call sequence here using a temp + * register and skip the normal post-call return-value move. + */ + + if (func_addr == (u64)arch_bpf_timed_may_goto) { + ret = 0; + if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) + ret = bpf_jit_emit_func_call(image, ctx, func_addr, + tmp1_reg); + + if (ret || IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) { + PPC_LI_ADDR(tmp1_reg, func_addr); + EMIT(PPC_RAW_MTCTR(tmp1_reg)); + EMIT(PPC_RAW_BCTRL()); + } + + break; + } + /* Take care of powerpc ABI requirements before kfunc call */ if (insn[i].src_reg == BPF_PSEUDO_KFUNC_CALL) { if (prepare_for_kfunc_call(fp, image, ctx, &insn[i])) diff --git a/arch/powerpc/net/bpf_timed_may_goto.S b/arch/powerpc/net/bpf_timed_may_goto.S new file mode 100644 index 000000000000..6fd8b1c9f4ac --- /dev/null +++ b/arch/powerpc/net/bpf_timed_may_goto.S @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 IBM Corporation, Saket Kumar Bhaskar */ + +#include +#include + +/* + * arch_bpf_timed_may_goto() trampoline for powerpc64 + * + * Custom BPF convention (verifier/JIT): + * - input: stack offset in BPF_REG_AX (r12) + * - output: updated count in BPF_REG_AX (r12) + * + * Call bpf_check_timed_may_goto(ptr) with normal powerpc64 ABI: + * - r3 = ptr, return in r3 + * + * Preserve BPF regs R0-R5 (mapping: r8, r3-r7). + */ + +SYM_FUNC_START(arch_bpf_timed_may_goto) + /* Prologue: save LR, allocate frame */ + mflr r0 + std r0, 16(r1) + stdu r1, -112(r1) + + /* Save BPF registers R0 - R5 (r8, r3-r7) */ + std r3, 32(r1) + std r4, 40(r1) + std r5, 48(r1) + std r6, 56(r1) + std r7, 64(r1) + std r8, 72(r1) + + /* + * r3 = BPF_REG_FP + BPF_REG_AX + * BPF_REG_FP is r31; BPF_REG_AX is r12 (stack offset in bytes). + */ + add r3, r31, r12 + bl bpf_check_timed_may_goto + + /* Put return value back into AX */ + mr r12, r3 + + /* Restore BPF registers R0 - R5 (r8, r3-r7) */ + ld r3, 32(r1) + ld r4, 40(r1) + ld r5, 48(r1) + ld r6, 56(r1) + ld r7, 64(r1) + ld r8, 72(r1) + + /* Epilogue: pop frame, restore LR, return */ + addi r1, r1, 112 + ld r0, 16(r1) + mtlr r0 + blr +SYM_FUNC_END(arch_bpf_timed_may_goto) From b3580dd1c68cec23d44a39c439e18b4686c13480 Mon Sep 17 00:00:00 2001 From: Adriano Vero Date: Thu, 7 May 2026 00:20:23 +0200 Subject: [PATCH 2483/5214] powerpc/fadump: Add timeout to RTAS busy-wait loops The ibm,configure-kernel-dump RTAS call sites in rtas_fadump_register(), rtas_fadump_unregister(), and rtas_fadump_invalidate() polled indefinitely while firmware returned a busy status. A misbehaving or hung firmware could stall these paths forever, blocking fadump registration at boot or preventing clean teardown. Introduce rtas_fadump_call(), a helper that wraps the common busy-wait pattern shared by all three sites. The helper accumulates the total delay and returns -ETIMEDOUT if firmware keeps returning a busy status beyond RTAS_FADUMP_MAX_WAIT_MS (60 seconds). A pr_debug() message is emitted on each busy iteration to aid diagnosis when the timeout is hit. Signed-off-by: Adriano Vero Reviewed-by: Sourabh Jain [Maddy: Fixed newline after Signed-off-by] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260506222024.30352-1-adri.vero.dev@gmail.com --- arch/powerpc/platforms/pseries/rtas-fadump.c | 80 ++++++++++++-------- arch/powerpc/platforms/pseries/rtas-fadump.h | 6 ++ 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c index eceb3289383e..3bb4ac2ab6cc 100644 --- a/arch/powerpc/platforms/pseries/rtas-fadump.c +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c @@ -179,9 +179,42 @@ static u64 rtas_fadump_get_bootmem_min(void) return RTAS_FADUMP_MIN_BOOT_MEM; } +/* + * Helper to make an ibm,configure-kernel-dump RTAS call with a bounded + * busy-wait loop. Returns the RTAS return code on completion, or + * -ETIMEDOUT if firmware keeps returning a busy status beyond + * RTAS_FADUMP_MAX_WAIT_MS milliseconds. + */ +static int rtas_fadump_call(struct fw_dump *fadump_conf, int operation, + void *fdm_ptr, unsigned int fdm_size, + const char *op_name) +{ + unsigned int wait_time, total_wait = 0; + int rc; + + do { + rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, + NULL, operation, fdm_ptr, fdm_size); + wait_time = rtas_busy_delay_time(rc); + if (wait_time) { + pr_debug("Firmware busy during fadump %s, waiting %ums (total %ums)\n", + op_name, wait_time, total_wait); + if (total_wait >= RTAS_FADUMP_MAX_WAIT_MS) { + pr_err("Timed out waiting for firmware to complete fadump %s\n", + op_name); + return -ETIMEDOUT; + } + total_wait += wait_time; + mdelay(wait_time); + } + } while (wait_time); + + return rc; +} + static int rtas_fadump_register(struct fw_dump *fadump_conf) { - unsigned int wait_time, fdm_size; + unsigned int fdm_size; int rc, err = -EIO; /* @@ -192,16 +225,10 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf) fdm_size = sizeof(struct rtas_fadump_section_header); fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section); - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_REGISTER, &fdm, fdm_size); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_REGISTER, &fdm, fdm_size, + "register"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; switch (rc) { case 0: @@ -234,19 +261,12 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf) static int rtas_fadump_unregister(struct fw_dump *fadump_conf) { - unsigned int wait_time; int rc; - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_UNREGISTER, &fdm, - sizeof(struct rtas_fadump_mem_struct)); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_UNREGISTER, &fdm, + sizeof(struct rtas_fadump_mem_struct), "unregister"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; if (rc) { pr_err("Failed to un-register - unexpected error(%d).\n", rc); @@ -259,19 +279,13 @@ static int rtas_fadump_unregister(struct fw_dump *fadump_conf) static int rtas_fadump_invalidate(struct fw_dump *fadump_conf) { - unsigned int wait_time; int rc; - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_INVALIDATE, fdm_active, - sizeof(struct rtas_fadump_mem_struct)); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_INVALIDATE, + (void *)fdm_active, + sizeof(struct rtas_fadump_mem_struct), "invalidate"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; if (rc) { pr_err("Failed to invalidate - unexpected error (%d).\n", rc); diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.h b/arch/powerpc/platforms/pseries/rtas-fadump.h index c109abf6befd..65fdab7b5b8d 100644 --- a/arch/powerpc/platforms/pseries/rtas-fadump.h +++ b/arch/powerpc/platforms/pseries/rtas-fadump.h @@ -41,6 +41,12 @@ #define MAX_SECTIONS 10 #define RTAS_FADUMP_MAX_BOOT_MEM_REGS 7 +/* + * Maximum time to wait for firmware to respond to an + * ibm,configure-kernel-dump RTAS call before giving up. + */ +#define RTAS_FADUMP_MAX_WAIT_MS 60000U + /* Kernel Dump section info */ struct rtas_fadump_section { __be32 request_flag; From 1ca0c82bbc72e8e3de0a41e6dbf92473c00f3d1e Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 27 Apr 2026 10:17:12 +0530 Subject: [PATCH 2484/5214] powerpc: Use cpumask_next_wrap instead cpu = cpumask_next(cpu, mask) if (cpu >= nr_cpu_ids) cpu = cpumask_first(mask) Above block is identical to: cpu = cpumask_next_wrap(cpu, mask) Replace it, No change in functionality or performance. Slightly simpler code. Reviewed-by: Yury Norov Signed-off-by: Shrikanth Hegde Reviewed-by: Yury Norov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427044715.559137-2-sshegde@linux.ibm.com --- arch/powerpc/kernel/irq.c | 5 +---- arch/powerpc/mm/book3s64/hash_utils.c | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index a0e8b998c9b5..f69de08ad347 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -370,10 +370,7 @@ int irq_choose_cpu(const struct cpumask *mask) do_round_robin: raw_spin_lock_irqsave(&irq_rover_lock, flags); - irq_rover = cpumask_next(irq_rover, cpu_online_mask); - if (irq_rover >= nr_cpu_ids) - irq_rover = cpumask_first(cpu_online_mask); - + irq_rover = cpumask_next_wrap(irq_rover, cpu_online_mask); cpuid = irq_rover; raw_spin_unlock_irqrestore(&irq_rover_lock, flags); diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 9dc5889d6ecb..e4fcf929cb33 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -1299,9 +1299,7 @@ static void stress_hpt_timer_fn(struct timer_list *timer) if (!firmware_has_feature(FW_FEATURE_LPAR)) tlbiel_all(); - next_cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); - if (next_cpu >= nr_cpu_ids) - next_cpu = cpumask_first(cpu_online_mask); + next_cpu = cpumask_next_wrap(raw_smp_processor_id(), cpu_online_mask); stress_hpt_timer.expires = jiffies + msecs_to_jiffies(10); add_timer_on(&stress_hpt_timer, next_cpu); } From a66eab7a5d7decadad51b4204c6f2f0d8cf665d1 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 27 Apr 2026 10:17:13 +0530 Subject: [PATCH 2485/5214] powerpc: Simplify cpumask api usage for cpuinfo display - cpumask_next can take -1 as valid argument. So simplify cpuinfo iterator. - Use cpumask_last to find if this_cpu is last online CPU. /proc/cpuinfo shows same info with patch. Reviewed-by: Yury Norov Signed-off-by: Shrikanth Hegde Reviewed-by: Yury Norov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427044715.559137-3-sshegde@linux.ibm.com --- arch/powerpc/kernel/setup-common.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 8a86b0efcb1c..aecabe9cf139 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -323,7 +323,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_putc(m, '\n'); /* If this is the last cpu, print the summary */ - if (cpumask_next(cpu_id, cpu_online_mask) >= nr_cpu_ids) + if (cpu_id == cpumask_last(cpu_online_mask)) show_cpuinfo_summary(m); return 0; @@ -331,10 +331,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) static void *c_start(struct seq_file *m, loff_t *pos) { - if (*pos == 0) /* just in case, cpu 0 is not the first */ - *pos = cpumask_first(cpu_online_mask); - else - *pos = cpumask_next(*pos - 1, cpu_online_mask); + *pos = cpumask_next(*pos - 1, cpu_online_mask); if ((*pos) < nr_cpu_ids) return (void *)(unsigned long)(*pos + 1); return NULL; From 1f8986fdae91880411a3be907879ba63973da100 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 27 Apr 2026 10:17:14 +0530 Subject: [PATCH 2486/5214] powerpc/perf: Use cpumask_intersects api for checking disable path First online CPU in the node disables the nest counters by making an OPAL call. Any other CPU in that node, will bail out. Instead of using a temporary mask to find out if any cpu in the node is visited or not, it is better to use the cpumask_intersects api to achieve the same. Similarly a temporary cpumask is used to check if a core is already part of core_imc_cpumask. Use the same cpumask_intersects api there. Signed-off-by: Shrikanth Hegde Reviewed-by: Yury Norov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427044715.559137-4-sshegde@linux.ibm.com --- arch/powerpc/perf/imc-pmu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index c1563b4eaa94..e3822f36c419 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -421,7 +421,6 @@ static int ppc_nest_imc_cpu_offline(unsigned int cpu) static int ppc_nest_imc_cpu_online(unsigned int cpu) { const struct cpumask *l_cpumask; - static struct cpumask tmp_mask; int res; /* Get the cpumask of this node */ @@ -431,7 +430,7 @@ static int ppc_nest_imc_cpu_online(unsigned int cpu) * If this is not the first online CPU on this node, then * just return. */ - if (cpumask_and(&tmp_mask, l_cpumask, &nest_imc_cpumask)) + if (cpumask_intersects(l_cpumask, &nest_imc_cpumask)) return 0; /* @@ -647,14 +646,13 @@ static bool is_core_imc_mem_inited(int cpu) static int ppc_core_imc_cpu_online(unsigned int cpu) { const struct cpumask *l_cpumask; - static struct cpumask tmp_mask; int ret = 0; /* Get the cpumask for this core */ l_cpumask = cpu_sibling_mask(cpu); /* If a cpu for this core is already set, then, don't do anything */ - if (cpumask_and(&tmp_mask, l_cpumask, &core_imc_cpumask)) + if (cpumask_intersects(l_cpumask, &core_imc_cpumask)) return 0; if (!is_core_imc_mem_inited(cpu)) { From 0521dbbc5710aca3b3d189747781761732289623 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 27 Apr 2026 10:17:15 +0530 Subject: [PATCH 2487/5214] powerpc/xive: Add warning if target CPU not found Add a warn_once to warn if the CPU target is not found. This could help to find about any such usecase. This is a very rare case, which either means mask was empty or atomic update failed for all online CPUs. So it is worth printing that path for potential fix. Signed-off-by: Shrikanth Hegde Reviewed-by: Yury Norov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260427044715.559137-5-sshegde@linux.ibm.com --- arch/powerpc/sysdev/xive/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index c120be73d149..dadd1f46ec93 100644 --- a/arch/powerpc/sysdev/xive/common.c +++ b/arch/powerpc/sysdev/xive/common.c @@ -564,6 +564,7 @@ static int xive_find_target_in_mask(const struct cpumask *mask, return cpu; } + WARN_ONCE(1, "target CPU not found in mask: %*pbl\n", cpumask_pr_args(mask)); return -1; } From 6ed8332639853b77329594a241eb99fc36d591a2 Mon Sep 17 00:00:00 2001 From: Shivang Upadhyay Date: Sun, 12 Apr 2026 17:00:57 +0530 Subject: [PATCH 2488/5214] ppc/fadump: invoke kmsg_dump in fadump panic path fadump is registered in panic_notifier_list and gets triggered before kmsg_dump_desc() in the panic path. As a result, kmsg_dumpers such as pstore are not executed during fadump crashes. This is problematic because pstore provides a critical fallback mechanism for crash analysis. When fadump fails to successfully reboot the system or capture a dump, pstore logs may be the only available information from the crashed kernel. Without invoking kmsg_dump_desc() in the fadump path, we lose this valuable diagnostic data. Invoke kmsg_dump_desc() from the fadump panic handler, but only when fadump is actually registered (checked via should_fadump_crash()). This ensures kmsg_dumpers are called without duplicating the call that occurs later in panic() when fadump is not active. The call is placed before crash_fadump() to ensure logs are captured before the system attempts to trigger the firmware-assisted dump. Reported-by: Shirisha G Suggested-by: Sourabh Jain Signed-off-by: Shivang Upadhyay Tested-by: Shirisha G Reviewed-by: Mahesh Salgaonkar Reviewed-by: Sourabh Jain Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260412113057.46090-1-shivangu@linux.ibm.com --- arch/powerpc/kernel/setup-common.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index aecabe9cf139..67c545f61f0d 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -68,6 +68,7 @@ #include #include #include +#include #include "setup.h" @@ -741,6 +742,13 @@ static int ppc_panic_fadump_handler(struct notifier_block *this, */ hard_irq_disable(); + /* + * Invoke kmsg_dump (e.g., pstore) before crash_fadump() as fadump + * runs before panic()'s kmsg_dump_desc() call. + */ + if (should_fadump_crash()) + kmsg_dump_desc(KMSG_DUMP_PANIC, (char *)ptr); + /* * If firmware-assisted dump has been registered then trigger * its callback and let the firmware handles everything else. From a5779442addf487b8093b12e7acdb16a5aab2f11 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 24 May 2026 23:29:45 +0200 Subject: [PATCH 2489/5214] MAINTAINERS: powerpc: update VMX AES entries Commit 7cf2082e74ce ("lib/crypto: powerpc/aes: Migrate POWER8 optimized code into library") removed arch/powerpc/crypto/aes.c and moved arch/powerpc/crypto/aesp8-ppc.pl to lib/crypto/powerpc/. However, the "IBM Power VMX Cryptographic instructions" entry still references the removed file and no longer covers the moved aesp8-ppc.pl. Remove the stale entry, add lib/crypto/powerpc/aesp8-ppc.pl, and tighten the arch/powerpc/crypto/aesp8-ppc.* pattern to match the remaining header only. Fixes: 7cf2082e74ce ("lib/crypto: powerpc/aes: Migrate POWER8 optimized code into library") Signed-off-by: Thorsten Blum Acked-by: Breno Leitao Acked-by: Eric Biggers Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260524212943.799757-3-thorsten.blum@linux.dev --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c2c6d79275c6..ab024c62c4ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12346,13 +12346,13 @@ L: linux-crypto@vger.kernel.org S: Supported F: arch/powerpc/crypto/Kconfig F: arch/powerpc/crypto/Makefile -F: arch/powerpc/crypto/aes.c F: arch/powerpc/crypto/aes_cbc.c F: arch/powerpc/crypto/aes_ctr.c F: arch/powerpc/crypto/aes_xts.c -F: arch/powerpc/crypto/aesp8-ppc.* +F: arch/powerpc/crypto/aesp8-ppc.h F: arch/powerpc/crypto/ppc-xlate.pl F: arch/powerpc/crypto/vmx.c +F: lib/crypto/powerpc/aesp8-ppc.pl F: lib/crypto/powerpc/gf128hash.h F: lib/crypto/powerpc/ghashp8-ppc.pl From ea8b474b5550d353a02f25a5813cb1682509d5e6 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 24 May 2026 15:00:03 +0200 Subject: [PATCH 2490/5214] powerpc: use sysfs_emit{_at} in sysfs show functions Replace sprintf() with sysfs_emit() and sysfs_emit_at() in sysfs show functions, which are preferred for formatting sysfs output because they provide safer bounds checking. While the current code only emits strings that fit easily within PAGE_SIZE, use sysfs_emit() and sysfs_emit_at() to follow secure coding best practices. This is a mechanical cleanup with a few simple edge cases: - In domains_show(), drop the redundant n < 0 check since neither sprintf() nor sysfs_emit() return negative values. - In powercap_show() and psr_show(), also drop the dead ret < 0 checks. - In ps3_fw_version_show(), normalize the output by adding a terminating newline as suggested by checkpatch. - In vio's modalias_show(), replace the deprecated strcpy() [1] followed by strlen() with sysfs_emit(). Leave validate_show() and the variable-length hv-gpci helpers unchanged since they already have explicit bounds handling, and converting those would be more than a mechanical conversion. [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy Signed-off-by: Thorsten Blum Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260524130002.793476-2-thorsten.blum@linux.dev --- arch/powerpc/kernel/cacheinfo.c | 13 +++++---- arch/powerpc/kernel/eeh_sysfs.c | 8 ++--- arch/powerpc/kernel/fadump.c | 10 +++---- arch/powerpc/kernel/iommu.c | 3 +- arch/powerpc/kernel/security.c | 13 +++++---- arch/powerpc/kernel/sysfs.c | 23 ++++++++------- arch/powerpc/perf/core-book3s.c | 3 +- arch/powerpc/perf/hv-24x7.c | 18 +++++------- arch/powerpc/perf/hv-gpci.c | 5 ++-- arch/powerpc/perf/kvm-hv-pmu.c | 3 +- arch/powerpc/perf/vpa-pmu.c | 3 +- .../powerpc/platforms/83xx/mcu_mpc8349emitx.c | 3 +- arch/powerpc/platforms/cell/spu_base.c | 5 ++-- arch/powerpc/platforms/powernv/idle.c | 3 +- arch/powerpc/platforms/powernv/opal-dump.c | 11 +++---- arch/powerpc/platforms/powernv/opal-elog.c | 9 +++--- arch/powerpc/platforms/powernv/opal-flash.c | 4 +-- .../powerpc/platforms/powernv/opal-powercap.c | 12 +++----- arch/powerpc/platforms/powernv/opal-psr.c | 12 +++----- arch/powerpc/platforms/powernv/subcore.c | 3 +- arch/powerpc/platforms/ps3/setup.c | 3 +- arch/powerpc/platforms/pseries/cmm.c | 5 ++-- arch/powerpc/platforms/pseries/dlpar.c | 3 +- arch/powerpc/platforms/pseries/ibmebus.c | 5 ++-- arch/powerpc/platforms/pseries/papr_scm.c | 5 ++-- arch/powerpc/platforms/pseries/power.c | 3 +- .../platforms/pseries/pseries_energy.c | 3 +- arch/powerpc/platforms/pseries/suspend.c | 3 +- arch/powerpc/platforms/pseries/vas-sysfs.c | 3 +- arch/powerpc/platforms/pseries/vio.c | 29 +++++++++---------- arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c | 3 +- 31 files changed, 118 insertions(+), 111 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 90d51d9b3ed2..04e5ea38bdc0 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -596,7 +597,7 @@ static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *b if (cache_size_kb(cache, &size_kb)) return -ENODEV; - return sprintf(buf, "%uK\n", size_kb); + return sysfs_emit(buf, "%uK\n", size_kb); } static struct kobj_attribute cache_size_attr = @@ -613,7 +614,7 @@ static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, ch if (cache_get_line_size(cache, &line_size)) return -ENODEV; - return sprintf(buf, "%u\n", line_size); + return sysfs_emit(buf, "%u\n", line_size); } static struct kobj_attribute cache_line_size_attr = @@ -629,7 +630,7 @@ static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char if (cache_nr_sets(cache, &nr_sets)) return -ENODEV; - return sprintf(buf, "%u\n", nr_sets); + return sysfs_emit(buf, "%u\n", nr_sets); } static struct kobj_attribute cache_nr_sets_attr = @@ -645,7 +646,7 @@ static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr if (cache_associativity(cache, &associativity)) return -ENODEV; - return sprintf(buf, "%u\n", associativity); + return sysfs_emit(buf, "%u\n", associativity); } static struct kobj_attribute cache_assoc_attr = @@ -657,7 +658,7 @@ static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *b cache = index_kobj_to_cache(k); - return sprintf(buf, "%s\n", cache_type_string(cache)); + return sysfs_emit(buf, "%s\n", cache_type_string(cache)); } static struct kobj_attribute cache_type_attr = @@ -671,7 +672,7 @@ static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char * index = kobj_to_cache_index_dir(k); cache = index->cache; - return sprintf(buf, "%d\n", cache->level); + return sysfs_emit(buf, "%d\n", cache->level); } static struct kobj_attribute cache_level_attr = diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c index 706e1eb95efe..b9785f105f75 100644 --- a/arch/powerpc/kernel/eeh_sysfs.c +++ b/arch/powerpc/kernel/eeh_sysfs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,7 @@ static ssize_t eeh_show_##_name(struct device *dev, \ if (!edev) \ return 0; \ \ - return sprintf(buf, _format "\n", edev->_memb); \ + return sysfs_emit(buf, _format "\n", edev->_memb); \ } \ static DEVICE_ATTR(_name, 0444, eeh_show_##_name, NULL); @@ -49,8 +50,7 @@ static ssize_t eeh_pe_state_show(struct device *dev, return -ENODEV; state = eeh_ops->get_state(edev->pe, NULL); - return sprintf(buf, "0x%08x 0x%08x\n", - state, edev->pe->state); + return sysfs_emit(buf, "0x%08x 0x%08x\n", state, edev->pe->state); } static ssize_t eeh_pe_state_store(struct device *dev, @@ -87,7 +87,7 @@ static ssize_t eeh_notify_resume_show(struct device *dev, if (!edev || !edev->pe) return -ENODEV; - return sprintf(buf, "%d\n", pdn->last_allow_rc); + return sysfs_emit(buf, "%d\n", pdn->last_allow_rc); } static ssize_t eeh_notify_resume_store(struct device *dev, diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 501d43bf18f3..a313b1653124 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -1422,7 +1422,7 @@ static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", fw_dump.fadump_enabled); + return sysfs_emit(buf, "%d\n", fw_dump.fadump_enabled); } /* @@ -1434,28 +1434,28 @@ static ssize_t hotplug_ready_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", 1); + return sysfs_emit(buf, "%d\n", 1); } static ssize_t mem_reserved_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%ld\n", fw_dump.reserve_dump_area_size); + return sysfs_emit(buf, "%ld\n", fw_dump.reserve_dump_area_size); } static ssize_t registered_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", fw_dump.dump_registered); + return sysfs_emit(buf, "%d\n", fw_dump.dump_registered); } static ssize_t bootargs_append_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", (char *)__va(fw_dump.param_area)); + return sysfs_emit(buf, "%s\n", (char *)__va(fw_dump.param_area)); } static ssize_t bootargs_append_store(struct kobject *kobj, diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index d122e8447831..ee1b5cb557c9 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -141,7 +142,7 @@ late_initcall(fail_iommu_debugfs); static ssize_t fail_iommu_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->archdata.fail_iommu); + return sysfs_emit(buf, "%d\n", dev->archdata.fail_iommu); } static ssize_t fail_iommu_store(struct device *dev, diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index fbb7ebd8aa08..600596cb4ffb 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -163,13 +164,13 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, cha } if (thread_priv) - return sprintf(buf, "Vulnerable: L1D private per thread\n"); + return sysfs_emit(buf, "Vulnerable: L1D private per thread\n"); if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf) @@ -352,14 +353,14 @@ ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute * default: type = "unknown"; } - return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); + return sysfs_emit(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); } if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } static int ssb_prctl_get(struct task_struct *task) diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 6b3dd6decdf9..329c1690b5ed 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ static ssize_t show_smt_snooze_delay(struct device *dev, { pr_warn_once("%s (%d) read from unsupported smt_snooze_delay\n", current->comm, current->pid); - return sprintf(buf, "100\n"); + return sysfs_emit(buf, "100\n"); } static DEVICE_ATTR(smt_snooze_delay, 0644, show_smt_snooze_delay, @@ -100,7 +101,7 @@ static ssize_t show_##NAME(struct device *dev, \ struct cpu *cpu = container_of(dev, struct cpu, dev); \ unsigned long val; \ smp_call_function_single(cpu->dev.id, read_##NAME, &val, 1); \ - return sprintf(buf, "%lx\n", val); \ + return sysfs_emit(buf, "%lx\n", val); \ } \ static ssize_t __used \ store_##NAME(struct device *dev, struct device_attribute *attr, \ @@ -183,7 +184,7 @@ static void add_write_permission_dev_attr(struct device_attribute *attr) static ssize_t show_dscr_default(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lx\n", dscr_default); + return sysfs_emit(buf, "%lx\n", dscr_default); } /** @@ -272,7 +273,7 @@ static ssize_t show_pw20_state(struct device *dev, value &= PWRMGTCR0_PW20_WAIT; - return sprintf(buf, "%u\n", value ? 1 : 0); + return sysfs_emit(buf, "%u\n", value ? 1 : 0); } static void do_store_pw20_state(void *val) @@ -337,7 +338,7 @@ static ssize_t show_pw20_wait_time(struct device *dev, time = pw20_wt; } - return sprintf(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); } static void set_pw20_wait_entry_bit(void *val) @@ -394,7 +395,7 @@ static ssize_t show_altivec_idle(struct device *dev, value &= PWRMGTCR0_AV_IDLE_PD_EN; - return sprintf(buf, "%u\n", value ? 1 : 0); + return sysfs_emit(buf, "%u\n", value ? 1 : 0); } static void do_store_altivec_idle(void *val) @@ -459,7 +460,7 @@ static ssize_t show_altivec_idle_wait_time(struct device *dev, time = altivec_idle_wt; } - return sprintf(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); } static void set_altivec_idle_wait_entry_bit(void *val) @@ -746,7 +747,7 @@ static struct device_attribute pa6t_attrs[] = { #ifdef CONFIG_PPC_SVM static ssize_t show_svm(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", is_secure_guest()); + return sysfs_emit(buf, "%u\n", is_secure_guest()); } static DEVICE_ATTR(svm, 0444, show_svm, NULL); @@ -780,7 +781,7 @@ static ssize_t idle_purr_show(struct device *dev, u64 val; smp_call_function_single(cpu->dev.id, read_idle_purr, &val, 1); - return sprintf(buf, "%llx\n", val); + return sysfs_emit(buf, "%llx\n", val); } static DEVICE_ATTR(idle_purr, 0400, idle_purr_show, NULL); @@ -810,7 +811,7 @@ static ssize_t idle_spurr_show(struct device *dev, u64 val; smp_call_function_single(cpu->dev.id, read_idle_spurr, &val, 1); - return sprintf(buf, "%llx\n", val); + return sysfs_emit(buf, "%llx\n", val); } static DEVICE_ATTR(idle_spurr, 0400, idle_spurr_show, NULL); @@ -1143,7 +1144,7 @@ static ssize_t show_physical_id(struct device *dev, { struct cpu *cpu = container_of(dev, struct cpu, dev); - return sprintf(buf, "%d\n", get_hard_smp_processor_id(cpu->dev.id)); + return sysfs_emit(buf, "%d\n", get_hard_smp_processor_id(cpu->dev.id)); } static DEVICE_ATTR(physical_id, 0444, show_physical_id, NULL); diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 802bc9b7ef6f..720b1a500922 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -2204,7 +2205,7 @@ ssize_t power_events_sysfs_show(struct device *dev, pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } static struct pmu power_pmu = { diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 243c0a1c8cda..abb4cfb11fcc 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -434,19 +435,19 @@ static ssize_t cpumask_show(struct device *dev, static ssize_t sockets_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_sockets); + return sysfs_emit(buf, "%d\n", phys_sockets); } static ssize_t chipspersocket_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_chipspersocket); + return sysfs_emit(buf, "%d\n", phys_chipspersocket); } static ssize_t coresperchip_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_coresperchip); + return sysfs_emit(buf, "%d\n", phys_coresperchip); } static struct attribute *device_str_attr_create_(char *name, char *str) @@ -1061,7 +1062,7 @@ static ssize_t catalog_read(struct file *filp, struct kobject *kobj, static ssize_t domains_show(struct device *dev, struct device_attribute *attr, char *page) { - int d, n, count = 0; + int d, count = 0; const char *str; for (d = 0; d < HV_PERF_DOMAIN_MAX; d++) { @@ -1069,12 +1070,7 @@ static ssize_t domains_show(struct device *dev, struct device_attribute *attr, if (!str) continue; - n = sprintf(page, "%d: %s\n", d, str); - if (n < 0) - break; - - count += n; - page += n; + count += sysfs_emit_at(page, count, "%d: %s\n", d, str); } return count; } @@ -1095,7 +1091,7 @@ static ssize_t _name##_show(struct device *dev, \ ret = -EIO; \ goto e_free; \ } \ - ret = sprintf(buf, _fmt, _expr); \ + ret = sysfs_emit(buf, _fmt, _expr); \ e_free: \ kmem_cache_free(hv_page_cache, page); \ return ret; \ diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c index 10c82cf8f5b3..7269273d3aa8 100644 --- a/arch/powerpc/perf/hv-gpci.c +++ b/arch/powerpc/perf/hv-gpci.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -85,7 +86,7 @@ static ssize_t _name##_show(struct device *dev, \ if (hret) \ return -EIO; \ \ - return sprintf(page, _format, caps._name); \ + return sysfs_emit(page, _format, caps._name); \ } \ static struct device_attribute hv_caps_attr_##_name = __ATTR_RO(_name) @@ -93,7 +94,7 @@ static ssize_t kernel_version_show(struct device *dev, struct device_attribute *attr, char *page) { - return sprintf(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); + return sysfs_emit(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); } static ssize_t cpumask_show(struct device *dev, diff --git a/arch/powerpc/perf/kvm-hv-pmu.c b/arch/powerpc/perf/kvm-hv-pmu.c index ae264c9080ef..aa72b96b5a8c 100644 --- a/arch/powerpc/perf/kvm-hv-pmu.c +++ b/arch/powerpc/perf/kvm-hv-pmu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -48,7 +49,7 @@ static ssize_t kvmppc_events_sysfs_show(struct device *dev, struct perf_pmu_events_attr *pmu_attr; pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } /* Holds the hostwide stats */ diff --git a/arch/powerpc/perf/vpa-pmu.c b/arch/powerpc/perf/vpa-pmu.c index 840733468959..bff4cfab7b94 100644 --- a/arch/powerpc/perf/vpa-pmu.c +++ b/arch/powerpc/perf/vpa-pmu.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -26,7 +27,7 @@ static ssize_t vpa_pmu_events_sysfs_show(struct device *dev, pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } #define VPA_PMU_EVENT_ATTR(_name, _id) \ diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 9b693594a5f7..c3fbec1f1d24 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -77,7 +78,7 @@ static ssize_t show_status(struct device *d, return -ENODEV; mcu->reg_ctrl = ret; - return sprintf(buf, "%02x\n", ret); + return sysfs_emit(buf, "%02x\n", ret); } static DEVICE_ATTR(status, 0444, show_status, NULL); diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 0ec7b3bdda56..8452153d4650 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -638,8 +639,8 @@ static ssize_t spu_stat_show(struct device *dev, { struct spu *spu = container_of(dev, struct spu, dev); - return sprintf(buf, "%s %llu %llu %llu %llu " - "%llu %llu %llu %llu %llu %llu %llu %llu\n", + return sysfs_emit(buf, + "%s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", spu_state_names[spu->stats.util_state], spu_acct_time(spu, SPU_UTIL_USER), spu_acct_time(spu, SPU_UTIL_SYSTEM), diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 6cd461f82968..33103a98cfd5 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -171,7 +172,7 @@ static u8 fastsleep_workaround_applyonce; static ssize_t show_fastsleep_workaround_applyonce(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", fastsleep_workaround_applyonce); + return sysfs_emit(buf, "%u\n", fastsleep_workaround_applyonce); } static ssize_t store_fastsleep_workaround_applyonce(struct device *dev, diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c index 2e4bffa74163..0586821d7af4 100644 --- a/arch/powerpc/platforms/powernv/opal-dump.c +++ b/arch/powerpc/platforms/powernv/opal-dump.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ static ssize_t dump_id_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "0x%x\n", dump_obj->id); + return sysfs_emit(buf, "0x%x\n", dump_obj->id); } static const char* dump_type_to_string(uint32_t type) @@ -58,15 +59,15 @@ static ssize_t dump_type_show(struct dump_obj *dump_obj, char *buf) { - return sprintf(buf, "0x%x %s\n", dump_obj->type, - dump_type_to_string(dump_obj->type)); + return sysfs_emit(buf, "0x%x %s\n", dump_obj->type, + dump_type_to_string(dump_obj->type)); } static ssize_t dump_ack_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "ack - acknowledge dump\n"); + return sysfs_emit(buf, "ack - acknowledge dump\n"); } /* @@ -114,7 +115,7 @@ static ssize_t init_dump_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "1 - initiate Service Processor(FSP) dump\n"); + return sysfs_emit(buf, "1 - initiate Service Processor(FSP) dump\n"); } static int64_t dump_fips_init(uint8_t type) diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c index 2b8331922ab9..6cacd3fd3cd5 100644 --- a/arch/powerpc/platforms/powernv/opal-elog.c +++ b/arch/powerpc/platforms/powernv/opal-elog.c @@ -40,7 +40,7 @@ static ssize_t elog_id_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "0x%llx\n", elog_obj->id); + return sysfs_emit(buf, "0x%llx\n", elog_obj->id); } static const char *elog_type_to_string(uint64_t type) @@ -55,16 +55,15 @@ static ssize_t elog_type_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "0x%llx %s\n", - elog_obj->type, - elog_type_to_string(elog_obj->type)); + return sysfs_emit(buf, "0x%llx %s\n", elog_obj->type, + elog_type_to_string(elog_obj->type)); } static ssize_t elog_ack_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "ack - acknowledge log message\n"); + return sysfs_emit(buf, "ack - acknowledge log message\n"); } static ssize_t elog_ack_store(struct elog_obj *elog_obj, diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c index a3f7a2928767..5ca5f6329a2d 100644 --- a/arch/powerpc/platforms/powernv/opal-flash.c +++ b/arch/powerpc/platforms/powernv/opal-flash.c @@ -238,7 +238,7 @@ static ssize_t manage_show(struct kobject *kobj, struct manage_flash_t *const args_buf = &manage_flash_data; int rc; - rc = sprintf(buf, "%d\n", args_buf->status); + rc = sysfs_emit(buf, "%d\n", args_buf->status); /* Set status to default*/ args_buf->status = FLASH_NO_OP; return rc; @@ -321,7 +321,7 @@ static ssize_t update_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct update_flash_t *const args_buf = &update_flash_data; - return sprintf(buf, "%d\n", args_buf->status); + return sysfs_emit(buf, "%d\n", args_buf->status); } /* diff --git a/arch/powerpc/platforms/powernv/opal-powercap.c b/arch/powerpc/platforms/powernv/opal-powercap.c index 9bb73cb42a65..bf18b333281e 100644 --- a/arch/powerpc/platforms/powernv/opal-powercap.c +++ b/arch/powerpc/platforms/powernv/opal-powercap.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -56,16 +57,11 @@ static ssize_t powercap_show(struct kobject *kobj, struct kobj_attribute *attr, goto out; } ret = opal_error_code(opal_get_async_rc(msg)); - if (!ret) { - ret = sprintf(buf, "%u\n", be32_to_cpu(pcap)); - if (ret < 0) - ret = -EIO; - } + if (!ret) + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); break; case OPAL_SUCCESS: - ret = sprintf(buf, "%u\n", be32_to_cpu(pcap)); - if (ret < 0) - ret = -EIO; + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); break; default: ret = opal_error_code(ret); diff --git a/arch/powerpc/platforms/powernv/opal-psr.c b/arch/powerpc/platforms/powernv/opal-psr.c index 24d0a894d965..19228181cb6f 100644 --- a/arch/powerpc/platforms/powernv/opal-psr.c +++ b/arch/powerpc/platforms/powernv/opal-psr.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -50,16 +51,11 @@ static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr, goto out; } ret = opal_error_code(opal_get_async_rc(msg)); - if (!ret) { - ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); - if (ret < 0) - ret = -EIO; - } + if (!ret) + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(psr)); break; case OPAL_SUCCESS: - ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); - if (ret < 0) - ret = -EIO; + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(psr)); break; default: ret = opal_error_code(ret); diff --git a/arch/powerpc/platforms/powernv/subcore.c b/arch/powerpc/platforms/powernv/subcore.c index 393e747541fb..f7668ef1ac1f 100644 --- a/arch/powerpc/platforms/powernv/subcore.c +++ b/arch/powerpc/platforms/powernv/subcore.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -409,7 +410,7 @@ static ssize_t __used store_subcores_per_core(struct device *dev, static ssize_t show_subcores_per_core(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%x\n", subcores_per_core); + return sysfs_emit(buf, "%x\n", subcores_per_core); } static DEVICE_ATTR(subcores_per_core, 0644, diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 150c09b58ae8..ca2608a70f4d 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -183,7 +184,7 @@ static int ps3_set_dabr(unsigned long dabr, unsigned long dabrx) static ssize_t ps3_fw_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%s", ps3_firmware_version_str); + return sysfs_emit(buf, "%s\n", ps3_firmware_version_str); } static int __init ps3_setup_sysfs(void) diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 8d83df12430f..38e22125b96f 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -333,7 +334,7 @@ static int cmm_thread(void *dummy) struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, format, ##args); \ + return sysfs_emit(buf, format, ##args); \ } \ static DEVICE_ATTR(name, 0444, show_##name, NULL) @@ -343,7 +344,7 @@ CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target)); static ssize_t show_oom_pages(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages)); + return sysfs_emit(buf, "%lu\n", PAGES2KB(oom_freed_pages)); } static ssize_t store_oom_pages(struct device *dev, diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index a7c451c2507d..f4d33b8dffd8 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "of_helpers.h" @@ -798,7 +799,7 @@ static ssize_t dlpar_store(const struct class *class, const struct class_attribu static ssize_t dlpar_show(const struct class *class, const struct class_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", "memory,cpu,dt"); + return sysfs_emit(buf, "%s\n", "memory,cpu,dt"); } static CLASS_ATTR_RW(dlpar); diff --git a/arch/powerpc/platforms/pseries/ibmebus.c b/arch/powerpc/platforms/pseries/ibmebus.c index cad2deb7e70d..2d0f991da2c8 100644 --- a/arch/powerpc/platforms/pseries/ibmebus.c +++ b/arch/powerpc/platforms/pseries/ibmebus.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -399,7 +400,7 @@ static ssize_t devspec_show(struct device *dev, struct platform_device *ofdev; ofdev = to_platform_device(dev); - return sprintf(buf, "%pOF\n", ofdev->dev.of_node); + return sysfs_emit(buf, "%pOF\n", ofdev->dev.of_node); } static DEVICE_ATTR_RO(devspec); @@ -409,7 +410,7 @@ static ssize_t name_show(struct device *dev, struct platform_device *ofdev; ofdev = to_platform_device(dev); - return sprintf(buf, "%pOFn\n", ofdev->dev.of_node); + return sysfs_emit(buf, "%pOFn\n", ofdev->dev.of_node); } static DEVICE_ATTR_RO(name); diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 63eca4ebb5e5..75da96c08cdd 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1062,8 +1063,8 @@ static ssize_t health_bitmap_inject_show(struct device *dev, struct nvdimm *dimm = to_nvdimm(dev); struct papr_scm_priv *p = nvdimm_provider_data(dimm); - return sprintf(buf, "%#llx\n", - READ_ONCE(p->health_bitmap_inject_mask)); + return sysfs_emit(buf, "%#llx\n", + READ_ONCE(p->health_bitmap_inject_mask)); } static DEVICE_ATTR_ADMIN_RO(health_bitmap_inject); diff --git a/arch/powerpc/platforms/pseries/power.c b/arch/powerpc/platforms/pseries/power.c index 3676cb297767..7b9dfe829f25 100644 --- a/arch/powerpc/platforms/pseries/power.c +++ b/arch/powerpc/platforms/pseries/power.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -22,7 +23,7 @@ unsigned long rtas_poweron_auto; /* default and normal state is 0 */ static ssize_t auto_poweron_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", rtas_poweron_auto); + return sysfs_emit(buf, "%lu\n", rtas_poweron_auto); } static ssize_t auto_poweron_store(struct kobject *kobj, diff --git a/arch/powerpc/platforms/pseries/pseries_energy.c b/arch/powerpc/platforms/pseries/pseries_energy.c index 2c661b798235..fdaf85ecd39b 100644 --- a/arch/powerpc/platforms/pseries/pseries_energy.c +++ b/arch/powerpc/platforms/pseries/pseries_energy.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -242,7 +243,7 @@ static ssize_t get_best_energy_data(struct device *dev, if (rc != H_SUCCESS) return -EINVAL; - return sprintf(page, "%lu\n", retbuf[1] >> 32); + return sysfs_emit(page, "%lu\n", retbuf[1] >> 32); } /* Wrapper functions */ diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c index c51db63d3e88..a9928d75624a 100644 --- a/arch/powerpc/platforms/pseries/suspend.c +++ b/arch/powerpc/platforms/pseries/suspend.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -121,7 +122,7 @@ static ssize_t show_hibernate(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", KERN_DT_UPDATE); + return sysfs_emit(buf, "%d\n", KERN_DT_UPDATE); } static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate); diff --git a/arch/powerpc/platforms/pseries/vas-sysfs.c b/arch/powerpc/platforms/pseries/vas-sysfs.c index 4f6fbbb672ae..00c6ffd3ef39 100644 --- a/arch/powerpc/platforms/pseries/vas-sysfs.c +++ b/arch/powerpc/platforms/pseries/vas-sysfs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "vas.h" @@ -58,7 +59,7 @@ static ssize_t update_total_credits_store(struct vas_cop_feat_caps *caps, #define sysfs_caps_entry_read(_name) \ static ssize_t _name##_show(struct vas_cop_feat_caps *caps, char *buf) \ { \ - return sprintf(buf, "%d\n", atomic_read(&caps->_name)); \ + return sysfs_emit(buf, "%d\n", atomic_read(&caps->_name)); \ } struct vas_sysfs_entry { diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 08e2add48adb..572bdf42335e 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -942,14 +943,14 @@ static ssize_t cmo_##name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, "%lu\n", to_vio_dev(dev)->cmo.name); \ + return sysfs_emit(buf, "%lu\n", to_vio_dev(dev)->cmo.name); \ } static ssize_t cmo_allocs_failed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vio_dev *viodev = to_vio_dev(dev); - return sprintf(buf, "%d\n", atomic_read(&viodev->cmo.allocs_failed)); + return sysfs_emit(buf, "%d\n", atomic_read(&viodev->cmo.allocs_failed)); } static ssize_t cmo_allocs_failed_store(struct device *dev, @@ -998,7 +999,7 @@ static DEVICE_ATTR_RW(cmo_allocs_failed); #define viobus_cmo_rd_attr(name) \ static ssize_t cmo_bus_##name##_show(const struct bus_type *bt, char *buf) \ { \ - return sprintf(buf, "%lu\n", vio_cmo.name); \ + return sysfs_emit(buf, "%lu\n", vio_cmo.name); \ } \ static struct bus_attribute bus_attr_cmo_bus_##name = \ __ATTR(cmo_##name, S_IRUGO, cmo_bus_##name##_show, NULL) @@ -1007,7 +1008,7 @@ static struct bus_attribute bus_attr_cmo_bus_##name = \ static ssize_t \ cmo_##name##_##var##_show(const struct bus_type *bt, char *buf) \ { \ - return sprintf(buf, "%lu\n", vio_cmo.name.var); \ + return sysfs_emit(buf, "%lu\n", vio_cmo.name.var); \ } \ static BUS_ATTR_RO(cmo_##name##_##var) @@ -1022,7 +1023,7 @@ viobus_cmo_pool_rd_attr(excess, free); static ssize_t cmo_high_show(const struct bus_type *bt, char *buf) { - return sprintf(buf, "%lu\n", vio_cmo.high); + return sysfs_emit(buf, "%lu\n", vio_cmo.high); } static ssize_t cmo_high_store(const struct bus_type *bt, const char *buf, @@ -1535,7 +1536,7 @@ machine_device_initcall(pseries, vio_device_init); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", to_vio_dev(dev)->name); + return sysfs_emit(buf, "%s\n", to_vio_dev(dev)->name); } static DEVICE_ATTR_RO(name); @@ -1544,7 +1545,7 @@ static ssize_t devspec_show(struct device *dev, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%pOF\n", of_node); + return sysfs_emit(buf, "%pOF\n", of_node); } static DEVICE_ATTR_RO(devspec); @@ -1556,17 +1557,13 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, const char *cp; dn = dev->of_node; - if (!dn) { - strcpy(buf, "\n"); - return strlen(buf); - } + if (!dn) + return sysfs_emit(buf, "\n"); cp = of_get_property(dn, "compatible", NULL); - if (!cp) { - strcpy(buf, "\n"); - return strlen(buf); - } + if (!cp) + return sysfs_emit(buf, "\n"); - return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp); + return sysfs_emit(buf, "vio:T%sS%s\n", vio_dev->type, cp); } static DEVICE_ATTR_RO(modalias); diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c index f9e64f54dc14..f63b89adf9f3 100644 --- a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c +++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,7 @@ static ssize_t fsl_timer_wakeup_show(struct device *dev, } mutex_unlock(&sysfs_lock); - return sprintf(buf, "%lld\n", interval); + return sysfs_emit(buf, "%lld\n", interval); } static ssize_t fsl_timer_wakeup_store(struct device *dev, From ab8bbf8024b7434e2b630965fd373fba5b89f29f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 13 Aug 2025 12:30:59 +0200 Subject: [PATCH 2491/5214] powerpc/rtas: Replace one-element array with flexible array member Replace the deprecated one-element array with a modern flexible array member in the struct rtas_error_log and add the __counted_by_be() compiler attribute to improve access bounds-checking via CONFIG_UBSAN_BOUNDS and CONFIG_FORTIFY_SOURCE. Link: https://github.com/KSPP/linux/issues/79 Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20250813103101.163698-2-thorsten.blum@linux.dev --- arch/powerpc/include/asm/rtas-types.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/rtas-types.h b/arch/powerpc/include/asm/rtas-types.h index 9d5b16803cbb..5d40d187b965 100644 --- a/arch/powerpc/include/asm/rtas-types.h +++ b/arch/powerpc/include/asm/rtas-types.h @@ -42,8 +42,9 @@ struct rtas_error_log { */ u8 byte3; /* General event or error*/ __be32 extended_log_length; /* length in bytes */ - unsigned char buffer[1]; /* Start of extended log */ - /* Variable length. */ + + /* Start of extended log, variable length */ + unsigned char buffer[] __counted_by_be(extended_log_length); }; /* RTAS general extended event log, Version 6. The extended log starts From 457cf3d00dab71b7e74603b6ee96e13c51a8fd3d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 17 May 2025 14:58:31 +0200 Subject: [PATCH 2492/5214] powerpc: Fix indentation and replace typedef with struct name Indent several struct members using tabs instead of spaces. Replace the typedef alias AOUTHDR with an explicit struct name to silence a checkpatch warning. Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20250517125834.421088-2-thorsten.blum@linux.dev --- arch/powerpc/boot/hack-coff.c | 2 +- arch/powerpc/boot/rs6000.h | 84 +++++++++++++++++------------------ 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/arch/powerpc/boot/hack-coff.c b/arch/powerpc/boot/hack-coff.c index a010e124ac4b..6bf0c94302f5 100644 --- a/arch/powerpc/boot/hack-coff.c +++ b/arch/powerpc/boot/hack-coff.c @@ -31,7 +31,7 @@ main(int ac, char **av) int i, nsect; int aoutsz; struct external_filehdr fhdr; - AOUTHDR aout; + struct aouthdr aout; struct external_scnhdr shdr; if (ac != 2) { diff --git a/arch/powerpc/boot/rs6000.h b/arch/powerpc/boot/rs6000.h index 16df8f3c43f1..970754a1f887 100644 --- a/arch/powerpc/boot/rs6000.h +++ b/arch/powerpc/boot/rs6000.h @@ -32,32 +32,30 @@ struct external_filehdr { /********************** AOUT "OPTIONAL HEADER" **********************/ -typedef struct -{ - unsigned char magic[2]; /* type of file */ - unsigned char vstamp[2]; /* version stamp */ - unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ - unsigned char dsize[4]; /* initialized data " " */ - unsigned char bsize[4]; /* uninitialized data " " */ - unsigned char entry[4]; /* entry pt. */ - unsigned char text_start[4]; /* base of text used for this file */ - unsigned char data_start[4]; /* base of data used for this file */ - unsigned char o_toc[4]; /* address of TOC */ - unsigned char o_snentry[2]; /* section number of entry point */ - unsigned char o_sntext[2]; /* section number of .text section */ - unsigned char o_sndata[2]; /* section number of .data section */ - unsigned char o_sntoc[2]; /* section number of TOC */ - unsigned char o_snloader[2]; /* section number of .loader section */ - unsigned char o_snbss[2]; /* section number of .bss section */ - unsigned char o_algntext[2]; /* .text alignment */ - unsigned char o_algndata[2]; /* .data alignment */ - unsigned char o_modtype[2]; /* module type (??) */ - unsigned char o_cputype[2]; /* cpu type */ - unsigned char o_maxstack[4]; /* max stack size (??) */ - unsigned char o_maxdata[4]; /* max data size (??) */ - unsigned char o_resv2[12]; /* reserved */ -} -AOUTHDR; +struct aouthdr { + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +}; #define AOUTSZ 72 #define SMALL_AOUTSZ (28) @@ -115,10 +113,10 @@ struct external_scnhdr { */ struct external_lineno { union { - char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0 */ char l_paddr[4]; /* (physical) address of line number */ } l_addr; - char l_lnno[2]; /* line number */ + char l_lnno[2]; /* line number */ }; @@ -132,20 +130,19 @@ struct external_lineno { #define E_FILNMLEN 14 /* # characters in a file name */ #define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ -struct external_syment -{ - union { - char e_name[E_SYMNMLEN]; - struct { - char e_zeroes[4]; - char e_offset[4]; - } e; - } e; - char e_value[4]; - char e_scnum[2]; - char e_type[2]; - char e_sclass[1]; - char e_numaux[1]; +struct external_syment { + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; }; @@ -187,7 +184,7 @@ union external_auxent { } x_file; struct { - char x_scnlen[4]; /* section length */ + char x_scnlen[4]; /* section length */ char x_nreloc[2]; /* # relocation entries */ char x_nlinno[2]; /* # line numbers */ } x_scn; @@ -207,7 +204,6 @@ union external_auxent { unsigned char x_stab[4]; unsigned char x_snstab[2]; } x_csect; - }; #define SYMENT struct external_syment From c8f80f95da32618d49c2a068ee561b85655b761d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 13 Oct 2025 15:57:02 +0200 Subject: [PATCH 2493/5214] powerpc/pseries/lparcfg: Replace deprecated strcpy in parse_system_parameter_string strcpy() is deprecated; use strscpy() instead. Use the return value of strscpy() instead of calling strlen() again, and ignore any potential string truncation since both strings are much shorter than the buffer size SPLPAR_MAXLENGTH. Change both if conditions to silence the following checkpatch warnings: Comparisons should place the constant on the right side of the test Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20251013135703.97260-2-thorsten.blum@linux.dev --- arch/powerpc/platforms/pseries/lparcfg.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c index 8821c378bfff..54b7ecf375b5 100644 --- a/arch/powerpc/platforms/pseries/lparcfg.c +++ b/arch/powerpc/platforms/pseries/lparcfg.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -431,17 +432,18 @@ static void parse_system_parameter_string(struct seq_file *m) w_idx = 0; } else if (local_buffer[idx] == '=') { /* code here to replace workbuffer contents - with different keyword strings */ - if (0 == strcmp(workbuffer, "MaxEntCap")) { - strcpy(workbuffer, - "partition_max_entitled_capacity"); - w_idx = strlen(workbuffer); - } - if (0 == strcmp(workbuffer, "MaxPlatProcs")) { - strcpy(workbuffer, - "system_potential_processors"); - w_idx = strlen(workbuffer); - } + * with different keyword strings. Truncation + * by strscpy is deliberately ignored because + * SPLPAR_MAXLENGTH >= maximum string size. + */ + if (!strcmp(workbuffer, "MaxEntCap")) + w_idx = strscpy(workbuffer, + "partition_max_entitled_capacity", + SPLPAR_MAXLENGTH); + if (!strcmp(workbuffer, "MaxPlatProcs")) + w_idx = strscpy(workbuffer, + "system_potential_processors", + SPLPAR_MAXLENGTH); } } kfree(workbuffer); From d8a4cef90b1a4ae9196a5bfba683eb9a0c75acdc Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:09 +0000 Subject: [PATCH 2494/5214] clk: spacemit: k3: Switch to pll2_d6 as parent for PCIe clock According to SpacemiT updated docs, the PCIe master and slave clock's parent is the pll2_d6 clock, so fix it. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-1-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index e98afd59f05c..8f0b743046ab 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -947,16 +947,16 @@ static const struct clk_parent_data edp1_pclk_parents[] = { }; CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT(17), 0); -CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); -CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); -CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); -CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); -CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); -CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); -CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); -CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); -CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); -CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); +CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); +CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); +CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); +CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); +CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), From 2f20c859a82a291483a8b3f01cbfbb1642782a14 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:10 +0000 Subject: [PATCH 2495/5214] clk: spacemit: k3: Fix PCIe clock register offset The offset of PCIe Clock CTRL register for port B and C controller was wrongly swapped, correct it here. Fixes: 091d19cc2401 ("clk: spacemit: k3: extract common header") Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-2-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan --- include/soc/spacemit/k3-syscon.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h index 0299bea065a0..a68255dd641f 100644 --- a/include/soc/spacemit/k3-syscon.h +++ b/include/soc/spacemit/k3-syscon.h @@ -168,8 +168,8 @@ #define APMU_CPU_C2_CLK_CTRL 0x394 #define APMU_CPU_C3_CLK_CTRL 0x208 #define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 -#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 -#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1c8 #define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 #define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 #define APMU_EMAC0_CLK_RES_CTRL 0x3e4 From 7a2db70a3196717bfb01415b6dde1f90d4291ab1 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:11 +0000 Subject: [PATCH 2496/5214] dt-bindings: soc: spacemit: k3: Add PCIe DBI clock IDs Add clock IDs of PCIe DBI (Data Bus Interface) clock. Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-3-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan --- include/dt-bindings/clock/spacemit,k3-clocks.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dt-bindings/clock/spacemit,k3-clocks.h b/include/dt-bindings/clock/spacemit,k3-clocks.h index b22336f3ae40..dfae52547cda 100644 --- a/include/dt-bindings/clock/spacemit,k3-clocks.h +++ b/include/dt-bindings/clock/spacemit,k3-clocks.h @@ -380,6 +380,11 @@ #define CLK_APMU_ISIM_VCLK1 86 #define CLK_APMU_ISIM_VCLK2 87 #define CLK_APMU_ISIM_VCLK3 88 +#define CLK_APMU_PCIE_PORTA_DBI 89 +#define CLK_APMU_PCIE_PORTB_DBI 90 +#define CLK_APMU_PCIE_PORTC_DBI 91 +#define CLK_APMU_PCIE_PORTD_DBI 92 +#define CLK_APMU_PCIE_PORTE_DBI 93 /* DCIU clocks */ #define CLK_DCIU_HDMA 0 From a37c75d7b5bb7f3344b0e639b313ac5ace6244ff Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 11 May 2026 02:59:12 +0000 Subject: [PATCH 2497/5214] clk: spacemit: k3: Add PCIe DBI clock Add PCIe DBI (Data Bus Interface) clock which was missing, This will support PCIe driver to explicitly request and enable all clocks that needed. Link: https://patch.msgid.link/20260511-06-pci-clk-fix-v2-4-c9a5e563bab3@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 8f0b743046ab..196d32194125 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -949,14 +949,19 @@ CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pciea_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(0), 0); CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(0), 0); CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pciec_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(0), 0); CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pcied_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(0), 0); CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciee_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(0), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), @@ -1391,14 +1396,19 @@ static struct clk_hw *k3_ccu_apmu_hws[] = { [CLK_APMU_EDP1_PXCLK] = &edp1_pxclk.common.hw, [CLK_APMU_PCIE_PORTA_MSTE] = &pciea_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTA_SLV] = &pciea_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTA_DBI] = &pciea_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTB_MSTE] = &pcieb_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTB_SLV] = &pcieb_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTB_DBI] = &pcieb_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTC_MSTE] = &pciec_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTC_SLV] = &pciec_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTC_DBI] = &pciec_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTD_MSTE] = &pcied_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTD_SLV] = &pcied_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTD_DBI] = &pcied_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTE_MSTE] = &pciee_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTE_SLV] = &pciee_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTE_DBI] = &pciee_dbi_clk.common.hw, [CLK_APMU_EMAC0_BUS] = &emac0_bus_clk.common.hw, [CLK_APMU_EMAC0_REF] = &emac0_ref_clk.common.hw, [CLK_APMU_EMAC0_1588] = &emac0_1588_clk.common.hw, From e45a44f2196e0d6aa22fd80beff1c18dee9e9348 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Sun, 31 May 2026 18:56:36 +0200 Subject: [PATCH 2498/5214] iio: light: veml3328: add support for new device Add support for the Vishay VEML3328 RGB/IR light sensor communicating via I2C (SMBus compatible). Also add a new entry for said driver into Kconfig and Makefile. Assisted-by: Gemini:3.1-Pro Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- MAINTAINERS | 5 + drivers/iio/light/Kconfig | 11 + drivers/iio/light/Makefile | 1 + drivers/iio/light/veml3328.c | 423 +++++++++++++++++++++++++++++++++++ 4 files changed, 440 insertions(+) create mode 100644 drivers/iio/light/veml3328.c diff --git a/MAINTAINERS b/MAINTAINERS index 9e9457c7bba6..a893a1a1181e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28402,6 +28402,11 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml F: drivers/iio/light/veml3235.c +VISHAY VEML3328 RGB IR LIGHT SENSOR DRIVER +M: Joshua Crofts +S: Maintained +F: drivers/iio/light/veml3328.c + VISHAY VEML6030 AMBIENT LIGHT SENSOR DRIVER M: Javier Carrasco S: Maintained diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index da4807a3fd3d..ef36824f312f 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -713,6 +713,17 @@ config VEML3235 To compile this driver as a module, choose M here: the module will be called veml3235. +config VEML3328 + tristate "VEML3328 RGBCIR light sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VEML3328 + RGB IR light sensor. + + To compile this driver as a module, choose M here: the + module will be called veml3328 + config VEML6030 tristate "VEML6030 and VEML6035 ambient light sensors" select REGMAP_I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 39e62dfc10c7..64e354c49ed8 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VCNL4035) += vcnl4035.o obj-$(CONFIG_VEML3235) += veml3235.o +obj-$(CONFIG_VEML3328) += veml3328.o obj-$(CONFIG_VEML6030) += veml6030.o obj-$(CONFIG_VEML6040) += veml6040.o obj-$(CONFIG_VEML6046X00) += veml6046x00.o diff --git a/drivers/iio/light/veml3328.c b/drivers/iio/light/veml3328.c new file mode 100644 index 000000000000..9309deb5bf19 --- /dev/null +++ b/drivers/iio/light/veml3328.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vishay VEML3328 RGBCIR light sensor driver + * + * Copyright (c) 2026 Joshua Crofts + * + * Datasheet: https://www.vishay.com/docs/84968/veml3328.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define VEML3328_REG_CONF 0x00 +#define VEML3328_REG_ID 0x0c +#define VEML3328_REG_DATA_C 0x04 +#define VEML3328_REG_DATA_R 0x05 +#define VEML3328_REG_DATA_G 0x06 +#define VEML3328_REG_DATA_B 0x07 +#define VEML3328_REG_DATA_IR 0x08 + +#define VEML3328_CONF_IT_MASK GENMASK(5, 4) +#define VEML3328_CONF_GAIN_MASK GENMASK(11, 10) + +#define VEML3328_MAX_IT_TIME (400 * USEC_PER_MSEC) + +#define VEML3328_ID_VAL 0x28 + +#define VEML3328_SHUTDOWN (BIT(0) | BIT(15)) + +struct veml3328_data { + struct regmap *regmap; + /* Ensure read-modify-write sequences are not interrupted. */ + struct mutex lock; +}; + +static const struct regmap_config veml3328_regmap_config = { + .name = "veml3328", + .reg_bits = 8, + .val_bits = 16, + .max_register = VEML3328_REG_ID, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +#define VEML3328_CHAN_SPEC(_color, _addr) { \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_color, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = _addr, \ +} + +static const struct iio_chan_spec veml3328_channels[] = { + { + .type = IIO_LIGHT, + + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), + .address = VEML3328_REG_DATA_G, + }, + VEML3328_CHAN_SPEC(CLEAR, VEML3328_REG_DATA_C), + VEML3328_CHAN_SPEC(RED, VEML3328_REG_DATA_R), + VEML3328_CHAN_SPEC(GREEN, VEML3328_REG_DATA_G), + VEML3328_CHAN_SPEC(BLUE, VEML3328_REG_DATA_B), + VEML3328_CHAN_SPEC(IR, VEML3328_REG_DATA_IR), +}; + +/* + * Precomputed scale values (micro units). + * Formula for calculation: 0.384 * (50000 / IT_us) * (1 / Gain) + * Gain indexes: 0 (x0.5), 1 (x1), 2 (x2), 3 (x4) + * IT indexes: 0 (50ms), 1 (100ms), 2 (200ms), 3 (400ms) + */ +static const int veml3328_scale_vals[4][8] = { + { 0, 768000, 0, 384000, 0, 192000, 0, 96000 }, + { 0, 384000, 0, 192000, 0, 96000, 0, 48000 }, + { 0, 192000, 0, 96000, 0, 48000, 0, 24000 }, + { 0, 96000, 0, 48000, 0, 24000, 0, 12000 }, +}; + +/* integration times in microseconds */ +static const int veml3328_it_times[][2] = { + { 0, 50 * USEC_PER_MSEC }, + { 0, 100 * USEC_PER_MSEC }, + { 0, 200 * USEC_PER_MSEC }, + { 0, 400 * USEC_PER_MSEC }, +}; + +static int veml3328_power_down(struct veml3328_data *data) +{ + return regmap_set_bits(data->regmap, VEML3328_REG_CONF, + VEML3328_SHUTDOWN); +} + +static int veml3328_power_up(struct veml3328_data *data) +{ + int ret; + + ret = regmap_clear_bits(data->regmap, VEML3328_REG_CONF, + VEML3328_SHUTDOWN); + if (ret) + return ret; + + /* + * Sleep for maximum integration time to ensure sensor is powered on + * correctly. + */ + fsleep(VEML3328_MAX_IT_TIME); + + return 0; +} + +static void veml3328_power_down_action(void *data) +{ + veml3328_power_down(data); +} + +static int veml3328_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct veml3328_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + struct device *dev = regmap_get_device(regmap); + unsigned int reg_val; + int it_inx, gain_inx; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(regmap, chan->address, ®_val); + if (ret) + return ret; + + *val = reg_val; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_INT_TIME: + ret = regmap_read(regmap, VEML3328_REG_CONF, ®_val); + if (ret) + return ret; + + it_inx = FIELD_GET(VEML3328_CONF_IT_MASK, reg_val); + if (it_inx >= ARRAY_SIZE(veml3328_it_times)) + return -EINVAL; + + *val = veml3328_it_times[it_inx][0]; + *val2 = veml3328_it_times[it_inx][1]; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(regmap, VEML3328_REG_CONF, ®_val); + if (ret) + return ret; + + it_inx = FIELD_GET(VEML3328_CONF_IT_MASK, reg_val); + gain_inx = FIELD_GET(VEML3328_CONF_GAIN_MASK, reg_val); + + if (it_inx >= ARRAY_SIZE(veml3328_it_times) || gain_inx >= 4) + return -EINVAL; + + /* Stride by 2 through the flattened array to match (val, val2) */ + *val = veml3328_scale_vals[it_inx][gain_inx * 2]; + *val2 = veml3328_scale_vals[it_inx][gain_inx * 2 + 1]; + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int veml3328_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct veml3328_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + struct device *dev = regmap_get_device(data->regmap); + unsigned int reg_val; + int ret, it_inx; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(veml3328_it_times) * 2; + *vals = (const int *)veml3328_it_times; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: { + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_read(regmap, VEML3328_REG_CONF, ®_val); + if (ret) + return ret; + + it_inx = FIELD_GET(VEML3328_CONF_IT_MASK, reg_val); + if (it_inx >= ARRAY_SIZE(veml3328_it_times)) + return -EINVAL; + + *length = 8; + *vals = (const int *)veml3328_scale_vals[it_inx]; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + } + default: + return -EINVAL; + } +} + +static int veml3328_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct veml3328_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + struct device *dev = regmap_get_device(regmap); + unsigned int reg_val; + int i, it_inx; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(veml3328_it_times); i++) { + if (veml3328_it_times[i][1] == val2) + break; + } + + if (i == ARRAY_SIZE(veml3328_it_times)) + return -EINVAL; + + return regmap_update_bits(regmap, VEML3328_REG_CONF, + VEML3328_CONF_IT_MASK, + FIELD_PREP(VEML3328_CONF_IT_MASK, i)); + + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(regmap, VEML3328_REG_CONF, ®_val); + if (ret) + return ret; + + it_inx = FIELD_GET(VEML3328_CONF_IT_MASK, reg_val); + if (it_inx >= ARRAY_SIZE(veml3328_it_times)) + return -EINVAL; + + for (i = 0; i < 4; i++) { + if (val == veml3328_scale_vals[it_inx][i * 2] && + val2 == veml3328_scale_vals[it_inx][i * 2 + 1]) + break; + } + + if (i == 4) + return -EINVAL; + + return regmap_update_bits(regmap, VEML3328_REG_CONF, + VEML3328_CONF_GAIN_MASK, + FIELD_PREP(VEML3328_CONF_GAIN_MASK, i)); + + default: + return -EINVAL; + } +} + +static const struct iio_info veml3328_info = { + .read_raw = veml3328_read_raw, + .write_raw = veml3328_write_raw, + .read_avail = veml3328_read_avail, +}; + +static int veml3328_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct veml3328_data *data; + struct iio_dev *indio_dev; + unsigned int reg_val; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &veml3328_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to initialize regmap\n"); + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + ret = regmap_read(data->regmap, VEML3328_REG_ID, ®_val); + if (ret) + return dev_err_probe(dev, ret, "Failed to read ID register\n"); + + if ((reg_val & 0xff) != VEML3328_ID_VAL) + dev_warn(dev, "Unknown device ID: 0x%02x\n", reg_val & 0xff); + + ret = veml3328_power_up(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to power on sensor\n"); + + ret = devm_add_action_or_reset(dev, veml3328_power_down_action, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to register teardown\n"); + + indio_dev->name = "veml3328"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &veml3328_info; + indio_dev->channels = veml3328_channels; + indio_dev->num_channels = ARRAY_SIZE(veml3328_channels); + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int veml3328_runtime_suspend(struct device *dev) +{ + struct veml3328_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = veml3328_power_down(data); + if (ret) + dev_err(dev, "Failed to suspend: %d\n", ret); + + return ret; +} + +static int veml3328_runtime_resume(struct device *dev) +{ + struct veml3328_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = veml3328_power_up(data); + if (ret) + dev_err(dev, "Failed to resume: %d\n", ret); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(veml3328_pm_ops, + veml3328_runtime_suspend, + veml3328_runtime_resume, + NULL); + +static const struct of_device_id veml3328_of_match[] = { + { .compatible = "vishay,veml3328" }, + { } +}; +MODULE_DEVICE_TABLE(of, veml3328_of_match); + +static const struct i2c_device_id veml3328_id[] = { + { .name = "veml3328" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, veml3328_id); + +static struct i2c_driver veml3328_driver = { + .driver = { + .name = "veml3328", + .of_match_table = veml3328_of_match, + .pm = pm_ptr(&veml3328_pm_ops), + }, + .probe = veml3328_probe, + .id_table = veml3328_id, +}; +module_i2c_driver(veml3328_driver); + +MODULE_AUTHOR("Joshua Crofts "); +MODULE_DESCRIPTION("VEML3328 RGBCIR Light Sensor"); +MODULE_LICENSE("GPL"); From 3f2d03ecf22b09da1d92ba06e1ba6e20d16060e9 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 2 Jun 2026 11:40:19 +0300 Subject: [PATCH 2499/5214] iio: adc: ad4080: fix AD4880 chip ID The AD4880 chip ID was incorrectly set to 0x0750. According to the datasheet, the product ID registers read 0x00 (PRODUCT_ID_H) and 0x59 (PRODUCT_ID_L), giving a combined chip ID of 0x0059. Fix the value to match the actual hardware. Signed-off-by: Antoniu Miclaus Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4080.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 764d49eca9e0..8d2953341b15 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -135,7 +135,7 @@ #define AD4086_CHIP_ID 0x0056 #define AD4087_CHIP_ID 0x0057 #define AD4088_CHIP_ID 0x0058 -#define AD4880_CHIP_ID 0x0750 +#define AD4880_CHIP_ID 0x0059 #define AD4884_CHIP_ID 0x005C #define AD4080_MAX_CHANNELS 2 From 628105e8f7af69c3b1d56c67896916b758f6f9c0 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:01 +0100 Subject: [PATCH 2500/5214] iio: dac: ad5686: refactor include headers Apply IWYU principle, replacing unused/generic headers for specific/missing headers. The resulting include directive lists are sorted accordingly. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 9 +++++++-- drivers/iio/dac/ad5686.c | 13 ++++++------- drivers/iio/dac/ad5686.h | 5 ++--- drivers/iio/dac/ad5696-i2c.c | 10 +++++++--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index df8619e0c092..cec784b617e6 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -8,11 +8,16 @@ * Copyright 2018 Analog Devices Inc. */ -#include "ad5686.h" - +#include +#include +#include #include #include +#include + +#include "ad5686.h" + static int ad5686_spi_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 19440b17645b..6f403b68cbec 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -5,17 +5,16 @@ * Copyright 2011 Analog Devices Inc. */ -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include -#include #include +#include #include -#include #include "ad5686.h" diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 36e16c5c4581..d08160e7fad9 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -8,10 +8,9 @@ #ifndef __DRIVERS_IIO_DAC_AD5686_H__ #define __DRIVERS_IIO_DAC_AD5686_H__ -#include -#include +#include #include -#include +#include #include diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 56625e1da817..645ef441c463 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -7,10 +7,14 @@ * Copyright 2018 Analog Devices Inc. */ -#include "ad5686.h" - -#include +#include #include +#include +#include + +#include + +#include "ad5686.h" static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) { From 126c78684073ca17c8dc2a174404f30b038d6881 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:02 +0100 Subject: [PATCH 2501/5214] iio: dac: ad5686: remove redundant register definition AD5683_REGMAP and AD5693_REGMAP behave the same way in the common code, and that is because they target single channel devices from the same sub-family. There is no reason to separate them and it will make things simpler when refactoring the chip info table. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 19 +++++-------------- drivers/iio/dac/ad5686.h | 2 -- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 6f403b68cbec..1df1145f35b3 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -116,10 +116,6 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (chan->channel > 0x7) address = 0x8; break; - case AD5693_REGMAP: - shift = 13; - ref_bit_msk = AD5693_REF_BIT_MSK; - break; default: return -EINVAL; } @@ -300,7 +296,7 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .channels = ad5311r_channels, .int_vref_mv = 2500, .num_channels = 1, - .regmap_type = AD5693_REGMAP, + .regmap_type = AD5683_REGMAP, }, [ID_AD5337R] = { .channels = ad5337r_channels, @@ -422,24 +418,24 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .channels = ad5691r_channels, .int_vref_mv = 2500, .num_channels = 1, - .regmap_type = AD5693_REGMAP, + .regmap_type = AD5683_REGMAP, }, [ID_AD5692R] = { .channels = ad5692r_channels, .int_vref_mv = 2500, .num_channels = 1, - .regmap_type = AD5693_REGMAP, + .regmap_type = AD5683_REGMAP, }, [ID_AD5693] = { .channels = ad5693_channels, .num_channels = 1, - .regmap_type = AD5693_REGMAP, + .regmap_type = AD5683_REGMAP, }, [ID_AD5693R] = { .channels = ad5693_channels, .int_vref_mv = 2500, .num_channels = 1, - .regmap_type = AD5693_REGMAP, + .regmap_type = AD5683_REGMAP, }, [ID_AD5694] = { .channels = ad5684_channels, @@ -540,11 +536,6 @@ int ad5686_probe(struct device *dev, cmd = AD5686_CMD_INTERNAL_REFER_SETUP; ref_bit_msk = AD5686_REF_BIT_MSK; break; - case AD5693_REGMAP: - cmd = AD5686_CMD_CONTROL_REG; - ref_bit_msk = AD5693_REF_BIT_MSK; - st->use_internal_vref = !has_external_vref; - break; default: return -EINVAL; } diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index d08160e7fad9..af15994c01ad 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -46,7 +46,6 @@ #define AD5310_REF_BIT_MSK BIT(8) #define AD5683_REF_BIT_MSK BIT(12) #define AD5686_REF_BIT_MSK BIT(0) -#define AD5693_REF_BIT_MSK BIT(12) /** * ad5686_supported_device_ids: @@ -89,7 +88,6 @@ enum ad5686_regmap_type { AD5310_REGMAP, AD5683_REGMAP, AD5686_REGMAP, - AD5693_REGMAP }; struct ad5686_state; From 0eb1728461a1a84613188f394e38921e29325d30 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:03 +0100 Subject: [PATCH 2502/5214] iio: dac: ad5686: drop enum id Split chip info table into separate structs and expose them to the spi i2c drivers. That is the preferrable approach and allows for the drivers to have knowledge of the device info before the common probe function gets called. Those chip info structs may be shared by SPI and I2C driver variants. Channel declaration definitions are grouped according to channel count and DECLARE_AD5693_CHANNELS() macro is renamed to DECLARE_AD5683_CHANNELS() to match the regmap_type enum. Use spi_get_device_match_data() and i2c_get_match_data() to get chip info struct reference, passing it as parameter to the core probe function. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 38 ++-- drivers/iio/dac/ad5686.c | 358 ++++++++++++++++------------------- drivers/iio/dac/ad5686.h | 66 +++---- drivers/iio/dac/ad5696-i2c.c | 65 ++++--- 4 files changed, 241 insertions(+), 286 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index cec784b617e6..3a2eafedce92 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -94,29 +94,27 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) static int ad5686_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); - - return ad5686_probe(&spi->dev, id->driver_data, id->name, - ad5686_spi_write, ad5686_spi_read); + return ad5686_probe(&spi->dev, spi_get_device_match_data(spi), + spi->modalias, ad5686_spi_write, ad5686_spi_read); } static const struct spi_device_id ad5686_spi_id[] = { - {"ad5310r", ID_AD5310R}, - {"ad5672r", ID_AD5672R}, - {"ad5674r", ID_AD5674R}, - {"ad5676", ID_AD5676}, - {"ad5676r", ID_AD5676R}, - {"ad5679r", ID_AD5679R}, - {"ad5681r", ID_AD5681R}, - {"ad5682r", ID_AD5682R}, - {"ad5683", ID_AD5683}, - {"ad5683r", ID_AD5683R}, - {"ad5684", ID_AD5684}, - {"ad5684r", ID_AD5684R}, - {"ad5685", ID_AD5685R}, /* Does not exist */ - {"ad5685r", ID_AD5685R}, - {"ad5686", ID_AD5686}, - {"ad5686r", ID_AD5686R}, + { .name = "ad5310r", .driver_data = (kernel_ulong_t)&ad5310r_chip_info }, + { .name = "ad5672r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, + { .name = "ad5674r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, + { .name = "ad5676", .driver_data = (kernel_ulong_t)&ad5676_chip_info }, + { .name = "ad5676r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, + { .name = "ad5679r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, + { .name = "ad5681r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, + { .name = "ad5682r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info }, + { .name = "ad5683", .driver_data = (kernel_ulong_t)&ad5683_chip_info }, + { .name = "ad5683r", .driver_data = (kernel_ulong_t)&ad5683r_chip_info }, + { .name = "ad5684", .driver_data = (kernel_ulong_t)&ad5684_chip_info }, + { .name = "ad5684r", .driver_data = (kernel_ulong_t)&ad5684r_chip_info }, + { .name = "ad5685", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, /* nonexistent */ + { .name = "ad5685r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, + { .name = "ad5686", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, + { .name = "ad5686r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 1df1145f35b3..431e7650704e 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -219,7 +219,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .ext_info = ad5686_ext_info, \ } -#define DECLARE_AD5693_CHANNELS(name, bits, _shift) \ +#define DECLARE_AD5683_CHANNELS(name, bits, _shift) \ static const struct iio_chan_spec name[] = { \ AD5868_CHANNEL(0, 0, bits, _shift), \ } @@ -270,205 +270,172 @@ static const struct iio_chan_spec name[] = { \ AD5868_CHANNEL(15, 15, bits, _shift), \ } -DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2); -DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6); +/* single-channel */ +DECLARE_AD5683_CHANNELS(ad5310r_channels, 10, 2); +DECLARE_AD5683_CHANNELS(ad5311r_channels, 10, 6); +DECLARE_AD5683_CHANNELS(ad5681r_channels, 12, 4); +DECLARE_AD5683_CHANNELS(ad5682r_channels, 14, 2); +DECLARE_AD5683_CHANNELS(ad5683r_channels, 16, 0); + +/* dual-channel */ DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); -DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); -DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4); -DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0); -DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0); -DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); -DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); -DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); -DECLARE_AD5693_CHANNELS(ad5693_channels, 16, 0); -DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2); -DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4); -static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { - [ID_AD5310R] = { - .channels = ad5310r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5310_REGMAP, - }, - [ID_AD5311R] = { - .channels = ad5311r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5337R] = { - .channels = ad5337r_channels, - .int_vref_mv = 2500, - .num_channels = 2, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5338R] = { - .channels = ad5338r_channels, - .int_vref_mv = 2500, - .num_channels = 2, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5671R] = { - .channels = ad5672_channels, - .int_vref_mv = 2500, - .num_channels = 8, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5672R] = { - .channels = ad5672_channels, - .int_vref_mv = 2500, - .num_channels = 8, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5673R] = { - .channels = ad5674r_channels, - .int_vref_mv = 2500, - .num_channels = 16, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5674R] = { - .channels = ad5674r_channels, - .int_vref_mv = 2500, - .num_channels = 16, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5675R] = { - .channels = ad5676_channels, - .int_vref_mv = 2500, - .num_channels = 8, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5676] = { - .channels = ad5676_channels, - .num_channels = 8, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5676R] = { - .channels = ad5676_channels, - .int_vref_mv = 2500, - .num_channels = 8, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5677R] = { - .channels = ad5679r_channels, - .int_vref_mv = 2500, - .num_channels = 16, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5679R] = { - .channels = ad5679r_channels, - .int_vref_mv = 2500, - .num_channels = 16, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5681R] = { - .channels = ad5691r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5682R] = { - .channels = ad5692r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5683] = { - .channels = ad5693_channels, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5683R] = { - .channels = ad5693_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5684] = { - .channels = ad5684_channels, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5684R] = { - .channels = ad5684_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5685R] = { - .channels = ad5685r_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5686] = { - .channels = ad5686_channels, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5686R] = { - .channels = ad5686_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5691R] = { - .channels = ad5691r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5692R] = { - .channels = ad5692r_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5693] = { - .channels = ad5693_channels, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5693R] = { - .channels = ad5693_channels, - .int_vref_mv = 2500, - .num_channels = 1, - .regmap_type = AD5683_REGMAP, - }, - [ID_AD5694] = { - .channels = ad5684_channels, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5694R] = { - .channels = ad5684_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5695R] = { - .channels = ad5685r_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5696] = { - .channels = ad5686_channels, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, - [ID_AD5696R] = { - .channels = ad5686_channels, - .int_vref_mv = 2500, - .num_channels = 4, - .regmap_type = AD5686_REGMAP, - }, +/* quad-channel */ +DECLARE_AD5686_CHANNELS(ad5684r_channels, 12, 4); +DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); +DECLARE_AD5686_CHANNELS(ad5686r_channels, 16, 0); + +/* 8-channel */ +DECLARE_AD5676_CHANNELS(ad5672r_channels, 12, 4); +DECLARE_AD5676_CHANNELS(ad5676r_channels, 16, 0); + +/* 16-channel */ +DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4); +DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0); + +const struct ad5686_chip_info ad5310r_chip_info = { + .channels = ad5310r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5310_REGMAP, }; +EXPORT_SYMBOL_NS_GPL(ad5310r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5311r_chip_info = { + .channels = ad5311r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5311r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5681r_chip_info = { + .channels = ad5681r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5681r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5682r_chip_info = { + .channels = ad5682r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5682r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5683_chip_info = { + .channels = ad5683r_channels, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5683_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5683r_chip_info = { + .channels = ad5683r_channels, + .int_vref_mv = 2500, + .num_channels = 1, + .regmap_type = AD5683_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5683r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5337r_chip_info = { + .channels = ad5337r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5337r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5338r_chip_info = { + .channels = ad5338r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5338r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5684_chip_info = { + .channels = ad5684r_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5684_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5684r_chip_info = { + .channels = ad5684r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5684r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5685r_chip_info = { + .channels = ad5685r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5685r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5686_chip_info = { + .channels = ad5686r_channels, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5686_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5686r_chip_info = { + .channels = ad5686r_channels, + .int_vref_mv = 2500, + .num_channels = 4, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5686r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5672r_chip_info = { + .channels = ad5672r_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5672r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5676_chip_info = { + .channels = ad5676r_channels, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5676_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5676r_chip_info = { + .channels = ad5676r_channels, + .int_vref_mv = 2500, + .num_channels = 8, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5676r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5674r_chip_info = { + .channels = ad5674r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5674r_chip_info, "IIO_AD5686"); + +const struct ad5686_chip_info ad5679r_chip_info = { + .channels = ad5679r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, +}; +EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); int ad5686_probe(struct device *dev, - enum ad5686_supported_device_ids chip_type, + const struct ad5686_chip_info *chip_info, const char *name, ad5686_write_func write, ad5686_read_func read) { @@ -488,8 +455,7 @@ int ad5686_probe(struct device *dev, st->dev = dev; st->write = write; st->read = read; - - st->chip_info = &ad5686_chip_info_tbl[chip_type]; + st->chip_info = chip_info; ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); if (ret < 0 && ret != -ENODEV) diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index af15994c01ad..caadc7403da1 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -47,42 +47,6 @@ #define AD5683_REF_BIT_MSK BIT(12) #define AD5686_REF_BIT_MSK BIT(0) -/** - * ad5686_supported_device_ids: - */ -enum ad5686_supported_device_ids { - ID_AD5310R, - ID_AD5311R, - ID_AD5337R, - ID_AD5338R, - ID_AD5671R, - ID_AD5672R, - ID_AD5673R, - ID_AD5674R, - ID_AD5675R, - ID_AD5676, - ID_AD5676R, - ID_AD5677R, - ID_AD5679R, - ID_AD5681R, - ID_AD5682R, - ID_AD5683, - ID_AD5683R, - ID_AD5684, - ID_AD5684R, - ID_AD5685R, - ID_AD5686, - ID_AD5686R, - ID_AD5691R, - ID_AD5692R, - ID_AD5693, - ID_AD5693R, - ID_AD5694, - ID_AD5694R, - ID_AD5695R, - ID_AD5696, - ID_AD5696R, -}; enum ad5686_regmap_type { AD5310_REGMAP, @@ -112,6 +76,34 @@ struct ad5686_chip_info { enum ad5686_regmap_type regmap_type; }; +/* single-channel instances */ +extern const struct ad5686_chip_info ad5310r_chip_info; +extern const struct ad5686_chip_info ad5311r_chip_info; +extern const struct ad5686_chip_info ad5681r_chip_info; +extern const struct ad5686_chip_info ad5682r_chip_info; +extern const struct ad5686_chip_info ad5683_chip_info; +extern const struct ad5686_chip_info ad5683r_chip_info; + +/* dual-channel instances */ +extern const struct ad5686_chip_info ad5337r_chip_info; +extern const struct ad5686_chip_info ad5338r_chip_info; + +/* quad-channel instances */ +extern const struct ad5686_chip_info ad5684_chip_info; +extern const struct ad5686_chip_info ad5684r_chip_info; +extern const struct ad5686_chip_info ad5685r_chip_info; +extern const struct ad5686_chip_info ad5686_chip_info; +extern const struct ad5686_chip_info ad5686r_chip_info; + +/* 8-channel instances */ +extern const struct ad5686_chip_info ad5672r_chip_info; +extern const struct ad5686_chip_info ad5676_chip_info; +extern const struct ad5686_chip_info ad5676r_chip_info; + +/* 16-channel instances */ +extern const struct ad5686_chip_info ad5674r_chip_info; +extern const struct ad5686_chip_info ad5679r_chip_info; + /** * struct ad5686_state - driver instance specific data * @spi: spi_device @@ -149,7 +141,7 @@ struct ad5686_state { int ad5686_probe(struct device *dev, - enum ad5686_supported_device_ids chip_type, + const struct ad5686_chip_info *chip_info, const char *name, ad5686_write_func write, ad5686_read_func read); diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 645ef441c463..c1f3cebabaf8 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -64,47 +64,46 @@ static int ad5686_i2c_write(struct ad5686_state *st, static int ad5686_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); - return ad5686_probe(&i2c->dev, id->driver_data, id->name, - ad5686_i2c_write, ad5686_i2c_read); + return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c), + i2c->name, ad5686_i2c_write, ad5686_i2c_read); } static const struct i2c_device_id ad5686_i2c_id[] = { - { .name = "ad5311r", .driver_data = ID_AD5311R }, - { .name = "ad5337r", .driver_data = ID_AD5337R }, - { .name = "ad5338r", .driver_data = ID_AD5338R }, - { .name = "ad5671r", .driver_data = ID_AD5671R }, - { .name = "ad5673r", .driver_data = ID_AD5673R }, - { .name = "ad5675r", .driver_data = ID_AD5675R }, - { .name = "ad5677r", .driver_data = ID_AD5677R }, - { .name = "ad5691r", .driver_data = ID_AD5691R }, - { .name = "ad5692r", .driver_data = ID_AD5692R }, - { .name = "ad5693", .driver_data = ID_AD5693 }, - { .name = "ad5693r", .driver_data = ID_AD5693R }, - { .name = "ad5694", .driver_data = ID_AD5694 }, - { .name = "ad5694r", .driver_data = ID_AD5694R }, - { .name = "ad5695r", .driver_data = ID_AD5695R }, - { .name = "ad5696", .driver_data = ID_AD5696 }, - { .name = "ad5696r", .driver_data = ID_AD5696R }, + { .name = "ad5311r", .driver_data = (kernel_ulong_t)&ad5311r_chip_info }, + { .name = "ad5337r", .driver_data = (kernel_ulong_t)&ad5337r_chip_info }, + { .name = "ad5338r", .driver_data = (kernel_ulong_t)&ad5338r_chip_info }, + { .name = "ad5671r", .driver_data = (kernel_ulong_t)&ad5672r_chip_info }, + { .name = "ad5673r", .driver_data = (kernel_ulong_t)&ad5674r_chip_info }, + { .name = "ad5675r", .driver_data = (kernel_ulong_t)&ad5676r_chip_info }, + { .name = "ad5677r", .driver_data = (kernel_ulong_t)&ad5679r_chip_info }, + { .name = "ad5691r", .driver_data = (kernel_ulong_t)&ad5681r_chip_info }, + { .name = "ad5692r", .driver_data = (kernel_ulong_t)&ad5682r_chip_info }, + { .name = "ad5693", .driver_data = (kernel_ulong_t)&ad5683_chip_info }, + { .name = "ad5693r", .driver_data = (kernel_ulong_t)&ad5683r_chip_info }, + { .name = "ad5694", .driver_data = (kernel_ulong_t)&ad5684_chip_info }, + { .name = "ad5694r", .driver_data = (kernel_ulong_t)&ad5684r_chip_info }, + { .name = "ad5695r", .driver_data = (kernel_ulong_t)&ad5685r_chip_info }, + { .name = "ad5696", .driver_data = (kernel_ulong_t)&ad5686_chip_info }, + { .name = "ad5696r", .driver_data = (kernel_ulong_t)&ad5686r_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { - { .compatible = "adi,ad5311r" }, - { .compatible = "adi,ad5337r" }, - { .compatible = "adi,ad5338r" }, - { .compatible = "adi,ad5671r" }, - { .compatible = "adi,ad5675r" }, - { .compatible = "adi,ad5691r" }, - { .compatible = "adi,ad5692r" }, - { .compatible = "adi,ad5693" }, - { .compatible = "adi,ad5693r" }, - { .compatible = "adi,ad5694" }, - { .compatible = "adi,ad5694r" }, - { .compatible = "adi,ad5695r" }, - { .compatible = "adi,ad5696" }, - { .compatible = "adi,ad5696r" }, + { .compatible = "adi,ad5311r", .data = &ad5311r_chip_info }, + { .compatible = "adi,ad5337r", .data = &ad5337r_chip_info }, + { .compatible = "adi,ad5338r", .data = &ad5338r_chip_info }, + { .compatible = "adi,ad5671r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5675r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5691r", .data = &ad5681r_chip_info }, + { .compatible = "adi,ad5692r", .data = &ad5682r_chip_info }, + { .compatible = "adi,ad5693", .data = &ad5683_chip_info }, + { .compatible = "adi,ad5693r", .data = &ad5683r_chip_info }, + { .compatible = "adi,ad5694", .data = &ad5684_chip_info }, + { .compatible = "adi,ad5694r", .data = &ad5684r_chip_info }, + { .compatible = "adi,ad5695r", .data = &ad5685r_chip_info }, + { .compatible = "adi,ad5696", .data = &ad5686_chip_info }, + { .compatible = "adi,ad5696r", .data = &ad5686r_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); From 34222a45490910141053aa7c46601bb2c27ac82c Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:04 +0100 Subject: [PATCH 2503/5214] iio: dac: ad5686: add of_match table to the spi driver Add of_match table for the SPI device variants to be consistent with the AD5696 I2C driver. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 3a2eafedce92..7eddffe86c00 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -119,9 +119,30 @@ static const struct spi_device_id ad5686_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); +static const struct of_device_id ad5686_of_match[] = { + { .compatible = "adi,ad5310r", .data = &ad5310r_chip_info }, + { .compatible = "adi,ad5672r", .data = &ad5672r_chip_info }, + { .compatible = "adi,ad5674r", .data = &ad5674r_chip_info }, + { .compatible = "adi,ad5676", .data = &ad5676_chip_info }, + { .compatible = "adi,ad5676r", .data = &ad5676r_chip_info }, + { .compatible = "adi,ad5679r", .data = &ad5679r_chip_info }, + { .compatible = "adi,ad5681r", .data = &ad5681r_chip_info }, + { .compatible = "adi,ad5682r", .data = &ad5682r_chip_info }, + { .compatible = "adi,ad5683", .data = &ad5683_chip_info }, + { .compatible = "adi,ad5683r", .data = &ad5683r_chip_info }, + { .compatible = "adi,ad5684", .data = &ad5684_chip_info }, + { .compatible = "adi,ad5684r", .data = &ad5684r_chip_info }, + { .compatible = "adi,ad5685r", .data = &ad5685r_chip_info }, + { .compatible = "adi,ad5686", .data = &ad5686_chip_info }, + { .compatible = "adi,ad5686r", .data = &ad5686r_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5686_of_match); + static struct spi_driver ad5686_spi_driver = { .driver = { .name = "ad5686", + .of_match_table = ad5686_of_match, }, .probe = ad5686_spi_probe, .id_table = ad5686_spi_id, From 16fc40c250198d970ae6dafbecf000fe68773c4d Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:05 +0100 Subject: [PATCH 2504/5214] iio: dac: ad5686: add helpers to handle powerdown masks Add ad5686_pd_field_set() and ad5686_pd_field_get() helpers to cleanup powerdown mask control. Define AD5686_PD_* constants, e.g. AD5686_PD_MSK to hold powerdown mask value for a single channel. AD5686_LDAC_PWRDN_* macros are replaced by AD5686_PD_MODE_*, because they are unused and the LDAC feature for async load of DAC channel values is not related to power down control. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 40 ++++++++++++++++++++++++++-------------- drivers/iio/dac/ad5686.h | 13 ++++++++----- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 431e7650704e..3409b551dbf7 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -33,28 +33,40 @@ static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan return __ffs(chan->address) * 2; } +static inline void ad5686_pd_field_set(const struct iio_chan_spec *chan, + unsigned int *pd, unsigned int val) +{ + unsigned int shift = ad5686_pd_mask_shift(chan); + + *pd = (*pd & ~(AD5686_PD_MSK << shift)) | ((val & AD5686_PD_MSK) << shift); +} + +static inline unsigned int ad5686_pd_field_get(const struct iio_chan_spec *chan, + unsigned int pd) +{ + unsigned int shift = ad5686_pd_mask_shift(chan); + + return (pd >> shift) & AD5686_PD_MSK; +} + static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { - unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return ((st->pwr_down_mode >> shift) & 0x3U) - 1; + return ad5686_pd_field_get(chan, st->pwr_down_mode) - 1; } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { - unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - - st->pwr_down_mode &= ~(0x3U << shift); - st->pwr_down_mode |= (mode + 1) << shift; + ad5686_pd_field_set(chan, &st->pwr_down_mode, mode + 1); return 0; } @@ -69,12 +81,12 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { - unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3U << shift))); + return sysfs_emit(buf, "%d\n", + !!ad5686_pd_field_get(chan, st->pwr_down_mask)); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, @@ -96,9 +108,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, guard(mutex)(&st->lock); if (readin) - st->pwr_down_mask |= 0x3U << ad5686_pd_mask_shift(chan); + ad5686_pd_field_set(chan, &st->pwr_down_mask, AD5686_PD_MSK_PWR_DOWN); else - st->pwr_down_mask &= ~(0x3U << ad5686_pd_mask_shift(chan)); + ad5686_pd_field_set(chan, &st->pwr_down_mask, AD5686_PD_MSK_PWR_UP); switch (st->chip_info->regmap_type) { case AD5310_REGMAP: @@ -471,10 +483,10 @@ int ad5686_probe(struct device *dev, /* Set all the power down mode for all channels to 1K pulldown */ for (i = 0; i < st->chip_info->num_channels; i++) { - shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]); - st->pwr_down_mask &= ~(0x3U << shift); /* powered up state */ - st->pwr_down_mode &= ~(0x3U << shift); - st->pwr_down_mode |= 0x01U << shift; + ad5686_pd_field_set(&st->chip_info->channels[i], + &st->pwr_down_mask, AD5686_PD_MSK_PWR_UP); + ad5686_pd_field_set(&st->chip_info->channels[i], + &st->pwr_down_mode, AD5686_PD_MODE_1K_TO_GND); } indio_dev->name = name; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index caadc7403da1..176d41966985 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -35,11 +35,6 @@ #define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 #define AD5686_CMD_READBACK_ENABLE 0x9 -#define AD5686_LDAC_PWRDN_NONE 0x0 -#define AD5686_LDAC_PWRDN_1K 0x1 -#define AD5686_LDAC_PWRDN_100K 0x2 -#define AD5686_LDAC_PWRDN_3STATE 0x3 - #define AD5686_CMD_CONTROL_REG 0x4 #define AD5686_CMD_READBACK_ENABLE_V2 0x5 @@ -47,6 +42,14 @@ #define AD5683_REF_BIT_MSK BIT(12) #define AD5686_REF_BIT_MSK BIT(0) +#define AD5686_PD_MSK GENMASK(1, 0) + +#define AD5686_PD_MODE_1K_TO_GND 0x1 +#define AD5686_PD_MODE_100K_TO_GND 0x2 +#define AD5686_PD_MODE_THREE_STATE 0x3 + +#define AD5686_PD_MSK_PWR_UP 0x0 +#define AD5686_PD_MSK_PWR_DOWN 0x3 enum ad5686_regmap_type { AD5310_REGMAP, From 592b378791d620ba479428eac6fb3c4bc2b18388 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:06 +0100 Subject: [PATCH 2505/5214] iio: dac: ad5686: add control_sync() for single-channel devices Create ad5310_control_sync() and ad5683_control_sync() functions that properly consume the mask definitions with FIELD_PREP(). This allows to reuse a function that updates the control register with cached values, without relying on confusing logic that depends on st->use_internal_vref, which is initialized earlier in ad5686_probe() because it is also applicable to the AD5686_REGMAP case, removing the need for the has_external_vref. Powerdown masks initialization is simplified as *_control_sync() masks outs any unused bits for the single-channel case. The change cleans up ad5686_write_dac_powerdown() and ad5686_probe(), organizing the code for feature extension, e.g. gain control support for single-channel devices. Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 94 +++++++++++++++++++++++----------------- drivers/iio/dac/ad5686.h | 7 ++- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 3409b551dbf7..86bc944a1acd 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include @@ -24,6 +26,24 @@ static const char * const ad5686_powerdown_modes[] = { "three_state" }; +static int ad5310_control_sync(struct ad5686_state *st) +{ + unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; + + return st->write(st, AD5686_CMD_CONTROL_REG, 0, + FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); +} + +static int ad5683_control_sync(struct ad5686_state *st) +{ + unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; + + return st->write(st, AD5686_CMD_CONTROL_REG, 0, + FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); +} + static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) { if (chan->channel == chan->address) @@ -98,8 +118,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, bool readin; int ret; struct ad5686_state *st = iio_priv(indio_dev); - unsigned int val, ref_bit_msk; - u8 shift, address = 0; + unsigned int val; + u8 address; ret = kstrtobool(buf, &readin); if (ret) @@ -114,32 +134,34 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - shift = 9; - ref_bit_msk = AD5310_REF_BIT_MSK; + ret = ad5310_control_sync(st); + if (ret) + return ret; break; case AD5683_REGMAP: - shift = 13; - ref_bit_msk = AD5683_REF_BIT_MSK; + ret = ad5683_control_sync(st); + if (ret) + return ret; break; case AD5686_REGMAP: - shift = 0; - ref_bit_msk = 0; /* AD5674R/AD5679R have 16 channels and 2 powerdown registers */ - if (chan->channel > 0x7) + val = st->pwr_down_mask & st->pwr_down_mode; + if (chan->channel > 0x7) { address = 0x8; + val = upper_16_bits(val); + } else { + address = 0x0; + val = lower_16_bits(val); + } + ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, address, val); + if (ret) + return ret; break; default: return -EINVAL; } - val = ((st->pwr_down_mask & st->pwr_down_mode) << shift); - if (!st->use_internal_vref) - val |= ref_bit_msk; - - ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, - address, val >> (address * 2)); - - return ret ? ret : len; + return len; } static int ad5686_read_raw(struct iio_dev *indio_dev, @@ -453,9 +475,6 @@ int ad5686_probe(struct device *dev, { struct ad5686_state *st; struct iio_dev *indio_dev; - unsigned int val, ref_bit_msk, shift; - bool has_external_vref; - u8 cmd; int ret, i; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -473,13 +492,12 @@ int ad5686_probe(struct device *dev, if (ret < 0 && ret != -ENODEV) return ret; - has_external_vref = ret != -ENODEV; - st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; + st->use_internal_vref = ret == -ENODEV; + st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000; - /* Initialize masks to all ones provided the max shift (last channel) */ - shift = ad5686_pd_mask_shift(&st->chip_info->channels[st->chip_info->num_channels - 1]); - st->pwr_down_mask = GENMASK(shift + 1, 0); - st->pwr_down_mode = GENMASK(shift + 1, 0); + /* Initialize masks to all ones */ + st->pwr_down_mask = ~0; + st->pwr_down_mode = ~0; /* Set all the power down mode for all channels to 1K pulldown */ for (i = 0; i < st->chip_info->num_channels; i++) { @@ -501,29 +519,25 @@ int ad5686_probe(struct device *dev, switch (st->chip_info->regmap_type) { case AD5310_REGMAP: - cmd = AD5686_CMD_CONTROL_REG; - ref_bit_msk = AD5310_REF_BIT_MSK; - st->use_internal_vref = !has_external_vref; + ret = ad5310_control_sync(st); + if (ret) + return ret; break; case AD5683_REGMAP: - cmd = AD5686_CMD_CONTROL_REG; - ref_bit_msk = AD5683_REF_BIT_MSK; - st->use_internal_vref = !has_external_vref; + ret = ad5683_control_sync(st); + if (ret) + return ret; break; case AD5686_REGMAP: - cmd = AD5686_CMD_INTERNAL_REFER_SETUP; - ref_bit_msk = AD5686_REF_BIT_MSK; + ret = st->write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, + st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); + if (ret) + return ret; break; default: return -EINVAL; } - val = has_external_vref ? ref_bit_msk : 0; - - ret = st->write(st, cmd, 0, val); - if (ret) - return ret; - return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 176d41966985..17980c54839c 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -39,9 +39,12 @@ #define AD5686_CMD_READBACK_ENABLE_V2 0x5 #define AD5310_REF_BIT_MSK BIT(8) -#define AD5683_REF_BIT_MSK BIT(12) -#define AD5686_REF_BIT_MSK BIT(0) +#define AD5310_PD_MSK GENMASK(10, 9) +#define AD5683_REF_BIT_MSK BIT(12) +#define AD5683_PD_MSK GENMASK(14, 13) + +#define AD5686_REF_BIT_MSK BIT(0) #define AD5686_PD_MSK GENMASK(1, 0) #define AD5686_PD_MODE_1K_TO_GND 0x1 From 883a752cc4c6420a2a6ce9cf572564963d4bcadc Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:07 +0100 Subject: [PATCH 2506/5214] iio: dac: ad5686: cleanup doc header of local structs Review documentation comment header for ad5686_chip_info and ad5686_state. Update variable names and description and remove unnecessary blank line between comment and struct declaration. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 17980c54839c..3945f5fb6b7e 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -69,12 +69,11 @@ typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr); /** * struct ad5686_chip_info - chip specific information - * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @int_vref_mv: the internal reference voltage * @num_channels: number of channels * @channel: channel specification * @regmap_type: register map layout variant */ - struct ad5686_chip_info { u16 int_vref_mv; unsigned int num_channels; @@ -112,16 +111,16 @@ extern const struct ad5686_chip_info ad5679r_chip_info; /** * struct ad5686_state - driver instance specific data - * @spi: spi_device + * @dev: device instance * @chip_info: chip model specific constants, available modes etc * @vref_mv: actual reference voltage used * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode * @use_internal_vref: set to true if the internal reference voltage is used - * @lock lock to protect the data buffer during regmap ops - * @data: spi transfer buffers + * @lock: lock to protect access to state fields, which includes + * the data buffer during regmap ops + * @data: transfer buffers */ - struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; From ae8360f3715aa61714864fc81f39790cbb883d40 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Sun, 24 May 2026 11:17:08 +0100 Subject: [PATCH 2507/5214] iio: dac: ad5686: create bus ops struct Create struct with bus operations, which will be used to extend bus implementation features. Auxiliary functions ad5686_write() and ad5686_read() are created and ad5686_probe() now receives an ops struct pointer rather than individual read and write functions. Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 7 ++++++- drivers/iio/dac/ad5686.c | 32 ++++++++++++++------------------ drivers/iio/dac/ad5686.h | 29 +++++++++++++++++++++-------- drivers/iio/dac/ad5696-i2c.c | 7 ++++++- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 7eddffe86c00..6b6ef1d7071f 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -92,10 +92,15 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr) return be32_to_cpu(st->data[2].d32); } +static const struct ad5686_bus_ops ad5686_spi_ops = { + .write = ad5686_spi_write, + .read = ad5686_spi_read, +}; + static int ad5686_spi_probe(struct spi_device *spi) { return ad5686_probe(&spi->dev, spi_get_device_match_data(spi), - spi->modalias, ad5686_spi_write, ad5686_spi_read); + spi->modalias, &ad5686_spi_ops); } static const struct spi_device_id ad5686_spi_id[] = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 86bc944a1acd..5840fda4b011 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -30,18 +30,18 @@ static int ad5310_control_sync(struct ad5686_state *st) { unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; - return st->write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, + FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); } static int ad5683_control_sync(struct ad5686_state *st) { unsigned int pd_val = st->pwr_down_mask & st->pwr_down_mode; - return st->write(st, AD5686_CMD_CONTROL_REG, 0, - FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | - FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); + return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0, + FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) | + FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1)); } static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) @@ -153,7 +153,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, address = 0x0; val = lower_16_bits(val); } - ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, address, val); + ret = ad5686_write(st, AD5686_CMD_POWERDOWN_DAC, address, val); if (ret) return ret; break; @@ -176,7 +176,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: mutex_lock(&st->lock); - ret = st->read(st, chan->address); + ret = ad5686_read(st, chan->address); mutex_unlock(&st->lock); if (ret < 0) return ret; @@ -206,10 +206,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, return -EINVAL; mutex_lock(&st->lock); - ret = st->write(st, - AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, - val << chan->scan_type.shift); + ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val << chan->scan_type.shift); mutex_unlock(&st->lock); break; default: @@ -470,8 +468,7 @@ EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, ad5686_write_func write, - ad5686_read_func read) + const char *name, const struct ad5686_bus_ops *ops) { struct ad5686_state *st; struct iio_dev *indio_dev; @@ -484,8 +481,7 @@ int ad5686_probe(struct device *dev, st = iio_priv(indio_dev); st->dev = dev; - st->write = write; - st->read = read; + st->ops = ops; st->chip_info = chip_info; ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); @@ -529,8 +525,8 @@ int ad5686_probe(struct device *dev, return ret; break; case AD5686_REGMAP: - ret = st->write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); + ret = ad5686_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, + st->use_internal_vref ? 0 : AD5686_REF_BIT_MSK); if (ret) return ret; break; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 3945f5fb6b7e..a06fe7d89305 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -62,10 +62,15 @@ enum ad5686_regmap_type { struct ad5686_state; -typedef int (*ad5686_write_func)(struct ad5686_state *st, - u8 cmd, u8 addr, u16 val); - -typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr); +/** + * struct ad5686_bus_ops - bus specific read/write operations + * @read: read a register value at the given address + * @write: write a command, address and value to the device + */ +struct ad5686_bus_ops { + int (*read)(struct ad5686_state *st, u8 addr); + int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val); +}; /** * struct ad5686_chip_info - chip specific information @@ -113,6 +118,7 @@ extern const struct ad5686_chip_info ad5679r_chip_info; * struct ad5686_state - driver instance specific data * @dev: device instance * @chip_info: chip model specific constants, available modes etc + * @ops: bus specific operations * @vref_mv: actual reference voltage used * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode @@ -124,11 +130,10 @@ extern const struct ad5686_chip_info ad5679r_chip_info; struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; + const struct ad5686_bus_ops *ops; unsigned short vref_mv; unsigned int pwr_down_mask; unsigned int pwr_down_mode; - ad5686_write_func write; - ad5686_read_func read; bool use_internal_vref; struct mutex lock; @@ -147,8 +152,16 @@ struct ad5686_state { int ad5686_probe(struct device *dev, const struct ad5686_chip_info *chip_info, - const char *name, ad5686_write_func write, - ad5686_read_func read); + const char *name, const struct ad5686_bus_ops *ops); +static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val) +{ + return st->ops->write(st, cmd, addr, val); +} + +static inline int ad5686_read(struct ad5686_state *st, u8 addr) +{ + return st->ops->read(st, addr); +} #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index c1f3cebabaf8..279309329b64 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -62,10 +62,15 @@ static int ad5686_i2c_write(struct ad5686_state *st, return (ret != 3) ? -EIO : 0; } +static const struct ad5686_bus_ops ad5686_i2c_ops = { + .write = ad5686_i2c_write, + .read = ad5686_i2c_read, +}; + static int ad5686_i2c_probe(struct i2c_client *i2c) { return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c), - i2c->name, ad5686_i2c_write, ad5686_i2c_read); + i2c->name, &ad5686_i2c_ops); } static const struct i2c_device_id ad5686_i2c_id[] = { From bb11485a87fbb2254b62cfed630b699d50e57da8 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Thu, 30 Apr 2026 14:30:13 +0200 Subject: [PATCH 2508/5214] fs/ntfs3: add bounds check to run_get_highest_vcn() run_get_highest_vcn() parses a packed NTFS mapping-pairs buffer without any length bound, relying solely on a 0x00 terminator to stop. A crafted $LogFile UpdateMappingPairs record whose embedded attribute contains mapping-pairs runs without a terminator causes the function to read past the slab allocation, triggering a KASAN slab-out-of-bounds read on mount. The sibling function run_unpack() received an analogous bounds-check in commit b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()"), but run_get_highest_vcn() was missed. Take a run_buf_size parameter and reject any run header whose payload would extend past the buffer end, mirroring the pattern used by run_unpack(). The caller in fslog.c passes the remaining attribute bytes after the mapping-pairs offset. KASAN report (on mainline v7.1 merge window HEAD): BUG: KASAN: slab-out-of-bounds in run_get_highest_vcn+0x3c0/0x410 Read of size 1 at addr ffff88800e2d5400 by task mount/72 Call Trace: run_get_highest_vcn+0x3c0/0x410 do_action.isra.0+0x3ba8/0x7b50 log_replay+0x9ddd/0x10200 ntfs_loadlog_and_replay+0x4ad/0x610 ntfs_fill_super+0x214a/0x4540 Fixes: b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()") Signed-off-by: Jaeyeong Lee Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 5 ++++- fs/ntfs3/ntfs_fs.h | 3 ++- fs/ntfs3/run.c | 9 +++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index acfa18b84401..0d1be4da3e4f 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3368,7 +3368,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, memmove(Add2Ptr(attr, aoff), data, dlen); if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), - attr_run(attr), &t64)) { + attr_run(attr), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), + &t64)) { goto dirty_vol; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index bbf3b6a1dcbe..d53febc2559c 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -877,7 +877,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); int run_clone(const struct runs_tree *run, struct runs_tree *new_run); bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done); CLST run_len(const struct runs_tree *run); diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 1ce7d92fb274..19aa044fd1fc 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1205,18 +1205,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { + const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; u8 size_size; - while ((size_size = *run_buf & 0xF)) { + while (run_buf < run_last && (size_size = *run_buf & 0xF)) { u8 offset_size = *run_buf++ >> 4; u64 len; if (size_size > 8 || offset_size > 8) return -EINVAL; + if (run_buf + size_size + offset_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); if (!len) return -EINVAL; From a6169cb0faac4f276df8fee7ffa14c098c05bb52 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 21 Apr 2026 22:26:20 +0200 Subject: [PATCH 2509/5214] ntfs3: avoid -Wmaybe-uninitialized warning This warning shows up with gcc-10 now: In file included from fs/ntfs3/index.c:15: fs/ntfs3/index.c: In function 'indx_add_allocate': fs/ntfs3/ntfs_fs.h:463:9: error: 'bmp_size' may be used uninitialized in this function [-Werror=maybe-uninitialized] 463 | return attr_set_size_ex(ni, type, name, name_len, run, new_size, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 464 | new_valid, keep_prealloc, NULL, false); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/ntfs3/index.c:1498:6: note: 'bmp_size' was declared here 1498 | u64 bmp_size, bmp_size_v; | ^~~~~~~~ The warning does look correct, as the 'out2' label can be reached without initializing bmp_size and bmp_size_v. Initialize these at the same place as bmp. Signed-off-by: Arnd Bergmann Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index ade276225999..d65984d9dc07 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1506,6 +1506,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, if (bit != MINUS_ONE_T) { bmp = NULL; + bmp_size = bmp_size_v = 0; } else { if (bmp->non_res) { bmp_size = le64_to_cpu(bmp->nres.data_size); From d62cf685d12e95cc0a4122c43cd9f0eb8bba0b4c Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Mon, 27 Apr 2026 15:26:50 +0800 Subject: [PATCH 2510/5214] fs/ntfs3: hold ni_lock across readdir metadata walk [BUG] KASAN reports a slab-use-after-free during getdents(2): BUG: KASAN: slab-use-after-free in ntfs_read_mft fs/ntfs3/inode.c:79 [inline] BUG: KASAN: slab-use-after-free in ntfs_iget5+0x59b/0x3450 fs/ntfs3/inode.c:541 Read of size 2 at addr ffff88800b7a5a4e by task syz.0.1061/2354 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xbe/0x130 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xd1/0x650 mm/kasan/report.c:482 kasan_report+0xfb/0x140 mm/kasan/report.c:595 __asan_report_load2_noabort+0x14/0x30 mm/kasan/report_generic.c:379 ntfs_read_mft fs/ntfs3/inode.c:79 [inline] ntfs_iget5+0x59b/0x3450 fs/ntfs3/inode.c:541 ntfs_dir_emit fs/ntfs3/dir.c:337 [inline] ntfs_read_hdr+0x714/0x930 fs/ntfs3/dir.c:385 ntfs_readdir+0xaad/0x1010 fs/ntfs3/dir.c:458 iterate_dir+0x276/0x9e0 fs/readdir.c:108 __do_sys_getdents fs/readdir.c:326 [inline] __se_sys_getdents fs/readdir.c:312 [inline] __x64_sys_getdents+0x143/0x290 fs/readdir.c:312 ... Allocated by task 2160: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 kasan_save_alloc_info+0x37/0x60 mm/kasan/generic.c:573 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xc3/0xd0 mm/kasan/common.c:417 kasan_kmalloc include/linux/kasan.h:262 [inline] __do_kmalloc_node mm/slub.c:5650 [inline] __kmalloc_noprof+0x2bd/0x900 mm/slub.c:5662 kmalloc_noprof include/linux/slab.h:961 [inline] mi_init+0x9d/0x110 fs/ntfs3/record.c:105 mi_format_new+0x6b/0x500 fs/ntfs3/record.c:422 ni_add_subrecord+0x129/0x540 fs/ntfs3/frecord.c:321 ntfs_look_free_mft+0x238/0xd90 fs/ntfs3/fsntfs.c:715 ni_create_attr_list+0x8e6/0x1690 fs/ntfs3/frecord.c:826 ni_ins_attr_ext+0x5ec/0x9d0 fs/ntfs3/frecord.c:924 ni_insert_attr+0x2bf/0x830 fs/ntfs3/frecord.c:1091 ni_insert_resident+0xec/0x3d0 fs/ntfs3/frecord.c:1475 ni_add_name+0x4b2/0x8a0 fs/ntfs3/frecord.c:2987 ni_rename+0xa6/0x160 fs/ntfs3/frecord.c:3026 ntfs_rename+0xa19/0xe00 fs/ntfs3/namei.c:332 vfs_rename+0xd42/0x1d50 fs/namei.c:5216 do_renameat2+0x715/0xb60 fs/namei.c:5364 __do_sys_rename fs/namei.c:5411 [inline] __se_sys_rename fs/namei.c:5409 [inline] __x64_sys_rename+0x83/0xb0 fs/namei.c:5409 x64_sys_call+0x8c4/0x26a0 arch/x86/include/generated/asm/syscalls_64.h:83 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x93/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e Freed by task 85: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 __kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:587 kasan_save_free_info mm/kasan/kasan.h:406 [inline] poison_slab_object mm/kasan/common.c:252 [inline] __kasan_slab_free+0x6f/0xa0 mm/kasan/common.c:284 kasan_slab_free include/linux/kasan.h:234 [inline] slab_free_hook mm/slub.c:2543 [inline] slab_free mm/slub.c:6642 [inline] kfree+0x2bf/0x6b0 mm/slub.c:6849 mi_clear fs/ntfs3/ntfs_fs.h:1107 [inline] mi_put+0x10e/0x1a0 fs/ntfs3/record.c:97 ni_write_inode+0x479/0x2a00 fs/ntfs3/frecord.c:3320 ntfs3_write_inode+0x51/0x70 fs/ntfs3/inode.c:1042 write_inode fs/fs-writeback.c:1564 [inline] __writeback_single_inode+0x8c9/0xc30 fs/fs-writeback.c:1784 writeback_sb_inodes+0x5e6/0xf60 fs/fs-writeback.c:2015 __writeback_inodes_wb+0x10c/0x2d0 fs/fs-writeback.c:2086 wb_writeback+0x63f/0x900 fs/fs-writeback.c:2197 wb_check_old_data_flush fs/fs-writeback.c:2301 [inline] wb_do_writeback fs/fs-writeback.c:2354 [inline] wb_workfn+0x8cc/0xd60 fs/fs-writeback.c:2382 process_one_work+0x8e0/0x1980 kernel/workqueue.c:3263 process_scheduled_works kernel/workqueue.c:3346 [inline] worker_thread+0x683/0xf80 kernel/workqueue.c:3427 kthread+0x3f0/0x850 kernel/kthread.c:463 ret_from_fork+0x50f/0x610 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 The faulting address sits 590 bytes inside a freed kmalloc-1k object allocated by ni_add_subrecord() and freed from ni_write_inode() writeback. [CAUSE] ntfs_readdir() loads all subrecords once, but then drops ni_lock() before it starts walking the directory metadata through ntfs_read_hdr(). That leaves the current NTFS_DE pointer backed by parent-directory subrecord memory that concurrent writeback is still allowed to compact and free. The later ntfs_dir_emit() -> ntfs_iget5() call exposes the stale e->ref, but the lifetime bug starts earlier: readdir is still consuming parent-directory metadata after releasing the lock that protects it. [FIX] Keep ni_lock() held from the point where ntfs_readdir() starts consuming the directory metadata until the walk over root/index entries is finished. This closes the parent-directory lifetime hole directly and keeps the existing readdir d_type behaviour unchanged. Signed-off-by: ZhengYuan Huang Signed-off-by: Konstantin Komarov --- fs/ntfs3/dir.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index d99ab086ef6f..a052ba9250e7 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -489,10 +489,17 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) goto out; } + /* + * Keep directory metadata stable for the whole walk. Loading subrecords + * once is not enough if concurrent writeback can still compact ATTR_LIST + * entries and free the record that ntfs_read_hdr() is currently walking. + */ + ni_lock(ni); + root = indx_get_root(&ni->dir, ni, NULL, NULL); if (!root) { err = -EINVAL; - goto out; + goto out_unlock; } if (pos >= sbi->record_size) { @@ -503,7 +510,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) */ err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx); if (err) - goto out; + goto out_unlock; bit = 0; } @@ -514,7 +521,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) /* Get the next used index. */ err = indx_used_bit(&ni->dir, ni, &bit); if (err) - goto out; + goto out_unlock; if (bit == MINUS_ONE_T) { /* no more used indexes. end of dir. */ @@ -524,13 +531,13 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) if (bit >= max_bit) { /* Corrupted directory. */ err = -EINVAL; - goto out; + goto out_unlock; } err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits, &node, &file->f_ra); if (err) - goto out; + goto out_unlock; /* * Add each name from index in 'ctx'. @@ -539,9 +546,12 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) ((u64)bit << index_bits) + sbi->record_size, pos, name, ctx); if (err) - goto out; + goto out_unlock; } +out_unlock: + ni_unlock(ni); + out: kfree(name); put_indx_node(node); From b1c1101067d9536bcb0fe023b96ee2dde5535959 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Mon, 27 Apr 2026 11:24:18 +0800 Subject: [PATCH 2511/5214] fs/ntfs3: preserve non-DOS attribute bits in system.dos_attrib [BUG] A corrupted ntfs3 image can hit a NULL function pointer call in generic_perform_write() after toggling system.ntfs_attrib and then overwriting system.dos_attrib on the same file. BUG: kernel NULL pointer dereference, address: 0000000000000000 \#PF: supervisor instruction fetch in kernel mode \#PF: error_code(0x0010) - not-present page PGD bed5067 P4D bed5067 PUD 0 Oops: Oops: 0010 [#1] SMP KASAN NOPTI RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. RSP: 0018:ffff88801025f988 EFLAGS: 00010246 Call Trace: generic_perform_write+0x409/0x8c0 mm/filemap.c:4255 __generic_file_write_iter+0x1bb/0x200 mm/filemap.c:4372 ntfs_file_write_iter+0xcd9/0x1c20 fs/ntfs3/file.c:1253 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x63b/0xf70 fs/read_write.c:686 ksys_write+0x133/0x250 fs/read_write.c:738 __do_sys_write fs/read_write.c:749 [inline] __se_sys_write fs/read_write.c:746 [inline] __x64_sys_write+0x77/0xc0 fs/read_write.c:746 ... [CAUSE] system.ntfs_attrib updates ATTR_DATA flags via ni_new_attr_flags() and switches i_mapping->a_ops to ntfs_aops_cmpr when FILE_ATTRIBUTE_COMPRESSED is set. system.dos_attrib then overwrites ni->std_fa from a one-byte DOS attribute value, clearing the compression bit without updating ATTR_DATA or the mapping operations. Old buffered writes use is_compressed(ni) to choose __generic_file_write_iter(). That leaves generic_perform_write() calling a NULL write_begin callback from ntfs_aops_cmpr. [FIX] Treat system.dos_attrib as a low-byte DOS attribute update and preserve the existing non-DOS attribute bits in ni->std_fa. This keeps compressed and sparse state consistent with ATTR_DATA and the mapping operations while keeping the existing DOS attribute semantics intact. Signed-off-by: ZhengYuan Huang Signed-off-by: Konstantin Komarov --- fs/ntfs3/xattr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 9eeac0ab2b71..7e5118247660 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -867,7 +867,9 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { if (sizeof(u8) != size) goto out; - new_fa = cpu_to_le32(*(u8 *)value); + /* system.dos_attrib only covers the low DOS attribute byte. */ + new_fa = (ni->std_fa & ~cpu_to_le32(0xff)) | + cpu_to_le32(*(u8 *)value); goto set_new_fa; } From 98d6e5d9dc1d34dcffc61549617581a5fe1ef807 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 24 Apr 2026 11:47:36 +0800 Subject: [PATCH 2512/5214] fs/ntfs3: validate index entry key bounds [BUG] A malformed NTFS directory index entry can advertise a key_size larger than the bytes actually present in its NTFS_DE payload. Directory lookup then passes that malformed key to cmp_fnames(), which can read past the end of the kmalloc'ed index buffer. BUG: KASAN: slab-out-of-bounds in fname_full_size fs/ntfs3/ntfs.h:590 [inline] BUG: KASAN: slab-out-of-bounds in cmp_fnames+0x1ea/0x230 fs/ntfs3/index.c:46 Read of size 1 at addr ffff88801c313018 by task syz.6.3365/9279 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xbe/0x130 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xd1/0x650 mm/kasan/report.c:482 kasan_report+0xfb/0x140 mm/kasan/report.c:595 __asan_report_load1_noabort+0x14/0x30 mm/kasan/report_generic.c:378 fname_full_size fs/ntfs3/ntfs.h:590 [inline] cmp_fnames+0x1ea/0x230 fs/ntfs3/index.c:46 hdr_find_e.isra.0+0x3ed/0x670 fs/ntfs3/index.c:762 indx_find+0x4b5/0x900 fs/ntfs3/index.c:1186 dir_search_u+0x2c0/0x460 fs/ntfs3/dir.c:254 ntfs_lookup+0x1cc/0x2a0 fs/ntfs3/namei.c:85 __lookup_slow+0x241/0x450 fs/namei.c:1816 lookup_slow fs/namei.c:1833 [inline] walk_component+0x31c/0x570 fs/namei.c:2151 link_path_walk+0x592/0xd60 fs/namei.c:2519 path_lookupat+0x138/0x660 fs/namei.c:2675 filename_lookup+0x1f3/0x560 fs/namei.c:2705 filename_setxattr+0xad/0x1c0 fs/xattr.c:660 path_setxattrat+0x1d8/0x280 fs/xattr.c:713 __do_sys_lsetxattr fs/xattr.c:754 [inline] __se_sys_lsetxattr fs/xattr.c:750 [inline] __x64_sys_lsetxattr+0xd0/0x150 fs/xattr.c:750 ... Allocated by task 9279: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 kasan_save_alloc_info+0x37/0x60 mm/kasan/generic.c:573 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xc3/0xd0 mm/kasan/common.c:417 kasan_kmalloc include/linux/kasan.h:262 [inline] __do_kmalloc_node mm/slub.c:5650 [inline] __kmalloc_noprof+0x2bd/0x900 mm/slub.c:5662 kmalloc_noprof include/linux/slab.h:961 [inline] indx_read+0x41d/0xad0 fs/ntfs3/index.c:1059 indx_find+0x447/0x900 fs/ntfs3/index.c:1179 dir_search_u+0x2c0/0x460 fs/ntfs3/dir.c:254 ntfs_lookup+0x1cc/0x2a0 fs/ntfs3/namei.c:85 __lookup_slow+0x241/0x450 fs/namei.c:1816 lookup_slow fs/namei.c:1833 [inline] walk_component+0x31c/0x570 fs/namei.c:2151 link_path_walk+0x592/0xd60 fs/namei.c:2519 path_lookupat+0x138/0x660 fs/namei.c:2675 filename_lookup+0x1f3/0x560 fs/namei.c:2705 filename_setxattr+0xad/0x1c0 fs/xattr.c:660 path_setxattrat+0x1d8/0x280 fs/xattr.c:713 __do_sys_lsetxattr fs/xattr.c:754 [inline] __se_sys_lsetxattr fs/xattr.c:750 [inline] __x64_sys_lsetxattr+0xd0/0x150 fs/xattr.c:750 ... [CAUSE] The index-header validators only validated INDEX_HDR-level geometry. They did not walk each NTFS_DE to verify entry alignment, subnode layout, or that key_size fit inside the entry payload. They also allowed a last sentinel entry to carry a non-zero key_size. [FIX] Walk every NTFS_DE in ntfs3's index-header validators and reject entries with invalid layout, mismatched subnode state, oversized key_size, or non-zero sentinel keys before lookup or log replay can consume them. Signed-off-by: ZhengYuan Huang Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 28 +++++++++++++++++++++------- fs/ntfs3/index.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 0d1be4da3e4f..66ead9db26ee 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -2599,11 +2599,12 @@ static int read_next_log_rec(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) { + const bool has_subnode = hdr_has_subnode(hdr); __le16 mask; u32 min_de, de_off, used, total; const struct NTFS_DE *e; - if (hdr_has_subnode(hdr)) { + if (has_subnode) { min_de = sizeof(struct NTFS_DE) + sizeof(u64); mask = NTFS_IE_HAS_SUBNODES; } else { @@ -2620,20 +2621,33 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; } - e = Add2Ptr(hdr, de_off); + e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); for (;;) { u16 esize = le16_to_cpu(e->size); - struct NTFS_DE *next = Add2Ptr(e, esize); + u16 key_size = le16_to_cpu(e->key_size); + u16 data_size; - if (esize < min_de || PtrOffset(hdr, next) > used || + if (!IS_ALIGNED(esize, 8) || esize < min_de || (e->flags & NTFS_IE_HAS_SUBNODES) != mask) { return false; } - if (de_is_last(e)) - break; + if (size_add(de_off, esize) > used) + return false; - e = next; + if (de_is_last(e)) { + if (key_size) + return false; + + break; + } + + data_size = esize - min_de; + if (key_size > data_size) + return false; + + de_off += esize; + e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); } return true; diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index d65984d9dc07..85f674e9e361 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -611,16 +611,51 @@ static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr, */ static bool index_hdr_check(const struct INDEX_HDR *hdr, u32 bytes) { + const bool has_subnode = hdr_has_subnode(hdr); + const u16 min_size = sizeof(struct NTFS_DE) + + (has_subnode ? sizeof(u64) : 0); u32 end = le32_to_cpu(hdr->used); u32 tot = le32_to_cpu(hdr->total); u32 off = le32_to_cpu(hdr->de_off); + const struct NTFS_DE *e; if (!IS_ALIGNED(off, 8) || tot > bytes || end > tot || - size_add(off, sizeof(struct NTFS_DE)) > end) { + size_add(off, min_size) > end) { /* incorrect index buffer. */ return false; } + /* Ensure every key stays inside its entry before lookup walks it. */ + e = (const struct NTFS_DE *)((const u8 *)hdr + off); + for (;;) { + u16 e_size = le16_to_cpu(e->size); + u16 key_size = le16_to_cpu(e->key_size); + u16 data_size; + + if (!IS_ALIGNED(e_size, 8) || e_size < min_size || + de_has_vcn(e) != has_subnode) { + /* incorrect index entry. */ + return false; + } + + if (size_add(off, e_size) > end) + return false; + + if (de_is_last(e)) { + if (key_size) + return false; + + break; + } + + data_size = e_size - min_size; + if (key_size > data_size) + return false; + + off += e_size; + e = (const struct NTFS_DE *)((const u8 *)hdr + off); + } + return true; } From 932fa0c1496286e39e14e27194c1ee7c2181629f Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 6 May 2026 15:55:54 +0800 Subject: [PATCH 2513/5214] fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory rename In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs the renamed file inode instead of the target directory new_dir: if (IS_DIRSYNC(new_dir)) ntfs_sync_inode(inode); /* should be new_dir */ DIRSYNC requires that directory metadata changes are written to disk synchronously. Since new_dir was modified (a new directory entry was added), it is new_dir that must be synced to satisfy the guarantee, not the renamed file itself. This bug has existed since the initial ntfs3 implementation and was carried through the refactoring in commit 78ab59fee07f ("fs/ntfs3: Rework file operations"). Fix by syncing new_dir instead of inode. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov --- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index b2af8f695e60..64cde1a856f4 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -340,7 +340,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ntfs_sync_inode(dir); if (IS_DIRSYNC(new_dir)) - ntfs_sync_inode(inode); + ntfs_sync_inode(new_dir); } if (dir_ni != new_dir_ni) From 57382ec6ac63b63dce2789e835fded28b698ae79 Mon Sep 17 00:00:00 2001 From: Yunpeng Tian Date: Mon, 4 May 2026 07:19:43 -0700 Subject: [PATCH 2514/5214] fs/ntfs3: validate Dirty Page Table capacity in log_replay copy_lcns In the analysis pass of $LogFile journal replay, log_replay() copies LCNs from each action log record into an existing Dirty Page Table (DPT) entry without bounding the destination index. A crafted NTFS image with DPT entry lcns_follow=1 and an action log record with lcns_follow=2 produces a kernel slab out-of-bounds write at mount time: BUG: KASAN: slab-out-of-bounds in log_replay+0x654c/0xdb60 Write of size 8 at addr ffff8880095e1040 by task mount Two attacker-controlled fields can drive j+i past the allocated page_lcns[] array: 1. dp->lcns_follow (capacity) can be smaller than lrh->lcns_follow. 2. lrh->target_vcn may be smaller than dp->vcn, making the u64 subtraction wrap to a huge size_t. Validate target VCN delta and per-record LCN count against the DPT entry capacity, bail via the existing out: cleanup label with -EINVAL. This mirrors the bounds-check pattern added in commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") and commit 0ca0485e4b2e ("fs/ntfs3: validate rec->used in journal-replay file record check"). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Reported-by: Yunpeng Tian Reported-by: Mingda Zhang Reported-by: Gongming Wang Reported-by: Peiyuan Xu Reported-by: Qinrun Dai Cc: stable@vger.kernel.org Signed-off-by: Yunpeng Tian Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 66ead9db26ee..767f7cdab8d1 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4564,11 +4564,21 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; From 36c7276816ed4266c155b71b1fa747b2785f23f7 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Fri, 8 May 2026 17:52:45 +0800 Subject: [PATCH 2515/5214] fs/ntfs3: fix wrong LCN in run_remove_range() when splitting a run When run_remove_range() removes a middle portion of a non-sparse run, it splits the run into head and tail parts. The tail is inserted via run_add_entry() but uses the original r->lcn as its starting LCN instead of advancing it by the split offset. For example, removing VCN range [10, 20) from a run {vcn=0, lcn=100, len=30} should produce: {vcn=0, lcn=100, len=10} (head) {vcn=20, lcn=120, len=10} (tail, lcn advanced by 20) But the current code produces: {vcn=0, lcn=100, len=10} {vcn=20, lcn=100, len=10} (wrong: points to same physical clusters) This creates overlapping physical mappings in the in-memory run tree, which can corrupt cluster allocation decisions and lead to data corruption. The correct pattern is already used in run_insert_range(): CLST lcn2 = r->lcn == SPARSE_LCN ? SPARSE_LCN : (r->lcn + len1); Apply the same logic in run_remove_range(). Fixes: 10d7c95af043 ("fs/ntfs3: add delayed-allocation (delalloc) support") Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov --- fs/ntfs3/run.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 19aa044fd1fc..ad7db67514ef 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1297,9 +1297,12 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done) if (r_end > end) { /* Remove a middle part, split. */ + CLST tail_lcn = r->lcn == SPARSE_LCN ? + SPARSE_LCN : (r->lcn + (end - r->vcn)); + *done += len; r->len = d; - return run_add_entry(run, end, r->lcn, r_end - end, + return run_add_entry(run, end, tail_lcn, r_end - end, false); } /* Remove tail of run .*/ From e8ed78f40eecd0176fda71d673f6957c98e7ffbe Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Wed, 6 May 2026 14:08:22 -0300 Subject: [PATCH 2516/5214] fs/ntfs3: call _ntfs_bad_inode() when failing to rename It is safe to call _ntfs_bad_inode on live inodes since: commit 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") The WARN_ON was added when it wasn't safe by: commit d99208b91933 ("fs/ntfs3: cancle set bad inode after removing name fails") Replace the WARN_ON with a call to _ntfs_bad_inode() to prevent further operations on the inconsistent inode. Reported-by: syzbot+4d8e30dbafb5c1260479@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=4d8e30dbafb5c1260479 Fixes: 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") Signed-off-by: Helen Koike Signed-off-by: Konstantin Komarov --- fs/ntfs3/frecord.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 7b035da63c12..78eb065c7e43 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2800,8 +2800,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, err = ni_add_name(new_dir_ni, ni, new_de); if (!err) { err = ni_remove_name(dir_ni, ni, de, &de2, &undo); - WARN_ON(err && - ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)); + if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)) + _ntfs_bad_inode(&ni->vfs_inode); } /* From 33f29fc7c67e1d7235755202697370f486a25413 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 14:36:13 +0200 Subject: [PATCH 2517/5214] fs/ntfs3: add fileattr support Implement fileattr_get() and fileattr_set() to fix a problem found during the internal testing. This allows ntfs3 to expose and modify inode flags through the generic file attribute interface used by FS_IOC_GETFLAGS and FS_IOC_SETFLAGS. Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ntfs3/inode.c | 2 ++ fs/ntfs3/namei.c | 4 +++ fs/ntfs3/ntfs_fs.h | 6 ++++ 4 files changed, 91 insertions(+) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index b041639ab406..f421a36b1ed6 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -89,6 +89,80 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) return 0; } +/* + * ntfs_fileattr_get - inode_operations::fileattr_get + */ +int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa) +{ + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); + u32 flags = 0; + + /* Avoid any operation if inode is bad. */ + if (unlikely(is_bad_ni(ni))) + return -EINVAL; + + if (inode->i_flags & S_IMMUTABLE) + flags |= FS_IMMUTABLE_FL; + + if (inode->i_flags & S_APPEND) + flags |= FS_APPEND_FL; + + if (is_compressed(ni)) + flags |= FS_COMPR_FL; + + if (is_encrypted(ni)) + flags |= FS_ENCRYPT_FL; + + if (ni->nodump) + flags |= FS_NODUMP_FL; + + fileattr_fill_flags(fa, flags); + + return 0; +} + +/* + * ntfs_fileattr_set - inode_operations::fileattr_set + */ +int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct file_kattr *fa) +{ + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); + u32 flags = fa->flags; + unsigned int new_fl = 0; + + /* Avoid any operation if inode is bad. */ + if (unlikely(is_bad_ni(ni))) + return -EINVAL; + + if (fileattr_has_fsx(fa)) + return -EOPNOTSUPP; + + if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL)) + return -EOPNOTSUPP; + + if (flags & FS_IMMUTABLE_FL) + new_fl |= S_IMMUTABLE; + + if (flags & FS_APPEND_FL) + new_fl |= S_APPEND; + + inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND); + + /* Save nodump flag to return in ntfs_getattr. */ + if (flags & FS_NODUMP_FL) + ni->nodump = 1; + else + ni->nodump = 0; + + inode_set_ctime_current(inode); + mark_inode_dirty(inode); + + return 0; +} + static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf) { if (copy_to_user(buf, sbi->volume.label, FSLABEL_MAX)) @@ -203,6 +277,9 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, if (inode->i_flags & S_APPEND) stat->attributes |= STATX_ATTR_APPEND; + if (ni->nodump) + stat->attributes |= STATX_ATTR_NODUMP; + if (is_compressed(ni)) stat->attributes |= STATX_ATTR_COMPRESSED; @@ -1547,6 +1624,8 @@ const struct inode_operations ntfs_file_inode_operations = { .get_acl = ntfs_get_acl, .set_acl = ntfs_set_acl, .fiemap = ntfs_fiemap, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct file_operations ntfs_file_operations = { diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 42af1abe17f8..356fc94c5a18 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -2095,6 +2095,8 @@ const struct inode_operations ntfs_link_inode_operations = { .get_link = ntfs_get_link, .setattr = ntfs_setattr, .listxattr = ntfs_listxattr, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct address_space_operations ntfs_aops = { diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 64cde1a856f4..c59de5f2fa97 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -518,6 +518,8 @@ const struct inode_operations ntfs_dir_inode_operations = { .getattr = ntfs_getattr, .listxattr = ntfs_listxattr, .fiemap = ntfs_fiemap, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct inode_operations ntfs_special_inode_operations = { @@ -526,6 +528,8 @@ const struct inode_operations ntfs_special_inode_operations = { .listxattr = ntfs_listxattr, .get_acl = ntfs_get_acl, .set_acl = ntfs_set_acl, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct dentry_operations ntfs_dentry_ops = { diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index d53febc2559c..9939556dcdc1 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -392,6 +392,9 @@ struct ntfs_inode { */ u8 ni_bad; + /* Keep track of FS_NODUMP_FL. */ + u8 nodump; + union { struct ntfs_index dir; struct { @@ -529,6 +532,9 @@ bool dir_is_empty(struct inode *dir); extern const struct file_operations ntfs_dir_operations; /* Globals from file.c */ +int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa); +int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct file_kattr *fa); int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, u32 flags); int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, From 4f165b1ff99a7baf2e38df0a1093fcc120d259a3 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 14:46:23 +0200 Subject: [PATCH 2518/5214] fs/ntfs3: zero stale pagecache beyond valid data length Zero cached folios beyond the valid data length when closing a writable mapping. This keeps cached data beyond initialized file contents zeroed and prevents stale pagecache exposure after mmap-based writes. Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index f421a36b1ed6..2d1de3739ffe 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -323,18 +323,44 @@ static int ntfs_extend_initialized_size(struct file *file, return 0; } +/* Zero pagecache after 'from'. */ +static void ntfs_zero_tail(struct address_space *mapping, loff_t from) +{ + struct folio_batch fbatch; + pgoff_t index = from >> PAGE_SHIFT; + unsigned nr, i; + + folio_batch_init(&fbatch); + + nr = filemap_get_folios(mapping, &index, -1, &fbatch); + + for (i = 0; i < nr; i++) { + struct folio *folio = fbatch.folios[i]; + u32 st = folio_pos(folio) < from ? + offset_in_folio(folio, from) : + 0; + + folio_lock(folio); + folio_zero_segment(folio, st, folio_size(folio)); + + folio_unlock(folio); + } + folio_batch_release(&fbatch); +} + static void ntfs_filemap_close(struct vm_area_struct *vma) { struct inode *inode = file_inode(vma->vm_file); struct ntfs_inode *ni = ntfs_i(inode); + u64 i_size = i_size_read(inode); u64 from = (u64)vma->vm_pgoff << PAGE_SHIFT; - u64 to = min_t(u64, i_size_read(inode), - from + vma->vm_end - vma->vm_start); + u64 to = min(i_size, from + vma->vm_end - vma->vm_start); if (ni->i_valid < to) { ni->i_valid = to; mark_inode_dirty(inode); } + ntfs_zero_tail(inode->i_mapping, ni->i_valid); } /* Copy of generic_file_vm_ops. */ From 5569b3c5259b26eccc8d9fdc939241abbae5afbb Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 14:52:37 +0200 Subject: [PATCH 2519/5214] fs/ntfs3: handle delayed allocation overlap in run lookup Introduce run_lookup_entry_da() to look up data runs while taking delayed allocation into account. ntfs3 may have both committed extents and delayed allocation extents for the same VCN range. The new helper checks delayed allocation first and falls back to the real run, then corrects the returned range when a real run overlaps with a delayed allocation run. Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 17 +++++-------- fs/ntfs3/ntfs_fs.h | 3 +++ fs/ntfs3/run.c | 61 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index e61c5bf7e27e..0caf2f8a8c1e 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -962,11 +962,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, /* Try to find in cache. */ down_read(&ni->file.run_lock); - if (!no_da && run_lookup_entry(&ni->file.run_da, vcn, lcn, len, NULL)) { - /* The requested vcn is delay allocated. */ - *lcn = DELALLOC_LCN; - } else if (run_lookup_entry(&ni->file.run, vcn, lcn, len, NULL)) { - /* The requested vcn is known in current run. */ + if (run_lookup_entry_da(&ni->file.run, !no_da ? &ni->file.run_da : NULL, + vcn, lcn, len)) { } else { *len = 0; } @@ -1011,11 +1008,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, int step; again: - if (da && run_lookup_entry(run_da, vcn, lcn, len, NULL)) { - /* The requested vcn is delay allocated. */ - *lcn = DELALLOC_LCN; - } else if (run_lookup_entry(run, vcn, lcn, len, NULL)) { - /* The requested vcn is known in current run. */ + if (run_lookup_entry_da(run, da ? &ni->file.run_da : NULL, vcn, lcn, + len)) { } else { *len = 0; } @@ -1100,7 +1094,8 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, } if (!*len) { - if (run_lookup_entry(run, vcn, lcn, len, NULL)) { + if (run_lookup_entry_da(run, da ? run_da : NULL, vcn, lcn, + len)) { if (*lcn != SPARSE_LCN || !new) goto ok; /* Slow normal way without allocation. */ diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 9939556dcdc1..d98d7e474476 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -858,6 +858,9 @@ static inline void mi_get_ref(const struct mft_inode *mi, struct MFT_REF *ref) /* Globals from run.c */ bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, CLST *len, size_t *index); +bool run_lookup_entry_da(const struct runs_tree *run, + const struct runs_tree *run_da, CLST vcn, CLST *lcn, + CLST *len); void run_truncate(struct runs_tree *run, CLST vcn); void run_truncate_head(struct runs_tree *run, CLST vcn); void run_truncate_around(struct runs_tree *run, CLST vcn); diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index ad7db67514ef..3ebf0154eda3 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -223,6 +223,66 @@ bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, return true; } +/* + * run_overlaps + * + * true if run overlaps with range [svcn, svcn + len) + */ +static bool run_overlaps(const struct runs_tree *run, CLST svcn, CLST len, + CLST *vcn, CLST *clen) +{ + size_t i; + const struct ntfs_run *r = run->runs; + CLST end = svcn + len; + + for (i = 0; i < run->count; i++, r++) { + /* Check if [r->vcn, r->vcn+r->len) overlaps [svcn, end). */ + if (r->vcn < end && svcn < r->vcn + r->len) { + if (vcn) + *vcn = r->vcn; + if (clen) + *clen = r->len; + return true; + } + } + + return false; +} + +/* + * run_lookup_entry_da + * + * - lookup vcn in delalloc run + * - lookup vcn in real run + * - correct result if real run overlaps with delalloc + */ +bool run_lookup_entry_da(const struct runs_tree *run, + const struct runs_tree *run_da, CLST vcn, CLST *lcn, + CLST *len) +{ + CLST vcn1, len1; + + if (run_da && run_lookup_entry(run_da, vcn, lcn, len, NULL)) { + *lcn = DELALLOC_LCN; + return true; + } + + if (!run_lookup_entry(run, vcn, lcn, len, NULL)) + return false; + + if (run_da && run_overlaps(run_da, vcn, *len, &vcn1, &len1)) { + /* Correct return value. */ + if (vcn1 > vcn) { + *len = vcn1 - vcn; + } else { + *lcn = DELALLOC_LCN; + *len = len1; + } + } + + return true; +} + /* * run_truncate_head - Decommit the range before vcn. */ @@ -1286,7 +1346,6 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done) return true; } - e = run->runs + run->count; r = run->runs + index; end = vcn + len; From 65bd5da999effd4ddac46c9f6b538ee65a9828c4 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 14:55:38 +0200 Subject: [PATCH 2520/5214] fs/ntfs3: fold resident writeback into writepages loop Remove the separate ntfs_resident_writepage() helper and handle resident writeback directly from ntfs_writepages(). This simplifies the resident writeback path and keeps the folio handling local to ntfs_writepages(). Signed-off-by: Konstantin Komarov --- fs/ntfs3/inode.c | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 356fc94c5a18..9a75c2d832ca 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -992,31 +992,6 @@ static const struct iomap_writeback_ops ntfs_writeback_ops = { .writeback_submit = iomap_ioend_writeback_submit, }; -static int ntfs_resident_writepage(struct folio *folio, - struct writeback_control *wbc) -{ - struct address_space *mapping = folio->mapping; - struct inode *inode = mapping->host; - struct ntfs_inode *ni = ntfs_i(inode); - int ret; - - /* Avoid any operation if inode is bad. */ - if (unlikely(is_bad_ni(ni))) - return -EINVAL; - - if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) - return -EIO; - - ni_lock(ni); - ret = attr_data_write_resident(ni, folio); - ni_unlock(ni); - - if (ret != E_NTFS_NONRESIDENT) - folio_unlock(folio); - mapping_set_error(mapping, ret); - return ret; -} - static int ntfs_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -1038,9 +1013,22 @@ static int ntfs_writepages(struct address_space *mapping, if (is_resident(ni)) { struct folio *folio = NULL; + err = 0; - while ((folio = writeback_iter(mapping, wbc, folio, &err))) - err = ntfs_resident_writepage(folio, wbc); + while ((folio = writeback_iter(mapping, wbc, folio, &err))) { + int err2; + + ni_lock(ni); + err2 = attr_data_write_resident(ni, folio); + ni_unlock(ni); + + folio_unlock(folio); + if (err2) { + mapping_set_error(mapping, err2); + if (!err) + err = err2; + } + } return err; } From 9245b4adb1242589f9ffdd08cf6e529f46b2de7a Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 14:58:12 +0200 Subject: [PATCH 2521/5214] fs/ntfs3: force waiting for direct I/O completion It makes ntfs3 wait for direct I/O completion before returning to the caller, instead of allowing the write path to complete asynchronously. The issue was discovered during internal tests. Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 2d1de3739ffe..26bec5be248e 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -1398,7 +1398,8 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; } - ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, 0, NULL, 0); + ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, + IOMAP_DIO_FORCE_WAIT, NULL, 0); if (ret == -ENOTBLK) { /* Returns -ENOTBLK in case of a page invalidation failure for writes.*/ From ecbb433f9a8e3297f298226c15fada69b3748d3e Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 15:11:25 +0200 Subject: [PATCH 2522/5214] fs/ntfs3: fold file size handling into ntfs_set_size() Remove the separate ntfs_extend() and ntfs_truncate() helpers and route file size changes through ntfs_set_size(). This consolidates ntfs3 size updates in one place and lets the write, fallocate, and setattr paths share the same logic for updating i_size, valid data length, and preallocated extents. This patch fixes a few issues found during internal tests. Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 178 +++++++++++------------------------------------ fs/ntfs3/inode.c | 21 +++--- 2 files changed, 51 insertions(+), 148 deletions(-) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 26bec5be248e..06a5d9b44ac1 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -450,93 +450,6 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc) return err; } -static int ntfs_extend(struct inode *inode, loff_t pos, size_t count, - struct file *file) -{ - struct ntfs_inode *ni = ntfs_i(inode); - struct address_space *mapping = inode->i_mapping; - loff_t end = pos + count; - bool extend_init = file && pos > ni->i_valid; - int err; - - if (end <= inode->i_size && !extend_init) - return 0; - - /* Mark rw ntfs as dirty. It will be cleared at umount. */ - ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); - - if (end > inode->i_size) { - /* - * Normal files: increase file size, allocate space. - * Sparse/Compressed: increase file size. No space allocated. - */ - err = ntfs_set_size(inode, end); - if (err) - goto out; - } - - if (extend_init && !is_compressed(ni)) { - err = ntfs_extend_initialized_size(file, ni, pos); - if (err) - goto out; - } else { - err = 0; - } - - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - mark_inode_dirty(inode); - - if (IS_SYNC(inode)) { - int err2; - - err = filemap_fdatawrite_range(mapping, pos, end - 1); - err2 = write_inode_now(inode, 1); - if (!err) - err = err2; - if (!err) - err = filemap_fdatawait_range(mapping, pos, end - 1); - } - -out: - return err; -} - -static int ntfs_truncate(struct inode *inode, loff_t new_size) -{ - int err; - struct ntfs_inode *ni = ntfs_i(inode); - u64 new_valid = min_t(u64, ni->i_valid, new_size); - - truncate_setsize(inode, new_size); - - ni_lock(ni); - - down_write(&ni->file.run_lock); - err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size, - &new_valid, ni->mi.sbi->options->prealloc, NULL, - false); - up_write(&ni->file.run_lock); - - ni->i_valid = new_valid; - - ni_unlock(ni); - - if (err) - return err; - - ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE; - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - if (!IS_DIRSYNC(inode)) { - mark_inode_dirty(inode); - } else { - err = ntfs_sync_inode(inode); - if (err) - return err; - } - - return 0; -} - /* * ntfs_fallocate - file_operations::ntfs_fallocate * @@ -743,57 +656,25 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) if (is_supported_holes) { CLST vcn = vbo >> cluster_bits; CLST cend = bytes_to_cluster(sbi, end); - CLST cend_v = bytes_to_cluster(sbi, ni->i_valid); CLST lcn, clen; bool new; - if (cend_v > cend) - cend_v = cend; - /* * Allocate and zero new clusters. - * Zeroing these clusters may be too long. - */ - for (; vcn < cend_v; vcn += clen) { - err = attr_data_get_block(ni, vcn, cend_v - vcn, - &lcn, &clen, &new, - true, NULL, false); - if (err) - goto out; - } - - /* - * Moving up 'valid size'. - */ - err = ntfs_extend_initialized_size( - file, ni, (u64)cend_v << cluster_bits); - if (err) - goto out; - - /* - * Allocate but not zero new clusters. */ for (; vcn < cend; vcn += clen) { err = attr_data_get_block(ni, vcn, cend - vcn, &lcn, &clen, &new, - false, NULL, false); + true, NULL, false); if (err) goto out; } } if (mode & FALLOC_FL_KEEP_SIZE) { - ni_lock(ni); - /* True - Keep preallocated. */ - err = attr_set_size(ni, ATTR_DATA, NULL, 0, - &ni->file.run, i_size, &ni->i_valid, - true); - ni_unlock(ni); + err = ntfs_set_size(inode, i_size); if (err) goto out; - i_size_write(inode, i_size); - } else if (new_size > i_size) { - i_size_write(inode, new_size); } } @@ -850,16 +731,20 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, oldsize = i_size_read(inode); newsize = attr->ia_size; - if (newsize <= oldsize) - err = ntfs_truncate(inode, newsize); - else - err = ntfs_extend(inode, newsize, 0, NULL); + if (newsize != oldsize) { + truncate_setsize(inode, newsize); - if (err) - goto out; + err = ntfs_set_size(inode, newsize); + if (err) { + i_size_write(inode, oldsize); + goto out; + } - ni->ni_flags |= NI_FLAG_UPDATE_PARENT; - i_size_write(inode, newsize); + ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE; + ni->ni_flags |= NI_FLAG_UPDATE_PARENT; + inode_set_mtime_to_ts(inode, + inode_set_ctime_current(inode)); + } } setattr_copy(idmap, inode, attr); @@ -1333,6 +1218,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); + loff_t vbo, endbyte; ssize_t ret, err; if (!inode_trylock(inode)) { @@ -1367,15 +1253,30 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; } - ret = ntfs_extend(inode, iocb->ki_pos, ret, file); - if (ret) - goto out; + vbo = iocb->ki_pos; + endbyte = vbo + ret; + + if (endbyte > inode->i_size) { + /* + * Normal files: increase file size, allocate space. + * Sparse/Compressed: increase file size. No space allocated. + */ + ret = ntfs_set_size(inode, endbyte); + if (ret) + goto out; + } if (is_compressed(ni)) { ret = ntfs_compress_write(iocb, from); goto out; } + if (vbo > ni->i_valid) { + ret = ntfs_extend_initialized_size(file, ni, vbo); + if (ret) + goto out; + } + /* Fallback to buffered I/O if the inode does not support direct I/O. */ if (!(iocb->ki_flags & IOCB_DIRECT) || !ntfs_should_use_dio(iocb, from)) { @@ -1408,7 +1309,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } if (ret >= 0 && iov_iter_count(from)) { - loff_t offset = iocb->ki_pos, endbyte; + vbo = iocb->ki_pos; iocb->ki_flags &= ~IOCB_DIRECT; err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops, @@ -1426,15 +1327,15 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) * to complete off the I/O request. */ ret += err; - endbyte = offset + err - 1; - err = filemap_write_and_wait_range(inode->i_mapping, offset, + endbyte = vbo + err - 1; + err = filemap_write_and_wait_range(inode->i_mapping, vbo, endbyte); if (err) { ret = err; goto out; } - invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT, + invalidate_mapping_pages(inode->i_mapping, vbo >> PAGE_SHIFT, endbyte >> PAGE_SHIFT); } @@ -1515,8 +1416,9 @@ static int ntfs_file_release(struct inode *inode, struct file *file) down_write(&ni->file.run_lock); /* Deallocate preallocated. */ - err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, - inode->i_size, &ni->i_valid, false); + err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, + inode->i_size, &ni->i_valid, false, NULL, + true); up_write(&ni->file.run_lock); ni_unlock(ni); diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 9a75c2d832ca..2d3aad60caa4 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -693,17 +693,18 @@ int ntfs_set_size(struct inode *inode, u64 new_size) return -EFBIG; } + /* Mark rw ntfs as dirty. It will be cleared at umount. */ + ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); + ni_lock(ni); down_write(&ni->file.run_lock); + if (new_size < ni->i_valid) + ni->i_valid = new_size; + /* last 'true' means keep preallocated. */ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size, &ni->i_valid, true); - if (!err) { - i_size_write(inode, new_size); - mark_inode_dirty(inode); - } - up_write(&ni->file.run_lock); ni_unlock(ni); @@ -778,11 +779,6 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return err; } - if (!clen) { - /* broken file? */ - return -EINVAL; - } - if (lcn == EOF_LCN) { /* request out of file. */ if (flags & IOMAP_REPORT) { @@ -816,6 +812,11 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return 0; } + if (!clen) { + /* broken file? */ + return -EINVAL; + } + iomap->bdev = inode->i_sb->s_bdev; iomap->offset = offset; iomap->length = ((loff_t)clen << cluster_bits) - off; From a82fbf481513a9546c06c4120571f75555c04581 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 22 May 2026 15:14:39 +0200 Subject: [PATCH 2523/5214] fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early Handle non-data/hole seeks through generic_file_llseek_size() and return -ENXIO immediately when SEEK_DATA or SEEK_HOLE is requested at or past EOF. Handle compressed files in such cases properly as well. Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 14 ++++++++------ fs/ntfs3/frecord.c | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 06a5d9b44ac1..1b52447bd228 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -1008,7 +1008,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) CLST lcn, clen; frame = valid >> frame_bits; - frame_vbo = valid & ~(frame_size - 1); + frame_vbo = valid & ~(u64)(frame_size - 1); off = valid & (frame_size - 1); err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn, @@ -1077,7 +1077,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) if (bytes > count) bytes = count; - frame_vbo = pos & ~(frame_size - 1); + frame_vbo = pos & ~(u64)(frame_size - 1); index = frame_vbo >> PAGE_SHIFT; if (unlikely(fault_in_iov_iter_readable(from, bytes))) { @@ -1530,7 +1530,12 @@ static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence) loff_t maxbytes = ntfs_get_maxbytes(ni); loff_t ret; - if (whence == SEEK_DATA || whence == SEEK_HOLE) { + if (whence != SEEK_DATA && whence != SEEK_HOLE) { + ret = generic_file_llseek_size(file, offset, whence, maxbytes, + i_size_read(inode)); + } else if ((unsigned long long)offset >= i_size_read(inode)) { + ret = -ENXIO; + } else { inode_lock_shared(inode); /* Scan file for hole or data. */ ret = ni_seek_data_or_hole(ni, offset, whence == SEEK_DATA); @@ -1538,9 +1543,6 @@ static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence) if (ret >= 0) ret = vfs_setpos(file, ret, maxbytes); - } else { - ret = generic_file_llseek_size(file, offset, whence, maxbytes, - i_size_read(inode)); } return ret; } diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 78eb065c7e43..4b5dd3de327b 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2889,8 +2889,14 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) * the file offset is set to offset. */ if (lcn != SPARSE_LCN) { - vbo = (u64)vcn << cluster_bits; - return max(vbo, offset); + /* Normal cluster. */ + break; + } + + if ((ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) && + (vcn & (NTFS_LZNT_CLUSTERS - 1))) { + /* Compressed cluster in compressed frame. */ + break; } } else { /* @@ -2904,8 +2910,8 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) /* native compression hole begins at aligned vcn. */ (!(ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) || !(vcn & (NTFS_LZNT_CLUSTERS - 1)))) { - vbo = (u64)vcn << cluster_bits; - return max(vbo, offset); + /* Hole in sparsed or compressed file frame. */ + break; } } @@ -2914,6 +2920,9 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) return -EINVAL; } } + + vbo = (u64)vcn << cluster_bits; + return max(vbo, offset); } /* From 723325dc9168ec0f3676a1d83f5c4a73ade9059a Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Wed, 27 May 2026 10:23:28 +0200 Subject: [PATCH 2524/5214] fs/ntfs3: format code, deal with comments format code according to .clang-format, add useful comments and remove non-useful comments. Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 2 +- fs/ntfs3/frecord.c | 4 ++-- fs/ntfs3/fsntfs.c | 1 - fs/ntfs3/index.c | 32 ++++++++++++++++++-------------- fs/ntfs3/inode.c | 9 ++------- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 0caf2f8a8c1e..a85d3ee51091 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1152,7 +1152,7 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, struct ATTRIB *attr2; attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, - 0, &vcn0, &mi); + 0, &vcn0, &mi); if (!attr2) { err = -EINVAL; goto out; diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 4b5dd3de327b..0d98bc932c24 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1855,8 +1855,8 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, static struct folio *ntfs_lock_new_page(struct address_space *mapping, pgoff_t index, gfp_t gfp) { - struct folio *folio = __filemap_get_folio(mapping, index, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); + struct folio *folio = __filemap_get_folio( + mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); if (IS_ERR(folio)) return folio; diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index d0434756029b..bc7469d0a34d 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2654,7 +2654,6 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) struct ATTRIB *attr; u32 uni_bytes; struct ntfs_inode *ni = sbi->volume.ni; - /* Allocate PATH_MAX bytes. */ struct cpu_str *uni = kmalloc(PATH_MAX, GFP_KERNEL); if (!uni) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 85f674e9e361..7d6bb14893e9 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1623,7 +1623,8 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, const struct NTFS_DE *new_de, struct NTFS_DE *root_de, const void *ctx, - struct ntfs_fnd *fnd, bool undo, NTFS_CMP_FUNC cmp) + struct ntfs_fnd *fnd, bool undo, + NTFS_CMP_FUNC cmp) { int err = 0; struct NTFS_DE *e, *e0, *re; @@ -1848,13 +1849,15 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, * Attempt to insert an entry into an Index Allocation Buffer. * If necessary, it will split the buffer. */ -static int -indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, - struct INDEX_ROOT *root, const struct NTFS_DE *new_de, - const void *ctx, int level, struct ntfs_fnd *fnd, NTFS_CMP_FUNC cmp) +static int indx_insert_into_buffer(struct ntfs_index *indx, + struct ntfs_inode *ni, + struct INDEX_ROOT *root, + const struct NTFS_DE *new_de, + const void *ctx, int level, + struct ntfs_fnd *fnd, NTFS_CMP_FUNC cmp) { int err; - const struct NTFS_DE *sp; + const struct NTFS_DE *sp; /* split_point */ struct NTFS_DE *e, *de_t, *up_e; struct indx_node *n2; struct indx_node *n1 = fnd->nodes[level]; @@ -1880,10 +1883,9 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, * No space to insert into buffer. Split it. * To split we: * - Save split point ('cause index buffers will be changed) - * - Allocate NewBuffer and copy all entries <= sp into new buffer - * - Remove all entries (sp including) from TargetBuffer - * - Insert NewEntry into left or right buffer (depending on sp <=> - * NewEntry) + * - Allocate new buffer (up_e) and copy all entries <= sp into new buffer + * - Remove all entries (sp including) from hdr1 + * - Insert new_de into left or right buffer (depending on sp <=> new_de) * - Insert sp into parent buffer (or root) * - Make sp a parent for new buffer */ @@ -1897,6 +1899,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, return -ENOMEM; memcpy(up_e, sp, sp_size); + /* Make a copy for undo. */ used1 = le32_to_cpu(hdr1->used); /* @@ -1960,8 +1963,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, */ hdr_insert_de(indx, (*cmp)(new_de + 1, le16_to_cpu(new_de->key_size), - up_e + 1, le16_to_cpu(up_e->key_size), - ctx) < 0 ? + up_e + 1, le16_to_cpu(up_e->key_size), ctx) < 0 ? hdr2 : hdr1, new_de, NULL, ctx, cmp); @@ -1978,11 +1980,13 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, * insert the promoted entry into the parent. */ if (!level) { - /* Insert in root. */ - err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0, cmp); + /* Insert split_point in root. */ + err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0, + cmp); } else { /* * The target buffer's parent is another index buffer. + * Insert split_point in parent index ( call itself recursively ) * TODO: Remove recursion. */ err = indx_insert_into_buffer(indx, ni, root, up_e, ctx, diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 2d3aad60caa4..7423e8c89738 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -592,7 +592,6 @@ static void ntfs_iomap_read_end_io(struct bio *bio) u32 f_size = folio_size(folio); loff_t f_pos = folio_pos(folio); - if (valid < f_pos + f_size) { u32 z_from = valid <= f_pos ? 0 : @@ -765,7 +764,7 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, clen_max = bytes_to_cluster(sbi, endbyte) - vcn; } - /* + /* * Force to allocate clusters if directIO(write) or writeback_range. * NOTE: attr_data_get_block allocates clusters only for sparse file. * Normal file allocates clusters in attr_set_size. @@ -830,7 +829,6 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, iomap->type = IOMAP_DELALLOC; iomap->addr = IOMAP_NULL_ADDR; } else { - /* Translate clusters into bytes. */ iomap->addr = ((loff_t)lcn << cluster_bits) + off; if (length && iomap->length > length) @@ -987,7 +985,6 @@ static ssize_t ntfs_writeback_range(struct iomap_writepage_ctx *wpc, return iomap_add_to_ioend(wpc, folio, offset, end_pos, len); } - static const struct iomap_writeback_ops ntfs_writeback_ops = { .writeback_range = ntfs_writeback_range, .writeback_submit = iomap_ioend_writeback_submit, @@ -1000,7 +997,7 @@ static int ntfs_writepages(struct address_space *mapping, struct inode *inode = mapping->host; struct ntfs_inode *ni = ntfs_i(inode); struct iomap_writepage_ctx wpc = { - .inode = mapping->host, + .inode = inode, .wbc = wbc, .ops = &ntfs_writeback_ops, }; @@ -1280,7 +1277,6 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, if (!(mode & 0222)) fa |= FILE_ATTRIBUTE_READONLY; - /* Allocate PATH_MAX bytes. */ new_de = kzalloc(PATH_MAX, GFP_KERNEL); if (!new_de) { err = -ENOMEM; @@ -1719,7 +1715,6 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; struct NTFS_DE *de; - /* Allocate PATH_MAX bytes. */ de = kzalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; From 70d3855594cf6e8791970714b65cac3202d6160e Mon Sep 17 00:00:00 2001 From: Mihai Brodschi Date: Mon, 11 May 2026 20:19:04 +0300 Subject: [PATCH 2525/5214] ntfs3: Allocate iomap inline_data using alloc_page This fixes a BUG reported in iomap_write_end_inline: iomap_inline_data_valid checks that the inline_data fits within a page. If the inline_data is allocated with kmemdup there's no guarantee that it's page-aligned, so the check sometimes fails. Allocate it with alloc_page to ensure it's page-aligned. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221446 Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations") Signed-off-by: Mihai Brodschi Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 10 +++++++--- fs/ntfs3/inode.c | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index a85d3ee51091..c621a4c582f9 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1001,6 +1001,7 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, struct ATTRIB *attr, *attr_b; struct ATTR_LIST_ENTRY *le, *le_b; struct mft_inode *mi, *mi_b; + struct page *page; CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0; CLST alloc, evcn; unsigned fr; @@ -1036,10 +1037,13 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, *lcn = RESIDENT_LCN; *len = data_size; if (res && data_size) { - *res = kmemdup(resident_data(attr_b), data_size, - GFP_KERNEL); - if (!*res) + page = alloc_page(GFP_KERNEL); + if (!page) { err = -ENOMEM; + } else { + *res = page_address(page); + memcpy(*res, resident_data(attr_b), data_size); + } } goto out; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 7423e8c89738..c43101cc064d 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -796,7 +796,7 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (lcn == RESIDENT_LCN) { if (offset >= clen) { - kfree(res); + __free_page(virt_to_page(res)); if (flags & IOMAP_REPORT) { /* special code for report. */ return -ENOENT; @@ -920,7 +920,7 @@ static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length, out: if (iomap->type == IOMAP_INLINE) { - kfree(iomap->private); + __free_page(virt_to_page(iomap->private)); iomap->private = NULL; } From 1bf15dd17385e3730521d17e9e158c475cd7474b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:09:50 +0200 Subject: [PATCH 2526/5214] ntfs3: avoid another -Wmaybe-uninitialized warning The ntfs3 specific -Wmaybe-uninitialized flag found one more false-postive, this time with gcc-10 on s390: fs/ntfs3/frecord.c: In function 'ni_expand_list': fs/ntfs3/frecord.c:1370:16: error: 'ins_attr' may be used uninitialized in this function [-Werror=maybe-uninitialized] Add an explicit NULL pointer check before using the pointer, and initialize it to NULL. Fixes: 48d9b57b169f ("fs/ntfs3: add a subset of W=1 warnings for stricter checks") Signed-off-by: Arnd Bergmann Signed-off-by: Konstantin Komarov --- fs/ntfs3/frecord.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 0d98bc932c24..abab45f6f180 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1330,7 +1330,7 @@ int ni_expand_list(struct ntfs_inode *ni) { int err = 0; u32 asize, done = 0; - struct ATTRIB *attr, *ins_attr; + struct ATTRIB *attr, *ins_attr = NULL; struct ATTR_LIST_ENTRY *le; bool is_mft = ni->mi.rno == MFT_REC_MFT; struct MFT_REF ref; @@ -1363,7 +1363,7 @@ int ni_expand_list(struct ntfs_inode *ni) le16_to_cpu(attr->name_off), true, &ins_attr, NULL, NULL); - if (err) + if (err || !ins_attr) goto out; memcpy(ins_attr, attr, asize); From b7a9125cac8645245d2473c6c0a50e338280ad23 Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Tue, 19 May 2026 12:42:20 -0700 Subject: [PATCH 2527/5214] fs/ntfs3: fix mount failure on 64K page-size kernels On 64K page-size kernels, mounting NTFS volumes smaller than ~650 MB fails with EINVAL. The issue is in log_replay(): the initial log page size probe uses PAGE_SIZE (65536) instead of DefaultLogPageSize (4096) when PAGE_SIZE exceeds DefaultLogPageSize * 2. This makes norm_file_page() require the $LogFile to be at least 50 * 65536 = 3.2 MB, but mkfs.ntfs creates a $LogFile of only ~1.5 MB for a typical 300 MB volume. norm_file_page() returns 0 and the mount is rejected with EINVAL. On 4K kernels the #if guard evaluates to true, so use_default=true is passed and DefaultLogPageSize (4096) is used, requiring only ~200 KB. This path works fine. Fix this by always passing use_default=true, which forces the initial probe to use DefaultLogPageSize regardless of the kernel's PAGE_SIZE. This is safe because, after reading the on-disk restart area, log_replay() already re-adjusts log->page_size to match the volume's actual sys_page_size. Also fix read_log_page() to pass log->page_size instead of PAGE_SIZE to ntfs_fix_post_read(), matching the actual buffer size. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Tested-by: Matthew R. Ochs Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 767f7cdab8d1..92a7e3a46240 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -1172,7 +1172,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, goto out; if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE) - ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false); + ntfs_fix_post_read(&page_buf->rhdr, log->page_size, false); if (page_buf != *buffer) memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes); @@ -3813,11 +3813,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) log->l_size = log->orig_file_size = ni->vfs_inode.i_size; /* Get the size of page. NOTE: To replay we can use default page. */ -#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2 log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, true); -#else - log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, false); -#endif if (!log->page_size) { err = -EINVAL; goto out; From aa1bdbb39f49c5bc9779316891c40005517842a5 Mon Sep 17 00:00:00 2001 From: Alessandro Schino <7991aleschino@gmail.com> Date: Mon, 11 May 2026 20:15:15 +0200 Subject: [PATCH 2528/5214] ntfs3: fix out-of-bounds read in ntfs_dir_emit() and hdr_find_e() The bounds check in ntfs_dir_emit() compares fname->name_len (a character count) against e->size (a byte count) without accounting for the 2-byte-per-character UTF-16LE encoding or the ATTR_FILE_NAME header size: if (fname->name_len + sizeof(struct NTFS_DE) > le16_to_cpu(e->size)) This computes: name_len + 16 > e_size The correct check must account for the ATTR_FILE_NAME header (66 bytes before the name) and the UTF-16LE character size (2 bytes each): sizeof(NTFS_DE) + offsetof(ATTR_FILE_NAME, name) + name_len * sizeof(short) > e_size Which computes: 16 + 66 + name_len * 2 > e_size The correct calculation already exists as fname_full_size() in ntfs.h and is used in cmp_fnames(), namei.c, and fslog.c, but was not used in the readdir path. A crafted NTFS image with an index entry containing a small e->size but large fname->name_len bypasses the current check, causing ntfs_utf16_to_nls() to read past the entry boundary. Additionally, add a key_size validation in hdr_find_e() to ensure the declared key_size does not exceed the available entry data, preventing comparison functions from reading past entry boundaries on the lookup path. Signed-off-by: Alessandro Schino <7991aleschino@gmail.com> Signed-off-by: Konstantin Komarov --- fs/ntfs3/dir.c | 4 +++- fs/ntfs3/index.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index a052ba9250e7..873d52233003 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -305,7 +305,9 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) return true; - if (fname->name_len + sizeof(struct NTFS_DE) > le16_to_cpu(e->size)) + if (sizeof(struct NTFS_DE) + + offsetof(struct ATTR_FILE_NAME, name) + + fname->name_len * sizeof(short) > le16_to_cpu(e->size)) return true; name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name, diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 7d6bb14893e9..2b439ac04356 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -789,6 +789,10 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, binary_search: e_key_len = le16_to_cpu(e->key_size); + /* Validate key_size fits within the entry data area. */ + if (e_key_len > le16_to_cpu(e->size) - sizeof(struct NTFS_DE)) + return NULL; + diff2 = (*cmp)(key, key_len, e + 1, e_key_len, ctx); if (diff2 > 0) { if (found) { From 376e118551545190debb3901ea4d0e46aa4c1dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Fri, 29 May 2026 16:00:14 +0200 Subject: [PATCH 2529/5214] KVM: x86: Take PIC lock on KVM_GET_IRQCHIP path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When userspace issues the KVM_SET_IRQCHIP ioctl to set the state of the PIC, kvm_vm_ioctl_set_irqchip() grabs @kvm->arch.vpic->lock before updating the state. However, the KVM_GET_IRQCHIP ioctl to retrieve the same PIC state does not grab such lock, potentially causing torn reads for userspace. Fix this by grabbing the lock on the read path. This issue goes all the way back. The bug was introduced with the addition of PIC ioctl code itself in 6ceb9d791eee ("KVM: Add get/ set irqchip ioctls for in-kernel PIC live migration support"). Later, 894a9c5543ab ("KVM: x86: missing locking in PIT/IRQCHIP/SET_BSP_CPU ioctl paths") added the locking for kvm_vm_ioctl_set_irqchip(), but missed kvm_vm_ioctl_get_irqchip(). Fixes: 6ceb9d791eee ("KVM: Add get/set irqchip ioctls for in-kernel PIC live migration support") Fixes: 894a9c5543ab ("KVM: x86: missing locking in PIT/IRQCHIP/SET_BSP_CPU ioctl paths") Reported-by: Claude Code:claude-opus-4.6 Signed-off-by: Carlos López Link: https://patch.msgid.link/20260529140013.14925-2-clopez@suse.de Signed-off-by: Sean Christopherson --- arch/x86/kvm/irq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 9519fec09ee6..8c62c6d4d5c1 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -585,12 +585,16 @@ int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip) r = 0; switch (chip->chip_id) { case KVM_IRQCHIP_PIC_MASTER: + spin_lock(&pic->lock); memcpy(&chip->chip.pic, &pic->pics[0], sizeof(struct kvm_pic_state)); + spin_unlock(&pic->lock); break; case KVM_IRQCHIP_PIC_SLAVE: + spin_lock(&pic->lock); memcpy(&chip->chip.pic, &pic->pics[1], sizeof(struct kvm_pic_state)); + spin_unlock(&pic->lock); break; case KVM_IRQCHIP_IOAPIC: kvm_get_ioapic(kvm, &chip->chip.ioapic); From 607af438e6430893a822964c841a1994b33acccc Mon Sep 17 00:00:00 2001 From: Ali Ahmet MEMIS Date: Sun, 26 Apr 2026 08:09:28 -0700 Subject: [PATCH 2530/5214] tools/power/x86/intel-speed-select: Harden daemon pidfile open Avoid symlink-based pidfile clobbering by opening the pidfile with O_NOFOLLOW and validating it with fstat() before locking/writing. The daemon currently uses a fixed pidfile path under /tmp. A local unprivileged user can pre-create a symlink at that path and cause a root-run daemon instance to write into an attacker-chosen file. Fixes: 7fd786dfbd2c ("tools/power/x86/intel-speed-select: OOB daemon mode") Signed-off-by: Ali Ahmet MEMIS Signed-off-by: Srinivas Pandruvada Cc: stable@kernel.org --- tools/power/x86/intel-speed-select/isst-daemon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 66df21b2b573..acedb7432849 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -148,6 +148,7 @@ static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; + struct stat st; struct sigaction sig_actions; sigset_t sig_set; int ret; @@ -200,11 +201,17 @@ static void daemonize(char *rundir, char *pidfile) if (ret == -1) exit(EXIT_FAILURE); - pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ exit(1); } + + if (fstat(pid_file_handle, &st) == -1) + exit(1); + + if (!S_ISREG(st.st_mode)) + exit(1); /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { From 10400b9455e735601d94e7293db1c48a685547f0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2026 22:01:24 +0100 Subject: [PATCH 2531/5214] media: qcom: iris: Fix FPS calculation and VPP FW overhead Use div_u64() instead of mult_fract as u64 operator division fails on 32 bit systems which don't link against libgcc. Fixes: 5c66647a5c3e ("media: iris: add FPS calculation and VPP FW overhead in frequency formula") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606030132.qnBXVDkM-lkp@intel.com/ Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 5a85568c5ee1..37dbfe433a08 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -444,7 +444,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz /* 1.05 is VPP FW overhead */ if (inst->fw_caps[STAGE].value == STAGE_2) - vpp_cycles += mult_frac(vpp_cycles, 5, 100); + vpp_cycles += div_u64(vpp_cycles * 5, 100); vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); From 7d460ca3917b47429439fd5793ac017b4031a92f Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:16 +0200 Subject: [PATCH 2532/5214] media: qcom: iris: add helpers for 8bit and 10bit formats To simplify code checking for pixel formats, add helpers to check for 8bit and 10bit formats. Reviewed-by: Dikshita Agarwal Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_utils.c | 12 ++++++++++++ drivers/media/platform/qcom/iris/iris_utils.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index 29b07d88507e..085665cd74ff 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -38,6 +38,18 @@ bool iris_split_mode_enabled(struct iris_inst *inst) inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; } +bool iris_fmt_is_8bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_NV12 || + pixelformat == V4L2_PIX_FMT_QC08C; +} + +bool iris_fmt_is_10bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_P010 || + pixelformat == V4L2_PIX_FMT_QC10C; +} + void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state) { diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h index b5705d156431..228a5f963812 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.h +++ b/drivers/media/platform/qcom/iris/iris_utils.h @@ -45,6 +45,8 @@ bool iris_res_is_less_than(u32 width, u32 height, u32 ref_width, u32 ref_height); int iris_get_mbpf(struct iris_inst *inst); bool iris_split_mode_enabled(struct iris_inst *inst); +bool iris_fmt_is_8bit(u32 pixelformat); +bool iris_fmt_is_10bit(u32 pixelformat); struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id); void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state); From 7aa4969dd9af6ad4beef6614d99b3673f42359a5 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:17 +0200 Subject: [PATCH 2533/5214] media: qcom: iris: add QC10C & P010 buffer size calculations The P010 (YUV format with 16-bits per pixel with interleaved UV) and QC10C (P010 compressed mode similar to QC08C) requires specific buffer calculations to allocate the right buffer size for the DPB (decoded picture buffer) frames and frames consumed by userspace. Similar to 8bit, the 10bit DPB frames uses QC10C format. Reviewed-by: Bryan O'Donoghue Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 195 +++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index ef7f6f931557..fbe2dd87f0d6 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -15,8 +15,11 @@ #define MAX_WIDTH 4096 #define MAX_HEIGHT 2304 #define Y_STRIDE_ALIGN 128 +#define Y_STRIDE_ALIGN_P010 256 #define UV_STRIDE_ALIGN 128 +#define UV_STRIDE_ALIGN_P010 256 #define Y_SCANLINE_ALIGN 32 +#define Y_SCANLINE_ALIGN_QC10C 16 #define UV_SCANLINE_ALIGN 16 #define UV_SCANLINE_ALIGN_QC08C 32 #define META_STRIDE_ALIGNED 64 @@ -80,6 +83,63 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst) return ALIGN(y_plane + uv_plane, PIXELS_4K); } +/* + * P010: + * YUV 4:2:0 image with a plane of 10 bit Y samples followed + * by an interleaved U/V plane containing 10 bit 2x2 subsampled + * colour difference samples. + * + * <-Y/UV_Stride (aligned to 256)-> + * <----- Width*2 -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32) + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * U V U V U V U V U V U V . . . . ^ + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K + * + * y_stride : Width*2 aligned to 256 + * uv_stride : Width*2 aligned to 256 + * y_scanlines: Height aligned to 32 + * uv_scanlines: Height/2 aligned to 16 + * Total size = align((y_stride * y_scanlines + * + uv_stride * uv_scanlines , 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_p010(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_stride = ALIGN(f->fmt.pix_mp.width * 2, Y_STRIDE_ALIGN_P010); + uv_stride = ALIGN(f->fmt.pix_mp.width * 2, UV_STRIDE_ALIGN_P010); + y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN); + uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN); + y_plane = y_stride * y_scanlines; + uv_plane = uv_stride * uv_scanlines; + + return ALIGN(y_plane + uv_plane, PIXELS_4K); +} + /* * QC08C: * Compressed Macro-tile format for NV12. @@ -204,6 +264,132 @@ static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst) return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); } +/* + * QC10C: + * UBWC-compressed format for P010. + * Contains 4 planes in the following order - + * (A) Y_Meta_Plane + * (B) Y_UBWC_Plane + * (C) UV_Meta_Plane + * (D) UV_UBWC_Plane + * + * Y_Meta_Plane consists of meta information to decode compressed + * tile data in Y_UBWC_Plane. + * Y_UBWC_Plane consists of Y data in compressed macro-tile format. + * UBWC decoder block will use the Y_Meta_Plane data together with + * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples. + * + * UV_Meta_Plane consists of meta information to decode compressed + * tile data in UV_UBWC_Plane. + * UV_UBWC_Plane consists of UV data in compressed macro-tile format. + * UBWC decoder block will use UV_Meta_Plane data together with + * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2 + * subsampled color difference samples. + * + * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable + * and randomly accessible. There is no dependency between tiles. + * + * <----- Y Meta stride -----> (aligned to 64) + * <-------- Width ----------> (aligned to 48) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height | + * M M M M M M M M M M M M . . | Meta_Y_Scanlines (aligned to 16) + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . V | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . V + * <--Compressed tile Y stride --> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines (aligned to 16) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . . . V + * <---- UV Meta stride ----> (aligned to 64) + * <----- Width / 2 --------> (aligned to 24) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height/2 | + * M M M M M M M M M M M M . . V M_UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * <--Compressed tile UV stride--> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * U* V* U* V* U* V* U* V* . . . . ^ + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * + * y_stride: width aligned to 256 + * uv_stride: width aligned to 256 + * y_scanlines: height aligned to 16 + * uv_scanlines: height aligned to 16 + * y_plane: buffer size aligned to 4096 + * uv_plane: buffer size aligned to 4096 + * y_meta_stride: width aligned to 64 + * y_meta_scanlines: height aligned to 16 + * y_meta_plane: buffer size aligned to 4096 + * uv_meta_stride: width aligned to 64 + * uv_meta_scanlines: height aligned to 16 + * uv_meta_plane: buffer size aligned to 4096 + * + * Total size = align( y_plane + uv_plane + + * y_meta_plane + uv_meta_plane, 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_qc10c(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride; + u32 uv_meta_stride, uv_meta_plane; + u32 y_meta_stride, y_meta_plane; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, 48), + META_STRIDE_ALIGNED); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height, 4), + META_SCANLINE_ALIGNED); + y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K); + + y_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, Y_STRIDE_ALIGN_P010); + y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN_QC10C), + PIXELS_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.width + 1) >> 1, 24), + META_STRIDE_ALIGNED); + uv_meta_plane = uv_meta_stride * + ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.height + 1) >> 1, 4), + META_SCANLINE_ALIGNED); + uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K); + + uv_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, UV_STRIDE_ALIGN_P010); + uv_plane = ALIGN(uv_stride * ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN), + PIXELS_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); +} + static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; @@ -268,10 +454,17 @@ int iris_get_buffer_size(struct iris_inst *inst, case BUF_OUTPUT: if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) return iris_yuv_buffer_size_qc08c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C) + return iris_yuv_buffer_size_qc10c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010) + return iris_yuv_buffer_size_p010(inst); else return iris_yuv_buffer_size_nv12(inst); case BUF_DPB: - return iris_yuv_buffer_size_qc08c(inst); + if (iris_fmt_is_10bit(inst->fmt_dst->fmt.pix_mp.pixelformat)) + return iris_yuv_buffer_size_qc10c(inst); + else + return iris_yuv_buffer_size_qc08c(inst); default: return 0; } From 3ea0343c09be74ae9eda9dff9c153750a6d9b961 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:18 +0200 Subject: [PATCH 2534/5214] media: qcom: iris: gen2: add support for 10bit decoding Add the necessary plumbing into the HFi Gen2 to signal the decoder the right 10bit pixel format and stride when in compressed mode. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_command.c | 75 ++++++++++++++++++- .../qcom/iris/iris_hfi_gen2_defines.h | 1 + drivers/media/platform/qcom/iris/iris_utils.c | 4 +- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 7ac69af63ead..ca2954f8bd3a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -480,8 +480,20 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; - hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? - HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC; + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + hfi_colorformat = HFI_COLOR_FMT_NV12; + break; + case V4L2_PIX_FMT_QC08C: + hfi_colorformat = HFI_COLOR_FMT_NV12_UBWC; + break; + case V4L2_PIX_FMT_P010: + hfi_colorformat = HFI_COLOR_FMT_P010; + break; + case V4L2_PIX_FMT_QC10C: + hfi_colorformat = HFI_COLOR_FMT_TP10_UBWC; + break; + } } else { pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? @@ -516,7 +528,8 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 stride_uv = stride_y; scanline_uv = scanline_y / 2; - if (pixelformat != V4L2_PIX_FMT_NV12) + if (pixelformat != V4L2_PIX_FMT_NV12 && + pixelformat != V4L2_PIX_FMT_P010) return 0; payload[0] = stride_y << 16 | scanline_y; @@ -531,6 +544,61 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 sizeof(u64)); } +static int iris_hfi_gen2_set_ubwc_stride_scanline(struct iris_inst *inst, u32 plane) +{ + u32 meta_stride_y, meta_scanline_y, meta_stride_uv, meta_scanline_uv; + u32 stride_y, scanline_y, stride_uv, scanline_uv; + u32 port = iris_hfi_gen2_get_port(inst, plane); + u32 pixelformat, width, height; + u32 payload[4]; + + if (inst->domain != DECODER || + inst->fmt_src->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_AV1) + return 0; + + pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; + width = inst->fmt_dst->fmt.pix_mp.width; + height = inst->fmt_dst->fmt.pix_mp.height; + + switch (pixelformat) { + case V4L2_PIX_FMT_QC08C: + stride_y = ALIGN(width, 128); + scanline_y = ALIGN(height, 32); + stride_uv = ALIGN(width, 128); + scanline_uv = ALIGN((height + 1) >> 1, 32); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 32), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 8), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 8), 16); + break; + case V4L2_PIX_FMT_QC10C: + stride_y = ALIGN(width * 4 / 3, 256); + scanline_y = ALIGN(height, 16); + stride_uv = ALIGN(width * 4 / 3, 256); + scanline_uv = ALIGN((height + 1) >> 1, 16); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 48), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 4), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + break; + default: + return 0; + } + + payload[0] = stride_y << 16 | scanline_y; + payload[1] = stride_uv << 16 | scanline_uv; + payload[2] = meta_stride_y << 16 | meta_scanline_y; + payload[3] = meta_stride_uv << 16 | meta_scanline_uv; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_UBWC_STRIDE_SCANLINE, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ARRAY, + &payload[0], + sizeof(u32) * 4); +} + static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane) { u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); @@ -619,6 +687,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat }, {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, + {HFI_PROP_UBWC_STRIDE_SCANLINE, iris_hfi_gen2_set_ubwc_stride_scanline }, {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, {HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate }, {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain }, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index d09096a9d5f9..776b21cd11b2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -136,6 +136,7 @@ enum hfi_flip { #define HFI_PROP_OPB_ENABLE 0x03000184 #define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187 #define HFI_PROP_AV1_DRAP_CONFIG 0x03000189 +#define HFI_PROP_UBWC_STRIDE_SCANLINE 0x03000190 #define HFI_PROP_COMV_BUFFER_COUNT 0x03000193 #define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197 #define HFI_PROP_END 0x03FFFFFF diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index 085665cd74ff..ba5c8dc1280c 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -35,7 +35,9 @@ int iris_get_mbpf(struct iris_inst *inst) bool iris_split_mode_enabled(struct iris_inst *inst) { return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 || - inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010 || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C; } bool iris_fmt_is_8bit(u32 pixelformat) From 2f2f76d43314a8ae86aedae28f908a6a03e22521 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:19 +0200 Subject: [PATCH 2535/5214] media: qcom: iris: vdec: update size and stride calculations for 10bit formats Update the gen2 response and vdec s_fmt code to take in account the P010 and QC010 when calculating the width, height and stride. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_response.c | 21 +++++++++++++--- drivers/media/platform/qcom/iris/iris_vdec.c | 24 ++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index c350d231265e..aca90aab8548 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -542,9 +542,24 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) pixmp_ip->width = width; pixmp_ip->height = height; - pixmp_op->width = ALIGN(width, 128); - pixmp_op->height = ALIGN(height, 32); - pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128); + switch (pixmp_op->pixelformat) { + case V4L2_PIX_FMT_P010: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + pixmp_op->width = roundup(width, 192); + pixmp_op->height = ALIGN(height, 16); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(pixmp_op->width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = pixmp_op->width; + break; + } pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); matrix_coeff = subsc_params.color_info & 0xFF; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index b65832042dc8..92e9201cd3a4 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -263,10 +263,28 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; - fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); - fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); fmt->fmt.pix_mp.num_planes = 1; - fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128); + switch (f->fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_P010: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width, 128); + break; + } fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; From 20c3ef4c7cae76d4e15f31c812aa7761def2207a Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:20 +0200 Subject: [PATCH 2536/5214] media: qcom: iris: vdec: update find_format to handle 8bit and 10bit formats The 10bit pixel format can be only used when the decoder identifies the stream as decoding into 10bit pixel format buffers, so update the find_format helper to filter the formats and only allow the proper formats when setting or trying a capture format. Reviewed-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_platform_common.h | 1 + drivers/media/platform/qcom/iris/iris_vdec.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6d69a1e3dcd3..c9256f2323dc 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -17,6 +17,7 @@ struct iris_inst; #define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma)) #define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8) +#define BIT_DEPTH_10 REGISTER_BIT_DEPTH(10, 10) #define CODED_FRAMES_PROGRESSIVE 0x0 #define DEFAULT_MAX_HOST_BUF_COUNT 64 #define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256 diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 92e9201cd3a4..d55671340600 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -93,10 +93,23 @@ static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) for (i = 0; i < size; i++) { if (fmt[i] == pixfmt) - return true; + break; } - return false; + if (i == size) + return false; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (iris_fmt_is_8bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value == BIT_DEPTH_10) + return false; + + if (iris_fmt_is_10bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value != BIT_DEPTH_10) + return false; + } + + return true; } static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) From 65c06d2edded3b1e1633bf75f0f7a26b609ed5ac Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:21 +0200 Subject: [PATCH 2537/5214] media: qcom: iris: vdec: allow GEN2 decoding into 10bit format Add the necessary bits into the gen2 platforms tables and handlers to allow decoding streams into 10bit pixel formats. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen2_response.c | 16 +++++++++++++++- drivers/media/platform/qcom/iris/iris_instance.h | 2 ++ drivers/media/platform/qcom/iris/iris_vdec.c | 2 ++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 7a85c1d4e5e6..acc0ed8adda1 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -31,9 +31,10 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_HEVC, .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, .hfi_id = HFI_PROP_PROFILE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, @@ -262,7 +263,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = BIT_DEPTH, .min = BIT_DEPTH_8, - .max = BIT_DEPTH_8, + .max = BIT_DEPTH_10, .step_or_mask = 1, .value = BIT_DEPTH_8, .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, @@ -996,6 +997,7 @@ static const u32 sm8550_vdec_output_config_params[] = { HFI_PROP_OPB_ENABLE, HFI_PROP_COLOR_FORMAT, HFI_PROP_LINEAR_STRIDE_SCANLINE, + HFI_PROP_UBWC_STRIDE_SCANLINE, }; static const u32 sm8550_venc_output_config_params[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index aca90aab8548..25162ae71357 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -542,6 +542,15 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) pixmp_ip->width = width; pixmp_ip->height = height; + if (subsc_params.bit_depth == BIT_DEPTH_8 && + pixmp_op->pixelformat != V4L2_PIX_FMT_NV12 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC08C) + pixmp_op->pixelformat = V4L2_PIX_FMT_NV12; + else if (subsc_params.bit_depth == BIT_DEPTH_10 && + pixmp_op->pixelformat != V4L2_PIX_FMT_P010 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC10C) + pixmp_op->pixelformat = V4L2_PIX_FMT_P010; + switch (pixmp_op->pixelformat) { case V4L2_PIX_FMT_P010: pixmp_op->width = ALIGN(width, 128); @@ -625,7 +634,12 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) inst->fw_caps[POC].value = subsc_params.pic_order_cnt; inst->fw_caps[TIER].value = subsc_params.tier; - if (subsc_params.bit_depth != BIT_DEPTH_8 || + if (subsc_params.bit_depth == BIT_DEPTH_8) + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_8; + else + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_10; + + if ((subsc_params.bit_depth != BIT_DEPTH_8 && subsc_params.bit_depth != BIT_DEPTH_10) || !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) { dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n", subsc_params.bit_depth, subsc_params.coded_frames); diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index c54d8ec8562a..a770331d1675 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -27,6 +27,8 @@ enum iris_fmt_type_out { enum iris_fmt_type_cap { IRIS_FMT_NV12, IRIS_FMT_QC08C, + IRIS_FMT_TP10, + IRIS_FMT_QC10C, }; /** diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index d55671340600..a8d6354bee28 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -71,6 +71,8 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) static const u32 iris_vdec_formats_cap[] = { [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, + [IRIS_FMT_TP10] = V4L2_PIX_FMT_P010, + [IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C, }; static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) From 9012c4e647df9a3c5450dcccd766877a3efebc46 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Fri, 8 May 2026 18:18:15 -0700 Subject: [PATCH 2538/5214] mm/damon: replace damon_rand() with a per-ctx lockless PRNG damon_rand() on the sampling_addr hot path called get_random_u32_below(), which takes a local_lock_irqsave() around a per-CPU batched entropy pool and periodically refills it with ChaCha20. At elevated nr_regions counts (20k+), the lock_acquire / local_lock pair plus __get_random_u32_below() dominate kdamond perf profiles. Replace the helper with a lockless lfsr113 generator (struct rnd_state) held per damon_ctx and seeded from get_random_u64() in damon_new_ctx(). kdamond is the single consumer of a given ctx, so no synchronization is required. Range mapping uses traditional reciprocal multiplication, similar as get_random_u32_below(); for spans larger than U32_MAX (only reachable on 64-bit) the slow path combines two u32 outputs and uses mul_u64_u64_shr() at 64-bit width. On 32-bit the slow path is dead code and gets eliminated by the compiler. The new helper takes a ctx parameter; damon_split_regions_of() and the kunit tests that call it directly are updated accordingly. lfsr113 is a linear PRNG and MUST NOT be used for anything security-sensitive. DAMON's sampling_addr is not exposed to userspace and is only consumed as a probe point for PTE accessed-bit sampling, so a non-cryptographic PRNG is appropriate here. Tested with paddr monitoring and max_nr_regions=20000: kdamond CPU usage reduced from ~72% to ~50% of one core. Link: https://lore.kernel.org/20260505145212.108644-1-jiayuan.chen@linux.dev Link: https://lore.kernel.org/damon/20260426173346.86238-1-sj@kernel.org/T/#m4f1fd74112728f83a41511e394e8c3fef703039c Link: https://lore.kernel.org/20260509011816.85145-1-sj@kernel.org Signed-off-by: Jiayuan Chen Signed-off-by: SeongJae Park Reviewed-by: SeongJae Park Cc: Shu Anzai Cc: Quanmin Yan Signed-off-by: Andrew Morton --- include/linux/damon.h | 28 +++++++++++++++++++++------- mm/damon/core.c | 12 ++++++++---- mm/damon/paddr.c | 8 ++++---- mm/damon/tests/core-kunit.h | 28 ++++++++++++++++++++++------ mm/damon/vaddr.c | 7 ++++--- 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index c7a31572689b..4d4f031bcb45 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -8,23 +8,18 @@ #ifndef _DAMON_H_ #define _DAMON_H_ +#include #include #include +#include #include #include -#include /* Minimal region size. Every damon_region is aligned by this. */ #define DAMON_MIN_REGION_SZ PAGE_SIZE /* Max priority score for DAMON-based operation schemes */ #define DAMOS_MAX_SCORE (99) -/* Get a random number in [l, r) */ -static inline unsigned long damon_rand(unsigned long l, unsigned long r) -{ - return l + get_random_u32_below(r - l); -} - /** * struct damon_addr_range - Represents an address region of [@start, @end). * @start: Start address of the region (inclusive). @@ -859,8 +854,27 @@ struct damon_ctx { struct list_head adaptive_targets; struct list_head schemes; + + /* Per-ctx PRNG state for damon_rand(); kdamond is the sole consumer. */ + struct rnd_state rnd_state; }; +/* Get a random number in [@l, @r) using @ctx's lockless PRNG. */ +static inline unsigned long damon_rand(struct damon_ctx *ctx, + unsigned long l, unsigned long r) +{ + unsigned long span = r - l; + u64 rnd; + + if (span <= U32_MAX) { + rnd = prandom_u32_state(&ctx->rnd_state); + return l + (unsigned long)((rnd * span) >> 32); + } + rnd = ((u64)prandom_u32_state(&ctx->rnd_state) << 32) | + prandom_u32_state(&ctx->rnd_state); + return l + mul_u64_u64_shr(rnd, span, 64); +} + static inline struct damon_region *damon_next_region(struct damon_region *r) { return container_of(r->list.next, struct damon_region, list); diff --git a/mm/damon/core.c b/mm/damon/core.c index 9f38deddcb30..3a8725e400c6 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -611,6 +611,8 @@ struct damon_ctx *damon_new_ctx(void) INIT_LIST_HEAD(&ctx->adaptive_targets); INIT_LIST_HEAD(&ctx->schemes); + prandom_seed_state(&ctx->rnd_state, get_random_u64()); + return ctx; } @@ -2939,8 +2941,9 @@ static void damon_split_region_at(struct damon_target *t, } /* Split every region in the given target into 'nr_subs' regions */ -static void damon_split_regions_of(struct damon_target *t, int nr_subs, - unsigned long min_region_sz) +static void damon_split_regions_of(struct damon_ctx *ctx, + struct damon_target *t, int nr_subs, + unsigned long min_region_sz) { struct damon_region *r, *next; unsigned long sz_region, sz_sub = 0; @@ -2955,7 +2958,7 @@ static void damon_split_regions_of(struct damon_target *t, int nr_subs, * Randomly select size of left sub-region to be at * least 10 percent and at most 90% of original region */ - sz_sub = ALIGN_DOWN(damon_rand(1, 10) * + sz_sub = ALIGN_DOWN(damon_rand(ctx, 1, 10) * sz_region / 10, min_region_sz); /* Do not allow blank region */ if (sz_sub == 0 || sz_sub >= sz_region) @@ -2996,7 +2999,8 @@ static void kdamond_split_regions(struct damon_ctx *ctx) nr_subregions = 3; damon_for_each_target(t, ctx) - damon_split_regions_of(t, nr_subregions, ctx->min_region_sz); + damon_split_regions_of(ctx, t, nr_subregions, + ctx->min_region_sz); last_nr_regions = nr_regions; } diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 5cdcc5037cbc..c4738cd5e221 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -49,11 +49,11 @@ static void damon_pa_mkold(phys_addr_t paddr) } static void __damon_pa_prepare_access_check(struct damon_region *r, - unsigned long addr_unit) + struct damon_ctx *ctx) { - r->sampling_addr = damon_rand(r->ar.start, r->ar.end); + r->sampling_addr = damon_rand(ctx, r->ar.start, r->ar.end); - damon_pa_mkold(damon_pa_phys_addr(r->sampling_addr, addr_unit)); + damon_pa_mkold(damon_pa_phys_addr(r->sampling_addr, ctx->addr_unit)); } static void damon_pa_prepare_access_checks(struct damon_ctx *ctx) @@ -63,7 +63,7 @@ static void damon_pa_prepare_access_checks(struct damon_ctx *ctx) damon_for_each_target(t, ctx) { damon_for_each_region(r, t) - __damon_pa_prepare_access_check(r, ctx->addr_unit); + __damon_pa_prepare_access_check(r, ctx); } } diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 1b23a22ac04c..866f716e5760 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -273,54 +273,70 @@ static void damon_test_merge_regions_of(struct kunit *test) static void damon_test_split_regions_of(struct kunit *test) { + struct damon_ctx *c; struct damon_target *t; struct damon_region *r; unsigned long sa[] = {0, 300, 500}; unsigned long ea[] = {220, 400, 700}; int i; + c = damon_new_ctx(); + if (!c) + kunit_skip(test, "ctx alloc fail"); + t = damon_new_target(); - if (!t) + if (!t) { + damon_destroy_ctx(c); kunit_skip(test, "target alloc fail"); + } r = damon_new_region(0, 22); if (!r) { damon_free_target(t); + damon_destroy_ctx(c); kunit_skip(test, "region alloc fail"); } damon_add_region(r, t); - damon_split_regions_of(t, 2, 1); + damon_split_regions_of(c, t, 2, 1); KUNIT_EXPECT_LE(test, damon_nr_regions(t), 2u); damon_free_target(t); t = damon_new_target(); - if (!t) + if (!t) { + damon_destroy_ctx(c); kunit_skip(test, "second target alloc fail"); + } r = damon_new_region(0, 220); if (!r) { damon_free_target(t); + damon_destroy_ctx(c); kunit_skip(test, "second region alloc fail"); } damon_add_region(r, t); - damon_split_regions_of(t, 4, 1); + damon_split_regions_of(c, t, 4, 1); KUNIT_EXPECT_LE(test, damon_nr_regions(t), 4u); damon_free_target(t); t = damon_new_target(); - if (!t) + if (!t) { + damon_destroy_ctx(c); kunit_skip(test, "third target alloc fail"); + } for (i = 0; i < ARRAY_SIZE(sa); i++) { r = damon_new_region(sa[i], ea[i]); if (!r) { damon_free_target(t); + damon_destroy_ctx(c); kunit_skip(test, "region alloc fail"); } damon_add_region(r, t); } - damon_split_regions_of(t, 4, 5); + damon_split_regions_of(c, t, 4, 5); KUNIT_EXPECT_LE(test, damon_nr_regions(t), 12u); damon_for_each_region(r, t) KUNIT_EXPECT_GE(test, damon_sz_region(r) % 5ul, 0ul); damon_free_target(t); + + damon_destroy_ctx(c); } static void damon_test_ops_registration(struct kunit *test) diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index dd5f2d7027ac..1b0ebe3b6951 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -333,9 +333,10 @@ static void damon_va_mkold(struct mm_struct *mm, unsigned long addr) */ static void __damon_va_prepare_access_check(struct mm_struct *mm, - struct damon_region *r) + struct damon_region *r, + struct damon_ctx *ctx) { - r->sampling_addr = damon_rand(r->ar.start, r->ar.end); + r->sampling_addr = damon_rand(ctx, r->ar.start, r->ar.end); damon_va_mkold(mm, r->sampling_addr); } @@ -351,7 +352,7 @@ static void damon_va_prepare_access_checks(struct damon_ctx *ctx) if (!mm) continue; damon_for_each_region(r, t) - __damon_va_prepare_access_check(mm, r); + __damon_va_prepare_access_check(mm, r, ctx); mmput(mm); } } From a1e6b0968833c2dd6193d05daf5700f9e0492126 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Sat, 9 May 2026 08:56:31 +0800 Subject: [PATCH 2539/5214] proc/meminfo: expose per-node balloon pages in node meminfo Commit 835de37603ef ("meminfo: add a per node counter for balloon drivers") added NR_BALLOON_PAGES and exposed it in /proc/meminfo. However, the per-node view at /sys/devices/system/node/nodeX/meminfo was not updated, even though the counter is already tracked per-node. Add it to node_read_meminfo() so users can see balloon usage per NUMA node without having to parse the raw vmstat file. Link: https://lore.kernel.org/20260509005631.17183-1-hao.ge@linux.dev Signed-off-by: Hao Ge Acked-by: David Hildenbrand (Arm) Cc: Danilo Krummrich Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton --- drivers/base/node.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/node.c b/drivers/base/node.c index 126f66aa2c3e..f4d9a21cc24e 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -523,6 +523,7 @@ static ssize_t node_read_meminfo(struct device *dev, #ifdef CONFIG_UNACCEPTED_MEMORY "Node %d Unaccepted: %8lu kB\n" #endif + "Node %d Balloon: %8lu kB\n" "Node %d GPUActive: %8lu kB\n" "Node %d GPUReclaim: %8lu kB\n" , @@ -559,6 +560,7 @@ static ssize_t node_read_meminfo(struct device *dev, nid, K(sum_zone_node_page_state(nid, NR_UNACCEPTED)) #endif , + nid, K(node_page_state(pgdat, NR_BALLOON_PAGES)), nid, K(node_page_state(pgdat, NR_GPU_ACTIVE)), nid, K(node_page_state(pgdat, NR_GPU_RECLAIM)) ); From ce872d5a5955bc0e8a9f5c7d3fad85212c13030d Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 11 May 2026 16:43:07 +0800 Subject: [PATCH 2540/5214] mm/memory_hotplug: factor out altmap freeing checks Use a small helper to centralize altmap freeing after verifying that all vmemmap pages were released. This keeps the check consistent between the normal teardown path and the memory hotplug error paths. Link: https://lore.kernel.org/20260511084307.1827127-1-songmuchun@bytedance.com Signed-off-by: Muchun Song Suggested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Reviewed-by: Donet Tom Signed-off-by: Andrew Morton --- mm/memory_hotplug.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 462d8dcd636d..af5489f03771 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1403,6 +1403,12 @@ bool mhp_supports_memmap_on_memory(void) } EXPORT_SYMBOL_GPL(mhp_supports_memmap_on_memory); +static void altmap_free(struct vmem_altmap *altmap) +{ + WARN_ONCE(altmap->alloc, "Altmap not fully unmapped"); + kfree(altmap); +} + static void remove_memory_blocks_and_altmaps(u64 start, u64 size) { unsigned long memblock_size = memory_block_size_bytes(); @@ -1427,12 +1433,8 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size) put_device(&mem->dev); remove_memory_block_devices(cur_start, memblock_size); - arch_remove_memory(cur_start, memblock_size, altmap, NULL); - - /* Verify that all vmemmap pages have actually been freed. */ - WARN(altmap->alloc, "Altmap not fully unmapped"); - kfree(altmap); + altmap_free(altmap); } } @@ -1463,7 +1465,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, /* call arch's memory hotadd */ ret = arch_add_memory(nid, cur_start, memblock_size, ¶ms); if (ret < 0) { - kfree(params.altmap); + altmap_free(params.altmap); goto out; } @@ -1472,7 +1474,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, params.altmap, group); if (ret) { arch_remove_memory(cur_start, memblock_size, params.altmap, NULL); - kfree(params.altmap); + altmap_free(params.altmap); goto out; } } From 2b117897d5c7c5ffdaca3ea40aa7658c54ae7cb8 Mon Sep 17 00:00:00 2001 From: Hongfu Li Date: Tue, 12 May 2026 18:13:05 +0800 Subject: [PATCH 2541/5214] selftests/mm: fix mmap() return value check in run_migration_benchmark mmap() returns MAP_FAILED on error, not NULL. The current check uses !buffer->ptr, which evaluates to false when mmap() fails (since MAP_FAILED is (void *)-1, not 0), so the error path is never taken. Link: https://lore.kernel.org/20260512101305.139509-1-lihongfu@kylinos.cn Signed-off-by: Hongfu Li Acked-by: David Hildenbrand (Arm) Reviewed-by: Dev Jain Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Lorenzo Stoakes Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 77fb4c5d871b..7a4daadfb0c8 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -2738,7 +2738,7 @@ static inline int run_migration_benchmark(int fd, int use_thp, size_t buffer_siz buffer->ptr = mmap(NULL, buffer_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (!buffer->ptr) + if (buffer->ptr == MAP_FAILED) return -1; /* Apply THP hint if requested */ From 42791eddab096b67e368ff0c1f3e331b4b72971a Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:29 +0200 Subject: [PATCH 2542/5214] sparc/mm: remove register_page_bootmem_info() Patch series "mm: remove CONFIG_HAVE_BOOTMEM_INFO_NODE (Part 1)". We want to remove CONFIG_HAVE_BOOTMEM_INFO_NODE. As a first step, let's limit the remaining harm to x86 and core code, removing sparc, ppc and s390 leftovers, starting the stepwise removal by removing and simplifying some code. Once a related x86 vmemmap fix [1] is in, we can merge part 2 that will remove CONFIG_HAVE_BOOTMEM_INFO_NODE entirely. Tested on x86-64 with hugetlb vmemmap optimization in combination with KMEMLEAK, making sure that the problem reported in dd0ff4d12dd2 ("bootmem: remove the vmemmap pages from kmemleak in put_page_bootmem") does not reappear -- hoping I managed to trigger the original problem. This patch (of 8): sparc does not select CONFIG_HAVE_BOOTMEM_INFO_NODE, therefore, register_page_bootmem_info_node() is a nop. Let's just get rid of register_page_bootmem_info(). Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-0-3fb0be6fc688@kernel.org Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-1-3fb0be6fc688@kernel.org Link: https://lore.kernel.org/r/20260429-vmemmap-v2-1-8dfcacffd877@kernel.org [1] Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- arch/sparc/mm/init_64.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 367c269305e5..3b679b1d1d72 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -2477,17 +2476,6 @@ int page_in_phys_avail(unsigned long paddr) return 0; } -static void __init register_page_bootmem_info(void) -{ -#ifdef CONFIG_NUMA - int i; - - for_each_online_node(i) - if (NODE_DATA(i)->node_spanned_pages) - register_page_bootmem_info_node(NODE_DATA(i)); -#endif -} - void __init arch_setup_zero_pages(void) { phys_addr_t zero_page_pa = kern_base + @@ -2498,14 +2486,6 @@ void __init arch_setup_zero_pages(void) void __init mem_init(void) { - /* - * Must be done after boot memory is put on freelist, because here we - * might set fields in deferred struct pages that have not yet been - * initialized, and memblock_free_all() initializes all the reserved - * deferred pages for us. - */ - register_page_bootmem_info(); - if (tlb_type == cheetah || tlb_type == cheetah_plus) cheetah_ecache_flush_init(); } From bf45fe08b0685435320ffa5179714559024ec302 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:30 +0200 Subject: [PATCH 2543/5214] mm/bootmem_info: drop initialization of page->lru In the past, we used to store the type in page->lru.next, introduced by commit 5f24ce5fd34c ("thp: remove PG_buddy"). The location changed over the years; ever since commit 0386aaa6e9c8 ("bootmem: stop using page->index"), we store it alongside the info in page->private. Consequently, there is no need to reset page->lru anymore. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-2-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- mm/bootmem_info.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/bootmem_info.c b/mm/bootmem_info.c index 3d7675a3ae04..a0a1ecdec8d0 100644 --- a/mm/bootmem_info.c +++ b/mm/bootmem_info.c @@ -34,7 +34,6 @@ void put_page_bootmem(struct page *page) if (page_ref_dec_return(page) == 1) { ClearPagePrivate(page); set_page_private(page, 0); - INIT_LIST_HEAD(&page->lru); kmemleak_free_part_phys(PFN_PHYS(page_to_pfn(page)), PAGE_SIZE); free_reserved_page(page); } From 7cb87e71e55bb8f3b234ea173964cd53278af11e Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:31 +0200 Subject: [PATCH 2544/5214] mm/bootmem_info: stop using PG_private Nobody checks PG_private for these pages, and we can happily use set_page_private() without setting PG_private. So let's just stop setting/clearing PG_private. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-3-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- mm/bootmem_info.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/bootmem_info.c b/mm/bootmem_info.c index a0a1ecdec8d0..6e2aaab3dca9 100644 --- a/mm/bootmem_info.c +++ b/mm/bootmem_info.c @@ -19,7 +19,6 @@ void get_page_bootmem(unsigned long info, struct page *page, { BUG_ON(type > 0xf); BUG_ON(info > (ULONG_MAX >> 4)); - SetPagePrivate(page); set_page_private(page, info << 4 | type); page_ref_inc(page); } @@ -32,7 +31,6 @@ void put_page_bootmem(struct page *page) type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE); if (page_ref_dec_return(page) == 1) { - ClearPagePrivate(page); set_page_private(page, 0); kmemleak_free_part_phys(PFN_PHYS(page_to_pfn(page)), PAGE_SIZE); free_reserved_page(page); From cf49b4ebd2ae13554c780eb482e7900447f29ce9 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:32 +0200 Subject: [PATCH 2545/5214] mm/bootmem_info: remove call to kmemleak_free_part_phys() The call to kmemleak_free_part_phys() was added in 2022 in commit dd0ff4d12dd2 ("bootmem: remove the vmemmap pages from kmemleak in put_page_bootmem"). In 2025, commit b2aad24b5333 ("mm/memmap: prevent double scanning of memmap by kmemleak") started to use MEMBLOCK_ALLOC_NOLEAKTRACE when allocating the memmap to skip the kmemleak_alloc_phys() in the buddy. So remove the call to kmemleak_free_part_phys(). If this would still be required for other purposes, either free_reserved_page() should take care of it, or selected users. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-4-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador Reviewed-by: Mike Rapoport (Microsoft) Tested-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- include/linux/bootmem_info.h | 1 - mm/bootmem_info.c | 1 - 2 files changed, 2 deletions(-) diff --git a/include/linux/bootmem_info.h b/include/linux/bootmem_info.h index 492ceeb1cdf8..f724340755e5 100644 --- a/include/linux/bootmem_info.h +++ b/include/linux/bootmem_info.h @@ -82,7 +82,6 @@ static inline void get_page_bootmem(unsigned long info, struct page *page, static inline void free_bootmem_page(struct page *page) { - kmemleak_free_part_phys(PFN_PHYS(page_to_pfn(page)), PAGE_SIZE); free_reserved_page(page); } #endif diff --git a/mm/bootmem_info.c b/mm/bootmem_info.c index 6e2aaab3dca9..74c1116626c8 100644 --- a/mm/bootmem_info.c +++ b/mm/bootmem_info.c @@ -32,7 +32,6 @@ void put_page_bootmem(struct page *page) if (page_ref_dec_return(page) == 1) { set_page_private(page, 0); - kmemleak_free_part_phys(PFN_PHYS(page_to_pfn(page)), PAGE_SIZE); free_reserved_page(page); } } From 0928e9050da334f629d0e4b97c5462aa90023c65 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:33 +0200 Subject: [PATCH 2546/5214] mm/bootmem_info: stop marking the pgdat as NODE_INFO We removed the last user of NODE_INFO in commit 119c31caa59e ("mm/sparse: remove !CONFIG_SPARSEMEM_VMEMMAP leftovers for CONFIG_MEMORY_HOTPLUG"). But it really was never used it besides for safety-checks ever since it was introduced in commit 04753278769f ("memory hotplug: register section/node id to free"), where we had the comment: 5) The node information like pgdat has similar issues. But, this will be able to be solved too by this. (Not implemented yet, but, remembering node id in the pages.) Of course, that never happened, and we are not planning on freeing the node data (pgdat/pglist_data), during memory hotunplug. So let's just stop marking the pgdat as NODE_INFO. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-5-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- mm/bootmem_info.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/mm/bootmem_info.c b/mm/bootmem_info.c index 74c1116626c8..cce1d560f094 100644 --- a/mm/bootmem_info.c +++ b/mm/bootmem_info.c @@ -62,15 +62,8 @@ static void __init register_page_bootmem_info_section(unsigned long start_pfn) void __init register_page_bootmem_info_node(struct pglist_data *pgdat) { - unsigned long i, pfn, end_pfn, nr_pages; + unsigned long pfn, end_pfn; int node = pgdat->node_id; - struct page *page; - - nr_pages = PAGE_ALIGN(sizeof(struct pglist_data)) >> PAGE_SHIFT; - page = virt_to_page(pgdat); - - for (i = 0; i < nr_pages; i++, page++) - get_page_bootmem(node, page, NODE_INFO); pfn = pgdat->node_start_pfn; end_pfn = pgdat_end_pfn(pgdat); From ae751d567baa08342e5e34b378b72a6f9b2cfada Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:34 +0200 Subject: [PATCH 2547/5214] mm/bootmem_info: stop marking mem_section_usage as MIX_SECTION_INFO We never free the ms->usage data for boot memory sections (see section_deactivate()). And to identify whether ms->usage was allocated from memblock, we simply identify it by looking at PG_reserved. Consequently, there is no need to mark ms->usage as MIX_SECTION_INFO. Let's just stop doing that. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-6-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- mm/bootmem_info.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/mm/bootmem_info.c b/mm/bootmem_info.c index cce1d560f094..0fa78db7fbc0 100644 --- a/mm/bootmem_info.c +++ b/mm/bootmem_info.c @@ -38,10 +38,8 @@ void put_page_bootmem(struct page *page) static void __init register_page_bootmem_info_section(unsigned long start_pfn) { - unsigned long mapsize, section_nr, i; + unsigned long section_nr; struct mem_section *ms; - struct mem_section_usage *usage; - struct page *page; start_pfn = SECTION_ALIGN_DOWN(start_pfn); section_nr = pfn_to_section_nr(start_pfn); @@ -50,14 +48,6 @@ static void __init register_page_bootmem_info_section(unsigned long start_pfn) if (!preinited_vmemmap_section(ms)) register_page_bootmem_memmap(section_nr, pfn_to_page(start_pfn), PAGES_PER_SECTION); - - usage = ms->usage; - page = virt_to_page(usage); - - mapsize = PAGE_ALIGN(mem_section_usage_size()) >> PAGE_SHIFT; - - for (i = 0; i < mapsize; i++, page++) - get_page_bootmem(section_nr, page, MIX_SECTION_INFO); } void __init register_page_bootmem_info_node(struct pglist_data *pgdat) From 0b7bf4bd1a7440e1c74c725984f4e20990854b37 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:35 +0200 Subject: [PATCH 2548/5214] s390/mm: use free_reserved_page() in vmem_free_pages() We never select CONFIG_HAVE_BOOTMEM_INFO_NODE on s390. Therefore, free_bootmem_page() nowadays always translates to free_reserved_page(). Let's use free_reserved_page() to replace the free_bootmem_page() loop. We can stop including bootmem_info.h. Likely, vmemmap freeing code could be factored out into the core in the future. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-7-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Heiko Carstens Reviewed-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Ritesh Harjani (IBM) Signed-off-by: Andrew Morton --- arch/s390/mm/vmem.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index eeadff45e0e1..d8b2a60e0c33 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -4,7 +4,6 @@ */ #include -#include #include #include #include @@ -51,7 +50,7 @@ static void vmem_free_pages(unsigned long addr, int order, struct vmem_altmap *a if (PageReserved(page)) { /* allocated from memblock */ while (nr_pages--) - free_bootmem_page(page++); + free_reserved_page(page++); } else { free_pages(addr, order); } From 14d1948fa2384d0208f32be4a046d6edbf9fcc43 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 11 May 2026 16:05:36 +0200 Subject: [PATCH 2549/5214] powerpc/mm: remove CONFIG_HAVE_BOOTMEM_INFO_NODE register_page_bootmem_info_node() essentially only calls register_page_bootmem_memmap(). However, on powerpc that function is a nop. So there is not benefit in using CONFIG_HAVE_BOOTMEM_INFO_NODE anymore, let's just drop it. We can stop including bootmem_info.h. Link: https://lore.kernel.org/20260511-bootmem_info_prep-v1-8-3fb0be6fc688@kernel.org Signed-off-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Acked-by: Michal Hocko Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Lance Yang Cc: Alexander Gordeev Cc: Andreas Larsson Cc: Christian Borntraeger Cc: David S. Miller Cc: Gerald Schaefer Cc: Heiko Carstens Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- arch/powerpc/mm/init_64.c | 8 -------- mm/Kconfig | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index b6f3ae03ca9e..64f0df5bb5cd 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -388,13 +387,6 @@ void __ref vmemmap_free(unsigned long start, unsigned long end, #endif -#ifdef CONFIG_HAVE_BOOTMEM_INFO_NODE -void register_page_bootmem_memmap(unsigned long section_nr, - struct page *start_page, unsigned long size) -{ -} -#endif /* CONFIG_HAVE_BOOTMEM_INFO_NODE */ - #endif /* CONFIG_SPARSEMEM_VMEMMAP */ #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/mm/Kconfig b/mm/Kconfig index e221fa1dc54d..97b079372325 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -537,7 +537,7 @@ endchoice config MEMORY_HOTREMOVE bool "Allow for memory hot remove" - select HAVE_BOOTMEM_INFO_NODE if (X86_64 || PPC64) + select HAVE_BOOTMEM_INFO_NODE if X86_64 depends on MEMORY_HOTPLUG select MIGRATION From 6d544529f97167162486e93bea035e08daa4d053 Mon Sep 17 00:00:00 2001 From: Vineet Agarwal Date: Tue, 12 May 2026 13:19:24 +0530 Subject: [PATCH 2550/5214] selftests/mm: check file initialization writes in split_huge_page_test create_pagecache_thp_and_fd() fills the backing file for the pagecache THP tests using repeated write() calls, but the return value is never checked. If a write fails or completes only partially, the test may continue with an incompletely initialized file and produce misleading results. Check the result of write() and fail the test if the expected number of bytes was not written. [akpm@linux-foundation.org: remove unneeded local, per David] Link: https://lore.kernel.org/da82de92-29d8-457c-9f65-40fc4900b922@kernel.org Link: https://lore.kernel.org/20260512074924.27721-1-agarwal.vineet2006@gmail.com Signed-off-by: Vineet Agarwal Acked-by: David Hildenbrand (Arm) Cc: Lorenzo Stoakes Cc: Wei Yang Cc: Vineet Agarwal Cc: Lorenzo Stoakes Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/split_huge_page_test.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 500d07c4938b..a8725942ee51 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -609,9 +609,13 @@ static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, assert(fd_size % sizeof(buf) == 0); for (i = 0; i < sizeof(buf); i++) buf[i] = (unsigned char)i; - for (i = 0; i < fd_size; i += sizeof(buf)) - write(*fd, buf, sizeof(buf)); - + for (i = 0; i < fd_size; i += sizeof(buf)) { + if (write(*fd, buf, sizeof(buf)) != sizeof(buf)) { + ksft_perror("write testfile"); + close(*fd); + goto err_out_unlink; + } + } close(*fd); sync(); *fd = open("/proc/sys/vm/drop_caches", O_WRONLY); From 9b1b295e9fd354b2263aee80a1ef3605d1eee32e Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 12 May 2026 15:26:35 +0800 Subject: [PATCH 2551/5214] drivers/base/memory: make memory block get/put explicit Rename the memory block lookup helper to make the acquired reference explicit, add memory_block_put() to wrap put_device(), remove find_memory_block(), and use memory_block_get() as the single block-id based lookup interface. This makes it clearer to callers that a successful lookup holds a reference that must be dropped, reducing the chance of forgetting the matching put and leaking the memory block device reference. Link: https://lore.kernel.org/linux-mm/7887915D-E598-42B3-9AFE-BFFBACE8DE2D@linux.dev/#t Link: https://lore.kernel.org/20260512072635.3969576-1-songmuchun@bytedance.com Signed-off-by: Muchun Song Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Michal Hocko Tested-by: Donet Tom Reviewed-by: Lorenzo Stoakes Tested-by: Sumanth Korikkar #s390 Cc: Richard Cheng Acked-by: Mike Rapoport (Microsoft) Cc: Alexander Gordeev Cc: Christian Borntraeger Cc: Danilo Krummrich Cc: Doug Anderson Cc: Greg Kroah-Hartman Cc: Heiko Carstens Cc: Kees Cook Cc: Liam R. Howlett Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: "Rafael J. Wysocki" Cc: Suren Baghdasaryan Cc: Sven Schnelle Cc: Vasily Gorbik Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .../platforms/pseries/hotplug-memory.c | 14 ++----- drivers/base/memory.c | 38 +++++++------------ drivers/base/node.c | 4 +- drivers/s390/char/sclp_mem.c | 17 ++++----- include/linux/memory.h | 7 +++- mm/memory_hotplug.c | 5 +-- 6 files changed, 35 insertions(+), 50 deletions(-) diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index b2f14db59034..5d3b51081ff3 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -164,13 +164,7 @@ static int update_lmb_associativity_index(struct drmem_lmb *lmb) static struct memory_block *lmb_to_memblock(struct drmem_lmb *lmb) { - unsigned long section_nr; - struct memory_block *mem_block; - - section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr)); - - mem_block = find_memory_block(section_nr); - return mem_block; + return memory_block_get(phys_to_block_id(lmb->base_addr)); } static int get_lmb_range(u32 drc_index, int n_lmbs, @@ -220,7 +214,7 @@ static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online) else rc = 0; - put_device(&mem_block->dev); + memory_block_put(mem_block); return rc; } @@ -319,12 +313,12 @@ static int dlpar_remove_lmb(struct drmem_lmb *lmb) rc = dlpar_offline_lmb(lmb); if (rc) { - put_device(&mem_block->dev); + memory_block_put(mem_block); return rc; } __remove_memory(lmb->base_addr, memory_block_size); - put_device(&mem_block->dev); + memory_block_put(mem_block); /* Update memory regions for memory remove */ memblock_remove(lmb->base_addr, memory_block_size); diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 6981b55d582a..d31a421f7483 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -649,7 +649,7 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn) * * Called under device_hotplug_lock. */ -struct memory_block *find_memory_block_by_id(unsigned long block_id) +struct memory_block *memory_block_get(unsigned long block_id) { struct memory_block *mem; @@ -659,16 +659,6 @@ struct memory_block *find_memory_block_by_id(unsigned long block_id) return mem; } -/* - * Called under device_hotplug_lock. - */ -struct memory_block *find_memory_block(unsigned long section_nr) -{ - unsigned long block_id = memory_block_id(section_nr); - - return find_memory_block_by_id(block_id); -} - static struct attribute *memory_memblk_attrs[] = { &dev_attr_phys_index.attr, &dev_attr_state.attr, @@ -701,7 +691,7 @@ static int __add_memory_block(struct memory_block *memory) ret = device_register(&memory->dev); if (ret) { - put_device(&memory->dev); + memory_block_put(memory); return ret; } ret = xa_err(xa_store(&memory_blocks, memory->dev.id, memory, @@ -795,9 +785,9 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state struct memory_block *mem; int ret = 0; - mem = find_memory_block_by_id(block_id); + mem = memory_block_get(block_id); if (mem) { - put_device(&mem->dev); + memory_block_put(mem); return -EEXIST; } mem = kzalloc_obj(*mem); @@ -845,8 +835,8 @@ static void remove_memory_block(struct memory_block *memory) memory->group = NULL; } - /* drop the ref. we got via find_memory_block() */ - put_device(&memory->dev); + /* drop the ref. we got via memory_block_get() */ + memory_block_put(memory); device_unregister(&memory->dev); } @@ -880,7 +870,7 @@ int create_memory_block_devices(unsigned long start, unsigned long size, end_block_id = block_id; for (block_id = start_block_id; block_id != end_block_id; block_id++) { - mem = find_memory_block_by_id(block_id); + mem = memory_block_get(block_id); if (WARN_ON_ONCE(!mem)) continue; remove_memory_block(mem); @@ -908,7 +898,7 @@ void remove_memory_block_devices(unsigned long start, unsigned long size) return; for (block_id = start_block_id; block_id != end_block_id; block_id++) { - mem = find_memory_block_by_id(block_id); + mem = memory_block_get(block_id); if (WARN_ON_ONCE(!mem)) continue; num_poisoned_pages_sub(-1UL, memblk_nr_poison(mem)); @@ -1015,12 +1005,12 @@ int walk_memory_blocks(unsigned long start, unsigned long size, return 0; for (block_id = start_block_id; block_id <= end_block_id; block_id++) { - mem = find_memory_block_by_id(block_id); + mem = memory_block_get(block_id); if (!mem) continue; ret = func(mem, arg); - put_device(&mem->dev); + memory_block_put(mem); if (ret) break; } @@ -1228,22 +1218,22 @@ int walk_dynamic_memory_groups(int nid, walk_memory_groups_func_t func, void memblk_nr_poison_inc(unsigned long pfn) { const unsigned long block_id = pfn_to_block_id(pfn); - struct memory_block *mem = find_memory_block_by_id(block_id); + struct memory_block *mem = memory_block_get(block_id); if (mem) { atomic_long_inc(&mem->nr_hwpoison); - put_device(&mem->dev); + memory_block_put(mem); } } void memblk_nr_poison_sub(unsigned long pfn, long i) { const unsigned long block_id = pfn_to_block_id(pfn); - struct memory_block *mem = find_memory_block_by_id(block_id); + struct memory_block *mem = memory_block_get(block_id); if (mem) { atomic_long_sub(i, &mem->nr_hwpoison); - put_device(&mem->dev); + memory_block_put(mem); } } diff --git a/drivers/base/node.c b/drivers/base/node.c index f4d9a21cc24e..3da91929ad4e 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -849,13 +849,13 @@ static void register_memory_blocks_under_nodes(void) for (block_id = start_block_id; block_id <= end_block_id; block_id++) { struct memory_block *mem; - mem = find_memory_block_by_id(block_id); + mem = memory_block_get(block_id); if (!mem) continue; memory_block_add_nid_early(mem, nid); do_register_memory_block_under_node(nid, mem); - put_device(&mem->dev); + memory_block_put(mem); } } diff --git a/drivers/s390/char/sclp_mem.c b/drivers/s390/char/sclp_mem.c index 78c054e26d17..6df1926d4c62 100644 --- a/drivers/s390/char/sclp_mem.c +++ b/drivers/s390/char/sclp_mem.c @@ -204,7 +204,7 @@ static ssize_t sclp_config_mem_store(struct kobject *kobj, struct kobj_attribute addr = sclp_mem->id * block_size; /* * Hold device_hotplug_lock when adding/removing memory blocks. - * Additionally, also protect calls to find_memory_block() and + * Additionally, also protect calls to memory_block_get() and * sclp_attach_storage(). */ rc = lock_device_hotplug_sysfs(); @@ -231,20 +231,19 @@ static ssize_t sclp_config_mem_store(struct kobject *kobj, struct kobj_attribute sclp_mem_change_state(addr, block_size, 0); goto out_unlock; } - mem = find_memory_block(pfn_to_section_nr(PFN_DOWN(addr))); - put_device(&mem->dev); + mem = memory_block_get(phys_to_block_id(addr)); + memory_block_put(mem); WRITE_ONCE(sclp_mem->config, 1); } else { if (!sclp_mem->config) goto out_unlock; - mem = find_memory_block(pfn_to_section_nr(PFN_DOWN(addr))); + mem = memory_block_get(phys_to_block_id(addr)); if (mem->state != MEM_OFFLINE) { - put_device(&mem->dev); + memory_block_put(mem); rc = -EBUSY; goto out_unlock; } - /* drop the ref just got via find_memory_block() */ - put_device(&mem->dev); + memory_block_put(mem); sclp_mem_change_state(addr, block_size, 0); __remove_memory(addr, block_size); #ifdef CONFIG_KASAN @@ -294,11 +293,11 @@ static ssize_t sclp_memmap_on_memory_store(struct kobject *kobj, struct kobj_att return rc; block_size = memory_block_size_bytes(); sclp_mem = container_of(kobj, struct sclp_mem, kobj); - mem = find_memory_block(pfn_to_section_nr(PFN_DOWN(sclp_mem->id * block_size))); + mem = memory_block_get(phys_to_block_id(sclp_mem->id * block_size)); if (!mem) { WRITE_ONCE(sclp_mem->memmap_on_memory, value); } else { - put_device(&mem->dev); + memory_block_put(mem); rc = -EBUSY; } unlock_device_hotplug(); diff --git a/include/linux/memory.h b/include/linux/memory.h index 5bb5599c6b2b..463dc02f6cff 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -158,7 +158,11 @@ int create_memory_block_devices(unsigned long start, unsigned long size, void remove_memory_block_devices(unsigned long start, unsigned long size); extern void memory_dev_init(void); extern int memory_notify(enum memory_block_state state, void *v); -extern struct memory_block *find_memory_block(unsigned long section_nr); +struct memory_block *memory_block_get(unsigned long block_id); +static inline void memory_block_put(struct memory_block *mem) +{ + put_device(&mem->dev); +} typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *); extern int walk_memory_blocks(unsigned long start, unsigned long size, void *arg, walk_memory_blocks_func_t func); @@ -171,7 +175,6 @@ struct memory_group *memory_group_find_by_id(int mgid); typedef int (*walk_memory_groups_func_t)(struct memory_group *, void *); int walk_dynamic_memory_groups(int nid, walk_memory_groups_func_t func, struct memory_group *excluded, void *arg); -struct memory_block *find_memory_block_by_id(unsigned long block_id); #define hotplug_memory_notifier(fn, pri) ({ \ static __meminitdata struct notifier_block fn##_mem_nb =\ { .notifier_call = fn, .priority = pri };\ diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index af5489f03771..7ac19fab2263 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1423,14 +1423,13 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size) struct vmem_altmap *altmap = NULL; struct memory_block *mem; - mem = find_memory_block(pfn_to_section_nr(PFN_DOWN(cur_start))); + mem = memory_block_get(phys_to_block_id(cur_start)); if (WARN_ON_ONCE(!mem)) continue; altmap = mem->altmap; mem->altmap = NULL; - /* drop the ref. we got via find_memory_block() */ - put_device(&mem->dev); + memory_block_put(mem); remove_memory_block_devices(cur_start, memblock_size); arch_remove_memory(cur_start, memblock_size, altmap, NULL); From d200cfc81c069e2192c6cc082c38d1c8b0427989 Mon Sep 17 00:00:00 2001 From: Vineet Agarwal Date: Tue, 12 May 2026 09:41:57 +0530 Subject: [PATCH 2552/5214] mm/damon/sysfs-schemes: fix double increment of nr_regions damos_sysfs_populate_region_dir() increments sysfs_regions->nr_regions twice when adding a new region: once explicitly before kobject_init_and_add(), and once again through the post-increment used for the kobject name. As a result, nr_regions no longer matches the actual number of live regions, and region directory names skip numbers (1, 3, 5, ...). Use the already incremented value for naming instead of incrementing nr_regions a second time. Link: https://lore.kernel.org/20260512041157.109845-1-agarwal.vineet2006@gmail.com Fixes: 66178e4ec30a ("mm/damon/sysfs: use damos_walk() for update_schemes_tried_{bytes,regions}") Signed-off-by: Vineet Agarwal Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index ab2153fff9a8..0d3021db0b99 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2995,7 +2995,7 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes, if (kobject_init_and_add(®ion->kobj, &damon_sysfs_scheme_region_ktype, &sysfs_regions->kobj, "%d", - sysfs_regions->nr_regions++)) { + sysfs_regions->nr_regions)) { kobject_put(®ion->kobj); return; } From da7bfa6a39fd4d72e03b6bc5f01148ac22fd216e Mon Sep 17 00:00:00 2001 From: Liew Rui Yan Date: Fri, 1 May 2026 09:37:49 +0800 Subject: [PATCH 2553/5214] mm/damon/lru_sort: validate min_region_size to be power of 2 Patch series "mm/damon: validate min_region_size to be power of 2", v5. Problem ======= When a user sets an invalid 'addr_unit' (e.g., 3) via DAMON_LRU_SORT or DAMON_RECLAIM, 'min_region_sz' becomes a non-power-of-2 value. While damon_commit_ctx() correctly detects this and returns -EINVAL, it sets the 'maybe_corrupted' flag during this process. This flag causes the running kdamond to terminate. While the termination is a safety measure, it is suboptimal in this case because the error is just a simple invalid input from the user, which shouldn't neccessitate stopping the kdamond. Solution ======== Add an early validation in damon_lru_sort_apply_parameters() and damon_reclaim_apply_parameters() to check 'min_region_sz' before any state change occurs. If it is non-power-of-2, return -EINVAL immediately, preventing 'maybe_corrupted' from being set. Patch 1 fixes the issue for DAMON_LRU_SORT. Patch 2 fixes the issue for DAMON_RECLAIM. This patch (of 2): Problem ======= When a user sets an invalid 'addr_unit' (e.g., 3) via DAMON_LRU_SORT, 'min_region_sz' becomes a non-power-of-2 value. While damon_commit_ctx() correctly detects this and returns -EINVAL, it sets the 'maybe_corrupted' flag during this process. This flag causes the running kdamond to terminate. While the termination is a safety measure, it is suboptimal in this case because the error is just a simple invalid input from the user, which shouldn't neccessitate stopping the kdamond. Reproduction ============ 1. Enable DAMON_LRU_SORT 2. Set addr_unit=3 3. Commit inputs via 'commit_inputs' 4. Observe kdamond termination Solution ======== Add an early validation in damon_lru_sort_apply_parameters() to check 'min_region_sz' before any state change occurs. If it is non-power-of-2, return -EINVAL immediately, preventing 'maybe_corrupted' from being set. Link: https://lore.kernel.org/20260501013750.71704-1-aethernet65535@gmail.com Link: https://lore.kernel.org/20260501013750.71704-2-aethernet65535@gmail.com Signed-off-by: Liew Rui Yan Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 2eb559d913b6..eca88ed941b3 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -286,6 +286,11 @@ static int damon_lru_sort_apply_parameters(void) param_ctx->addr_unit = addr_unit; param_ctx->min_region_sz = max(DAMON_MIN_REGION_SZ / addr_unit, 1); + if (!is_power_of_2(param_ctx->min_region_sz)) { + err = -EINVAL; + goto out; + } + if (!damon_lru_sort_mon_attrs.sample_interval) { err = -EINVAL; goto out; From d52c1331d28f7a1ae1255cb64d652e86431e6a03 Mon Sep 17 00:00:00 2001 From: Liew Rui Yan Date: Fri, 1 May 2026 09:37:50 +0800 Subject: [PATCH 2554/5214] mm/damon/reclaim: validate min_region_size to be power of 2 Problem ======= When a user sets an invalid 'addr_unit' (e.g., 3) via DAMON_RECLAIM, 'min_region_sz' becomes a non-power-of-2 value. While damon_commit_ctx() correctly detects this and returns -EINVAL, it sets the 'maybe_corrupted' flag during this process. This flag causes the running kdamond to terminate. While the termination is a safety measure, it is suboptimal in this case because the error is just a simple invalid input from the user, which shouldn't neccessitate stopping the kdamond. Reproduction ============ 1. Enable DAMON_RECLAIM 2. Set addr_unit=3 3. Commit inputs via 'commit_inputs' 4. Observe kdamond termination Solution ======== Add an early validation in damon_reclaim_apply_parameters() to check 'min_region_sz' before any state change occurs. If it is non-power-of-2, return -EINVAL immediately, preventing 'maybe_corrupted' from being set. Link: https://lore.kernel.org/20260501013750.71704-3-aethernet65535@gmail.com Signed-off-by: Liew Rui Yan Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- mm/damon/reclaim.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index 7126d47fb8b2..ed446d00ef1c 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -210,6 +210,11 @@ static int damon_reclaim_apply_parameters(void) param_ctx->addr_unit = addr_unit; param_ctx->min_region_sz = max(DAMON_MIN_REGION_SZ / addr_unit, 1); + if (!is_power_of_2(param_ctx->min_region_sz)) { + err = -EINVAL; + goto out; + } + if (!damon_reclaim_mon_attrs.aggr_interval) { err = -EINVAL; goto out; From 7201b96495522c9ed6fcd9a6b95c08e96c6b3df8 Mon Sep 17 00:00:00 2001 From: zenghongling Date: Mon, 11 May 2026 15:03:09 +0800 Subject: [PATCH 2555/5214] mm/percpu-internal.h: optimise pcpu_chunk struct to save memory Using pahole, we can see that there are some padding holes in the current pcpu_chunk structure,Adjusting the layout of pcpu_chunk can reduce these holes,decreasing its size from 192 bytes to 128 bytes and eliminating a wasted cache line. With allmodconfig (CONFIG_PERCPU_STATS + NEED_PCPUOBJ_EXT) Before: /* size: 256, cachelines: 4, members: 19 */ After: /* size: 192, cachelines: 3, members: 19 */ with NEED_PCPUOBJ_EXT Before: struct pcpu_chunk { struct list_head list; /* 0 16 */ int free_bytes; /* 16 4 */ struct pcpu_block_md chunk_md; /* 20 32 */ /* XXX 4 bytes hole, try to pack */ long unsigned int * bound_map; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ void * base_addr __attribute__((__aligned__(64))); /* 64 8 */ long unsigned int * alloc_map; /* 72 8 */ struct pcpu_block_md * md_blocks; /* 80 8 */ void * data; /* 88 8 */ bool immutable; /* 96 1 */ bool isolated; /* 97 1 */ /* XXX 2 bytes hole, try to pack */ int start_offset; /* 100 4 */ int end_offset; /* 104 4 */ /* XXX 4 bytes hole, try to pack */ struct obj_cgroup * * obj_cgroups; /* 112 8 */ int nr_pages; /* 120 4 */ int nr_populated; /* 124 4 */ /* --- cacheline 2 boundary (128 bytes) --- */ int nr_empty_pop_pages; /* 128 4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int populated[]; /* 136 0 */ /* size: 192, cachelines: 3, members: 17 */ /* sum members: 122, holes: 4, sum holes: 14 */ /* padding: 56 */ /* forced alignments: 1 */ } __attribute__((__aligned__(64))); After: struct pcpu_chunk { struct list_head list; /* 0 16 */ int free_bytes; /* 16 4 */ struct pcpu_block_md chunk_md; /* 20 32 */ /* XXX 4 bytes hole, try to pack */ long unsigned int * bound_map; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ void * base_addr __attribute__((__aligned__(64))); /* 64 8 */ long unsigned int * alloc_map; /* 72 8 */ struct pcpu_block_md * md_blocks; /* 80 8 */ void * data; /* 88 8 */ bool immutable; /* 96 1 */ bool isolated; /* 97 1 */ /* XXX 2 bytes hole, try to pack */ int start_offset; /* 100 4 */ int end_offset; /* 104 4 */ int nr_pages; /* 108 4 */ int nr_populated; /* 112 4 */ int nr_empty_pop_pages; /* 116 4 */ struct obj_cgroup * * obj_cgroups; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ long unsigned int populated[]; /* 128 0 */ /* size: 128, cachelines: 2, members: 17 */ /* sum members: 122, holes: 2, sum holes: 6 */ /* forced alignments: 1 */ } __attribute__((__aligned__(64))); Link: https://lore.kernel.org/20260511070309.44044-1-zenghongling@kylinos.cn Signed-off-by: zenghongling Suggested-by: Dennis Zhou Acked-by: Dennis Zhou Cc: Tejun Heo Signed-off-by: Andrew Morton --- mm/percpu-internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/percpu-internal.h b/mm/percpu-internal.h index 4b3d6ec43703..8cbe039bf847 100644 --- a/mm/percpu-internal.h +++ b/mm/percpu-internal.h @@ -77,13 +77,13 @@ struct pcpu_chunk { int end_offset; /* additional area required to have the region end page aligned */ + int nr_pages; /* # of pages served by this chunk */ + int nr_populated; /* # of populated pages */ + int nr_empty_pop_pages; /* # of empty populated pages */ #ifdef NEED_PCPUOBJ_EXT struct pcpuobj_ext *obj_exts; /* vector of object cgroups */ #endif - int nr_pages; /* # of pages served by this chunk */ - int nr_populated; /* # of populated pages */ - int nr_empty_pop_pages; /* # of empty populated pages */ unsigned long populated[]; /* populated bitmap */ }; From a9920428f19481d1227992ecbf1c73efd5b93001 Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Mon, 11 May 2026 10:54:07 +0800 Subject: [PATCH 2556/5214] mm/khugepaged: fix inconsistent MMF_VM_HUGEPAGE flag due to allocation failure order __khugepaged_enter() sets MMF_VM_HUGEPAGE before allocating the corresponding mm_slot. If mm_slot_alloc() fails, the function returns with the flag set but without inserting the mm into the khugepaged tracking structures, leaving the mm in an inconsistent state where future registration attempts are skipped. Fix this by reordering: allocate the mm_slot first, then check and set the flag. If the flag is already set, free the allocated slot and return. This ensures the flag is only set when the mm is successfully registered in the khugepaged tracking structures. Link: https://lore.kernel.org/20260511025408.54035-1-ye.liu@linux.dev Fixes: 16618670276a ("mm: khugepaged: avoid pointless allocation for "struct mm_slot"") Signed-off-by: Ye Liu Suggested-by: David Hildenbrand Reviewed-by: Lance Yang Acked-by: David Hildenbrand (Arm) Reviewed-by: Dev Jain Reviewed-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Cc: Barry Song Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Xin Hao Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 28a843f30b32..a4b97ec8ce56 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -437,13 +437,16 @@ void __khugepaged_enter(struct mm_struct *mm) /* __khugepaged_exit() must not run from under us */ VM_BUG_ON_MM(collapse_test_exit(mm), mm); - if (unlikely(mm_flags_test_and_set(MMF_VM_HUGEPAGE, mm))) - return; slot = mm_slot_alloc(mm_slot_cache); if (!slot) return; + if (unlikely(mm_flags_test_and_set(MMF_VM_HUGEPAGE, mm))) { + mm_slot_free(mm_slot_cache, slot); + return; + } + spin_lock(&khugepaged_mm_lock); mm_slot_insert(mm_slots_hash, mm, slot); /* From 62b21c6f1d88c66a200c2c54b704e503e2e5a60f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 10 May 2026 19:37:00 +0100 Subject: [PATCH 2557/5214] mm/shrinker: avoid out-of-bounds read in set_shrinker_bit() set_shrinker_bit() reads info->unit[shrinker_id_to_index(shrinker_id)] before checking shrinker_id against info->map_nr_max, so an id past the currently visible map_nr_max reads past the unit[] array before the WARN_ON_ONCE() catches it. Determined from code inspection. Move the load into the bounded branch. Link: https://lore.kernel.org/20260510183700.102475-1-devnexen@gmail.com Fixes: 307bececcd12 ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}") Signed-off-by: David Carlier Reviewed-by: Qi Zheng Acked-by: Muchun Song Cc: Dave Chinner Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/shrinker.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 76b3f750cf65..49256f81199f 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -197,12 +197,13 @@ void set_shrinker_bit(struct mem_cgroup *memcg, int nid, int shrinker_id) { if (shrinker_id >= 0 && memcg && !mem_cgroup_is_root(memcg)) { struct shrinker_info *info; - struct shrinker_info_unit *unit; rcu_read_lock(); info = rcu_dereference(memcg->nodeinfo[nid]->shrinker_info); - unit = info->unit[shrinker_id_to_index(shrinker_id)]; if (!WARN_ON_ONCE(shrinker_id >= info->map_nr_max)) { + struct shrinker_info_unit *unit; + + unit = info->unit[shrinker_id_to_index(shrinker_id)]; /* Pairs with smp mb in shrink_slab() */ smp_mb__before_atomic(); set_bit(shrinker_id_to_offset(shrinker_id), unit->map); From fd003fac7cc7d98a942a0778de76683ab731dd9c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 12 May 2026 17:56:23 -0400 Subject: [PATCH 2558/5214] maple_tree: document that "last" in mtree_insert_range() is inclusive The kernel doc of mtree_insert_range() does not state if the address represented by the "last" parameter is inclusive or exclusive. This can lead to bugs by code that assumes it is exclusive. Explicitly state that the parameter is inclusive. Link: https://lore.kernel.org/20260512175623.4c5ca8d2@gandalf.local.home Signed-off-by: Steven Rostedt Reviewed-by: "Liam R. Howlett" Acked-by: SeongJae Park Cc: Alice Ryhl Cc: Andrew Ballance Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- lib/maple_tree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/maple_tree.c b/lib/maple_tree.c index 60ae5e6fc1ee..e52876435b77 100644 --- a/lib/maple_tree.c +++ b/lib/maple_tree.c @@ -5727,13 +5727,16 @@ int mtree_store(struct maple_tree *mt, unsigned long index, void *entry, EXPORT_SYMBOL(mtree_store); /** - * mtree_insert_range() - Insert an entry at a given range if there is no value. + * mtree_insert_range() - Insert an entry from [first, last] at a given range + * if there is no value. * @mt: The maple tree * @first: The start of the range - * @last: The end of the range + * @last: The end of the range (inclusive) * @entry: The entry to store * @gfp: The GFP_FLAGS to use for allocations. * + * Note that @last is inclusive. That is, @last = @first + length - 1; + * * Return: 0 on success, -EEXISTS if the range is occupied, -EINVAL on invalid * request, -ENOMEM if memory could not be allocated. */ From c516c365d9915bafc3d2cdeac50a984da22729b5 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Tue, 12 May 2026 13:31:35 -0700 Subject: [PATCH 2559/5214] mm/readahead: add kerneldoc for read_pages Patch series "mm: document read_pages and simplify usage". Add a kerneldoc for read_pages() to formalize an invariant and then use it to simplify the callers in page_cache_ra_unbounded(). This patch (of 2): Formalize one of the invariants provided by the current implementation so that callers can depend on it, as discussed in [1]. Link: https://lore.kernel.org/all/20260501061146.6e61392d125cf1847d7cc181@linux-foundation.org/ [1] Link: https://lore.kernel.org/20260512203154.754075-2-fmayle@google.com Signed-off-by: Frederick Mayle Cc: Jan Kara Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/readahead.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/readahead.c b/mm/readahead.c index 8c12b63ccd4a..23bec5497308 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -146,6 +146,17 @@ file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping) } EXPORT_SYMBOL_GPL(file_ra_state_init); +/** + * read_pages() - Start IO for a contiguous range of allocated folios in the + * page cache. + * @rac: Readahead control. + * + * When read_pages() returns, it is guaranteed that all of the folios will have + * been processed or removed so that ``readahead_count(rac) == 0``. However, + * that does not imply that ``readahead_index(rac)`` will be updated to point + * to the end of the originally requested range because, for example, the + * filesystem may expand the range upwards. + */ static void read_pages(struct readahead_control *rac) { const struct address_space_operations *aops = rac->mapping->a_ops; From 418bffb6ba2474f445305dd2a5173d8a9ce446b3 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Tue, 12 May 2026 13:31:36 -0700 Subject: [PATCH 2560/5214] mm/readahead: simplify page_cache_ra_unbounded loop counter reset Minor cleanup, no behavior change intended. `read_pages` ensures that `ractl->_nr_pages` is zero before it returns, so the `ractl->_nr_pages` term in these expressions contributes nothing. This seems to have been true since the statements were introduced in commit f615bd5c4725f ("mm/readahead: Handle ractl nr_pages being modified"). The new expression has an intuitive explanation. When filesystems perform readahead, they increment `ractl->_index` by the number of pages processed, so, after `read_pages` returns, `ractl->_index` points to the first page after those already processed. `index` points to the first page considered in the loop. So, `ractl->_index - index` is the number of pages processed by the loop so far. Link: https://lore.kernel.org/20260512203154.754075-3-fmayle@google.com Signed-off-by: Frederick Mayle Cc: Jan Kara Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/readahead.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/readahead.c b/mm/readahead.c index 23bec5497308..42f2f20633b0 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -281,7 +281,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl, */ read_pages(ractl); ractl->_index += min_nrpages; - i = ractl->_index + ractl->_nr_pages - index; + i = ractl->_index - index; continue; } @@ -297,7 +297,7 @@ void page_cache_ra_unbounded(struct readahead_control *ractl, break; read_pages(ractl); ractl->_index += min_nrpages; - i = ractl->_index + ractl->_nr_pages - index; + i = ractl->_index - index; continue; } if (i == mark) From 8e0c2085c978ed6d9764d79fc785920360096f21 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Mon, 4 May 2026 12:06:37 +0200 Subject: [PATCH 2561/5214] lib/test_meminit: use && for bools As pointed out by Dan Carpenter, test_kmemcache() was using a bitwise AND on two bools instead of a boolean AND. Fix this for the sake of code cleanliness. Link: https://lore.kernel.org/20260504100637.1535762-1-glider@google.com Fixes: 5015a300a522 ("lib: introduce test_meminit module") Signed-off-by: Alexander Potapenko Reported-by: Dan Carpenter Closes: https://lore.kernel.org/kernel-janitors/afOcIan1ap9kD26M@stanley.mountain/ Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/test_meminit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_meminit.c b/lib/test_meminit.c index 6298f66c964b..d028a6552cd6 100644 --- a/lib/test_meminit.c +++ b/lib/test_meminit.c @@ -387,7 +387,7 @@ static int __init test_kmemcache(int *total_failures) ctor = flags & 1; rcu = flags & 2; zero = flags & 4; - if (ctor & zero) + if (ctor && zero) continue; num_tests += do_kmem_cache_size(size, ctor, rcu, zero, &failures); From 13f263b60fee0c463f3a9a6c728cd010d8802d69 Mon Sep 17 00:00:00 2001 From: Vineet Agarwal Date: Mon, 4 May 2026 13:43:13 +0530 Subject: [PATCH 2562/5214] selftests/mm: ksm-functional-tests: fix partial write handling Update write() checks to properly detect and handle partial writes. Previously, the write() calls used <= 0 to detect failure. This condition is never true for partial writes (ret > 0 but ret < len), so partial writes were silently treated as success. Fix this by verifying that write() returns the full expected length and treating any mismatch as failure. Link: https://lore.kernel.org/20260504081638.683223-1-agarwal.vineet2006@gmail.com Signed-off-by: Vineet Agarwal Acked-by: Mike Rapoport (Microsoft) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .../selftests/mm/ksm_functional_tests.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c index 8d874c4754f3..31c06c72203f 100644 --- a/tools/testing/selftests/mm/ksm_functional_tests.c +++ b/tools/testing/selftests/mm/ksm_functional_tests.c @@ -498,6 +498,7 @@ static void test_prctl_fork(void) static int start_ksmd_and_set_frequency(char *pages_to_scan, char *sleep_ms) { int ksm_fd; + size_t len; ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR); if (ksm_fd < 0) @@ -506,11 +507,13 @@ static int start_ksmd_and_set_frequency(char *pages_to_scan, char *sleep_ms) if (write(ksm_fd, "1", 1) != 1) return -errno; - if (write(pages_to_scan_fd, pages_to_scan, strlen(pages_to_scan)) <= 0) - return -errno; + len = strlen(pages_to_scan); + if (write(pages_to_scan_fd, pages_to_scan, len) != len) + return -1; - if (write(sleep_millisecs_fd, sleep_ms, strlen(sleep_ms)) <= 0) - return -errno; + len = strlen(sleep_ms); + if (write(sleep_millisecs_fd, sleep_ms, len) != len) + return -1; return 0; } @@ -526,11 +529,11 @@ static int stop_ksmd_and_restore_frequency(void) if (write(ksm_fd, "2", 1) != 1) return -errno; - if (write(pages_to_scan_fd, "100", 3) <= 0) - return -errno; + if (write(pages_to_scan_fd, "100", 3) != 3) + return -1; - if (write(sleep_millisecs_fd, "20", 2) <= 0) - return -errno; + if (write(sleep_millisecs_fd, "20", 2) != 2) + return -1; return 0; } From 7d40e6b66d97d7feef8ca3c096827fd24c6d623d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 3 May 2026 13:59:16 +0200 Subject: [PATCH 2563/5214] mm/mseal: use min/max in mseal_apply Use the type-checked min()/max() macros instead of MIN()/MAX(), which are supposed to be used "for obvious constants only". Link: https://lore.kernel.org/20260503115915.18680-3-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: SeongJae Park Cc: Jann Horn Cc: Liam R. Howlett Cc: Thorsten Blum Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/mseal.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/mseal.c b/mm/mseal.c index e2093ae3d25c..9781647483d1 100644 --- a/mm/mseal.c +++ b/mm/mseal.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -65,8 +66,8 @@ static int mseal_apply(struct mm_struct *mm, prev = vma; for_each_vma_range(vmi, vma, end) { - const unsigned long curr_start = MAX(vma->vm_start, start); - const unsigned long curr_end = MIN(vma->vm_end, end); + const unsigned long curr_start = max(vma->vm_start, start); + const unsigned long curr_end = min(vma->vm_end, end); if (!vma_test(vma, VMA_SEALED_BIT)) { vma_flags_t vma_flags = vma->flags; From fb95c50921f0a65ef9fd734ae712e416db949d91 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 3 May 2026 17:42:25 +0900 Subject: [PATCH 2564/5214] mm/hugetlb_cma: restrict hugetlb_cma parameter to gigantic-page alignment Existing hugetlb_cma parameter handling logic rejects sizes smaller than one gigantic page, but rounds up larger sizes that are not a multiple of it. The two behaviors are inconsistent and neither is documented. To remove existing inconsistent and undefined behavior, restrict hugetlb_cma parameter to only accept multiples of the gigantic page size. After this restriction, the redundant round_up() in the allocation loop can be removed. The new restriction is also documented in kernel-parameters.txt. Also, including other minor changes for readability improvement with no functional change. Link: https://lore.kernel.org/20260503084225.415980-1-ekffu200098@gmail.com Signed-off-by: Sang-Heon Jeon Suggested-by: Muchun Song Acked-by: Muchun Song Acked-by: Oscar Salvador Cc: David Hildenbrand Signed-off-by: Andrew Morton --- .../admin-guide/kernel-parameters.txt | 4 +++ mm/hugetlb_cma.c | 35 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4d0f545fb3ec..23be2f64439c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2100,6 +2100,10 @@ Kernel parameters Format: nn[KMGTPE] or (node format) :nn[KMGTPE][,:nn[KMGTPE]] + The size must be a multiple of the gigantic page size. + When using node format, this applies to each per-node size. + Missaligned values are dropped with a warning. + Reserve a CMA area of given size and allocate gigantic hugepages using the CMA allocator. If enabled, the boot-time allocation of gigantic hugepages is skipped. diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c index 7693ccefd0c6..39344d6c78d8 100644 --- a/mm/hugetlb_cma.c +++ b/mm/hugetlb_cma.c @@ -142,7 +142,7 @@ unsigned int __weak arch_hugetlb_cma_order(void) void __init hugetlb_cma_reserve(void) { - unsigned long size, reserved, per_node, order; + unsigned long size, reserved, per_node, order, gigantic_page_size; bool node_specific_cma_alloc = false; int nid; @@ -162,37 +162,36 @@ void __init hugetlb_cma_reserve(void) * breaking this assumption. */ VM_WARN_ON(order <= MAX_PAGE_ORDER); + gigantic_page_size = PAGE_SIZE << order; hugetlb_bootmem_set_nodes(); for (nid = 0; nid < MAX_NUMNODES; nid++) { - if (hugetlb_cma_size_in_node[nid] == 0) + size = hugetlb_cma_size_in_node[nid]; + if (size == 0) continue; if (!node_isset(nid, hugetlb_bootmem_nodes)) { pr_warn("hugetlb_cma: invalid node %d specified\n", nid); - hugetlb_cma_size -= hugetlb_cma_size_in_node[nid]; - hugetlb_cma_size_in_node[nid] = 0; + } else if (!IS_ALIGNED(size, gigantic_page_size)) { + pr_warn("hugetlb_cma: cma area of node %d must be a multiple of %lu MiB\n", + nid, gigantic_page_size / SZ_1M); + } else { + node_specific_cma_alloc = true; continue; } - if (hugetlb_cma_size_in_node[nid] < (PAGE_SIZE << order)) { - pr_warn("hugetlb_cma: cma area of node %d should be at least %lu MiB\n", - nid, (PAGE_SIZE << order) / SZ_1M); - hugetlb_cma_size -= hugetlb_cma_size_in_node[nid]; - hugetlb_cma_size_in_node[nid] = 0; - } else { - node_specific_cma_alloc = true; - } + hugetlb_cma_size -= size; + hugetlb_cma_size_in_node[nid] = 0; } /* Validate the CMA size again in case some invalid nodes specified. */ if (!hugetlb_cma_size) return; - if (hugetlb_cma_size < (PAGE_SIZE << order)) { - pr_warn("hugetlb_cma: cma area should be at least %lu MiB\n", - (PAGE_SIZE << order) / SZ_1M); + if (!IS_ALIGNED(hugetlb_cma_size, gigantic_page_size)) { + pr_warn("hugetlb_cma: cma area must be a multiple of %lu MiB\n", + gigantic_page_size / SZ_1M); hugetlb_cma_size = 0; return; } @@ -204,7 +203,7 @@ void __init hugetlb_cma_reserve(void) */ per_node = DIV_ROUND_UP(hugetlb_cma_size, nodes_weight(hugetlb_bootmem_nodes)); - per_node = round_up(per_node, PAGE_SIZE << order); + per_node = round_up(per_node, gigantic_page_size); pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n", hugetlb_cma_size / SZ_1M, per_node / SZ_1M); } @@ -223,15 +222,13 @@ void __init hugetlb_cma_reserve(void) size = min(per_node, hugetlb_cma_size - reserved); } - size = round_up(size, PAGE_SIZE << order); - snprintf(name, sizeof(name), "hugetlb%d", nid); /* * Note that 'order per bit' is based on smallest size that * may be returned to CMA allocator in the case of * huge page demotion. */ - res = cma_declare_contiguous_multi(size, PAGE_SIZE << order, + res = cma_declare_contiguous_multi(size, gigantic_page_size, HUGETLB_PAGE_ORDER, name, &hugetlb_cma[nid], nid); if (res) { From 80eacd489a50ab2a560bc233b26b94ad9df68410 Mon Sep 17 00:00:00 2001 From: Takahiro Itazuri Date: Wed, 13 May 2026 09:35:46 -0700 Subject: [PATCH 2565/5214] mm/mmu_notifier: fix a begin vs. start typo in the invalidate range comment Fix a goof in the block comment for invalidate_range_{start,end}() where start() is incorrectly referred to as begin(). No functional change intended. [seanjc@google.com: split to separate patch, write changelog] Link: https://lore.kernel.org/20260513163546.1176742-1-seanjc@google.com Signed-off-by: Takahiro Itazuri Signed-off-by: Sean Christopherson Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Reviewed-by: Mike Rapoport (Microsoft) Cc: Liam R. Howlett Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/mmu_notifier.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h index 69c304b467df..a11a44eef521 100644 --- a/include/linux/mmu_notifier.h +++ b/include/linux/mmu_notifier.h @@ -134,8 +134,8 @@ struct mmu_notifier_ops { * Invalidation of multiple concurrent ranges may be * optionally permitted by the driver. Either way the * establishment of sptes is forbidden in the range passed to - * invalidate_range_begin/end for the whole duration of the - * invalidate_range_begin/end critical section. + * invalidate_range_start/end for the whole duration of the + * invalidate_range_start/end critical section. * * invalidate_range_start() is called when all pages in the * range are still mapped and have at least a refcount of one. From de97ae6222c1326db5475467879887d0dd2c62a6 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Fri, 8 May 2026 11:12:31 -0700 Subject: [PATCH 2566/5214] mm/readahead: no PG_readahead on EOF When readahead pulls in all the remaining pages for a file, setting the readahead bit is counter productive. The async readahead it would trigger would almost certainly be a no-op. Additionally, for mmap'd file IO, the readahead bit limits the fault around [1], causing an extra minor fault when the page is accessed. This was discovered when looking at /sys/kernel/tracing/events/readahead traces for a simple program. With the patch applied, fewer page_cache_ra_unbounded calls are observed. [1] do_fault_around calls filemap_map_pages, which finds eligible pages by calling next_uptodate_folio [2]. next_uptodate_folio skips pages with PG_readahead set [3]. Link: https://github.com/torvalds/linux/blob/v7.0/mm/filemap.c#L3921-L3939 [2] Link: https://github.com/torvalds/linux/blob/v7.0/mm/filemap.c#L3721-L3722 [3] Link: https://lore.kernel.org/20260508181237.670645-1-fmayle@google.com Signed-off-by: Frederick Mayle Reviewed-by: Jan Kara Cc: Kalesh Singh Cc: Suren Baghdasaryan Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/readahead.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mm/readahead.c b/mm/readahead.c index 42f2f20633b0..38ce16e3fcbd 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -340,8 +340,11 @@ static void do_page_cache_ra(struct readahead_control *ractl, if (index > end_index) return; /* Don't read past the page containing the last byte of the file */ - if (nr_to_read > end_index - index) + if (nr_to_read > end_index - index) { nr_to_read = end_index - index + 1; + /* We've reached the end, so don't set a readahead marker. */ + lookahead_size = 0; + } filemap_invalidate_lock_shared(mapping); page_cache_ra_unbounded(ractl, nr_to_read, lookahead_size); @@ -485,7 +488,7 @@ void page_cache_ra_order(struct readahead_control *ractl, pgoff_t index = start; unsigned int min_order = mapping_min_folio_order(mapping); pgoff_t limit; - pgoff_t mark = index + ra->size - ra->async_size; + pgoff_t mark; unsigned int nofs; int err = 0; gfp_t gfp = readahead_gfp_mask(mapping); @@ -499,7 +502,13 @@ void page_cache_ra_order(struct readahead_control *ractl, limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT; limit = min(limit, ractl->_max_index); - limit = min(limit, index + ra->size - 1); + if (limit > index + ra->size - 1) { + limit = index + ra->size - 1; + mark = index + ra->size - ra->async_size; + } else { + /* We've reached the end, so don't set a readahead marker. */ + mark = ULONG_MAX; + } new_order = min(mapping_max_folio_order(mapping), new_order); new_order = min_t(unsigned int, new_order, ilog2(ra->size)); From 395085eacdfa37a64b37ae16a6dc467fb8670faf Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Wed, 13 May 2026 17:21:11 +0800 Subject: [PATCH 2567/5214] mm, swap: avoid leaving unused extend table after alloc race Allocating an extend table requires dropping the ci lock first. While the lock is dropped, a concurrent put can decrease the slot's swap count to a value that is no longer maxed out, so the extend table is no longer required. The current allocation path still attach the new extend table to the cluster anyway, leaving it unused. The next maxed out count on the same cluster may still reuse the table, and frees it properly. But swapoff could leak it indeed. To eliminate the waste, re-check under the ci lock that the extend table is still needed before publishing it, and free the local allocation otherwise. Also close the check window by ensuring every count decrement that brings a slot below SWP_TB_COUNT_MAX - 1 runs swap_extend_table_try_free(), not just the MAX to MAX - 1 transition. With this, a freshly published extend table that becomes redundant due to a racing put is freed on the very next decrement, restoring the invariant that an empty cluster never has a non-NULL ci->extend_table. The added overhead is ignorable. [kasong@tencent.com: v2] Link: https://lore.kernel.org/20260515-swap-extend-table-fix-v2-1-833d72ad53e5@tencent.com Link: https://lore.kernel.org/20260513-swap-extend-table-fix-v1-1-a71dea851fb3@tencent.com Fixes: 0d6af9bcf383 ("mm, swap: use the swap table to track the swap count") Signed-off-by: Kairui Song Reported-by: Breno Leitao Closes: https://lore.kernel.org/linux-mm/agG6Dp0umhs6O1SY@gmail.com/ Tested-by: Breno Leitao Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: Kemeng Shi Cc: Nhat Pham Signed-off-by: Andrew Morton --- mm/swapfile.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 74a1e324449d..ee515a6fbccd 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1443,8 +1443,10 @@ static bool swap_sync_discard(void) } static int swap_extend_table_alloc(struct swap_info_struct *si, - struct swap_cluster_info *ci, gfp_t gfp) + struct swap_cluster_info *ci, + unsigned int ci_off, gfp_t gfp) { + int count; void *table; table = kzalloc(sizeof(ci->extend_table[0]) * SWAPFILE_CLUSTER, gfp); @@ -1452,12 +1454,28 @@ static int swap_extend_table_alloc(struct swap_info_struct *si, return -ENOMEM; spin_lock(&ci->lock); - if (!ci->extend_table) - ci->extend_table = table; - else - kfree(table); + /* + * Extend table allocation requires releasing ci lock first so it's + * possible that the slot has been freed, no longer overflowed, or + * a concurrent extend table allocation has already succeeded, so + * the allocation is no longer needed. + */ + if (!cluster_table_is_alloced(ci)) + goto out_free; + count = swp_tb_get_count(__swap_table_get(ci, ci_off)); + if (count < (SWP_TB_COUNT_MAX - 1)) + goto out_free; + if (ci->extend_table) + goto out_free; + + ci->extend_table = table; spin_unlock(&ci->lock); return 0; + +out_free: + spin_unlock(&ci->lock); + kfree(table); + return 0; } int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp) @@ -1472,7 +1490,7 @@ int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp) return 0; ci = __swap_offset_to_cluster(si, offset); - ret = swap_extend_table_alloc(si, ci, gfp); + ret = swap_extend_table_alloc(si, ci, swp_cluster_offset(entry), gfp); put_swap_device(si); return ret; @@ -1519,13 +1537,21 @@ static void __swap_cluster_put_entry(struct swap_cluster_info *ci, if (count == (SWP_TB_COUNT_MAX - 1)) { ci->extend_table[ci_off] = 0; __swap_table_set(ci, ci_off, __swp_tb_mk_count(swp_tb, count)); - swap_extend_table_try_free(ci); } else { ci->extend_table[ci_off] = count; } } else { __swap_table_set(ci, ci_off, __swp_tb_mk_count(swp_tb, --count)); } + + /* + * `SWP_TB_COUNT_MAX - 1` triggers extend table allocation. If the + * count was above that, then the extend table is no longer needed, + * so free it. And if we just put the count value from MAX - 1, it's + * also possible that a pending dup just attached an extend table. + */ + if (unlikely(count == SWP_TB_COUNT_MAX - 2 || count == SWP_TB_COUNT_MAX - 1)) + swap_extend_table_try_free(ci); } /** @@ -1665,7 +1691,7 @@ static int swap_dup_entries_cluster(struct swap_info_struct *si, if (unlikely(err)) { if (err == -ENOMEM) { spin_unlock(&ci->lock); - err = swap_extend_table_alloc(si, ci, GFP_ATOMIC); + err = swap_extend_table_alloc(si, ci, ci_off, GFP_ATOMIC); spin_lock(&ci->lock); if (!err) goto restart; From 59f19bf6f119eecfa16355186b593abba8eb5198 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Wed, 13 May 2026 16:25:25 +0800 Subject: [PATCH 2568/5214] lib/test_hmm: use kvfree() to free kvcalloc() allocations Coccinelle scripts/coccinelle/api/kfree_mismatch.cocci reports the following warnings: lib/test_hmm.c:1256:15-16: WARNING kvmalloc is used to allocate this memory at line 1191 lib/test_hmm.c:1257:15-16: WARNING kvmalloc is used to allocate this memory at line 1196 Fix this by replacing kfree() with kvfree() to correctly handle the vmalloc() fallback path of kvcalloc(). Link: https://lore.kernel.org/20260513082525.154036-1-hao.ge@linux.dev Fixes: 775465fd26a3 ("lib/test_hmm: add zone device private THP test infrastructure") Signed-off-by: Hao Ge Acked-by: Balbir Singh Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Signed-off-by: Andrew Morton --- lib/test_hmm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 213504915737..38996c4baa40 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1253,8 +1253,8 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror, mmap_read_unlock(mm); mmput(mm); free_mem: - kfree(src_pfns); - kfree(dst_pfns); + kvfree(src_pfns); + kvfree(dst_pfns); return ret; } From 0496a59745b0723ea74274db16fd5c8b1379b9a9 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 13 May 2026 11:14:16 +0300 Subject: [PATCH 2569/5214] userfaultfd: ensure mremap_userfaultfd_fail() releases mmap_changing Sashiko says: mremap_userfaultfd_prep() increments ctx->mmap_changing to stall concurrent operations, but mremap_userfaultfd_fail() does not decrement it before dropping the context reference. If an mremap operation fails, ctx->mmap_changing remains elevated. This will causes subsequent userfaultfd operations like a UFFDIO_COPY to fail with -EAGAIN. Decrement ctx->mmap_changing in mremap_userfaultfd_fail(). Link: https://sashiko.dev/#/patchset/20260430113512.115938-1-rppt@kernel.org Link: https://lore.kernel.org/20260513081416.495963-1-rppt@kernel.org Fixes: df2cc96e7701 ("userfaultfd: prevent non-cooperative events vs mcopy_atomic races") Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Peter Xu Cc: Signed-off-by: Andrew Morton --- fs/userfaultfd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 4b53dc4a3266..390e4b7d9cb9 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -786,6 +786,8 @@ void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) if (!ctx) return; + atomic_dec(&ctx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); userfaultfd_ctx_put(ctx); } From 12ccf2bef35c4f42f7bf433f7ab699ec103e7f53 Mon Sep 17 00:00:00 2001 From: wangxuewen <18810879172@163.com> Date: Wed, 13 May 2026 15:52:14 +0800 Subject: [PATCH 2570/5214] mm/shrinker: simplify shrinker_memcg_alloc() using guard() Use guard(mutex) to automatically handle shrinker_mutex locking and unlocking in shrinker_memcg_alloc(). This removes the explicit mutex_unlock() call, the goto-based error path, and the redundant ret variable, resulting in cleaner and more concise code. Link: https://lore.kernel.org/20260513075214.2655710-1-18810879172@163.com Signed-off-by: wangxuewen Acked-by: Muchun Song Cc: Dave Chinner Cc: Roman Gushchin Cc: Xuewen Wang Signed-off-by: Andrew Morton --- mm/shrinker.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 49256f81199f..7082d01c8c9d 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -216,29 +216,26 @@ static DEFINE_IDR(shrinker_idr); static int shrinker_memcg_alloc(struct shrinker *shrinker) { - int id, ret = -ENOMEM; + int id; if (mem_cgroup_disabled()) return -ENOSYS; if (mem_cgroup_kmem_disabled() && !(shrinker->flags & SHRINKER_NONSLAB)) return -ENOSYS; - mutex_lock(&shrinker_mutex); + guard(mutex)(&shrinker_mutex); id = idr_alloc(&shrinker_idr, shrinker, 0, 0, GFP_KERNEL); if (id < 0) - goto unlock; + return id; if (id >= shrinker_nr_max) { if (expand_shrinker_info(id)) { idr_remove(&shrinker_idr, id); - goto unlock; + return -ENOMEM; } } shrinker->id = id; - ret = 0; -unlock: - mutex_unlock(&shrinker_mutex); - return ret; + return 0; } static void shrinker_memcg_remove(struct shrinker *shrinker) From 96f9fb92126a4fb5b24a54964eaef8f82cc2ab7f Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Wed, 13 May 2026 10:21:16 +0800 Subject: [PATCH 2571/5214] tools/mm/page-types: fix typo in madvise() error message Patch series "tools/mm/page-types: Fix misc bugs". This series fixes three issues in tools/mm/page-types.c: 1. Fix two typos in madvise() error messages ("madvice" -> "madvise") 2. Fix operator precedence bug in the sigbus handler where the ternary operator binds looser than addition, producing incorrect offset calculation when sigbus_addr is non-NULL 3. Fix --kpageflags option declaration in getopt_long: has_arg should be 1 (required_argument) since the option requires a file path This patch (of 3): Two error messages incorrectly spelled the madvise() function name as "madvice". Fix the typo in both occurrences. Link: https://lore.kernel.org/20260513022120.58033-1-ye.liu@linux.dev Link: https://lore.kernel.org/20260513022120.58033-2-ye.liu@linux.dev Signed-off-by: Ye Liu Acked-by: David Hildenbrand (Arm) Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- tools/mm/page-types.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c index d7e5e8902af8..6594245217a8 100644 --- a/tools/mm/page-types.c +++ b/tools/mm/page-types.c @@ -997,7 +997,7 @@ static void walk_file_range(const char *name, int fd, /* turn off readahead */ if (madvise(ptr, len, MADV_RANDOM)) - fatal("madvice failed: %s", name); + fatal("madvise failed: %s", name); if (sigsetjmp(sigbus_jmp, 1)) { end = off + sigbus_addr ? sigbus_addr - ptr : 0; @@ -1015,7 +1015,7 @@ static void walk_file_range(const char *name, int fd, /* turn off harvesting reference bits */ if (madvise(ptr, len, MADV_SEQUENTIAL)) - fatal("madvice failed: %s", name); + fatal("madvise failed: %s", name); if (pagemap_read(buf, (unsigned long)ptr / page_size, nr_pages) != nr_pages) From e696ff06db374c1adb877d20e56085abe1d109a3 Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Wed, 13 May 2026 10:21:17 +0800 Subject: [PATCH 2572/5214] tools/mm/page-types: fix ternary operator precedence in sigbus handler The ternary operator (?:) has lower precedence than addition (+), so the expression `off + sigbus_addr ? sigbus_addr - ptr : 0` was parsed as `(off + sigbus_addr) ? (sigbus_addr - ptr) : 0` rather than the intended `off + (sigbus_addr ? sigbus_addr - ptr : 0)`. Add explicit parentheses to ensure the correct evaluation order. Link: https://lore.kernel.org/20260513022120.58033-3-ye.liu@linux.dev Signed-off-by: Ye Liu Acked-by: SeongJae Park Cc: David Hildenbrand (Arm) Signed-off-by: Andrew Morton --- tools/mm/page-types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c index 6594245217a8..66f429f2b698 100644 --- a/tools/mm/page-types.c +++ b/tools/mm/page-types.c @@ -1000,7 +1000,7 @@ static void walk_file_range(const char *name, int fd, fatal("madvise failed: %s", name); if (sigsetjmp(sigbus_jmp, 1)) { - end = off + sigbus_addr ? sigbus_addr - ptr : 0; + end = off + (sigbus_addr ? sigbus_addr - ptr : 0); fprintf(stderr, "got sigbus at offset %lld: %s\n", (long long)end, name); goto got_sigbus; From 3fb355431eb864a95be3b832605d0575f43d6971 Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Wed, 13 May 2026 10:21:18 +0800 Subject: [PATCH 2573/5214] tools/mm/page-types: fix kpageflags option argument in getopt_long The --kpageflags option requires an argument to specify the kpageflags file path, but has_arg was set to 0 (no_argument) in the long options table. Change it to 1 (required_argument) so getopt_long correctly parses the argument. Link: https://lore.kernel.org/20260513022120.58033-4-ye.liu@linux.dev Signed-off-by: Ye Liu Acked-by: David Hildenbrand (Arm) Reviewed-by: SeongJae Park Signed-off-by: Andrew Morton --- tools/mm/page-types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c index 66f429f2b698..7fc5a8be5997 100644 --- a/tools/mm/page-types.c +++ b/tools/mm/page-types.c @@ -1261,7 +1261,7 @@ static const struct option opts[] = { { "no-summary", 0, NULL, 'N' }, { "hwpoison" , 0, NULL, 'X' }, { "unpoison" , 0, NULL, 'x' }, - { "kpageflags", 0, NULL, 'F' }, + { "kpageflags", 1, NULL, 'F' }, { "help" , 0, NULL, 'h' }, { NULL , 0, NULL, 0 } }; From 88e09fffeef5825931e6374b9e88d4b1a1d5f6f8 Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Tue, 12 May 2026 16:45:59 -0400 Subject: [PATCH 2574/5214] mm/filemap: fix page_cache_prev_miss() when no hole is found page_cache_prev_miss() is documented to return a value outside the searched range when no gap is found. However, the no-gap-found path returns xas.xa_index, which after a successful loop is the first index in the range. As such, that index is misreported as a gap. The sole caller, page_cache_sync_ra(), uses the return value to estimate the cached run preceding a sequential read. In some cases, the buggy return value can undercount the contiguous range by one, shrinking the readahead window or pushing borderline requests into the small-random-read branch. Fix this by returning the start of the range - 1 when no hole is found. Update page_cache_next_miss() for clarity as well. Both helpers were previously fixed together in commit 9425c591e06a ("page cache: fix page_cache_next/prev_miss off by one"), but the fix was reverted because it caused a hugetlb performance regression. hugetlb no longer uses these functions and next_miss was subsequently refixed in commit 901a269ff3d5 ("filemap: fix page_cache_next_miss() when no hole found") and commit bbcaee20e03e ("readahead: fix return value of page_cache_next_miss() when no hole is found"), but prev_miss was not addressed. This was found by pointing Claude Opus 4.7 at mm/filemap.c. Link: https://lore.kernel.org/20260512-prev_miss_fix-v2-1-4af8e5c1ae62@columbia.edu Fixes: 0d3f92966629 ("page cache: Convert hole search to XArray") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Tal Zussman Reviewed-by: Jan Kara Reviewed-by: Vishal Moola Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/filemap.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index ab34cab2416a..4263d9775998 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1808,9 +1808,8 @@ pgoff_t page_cache_next_miss(struct address_space *mapping, pgoff_t index, unsigned long max_scan) { XA_STATE(xas, &mapping->i_pages, index); - unsigned long nr = max_scan; - while (nr--) { + while (max_scan--) { void *entry = xas_next(&xas); if (!entry || xa_is_value(entry)) return xas.xa_index; @@ -1818,7 +1817,8 @@ pgoff_t page_cache_next_miss(struct address_space *mapping, return 0; } - return index + max_scan; + /* Return end of the range + 1 when no hole is found */ + return xas.xa_index + 1; } EXPORT_SYMBOL(page_cache_next_miss); @@ -1849,12 +1849,13 @@ pgoff_t page_cache_prev_miss(struct address_space *mapping, while (max_scan--) { void *entry = xas_prev(&xas); if (!entry || xa_is_value(entry)) - break; + return xas.xa_index; if (xas.xa_index == ULONG_MAX) - break; + return ULONG_MAX; } - return xas.xa_index; + /* Return start of the range - 1 when no hole is found */ + return xas.xa_index - 1; } EXPORT_SYMBOL(page_cache_prev_miss); From 9c860d1d5d69f9cb19eb7c36573ee14065a9c85a Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 13 May 2026 12:35:13 +0000 Subject: [PATCH 2575/5214] mm: introduce for_each_free_list() Patch series "mm: misc cleanups from __GFP_UNMAPPED series". In v2 of the __GFP_UNMAPPED series [0], we realised that some of the patches could potentially be merged as independent cleanups. These are all independent of one another, if you think some are useful cleanups and others are pointless churn, it should be fine to just pick whatever subset you prefer. No functional change intended. This patch (of 4): There are a couple of places that iterate over the freelists with awareness of the data structures' layout. It seems ideally, code outside of mm should not be aware of the page allocator's freelists at all. But, this patch just doesn't hide them completely, it's just a meek incremental step in that direction: provide a macro to iterate over it without needing to be aware of the actual struct fields. Link: https://lore.kernel.org/20260513-page_alloc-unmapped-prep-v1-0-dacdf5402be8@google.com Link: https://lore.kernel.org/20260513-page_alloc-unmapped-prep-v1-1-dacdf5402be8@google.com Link: https://lore.kernel.org/all/20260320-page_alloc-unmapped-v2-0-28bf1bd54f41@google.com/ [0] Signed-off-by: Brendan Jackman Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Vlastimil Babka (SUSE) Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Len Brown Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: "Rafael J. Wysocki" Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Wei Xu Cc: Yuanchu Xie Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/mmzone.h | 9 ++++++--- kernel/power/snapshot.c | 8 ++++---- mm/mm_init.c | 11 +++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9adb2ad21da5..1331a7b93f33 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -177,9 +177,12 @@ static inline bool migratetype_is_mergeable(int mt) return mt < MIGRATE_PCPTYPES; } -#define for_each_migratetype_order(order, type) \ - for (order = 0; order < NR_PAGE_ORDERS; order++) \ - for (type = 0; type < MIGRATE_TYPES; type++) +#define for_each_free_list(list, zone, order) \ + for (order = 0; order < NR_PAGE_ORDERS; order++) \ + for (unsigned int __type = 0; \ + __type < MIGRATE_TYPES && \ + (list = &(zone)->free_area[order].free_list[__type], 1); \ + __type++) extern int page_group_by_mobility_disabled; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index a564650734dc..d933b5b2c05d 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1244,8 +1244,9 @@ unsigned int snapshot_additional_pages(struct zone *zone) static void mark_free_pages(struct zone *zone) { unsigned long pfn, max_zone_pfn, page_count = WD_PAGE_COUNT; + struct list_head *free_list; unsigned long flags; - unsigned int order, t; + unsigned int order; struct page *page; if (zone_is_empty(zone)) @@ -1269,9 +1270,8 @@ static void mark_free_pages(struct zone *zone) swsusp_unset_page_free(page); } - for_each_migratetype_order(order, t) { - list_for_each_entry(page, - &zone->free_area[order].free_list[t], buddy_list) { + for_each_free_list(free_list, zone, order) { + list_for_each_entry(page, free_list, buddy_list) { unsigned long i; pfn = page_to_pfn(page); diff --git a/mm/mm_init.c b/mm/mm_init.c index bd466a3c10c8..db5568cf36e1 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1429,11 +1429,14 @@ static void __meminit zone_init_internals(struct zone *zone, enum zone_type idx, static void __meminit zone_init_free_lists(struct zone *zone) { - unsigned int order, t; - for_each_migratetype_order(order, t) { - INIT_LIST_HEAD(&zone->free_area[order].free_list[t]); + struct list_head *list; + unsigned int order; + + for_each_free_list(list, zone, order) + INIT_LIST_HEAD(list); + + for (order = 0; order < NR_PAGE_ORDERS; order++) zone->free_area[order].nr_free = 0; - } #ifdef CONFIG_UNACCEPTED_MEMORY INIT_LIST_HEAD(&zone->unaccepted_pages); From 23378be820a3f094607f0dca16032ba6c48a8577 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 13 May 2026 12:35:14 +0000 Subject: [PATCH 2576/5214] mm/page_alloc: don't overload migratetype in find_suitable_fallback() This function currently returns a signed integer that encodes status in-band, as negative numbers, along with a migratetype. Switch to a more explicit/verbose style that encodes the status and migratetype separately. In the spirit of making things more explicit, also create an enum to avoid using magic integer literals with special meanings. This enables documenting the values at their definition instead of in one of the callers. Link: https://lore.kernel.org/20260513-page_alloc-unmapped-prep-v1-2-dacdf5402be8@google.com Signed-off-by: Brendan Jackman Reviewed-by: Vlastimil Babka (SUSE) Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Len Brown Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: "Rafael J. Wysocki" Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Wei Xu Cc: Yuanchu Xie Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/compaction.c | 3 ++- mm/internal.h | 14 +++++++++++--- mm/page_alloc.c | 40 +++++++++++++++++++++++----------------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index 3648ce22c807..168e63940b78 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -2340,7 +2340,8 @@ static enum compact_result __compact_finished(struct compact_control *cc) * Job done if allocation would steal freepages from * other migratetype buddy lists. */ - if (find_suitable_fallback(area, order, migratetype, true) >= 0) + if (find_suitable_fallback(area, order, migratetype, true, NULL) + == FALLBACK_FOUND) /* * Movable pages are OK in any pageblock. If we are * stealing for a non-movable allocation, make sure diff --git a/mm/internal.h b/mm/internal.h index 5a2ddcf68e0b..09931b1e535f 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1104,9 +1104,17 @@ static inline void init_cma_pageblock(struct page *page) } #endif - -int find_suitable_fallback(struct free_area *area, unsigned int order, - int migratetype, bool claimable); +enum fallback_result { + /* Found suitable migratetype, *mt_out is valid. */ + FALLBACK_FOUND, + /* No fallback found in requested order. */ + FALLBACK_EMPTY, + /* Passed @claimable, but claiming whole block is a bad idea. */ + FALLBACK_NOCLAIM, +}; +enum fallback_result +find_suitable_fallback(struct free_area *area, unsigned int order, + int migratetype, bool claimable, int *mt_out); static inline bool free_area_empty(struct free_area *area, int migratetype) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 69a99af77777..3e4c4af06f37 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2259,25 +2259,29 @@ static bool should_try_claim_block(unsigned int order, int start_mt) * we would do this whole-block claiming. This would help to reduce * fragmentation due to mixed migratetype pages in one pageblock. */ -int find_suitable_fallback(struct free_area *area, unsigned int order, - int migratetype, bool claimable) +enum fallback_result +find_suitable_fallback(struct free_area *area, unsigned int order, + int migratetype, bool claimable, int *mt_out) { int i; if (claimable && !should_try_claim_block(order, migratetype)) - return -2; + return FALLBACK_NOCLAIM; if (area->nr_free == 0) - return -1; + return FALLBACK_EMPTY; for (i = 0; i < MIGRATE_PCPTYPES - 1 ; i++) { int fallback_mt = fallbacks[migratetype][i]; - if (!free_area_empty(area, fallback_mt)) - return fallback_mt; + if (!free_area_empty(area, fallback_mt)) { + if (mt_out) + *mt_out = fallback_mt; + return FALLBACK_FOUND; + } } - return -1; + return FALLBACK_EMPTY; } /* @@ -2387,16 +2391,16 @@ __rmqueue_claim(struct zone *zone, int order, int start_migratetype, */ for (current_order = MAX_PAGE_ORDER; current_order >= min_order; --current_order) { - area = &(zone->free_area[current_order]); - fallback_mt = find_suitable_fallback(area, current_order, - start_migratetype, true); + enum fallback_result result; - /* No block in that order */ - if (fallback_mt == -1) + area = &(zone->free_area[current_order]); + result = find_suitable_fallback(area, current_order, + start_migratetype, true, &fallback_mt); + + if (result == FALLBACK_EMPTY) continue; - /* Advanced into orders too low to claim, abort */ - if (fallback_mt == -2) + if (result == FALLBACK_NOCLAIM) break; page = get_page_from_free_area(area, fallback_mt); @@ -2426,10 +2430,12 @@ __rmqueue_steal(struct zone *zone, int order, int start_migratetype) int fallback_mt; for (current_order = order; current_order < NR_PAGE_ORDERS; current_order++) { + enum fallback_result result; + area = &(zone->free_area[current_order]); - fallback_mt = find_suitable_fallback(area, current_order, - start_migratetype, false); - if (fallback_mt == -1) + result = find_suitable_fallback(area, current_order, start_migratetype, + false, &fallback_mt); + if (result == FALLBACK_EMPTY) continue; page = get_page_from_free_area(area, fallback_mt); From 3687c0fd67249cb971990b382a47f02f19ed9f67 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 13 May 2026 12:35:15 +0000 Subject: [PATCH 2577/5214] mm: rejig pageblock mask definitions - Add a PAGEBLOCK_ prefix to the names to avoid polluting the "global namespace" too much. - This new prefix makes MIGRATETYPE_AND_ISO_MASK look pretty long. Well, that global mask only exists for quite a specific purpose, and is quite a weird thing to have a name for anyway. So drop it and take advantage of the newly-defined PAGEBLOCK_ISO_MASK. Link: https://lore.kernel.org/20260513-page_alloc-unmapped-prep-v1-3-dacdf5402be8@google.com Signed-off-by: Brendan Jackman Reviewed-by: Vlastimil Babka (SUSE) Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Len Brown Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: "Rafael J. Wysocki" Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Wei Xu Cc: Yuanchu Xie Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/pageblock-flags.h | 6 +++--- mm/page_alloc.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index e046278a01fa..9a6c3ea17684 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h @@ -36,12 +36,12 @@ enum pageblock_bits { #define NR_PAGEBLOCK_BITS (roundup_pow_of_two(__NR_PAGEBLOCK_BITS)) -#define MIGRATETYPE_MASK (BIT(PB_migrate_0)|BIT(PB_migrate_1)|BIT(PB_migrate_2)) +#define PAGEBLOCK_MIGRATETYPE_MASK (BIT(PB_migrate_0)|BIT(PB_migrate_1)|BIT(PB_migrate_2)) #ifdef CONFIG_MEMORY_ISOLATION -#define MIGRATETYPE_AND_ISO_MASK (MIGRATETYPE_MASK | BIT(PB_migrate_isolate)) +#define PAGEBLOCK_ISO_MASK BIT(PB_migrate_isolate) #else -#define MIGRATETYPE_AND_ISO_MASK MIGRATETYPE_MASK +#define PAGEBLOCK_ISO_MASK 0 #endif #if defined(CONFIG_HUGETLB_PAGE) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3e4c4af06f37..0278d642445a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -364,7 +364,7 @@ get_pfnblock_bitmap_bitidx(const struct page *page, unsigned long pfn, #else BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4); #endif - BUILD_BUG_ON(__MIGRATE_TYPE_END > MIGRATETYPE_MASK); + BUILD_BUG_ON(__MIGRATE_TYPE_END > PAGEBLOCK_MIGRATETYPE_MASK); VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page); bitmap = get_pageblock_bitmap(page, pfn); @@ -437,7 +437,7 @@ bool get_pfnblock_bit(const struct page *page, unsigned long pfn, __always_inline enum migratetype get_pfnblock_migratetype(const struct page *page, unsigned long pfn) { - unsigned long mask = MIGRATETYPE_AND_ISO_MASK; + unsigned long mask = PAGEBLOCK_MIGRATETYPE_MASK | PAGEBLOCK_ISO_MASK; unsigned long flags; flags = __get_pfnblock_flags_mask(page, pfn, mask); @@ -446,7 +446,7 @@ get_pfnblock_migratetype(const struct page *page, unsigned long pfn) if (flags & BIT(PB_migrate_isolate)) return MIGRATE_ISOLATE; #endif - return flags & MIGRATETYPE_MASK; + return flags & PAGEBLOCK_MIGRATETYPE_MASK; } /** @@ -534,11 +534,11 @@ static void set_pageblock_migratetype(struct page *page, } VM_WARN_ONCE(get_pageblock_isolate(page), "Use clear_pageblock_isolate() to unisolate pageblock"); - /* MIGRATETYPE_AND_ISO_MASK clears PB_migrate_isolate if it is set */ + /* PAGEBLOCK_ISO_MASK clears PB_migrate_isolate if it is set */ #endif __set_pfnblock_flags_mask(page, page_to_pfn(page), (unsigned long)migratetype, - MIGRATETYPE_AND_ISO_MASK); + PAGEBLOCK_MIGRATETYPE_MASK | PAGEBLOCK_ISO_MASK); } void __meminit init_pageblock_migratetype(struct page *page, @@ -564,7 +564,7 @@ void __meminit init_pageblock_migratetype(struct page *page, flags |= BIT(PB_migrate_isolate); #endif __set_pfnblock_flags_mask(page, page_to_pfn(page), flags, - MIGRATETYPE_AND_ISO_MASK); + PAGEBLOCK_MIGRATETYPE_MASK | PAGEBLOCK_ISO_MASK); } #ifdef CONFIG_DEBUG_VM @@ -2140,15 +2140,15 @@ static bool __move_freepages_block_isolate(struct zone *zone, } move: - /* Use MIGRATETYPE_MASK to get non-isolate migratetype */ + /* Use PAGEBLOCK_MIGRATETYPE_MASK to get non-isolate migratetype */ if (isolate) { from_mt = __get_pfnblock_flags_mask(page, page_to_pfn(page), - MIGRATETYPE_MASK); + PAGEBLOCK_MIGRATETYPE_MASK); to_mt = MIGRATE_ISOLATE; } else { from_mt = MIGRATE_ISOLATE; to_mt = __get_pfnblock_flags_mask(page, page_to_pfn(page), - MIGRATETYPE_MASK); + PAGEBLOCK_MIGRATETYPE_MASK); } __move_freepages_block(zone, start_pfn, from_mt, to_mt); From 248b144a8a6dc534d8bc1c1470efe571de5b7ae6 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 13 May 2026 12:35:16 +0000 Subject: [PATCH 2578/5214] mm/page_alloc: remove ifdefs from pindex helpers The ifdefs are not technically needed here, everything used here is always defined. Switching to IS_ENABLED() makes the code a bit less tiresome to read. Link: https://lore.kernel.org/20260513-page_alloc-unmapped-prep-v1-4-dacdf5402be8@google.com Signed-off-by: Brendan Jackman Reviewed-by: Vlastimil Babka (SUSE) Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Len Brown Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: "Rafael J. Wysocki" Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Wei Xu Cc: Yuanchu Xie Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0278d642445a..dc09a2520313 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -650,19 +650,17 @@ static void bad_page(struct page *page, const char *reason) static inline unsigned int order_to_pindex(int migratetype, int order) { + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { + bool movable = migratetype == MIGRATE_MOVABLE; -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - bool movable; - if (order > PAGE_ALLOC_COSTLY_ORDER) { - VM_BUG_ON(!is_pmd_order(order)); + if (order > PAGE_ALLOC_COSTLY_ORDER) { + VM_BUG_ON(!is_pmd_order(order)); - movable = migratetype == MIGRATE_MOVABLE; - - return NR_LOWORDER_PCP_LISTS + movable; + return NR_LOWORDER_PCP_LISTS + movable; + } + } else { + VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); } -#else - VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); -#endif return (MIGRATE_PCPTYPES * order) + migratetype; } @@ -671,12 +669,12 @@ static inline int pindex_to_order(unsigned int pindex) { int order = pindex / MIGRATE_PCPTYPES; -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - if (pindex >= NR_LOWORDER_PCP_LISTS) - order = HPAGE_PMD_ORDER; -#else - VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); -#endif + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { + if (pindex >= NR_LOWORDER_PCP_LISTS) + order = HPAGE_PMD_ORDER; + } else { + VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); + } return order; } From d231522bf07287c5bcf7c6af6960f476663324b5 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Sun, 17 May 2026 23:37:05 +0000 Subject: [PATCH 2579/5214] mm/page_alloc: drop a misleading __always_inline get_pfnblock_migratetype() is called from outside page_alloc.c, so it cannot always be inlined. Remove the annotation to avoid misleading readers. At least in my minimal config, with GCC, this doesn't change mm/page_alloc.o at all. Link: https://lore.kernel.org/all/20260517-b4-drop-always-inline-v1-1-97b90930e8b8@google.com/ Signed-off-by: Brendan Jackman Suggested-by: Vlastimil Babka Link: https://lore.kernel.org/all/016c8bef-57ef-44ef-bf60-86dbfd368dcd@kernel.org/ Acked-by: Johannes Weiner Reviewed-by: SeongJae Park Reviewed-by: Vishal Moola Reviewed-by: Vlastimil Babka (SUSE) Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index dc09a2520313..d7b7f9504bd8 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -434,7 +434,7 @@ bool get_pfnblock_bit(const struct page *page, unsigned long pfn, * Use get_pfnblock_migratetype() if caller already has both @page and @pfn * to save a call to page_to_pfn(). */ -__always_inline enum migratetype +enum migratetype get_pfnblock_migratetype(const struct page *page, unsigned long pfn) { unsigned long mask = PAGEBLOCK_MIGRATETYPE_MASK | PAGEBLOCK_ISO_MASK; From 47166f2199557e57cbab2882b033fb2949818fbb Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Tue, 19 May 2026 14:17:58 +0000 Subject: [PATCH 2580/5214] mm/page_alloc: document that alloc_pages_nolock() uses RCU The allocator interacts with cgroups which rely on RCU. RCU does not work everywhere, so the "any context" claim is slightly overstated here. This should already be enforced by objtool, since this function is not marked noinstr the x86 build should fail if you call it from a place where RCU is not watching. But, expecting readers to make that connection for themselves seems a bit cruel (I don't think there is even any documentation of what noinstr means at all, let alone the connection with RCU). Note this is not claiming that any cgroup code called from the allocator would actually break if this restriction was violated, it could very well be that there's no real way for the allocator to act on a cgroup that can disappear concurrently. But, since it's likely nobody has verified this one way or another, better to just be safe and declare that RCU is required. Allocating from an RCU-unsafe context seems a bit crazy anyway. Link: https://lore.kernel.org/20260519-nolock-rcu-comment-v1-1-4a630c8794e5@google.com Signed-off-by: Brendan Jackman Suggested-by: Junaid Shahid Acked-by: Harry Yoo (Oracle) Acked-by: Vlastimil Babka (SUSE) Cc: Alexei Starovoitov Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d7b7f9504bd8..0ebffb0bb98b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7937,8 +7937,8 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned * @order: allocation order size * * Allocates pages of a given order from the given node. This is safe to - * call from any context (from atomic, NMI, and also reentrant - * allocator -> tracepoint -> alloc_pages_nolock_noprof). + * call from any context where RCU is watching (from atomic, NMI, and also + * reentrant allocator -> tracepoint -> alloc_pages_nolock_noprof). * Allocation is best effort and to be expected to fail easily so nobody should * rely on the success. Failures are not reported via warn_alloc(). * See always fail conditions below. From 63b02a9409cb5180398491b093e48bcb5315f5fb Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Mon, 4 May 2026 12:55:17 +0000 Subject: [PATCH 2581/5214] mm: swap_cgroup: fix NULL deref in lookup_swap_cgroup_id on swapless host lookup_swap_cgroup_id() passes swap_cgroup_ctrl[type].map to __swap_cgroup_id_lookup() without checking that the type was ever registered via swap_cgroup_swapon(). On a swapless host every ctrl->map is NULL, so __swap_cgroup_id_lookup() dereferences NULL + a scaled swp_offset(). Since commit bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()"), zap_pte_range() -> swap_pte_batch() calls lookup_swap_cgroup_id() on any non-present, non-none PTE that decodes as a real swap entry, without first validating it against swap_info[]. A single PTE corrupted into a type-0 swap entry takes the host down at process exit. We hit this in production on a swapless 6.12.58 host: ~1s of "get_swap_device: Bad swap file entry 3f800204222bb" (do_swap_page() being correctly defensive about the same entry) followed by BUG: unable to handle page fault for address: 000003f800204220 RIP: 0010:lookup_swap_cgroup_id+0x2b/0x60 Call Trace: swap_pte_batch+0xbf/0x230 zap_pte_range+0x4c8/0x780 unmap_page_range+0x190/0x3e0 exit_mmap+0xd9/0x3c0 do_exit+0x20c/0x4b0 syzbot has reported the identical stack. The source of the PTE corruption is a separate bug; this change makes the teardown path as robust as the fault path already is. Every other caller of lookup_swap_cgroup_id() is downstream of a get_swap_device() that has already validated the entry, so the new branch is cold. Link: https://lore.kernel.org/20260504-swap-cgroup-fix-7-0-v1-1-f53ff41ee553@linux.dev Fixes: bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()") Signed-off-by: Jose Fernandez (Anthropic) Reported-by: syzbot+e12bd9ca48157add237a@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/69859728.050a0220.3b3015.0033.GAE@google.com Assisted-by: Claude:unspecified Cc: Barry Song Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kairui Song Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton --- mm/swap_cgroup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/swap_cgroup.c b/mm/swap_cgroup.c index de779fed8c21..95c38e54dd58 100644 --- a/mm/swap_cgroup.c +++ b/mm/swap_cgroup.c @@ -124,6 +124,8 @@ unsigned short lookup_swap_cgroup_id(swp_entry_t ent) return 0; ctrl = &swap_cgroup_ctrl[swp_type(ent)]; + if (unlikely(!ctrl->map)) + return 0; return __swap_cgroup_id_lookup(ctrl->map, swp_offset(ent)); } From a2e61ffb47493ff009b24105792318b3b62e18e2 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:40 +0800 Subject: [PATCH 2582/5214] mm, swap: simplify swap cache allocation helper Patch series "mm, swap: swap table phase IV: unify allocation", v5. This series unifies the allocation and charging of anon and shmem swap in folios, provides better synchronization, consolidates the metadata management, hence dropping the static array and map, and improves the performance. The static metadata overhead is now close to zero, and workload performance is slightly improved. For example, mounting a 1TB swap device saves about 512MB of memory: Before: free -m total used free shared buff/cache available Mem: 1464 805 346 1 382 658 Swap: 1048575 0 1048575 After: free -m total used free shared buff/cache available Mem: 1464 277 899 1 356 1187 Swap: 1048575 0 1048575 Memory usage is ~512M lower, and we now have a close to 0 static overhead. It was about 2 bytes per slot before, now roughly 0.09375 bytes per slot (48 bytes ci info per cluster, which is 512 slots). Performance test is also looking good, testing Redis in a 2G VM using 6G ZRAM as swap: valkey-server --maxmemory 2560M redis-benchmark -r 3000000 -n 3000000 -d 1024 -c 12 -P 32 -t get Before: 3385017.283654 RPS After: 3433309.307292 RPS (1.42% better) Testing with build kernel under global pressure on a 48c96t system, limiting the total memory to 8G, using 12G ZRAM, 24 test runs, enabling THP: make -j96, using defconfig Before: user time 2904.59s system time 4773.99s After: user time 2909.38s system time 4641.55s (2.77% better) Testing with usemem on a 32c machine using 48G brd ramdisk and 16G RAM, 12 test run: usemem --init-time -O -y -x -n 48 1G Before: Throughput (Sum): 6482.58 MB/s Free Latency: 371371.67us After: Throughput (Sum): 6539.28 MB/s Free Latency: 363059.88us Seems similar, or slightly better. This series also reduces memory thrashing, I no longer see any: "Huh VM_FAULT_OOM leaked out to the #PF handler. Retrying PF", it was shown several times during stress testing before this series when under great pressure: Before: grep -Ri VM_FAULT_OOM | wc -l => 18 After: grep -Ri VM_FAULT_OOM | wc -l => 0 This patch (of 12): Instead of trying to return the existing folio if the entry is already cached in swap_cache_alloc_folio, simply return an error pointer if the allocation failed, and drop the output argument that indicates what kind of folio is actually returned. And a proper wrapper swap_cache_read_folio that decouples and handles the actual requirement - read in the folio, or return the already read folio in cache. This is what async swapin and readahead actually required. As for zswap swap out, the caller just needs to abort if the allocation fails because the entry is gone or already cached, so removing simplifies the return argument, making it cleaner. No feature change. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-0-88ae43e064c7@tencent.com Link: https://lore.kernel.org/20260517-swap-table-p4-v5-1-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Zi Yan Cc: Youngjun Park Signed-off-by: Andrew Morton --- mm/swap.h | 3 +- mm/swap_state.c | 180 +++++++++++++++++++++++++----------------------- mm/zswap.c | 23 +++---- 3 files changed, 103 insertions(+), 103 deletions(-) diff --git a/mm/swap.h b/mm/swap.h index a77016f2423b..ad8b17a93758 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -281,8 +281,7 @@ struct folio *swap_cache_get_folio(swp_entry_t entry); void *swap_cache_get_shadow(swp_entry_t entry); void swap_cache_del_folio(struct folio *folio); struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_flags, - struct mempolicy *mpol, pgoff_t ilx, - bool *alloced); + struct mempolicy *mpol, pgoff_t ilx); /* Below helpers require the caller to lock and pass in the swap cluster. */ void __swap_cache_add_folio(struct swap_cluster_info *ci, struct folio *folio, swp_entry_t entry); diff --git a/mm/swap_state.c b/mm/swap_state.c index 1415a5c54a43..3bba82f6dc79 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -459,54 +459,38 @@ void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma, * All swap slots covered by the folio must have a non-zero swap count. * * Context: Caller must protect the swap device with reference count or locks. - * Return: Returns the folio being added on success. Returns the existing folio - * if @entry is already cached. Returns NULL if raced with swapin or swapoff. + * Return: 0 if success, error code if failed. */ -static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry, - struct folio *folio, - gfp_t gfp, bool charged) +static int __swap_cache_prepare_and_add(swp_entry_t entry, + struct folio *folio, + gfp_t gfp, bool charged) { - struct folio *swapcache = NULL; void *shadow; int ret; __folio_set_locked(folio); __folio_set_swapbacked(folio); - if (!charged && mem_cgroup_swapin_charge_folio(folio, NULL, gfp, entry)) + if (!charged && mem_cgroup_swapin_charge_folio(folio, NULL, gfp, entry)) { + ret = -ENOMEM; goto failed; - - for (;;) { - ret = swap_cache_add_folio(folio, entry, &shadow); - if (!ret) - break; - - /* - * Large order allocation needs special handling on - * race: if a smaller folio exists in cache, swapin needs - * to fallback to order 0, and doing a swap cache lookup - * might return a folio that is irrelevant to the faulting - * entry because @entry is aligned down. Just return NULL. - */ - if (ret != -EEXIST || folio_test_large(folio)) - goto failed; - - swapcache = swap_cache_get_folio(entry); - if (swapcache) - goto failed; } + ret = swap_cache_add_folio(folio, entry, &shadow); + if (ret) + goto failed; + memcg1_swapin(entry, folio_nr_pages(folio)); if (shadow) workingset_refault(folio, shadow); /* Caller will initiate read into locked folio */ folio_add_lru(folio); - return folio; + return 0; failed: folio_unlock(folio); - return swapcache; + return ret; } /** @@ -515,7 +499,6 @@ static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry, * @gfp_mask: memory allocation flags * @mpol: NUMA memory allocation policy to be applied * @ilx: NUMA interleave index, for use only when MPOL_INTERLEAVE - * @new_page_allocated: sets true if allocation happened, false otherwise * * Allocate a folio in the swap cache for one swap slot, typically before * doing IO (e.g. swap in or zswap writeback). The swap slot indicated by @@ -523,18 +506,40 @@ static struct folio *__swap_cache_prepare_and_add(swp_entry_t entry, * Currently only supports order 0. * * Context: Caller must protect the swap device with reference count or locks. - * Return: Returns the existing folio if @entry is cached already. Returns - * NULL if failed due to -ENOMEM or @entry have a swap count < 1. + * Return: Returns the folio if allocation succeeded and folio is added to + * swap cache. Returns error code if allocation failed due to race or OOM. */ struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_mask, - struct mempolicy *mpol, pgoff_t ilx, - bool *new_page_allocated) + struct mempolicy *mpol, pgoff_t ilx) +{ + int err; + struct folio *folio; + + /* Allocate a new folio to be added into the swap cache. */ + folio = folio_alloc_mpol(gfp_mask, 0, mpol, ilx, numa_node_id()); + if (!folio) + return ERR_PTR(-ENOMEM); + + /* + * Try to add the new folio to the swap cache. It returns + * -EEXIST if the entry is already cached. + */ + err = __swap_cache_prepare_and_add(entry, folio, gfp_mask, false); + if (err) { + folio_put(folio); + return ERR_PTR(err); + } + + return folio; +} + +static struct folio *swap_cache_read_folio(swp_entry_t entry, gfp_t gfp, + struct mempolicy *mpol, pgoff_t ilx, + struct swap_iocb **plug, bool readahead) { struct swap_info_struct *si = __swap_entry_to_info(entry); struct folio *folio; - struct folio *result = NULL; - *new_page_allocated = false; /* Check the swap cache again for readahead path. */ folio = swap_cache_get_folio(entry); if (folio) @@ -544,17 +549,24 @@ struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_mask, if (!swap_entry_swapped(si, entry)) return NULL; - /* Allocate a new folio to be added into the swap cache. */ - folio = folio_alloc_mpol(gfp_mask, 0, mpol, ilx, numa_node_id()); - if (!folio) + do { + folio = swap_cache_get_folio(entry); + if (folio) + return folio; + + folio = swap_cache_alloc_folio(entry, gfp, mpol, ilx); + } while (PTR_ERR(folio) == -EEXIST); + + if (IS_ERR_OR_NULL(folio)) return NULL; - /* Try add the new folio, returns existing folio or NULL on failure. */ - result = __swap_cache_prepare_and_add(entry, folio, gfp_mask, false); - if (result == folio) - *new_page_allocated = true; - else - folio_put(folio); - return result; + + swap_read_folio(folio, plug); + if (readahead) { + folio_set_readahead(folio); + count_vm_event(SWAP_RA); + } + + return folio; } /** @@ -573,15 +585,35 @@ struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_mask, */ struct folio *swapin_folio(swp_entry_t entry, struct folio *folio) { + int ret; struct folio *swapcache; pgoff_t offset = swp_offset(entry); unsigned long nr_pages = folio_nr_pages(folio); entry = swp_entry(swp_type(entry), round_down(offset, nr_pages)); - swapcache = __swap_cache_prepare_and_add(entry, folio, 0, true); - if (swapcache == folio) - swap_read_folio(folio, NULL); - return swapcache; + for (;;) { + ret = __swap_cache_prepare_and_add(entry, folio, 0, true); + if (!ret) { + swap_read_folio(folio, NULL); + break; + } + + /* + * Large order allocation needs special handling on + * race: if a smaller folio exists in cache, swapin needs + * to fall back to order 0, and doing a swap cache lookup + * might return a folio that is irrelevant to the faulting + * entry because @entry is aligned down. Just return NULL. + */ + if (ret != -EEXIST || nr_pages > 1) + return NULL; + + swapcache = swap_cache_get_folio(entry); + if (swapcache) + return swapcache; + } + + return folio; } /* @@ -595,7 +627,6 @@ struct folio *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, struct swap_iocb **plug) { struct swap_info_struct *si; - bool page_allocated; struct mempolicy *mpol; pgoff_t ilx; struct folio *folio; @@ -605,13 +636,9 @@ struct folio *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, return NULL; mpol = get_vma_policy(vma, addr, 0, &ilx); - folio = swap_cache_alloc_folio(entry, gfp_mask, mpol, ilx, - &page_allocated); + folio = swap_cache_read_folio(entry, gfp_mask, mpol, ilx, plug, false); mpol_cond_put(mpol); - if (page_allocated) - swap_read_folio(folio, plug); - put_swap_device(si); return folio; } @@ -696,7 +723,7 @@ static unsigned long swapin_nr_pages(unsigned long offset) * are fairly likely to have been swapped out from the same node. */ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask, - struct mempolicy *mpol, pgoff_t ilx) + struct mempolicy *mpol, pgoff_t ilx) { struct folio *folio; unsigned long entry_offset = swp_offset(entry); @@ -706,7 +733,7 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask, struct swap_info_struct *si = __swap_entry_to_info(entry); struct blk_plug plug; struct swap_iocb *splug = NULL; - bool page_allocated; + swp_entry_t ra_entry; mask = swapin_nr_pages(offset) - 1; if (!mask) @@ -723,18 +750,11 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask, blk_start_plug(&plug); for (offset = start_offset; offset <= end_offset ; offset++) { /* Ok, do the async read-ahead now */ - folio = swap_cache_alloc_folio( - swp_entry(swp_type(entry), offset), gfp_mask, mpol, ilx, - &page_allocated); + ra_entry = swp_entry(swp_type(entry), offset); + folio = swap_cache_read_folio(ra_entry, gfp_mask, mpol, ilx, + &splug, offset != entry_offset); if (!folio) continue; - if (page_allocated) { - swap_read_folio(folio, &splug); - if (offset != entry_offset) { - folio_set_readahead(folio); - count_vm_event(SWAP_RA); - } - } folio_put(folio); } blk_finish_plug(&plug); @@ -742,11 +762,7 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask, lru_add_drain(); /* Push any new pages onto the LRU now */ skip: /* The page was likely read above, so no need for plugging here */ - folio = swap_cache_alloc_folio(entry, gfp_mask, mpol, ilx, - &page_allocated); - if (unlikely(page_allocated)) - swap_read_folio(folio, NULL); - return folio; + return swap_cache_read_folio(entry, gfp_mask, mpol, ilx, NULL, false); } static int swap_vma_ra_win(struct vm_fault *vmf, unsigned long *start, @@ -812,8 +828,7 @@ static struct folio *swap_vma_readahead(swp_entry_t targ_entry, gfp_t gfp_mask, pte_t *pte = NULL, pentry; int win; unsigned long start, end, addr; - pgoff_t ilx; - bool page_allocated; + pgoff_t ilx = targ_ilx; win = swap_vma_ra_win(vmf, &start, &end); if (win == 1) @@ -847,19 +862,12 @@ static struct folio *swap_vma_readahead(swp_entry_t targ_entry, gfp_t gfp_mask, if (!si) continue; } - folio = swap_cache_alloc_folio(entry, gfp_mask, mpol, ilx, - &page_allocated); + folio = swap_cache_read_folio(entry, gfp_mask, mpol, ilx, + &splug, addr != vmf->address); if (si) put_swap_device(si); if (!folio) continue; - if (page_allocated) { - swap_read_folio(folio, &splug); - if (addr != vmf->address) { - folio_set_readahead(folio); - count_vm_event(SWAP_RA); - } - } folio_put(folio); } if (pte) @@ -869,10 +877,8 @@ static struct folio *swap_vma_readahead(swp_entry_t targ_entry, gfp_t gfp_mask, lru_add_drain(); skip: /* The folio was likely read above, so no need for plugging here */ - folio = swap_cache_alloc_folio(targ_entry, gfp_mask, mpol, targ_ilx, - &page_allocated); - if (unlikely(page_allocated)) - swap_read_folio(folio, NULL); + folio = swap_cache_read_folio(targ_entry, gfp_mask, mpol, targ_ilx, + NULL, false); return folio; } diff --git a/mm/zswap.c b/mm/zswap.c index 4b5149173b0e..e27f6e96f003 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -991,7 +991,6 @@ static int zswap_writeback_entry(struct zswap_entry *entry, pgoff_t offset = swp_offset(swpentry); struct folio *folio; struct mempolicy *mpol; - bool folio_was_allocated; struct swap_info_struct *si; int ret = 0; @@ -1002,22 +1001,18 @@ static int zswap_writeback_entry(struct zswap_entry *entry, mpol = get_task_policy(current); folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, mpol, - NO_INTERLEAVE_INDEX, &folio_was_allocated); + NO_INTERLEAVE_INDEX); put_swap_device(si); - if (!folio) - return -ENOMEM; /* - * Found an existing folio, we raced with swapin or concurrent - * shrinker. We generally writeback cold folios from zswap, and - * swapin means the folio just became hot, so skip this folio. - * For unlikely concurrent shrinker case, it will be unlinked - * and freed when invalidated by the concurrent shrinker anyway. + * Swap cache allocation might fail due to OOM, or the entry + * may already be cached due to concurrent swapin or have been + * freed. If already cached, a concurrent swapin made the folio + * hot, so skip it. For the unlikely concurrent shrinker case, + * it will be unlinked and freed when invalidated anyway. */ - if (!folio_was_allocated) { - ret = -EEXIST; - goto out; - } + if (IS_ERR(folio)) + return PTR_ERR(folio); /* * folio is locked, and the swapcache is now secured against @@ -1057,7 +1052,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry, __swap_writepage(folio, NULL); out: - if (ret && ret != -EEXIST) { + if (ret) { swap_cache_del_folio(folio); folio_unlock(folio); } From bebee474c1c1a3e9db2e1079639da1cd6e3ab0ba Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:41 +0800 Subject: [PATCH 2583/5214] mm, swap: move common swap cache operations into standalone helpers Move a few swap cache checking, adding, and deletion operations into standalone helpers to be used later. And while at it, add proper kernel doc. No feature or behavior change. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-2-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/swap_state.c | 146 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 46 deletions(-) diff --git a/mm/swap_state.c b/mm/swap_state.c index 3bba82f6dc79..89fa19ec13f6 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -137,8 +137,47 @@ void *swap_cache_get_shadow(swp_entry_t entry) return NULL; } -void __swap_cache_add_folio(struct swap_cluster_info *ci, - struct folio *folio, swp_entry_t entry) +/** + * __swap_cache_add_check - Check if a range is suitable for adding a folio. + * @ci: The locked swap cluster. + * @ci_off: Range start offset. + * @nr: Number of slots to check. + * @shadow: Returns the shadow value if one exists in the range. + * + * Check if all slots covered by given range have a swap count >= 1. + * Retrieves the shadow if there is one. + * + * Context: Caller must lock the cluster. + * Return: 0 if success, error code if failed. + */ +static int __swap_cache_add_check(struct swap_cluster_info *ci, + unsigned int ci_off, unsigned int nr, + void **shadow) +{ + unsigned int ci_end = ci_off + nr; + unsigned long old_tb; + + lockdep_assert_held(&ci->lock); + if (WARN_ON_ONCE(ci_off >= SWAPFILE_CLUSTER)) + return -EINVAL; + + if (unlikely(!ci->table)) + return -ENOENT; + do { + old_tb = __swap_table_get(ci, ci_off); + if (unlikely(swp_tb_is_folio(old_tb))) + return -EEXIST; + if (unlikely(!__swp_tb_get_count(old_tb))) + return -ENOENT; + if (swp_tb_is_shadow(old_tb)) + *shadow = swp_tb_to_shadow(old_tb); + } while (++ci_off < ci_end); + + return 0; +} + +static void __swap_cache_do_add_folio(struct swap_cluster_info *ci, + struct folio *folio, swp_entry_t entry) { unsigned int ci_off = swp_cluster_offset(entry), ci_end; unsigned long nr_pages = folio_nr_pages(folio); @@ -159,7 +198,28 @@ void __swap_cache_add_folio(struct swap_cluster_info *ci, folio_ref_add(folio, nr_pages); folio_set_swapcache(folio); folio->swap = entry; +} +/** + * __swap_cache_add_folio - Add a folio to the swap cache and update stats. + * @ci: The locked swap cluster. + * @folio: The folio to be added. + * @entry: The swap entry corresponding to the folio. + * + * Unconditionally add a folio to the swap cache. The caller must ensure + * all slots are usable and have no conflicts. This assigns entry to + * @folio->swap, increases folio refcount by the number of pages, and + * updates swap cache stats. + * + * Context: Caller must ensure the folio is locked and lock the cluster + * that holds the entries. + */ +void __swap_cache_add_folio(struct swap_cluster_info *ci, + struct folio *folio, swp_entry_t entry) +{ + unsigned long nr_pages = folio_nr_pages(folio); + + __swap_cache_do_add_folio(ci, folio, entry); node_stat_mod_folio(folio, NR_FILE_PAGES, nr_pages); lruvec_stat_mod_folio(folio, NR_SWAPCACHE, nr_pages); } @@ -168,9 +228,11 @@ void __swap_cache_add_folio(struct swap_cluster_info *ci, * swap_cache_add_folio - Add a folio into the swap cache. * @folio: The folio to be added. * @entry: The swap entry corresponding to the folio. - * @gfp: gfp_mask for XArray node allocation. * @shadowp: If a shadow is found, return the shadow. * + * Add a folio into the swap cache. Will return error if any slot is no + * longer a valid swapped out slot or already occupied by another folio. + * * Context: Caller must ensure @entry is valid and protect the swap device * with reference count or locks. */ @@ -179,60 +241,31 @@ static int swap_cache_add_folio(struct folio *folio, swp_entry_t entry, { int err; void *shadow = NULL; - unsigned long old_tb; + unsigned int ci_off; struct swap_info_struct *si; struct swap_cluster_info *ci; - unsigned int ci_start, ci_off, ci_end; unsigned long nr_pages = folio_nr_pages(folio); si = __swap_entry_to_info(entry); - ci_start = swp_cluster_offset(entry); - ci_end = ci_start + nr_pages; - ci_off = ci_start; ci = swap_cluster_lock(si, swp_offset(entry)); - if (unlikely(!ci->table)) { - err = -ENOENT; - goto failed; + ci_off = swp_cluster_offset(entry); + err = __swap_cache_add_check(ci, ci_off, nr_pages, &shadow); + if (err) { + swap_cluster_unlock(ci); + return err; } - do { - old_tb = __swap_table_get(ci, ci_off); - if (unlikely(swp_tb_is_folio(old_tb))) { - err = -EEXIST; - goto failed; - } - if (unlikely(!__swp_tb_get_count(old_tb))) { - err = -ENOENT; - goto failed; - } - if (swp_tb_is_shadow(old_tb)) - shadow = swp_tb_to_shadow(old_tb); - } while (++ci_off < ci_end); + __swap_cache_add_folio(ci, folio, entry); swap_cluster_unlock(ci); if (shadowp) *shadowp = shadow; - return 0; -failed: - swap_cluster_unlock(ci); - return err; + return 0; } -/** - * __swap_cache_del_folio - Removes a folio from the swap cache. - * @ci: The locked swap cluster. - * @folio: The folio. - * @entry: The first swap entry that the folio corresponds to. - * @shadow: shadow value to be filled in the swap cache. - * - * Removes a folio from the swap cache and fills a shadow in place. - * This won't put the folio's refcount. The caller has to do that. - * - * Context: Caller must ensure the folio is locked and in the swap cache - * using the index of @entry, and lock the cluster that holds the entries. - */ -void __swap_cache_del_folio(struct swap_cluster_info *ci, struct folio *folio, - swp_entry_t entry, void *shadow) +static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, + struct folio *folio, + swp_entry_t entry, void *shadow) { int count; unsigned long old_tb; @@ -259,14 +292,12 @@ void __swap_cache_del_folio(struct swap_cluster_info *ci, struct folio *folio, folio_swapped = true; else need_free = true; - /* If shadow is NULL, we sets an empty shadow. */ + /* If shadow is NULL, we set an empty shadow. */ __swap_table_set(ci, ci_off, shadow_to_swp_tb(shadow, count)); } while (++ci_off < ci_end); folio->swap.val = 0; folio_clear_swapcache(folio); - node_stat_mod_folio(folio, NR_FILE_PAGES, -nr_pages); - lruvec_stat_mod_folio(folio, NR_SWAPCACHE, -nr_pages); if (!folio_swapped) { __swap_cluster_free_entries(si, ci, ci_start, nr_pages); @@ -279,6 +310,29 @@ void __swap_cache_del_folio(struct swap_cluster_info *ci, struct folio *folio, } } +/** + * __swap_cache_del_folio - Removes a folio from the swap cache. + * @ci: The locked swap cluster. + * @folio: The folio. + * @entry: The first swap entry that the folio corresponds to. + * @shadow: shadow value to be filled in the swap cache. + * + * Removes a folio from the swap cache and fills a shadow in place. + * This won't put the folio's refcount. The caller has to do that. + * + * Context: Caller must ensure the folio is locked and in the swap cache + * using the index of @entry, and lock the cluster that holds the entries. + */ +void __swap_cache_del_folio(struct swap_cluster_info *ci, struct folio *folio, + swp_entry_t entry, void *shadow) +{ + unsigned long nr_pages = folio_nr_pages(folio); + + __swap_cache_do_del_folio(ci, folio, entry, shadow); + node_stat_mod_folio(folio, NR_FILE_PAGES, -nr_pages); + lruvec_stat_mod_folio(folio, NR_SWAPCACHE, -nr_pages); +} + /** * swap_cache_del_folio - Removes a folio from the swap cache. * @folio: The folio. From 1dfbe92e702675964da45847ffe022a41bf4045e Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:42 +0800 Subject: [PATCH 2584/5214] mm/huge_memory: move THP gfp limit helper into header Shmem has some special requirements for THP GFP and has to limit it in certain zones or provide a more lenient fallback. We'll use this helper for generic swap THP allocation, which needs to support shmem. For a typical GFP_HIGHUSER_MOVABLE swap-in, this helper is basically a no-op. But it's necessary for certain shmem users, mostly drivers. No feature change. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-3-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Reviewed-by: Zi Yan Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Signed-off-by: Andrew Morton --- include/linux/huge_mm.h | 30 ++++++++++++++++++++++++++++++ mm/shmem.c | 30 +++--------------------------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 2949e5acff35..58382e97a66d 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -237,6 +237,31 @@ static inline bool thp_vma_suitable_order(struct vm_area_struct *vma, return true; } +/* + * Make sure huge_gfp is always more limited than limit_gfp. + * Some shmem users want THP allocation to be done less aggressively + * and only in certain zone. + */ +static inline gfp_t thp_shmem_limit_gfp_mask(gfp_t huge_gfp, gfp_t limit_gfp) +{ + gfp_t allowflags = __GFP_IO | __GFP_FS | __GFP_RECLAIM; + gfp_t denyflags = __GFP_NOWARN | __GFP_NORETRY; + gfp_t zoneflags = limit_gfp & GFP_ZONEMASK; + gfp_t result = huge_gfp & ~(allowflags | GFP_ZONEMASK); + + /* Allow allocations only from the originally specified zones. */ + result |= zoneflags; + + /* + * Minimize the result gfp by taking the union with the deny flags, + * and the intersection of the allow flags. + */ + result |= (limit_gfp & denyflags); + result |= (huge_gfp & limit_gfp) & allowflags; + + return result; +} + /* * Filter the bitfield of input orders to the ones suitable for use in the vma. * See thp_vma_suitable_order(). @@ -581,6 +606,11 @@ static inline bool thp_vma_suitable_order(struct vm_area_struct *vma, return false; } +static inline gfp_t thp_shmem_limit_gfp_mask(gfp_t huge_gfp, gfp_t limit_gfp) +{ + return huge_gfp; +} + static inline unsigned long thp_vma_suitable_orders(struct vm_area_struct *vma, unsigned long addr, unsigned long orders) { diff --git a/mm/shmem.c b/mm/shmem.c index bab3529af23c..6edb23b41bac 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1791,30 +1791,6 @@ static struct folio *shmem_swapin_cluster(swp_entry_t swap, gfp_t gfp, return folio; } -/* - * Make sure huge_gfp is always more limited than limit_gfp. - * Some of the flags set permissions, while others set limitations. - */ -static gfp_t limit_gfp_mask(gfp_t huge_gfp, gfp_t limit_gfp) -{ - gfp_t allowflags = __GFP_IO | __GFP_FS | __GFP_RECLAIM; - gfp_t denyflags = __GFP_NOWARN | __GFP_NORETRY; - gfp_t zoneflags = limit_gfp & GFP_ZONEMASK; - gfp_t result = huge_gfp & ~(allowflags | GFP_ZONEMASK); - - /* Allow allocations only from the originally specified zones. */ - result |= zoneflags; - - /* - * Minimize the result gfp by taking the union with the deny flags, - * and the intersection of the allow flags. - */ - result |= (limit_gfp & denyflags); - result |= (huge_gfp & limit_gfp) & allowflags; - - return result; -} - #ifdef CONFIG_TRANSPARENT_HUGEPAGE bool shmem_hpage_pmd_enabled(void) { @@ -2065,7 +2041,7 @@ static struct folio *shmem_swap_alloc_folio(struct inode *inode, non_swapcache_batch(entry, nr_pages) != nr_pages) goto fallback; - alloc_gfp = limit_gfp_mask(vma_thp_gfp_mask(vma), gfp); + alloc_gfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp); } retry: new = shmem_alloc_folio(alloc_gfp, order, info, index); @@ -2141,7 +2117,7 @@ static int shmem_replace_folio(struct folio **foliop, gfp_t gfp, if (nr_pages > 1) { gfp_t huge_gfp = vma_thp_gfp_mask(vma); - gfp = limit_gfp_mask(huge_gfp, gfp); + gfp = thp_shmem_limit_gfp_mask(huge_gfp, gfp); } #endif @@ -2548,7 +2524,7 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, gfp_t huge_gfp; huge_gfp = vma_thp_gfp_mask(vma); - huge_gfp = limit_gfp_mask(huge_gfp, gfp); + huge_gfp = thp_shmem_limit_gfp_mask(huge_gfp, gfp); folio = shmem_alloc_and_add_folio(vmf, huge_gfp, inode, index, fault_mm, orders); if (!IS_ERR(folio)) { From e1e6750df3b47380a5c1ba9f517e634a8328283f Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:43 +0800 Subject: [PATCH 2585/5214] mm, swap: add support for stable large allocation in swap cache directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make it possible to allocate large folios directly in swap cache, provide a new infrastructure helper to handle the swap cache status check, allocation, and order fallback in the swap cache layer The new helper replaces the existing swap_cache_alloc_folio. Based on this, all the separate swap folio allocation that is being done by anon / shmem before is converted to use this helper directly, unifying folio allocation for anon, shmem, and readahead. This slightly consolidates how allocation is synchronized, making it more stable and less prone to errors. The slot-count and cache-conflict check is now always performed with the cluster lock held before allocation, and repeated under the same lock right before cache insertion. This double check produces a stable result compared to the previous anon and shmem mTHP allocation implementation, avoids the false-negative conflict checks that the lockless path can return — large allocations no longer have to be unwound because the range turned out to be occupied — and aborts early for already-freed slots, which helps ordinary swapin and especially readahead, with only a marginal increase in cluster-lock contention (the lock is very lightly contended and stays local in the first place). Hence, callers of swap_cache_alloc_folio() no longer need to check the swap slot count or swap cache status themselves. And now whoever first successfully allocates a folio in the swap cache will be the one who charges it and performs the swap-in. The race window of swapping is also reduced since the loop is much more compact. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-4-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: Chris Li Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/swap.h | 3 +- mm/swap_state.c | 236 ++++++++++++++++++++++++++++++++++-------------- mm/zswap.c | 2 +- 3 files changed, 170 insertions(+), 71 deletions(-) diff --git a/mm/swap.h b/mm/swap.h index ad8b17a93758..6774af10a943 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -280,7 +280,8 @@ bool swap_cache_has_folio(swp_entry_t entry); struct folio *swap_cache_get_folio(swp_entry_t entry); void *swap_cache_get_shadow(swp_entry_t entry); void swap_cache_del_folio(struct folio *folio); -struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_flags, +struct folio *swap_cache_alloc_folio(swp_entry_t target_entry, gfp_t gfp_mask, + unsigned long orders, struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx); /* Below helpers require the caller to lock and pass in the swap cluster. */ void __swap_cache_add_folio(struct swap_cluster_info *ci, diff --git a/mm/swap_state.c b/mm/swap_state.c index 89fa19ec13f6..0adb0565bbb1 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -139,10 +139,10 @@ void *swap_cache_get_shadow(swp_entry_t entry) /** * __swap_cache_add_check - Check if a range is suitable for adding a folio. - * @ci: The locked swap cluster. - * @ci_off: Range start offset. - * @nr: Number of slots to check. - * @shadow: Returns the shadow value if one exists in the range. + * @ci: The locked swap cluster + * @targ_entry: The target swap entry to check, will be rounded down by @nr + * @nr: Number of slots to check, must be a power of 2 + * @shadowp: Returns the shadow value if one exists in the range. * * Check if all slots covered by given range have a swap count >= 1. * Retrieves the shadow if there is one. @@ -151,26 +151,40 @@ void *swap_cache_get_shadow(swp_entry_t entry) * Return: 0 if success, error code if failed. */ static int __swap_cache_add_check(struct swap_cluster_info *ci, - unsigned int ci_off, unsigned int nr, - void **shadow) + swp_entry_t targ_entry, + unsigned long nr, void **shadowp) { - unsigned int ci_end = ci_off + nr; + unsigned int ci_off, ci_end; unsigned long old_tb; lockdep_assert_held(&ci->lock); - if (WARN_ON_ONCE(ci_off >= SWAPFILE_CLUSTER)) - return -EINVAL; + /* + * If the target slot is not swapped out or already cached, return + * -ENOENT or -EEXIST. If the batch is not suitable, could be a + * race with concurrent free or cache add, return -EBUSY. + */ if (unlikely(!ci->table)) return -ENOENT; + ci_off = swp_cluster_offset(targ_entry); + old_tb = __swap_table_get(ci, ci_off); + if (swp_tb_is_folio(old_tb)) + return -EEXIST; + if (!__swp_tb_get_count(old_tb)) + return -ENOENT; + if (swp_tb_is_shadow(old_tb) && shadowp) + *shadowp = swp_tb_to_shadow(old_tb); + + if (nr == 1) + return 0; + + ci_off = round_down(ci_off, nr); + ci_end = ci_off + nr; do { old_tb = __swap_table_get(ci, ci_off); - if (unlikely(swp_tb_is_folio(old_tb))) - return -EEXIST; - if (unlikely(!__swp_tb_get_count(old_tb))) - return -ENOENT; - if (swp_tb_is_shadow(old_tb)) - *shadow = swp_tb_to_shadow(old_tb); + if (unlikely(swp_tb_is_folio(old_tb) || + !__swp_tb_get_count(old_tb))) + return -EBUSY; } while (++ci_off < ci_end); return 0; @@ -241,15 +255,13 @@ static int swap_cache_add_folio(struct folio *folio, swp_entry_t entry, { int err; void *shadow = NULL; - unsigned int ci_off; struct swap_info_struct *si; struct swap_cluster_info *ci; unsigned long nr_pages = folio_nr_pages(folio); si = __swap_entry_to_info(entry); ci = swap_cluster_lock(si, swp_offset(entry)); - ci_off = swp_cluster_offset(entry); - err = __swap_cache_add_check(ci, ci_off, nr_pages, &shadow); + err = __swap_cache_add_check(ci, entry, nr_pages, &shadow); if (err) { swap_cluster_unlock(ci); return err; @@ -404,6 +416,142 @@ void __swap_cache_replace_folio(struct swap_cluster_info *ci, } } +/* + * Try to allocate a folio of given order in the swap cache. + * + * This helper resolves the potential races of swap allocation + * and prepares a folio to be used for swap IO. May return following + * value: + * + * -ENOMEM / -EBUSY: Order is too large or in conflict with sub slot, + * caller should shrink the order and retry + * -ENOENT / -EEXIST: Target swap entry is unavailable or cached, the caller + * should abort or try to use the cached folio instead + */ +static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, + swp_entry_t targ_entry, gfp_t gfp, + unsigned int order, struct vm_fault *vmf, + struct mempolicy *mpol, pgoff_t ilx) +{ + int err; + swp_entry_t entry; + struct folio *folio; + void *shadow = NULL; + unsigned long address, nr_pages = 1UL << order; + struct vm_area_struct *vma = vmf ? vmf->vma : NULL; + + VM_WARN_ON_ONCE(nr_pages > SWAPFILE_CLUSTER); + entry.val = round_down(targ_entry.val, nr_pages); + + /* Check if the slot and range are available, skip allocation if not */ + spin_lock(&ci->lock); + err = __swap_cache_add_check(ci, targ_entry, nr_pages, NULL); + spin_unlock(&ci->lock); + if (unlikely(err)) + return ERR_PTR(err); + + /* + * Limit THP gfp. The limitation is a no-op for typical + * GFP_HIGHUSER_MOVABLE but matters for shmem. + */ + if (order) + gfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp); + + if (mpol || !vmf) { + folio = folio_alloc_mpol(gfp, order, mpol, ilx, numa_node_id()); + } else { + address = round_down(vmf->address, PAGE_SIZE << order); + folio = vma_alloc_folio(gfp, order, vmf->vma, address); + } + if (unlikely(!folio)) + return ERR_PTR(-ENOMEM); + + /* Double check the range is still not in conflict */ + spin_lock(&ci->lock); + err = __swap_cache_add_check(ci, targ_entry, nr_pages, &shadow); + if (unlikely(err)) { + spin_unlock(&ci->lock); + folio_put(folio); + return ERR_PTR(err); + } + + __folio_set_locked(folio); + __folio_set_swapbacked(folio); + __swap_cache_do_add_folio(ci, folio, entry); + spin_unlock(&ci->lock); + + if (mem_cgroup_swapin_charge_folio(folio, vmf ? vmf->vma->vm_mm : NULL, + gfp, entry)) { + spin_lock(&ci->lock); + __swap_cache_do_del_folio(ci, folio, entry, shadow); + spin_unlock(&ci->lock); + folio_unlock(folio); + /* nr_pages refs from swap cache, 1 from allocation */ + folio_put_refs(folio, nr_pages + 1); + count_mthp_stat(order, MTHP_STAT_SWPIN_FALLBACK_CHARGE); + return ERR_PTR(-ENOMEM); + } + + /* For memsw accounting, swap is uncharged when folio is added to swap cache */ + memcg1_swapin(entry, 1 << order); + if (shadow) + workingset_refault(folio, shadow); + + node_stat_mod_folio(folio, NR_FILE_PAGES, nr_pages); + lruvec_stat_mod_folio(folio, NR_SWAPCACHE, nr_pages); + + /* Caller will initiate read into locked new_folio */ + folio_add_lru(folio); + return folio; +} + +/** + * swap_cache_alloc_folio - Allocate folio for swapped out slot in swap cache. + * @targ_entry: swap entry indicating the target slot + * @gfp: memory allocation flags + * @orders: allocation orders, must be non zero + * @vmf: fault information + * @mpol: NUMA memory allocation policy to be applied + * @ilx: NUMA interleave index, for use only when MPOL_INTERLEAVE + * + * Allocate a folio in the swap cache for one swap slot, typically before + * doing IO (e.g. swap in or zswap writeback). The swap slot indicated by + * @targ_entry must have a non-zero swap count (swapped out). + * + * Context: Caller must protect the swap device with reference count or locks. + * Return: Returns the folio if allocation succeeded and folio is in the swap + * cache. Returns error code if failed due to race, OOM or invalid arguments. + */ +struct folio *swap_cache_alloc_folio(swp_entry_t targ_entry, gfp_t gfp, + unsigned long orders, struct vm_fault *vmf, + struct mempolicy *mpol, pgoff_t ilx) +{ + int order, err; + struct folio *ret; + struct swap_cluster_info *ci; + + ci = __swap_entry_to_cluster(targ_entry); + order = highest_order(orders); + + /* orders must be non-zero, and must not exceed cluster size. */ + if (WARN_ON_ONCE(!orders || (1UL << order) > SWAPFILE_CLUSTER)) + return ERR_PTR(-EINVAL); + + do { + ret = __swap_cache_alloc(ci, targ_entry, gfp, order, + vmf, mpol, ilx); + if (!IS_ERR(ret)) + break; + err = PTR_ERR(ret); + if (!order || (err && err != -EBUSY && err != -ENOMEM)) + break; + count_mthp_stat(order, MTHP_STAT_SWPIN_FALLBACK); + order = next_order(&orders, order); + } while (orders); + + return ret; +} + /* * If we are the only user, then try to free up the swap cache. * @@ -547,68 +695,18 @@ static int __swap_cache_prepare_and_add(swp_entry_t entry, return ret; } -/** - * swap_cache_alloc_folio - Allocate folio for swapped out slot in swap cache. - * @entry: the swapped out swap entry to be binded to the folio. - * @gfp_mask: memory allocation flags - * @mpol: NUMA memory allocation policy to be applied - * @ilx: NUMA interleave index, for use only when MPOL_INTERLEAVE - * - * Allocate a folio in the swap cache for one swap slot, typically before - * doing IO (e.g. swap in or zswap writeback). The swap slot indicated by - * @entry must have a non-zero swap count (swapped out). - * Currently only supports order 0. - * - * Context: Caller must protect the swap device with reference count or locks. - * Return: Returns the folio if allocation succeeded and folio is added to - * swap cache. Returns error code if allocation failed due to race or OOM. - */ -struct folio *swap_cache_alloc_folio(swp_entry_t entry, gfp_t gfp_mask, - struct mempolicy *mpol, pgoff_t ilx) -{ - int err; - struct folio *folio; - - /* Allocate a new folio to be added into the swap cache. */ - folio = folio_alloc_mpol(gfp_mask, 0, mpol, ilx, numa_node_id()); - if (!folio) - return ERR_PTR(-ENOMEM); - - /* - * Try to add the new folio to the swap cache. It returns - * -EEXIST if the entry is already cached. - */ - err = __swap_cache_prepare_and_add(entry, folio, gfp_mask, false); - if (err) { - folio_put(folio); - return ERR_PTR(err); - } - - return folio; -} - static struct folio *swap_cache_read_folio(swp_entry_t entry, gfp_t gfp, struct mempolicy *mpol, pgoff_t ilx, struct swap_iocb **plug, bool readahead) { - struct swap_info_struct *si = __swap_entry_to_info(entry); struct folio *folio; - /* Check the swap cache again for readahead path. */ - folio = swap_cache_get_folio(entry); - if (folio) - return folio; - - /* Skip allocation for unused and bad swap slot for readahead. */ - if (!swap_entry_swapped(si, entry)) - return NULL; - do { folio = swap_cache_get_folio(entry); if (folio) return folio; - folio = swap_cache_alloc_folio(entry, gfp, mpol, ilx); + folio = swap_cache_alloc_folio(entry, gfp, BIT(0), NULL, mpol, ilx); } while (PTR_ERR(folio) == -EEXIST); if (IS_ERR_OR_NULL(folio)) diff --git a/mm/zswap.c b/mm/zswap.c index e27f6e96f003..761cd699e0a3 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1000,7 +1000,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry, return -EEXIST; mpol = get_task_policy(current); - folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, mpol, + folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol, NO_INTERLEAVE_INDEX); put_swap_device(si); From 02d733a7ec1d751ddb624cf5d1eb953d0bf2f704 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:44 +0800 Subject: [PATCH 2586/5214] mm, swap: unify large folio allocation Now that direct large order allocation is supported in the swap cache, both anon and shmem can use it instead of implementing their own methods. This unifies the fallback and swap cache check, which also reduces the TOCTOU race window of swap cache state: previously, high order swapin required checking swap cache states first, then allocating and falling back separately. Now all these steps happen in the same compact loop. Order fallback and statistics are also unified, callers just need to check and pass the acceptable order bitmask. There is basically no behavior change. This only makes things more unified and prepares for later commits. Cgroup and zero map checks can also be moved into the compact loop, further reducing race windows and redundancy Link: https://lore.kernel.org/20260517-swap-table-p4-v5-5-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: Chris Li Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/memory.c | 80 ++++++--------------------- mm/shmem.c | 102 +++++++++++----------------------- mm/swap.h | 30 ++-------- mm/swap_state.c | 143 ++++++++---------------------------------------- mm/swapfile.c | 3 +- 5 files changed, 79 insertions(+), 279 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 0c9d9c2cbf0e..da891bcce59c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4609,26 +4609,6 @@ static vm_fault_t handle_pte_marker(struct vm_fault *vmf) return VM_FAULT_SIGBUS; } -static struct folio *__alloc_swap_folio(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct folio *folio; - softleaf_t entry; - - folio = vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, vma, vmf->address); - if (!folio) - return NULL; - - entry = softleaf_from_pte(vmf->orig_pte); - if (mem_cgroup_swapin_charge_folio(folio, vma->vm_mm, - GFP_KERNEL, entry)) { - folio_put(folio); - return NULL; - } - - return folio; -} - #ifdef CONFIG_TRANSPARENT_HUGEPAGE /* * Check if the PTEs within a range are contiguous swap entries @@ -4658,8 +4638,6 @@ static bool can_swapin_thp(struct vm_fault *vmf, pte_t *ptep, int nr_pages) */ if (unlikely(swap_zeromap_batch(entry, nr_pages, NULL) != nr_pages)) return false; - if (unlikely(non_swapcache_batch(entry, nr_pages) != nr_pages)) - return false; return true; } @@ -4687,16 +4665,14 @@ static inline unsigned long thp_swap_suitable_orders(pgoff_t swp_offset, return orders; } -static struct folio *alloc_swap_folio(struct vm_fault *vmf) +static unsigned long thp_swapin_suitable_orders(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; unsigned long orders; - struct folio *folio; unsigned long addr; softleaf_t entry; spinlock_t *ptl; pte_t *pte; - gfp_t gfp; int order; /* @@ -4704,7 +4680,7 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf) * maintain the uffd semantics. */ if (unlikely(userfaultfd_armed(vma))) - goto fallback; + return 0; /* * A large swapped out folio could be partially or fully in zswap. We @@ -4712,7 +4688,7 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf) * folio. */ if (!zswap_never_enabled()) - goto fallback; + return 0; entry = softleaf_from_pte(vmf->orig_pte); /* @@ -4726,12 +4702,12 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf) vmf->address, orders); if (!orders) - goto fallback; + return 0; pte = pte_offset_map_lock(vmf->vma->vm_mm, vmf->pmd, vmf->address & PMD_MASK, &ptl); if (unlikely(!pte)) - goto fallback; + return 0; /* * For do_swap_page, find the highest order where the aligned range is @@ -4747,29 +4723,12 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf) pte_unmap_unlock(pte, ptl); - /* Try allocating the highest of the remaining orders. */ - gfp = vma_thp_gfp_mask(vma); - while (orders) { - addr = ALIGN_DOWN(vmf->address, PAGE_SIZE << order); - folio = vma_alloc_folio(gfp, order, vma, addr); - if (folio) { - if (!mem_cgroup_swapin_charge_folio(folio, vma->vm_mm, - gfp, entry)) - return folio; - count_mthp_stat(order, MTHP_STAT_SWPIN_FALLBACK_CHARGE); - folio_put(folio); - } - count_mthp_stat(order, MTHP_STAT_SWPIN_FALLBACK); - order = next_order(&orders, order); - } - -fallback: - return __alloc_swap_folio(vmf); + return orders; } #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ -static struct folio *alloc_swap_folio(struct vm_fault *vmf) +static unsigned long thp_swapin_suitable_orders(struct vm_fault *vmf) { - return __alloc_swap_folio(vmf); + return 0; } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ @@ -4875,23 +4834,15 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) if (folio) swap_update_readahead(folio, vma, vmf->address); if (!folio) { - if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) { - folio = alloc_swap_folio(vmf); - if (folio) { - /* - * folio is charged, so swapin can only fail due - * to raced swapin and return NULL. - */ - swapcache = swapin_folio(entry, folio); - if (swapcache != folio) - folio_put(folio); - folio = swapcache; - } - } else { + /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices */ + if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) + folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE, + thp_swapin_suitable_orders(vmf) | BIT(0), + vmf, NULL, 0); + else folio = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, vmf); - } - if (!folio) { + if (IS_ERR_OR_NULL(folio)) { /* * Back out if somebody else faulted in this pte * while we released the pte lock. @@ -4901,6 +4852,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) if (likely(vmf->pte && pte_same(ptep_get(vmf->pte), vmf->orig_pte))) ret = VM_FAULT_OOM; + folio = NULL; goto unlock; } diff --git a/mm/shmem.c b/mm/shmem.c index 6edb23b41bac..77a3e28e5160 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -159,7 +159,7 @@ static unsigned long shmem_default_max_inodes(void) static int shmem_swapin_folio(struct inode *inode, pgoff_t index, struct folio **foliop, enum sgp_type sgp, gfp_t gfp, - struct vm_area_struct *vma, vm_fault_t *fault_type); + struct vm_fault *vmf, vm_fault_t *fault_type); static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb) { @@ -2017,68 +2017,32 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf, } static struct folio *shmem_swap_alloc_folio(struct inode *inode, - struct vm_area_struct *vma, pgoff_t index, + struct vm_fault *vmf, pgoff_t index, swp_entry_t entry, int order, gfp_t gfp) { + pgoff_t ilx; + struct folio *folio; + struct mempolicy *mpol; struct shmem_inode_info *info = SHMEM_I(inode); - struct folio *new, *swapcache; - int nr_pages = 1 << order; - gfp_t alloc_gfp = gfp; - if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { - if (WARN_ON_ONCE(order)) - return ERR_PTR(-EINVAL); - } else if (order) { - /* - * If uffd is active for the vma, we need per-page fault - * fidelity to maintain the uffd semantics, then fallback - * to swapin order-0 folio, as well as for zswap case. - * Any existing sub folio in the swap cache also blocks - * mTHP swapin. - */ - if ((vma && unlikely(userfaultfd_armed(vma))) || - !zswap_never_enabled() || - non_swapcache_batch(entry, nr_pages) != nr_pages) - goto fallback; + if ((vmf && unlikely(userfaultfd_armed(vmf->vma))) || + !zswap_never_enabled()) + order = 0; - alloc_gfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp); - } -retry: - new = shmem_alloc_folio(alloc_gfp, order, info, index); - if (!new) { - new = ERR_PTR(-ENOMEM); - goto fallback; +again: + mpol = shmem_get_pgoff_policy(info, index, order, &ilx); + folio = swapin_sync(entry, gfp, BIT(order), vmf, mpol, ilx); + mpol_cond_put(mpol); + + if (!IS_ERR(folio)) + return folio; + + if (order) { + order = 0; + goto again; } - if (mem_cgroup_swapin_charge_folio(new, vma ? vma->vm_mm : NULL, - alloc_gfp, entry)) { - folio_put(new); - new = ERR_PTR(-ENOMEM); - goto fallback; - } - - swapcache = swapin_folio(entry, new); - if (swapcache != new) { - folio_put(new); - if (!swapcache) { - /* - * The new folio is charged already, swapin can - * only fail due to another raced swapin. - */ - new = ERR_PTR(-EEXIST); - goto fallback; - } - } - return swapcache; -fallback: - /* Order 0 swapin failed, nothing to fallback to, abort */ - if (!order) - return new; - entry.val += index - round_down(index, nr_pages); - alloc_gfp = gfp; - nr_pages = 1; - order = 0; - goto retry; + return folio; } /* @@ -2265,11 +2229,12 @@ static int shmem_split_large_entry(struct inode *inode, pgoff_t index, */ static int shmem_swapin_folio(struct inode *inode, pgoff_t index, struct folio **foliop, enum sgp_type sgp, - gfp_t gfp, struct vm_area_struct *vma, + gfp_t gfp, struct vm_fault *vmf, vm_fault_t *fault_type) { struct address_space *mapping = inode->i_mapping; - struct mm_struct *fault_mm = vma ? vma->vm_mm : NULL; + struct vm_area_struct *vma = vmf ? vmf->vma : NULL; + struct mm_struct *fault_mm = vmf ? vmf->vma->vm_mm : NULL; struct shmem_inode_info *info = SHMEM_I(inode); swp_entry_t swap; softleaf_t index_entry; @@ -2310,20 +2275,19 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index, if (!folio) { if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) { /* Direct swapin skipping swap cache & readahead */ - folio = shmem_swap_alloc_folio(inode, vma, index, - index_entry, order, gfp); - if (IS_ERR(folio)) { - error = PTR_ERR(folio); - folio = NULL; - goto failed; - } + folio = shmem_swap_alloc_folio(inode, vmf, index, + swap, order, gfp); } else { /* Cached swapin only supports order 0 folio */ folio = shmem_swapin_cluster(swap, gfp, info, index); - if (!folio) { + } + if (IS_ERR_OR_NULL(folio)) { + if (IS_ERR(folio)) + error = PTR_ERR(folio); + else error = -ENOMEM; - goto failed; - } + folio = NULL; + goto failed; } if (fault_type) { *fault_type |= VM_FAULT_MAJOR; @@ -2471,7 +2435,7 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, if (xa_is_value(folio)) { error = shmem_swapin_folio(inode, index, &folio, - sgp, gfp, vma, fault_type); + sgp, gfp, vmf, fault_type); if (error == -EEXIST) goto repeat; diff --git a/mm/swap.h b/mm/swap.h index 6774af10a943..8e57e9431624 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -300,7 +300,8 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t flag, struct mempolicy *mpol, pgoff_t ilx); struct folio *swapin_readahead(swp_entry_t entry, gfp_t flag, struct vm_fault *vmf); -struct folio *swapin_folio(swp_entry_t entry, struct folio *folio); +struct folio *swapin_sync(swp_entry_t entry, gfp_t flag, unsigned long orders, + struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx); void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma, unsigned long addr); @@ -334,24 +335,6 @@ static inline int swap_zeromap_batch(swp_entry_t entry, int max_nr, return find_next_bit(sis->zeromap, end, start) - start; } -static inline int non_swapcache_batch(swp_entry_t entry, int max_nr) -{ - int i; - - /* - * While allocating a large folio and doing mTHP swapin, we need to - * ensure all entries are not cached, otherwise, the mTHP folio will - * be in conflict with the folio in swap cache. - */ - for (i = 0; i < max_nr; i++) { - if (swap_cache_has_folio(entry)) - return i; - entry.val++; - } - - return i; -} - #else /* CONFIG_SWAP */ struct swap_iocb; static inline struct swap_cluster_info *swap_cluster_lock( @@ -433,7 +416,9 @@ static inline struct folio *swapin_readahead(swp_entry_t swp, gfp_t gfp_mask, return NULL; } -static inline struct folio *swapin_folio(swp_entry_t entry, struct folio *folio) +static inline struct folio *swapin_sync( + swp_entry_t entry, gfp_t flag, unsigned long orders, + struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx) { return NULL; } @@ -493,10 +478,5 @@ static inline int swap_zeromap_batch(swp_entry_t entry, int max_nr, { return 0; } - -static inline int non_swapcache_batch(swp_entry_t entry, int max_nr) -{ - return 0; -} #endif /* CONFIG_SWAP */ #endif /* _MM_SWAP_H */ diff --git a/mm/swap_state.c b/mm/swap_state.c index 0adb0565bbb1..98c8691826fb 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -238,43 +238,6 @@ void __swap_cache_add_folio(struct swap_cluster_info *ci, lruvec_stat_mod_folio(folio, NR_SWAPCACHE, nr_pages); } -/** - * swap_cache_add_folio - Add a folio into the swap cache. - * @folio: The folio to be added. - * @entry: The swap entry corresponding to the folio. - * @shadowp: If a shadow is found, return the shadow. - * - * Add a folio into the swap cache. Will return error if any slot is no - * longer a valid swapped out slot or already occupied by another folio. - * - * Context: Caller must ensure @entry is valid and protect the swap device - * with reference count or locks. - */ -static int swap_cache_add_folio(struct folio *folio, swp_entry_t entry, - void **shadowp) -{ - int err; - void *shadow = NULL; - struct swap_info_struct *si; - struct swap_cluster_info *ci; - unsigned long nr_pages = folio_nr_pages(folio); - - si = __swap_entry_to_info(entry); - ci = swap_cluster_lock(si, swp_offset(entry)); - err = __swap_cache_add_check(ci, entry, nr_pages, &shadow); - if (err) { - swap_cluster_unlock(ci); - return err; - } - - __swap_cache_add_folio(ci, folio, entry); - swap_cluster_unlock(ci); - if (shadowp) - *shadowp = shadow; - - return 0; -} - static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, struct folio *folio, swp_entry_t entry, void *shadow) @@ -650,51 +613,6 @@ void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma, } } -/** - * __swap_cache_prepare_and_add - Prepare the folio and add it to swap cache. - * @entry: swap entry to be bound to the folio. - * @folio: folio to be added. - * @gfp: memory allocation flags for charge, can be 0 if @charged if true. - * @charged: if the folio is already charged. - * - * Update the swap_map and add folio as swap cache, typically before swapin. - * All swap slots covered by the folio must have a non-zero swap count. - * - * Context: Caller must protect the swap device with reference count or locks. - * Return: 0 if success, error code if failed. - */ -static int __swap_cache_prepare_and_add(swp_entry_t entry, - struct folio *folio, - gfp_t gfp, bool charged) -{ - void *shadow; - int ret; - - __folio_set_locked(folio); - __folio_set_swapbacked(folio); - - if (!charged && mem_cgroup_swapin_charge_folio(folio, NULL, gfp, entry)) { - ret = -ENOMEM; - goto failed; - } - - ret = swap_cache_add_folio(folio, entry, &shadow); - if (ret) - goto failed; - - memcg1_swapin(entry, folio_nr_pages(folio)); - if (shadow) - workingset_refault(folio, shadow); - - /* Caller will initiate read into locked folio */ - folio_add_lru(folio); - return 0; - -failed: - folio_unlock(folio); - return ret; -} - static struct folio *swap_cache_read_folio(swp_entry_t entry, gfp_t gfp, struct mempolicy *mpol, pgoff_t ilx, struct swap_iocb **plug, bool readahead) @@ -705,7 +623,6 @@ static struct folio *swap_cache_read_folio(swp_entry_t entry, gfp_t gfp, folio = swap_cache_get_folio(entry); if (folio) return folio; - folio = swap_cache_alloc_folio(entry, gfp, BIT(0), NULL, mpol, ilx); } while (PTR_ERR(folio) == -EEXIST); @@ -722,49 +639,37 @@ static struct folio *swap_cache_read_folio(swp_entry_t entry, gfp_t gfp, } /** - * swapin_folio - swap-in one or multiple entries skipping readahead. - * @entry: starting swap entry to swap in - * @folio: a new allocated and charged folio + * swapin_sync - swap-in one or multiple entries skipping readahead. + * @entry: swap entry indicating the target slot + * @gfp: memory allocation flags + * @orders: allocation orders + * @vmf: fault information + * @mpol: NUMA memory allocation policy to be applied + * @ilx: NUMA interleave index, for use only when MPOL_INTERLEAVE * - * Reads @entry into @folio, @folio will be added to the swap cache. - * If @folio is a large folio, the @entry will be rounded down to align - * with the folio size. + * This allocates a folio suitable for given @orders, or returns the + * existing folio in the swap cache for @entry. This initiates the IO, too, + * if needed. @entry is rounded down if @orders allow large allocation. * - * Return: returns pointer to @folio on success. If folio is a large folio - * and this raced with another swapin, NULL will be returned to allow fallback - * to order 0. Else, if another folio was already added to the swap cache, - * return that swap cache folio instead. + * Context: Caller must ensure @entry is valid and pin the swap device with refcount. + * Return: Returns the folio on success, error code if failed. */ -struct folio *swapin_folio(swp_entry_t entry, struct folio *folio) +struct folio *swapin_sync(swp_entry_t entry, gfp_t gfp, unsigned long orders, + struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx) { - int ret; - struct folio *swapcache; - pgoff_t offset = swp_offset(entry); - unsigned long nr_pages = folio_nr_pages(folio); + struct folio *folio; - entry = swp_entry(swp_type(entry), round_down(offset, nr_pages)); - for (;;) { - ret = __swap_cache_prepare_and_add(entry, folio, 0, true); - if (!ret) { - swap_read_folio(folio, NULL); - break; - } + do { + folio = swap_cache_get_folio(entry); + if (folio) + return folio; + folio = swap_cache_alloc_folio(entry, gfp, orders, vmf, mpol, ilx); + } while (PTR_ERR(folio) == -EEXIST); - /* - * Large order allocation needs special handling on - * race: if a smaller folio exists in cache, swapin needs - * to fall back to order 0, and doing a swap cache lookup - * might return a folio that is irrelevant to the faulting - * entry because @entry is aligned down. Just return NULL. - */ - if (ret != -EEXIST || nr_pages > 1) - return NULL; - - swapcache = swap_cache_get_folio(entry); - if (swapcache) - return swapcache; - } + if (IS_ERR(folio)) + return folio; + swap_read_folio(folio, NULL); return folio; } diff --git a/mm/swapfile.c b/mm/swapfile.c index ee515a6fbccd..4ffd491cacca 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1853,8 +1853,7 @@ void folio_put_swap(struct folio *folio, struct page *subpage) * do_swap_page() * ... swapoff+swapon * swap_cache_alloc_folio() - * swap_cache_add_folio() - * // check swap_map + * // check swap_map * // verify PTE not changed * * In __swap_duplicate(), the swap_map need to be checked before From 945578fee2ec17bebdec067371214d3cbed48822 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:45 +0800 Subject: [PATCH 2587/5214] mm/memcg, swap: tidy up cgroup v1 memsw swap helpers The cgroup v1 swap helpers always operate on swap cache folios whose swap entry is stable: the folio is locked and in the swap cache. There is no need to pass the swap entry or page count as separate parameters when they can be derived from the folio itself. Simplify the redundant parameters and add sanity checks to document the required preconditions. Also rename memcg1_swapout to __memcg1_swapout to indicate it requires special calling context: the folio must be isolated and dying, and the call must be made with interrupts disabled. No functional change. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-6-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/memcontrol.h | 8 ++++---- include/linux/swap.h | 10 ++++------ mm/huge_memory.c | 2 +- mm/memcontrol-v1.c | 33 ++++++++++++++++++++------------- mm/memcontrol.c | 9 ++++----- mm/swap_state.c | 4 ++-- mm/swapfile.c | 2 +- mm/vmscan.c | 2 +- 8 files changed, 37 insertions(+), 33 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index dc3fa687759b..7d08128de1fd 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1899,8 +1899,8 @@ static inline void mem_cgroup_exit_user_fault(void) current->in_user_fault = 0; } -void memcg1_swapout(struct folio *folio, swp_entry_t entry); -void memcg1_swapin(swp_entry_t entry, unsigned int nr_pages); +void __memcg1_swapout(struct folio *folio); +void memcg1_swapin(struct folio *folio); #else /* CONFIG_MEMCG_V1 */ static inline @@ -1929,11 +1929,11 @@ static inline void mem_cgroup_exit_user_fault(void) { } -static inline void memcg1_swapout(struct folio *folio, swp_entry_t entry) +static inline void __memcg1_swapout(struct folio *folio) { } -static inline void memcg1_swapin(swp_entry_t entry, unsigned int nr_pages) +static inline void memcg1_swapin(struct folio *folio) { } diff --git a/include/linux/swap.h b/include/linux/swap.h index 7a09df6977a5..f907d3df52d0 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -571,13 +571,12 @@ static inline void folio_throttle_swaprate(struct folio *folio, gfp_t gfp) #endif #if defined(CONFIG_MEMCG) && defined(CONFIG_SWAP) -int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry); -static inline int mem_cgroup_try_charge_swap(struct folio *folio, - swp_entry_t entry) +int __mem_cgroup_try_charge_swap(struct folio *folio); +static inline int mem_cgroup_try_charge_swap(struct folio *folio) { if (mem_cgroup_disabled()) return 0; - return __mem_cgroup_try_charge_swap(folio, entry); + return __mem_cgroup_try_charge_swap(folio); } extern void __mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages); @@ -591,8 +590,7 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_p extern long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg); extern bool mem_cgroup_swap_full(struct folio *folio); #else -static inline int mem_cgroup_try_charge_swap(struct folio *folio, - swp_entry_t entry) +static inline int mem_cgroup_try_charge_swap(struct folio *folio) { return 0; } diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b7df167f7acb..1f14c5c48b4a 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4446,7 +4446,7 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) /* * Exclude swapcache: originally to avoid a corrupt deferred split - * queue. Nowadays that is fully prevented by memcg1_swapout(); + * queue. Nowadays that is fully prevented by __memcg1_swapout(); * but if page reclaim is already handling the same folio, it is * unnecessary to handle it again in the shrinker, so excluding * swapcache here may still be a useful optimization. diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 433bba9dfe71..36c507d81dc5 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -604,18 +604,23 @@ void memcg1_commit_charge(struct folio *folio, struct mem_cgroup *memcg) } /** - * memcg1_swapout - transfer a memsw charge to swap + * __memcg1_swapout - transfer a memsw charge to swap * @folio: folio whose memsw charge to transfer - * @entry: swap entry to move the charge to * - * Transfer the memsw charge of @folio to @entry. + * Transfer the memsw charge of @folio to the swap entry stored in + * folio->swap. + * + * Context: folio must be isolated, unmapped, locked and is just about + * to be freed, and caller must disable IRQs. */ -void memcg1_swapout(struct folio *folio, swp_entry_t entry) +void __memcg1_swapout(struct folio *folio) { struct mem_cgroup *memcg, *swap_memcg; struct obj_cgroup *objcg; unsigned int nr_entries; + VM_WARN_ON_ONCE_FOLIO(!folio_test_swapcache(folio), folio); + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); VM_BUG_ON_FOLIO(folio_test_lru(folio), folio); VM_BUG_ON_FOLIO(folio_ref_count(folio), folio); @@ -641,7 +646,7 @@ void memcg1_swapout(struct folio *folio, swp_entry_t entry) swap_memcg = mem_cgroup_private_id_get_online(memcg, nr_entries); mod_memcg_state(swap_memcg, MEMCG_SWAP, nr_entries); - swap_cgroup_record(folio, mem_cgroup_private_id(swap_memcg), entry); + swap_cgroup_record(folio, mem_cgroup_private_id(swap_memcg), folio->swap); folio_unqueue_deferred_split(folio); folio->memcg_data = 0; @@ -671,18 +676,20 @@ void memcg1_swapout(struct folio *folio, swp_entry_t entry) obj_cgroup_put(objcg); } -/* +/** * memcg1_swapin - uncharge swap slot - * @entry: the first swap entry for which the pages are charged - * @nr_pages: number of pages which will be uncharged + * @folio: folio being swapped in * - * Call this function after successfully adding the charged page to swapcache. + * Call this function after successfully adding the charged + * folio to swapcache. * - * Note: This function assumes the page for which swap slot is being uncharged - * is order 0 page. + * Context: The folio has to be in swap cache and locked. */ -void memcg1_swapin(swp_entry_t entry, unsigned int nr_pages) +void memcg1_swapin(struct folio *folio) { + VM_WARN_ON_ONCE_FOLIO(!folio_test_swapcache(folio), folio); + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); + /* * Cgroup1's unified memory+swap counter has been charged with the * new swapcache page, finish the transfer by uncharging the swap @@ -701,7 +708,7 @@ void memcg1_swapin(swp_entry_t entry, unsigned int nr_pages) * let's not wait for it. The page already received a * memory+swap charge, drop the swap entry duplicate. */ - mem_cgroup_uncharge_swap(entry, nr_pages); + mem_cgroup_uncharge_swap(folio->swap, folio_nr_pages(folio)); } } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 431cad99189f..c3d0f79dc84e 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5473,13 +5473,12 @@ int __init mem_cgroup_init(void) /** * __mem_cgroup_try_charge_swap - try charging swap space for a folio * @folio: folio being added to swap - * @entry: swap entry to charge * - * Try to charge @folio's memcg for the swap space at @entry. + * Try to charge @folio's memcg for the swap space at folio->swap. * * Returns 0 on success, -ENOMEM on failure. */ -int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry) +int __mem_cgroup_try_charge_swap(struct folio *folio) { unsigned int nr_pages = folio_nr_pages(folio); struct page_counter *counter; @@ -5496,7 +5495,7 @@ int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry) rcu_read_lock(); memcg = obj_cgroup_memcg(objcg); - if (!entry.val) { + if (!folio_test_swapcache(folio)) { memcg_memory_event(memcg, MEMCG_SWAP_FAIL); rcu_read_unlock(); return 0; @@ -5515,7 +5514,7 @@ int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry) } mod_memcg_state(memcg, MEMCG_SWAP, nr_pages); - swap_cgroup_record(folio, mem_cgroup_private_id(memcg), entry); + swap_cgroup_record(folio, mem_cgroup_private_id(memcg), folio->swap); return 0; } diff --git a/mm/swap_state.c b/mm/swap_state.c index 98c8691826fb..7a80494fa37f 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -455,8 +455,8 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, return ERR_PTR(-ENOMEM); } - /* For memsw accounting, swap is uncharged when folio is added to swap cache */ - memcg1_swapin(entry, 1 << order); + /* memsw uncharges swap when folio is added to swap cache */ + memcg1_swapin(folio); if (shadow) workingset_refault(folio, shadow); diff --git a/mm/swapfile.c b/mm/swapfile.c index 4ffd491cacca..4875b3d3e658 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1757,7 +1757,7 @@ int folio_alloc_swap(struct folio *folio) } /* Need to call this even if allocation failed, for MEMCG_SWAP_FAIL. */ - if (unlikely(mem_cgroup_try_charge_swap(folio, folio->swap))) + if (unlikely(mem_cgroup_try_charge_swap(folio))) swap_cache_del_folio(folio); if (unlikely(!folio_test_swapcache(folio))) diff --git a/mm/vmscan.c b/mm/vmscan.c index 4b0984387658..3231af682fa7 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -739,7 +739,7 @@ static int __remove_mapping(struct address_space *mapping, struct folio *folio, if (reclaimed && !mapping_exiting(mapping)) shadow = workingset_eviction(folio, target_memcg); - memcg1_swapout(folio, swap); + __memcg1_swapout(folio); __swap_cache_del_folio(ci, folio, swap, shadow); swap_cluster_unlock_irq(ci); } else { From c1fd92589c0dc15f4daa798c3a83d190a1ce674a Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:46 +0800 Subject: [PATCH 2588/5214] mm, swap: support flexible batch freeing of slots in different memcgs Instead of requiring the caller to ensure all slots are in the same memcg, make the function handle different memcgs at once. This is both a micro optimization and required for removing the memcg lookup in the page table layer, so it can be unified at the swap layer. We are not removing the memcg lookup in the page table in this commit. It has to be done after the memcg lookup is deferred to the swap layer. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-7-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/swapfile.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 4875b3d3e658..60d8f0df3f32 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1899,21 +1899,46 @@ void __swap_cluster_free_entries(struct swap_info_struct *si, unsigned int ci_start, unsigned int nr_pages) { unsigned long old_tb; + unsigned int type = si->type; + unsigned short batch_id = 0, id_cur; unsigned int ci_off = ci_start, ci_end = ci_start + nr_pages; - unsigned long offset = cluster_offset(si, ci) + ci_start; + unsigned long ci_head = cluster_offset(si, ci); + unsigned int batch_off = ci_off; + swp_entry_t entry; VM_WARN_ON(ci->count < nr_pages); ci->count -= nr_pages; do { old_tb = __swap_table_get(ci, ci_off); - /* Release the last ref, or after swap cache is dropped */ + /* + * Freeing is done after release of the last swap count + * ref, or after swap cache is dropped + */ VM_WARN_ON(!swp_tb_is_shadow(old_tb) || __swp_tb_get_count(old_tb) > 1); __swap_table_set(ci, ci_off, null_to_swp_tb()); + + /* + * Uncharge swap slots by memcg in batches. Consecutive + * slots with the same cgroup id are uncharged together. + */ + entry = swp_entry(type, ci_head + ci_off); + id_cur = lookup_swap_cgroup_id(entry); + if (batch_id != id_cur) { + if (batch_id) + mem_cgroup_uncharge_swap(swp_entry(type, ci_head + batch_off), + ci_off - batch_off); + batch_id = id_cur; + batch_off = ci_off; + } } while (++ci_off < ci_end); - mem_cgroup_uncharge_swap(swp_entry(si->type, offset), nr_pages); - swap_range_free(si, offset, nr_pages); + if (batch_id) { + mem_cgroup_uncharge_swap(swp_entry(type, ci_head + batch_off), + ci_off - batch_off); + } + + swap_range_free(si, ci_head + ci_start, nr_pages); swap_cluster_assert_empty(ci, ci_start, nr_pages, false); if (!ci->count) From bc34e87a51d9e51d398ef6d8c2c35cf1a4ff38b9 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:47 +0800 Subject: [PATCH 2589/5214] mm, swap: delay and unify memcg lookup and charging for swapin Instead of checking the cgroup private ID during page table walk in swap_pte_batch(), move the memcg lookup into __swap_cache_add_check() under the cluster lock. The first pre-alloc check is speculative and skips the memcg check since the post-alloc stable check ensures all slots covered by the folio belong to the same memcg. It is very rare for contiguous and aligned entries across a contiguous region of a page table of the same process or shmem mapping to belong to different memcgs. This also prepares for recording the memcg info in the cluster's table. Also make the order check and fallback more compact. There should be no user-observable behavior change. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-8-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/memcontrol.h | 6 +++--- mm/internal.h | 10 +--------- mm/memcontrol.c | 10 ++++------ mm/swap_state.c | 28 +++++++++++++++++++--------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 7d08128de1fd..a013f37f24aa 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -646,8 +646,8 @@ static inline int mem_cgroup_charge(struct folio *folio, struct mm_struct *mm, int mem_cgroup_charge_hugetlb(struct folio* folio, gfp_t gfp); -int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm, - gfp_t gfp, swp_entry_t entry); +int mem_cgroup_swapin_charge_folio(struct folio *folio, unsigned short id, + struct mm_struct *mm, gfp_t gfp); void __mem_cgroup_uncharge(struct folio *folio); @@ -1137,7 +1137,7 @@ static inline int mem_cgroup_charge_hugetlb(struct folio* folio, gfp_t gfp) } static inline int mem_cgroup_swapin_charge_folio(struct folio *folio, - struct mm_struct *mm, gfp_t gfp, swp_entry_t entry) + unsigned short id, struct mm_struct *mm, gfp_t gfp) { return 0; } diff --git a/mm/internal.h b/mm/internal.h index 09931b1e535f..9dbd8e3c991f 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -451,24 +451,16 @@ static inline int swap_pte_batch(pte_t *start_ptep, int max_nr, pte_t pte) { pte_t expected_pte = pte_next_swp_offset(pte); const pte_t *end_ptep = start_ptep + max_nr; - const softleaf_t entry = softleaf_from_pte(pte); pte_t *ptep = start_ptep + 1; - unsigned short cgroup_id; VM_WARN_ON(max_nr < 1); - VM_WARN_ON(!softleaf_is_swap(entry)); + VM_WARN_ON(!softleaf_is_swap(softleaf_from_pte(pte))); - cgroup_id = lookup_swap_cgroup_id(entry); while (ptep < end_ptep) { - softleaf_t entry; - pte = ptep_get(ptep); if (!pte_same(pte, expected_pte)) break; - entry = softleaf_from_pte(pte); - if (lookup_swap_cgroup_id(entry) != cgroup_id) - break; expected_pte = pte_next_swp_offset(expected_pte); ptep++; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c3d0f79dc84e..1b58b314cb18 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5079,27 +5079,25 @@ int mem_cgroup_charge_hugetlb(struct folio *folio, gfp_t gfp) /** * mem_cgroup_swapin_charge_folio - Charge a newly allocated folio for swapin. - * @folio: folio to charge. + * @folio: the folio to charge + * @id: memory cgroup id * @mm: mm context of the victim * @gfp: reclaim mode - * @entry: swap entry for which the folio is allocated * * This function charges a folio allocated for swapin. Please call this before * adding the folio to the swapcache. * * Returns 0 on success. Otherwise, an error code is returned. */ -int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm, - gfp_t gfp, swp_entry_t entry) +int mem_cgroup_swapin_charge_folio(struct folio *folio, unsigned short id, + struct mm_struct *mm, gfp_t gfp) { struct mem_cgroup *memcg; - unsigned short id; int ret; if (mem_cgroup_disabled()) return 0; - id = lookup_swap_cgroup_id(entry); rcu_read_lock(); memcg = mem_cgroup_from_private_id(id); if (!memcg || !css_tryget_online(&memcg->css)) diff --git a/mm/swap_state.c b/mm/swap_state.c index 7a80494fa37f..bdd949ae0044 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -142,17 +142,21 @@ void *swap_cache_get_shadow(swp_entry_t entry) * @ci: The locked swap cluster * @targ_entry: The target swap entry to check, will be rounded down by @nr * @nr: Number of slots to check, must be a power of 2 - * @shadowp: Returns the shadow value if one exists in the range. + * @shadowp: Returns the shadow value if one exists in the range + * @memcg_id: Returns the memory cgroup id, NULL to ignore cgroup check * * Check if all slots covered by given range have a swap count >= 1. - * Retrieves the shadow if there is one. + * Retrieves the shadow if there is one. If @memcg_id is not NULL, also + * checks if all slots belong to the same cgroup and return the cgroup + * private id. * * Context: Caller must lock the cluster. * Return: 0 if success, error code if failed. */ static int __swap_cache_add_check(struct swap_cluster_info *ci, swp_entry_t targ_entry, - unsigned long nr, void **shadowp) + unsigned long nr, void **shadowp, + unsigned short *memcg_id) { unsigned int ci_off, ci_end; unsigned long old_tb; @@ -172,19 +176,24 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, return -EEXIST; if (!__swp_tb_get_count(old_tb)) return -ENOENT; - if (swp_tb_is_shadow(old_tb) && shadowp) + if (shadowp && swp_tb_is_shadow(old_tb)) *shadowp = swp_tb_to_shadow(old_tb); + if (memcg_id) + *memcg_id = lookup_swap_cgroup_id(targ_entry); if (nr == 1) return 0; + targ_entry.val = round_down(targ_entry.val, nr); ci_off = round_down(ci_off, nr); ci_end = ci_off + nr; do { old_tb = __swap_table_get(ci, ci_off); if (unlikely(swp_tb_is_folio(old_tb) || - !__swp_tb_get_count(old_tb))) + !__swp_tb_get_count(old_tb) || + (memcg_id && *memcg_id != lookup_swap_cgroup_id(targ_entry)))) return -EBUSY; + targ_entry.val++; } while (++ci_off < ci_end); return 0; @@ -400,6 +409,7 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, swp_entry_t entry; struct folio *folio; void *shadow = NULL; + unsigned short memcg_id; unsigned long address, nr_pages = 1UL << order; struct vm_area_struct *vma = vmf ? vmf->vma : NULL; @@ -408,7 +418,7 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, /* Check if the slot and range are available, skip allocation if not */ spin_lock(&ci->lock); - err = __swap_cache_add_check(ci, targ_entry, nr_pages, NULL); + err = __swap_cache_add_check(ci, targ_entry, nr_pages, NULL, NULL); spin_unlock(&ci->lock); if (unlikely(err)) return ERR_PTR(err); @@ -431,7 +441,7 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, /* Double check the range is still not in conflict */ spin_lock(&ci->lock); - err = __swap_cache_add_check(ci, targ_entry, nr_pages, &shadow); + err = __swap_cache_add_check(ci, targ_entry, nr_pages, &shadow, &memcg_id); if (unlikely(err)) { spin_unlock(&ci->lock); folio_put(folio); @@ -443,8 +453,8 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, __swap_cache_do_add_folio(ci, folio, entry); spin_unlock(&ci->lock); - if (mem_cgroup_swapin_charge_folio(folio, vmf ? vmf->vma->vm_mm : NULL, - gfp, entry)) { + if (mem_cgroup_swapin_charge_folio(folio, memcg_id, + vmf ? vmf->vma->vm_mm : NULL, gfp)) { spin_lock(&ci->lock); __swap_cache_do_del_folio(ci, folio, entry, shadow); spin_unlock(&ci->lock); From cdd77f84d96675c9e8c776073df8d58d2af10607 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:48 +0800 Subject: [PATCH 2590/5214] mm, swap: consolidate cluster allocation helpers Swap cluster table management is spread across several narrow helpers. As a result, the allocation and fallback sequences are open-coded in multiple places. A few more per-cluster tables will be added soon, so avoid duplicating these sequences per table type. Fold the existing pairs into cluster-oriented helpers, and rename for consistency. No functional change, only a few sanity checks are slightly adjusted. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-9-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/swapfile.c | 110 ++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 60d8f0df3f32..2ddabc0f3a88 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -411,20 +411,7 @@ static inline unsigned int cluster_offset(struct swap_info_struct *si, return cluster_index(si, ci) * SWAPFILE_CLUSTER; } -static struct swap_table *swap_table_alloc(gfp_t gfp) -{ - struct folio *folio; - - if (!SWP_TABLE_USE_PAGE) - return kmem_cache_zalloc(swap_table_cachep, gfp); - - folio = folio_alloc(gfp | __GFP_ZERO, 0); - if (folio) - return folio_address(folio); - return NULL; -} - -static void swap_table_free_folio_rcu_cb(struct rcu_head *head) +static void swap_cluster_free_table_folio_rcu_cb(struct rcu_head *head) { struct folio *folio; @@ -432,15 +419,46 @@ static void swap_table_free_folio_rcu_cb(struct rcu_head *head) folio_put(folio); } -static void swap_table_free(struct swap_table *table) +static void swap_cluster_free_table(struct swap_cluster_info *ci) { + struct swap_table *table; + + table = (struct swap_table *)rcu_dereference_protected(ci->table, true); + if (!table) + return; + + rcu_assign_pointer(ci->table, NULL); if (!SWP_TABLE_USE_PAGE) { kmem_cache_free(swap_table_cachep, table); return; } call_rcu(&(folio_page(virt_to_folio(table), 0)->rcu_head), - swap_table_free_folio_rcu_cb); + swap_cluster_free_table_folio_rcu_cb); +} + +static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp) +{ + struct swap_table *table = NULL; + struct folio *folio; + + /* The cluster must be empty and not on any list during allocation. */ + VM_WARN_ON_ONCE(ci->flags || !cluster_is_empty(ci)); + if (rcu_access_pointer(ci->table)) + return 0; + + if (SWP_TABLE_USE_PAGE) { + folio = folio_alloc(gfp | __GFP_ZERO, 0); + if (folio) + table = folio_address(folio); + } else { + table = kmem_cache_zalloc(swap_table_cachep, gfp); + } + if (!table) + return -ENOMEM; + + rcu_assign_pointer(ci->table, table); + return 0; } /* @@ -471,27 +489,15 @@ static void swap_cluster_assert_empty(struct swap_cluster_info *ci, WARN_ON_ONCE(nr == SWAPFILE_CLUSTER && ci->extend_table); } -static void swap_cluster_free_table(struct swap_cluster_info *ci) -{ - struct swap_table *table; - - /* Only empty cluster's table is allow to be freed */ - lockdep_assert_held(&ci->lock); - table = (void *)rcu_dereference_protected(ci->table, true); - rcu_assign_pointer(ci->table, NULL); - - swap_table_free(table); -} - /* * Allocate swap table for one cluster. Attempt an atomic allocation first, * then fallback to sleeping allocation. */ static struct swap_cluster_info * -swap_cluster_alloc_table(struct swap_info_struct *si, +swap_cluster_populate(struct swap_info_struct *si, struct swap_cluster_info *ci) { - struct swap_table *table; + int ret; /* * Only cluster isolation from the allocator does table allocation. @@ -502,14 +508,9 @@ swap_cluster_alloc_table(struct swap_info_struct *si, lockdep_assert_held(&si->global_cluster_lock); lockdep_assert_held(&ci->lock); - /* The cluster must be free and was just isolated from the free list. */ - VM_WARN_ON_ONCE(ci->flags || !cluster_is_empty(ci)); - - table = swap_table_alloc(__GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN); - if (table) { - rcu_assign_pointer(ci->table, table); + if (!swap_cluster_alloc_table(ci, __GFP_HIGH | __GFP_NOMEMALLOC | + __GFP_NOWARN)) return ci; - } /* * Try a sleep allocation. Each isolated free cluster may cause @@ -521,7 +522,8 @@ swap_cluster_alloc_table(struct swap_info_struct *si, spin_unlock(&si->global_cluster_lock); local_unlock(&percpu_swap_cluster.lock); - table = swap_table_alloc(__GFP_HIGH | __GFP_NOMEMALLOC | GFP_KERNEL); + ret = swap_cluster_alloc_table(ci, __GFP_HIGH | __GFP_NOMEMALLOC | + GFP_KERNEL); /* * Back to atomic context. We might have migrated to a new CPU with a @@ -536,20 +538,11 @@ swap_cluster_alloc_table(struct swap_info_struct *si, spin_lock(&si->global_cluster_lock); spin_lock(&ci->lock); - /* Nothing except this helper should touch a dangling empty cluster. */ - if (WARN_ON_ONCE(cluster_table_is_alloced(ci))) { - if (table) - swap_table_free(table); - return ci; - } - - if (!table) { + if (ret) { move_cluster(si, ci, &si->free_clusters, CLUSTER_FLAG_FREE); spin_unlock(&ci->lock); return NULL; } - - rcu_assign_pointer(ci->table, table); return ci; } @@ -621,12 +614,11 @@ static struct swap_cluster_info *isolate_lock_cluster( } spin_unlock(&si->lock); - if (found && !cluster_table_is_alloced(found)) { - /* Only an empty free cluster's swap table can be freed. */ - VM_WARN_ON_ONCE(flags != CLUSTER_FLAG_FREE); + /* Cluster's table is freed when and only when it's on the free list. */ + if (found && flags == CLUSTER_FLAG_FREE) { VM_WARN_ON_ONCE(list != &si->free_clusters); - VM_WARN_ON_ONCE(!cluster_is_empty(found)); - return swap_cluster_alloc_table(si, found); + VM_WARN_ON_ONCE(cluster_table_is_alloced(found)); + return swap_cluster_populate(si, found); } return found; @@ -769,7 +761,6 @@ static int swap_cluster_setup_bad_slot(struct swap_info_struct *si, unsigned int ci_off = offset % SWAPFILE_CLUSTER; unsigned long idx = offset / SWAPFILE_CLUSTER; struct swap_cluster_info *ci; - struct swap_table *table; int ret = 0; /* si->max may got shrunk by swap swap_activate() */ @@ -790,12 +781,9 @@ static int swap_cluster_setup_bad_slot(struct swap_info_struct *si, } ci = cluster_info + idx; - if (!ci->table) { - table = swap_table_alloc(GFP_KERNEL); - if (!table) - return -ENOMEM; - rcu_assign_pointer(ci->table, table); - } + /* Need to allocate swap table first for initial bad slot marking. */ + if (!ci->count && swap_cluster_alloc_table(ci, GFP_KERNEL)) + return -ENOMEM; spin_lock(&ci->lock); /* Check for duplicated bad swap slots. */ if (__swap_table_xchg(ci, ci_off, SWP_TB_BAD) != SWP_TB_NULL) { @@ -2920,7 +2908,7 @@ static void free_swap_cluster_info(struct swap_cluster_info *cluster_info, ci = cluster_info + i; /* Cluster with bad marks count will have a remaining table */ spin_lock(&ci->lock); - if (rcu_dereference_protected(ci->table, true)) { + if (cluster_table_is_alloced(ci)) { swap_cluster_assert_empty(ci, 0, SWAPFILE_CLUSTER, true); swap_cluster_free_table(ci); } From b197d41462c2076bc88c79fead7f400e48881c19 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:49 +0800 Subject: [PATCH 2591/5214] mm/memcg, swap: store cgroup id in cluster table directly Drop the usage of the swap_cgroup_ctrl, and use the dynamic cluster table instead. The per-cluster memcg table is 1024 / 512 bytes on most archs, and does not need RCU protection: the cgroup data is only read and written under the cluster lock. That keeps things simple, lets the allocation use plain kmalloc with immediate kfree (no deferred free), and keeps fragmentation acceptable. [akpm@linux-foundation.org: memcgv1: don't compile swap functions when CONFIG_SWAP=n] Link: https://lore.kernel.org/202605281711.bSeZlErK-lkp@intel.com [akpm@linux-foundation.org: fix CONFIG_SWAP=n build] Link: https://lore.kernel.org/20260517-swap-table-p4-v5-10-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/memcontrol.h | 19 +++++++---- include/linux/swap.h | 8 ++--- mm/memcontrol-v1.c | 44 +++++++++++++++++--------- mm/memcontrol.c | 13 +++++--- mm/swap.h | 4 +++ mm/swap_state.c | 6 ++-- mm/swap_table.h | 64 ++++++++++++++++++++++++++++++++++++++ mm/swapfile.c | 37 +++++++++++++++------- mm/vmscan.c | 2 +- 9 files changed, 150 insertions(+), 47 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index a013f37f24aa..8f2662db166b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -29,6 +29,7 @@ struct obj_cgroup; struct page; struct mm_struct; struct kmem_cache; +struct swap_cluster_info; /* Cgroup-specific page state, on top of universal node page state */ enum memcg_stat_item { @@ -1899,9 +1900,6 @@ static inline void mem_cgroup_exit_user_fault(void) current->in_user_fault = 0; } -void __memcg1_swapout(struct folio *folio); -void memcg1_swapin(struct folio *folio); - #else /* CONFIG_MEMCG_V1 */ static inline unsigned long memcg1_soft_limit_reclaim(pg_data_t *pgdat, int order, @@ -1929,14 +1927,23 @@ static inline void mem_cgroup_exit_user_fault(void) { } -static inline void __memcg1_swapout(struct folio *folio) +#endif /* CONFIG_MEMCG_V1 */ + +#if defined(CONFIG_MEMCG_V1) && defined(CONFIG_SWAP) + +void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci); +void memcg1_swapin(struct folio *folio); + +#else + +static inline void __memcg1_swapout(struct folio *folio, + struct swap_cluster_info *ci) { } static inline void memcg1_swapin(struct folio *folio) { } - -#endif /* CONFIG_MEMCG_V1 */ +#endif #endif /* _LINUX_MEMCONTROL_H */ diff --git a/include/linux/swap.h b/include/linux/swap.h index f907d3df52d0..200e7c345f26 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -579,12 +579,12 @@ static inline int mem_cgroup_try_charge_swap(struct folio *folio) return __mem_cgroup_try_charge_swap(folio); } -extern void __mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages); -static inline void mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages) +extern void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages); +static inline void mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages) { if (mem_cgroup_disabled()) return; - __mem_cgroup_uncharge_swap(entry, nr_pages); + __mem_cgroup_uncharge_swap(id, nr_pages); } extern long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg); @@ -595,7 +595,7 @@ static inline int mem_cgroup_try_charge_swap(struct folio *folio) return 0; } -static inline void mem_cgroup_uncharge_swap(swp_entry_t entry, +static inline void mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages) { } diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 36c507d81dc5..517b21236672 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -14,6 +14,7 @@ #include "internal.h" #include "swap.h" +#include "swap_table.h" #include "memcontrol-v1.h" /* @@ -603,17 +604,19 @@ void memcg1_commit_charge(struct folio *folio, struct mem_cgroup *memcg) local_irq_restore(flags); } +#ifdef CONFIG_SWAP /** * __memcg1_swapout - transfer a memsw charge to swap * @folio: folio whose memsw charge to transfer + * @ci: the locked swap cluster holding the swap entries * * Transfer the memsw charge of @folio to the swap entry stored in * folio->swap. * - * Context: folio must be isolated, unmapped, locked and is just about - * to be freed, and caller must disable IRQs. + * Context: folio must be isolated, unmapped, locked and is just about to + * be freed, and caller must disable IRQs and hold the swap cluster lock. */ -void __memcg1_swapout(struct folio *folio) +void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci) { struct mem_cgroup *memcg, *swap_memcg; struct obj_cgroup *objcg; @@ -646,7 +649,8 @@ void __memcg1_swapout(struct folio *folio) swap_memcg = mem_cgroup_private_id_get_online(memcg, nr_entries); mod_memcg_state(swap_memcg, MEMCG_SWAP, nr_entries); - swap_cgroup_record(folio, mem_cgroup_private_id(swap_memcg), folio->swap); + __swap_cgroup_set(ci, swp_cluster_offset(folio->swap), nr_entries, + mem_cgroup_private_id(swap_memcg)); folio_unqueue_deferred_split(folio); folio->memcg_data = 0; @@ -661,8 +665,7 @@ void __memcg1_swapout(struct folio *folio) } /* - * Interrupts should be disabled here because the caller holds the - * i_pages lock which is taken with interrupts-off. It is + * The caller must hold the swap cluster lock with IRQ off. It is * important here to have the interrupts disabled because it is the * only synchronisation we have for updating the per-CPU variables. */ @@ -677,7 +680,7 @@ void __memcg1_swapout(struct folio *folio) } /** - * memcg1_swapin - uncharge swap slot + * memcg1_swapin - uncharge swap slot on swapin * @folio: folio being swapped in * * Call this function after successfully adding the charged @@ -687,6 +690,10 @@ void __memcg1_swapout(struct folio *folio) */ void memcg1_swapin(struct folio *folio) { + struct swap_cluster_info *ci; + unsigned long nr_pages; + unsigned short id; + VM_WARN_ON_ONCE_FOLIO(!folio_test_swapcache(folio), folio); VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); @@ -702,15 +709,22 @@ void memcg1_swapin(struct folio *folio) * correspond 1:1 to page and swap slot lifetimes: we charge the * page to memory here, and uncharge swap when the slot is freed. */ - if (do_memsw_account()) { - /* - * The swap entry might not get freed for a long time, - * let's not wait for it. The page already received a - * memory+swap charge, drop the swap entry duplicate. - */ - mem_cgroup_uncharge_swap(folio->swap, folio_nr_pages(folio)); - } + if (!do_memsw_account()) + return; + + /* + * The swap entry might not get freed for a long time, + * let's not wait for it. The page already received a + * memory+swap charge, drop the swap entry duplicate. + */ + nr_pages = folio_nr_pages(folio); + ci = swap_cluster_get_and_lock(folio); + id = __swap_cgroup_clear(ci, swp_cluster_offset(folio->swap), + nr_pages); + swap_cluster_unlock(ci); + mem_cgroup_uncharge_swap(id, nr_pages); } +#endif void memcg1_uncharge_batch(struct mem_cgroup *memcg, unsigned long pgpgout, unsigned long nr_memory, int nid) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 1b58b314cb18..beecfc6f376d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -64,6 +64,7 @@ #include #include #include "internal.h" +#include "swap_table.h" #include #include #include "slab.h" @@ -5479,6 +5480,7 @@ int __init mem_cgroup_init(void) int __mem_cgroup_try_charge_swap(struct folio *folio) { unsigned int nr_pages = folio_nr_pages(folio); + struct swap_cluster_info *ci; struct page_counter *counter; struct mem_cgroup *memcg; struct obj_cgroup *objcg; @@ -5512,22 +5514,23 @@ int __mem_cgroup_try_charge_swap(struct folio *folio) } mod_memcg_state(memcg, MEMCG_SWAP, nr_pages); - swap_cgroup_record(folio, mem_cgroup_private_id(memcg), folio->swap); + ci = swap_cluster_get_and_lock(folio); + __swap_cgroup_set(ci, swp_cluster_offset(folio->swap), nr_pages, + mem_cgroup_private_id(memcg)); + swap_cluster_unlock(ci); return 0; } /** * __mem_cgroup_uncharge_swap - uncharge swap space - * @entry: swap entry to uncharge + * @id: cgroup id to uncharge * @nr_pages: the amount of swap space to uncharge */ -void __mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages) +void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages) { struct mem_cgroup *memcg; - unsigned short id; - id = swap_cgroup_clear(entry, nr_pages); rcu_read_lock(); memcg = mem_cgroup_from_private_id(id); if (memcg) { diff --git a/mm/swap.h b/mm/swap.h index 8e57e9431624..5b2f095fff6e 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -5,6 +5,7 @@ #include /* for atomic_long_t */ struct mempolicy; struct swap_iocb; +struct swap_memcg_table; extern int page_cluster; @@ -38,6 +39,9 @@ struct swap_cluster_info { u8 order; atomic_long_t __rcu *table; /* Swap table entries, see mm/swap_table.h */ unsigned int *extend_table; /* For large swap count, protected by ci->lock */ +#ifdef CONFIG_MEMCG + struct swap_memcg_table *memcg_table; /* Swap table entries' cgroup record */ +#endif struct list_head list; }; diff --git a/mm/swap_state.c b/mm/swap_state.c index bdd949ae0044..873cb3f26337 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -179,21 +179,19 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, if (shadowp && swp_tb_is_shadow(old_tb)) *shadowp = swp_tb_to_shadow(old_tb); if (memcg_id) - *memcg_id = lookup_swap_cgroup_id(targ_entry); + *memcg_id = __swap_cgroup_get(ci, ci_off); if (nr == 1) return 0; - targ_entry.val = round_down(targ_entry.val, nr); ci_off = round_down(ci_off, nr); ci_end = ci_off + nr; do { old_tb = __swap_table_get(ci, ci_off); if (unlikely(swp_tb_is_folio(old_tb) || !__swp_tb_get_count(old_tb) || - (memcg_id && *memcg_id != lookup_swap_cgroup_id(targ_entry)))) + (memcg_id && *memcg_id != __swap_cgroup_get(ci, ci_off)))) return -EBUSY; - targ_entry.val++; } while (++ci_off < ci_end); return 0; diff --git a/mm/swap_table.h b/mm/swap_table.h index 8415ffbe2b9c..b4e1100f8296 100644 --- a/mm/swap_table.h +++ b/mm/swap_table.h @@ -11,6 +11,11 @@ struct swap_table { atomic_long_t entries[SWAPFILE_CLUSTER]; }; +/* For storing memcg private id */ +struct swap_memcg_table { + unsigned short id[SWAPFILE_CLUSTER]; +}; + #define SWP_TABLE_USE_PAGE (sizeof(struct swap_table) == PAGE_SIZE) /* @@ -247,4 +252,63 @@ static inline unsigned long swap_table_get(struct swap_cluster_info *ci, return swp_tb; } + +#ifdef CONFIG_MEMCG +static inline void __swap_cgroup_set(struct swap_cluster_info *ci, + unsigned int ci_off, unsigned long nr, unsigned short id) +{ + lockdep_assert_held(&ci->lock); + VM_WARN_ON_ONCE(ci_off >= SWAPFILE_CLUSTER); + if (WARN_ON_ONCE(!ci->memcg_table)) + return; + do { + ci->memcg_table->id[ci_off++] = id; + } while (--nr); +} + +static inline unsigned short __swap_cgroup_get(struct swap_cluster_info *ci, + unsigned int ci_off) +{ + lockdep_assert_held(&ci->lock); + VM_WARN_ON_ONCE(ci_off >= SWAPFILE_CLUSTER); + if (unlikely(!ci->memcg_table)) + return 0; + return ci->memcg_table->id[ci_off]; +} + +static inline unsigned short __swap_cgroup_clear(struct swap_cluster_info *ci, + unsigned int ci_off, + unsigned long nr) +{ + unsigned short old = __swap_cgroup_get(ci, ci_off); + + if (!old) + return 0; + do { + VM_WARN_ON_ONCE(ci->memcg_table->id[ci_off] != old); + ci->memcg_table->id[ci_off++] = 0; + } while (--nr); + + return old; +} +#else +static inline void __swap_cgroup_set(struct swap_cluster_info *ci, + unsigned int ci_off, unsigned long nr, unsigned short id) +{ +} + +static inline unsigned short __swap_cgroup_get(struct swap_cluster_info *ci, + unsigned int ci_off) +{ + return 0; +} + +static inline unsigned short __swap_cgroup_clear(struct swap_cluster_info *ci, + unsigned int ci_off, + unsigned long nr) +{ + return 0; +} +#endif + #endif diff --git a/mm/swapfile.c b/mm/swapfile.c index 2ddabc0f3a88..bd141eb9ef10 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -423,7 +423,12 @@ static void swap_cluster_free_table(struct swap_cluster_info *ci) { struct swap_table *table; - table = (struct swap_table *)rcu_dereference_protected(ci->table, true); +#ifdef CONFIG_MEMCG + kfree(ci->memcg_table); + ci->memcg_table = NULL; +#endif + + table = (struct swap_table *)rcu_access_pointer(ci->table); if (!table) return; @@ -441,6 +446,7 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp) { struct swap_table *table = NULL; struct folio *folio; + int ret = 0; /* The cluster must be empty and not on any list during allocation. */ VM_WARN_ON_ONCE(ci->flags || !cluster_is_empty(ci)); @@ -458,7 +464,19 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp) return -ENOMEM; rcu_assign_pointer(ci->table, table); - return 0; + +#ifdef CONFIG_MEMCG + if (!mem_cgroup_disabled()) { + VM_WARN_ON_ONCE(ci->memcg_table); + ci->memcg_table = kzalloc_obj(*ci->memcg_table, gfp); + if (!ci->memcg_table) + ret = -ENOMEM; + } +#endif + if (ret) + swap_cluster_free_table(ci); + + return ret; } /* @@ -483,6 +501,7 @@ static void swap_cluster_assert_empty(struct swap_cluster_info *ci, bad_slots++; else WARN_ON_ONCE(!swp_tb_is_null(swp_tb)); + WARN_ON_ONCE(__swap_cgroup_get(ci, ci_off)); } while (++ci_off < ci_end); WARN_ON_ONCE(bad_slots != (swapoff ? ci->count : 0)); @@ -1887,12 +1906,10 @@ void __swap_cluster_free_entries(struct swap_info_struct *si, unsigned int ci_start, unsigned int nr_pages) { unsigned long old_tb; - unsigned int type = si->type; unsigned short batch_id = 0, id_cur; unsigned int ci_off = ci_start, ci_end = ci_start + nr_pages; unsigned long ci_head = cluster_offset(si, ci); unsigned int batch_off = ci_off; - swp_entry_t entry; VM_WARN_ON(ci->count < nr_pages); @@ -1910,21 +1927,17 @@ void __swap_cluster_free_entries(struct swap_info_struct *si, * Uncharge swap slots by memcg in batches. Consecutive * slots with the same cgroup id are uncharged together. */ - entry = swp_entry(type, ci_head + ci_off); - id_cur = lookup_swap_cgroup_id(entry); + id_cur = __swap_cgroup_clear(ci, ci_off, 1); if (batch_id != id_cur) { if (batch_id) - mem_cgroup_uncharge_swap(swp_entry(type, ci_head + batch_off), - ci_off - batch_off); + mem_cgroup_uncharge_swap(batch_id, ci_off - batch_off); batch_id = id_cur; batch_off = ci_off; } } while (++ci_off < ci_end); - if (batch_id) { - mem_cgroup_uncharge_swap(swp_entry(type, ci_head + batch_off), - ci_off - batch_off); - } + if (batch_id) + mem_cgroup_uncharge_swap(batch_id, ci_off - batch_off); swap_range_free(si, ci_head + ci_start, nr_pages); swap_cluster_assert_empty(ci, ci_start, nr_pages, false); diff --git a/mm/vmscan.c b/mm/vmscan.c index 3231af682fa7..3c856a78c0a5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -739,7 +739,7 @@ static int __remove_mapping(struct address_space *mapping, struct folio *folio, if (reclaimed && !mapping_exiting(mapping)) shadow = workingset_eviction(folio, target_memcg); - __memcg1_swapout(folio); + __memcg1_swapout(folio, ci); __swap_cache_del_folio(ci, folio, swap, shadow); swap_cluster_unlock_irq(ci); } else { From 4e8e1c498de9f97628207d1ef84506058b06bb51 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:50 +0800 Subject: [PATCH 2592/5214] mm/memcg: remove no longer used swap cgroup array Now all swap cgroup records are stored in the swap cluster directly, the static array is no longer needed. Link: https://lore.kernel.org/20260517-swap-table-p4-v5-11-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Youngjun Park Cc: Zi Yan Signed-off-by: Andrew Morton --- MAINTAINERS | 1 - include/linux/swap_cgroup.h | 47 ---------- mm/Makefile | 3 - mm/internal.h | 1 - mm/memcontrol-v1.c | 1 - mm/memcontrol.c | 1 - mm/swap_cgroup.c | 174 ------------------------------------ mm/swapfile.c | 8 -- 8 files changed, 236 deletions(-) delete mode 100644 include/linux/swap_cgroup.h delete mode 100644 mm/swap_cgroup.c diff --git a/MAINTAINERS b/MAINTAINERS index 461a3eed6129..782ed63e4e67 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6573,7 +6573,6 @@ F: mm/memcontrol.c F: mm/memcontrol-v1.c F: mm/memcontrol-v1.h F: mm/page_counter.c -F: mm/swap_cgroup.c F: samples/cgroup/* F: tools/testing/selftests/cgroup/memcg_protection.m F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c diff --git a/include/linux/swap_cgroup.h b/include/linux/swap_cgroup.h deleted file mode 100644 index 91cdf12190a0..000000000000 --- a/include/linux/swap_cgroup.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_SWAP_CGROUP_H -#define __LINUX_SWAP_CGROUP_H - -#include - -#if defined(CONFIG_MEMCG) && defined(CONFIG_SWAP) - -extern void swap_cgroup_record(struct folio *folio, unsigned short id, swp_entry_t ent); -extern unsigned short swap_cgroup_clear(swp_entry_t ent, unsigned int nr_ents); -extern unsigned short lookup_swap_cgroup_id(swp_entry_t ent); -extern int swap_cgroup_swapon(int type, unsigned long max_pages); -extern void swap_cgroup_swapoff(int type); - -#else - -static inline -void swap_cgroup_record(struct folio *folio, unsigned short id, swp_entry_t ent) -{ -} - -static inline -unsigned short swap_cgroup_clear(swp_entry_t ent, unsigned int nr_ents) -{ - return 0; -} - -static inline -unsigned short lookup_swap_cgroup_id(swp_entry_t ent) -{ - return 0; -} - -static inline int -swap_cgroup_swapon(int type, unsigned long max_pages) -{ - return 0; -} - -static inline void swap_cgroup_swapoff(int type) -{ - return; -} - -#endif - -#endif /* __LINUX_SWAP_CGROUP_H */ diff --git a/mm/Makefile b/mm/Makefile index 8ad2ab08244e..eff9f9e7e061 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -103,9 +103,6 @@ obj-$(CONFIG_PAGE_COUNTER) += page_counter.o obj-$(CONFIG_LIVEUPDATE_MEMFD) += memfd_luo.o obj-$(CONFIG_MEMCG_V1) += memcontrol-v1.o obj-$(CONFIG_MEMCG) += memcontrol.o vmpressure.o -ifdef CONFIG_SWAP -obj-$(CONFIG_MEMCG) += swap_cgroup.o -endif ifdef CONFIG_BPF_SYSCALL obj-$(CONFIG_MEMCG) += bpf_memcontrol.o endif diff --git a/mm/internal.h b/mm/internal.h index 9dbd8e3c991f..5602393054f3 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -17,7 +17,6 @@ #include #include #include -#include #include /* Internal core VMA manipulation functions. */ diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 517b21236672..765069211567 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/mm/memcontrol.c b/mm/memcontrol.c index beecfc6f376d..92269740eef1 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include diff --git a/mm/swap_cgroup.c b/mm/swap_cgroup.c deleted file mode 100644 index 95c38e54dd58..000000000000 --- a/mm/swap_cgroup.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -#include /* depends on mm.h include */ - -static DEFINE_MUTEX(swap_cgroup_mutex); - -/* Pack two cgroup id (short) of two entries in one swap_cgroup (atomic_t) */ -#define ID_PER_SC (sizeof(struct swap_cgroup) / sizeof(unsigned short)) -#define ID_SHIFT (BITS_PER_TYPE(unsigned short)) -#define ID_MASK (BIT(ID_SHIFT) - 1) -struct swap_cgroup { - atomic_t ids; -}; - -struct swap_cgroup_ctrl { - struct swap_cgroup *map; -}; - -static struct swap_cgroup_ctrl swap_cgroup_ctrl[MAX_SWAPFILES]; - -static unsigned short __swap_cgroup_id_lookup(struct swap_cgroup *map, - pgoff_t offset) -{ - unsigned int shift = (offset % ID_PER_SC) * ID_SHIFT; - unsigned int old_ids = atomic_read(&map[offset / ID_PER_SC].ids); - - BUILD_BUG_ON(!is_power_of_2(ID_PER_SC)); - BUILD_BUG_ON(sizeof(struct swap_cgroup) != sizeof(atomic_t)); - - return (old_ids >> shift) & ID_MASK; -} - -static unsigned short __swap_cgroup_id_xchg(struct swap_cgroup *map, - pgoff_t offset, - unsigned short new_id) -{ - unsigned short old_id; - struct swap_cgroup *sc = &map[offset / ID_PER_SC]; - unsigned int shift = (offset % ID_PER_SC) * ID_SHIFT; - unsigned int new_ids, old_ids = atomic_read(&sc->ids); - - do { - old_id = (old_ids >> shift) & ID_MASK; - new_ids = (old_ids & ~(ID_MASK << shift)); - new_ids |= ((unsigned int)new_id) << shift; - } while (!atomic_try_cmpxchg(&sc->ids, &old_ids, new_ids)); - - return old_id; -} - -/** - * swap_cgroup_record - record mem_cgroup for a set of swap entries. - * These entries must belong to one single folio, and that folio - * must be being charged for swap space (swap out), and these - * entries must not have been charged - * - * @folio: the folio that the swap entry belongs to - * @id: mem_cgroup ID to be recorded - * @ent: the first swap entry to be recorded - */ -void swap_cgroup_record(struct folio *folio, unsigned short id, - swp_entry_t ent) -{ - unsigned int nr_ents = folio_nr_pages(folio); - struct swap_cgroup *map; - pgoff_t offset, end; - unsigned short old; - - offset = swp_offset(ent); - end = offset + nr_ents; - map = swap_cgroup_ctrl[swp_type(ent)].map; - - do { - old = __swap_cgroup_id_xchg(map, offset, id); - VM_BUG_ON(old); - } while (++offset != end); -} - -/** - * swap_cgroup_clear - clear mem_cgroup for a set of swap entries. - * These entries must be being uncharged from swap. They either - * belongs to one single folio in the swap cache (swap in for - * cgroup v1), or no longer have any users (slot freeing). - * - * @ent: the first swap entry to be recorded into - * @nr_ents: number of swap entries to be recorded - * - * Returns the existing old value. - */ -unsigned short swap_cgroup_clear(swp_entry_t ent, unsigned int nr_ents) -{ - pgoff_t offset, end; - struct swap_cgroup *map; - unsigned short old, iter = 0; - - offset = swp_offset(ent); - end = offset + nr_ents; - map = swap_cgroup_ctrl[swp_type(ent)].map; - - do { - old = __swap_cgroup_id_xchg(map, offset, 0); - if (!iter) - iter = old; - VM_BUG_ON(iter != old); - } while (++offset != end); - - return old; -} - -/** - * lookup_swap_cgroup_id - lookup mem_cgroup id tied to swap entry - * @ent: swap entry to be looked up. - * - * Returns ID of mem_cgroup at success. 0 at failure. (0 is invalid ID) - */ -unsigned short lookup_swap_cgroup_id(swp_entry_t ent) -{ - struct swap_cgroup_ctrl *ctrl; - - if (mem_cgroup_disabled()) - return 0; - - ctrl = &swap_cgroup_ctrl[swp_type(ent)]; - if (unlikely(!ctrl->map)) - return 0; - return __swap_cgroup_id_lookup(ctrl->map, swp_offset(ent)); -} - -int swap_cgroup_swapon(int type, unsigned long max_pages) -{ - struct swap_cgroup *map; - struct swap_cgroup_ctrl *ctrl; - - if (mem_cgroup_disabled()) - return 0; - - BUILD_BUG_ON(sizeof(unsigned short) * ID_PER_SC != - sizeof(struct swap_cgroup)); - map = vzalloc(DIV_ROUND_UP(max_pages, ID_PER_SC) * - sizeof(struct swap_cgroup)); - if (!map) - goto nomem; - - ctrl = &swap_cgroup_ctrl[type]; - mutex_lock(&swap_cgroup_mutex); - ctrl->map = map; - mutex_unlock(&swap_cgroup_mutex); - - return 0; -nomem: - pr_info("couldn't allocate enough memory for swap_cgroup\n"); - pr_info("swap_cgroup can be disabled by swapaccount=0 boot option\n"); - return -ENOMEM; -} - -void swap_cgroup_swapoff(int type) -{ - struct swap_cgroup *map; - struct swap_cgroup_ctrl *ctrl; - - if (mem_cgroup_disabled()) - return; - - mutex_lock(&swap_cgroup_mutex); - ctrl = &swap_cgroup_ctrl[type]; - map = ctrl->map; - ctrl->map = NULL; - mutex_unlock(&swap_cgroup_mutex); - - vfree(map); -} diff --git a/mm/swapfile.c b/mm/swapfile.c index bd141eb9ef10..992e77b7105d 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -45,7 +45,6 @@ #include #include -#include #include "swap_table.h" #include "internal.h" #include "swap.h" @@ -3058,8 +3057,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) p->global_cluster = NULL; kvfree(zeromap); free_swap_cluster_info(cluster_info, maxpages); - /* Destroy swap account information */ - swap_cgroup_swapoff(p->type); inode = mapping->host; @@ -3590,10 +3587,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) if (error) goto bad_swap_unlock_inode; - error = swap_cgroup_swapon(si->type, maxpages); - if (error) - goto bad_swap_unlock_inode; - /* * Use kvmalloc_array instead of bitmap_zalloc as the allocation order might * be above MAX_PAGE_ORDER incase of a large swap file. @@ -3704,7 +3697,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) si->global_cluster = NULL; inode = NULL; destroy_swap_extents(si, swap_file); - swap_cgroup_swapoff(si->type); free_swap_cluster_info(si->cluster_info, si->max); si->cluster_info = NULL; kvfree(si->zeromap); From d9ceded101a142cd56f1e88fc7e893560ee59f4d Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Sun, 17 May 2026 23:39:51 +0800 Subject: [PATCH 2593/5214] mm, swap: merge zeromap into swap table By allocating one additional bit in the swap table entry's flags field alongside the count, we can store the zeromap inline For 64 bit systems, zeromap will store in the swap table, avoiding zeromap allocation. It reduces the allocated memory. That is the happy path. For certain 32-bit archs, there might not be enough bits in the swap table to contain both PFN and flags. Therefore, conditionally let each cluster have a zeromap field at build time, and use that instead. If the swapfile cluster is not fully used, it will still save memory for zeromap. The empty cluster does not allocate a zeromap. In the worst case, all cluster are fully populated. We will use memory similar to the previous zeromap implementation. A few macros were moved to different headers for build time struct definition. [akpm@linux-foundation.org: swap_cluster_alloc_table(): remove unused local `ret] [akpm@linux-foundation.org: fix unused label `err_free'] Link: https://lore.kernel.org/20260517-swap-table-p4-v5-12-88ae43e064c7@tencent.com Signed-off-by: Kairui Song Acked-by: Chris Li Reviewed-by: Youngjun Park Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Chengming Zhou Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Muchun Song Cc: Nhat Pham Cc: Roman Gushchin Cc: Shakeel Butt Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/swap.h | 1 - mm/memory.c | 11 +---- mm/page_io.c | 61 +++++++++++++++++++---- mm/swap.h | 50 ++++++++----------- mm/swap_state.c | 14 +++--- mm/swap_table.h | 115 ++++++++++++++++++++++++++++++++----------- mm/swapfile.c | 57 ++++++++++----------- 7 files changed, 192 insertions(+), 117 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 200e7c345f26..8c43bc3055c9 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -252,7 +252,6 @@ struct swap_info_struct { struct plist_node list; /* entry in swap_active_head */ signed char type; /* strange name for an index */ unsigned int max; /* size of this swap device */ - unsigned long *zeromap; /* kvmalloc'ed bitmap to track zero pages */ struct swap_cluster_info *cluster_info; /* cluster info. Only for SSD */ struct list_head free_clusters; /* free clusters list */ struct list_head full_clusters; /* full clusters list */ diff --git a/mm/memory.c b/mm/memory.c index da891bcce59c..7c020995eafc 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4611,13 +4611,11 @@ static vm_fault_t handle_pte_marker(struct vm_fault *vmf) #ifdef CONFIG_TRANSPARENT_HUGEPAGE /* - * Check if the PTEs within a range are contiguous swap entries - * and have consistent swapcache, zeromap. + * Check if the PTEs within a range are contiguous swap entries. */ static bool can_swapin_thp(struct vm_fault *vmf, pte_t *ptep, int nr_pages) { unsigned long addr; - softleaf_t entry; int idx; pte_t pte; @@ -4627,18 +4625,13 @@ static bool can_swapin_thp(struct vm_fault *vmf, pte_t *ptep, int nr_pages) if (!pte_same(pte, pte_move_swp_offset(vmf->orig_pte, -idx))) return false; - entry = softleaf_from_pte(pte); - if (swap_pte_batch(ptep, nr_pages, pte) != nr_pages) - return false; - /* * swap_read_folio() can't handle the case a large folio is hybridly * from different backends. And they are likely corner cases. Similar * things might be added once zswap support large folios. */ - if (unlikely(swap_zeromap_batch(entry, nr_pages, NULL) != nr_pages)) + if (swap_pte_batch(ptep, nr_pages, pte) != nr_pages) return false; - return true; } diff --git a/mm/page_io.c b/mm/page_io.c index 7ed76592e20d..f2d8fe7fd057 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -26,6 +26,7 @@ #include #include #include "swap.h" +#include "swap_table.h" static void __end_swap_bio_write(struct bio *bio) { @@ -204,15 +205,20 @@ static bool is_folio_zero_filled(struct folio *folio) static void swap_zeromap_folio_set(struct folio *folio) { struct obj_cgroup *objcg = get_obj_cgroup_from_folio(folio); - struct swap_info_struct *sis = __swap_entry_to_info(folio->swap); int nr_pages = folio_nr_pages(folio); + struct swap_cluster_info *ci; swp_entry_t entry; unsigned int i; + VM_WARN_ON_ONCE_FOLIO(!folio_test_swapcache(folio), folio); + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); + + ci = swap_cluster_get_and_lock(folio); for (i = 0; i < folio_nr_pages(folio); i++) { entry = page_swap_entry(folio_page(folio, i)); - set_bit(swp_offset(entry), sis->zeromap); + __swap_table_set_zero(ci, swp_cluster_offset(entry)); } + swap_cluster_unlock(ci); count_vm_events(SWPOUT_ZERO, nr_pages); if (objcg) { @@ -223,14 +229,19 @@ static void swap_zeromap_folio_set(struct folio *folio) static void swap_zeromap_folio_clear(struct folio *folio) { - struct swap_info_struct *sis = __swap_entry_to_info(folio->swap); + struct swap_cluster_info *ci; swp_entry_t entry; unsigned int i; + VM_WARN_ON_ONCE_FOLIO(!folio_test_swapcache(folio), folio); + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); + + ci = swap_cluster_get_and_lock(folio); for (i = 0; i < folio_nr_pages(folio); i++) { entry = page_swap_entry(folio_page(folio, i)); - clear_bit(swp_offset(entry), sis->zeromap); + __swap_table_clear_zero(ci, swp_cluster_offset(entry)); } + swap_cluster_unlock(ci); } /* @@ -255,10 +266,9 @@ int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug) } /* - * Use a bitmap (zeromap) to avoid doing IO for zero-filled pages. - * The bits in zeromap are protected by the locked swapcache folio - * and atomic updates are used to protect against read-modify-write - * corruption due to other zero swap entries seeing concurrent updates. + * Use the swap table zero mark to avoid doing IO for zero-filled + * pages. The zero mark is protected by the cluster lock, which is + * acquired internally by swap_zeromap_folio_set/clear. */ if (is_folio_zero_filled(folio)) { swap_zeromap_folio_set(folio); @@ -509,19 +519,52 @@ static void sio_read_complete(struct kiocb *iocb, long ret) mempool_free(sio, sio_pool); } +/* + * Return the count of contiguous swap entries that share the same + * zeromap status as the starting entry. If is_zerop is not NULL, + * it will return the zeromap status of the starting entry. + * + * Context: Caller must ensure the cluster containing the entries + * that will be checked won't be freed. + */ +static int swap_zeromap_batch(swp_entry_t entry, int max_nr, + bool *is_zerop) +{ + int i; + bool is_zero; + unsigned int ci_start = swp_cluster_offset(entry); + struct swap_cluster_info *ci = __swap_entry_to_cluster(entry); + + VM_WARN_ON_ONCE(ci_start + max_nr > SWAPFILE_CLUSTER); + + rcu_read_lock(); + is_zero = __swap_table_test_zero(ci, ci_start); + for (i = 1; i < max_nr; i++) + if (is_zero != __swap_table_test_zero(ci, ci_start + i)) + break; + rcu_read_unlock(); + if (is_zerop) + *is_zerop = is_zero; + + return i; +} + static bool swap_read_folio_zeromap(struct folio *folio) { int nr_pages = folio_nr_pages(folio); struct obj_cgroup *objcg; bool is_zeromap; + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); + /* * Swapping in a large folio that is partially in the zeromap is not * currently handled. Return true without marking the folio uptodate so * that an IO error is emitted (e.g. do_swap_page() will sigbus). + * Folio lock stabilizes the cluster and map, so the check is safe. */ if (WARN_ON_ONCE(swap_zeromap_batch(folio->swap, nr_pages, - &is_zeromap) != nr_pages)) + &is_zeromap) != nr_pages)) return true; if (!is_zeromap) diff --git a/mm/swap.h b/mm/swap.h index 5b2f095fff6e..77d2d14eda42 100644 --- a/mm/swap.h +++ b/mm/swap.h @@ -3,12 +3,29 @@ #define _MM_SWAP_H #include /* for atomic_long_t */ +#include /* for PAGE_SHIFT */ struct mempolicy; struct swap_iocb; struct swap_memcg_table; extern int page_cluster; +#if defined(MAX_POSSIBLE_PHYSMEM_BITS) +#define SWAP_CACHE_PFN_BITS (MAX_POSSIBLE_PHYSMEM_BITS - PAGE_SHIFT) +#elif defined(MAX_PHYSMEM_BITS) +#define SWAP_CACHE_PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) +#else +#define SWAP_CACHE_PFN_BITS (BITS_PER_LONG - PAGE_SHIFT) +#endif + +/* Swap table marker, 0x1 means shadow, 0x2 means PFN (SWP_TB_PFN_MARK) */ +#define SWAP_CACHE_PFN_MARK_BITS 2 +/* At least 2 bits are needed to distinguish SWP_TB_COUNT_MAX, 1 and 0 */ +#define SWAP_COUNT_MIN_BITS 2 +/* If there are enough bits besides PFN and marker, store zero flag inline */ +#define SWAP_TABLE_HAS_ZEROFLAG ((BITS_PER_LONG - SWAP_CACHE_PFN_MARK_BITS - \ + SWAP_CACHE_PFN_BITS) > SWAP_COUNT_MIN_BITS) + #ifdef CONFIG_THP_SWAP #define SWAPFILE_CLUSTER HPAGE_PMD_NR #define swap_entry_order(order) (order) @@ -41,6 +58,9 @@ struct swap_cluster_info { unsigned int *extend_table; /* For large swap count, protected by ci->lock */ #ifdef CONFIG_MEMCG struct swap_memcg_table *memcg_table; /* Swap table entries' cgroup record */ +#endif +#if !SWAP_TABLE_HAS_ZEROFLAG + unsigned long *zero_bitmap; #endif struct list_head list; }; @@ -314,31 +334,6 @@ static inline unsigned int folio_swap_flags(struct folio *folio) return __swap_entry_to_info(folio->swap)->flags; } -/* - * Return the count of contiguous swap entries that share the same - * zeromap status as the starting entry. If is_zeromap is not NULL, - * it will return the zeromap status of the starting entry. - */ -static inline int swap_zeromap_batch(swp_entry_t entry, int max_nr, - bool *is_zeromap) -{ - struct swap_info_struct *sis = __swap_entry_to_info(entry); - unsigned long start = swp_offset(entry); - unsigned long end = start + max_nr; - bool first_bit; - - first_bit = test_bit(start, sis->zeromap); - if (is_zeromap) - *is_zeromap = first_bit; - - if (max_nr <= 1) - return max_nr; - if (first_bit) - return find_next_zero_bit(sis->zeromap, end, start) - start; - else - return find_next_bit(sis->zeromap, end, start) - start; -} - #else /* CONFIG_SWAP */ struct swap_iocb; static inline struct swap_cluster_info *swap_cluster_lock( @@ -477,10 +472,5 @@ static inline unsigned int folio_swap_flags(struct folio *folio) return 0; } -static inline int swap_zeromap_batch(swp_entry_t entry, int max_nr, - bool *has_zeromap) -{ - return 0; -} #endif /* CONFIG_SWAP */ #endif /* _MM_SWAP_H */ diff --git a/mm/swap_state.c b/mm/swap_state.c index 873cb3f26337..04f5ce992401 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -160,6 +160,7 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, { unsigned int ci_off, ci_end; unsigned long old_tb; + bool is_zero; lockdep_assert_held(&ci->lock); @@ -184,12 +185,14 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci, if (nr == 1) return 0; + is_zero = __swap_table_test_zero(ci, ci_off); ci_off = round_down(ci_off, nr); ci_end = ci_off + nr; do { old_tb = __swap_table_get(ci, ci_off); if (unlikely(swp_tb_is_folio(old_tb) || !__swp_tb_get_count(old_tb) || + is_zero != __swap_table_test_zero(ci, ci_off) || (memcg_id && *memcg_id != __swap_cgroup_get(ci, ci_off)))) return -EBUSY; } while (++ci_off < ci_end); @@ -213,7 +216,7 @@ static void __swap_cache_do_add_folio(struct swap_cluster_info *ci, do { old_tb = __swap_table_get(ci, ci_off); VM_WARN_ON_ONCE(swp_tb_is_folio(old_tb)); - __swap_table_set(ci, ci_off, pfn_to_swp_tb(pfn, __swp_tb_get_count(old_tb))); + __swap_table_set(ci, ci_off, pfn_to_swp_tb(pfn, __swp_tb_get_flags(old_tb))); } while (++ci_off < ci_end); folio_ref_add(folio, nr_pages); @@ -249,7 +252,6 @@ static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, struct folio *folio, swp_entry_t entry, void *shadow) { - int count; unsigned long old_tb; struct swap_info_struct *si; unsigned int ci_start, ci_off, ci_end; @@ -269,13 +271,13 @@ static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, old_tb = __swap_table_get(ci, ci_off); WARN_ON_ONCE(!swp_tb_is_folio(old_tb) || swp_tb_to_folio(old_tb) != folio); - count = __swp_tb_get_count(old_tb); - if (count) + if (__swp_tb_get_count(old_tb)) folio_swapped = true; else need_free = true; /* If shadow is NULL, we set an empty shadow. */ - __swap_table_set(ci, ci_off, shadow_to_swp_tb(shadow, count)); + __swap_table_set(ci, ci_off, shadow_to_swp_tb(shadow, + __swp_tb_get_flags(old_tb))); } while (++ci_off < ci_end); folio->swap.val = 0; @@ -369,7 +371,7 @@ void __swap_cache_replace_folio(struct swap_cluster_info *ci, do { old_tb = __swap_table_get(ci, ci_off); WARN_ON_ONCE(!swp_tb_is_folio(old_tb) || swp_tb_to_folio(old_tb) != old); - __swap_table_set(ci, ci_off, pfn_to_swp_tb(pfn, __swp_tb_get_count(old_tb))); + __swap_table_set(ci, ci_off, pfn_to_swp_tb(pfn, __swp_tb_get_flags(old_tb))); } while (++ci_off < ci_end); /* diff --git a/mm/swap_table.h b/mm/swap_table.h index b4e1100f8296..e6613e62f8d0 100644 --- a/mm/swap_table.h +++ b/mm/swap_table.h @@ -26,12 +26,14 @@ struct swap_memcg_table { * Swap table entry type and bits layouts: * * NULL: |---------------- 0 ---------------| - Free slot - * Shadow: | SWAP_COUNT |---- SHADOW_VAL ---|1| - Swapped out slot - * PFN: | SWAP_COUNT |------ PFN -------|10| - Cached slot + * Shadow: |SWAP_COUNT|Z|---- SHADOW_VAL ---|1| - Swapped out slot + * PFN: |SWAP_COUNT|Z|------ PFN -------|10| - Cached slot * Pointer: |----------- Pointer ----------|100| - (Unused) * Bad: |------------- 1 -------------|1000| - Bad slot * - * SWAP_COUNT is `SWP_TB_COUNT_BITS` long, each entry is an atomic long. + * COUNT is `SWP_TB_COUNT_BITS` long, Z is the `SWP_TB_ZERO_FLAG` bit, + * and together they form the `SWP_TB_FLAGS_BITS` wide flags field. + * Each entry is an atomic long. * * Usages: * @@ -54,14 +56,6 @@ struct swap_memcg_table { * - Bad: Swap slot is reserved, protects swap header or holes on swap devices. */ -#if defined(MAX_POSSIBLE_PHYSMEM_BITS) -#define SWAP_CACHE_PFN_BITS (MAX_POSSIBLE_PHYSMEM_BITS - PAGE_SHIFT) -#elif defined(MAX_PHYSMEM_BITS) -#define SWAP_CACHE_PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) -#else -#define SWAP_CACHE_PFN_BITS (BITS_PER_LONG - PAGE_SHIFT) -#endif - /* NULL Entry, all 0 */ #define SWP_TB_NULL 0UL @@ -69,22 +63,26 @@ struct swap_memcg_table { #define SWP_TB_SHADOW_MARK 0b1UL /* Cached: PFN */ -#define SWP_TB_PFN_BITS (SWAP_CACHE_PFN_BITS + SWP_TB_PFN_MARK_BITS) +#define SWP_TB_PFN_BITS (SWAP_CACHE_PFN_BITS + SWAP_CACHE_PFN_MARK_BITS) #define SWP_TB_PFN_MARK 0b10UL -#define SWP_TB_PFN_MARK_BITS 2 -#define SWP_TB_PFN_MARK_MASK (BIT(SWP_TB_PFN_MARK_BITS) - 1) +#define SWP_TB_PFN_MARK_MASK (BIT(SWAP_CACHE_PFN_MARK_BITS) - 1) -/* SWAP_COUNT part for PFN or shadow, the width can be shrunk or extended */ -#define SWP_TB_COUNT_BITS min(4, BITS_PER_LONG - SWP_TB_PFN_BITS) +/* Flags: For PFN or shadow, contains SWAP_COUNT, width changes */ +#define SWP_TB_FLAGS_BITS min(5, BITS_PER_LONG - SWP_TB_PFN_BITS) +#define SWP_TB_COUNT_BITS (SWP_TB_FLAGS_BITS - SWAP_TABLE_HAS_ZEROFLAG) +#define SWP_TB_FLAGS_MASK (~((~0UL) >> SWP_TB_FLAGS_BITS)) #define SWP_TB_COUNT_MASK (~((~0UL) >> SWP_TB_COUNT_BITS)) +#define SWP_TB_FLAGS_SHIFT (BITS_PER_LONG - SWP_TB_FLAGS_BITS) #define SWP_TB_COUNT_SHIFT (BITS_PER_LONG - SWP_TB_COUNT_BITS) #define SWP_TB_COUNT_MAX ((1 << SWP_TB_COUNT_BITS) - 1) +/* The first flag is zero bit (SWAP_TABLE_HAS_ZEROFLAG) */ +#define SWP_TB_ZERO_FLAG BIT(BITS_PER_LONG - SWP_TB_FLAGS_BITS) /* Bad slot: ends with 0b1000 and rests of bits are all 1 */ #define SWP_TB_BAD ((~0UL) << 3) /* Macro for shadow offset calculation */ -#define SWAP_COUNT_SHIFT SWP_TB_COUNT_BITS +#define SWAP_COUNT_SHIFT SWP_TB_FLAGS_BITS /* * Helpers for casting one type of info into a swap table entry. @@ -102,40 +100,47 @@ static inline unsigned long __count_to_swp_tb(unsigned char count) * used (count > 0 && count < SWP_TB_COUNT_MAX), and * overflow (count == SWP_TB_COUNT_MAX). */ - BUILD_BUG_ON(SWP_TB_COUNT_MAX < 2 || SWP_TB_COUNT_BITS < 2); + BUILD_BUG_ON(SWP_TB_COUNT_BITS < SWAP_COUNT_MIN_BITS); VM_WARN_ON(count > SWP_TB_COUNT_MAX); return ((unsigned long)count) << SWP_TB_COUNT_SHIFT; } -static inline unsigned long pfn_to_swp_tb(unsigned long pfn, unsigned int count) +static inline unsigned long __flags_to_swp_tb(unsigned char flags) +{ + BUILD_BUG_ON(SWP_TB_FLAGS_BITS > BITS_PER_BYTE); + VM_WARN_ON(flags >> SWP_TB_FLAGS_BITS); + return ((unsigned long)flags) << SWP_TB_FLAGS_SHIFT; +} + +static inline unsigned long pfn_to_swp_tb(unsigned long pfn, unsigned char flags) { unsigned long swp_tb; BUILD_BUG_ON(sizeof(unsigned long) != sizeof(void *)); BUILD_BUG_ON(SWAP_CACHE_PFN_BITS > - (BITS_PER_LONG - SWP_TB_PFN_MARK_BITS - SWP_TB_COUNT_BITS)); + (BITS_PER_LONG - SWAP_CACHE_PFN_MARK_BITS - SWP_TB_FLAGS_BITS)); - swp_tb = (pfn << SWP_TB_PFN_MARK_BITS) | SWP_TB_PFN_MARK; - VM_WARN_ON_ONCE(swp_tb & SWP_TB_COUNT_MASK); + swp_tb = (pfn << SWAP_CACHE_PFN_MARK_BITS) | SWP_TB_PFN_MARK; + VM_WARN_ON_ONCE(swp_tb & SWP_TB_FLAGS_MASK); - return swp_tb | __count_to_swp_tb(count); + return swp_tb | __flags_to_swp_tb(flags); } -static inline unsigned long folio_to_swp_tb(struct folio *folio, unsigned int count) +static inline unsigned long folio_to_swp_tb(struct folio *folio, unsigned char flags) { - return pfn_to_swp_tb(folio_pfn(folio), count); + return pfn_to_swp_tb(folio_pfn(folio), flags); } -static inline unsigned long shadow_to_swp_tb(void *shadow, unsigned int count) +static inline unsigned long shadow_to_swp_tb(void *shadow, unsigned char flags) { BUILD_BUG_ON((BITS_PER_XA_VALUE + 1) != BITS_PER_BYTE * sizeof(unsigned long)); BUILD_BUG_ON((unsigned long)xa_mk_value(0) != SWP_TB_SHADOW_MARK); VM_WARN_ON_ONCE(shadow && !xa_is_value(shadow)); - VM_WARN_ON_ONCE(shadow && ((unsigned long)shadow & SWP_TB_COUNT_MASK)); + VM_WARN_ON_ONCE(shadow && ((unsigned long)shadow & SWP_TB_FLAGS_MASK)); - return (unsigned long)shadow | __count_to_swp_tb(count) | SWP_TB_SHADOW_MARK; + return (unsigned long)shadow | SWP_TB_SHADOW_MARK | __flags_to_swp_tb(flags); } /* @@ -173,14 +178,14 @@ static inline bool swp_tb_is_countable(unsigned long swp_tb) static inline struct folio *swp_tb_to_folio(unsigned long swp_tb) { VM_WARN_ON(!swp_tb_is_folio(swp_tb)); - return pfn_folio((swp_tb & ~SWP_TB_COUNT_MASK) >> SWP_TB_PFN_MARK_BITS); + return pfn_folio((swp_tb & ~SWP_TB_FLAGS_MASK) >> SWAP_CACHE_PFN_MARK_BITS); } static inline void *swp_tb_to_shadow(unsigned long swp_tb) { VM_WARN_ON(!swp_tb_is_shadow(swp_tb)); /* No shift needed, xa_value is stored as it is in the lower bits. */ - return (void *)(swp_tb & ~SWP_TB_COUNT_MASK); + return (void *)(swp_tb & ~SWP_TB_FLAGS_MASK); } static inline unsigned char __swp_tb_get_count(unsigned long swp_tb) @@ -189,6 +194,12 @@ static inline unsigned char __swp_tb_get_count(unsigned long swp_tb) return ((swp_tb & SWP_TB_COUNT_MASK) >> SWP_TB_COUNT_SHIFT); } +static inline unsigned char __swp_tb_get_flags(unsigned long swp_tb) +{ + VM_WARN_ON(!swp_tb_is_countable(swp_tb)); + return ((swp_tb & SWP_TB_FLAGS_MASK) >> SWP_TB_FLAGS_SHIFT); +} + static inline int swp_tb_get_count(unsigned long swp_tb) { if (swp_tb_is_countable(swp_tb)) @@ -253,6 +264,50 @@ static inline unsigned long swap_table_get(struct swap_cluster_info *ci, return swp_tb; } +static inline void __swap_table_set_zero(struct swap_cluster_info *ci, + unsigned int ci_off) +{ +#if SWAP_TABLE_HAS_ZEROFLAG + unsigned long swp_tb = __swap_table_get(ci, ci_off); + + BUILD_BUG_ON(SWP_TB_ZERO_FLAG & ~SWP_TB_FLAGS_MASK); + VM_WARN_ON(!swp_tb_is_countable(swp_tb)); + swp_tb |= SWP_TB_ZERO_FLAG; + __swap_table_set(ci, ci_off, swp_tb); +#else + lockdep_assert_held(&ci->lock); + __set_bit(ci_off, ci->zero_bitmap); +#endif +} + +static inline bool __swap_table_test_zero(struct swap_cluster_info *ci, + unsigned int ci_off) +{ +#if SWAP_TABLE_HAS_ZEROFLAG + unsigned long swp_tb = __swap_table_get(ci, ci_off); + + VM_WARN_ON(!swp_tb_is_countable(swp_tb)); + return !!(swp_tb & SWP_TB_ZERO_FLAG); +#else + return test_bit(ci_off, ci->zero_bitmap); +#endif +} + +static inline void __swap_table_clear_zero(struct swap_cluster_info *ci, + unsigned int ci_off) +{ +#if SWAP_TABLE_HAS_ZEROFLAG + unsigned long swp_tb = __swap_table_get(ci, ci_off); + + VM_WARN_ON(!swp_tb_is_countable(swp_tb)); + swp_tb &= ~SWP_TB_ZERO_FLAG; + __swap_table_set(ci, ci_off, swp_tb); +#else + lockdep_assert_held(&ci->lock); + __clear_bit(ci_off, ci->zero_bitmap); +#endif +} + #ifdef CONFIG_MEMCG static inline void __swap_cgroup_set(struct swap_cluster_info *ci, unsigned int ci_off, unsigned long nr, unsigned short id) diff --git a/mm/swapfile.c b/mm/swapfile.c index 992e77b7105d..615d90867111 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -427,6 +427,11 @@ static void swap_cluster_free_table(struct swap_cluster_info *ci) ci->memcg_table = NULL; #endif +#if !SWAP_TABLE_HAS_ZEROFLAG + kfree(ci->zero_bitmap); + ci->zero_bitmap = NULL; +#endif + table = (struct swap_table *)rcu_access_pointer(ci->table); if (!table) return; @@ -445,7 +450,6 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp) { struct swap_table *table = NULL; struct folio *folio; - int ret = 0; /* The cluster must be empty and not on any list during allocation. */ VM_WARN_ON_ONCE(ci->flags || !cluster_is_empty(ci)); @@ -468,14 +472,22 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp) if (!mem_cgroup_disabled()) { VM_WARN_ON_ONCE(ci->memcg_table); ci->memcg_table = kzalloc_obj(*ci->memcg_table, gfp); - if (!ci->memcg_table) - ret = -ENOMEM; + if (!ci->memcg_table) { + swap_cluster_free_table(ci); + return -ENOMEM; + } } #endif - if (ret) - swap_cluster_free_table(ci); - return ret; +#if !SWAP_TABLE_HAS_ZEROFLAG + VM_WARN_ON_ONCE(ci->zero_bitmap); + ci->zero_bitmap = bitmap_zalloc(SWAPFILE_CLUSTER, gfp); + if (!ci->zero_bitmap) { + swap_cluster_free_table(ci); + return -ENOMEM; + } +#endif + return 0; } /* @@ -928,8 +940,8 @@ static bool __swap_cluster_alloc_entries(struct swap_info_struct *si, order = 0; nr_pages = 1; swap_cluster_assert_empty(ci, ci_off, 1, false); - /* Sets a fake shadow as placeholder */ - __swap_table_set(ci, ci_off, shadow_to_swp_tb(NULL, 1)); + /* Fake shadow placeholder with no flag, hibernation does not use the zeromap */ + __swap_table_set(ci, ci_off, __swp_tb_mk_count(shadow_to_swp_tb(NULL, 0), 1)); } else { /* Allocation without folio is only possible with hibernation */ WARN_ON_ONCE(1); @@ -1302,14 +1314,8 @@ static void swap_range_free(struct swap_info_struct *si, unsigned long offset, void (*swap_slot_free_notify)(struct block_device *, unsigned long); unsigned int i; - /* - * Use atomic clear_bit operations only on zeromap instead of non-atomic - * bitmap_clear to prevent adjacent bits corruption due to simultaneous writes. - */ - for (i = 0; i < nr_entries; i++) { - clear_bit(offset + i, si->zeromap); + for (i = 0; i < nr_entries; i++) zswap_invalidate(swp_entry(si->type, offset + i)); - } if (si->flags & SWP_BLKDEV) swap_slot_free_notify = @@ -1920,7 +1926,11 @@ void __swap_cluster_free_entries(struct swap_info_struct *si, * ref, or after swap cache is dropped */ VM_WARN_ON(!swp_tb_is_shadow(old_tb) || __swp_tb_get_count(old_tb) > 1); + + /* Resetting the slot to NULL also clears the inline flags. */ __swap_table_set(ci, ci_off, null_to_swp_tb()); + if (!SWAP_TABLE_HAS_ZEROFLAG) + __swap_table_clear_zero(ci, ci_off); /* * Uncharge swap slots by memcg in batches. Consecutive @@ -2954,7 +2964,6 @@ static void flush_percpu_swap_cluster(struct swap_info_struct *si) SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) { struct swap_info_struct *p = NULL; - unsigned long *zeromap; struct swap_cluster_info *cluster_info; struct file *swap_file, *victim; struct address_space *mapping; @@ -3042,8 +3051,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) swap_file = p->swap_file; p->swap_file = NULL; - zeromap = p->zeromap; - p->zeromap = NULL; maxpages = p->max; cluster_info = p->cluster_info; p->max = 0; @@ -3055,7 +3062,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) mutex_unlock(&swapon_mutex); kfree(p->global_cluster); p->global_cluster = NULL; - kvfree(zeromap); free_swap_cluster_info(cluster_info, maxpages); inode = mapping->host; @@ -3587,17 +3593,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) if (error) goto bad_swap_unlock_inode; - /* - * Use kvmalloc_array instead of bitmap_zalloc as the allocation order might - * be above MAX_PAGE_ORDER incase of a large swap file. - */ - si->zeromap = kvmalloc_array(BITS_TO_LONGS(maxpages), sizeof(long), - GFP_KERNEL | __GFP_ZERO); - if (!si->zeromap) { - error = -ENOMEM; - goto bad_swap_unlock_inode; - } - if (si->bdev && bdev_stable_writes(si->bdev)) si->flags |= SWP_STABLE_WRITES; @@ -3699,8 +3694,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) destroy_swap_extents(si, swap_file); free_swap_cluster_info(si->cluster_info, si->max); si->cluster_info = NULL; - kvfree(si->zeromap); - si->zeromap = NULL; /* * Clear the SWP_USED flag after all resources are freed so * alloc_swap_info can reuse this si safely. From 0c946c54a7013742157b8f79b241140b0c670764 Mon Sep 17 00:00:00 2001 From: Sakurai Shun Date: Sun, 17 May 2026 19:36:35 +0900 Subject: [PATCH 2594/5214] docs/mm: fix typo in process_addrs.rst Replace "presense" with "presence" Link: https://lore.kernel.org/20260517103640.45444-1-ssh1326@icloud.com Signed-off-by: Sakurai Shun Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- Documentation/mm/process_addrs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/mm/process_addrs.rst b/Documentation/mm/process_addrs.rst index 851680ead45f..042d64d72421 100644 --- a/Documentation/mm/process_addrs.rst +++ b/Documentation/mm/process_addrs.rst @@ -775,7 +775,7 @@ lock, releasing or downgrading the mmap write lock also releases the VMA write lock so there is no :c:func:`!vma_end_write` function. Note that when write-locking a VMA lock, the :c:member:`!vma.vm_refcnt` is temporarily -modified so that readers can detect the presense of a writer. The reference counter is +modified so that readers can detect the presence of a writer. The reference counter is restored once the vma sequence number used for serialisation is updated. This ensures the semantics we require - VMA write locks provide exclusive write From 6fa411adff39e4955f11aa3e854a876680444d2a Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Fri, 15 May 2026 15:03:11 +0800 Subject: [PATCH 2595/5214] lib/test_hmm: fix error path in dmirror_devmem_fault() Handle migrate_vma_setup() failure via goto err for unified cleanup. Link: https://lore.kernel.org/20260515070312.130435-1-liuqiangneo@163.com Signed-off-by: Qiang Liu Reviewed-by: Alistair Popple Cc: Jason Gunthorpe Cc: Leon Romanovsky Signed-off-by: Andrew Morton --- lib/test_hmm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 38996c4baa40..63bf77dee987 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1679,8 +1679,14 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf) if (order) args.flags |= MIGRATE_VMA_SELECT_COMPOUND; - if (migrate_vma_setup(&args)) - return VM_FAULT_SIGBUS; + /* + * In practice migrate_vma_setup() should never fail unless the + * test is wrong as it just tests some static VMA properties. + */ + if (migrate_vma_setup(&args)) { + ret = VM_FAULT_SIGBUS; + goto err; + } ret = dmirror_devmem_fault_alloc_and_copy(&args, dmirror); if (ret) From 5ee5ff9dbce0b80297794fb77857bd80782b92db Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Fri, 15 May 2026 10:01:43 +0800 Subject: [PATCH 2596/5214] mm/memory-failure: remove hugetlb output parameter from try_memory_failure_hugetlb() Use -ENOENT return value to distinguish "not a hugetlb page" from "hugetlb handled", instead of carrying an extra output parameter. Link: https://lore.kernel.org/20260515020144.164941-1-ye.liu@linux.dev Signed-off-by: Ye Liu Suggested-by: Oscar Salvador Acked-by: Miaohe Lin Acked-by: Oscar Salvador (SUSE) Cc: Naoya Horiguchi Signed-off-by: Andrew Morton --- mm/memory-failure.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index eff405a21c68..1b8d0bade04a 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -2027,13 +2027,14 @@ static int get_huge_page_for_hwpoison(unsigned long pfn, int flags, * So some of prechecks for hwpoison (pinning, and testing/setting * PageHWPoison) should be done in single hugetlb_lock range. * Returns: - * 0 - not hugetlb, or recovered + * 0 - recovered + * -ENOENT - no hugetlb page * -EBUSY - not recovered * -EOPNOTSUPP - hwpoison_filter'ed * -EHWPOISON - folio or exact page already poisoned * -EFAULT - kill_accessing_process finds current->mm null */ -static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb) +static int try_memory_failure_hugetlb(unsigned long pfn, int flags) { int res, rv; struct page *p = pfn_to_page(pfn); @@ -2041,13 +2042,11 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb unsigned long page_flags; bool migratable_cleared = false; - *hugetlb = 1; retry: res = get_huge_page_for_hwpoison(pfn, flags, &migratable_cleared); switch (res) { case MF_HUGETLB_NON_HUGEPAGE: /* fallback to normal page handling */ - *hugetlb = 0; - return 0; + return -ENOENT; case MF_HUGETLB_RETRY: if (!(flags & MF_NO_RETRY)) { flags |= MF_NO_RETRY; @@ -2108,9 +2107,9 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb } #else -static inline int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb) +static inline int try_memory_failure_hugetlb(unsigned long pfn, int flags) { - return 0; + return -ENOENT; } static inline unsigned long folio_free_raw_hwp(struct folio *folio, bool flag) @@ -2348,7 +2347,6 @@ int memory_failure(unsigned long pfn, int flags) int res = 0; unsigned long page_flags; bool retry = true; - int hugetlb = 0; if (!sysctl_memory_failure_recovery) panic("Memory failure on page %lx", pfn); @@ -2387,8 +2385,11 @@ int memory_failure(unsigned long pfn, int flags) } try_again: - res = try_memory_failure_hugetlb(pfn, flags, &hugetlb); - if (hugetlb) + res = try_memory_failure_hugetlb(pfn, flags); + /* + * -ENOENT means the page we found is not hugetlb, so proceed with normal page handling + */ + if (res != -ENOENT) goto unlock_mutex; if (TestSetPageHWPoison(p)) { From 45c49d9fd6089e344663176b8488c97d905ca3ac Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:49 -0700 Subject: [PATCH 2597/5214] mm/damon/core: introduce struct damon_probe Patch series "mm/damon: introduce data attributes monitoring". TL; DR ====== Extend DAMON for monitoring general data attributes other than accesses. The short term motivation is lightweight page type (e.g., belonging cgroup) aware monitoring. In long term, this will help extending DAMON for multiple access events capture primitives (e.g., page faults and PMU) and eventually pivotting DAMON to a "Data Attributes Monitoring and Operations eNgine" in long term. Background: High Cost of Page Level Properties Monitoring ========================================================= DAMON is initially introduced as a Data Access MONitor. It has been extended for not only access monitoring but also data access-aware system operations (DAMOS). But still the monitoring part is only for data accesses. Data access patterns is good information, but some users need more holistic views. Particularly, users want to show the access pattern information together with the types of the memory. For example, users who work for making huge pages efficiently want to know how much of DAMON-found hot/cold regions are backed by huge pages. Users who run multiple workloads with different cgroups want to know how much of DAMON-found hot/cold regions belong to specific cgroups. For the user demand, we developed a DAMOS extension for page level properties based monitoring [1], which has landed on 6.14. Using the feature, users can inform the page level data properties that they are interested in, in a flexible format that uses DAMOS filters. Then, DAMON applies the filters to each folio of the entire DAMON region and lets users know how many bytes of memory in each DAMON region passed the given filters. This gives page level detailed and deterministic information to users. But, because the operation is done at page level, the overhead is proportional to the memory size. It was useful for test or debugging purposes on a small number of machines. But it was obviously too heavy to be enabled always on all machines running the real user workloads. For real world workloads, it was recommended to use the feature with user-space controlled sampling approaches. For example, users could do the page level monitoring only once per hour, on randomly selected one percent of machines of their fleet. If the runtime and the size of the fleet is long and big enough, it should provide statistically meaningful data. But users are too busy to implement such controls on their own. Data Attributes Monitoring ========================== Extend DAMON to monitor not only data accesses, but also general data attributes. Do the extension while keeping the main promise of DAMON, the bounded and best-effort minimum overhead. Allow users to specify what data attributes in addition to the data access they want to monitor. Users can install one 'data probe' per data attribute of their interest for this purpose. The 'data probe' should be able to be applied to any memory, and determine if the given memory has the appropriate data attribute. E.g., if memory of physical address 42 belongs to cgroup A. Each 'data probe' is configured with filters that are very similar to the DAMOS filters. When DAMON checks if each sampling address memory of each region is accessed since the last check, it applies data probes if registered. Same to the number of access check-positive samples accounting (nr_accesses), it accounts the number of each data probe-positive samples in another per-region counters array, namely 'probe_hits'. When DAMON resets nr_accesses every aggregation interval, it resets 'probe_hits' together. Users can read 'probe_hits' just before the values are reset. In this way, users can know how many hot/cold memory regions have data attributes of their interest. E.g., 30 percent of this system's hot memory is belonging to cgroup A, and 80 percent of the cgroup A-belonging hot memory is backed by huge pages. Patches Sequence ================ First eight patches implement the core feature, interface and the working support. Patch 1 introduces data probe data structure, namely damon_probe. Patch 2 extends damon_ctx for installing data probes. Patch 3 introduces another data structure for filters of each data probe, namely damon_filter. Patch 4 updates damon_ctx commit function to handle the probes. Patch 5 extends damon_region for the per-region per-probe positive samples counter, namely probe_hits. Patch 6 extends damon_operations for applying probes on the underlying DAMON operations implementation. Patch 7 updates kdamond_fn() to invoke the probes applying callback. Patch 8 finally implements the probes support on paddr ops. Ten changes for user interface (patches 9-18) come next. Patches 9-13 implements sysfs directories and files for setting data probes, namely probes directory, probe directory, filters directory, filter directory and filter directory internal files, respectively. Patch 14 connects the user inputs that are made via the sysfs files to DAMON core. Following three patches (patches 15-17) implement sysfs directories and files for showing the probe_hits to users, namely probes directory, probe directory and hits files, respectively. Patch 18 introduces a new tracepoint for showing the probe_hits via tracefs. Patch 19 adds a selftest for the sysfs files. Patches 20 and 21 documents the design and usage of the new feature, respectively. Seven additional patches (patches 22-28) for monitoring belonging memory cgroup follow. Depending on the feedback, this part might be separated to another series in future. Patch 22 defines the DAMON filter type for the new attribute, namely DAMON_FILTER_TYPE_MEMCG. Patch 23 add the support on paddr ops. Patch 24 updates the sysfs interface for setup of the target memcg. Patch 25 move code for easy reuse of the filter target memcg setup. Patch 26 connects the user input to the core layer. Finally, patches 27 and 28 update the design and usage documents for the memcg attribute monitoring support. Discussion ========== This allows the page properties monitoring with overhead that is low enough to be enabled always on real world workloads. Because the sampling time for access check is reused for data attributes check, the upper-bounded and best-effort minimum overhead of DAMON is kept. Because the sampling memory for access check is reused for data attributes check, additional overhead is minimum. Still DAMOS-based page level properties monitoring should be useful, because it provides a deterministic page level information. When in doubt of the sampling based information, running DAMOS-based one together and comparing the results would be useful, for debugging and tuning. Future Works: Mid Term ======================== This version of implementation is limiting the maximum number of data probes to four. I will try to find a way to remove the limit in future. I personally think it should be enough for common use cases, though, and therefore not giving high priority at the moment. Future Works: Long Term ======================= There are user requests for extending DAMON with detailed access information, for example, per-CPUs/threads/read/writes monitoring. For that, I was working [2] on extending DAMON to use page fault events as another access check primitives, and making the infrastructure flexible for future use of yet another access check primitive. Actually there is another ongoing work [3] for extending DAMON with PMU events. The motivation of the work is reducing the overhead, though. In my work [2], I was introducing a new interface for access sampling primitives control. Now I think this data probe interface can be used for that, too. That is, data access becomes just one type of data attribute. Also, pg_idle-confirmed access, page fault-confirmed access, and PMU event-confirmed access will be different types of data attributes. The regions adjustment mechanism is currently working based on the access information. That's because DAMON is designed for data access monitoring. That is, data access information is the primary interest, and therefore DAMON adjusts regions in a way that can best-present the information. Once data access becomes just one of data attributes, there is no reason to think data access that special. There might be some users not interested in access at all but want to know the location of memory of specific type. Data probes interface will allow doing that. Further, we could extend the interface to let users set any data attribute as the 'primary' attribute. Then, DAMON will split and merge regions in a way that can best-present the 'primary' attributes. DAMOS will also be extended, to specify targets based on not only the data access pattern, but all user-registered data attributes. From this stage, we may be able to call DAMON as a "Data Attributes Monitoring and Operations eNgine". This patch (of 28): Introduce a data structure for data attribute probe. It is just a linked list header at this step. It will be extended in a way that it can determine if a given memory has a specific data attribute. Link: https://lore.kernel.org/20260518234119.97569-1-sj@kernel.org Link: https://lore.kernel.org/20260518234119.97569-2-sj@kernel.org Link: https://lore.kernel.org/20250106193401.109161-1-sj@kernel.org [1] Link: https://lore.kernel.org/20251208062943.68824-1-sj@kernel.org/ [2] Link: https://lore.kernel.org/20260423004211.7037-1-akinobu.mita@gmail.com [3] Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 4d4f031bcb45..4794931fa2ea 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -730,6 +730,15 @@ struct damon_intervals_goal { unsigned long max_sample_us; }; +/** + * struct damon_probe - Data region attribute probe. + * + * @list: Siblings list. + */ +struct damon_probe { + struct list_head list; +}; + /** * struct damon_attrs - Monitoring attributes for accuracy/overhead control. * From 18c777859f28d5e9b65d94c4fdc64f240250df3a Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:50 -0700 Subject: [PATCH 2598/5214] mm/damon/core: embed damon_probe objects in damon_ctx Let damon_probe objects be able to be installed on a given damon_ctx, by adding a linked list header for storing the objects. Add initialization and cleanup of the new field with helper functions, too. Link: https://lore.kernel.org/20260518234119.97569-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 9 +++++++++ mm/damon/core.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 4794931fa2ea..43d71eb24ccb 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -857,6 +857,7 @@ struct damon_ctx { /* public: */ struct damon_operations ops; + struct list_head probes; unsigned long addr_unit; unsigned long min_region_sz; bool pause; @@ -909,6 +910,11 @@ static inline unsigned long damon_sz_region(struct damon_region *r) return r->ar.end - r->ar.start; } +#define damon_for_each_probe(p, ctx) \ + list_for_each_entry(p, &(ctx)->probes, list) + +#define damon_for_each_probe_safe(p, next, ctx) \ + list_for_each_entry_safe(p, next, &(ctx)->probes, list) #define damon_for_each_region(r, t) \ list_for_each_entry(r, &t->regions_list, list) @@ -951,6 +957,9 @@ static inline unsigned long damon_sz_region(struct damon_region *r) #ifdef CONFIG_DAMON +struct damon_probe *damon_new_probe(void); +void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe); + struct damon_region *damon_new_region(unsigned long start, unsigned long end); /* diff --git a/mm/damon/core.c b/mm/damon/core.c index 3a8725e400c6..8a55cc61d297 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -113,6 +113,38 @@ int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id) return err; } +struct damon_probe *damon_new_probe(void) +{ + struct damon_probe *p; + + p = kmalloc_obj(*p); + if (!p) + return NULL; + INIT_LIST_HEAD(&p->list); + return p; +} + +void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe) +{ + list_add_tail(&probe->list, &ctx->probes); +} + +static void damon_del_probe(struct damon_probe *p) +{ + list_del(&p->list); +} + +static void damon_free_probe(struct damon_probe *p) +{ + kfree(p); +} + +static void damon_destroy_probe(struct damon_probe *p) +{ + damon_del_probe(p); + damon_free_probe(p); +} + #ifdef CONFIG_DAMON_DEBUG_SANITY static void damon_verify_new_region(unsigned long start, unsigned long end) { @@ -605,6 +637,8 @@ struct damon_ctx *damon_new_ctx(void) ctx->attrs.min_nr_regions = 10; ctx->attrs.max_nr_regions = 1000; + INIT_LIST_HEAD(&ctx->probes); + ctx->addr_unit = 1; ctx->min_region_sz = DAMON_MIN_REGION_SZ; @@ -627,12 +661,16 @@ static void damon_destroy_targets(struct damon_ctx *ctx) void damon_destroy_ctx(struct damon_ctx *ctx) { struct damos *s, *next_s; + struct damon_probe *p, *next_p; damon_destroy_targets(ctx); damon_for_each_scheme_safe(s, next_s, ctx) damon_destroy_scheme(s); + damon_for_each_probe_safe(p, next_p, ctx) + damon_destroy_probe(p); + kfree(ctx); } From f557693dd8ac9cd87d2a1ae1025ee9f568e916e6 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:51 -0700 Subject: [PATCH 2599/5214] mm/damon/core: introduce damon_filter Define a data structure for constructing damon_probe's attributes check, namely damon_filter. It is very similar to damos_filter but works only for monitoring purposes. Also embed that into damon_probe, implement essential handling of the link, with fundamental helpers. Link: https://lore.kernel.org/20260518234119.97569-4-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 36 ++++++++++++++++++++++++++++++++++++ mm/damon/core.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 43d71eb24ccb..f8b679dd944d 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -730,12 +730,38 @@ struct damon_intervals_goal { unsigned long max_sample_us; }; +/** + * enum damon_filter_type - Type of &struct damon_filter + * + * @DAMON_FILTER_TYPE_ANON: Anonymous pages. + */ +enum damon_filter_type { + DAMON_FILTER_TYPE_ANON, +}; + +/** + * struct damon_filter - DAMON region filter for &struct damon_probe. + * + * @type: Type of the region. + * @matching: Whether this filter is for the type-matching ones. + * @allow: Whether the @type-@matching ones should pass this filter. + * @list: Siblings list. + */ +struct damon_filter { + enum damon_filter_type type; + bool matching; + bool allow; + struct list_head list; +}; + /** * struct damon_probe - Data region attribute probe. * + * @filters: Filters for assessing if a given region is for this probe. * @list: Siblings list. */ struct damon_probe { + struct list_head filters; struct list_head list; }; @@ -910,6 +936,12 @@ static inline unsigned long damon_sz_region(struct damon_region *r) return r->ar.end - r->ar.start; } +#define damon_for_each_filter(f, p) \ + list_for_each_entry(f, &(p)->filters, list) + +#define damon_for_each_filter_safe(f, next, p) \ + list_for_each_entry_safe(f, next, &(p)->filters, list) + #define damon_for_each_probe(p, ctx) \ list_for_each_entry(p, &(ctx)->probes, list) @@ -957,6 +989,10 @@ static inline unsigned long damon_sz_region(struct damon_region *r) #ifdef CONFIG_DAMON +struct damon_filter *damon_new_filter(enum damon_filter_type type, + bool matching, bool allow); +void damon_add_filter(struct damon_probe *probe, struct damon_filter *f); + struct damon_probe *damon_new_probe(void); void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe); diff --git a/mm/damon/core.c b/mm/damon/core.c index 8a55cc61d297..d01417955a3b 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -113,6 +113,31 @@ int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id) return err; } +struct damon_filter *damon_new_filter(enum damon_filter_type type, + bool matching, bool allow) +{ + struct damon_filter *filter; + + filter = kmalloc_obj(*filter); + if (!filter) + return NULL; + filter->type = type; + filter->matching = matching; + filter->allow = allow; + INIT_LIST_HEAD(&filter->list); + return filter; +} + +void damon_add_filter(struct damon_probe *p, struct damon_filter *f) +{ + list_add_tail(&f->list, &p->filters); +} + +static void damon_free_filter(struct damon_filter *f) +{ + kfree(f); +} + struct damon_probe *damon_new_probe(void) { struct damon_probe *p; @@ -120,6 +145,7 @@ struct damon_probe *damon_new_probe(void) p = kmalloc_obj(*p); if (!p) return NULL; + INIT_LIST_HEAD(&p->filters); INIT_LIST_HEAD(&p->list); return p; } @@ -136,6 +162,10 @@ static void damon_del_probe(struct damon_probe *p) static void damon_free_probe(struct damon_probe *p) { + struct damon_filter *f, *next; + + damon_for_each_filter_safe(f, next, p) + damon_free_filter(f); kfree(p); } From d0de4b29c722d903e3b82dfc035cb78c015b46e0 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:52 -0700 Subject: [PATCH 2600/5214] mm/damon/core: commit probes Update damon_commit_ctx() to commit installed data probes, too. Link: https://lore.kernel.org/20260518234119.97569-5-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/core.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index d01417955a3b..240cae1420c1 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -133,11 +133,34 @@ void damon_add_filter(struct damon_probe *p, struct damon_filter *f) list_add_tail(&f->list, &p->filters); } +static void damon_del_filter(struct damon_filter *f) +{ + list_del(&f->list); +} + static void damon_free_filter(struct damon_filter *f) { kfree(f); } +static void damon_destroy_filter(struct damon_filter *f) +{ + damon_del_filter(f); + damon_free_filter(f); +} + +static struct damon_filter *damon_nth_filter(int n, struct damon_probe *p) +{ + struct damon_filter *f; + int i = 0; + + damon_for_each_filter(f, p) { + if (i++ == n) + return f; + } + return NULL; +} + struct damon_probe *damon_new_probe(void) { struct damon_probe *p; @@ -175,6 +198,18 @@ static void damon_destroy_probe(struct damon_probe *p) damon_free_probe(p); } +static struct damon_probe *damon_nth_probe(int n, struct damon_ctx *ctx) +{ + struct damon_probe *p; + int i = 0; + + damon_for_each_probe(p, ctx) { + if (i++ == n) + return p; + } + return NULL; +} + #ifdef CONFIG_DAMON_DEBUG_SANITY static void damon_verify_new_region(unsigned long start, unsigned long end) { @@ -1386,6 +1421,72 @@ static int damon_commit_targets( return 0; } +static void damon_commit_filter(struct damon_filter *dst, + struct damon_filter *src) +{ + dst->type = src->type; + dst->matching = src->matching; + dst->allow = src->allow; +} + +static int damon_commit_filters(struct damon_probe *dst, + struct damon_probe *src) +{ + struct damon_filter *dst_filter, *next, *src_filter, *new_filter; + int i = 0, j = 0; + + damon_for_each_filter_safe(dst_filter, next, dst) { + src_filter = damon_nth_filter(i++, src); + if (src_filter) + damon_commit_filter(dst_filter, src_filter); + else + damon_destroy_filter(dst_filter); + } + + damon_for_each_filter_safe(src_filter, next, src) { + if (j++ < i) + continue; + + new_filter = damon_new_filter(src_filter->type, + src_filter->matching, src_filter->allow); + if (!new_filter) + return -ENOMEM; + damon_add_filter(dst, new_filter); + } + return 0; +} + +static int damon_commit_probes(struct damon_ctx *dst, struct damon_ctx *src) +{ + struct damon_probe *dst_probe, *next, *src_probe, *new_probe; + int i = 0, j = 0, err; + + damon_for_each_probe_safe(dst_probe, next, dst) { + src_probe = damon_nth_probe(i++, src); + if (src_probe) { + err = damon_commit_filters(dst_probe, src_probe); + if (err) + return err; + } else { + damon_destroy_probe(dst_probe); + } + } + + damon_for_each_probe_safe(src_probe, next, src) { + if (j++ < i) + continue; + + new_probe = damon_new_probe(); + if (!new_probe) + return -ENOMEM; + damon_add_probe(dst, new_probe); + err = damon_commit_filters(new_probe, src_probe); + if (err) + return err; + } + return 0; +} + /** * damon_commit_ctx() - Commit parameters of a DAMON context to another. * @dst: The commit destination DAMON context. @@ -1442,6 +1543,9 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) } dst->pause = src->pause; dst->ops = src->ops; + err = damon_commit_probes(dst, src); + if (err) + return err; dst->addr_unit = src->addr_unit; dst->min_region_sz = src->min_region_sz; From 57c6332f2548d94f137f51bd18111e4316fd1ba4 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:53 -0700 Subject: [PATCH 2601/5214] mm/damon/core: introduce damon_region->probe_hits Add an array for the per-region per-probe positive samples count. For simple and efficient implementation, add a limit to the number of data probes and set the array to support only the limited number of counters. Link: https://lore.kernel.org/20260518234119.97569-6-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 4 ++++ mm/damon/core.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index f8b679dd944d..3a30af119ac6 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -17,6 +17,8 @@ /* Minimal region size. Every damon_region is aligned by this. */ #define DAMON_MIN_REGION_SZ PAGE_SIZE +/* Maximum number of monitoring probes. */ +#define DAMON_MAX_PROBES (4) /* Max priority score for DAMON-based operation schemes */ #define DAMOS_MAX_SCORE (99) @@ -47,6 +49,7 @@ struct damon_size_range { * @nr_accesses: Access frequency of this region. * @nr_accesses_bp: @nr_accesses in basis point (0.01%) that updated for * each sampling interval. + * @probe_hits: Number of probe-positive region samples. * @list: List head for siblings. * @age: Age of this region. * @@ -75,6 +78,7 @@ struct damon_region { unsigned long sampling_addr; unsigned int nr_accesses; unsigned int nr_accesses_bp; + unsigned char probe_hits[DAMON_MAX_PROBES]; struct list_head list; unsigned int age; diff --git a/mm/damon/core.c b/mm/damon/core.c index 240cae1420c1..0f6b3b66d1de 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -229,6 +229,7 @@ static void damon_verify_new_region(unsigned long start, unsigned long end) struct damon_region *damon_new_region(unsigned long start, unsigned long end) { struct damon_region *region; + int i; damon_verify_new_region(start, end); region = kmem_cache_alloc(damon_region_cache, GFP_KERNEL); @@ -239,6 +240,8 @@ struct damon_region *damon_new_region(unsigned long start, unsigned long end) region->ar.end = end; region->nr_accesses = 0; region->nr_accesses_bp = 0; + for (i = 0; i < DAMON_MAX_PROBES; i++) + region->probe_hits[i] = 0; INIT_LIST_HEAD(®ion->list); region->age = 0; @@ -2980,12 +2983,17 @@ static void damon_merge_two_regions(struct damon_target *t, struct damon_region *l, struct damon_region *r) { unsigned long sz_l = damon_sz_region(l), sz_r = damon_sz_region(r); + int i; l->nr_accesses = (l->nr_accesses * sz_l + r->nr_accesses * sz_r) / (sz_l + sz_r); l->nr_accesses_bp = l->nr_accesses * 10000; l->age = (l->age * sz_l + r->age * sz_r) / (sz_l + sz_r); l->ar.end = r->ar.end; + /* todo: do this for only installed probes */ + for (i = 0; i < DAMON_MAX_PROBES; i++) + l->probe_hits[i] = (l->probe_hits[i] * sz_l + r->probe_hits[i] + * sz_r) / (sz_l + sz_r); damon_verify_merge_two_regions(l, r); damon_destroy_region(r, t); } @@ -3108,6 +3116,8 @@ static void damon_split_region_at(struct damon_target *t, new->last_nr_accesses = r->last_nr_accesses; new->nr_accesses_bp = r->nr_accesses_bp; new->nr_accesses = r->nr_accesses; + /* todo: do this for only installed probes */ + memcpy(new->probe_hits, r->probe_hits, sizeof(r->probe_hits)); damon_insert_region(new, r, damon_next_region(r), t); } From 1a9e847589180359be4198c7d2a3d2ea15b2ddd0 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:54 -0700 Subject: [PATCH 2602/5214] mm/damon/core: introduce damon_ops->apply_probes Extend damon_operations struct with a new callback, namely apply_probes. The callback will be invoked for data attributes monitoring. More specifically, the callback will apply damon_probe objects to each region and update the per-region per-probe counters for the number of encountered probe-positive samples. Link: https://lore.kernel.org/20260518234119.97569-7-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 3a30af119ac6..1fb271a35e98 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -630,6 +630,7 @@ enum damon_ops_id { * @update: Update operations-related data structures. * @prepare_access_checks: Prepare next access check of target regions. * @check_accesses: Check the accesses to target regions. + * @apply_probes: Apply probes for each region. * @get_scheme_score: Get the score of a region for a scheme. * @apply_scheme: Apply a DAMON-based operation scheme. * @target_valid: Determine if the target is valid. @@ -656,6 +657,8 @@ enum damon_ops_id { * last preparation and update the number of observed accesses of each region. * It should also return max number of observed accesses that made as a result * of its update. The value will be used for regions adjustment threshold. + * @apply_probes should apply the data attribute probes to each region and + * accordingly update the probe hits counter of the region. * @get_scheme_score should return the priority score of a region for a scheme * as an integer in [0, &DAMOS_MAX_SCORE]. * @apply_scheme is called from @kdamond when a region for user provided @@ -673,6 +676,7 @@ struct damon_operations { void (*update)(struct damon_ctx *context); void (*prepare_access_checks)(struct damon_ctx *context); unsigned int (*check_accesses)(struct damon_ctx *context); + void (*apply_probes)(struct damon_ctx *context); int (*get_scheme_score)(struct damon_ctx *context, struct damon_region *r, struct damos *scheme); unsigned long (*apply_scheme)(struct damon_ctx *context, From 9b1f8c8d015bc92cab358f1395ee053fd01d7b89 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:55 -0700 Subject: [PATCH 2603/5214] mm/damon/core: do data attributes monitoring Implement the data attributes monitoring execution. Update kdamond to invoke the probes application callback, and reset the aggregated number of per-region per-probe positive samples for every aggregation interval. Link: https://lore.kernel.org/20260518234119.97569-8-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index 0f6b3b66d1de..500e8b08d441 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1910,10 +1910,14 @@ static void kdamond_reset_aggregated(struct damon_ctx *c) struct damon_region *r; damon_for_each_region(r, t) { + int i; + trace_damon_aggregated(ti, r, damon_nr_regions(t)); damon_warn_fix_nr_accesses_corruption(r); r->last_nr_accesses = r->nr_accesses; r->nr_accesses = 0; + for (i = 0; i < DAMON_MAX_PROBES; i++) + r->probe_hits[i] = 0; damon_verify_reset_aggregated(r, c); } ti++; @@ -3407,6 +3411,8 @@ static int kdamond_fn(void *data) if (ctx->ops.check_accesses) max_nr_accesses = ctx->ops.check_accesses(ctx); + if (ctx->ops.apply_probes) + ctx->ops.apply_probes(ctx); if (time_after_eq(ctx->passed_sample_intervals, next_aggregation_sis)) { From 09acfaced2d45b4f6d70e3999783d6e8ccec0ea7 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:56 -0700 Subject: [PATCH 2604/5214] mm/damon/paddr: support data attributes monitoring Implement and register damon_operations->apply_probes() callback to support data attributes monitoring. Link: https://lore.kernel.org/20260518234119.97569-9-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/paddr.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index c4738cd5e221..9997c5174ef1 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -120,6 +120,67 @@ static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx) return max_nr_accesses; } +static bool damon_pa_filter_match(struct damon_filter *filter, + struct folio *folio) +{ + bool matched = false; + + switch (filter->type) { + case DAMON_FILTER_TYPE_ANON: + if (!folio) { + matched = false; + break; + } + matched = folio_test_anon(folio); + break; + default: + break; + } + return matched == filter->matching; +} + +static bool damon_pa_filter_pass(phys_addr_t pa, struct folio *folio, + struct damon_probe *p) +{ + struct damon_filter *f; + bool pass = true; + + damon_for_each_filter(f, p) { + if (damon_pa_filter_match(f, folio)) { + pass = f->allow; + break; + } + pass = !f->allow; + } + return pass; +} + +static void damon_pa_apply_probes(struct damon_ctx *ctx) +{ + struct damon_target *t; + struct damon_region *r; + struct damon_probe *p; + + damon_for_each_target(t, ctx) { + damon_for_each_region(r, t) { + int i = 0; + phys_addr_t pa; + struct folio *folio; + + pa = damon_pa_phys_addr(r->sampling_addr, + ctx->addr_unit); + folio = damon_get_folio(PHYS_PFN(pa)); + damon_for_each_probe(p, ctx) { + if (damon_pa_filter_pass(pa, folio, p)) + r->probe_hits[i]++; + i++; + } + if (folio) + folio_put(folio); + } + } +} + /* * damos_pa_filter_out - Return true if the page should be filtered out. */ @@ -371,6 +432,7 @@ static int __init damon_pa_initcall(void) .update = NULL, .prepare_access_checks = damon_pa_prepare_access_checks, .check_accesses = damon_pa_check_accesses, + .apply_probes = damon_pa_apply_probes, .target_valid = NULL, .apply_scheme = damon_pa_apply_scheme, .get_scheme_score = damon_pa_scheme_score, From 90a8322934ae8ab4b3e9418ed006e81df0d33dfc Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:57 -0700 Subject: [PATCH 2605/5214] mm/damon/sysfs: implement probes dir Implement sysfs directory that can be used by the users to install data probes. Link: https://lore.kernel.org/20260518234119.97569-10-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index d5863cc33d23..ccd19fc062f3 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -747,6 +747,35 @@ static const struct kobj_type damon_sysfs_intervals_ktype = { .default_groups = damon_sysfs_intervals_groups, }; +/* + * probes directory + */ + +struct damon_sysfs_probes { + struct kobject kobj; +}; + +static struct damon_sysfs_probes *damon_sysfs_probes_alloc(void) +{ + return kzalloc_obj(struct damon_sysfs_probes); +} + +static void damon_sysfs_probes_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_probes, kobj)); +} + +static struct attribute *damon_sysfs_probes_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_probes); + +static const struct kobj_type damon_sysfs_probes_ktype = { + .release = damon_sysfs_probes_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_probes_groups, +}; + /* * monitoring_attrs directory */ @@ -755,6 +784,7 @@ struct damon_sysfs_attrs { struct kobject kobj; struct damon_sysfs_intervals *intervals; struct damon_sysfs_ul_range *nr_regions_range; + struct damon_sysfs_probes *probes; }; static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void) @@ -771,6 +801,7 @@ static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs) { struct damon_sysfs_intervals *intervals; struct damon_sysfs_ul_range *nr_regions_range; + struct damon_sysfs_probes *probes; int err; intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000); @@ -799,8 +830,22 @@ static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs) if (err) goto put_nr_regions_intervals_out; attrs->nr_regions_range = nr_regions_range; + + probes = damon_sysfs_probes_alloc(); + if (!probes) { + err = -ENOMEM; + goto put_nr_regions_intervals_out; + } + err = kobject_init_and_add(&probes->kobj, + &damon_sysfs_probes_ktype, &attrs->kobj, "probes"); + if (err) + goto put_probes_out; + attrs->probes = probes; return 0; +put_probes_out: + kobject_put(&probes->kobj); + attrs->probes = NULL; put_nr_regions_intervals_out: kobject_put(&nr_regions_range->kobj); attrs->nr_regions_range = NULL; @@ -817,6 +862,7 @@ static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs) kobject_put(&attrs->nr_regions_range->kobj); damon_sysfs_intervals_rm_dirs(attrs->intervals); kobject_put(&attrs->intervals->kobj); + kobject_put(&attrs->probes->kobj); } static void damon_sysfs_attrs_release(struct kobject *kobj) From 7d49f5aaee63bddded9e8f2fd15949596f69ae6b Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:58 -0700 Subject: [PATCH 2606/5214] mm/damon/sysfs: implement probe dir Implement sysfs directory for letting users install each data probe. Link: https://lore.kernel.org/20260518234119.97569-11-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index ccd19fc062f3..6cef3eaa4431 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -747,12 +747,43 @@ static const struct kobj_type damon_sysfs_intervals_ktype = { .default_groups = damon_sysfs_intervals_groups, }; +/* + * probe directory + */ + +struct damon_sysfs_probe { + struct kobject kobj; +}; + +static struct damon_sysfs_probe *damon_sysfs_probe_alloc(void) +{ + return kzalloc_obj(struct damon_sysfs_probe); +} + +static void damon_sysfs_probe_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_probe, kobj)); +} + +static struct attribute *damon_sysfs_probe_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_probe); + +static const struct kobj_type damon_sysfs_probe_ktype = { + .release = damon_sysfs_probe_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_probe_groups, +}; + /* * probes directory */ struct damon_sysfs_probes { struct kobject kobj; + struct damon_sysfs_probe **probes_arr; + int nr; }; static struct damon_sysfs_probes *damon_sysfs_probes_alloc(void) @@ -760,12 +791,99 @@ static struct damon_sysfs_probes *damon_sysfs_probes_alloc(void) return kzalloc_obj(struct damon_sysfs_probes); } +static void damon_sysfs_probes_rm_dirs( + struct damon_sysfs_probes *probes) +{ + struct damon_sysfs_probe **probes_arr = probes->probes_arr; + int i; + + for (i = 0; i < probes->nr; i++) + kobject_put(&probes_arr[i]->kobj); + probes->nr = 0; + kfree(probes_arr); + probes->probes_arr = NULL; +} + +static int damon_sysfs_probes_add_dirs( + struct damon_sysfs_probes *probes, int nr_probes) +{ + struct damon_sysfs_probe **probes_arr, *probe; + int err, i; + + damon_sysfs_probes_rm_dirs(probes); + if (!nr_probes) + return 0; + + probes_arr = kmalloc_objs(*probes_arr, nr_probes, + GFP_KERNEL | __GFP_NOWARN); + if (!probes_arr) + return -ENOMEM; + probes->probes_arr = probes_arr; + + for (i = 0; i < nr_probes; i++) { + probe = damon_sysfs_probe_alloc(); + if (!probe) { + damon_sysfs_probes_rm_dirs(probes); + return -ENOMEM; + } + + err = kobject_init_and_add(&probe->kobj, + &damon_sysfs_probe_ktype, &probes->kobj, + "%d", i); + if (err) { + kobject_put(&probe->kobj); + damon_sysfs_probes_rm_dirs(probes); + return err; + } + + probes_arr[i] = probe; + probes->nr++; + } + return 0; +} + +static ssize_t nr_probes_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_probes *probes = container_of(kobj, + struct damon_sysfs_probes, kobj); + + return sysfs_emit(buf, "%d\n", probes->nr); +} + +static ssize_t nr_probes_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_probes *probes; + int nr, err = kstrtoint(buf, 0, &nr); + + if (err) + return err; + if (nr < 0 || nr > DAMON_MAX_PROBES) + return -EINVAL; + + probes = container_of(kobj, struct damon_sysfs_probes, kobj); + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + err = damon_sysfs_probes_add_dirs(probes, nr); + mutex_unlock(&damon_sysfs_lock); + if (err) + return err; + + return count; +} + static void damon_sysfs_probes_release(struct kobject *kobj) { kfree(container_of(kobj, struct damon_sysfs_probes, kobj)); } +static struct kobj_attribute damon_sysfs_probes_nr_probes = + __ATTR_RW_MODE(nr_probes, 0600); + static struct attribute *damon_sysfs_probes_attrs[] = { + &damon_sysfs_probes_nr_probes.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_probes); @@ -862,6 +980,7 @@ static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs) kobject_put(&attrs->nr_regions_range->kobj); damon_sysfs_intervals_rm_dirs(attrs->intervals); kobject_put(&attrs->intervals->kobj); + damon_sysfs_probes_rm_dirs(attrs->probes); kobject_put(&attrs->probes->kobj); } From af7cb41af9a9310a6e654942199d2bb29f4f0021 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:40:59 -0700 Subject: [PATCH 2607/5214] mm/damon/sysfs: implement filters directory Implement a directory for letting users to install data probe filters. Link: https://lore.kernel.org/20260518234119.97569-12-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 6cef3eaa4431..dad4985a826d 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -747,12 +747,42 @@ static const struct kobj_type damon_sysfs_intervals_ktype = { .default_groups = damon_sysfs_intervals_groups, }; +/* + * filters directory + */ + +struct damon_sysfs_filters { + struct kobject kobj; +}; + +static struct damon_sysfs_filters *damon_sysfs_filters_alloc(void) +{ + return kzalloc_obj(struct damon_sysfs_filters); +} + +static void damon_sysfs_filters_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_filters, kobj)); +} + +static struct attribute *damon_sysfs_filters_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_filters); + +static const struct kobj_type damon_sysfs_filters_ktype = { + .release = damon_sysfs_filters_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_filters_groups, +}; + /* * probe directory */ struct damon_sysfs_probe { struct kobject kobj; + struct damon_sysfs_filters *filters; }; static struct damon_sysfs_probe *damon_sysfs_probe_alloc(void) @@ -760,6 +790,30 @@ static struct damon_sysfs_probe *damon_sysfs_probe_alloc(void) return kzalloc_obj(struct damon_sysfs_probe); } +static int damon_sysfs_probe_add_dirs(struct damon_sysfs_probe *attr) +{ + struct damon_sysfs_filters *filters; + int err; + + filters = damon_sysfs_filters_alloc(); + if (!filters) + return -ENOMEM; + attr->filters = filters; + + err = kobject_init_and_add(&filters->kobj, &damon_sysfs_filters_ktype, + &attr->kobj, "filters"); + if (err) { + kobject_put(&filters->kobj); + attr->filters = NULL; + } + return err; +} + +static void damon_sysfs_probe_rm_dirs(struct damon_sysfs_probe *attr) +{ + kobject_put(&attr->filters->kobj); +} + static void damon_sysfs_probe_release(struct kobject *kobj) { kfree(container_of(kobj, struct damon_sysfs_probe, kobj)); @@ -797,8 +851,10 @@ static void damon_sysfs_probes_rm_dirs( struct damon_sysfs_probe **probes_arr = probes->probes_arr; int i; - for (i = 0; i < probes->nr; i++) + for (i = 0; i < probes->nr; i++) { + damon_sysfs_probe_rm_dirs(probes_arr[i]); kobject_put(&probes_arr[i]->kobj); + } probes->nr = 0; kfree(probes_arr); probes->probes_arr = NULL; @@ -836,6 +892,13 @@ static int damon_sysfs_probes_add_dirs( return err; } + err = damon_sysfs_probe_add_dirs(probe); + if (err) { + kobject_put(&probe->kobj); + damon_sysfs_probes_rm_dirs(probes); + return err; + } + probes_arr[i] = probe; probes->nr++; } From 956bf44e4576121a7aa2d9c7f4a9e065edd293f8 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:00 -0700 Subject: [PATCH 2608/5214] mm/damon/sysfs: implement filter dir Implement a sysfs directory for letting the users to configure each data probe filter. Link: https://lore.kernel.org/20260518234119.97569-13-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index dad4985a826d..2dc475ea0f0f 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -747,12 +747,46 @@ static const struct kobj_type damon_sysfs_intervals_ktype = { .default_groups = damon_sysfs_intervals_groups, }; +/* + * filter directory + */ + +struct damon_sysfs_filter { + struct kobject kobj; +}; + +static struct damon_sysfs_filter *damon_sysfs_filter_alloc(void) +{ + return kzalloc_obj(struct damon_sysfs_filter); +} + +static void damon_sysfs_filter_release(struct kobject *kobj) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + + kfree(filter); +} + +static struct attribute *damon_sysfs_filter_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_filter); + +static const struct kobj_type damon_sysfs_filter_ktype = { + .release = damon_sysfs_filter_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_filter_groups, +}; + /* * filters directory */ struct damon_sysfs_filters { struct kobject kobj; + struct damon_sysfs_filter **filters_arr; + int nr; }; static struct damon_sysfs_filters *damon_sysfs_filters_alloc(void) @@ -760,12 +794,98 @@ static struct damon_sysfs_filters *damon_sysfs_filters_alloc(void) return kzalloc_obj(struct damon_sysfs_filters); } +static void damon_sysfs_filters_rm_dirs(struct damon_sysfs_filters *filters) +{ + struct damon_sysfs_filter **filters_arr = filters->filters_arr; + int i; + + for (i = 0; i < filters->nr; i++) + kobject_put(&filters_arr[i]->kobj); + filters->nr = 0; + kfree(filters_arr); + filters->filters_arr = NULL; +} + +static int damon_sysfs_filters_add_dirs( + struct damon_sysfs_filters *filters, int nr_filters) +{ + struct damon_sysfs_filter **filters_arr, *filter; + int err, i; + + damon_sysfs_filters_rm_dirs(filters); + if (!nr_filters) + return 0; + + filters_arr = kmalloc_objs(*filters_arr, nr_filters, + GFP_KERNEL | __GFP_NOWARN); + if (!filters_arr) + return -ENOMEM; + filters->filters_arr = filters_arr; + + for (i = 0; i < nr_filters; i++) { + filter = damon_sysfs_filter_alloc(); + if (!filter) { + damon_sysfs_filters_rm_dirs(filters); + return -ENOMEM; + } + + err = kobject_init_and_add(&filter->kobj, + &damon_sysfs_filter_ktype, &filters->kobj, + "%d", i); + if (err) { + kobject_put(&filter->kobj); + damon_sysfs_filters_rm_dirs(filters); + return err; + } + + filters_arr[i] = filter; + filters->nr++; + } + return 0; +} + +static ssize_t nr_filters_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_filters *filters = container_of(kobj, + struct damon_sysfs_filters, kobj); + + return sysfs_emit(buf, "%d\n", filters->nr); +} + +static ssize_t nr_filters_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_filters *filters; + int nr, err = kstrtoint(buf, 0, &nr); + + if (err) + return err; + if (nr < 0) + return -EINVAL; + + filters = container_of(kobj, struct damon_sysfs_filters, kobj); + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + err = damon_sysfs_filters_add_dirs(filters, nr); + mutex_unlock(&damon_sysfs_lock); + if (err) + return err; + + return count; +} + static void damon_sysfs_filters_release(struct kobject *kobj) { kfree(container_of(kobj, struct damon_sysfs_filters, kobj)); } +static struct kobj_attribute damon_sysfs_filters_nr_attr = + __ATTR_RW_MODE(nr_filters, 0600); + static struct attribute *damon_sysfs_filters_attrs[] = { + &damon_sysfs_filters_nr_attr.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_filters); @@ -811,7 +931,10 @@ static int damon_sysfs_probe_add_dirs(struct damon_sysfs_probe *attr) static void damon_sysfs_probe_rm_dirs(struct damon_sysfs_probe *attr) { - kobject_put(&attr->filters->kobj); + if (attr->filters) { + damon_sysfs_filters_rm_dirs(attr->filters); + kobject_put(&attr->filters->kobj); + } } static void damon_sysfs_probe_release(struct kobject *kobj) From 8caba144827849293a65169c9e138b6353156285 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:01 -0700 Subject: [PATCH 2609/5214] mm/damon/sysfs: implement filter dir files Implement sysfs files under the data probe filter directory for letting users to configure each filter. Link: https://lore.kernel.org/20260518234119.97569-14-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 2dc475ea0f0f..51a4f05c9275 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -753,6 +753,9 @@ static const struct kobj_type damon_sysfs_intervals_ktype = { struct damon_sysfs_filter { struct kobject kobj; + enum damon_filter_type type; + bool matching; + bool allow; }; static struct damon_sysfs_filter *damon_sysfs_filter_alloc(void) @@ -760,6 +763,105 @@ static struct damon_sysfs_filter *damon_sysfs_filter_alloc(void) return kzalloc_obj(struct damon_sysfs_filter); } +struct damon_sysfs_filter_type_name { + enum damon_filter_type type; + char *name; +}; + +static const struct damon_sysfs_filter_type_name +damon_sysfs_filter_type_names[] = { + { + .type = DAMON_FILTER_TYPE_ANON, + .name = "anon", + }, +}; + +static ssize_t type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + int i; + + for (i = 0; i < ARRAY_SIZE(damon_sysfs_filter_type_names); i++) { + const struct damon_sysfs_filter_type_name *type_name; + + type_name = &damon_sysfs_filter_type_names[i]; + if (type_name->type == filter->type) + return sysfs_emit(buf, "%s\n", type_name->name); + } + return -EINVAL; +} + +static ssize_t type_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + ssize_t ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(damon_sysfs_filter_type_names); i++) { + const struct damon_sysfs_filter_type_name *type_name; + + type_name = &damon_sysfs_filter_type_names[i]; + if (sysfs_streq(buf, type_name->name)) { + filter->type = type_name->type; + ret = count; + break; + } + } + return ret; +} + +static ssize_t matching_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + + return sysfs_emit(buf, "%c\n", filter->matching ? 'Y' : 'N'); +} + +static ssize_t matching_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + bool matching; + int err = kstrtobool(buf, &matching); + + if (err) + return err; + + filter->matching = matching; + return count; +} + +static ssize_t allow_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + + return sysfs_emit(buf, "%c\n", filter->allow ? 'Y' : 'N'); +} + +static ssize_t allow_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + bool allow; + int err = kstrtobool(buf, &allow); + + if (err) + return err; + + filter->allow = allow; + return count; +} + static void damon_sysfs_filter_release(struct kobject *kobj) { struct damon_sysfs_filter *filter = container_of(kobj, @@ -768,7 +870,19 @@ static void damon_sysfs_filter_release(struct kobject *kobj) kfree(filter); } +static struct kobj_attribute damon_sysfs_filter_type_attr = + __ATTR_RW_MODE(type, 0600); + +static struct kobj_attribute damon_sysfs_filter_matching_attr = + __ATTR_RW_MODE(matching, 0600); + +static struct kobj_attribute damon_sysfs_filter_allow_attr = + __ATTR_RW_MODE(allow, 0600); + static struct attribute *damon_sysfs_filter_attrs[] = { + &damon_sysfs_filter_type_attr.attr, + &damon_sysfs_filter_matching_attr.attr, + &damon_sysfs_filter_allow_attr.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_filter); From 24e969aa296c1b02b797420a428315a525540420 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:02 -0700 Subject: [PATCH 2610/5214] mm/damon/sysfs: setup probes on DAMON core API parameters Add user-installed data probes to DAMON core API parameters, so that user inputs for data probes are passed to DAMON core. Link: https://lore.kernel.org/20260518234119.97569-15-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 51a4f05c9275..eeb7fdd030cf 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1855,6 +1855,40 @@ static int damon_sysfs_set_attrs(struct damon_ctx *ctx, return damon_set_attrs(ctx, &attrs); } +static int damon_sysfs_set_probes(struct damon_ctx *ctx, + struct damon_sysfs_probes *sys_probes) +{ + int i; + + for (i = 0; i < sys_probes->nr; i++) { + struct damon_sysfs_filters *sys_filters = + sys_probes->probes_arr[i]->filters; + struct damon_probe *c; + int j; + + if (!sys_filters) + continue; + c = damon_new_probe(); + if (!c) + return -ENOMEM; + damon_add_probe(ctx, c); + + for (j = 0; j < sys_filters->nr; j++) { + struct damon_sysfs_filter *sys_filter = + sys_filters->filters_arr[j]; + struct damon_filter *filter; + + filter = damon_new_filter(sys_filter->type, + sys_filter->matching, + sys_filter->allow); + if (!filter) + return -ENOMEM; + damon_add_filter(c, filter); + } + } + return 0; +} + static int damon_sysfs_set_regions(struct damon_target *t, struct damon_sysfs_regions *sysfs_regions, unsigned long min_region_sz) @@ -1967,6 +2001,9 @@ static int damon_sysfs_apply_inputs(struct damon_ctx *ctx, DAMON_MIN_REGION_SZ / sys_ctx->addr_unit, 1); ctx->pause = sys_ctx->pause; err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs); + if (err) + return err; + err = damon_sysfs_set_probes(ctx, sys_ctx->attrs->probes); if (err) return err; err = damon_sysfs_add_targets(ctx, sys_ctx->targets); From b574a82d10de9c32ddc005c6a5d92e037f35ed43 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:03 -0700 Subject: [PATCH 2611/5214] mm/damon/sysfs-schemes: implement tried_regions//probes/ Implement a sysfs directory for showing the per-region probe hit counts. It is named 'probes/' and located under the DAMOS tried region directory. Link: https://lore.kernel.org/20260518234119.97569-16-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 67 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 0d3021db0b99..3b66c3a757b2 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -10,6 +10,32 @@ #include "sysfs-common.h" +/* + * probes directory + */ + +struct damos_sysfs_probes { + struct kobject kobj; +}; + +static struct damos_sysfs_probes *damos_sysfs_probes_alloc(void) +{ + return kzalloc_obj(struct damos_sysfs_probes); +} + +static void damos_sysfs_probes_release(struct kobject *kobj) +{ + struct damos_sysfs_probes *probes = container_of(kobj, + struct damos_sysfs_probes, kobj); + + kfree(probes); +} + +static const struct kobj_type damos_sysfs_probes_ktype = { + .release = damos_sysfs_probes_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + /* * scheme region directory */ @@ -20,6 +46,7 @@ struct damon_sysfs_scheme_region { unsigned int nr_accesses; unsigned int age; unsigned long sz_filter_passed; + struct damos_sysfs_probes *probes; struct list_head list; }; @@ -34,10 +61,36 @@ static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc( sysfs_region->ar = region->ar; sysfs_region->nr_accesses = region->nr_accesses_bp / 10000; sysfs_region->age = region->age; + sysfs_region->probes = NULL; INIT_LIST_HEAD(&sysfs_region->list); return sysfs_region; } +static int damos_sysfs_region_add_dirs( + struct damon_sysfs_scheme_region *region) +{ + struct damos_sysfs_probes *probes = damos_sysfs_probes_alloc(); + int err; + + if (!probes) + return -ENOMEM; + err = kobject_init_and_add(&probes->kobj, &damos_sysfs_probes_ktype, + ®ion->kobj, "probes"); + if (err) { + kobject_put(&probes->kobj); + return err; + } + + region->probes = probes; + return 0; +} + +static void damos_sysfs_region_rm_dirs( + struct damon_sysfs_scheme_region *region) +{ + kobject_put(®ion->probes->kobj); +} + static ssize_t start_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -163,6 +216,7 @@ static void damon_sysfs_scheme_regions_rm_dirs( struct damon_sysfs_scheme_region *r, *next; list_for_each_entry_safe(r, next, ®ions->regions_list, list) { + damos_sysfs_region_rm_dirs(r); list_del(&r->list); kobject_put(&r->kobj); regions->nr_regions--; @@ -2995,12 +3049,17 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes, if (kobject_init_and_add(®ion->kobj, &damon_sysfs_scheme_region_ktype, &sysfs_regions->kobj, "%d", - sysfs_regions->nr_regions)) { - kobject_put(®ion->kobj); - return; - } + sysfs_regions->nr_regions)) + goto out; + if (damos_sysfs_region_add_dirs(region)) + goto out; + list_add_tail(®ion->list, &sysfs_regions->regions_list); sysfs_regions->nr_regions++; + return; + +out: + kobject_put(®ion->kobj); } int damon_sysfs_schemes_clear_regions( From a1536db4dc8b9045e4ab13da4fe44b3d2b68f8ed Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:04 -0700 Subject: [PATCH 2612/5214] mm/damon/sysfs-schemes: implement probe dir Implement sysfs directory for showing per-probe hits count of each region. Link: https://lore.kernel.org/20260518234119.97569-17-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 101 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 3b66c3a757b2..7e21e78d7751 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -10,12 +10,40 @@ #include "sysfs-common.h" +/* + * probe directory + */ + +struct damos_sysfs_probe { + struct kobject kobj; +}; + +static struct damos_sysfs_probe *damos_sysfs_probe_alloc(void) +{ + return kzalloc_obj(struct damos_sysfs_probe); +} + +static void damos_sysfs_probe_release(struct kobject *kobj) +{ + struct damos_sysfs_probe *probe = container_of(kobj, + struct damos_sysfs_probe, kobj); + + kfree(probe); +} + +static const struct kobj_type damos_sysfs_probe_ktype = { + .release = damos_sysfs_probe_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + /* * probes directory */ struct damos_sysfs_probes { struct kobject kobj; + struct damos_sysfs_probe **probes_arr; + int nr; }; static struct damos_sysfs_probes *damos_sysfs_probes_alloc(void) @@ -23,6 +51,60 @@ static struct damos_sysfs_probes *damos_sysfs_probes_alloc(void) return kzalloc_obj(struct damos_sysfs_probes); } +static void damos_sysfs_probes_rm_dirs(struct damos_sysfs_probes *probes) +{ + struct damos_sysfs_probe **probes_arr = probes->probes_arr; + int i; + + for (i = 0; i < probes->nr; i++) + kobject_put(&probes_arr[i]->kobj); + probes->nr = 0; + kfree(probes_arr); + probes->probes_arr = NULL; +} + +static int damos_sysfs_probes_add_dirs(struct damos_sysfs_probes *probes, + struct damon_ctx *ctx) +{ + struct damon_probe *probe; + struct damos_sysfs_probe **probes_arr; + int i = 0; + + damon_for_each_probe(probe, ctx) + i++; + + if (!i) + return 0; + + probes_arr = kmalloc_objs(*probes_arr, i); + if (!probes_arr) + return -ENOMEM; + probes->probes_arr = probes_arr; + + i = 0; + damon_for_each_probe(probe, ctx) { + struct damos_sysfs_probe *sys_probe; + int err; + + sys_probe = damos_sysfs_probe_alloc(); + if (!sys_probe) { + damos_sysfs_probes_rm_dirs(probes); + return -ENOMEM; + } + err = kobject_init_and_add(&sys_probe->kobj, + &damos_sysfs_probe_ktype, &probes->kobj, "%d", + i); + if (err) { + kobject_put(&sys_probe->kobj); + damos_sysfs_probes_rm_dirs(probes); + return err; + } + probes_arr[i++] = sys_probe; + probes->nr++; + } + return 0; +} + static void damos_sysfs_probes_release(struct kobject *kobj) { struct damos_sysfs_probes *probes = container_of(kobj, @@ -67,7 +149,8 @@ static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc( } static int damos_sysfs_region_add_dirs( - struct damon_sysfs_scheme_region *region) + struct damon_sysfs_scheme_region *region, + struct damon_ctx *ctx) { struct damos_sysfs_probes *probes = damos_sysfs_probes_alloc(); int err; @@ -76,18 +159,24 @@ static int damos_sysfs_region_add_dirs( return -ENOMEM; err = kobject_init_and_add(&probes->kobj, &damos_sysfs_probes_ktype, ®ion->kobj, "probes"); - if (err) { - kobject_put(&probes->kobj); - return err; - } + if (err) + goto fail; + err = damos_sysfs_probes_add_dirs(probes, ctx); + if (err) + goto fail; region->probes = probes; return 0; + +fail: + kobject_put(&probes->kobj); + return err; } static void damos_sysfs_region_rm_dirs( struct damon_sysfs_scheme_region *region) { + damos_sysfs_probes_rm_dirs(region->probes); kobject_put(®ion->probes->kobj); } @@ -3051,7 +3140,7 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes, &sysfs_regions->kobj, "%d", sysfs_regions->nr_regions)) goto out; - if (damos_sysfs_region_add_dirs(region)) + if (damos_sysfs_region_add_dirs(region, ctx)) goto out; list_add_tail(®ion->list, &sysfs_regions->regions_list); From 5b0de1bc3325c34e341fe0f5314292c57b4616b9 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:05 -0700 Subject: [PATCH 2613/5214] mm/damon/sysfs-schemes: implement probe/hits file Implement sysfs file for showing the per-region per-probe hits count. Link: https://lore.kernel.org/20260518234119.97569-18-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 41 +++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 7e21e78d7751..e25f4824b72f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -16,11 +16,27 @@ struct damos_sysfs_probe { struct kobject kobj; + unsigned char hits; }; -static struct damos_sysfs_probe *damos_sysfs_probe_alloc(void) +static struct damos_sysfs_probe *damos_sysfs_probe_alloc(unsigned char hits) { - return kzalloc_obj(struct damos_sysfs_probe); + struct damos_sysfs_probe *probe; + + probe = kzalloc_obj(*probe); + if (!probe) + return NULL; + probe->hits = hits; + return probe; +} + +static ssize_t hits_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct damos_sysfs_probe *probe = container_of(kobj, + struct damos_sysfs_probe, kobj); + + return sysfs_emit(buf, "%hhu\n", probe->hits); } static void damos_sysfs_probe_release(struct kobject *kobj) @@ -31,9 +47,19 @@ static void damos_sysfs_probe_release(struct kobject *kobj) kfree(probe); } +static struct kobj_attribute damos_sysfs_probe_hits_attr = + __ATTR_RO_MODE(hits, 0400); + +static struct attribute *damos_sysfs_probe_attrs[] = { + &damos_sysfs_probe_hits_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damos_sysfs_probe); + static const struct kobj_type damos_sysfs_probe_ktype = { .release = damos_sysfs_probe_release, .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damos_sysfs_probe_groups, }; /* @@ -64,7 +90,7 @@ static void damos_sysfs_probes_rm_dirs(struct damos_sysfs_probes *probes) } static int damos_sysfs_probes_add_dirs(struct damos_sysfs_probes *probes, - struct damon_ctx *ctx) + struct damon_ctx *ctx, struct damon_region *region) { struct damon_probe *probe; struct damos_sysfs_probe **probes_arr; @@ -86,7 +112,7 @@ static int damos_sysfs_probes_add_dirs(struct damos_sysfs_probes *probes, struct damos_sysfs_probe *sys_probe; int err; - sys_probe = damos_sysfs_probe_alloc(); + sys_probe = damos_sysfs_probe_alloc(region->probe_hits[i]); if (!sys_probe) { damos_sysfs_probes_rm_dirs(probes); return -ENOMEM; @@ -150,7 +176,8 @@ static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc( static int damos_sysfs_region_add_dirs( struct damon_sysfs_scheme_region *region, - struct damon_ctx *ctx) + struct damon_ctx *ctx, + struct damon_region *dregion) { struct damos_sysfs_probes *probes = damos_sysfs_probes_alloc(); int err; @@ -161,7 +188,7 @@ static int damos_sysfs_region_add_dirs( ®ion->kobj, "probes"); if (err) goto fail; - err = damos_sysfs_probes_add_dirs(probes, ctx); + err = damos_sysfs_probes_add_dirs(probes, ctx, dregion); if (err) goto fail; @@ -3140,7 +3167,7 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes, &sysfs_regions->kobj, "%d", sysfs_regions->nr_regions)) goto out; - if (damos_sysfs_region_add_dirs(region, ctx)) + if (damos_sysfs_region_add_dirs(region, ctx, r)) goto out; list_add_tail(®ion->list, &sysfs_regions->regions_list); From b9b7bad279de29294c4d3314fe90fca345c38ea6 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:06 -0700 Subject: [PATCH 2614/5214] mm/damon: trace probe_hits Introduce a new tracepoint for exposing the per-region per-probe positive sample count via tracefs. Link: https://lore.kernel.org/20260518234119.97569-19-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/trace/events/damon.h | 38 ++++++++++++++++++++++++++++++++++++ mm/damon/core.c | 9 +++++++++ 2 files changed, 47 insertions(+) diff --git a/include/trace/events/damon.h b/include/trace/events/damon.h index 7e25f4469b81..78388538acf4 100644 --- a/include/trace/events/damon.h +++ b/include/trace/events/damon.h @@ -130,6 +130,44 @@ TRACE_EVENT(damon_monitor_intervals_tune, TP_printk("sample_us=%lu", __entry->sample_us) ); +TRACE_EVENT_CONDITION(damon_region_aggregated, + + TP_PROTO(unsigned int target_id, struct damon_region *r, + unsigned int nr_regions, unsigned int nr_probes), + + TP_ARGS(target_id, r, nr_regions, nr_probes), + + TP_CONDITION(nr_probes > 0), + + TP_STRUCT__entry( + __field(unsigned long, target_id) + __field(unsigned long, start) + __field(unsigned long, end) + __field(unsigned int, nr_regions) + __field(unsigned int, nr_accesses) + __field(unsigned int, age) + __dynamic_array(unsigned char, probe_hits, nr_probes) + ), + + TP_fast_assign( + __entry->target_id = target_id; + __entry->start = r->ar.start; + __entry->end = r->ar.end; + __entry->nr_regions = nr_regions; + __entry->nr_accesses = r->nr_accesses; + __entry->age = r->age; + memcpy(__get_dynamic_array(probe_hits), r->probe_hits, + sizeof(*r->probe_hits) * nr_probes); + ), + + TP_printk("target_id=%lu nr_regions=%u %lu-%lu: %u %u probe_hits=%s", + __entry->target_id, __entry->nr_regions, + __entry->start, __entry->end, + __entry->nr_accesses, __entry->age, + __print_hex(__get_dynamic_array(probe_hits), + __get_dynamic_array_len(probe_hits))) +); + TRACE_EVENT(damon_aggregated, TP_PROTO(unsigned int target_id, struct damon_region *r, diff --git a/mm/damon/core.c b/mm/damon/core.c index 500e8b08d441..903fd6fc9789 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1905,6 +1905,13 @@ static void kdamond_reset_aggregated(struct damon_ctx *c) { struct damon_target *t; unsigned int ti = 0; /* target's index */ + unsigned int nr_probes = 0; + struct damon_probe *probe; + + if (trace_damon_region_aggregated_enabled()) { + damon_for_each_probe(probe, c) + nr_probes++; + } damon_for_each_target(t, c) { struct damon_region *r; @@ -1913,6 +1920,8 @@ static void kdamond_reset_aggregated(struct damon_ctx *c) int i; trace_damon_aggregated(ti, r, damon_nr_regions(t)); + trace_damon_region_aggregated(ti, r, + damon_nr_regions(t), nr_probes); damon_warn_fix_nr_accesses_corruption(r); r->last_nr_accesses = r->nr_accesses; r->nr_accesses = 0; From 14885da09b0f3350004c80202fbe533d50336c8c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:07 -0700 Subject: [PATCH 2615/5214] selftests/damon/sysfs.sh: test probes dir Add simple existence tests for data probes sysfs directories and files. Link: https://lore.kernel.org/20260518234119.97569-20-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.sh | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index 83e3b7f63d81..1ac3e2ce8e44 100755 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -291,11 +291,59 @@ test_intervals() ensure_file "$intervals_dir/update_us" "exist" "600" } +test_damon_filter() +{ + damon_filter_dir=$1 + ensure_file "$damon_filter_dir/type" "exist" "600" + ensure_write_succ "$damon_filter_dir/type" "anon" "valid input" + ensure_write_fail "$damon_filter_dir/type" "foo" "invalid input" + ensure_file "$damon_filter_dir/matching" "exist" "600" + ensure_file "$damon_filter_dir/allow" "exist" "600" +} + +test_damon_filters() +{ + filters_dir=$1 + ensure_dir "$filters_dir" "exist" + ensure_file "$filters_dir/nr_filters" "exist" "600" + ensure_write_succ "$filters_dir/nr_filters" "1" "valid input" + test_damon_filter "$filters_dir/0" + + ensure_write_succ "$filters_dir/nr_filters" "2" "valid input" + test_damon_filter "$filters_dir/0" + test_damon_filter "$filters_dir/1" + + ensure_write_succ "$filters_dir/nr_filters" "0" "valid input" + ensure_dir "$filters_dir/0" "not_exist" + ensure_dir "$filters_dir/1" "not_exist" +} + +test_probe() +{ + probe_dir=$1 + ensure_dir "$probe_dir" "exist" + test_damon_filters "$probe_dir/filters" +} + +test_probes() +{ + probes_dir=$1 + ensure_dir "$probes_dir" "exist" + ensure_file "$probes_dir/nr_probes" "exist" "600" + + ensure_write_succ "$probes_dir/nr_probes" "1" "valid input" + test_probe "$probes_dir/0" + + ensure_write_succ "$probes_dir/nr_probes" "0" "valid input" + ensure_dir "$probes_dir/0" "not_exist" +} + test_monitoring_attrs() { monitoring_attrs_dir=$1 ensure_dir "$monitoring_attrs_dir" "exist" test_intervals "$monitoring_attrs_dir/intervals" + test_probes "$monitoring_attrs_dir/probes" test_range "$monitoring_attrs_dir/nr_regions" } From f4e98954234b104c23902ee5bb4e59be6f9904a7 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:08 -0700 Subject: [PATCH 2616/5214] Docs/mm/damon/design: document data attributes monitoring Update DAMON design document for newly added data attributes monitoring feature. Link: https://lore.kernel.org/20260518234119.97569-21-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index fa7392b5a331..6731c3102d0f 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -276,6 +276,43 @@ interval``, DAMON checks if the region's size and access frequency (``nr_accesses``) has significantly changed. If so, the counter is reset to zero. Otherwise, the counter is increased. +Data Attributes Monitoring +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Data access pattern is only one type of data attributes. In some use cases, +users need to know more data attributes information. For example, users may +need to know how much of a given hot or cold memory region is backed by +anonymous pages, or belong to a specific cgroup. For such use case, data +attributes monitoring feature is provided. + +Using the feature, users can register data attributes of their interest to the +DAMON :ref:`context `. The +registration is made by specifying a probe per attribute. Each of the probe +specifies a rule to determine if a given memory region has the related +attribute. The rule is constructed with multiple filters. The filters work +same to :ref:`DAMOS filters ` except the supported +filter types. Currently only ``anon`` filter type is supported for data +attributes monitoring. + +If such probes are registered, DAMON executes the probes for each region's +sampling memory when it does the access :ref:`sampling +`. The number of samples that identified +as having the data attribute (hitting the probe) per :ref:`aggregation interval +` is accounted in a per-region per-probe counter. +Users can therefore know how much of a given DAMON region has a specific data +attribute by reading the per-region per-probe probe hits counter after each +aggregation interval. + +This is a sampling based mechanism. Hence, it is lightweight but the output +may include some measurement errors. The output should be used with good +understanding of statistics. + +Another way to do this for higher accuracy is using :ref:`DAMOS filter +` with ``stat`` :ref:`action +` and ``sz_ops_filter_passed`` :ref:`stat +`. This approach provides the data attributes +information in page level. But, because it is operated in page level, the +overhead is proportional to the size of the memory. Dynamic Target Space Updates Handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 69a743520114b2a9c47db37059d25abe2a84e8f5 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:09 -0700 Subject: [PATCH 2617/5214] Docs/admin-guide/mm/damon/usage: document data attributes monitoring Update DAMON usage document for the newly added data attributes monitoring feature. Link: https://lore.kernel.org/20260518234119.97569-22-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 44 ++++++++++++++++++-- Documentation/mm/damon/design.rst | 2 + 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index 11c75a598393..5cf55ff6de31 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -72,6 +72,11 @@ comma (","). │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us │ │ │ │ │ │ │ intervals_goal/access_bp,aggrs,min_sample_us,max_sample_us │ │ │ │ │ │ nr_regions/min,max + │ │ │ │ │ │ :ref:`probes `/nr_probes + │ │ │ │ │ │ │ 0/filters/nr_filters + │ │ │ │ │ │ │ │ 0/type,matching,allow + │ │ │ │ │ │ │ │ ... + │ │ │ │ │ │ │ ... │ │ │ │ │ :ref:`targets `/nr_targets │ │ │ │ │ │ :ref:`0 `/pid_target,obsolete_target │ │ │ │ │ │ │ :ref:`regions `/nr_regions @@ -98,6 +103,9 @@ comma (","). │ │ │ │ │ │ │ :ref:`stats `/nr_tried,sz_tried,nr_applied,sz_applied,sz_ops_filter_passed,qt_exceeds,nr_snapshots,max_nr_snapshots │ │ │ │ │ │ │ :ref:`tried_regions `/total_bytes │ │ │ │ │ │ │ │ 0/start,end,nr_accesses,age,sz_filter_passed + │ │ │ │ │ │ │ │ │ probes + │ │ │ │ │ │ │ │ │ │ 0/hits + │ │ │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ ... │ │ │ │ ... @@ -227,8 +235,8 @@ contexts//monitoring_attrs/ Files for specifying attributes of the monitoring including required quality and efficiency of the monitoring are in ``monitoring_attrs`` directory. -Specifically, two directories, ``intervals`` and ``nr_regions`` exist in this -directory. +Specifically, three directories, ``intervals``, ``nr_regions`` and ``probes`` +exist in this directory. Under ``intervals`` directory, three files for DAMON's sampling interval (``sample_us``), aggregation interval (``aggr_us``), and update interval @@ -262,6 +270,27 @@ tuning-applied current values of the two intervals can be read from the ``sample_us`` and ``aggr_us`` files after writing ``update_tuned_intervals`` to the ``state`` file. +.. _damon_usage_sysfs_probes: + +contexts//monitoring_attrs/probes/ +------------------------------------- + +A directory for registering :ref:`data attributes monitoring +` probes. + +In the beginning, this directory has only one file, ``nr_probes``. Writing a +number (``N``) to the file creates the number of child directories named ``0`` +to ``N-1``. Each directory represents each monitoring probe. + +In each probe directory, one directory, ``filters`` exists. The directory +contains files for installing filters for the probe, that is used to determine +the data attribute for the probe. + +In the beginning, ``filters`` directory has only one file, ``nr_filters``. +Writing a number (``N``) to the file creates the number of child directories +named ``0`` to ``N-1``. Each directory represents each filter and works in a +way similar to that for :ref:`DAMOS filter `. + .. _sysfs_targets: contexts//targets/ @@ -615,10 +644,19 @@ tried_regions// ------------------ In each region directory, you will find five files (``start``, ``end``, -``nr_accesses``, ``age``, and ``sz_filter_passed``). Reading the files will +``nr_accesses``, ``age`` and ``sz_filter_passed``). Reading the files will show the properties of the region that corresponding DAMON-based operation scheme ``action`` has tried to be applied. +tried_regions//probes/ +------------------------- + +In each region directory, one directory (``probes``) also exists. In the +directory, subdirectories named ``0`` to ``N-1`` exists. ``N`` is the number +of installed probes. In each number-named directory, a file (``hits``) exist. +Reading the file shows the number of data attributes monitoring probe-hit +positive samples of the region. + Example ~~~~~~~ diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index 6731c3102d0f..887b45cbeb71 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -276,6 +276,8 @@ interval``, DAMON checks if the region's size and access frequency (``nr_accesses``) has significantly changed. If so, the counter is reset to zero. Otherwise, the counter is increased. +.. _damon_design_data_attrs_monitoring: + Data Attributes Monitoring ~~~~~~~~~~~~~~~~~~~~~~~~~~ From d9f23f2f822a59771fdc3cab648785d4f651e1b2 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:10 -0700 Subject: [PATCH 2618/5214] mm/damon/core: introduce DAMON_FILTER_TYPE_MEMCG Belonging memory cgoup is another data attribute that can be useful to monitor. Introduce a new DAMON filter type, namely DAMON_FILTER_TYPE_MEMCG, for monitoring of this attribute. Link: https://lore.kernel.org/20260518234119.97569-23-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 6 ++++++ mm/damon/core.c | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 1fb271a35e98..6a54c601889b 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -742,9 +742,11 @@ struct damon_intervals_goal { * enum damon_filter_type - Type of &struct damon_filter * * @DAMON_FILTER_TYPE_ANON: Anonymous pages. + * @DAMON_FILTER_TYPE_MEMCG: Specific memcg's pages. */ enum damon_filter_type { DAMON_FILTER_TYPE_ANON, + DAMON_FILTER_TYPE_MEMCG, }; /** @@ -753,12 +755,16 @@ enum damon_filter_type { * @type: Type of the region. * @matching: Whether this filter is for the type-matching ones. * @allow: Whether the @type-@matching ones should pass this filter. + * @memcg_id: Memcg id of the question if @type is DAMON_FILTER_MEMCG. * @list: Siblings list. */ struct damon_filter { enum damon_filter_type type; bool matching; bool allow; + union { + u64 memcg_id; + }; struct list_head list; }; diff --git a/mm/damon/core.c b/mm/damon/core.c index 903fd6fc9789..9a5a835a4d3f 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1430,6 +1430,13 @@ static void damon_commit_filter(struct damon_filter *dst, dst->type = src->type; dst->matching = src->matching; dst->allow = src->allow; + switch (dst->type) { + case DAMON_FILTER_TYPE_MEMCG: + dst->memcg_id = src->memcg_id; + break; + default: + break; + } } static int damon_commit_filters(struct damon_probe *dst, @@ -1454,6 +1461,13 @@ static int damon_commit_filters(struct damon_probe *dst, src_filter->matching, src_filter->allow); if (!new_filter) return -ENOMEM; + switch (src_filter->type) { + case DAMON_FILTER_TYPE_MEMCG: + new_filter->memcg_id = src_filter->memcg_id; + break; + default: + break; + } damon_add_filter(dst, new_filter); } return 0; From ba3be5430ffa7e5debec2e0fe61518a2db0489ca Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:11 -0700 Subject: [PATCH 2619/5214] mm/damon/paddr: support DAMON_FILTER_TYPE_MEMCG Implement the support of DAMON_FILTER_TYPE_MEMCG on the DAMON operation set implementation for the physical address space. Link: https://lore.kernel.org/20260518234119.97569-24-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/paddr.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 9997c5174ef1..d0598f5f2688 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -124,6 +124,7 @@ static bool damon_pa_filter_match(struct damon_filter *filter, struct folio *folio) { bool matched = false; + struct mem_cgroup *memcg; switch (filter->type) { case DAMON_FILTER_TYPE_ANON: @@ -133,6 +134,19 @@ static bool damon_pa_filter_match(struct damon_filter *filter, } matched = folio_test_anon(folio); break; + case DAMON_FILTER_TYPE_MEMCG: + if (!folio) { + matched = false; + break; + } + rcu_read_lock(); + memcg = folio_memcg_check(folio); + if (!memcg) + matched = false; + else + matched = filter->memcg_id == mem_cgroup_id(memcg); + rcu_read_unlock(); + break; default: break; } From c71f8e13462d6eab9928f579c15c0a4b16abab84 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:12 -0700 Subject: [PATCH 2620/5214] mm/damon/sysfs: add filters//path file Introduce a new DAMON sysfs file for letting users setup the target memory cgroup of the belonging memory cgroup attribute monitoring. The file is named 'path', located under the probe filter directory. Users can set the target memory cgroup by writing the path to the memory cgroup from the cgroup mount point to the file. Link: https://lore.kernel.org/20260518234119.97569-25-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index eeb7fdd030cf..0f6379caf481 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -756,6 +756,7 @@ struct damon_sysfs_filter { enum damon_filter_type type; bool matching; bool allow; + char *path; }; static struct damon_sysfs_filter *damon_sysfs_filter_alloc(void) @@ -774,6 +775,10 @@ damon_sysfs_filter_type_names[] = { .type = DAMON_FILTER_TYPE_ANON, .name = "anon", }, + { + .type = DAMON_FILTER_TYPE_MEMCG, + .name = "memcg", + }, }; static ssize_t type_show(struct kobject *kobj, @@ -862,11 +867,46 @@ static ssize_t allow_store(struct kobject *kobj, return count; } +static ssize_t path_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + int len; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->path ? filter->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; +} + +static ssize_t path_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_filter *filter = container_of(kobj, + struct damon_sysfs_filter, kobj); + char *path = kmalloc_objs(*path, size_add(count, 1)); + + if (!path) + return -ENOMEM; + strscpy(path, buf, size_add(count, 1)); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } + kfree(filter->path); + filter->path = path; + mutex_unlock(&damon_sysfs_lock); + return count; +} + static void damon_sysfs_filter_release(struct kobject *kobj) { struct damon_sysfs_filter *filter = container_of(kobj, struct damon_sysfs_filter, kobj); + kfree(filter->path); kfree(filter); } @@ -879,10 +919,14 @@ static struct kobj_attribute damon_sysfs_filter_matching_attr = static struct kobj_attribute damon_sysfs_filter_allow_attr = __ATTR_RW_MODE(allow, 0600); +static struct kobj_attribute damon_sysfs_filter_path_attr = + __ATTR_RW_MODE(path, 0600); + static struct attribute *damon_sysfs_filter_attrs[] = { &damon_sysfs_filter_type_attr.attr, &damon_sysfs_filter_matching_attr.attr, &damon_sysfs_filter_allow_attr.attr, + &damon_sysfs_filter_path_attr.attr, NULL, }; ATTRIBUTE_GROUPS(damon_sysfs_filter); From b2025ce0662b186b3158c25f7f9c25b4e6931acc Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:13 -0700 Subject: [PATCH 2621/5214] mm/damon/sysfs-schemes: move memcg_path_to_id() to sysfs-common The next commit will need to find the memcg id from the user-passed path to the memory cgroup, from sysfs.c. memcg_path_to_id() is doing that, but defined in sysfs-schemes.c as a static function. Move the function to sysfs-common.c and mark it as non-static, so that the next commit can reuse the function. Link: https://lore.kernel.org/20260518234119.97569-26-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/damon/sysfs-common.c | 41 ++++++++++++++++++++++++++++++++++++++++ mm/damon/sysfs-common.h | 2 ++ mm/damon/sysfs-schemes.c | 41 ---------------------------------------- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/mm/damon/sysfs-common.c b/mm/damon/sysfs-common.c index 83e24a9b5a0d..bdc6ae2639e4 100644 --- a/mm/damon/sysfs-common.c +++ b/mm/damon/sysfs-common.c @@ -104,3 +104,44 @@ const struct kobj_type damon_sysfs_ul_range_ktype = { .default_groups = damon_sysfs_ul_range_groups, }; + +static bool damon_sysfs_memcg_path_eq(struct mem_cgroup *memcg, + char *memcg_path_buf, char *path) +{ +#ifdef CONFIG_MEMCG + cgroup_path(memcg->css.cgroup, memcg_path_buf, PATH_MAX); + if (sysfs_streq(memcg_path_buf, path)) + return true; +#endif /* CONFIG_MEMCG */ + return false; +} + +int damon_sysfs_memcg_path_to_id(char *memcg_path, u64 *id) +{ + struct mem_cgroup *memcg; + char *path; + bool found = false; + + if (!memcg_path) + return -EINVAL; + + path = kmalloc_array(PATH_MAX, sizeof(*path), GFP_KERNEL); + if (!path) + return -ENOMEM; + + for (memcg = mem_cgroup_iter(NULL, NULL, NULL); memcg; + memcg = mem_cgroup_iter(NULL, memcg, NULL)) { + /* skip offlined memcg */ + if (!mem_cgroup_online(memcg)) + continue; + if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) { + *id = mem_cgroup_id(memcg); + found = true; + mem_cgroup_iter_break(NULL, memcg); + break; + } + } + + kfree(path); + return found ? 0 : -EINVAL; +} diff --git a/mm/damon/sysfs-common.h b/mm/damon/sysfs-common.h index 2099adee11d0..3079306966a9 100644 --- a/mm/damon/sysfs-common.h +++ b/mm/damon/sysfs-common.h @@ -59,3 +59,5 @@ int damos_sysfs_set_quota_scores(struct damon_sysfs_schemes *sysfs_schemes, void damos_sysfs_update_effective_quotas( struct damon_sysfs_schemes *sysfs_schemes, struct damon_ctx *ctx); + +int damon_sysfs_memcg_path_to_id(char *memcg_path, u64 *id); diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index e25f4824b72f..329cfd0bbe9f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2791,47 +2791,6 @@ const struct kobj_type damon_sysfs_schemes_ktype = { .default_groups = damon_sysfs_schemes_groups, }; -static bool damon_sysfs_memcg_path_eq(struct mem_cgroup *memcg, - char *memcg_path_buf, char *path) -{ -#ifdef CONFIG_MEMCG - cgroup_path(memcg->css.cgroup, memcg_path_buf, PATH_MAX); - if (sysfs_streq(memcg_path_buf, path)) - return true; -#endif /* CONFIG_MEMCG */ - return false; -} - -static int damon_sysfs_memcg_path_to_id(char *memcg_path, u64 *id) -{ - struct mem_cgroup *memcg; - char *path; - bool found = false; - - if (!memcg_path) - return -EINVAL; - - path = kmalloc_array(PATH_MAX, sizeof(*path), GFP_KERNEL); - if (!path) - return -ENOMEM; - - for (memcg = mem_cgroup_iter(NULL, NULL, NULL); memcg; - memcg = mem_cgroup_iter(NULL, memcg, NULL)) { - /* skip offlined memcg */ - if (!mem_cgroup_online(memcg)) - continue; - if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) { - *id = mem_cgroup_id(memcg); - found = true; - mem_cgroup_iter_break(NULL, memcg); - break; - } - } - - kfree(path); - return found ? 0 : -EINVAL; -} - static int damon_sysfs_add_scheme_filters(struct damos *scheme, struct damon_sysfs_scheme_filters *sysfs_filters) { From 543ab01db7ace5bb28972ac70f321d55cc4f0214 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:14 -0700 Subject: [PATCH 2622/5214] mm/damon/sysfs: setup damon_filter->memcg_id from path Find and set the memcg_id for damon_filter from the user-passed memory cgroup path when updating the DAMON input parameters. Link: https://lore.kernel.org/20260518234119.97569-27-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/damon.h | 1 + mm/damon/core.c | 2 +- mm/damon/sysfs.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 6a54c601889b..4014fd0d463c 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1006,6 +1006,7 @@ static inline unsigned long damon_sz_region(struct damon_region *r) struct damon_filter *damon_new_filter(enum damon_filter_type type, bool matching, bool allow); void damon_add_filter(struct damon_probe *probe, struct damon_filter *f); +void damon_destroy_filter(struct damon_filter *f); struct damon_probe *damon_new_probe(void); void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe); diff --git a/mm/damon/core.c b/mm/damon/core.c index 9a5a835a4d3f..4e223857a0f9 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -143,7 +143,7 @@ static void damon_free_filter(struct damon_filter *f) kfree(f); } -static void damon_destroy_filter(struct damon_filter *f) +void damon_destroy_filter(struct damon_filter *f) { damon_del_filter(f); damon_free_filter(f); diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 0f6379caf481..2e95e3bac774 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1927,6 +1927,17 @@ static int damon_sysfs_set_probes(struct damon_ctx *ctx, sys_filter->allow); if (!filter) return -ENOMEM; + if (filter->type == DAMON_FILTER_TYPE_MEMCG) { + int err; + + err = damon_sysfs_memcg_path_to_id( + sys_filter->path, + &filter->memcg_id); + if (err) { + damon_destroy_filter(filter); + return err; + } + } damon_add_filter(c, filter); } } From 2fd777ebdfaafaead833a04882cbe8b1cdc5bdf1 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:15 -0700 Subject: [PATCH 2623/5214] Docs/mm/damon/design: update for memcg damon filter Update DAMON design document for the newly added belonging memory cgroup attribute monitoring feature. Link: https://lore.kernel.org/20260518234119.97569-28-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index 887b45cbeb71..a24f9f00d183 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -293,8 +293,8 @@ registration is made by specifying a probe per attribute. Each of the probe specifies a rule to determine if a given memory region has the related attribute. The rule is constructed with multiple filters. The filters work same to :ref:`DAMOS filters ` except the supported -filter types. Currently only ``anon`` filter type is supported for data -attributes monitoring. +filter types. Currently only ``anon`` and ``memcg`` filter types are supported +for data attributes monitoring. If such probes are registered, DAMON executes the probes for each region's sampling memory when it does the access :ref:`sampling From 9d3678808a3e575088f22db306a000c4f4458dfe Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 16:41:16 -0700 Subject: [PATCH 2624/5214] Docs/admin-guide/mm/damon/usage: update for memcg damon filter Update DAMON usage document for the newly added belonging memory cgroup attribute monitoring feature. Link: https://lore.kernel.org/20260518234119.97569-29-sj@kernel.org Signed-off-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index 5cf55ff6de31..0d6a27dc97b0 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -74,7 +74,7 @@ comma (","). │ │ │ │ │ │ nr_regions/min,max │ │ │ │ │ │ :ref:`probes `/nr_probes │ │ │ │ │ │ │ 0/filters/nr_filters - │ │ │ │ │ │ │ │ 0/type,matching,allow + │ │ │ │ │ │ │ │ 0/type,matching,allow,path │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ │ ... │ │ │ │ │ :ref:`targets `/nr_targets @@ -289,7 +289,9 @@ the data attribute for the probe. In the beginning, ``filters`` directory has only one file, ``nr_filters``. Writing a number (``N``) to the file creates the number of child directories named ``0`` to ``N-1``. Each directory represents each filter and works in a -way similar to that for :ref:`DAMOS filter `. +way similar to that for :ref:`DAMOS filter `. When the filter +``type`` is ``memcg``, ``path`` file acts as ``memcg_path`` for :ref:`DAMOS +filter `. .. _sysfs_targets: From 4f1839e22527f1621767721a90fa00425fbb0877 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Tue, 19 May 2026 17:42:14 +0530 Subject: [PATCH 2625/5214] mm/vmalloc: extract vm_area_free_pages() helper from vfree() Patch series "mm/vmalloc: free unused pages on vrealloc() shrink", v14. This series implements the TODO in vrealloc() to unmap and free unused pages when shrinking across a page boundary. Problem: When vrealloc() shrinks an allocation, it updates bookkeeping (requested_size, KASAN shadow) but does not free the underlying physical pages. This wastes memory for the lifetime of the allocation. Solution: - Patch 1: Extracts a vm_area_free_pages(vm, start_idx, end_idx) helper from vfree() that frees a range of pages with memcg and nr_vmalloc_pages accounting. Freed page pointers are set to NULL to prevent stale references. - Patch 2: Update the grow-in-place check in vrealloc() to compare the requested size against the actual physical page count (vm->nr_pages) rather than the virtual area sizes. This is a prerequisite for shrinking. - Patch 3: For VM_ALLOC areas in vread_iter(), derive the vm area size from vm->nr_pages rather than get_vm_area_size(), which would overestimate the mapped range after a shrink. Other mapping types (vmap, ioremap) don't set nr_pages and keep using get_vm_area_size(). - Patch 4: Uses the helper to free tail pages when vrealloc() shrinks across a page boundary. - Patch 5: Adds a vrealloc test case to lib/test_vmalloc that exercises grow-realloc, shrink-across-boundary, shrink-within-page, and grow-in-place paths. The virtual address reservation is kept intact to preserve the range for potential future grow-in-place support. A concrete user is the Rust binder driver's KVVec::shrink_to [1], which performs explicit vrealloc() shrinks for memory reclamation. This patch (of 5): Extract page freeing and NR_VMALLOC stat accounting from vfree() into a reusable vm_area_free_pages() helper. The helper operates on a range [start_idx, end_idx) of pages from a vm_struct, making it suitable for both full free (vfree) and partial free (upcoming vrealloc shrink). Freed page pointers in vm->pages[] are set to NULL to prevent stale references when the vm_struct outlives the free (as in vrealloc shrink). Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-0-70b96ee3e9c9@zohomail.in Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-1-70b96ee3e9c9@zohomail.in Link: https://lore.kernel.org/all/20260216-binder-shrink-vec-v3-v6-0-ece8e8593e53@zohomail.in/ [1] Signed-off-by: Shivam Kalra Reviewed-by: Uladzislau Rezki (Sony) Cc: Alice Ryhl Cc: Danilo Krummrich Signed-off-by: Andrew Morton --- mm/vmalloc.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index eabb86b13b7e..5555601b9529 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3416,6 +3416,32 @@ void vfree_atomic(const void *addr) schedule_work(&p->wq); } +/* + * vm_area_free_pages - free a range of pages from a vmalloc allocation + * @vm: the vm_struct containing the pages + * @start_idx: first page index to free (inclusive) + * @end_idx: last page index to free (exclusive) + * + * Free pages [start_idx, end_idx) updating NR_VMALLOC stat accounting. + * Freed vm->pages[] entries are set to NULL. + * Caller is responsible for unmapping (vunmap_range) and KASAN + * poisoning before calling this. + */ +static void vm_area_free_pages(struct vm_struct *vm, unsigned int start_idx, + unsigned int end_idx) +{ + unsigned int i; + + if (!(vm->flags & VM_MAP_PUT_PAGES)) { + for (i = start_idx; i < end_idx; i++) + mod_lruvec_page_state(vm->pages[i], NR_VMALLOC, -1); + } + free_pages_bulk(vm->pages + start_idx, end_idx - start_idx); + + for (i = start_idx; i < end_idx; i++) + vm->pages[i] = NULL; +} + /** * vfree - Release memory allocated by vmalloc() * @addr: Memory base address @@ -3436,7 +3462,6 @@ void vfree_atomic(const void *addr) void vfree(const void *addr) { struct vm_struct *vm; - int i; if (unlikely(in_interrupt())) { vfree_atomic(addr); @@ -3460,12 +3485,7 @@ void vfree(const void *addr) if (unlikely(vm->flags & VM_FLUSH_RESET_PERMS)) vm_reset_perms(vm); - if (!(vm->flags & VM_MAP_PUT_PAGES)) { - for (i = 0; i < vm->nr_pages; i++) - mod_lruvec_page_state(vm->pages[i], NR_VMALLOC, -1); - } - free_pages_bulk(vm->pages, vm->nr_pages); - + vm_area_free_pages(vm, 0, vm->nr_pages); kvfree(vm->pages); kfree(vm); } From d57ac904ffdce6c06e9a113fce603420c041b48c Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Tue, 19 May 2026 17:42:15 +0530 Subject: [PATCH 2626/5214] mm/vmalloc: use physical page count for vrealloc() grow-in-place check Update the grow-in-place check in vrealloc() to compare the requested size against the actual physical page count (vm->nr_pages) rather than the virtual area size (alloced_size, derived from get_vm_area_size()). Currently both values are equivalent, but the upcoming vrealloc() shrink functionality will free pages without reducing the virtual reservation size. After such a shrink, the old alloced_size-based comparison would incorrectly allow a grow-in-place operation to succeed and attempt to access freed pages. Switch to vm->nr_pages now so the check remains correct once shrink support is added. Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-2-70b96ee3e9c9@zohomail.in Signed-off-by: Shivam Kalra Reviewed-by: Uladzislau Rezki (Sony) Cc: Alice Ryhl Cc: Danilo Krummrich Signed-off-by: Andrew Morton --- mm/vmalloc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5555601b9529..3e159b74cfab 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4343,6 +4343,12 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align if (unlikely(flags & __GFP_THISNODE) && nid != NUMA_NO_NODE && nid != page_to_nid(vmalloc_to_page(p))) goto need_realloc; + } else { + /* + * If p is NULL, vrealloc behaves exactly like vmalloc. + * Skip the shrink and in-place grow paths. + */ + goto need_realloc; } /* @@ -4361,7 +4367,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align /* * We already have the bytes available in the allocation; use them. */ - if (size <= alloced_size) { + if (size <= vm->nr_pages << PAGE_SHIFT) { /* * No need to zero memory here, as unused memory will have * already been zeroed at initial allocation time or during From 0bca23804632cc7275fc5f67191b6be58993cd28 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Tue, 19 May 2026 17:42:16 +0530 Subject: [PATCH 2627/5214] mm/vmalloc: use physical page count in vread_iter() for VM_ALLOC areas For VM_ALLOC areas in vread_iter(), derive the vm area size from vm->nr_pages rather than get_vm_area_size(). Only VM_ALLOC areas are subject to vrealloc() shrinking, which frees pages without reducing the virtual reservation size. Switch to using vm->nr_pages for VM_ALLOC areas so the reader remains correct once shrink support is added. Other mapping types (vmap, ioremap) do not initialize nr_pages and will continue using get_vm_area_size(). [shivamkalra98@zohomail.in: add an nr_pages check] Link: https://lore.kernel.org/aff47da5-4fd5-481d-be18-e1eb99639490@zohomail.in Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-3-70b96ee3e9c9@zohomail.in Signed-off-by: Shivam Kalra Reviewed-by: Uladzislau Rezki (Sony) Cc: Alice Ryhl Cc: Danilo Krummrich Signed-off-by: Andrew Morton --- mm/vmalloc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 3e159b74cfab..bc21bf8e188b 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4666,7 +4666,18 @@ long vread_iter(struct iov_iter *iter, const char *addr, size_t count) smp_rmb(); vaddr = (char *) va->va_start; - size = vm ? get_vm_area_size(vm) : va_size(va); + if (vm) + /* + * For VM_ALLOC areas, use nr_pages rather than + * get_vm_area_size() because vrealloc() may shrink + * the mapping without updating area->size. Other + * mapping types (vmap, ioremap) don't set nr_pages. + */ + size = (vm->flags & VM_ALLOC && vm->nr_pages) ? + (vm->nr_pages << PAGE_SHIFT) : + get_vm_area_size(vm); + else + size = va_size(va); if (addr >= vaddr + size) goto next_va; From 5ea8ec74c57c0c920c26530ba586391a9a3f3e5f Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Tue, 19 May 2026 17:42:17 +0530 Subject: [PATCH 2628/5214] mm/vmalloc: free unused pages on vrealloc() shrink When vrealloc() shrinks an allocation and the new size crosses a page boundary, unmap and free the tail pages that are no longer needed. This reclaims physical memory that was previously wasted for the lifetime of the allocation. The heuristic is simple: always free when at least one full page becomes unused. Huge page allocations (page_order > 0) are skipped, as partial freeing would require splitting. Allocations with VM_FLUSH_RESET_PERMS are also skipped, as their direct-map permissions must be reset before pages are returned to the page allocator, which is handled by vm_reset_perms() during vfree(). Additionally, allocations with VM_USERMAP are skipped because remap_vmalloc_range_partial() validates mapping requests against the unchanged vm->size; freeing tail pages would cause vmalloc_to_page() to return NULL for the unmapped range. To protect concurrent readers, the shrink path uses Node lock to synchronize before freeing the pages. Finally, we notify kmemleak of the reduced allocation size using kmemleak_free_part() to prevent the kmemleak scanner from faulting on the newly unmapped virtual addresses. The virtual address reservation (vm->size / vmap_area) is intentionally kept unchanged, preserving the address for potential future grow-in-place support. Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-4-70b96ee3e9c9@zohomail.in Signed-off-by: Shivam Kalra Suggested-by: Danilo Krummrich Reviewed-by: Uladzislau Rezki (Sony) Cc: Alice Ryhl Signed-off-by: Andrew Morton --- mm/vmalloc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index bc21bf8e188b..1afca3568b9b 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4351,14 +4351,62 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align goto need_realloc; } - /* - * TODO: Shrink the vm_area, i.e. unmap and free unused pages. What - * would be a good heuristic for when to shrink the vm_area? - */ if (size <= old_size) { + unsigned int new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + /* Zero out "freed" memory, potentially for future realloc. */ if (want_init_on_free() || want_init_on_alloc(flags)) memset((void *)p + size, 0, old_size - size); + + /* + * Free tail pages when shrink crosses a page boundary. + * + * Skip huge page allocations (page_order > 0) as partial + * freeing would require splitting. + * + * Skip VM_FLUSH_RESET_PERMS, as direct-map permissions must + * be reset before pages are returned to the allocator. + * + * Skip VM_USERMAP, as remap_vmalloc_range_partial() validates + * mapping requests against the unchanged vm->size; freeing + * tail pages would cause vmalloc_to_page() to return NULL for + * the unmapped range. + * + * Skip if either GFP_NOFS or GFP_NOIO are used. + * kmemleak_free_part() internally allocates with + * GFP_KERNEL, which could trigger a recursive deadlock + * if we are under filesystem or I/O reclaim. + */ + if (new_nr_pages < vm->nr_pages && !vm_area_page_order(vm) && + !(vm->flags & (VM_FLUSH_RESET_PERMS | VM_USERMAP)) && + gfp_has_io_fs(flags)) { + unsigned long addr = (unsigned long)kasan_reset_tag(p); + unsigned int old_nr_pages = vm->nr_pages; + + /* + * Use the node lock to synchronize with concurrent + * readers (vmalloc_info_show). + */ + struct vmap_node *vn = addr_to_node(addr); + + spin_lock(&vn->busy.lock); + vm->nr_pages = new_nr_pages; + spin_unlock(&vn->busy.lock); + + /* Notify kmemleak of the reduced allocation size before unmapping. */ + kmemleak_free_part( + (void *)addr + ((unsigned long)new_nr_pages + << PAGE_SHIFT), + (unsigned long)(old_nr_pages - new_nr_pages) + << PAGE_SHIFT); + + vunmap_range(addr + ((unsigned long)new_nr_pages + << PAGE_SHIFT), + addr + ((unsigned long)old_nr_pages + << PAGE_SHIFT)); + + vm_area_free_pages(vm, new_nr_pages, old_nr_pages); + } vm->requested_size = size; kasan_vrealloc(p, old_size, size); return (void *)p; From 3c3daeafcdb60e182554679fc32d2c912d1b0b6a Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Tue, 19 May 2026 17:42:18 +0530 Subject: [PATCH 2629/5214] lib/test_vmalloc: add vrealloc test case Introduce a new test case "vrealloc_test" that exercises the vrealloc() shrink and in-place grow paths: - Grow beyond allocated pages (triggers full reallocation). - Shrink crossing a page boundary (frees tail pages). - Shrink within the same page (no page freeing). - Grow within the already allocated page count (in-place). Data integrity is validated after each realloc step by checking that the first byte of the original allocation is preserved. The test is gated behind run_test_mask bit 12 (id 4096). Link: https://lore.kernel.org/20260519-vmalloc-shrink-v14-5-70b96ee3e9c9@zohomail.in Signed-off-by: Shivam Kalra Reviewed-by: Uladzislau Rezki (Sony) Cc: Alice Ryhl Cc: Danilo Krummrich Signed-off-by: Andrew Morton --- lib/test_vmalloc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/lib/test_vmalloc.c b/lib/test_vmalloc.c index 876c72c18a0c..b23f85e8f8ca 100644 --- a/lib/test_vmalloc.c +++ b/lib/test_vmalloc.c @@ -55,6 +55,7 @@ __param(int, run_test_mask, 7, "\t\tid: 512, name: kvfree_rcu_2_arg_vmalloc_test\n" "\t\tid: 1024, name: vm_map_ram_test\n" "\t\tid: 2048, name: no_block_alloc_test\n" + "\t\tid: 4096, name: vrealloc_test\n" /* Add a new test case description here. */ ); @@ -421,6 +422,66 @@ vm_map_ram_test(void) return nr_allocated != map_nr_pages; } +static int vrealloc_test(void) +{ + void *ptr, *tmp; + int i; + + for (i = 0; i < test_loop_count; i++) { + int err = -1; + + ptr = vrealloc(NULL, PAGE_SIZE, GFP_KERNEL); + if (!ptr) + return -1; + + *((__u8 *)ptr) = 'a'; + + /* Grow: beyond allocated pages, triggers full realloc. */ + tmp = vrealloc(ptr, 4 * PAGE_SIZE, GFP_KERNEL); + if (!tmp) + goto error; + ptr = tmp; + + if (*((__u8 *)ptr) != 'a') + goto error; + + /* Shrink: crosses page boundary, frees tail pages. */ + tmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL); + if (!tmp) + goto error; + ptr = tmp; + + if (*((__u8 *)ptr) != 'a') + goto error; + + /* Shrink: within same page, no page freeing. */ + tmp = vrealloc(ptr, PAGE_SIZE / 2, GFP_KERNEL); + if (!tmp) + goto error; + ptr = tmp; + + if (*((__u8 *)ptr) != 'a') + goto error; + + /* Grow: within allocated page, in-place, no realloc. */ + tmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL); + if (!tmp) + goto error; + ptr = tmp; + + if (*((__u8 *)ptr) != 'a') + goto error; + + err = 0; +error: + vfree(ptr); + if (err) + return err; + } + + return 0; +} + struct test_case_desc { const char *test_name; int (*test_func)(void); @@ -440,6 +501,7 @@ static struct test_case_desc test_case_array[] = { { "kvfree_rcu_2_arg_vmalloc_test", kvfree_rcu_2_arg_vmalloc_test, }, { "vm_map_ram_test", vm_map_ram_test, }, { "no_block_alloc_test", no_block_alloc_test, true }, + { "vrealloc_test", vrealloc_test, }, /* Add a new test case here. */ }; From a2b8d7827f48ee54a686cb80e4a1d0ff954ec42a Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 14 May 2026 02:26:57 -0700 Subject: [PATCH 2630/5214] drivers/base/memory: set mem->altmap after successful device registration If __add_memory_block() fails at xa_store() (under memory pressure for example), device_unregister() is called, which eventually triggers memory_block_release() with mem->altmap still set, causing a WARN_ON(mem->altmap). This was triggered by modifying virtio-mem driver. Fix this by delaying the assignment of mem->altmap until after __add_memory_block() has succeeded. Link: https://lore.kernel.org/20260514092657.3057141-1-georgi.djakov@oss.qualcomm.com Fixes: 1a8c64e11043 ("mm/memory_hotplug: embed vmem_altmap details in memory block") Signed-off-by: Georgi Djakov Acked-by: Oscar Salvador (SUSE) Cc: Vishal Verma Cc: Mike Rapoport Cc: Richard Cheng Cc: David Hildenbrand Cc: Georgi Djakov Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Signed-off-by: Andrew Morton --- drivers/base/memory.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index d31a421f7483..b318344426fa 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -797,7 +797,6 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state mem->start_section_nr = block_id * sections_per_block; mem->state = state; mem->nid = nid; - mem->altmap = altmap; INIT_LIST_HEAD(&mem->group_next); #ifndef CONFIG_NUMA @@ -815,6 +814,8 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state if (ret) return ret; + mem->altmap = altmap; + if (group) { mem->group = group; list_add(&mem->group_next, &group->memory_blocks); From a10848d98ec9c5372a979d7aa91a8b8fe50fd8f8 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Thu, 14 May 2026 16:57:54 +0800 Subject: [PATCH 2631/5214] mm/memory-failure: use zone_pcp_disable() for poison handling __page_handle_poison() used drain_all_pages() instead of zone_pcp_disable() because dissolve_free_hugetlb_folio() could restore HVO vmemmap pages and decrement hugetlb_optimize_vmemmap_key. That static key update took cpu_hotplug_lock through static_key_slow_dec(), while zone_pcp_disable() holds pcp_batch_high_lock. CPU hotplug takes the locks in the opposite order through page_alloc_cpu_online/dead(), so the combination could deadlock. That dependency no longer exists. Commit da3e2d1ca43d ("mm/hugetlb: remove hugetlb_optimize_vmemmap_key static key") removed the HVO static key and the static_branch_dec() from hugetlb_vmemmap_restore_folio(). The dissolve_free_hugetlb_folio() path no longer reaches static_key_slow_dec(). Use zone_pcp_disable() again while dissolving the hugetlb folio and taking the target page off the buddy allocator. This prevents the drained PCP lists from being refilled before take_page_off_buddy() runs, making the page isolation deterministic. Link: https://lore.kernel.org/20260514085754.84097-1-kaitao.cheng@linux.dev Signed-off-by: Kaitao Cheng Reviewed-by: Oscar Salvador Acked-by: Miaohe Lin Cc: Naoya Horiguchi Signed-off-by: Andrew Morton --- mm/memory-failure.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 1b8d0bade04a..51508a55c405 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -172,23 +172,11 @@ static int __page_handle_poison(struct page *page) { int ret; - /* - * zone_pcp_disable() can't be used here. It will - * hold pcp_batch_high_lock and dissolve_free_hugetlb_folio() might hold - * cpu_hotplug_lock via static_key_slow_dec() when hugetlb vmemmap - * optimization is enabled. This will break current lock dependency - * chain and leads to deadlock. - * Disabling pcp before dissolving the page was a deterministic - * approach because we made sure that those pages cannot end up in any - * PCP list. Draining PCP lists expels those pages to the buddy system, - * but nothing guarantees that those pages do not get back to a PCP - * queue if we need to refill those. - */ + zone_pcp_disable(page_zone(page)); ret = dissolve_free_hugetlb_folio(page_folio(page)); - if (!ret) { - drain_all_pages(page_zone(page)); + if (!ret) ret = take_page_off_buddy(page); - } + zone_pcp_enable(page_zone(page)); return ret; } From f30462fc7d2370761b84eaf5b3ed84a03bdf3266 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Tue, 12 May 2026 23:15:23 +0800 Subject: [PATCH 2632/5214] mm/damon/vaddr: attempt per-vma lock during page table walk Currently, DAMON virtual address operations use mmap_read_lock during page table walks, which can cause unnecessary contention under high concurrency. Introduce damon_va_walk_page_range() to first attempt acquiring a per-vma lock. If the VMA is found and the range is fully contained within it, the page table walk proceeds with the per-vma lock instead of mmap_read_lock. This optimization is expected to be particularly effective for damon_va_young() and damon_va_mkold(), which are frequently called and typically operate within a single VMA. Link: https://lore.kernel.org/20260512151523.2092638-1-wangkefeng.wang@huawei.com Signed-off-by: Kefeng Wang Reviewed-by: SeongJae Park Cc: Nanyong Sun Signed-off-by: Andrew Morton --- mm/damon/vaddr.c | 69 ++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 1b0ebe3b6951..d27147603564 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -237,6 +237,35 @@ static void damon_va_update(struct damon_ctx *ctx) } } +static void damon_va_walk_page_range(struct mm_struct *mm, unsigned long start, + unsigned long end, struct mm_walk_ops *ops, void *private) +{ + struct vm_area_struct *vma; + + vma = lock_vma_under_rcu(mm, start); + if (!vma) + goto lock_mmap; + + if (end > vma->vm_end) { + vma_end_read(vma); + goto lock_mmap; + } + + if (!(vma->vm_flags & VM_PFNMAP)) { + ops->walk_lock = PGWALK_VMA_RDLOCK_VERIFY; + walk_page_range_vma(vma, start, end, ops, private); + } + + vma_end_read(vma); + return; + +lock_mmap: + mmap_read_lock(mm); + ops->walk_lock = PGWALK_RDLOCK; + walk_page_range(mm, start, end, ops, private); + mmap_read_unlock(mm); +} + static int damon_mkold_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long next, struct mm_walk *walk) { @@ -315,17 +344,14 @@ static int damon_mkold_hugetlb_entry(pte_t *pte, unsigned long hmask, #define damon_mkold_hugetlb_entry NULL #endif /* CONFIG_HUGETLB_PAGE */ -static const struct mm_walk_ops damon_mkold_ops = { - .pmd_entry = damon_mkold_pmd_entry, - .hugetlb_entry = damon_mkold_hugetlb_entry, - .walk_lock = PGWALK_RDLOCK, -}; - static void damon_va_mkold(struct mm_struct *mm, unsigned long addr) { - mmap_read_lock(mm); - walk_page_range(mm, addr, addr + 1, &damon_mkold_ops, NULL); - mmap_read_unlock(mm); + struct mm_walk_ops damon_mkold_ops = { + .pmd_entry = damon_mkold_pmd_entry, + .hugetlb_entry = damon_mkold_hugetlb_entry, + }; + + damon_va_walk_page_range(mm, addr, addr + 1, &damon_mkold_ops, NULL); } /* @@ -445,12 +471,6 @@ static int damon_young_hugetlb_entry(pte_t *pte, unsigned long hmask, #define damon_young_hugetlb_entry NULL #endif /* CONFIG_HUGETLB_PAGE */ -static const struct mm_walk_ops damon_young_ops = { - .pmd_entry = damon_young_pmd_entry, - .hugetlb_entry = damon_young_hugetlb_entry, - .walk_lock = PGWALK_RDLOCK, -}; - static bool damon_va_young(struct mm_struct *mm, unsigned long addr, unsigned long *folio_sz) { @@ -459,9 +479,12 @@ static bool damon_va_young(struct mm_struct *mm, unsigned long addr, .young = false, }; - mmap_read_lock(mm); - walk_page_range(mm, addr, addr + 1, &damon_young_ops, &arg); - mmap_read_unlock(mm); + struct mm_walk_ops damon_young_ops = { + .pmd_entry = damon_young_pmd_entry, + .hugetlb_entry = damon_young_hugetlb_entry, + }; + + damon_va_walk_page_range(mm, addr, addr + 1, &damon_young_ops, &arg); return arg.young; } @@ -750,7 +773,6 @@ static unsigned long damos_va_migrate(struct damon_target *target, struct mm_walk_ops walk_ops = { .pmd_entry = damos_va_migrate_pmd_entry, .pte_entry = NULL, - .walk_lock = PGWALK_RDLOCK, }; use_target_nid = dests->nr_dests == 0; @@ -768,9 +790,7 @@ static unsigned long damos_va_migrate(struct damon_target *target, if (!mm) goto free_lists; - mmap_read_lock(mm); - walk_page_range(mm, r->ar.start, r->ar.end, &walk_ops, &priv); - mmap_read_unlock(mm); + damon_va_walk_page_range(mm, r->ar.start, r->ar.end, &walk_ops, &priv); mmput(mm); for (int i = 0; i < nr_dests; i++) { @@ -862,7 +882,6 @@ static unsigned long damos_va_stat(struct damon_target *target, struct mm_struct *mm; struct mm_walk_ops walk_ops = { .pmd_entry = damos_va_stat_pmd_entry, - .walk_lock = PGWALK_RDLOCK, }; priv.scheme = s; @@ -875,9 +894,7 @@ static unsigned long damos_va_stat(struct damon_target *target, if (!mm) return 0; - mmap_read_lock(mm); - walk_page_range(mm, r->ar.start, r->ar.end, &walk_ops, &priv); - mmap_read_unlock(mm); + damon_va_walk_page_range(mm, r->ar.start, r->ar.end, &walk_ops, &priv); mmput(mm); return 0; } From c33afe6f972d7bfada751c9ee83d9875ea38d6dc Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Wed, 20 May 2026 13:17:51 +0800 Subject: [PATCH 2633/5214] Documentation/admin-guide/mm: fix typos in transhuge.rst Fix these two typos: 1. approporiately -> appropriately 2. presure -> pressure Link: https://lore.kernel.org/20260520051751.74396-1-leon.hwang@linux.dev Signed-off-by: Leon Hwang Reviewed-by: Mike Rapoport (Microsoft) Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Reviewed-by: SeongJae Park Reviewed-by: Lorenzo Stoakes Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Jonathan Corbet Cc: Leon Hwang Cc: Liam R. Howlett Cc: Michal Hocko Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/transhuge.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index 5fbc3d89bb07..76f4eb14e262 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -57,7 +57,7 @@ prominent because the size of each page isn't as huge as the PMD-sized variant and there is less memory to clear in each page fault. Some architectures also employ TLB compression mechanisms to squeeze more entries in when a set of PTEs are virtually and physically contiguous -and approporiately aligned. In this case, TLB misses will occur less +and appropriately aligned. In this case, TLB misses will occur less often. THP can be enabled system wide or restricted to certain tasks or even @@ -210,7 +210,7 @@ PMD-mappable transparent hugepage:: cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size All THPs at fault and collapse time will be added to _deferred_list, -and will therefore be split under memory presure if they are considered +and will therefore be split under memory pressure if they are considered "underused". A THP is underused if the number of zero-filled pages in the THP is above max_ptes_none (see below). It is possible to disable this behaviour by writing 0 to shrink_underused, and enable it by writing From e186709b0a2d5a05c3cb38e46d13b399f5f5d3f9 Mon Sep 17 00:00:00 2001 From: niecheng Date: Tue, 19 May 2026 18:20:59 -0700 Subject: [PATCH 2634/5214] mm/damon/core: clarify next_intervals_tune_sis update path Patch series "mm/damon: documentation and comment fixes". This patch (of 3): damon_set_attrs() updates next_aggregation_sis and next_ops_update_sis for online attrs updates, but it does not update next_intervals_tune_sis there. This can look like a missing update when reading damon_set_attrs() alone, while next_intervals_tune_sis is actually updated in kdamond_fn(). Add a short comment to make this explicit. Link: https://lore.kernel.org/20260520012104.93602-1-sj@kernel.org Link: https://lore.kernel.org/20260520012104.93602-2-sj@kernel.org Suggested-by: SeongJae Park Signed-off-by: niecheng Signed-off-by: SeongJae Park Reviewed-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Sakurai Shun Cc: Zenghui Yu Signed-off-by: Andrew Morton --- mm/damon/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index 4e223857a0f9..68b3b4bbc8fc 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -909,6 +909,9 @@ int damon_set_attrs(struct damon_ctx *ctx, struct damon_attrs *attrs) attrs->aggr_interval / sample_interval; ctx->next_ops_update_sis = ctx->passed_sample_intervals + attrs->ops_update_interval / sample_interval; + /* + * next_intervals_tune_sis will be updated inside kdamond_fn(). + */ damon_update_monitoring_results(ctx, attrs, aggregating); ctx->attrs = *attrs; From de5480aeffc59d0792855c59c98624038ce67b67 Mon Sep 17 00:00:00 2001 From: Sakurai Shun Date: Tue, 19 May 2026 18:21:00 -0700 Subject: [PATCH 2635/5214] Docs/mm/damon/design: fix three typos L140: "unsinged" -> "unsigned" L371: "sampleing" -> "sampling" L387: "multipled" -> "multiplied" Link: https://lore.kernel.org/20260520012104.93602-3-sj@kernel.org Signed-off-by: Sakurai Shun Signed-off-by: SeongJae Park Reviewed-by: Lorenzo Stoakes Acked-by: Mike Rapoport (Microsoft) Reviewed-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Michal Hocko Cc: niecheng Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Signed-off-by: Andrew Morton --- Documentation/mm/damon/design.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst index a24f9f00d183..2da7ca0d3d17 100644 --- a/Documentation/mm/damon/design.rst +++ b/Documentation/mm/damon/design.rst @@ -147,7 +147,7 @@ as Idle page tracking does. Address Unit ------------ -DAMON core layer uses ``unsinged long`` type for monitoring target address +DAMON core layer uses ``unsigned long`` type for monitoring target address ranges. In some cases, the address space for a given operations set could be too large to be handled with the type. ARM (32-bit) with large physical address extension is an example. For such cases, a per-operations set @@ -417,7 +417,7 @@ with theoretical maximum ``nr_accesses``, which can be calculated as ``aggregation interval / sampling interval``. The mechanism calculates the ratio of access events for ``aggrs`` aggregations, -and increases or decrease the ``sampleing interval`` and ``aggregation +and increases or decrease the ``sampling interval`` and ``aggregation interval`` in same ratio, if the observed access ratio is lower or higher than the target, respectively. The ratio of the intervals change is decided in proportion to the distance between current samples ratio and the target ratio. @@ -433,7 +433,7 @@ The tuning is turned off by default, and need to be set explicitly by the user. As a rule of thumbs and the Parreto principle, 4% access samples ratio target is recommended. Note that Parreto principle (80/20 rule) has applied twice. That is, assumes 4% (20% of 20%) DAMON-observed access events ratio (source) -to capture 64% (80% multipled by 80%) real access events (outcomes). +to capture 64% (80% multiplied by 80%) real access events (outcomes). To know how user-space can use this feature via :ref:`DAMON sysfs interface `, refer to :ref:`intervals_goal From 12e4d4bb6e5a4845f0c22e5d8820fdc6244653a4 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Tue, 19 May 2026 18:21:01 -0700 Subject: [PATCH 2636/5214] Docs/{ABI,admin-guide}/damon: fix various typoes ``damon_target_idx`` was wrongly written as ``target_idx`` in the docs. Fix it all over the place, as well as the wrong directory count, grammar, etc. Link: https://lore.kernel.org/20260520012104.93602-4-sj@kernel.org Signed-off-by: SeongJae Park Signed-off-by: Zenghui Yu Reviewed-by: SeongJae Park Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: niecheng Cc: Sakurai Shun Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .../ABI/testing/sysfs-kernel-mm-damon | 2 +- Documentation/admin-guide/mm/damon/usage.rst | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index ee29d4e204ff..b73e6bc28ea5 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -452,7 +452,7 @@ Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the maximum size of the hugepage for the filter. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//target_idx +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//damon_target_idx Date: Feb 2025 Contact: SeongJae Park Description: If 'target' is written to the 'type' file, writing to or diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index 0d6a27dc97b0..d46875e603d8 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -97,7 +97,7 @@ comma (","). │ │ │ │ │ │ │ │ │ 0/target_metric,target_value,current_value,nid,path │ │ │ │ │ │ │ :ref:`watermarks `/metric,interval_us,high,mid,low │ │ │ │ │ │ │ :ref:`{core_,ops_,}filters `/nr_filters - │ │ │ │ │ │ │ │ 0/type,matching,allow,memcg_path,addr_start,addr_end,target_idx,min,max + │ │ │ │ │ │ │ │ 0/type,matching,allow,memcg_path,addr_start,addr_end,damon_target_idx,min,max │ │ │ │ │ │ │ :ref:`dests `/nr_dests │ │ │ │ │ │ │ │ 0/id,weight │ │ │ │ │ │ │ :ref:`stats `/nr_tried,sz_tried,nr_applied,sz_applied,sz_ops_filter_passed,qt_exceeds,nr_snapshots,max_nr_snapshots @@ -374,7 +374,7 @@ to ``N-1``. Each directory represents each DAMON-based operation scheme. schemes// ------------ -In each scheme directory, eight directories (``access_pattern``, ``quotas``, +In each scheme directory, nine directories (``access_pattern``, ``quotas``, ``watermarks``, ``core_filters``, ``ops_filters``, ``filters``, ``dests``, ``stats``, and ``tried_regions``) and three files (``action``, ``target_nid`` and ``apply_interval``) exist. @@ -492,7 +492,7 @@ given DAMON-based operation scheme. Under the watermarks directory, five files (``metric``, ``interval_us``, ``high``, ``mid``, and ``low``) for setting the metric, the time interval between check of the metric, and the three watermarks exist. You can set and -get the five values by writing to the files, respectively. +get the five values by writing to and reading from the files, respectively. Keywords and meanings of those that can be written to the ``metric`` file are as below. @@ -500,7 +500,7 @@ as below. - none: Ignore the watermarks - free_mem_rate: System's free memory rate (per thousand) -The ``interval`` should written in microseconds unit. +The ``interval_us`` should be written in microseconds unit. .. _sysfs_filters: @@ -528,9 +528,9 @@ in the numeric order. Each filter directory contains nine files, namely ``type``, ``matching``, ``allow``, ``memcg_path``, ``addr_start``, ``addr_end``, ``min``, ``max`` -and ``target_idx``. To ``type`` file, you can write the type of the filter. -Refer to :ref:`the design doc ` for available type -names, their meaning and on what layer those are handled. +and ``damon_target_idx``. To ``type`` file, you can write the type of the +filter. Refer to :ref:`the design doc ` for +available type names, their meaning and on what layer those are handled. For ``memcg`` type, you can specify the memory cgroup of the interest by writing the path of the memory cgroup from the cgroups mount point to @@ -540,7 +540,7 @@ files, respectively. For ``hugepage_size`` type, you can specify the minimum and maximum size of the range (closed interval) to ``min`` and ``max`` files, respectively. For ``target`` type, you can specify the index of the target between the list of the DAMON context's monitoring targets list to -``target_idx`` file. +``damon_target_idx`` file. You can write ``Y`` or ``N`` to ``matching`` file to specify whether the filter is for memory that matches the ``type``. You can write ``Y`` or ``N`` to @@ -731,7 +731,7 @@ show results using tracepoint supporting tools like ``perf``. For example:: Each line of the perf script output represents each monitoring region. The first five fields are as usual other tracepoint outputs. The sixth field -(``target_id=X``) shows the ide of the monitoring target of the region. The +(``target_id=X``) shows the id of the monitoring target of the region. The seventh field (``nr_regions=X``) shows the total number of monitoring regions for the target. The eighth field (``X-Y:``) shows the start (``X``) and end (``Y``) addresses of the region in bytes. The ninth field (``X``) shows the From 43a4b19c17d156786adf3515434e7d522664d75d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 14 Apr 2026 11:59:07 +0800 Subject: [PATCH 2637/5214] dt-bindings: power: qcom,rpmhpd: Fix whitespace in RPMHPD defines Some RPMHPD_* defines in the Generic RPMH Power Domain Indexes section were using spaces instead of tabs for alignment. Fix them to be consistent with the rest of the file. Signed-off-by: Shawn Guo Acked-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- include/dt-bindings/power/qcom,rpmhpd.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/dt-bindings/power/qcom,rpmhpd.h b/include/dt-bindings/power/qcom,rpmhpd.h index 67e2634fdc99..74e910150956 100644 --- a/include/dt-bindings/power/qcom,rpmhpd.h +++ b/include/dt-bindings/power/qcom,rpmhpd.h @@ -7,7 +7,7 @@ #define _DT_BINDINGS_POWER_QCOM_RPMHPD_H /* Generic RPMH Power Domain Indexes */ -#define RPMHPD_CX 0 +#define RPMHPD_CX 0 #define RPMHPD_CX_AO 1 #define RPMHPD_EBI 2 #define RPMHPD_GFX 3 @@ -19,14 +19,14 @@ #define RPMHPD_MX_AO 9 #define RPMHPD_MXC 10 #define RPMHPD_MXC_AO 11 -#define RPMHPD_MSS 12 +#define RPMHPD_MSS 12 #define RPMHPD_NSP 13 -#define RPMHPD_NSP0 14 -#define RPMHPD_NSP1 15 -#define RPMHPD_QPHY 16 -#define RPMHPD_DDR 17 -#define RPMHPD_XO 18 -#define RPMHPD_NSP2 19 +#define RPMHPD_NSP0 14 +#define RPMHPD_NSP1 15 +#define RPMHPD_QPHY 16 +#define RPMHPD_DDR 17 +#define RPMHPD_XO 18 +#define RPMHPD_NSP2 19 #define RPMHPD_GMXC 20 #define RPMHPD_DCX 21 #define RPMHPD_GBX 22 From 5f0e40827295a4e46da719a399eb72cc2a5a14d6 Mon Sep 17 00:00:00 2001 From: Kamal Wadhwa Date: Tue, 14 Apr 2026 11:59:08 +0800 Subject: [PATCH 2638/5214] dt-bindings: power: qcom,rpmhpd: Add RPMh power domain for Nord SoC Document the RPMh power domain for Nord SoC, and add definitions for the new power domains present on Nord SoC. - RPMHPD_NSP3: power domain for the 4th NSP subsystem - RPMHPD_GFX1: power domain for the 2nd GFX subsystem Signed-off-by: Kamal Wadhwa Signed-off-by: Shawn Guo Acked-by: Rob Herring (Arm) Reviewed-by: Konrad Dybcio Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom,rpmhpd.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 0bf1e13a9964..779380cc7e44 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -35,6 +35,7 @@ properties: - qcom,msm8994-rpmpd - qcom,msm8996-rpmpd - qcom,msm8998-rpmpd + - qcom,nord-rpmhpd - qcom,qcm2290-rpmpd - qcom,qcs404-rpmpd - qcom,qcs615-rpmhpd diff --git a/include/dt-bindings/power/qcom,rpmhpd.h b/include/dt-bindings/power/qcom,rpmhpd.h index 74e910150956..07bd2a7b0150 100644 --- a/include/dt-bindings/power/qcom,rpmhpd.h +++ b/include/dt-bindings/power/qcom,rpmhpd.h @@ -30,6 +30,8 @@ #define RPMHPD_GMXC 20 #define RPMHPD_DCX 21 #define RPMHPD_GBX 22 +#define RPMHPD_NSP3 23 +#define RPMHPD_GFX1 24 /* RPMh Power Domain performance levels */ #define RPMH_REGULATOR_LEVEL_RETENTION 16 From bb85d843e3aaa452ba7084e9695163cbede206da Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Apr 2026 10:33:42 +0200 Subject: [PATCH 2639/5214] pmdomain: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/qcom/Kconfig b/drivers/pmdomain/qcom/Kconfig index 72cbcfe7a0c9..4abd43a88d08 100644 --- a/drivers/pmdomain/qcom/Kconfig +++ b/drivers/pmdomain/qcom/Kconfig @@ -2,7 +2,7 @@ menu "Qualcomm PM Domains" config QCOM_CPR - tristate "QCOM Core Power Reduction (CPR) support" + tristate "Qualcomm Core Power Reduction (CPR) support" depends on (ARCH_QCOM || COMPILE_TEST) && HAS_IOMEM select PM_OPP select REGMAP From a96e40f4afdcb52a9c97a5465c964e010734d105 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:40:50 +0200 Subject: [PATCH 2640/5214] pmdomain: core: switch to dynamic root device Driver core expects devices to be dynamically allocated and will, for example, complain loudly if a device that lacks a release function is ever freed. Use root_device_register() to allocate and register the root device instead of open coding using a static device. Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index f7731270015d..7c96e88302d3 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -33,9 +33,7 @@ static const struct bus_type genpd_provider_bus_type = { }; /* The parent for genpd_provider devices. */ -static struct device genpd_provider_bus = { - .init_name = "genpd_provider", -}; +static struct device *genpd_provider_bus; #define GENPD_RETRY_MAX_MS 250 /* Approximate */ @@ -2325,7 +2323,7 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) device_initialize(&genpd->dev); genpd->dev.release = genpd_provider_release; genpd->dev.bus = &genpd_provider_bus_type; - genpd->dev.parent = &genpd_provider_bus; + genpd->dev.parent = genpd_provider_bus; if (!genpd_is_dev_name_fw(genpd)) { dev_set_name(&genpd->dev, "%s", genpd->name); @@ -3742,11 +3740,9 @@ static int __init genpd_bus_init(void) { int ret; - ret = device_register(&genpd_provider_bus); - if (ret) { - put_device(&genpd_provider_bus); - return ret; - } + genpd_provider_bus = root_device_register("genpd_provider"); + if (IS_ERR(genpd_provider_bus)) + return PTR_ERR(genpd_provider_bus); ret = bus_register(&genpd_provider_bus_type); if (ret) @@ -3768,7 +3764,7 @@ static int __init genpd_bus_init(void) err_prov_bus: bus_unregister(&genpd_provider_bus_type); err_dev: - device_unregister(&genpd_provider_bus); + root_device_unregister(genpd_provider_bus); return ret; } core_initcall(genpd_bus_init); From b462a227d8547d62b1f654bf80b3505a84746b54 Mon Sep 17 00:00:00 2001 From: Yuanshen Cao Date: Sun, 10 May 2026 05:25:35 +0000 Subject: [PATCH 2641/5214] pmdomain: sunxi: support power domain flags for pck600 While bringing up the PowerVR GPU on the A733 (Radxa Cubie A7Z), we found that one of the GPU power domains must be configured as "always on." While the Radxa BSP device tree leaves the GPU power domain nodes commented out, the GPU driver code contains traces indicating an "always on" requirement [1]. Currently, sunxi_pck600_desc only supports specifying pd_names. This patch introduces sunxi_pck600_pd_desc, which stores both the name and its associated flags. This also (more or less) aligns the implementation with the existing sun50i PPU handling of always-on domains. With this change, individual power domains can now be configured more granularly. In particular, the GPU_CORE domain in sun60i_a733_pck600_pds can now be explicitly marked with GENPD_FLAG_ALWAYS_ON. The patch was tested on the Radxa Cubie A7Z, where the GPU now functions as expected. Thanks to Icenowy for her support and expertise on sunxi and PowerVR, and thanks to Mikhail for identifying this exact cause of the GPU bring-up issue. [1] https://github.com/radxa/allwinner-bsp/blob/cubie-aiot-v1.4.6/modules/gpu/img-bxm/linux/rogue_km/services/system/rogue/rgx_sunxi/sunxi_platform.c#L62 Signed-off-by: Yuanshen Cao Signed-off-by: Ulf Hansson --- drivers/pmdomain/sunxi/sun55i-pck600.c | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/pmdomain/sunxi/sun55i-pck600.c b/drivers/pmdomain/sunxi/sun55i-pck600.c index 1d47bbd35ced..8a37d11b65a2 100644 --- a/drivers/pmdomain/sunxi/sun55i-pck600.c +++ b/drivers/pmdomain/sunxi/sun55i-pck600.c @@ -41,8 +41,13 @@ #define PPU_REG_SIZE 0x1000 +struct sunxi_pck600_pd_desc { + const char *name; + unsigned int flags; +}; + struct sunxi_pck600_desc { - const char * const *pd_names; + const struct sunxi_pck600_pd_desc *pd_descs; unsigned int num_domains; u32 logic_power_switch0_delay_offset; u32 logic_power_switch1_delay_offset; @@ -164,10 +169,12 @@ static int sunxi_pck600_probe(struct platform_device *pdev) for (i = 0; i < desc->num_domains; i++) { struct sunxi_pck600_pd *pd = &pck->pds[i]; + const struct sunxi_pck600_pd_desc *pd_desc = &desc->pd_descs[i]; - pd->genpd.name = desc->pd_names[i]; + pd->genpd.name = pd_desc->name; pd->genpd.power_off = sunxi_pck600_power_off; pd->genpd.power_on = sunxi_pck600_power_on; + pd->genpd.flags = pd_desc->flags; pd->base = base + PPU_REG_SIZE * i; sunxi_pck600_pd_setup(pd, desc); @@ -195,13 +202,14 @@ static int sunxi_pck600_probe(struct platform_device *pdev) return ret; } -static const char * const sun55i_a523_pck600_pd_names[] = { - "VE", "GPU", "VI", "VO0", "VO1", "DE", "NAND", "PCIE" +static const struct sunxi_pck600_pd_desc sun55i_a523_pck600_pds[] = { + { "VE", }, { "GPU", }, { "VI", }, { "VO0", }, { "VO1", }, + { "DE", }, { "NAND", }, { "PCIE", }, }; static const struct sunxi_pck600_desc sun55i_a523_pck600_desc = { - .pd_names = sun55i_a523_pck600_pd_names, - .num_domains = ARRAY_SIZE(sun55i_a523_pck600_pd_names), + .pd_descs = sun55i_a523_pck600_pds, + .num_domains = ARRAY_SIZE(sun55i_a523_pck600_pds), .logic_power_switch0_delay_offset = 0xc00, .logic_power_switch1_delay_offset = 0xc04, .off2on_delay_offset = 0xc10, @@ -213,14 +221,15 @@ static const struct sunxi_pck600_desc sun55i_a523_pck600_desc = { .has_rst_clk = true, }; -static const char * const sun60i_a733_pck600_pd_names[] = { - "VI", "DE_SYS", "VE_DEC", "VE_ENC", "NPU", - "GPU_TOP", "GPU_CORE", "PCIE", "USB2", "VO", "VO1" +static const struct sunxi_pck600_pd_desc sun60i_a733_pck600_pds[] = { + { "VI", }, { "DE_SYS", }, { "VE_DEC", }, { "VE_ENC", }, { "NPU", }, + { "GPU_TOP", }, { "GPU_CORE", GENPD_FLAG_ALWAYS_ON }, + { "PCIE", }, { "USB2", }, { "VO", }, { "VO1", }, }; static const struct sunxi_pck600_desc sun60i_a733_pck600_desc = { - .pd_names = sun60i_a733_pck600_pd_names, - .num_domains = ARRAY_SIZE(sun60i_a733_pck600_pd_names), + .pd_descs = sun60i_a733_pck600_pds, + .num_domains = ARRAY_SIZE(sun60i_a733_pck600_pds), .logic_power_switch0_delay_offset = 0xc00, .logic_power_switch1_delay_offset = 0xc04, .off2on_delay_offset = 0xc10, From d8985743dcf86245cd4583a7855e248955d42c2b Mon Sep 17 00:00:00 2001 From: Rakesh Kota Date: Fri, 22 May 2026 13:51:54 +0530 Subject: [PATCH 2642/5214] dt-bindings: power: qcom,rpmpd: document the Shikra RPM Power Domains Document the RPM Power Domains on the Shikra Platform. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Rakesh Kota Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 779380cc7e44..0744a867c79e 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -56,6 +56,7 @@ properties: - qcom,sdx55-rpmhpd - qcom,sdx65-rpmhpd - qcom,sdx75-rpmhpd + - qcom,shikra-rpmpd - qcom,sm4450-rpmhpd - qcom,sm6115-rpmpd - qcom,sm6125-rpmpd From eaefa3d6095b88227bf374e3a36fe2e9fab4cbbc Mon Sep 17 00:00:00 2001 From: Kamal Wadhwa Date: Tue, 14 Apr 2026 11:59:09 +0800 Subject: [PATCH 2643/5214] pmdomain: qcom: rpmhpd: Add power domains for Nord SoC Add RPMh power domains required for Nord SoC. This includes new definitions for power domains supplying GFX1 and NSP3 subsystem. Co-developed-by: Bartosz Golaszewski Signed-off-by: Bartosz Golaszewski Signed-off-by: Kamal Wadhwa Signed-off-by: Shawn Guo Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index ba0cf4694435..63120e703923 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -122,6 +122,11 @@ static struct rpmhpd gfx = { .res_name = "gfx.lvl", }; +static struct rpmhpd gfx1 = { + .pd = { .name = "gfx1", }, + .res_name = "gfx1.lvl", +}; + static struct rpmhpd lcx = { .pd = { .name = "lcx", }, .res_name = "lcx.lvl", @@ -217,6 +222,11 @@ static struct rpmhpd nsp2 = { .res_name = "nsp2.lvl", }; +static struct rpmhpd nsp3 = { + .pd = { .name = "nsp3", }, + .res_name = "nsp3.lvl", +}; + static struct rpmhpd qphy = { .pd = { .name = "qphy", }, .res_name = "qphy.lvl", @@ -308,6 +318,30 @@ static const struct rpmhpd_desc sa8775p_desc = { .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), }; +/* Nord RPMH powerdomains */ +static struct rpmhpd *nord_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_GFX1] = &gfx1, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP0] = &nsp0, + [RPMHPD_NSP1] = &nsp1, + [RPMHPD_NSP2] = &nsp2, + [RPMHPD_NSP3] = &nsp3, +}; + +static const struct rpmhpd_desc nord_desc = { + .rpmhpds = nord_rpmhpds, + .num_pds = ARRAY_SIZE(nord_rpmhpds), +}; + /* SAR2130P RPMH powerdomains */ static struct rpmhpd *sar2130p_rpmhpds[] = { [RPMHPD_CX] = &cx, @@ -856,6 +890,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,hawi-rpmhpd", .data = &hawi_desc }, { .compatible = "qcom,kaanapali-rpmhpd", .data = &kaanapali_desc }, { .compatible = "qcom,milos-rpmhpd", .data = &milos_desc }, + { .compatible = "qcom,nord-rpmhpd", .data = &nord_desc }, { .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc }, { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, From b3677645c72686a7e707aff233de5aa4888cb8fd Mon Sep 17 00:00:00 2001 From: Rakesh Kota Date: Fri, 22 May 2026 13:51:55 +0530 Subject: [PATCH 2644/5214] pmdomain: qcom: rpmpd: Add Shikra RPM Power Domains Add RPM power domain support for Shikra, reusing SM6125 power domains with RPM_SMD_LEVEL_TURBO_NO_CPR as the max state. Reviewed-by: Konrad Dybcio Signed-off-by: Rakesh Kota Reviewed-by: Dmitry Baryshkov Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmpd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 15a11ff282c3..5f55fc791131 100644 --- a/drivers/pmdomain/qcom/rpmpd.c +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -895,6 +895,12 @@ static const struct rpmpd_desc sm6125_desc = { .max_state = RPM_SMD_LEVEL_BINNING, }; +static const struct rpmpd_desc shikra_desc = { + .rpmpds = sm6125_rpmpds, + .num_pds = ARRAY_SIZE(sm6125_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, +}; + static struct rpmpd *sm6375_rpmpds[] = { [SM6375_VDDCX] = &cx_rwcx0_lvl, [SM6375_VDDCX_AO] = &cx_rwcx0_lvl_ao, @@ -949,6 +955,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { .compatible = "qcom,qm215-rpmpd", .data = &qm215_desc }, { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, + { .compatible = "qcom,shikra-rpmpd", .data = &shikra_desc }, { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, { .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc }, From e46d95f060a2d465413638c5861ee0c205ba12b5 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 28 May 2026 13:24:55 -0700 Subject: [PATCH 2645/5214] pmdomain: mediatek: mfg: move __packed after struct name to fix kernel-doc The kernel-doc parser cannot parse 'struct __packed mtk_mfg_opp_entry {'. Move __packed to the closing brace, which is the more common kernel style. Assisted-by: Opencode:Big-pickle Signed-off-by: Rosen Penev Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c index 3ce6fb74dd53..53bdab66cf15 100644 --- a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c +++ b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c @@ -236,14 +236,14 @@ struct __packed mtk_mfg_ipi_sleep_msg { * * This struct is part of the ABI with the EB firmware. Do not change it. */ -struct __packed mtk_mfg_opp_entry { +struct mtk_mfg_opp_entry { __le32 freq_khz; __le32 voltage_core; __le32 voltage_sram; __le32 posdiv; __le32 voltage_margin; __le32 power_mw; -}; +} __packed; struct mtk_mfg_mbox { struct mbox_client cl; From b1d1bfa26c661ad18a37456a3153b8f15826e0ad Mon Sep 17 00:00:00 2001 From: Lucas Rabaquim Date: Tue, 2 Jun 2026 15:11:49 -0300 Subject: [PATCH 2646/5214] iio: light: tsl2591: remove unneeded tsl2591_compatible_als_persist_cycle() The function was only used to verify if als_persist is a TSL2591_PRST_ALS_INT_CYCLE_* value. However, before its call in tsl2591_write_event_value(), the line als_persist = tsl2591_persist_lit_to_cycle(period) is executed, meaning that by the time tsl2591_compatible_als_persist_cycle() is reached, als_persist is a TSL2591_PRST_ALS_INT_CYCLE_* value, making the verification pointless. Suggested-by: Sashiko Link: https://sashiko.dev/#/patchset/20260528185912.24774-1-matheus.feitosa%40usp.br Signed-off-by: Lucas Rabaquim Co-developed-by: Matheus Silveira Signed-off-by: Matheus Silveira Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2591.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index ac5e1822b993..f3ffa9721ad5 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -308,31 +308,6 @@ static int tsl2591_compatible_gain(struct tsl2591_chip *chip, const u8 als_gain) } } -static int tsl2591_compatible_als_persist_cycle(struct tsl2591_chip *chip, - const u32 als_persist) -{ - switch (als_persist) { - case TSL2591_PRST_ALS_INT_CYCLE_ANY: - case TSL2591_PRST_ALS_INT_CYCLE_2: - case TSL2591_PRST_ALS_INT_CYCLE_3: - case TSL2591_PRST_ALS_INT_CYCLE_5: - case TSL2591_PRST_ALS_INT_CYCLE_10: - case TSL2591_PRST_ALS_INT_CYCLE_15: - case TSL2591_PRST_ALS_INT_CYCLE_20: - case TSL2591_PRST_ALS_INT_CYCLE_25: - case TSL2591_PRST_ALS_INT_CYCLE_30: - case TSL2591_PRST_ALS_INT_CYCLE_35: - case TSL2591_PRST_ALS_INT_CYCLE_40: - case TSL2591_PRST_ALS_INT_CYCLE_45: - case TSL2591_PRST_ALS_INT_CYCLE_50: - case TSL2591_PRST_ALS_INT_CYCLE_55: - case TSL2591_PRST_ALS_INT_CYCLE_60: - return 0; - default: - return -EINVAL; - } -} - static int tsl2591_check_als_valid(struct i2c_client *client) { int ret; @@ -914,10 +889,6 @@ static int tsl2591_write_event_value(struct iio_dev *indio_dev, goto err_unlock; } - ret = tsl2591_compatible_als_persist_cycle(chip, als_persist); - if (ret < 0) - goto err_unlock; - ret = tsl2591_set_als_persist_cycle(chip, als_persist); if (ret < 0) goto err_unlock; From a3f3859cecacb64f18fd446271ece9a3b3f2d4de Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 3 Jun 2026 12:06:34 +0000 Subject: [PATCH 2647/5214] ipmi: fix refcount leak in i_ipmi_request() When a caller provides a `supplied_recv` message to i_ipmi_request(), the function increments the user's `nr_msgs` reference count. If an error occurs later, the out_err cleanup path only frees the recv_msg if the function allocated it itself (i.e., !supplied_recv). In the supplied_recv case the cleanup is skipped, leaving the reference count elevated. The caller ipmi_request_supply_msgs() does not release the supplied_recv on error, so the reference is permanently leaked. Fix this by explicitly reverting the reference count operations when a supplied recv_msg with a valid user pointer is present in the error path: decrement nr_msgs and drop the user's kref. Cc: stable@vger.kernel.org Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Signed-off-by: Wentao Liang Message-ID: <20260603120634.3758747-1-vulab@iscas.ac.cn> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7ca2cacbaa05..ab4c85f3d6fe 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2345,6 +2345,10 @@ static int i_ipmi_request(struct ipmi_user *user, if (smi_msg == NULL) { if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } return -ENOMEM; } } @@ -2418,6 +2422,10 @@ static int i_ipmi_request(struct ipmi_user *user, ipmi_free_smi_msg(smi_msg); if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } } return rv; } From 42a842f3f6b9eaa361914c96a7c974973e1e2132 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:44 -0700 Subject: [PATCH 2648/5214] KVM: SVM: Truncate INVLPGA address in compatibility mode Check for full 64-bit mode, not just long mode, when truncating the virtual address as part of INVLPGA emulation. Compatibility mode doesn't support 64-bit addressing. Note, the FIXME still applies, e.g. if the guest deliberately targeted EAX while in 64-bit via an address size override. That flaw isn't worth fixing as it would require decoding the code stream, which would open an entirely different can of worms, and in practice no sane guest would shove garbage into RAX[63:32] and execute INVLPGA. Note #2, VMSAVE, VMLOAD, and VMRUN all suffer from the same architectural flaw of not providing the full linear address in a VMCB exit information field, because, quoting the APM verbatim: the linear address is available directly from the guest rAX register (VMSAVE, VMLOAD, and VMRUN take a physical address, but their behavior with respect to rAX is otherwise identical). Fixes: bc9eff67fc35 ("KVM: SVM: Use default rAX size for INVLPGA emulation") Reviewed-by: Yosry Ahmed Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 95c411da6f2c..be15cb516803 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2404,7 +2404,7 @@ static int invlpga_interception(struct kvm_vcpu *vcpu) return 1; /* FIXME: Handle an address size prefix. */ - if (!is_long_mode(vcpu)) + if (!is_64_bit_mode(vcpu)) gva = (u32)gva; trace_kvm_invlpga(to_svm(vcpu)->vmcb->save.rip, asid, gva); From 09912b8ad22f981d83818fc83a141dd8216d9ffb Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:45 -0700 Subject: [PATCH 2649/5214] KVM: x86/xen: Bug the VM if 32-bit KVM observes a 64-bit mode hypercall Bug the VM if 32-bit KVM attempts to handle a 64-bit hypercall, primarily so that a future change to set "input" in mode-specific code doesn't trigger a false positive warn=>error: arch/x86/kvm/xen.c:1687:6: error: variable 'input' is used uninitialized whenever 'if' condition is false [-Werror,-Wsometimes-uninitialized] 1687 | if (!longmode) { | ^~~~~~~~~ arch/x86/kvm/xen.c:1708:31: note: uninitialized use occurs here 1708 | trace_kvm_xen_hypercall(cpl, input, params[0], params[1], params[2], | ^~~~~ x86/kvm/xen.c:1687:2: note: remove the 'if' if its condition is always true 1687 | if (!longmode) { | ^~~~~~~~~~~~~~ arch/x86/kvm/xen.c:1677:11: note: initialize the variable 'input' to silence this warning 1677 | u64 input, params[6], r = -ENOSYS; | ^ 1 error generated. Note, params[] also has the same flaw, but -Wsometimes-uninitialized doesn't seem to be enforced for arrays, presumably because it's difficult to avoid false positives on specific entries. Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-3-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/xen.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 91fd3673c09a..6d9be74bb673 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1694,16 +1694,19 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu) params[4] = (u32)kvm_rdi_read(vcpu); params[5] = (u32)kvm_rbp_read(vcpu); } -#ifdef CONFIG_X86_64 else { +#ifdef CONFIG_X86_64 params[0] = (u64)kvm_rdi_read(vcpu); params[1] = (u64)kvm_rsi_read(vcpu); params[2] = (u64)kvm_rdx_read(vcpu); params[3] = (u64)kvm_r10_read(vcpu); params[4] = (u64)kvm_r8_read(vcpu); params[5] = (u64)kvm_r9_read(vcpu); - } +#else + KVM_BUG_ON(1, vcpu->kvm); + return -EIO; #endif + } cpl = kvm_x86_call(get_cpl)(vcpu); trace_kvm_xen_hypercall(cpl, input, params[0], params[1], params[2], params[3], params[4], params[5]); From 9377016c2f9e2e25dca5788ac109190daf116134 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:46 -0700 Subject: [PATCH 2650/5214] KVM: x86/xen: Don't truncate RAX when handling hypercall from protected guest Don't truncate RAX when handling a Xen hypercall for a guest with protected state, as KVM's ABI is to assume the guest is in 64-bit for such cases (the guest leaving garbage in 63:32 after a transition to 32-bit mode is far less likely than 63:32 being necessary to complete the hypercall). Fixes: b5aead0064f3 ("KVM: x86: Assume a 64-bit hypercall for guests with protected state") Reviewed-by: David Woodhouse Link: https://patch.msgid.link/20260529222223.870923-4-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/xen.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 6d9be74bb673..895095dc684e 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1678,15 +1678,14 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu) bool handled = false; u8 cpl; - input = (u64)kvm_register_read(vcpu, VCPU_REGS_RAX); - /* Hyper-V hypercalls get bit 31 set in EAX */ - if ((input & 0x80000000) && + if ((kvm_rax_read(vcpu) & 0x80000000) && kvm_hv_hypercall_enabled(vcpu)) return kvm_hv_hypercall(vcpu); longmode = is_64_bit_hypercall(vcpu); if (!longmode) { + input = (u32)kvm_rax_read(vcpu); params[0] = (u32)kvm_rbx_read(vcpu); params[1] = (u32)kvm_rcx_read(vcpu); params[2] = (u32)kvm_rdx_read(vcpu); @@ -1696,6 +1695,7 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu) } else { #ifdef CONFIG_X86_64 + input = (u64)kvm_rax_read(vcpu); params[0] = (u64)kvm_rdi_read(vcpu); params[1] = (u64)kvm_rsi_read(vcpu); params[2] = (u64)kvm_rdx_read(vcpu); From f43caad54cc0d7ab8c3a08092687fad515ce8da6 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:47 -0700 Subject: [PATCH 2651/5214] KVM: VMX: Read 32-bit GPR values for ENCLS instructions outside of 64-bit mode When getting register values for ENCLS emulation, use kvm_register_read() instead of kvm__read() so that bits 63:32 of the register are dropped if the guest is in 32-bit mode. Note, the misleading/surprising behavior of kvm__read() being "raw" variants under the hood will be addressed once all non-benign bugs are fixed. Fixes: 70210c044b4e ("KVM: VMX: Add SGX ENCLS[ECREATE] handler to enforce CPUID restrictions") Fixes: b6f084ca5538 ("KVM: VMX: Add ENCLS[EINIT] handler to support SGX Launch Control (LC)") Acked-by: Kai Huang Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-5-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/sgx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index df1d0cf76947..4c61fc33f764 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -225,8 +225,8 @@ static int handle_encls_ecreate(struct kvm_vcpu *vcpu) struct x86_exception ex; int r; - if (sgx_get_encls_gva(vcpu, kvm_rbx_read(vcpu), 32, 32, &pageinfo_gva) || - sgx_get_encls_gva(vcpu, kvm_rcx_read(vcpu), 4096, 4096, &secs_gva)) + if (sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RBX), 32, 32, &pageinfo_gva) || + sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RCX), 4096, 4096, &secs_gva)) return 1; /* @@ -302,9 +302,9 @@ static int handle_encls_einit(struct kvm_vcpu *vcpu) gpa_t sig_gpa, secs_gpa, token_gpa; int ret, trapnr; - if (sgx_get_encls_gva(vcpu, kvm_rbx_read(vcpu), 1808, 4096, &sig_gva) || - sgx_get_encls_gva(vcpu, kvm_rcx_read(vcpu), 4096, 4096, &secs_gva) || - sgx_get_encls_gva(vcpu, kvm_rdx_read(vcpu), 304, 512, &token_gva)) + if (sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RBX), 1808, 4096, &sig_gva) || + sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RCX), 4096, 4096, &secs_gva) || + sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RDX), 304, 512, &token_gva)) return 1; /* From 16b5d193b2127caab46ca5840e5d945732215e55 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:48 -0700 Subject: [PATCH 2652/5214] KVM: x86: Trace hypercall register *after* truncating values for 32-bit When tracing hypercalls, invoke the tracepoint *after* truncating the register values for 32-bit guests so as not to record unused garbage (in the extremely unlikely scenario that the guest left garbage in a register after transitioning from 64-bit mode to 32-bit mode). Fixes: 229456fc34b1 ("KVM: convert custom marker based tracing to event traces") Reviewed-by: Yosry Ahmed Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-6-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 57ce0f1f1860..63ee091aed70 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10433,8 +10433,6 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, ++vcpu->stat.hypercalls; - trace_kvm_hypercall(nr, a0, a1, a2, a3); - if (!op_64_bit) { nr &= 0xFFFFFFFF; a0 &= 0xFFFFFFFF; @@ -10443,6 +10441,8 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, a3 &= 0xFFFFFFFF; } + trace_kvm_hypercall(nr, a0, a1, a2, a3); + if (cpl) { ret = -KVM_EPERM; goto out; From ece08316ca21ac2a3f3cc47a27b25f95adf1cfb2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:49 -0700 Subject: [PATCH 2653/5214] KVM: x86: Rename kvm_cache_regs.h => regs.h Rename kvm_cache_regs.h to simply regs.h, as the "cache" nomenclature is already a lie (the file deals with state/registers that aren't cached per se), and so that more code/functionality can be landed in the header without making it a truly horrible misnomer. Deliberately drop the kvm_ prefix/namespace to align with other "local" headers, and to further differentiate regs.h from the public/global arch/x86/include/asm/kvm_vcpu_regs.h, which sadly needs to stay in asm/ so that the number of registers can be referenced by kvm_vcpu_arch. No functional change intended. Reviewed-by: Yosry Ahmed Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-7-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/emulate.c | 2 +- arch/x86/kvm/lapic.c | 2 +- arch/x86/kvm/mmu.h | 2 +- arch/x86/kvm/mmu/mmu.c | 2 +- arch/x86/kvm/{kvm_cache_regs.h => regs.h} | 4 ++-- arch/x86/kvm/smm.c | 2 +- arch/x86/kvm/svm/svm.c | 2 +- arch/x86/kvm/svm/svm.h | 2 +- arch/x86/kvm/vmx/nested.h | 2 +- arch/x86/kvm/vmx/sgx.c | 2 +- arch/x86/kvm/vmx/vmx.c | 2 +- arch/x86/kvm/vmx/vmx.h | 2 +- arch/x86/kvm/x86.c | 2 +- arch/x86/kvm/x86.h | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) rename arch/x86/kvm/{kvm_cache_regs.h => regs.h} (99%) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a367828ce869..94e253148771 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -20,7 +20,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include "kvm_cache_regs.h" +#include "regs.h" #include "kvm_emulate.h" #include #include diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 4078e624ca66..d8dbfb107bfb 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -37,7 +37,7 @@ #include #include #include -#include "kvm_cache_regs.h" +#include "regs.h" #include "irq.h" #include "ioapic.h" #include "trace.h" diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index ddf4e467c071..e1bb663ebbd5 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -3,7 +3,7 @@ #define __KVM_X86_MMU_H #include -#include "kvm_cache_regs.h" +#include "regs.h" #include "x86.h" #include "cpuid.h" diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f8aa7eda661e..d7a929ea2a26 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -22,7 +22,7 @@ #include "mmu_internal.h" #include "tdp_mmu.h" #include "x86.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "smm.h" #include "kvm_emulate.h" #include "page_track.h" diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/regs.h similarity index 99% rename from arch/x86/kvm/kvm_cache_regs.h rename to arch/x86/kvm/regs.h index 2ae492ad6412..4440f3992fce 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ASM_KVM_CACHE_REGS_H -#define ASM_KVM_CACHE_REGS_H +#ifndef ARCH_X86_KVM_REGS_H +#define ARCH_X86_KVM_REGS_H #include diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c index f623c5986119..a446487bdd5c 100644 --- a/arch/x86/kvm/smm.c +++ b/arch/x86/kvm/smm.c @@ -3,7 +3,7 @@ #include #include "x86.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "kvm_emulate.h" #include "smm.h" #include "cpuid.h" diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index be15cb516803..faa1ca7588b9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4,7 +4,7 @@ #include "irq.h" #include "mmu.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "x86.h" #include "smm.h" #include "cpuid.h" diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 19b80ef56e2b..38d9f296159d 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -23,7 +23,7 @@ #include #include "cpuid.h" -#include "kvm_cache_regs.h" +#include "regs.h" /* * Helpers to convert to/from physical addresses for pages whose address is diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h index 213a448104af..6d6cd5904ddf 100644 --- a/arch/x86/kvm/vmx/nested.h +++ b/arch/x86/kvm/vmx/nested.h @@ -2,7 +2,7 @@ #ifndef __KVM_X86_VMX_NESTED_H #define __KVM_X86_VMX_NESTED_H -#include "kvm_cache_regs.h" +#include "regs.h" #include "hyperv.h" #include "vmcs12.h" #include "vmx.h" diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index 4c61fc33f764..66c315554b46 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -6,7 +6,7 @@ #include #include "x86.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "nested.h" #include "sgx.h" #include "vmx.h" diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index cf9f2f55f569..18ce36bdce6e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -59,7 +59,7 @@ #include "hyperv.h" #include "kvm_onhyperv.h" #include "irq.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "lapic.h" #include "mmu.h" #include "nested.h" diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index daedf663c0a9..de9de0d2016c 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -10,7 +10,7 @@ #include #include "capabilities.h" -#include "../kvm_cache_regs.h" +#include "../regs.h" #include "pmu_intel.h" #include "vmcs.h" #include "vmx_ops.h" diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 63ee091aed70..da7287546846 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -23,7 +23,7 @@ #include "mmu.h" #include "i8254.h" #include "tss.h" -#include "kvm_cache_regs.h" +#include "regs.h" #include "kvm_emulate.h" #include "mmu/page_track.h" #include "x86.h" diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index aa7d5b757fb5..fee92c603ad0 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -6,7 +6,7 @@ #include #include #include -#include "kvm_cache_regs.h" +#include "regs.h" #include "kvm_emulate.h" #include "cpuid.h" From ed8a7b89c504aa24c01014dd4ebde50dbf8ac9d0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:50 -0700 Subject: [PATCH 2654/5214] KVM: x86: Move inlined GPR, CR, and DR helpers from x86.h to regs.h Move inlined General Purpose Register, Control Register, and Debug Register helpers from x86.h to the aptly named regs.h, to help trim down x86.h (and x86.c in the future). Move *very* select EFER functionality as well, but leave behind the bulk of EFER handling and all other MSR handling. There is more than enough MSR code to carve out msrs.{c,h} in the future. Give is_long_bit_mode() special treatment as it's more along the lines of a CR4 bit check, but just happens to be accessed through an MSR interface. And more importantly, because giving regs.h access to is_long_bit_mode() greatly simplifies dependency chains. No functional change intended. Reviewed-by: Yosry Ahmed Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-8-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/regs.h | 123 ++++++++++++++++++++++++++++++++++++++++++-- arch/x86/kvm/x86.h | 117 ----------------------------------------- 2 files changed, 120 insertions(+), 120 deletions(-) diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h index 4440f3992fce..62cc9deea226 100644 --- a/arch/x86/kvm/regs.h +++ b/arch/x86/kvm/regs.h @@ -16,6 +16,37 @@ static_assert(!(KVM_POSSIBLE_CR0_GUEST_BITS & X86_CR0_PDPTR_BITS)); +static inline bool is_long_mode(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_X86_64 + return !!(vcpu->arch.efer & EFER_LMA); +#else + return false; +#endif +} + +static inline bool is_64_bit_mode(struct kvm_vcpu *vcpu) +{ + int cs_db, cs_l; + + WARN_ON_ONCE(vcpu->arch.guest_state_protected); + + if (!is_long_mode(vcpu)) + return false; + kvm_x86_call(get_cs_db_l_bits)(vcpu, &cs_db, &cs_l); + return cs_l; +} + +static inline bool is_64_bit_hypercall(struct kvm_vcpu *vcpu) +{ + /* + * If running with protected guest state, the CS register is not + * accessible. The hypercall register values will have had to been + * provided in 64-bit mode, so assume the guest is in 64-bit. + */ + return vcpu->arch.guest_state_protected || is_64_bit_mode(vcpu); +} + #define BUILD_KVM_GPR_ACCESSORS(lname, uname) \ static __always_inline unsigned long kvm_##lname##_read(struct kvm_vcpu *vcpu)\ { \ @@ -143,6 +174,13 @@ static inline unsigned long kvm_register_read_raw(struct kvm_vcpu *vcpu, int reg return vcpu->arch.regs[reg]; } +static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu, int reg) +{ + unsigned long val = kvm_register_read_raw(vcpu, reg); + + return is_64_bit_mode(vcpu) ? val : (u32)val; +} + static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, unsigned long val) { @@ -153,6 +191,14 @@ static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, kvm_register_mark_dirty(vcpu, reg); } +static inline void kvm_register_write(struct kvm_vcpu *vcpu, + int reg, unsigned long val) +{ + if (!is_64_bit_mode(vcpu)) + val = (u32)val; + return kvm_register_write_raw(vcpu, reg, val); +} + static inline unsigned long kvm_rip_read(struct kvm_vcpu *vcpu) { if (!kvm_register_is_available(vcpu, VCPU_REG_RIP)) @@ -177,6 +223,12 @@ static inline void kvm_rsp_write(struct kvm_vcpu *vcpu, unsigned long val) kvm_register_write_raw(vcpu, VCPU_REGS_RSP, val); } +static inline u64 kvm_read_edx_eax(struct kvm_vcpu *vcpu) +{ + return (kvm_rax_read(vcpu) & -1u) + | ((u64)(kvm_rdx_read(vcpu) & -1u) << 32); +} + static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index) { might_sleep(); /* on svm */ @@ -243,10 +295,75 @@ static inline ulong kvm_read_cr4(struct kvm_vcpu *vcpu) return kvm_read_cr4_bits(vcpu, ~0UL); } -static inline u64 kvm_read_edx_eax(struct kvm_vcpu *vcpu) +static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { - return (kvm_rax_read(vcpu) & -1u) - | ((u64)(kvm_rdx_read(vcpu) & -1u) << 32); + return !(cr4 & vcpu->arch.cr4_guest_rsvd_bits); +} + +#define __cr4_reserved_bits(__cpu_has, __c) \ +({ \ + u64 __reserved_bits = CR4_RESERVED_BITS; \ + \ + if (!__cpu_has(__c, X86_FEATURE_XSAVE)) \ + __reserved_bits |= X86_CR4_OSXSAVE; \ + if (!__cpu_has(__c, X86_FEATURE_SMEP)) \ + __reserved_bits |= X86_CR4_SMEP; \ + if (!__cpu_has(__c, X86_FEATURE_SMAP)) \ + __reserved_bits |= X86_CR4_SMAP; \ + if (!__cpu_has(__c, X86_FEATURE_FSGSBASE)) \ + __reserved_bits |= X86_CR4_FSGSBASE; \ + if (!__cpu_has(__c, X86_FEATURE_PKU)) \ + __reserved_bits |= X86_CR4_PKE; \ + if (!__cpu_has(__c, X86_FEATURE_LA57)) \ + __reserved_bits |= X86_CR4_LA57; \ + if (!__cpu_has(__c, X86_FEATURE_UMIP)) \ + __reserved_bits |= X86_CR4_UMIP; \ + if (!__cpu_has(__c, X86_FEATURE_VMX)) \ + __reserved_bits |= X86_CR4_VMXE; \ + if (!__cpu_has(__c, X86_FEATURE_PCID)) \ + __reserved_bits |= X86_CR4_PCIDE; \ + if (!__cpu_has(__c, X86_FEATURE_LAM)) \ + __reserved_bits |= X86_CR4_LAM_SUP; \ + if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ + !__cpu_has(__c, X86_FEATURE_IBT)) \ + __reserved_bits |= X86_CR4_CET; \ + __reserved_bits; \ +}) + +static inline bool is_protmode(struct kvm_vcpu *vcpu) +{ + return kvm_is_cr0_bit_set(vcpu, X86_CR0_PE); +} + +static inline bool is_pae(struct kvm_vcpu *vcpu) +{ + return kvm_is_cr4_bit_set(vcpu, X86_CR4_PAE); +} + +static inline bool is_pse(struct kvm_vcpu *vcpu) +{ + return kvm_is_cr4_bit_set(vcpu, X86_CR4_PSE); +} + +static inline bool is_paging(struct kvm_vcpu *vcpu) +{ + return likely(kvm_is_cr0_bit_set(vcpu, X86_CR0_PG)); +} + +static inline bool is_pae_paging(struct kvm_vcpu *vcpu) +{ + return !is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu); +} + +static inline bool kvm_dr7_valid(u64 data) +{ + /* Bits [63:32] are reserved */ + return !(data >> 32); +} +static inline bool kvm_dr6_valid(u64 data) +{ + /* Bits [63:32] are reserved */ + return !(data >> 32); } static inline void enter_guest_mode(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index fee92c603ad0..e06f0ee63515 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -243,42 +243,6 @@ static inline bool kvm_exception_is_soft(unsigned int nr) return (nr == BP_VECTOR) || (nr == OF_VECTOR); } -static inline bool is_protmode(struct kvm_vcpu *vcpu) -{ - return kvm_is_cr0_bit_set(vcpu, X86_CR0_PE); -} - -static inline bool is_long_mode(struct kvm_vcpu *vcpu) -{ -#ifdef CONFIG_X86_64 - return !!(vcpu->arch.efer & EFER_LMA); -#else - return false; -#endif -} - -static inline bool is_64_bit_mode(struct kvm_vcpu *vcpu) -{ - int cs_db, cs_l; - - WARN_ON_ONCE(vcpu->arch.guest_state_protected); - - if (!is_long_mode(vcpu)) - return false; - kvm_x86_call(get_cs_db_l_bits)(vcpu, &cs_db, &cs_l); - return cs_l; -} - -static inline bool is_64_bit_hypercall(struct kvm_vcpu *vcpu) -{ - /* - * If running with protected guest state, the CS register is not - * accessible. The hypercall register values will have had to been - * provided in 64-bit mode, so assume the guest is in 64-bit. - */ - return vcpu->arch.guest_state_protected || is_64_bit_mode(vcpu); -} - static inline bool x86_exception_has_error_code(unsigned int vector) { static u32 exception_has_error_code = BIT(DF_VECTOR) | BIT(TS_VECTOR) | @@ -293,26 +257,6 @@ static inline bool mmu_is_nested(struct kvm_vcpu *vcpu) return vcpu->arch.walk_mmu == &vcpu->arch.nested_mmu; } -static inline bool is_pae(struct kvm_vcpu *vcpu) -{ - return kvm_is_cr4_bit_set(vcpu, X86_CR4_PAE); -} - -static inline bool is_pse(struct kvm_vcpu *vcpu) -{ - return kvm_is_cr4_bit_set(vcpu, X86_CR4_PSE); -} - -static inline bool is_paging(struct kvm_vcpu *vcpu) -{ - return likely(kvm_is_cr0_bit_set(vcpu, X86_CR0_PG)); -} - -static inline bool is_pae_paging(struct kvm_vcpu *vcpu) -{ - return !is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu); -} - static inline u8 vcpu_virt_addr_bits(struct kvm_vcpu *vcpu) { return kvm_is_cr4_bit_set(vcpu, X86_CR4_LA57) ? 57 : 48; @@ -421,21 +365,6 @@ static inline bool vcpu_match_mmio_gpa(struct kvm_vcpu *vcpu, gpa_t gpa) return false; } -static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu, int reg) -{ - unsigned long val = kvm_register_read_raw(vcpu, reg); - - return is_64_bit_mode(vcpu) ? val : (u32)val; -} - -static inline void kvm_register_write(struct kvm_vcpu *vcpu, - int reg, unsigned long val) -{ - if (!is_64_bit_mode(vcpu)) - val = (u32)val; - return kvm_register_write_raw(vcpu, reg, val); -} - static inline bool kvm_check_has_quirk(struct kvm *kvm, u64 quirk) { return !(kvm->arch.disabled_quirks & quirk); @@ -630,17 +559,6 @@ static inline bool kvm_pat_valid(u64 data) return (data | ((data & 0x0202020202020202ull) << 1)) == data; } -static inline bool kvm_dr7_valid(u64 data) -{ - /* Bits [63:32] are reserved */ - return !(data >> 32); -} -static inline bool kvm_dr6_valid(u64 data) -{ - /* Bits [63:32] are reserved */ - return !(data >> 32); -} - /* * Trigger machine check on the host. We assume all the MSRs are already set up * by the CPU and that we still run on the same CPU as the MCE occurred on. @@ -687,41 +605,6 @@ enum kvm_msr_access { #define KVM_MSR_RET_UNSUPPORTED 2 #define KVM_MSR_RET_FILTERED 3 -static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) -{ - return !(cr4 & vcpu->arch.cr4_guest_rsvd_bits); -} - -#define __cr4_reserved_bits(__cpu_has, __c) \ -({ \ - u64 __reserved_bits = CR4_RESERVED_BITS; \ - \ - if (!__cpu_has(__c, X86_FEATURE_XSAVE)) \ - __reserved_bits |= X86_CR4_OSXSAVE; \ - if (!__cpu_has(__c, X86_FEATURE_SMEP)) \ - __reserved_bits |= X86_CR4_SMEP; \ - if (!__cpu_has(__c, X86_FEATURE_SMAP)) \ - __reserved_bits |= X86_CR4_SMAP; \ - if (!__cpu_has(__c, X86_FEATURE_FSGSBASE)) \ - __reserved_bits |= X86_CR4_FSGSBASE; \ - if (!__cpu_has(__c, X86_FEATURE_PKU)) \ - __reserved_bits |= X86_CR4_PKE; \ - if (!__cpu_has(__c, X86_FEATURE_LA57)) \ - __reserved_bits |= X86_CR4_LA57; \ - if (!__cpu_has(__c, X86_FEATURE_UMIP)) \ - __reserved_bits |= X86_CR4_UMIP; \ - if (!__cpu_has(__c, X86_FEATURE_VMX)) \ - __reserved_bits |= X86_CR4_VMXE; \ - if (!__cpu_has(__c, X86_FEATURE_PCID)) \ - __reserved_bits |= X86_CR4_PCIDE; \ - if (!__cpu_has(__c, X86_FEATURE_LAM)) \ - __reserved_bits |= X86_CR4_LAM_SUP; \ - if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ - !__cpu_has(__c, X86_FEATURE_IBT)) \ - __reserved_bits |= X86_CR4_CET; \ - __reserved_bits; \ -}) - int kvm_sev_es_mmio(struct kvm_vcpu *vcpu, bool is_write, gpa_t gpa, unsigned int bytes, void *data); int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size, From fc40b1254c32eb76c9147464cf2ad92b3e03bcad Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:51 -0700 Subject: [PATCH 2655/5214] KVM: x86: Add mode-aware versions of kvm__{read,write}() helpers Make kvm__{read,write}() mode-aware (where the value is truncated to 32 bits if the vCPU isn't in 64-bit mode), and convert all the intentional "raw" accesses to kvm__{read,write}_raw() versions. To avoid confusion and bikeshedding over whether or not explicit 32-bit accesses should use the "raw" or mode-aware variants, add and use "e" versions, e.g. for things like RDMSR, WRMSR, and CPUID, where the instruction uses only bits 31:0, regardless of mode. No functional change intended (all use of "e" versions is for cases where the value is already truncated due to bouncing through a u32). Cc: Binbin Wu Cc: Kai Huang Reviewed-by: Binbin Wu Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260529222223.870923-9-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/cpuid.c | 12 ++-- arch/x86/kvm/hyperv.c | 21 +++---- arch/x86/kvm/hyperv.h | 4 +- arch/x86/kvm/regs.h | 90 +++++++++++++++++----------- arch/x86/kvm/svm/nested.c | 6 +- arch/x86/kvm/svm/svm.c | 7 +-- arch/x86/kvm/vmx/nested.c | 8 +-- arch/x86/kvm/vmx/sgx.c | 4 +- arch/x86/kvm/vmx/tdx.c | 18 +++--- arch/x86/kvm/x86.c | 121 +++++++++++++++++++------------------- arch/x86/kvm/xen.c | 32 +++++----- 11 files changed, 171 insertions(+), 152 deletions(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 8e5340dd2621..fd3b02575cd0 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -2166,13 +2166,13 @@ int kvm_emulate_cpuid(struct kvm_vcpu *vcpu) return 1; } - eax = kvm_rax_read(vcpu); - ecx = kvm_rcx_read(vcpu); + eax = kvm_eax_read(vcpu); + ecx = kvm_ecx_read(vcpu); kvm_cpuid(vcpu, &eax, &ebx, &ecx, &edx, false); - kvm_rax_write(vcpu, eax); - kvm_rbx_write(vcpu, ebx); - kvm_rcx_write(vcpu, ecx); - kvm_rdx_write(vcpu, edx); + kvm_eax_write(vcpu, eax); + kvm_ebx_write(vcpu, ebx); + kvm_ecx_write(vcpu, ecx); + kvm_edx_write(vcpu, edx); return kvm_skip_emulated_instruction(vcpu); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_cpuid); diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 015c6947b462..3551af9a9453 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -2377,10 +2377,10 @@ static void kvm_hv_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result) longmode = is_64_bit_hypercall(vcpu); if (longmode) - kvm_rax_write(vcpu, result); + kvm_rax_write_raw(vcpu, result); else { - kvm_rdx_write(vcpu, result >> 32); - kvm_rax_write(vcpu, result & 0xffffffff); + kvm_edx_write(vcpu, result >> 32); + kvm_eax_write(vcpu, result); } } @@ -2544,18 +2544,15 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu) #ifdef CONFIG_X86_64 if (is_64_bit_hypercall(vcpu)) { - hc.param = kvm_rcx_read(vcpu); - hc.ingpa = kvm_rdx_read(vcpu); - hc.outgpa = kvm_r8_read(vcpu); + hc.param = kvm_rcx_read_raw(vcpu); + hc.ingpa = kvm_rdx_read_raw(vcpu); + hc.outgpa = kvm_r8_read_raw(vcpu); } else #endif { - hc.param = ((u64)kvm_rdx_read(vcpu) << 32) | - (kvm_rax_read(vcpu) & 0xffffffff); - hc.ingpa = ((u64)kvm_rbx_read(vcpu) << 32) | - (kvm_rcx_read(vcpu) & 0xffffffff); - hc.outgpa = ((u64)kvm_rdi_read(vcpu) << 32) | - (kvm_rsi_read(vcpu) & 0xffffffff); + hc.param = ((u64)kvm_edx_read(vcpu) << 32) | kvm_eax_read(vcpu); + hc.ingpa = ((u64)kvm_ebx_read(vcpu) << 32) | kvm_ecx_read(vcpu); + hc.outgpa = ((u64)kvm_edi_read(vcpu) << 32) | kvm_esi_read(vcpu); } hc.code = hc.param & 0xffff; diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h index 6301f79fcbae..65e89ed65349 100644 --- a/arch/x86/kvm/hyperv.h +++ b/arch/x86/kvm/hyperv.h @@ -232,8 +232,8 @@ static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu) if (!hv_vcpu) return false; - code = is_64_bit_hypercall(vcpu) ? kvm_rcx_read(vcpu) : - kvm_rax_read(vcpu); + code = is_64_bit_hypercall(vcpu) ? kvm_rcx_read_raw(vcpu) : + kvm_eax_read(vcpu); return (code == HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE || code == HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST || diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h index 62cc9deea226..12db5039aace 100644 --- a/arch/x86/kvm/regs.h +++ b/arch/x86/kvm/regs.h @@ -47,32 +47,61 @@ static inline bool is_64_bit_hypercall(struct kvm_vcpu *vcpu) return vcpu->arch.guest_state_protected || is_64_bit_mode(vcpu); } -#define BUILD_KVM_GPR_ACCESSORS(lname, uname) \ -static __always_inline unsigned long kvm_##lname##_read(struct kvm_vcpu *vcpu)\ -{ \ - return vcpu->arch.regs[VCPU_REGS_##uname]; \ -} \ -static __always_inline void kvm_##lname##_write(struct kvm_vcpu *vcpu, \ - unsigned long val) \ -{ \ - vcpu->arch.regs[VCPU_REGS_##uname] = val; \ -} -BUILD_KVM_GPR_ACCESSORS(rax, RAX) -BUILD_KVM_GPR_ACCESSORS(rbx, RBX) -BUILD_KVM_GPR_ACCESSORS(rcx, RCX) -BUILD_KVM_GPR_ACCESSORS(rdx, RDX) -BUILD_KVM_GPR_ACCESSORS(rbp, RBP) -BUILD_KVM_GPR_ACCESSORS(rsi, RSI) -BUILD_KVM_GPR_ACCESSORS(rdi, RDI) +static __always_inline unsigned long kvm_reg_mode_mask(struct kvm_vcpu *vcpu) +{ #ifdef CONFIG_X86_64 -BUILD_KVM_GPR_ACCESSORS(r8, R8) -BUILD_KVM_GPR_ACCESSORS(r9, R9) -BUILD_KVM_GPR_ACCESSORS(r10, R10) -BUILD_KVM_GPR_ACCESSORS(r11, R11) -BUILD_KVM_GPR_ACCESSORS(r12, R12) -BUILD_KVM_GPR_ACCESSORS(r13, R13) -BUILD_KVM_GPR_ACCESSORS(r14, R14) -BUILD_KVM_GPR_ACCESSORS(r15, R15) + return is_64_bit_mode(vcpu) ? GENMASK(63, 0) : GENMASK(31, 0); +#else + return GENMASK(31, 0); +#endif +} + +#define __BUILD_KVM_GPR_ACCESSORS(lname, uname) \ +static __always_inline unsigned long kvm_##lname##_read(struct kvm_vcpu *vcpu) \ +{ \ + return vcpu->arch.regs[VCPU_REGS_##uname] & kvm_reg_mode_mask(vcpu); \ +} \ +static __always_inline void kvm_##lname##_write(struct kvm_vcpu *vcpu, \ + unsigned long val) \ +{ \ + vcpu->arch.regs[VCPU_REGS_##uname] = val & kvm_reg_mode_mask(vcpu); \ +} \ +static __always_inline unsigned long kvm_##lname##_read_raw(struct kvm_vcpu *vcpu) \ +{ \ + return vcpu->arch.regs[VCPU_REGS_##uname]; \ +} \ +static __always_inline void kvm_##lname##_write_raw(struct kvm_vcpu *vcpu, \ + unsigned long val) \ +{ \ + vcpu->arch.regs[VCPU_REGS_##uname] = val; \ +} +#define BUILD_KVM_GPR_ACCESSORS(lname, uname) \ +static __always_inline u32 kvm_e##lname##_read(struct kvm_vcpu *vcpu) \ +{ \ + return vcpu->arch.regs[VCPU_REGS_##uname]; \ +} \ +static __always_inline void kvm_e##lname##_write(struct kvm_vcpu *vcpu, u32 val) \ +{ \ + vcpu->arch.regs[VCPU_REGS_##uname] = val; \ +} \ +__BUILD_KVM_GPR_ACCESSORS(r##lname, uname) + +BUILD_KVM_GPR_ACCESSORS(ax, RAX) +BUILD_KVM_GPR_ACCESSORS(bx, RBX) +BUILD_KVM_GPR_ACCESSORS(cx, RCX) +BUILD_KVM_GPR_ACCESSORS(dx, RDX) +BUILD_KVM_GPR_ACCESSORS(bp, RBP) +BUILD_KVM_GPR_ACCESSORS(si, RSI) +BUILD_KVM_GPR_ACCESSORS(di, RDI) +#ifdef CONFIG_X86_64 +__BUILD_KVM_GPR_ACCESSORS(r8, R8) +__BUILD_KVM_GPR_ACCESSORS(r9, R9) +__BUILD_KVM_GPR_ACCESSORS(r10, R10) +__BUILD_KVM_GPR_ACCESSORS(r11, R11) +__BUILD_KVM_GPR_ACCESSORS(r12, R12) +__BUILD_KVM_GPR_ACCESSORS(r13, R13) +__BUILD_KVM_GPR_ACCESSORS(r14, R14) +__BUILD_KVM_GPR_ACCESSORS(r15, R15) #endif /* @@ -176,9 +205,7 @@ static inline unsigned long kvm_register_read_raw(struct kvm_vcpu *vcpu, int reg static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu, int reg) { - unsigned long val = kvm_register_read_raw(vcpu, reg); - - return is_64_bit_mode(vcpu) ? val : (u32)val; + return kvm_register_read_raw(vcpu, reg) & kvm_reg_mode_mask(vcpu); } static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, @@ -194,9 +221,7 @@ static inline void kvm_register_write_raw(struct kvm_vcpu *vcpu, int reg, static inline void kvm_register_write(struct kvm_vcpu *vcpu, int reg, unsigned long val) { - if (!is_64_bit_mode(vcpu)) - val = (u32)val; - return kvm_register_write_raw(vcpu, reg, val); + return kvm_register_write_raw(vcpu, reg, val & kvm_reg_mode_mask(vcpu)); } static inline unsigned long kvm_rip_read(struct kvm_vcpu *vcpu) @@ -225,8 +250,7 @@ static inline void kvm_rsp_write(struct kvm_vcpu *vcpu, unsigned long val) static inline u64 kvm_read_edx_eax(struct kvm_vcpu *vcpu) { - return (kvm_rax_read(vcpu) & -1u) - | ((u64)(kvm_rdx_read(vcpu) & -1u) << 32); + return kvm_eax_read(vcpu) | (u64)(kvm_edx_read(vcpu)) << 32; } static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 997a740c653d..be7671880261 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -775,7 +775,7 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) svm->vcpu.arch.cr2 = save->cr2; - kvm_rax_write(vcpu, save->rax); + kvm_rax_write_raw(vcpu, save->rax); kvm_rsp_write(vcpu, save->rsp); kvm_rip_write(vcpu, save->rip); @@ -1260,7 +1260,7 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu) vmcb12->save.rflags = kvm_get_rflags(vcpu); vmcb12->save.rip = kvm_rip_read(vcpu); vmcb12->save.rsp = kvm_rsp_read(vcpu); - vmcb12->save.rax = kvm_rax_read(vcpu); + vmcb12->save.rax = kvm_rax_read_raw(vcpu); vmcb12->save.dr7 = vmcb02->save.dr7; vmcb12->save.dr6 = svm->vcpu.arch.dr6; vmcb12->save.cpl = vmcb02->save.cpl; @@ -1413,7 +1413,7 @@ void nested_svm_vmexit(struct vcpu_svm *svm) svm_set_efer(vcpu, vmcb01->save.efer); svm_set_cr0(vcpu, vmcb01->save.cr0 | X86_CR0_PE); svm_set_cr4(vcpu, vmcb01->save.cr4); - kvm_rax_write(vcpu, vmcb01->save.rax); + kvm_rax_write_raw(vcpu, vmcb01->save.rax); kvm_rsp_write(vcpu, vmcb01->save.rsp); kvm_rip_write(vcpu, vmcb01->save.rip); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index faa1ca7588b9..ee8f6664f6f3 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2397,16 +2397,13 @@ static int clgi_interception(struct kvm_vcpu *vcpu) static int invlpga_interception(struct kvm_vcpu *vcpu) { + /* FIXME: Handle an address size prefix. */ gva_t gva = kvm_rax_read(vcpu); - u32 asid = kvm_rcx_read(vcpu); + u32 asid = kvm_ecx_read(vcpu); if (nested_svm_check_permissions(vcpu)) return 1; - /* FIXME: Handle an address size prefix. */ - if (!is_64_bit_mode(vcpu)) - gva = (u32)gva; - trace_kvm_invlpga(to_svm(vcpu)->vmcb->save.rip, asid, gva); /* Let's treat INVLPGA the same as INVLPG (can be optimized!) */ diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 30dcabc899a2..b2c851cc7d5c 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -6165,7 +6165,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) static int nested_vmx_eptp_switching(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { - u32 index = kvm_rcx_read(vcpu); + u32 index = kvm_ecx_read(vcpu); u64 new_eptp; if (WARN_ON_ONCE(!nested_cpu_has_ept(vmcs12))) @@ -6199,7 +6199,7 @@ static int handle_vmfunc(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); struct vmcs12 *vmcs12; - u32 function = kvm_rax_read(vcpu); + u32 function = kvm_eax_read(vcpu); /* * VMFUNC should never execute cleanly while L1 is active; KVM supports @@ -6321,7 +6321,7 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu, exit_reason.basic == EXIT_REASON_MSR_WRITE_IMM) msr_index = vmx_get_exit_qual(vcpu); else - msr_index = kvm_rcx_read(vcpu); + msr_index = kvm_ecx_read(vcpu); /* * The MSR_BITMAP page is divided into four 1024-byte bitmaps, @@ -6431,7 +6431,7 @@ static bool nested_vmx_exit_handled_encls(struct kvm_vcpu *vcpu, !nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING)) return false; - encls_leaf = kvm_rax_read(vcpu); + encls_leaf = kvm_eax_read(vcpu); if (encls_leaf > 62) encls_leaf = 63; return vmcs12->encls_exiting_bitmap & BIT_ULL(encls_leaf); diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index 66c315554b46..2f5a1c58f3c5 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -352,7 +352,7 @@ static int handle_encls_einit(struct kvm_vcpu *vcpu) rflags &= ~X86_EFLAGS_ZF; vmx_set_rflags(vcpu, rflags); - kvm_rax_write(vcpu, ret); + kvm_eax_write(vcpu, ret); return kvm_skip_emulated_instruction(vcpu); } @@ -380,7 +380,7 @@ static inline bool sgx_enabled_in_guest_bios(struct kvm_vcpu *vcpu) int handle_encls(struct kvm_vcpu *vcpu) { - u32 leaf = (u32)kvm_rax_read(vcpu); + u32 leaf = kvm_eax_read(vcpu); if (!enable_sgx || !guest_cpu_cap_has(vcpu, X86_FEATURE_SGX) || !guest_cpu_cap_has(vcpu, X86_FEATURE_SGX1)) { diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 738fd5ea9257..6089aa8f9053 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -1169,11 +1169,11 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu) static int tdx_emulate_vmcall(struct kvm_vcpu *vcpu) { - kvm_rax_write(vcpu, to_tdx(vcpu)->vp_enter_args.r10); - kvm_rbx_write(vcpu, to_tdx(vcpu)->vp_enter_args.r11); - kvm_rcx_write(vcpu, to_tdx(vcpu)->vp_enter_args.r12); - kvm_rdx_write(vcpu, to_tdx(vcpu)->vp_enter_args.r13); - kvm_rsi_write(vcpu, to_tdx(vcpu)->vp_enter_args.r14); + kvm_rax_write_raw(vcpu, to_tdx(vcpu)->vp_enter_args.r10); + kvm_rbx_write_raw(vcpu, to_tdx(vcpu)->vp_enter_args.r11); + kvm_rcx_write_raw(vcpu, to_tdx(vcpu)->vp_enter_args.r12); + kvm_rdx_write_raw(vcpu, to_tdx(vcpu)->vp_enter_args.r13); + kvm_rsi_write_raw(vcpu, to_tdx(vcpu)->vp_enter_args.r14); return __kvm_emulate_hypercall(vcpu, 0, complete_hypercall_exit); } @@ -2051,12 +2051,12 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) case EXIT_REASON_IO_INSTRUCTION: return tdx_emulate_io(vcpu); case EXIT_REASON_MSR_READ: - kvm_rcx_write(vcpu, tdx->vp_enter_args.r12); + kvm_ecx_write(vcpu, tdx->vp_enter_args.r12); return kvm_emulate_rdmsr(vcpu); case EXIT_REASON_MSR_WRITE: - kvm_rcx_write(vcpu, tdx->vp_enter_args.r12); - kvm_rax_write(vcpu, tdx->vp_enter_args.r13 & -1u); - kvm_rdx_write(vcpu, tdx->vp_enter_args.r13 >> 32); + kvm_ecx_write(vcpu, tdx->vp_enter_args.r12); + kvm_eax_write(vcpu, tdx->vp_enter_args.r13); + kvm_edx_write(vcpu, tdx->vp_enter_args.r13 >> 32); return kvm_emulate_wrmsr(vcpu); case EXIT_REASON_EPT_MISCONFIG: return tdx_emulate_mmio(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index da7287546846..58562163155b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1309,7 +1309,7 @@ int kvm_emulate_xsetbv(struct kvm_vcpu *vcpu) { /* Note, #UD due to CR4.OSXSAVE=0 has priority over the intercept. */ if (kvm_x86_call(get_cpl)(vcpu) != 0 || - __kvm_set_xcr(vcpu, kvm_rcx_read(vcpu), kvm_read_edx_eax(vcpu))) { + __kvm_set_xcr(vcpu, kvm_ecx_read(vcpu), kvm_read_edx_eax(vcpu))) { kvm_inject_gp(vcpu, 0); return 1; } @@ -1606,7 +1606,7 @@ static unsigned long kvm_get_effective_dr7(struct kvm_vcpu *vcpu) int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu) { - u32 pmc = kvm_rcx_read(vcpu); + u32 pmc = kvm_ecx_read(vcpu); u64 data; if (kvm_pmu_rdpmc(vcpu, pmc, &data)) { @@ -1614,8 +1614,8 @@ int kvm_emulate_rdpmc(struct kvm_vcpu *vcpu) return 1; } - kvm_rax_write(vcpu, (u32)data); - kvm_rdx_write(vcpu, data >> 32); + kvm_eax_write(vcpu, data); + kvm_edx_write(vcpu, data >> 32); return kvm_skip_emulated_instruction(vcpu); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_rdpmc); @@ -2062,8 +2062,8 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_msr_write); static void complete_userspace_rdmsr(struct kvm_vcpu *vcpu) { if (!vcpu->run->msr.error) { - kvm_rax_write(vcpu, (u32)vcpu->run->msr.data); - kvm_rdx_write(vcpu, vcpu->run->msr.data >> 32); + kvm_eax_write(vcpu, vcpu->run->msr.data); + kvm_edx_write(vcpu, vcpu->run->msr.data >> 32); } } @@ -2144,8 +2144,8 @@ static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg, trace_kvm_msr_read(msr, data); if (reg < 0) { - kvm_rax_write(vcpu, data & -1u); - kvm_rdx_write(vcpu, (data >> 32) & -1u); + kvm_eax_write(vcpu, data); + kvm_edx_write(vcpu, data >> 32); } else { kvm_register_write(vcpu, reg, data); } @@ -2162,7 +2162,7 @@ static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg, int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu) { - return __kvm_emulate_rdmsr(vcpu, kvm_rcx_read(vcpu), -1, + return __kvm_emulate_rdmsr(vcpu, kvm_ecx_read(vcpu), -1, complete_fast_rdmsr); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_rdmsr); @@ -2198,7 +2198,7 @@ static int __kvm_emulate_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 data) int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu) { - return __kvm_emulate_wrmsr(vcpu, kvm_rcx_read(vcpu), + return __kvm_emulate_wrmsr(vcpu, kvm_ecx_read(vcpu), kvm_read_edx_eax(vcpu)); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_emulate_wrmsr); @@ -2308,7 +2308,7 @@ static fastpath_t __handle_fastpath_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 da fastpath_t handle_fastpath_wrmsr(struct kvm_vcpu *vcpu) { - return __handle_fastpath_wrmsr(vcpu, kvm_rcx_read(vcpu), + return __handle_fastpath_wrmsr(vcpu, kvm_ecx_read(vcpu), kvm_read_edx_eax(vcpu)); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(handle_fastpath_wrmsr); @@ -9694,7 +9694,7 @@ static int complete_fast_pio_out(struct kvm_vcpu *vcpu) static int kvm_fast_pio_out(struct kvm_vcpu *vcpu, int size, unsigned short port) { - unsigned long val = kvm_rax_read(vcpu); + unsigned long val = kvm_rax_read_raw(vcpu); int ret = emulator_pio_out(vcpu, size, port, &val, 1); if (ret) @@ -9730,10 +9730,10 @@ static int complete_fast_pio_in(struct kvm_vcpu *vcpu) } /* For size less than 4 we merge, else we zero extend */ - val = (vcpu->arch.pio.size < 4) ? kvm_rax_read(vcpu) : 0; + val = (vcpu->arch.pio.size < 4) ? kvm_rax_read_raw(vcpu) : 0; complete_emulator_pio_in(vcpu, &val); - kvm_rax_write(vcpu, val); + kvm_rax_write_raw(vcpu, val); return kvm_skip_emulated_instruction(vcpu); } @@ -9745,11 +9745,11 @@ static int kvm_fast_pio_in(struct kvm_vcpu *vcpu, int size, int ret; /* For size less than 4 we merge, else we zero extend */ - val = (size < 4) ? kvm_rax_read(vcpu) : 0; + val = (size < 4) ? kvm_rax_read_raw(vcpu) : 0; ret = emulator_pio_in(vcpu, size, port, &val, 1); if (ret) { - kvm_rax_write(vcpu, val); + kvm_rax_write_raw(vcpu, val); return ret; } @@ -10416,29 +10416,30 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu) if (!is_64_bit_hypercall(vcpu)) ret = (u32)ret; - kvm_rax_write(vcpu, ret); + kvm_rax_write_raw(vcpu, ret); return kvm_skip_emulated_instruction(vcpu); } int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, int (*complete_hypercall)(struct kvm_vcpu *)) { - unsigned long ret; - unsigned long nr = kvm_rax_read(vcpu); - unsigned long a0 = kvm_rbx_read(vcpu); - unsigned long a1 = kvm_rcx_read(vcpu); - unsigned long a2 = kvm_rdx_read(vcpu); - unsigned long a3 = kvm_rsi_read(vcpu); int op_64_bit = is_64_bit_hypercall(vcpu); + unsigned long ret, nr, a0, a1, a2, a3; ++vcpu->stat.hypercalls; - if (!op_64_bit) { - nr &= 0xFFFFFFFF; - a0 &= 0xFFFFFFFF; - a1 &= 0xFFFFFFFF; - a2 &= 0xFFFFFFFF; - a3 &= 0xFFFFFFFF; + if (op_64_bit) { + nr = kvm_rax_read_raw(vcpu); + a0 = kvm_rbx_read_raw(vcpu); + a1 = kvm_rcx_read_raw(vcpu); + a2 = kvm_rdx_read_raw(vcpu); + a3 = kvm_rsi_read_raw(vcpu); + } else { + nr = kvm_eax_read(vcpu); + a0 = kvm_ebx_read(vcpu); + a1 = kvm_ecx_read(vcpu); + a2 = kvm_edx_read(vcpu); + a3 = kvm_esi_read(vcpu); } trace_kvm_hypercall(nr, a0, a1, a2, a3); @@ -12136,23 +12137,23 @@ static void __get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) emulator_writeback_register_cache(vcpu->arch.emulate_ctxt); vcpu->arch.emulate_regs_need_sync_to_vcpu = false; } - regs->rax = kvm_rax_read(vcpu); - regs->rbx = kvm_rbx_read(vcpu); - regs->rcx = kvm_rcx_read(vcpu); - regs->rdx = kvm_rdx_read(vcpu); - regs->rsi = kvm_rsi_read(vcpu); - regs->rdi = kvm_rdi_read(vcpu); + regs->rax = kvm_rax_read_raw(vcpu); + regs->rbx = kvm_rbx_read_raw(vcpu); + regs->rcx = kvm_rcx_read_raw(vcpu); + regs->rdx = kvm_rdx_read_raw(vcpu); + regs->rsi = kvm_rsi_read_raw(vcpu); + regs->rdi = kvm_rdi_read_raw(vcpu); regs->rsp = kvm_rsp_read(vcpu); - regs->rbp = kvm_rbp_read(vcpu); + regs->rbp = kvm_rbp_read_raw(vcpu); #ifdef CONFIG_X86_64 - regs->r8 = kvm_r8_read(vcpu); - regs->r9 = kvm_r9_read(vcpu); - regs->r10 = kvm_r10_read(vcpu); - regs->r11 = kvm_r11_read(vcpu); - regs->r12 = kvm_r12_read(vcpu); - regs->r13 = kvm_r13_read(vcpu); - regs->r14 = kvm_r14_read(vcpu); - regs->r15 = kvm_r15_read(vcpu); + regs->r8 = kvm_r8_read_raw(vcpu); + regs->r9 = kvm_r9_read_raw(vcpu); + regs->r10 = kvm_r10_read_raw(vcpu); + regs->r11 = kvm_r11_read_raw(vcpu); + regs->r12 = kvm_r12_read_raw(vcpu); + regs->r13 = kvm_r13_read_raw(vcpu); + regs->r14 = kvm_r14_read_raw(vcpu); + regs->r15 = kvm_r15_read_raw(vcpu); #endif regs->rip = kvm_rip_read(vcpu); @@ -12176,23 +12177,23 @@ static void __set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) vcpu->arch.emulate_regs_need_sync_from_vcpu = true; vcpu->arch.emulate_regs_need_sync_to_vcpu = false; - kvm_rax_write(vcpu, regs->rax); - kvm_rbx_write(vcpu, regs->rbx); - kvm_rcx_write(vcpu, regs->rcx); - kvm_rdx_write(vcpu, regs->rdx); - kvm_rsi_write(vcpu, regs->rsi); - kvm_rdi_write(vcpu, regs->rdi); + kvm_rax_write_raw(vcpu, regs->rax); + kvm_rbx_write_raw(vcpu, regs->rbx); + kvm_rcx_write_raw(vcpu, regs->rcx); + kvm_rdx_write_raw(vcpu, regs->rdx); + kvm_rsi_write_raw(vcpu, regs->rsi); + kvm_rdi_write_raw(vcpu, regs->rdi); kvm_rsp_write(vcpu, regs->rsp); - kvm_rbp_write(vcpu, regs->rbp); + kvm_rbp_write_raw(vcpu, regs->rbp); #ifdef CONFIG_X86_64 - kvm_r8_write(vcpu, regs->r8); - kvm_r9_write(vcpu, regs->r9); - kvm_r10_write(vcpu, regs->r10); - kvm_r11_write(vcpu, regs->r11); - kvm_r12_write(vcpu, regs->r12); - kvm_r13_write(vcpu, regs->r13); - kvm_r14_write(vcpu, regs->r14); - kvm_r15_write(vcpu, regs->r15); + kvm_r8_write_raw(vcpu, regs->r8); + kvm_r9_write_raw(vcpu, regs->r9); + kvm_r10_write_raw(vcpu, regs->r10); + kvm_r11_write_raw(vcpu, regs->r11); + kvm_r12_write_raw(vcpu, regs->r12); + kvm_r13_write_raw(vcpu, regs->r13); + kvm_r14_write_raw(vcpu, regs->r14); + kvm_r15_write_raw(vcpu, regs->r15); #endif kvm_rip_write(vcpu, regs->rip); @@ -13095,7 +13096,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) * on RESET. But, go through the motions in case that's ever remedied. */ cpuid_0x1 = kvm_find_cpuid_entry(vcpu, 1); - kvm_rdx_write(vcpu, cpuid_0x1 ? cpuid_0x1->eax : 0x600); + kvm_edx_write(vcpu, cpuid_0x1 ? cpuid_0x1->eax : 0x600); kvm_x86_call(vcpu_reset)(vcpu, init_event); diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 895095dc684e..694b31c1fcc9 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1408,7 +1408,7 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc) static int kvm_xen_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result) { - kvm_rax_write(vcpu, result); + kvm_rax_write_raw(vcpu, result); return kvm_skip_emulated_instruction(vcpu); } @@ -1679,29 +1679,29 @@ int kvm_xen_hypercall(struct kvm_vcpu *vcpu) u8 cpl; /* Hyper-V hypercalls get bit 31 set in EAX */ - if ((kvm_rax_read(vcpu) & 0x80000000) && + if ((kvm_rax_read_raw(vcpu) & 0x80000000) && kvm_hv_hypercall_enabled(vcpu)) return kvm_hv_hypercall(vcpu); longmode = is_64_bit_hypercall(vcpu); if (!longmode) { - input = (u32)kvm_rax_read(vcpu); - params[0] = (u32)kvm_rbx_read(vcpu); - params[1] = (u32)kvm_rcx_read(vcpu); - params[2] = (u32)kvm_rdx_read(vcpu); - params[3] = (u32)kvm_rsi_read(vcpu); - params[4] = (u32)kvm_rdi_read(vcpu); - params[5] = (u32)kvm_rbp_read(vcpu); + input = kvm_eax_read(vcpu); + params[0] = kvm_ebx_read(vcpu); + params[1] = kvm_ecx_read(vcpu); + params[2] = kvm_edx_read(vcpu); + params[3] = kvm_esi_read(vcpu); + params[4] = kvm_edi_read(vcpu); + params[5] = kvm_ebp_read(vcpu); } else { #ifdef CONFIG_X86_64 - input = (u64)kvm_rax_read(vcpu); - params[0] = (u64)kvm_rdi_read(vcpu); - params[1] = (u64)kvm_rsi_read(vcpu); - params[2] = (u64)kvm_rdx_read(vcpu); - params[3] = (u64)kvm_r10_read(vcpu); - params[4] = (u64)kvm_r8_read(vcpu); - params[5] = (u64)kvm_r9_read(vcpu); + input = (u64)kvm_rax_read_raw(vcpu); + params[0] = (u64)kvm_rdi_read_raw(vcpu); + params[1] = (u64)kvm_rsi_read_raw(vcpu); + params[2] = (u64)kvm_rdx_read_raw(vcpu); + params[3] = (u64)kvm_r10_read_raw(vcpu); + params[4] = (u64)kvm_r8_read_raw(vcpu); + params[5] = (u64)kvm_r9_read_raw(vcpu); #else KVM_BUG_ON(1, vcpu->kvm); return -EIO; From 308851c2d6f86baee7d6ea718b38bd671ff02081 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:52 -0700 Subject: [PATCH 2656/5214] KVM: x86: Drop non-raw kvm__write() helpers Drop the non-raw, mode-aware kvm__write() helpers as there is no usage in KVM, and in all likelihood there will never be usage in KVM as use of hardcoded registers in instructions is uncommon, and *modifying* hardcoded registers is practically unheard of. While there are a few instructions that modify registers in mode-aware ways, e.g. REP string and some ENCLS varieties, the odds of KVM needing to emulate such instructions (outside of the fully emulator) are vanishingly small. Drop kvm__write() to prevent incorrect usage; _if_ a new instruction comes along that needs to modify a hardcoded register, this can be reverted. No functional change intended. Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-10-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/regs.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h index 12db5039aace..f22b3a8cd483 100644 --- a/arch/x86/kvm/regs.h +++ b/arch/x86/kvm/regs.h @@ -61,11 +61,6 @@ static __always_inline unsigned long kvm_##lname##_read(struct kvm_vcpu *vcpu) { \ return vcpu->arch.regs[VCPU_REGS_##uname] & kvm_reg_mode_mask(vcpu); \ } \ -static __always_inline void kvm_##lname##_write(struct kvm_vcpu *vcpu, \ - unsigned long val) \ -{ \ - vcpu->arch.regs[VCPU_REGS_##uname] = val & kvm_reg_mode_mask(vcpu); \ -} \ static __always_inline unsigned long kvm_##lname##_read_raw(struct kvm_vcpu *vcpu) \ { \ return vcpu->arch.regs[VCPU_REGS_##uname]; \ From a7e6e09b27ca797abc593778f0fbc23f70f5b4c7 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:53 -0700 Subject: [PATCH 2657/5214] KVM: nSVM: Use kvm_rax_read() now that it's mode-aware Now that kvm_rax_read() truncates the output value to 32 bits if the vCPU isn't in 64-bit mode, use it instead of the more verbose (and very technically slower) kvm_register_read(). Note! VMLOAD, VMSAVE, and VMRUN emulation are still technically buggy, as they can use EAX (versus RAX) in 64-bit mode via an operand size prefix. Don't bother trying to handle that case, as it would require decoding the code stream, which would open an entirely different can of worms, and in practice no sane guest would shove garbage into RAX[63:32] and then execute VMLOAD/VMSAVE/VMRUN with just EAX. No functional change intended. Cc: Yosry Ahmed Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-11-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/nested.c | 2 +- arch/x86/kvm/svm/svm.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index be7671880261..de9de9cc9d69 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -1135,7 +1135,7 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) if (WARN_ON_ONCE(!svm->nested.initialized)) return -EINVAL; - vmcb12_gpa = kvm_register_read(vcpu, VCPU_REGS_RAX); + vmcb12_gpa = kvm_rax_read(vcpu); if (!page_address_valid(vcpu, vmcb12_gpa)) { kvm_inject_gp(vcpu, 0); return 1; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ee8f6664f6f3..f8ab5bae368c 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2206,7 +2206,7 @@ static int intr_interception(struct kvm_vcpu *vcpu) static int vmload_vmsave_interception(struct kvm_vcpu *vcpu, bool vmload) { - u64 vmcb12_gpa = kvm_register_read(vcpu, VCPU_REGS_RAX); + u64 vmcb12_gpa = kvm_rax_read(vcpu); struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb12; struct kvm_host_map map; @@ -2314,7 +2314,7 @@ static int gp_interception(struct kvm_vcpu *vcpu) if (nested_svm_check_permissions(vcpu)) return 1; - if (!page_address_valid(vcpu, kvm_register_read(vcpu, VCPU_REGS_RAX))) + if (!page_address_valid(vcpu, kvm_rax_read(vcpu))) goto reinject; /* From b6a0fdf2bbf0ee6c9320a5c1c5d1e2f39b693fbf Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:54 -0700 Subject: [PATCH 2658/5214] Revert "KVM: VMX: Read 32-bit GPR values for ENCLS instructions outside of 64-bit mode" Now that kvm__read() are mode aware, i.e. are functionally equivalent to kvm_register_read(), revert aback to the less verbose versions. No functional change intended. This reverts commit 60919eccf6764c71cef31a1afeaa1a36b8e5ab85. Acked-by: Kai Huang Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-12-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/sgx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index 2f5a1c58f3c5..876dc2814108 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -225,8 +225,8 @@ static int handle_encls_ecreate(struct kvm_vcpu *vcpu) struct x86_exception ex; int r; - if (sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RBX), 32, 32, &pageinfo_gva) || - sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RCX), 4096, 4096, &secs_gva)) + if (sgx_get_encls_gva(vcpu, kvm_rbx_read(vcpu), 32, 32, &pageinfo_gva) || + sgx_get_encls_gva(vcpu, kvm_rcx_read(vcpu), 4096, 4096, &secs_gva)) return 1; /* @@ -302,9 +302,9 @@ static int handle_encls_einit(struct kvm_vcpu *vcpu) gpa_t sig_gpa, secs_gpa, token_gpa; int ret, trapnr; - if (sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RBX), 1808, 4096, &sig_gva) || - sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RCX), 4096, 4096, &secs_gva) || - sgx_get_encls_gva(vcpu, kvm_register_read(vcpu, VCPU_REGS_RDX), 304, 512, &token_gva)) + if (sgx_get_encls_gva(vcpu, kvm_rbx_read(vcpu), 1808, 4096, &sig_gva) || + sgx_get_encls_gva(vcpu, kvm_rcx_read(vcpu), 4096, 4096, &secs_gva) || + sgx_get_encls_gva(vcpu, kvm_rdx_read(vcpu), 304, 512, &token_gva)) return 1; /* From 879fffc09474241428e142142206f903585e0834 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:55 -0700 Subject: [PATCH 2659/5214] KVM: x86: Harden is_64_bit_hypercall() against bugs on 32-bit kernels Unconditionally return %false for is_64_bit_hypercall() on 32-bit kernels to guard against incorrectly setting guest_state_protected, and because in a (very) hypothetical world where 32-bit KVM supports protected guests, assuming a hypercall was made in 64-bit mode is flat out wrong. Reviewed-by: Kai Huang Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260529222223.870923-13-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/regs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kvm/regs.h b/arch/x86/kvm/regs.h index f22b3a8cd483..a57ba26279ed 100644 --- a/arch/x86/kvm/regs.h +++ b/arch/x86/kvm/regs.h @@ -39,12 +39,16 @@ static inline bool is_64_bit_mode(struct kvm_vcpu *vcpu) static inline bool is_64_bit_hypercall(struct kvm_vcpu *vcpu) { +#ifdef CONFIG_X86_64 /* * If running with protected guest state, the CS register is not * accessible. The hypercall register values will have had to been * provided in 64-bit mode, so assume the guest is in 64-bit. */ return vcpu->arch.guest_state_protected || is_64_bit_mode(vcpu); +#else + return false; +#endif } static __always_inline unsigned long kvm_reg_mode_mask(struct kvm_vcpu *vcpu) From c7722e5e1daeeabbd9f969554d52bb7158120b27 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:56 -0700 Subject: [PATCH 2660/5214] KVM: x86: Move update_cr8_intercept() to lapic.c Move update_cr8_intercept() to lapic.c so that it's globally visible in anticipation of extracting most of the register-specific code out of x86.c and into a new compilation unit. Opportunistically prefix the helper kvm_lapic_ to make its role/scope more obvious. No functional change intended. Reviewed-by: Kai Huang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-14-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/lapic.c | 26 ++++++++++++++++++++++++++ arch/x86/kvm/lapic.h | 1 + arch/x86/kvm/x86.c | 34 +++------------------------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index d8dbfb107bfb..27cca31308bd 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2744,6 +2744,32 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) return (tpr & 0xf0) >> 4; } +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu) +{ + int max_irr, tpr; + + if (!kvm_x86_ops.update_cr8_intercept) + return; + + if (!lapic_in_kernel(vcpu)) + return; + + if (vcpu->arch.apic->apicv_active) + return; + + if (!vcpu->arch.apic->vapic_addr) + max_irr = kvm_lapic_find_highest_irr(vcpu); + else + max_irr = -1; + + if (max_irr != -1) + max_irr >>= 4; + + tpr = kvm_lapic_get_cr8(vcpu); + + kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); +} + static void __kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value) { u64 old_value = vcpu->arch.apic_base; diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 274885af4ebc..533581d06151 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -100,6 +100,7 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu); void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event); u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu); void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu); void kvm_apic_set_version(struct kvm_vcpu *vcpu); void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 58562163155b..326ae99db6de 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -128,7 +128,6 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); KVM_X2APIC_ENABLE_SUPPRESS_EOI_BROADCAST | \ KVM_X2APIC_DISABLE_SUPPRESS_EOI_BROADCAST) -static void update_cr8_intercept(struct kvm_vcpu *vcpu); static void process_nmi(struct kvm_vcpu *vcpu); static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); static void store_regs(struct kvm_vcpu *vcpu); @@ -5341,7 +5340,7 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, r = kvm_apic_set_state(vcpu, s); if (r) return r; - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); return 0; } @@ -10586,33 +10585,6 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu) kvm_run->flags |= KVM_RUN_X86_GUEST_MODE; } -static void update_cr8_intercept(struct kvm_vcpu *vcpu) -{ - int max_irr, tpr; - - if (!kvm_x86_ops.update_cr8_intercept) - return; - - if (!lapic_in_kernel(vcpu)) - return; - - if (vcpu->arch.apic->apicv_active) - return; - - if (!vcpu->arch.apic->vapic_addr) - max_irr = kvm_lapic_find_highest_irr(vcpu); - else - max_irr = -1; - - if (max_irr != -1) - max_irr >>= 4; - - tpr = kvm_lapic_get_cr8(vcpu); - - kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); -} - - int kvm_check_nested_events(struct kvm_vcpu *vcpu) { if (kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu)) { @@ -11353,7 +11325,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_x86_call(enable_irq_window)(vcpu); if (kvm_lapic_enabled(vcpu)) { - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); kvm_lapic_sync_to_vapic(vcpu); } } @@ -12499,7 +12471,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR); kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR); - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); /* Older userspace won't unhalt the vcpu on reset. */ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 && From bc87aec39399f5dbcb83eb3ebbce0b0beb57e893 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:21:57 -0700 Subject: [PATCH 2661/5214] KVM: x86: Move async #PF helpers to x86.h (as inlines) Move kvm_pv_async_pf_enabled() and kvm_async_pf_hash_reset() to x86.h in anticipation of extracting the majority of register and MSR specific code out of x86.c. No functional change intended. Reviewed-by: Kai Huang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-15-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/x86.c | 19 ------------------- arch/x86/kvm/x86.h | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 326ae99db6de..4a8d2b701f05 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -575,13 +575,6 @@ static struct kmem_cache *kvm_alloc_emulator_cache(void) static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt); -static inline void kvm_async_pf_hash_reset(struct kvm_vcpu *vcpu) -{ - int i; - for (i = 0; i < ASYNC_PF_PER_VCPU; i++) - vcpu->arch.apf.gfns[i] = ~0; -} - static void kvm_destroy_user_return_msrs(void) { int cpu; @@ -1032,18 +1025,6 @@ bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_require_dr); -static bool __kvm_pv_async_pf_enabled(u64 data) -{ - u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT; - - return (data & mask) == mask; -} - -static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) -{ - return __kvm_pv_async_pf_enabled(vcpu->arch.apf.msr_en_val); -} - static inline u64 pdptr_rsvd_bits(struct kvm_vcpu *vcpu) { return vcpu->arch.reserved_gpa_bits | rsvd_bits(5, 8) | rsvd_bits(1, 2); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index e06f0ee63515..dabbecc21378 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -559,6 +559,25 @@ static inline bool kvm_pat_valid(u64 data) return (data | ((data & 0x0202020202020202ull) << 1)) == data; } +static inline bool __kvm_pv_async_pf_enabled(u64 data) +{ + u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT; + + return (data & mask) == mask; +} + +static inline bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu) +{ + return __kvm_pv_async_pf_enabled(vcpu->arch.apf.msr_en_val); +} + +static inline void kvm_async_pf_hash_reset(struct kvm_vcpu *vcpu) +{ + int i; + for (i = 0; i < ASYNC_PF_PER_VCPU; i++) + vcpu->arch.apf.gfns[i] = ~0; +} + /* * Trigger machine check on the host. We assume all the MSRs are already set up * by the CPU and that we still run on the same CPU as the MCE occurred on. From e688ca78589df501ea4465f6e9c063340e684f8b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:22:00 -0700 Subject: [PATCH 2662/5214] KVM: x86: Drop defunct vcpu_tsc_khz() declaration Remove a dead vcpu_tsc_khz() declaration. No functional change intended. Reviewed-by: Kai Huang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-18-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index f5a35136ebb8..87331068dac5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2156,8 +2156,6 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3); extern bool tdp_enabled; -u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - /* * EMULTYPE_NO_DECODE - Set when re-emulating an instruction (after completing * userspace I/O) to indicate that the emulation context From 5fd25aacdf893212f027d3f54ed5dee701dc69e4 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 15:22:12 -0700 Subject: [PATCH 2663/5214] KVM: x86: Remove defunct kvm_load_segment_descriptor() declaration. Remove a dead kvm_load_segment_descriptor() declaration, no functional change intended. Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-30-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm_host.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 87331068dac5..602aa5f03413 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2275,7 +2275,6 @@ int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu); void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); void kvm_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); -int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg); void kvm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector); int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index, From eba85fee7fc6cf28fec38a5bf3c378bef9a79ca6 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 2 Jun 2026 10:09:19 -0700 Subject: [PATCH 2664/5214] KVM: guest_memfd: Treat memslot binding offset+size as unsigned values When binding a memslot to a guest_memfd file, treat the offset and size as unsigned values to fix a bug where the sum of the two can result in a false negative when checking for overflow against the size of the file. Passing unsigned values also avoids relying on somewhat obscure checks in other flows for safety, and tracks the offset and size as they are intended to be tracked, as unsigned values. On 64-bit kernels, the number of pages a memslot contains and thus the size (and offset) of its guest_memfd binding are unsigned 64-bit values. Taking the offset+size as an loff_t instead of a uoff_t inadvertently converts the unsigned value to a signed value if the offset and/or size is massive. Locally storing the offset and size as signed values is benign in and of itself (though even that is *extremely* difficult to discern), but operating on their sum is not. For the offset, KVM explicitly checks against a negative value, which might seem like a bug as KVM could incorrectly reject a legitimate binding, but that's not actually the case as KVM_CREATE_GUEST_MEMFD takes a signed value for its size, i.e. a would-be-negative offset is also greater than the maximum possible size of any guest_memfd file. Regarding the size, while KVM lacks an explicit check for a negative value, i.e. seemingly has a flawed overflow check, KVM restricts the number of pages in a single memslot to the largest positive signed 32-bit value: if (id < KVM_USER_MEM_SLOTS && (mem->memory_size >> PAGE_SHIFT) > KVM_MEM_MAX_NR_PAGES) return -EINVAL; and so that maximum "size" will ever be is 0x7fffffff000. The sum of the two is, however, problematic. While the size is restricted by KVM's memslot logic, the offset is not, i.e. the offset is completely unchecked until the "offset + size > i_size_read(inode)" check. If the offset is the (nearly) largest possible _positive_ value, then adding size to the offset can result in a signed, negative 64-bit value. When compared against the size of the file (guaranteed to be positive), the negative sum is always smaller, and KVM incorrectly allows the absurd offset. Opportunistically add missing includes in kvm_mm.h (instead of relying on its parents). Fixes: a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory") Cc: stable@vger.kernel.org Cc: Ackerley Tng Reviewed-by: Michael Roth Reviewed-by: Ackerley Tng Link: https://patch.msgid.link/20260602170921.1304394-2-seanjc@google.com Signed-off-by: Sean Christopherson --- virt/kvm/guest_memfd.c | 8 ++++---- virt/kvm/kvm_mm.h | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 46727539d08a..f54502640b08 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -640,15 +640,16 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) } int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { - loff_t size = slot->npages << PAGE_SHIFT; + uoff_t size = slot->npages << PAGE_SHIFT; unsigned long start, end; struct gmem_file *f; struct inode *inode; struct file *file; int r = -EINVAL; + BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset)); BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff)); file = fget(fd); @@ -664,8 +665,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, inode = file_inode(file); - if (offset < 0 || !PAGE_ALIGNED(offset) || - offset + size > i_size_read(inode)) + if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode)) goto err; filemap_invalidate_lock(inode->i_mapping); diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h index 9fcc5d5b7f8d..7510ca915dd1 100644 --- a/virt/kvm/kvm_mm.h +++ b/virt/kvm/kvm_mm.h @@ -3,6 +3,9 @@ #ifndef __KVM_MM_H__ #define __KVM_MM_H__ 1 +#include +#include + /* * Architectures can choose whether to use an rwlock or spinlock * for the mmu_lock. These macros, for use in common code @@ -72,7 +75,7 @@ int kvm_gmem_init(struct module *module); void kvm_gmem_exit(void); int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args); int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset); + unsigned int fd, uoff_t offset); void kvm_gmem_unbind(struct kvm_memory_slot *slot); #else static inline int kvm_gmem_init(struct module *module) @@ -82,7 +85,7 @@ static inline int kvm_gmem_init(struct module *module) static inline void kvm_gmem_exit(void) {}; static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { WARN_ON_ONCE(1); return -EIO; From b7a23fb0ed7e37774997f9b2029dd9dce5653d74 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 2 Jun 2026 10:09:20 -0700 Subject: [PATCH 2665/5214] KVM: selftests: Expand the guest_memfd test macros to allow passing the VM Expand the gmem test macros to allow passing the VM to testcases, without needing to plumb the VM into _every_ testcase, as the vast majority of testcases only need the fd and size. No functional change intended. Reviewed-by: Ackerley Tng Tested-by: Ackerley Tng Link: https://patch.msgid.link/20260602170921.1304394-3-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/guest_memfd_test.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index d6528c6f5e03..4b4462c8bacd 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -408,17 +408,26 @@ static void test_guest_memfd_flags(struct kvm_vm *vm) } } -#define __gmem_test(__test, __vm, __flags, __gmem_size) \ +#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \ do { \ int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \ \ - test_##__test(fd, __gmem_size); \ + test_##__test(args); \ close(fd); \ } while (0) +#define __gmem_test(__test, __vm, __flags, __gmem_size) \ + ____gmem_test(__test, __vm, __flags, __gmem_size, fd, __gmem_size) + #define gmem_test(__test, __vm, __flags) \ __gmem_test(__test, __vm, __flags, page_size * 4) +#define __gmem_test_vm(__test, __vm, __flags, __gmem_size) \ + ____gmem_test(__test, __vm, __flags, __gmem_size, __vm, fd, __gmem_size) + +#define gmem_test_vm(__test, __vm, __flags) \ + __gmem_test_vm(__test, __vm, __flags, page_size * 4) + static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) { test_create_guest_memfd_multiple(vm); From b408b52e7111c560dcd7d69612c2e07c59c36ae3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 2 Jun 2026 10:09:21 -0700 Subject: [PATCH 2666/5214] KVM: selftests: Add guest_memfd regression test signed offset+size bug Add a regression (and proof-of-bug) testcase to ensure KVM rejects an offset+size that would result in a negative value when computed as a signed 64-bit value. KVM had a flaw where it would allow binding a memslot to a guest_memfd instance even with a wildly out-of-range offset, if the offset and size were both positive values, but the combined offset+size was negative. Use "0x7fffffffffffffffull - page_size", i.e. "INT64_MAX - page_size", for the offset as the size of the guest_memfd file must be at least page_size (KVM requires memslots and gmem files to be host page-size aligned). I.e. "INT64_MAX - page_size + size" is guaranteed to generate an offset+size that is negative when converted to a signed 64-bit value *and* honors KVM's alignment requirements. Reviewed-by: Ackerley Tng Tested-by: Ackerley Tng Link: https://patch.msgid.link/20260602170921.1304394-4-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/guest_memfd_test.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index 4b4462c8bacd..775f99443d49 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -345,6 +345,16 @@ static void test_invalid_punch_hole(int fd, size_t total_size) } } +static void test_invalid_binding(struct kvm_vm *vm, int fd, size_t size) +{ + int r; + + r = __vm_set_user_memory_region2(vm, 0, KVM_MEM_GUEST_MEMFD, 0, size, 0, + fd, ALIGN_DOWN(INT64_MAX, page_size)); + TEST_ASSERT(r && errno == EINVAL, + "Memslot with out-of-range offset+size should fail"); +} + static void test_create_guest_memfd_invalid_sizes(struct kvm_vm *vm, u64 guest_memfd_flags) { @@ -456,6 +466,7 @@ static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) gmem_test(file_size, vm, flags); gmem_test(fallocate, vm, flags); gmem_test(invalid_punch_hole, vm, flags); + gmem_test_vm(invalid_binding, vm, flags); } static void test_guest_memfd(unsigned long vm_type) From adeb462cecae964ae4525512e31d54e545539476 Mon Sep 17 00:00:00 2001 From: Yosry Ahmed Date: Thu, 28 May 2026 16:10:52 -0700 Subject: [PATCH 2667/5214] KVM: selftests: Add a test for gPAT handling in L2 When KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT is disabled, verify that KVM correctly virtualizes the host PAT MSR and the guest PAT register for nested SVM guests. With nested NPT disabled: * L1 and L2 share the same PAT * The vmcb12.g_pat is ignored With nested NPT enabled: * An invalid g_pat in vmcb12 causes VMEXIT_INVALID * RDMSR(IA32_PAT) from L2 returns the value of the guest PAT register * WRMSR(IA32_PAT) from L2 is reflected in vmcb12's g_pat on VMEXIT * RDMSR(IA32_PAT) from L1 returns the value of the host PAT MSR Verify that save/restore with the vCPU in guest mode behaves as expected in both cases, e.g. preserves both hPAT and gPAT when NPT is enabled. Originally-by: Jim Mattson Signed-off-by: Yosry Ahmed [sean: use even fancier macro shenanigans] Link: https://patch.msgid.link/20260528231052.404737-1-seanjc@google.com [sean: avoid use of goto, print skips] Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/svm_nested_pat_test.c | 196 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/svm_nested_pat_test.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 9118a5a51b89..d658dc7cd2b1 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -117,6 +117,7 @@ TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa +TEST_GEN_PROGS_x86 += x86/svm_nested_pat_test TEST_GEN_PROGS_x86 += x86/svm_lbr_nested_state TEST_GEN_PROGS_x86 += x86/tsc_scaling_sync TEST_GEN_PROGS_x86 += x86/sync_regs_test diff --git a/tools/testing/selftests/kvm/x86/svm_nested_pat_test.c b/tools/testing/selftests/kvm/x86/svm_nested_pat_test.c new file mode 100644 index 000000000000..92da8ff34da1 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/svm_nested_pat_test.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, Google LLC. + * + * Test that KVM correctly virtualizes the PAT MSR and VMCB g_pat field + * for nested SVM guests: + * + * o With nested NPT disabled: + * - L1 and L2 share the same PAT + * - The vmcb12.g_pat is ignored + * o With nested NPT enabled: + * - Invalid g_pat in vmcb12 should cause VMEXIT_INVALID + * - L2 should see vmcb12.g_pat via RDMSR, not L1's PAT + * - L2's writes to PAT should be saved to vmcb12 on exit + * - L1's PAT should be restored after #VMEXIT from L2 + * - State save/restore should preserve both L1's and L2's PAT values + */ +#include +#include +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" + +#define L2_GUEST_STACK_SIZE 256 + +#define PAT_DEFAULT 0x0007040600070406ULL +#define L1_PAT_VALUE 0x0007040600070404ULL /* Change PA0 to WT */ +#define L2_VMCB12_PAT 0x0606060606060606ULL /* All WB */ +#define L2_PAT_MODIFIED 0x0606060606060604ULL /* Change PA0 to WT */ +#define INVALID_PAT_VALUE 0x0808080808080808ULL /* 8 is reserved */ + +bool npt_enabled; +int nr_iterations; + +static void l2_guest_code(void) +{ + u64 expected_pat = npt_enabled ? L2_VMCB12_PAT : L1_PAT_VALUE; + int i; + + for (i = 0; i < nr_iterations; i++) { + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), expected_pat); + GUEST_SYNC(1); + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), expected_pat); + + wrmsr(MSR_IA32_CR_PAT, L2_PAT_MODIFIED); + expected_pat = L2_PAT_MODIFIED; + + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); + GUEST_SYNC(2); + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); + + vmmcall(); + } +} + +static void l1_guest_code(struct svm_test_data *svm) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + int i; + + wrmsr(MSR_IA32_CR_PAT, L1_PAT_VALUE); + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L1_PAT_VALUE); + + generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + vmcb->save.g_pat = L2_VMCB12_PAT; + vmcb->control.intercept &= ~(1ULL << INTERCEPT_MSR_PROT); + + for (i = 0; i < nr_iterations; i++) { + run_guest(vmcb, svm->vmcb_gpa); + + GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); + + /* + * If NPT is enabled by L1, L2 has a unique PAT and L1's PAT is + * unchanged. Otherwise, PAT is shared between L1 and L2. + */ + if (npt_enabled) { + GUEST_ASSERT_EQ(vmcb->save.g_pat, L2_PAT_MODIFIED); + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L1_PAT_VALUE); + } else { + GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); + } + vmcb->save.rip += 3; /* skip over VMMCALL */ + } + + GUEST_DONE(); +} + +static void l1_guest_code_invalid_gpat(struct svm_test_data *svm) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + + /* VMRUN should fail without running L2 */ + generic_svm_setup(svm, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + vmcb->save.g_pat = INVALID_PAT_VALUE; + run_guest(vmcb, svm->vmcb_gpa); + + GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_ERR); + GUEST_DONE(); +} + +static void run_test(void *guest_code, bool do_save_restore, int nr_iters) +{ + struct kvm_x86_state *state; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + gva_t svm_gva; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); + + if (npt_enabled) + vm_enable_npt(vm); + + vcpu_alloc_svm(vm, &svm_gva); + + if (npt_enabled) + tdp_identity_map_default_memslots(vm); + + vcpu_args_set(vcpu, 1, svm_gva); + + nr_iterations = nr_iters; + sync_global_to_guest(vm, npt_enabled); + sync_global_to_guest(vm, nr_iterations); + + for (;;) { + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + /* NOT REACHED */ + case UCALL_SYNC: + if (do_save_restore) { + state = vcpu_save_state(vcpu); + kvm_vm_release(vm); + vcpu = vm_recreate_with_one_vcpu(vm); + vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); + vcpu_load_state(vcpu, state); + kvm_x86_state_cleanup(state); + } + break; + case UCALL_DONE: + kvm_vm_free(vm); + return; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } +} + +#define gpat_test(test_name, guest_code, npt_setting) \ +do { \ + npt_setting; \ + \ + if (npt_enabled && !kvm_cpu_has(X86_FEATURE_NPT)) { \ + pr_info("Skipping: " test_name " (no NPT support)\n"); \ + break; \ + } \ + \ + pr_info("Testing: " test_name "\n"); \ + run_test(guest_code, false, 1); \ + \ + if (guest_code == l1_guest_code) { \ + pr_info("Testing: " test_name " Save/Restore\n"); \ + run_test(guest_code, true, 1); \ + \ + pr_info("Testing: " test_name " Multiple VMRUNs\n"); \ + run_test(guest_code, false, 10); \ + } \ +} while (0) + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); + TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & + KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); + + gpat_test("Invalid gPAT", l1_guest_code_invalid_gpat, npt_enabled = true); + gpat_test("Nested NPT enabled", l1_guest_code, npt_enabled = true); + gpat_test("Nested NPT disabled", l1_guest_code, npt_enabled = false); + return 0; +} From 9090ba2e7cf8bf8a54879182db5665452d515bb0 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 17 May 2026 23:34:23 +0800 Subject: [PATCH 2668/5214] KVM: riscv: Rely on common MMU notifier locking The common KVM invalidation paths call kvm_unmap_gfn_range() with mmu_lock already held for write. For the standard MMU notifier path, the call chain is: kvm_mmu_notifier_invalidate_range_start() kvm_handle_hva_range() kvm_unmap_gfn_range() kvm_mmu_notifier_invalidate_range_start() leaves range.lockless clear. kvm_handle_hva_range() therefore takes KVM_MMU_LOCK(kvm) before invoking the handler. The guest_memfd path has the same locking contract: __kvm_gmem_invalidate_begin() kvm_mmu_unmap_gfn_range() kvm_unmap_gfn_range() __kvm_gmem_invalidate_begin() explicitly takes KVM_MMU_LOCK(kvm) before calling kvm_mmu_unmap_gfn_range(). So remove the local trylock and make the common locking contract explicit with lockdep_assert_held_write() like x86. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-2-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/mmu.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 8469ed932421..da944cb68404 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -245,22 +245,17 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) { struct kvm_gstage gstage; - bool mmu_locked; bool flush; if (!kvm->arch.pgd) return false; - kvm_riscv_gstage_init(&gstage, kvm); - mmu_locked = spin_trylock(&kvm->mmu_lock); + lockdep_assert_held_write(&kvm->mmu_lock); + kvm_riscv_gstage_init(&gstage, kvm); flush = kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT, (range->end - range->start) << PAGE_SHIFT, range->may_block); - - if (mmu_locked) - spin_unlock(&kvm->mmu_lock); - if (flush) kvm_flush_remote_tlbs_range(kvm, range->start, range->end - range->start); From cc98f006c63c8e9f825ca5f89388fe5ace6a5c74 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 17 May 2026 23:34:24 +0800 Subject: [PATCH 2669/5214] KVM: riscv: Use an rwlock for mmu_lock RISC-V KVM currently uses a spinlock for mmu_lock. That serializes all G-stage MMU operations, including permission-only updates that do not allocate or free page-table pages. Use KVM's rwlock form of mmu_lock, as x86 and arm64 already do. Keep the existing map, unmap and teardown paths on the write side. This prepares RISC-V for read-side handling of G-stage permission updates. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-3-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_host.h | 2 ++ arch/riscv/kvm/gstage.c | 2 +- arch/riscv/kvm/mmu.c | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 75b0a951c1bc..60017ceec9d2 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -48,6 +48,8 @@ #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE +#define KVM_HAVE_MMU_RWLOCK + #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \ KVM_DIRTY_LOG_INITIALLY_SET) diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index e020b334ae6f..d4ce5e76989c 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -414,7 +414,7 @@ bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, * to prevent starvation and lockup detector warnings. */ if (!(gstage->flags & KVM_GSTAGE_FLAGS_LOCAL) && may_block && addr < end) - cond_resched_lock(&gstage->kvm->mmu_lock); + cond_resched_rwlock_write(&gstage->kvm->mmu_lock); } return flush; diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index da944cb68404..514f06a1f688 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -27,9 +27,9 @@ static void mmu_wp_memory_region(struct kvm *kvm, int slot) kvm_riscv_gstage_init(&gstage, kvm); - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); flush = kvm_riscv_gstage_wp_range(&gstage, start, end); - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); if (flush) kvm_flush_remote_tlbs_memslot(kvm, memslot); } @@ -67,9 +67,9 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, if (ret) goto out; - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); ret = kvm_riscv_gstage_set_pte(&gstage, &pcache, &map); - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); if (ret) goto out; @@ -88,9 +88,9 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) kvm_riscv_gstage_init(&gstage, kvm); - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); if (flush) kvm_flush_remote_tlbs_range(kvm, gpa >> PAGE_SHIFT, @@ -143,9 +143,9 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, kvm_riscv_gstage_init(&gstage, kvm); - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); if (flush) kvm_flush_remote_tlbs_range(kvm, gpa >> PAGE_SHIFT, size >> PAGE_SHIFT); @@ -523,7 +523,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, if (logging && !is_write) writable = false; - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); if (mmu_invalidate_retry(kvm, mmu_seq)) goto out_unlock; @@ -546,7 +546,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, out_unlock: kvm_release_faultin_page(kvm, page, ret && ret != -EEXIST, writable); - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); return ret; } @@ -576,7 +576,7 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) void *pgd = NULL; bool flush = false; - spin_lock(&kvm->mmu_lock); + write_lock(&kvm->mmu_lock); if (kvm->arch.pgd) { kvm_riscv_gstage_init(&gstage, kvm); flush = kvm_riscv_gstage_unmap_range(&gstage, 0UL, @@ -586,7 +586,7 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) kvm->arch.pgd_phys = 0; kvm->arch.pgd_levels = 0; } - spin_unlock(&kvm->mmu_lock); + write_unlock(&kvm->mmu_lock); if (flush) kvm_flush_remote_tlbs(kvm); From 7dd416fdd3fba2095c3cea58e36f8ee5d86dadc3 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 17 May 2026 23:34:25 +0800 Subject: [PATCH 2670/5214] KVM: riscv: Add a G-stage PTE cmpxchg helper Permission-only G-stage PTE updates can run in parallel once they are moved to the read side of mmu_lock. Plain set_pte() is not enough for that case because another CPU may update the same PTE first. x86 handles the same class of SPTE races with cmpxchg-based updates in its fast page fault and TDP MMU paths. Add a small RISC-V helper for atomic G-stage PTE updates. The helper reports contention to the caller and flushes the target range only when the PTE value actually changes. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-4-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_gstage.h | 4 ++++ arch/riscv/kvm/gstage.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h index f820c6783e16..21e2019df0cf 100644 --- a/arch/riscv/include/asm/kvm_gstage.h +++ b/arch/riscv/include/asm/kvm_gstage.h @@ -54,6 +54,10 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, const struct kvm_gstage_mapping *map); +bool kvm_riscv_gstage_try_update_pte(struct kvm_gstage *gstage, u32 level, + gpa_t addr, pte_t *ptep, + pte_t old_pte, pte_t new_pte); + int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, gpa_t gpa, phys_addr_t hpa, unsigned long page_size, diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index d4ce5e76989c..d878100aeb35 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -123,6 +123,20 @@ static void gstage_tlb_flush(struct kvm_gstage *gstage, u32 level, gpa_t addr) gstage->vmid); } +bool kvm_riscv_gstage_try_update_pte(struct kvm_gstage *gstage, u32 level, + gpa_t addr, pte_t *ptep, + pte_t old_pte, pte_t new_pte) +{ + if (cmpxchg(&ptep->pte, pte_val(old_pte), pte_val(new_pte)) != + pte_val(old_pte)) + return false; + + if (pte_val(old_pte) != pte_val(new_pte)) + gstage_tlb_flush(gstage, level, addr); + + return true; +} + int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, const struct kvm_gstage_mapping *map) From d7a26a0ba71542c9778d7d802dc79b0874f852bf Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 17 May 2026 23:34:26 +0800 Subject: [PATCH 2671/5214] KVM: riscv: Update G-stage PTE permissions atomically When a fault hits an existing G-stage leaf with the same PFN, KVM only needs to update the PTE permissions. This path will be used by read-side fault handling, so it must not overwrite a concurrent PTE update. Use the cmpxchg helper when relaxing permissions on an existing leaf, following the same concurrency model used by x86 for atomic SPTE permission updates. Retry if another CPU changed the PTE first, and use cpu_relax() while spinning. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-5-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/gstage.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index d878100aeb35..b83b85d1c016 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -182,17 +182,22 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, static void kvm_riscv_gstage_update_pte_prot(struct kvm_gstage *gstage, u32 level, gpa_t addr, pte_t *ptep, pgprot_t prot) { - pte_t new_pte; + pte_t old_pte, new_pte; - if (pgprot_val(pte_pgprot(ptep_get(ptep))) == pgprot_val(prot)) - return; + for (;;) { + old_pte = ptep_get(ptep); + if (pgprot_val(pte_pgprot(old_pte)) == pgprot_val(prot)) + return; - new_pte = pfn_pte(pte_pfn(ptep_get(ptep)), prot); - new_pte = pte_mkdirty(new_pte); + new_pte = pfn_pte(pte_pfn(old_pte), prot); + new_pte = pte_mkdirty(new_pte); - set_pte(ptep, new_pte); + if (kvm_riscv_gstage_try_update_pte(gstage, level, addr, ptep, + old_pte, new_pte)) + return; - gstage_tlb_flush(gstage, level, addr); + cpu_relax(); + } } int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, From 7705be59eb2d173933b55608ff7d26e14343e2f3 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Sun, 17 May 2026 23:34:27 +0800 Subject: [PATCH 2672/5214] KVM: riscv: Fast-path dirty logging write faults With dirty logging enabled, guest writes often fault on an existing 4K G-stage leaf that was write-protected only for dirty tracking. The slow path still performs the full fault handling flow and takes mmu_lock for write, even though the page-table shape does not change. x86 handles the analogous case in its fast page fault path by atomically making a writable SPTE writable again when the fault is only a write-protection fault. Add the same style of fast path for RISC-V. If a write fault hits an existing 4K leaf in a writable dirty-log memslot, mark the page dirty and atomically set the PTE writable and dirty under the read side of mmu_lock. The dirty bitmap is updated before the PTE becomes writable again. The PTE D bit is also set so systems that trap on a clear D bit do not fall back to the slow path for a writable but clean PTE. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-6-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/mmu.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 514f06a1f688..c847b101c73e 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -438,6 +438,77 @@ static unsigned long transparent_hugepage_adjust(struct kvm *kvm, return PAGE_SIZE; } +static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm, + struct kvm_memory_slot *memslot, + gpa_t gpa, + struct kvm_gstage_mapping *out_map) +{ + struct kvm_gstage gstage; + unsigned long mmu_seq; + pte_t old_pte, new_pte; + pte_t *ptep; + gfn_t gfn = gpa >> PAGE_SHIFT; + u32 ptep_level; + bool dirty_marked = false; + bool ret; + + kvm_riscv_gstage_init(&gstage, kvm); + mmu_seq = kvm->mmu_invalidate_seq; + + read_lock(&kvm->mmu_lock); + + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) { + ret = false; + goto out_unlock; + } + + if (!kvm_riscv_gstage_get_leaf(&gstage, gpa, &ptep, &ptep_level) || + ptep_level) { + ret = false; + goto out_unlock; + } + + for (;;) { + old_pte = ptep_get(ptep); + if (!(pte_val(old_pte) & _PAGE_LEAF)) { + ret = false; + break; + } + + if (!dirty_marked) { + mark_page_dirty_in_slot(kvm, memslot, gfn); + dirty_marked = true; + } + + if ((pte_val(old_pte) & (_PAGE_WRITE | _PAGE_DIRTY)) == + (_PAGE_WRITE | _PAGE_DIRTY)) { + new_pte = old_pte; + ret = true; + break; + } + + new_pte = pte_mkdirty(pte_mkwrite_novma(old_pte)); + + if (kvm_riscv_gstage_try_update_pte(&gstage, ptep_level, gpa, + ptep, old_pte, new_pte)) { + ret = true; + break; + } + cpu_relax(); + } + +out_unlock: + read_unlock(&kvm->mmu_lock); + + if (ret) { + out_map->addr = gpa & PAGE_MASK; + out_map->level = 0; + out_map->pte = new_pte; + } + + return ret; +} + int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, gpa_t gpa, unsigned long hva, bool is_write, struct kvm_gstage_mapping *out_map) @@ -461,6 +532,10 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, /* Setup initial state of output mapping */ memset(out_map, 0, sizeof(*out_map)); + if (is_write && logging && + kvm_riscv_mmu_dirty_log_write_fault_fast(kvm, memslot, gpa, out_map)) + return 0; + /* We need minimum second+third level pages */ ret = kvm_mmu_topup_memory_cache(pcache, kvm->arch.pgd_levels); if (ret) { From eb60a24b35bfb9e85a272e561379833e49a12a79 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Sun, 31 May 2026 18:38:28 -0500 Subject: [PATCH 2673/5214] iio: chemical: scd30: Replace manual locking with RAII locking scd30_core.c currently uses manual mutex_lock() and mutex_unlock() calls. Replace them with the newer guard(mutex)() for cleaner RAII patterns and to improve maintainability. Add new helper function scd30_trigger_handler_helper() containing the critical section for scd30_trigger_handler(). In addition, small refactor to replace "?:" operator with regular if/else returns. Signed-off-by: Maxwell Doose Reviewed-by: Joshua Crofts Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 66 ++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 11d6bc1b63e6..4e99b29f4518 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -368,11 +368,13 @@ static ssize_t calibration_auto_enable_show(struct device *dev, struct device_at int ret; u16 val; - mutex_lock(&state->lock); - ret = scd30_command_read(state, CMD_ASC, &val); - mutex_unlock(&state->lock); + guard(mutex)(&state->lock); - return ret ?: sysfs_emit(buf, "%d\n", val); + ret = scd30_command_read(state, CMD_ASC, &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr, @@ -387,11 +389,13 @@ static ssize_t calibration_auto_enable_store(struct device *dev, struct device_a if (ret) return ret; - mutex_lock(&state->lock); - ret = scd30_command_write(state, CMD_ASC, val); - mutex_unlock(&state->lock); + guard(mutex)(&state->lock); - return ret ?: len; + ret = scd30_command_write(state, CMD_ASC, val); + if (ret) + return ret; + + return len; } static ssize_t calibration_forced_value_show(struct device *dev, struct device_attribute *attr, @@ -402,11 +406,13 @@ static ssize_t calibration_forced_value_show(struct device *dev, struct device_a int ret; u16 val; - mutex_lock(&state->lock); - ret = scd30_command_read(state, CMD_FRC, &val); - mutex_unlock(&state->lock); + guard(mutex)(&state->lock); - return ret ?: sysfs_emit(buf, "%d\n", val); + ret = scd30_command_read(state, CMD_FRC, &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr, @@ -424,11 +430,13 @@ static ssize_t calibration_forced_value_store(struct device *dev, struct device_ if (val < SCD30_FRC_MIN_PPM || val > SCD30_FRC_MAX_PPM) return -EINVAL; - mutex_lock(&state->lock); - ret = scd30_command_write(state, CMD_FRC, val); - mutex_unlock(&state->lock); + guard(mutex)(&state->lock); - return ret ?: len; + ret = scd30_command_write(state, CMD_FRC, val); + if (ret) + return ret; + + return len; } static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0); @@ -579,24 +587,34 @@ static irqreturn_t scd30_irq_thread_handler(int irq, void *priv) return IRQ_HANDLED; } +static int scd30_trigger_handler_helper(struct iio_dev *indio_dev, int *scan_data, + size_t scan_data_size) +{ + struct scd30_state *state = iio_priv(indio_dev); + int ret; + + guard(mutex)(&state->lock); + + if (!iio_trigger_using_own(indio_dev)) + ret = scd30_read_poll(state); + else + ret = scd30_read_meas(state); + memcpy(scan_data, state->meas, scan_data_size); + + return ret; +} + static irqreturn_t scd30_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct scd30_state *state = iio_priv(indio_dev); struct { int data[SCD30_MEAS_COUNT]; aligned_s64 ts; } scan = { }; int ret; - mutex_lock(&state->lock); - if (!iio_trigger_using_own(indio_dev)) - ret = scd30_read_poll(state); - else - ret = scd30_read_meas(state); - memcpy(scan.data, state->meas, sizeof(state->meas)); - mutex_unlock(&state->lock); + ret = scd30_trigger_handler_helper(indio_dev, scan.data, sizeof(scan.data)); if (ret) goto out; From d1570c48f49a693974d000251030370ee2e83539 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:15:47 +0200 Subject: [PATCH 2674/5214] fs/ntfs3: bound attr_off in UpdateResidentValue against data_off In do_action()'s UpdateResidentValue case (fslog.c:3307), lrh->attr_off and lrh->redo_len come from the on-disk LRH. When they satisfy aoff + dlen < attr->res.data_off, the assignment attr->res.data_size = cpu_to_le32(aoff + dlen - data_off); underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1, data_off=0x18). Subsequent code that reads attr->res.data_size to walk the resident attribute payload would then read up to 4 GiB past the 1024-byte MFT record allocation. The existing mi_enum_attr() defense in fs/ntfs3/record.c:287 catches the corrupted data_size on the next attribute walk and fails the mount, but only on the path that walks all attributes. A read site that picks an attribute by name and reads its data_size without re-validating is not covered. Validate aoff against data_off and asize at the source. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe: with aoff=0x10 and data_off=0x18, the post-assignment data_size is 0xfffffff9 (mount then fails at -22 from mi_enum_attr). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 92a7e3a46240..847048637dda 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3339,6 +3339,17 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, nsize = ALIGN(nsize, 8); data_off = le16_to_cpu(attr->res.data_off); + /* + * aoff comes from the on-disk lrh->attr_off. Forbid + * writes that begin below the resident attribute's + * data_off (which would overwrite the resident header), + * and forbid aoff + dlen < data_off, which would make + * the data_size assignment below underflow to ~4 GiB. + */ + if (aoff < data_off || aoff + dlen < data_off || + aoff + dlen > asize) + goto dirty_vol; + if (nsize < asize) { memmove(Add2Ptr(attr, aoff), data, dlen); data = NULL; // To skip below memmove(). From fc4626bb3656362de8b0ecd56605d47a19ec3518 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:21:03 +0200 Subject: [PATCH 2675/5214] fs/ntfs3: bound DeleteIndexEntryAllocation memmove length In do_action()'s DeleteIndexEntryAllocation case, e->size comes from an on-disk INDEX_BUFFER entry. When e->size makes e + e->size point past hdr + hdr->used, PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t that is silently cast to a quasi-infinite size_t when passed to memmove(). The memmove then walks past the destination buffer. The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543 already carries the corresponding guard: if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize || Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) || used + esize > le32_to_cpu(hdr->total)) { goto dirty_vol; } Apply the same shape to the allocation-path case. Also reject esize == 0: memmove(e, e, ...) is a no-op and leaves hdr->used unchanged, hiding a malformed entry from the existing check_index_header() walk. Reproduced under UML+KASAN on mainline 8d90b09e6741 by mounting a crafted NTFS image: the unguarded memmove takes a length of 0xffffffffffffff00 and the kernel oopses in memmove+0x81/0x1a0 on the do_action+0x36a2 frame. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 847048637dda..2bd8754ef26d 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3598,9 +3598,23 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, } e1 = Add2Ptr(e, esize); - nsize = esize; used = le32_to_cpu(hdr->used); + /* + * Reject crafted entries whose e->size makes e + esize + * point past the INDEX_HDR's used boundary. Without this, + * PtrOffset(e1, hdr + used) underflows to a quasi-infinite + * size_t when fed to the memmove() below. + * + * Also reject esize == 0: memmove(e, e, ...) is a no-op and + * leaves hdr->used unchanged, masking the crafted entry. + */ + if (!esize || Add2Ptr(e, esize) > Add2Ptr(hdr, used) || + PtrOffset(e1, Add2Ptr(hdr, used)) < esize) + goto dirty_vol; + + nsize = esize; + memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used))); hdr->used = cpu_to_le32(used - nsize); From 5e7b598660cfa8e5af172cf4c65cffc126333307 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 12:34:05 -0400 Subject: [PATCH 2676/5214] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries: t16 = le16_to_cpu(lrh->lcns_follow); for (i = 0; i < t16; i++) { size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn)); dp->page_lcns[j + i] = lrh->page_lcns[i]; } find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array. Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard. Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path. This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <20260502154252.164586-1-jhapavitra98@gmail.com>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes, fixed conflicts] Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 2bd8754ef26d..81a305d4bcf2 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3394,8 +3394,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), attr_run(attr), - le32_to_cpu(attr->size) - - le16_to_cpu(attr->nres.run_off), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), &t64)) { goto dirty_vol; } @@ -4585,22 +4585,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - t32 = le32_to_cpu(dp->lcns_follow); - if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { - err = -EINVAL; - goto out; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - if (j >= t32 || i >= t32 - j) { - err = -EINVAL; - goto out; - } - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + /* + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ + if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > + le32_to_cpu(dp->lcns_follow)) { + err = -EINVAL; + log->set_dirty = true; + goto out; + } + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; case DeleteDirtyClusters: { From 6a4c53a2e26a865565bd6a460961e8d6fcb32329 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Mon, 1 Jun 2026 10:57:56 +0200 Subject: [PATCH 2677/5214] fs/ntfs3: validate lcns_follow in log_replay conversion log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY records when replaying version 0 restart tables. During this conversion, the memmove() length is derived directly from the on-disk lcns_follow field: memmove(&dp->vcn, &dp0->vcn_low, 2 * sizeof(u64) + le32_to_cpu(dp->lcns_follow) * sizeof(u64)); check_rstbl() validates restart table structure, but does not constrain per-entry lcns_follow values relative to the entry size. A malformed filesystem image can provide an oversized lcns_follow value, causing the conversion memmove() to access memory beyond the bounds of the allocated restart table buffer. The same field is later used to bound iteration over page_lcns[], so validating lcns_follow during conversion also prevents downstream out-of-bounds access from the same malformed metadata. Compute the maximum valid lcns_follow from the already-validated restart table entry size and reject entries that exceed this bound. Reuse the existing t16/t32 scratch variables already declared in log_replay() to avoid introducing new declarations. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha [almaz.alexandrovich@paragon-software.com: fixed the conflicts] Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 81a305d4bcf2..95e1cdb47475 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4257,13 +4257,26 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) if (rst->major_ver) goto end_conv_1; /* reduce tab pressure. */ + t16 = le16_to_cpu(dptbl->size); + if (t16 < sizeof(struct DIR_PAGE_ENTRY)) { + log->set_dirty = true; + goto out; + } + + t32 = (t16 - sizeof(struct DIR_PAGE_ENTRY)) / sizeof(u64); + dp = NULL; while ((dp = enum_rstbl(dptbl, dp))) { struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; - // NOTE: Danger. Check for of boundary. + u32 lcns = le32_to_cpu(dp->lcns_follow); + + if (lcns > t32) { + log->set_dirty = true; + goto out; + } + memmove(&dp->vcn, &dp0->vcn_low, - 2 * sizeof(u64) + - le32_to_cpu(dp->lcns_follow) * sizeof(u64)); + 2 * sizeof(u64) + lcns * sizeof(u64)); } end_conv_1: From 1797e00bf802d64b859ea18505087daad92019c8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:43 +0200 Subject: [PATCH 2678/5214] KVM: SEV: Don't terminate SNP VMs on #VMGEXIT without a registered GHCB If the guest attempts a non-MSR #VMGEXIT without the registered GHCB, return a GHCB_HV_RESP_MALFORMED_INPUT+GHCB_ERR_NOT_REGISTERED error to the guest instead of exiting KVM_RUN with -EINVAL (and in likelihood killing the VM). KVM has already mapped the requested GHCB, i.e. can cleanly report an error, and so exiting with -EINVAL is completely unjustified. Fixes: 0c76b1d08280 ("KVM: SEV: Add support to handle GHCB GPA register VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-19-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-19-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 6c6a6d663e29..7c2ebc81306f 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4520,9 +4520,12 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) sev_es_sync_from_ghcb(svm); /* SEV-SNP guest requires that the GHCB GPA must be registered */ - if (is_sev_snp_guest(vcpu) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) { - vcpu_unimpl(&svm->vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ghcb_gpa); - return -EINVAL; + if (is_sev_snp_guest(vcpu) && + !ghcb_gpa_is_registered(svm, control->ghcb_gpa)) { + vcpu_unimpl(vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", + control->ghcb_gpa); + svm_vmgexit_bad_input(svm, GHCB_ERR_NOT_REGISTERED); + return 1; } ret = sev_es_validate_vmgexit(svm); From 9a6e8d23dd6129eef5bfa474ddf161d732613701 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:44 +0200 Subject: [PATCH 2679/5214] KVM: SEV: Move GHCB "usage" check out of sev_es_validate_vmgexit() Move the check to verify the guest's requested GHCB out of sev_es_validate_vmgexit() as the first step towards making said helper a predicate whose sole purpose is to verify the guest has marked required GHCB fields as valid. Using a single "validate" helper sounds good on paper, but in practice it's difficult to verify that KVM is performing the necessary sanity checks (the usage of state is far removed from the relevant checks), makes it difficult to understand that "legacy" exits are simply routed to KVM's existing exit handlers, and most importantly, has directly contributed to a number of bugs as adding case-statements to the validation subtly removes them from the default path that rejects unknown exit codes with INVALID_EVENT. Deliberately extract the usage code check first so as to preserve the order of KVM's checks, even though future code extraction will technically fix bugs. No functional change intended. Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-20-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-20-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 7c2ebc81306f..880a2acd77bf 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3416,12 +3416,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) struct kvm_vcpu *vcpu = &svm->vcpu; u64 reason; - /* Only GHCB Usage code 0 is supported */ - if (svm->sev_es.ghcb->ghcb_usage) { - reason = GHCB_ERR_INVALID_USAGE; - goto vmgexit_err; - } - reason = GHCB_ERR_MISSING_INPUT; if (!kvm_ghcb_sw_exit_code_is_valid(svm) || @@ -3534,10 +3528,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) * Print the exit code even though it may not be marked valid as it * could help with debugging. */ - if (reason == GHCB_ERR_INVALID_USAGE) { - vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n", - svm->sev_es.ghcb->ghcb_usage); - } else if (reason == GHCB_ERR_INVALID_EVENT) { + if (reason == GHCB_ERR_INVALID_EVENT) { vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", control->exit_code); } else { @@ -4528,6 +4519,14 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) return 1; } + /* Only GHCB Usage code 0 is supported */ + if (svm->sev_es.ghcb->ghcb_usage) { + vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n", + svm->sev_es.ghcb->ghcb_usage); + svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_USAGE); + return 1; + } + ret = sev_es_validate_vmgexit(svm); if (ret) return ret; From 95d8c59cdf6d6c49b24cda1de595ed409cbb8596 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:45 +0200 Subject: [PATCH 2680/5214] KVM: SEV: Return INVALID_EVENT for SNP-only #VMGEXIT from non-SNP guest Signal INVALID_EVENT, not MISSING_INPUT, if a non-SNP guest attempts to invoke an SNP-only #VMGEXIT. Opportunistically move the checks out of sev_es_validate_vmgexit() to continue the march towards making said helper a predicate whose sole purpose is to verify the guest has marked required GHCB fields as valid. Fixes: e366f92ea99e ("KVM: SEV: Support SEV-SNP AP Creation NAE event") Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") Fixes: 88caf544c930 ("KVM: SEV: Provide support for SNP_GUEST_REQUEST NAE event") Fixes: 74458e4859d8 ("KVM: SEV: Provide support for SNP_EXTENDED_GUEST_REQUEST NAE event") Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-21-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-21-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 880a2acd77bf..b59adddfdbcc 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3491,8 +3491,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) goto vmgexit_err; break; case SVM_VMGEXIT_AP_CREATION: - if (!is_sev_snp_guest(vcpu)) - goto vmgexit_err; if (lower_32_bits(control->exit_info_1) != SVM_VMGEXIT_AP_DESTROY) if (!kvm_ghcb_rax_is_valid(svm)) goto vmgexit_err; @@ -3505,13 +3503,12 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) case SVM_VMGEXIT_TERM_REQUEST: break; case SVM_VMGEXIT_PSC: - if (!is_sev_snp_guest(vcpu) || !kvm_ghcb_sw_scratch_is_valid(svm)) + if (!kvm_ghcb_sw_scratch_is_valid(svm)) goto vmgexit_err; break; case SVM_VMGEXIT_GUEST_REQUEST: case SVM_VMGEXIT_EXT_GUEST_REQUEST: - if (!is_sev_snp_guest(vcpu) || - !PAGE_ALIGNED(control->exit_info_1) || + if (!PAGE_ALIGNED(control->exit_info_1) || !PAGE_ALIGNED(control->exit_info_2) || control->exit_info_1 == control->exit_info_2) goto vmgexit_err; @@ -4476,6 +4473,19 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) return 0; } +static bool is_snp_only_vmgexit(u64 exit_code) +{ + switch (exit_code) { + case SVM_VMGEXIT_AP_CREATION: + case SVM_VMGEXIT_GUEST_REQUEST: + case SVM_VMGEXIT_EXT_GUEST_REQUEST: + case SVM_VMGEXIT_PSC: + return true; + default: + return false; + } +} + int sev_handle_vmgexit(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -4527,6 +4537,13 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) return 1; } + if (is_snp_only_vmgexit(control->exit_code) && !is_sev_snp_guest(vcpu)) { + vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is SNP-only\n", + control->exit_code); + svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_EVENT); + return 1; + } + ret = sev_es_validate_vmgexit(svm); if (ret) return ret; From 5eb5d5d123a8bd29204147a0cdd712f6d6d339e4 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:46 +0200 Subject: [PATCH 2681/5214] KVM: SEV: Return INVALID_INPUT, not MISSING_INPUT, for bad GUEST_REQUEST input(s) Return INVALID_INPUT, not MISSING_INPUT, if the guest provides an unaligned address for a GUEST_REQUEST, and/or attempts to use the same page for the source and destination. The inputs are obviously invalid, not missing. Opportunistically move the checks out of sev_es_validate_vmgexit(), to continue the march towards reducing the scope of the helper, and to help guide future changes into correctly handling bad input. Fixes: 88caf544c930 ("KVM: SEV: Provide support for SNP_GUEST_REQUEST NAE event") Fixes: 74458e4859d8 ("KVM: SEV: Provide support for SNP_EXTENDED_GUEST_REQUEST NAE event") Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-22-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-22-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index b59adddfdbcc..84421d9a116b 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3508,10 +3508,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) break; case SVM_VMGEXIT_GUEST_REQUEST: case SVM_VMGEXIT_EXT_GUEST_REQUEST: - if (!PAGE_ALIGNED(control->exit_info_1) || - !PAGE_ALIGNED(control->exit_info_2) || - control->exit_info_1 == control->exit_info_2) - goto vmgexit_err; break; default: reason = GHCB_ERR_INVALID_EVENT; @@ -4631,10 +4627,20 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) ret = 1; break; case SVM_VMGEXIT_GUEST_REQUEST: - ret = snp_handle_guest_req(svm, control->exit_info_1, control->exit_info_2); - break; case SVM_VMGEXIT_EXT_GUEST_REQUEST: - ret = snp_handle_ext_guest_req(svm, control->exit_info_1, control->exit_info_2); + if (!PAGE_ALIGNED(control->exit_info_1) || + !PAGE_ALIGNED(control->exit_info_2) || + control->exit_info_1 == control->exit_info_2) { + svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT); + return 1; + } + + if (control->exit_code == SVM_VMGEXIT_GUEST_REQUEST) + ret = snp_handle_guest_req(svm, control->exit_info_1, + control->exit_info_2); + else + ret = snp_handle_ext_guest_req(svm, control->exit_info_1, + control->exit_info_2); break; case SVM_VMGEXIT_UNSUPPORTED_EVENT: vcpu_unimpl(vcpu, From b9114841777021ddc3b975c05fd5998a47b44587 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:47 +0200 Subject: [PATCH 2682/5214] KVM: SEV: Handle unknown #VMGEXIT reasons in sev_handle_vmgexit() Handle unknown #VMGEXIT reasons in sev_handle_vmgexit(), not in sev_es_validate_vmgexit(). This makes it _much_ more obvious that KVM simply funnels "legacy" exits to the standard SVM interception handlers, and is the final preparatory change needed to reduce the scope of sev_es_validate_vmgexit(). No functional change intended. Reviewed-by: Tom Lendacky Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-23-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-23-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 74 +++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 84421d9a116b..864d6aea544b 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3414,9 +3414,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) { struct vmcb_control_area *control = &svm->vmcb->control; struct kvm_vcpu *vcpu = &svm->vcpu; - u64 reason; - - reason = GHCB_ERR_MISSING_INPUT; if (!kvm_ghcb_sw_exit_code_is_valid(svm) || !kvm_ghcb_sw_exit_info_1_is_valid(svm) || @@ -3424,14 +3421,10 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) goto vmgexit_err; switch (control->exit_code) { - case SVM_EXIT_READ_DR7: - break; case SVM_EXIT_WRITE_DR7: if (!kvm_ghcb_rax_is_valid(svm)) goto vmgexit_err; break; - case SVM_EXIT_RDTSC: - break; case SVM_EXIT_RDPMC: if (!kvm_ghcb_rcx_is_valid(svm)) goto vmgexit_err; @@ -3444,8 +3437,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) if (!kvm_ghcb_xcr0_is_valid(svm)) goto vmgexit_err; break; - case SVM_EXIT_INVD: - break; case SVM_EXIT_IOIO: if (control->exit_info_1 & SVM_IOIO_STR_MASK) { if (!kvm_ghcb_sw_scratch_is_valid(svm)) @@ -3470,10 +3461,6 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) !kvm_ghcb_cpl_is_valid(svm)) goto vmgexit_err; break; - case SVM_EXIT_RDTSCP: - break; - case SVM_EXIT_WBINVD: - break; case SVM_EXIT_MONITOR: if (!kvm_ghcb_rax_is_valid(svm) || !kvm_ghcb_rcx_is_valid(svm) || @@ -3495,23 +3482,12 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) if (!kvm_ghcb_rax_is_valid(svm)) goto vmgexit_err; break; - case SVM_VMGEXIT_NMI_COMPLETE: - case SVM_VMGEXIT_AP_HLT_LOOP: - case SVM_VMGEXIT_AP_JUMP_TABLE: - case SVM_VMGEXIT_UNSUPPORTED_EVENT: - case SVM_VMGEXIT_HV_FEATURES: - case SVM_VMGEXIT_TERM_REQUEST: - break; case SVM_VMGEXIT_PSC: if (!kvm_ghcb_sw_scratch_is_valid(svm)) goto vmgexit_err; break; - case SVM_VMGEXIT_GUEST_REQUEST: - case SVM_VMGEXIT_EXT_GUEST_REQUEST: - break; default: - reason = GHCB_ERR_INVALID_EVENT; - goto vmgexit_err; + break; } return 0; @@ -3521,16 +3497,10 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) * Print the exit code even though it may not be marked valid as it * could help with debugging. */ - if (reason == GHCB_ERR_INVALID_EVENT) { - vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", - control->exit_code); - } else { - vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n", - control->exit_code); - dump_ghcb(svm); - } - - svm_vmgexit_bad_input(svm, reason); + vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n", + control->exit_code); + dump_ghcb(svm); + svm_vmgexit_bad_input(svm, GHCB_ERR_MISSING_INPUT); /* Resume the guest to "return" the error code. */ return 1; @@ -4547,6 +4517,25 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) svm_vmgexit_success(svm, 0); switch (control->exit_code) { + case SVM_EXIT_IOIO: + if (!((control->exit_info_1 & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT)) + return 1; + + fallthrough; + case SVM_EXIT_READ_DR7: + case SVM_EXIT_WRITE_DR7: + case SVM_EXIT_RDTSC: + case SVM_EXIT_RDTSCP: + case SVM_EXIT_RDPMC: + case SVM_EXIT_CPUID: + case SVM_EXIT_INVD: + case SVM_EXIT_MSR: + case SVM_EXIT_VMMCALL: + case SVM_EXIT_WBINVD: + case SVM_EXIT_MONITOR: + case SVM_EXIT_MWAIT: + ret = svm_invoke_exit_handler(vcpu, control->exit_code); + break; case SVM_VMGEXIT_MMIO_READ: case SVM_VMGEXIT_MMIO_WRITE: { bool is_write = control->exit_code == SVM_VMGEXIT_MMIO_WRITE; @@ -4643,18 +4632,21 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) control->exit_info_2); break; case SVM_VMGEXIT_UNSUPPORTED_EVENT: + /* + * Note, the _guest_ is reporting an unsupported #VC, i.e. this + * isn't the same thing as KVM getting an unsupported #VMGEXIT. + */ vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n", control->exit_info_1, control->exit_info_2); ret = -EINVAL; break; - case SVM_EXIT_IOIO: - if (!((control->exit_info_1 & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT)) - return 1; - - fallthrough; default: - ret = svm_invoke_exit_handler(vcpu, control->exit_code); + vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", + control->exit_code); + svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_EVENT); + ret = 1; + break; } return ret; From abb97ac118f03cb2f5007edb1cf9d4e9d53704df Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:48 +0200 Subject: [PATCH 2683/5214] KVM: SEV: Turn sev_es_validate_vmgexit() into a dedicated predicate Now that sev_es_validate_vmgexit() is only responsible for checking that all required GHCB fields are marked valid, turn it into a predicate whose name reflects exactly that. No functional change intended. Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-24-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-24-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 112 +++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 71 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 864d6aea544b..bb70df2bf1a4 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3410,7 +3410,7 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm) memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); } -static int sev_es_validate_vmgexit(struct vcpu_svm *svm) +static bool sev_es_are_required_ghcb_fields_valid(struct vcpu_svm *svm) { struct vmcb_control_area *control = &svm->vmcb->control; struct kvm_vcpu *vcpu = &svm->vcpu; @@ -3418,92 +3418,53 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) if (!kvm_ghcb_sw_exit_code_is_valid(svm) || !kvm_ghcb_sw_exit_info_1_is_valid(svm) || !kvm_ghcb_sw_exit_info_2_is_valid(svm)) - goto vmgexit_err; + return false; switch (control->exit_code) { case SVM_EXIT_WRITE_DR7: - if (!kvm_ghcb_rax_is_valid(svm)) - goto vmgexit_err; - break; + return kvm_ghcb_rax_is_valid(svm); case SVM_EXIT_RDPMC: - if (!kvm_ghcb_rcx_is_valid(svm)) - goto vmgexit_err; - break; + return kvm_ghcb_rcx_is_valid(svm); case SVM_EXIT_CPUID: if (!kvm_ghcb_rax_is_valid(svm) || !kvm_ghcb_rcx_is_valid(svm)) - goto vmgexit_err; - if (vcpu->arch.regs[VCPU_REGS_RAX] == 0xd) - if (!kvm_ghcb_xcr0_is_valid(svm)) - goto vmgexit_err; - break; + return false; + + return vcpu->arch.regs[VCPU_REGS_RAX] != 0xd || + kvm_ghcb_xcr0_is_valid(svm); case SVM_EXIT_IOIO: - if (control->exit_info_1 & SVM_IOIO_STR_MASK) { - if (!kvm_ghcb_sw_scratch_is_valid(svm)) - goto vmgexit_err; - } else { - if (!(control->exit_info_1 & SVM_IOIO_TYPE_MASK)) - if (!kvm_ghcb_rax_is_valid(svm)) - goto vmgexit_err; - } - break; + if (control->exit_info_1 & SVM_IOIO_STR_MASK) + return kvm_ghcb_sw_scratch_is_valid(svm); + + if (!(control->exit_info_1 & SVM_IOIO_TYPE_MASK)) + return kvm_ghcb_rax_is_valid(svm); + + return true; case SVM_EXIT_MSR: if (!kvm_ghcb_rcx_is_valid(svm)) - goto vmgexit_err; - if (control->exit_info_1) { - if (!kvm_ghcb_rax_is_valid(svm) || - !kvm_ghcb_rdx_is_valid(svm)) - goto vmgexit_err; - } - break; + return false; + + return !control->exit_info_1 || + (kvm_ghcb_rax_is_valid(svm) && kvm_ghcb_rdx_is_valid(svm)); case SVM_EXIT_VMMCALL: - if (!kvm_ghcb_rax_is_valid(svm) || - !kvm_ghcb_cpl_is_valid(svm)) - goto vmgexit_err; - break; + return kvm_ghcb_rax_is_valid(svm) && kvm_ghcb_cpl_is_valid(svm); case SVM_EXIT_MONITOR: - if (!kvm_ghcb_rax_is_valid(svm) || - !kvm_ghcb_rcx_is_valid(svm) || - !kvm_ghcb_rdx_is_valid(svm)) - goto vmgexit_err; - break; + return kvm_ghcb_rax_is_valid(svm) && + kvm_ghcb_rcx_is_valid(svm) && + kvm_ghcb_rdx_is_valid(svm); case SVM_EXIT_MWAIT: - if (!kvm_ghcb_rax_is_valid(svm) || - !kvm_ghcb_rcx_is_valid(svm)) - goto vmgexit_err; + return kvm_ghcb_rax_is_valid(svm) && kvm_ghcb_rcx_is_valid(svm); + case SVM_VMGEXIT_AP_CREATION: + return kvm_ghcb_rax_is_valid(svm) || + lower_32_bits(control->exit_info_1) == SVM_VMGEXIT_AP_DESTROY; break; case SVM_VMGEXIT_MMIO_READ: case SVM_VMGEXIT_MMIO_WRITE: - if (!kvm_ghcb_sw_scratch_is_valid(svm)) - goto vmgexit_err; - break; - case SVM_VMGEXIT_AP_CREATION: - if (lower_32_bits(control->exit_info_1) != SVM_VMGEXIT_AP_DESTROY) - if (!kvm_ghcb_rax_is_valid(svm)) - goto vmgexit_err; - break; case SVM_VMGEXIT_PSC: - if (!kvm_ghcb_sw_scratch_is_valid(svm)) - goto vmgexit_err; - break; + return kvm_ghcb_sw_scratch_is_valid(svm); default: - break; + return true; } - - return 0; - -vmgexit_err: - /* - * Print the exit code even though it may not be marked valid as it - * could help with debugging. - */ - vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n", - control->exit_code); - dump_ghcb(svm); - svm_vmgexit_bad_input(svm, GHCB_ERR_MISSING_INPUT); - - /* Resume the guest to "return" the error code. */ - return 1; } static void __sev_es_unmap_ghcb(struct vcpu_svm *svm) @@ -4510,9 +4471,17 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) return 1; } - ret = sev_es_validate_vmgexit(svm); - if (ret) - return ret; + if (!sev_es_are_required_ghcb_fields_valid(svm)) { + /* + * Print the exit code even though it may not be marked valid + * as it could help with debugging. + */ + vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n", + control->exit_code); + dump_ghcb(svm); + svm_vmgexit_bad_input(svm, GHCB_ERR_MISSING_INPUT); + return 1; + } svm_vmgexit_success(svm, 0); @@ -4599,6 +4568,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SEV_TERM; vcpu->run->system_event.ndata = 1; vcpu->run->system_event.data[0] = control->ghcb_gpa; + ret = 0; break; case SVM_VMGEXIT_PSC: ret = setup_vmgexit_scratch(svm, true, sizeof(struct psc_hdr)); From c114187c5dcf1a66a2910ec66a87d230d523e5c1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 29 May 2026 20:35:49 +0200 Subject: [PATCH 2684/5214] KVM: SEV: Remove sometimes-used function-scoped "ret" from #VMGEXIT handler Now that only two case-statements actually need a local "ret" variable, refactor sev_handle_vmgexit() to have all flows return directly when possible, and bury "ret" as "r" in the two paths that need to propagate a return value from a helper. No functional change intended. Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-25-seanjc@google.com> Signed-off-by: Paolo Bonzini Message-ID: <20260529183549.1104619-25-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 74 ++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index bb70df2bf1a4..bc9dd39778a1 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4418,7 +4418,6 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); struct vmcb_control_area *control = &svm->vmcb->control; u64 ghcb_gpa; - int ret; /* Validate the GHCB */ ghcb_gpa = control->ghcb_gpa; @@ -4503,12 +4502,12 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) case SVM_EXIT_WBINVD: case SVM_EXIT_MONITOR: case SVM_EXIT_MWAIT: - ret = svm_invoke_exit_handler(vcpu, control->exit_code); - break; + return svm_invoke_exit_handler(vcpu, control->exit_code); case SVM_VMGEXIT_MMIO_READ: case SVM_VMGEXIT_MMIO_WRITE: { bool is_write = control->exit_code == SVM_VMGEXIT_MMIO_WRITE; u64 len = control->exit_info_2; + int r; if (!len) return 1; @@ -4518,24 +4517,21 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) return 1; } - ret = setup_vmgexit_scratch(svm, !is_write, len); - if (ret) - break; + r = setup_vmgexit_scratch(svm, !is_write, len); + if (r) + return r; - ret = kvm_sev_es_mmio(vcpu, is_write, control->exit_info_1, len, - svm->sev_es.ghcb_sa); - break; + return kvm_sev_es_mmio(vcpu, is_write, control->exit_info_1, len, + svm->sev_es.ghcb_sa); } case SVM_VMGEXIT_NMI_COMPLETE: ++vcpu->stat.nmi_window_exits; svm->nmi_masked = false; kvm_make_request(KVM_REQ_EVENT, vcpu); - ret = 1; - break; + return 1; case SVM_VMGEXIT_AP_HLT_LOOP: svm->sev_es.ap_reset_hold_type = AP_RESET_HOLD_NAE_EVENT; - ret = kvm_emulate_ap_reset_hold(vcpu); - break; + return kvm_emulate_ap_reset_hold(vcpu); case SVM_VMGEXIT_AP_JUMP_TABLE: { struct kvm_sev_info *sev = to_kvm_sev_info(vcpu->kvm); @@ -4553,14 +4549,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) control->exit_info_1); svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT); } - - ret = 1; - break; + return 1; } case SVM_VMGEXIT_HV_FEATURES: svm_vmgexit_success(svm, GHCB_HV_FT_SUPPORTED); - ret = 1; - break; + return 1; case SVM_VMGEXIT_TERM_REQUEST: pr_info("SEV-ES guest requested termination: reason %#llx info %#llx\n", control->exit_info_1, control->exit_info_2); @@ -4568,23 +4561,20 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) vcpu->run->system_event.type = KVM_SYSTEM_EVENT_SEV_TERM; vcpu->run->system_event.ndata = 1; vcpu->run->system_event.data[0] = control->ghcb_gpa; - ret = 0; - break; - case SVM_VMGEXIT_PSC: - ret = setup_vmgexit_scratch(svm, true, sizeof(struct psc_hdr)); - if (ret) - break; + return 0; + case SVM_VMGEXIT_PSC: { + int r; - ret = snp_begin_psc(svm); - break; + r = setup_vmgexit_scratch(svm, true, sizeof(struct psc_hdr)); + if (r) + return r; + + return snp_begin_psc(svm); + } case SVM_VMGEXIT_AP_CREATION: - ret = sev_snp_ap_creation(svm); - if (ret) { + if (sev_snp_ap_creation(svm)) svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT); - } - - ret = 1; - break; + return 1; case SVM_VMGEXIT_GUEST_REQUEST: case SVM_VMGEXIT_EXT_GUEST_REQUEST: if (!PAGE_ALIGNED(control->exit_info_1) || @@ -4595,12 +4585,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) } if (control->exit_code == SVM_VMGEXIT_GUEST_REQUEST) - ret = snp_handle_guest_req(svm, control->exit_info_1, - control->exit_info_2); - else - ret = snp_handle_ext_guest_req(svm, control->exit_info_1, - control->exit_info_2); - break; + return snp_handle_guest_req(svm, control->exit_info_1, + control->exit_info_2); + + return snp_handle_ext_guest_req(svm, control->exit_info_1, + control->exit_info_2); case SVM_VMGEXIT_UNSUPPORTED_EVENT: /* * Note, the _guest_ is reporting an unsupported #VC, i.e. this @@ -4609,17 +4598,16 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n", control->exit_info_1, control->exit_info_2); - ret = -EINVAL; - break; + return -EINVAL; default: vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", control->exit_code); svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_EVENT); - ret = 1; - break; + return 1; } - return ret; + KVM_BUG_ON(1, vcpu->kvm); + return -EIO; } int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in) From a592b28bc82d88e7d3f4ff69ccc1033dd2b0ba86 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 3 Jun 2026 12:11:18 +0300 Subject: [PATCH 2685/5214] usb: xhci: fix typo in xhci_set_port_power() comment Fix a spelling mistake (re-aquire -> re-acquire) in the function header comment. No functional change. Signed-off-by: Stepan Ionichev Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 3830d4123961..b0264bd8577a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -638,7 +638,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) /* * xhci_set_port_power() must be called with xhci->lock held. - * It will release and re-aquire the lock while calling ACPI + * It will release and re-acquire the lock while calling ACPI * method. */ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, From 7ee645963075651d72f8d85bee428a9b7f1f148c Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 3 Jun 2026 12:11:19 +0300 Subject: [PATCH 2686/5214] usb: xhci: remove legacy 'num_trbs_free' tracking Keeping track of free TRBs in a ring by adding and subtracting each time a enqueue or dequeue pointer is modified has proven to be buggy and complicated, especially over long periods of time. The xhci driver has already moved to calculating free TRBs dynamically based on ring size and the enqueue/dequeue positions. The DbC path is the last user of 'num_trbs_free'. Rather than maintaining two separate accounting mechanisms, remove the field entirely and switch DbC to use xhci_num_trbs_free(). Since 'num_trbs_free' undercounts by one, and xhci_num_trbs_free() does not, the check for sufficient free TRBs is adjusted. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 5 +---- drivers/usb/host/xhci-mem.c | 6 ------ drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.h | 2 +- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 7e6f7d72f03e..6a9f73fecb73 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -275,7 +275,6 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic, xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); - ring->num_trbs_free--; next = ++(ring->enqueue); if (TRB_TYPE_LINK_LE32(next->link.control)) { next->link.control ^= cpu_to_le32(TRB_CYCLE); @@ -296,7 +295,7 @@ static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep, num_trbs = count_trbs(req->dma, req->length); WARN_ON(num_trbs != 1); - if (ring->num_trbs_free < num_trbs) + if (xhci_num_trbs_free(ring) <= num_trbs) return -EBUSY; addr = req->dma; @@ -796,7 +795,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) } if (r->status == -COMP_STALL_ERROR) { dev_warn(dbc->dev, "Give back stale stalled req\n"); - ring->num_trbs_free++; xhci_dbc_giveback(r, 0); } } @@ -861,7 +859,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) break; } - ring->num_trbs_free++; req->actual = req->length - remain_length; xhci_dbc_giveback(req, status); } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 997fe90f54e5..289461c06bf9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -324,12 +324,6 @@ void xhci_initialize_ring_info(struct xhci_ring *ring) * handling ring expansion, set the cycle state equal to the old ring. */ ring->cycle_state = 1; - - /* - * Each segment has a link TRB, and leave an extra TRB for SW - * accounting purpose - */ - ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e47e644b296e..f62db238276d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -340,7 +340,7 @@ static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) * Only for transfer and command rings where driver is the producer, not for * event rings. */ -static unsigned int xhci_num_trbs_free(struct xhci_ring *ring) +unsigned int xhci_num_trbs_free(struct xhci_ring *ring) { struct xhci_segment *enq_seg = ring->enq_seg; union xhci_trb *enq = ring->enqueue; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index aeecd301f207..e73c498449e8 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1376,7 +1376,6 @@ struct xhci_ring { u32 cycle_state; unsigned int stream_id; unsigned int num_segs; - unsigned int num_trbs_free; /* used only by xhci DbC */ unsigned int bounce_buf_len; enum xhci_ring_type type; u32 old_trb_comp_code; @@ -1955,6 +1954,7 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, void xhci_cleanup_command_queue(struct xhci_hcd *xhci); void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); unsigned int count_trbs(u64 addr, u64 len); +unsigned int xhci_num_trbs_free(struct xhci_ring *ring); int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, gfp_t gfp_flags); void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); From f232db53c265467293123ccc02cf52793bca9cdd Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 3 Jun 2026 12:11:20 +0300 Subject: [PATCH 2687/5214] usb: xhci: Simplify xhci_quiesce() The function reads USBCMD, clears some bits and writes it back. Its treatment of the Run bit is weird: the bit is usually written as 0, as we would expect, but it may also be written as 1 if both its current value and USBSTS.HCHalted are observed as 1. Per xHCI 5.4.2, HCHalted is 0 whenever Run is 1, so the above can only happen due to buggy HW or SW, e.g. concurrent xhci_quiesce() and xhci_start() execution. It's unclear why we should treat such cases specially and write the bit as 1. The logic comes from original PoC implementation and has never been explained. Just write 0 every time, which looks like the safer choice when the intent is to stop the xHC. We could get in trouble if clearing Run causes some very broken xHC to start running after it was halted, but no such case has been documented. It seems the logic was just poorly thought out. Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a54f5b57f205..0bf0446b4c87 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -102,17 +102,10 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us) */ void xhci_quiesce(struct xhci_hcd *xhci) { - u32 halted; u32 cmd; - u32 mask; - - mask = ~(XHCI_IRQS); - halted = readl(&xhci->op_regs->status) & STS_HALT; - if (!halted) - mask &= ~CMD_RUN; cmd = readl(&xhci->op_regs->command); - cmd &= mask; + cmd &= ~(CMD_RUN | XHCI_IRQS); writel(cmd, &xhci->op_regs->command); } From d20405dcdfb616cbae5c3e17e790a969ea03469d Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 3 Jun 2026 12:11:21 +0300 Subject: [PATCH 2688/5214] usb: xhci: Remove skip_isoc_td() This function is pointless because usb_submit_urb() initializes all isoc frame descriptors to -EXDEV and 0 length so that HCDs don't need to do anything with transfers which were never executed. Other HCDs rely on this (e.g. EHCI itd_complete()), so we can too. This gets rid of a potentially dangereous function which could corrupt memory if we weren't super careful to only call it on isoc URBs. Also, set status to 0 rather than any random status determined by the later TD which caused skipping. This status will be ignored anyway. Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f62db238276d..1fbf43a51037 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2492,26 +2492,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, finish_td(xhci, ep, ep_ring, td, trb_comp_code); } -static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_virt_ep *ep, int status) -{ - struct urb_priv *urb_priv; - struct usb_iso_packet_descriptor *frame; - int idx; - - urb_priv = td->urb->hcpriv; - idx = urb_priv->num_tds_done; - frame = &td->urb->iso_frame_desc[idx]; - - /* The transfer is partly done. */ - frame->status = -EXDEV; - - /* calc actual length */ - frame->actual_length = 0; - - xhci_dequeue_td(xhci, td, ep->ring, status); -} - /* * Process bulk and interrupt tds, update urb status and actual_length. */ @@ -2854,7 +2834,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID) return 0; - skip_isoc_td(xhci, td, ep, status); + /* + * TD was missed, skip it. Core already initialized frame->status + * to -EXDEV and frame->actual_length to 0, nothing more to do. + */ + xhci_dequeue_td(xhci, td, ep_ring, 0); if (!list_empty(&ep_ring->td_list)) { if (ring_xrun_event) { From 298ff132714a660283ae6ed700e9bd33ef58ec1a Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 3 Jun 2026 12:11:22 +0300 Subject: [PATCH 2689/5214] usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling This URB flag was never supposed to have any effect on isoc endpoints. No kernel code uses the flag except usb_sg_init(), on non-isoc only. USBFS can't use it on isoc because proc_do_submiturb() rejects it. Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 1fbf43a51037..a5342d49a65b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2395,7 +2395,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, u32 trb_comp_code; bool sum_trbs_for_length = false; u32 remaining, requested, ep_trb_len; - int short_framestatus; trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); urb_priv = td->urb->hcpriv; @@ -2404,8 +2403,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, requested = frame->length; remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2])); - short_framestatus = td->urb->transfer_flags & URB_SHORT_NOT_OK ? - -EREMOTEIO : 0; /* handle completion code */ switch (trb_comp_code) { @@ -2413,15 +2410,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* Don't overwrite status if TD had an error, see xHCI 4.9.1 */ if (td->error_mid_td) break; - if (remaining) { - frame->status = short_framestatus; + if (remaining) sum_trbs_for_length = true; - break; - } frame->status = 0; break; case COMP_SHORT_PACKET: - frame->status = short_framestatus; + frame->status = 0; sum_trbs_for_length = true; break; case COMP_BANDWIDTH_OVERRUN_ERROR: @@ -2456,7 +2450,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, break; case COMP_STOPPED_SHORT_PACKET: /* field normally containing residue now contains transferred */ - frame->status = short_framestatus; + frame->status = 0; requested = remaining; break; case COMP_STOPPED_LENGTH_INVALID: From e765ab012f73717238c95ab9c34bfc3c767fa48c Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 3 Jun 2026 12:11:23 +0300 Subject: [PATCH 2690/5214] usb: xhci: Improve Soft Retries after short transfers A short transfer is a successful one, so reset the error count. Otherwise, endpoints which always complete short are limited to three retries per endpoint life rather than per URB. Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a5342d49a65b..608b6f3ec9f6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2516,6 +2516,7 @@ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; break; case COMP_SHORT_PACKET: + ep->err_count = 0; td->status = 0; break; case COMP_STOPPED_SHORT_PACKET: From dc2ff8ba9b9093eb0564407e46e71520b054fc85 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:24 +0300 Subject: [PATCH 2691/5214] xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states Reading the 'dbc' sysfs file won't return the "stalled" string anymore as "stalled" is no longer considered a DbC state since commit 9044ad57b60b ("xhci: dbc: Fix STALL transfer event handling") in 6.12 kernel. Remove it from sysfs-bus-pci-drivers-xhci_hcd ABI documentation Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd index d10e6de3adb2..98a8376a83d2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd @@ -22,7 +22,7 @@ Description: Reading this attribute gives the state of the DbC. It can be one of the following states: disabled, enabled, - initialized, connected, configured and stalled. + initialized, connected or configured. What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor Date: March 2023 From 520058b73ba336380ecf7ea412263de9a7573df8 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:25 +0300 Subject: [PATCH 2692/5214] xhci: dbc: serialize enabling and disabling dbc DbC can be enabled and disabled via sysfs, serialize those with a mutex to make sure everything is done in the correct order. remove xhci_do_dbc_stop() and integrate the register write and dbc->state setting into xhci_do_stop() Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 66 ++++++++++++++++++++-------------- drivers/usb/host/xhci-dbgcap.h | 1 + 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 6a9f73fecb73..49ae546c4103 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -661,17 +661,6 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) return 0; } -static int xhci_do_dbc_stop(struct xhci_dbc *dbc) -{ - if (dbc->state == DS_DISABLED) - return -EINVAL; - - writel(0, &dbc->regs->control); - dbc->state = DS_DISABLED; - - return 0; -} - static int xhci_dbc_start(struct xhci_dbc *dbc) { int ret; @@ -683,29 +672,37 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_start(dbc); + if (ret) + goto err_unlock; + spin_unlock_irqrestore(&dbc->lock, flags); - if (ret) { - pm_runtime_put(dbc->dev); /* note this was self.controller */ - return ret; - } + mod_delayed_work(system_percpu_wq, &dbc->event_work, + msecs_to_jiffies(dbc->poll_interval)); - return mod_delayed_work(system_percpu_wq, &dbc->event_work, - msecs_to_jiffies(dbc->poll_interval)); + return 0; + +err_unlock: + + spin_unlock_irqrestore(&dbc->lock, flags); + pm_runtime_put(dbc->dev); /* note this was self.controller */ + + return ret; } static void xhci_dbc_stop(struct xhci_dbc *dbc) { - int ret; unsigned long flags; WARN_ON(!dbc); + spin_lock(&dbc->lock); + switch (dbc->state) { case DS_DISABLED: + spin_unlock(&dbc->lock); return; case DS_CONFIGURED: - spin_lock(&dbc->lock); xhci_dbc_flush_requests(dbc); spin_unlock(&dbc->lock); @@ -713,19 +710,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) dbc->driver->disconnect(dbc); break; default: + spin_unlock(&dbc->lock); break; } cancel_delayed_work_sync(&dbc->event_work); spin_lock_irqsave(&dbc->lock, flags); - ret = xhci_do_dbc_stop(dbc); + writel(0, &dbc->regs->control); + dbc->state = DS_DISABLED; spin_unlock_irqrestore(&dbc->lock, flags); - if (ret) - return; xhci_dbc_mem_cleanup(dbc); - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ + + pm_runtime_put(dbc->dev); /* note, was self.controller */ } static void @@ -1072,12 +1070,17 @@ static ssize_t dbc_store(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - if (sysfs_streq(buf, "enable")) + if (sysfs_streq(buf, "enable")) { + mutex_lock(&dbc->enable_mutex); xhci_dbc_start(dbc); - else if (sysfs_streq(buf, "disable")) + mutex_unlock(&dbc->enable_mutex); + } else if (sysfs_streq(buf, "disable")) { + mutex_lock(&dbc->enable_mutex); xhci_dbc_stop(dbc); - else + mutex_unlock(&dbc->enable_mutex); + } else { return -EINVAL; + } return count; } @@ -1443,6 +1446,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); spin_lock_init(&dbc->lock); + mutex_init(&dbc->enable_mutex); ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups); if (ret) @@ -1460,8 +1464,9 @@ void xhci_dbc_remove(struct xhci_dbc *dbc) if (!dbc) return; /* stop hw, stop wq and call dbc->ops->stop() */ + mutex_lock(&dbc->enable_mutex); xhci_dbc_stop(dbc); - + mutex_unlock(&dbc->enable_mutex); /* remove sysfs files */ sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups); @@ -1514,6 +1519,8 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) if (!dbc) return 0; + mutex_lock(&dbc->enable_mutex); + switch (dbc->state) { case DS_ENABLED: case DS_CONNECTED: @@ -1525,6 +1532,7 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) } xhci_dbc_stop(dbc); + mutex_unlock(&dbc->enable_mutex); return 0; } @@ -1537,11 +1545,15 @@ int xhci_dbc_resume(struct xhci_hcd *xhci) if (!dbc) return 0; + mutex_lock(&dbc->enable_mutex); + if (dbc->resume_required) { dbc->resume_required = 0; xhci_dbc_start(dbc); } + mutex_unlock(&dbc->enable_mutex); + return ret; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 20ae4e7617f2..f4c0169a1b4d 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -140,6 +140,7 @@ struct dbc_driver { struct xhci_dbc { spinlock_t lock; /* device access */ + struct mutex enable_mutex; struct device *dev; struct xhci_hcd *xhci; struct dbc_regs __iomem *regs; From de58a7d374eb0caae801704c3e47400b41a53acf Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:26 +0300 Subject: [PATCH 2693/5214] xhci: dbc: add helper to set and clear DbC DCE enable bit Add xhci_dbc_enable_dce() helper to enable or disable DbC by manipulating DCE bit correctly. It will be used for stuck DbC recovery attempts in addition to normal DbC enable and disable functionality Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 49ae546c4103..8cf0f0356bf1 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -628,18 +628,30 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) dbc->ring_evt = NULL; } +static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable) +{ + u32 done_state = 0; + u32 ctrl = 0; + + if (enable) { + ctrl = readl(&dbc->regs->control); + ctrl |= DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE; + done_state = DBC_CTRL_DBC_ENABLE; + } + + writel(ctrl, &dbc->regs->control); + return xhci_handshake(&dbc->regs->control, DBC_CTRL_DBC_ENABLE, + done_state, 1000); +} + static int xhci_do_dbc_start(struct xhci_dbc *dbc) { int ret; - u32 ctrl; if (dbc->state != DS_DISABLED) return -EINVAL; - writel(0, &dbc->regs->control); - ret = xhci_handshake(&dbc->regs->control, - DBC_CTRL_DBC_ENABLE, - 0, 1000); + ret = xhci_dbc_enable_dce(dbc, false); if (ret) return ret; @@ -647,12 +659,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) if (ret) return ret; - ctrl = readl(&dbc->regs->control); - writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE, - &dbc->regs->control); - ret = xhci_handshake(&dbc->regs->control, - DBC_CTRL_DBC_ENABLE, - DBC_CTRL_DBC_ENABLE, 1000); + ret = xhci_dbc_enable_dce(dbc, true); if (ret) return ret; From 724eab31dd7edea68b731b7635f91e3158c61d2a Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:27 +0300 Subject: [PATCH 2694/5214] xhci: dbc: add timestamps to DbC state changes in a new helper. The timestamp helps us track when a state changed the last time. It allows us to detect if DbC is stuck in connected state for too long, and can later be used to enable runtime suspend if there is no activity for some time Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-11-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 18 ++++++++++++------ drivers/usb/host/xhci-dbgcap.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 8cf0f0356bf1..34441ffa5e15 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -644,6 +644,12 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable) done_state, 1000); } +static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state) +{ + dbc->state_timestamp = jiffies; + dbc->state = new_state; +} + static int xhci_do_dbc_start(struct xhci_dbc *dbc) { int ret; @@ -663,7 +669,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) if (ret) return ret; - dbc->state = DS_ENABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); return 0; } @@ -725,7 +731,7 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); writel(0, &dbc->regs->control); - dbc->state = DS_DISABLED; + xhci_dbc_set_state(dbc, DS_DISABLED); spin_unlock_irqrestore(&dbc->lock, flags); xhci_dbc_mem_cleanup(dbc); @@ -896,7 +902,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) case DS_ENABLED: portsc = readl(&dbc->regs->portsc); if (portsc & DBC_PORTSC_CONN_STATUS) { - dbc->state = DS_CONNECTED; + xhci_dbc_set_state(dbc, DS_CONNECTED); dev_info(dbc->dev, "DbC connected\n"); } @@ -904,7 +910,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) case DS_CONNECTED: ctrl = readl(&dbc->regs->control); if (ctrl & DBC_CTRL_DBC_RUN) { - dbc->state = DS_CONFIGURED; + xhci_dbc_set_state(dbc, DS_CONFIGURED); dev_info(dbc->dev, "DbC configured\n"); portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); @@ -919,7 +925,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) if (!(portsc & DBC_PORTSC_PORT_ENABLED) && !(portsc & DBC_PORTSC_CONN_STATUS)) { dev_info(dbc->dev, "DbC cable unplugged\n"); - dbc->state = DS_ENABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); xhci_dbc_flush_requests(dbc); xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; @@ -929,7 +935,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) if (portsc & DBC_PORTSC_RESET_CHANGE) { dev_info(dbc->dev, "DbC port reset\n"); writel(portsc, &dbc->regs->portsc); - dbc->state = DS_ENABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); xhci_dbc_flush_requests(dbc); xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index f4c0169a1b4d..b3efea43a6be 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -163,6 +163,7 @@ struct xhci_dbc { struct delayed_work event_work; unsigned int poll_interval; /* ms */ unsigned long xfer_timestamp; + unsigned long state_timestamp; unsigned resume_required:1; struct dbc_ep eps[2]; From a3d34e5525cef4a53b755b94807dbe8c2976d7e7 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:28 +0300 Subject: [PATCH 2695/5214] xhci: dbc: detect and recover hung DbC during enumeraton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a timeout between the detection of the debug host connection and the DbC Run transition to ‘1’. Toggle the DCE bit to re-enable DbC in order to retry the debug device enumeration process if the DbC run transition takes too long. Set the timeout to 2 seconds See xhci specification section 7.6.4.1 "Debug Capability Initialization" Also detect cable disconnect during enable and connected state. Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 28 +++++++++++++++++++++++++++- drivers/usb/host/xhci-dbgcap.h | 1 + 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 34441ffa5e15..b1cabf5582fa 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -901,23 +901,49 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_ERR; case DS_ENABLED: portsc = readl(&dbc->regs->portsc); + ctrl = readl(&dbc->regs->control); + if (portsc & DBC_PORTSC_CONN_STATUS) { xhci_dbc_set_state(dbc, DS_CONNECTED); dev_info(dbc->dev, "DbC connected\n"); + } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) { + dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n"); } return EVT_DONE; case DS_CONNECTED: ctrl = readl(&dbc->regs->control); + portsc = readl(&dbc->regs->portsc); if (ctrl & DBC_CTRL_DBC_RUN) { xhci_dbc_set_state(dbc, DS_CONFIGURED); dev_info(dbc->dev, "DbC configured\n"); - portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); ret = EVT_GSER; break; } + /* Connection lost */ + if (!(portsc & DBC_PORTSC_CONN_STATUS)) { + /* covers DCE == 0 as it also sets CONN_STATUS to 0 */ + dev_warn(dbc->dev, "DbC connection lost mid enumeration\n"); + xhci_dbc_set_state(dbc, DS_ENABLED); + + return EVT_DONE; + } + + /* Enumeration timeout */ + if (time_is_before_jiffies(dbc->state_timestamp + + msecs_to_jiffies(DBC_ENUMERATION_TIMEOUT))) { + dev_err(dbc->dev, "DbC enumeration timeout, re-enabling DbC\n"); + dev_dbg(dbc->dev, "dcctrl %x, dcportsc %x\n", ctrl, portsc); + + /* Toggle DCE to retry enumeration */ + ret = xhci_dbc_enable_dce(dbc, false); + udelay(100); + ret = xhci_dbc_enable_dce(dbc, true); + xhci_dbc_set_state(dbc, DS_ENABLED); + } + return EVT_DONE; case DS_CONFIGURED: /* Handle cable unplug event: */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index b3efea43a6be..df7aca8bfe99 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -113,6 +113,7 @@ struct dbc_ep { #define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */ #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */ #define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */ +#define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */ /* * Private structure for DbC hardware state: */ From 82b70c799281cc24506085be978b829149ba0ca4 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 3 Jun 2026 12:11:29 +0300 Subject: [PATCH 2696/5214] xhci: Prevent queuing new commands if xhci is inaccessible Refuse to queue a new command on the command ring if xHC is marked inaccessible with the HCD_FLAG_HW_ACCESSIBLE. HCD_FLAG_HW_ACCESSIBLE is set and cleared in suspend and resume. Also print a warning if xhci is being suspended with commands still pending on the command ring. Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-13-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 6 ++++++ drivers/usb/host/xhci.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 608b6f3ec9f6..83209db29962 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -4332,6 +4332,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, u32 field3, u32 field4, bool command_must_succeed) { int reserved_trbs = xhci->cmd_ring_reserved_trbs; + struct usb_hcd *hcd = xhci_to_hcd(xhci); int ret; if ((xhci->xhc_state & XHCI_STATE_DYING) || @@ -4341,6 +4342,11 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, return -ESHUTDOWN; } + if (!HCD_HW_ACCESSIBLE(hcd)) { + xhci_warn(xhci, "Can't queue command, xHC not accessible\n"); + return -ESHUTDOWN; + } + if (!command_must_succeed) reserved_trbs++; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0bf0446b4c87..0646fedf2042 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -992,6 +992,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) /* step 1: stop endpoint */ /* skipped assuming that port suspend has done */ + /* Check if command ring is empty */ + if (!list_empty(&xhci->cmd_list)) + xhci_warn(xhci, "Suspending and stopping xHC with pending command!\n"); + /* step 2: clear Run/Stop bit */ command = readl(&xhci->op_regs->command); command &= ~CMD_RUN; From 7cd89392f6f981e3c537ce5e06d928f8e4f87489 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 3 Jun 2026 12:11:30 +0300 Subject: [PATCH 2697/5214] usb: xhci: refactor DCBAA struct Embed the 'xhci_device_context_array' structure directly within 'xhci_hcd' instead of allocating it as a separate block. Only the array of device context addresses is now allocated separately. Since the device context addresses are no longer part of an array structure, rename 'dev_context_ptrs' to 'ctx_array' for clearer access semantics. Also remove the redundant comment next to the 'ctx_array' allocation; using dma_alloc_coherent() for 64-bit * N allocations guarantees both physically contiguous and properly aligned for 64-byte boundaries. The xHCI section (5.4.6) refers to DCBAAP instead of DCBAA (6.1). This change does not modify the number of host controller slots but simplifies memory management and prepares the driver for a variable number of HC slots in the future. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-14-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 40 ++++++++++++++++++------------------ drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 6 +++--- drivers/usb/host/xhci.h | 13 +++++++----- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 289461c06bf9..019dac47c5d6 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -877,8 +877,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, /* If device ctx array still points to _this_ device, clear it */ if (dev->out_ctx && - xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma)) - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + xhci->dcbaa.ctx_array[slot_id] == cpu_to_le64(dev->out_ctx->dma)) + xhci->dcbaa.ctx_array[slot_id] = 0; trace_xhci_free_virt_device(dev); @@ -1016,11 +1016,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, dev->udev = udev; /* Point to output device context in dcbaa. */ - xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma); + xhci->dcbaa.ctx_array[slot_id] = cpu_to_le64(dev->out_ctx->dma); xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", slot_id, - &xhci->dcbaa->dev_context_ptrs[slot_id], - le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); + &xhci->dcbaa.ctx_array[slot_id], + le64_to_cpu(xhci->dcbaa.ctx_array[slot_id])); trace_xhci_alloc_virt_device(dev); @@ -1671,7 +1671,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad->sp_buffers) goto fail_sp3; - xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); + xhci->dcbaa.ctx_array[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, @@ -1927,6 +1927,7 @@ void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_device_context_array *dcbaa; int i; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1972,10 +1973,12 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed medium stream array pool"); - if (xhci->dcbaa) - dma_free_coherent(dev, sizeof(*xhci->dcbaa), - xhci->dcbaa, xhci->dcbaa->dma); - xhci->dcbaa = NULL; + dcbaa = &xhci->dcbaa; + if (dcbaa->ctx_array) { + dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS), + dcbaa->ctx_array, dcbaa->dma); + dcbaa->ctx_array = NULL; + } scratchpad_free(xhci); @@ -2403,23 +2406,20 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - dma_addr_t dma; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_device_context_array *dcbaa = &xhci->dcbaa; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); - /* - * xHCI section 5.4.6 - Device Context array must be - * "physically contiguous and 64-byte (cache line) aligned". - */ - xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags); - if (!xhci->dcbaa) + dcbaa->ctx_array = + dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS), + &dcbaa->dma, flags); + if (!dcbaa->ctx_array) goto fail; - xhci->dcbaa->dma = dma; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Device context base array address = %pad (DMA), %p (virt)", - &xhci->dcbaa->dma, xhci->dcbaa); + &dcbaa->dma, dcbaa->ctx_array); /* * Initialize the ring segment pool. The ring must be a contiguous diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 83209db29962..3c304372bad3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1613,7 +1613,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id, /* Delete default control endpoint resources */ xhci_free_device_endpoint_resources(xhci, virt_dev, true); if (cmd_comp_code == COMP_SUCCESS) { - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + xhci->dcbaa.ctx_array[slot_id] = 0; xhci->devs[slot_id] = NULL; } } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0646fedf2042..14d7f866f220 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -551,7 +551,7 @@ static void xhci_init(struct usb_hcd *hcd) xhci_set_cmd_ring_deq(xhci); /* Set Device Context Base Address Array pointer */ - xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr); + xhci_write_64(xhci, xhci->dcbaa.dma, &xhci->op_regs->dcbaa_ptr); /* Set Doorbell array pointer */ xhci_set_doorbell_ptr(xhci); @@ -4457,9 +4457,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Slot ID %d dcbaa entry @%p = %#016llx", udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + &xhci->dcbaa.ctx_array[udev->slot_id], (unsigned long long) - le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); + le64_to_cpu(xhci->dcbaa.ctx_array[udev->slot_id])); xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e73c498449e8..9d57def08ea6 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -792,12 +792,15 @@ struct xhci_tt_bw_info { /** * struct xhci_device_context_array - * @dev_context_ptr array of 64-bit DMA addresses for device contexts + * @ctx_array: Pointer to an array of addresses + * @dma: DMA address to @ctx_array + * + * Device Context Base Address Array (DCBAA) - Section 6.1. + * ctx_array[0]: Scratchpad Buffer Array Base Address + * ctx_array[1-MaxSlots]: Device Context Base Address */ struct xhci_device_context_array { - /* 64-bit device addresses; we only write 32-bit addresses */ - __le64 dev_context_ptrs[MAX_HC_SLOTS]; - /* private xHCD pointers */ + __le64 *ctx_array; dma_addr_t dma; }; /* @@ -1531,7 +1534,7 @@ struct xhci_hcd { /* optional reset controller */ struct reset_control *reset; /* data structures */ - struct xhci_device_context_array *dcbaa; + struct xhci_device_context_array dcbaa; struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; From 6ef46f3a013012e8b9943adac4d9e8780de9688e Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 3 Jun 2026 12:11:31 +0300 Subject: [PATCH 2698/5214] usb: xhci: allocate DCBAA based on host controller max slots Allocate the Device Context Base Address Array (DCBAA) according to the maximum number of device slots supported by the host controller, instead of always allocating the absolute maximum of 255 entries. The xHCI specification defines the DCBAA size as (MaxSlotsEnabled + 1) entries. In the xhci driver there is currently no distinction between MaxSlots and MaxSlotsEnabled, as all available slots are enabled during initialization. As a result, 'max_slots' effectively represents both values. This change allows the xHCI driver to respect custom slot limits, reduces unnecessary memory usage, and removes the obsolete "TODO" comment. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-15-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 6 +++--- drivers/usb/host/xhci-ring.c | 4 ++-- drivers/usb/host/xhci.h | 7 ++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 019dac47c5d6..939151e5440e 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1975,7 +1975,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) dcbaa = &xhci->dcbaa; if (dcbaa->ctx_array) { - dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS), + dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1), dcbaa->ctx_array, dcbaa->dma); dcbaa->ctx_array = NULL; } @@ -2411,8 +2411,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); - dcbaa->ctx_array = - dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS), + xhci->dcbaa.ctx_array = + dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1), &dcbaa->dma, flags); if (!dcbaa->ctx_array) goto fail; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3c304372bad3..4f98d8269625 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -609,7 +609,7 @@ static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index) { - if (slot_id == 0 || slot_id >= MAX_HC_SLOTS) { + if (slot_id == 0 || slot_id > xhci->max_slots) { xhci_warn(xhci, "Invalid slot_id %u\n", slot_id); return NULL; } @@ -1804,7 +1804,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_command *cmd; u32 cmd_type; - if (slot_id >= MAX_HC_SLOTS) { + if (slot_id > xhci->max_slots) { xhci_warn(xhci, "Invalid slot_id %u\n", slot_id); return; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9d57def08ea6..cb80acac97d4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -792,7 +792,8 @@ struct xhci_tt_bw_info { /** * struct xhci_device_context_array - * @ctx_array: Pointer to an array of addresses + * @ctx_array: Pointer to an array of addresses. The array size depends on Max + * Slots read from HCSPARAMS1. * @dma: DMA address to @ctx_array * * Device Context Base Address Array (DCBAA) - Section 6.1. @@ -803,10 +804,6 @@ struct xhci_device_context_array { __le64 *ctx_array; dma_addr_t dma; }; -/* - * TODO: change this to be dynamically sized at HC mem init time since the HC - * might not be able to handle the maximum number of devices possible. - */ struct xhci_transfer_event { From e0df1a7db3c0b77d5005928b7c6c48b97f7d072e Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 3 Jun 2026 12:11:32 +0300 Subject: [PATCH 2699/5214] usb: xhci: allocate internal DCBAA mirror dynamically Allocate the internal virtual device array dynamically based on the maximum number of slots reported by the host controller. Previously, the array was always allocated to the absolute maximum of 255 entries. Repurpose the 'MAX_HC_SLOTS' macro to limit the number of enabled slots. This mirrors how the maximum number of ports and interrupters are handled. The allocation now uses kcalloc_node(), which zeroes the memory automatically, making the explicit memset() call unnecessary. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-16-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 14 ++++++++++++-- drivers/usb/host/xhci.c | 4 +--- drivers/usb/host/xhci.h | 9 ++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 939151e5440e..a5e7f363922f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1947,8 +1947,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - for (i = xhci->max_slots; i > 0; i--) - xhci_free_virt_devices_depth_first(xhci, i); + if (xhci->devs) { + for (i = xhci->max_slots; i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); + kfree(xhci->devs); + } dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; @@ -2005,6 +2008,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->rh_bw = NULL; xhci->port_caps = NULL; xhci->interrupters = NULL; + xhci->devs = NULL; xhci->usb2_rhub.bus_state.bus_suspended = 0; xhci->usb3_rhub.bus_state.bus_suspended = 0; @@ -2411,6 +2415,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating internal virtual device array"); + xhci->devs = kcalloc_node(xhci->max_slots + 1, sizeof(*xhci->devs), flags, + dev_to_node(dev)); + if (!xhci->devs) + goto fail; + xhci->dcbaa.ctx_array = dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1), &dcbaa->dma, flags); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 14d7f866f220..6922cc5496c1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5457,7 +5457,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); - xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); + xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ if (!xhci->max_interrupters) @@ -5530,8 +5530,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) init_completion(&xhci->cmd_ring_stop_completion); xhci_hcd_page_size(xhci); - memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs)); - /* Allocate xHCI data structures */ retval = xhci_mem_init(xhci, GFP_KERNEL); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cb80acac97d4..d02046a573e4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -33,8 +33,11 @@ /* xHCI PCI Configuration Registers */ #define XHCI_SBRN_OFFSET (0x60) -/* Max number of USB devices for any host controller - limit in section 6.1 */ -#define MAX_HC_SLOTS 256 +/* + * Max number of Devices Slots. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 255. + */ +#define MAX_HC_SLOTS 255 /* * Max Number of Ports. xHCI specification section 5.3.3 * Valid values are in the range of 1 to 255. @@ -1551,7 +1554,7 @@ struct xhci_hcd { /* these are not thread safe so use mutex */ struct mutex mutex; /* Internal mirror of the HW's dcbaa */ - struct xhci_virt_device *devs[MAX_HC_SLOTS]; + struct xhci_virt_device **devs; /* For keeping track of bandwidth domains per roothub. */ struct xhci_root_port_bw_info *rh_bw; From a34c30412696867941d3e2d5f45b486d0e9c6cf8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 3 Jun 2026 17:17:09 +0200 Subject: [PATCH 2700/5214] usb: host: xhci-rcar: Remove SET_XHCI_PLAT_PRIV_FOR_RCAR() macro The SET_XHCI_PLAT_PRIV_FOR_RCAR() macro does not add much value (there are only two users), and stands in the way of handling differences between R-Car Gen2 and Gen3. Remove it. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/a7083c3c822837556b91d845bd449c099db64769.1780499433.git.geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-rcar.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 8a993ee21c87..d2efc98090ce 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -213,19 +213,20 @@ static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) * long delay for the handshake of STS_HALT is neeed in xhci_suspend() * by using the XHCI_SLOW_SUSPEND quirk. */ -#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ - .firmware_name = firmware, \ - .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, \ - .init_quirk = xhci_rcar_init_quirk, \ - .plat_start = xhci_rcar_start, \ - .resume_quirk = xhci_rcar_resume_quirk, - static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) + .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1, + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, + .init_quirk = xhci_rcar_init_quirk, + .plat_start = xhci_rcar_start, + .resume_quirk = xhci_rcar_resume_quirk, }; static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) + .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3, + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, + .init_quirk = xhci_rcar_init_quirk, + .plat_start = xhci_rcar_start, + .resume_quirk = xhci_rcar_resume_quirk, }; static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { From 511e746700ee6e93104d061a87743906128ab709 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 3 Jun 2026 17:17:10 +0200 Subject: [PATCH 2701/5214] usb: host: xhci-rcar: Split R-Car Gen2 and Gen3 .plat_start() handling Currently, R-Car Gen2 and Gen3 share the same .plat_start() callback. However, this single callback performs different operations, after checking the XHCI's controller compatible value. Avoid repeated checking of compatible values and reduce kernel size by splitting this method in two separate functions. Update xhci_rcar_resume_quirk() to dispatch to the correct method by calling it through the .plat_start() function pointer, too. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/d1ee4e1bb9106f8251b061b52948434d560b4675.1780499433.git.geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-rcar.c | 53 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index d2efc98090ce..d747c0905827 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -32,29 +32,6 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1); MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); -static void xhci_rcar_start_gen2(struct usb_hcd *hcd) -{ - /* LCLK Select */ - writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); - /* USB3.0 Configuration */ - writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); - writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); - writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); - /* USB3.0 Polarity */ - writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); - writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); -} - -static int xhci_rcar_is_gen2(struct device *dev) -{ - struct device_node *node = dev->of_node; - - return of_device_is_compatible(node, "renesas,xhci-r8a7790") || - of_device_is_compatible(node, "renesas,xhci-r8a7791") || - of_device_is_compatible(node, "renesas,xhci-r8a7793") || - of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); -} - static void xhci_rcar_start(struct usb_hcd *hcd) { u32 temp; @@ -64,8 +41,23 @@ static void xhci_rcar_start(struct usb_hcd *hcd) temp = readl(hcd->regs + RCAR_USB3_INT_ENA); temp |= RCAR_USB3_INT_ENA_VAL; writel(temp, hcd->regs + RCAR_USB3_INT_ENA); - if (xhci_rcar_is_gen2(hcd->self.controller)) - xhci_rcar_start_gen2(hcd); + } +} + +static void xhci_rcar_gen2_start(struct usb_hcd *hcd) +{ + if (hcd->regs != NULL) { + xhci_rcar_start(hcd); + + /* LCLK Select */ + writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); + /* USB3.0 Configuration */ + writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); + writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); + writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); + /* USB3.0 Polarity */ + writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); + writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); } } @@ -192,13 +184,16 @@ static int xhci_rcar_init_quirk(struct usb_hcd *hcd) static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) { + struct xhci_plat_priv *priv; int ret; ret = xhci_rcar_download_firmware(hcd); - if (!ret) - xhci_rcar_start(hcd); + if (ret) + return ret; - return ret; + priv = hcd_to_xhci_priv(hcd); + priv->plat_start(hcd); + return 0; } /* @@ -217,7 +212,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1, .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, .init_quirk = xhci_rcar_init_quirk, - .plat_start = xhci_rcar_start, + .plat_start = xhci_rcar_gen2_start, .resume_quirk = xhci_rcar_resume_quirk, }; From ae696dfa47c30016cd429b9db5e70b259b8f509e Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 23:11:38 +0500 Subject: [PATCH 2702/5214] iio: adc: nxp-sar-adc: harden buffer ISR against per-channel read failure nxp_sar_adc_isr_buffer() bails on the first channel-read failure without calling iio_trigger_notify_done(), so the trigger use_count is left incremented and iio_trigger_poll_chained() drops subsequent dispatches until the device is rebound. Reaching this path means a state machine has gone wrong (driver bug or the SAR ADC in an unexpected state) rather than a transient bus issue, so this is hardening rather than a bug fix. If the underlying condition persists the device is wedged and needs an unbind anyway. Call iio_trigger_notify_done() on the error exit too, matching the success path. The nxp_sar_adc_read_notify() duplication is intentional and avoids a goto label for a two-line bail-out, as suggested by David. Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 01f3da56e987..15c7432808f4 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -346,6 +346,7 @@ static void nxp_sar_adc_isr_buffer(struct iio_dev *indio_dev) ret = nxp_sar_adc_read_data(info, info->buffered_chan[i]); if (ret < 0) { nxp_sar_adc_read_notify(info); + iio_trigger_notify_done(indio_dev->trig); return; } From 81fbb909ec07868415f6b2269922c8d1cc6a215a Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:50 +0000 Subject: [PATCH 2703/5214] liveupdate: change file_set->count type to u64 for type safety This improves type safety and aligns the in-memory file_set->count with the serialized count type. It avoids potential truncation or sign conversion mismatch issues. Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-2-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index dd53d4a7277e..ae58206f14ac 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -52,7 +52,7 @@ static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, struct luo_file_set { struct list_head files_list; struct luo_file_ser *files; - long count; + u64 count; }; /** From 6af06e11bd48bdefaf9381f6ff0bd65b1e5d98ab Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:51 +0000 Subject: [PATCH 2704/5214] liveupdate: avoid mixing cleanup guards with goto in luo_session_retrieve_fd Refactoring luo_session_retrieve_fd() to avoid mixing automated cleanup-style guards with goto-based resource release, which is not recommended under the Linux kernel coding style. Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-3-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 5c6cebc6e326..47566db64598 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -291,10 +291,11 @@ static int luo_session_retrieve_fd(struct luo_session *session, if (argp->fd < 0) return argp->fd; - guard(mutex)(&session->mutex); + mutex_lock(&session->mutex); err = luo_retrieve_file(&session->file_set, argp->token, &file); + mutex_unlock(&session->mutex); if (err < 0) - goto err_put_fd; + goto err_put_fd; err = luo_ucmd_respond(ucmd, sizeof(*argp)); if (err) From d376e4b55c9a0adb3e701c7eaff21d9ba655a1c6 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:52 +0000 Subject: [PATCH 2705/5214] liveupdate: centralize state management into struct luo_ser Transition the LUO to ABI v2, which centralizes state management into a single struct luo_ser header. Previously, LUO state was spread across multiple FDT properties and subnodes. ABI v2 simplifies this by placing all core state, including the liveupdate number and physical addresses for sessions and FLB headers into a centralized struct luo_ser. Note that this change introduces a semantic difference: the sessions and FLB serialization formats are no longer completely independent of the core LUO. Their metadata (such as physical addresses for sessions and FLB headers) is now coupled to and managed via the centralized struct luo_ser. Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-4-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/luo.h | 91 +++++++++++--------------------- kernel/liveupdate/luo_core.c | 64 +++++++++++++++------- kernel/liveupdate/luo_flb.c | 60 +++------------------ kernel/liveupdate/luo_internal.h | 8 +-- kernel/liveupdate/luo_session.c | 64 ++++------------------ 5 files changed, 96 insertions(+), 191 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 46750a0ddf88..1b2f865a771a 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -30,52 +30,25 @@ * .. code-block:: none * * / { - * compatible = "luo-v1"; - * liveupdate-number = <...>; - * - * luo-session { - * compatible = "luo-session-v1"; - * luo-session-header = ; - * }; - * - * luo-flb { - * compatible = "luo-flb-v1"; - * luo-flb-header = ; - * }; + * compatible = "luo-v2"; + * luo-abi-header = ; * }; * * Main LUO Node (/): * - * - compatible: "luo-v1" + * - compatible: "luo-v2" * Identifies the overall LUO ABI version. - * - liveupdate-number: u64 - * A counter tracking the number of successful live updates performed. - * - * Session Node (luo-session): - * This node describes all preserved user-space sessions. - * - * - compatible: "luo-session-v1" - * Identifies the session ABI version. - * - luo-session-header: u64 - * The physical address of a `struct luo_session_header_ser`. This structure - * is the header for a contiguous block of memory containing an array of - * `struct luo_session_ser`, one for each preserved session. - * - * File-Lifecycle-Bound Node (luo-flb): - * This node describes all preserved global objects whose lifecycle is bound - * to that of the preserved files (e.g., shared IOMMU state). - * - * - compatible: "luo-flb-v1" - * Identifies the FLB ABI version. - * - luo-flb-header: u64 - * The physical address of a `struct luo_flb_header_ser`. This structure is - * the header for a contiguous block of memory containing an array of - * `struct luo_flb_ser`, one for each preserved global object. + * - luo-abi-header: u64 + * The physical address of `struct luo_ser`. * * Serialization Structures: * The FDT properties point to memory regions containing arrays of simple, * `__packed` structures. These structures contain the actual preserved state. * + * - struct luo_ser: + * The central ABI structure that contains the overall state of the LUO. + * It includes the liveupdate-number and pointers to sessions and FLBs. + * * - struct luo_session_header_ser: * Header for the session array. Contains the total page count of the * preserved memory block and the number of `struct luo_session_ser` @@ -109,13 +82,26 @@ /* * The LUO FDT hooks all LUO state for sessions, fds, etc. - * In the root it also carries "liveupdate-number" 64-bit property that - * corresponds to the number of live-updates performed on this machine. */ #define LUO_FDT_SIZE PAGE_SIZE #define LUO_FDT_KHO_ENTRY_NAME "LUO" -#define LUO_FDT_COMPATIBLE "luo-v1" -#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number" +#define LUO_FDT_COMPATIBLE "luo-v2" +#define LUO_FDT_ABI_HEADER "luo-abi-header" + +/** + * struct luo_ser - Centralized LUO ABI header. + * @liveupdate_num: A counter tracking the number of successful live updates. + * @sessions_pa: Physical address of the first session block header. + * @flbs_pa: Physical address of the FLB header. + * + * This structure is the root of all preserved LUO state. It is pointed to by + * the "luo-abi-header" property in the LUO FDT. + */ +struct luo_ser { + u64 liveupdate_num; + u64 sessions_pa; + u64 flbs_pa; +} __packed; #define LIVEUPDATE_HNDL_COMPAT_LENGTH 48 @@ -147,15 +133,6 @@ struct luo_file_set_ser { u64 count; } __packed; -/* - * LUO FDT session node - * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct - * luo_session_header_ser - */ -#define LUO_FDT_SESSION_NODE_NAME "luo-session" -#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v2" -#define LUO_FDT_SESSION_HEADER "luo-session-header" - /** * struct luo_session_header_ser - Header for the serialized session data block. * @count: The number of `struct luo_session_ser` entries that immediately @@ -165,7 +142,7 @@ struct luo_file_set_ser { * physical memory preserved across the kexec. It provides the necessary * metadata to interpret the array of session entries that follow. * - * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. */ struct luo_session_header_ser { u64 count; @@ -182,7 +159,7 @@ struct luo_session_header_ser { * session) is created and passed to the new kernel, allowing it to reconstruct * the session context. * - * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. */ struct luo_session_ser { char name[LIVEUPDATE_SESSION_NAME_LENGTH]; @@ -192,10 +169,6 @@ struct luo_session_ser { /* The max size is set so it can be reliably used during in serialization */ #define LIVEUPDATE_FLB_COMPAT_LENGTH 48 -#define LUO_FDT_FLB_NODE_NAME "luo-flb" -#define LUO_FDT_FLB_COMPATIBLE "luo-flb-v1" -#define LUO_FDT_FLB_HEADER "luo-flb-header" - /** * struct luo_flb_header_ser - Header for the serialized FLB data block. * @pgcnt: The total number of pages occupied by the entire preserved memory @@ -205,11 +178,9 @@ struct luo_session_ser { * in the memory block. * * This structure is located at the physical address specified by the - * `LUO_FDT_FLB_HEADER` FDT property. It provides the new kernel with the - * necessary information to find and iterate over the array of preserved - * File-Lifecycle-Bound objects and to manage the underlying memory. + * flbs_pa in luo_ser. * - * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated. + * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. */ struct luo_flb_header_ser { u64 pgcnt; @@ -231,7 +202,7 @@ struct luo_flb_header_ser { * passed to the new kernel. Each entry allows the LUO core to restore one * global, shared object. * - * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated. + * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. */ struct luo_flb_ser { char name[LIVEUPDATE_FLB_COMPAT_LENGTH]; diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 5d5827ced73c..085c0dfc1ef1 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -61,7 +61,6 @@ #include #include #include -#include #include "kexec_handover_internal.h" #include "luo_internal.h" @@ -86,9 +85,11 @@ early_param("liveupdate", early_liveupdate_param); static int __init luo_early_startup(void) { + struct luo_ser *luo_ser; + int err, header_size; phys_addr_t fdt_phys; - int err, ln_size; const void *ptr; + u64 luo_ser_pa; if (!kho_is_enabled()) { if (liveupdate_enabled()) @@ -119,26 +120,32 @@ static int __init luo_early_startup(void) return -EINVAL; } - ln_size = 0; - ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM, - &ln_size); - if (!ptr || ln_size != sizeof(luo_global.liveupdate_num)) { - pr_err("Unable to get live update number '%s' [%d]\n", - LUO_FDT_LIVEUPDATE_NUM, ln_size); + header_size = 0; + ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size); + if (!ptr || header_size != sizeof(u64)) { + pr_err("Unable to get ABI header '%s' [%d]\n", + LUO_FDT_ABI_HEADER, header_size); return -EINVAL; } - luo_global.liveupdate_num = get_unaligned((u64 *)ptr); + luo_ser_pa = get_unaligned((u64 *)ptr); + luo_ser = phys_to_virt(luo_ser_pa); + + luo_global.liveupdate_num = luo_ser->liveupdate_num; pr_info("Retrieved live update data, liveupdate number: %lld\n", luo_global.liveupdate_num); - err = luo_session_setup_incoming(luo_global.fdt_in); + err = luo_session_setup_incoming(luo_ser->sessions_pa); if (err) - return err; + goto out_free_ser; - err = luo_flb_setup_incoming(luo_global.fdt_in); + luo_flb_setup_incoming(luo_ser->flbs_pa); + err = 0; + +out_free_ser: + kho_restore_free(luo_ser); return err; } @@ -160,7 +167,8 @@ early_initcall(liveupdate_early_init); /* Called during boot to create outgoing LUO fdt tree */ static int __init luo_fdt_setup(void) { - const u64 ln = luo_global.liveupdate_num + 1; + struct luo_ser *luo_ser; + u64 luo_ser_pa; void *fdt_out; int err; @@ -170,27 +178,45 @@ static int __init luo_fdt_setup(void) return PTR_ERR(fdt_out); } + luo_ser = kho_alloc_preserve(sizeof(*luo_ser)); + if (IS_ERR(luo_ser)) { + err = PTR_ERR(luo_ser); + goto exit_free_fdt; + } + luo_ser_pa = virt_to_phys(luo_ser); + err = fdt_create(fdt_out, LUO_FDT_SIZE); err |= fdt_finish_reservemap(fdt_out); err |= fdt_begin_node(fdt_out, ""); err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); - err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); - err |= luo_session_setup_outgoing(fdt_out); - err |= luo_flb_setup_outgoing(fdt_out); + err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa, + sizeof(luo_ser_pa)); err |= fdt_end_node(fdt_out); err |= fdt_finish(fdt_out); if (err) - goto exit_free; + goto exit_free_luo_ser; + + err = luo_session_setup_outgoing(&luo_ser->sessions_pa); + if (err) + goto exit_free_luo_ser; + + err = luo_flb_setup_outgoing(&luo_ser->flbs_pa); + if (err) + goto exit_free_luo_ser; + + luo_ser->liveupdate_num = luo_global.liveupdate_num + 1; err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out, fdt_totalsize(fdt_out)); if (err) - goto exit_free; + goto exit_free_luo_ser; luo_global.fdt_out = fdt_out; return 0; -exit_free: +exit_free_luo_ser: + kho_unpreserve_free(luo_ser); +exit_free_fdt: kho_unpreserve_free(fdt_out); pr_err("failed to prepare LUO FDT: %d\n", err); diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index 8f5c5dd01cd0..5c27134ce7ba 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -44,13 +44,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include "luo_internal.h" #define LUO_FLB_PGCNT 1ul @@ -551,27 +549,15 @@ int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp) return 0; } -int __init luo_flb_setup_outgoing(void *fdt_out) +int __init luo_flb_setup_outgoing(u64 *flbs_pa) { struct luo_flb_header_ser *header_ser; - u64 header_ser_pa; - int err; header_ser = kho_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT); if (IS_ERR(header_ser)) return PTR_ERR(header_ser); - header_ser_pa = virt_to_phys(header_ser); - - err = fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME); - err |= fdt_property_string(fdt_out, "compatible", - LUO_FDT_FLB_COMPATIBLE); - err |= fdt_property(fdt_out, LUO_FDT_FLB_HEADER, &header_ser_pa, - sizeof(header_ser_pa)); - err |= fdt_end_node(fdt_out); - - if (err) - goto err_unpreserve; + *flbs_pa = virt_to_phys(header_ser); header_ser->pgcnt = LUO_FLB_PGCNT; luo_flb_global.outgoing.header_ser = header_ser; @@ -579,53 +565,19 @@ int __init luo_flb_setup_outgoing(void *fdt_out) luo_flb_global.outgoing.active = true; return 0; - -err_unpreserve: - kho_unpreserve_free(header_ser); - - return err; } -int __init luo_flb_setup_incoming(void *fdt_in) +void __init luo_flb_setup_incoming(u64 flbs_pa) { struct luo_flb_header_ser *header_ser; - int err, header_size, offset; - const void *ptr; - u64 header_ser_pa; - offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME); - if (offset < 0) { - pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME); - - return -ENOENT; - } - - err = fdt_node_check_compatible(fdt_in, offset, - LUO_FDT_FLB_COMPATIBLE); - if (err) { - pr_err("FLB node is incompatible with '%s' [%d]\n", - LUO_FDT_FLB_COMPATIBLE, err); - - return -EINVAL; - } - - header_size = 0; - ptr = fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEADER, &header_size); - if (!ptr || header_size != sizeof(u64)) { - pr_err("Unable to get FLB header property '%s' [%d]\n", - LUO_FDT_FLB_HEADER, header_size); - - return -EINVAL; - } - - header_ser_pa = get_unaligned((u64 *)ptr); - header_ser = phys_to_virt(header_ser_pa); + if (!flbs_pa) + return; + header_ser = phys_to_virt(flbs_pa); luo_flb_global.incoming.header_ser = header_ser; luo_flb_global.incoming.ser = (void *)(header_ser + 1); luo_flb_global.incoming.active = true; - - return 0; } /** diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index ae58206f14ac..fe22086bfbeb 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -79,8 +79,8 @@ extern struct rw_semaphore luo_register_rwlock; int luo_session_create(const char *name, struct file **filep); int luo_session_retrieve(const char *name, struct file **filep); -int __init luo_session_setup_outgoing(void *fdt); -int __init luo_session_setup_incoming(void *fdt); +int __init luo_session_setup_outgoing(u64 *sessions_pa); +int __init luo_session_setup_incoming(u64 sessions_pa); int luo_session_serialize(void); int luo_session_deserialize(void); @@ -102,8 +102,8 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh); void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh); void luo_flb_file_finish(struct liveupdate_file_handler *fh); void luo_flb_unregister_all(struct liveupdate_file_handler *fh); -int __init luo_flb_setup_outgoing(void *fdt); -int __init luo_flb_setup_incoming(void *fdt); +int __init luo_flb_setup_outgoing(u64 *flbs_pa); +void __init luo_flb_setup_incoming(u64 flbs_pa); void luo_flb_serialize(void); #ifdef CONFIG_LIVEUPDATE_TEST diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 47566db64598..85782c6f3d6c 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -25,9 +25,8 @@ * * - Serialization: Session metadata is preserved using the KHO framework. When * a live update is triggered via kexec, an array of `struct luo_session_ser` - * is populated and placed in a preserved memory region. An FDT node is also - * created, containing the count of sessions and the physical address of this - * array. + * is populated and placed in a preserved memory region. The physical address + * of this array is stored in the centralized `struct luo_ser` structure. * * Session Lifecycle: * @@ -91,13 +90,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include "luo_internal.h" @@ -527,75 +524,34 @@ int luo_session_retrieve(const char *name, struct file **filep) return err; } -int __init luo_session_setup_outgoing(void *fdt_out) +int __init luo_session_setup_outgoing(u64 *sessions_pa) { struct luo_session_header_ser *header_ser; - u64 header_ser_pa; - int err; header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); if (IS_ERR(header_ser)) return PTR_ERR(header_ser); - header_ser_pa = virt_to_phys(header_ser); - err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME); - err |= fdt_property_string(fdt_out, "compatible", - LUO_FDT_SESSION_COMPATIBLE); - err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa, - sizeof(header_ser_pa)); - err |= fdt_end_node(fdt_out); - - if (err) - goto err_unpreserve; + *sessions_pa = virt_to_phys(header_ser); luo_session_global.outgoing.header_ser = header_ser; luo_session_global.outgoing.ser = (void *)(header_ser + 1); luo_session_global.outgoing.active = true; return 0; - -err_unpreserve: - kho_unpreserve_free(header_ser); - return err; } -int __init luo_session_setup_incoming(void *fdt_in) +int __init luo_session_setup_incoming(u64 sessions_pa) { struct luo_session_header_ser *header_ser; - int err, header_size, offset; - u64 header_ser_pa; - const void *ptr; - offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME); - if (offset < 0) { - pr_err("Unable to get session node: [%s]\n", - LUO_FDT_SESSION_NODE_NAME); - return -EINVAL; + if (sessions_pa) { + header_ser = phys_to_virt(sessions_pa); + luo_session_global.incoming.header_ser = header_ser; + luo_session_global.incoming.ser = (void *)(header_ser + 1); + luo_session_global.incoming.active = true; } - err = fdt_node_check_compatible(fdt_in, offset, - LUO_FDT_SESSION_COMPATIBLE); - if (err) { - pr_err("Session node incompatible [%s]\n", - LUO_FDT_SESSION_COMPATIBLE); - return -EINVAL; - } - - header_size = 0; - ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size); - if (!ptr || header_size != sizeof(u64)) { - pr_err("Unable to get session header '%s' [%d]\n", - LUO_FDT_SESSION_HEADER, header_size); - return -EINVAL; - } - - header_ser_pa = get_unaligned((u64 *)ptr); - header_ser = phys_to_virt(header_ser_pa); - - luo_session_global.incoming.header_ser = header_ser; - luo_session_global.incoming.ser = (void *)(header_ser + 1); - luo_session_global.incoming.active = true; - return 0; } From cf071b3536df76a2a75b83ca1fe8c043824352c3 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:53 +0000 Subject: [PATCH 2706/5214] liveupdate: register luo_ser as KHO subtree Entirely remove the LUO FDT wrapper since the FDT only carries the compatible string and the pointer to the centralized struct luo_ser. Instead, register the struct luo_ser via the KHO raw subtree API, placing the compatibility string inside the structure itself. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-5-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/luo.h | 57 +++++++++---------------- kernel/liveupdate/luo_core.c | 83 +++++++++++------------------------- 2 files changed, 45 insertions(+), 95 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 1b2f865a771a..9a4fe491812b 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -10,11 +10,11 @@ * * Live Update Orchestrator uses the stable Application Binary Interface * defined below to pass state from a pre-update kernel to a post-update - * kernel. The ABI is built upon the Kexec HandOver framework and uses a - * Flattened Device Tree to describe the preserved data. + * kernel. The ABI is built upon the Kexec HandOver framework and registers + * the central `struct luo_ser` via the KHO raw subtree API. * - * This interface is a contract. Any modification to the FDT structure, node - * properties, compatible strings, or the layout of the `__packed` serialization + * This interface is a contract. Any modification to the structure fields, + * compatible strings, or the layout of the `__packed` serialization * structures defined here constitutes a breaking change. Such changes require * incrementing the version number in the relevant `_COMPATIBLE` string to * prevent a new kernel from misinterpreting data from an old kernel. @@ -23,31 +23,15 @@ * however, backward/forward compatibility is only guaranteed for kernels * supporting the same ABI version. * - * FDT Structure Overview: + * KHO Structure Overview: * The entire LUO state is encapsulated within a single KHO entry named "LUO". - * This entry contains an FDT with the following layout: - * - * .. code-block:: none - * - * / { - * compatible = "luo-v2"; - * luo-abi-header = ; - * }; - * - * Main LUO Node (/): - * - * - compatible: "luo-v2" - * Identifies the overall LUO ABI version. - * - luo-abi-header: u64 - * The physical address of `struct luo_ser`. + * This entry contains the `struct luo_ser` structure. * * Serialization Structures: - * The FDT properties point to memory regions containing arrays of simple, - * `__packed` structures. These structures contain the actual preserved state. - * * - struct luo_ser: * The central ABI structure that contains the overall state of the LUO. - * It includes the liveupdate-number and pointers to sessions and FLBs. + * It includes the compatibility string, the liveupdate-number, and pointers + * to sessions and FLBs. * * - struct luo_session_header_ser: * Header for the session array. Contains the total page count of the @@ -78,26 +62,27 @@ #ifndef _LINUX_KHO_ABI_LUO_H #define _LINUX_KHO_ABI_LUO_H +#include #include /* - * The LUO FDT hooks all LUO state for sessions, fds, etc. + * The LUO state is registered under this KHO entry name. */ -#define LUO_FDT_SIZE PAGE_SIZE -#define LUO_FDT_KHO_ENTRY_NAME "LUO" -#define LUO_FDT_COMPATIBLE "luo-v2" -#define LUO_FDT_ABI_HEADER "luo-abi-header" +#define LUO_KHO_ENTRY_NAME "LUO" +#define LUO_ABI_COMPATIBLE "luo-v3" +#define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8) /** * struct luo_ser - Centralized LUO ABI header. + * @compatible: Compatibility string identifying the LUO ABI version. * @liveupdate_num: A counter tracking the number of successful live updates. * @sessions_pa: Physical address of the first session block header. * @flbs_pa: Physical address of the FLB header. * - * This structure is the root of all preserved LUO state. It is pointed to by - * the "luo-abi-header" property in the LUO FDT. + * This structure is the root of all preserved LUO state. */ struct luo_ser { + char compatible[LUO_ABI_COMPAT_LEN]; u64 liveupdate_num; u64 sessions_pa; u64 flbs_pa; @@ -111,7 +96,7 @@ struct luo_ser { * @data: Private data * @token: User provided token for this file * - * If this structure is modified, LUO_SESSION_COMPATIBLE must be updated. + * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. */ struct luo_file_ser { char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH]; @@ -142,7 +127,7 @@ struct luo_file_set_ser { * physical memory preserved across the kexec. It provides the necessary * metadata to interpret the array of session entries that follow. * - * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. */ struct luo_session_header_ser { u64 count; @@ -159,7 +144,7 @@ struct luo_session_header_ser { * session) is created and passed to the new kernel, allowing it to reconstruct * the session context. * - * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. */ struct luo_session_ser { char name[LIVEUPDATE_SESSION_NAME_LENGTH]; @@ -180,7 +165,7 @@ struct luo_session_ser { * This structure is located at the physical address specified by the * flbs_pa in luo_ser. * - * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. */ struct luo_flb_header_ser { u64 pgcnt; @@ -202,7 +187,7 @@ struct luo_flb_header_ser { * passed to the new kernel. Each entry allows the LUO core to restore one * global, shared object. * - * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated. + * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. */ struct luo_flb_ser { char name[LIVEUPDATE_FLB_COMPAT_LENGTH]; diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 085c0dfc1ef1..69b00e7d0f8f 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -67,8 +66,7 @@ static struct { bool enabled; - void *fdt_out; - void *fdt_in; + struct luo_ser *luo_ser_out; u64 liveupdate_num; } luo_global; @@ -85,11 +83,10 @@ early_param("liveupdate", early_liveupdate_param); static int __init luo_early_startup(void) { + phys_addr_t luo_ser_phys; struct luo_ser *luo_ser; - int err, header_size; - phys_addr_t fdt_phys; - const void *ptr; - u64 luo_ser_pa; + size_t len; + int err; if (!kho_is_enabled()) { if (liveupdate_enabled()) @@ -98,40 +95,29 @@ static int __init luo_early_startup(void) return 0; } - /* Retrieve LUO subtree, and verify its format. */ - err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL); + /* Retrieve LUO state from KHO. */ + err = kho_retrieve_subtree(LUO_KHO_ENTRY_NAME, &luo_ser_phys, &len); if (err) { if (err != -ENOENT) { - pr_err("failed to retrieve FDT '%s' from KHO: %pe\n", - LUO_FDT_KHO_ENTRY_NAME, ERR_PTR(err)); + pr_err("failed to retrieve LUO state '%s' from KHO: %pe\n", + LUO_KHO_ENTRY_NAME, ERR_PTR(err)); return err; } return 0; } - luo_global.fdt_in = phys_to_virt(fdt_phys); - err = fdt_node_check_compatible(luo_global.fdt_in, 0, - LUO_FDT_COMPATIBLE); - if (err) { - pr_err("FDT '%s' is incompatible with '%s' [%d]\n", - LUO_FDT_KHO_ENTRY_NAME, LUO_FDT_COMPATIBLE, err); - + if (len < sizeof(*luo_ser)) { + pr_err("LUO state is too small (%zu < %zu)\n", len, sizeof(*luo_ser)); return -EINVAL; } - header_size = 0; - ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size); - if (!ptr || header_size != sizeof(u64)) { - pr_err("Unable to get ABI header '%s' [%d]\n", - LUO_FDT_ABI_HEADER, header_size); - + luo_ser = phys_to_virt(luo_ser_phys); + if (strncmp(luo_ser->compatible, LUO_ABI_COMPATIBLE, LUO_ABI_COMPAT_LEN)) { + pr_err("LUO state is incompatible with '%s'\n", LUO_ABI_COMPATIBLE); return -EINVAL; } - luo_ser_pa = get_unaligned((u64 *)ptr); - luo_ser = phys_to_virt(luo_ser_pa); - luo_global.liveupdate_num = luo_ser->liveupdate_num; pr_info("Retrieved live update data, liveupdate number: %lld\n", luo_global.liveupdate_num); @@ -164,37 +150,20 @@ static int __init liveupdate_early_init(void) } early_initcall(liveupdate_early_init); -/* Called during boot to create outgoing LUO fdt tree */ -static int __init luo_fdt_setup(void) +/* Called during boot to create outgoing LUO state */ +static int __init luo_state_setup(void) { struct luo_ser *luo_ser; - u64 luo_ser_pa; - void *fdt_out; int err; - fdt_out = kho_alloc_preserve(LUO_FDT_SIZE); - if (IS_ERR(fdt_out)) { - pr_err("failed to allocate/preserve FDT memory\n"); - return PTR_ERR(fdt_out); - } - luo_ser = kho_alloc_preserve(sizeof(*luo_ser)); if (IS_ERR(luo_ser)) { - err = PTR_ERR(luo_ser); - goto exit_free_fdt; + pr_err("failed to allocate/preserve LUO state memory\n"); + return PTR_ERR(luo_ser); } - luo_ser_pa = virt_to_phys(luo_ser); - err = fdt_create(fdt_out, LUO_FDT_SIZE); - err |= fdt_finish_reservemap(fdt_out); - err |= fdt_begin_node(fdt_out, ""); - err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); - err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa, - sizeof(luo_ser_pa)); - err |= fdt_end_node(fdt_out); - err |= fdt_finish(fdt_out); - if (err) - goto exit_free_luo_ser; + strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible)); + luo_ser->liveupdate_num = luo_global.liveupdate_num + 1; err = luo_session_setup_outgoing(&luo_ser->sessions_pa); if (err) @@ -204,21 +173,17 @@ static int __init luo_fdt_setup(void) if (err) goto exit_free_luo_ser; - luo_ser->liveupdate_num = luo_global.liveupdate_num + 1; - - err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out, - fdt_totalsize(fdt_out)); + err = kho_add_subtree(LUO_KHO_ENTRY_NAME, luo_ser, sizeof(*luo_ser)); if (err) goto exit_free_luo_ser; - luo_global.fdt_out = fdt_out; + + luo_global.luo_ser_out = luo_ser; return 0; exit_free_luo_ser: kho_unpreserve_free(luo_ser); -exit_free_fdt: - kho_unpreserve_free(fdt_out); - pr_err("failed to prepare LUO FDT: %d\n", err); + pr_err("failed to prepare LUO state: %d\n", err); return err; } @@ -234,7 +199,7 @@ static int __init luo_late_startup(void) if (!liveupdate_enabled()) return 0; - err = luo_fdt_setup(); + err = luo_state_setup(); if (err) luo_global.enabled = false; From 51b71af922a7145e63fdc0cab075d681ecd89e4a Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:54 +0000 Subject: [PATCH 2707/5214] liveupdate: Extract luo_file_deserialize_one helper Extract the logic for deserializing single entries for files into separate helper functions. In preparation to a linked-block serialization for files. This is a pure code movement, no other changes intended. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-6-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_file.c | 77 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 208987502f73..9eec07a9e9fc 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -753,6 +753,46 @@ int luo_file_finish(struct luo_file_set *file_set) return 0; } +static int luo_file_deserialize_one(struct luo_file_set *file_set, + struct luo_file_ser *ser) +{ + struct liveupdate_file_handler *fh; + bool handler_found = false; + struct luo_file *luo_file; + + down_read(&luo_register_rwlock); + list_private_for_each_entry(fh, &luo_file_handler_list, list) { + if (!strcmp(fh->compatible, ser->compatible)) { + if (try_module_get(fh->ops->owner)) + handler_found = true; + break; + } + } + up_read(&luo_register_rwlock); + + if (!handler_found) { + pr_warn("No registered handler for compatible '%.*s'\n", + (int)sizeof(ser->compatible), + ser->compatible); + return -ENOENT; + } + + luo_file = kzalloc_obj(*luo_file); + if (!luo_file) { + module_put(fh->ops->owner); + return -ENOMEM; + } + + luo_file->fh = fh; + luo_file->file = NULL; + luo_file->serialized_data = ser->data; + luo_file->token = ser->token; + mutex_init(&luo_file->mutex); + list_add_tail(&luo_file->list, &file_set->files_list); + + return 0; +} + /** * luo_file_deserialize - Reconstructs the list of preserved files in the new kernel. * @file_set: The incoming file_set to fill with deserialized data. @@ -782,6 +822,7 @@ int luo_file_deserialize(struct luo_file_set *file_set, struct luo_file_set_ser *file_set_ser) { struct luo_file_ser *file_ser; + int err; u64 i; if (!file_set_ser->files) { @@ -809,39 +850,9 @@ int luo_file_deserialize(struct luo_file_set *file_set, */ file_ser = file_set->files; for (i = 0; i < file_set->count; i++) { - struct liveupdate_file_handler *fh; - bool handler_found = false; - struct luo_file *luo_file; - - down_read(&luo_register_rwlock); - list_private_for_each_entry(fh, &luo_file_handler_list, list) { - if (!strcmp(fh->compatible, file_ser[i].compatible)) { - if (try_module_get(fh->ops->owner)) - handler_found = true; - break; - } - } - up_read(&luo_register_rwlock); - - if (!handler_found) { - pr_warn("No registered handler for compatible '%.*s'\n", - (int)sizeof(file_ser[i].compatible), - file_ser[i].compatible); - return -ENOENT; - } - - luo_file = kzalloc_obj(*luo_file); - if (!luo_file) { - module_put(fh->ops->owner); - return -ENOMEM; - } - - luo_file->fh = fh; - luo_file->file = NULL; - luo_file->serialized_data = file_ser[i].data; - luo_file->token = file_ser[i].token; - mutex_init(&luo_file->mutex); - list_add_tail(&luo_file->list, &file_set->files_list); + err = luo_file_deserialize_one(file_set, &file_ser[i]); + if (err) + return err; } return 0; From be9d10d167652e11283cd07c7daf187222808db1 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:55 +0000 Subject: [PATCH 2708/5214] liveupdate: Extract luo_session_deserialize_one helper Extract the logic for deserializing single entries for sessions into separate helper functions. In preparation to a linked-block serialization for sessions. This is a pure code movement, no other changes intended. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-7-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 63 +++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 85782c6f3d6c..1cd315e0f6de 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -555,6 +555,40 @@ int __init luo_session_setup_incoming(u64 sessions_pa) return 0; } +static int luo_session_deserialize_one(struct luo_session_header *sh, + struct luo_session_ser *ser) +{ + struct luo_session *session; + int err; + + session = luo_session_alloc(ser->name); + if (IS_ERR(session)) { + pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n", + (int)sizeof(ser->name), ser->name, session); + return PTR_ERR(session); + } + + err = luo_session_insert(sh, session); + if (err) { + pr_warn("Failed to insert session [%s] %pe\n", + session->name, ERR_PTR(err)); + luo_session_free(session); + return err; + } + + scoped_guard(mutex, &session->mutex) { + err = luo_file_deserialize(&session->file_set, + &ser->file_set_ser); + } + if (err) { + pr_warn("Failed to deserialize files for session [%s] %pe\n", + session->name, ERR_PTR(err)); + return err; + } + + return 0; +} + int luo_session_deserialize(void) { struct luo_session_header *sh = &luo_session_global.incoming; @@ -586,34 +620,9 @@ int luo_session_deserialize(void) * reliably reset devices and reclaim memory. */ for (int i = 0; i < sh->header_ser->count; i++) { - struct luo_session *session; - - session = luo_session_alloc(sh->ser[i].name); - if (IS_ERR(session)) { - pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n", - (int)sizeof(sh->ser[i].name), - sh->ser[i].name, session); - err = PTR_ERR(session); + err = luo_session_deserialize_one(sh, &sh->ser[i]); + if (err) goto save_err; - } - - err = luo_session_insert(sh, session); - if (err) { - pr_warn("Failed to insert session [%s] %pe\n", - session->name, ERR_PTR(err)); - luo_session_free(session); - goto save_err; - } - - scoped_guard(mutex, &session->mutex) { - err = luo_file_deserialize(&session->file_set, - &sh->ser[i].file_set_ser); - } - if (err) { - pr_warn("Failed to deserialize files for session [%s] %pe\n", - session->name, ERR_PTR(err)); - goto save_err; - } } kho_restore_free(sh->header_ser); From 0349ff2887059112ce06831ab29aec47a2a7285a Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:56 +0000 Subject: [PATCH 2709/5214] kho: add support for linked-block serialization Introduce a linked-block serialization mechanism for state handover. Previously, LUO used contiguous memory blocks for serializing sessions and files, which imposed limits on the total number of items that could be preserved across a live update. This commit adds the infrastructure for a more flexible, block-based approach where serialized data is stored in a chain of linked blocks. This is a generic KHO serialization block infrastructure that can be used by multiple subsystems. Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-8-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/core-api/kho/abi.rst | 5 + Documentation/core-api/kho/index.rst | 11 + MAINTAINERS | 1 + include/linux/kho/abi/block.h | 54 ++++ include/linux/kho/abi/kexec_handover.h | 2 +- include/linux/kho_block.h | 106 +++++++ kernel/liveupdate/Makefile | 1 + kernel/liveupdate/kho_block.c | 416 +++++++++++++++++++++++++ 8 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 include/linux/kho/abi/block.h create mode 100644 include/linux/kho_block.h create mode 100644 kernel/liveupdate/kho_block.c diff --git a/Documentation/core-api/kho/abi.rst b/Documentation/core-api/kho/abi.rst index 799d743105a6..edeb5b311963 100644 --- a/Documentation/core-api/kho/abi.rst +++ b/Documentation/core-api/kho/abi.rst @@ -28,6 +28,11 @@ KHO persistent memory tracker ABI .. kernel-doc:: include/linux/kho/abi/kexec_handover.h :doc: KHO persistent memory tracker +KHO serialization block ABI +=========================== + +.. kernel-doc:: include/linux/kho/abi/block.h + See Also ======== diff --git a/Documentation/core-api/kho/index.rst b/Documentation/core-api/kho/index.rst index 0a2dee4f8e7d..320914a42178 100644 --- a/Documentation/core-api/kho/index.rst +++ b/Documentation/core-api/kho/index.rst @@ -83,6 +83,17 @@ Public API .. kernel-doc:: kernel/liveupdate/kexec_handover.c :export: +KHO Serialization Blocks API +============================ + +.. kernel-doc:: kernel/liveupdate/kho_block.c + :doc: KHO Serialization Blocks + +.. kernel-doc:: include/linux/kho_block.h + +.. kernel-doc:: kernel/liveupdate/kho_block.c + :internal: + See Also ======== diff --git a/MAINTAINERS b/MAINTAINERS index 9ec290e38b44..920ba7622afa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14208,6 +14208,7 @@ F: Documentation/admin-guide/mm/kho.rst F: Documentation/core-api/kho/* F: include/linux/kexec_handover.h F: include/linux/kho/ +F: include/linux/kho_block.h F: kernel/liveupdate/kexec_handover* F: lib/test_kho.c F: tools/testing/selftests/kho/ diff --git a/include/linux/kho/abi/block.h b/include/linux/kho/abi/block.h new file mode 100644 index 000000000000..d06d64b963be --- /dev/null +++ b/include/linux/kho/abi/block.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: KHO Serialization Blocks ABI + * + * Subsystems using the KHO Serialization Blocks framework rely on the stable + * Application Binary Interface defined below to pass serialized state from a + * pre-update kernel to a post-update kernel. + * + * This interface is a contract. Any modification to the structure fields, + * compatible strings, or the layout of the `__packed` serialization + * structures defined here constitutes a breaking change. Such changes require + * incrementing the version number in the `KHO_FDT_COMPATIBLE` string to + * prevent a new kernel from misinterpreting data from an old kernel. + * + * Changes are allowed provided the compatibility version is incremented; + * however, backward/forward compatibility is only guaranteed for kernels + * supporting the same ABI version. + */ + +#ifndef _LINUX_KHO_ABI_BLOCK_H +#define _LINUX_KHO_ABI_BLOCK_H + +#include +#include + +/** + * KHO_BLOCK_SIZE - The size of each serialization block. + * + * This is defined as PAGE_SIZE. PAGE_SIZE is ABI compliant because live + * update between kernels with different page sizes is not supported by KHO. + */ +#define KHO_BLOCK_SIZE PAGE_SIZE + +/** + * struct kho_block_header_ser - Header for the serialized data block. + * @next: Physical address of the next struct kho_block_header_ser. + * @count: The number of entries that immediately follow this header in the + * memory block. + * + * This structure is located at the beginning of a block of physical memory + * preserved across a kexec. It provides the necessary metadata to interpret + * the array of entries that follow. + */ +struct kho_block_header_ser { + u64 next; + u64 count; +} __packed; + +#endif /* _LINUX_KHO_ABI_BLOCK_H */ diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h index fb2d37417ad9..5e2eb8519bda 100644 --- a/include/linux/kho/abi/kexec_handover.h +++ b/include/linux/kho/abi/kexec_handover.h @@ -90,7 +90,7 @@ */ /* The compatible string for the KHO FDT root node. */ -#define KHO_FDT_COMPATIBLE "kho-v3" +#define KHO_FDT_COMPATIBLE "kho-v4" /* The FDT property for the preserved memory map. */ #define KHO_FDT_MEMORY_MAP_PROP_NAME "preserved-memory-map" diff --git a/include/linux/kho_block.h b/include/linux/kho_block.h new file mode 100644 index 000000000000..93a7cc2be5f5 --- /dev/null +++ b/include/linux/kho_block.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + */ + +#ifndef _LINUX_KHO_BLOCK_H +#define _LINUX_KHO_BLOCK_H + +#include +#include +#include + +/** + * struct kho_block - Internal representation of a serialization block. + * @list: List head for linking blocks in memory. + * @ser: Pointer to the serialized header in preserved memory. + */ +struct kho_block { + struct list_head list; + struct kho_block_header_ser *ser; +}; + +/** + * struct kho_block_set - A set of blocks containing serialized entries of the same type. + * @blocks: The list of serialization blocks (struct kho_block). + * @nblocks: The number of allocated serialization blocks. + * @head_pa: Physical address of the first block header. + * @entry_size: The size of each entry in the blocks. + * @count_per_block: The maximum number of entries each block can hold. + * @incoming: True if this block set was restored from the previous kernel. + * + * Note: Synchronization and locking are the responsibility of the caller. + * The block set structure itself is not internally synchronized. + */ +struct kho_block_set { + struct list_head blocks; + long nblocks; + u64 head_pa; + size_t entry_size; + u64 count_per_block; + bool incoming; +}; + +/** + * struct kho_block_set_it - Iterator for serializing entries into blocks. + * @bs: The block set being iterated. + * @block: The current block. + * @i: The current entry index within @block. + */ +struct kho_block_set_it { + struct kho_block_set *bs; + struct kho_block *block; + u64 i; +}; + +/** + * KHO_BLOCK_SET_INIT - Initialize a static kho_block_set. + * @_name: Name of the kho_block_set variable. + * @_entry_size: The size of each entry in the block set. + */ +#define KHO_BLOCK_SET_INIT(_name, _entry_size) { \ + .blocks = LIST_HEAD_INIT((_name).blocks), \ + .entry_size = _entry_size, \ + .count_per_block = (KHO_BLOCK_SIZE - \ + sizeof(struct kho_block_header_ser)) / \ + (_entry_size), \ +} + +void kho_block_set_init(struct kho_block_set *bs, size_t entry_size); + +int kho_block_set_grow(struct kho_block_set *bs, u64 count); +void kho_block_set_shrink(struct kho_block_set *bs, u64 count); + +int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa); +void kho_block_set_destroy(struct kho_block_set *bs); +void kho_block_set_clear(struct kho_block_set *bs); + +/** + * kho_block_set_head_pa - Get the physical address of the first block header. + * @bs: The block set. + * + * Return: The physical address of the first block header, or 0 if empty. + */ +static inline u64 kho_block_set_head_pa(struct kho_block_set *bs) +{ + return bs->head_pa; +} + +/** + * kho_block_set_is_empty - Check if the block set has no allocated blocks. + * @bs: The block set. + * + * Return: True if there are no blocks in the set, false otherwise. + */ +static inline bool kho_block_set_is_empty(struct kho_block_set *bs) +{ + return list_empty(&bs->blocks); +} + +void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs); +void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it); +void *kho_block_set_it_read_entry(struct kho_block_set_it *it); +void *kho_block_set_it_prev(struct kho_block_set_it *it); + +#endif /* _LINUX_KHO_BLOCK_H */ diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index d2f779cbe279..eec9d3ae07eb 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 luo-y := \ + kho_block.o \ luo_core.o \ luo_file.o \ luo_flb.o \ diff --git a/kernel/liveupdate/kho_block.c b/kernel/liveupdate/kho_block.c new file mode 100644 index 000000000000..0d2a342ef422 --- /dev/null +++ b/kernel/liveupdate/kho_block.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: KHO Serialization Blocks + * + * KHO provides a mechanism to preserve stateful data across a kexec handover + * by serializing it into memory blocks, and provides the common + * infrastructure for managing these blocks. + * + * Each block consists of a header (struct kho_block_header_ser) followed by an + * array of serialized entries. Multiple blocks are linked together via a + * physical pointer in the header, forming a linked list that can be easily + * traversed in both the current and the next kernel. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +/* + * Safeguard limit for the number of serialization blocks. This is used to + * prevent infinite loops and excessive memory allocation in case of memory + * corruption in the preserved state. + * + * With a 4KB page size, 10k blocks is about 40MB. For 32-byte entries + * (e.g. 4 u64s), each block holds up to 127 entries (accounting for the + * 16-byte header), allowing the block set to hold up to 1.27M entries. + */ +#define KHO_MAX_BLOCKS 10000 + +/** + * kho_block_set_init - Initialize a block set. + * @bs: The block set to initialize. + * @entry_size: The size of each entry in the blocks. + */ +void kho_block_set_init(struct kho_block_set *bs, size_t entry_size) +{ + *bs = (struct kho_block_set)KHO_BLOCK_SET_INIT(*bs, entry_size); + WARN_ON_ONCE(!bs->count_per_block); +} + +/* Serialized entries start immediately after the block header */ +static void *kho_block_entries(struct kho_block *block) +{ + return (void *)(block->ser + 1); +} + +/* Get the address of the serialized entry at the specified index */ +static void *kho_block_entry(struct kho_block_set_it *it, u64 index) +{ + return kho_block_entries(it->block) + (index * it->bs->entry_size); +} + +/* Free serialized data */ +static void kho_block_free_ser(struct kho_block_set *bs, + struct kho_block_header_ser *ser) +{ + if (bs->incoming) + kho_restore_free(ser); + else + kho_unpreserve_free(ser); +} + +static struct kho_block_header_ser *kho_block_alloc_ser(struct kho_block_set *bs) +{ + WARN_ON_ONCE(bs->incoming); + return kho_alloc_preserve(KHO_BLOCK_SIZE); +} + +static int kho_block_add(struct kho_block_set *bs, + struct kho_block_header_ser *ser) +{ + struct kho_block *block, *last; + + if (bs->nblocks >= KHO_MAX_BLOCKS) + return -ENOSPC; + + block = kzalloc_obj(*block); + if (!block) + return -ENOMEM; + + block->ser = ser; + last = list_last_entry_or_null(&bs->blocks, struct kho_block, list); + list_add_tail(&block->list, &bs->blocks); + bs->nblocks++; + + if (last) + last->ser->next = virt_to_phys(ser); + else + bs->head_pa = virt_to_phys(ser); + + return 0; +} + +static int kho_block_set_grow_one(struct kho_block_set *bs) +{ + struct kho_block_header_ser *ser; + int err; + + ser = kho_block_alloc_ser(bs); + if (IS_ERR(ser)) + return PTR_ERR(ser); + + err = kho_block_add(bs, ser); + if (err) { + kho_block_free_ser(bs, ser); + return err; + } + + return 0; +} + +static void kho_block_set_shrink_one(struct kho_block_set *bs) +{ + struct kho_block *last, *new_last; + + if (list_empty(&bs->blocks)) + return; + + last = list_last_entry(&bs->blocks, struct kho_block, list); + list_del(&last->list); + bs->nblocks--; + kho_block_free_ser(bs, last->ser); + kfree(last); + + new_last = list_last_entry_or_null(&bs->blocks, struct kho_block, list); + if (new_last) + new_last->ser->next = 0; + else + bs->head_pa = 0; +} + +/** + * kho_block_set_grow - Expand the block set to accommodate the target count. + * @bs: The block set. + * @count: The target number of valid entries to accommodate. + * + * Dynamically preallocates and links preserved memory blocks if the target + * entry count exceeds the current total capacity of the set, ensuring they + * are available during serialization/deserialization. + * + * Context: Caller must hold a lock protecting the block set. + * Return: 0 on success, or a negative errno on failure. + */ +int kho_block_set_grow(struct kho_block_set *bs, u64 count) +{ + long orig_nblocks = bs->nblocks; + int err; + + if (WARN_ON_ONCE(bs->incoming)) + return -EINVAL; + + while (count > bs->nblocks * bs->count_per_block) { + err = kho_block_set_grow_one(bs); + if (err) + goto err_shrink; + } + + return 0; + +err_shrink: + while (bs->nblocks > orig_nblocks) + kho_block_set_shrink_one(bs); + return err; +} + +/** + * kho_block_set_shrink - Shrink the block set to accommodate the target count. + * @bs: The block set. + * @count: The target number of valid entries to accommodate. + * + * Releases and unallocates redundant preserved memory blocks. Checks if the + * last block in the set can be removed because the remaining entry count is + * fully accommodated by the preceding blocks. + * + * Note: It is the caller's responsibility to ensure that entries are removed + * in the reverse order of their insertion. Because shrinking destroys the last + * block in the set, removing entries in any other order would corrupt active + * data. + * + * Context: Caller must hold a lock protecting the block set. + */ +void kho_block_set_shrink(struct kho_block_set *bs, u64 count) +{ + while (bs->nblocks > 0 && count <= (bs->nblocks - 1) * bs->count_per_block) + kho_block_set_shrink_one(bs); +} + +/* + * kho_block_set_is_cyclic - Check for cycles in a linked list of blocks. + * Uses Floyd's cycle-finding algorithm to ensure sanity of the incoming list. + * + * Return: true if a cycle or corruption is detected, false otherwise. + */ +static bool kho_block_set_is_cyclic(struct kho_block_set *bs) +{ + struct kho_block_header_ser *fast; + struct kho_block_header_ser *slow; + int count = 0; + + fast = phys_to_virt(bs->head_pa); + slow = fast; + + while (fast) { + if (count++ >= KHO_MAX_BLOCKS) { + pr_err("Block set is corrupted\n"); + return true; + } + + if (!fast->next) + break; + + fast = phys_to_virt(fast->next); + if (!fast->next) + break; + + fast = phys_to_virt(fast->next); + slow = phys_to_virt(slow->next); + + if (slow == fast) { + pr_err("Block set is corrupted\n"); + return true; + } + } + + return false; +} + +/** + * kho_block_set_restore - Restore a block set from a physical address. + * @bs: The block set to restore. + * @head_pa: Physical address of the first block header. + * + * Restores a serialized block set from a given physical address. The caller is + * responsible for ensuring that the block set @bs has been allocated and + * initialized prior to calling this function. + * + * Return: 0 on success, or a negative errno on failure. + */ +int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa) +{ + struct kho_block_header_ser *ser; + u64 next_pa = head_pa; + int err; + + /* Restored block sets use size from the previous kernel */ + bs->incoming = true; + if (!head_pa) + return 0; + + bs->head_pa = head_pa; + if (kho_block_set_is_cyclic(bs)) { + bs->head_pa = 0; + return -EINVAL; + } + + while (next_pa) { + ser = phys_to_virt(next_pa); + if (!ser->count || ser->count > bs->count_per_block) { + pr_warn("Block contains invalid entry count: %llu\n", + ser->count); + err = -EINVAL; + goto err_destroy; + } + err = kho_block_add(bs, ser); + if (err) + goto err_destroy; + next_pa = ser->next; + } + + return 0; + +err_destroy: + kho_block_set_destroy(bs); + + /* Free the remaining un-restored blocks in the physical chain */ + while (next_pa) { + struct kho_block_header_ser *next_ser = phys_to_virt(next_pa); + + next_pa = next_ser->next; + kho_block_free_ser(bs, next_ser); + } + return err; +} + +/** + * kho_block_set_destroy - Destroy all blocks in a block set. + * @bs: The block set. + */ +void kho_block_set_destroy(struct kho_block_set *bs) +{ + struct kho_block *block, *tmp; + + list_for_each_entry_safe(block, tmp, &bs->blocks, list) { + list_del(&block->list); + kho_block_free_ser(bs, block->ser); + kfree(block); + } + bs->nblocks = 0; + bs->head_pa = 0; +} + +/** + * kho_block_set_clear - Clear all serialized data in a block set. + * @bs: The block set to clear. + */ +void kho_block_set_clear(struct kho_block_set *bs) +{ + struct kho_block *block; + + list_for_each_entry(block, &bs->blocks, list) { + block->ser->count = 0; + memset(block->ser + 1, 0, KHO_BLOCK_SIZE - sizeof(*block->ser)); + } +} + +/** + * kho_block_set_it_init - Initialize a block set iterator. + * @it: The iterator to initialize. + * @bs: The block set to iterate over. + */ +void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs) +{ + it->bs = bs; + it->block = list_first_entry_or_null(&bs->blocks, struct kho_block, list); + it->i = 0; +} + +/** + * kho_block_set_it_reserve_entry - Reserve and return the next available slot for writing. + * @it: The block iterator. + * + * Reserves a slot in the current block during state serialization to add a new + * entry, advancing the internal index. If the current block is full, it + * automatically moves to the next block in the set. + * + * Return: A pointer to the reserved entry slot, or NULL if the block set's + * capacity is fully exhausted. + */ +void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it) +{ + void *entry; + + if (!it->block) + return NULL; + + if (it->i == it->bs->count_per_block) { + if (list_is_last(&it->block->list, &it->bs->blocks)) + return NULL; + it->block = list_next_entry(it->block, list); + it->i = 0; + } + + entry = kho_block_entry(it, it->i++); + it->block->ser->count = it->i; + return entry; +} + +/** + * kho_block_set_it_read_entry - Read the next serialized entry from the block set. + * @it: The block iterator. + * + * Iterates through previously written entries during state deserialization, + * respecting the actual count stored in each block's header. + * + * Return: A pointer to the next serialized entry, or NULL if all serialized + * entries have been read. + */ +void *kho_block_set_it_read_entry(struct kho_block_set_it *it) +{ + if (!it->block) + return NULL; + + if (it->i == it->block->ser->count) { + if (list_is_last(&it->block->list, &it->bs->blocks)) + return NULL; + it->block = list_next_entry(it->block, list); + it->i = 0; + } + + return kho_block_entry(it, it->i++); +} + +/** + * kho_block_set_it_prev - Return the previous entry slot in the block set. + * @it: The block iterator. + * + * If the current index is at the start of a block, it automatically moves to + * the end of the previous block. + * + * Return: A pointer to the previous entry slot, or NULL if at the very + * beginning of the block set. + */ +void *kho_block_set_it_prev(struct kho_block_set_it *it) +{ + if (!it->block) + return NULL; + + if (it->i == 0) { + if (list_is_first(&it->block->list, &it->bs->blocks)) + return NULL; + it->block = list_prev_entry(it->block, list); + it->i = it->bs->count_per_block; + } + + return kho_block_entry(it, --it->i); +} From b5a58a922e6f2f9f40faddd8e0e1fe3ce0ea9c56 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:57 +0000 Subject: [PATCH 2710/5214] liveupdate: defer session block allocation and physical address setting Currently, luo_session_setup_outgoing() allocates the session block and sets its physical address in the header immediately. With upcoming dynamic block-based session management, this makes the first block different from the rest. Move the allocation to where it is first needed. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-9-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_core.c | 4 +- kernel/liveupdate/luo_internal.h | 2 +- kernel/liveupdate/luo_session.c | 68 ++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 69b00e7d0f8f..1b2bda22902d 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -165,9 +165,7 @@ static int __init luo_state_setup(void) strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible)); luo_ser->liveupdate_num = luo_global.liveupdate_num + 1; - err = luo_session_setup_outgoing(&luo_ser->sessions_pa); - if (err) - goto exit_free_luo_ser; + luo_session_setup_outgoing(&luo_ser->sessions_pa); err = luo_flb_setup_outgoing(&luo_ser->flbs_pa); if (err) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index fe22086bfbeb..ee18f9a11b91 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -79,7 +79,7 @@ extern struct rw_semaphore luo_register_rwlock; int luo_session_create(const char *name, struct file **filep); int luo_session_retrieve(const char *name, struct file **filep); -int __init luo_session_setup_outgoing(u64 *sessions_pa); +void __init luo_session_setup_outgoing(u64 *sessions_pa); int __init luo_session_setup_incoming(u64 sessions_pa); int luo_session_serialize(void); int luo_session_deserialize(void); diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 1cd315e0f6de..2411849a34e3 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -108,15 +108,16 @@ static DECLARE_RWSEM(luo_session_serialize_rwsem); /** * struct luo_session_header - Header struct for managing LUO sessions. - * @count: The number of sessions currently tracked in the @list. - * @list: The head of the linked list of `struct luo_session` instances. - * @rwsem: A read-write semaphore providing synchronized access to the - * session list and other fields in this structure. - * @header_ser: The header data of serialization array. - * @ser: The serialized session data (an array of - * `struct luo_session_ser`). - * @active: Set to true when first initialized. If previous kernel did not - * send session data, active stays false for incoming. + * @count: The number of sessions currently tracked in the @list. + * @list: The head of the linked list of `struct luo_session` instances. + * @rwsem: A read-write semaphore providing synchronized access to the + * session list and other fields in this structure. + * @header_ser: The header data of serialization array. + * @ser: The serialized session data (an array of + * `struct luo_session_ser`). + * @sessions_pa: Points to the location of sessions_pa within struct luo_ser. + * @active: Set to true when first initialized. If previous kernel did not + * send session data, active stays false for incoming. */ struct luo_session_header { long count; @@ -124,6 +125,7 @@ struct luo_session_header { struct rw_semaphore rwsem; struct luo_session_header_ser *header_ser; struct luo_session_ser *ser; + u64 *sessions_pa; bool active; }; @@ -171,10 +173,30 @@ static void luo_session_free(struct luo_session *session) kfree(session); } +static int luo_session_grow_ser(struct luo_session_header *sh) +{ + struct luo_session_header_ser *header_ser; + + if (sh->count == LUO_SESSION_MAX) + return -ENOMEM; + + if (sh->header_ser) + return 0; + + header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); + if (IS_ERR(header_ser)) + return PTR_ERR(header_ser); + + sh->header_ser = header_ser; + sh->ser = (void *)(header_ser + 1); + return 0; +} + static int luo_session_insert(struct luo_session_header *sh, struct luo_session *session) { struct luo_session *it; + int err; guard(rwsem_write)(&sh->rwsem); @@ -183,8 +205,9 @@ static int luo_session_insert(struct luo_session_header *sh, * for new session. */ if (sh == &luo_session_global.outgoing) { - if (sh->count == LUO_SESSION_MAX) - return -ENOMEM; + err = luo_session_grow_ser(sh); + if (err) + return err; } /* @@ -524,21 +547,10 @@ int luo_session_retrieve(const char *name, struct file **filep) return err; } -int __init luo_session_setup_outgoing(u64 *sessions_pa) +void __init luo_session_setup_outgoing(u64 *sessions_pa) { - struct luo_session_header_ser *header_ser; - - header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); - if (IS_ERR(header_ser)) - return PTR_ERR(header_ser); - - *sessions_pa = virt_to_phys(header_ser); - - luo_session_global.outgoing.header_ser = header_ser; - luo_session_global.outgoing.ser = (void *)(header_ser + 1); + luo_session_global.outgoing.sessions_pa = sessions_pa; luo_session_global.outgoing.active = true; - - return 0; } int __init luo_session_setup_incoming(u64 sessions_pa) @@ -644,6 +656,8 @@ int luo_session_serialize(void) down_write(&luo_session_serialize_rwsem); down_write(&sh->rwsem); + *sh->sessions_pa = 0; + list_for_each_entry(session, &sh->list, list) { err = luo_session_freeze_one(session, &sh->ser[i]); if (err) @@ -653,7 +667,11 @@ int luo_session_serialize(void) sizeof(sh->ser[i].name)); i++; } - sh->header_ser->count = sh->count; + + if (sh->header_ser && sh->count > 0) { + sh->header_ser->count = sh->count; + *sh->sessions_pa = virt_to_phys(sh->header_ser); + } up_write(&sh->rwsem); return 0; From 2a441a14c2c03b39d1c89438dd28cef9d8fa57d5 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:58 +0000 Subject: [PATCH 2711/5214] liveupdate: Remove limit on the number of sessions Currently, the number of LUO sessions is limited by a fixed number of pre-allocated pages for serialization (16 pages, allowing for ~819 sessions). This limitation is problematic if LUO is used to support things such as systemd file descriptor store, and would be used not just as VM memory but to save other states on the machine. Remove this limit by transitioning to a linked-block approach for session metadata serialization. Instead of a single contiguous block, session metadata is now stored in a chain of 16-page blocks. Each block starts with a header containing the physical address of the next block and the number of session entries in the current block. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-10-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/luo.h | 23 +------ kernel/liveupdate/luo_session.c | 113 +++++++++++++++----------------- 2 files changed, 55 insertions(+), 81 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 9a4fe491812b..03d940d0f9bb 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -33,11 +33,6 @@ * It includes the compatibility string, the liveupdate-number, and pointers * to sessions and FLBs. * - * - struct luo_session_header_ser: - * Header for the session array. Contains the total page count of the - * preserved memory block and the number of `struct luo_session_ser` - * entries that follow. - * * - struct luo_session_ser: * Metadata for a single session, including its name and a physical pointer * to another preserved memory block containing an array of @@ -63,13 +58,14 @@ #define _LINUX_KHO_ABI_LUO_H #include +#include #include /* * The LUO state is registered under this KHO entry name. */ #define LUO_KHO_ENTRY_NAME "LUO" -#define LUO_ABI_COMPATIBLE "luo-v3" +#define LUO_ABI_COMPATIBLE "luo-v4" #define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8) /** @@ -118,21 +114,6 @@ struct luo_file_set_ser { u64 count; } __packed; -/** - * struct luo_session_header_ser - Header for the serialized session data block. - * @count: The number of `struct luo_session_ser` entries that immediately - * follow this header in the memory block. - * - * This structure is located at the beginning of a contiguous block of - * physical memory preserved across the kexec. It provides the necessary - * metadata to interpret the array of session entries that follow. - * - * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated. - */ -struct luo_session_header_ser { - u64 count; -} __packed; - /** * struct luo_session_ser - Represents the serialized metadata for a LUO session. * @name: The unique name of the session, provided by the userspace at diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 2411849a34e3..b79b2a488974 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -24,9 +24,10 @@ * ioctls on /dev/liveupdate. * * - Serialization: Session metadata is preserved using the KHO framework. When - * a live update is triggered via kexec, an array of `struct luo_session_ser` - * is populated and placed in a preserved memory region. The physical address - * of this array is stored in the centralized `struct luo_ser` structure. + * a live update is triggered via kexec, session metadata is serialized into + * a chain of linked-blocks and placed in a preserved memory region. The + * physical address of the first block header is stored in the centralized + * `struct luo_ser` structure. * * Session Lifecycle: * @@ -89,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -98,23 +100,14 @@ #include #include "luo_internal.h" -/* 16 4K pages, give space for 744 sessions */ -#define LUO_SESSION_PGCNT 16ul -#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ - sizeof(struct luo_session_header_ser)) / \ - sizeof(struct luo_session_ser)) - static DECLARE_RWSEM(luo_session_serialize_rwsem); - /** * struct luo_session_header - Header struct for managing LUO sessions. * @count: The number of sessions currently tracked in the @list. * @list: The head of the linked list of `struct luo_session` instances. * @rwsem: A read-write semaphore providing synchronized access to the * session list and other fields in this structure. - * @header_ser: The header data of serialization array. - * @ser: The serialized session data (an array of - * `struct luo_session_ser`). + * @block_set: The set of serialization blocks. * @sessions_pa: Points to the location of sessions_pa within struct luo_ser. * @active: Set to true when first initialized. If previous kernel did not * send session data, active stays false for incoming. @@ -123,8 +116,7 @@ struct luo_session_header { long count; struct list_head list; struct rw_semaphore rwsem; - struct luo_session_header_ser *header_ser; - struct luo_session_ser *ser; + struct kho_block_set block_set; u64 *sessions_pa; bool active; }; @@ -143,10 +135,14 @@ static struct luo_session_global luo_session_global = { .incoming = { .list = LIST_HEAD_INIT(luo_session_global.incoming.list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), + .block_set = KHO_BLOCK_SET_INIT(luo_session_global.incoming.block_set, + sizeof(struct luo_session_ser)), }, .outgoing = { .list = LIST_HEAD_INIT(luo_session_global.outgoing.list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), + .block_set = KHO_BLOCK_SET_INIT(luo_session_global.outgoing.block_set, + sizeof(struct luo_session_ser)), }, }; @@ -173,25 +169,6 @@ static void luo_session_free(struct luo_session *session) kfree(session); } -static int luo_session_grow_ser(struct luo_session_header *sh) -{ - struct luo_session_header_ser *header_ser; - - if (sh->count == LUO_SESSION_MAX) - return -ENOMEM; - - if (sh->header_ser) - return 0; - - header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); - if (IS_ERR(header_ser)) - return PTR_ERR(header_ser); - - sh->header_ser = header_ser; - sh->ser = (void *)(header_ser + 1); - return 0; -} - static int luo_session_insert(struct luo_session_header *sh, struct luo_session *session) { @@ -205,7 +182,7 @@ static int luo_session_insert(struct luo_session_header *sh, * for new session. */ if (sh == &luo_session_global.outgoing) { - err = luo_session_grow_ser(sh); + err = kho_block_set_grow(&sh->block_set, sh->count + 1); if (err) return err; } @@ -232,6 +209,8 @@ static void luo_session_remove(struct luo_session_header *sh, guard(rwsem_write)(&sh->rwsem); list_del(&session->list); sh->count--; + if (sh == &luo_session_global.outgoing) + kho_block_set_shrink(&sh->block_set, sh->count); } static int luo_session_finish_one(struct luo_session *session) @@ -555,15 +534,17 @@ void __init luo_session_setup_outgoing(u64 *sessions_pa) int __init luo_session_setup_incoming(u64 sessions_pa) { - struct luo_session_header_ser *header_ser; + struct luo_session_header *sh = &luo_session_global.incoming; + int err; - if (sessions_pa) { - header_ser = phys_to_virt(sessions_pa); - luo_session_global.incoming.header_ser = header_ser; - luo_session_global.incoming.ser = (void *)(header_ser + 1); - luo_session_global.incoming.active = true; - } + if (!sessions_pa) + return 0; + err = kho_block_set_restore(&sh->block_set, sessions_pa); + if (err) + return err; + + sh->active = true; return 0; } @@ -605,6 +586,8 @@ int luo_session_deserialize(void) { struct luo_session_header *sh = &luo_session_global.incoming; static bool is_deserialized; + struct luo_session_ser *ser; + struct kho_block_set_it it; static int saved_err; int err; @@ -631,18 +614,19 @@ int luo_session_deserialize(void) * userspace to detect the failure and trigger a reboot, which will * reliably reset devices and reclaim memory. */ - for (int i = 0; i < sh->header_ser->count; i++) { - err = luo_session_deserialize_one(sh, &sh->ser[i]); + kho_block_set_it_init(&it, &sh->block_set); + while ((ser = kho_block_set_it_read_entry(&it))) { + err = luo_session_deserialize_one(sh, ser); if (err) goto save_err; } - kho_restore_free(sh->header_ser); - sh->header_ser = NULL; - sh->ser = NULL; + kho_block_set_destroy(&sh->block_set); return 0; + save_err: + kho_block_set_destroy(&sh->block_set); saved_err = err; return err; } @@ -651,36 +635,45 @@ int luo_session_serialize(void) { struct luo_session_header *sh = &luo_session_global.outgoing; struct luo_session *session; - int i = 0; + struct kho_block_set_it it; int err; down_write(&luo_session_serialize_rwsem); down_write(&sh->rwsem); *sh->sessions_pa = 0; + kho_block_set_it_init(&it, &sh->block_set); + list_for_each_entry(session, &sh->list, list) { - err = luo_session_freeze_one(session, &sh->ser[i]); - if (err) + struct luo_session_ser *ser = kho_block_set_it_reserve_entry(&it); + + /* This should not fail normally as blocks were pre-allocated */ + if (WARN_ON_ONCE(!ser)) { + err = -ENOSPC; goto err_undo; + } - strscpy(sh->ser[i].name, session->name, - sizeof(sh->ser[i].name)); - i++; + err = luo_session_freeze_one(session, ser); + if (err) { + kho_block_set_it_prev(&it); + goto err_undo; + } + + strscpy(ser->name, session->name, sizeof(ser->name)); } - if (sh->header_ser && sh->count > 0) { - sh->header_ser->count = sh->count; - *sh->sessions_pa = virt_to_phys(sh->header_ser); - } + if (sh->count > 0) + *sh->sessions_pa = kho_block_set_head_pa(&sh->block_set); up_write(&sh->rwsem); return 0; err_undo: list_for_each_entry_continue_reverse(session, &sh->list, list) { - i--; - luo_session_unfreeze_one(session, &sh->ser[i]); - memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name)); + struct luo_session_ser *ser = kho_block_set_it_prev(&it); + + luo_session_unfreeze_one(session, ser); + memset(ser->name, 0, sizeof(ser->name)); } up_write(&sh->rwsem); up_write(&luo_session_serialize_rwsem); From 1d1153097f4dd417e2ea00404edec9fbd1d88f28 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:43:59 +0000 Subject: [PATCH 2712/5214] liveupdate: Remove limit on the number of files per session To remove the fixed limit on the number of preserved files per session, transition the file metadata serialization from a single contiguous memory block to a chain of linked blocks. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-11-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/luo.h | 13 +-- kernel/liveupdate/luo_file.c | 138 ++++++++++++++----------------- kernel/liveupdate/luo_internal.h | 6 +- 3 files changed, 74 insertions(+), 83 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 03d940d0f9bb..288076de6d4a 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -35,8 +35,8 @@ * * - struct luo_session_ser: * Metadata for a single session, including its name and a physical pointer - * to another preserved memory block containing an array of - * `struct luo_file_ser` for all files in that session. + * to the first `struct kho_block_header_ser` for all files in that session. + * Multiple blocks are linked via the `next` field in the header. * * - struct luo_file_ser: * Metadata for a single preserved file. Contains the `compatible` string to @@ -65,7 +65,7 @@ * The LUO state is registered under this KHO entry name. */ #define LUO_KHO_ENTRY_NAME "LUO" -#define LUO_ABI_COMPATIBLE "luo-v4" +#define LUO_ABI_COMPATIBLE "luo-v5" #define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8) /** @@ -102,9 +102,10 @@ struct luo_file_ser { /** * struct luo_file_set_ser - Represents the serialized metadata for file set - * @files: The physical address of a contiguous memory block that holds - * the serialized state of files (array of luo_file_ser) in this file - * set. + * @files: The physical address of the first `struct kho_block_header_ser`. + * This structure is the header for a block of memory containing + * an array of `struct luo_file_ser` entries. Multiple blocks are + * linked via the `next` field in the header. * @count: The total number of files that were part of this session during * serialization. Used for iteration and validation during * restoration. diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 9eec07a9e9fc..c39f96961a85 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -118,11 +118,6 @@ static LIST_HEAD(luo_file_handler_list); /* Keep track of files being preserved by LUO */ static DEFINE_XARRAY(luo_preserved_files); -/* 2 4K pages, give space for 128 files per file_set */ -#define LUO_FILE_PGCNT 2ul -#define LUO_FILE_MAX \ - ((LUO_FILE_PGCNT << PAGE_SHIFT) / sizeof(struct luo_file_ser)) - /** * struct luo_file - Represents a single preserved file instance. * @fh: Pointer to the &struct liveupdate_file_handler that manages @@ -174,39 +169,6 @@ struct luo_file { u64 token; }; -static int luo_alloc_files_mem(struct luo_file_set *file_set) -{ - size_t size; - void *mem; - - if (file_set->files) - return 0; - - WARN_ON_ONCE(file_set->count); - - size = LUO_FILE_PGCNT << PAGE_SHIFT; - mem = kho_alloc_preserve(size); - if (IS_ERR(mem)) - return PTR_ERR(mem); - - file_set->files = mem; - - return 0; -} - -static void luo_free_files_mem(struct luo_file_set *file_set) -{ - /* If file_set has files, no need to free preservation memory */ - if (file_set->count) - return; - - if (!file_set->files) - return; - - kho_unpreserve_free(file_set->files); - file_set->files = NULL; -} - static unsigned long luo_get_id(struct liveupdate_file_handler *fh, struct file *file) { @@ -276,16 +238,15 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) if (luo_token_is_used(file_set, token)) return -EEXIST; - if (file_set->count == LUO_FILE_MAX) - return -ENOSPC; + err = kho_block_set_grow(&file_set->block_set, file_set->count + 1); + if (err) + return err; file = fget(fd); - if (!file) - return -EBADF; - - err = luo_alloc_files_mem(file_set); - if (err) - goto err_fput; + if (!file) { + err = -EBADF; + goto err_shrink; + } err = -ENOENT; down_read(&luo_register_rwlock); @@ -300,7 +261,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) /* err is still -ENOENT if no handler was found */ if (err) - goto err_free_files_mem; + goto err_fput; err = xa_insert(&luo_preserved_files, luo_get_id(fh, file), file, GFP_KERNEL); @@ -343,10 +304,10 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) xa_erase(&luo_preserved_files, luo_get_id(fh, file)); err_module_put: module_put(fh->ops->owner); -err_free_files_mem: - luo_free_files_mem(file_set); err_fput: fput(file); +err_shrink: + kho_block_set_shrink(&file_set->block_set, file_set->count); return err; } @@ -392,13 +353,14 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set) list_del(&luo_file->list); file_set->count--; + kho_block_set_shrink(&file_set->block_set, file_set->count); fput(luo_file->file); mutex_destroy(&luo_file->mutex); kfree(luo_file); } - luo_free_files_mem(file_set); + kho_block_set_destroy(&file_set->block_set); } static int luo_file_freeze_one(struct luo_file_set *file_set, @@ -454,7 +416,7 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set, luo_file_unfreeze_one(file_set, luo_file); } - memset(file_set->files, 0, LUO_FILE_PGCNT << PAGE_SHIFT); + kho_block_set_clear(&file_set->block_set); } /** @@ -493,19 +455,24 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set, int luo_file_freeze(struct luo_file_set *file_set, struct luo_file_set_ser *file_set_ser) { - struct luo_file_ser *file_ser = file_set->files; struct luo_file *luo_file; + struct kho_block_set_it it; int err; - int i; if (!file_set->count) return 0; - if (WARN_ON(!file_ser)) - return -EINVAL; + kho_block_set_it_init(&it, &file_set->block_set); - i = 0; list_for_each_entry(luo_file, &file_set->files_list, list) { + struct luo_file_ser *file_ser = kho_block_set_it_reserve_entry(&it); + + /* This should not fail normally as blocks were pre-allocated */ + if (WARN_ON_ONCE(!file_ser)) { + err = -ENOSPC; + goto err_unfreeze; + } + err = luo_file_freeze_one(file_set, luo_file); if (err < 0) { pr_warn("Freeze failed for token[%#0llx] handler[%s] err[%pe]\n", @@ -514,16 +481,14 @@ int luo_file_freeze(struct luo_file_set *file_set, goto err_unfreeze; } - strscpy(file_ser[i].compatible, luo_file->fh->compatible, - sizeof(file_ser[i].compatible)); - file_ser[i].data = luo_file->serialized_data; - file_ser[i].token = luo_file->token; - i++; + strscpy(file_ser->compatible, luo_file->fh->compatible, + sizeof(file_ser->compatible)); + file_ser->data = luo_file->serialized_data; + file_ser->token = luo_file->token; } file_set_ser->count = file_set->count; - if (file_set->files) - file_set_ser->files = virt_to_phys(file_set->files); + file_set_ser->files = kho_block_set_head_pa(&file_set->block_set); return 0; @@ -741,14 +706,12 @@ int luo_file_finish(struct luo_file_set *file_set) module_put(luo_file->fh->ops->owner); list_del(&luo_file->list); file_set->count--; + kho_block_set_shrink(&file_set->block_set, file_set->count); mutex_destroy(&luo_file->mutex); kfree(luo_file); } - if (file_set->files) { - kho_restore_free(file_set->files); - file_set->files = NULL; - } + kho_block_set_destroy(&file_set->block_set); return 0; } @@ -822,16 +785,18 @@ int luo_file_deserialize(struct luo_file_set *file_set, struct luo_file_set_ser *file_set_ser) { struct luo_file_ser *file_ser; + struct kho_block_set_it it; int err; - u64 i; if (!file_set_ser->files) { WARN_ON(file_set_ser->count); return 0; } - file_set->count = file_set_ser->count; - file_set->files = phys_to_virt(file_set_ser->files); + file_set->count = 0; + err = kho_block_set_restore(&file_set->block_set, file_set_ser->files); + if (err) + return err; /* * Note on error handling: @@ -848,25 +813,50 @@ int luo_file_deserialize(struct luo_file_set *file_set, * userspace to detect the failure and trigger a reboot, which will * reliably reset devices and reclaim memory. */ - file_ser = file_set->files; - for (i = 0; i < file_set->count; i++) { - err = luo_file_deserialize_one(file_set, &file_ser[i]); + kho_block_set_it_init(&it, &file_set->block_set); + while ((file_ser = kho_block_set_it_read_entry(&it))) { + err = luo_file_deserialize_one(file_set, file_ser); if (err) - return err; + goto err_destroy_blocks; + file_set->count++; + } + + if (file_set->count != file_set_ser->count) { + pr_warn("File count mismatch: expected %llu, found %llu\n", + file_set_ser->count, file_set->count); + err = -EINVAL; + goto err_destroy_blocks; } return 0; + +err_destroy_blocks: + while (!list_empty(&file_set->files_list)) { + struct luo_file *luo_file; + + luo_file = list_first_entry(&file_set->files_list, + struct luo_file, list); + list_del(&luo_file->list); + module_put(luo_file->fh->ops->owner); + mutex_destroy(&luo_file->mutex); + kfree(luo_file); + } + file_set->count = 0; + kho_block_set_destroy(&file_set->block_set); + return err; } void luo_file_set_init(struct luo_file_set *file_set) { INIT_LIST_HEAD(&file_set->files_list); + kho_block_set_init(&file_set->block_set, sizeof(struct luo_file_ser)); } void luo_file_set_destroy(struct luo_file_set *file_set) { WARN_ON(file_set->count); WARN_ON(!list_empty(&file_set->files_list)); + WARN_ON(!kho_block_set_is_empty(&file_set->block_set)); } /** diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index ee18f9a11b91..64879ffe7378 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -10,6 +10,7 @@ #include #include +#include struct luo_ucmd { void __user *ubuffer; @@ -44,14 +45,13 @@ static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, * struct luo_file_set - A set of files that belong to the same sessions. * @files_list: An ordered list of files associated with this session, it is * ordered by preservation time. - * @files: The physically contiguous memory block that holds the serialized - * state of files. + * @block_set: The set of serialization blocks. * @count: A counter tracking the number of files currently stored in the * @files_list for this session. */ struct luo_file_set { struct list_head files_list; - struct luo_file_ser *files; + struct kho_block_set block_set; u64 count; }; From 5ba3f30643cbdd79fb82e525aa1ca55b62fcc7ac Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:44:00 +0000 Subject: [PATCH 2713/5214] selftests/liveupdate: Test session and file limit removal With the removal of static limits on the number of sessions and files per session, the orchestrator now uses dynamic allocation. Add new test cases to verify that the system can handle a large number of sessions and files. These tests ensure that the dynamic block allocation and reuse logic for session metadata and outgoing files work correctly beyond the previous static limits. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-12-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/liveupdate/liveupdate.c | 75 +++++++++++++++++++ .../selftests/liveupdate/luo_test_utils.c | 24 ++++++ .../selftests/liveupdate/luo_test_utils.h | 2 + 3 files changed, 101 insertions(+) diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index c7d94b9181e1..502fb3567e38 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -26,6 +26,7 @@ #include +#include "luo_test_utils.h" #include "../kselftest.h" #include "../kselftest_harness.h" @@ -499,4 +500,78 @@ TEST_F(liveupdate_device, get_session_name_max_length) ASSERT_EQ(close(session_fd), 0); } +/* + * Test Case: Manage Many Sessions + * + * Verifies that a large number of sessions can be created and then + * destroyed during normal system operation. This specifically tests the + * dynamic block allocation and reuse logic for session metadata management + * without preserving any files. + */ +TEST_F(liveupdate_device, preserve_many_sessions) +{ +#define MANY_SESSIONS 2000 + int session_fds[MANY_SESSIONS]; + int ret, i; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + ret = luo_ensure_nofile_limit(MANY_SESSIONS); + if (ret == -EPERM) + SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); + ASSERT_EQ(ret, 0); + + for (i = 0; i < MANY_SESSIONS; i++) { + char name[64]; + + snprintf(name, sizeof(name), "many-session-%d", i); + session_fds[i] = create_session(self->fd1, name); + ASSERT_GE(session_fds[i], 0); + } + + for (i = 0; i < MANY_SESSIONS; i++) + ASSERT_EQ(close(session_fds[i]), 0); +} + +/* + * Test Case: Preserve Many Files + * + * Verifies that a large number of files can be preserved in a single session + * and then destroyed during normal system operation. This tests the dynamic + * block allocation and management for outgoing files. + */ +TEST_F(liveupdate_device, preserve_many_files) +{ +#define MANY_FILES 500 + int mem_fds[MANY_FILES]; + int session_fd, ret, i; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd = create_session(self->fd1, "many-files-test"); + ASSERT_GE(session_fd, 0); + + ret = luo_ensure_nofile_limit(MANY_FILES + 10); + if (ret == -EPERM) + SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); + ASSERT_EQ(ret, 0); + + for (i = 0; i < MANY_FILES; i++) { + mem_fds[i] = memfd_create("test-memfd", 0); + ASSERT_GE(mem_fds[i], 0); + ASSERT_EQ(preserve_fd(session_fd, mem_fds[i], i), 0); + } + + for (i = 0; i < MANY_FILES; i++) + ASSERT_EQ(close(mem_fds[i]), 0); + + ASSERT_EQ(close(session_fd), 0); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.c b/tools/testing/selftests/liveupdate/luo_test_utils.c index 3c8721c505df..333a3530051b 100644 --- a/tools/testing/selftests/liveupdate/luo_test_utils.c +++ b/tools/testing/selftests/liveupdate/luo_test_utils.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,29 @@ int luo_open_device(void) return open(LUO_DEVICE, O_RDWR); } +int luo_ensure_nofile_limit(long min_limit) +{ + struct rlimit hl; + + /* Allow to extra files to be used by test itself */ + min_limit += 32; + + if (getrlimit(RLIMIT_NOFILE, &hl) < 0) + return -errno; + + if (hl.rlim_cur >= min_limit) + return 0; + + hl.rlim_cur = min_limit; + if (hl.rlim_cur > hl.rlim_max) + hl.rlim_max = hl.rlim_cur; + + if (setrlimit(RLIMIT_NOFILE, &hl) < 0) + return -errno; + + return 0; +} + int luo_create_session(int luo_fd, const char *name) { struct liveupdate_ioctl_create_session arg = { .size = sizeof(arg) }; diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.h b/tools/testing/selftests/liveupdate/luo_test_utils.h index 90099bf49577..6a0d85386613 100644 --- a/tools/testing/selftests/liveupdate/luo_test_utils.h +++ b/tools/testing/selftests/liveupdate/luo_test_utils.h @@ -26,6 +26,8 @@ int luo_create_session(int luo_fd, const char *name); int luo_retrieve_session(int luo_fd, const char *name); int luo_session_finish(int session_fd); +int luo_ensure_nofile_limit(long min_limit); + int create_and_preserve_memfd(int session_fd, int token, const char *data); int restore_and_verify_memfd(int session_fd, int token, const char *expected_data); From 3432292fb9130191dca57953941f7ae3888d52d8 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:44:01 +0000 Subject: [PATCH 2714/5214] selftests/liveupdate: Add stress-sessions kexec test Add a new test that creates 2000 LUO sessions before a kexec reboot and verifies their presence after the reboot. This ensures that the linked-block serialization mechanism works correctly for a large number of sessions. Acked-by: Mike Rapoport (Microsoft) Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-13-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/selftests/liveupdate/Makefile | 1 + .../liveupdate/luo_stress_sessions.c | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/luo_stress_sessions.c diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/selftests/liveupdate/Makefile index 080754787ede..ed7534468386 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -6,6 +6,7 @@ TEST_GEN_PROGS += liveupdate TEST_GEN_PROGS_EXTENDED += luo_kexec_simple TEST_GEN_PROGS_EXTENDED += luo_multi_session +TEST_GEN_PROGS_EXTENDED += luo_stress_sessions TEST_FILES += do_kexec.sh diff --git a/tools/testing/selftests/liveupdate/luo_stress_sessions.c b/tools/testing/selftests/liveupdate/luo_stress_sessions.c new file mode 100644 index 000000000000..f201b1839d1d --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_stress_sessions.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + * + * Validate that LUO can handle a large number of sessions across a kexec + * reboot. + */ + +#include +#include +#include "luo_test_utils.h" + +#define NUM_SESSIONS 2000 +#define STATE_SESSION_NAME "kexec_many_state" +#define STATE_MEMFD_TOKEN 999 + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int ret, i; + + ksft_print_msg("[STAGE 1] Increasing ulimit for open files...\n"); + ret = luo_ensure_nofile_limit(NUM_SESSIONS); + if (ret == -EPERM) + ksft_exit_skip("Insufficient privileges to set RLIMIT_NOFILE\n"); + if (ret < 0) + ksft_exit_fail_msg("luo_ensure_nofile_limit failed: %s\n", strerror(-ret)); + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating %d sessions...\n", NUM_SESSIONS); + + for (i = 0; i < NUM_SESSIONS; i++) { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + int s_fd; + + snprintf(name, sizeof(name), "many-test-%d", i); + s_fd = luo_create_session(luo_fd, name); + if (s_fd < 0) { + fail_exit("luo_create_session for '%s' at index %d", + name, i); + } + } + + ksft_print_msg("[STAGE 1] Successfully created %d sessions.\n", + NUM_SESSIONS); + + close(luo_fd); + daemonize_and_wait(); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int i, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage != 2) { + fail_exit("Expected stage 2, but state file contains %d", + stage); + } + + ksft_print_msg("[STAGE 2] Retrieving and finishing %d sessions...\n", + NUM_SESSIONS); + + for (i = 0; i < NUM_SESSIONS; i++) { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + int s_fd; + + snprintf(name, sizeof(name), "many-test-%d", i); + s_fd = luo_retrieve_session(luo_fd, name); + if (s_fd < 0) { + fail_exit("luo_retrieve_session for '%s' at index %d", + name, i); + } + + if (luo_session_finish(s_fd) < 0) { + fail_exit("luo_session_finish for '%s' at index %d", + name, i); + } + close(s_fd); + } + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- MANY-SESSIONS KEXEC TEST PASSED (%d sessions) ---\n", + NUM_SESSIONS); +} + +int main(int argc, char *argv[]) +{ + return luo_test(argc, argv, STATE_SESSION_NAME, + run_stage_1, run_stage_2); +} From 46429a15a6dfe522880d5085f1f6999357758872 Mon Sep 17 00:00:00 2001 From: Pasha Tatashin Date: Wed, 3 Jun 2026 15:44:02 +0000 Subject: [PATCH 2715/5214] selftests/liveupdate: Add stress-files kexec test Add a new luo_stress_files kexec test that verifies preserving and retrieving 500 files across a kexec reboot. Reviewed-by: Pratyush Yadav (Google) Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Pasha Tatashin Link: https://patch.msgid.link/20260603154402.468928-14-pasha.tatashin@soleen.com Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/selftests/liveupdate/Makefile | 1 + .../selftests/liveupdate/luo_stress_files.c | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/luo_stress_files.c diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/selftests/liveupdate/Makefile index ed7534468386..30689d22cb02 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -7,6 +7,7 @@ TEST_GEN_PROGS += liveupdate TEST_GEN_PROGS_EXTENDED += luo_kexec_simple TEST_GEN_PROGS_EXTENDED += luo_multi_session TEST_GEN_PROGS_EXTENDED += luo_stress_sessions +TEST_GEN_PROGS_EXTENDED += luo_stress_files TEST_FILES += do_kexec.sh diff --git a/tools/testing/selftests/liveupdate/luo_stress_files.c b/tools/testing/selftests/liveupdate/luo_stress_files.c new file mode 100644 index 000000000000..0cdf9cd4bac7 --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_stress_files.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + * + * Validate that LUO can handle a large number of files per session across + * a kexec reboot. + */ + +#include +#include +#include "luo_test_utils.h" + +#define NUM_FILES 500 +#define STATE_SESSION_NAME "kexec_many_files_state" +#define STATE_MEMFD_TOKEN 9999 +#define TEST_SESSION_NAME "many_files_session" + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int session_fd, i; + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating test session '%s'...\n", TEST_SESSION_NAME); + session_fd = luo_create_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_create_session"); + + ksft_print_msg("[STAGE 1] Preserving %d files...\n", NUM_FILES); + for (i = 0; i < NUM_FILES; i++) { + char data[64]; + + snprintf(data, sizeof(data), "file-data-%d", i); + if (create_and_preserve_memfd(session_fd, i, data) < 0) + fail_exit("create_and_preserve_memfd for index %d", i); + } + + ksft_print_msg("[STAGE 1] Successfully preserved %d files.\n", NUM_FILES); + + close(luo_fd); + daemonize_and_wait(); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int session_fd; + int i, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage != 2) { + fail_exit("Expected stage 2, but state file contains %d", + stage); + } + + ksft_print_msg("[STAGE 2] Retrieving test session '%s'...\n", TEST_SESSION_NAME); + session_fd = luo_retrieve_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_retrieve_session"); + + ksft_print_msg("[STAGE 2] Verifying %d files...\n", NUM_FILES); + for (i = 0; i < NUM_FILES; i++) { + char data[64]; + int fd; + + snprintf(data, sizeof(data), "file-data-%d", i); + fd = restore_and_verify_memfd(session_fd, i, data); + if (fd < 0) + fail_exit("restore_and_verify_memfd for index %d", i); + close(fd); + } + + ksft_print_msg("[STAGE 2] Finishing test session...\n"); + if (luo_session_finish(session_fd) < 0) + fail_exit("luo_session_finish for test session"); + close(session_fd); + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- MANY-FILES KEXEC TEST PASSED (%d files) ---\n", + NUM_FILES); +} + +int main(int argc, char *argv[]) +{ + return luo_test(argc, argv, STATE_SESSION_NAME, + run_stage_1, run_stage_2); +} From 63f2279aa1eca55c0f6403f488739f1ff54d8564 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 6 Apr 2026 16:57:49 -0400 Subject: [PATCH 2716/5214] power: supply: max17042_battery: Put LSB units into defines Signed-off-by: Sebastian Krzyszkowiak Link: https://patch.msgid.link/20260406205759.493288-2-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 38 ++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 167fb3fb3732..103ce1d47aa8 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -52,6 +52,13 @@ #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ +#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */ +#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */ +#define MAX17042_TIME_LSB 5625 / 1000 /* s */ +#define MAX17042_VOLTAGE_LSB 625 / 8 /* µV */ +#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */ +#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */ + struct max17042_chip { struct device *dev; struct regmap *regmap; @@ -107,8 +114,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) *temp = sign_extend32(data, 15); /* The value is converted into deci-centigrade scale */ - /* Units of LSB = 1 / 256 degree Celsius */ - *temp = *temp * 10 / 256; + *temp = *temp * 10 * MAX17042_TEMPERATURE_LSB; return 0; } @@ -185,7 +191,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) goto health_error; /* bits [0-3] unused */ - vavg = val * 625 / 8; + vavg = val * MAX17042_VOLTAGE_LSB; /* Convert to millivolts */ vavg /= 1000; @@ -194,7 +200,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) goto health_error; /* bits [0-3] unused */ - vbatt = val * 625 / 8; + vbatt = val * MAX17042_VOLTAGE_LSB; /* Convert to millivolts */ vbatt /= 1000; @@ -301,21 +307,21 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - val->intval = data * 625 / 8; + val->intval = data * MAX17042_VOLTAGE_LSB; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: ret = regmap_read(map, MAX17042_AvgVCELL, &data); if (ret < 0) return ret; - val->intval = data * 625 / 8; + val->intval = data * MAX17042_VOLTAGE_LSB; break; case POWER_SUPPLY_PROP_VOLTAGE_OCV: ret = regmap_read(map, MAX17042_OCVInternal, &data); if (ret < 0) return ret; - val->intval = data * 625 / 8; + val->intval = data * MAX17042_VOLTAGE_LSB; break; case POWER_SUPPLY_PROP_CAPACITY: if (chip->pdata->enable_current_sense) @@ -332,7 +338,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = data * 5000000ll; + data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); @@ -343,7 +349,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = data * 5000000ll; + data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); @@ -354,7 +360,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = data * 5000000ll; + data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); @@ -365,7 +371,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = sign_extend64(data, 15) * 5000000ll; + data64 = sign_extend64(data, 15) * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; data64 = div_s64(data64, MAX17042_DEFAULT_TASK_PERIOD); val->intval = div_s64(data64, chip->pdata->r_sns); @@ -409,7 +415,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = sign_extend64(data, 15) * 1562500ll; + data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB; val->intval = div_s64(data64, chip->pdata->r_sns); } else { return -EINVAL; @@ -421,7 +427,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = sign_extend64(data, 15) * 1562500ll; + data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB; val->intval = div_s64(data64, chip->pdata->r_sns); } else { return -EINVAL; @@ -432,7 +438,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - data64 = data * 1562500ll; + data64 = data * MAX17042_CURRENT_LSB; val->intval = div_s64(data64, chip->pdata->r_sns); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: @@ -444,7 +450,7 @@ static int max17042_get_property(struct power_supply *psy, if (data == U16_MAX) return -ENODATA; - val->intval = data * 5625 / 1000; + val->intval = data * MAX17042_TIME_LSB; break; case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 && @@ -459,7 +465,7 @@ static int max17042_get_property(struct power_supply *psy, if (data == U16_MAX) return -ENODATA; - val->intval = data * 5625 / 1000; + val->intval = data * MAX17042_TIME_LSB; break; default: return -EINVAL; From 0a733cd247eb9bda928049d72ab7ae16b9bff353 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 6 Apr 2026 16:57:50 -0400 Subject: [PATCH 2717/5214] power: supply: max17042_battery: Use Current register in get_status It can take a while for AvgCurrent to adjust after (un)plugging the charger. Use the instantaneous value in order to not confuse the userspace. While at that, don't do unit conversion of the read value. The current code was prone to overflows and we only care about the sign anyway. Signed-off-by: Sebastian Krzyszkowiak Link: https://patch.msgid.link/20260406205759.493288-3-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 103ce1d47aa8..e896cd169a91 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -121,7 +121,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) static int max17042_get_status(struct max17042_chip *chip, int *status) { int ret, charge_full, charge_now; - int avg_current; + int current_now; u32 data; ret = power_supply_am_i_supplied(chip->battery); @@ -166,14 +166,13 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) return 0; } - ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data); + ret = regmap_read(chip->regmap, MAX17042_Current, &data); if (ret < 0) return ret; - avg_current = sign_extend32(data, 15); - avg_current *= 1562500 / chip->pdata->r_sns; + current_now = sign_extend32(data, 15); - if (avg_current > 0) + if (current_now > 0) *status = POWER_SUPPLY_STATUS_CHARGING; else *status = POWER_SUPPLY_STATUS_DISCHARGING; From 6649d933f5b91284750b4ab98ca3c7035e4f04ee Mon Sep 17 00:00:00 2001 From: Vincent Cloutier Date: Mon, 6 Apr 2026 16:57:52 -0400 Subject: [PATCH 2718/5214] power: supply: max17042_battery: Route MAX17055 SOC alerts through dSOCi Use MAX17055 dSOCi for ordinary 1% state-of-charge notifications and leave SALRT configured for the critical low-battery threshold instead of reprogramming the SALRT window on every alert. Signed-off-by: Vincent Cloutier Link: https://patch.msgid.link/20260406205759.493288-5-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 39 +++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index e896cd169a91..d05202974a71 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -27,6 +27,7 @@ /* Status register bits */ #define STATUS_POR_BIT (1 << 1) #define STATUS_BST_BIT (1 << 3) +#define STATUS_DSOCI_BIT (1 << 7) #define STATUS_VMN_BIT (1 << 8) #define STATUS_TMN_BIT (1 << 9) #define STATUS_SMN_BIT (1 << 10) @@ -38,6 +39,7 @@ /* Interrupt mask bits */ #define CFG_ALRT_BIT_ENBL (1 << 2) +#define CFG2_DSOCI_BIT_ENBL (1 << 7) #define VFSOC0_LOCK 0x0000 #define VFSOC0_UNLOCK 0x0080 @@ -52,6 +54,8 @@ #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ +#define MAX17042_CRITICAL_SOC 0x03 + #define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */ #define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */ #define MAX17042_TIME_LSB 5625 / 1000 /* s */ @@ -903,6 +907,34 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) regmap_write(map, MAX17042_SALRT_Th, soc_tr); } +static void max17042_set_critical_soc_threshold(struct max17042_chip *chip) +{ + struct regmap *map = chip->regmap; + u32 soc; + + if (chip->pdata->enable_current_sense) + regmap_read(map, MAX17042_RepSOC, &soc); + else + regmap_read(map, MAX17042_VFSOC, &soc); + + regmap_write(map, MAX17042_SALRT_Th, + ((soc >> 8) >= MAX17042_CRITICAL_SOC) ? + 0xff00 + MAX17042_CRITICAL_SOC : 0xff00); +} + +static void max17042_enable_soc_alerts(struct max17042_chip *chip) +{ + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) { + regmap_update_bits(chip->regmap, MAX17055_Config2, + CFG2_DSOCI_BIT_ENBL, + CFG2_DSOCI_BIT_ENBL); + max17042_set_critical_soc_threshold(chip); + return; + } + + max17042_set_soc_threshold(chip, 1); +} + static irqreturn_t max17042_thread_handler(int id, void *dev) { struct max17042_chip *chip = dev; @@ -913,9 +945,10 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) if (ret) return IRQ_HANDLED; - if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) { + if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT) || + (val & STATUS_DSOCI_BIT)) { dev_dbg(chip->dev, "SOC threshold INTR\n"); - max17042_set_soc_threshold(chip, 1); + max17042_enable_soc_alerts(chip); } /* we implicitly handle all alerts via power_supply_changed */ @@ -1201,7 +1234,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq regmap_update_bits(chip->regmap, MAX17042_CONFIG, CFG_ALRT_BIT_ENBL, CFG_ALRT_BIT_ENBL); - max17042_set_soc_threshold(chip, 1); + max17042_enable_soc_alerts(chip); } else { irq = 0; if (ret != -EBUSY) From 601885ffb5e9a0fd30c40a6c8cffeed2a04d695e Mon Sep 17 00:00:00 2001 From: Vincent Cloutier Date: Mon, 6 Apr 2026 16:57:53 -0400 Subject: [PATCH 2719/5214] power: supply: max17042_battery: Keep only critical alerts during suspend Disable MAX17055 dSOCi while the system is suspended so state-of-charge changes do not wake the system repeatedly. Leave SALRT armed for the critical low-battery threshold and restore runtime alert handling on resume. Signed-off-by: Vincent Cloutier Link: https://patch.msgid.link/20260406205759.493288-6-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index d05202974a71..2110b87bdc4b 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -935,6 +935,16 @@ static void max17042_enable_soc_alerts(struct max17042_chip *chip) max17042_set_soc_threshold(chip, 1); } +static void max17042_suspend_soc_alerts(struct max17042_chip *chip) +{ + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) + return; + + regmap_update_bits(chip->regmap, MAX17055_Config2, + CFG2_DSOCI_BIT_ENBL, 0); + max17042_set_critical_soc_threshold(chip); +} + static irqreturn_t max17042_thread_handler(int id, void *dev) { struct max17042_chip *chip = dev; @@ -1310,6 +1320,7 @@ static int max17042_suspend(struct device *dev) */ if (chip->irq) { disable_irq(chip->irq); + max17042_suspend_soc_alerts(chip); enable_irq_wake(chip->irq); } @@ -1323,8 +1334,8 @@ static int max17042_resume(struct device *dev) if (chip->irq) { disable_irq_wake(chip->irq); enable_irq(chip->irq); - /* re-program the SOC thresholds to 1% change */ - max17042_set_soc_threshold(chip, 1); + /* re-arm runtime SOC alerts */ + max17042_enable_soc_alerts(chip); } return 0; From 7a490187f22b4bfae7ef752edbe3fb13017ca11c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 13:07:52 -0300 Subject: [PATCH 2720/5214] perf sample: Add file_offset field to struct perf_sample Add a file_offset field to struct perf_sample so that event processing callbacks can report the byte offset of the problematic event in perf.data, letting users cross-reference with 'perf report -D' output. Set sample.file_offset in perf_session__deliver_event(), which is the common entry point for both file mode (mmap'd offset) and pipe mode (running byte counter from __perf_session__process_pipe_events). The assignment is placed after evsel__parse_sample(), which zeroes the struct via memset. Preserve file_offset through the deferred callchain delivery path by storing it in struct deferred_event and restoring it after evlist__parse_sample() in both evlist__deliver_deferred_callchain() and session__flush_deferred_samples(). Subsequent patches will use this field in skip/stop warning messages. Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sample.h | 2 ++ tools/perf/util/session.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h index e556c9b656ea..c4eae8b2fd06 100644 --- a/tools/perf/util/sample.h +++ b/tools/perf/util/sample.h @@ -158,6 +158,8 @@ struct perf_sample { u64 code_page_size; /** @cgroup: The sample event PERF_SAMPLE_CGROUP value. */ u64 cgroup; + /** @file_offset: Byte offset of this event in the perf.data file. */ + u64 file_offset; /** @flags: Extra flag data from auxiliary events like intel-pt. */ u32 flags; /** @machine_pid: The guest machine pid derived from the sample id. */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e2e821b77766..7996787d742e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1824,6 +1824,7 @@ static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool struct deferred_event { struct list_head list; union perf_event *event; + u64 file_offset; }; /* @@ -1858,6 +1859,7 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist, perf_sample__exit(&orig_sample); break; } + orig_sample.file_offset = de->file_offset; if (sample->tid != orig_sample.tid) { perf_sample__exit(&orig_sample); @@ -1906,6 +1908,7 @@ static int session__flush_deferred_samples(struct perf_session *session, perf_sample__exit(&sample); break; } + sample.file_offset = de->file_offset; sample.evsel = evlist__id2evsel(evlist, sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, @@ -1984,6 +1987,7 @@ static int machines__deliver_event(struct machines *machines, return -ENOMEM; } memcpy(de->event, event, sz); + de->file_offset = sample->file_offset; list_add_tail(&de->list, &evlist->deferred_samples); return 0; } @@ -2126,6 +2130,7 @@ static int perf_session__deliver_event(struct perf_session *session, pr_err("Can't parse sample, err = %d\n", ret); goto out; } + sample.file_offset = file_offset; /* * evsel__parse_sample() doesn't populate machine_pid/vcpu, * which are needed by machines__find_for_cpumode() to From 43f827251af596072834fd6eb2b562468d1990d5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 14:19:30 -0300 Subject: [PATCH 2721/5214] perf session: Include file offset in event skip/stop messages Add 'at offset %#' to all warning and error messages in session.c that fire when events are skipped or processing stops due to validation failures. This lets users cross-reference with 'perf report -D' output to inspect the surrounding records and understand the corruption context. Covers messages in perf_session__process_event() (alignment, min size, swap failure), perf_session__deliver_event() (no evsel, parse failure, CPU clamping), machines__deliver_event() (NAMESPACES, TEXT_POKE, null-terminated string checks for MMAP/MMAP2/COMM/CGROUP/KSYMBOL), and perf_session__process_user_event() (THREAD_MAP, CPU_MAP, STAT_CONFIG, BPF_METADATA, HEADER_BUILD_ID). Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 112 ++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7996787d742e..e4efb7550927 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1931,13 +1931,14 @@ static int session__flush_deferred_samples(struct perf_session *session, * read-only (MAP_SHARED + PROT_READ) so we cannot write a * null byte in place; skip the event instead. */ -static bool perf_event__check_nul(const char *str, const void *end, const char *event_name) +static bool perf_event__check_nul(const char *str, const void *end, + const char *event_name, u64 file_offset) { size_t max_len = (const char *)end - str; if (max_len == 0 || strnlen(str, max_len) == max_len) { - pr_warning("WARNING: PERF_RECORD_%s: string not null-terminated, skipping event\n", - event_name); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_%s: string not null-terminated, skipping event\n", + file_offset, event_name); return false; } @@ -1995,7 +1996,7 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_MMAP: if (!perf_event__check_nul(event->mmap.filename, (void *)event + event->header.size, - "MMAP")) + "MMAP", file_offset)) return 0; return tool->mmap(tool, event, sample, machine); case PERF_RECORD_MMAP2: @@ -2003,13 +2004,13 @@ static int machines__deliver_event(struct machines *machines, ++evlist->stats.nr_proc_map_timeout; if (!perf_event__check_nul(event->mmap2.filename, (void *)event + event->header.size, - "MMAP2")) + "MMAP2", file_offset)) return 0; return tool->mmap2(tool, event, sample, machine); case PERF_RECORD_COMM: if (!perf_event__check_nul(event->comm.comm, (void *)event + event->header.size, - "COMM")) + "COMM", file_offset)) return 0; return tool->comm(tool, event, sample, machine); case PERF_RECORD_NAMESPACES: { @@ -2027,8 +2028,8 @@ static int machines__deliver_event(struct machines *machines, * cross-endian path. */ if (event->namespaces.nr_namespaces > max_nr) { - pr_warning("WARNING: PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", - (u64)event->namespaces.nr_namespaces, max_nr); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_NAMESPACES: nr_namespaces %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", + file_offset, (u64)event->namespaces.nr_namespaces, max_nr); return 0; } return tool->namespaces(tool, event, sample, machine); @@ -2036,7 +2037,7 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_CGROUP: if (!perf_event__check_nul(event->cgroup.path, (void *)event + event->header.size, - "CGROUP")) + "CGROUP", file_offset)) return 0; return tool->cgroup(tool, event, sample, machine); case PERF_RECORD_FORK: @@ -2078,7 +2079,7 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_KSYMBOL: if (!perf_event__check_nul(event->ksymbol.name, (void *)event + event->header.size, - "KSYMBOL")) + "KSYMBOL", file_offset)) return 0; return tool->ksymbol(tool, event, sample, machine); case PERF_RECORD_BPF_EVENT: @@ -2090,7 +2091,8 @@ static int machines__deliver_event(struct machines *machines, event->text_poke.new_len; if (event->header.size < text_poke_len) { - pr_warning("WARNING: PERF_RECORD_TEXT_POKE: old_len+new_len exceeds event, skipping\n"); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_TEXT_POKE: old_len+new_len exceeds event, skipping\n", + file_offset); return 0; } return tool->text_poke(tool, event, sample, machine); @@ -2120,14 +2122,17 @@ static int perf_session__deliver_event(struct perf_session *session, perf_sample__init(&sample, /*all=*/false); evsel = evlist__event2evsel(session->evlist, event); if (!evsel) { - pr_err("No evsel found for event type %u\n", + pr_err("ERROR: at offset %#" PRIx64 ": no evsel found for %s (%u) event\n", + file_offset, perf_event__name(event->header.type), event->header.type); ret = -EFAULT; goto out; } ret = evsel__parse_sample(evsel, event, &sample); if (ret) { - pr_err("Can't parse sample, err = %d\n", ret); + pr_err("ERROR: at offset %#" PRIx64 ": can't parse %s (%u) sample, err = %d\n", + file_offset, perf_event__name(event->header.type), + event->header.type, ret); goto out; } sample.file_offset = file_offset; @@ -2204,8 +2209,8 @@ static int perf_session__deliver_event(struct perf_session *session, * Downstream array users (timechart, kwork) have * their own per-callback bounds checks. */ - pr_warning_once("WARNING: sample CPU %u >= nr_cpus_avail %u, clamping to 0\n", - sample.cpu, nr_cpus_avail); + pr_warning_once("WARNING: at offset %#" PRIx64 ": sample CPU %u >= nr_cpus_avail %u, clamping to 0\n", + file_offset, sample.cpu, nr_cpus_avail); sample.cpu = 0; } } @@ -2278,7 +2283,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_HEADER_BUILD_ID: if (!perf_event__check_nul(event->build_id.filename, (void *)event + event_size, - "HEADER_BUILD_ID")) { + "HEADER_BUILD_ID", file_offset)) { err = 0; break; } @@ -2311,8 +2316,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, u64 max_nr; if (event_size < sizeof(event->thread_map)) { - pr_err("PERF_RECORD_THREAD_MAP: header.size (%u) too small\n", - event_size); + pr_err("ERROR: at offset %#" PRIx64 ": PERF_RECORD_THREAD_MAP: header.size (%u) too small\n", + file_offset, event_size); err = -EINVAL; break; } @@ -2320,8 +2325,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, max_nr = (event_size - sizeof(event->thread_map)) / sizeof(event->thread_map.entries[0]); if (event->thread_map.nr > max_nr) { - pr_err("PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n", - (u64)event->thread_map.nr, max_nr); + pr_err("ERROR: at offset %#" PRIx64 ": PERF_RECORD_THREAD_MAP: nr %" PRIu64 " exceeds max %" PRIu64 "\n", + file_offset, (u64)event->thread_map.nr, max_nr); err = -EINVAL; break; } @@ -2345,8 +2350,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, sizeof(data->cpus_data.cpu[0]); if (data->cpus_data.nr > max_nr) { - pr_warning("WARNING: PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), skipping\n", - data->cpus_data.nr, max_nr); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP: nr %u exceeds payload (max %u), skipping\n", + file_offset, data->cpus_data.nr, max_nr); err = 0; goto out; } @@ -2359,8 +2364,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, sizeof(data->mask32_data.mask[0]); if (data->mask32_data.nr > max_nr) { - pr_warning("WARNING: PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), skipping\n", - data->mask32_data.nr, max_nr); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP mask32: nr %u exceeds payload (max %u), skipping\n", + file_offset, data->mask32_data.nr, max_nr); err = 0; goto out; } @@ -2375,14 +2380,14 @@ static s64 perf_session__process_user_event(struct perf_session *session, mask64_data.mask)) / sizeof(data->mask64_data.mask[0]); if (data->mask64_data.nr > max_nr) { - pr_warning("WARNING: PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), skipping\n", - data->mask64_data.nr, max_nr); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP mask64: nr %u exceeds payload (max %u), skipping\n", + file_offset, data->mask64_data.nr, max_nr); err = 0; goto out; } } else { - pr_warning("WARNING: PERF_RECORD_CPU_MAP: unsupported long_size %u, skipping\n", - data->mask32_data.long_size); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_CPU_MAP: unsupported long_size %u, skipping\n", + file_offset, data->mask32_data.long_size); err = 0; goto out; } @@ -2404,8 +2409,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, * cannot clamp nr in place. Skip the event instead. */ if (event->stat_config.nr > max_nr) { - pr_warning("WARNING: PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", - (u64)event->stat_config.nr, max_nr); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_STAT_CONFIG: nr %" PRIu64 " exceeds payload (max %" PRIu64 "), skipping\n", + file_offset, (u64)event->stat_config.nr, max_nr); err = 0; goto out; } @@ -2446,8 +2451,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, u64 nr_entries, max_entries; if (event_size < sizeof(event->bpf_metadata)) { - pr_warning("WARNING: PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n", - event_size); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: header.size (%u) too small, skipping\n", + file_offset, event_size); err = 0; break; } @@ -2458,7 +2463,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, */ if (strnlen(event->bpf_metadata.prog_name, BPF_PROG_NAME_LEN) == BPF_PROG_NAME_LEN) { - pr_warning("WARNING: PERF_RECORD_BPF_METADATA: prog_name not null-terminated, skipping\n"); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: prog_name not null-terminated, skipping\n", + file_offset); err = 0; break; } @@ -2467,8 +2473,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, max_entries = (event_size - sizeof(event->bpf_metadata)) / sizeof(event->bpf_metadata.entries[0]); if (nr_entries > max_entries) { - pr_warning("WARNING: PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n", - nr_entries, max_entries); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: nr_entries %" PRIu64 " exceeds max %" PRIu64 ", skipping\n", + file_offset, nr_entries, max_entries); err = 0; break; } @@ -2478,7 +2484,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, BPF_METADATA_KEY_LEN) == BPF_METADATA_KEY_LEN || strnlen(event->bpf_metadata.entries[i].value, BPF_METADATA_VALUE_LEN) == BPF_METADATA_VALUE_LEN) { - pr_warning("WARNING: PERF_RECORD_BPF_METADATA: entry %" PRIu64 " key/value not null-terminated, skipping\n", i); + pr_warning("WARNING: at offset %#" PRIx64 ": PERF_RECORD_BPF_METADATA: entry %" PRIu64 " key/value not null-terminated, skipping\n", + file_offset, i); err = 0; goto out; } @@ -2752,22 +2759,22 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, event->header.type != PERF_RECORD_HEADER_TRACING_DATA && event->header.type != PERF_RECORD_COMPRESSED && event->header.type != PERF_RECORD_HEADER_FEATURE) { - pr_warning("WARNING: peek_event: event type %u size %u not aligned to %zu\n", - event->header.type, - event->header.size, sizeof(u64)); + pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u not aligned to %zu\n", + (u64)file_offset, perf_event__name(event->header.type), + event->header.type, event->header.size, sizeof(u64)); return -1; } if (event->header.type >= PERF_RECORD_HEADER_MAX) { - pr_warning("WARNING: peek_event: unsupported event type %u, skipping\n", - event->header.type); + pr_warning("WARNING: at offset %#" PRIx64 ": unsupported event type %u, skipping\n", + (u64)file_offset, event->header.type); return 0; } if (perf_event__too_small(event, &min_sz)) { - pr_warning("WARNING: peek_event: %s event size %u too small (min %u)\n", - perf_event__name(event->header.type), - event->header.size, min_sz); + pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u too small (min %u)\n", + (u64)file_offset, perf_event__name(event->header.type), + event->header.type, event->header.size, min_sz); return -1; } @@ -2883,9 +2890,9 @@ static s64 perf_session__process_event(struct perf_session *session, event->header.type != PERF_RECORD_HEADER_TRACING_DATA && event->header.type != PERF_RECORD_COMPRESSED && event->header.type != PERF_RECORD_HEADER_FEATURE) { - pr_err("ERROR: %s event size %u is not 8-byte aligned, aborting\n", - perf_event__name(event->header.type), - event->header.size); + pr_err("ERROR: at offset %#" PRIx64 ": %s (%u) event size %u is not 8-byte aligned, aborting\n", + file_offset, perf_event__name(event->header.type), + event->header.type, event->header.size); return -EINVAL; } @@ -2905,16 +2912,17 @@ static s64 perf_session__process_event(struct perf_session *session, * can be safely stepped over without misaligning the stream. */ if (perf_event__too_small(event, &min_sz)) { - pr_warning("WARNING: %s event size %u too small (min %u), skipping\n", - perf_event__name(event->header.type), - event->header.size, min_sz); + pr_warning("WARNING: at offset %#" PRIx64 ": %s (%u) event size %u too small (min %u), skipping\n", + file_offset, perf_event__name(event->header.type), + event->header.type, event->header.size, min_sz); return 0; } if (session->header.needs_swap && event_swap(event, evlist__sample_id_all(evlist))) { - pr_warning("WARNING: swap failed for %s event, skipping\n", - perf_event__name(event->header.type)); + pr_warning("WARNING: at offset %#" PRIx64 ": swap failed for %s (%u) event, skipping\n", + file_offset, perf_event__name(event->header.type), + event->header.type); return 0; } From d38bee6d8082c23635faa5473162291d228ca96e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 14:21:44 -0300 Subject: [PATCH 2722/5214] perf sched: Include file offset in event skip messages Add the perf.data file offset to the CPU out-of-bounds and machine__resolve failure messages emitted when samples are skipped in process_sched_switch_event(), process_sched_runtime_event(), and timehist_sched_change_event(). Also switch event type from raw integer to perf_event__name() string for readability. Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 812a1b0d56d6..9ec8e049e19b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1792,8 +1792,10 @@ static int process_sched_switch_event(const struct perf_tool *tool, u32 prev_pid = perf_sample__intval(sample, "prev_pid"), next_pid = perf_sample__intval(sample, "next_pid"); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (this_cpu < 0 || this_cpu >= MAX_CPUS) { - pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", this_cpu); + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, this_cpu); return 0; } @@ -1819,8 +1821,10 @@ static int process_sched_runtime_event(const struct perf_tool *tool, { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { - pr_warning("Out-of-bound sample CPU %u. Skipping sample\n", sample->cpu); + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %u, skipping sample\n", + sample->file_offset, sample->cpu); return 0; } @@ -2786,15 +2790,18 @@ static int timehist_sched_change_event(const struct perf_tool *tool, int rc = 0; const char state = perf_sample__taskstate(sample, "prev_state"); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { - pr_warning("Out-of-bound sample CPU %d. Skipping sample\n", sample->cpu); + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, sample->cpu); return 0; } addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_err("problem processing %d event. skipping it\n", - event->header.type); + pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); rc = -1; goto out; } From 4cd74dee09d4b06f0e80826bf9d472d5aa42caaf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 14:25:40 -0300 Subject: [PATCH 2723/5214] perf timechart: Include file offset in CPU bounds check messages Add the perf.data file offset to the out-of-bounds CPU debug messages in process_sample_cpu_idle(), process_sample_cpu_frequency(), process_sample_sched_wakeup(), and process_sample_sched_switch(). Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 630756bebe32..071987241a52 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -605,8 +605,10 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused, u32 state = perf_sample__intval(sample, "state"); u32 cpu_id = perf_sample__intval(sample, "cpu_id"); + /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu_id %u\n", cpu_id); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %u\n", + sample->file_offset, cpu_id); return -1; } if (state == (u32)PWR_EVENT_EXIT) @@ -624,8 +626,10 @@ process_sample_cpu_frequency(struct timechart *tchart, u32 state = perf_sample__intval(sample, "state"); u32 cpu_id = perf_sample__intval(sample, "cpu_id"); + /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu_id %u\n", cpu_id); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %u\n", + sample->file_offset, cpu_id); return -1; } p_state_change(tchart, cpu_id, sample->time, state); @@ -641,8 +645,10 @@ process_sample_sched_wakeup(struct timechart *tchart, int waker = perf_sample__intval(sample, "common_pid"); int wakee = perf_sample__intval(sample, "pid"); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n", + sample->file_offset, sample->cpu); return -1; } sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace); @@ -658,8 +664,10 @@ process_sample_sched_switch(struct timechart *tchart, int next_pid = perf_sample__intval(sample, "next_pid"); u64 prev_state = perf_sample__intval(sample, "prev_state"); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n", + sample->file_offset, sample->cpu); return -1; } sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, @@ -676,6 +684,7 @@ process_sample_power_start(struct timechart *tchart __maybe_unused, u64 cpu_id = perf_sample__intval(sample, "cpu_id"); u64 value = perf_sample__intval(sample, "value"); + /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); return -1; @@ -689,6 +698,7 @@ process_sample_power_end(struct timechart *tchart, struct perf_sample *sample, const char *backtrace __maybe_unused) { + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { pr_debug("Out-of-bounds cpu %u\n", sample->cpu); return -1; @@ -705,6 +715,7 @@ process_sample_power_frequency(struct timechart *tchart, u64 cpu_id = perf_sample__intval(sample, "cpu_id"); u64 value = perf_sample__intval(sample, "value"); + /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); return -1; From 865224bfde78b31be8883302db496ecb3b7919ab Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 15:21:20 -0300 Subject: [PATCH 2724/5214] perf tools: Include file offset and event type name in skip messages Add the perf.data file offset and use perf_event__name() instead of raw event type integers in the 'problem processing event, skipping it' messages emitted by process_sample_event() callbacks across annotate, c2c, diff, kmem, kvm, kwork, lock, report, script, and build-id. This lets users cross-reference skipped events with 'perf report -D' output. Also add explicit #include "util/event.h" and where needed to avoid depending on transitive includes. Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 5 +++-- tools/perf/builtin-c2c.c | 5 +++-- tools/perf/builtin-diff.c | 8 +++++--- tools/perf/builtin-kmem.c | 6 ++++-- tools/perf/builtin-kvm.c | 9 ++++++--- tools/perf/builtin-kwork.c | 4 +++- tools/perf/builtin-lock.c | 6 ++++-- tools/perf/builtin-report.c | 9 ++++++--- tools/perf/builtin-script.c | 10 ++++++---- tools/perf/util/build-id.c | 5 +++-- 10 files changed, 43 insertions(+), 24 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5f450c8093c0..b918f9eed5fd 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -288,8 +288,9 @@ static int process_sample_event(const struct perf_tool *tool, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_warning("problem processing %d event, skipping it.\n", - event->header.type); + pr_warning("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out_put; } diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 36f386949923..d3503be9350c 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -328,8 +328,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out; } diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index b4ff863b304c..9592f44b6545 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -409,8 +409,9 @@ static int diff__process_sample_event(const struct perf_tool *tool, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_warning("problem processing %d event, skipping it.\n", - event->header.type); + pr_warning("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out; } @@ -436,7 +437,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, case COMPUTE_STREAM: if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, NULL)) { - pr_debug("problem adding hist entry, skipping event\n"); + pr_debug("problem adding hist entry at offset %#" PRIx64 ", skipping event\n", + sample->file_offset); goto out; } break; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 33585e353efe..e1b2f5bc1ba8 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -22,6 +22,7 @@ #include "util/cpumap.h" #include "util/debug.h" +#include "util/event.h" #include "util/string2.h" #include "util/util.h" @@ -987,8 +988,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, sample->tid); if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); return -1; } diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index dd2ed21596aa..394302ebdb16 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -22,6 +22,7 @@ #include "util/synthetic-events.h" #include "util/top.h" #include "util/data.h" +#include "util/event.h" #include "util/ordered-events.h" #include "util/kvm-stat.h" #include "util/util.h" @@ -1141,14 +1142,16 @@ static int process_sample_event(const struct perf_tool *tool, return 0; if (machine__resolve(machine, &kvm->al, sample) < 0) { - pr_warning("Fail to resolve address location, skip sample.\n"); + pr_warning("WARNING: at offset %#" PRIx64 ": fail to resolve address location, skipping sample\n", + sample->file_offset); return 0; } thread = machine__findnew_thread(machine, sample->pid, sample->tid); if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); return -1; } diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 99dc293a0744..110de3507d48 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -9,6 +9,7 @@ #include "perf.h" #include "util/data.h" +#include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/header.h" @@ -897,7 +898,8 @@ static int timehist_exit_event(struct perf_kwork *kwork, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_debug("Problem processing event, skipping it\n"); + pr_debug("problem processing event at offset %#" PRIx64 ", skipping it\n", + sample->file_offset); ret = -1; goto out; } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 94a8c35abb0b..5841d43be971 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -21,6 +21,7 @@ #include "util/tracepoint.h" #include "util/debug.h" +#include "util/event.h" #include "util/session.h" #include "util/tool.h" #include "util/data.h" @@ -1433,8 +1434,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, sample->tid); if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); return -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 973d97af8501..cd052aa78132 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -27,6 +27,7 @@ #include "perf.h" #include "util/debug.h" +#include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/evswitch.h" @@ -284,8 +285,9 @@ static int process_sample_event(const struct perf_tool *tool, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); + pr_debug("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out_put; } @@ -332,7 +334,8 @@ static int process_sample_event(const struct perf_tool *tool, ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep); if (ret < 0) - pr_debug("problem adding hist entry, skipping event\n"); + pr_debug("problem adding hist entry at offset %#" PRIx64 ", skipping event\n", + sample->file_offset); out_put: addr_location__exit(&al); return ret; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 5124edf2b7a6..f4aa255fc329 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2693,8 +2693,9 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; if (!al.thread && machine__resolve(machine, &al, sample) < 0) { - pr_err("problem processing %d event, skipping it.\n", - event->header.type); + pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out_put; } @@ -2775,8 +2776,9 @@ static int process_deferred_sample_event(const struct perf_tool *tool, goto out_put; if (machine__resolve(machine, &al, sample) < 0) { - pr_err("problem processing %d event, skipping it.\n", - event->header.type); + pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); ret = -1; goto out_put; } diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index af4d874f1381..8c0a9ae932aa 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -10,6 +10,7 @@ #include "util.h" // lsdir(), mkdir_p(), rm_rf() #include #include +#include #include #include #include @@ -62,8 +63,8 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, sample->tid); if (thread == NULL) { - pr_err("problem processing %d event, skipping it.\n", - event->header.type); + pr_err("problem processing %s event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), sample->file_offset); return -1; } From fa06520fa0aae5a8e7386c1d6b622c4d8b6a400d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 19:24:42 -0300 Subject: [PATCH 2725/5214] perf timechart: Fix cat_backtrace() use-after-free on corrupted callchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cat_backtrace() uses open_memstream() to build a backtrace string. When an invalid callchain context is encountered, zfree(&p) frees the memstream buffer, then the exit path calls fclose(f), which flushes to the already-freed buffer — a use-after-free. The function then returns a dangling pointer that the caller passes to a handler and subsequently double-frees. Fix by replacing the zfree(&p) with a 'corrupted' flag. At the exit label, always fclose(f) first (which finalizes the buffer), then conditionally free it when corrupted. This ensures the memstream contract is honored: the buffer remains valid until fclose(). While here, update the machine__resolve failure message to include file_offset and the event type name, matching the pattern from the preceding series. Also update the three legacy power event handlers under SUPPORT_OLD_POWER_EVENTS to include file_offset in their out-of-bounds CPU messages for consistency. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 36 ++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 071987241a52..85a9ad0455ae 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -489,6 +489,10 @@ static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, } } +/* + * Returns a malloc'd backtrace string built via open_memstream, or NULL + * on error. Caller must free() the returned pointer. + */ static char *cat_backtrace(union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -500,6 +504,7 @@ static char *cat_backtrace(union perf_event *event, u8 cpumode = PERF_RECORD_MISC_USER; struct ip_callchain *chain = sample->callchain; FILE *f = open_memstream(&p, &p_len); + bool corrupted = false; if (!f) { perror("open_memstream error"); @@ -511,8 +516,9 @@ static char *cat_backtrace(union perf_event *event, goto exit; if (machine__resolve(machine, &al, sample) < 0) { - fprintf(stderr, "problem processing %d event, skipping it.\n", - event->header.type); + pr_err("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n", + perf_event__name(event->header.type), event->header.type, + sample->file_offset); goto exit; } @@ -537,14 +543,8 @@ static char *cat_backtrace(union perf_event *event, cpumode = PERF_RECORD_MISC_USER; break; default: - pr_debug("invalid callchain context: " - "%"PRId64"\n", (s64) ip); - - /* - * It seems the callchain is corrupted. - * Discard all. - */ - zfree(&p); + pr_debug("invalid callchain context: %" PRId64 "\n", (s64) ip); + corrupted = true; goto exit; } continue; @@ -561,7 +561,14 @@ static char *cat_backtrace(union perf_event *event, } exit: addr_location__exit(&al); + /* + * fclose() on an open_memstream always sets p to a valid buffer, + * even if nothing was written — see open_memstream(3). So p is + * never NULL after fclose and we need the flag to discard it. + */ fclose(f); + if (corrupted) + zfree(&p); return p; } @@ -686,7 +693,8 @@ process_sample_power_start(struct timechart *tchart __maybe_unused, /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %llu\n", + sample->file_offset, (unsigned long long)cpu_id); return -1; } c_state_start(cpu_id, sample->time, value); @@ -700,7 +708,8 @@ process_sample_power_end(struct timechart *tchart, { /* perf.data is untrusted input — CPU may be absent or corrupted */ if (sample->cpu >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu %u\n", sample->cpu); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu %u\n", + sample->file_offset, sample->cpu); return -1; } c_state_end(tchart, sample->cpu, sample->time); @@ -717,7 +726,8 @@ process_sample_power_frequency(struct timechart *tchart, /* perf.data is untrusted input — cpu_id may be corrupted */ if (cpu_id >= MAX_CPUS) { - pr_debug("Out-of-bounds cpu_id %llu\n", (unsigned long long)cpu_id); + pr_debug("at offset %#" PRIx64 ": out-of-bounds cpu_id %llu\n", + sample->file_offset, (unsigned long long)cpu_id); return -1; } p_state_change(tchart, cpu_id, sample->time, value); From 1e2c83f732deb329ebce23e26cbc482f4c4bf194 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 1 Jun 2026 19:53:41 -0300 Subject: [PATCH 2726/5214] perf sched: Replace BUG_ON on invalid CPU with graceful skip latency_switch_event(), latency_runtime_event(), and map_switch_event() use BUG_ON(cpu >= MAX_CPUS || cpu < 0) to validate the sample CPU. When PERF_SAMPLE_CPU is absent from the sample type, evsel__parse_sample() initializes sample->cpu to (u32)-1. Casting this to int yields -1, which triggers the BUG_ON and aborts perf sched. The central CPU validation in perf_session__deliver_event() intentionally preserves the (u32)-1 sentinel for downstream tools like perf script and perf inject, so leaf callbacks must handle it themselves. Replace the three BUG_ON calls with graceful skips using pr_warning(), matching the existing pattern in process_sched_switch_event() and process_sched_runtime_event() earlier in the same file. Include the file offset for cross-referencing with perf report -D. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9ec8e049e19b..81833d169470 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1145,7 +1145,12 @@ static int latency_switch_event(struct perf_sched *sched, int cpu = sample->cpu, err = -1; s64 delta; - BUG_ON(cpu >= MAX_CPUS || cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (cpu >= MAX_CPUS || cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, cpu); + return 0; + } timestamp0 = sched->cpu_last_switched[cpu]; sched->cpu_last_switched[cpu] = timestamp; @@ -1215,7 +1220,13 @@ static int latency_runtime_event(struct perf_sched *sched, if (thread == NULL) return -1; - BUG_ON(cpu >= MAX_CPUS || cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (cpu >= MAX_CPUS || cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, cpu); + err = 0; + goto out_put; + } if (!atoms) { if (thread_atoms_insert(sched, thread)) goto out_put; @@ -1640,7 +1651,12 @@ static int map_switch_event(struct perf_sched *sched, struct perf_sample *sampl const char *str; int ret = -1; - BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0); + /* perf.data is untrusted input — CPU may be absent or corrupted */ + if (this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0) { + pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", + sample->file_offset, this_cpu.cpu); + return 0; + } if (this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; From 078fc0430bf17e2a571e36877c1942f6c0860524 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 2 Jun 2026 20:45:14 -0300 Subject: [PATCH 2727/5214] perf test: Add file offset diagnostic test for corrupted perf.data Add a shell test that verifies the file_offset diagnostic messages work correctly when perf encounters corrupted events. The test corrupts a MMAP2 event's size field in a recorded perf.data file, then checks that perf report produces warning messages that include both the file offset (e.g. "at offset 0x2738:") and the event type name with numeric id (e.g. "MMAP2 (10)"). This exercises the diagnostic improvements from the file_offset series, which retrofitted all skip/stop/error messages to include the position and type of the problematic event. Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/data_file_offset_diagnostics.sh | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100755 tools/perf/tests/shell/data_file_offset_diagnostics.sh diff --git a/tools/perf/tests/shell/data_file_offset_diagnostics.sh b/tools/perf/tests/shell/data_file_offset_diagnostics.sh new file mode 100755 index 000000000000..389b0b84752d --- /dev/null +++ b/tools/perf/tests/shell/data_file_offset_diagnostics.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Test that perf report includes file offsets and event type names in diagnostic messages. +# SPDX-License-Identifier: GPL-2.0 +# +# The file_offset diagnostic series adds "at offset 0x...: TYPE (N)" +# to all skip/stop/error messages. This test corrupts an event's size +# field in a perf.data file, then verifies the resulting warning +# includes the file offset and event type. + +err=0 + +cleanup() { + [ -n "${perfdata}" ] && rm -f "${perfdata}" "${perfdata}.old" + rm -f "${corrupted}" "${stderrfile}" + trap - EXIT TERM INT +} +trap 'cleanup; exit 1' TERM INT +trap cleanup EXIT + +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 +corrupted=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 +stderrfile=$(mktemp /tmp/__perf_test.perf.data.XXXXX) || exit 2 + +if ! perf record -o "${perfdata}" -- perf test -w noploop 2>/dev/null; then + echo "Skip: perf record failed" + cleanup + exit 2 +fi + +# Find the file offset of the first MMAP2 event via perf report -D. +# Format: "timestamp 0xOFFSET [0xSIZE]: PERF_RECORD_MMAP2 ..." +mmap2_line=$(perf report -D -i "${perfdata}" 2>/dev/null | grep "PERF_RECORD_MMAP2" | head -1) +if [ -z "${mmap2_line}" ]; then + echo "Skip: no MMAP2 events found in perf.data" + cleanup + exit 2 +fi + +mmap2_offset=$(echo "${mmap2_line}" | awk '{print $2}') +mmap2_offset_dec=$((mmap2_offset)) + +# Copy the file and corrupt the MMAP2 event's size field. +# perf_event_header layout: type(u32) misc(u16) size(u16) +# Set size to 16 — below the MMAP2 minimum, which triggers +# the "event size too small" warning. +# +# perf.data uses native byte order, so write the u16 in the +# host's endianness to work on both LE and BE architectures. +cp "${perfdata}" "${corrupted}" +if printf '\x01\x02' | od -A n -t x2 | grep -q "0201"; then + # Little-endian + printf '\x10\x00' +else + # Big-endian + printf '\x00\x10' +fi | dd of="${corrupted}" bs=1 seek=$((mmap2_offset_dec + 6)) conv=notrunc 2>/dev/null + +perf report -i "${corrupted}" --stdio > /dev/null 2> "${stderrfile}" + +# Check that warnings include "at offset 0x..." +if grep -q "at offset 0x" "${stderrfile}"; then + echo "File offset in diagnostics [Success]" +else + echo "File offset in diagnostics [Failed: no 'at offset 0x...' found]" + echo " stderr was:" + head -5 "${stderrfile}" + err=1 +fi + +# Check that the event type name and numeric id appear: "MMAP2 (10)" +if grep -q "MMAP2 (10)" "${stderrfile}"; then + echo "Event type name in diagnostics [Success]" +else + echo "Event type name in diagnostics [Failed: no 'MMAP2 (10)' found]" + echo " stderr was:" + head -5 "${stderrfile}" + err=1 +fi + +# Check that the reported offset matches the actual corruption point +if grep -q "at offset ${mmap2_offset}:" "${stderrfile}"; then + echo "Correct offset reported [Success]" +else + echo "Correct offset reported [Failed: expected offset ${mmap2_offset}]" + echo " stderr was:" + head -5 "${stderrfile}" + err=1 +fi + +cleanup +exit ${err} From ed52302f51ccb217a1d1b70bd7cec36d6f75693c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:24:58 -0700 Subject: [PATCH 2728/5214] perf env: Add perf_env__e_machine helper and use in perf_env__arch Add a helper that lazily computes the e_machine and falls back to EM_HOST. Use the perf_env's arch to compute the e_machine if available, using a binary search for efficiency while handling duplicate rules. Switch perf_env__arch to be derived from e_machine for consistency. To support 32-bit compat binaries on 64-bit hosts during dynamic local or live operations, unpopulated arch fallback paths query uname() at runtime to dynamically resolve the correct host e_machine, safely preventing bitness misclassification regressions. Update session and header to use the helper to safely record e_machine and flags without forcing premature thread scanning. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/env.c | 290 ++++++++++++++++++++++++++++++++------ tools/perf/util/env.h | 2 + tools/perf/util/header.c | 32 +++-- tools/perf/util/session.c | 26 ++-- 4 files changed, 284 insertions(+), 66 deletions(-) diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 20953ef7b9d8..0cd23b5fc651 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 #include "cpumap.h" +#include "dwarf-regs.h" #include "debug.h" #include "env.h" #include "util/header.h" #include "util/rwsem.h" #include +#include #include #include #include @@ -309,15 +311,27 @@ void perf_env__init(struct perf_env *env) static void perf_env__init_kernel_mode(struct perf_env *env) { - const char *arch = perf_env__raw_arch(env); + const char *arch = env->arch; - if (!strncmp(arch, "x86_64", 6) || !strncmp(arch, "aarch64", 7) || - !strncmp(arch, "arm64", 5) || !strncmp(arch, "mips64", 6) || - !strncmp(arch, "parisc64", 8) || !strncmp(arch, "riscv64", 7) || - !strncmp(arch, "s390x", 5) || !strncmp(arch, "sparc64", 7)) - env->kernel_is_64_bit = 1; - else - env->kernel_is_64_bit = 0; + if (!arch) { + static struct utsname uts = { .machine[0] = '\0', }; + + if (uts.machine[0] == '\0') + uname(&uts); + if (uts.machine[0] != '\0') + arch = uts.machine; + } + + if (arch) { + if (strstr(arch, "64") || strstr(arch, "s390x")) + env->kernel_is_64_bit = 1; + else + env->kernel_is_64_bit = 0; + return; + } + + /* Fallback if completely unresolvable (assume host-bitness) */ + env->kernel_is_64_bit = (sizeof(void *) == 8) ? 1 : 0; } int perf_env__kernel_is_64_bit(struct perf_env *env) @@ -588,51 +602,237 @@ void cpu_cache_level__free(struct cpu_cache_level *cache) zfree(&cache->size); } -/* - * Return architecture name in a normalized form. - * The conversion logic comes from the Makefile. - */ -static const char *normalize_arch(char *arch) -{ - if (!strcmp(arch, "x86_64")) - return "x86"; - if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6') - return "x86"; - if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) - return "sparc"; - if (!strncmp(arch, "aarch64", 7) || !strncmp(arch, "arm64", 5)) - return "arm64"; - if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) - return "arm"; - if (!strncmp(arch, "s390", 4)) - return "s390"; - if (!strncmp(arch, "parisc", 6)) - return "parisc"; - if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3)) - return "powerpc"; - if (!strncmp(arch, "mips", 4)) - return "mips"; - if (!strncmp(arch, "sh", 2) && isdigit(arch[2])) - return "sh"; - if (!strncmp(arch, "loongarch", 9)) - return "loongarch"; +struct arch_to_e_machine { + const char *prefix; + uint16_t e_machine; +}; - return arch; +/* + * A mapping from an arch prefix string to an ELF machine that can be used in a + * bsearch. Some arch prefixes are shared an need additional processing as + * marked next to the architecture. The prefixes handle both perf's architecture + * naming and those from uname. + */ +static const struct arch_to_e_machine prefix_to_e_machine[] = { + {"aarch64", EM_AARCH64}, + {"alpha", EM_ALPHA}, + {"arc", EM_ARC}, + {"arm", EM_ARM}, /* Check also for EM_AARCH64. */ + {"avr", EM_AVR}, /* Check also for EM_AVR32. */ + {"bfin", EM_BLACKFIN}, + {"blackfin", EM_BLACKFIN}, + {"cris", EM_CRIS}, + {"csky", EM_CSKY}, + {"hppa", EM_PARISC}, + {"i386", EM_386}, + {"i486", EM_386}, + {"i586", EM_386}, + {"i686", EM_386}, + {"loongarch", EM_LOONGARCH}, + {"m32r", EM_M32R}, + {"m68k", EM_68K}, + {"microblaze", EM_MICROBLAZE}, + {"mips", EM_MIPS}, + {"msp430", EM_MSP430}, + {"parisc", EM_PARISC}, + {"powerpc", EM_PPC}, /* Check also for EM_PPC64. */ + {"ppc", EM_PPC}, /* Check also for EM_PPC64. */ + {"riscv", EM_RISCV}, + {"s390", EM_S390}, + {"sa110", EM_ARM}, + {"sh", EM_SH}, + {"sparc", EM_SPARC}, /* Check also for EM_SPARCV9. */ + {"sun4u", EM_SPARC}, + {"x86", EM_X86_64}, /* Check also for EM_386. */ + {"xtensa", EM_XTENSA}, +}; + +static int compare_prefix(const void *key, const void *element) +{ + const char *search_key = key; + const struct arch_to_e_machine *map_element = element; + size_t prefix_len = strlen(map_element->prefix); + + return strncmp(search_key, map_element->prefix, prefix_len); +} + +static uint16_t perf_arch_to_e_machine(const char *perf_arch, int is_64_bit) +{ + /* Binary search for a matching prefix. */ + const struct arch_to_e_machine *result; + + if (!perf_arch) + return EM_HOST; + + result = bsearch(perf_arch, + prefix_to_e_machine, ARRAY_SIZE(prefix_to_e_machine), + sizeof(prefix_to_e_machine[0]), + compare_prefix); + + if (!result) { + pr_debug("Unknown perf arch for ELF machine mapping: %s\n", perf_arch); + return EM_NONE; + } + + /* + * Handle conflicting prefixes. If the is_64_bit is unknown (-1) then + * assume 64-bit. We can't use perf_env__kernel_is_64_bit as that + * depends on the arch string. + */ + switch (result->e_machine) { + case EM_ARM: + return !strcmp(perf_arch, "arm64") || !strcmp(perf_arch, "aarch64") + ? EM_AARCH64 : EM_ARM; + case EM_AVR: + return !strcmp(perf_arch, "avr32") ? EM_AVR32 : EM_AVR; + case EM_PPC: + if (is_64_bit == 1) + return EM_PPC64; + if (is_64_bit == 0) + return EM_PPC; + return strstarts(perf_arch, "ppc64") ? EM_PPC64 : EM_PPC; + case EM_SPARC: + if (is_64_bit == 1) + return EM_SPARCV9; + if (is_64_bit == 0) + return EM_SPARC; + return !strcmp(perf_arch, "sparc64") || !strcmp(perf_arch, "sun4u") + ? EM_SPARCV9 : EM_SPARC; + case EM_X86_64: + if (is_64_bit == 1) + return EM_X86_64; + if (is_64_bit == 0) + return EM_386; + return !strcmp(perf_arch, "x86_64") || !strcmp(perf_arch, "x86") + ? EM_X86_64 : EM_386; + default: + return result->e_machine; + } +} + +static const char *e_machine_to_perf_arch(uint16_t e_machine) +{ + /* + * Table for if either the perf arch string differs from uname or there + * are >1 ELF machine with the prefix. + */ + static const struct arch_to_e_machine extras[] = { + {"arm64", EM_AARCH64}, + {"avr32", EM_AVR32}, + {"powerpc", EM_PPC}, + {"powerpc", EM_PPC64}, + {"sparc", EM_SPARCV9}, + {"x86", EM_386}, + {"x86", EM_X86_64}, + {"none", EM_NONE}, + }; + + for (size_t i = 0; i < ARRAY_SIZE(extras); i++) { + if (extras[i].e_machine == e_machine) + return extras[i].prefix; + } + + for (size_t i = 0; i < ARRAY_SIZE(prefix_to_e_machine); i++) { + if (prefix_to_e_machine[i].e_machine == e_machine) + return prefix_to_e_machine[i].prefix; + + } + return "unknown"; +} + +uint16_t perf_env__e_machine_nocache(struct perf_env *env, uint32_t *e_flags) +{ + uint16_t e_machine = EM_NONE; + const char *arch = NULL; + int is_64_bit = -1; + + if (e_flags) + *e_flags = 0; + + if (env) { + arch = env->arch; + is_64_bit = env->kernel_is_64_bit; + } + + if (!arch) { + static struct utsname uts = { .machine[0] = '\0', }; + + if (uts.machine[0] == '\0') + uname(&uts); + if (uts.machine[0] != '\0') + arch = uts.machine; + } + + e_machine = perf_arch_to_e_machine(arch, is_64_bit); + + if (e_flags) + *e_flags = (e_machine == EM_HOST) ? EF_HOST : 0; + + return e_machine; +} + +uint16_t perf_env__e_machine(struct perf_env *env, uint32_t *e_flags) +{ + uint16_t e_machine; + uint32_t local_e_flags = 0; + + if (env && env->e_machine != EM_NONE) { + if (e_flags) + *e_flags = env->e_flags; + + return env->e_machine; + } + e_machine = perf_env__e_machine_nocache(env, &local_e_flags); + /* + * Only cache the e_machine in perf_env if env->arch is not NULL. + * If env->arch is NULL, the e_machine is just a fallback to EM_HOST. + * Caching it permanently would prevent dynamic, more accurate + * thread-based session e_machine scanning later in + * perf_session__e_machine(). + */ + if (env && env->arch) { + env->e_machine = e_machine; + env->e_flags = local_e_flags; + } + if (e_flags) + *e_flags = local_e_flags; + + return e_machine; } const char *perf_env__arch(struct perf_env *env) { - char *arch_name; + uint16_t e_machine; + const char *arch; - if (!env || !env->arch) { /* Assume local operation */ + if (!env) { static struct utsname uts = { .machine[0] = '\0', }; - if (uts.machine[0] == '\0' && uname(&uts) < 0) - return NULL; - arch_name = uts.machine; - } else - arch_name = env->arch; + uint16_t host_e_machine; - return normalize_arch(arch_name); + if (uts.machine[0] == '\0') + uname(&uts); + if (uts.machine[0] != '\0') { + host_e_machine = perf_arch_to_e_machine(uts.machine, -1); + return e_machine_to_perf_arch(host_e_machine); + } + return e_machine_to_perf_arch(EM_HOST); + } + + /* + * Lazily compute/allocate arch. The e_machine may have been + * read from a data file and so may not be EM_HOST. + */ + e_machine = perf_env__e_machine(env, /*e_flags=*/NULL); + arch = e_machine_to_perf_arch(e_machine); + + if (e_machine == EM_RISCV && perf_env__kernel_is_64_bit(env) == 1) + arch = "riscv64"; + else if (e_machine == EM_MIPS && perf_env__kernel_is_64_bit(env) == 1) + arch = "mips64"; + else if (e_machine == EM_PARISC && perf_env__kernel_is_64_bit(env) == 1) + arch = "parisc64"; + + return arch; } const char *perf_env__arch_strerrno(struct perf_env *env __maybe_unused, int err __maybe_unused) diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 739d884fc236..bde192fd5be5 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -187,6 +187,8 @@ int perf_env__read_cpu_topology_map(struct perf_env *env); void cpu_cache_level__free(struct cpu_cache_level *cache); +uint16_t perf_env__e_machine_nocache(struct perf_env *env, uint32_t *e_flags); +uint16_t perf_env__e_machine(struct perf_env *env, uint32_t *e_flags); const char *perf_env__arch(struct perf_env *env); const char *perf_env__arch_strerrno(struct perf_env *env, int err); arch_syscalls__strerrno_t *arch_syscalls__strerrno_function(const char *arch); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5b1fa1653d2a..220e7720fbdb 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -441,21 +441,25 @@ static int write_osrelease(struct feat_fd *ff, return do_write_string(ff, uts.release); } -static int write_arch(struct feat_fd *ff, - struct evlist *evlist __maybe_unused) +static int write_arch(struct feat_fd *ff, struct evlist *evlist) { struct utsname uts; - int ret; + const char *arch = NULL; - ret = uname(&uts); - if (ret < 0) - return -1; + if (evlist->session) + arch = perf_env__arch(perf_session__env(evlist->session)); - return do_write_string(ff, uts.machine); + if (!arch) { + int ret = uname(&uts); + + if (ret < 0) + return -1; + arch = uts.machine; + } + return do_write_string(ff, arch); } -static int write_e_machine(struct feat_fd *ff, - struct evlist *evlist __maybe_unused) +static int write_e_machine(struct feat_fd *ff, struct evlist *evlist) { /* e_machine expanded from 16 to 32-bits for alignment. */ uint32_t e_flags; @@ -2841,10 +2845,18 @@ static int process_##__feat(struct feat_fd *ff, void *data __maybe_unused) \ FEAT_PROCESS_STR_FUN(hostname, hostname); FEAT_PROCESS_STR_FUN(osrelease, os_release); FEAT_PROCESS_STR_FUN(version, version); -FEAT_PROCESS_STR_FUN(arch, arch); FEAT_PROCESS_STR_FUN(cpudesc, cpu_desc); FEAT_PROCESS_STR_FUN(cpuid, cpuid); +static int process_arch(struct feat_fd *ff, void *data __maybe_unused) +{ + free(ff->ph->env.arch); + ff->ph->env.arch = do_read_string(ff); + if (!ff->ph->env.arch) + return -ENOMEM; + return 0; +} + static int process_e_machine(struct feat_fd *ff, void *data __maybe_unused) { int ret; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e4efb7550927..1a9a008ddda3 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -4093,14 +4093,19 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags return EM_HOST; } + /* + * Is the env caching an e_machine? If not we want to compute from the + * more accurate threads. + */ env = perf_session__env(session); - if (env && env->e_machine != EM_NONE) { - if (e_flags) - *e_flags = env->e_flags; - - return env->e_machine; - } + if (env && env->e_machine != EM_NONE) + return perf_env__e_machine(env, e_flags); + /* + * Compute from threads, note this is more accurate than + * perf_env__e_machine that falls back on EM_HOST and doesn't consider + * mixed 32-bit and 64-bit threads. + */ machines__for_each_thread(&session->machines, perf_session__e_machine_cb, &args); @@ -4118,10 +4123,9 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags /* * Couldn't determine from the perf_env or current set of - * threads. Default to the host. + * threads. Potentially use logic that uses the arch string otherwise + * default to the host. Don't cache in the perf_env in case later + * threads indicate a better ELF machine type. */ - if (e_flags) - *e_flags = EF_HOST; - - return EM_HOST; + return perf_env__e_machine_nocache(env, e_flags); } From d0c54c6f9dab20f93185df7722ed133097aaccb3 Mon Sep 17 00:00:00 2001 From: Vincent Cloutier Date: Mon, 6 Apr 2026 16:57:54 -0400 Subject: [PATCH 2729/5214] power: supply: max17042_battery: Remove unused platform-data plumbing No in-tree user still provides `max17042_platform_data` or `max17042_reg_data`. Move the simple runtime fields into `struct max17042_chip`, populate them directly from DT or the default hardware state, and drop the unused public platform-data interface. While here, write the MAX17047/MAX17050 default `FullSOCThr` value directly in probe instead of carrying it through an `init_data` table. Signed-off-by: Vincent Cloutier Link: https://patch.msgid.link/20260406205759.493288-7-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 204 +++++++++++------------- include/linux/power/max17042_battery.h | 29 ---- 2 files changed, 89 insertions(+), 144 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 2110b87bdc4b..446f8926a8e0 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -68,11 +68,18 @@ struct max17042_chip { struct regmap *regmap; struct power_supply *battery; enum max170xx_chip_type chip_type; - struct max17042_platform_data *pdata; + struct max17042_config_data *config_data; struct work_struct work; int init_complete; int irq; int task_period; + bool enable_current_sense; + bool enable_por_init; + unsigned int r_sns; + int vmin; /* in millivolts */ + int vmax; /* in millivolts */ + int temp_min; /* in tenths of degree Celsius */ + int temp_max; /* in tenths of degree Celsius */ }; static enum power_supply_property max17042_battery_props[] = { @@ -165,7 +172,7 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) * Even though we are supplied, we may still be discharging if the * supply is e.g. only delivering 5V 0.5A. Check current if available. */ - if (!chip->pdata->enable_current_sense) { + if (!chip->enable_current_sense) { *status = POWER_SUPPLY_STATUS_CHARGING; return 0; } @@ -207,12 +214,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) /* Convert to millivolts */ vbatt /= 1000; - if (vavg < chip->pdata->vmin) { + if (vavg < chip->vmin) { *health = POWER_SUPPLY_HEALTH_DEAD; goto out; } - if (vbatt > size_add(chip->pdata->vmax, MAX17042_VMAX_TOLERANCE)) { + if (vbatt > size_add(chip->vmax, MAX17042_VMAX_TOLERANCE)) { *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; goto out; } @@ -221,12 +228,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) if (ret < 0) goto health_error; - if (temp < chip->pdata->temp_min) { + if (temp < chip->temp_min) { *health = POWER_SUPPLY_HEALTH_COLD; goto out; } - if (temp > chip->pdata->temp_max) { + if (temp > chip->temp_max) { *health = POWER_SUPPLY_HEALTH_OVERHEAT; goto out; } @@ -327,7 +334,7 @@ static int max17042_get_property(struct power_supply *psy, val->intval = data * MAX17042_VOLTAGE_LSB; break; case POWER_SUPPLY_PROP_CAPACITY: - if (chip->pdata->enable_current_sense) + if (chip->enable_current_sense) ret = regmap_read(map, MAX17042_RepSOC, &data); else ret = regmap_read(map, MAX17042_VFSOC, &data); @@ -344,7 +351,7 @@ static int max17042_get_property(struct power_supply *psy, data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); - do_div(data64, chip->pdata->r_sns); + do_div(data64, chip->r_sns); val->intval = data64; break; case POWER_SUPPLY_PROP_CHARGE_FULL: @@ -355,7 +362,7 @@ static int max17042_get_property(struct power_supply *psy, data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); - do_div(data64, chip->pdata->r_sns); + do_div(data64, chip->r_sns); val->intval = data64; break; case POWER_SUPPLY_PROP_CHARGE_NOW: @@ -366,7 +373,7 @@ static int max17042_get_property(struct power_supply *psy, data64 = data * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); - do_div(data64, chip->pdata->r_sns); + do_div(data64, chip->r_sns); val->intval = data64; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: @@ -377,7 +384,7 @@ static int max17042_get_property(struct power_supply *psy, data64 = sign_extend64(data, 15) * MAX17042_CAPACITY_LSB; data64 *= chip->task_period; data64 = div_s64(data64, MAX17042_DEFAULT_TASK_PERIOD); - val->intval = div_s64(data64, chip->pdata->r_sns); + val->intval = div_s64(data64, chip->r_sns); break; case POWER_SUPPLY_PROP_TEMP: ret = max17042_get_temperature(chip, &val->intval); @@ -399,10 +406,10 @@ static int max17042_get_property(struct power_supply *psy, val->intval = sign_extend32(data >> 8, 7) * 10; break; case POWER_SUPPLY_PROP_TEMP_MIN: - val->intval = chip->pdata->temp_min; + val->intval = chip->temp_min; break; case POWER_SUPPLY_PROP_TEMP_MAX: - val->intval = chip->pdata->temp_max; + val->intval = chip->temp_max; break; case POWER_SUPPLY_PROP_HEALTH: ret = max17042_get_battery_health(chip, &val->intval); @@ -413,25 +420,25 @@ static int max17042_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_SCOPE_SYSTEM; break; case POWER_SUPPLY_PROP_CURRENT_NOW: - if (chip->pdata->enable_current_sense) { + if (chip->enable_current_sense) { ret = regmap_read(map, MAX17042_Current, &data); if (ret < 0) return ret; data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB; - val->intval = div_s64(data64, chip->pdata->r_sns); + val->intval = div_s64(data64, chip->r_sns); } else { return -EINVAL; } break; case POWER_SUPPLY_PROP_CURRENT_AVG: - if (chip->pdata->enable_current_sense) { + if (chip->enable_current_sense) { ret = regmap_read(map, MAX17042_AvgCurrent, &data); if (ret < 0) return ret; data64 = sign_extend64(data, 15) * MAX17042_CURRENT_LSB; - val->intval = div_s64(data64, chip->pdata->r_sns); + val->intval = div_s64(data64, chip->r_sns); } else { return -EINVAL; } @@ -442,7 +449,7 @@ static int max17042_get_property(struct power_supply *psy, return ret; data64 = data * MAX17042_CURRENT_LSB; - val->intval = div_s64(data64, chip->pdata->r_sns); + val->intval = div_s64(data64, chip->r_sns); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: ret = regmap_read(map, MAX17042_TTE, &data); @@ -591,7 +598,7 @@ static inline void max17042_write_model_data(struct max17042_chip *chip, for (i = 0; i < size; i++) regmap_write(map, addr + i, - chip->pdata->config_data->cell_char_tbl[i]); + chip->config_data->cell_char_tbl[i]); } static inline void max17042_read_model_data(struct max17042_chip *chip, @@ -626,7 +633,7 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip, static int max17042_init_model(struct max17042_chip *chip) { int ret; - int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); + int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl); u16 *temp_data; temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); @@ -641,7 +648,7 @@ static int max17042_init_model(struct max17042_chip *chip) ret = max17042_model_data_compare( chip, - chip->pdata->config_data->cell_char_tbl, + chip->config_data->cell_char_tbl, temp_data, table_size); @@ -654,7 +661,7 @@ static int max17042_init_model(struct max17042_chip *chip) static int max17042_verify_model_lock(struct max17042_chip *chip) { int i; - int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); + int table_size = ARRAY_SIZE(chip->config_data->cell_char_tbl); u16 *temp_data; int ret = 0; @@ -674,7 +681,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip) static void max17042_write_config_regs(struct max17042_chip *chip) { - struct max17042_config_data *config = chip->pdata->config_data; + struct max17042_config_data *config = chip->config_data; struct regmap *map = chip->regmap; regmap_write(map, MAX17042_CONFIG, config->config); @@ -692,7 +699,7 @@ static void max17042_write_config_regs(struct max17042_chip *chip) static void max17042_write_custom_regs(struct max17042_chip *chip) { - struct max17042_config_data *config = chip->pdata->config_data; + struct max17042_config_data *config = chip->config_data; struct regmap *map = chip->regmap; max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); @@ -716,7 +723,7 @@ static void max17042_write_custom_regs(struct max17042_chip *chip) static void max17042_update_capacity_regs(struct max17042_chip *chip) { - struct max17042_config_data *config = chip->pdata->config_data; + struct max17042_config_data *config = chip->config_data; struct regmap *map = chip->regmap; max17042_write_verify_reg(map, MAX17042_FullCAP, @@ -742,7 +749,7 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip) u32 full_cap0, rep_cap, dq_acc, vfSoc; u32 rem_cap; - struct max17042_config_data *config = chip->pdata->config_data; + struct max17042_config_data *config = chip->config_data; struct regmap *map = chip->regmap; regmap_read(map, MAX17042_FullCAP0, &full_cap0); @@ -774,14 +781,14 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip) } /* - * Block write all the override values coming from platform data. + * Block write all the override values coming from config_data. * This function MUST be called before the POR initialization procedure * specified by maxim. */ static inline void max17042_override_por_values(struct max17042_chip *chip) { struct regmap *map = chip->regmap; - struct max17042_config_data *config = chip->pdata->config_data; + struct max17042_config_data *config = chip->config_data; max17042_override_por(map, MAX17042_TGAIN, config->tgain); max17042_override_por(map, MAX17042_TOFF, config->toff); @@ -896,7 +903,7 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) /* program interrupt thresholds such that we should * get interrupt for every 'off' perc change in the soc */ - if (chip->pdata->enable_current_sense) + if (chip->enable_current_sense) regmap_read(map, MAX17042_RepSOC, &soc); else regmap_read(map, MAX17042_VFSOC, &soc); @@ -912,7 +919,7 @@ static void max17042_set_critical_soc_threshold(struct max17042_chip *chip) struct regmap *map = chip->regmap; u32 soc; - if (chip->pdata->enable_current_sense) + if (chip->enable_current_sense) regmap_read(map, MAX17042_RepSOC, &soc); else regmap_read(map, MAX17042_VFSOC, &soc); @@ -975,8 +982,8 @@ static void max17042_init_worker(struct work_struct *work) struct max17042_chip, work); int ret; - /* Initialize registers according to values from the platform data */ - if (chip->pdata->enable_por_init && chip->pdata->config_data) { + /* Initialize registers according to values from config_data */ + if (chip->enable_por_init && chip->config_data) { ret = max17042_init_chip(chip); if (ret) return; @@ -986,17 +993,10 @@ static void max17042_init_worker(struct work_struct *work) } #ifdef CONFIG_OF -static struct max17042_platform_data * -max17042_get_of_pdata(struct max17042_chip *chip) +static int max17042_parse_dt(struct max17042_chip *chip) { - struct device *dev = chip->dev; - struct device_node *np = dev->of_node; + struct device_node *np = chip->dev->of_node; u32 prop; - struct max17042_platform_data *pdata; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; /* * Require current sense resistor value to be specified for @@ -1007,85 +1007,46 @@ max17042_get_of_pdata(struct max17042_chip *chip) if ((of_property_read_u32(np, "shunt-resistor-micro-ohms", &prop) == 0) || (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0)) { - pdata->r_sns = prop; - pdata->enable_current_sense = true; + chip->r_sns = prop; + chip->enable_current_sense = true; } - if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min)) - pdata->temp_min = INT_MIN; - if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max)) - pdata->temp_max = INT_MAX; - if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin)) - pdata->vmin = INT_MIN; - if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax)) - pdata->vmax = INT_MAX; + if (of_property_read_s32(np, "maxim,cold-temp", &chip->temp_min)) + chip->temp_min = INT_MIN; + if (of_property_read_s32(np, "maxim,over-heat-temp", &chip->temp_max)) + chip->temp_max = INT_MAX; + if (of_property_read_s32(np, "maxim,dead-volt", &chip->vmin)) + chip->vmin = INT_MIN; + if (of_property_read_s32(np, "maxim,over-volt", &chip->vmax)) + chip->vmax = INT_MAX; - return pdata; + return 0; } #endif -static struct max17042_reg_data max17047_default_pdata_init_regs[] = { - /* - * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection - * when the voltage FG reports 95%, as recommended in the datasheet. - */ - { MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 }, -}; - -static struct max17042_platform_data * -max17042_get_default_pdata(struct max17042_chip *chip) +static int max17042_init_defaults(struct max17042_chip *chip) { - struct device *dev = chip->dev; - struct max17042_platform_data *pdata; int ret, misc_cfg; /* - * The MAX17047 gets used on x86 where we might not have pdata, assume + * The MAX17047 gets used on x86 where we might not have DT, assume * the firmware will already have initialized the fuel-gauge and provide * default values for the non init bits to make things work. */ - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return pdata; - - if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) { - pdata->init_data = max17047_default_pdata_init_regs; - pdata->num_init_data = - ARRAY_SIZE(max17047_default_pdata_init_regs); - } ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg); if (ret < 0) - return NULL; + return ret; /* If bits 0-1 are set to 3 then only Voltage readings are used */ - if ((misc_cfg & 0x3) == 0x3) - pdata->enable_current_sense = false; - else - pdata->enable_current_sense = true; + chip->enable_current_sense = (misc_cfg & 0x3) != 0x3; - pdata->vmin = MAX17042_DEFAULT_VMIN; - pdata->vmax = MAX17042_DEFAULT_VMAX; - pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN; - pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX; + chip->vmin = MAX17042_DEFAULT_VMIN; + chip->vmax = MAX17042_DEFAULT_VMAX; + chip->temp_min = MAX17042_DEFAULT_TEMP_MIN; + chip->temp_max = MAX17042_DEFAULT_TEMP_MAX; - return pdata; -} - -static struct max17042_platform_data * -max17042_get_pdata(struct max17042_chip *chip) -{ - struct device *dev = chip->dev; - -#ifdef CONFIG_OF - if (dev->of_node) - return max17042_get_of_pdata(chip); -#endif - if (dev->platform_data) - return dev->platform_data; - - return max17042_get_default_pdata(chip); + return 0; } static const struct regmap_config max17042_regmap_config = { @@ -1164,8 +1125,8 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq struct power_supply_config psy_cfg = {}; struct max17042_chip *chip; int ret; - int i; u32 val; + bool use_default_config = false; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; @@ -1186,10 +1147,19 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq return dev_err_probe(dev, PTR_ERR(chip->regmap), "Failed to initialize regmap\n"); - chip->pdata = max17042_get_pdata(chip); - if (!chip->pdata) - return dev_err_probe(dev, -EINVAL, - "no platform data provided\n"); +#ifdef CONFIG_OF + if (dev->of_node) { + ret = max17042_parse_dt(chip); + if (ret) + return ret; + } else +#endif + { + ret = max17042_init_defaults(chip); + if (ret) + return ret; + use_default_config = true; + } dev_set_drvdata(dev, chip); psy_cfg.drv_data = chip; @@ -1197,19 +1167,13 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq /* When current is not measured, * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ - if (!chip->pdata->enable_current_sense) + if (!chip->enable_current_sense) max17042_desc = &max17042_no_current_sense_psy_desc; - if (chip->pdata->r_sns == 0) - chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; + if (chip->r_sns == 0) + chip->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; - if (chip->pdata->init_data) - for (i = 0; i < chip->pdata->num_init_data; i++) - regmap_write(chip->regmap, - chip->pdata->init_data[i].addr, - chip->pdata->init_data[i].data); - - if (!chip->pdata->enable_current_sense) { + if (!chip->enable_current_sense) { regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000); regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003); regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); @@ -1232,6 +1196,16 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq return dev_err_probe(dev, PTR_ERR(chip->battery), "failed: power supply register\n"); + /* + * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection + * when the voltage FG reports 95%, as recommended in the datasheet. + */ + if (use_default_config && + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) + regmap_write(chip->regmap, MAX17047_FullSOCThr, + MAX17042_BATTERY_FULL << 8); + if (irq) { unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED; diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index d5b08313cf11..25dccf908d20 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -198,16 +198,6 @@ enum max170xx_chip_type { MAXIM_DEVICE_TYPE_NUM }; -/* - * used for setting a register to a desired value - * addr : address for a register - * data : setting value for the register - */ -struct max17042_reg_data { - u8 addr; - u16 data; -}; - struct max17042_config_data { /* External current sense resistor value in milli-ohms */ u32 cur_sense_val; @@ -265,23 +255,4 @@ struct max17042_config_data { u16 cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE]; } __packed; -struct max17042_platform_data { - struct max17042_reg_data *init_data; - struct max17042_config_data *config_data; - int num_init_data; /* Number of enties in init_data array */ - bool enable_current_sense; - bool enable_por_init; /* Use POR init from Maxim appnote */ - - /* - * R_sns in micro-ohms. - * default 10000 (if r_sns = 0) as it is the recommended value by - * the datasheet although it can be changed by board designers. - */ - unsigned int r_sns; - int vmin; /* in millivolts */ - int vmax; /* in millivolts */ - int temp_min; /* in tenths of degree Celsius */ - int temp_max; /* in tenths of degree Celsius */ -}; - #endif /* __MAX17042_BATTERY_H_ */ From 4ccbc91df98a19eba2f40088e9bf5ab0888cf80a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:24:59 -0700 Subject: [PATCH 2730/5214] perf tests topology: Switch env->arch use to env->e_machine Some arch string comparisons weren't normalized. Avoid potential issues with normalized names vs uname values by swtiching to using the e_machine. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/topology.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index f54502ebef4b..bd7b859dea66 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -11,6 +11,7 @@ #include "pmus.h" #include "target.h" #include +#include "dwarf-regs.h" #define TEMPL "/tmp/perf-test-XXXXXX" #define DATA_SIZE 10 @@ -74,6 +75,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) struct aggr_cpu_id id; struct perf_cpu cpu; struct perf_env *env; + uint16_t e_machine; session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); @@ -101,7 +103,9 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) * condition is true (see do_core_id_test in header.c). So always * run this test on those platforms. */ - if (!env->cpu && strncmp(env->arch, "s390", 4) && strncmp(env->arch, "aarch64", 7)) + e_machine = perf_env__e_machine(env, NULL); + + if (!env->cpu && e_machine != EM_S390 && e_machine != EM_AARCH64) return TEST_SKIP; /* @@ -110,7 +114,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) * physical_package_id will be set to -1. Hence skip this * test if physical_package_id returns -1 for cpu from perf_cpu_map. */ - if (!strncmp(env->arch, "ppc64le", 7)) { + if (e_machine == EM_PPC64) { if (cpu__get_socket_id(perf_cpu_map__cpu(map, 0)) == -1) return TEST_SKIP; } From f7c6e4b99ded250b3fdeaa5be4ccaac9ae05f8d9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:00 -0700 Subject: [PATCH 2731/5214] perf env, dso, thread: Add _endian variants for e_machine helpers Add perf_arch_is_big_endian(), dso__read_e_machine_endian(), dso__e_machine_endian(), and thread__e_machine_endian() to support bi-endianness and cross-architecture analysis without breaking the existing API. These helpers allow querying the absolute endianness of a DSO or thread, which is required for tools like Capstone that need to set the correct disassembly mode. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 19 ++++++++----- tools/perf/util/dso.h | 14 ++++++++-- tools/perf/util/env.c | 16 +++++++++++ tools/perf/util/env.h | 1 + tools/perf/util/thread.c | 58 ++++++++++++++++++++++++++++++---------- tools/perf/util/thread.h | 23 +++++++++++++++- 6 files changed, 108 insertions(+), 23 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index b791e1b6b2cf..7dced896c64e 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1220,7 +1220,8 @@ static enum dso_swap_type dso_swap_type__from_elf_data(unsigned char eidata) } /* Reads e_machine from fd, optionally caching data in dso. */ -uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags) +uint16_t dso__read_e_machine_endian(struct dso *optional_dso, int fd, uint32_t *e_flags, + bool *is_big_endian) { uint16_t e_machine = EM_NONE; unsigned char e_ident[EI_NIDENT]; @@ -1250,6 +1251,9 @@ uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags if (swap_type == DSO_SWAP__UNSET) return EM_NONE; // Bad ELF data encoding. + if (is_big_endian) + *is_big_endian = (e_ident[EI_DATA] == ELFDATA2MSB); + /* Cache the need for swapping. */ if (optional_dso) { assert(dso__needs_swap(optional_dso) == DSO_SWAP__UNSET || @@ -1288,7 +1292,8 @@ uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags return e_machine; } -uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags) +uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_t *e_flags, + bool *is_big_endian) { uint16_t e_machine = EM_NONE; int fd; @@ -1308,9 +1313,11 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_fl case DSO_BINARY_TYPE__BPF_IMAGE: case DSO_BINARY_TYPE__OOL: case DSO_BINARY_TYPE__JAVA_JIT: - if (e_flags) - *e_flags = EF_HOST; - return EM_HOST; + if (is_big_endian) { + *is_big_endian = perf_arch_is_big_endian( + machine && machine->env ? perf_env__arch(machine->env) : NULL); + } + return perf_env__e_machine(machine ? machine->env : NULL, e_flags); case DSO_BINARY_TYPE__DEBUGLINK: case DSO_BINARY_TYPE__BUILD_ID_CACHE: case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: @@ -1338,7 +1345,7 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_fl try_to_open_dso(dso, machine); fd = dso__data(dso)->fd; if (fd >= 0) - e_machine = dso__read_e_machine(dso, fd, e_flags); + e_machine = dso__read_e_machine_endian(dso, fd, e_flags, is_big_endian); else if (e_flags) *e_flags = 0; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ede691e9a249..2916b954a804 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -866,8 +866,18 @@ int dso__data_file_size(struct dso *dso, struct machine *machine); off_t dso__data_size(struct dso *dso, struct machine *machine); ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size); -uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags); -uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags); +uint16_t dso__read_e_machine_endian(struct dso *optional_dso, int fd, uint32_t *e_flags, + bool *is_big_endian); +static inline uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags) +{ + return dso__read_e_machine_endian(optional_dso, fd, e_flags, NULL); +} +uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_t *e_flags, + bool *is_big_endian); +static inline uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags) +{ + return dso__e_machine_endian(dso, machine, e_flags, NULL); +} ssize_t dso__data_read_addr(struct dso *dso, struct map *map, struct machine *machine, u64 addr, u8 *data, ssize_t size); diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 0cd23b5fc651..fae70b07ba8d 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -342,6 +342,22 @@ int perf_env__kernel_is_64_bit(struct perf_env *env) return env->kernel_is_64_bit; } +bool perf_arch_is_big_endian(const char *arch) +{ + if (!arch) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; + + if (str_ends_with(arch, "_be") || !strcmp(arch, "sparc") || !strcmp(arch, "sparc64") || + !strcmp(arch, "s390") || !strcmp(arch, "s390x") || !strcmp(arch, "powerpc") || + !strcmp(arch, "ppc") || !strcmp(arch, "ppc64") || + !strcmp(arch, "mips") || !strcmp(arch, "mips64") || !strcmp(arch, "parisc") || + !strcmp(arch, "parisc64") || !strcmp(arch, "m68k") || + !strcmp(arch, "armeb") || !strcmp(arch, "mipseb") || !strcmp(arch, "mips64eb")) + return true; + + return false; +} + int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) { int i; diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index bde192fd5be5..dd9907dbc345 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -175,6 +175,7 @@ void free_cpu_domain_info(struct cpu_domain_map **cd_map, u32 schedstat_version, void perf_env__exit(struct perf_env *env); int perf_env__kernel_is_64_bit(struct perf_env *env); +bool perf_arch_is_big_endian(const char *arch); int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index aac9cb75dcf4..ba33c0dfc18f 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -56,6 +56,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread__set_cpu(thread, -1); thread__set_guest_cpu(thread, -1); thread__set_e_machine(thread, EM_NONE); + thread__set_e_is_big_endian(thread, false); thread__set_lbr_stitch_enable(thread, false); INIT_LIST_HEAD(thread__namespaces_list(thread)); INIT_LIST_HEAD(thread__comm_list(thread)); @@ -429,7 +430,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, } } -static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags) +static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags, bool *is_big_endian) { char path[6 /* "/proc/" */ + 11 /* max length of pid */ + 5 /* "/exe\0" */]; int fd; @@ -438,7 +439,8 @@ static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags) snprintf(path, sizeof(path), "/proc/%d/exe", pid); fd = open(path, O_RDONLY); if (fd >= 0) { - e_machine = dso__read_e_machine(/*optional_dso=*/NULL, fd, e_flags); + e_machine = dso__read_e_machine_endian(/*optional_dso=*/NULL, fd, e_flags, + is_big_endian); close(fd); } return e_machine; @@ -448,6 +450,7 @@ struct thread__e_machine_callback_args { struct machine *machine; uint32_t e_flags; uint16_t e_machine; + bool is_big_endian; }; static int thread__e_machine_callback(struct map *map, void *_args) @@ -458,24 +461,38 @@ static int thread__e_machine_callback(struct map *map, void *_args) if (!dso) return 0; // No dso, continue search. - args->e_machine = dso__e_machine(dso, args->machine, &args->e_flags); + args->e_machine = + dso__e_machine_endian(dso, args->machine, &args->e_flags, &args->is_big_endian); return args->e_machine != EM_NONE ? 1 /* stop search */ : 0 /* continue search */; } -uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint32_t *e_flags) +uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine, uint32_t *e_flags, + bool *is_big_endian) { pid_t tid, pid; - uint16_t e_machine = RC_CHK_ACCESS(thread)->e_machine; + uint16_t e_machine; uint32_t local_e_flags = 0; - struct thread__e_machine_callback_args args = { - .machine = machine, - .e_flags = 0, - .e_machine = EM_NONE, - }; + struct thread__e_machine_callback_args args; + + if (!thread) { + if (is_big_endian) { + *is_big_endian = perf_arch_is_big_endian( + machine && machine->env ? perf_env__arch(machine->env) : NULL); + } + return perf_env__e_machine(machine ? machine->env : NULL, e_flags); + } + + e_machine = RC_CHK_ACCESS(thread)->e_machine; + args.machine = machine; + args.e_flags = 0; + args.e_machine = EM_NONE; + args.is_big_endian = false; if (e_machine != EM_NONE) { if (e_flags) *e_flags = thread__e_flags(thread); + if (is_big_endian) + *is_big_endian = thread__e_is_big_endian(thread); return e_machine; } @@ -483,6 +500,7 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3 struct maps *maps = thread__maps(thread); machine = maps__machine(maps); + args.machine = machine; } tid = thread__tid(thread); pid = thread__pid(thread); @@ -490,7 +508,8 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3 struct thread *parent = machine__findnew_thread(machine, pid, pid); if (parent) { - e_machine = thread__e_machine(parent, machine, &local_e_flags); + e_machine = thread__e_machine_endian(parent, machine, &local_e_flags, + &args.is_big_endian); thread__put(parent); goto out; } @@ -515,16 +534,27 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3 is_live = !!session->data; } /* Read from /proc/pid/exe if live. */ - if (is_live) - e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags); + if (is_live) { + e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags, + &args.is_big_endian); + } else if (machine && machine->env) { + /* Offline analysis: fallback to environment metadata. */ + e_machine = perf_env__e_machine(machine->env, &local_e_flags); + args.is_big_endian = perf_arch_is_big_endian(perf_env__arch(machine->env)); + } } out: if (e_machine != EM_NONE) { - thread__set_e_machine(thread, e_machine); thread__set_e_flags(thread, local_e_flags); + thread__set_e_is_big_endian(thread, args.is_big_endian); + thread__set_e_machine(thread, e_machine); + if (is_big_endian) + *is_big_endian = args.is_big_endian; } else { e_machine = EM_HOST; local_e_flags = EF_HOST; + if (is_big_endian) + *is_big_endian = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__); } if (e_flags) *e_flags = local_e_flags; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index f5792d3e8a16..d82fce8173ae 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -69,6 +69,11 @@ DECLARE_RC_STRUCT(thread) { * computed. */ uint16_t e_machine; + /** + * @e_is_big_endian: True if the ELF architecture of the thread is big endian. + * Valid if e_machine != EM_NONE. + */ + bool e_is_big_endian; /* LBR call stack stitch */ bool lbr_stitch_enable; struct lbr_stitch *lbr_stitch; @@ -311,7 +316,13 @@ static inline void thread__set_filter_entry_depth(struct thread *thread, int dep RC_CHK_ACCESS(thread)->filter_entry_depth = depth; } -uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint32_t *e_flags); +uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine, uint32_t *e_flags, + bool *is_big_endian); +static inline uint16_t thread__e_machine(struct thread *thread, struct machine *machine, + uint32_t *e_flags) +{ + return thread__e_machine_endian(thread, machine, e_flags, NULL); +} static inline void thread__set_e_machine(struct thread *thread, uint16_t e_machine) { @@ -328,6 +339,16 @@ static inline void thread__set_e_flags(struct thread *thread, uint32_t e_flags) RC_CHK_ACCESS(thread)->e_flags = e_flags; } +static inline bool thread__e_is_big_endian(const struct thread *thread) +{ + return RC_CHK_ACCESS(thread)->e_is_big_endian; +} + +static inline void thread__set_e_is_big_endian(struct thread *thread, bool is_big_endian) +{ + RC_CHK_ACCESS(thread)->e_is_big_endian = is_big_endian; +} + static inline bool thread__lbr_stitch_enable(const struct thread *thread) { From 12c4737f55f2fb4a0062258336435c69847a426c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:01 -0700 Subject: [PATCH 2732/5214] perf capstone: Determine architecture from e_machine Avoid the use of arch string that is imprecise and use the e_machine. Do more e_machine to capstone machine translations adding MIPS and RISCV. Remove unnecessary maybe_unused annotations. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/capstone.c | 132 ++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 44 deletions(-) diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c index 25cf6e15ec27..5ad537fea436 100644 --- a/tools/perf/util/capstone.c +++ b/tools/perf/util/capstone.c @@ -1,7 +1,19 @@ // SPDX-License-Identifier: GPL-2.0 #include "capstone.h" -#include "annotate.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + #include "addr_location.h" +#include "annotate.h" #include "debug.h" #include "disasm.h" #include "dso.h" @@ -11,13 +23,6 @@ #include "print_insn.h" #include "symbol.h" #include "thread.h" -#include -#include -#include -#include -#include - -#include #ifdef LIBCAPSTONE_DLOPEN static void *perf_cs_dll_handle(void) @@ -137,37 +142,70 @@ static enum cs_err perf_cs_close(csh *handle) #endif } -static int capstone_init(struct machine *machine, csh *cs_handle, bool is64, +static bool e_machine_to_capstone(uint16_t e_machine, bool is64, bool is_big_endian, + enum cs_arch *arch, enum cs_mode *mode) +{ + *mode = is_big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN; + + switch (e_machine) { + case EM_X86_64: + case EM_386: + *arch = CS_ARCH_X86; + *mode |= is64 ? CS_MODE_64 : CS_MODE_32; + return true; + case EM_AARCH64: + *arch = CS_ARCH_ARM64; + *mode |= CS_MODE_ARM; + return true; + case EM_ARM: + *arch = CS_ARCH_ARM; + *mode |= CS_MODE_ARM | CS_MODE_V8; + return true; + case EM_S390: + *arch = CS_ARCH_SYSZ; + return true; + case EM_MIPS: + *arch = CS_ARCH_MIPS; + *mode |= is64 ? CS_MODE_MIPS64 : CS_MODE_MIPS32; + return true; + case EM_PPC: + *arch = CS_ARCH_PPC; + return true; + case EM_PPC64: + *arch = CS_ARCH_PPC; + *mode |= CS_MODE_64; + return true; + case EM_SPARC: + *arch = CS_ARCH_SPARC; + return true; + case EM_SPARCV9: + *arch = CS_ARCH_SPARC; + *mode |= CS_MODE_V9; + return true; + case EM_RISCV: + *arch = CS_ARCH_RISCV; + *mode |= (is64 ? CS_MODE_RISCV64 : CS_MODE_RISCV32) | CS_MODE_RISCVC; + return true; + default: + return false; + } +} + +static int capstone_init(uint16_t e_machine, csh *cs_handle, bool is64, bool is_big_endian, bool disassembler_style) { enum cs_arch arch; enum cs_mode mode; - if (machine__is(machine, "x86_64") && is64) { - arch = CS_ARCH_X86; - mode = CS_MODE_64; - } else if (machine__normalized_is(machine, "x86")) { - arch = CS_ARCH_X86; - mode = CS_MODE_32; - } else if (machine__normalized_is(machine, "arm64")) { - arch = CS_ARCH_ARM64; - mode = CS_MODE_ARM; - } else if (machine__normalized_is(machine, "arm")) { - arch = CS_ARCH_ARM; - mode = CS_MODE_ARM + CS_MODE_V8; - } else if (machine__normalized_is(machine, "s390")) { - arch = CS_ARCH_SYSZ; - mode = CS_MODE_BIG_ENDIAN; - } else { + if (!e_machine_to_capstone(e_machine, is64, is_big_endian, &arch, &mode)) return -1; - } if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) { pr_warning_once("cs_open failed\n"); return -1; } - if (machine__normalized_is(machine, "x86")) { + if (arch == CS_ARCH_X86) { /* * In case of using capstone_init while symbol__disassemble * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts @@ -211,29 +249,28 @@ static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn * return printed; } - -ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused, - struct thread *thread __maybe_unused, - u8 cpumode __maybe_unused, bool is64bit __maybe_unused, - const uint8_t *code __maybe_unused, - size_t code_size __maybe_unused, - uint64_t ip __maybe_unused, int *lenp __maybe_unused, - int print_opts __maybe_unused, FILE *fp __maybe_unused) +ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode, + bool is64bit, const uint8_t *code, size_t code_size, uint64_t ip, + int *lenp, int print_opts, FILE *fp) { size_t printed; struct cs_insn *insn; csh cs_handle; size_t count; + bool is_big_endian = false; + uint16_t e_machine = thread__e_machine_endian(thread, machine, + /*e_flags=*/NULL, &is_big_endian); int ret; /* TODO: Try to initiate capstone only once but need a proper place. */ - ret = capstone_init(machine, &cs_handle, is64bit, true); + ret = capstone_init(e_machine, &cs_handle, is64bit, is_big_endian, + /*disassembler_style=*/true); if (ret < 0) return ret; count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn); if (count > 0) { - if (machine__normalized_is(machine, "x86")) + if (e_machine == EM_X86_64 || e_machine == EM_386) printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp); else printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str); @@ -322,9 +359,8 @@ static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg) return 0; } -int symbol__disassemble_capstone(const char *filename __maybe_unused, - struct symbol *sym __maybe_unused, - struct annotate_args *args __maybe_unused) +int symbol__disassemble_capstone(const char *filename, struct symbol *sym, + struct annotate_args *args) { struct annotation *notes = symbol__annotation(sym); struct map *map = args->ms->map; @@ -344,6 +380,8 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused, char disasm_buf[512]; struct disasm_line *dl; bool disassembler_style = false; + uint16_t e_machine; + bool is_big_endian = false; if (args->options->objdump_path) return -1; @@ -373,8 +411,10 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused, !strcmp(args->options->disassembler_style, "att")) disassembler_style = true; - if (capstone_init(maps__machine(thread__maps(args->ms->thread)), &handle, is_64bit, - disassembler_style) < 0) + e_machine = thread__e_machine_endian(args->ms->thread, + /*machine=*/NULL, + /*e_flags=*/NULL, &is_big_endian); + if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0) goto err; needs_cs_close = true; @@ -466,6 +506,8 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused, struct disasm_line *dl; u32 *line; bool disassembler_style = false; + uint16_t e_machine; + bool is_big_endian = false; if (args->options->objdump_path) return -1; @@ -484,8 +526,10 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused, !strcmp(args->options->disassembler_style, "att")) disassembler_style = true; - if (capstone_init(maps__machine(thread__maps(args->ms->thread)), &handle, is_64bit, - disassembler_style) < 0) + e_machine = thread__e_machine_endian(args->ms->thread, + /*machine=*/NULL, + /*e_flags=*/NULL, &is_big_endian); + if (capstone_init(e_machine, &handle, is_64bit, is_big_endian, disassembler_style) < 0) goto err; needs_cs_close = true; From c4e16f0543ff5df76e221141ab8e7430792bf841 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:02 -0700 Subject: [PATCH 2733/5214] perf print_insn: Use e_machine for fallback IP length check Avoid string comparisons with perf_env arch, switch to using the more precise ELF machine. Sort header files and fix missing definitions. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/print_insn.c | 23 ++++++++++++++--------- tools/perf/util/print_insn.h | 2 ++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/print_insn.c b/tools/perf/util/print_insn.c index 02e6fbb8ca04..4068436f26ea 100644 --- a/tools/perf/util/print_insn.c +++ b/tools/perf/util/print_insn.c @@ -4,19 +4,24 @@ * * Author(s): Changbin Du */ +#include "print_insn.h" + #include -#include #include +#include + +#include + #include "capstone.h" #include "debug.h" +#include "dso.h" +#include "dump-insn.h" +#include "env.h" +#include "machine.h" +#include "map.h" #include "sample.h" #include "symbol.h" -#include "machine.h" #include "thread.h" -#include "print_insn.h" -#include "dump-insn.h" -#include "map.h" -#include "dso.h" size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp) { @@ -33,13 +38,13 @@ size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp) static bool is64bitip(struct machine *machine, struct addr_location *al) { const struct dso *dso = al->map ? map__dso(al->map) : NULL; + uint16_t e_machine; if (dso) return dso__is_64_bit(dso); - return machine__is(machine, "x86_64") || - machine__normalized_is(machine, "arm64") || - machine__normalized_is(machine, "s390"); + e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL); + return e_machine == EM_X86_64 || e_machine == EM_AARCH64 || e_machine == EM_S390; } ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode, diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h index a54f7e858e49..cefa5c5f246e 100644 --- a/tools/perf/util/print_insn.h +++ b/tools/perf/util/print_insn.h @@ -5,6 +5,8 @@ #include #include +#include + struct addr_location; struct machine; struct perf_insn; From 0af46a17c86ec447404d1e1555973186d058b775 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:03 -0700 Subject: [PATCH 2734/5214] perf symbol: Avoid use of machine__is Switch to using the ELF machine from the dso or running machine rather than the machine perf_env arch that may fall back on EM_HOST. This also avoids potentially imprecise string comparisons. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 714b6e6048fa..2ce512f08a1d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -851,6 +851,23 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso) return count; } +static uint16_t machine_or_dso_e_machine(struct machine *machine, struct dso *dso) +{ + uint16_t e_machine = EM_NONE; + /* DSO should be most accurate */ + if (dso) + e_machine = dso__e_machine(dso, machine, /*e_flags=*/NULL); + + if (e_machine != EM_NONE) + return e_machine; + + /* Check the global environment next. */ + if (machine && machine->env && machine->env->e_machine != EM_NONE) + return machine->env->e_machine; + + return perf_env__e_machine(machine ? machine->env : NULL, /*e_flags=*/NULL); +} + /* * Split the symbols into maps, making sure there are no overlaps, i.e. the * kernel range is broken in several maps, named [kernel].N, as we don't have @@ -866,14 +883,13 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta, struct rb_root_cached *root = dso__symbols(dso); struct rb_node *next = rb_first_cached(root); int kernel_range = 0; - bool x86_64; + uint16_t e_machine = EM_NONE; if (!kmaps) return -1; machine = maps__machine(kmaps); - - x86_64 = machine__is(machine, "x86_64"); + e_machine = machine_or_dso_e_machine(machine, dso); while (next) { char *module; @@ -925,7 +941,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta, */ pos->start = map__map_ip(curr_map, pos->start); pos->end = map__map_ip(curr_map, pos->end); - } else if (x86_64 && is_entry_trampoline(pos->name)) { + } else if (e_machine == EM_X86_64 && is_entry_trampoline(pos->name)) { /* * These symbols are not needed anymore since the * trampoline maps refer to the text section and it's @@ -1428,7 +1444,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, free(new_node); } - if (machine__is(machine, "x86_64")) { + if (machine_or_dso_e_machine(machine, dso) == EM_X86_64) { u64 addr; /* @@ -1716,7 +1732,7 @@ int dso__load(struct dso *dso, struct map *map) ret = dso__load_guest_kernel_sym(dso, map); machine = maps__machine(map__kmaps(map)); - if (machine__is(machine, "x86_64")) + if (machine && machine_or_dso_e_machine(machine, dso) == EM_X86_64) machine__map_x86_64_entry_trampolines(machine, dso); goto out; } From dfe48498e6ab1fff4a27600450d073e117b0bd5f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:04 -0700 Subject: [PATCH 2735/5214] perf machine: Use perf_env e_machine rather than arch The arch string is derived from uname and may be normalized causing potential differences meaning the ELF machine can be more precise. Reduce the scope of machine__is as often it is better to use a thread for the e_machine rather than the machine. Switch from string to ELF machine constant comparisons. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 35 ++++++++++++++++++----------------- tools/perf/util/machine.h | 2 -- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e5d1e8b882a9..47be7a44a5f7 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -9,6 +9,7 @@ #include "debug.h" #include "dso.h" #include "env.h" +#include "dwarf-regs.h" #include "event.h" #include "evsel.h" #include "hist.h" @@ -1627,10 +1628,24 @@ static bool machine__uses_kcore(struct machine *machine) return dsos__for_each_dso(&machine->dsos, machine__uses_kcore_cb, NULL) != 0 ? true : false; } +static bool machine__is(struct machine *machine, uint16_t e_machine) +{ + if (!machine) + return false; + + if (!machine->env) { + if (machine__is_host(machine)) + return e_machine == EM_HOST; + return false; + } + + return perf_env__e_machine(machine->env, NULL) == e_machine; +} + static bool perf_event__is_extra_kernel_mmap(struct machine *machine, struct extra_kernel_map *xm) { - return machine__is(machine, "x86_64") && + return machine__is(machine, EM_X86_64) && is_entry_trampoline(xm->name); } @@ -2786,7 +2801,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, static u64 get_leaf_frame_caller(struct perf_sample *sample, struct thread *thread, int usr_idx) { - if (machine__normalized_is(maps__machine(thread__maps(thread)), "arm64")) + if (thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL) == EM_AARCH64) return get_leaf_frame_caller_aarch64(sample, thread, usr_idx); else return 0; @@ -3157,20 +3172,6 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, return 0; } -/* - * Compares the raw arch string. N.B. see instead perf_env__arch() or - * machine__normalized_is() if a normalized arch is needed. - */ -bool machine__is(struct machine *machine, const char *arch) -{ - return machine && !strcmp(perf_env__raw_arch(machine->env), arch); -} - -bool machine__normalized_is(struct machine *machine, const char *arch) -{ - return machine && !strcmp(perf_env__arch(machine->env), arch); -} - int machine__nr_cpus_avail(struct machine *machine) { return machine ? perf_env__nr_cpus_avail(machine->env) : 0; @@ -3197,7 +3198,7 @@ int machine__get_kernel_start(struct machine *machine) * start of kernel text, but still above 2^63. So leave * kernel_start = 1ULL << 63 for x86_64. */ - if (!err && !machine__is(machine, "x86_64")) + if (!err && !machine__is(machine, EM_X86_64)) machine->kernel_start = map__start(map); } return err; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 048b24e9bd38..aaddfb70ea66 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -224,8 +224,6 @@ static inline bool machine__is_host(struct machine *machine) } bool machine__is_lock_function(struct machine *machine, u64 addr); -bool machine__is(struct machine *machine, const char *arch); -bool machine__normalized_is(struct machine *machine, const char *arch); int machine__nr_cpus_avail(struct machine *machine); struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); From d08cb3fb0fbbb759c2e10747fec099290f2108ad Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:05 -0700 Subject: [PATCH 2736/5214] perf sample-raw: Use perf_env e_machine rather than arch Use the e_machine rather than the arch to determine S390 and x86 types. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sample-raw.c | 27 ++++++++++++++------------- tools/perf/util/sample-raw.h | 6 +++++- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/sample-raw.c b/tools/perf/util/sample-raw.c index bcf442574d6e..e20b73c0c5bd 100644 --- a/tools/perf/util/sample-raw.c +++ b/tools/perf/util/sample-raw.c @@ -1,11 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ - -#include -#include -#include "evlist.h" -#include "env.h" -#include "header.h" #include "sample-raw.h" + +#include +#include + +#include "env.h" +#include "evlist.h" +#include "header.h" #include "session.h" /* @@ -14,14 +15,14 @@ */ void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env *env) { - const char *arch_pf = perf_env__arch(env); - const char *cpuid = perf_env__cpuid(env); + uint16_t e_machine = perf_env__e_machine(env, /*e_flags=*/NULL); - if (arch_pf && !strcmp("s390", arch_pf)) + if (e_machine == EM_S390) { evlist->trace_event_sample_raw = evlist__s390_sample_raw; - else if (arch_pf && !strcmp("x86", arch_pf) && - cpuid && strstarts(cpuid, "AuthenticAMD") && - evlist__has_amd_ibs(evlist)) { - evlist->trace_event_sample_raw = evlist__amd_sample_raw; + } else if (e_machine == EM_X86_64 || e_machine == EM_386) { + const char *cpuid = perf_env__cpuid(env); + + if (cpuid && strstarts(cpuid, "AuthenticAMD") && evlist__has_amd_ibs(evlist)) + evlist->trace_event_sample_raw = evlist__amd_sample_raw; } } diff --git a/tools/perf/util/sample-raw.h b/tools/perf/util/sample-raw.h index 896e9a87e373..c8d38c841c8c 100644 --- a/tools/perf/util/sample-raw.h +++ b/tools/perf/util/sample-raw.h @@ -2,7 +2,10 @@ #ifndef __SAMPLE_RAW_H #define __SAMPLE_RAW_H 1 +#include + struct evlist; +struct perf_env; union perf_event; struct perf_sample; @@ -12,4 +15,5 @@ bool evlist__has_amd_ibs(struct evlist *evlist); void evlist__amd_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample); void evlist__init_trace_event_sample_raw(struct evlist *evlist, struct perf_env *env); -#endif /* __PERF_EVLIST_H */ + +#endif /* __SAMPLE_RAW_H */ From 0158eb0d121c1a6c6972c01ae237784caca8a367 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:06 -0700 Subject: [PATCH 2737/5214] perf sort: Use perf_env e_machine rather than arch Use the e_machine rather than the arch to determine x86 or PPC types. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 62 +++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0020089cb13c..90bc4a31bb55 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,40 +1,45 @@ // SPDX-License-Identifier: GPL-2.0 +#include "sort.h" + #include #include #include -#include #include + +#include +#include #include +#include #include + +#include + +#include "annotate-data.h" +#include "annotate.h" +#include "branch.h" +#include "cacheline.h" +#include "cgroup.h" +#include "comm.h" #include "debug.h" #include "dso.h" -#include "sort.h" -#include "hist.h" -#include "cacheline.h" -#include "comm.h" -#include "map.h" -#include "maps.h" -#include "symbol.h" -#include "map_symbol.h" -#include "branch.h" -#include "thread.h" -#include "evsel.h" +#include "event.h" #include "evlist.h" -#include "srcline.h" -#include "strlist.h" -#include "strbuf.h" +#include "evsel.h" +#include "hist.h" +#include "machine.h" +#include "map.h" +#include "map_symbol.h" +#include "maps.h" #include "mem-events.h" #include "mem-info.h" -#include "annotate.h" -#include "annotate-data.h" -#include "event.h" -#include "time-utils.h" -#include "cgroup.h" -#include "machine.h" #include "session.h" +#include "srcline.h" +#include "strbuf.h" +#include "strlist.h" +#include "symbol.h" +#include "thread.h" +#include "time-utils.h" #include "trace-event.h" -#include -#include #ifdef HAVE_LIBTRACEEVENT #include @@ -2673,9 +2678,10 @@ struct sort_dimension { static int arch_support_sort_key(const char *sort_key, struct perf_env *env) { - const char *arch = perf_env__arch(env); + uint16_t e_machine = perf_env__e_machine(env, /*e_eflags=*/NULL); - if (!strcmp("x86", arch) || !strcmp("powerpc", arch)) { + if (e_machine == EM_X86_64 || e_machine == EM_386 || e_machine == EM_PPC64 || + e_machine == EM_PPC) { if (!strcmp(sort_key, "p_stage_cyc")) return 1; if (!strcmp(sort_key, "local_p_stage_cyc")) @@ -2686,14 +2692,14 @@ static int arch_support_sort_key(const char *sort_key, struct perf_env *env) static const char *arch_perf_header_entry(const char *se_header, struct perf_env *env) { - const char *arch = perf_env__arch(env); + uint16_t e_machine = perf_env__e_machine(env, /*e_eflags=*/NULL); - if (!strcmp("x86", arch)) { + if (e_machine == EM_X86_64 || e_machine == EM_386) { if (!strcmp(se_header, "Local Pipeline Stage Cycle")) return "Local Retire Latency"; else if (!strcmp(se_header, "Pipeline Stage Cycle")) return "Retire Latency"; - } else if (!strcmp("powerpc", arch)) { + } else if (e_machine == EM_PPC64 || e_machine == EM_PPC) { if (!strcmp(se_header, "Local INSTR Latency")) return "Finish Cyc"; else if (!strcmp(se_header, "INSTR Latency")) From 82ed70f00ccce18bd68cc82b62a80bb5a7f44856 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:07 -0700 Subject: [PATCH 2738/5214] perf arch common: Use perf_env e_machine rather than arch Use the e_machine rather than arch string matching. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/common.c | 94 ++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index ad0cab830a4d..5ad50e331c55 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -1,13 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 +#include "common.h" + #include #include #include #include -#include -#include "common.h" -#include "../util/env.h" -#include "../util/debug.h" + #include +#include + +#include + +#include "../util/debug.h" +#include "../util/env.h" static const char *const arc_triplets[] = { "arc-linux-", @@ -141,11 +146,40 @@ static int lookup_triplets(const char *const *triplets, const char *name) return -1; } +static bool is_native_compatible(struct perf_env *env, uint16_t target, uint16_t host) +{ + if (target != host) { + /* A 64-bit host can natively disassemble its 32-bit compat architecture */ + if (host == EM_X86_64 && target == EM_386) + return true; + if (host == EM_AARCH64 && target == EM_ARM) + return true; + if (host == EM_PPC64 && target == EM_PPC) + return true; + if (host == EM_SPARCV9 && target == EM_SPARC) + return true; + return false; + } + + /* target == host case */ + if (target == EM_RISCV) { + bool target_is_64 = perf_env__kernel_is_64_bit(env); + bool host_is_64 = (sizeof(void *) == 8); + + /* 32-bit host cannot natively disassemble 64-bit target */ + if (!host_is_64 && target_is_64) + return false; + } + + return true; +} + static int perf_env__lookup_binutils_path(struct perf_env *env, const char *name, char **path) { int idx; - const char *arch = perf_env__arch(env), *cross_env; + uint16_t e_machine = perf_env__e_machine(env, /*e_flags=*/NULL); + const char *cross_env; const char *const *path_list; char *buf = NULL; @@ -153,7 +187,7 @@ static int perf_env__lookup_binutils_path(struct perf_env *env, * We don't need to try to find objdump path for native system. * Just use default binutils path (e.g.: "objdump"). */ - if (!strcmp(perf_env__arch(NULL), arch)) + if (is_native_compatible(env, e_machine, EM_HOST)) goto out; cross_env = getenv("CROSS_COMPILE"); @@ -170,30 +204,42 @@ static int perf_env__lookup_binutils_path(struct perf_env *env, zfree(&buf); } - if (!strcmp(arch, "arc")) + switch (e_machine) { + case EM_ARC: path_list = arc_triplets; - else if (!strcmp(arch, "arm")) + break; + case EM_ARM: path_list = arm_triplets; - else if (!strcmp(arch, "arm64")) + break; + case EM_AARCH64: path_list = arm64_triplets; - else if (!strcmp(arch, "powerpc")) + break; + case EM_PPC: + case EM_PPC64: path_list = powerpc_triplets; - else if (!strcmp(arch, "riscv32")) - path_list = riscv32_triplets; - else if (!strcmp(arch, "riscv64")) - path_list = riscv64_triplets; - else if (!strcmp(arch, "sh")) + break; + case EM_RISCV: + path_list = perf_env__kernel_is_64_bit(env) ? riscv64_triplets : riscv32_triplets; + break; + case EM_SH: path_list = sh_triplets; - else if (!strcmp(arch, "s390")) + break; + case EM_S390: path_list = s390_triplets; - else if (!strcmp(arch, "sparc")) + break; + case EM_SPARC: + case EM_SPARCV9: path_list = sparc_triplets; - else if (!strcmp(arch, "x86")) + break; + case EM_X86_64: + case EM_386: path_list = x86_triplets; - else if (!strcmp(arch, "mips")) + break; + case EM_MIPS: path_list = mips_triplets; - else { - ui__error("binutils for %s not supported.\n", arch); + break; + default: + ui__error("binutils for %s not supported.\n", perf_env__arch(env)); goto out_error; } @@ -202,7 +248,7 @@ static int perf_env__lookup_binutils_path(struct perf_env *env, ui__error("Please install %s for %s.\n" "You can add it to PATH, set CROSS_COMPILE or " "override the default using --%s.\n", - name, arch, name); + name, perf_env__arch(env), name); goto out_error; } @@ -237,7 +283,7 @@ int perf_env__lookup_objdump(struct perf_env *env, char **path) */ bool perf_env__single_address_space(struct perf_env *env) { - const char *arch = perf_env__arch(env); + uint16_t e_machine = perf_env__e_machine(env, /*e_flags=*/NULL); - return strcmp(arch, "s390") && strcmp(arch, "sparc"); + return e_machine != EM_SPARC && e_machine != EM_SPARCV9 && e_machine != EM_S390; } From 5d57371b9cd2a40e04df0104b62a4c302555d076 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:08 -0700 Subject: [PATCH 2739/5214] perf header: In print_pmu_caps use perf_env e_machine Switch from arch to e_machine in print_pmu_caps. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 220e7720fbdb..ecdac427d9c4 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2461,15 +2461,16 @@ static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) static void print_pmu_caps(struct feat_fd *ff, FILE *fp) { struct perf_env *env = &ff->ph->env; - struct pmu_caps *pmu_caps; + uint16_t e_machine = perf_env__e_machine(env, /*e_flags=*/NULL); for (int i = 0; i < env->nr_pmus_with_caps; i++) { - pmu_caps = &env->pmu_caps[i]; + struct pmu_caps *pmu_caps = &env->pmu_caps[i]; + __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps, pmu_caps->pmu_name); } - if (strcmp(perf_env__arch(env), "x86") == 0 && + if ((e_machine == EM_X86_64 || e_machine == EM_386) && perf_env__has_pmu_mapping(env, "ibs_op")) { char *max_precise = perf_env__find_pmu_cap(env, "cpu", "max_precise"); From 70b3c4f734bbfd5664cd6e4eb007c108bf2b1c43 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:09 -0700 Subject: [PATCH 2740/5214] perf c2c: Use perf_env e_machine rather than arch Use the e_machine rather than arch string matching for AARCH64. Add include of dwarf-regs.h in case the EM_AARCH64 isn't defined, sort the headers given this include. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index d3503be9350c..3dd45a550fdb 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -12,41 +12,45 @@ */ #include #include + +#include #include #include #include #include #include -#include #include -#include "debug.h" -#include "builtin.h" + +#include #include #include #include -#include "map_symbol.h" -#include "mem-events.h" -#include "session.h" -#include "hist.h" -#include "sort.h" -#include "tool.h" + +#include "builtin.h" #include "cacheline.h" #include "data.h" +#include "debug.h" #include "event.h" #include "evlist.h" #include "evsel.h" -#include "ui/browsers/hists.h" -#include "thread.h" -#include "mem2node.h" +#include "hist.h" +#include "map_symbol.h" +#include "mem-events.h" #include "mem-info.h" -#include "symbol.h" -#include "ui/ui.h" -#include "ui/progress.h" +#include "mem2node.h" #include "pmus.h" +#include "session.h" +#include "sort.h" #include "string2.h" -#include "util/util.h" -#include "util/symbol.h" +#include "symbol.h" +#include "thread.h" +#include "tool.h" +#include "ui/browsers/hists.h" +#include "ui/progress.h" +#include "ui/ui.h" #include "util/annotate.h" +#include "util/symbol.h" +#include "util/util.h" struct c2c_hists { struct hists hists; @@ -3203,7 +3207,7 @@ static int perf_c2c__report(int argc, const char **argv) * default display type. */ if (!display) { - if (!strcmp(perf_env__arch(env), "arm64")) + if (perf_env__e_machine(env, /*e_flags=*/NULL) == EM_AARCH64) display = "peer"; else display = "tot"; From 661b1d83ef9ee997019cbf6428d3029fb9e9b0af Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:10 -0700 Subject: [PATCH 2741/5214] perf lock-contention: Use perf_env e_machine rather than arch Use the e_machine rather than arch string matching for powerpc. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/lock-contention.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c index 92e7b7b572a2..119a7206f3cd 100644 --- a/tools/perf/util/lock-contention.c +++ b/tools/perf/util/lock-contention.c @@ -104,7 +104,8 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta struct map *kmap; struct symbol *sym; u64 ip; - const char *arch = perf_env__arch(machine->env); + uint16_t e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL); + bool is_powerpc = e_machine == EM_PPC64 || e_machine == EM_PPC; if (list_empty(&callstack_filters)) return true; @@ -125,8 +126,7 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta * incase first or second callstack index entry has 0 * address for powerpc. */ - if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") || - (i != 1 && i != 2)))) + if (!callstack || (!callstack[i] && (!is_powerpc || (i != 1 && i != 2)))) break; ip = callstack[i]; From 13d5660026b5415caea341380d68bc75960ba502 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:11 -0700 Subject: [PATCH 2742/5214] perf env: Refactor perf_env__arch_strerrno The previous approach maps an architecture string to a function pointer to a function that takes an int errno value and returns a string. The new approach takes an e_machine and an errno value and returns a string. As the only call site is in builtin-trace.c, the e_machine is already present and potentially more specific than the perf_env arch string that is a single global value. Since the errno-to-name mapping is now generated statically and no longer depends on libtraceevent, we can remove the HAVE_LIBTRACEEVENT guards entirely, making perf_env__arch_strerrno unconditionally available. The major complication in this approach is having the shell script that generates the C code map a linux directory name to the matching ELF machine constants. To ensure compatibility with older hosts that have older glibc versions, output fallback definitions for newer ELF machine constants (EM_AARCH64, EM_CSKY, EM_LOONGARCH) if they are not defined in the system . Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 7 +-- tools/perf/trace/beauty/Build | 1 + tools/perf/trace/beauty/arch_errno_names.sh | 53 +++++++++++++++++++-- tools/perf/util/env.c | 13 ++--- tools/perf/util/env.h | 6 +-- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2530b4035e4f..377f0a18b00e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3032,9 +3032,8 @@ static int trace__sys_exit(struct trace *trace, } else if (ret < 0) { errno_print: { char bf[STRERR_BUFSIZE]; - struct perf_env *env = evsel__env(evsel) ?: &trace->host_env; const char *emsg = str_error_r(-ret, bf, sizeof(bf)); - const char *e = perf_env__arch_strerrno(env, err); + const char *e = perf_env__arch_strerrno(e_machine, err); fprintf(trace->output, "-1 %s (%s)", e, emsg); } @@ -4921,7 +4920,9 @@ static size_t syscall__dump_stats(struct trace *trace, int e_machine, FILE *fp, for (e = 0; e < stats->max_errno; ++e) { if (stats->errnos[e] != 0) - fprintf(fp, "\t\t\t\t%s: %d\n", perf_env__arch_strerrno(trace->host->env, e + 1), stats->errnos[e]); + fprintf(fp, "\t\t\t\t%s: %d\n", + perf_env__arch_strerrno(e_machine, e + 1), + stats->errnos[e]); } } lines++; diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build index 996e63cdf765..bf9553f683f8 100644 --- a/tools/perf/trace/beauty/Build +++ b/tools/perf/trace/beauty/Build @@ -36,6 +36,7 @@ perf-y += signum.o perf-y += socket_type.o perf-y += waitid_options.o perf-util-y += arch_errno_names.o + perf-y += tracepoints/ ifdef SHELLCHECK diff --git a/tools/perf/trace/beauty/arch_errno_names.sh b/tools/perf/trace/beauty/arch_errno_names.sh index d48d8561a7bb..8751bfa4a2b2 100755 --- a/tools/perf/trace/beauty/arch_errno_names.sh +++ b/tools/perf/trace/beauty/arch_errno_names.sh @@ -52,21 +52,50 @@ process_arch() |IFS=, create_errno_lookup_func "$arch" } +arch_to_e_machine() +{ + case "$1" in + alpha) printf '\tcase EM_ALPHA:\n' ;; + arc) printf '\tcase EM_ARC:\n' ;; + arm) printf '\tcase EM_ARM:\n' ;; + arm64) printf '\tcase EM_AARCH64:\n' ;; + csky) printf '\tcase EM_CSKY:\n' ;; + hexagon) printf '\tcase EM_HEXAGON:\n' ;; + loongarch) printf '\tcase EM_LOONGARCH:\n' ;; + microblaze) printf '\tcase EM_MICROBLAZE:\n' ;; + mips) printf '\tcase EM_MIPS:\n' ;; + parisc) printf '\tcase EM_PARISC:\n' ;; + powerpc) printf '\tcase EM_PPC:\n\tcase EM_PPC64:\n' ;; + riscv) printf '\tcase EM_RISCV:\n' ;; + s390) printf '\tcase EM_S390:\n' ;; + sh) printf '\tcase EM_SH:\n' ;; + sparc) printf '\tcase EM_SPARC:\n\tcase EM_SPARCV9:\n' ;; + x86) printf '\tcase EM_386:\n\tcase EM_X86_64:\n' ;; + xtensa) printf '\tcase EM_XTENSA:\n' ;; + esac +} + create_arch_errno_table_func() { archlist="$1" default="$2" - printf 'arch_syscalls__strerrno_t *\n' - printf 'arch_syscalls__strerrno_function(const char *arch)\n' + printf 'const char *arch_syscalls__strerrno(uint16_t e_machine, int err);\n\n' + printf '__attribute__((unused)) const char *\n' + printf 'arch_syscalls__strerrno(uint16_t e_machine, int err)\n' printf '{\n' + printf '\tswitch (e_machine) {\n' for arch in $archlist; do arch_str=$(arch_string "$arch") - printf '\tif (!strcmp(arch, "%s"))\n' "$arch_str" - printf '\t\treturn errno_to_name__%s;\n' "$arch_str" + ems=$(arch_to_e_machine "$arch_str") + if [ -n "$ems" ]; then + printf '%s\n' "$ems" + printf '\t\treturn errno_to_name__%s(err);\n' "$arch_str" + fi done arch_str=$(arch_string "$default") - printf '\treturn errno_to_name__%s;\n' "$arch_str" + printf '\tdefault:\n\t\treturn errno_to_name__%s(err);\n' "$arch_str" + printf '\t}\n' printf '}\n' } @@ -74,6 +103,20 @@ cat < +#include +#include + +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif + +#ifndef EM_CSKY +#define EM_CSKY 252 +#endif + +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 +#endif EoHEADER diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index fae70b07ba8d..b41562fb06c6 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -851,16 +851,11 @@ const char *perf_env__arch(struct perf_env *env) return arch; } -const char *perf_env__arch_strerrno(struct perf_env *env __maybe_unused, int err __maybe_unused) -{ -#if defined(HAVE_LIBTRACEEVENT) - if (env->arch_strerrno == NULL) - env->arch_strerrno = arch_syscalls__strerrno_function(perf_env__arch(env)); +const char *arch_syscalls__strerrno(uint16_t e_machine, int err); - return env->arch_strerrno ? env->arch_strerrno(err) : "no arch specific strerrno function"; -#else - return "!HAVE_LIBTRACEEVENT"; -#endif +const char *perf_env__arch_strerrno(uint16_t e_machine, int err) +{ + return arch_syscalls__strerrno(e_machine, err); } const char *perf_env__cpuid(struct perf_env *env) diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index dd9907dbc345..5a917271ca0d 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -67,8 +67,6 @@ struct cpu_domain_map { struct domain_info **domains; }; -typedef const char *(arch_syscalls__strerrno_t)(int err); - struct perf_env { char *hostname; char *os_release; @@ -158,7 +156,6 @@ struct perf_env { */ bool enabled; } clock; - arch_syscalls__strerrno_t *arch_strerrno; }; enum perf_compress_type { @@ -191,8 +188,7 @@ void cpu_cache_level__free(struct cpu_cache_level *cache); uint16_t perf_env__e_machine_nocache(struct perf_env *env, uint32_t *e_flags); uint16_t perf_env__e_machine(struct perf_env *env, uint32_t *e_flags); const char *perf_env__arch(struct perf_env *env); -const char *perf_env__arch_strerrno(struct perf_env *env, int err); -arch_syscalls__strerrno_t *arch_syscalls__strerrno_function(const char *arch); +const char *perf_env__arch_strerrno(uint16_t e_machine, int err); const char *perf_env__cpuid(struct perf_env *env); const char *perf_env__raw_arch(struct perf_env *env); int perf_env__nr_cpus_avail(struct perf_env *env); From da976dd4842981fa9fe8bf736ec3d440fa9ce029 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:12 -0700 Subject: [PATCH 2743/5214] perf env: Remove unused perf_env__raw_arch The switch to using e_machine has made the perf_env__raw_arch function unused so remove it. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/env.c | 18 ------------------ tools/perf/util/env.h | 1 - 2 files changed, 19 deletions(-) diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index b41562fb06c6..28c54c58193e 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -470,19 +470,6 @@ int perf_env__read_cpuid(struct perf_env *env) return 0; } -static int perf_env__read_arch(struct perf_env *env) -{ - struct utsname uts; - - if (env->arch) - return 0; - - if (!uname(&uts)) - env->arch = strdup(uts.machine); - - return env->arch ? 0 : -ENOMEM; -} - static int perf_env__read_nr_cpus_avail(struct perf_env *env) { if (env->nr_cpus_avail == 0) @@ -601,11 +588,6 @@ int perf_env__read_core_pmu_caps(struct perf_env *env) return ret; } -const char *perf_env__raw_arch(struct perf_env *env) -{ - return env && !perf_env__read_arch(env) ? env->arch : "unknown"; -} - int perf_env__nr_cpus_avail(struct perf_env *env) { return env && !perf_env__read_nr_cpus_avail(env) ? env->nr_cpus_avail : 0; diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 5a917271ca0d..83e74328798f 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -190,7 +190,6 @@ uint16_t perf_env__e_machine(struct perf_env *env, uint32_t *e_flags); const char *perf_env__arch(struct perf_env *env); const char *perf_env__arch_strerrno(uint16_t e_machine, int err); const char *perf_env__cpuid(struct perf_env *env); -const char *perf_env__raw_arch(struct perf_env *env); int perf_env__nr_cpus_avail(struct perf_env *env); void perf_env__init(struct perf_env *env); From 0ff9718c266f9eb334674377501ccf37d8d351a4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:13 -0700 Subject: [PATCH 2744/5214] perf env: Add mutex to protect lazy environment initialization Introduce a mutex to 'struct perf_env' to safely protect lazy metadata setup, such as os_release or e_machine resolution, preventing concurrent initialization data races and memory leaks during multi-threaded profiling or symbol loading. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/env.c | 5 +++++ tools/perf/util/env.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 28c54c58193e..03d90a45992c 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -250,6 +250,8 @@ void perf_env__exit(struct perf_env *env) { int i, j; + mutex_destroy(&env->lock); + perf_env__purge_bpf(env); perf_env__purge_cgroups(env); zfree(&env->hostname); @@ -307,6 +309,7 @@ void perf_env__init(struct perf_env *env) init_rwsem(&env->bpf_progs.lock); #endif env->kernel_is_64_bit = -1; + mutex_init(&env->lock); } static void perf_env__init_kernel_mode(struct perf_env *env) @@ -1014,6 +1017,7 @@ bool x86__is_amd_cpu(void) struct perf_env env = { .total_mem = 0, }; bool is_amd; + perf_env__init(&env); perf_env__cpuid(&env); is_amd = perf_env__is_x86_amd_cpu(&env); perf_env__exit(&env); @@ -1036,6 +1040,7 @@ bool x86__is_intel_cpu(void) struct perf_env env = { .total_mem = 0, }; bool is_intel; + perf_env__init(&env); perf_env__cpuid(&env); is_intel = perf_env__is_x86_intel_cpu(&env); perf_env__exit(&env); diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 83e74328798f..6aaf80c640bd 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -6,6 +6,7 @@ #include #include "cpumap.h" #include "rwsem.h" +#include "mutex.h" struct perf_cpu_map; @@ -156,6 +157,8 @@ struct perf_env { */ bool enabled; } clock; + /* Protects lazy environment initialization (e.g. os_release, e_machine). */ + struct mutex lock; }; enum perf_compress_type { From b34c7c147bff19722f729d10370f00850d477eac Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:14 -0700 Subject: [PATCH 2745/5214] perf env: Add helper to lazily compute the os_release In live mode the os_release isn't being initialized, make a lazy initialization helper that assumes when the os_release isn't initialized this is live mode. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-bt.c | 2 +- tools/perf/util/data-convert-json.c | 6 +++-- tools/perf/util/env.c | 38 +++++++++++++++++++++++++++++ tools/perf/util/env.h | 1 + tools/perf/util/header.c | 16 ++++++++---- tools/perf/util/symbol.c | 4 +-- 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index b3f745cff2a7..cc51b8677c8e 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1414,7 +1414,7 @@ do { \ ADD("host", env->hostname); ADD("sysname", "Linux"); - ADD("release", env->os_release); + ADD("release", perf_env__os_release(env)); ADD("version", env->version); ADD("machine", env->arch); ADD("domain", "kernel"); diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index a7da93a7ff0e..c71dfb77c697 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -16,6 +16,7 @@ #include "linux/err.h" #include "util/auxtrace.h" #include "util/debug.h" +#include "util/env.h" #include "util/dso.h" #include "util/event.h" #include "util/evsel.h" @@ -272,7 +273,7 @@ static void output_headers(struct perf_session *session, struct convert_json *c) { struct stat st; const struct perf_header *header = &session->header; - const struct perf_env *env = perf_session__env(session); + struct perf_env *env = perf_session__env(session); int ret; int fd = perf_data__fd(session->data); int i; @@ -296,7 +297,8 @@ static void output_headers(struct perf_session *session, struct convert_json *c) output_json_key_format(out, true, 2, "feat-offset", "%" PRIu64, header->feat_offset); output_json_key_string(out, true, 2, "hostname", env->hostname); - output_json_key_string(out, true, 2, "os-release", env->os_release); + output_json_key_string(out, true, 2, "os-release", + perf_env__os_release(env)); output_json_key_string(out, true, 2, "arch", env->arch); if (env->cpu_desc) diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 03d90a45992c..c0e2b9d5f0b2 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -361,6 +361,44 @@ bool perf_arch_is_big_endian(const char *arch) return false; } +const char *perf_env__os_release(struct perf_env *env) +{ + struct utsname uts; + int ret; + const char *release; + + if (!env) + return perf_version_string; + + mutex_lock(&env->lock); + if (env->os_release) { + release = env->os_release; + goto out; + } + + /* + * If env->arch is set, this is an offline target environment. + * If the os_release is not populated in the file, we do not want + * to poison it with the host's release which would break guest checks. + */ + if (env->arch) { + release = NULL; + goto out; + } + + /* + * The os_release is being accessed but wasn't initialized from a data + * file, assume this is 'live' mode and use the release from uname. If + * uname or strdup fails then use the current perf tool version. + */ + ret = uname(&uts); + env->os_release = strdup(ret < 0 ? perf_version_string : uts.release); + release = env->os_release ?: perf_version_string; +out: + mutex_unlock(&env->lock); + return release; +} + int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) { int i; diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 6aaf80c640bd..7621d1f73b83 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -176,6 +176,7 @@ void perf_env__exit(struct perf_env *env); int perf_env__kernel_is_64_bit(struct perf_env *env); bool perf_arch_is_big_endian(const char *arch); +const char *perf_env__os_release(struct perf_env *env); int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index ecdac427d9c4..d7f41db7322c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -432,13 +432,19 @@ static int write_osrelease(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { struct utsname uts; - int ret; + const char *release = NULL; - ret = uname(&uts); - if (ret < 0) - return -1; + if (evlist->session) + release = perf_env__os_release(perf_session__env(evlist->session)); - return do_write_string(ff, uts.release); + if (!release) { + int ret = uname(&uts); + + if (ret < 0) + return -1; + release = uts.release; + } + return do_write_string(ff, release); } static int write_arch(struct feat_fd *ff, struct evlist *evlist) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 2ce512f08a1d..077d19af5240 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2225,7 +2225,7 @@ static int vmlinux_path__init(struct perf_env *env) { struct utsname uts; char bf[PATH_MAX]; - char *kernel_version; + const char *kernel_version; unsigned int i; vmlinux_path = malloc(sizeof(char *) * (ARRAY_SIZE(vmlinux_paths) + @@ -2242,7 +2242,7 @@ static int vmlinux_path__init(struct perf_env *env) return 0; if (env) { - kernel_version = env->os_release; + kernel_version = perf_env__os_release(env); } else { if (uname(&uts) < 0) goto out_fail; From 979e87ab1f258036aab9ed874aecede0163586e5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:15 -0700 Subject: [PATCH 2746/5214] perf symbol: Add setters for bitfields sharing a byte to avoid concurrent update issues A problem with putting bitfields into struct symbol is that other bits in the symbol could be updated concurrently and only one update to the underlying storage unit happen, leading to lost updates. To avoid this, use atomics to atomically read or set part of 16-bits of flags in the symbol. Add accessors to simplify this. The idle value has 3 values in preparation for a later change that will lazily update it. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 10 +-- tools/perf/builtin-kwork.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-sched.c | 4 +- tools/perf/builtin-top.c | 7 +- tools/perf/tests/symbols.c | 2 +- tools/perf/tests/vmlinux-kallsyms.c | 2 +- tools/perf/ui/browsers/annotate.c | 2 +- tools/perf/ui/browsers/map.c | 4 +- tools/perf/util/annotate.c | 5 +- tools/perf/util/auxtrace.c | 6 +- tools/perf/util/callchain.c | 4 +- tools/perf/util/dlfilter.c | 2 +- tools/perf/util/evsel_fprintf.c | 6 +- tools/perf/util/intel-pt.c | 2 +- tools/perf/util/libdw.c | 2 +- tools/perf/util/machine.c | 2 +- tools/perf/util/probe-event.c | 4 +- .../util/scripting-engines/trace-event-perl.c | 2 +- .../scripting-engines/trace-event-python.c | 4 +- tools/perf/util/sort.c | 8 +- tools/perf/util/srcline.c | 10 +-- tools/perf/util/symbol-elf.c | 3 +- tools/perf/util/symbol.c | 86 +++++++++++++++---- tools/perf/util/symbol.h | 78 +++++++++++++---- tools/perf/util/symbol_fprintf.c | 4 +- 26 files changed, 183 insertions(+), 80 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index d8cb1f562f69..75ffe31d03fe 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -549,13 +549,11 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, node = cursor->first; for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { - if (machine->single_address_space && - machine__kernel_ip(machine, node->ip)) - /* kernel IPs were added already */; - else if (node->ms.sym && node->ms.sym->inlined) - /* we can't handle inlined callchains */; - else + if (!(machine->single_address_space && + machine__kernel_ip(machine, node->ip)) && + !(node->ms.sym && symbol__inlined(node->ms.sym))) { inject->raw_callchain->ips[i++] = node->ip; + } node = node->next; } diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 110de3507d48..7b61168e01e9 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -770,7 +770,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork, if (sym) { if (!strcmp(sym->name, "__softirqentry_text_start") || !strcmp(sym->name, "__do_softirq")) - sym->ignore = 1; + symbol__set_ignore(sym, true); } callchain_cursor_advance(cursor); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cd052aa78132..6f044c3df893 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -752,7 +752,7 @@ static int hists__resort_cb(struct hist_entry *he, void *arg) struct report *rep = arg; struct symbol *sym = he->ms.sym; - if (rep->symbol_ipc && sym && !sym->annotate2) { + if (rep->symbol_ipc && sym && !symbol__is_annotate2(sym)) { struct evsel *evsel = hists_to_evsel(he->hists); symbol__annotate2(&he->ms, evsel, NULL); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 81833d169470..4de2baf03c50 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2387,7 +2387,7 @@ static void save_task_callchain(struct perf_sched *sched, if (!strcmp(sym->name, "schedule") || !strcmp(sym->name, "__schedule") || !strcmp(sym->name, "preempt_schedule")) - sym->ignore = 1; + symbol__set_ignore(sym, true); } callchain_cursor_advance(cursor); @@ -3048,7 +3048,7 @@ static size_t callchain__fprintf_folded(FILE *fp, struct callchain_node *node) list_for_each_entry(chain, &node->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; - if (chain->ms.sym && chain->ms.sym->ignore) + if (chain->ms.sym && symbol__ignore(chain->ms.sym)) continue; ret += fprintf(fp, "%s%s", first ? "" : sep, callchain_list__sym_name(chain, bf, sizeof(bf), diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c8474f7ac658..1211401616ee 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -186,8 +186,8 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip) "Please report to linux-kernel@vger.kernel.org\n", ip, dso__long_name(dso), dso__symtab_origin(dso), map__start(map), map__end(map), sym->start, sym->end, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w', sym->name, + symbol__binding(sym) == STB_GLOBAL ? 'g' : + symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name, err ? "[unknown]" : uts.machine, err ? "[unknown]" : uts.release, perf_version_string); if (use_browser <= 0) @@ -828,7 +828,8 @@ static void perf_event__process_sample(const struct perf_tool *tool, } } - if (al.sym == NULL || !al.sym->idle) { + if (al.sym == NULL || + !symbol__is_idle(al.sym, al.map ? map__dso(al.map) : NULL, machine->env)) { struct hists *hists = evsel__hists(sample->evsel); struct hist_entry_iter iter = { .sample = sample, diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c index f4ffe5804f40..c09e04f36035 100644 --- a/tools/perf/tests/symbols.c +++ b/tools/perf/tests/symbols.c @@ -125,7 +125,7 @@ static int test_dso(struct dso *dso) for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) { struct symbol *sym = rb_entry(nd, struct symbol, rb_node); - if (sym->type != STT_FUNC && sym->type != STT_GNU_IFUNC) + if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC) continue; /* Check for overlapping function symbols */ diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 524d46478364..7409abe4aa36 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -346,7 +346,7 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused * such as __indirect_thunk_end. */ continue; - } else if (is_ignored_symbol(sym->name, sym->type)) { + } else if (is_ignored_symbol(sym->name, symbol__type(sym))) { /* * Ignore hidden symbols, see scripts/kallsyms.c for the details */ diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index ea17e6d29a7e..e220c4dfc881 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -1185,7 +1185,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, if (dso__annotate_warned(dso)) return -1; - if (not_annotated || !sym->annotate2) { + if (not_annotated || !symbol__is_annotate2(sym)) { err = symbol__annotate2(ms, evsel, &browser.arch); if (err) { annotate_browser__symbol_annotate_error(&browser, err); diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index c61ba3174a24..075a575cdc5d 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -32,8 +32,8 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row) ui_browser__set_percent_color(browser, 0, current_entry); ui_browser__printf(browser, "%*" PRIx64 " %*" PRIx64 " %c ", mb->addrlen, sym->start, mb->addrlen, sym->end, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w'); + symbol__binding(sym) == STB_GLOBAL ? 'g' : + symbol__binding(sym) == STB_LOCAL ? 'l' : 'w'); width = browser->width - ((mb->addrlen * 2) + 4); if (width > 0) ui_browser__write_nstring(browser, sym->name, width); diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 470569745abe..02505222d8c2 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -236,7 +236,8 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms, h = annotated_source__histogram(src, evsel); if (h == NULL) { pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n", - __func__, __LINE__, sym->name, sym->start, addr, sym->end, sym->type == STT_FUNC); + __func__, __LINE__, sym->name, sym->start, addr, sym->end, + symbol__type(sym) == STT_FUNC); return -ENOMEM; } @@ -2223,7 +2224,7 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, annotation__init_column_widths(notes, sym); annotation__update_column_widths(notes); - sym->annotate2 = 1; + symbol__set_annotate2(sym, true); return 0; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index fcf564e0d777..5f4aa1701aef 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -2694,7 +2694,7 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt, { /* Same name, and global or the n'th found or any */ return !arch__compare_symbol_names(name, sym->name) && - ((!idx && sym->binding == STB_GLOBAL) || + ((!idx && symbol__binding(sym) == STB_GLOBAL) || (idx > 0 && ++*cnt == idx) || idx < 0); } @@ -2712,8 +2712,8 @@ static void print_duplicate_syms(struct dso *dso, const char *sym_name) if (dso_sym_match(sym, sym_name, &cnt, -1)) { pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", ++cnt, sym->start, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w', + symbol__binding(sym) == STB_GLOBAL ? 'g' : + symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name); near = true; } else if (near) { diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 5c2282051e39..8981ae879ebb 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -801,7 +801,7 @@ static enum match_result match_chain(struct callchain_cursor_node *node, * symbol start. Otherwise do a faster comparison based * on the symbol start address. */ - if (cnode->ms.sym->inlined || node->ms.sym->inlined) { + if (symbol__inlined(cnode->ms.sym) || symbol__inlined(node->ms.sym)) { match = match_chain_strings(cnode->ms.sym->name, node->ms.sym->name); if (match != MATCH_ERROR) @@ -1245,7 +1245,7 @@ char *callchain_list__sym_name(struct callchain_list *cl, int printed; if (cl->ms.sym) { - const char *inlined = cl->ms.sym->inlined ? " (inlined)" : ""; + const char *inlined = symbol__inlined(cl->ms.sym) ? " (inlined)" : ""; if (show_srcline && cl->srcline) printed = scnprintf(bf, bfsize, "%s %s%s", diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index dc31b5e7149e..e11e144af62b 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -56,7 +56,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al) d_al->symoff = al->addr - map__start(al->map) - sym->start; else d_al->symoff = 0; - d_al->sym_binding = sym->binding; + d_al->sym_binding = symbol__binding(sym); } else { d_al->sym = NULL; d_al->sym_start = 0; diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 5521d00bff2c..0f7a25500a44 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -146,7 +146,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, sym = node->ms.sym; map = node->ms.map; - if (sym && sym->ignore && print_skip_ignored) + if (sym && symbol__ignore(sym) && print_skip_ignored) goto next; printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); @@ -182,7 +182,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, addr_location__exit(&node_al); } - if (print_dso && (!sym || !sym->inlined)) + if (print_dso && (!sym || !symbol__inlined(sym))) printed += map__fprintf_dsoname_dsoff(map, print_dsoff, addr, fp); if (print_srcline) { @@ -192,7 +192,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, printed += map__fprintf_srcline(map, addr, "\n ", fp); } - if (sym && sym->inlined) + if (sym && symbol__inlined(sym)) printed += fprintf(fp, " (inlined)"); if (!print_oneline) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index b8add2b20033..56a9e439f5f8 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -3000,7 +3000,7 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip) start = dso__first_symbol(map__dso(map)); for (sym = start; sym; sym = dso__next_symbol(sym)) { - if (sym->binding == STB_GLOBAL && + if (symbol__binding(sym) == STB_GLOBAL && !strcmp(sym->name, "__switch_to")) { ip = map__unmap_ip(map, sym->start); if (ip >= map__start(map) && ip < map__end(map)) { diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 84713b2a7ad5..d5d2958902c0 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -130,7 +130,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args) return 0; abort_delete_sym: - if (inline_sym->inlined) + if (symbol__inlined(inline_sym)) symbol__delete(inline_sym); abort_enomem: args->err = -ENOMEM; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 47be7a44a5f7..da1ad58758af 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1095,7 +1095,7 @@ static u64 find_entry_trampoline(struct dso *dso) unsigned int i; for (; sym; sym = dso__next_symbol(sym)) { - if (sym->binding != STB_GLOBAL) + if (symbol__binding(sym) != STB_GLOBAL) continue; for (i = 0; i < ARRAY_SIZE(syms); i++) { if (!strcmp(sym->name, syms[i])) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 34b4badd2c14..11ae4a09412c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -416,7 +416,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo, map__for_each_symbol_by_name(map, pp->function, sym, idx) { if (uprobes) { address = sym->start; - if (sym->type == STT_GNU_IFUNC) + if (symbol__type(sym) == STT_GNU_IFUNC) pr_warning("Warning: The probe function (%s) is a GNU indirect function.\n" "Consider identifying the final function used at run time and set the probe directly on that.\n", pp->function); @@ -3189,7 +3189,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, for (j = 0; j < num_matched_functions; j++) { sym = syms[j]; - if (sym->type != STT_FUNC) + if (symbol__type(sym) != STT_FUNC) continue; /* There can be duplicated symbols in the map */ diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 7a18ea4b7d50..410dc4cd0600 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -303,7 +303,7 @@ static SV *perl_process_callchain(struct perf_sample *sample, } if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) || !hv_stores(sym, "end", newSVuv(node->ms.sym->end)) || - !hv_stores(sym, "binding", newSVuv(node->ms.sym->binding)) || + !hv_stores(sym, "binding", newSVuv(symbol__binding(node->ms.sym))) || !hv_stores(sym, "name", newSVpvn(node->ms.sym->name, node->ms.sym->namelen)) || !hv_stores(elem, "sym", newRV_noinc((SV*)sym))) { diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cee1f32d7022..8f832ae316ca 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -436,7 +436,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample, pydict_set_item_string_decref(pysym, "end", PyLong_FromUnsignedLongLong(node->ms.sym->end)); pydict_set_item_string_decref(pysym, "binding", - _PyLong_FromLong(node->ms.sym->binding)); + _PyLong_FromLong(symbol__binding(node->ms.sym))); pydict_set_item_string_decref(pysym, "name", _PyUnicode_FromStringAndSize(node->ms.sym->name, node->ms.sym->namelen)); @@ -1270,7 +1270,7 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym, tuple_set_d64(t, 1, dso__db_id(dso)); tuple_set_d64(t, 2, sym->start); tuple_set_d64(t, 3, sym->end); - tuple_set_s32(t, 4, sym->binding); + tuple_set_s32(t, 4, symbol__binding(sym)); tuple_set_string(t, 5, sym->name); call_object(tables->symbol_handler, t, "symbol_table"); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 90bc4a31bb55..005e7d85dc4a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -469,7 +469,7 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) if (sym_l == sym_r) return 0; - if (sym_l->inlined || sym_r->inlined) { + if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) { int ret = strcmp(sym_l->name, sym_r->name); if (ret) @@ -536,7 +536,7 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms, ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); if (sym && map) { - if (sym->type == STT_OBJECT) { + if (symbol__type(sym) == STT_OBJECT) { ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", ip - map__unmap_ip(map, sym->start)); @@ -544,7 +544,7 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms, ret += repsep_snprintf(bf + ret, size - ret, "%.*s", width - ret, sym->name); - if (sym->inlined) + if (symbol__inlined(sym)) ret += repsep_snprintf(bf + ret, size - ret, " (inlined)"); } @@ -1483,7 +1483,7 @@ static int _hist_entry__addr_snprintf(struct map_symbol *ms, ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); if (sym && map) { - if (sym->type == STT_OBJECT) { + if (symbol__type(sym) == STT_OBJECT) { ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", ip - map__unmap_ip(map, sym->start)); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 62884428fb5a..b082178c279b 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -113,16 +113,16 @@ struct symbol *new_inline_sym(struct dso *dso, /* ensure that we don't alias an inlined symbol, which could * lead to double frees in inline_node__delete */ - assert(!base_sym->inlined); + assert(!symbol__inlined(base_sym)); } else { /* create a fake symbol for the inline frame */ inline_sym = symbol__new(base_sym ? base_sym->start : 0, base_sym ? (base_sym->end - base_sym->start) : 0, - base_sym ? base_sym->binding : 0, - base_sym ? base_sym->type : 0, + base_sym ? symbol__binding(base_sym) : 0, + base_sym ? symbol__type(base_sym) : 0, funcname); if (inline_sym) - inline_sym->inlined = 1; + symbol__set_inlined(inline_sym, true); } free(demangled); @@ -440,7 +440,7 @@ void inline_node__clear_frames(struct inline_node *node) list_del_init(&ilist->list); zfree_srcline(&ilist->srcline); /* only the inlined symbols are owned by the list */ - if (ilist->symbol && ilist->symbol->inlined) + if (ilist->symbol && symbol__inlined(ilist->symbol)) symbol__delete(ilist->symbol); free(ilist); } diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 77e6dcba8fda..c5ed5e051976 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -350,7 +350,8 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr, sym = dso__find_symbol_nocache(dso, addr); /* Expecting the address to be an IFUNC or IFUNC alias */ - if (!sym || sym->start != addr || (sym->type != STT_GNU_IFUNC && !sym->ifunc_alias)) + if (!sym || sym->start != addr || + (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym))) return false; snprintf(buf, buf_sz, "%s@plt", sym->name); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 077d19af5240..ddd3106b03b1 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -50,7 +50,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map); -static bool symbol__is_idle(const char *name); +static bool symbol__compute_is_idle(const char *name); int vmlinux_path__nr_entries; char **vmlinux_path; @@ -163,24 +163,24 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) else if ((a == 0) && (b > 0)) return SYMBOL_B; - if (syma->type != symb->type) { - if (syma->type == STT_NOTYPE) + if (symbol__type(syma) != symbol__type(symb)) { + if (symbol__type(syma) == STT_NOTYPE) return SYMBOL_B; - if (symb->type == STT_NOTYPE) + if (symbol__type(symb) == STT_NOTYPE) return SYMBOL_A; } /* Prefer a non weak symbol over a weak one */ - a = syma->binding == STB_WEAK; - b = symb->binding == STB_WEAK; + a = symbol__binding(syma) == STB_WEAK; + b = symbol__binding(symb) == STB_WEAK; if (b && !a) return SYMBOL_A; if (a && !b) return SYMBOL_B; /* Prefer a global symbol over a non global one */ - a = syma->binding == STB_GLOBAL; - b = symb->binding == STB_GLOBAL; + a = symbol__binding(syma) == STB_GLOBAL; + b = symbol__binding(symb) == STB_GLOBAL; if (a && !b) return SYMBOL_A; if (b && !a) @@ -227,14 +227,14 @@ void symbols__fixup_duplicate(struct rb_root_cached *symbols) continue; if (choose_best_symbol(curr, next) == SYMBOL_A) { - if (next->type == STT_GNU_IFUNC) - curr->ifunc_alias = true; + if (symbol__type(next) == STT_GNU_IFUNC) + symbol__set_ifunc_alias(curr, true); rb_erase_cached(&next->rb_node, symbols); symbol__delete(next); goto again; } else { - if (curr->type == STT_GNU_IFUNC) - next->ifunc_alias = true; + if (symbol__type(curr) == STT_GNU_IFUNC) + symbol__set_ifunc_alias(next, true); nd = rb_next(&curr->rb_node); rb_erase_cached(&curr->rb_node, symbols); symbol__delete(curr); @@ -322,8 +322,8 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char * sym->start = start; sym->end = len ? start + len : start; - sym->type = type; - sym->binding = binding; + atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) | + (binding << SYMBOL_FLAG_BINDING_SHIFT)); sym->namelen = namelen - 1; pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", @@ -345,6 +345,49 @@ void symbol__delete(struct symbol *sym) free(((void *)sym) - symbol_conf.priv_size); } +void symbol__set_ignore(struct symbol *sym, bool ignore) +{ + if (ignore) + atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IGNORE); + else + atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IGNORE); +} + +void symbol__set_annotate2(struct symbol *sym, bool annotate2) +{ + if (annotate2) + atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATE2); + else + atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATE2); +} + +void symbol__set_inlined(struct symbol *sym, bool inlined) +{ + if (inlined) + atomic_fetch_or(&sym->flags, SYMBOL_FLAG_INLINED); + else + atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_INLINED); +} + +void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias) +{ + if (ifunc_alias) + atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IFUNC_ALIAS); + else + atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS); +} + +static void symbol__set_idle(struct symbol *sym, bool idle) +{ + uint16_t old_flags = atomic_load(&sym->flags); + uint16_t new_flags; + uint16_t idle_val = idle ? SYMBOL_IDLE__IDLE : SYMBOL_IDLE__NOT_IDLE; + + do { + new_flags = old_flags & ~SYMBOL_FLAG_IDLE_MASK; + new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT); + } while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags)); +} void symbols__delete(struct rb_root_cached *symbols) { struct symbol *pos; @@ -375,7 +418,7 @@ void __symbols__insert(struct rb_root_cached *symbols, */ if (name[0] == '.') name++; - sym->idle = symbol__is_idle(name); + symbol__set_idle(sym, symbol__compute_is_idle(name)); } while (*p != NULL) { @@ -717,11 +760,21 @@ int modules__parse(const char *filename, void *arg, return err; } +bool symbol__is_idle(struct symbol *sym, + const struct dso *dso __maybe_unused, + struct perf_env *env __maybe_unused) +{ + uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed); + uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT; + + return idle_val == SYMBOL_IDLE__IDLE; +} + /* * These are symbols in the kernel image, so make sure that * sym is from a kernel DSO. */ -static bool symbol__is_idle(const char *name) +static bool symbol__compute_is_idle(const char *name) { const char * const idle_symbols[] = { "acpi_idle_do_entry", @@ -2492,6 +2545,7 @@ void symbol__exit(void) { if (!symbol_conf.initialized) return; + strlist__delete(symbol_conf.bt_stop_list); strlist__delete(symbol_conf.sym_list); strlist__delete(symbol_conf.dso_list); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 95592779eb77..16a27074a474 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct map; struct maps; struct option; struct build_id; +struct perf_env; /* * Ignore kernel mapping symbols, matching kernel is_mapping_symbol() logic. @@ -58,6 +60,23 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, GElf_Shdr *shp, const char *name, size_t *idx); #endif +enum symbol_idle_kind { + SYMBOL_IDLE__UNKNOWN = 0, + SYMBOL_IDLE__NOT_IDLE = 1, + SYMBOL_IDLE__IDLE = 2, +}; + +#define SYMBOL_FLAG_TYPE_SHIFT 0 +#define SYMBOL_FLAG_TYPE_MASK (0xF << SYMBOL_FLAG_TYPE_SHIFT) +#define SYMBOL_FLAG_BINDING_SHIFT 4 +#define SYMBOL_FLAG_BINDING_MASK (0xF << SYMBOL_FLAG_BINDING_SHIFT) +#define SYMBOL_FLAG_IDLE_SHIFT 8 +#define SYMBOL_FLAG_IDLE_MASK (0x3 << SYMBOL_FLAG_IDLE_SHIFT) +#define SYMBOL_FLAG_IGNORE (1 << 10) +#define SYMBOL_FLAG_INLINED (1 << 11) +#define SYMBOL_FLAG_ANNOTATE2 (1 << 12) +#define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13) + /** * A symtab entry. When allocated this may be preceded by an annotation (see * symbol__annotation) and/or a browser_index (see symbol__browser_index). @@ -69,20 +88,7 @@ struct symbol { u64 end; /** Length of the string name. */ u16 namelen; - /** ELF symbol type as defined for st_info. E.g STT_OBJECT or STT_FUNC. */ - u8 type:4; - /** ELF binding type as defined for st_info. E.g. STB_WEAK or STB_GLOBAL. */ - u8 binding:4; - /** Set true for kernel symbols of idle routines. */ - u8 idle:1; - /** Resolvable but tools ignore it (e.g. idle routines). */ - u8 ignore:1; - /** Symbol for an inlined function. */ - u8 inlined:1; - /** Has symbol__annotate2 been performed. */ - u8 annotate2:1; - /** Symbol is an alias of an STT_GNU_IFUNC */ - u8 ifunc_alias:1; + _Atomic uint16_t flags; /** Architecture specific. Unused except on PPC where it holds st_other. */ u8 arch_sym; /** The name of length namelen associated with the symbol. */ @@ -92,6 +98,49 @@ struct symbol { void symbol__delete(struct symbol *sym); void symbols__delete(struct rb_root_cached *symbols); +static inline u8 symbol__type(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_TYPE_MASK) >> SYMBOL_FLAG_TYPE_SHIFT; +} + +static inline u8 symbol__binding(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_BINDING_MASK) >> SYMBOL_FLAG_BINDING_SHIFT; +} + +static inline bool symbol__ignore(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_IGNORE) != 0; +} + +static inline bool symbol__inlined(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_INLINED) != 0; +} + +static inline bool symbol__is_annotate2(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_ANNOTATE2) != 0; +} + +static inline bool symbol__ifunc_alias(const struct symbol *sym) +{ + return (atomic_load_explicit(&sym->flags, memory_order_relaxed) & + SYMBOL_FLAG_IFUNC_ALIAS) != 0; +} + +bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env); + +void symbol__set_ignore(struct symbol *sym, bool ignore); +void symbol__set_annotate2(struct symbol *sym, bool annotate2); +void symbol__set_inlined(struct symbol *sym, bool inlined); +void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias); + /* symbols__for_each_entry - iterate over symbols (rb_root) * * @symbols: the rb_root of symbols @@ -169,7 +218,6 @@ int filename__read_debuglink(const char *filename, char *debuglink, size_t size); bool filename__has_section(const char *filename, const char *sec); -struct perf_env; int symbol__init(struct perf_env *env); void symbol__exit(void); void symbol__elf_init(void); diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index 53e1af4ed9ac..4dc8d5761f52 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c @@ -11,8 +11,8 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp) { return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", sym->start, sym->end, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w', + symbol__binding(sym) == STB_GLOBAL ? 'g' : + symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name); } From 044462f6f64d16e9f51a695719a3248e277c1d4e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 08:25:16 -0700 Subject: [PATCH 2747/5214] perf symbol: Lazily compute idle Switch from an idle boolean to a helper symbol__is_idle function. In the function lazily compute whether a symbol is an idle function taking into consideration the kernel version and architecture of the machine. As symbols__insert no longer needs to know if a symbol is for the kernel, remove the argument. To protect against drop-filtering of legitimate setup, online, or hotplug management functions (such as intel_idle_init), x86 matches are strictly constrained to exact known run-loops (intel_idle, intel_idle_irq, mwait_idle, mwait_idle_with_hints). If the target environment OS release is unresolvable (such as on guest traces), default to treating psw_idle as idle to prevent false negatives and match legacy trace behavior safely. This change is inspired by mailing list discussion, particularly from Thomas Richter and Heiko Carstens : https://lore.kernel.org/lkml/20260219113850.354271-1-tmricht@linux.ibm.com/ Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Honglei Wang Cc: Jan Polensky Cc: Sumanth Korikkar Cc: Thomas Richter Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.c | 146 +++++++++++++++++++++++------------ tools/perf/util/symbol.h | 4 +- 3 files changed, 100 insertions(+), 52 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c5ed5e051976..186e6d92ac3d 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1734,7 +1734,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss, arch__sym_update(f, &sym); - __symbols__insert(dso__symbols(curr_dso), f, dso__kernel(dso)); + __symbols__insert(dso__symbols(curr_dso), f); nr++; } dso__put(curr_dso); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ddd3106b03b1..0c46b24ee098 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -50,7 +50,6 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map); -static bool symbol__compute_is_idle(const char *name); int vmlinux_path__nr_entries; char **vmlinux_path; @@ -379,7 +378,7 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias) static void symbol__set_idle(struct symbol *sym, bool idle) { - uint16_t old_flags = atomic_load(&sym->flags); + uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed); uint16_t new_flags; uint16_t idle_val = idle ? SYMBOL_IDLE__IDLE : SYMBOL_IDLE__NOT_IDLE; @@ -401,8 +400,7 @@ void symbols__delete(struct rb_root_cached *symbols) } } -void __symbols__insert(struct rb_root_cached *symbols, - struct symbol *sym, bool kernel) +void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym) { struct rb_node **p = &symbols->rb_root.rb_node; struct rb_node *parent = NULL; @@ -410,17 +408,6 @@ void __symbols__insert(struct rb_root_cached *symbols, struct symbol *s; bool leftmost = true; - if (kernel) { - const char *name = sym->name; - /* - * ppc64 uses function descriptors and appends a '.' to the - * start of every instruction address. Remove it. - */ - if (name[0] == '.') - name++; - symbol__set_idle(sym, symbol__compute_is_idle(name)); - } - while (*p != NULL) { parent = *p; s = rb_entry(parent, struct symbol, rb_node); @@ -437,7 +424,7 @@ void __symbols__insert(struct rb_root_cached *symbols, void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym) { - __symbols__insert(symbols, sym, false); + __symbols__insert(symbols, sym); } static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip) @@ -598,7 +585,7 @@ void dso__reset_find_symbol_cache(struct dso *dso) void dso__insert_symbol(struct dso *dso, struct symbol *sym) { - __symbols__insert(dso__symbols(dso), sym, dso__kernel(dso)); + __symbols__insert(dso__symbols(dso), sym); /* update the symbol cache if necessary */ if (dso__last_find_result_addr(dso) >= sym->start && @@ -760,57 +747,120 @@ int modules__parse(const char *filename, void *arg, return err; } -bool symbol__is_idle(struct symbol *sym, - const struct dso *dso __maybe_unused, - struct perf_env *env __maybe_unused) -{ - uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed); - uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT; - - return idle_val == SYMBOL_IDLE__IDLE; -} /* * These are symbols in the kernel image, so make sure that * sym is from a kernel DSO. */ -static bool symbol__compute_is_idle(const char *name) +static int sym_name_cmp(const void *a, const void *b) { - const char * const idle_symbols[] = { + const char *name = a; + const char *const *sym = b; + + return strcmp(name, *sym); +} + +static bool match_x86_idle_routine(const char *name, const char *base) +{ + if (strstarts(name, base)) { + size_t len = strlen(base); + + if (name[len] == '\0' || name[len] == '.') + return true; + } + return false; +} + +bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env) +{ + static const char * const idle_symbols[] = { "acpi_idle_do_entry", "acpi_processor_ffh_cstate_enter", "arch_cpu_idle", "cpu_idle", "cpu_startup_entry", - "idle_cpu", - "intel_idle", - "intel_idle_ibrs", "default_idle", - "native_safe_halt", "enter_idle", "exit_idle", - "mwait_idle", - "mwait_idle_with_hints", - "mwait_idle_with_hints.constprop.0", + "idle_cpu", + "native_safe_halt", "poll_idle", - "ppc64_runlatch_off", "pseries_dedicated_idle_sleep", - "psw_idle", - "psw_idle_exit", - NULL }; - int i; - static struct strlist *idle_symbols_list; + const char *name = sym->name; + uint16_t e_machine; - if (idle_symbols_list) - return strlist__has_entry(idle_symbols_list, name); + { + uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed); + uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT; - idle_symbols_list = strlist__new(NULL, NULL); + if (idle_val != SYMBOL_IDLE__UNKNOWN) + return idle_val == SYMBOL_IDLE__IDLE; + } - for (i = 0; idle_symbols[i]; i++) - strlist__add(idle_symbols_list, idle_symbols[i]); + if (!dso || dso__kernel(dso) == DSO_SPACE__USER) { + symbol__set_idle(sym, /*idle=*/false); + return false; + } - return strlist__has_entry(idle_symbols_list, name); + /* + * ppc64 uses function descriptors and appends a '.' to the + * start of every instruction address. Remove it. + */ + if (name[0] == '.') + name++; + + if (bsearch(name, idle_symbols, ARRAY_SIZE(idle_symbols), + sizeof(idle_symbols[0]), sym_name_cmp)) { + symbol__set_idle(sym, /*idle=*/true); + return true; + } + + e_machine = (env && env->arch) ? perf_env__e_machine(env, NULL) : EM_NONE; + if (e_machine == EM_NONE && dso) + e_machine = dso__e_machine((struct dso *)dso, NULL, NULL); + if (e_machine == EM_NONE && env) + e_machine = perf_env__e_machine(env, NULL); + + if (e_machine == EM_386 || e_machine == EM_X86_64) { + if (match_x86_idle_routine(name, "intel_idle") || + match_x86_idle_routine(name, "intel_idle_irq") || + match_x86_idle_routine(name, "intel_idle_ibrs") || + match_x86_idle_routine(name, "mwait_idle") || + match_x86_idle_routine(name, "mwait_idle_with_hints")) { + symbol__set_idle(sym, /*idle=*/true); + return true; + } + } + + if (e_machine == EM_PPC64 && !strcmp(name, "ppc64_runlatch_off")) { + symbol__set_idle(sym, /*idle=*/true); + return true; + } + + if (e_machine == EM_S390 && strstarts(name, "psw_idle")) { + int major = 0, minor = 0; + const char *release = env ? perf_env__os_release(env) : NULL; + + /* + * If we can't determine the release (e.g. unpopulated guest traces), + * default to idle. + */ + if (!release) { + symbol__set_idle(sym, /*idle=*/true); + return true; + } + + /* Before v6.10, s390 used psw_idle. */ + if (sscanf(release, "%d.%d", &major, &minor) == 2 && + (major < 6 || (major == 6 && minor < 10))) { + symbol__set_idle(sym, /*idle=*/true); + return true; + } + } + + symbol__set_idle(sym, /*idle=*/false); + return false; } static int map__process_kallsym_symbol(void *arg, const char *name, @@ -839,7 +889,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * We will pass the symbols to the filter later, in * map__split_kallsyms, when we have split the maps per module */ - __symbols__insert(root, sym, !strchr(name, '[')); + __symbols__insert(root, sym); return 0; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 16a27074a474..a71525335703 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -76,7 +76,6 @@ enum symbol_idle_kind { #define SYMBOL_FLAG_INLINED (1 << 11) #define SYMBOL_FLAG_ANNOTATE2 (1 << 12) #define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13) - /** * A symtab entry. When allocated this may be preceded by an annotation (see * symbol__annotation) and/or a browser_index (see symbol__browser_index). @@ -257,8 +256,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss); char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); -void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym, - bool kernel); +void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym); void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym); void symbols__fixup_duplicate(struct rb_root_cached *symbols); void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms); From b26bc0b97d8d1b7454b7f7ab64f1bda8d1cd4002 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Mon, 6 Apr 2026 16:57:55 -0400 Subject: [PATCH 2748/5214] power: supply: max17042_battery: use ModelCfg refresh on max17055 Unlike other models, max17055 doesn't require cell characterization data and operates on a smaller set of input variables (`DesignCap`, `VEmpty`, `IChgTerm`, and `ModelCfg`). Those values can be filled in through `max17042_override_por_values()`, but the refresh bit has to be set afterward in order to make them apply. Signed-off-by: Sebastian Krzyszkowiak Signed-off-by: Vincent Cloutier Link: https://patch.msgid.link/20260406205759.493288-8-vincent.cloutier@icloud.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 74 ++++++++++++++----------- include/linux/power/max17042_battery.h | 3 + 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 446f8926a8e0..abd276e13475 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -843,6 +843,9 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) { max17042_override_por(map, MAX17047_V_empty, config->vempty); } + + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + max17042_override_por(map, MAX17055_ModelCfg, config->model_cfg); } static int max17042_init_chip(struct max17042_chip *chip) @@ -851,45 +854,54 @@ static int max17042_init_chip(struct max17042_chip *chip) int ret; max17042_override_por_values(chip); + + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) { + regmap_write_bits(map, MAX17055_ModelCfg, + MAX17055_MODELCFG_REFRESH_BIT, + MAX17055_MODELCFG_REFRESH_BIT); + } + /* After Power up, the MAX17042 requires 500mS in order * to perform signal debouncing and initial SOC reporting */ msleep(500); - /* Initialize configuration */ - max17042_write_config_regs(chip); + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) { + /* Initialize configuration */ + max17042_write_config_regs(chip); - /* write cell characterization data */ - ret = max17042_init_model(chip); - if (ret) { - dev_err(chip->dev, "%s init failed\n", - __func__); - return -EIO; + /* write cell characterization data */ + ret = max17042_init_model(chip); + if (ret) { + dev_err(chip->dev, "%s init failed\n", + __func__); + return -EIO; + } + + ret = max17042_verify_model_lock(chip); + if (ret) { + dev_err(chip->dev, "%s lock verify failed\n", + __func__); + return -EIO; + } + /* write custom parameters */ + max17042_write_custom_regs(chip); + + /* update capacity params */ + max17042_update_capacity_regs(chip); + + /* delay must be atleast 350mS to allow VFSOC + * to be calculated from the new configuration + */ + msleep(350); + + /* reset vfsoc0 reg */ + max17042_reset_vfsoc0_reg(chip); + + /* load new capacity params */ + max17042_load_new_capacity_params(chip); } - ret = max17042_verify_model_lock(chip); - if (ret) { - dev_err(chip->dev, "%s lock verify failed\n", - __func__); - return -EIO; - } - /* write custom parameters */ - max17042_write_custom_regs(chip); - - /* update capacity params */ - max17042_update_capacity_regs(chip); - - /* delay must be atleast 350mS to allow VFSOC - * to be calculated from the new configuration - */ - msleep(350); - - /* reset vfsoc0 reg */ - max17042_reset_vfsoc0_reg(chip); - - /* load new capacity params */ - max17042_load_new_capacity_params(chip); - /* Init complete, Clear the POR bit */ regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0); return 0; diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 25dccf908d20..13aeab1597c6 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -24,6 +24,8 @@ #define MAX17042_CHARACTERIZATION_DATA_SIZE 48 +#define MAX17055_MODELCFG_REFRESH_BIT BIT(15) + enum max17042_register { MAX17042_STATUS = 0x00, MAX17042_VALRT_Th = 0x01, @@ -219,6 +221,7 @@ struct max17042_config_data { u16 full_soc_thresh; /* 0x13 */ u16 design_cap; /* 0x18 */ u16 ichgt_term; /* 0x1E */ + u16 model_cfg; /* 0xDB */ /* MG3 config */ u16 at_rate; /* 0x04 */ From 8eec545cde69e46e9a1d2b7d915ce4f5df85b3bd Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 7 Apr 2026 07:30:25 +0000 Subject: [PATCH 2749/5214] power: reset: linkstation-poweroff: fix use-after-free in the linkstation_poweroff_init() Move of_node_put(dn) after the of_match_node() call, which still needs the node pointer. The node reference is correctly released after use. Fixes: e2f471efe1d6 ("power: reset: linkstation-poweroff: prepare for new devices") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260407073025.271865-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel --- drivers/power/reset/linkstation-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c index 02f5fdb8ffc4..e56d75bfcc43 100644 --- a/drivers/power/reset/linkstation-poweroff.c +++ b/drivers/power/reset/linkstation-poweroff.c @@ -163,10 +163,10 @@ static int __init linkstation_poweroff_init(void) dn = of_find_matching_node(NULL, ls_poweroff_of_match); if (!dn) return -ENODEV; - of_node_put(dn); match = of_match_node(ls_poweroff_of_match, dn); cfg = match->data; + of_node_put(dn); dn = of_find_node_by_name(NULL, cfg->mdio_node_name); if (!dn) From 68d234144b7dffd1f50b07ba74d0d6e833ef43a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 14:33:38 +0200 Subject: [PATCH 2750/5214] power: supply: max17042: fix OF node reference imbalance The driver reuses the OF node of the parent multi-function device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the MFD and deregistering the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 0cd4f1f77ad4 ("power: supply: max17042: add platform driver variant") Cc: stable@vger.kernel.org # 6.14 Cc: Dzmitry Sankouski Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407123338.2677375-1-johan@kernel.org Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index abd276e13475..024fa38888f6 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1288,7 +1288,8 @@ static int max17042_platform_probe(struct platform_device *pdev) if (!i2c) return -EINVAL; - dev->of_node = dev->parent->of_node; + device_set_of_node_from_dev(dev, dev->parent); + id = platform_get_device_id(pdev); irq = platform_get_irq(pdev, 0); From a2c14ff63e0e02e3c832385e523e9cc81301171c Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Fri, 24 Apr 2026 09:10:13 +0800 Subject: [PATCH 2751/5214] power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cpcap_battery_detect_battery_type(), the reference to an nvmem device obtained via nvmem_device_find() is not released with nvmem_device_put() on the success or read-failure paths, causing a permanent reference leak. The driver’s retry logic on subsequent battery property reads can compound this leak, preventing the nvmem device from ever being freed. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: fd46821e85de ("power: supply: cpcap-battery: Add battery type auto detection for mapphone devices") Link: https://patch.msgid.link/20260424011013.879639-1-make24@iscas.ac.cn Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-battery.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 7b7bdce3162f..59c741993ef8 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -439,10 +439,13 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) if (IS_ERR_OR_NULL(nvmem)) { ddata->check_nvmem = true; dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n"); - } else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { - battery_id = 0; - ddata->check_nvmem = true; - dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } else { + if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { + battery_id = 0; + ddata->check_nvmem = true; + dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } + nvmem_device_put(nvmem); } switch (battery_id) { From 978cd6b2ad036168712aad8fca213385a5b15e2d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Jun 2026 16:54:27 +0100 Subject: [PATCH 2752/5214] KVM: arm64: Key CPTR_EL2.E0POE propagation on FEAT_S1POE We propagate CPTR_EL2.E0POE from a L1 into the L0 configuration, but we key this on the L1 guest supporting FEAT_S2POE. This is obviously wrong, as this bit is solely concerned with Stage-1 translation. Fix this by making the update depend on FEAT_S1POE. Fixes: cd931bd6093cb ("KVM: arm64: nv: Add additional trap setup for CPTR_EL2") Reviewed-by: Joey Gouly Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260602155430.2088142-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 98b2976837b1..4d814ae90613 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -141,7 +141,7 @@ static inline void __activate_cptr_traps_vhe(struct kvm_vcpu *vcpu) if (!(SYS_FIELD_GET(CPACR_EL1, ZEN, cptr) & BIT(0))) val &= ~CPACR_EL1_ZEN; - if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S2POE, IMP)) + if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1POE, IMP)) val |= cptr & CPACR_EL1_E0POE; val |= cptr & CPTR_EL2_TCPAC; From f41b481548cc263112b6da4a3b4869fcd35b4e45 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Jun 2026 16:54:28 +0100 Subject: [PATCH 2753/5214] KVM: arm64: Wire AT S1E1A in the system instruction handling table Despite having handling code for AT S1E1A, the instruction was never plugged into the system instruction table, leading to an exception being injected in the guest. If the guest is Linux and using the __kvm_at() helper, the exception is actually handled in the helper, and KVM continues more or less silently by reentering the guest. Not exactly what you'd expect. Fix this by plugging the emulation code where required. Fixes: ff987ffc0c18c ("KVM: arm64: nv: Add support for FEAT_ATS1A") Reviewed-by: Joey Gouly Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260602155430.2088142-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 148fc3400ea8..753fe30d322c 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -4217,6 +4217,7 @@ static struct sys_reg_desc sys_insn_descs[] = { SYS_INSN(AT_S1E0W, handle_at_s1e01), SYS_INSN(AT_S1E1RP, handle_at_s1e01), SYS_INSN(AT_S1E1WP, handle_at_s1e01), + SYS_INSN(AT_S1E1A, handle_at_s1e01), { SYS_DESC(SYS_DC_CSW), access_dcsw }, { SYS_DESC(SYS_DC_CGSW), access_dcgsw }, From a62b4226ae47202eb00306b576859131c4c7196e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Jun 2026 16:54:29 +0100 Subject: [PATCH 2754/5214] arm64: cpufeature: Expose ID_AA64ISAR2_EL1.ATS1A to KVM KVM needs to know if the HW implements FEAT_ATS1A in order to correctly sanitise HFGITR_EL2.ATS1E1A, which otherwise defaults to RES0 and AT S1E1A traps are handled as UNDEF. Solves this by exposing ID_AA64ISAR2_EL1.ATS1A to the rest of the kernel. Fixes: ff987ffc0c18c ("KVM: arm64: nv: Add support for FEAT_ATS1A") Reviewed-by: Joey Gouly Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260602155430.2088142-4-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kernel/cpufeature.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 6d53bb15cf7b..62b0d77217ee 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -266,6 +266,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { }; static const struct arm64_ftr_bits ftr_id_aa64isar2[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_ATS1A_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_LUT_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_CSSC_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_RPRFM_SHIFT, 4, 0), From 9f76b039a72d7e06374aa96862f0232ed53f7787 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:46 -0700 Subject: [PATCH 2755/5214] KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU notifier In the case that kvm_translate_vncr() races with an MMU notifier the early return does not release a reference on the faulted in PFN. Add the necessary call to kvm_release_faultin_page() for the unused PFN. Cc: stable@vger.kernel.org Fixes: 069a05e535496 ("KVM: arm64: nv: Handle VNCR_EL2-triggered faults") Reported-by: Sashiko (local):gemini-3.1-pro Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-2-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 883b6c1008fb..4fa82e96454d 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1326,8 +1326,10 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { - if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) + if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); return -EAGAIN; + } vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; From 5949004d7032767e8fde1e8c986a33f241b2a192 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:47 -0700 Subject: [PATCH 2756/5214] KVM: arm64: nv: Fully update VNCR fixmap state in kvm_translate_vncr() kvm_translate_vncr() first invalidates the pseudo-TLB entry and corresponding fixmap in anticipation of installing a new translation. While the fixmap invalidation does clear the mapping from host stage-1, it does not clear the L1_VNCR_MAPPED flag. Depending on the state of the VNCR TLB at vcpu_put(), this could potentially precipitate a BUG_ON() if vt->cpu is reset. Share a helper with kvm_vcpu_put_hw_mmu(), ensuring that KVM's view of the VNCR fixmap is in sync with the state of the VNCR TLB. Give it a slightly verbose name to make it obvious that it is meant to be used local to a CPU, unlike other VNCR TLB maintenance. Fixes: 069a05e535496 ("KVM: arm64: nv: Handle VNCR_EL2-triggered faults") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-3-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 4fa82e96454d..d0545144eaac 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -797,18 +797,24 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu) } } +static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu) +{ + if (!host_data_test_flag(L1_VNCR_MAPPED)) + return; + + BUG_ON(vcpu->arch.vncr_tlb->cpu != smp_processor_id()); + BUG_ON(is_hyp_ctxt(vcpu)); + + clear_fixmap(vncr_fixmap(vcpu->arch.vncr_tlb->cpu)); + vcpu->arch.vncr_tlb->cpu = -1; + host_data_clear_flag(L1_VNCR_MAPPED); + atomic_dec(&vcpu->kvm->arch.vncr_map_count); +} + void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu) { /* Unconditionally drop the VNCR mapping if we have one */ - if (host_data_test_flag(L1_VNCR_MAPPED)) { - BUG_ON(vcpu->arch.vncr_tlb->cpu != smp_processor_id()); - BUG_ON(is_hyp_ctxt(vcpu)); - - clear_fixmap(vncr_fixmap(vcpu->arch.vncr_tlb->cpu)); - vcpu->arch.vncr_tlb->cpu = -1; - host_data_clear_flag(L1_VNCR_MAPPED); - atomic_dec(&vcpu->kvm->arch.vncr_map_count); - } + this_cpu_reset_vncr_fixmap(vcpu); /* * Keep a reference on the associated stage-2 MMU if the vCPU is @@ -1282,7 +1288,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) * We also prepare the next walk wilst we're at it. */ scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { - invalidate_vncr(vt); + this_cpu_reset_vncr_fixmap(vcpu); + vt->valid = false; vt->wi = (struct s1_walk_info) { .regime = TR_EL20, From efa871f4a2517385295de2e3f786e4ae4ffa6e77 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:48 -0700 Subject: [PATCH 2757/5214] KVM: arm64: nv: Inject SEA TTW when desc update can't write to GPA Similar to the handling of descriptor reads, inject an SEA during TTW when the descriptor access fails for reasons other than a race, such as a read-only memslot or a bad HVA. Fixes: bff8aa213dee ("KVM: arm64: Implement HW access flag management in stage-1 SW PTW") Fixes: e4c7dfac2f1a ("KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-4-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 6 +++++- arch/arm64/kvm/nested.c | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 9f8f0ae8e86e..119a603e636e 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -521,8 +521,12 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, } ret = kvm_swap_s1_desc(vcpu, ipa, desc, new_desc, wi); - if (ret) + if (ret == -EAGAIN) return ret; + if (ret) { + fail_s1_walk(wr, ESR_ELx_FSC_SEA_TTW(level), false); + return ret; + } desc = new_desc; } diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index d0545144eaac..f8d3f3a72328 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -352,8 +352,13 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, if (new_desc != desc) { ret = swap_guest_s2_desc(vcpu, paddr, desc, new_desc, wi); - if (ret) + if (ret == -EAGAIN) return ret; + if (ret) { + out->esr = ESR_ELx_FSC_SEA_TTW(level); + out->desc = desc; + return 1; + } desc = new_desc; } From 699a2cc7f608145d55621e57828ccf6bfcb8d906 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:49 -0700 Subject: [PATCH 2758/5214] KVM: arm64: Restart instruction upon race in __kvm_at_s12() __kvm_at_s*() are expected to return -EAGAIN if the page table walk raced with a concurrent update to a page table descriptor, which is interpreted as a signal to restart the trapping instruction. While this mostly works, __kvm_at_s12() silently eats the return from __kvm_at_s1e01() and consumes an uninitialized PAR value. Propagate the nonzero return instead. Fixes: 92c6443222ca ("KVM: arm64: Propagate PTW errors up to AT emulation") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-5-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 119a603e636e..6cc5892023dd 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -1557,7 +1557,10 @@ int __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr) return 0; } - __kvm_at_s1e01(vcpu, op, vaddr); + ret = __kvm_at_s1e01(vcpu, op, vaddr); + if (ret) + return ret; + par = vcpu_read_sys_reg(vcpu, PAR_EL1); if (par & SYS_PAR_EL1_F) return 0; From d8839941df7de41fd4b02b7b7cdd0c46e5ba501e Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:50 -0700 Subject: [PATCH 2759/5214] KVM: arm64: nv: Restart stage-1 walk if stage-2 desc update fails kvm_walk_nested_s2() returns -EAGAIN as an indication that an underlying descriptor update fails due to a race. The expectation is that the caller restart translation, yet walk_s1() actually synthesizes an abort. Propagate the -EAGAIN return out of walk_s1(), relying on callers to restart the translation fetch. Fixes: e4c7dfac2f1a ("KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-6-oupton@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 6cc5892023dd..4d4285e60fce 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -423,6 +423,9 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, if (wi->s2) { ret = kvm_walk_nested_s2(vcpu, ipa, &s2_trans); + if (ret == -EAGAIN) + return ret; + if (ret) { fail_s1_walk(wr, (s2_trans.esr & ~ESR_ELx_FSC_LEVEL) | level, From b6c6b9260a92dacef6edef8e93bc2767b86b1dfe Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:50 +0400 Subject: [PATCH 2760/5214] power: supply: bq257xx: Fix VSYSMIN clamping logic The minimal system voltage (VSYSMIN) is meant to protect the battery from dangerous over-discharge. When the device tree provides a value for the minimum design voltage of the battery, the user should not be allowed to set a lower VSYSMIN, as that would defeat the purpose of this protection. Flip the clamping logic when setting VSYSMIN to ensure that battery design voltage is respected. Cc: stable@vger.kernel.org Fixes: 1cc017b7f9c7 ("power: supply: bq257xx: Add support for BQ257XX charger") Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-2-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq257xx_charger.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 02c7d8b61e82..7ca4ae610902 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -128,9 +128,8 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval) * @vsys: voltage value to set in uV. * * This function takes a requested minimum system voltage value, clamps - * it between the minimum supported value by the charger and a user - * defined minimum system value, and then writes the value to the - * appropriate register. + * it between the user defined minimum system value and the maximum supported + * value by the charger, and then writes the value to the appropriate register. * * Return: Returns 0 on success or error if an error occurs. */ @@ -139,7 +138,7 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys) unsigned int reg; int vsys_min = pdata->vsys_min; - vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min); + vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV); reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV); reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg); From 759aa7d62b55f5910d916b63b88b092395a8c2c9 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:51 +0400 Subject: [PATCH 2761/5214] power: supply: bq257xx: Make the default current limit a per-chip attribute Add a field for the default current limit to the bq257xx_info structure and use it instead of the hardcoded value in the probe function. This prepares the driver for allowing different electrical constraints for different chip variants. Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-3-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq257xx_charger.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 7ca4ae610902..39718223c3f9 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -18,6 +18,7 @@ struct bq257xx_chg; /** * struct bq257xx_chip_info - chip specific routines + * @default_iindpm_uA: default input current limit in microamps * @bq257xx_hw_init: init function for hw * @bq257xx_hw_shutdown: shutdown function for hw * @bq257xx_get_state: get and update state of hardware @@ -26,6 +27,7 @@ struct bq257xx_chg; * @bq257xx_set_iindpm: set maximum input current (in uA) */ struct bq257xx_chip_info { + int default_iindpm_uA; int (*bq257xx_hw_init)(struct bq257xx_chg *pdata); void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata); int (*bq257xx_get_state)(struct bq257xx_chg *pdata); @@ -627,6 +629,7 @@ static const struct power_supply_desc bq257xx_power_supply_desc = { }; static const struct bq257xx_chip_info bq25703_chip_info = { + .default_iindpm_uA = BQ25703_IINDPM_DEFAULT_UA, .bq257xx_hw_init = &bq25703_hw_init, .bq257xx_hw_shutdown = &bq25703_hw_shutdown, .bq257xx_get_state = &bq25703_get_state, @@ -675,7 +678,7 @@ static int bq257xx_parse_dt(struct bq257xx_chg *pdata, "input-current-limit-microamp", &pdata->iindpm_max); if (ret) - pdata->iindpm_max = BQ25703_IINDPM_DEFAULT_UA; + pdata->iindpm_max = pdata->chip->default_iindpm_uA; return 0; } From 528058841022e1bbe468b2246d4fc318881cf1cb Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:52 +0400 Subject: [PATCH 2762/5214] power: supply: bq257xx: Consistently use indirect get/set helpers Move the remaining get/set helper functions to indirect calls via the per-chip bq257xx_chip_info struct. This improves the consistency of the code and prepares the driver to support multiple chip variants with different register layouts and bit definitions. Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-4-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq257xx_charger.c | 30 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 39718223c3f9..0765673728e4 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -22,18 +22,30 @@ struct bq257xx_chg; * @bq257xx_hw_init: init function for hw * @bq257xx_hw_shutdown: shutdown function for hw * @bq257xx_get_state: get and update state of hardware + * @bq257xx_get_ichg: get maximum charge current (in uA) * @bq257xx_set_ichg: set maximum charge current (in uA) + * @bq257xx_get_vbatreg: get maximum charge voltage (in uV) * @bq257xx_set_vbatreg: set maximum charge voltage (in uV) + * @bq257xx_get_iindpm: get maximum input current (in uA) * @bq257xx_set_iindpm: set maximum input current (in uA) + * @bq257xx_get_cur: get battery current from ADC (in uA) + * @bq257xx_get_vbat: get battery voltage from ADC (in uV) + * @bq257xx_get_min_vsys: get minimum system voltage (in uV) */ struct bq257xx_chip_info { int default_iindpm_uA; int (*bq257xx_hw_init)(struct bq257xx_chg *pdata); void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata); int (*bq257xx_get_state)(struct bq257xx_chg *pdata); + int (*bq257xx_get_ichg)(struct bq257xx_chg *pdata, int *intval); int (*bq257xx_set_ichg)(struct bq257xx_chg *pdata, int ichg); + int (*bq257xx_get_vbatreg)(struct bq257xx_chg *pdata, int *intval); int (*bq257xx_set_vbatreg)(struct bq257xx_chg *pdata, int vbatreg); + int (*bq257xx_get_iindpm)(struct bq257xx_chg *pdata, int *intval); int (*bq257xx_set_iindpm)(struct bq257xx_chg *pdata, int iindpm); + int (*bq257xx_get_cur)(struct bq257xx_chg *pdata, int *intval); + int (*bq257xx_get_vbat)(struct bq257xx_chg *pdata, int *intval); + int (*bq257xx_get_min_vsys)(struct bq257xx_chg *pdata, int *intval); }; /** @@ -490,22 +502,22 @@ static int bq257xx_get_charger_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - return bq25703_get_iindpm(pdata, &val->intval); + return pdata->chip->bq257xx_get_iindpm(pdata, &val->intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: - return bq25703_get_chrg_volt(pdata, &val->intval); + return pdata->chip->bq257xx_get_vbatreg(pdata, &val->intval); case POWER_SUPPLY_PROP_CURRENT_NOW: - return bq25703_get_cur(pdata, &val->intval); + return pdata->chip->bq257xx_get_cur(pdata, &val->intval); case POWER_SUPPLY_PROP_VOLTAGE_NOW: - return bq25703_get_vbat(pdata, &val->intval); + return pdata->chip->bq257xx_get_vbat(pdata, &val->intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - return bq25703_get_ichg_cur(pdata, &val->intval); + return pdata->chip->bq257xx_get_ichg(pdata, &val->intval); case POWER_SUPPLY_PROP_VOLTAGE_MIN: - return bq25703_get_min_vsys(pdata, &val->intval); + return pdata->chip->bq257xx_get_min_vsys(pdata, &val->intval); case POWER_SUPPLY_PROP_USB_TYPE: val->intval = pdata->usb_type; @@ -633,9 +645,15 @@ static const struct bq257xx_chip_info bq25703_chip_info = { .bq257xx_hw_init = &bq25703_hw_init, .bq257xx_hw_shutdown = &bq25703_hw_shutdown, .bq257xx_get_state = &bq25703_get_state, + .bq257xx_get_ichg = &bq25703_get_ichg_cur, .bq257xx_set_ichg = &bq25703_set_ichg_cur, + .bq257xx_get_vbatreg = &bq25703_get_chrg_volt, .bq257xx_set_vbatreg = &bq25703_set_chrg_volt, + .bq257xx_get_iindpm = &bq25703_get_iindpm, .bq257xx_set_iindpm = &bq25703_set_iindpm, + .bq257xx_get_cur = &bq25703_get_cur, + .bq257xx_get_vbat = &bq25703_get_vbat, + .bq257xx_get_min_vsys = &bq25703_get_min_vsys, }; /** From 5d543bf02965d1c04064acfd5e97d1529268d86f Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:53 +0400 Subject: [PATCH 2763/5214] power: supply: bq257xx: Add fields for 'charging' and 'overvoltage' states The driver currently reports the 'charging' and 'overvoltage' states based on a logical expression in the get_charger_property() wrapper function. This doesn't scale well to other chip variants, which may have a different number and type of hardware reported conditions which fall into these broad power supply states. Move the logic for determining 'charging' and 'overvoltage' states into chip-specific accessors, which can be overridden by each variant as needed. This helps keep the get_charger_property() wrapper function chip-agnostic while allowing for new chip variants to be added bringing their own logic. Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-5-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq257xx_charger.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 0765673728e4..9c082865e745 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -54,8 +54,10 @@ struct bq257xx_chip_info { * @bq: parent MFD device * @charger: power supply device * @online: charger input is present + * @charging: charger is actively charging the battery * @fast_charge: charger is in fast charge mode * @pre_charge: charger is in pre-charge mode + * @overvoltage: overvoltage fault detected * @ov_fault: charger reports over voltage fault * @batoc_fault: charger reports battery over current fault * @oc_fault: charger reports over current fault @@ -71,8 +73,10 @@ struct bq257xx_chg { struct bq257xx_device *bq; struct power_supply *charger; bool online; + bool charging; bool fast_charge; bool pre_charge; + bool overvoltage; bool ov_fault; bool batoc_fault; bool oc_fault; @@ -106,8 +110,10 @@ static int bq25703_get_state(struct bq257xx_chg *pdata) pdata->online = reg & BQ25703_STS_AC_STAT; pdata->fast_charge = reg & BQ25703_STS_IN_FCHRG; pdata->pre_charge = reg & BQ25703_STS_IN_PCHRG; + pdata->charging = pdata->fast_charge || pdata->pre_charge; pdata->ov_fault = reg & BQ25703_STS_FAULT_ACOV; pdata->batoc_fault = reg & BQ25703_STS_FAULT_BATOC; + pdata->overvoltage = pdata->ov_fault || pdata->batoc_fault; pdata->oc_fault = reg & BQ25703_STS_FAULT_ACOC; return 0; @@ -478,14 +484,14 @@ static int bq257xx_get_charger_property(struct power_supply *psy, case POWER_SUPPLY_PROP_STATUS: if (!pdata->online) val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - else if (pdata->fast_charge || pdata->pre_charge) + else if (pdata->charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; else val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case POWER_SUPPLY_PROP_HEALTH: - if (pdata->ov_fault || pdata->batoc_fault) + if (pdata->overvoltage) val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; else if (pdata->oc_fault) val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; From c05383b9074d72d244d3dea34832eeb725317575 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 17:41:44 -0700 Subject: [PATCH 2764/5214] power: reset: st-poweroff: Use of_device_get_match_data() Use of_device_get_match_data() to fetch the reset syscfg data directly instead of open-coding an of_match_device() lookup. This also lets the driver drop the of_device.h include. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260519004144.626969-1-rosenp@gmail.com Signed-off-by: Sebastian Reichel --- drivers/power/reset/st-poweroff.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c index 85175066beea..2c0cedd18406 100644 --- a/drivers/power/reset/st-poweroff.c +++ b/drivers/power/reset/st-poweroff.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -73,15 +72,12 @@ static const struct of_device_id st_reset_of_match[] = { static int st_reset_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match; struct device *dev = &pdev->dev; - match = of_match_device(st_reset_of_match, dev); - if (!match) + st_restart_syscfg = (struct reset_syscfg *)of_device_get_match_data(dev); + if (!st_restart_syscfg) return -ENODEV; - st_restart_syscfg = (struct reset_syscfg *)match->data; - st_restart_syscfg->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); if (IS_ERR(st_restart_syscfg->regmap)) { From 0549c4c01338abe343a4ffaa4733475d0a85d38a Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 15 May 2026 21:50:41 +0300 Subject: [PATCH 2765/5214] power: supply: Remove unused jz4740-battery.h The last user was removed in commit aea12071d6fc ("power/supply: Drop obsolete JZ4740 driver") and replaced by a self-contained IIO-based driver. No file includes this header. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Costa Shulyupin Link: https://patch.msgid.link/20260515185043.1523363-1-costa.shul@redhat.com Signed-off-by: Sebastian Reichel --- include/linux/power/jz4740-battery.h | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 include/linux/power/jz4740-battery.h diff --git a/include/linux/power/jz4740-battery.h b/include/linux/power/jz4740-battery.h deleted file mode 100644 index 10da211678c8..000000000000 --- a/include/linux/power/jz4740-battery.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2009, Jiejing Zhang - */ - -#ifndef __JZ4740_BATTERY_H -#define __JZ4740_BATTERY_H - -struct jz_battery_platform_data { - struct power_supply_info info; - int gpio_charge; /* GPIO port of Charger state */ - int gpio_charge_active_low; -}; - -#endif From 347fcfcc0758cc7523f440f47594f9a5037c50c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 12:16:28 +0200 Subject: [PATCH 2766/5214] power: supply: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Luca Ceresoli # max77976_charger.c Link: https://patch.msgid.link/20260515101629.4132270-2-u.kleine-koenig@baylibre.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/adp5061.c | 2 +- drivers/power/supply/bq2415x_charger.c | 28 ++++---- drivers/power/supply/bq24190_charger.c | 16 ++--- drivers/power/supply/bq24257_charger.c | 8 +-- drivers/power/supply/bq24735-charger.c | 4 +- drivers/power/supply/bq2515x_charger.c | 6 +- drivers/power/supply/bq256xx_charger.c | 16 ++--- drivers/power/supply/bq25890_charger.c | 10 +-- drivers/power/supply/bq25980_charger.c | 8 +-- drivers/power/supply/bq27xxx_battery_i2c.c | 64 +++++++++---------- drivers/power/supply/cw2015_battery.c | 2 +- drivers/power/supply/ds2782_battery.c | 6 +- drivers/power/supply/lp8727_charger.c | 2 +- drivers/power/supply/ltc2941-battery-gauge.c | 10 +-- drivers/power/supply/ltc4162-l-charger.c | 8 +-- .../power/supply/max14656_charger_detector.c | 4 +- drivers/power/supply/max17040_battery.c | 18 +++--- drivers/power/supply/max17042_battery.c | 12 ++-- drivers/power/supply/max77976_charger.c | 2 +- drivers/power/supply/max8971_charger.c | 2 +- drivers/power/supply/mm8013.c | 4 +- drivers/power/supply/rt5033_battery.c | 2 +- drivers/power/supply/rt9455_charger.c | 2 +- drivers/power/supply/sbs-battery.c | 8 +-- drivers/power/supply/sbs-charger.c | 2 +- drivers/power/supply/sbs-manager.c | 4 +- drivers/power/supply/smb347-charger.c | 8 +-- drivers/power/supply/stc3117_fuel_gauge.c | 2 +- drivers/power/supply/ug3105_battery.c | 2 +- 29 files changed, 131 insertions(+), 131 deletions(-) diff --git a/drivers/power/supply/adp5061.c b/drivers/power/supply/adp5061.c index 458fd3024373..7d5754c24553 100644 --- a/drivers/power/supply/adp5061.c +++ b/drivers/power/supply/adp5061.c @@ -727,7 +727,7 @@ static int adp5061_probe(struct i2c_client *client) } static const struct i2c_device_id adp5061_id[] = { - { "adp5061" }, + { .name = "adp5061" }, { } }; MODULE_DEVICE_TABLE(i2c, adp5061_id); diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index b50a28b9dd38..9f801717bfb0 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -1738,20 +1738,20 @@ static void bq2415x_remove(struct i2c_client *client) } static const struct i2c_device_id bq2415x_i2c_id_table[] = { - { "bq2415x", BQUNKNOWN }, - { "bq24150", BQ24150 }, - { "bq24150a", BQ24150A }, - { "bq24151", BQ24151 }, - { "bq24151a", BQ24151A }, - { "bq24152", BQ24152 }, - { "bq24153", BQ24153 }, - { "bq24153a", BQ24153A }, - { "bq24155", BQ24155 }, - { "bq24156", BQ24156 }, - { "bq24156a", BQ24156A }, - { "bq24157s", BQ24157S }, - { "bq24158", BQ24158 }, - {}, + { .name = "bq2415x", .driver_data = BQUNKNOWN }, + { .name = "bq24150", .driver_data = BQ24150 }, + { .name = "bq24150a", .driver_data = BQ24150A }, + { .name = "bq24151", .driver_data = BQ24151 }, + { .name = "bq24151a", .driver_data = BQ24151A }, + { .name = "bq24152", .driver_data = BQ24152 }, + { .name = "bq24153", .driver_data = BQ24153 }, + { .name = "bq24153a", .driver_data = BQ24153A }, + { .name = "bq24155", .driver_data = BQ24155 }, + { .name = "bq24156", .driver_data = BQ24156 }, + { .name = "bq24156a", .driver_data = BQ24156A }, + { .name = "bq24157s", .driver_data = BQ24157S }, + { .name = "bq24158", .driver_data = BQ24158 }, + { } }; MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table); diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 55da91bacc3e..6700d578a98f 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -2308,14 +2308,14 @@ static const struct dev_pm_ops bq24190_pm_ops = { }; static const struct i2c_device_id bq24190_i2c_ids[] = { - { "bq24190", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] }, - { "bq24192", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] }, - { "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] }, - { "bq24193", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24193] }, - { "bq24196", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] }, - { "bq24296", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] }, - { "bq24297", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] }, - { }, + { .name = "bq24190", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] }, + { .name = "bq24192", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] }, + { .name = "bq24192i", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] }, + { .name = "bq24193", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24193] }, + { .name = "bq24196", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] }, + { .name = "bq24296", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] }, + { .name = "bq24297", .driver_data = (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24297] }, + { } }; MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 766eecb35694..72f1bfea8d54 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -1133,10 +1133,10 @@ static const struct bq2425x_chip_info bq24257_info = { }; static const struct i2c_device_id bq24257_i2c_ids[] = { - { "bq24250", (kernel_ulong_t)&bq24250_info }, - { "bq24251", (kernel_ulong_t)&bq24251_info }, - { "bq24257", (kernel_ulong_t)&bq24257_info }, - {} + { .name = "bq24250", .driver_data = (kernel_ulong_t)&bq24250_info }, + { .name = "bq24251", .driver_data = (kernel_ulong_t)&bq24251_info }, + { .name = "bq24257", .driver_data = (kernel_ulong_t)&bq24257_info }, + { } }; MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids); diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index 637e0da65f87..99abbaf0470f 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -489,8 +489,8 @@ static int bq24735_charger_probe(struct i2c_client *client) } static const struct i2c_device_id bq24735_charger_id[] = { - { "bq24735-charger" }, - {} + { .name = "bq24735-charger" }, + { } }; MODULE_DEVICE_TABLE(i2c, bq24735_charger_id); diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 437bff5bc420..0208358ebbe4 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -1137,9 +1137,9 @@ static const struct bq2515x_info bq25155 = { }; static const struct i2c_device_id bq2515x_i2c_ids[] = { - { "bq25150", (kernel_ulong_t)&bq25150 }, - { "bq25155", (kernel_ulong_t)&bq25155 }, - {} + { .name = "bq25150", .driver_data = (kernel_ulong_t)&bq25150 }, + { .name = "bq25155", .driver_data = (kernel_ulong_t)&bq25155 }, + { } }; MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids); diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index 563f512709b3..5a6634fde837 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1768,14 +1768,14 @@ static int bq256xx_probe(struct i2c_client *client) } static const struct i2c_device_id bq256xx_i2c_ids[] = { - { "bq25600", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] }, - { "bq25600d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] }, - { "bq25601", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] }, - { "bq25601d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] }, - { "bq25611d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] }, - { "bq25618", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] }, - { "bq25619", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] }, - {} + { .name = "bq25600", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] }, + { .name = "bq25600d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] }, + { .name = "bq25601", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] }, + { .name = "bq25601d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] }, + { .name = "bq25611d", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] }, + { .name = "bq25618", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] }, + { .name = "bq25619", .driver_data = (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] }, + { } }; MODULE_DEVICE_TABLE(i2c, bq256xx_i2c_ids); diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 868e86e1749b..c1c12a447178 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -1617,11 +1617,11 @@ static const struct dev_pm_ops bq25890_pm = { }; static const struct i2c_device_id bq25890_i2c_ids[] = { - { "bq25890" }, - { "bq25892" }, - { "bq25895" }, - { "bq25896" }, - {} + { .name = "bq25890" }, + { .name = "bq25892" }, + { .name = "bq25895" }, + { .name = "bq25896" }, + { } }; MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index 44af3a0c92e5..eced3aecc7d2 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -1266,10 +1266,10 @@ static int bq25980_probe(struct i2c_client *client) } static const struct i2c_device_id bq25980_i2c_ids[] = { - { "bq25980", BQ25980 }, - { "bq25975", BQ25975 }, - { "bq25960", BQ25960 }, - {}, + { .name = "bq25980", .driver_data = BQ25980 }, + { .name = "bq25975", .driver_data = BQ25975 }, + { .name = "bq25960", .driver_data = BQ25960 }, + { } }; MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 868e95f0887e..c4e7a9521d08 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -225,38 +225,38 @@ static void bq27xxx_battery_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id bq27xxx_i2c_id_table[] = { - { "bq27200", BQ27000 }, - { "bq27210", BQ27010 }, - { "bq27500", BQ2750X }, - { "bq27510", BQ2751X }, - { "bq27520", BQ2752X }, - { "bq27500-1", BQ27500 }, - { "bq27510g1", BQ27510G1 }, - { "bq27510g2", BQ27510G2 }, - { "bq27510g3", BQ27510G3 }, - { "bq27520g1", BQ27520G1 }, - { "bq27520g2", BQ27520G2 }, - { "bq27520g3", BQ27520G3 }, - { "bq27520g4", BQ27520G4 }, - { "bq27521", BQ27521 }, - { "bq27530", BQ27530 }, - { "bq27531", BQ27531 }, - { "bq27541", BQ27541 }, - { "bq27542", BQ27542 }, - { "bq27546", BQ27546 }, - { "bq27742", BQ27742 }, - { "bq27545", BQ27545 }, - { "bq27411", BQ27411 }, - { "bq27421", BQ27421 }, - { "bq27425", BQ27425 }, - { "bq27426", BQ27426 }, - { "bq27441", BQ27441 }, - { "bq27621", BQ27621 }, - { "bq27z561", BQ27Z561 }, - { "bq28z610", BQ28Z610 }, - { "bq34z100", BQ34Z100 }, - { "bq78z100", BQ78Z100 }, - {}, + { .name = "bq27200", .driver_data = BQ27000 }, + { .name = "bq27210", .driver_data = BQ27010 }, + { .name = "bq27500", .driver_data = BQ2750X }, + { .name = "bq27510", .driver_data = BQ2751X }, + { .name = "bq27520", .driver_data = BQ2752X }, + { .name = "bq27500-1", .driver_data = BQ27500 }, + { .name = "bq27510g1", .driver_data = BQ27510G1 }, + { .name = "bq27510g2", .driver_data = BQ27510G2 }, + { .name = "bq27510g3", .driver_data = BQ27510G3 }, + { .name = "bq27520g1", .driver_data = BQ27520G1 }, + { .name = "bq27520g2", .driver_data = BQ27520G2 }, + { .name = "bq27520g3", .driver_data = BQ27520G3 }, + { .name = "bq27520g4", .driver_data = BQ27520G4 }, + { .name = "bq27521", .driver_data = BQ27521 }, + { .name = "bq27530", .driver_data = BQ27530 }, + { .name = "bq27531", .driver_data = BQ27531 }, + { .name = "bq27541", .driver_data = BQ27541 }, + { .name = "bq27542", .driver_data = BQ27542 }, + { .name = "bq27546", .driver_data = BQ27546 }, + { .name = "bq27742", .driver_data = BQ27742 }, + { .name = "bq27545", .driver_data = BQ27545 }, + { .name = "bq27411", .driver_data = BQ27411 }, + { .name = "bq27421", .driver_data = BQ27421 }, + { .name = "bq27425", .driver_data = BQ27425 }, + { .name = "bq27426", .driver_data = BQ27426 }, + { .name = "bq27441", .driver_data = BQ27441 }, + { .name = "bq27621", .driver_data = BQ27621 }, + { .name = "bq27z561", .driver_data = BQ27Z561 }, + { .name = "bq28z610", .driver_data = BQ28Z610 }, + { .name = "bq34z100", .driver_data = BQ34Z100 }, + { .name = "bq78z100", .driver_data = BQ78Z100 }, + { } }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index 286524d2318c..7496295d2948 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -733,7 +733,7 @@ static int __maybe_unused cw_bat_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); static const struct i2c_device_id cw_bat_id_table[] = { - { "cw2015" }, + { .name = "cw2015" }, { } }; diff --git a/drivers/power/supply/ds2782_battery.c b/drivers/power/supply/ds2782_battery.c index cae95d35d398..ed44837473e7 100644 --- a/drivers/power/supply/ds2782_battery.c +++ b/drivers/power/supply/ds2782_battery.c @@ -423,9 +423,9 @@ static int ds278x_battery_probe(struct i2c_client *client) } static const struct i2c_device_id ds278x_id[] = { - {"ds2782", DS2782}, - {"ds2786", DS2786}, - {}, + { .name = "ds2782", .driver_data = DS2782 }, + { .name = "ds2786", .driver_data = DS2786 }, + { } }; MODULE_DEVICE_TABLE(i2c, ds278x_id); diff --git a/drivers/power/supply/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c index 4186fcd37512..641e0445a410 100644 --- a/drivers/power/supply/lp8727_charger.c +++ b/drivers/power/supply/lp8727_charger.c @@ -584,7 +584,7 @@ static const struct of_device_id lp8727_dt_ids[] __maybe_unused = { MODULE_DEVICE_TABLE(of, lp8727_dt_ids); static const struct i2c_device_id lp8727_ids[] = { - { "lp8727" }, + { .name = "lp8727" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8727_ids); diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index a1ddc4b060ce..5b6760722f38 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -600,11 +600,11 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume); static const struct i2c_device_id ltc294x_i2c_id[] = { - { "ltc2941", LTC2941_ID, }, - { "ltc2942", LTC2942_ID, }, - { "ltc2943", LTC2943_ID, }, - { "ltc2944", LTC2944_ID, }, - { }, + { .name = "ltc2941", .driver_data = LTC2941_ID }, + { .name = "ltc2942", .driver_data = LTC2942_ID }, + { .name = "ltc2943", .driver_data = LTC2943_ID }, + { .name = "ltc2944", .driver_data = LTC2944_ID }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); diff --git a/drivers/power/supply/ltc4162-l-charger.c b/drivers/power/supply/ltc4162-l-charger.c index 9dd74fa9552d..5c09e0368e6f 100644 --- a/drivers/power/supply/ltc4162-l-charger.c +++ b/drivers/power/supply/ltc4162-l-charger.c @@ -1229,10 +1229,10 @@ static void ltc4162l_alert(struct i2c_client *client, } static const struct i2c_device_id ltc4162l_i2c_id_table[] = { - { "ltc4015", (kernel_ulong_t)<c4015_chip_info }, - { "ltc4162-f", (kernel_ulong_t)<c4162f_chip_info }, - { "ltc4162-l", (kernel_ulong_t)<c4162l_chip_info }, - { "ltc4162-s", (kernel_ulong_t)<c4162s_chip_info }, + { .name = "ltc4015", .driver_data = (kernel_ulong_t)<c4015_chip_info }, + { .name = "ltc4162-f", .driver_data = (kernel_ulong_t)<c4162f_chip_info }, + { .name = "ltc4162-l", .driver_data = (kernel_ulong_t)<c4162l_chip_info }, + { .name = "ltc4162-s", .driver_data = (kernel_ulong_t)<c4162s_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table); diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c index a5b42b42d134..b6c3bc0d9ec1 100644 --- a/drivers/power/supply/max14656_charger_detector.c +++ b/drivers/power/supply/max14656_charger_detector.c @@ -300,8 +300,8 @@ static int max14656_probe(struct i2c_client *client) } static const struct i2c_device_id max14656_id[] = { - { "max14656" }, - {} + { .name = "max14656" }, + { } }; MODULE_DEVICE_TABLE(i2c, max14656_id); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 48453508688a..e94d53b36aa4 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -598,15 +598,15 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max17040_id[] = { - { "max17040", ID_MAX17040 }, - { "max17041", ID_MAX17041 }, - { "max17043", ID_MAX17043 }, - { "max77836-battery", ID_MAX17043 }, - { "max17044", ID_MAX17044 }, - { "max17048", ID_MAX17048 }, - { "max17049", ID_MAX17049 }, - { "max17058", ID_MAX17058 }, - { "max17059", ID_MAX17059 }, + { .name = "max17040", .driver_data = ID_MAX17040 }, + { .name = "max17041", .driver_data = ID_MAX17041 }, + { .name = "max17043", .driver_data = ID_MAX17043 }, + { .name = "max77836-battery", .driver_data = ID_MAX17043 }, + { .name = "max17044", .driver_data = ID_MAX17044 }, + { .name = "max17048", .driver_data = ID_MAX17048 }, + { .name = "max17049", .driver_data = ID_MAX17049 }, + { .name = "max17058", .driver_data = ID_MAX17058 }, + { .name = "max17059", .driver_data = ID_MAX17059 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, max17040_id); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 024fa38888f6..0fb46c1c203f 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1371,12 +1371,12 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match); #endif static const struct i2c_device_id max17042_id[] = { - { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, - { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, - { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, - { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, - { "max77759-fg", MAXIM_DEVICE_TYPE_MAX77759 }, - { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 }, + { .name = "max17042", .driver_data = MAXIM_DEVICE_TYPE_MAX17042 }, + { .name = "max17047", .driver_data = MAXIM_DEVICE_TYPE_MAX17047 }, + { .name = "max17050", .driver_data = MAXIM_DEVICE_TYPE_MAX17050 }, + { .name = "max17055", .driver_data = MAXIM_DEVICE_TYPE_MAX17055 }, + { .name = "max77759-fg", .driver_data = MAXIM_DEVICE_TYPE_MAX77759 }, + { .name = "max77849-battery", .driver_data = MAXIM_DEVICE_TYPE_MAX17047 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c index 3d6ff4005533..9a8a09d2a55e 100644 --- a/drivers/power/supply/max77976_charger.c +++ b/drivers/power/supply/max77976_charger.c @@ -484,7 +484,7 @@ static int max77976_probe(struct i2c_client *client) } static const struct i2c_device_id max77976_i2c_id[] = { - { MAX77976_DRIVER_NAME }, + { .name = MAX77976_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, max77976_i2c_id); diff --git a/drivers/power/supply/max8971_charger.c b/drivers/power/supply/max8971_charger.c index 26416d26f235..49a05858bef8 100644 --- a/drivers/power/supply/max8971_charger.c +++ b/drivers/power/supply/max8971_charger.c @@ -731,7 +731,7 @@ static const struct of_device_id max8971_match_ids[] = { MODULE_DEVICE_TABLE(of, max8971_match_ids); static const struct i2c_device_id max8971_i2c_id[] = { - { "max8971" }, + { .name = "max8971" }, { } }; MODULE_DEVICE_TABLE(i2c, max8971_i2c_id); diff --git a/drivers/power/supply/mm8013.c b/drivers/power/supply/mm8013.c index 93c50cff31bc..819667a27cad 100644 --- a/drivers/power/supply/mm8013.c +++ b/drivers/power/supply/mm8013.c @@ -284,8 +284,8 @@ static int mm8013_probe(struct i2c_client *client) } static const struct i2c_device_id mm8013_id_table[] = { - { "mm8013" }, - {} + { .name = "mm8013" }, + { } }; MODULE_DEVICE_TABLE(i2c, mm8013_id_table); diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index b2674adfa30b..63333f6aa819 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -174,7 +174,7 @@ static int rt5033_battery_probe(struct i2c_client *client) } static const struct i2c_device_id rt5033_battery_id[] = { - { "rt5033-battery", }, + { .name = "rt5033-battery" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c index 5130d2395e88..7045d2908148 100644 --- a/drivers/power/supply/rt9455_charger.c +++ b/drivers/power/supply/rt9455_charger.c @@ -1719,7 +1719,7 @@ static void rt9455_remove(struct i2c_client *client) } static const struct i2c_device_id rt9455_i2c_id_table[] = { - { RT9455_DRIVER_NAME }, + { .name = RT9455_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table); diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 43c48196c167..017ec06be766 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -1254,10 +1254,10 @@ static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL); #endif static const struct i2c_device_id sbs_id[] = { - { "bq20z65", SBS_FLAGS_TI_BQ20ZX5 }, - { "bq20z75", SBS_FLAGS_TI_BQ20ZX5 }, - { "sbs-battery", 0 }, - {} + { .name = "bq20z65", .driver_data = SBS_FLAGS_TI_BQ20ZX5 }, + { .name = "bq20z75", .driver_data = SBS_FLAGS_TI_BQ20ZX5 }, + { .name = "sbs-battery", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, sbs_id); diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c index 7d5e67620580..a00c710601e8 100644 --- a/drivers/power/supply/sbs-charger.c +++ b/drivers/power/supply/sbs-charger.c @@ -244,7 +244,7 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids); #endif static const struct i2c_device_id sbs_id[] = { - { "sbs-charger" }, + { .name = "sbs-charger" }, { } }; MODULE_DEVICE_TABLE(i2c, sbs_id); diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index 343ad4ab4082..9ad3fb117a4d 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -389,8 +389,8 @@ static int sbsm_probe(struct i2c_client *client) } static const struct i2c_device_id sbsm_ids[] = { - { "sbs-manager" }, - { "ltc1760" }, + { .name = "sbs-manager" }, + { .name = "ltc1760" }, { } }; MODULE_DEVICE_TABLE(i2c, sbsm_ids); diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 8b95f7e8712f..d9624b6a898c 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -1609,10 +1609,10 @@ static void smb347_shutdown(struct i2c_client *client) } static const struct i2c_device_id smb347_id[] = { - { "smb345", SMB345 }, - { "smb347", SMB347 }, - { "smb358", SMB358 }, - { }, + { .name = "smb345", .driver_data = SMB345 }, + { .name = "smb347", .driver_data = SMB347 }, + { .name = "smb358", .driver_data = SMB358 }, + { } }; MODULE_DEVICE_TABLE(i2c, smb347_id); diff --git a/drivers/power/supply/stc3117_fuel_gauge.c b/drivers/power/supply/stc3117_fuel_gauge.c index a1bc5970370a..469f2b359920 100644 --- a/drivers/power/supply/stc3117_fuel_gauge.c +++ b/drivers/power/supply/stc3117_fuel_gauge.c @@ -584,7 +584,7 @@ static int stc3117_probe(struct i2c_client *client) } static const struct i2c_device_id stc3117_id[] = { - { "stc3117", 0 }, + { .name = "stc3117", .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(i2c, stc3117_id); diff --git a/drivers/power/supply/ug3105_battery.c b/drivers/power/supply/ug3105_battery.c index 210e0f9aa5e0..0cbf45856fac 100644 --- a/drivers/power/supply/ug3105_battery.c +++ b/drivers/power/supply/ug3105_battery.c @@ -195,7 +195,7 @@ static SIMPLE_DEV_PM_OPS(ug3105_pm_ops, ug3105_suspend, ug3105_resume); static const struct i2c_device_id ug3105_id[] = { - { "ug3105" }, + { .name = "ug3105" }, { } }; MODULE_DEVICE_TABLE(i2c, ug3105_id); From 15384402c94a55b05d06a446aedd19e242367b6f Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Thu, 7 May 2026 22:18:40 +0300 Subject: [PATCH 2767/5214] power: supply: ab8500_fg: Fix typos in comments Fix spelling mistake in comments: - occured -> occurred (twice) Acked-by: Linus Walleij Signed-off-by: Md Shofiqul Islam Link: https://patch.msgid.link/20260507191840.25941-1-shofiqtest@gmail.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_fg.c | 2 +- drivers/power/supply/smb347-charger.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 9dd99722667a..eb5c1ae68e44 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2037,7 +2037,7 @@ static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di) } /** - * ab8500_fg_batt_ovv_handler() - Battery OVV occured + * ab8500_fg_batt_ovv_handler() - Battery OVV occurred * @irq: interrupt number * @_di: pointer to the ab8500_fg structure * diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index d9624b6a898c..c1b67dac8ef6 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -1083,7 +1083,7 @@ static int smb347_get_charging_status(struct smb347_charger *smb, } else { /* * in this case no charger error or termination - * occured but charging is not in progress!!! + * occurred but charging is not in progress!!! */ status = POWER_SUPPLY_STATUS_NOT_CHARGING; } From 43401898fde586bdeb73ac2c11d4f949fcba9f77 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:27:52 +0200 Subject: [PATCH 2768/5214] power: supply: cros_charge-control: Move MODULE_DEVICE_TABLE next to the table itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the privers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Acked-by: Thomas Weißschuh Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/20260505102752.182089-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/cros_charge-control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c index b489325be816..e1b8f3b1b7de 100644 --- a/drivers/power/supply/cros_charge-control.c +++ b/drivers/power/supply/cros_charge-control.c @@ -323,6 +323,7 @@ static const struct platform_device_id cros_chctl_id[] = { { .name = "cros-charge-control" }, { } }; +MODULE_DEVICE_TABLE(platform, cros_chctl_id); static struct platform_driver cros_chctl_driver = { .driver.name = "cros-charge-control", @@ -331,7 +332,6 @@ static struct platform_driver cros_chctl_driver = { }; module_platform_driver(cros_chctl_driver); -MODULE_DEVICE_TABLE(platform, cros_chctl_id); MODULE_DESCRIPTION("ChromeOS EC charge control"); MODULE_AUTHOR("Thomas Weißschuh "); MODULE_LICENSE("GPL"); From a9e36028b688d693d8aefbf84a9899f31a20fcf0 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 5 May 2026 13:05:18 +0300 Subject: [PATCH 2769/5214] power: supply: cpcap-charger: include missing This file uses dev_fwnode() without including the proper header for it, relying on transitive header inclusion from: drivers/power/supply/cpcap-charger.c - include/linux/phy/omap_usb.h - include/linux/usb/phy_companion.h - include/linux/usb/otg.h - include/linux/phy/phy.h - drivers/phy/phy-provider.h - include/linux/of.h - include/linux/property.h With the future removal of drivers/phy/phy-provider.h from include/linux/phy/phy.h, this transitive inclusion would break. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260505100523.1922388-27-vladimir.oltean@nxp.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index d0c3008db534..24221244b45b 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include From e92786dd86a24bf02ab4503856799192b7f073c7 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 4 May 2026 18:40:17 +0200 Subject: [PATCH 2770/5214] power: supply: bd71828: sysfs for auto input current limitation Add the possibility to disable the auto adjustment for input current limitation via sysfs because it gives strange results under certain circumstances e.g. when powering the device with solar panels resulting in no input power usage at all. Signed-off-by: Andreas Kemnade Reviewed-by: Matti Vaittinen Link: https://patch.msgid.link/20260504164017.467679-1-andreas@kemnade.info Signed-off-by: Sebastian Reichel --- .../ABI/testing/sysfs-class-power-bd71828 | 12 ++++ drivers/power/supply/bd71828-power.c | 69 ++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-class-power-bd71828 diff --git a/Documentation/ABI/testing/sysfs-class-power-bd71828 b/Documentation/ABI/testing/sysfs-class-power-bd71828 new file mode 100644 index 000000000000..2d451e1c8336 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-power-bd71828 @@ -0,0 +1,12 @@ +What: /sys/class/power_supply/bd71828_ac/auto_dcin_limit +Description: + Enable/Disable automatic management of input current limit + (ILIM_DCIN_EN bit). + + Possible values are: + + ============ =========================================== + 1 automatic adjustment of input current limit + 0 no adjustment of input current limit. This + helps for more unusual power sources like + solar modules. diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c index 5ab514ff5c65..b671563ead79 100644 --- a/drivers/power/supply/bd71828-power.c +++ b/drivers/power/supply/bd71828-power.c @@ -12,6 +12,7 @@ #include #include #include +#include /* common defines */ #define BD7182x_MASK_VBAT_U 0x1f @@ -25,6 +26,7 @@ #define BD71815_MASK_CONF_XSTB BIT(1) #define BD7182x_MASK_BAT_STAT 0x3f #define BD7182x_MASK_ILIM 0x3f +#define BD71828_MASK_ILIM_DCIN_EN BIT(6) #define BD7182x_MASK_DCIN_STAT 0x07 #define BD7182x_MASK_WDT_AUTO 0x40 @@ -1099,10 +1101,75 @@ static int bd7182x_get_rsens(struct bd71828_power *pwr) return 0; } +static ssize_t auto_dcin_limit_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bd71828_power *pwr = dev_get_drvdata(dev->parent); + int ret; + unsigned int v; + + ret = regmap_read(pwr->regmap, pwr->regs->dcin_set, &v); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", !!(v & BD71828_MASK_ILIM_DCIN_EN)); +} + +static ssize_t auto_dcin_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bd71828_power *pwr = dev_get_drvdata(dev->parent); + int ret; + bool v; + + ret = kstrtobool(buf, &v); + if (ret < 0) + return ret; + + ret = regmap_update_bits(pwr->regmap, BD71828_REG_DCIN_SET, + BD71828_MASK_ILIM_DCIN_EN, + v ? BD71828_MASK_ILIM_DCIN_EN : 0); + if (ret < 0) + return ret; + + return len; +} + +static DEVICE_ATTR_RW(auto_dcin_limit); + +static struct attribute *bd71828_ac_sysfs_attrs[] = { + &dev_attr_auto_dcin_limit.attr, + NULL, +}; + +static bool bd71828_ac_sysfs_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct bd71828_power *pwr = dev_get_drvdata(dev->parent); + + return !!pwr->regs->dcin_set; +} + +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(bd71828_ac_sysfs); + +static const struct attribute_group bd71828_ac_sysfs_group = { + .attrs = bd71828_ac_sysfs_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(bd71828_ac_sysfs) +}; + +static const struct attribute_group *bd71828_ac_sysfs_groups[] = { + &bd71828_ac_sysfs_group, + NULL +}; + static int bd71828_power_probe(struct platform_device *pdev) { struct bd71828_power *pwr; - struct power_supply_config ac_cfg = {}; + struct power_supply_config ac_cfg = { + .attr_grp = bd71828_ac_sysfs_groups, + }; struct power_supply_config bat_cfg = {}; int ret; From a80b32a140c8612bbaed27009c383d43304db6d5 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 7 May 2026 11:09:34 -0500 Subject: [PATCH 2771/5214] clk: keystone: don't cache clock rate The TISCI firmware will return 0 if the clock or consumer is not enabled although there is a stored value in the firmware. IOW a call to set rate will work but at get rate will always return 0 if the clock is disabled. The clk framework will try to cache the clock rate when it's requested by a consumer. If the clock or consumer is not enabled at that point, the cached value is 0, which is wrong. Thus, disable the cache altogether. Signed-off-by: Michael Walle Reviewed-by: Kevin Hilman Reviewed-by: Randolph Sapp Reviewed-by: Nishanth Menon Signed-off-by: Antonios Christidis Reviewed-by: Brian Masney Link: https://patch.msgid.link/20260507-clk-sci-v2-1-38f59b48777a@ti.com Signed-off-by: Nishanth Menon --- drivers/clk/keystone/sci-clk.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 9d5071223f4c..0a1565fdbb3b 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -333,6 +333,14 @@ static int _sci_clk_build(struct sci_clk_provider *provider, init.ops = &sci_clk_ops; init.num_parents = sci_clk->num_parents; + + /* + * A clock rate query to the SCI firmware will return 0 if either the + * clock itself is disabled or the attached device/consumer is disabled. + * This makes it inherently unsuitable for the caching of the clk + * framework. + */ + init.flags = CLK_GET_RATE_NOCACHE; sci_clk->hw.init = &init; ret = devm_clk_hw_register(provider->dev, &sci_clk->hw); From 2b0123e4a9257fa2933d13d1bca9ac36467efac1 Mon Sep 17 00:00:00 2001 From: Jing Yangyang Date: Tue, 12 May 2026 06:00:28 -0500 Subject: [PATCH 2772/5214] clk: keystone: sci-clk: fix application of sizeof to pointer Coccinelle (scripts/coccinelle/misc/noderef.cocci) reports: drivers/clk/keystone/sci-clk.c:391:8-14: ERROR: application of sizeof to pointer In sci_clk_get(), 'clk' is declared as 'struct sci_clk **', so sizeof(clk) is sizeof(struct sci_clk **) which is the size of a pointer rather than the size of an array element. provider->clocks is an array of 'struct sci_clk *', so the canonical size argument to bsearch() is sizeof(*clk) (i.e. sizeof(struct sci_clk *)). The two values are equal on every supported architecture, so this is correctness/idiom, not a runtime fix, but the new form matches the rest of the bsearch() callers in the tree and silences the Coccinelle warning the script flagged. Reported-by: Zeal Robot Closes: https://lore.kernel.org/all/84a6ba16686347099a3dab2e5161a930e792eb6e.1629198281.git.jing.yangyang@zte.com.cn/ Reported-by: kernel test robot Reported-by: Julia Lawall Closes: https://lore.kernel.org/all/202512040525.zrHSDl5h-lkp@intel.com/ Link: https://lore.kernel.org/linux-clk/20211012021931.176727-1-davidcomponentone@gmail.com/ Reviewed-by: Stepan Ionichev Reviewed-by: Andrew Davis Signed-off-by: Jing Yangyang Signed-off-by: David Yang [nm@ti.com: Improved commit message] Reviewed-by: Brian Masney Link: https://patch.msgid.link/20260512110028.2999471-1-nm@ti.com Signed-off-by: Nishanth Menon --- drivers/clk/keystone/sci-clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 0a1565fdbb3b..aed8dba29126 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -396,7 +396,7 @@ static struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data) key.clk_id = clkspec->args[1]; clk = bsearch(&key, provider->clocks, provider->num_clocks, - sizeof(clk), _cmp_sci_clk); + sizeof(*clk), _cmp_sci_clk); if (!clk) return ERR_PTR(-ENODEV); From 503c5ae1e72aa9ed91925dafa3d82ee2e992747f Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 3 Jun 2026 17:59:57 +0800 Subject: [PATCH 2773/5214] thunderbolt: debugfs: Fix margining error counter buffer leak When USB4 lane margining debugfs write support is enabled, margining_error_counter_write() copies the user input with validate_and_copy_from_user(). This allocates a temporary page that is only needed while parsing the requested error counter mode. The function currently returns without freeing that page. This leaks one page per write to the error_counter debugfs file, including successful writes and writes that later fail while taking the domain lock or because software margining is not enabled. Free the temporary page once parsing has completed, and also before returning from the invalid-input path. Fixes: 10904df3f20c ("thunderbolt: Improve software receiver lane margining") Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 569163a26afa..369904287497 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -956,7 +956,9 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, else if (!strcmp(buf, "stop")) error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; else - return -EINVAL; + goto err_free; + + free_page((unsigned long)buf); scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { if (!margining->software) @@ -966,6 +968,10 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, } return count; + +err_free: + free_page((unsigned long)buf); + return -EINVAL; } static int margining_error_counter_show(struct seq_file *s, void *not_used) From ce31a1ee2a1ed61f6d42308633f9bed717f5348b Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Mon, 25 May 2026 01:36:42 +0000 Subject: [PATCH 2774/5214] RISC-V: KVM: Document a TOCTOU race in SBI system suspend handler The SUSP handler checks that all other vCPUs are stopped before entering system suspend, but a concurrent HSM HART_START can start a vCPU after it has already passed the check. This is a known TOCTOU race. We do not fix it because: 1. Triggering it requires a pathological guest. 2. Only guest state is at risk, not host integrity. 3. Userspace can double-check vCPU states before suspend. Add a comment documenting the race and the rationale for not fixing it. Signed-off-by: Jiakai Xu Signed-off-by: Jiakai Xu Assisted-by: YuanSheng:DeepSeek-V3.2 Reviewed-by: Andrew Jones Link: https://lore.kernel.org/r/20260525013642.999187-1-xujiakai2025@iscas.ac.cn Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_sbi_system.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/riscv/kvm/vcpu_sbi_system.c b/arch/riscv/kvm/vcpu_sbi_system.c index c6f7e609ac79..6f64a59e5d3c 100644 --- a/arch/riscv/kvm/vcpu_sbi_system.c +++ b/arch/riscv/kvm/vcpu_sbi_system.c @@ -35,6 +35,20 @@ static int kvm_sbi_ext_susp_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, return 0; } + /* + * Check that all other vCPUs are stopped before entering + * system suspend. + * + * There is a known TOCTOU race here: a concurrent HSM + * HART_START on another vCPU can start a vCPU after it + * has already passed this check, violating the invariant. + * + * We do not fix this because: + * 1. Triggering the race requires a pathological guest. + * 2. Only guest state is at risk, not host integrity. + * 3. Userspace can double-check vCPU states before + * proceeding with suspend. + */ kvm_for_each_vcpu(i, tmp, vcpu->kvm) { if (tmp == vcpu) continue; From 76ae7c7ee004b3d9d869f4d59b175ab4750db985 Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Tue, 26 May 2026 03:15:17 +0000 Subject: [PATCH 2775/5214] RISC-V: KVM: Fix NULL pointer dereference in AIA IMSIC functions Fuzzer reported a NULL pointer dereference in kvm_riscv_vcpu_aia_imsic_put() when a VCPU's imsic_state was NULL while kvm_riscv_aia_initialized() returned true. The global initialized flag is set per-VM in aia_init(), but imsic_state is allocated per-VCPU in kvm_riscv_vcpu_aia_imsic_init(). If a VCPU is created after aia_init() has already run, its imsic_state remains NULL while the global flag is true. When this VCPU is preempted, kvm_sched_out() calls kvm_arch_vcpu_put() -> kvm_riscv_vcpu_aia_put() -> kvm_riscv_vcpu_aia_imsic_put() which dereferences NULL. Add NULL pointer guards to kvm_riscv_vcpu_aia_imsic_put(), consistent with the NULL checks already present in all other functions in the same file. Also add a NULL guard to kvm_riscv_vcpu_aia_imsic_release() and kvm_riscv_vcpu_aia_imsic_has_interrupt() for the same reason. Fixes: 4cec89db80ba ("RISC-V: KVM: Move HGEI[E|P] CSR access to IMSIC virtualization") Signed-off-by: Jiakai Xu Signed-off-by: Jiakai Xu Assisted-by: YuanSheng:DeepSeek-V3.2 Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260526031517.1166025-1-xujiakai2025@iscas.ac.cn Signed-off-by: Anup Patel --- arch/riscv/kvm/aia_imsic.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c index 8786f52cf65a..d38f5de0834c 100644 --- a/arch/riscv/kvm/aia_imsic.c +++ b/arch/riscv/kvm/aia_imsic.c @@ -683,6 +683,9 @@ bool kvm_riscv_vcpu_aia_imsic_has_interrupt(struct kvm_vcpu *vcpu) unsigned long flags; bool ret = false; + if (!imsic) + return false; + /* * The IMSIC SW-file directly injects interrupt via hvip so * only check for interrupt when IMSIC VS-file is being used. @@ -722,6 +725,9 @@ void kvm_riscv_vcpu_aia_imsic_put(struct kvm_vcpu *vcpu) struct imsic *imsic = vcpu->arch.aia_context.imsic_state; unsigned long flags; + if (!imsic) + return; + if (!kvm_vcpu_is_blocking(vcpu)) return; @@ -738,6 +744,9 @@ void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu) int old_vsfile_hgei, old_vsfile_cpu; struct imsic *imsic = vcpu->arch.aia_context.imsic_state; + if (!imsic) + return; + /* Read and clear IMSIC VS-file details */ write_lock_irqsave(&imsic->vsfile_lock, flags); old_vsfile_hgei = imsic->vsfile_hgei; From d52d42e2e5d9f13166e81ac837ebb023d1306e61 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 7 Apr 2026 11:27:55 +0800 Subject: [PATCH 2776/5214] dmaengine: imx-sdma: Refine spba bus searching in probe There are multi spba-busses for i.MX8M* platforms, if only search for the first spba-bus in DT, the found spba-bus may not the real bus of audio devices, which cause issue for sdma p2p case, as the sdma p2p script presently does not deal with the transactions involving two devices connected to the AIPS bus. Search the SDMA parent node first, which should be the AIPS bus, then search the child node whose compatible string is spba-bus under that AIPS bus for the above multi spba-busses case. Fixes: 8391ecf465ec ("dmaengine: imx-sdma: Add device to device support") Signed-off-by: Shengjiu Wang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407032755.2758049-1-shengjiu.wang@nxp.com Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 3d527883776b..36368835a845 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2364,7 +2364,9 @@ static int sdma_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "failed to register controller\n"); - spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus"); + struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np); + + spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus"); ret = of_address_to_resource(spba_bus, 0, &spba_res); if (!ret) { sdma->spba_start_addr = spba_res.start; From 6d4c17ed56201eec04247f4c72b4028cfbb0eba4 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Tue, 26 May 2026 15:55:44 +0800 Subject: [PATCH 2777/5214] RISC-V: KVM: Fix timer state restore The KVM_REG_RISCV_TIMER_REG(state) one-reg write passes the value written by userspace to kvm_riscv_vcpu_timer_next_event() when re-enabling the timer. That value is the timer state, KVM_RISCV_TIMER_STATE_ON, not the timer compare value. During migration or state restore, userspace restores the compare register separately, which stores the target cycle in t->next_cycles. Re-arming the timer with the state value schedules the next event at cycle 1 instead of the restored compare value, causing the virtual timer to fire too early. Use the restored compare value from t->next_cycles when turning the timer back on. Fixes: 3a9f66cb25e1 ("RISC-V: KVM: Add timer functionality") Signed-off-by: Qiang Ma Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260526075544.796396-1-maqianga@uniontech.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index 9817ff802821..ae53133c7ab0 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -231,7 +231,7 @@ int kvm_riscv_vcpu_set_reg_timer(struct kvm_vcpu *vcpu, break; case KVM_REG_RISCV_TIMER_REG(state): if (reg_val == KVM_RISCV_TIMER_STATE_ON) - ret = kvm_riscv_vcpu_timer_next_event(vcpu, reg_val); + ret = kvm_riscv_vcpu_timer_next_event(vcpu, t->next_cycles); else ret = kvm_riscv_vcpu_timer_cancel(t); break; From cc6049bd3fa8501ee27042df469a19ed69cf406d Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:54 +0530 Subject: [PATCH 2778/5214] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional On Tegra264, GPCDMA reset control is not exposed to Linux and is handled by the boot firmware. Although reset was not exposed in Tegra234 as well, the firmware supported a dummy reset which just returns success on reset without doing an actual reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset' and 'reset-names' properties as required only for devices prior to Tegra264. This also necessitates that the Tegra264 compatible be standalone and cannot have the fallback compatible of Tegra186. Since there is no functional impact, we keep reset as required for Tegra234 to avoid breaking the ABI. Fixes: bb8c97571db5 ("dt-bindings: dma: Add Tegra264 compatible string") Signed-off-by: Akhil R Acked-by: Rob Herring (Arm) Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-2-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index 0dabe9bbb219..64f1e9d9896d 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -15,16 +15,14 @@ maintainers: - Jon Hunter - Rajesh Gumasta -allOf: - - $ref: dma-controller.yaml# - properties: compatible: oneOf: - - const: nvidia,tegra186-gpcdma + - enum: + - nvidia,tegra264-gpcdma + - nvidia,tegra186-gpcdma - items: - enum: - - nvidia,tegra264-gpcdma - nvidia,tegra234-gpcdma - nvidia,tegra194-gpcdma - const: nvidia,tegra186-gpcdma @@ -60,12 +58,23 @@ required: - compatible - reg - interrupts - - resets - - reset-names - "#dma-cells" - iommus - dma-channel-mask +allOf: + - $ref: dma-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra186-gpcdma + then: + required: + - resets + - reset-names + additionalProperties: false examples: From d6d7ffb994c676e6414a725d7eb8f208d901b63a Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:56 +0530 Subject: [PATCH 2779/5214] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Add iommu-map property to specify separate stream IDs for each DMA channel. This enables each channel to be in its own IOMMU domain, keeping memory isolated from other devices sharing the same DMA controller. Define the constraints such that if the channel and stream IDs are contiguous, a single entry can map all the channels, but if the channels or stream IDs are non-contiguous support multiple entries. Signed-off-by: Akhil R Acked-by: Rob Herring (Arm) Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-4-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index 64f1e9d9896d..bc093c783d98 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -14,6 +14,7 @@ description: | maintainers: - Jon Hunter - Rajesh Gumasta + - Akhil R properties: compatible: @@ -49,6 +50,14 @@ properties: iommus: maxItems: 1 + iommu-map: + description: + Maps DMA channel numbers to IOMMU stream IDs. A single entry can map all + channels when stream IDs are contiguous. In systems where the channels or + stream IDs are not contiguous, multiple entries may be needed. + minItems: 1 + maxItems: 32 + dma-coherent: true dma-channel-mask: From 680e1b928a6adc1b2d95038ffe9c9887ceafd478 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:57 +0530 Subject: [PATCH 2780/5214] dmaengine: tegra: Make reset control optional On Tegra264, reset is not available for the driver to control as this is handled by the boot firmware. Hence make the reset control optional and update the error message to reflect the correct error. Signed-off-by: Akhil R Reviewed-by: Frank Li Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-5-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 5948fbf32c21..a0522a992ebc 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1381,10 +1381,10 @@ static int tegra_dma_probe(struct platform_device *pdev) if (IS_ERR(tdma->base_addr)) return PTR_ERR(tdma->base_addr); - tdma->rst = devm_reset_control_get_exclusive(&pdev->dev, "gpcdma"); + tdma->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "gpcdma"); if (IS_ERR(tdma->rst)) { return dev_err_probe(&pdev->dev, PTR_ERR(tdma->rst), - "Missing controller reset\n"); + "Failed to get controller reset\n"); } reset_control_reset(tdma->rst); From 5000beabae65310ec81db40dcda181b0a6192ff3 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:58 +0530 Subject: [PATCH 2781/5214] dmaengine: tegra: Use struct for register offsets Repurpose the struct tegra_dma_channel_regs to define offsets for all the channel registers. Previously, the struct only held the register values for each transfer and was wrapped within tegra_dma_sg_req. Move the values directly into tegra_dma_sg_req and use channel_regs for storing the register offsets. Update all register reads/writes to use the struct channel_regs. This prepares for the register offset change in Tegra264. Signed-off-by: Akhil R Reviewed-by: Frank Li Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-6-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 282 +++++++++++++++++---------------- 1 file changed, 142 insertions(+), 140 deletions(-) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index a0522a992ebc..b213c4ae07d2 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -22,7 +22,6 @@ #include "virt-dma.h" /* CSR register */ -#define TEGRA_GPCDMA_CHAN_CSR 0x00 #define TEGRA_GPCDMA_CSR_ENB BIT(31) #define TEGRA_GPCDMA_CSR_IE_EOC BIT(30) #define TEGRA_GPCDMA_CSR_ONCE BIT(27) @@ -58,7 +57,6 @@ #define TEGRA_GPCDMA_CSR_WEIGHT GENMASK(13, 10) /* STATUS register */ -#define TEGRA_GPCDMA_CHAN_STATUS 0x004 #define TEGRA_GPCDMA_STATUS_BUSY BIT(31) #define TEGRA_GPCDMA_STATUS_ISE_EOC BIT(30) #define TEGRA_GPCDMA_STATUS_PING_PONG BIT(28) @@ -70,22 +68,13 @@ #define TEGRA_GPCDMA_STATUS_IRQ_STA BIT(21) #define TEGRA_GPCDMA_STATUS_IRQ_TRIG_STA BIT(20) -#define TEGRA_GPCDMA_CHAN_CSRE 0x008 #define TEGRA_GPCDMA_CHAN_CSRE_PAUSE BIT(31) -/* Source address */ -#define TEGRA_GPCDMA_CHAN_SRC_PTR 0x00C - -/* Destination address */ -#define TEGRA_GPCDMA_CHAN_DST_PTR 0x010 - /* High address pointer */ -#define TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR 0x014 #define TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR GENMASK(7, 0) #define TEGRA_GPCDMA_HIGH_ADDR_DST_PTR GENMASK(23, 16) /* MC sequence register */ -#define TEGRA_GPCDMA_CHAN_MCSEQ 0x18 #define TEGRA_GPCDMA_MCSEQ_DATA_SWAP BIT(31) #define TEGRA_GPCDMA_MCSEQ_REQ_COUNT GENMASK(30, 25) #define TEGRA_GPCDMA_MCSEQ_BURST GENMASK(24, 23) @@ -101,7 +90,6 @@ #define TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK GENMASK(6, 0) /* MMIO sequence register */ -#define TEGRA_GPCDMA_CHAN_MMIOSEQ 0x01c #define TEGRA_GPCDMA_MMIOSEQ_DBL_BUF BIT(31) #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH GENMASK(30, 28) #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_8 \ @@ -120,17 +108,7 @@ #define TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD GENMASK(18, 16) #define TEGRA_GPCDMA_MMIOSEQ_MMIO_PROT GENMASK(8, 7) -/* Channel WCOUNT */ -#define TEGRA_GPCDMA_CHAN_WCOUNT 0x20 - -/* Transfer count */ -#define TEGRA_GPCDMA_CHAN_XFER_COUNT 0x24 - -/* DMA byte count status */ -#define TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS 0x28 - /* Error Status Register */ -#define TEGRA_GPCDMA_CHAN_ERR_STATUS 0x30 #define TEGRA_GPCDMA_CHAN_ERR_TYPE_SHIFT 8 #define TEGRA_GPCDMA_CHAN_ERR_TYPE_MASK 0xF #define TEGRA_GPCDMA_CHAN_ERR_TYPE(err) ( \ @@ -143,16 +121,6 @@ #define TEGRA_DMA_MC_SLAVE_ERR 0xB #define TEGRA_DMA_MMIO_SLAVE_ERR 0xA -/* Fixed Pattern */ -#define TEGRA_GPCDMA_CHAN_FIXED_PATTERN 0x34 - -#define TEGRA_GPCDMA_CHAN_TZ 0x38 -#define TEGRA_GPCDMA_CHAN_TZ_MMIO_PROT_1 BIT(0) -#define TEGRA_GPCDMA_CHAN_TZ_MC_PROT_1 BIT(1) - -#define TEGRA_GPCDMA_CHAN_SPARE 0x3c -#define TEGRA_GPCDMA_CHAN_SPARE_EN_LEGACY_FC BIT(16) - /* * If any burst is in flight and DMA paused then this is the time to complete * on-flight burst and update DMA status register. @@ -181,18 +149,24 @@ struct tegra_dma_chip_data { unsigned int nr_channels; unsigned int channel_reg_size; unsigned int max_dma_count; + const struct tegra_dma_channel_regs *channel_regs; int (*terminate)(struct tegra_dma_channel *tdc); }; /* DMA channel registers */ struct tegra_dma_channel_regs { u32 csr; - u32 src_ptr; - u32 dst_ptr; - u32 high_addr_ptr; + u32 status; + u32 csre; + u32 src; + u32 dst; + u32 high_addr; u32 mc_seq; u32 mmio_seq; u32 wcount; + u32 wxfer; + u32 wstatus; + u32 err_status; u32 fixed_pattern; }; @@ -205,7 +179,14 @@ struct tegra_dma_channel_regs { */ struct tegra_dma_sg_req { unsigned int len; - struct tegra_dma_channel_regs ch_regs; + u32 csr; + u32 src; + u32 dst; + u32 high_addr; + u32 mc_seq; + u32 mmio_seq; + u32 wcount; + u32 fixed_pattern; }; /* @@ -228,19 +209,20 @@ struct tegra_dma_desc { * tegra_dma_channel: Channel specific information */ struct tegra_dma_channel { - bool config_init; - char name[30]; - enum dma_transfer_direction sid_dir; - enum dma_status status; - int id; - int irq; - int slave_id; + const struct tegra_dma_channel_regs *regs; struct tegra_dma *tdma; struct virt_dma_chan vc; struct tegra_dma_desc *dma_desc; struct dma_slave_config dma_sconfig; + enum dma_transfer_direction sid_dir; + enum dma_status status; unsigned int stream_id; unsigned long chan_base_offset; + bool config_init; + char name[30]; + int id; + int irq; + int slave_id; }; /* @@ -288,22 +270,22 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc) { dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n", tdc->id, tdc->name); - dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x SRC %x DST %x\n", - tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_DST_PTR) - ); - dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x BSTA %x\n", - tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_WCOUNT), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT), - tdc_read(tdc, TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS) - ); + dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x\n", + tdc_read(tdc, tdc->regs->csr), + tdc_read(tdc, tdc->regs->status), + tdc_read(tdc, tdc->regs->csre)); + dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n", + tdc_read(tdc, tdc->regs->src), + tdc_read(tdc, tdc->regs->dst), + tdc_read(tdc, tdc->regs->high_addr)); + dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n", + tdc_read(tdc, tdc->regs->mc_seq), + tdc_read(tdc, tdc->regs->mmio_seq), + tdc_read(tdc, tdc->regs->wcount), + tdc_read(tdc, tdc->regs->wxfer), + tdc_read(tdc, tdc->regs->wstatus)); dev_dbg(tdc2dev(tdc), "DMA ERR_STA %x\n", - tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS)); + tdc_read(tdc, tdc->regs->err_status)); } static int tegra_dma_sid_reserve(struct tegra_dma_channel *tdc, @@ -377,13 +359,13 @@ static int tegra_dma_pause(struct tegra_dma_channel *tdc) int ret; u32 val; - val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE); + val = tdc_read(tdc, tdc->regs->csre); val |= TEGRA_GPCDMA_CHAN_CSRE_PAUSE; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val); + tdc_write(tdc, tdc->regs->csre, val); /* Wait until busy bit is de-asserted */ ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr + - tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS, + tdc->chan_base_offset + tdc->regs->status, val, !(val & TEGRA_GPCDMA_STATUS_BUSY), TEGRA_GPCDMA_BURST_COMPLETE_TIME, @@ -419,9 +401,9 @@ static void tegra_dma_resume(struct tegra_dma_channel *tdc) { u32 val; - val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE); + val = tdc_read(tdc, tdc->regs->csre); val &= ~TEGRA_GPCDMA_CHAN_CSRE_PAUSE; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val); + tdc_write(tdc, tdc->regs->csre, val); tdc->status = DMA_IN_PROGRESS; } @@ -456,27 +438,27 @@ static void tegra_dma_disable(struct tegra_dma_channel *tdc) { u32 csr, status; - csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR); + csr = tdc_read(tdc, tdc->regs->csr); /* Disable interrupts */ csr &= ~TEGRA_GPCDMA_CSR_IE_EOC; /* Disable DMA */ csr &= ~TEGRA_GPCDMA_CSR_ENB; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr); + tdc_write(tdc, tdc->regs->csr, csr); /* Clear interrupt status if it is there */ - status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS); + status = tdc_read(tdc, tdc->regs->status); if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) { dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, status); + tdc_write(tdc, tdc->regs->status, status); } } static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc) { struct tegra_dma_desc *dma_desc = tdc->dma_desc; - struct tegra_dma_channel_regs *ch_regs; + struct tegra_dma_sg_req *sg_req; int ret; u32 val; @@ -488,29 +470,29 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc) /* Configure next transfer immediately after DMA is busy */ ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr + - tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS, + tdc->chan_base_offset + tdc->regs->status, val, (val & TEGRA_GPCDMA_STATUS_BUSY), 0, TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT); if (ret) return; - ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs; + sg_req = &dma_desc->sg_req[dma_desc->sg_idx]; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr); + tdc_write(tdc, tdc->regs->wcount, sg_req->wcount); + tdc_write(tdc, tdc->regs->src, sg_req->src); + tdc_write(tdc, tdc->regs->dst, sg_req->dst); + tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr); /* Start DMA */ - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, - ch_regs->csr | TEGRA_GPCDMA_CSR_ENB); + tdc_write(tdc, tdc->regs->csr, + sg_req->csr | TEGRA_GPCDMA_CSR_ENB); } static void tegra_dma_start(struct tegra_dma_channel *tdc) { struct tegra_dma_desc *dma_desc = tdc->dma_desc; - struct tegra_dma_channel_regs *ch_regs; + struct tegra_dma_sg_req *sg_req; struct virt_dma_desc *vdesc; if (!dma_desc) { @@ -526,21 +508,21 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc) tegra_dma_resume(tdc); } - ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs; + sg_req = &dma_desc->sg_req[dma_desc->sg_idx]; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, 0); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_FIXED_PATTERN, ch_regs->fixed_pattern); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ, ch_regs->mmio_seq); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, ch_regs->mc_seq); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, ch_regs->csr); + tdc_write(tdc, tdc->regs->wcount, sg_req->wcount); + tdc_write(tdc, tdc->regs->csr, 0); + tdc_write(tdc, tdc->regs->src, sg_req->src); + tdc_write(tdc, tdc->regs->dst, sg_req->dst); + tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr); + tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern); + tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq); + tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq); + tdc_write(tdc, tdc->regs->csr, sg_req->csr); /* Start DMA */ - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, - ch_regs->csr | TEGRA_GPCDMA_CSR_ENB); + tdc_write(tdc, tdc->regs->csr, + sg_req->csr | TEGRA_GPCDMA_CSR_ENB); } static void tegra_dma_xfer_complete(struct tegra_dma_channel *tdc) @@ -601,19 +583,19 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id) u32 status; /* Check channel error status register */ - status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS); + status = tdc_read(tdc, tdc->regs->err_status); if (status) { tegra_dma_chan_decode_error(tdc, status); tegra_dma_dump_chan_regs(tdc); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF); + tdc_write(tdc, tdc->regs->err_status, 0xFFFFFFFF); } spin_lock(&tdc->vc.lock); - status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS); + status = tdc_read(tdc, tdc->regs->status); if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC)) goto irq_done; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, + tdc_write(tdc, tdc->regs->status, TEGRA_GPCDMA_STATUS_ISE_EOC); if (!dma_desc) @@ -673,10 +655,10 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc) * to stop DMA engine from starting any more bursts for * the given client and wait for in flight bursts to complete */ - csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR); + csr = tdc_read(tdc, tdc->regs->csr); csr &= ~(TEGRA_GPCDMA_CSR_REQ_SEL_MASK); csr |= TEGRA_GPCDMA_CSR_REQ_SEL_UNUSED; - tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr); + tdc_write(tdc, tdc->regs->csr, csr); /* Wait for in flight data transfer to finish */ udelay(TEGRA_GPCDMA_BURST_COMPLETE_TIME); @@ -687,7 +669,7 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc) ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr + tdc->chan_base_offset + - TEGRA_GPCDMA_CHAN_STATUS, + tdc->regs->status, status, !(status & (TEGRA_GPCDMA_STATUS_CHANNEL_TX | TEGRA_GPCDMA_STATUS_CHANNEL_RX)), @@ -739,14 +721,14 @@ static int tegra_dma_get_residual(struct tegra_dma_channel *tdc) unsigned int bytes_xfer, residual; u32 wcount = 0, status; - wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT); + wcount = tdc_read(tdc, tdc->regs->wxfer); /* * Set wcount = 0 if EOC bit is set. The transfer would have * already completed and the CHAN_XFER_COUNT could have updated * for the next transfer, specifically in case of cyclic transfers. */ - status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS); + status = tdc_read(tdc, tdc->regs->status); if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) wcount = 0; @@ -893,7 +875,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value, /* Configure default priority weight for the channel */ csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1); - mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ); + mc_seq = tdc_read(tdc, tdc->regs->mc_seq); /* retain stream-id and clean rest */ mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK; @@ -916,16 +898,16 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value, dma_desc->sg_count = 1; sg_req = dma_desc->sg_req; - sg_req[0].ch_regs.src_ptr = 0; - sg_req[0].ch_regs.dst_ptr = dest; - sg_req[0].ch_regs.high_addr_ptr = + sg_req[0].src = 0; + sg_req[0].dst = dest; + sg_req[0].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32)); - sg_req[0].ch_regs.fixed_pattern = value; + sg_req[0].fixed_pattern = value; /* Word count reg takes value as (N +1) words */ - sg_req[0].ch_regs.wcount = ((len - 4) >> 2); - sg_req[0].ch_regs.csr = csr; - sg_req[0].ch_regs.mmio_seq = 0; - sg_req[0].ch_regs.mc_seq = mc_seq; + sg_req[0].wcount = ((len - 4) >> 2); + sg_req[0].csr = csr; + sg_req[0].mmio_seq = 0; + sg_req[0].mc_seq = mc_seq; sg_req[0].len = len; dma_desc->cyclic = false; @@ -961,7 +943,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest, /* Configure default priority weight for the channel */ csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1); - mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ); + mc_seq = tdc_read(tdc, tdc->regs->mc_seq); /* retain stream-id and clean rest */ mc_seq &= (TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK) | (TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK); @@ -985,17 +967,17 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest, dma_desc->sg_count = 1; sg_req = dma_desc->sg_req; - sg_req[0].ch_regs.src_ptr = src; - sg_req[0].ch_regs.dst_ptr = dest; - sg_req[0].ch_regs.high_addr_ptr = + sg_req[0].src = src; + sg_req[0].dst = dest; + sg_req[0].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32)); - sg_req[0].ch_regs.high_addr_ptr |= + sg_req[0].high_addr |= FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32)); /* Word count reg takes value as (N +1) words */ - sg_req[0].ch_regs.wcount = ((len - 4) >> 2); - sg_req[0].ch_regs.csr = csr; - sg_req[0].ch_regs.mmio_seq = 0; - sg_req[0].ch_regs.mc_seq = mc_seq; + sg_req[0].wcount = ((len - 4) >> 2); + sg_req[0].csr = csr; + sg_req[0].mmio_seq = 0; + sg_req[0].mc_seq = mc_seq; sg_req[0].len = len; dma_desc->cyclic = false; @@ -1049,7 +1031,7 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl, if (flags & DMA_PREP_INTERRUPT) csr |= TEGRA_GPCDMA_CSR_IE_EOC; - mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ); + mc_seq = tdc_read(tdc, tdc->regs->mc_seq); /* retain stream-id and clean rest */ mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK; @@ -1096,14 +1078,14 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl, dma_desc->bytes_req += len; if (direction == DMA_MEM_TO_DEV) { - sg_req[i].ch_regs.src_ptr = mem; - sg_req[i].ch_regs.dst_ptr = apb_ptr; - sg_req[i].ch_regs.high_addr_ptr = + sg_req[i].src = mem; + sg_req[i].dst = apb_ptr; + sg_req[i].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32)); } else if (direction == DMA_DEV_TO_MEM) { - sg_req[i].ch_regs.src_ptr = apb_ptr; - sg_req[i].ch_regs.dst_ptr = mem; - sg_req[i].ch_regs.high_addr_ptr = + sg_req[i].src = apb_ptr; + sg_req[i].dst = mem; + sg_req[i].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32)); } @@ -1111,10 +1093,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl, * Word count register takes input in words. Writing a value * of N into word count register means a req of (N+1) words. */ - sg_req[i].ch_regs.wcount = ((len - 4) >> 2); - sg_req[i].ch_regs.csr = csr; - sg_req[i].ch_regs.mmio_seq = mmio_seq; - sg_req[i].ch_regs.mc_seq = mc_seq; + sg_req[i].wcount = ((len - 4) >> 2); + sg_req[i].csr = csr; + sg_req[i].mmio_seq = mmio_seq; + sg_req[i].mc_seq = mc_seq; sg_req[i].len = len; } @@ -1186,7 +1168,7 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l mmio_seq |= FIELD_PREP(TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD, 1); - mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ); + mc_seq = tdc_read(tdc, tdc->regs->mc_seq); /* retain stream-id and clean rest */ mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK; @@ -1217,24 +1199,24 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l for (i = 0; i < period_count; i++) { mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len); if (direction == DMA_MEM_TO_DEV) { - sg_req[i].ch_regs.src_ptr = mem; - sg_req[i].ch_regs.dst_ptr = apb_ptr; - sg_req[i].ch_regs.high_addr_ptr = + sg_req[i].src = mem; + sg_req[i].dst = apb_ptr; + sg_req[i].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32)); } else if (direction == DMA_DEV_TO_MEM) { - sg_req[i].ch_regs.src_ptr = apb_ptr; - sg_req[i].ch_regs.dst_ptr = mem; - sg_req[i].ch_regs.high_addr_ptr = + sg_req[i].src = apb_ptr; + sg_req[i].dst = mem; + sg_req[i].high_addr = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32)); } /* * Word count register takes input in words. Writing a value * of N into word count register means a req of (N+1) words. */ - sg_req[i].ch_regs.wcount = ((len - 4) >> 2); - sg_req[i].ch_regs.csr = csr; - sg_req[i].ch_regs.mmio_seq = mmio_seq; - sg_req[i].ch_regs.mc_seq = mc_seq; + sg_req[i].wcount = ((len - 4) >> 2); + sg_req[i].csr = csr; + sg_req[i].mmio_seq = mmio_seq; + sg_req[i].mc_seq = mc_seq; sg_req[i].len = len; mem += len; @@ -1304,11 +1286,28 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec, return chan; } +static const struct tegra_dma_channel_regs tegra186_reg_offsets = { + .csr = 0x0, + .status = 0x4, + .csre = 0x8, + .src = 0xc, + .dst = 0x10, + .high_addr = 0x14, + .mc_seq = 0x18, + .mmio_seq = 0x1c, + .wcount = 0x20, + .wxfer = 0x24, + .wstatus = 0x28, + .err_status = 0x30, + .fixed_pattern = 0x34, +}; + static const struct tegra_dma_chip_data tegra186_dma_chip_data = { .nr_channels = 32, .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = false, + .channel_regs = &tegra186_reg_offsets, .terminate = tegra_dma_stop_client, }; @@ -1317,6 +1316,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = { .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = true, + .channel_regs = &tegra186_reg_offsets, .terminate = tegra_dma_pause, }; @@ -1325,6 +1325,7 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = { .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = true, + .channel_regs = &tegra186_reg_offsets, .terminate = tegra_dma_pause_noerr, }; @@ -1345,7 +1346,7 @@ MODULE_DEVICE_TABLE(of, tegra_dma_of_match); static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id) { - unsigned int reg_val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ); + unsigned int reg_val = tdc_read(tdc, tdc->regs->mc_seq); reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK); reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK); @@ -1353,7 +1354,7 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id) reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK, stream_id); reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK, stream_id); - tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, reg_val); + tdc_write(tdc, tdc->regs->mc_seq, reg_val); return 0; } @@ -1419,6 +1420,7 @@ static int tegra_dma_probe(struct platform_device *pdev) tdc->chan_base_offset = TEGRA_GPCDMA_CHANNEL_BASE_ADDR_OFFSET + i * cdata->channel_reg_size; snprintf(tdc->name, sizeof(tdc->name), "gpcdma.%d", i); + tdc->regs = cdata->channel_regs; tdc->tdma = tdma; tdc->id = i; tdc->slave_id = -1; From 286632b9bf1cf239482d54b592cc1d5bbd5ec783 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:59 +0530 Subject: [PATCH 2782/5214] dmaengine: tegra: Support address width > 39 bits Tegra264 supports address width of 41 bits. Unlike older SoCs which use a common high_addr register for upper address bits, Tegra264 has separate src_high and dst_high registers to accommodate this wider address space. Add an addr_bits property to the device data structure to specify the number of address bits supported on each device and use that to program the appropriate registers. Update the sg_req struct to remove the high_addr field and use dma_addr_t for src and dst to store the complete addresses. Extract the high address bits only when programming the registers. Signed-off-by: Akhil R Reviewed-by: Frank Li Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-7-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 83 +++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index b213c4ae07d2..3ac43ad19ed6 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -146,6 +146,7 @@ struct tegra_dma_channel; */ struct tegra_dma_chip_data { bool hw_support_pause; + unsigned int addr_bits; unsigned int nr_channels; unsigned int channel_reg_size; unsigned int max_dma_count; @@ -161,6 +162,8 @@ struct tegra_dma_channel_regs { u32 src; u32 dst; u32 high_addr; + u32 src_high; + u32 dst_high; u32 mc_seq; u32 mmio_seq; u32 wcount; @@ -179,10 +182,9 @@ struct tegra_dma_channel_regs { */ struct tegra_dma_sg_req { unsigned int len; + dma_addr_t src; + dma_addr_t dst; u32 csr; - u32 src; - u32 dst; - u32 high_addr; u32 mc_seq; u32 mmio_seq; u32 wcount; @@ -266,6 +268,25 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc) return tdc->vc.chan.device->dev; } +static void tegra_dma_program_addr(struct tegra_dma_channel *tdc, + struct tegra_dma_sg_req *sg_req) +{ + tdc_write(tdc, tdc->regs->src, lower_32_bits(sg_req->src)); + tdc_write(tdc, tdc->regs->dst, lower_32_bits(sg_req->dst)); + + if (tdc->tdma->chip_data->addr_bits > 39) { + tdc_write(tdc, tdc->regs->src_high, upper_32_bits(sg_req->src)); + tdc_write(tdc, tdc->regs->dst_high, upper_32_bits(sg_req->dst)); + } else { + u32 src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, + upper_32_bits(sg_req->src)); + u32 dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, + upper_32_bits(sg_req->dst)); + + tdc_write(tdc, tdc->regs->high_addr, src_high | dst_high); + } +} + static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc) { dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n", @@ -274,10 +295,20 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc) tdc_read(tdc, tdc->regs->csr), tdc_read(tdc, tdc->regs->status), tdc_read(tdc, tdc->regs->csre)); - dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n", - tdc_read(tdc, tdc->regs->src), - tdc_read(tdc, tdc->regs->dst), - tdc_read(tdc, tdc->regs->high_addr)); + + if (tdc->tdma->chip_data->addr_bits > 39) { + dev_dbg(tdc2dev(tdc), "SRC %x SRC HI %x DST %x DST HI %x\n", + tdc_read(tdc, tdc->regs->src), + tdc_read(tdc, tdc->regs->src_high), + tdc_read(tdc, tdc->regs->dst), + tdc_read(tdc, tdc->regs->dst_high)); + } else { + dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n", + tdc_read(tdc, tdc->regs->src), + tdc_read(tdc, tdc->regs->dst), + tdc_read(tdc, tdc->regs->high_addr)); + } + dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n", tdc_read(tdc, tdc->regs->mc_seq), tdc_read(tdc, tdc->regs->mmio_seq), @@ -480,9 +511,7 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc) sg_req = &dma_desc->sg_req[dma_desc->sg_idx]; tdc_write(tdc, tdc->regs->wcount, sg_req->wcount); - tdc_write(tdc, tdc->regs->src, sg_req->src); - tdc_write(tdc, tdc->regs->dst, sg_req->dst); - tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr); + tegra_dma_program_addr(tdc, sg_req); /* Start DMA */ tdc_write(tdc, tdc->regs->csr, @@ -510,11 +539,9 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc) sg_req = &dma_desc->sg_req[dma_desc->sg_idx]; + tegra_dma_program_addr(tdc, sg_req); tdc_write(tdc, tdc->regs->wcount, sg_req->wcount); tdc_write(tdc, tdc->regs->csr, 0); - tdc_write(tdc, tdc->regs->src, sg_req->src); - tdc_write(tdc, tdc->regs->dst, sg_req->dst); - tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr); tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern); tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq); tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq); @@ -819,7 +846,7 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc, static int get_transfer_param(struct tegra_dma_channel *tdc, enum dma_transfer_direction direction, - u32 *apb_addr, + dma_addr_t *apb_addr, u32 *mmio_seq, u32 *csr, unsigned int *burst_size, @@ -897,11 +924,9 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value, dma_desc->bytes_req = len; dma_desc->sg_count = 1; sg_req = dma_desc->sg_req; - sg_req[0].src = 0; sg_req[0].dst = dest; - sg_req[0].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32)); + sg_req[0].fixed_pattern = value; /* Word count reg takes value as (N +1) words */ sg_req[0].wcount = ((len - 4) >> 2); @@ -969,10 +994,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest, sg_req[0].src = src; sg_req[0].dst = dest; - sg_req[0].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32)); - sg_req[0].high_addr |= - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32)); + /* Word count reg takes value as (N +1) words */ sg_req[0].wcount = ((len - 4) >> 2); sg_req[0].csr = csr; @@ -992,7 +1014,8 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl, struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); unsigned int max_dma_count = tdc->tdma->chip_data->max_dma_count; enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED; - u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0; + u32 csr, mc_seq, mmio_seq = 0; + dma_addr_t apb_ptr = 0; struct tegra_dma_sg_req *sg_req; struct tegra_dma_desc *dma_desc; struct scatterlist *sg; @@ -1080,13 +1103,9 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl, if (direction == DMA_MEM_TO_DEV) { sg_req[i].src = mem; sg_req[i].dst = apb_ptr; - sg_req[i].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32)); } else if (direction == DMA_DEV_TO_MEM) { sg_req[i].src = apb_ptr; sg_req[i].dst = mem; - sg_req[i].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32)); } /* @@ -1110,7 +1129,8 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l unsigned long flags) { enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED; - u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0, burst_size; + u32 csr, mc_seq, mmio_seq = 0, burst_size; + dma_addr_t apb_ptr = 0; unsigned int max_dma_count, len, period_count, i; struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); struct tegra_dma_desc *dma_desc; @@ -1201,13 +1221,9 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l if (direction == DMA_MEM_TO_DEV) { sg_req[i].src = mem; sg_req[i].dst = apb_ptr; - sg_req[i].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32)); } else if (direction == DMA_DEV_TO_MEM) { sg_req[i].src = apb_ptr; sg_req[i].dst = mem; - sg_req[i].high_addr = - FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32)); } /* * Word count register takes input in words. Writing a value @@ -1304,6 +1320,7 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = { static const struct tegra_dma_chip_data tegra186_dma_chip_data = { .nr_channels = 32, + .addr_bits = 39, .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = false, @@ -1313,6 +1330,7 @@ static const struct tegra_dma_chip_data tegra186_dma_chip_data = { static const struct tegra_dma_chip_data tegra194_dma_chip_data = { .nr_channels = 32, + .addr_bits = 39, .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = true, @@ -1322,6 +1340,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = { static const struct tegra_dma_chip_data tegra234_dma_chip_data = { .nr_channels = 32, + .addr_bits = 39, .channel_reg_size = SZ_64K, .max_dma_count = SZ_1G, .hw_support_pause = true, @@ -1433,6 +1452,8 @@ static int tegra_dma_probe(struct platform_device *pdev) tdc->stream_id = stream_id; } + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits)); + dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_MEMCPY, tdma->dma_dev.cap_mask); From 45921a3282d642038d92737fab24107522324bd4 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:53:00 +0530 Subject: [PATCH 2783/5214] dmaengine: tegra: Use managed DMA controller registration Switch to managed registration in probe. This simplifies the error paths in the probe and also removes the requirement of the driver remove function. Signed-off-by: Akhil R Suggested-by: Frank Li Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-8-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 3ac43ad19ed6..9bea2ffb3b9e 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1483,37 +1483,27 @@ static int tegra_dma_probe(struct platform_device *pdev) tdma->dma_dev.device_synchronize = tegra_dma_chan_synchronize; tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - ret = dma_async_device_register(&tdma->dma_dev); + ret = dmaenginem_async_device_register(&tdma->dma_dev); if (ret < 0) { dev_err_probe(&pdev->dev, ret, "GPC DMA driver registration failed\n"); return ret; } - ret = of_dma_controller_register(pdev->dev.of_node, - tegra_dma_of_xlate, tdma); + ret = devm_of_dma_controller_register(&pdev->dev, pdev->dev.of_node, + tegra_dma_of_xlate, tdma); if (ret < 0) { dev_err_probe(&pdev->dev, ret, "GPC DMA OF registration failed\n"); - - dma_async_device_unregister(&tdma->dma_dev); return ret; } - dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n", + dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n", hweight_long(tdma->chan_mask)); return 0; } -static void tegra_dma_remove(struct platform_device *pdev) -{ - struct tegra_dma *tdma = platform_get_drvdata(pdev); - - of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&tdma->dma_dev); -} - static int __maybe_unused tegra_dma_pm_suspend(struct device *dev) { struct tegra_dma *tdma = dev_get_drvdata(dev); @@ -1564,7 +1554,6 @@ static struct platform_driver tegra_dma_driver = { .of_match_table = tegra_dma_of_match, }, .probe = tegra_dma_probe, - .remove = tegra_dma_remove, }; module_platform_driver(tegra_dma_driver); From 321c0a15f027b83b20ed37717191a2187c9e2eb7 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:53:01 +0530 Subject: [PATCH 2784/5214] dmaengine: tegra: Use iommu-map for stream ID Use 'iommu-map', when provided, to get the stream ID to be programmed for each channel. Iterate over the channels registered and configure each channel device separately using of_dma_configure_id() to allow it to use a separate IOMMU domain for the transfer. However, do this in a second loop since the first loop populates the DMA device channels list and async_device_register() registers the channels. Both are prerequisites for using the channel device in the next loop. Channels will continue to use the same global stream ID if the 'iommu-map' property is not present in the device tree. Signed-off-by: Akhil R Reviewed-by: Frank Li Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-9-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 53 ++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 9bea2ffb3b9e..939eb5462d0a 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1380,9 +1381,13 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id) static int tegra_dma_probe(struct platform_device *pdev) { const struct tegra_dma_chip_data *cdata = NULL; + struct tegra_dma_channel *tdc; + struct tegra_dma *tdma; + struct dma_chan *chan; + struct device *chdev; + bool use_iommu_map = false; unsigned int i; u32 stream_id; - struct tegra_dma *tdma; int ret; cdata = of_device_get_match_data(&pdev->dev); @@ -1410,9 +1415,10 @@ static int tegra_dma_probe(struct platform_device *pdev) tdma->dma_dev.dev = &pdev->dev; - if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) { - dev_err(&pdev->dev, "Missing iommu stream-id\n"); - return -EINVAL; + use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map"); + if (!use_iommu_map) { + if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) + return dev_err_probe(&pdev->dev, -EINVAL, "Missing iommu stream-id\n"); } ret = device_property_read_u32(&pdev->dev, "dma-channel-mask", @@ -1424,9 +1430,10 @@ static int tegra_dma_probe(struct platform_device *pdev) tdma->chan_mask = TEGRA_GPCDMA_DEFAULT_CHANNEL_MASK; } + /* Initialize vchan for each channel and populate the channels list */ INIT_LIST_HEAD(&tdma->dma_dev.channels); for (i = 0; i < cdata->nr_channels; i++) { - struct tegra_dma_channel *tdc = &tdma->channels[i]; + tdc = &tdma->channels[i]; /* Check for channel mask */ if (!(tdma->chan_mask & BIT(i))) @@ -1446,10 +1453,6 @@ static int tegra_dma_probe(struct platform_device *pdev) vchan_init(&tdc->vc, &tdma->dma_dev); tdc->vc.desc_free = tegra_dma_desc_free; - - /* program stream-id for this channel */ - tegra_dma_program_sid(tdc, stream_id); - tdc->stream_id = stream_id; } dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits)); @@ -1483,6 +1486,7 @@ static int tegra_dma_probe(struct platform_device *pdev) tdma->dma_dev.device_synchronize = tegra_dma_chan_synchronize; tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + /* Register the DMA device and the channels */ ret = dmaenginem_async_device_register(&tdma->dma_dev); if (ret < 0) { dev_err_probe(&pdev->dev, ret, @@ -1490,6 +1494,37 @@ static int tegra_dma_probe(struct platform_device *pdev) return ret; } + /* + * Configure stream ID for each channel from the channels registered + * above. This is done in a separate iteration to ensure that only + * the channels available and registered for the DMA device are used. + */ + list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) { + chdev = &chan->dev->device; + tdc = to_tegra_dma_chan(chan); + + if (use_iommu_map) { + chdev->bus = pdev->dev.bus; + dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits)); + + ret = of_dma_configure_id(chdev, pdev->dev.of_node, + true, &tdc->id); + if (ret) + return dev_err_probe(chdev, ret, + "Failed to configure IOMMU for channel %d\n", tdc->id); + + if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) + return dev_err_probe(chdev, -EINVAL, + "Failed to get stream ID for channel %d\n", tdc->id); + + chan->dev->chan_dma_dev = true; + } + + /* program stream-id for this channel */ + tegra_dma_program_sid(tdc, stream_id); + tdc->stream_id = stream_id; + } + ret = devm_of_dma_controller_register(&pdev->dev, pdev->dev.of_node, tegra_dma_of_xlate, tdma); if (ret < 0) { From b236b7973808195fd9c471492bee0041148b823e Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:53:02 +0530 Subject: [PATCH 2785/5214] dmaengine: tegra: Add Tegra264 support Add compatible and chip data to support GPCDMA in Tegra264, which has differences in register layout and address bits compared to previous versions. Signed-off-by: Akhil R Reviewed-by: Frank Li Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-10-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 939eb5462d0a..c364c278a8b0 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1319,6 +1319,23 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = { .fixed_pattern = 0x34, }; +static const struct tegra_dma_channel_regs tegra264_reg_offsets = { + .csr = 0x0, + .status = 0x4, + .csre = 0x8, + .src = 0xc, + .dst = 0x10, + .src_high = 0x14, + .dst_high = 0x18, + .mc_seq = 0x1c, + .mmio_seq = 0x20, + .wcount = 0x24, + .wxfer = 0x28, + .wstatus = 0x2c, + .err_status = 0x34, + .fixed_pattern = 0x38, +}; + static const struct tegra_dma_chip_data tegra186_dma_chip_data = { .nr_channels = 32, .addr_bits = 39, @@ -1349,6 +1366,16 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = { .terminate = tegra_dma_pause_noerr, }; +static const struct tegra_dma_chip_data tegra264_dma_chip_data = { + .nr_channels = 32, + .addr_bits = 41, + .channel_reg_size = SZ_64K, + .max_dma_count = SZ_1G, + .hw_support_pause = true, + .channel_regs = &tegra264_reg_offsets, + .terminate = tegra_dma_pause_noerr, +}; + static const struct of_device_id tegra_dma_of_match[] = { { .compatible = "nvidia,tegra186-gpcdma", @@ -1359,6 +1386,9 @@ static const struct of_device_id tegra_dma_of_match[] = { }, { .compatible = "nvidia,tegra234-gpcdma", .data = &tegra234_dma_chip_data, + }, { + .compatible = "nvidia,tegra264-gpcdma", + .data = &tegra264_dma_chip_data, }, { }, }; From 98495b5a4d77dd22e106f462b76e1093a55b29a7 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 4 Jun 2026 15:34:25 +0800 Subject: [PATCH 2786/5214] coresight: ultrasoc-smb: Fix OOB write in smb_sync_perf_buffer() When the SMB sink is used as a perf AUX sink, smb_update_buffer() calls smb_sync_perf_buffer() to copy hardware trace data into the perf AUX ring buffer pages. It derives pg_idx = head >> PAGE_SHIFT from @head, which is handle->head, and indexes dst_pages[pg_idx]. The pg_idx %= nr_pages normalization is only applied after the first loop iteration. This leaves the initial page index underived from the buffer size, which can result in an out-of-bounds write past dst_pages[] when head exceeds the AUX buffer size. Normalize head modulo the AUX buffer size before deriving the page index and offset, mirroring tmc_etr_sync_perf_buffer(). Fixes: 06f5c2926aaa ("drivers/coresight: Add UltraSoc System Memory Buffer driver") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/SYBPR01MB788156B3380A36835DB22290AF102@SYBPR01MB7881.ausprd01.prod.outlook.com --- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 5776f63468fa..20a950b9dd4f 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -337,6 +337,7 @@ static void smb_sync_perf_buffer(struct smb_drv_data *drvdata, unsigned long to_copy; long pg_idx, pg_offset; + head %= (unsigned long)buf->nr_pages << PAGE_SHIFT; pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); From 39851b7e580a65bee732e5364f0efb974b242370 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 12:25:11 -0700 Subject: [PATCH 2787/5214] ipmi: si: Use platform_get_irq_optional() to retrieve interrupt Use platform_get_irq_optional() to retrieve the interrupt resource instead of directly parsing and mapping the OF node via irq_of_parse_and_map(). This is the standard pattern for platform devices. irq_of_parse_and_map() requires ire_dispose_mapping(), which is missing. Assisted-by: Antigravity:Gemini-3.5-Flash Signed-off-by: Rosen Penev Message-ID: <20260603192511.6869-1-rosenp@gmail.com> [Handle a negative return from platform_get_irq_optional() to mean no interrupt is assigned.] Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_si_platform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index fb6e359ae494..704b06c919f0 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -276,7 +276,10 @@ static int of_ipmi_probe(struct platform_device *pdev) io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; io.regshift = regshift ? be32_to_cpup(regshift) : 0; - io.irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + io.irq = platform_get_irq_optional(pdev, 0); + if (io.irq < 0) + io.irq = 0; + io.dev = &pdev->dev; dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", From b8d3d74d8b5fee73804faec5630c4ef44c3120bd Mon Sep 17 00:00:00 2001 From: Kai Huang Date: Thu, 4 Jun 2026 13:11:06 +1200 Subject: [PATCH 2788/5214] KVM: x86: Use for lockdep header inclusion When KVM added a lockdep assertion to catch unexpected cases where guest CPUID lookups are performed in IRQ disabled context, it used "linux/lockdep.h" for header inclusion even though lockdep.h is a kernel wide header. Switch to using . Fixes: 9717efbe5ba3 ("KVM: x86: Disallow guest CPUID lookups when IRQs are disabled") Signed-off-by: Kai Huang Link: https://patch.msgid.link/20260604011106.315176-1-kai.huang@intel.com [sean: add Fixes, Kai is too polite :-)] Signed-off-by: Sean Christopherson --- arch/x86/kvm/cpuid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index fd3b02575cd0..591d2294acd7 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -11,7 +11,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include "linux/lockdep.h" +#include #include #include #include From b52ba22c7078e1987ce0cc0a8385654cb36296e3 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 3 Jun 2026 03:35:07 -0700 Subject: [PATCH 2789/5214] perf bench: Add --write-size option to sched pipe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default ping-pong uses sizeof(int) (4 bytes) per iteration, which exercises only the pipe-buffer merge path and keeps allocation entirely out of the picture. That makes the bench a useful scheduler / context- switch latency probe but unable to surface anything from the pipe page-allocation hot path. Add a -s/--write-size option that sets the bytes written and read per ping-pong iteration. The buffer is allocated for each side via struct thread_data and replaces the on-stack int previously used. The default remains sizeof(int) so existing invocations are unchanged. With --write-size set above PAGE_SIZE the bench drives anon_pipe_write() through alloc_page() (or the bulk pre-alloc, if the relevant patch is applied), which is what we want when measuring pipe locking and page allocation work. The bench is a ping-pong: both sides call write() before read(), so a single write_size payload must fit entirely in the pipe buffer or both sides deadlock waiting for the other to drain. Resize the pipe via F_SETPIPE_SZ to match write_size (skipped at the sizeof(int) default), and error out cleanly when the request exceeds /proc/sys/fs/pipe-max-size. Committer testing: ⬢ [acme@toolbx perf-tools-next]$ perf bench sched pipe # Running 'sched/pipe' benchmark: # Executed 1000000 pipe operations between two processes Total time: 0.915 [sec] 0.915493 usecs/op 1092307 ops/sec ⬢ [acme@toolbx perf-tools-next]$ perf bench sched pipe --write-size 1024 # Running 'sched/pipe' benchmark: # Executed 1000000 pipe operations between two processes Total time: 0.891 [sec] 0.891915 usecs/op 1121183 ops/sec ⬢ [acme@toolbx perf-tools-next]$ perf bench sched pipe --write-size 4096 # Running 'sched/pipe' benchmark: # Executed 1000000 pipe operations between two processes Total time: 1.366 [sec] 1.366073 usecs/op 732025 ops/sec ⬢ [acme@toolbx perf-tools-next]$ strace -e fcntl perf bench sched pipe --write-size 4096 # Running 'sched/pipe' benchmark: fcntl(4, F_SETPIPE_SZ, 4096) = 4096 fcntl(6, F_SETPIPE_SZ, 4096) = 4096 ^Cstrace: Process 17840 detached ⬢ [acme@toolbx perf-tools-next]$ strace -e fcntl perf bench sched pipe --write-size 1024 # Running 'sched/pipe' benchmark: fcntl(4, F_SETPIPE_SZ, 1024) = 4096 fcntl(6, F_SETPIPE_SZ, 1024) = 4096 ^Cstrace: Process 17845 detached ⬢ [acme@toolbx perf-tools-next]$ strace -e fcntl perf bench sched pipe # Running 'sched/pipe' benchmark: ^Cstrace: Process 17851 detached ⬢ [acme@toolbx perf-tools-next]$ ⬢ [acme@toolbx perf-tools-next]$ perf bench sched pipe --write-size 1048577 # Running 'sched/pipe' benchmark: --write-size 1048577 exceeds /proc/sys/fs/pipe-max-size ⬢ [acme@toolbx perf-tools-next]$ cat /proc/sys/fs/pipe-max-size 1048576 ⬢ [acme@toolbx perf-tools-next]$ acme@number:~/git/perf-tools-next$ Signed-off-by: Breno Leitao Acked-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/bench/sched-pipe.c | 129 ++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 13 deletions(-) diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 70139036d68f..eb20c6d73d06 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -39,6 +41,7 @@ struct thread_data { int epoll_fd; bool cgroup_failed; pthread_t pthread; + char *buf; }; #define LOOPS_DEFAULT 1000000 @@ -48,6 +51,7 @@ static int loops = LOOPS_DEFAULT; static bool threaded; static bool nonblocking; +static unsigned int write_size = sizeof(int); static char *cgrp_names[2]; static struct cgroup *cgrps[2]; @@ -88,6 +92,8 @@ static const struct option options[] = { OPT_BOOLEAN('n', "nonblocking", &nonblocking, "Use non-blocking operations"), OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"), + OPT_UINTEGER('s', "write-size", &write_size, + "Bytes per ping-pong write (default 4-bytes). Use larger values to exercise the pipe page-allocation path."), OPT_CALLBACK('G', "cgroups", NULL, "SEND,RECV", "Put sender and receivers in given cgroups", parse_two_cgroups), @@ -170,25 +176,77 @@ static void exit_cgroup(int nr) free(cgrp_names[nr]); } +/* Sleep until @fd is writable, so we don't busy-spin on EWOULDBLOCK. */ +static inline void wait_writable(int fd) +{ + struct pollfd pfd = { + .fd = fd, + .events = POLLOUT, + }; + + poll(&pfd, 1, -1); +} + +/* + * Loop on short read()/write(): the kernel may return fewer bytes than + * requested, retry on EINTR, and in non-blocking mode wait via poll() + * when the writer transiently hits EWOULDBLOCK while the peer is still + * draining a full pipe (capacity is sized to write_size). + */ +static inline int write_pipe(struct thread_data *td) +{ + unsigned int done = 0; + int ret; + + while (done < write_size) { + ret = write(td->pipe_write, td->buf + done, write_size - done); + if (ret < 0) { + if (errno == EINTR) + continue; + if (nonblocking && errno == EWOULDBLOCK) { + wait_writable(td->pipe_write); + continue; + } + return ret; + } + done += ret; + } + return done; +} + static inline int read_pipe(struct thread_data *td) { - int ret, m; -retry: - if (nonblocking) { - ret = epoll_wait(td->epoll_fd, &td->epoll_ev, 1, -1); - if (ret < 0) + unsigned int done = 0; + int ret; + + while (done < write_size) { + if (nonblocking) { + ret = epoll_wait(td->epoll_fd, &td->epoll_ev, 1, -1); + if (ret < 0) { + if (errno == EINTR) + continue; + return ret; + } + } + ret = read(td->pipe_read, td->buf + done, write_size - done); + if (ret < 0) { + if (errno == EINTR) + continue; + if (nonblocking && errno == EWOULDBLOCK) + continue; return ret; + } + if (ret == 0) + return done; + done += ret; } - ret = read(td->pipe_read, &m, sizeof(int)); - if (nonblocking && ret < 0 && errno == EWOULDBLOCK) - goto retry; - return ret; + return done; } static void *worker_thread(void *__tdata) { struct thread_data *td = __tdata; - int i, ret, m = 0; + int i, ret; ret = enter_cgroup(td->nr); if (ret < 0) { @@ -204,15 +262,38 @@ static void *worker_thread(void *__tdata) } for (i = 0; i < loops; i++) { - ret = write(td->pipe_write, &m, sizeof(int)); - BUG_ON(ret != sizeof(int)); + ret = write_pipe(td); + BUG_ON(ret != (int)write_size); ret = read_pipe(td); - BUG_ON(ret != sizeof(int)); + BUG_ON(ret != (int)write_size); } return NULL; } +/* + * On a custom write_size, resize the pipes so a single payload fits. + */ +static int resize_pipes(int wfd1, int wfd2) +{ + int r1, r2; + + if (write_size <= sizeof(int)) + return 0; + + r1 = fcntl(wfd1, F_SETPIPE_SZ, write_size); + r2 = fcntl(wfd2, F_SETPIPE_SZ, write_size); + if (r1 < 0 || r2 < 0 || + (unsigned int)r1 < write_size || + (unsigned int)r2 < write_size) { + fprintf(stderr, + "--write-size %u exceeds /proc/sys/fs/pipe-max-size\n", + write_size); + return -1; + } + return 0; +} + int bench_sched_pipe(int argc, const char **argv) { struct thread_data threads[2] = {}; @@ -233,12 +314,31 @@ int bench_sched_pipe(int argc, const char **argv) argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0); + /* + * The error paths below return early without closing the pipes or + * freeing the cgroup state. That is fine: bench_sched_pipe() runs + * once and the process exits right after it returns, so these are + * not real leaks. + */ + if (write_size == 0 || write_size > INT_MAX) { + fprintf(stderr, "--write-size must be in 1..%d\n", INT_MAX); + return -1; + } + if (nonblocking) flags |= O_NONBLOCK; BUG_ON(pipe2(pipe_1, flags)); BUG_ON(pipe2(pipe_2, flags)); + if (resize_pipes(pipe_1[1], pipe_2[1]) < 0) + return -1; + + for (t = 0; t < nr_threads; t++) { + threads[t].buf = calloc(1, write_size); + BUG_ON(!threads[t].buf); + } + gettimeofday(&start, NULL); for (t = 0; t < nr_threads; t++) { @@ -287,6 +387,9 @@ int bench_sched_pipe(int argc, const char **argv) gettimeofday(&stop, NULL); timersub(&stop, &start, &diff); + for (t = 0; t < nr_threads; t++) + free(threads[t].buf); + exit_cgroup(0); exit_cgroup(1); From 7a92b1e22dc267e1e21c66a6b64b147726050f30 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 31 May 2026 01:22:30 +0530 Subject: [PATCH 2790/5214] perf lock: Fix non-atomic max/time and min_time updates in contention_data The update_contention_data() had a FIXME noting that max_time and min_time updates lacked atomicity. Two CPUs could simultaneously read a stale value, pass the comparison check and race on the write-back, with the smaller value potentially overwriting the larger one and silently corrupting the statistics. Fix this by replacing the bare conditional assignments with a bpf_loop()-based CAS retry loop. Each field tracks its own convergence independently via max_done/min_done flags in cas_ctx, so a successful CAS on one field is never retried even if the other field needs more attempts. Signed-off-by: Suchit Karunakaran Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/bpf_skel/lock_contention.bpf.c | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index 96e7d853b9ed..5c8431be674a 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -175,6 +175,13 @@ struct mm_struct___new { struct rw_semaphore mmap_lock; } __attribute__((preserve_access_index)); +struct cas_ctx { + struct contention_data *data; + u64 duration; + int max_done; + int min_done; +}; + extern struct kmem_cache *bpf_get_kmem_cache(u64 addr) __ksym __weak; /* control flags */ @@ -486,16 +493,49 @@ static inline s32 get_owner_stack_id(u64 *stacktrace) return -1; } +static long cas_min_max_cb(u64 idx, void *arg) +{ + struct cas_ctx *ctx = arg; + + if (!ctx->max_done) { + u64 old_max = ctx->data->max_time; + if (old_max >= ctx->duration) { + ctx->max_done = 1; + } else { + u64 r = __sync_val_compare_and_swap( + &ctx->data->max_time, old_max, ctx->duration); + if (r == old_max) + ctx->max_done = 1; + } + } + + if (!ctx->min_done) { + u64 old_min = ctx->data->min_time; + if (old_min <= ctx->duration) { + ctx->min_done = 1; + } else { + u64 r = __sync_val_compare_and_swap( + &ctx->data->min_time, old_min, ctx->duration); + if (r == old_min) + ctx->min_done = 1; + } + } + + return (ctx->max_done && ctx->min_done) ? 1 : 0; +} + static inline void update_contention_data(struct contention_data *data, u64 duration, u32 count) { __sync_fetch_and_add(&data->total_time, duration); __sync_fetch_and_add(&data->count, count); - /* FIXME: need atomic operations */ - if (data->max_time < duration) - data->max_time = duration; - if (data->min_time > duration) - data->min_time = duration; + struct cas_ctx ctx = { + .data = data, + .duration = duration, + .max_done = 0, + .min_done = 0, + }; + bpf_loop(64, cas_min_max_cb, &ctx, 0); } static inline void update_owner_stat(u32 id, u64 duration, u32 flags) From 4caadd04f62104997cbf3c71364b0509e7b64108 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 31 May 2026 01:25:10 +0530 Subject: [PATCH 2791/5214] perf build: Compile BPF skeletons with -mcpu=v3 The lock_contention BPF program uses __sync_val_compare_and_swap() to atomically update the max_time and min_time fields in contention_data. This builtin lowers to the BPF_CMPXCHG instruction, which is only available in BPF ISA v3. Without an explicit -mcpu flag, Clang targets BPF v1/v2 by default on older toolchains (Clang < 18), causing build errors when v3 instructions are emitted. Add -mcpu=v3 to CLANG_OPTIONS, which is used exclusively in the BPF skeleton compilation rule. Reviewed-by: Namhyung Kim Signed-off-by: Suchit Karunakaran Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suchit Karunakaran Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/bpf_skel.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/bpf_skel.mak b/tools/perf/bpf_skel.mak index 7704e7e635d8..f2559de39f96 100644 --- a/tools/perf/bpf_skel.mak +++ b/tools/perf/bpf_skel.mak @@ -29,7 +29,7 @@ ifneq ($(CROSS_COMPILE),) CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%)) endif -CLANG_OPTIONS = -Wall +CLANG_OPTIONS = -Wall -mcpu=v3 CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH)) LIBBPF_INCLUDE := $(abspath $(or $(OUTPUT),.))/libbpf/include BPF_INCLUDE := -I$(SKEL_TMP_OUT)/.. -I$(SKEL_TOOL_OUT) -I$(LIBBPF_INCLUDE) $(CLANG_SYS_INCLUDES) From 7e1f74bbbed7d3bf2f0597f820eba15ec70f35fa Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 30 May 2026 18:09:24 -0700 Subject: [PATCH 2792/5214] tools build: Fix feature checks to touch target files on success In tools/build/feature/Makefile, test-clang-bpf-co-re.bin and test-bpftool-skeletons.bin redirected grep output but never touched or created the $@ target file upon success. Because the target file was never created on disk, Kbuild could never cache the result of the check. Consequently, Make treated the prerequisite as missing and continuously re-executed the Clang BPF backend and bpftool feature checks on every single sub-make evaluation during build startup, or on every incremental build. Refactor both feature check recipes to touch $@ on success. For test-clang-bpf-co-re.bin, group the shell pipeline within curly braces and redirect both stdout and stderr to .make.output to allow errors to be inspected and not appear in build output. List test-clang-bpf-co-re.bin's input C file as a dependency so modification triggers a rebuild. For test-bpftool-skeletons.bin, add it to the FILES list so that it will be cleaned. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Bill Wendling Cc: Costa Shulyupin Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Justin Stitt Cc: Leo Yan Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Yuzhuo Jing Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/Makefile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 4e4a92ef5e1e..9da5a4fd956a 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -72,7 +72,8 @@ FILES= \ test-file-handle.bin \ test-libpfm4.bin \ test-rust.bin \ - test-libopenssl.bin + test-libopenssl.bin \ + test-bpftool-skeletons.bin FILES := $(addprefix $(OUTPUT),$(FILES)) @@ -379,9 +380,9 @@ $(OUTPUT)test-libaio.bin: $(OUTPUT)test-libzstd.bin: $(BUILD) -lzstd -$(OUTPUT)test-clang-bpf-co-re.bin: - $(CLANG) -S -g --target=bpf -o - $(patsubst %.bin,%.c,$(@F)) | \ - grep BTF_KIND_VAR +$(OUTPUT)test-clang-bpf-co-re.bin: test-clang-bpf-co-re.c + { $(CLANG) -S -g --target=bpf -o - $< | \ + grep BTF_KIND_VAR; } > $(@:.bin=.make.output) 2>&1 && touch $@ $(OUTPUT)test-file-handle.bin: $(BUILD) @@ -393,8 +394,8 @@ $(OUTPUT)test-libopenssl.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags openssl 2>/dev/null) $(OUTPUT)test-bpftool-skeletons.bin: - $(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons' \ - > $(@:.bin=.make.output) 2>&1 + { $(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons'; } \ + > $(@:.bin=.make.output) 2>&1 && touch $@ # Testing Rust is special: we don't compile anything, it's enough to check the # compiler presence. Compiling a test code for this purposes is problematic, From e565ceb48bbf82cc4db4a42a33e378fe30d8d010 Mon Sep 17 00:00:00 2001 From: Chun-Tse Shao Date: Thu, 21 May 2026 13:15:04 -0700 Subject: [PATCH 2793/5214] perf stat: Add aggr_nr metric parser support Introduce the `aggr_nr` function to the metric expression parser. `aggr_nr` allows metric formulas to dynamically utilize the number of aggregated targets (`aggr->nr`) instead of relying on the static `source_count` (which represents the static socket or node count). This adds the `AGGR_NR` token to the lexer and parser, updates the expression parsing context helpers to store `aggr_nr`, and feeds `aggr->nr` from the aggregation structure in `prepare_metric`. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Chun-Tse Shao Tested-by: Zide Chen Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sandipan Das Cc: Thomas Falcon Cc: Yang Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/expr.c | 26 ++++++++++++++++++++++---- tools/perf/util/expr.h | 6 +++++- tools/perf/util/expr.l | 1 + tools/perf/util/expr.y | 24 +++++++++++++++++------- tools/perf/util/stat-shadow.c | 6 +++++- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index 644769e92708..232998fef72b 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -27,6 +27,7 @@ struct expr_id_data { struct { double val; int source_count; + int aggr_nr; } val; struct { double val; @@ -151,8 +152,8 @@ int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val) } /* Caller must make sure id is allocated */ -int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, - double val, int source_count) +int expr__add_id_val_source_count_aggr_nr(struct expr_parse_ctx *ctx, const char *id, + double val, int source_count, int aggr_nr) { struct expr_id_data *data_ptr = NULL, *old_data = NULL; char *old_key = NULL; @@ -163,6 +164,7 @@ int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, return -ENOMEM; data_ptr->val.val = val; data_ptr->val.source_count = source_count; + data_ptr->val.aggr_nr = aggr_nr; data_ptr->kind = EXPR_ID_DATA__VALUE; ret = hashmap__set(ctx->ids, id, data_ptr, &old_key, &old_data); @@ -171,12 +173,20 @@ int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, } else if (old_data) { data_ptr->val.val += old_data->val.val; data_ptr->val.source_count += old_data->val.source_count; + data_ptr->val.aggr_nr += old_data->val.aggr_nr; } free(old_key); free(old_data); return ret; } +/* Caller must make sure id is allocated */ +int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, + double val, int source_count) +{ + return expr__add_id_val_source_count_aggr_nr(ctx, id, val, source_count, 1); +} + int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) { struct expr_id_data *data_ptr = NULL, *old_data = NULL; @@ -390,8 +400,16 @@ double expr_id_data__value(const struct expr_id_data *data) double expr_id_data__source_count(const struct expr_id_data *data) { - assert(data->kind == EXPR_ID_DATA__VALUE); - return data->val.source_count; + if (data->kind == EXPR_ID_DATA__VALUE) + return data->val.source_count; + return 1.0; +} + +double expr_id_data__aggr_nr(const struct expr_id_data *data) +{ + if (data->kind == EXPR_ID_DATA__VALUE) + return data->val.aggr_nr; + return 1.0; } double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx) diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h index c0cec29ddc29..ed12e4007d2d 100644 --- a/tools/perf/util/expr.h +++ b/tools/perf/util/expr.h @@ -36,7 +36,9 @@ void expr__del_id(struct expr_parse_ctx *ctx, const char *id); int expr__add_id(struct expr_parse_ctx *ctx, const char *id); int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val); int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, - double val, int source_count); + double val, int source_count); +int expr__add_id_val_source_count_aggr_nr(struct expr_parse_ctx *ctx, const char *id, + double val, int source_count, int aggr_nr); int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref); int expr__get_id(struct expr_parse_ctx *ctx, const char *id, struct expr_id_data **data); @@ -53,6 +55,8 @@ int expr__find_ids(const char *expr, const char *one, double expr_id_data__value(const struct expr_id_data *data); double expr_id_data__source_count(const struct expr_id_data *data); +double expr_id_data__aggr_nr(const struct expr_id_data *data); + double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l index a2fc43159ee9..f16ccff278d0 100644 --- a/tools/perf/util/expr.l +++ b/tools/perf/util/expr.l @@ -121,6 +121,7 @@ min { return MIN; } if { return IF; } else { return ELSE; } source_count { return SOURCE_COUNT; } +aggr_nr { return AGGR_NR; } has_event { return HAS_EVENT; } strcmp_cpuid_str { return STRCMP_CPUID_STR; } NaN { return nan_value(yyscanner); } diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index e364790babb5..e20f649354cf 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -41,7 +41,7 @@ int expr_lex(YYSTYPE * yylval_param , void *yyscanner); } ids; } -%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR +%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT AGGR_NR HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR %left MIN MAX IF %left '|' %left '^' @@ -87,8 +87,14 @@ static struct ids union_expr(struct ids ids1, struct ids ids2) return result; } +enum expr_id_kind { + EXPR_ID_KIND__VALUE, + EXPR_ID_KIND__SOURCE_COUNT, + EXPR_ID_KIND__AGGR_NR, +}; + static struct ids handle_id(struct expr_parse_ctx *ctx, char *id, - bool compute_ids, bool source_count) + bool compute_ids, enum expr_id_kind kind) { struct ids result; @@ -101,9 +107,12 @@ static struct ids handle_id(struct expr_parse_ctx *ctx, char *id, result.val = NAN; if (expr__resolve_id(ctx, id, &data) == 0) { - result.val = source_count - ? expr_id_data__source_count(data) - : expr_id_data__value(data); + if (kind == EXPR_ID_KIND__SOURCE_COUNT) + result.val = expr_id_data__source_count(data); + else if (kind == EXPR_ID_KIND__AGGR_NR) + result.val = expr_id_data__aggr_nr(data); + else + result.val = expr_id_data__value(data); } result.ids = NULL; free(id); @@ -201,8 +210,9 @@ expr: NUMBER $$.val = $1; $$.ids = NULL; } -| ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } -| SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } +| ID { $$ = handle_id(ctx, $1, compute_ids, EXPR_ID_KIND__VALUE); } +| SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, EXPR_ID_KIND__SOURCE_COUNT); } +| AGGR_NR '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, EXPR_ID_KIND__AGGR_NR); } | HAS_EVENT '(' ID ')' { $$.val = expr__has_event(ctx, compute_ids, $3); diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index bc2d44df7baf..c17373bb0e1e 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -53,6 +53,7 @@ static int prepare_metric(struct perf_stat_config *config, for (i = 0; metric_events[i]; i++) { int source_count = 0, tool_aggr_idx; + int aggr_nr = 1; bool is_tool_time = tool_pmu__is_time_event(config, metric_events[i], &tool_aggr_idx); struct perf_stat_evsel *ps = metric_events[i]->stats; @@ -89,6 +90,7 @@ static int prepare_metric(struct perf_stat_config *config, */ val = NAN; source_count = 0; + aggr_nr = 0; } else { struct perf_stat_aggr *aggr = &ps->aggr[is_tool_time ? tool_aggr_idx : aggr_idx]; @@ -96,6 +98,7 @@ static int prepare_metric(struct perf_stat_config *config, if (aggr->counts.run == 0) { val = NAN; source_count = 0; + aggr_nr = 0; } else { val = aggr->counts.val; if (is_tool_time) { @@ -104,13 +107,14 @@ static int prepare_metric(struct perf_stat_config *config, } if (!source_count) source_count = evsel__source_count(metric_events[i]); + aggr_nr = aggr->nr ?: 1; } } n = strdup(evsel__metric_id(metric_events[i])); if (!n) return -ENOMEM; - expr__add_id_val_source_count(pctx, n, val, source_count); + expr__add_id_val_source_count_aggr_nr(pctx, n, val, source_count, aggr_nr); } for (int j = 0; metric_refs && metric_refs[j].metric_name; j++) { From ca156ab12b2e2718b35dd6d30e0aea7be0edb11d Mon Sep 17 00:00:00 2001 From: Chun-Tse Shao Date: Thu, 21 May 2026 13:15:05 -0700 Subject: [PATCH 2794/5214] perf stat: Use aggr_nr scaling for Intel uncore miss latency metrics Update `metric.py` to support the new `aggr_nr` keyword in the python metric generator. Replace the usage of `source_count` with `aggr_nr` in `IntelMissLat` inside `intel_metrics.py` so that uncore latency metrics (like `lpm_miss_lat`) scale correctly on multi-socket and SNC systems when aggregated globally. Additionally, update the validation bypass logic in `CheckEveryEvent()` inside `metric.py` to whitelist 'cha' and 'uncore' events. This prevents validation failures when compiling metrics referencing these PMU-specific uncore events. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Chun-Tse Shao Tested-by: Zide Chen Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Leo Yan Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sandipan Das Cc: Thomas Falcon Cc: Yang Li Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/intel_metrics.py | 6 +++--- tools/perf/pmu-events/metric.py | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/perf/pmu-events/intel_metrics.py b/tools/perf/pmu-events/intel_metrics.py index c3a5c2965f74..bc2b920d3a0d 100755 --- a/tools/perf/pmu-events/intel_metrics.py +++ b/tools/perf/pmu-events/intel_metrics.py @@ -7,7 +7,7 @@ import os import re from typing import Optional from common_metrics import Cycles -from metric import (d_ratio, has_event, max, source_count, CheckPmu, Event, +from metric import (d_ratio, has_event, max, aggr_nr, CheckPmu, Event, JsonEncodeMetric, JsonEncodeMetricGroupDescriptions, Literal, LoadEvents, Metric, MetricConstraint, MetricGroup, MetricRef, Select) @@ -735,10 +735,10 @@ def IntelMissLat() -> Optional[MetricGroup]: else: assert data_rd_loc_occ.name == "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL", data_rd_loc_occ - ticks_per_cha = ticks / source_count(data_rd_loc_ins) + ticks_per_cha = ticks / aggr_nr(data_rd_loc_ins) loc_lat = interval_sec * 1e9 * data_rd_loc_occ / \ (ticks_per_cha * data_rd_loc_ins) - ticks_per_cha = ticks / source_count(data_rd_rem_ins) + ticks_per_cha = ticks / aggr_nr(data_rd_rem_ins) rem_lat = interval_sec * 1e9 * data_rd_rem_occ / \ (ticks_per_cha * data_rd_rem_ins) return MetricGroup("lpm_miss_lat", [ diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index ac582db785fc..a91ccb5977f0 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -93,7 +93,7 @@ def CheckEveryEvent(*names: str) -> None: name = name[:name.find(':')] elif '/' in name: name = name[:name.find('/')] - if any([name.startswith(x) for x in ['amd', 'arm', 'cpu', 'msr', 'power']]): + if any([name.startswith(x) for x in ['amd', 'arm', 'cpu', 'msr', 'power', 'cha', 'uncore']]): continue if name not in all_events_all_models: raise Exception(f"Is {name} a named json event?") @@ -576,6 +576,11 @@ def source_count(event: Event) -> Function: return Function('source_count', event) +def aggr_nr(event: Event) -> Function: + # pylint: disable=invalid-name + return Function('aggr_nr', event) + + def has_event(event: Event) -> Function: # pylint: disable=redefined-builtin # pylint: disable=invalid-name @@ -762,7 +767,7 @@ def ParsePerfJson(orig: str) -> Expression: # Convert accidentally converted scientific notation constants back py = re.sub(r'([0-9]+)Event\(r"(e[0-9]*)"\)', r'\1\2', py) # Convert all the known keywords back from events to just the keyword - keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event', 'strcmp_cpuid_str'] + keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'aggr_nr', 'has_event', 'strcmp_cpuid_str'] for kw in keywords: py = re.sub(rf'Event\(r"{kw}"\)', kw, py) try: From 68018df3f55eba96a20dd703f5f276a6518f4963 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Thu, 28 May 2026 14:23:55 +0800 Subject: [PATCH 2795/5214] perf: Fix off-by-one stack buffer overflow in kallsyms__parse() In kallsyms__parse(), the loop reading symbol names iterates with i < sizeof(symbol_name), which allows i to reach sizeof(symbol_name) upon loop exit. The subsequent symbol_name[i] = '\0' then writes one byte past the end of the stack-allocated symbol_name[] array. Fix this by changing the loop bound to KSYM_NAME_LEN, so the null terminator always lands within the array. The overflow is triggerable by a kallsyms entry with a symbol name of KSYM_NAME_LEN+1 or more characters (e.g., long Rust mangled names or a malicious /proc/kallsyms). Fixes: 53df2b9344128984 ("libsymbols kallsyms: Parse using io api") Signed-off-by: Rui Qi Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/symbol/kallsyms.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c index e335ac2b9e19..d64bd9cc82a9 100644 --- a/tools/lib/symbol/kallsyms.c +++ b/tools/lib/symbol/kallsyms.c @@ -60,7 +60,7 @@ int kallsyms__parse(const char *filename, void *arg, read_to_eol(&io); continue; } - for (i = 0; i < sizeof(symbol_name); i++) { + for (i = 0; i < KSYM_NAME_LEN; i++) { ch = io__get_char(&io); if (ch < 0 || ch == '\n') break; @@ -68,6 +68,9 @@ int kallsyms__parse(const char *filename, void *arg, } symbol_name[i] = '\0'; + if (i == KSYM_NAME_LEN) + read_to_eol(&io); + err = process_symbol(arg, symbol_name, symbol_type, start); if (err) break; From 154b7c3ec4c1a85f5ff230678a1515c5f91bbc45 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 14:19:00 -0700 Subject: [PATCH 2796/5214] perf tools: Ensure event leader stays at head of evlist after sorting For evlist of a certain event/metric, the HEAD should be the event leader. In some scenarios where uncore_xxx_0 does not exist, the event leader is not the first element after sorting. For example, on my test machine uncore_iio_0 does not exist, the event leader is uncore_iio_2. However, in `evlist__cmp`, it was reordered based on the PMU name, which makes uncore_iio_1 the HEAD of evlist, breaking the following merge logic in `evsel__merge_aliases`. The patch adds a loop at the end of `parse_events__sort_events_and_fix_groups` to make sure the first wildcard match is the earliest entry in the list, updating pointers accordingly without breaking reordering detection. Tested on device lacks uncore_iio_0, and `perf test` looks good. Signed-off-by: Chun-Tse Shao Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Cc: Thomas Falcon Cc: Thomas Richter Signed-off-by: Ian Rogers Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1497e1f2a08c..943569e82b82 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2251,6 +2251,33 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) } last_event_was_forced_leader = (force_grouped_leader == pos); } + + /* + * Make sure the first wildcard match is the earliest entry in the list. + * Since list_sort might have reordered the aliases, the original leader + * might not be at the head of the list anymore. We find the first + * alias in the sorted list and make it the new leader, and redirect + * all other aliases to it. + */ + list_for_each_entry(pos, list, core.node) { + struct evsel *orig_leader = pos->first_wildcard_match; + + if (!orig_leader) + continue; + + if (orig_leader->first_wildcard_match) { + /* Original leader was redirected to a new leader */ + pos->first_wildcard_match = orig_leader->first_wildcard_match; + } else if (pos->core.idx < orig_leader->core.idx) { + /* + * We are earlier than the original leader in sorted order, + * and no earlier alias has claimed leadership yet. + */ + orig_leader->first_wildcard_match = pos; + pos->first_wildcard_match = NULL; + } + } + list_for_each_entry(pos, list, core.node) { struct evsel *pos_leader = evsel__leader(pos); From 74802634e4a7e556429417963a8cbda27dd8b4b3 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 20 Apr 2026 12:52:17 +0100 Subject: [PATCH 2797/5214] perf annotate: Fix crashes on empty annotate windows Annotate can open with an empty window if the disassembly tool fails. After the linked change, the TUI started assuming there was a current annotation line and could assert or segfault in the seek, refresh, and source-toggle paths. Handle empty annotate windows explicitly: set the asm entry count before resetting the browser, return early when refreshing an empty list, and ignore source line toggle when there is no current annotation line. Fixes the following when opening an annotation: perf: ui/browser.c:125: ui_browser__list_head_seek: Assertion `pos != NULL' failed. Aborted Fixes: e201757f7a0a901e ("perf annotate: Fix source code annotate with objdump") Assisted-by: GitHub Copilot:GPT-5.4 Signed-off-by: James Clark Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browser.c | 3 +++ tools/perf/ui/browsers/annotate.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index dc88427b4ae5..321187b204d3 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -513,6 +513,9 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) struct list_head *head = browser->entries; int row = 0; + if (browser->nr_entries == 0) + return 0; + if (browser->top == NULL || browser->top == browser->entries) browser->top = ui_browser__list_head_filter_entries(browser, head->next); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index e220c4dfc881..97ae4c86bebb 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -449,6 +449,9 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; + if (browser->b.nr_entries == 0) + return false; + browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); @@ -542,8 +545,8 @@ static void annotate_browser__show_full_location(struct ui_browser *browser) static void ui_browser__init_asm_mode(struct ui_browser *browser) { struct annotation *notes = browser__annotation(browser); - ui_browser__reset_index(browser); browser->nr_entries = notes->src->nr_asm_entries; + ui_browser__reset_index(browser); } static int sym_title(struct symbol *sym, struct map *map, char *title, From bded0398ac19bc808984f50023bb482ce41f7bdd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:12 -0700 Subject: [PATCH 2798/5214] perf tpebs: Fix concurrent stop races and PID reuse hazards in tpebs_stop Parallel verbose test execution can trigger a race condition in tpebs_stop if called concurrently or when PID reuse occurs, causing finish_command() to block or reap the wrong process. Introduce a `tpebs_stopping` flag inside intel-tpebs.c to prevent redundant stop execution paths, and safely restore the `cmd.pid` temporarily only during `finish_command()` to ensure it is properly reaped, while preventing other threads from referencing it. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-tpebs.c | 92 ++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index ed8cfe2ba2fa..bc3b79bfa01a 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -37,6 +37,7 @@ static pthread_t tpebs_reader_thread; static struct child_process tpebs_cmd; static int control_fd[2], ack_fd[2]; static struct mutex tpebs_mtx; +static bool tpebs_stopping; struct tpebs_retire_lat { struct list_head nd; @@ -52,16 +53,18 @@ struct tpebs_retire_lat { bool started; }; -static void tpebs_mtx_init(void) +static void tpebs_init(void) { mutex_init(&tpebs_mtx); + control_fd[0] = control_fd[1] = -1; + ack_fd[0] = ack_fd[1] = -1; } static struct mutex *tpebs_mtx_get(void) { - static pthread_once_t tpebs_mtx_once = PTHREAD_ONCE_INIT; + static pthread_once_t tpebs_once = PTHREAD_ONCE_INIT; - pthread_once(&tpebs_mtx_once, tpebs_mtx_init); + pthread_once(&tpebs_once, tpebs_init); return &tpebs_mtx; } @@ -111,6 +114,7 @@ static int evsel__tpebs_start_perf_record(struct evsel *evsel) /* Note, no workload given so system wide is implied. */ assert(tpebs_cmd.pid == 0); + memset(&tpebs_cmd, 0, sizeof(tpebs_cmd)); tpebs_cmd.argv = record_argv; tpebs_cmd.out = -1; ret = start_command(&tpebs_cmd); @@ -320,20 +324,43 @@ static int tpebs_stop(void) EXCLUSIVE_LOCKS_REQUIRED(tpebs_mtx_get()) { int ret = 0; + if (tpebs_stopping) + return 0; + /* Like tpebs_start, we should only run tpebs_end once. */ if (tpebs_cmd.pid != 0) { + pid_t actual_pid = tpebs_cmd.pid; + + tpebs_stopping = true; tpebs_send_record_cmd(EVLIST_CTL_CMD_STOP_TAG); tpebs_cmd.pid = 0; mutex_unlock(tpebs_mtx_get()); pthread_join(tpebs_reader_thread, NULL); mutex_lock(tpebs_mtx_get()); - close(control_fd[0]); - close(control_fd[1]); - close(ack_fd[0]); - close(ack_fd[1]); - close(tpebs_cmd.out); + if (control_fd[0] >= 0) { + close(control_fd[0]); + control_fd[0] = -1; + } + if (control_fd[1] >= 0) { + close(control_fd[1]); + control_fd[1] = -1; + } + if (ack_fd[0] >= 0) { + close(ack_fd[0]); + ack_fd[0] = -1; + } + if (ack_fd[1] >= 0) { + close(ack_fd[1]); + ack_fd[1] = -1; + } + if (tpebs_cmd.out >= 0) { + close(tpebs_cmd.out); + tpebs_cmd.out = -1; + } + tpebs_cmd.pid = actual_pid; ret = finish_command(&tpebs_cmd); tpebs_cmd.pid = 0; + tpebs_stopping = false; if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) ret = 0; } @@ -486,30 +513,42 @@ int evsel__tpebs_open(struct evsel *evsel) { int ret; bool tpebs_empty; + bool started_process = false; /* We should only run tpebs_start when tpebs_recording is enabled. */ if (!tpebs_recording) return 0; + + mutex_lock(tpebs_mtx_get()); + if (tpebs_stopping) { + mutex_unlock(tpebs_mtx_get()); + return -EBUSY; + } /* Only start the events once. */ if (tpebs_cmd.pid != 0) { struct tpebs_retire_lat *t; bool valid; - mutex_lock(tpebs_mtx_get()); t = tpebs_retire_lat__find(evsel); valid = t && t->started; mutex_unlock(tpebs_mtx_get()); /* May fail as the event wasn't started. */ return valid ? 0 : -EBUSY; } + mutex_unlock(tpebs_mtx_get()); ret = evsel__tpebs_prepare(evsel); if (ret) return ret; mutex_lock(tpebs_mtx_get()); + if (tpebs_stopping || tpebs_cmd.pid != 0) { + ret = -EBUSY; + goto out; + } tpebs_empty = list_empty(&tpebs_results); if (!tpebs_empty) { + started_process = true; /*Create control and ack fd for --control*/ if (pipe(control_fd) < 0) { pr_err("tpebs: Failed to create control fifo"); @@ -529,7 +568,6 @@ int evsel__tpebs_open(struct evsel *evsel) if (pthread_create(&tpebs_reader_thread, /*attr=*/NULL, __sample_reader, /*arg=*/NULL)) { kill(tpebs_cmd.pid, SIGTERM); - close(tpebs_cmd.out); pr_err("Could not create thread to process sample data.\n"); ret = -1; goto out; @@ -540,8 +578,38 @@ int evsel__tpebs_open(struct evsel *evsel) if (ret) { struct tpebs_retire_lat *t = tpebs_retire_lat__find(evsel); - list_del_init(&t->nd); - tpebs_retire_lat__delete(t); + if (t) { + list_del_init(&t->nd); + tpebs_retire_lat__delete(t); + } + + if (started_process) { + if (tpebs_cmd.pid > 0) { + kill(tpebs_cmd.pid, SIGTERM); + finish_command(&tpebs_cmd); + tpebs_cmd.pid = 0; + } + if (tpebs_cmd.out >= 0) { + close(tpebs_cmd.out); + tpebs_cmd.out = -1; + } + if (control_fd[0] >= 0) { + close(control_fd[0]); + control_fd[0] = -1; + } + if (control_fd[1] >= 0) { + close(control_fd[1]); + control_fd[1] = -1; + } + if (ack_fd[0] >= 0) { + close(ack_fd[0]); + ack_fd[0] = -1; + } + if (ack_fd[1] >= 0) { + close(ack_fd[1]); + ack_fd[1] = -1; + } + } } mutex_unlock(tpebs_mtx_get()); return ret; From 2c43c0c78668a7026967ab8280f0f9ffc04e65f4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:13 -0700 Subject: [PATCH 2799/5214] perf jevents.py: Make generated C code more kernel style Make jevents.py generate C code that complies with formatting tools: - Add /* clang-format off */ before big_c_string and re-enable it after system mapping tables, bypassing large generated tables while checking functions and early structs. - Make comments more human readable and avoid going over 100 character line length. - Fix spaces indentation to tabs in struct/array initializers. - Fix other checkpatch detected related issues. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/empty-pmu-events.c | 8703 ++++++++++++++-------- tools/perf/pmu-events/jevents.py | 719 +- 2 files changed, 6022 insertions(+), 3400 deletions(-) diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index a92dd0424f79..ad5ade37adb0 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -1,6 +1,5 @@ - /* SPDX-License-Identifier: GPL-2.0 */ -/* THIS FILE WAS AUTOGENERATED BY jevents.py arch=none model=none ! */ +/* THIS FILE WAS AUTOGENERATED BY `jevents.py arch=none model=none` ! */ #include #include "util/header.h" @@ -9,2777 +8,5403 @@ #include struct compact_pmu_event { - int offset; + int offset; }; struct pmu_table_entry { - const struct compact_pmu_event *entries; - uint32_t num_entries; - struct compact_pmu_event pmu_name; + const struct compact_pmu_event *entries; + uint32_t num_entries; + struct compact_pmu_event pmu_name; }; +/* clang-format off */ static const char *const big_c_string = -/* offset=0 */ "default_core\000" -/* offset=13 */ "l1-dcache\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=99 */ "l1-dcache-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=190 */ "l1-dcache-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=286 */ "l1-dcache-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=387 */ "l1-dcache-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=482 */ "l1-dcache-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=580 */ "l1-dcache-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00000\000\000\000\000\000" -/* offset=682 */ "l1-dcache-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=782 */ "l1-dcache-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00000\000\000\000\000\000" -/* offset=874 */ "l1-dcache-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=971 */ "l1-dcache-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1073 */ "l1-dcache-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1169 */ "l1-dcache-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1268 */ "l1-dcache-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=1371 */ "l1-dcache-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=1472 */ "l1-dcache-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1563 */ "l1-dcache-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1659 */ "l1-dcache-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1760 */ "l1-dcache-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1855 */ "l1-dcache-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=1953 */ "l1-dcache-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=2055 */ "l1-dcache-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=2155 */ "l1-dcache-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=2252 */ "l1-dcache-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=2354 */ "l1-dcache-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=2461 */ "l1-dcache-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=2562 */ "l1-dcache-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=2666 */ "l1-dcache-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00000\000\000\000\000\000" -/* offset=2770 */ "l1-dcache-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=2872 */ "l1-dcache-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00000\000\000\000\000\000" -/* offset=2970 */ "l1-dcache-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3073 */ "l1-dcache-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3181 */ "l1-dcache-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3283 */ "l1-dcache-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3388 */ "l1-dcache-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=3493 */ "l1-dcache-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=3596 */ "l1-dcache-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3693 */ "l1-dcache-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3795 */ "l1-dcache-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=3902 */ "l1-dcache-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=4003 */ "l1-dcache-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=4107 */ "l1-dcache-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=4211 */ "l1-dcache-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=4313 */ "l1-dcache-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=4416 */ "l1-dcache-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=4524 */ "l1-dcache-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=4637 */ "l1-dcache-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=4744 */ "l1-dcache-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=4854 */ "l1-dcache-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00000\000\000\000\000\000" -/* offset=4964 */ "l1-dcache-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=5072 */ "l1-dcache-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00000\000\000\000\000\000" -/* offset=5177 */ "l1-dcache-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=5287 */ "l1-dcache-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=5402 */ "l1-dcache-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=5511 */ "l1-dcache-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=5623 */ "l1-dcache-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=5735 */ "l1-dcache-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=5845 */ "l1-dcache-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=5956 */ "l1-dcache-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6072 */ "l1-dcache-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6193 */ "l1-dcache-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6308 */ "l1-dcache-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6426 */ "l1-dcache-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=6544 */ "l1-dcache-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=6660 */ "l1-dcache-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6771 */ "l1-dcache-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=6887 */ "l1-dcache-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=7008 */ "l1-dcache-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=7123 */ "l1-dcache-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=7241 */ "l1-dcache-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=7359 */ "l1-dcache-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=7475 */ "l1-dcache-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=7566 */ "l1-dcache-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=7662 */ "l1-dcache-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=7752 */ "l1-dcache-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=7845 */ "l1-dcache-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=7942 */ "l1-dcache-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=8037 */ "l1-d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8118 */ "l1-d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8204 */ "l1-d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8295 */ "l1-d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8391 */ "l1-d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8481 */ "l1-d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8574 */ "l1-d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=8671 */ "l1-d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=8766 */ "l1-d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8853 */ "l1-d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=8945 */ "l1-d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9042 */ "l1-d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9133 */ "l1-d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9227 */ "l1-d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=9325 */ "l1-d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=9421 */ "l1-d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9507 */ "l1-d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9598 */ "l1-d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9694 */ "l1-d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9784 */ "l1-d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=9877 */ "l1-d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=9974 */ "l1-d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=10069 */ "l1-d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10161 */ "l1-d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10258 */ "l1-d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10360 */ "l1-d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10456 */ "l1-d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10555 */ "l1-d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=10654 */ "l1-d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=10751 */ "l1-d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10844 */ "l1-d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=10942 */ "l1-d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11045 */ "l1-d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11142 */ "l1-d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11242 */ "l1-d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=11342 */ "l1-d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=11440 */ "l1-d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11532 */ "l1-d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11629 */ "l1-d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11731 */ "l1-d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11827 */ "l1-d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=11926 */ "l1-d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=12025 */ "l1-d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=12122 */ "l1-d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12220 */ "l1-d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12323 */ "l1-d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12431 */ "l1-d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12533 */ "l1-d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12638 */ "l1-d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=12743 */ "l1-d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=12846 */ "l1-d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=12946 */ "l1-d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13051 */ "l1-d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13161 */ "l1-d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13265 */ "l1-d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13372 */ "l1-d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=13479 */ "l1-d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=13584 */ "l1-d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13690 */ "l1-d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13801 */ "l1-d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=13917 */ "l1-d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14027 */ "l1-d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14140 */ "l1-d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=14253 */ "l1-d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=14364 */ "l1-d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14470 */ "l1-d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14581 */ "l1-d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14697 */ "l1-d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14807 */ "l1-d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=14920 */ "l1-d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=15033 */ "l1-d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=15144 */ "l1-d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15230 */ "l1-d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15321 */ "l1-d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15406 */ "l1-d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15494 */ "l1-d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=15586 */ "l1-d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=15676 */ "l1d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15756 */ "l1d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15841 */ "l1d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=15931 */ "l1d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16026 */ "l1d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16115 */ "l1d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16207 */ "l1d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=16303 */ "l1d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=16397 */ "l1d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16483 */ "l1d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16574 */ "l1d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16670 */ "l1d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16760 */ "l1d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=16853 */ "l1d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=16950 */ "l1d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=17045 */ "l1d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=17130 */ "l1d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=17220 */ "l1d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=17315 */ "l1d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=17404 */ "l1d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=17496 */ "l1d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=17592 */ "l1d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=17686 */ "l1d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=17777 */ "l1d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=17873 */ "l1d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=17974 */ "l1d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18069 */ "l1d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18167 */ "l1d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=18265 */ "l1d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=18361 */ "l1d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18453 */ "l1d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18550 */ "l1d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18652 */ "l1d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18748 */ "l1d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=18847 */ "l1d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=18946 */ "l1d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=19043 */ "l1d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=19134 */ "l1d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=19230 */ "l1d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=19331 */ "l1d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=19426 */ "l1d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=19524 */ "l1d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=19622 */ "l1d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=19718 */ "l1d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=19815 */ "l1d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=19917 */ "l1d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20024 */ "l1d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20125 */ "l1d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20229 */ "l1d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=20333 */ "l1d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=20435 */ "l1d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20534 */ "l1d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20638 */ "l1d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20747 */ "l1d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20850 */ "l1d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=20956 */ "l1d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=21062 */ "l1d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=21166 */ "l1d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=21271 */ "l1d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=21381 */ "l1d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=21496 */ "l1d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=21605 */ "l1d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=21717 */ "l1d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=21829 */ "l1d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=21939 */ "l1d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=22044 */ "l1d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=22154 */ "l1d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=22269 */ "l1d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=22378 */ "l1d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=22490 */ "l1d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=22602 */ "l1d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=22712 */ "l1d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=22797 */ "l1d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=22887 */ "l1d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=22971 */ "l1d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23058 */ "l1d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=23149 */ "l1d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=23238 */ "l1-data\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23322 */ "l1-data-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23411 */ "l1-data-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23505 */ "l1-data-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23604 */ "l1-data-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23697 */ "l1-data-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=23793 */ "l1-data-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=23893 */ "l1-data-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=23991 */ "l1-data-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24081 */ "l1-data-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24176 */ "l1-data-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24276 */ "l1-data-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24370 */ "l1-data-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24467 */ "l1-data-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=24568 */ "l1-data-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=24667 */ "l1-data-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24756 */ "l1-data-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24850 */ "l1-data-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=24949 */ "l1-data-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=25042 */ "l1-data-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=25138 */ "l1-data-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=25238 */ "l1-data-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=25336 */ "l1-data-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=25431 */ "l1-data-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=25531 */ "l1-data-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=25636 */ "l1-data-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=25735 */ "l1-data-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=25837 */ "l1-data-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=25939 */ "l1-data-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=26039 */ "l1-data-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26135 */ "l1-data-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26236 */ "l1-data-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26342 */ "l1-data-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26442 */ "l1-data-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26545 */ "l1-data-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=26648 */ "l1-data-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=26749 */ "l1-data-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26844 */ "l1-data-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=26944 */ "l1-data-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=27049 */ "l1-data-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=27148 */ "l1-data-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" -/* offset=27250 */ "l1-data-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=27352 */ "l1-data-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" -/* offset=27452 */ "l1-data-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=27553 */ "l1-data-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=27659 */ "l1-data-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=27770 */ "l1-data-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=27875 */ "l1-data-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=27983 */ "l1-data-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=28091 */ "l1-data-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=28197 */ "l1-data-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=28300 */ "l1-data-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=28408 */ "l1-data-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=28521 */ "l1-data-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=28628 */ "l1-data-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=28738 */ "l1-data-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=28848 */ "l1-data-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=28956 */ "l1-data-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29065 */ "l1-data-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29179 */ "l1-data-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29298 */ "l1-data-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29411 */ "l1-data-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29527 */ "l1-data-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=29643 */ "l1-data-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=29757 */ "l1-data-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29866 */ "l1-data-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=29980 */ "l1-data-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=30099 */ "l1-data-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=30212 */ "l1-data-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" -/* offset=30328 */ "l1-data-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=30444 */ "l1-data-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" -/* offset=30558 */ "l1-data-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=30647 */ "l1-data-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=30741 */ "l1-data-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=30829 */ "l1-data-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" -/* offset=30920 */ "l1-data-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=31015 */ "l1-data-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" -/* offset=31108 */ "l1-icache\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31201 */ "l1-icache-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31299 */ "l1-icache-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31402 */ "l1-icache-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31510 */ "l1-icache-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31612 */ "l1-icache-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=31717 */ "l1-icache-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00000\000\000\000\000\000" -/* offset=31826 */ "l1-icache-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=31933 */ "l1-icache-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00000\000\000\000\000\000" -/* offset=32032 */ "l1-icache-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32136 */ "l1-icache-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32245 */ "l1-icache-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32348 */ "l1-icache-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32454 */ "l1-icache-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=32564 */ "l1-icache-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=32672 */ "l1-icache-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32770 */ "l1-icache-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32873 */ "l1-icache-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=32981 */ "l1-icache-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=33083 */ "l1-icache-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=33188 */ "l1-icache-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=33297 */ "l1-icache-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=33404 */ "l1-icache-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=33514 */ "l1-icache-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=33629 */ "l1-icache-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=33749 */ "l1-icache-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=33863 */ "l1-icache-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=33980 */ "l1-icache-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00000\000\000\000\000\000" -/* offset=34097 */ "l1-icache-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=34212 */ "l1-icache-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00000\000\000\000\000\000" -/* offset=34324 */ "l1-icache-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=34441 */ "l1-icache-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=34563 */ "l1-icache-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=34679 */ "l1-icache-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=34798 */ "l1-icache-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=34917 */ "l1-icache-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=35034 */ "l1-icache-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=35152 */ "l1-icache-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=35275 */ "l1-icache-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=35403 */ "l1-icache-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=35525 */ "l1-icache-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=35650 */ "l1-icache-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=35775 */ "l1-icache-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=35898 */ "l1-icache-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=36016 */ "l1-icache-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=36139 */ "l1-icache-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=36267 */ "l1-icache-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=36389 */ "l1-icache-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=36514 */ "l1-icache-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=36639 */ "l1-icache-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=36762 */ "l1-icache-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=36860 */ "l1-icache-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=36963 */ "l1-icache-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37060 */ "l1-icache-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37160 */ "l1-icache-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=37264 */ "l1-icache-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=37366 */ "l1-i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37454 */ "l1-i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37547 */ "l1-i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37645 */ "l1-i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37748 */ "l1-i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37845 */ "l1-i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=37945 */ "l1-i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=38049 */ "l1-i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=38151 */ "l1-i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38245 */ "l1-i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38344 */ "l1-i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38448 */ "l1-i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38546 */ "l1-i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38647 */ "l1-i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=38752 */ "l1-i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=38855 */ "l1-i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=38948 */ "l1-i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=39046 */ "l1-i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=39149 */ "l1-i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=39246 */ "l1-i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=39346 */ "l1-i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=39450 */ "l1-i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=39552 */ "l1-i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=39657 */ "l1-i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=39767 */ "l1-i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=39882 */ "l1-i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=39991 */ "l1-i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40103 */ "l1-i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=40215 */ "l1-i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=40325 */ "l1-i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40432 */ "l1-i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40544 */ "l1-i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40661 */ "l1-i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40772 */ "l1-i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=40886 */ "l1-i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=41000 */ "l1-i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=41112 */ "l1-i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=41225 */ "l1-i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=41343 */ "l1-i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=41466 */ "l1-i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=41583 */ "l1-i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=41703 */ "l1-i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=41823 */ "l1-i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=41941 */ "l1-i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=42054 */ "l1-i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=42172 */ "l1-i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=42295 */ "l1-i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=42412 */ "l1-i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=42532 */ "l1-i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=42652 */ "l1-i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=42770 */ "l1-i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=42863 */ "l1-i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=42961 */ "l1-i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43053 */ "l1-i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43148 */ "l1-i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=43247 */ "l1-i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=43344 */ "l1i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43431 */ "l1i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43523 */ "l1i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43620 */ "l1i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43722 */ "l1i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43818 */ "l1i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=43917 */ "l1i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=44020 */ "l1i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=44121 */ "l1i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44214 */ "l1i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44312 */ "l1i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44415 */ "l1i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44512 */ "l1i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44612 */ "l1i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=44716 */ "l1i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=44818 */ "l1i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=44910 */ "l1i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=45007 */ "l1i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=45109 */ "l1i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=45205 */ "l1i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=45304 */ "l1i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=45407 */ "l1i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=45508 */ "l1i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=45612 */ "l1i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=45721 */ "l1i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=45835 */ "l1i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=45943 */ "l1i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46054 */ "l1i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=46165 */ "l1i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=46274 */ "l1i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46380 */ "l1i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46491 */ "l1i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46607 */ "l1i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46717 */ "l1i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=46830 */ "l1i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=46943 */ "l1i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=47054 */ "l1i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47166 */ "l1i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47283 */ "l1i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47405 */ "l1i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47521 */ "l1i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47640 */ "l1i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=47759 */ "l1i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=47876 */ "l1i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=47988 */ "l1i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=48105 */ "l1i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=48227 */ "l1i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=48343 */ "l1i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=48462 */ "l1i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=48581 */ "l1i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=48698 */ "l1i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=48790 */ "l1i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=48887 */ "l1i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=48978 */ "l1i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49072 */ "l1i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=49170 */ "l1i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=49266 */ "l1-instruction\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49364 */ "l1-instruction-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49467 */ "l1-instruction-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49575 */ "l1-instruction-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49688 */ "l1-instruction-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49795 */ "l1-instruction-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=49905 */ "l1-instruction-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=50019 */ "l1-instruction-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=50131 */ "l1-instruction-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=50235 */ "l1-instruction-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=50344 */ "l1-instruction-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=50458 */ "l1-instruction-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=50566 */ "l1-instruction-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=50677 */ "l1-instruction-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=50792 */ "l1-instruction-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=50905 */ "l1-instruction-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=51008 */ "l1-instruction-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=51116 */ "l1-instruction-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=51229 */ "l1-instruction-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=51336 */ "l1-instruction-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=51446 */ "l1-instruction-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=51560 */ "l1-instruction-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=51672 */ "l1-instruction-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=51787 */ "l1-instruction-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=51907 */ "l1-instruction-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52032 */ "l1-instruction-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52151 */ "l1-instruction-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52273 */ "l1-instruction-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=52395 */ "l1-instruction-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=52515 */ "l1-instruction-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52632 */ "l1-instruction-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52754 */ "l1-instruction-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=52881 */ "l1-instruction-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53002 */ "l1-instruction-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53126 */ "l1-instruction-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=53250 */ "l1-instruction-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=53372 */ "l1-instruction-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53495 */ "l1-instruction-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53623 */ "l1-instruction-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53756 */ "l1-instruction-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=53883 */ "l1-instruction-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54013 */ "l1-instruction-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=54143 */ "l1-instruction-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=54271 */ "l1-instruction-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54394 */ "l1-instruction-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54522 */ "l1-instruction-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54655 */ "l1-instruction-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54782 */ "l1-instruction-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" -/* offset=54912 */ "l1-instruction-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=55042 */ "l1-instruction-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" -/* offset=55170 */ "l1-instruction-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=55273 */ "l1-instruction-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=55381 */ "l1-instruction-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=55483 */ "l1-instruction-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" -/* offset=55588 */ "l1-instruction-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=55697 */ "l1-instruction-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" -/* offset=55804 */ "llc\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=55882 */ "llc-load\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=55965 */ "llc-load-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56053 */ "llc-load-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56146 */ "llc-load-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56233 */ "llc-load-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56323 */ "llc-load-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00000\000\000\000\000\000" -/* offset=56417 */ "llc-load-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=56509 */ "llc-loads\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00000\000\000\000\000\000" -/* offset=56593 */ "llc-loads-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56682 */ "llc-loads-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56776 */ "llc-loads-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56864 */ "llc-loads-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=56955 */ "llc-loads-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=57050 */ "llc-loads-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=57143 */ "llc-read\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=57226 */ "llc-read-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=57314 */ "llc-read-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=57407 */ "llc-read-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=57494 */ "llc-read-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=57584 */ "llc-read-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=57678 */ "llc-read-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=57770 */ "llc-store\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=57859 */ "llc-store-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=57953 */ "llc-store-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58052 */ "llc-store-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58145 */ "llc-store-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58241 */ "llc-store-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00000\000\000\000\000\000" -/* offset=58337 */ "llc-store-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=58431 */ "llc-stores\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00000\000\000\000\000\000" -/* offset=58521 */ "llc-stores-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58616 */ "llc-stores-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58716 */ "llc-stores-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58810 */ "llc-stores-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=58907 */ "llc-stores-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=59004 */ "llc-stores-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=59099 */ "llc-write\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=59188 */ "llc-write-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=59282 */ "llc-write-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=59381 */ "llc-write-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=59474 */ "llc-write-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=59570 */ "llc-write-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=59666 */ "llc-write-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=59760 */ "llc-prefetch\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=59855 */ "llc-prefetch-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=59955 */ "llc-prefetch-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60060 */ "llc-prefetch-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60159 */ "llc-prefetch-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60261 */ "llc-prefetch-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00000\000\000\000\000\000" -/* offset=60363 */ "llc-prefetch-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=60463 */ "llc-prefetches\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00000\000\000\000\000\000" -/* offset=60560 */ "llc-prefetches-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60662 */ "llc-prefetches-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60769 */ "llc-prefetches-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60870 */ "llc-prefetches-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=60974 */ "llc-prefetches-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=61078 */ "llc-prefetches-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=61180 */ "llc-speculative-read\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=61283 */ "llc-speculative-read-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=61391 */ "llc-speculative-read-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=61504 */ "llc-speculative-read-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=61611 */ "llc-speculative-read-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=61721 */ "llc-speculative-read-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=61831 */ "llc-speculative-read-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=61939 */ "llc-speculative-load\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=62042 */ "llc-speculative-load-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=62150 */ "llc-speculative-load-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=62263 */ "llc-speculative-load-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=62370 */ "llc-speculative-load-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=62480 */ "llc-speculative-load-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=62590 */ "llc-speculative-load-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=62698 */ "llc-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=62781 */ "llc-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=62869 */ "llc-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=62951 */ "llc-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63036 */ "llc-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=63125 */ "llc-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=63212 */ "l2\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63309 */ "l2-load\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63411 */ "l2-load-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63518 */ "l2-load-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63630 */ "l2-load-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63736 */ "l2-load-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=63845 */ "l2-load-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=63958 */ "l2-load-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=64069 */ "l2-loads\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64172 */ "l2-loads-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64280 */ "l2-loads-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64393 */ "l2-loads-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64500 */ "l2-loads-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64610 */ "l2-loads-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=64724 */ "l2-loads-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=64836 */ "l2-read\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=64938 */ "l2-read-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=65045 */ "l2-read-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=65157 */ "l2-read-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=65263 */ "l2-read-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=65372 */ "l2-read-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=65485 */ "l2-read-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=65596 */ "l2-store\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=65704 */ "l2-store-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=65817 */ "l2-store-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=65935 */ "l2-store-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66047 */ "l2-store-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66162 */ "l2-store-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=66277 */ "l2-store-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=66390 */ "l2-stores\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66499 */ "l2-stores-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66613 */ "l2-stores-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66732 */ "l2-stores-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66845 */ "l2-stores-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=66961 */ "l2-stores-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=67077 */ "l2-stores-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=67191 */ "l2-write\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=67299 */ "l2-write-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=67412 */ "l2-write-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=67530 */ "l2-write-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=67642 */ "l2-write-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" -/* offset=67757 */ "l2-write-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=67872 */ "l2-write-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" -/* offset=67985 */ "l2-prefetch\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68099 */ "l2-prefetch-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68218 */ "l2-prefetch-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68342 */ "l2-prefetch-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68460 */ "l2-prefetch-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68581 */ "l2-prefetch-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=68702 */ "l2-prefetch-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=68821 */ "l2-prefetches\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=68937 */ "l2-prefetches-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69058 */ "l2-prefetches-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69184 */ "l2-prefetches-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69304 */ "l2-prefetches-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69427 */ "l2-prefetches-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=69550 */ "l2-prefetches-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=69671 */ "l2-speculative-read\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69793 */ "l2-speculative-read-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=69920 */ "l2-speculative-read-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70052 */ "l2-speculative-read-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70178 */ "l2-speculative-read-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70307 */ "l2-speculative-read-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=70436 */ "l2-speculative-read-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=70563 */ "l2-speculative-load\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70685 */ "l2-speculative-load-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70812 */ "l2-speculative-load-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=70944 */ "l2-speculative-load-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=71070 */ "l2-speculative-load-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" -/* offset=71199 */ "l2-speculative-load-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=71328 */ "l2-speculative-load-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" -/* offset=71455 */ "l2-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=71557 */ "l2-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=71664 */ "l2-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=71765 */ "l2-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" -/* offset=71869 */ "l2-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=71977 */ "l2-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" -/* offset=72083 */ "dtlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72154 */ "dtlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72230 */ "dtlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72311 */ "dtlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72397 */ "dtlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72477 */ "dtlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72560 */ "dtlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00000\000\000\000\000\000" -/* offset=72647 */ "dtlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=72732 */ "dtlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00000\000\000\000\000\000" -/* offset=72809 */ "dtlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72891 */ "dtlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=72978 */ "dtlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73059 */ "dtlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73143 */ "dtlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=73231 */ "dtlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=73317 */ "dtlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73393 */ "dtlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73474 */ "dtlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73560 */ "dtlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73640 */ "dtlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=73723 */ "dtlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=73810 */ "dtlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=73895 */ "dtlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=73977 */ "dtlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74064 */ "dtlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74156 */ "dtlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74242 */ "dtlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74331 */ "dtlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00000\000\000\000\000\000" -/* offset=74420 */ "dtlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=74507 */ "dtlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00000\000\000\000\000\000" -/* offset=74590 */ "dtlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74678 */ "dtlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74771 */ "dtlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74858 */ "dtlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=74948 */ "dtlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=75038 */ "dtlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=75126 */ "dtlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=75208 */ "dtlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=75295 */ "dtlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=75387 */ "dtlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=75473 */ "dtlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=75562 */ "dtlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=75651 */ "dtlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=75738 */ "dtlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=75826 */ "dtlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=75919 */ "dtlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76017 */ "dtlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76109 */ "dtlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76204 */ "dtlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00000\000\000\000\000\000" -/* offset=76299 */ "dtlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=76392 */ "dtlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00000\000\000\000\000\000" -/* offset=76482 */ "dtlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76577 */ "dtlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76677 */ "dtlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76771 */ "dtlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=76868 */ "dtlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=76965 */ "dtlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=77060 */ "dtlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77156 */ "dtlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77257 */ "dtlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77363 */ "dtlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77463 */ "dtlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77566 */ "dtlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=77669 */ "dtlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=77770 */ "dtlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77866 */ "dtlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=77967 */ "dtlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=78073 */ "dtlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=78173 */ "dtlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=78276 */ "dtlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=78379 */ "dtlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=78480 */ "dtlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=78556 */ "dtlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=78637 */ "dtlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=78712 */ "dtlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=78790 */ "dtlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=78872 */ "dtlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=78952 */ "d-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79024 */ "d-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79101 */ "d-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79183 */ "d-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79270 */ "d-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79351 */ "d-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79435 */ "d-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=79523 */ "d-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=79609 */ "d-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79687 */ "d-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79770 */ "d-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79858 */ "d-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=79940 */ "d-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80025 */ "d-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=80114 */ "d-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=80201 */ "d-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80278 */ "d-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80360 */ "d-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80447 */ "d-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80528 */ "d-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=80612 */ "d-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=80700 */ "d-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=80786 */ "d-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=80869 */ "d-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=80957 */ "d-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81050 */ "d-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81137 */ "d-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81227 */ "d-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=81317 */ "d-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=81405 */ "d-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81489 */ "d-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81578 */ "d-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81672 */ "d-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81760 */ "d-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=81851 */ "d-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=81942 */ "d-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=82031 */ "d-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=82114 */ "d-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=82202 */ "d-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=82295 */ "d-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=82382 */ "d-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=82472 */ "d-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=82562 */ "d-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=82650 */ "d-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=82739 */ "d-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=82833 */ "d-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=82932 */ "d-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83025 */ "d-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83121 */ "d-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=83217 */ "d-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=83311 */ "d-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83402 */ "d-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83498 */ "d-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83599 */ "d-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83694 */ "d-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=83792 */ "d-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=83890 */ "d-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=83986 */ "d-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84083 */ "d-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84185 */ "d-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84292 */ "d-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84393 */ "d-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84497 */ "d-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=84601 */ "d-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=84703 */ "d-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84800 */ "d-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=84902 */ "d-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=85009 */ "d-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=85110 */ "d-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=85214 */ "d-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=85318 */ "d-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=85420 */ "d-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=85497 */ "d-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=85579 */ "d-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=85655 */ "d-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=85734 */ "d-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=85817 */ "d-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=85898 */ "data-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=85973 */ "data-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86053 */ "data-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86138 */ "data-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86228 */ "data-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86312 */ "data-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86399 */ "data-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=86490 */ "data-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=86579 */ "data-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86660 */ "data-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86746 */ "data-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86837 */ "data-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=86922 */ "data-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87010 */ "data-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=87102 */ "data-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=87192 */ "data-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87272 */ "data-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87357 */ "data-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87447 */ "data-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87531 */ "data-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=87618 */ "data-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=87709 */ "data-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=87798 */ "data-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=87884 */ "data-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=87975 */ "data-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88071 */ "data-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88161 */ "data-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88254 */ "data-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=88347 */ "data-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=88438 */ "data-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88525 */ "data-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88617 */ "data-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88714 */ "data-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88805 */ "data-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=88899 */ "data-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=88993 */ "data-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=89085 */ "data-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=89171 */ "data-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=89262 */ "data-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=89358 */ "data-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=89448 */ "data-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" -/* offset=89541 */ "data-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=89634 */ "data-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" -/* offset=89725 */ "data-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=89817 */ "data-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=89914 */ "data-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90016 */ "data-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90112 */ "data-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90211 */ "data-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=90310 */ "data-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=90407 */ "data-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90501 */ "data-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90600 */ "data-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90704 */ "data-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90802 */ "data-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=90903 */ "data-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=91004 */ "data-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=91103 */ "data-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91203 */ "data-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91308 */ "data-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91418 */ "data-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91522 */ "data-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91629 */ "data-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=91736 */ "data-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=91841 */ "data-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=91941 */ "data-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=92046 */ "data-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=92156 */ "data-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=92260 */ "data-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" -/* offset=92367 */ "data-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=92474 */ "data-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" -/* offset=92579 */ "data-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=92659 */ "data-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=92744 */ "data-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=92823 */ "data-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" -/* offset=92905 */ "data-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=92991 */ "data-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" -/* offset=93075 */ "itlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93153 */ "itlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93236 */ "itlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93324 */ "itlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93417 */ "itlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93504 */ "itlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93594 */ "itlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00000\000\000\000\000\000" -/* offset=93688 */ "itlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=93780 */ "itlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00000\000\000\000\000\000" -/* offset=93864 */ "itlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=93953 */ "itlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94047 */ "itlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94135 */ "itlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94226 */ "itlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=94321 */ "itlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=94414 */ "itlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94497 */ "itlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94585 */ "itlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94678 */ "itlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94765 */ "itlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=94855 */ "itlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=94949 */ "itlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=95041 */ "itlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95124 */ "itlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95212 */ "itlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95294 */ "itlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95379 */ "itlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=95468 */ "itlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=95555 */ "i-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95634 */ "i-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95718 */ "i-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95807 */ "i-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95901 */ "i-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=95989 */ "i-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96080 */ "i-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=96175 */ "i-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=96268 */ "i-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96353 */ "i-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96443 */ "i-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96538 */ "i-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96627 */ "i-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96719 */ "i-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=96815 */ "i-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=96909 */ "i-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=96993 */ "i-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97082 */ "i-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97176 */ "i-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97264 */ "i-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97355 */ "i-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=97450 */ "i-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=97543 */ "i-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97627 */ "i-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97716 */ "i-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97799 */ "i-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=97885 */ "i-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=97975 */ "i-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=98063 */ "instruction-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98152 */ "instruction-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98246 */ "instruction-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98345 */ "instruction-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98449 */ "instruction-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98547 */ "instruction-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98648 */ "instruction-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=98753 */ "instruction-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=98856 */ "instruction-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=98951 */ "instruction-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99051 */ "instruction-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99156 */ "instruction-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99255 */ "instruction-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99357 */ "instruction-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=99463 */ "instruction-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=99567 */ "instruction-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99661 */ "instruction-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99760 */ "instruction-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99864 */ "instruction-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=99962 */ "instruction-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=100063 */ "instruction-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=100168 */ "instruction-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=100271 */ "instruction-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=100365 */ "instruction-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=100464 */ "instruction-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=100557 */ "instruction-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" -/* offset=100653 */ "instruction-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=100753 */ "instruction-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" -/* offset=100851 */ "branch\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=100938 */ "branch-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101030 */ "branch-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101127 */ "branch-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101229 */ "branch-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101325 */ "branch-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101424 */ "branch-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00000\000\000\000\000\000" -/* offset=101527 */ "branch-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=101628 */ "branch-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00000\000\000\000\000\000" -/* offset=101721 */ "branch-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101819 */ "branch-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=101922 */ "branch-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102019 */ "branch-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102119 */ "branch-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=102223 */ "branch-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=102325 */ "branch-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102417 */ "branch-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102514 */ "branch-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102616 */ "branch-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102712 */ "branch-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=102811 */ "branch-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=102914 */ "branch-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=103015 */ "branch-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103107 */ "branch-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103204 */ "branch-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103295 */ "branch-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103389 */ "branch-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=103485 */ "branches-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103579 */ "branches-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103678 */ "branches-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103782 */ "branches-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103880 */ "branches-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=103981 */ "branches-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=104086 */ "branches-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=104189 */ "branches-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104284 */ "branches-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104384 */ "branches-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104489 */ "branches-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104588 */ "branches-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104690 */ "branches-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=104796 */ "branches-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=104900 */ "branches-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=104994 */ "branches-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105093 */ "branches-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105197 */ "branches-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105295 */ "branches-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105396 */ "branches-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=105501 */ "branches-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=105604 */ "branches-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105698 */ "branches-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105797 */ "branches-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105890 */ "branches-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=105986 */ "branches-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=106086 */ "branches-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=106184 */ "bpu\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106268 */ "bpu-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106357 */ "bpu-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106451 */ "bpu-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106550 */ "bpu-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106643 */ "bpu-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=106739 */ "bpu-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=106839 */ "bpu-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=106937 */ "bpu-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107027 */ "bpu-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107122 */ "bpu-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107222 */ "bpu-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107316 */ "bpu-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107413 */ "bpu-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=107514 */ "bpu-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=107613 */ "bpu-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107702 */ "bpu-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107796 */ "bpu-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107895 */ "bpu-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=107988 */ "bpu-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108084 */ "bpu-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=108184 */ "bpu-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=108282 */ "bpu-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108371 */ "bpu-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108465 */ "bpu-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108553 */ "bpu-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108644 */ "bpu-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=108739 */ "bpu-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=108832 */ "btb\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=108916 */ "btb-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109005 */ "btb-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109099 */ "btb-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109198 */ "btb-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109291 */ "btb-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109387 */ "btb-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=109487 */ "btb-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=109585 */ "btb-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109675 */ "btb-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109770 */ "btb-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109870 */ "btb-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=109964 */ "btb-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110061 */ "btb-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=110162 */ "btb-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=110261 */ "btb-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110350 */ "btb-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110444 */ "btb-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110543 */ "btb-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110636 */ "btb-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=110732 */ "btb-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=110832 */ "btb-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=110930 */ "btb-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111019 */ "btb-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111113 */ "btb-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111201 */ "btb-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111292 */ "btb-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=111387 */ "btb-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=111480 */ "bpc\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111564 */ "bpc-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111653 */ "bpc-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111747 */ "bpc-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111846 */ "bpc-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=111939 */ "bpc-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112035 */ "bpc-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=112135 */ "bpc-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=112233 */ "bpc-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112323 */ "bpc-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112418 */ "bpc-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112518 */ "bpc-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112612 */ "bpc-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112709 */ "bpc-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=112810 */ "bpc-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=112909 */ "bpc-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=112998 */ "bpc-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113092 */ "bpc-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113191 */ "bpc-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113284 */ "bpc-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113380 */ "bpc-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=113480 */ "bpc-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=113578 */ "bpc-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113667 */ "bpc-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113761 */ "bpc-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113849 */ "bpc-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" -/* offset=113940 */ "bpc-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=114035 */ "bpc-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" -/* offset=114128 */ "node\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114203 */ "node-load\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114283 */ "node-load-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114368 */ "node-load-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114458 */ "node-load-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114542 */ "node-load-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114629 */ "node-load-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00000\000\000\000\000\000" -/* offset=114720 */ "node-load-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=114809 */ "node-loads\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00000\000\000\000\000\000" -/* offset=114890 */ "node-loads-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=114976 */ "node-loads-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115067 */ "node-loads-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115152 */ "node-loads-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115240 */ "node-loads-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=115332 */ "node-loads-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=115422 */ "node-read\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115502 */ "node-read-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115587 */ "node-read-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115677 */ "node-read-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115761 */ "node-read-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=115848 */ "node-read-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=115939 */ "node-read-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=116028 */ "node-store\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116114 */ "node-store-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116205 */ "node-store-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116301 */ "node-store-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116391 */ "node-store-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116484 */ "node-store-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00000\000\000\000\000\000" -/* offset=116577 */ "node-store-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" -/* offset=116668 */ "node-stores\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00000\000\000\000\000\000" -/* offset=116755 */ "node-stores-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116847 */ "node-stores-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=116944 */ "node-stores-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117035 */ "node-stores-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117129 */ "node-stores-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" -/* offset=117223 */ "node-stores-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" -/* offset=117315 */ "node-write\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117401 */ "node-write-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117492 */ "node-write-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117588 */ "node-write-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117678 */ "node-write-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" -/* offset=117771 */ "node-write-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" -/* offset=117864 */ "node-write-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" -/* offset=117955 */ "node-prefetch\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118047 */ "node-prefetch-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118144 */ "node-prefetch-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118246 */ "node-prefetch-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118342 */ "node-prefetch-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118441 */ "node-prefetch-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00000\000\000\000\000\000" -/* offset=118540 */ "node-prefetch-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=118637 */ "node-prefetches\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00000\000\000\000\000\000" -/* offset=118731 */ "node-prefetches-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118830 */ "node-prefetches-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=118934 */ "node-prefetches-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119032 */ "node-prefetches-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119133 */ "node-prefetches-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=119234 */ "node-prefetches-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=119333 */ "node-speculative-read\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119433 */ "node-speculative-read-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119538 */ "node-speculative-read-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119648 */ "node-speculative-read-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119752 */ "node-speculative-read-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=119859 */ "node-speculative-read-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=119966 */ "node-speculative-read-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=120071 */ "node-speculative-load\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=120171 */ "node-speculative-load-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=120276 */ "node-speculative-load-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=120386 */ "node-speculative-load-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=120490 */ "node-speculative-load-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" -/* offset=120597 */ "node-speculative-load-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=120704 */ "node-speculative-load-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" -/* offset=120809 */ "node-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=120889 */ "node-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=120974 */ "node-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=121053 */ "node-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" -/* offset=121135 */ "node-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=121221 */ "node-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" -/* offset=121305 */ "cpu-cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000" -/* offset=121467 */ "cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cpu-cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000" -/* offset=121629 */ "instructions\000legacy hardware\000Retired instructions. Be careful, these can be affected by various issues, most notably hardware interrupt counts\000legacy-hardware-config=1\000\00000\000\000\000\000\000" -/* offset=121805 */ "cache-references\000legacy hardware\000Cache accesses. Usually this indicates Last Level Cache accesses but this may vary depending on your CPU. This may include prefetches and coherency messages; again this depends on the design of your CPU\000legacy-hardware-config=2\000\00000\000\000\000\000\000" -/* offset=122075 */ "cache-misses\000legacy hardware\000Cache misses. Usually this indicates Last Level Cache misses; this is intended to be used in conjunction with the PERF_COUNT_HW_CACHE_REFERENCES event to calculate cache miss rates\000legacy-hardware-config=3\000\00000\000\000\000\000\000" -/* offset=122318 */ "branches\000legacy hardware\000Retired branch instructions [This event is an alias of branch-instructions]\000legacy-hardware-config=4\000\00000\000\000\000\000\000" -/* offset=122452 */ "branch-instructions\000legacy hardware\000Retired branch instructions [This event is an alias of branches]\000legacy-hardware-config=4\000\00000\000\000\000\000\000" -/* offset=122586 */ "branch-misses\000legacy hardware\000Mispredicted branch instructions\000legacy-hardware-config=5\000\00000\000\000\000\000\000" -/* offset=122682 */ "bus-cycles\000legacy hardware\000Bus cycles, which can be different from total cycles\000legacy-hardware-config=6\000\00000\000\000\000\000\000" -/* offset=122795 */ "stalled-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of idle-cycles-frontend]\000legacy-hardware-config=7\000\00000\000\000\000\000\000" -/* offset=122945 */ "idle-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of stalled-cycles-fronted]\000legacy-hardware-config=7\000\00000\000\000\000\000\000" -/* offset=123094 */ "stalled-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of idle-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000" -/* offset=123247 */ "idle-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of stalled-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000" -/* offset=123400 */ "ref-cycles\000legacy hardware\000Total cycles; not affected by CPU frequency scaling\000legacy-hardware-config=9\000\00000\000\000\000\000\000" -/* offset=123512 */ "software\000" -/* offset=123521 */ "cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\000\000001e-6msec\000\000\000\000\000" -/* offset=123607 */ "task-clock\000software\000Per-task high-resolution timer based event\000config=1\000\000001e-6msec\000\000\000\000\000" -/* offset=123695 */ "faults\000software\000Number of page faults [This event is an alias of page-faults]\000config=2\000\00000\000\000\000\000\000" -/* offset=123790 */ "page-faults\000software\000Number of page faults [This event is an alias of faults]\000config=2\000\00000\000\000\000\000\000" -/* offset=123885 */ "context-switches\000software\000Number of context switches [This event is an alias of cs]\000config=3\000\00000\000\000\000\000\000" -/* offset=123986 */ "cs\000software\000Number of context switches [This event is an alias of context-switches]\000config=3\000\00000\000\000\000\000\000" -/* offset=124087 */ "cpu-migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of migrations]\000config=4\000\00000\000\000\000\000\000" -/* offset=124219 */ "migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]\000config=4\000\00000\000\000\000\000\000" -/* offset=124351 */ "minor-faults\000software\000Number of minor page faults. Minor faults don't require I/O to handle\000config=5\000\00000\000\000\000\000\000" -/* offset=124460 */ "major-faults\000software\000Number of major page faults. Major faults require I/O to handle\000config=6\000\00000\000\000\000\000\000" -/* offset=124563 */ "alignment-faults\000software\000Number of kernel handled memory alignment faults\000config=7\000\00000\000\000\000\000\000" -/* offset=124655 */ "emulation-faults\000software\000Number of kernel handled unimplemented instruction faults handled through emulation\000config=8\000\00000\000\000\000\000\000" -/* offset=124782 */ "dummy\000software\000A placeholder event that doesn't count anything\000config=9\000\00000\000\000\000\000\000" -/* offset=124862 */ "bpf-output\000software\000An event used by BPF programs to write to the perf ring buffer\000config=0xa\000\00000\000\000\000\000\000" -/* offset=124964 */ "cgroup-switches\000software\000Number of context switches to a task in a different cgroup\000config=0xb\000\00000\000\000\000\000\000" -/* offset=125067 */ "tool\000" -/* offset=125072 */ "duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000" -/* offset=125148 */ "user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000" -/* offset=125218 */ "system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000" -/* offset=125286 */ "has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000" -/* offset=125362 */ "num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000" -/* offset=125507 */ "num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000" -/* offset=125610 */ "num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000" -/* offset=125727 */ "num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000" -/* offset=125803 */ "num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000" -/* offset=125889 */ "slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000" -/* offset=125999 */ "smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000" -/* offset=126106 */ "system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000" -/* offset=126205 */ "core_wide\000tool\0001 if not SMT, if SMT are events being gathered on all SMT threads 1 otherwise 0\000config=0xd\000\00000\000\000\000\000\000" -/* offset=126319 */ "target_cpu\000tool\0001 if CPUs being analyzed, 0 if threads/processes\000config=0xe\000\00000\000\000\000\000\000" -/* offset=126403 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000" -/* offset=126465 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000" -/* offset=126527 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000" -/* offset=126625 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000" -/* offset=126727 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000" -/* offset=126860 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000" -/* offset=126978 */ "hisi_sccl,ddrc\000" -/* offset=126993 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000\000" -/* offset=127063 */ "uncore_cbox\000" -/* offset=127075 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000\000" -/* offset=127229 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000\000" -/* offset=127283 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000\000" -/* offset=127341 */ "hisi_sccl,l3c\000" -/* offset=127355 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000\000" -/* offset=127423 */ "uncore_imc_free_running\000" -/* offset=127447 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000\000" -/* offset=127527 */ "uncore_imc\000" -/* offset=127538 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000\000" -/* offset=127603 */ "uncore_sys_ddr_pmu\000" -/* offset=127622 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000" -/* offset=127698 */ "uncore_sys_ccn_pmu\000" -/* offset=127717 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000" -/* offset=127794 */ "uncore_sys_cmn_pmu\000" -/* offset=127813 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000" -/* offset=127956 */ "CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@) / (duration_time * 1e9)\000\000Average CPU utilization\000\0001CPUs\000\000\000\000011" -/* offset=128142 */ "cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switches@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Context switches per CPU second\000\0001cs/sec\000\000\000\000011" -/* offset=128375 */ "migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011" -/* offset=128635 */ "page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011" -/* offset=128866 */ "insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001" -/* offset=128979 */ "stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001" -/* offset=129404 */ "frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001" -/* offset=129583 */ "backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001" -/* offset=129757 */ "cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011" -/* offset=129933 */ "branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011" -/* offset=130113 */ "branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001" -/* offset=130217 */ "l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001" -/* offset=130333 */ "llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001" -/* offset=130434 */ "l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001" -/* offset=130549 */ "dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001" -/* offset=130655 */ "itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001" -/* offset=130761 */ "l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001" -/* offset=130909 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000" -/* offset=130932 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000" -/* offset=130996 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000" -/* offset=131163 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" -/* offset=131228 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" -/* offset=131296 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000" -/* offset=131368 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000" -/* offset=131463 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000" -/* offset=131598 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000" -/* offset=131663 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000" -/* offset=131732 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000" -/* offset=131803 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\000000" -/* offset=131826 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\000000" -/* offset=131849 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\000000" -/* offset=131870 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000" +/* offset=0 */ +"default_core\000" +/* offset=13 */ +"l1-dcache\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=99 */ +"l1-dcache-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=190 */ +"l1-dcache-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=286 */ +"l1-dcache-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=387 */ +"l1-dcache-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=482 */ +"l1-dcache-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=580 */ +"l1-dcache-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00000\000\000\000\000\000" +/* offset=682 */ +"l1-dcache-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=782 */ +"l1-dcache-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00000\000\000\000\000\000" +/* offset=874 */ +"l1-dcache-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=971 */ +"l1-dcache-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1073 */ +"l1-dcache-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1169 */ +"l1-dcache-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1268 */ +"l1-dcache-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=1371 */ +"l1-dcache-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=1472 */ +"l1-dcache-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1563 */ +"l1-dcache-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1659 */ +"l1-dcache-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1760 */ +"l1-dcache-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1855 */ +"l1-dcache-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=1953 */ +"l1-dcache-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=2055 */ +"l1-dcache-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=2155 */ +"l1-dcache-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=2252 */ +"l1-dcache-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=2354 */ +"l1-dcache-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=2461 */ +"l1-dcache-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=2562 */ +"l1-dcache-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=2666 */ +"l1-dcache-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00000\000\000\000\000\000" +/* offset=2770 */ +"l1-dcache-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=2872 */ +"l1-dcache-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00000\000\000\000\000\000" +/* offset=2970 */ +"l1-dcache-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3073 */ +"l1-dcache-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3181 */ +"l1-dcache-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3283 */ +"l1-dcache-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3388 */ +"l1-dcache-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=3493 */ +"l1-dcache-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=3596 */ +"l1-dcache-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3693 */ +"l1-dcache-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3795 */ +"l1-dcache-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=3902 */ +"l1-dcache-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=4003 */ +"l1-dcache-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=4107 */ +"l1-dcache-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=4211 */ +"l1-dcache-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=4313 */ +"l1-dcache-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=4416 */ +"l1-dcache-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=4524 */ +"l1-dcache-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=4637 */ +"l1-dcache-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=4744 */ +"l1-dcache-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=4854 */ +"l1-dcache-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00000\000\000\000\000\000" +/* offset=4964 */ +"l1-dcache-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=5072 */ +"l1-dcache-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00000\000\000\000\000\000" +/* offset=5177 */ +"l1-dcache-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=5287 */ +"l1-dcache-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=5402 */ +"l1-dcache-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=5511 */ +"l1-dcache-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=5623 */ +"l1-dcache-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=5735 */ +"l1-dcache-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=5845 */ +"l1-dcache-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=5956 */ +"l1-dcache-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6072 */ +"l1-dcache-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6193 */ +"l1-dcache-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6308 */ +"l1-dcache-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6426 */ +"l1-dcache-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=6544 */ +"l1-dcache-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=6660 */ +"l1-dcache-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6771 */ +"l1-dcache-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=6887 */ +"l1-dcache-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=7008 */ +"l1-dcache-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=7123 */ +"l1-dcache-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=7241 */ +"l1-dcache-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=7359 */ +"l1-dcache-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=7475 */ +"l1-dcache-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=7566 */ +"l1-dcache-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=7662 */ +"l1-dcache-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=7752 */ +"l1-dcache-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=7845 */ +"l1-dcache-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=7942 */ +"l1-dcache-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=8037 */ +"l1-d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8118 */ +"l1-d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8204 */ +"l1-d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8295 */ +"l1-d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8391 */ +"l1-d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8481 */ +"l1-d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8574 */ +"l1-d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=8671 */ +"l1-d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=8766 */ +"l1-d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8853 */ +"l1-d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=8945 */ +"l1-d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9042 */ +"l1-d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9133 */ +"l1-d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9227 */ +"l1-d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=9325 */ +"l1-d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=9421 */ +"l1-d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9507 */ +"l1-d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9598 */ +"l1-d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9694 */ +"l1-d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9784 */ +"l1-d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=9877 */ +"l1-d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=9974 */ +"l1-d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=10069 */ +"l1-d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10161 */ +"l1-d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10258 */ +"l1-d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10360 */ +"l1-d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10456 */ +"l1-d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10555 */ +"l1-d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=10654 */ +"l1-d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=10751 */ +"l1-d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10844 */ +"l1-d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=10942 */ +"l1-d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11045 */ +"l1-d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11142 */ +"l1-d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11242 */ +"l1-d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=11342 */ +"l1-d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=11440 */ +"l1-d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11532 */ +"l1-d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11629 */ +"l1-d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11731 */ +"l1-d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11827 */ +"l1-d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=11926 */ +"l1-d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=12025 */ +"l1-d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=12122 */ +"l1-d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12220 */ +"l1-d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12323 */ +"l1-d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12431 */ +"l1-d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12533 */ +"l1-d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12638 */ +"l1-d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=12743 */ +"l1-d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=12846 */ +"l1-d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=12946 */ +"l1-d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13051 */ +"l1-d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13161 */ +"l1-d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13265 */ +"l1-d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13372 */ +"l1-d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=13479 */ +"l1-d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=13584 */ +"l1-d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13690 */ +"l1-d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13801 */ +"l1-d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=13917 */ +"l1-d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14027 */ +"l1-d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14140 */ +"l1-d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=14253 */ +"l1-d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=14364 */ +"l1-d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14470 */ +"l1-d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14581 */ +"l1-d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14697 */ +"l1-d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14807 */ +"l1-d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=14920 */ +"l1-d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=15033 */ +"l1-d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=15144 */ +"l1-d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15230 */ +"l1-d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15321 */ +"l1-d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15406 */ +"l1-d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15494 */ +"l1-d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=15586 */ +"l1-d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=15676 */ +"l1d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15756 */ +"l1d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15841 */ +"l1d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=15931 */ +"l1d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16026 */ +"l1d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16115 */ +"l1d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16207 */ +"l1d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=16303 */ +"l1d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=16397 */ +"l1d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16483 */ +"l1d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16574 */ +"l1d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16670 */ +"l1d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16760 */ +"l1d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=16853 */ +"l1d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=16950 */ +"l1d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=17045 */ +"l1d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=17130 */ +"l1d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=17220 */ +"l1d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=17315 */ +"l1d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=17404 */ +"l1d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=17496 */ +"l1d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=17592 */ +"l1d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=17686 */ +"l1d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=17777 */ +"l1d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=17873 */ +"l1d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=17974 */ +"l1d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18069 */ +"l1d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18167 */ +"l1d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=18265 */ +"l1d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=18361 */ +"l1d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18453 */ +"l1d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18550 */ +"l1d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18652 */ +"l1d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18748 */ +"l1d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=18847 */ +"l1d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=18946 */ +"l1d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=19043 */ +"l1d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=19134 */ +"l1d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=19230 */ +"l1d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=19331 */ +"l1d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=19426 */ +"l1d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=19524 */ +"l1d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=19622 */ +"l1d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=19718 */ +"l1d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=19815 */ +"l1d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=19917 */ +"l1d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20024 */ +"l1d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20125 */ +"l1d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20229 */ +"l1d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=20333 */ +"l1d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=20435 */ +"l1d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20534 */ +"l1d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20638 */ +"l1d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20747 */ +"l1d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20850 */ +"l1d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=20956 */ +"l1d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=21062 */ +"l1d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=21166 */ +"l1d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=21271 */ +"l1d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=21381 */ +"l1d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=21496 */ +"l1d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=21605 */ +"l1d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=21717 */ +"l1d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=21829 */ +"l1d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=21939 */ +"l1d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=22044 */ +"l1d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=22154 */ +"l1d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=22269 */ +"l1d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=22378 */ +"l1d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=22490 */ +"l1d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=22602 */ +"l1d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=22712 */ +"l1d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=22797 */ +"l1d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=22887 */ +"l1d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=22971 */ +"l1d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23058 */ +"l1d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=23149 */ +"l1d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=23238 */ +"l1-data\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23322 */ +"l1-data-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23411 */ +"l1-data-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23505 */ +"l1-data-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23604 */ +"l1-data-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23697 */ +"l1-data-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=23793 */ +"l1-data-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=23893 */ +"l1-data-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=23991 */ +"l1-data-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24081 */ +"l1-data-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24176 */ +"l1-data-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24276 */ +"l1-data-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24370 */ +"l1-data-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24467 */ +"l1-data-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=24568 */ +"l1-data-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=24667 */ +"l1-data-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24756 */ +"l1-data-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24850 */ +"l1-data-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=24949 */ +"l1-data-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=25042 */ +"l1-data-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=25138 */ +"l1-data-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=25238 */ +"l1-data-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=25336 */ +"l1-data-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=25431 */ +"l1-data-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=25531 */ +"l1-data-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=25636 */ +"l1-data-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=25735 */ +"l1-data-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=25837 */ +"l1-data-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=25939 */ +"l1-data-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=26039 */ +"l1-data-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26135 */ +"l1-data-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26236 */ +"l1-data-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26342 */ +"l1-data-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26442 */ +"l1-data-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26545 */ +"l1-data-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=26648 */ +"l1-data-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=26749 */ +"l1-data-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26844 */ +"l1-data-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=26944 */ +"l1-data-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=27049 */ +"l1-data-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=27148 */ +"l1-data-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000" +/* offset=27250 */ +"l1-data-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=27352 */ +"l1-data-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000" +/* offset=27452 */ +"l1-data-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=27553 */ +"l1-data-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=27659 */ +"l1-data-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=27770 */ +"l1-data-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=27875 */ +"l1-data-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=27983 */ +"l1-data-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=28091 */ +"l1-data-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=28197 */ +"l1-data-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=28300 */ +"l1-data-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=28408 */ +"l1-data-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=28521 */ +"l1-data-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=28628 */ +"l1-data-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=28738 */ +"l1-data-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=28848 */ +"l1-data-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=28956 */ +"l1-data-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29065 */ +"l1-data-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29179 */ +"l1-data-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29298 */ +"l1-data-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29411 */ +"l1-data-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29527 */ +"l1-data-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=29643 */ +"l1-data-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=29757 */ +"l1-data-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29866 */ +"l1-data-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=29980 */ +"l1-data-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=30099 */ +"l1-data-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=30212 */ +"l1-data-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000" +/* offset=30328 */ +"l1-data-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=30444 */ +"l1-data-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000" +/* offset=30558 */ +"l1-data-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=30647 */ +"l1-data-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=30741 */ +"l1-data-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=30829 */ +"l1-data-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000" +/* offset=30920 */ +"l1-data-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=31015 */ +"l1-data-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000" +/* offset=31108 */ +"l1-icache\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31201 */ +"l1-icache-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31299 */ +"l1-icache-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31402 */ +"l1-icache-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31510 */ +"l1-icache-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31612 */ +"l1-icache-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=31717 */ +"l1-icache-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00000\000\000\000\000\000" +/* offset=31826 */ +"l1-icache-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=31933 */ +"l1-icache-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00000\000\000\000\000\000" +/* offset=32032 */ +"l1-icache-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32136 */ +"l1-icache-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32245 */ +"l1-icache-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32348 */ +"l1-icache-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32454 */ +"l1-icache-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=32564 */ +"l1-icache-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=32672 */ +"l1-icache-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32770 */ +"l1-icache-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32873 */ +"l1-icache-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=32981 */ +"l1-icache-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=33083 */ +"l1-icache-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=33188 */ +"l1-icache-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=33297 */ +"l1-icache-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=33404 */ +"l1-icache-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=33514 */ +"l1-icache-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=33629 */ +"l1-icache-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=33749 */ +"l1-icache-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=33863 */ +"l1-icache-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=33980 */ +"l1-icache-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00000\000\000\000\000\000" +/* offset=34097 */ +"l1-icache-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=34212 */ +"l1-icache-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00000\000\000\000\000\000" +/* offset=34324 */ +"l1-icache-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=34441 */ +"l1-icache-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=34563 */ +"l1-icache-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=34679 */ +"l1-icache-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=34798 */ +"l1-icache-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=34917 */ +"l1-icache-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=35034 */ +"l1-icache-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=35152 */ +"l1-icache-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=35275 */ +"l1-icache-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=35403 */ +"l1-icache-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=35525 */ +"l1-icache-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=35650 */ +"l1-icache-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=35775 */ +"l1-icache-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=35898 */ +"l1-icache-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=36016 */ +"l1-icache-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=36139 */ +"l1-icache-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=36267 */ +"l1-icache-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=36389 */ +"l1-icache-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=36514 */ +"l1-icache-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=36639 */ +"l1-icache-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=36762 */ +"l1-icache-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=36860 */ +"l1-icache-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=36963 */ +"l1-icache-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37060 */ +"l1-icache-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37160 */ +"l1-icache-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=37264 */ +"l1-icache-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=37366 */ +"l1-i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37454 */ +"l1-i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37547 */ +"l1-i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37645 */ +"l1-i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37748 */ +"l1-i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37845 */ +"l1-i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=37945 */ +"l1-i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=38049 */ +"l1-i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=38151 */ +"l1-i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38245 */ +"l1-i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38344 */ +"l1-i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38448 */ +"l1-i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38546 */ +"l1-i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38647 */ +"l1-i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=38752 */ +"l1-i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=38855 */ +"l1-i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=38948 */ +"l1-i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=39046 */ +"l1-i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=39149 */ +"l1-i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=39246 */ +"l1-i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=39346 */ +"l1-i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=39450 */ +"l1-i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=39552 */ +"l1-i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=39657 */ +"l1-i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=39767 */ +"l1-i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=39882 */ +"l1-i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=39991 */ +"l1-i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40103 */ +"l1-i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=40215 */ +"l1-i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=40325 */ +"l1-i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40432 */ +"l1-i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40544 */ +"l1-i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40661 */ +"l1-i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40772 */ +"l1-i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=40886 */ +"l1-i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=41000 */ +"l1-i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=41112 */ +"l1-i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=41225 */ +"l1-i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=41343 */ +"l1-i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=41466 */ +"l1-i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=41583 */ +"l1-i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=41703 */ +"l1-i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=41823 */ +"l1-i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=41941 */ +"l1-i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=42054 */ +"l1-i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=42172 */ +"l1-i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=42295 */ +"l1-i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=42412 */ +"l1-i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=42532 */ +"l1-i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=42652 */ +"l1-i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=42770 */ +"l1-i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=42863 */ +"l1-i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=42961 */ +"l1-i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43053 */ +"l1-i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43148 */ +"l1-i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=43247 */ +"l1-i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=43344 */ +"l1i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43431 */ +"l1i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43523 */ +"l1i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43620 */ +"l1i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43722 */ +"l1i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43818 */ +"l1i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=43917 */ +"l1i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=44020 */ +"l1i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=44121 */ +"l1i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44214 */ +"l1i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44312 */ +"l1i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44415 */ +"l1i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44512 */ +"l1i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44612 */ +"l1i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=44716 */ +"l1i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=44818 */ +"l1i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=44910 */ +"l1i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=45007 */ +"l1i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=45109 */ +"l1i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=45205 */ +"l1i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=45304 */ +"l1i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=45407 */ +"l1i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=45508 */ +"l1i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=45612 */ +"l1i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=45721 */ +"l1i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=45835 */ +"l1i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=45943 */ +"l1i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46054 */ +"l1i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=46165 */ +"l1i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=46274 */ +"l1i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46380 */ +"l1i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46491 */ +"l1i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46607 */ +"l1i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46717 */ +"l1i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=46830 */ +"l1i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=46943 */ +"l1i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=47054 */ +"l1i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47166 */ +"l1i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47283 */ +"l1i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47405 */ +"l1i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47521 */ +"l1i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47640 */ +"l1i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=47759 */ +"l1i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=47876 */ +"l1i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=47988 */ +"l1i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=48105 */ +"l1i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=48227 */ +"l1i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=48343 */ +"l1i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=48462 */ +"l1i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=48581 */ +"l1i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=48698 */ +"l1i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=48790 */ +"l1i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=48887 */ +"l1i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=48978 */ +"l1i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49072 */ +"l1i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=49170 */ +"l1i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=49266 */ +"l1-instruction\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49364 */ +"l1-instruction-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49467 */ +"l1-instruction-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49575 */ +"l1-instruction-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49688 */ +"l1-instruction-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49795 */ +"l1-instruction-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=49905 */ +"l1-instruction-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=50019 */ +"l1-instruction-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=50131 */ +"l1-instruction-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=50235 */ +"l1-instruction-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=50344 */ +"l1-instruction-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=50458 */ +"l1-instruction-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=50566 */ +"l1-instruction-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=50677 */ +"l1-instruction-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=50792 */ +"l1-instruction-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=50905 */ +"l1-instruction-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=51008 */ +"l1-instruction-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=51116 */ +"l1-instruction-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=51229 */ +"l1-instruction-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=51336 */ +"l1-instruction-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=51446 */ +"l1-instruction-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=51560 */ +"l1-instruction-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=51672 */ +"l1-instruction-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=51787 */ +"l1-instruction-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=51907 */ +"l1-instruction-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52032 */ +"l1-instruction-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52151 */ +"l1-instruction-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52273 */ +"l1-instruction-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=52395 */ +"l1-instruction-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=52515 */ +"l1-instruction-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52632 */ +"l1-instruction-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52754 */ +"l1-instruction-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=52881 */ +"l1-instruction-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53002 */ +"l1-instruction-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53126 */ +"l1-instruction-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=53250 */ +"l1-instruction-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=53372 */ +"l1-instruction-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53495 */ +"l1-instruction-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53623 */ +"l1-instruction-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53756 */ +"l1-instruction-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=53883 */ +"l1-instruction-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54013 */ +"l1-instruction-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=54143 */ +"l1-instruction-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=54271 */ +"l1-instruction-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54394 */ +"l1-instruction-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54522 */ +"l1-instruction-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54655 */ +"l1-instruction-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54782 */ +"l1-instruction-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000" +/* offset=54912 */ +"l1-instruction-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=55042 */ +"l1-instruction-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000" +/* offset=55170 */ +"l1-instruction-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=55273 */ +"l1-instruction-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=55381 */ +"l1-instruction-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=55483 */ +"l1-instruction-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000" +/* offset=55588 */ +"l1-instruction-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=55697 */ +"l1-instruction-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000" +/* offset=55804 */ +"llc\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=55882 */ +"llc-load\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=55965 */ +"llc-load-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56053 */ +"llc-load-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56146 */ +"llc-load-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56233 */ +"llc-load-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56323 */ +"llc-load-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00000\000\000\000\000\000" +/* offset=56417 */ +"llc-load-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=56509 */ +"llc-loads\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00000\000\000\000\000\000" +/* offset=56593 */ +"llc-loads-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56682 */ +"llc-loads-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56776 */ +"llc-loads-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56864 */ +"llc-loads-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=56955 */ +"llc-loads-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=57050 */ +"llc-loads-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=57143 */ +"llc-read\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=57226 */ +"llc-read-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=57314 */ +"llc-read-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=57407 */ +"llc-read-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=57494 */ +"llc-read-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=57584 */ +"llc-read-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=57678 */ +"llc-read-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=57770 */ +"llc-store\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=57859 */ +"llc-store-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=57953 */ +"llc-store-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58052 */ +"llc-store-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58145 */ +"llc-store-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58241 */ +"llc-store-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00000\000\000\000\000\000" +/* offset=58337 */ +"llc-store-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=58431 */ +"llc-stores\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00000\000\000\000\000\000" +/* offset=58521 */ +"llc-stores-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58616 */ +"llc-stores-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58716 */ +"llc-stores-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58810 */ +"llc-stores-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=58907 */ +"llc-stores-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=59004 */ +"llc-stores-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=59099 */ +"llc-write\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=59188 */ +"llc-write-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=59282 */ +"llc-write-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=59381 */ +"llc-write-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=59474 */ +"llc-write-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=59570 */ +"llc-write-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=59666 */ +"llc-write-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=59760 */ +"llc-prefetch\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=59855 */ +"llc-prefetch-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=59955 */ +"llc-prefetch-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60060 */ +"llc-prefetch-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60159 */ +"llc-prefetch-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60261 */ +"llc-prefetch-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00000\000\000\000\000\000" +/* offset=60363 */ +"llc-prefetch-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=60463 */ +"llc-prefetches\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00000\000\000\000\000\000" +/* offset=60560 */ +"llc-prefetches-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60662 */ +"llc-prefetches-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60769 */ +"llc-prefetches-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60870 */ +"llc-prefetches-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=60974 */ +"llc-prefetches-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=61078 */ +"llc-prefetches-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=61180 */ +"llc-speculative-read\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=61283 */ +"llc-speculative-read-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=61391 */ +"llc-speculative-read-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=61504 */ +"llc-speculative-read-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=61611 */ +"llc-speculative-read-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=61721 */ +"llc-speculative-read-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=61831 */ +"llc-speculative-read-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=61939 */ +"llc-speculative-load\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=62042 */ +"llc-speculative-load-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=62150 */ +"llc-speculative-load-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=62263 */ +"llc-speculative-load-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=62370 */ +"llc-speculative-load-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=62480 */ +"llc-speculative-load-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=62590 */ +"llc-speculative-load-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=62698 */ +"llc-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=62781 */ +"llc-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=62869 */ +"llc-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=62951 */ +"llc-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63036 */ +"llc-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=63125 */ +"llc-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=63212 */ +"l2\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63309 */ +"l2-load\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63411 */ +"l2-load-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63518 */ +"l2-load-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63630 */ +"l2-load-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63736 */ +"l2-load-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=63845 */ +"l2-load-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=63958 */ +"l2-load-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=64069 */ +"l2-loads\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64172 */ +"l2-loads-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64280 */ +"l2-loads-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64393 */ +"l2-loads-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64500 */ +"l2-loads-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64610 */ +"l2-loads-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=64724 */ +"l2-loads-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=64836 */ +"l2-read\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=64938 */ +"l2-read-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=65045 */ +"l2-read-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=65157 */ +"l2-read-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=65263 */ +"l2-read-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=65372 */ +"l2-read-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=65485 */ +"l2-read-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=65596 */ +"l2-store\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=65704 */ +"l2-store-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=65817 */ +"l2-store-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=65935 */ +"l2-store-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66047 */ +"l2-store-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66162 */ +"l2-store-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=66277 */ +"l2-store-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=66390 */ +"l2-stores\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66499 */ +"l2-stores-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66613 */ +"l2-stores-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66732 */ +"l2-stores-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66845 */ +"l2-stores-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=66961 */ +"l2-stores-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=67077 */ +"l2-stores-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=67191 */ +"l2-write\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=67299 */ +"l2-write-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=67412 */ +"l2-write-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=67530 */ +"l2-write-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=67642 */ +"l2-write-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000" +/* offset=67757 */ +"l2-write-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=67872 */ +"l2-write-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000" +/* offset=67985 */ +"l2-prefetch\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68099 */ +"l2-prefetch-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68218 */ +"l2-prefetch-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68342 */ +"l2-prefetch-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68460 */ +"l2-prefetch-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68581 */ +"l2-prefetch-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=68702 */ +"l2-prefetch-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=68821 */ +"l2-prefetches\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=68937 */ +"l2-prefetches-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69058 */ +"l2-prefetches-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69184 */ +"l2-prefetches-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69304 */ +"l2-prefetches-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69427 */ +"l2-prefetches-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=69550 */ +"l2-prefetches-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=69671 */ +"l2-speculative-read\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69793 */ +"l2-speculative-read-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=69920 */ +"l2-speculative-read-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70052 */ +"l2-speculative-read-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70178 */ +"l2-speculative-read-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70307 */ +"l2-speculative-read-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=70436 */ +"l2-speculative-read-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=70563 */ +"l2-speculative-load\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70685 */ +"l2-speculative-load-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70812 */ +"l2-speculative-load-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=70944 */ +"l2-speculative-load-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=71070 */ +"l2-speculative-load-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000" +/* offset=71199 */ +"l2-speculative-load-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=71328 */ +"l2-speculative-load-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000" +/* offset=71455 */ +"l2-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=71557 */ +"l2-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=71664 */ +"l2-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=71765 */ +"l2-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000" +/* offset=71869 */ +"l2-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=71977 */ +"l2-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000" +/* offset=72083 */ +"dtlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72154 */ +"dtlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72230 */ +"dtlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72311 */ +"dtlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72397 */ +"dtlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72477 */ +"dtlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72560 */ +"dtlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00000\000\000\000\000\000" +/* offset=72647 */ +"dtlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=72732 */ +"dtlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00000\000\000\000\000\000" +/* offset=72809 */ +"dtlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72891 */ +"dtlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=72978 */ +"dtlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73059 */ +"dtlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73143 */ +"dtlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=73231 */ +"dtlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=73317 */ +"dtlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73393 */ +"dtlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73474 */ +"dtlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73560 */ +"dtlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73640 */ +"dtlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=73723 */ +"dtlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=73810 */ +"dtlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=73895 */ +"dtlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=73977 */ +"dtlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74064 */ +"dtlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74156 */ +"dtlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74242 */ +"dtlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74331 */ +"dtlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00000\000\000\000\000\000" +/* offset=74420 */ +"dtlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=74507 */ +"dtlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00000\000\000\000\000\000" +/* offset=74590 */ +"dtlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74678 */ +"dtlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74771 */ +"dtlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74858 */ +"dtlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=74948 */ +"dtlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=75038 */ +"dtlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=75126 */ +"dtlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=75208 */ +"dtlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=75295 */ +"dtlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=75387 */ +"dtlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=75473 */ +"dtlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=75562 */ +"dtlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=75651 */ +"dtlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=75738 */ +"dtlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=75826 */ +"dtlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=75919 */ +"dtlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76017 */ +"dtlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76109 */ +"dtlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76204 */ +"dtlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00000\000\000\000\000\000" +/* offset=76299 */ +"dtlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=76392 */ +"dtlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00000\000\000\000\000\000" +/* offset=76482 */ +"dtlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76577 */ +"dtlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76677 */ +"dtlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76771 */ +"dtlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=76868 */ +"dtlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=76965 */ +"dtlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=77060 */ +"dtlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77156 */ +"dtlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77257 */ +"dtlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77363 */ +"dtlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77463 */ +"dtlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77566 */ +"dtlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=77669 */ +"dtlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=77770 */ +"dtlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77866 */ +"dtlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=77967 */ +"dtlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=78073 */ +"dtlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=78173 */ +"dtlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=78276 */ +"dtlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=78379 */ +"dtlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=78480 */ +"dtlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=78556 */ +"dtlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=78637 */ +"dtlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=78712 */ +"dtlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=78790 */ +"dtlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=78872 */ +"dtlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=78952 */ +"d-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79024 */ +"d-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79101 */ +"d-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79183 */ +"d-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79270 */ +"d-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79351 */ +"d-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79435 */ +"d-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=79523 */ +"d-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=79609 */ +"d-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79687 */ +"d-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79770 */ +"d-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79858 */ +"d-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=79940 */ +"d-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80025 */ +"d-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=80114 */ +"d-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=80201 */ +"d-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80278 */ +"d-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80360 */ +"d-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80447 */ +"d-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80528 */ +"d-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=80612 */ +"d-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=80700 */ +"d-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=80786 */ +"d-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=80869 */ +"d-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=80957 */ +"d-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81050 */ +"d-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81137 */ +"d-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81227 */ +"d-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=81317 */ +"d-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=81405 */ +"d-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81489 */ +"d-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81578 */ +"d-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81672 */ +"d-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81760 */ +"d-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=81851 */ +"d-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=81942 */ +"d-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=82031 */ +"d-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=82114 */ +"d-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=82202 */ +"d-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=82295 */ +"d-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=82382 */ +"d-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=82472 */ +"d-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=82562 */ +"d-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=82650 */ +"d-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=82739 */ +"d-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=82833 */ +"d-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=82932 */ +"d-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83025 */ +"d-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83121 */ +"d-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=83217 */ +"d-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=83311 */ +"d-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83402 */ +"d-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83498 */ +"d-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83599 */ +"d-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83694 */ +"d-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=83792 */ +"d-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=83890 */ +"d-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=83986 */ +"d-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84083 */ +"d-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84185 */ +"d-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84292 */ +"d-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84393 */ +"d-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84497 */ +"d-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=84601 */ +"d-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=84703 */ +"d-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84800 */ +"d-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=84902 */ +"d-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=85009 */ +"d-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=85110 */ +"d-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=85214 */ +"d-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=85318 */ +"d-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=85420 */ +"d-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=85497 */ +"d-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=85579 */ +"d-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=85655 */ +"d-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=85734 */ +"d-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=85817 */ +"d-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=85898 */ +"data-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=85973 */ +"data-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86053 */ +"data-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86138 */ +"data-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86228 */ +"data-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86312 */ +"data-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86399 */ +"data-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=86490 */ +"data-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=86579 */ +"data-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86660 */ +"data-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86746 */ +"data-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86837 */ +"data-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=86922 */ +"data-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87010 */ +"data-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=87102 */ +"data-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=87192 */ +"data-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87272 */ +"data-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87357 */ +"data-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87447 */ +"data-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87531 */ +"data-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=87618 */ +"data-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=87709 */ +"data-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=87798 */ +"data-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=87884 */ +"data-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=87975 */ +"data-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88071 */ +"data-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88161 */ +"data-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88254 */ +"data-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=88347 */ +"data-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=88438 */ +"data-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88525 */ +"data-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88617 */ +"data-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88714 */ +"data-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88805 */ +"data-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=88899 */ +"data-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=88993 */ +"data-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=89085 */ +"data-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=89171 */ +"data-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=89262 */ +"data-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=89358 */ +"data-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=89448 */ +"data-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000" +/* offset=89541 */ +"data-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=89634 */ +"data-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000" +/* offset=89725 */ +"data-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=89817 */ +"data-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=89914 */ +"data-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90016 */ +"data-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90112 */ +"data-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90211 */ +"data-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=90310 */ +"data-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=90407 */ +"data-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90501 */ +"data-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90600 */ +"data-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90704 */ +"data-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90802 */ +"data-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=90903 */ +"data-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=91004 */ +"data-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=91103 */ +"data-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91203 */ +"data-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91308 */ +"data-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91418 */ +"data-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91522 */ +"data-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91629 */ +"data-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=91736 */ +"data-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=91841 */ +"data-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=91941 */ +"data-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=92046 */ +"data-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=92156 */ +"data-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=92260 */ +"data-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000" +/* offset=92367 */ +"data-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=92474 */ +"data-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000" +/* offset=92579 */ +"data-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=92659 */ +"data-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=92744 */ +"data-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=92823 */ +"data-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000" +/* offset=92905 */ +"data-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=92991 */ +"data-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000" +/* offset=93075 */ +"itlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93153 */ +"itlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93236 */ +"itlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93324 */ +"itlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93417 */ +"itlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93504 */ +"itlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93594 */ +"itlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00000\000\000\000\000\000" +/* offset=93688 */ +"itlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=93780 */ +"itlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00000\000\000\000\000\000" +/* offset=93864 */ +"itlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=93953 */ +"itlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94047 */ +"itlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94135 */ +"itlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94226 */ +"itlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=94321 */ +"itlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=94414 */ +"itlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94497 */ +"itlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94585 */ +"itlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94678 */ +"itlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94765 */ +"itlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=94855 */ +"itlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=94949 */ +"itlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=95041 */ +"itlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95124 */ +"itlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95212 */ +"itlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95294 */ +"itlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95379 */ +"itlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=95468 */ +"itlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=95555 */ +"i-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95634 */ +"i-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95718 */ +"i-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95807 */ +"i-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95901 */ +"i-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=95989 */ +"i-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96080 */ +"i-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=96175 */ +"i-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=96268 */ +"i-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96353 */ +"i-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96443 */ +"i-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96538 */ +"i-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96627 */ +"i-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96719 */ +"i-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=96815 */ +"i-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=96909 */ +"i-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=96993 */ +"i-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97082 */ +"i-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97176 */ +"i-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97264 */ +"i-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97355 */ +"i-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=97450 */ +"i-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=97543 */ +"i-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97627 */ +"i-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97716 */ +"i-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97799 */ +"i-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=97885 */ +"i-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=97975 */ +"i-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=98063 */ +"instruction-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98152 */ +"instruction-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98246 */ +"instruction-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98345 */ +"instruction-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98449 */ +"instruction-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98547 */ +"instruction-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98648 */ +"instruction-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=98753 */ +"instruction-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=98856 */ +"instruction-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=98951 */ +"instruction-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99051 */ +"instruction-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99156 */ +"instruction-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99255 */ +"instruction-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99357 */ +"instruction-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=99463 */ +"instruction-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=99567 */ +"instruction-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99661 */ +"instruction-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99760 */ +"instruction-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99864 */ +"instruction-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=99962 */ +"instruction-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=100063 */ +"instruction-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=100168 */ +"instruction-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=100271 */ +"instruction-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=100365 */ +"instruction-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=100464 */ +"instruction-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=100557 */ +"instruction-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000" +/* offset=100653 */ +"instruction-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=100753 */ +"instruction-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000" +/* offset=100851 */ +"branch\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=100938 */ +"branch-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101030 */ +"branch-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101127 */ +"branch-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101229 */ +"branch-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101325 */ +"branch-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101424 */ +"branch-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00000\000\000\000\000\000" +/* offset=101527 */ +"branch-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=101628 */ +"branch-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00000\000\000\000\000\000" +/* offset=101721 */ +"branch-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101819 */ +"branch-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=101922 */ +"branch-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102019 */ +"branch-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102119 */ +"branch-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=102223 */ +"branch-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=102325 */ +"branch-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102417 */ +"branch-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102514 */ +"branch-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102616 */ +"branch-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102712 */ +"branch-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=102811 */ +"branch-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=102914 */ +"branch-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=103015 */ +"branch-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103107 */ +"branch-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103204 */ +"branch-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103295 */ +"branch-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103389 */ +"branch-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=103485 */ +"branches-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103579 */ +"branches-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103678 */ +"branches-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103782 */ +"branches-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103880 */ +"branches-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=103981 */ +"branches-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=104086 */ +"branches-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=104189 */ +"branches-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104284 */ +"branches-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104384 */ +"branches-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104489 */ +"branches-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104588 */ +"branches-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104690 */ +"branches-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=104796 */ +"branches-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=104900 */ +"branches-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=104994 */ +"branches-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105093 */ +"branches-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105197 */ +"branches-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105295 */ +"branches-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105396 */ +"branches-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=105501 */ +"branches-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=105604 */ +"branches-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105698 */ +"branches-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105797 */ +"branches-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105890 */ +"branches-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=105986 */ +"branches-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=106086 */ +"branches-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=106184 */ +"bpu\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106268 */ +"bpu-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106357 */ +"bpu-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106451 */ +"bpu-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106550 */ +"bpu-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106643 */ +"bpu-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=106739 */ +"bpu-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=106839 */ +"bpu-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=106937 */ +"bpu-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107027 */ +"bpu-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107122 */ +"bpu-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107222 */ +"bpu-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107316 */ +"bpu-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107413 */ +"bpu-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=107514 */ +"bpu-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=107613 */ +"bpu-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107702 */ +"bpu-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107796 */ +"bpu-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107895 */ +"bpu-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=107988 */ +"bpu-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108084 */ +"bpu-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=108184 */ +"bpu-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=108282 */ +"bpu-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108371 */ +"bpu-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108465 */ +"bpu-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108553 */ +"bpu-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108644 */ +"bpu-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=108739 */ +"bpu-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=108832 */ +"btb\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=108916 */ +"btb-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109005 */ +"btb-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109099 */ +"btb-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109198 */ +"btb-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109291 */ +"btb-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109387 */ +"btb-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=109487 */ +"btb-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=109585 */ +"btb-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109675 */ +"btb-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109770 */ +"btb-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109870 */ +"btb-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=109964 */ +"btb-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110061 */ +"btb-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=110162 */ +"btb-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=110261 */ +"btb-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110350 */ +"btb-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110444 */ +"btb-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110543 */ +"btb-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110636 */ +"btb-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=110732 */ +"btb-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=110832 */ +"btb-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=110930 */ +"btb-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111019 */ +"btb-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111113 */ +"btb-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111201 */ +"btb-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111292 */ +"btb-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=111387 */ +"btb-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=111480 */ +"bpc\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111564 */ +"bpc-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111653 */ +"bpc-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111747 */ +"bpc-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111846 */ +"bpc-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=111939 */ +"bpc-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112035 */ +"bpc-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=112135 */ +"bpc-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=112233 */ +"bpc-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112323 */ +"bpc-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112418 */ +"bpc-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112518 */ +"bpc-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112612 */ +"bpc-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112709 */ +"bpc-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=112810 */ +"bpc-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=112909 */ +"bpc-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=112998 */ +"bpc-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113092 */ +"bpc-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113191 */ +"bpc-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113284 */ +"bpc-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113380 */ +"bpc-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=113480 */ +"bpc-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=113578 */ +"bpc-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113667 */ +"bpc-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113761 */ +"bpc-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113849 */ +"bpc-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000" +/* offset=113940 */ +"bpc-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=114035 */ +"bpc-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000" +/* offset=114128 */ +"node\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114203 */ +"node-load\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114283 */ +"node-load-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114368 */ +"node-load-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114458 */ +"node-load-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114542 */ +"node-load-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114629 */ +"node-load-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00000\000\000\000\000\000" +/* offset=114720 */ +"node-load-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=114809 */ +"node-loads\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00000\000\000\000\000\000" +/* offset=114890 */ +"node-loads-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=114976 */ +"node-loads-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115067 */ +"node-loads-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115152 */ +"node-loads-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115240 */ +"node-loads-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=115332 */ +"node-loads-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=115422 */ +"node-read\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115502 */ +"node-read-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115587 */ +"node-read-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115677 */ +"node-read-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115761 */ +"node-read-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=115848 */ +"node-read-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=115939 */ +"node-read-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=116028 */ +"node-store\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116114 */ +"node-store-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116205 */ +"node-store-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116301 */ +"node-store-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116391 */ +"node-store-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116484 */ +"node-store-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00000\000\000\000\000\000" +/* offset=116577 */ +"node-store-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" +/* offset=116668 */ +"node-stores\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00000\000\000\000\000\000" +/* offset=116755 */ +"node-stores-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116847 */ +"node-stores-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=116944 */ +"node-stores-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117035 */ +"node-stores-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117129 */ +"node-stores-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" +/* offset=117223 */ +"node-stores-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" +/* offset=117315 */ +"node-write\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117401 */ +"node-write-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117492 */ +"node-write-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117588 */ +"node-write-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117678 */ +"node-write-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000" +/* offset=117771 */ +"node-write-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" +/* offset=117864 */ +"node-write-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000" +/* offset=117955 */ +"node-prefetch\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118047 */ +"node-prefetch-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118144 */ +"node-prefetch-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118246 */ +"node-prefetch-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118342 */ +"node-prefetch-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118441 */ +"node-prefetch-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00000\000\000\000\000\000" +/* offset=118540 */ +"node-prefetch-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=118637 */ +"node-prefetches\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00000\000\000\000\000\000" +/* offset=118731 */ +"node-prefetches-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118830 */ +"node-prefetches-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=118934 */ +"node-prefetches-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119032 */ +"node-prefetches-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119133 */ +"node-prefetches-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=119234 */ +"node-prefetches-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=119333 */ +"node-speculative-read\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119433 */ +"node-speculative-read-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119538 */ +"node-speculative-read-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119648 */ +"node-speculative-read-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119752 */ +"node-speculative-read-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=119859 */ +"node-speculative-read-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=119966 */ +"node-speculative-read-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=120071 */ +"node-speculative-load\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=120171 */ +"node-speculative-load-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=120276 */ +"node-speculative-load-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=120386 */ +"node-speculative-load-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=120490 */ +"node-speculative-load-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000" +/* offset=120597 */ +"node-speculative-load-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=120704 */ +"node-speculative-load-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000" +/* offset=120809 */ +"node-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=120889 */ +"node-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=120974 */ +"node-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=121053 */ +"node-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000" +/* offset=121135 */ +"node-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=121221 */ +"node-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000" +/* offset=121305 */ +"cpu-cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000" +/* offset=121467 */ +"cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cpu-cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000" +/* offset=121629 */ +"instructions\000legacy hardware\000Retired instructions. Be careful, these can be affected by various issues, most notably hardware interrupt counts\000legacy-hardware-config=1\000\00000\000\000\000\000\000" +/* offset=121805 */ +"cache-references\000legacy hardware\000Cache accesses. Usually this indicates Last Level Cache accesses but this may vary depending on your CPU. This may include prefetches and coherency messages; again this depends on the design of your CPU\000legacy-hardware-config=2\000\00000\000\000\000\000\000" +/* offset=122075 */ +"cache-misses\000legacy hardware\000Cache misses. Usually this indicates Last Level Cache misses; this is intended to be used in conjunction with the PERF_COUNT_HW_CACHE_REFERENCES event to calculate cache miss rates\000legacy-hardware-config=3\000\00000\000\000\000\000\000" +/* offset=122318 */ +"branches\000legacy hardware\000Retired branch instructions [This event is an alias of branch-instructions]\000legacy-hardware-config=4\000\00000\000\000\000\000\000" +/* offset=122452 */ +"branch-instructions\000legacy hardware\000Retired branch instructions [This event is an alias of branches]\000legacy-hardware-config=4\000\00000\000\000\000\000\000" +/* offset=122586 */ +"branch-misses\000legacy hardware\000Mispredicted branch instructions\000legacy-hardware-config=5\000\00000\000\000\000\000\000" +/* offset=122682 */ +"bus-cycles\000legacy hardware\000Bus cycles, which can be different from total cycles\000legacy-hardware-config=6\000\00000\000\000\000\000\000" +/* offset=122795 */ +"stalled-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of idle-cycles-frontend]\000legacy-hardware-config=7\000\00000\000\000\000\000\000" +/* offset=122945 */ +"idle-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of stalled-cycles-fronted]\000legacy-hardware-config=7\000\00000\000\000\000\000\000" +/* offset=123094 */ +"stalled-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of idle-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000" +/* offset=123247 */ +"idle-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of stalled-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000" +/* offset=123400 */ +"ref-cycles\000legacy hardware\000Total cycles; not affected by CPU frequency scaling\000legacy-hardware-config=9\000\00000\000\000\000\000\000" +/* offset=123512 */ +"software\000" +/* offset=123521 */ +"cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\000\000001e-6msec\000\000\000\000\000" +/* offset=123607 */ +"task-clock\000software\000Per-task high-resolution timer based event\000config=1\000\000001e-6msec\000\000\000\000\000" +/* offset=123695 */ +"faults\000software\000Number of page faults [This event is an alias of page-faults]\000config=2\000\00000\000\000\000\000\000" +/* offset=123790 */ +"page-faults\000software\000Number of page faults [This event is an alias of faults]\000config=2\000\00000\000\000\000\000\000" +/* offset=123885 */ +"context-switches\000software\000Number of context switches [This event is an alias of cs]\000config=3\000\00000\000\000\000\000\000" +/* offset=123986 */ +"cs\000software\000Number of context switches [This event is an alias of context-switches]\000config=3\000\00000\000\000\000\000\000" +/* offset=124087 */ +"cpu-migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of migrations]\000config=4\000\00000\000\000\000\000\000" +/* offset=124219 */ +"migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]\000config=4\000\00000\000\000\000\000\000" +/* offset=124351 */ +"minor-faults\000software\000Number of minor page faults. Minor faults don't require I/O to handle\000config=5\000\00000\000\000\000\000\000" +/* offset=124460 */ +"major-faults\000software\000Number of major page faults. Major faults require I/O to handle\000config=6\000\00000\000\000\000\000\000" +/* offset=124563 */ +"alignment-faults\000software\000Number of kernel handled memory alignment faults\000config=7\000\00000\000\000\000\000\000" +/* offset=124655 */ +"emulation-faults\000software\000Number of kernel handled unimplemented instruction faults handled through emulation\000config=8\000\00000\000\000\000\000\000" +/* offset=124782 */ +"dummy\000software\000A placeholder event that doesn't count anything\000config=9\000\00000\000\000\000\000\000" +/* offset=124862 */ +"bpf-output\000software\000An event used by BPF programs to write to the perf ring buffer\000config=0xa\000\00000\000\000\000\000\000" +/* offset=124964 */ +"cgroup-switches\000software\000Number of context switches to a task in a different cgroup\000config=0xb\000\00000\000\000\000\000\000" +/* offset=125067 */ +"tool\000" +/* offset=125072 */ +"duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000" +/* offset=125148 */ +"user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000" +/* offset=125218 */ +"system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000" +/* offset=125286 */ +"has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000" +/* offset=125362 */ +"num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000" +/* offset=125507 */ +"num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000" +/* offset=125610 */ +"num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000" +/* offset=125727 */ +"num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000" +/* offset=125803 */ +"num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000" +/* offset=125889 */ +"slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000" +/* offset=125999 */ +"smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000" +/* offset=126106 */ +"system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000" +/* offset=126205 */ +"core_wide\000tool\0001 if not SMT, if SMT are events being gathered on all SMT threads 1 otherwise 0\000config=0xd\000\00000\000\000\000\000\000" +/* offset=126319 */ +"target_cpu\000tool\0001 if CPUs being analyzed, 0 if threads/processes\000config=0xe\000\00000\000\000\000\000\000" +/* offset=126403 */ +"bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000" +/* offset=126465 */ +"bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000" +/* offset=126527 */ +"l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000" +/* offset=126625 */ +"segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000" +/* offset=126727 */ +"dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000" +/* offset=126860 */ +"eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000" +/* offset=126978 */ +"hisi_sccl,ddrc\000" +/* offset=126993 */ +"uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000\000" +/* offset=127063 */ +"uncore_cbox\000" +/* offset=127075 */ +"unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000\000" +/* offset=127229 */ +"event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000\000" +/* offset=127283 */ +"event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000\000" +/* offset=127341 */ +"hisi_sccl,l3c\000" +/* offset=127355 */ +"uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000\000" +/* offset=127423 */ +"uncore_imc_free_running\000" +/* offset=127447 */ +"uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000\000" +/* offset=127527 */ +"uncore_imc\000" +/* offset=127538 */ +"uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000\000" +/* offset=127603 */ +"uncore_sys_ddr_pmu\000" +/* offset=127622 */ +"sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000" +/* offset=127698 */ +"uncore_sys_ccn_pmu\000" +/* offset=127717 */ +"sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000" +/* offset=127794 */ +"uncore_sys_cmn_pmu\000" +/* offset=127813 */ +"sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000" +/* offset=127956 */ +"CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@) / (duration_time * 1e9)\000\000Average CPU utilization\000\0001CPUs\000\000\000\000011" +/* offset=128142 */ +"cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switches@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Context switches per CPU second\000\0001cs/sec\000\000\000\000011" +/* offset=128375 */ +"migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011" +/* offset=128635 */ +"page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011" +/* offset=128866 */ +"insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001" +/* offset=128979 */ +"stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001" +/* offset=129404 */ +"frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001" +/* offset=129583 */ +"backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001" +/* offset=129757 */ +"cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011" +/* offset=129933 */ +"branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011" +/* offset=130113 */ +"branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001" +/* offset=130217 */ +"l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001" +/* offset=130333 */ +"llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001" +/* offset=130434 */ +"l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001" +/* offset=130549 */ +"dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001" +/* offset=130655 */ +"itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001" +/* offset=130761 */ +"l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001" +/* offset=130909 */ +"CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000" +/* offset=130932 */ +"IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000" +/* offset=130996 */ +"Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000" +/* offset=131163 */ +"dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" +/* offset=131228 */ +"icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" +/* offset=131296 */ +"cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000" +/* offset=131368 */ +"DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000" +/* offset=131463 */ +"DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000" +/* offset=131598 */ +"DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000" +/* offset=131663 */ +"DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000" +/* offset=131732 */ +"DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000" +/* offset=131803 */ +"M1\000\000ipc + M2\000\000\000\000\000\000\000\000000" +/* offset=131826 */ +"M2\000\000ipc + M1\000\000\000\000\000\000\000\000000" +/* offset=131849 */ +"M3\000\0001 / M3\000\000\000\000\000\000\000\000000" +/* offset=131870 */ +"L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000" ; static const struct compact_pmu_event pmu_events__common_default_core[] = { -{ 111480 }, /* bpc\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113849 }, /* bpc-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111564 }, /* bpc-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111939 }, /* bpc-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112135 }, /* bpc-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 112035 }, /* bpc-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 111846 }, /* bpc-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111747 }, /* bpc-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111653 }, /* bpc-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112233 }, /* bpc-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112612 }, /* bpc-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112810 }, /* bpc-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 112709 }, /* bpc-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 112518 }, /* bpc-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112418 }, /* bpc-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112323 }, /* bpc-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 114035 }, /* bpc-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 113940 }, /* bpc-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 113761 }, /* bpc-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112909 }, /* bpc-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113284 }, /* bpc-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113480 }, /* bpc-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 113380 }, /* bpc-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 113191 }, /* bpc-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113092 }, /* bpc-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 112998 }, /* bpc-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113667 }, /* bpc-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 113578 }, /* bpc-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106184 }, /* bpu\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108553 }, /* bpu-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106268 }, /* bpu-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106643 }, /* bpu-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106839 }, /* bpu-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 106739 }, /* bpu-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 106550 }, /* bpu-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106451 }, /* bpu-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106357 }, /* bpu-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106937 }, /* bpu-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107316 }, /* bpu-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107514 }, /* bpu-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 107413 }, /* bpu-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 107222 }, /* bpu-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107122 }, /* bpu-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107027 }, /* bpu-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108739 }, /* bpu-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 108644 }, /* bpu-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 108465 }, /* bpu-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107613 }, /* bpu-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107988 }, /* bpu-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108184 }, /* bpu-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 108084 }, /* bpu-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 107895 }, /* bpu-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107796 }, /* bpu-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 107702 }, /* bpu-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108371 }, /* bpu-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108282 }, /* bpu-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 100851 }, /* branch\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103295 }, /* branch-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 122452 }, /* branch-instructions\000legacy hardware\000Retired branch instructions [This event is an alias of branches]\000legacy-hardware-config=4\000\00000\000\000\000\000\000 */ -{ 100938 }, /* branch-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101325 }, /* branch-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101527 }, /* branch-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 101424 }, /* branch-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00000\000\000\000\000\000 */ -{ 101229 }, /* branch-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101127 }, /* branch-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101030 }, /* branch-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101628 }, /* branch-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00000\000\000\000\000\000 */ -{ 102019 }, /* branch-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102223 }, /* branch-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 102119 }, /* branch-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 101922 }, /* branch-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101819 }, /* branch-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 101721 }, /* branch-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103389 }, /* branch-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 122586 }, /* branch-misses\000legacy hardware\000Mispredicted branch instructions\000legacy-hardware-config=5\000\00000\000\000\000\000\000 */ -{ 103204 }, /* branch-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102325 }, /* branch-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102712 }, /* branch-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102914 }, /* branch-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 102811 }, /* branch-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 102616 }, /* branch-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102514 }, /* branch-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 102417 }, /* branch-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103107 }, /* branch-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103015 }, /* branch-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 122318 }, /* branches\000legacy hardware\000Retired branch instructions [This event is an alias of branch-instructions]\000legacy-hardware-config=4\000\00000\000\000\000\000\000 */ -{ 105890 }, /* branches-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103485 }, /* branches-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103880 }, /* branches-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104086 }, /* branches-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 103981 }, /* branches-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 103782 }, /* branches-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103678 }, /* branches-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 103579 }, /* branches-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104189 }, /* branches-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104588 }, /* branches-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104796 }, /* branches-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 104690 }, /* branches-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 104489 }, /* branches-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104384 }, /* branches-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104284 }, /* branches-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 106086 }, /* branches-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 105986 }, /* branches-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 105797 }, /* branches-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104900 }, /* branches-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 105295 }, /* branches-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 105501 }, /* branches-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 105396 }, /* branches-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 105197 }, /* branches-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 105093 }, /* branches-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 104994 }, /* branches-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 105698 }, /* branches-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 105604 }, /* branches-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108832 }, /* btb\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111201 }, /* btb-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 108916 }, /* btb-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109291 }, /* btb-load-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109487 }, /* btb-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 109387 }, /* btb-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 109198 }, /* btb-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109099 }, /* btb-load-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109005 }, /* btb-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109585 }, /* btb-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109964 }, /* btb-loads-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110162 }, /* btb-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 110061 }, /* btb-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 109870 }, /* btb-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109770 }, /* btb-loads-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 109675 }, /* btb-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111387 }, /* btb-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 111292 }, /* btb-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 111113 }, /* btb-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110261 }, /* btb-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110636 }, /* btb-read-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110832 }, /* btb-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 110732 }, /* btb-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache-config=0x10005\000\00010\000\000\000\000\000 */ -{ 110543 }, /* btb-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110444 }, /* btb-read-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110350 }, /* btb-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 111019 }, /* btb-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 110930 }, /* btb-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-config=5\000\00010\000\000\000\000\000 */ -{ 122682 }, /* bus-cycles\000legacy hardware\000Bus cycles, which can be different from total cycles\000legacy-hardware-config=6\000\00000\000\000\000\000\000 */ -{ 122075 }, /* cache-misses\000legacy hardware\000Cache misses. Usually this indicates Last Level Cache misses; this is intended to be used in conjunction with the PERF_COUNT_HW_CACHE_REFERENCES event to calculate cache miss rates\000legacy-hardware-config=3\000\00000\000\000\000\000\000 */ -{ 121805 }, /* cache-references\000legacy hardware\000Cache accesses. Usually this indicates Last Level Cache accesses but this may vary depending on your CPU. This may include prefetches and coherency messages; again this depends on the design of your CPU\000legacy-hardware-config=2\000\00000\000\000\000\000\000 */ -{ 121305 }, /* cpu-cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000 */ -{ 121467 }, /* cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU frequency scaling [This event is an alias of cpu-cycles]\000legacy-hardware-config=0\000\00000\000\000\000\000\000 */ -{ 78952 }, /* d-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 85655 }, /* d-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79024 }, /* d-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79351 }, /* d-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79523 }, /* d-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 79435 }, /* d-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 79270 }, /* d-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79183 }, /* d-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79101 }, /* d-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79609 }, /* d-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79940 }, /* d-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 80114 }, /* d-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 80025 }, /* d-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 79858 }, /* d-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79770 }, /* d-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 79687 }, /* d-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 85817 }, /* d-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 85734 }, /* d-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 85579 }, /* d-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 82650 }, /* d-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83025 }, /* d-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83217 }, /* d-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 83121 }, /* d-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 82932 }, /* d-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 82833 }, /* d-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 82739 }, /* d-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83311 }, /* d-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83694 }, /* d-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83890 }, /* d-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 83792 }, /* d-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 83599 }, /* d-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83498 }, /* d-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83402 }, /* d-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 80201 }, /* d-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 80528 }, /* d-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 80700 }, /* d-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 80612 }, /* d-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 80447 }, /* d-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 80360 }, /* d-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 80278 }, /* d-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 85497 }, /* d-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 85420 }, /* d-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 84703 }, /* d-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 85110 }, /* d-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 85318 }, /* d-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 85214 }, /* d-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 85009 }, /* d-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84902 }, /* d-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84800 }, /* d-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 83986 }, /* d-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84393 }, /* d-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84601 }, /* d-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 84497 }, /* d-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 84292 }, /* d-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84185 }, /* d-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 84083 }, /* d-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 80786 }, /* d-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81137 }, /* d-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81317 }, /* d-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 81227 }, /* d-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 81050 }, /* d-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 80957 }, /* d-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 80869 }, /* d-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81405 }, /* d-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81760 }, /* d-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81942 }, /* d-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 81851 }, /* d-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 81672 }, /* d-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81578 }, /* d-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 81489 }, /* d-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 82031 }, /* d-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 82382 }, /* d-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 82562 }, /* d-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 82472 }, /* d-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 82295 }, /* d-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 82202 }, /* d-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 82114 }, /* d-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 85898 }, /* data-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 92823 }, /* data-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 85973 }, /* data-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86312 }, /* data-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86490 }, /* data-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 86399 }, /* data-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 86228 }, /* data-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86138 }, /* data-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86053 }, /* data-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86579 }, /* data-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86922 }, /* data-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 87102 }, /* data-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 87010 }, /* data-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 86837 }, /* data-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86746 }, /* data-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 86660 }, /* data-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 92991 }, /* data-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 92905 }, /* data-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 92744 }, /* data-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 89725 }, /* data-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90112 }, /* data-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90310 }, /* data-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 90211 }, /* data-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 90016 }, /* data-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 89914 }, /* data-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 89817 }, /* data-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90407 }, /* data-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90802 }, /* data-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91004 }, /* data-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 90903 }, /* data-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 90704 }, /* data-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90600 }, /* data-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 90501 }, /* data-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 87192 }, /* data-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 87531 }, /* data-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 87709 }, /* data-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 87618 }, /* data-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 87447 }, /* data-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 87357 }, /* data-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 87272 }, /* data-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 92659 }, /* data-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 92579 }, /* data-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 91841 }, /* data-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 92260 }, /* data-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 92474 }, /* data-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 92367 }, /* data-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 92156 }, /* data-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 92046 }, /* data-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91941 }, /* data-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91103 }, /* data-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91522 }, /* data-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91736 }, /* data-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 91629 }, /* data-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 91418 }, /* data-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91308 }, /* data-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 91203 }, /* data-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 87798 }, /* data-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88161 }, /* data-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88347 }, /* data-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 88254 }, /* data-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 88071 }, /* data-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 87975 }, /* data-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 87884 }, /* data-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88438 }, /* data-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88805 }, /* data-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88993 }, /* data-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 88899 }, /* data-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 88714 }, /* data-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88617 }, /* data-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 88525 }, /* data-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 89085 }, /* data-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 89448 }, /* data-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 89634 }, /* data-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 89541 }, /* data-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 89358 }, /* data-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 89262 }, /* data-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 89171 }, /* data-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 72083 }, /* dtlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 78712 }, /* dtlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72154 }, /* dtlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72477 }, /* dtlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72647 }, /* dtlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 72560 }, /* dtlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00000\000\000\000\000\000 */ -{ 72397 }, /* dtlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72311 }, /* dtlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72230 }, /* dtlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72732 }, /* dtlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00000\000\000\000\000\000 */ -{ 73059 }, /* dtlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 73231 }, /* dtlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 73143 }, /* dtlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 72978 }, /* dtlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72891 }, /* dtlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 72809 }, /* dtlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 78872 }, /* dtlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 78790 }, /* dtlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 78637 }, /* dtlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 75738 }, /* dtlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76109 }, /* dtlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76299 }, /* dtlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 76204 }, /* dtlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00000\000\000\000\000\000 */ -{ 76017 }, /* dtlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 75919 }, /* dtlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 75826 }, /* dtlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76392 }, /* dtlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00000\000\000\000\000\000 */ -{ 76771 }, /* dtlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76965 }, /* dtlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 76868 }, /* dtlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 76677 }, /* dtlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76577 }, /* dtlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 76482 }, /* dtlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 73317 }, /* dtlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 73640 }, /* dtlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 73810 }, /* dtlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 73723 }, /* dtlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003\000\00010\000\000\000\000\000 */ -{ 73560 }, /* dtlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 73474 }, /* dtlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 73393 }, /* dtlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 78556 }, /* dtlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 78480 }, /* dtlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\00010\000\000\000\000\000 */ -{ 77770 }, /* dtlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 78173 }, /* dtlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 78379 }, /* dtlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 78276 }, /* dtlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 78073 }, /* dtlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77967 }, /* dtlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77866 }, /* dtlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77060 }, /* dtlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77463 }, /* dtlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77669 }, /* dtlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 77566 }, /* dtlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache-config=0x10203\000\00010\000\000\000\000\000 */ -{ 77363 }, /* dtlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77257 }, /* dtlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 77156 }, /* dtlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-config=0x203\000\00010\000\000\000\000\000 */ -{ 73895 }, /* dtlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74242 }, /* dtlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74420 }, /* dtlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 74331 }, /* dtlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00000\000\000\000\000\000 */ -{ 74156 }, /* dtlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74064 }, /* dtlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 73977 }, /* dtlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74507 }, /* dtlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00000\000\000\000\000\000 */ -{ 74858 }, /* dtlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75038 }, /* dtlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 74948 }, /* dtlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 74771 }, /* dtlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74678 }, /* dtlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 74590 }, /* dtlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75126 }, /* dtlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75473 }, /* dtlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75651 }, /* dtlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 75562 }, /* dtlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-config=0x10103\000\00010\000\000\000\000\000 */ -{ 75387 }, /* dtlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75295 }, /* dtlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 75208 }, /* dtlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x103\000\00010\000\000\000\000\000 */ -{ 95555 }, /* i-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97799 }, /* i-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95634 }, /* i-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95989 }, /* i-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96175 }, /* i-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 96080 }, /* i-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 95901 }, /* i-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95807 }, /* i-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95718 }, /* i-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96268 }, /* i-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96627 }, /* i-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96815 }, /* i-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 96719 }, /* i-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 96538 }, /* i-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96443 }, /* i-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96353 }, /* i-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97975 }, /* i-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 97885 }, /* i-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 97716 }, /* i-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96909 }, /* i-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97264 }, /* i-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97450 }, /* i-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 97355 }, /* i-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 97176 }, /* i-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97082 }, /* i-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 96993 }, /* i-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97627 }, /* i-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 97543 }, /* i-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 123247 }, /* idle-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of stalled-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000 */ -{ 122945 }, /* idle-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of stalled-cycles-fronted]\000legacy-hardware-config=7\000\00000\000\000\000\000\000 */ -{ 98063 }, /* instruction-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 100557 }, /* instruction-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98152 }, /* instruction-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98547 }, /* instruction-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98753 }, /* instruction-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 98648 }, /* instruction-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 98449 }, /* instruction-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98345 }, /* instruction-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98246 }, /* instruction-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98856 }, /* instruction-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99255 }, /* instruction-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99463 }, /* instruction-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 99357 }, /* instruction-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 99156 }, /* instruction-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99051 }, /* instruction-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 98951 }, /* instruction-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 100753 }, /* instruction-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 100653 }, /* instruction-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 100464 }, /* instruction-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99567 }, /* instruction-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99962 }, /* instruction-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 100168 }, /* instruction-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 100063 }, /* instruction-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 99864 }, /* instruction-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99760 }, /* instruction-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 99661 }, /* instruction-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 100365 }, /* instruction-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 100271 }, /* instruction-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 121629 }, /* instructions\000legacy hardware\000Retired instructions. Be careful, these can be affected by various issues, most notably hardware interrupt counts\000legacy-hardware-config=1\000\00000\000\000\000\000\000 */ -{ 93075 }, /* itlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95294 }, /* itlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93153 }, /* itlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93504 }, /* itlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93688 }, /* itlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 93594 }, /* itlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00000\000\000\000\000\000 */ -{ 93417 }, /* itlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93324 }, /* itlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93236 }, /* itlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93780 }, /* itlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00000\000\000\000\000\000 */ -{ 94135 }, /* itlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94321 }, /* itlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 94226 }, /* itlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 94047 }, /* itlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93953 }, /* itlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 93864 }, /* itlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95468 }, /* itlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 95379 }, /* itlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 95212 }, /* itlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94414 }, /* itlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94765 }, /* itlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94949 }, /* itlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 94855 }, /* itlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=0x10004\000\00010\000\000\000\000\000 */ -{ 94678 }, /* itlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94585 }, /* itlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 94497 }, /* itlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95124 }, /* itlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 95041 }, /* itlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\000\00010\000\000\000\000\000 */ -{ 8037 }, /* l1-d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15406 }, /* l1-d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8118 }, /* l1-d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8481 }, /* l1-d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8671 }, /* l1-d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 8574 }, /* l1-d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 8391 }, /* l1-d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8295 }, /* l1-d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8204 }, /* l1-d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8766 }, /* l1-d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9133 }, /* l1-d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9325 }, /* l1-d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 9227 }, /* l1-d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 9042 }, /* l1-d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8945 }, /* l1-d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 8853 }, /* l1-d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15586 }, /* l1-d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 15494 }, /* l1-d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 15321 }, /* l1-d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 12122 }, /* l1-d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12533 }, /* l1-d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12743 }, /* l1-d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 12638 }, /* l1-d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 12431 }, /* l1-d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12323 }, /* l1-d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12220 }, /* l1-d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12846 }, /* l1-d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13265 }, /* l1-d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13479 }, /* l1-d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 13372 }, /* l1-d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 13161 }, /* l1-d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13051 }, /* l1-d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 12946 }, /* l1-d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 9421 }, /* l1-d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9784 }, /* l1-d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9974 }, /* l1-d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 9877 }, /* l1-d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 9694 }, /* l1-d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9598 }, /* l1-d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 9507 }, /* l1-d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15230 }, /* l1-d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15144 }, /* l1-d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 14364 }, /* l1-d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 14807 }, /* l1-d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 15033 }, /* l1-d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 14920 }, /* l1-d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 14697 }, /* l1-d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 14581 }, /* l1-d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 14470 }, /* l1-d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13584 }, /* l1-d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 14027 }, /* l1-d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 14253 }, /* l1-d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 14140 }, /* l1-d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 13917 }, /* l1-d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13801 }, /* l1-d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 13690 }, /* l1-d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 10069 }, /* l1-d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10456 }, /* l1-d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10654 }, /* l1-d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 10555 }, /* l1-d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 10360 }, /* l1-d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10258 }, /* l1-d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10161 }, /* l1-d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10751 }, /* l1-d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11142 }, /* l1-d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11342 }, /* l1-d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 11242 }, /* l1-d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 11045 }, /* l1-d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10942 }, /* l1-d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 10844 }, /* l1-d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11440 }, /* l1-d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11827 }, /* l1-d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 12025 }, /* l1-d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 11926 }, /* l1-d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 11731 }, /* l1-d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11629 }, /* l1-d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 11532 }, /* l1-d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 23238 }, /* l1-data\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 30829 }, /* l1-data-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23322 }, /* l1-data-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23697 }, /* l1-data-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23893 }, /* l1-data-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 23793 }, /* l1-data-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 23604 }, /* l1-data-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23505 }, /* l1-data-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23411 }, /* l1-data-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23991 }, /* l1-data-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24370 }, /* l1-data-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24568 }, /* l1-data-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 24467 }, /* l1-data-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 24276 }, /* l1-data-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24176 }, /* l1-data-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24081 }, /* l1-data-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 31015 }, /* l1-data-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 30920 }, /* l1-data-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 30741 }, /* l1-data-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 27452 }, /* l1-data-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 27875 }, /* l1-data-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28091 }, /* l1-data-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 27983 }, /* l1-data-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 27770 }, /* l1-data-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 27659 }, /* l1-data-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 27553 }, /* l1-data-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28197 }, /* l1-data-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28628 }, /* l1-data-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28848 }, /* l1-data-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 28738 }, /* l1-data-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 28521 }, /* l1-data-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28408 }, /* l1-data-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28300 }, /* l1-data-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 24667 }, /* l1-data-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 25042 }, /* l1-data-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 25238 }, /* l1-data-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 25138 }, /* l1-data-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 24949 }, /* l1-data-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24850 }, /* l1-data-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 24756 }, /* l1-data-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 30647 }, /* l1-data-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 30558 }, /* l1-data-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 29757 }, /* l1-data-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 30212 }, /* l1-data-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 30444 }, /* l1-data-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 30328 }, /* l1-data-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 30099 }, /* l1-data-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29980 }, /* l1-data-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29866 }, /* l1-data-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 28956 }, /* l1-data-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29411 }, /* l1-data-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29643 }, /* l1-data-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 29527 }, /* l1-data-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 29298 }, /* l1-data-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29179 }, /* l1-data-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 29065 }, /* l1-data-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 25336 }, /* l1-data-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 25735 }, /* l1-data-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 25939 }, /* l1-data-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 25837 }, /* l1-data-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 25636 }, /* l1-data-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 25531 }, /* l1-data-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 25431 }, /* l1-data-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26039 }, /* l1-data-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26442 }, /* l1-data-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26648 }, /* l1-data-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 26545 }, /* l1-data-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 26342 }, /* l1-data-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26236 }, /* l1-data-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26135 }, /* l1-data-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26749 }, /* l1-data-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 27148 }, /* l1-data-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 27352 }, /* l1-data-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 27250 }, /* l1-data-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 27049 }, /* l1-data-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26944 }, /* l1-data-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 26844 }, /* l1-data-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 13 }, /* l1-dcache\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 7752 }, /* l1-dcache-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 99 }, /* l1-dcache-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 482 }, /* l1-dcache-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 682 }, /* l1-dcache-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 580 }, /* l1-dcache-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00000\000\000\000\000\000 */ -{ 387 }, /* l1-dcache-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 286 }, /* l1-dcache-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 190 }, /* l1-dcache-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 782 }, /* l1-dcache-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00000\000\000\000\000\000 */ -{ 1169 }, /* l1-dcache-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 1371 }, /* l1-dcache-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 1268 }, /* l1-dcache-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 1073 }, /* l1-dcache-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 971 }, /* l1-dcache-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 874 }, /* l1-dcache-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 7942 }, /* l1-dcache-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 7845 }, /* l1-dcache-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 7662 }, /* l1-dcache-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 4313 }, /* l1-dcache-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 4744 }, /* l1-dcache-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 4964 }, /* l1-dcache-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 4854 }, /* l1-dcache-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00000\000\000\000\000\000 */ -{ 4637 }, /* l1-dcache-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 4524 }, /* l1-dcache-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 4416 }, /* l1-dcache-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5072 }, /* l1-dcache-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00000\000\000\000\000\000 */ -{ 5511 }, /* l1-dcache-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5735 }, /* l1-dcache-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 5623 }, /* l1-dcache-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 5402 }, /* l1-dcache-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5287 }, /* l1-dcache-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5177 }, /* l1-dcache-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 1472 }, /* l1-dcache-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 1855 }, /* l1-dcache-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 2055 }, /* l1-dcache-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 1953 }, /* l1-dcache-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 1760 }, /* l1-dcache-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 1659 }, /* l1-dcache-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 1563 }, /* l1-dcache-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 7566 }, /* l1-dcache-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 7475 }, /* l1-dcache-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 6660 }, /* l1-dcache-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 7123 }, /* l1-dcache-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 7359 }, /* l1-dcache-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 7241 }, /* l1-dcache-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 7008 }, /* l1-dcache-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 6887 }, /* l1-dcache-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 6771 }, /* l1-dcache-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5845 }, /* l1-dcache-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 6308 }, /* l1-dcache-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 6544 }, /* l1-dcache-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 6426 }, /* l1-dcache-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 6193 }, /* l1-dcache-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 6072 }, /* l1-dcache-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 5956 }, /* l1-dcache-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 2155 }, /* l1-dcache-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2562 }, /* l1-dcache-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2770 }, /* l1-dcache-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 2666 }, /* l1-dcache-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00000\000\000\000\000\000 */ -{ 2461 }, /* l1-dcache-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2354 }, /* l1-dcache-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2252 }, /* l1-dcache-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2872 }, /* l1-dcache-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00000\000\000\000\000\000 */ -{ 3283 }, /* l1-dcache-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 3493 }, /* l1-dcache-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 3388 }, /* l1-dcache-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 3181 }, /* l1-dcache-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 3073 }, /* l1-dcache-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 2970 }, /* l1-dcache-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 3596 }, /* l1-dcache-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 4003 }, /* l1-dcache-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 4211 }, /* l1-dcache-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 4107 }, /* l1-dcache-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 3902 }, /* l1-dcache-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 3795 }, /* l1-dcache-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 3693 }, /* l1-dcache-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 37366 }, /* l1-i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43053 }, /* l1-i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37454 }, /* l1-i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37845 }, /* l1-i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38049 }, /* l1-i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 37945 }, /* l1-i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 37748 }, /* l1-i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37645 }, /* l1-i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37547 }, /* l1-i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38151 }, /* l1-i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38546 }, /* l1-i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38752 }, /* l1-i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 38647 }, /* l1-i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 38448 }, /* l1-i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38344 }, /* l1-i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38245 }, /* l1-i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43247 }, /* l1-i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 43148 }, /* l1-i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 42961 }, /* l1-i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 39552 }, /* l1-i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 39991 }, /* l1-i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 40215 }, /* l1-i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 40103 }, /* l1-i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 39882 }, /* l1-i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 39767 }, /* l1-i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 39657 }, /* l1-i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 40325 }, /* l1-i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 40772 }, /* l1-i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41000 }, /* l1-i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 40886 }, /* l1-i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 40661 }, /* l1-i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 40544 }, /* l1-i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 40432 }, /* l1-i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 38855 }, /* l1-i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 39246 }, /* l1-i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 39450 }, /* l1-i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 39346 }, /* l1-i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 39149 }, /* l1-i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 39046 }, /* l1-i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 38948 }, /* l1-i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 42863 }, /* l1-i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 42770 }, /* l1-i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 41941 }, /* l1-i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 42412 }, /* l1-i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 42652 }, /* l1-i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 42532 }, /* l1-i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 42295 }, /* l1-i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 42172 }, /* l1-i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 42054 }, /* l1-i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41112 }, /* l1-i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41583 }, /* l1-i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41823 }, /* l1-i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 41703 }, /* l1-i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 41466 }, /* l1-i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41343 }, /* l1-i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 41225 }, /* l1-i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 31108 }, /* l1-icache\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37060 }, /* l1-icache-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31201 }, /* l1-icache-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31612 }, /* l1-icache-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31826 }, /* l1-icache-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 31717 }, /* l1-icache-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00000\000\000\000\000\000 */ -{ 31510 }, /* l1-icache-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31402 }, /* l1-icache-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31299 }, /* l1-icache-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 31933 }, /* l1-icache-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00000\000\000\000\000\000 */ -{ 32348 }, /* l1-icache-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 32564 }, /* l1-icache-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 32454 }, /* l1-icache-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 32245 }, /* l1-icache-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 32136 }, /* l1-icache-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 32032 }, /* l1-icache-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 37264 }, /* l1-icache-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 37160 }, /* l1-icache-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 36963 }, /* l1-icache-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 33404 }, /* l1-icache-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 33863 }, /* l1-icache-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 34097 }, /* l1-icache-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 33980 }, /* l1-icache-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00000\000\000\000\000\000 */ -{ 33749 }, /* l1-icache-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 33629 }, /* l1-icache-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 33514 }, /* l1-icache-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 34212 }, /* l1-icache-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00000\000\000\000\000\000 */ -{ 34679 }, /* l1-icache-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 34917 }, /* l1-icache-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 34798 }, /* l1-icache-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 34563 }, /* l1-icache-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 34441 }, /* l1-icache-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 34324 }, /* l1-icache-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 32672 }, /* l1-icache-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 33083 }, /* l1-icache-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 33297 }, /* l1-icache-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 33188 }, /* l1-icache-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 32981 }, /* l1-icache-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 32873 }, /* l1-icache-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 32770 }, /* l1-icache-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 36860 }, /* l1-icache-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 36762 }, /* l1-icache-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 35898 }, /* l1-icache-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 36389 }, /* l1-icache-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 36639 }, /* l1-icache-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 36514 }, /* l1-icache-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 36267 }, /* l1-icache-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 36139 }, /* l1-icache-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 36016 }, /* l1-icache-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 35034 }, /* l1-icache-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 35525 }, /* l1-icache-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 35775 }, /* l1-icache-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 35650 }, /* l1-icache-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 35403 }, /* l1-icache-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 35275 }, /* l1-icache-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 35152 }, /* l1-icache-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 49266 }, /* l1-instruction\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 55483 }, /* l1-instruction-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 49364 }, /* l1-instruction-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 49795 }, /* l1-instruction-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50019 }, /* l1-instruction-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 49905 }, /* l1-instruction-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 49688 }, /* l1-instruction-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 49575 }, /* l1-instruction-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 49467 }, /* l1-instruction-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50131 }, /* l1-instruction-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50566 }, /* l1-instruction-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50792 }, /* l1-instruction-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 50677 }, /* l1-instruction-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 50458 }, /* l1-instruction-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50344 }, /* l1-instruction-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 50235 }, /* l1-instruction-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 55697 }, /* l1-instruction-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 55588 }, /* l1-instruction-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 55381 }, /* l1-instruction-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 51672 }, /* l1-instruction-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 52151 }, /* l1-instruction-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 52395 }, /* l1-instruction-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 52273 }, /* l1-instruction-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 52032 }, /* l1-instruction-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 51907 }, /* l1-instruction-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 51787 }, /* l1-instruction-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 52515 }, /* l1-instruction-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53002 }, /* l1-instruction-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53250 }, /* l1-instruction-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 53126 }, /* l1-instruction-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 52881 }, /* l1-instruction-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 52754 }, /* l1-instruction-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 52632 }, /* l1-instruction-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 50905 }, /* l1-instruction-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 51336 }, /* l1-instruction-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 51560 }, /* l1-instruction-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 51446 }, /* l1-instruction-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 51229 }, /* l1-instruction-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 51116 }, /* l1-instruction-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 51008 }, /* l1-instruction-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 55273 }, /* l1-instruction-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 55170 }, /* l1-instruction-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 54271 }, /* l1-instruction-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 54782 }, /* l1-instruction-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 55042 }, /* l1-instruction-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 54912 }, /* l1-instruction-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 54655 }, /* l1-instruction-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 54522 }, /* l1-instruction-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 54394 }, /* l1-instruction-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53372 }, /* l1-instruction-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53883 }, /* l1-instruction-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 54143 }, /* l1-instruction-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 54013 }, /* l1-instruction-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 53756 }, /* l1-instruction-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53623 }, /* l1-instruction-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 53495 }, /* l1-instruction-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 15676 }, /* l1d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 22971 }, /* l1d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15756 }, /* l1d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16115 }, /* l1d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16303 }, /* l1d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 16207 }, /* l1d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 16026 }, /* l1d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15931 }, /* l1d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 15841 }, /* l1d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16397 }, /* l1d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16760 }, /* l1d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16950 }, /* l1d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 16853 }, /* l1d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 16670 }, /* l1d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16574 }, /* l1d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 16483 }, /* l1d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 23149 }, /* l1d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 23058 }, /* l1d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 22887 }, /* l1d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 19718 }, /* l1d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20125 }, /* l1d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20333 }, /* l1d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 20229 }, /* l1d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 20024 }, /* l1d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 19917 }, /* l1d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 19815 }, /* l1d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20435 }, /* l1d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20850 }, /* l1d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21062 }, /* l1d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 20956 }, /* l1d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 20747 }, /* l1d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20638 }, /* l1d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 20534 }, /* l1d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 17045 }, /* l1d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 17404 }, /* l1d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 17592 }, /* l1d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 17496 }, /* l1d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-config=0x10000\000\00010\000\000\000\000\000 */ -{ 17315 }, /* l1d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 17220 }, /* l1d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 17130 }, /* l1d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 22797 }, /* l1d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 22712 }, /* l1d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0\000\00010\000\000\000\000\000 */ -{ 21939 }, /* l1d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 22378 }, /* l1d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 22602 }, /* l1d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 22490 }, /* l1d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 22269 }, /* l1d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 22154 }, /* l1d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 22044 }, /* l1d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21166 }, /* l1d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21605 }, /* l1d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21829 }, /* l1d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 21717 }, /* l1d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misses\000legacy-cache-config=0x10200\000\00010\000\000\000\000\000 */ -{ 21496 }, /* l1d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21381 }, /* l1d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 21271 }, /* l1d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-cache-config=0x200\000\00010\000\000\000\000\000 */ -{ 17686 }, /* l1d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18069 }, /* l1d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18265 }, /* l1d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 18167 }, /* l1d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 17974 }, /* l1d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 17873 }, /* l1d-store-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 17777 }, /* l1d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18361 }, /* l1d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18748 }, /* l1d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18946 }, /* l1d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 18847 }, /* l1d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 18652 }, /* l1d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18550 }, /* l1d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 18453 }, /* l1d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 19043 }, /* l1d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 19426 }, /* l1d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 19622 }, /* l1d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 19524 }, /* l1d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-cache-config=0x10100\000\00010\000\000\000\000\000 */ -{ 19331 }, /* l1d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 19230 }, /* l1d-write-reference\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 19134 }, /* l1d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-config=0x100\000\00010\000\000\000\000\000 */ -{ 43344 }, /* l1i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 48978 }, /* l1i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43431 }, /* l1i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43818 }, /* l1i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44020 }, /* l1i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 43917 }, /* l1i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 43722 }, /* l1i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43620 }, /* l1i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 43523 }, /* l1i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44121 }, /* l1i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44512 }, /* l1i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44716 }, /* l1i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 44612 }, /* l1i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 44415 }, /* l1i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44312 }, /* l1i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44214 }, /* l1i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 49170 }, /* l1i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 49072 }, /* l1i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 48887 }, /* l1i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 45508 }, /* l1i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 45943 }, /* l1i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46165 }, /* l1i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 46054 }, /* l1i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 45835 }, /* l1i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 45721 }, /* l1i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 45612 }, /* l1i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46274 }, /* l1i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46717 }, /* l1i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46943 }, /* l1i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 46830 }, /* l1i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 46607 }, /* l1i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46491 }, /* l1i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 46380 }, /* l1i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 44818 }, /* l1i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 45205 }, /* l1i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 45407 }, /* l1i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 45304 }, /* l1i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-cache-config=0x10001\000\00010\000\000\000\000\000 */ -{ 45109 }, /* l1i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 45007 }, /* l1i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 44910 }, /* l1i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 48790 }, /* l1i-reference\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 48698 }, /* l1i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-config=1\000\00010\000\000\000\000\000 */ -{ 47876 }, /* l1i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 48343 }, /* l1i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 48581 }, /* l1i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 48462 }, /* l1i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 48227 }, /* l1i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 48105 }, /* l1i-speculative-load-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47988 }, /* l1i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47054 }, /* l1i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47521 }, /* l1i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47759 }, /* l1i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 47640 }, /* l1i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetch misses\000legacy-cache-config=0x10201\000\00010\000\000\000\000\000 */ -{ 47405 }, /* l1i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47283 }, /* l1i-speculative-read-reference\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 47166 }, /* l1i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch accesses\000legacy-cache-config=0x201\000\00010\000\000\000\000\000 */ -{ 63212 }, /* l2\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 71765 }, /* l2-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63309 }, /* l2-load\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63736 }, /* l2-load-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63958 }, /* l2-load-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 63845 }, /* l2-load-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 63630 }, /* l2-load-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63518 }, /* l2-load-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63411 }, /* l2-load-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64069 }, /* l2-loads\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64500 }, /* l2-loads-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64724 }, /* l2-loads-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 64610 }, /* l2-loads-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 64393 }, /* l2-loads-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64280 }, /* l2-loads-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64172 }, /* l2-loads-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 71977 }, /* l2-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 71869 }, /* l2-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 71664 }, /* l2-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 67985 }, /* l2-prefetch\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68460 }, /* l2-prefetch-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68702 }, /* l2-prefetch-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 68581 }, /* l2-prefetch-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 68342 }, /* l2-prefetch-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68218 }, /* l2-prefetch-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68099 }, /* l2-prefetch-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68821 }, /* l2-prefetches\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69304 }, /* l2-prefetches-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69550 }, /* l2-prefetches-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 69427 }, /* l2-prefetches-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 69184 }, /* l2-prefetches-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69058 }, /* l2-prefetches-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 68937 }, /* l2-prefetches-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 64836 }, /* l2-read\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 65263 }, /* l2-read-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 65485 }, /* l2-read-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 65372 }, /* l2-read-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 65157 }, /* l2-read-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 65045 }, /* l2-read-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 64938 }, /* l2-read-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 71557 }, /* l2-reference\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 71455 }, /* l2-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 70563 }, /* l2-speculative-load\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 71070 }, /* l2-speculative-load-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 71328 }, /* l2-speculative-load-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 71199 }, /* l2-speculative-load-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 70944 }, /* l2-speculative-load-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 70812 }, /* l2-speculative-load-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 70685 }, /* l2-speculative-load-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69671 }, /* l2-speculative-read\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 70178 }, /* l2-speculative-read-access\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 70436 }, /* l2-speculative-read-miss\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 70307 }, /* l2-speculative-read-misses\000legacy cache\000Level 2 (or higher) last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 70052 }, /* l2-speculative-read-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69920 }, /* l2-speculative-read-reference\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 69793 }, /* l2-speculative-read-refs\000legacy cache\000Level 2 (or higher) last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 65596 }, /* l2-store\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66047 }, /* l2-store-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66277 }, /* l2-store-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 66162 }, /* l2-store-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 65935 }, /* l2-store-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 65817 }, /* l2-store-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 65704 }, /* l2-store-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66390 }, /* l2-stores\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66845 }, /* l2-stores-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67077 }, /* l2-stores-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 66961 }, /* l2-stores-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 66732 }, /* l2-stores-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66613 }, /* l2-stores-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 66499 }, /* l2-stores-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67191 }, /* l2-write\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67642 }, /* l2-write-access\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67872 }, /* l2-write-miss\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 67757 }, /* l2-write-misses\000legacy cache\000Level 2 (or higher) last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 67530 }, /* l2-write-ops\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67412 }, /* l2-write-reference\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 67299 }, /* l2-write-refs\000legacy cache\000Level 2 (or higher) last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 55804 }, /* llc\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 62951 }, /* llc-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 55882 }, /* llc-load\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56233 }, /* llc-load-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56417 }, /* llc-load-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 56323 }, /* llc-load-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00000\000\000\000\000\000 */ -{ 56146 }, /* llc-load-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56053 }, /* llc-load-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 55965 }, /* llc-load-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56509 }, /* llc-loads\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00000\000\000\000\000\000 */ -{ 56864 }, /* llc-loads-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 57050 }, /* llc-loads-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 56955 }, /* llc-loads-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 56776 }, /* llc-loads-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56682 }, /* llc-loads-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 56593 }, /* llc-loads-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 63125 }, /* llc-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 63036 }, /* llc-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 62869 }, /* llc-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 59760 }, /* llc-prefetch\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 60159 }, /* llc-prefetch-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 60363 }, /* llc-prefetch-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 60261 }, /* llc-prefetch-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00000\000\000\000\000\000 */ -{ 60060 }, /* llc-prefetch-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 59955 }, /* llc-prefetch-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 59855 }, /* llc-prefetch-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 60463 }, /* llc-prefetches\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00000\000\000\000\000\000 */ -{ 60870 }, /* llc-prefetches-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61078 }, /* llc-prefetches-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 60974 }, /* llc-prefetches-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 60769 }, /* llc-prefetches-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 60662 }, /* llc-prefetches-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 60560 }, /* llc-prefetches-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 57143 }, /* llc-read\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 57494 }, /* llc-read-access\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 57678 }, /* llc-read-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 57584 }, /* llc-read-misses\000legacy cache\000Last level cache read misses\000legacy-cache-config=0x10002\000\00010\000\000\000\000\000 */ -{ 57407 }, /* llc-read-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 57314 }, /* llc-read-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 57226 }, /* llc-read-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 62781 }, /* llc-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 62698 }, /* llc-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\000\00010\000\000\000\000\000 */ -{ 61939 }, /* llc-speculative-load\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 62370 }, /* llc-speculative-load-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 62590 }, /* llc-speculative-load-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 62480 }, /* llc-speculative-load-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 62263 }, /* llc-speculative-load-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 62150 }, /* llc-speculative-load-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 62042 }, /* llc-speculative-load-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61180 }, /* llc-speculative-read\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61611 }, /* llc-speculative-read-access\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61831 }, /* llc-speculative-read-miss\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 61721 }, /* llc-speculative-read-misses\000legacy cache\000Last level cache prefetch misses\000legacy-cache-config=0x10202\000\00010\000\000\000\000\000 */ -{ 61504 }, /* llc-speculative-read-ops\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61391 }, /* llc-speculative-read-reference\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 61283 }, /* llc-speculative-read-refs\000legacy cache\000Last level cache prefetch accesses\000legacy-cache-config=0x202\000\00010\000\000\000\000\000 */ -{ 57770 }, /* llc-store\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 58145 }, /* llc-store-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 58337 }, /* llc-store-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 58241 }, /* llc-store-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00000\000\000\000\000\000 */ -{ 58052 }, /* llc-store-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 57953 }, /* llc-store-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 57859 }, /* llc-store-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 58431 }, /* llc-stores\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00000\000\000\000\000\000 */ -{ 58810 }, /* llc-stores-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59004 }, /* llc-stores-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 58907 }, /* llc-stores-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 58716 }, /* llc-stores-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 58616 }, /* llc-stores-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 58521 }, /* llc-stores-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59099 }, /* llc-write\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59474 }, /* llc-write-access\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59666 }, /* llc-write-miss\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 59570 }, /* llc-write-misses\000legacy cache\000Last level cache write misses\000legacy-cache-config=0x10102\000\00010\000\000\000\000\000 */ -{ 59381 }, /* llc-write-ops\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59282 }, /* llc-write-reference\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 59188 }, /* llc-write-refs\000legacy cache\000Last level cache write accesses\000legacy-cache-config=0x102\000\00010\000\000\000\000\000 */ -{ 114128 }, /* node\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 121053 }, /* node-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114203 }, /* node-load\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114542 }, /* node-load-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114720 }, /* node-load-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 114629 }, /* node-load-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00000\000\000\000\000\000 */ -{ 114458 }, /* node-load-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114368 }, /* node-load-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114283 }, /* node-load-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114809 }, /* node-loads\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00000\000\000\000\000\000 */ -{ 115152 }, /* node-loads-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 115332 }, /* node-loads-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 115240 }, /* node-loads-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 115067 }, /* node-loads-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114976 }, /* node-loads-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 114890 }, /* node-loads-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 121221 }, /* node-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 121135 }, /* node-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 120974 }, /* node-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 117955 }, /* node-prefetch\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118342 }, /* node-prefetch-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118540 }, /* node-prefetch-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 118441 }, /* node-prefetch-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00000\000\000\000\000\000 */ -{ 118246 }, /* node-prefetch-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118144 }, /* node-prefetch-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118047 }, /* node-prefetch-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118637 }, /* node-prefetches\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00000\000\000\000\000\000 */ -{ 119032 }, /* node-prefetches-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119234 }, /* node-prefetches-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 119133 }, /* node-prefetches-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 118934 }, /* node-prefetches-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118830 }, /* node-prefetches-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 118731 }, /* node-prefetches-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 115422 }, /* node-read\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 115761 }, /* node-read-access\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 115939 }, /* node-read-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 115848 }, /* node-read-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0x10006\000\00010\000\000\000\000\000 */ -{ 115677 }, /* node-read-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 115587 }, /* node-read-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 115502 }, /* node-read-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 120889 }, /* node-reference\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 120809 }, /* node-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\00010\000\000\000\000\000 */ -{ 120071 }, /* node-speculative-load\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 120490 }, /* node-speculative-load-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 120704 }, /* node-speculative-load-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 120597 }, /* node-speculative-load-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 120386 }, /* node-speculative-load-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 120276 }, /* node-speculative-load-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 120171 }, /* node-speculative-load-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119333 }, /* node-speculative-read\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119752 }, /* node-speculative-read-access\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119966 }, /* node-speculative-read-miss\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 119859 }, /* node-speculative-read-misses\000legacy cache\000Local memory prefetch misses\000legacy-cache-config=0x10206\000\00010\000\000\000\000\000 */ -{ 119648 }, /* node-speculative-read-ops\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119538 }, /* node-speculative-read-reference\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 119433 }, /* node-speculative-read-refs\000legacy cache\000Local memory prefetch accesses\000legacy-cache-config=0x206\000\00010\000\000\000\000\000 */ -{ 116028 }, /* node-store\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116391 }, /* node-store-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116577 }, /* node-store-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000 */ -{ 116484 }, /* node-store-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00000\000\000\000\000\000 */ -{ 116301 }, /* node-store-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116205 }, /* node-store-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116114 }, /* node-store-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116668 }, /* node-stores\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00000\000\000\000\000\000 */ -{ 117035 }, /* node-stores-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117223 }, /* node-stores-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000 */ -{ 117129 }, /* node-stores-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000 */ -{ 116944 }, /* node-stores-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116847 }, /* node-stores-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 116755 }, /* node-stores-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117315 }, /* node-write\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117678 }, /* node-write-access\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117864 }, /* node-write-miss\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000 */ -{ 117771 }, /* node-write-misses\000legacy cache\000Local memory write misses\000legacy-cache-config=0x10106\000\00010\000\000\000\000\000 */ -{ 117588 }, /* node-write-ops\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117492 }, /* node-write-reference\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 117401 }, /* node-write-refs\000legacy cache\000Local memory write accesses\000legacy-cache-config=0x106\000\00010\000\000\000\000\000 */ -{ 123400 }, /* ref-cycles\000legacy hardware\000Total cycles; not affected by CPU frequency scaling\000legacy-hardware-config=9\000\00000\000\000\000\000\000 */ -{ 123094 }, /* stalled-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This event is an alias of idle-cycles-backend]\000legacy-hardware-config=8\000\00000\000\000\000\000\000 */ -{ 122795 }, /* stalled-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This event is an alias of idle-cycles-frontend]\000legacy-hardware-config=7\000\00000\000\000\000\000\000 */ + /* bpc\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-conf... */ + { 111480 }, + /* bpc-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cac... */ + { 113849 }, + /* bpc-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 111564 }, + /* bpc-load-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 111939 }, + /* bpc-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 112135 }, + /* bpc-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 112035 }, + /* bpc-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 111846 }, + /* bpc-load-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 111747 }, + /* bpc-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 111653 }, + /* bpc-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cach... */ + { 112233 }, + /* bpc-loads-access\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 112612 }, + /* bpc-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-c... */ + { 112810 }, + /* bpc-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy... */ + { 112709 }, + /* bpc-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 112518 }, + /* bpc-loads-reference\000legacy cache\000Branch prediction unit read accesses\000l... */ + { 112418 }, + /* bpc-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy... */ + { 112323 }, + /* bpc-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-c... */ + { 114035 }, + /* bpc-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache... */ + { 113940 }, + /* bpc-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-... */ + { 113761 }, + /* bpc-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 112909 }, + /* bpc-read-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 113284 }, + /* bpc-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 113480 }, + /* bpc-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 113380 }, + /* bpc-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 113191 }, + /* bpc-read-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 113092 }, + /* bpc-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 112998 }, + /* bpc-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 113667 }, + /* bpc-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 113578 }, + /* bpu\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-conf... */ + { 106184 }, + /* bpu-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cac... */ + { 108553 }, + /* bpu-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 106268 }, + /* bpu-load-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 106643 }, + /* bpu-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 106839 }, + /* bpu-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 106739 }, + /* bpu-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 106550 }, + /* bpu-load-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 106451 }, + /* bpu-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 106357 }, + /* bpu-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cach... */ + { 106937 }, + /* bpu-loads-access\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 107316 }, + /* bpu-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-c... */ + { 107514 }, + /* bpu-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy... */ + { 107413 }, + /* bpu-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 107222 }, + /* bpu-loads-reference\000legacy cache\000Branch prediction unit read accesses\000l... */ + { 107122 }, + /* bpu-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy... */ + { 107027 }, + /* bpu-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-c... */ + { 108739 }, + /* bpu-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache... */ + { 108644 }, + /* bpu-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-... */ + { 108465 }, + /* bpu-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 107613 }, + /* bpu-read-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 107988 }, + /* bpu-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 108184 }, + /* bpu-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 108084 }, + /* bpu-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 107895 }, + /* bpu-read-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 107796 }, + /* bpu-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 107702 }, + /* bpu-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 108371 }, + /* bpu-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 108282 }, + /* branch\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-c... */ + { 100851 }, + /* branch-access\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 103295 }, + /* branch-instructions\000legacy hardware\000Retired branch instructions [This even... */ + { 122452 }, + /* branch-load\000legacy cache\000Branch prediction unit read accesses\000legacy-ca... */ + { 100938 }, + /* branch-load-access\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 101325 }, + /* branch-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy... */ + { 101527 }, + /* branch-load-misses\000legacy cache\000Branch prediction unit read misses\000lega... */ + { 101424 }, + /* branch-load-ops\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 101229 }, + /* branch-load-reference\000legacy cache\000Branch prediction unit read accesses\00... */ + { 101127 }, + /* branch-load-refs\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 101030 }, + /* branch-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 101628 }, + /* branch-loads-access\000legacy cache\000Branch prediction unit read accesses\000l... */ + { 102019 }, + /* branch-loads-miss\000legacy cache\000Branch prediction unit read misses\000legac... */ + { 102223 }, + /* branch-loads-misses\000legacy cache\000Branch prediction unit read misses\000leg... */ + { 102119 }, + /* branch-loads-ops\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 101922 }, + /* branch-loads-reference\000legacy cache\000Branch prediction unit read accesses\0... */ + { 101819 }, + /* branch-loads-refs\000legacy cache\000Branch prediction unit read accesses\000leg... */ + { 101721 }, + /* branch-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cach... */ + { 103389 }, + /* branch-misses\000legacy hardware\000Mispredicted branch instructions\000legacy-h... */ + { 122586 }, + /* branch-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cac... */ + { 103204 }, + /* branch-read\000legacy cache\000Branch prediction unit read accesses\000legacy-ca... */ + { 102325 }, + /* branch-read-access\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 102712 }, + /* branch-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy... */ + { 102914 }, + /* branch-read-misses\000legacy cache\000Branch prediction unit read misses\000lega... */ + { 102811 }, + /* branch-read-ops\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 102616 }, + /* branch-read-reference\000legacy cache\000Branch prediction unit read accesses\00... */ + { 102514 }, + /* branch-read-refs\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 102417 }, + /* branch-reference\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 103107 }, + /* branch-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-ca... */ + { 103015 }, + /* branches\000legacy hardware\000Retired branch instructions [This event is an ali... */ + { 122318 }, + /* branches-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 105890 }, + /* branches-load\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 103485 }, + /* branches-load-access\000legacy cache\000Branch prediction unit read accesses\000... */ + { 103880 }, + /* branches-load-miss\000legacy cache\000Branch prediction unit read misses\000lega... */ + { 104086 }, + /* branches-load-misses\000legacy cache\000Branch prediction unit read misses\000le... */ + { 103981 }, + /* branches-load-ops\000legacy cache\000Branch prediction unit read accesses\000leg... */ + { 103782 }, + /* branches-load-reference\000legacy cache\000Branch prediction unit read accesses\... */ + { 103678 }, + /* branches-load-refs\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 103579 }, + /* branches-loads\000legacy cache\000Branch prediction unit read accesses\000legacy... */ + { 104189 }, + /* branches-loads-access\000legacy cache\000Branch prediction unit read accesses\00... */ + { 104588 }, + /* branches-loads-miss\000legacy cache\000Branch prediction unit read misses\000leg... */ + { 104796 }, + /* branches-loads-misses\000legacy cache\000Branch prediction unit read misses\000l... */ + { 104690 }, + /* branches-loads-ops\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 104489 }, + /* branches-loads-reference\000legacy cache\000Branch prediction unit read accesses... */ + { 104384 }, + /* branches-loads-refs\000legacy cache\000Branch prediction unit read accesses\000l... */ + { 104284 }, + /* branches-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 106086 }, + /* branches-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 105986 }, + /* branches-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 105797 }, + /* branches-read\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 104900 }, + /* branches-read-access\000legacy cache\000Branch prediction unit read accesses\000... */ + { 105295 }, + /* branches-read-miss\000legacy cache\000Branch prediction unit read misses\000lega... */ + { 105501 }, + /* branches-read-misses\000legacy cache\000Branch prediction unit read misses\000le... */ + { 105396 }, + /* branches-read-ops\000legacy cache\000Branch prediction unit read accesses\000leg... */ + { 105197 }, + /* branches-read-reference\000legacy cache\000Branch prediction unit read accesses\... */ + { 105093 }, + /* branches-read-refs\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 104994 }, + /* branches-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 105698 }, + /* branches-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 105604 }, + /* btb\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-conf... */ + { 108832 }, + /* btb-access\000legacy cache\000Branch prediction unit read accesses\000legacy-cac... */ + { 111201 }, + /* btb-load\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 108916 }, + /* btb-load-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 109291 }, + /* btb-load-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 109487 }, + /* btb-load-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 109387 }, + /* btb-load-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 109198 }, + /* btb-load-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 109099 }, + /* btb-load-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 109005 }, + /* btb-loads\000legacy cache\000Branch prediction unit read accesses\000legacy-cach... */ + { 109585 }, + /* btb-loads-access\000legacy cache\000Branch prediction unit read accesses\000lega... */ + { 109964 }, + /* btb-loads-miss\000legacy cache\000Branch prediction unit read misses\000legacy-c... */ + { 110162 }, + /* btb-loads-misses\000legacy cache\000Branch prediction unit read misses\000legacy... */ + { 110061 }, + /* btb-loads-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 109870 }, + /* btb-loads-reference\000legacy cache\000Branch prediction unit read accesses\000l... */ + { 109770 }, + /* btb-loads-refs\000legacy cache\000Branch prediction unit read accesses\000legacy... */ + { 109675 }, + /* btb-miss\000legacy cache\000Branch prediction unit read misses\000legacy-cache-c... */ + { 111387 }, + /* btb-misses\000legacy cache\000Branch prediction unit read misses\000legacy-cache... */ + { 111292 }, + /* btb-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-cache-... */ + { 111113 }, + /* btb-read\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 110261 }, + /* btb-read-access\000legacy cache\000Branch prediction unit read accesses\000legac... */ + { 110636 }, + /* btb-read-miss\000legacy cache\000Branch prediction unit read misses\000legacy-ca... */ + { 110832 }, + /* btb-read-misses\000legacy cache\000Branch prediction unit read misses\000legacy-... */ + { 110732 }, + /* btb-read-ops\000legacy cache\000Branch prediction unit read accesses\000legacy-c... */ + { 110543 }, + /* btb-read-reference\000legacy cache\000Branch prediction unit read accesses\000le... */ + { 110444 }, + /* btb-read-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 110350 }, + /* btb-reference\000legacy cache\000Branch prediction unit read accesses\000legacy-... */ + { 111019 }, + /* btb-refs\000legacy cache\000Branch prediction unit read accesses\000legacy-cache... */ + { 110930 }, + /* bus-cycles\000legacy hardware\000Bus cycles, which can be different from total c... */ + { 122682 }, + /* cache-misses\000legacy hardware\000Cache misses. Usually this indicates Last Lev... */ + { 122075 }, + /* cache-references\000legacy hardware\000Cache accesses. Usually this indicates La... */ + { 121805 }, + /* cpu-cycles\000legacy hardware\000Total cycles. Be wary of what happens during CP... */ + { 121305 }, + /* cycles\000legacy hardware\000Total cycles. Be wary of what happens during CPU fr... */ + { 121467 }, + /* d-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\000... */ + { 78952 }, + /* d-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\... */ + { 85655 }, + /* d-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\00... */ + { 79024 }, + /* d-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-conf... */ + { 79351 }, + /* d-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0... */ + { 79523 }, + /* d-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config... */ + { 79435 }, + /* d-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 79270 }, + /* d-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-c... */ + { 79183 }, + /* d-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 79101 }, + /* d-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\0... */ + { 79609 }, + /* d-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-con... */ + { 79940 }, + /* d-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=... */ + { 80114 }, + /* d-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-confi... */ + { 80025 }, + /* d-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 79858 }, + /* d-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-... */ + { 79770 }, + /* d-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-confi... */ + { 79687 }, + /* d-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x1000... */ + { 85817 }, + /* d-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10... */ + { 85734 }, + /* d-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000... */ + { 85579 }, + /* d-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-con... */ + { 82650 }, + /* d-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-ca... */ + { 83025 }, + /* d-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-... */ + { 83217 }, + /* d-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cach... */ + { 83121 }, + /* d-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache... */ + { 82932 }, + /* d-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy... */ + { 82833 }, + /* d-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cach... */ + { 82739 }, + /* d-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-c... */ + { 83311 }, + /* d-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-... */ + { 83694 }, + /* d-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cach... */ + { 83890 }, + /* d-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-ca... */ + { 83792 }, + /* d-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cac... */ + { 83599 }, + /* d-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 83498 }, + /* d-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-ca... */ + { 83402 }, + /* d-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\00... */ + { 80201 }, + /* d-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-conf... */ + { 80528 }, + /* d-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0... */ + { 80700 }, + /* d-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config... */ + { 80612 }, + /* d-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 80447 }, + /* d-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-c... */ + { 80360 }, + /* d-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 80278 }, + /* d-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 85497 }, + /* d-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\00... */ + { 85420 }, + /* d-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-c... */ + { 84703 }, + /* d-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000l... */ + { 85110 }, + /* d-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legac... */ + { 85318 }, + /* d-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000leg... */ + { 85214 }, + /* d-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 85009 }, + /* d-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\0... */ + { 84902 }, + /* d-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000leg... */ + { 84800 }, + /* d-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-c... */ + { 83986 }, + /* d-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000l... */ + { 84393 }, + /* d-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legac... */ + { 84601 }, + /* d-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000leg... */ + { 84497 }, + /* d-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 84292 }, + /* d-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\0... */ + { 84185 }, + /* d-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000leg... */ + { 84083 }, + /* d-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x... */ + { 80786 }, + /* d-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-co... */ + { 81137 }, + /* d-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config... */ + { 81317 }, + /* d-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-conf... */ + { 81227 }, + /* d-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 81050 }, + /* d-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache... */ + { 80957 }, + /* d-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-conf... */ + { 80869 }, + /* d-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0... */ + { 81405 }, + /* d-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-c... */ + { 81760 }, + /* d-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-confi... */ + { 81942 }, + /* d-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-con... */ + { 81851 }, + /* d-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-conf... */ + { 81672 }, + /* d-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cach... */ + { 81578 }, + /* d-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-con... */ + { 81489 }, + /* d-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x... */ + { 82031 }, + /* d-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-co... */ + { 82382 }, + /* d-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config... */ + { 82562 }, + /* d-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-conf... */ + { 82472 }, + /* d-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 82295 }, + /* d-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache... */ + { 82202 }, + /* d-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-conf... */ + { 82114 }, + /* data-tlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\... */ + { 85898 }, + /* data-tlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 92823 }, + /* data-tlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3... */ + { 85973 }, + /* data-tlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-c... */ + { 86312 }, + /* data-tlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-confi... */ + { 86490 }, + /* data-tlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-con... */ + { 86399 }, + /* data-tlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-conf... */ + { 86228 }, + /* data-tlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cach... */ + { 86138 }, + /* data-tlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-con... */ + { 86053 }, + /* data-tlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 86579 }, + /* data-tlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-... */ + { 86922 }, + /* data-tlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-conf... */ + { 87102 }, + /* data-tlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-co... */ + { 87010 }, + /* data-tlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-con... */ + { 86837 }, + /* data-tlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cac... */ + { 86746 }, + /* data-tlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-co... */ + { 86660 }, + /* data-tlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x1... */ + { 92991 }, + /* data-tlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0... */ + { 92905 }, + /* data-tlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\... */ + { 92744 }, + /* data-tlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-... */ + { 89725 }, + /* data-tlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy... */ + { 90112 }, + /* data-tlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cac... */ + { 90310 }, + /* data-tlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-c... */ + { 90211 }, + /* data-tlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-ca... */ + { 90016 }, + /* data-tlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000leg... */ + { 89914 }, + /* data-tlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-c... */ + { 89817 }, + /* data-tlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cach... */ + { 90407 }, + /* data-tlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 90802 }, + /* data-tlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-c... */ + { 91004 }, + /* data-tlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy... */ + { 90903 }, + /* data-tlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-... */ + { 90704 }, + /* data-tlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000l... */ + { 90600 }, + /* data-tlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy... */ + { 90501 }, + /* data-tlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3... */ + { 87192 }, + /* data-tlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-c... */ + { 87531 }, + /* data-tlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-confi... */ + { 87709 }, + /* data-tlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-con... */ + { 87618 }, + /* data-tlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-conf... */ + { 87447 }, + /* data-tlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cach... */ + { 87357 }, + /* data-tlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-con... */ + { 87272 }, + /* data-tlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-con... */ + { 92659 }, + /* data-tlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3... */ + { 92579 }, + /* data-tlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legac... */ + { 91841 }, + /* data-tlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\0... */ + { 92260 }, + /* data-tlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000le... */ + { 92474 }, + /* data-tlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000... */ + { 92367 }, + /* data-tlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000l... */ + { 92156 }, + /* data-tlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesse... */ + { 92046 }, + /* data-tlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000... */ + { 91941 }, + /* data-tlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legac... */ + { 91103 }, + /* data-tlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\0... */ + { 91522 }, + /* data-tlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000le... */ + { 91736 }, + /* data-tlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000... */ + { 91629 }, + /* data-tlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000l... */ + { 91418 }, + /* data-tlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesse... */ + { 91308 }, + /* data-tlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000... */ + { 91203 }, + /* data-tlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config... */ + { 87798 }, + /* data-tlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache... */ + { 88161 }, + /* data-tlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-con... */ + { 88347 }, + /* data-tlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-c... */ + { 88254 }, + /* data-tlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-co... */ + { 88071 }, + /* data-tlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-ca... */ + { 87975 }, + /* data-tlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-c... */ + { 87884 }, + /* data-tlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 88438 }, + /* data-tlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cach... */ + { 88805 }, + /* data-tlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-co... */ + { 88993 }, + /* data-tlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-... */ + { 88899 }, + /* data-tlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-c... */ + { 88714 }, + /* data-tlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-c... */ + { 88617 }, + /* data-tlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-... */ + { 88525 }, + /* data-tlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config... */ + { 89085 }, + /* data-tlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache... */ + { 89448 }, + /* data-tlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-con... */ + { 89634 }, + /* data-tlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-c... */ + { 89541 }, + /* data-tlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-co... */ + { 89358 }, + /* data-tlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-ca... */ + { 89262 }, + /* data-tlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-c... */ + { 89171 }, + /* dtlb\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\0001... */ + { 72083 }, + /* dtlb-access\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\0... */ + { 78712 }, + /* dtlb-load\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000... */ + { 72154 }, + /* dtlb-load-access\000legacy cache\000Data TLB read accesses\000legacy-cache-confi... */ + { 72477 }, + /* dtlb-load-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x... */ + { 72647 }, + /* dtlb-load-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=... */ + { 72560 }, + /* dtlb-load-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3... */ + { 72397 }, + /* dtlb-load-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-co... */ + { 72311 }, + /* dtlb-load-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 72230 }, + /* dtlb-loads\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\00... */ + { 72732 }, + /* dtlb-loads-access\000legacy cache\000Data TLB read accesses\000legacy-cache-conf... */ + { 73059 }, + /* dtlb-loads-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0... */ + { 73231 }, + /* dtlb-loads-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config... */ + { 73143 }, + /* dtlb-loads-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 72978 }, + /* dtlb-loads-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-c... */ + { 72891 }, + /* dtlb-loads-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config... */ + { 72809 }, + /* dtlb-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x10003... */ + { 78872 }, + /* dtlb-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x100... */ + { 78790 }, + /* dtlb-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000\... */ + { 78637 }, + /* dtlb-prefetch\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-conf... */ + { 75738 }, + /* dtlb-prefetch-access\000legacy cache\000Data TLB prefetch accesses\000legacy-cac... */ + { 76109 }, + /* dtlb-prefetch-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache-c... */ + { 76299 }, + /* dtlb-prefetch-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cache... */ + { 76204 }, + /* dtlb-prefetch-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-... */ + { 76017 }, + /* dtlb-prefetch-reference\000legacy cache\000Data TLB prefetch accesses\000legacy-... */ + { 75919 }, + /* dtlb-prefetch-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cache... */ + { 75826 }, + /* dtlb-prefetches\000legacy cache\000Data TLB prefetch accesses\000legacy-cache-co... */ + { 76392 }, + /* dtlb-prefetches-access\000legacy cache\000Data TLB prefetch accesses\000legacy-c... */ + { 76771 }, + /* dtlb-prefetches-miss\000legacy cache\000Data TLB prefetch misses\000legacy-cache... */ + { 76965 }, + /* dtlb-prefetches-misses\000legacy cache\000Data TLB prefetch misses\000legacy-cac... */ + { 76868 }, + /* dtlb-prefetches-ops\000legacy cache\000Data TLB prefetch accesses\000legacy-cach... */ + { 76677 }, + /* dtlb-prefetches-reference\000legacy cache\000Data TLB prefetch accesses\000legac... */ + { 76577 }, + /* dtlb-prefetches-refs\000legacy cache\000Data TLB prefetch accesses\000legacy-cac... */ + { 76482 }, + /* dtlb-read\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000... */ + { 73317 }, + /* dtlb-read-access\000legacy cache\000Data TLB read accesses\000legacy-cache-confi... */ + { 73640 }, + /* dtlb-read-miss\000legacy cache\000Data TLB read misses\000legacy-cache-config=0x... */ + { 73810 }, + /* dtlb-read-misses\000legacy cache\000Data TLB read misses\000legacy-cache-config=... */ + { 73723 }, + /* dtlb-read-ops\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3... */ + { 73560 }, + /* dtlb-read-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-co... */ + { 73474 }, + /* dtlb-read-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 73393 }, + /* dtlb-reference\000legacy cache\000Data TLB read accesses\000legacy-cache-config=... */ + { 78556 }, + /* dtlb-refs\000legacy cache\000Data TLB read accesses\000legacy-cache-config=3\000... */ + { 78480 }, + /* dtlb-speculative-load\000legacy cache\000Data TLB prefetch accesses\000legacy-ca... */ + { 77770 }, + /* dtlb-speculative-load-access\000legacy cache\000Data TLB prefetch accesses\000le... */ + { 78173 }, + /* dtlb-speculative-load-miss\000legacy cache\000Data TLB prefetch misses\000legacy... */ + { 78379 }, + /* dtlb-speculative-load-misses\000legacy cache\000Data TLB prefetch misses\000lega... */ + { 78276 }, + /* dtlb-speculative-load-ops\000legacy cache\000Data TLB prefetch accesses\000legac... */ + { 78073 }, + /* dtlb-speculative-load-reference\000legacy cache\000Data TLB prefetch accesses\00... */ + { 77967 }, + /* dtlb-speculative-load-refs\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 77866 }, + /* dtlb-speculative-read\000legacy cache\000Data TLB prefetch accesses\000legacy-ca... */ + { 77060 }, + /* dtlb-speculative-read-access\000legacy cache\000Data TLB prefetch accesses\000le... */ + { 77463 }, + /* dtlb-speculative-read-miss\000legacy cache\000Data TLB prefetch misses\000legacy... */ + { 77669 }, + /* dtlb-speculative-read-misses\000legacy cache\000Data TLB prefetch misses\000lega... */ + { 77566 }, + /* dtlb-speculative-read-ops\000legacy cache\000Data TLB prefetch accesses\000legac... */ + { 77363 }, + /* dtlb-speculative-read-reference\000legacy cache\000Data TLB prefetch accesses\00... */ + { 77257 }, + /* dtlb-speculative-read-refs\000legacy cache\000Data TLB prefetch accesses\000lega... */ + { 77156 }, + /* dtlb-store\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x1... */ + { 73895 }, + /* dtlb-store-access\000legacy cache\000Data TLB write accesses\000legacy-cache-con... */ + { 74242 }, + /* dtlb-store-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=... */ + { 74420 }, + /* dtlb-store-misses\000legacy cache\000Data TLB write misses\000legacy-cache-confi... */ + { 74331 }, + /* dtlb-store-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config... */ + { 74156 }, + /* dtlb-store-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-... */ + { 74064 }, + /* dtlb-store-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 73977 }, + /* dtlb-stores\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x... */ + { 74507 }, + /* dtlb-stores-access\000legacy cache\000Data TLB write accesses\000legacy-cache-co... */ + { 74858 }, + /* dtlb-stores-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config... */ + { 75038 }, + /* dtlb-stores-misses\000legacy cache\000Data TLB write misses\000legacy-cache-conf... */ + { 74948 }, + /* dtlb-stores-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 74771 }, + /* dtlb-stores-reference\000legacy cache\000Data TLB write accesses\000legacy-cache... */ + { 74678 }, + /* dtlb-stores-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-conf... */ + { 74590 }, + /* dtlb-write\000legacy cache\000Data TLB write accesses\000legacy-cache-config=0x1... */ + { 75126 }, + /* dtlb-write-access\000legacy cache\000Data TLB write accesses\000legacy-cache-con... */ + { 75473 }, + /* dtlb-write-miss\000legacy cache\000Data TLB write misses\000legacy-cache-config=... */ + { 75651 }, + /* dtlb-write-misses\000legacy cache\000Data TLB write misses\000legacy-cache-confi... */ + { 75562 }, + /* dtlb-write-ops\000legacy cache\000Data TLB write accesses\000legacy-cache-config... */ + { 75387 }, + /* dtlb-write-reference\000legacy cache\000Data TLB write accesses\000legacy-cache-... */ + { 75295 }, + /* dtlb-write-refs\000legacy cache\000Data TLB write accesses\000legacy-cache-confi... */ + { 75208 }, + /* i-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\... */ + { 95555 }, + /* i-tlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-co... */ + { 97799 }, + /* i-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-conf... */ + { 95634 }, + /* i-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cac... */ + { 95989 }, + /* i-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-c... */ + { 96175 }, + /* i-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache... */ + { 96080 }, + /* i-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 95901 }, + /* i-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 95807 }, + /* i-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 95718 }, + /* i-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-con... */ + { 96268 }, + /* i-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-ca... */ + { 96627 }, + /* i-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-... */ + { 96815 }, + /* i-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cach... */ + { 96719 }, + /* i-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 96538 }, + /* i-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy... */ + { 96443 }, + /* i-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cach... */ + { 96353 }, + /* i-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config... */ + { 97975 }, + /* i-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-conf... */ + { 97885 }, + /* i-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-confi... */ + { 97716 }, + /* i-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-conf... */ + { 96909 }, + /* i-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cac... */ + { 97264 }, + /* i-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-c... */ + { 97450 }, + /* i-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache... */ + { 97355 }, + /* i-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 97176 }, + /* i-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 97082 }, + /* i-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 96993 }, + /* i-tlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 97627 }, + /* i-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-conf... */ + { 97543 }, + /* idle-cycles-backend\000legacy hardware\000Stalled cycles during retirement [This... */ + { 123247 }, + /* idle-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This eve... */ + { 122945 }, + /* instruction-tlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 98063 }, + /* instruction-tlb-access\000legacy cache\000Instruction TLB read accesses\000legac... */ + { 100557 }, + /* instruction-tlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 98152 }, + /* instruction-tlb-load-access\000legacy cache\000Instruction TLB read accesses\000... */ + { 98547 }, + /* instruction-tlb-load-miss\000legacy cache\000Instruction TLB read misses\000lega... */ + { 98753 }, + /* instruction-tlb-load-misses\000legacy cache\000Instruction TLB read misses\000le... */ + { 98648 }, + /* instruction-tlb-load-ops\000legacy cache\000Instruction TLB read accesses\000leg... */ + { 98449 }, + /* instruction-tlb-load-reference\000legacy cache\000Instruction TLB read accesses\... */ + { 98345 }, + /* instruction-tlb-load-refs\000legacy cache\000Instruction TLB read accesses\000le... */ + { 98246 }, + /* instruction-tlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy... */ + { 98856 }, + /* instruction-tlb-loads-access\000legacy cache\000Instruction TLB read accesses\00... */ + { 99255 }, + /* instruction-tlb-loads-miss\000legacy cache\000Instruction TLB read misses\000leg... */ + { 99463 }, + /* instruction-tlb-loads-misses\000legacy cache\000Instruction TLB read misses\000l... */ + { 99357 }, + /* instruction-tlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000le... */ + { 99156 }, + /* instruction-tlb-loads-reference\000legacy cache\000Instruction TLB read accesses... */ + { 99051 }, + /* instruction-tlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000l... */ + { 98951 }, + /* instruction-tlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-ca... */ + { 100753 }, + /* instruction-tlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-... */ + { 100653 }, + /* instruction-tlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-c... */ + { 100464 }, + /* instruction-tlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 99567 }, + /* instruction-tlb-read-access\000legacy cache\000Instruction TLB read accesses\000... */ + { 99962 }, + /* instruction-tlb-read-miss\000legacy cache\000Instruction TLB read misses\000lega... */ + { 100168 }, + /* instruction-tlb-read-misses\000legacy cache\000Instruction TLB read misses\000le... */ + { 100063 }, + /* instruction-tlb-read-ops\000legacy cache\000Instruction TLB read accesses\000leg... */ + { 99864 }, + /* instruction-tlb-read-reference\000legacy cache\000Instruction TLB read accesses\... */ + { 99760 }, + /* instruction-tlb-read-refs\000legacy cache\000Instruction TLB read accesses\000le... */ + { 99661 }, + /* instruction-tlb-reference\000legacy cache\000Instruction TLB read accesses\000le... */ + { 100365 }, + /* instruction-tlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 100271 }, + /* instructions\000legacy hardware\000Retired instructions. Be careful, these can b... */ + { 121629 }, + /* itlb\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config=4\0... */ + { 93075 }, + /* itlb-access\000legacy cache\000Instruction TLB read accesses\000legacy-cache-con... */ + { 95294 }, + /* itlb-load\000legacy cache\000Instruction TLB read accesses\000legacy-cache-confi... */ + { 93153 }, + /* itlb-load-access\000legacy cache\000Instruction TLB read accesses\000legacy-cach... */ + { 93504 }, + /* itlb-load-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-co... */ + { 93688 }, + /* itlb-load-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-... */ + { 93594 }, + /* itlb-load-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-c... */ + { 93417 }, + /* itlb-load-reference\000legacy cache\000Instruction TLB read accesses\000legacy-c... */ + { 93324 }, + /* itlb-load-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 93236 }, + /* itlb-loads\000legacy cache\000Instruction TLB read accesses\000legacy-cache-conf... */ + { 93780 }, + /* itlb-loads-access\000legacy cache\000Instruction TLB read accesses\000legacy-cac... */ + { 94135 }, + /* itlb-loads-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-c... */ + { 94321 }, + /* itlb-loads-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache... */ + { 94226 }, + /* itlb-loads-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 94047 }, + /* itlb-loads-reference\000legacy cache\000Instruction TLB read accesses\000legacy-... */ + { 93953 }, + /* itlb-loads-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache... */ + { 93864 }, + /* itlb-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-config=... */ + { 95468 }, + /* itlb-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-confi... */ + { 95379 }, + /* itlb-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-config... */ + { 95212 }, + /* itlb-read\000legacy cache\000Instruction TLB read accesses\000legacy-cache-confi... */ + { 94414 }, + /* itlb-read-access\000legacy cache\000Instruction TLB read accesses\000legacy-cach... */ + { 94765 }, + /* itlb-read-miss\000legacy cache\000Instruction TLB read misses\000legacy-cache-co... */ + { 94949 }, + /* itlb-read-misses\000legacy cache\000Instruction TLB read misses\000legacy-cache-... */ + { 94855 }, + /* itlb-read-ops\000legacy cache\000Instruction TLB read accesses\000legacy-cache-c... */ + { 94678 }, + /* itlb-read-reference\000legacy cache\000Instruction TLB read accesses\000legacy-c... */ + { 94585 }, + /* itlb-read-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 94497 }, + /* itlb-reference\000legacy cache\000Instruction TLB read accesses\000legacy-cache-... */ + { 95124 }, + /* itlb-refs\000legacy cache\000Instruction TLB read accesses\000legacy-cache-confi... */ + { 95041 }, + /* l1-d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=... */ + { 8037 }, + /* l1-d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-... */ + { 15406 }, + /* l1-d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-co... */ + { 8118 }, + /* l1-d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 8481 }, + /* l1-d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache... */ + { 8671 }, + /* l1-d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cac... */ + { 8574 }, + /* l1-d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 8391 }, + /* l1-d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 8295 }, + /* l1-d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 8204 }, + /* l1-d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-c... */ + { 8766 }, + /* l1-d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-... */ + { 9133 }, + /* l1-d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cach... */ + { 9325 }, + /* l1-d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-ca... */ + { 9227 }, + /* l1-d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 9042 }, + /* l1-d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000lega... */ + { 8945 }, + /* l1-d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-ca... */ + { 8853 }, + /* l1-d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-conf... */ + { 15586 }, + /* l1-d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-co... */ + { 15494 }, + /* l1-d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-con... */ + { 15321 }, + /* l1-d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-... */ + { 12122 }, + /* l1-d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 12533 }, + /* l1-d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000lega... */ + { 12743 }, + /* l1-d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000le... */ + { 12638 }, + /* l1-d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000leg... */ + { 12431 }, + /* l1-d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\... */ + { 12323 }, + /* l1-d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000le... */ + { 12220 }, + /* l1-d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legac... */ + { 12846 }, + /* l1-d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\0... */ + { 13265 }, + /* l1-d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000le... */ + { 13479 }, + /* l1-d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000... */ + { 13372 }, + /* l1-d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000l... */ + { 13161 }, + /* l1-d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 13051 }, + /* l1-d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 12946 }, + /* l1-d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-co... */ + { 9421 }, + /* l1-d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 9784 }, + /* l1-d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache... */ + { 9974 }, + /* l1-d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cac... */ + { 9877 }, + /* l1-d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 9694 }, + /* l1-d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 9598 }, + /* l1-d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 9507 }, + /* l1-d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 15230 }, + /* l1-d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-co... */ + { 15144 }, + /* l1-d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\00... */ + { 14364 }, + /* l1-d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch acce... */ + { 14807 }, + /* l1-d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses... */ + { 15033 }, + /* l1-d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch miss... */ + { 14920 }, + /* l1-d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 14697 }, + /* l1-d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch a... */ + { 14581 }, + /* l1-d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch access... */ + { 14470 }, + /* l1-d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\00... */ + { 13584 }, + /* l1-d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch acce... */ + { 14027 }, + /* l1-d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses... */ + { 14253 }, + /* l1-d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch miss... */ + { 14140 }, + /* l1-d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 13917 }, + /* l1-d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch a... */ + { 13801 }, + /* l1-d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch access... */ + { 13690 }, + /* l1-d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-... */ + { 10069 }, + /* l1-d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy... */ + { 10456 }, + /* l1-d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cac... */ + { 10654 }, + /* l1-d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-c... */ + { 10555 }, + /* l1-d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 10360 }, + /* l1-d-store-reference\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 10258 }, + /* l1-d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 10161 }, + /* l1-d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache... */ + { 10751 }, + /* l1-d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legac... */ + { 11142 }, + /* l1-d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-ca... */ + { 11342 }, + /* l1-d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-... */ + { 11242 }, + /* l1-d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 11045 }, + /* l1-d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000le... */ + { 10942 }, + /* l1-d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-... */ + { 10844 }, + /* l1-d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-... */ + { 11440 }, + /* l1-d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy... */ + { 11827 }, + /* l1-d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cac... */ + { 12025 }, + /* l1-d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-c... */ + { 11926 }, + /* l1-d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 11731 }, + /* l1-d-write-reference\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 11629 }, + /* l1-d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 11532 }, + /* l1-data\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-conf... */ + { 23238 }, + /* l1-data-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 30829 }, + /* l1-data-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache... */ + { 23322 }, + /* l1-data-load-access\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 23697 }, + /* l1-data-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-ca... */ + { 23893 }, + /* l1-data-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-... */ + { 23793 }, + /* l1-data-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 23604 }, + /* l1-data-load-reference\000legacy cache\000Level 1 data cache read accesses\000le... */ + { 23505 }, + /* l1-data-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-... */ + { 23411 }, + /* l1-data-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 23991 }, + /* l1-data-loads-access\000legacy cache\000Level 1 data cache read accesses\000lega... */ + { 24370 }, + /* l1-data-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-c... */ + { 24568 }, + /* l1-data-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy... */ + { 24467 }, + /* l1-data-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-... */ + { 24276 }, + /* l1-data-loads-reference\000legacy cache\000Level 1 data cache read accesses\000l... */ + { 24176 }, + /* l1-data-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy... */ + { 24081 }, + /* l1-data-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-c... */ + { 31015 }, + /* l1-data-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache... */ + { 30920 }, + /* l1-data-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-... */ + { 30741 }, + /* l1-data-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000lega... */ + { 27452 }, + /* l1-data-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\... */ + { 27875 }, + /* l1-data-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000l... */ + { 28091 }, + /* l1-data-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\00... */ + { 27983 }, + /* l1-data-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 27770 }, + /* l1-data-prefetch-reference\000legacy cache\000Level 1 data cache prefetch access... */ + { 27659 }, + /* l1-data-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\00... */ + { 27553 }, + /* l1-data-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000le... */ + { 28197 }, + /* l1-data-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 28628 }, + /* l1-data-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\00... */ + { 28848 }, + /* l1-data-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\... */ + { 28738 }, + /* l1-data-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\0... */ + { 28521 }, + /* l1-data-prefetches-reference\000legacy cache\000Level 1 data cache prefetch acce... */ + { 28408 }, + /* l1-data-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\... */ + { 28300 }, + /* l1-data-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache... */ + { 24667 }, + /* l1-data-read-access\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 25042 }, + /* l1-data-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-ca... */ + { 25238 }, + /* l1-data-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-... */ + { 25138 }, + /* l1-data-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 24949 }, + /* l1-data-read-reference\000legacy cache\000Level 1 data cache read accesses\000le... */ + { 24850 }, + /* l1-data-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-... */ + { 24756 }, + /* l1-data-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-... */ + { 30647 }, + /* l1-data-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache... */ + { 30558 }, + /* l1-data-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 29757 }, + /* l1-data-speculative-load-access\000legacy cache\000Level 1 data cache prefetch a... */ + { 30212 }, + /* l1-data-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch mis... */ + { 30444 }, + /* l1-data-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch m... */ + { 30328 }, + /* l1-data-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch acce... */ + { 30099 }, + /* l1-data-speculative-load-reference\000legacy cache\000Level 1 data cache prefetc... */ + { 29980 }, + /* l1-data-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch acc... */ + { 29866 }, + /* l1-data-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 28956 }, + /* l1-data-speculative-read-access\000legacy cache\000Level 1 data cache prefetch a... */ + { 29411 }, + /* l1-data-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch mis... */ + { 29643 }, + /* l1-data-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch m... */ + { 29527 }, + /* l1-data-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch acce... */ + { 29298 }, + /* l1-data-speculative-read-reference\000legacy cache\000Level 1 data cache prefetc... */ + { 29179 }, + /* l1-data-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch acc... */ + { 29065 }, + /* l1-data-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cac... */ + { 25336 }, + /* l1-data-store-access\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 25735 }, + /* l1-data-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-... */ + { 25939 }, + /* l1-data-store-misses\000legacy cache\000Level 1 data cache write misses\000legac... */ + { 25837 }, + /* l1-data-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy... */ + { 25636 }, + /* l1-data-store-reference\000legacy cache\000Level 1 data cache write accesses\000... */ + { 25531 }, + /* l1-data-store-refs\000legacy cache\000Level 1 data cache write accesses\000legac... */ + { 25431 }, + /* l1-data-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 26039 }, + /* l1-data-stores-access\000legacy cache\000Level 1 data cache write accesses\000le... */ + { 26442 }, + /* l1-data-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy... */ + { 26648 }, + /* l1-data-stores-misses\000legacy cache\000Level 1 data cache write misses\000lega... */ + { 26545 }, + /* l1-data-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legac... */ + { 26342 }, + /* l1-data-stores-reference\000legacy cache\000Level 1 data cache write accesses\00... */ + { 26236 }, + /* l1-data-stores-refs\000legacy cache\000Level 1 data cache write accesses\000lega... */ + { 26135 }, + /* l1-data-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cac... */ + { 26749 }, + /* l1-data-write-access\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 27148 }, + /* l1-data-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-... */ + { 27352 }, + /* l1-data-write-misses\000legacy cache\000Level 1 data cache write misses\000legac... */ + { 27250 }, + /* l1-data-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy... */ + { 27049 }, + /* l1-data-write-reference\000legacy cache\000Level 1 data cache write accesses\000... */ + { 26944 }, + /* l1-data-write-refs\000legacy cache\000Level 1 data cache write accesses\000legac... */ + { 26844 }, + /* l1-dcache\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-co... */ + { 13 }, + /* l1-dcache-access\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 7752 }, + /* l1-dcache-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 99 }, + /* l1-dcache-load-access\000legacy cache\000Level 1 data cache read accesses\000leg... */ + { 482 }, + /* l1-dcache-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-... */ + { 682 }, + /* l1-dcache-load-misses\000legacy cache\000Level 1 data cache read misses\000legac... */ + { 580 }, + /* l1-dcache-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy... */ + { 387 }, + /* l1-dcache-load-reference\000legacy cache\000Level 1 data cache read accesses\000... */ + { 286 }, + /* l1-dcache-load-refs\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 190 }, + /* l1-dcache-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-ca... */ + { 782 }, + /* l1-dcache-loads-access\000legacy cache\000Level 1 data cache read accesses\000le... */ + { 1169 }, + /* l1-dcache-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy... */ + { 1371 }, + /* l1-dcache-loads-misses\000legacy cache\000Level 1 data cache read misses\000lega... */ + { 1268 }, + /* l1-dcache-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 1073 }, + /* l1-dcache-loads-reference\000legacy cache\000Level 1 data cache read accesses\00... */ + { 971 }, + /* l1-dcache-loads-refs\000legacy cache\000Level 1 data cache read accesses\000lega... */ + { 874 }, + /* l1-dcache-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache... */ + { 7942 }, + /* l1-dcache-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cac... */ + { 7845 }, + /* l1-dcache-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 7662 }, + /* l1-dcache-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000le... */ + { 4313 }, + /* l1-dcache-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 4744 }, + /* l1-dcache-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\00... */ + { 4964 }, + /* l1-dcache-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\... */ + { 4854 }, + /* l1-dcache-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\0... */ + { 4637 }, + /* l1-dcache-prefetch-reference\000legacy cache\000Level 1 data cache prefetch acce... */ + { 4524 }, + /* l1-dcache-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\... */ + { 4416 }, + /* l1-dcache-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 5072 }, + /* l1-dcache-prefetches-access\000legacy cache\000Level 1 data cache prefetch acces... */ + { 5511 }, + /* l1-dcache-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\... */ + { 5735 }, + /* l1-dcache-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misse... */ + { 5623 }, + /* l1-dcache-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 5402 }, + /* l1-dcache-prefetches-reference\000legacy cache\000Level 1 data cache prefetch ac... */ + { 5287 }, + /* l1-dcache-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 5177 }, + /* l1-dcache-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 1472 }, + /* l1-dcache-read-access\000legacy cache\000Level 1 data cache read accesses\000leg... */ + { 1855 }, + /* l1-dcache-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-... */ + { 2055 }, + /* l1-dcache-read-misses\000legacy cache\000Level 1 data cache read misses\000legac... */ + { 1953 }, + /* l1-dcache-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy... */ + { 1760 }, + /* l1-dcache-read-reference\000legacy cache\000Level 1 data cache read accesses\000... */ + { 1659 }, + /* l1-dcache-read-refs\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 1563 }, + /* l1-dcache-reference\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 7566 }, + /* l1-dcache-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 7475 }, + /* l1-dcache-speculative-load\000legacy cache\000Level 1 data cache prefetch access... */ + { 6660 }, + /* l1-dcache-speculative-load-access\000legacy cache\000Level 1 data cache prefetch... */ + { 7123 }, + /* l1-dcache-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch m... */ + { 7359 }, + /* l1-dcache-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch... */ + { 7241 }, + /* l1-dcache-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch ac... */ + { 7008 }, + /* l1-dcache-speculative-load-reference\000legacy cache\000Level 1 data cache prefe... */ + { 6887 }, + /* l1-dcache-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch a... */ + { 6771 }, + /* l1-dcache-speculative-read\000legacy cache\000Level 1 data cache prefetch access... */ + { 5845 }, + /* l1-dcache-speculative-read-access\000legacy cache\000Level 1 data cache prefetch... */ + { 6308 }, + /* l1-dcache-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch m... */ + { 6544 }, + /* l1-dcache-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch... */ + { 6426 }, + /* l1-dcache-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch ac... */ + { 6193 }, + /* l1-dcache-speculative-read-reference\000legacy cache\000Level 1 data cache prefe... */ + { 6072 }, + /* l1-dcache-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch a... */ + { 5956 }, + /* l1-dcache-store\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 2155 }, + /* l1-dcache-store-access\000legacy cache\000Level 1 data cache write accesses\000l... */ + { 2562 }, + /* l1-dcache-store-miss\000legacy cache\000Level 1 data cache write misses\000legac... */ + { 2770 }, + /* l1-dcache-store-misses\000legacy cache\000Level 1 data cache write misses\000leg... */ + { 2666 }, + /* l1-dcache-store-ops\000legacy cache\000Level 1 data cache write accesses\000lega... */ + { 2461 }, + /* l1-dcache-store-reference\000legacy cache\000Level 1 data cache write accesses\0... */ + { 2354 }, + /* l1-dcache-store-refs\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 2252 }, + /* l1-dcache-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-... */ + { 2872 }, + /* l1-dcache-stores-access\000legacy cache\000Level 1 data cache write accesses\000... */ + { 3283 }, + /* l1-dcache-stores-miss\000legacy cache\000Level 1 data cache write misses\000lega... */ + { 3493 }, + /* l1-dcache-stores-misses\000legacy cache\000Level 1 data cache write misses\000le... */ + { 3388 }, + /* l1-dcache-stores-ops\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 3181 }, + /* l1-dcache-stores-reference\000legacy cache\000Level 1 data cache write accesses\... */ + { 3073 }, + /* l1-dcache-stores-refs\000legacy cache\000Level 1 data cache write accesses\000le... */ + { 2970 }, + /* l1-dcache-write\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 3596 }, + /* l1-dcache-write-access\000legacy cache\000Level 1 data cache write accesses\000l... */ + { 4003 }, + /* l1-dcache-write-miss\000legacy cache\000Level 1 data cache write misses\000legac... */ + { 4211 }, + /* l1-dcache-write-misses\000legacy cache\000Level 1 data cache write misses\000leg... */ + { 4107 }, + /* l1-dcache-write-ops\000legacy cache\000Level 1 data cache write accesses\000lega... */ + { 3902 }, + /* l1-dcache-write-reference\000legacy cache\000Level 1 data cache write accesses\0... */ + { 3795 }, + /* l1-dcache-write-refs\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 3693 }, + /* l1-i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-... */ + { 37366 }, + /* l1-i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy... */ + { 43053 }, + /* l1-i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-c... */ + { 37454 }, + /* l1-i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000l... */ + { 37845 }, + /* l1-i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legac... */ + { 38049 }, + /* l1-i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000leg... */ + { 37945 }, + /* l1-i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 37748 }, + /* l1-i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 37645 }, + /* l1-i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 37547 }, + /* l1-i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-... */ + { 38151 }, + /* l1-i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000... */ + { 38546 }, + /* l1-i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000lega... */ + { 38752 }, + /* l1-i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000le... */ + { 38647 }, + /* l1-i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 38448 }, + /* l1-i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\... */ + { 38344 }, + /* l1-i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000le... */ + { 38245 }, + /* l1-i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cac... */ + { 43247 }, + /* l1-i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-c... */ + { 43148 }, + /* l1-i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-ca... */ + { 42961 }, + /* l1-i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000... */ + { 39552 }, + /* l1-i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch acces... */ + { 39991 }, + /* l1-i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\... */ + { 40215 }, + /* l1-i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misse... */ + { 40103 }, + /* l1-i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses... */ + { 39882 }, + /* l1-i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch ac... */ + { 39767 }, + /* l1-i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesse... */ + { 39657 }, + /* l1-i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\0... */ + { 40325 }, + /* l1-i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch acc... */ + { 40772 }, + /* l1-i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misse... */ + { 41000 }, + /* l1-i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch mis... */ + { 40886 }, + /* l1-i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch access... */ + { 40661 }, + /* l1-i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 40544 }, + /* l1-i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch acces... */ + { 40432 }, + /* l1-i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-c... */ + { 38855 }, + /* l1-i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000l... */ + { 39246 }, + /* l1-i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legac... */ + { 39450 }, + /* l1-i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000leg... */ + { 39346 }, + /* l1-i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 39149 }, + /* l1-i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 39046 }, + /* l1-i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 38948 }, + /* l1-i-reference\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 42863 }, + /* l1-i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-c... */ + { 42770 }, + /* l1-i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch acce... */ + { 41941 }, + /* l1-i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefet... */ + { 42412 }, + /* l1-i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch... */ + { 42652 }, + /* l1-i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefet... */ + { 42532 }, + /* l1-i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 42295 }, + /* l1-i-speculative-load-reference\000legacy cache\000Level 1 instruction cache pre... */ + { 42172 }, + /* l1-i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch... */ + { 42054 }, + /* l1-i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch acce... */ + { 41112 }, + /* l1-i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefet... */ + { 41583 }, + /* l1-i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch... */ + { 41823 }, + /* l1-i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefet... */ + { 41703 }, + /* l1-i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 41466 }, + /* l1-i-speculative-read-reference\000legacy cache\000Level 1 instruction cache pre... */ + { 41343 }, + /* l1-i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch... */ + { 41225 }, + /* l1-icache\000legacy cache\000Level 1 instruction cache read accesses\000legacy-c... */ + { 31108 }, + /* l1-icache-access\000legacy cache\000Level 1 instruction cache read accesses\000l... */ + { 37060 }, + /* l1-icache-load\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 31201 }, + /* l1-icache-load-access\000legacy cache\000Level 1 instruction cache read accesses... */ + { 31612 }, + /* l1-icache-load-miss\000legacy cache\000Level 1 instruction cache read misses\000... */ + { 31826 }, + /* l1-icache-load-misses\000legacy cache\000Level 1 instruction cache read misses\0... */ + { 31717 }, + /* l1-icache-load-ops\000legacy cache\000Level 1 instruction cache read accesses\00... */ + { 31510 }, + /* l1-icache-load-reference\000legacy cache\000Level 1 instruction cache read acces... */ + { 31402 }, + /* l1-icache-load-refs\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 31299 }, + /* l1-icache-loads\000legacy cache\000Level 1 instruction cache read accesses\000le... */ + { 31933 }, + /* l1-icache-loads-access\000legacy cache\000Level 1 instruction cache read accesse... */ + { 32348 }, + /* l1-icache-loads-miss\000legacy cache\000Level 1 instruction cache read misses\00... */ + { 32564 }, + /* l1-icache-loads-misses\000legacy cache\000Level 1 instruction cache read misses\... */ + { 32454 }, + /* l1-icache-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 32245 }, + /* l1-icache-loads-reference\000legacy cache\000Level 1 instruction cache read acce... */ + { 32136 }, + /* l1-icache-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\... */ + { 32032 }, + /* l1-icache-miss\000legacy cache\000Level 1 instruction cache read misses\000legac... */ + { 37264 }, + /* l1-icache-misses\000legacy cache\000Level 1 instruction cache read misses\000leg... */ + { 37160 }, + /* l1-icache-ops\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 36963 }, + /* l1-icache-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesse... */ + { 33404 }, + /* l1-icache-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 33863 }, + /* l1-icache-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch mi... */ + { 34097 }, + /* l1-icache-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 33980 }, + /* l1-icache-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch acc... */ + { 33749 }, + /* l1-icache-prefetch-reference\000legacy cache\000Level 1 instruction cache prefet... */ + { 33629 }, + /* l1-icache-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch ac... */ + { 33514 }, + /* l1-icache-prefetches\000legacy cache\000Level 1 instruction cache prefetch acces... */ + { 34212 }, + /* l1-icache-prefetches-access\000legacy cache\000Level 1 instruction cache prefetc... */ + { 34679 }, + /* l1-icache-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 34917 }, + /* l1-icache-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetc... */ + { 34798 }, + /* l1-icache-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch a... */ + { 34563 }, + /* l1-icache-prefetches-reference\000legacy cache\000Level 1 instruction cache pref... */ + { 34441 }, + /* l1-icache-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 34324 }, + /* l1-icache-read\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 32672 }, + /* l1-icache-read-access\000legacy cache\000Level 1 instruction cache read accesses... */ + { 33083 }, + /* l1-icache-read-miss\000legacy cache\000Level 1 instruction cache read misses\000... */ + { 33297 }, + /* l1-icache-read-misses\000legacy cache\000Level 1 instruction cache read misses\0... */ + { 33188 }, + /* l1-icache-read-ops\000legacy cache\000Level 1 instruction cache read accesses\00... */ + { 32981 }, + /* l1-icache-read-reference\000legacy cache\000Level 1 instruction cache read acces... */ + { 32873 }, + /* l1-icache-read-refs\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 32770 }, + /* l1-icache-reference\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 36860 }, + /* l1-icache-refs\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 36762 }, + /* l1-icache-speculative-load\000legacy cache\000Level 1 instruction cache prefetch... */ + { 35898 }, + /* l1-icache-speculative-load-access\000legacy cache\000Level 1 instruction cache p... */ + { 36389 }, + /* l1-icache-speculative-load-miss\000legacy cache\000Level 1 instruction cache pre... */ + { 36639 }, + /* l1-icache-speculative-load-misses\000legacy cache\000Level 1 instruction cache p... */ + { 36514 }, + /* l1-icache-speculative-load-ops\000legacy cache\000Level 1 instruction cache pref... */ + { 36267 }, + /* l1-icache-speculative-load-reference\000legacy cache\000Level 1 instruction cach... */ + { 36139 }, + /* l1-icache-speculative-load-refs\000legacy cache\000Level 1 instruction cache pre... */ + { 36016 }, + /* l1-icache-speculative-read\000legacy cache\000Level 1 instruction cache prefetch... */ + { 35034 }, + /* l1-icache-speculative-read-access\000legacy cache\000Level 1 instruction cache p... */ + { 35525 }, + /* l1-icache-speculative-read-miss\000legacy cache\000Level 1 instruction cache pre... */ + { 35775 }, + /* l1-icache-speculative-read-misses\000legacy cache\000Level 1 instruction cache p... */ + { 35650 }, + /* l1-icache-speculative-read-ops\000legacy cache\000Level 1 instruction cache pref... */ + { 35403 }, + /* l1-icache-speculative-read-reference\000legacy cache\000Level 1 instruction cach... */ + { 35275 }, + /* l1-icache-speculative-read-refs\000legacy cache\000Level 1 instruction cache pre... */ + { 35152 }, + /* l1-instruction\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 49266 }, + /* l1-instruction-access\000legacy cache\000Level 1 instruction cache read accesses... */ + { 55483 }, + /* l1-instruction-load\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 49364 }, + /* l1-instruction-load-access\000legacy cache\000Level 1 instruction cache read acc... */ + { 49795 }, + /* l1-instruction-load-miss\000legacy cache\000Level 1 instruction cache read misse... */ + { 50019 }, + /* l1-instruction-load-misses\000legacy cache\000Level 1 instruction cache read mis... */ + { 49905 }, + /* l1-instruction-load-ops\000legacy cache\000Level 1 instruction cache read access... */ + { 49688 }, + /* l1-instruction-load-reference\000legacy cache\000Level 1 instruction cache read ... */ + { 49575 }, + /* l1-instruction-load-refs\000legacy cache\000Level 1 instruction cache read acces... */ + { 49467 }, + /* l1-instruction-loads\000legacy cache\000Level 1 instruction cache read accesses\... */ + { 50131 }, + /* l1-instruction-loads-access\000legacy cache\000Level 1 instruction cache read ac... */ + { 50566 }, + /* l1-instruction-loads-miss\000legacy cache\000Level 1 instruction cache read miss... */ + { 50792 }, + /* l1-instruction-loads-misses\000legacy cache\000Level 1 instruction cache read mi... */ + { 50677 }, + /* l1-instruction-loads-ops\000legacy cache\000Level 1 instruction cache read acces... */ + { 50458 }, + /* l1-instruction-loads-reference\000legacy cache\000Level 1 instruction cache read... */ + { 50344 }, + /* l1-instruction-loads-refs\000legacy cache\000Level 1 instruction cache read acce... */ + { 50235 }, + /* l1-instruction-miss\000legacy cache\000Level 1 instruction cache read misses\000... */ + { 55697 }, + /* l1-instruction-misses\000legacy cache\000Level 1 instruction cache read misses\0... */ + { 55588 }, + /* l1-instruction-ops\000legacy cache\000Level 1 instruction cache read accesses\00... */ + { 55381 }, + /* l1-instruction-prefetch\000legacy cache\000Level 1 instruction cache prefetch ac... */ + { 51672 }, + /* l1-instruction-prefetch-access\000legacy cache\000Level 1 instruction cache pref... */ + { 52151 }, + /* l1-instruction-prefetch-miss\000legacy cache\000Level 1 instruction cache prefet... */ + { 52395 }, + /* l1-instruction-prefetch-misses\000legacy cache\000Level 1 instruction cache pref... */ + { 52273 }, + /* l1-instruction-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetc... */ + { 52032 }, + /* l1-instruction-prefetch-reference\000legacy cache\000Level 1 instruction cache p... */ + { 51907 }, + /* l1-instruction-prefetch-refs\000legacy cache\000Level 1 instruction cache prefet... */ + { 51787 }, + /* l1-instruction-prefetches\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 52515 }, + /* l1-instruction-prefetches-access\000legacy cache\000Level 1 instruction cache pr... */ + { 53002 }, + /* l1-instruction-prefetches-miss\000legacy cache\000Level 1 instruction cache pref... */ + { 53250 }, + /* l1-instruction-prefetches-misses\000legacy cache\000Level 1 instruction cache pr... */ + { 53126 }, + /* l1-instruction-prefetches-ops\000legacy cache\000Level 1 instruction cache prefe... */ + { 52881 }, + /* l1-instruction-prefetches-reference\000legacy cache\000Level 1 instruction cache... */ + { 52754 }, + /* l1-instruction-prefetches-refs\000legacy cache\000Level 1 instruction cache pref... */ + { 52632 }, + /* l1-instruction-read\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 50905 }, + /* l1-instruction-read-access\000legacy cache\000Level 1 instruction cache read acc... */ + { 51336 }, + /* l1-instruction-read-miss\000legacy cache\000Level 1 instruction cache read misse... */ + { 51560 }, + /* l1-instruction-read-misses\000legacy cache\000Level 1 instruction cache read mis... */ + { 51446 }, + /* l1-instruction-read-ops\000legacy cache\000Level 1 instruction cache read access... */ + { 51229 }, + /* l1-instruction-read-reference\000legacy cache\000Level 1 instruction cache read ... */ + { 51116 }, + /* l1-instruction-read-refs\000legacy cache\000Level 1 instruction cache read acces... */ + { 51008 }, + /* l1-instruction-reference\000legacy cache\000Level 1 instruction cache read acces... */ + { 55273 }, + /* l1-instruction-refs\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 55170 }, + /* l1-instruction-speculative-load\000legacy cache\000Level 1 instruction cache pre... */ + { 54271 }, + /* l1-instruction-speculative-load-access\000legacy cache\000Level 1 instruction ca... */ + { 54782 }, + /* l1-instruction-speculative-load-miss\000legacy cache\000Level 1 instruction cach... */ + { 55042 }, + /* l1-instruction-speculative-load-misses\000legacy cache\000Level 1 instruction ca... */ + { 54912 }, + /* l1-instruction-speculative-load-ops\000legacy cache\000Level 1 instruction cache... */ + { 54655 }, + /* l1-instruction-speculative-load-reference\000legacy cache\000Level 1 instruction... */ + { 54522 }, + /* l1-instruction-speculative-load-refs\000legacy cache\000Level 1 instruction cach... */ + { 54394 }, + /* l1-instruction-speculative-read\000legacy cache\000Level 1 instruction cache pre... */ + { 53372 }, + /* l1-instruction-speculative-read-access\000legacy cache\000Level 1 instruction ca... */ + { 53883 }, + /* l1-instruction-speculative-read-miss\000legacy cache\000Level 1 instruction cach... */ + { 54143 }, + /* l1-instruction-speculative-read-misses\000legacy cache\000Level 1 instruction ca... */ + { 54013 }, + /* l1-instruction-speculative-read-ops\000legacy cache\000Level 1 instruction cache... */ + { 53756 }, + /* l1-instruction-speculative-read-reference\000legacy cache\000Level 1 instruction... */ + { 53623 }, + /* l1-instruction-speculative-read-refs\000legacy cache\000Level 1 instruction cach... */ + { 53495 }, + /* l1d\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-config=0... */ + { 15676 }, + /* l1d-access\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-c... */ + { 22971 }, + /* l1d-load\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-con... */ + { 15756 }, + /* l1d-load-access\000legacy cache\000Level 1 data cache read accesses\000legacy-ca... */ + { 16115 }, + /* l1d-load-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-... */ + { 16303 }, + /* l1d-load-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cach... */ + { 16207 }, + /* l1d-load-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache... */ + { 16026 }, + /* l1d-load-reference\000legacy cache\000Level 1 data cache read accesses\000legacy... */ + { 15931 }, + /* l1d-load-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 15841 }, + /* l1d-loads\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-co... */ + { 16397 }, + /* l1d-loads-access\000legacy cache\000Level 1 data cache read accesses\000legacy-c... */ + { 16760 }, + /* l1d-loads-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache... */ + { 16950 }, + /* l1d-loads-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cac... */ + { 16853 }, + /* l1d-loads-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 16670 }, + /* l1d-loads-reference\000legacy cache\000Level 1 data cache read accesses\000legac... */ + { 16574 }, + /* l1d-loads-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cac... */ + { 16483 }, + /* l1d-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-confi... */ + { 23149 }, + /* l1d-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cache-con... */ + { 23058 }, + /* l1d-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-conf... */ + { 22887 }, + /* l1d-prefetch\000legacy cache\000Level 1 data cache prefetch accesses\000legacy-c... */ + { 19718 }, + /* l1d-prefetch-access\000legacy cache\000Level 1 data cache prefetch accesses\000l... */ + { 20125 }, + /* l1d-prefetch-miss\000legacy cache\000Level 1 data cache prefetch misses\000legac... */ + { 20333 }, + /* l1d-prefetch-misses\000legacy cache\000Level 1 data cache prefetch misses\000leg... */ + { 20229 }, + /* l1d-prefetch-ops\000legacy cache\000Level 1 data cache prefetch accesses\000lega... */ + { 20024 }, + /* l1d-prefetch-reference\000legacy cache\000Level 1 data cache prefetch accesses\0... */ + { 19917 }, + /* l1d-prefetch-refs\000legacy cache\000Level 1 data cache prefetch accesses\000leg... */ + { 19815 }, + /* l1d-prefetches\000legacy cache\000Level 1 data cache prefetch accesses\000legacy... */ + { 20435 }, + /* l1d-prefetches-access\000legacy cache\000Level 1 data cache prefetch accesses\00... */ + { 20850 }, + /* l1d-prefetches-miss\000legacy cache\000Level 1 data cache prefetch misses\000leg... */ + { 21062 }, + /* l1d-prefetches-misses\000legacy cache\000Level 1 data cache prefetch misses\000l... */ + { 20956 }, + /* l1d-prefetches-ops\000legacy cache\000Level 1 data cache prefetch accesses\000le... */ + { 20747 }, + /* l1d-prefetches-reference\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 20638 }, + /* l1d-prefetches-refs\000legacy cache\000Level 1 data cache prefetch accesses\000l... */ + { 20534 }, + /* l1d-read\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-con... */ + { 17045 }, + /* l1d-read-access\000legacy cache\000Level 1 data cache read accesses\000legacy-ca... */ + { 17404 }, + /* l1d-read-miss\000legacy cache\000Level 1 data cache read misses\000legacy-cache-... */ + { 17592 }, + /* l1d-read-misses\000legacy cache\000Level 1 data cache read misses\000legacy-cach... */ + { 17496 }, + /* l1d-read-ops\000legacy cache\000Level 1 data cache read accesses\000legacy-cache... */ + { 17315 }, + /* l1d-read-reference\000legacy cache\000Level 1 data cache read accesses\000legacy... */ + { 17220 }, + /* l1d-read-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 17130 }, + /* l1d-reference\000legacy cache\000Level 1 data cache read accesses\000legacy-cach... */ + { 22797 }, + /* l1d-refs\000legacy cache\000Level 1 data cache read accesses\000legacy-cache-con... */ + { 22712 }, + /* l1d-speculative-load\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 21939 }, + /* l1d-speculative-load-access\000legacy cache\000Level 1 data cache prefetch acces... */ + { 22378 }, + /* l1d-speculative-load-miss\000legacy cache\000Level 1 data cache prefetch misses\... */ + { 22602 }, + /* l1d-speculative-load-misses\000legacy cache\000Level 1 data cache prefetch misse... */ + { 22490 }, + /* l1d-speculative-load-ops\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 22269 }, + /* l1d-speculative-load-reference\000legacy cache\000Level 1 data cache prefetch ac... */ + { 22154 }, + /* l1d-speculative-load-refs\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 22044 }, + /* l1d-speculative-read\000legacy cache\000Level 1 data cache prefetch accesses\000... */ + { 21166 }, + /* l1d-speculative-read-access\000legacy cache\000Level 1 data cache prefetch acces... */ + { 21605 }, + /* l1d-speculative-read-miss\000legacy cache\000Level 1 data cache prefetch misses\... */ + { 21829 }, + /* l1d-speculative-read-misses\000legacy cache\000Level 1 data cache prefetch misse... */ + { 21717 }, + /* l1d-speculative-read-ops\000legacy cache\000Level 1 data cache prefetch accesses... */ + { 21496 }, + /* l1d-speculative-read-reference\000legacy cache\000Level 1 data cache prefetch ac... */ + { 21381 }, + /* l1d-speculative-read-refs\000legacy cache\000Level 1 data cache prefetch accesse... */ + { 21271 }, + /* l1d-store\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-c... */ + { 17686 }, + /* l1d-store-access\000legacy cache\000Level 1 data cache write accesses\000legacy-... */ + { 18069 }, + /* l1d-store-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cach... */ + { 18265 }, + /* l1d-store-misses\000legacy cache\000Level 1 data cache write misses\000legacy-ca... */ + { 18167 }, + /* l1d-store-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cac... */ + { 17974 }, + /* l1d-store-reference\000legacy cache\000Level 1 data cache write accesses\000lega... */ + { 17873 }, + /* l1d-store-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 17777 }, + /* l1d-stores\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-... */ + { 18361 }, + /* l1d-stores-access\000legacy cache\000Level 1 data cache write accesses\000legacy... */ + { 18748 }, + /* l1d-stores-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cac... */ + { 18946 }, + /* l1d-stores-misses\000legacy cache\000Level 1 data cache write misses\000legacy-c... */ + { 18847 }, + /* l1d-stores-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 18652 }, + /* l1d-stores-reference\000legacy cache\000Level 1 data cache write accesses\000leg... */ + { 18550 }, + /* l1d-stores-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-c... */ + { 18453 }, + /* l1d-write\000legacy cache\000Level 1 data cache write accesses\000legacy-cache-c... */ + { 19043 }, + /* l1d-write-access\000legacy cache\000Level 1 data cache write accesses\000legacy-... */ + { 19426 }, + /* l1d-write-miss\000legacy cache\000Level 1 data cache write misses\000legacy-cach... */ + { 19622 }, + /* l1d-write-misses\000legacy cache\000Level 1 data cache write misses\000legacy-ca... */ + { 19524 }, + /* l1d-write-ops\000legacy cache\000Level 1 data cache write accesses\000legacy-cac... */ + { 19331 }, + /* l1d-write-reference\000legacy cache\000Level 1 data cache write accesses\000lega... */ + { 19230 }, + /* l1d-write-refs\000legacy cache\000Level 1 data cache write accesses\000legacy-ca... */ + { 19134 }, + /* l1i\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cache-c... */ + { 43344 }, + /* l1i-access\000legacy cache\000Level 1 instruction cache read accesses\000legacy-... */ + { 48978 }, + /* l1i-load\000legacy cache\000Level 1 instruction cache read accesses\000legacy-ca... */ + { 43431 }, + /* l1i-load-access\000legacy cache\000Level 1 instruction cache read accesses\000le... */ + { 43818 }, + /* l1i-load-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy... */ + { 44020 }, + /* l1i-load-misses\000legacy cache\000Level 1 instruction cache read misses\000lega... */ + { 43917 }, + /* l1i-load-ops\000legacy cache\000Level 1 instruction cache read accesses\000legac... */ + { 43722 }, + /* l1i-load-reference\000legacy cache\000Level 1 instruction cache read accesses\00... */ + { 43620 }, + /* l1i-load-refs\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 43523 }, + /* l1i-loads\000legacy cache\000Level 1 instruction cache read accesses\000legacy-c... */ + { 44121 }, + /* l1i-loads-access\000legacy cache\000Level 1 instruction cache read accesses\000l... */ + { 44512 }, + /* l1i-loads-miss\000legacy cache\000Level 1 instruction cache read misses\000legac... */ + { 44716 }, + /* l1i-loads-misses\000legacy cache\000Level 1 instruction cache read misses\000leg... */ + { 44612 }, + /* l1i-loads-ops\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 44415 }, + /* l1i-loads-reference\000legacy cache\000Level 1 instruction cache read accesses\0... */ + { 44312 }, + /* l1i-loads-refs\000legacy cache\000Level 1 instruction cache read accesses\000leg... */ + { 44214 }, + /* l1i-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy-cach... */ + { 49170 }, + /* l1i-misses\000legacy cache\000Level 1 instruction cache read misses\000legacy-ca... */ + { 49072 }, + /* l1i-ops\000legacy cache\000Level 1 instruction cache read accesses\000legacy-cac... */ + { 48887 }, + /* l1i-prefetch\000legacy cache\000Level 1 instruction cache prefetch accesses\000l... */ + { 45508 }, + /* l1i-prefetch-access\000legacy cache\000Level 1 instruction cache prefetch access... */ + { 45943 }, + /* l1i-prefetch-miss\000legacy cache\000Level 1 instruction cache prefetch misses\0... */ + { 46165 }, + /* l1i-prefetch-misses\000legacy cache\000Level 1 instruction cache prefetch misses... */ + { 46054 }, + /* l1i-prefetch-ops\000legacy cache\000Level 1 instruction cache prefetch accesses\... */ + { 45835 }, + /* l1i-prefetch-reference\000legacy cache\000Level 1 instruction cache prefetch acc... */ + { 45721 }, + /* l1i-prefetch-refs\000legacy cache\000Level 1 instruction cache prefetch accesses... */ + { 45612 }, + /* l1i-prefetches\000legacy cache\000Level 1 instruction cache prefetch accesses\00... */ + { 46274 }, + /* l1i-prefetches-access\000legacy cache\000Level 1 instruction cache prefetch acce... */ + { 46717 }, + /* l1i-prefetches-miss\000legacy cache\000Level 1 instruction cache prefetch misses... */ + { 46943 }, + /* l1i-prefetches-misses\000legacy cache\000Level 1 instruction cache prefetch miss... */ + { 46830 }, + /* l1i-prefetches-ops\000legacy cache\000Level 1 instruction cache prefetch accesse... */ + { 46607 }, + /* l1i-prefetches-reference\000legacy cache\000Level 1 instruction cache prefetch a... */ + { 46491 }, + /* l1i-prefetches-refs\000legacy cache\000Level 1 instruction cache prefetch access... */ + { 46380 }, + /* l1i-read\000legacy cache\000Level 1 instruction cache read accesses\000legacy-ca... */ + { 44818 }, + /* l1i-read-access\000legacy cache\000Level 1 instruction cache read accesses\000le... */ + { 45205 }, + /* l1i-read-miss\000legacy cache\000Level 1 instruction cache read misses\000legacy... */ + { 45407 }, + /* l1i-read-misses\000legacy cache\000Level 1 instruction cache read misses\000lega... */ + { 45304 }, + /* l1i-read-ops\000legacy cache\000Level 1 instruction cache read accesses\000legac... */ + { 45109 }, + /* l1i-read-reference\000legacy cache\000Level 1 instruction cache read accesses\00... */ + { 45007 }, + /* l1i-read-refs\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 44910 }, + /* l1i-reference\000legacy cache\000Level 1 instruction cache read accesses\000lega... */ + { 48790 }, + /* l1i-refs\000legacy cache\000Level 1 instruction cache read accesses\000legacy-ca... */ + { 48698 }, + /* l1i-speculative-load\000legacy cache\000Level 1 instruction cache prefetch acces... */ + { 47876 }, + /* l1i-speculative-load-access\000legacy cache\000Level 1 instruction cache prefetc... */ + { 48343 }, + /* l1i-speculative-load-miss\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 48581 }, + /* l1i-speculative-load-misses\000legacy cache\000Level 1 instruction cache prefetc... */ + { 48462 }, + /* l1i-speculative-load-ops\000legacy cache\000Level 1 instruction cache prefetch a... */ + { 48227 }, + /* l1i-speculative-load-reference\000legacy cache\000Level 1 instruction cache pref... */ + { 48105 }, + /* l1i-speculative-load-refs\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 47988 }, + /* l1i-speculative-read\000legacy cache\000Level 1 instruction cache prefetch acces... */ + { 47054 }, + /* l1i-speculative-read-access\000legacy cache\000Level 1 instruction cache prefetc... */ + { 47521 }, + /* l1i-speculative-read-miss\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 47759 }, + /* l1i-speculative-read-misses\000legacy cache\000Level 1 instruction cache prefetc... */ + { 47640 }, + /* l1i-speculative-read-ops\000legacy cache\000Level 1 instruction cache prefetch a... */ + { 47405 }, + /* l1i-speculative-read-reference\000legacy cache\000Level 1 instruction cache pref... */ + { 47283 }, + /* l1i-speculative-read-refs\000legacy cache\000Level 1 instruction cache prefetch ... */ + { 47166 }, + /* l2\000legacy cache\000Level 2 (or higher) last level cache read accesses\000lega... */ + { 63212 }, + /* l2-access\000legacy cache\000Level 2 (or higher) last level cache read accesses\... */ + { 71765 }, + /* l2-load\000legacy cache\000Level 2 (or higher) last level cache read accesses\00... */ + { 63309 }, + /* l2-load-access\000legacy cache\000Level 2 (or higher) last level cache read acce... */ + { 63736 }, + /* l2-load-miss\000legacy cache\000Level 2 (or higher) last level cache read misses... */ + { 63958 }, + /* l2-load-misses\000legacy cache\000Level 2 (or higher) last level cache read miss... */ + { 63845 }, + /* l2-load-ops\000legacy cache\000Level 2 (or higher) last level cache read accesse... */ + { 63630 }, + /* l2-load-reference\000legacy cache\000Level 2 (or higher) last level cache read a... */ + { 63518 }, + /* l2-load-refs\000legacy cache\000Level 2 (or higher) last level cache read access... */ + { 63411 }, + /* l2-loads\000legacy cache\000Level 2 (or higher) last level cache read accesses\0... */ + { 64069 }, + /* l2-loads-access\000legacy cache\000Level 2 (or higher) last level cache read acc... */ + { 64500 }, + /* l2-loads-miss\000legacy cache\000Level 2 (or higher) last level cache read misse... */ + { 64724 }, + /* l2-loads-misses\000legacy cache\000Level 2 (or higher) last level cache read mis... */ + { 64610 }, + /* l2-loads-ops\000legacy cache\000Level 2 (or higher) last level cache read access... */ + { 64393 }, + /* l2-loads-reference\000legacy cache\000Level 2 (or higher) last level cache read ... */ + { 64280 }, + /* l2-loads-refs\000legacy cache\000Level 2 (or higher) last level cache read acces... */ + { 64172 }, + /* l2-miss\000legacy cache\000Level 2 (or higher) last level cache read misses\000l... */ + { 71977 }, + /* l2-misses\000legacy cache\000Level 2 (or higher) last level cache read misses\00... */ + { 71869 }, + /* l2-ops\000legacy cache\000Level 2 (or higher) last level cache read accesses\000... */ + { 71664 }, + /* l2-prefetch\000legacy cache\000Level 2 (or higher) last level cache prefetch acc... */ + { 67985 }, + /* l2-prefetch-access\000legacy cache\000Level 2 (or higher) last level cache prefe... */ + { 68460 }, + /* l2-prefetch-miss\000legacy cache\000Level 2 (or higher) last level cache prefetc... */ + { 68702 }, + /* l2-prefetch-misses\000legacy cache\000Level 2 (or higher) last level cache prefe... */ + { 68581 }, + /* l2-prefetch-ops\000legacy cache\000Level 2 (or higher) last level cache prefetch... */ + { 68342 }, + /* l2-prefetch-reference\000legacy cache\000Level 2 (or higher) last level cache pr... */ + { 68218 }, + /* l2-prefetch-refs\000legacy cache\000Level 2 (or higher) last level cache prefetc... */ + { 68099 }, + /* l2-prefetches\000legacy cache\000Level 2 (or higher) last level cache prefetch a... */ + { 68821 }, + /* l2-prefetches-access\000legacy cache\000Level 2 (or higher) last level cache pre... */ + { 69304 }, + /* l2-prefetches-miss\000legacy cache\000Level 2 (or higher) last level cache prefe... */ + { 69550 }, + /* l2-prefetches-misses\000legacy cache\000Level 2 (or higher) last level cache pre... */ + { 69427 }, + /* l2-prefetches-ops\000legacy cache\000Level 2 (or higher) last level cache prefet... */ + { 69184 }, + /* l2-prefetches-reference\000legacy cache\000Level 2 (or higher) last level cache ... */ + { 69058 }, + /* l2-prefetches-refs\000legacy cache\000Level 2 (or higher) last level cache prefe... */ + { 68937 }, + /* l2-read\000legacy cache\000Level 2 (or higher) last level cache read accesses\00... */ + { 64836 }, + /* l2-read-access\000legacy cache\000Level 2 (or higher) last level cache read acce... */ + { 65263 }, + /* l2-read-miss\000legacy cache\000Level 2 (or higher) last level cache read misses... */ + { 65485 }, + /* l2-read-misses\000legacy cache\000Level 2 (or higher) last level cache read miss... */ + { 65372 }, + /* l2-read-ops\000legacy cache\000Level 2 (or higher) last level cache read accesse... */ + { 65157 }, + /* l2-read-reference\000legacy cache\000Level 2 (or higher) last level cache read a... */ + { 65045 }, + /* l2-read-refs\000legacy cache\000Level 2 (or higher) last level cache read access... */ + { 64938 }, + /* l2-reference\000legacy cache\000Level 2 (or higher) last level cache read access... */ + { 71557 }, + /* l2-refs\000legacy cache\000Level 2 (or higher) last level cache read accesses\00... */ + { 71455 }, + /* l2-speculative-load\000legacy cache\000Level 2 (or higher) last level cache pref... */ + { 70563 }, + /* l2-speculative-load-access\000legacy cache\000Level 2 (or higher) last level cac... */ + { 71070 }, + /* l2-speculative-load-miss\000legacy cache\000Level 2 (or higher) last level cache... */ + { 71328 }, + /* l2-speculative-load-misses\000legacy cache\000Level 2 (or higher) last level cac... */ + { 71199 }, + /* l2-speculative-load-ops\000legacy cache\000Level 2 (or higher) last level cache ... */ + { 70944 }, + /* l2-speculative-load-reference\000legacy cache\000Level 2 (or higher) last level ... */ + { 70812 }, + /* l2-speculative-load-refs\000legacy cache\000Level 2 (or higher) last level cache... */ + { 70685 }, + /* l2-speculative-read\000legacy cache\000Level 2 (or higher) last level cache pref... */ + { 69671 }, + /* l2-speculative-read-access\000legacy cache\000Level 2 (or higher) last level cac... */ + { 70178 }, + /* l2-speculative-read-miss\000legacy cache\000Level 2 (or higher) last level cache... */ + { 70436 }, + /* l2-speculative-read-misses\000legacy cache\000Level 2 (or higher) last level cac... */ + { 70307 }, + /* l2-speculative-read-ops\000legacy cache\000Level 2 (or higher) last level cache ... */ + { 70052 }, + /* l2-speculative-read-reference\000legacy cache\000Level 2 (or higher) last level ... */ + { 69920 }, + /* l2-speculative-read-refs\000legacy cache\000Level 2 (or higher) last level cache... */ + { 69793 }, + /* l2-store\000legacy cache\000Level 2 (or higher) last level cache write accesses\... */ + { 65596 }, + /* l2-store-access\000legacy cache\000Level 2 (or higher) last level cache write ac... */ + { 66047 }, + /* l2-store-miss\000legacy cache\000Level 2 (or higher) last level cache write miss... */ + { 66277 }, + /* l2-store-misses\000legacy cache\000Level 2 (or higher) last level cache write mi... */ + { 66162 }, + /* l2-store-ops\000legacy cache\000Level 2 (or higher) last level cache write acces... */ + { 65935 }, + /* l2-store-reference\000legacy cache\000Level 2 (or higher) last level cache write... */ + { 65817 }, + /* l2-store-refs\000legacy cache\000Level 2 (or higher) last level cache write acce... */ + { 65704 }, + /* l2-stores\000legacy cache\000Level 2 (or higher) last level cache write accesses... */ + { 66390 }, + /* l2-stores-access\000legacy cache\000Level 2 (or higher) last level cache write a... */ + { 66845 }, + /* l2-stores-miss\000legacy cache\000Level 2 (or higher) last level cache write mis... */ + { 67077 }, + /* l2-stores-misses\000legacy cache\000Level 2 (or higher) last level cache write m... */ + { 66961 }, + /* l2-stores-ops\000legacy cache\000Level 2 (or higher) last level cache write acce... */ + { 66732 }, + /* l2-stores-reference\000legacy cache\000Level 2 (or higher) last level cache writ... */ + { 66613 }, + /* l2-stores-refs\000legacy cache\000Level 2 (or higher) last level cache write acc... */ + { 66499 }, + /* l2-write\000legacy cache\000Level 2 (or higher) last level cache write accesses\... */ + { 67191 }, + /* l2-write-access\000legacy cache\000Level 2 (or higher) last level cache write ac... */ + { 67642 }, + /* l2-write-miss\000legacy cache\000Level 2 (or higher) last level cache write miss... */ + { 67872 }, + /* l2-write-misses\000legacy cache\000Level 2 (or higher) last level cache write mi... */ + { 67757 }, + /* l2-write-ops\000legacy cache\000Level 2 (or higher) last level cache write acces... */ + { 67530 }, + /* l2-write-reference\000legacy cache\000Level 2 (or higher) last level cache write... */ + { 67412 }, + /* l2-write-refs\000legacy cache\000Level 2 (or higher) last level cache write acce... */ + { 67299 }, + /* llc\000legacy cache\000Last level cache read accesses\000legacy-cache-config=2\0... */ + { 55804 }, + /* llc-access\000legacy cache\000Last level cache read accesses\000legacy-cache-con... */ + { 62951 }, + /* llc-load\000legacy cache\000Last level cache read accesses\000legacy-cache-confi... */ + { 55882 }, + /* llc-load-access\000legacy cache\000Last level cache read accesses\000legacy-cach... */ + { 56233 }, + /* llc-load-miss\000legacy cache\000Last level cache read misses\000legacy-cache-co... */ + { 56417 }, + /* llc-load-misses\000legacy cache\000Last level cache read misses\000legacy-cache-... */ + { 56323 }, + /* llc-load-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-c... */ + { 56146 }, + /* llc-load-reference\000legacy cache\000Last level cache read accesses\000legacy-c... */ + { 56053 }, + /* llc-load-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-... */ + { 55965 }, + /* llc-loads\000legacy cache\000Last level cache read accesses\000legacy-cache-conf... */ + { 56509 }, + /* llc-loads-access\000legacy cache\000Last level cache read accesses\000legacy-cac... */ + { 56864 }, + /* llc-loads-miss\000legacy cache\000Last level cache read misses\000legacy-cache-c... */ + { 57050 }, + /* llc-loads-misses\000legacy cache\000Last level cache read misses\000legacy-cache... */ + { 56955 }, + /* llc-loads-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-... */ + { 56776 }, + /* llc-loads-reference\000legacy cache\000Last level cache read accesses\000legacy-... */ + { 56682 }, + /* llc-loads-refs\000legacy cache\000Last level cache read accesses\000legacy-cache... */ + { 56593 }, + /* llc-miss\000legacy cache\000Last level cache read misses\000legacy-cache-config=... */ + { 63125 }, + /* llc-misses\000legacy cache\000Last level cache read misses\000legacy-cache-confi... */ + { 63036 }, + /* llc-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-config... */ + { 62869 }, + /* llc-prefetch\000legacy cache\000Last level cache prefetch accesses\000legacy-cac... */ + { 59760 }, + /* llc-prefetch-access\000legacy cache\000Last level cache prefetch accesses\000leg... */ + { 60159 }, + /* llc-prefetch-miss\000legacy cache\000Last level cache prefetch misses\000legacy-... */ + { 60363 }, + /* llc-prefetch-misses\000legacy cache\000Last level cache prefetch misses\000legac... */ + { 60261 }, + /* llc-prefetch-ops\000legacy cache\000Last level cache prefetch accesses\000legacy... */ + { 60060 }, + /* llc-prefetch-reference\000legacy cache\000Last level cache prefetch accesses\000... */ + { 59955 }, + /* llc-prefetch-refs\000legacy cache\000Last level cache prefetch accesses\000legac... */ + { 59855 }, + /* llc-prefetches\000legacy cache\000Last level cache prefetch accesses\000legacy-c... */ + { 60463 }, + /* llc-prefetches-access\000legacy cache\000Last level cache prefetch accesses\000l... */ + { 60870 }, + /* llc-prefetches-miss\000legacy cache\000Last level cache prefetch misses\000legac... */ + { 61078 }, + /* llc-prefetches-misses\000legacy cache\000Last level cache prefetch misses\000leg... */ + { 60974 }, + /* llc-prefetches-ops\000legacy cache\000Last level cache prefetch accesses\000lega... */ + { 60769 }, + /* llc-prefetches-reference\000legacy cache\000Last level cache prefetch accesses\0... */ + { 60662 }, + /* llc-prefetches-refs\000legacy cache\000Last level cache prefetch accesses\000leg... */ + { 60560 }, + /* llc-read\000legacy cache\000Last level cache read accesses\000legacy-cache-confi... */ + { 57143 }, + /* llc-read-access\000legacy cache\000Last level cache read accesses\000legacy-cach... */ + { 57494 }, + /* llc-read-miss\000legacy cache\000Last level cache read misses\000legacy-cache-co... */ + { 57678 }, + /* llc-read-misses\000legacy cache\000Last level cache read misses\000legacy-cache-... */ + { 57584 }, + /* llc-read-ops\000legacy cache\000Last level cache read accesses\000legacy-cache-c... */ + { 57407 }, + /* llc-read-reference\000legacy cache\000Last level cache read accesses\000legacy-c... */ + { 57314 }, + /* llc-read-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-... */ + { 57226 }, + /* llc-reference\000legacy cache\000Last level cache read accesses\000legacy-cache-... */ + { 62781 }, + /* llc-refs\000legacy cache\000Last level cache read accesses\000legacy-cache-confi... */ + { 62698 }, + /* llc-speculative-load\000legacy cache\000Last level cache prefetch accesses\000le... */ + { 61939 }, + /* llc-speculative-load-access\000legacy cache\000Last level cache prefetch accesse... */ + { 62370 }, + /* llc-speculative-load-miss\000legacy cache\000Last level cache prefetch misses\00... */ + { 62590 }, + /* llc-speculative-load-misses\000legacy cache\000Last level cache prefetch misses\... */ + { 62480 }, + /* llc-speculative-load-ops\000legacy cache\000Last level cache prefetch accesses\0... */ + { 62263 }, + /* llc-speculative-load-reference\000legacy cache\000Last level cache prefetch acce... */ + { 62150 }, + /* llc-speculative-load-refs\000legacy cache\000Last level cache prefetch accesses\... */ + { 62042 }, + /* llc-speculative-read\000legacy cache\000Last level cache prefetch accesses\000le... */ + { 61180 }, + /* llc-speculative-read-access\000legacy cache\000Last level cache prefetch accesse... */ + { 61611 }, + /* llc-speculative-read-miss\000legacy cache\000Last level cache prefetch misses\00... */ + { 61831 }, + /* llc-speculative-read-misses\000legacy cache\000Last level cache prefetch misses\... */ + { 61721 }, + /* llc-speculative-read-ops\000legacy cache\000Last level cache prefetch accesses\0... */ + { 61504 }, + /* llc-speculative-read-reference\000legacy cache\000Last level cache prefetch acce... */ + { 61391 }, + /* llc-speculative-read-refs\000legacy cache\000Last level cache prefetch accesses\... */ + { 61283 }, + /* llc-store\000legacy cache\000Last level cache write accesses\000legacy-cache-con... */ + { 57770 }, + /* llc-store-access\000legacy cache\000Last level cache write accesses\000legacy-ca... */ + { 58145 }, + /* llc-store-miss\000legacy cache\000Last level cache write misses\000legacy-cache-... */ + { 58337 }, + /* llc-store-misses\000legacy cache\000Last level cache write misses\000legacy-cach... */ + { 58241 }, + /* llc-store-ops\000legacy cache\000Last level cache write accesses\000legacy-cache... */ + { 58052 }, + /* llc-store-reference\000legacy cache\000Last level cache write accesses\000legacy... */ + { 57953 }, + /* llc-store-refs\000legacy cache\000Last level cache write accesses\000legacy-cach... */ + { 57859 }, + /* llc-stores\000legacy cache\000Last level cache write accesses\000legacy-cache-co... */ + { 58431 }, + /* llc-stores-access\000legacy cache\000Last level cache write accesses\000legacy-c... */ + { 58810 }, + /* llc-stores-miss\000legacy cache\000Last level cache write misses\000legacy-cache... */ + { 59004 }, + /* llc-stores-misses\000legacy cache\000Last level cache write misses\000legacy-cac... */ + { 58907 }, + /* llc-stores-ops\000legacy cache\000Last level cache write accesses\000legacy-cach... */ + { 58716 }, + /* llc-stores-reference\000legacy cache\000Last level cache write accesses\000legac... */ + { 58616 }, + /* llc-stores-refs\000legacy cache\000Last level cache write accesses\000legacy-cac... */ + { 58521 }, + /* llc-write\000legacy cache\000Last level cache write accesses\000legacy-cache-con... */ + { 59099 }, + /* llc-write-access\000legacy cache\000Last level cache write accesses\000legacy-ca... */ + { 59474 }, + /* llc-write-miss\000legacy cache\000Last level cache write misses\000legacy-cache-... */ + { 59666 }, + /* llc-write-misses\000legacy cache\000Last level cache write misses\000legacy-cach... */ + { 59570 }, + /* llc-write-ops\000legacy cache\000Last level cache write accesses\000legacy-cache... */ + { 59381 }, + /* llc-write-reference\000legacy cache\000Last level cache write accesses\000legacy... */ + { 59282 }, + /* llc-write-refs\000legacy cache\000Last level cache write accesses\000legacy-cach... */ + { 59188 }, + /* node\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\000\... */ + { 114128 }, + /* node-access\000legacy cache\000Local memory read accesses\000legacy-cache-config... */ + { 121053 }, + /* node-load\000legacy cache\000Local memory read accesses\000legacy-cache-config=6... */ + { 114203 }, + /* node-load-access\000legacy cache\000Local memory read accesses\000legacy-cache-c... */ + { 114542 }, + /* node-load-miss\000legacy cache\000Local memory read misses\000legacy-cache-confi... */ + { 114720 }, + /* node-load-misses\000legacy cache\000Local memory read misses\000legacy-cache-con... */ + { 114629 }, + /* node-load-ops\000legacy cache\000Local memory read accesses\000legacy-cache-conf... */ + { 114458 }, + /* node-load-reference\000legacy cache\000Local memory read accesses\000legacy-cach... */ + { 114368 }, + /* node-load-refs\000legacy cache\000Local memory read accesses\000legacy-cache-con... */ + { 114283 }, + /* node-loads\000legacy cache\000Local memory read accesses\000legacy-cache-config=... */ + { 114809 }, + /* node-loads-access\000legacy cache\000Local memory read accesses\000legacy-cache-... */ + { 115152 }, + /* node-loads-miss\000legacy cache\000Local memory read misses\000legacy-cache-conf... */ + { 115332 }, + /* node-loads-misses\000legacy cache\000Local memory read misses\000legacy-cache-co... */ + { 115240 }, + /* node-loads-ops\000legacy cache\000Local memory read accesses\000legacy-cache-con... */ + { 115067 }, + /* node-loads-reference\000legacy cache\000Local memory read accesses\000legacy-cac... */ + { 114976 }, + /* node-loads-refs\000legacy cache\000Local memory read accesses\000legacy-cache-co... */ + { 114890 }, + /* node-miss\000legacy cache\000Local memory read misses\000legacy-cache-config=0x1... */ + { 121221 }, + /* node-misses\000legacy cache\000Local memory read misses\000legacy-cache-config=0... */ + { 121135 }, + /* node-ops\000legacy cache\000Local memory read accesses\000legacy-cache-config=6\... */ + { 120974 }, + /* node-prefetch\000legacy cache\000Local memory prefetch accesses\000legacy-cache-... */ + { 117955 }, + /* node-prefetch-access\000legacy cache\000Local memory prefetch accesses\000legacy... */ + { 118342 }, + /* node-prefetch-miss\000legacy cache\000Local memory prefetch misses\000legacy-cac... */ + { 118540 }, + /* node-prefetch-misses\000legacy cache\000Local memory prefetch misses\000legacy-c... */ + { 118441 }, + /* node-prefetch-ops\000legacy cache\000Local memory prefetch accesses\000legacy-ca... */ + { 118246 }, + /* node-prefetch-reference\000legacy cache\000Local memory prefetch accesses\000leg... */ + { 118144 }, + /* node-prefetch-refs\000legacy cache\000Local memory prefetch accesses\000legacy-c... */ + { 118047 }, + /* node-prefetches\000legacy cache\000Local memory prefetch accesses\000legacy-cach... */ + { 118637 }, + /* node-prefetches-access\000legacy cache\000Local memory prefetch accesses\000lega... */ + { 119032 }, + /* node-prefetches-miss\000legacy cache\000Local memory prefetch misses\000legacy-c... */ + { 119234 }, + /* node-prefetches-misses\000legacy cache\000Local memory prefetch misses\000legacy... */ + { 119133 }, + /* node-prefetches-ops\000legacy cache\000Local memory prefetch accesses\000legacy-... */ + { 118934 }, + /* node-prefetches-reference\000legacy cache\000Local memory prefetch accesses\000l... */ + { 118830 }, + /* node-prefetches-refs\000legacy cache\000Local memory prefetch accesses\000legacy... */ + { 118731 }, + /* node-read\000legacy cache\000Local memory read accesses\000legacy-cache-config=6... */ + { 115422 }, + /* node-read-access\000legacy cache\000Local memory read accesses\000legacy-cache-c... */ + { 115761 }, + /* node-read-miss\000legacy cache\000Local memory read misses\000legacy-cache-confi... */ + { 115939 }, + /* node-read-misses\000legacy cache\000Local memory read misses\000legacy-cache-con... */ + { 115848 }, + /* node-read-ops\000legacy cache\000Local memory read accesses\000legacy-cache-conf... */ + { 115677 }, + /* node-read-reference\000legacy cache\000Local memory read accesses\000legacy-cach... */ + { 115587 }, + /* node-read-refs\000legacy cache\000Local memory read accesses\000legacy-cache-con... */ + { 115502 }, + /* node-reference\000legacy cache\000Local memory read accesses\000legacy-cache-con... */ + { 120889 }, + /* node-refs\000legacy cache\000Local memory read accesses\000legacy-cache-config=6... */ + { 120809 }, + /* node-speculative-load\000legacy cache\000Local memory prefetch accesses\000legac... */ + { 120071 }, + /* node-speculative-load-access\000legacy cache\000Local memory prefetch accesses\0... */ + { 120490 }, + /* node-speculative-load-miss\000legacy cache\000Local memory prefetch misses\000le... */ + { 120704 }, + /* node-speculative-load-misses\000legacy cache\000Local memory prefetch misses\000... */ + { 120597 }, + /* node-speculative-load-ops\000legacy cache\000Local memory prefetch accesses\000l... */ + { 120386 }, + /* node-speculative-load-reference\000legacy cache\000Local memory prefetch accesse... */ + { 120276 }, + /* node-speculative-load-refs\000legacy cache\000Local memory prefetch accesses\000... */ + { 120171 }, + /* node-speculative-read\000legacy cache\000Local memory prefetch accesses\000legac... */ + { 119333 }, + /* node-speculative-read-access\000legacy cache\000Local memory prefetch accesses\0... */ + { 119752 }, + /* node-speculative-read-miss\000legacy cache\000Local memory prefetch misses\000le... */ + { 119966 }, + /* node-speculative-read-misses\000legacy cache\000Local memory prefetch misses\000... */ + { 119859 }, + /* node-speculative-read-ops\000legacy cache\000Local memory prefetch accesses\000l... */ + { 119648 }, + /* node-speculative-read-reference\000legacy cache\000Local memory prefetch accesse... */ + { 119538 }, + /* node-speculative-read-refs\000legacy cache\000Local memory prefetch accesses\000... */ + { 119433 }, + /* node-store\000legacy cache\000Local memory write accesses\000legacy-cache-config... */ + { 116028 }, + /* node-store-access\000legacy cache\000Local memory write accesses\000legacy-cache... */ + { 116391 }, + /* node-store-miss\000legacy cache\000Local memory write misses\000legacy-cache-con... */ + { 116577 }, + /* node-store-misses\000legacy cache\000Local memory write misses\000legacy-cache-c... */ + { 116484 }, + /* node-store-ops\000legacy cache\000Local memory write accesses\000legacy-cache-co... */ + { 116301 }, + /* node-store-reference\000legacy cache\000Local memory write accesses\000legacy-ca... */ + { 116205 }, + /* node-store-refs\000legacy cache\000Local memory write accesses\000legacy-cache-c... */ + { 116114 }, + /* node-stores\000legacy cache\000Local memory write accesses\000legacy-cache-confi... */ + { 116668 }, + /* node-stores-access\000legacy cache\000Local memory write accesses\000legacy-cach... */ + { 117035 }, + /* node-stores-miss\000legacy cache\000Local memory write misses\000legacy-cache-co... */ + { 117223 }, + /* node-stores-misses\000legacy cache\000Local memory write misses\000legacy-cache-... */ + { 117129 }, + /* node-stores-ops\000legacy cache\000Local memory write accesses\000legacy-cache-c... */ + { 116944 }, + /* node-stores-reference\000legacy cache\000Local memory write accesses\000legacy-c... */ + { 116847 }, + /* node-stores-refs\000legacy cache\000Local memory write accesses\000legacy-cache-... */ + { 116755 }, + /* node-write\000legacy cache\000Local memory write accesses\000legacy-cache-config... */ + { 117315 }, + /* node-write-access\000legacy cache\000Local memory write accesses\000legacy-cache... */ + { 117678 }, + /* node-write-miss\000legacy cache\000Local memory write misses\000legacy-cache-con... */ + { 117864 }, + /* node-write-misses\000legacy cache\000Local memory write misses\000legacy-cache-c... */ + { 117771 }, + /* node-write-ops\000legacy cache\000Local memory write accesses\000legacy-cache-co... */ + { 117588 }, + /* node-write-reference\000legacy cache\000Local memory write accesses\000legacy-ca... */ + { 117492 }, + /* node-write-refs\000legacy cache\000Local memory write accesses\000legacy-cache-c... */ + { 117401 }, + /* ref-cycles\000legacy hardware\000Total cycles; not affected by CPU frequency sca... */ + { 123400 }, + /* stalled-cycles-backend\000legacy hardware\000Stalled cycles during retirement [T... */ + { 123094 }, + /* stalled-cycles-frontend\000legacy hardware\000Stalled cycles during issue [This ... */ + { 122795 }, }; static const struct compact_pmu_event pmu_events__common_software[] = { -{ 124563 }, /* alignment-faults\000software\000Number of kernel handled memory alignment faults\000config=7\000\00000\000\000\000\000\000 */ -{ 124862 }, /* bpf-output\000software\000An event used by BPF programs to write to the perf ring buffer\000config=0xa\000\00000\000\000\000\000\000 */ -{ 124964 }, /* cgroup-switches\000software\000Number of context switches to a task in a different cgroup\000config=0xb\000\00000\000\000\000\000\000 */ -{ 123885 }, /* context-switches\000software\000Number of context switches [This event is an alias of cs]\000config=3\000\00000\000\000\000\000\000 */ -{ 123521 }, /* cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\000\000001e-6msec\000\000\000\000\000 */ -{ 124087 }, /* cpu-migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of migrations]\000config=4\000\00000\000\000\000\000\000 */ -{ 123986 }, /* cs\000software\000Number of context switches [This event is an alias of context-switches]\000config=3\000\00000\000\000\000\000\000 */ -{ 124782 }, /* dummy\000software\000A placeholder event that doesn't count anything\000config=9\000\00000\000\000\000\000\000 */ -{ 124655 }, /* emulation-faults\000software\000Number of kernel handled unimplemented instruction faults handled through emulation\000config=8\000\00000\000\000\000\000\000 */ -{ 123695 }, /* faults\000software\000Number of page faults [This event is an alias of page-faults]\000config=2\000\00000\000\000\000\000\000 */ -{ 124460 }, /* major-faults\000software\000Number of major page faults. Major faults require I/O to handle\000config=6\000\00000\000\000\000\000\000 */ -{ 124219 }, /* migrations\000software\000Number of times a process has migrated to a new CPU [This event is an alias of cpu-migrations]\000config=4\000\00000\000\000\000\000\000 */ -{ 124351 }, /* minor-faults\000software\000Number of minor page faults. Minor faults don't require I/O to handle\000config=5\000\00000\000\000\000\000\000 */ -{ 123790 }, /* page-faults\000software\000Number of page faults [This event is an alias of faults]\000config=2\000\00000\000\000\000\000\000 */ -{ 123607 }, /* task-clock\000software\000Per-task high-resolution timer based event\000config=1\000\000001e-6msec\000\000\000\000\000 */ + /* alignment-faults\000software\000Number of kernel handled memory alignment faults... */ + { 124563 }, + /* bpf-output\000software\000An event used by BPF programs to write to the perf rin... */ + { 124862 }, + /* cgroup-switches\000software\000Number of context switches to a task in a differe... */ + { 124964 }, + /* context-switches\000software\000Number of context switches [This event is an ali... */ + { 123885 }, + /* cpu-clock\000software\000Per-CPU high-resolution timer based event\000config=0\0... */ + { 123521 }, + /* cpu-migrations\000software\000Number of times a process has migrated to a new CP... */ + { 124087 }, + /* cs\000software\000Number of context switches [This event is an alias of context-... */ + { 123986 }, + /* dummy\000software\000A placeholder event that doesn't count anything\000config=9... */ + { 124782 }, + /* emulation-faults\000software\000Number of kernel handled unimplemented instructi... */ + { 124655 }, + /* faults\000software\000Number of page faults [This event is an alias of page-faul... */ + { 123695 }, + /* major-faults\000software\000Number of major page faults. Major faults require I/... */ + { 124460 }, + /* migrations\000software\000Number of times a process has migrated to a new CPU [T... */ + { 124219 }, + /* minor-faults\000software\000Number of minor page faults. Minor faults don't requ... */ + { 124351 }, + /* page-faults\000software\000Number of page faults [This event is an alias of faul... */ + { 123790 }, + /* task-clock\000software\000Per-task high-resolution timer based event\000config=1... */ + { 123607 }, }; static const struct compact_pmu_event pmu_events__common_tool[] = { -{ 126205 }, /* core_wide\000tool\0001 if not SMT, if SMT are events being gathered on all SMT threads 1 otherwise 0\000config=0xd\000\00000\000\000\000\000\000 */ -{ 125072 }, /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000\000\000\000 */ -{ 125286 }, /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000\000\000\000 */ -{ 125362 }, /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000\000\000\000 */ -{ 125507 }, /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000\000\000\000 */ -{ 125610 }, /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000\000\000\000 */ -{ 125727 }, /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000\000\000\000 */ -{ 125803 }, /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000\000\000\000 */ -{ 125889 }, /* slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000\000\000\000 */ -{ 125999 }, /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000\000\000\000 */ -{ 125218 }, /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000\000\000\000 */ -{ 126106 }, /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000\000\000\000 */ -{ 126319 }, /* target_cpu\000tool\0001 if CPUs being analyzed, 0 if threads/processes\000config=0xe\000\00000\000\000\000\000\000 */ -{ 125148 }, /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000\000\000\000 */ + /* core_wide\000tool\0001 if not SMT, if SMT are events being gathered on all SMT t... */ + { 126205 }, + /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000... */ + { 125072 }, + /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000... */ + { 125286 }, + /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with ... */ + { 125362 }, + /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPU... */ + { 125507 }, + /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be mul... */ + { 125610 }, + /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000... */ + { 125727 }, + /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000co... */ + { 125803 }, + /* slots\000tool\000Number of functional units that in parallel can execute parts o... */ + { 125889 }, + /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enabl... */ + { 125999 }, + /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\0... */ + { 125218 }, + /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per s... */ + { 126106 }, + /* target_cpu\000tool\0001 if CPUs being analyzed, 0 if threads/processes\000config... */ + { 126319 }, + /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000... */ + { 125148 }, }; static const struct pmu_table_entry pmu_events__common[] = { -{ - .entries = pmu_events__common_default_core, - .num_entries = ARRAY_SIZE(pmu_events__common_default_core), - .pmu_name = { 0 /* default_core\000 */ }, -}, -{ - .entries = pmu_events__common_software, - .num_entries = ARRAY_SIZE(pmu_events__common_software), - .pmu_name = { 123512 /* software\000 */ }, -}, -{ - .entries = pmu_events__common_tool, - .num_entries = ARRAY_SIZE(pmu_events__common_tool), - .pmu_name = { 125067 /* tool\000 */ }, -}, + { + .entries = pmu_events__common_default_core, + .num_entries = ARRAY_SIZE(pmu_events__common_default_core), + .pmu_name = { 0 /* default_core\000 */ }, + }, + { + .entries = pmu_events__common_software, + .num_entries = ARRAY_SIZE(pmu_events__common_software), + .pmu_name = { 123512 /* software\000 */ }, + }, + { + .entries = pmu_events__common_tool, + .num_entries = ARRAY_SIZE(pmu_events__common_tool), + .pmu_name = { 125067 /* tool\000 */ }, + }, }; static const struct compact_pmu_event pmu_metrics__common_default_core[] = { -{ 127956 }, /* CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@) / (duration_time * 1e9)\000\000Average CPU utilization\000\0001CPUs\000\000\000\000011 */ -{ 129583 }, /* backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001 */ -{ 129933 }, /* branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011 */ -{ 130113 }, /* branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001 */ -{ 128142 }, /* cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switches@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Context switches per CPU second\000\0001cs/sec\000\000\000\000011 */ -{ 129757 }, /* cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011 */ -{ 130549 }, /* dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001 */ -{ 129404 }, /* frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001 */ -{ 128866 }, /* insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001 */ -{ 130655 }, /* itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001 */ -{ 130761 }, /* l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001 */ -{ 130217 }, /* l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001 */ -{ 130434 }, /* l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001 */ -{ 130333 }, /* llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001 */ -{ 128375 }, /* migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011 */ -{ 128635 }, /* page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011 */ -{ 128979 }, /* stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001 */ + /* CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #targ... */ + { 127956 }, + /* backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if ... */ + { 129583 }, + /* branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-c... */ + { 129933 }, + /* branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > ... */ + { 130113 }, + /* cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switc... */ + { 128142 }, + /* cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu... */ + { 129757 }, + /* dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_r... */ + { 130549 }, + /* frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles i... */ + { 129404 }, + /* insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\0... */ + { 128866 }, + /* itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_r... */ + { 130655 }, + /* l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcac... */ + { 130761 }, + /* l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\0... */ + { 130217 }, + /* l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\0... */ + { 130434 }, + /* llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate ... */ + { 130333 }, + /* migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-mig... */ + { 128375 }, + /* page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faul... */ + { 128635 }, + /* stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, s... */ + { 128979 }, }; static const struct pmu_table_entry pmu_metrics__common[] = { -{ - .entries = pmu_metrics__common_default_core, - .num_entries = ARRAY_SIZE(pmu_metrics__common_default_core), - .pmu_name = { 0 /* default_core\000 */ }, -}, + { + .entries = pmu_metrics__common_default_core, + .num_entries = ARRAY_SIZE(pmu_metrics__common_default_core), + .pmu_name = { 0 /* default_core\000 */ }, + }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_default_core[] = { -{ 126403 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000\000\000\000 */ -{ 126465 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000\000\000\000 */ -{ 126727 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000\000\000\000 */ -{ 126860 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000\000\000\000 */ -{ 126527 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\000\000Attributable Level 3 cache access, read\000 */ -{ 126625 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000\000\000\000 */ + /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000... */ + { 126403 }, + /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000... */ + { 126465 }, + /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispat... */ + { 126727 }, + /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) t... */ + { 126860 }, + /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000\000\00... */ + { 126527 }, + /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,pe... */ + { 126625 }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_ddrc[] = { -{ 126993 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000\000\000\000\000 */ + /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000... */ + { 126993 }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_l3c[] = { -{ 127355 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000\000\000\000\000 */ + /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\0... */ + { 127355 }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_cbox[] = { -{ 127229 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000\000 */ -{ 127283 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000\000\000 */ -{ 127075 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000\000\000\000\000 */ + /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000\000\000\000... */ + { 127229 }, + /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000\000\000... */ + { 127283 }, + /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted fro... */ + { 127075 }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc[] = { -{ 127538 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\000\000\000\000 */ + /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000\... */ + { 127538 }, }; static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc_free_running[] = { -{ 127447 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000\000\000\000\000 */ + /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12... */ + { 127447 }, }; static const struct pmu_table_entry pmu_events__test_soc_cpu[] = { -{ - .entries = pmu_events__test_soc_cpu_default_core, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core), - .pmu_name = { 0 /* default_core\000 */ }, -}, -{ - .entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc), - .pmu_name = { 126978 /* hisi_sccl,ddrc\000 */ }, -}, -{ - .entries = pmu_events__test_soc_cpu_hisi_sccl_l3c, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c), - .pmu_name = { 127341 /* hisi_sccl,l3c\000 */ }, -}, -{ - .entries = pmu_events__test_soc_cpu_uncore_cbox, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox), - .pmu_name = { 127063 /* uncore_cbox\000 */ }, -}, -{ - .entries = pmu_events__test_soc_cpu_uncore_imc, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc), - .pmu_name = { 127527 /* uncore_imc\000 */ }, -}, -{ - .entries = pmu_events__test_soc_cpu_uncore_imc_free_running, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running), - .pmu_name = { 127423 /* uncore_imc_free_running\000 */ }, -}, + { + .entries = pmu_events__test_soc_cpu_default_core, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core), + .pmu_name = { 0 /* default_core\000 */ }, + }, + { + .entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc), + .pmu_name = { 126978 /* hisi_sccl,ddrc\000 */ }, + }, + { + .entries = pmu_events__test_soc_cpu_hisi_sccl_l3c, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c), + .pmu_name = { 127341 /* hisi_sccl,l3c\000 */ }, + }, + { + .entries = pmu_events__test_soc_cpu_uncore_cbox, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox), + .pmu_name = { 127063 /* uncore_cbox\000 */ }, + }, + { + .entries = pmu_events__test_soc_cpu_uncore_imc, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc), + .pmu_name = { 127527 /* uncore_imc\000 */ }, + }, + { + .entries = pmu_events__test_soc_cpu_uncore_imc_free_running, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running), + .pmu_name = { 127423 /* uncore_imc_free_running\000 */ }, + }, }; static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = { -{ 130909 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000 */ -{ 131598 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000 */ -{ 131368 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000 */ -{ 131463 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000 */ -{ 131663 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ -{ 131732 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ -{ 130996 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000 */ -{ 130932 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000 */ -{ 131870 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000 */ -{ 131803 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\000000 */ -{ 131826 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\000000 */ -{ 131849 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\000000 */ -{ 131296 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000 */ -{ 131163 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ -{ 131228 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ + /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000 */ + { 130909 }, + /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000... */ + { 131598 }, + /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqs... */ + { 131368 }, + /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data... */ + { 131463 }, + /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000... */ + { 131663 }, + /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\0... */ + { 131732 }, + /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.t... */ + { 130996 }, + /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\... */ + { 130932 }, + /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\... */ + { 131870 }, + /* M1\000\000ipc + M2\000\000\000\000\000\000\000\000000 */ + { 131803 }, + /* M2\000\000ipc + M1\000\000\000\000\000\000\000\000000 */ + { 131826 }, + /* M3\000\0001 / M3\000\000\000\000\000\000\000\000000 */ + { 131849 }, + /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\... */ + { 131296 }, + /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\0... */ + { 131163 }, + /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\00... */ + { 131228 }, }; static const struct pmu_table_entry pmu_metrics__test_soc_cpu[] = { -{ - .entries = pmu_metrics__test_soc_cpu_default_core, - .num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core), - .pmu_name = { 0 /* default_core\000 */ }, -}, + { + .entries = pmu_metrics__test_soc_cpu_default_core, + .num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core), + .pmu_name = { 0 /* default_core\000 */ }, + }, }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ccn_pmu[] = { -{ 127717 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000\000\000\000 */ + /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x0... */ + { 127717 }, }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_cmn_pmu[] = { -{ 127813 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000\000\000\000 */ + /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first looku... */ + { 127813 }, }; static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ddr_pmu[] = { -{ 127622 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000\000\000\000 */ + /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8... */ + { 127622 }, }; static const struct pmu_table_entry pmu_events__test_soc_sys[] = { -{ - .entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu), - .pmu_name = { 127698 /* uncore_sys_ccn_pmu\000 */ }, -}, -{ - .entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu), - .pmu_name = { 127794 /* uncore_sys_cmn_pmu\000 */ }, -}, -{ - .entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu, - .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu), - .pmu_name = { 127603 /* uncore_sys_ddr_pmu\000 */ }, -}, + { + .entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu), + .pmu_name = { 127698 /* uncore_sys_ccn_pmu\000 */ }, + }, + { + .entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu), + .pmu_name = { 127794 /* uncore_sys_cmn_pmu\000 */ }, + }, + { + .entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu), + .pmu_name = { 127603 /* uncore_sys_ddr_pmu\000 */ }, + }, }; /* Struct used to make the PMU event table implementation opaque to callers. */ struct pmu_events_table { - const struct pmu_table_entry *pmus; - uint32_t num_pmus; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { - const struct pmu_table_entry *pmus; - uint32_t num_pmus; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* @@ -2791,10 +5416,10 @@ struct pmu_metrics_table { * The cpuid can contain any character other than the comma. */ struct pmu_events_map { - const char *arch; - const char *cpuid; - struct pmu_events_table event_table; - struct pmu_metrics_table metric_table; + const char *arch; + const char *cpuid; + struct pmu_events_table event_table; + struct pmu_metrics_table metric_table; }; /* @@ -2915,456 +5540,455 @@ static void decompress_metric(int offset, struct pmu_metric *pm) } static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, - const struct pmu_table_entry *pmu, - pmu_event_iter_fn fn, - void *data) + const struct pmu_table_entry *pmu, + pmu_event_iter_fn fn, + void *data) { - int ret; - struct pmu_event pe = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; + int ret; + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; - for (uint32_t i = 0; i < pmu->num_entries; i++) { - decompress_event(pmu->entries[i].offset, &pe); - if (!pe.name) - continue; - ret = fn(&pe, table, data); - if (ret) - return ret; - } - return 0; + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_event(pmu->entries[i].offset, &pe); + if (!pe.name) + continue; + ret = fn(&pe, table, data); + if (ret) + return ret; + } + return 0; } static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, - const struct pmu_table_entry *pmu, - const char *name, - pmu_event_iter_fn fn, - void *data) + const struct pmu_table_entry *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) { - struct pmu_event pe = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; - int low = 0, high = pmu->num_entries - 1; + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + int low = 0, high = pmu->num_entries - 1; - while (low <= high) { - int cmp, mid = (low + high) / 2; + while (low <= high) { + int cmp, mid = (low + high) / 2; - decompress_event(pmu->entries[mid].offset, &pe); + decompress_event(pmu->entries[mid].offset, &pe); - if (!pe.name && !name) - goto do_call; + if (!pe.name && !name) + goto do_call; - if (!pe.name && name) { - low = mid + 1; - continue; - } - if (pe.name && !name) { - high = mid - 1; - continue; - } + if (!pe.name && name) { + low = mid + 1; + continue; + } + if (pe.name && !name) { + high = mid - 1; + continue; + } - cmp = strcasecmp(pe.name, name); - if (cmp < 0) { - low = mid + 1; - continue; - } - if (cmp > 0) { - high = mid - 1; - continue; - } + cmp = strcasecmp(pe.name, name); + if (cmp < 0) { + low = mid + 1; + continue; + } + if (cmp > 0) { + high = mid - 1; + continue; + } do_call: - return fn ? fn(&pe, table, data) : 0; - } - return PMU_EVENTS__NOT_FOUND; + return fn ? fn(&pe, table, data) : 0; + } + return PMU_EVENTS__NOT_FOUND; } int pmu_events_table__for_each_event(const struct pmu_events_table *table, - struct perf_pmu *pmu, - pmu_event_iter_fn fn, - void *data) + struct perf_pmu *pmu, + pmu_event_iter_fn fn, + void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; + if (!table) + return 0; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; + if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) + continue; - ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); - if (ret) - return ret; - } - return 0; + ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); + if (ret) + return ret; + } + return 0; } int pmu_events_table__find_event(const struct pmu_events_table *table, - struct perf_pmu *pmu, - const char *name, - pmu_event_iter_fn fn, - void *data) + struct perf_pmu *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) { - if (!table) - return PMU_EVENTS__NOT_FOUND; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; + if (!table) + return PMU_EVENTS__NOT_FOUND; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; + if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) + continue; - ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); - if (ret != PMU_EVENTS__NOT_FOUND) - return ret; - } - return PMU_EVENTS__NOT_FOUND; + ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); + if (ret != PMU_EVENTS__NOT_FOUND) + return ret; + } + return PMU_EVENTS__NOT_FOUND; } -size_t pmu_events_table__num_events(const struct pmu_events_table *table, - struct perf_pmu *pmu) +size_t pmu_events_table__num_events(const struct pmu_events_table *table, struct perf_pmu *pmu) { - size_t count = 0; + size_t count = 0; - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + if (!table) + return 0; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (perf_pmu__name_wildcard_match(pmu, pmu_name)) - count += table_pmu->num_entries; - } - return count; + if (perf_pmu__name_wildcard_match(pmu, pmu_name)) + count += table_pmu->num_entries; + } + return count; } static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, - const struct pmu_table_entry *pmu, - pmu_metric_iter_fn fn, - void *data) + const struct pmu_table_entry *pmu, + pmu_metric_iter_fn fn, + void *data) { - int ret; - struct pmu_metric pm = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; + int ret; + struct pmu_metric pm = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; - for (uint32_t i = 0; i < pmu->num_entries; i++) { - decompress_metric(pmu->entries[i].offset, &pm); - if (!pm.metric_expr) - continue; - ret = fn(&pm, table, data); - if (ret) - return ret; - } - return 0; + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_metric(pmu->entries[i].offset, &pm); + if (!pm.metric_expr) + continue; + ret = fn(&pm, table, data); + if (ret) + return ret; + } + return 0; } static int pmu_metrics_table__find_metric_pmu(const struct pmu_metrics_table *table, - const struct pmu_table_entry *pmu, - const char *metric, - pmu_metric_iter_fn fn, - void *data) + const struct pmu_table_entry *pmu, + const char *metric, + pmu_metric_iter_fn fn, + void *data) { - struct pmu_metric pm = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; - int low = 0, high = pmu->num_entries - 1; + struct pmu_metric pm = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + int low = 0, high = pmu->num_entries - 1; - while (low <= high) { - int cmp, mid = (low + high) / 2; + while (low <= high) { + int cmp, mid = (low + high) / 2; - decompress_metric(pmu->entries[mid].offset, &pm); + decompress_metric(pmu->entries[mid].offset, &pm); - if (!pm.metric_name && !metric) - goto do_call; + if (!pm.metric_name && !metric) + goto do_call; - if (!pm.metric_name && metric) { - low = mid + 1; - continue; - } - if (pm.metric_name && !metric) { - high = mid - 1; - continue; - } + if (!pm.metric_name && metric) { + low = mid + 1; + continue; + } + if (pm.metric_name && !metric) { + high = mid - 1; + continue; + } - cmp = strcmp(pm.metric_name, metric); - if (cmp < 0) { - low = mid + 1; - continue; - } - if (cmp > 0) { - high = mid - 1; - continue; - } + cmp = strcmp(pm.metric_name, metric); + if (cmp < 0) { + low = mid + 1; + continue; + } + if (cmp > 0) { + high = mid - 1; + continue; + } do_call: - return fn ? fn(&pm, table, data) : 0; - } - return PMU_METRICS__NOT_FOUND; + return fn ? fn(&pm, table, data) : 0; + } + return PMU_METRICS__NOT_FOUND; } int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, - pmu_metric_iter_fn fn, - void *data) + pmu_metric_iter_fn fn, + void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - int ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], - fn, data); + if (!table) + return 0; + for (size_t i = 0; i < table->num_pmus; i++) { + int ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } int pmu_metrics_table__find_metric(const struct pmu_metrics_table *table, - struct perf_pmu *pmu, - const char *metric, - pmu_metric_iter_fn fn, - void *data) + struct perf_pmu *pmu, + const char *metric, + pmu_metric_iter_fn fn, + void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; + if (!table) + return 0; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; + if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) + continue; - ret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); - if (ret != PMU_METRICS__NOT_FOUND) - return ret; - } - return PMU_METRICS__NOT_FOUND; + ret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); + if (ret != PMU_METRICS__NOT_FOUND) + return ret; + } + return PMU_METRICS__NOT_FOUND; } static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) { - static struct { - const struct pmu_events_map *map; - struct perf_cpu cpu; - } last_result; - static struct { - const struct pmu_events_map *map; - char *cpuid; - } last_map_search; - static bool has_last_result, has_last_map_search; - const struct pmu_events_map *map = NULL; - char *cpuid = NULL; - size_t i; + static struct { + const struct pmu_events_map *map; + struct perf_cpu cpu; + } last_result; + static struct { + const struct pmu_events_map *map; + char *cpuid; + } last_map_search; + static bool has_last_result, has_last_map_search; + const struct pmu_events_map *map = NULL; + char *cpuid = NULL; + size_t i; - if (has_last_result && last_result.cpu.cpu == cpu.cpu) - return last_result.map; + if (has_last_result && last_result.cpu.cpu == cpu.cpu) + return last_result.map; - cpuid = get_cpuid_allow_env_override(cpu); + cpuid = get_cpuid_allow_env_override(cpu); - /* - * On some platforms which uses cpus map, cpuid can be NULL for - * PMUs other than CORE PMUs. - */ - if (!cpuid) - goto out_update_last_result; + /* + * On some platforms which uses cpus map, cpuid can be NULL for + * PMUs other than CORE PMUs. + */ + if (!cpuid) + goto out_update_last_result; - if (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { - map = last_map_search.map; - free(cpuid); - } else { - i = 0; - for (;;) { - map = &pmu_events_map[i++]; + if (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { + map = last_map_search.map; + free(cpuid); + } else { + i = 0; + for (;;) { + map = &pmu_events_map[i++]; - if (!map->arch) { - map = NULL; - break; - } + if (!map->arch) { + map = NULL; + break; + } - if (!strcmp_cpuid_str(map->cpuid, cpuid)) - break; - } - free(last_map_search.cpuid); - last_map_search.cpuid = cpuid; - last_map_search.map = map; - has_last_map_search = true; - } + if (!strcmp_cpuid_str(map->cpuid, cpuid)) + break; + } + free(last_map_search.cpuid); + last_map_search.cpuid = cpuid; + last_map_search.map = map; + has_last_map_search = true; + } out_update_last_result: - last_result.cpu = cpu; - last_result.map = map; - has_last_result = true; - return map; + last_result.cpu = cpu; + last_result.map = map; + has_last_result = true; + return map; } static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) { - struct perf_cpu cpu = {-1}; + struct perf_cpu cpu = { -1 }; - if (pmu) { - for (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { - const char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; + if (pmu) { + for (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { + const char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; - if (!strcmp(pmu_name, pmu->name)) { - const struct pmu_events_map *map = &pmu_events_map[0]; + if (!strcmp(pmu_name, pmu->name)) { + const struct pmu_events_map *map = &pmu_events_map[0]; - while (strcmp("common", map->arch)) - map++; - return map; - } - } - cpu = perf_cpu_map__min(pmu->cpus); - } - return map_for_cpu(cpu); + while (strcmp("common", map->arch)) + map++; + return map; + } + } + cpu = perf_cpu_map__min(pmu->cpus); + } + return map_for_cpu(cpu); } const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { - const struct pmu_events_map *map = map_for_pmu(pmu); + const struct pmu_events_map *map = map_for_pmu(pmu); - if (!map) - return NULL; + if (!map) + return NULL; - if (!pmu) - return &map->event_table; + if (!pmu) + return &map->event_table; - for (size_t i = 0; i < map->event_table.num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + for (size_t i = 0; i < map->event_table.num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (perf_pmu__name_wildcard_match(pmu, pmu_name)) - return &map->event_table; - } - return NULL; + if (perf_pmu__name_wildcard_match(pmu, pmu_name)) + return &map->event_table; + } + return NULL; } const struct pmu_events_table *perf_pmu__default_core_events_table(void) { - int i = 0; + int i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; + for (;;) { + const struct pmu_events_map *map = &pmu_events_map[i++]; - if (!map->arch) - break; + if (!map->arch) + break; - if (!strcmp(map->cpuid, "common")) - return &map->event_table; - } - return NULL; + if (!strcmp(map->cpuid, "common")) + return &map->event_table; + } + return NULL; } const struct pmu_metrics_table *pmu_metrics_table__find(void) { - struct perf_cpu cpu = {-1}; - const struct pmu_events_map *map = map_for_cpu(cpu); + struct perf_cpu cpu = { -1 }; + const struct pmu_events_map *map = map_for_cpu(cpu); - return map ? &map->metric_table : NULL; + return map ? &map->metric_table : NULL; } const struct pmu_metrics_table *pmu_metrics_table__default(void) { - int i = 0; + int i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; + for (;;) { + const struct pmu_events_map *map = &pmu_events_map[i++]; - if (!map->arch) - break; + if (!map->arch) + break; - if (!strcmp(map->cpuid, "common")) - return &map->metric_table; - } - return NULL; + if (!strcmp(map->cpuid, "common")) + return &map->metric_table; + } + return NULL; } const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->event_table; - } - return NULL; + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) + return &tables->event_table; + } + return NULL; } const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->metric_table; - } - return NULL; + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) + return &tables->metric_table; + } + return NULL; } int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - int ret = pmu_events_table__for_each_event(&tables->event_table, - /*pmu=*/ NULL, fn, data); + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/NULL, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } const struct pmu_events_table *find_sys_events_table(const char *name) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - if (!strcmp(tables->name, name)) - return &tables->event_table; - } - return NULL; + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + if (!strcmp(tables->name, name)) + return &tables->event_table; + } + return NULL; } int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - int ret = pmu_events_table__for_each_event(&tables->event_table, - /*pmu=*/ NULL, fn, data); + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/NULL, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } +/* clang-format on */ static const int metricgroups[][2] = { @@ -3372,20 +5996,19 @@ static const int metricgroups[][2] = { const char *describe_metricgroup(const char *group) { - int low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; + int low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; - while (low <= high) { - int mid = (low + high) / 2; - const char *mgroup = &big_c_string[metricgroups[mid][0]]; - int cmp = strcmp(mgroup, group); + while (low <= high) { + int mid = (low + high) / 2; + const char *mgroup = &big_c_string[metricgroups[mid][0]]; + int cmp = strcmp(mgroup, group); - if (cmp == 0) { - return &big_c_string[metricgroups[mid][1]]; - } else if (cmp < 0) { - low = mid + 1; - } else { - high = mid - 1; - } - } - return NULL; + if (cmp == 0) + return &big_c_string[metricgroups[mid][1]]; + else if (cmp < 0) + low = mid + 1; + else + high = mid - 1; + } + return NULL; } diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 7344940e776a..4279e385f243 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -183,7 +183,7 @@ class BigCString: for s in sorted(self.strings, key=string_cmp_key): if s not in folded_strings: self.offsets[s] = big_string_offset - self.big_string.append(f'/* offset={big_string_offset} */ "') + self.big_string.append(f'/* offset={big_string_offset} */\n"') self.big_string.append(s) self.big_string.append('"') if s in fold_into_strings: @@ -450,11 +450,12 @@ class JsonEvent: def to_c_string(self, metric: bool) -> str: """Representation of the event as a C struct initializer.""" - def fix_comment(s: str) -> str: - return s.replace('*/', r'\*\/') + def make_comment(s: str) -> str: + s = s.replace('*/', r'\*\/') + return f'\t/* {s} */\n' if len(s) < 80 else f'\t/* {s[0:80]}... */\n' s = self.build_c_string(metric) - return f'{{ { _bcs.offsets[s] } }}, /* {fix_comment(s)} */\n' + return f'{make_comment(s)}\t{{ { _bcs.offsets[s] } }},\n' @lru_cache(maxsize=None) @@ -558,11 +559,11 @@ static const struct pmu_table_entry {_pending_events_tblname}[] = {{ """) for (pmu, tbl_pmu) in sorted(pmus): pmu_name = f"{pmu}\\000" - _args.output_file.write(f"""{{ - .entries = {_pending_events_tblname}_{tbl_pmu}, - .num_entries = ARRAY_SIZE({_pending_events_tblname}_{tbl_pmu}), - .pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, -}}, + _args.output_file.write(f"""\t{{ +\t\t.entries = {_pending_events_tblname}_{tbl_pmu}, +\t\t.num_entries = ARRAY_SIZE({_pending_events_tblname}_{tbl_pmu}), +\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, +\t}}, """) _args.output_file.write('};\n\n') @@ -613,11 +614,11 @@ static const struct pmu_table_entry {_pending_metrics_tblname}[] = {{ """) for (pmu, tbl_pmu) in sorted(pmus): pmu_name = f"{pmu}\\000" - _args.output_file.write(f"""{{ - .entries = {_pending_metrics_tblname}_{tbl_pmu}, - .num_entries = ARRAY_SIZE({_pending_metrics_tblname}_{tbl_pmu}), - .pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, -}}, + _args.output_file.write(f"""\t{{ +\t\t.entries = {_pending_metrics_tblname}_{tbl_pmu}, +\t\t.num_entries = ARRAY_SIZE({_pending_metrics_tblname}_{tbl_pmu}), +\t\t.pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, +\t}}, """) _args.output_file.write('};\n\n') @@ -705,14 +706,14 @@ def print_mapping_table(archs: Sequence[str]) -> None: _args.output_file.write(""" /* Struct used to make the PMU event table implementation opaque to callers. */ struct pmu_events_table { - const struct pmu_table_entry *pmus; - uint32_t num_pmus; +\tconst struct pmu_table_entry *pmus; +\tuint32_t num_pmus; }; /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { - const struct pmu_table_entry *pmus; - uint32_t num_pmus; +\tconst struct pmu_table_entry *pmus; +\tuint32_t num_pmus; }; /* @@ -724,10 +725,10 @@ struct pmu_metrics_table { * The cpuid can contain any character other than the comma. */ struct pmu_events_map { - const char *arch; - const char *cpuid; - struct pmu_events_table event_table; - struct pmu_metrics_table metric_table; +\tconst char *arch; +\tconst char *cpuid; +\tstruct pmu_events_table event_table; +\tstruct pmu_metrics_table metric_table; }; /* @@ -896,455 +897,453 @@ static void decompress_metric(int offset, struct pmu_metric *pm) _args.output_file.write("""} static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, - const struct pmu_table_entry *pmu, - pmu_event_iter_fn fn, - void *data) +\t\t\t\t\t\tconst struct pmu_table_entry *pmu, +\t\t\t\t\t\tpmu_event_iter_fn fn, +\t\t\t\t\t\tvoid *data) { - int ret; - struct pmu_event pe = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; +\tint ret; +\tstruct pmu_event pe = { +\t\t.pmu = &big_c_string[pmu->pmu_name.offset], +\t}; - for (uint32_t i = 0; i < pmu->num_entries; i++) { - decompress_event(pmu->entries[i].offset, &pe); - if (!pe.name) - continue; - ret = fn(&pe, table, data); - if (ret) - return ret; - } - return 0; +\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { +\t\tdecompress_event(pmu->entries[i].offset, &pe); +\t\tif (!pe.name) +\t\t\tcontinue; +\t\tret = fn(&pe, table, data); +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, - const struct pmu_table_entry *pmu, - const char *name, - pmu_event_iter_fn fn, - void *data) +\t\t\t\t\t const struct pmu_table_entry *pmu, +\t\t\t\t\t const char *name, +\t\t\t\t\t pmu_event_iter_fn fn, +\t\t\t\t\t void *data) { - struct pmu_event pe = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; - int low = 0, high = pmu->num_entries - 1; +\tstruct pmu_event pe = { +\t\t.pmu = &big_c_string[pmu->pmu_name.offset], +\t}; +\tint low = 0, high = pmu->num_entries - 1; - while (low <= high) { - int cmp, mid = (low + high) / 2; +\twhile (low <= high) { +\t\tint cmp, mid = (low + high) / 2; - decompress_event(pmu->entries[mid].offset, &pe); +\t\tdecompress_event(pmu->entries[mid].offset, &pe); - if (!pe.name && !name) - goto do_call; +\t\tif (!pe.name && !name) +\t\t\tgoto do_call; - if (!pe.name && name) { - low = mid + 1; - continue; - } - if (pe.name && !name) { - high = mid - 1; - continue; - } +\t\tif (!pe.name && name) { +\t\t\tlow = mid + 1; +\t\t\tcontinue; +\t\t} +\t\tif (pe.name && !name) { +\t\t\thigh = mid - 1; +\t\t\tcontinue; +\t\t} - cmp = strcasecmp(pe.name, name); - if (cmp < 0) { - low = mid + 1; - continue; - } - if (cmp > 0) { - high = mid - 1; - continue; - } +\t\tcmp = strcasecmp(pe.name, name); +\t\tif (cmp < 0) { +\t\t\tlow = mid + 1; +\t\t\tcontinue; +\t\t} +\t\tif (cmp > 0) { +\t\t\thigh = mid - 1; +\t\t\tcontinue; +\t\t} do_call: - return fn ? fn(&pe, table, data) : 0; - } - return PMU_EVENTS__NOT_FOUND; +\t\treturn fn ? fn(&pe, table, data) : 0; +\t} +\treturn PMU_EVENTS__NOT_FOUND; } int pmu_events_table__for_each_event(const struct pmu_events_table *table, - struct perf_pmu *pmu, - pmu_event_iter_fn fn, - void *data) +\t\t\t\t struct perf_pmu *pmu, +\t\t\t\t pmu_event_iter_fn fn, +\t\t\t\t void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; +\tif (!table) +\t\treturn 0; +\tfor (size_t i = 0; i < table->num_pmus; i++) { +\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; +\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; +\t\tint ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; +\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) +\t\t\tcontinue; - ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); - if (ret) - return ret; - } - return 0; +\t\tret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } int pmu_events_table__find_event(const struct pmu_events_table *table, - struct perf_pmu *pmu, - const char *name, - pmu_event_iter_fn fn, - void *data) +\t\t\t\t struct perf_pmu *pmu, +\t\t\t\t const char *name, +\t\t\t\t pmu_event_iter_fn fn, +\t\t\t\t void *data) { - if (!table) - return PMU_EVENTS__NOT_FOUND; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; +\tif (!table) +\t\treturn PMU_EVENTS__NOT_FOUND; +\tfor (size_t i = 0; i < table->num_pmus; i++) { +\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; +\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; +\t\tint ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; +\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) +\t\t\tcontinue; - ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); - if (ret != PMU_EVENTS__NOT_FOUND) - return ret; - } - return PMU_EVENTS__NOT_FOUND; +\t\tret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); +\t\tif (ret != PMU_EVENTS__NOT_FOUND) +\t\t\treturn ret; +\t} +\treturn PMU_EVENTS__NOT_FOUND; } -size_t pmu_events_table__num_events(const struct pmu_events_table *table, - struct perf_pmu *pmu) +size_t pmu_events_table__num_events(const struct pmu_events_table *table, struct perf_pmu *pmu) { - size_t count = 0; +\tsize_t count = 0; - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; +\tif (!table) +\t\treturn 0; +\tfor (size_t i = 0; i < table->num_pmus; i++) { +\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; +\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (perf_pmu__name_wildcard_match(pmu, pmu_name)) - count += table_pmu->num_entries; - } - return count; +\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) +\t\t\tcount += table_pmu->num_entries; +\t} +\treturn count; } static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, - const struct pmu_table_entry *pmu, - pmu_metric_iter_fn fn, - void *data) +\t\t\t\t\t\tconst struct pmu_table_entry *pmu, +\t\t\t\t\t\tpmu_metric_iter_fn fn, +\t\t\t\t\t\tvoid *data) { - int ret; - struct pmu_metric pm = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; +\tint ret; +\tstruct pmu_metric pm = { +\t\t.pmu = &big_c_string[pmu->pmu_name.offset], +\t}; - for (uint32_t i = 0; i < pmu->num_entries; i++) { - decompress_metric(pmu->entries[i].offset, &pm); - if (!pm.metric_expr) - continue; - ret = fn(&pm, table, data); - if (ret) - return ret; - } - return 0; +\tfor (uint32_t i = 0; i < pmu->num_entries; i++) { +\t\tdecompress_metric(pmu->entries[i].offset, &pm); +\t\tif (!pm.metric_expr) +\t\t\tcontinue; +\t\tret = fn(&pm, table, data); +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } static int pmu_metrics_table__find_metric_pmu(const struct pmu_metrics_table *table, - const struct pmu_table_entry *pmu, - const char *metric, - pmu_metric_iter_fn fn, - void *data) +\t\t\t\t\t const struct pmu_table_entry *pmu, +\t\t\t\t\t const char *metric, +\t\t\t\t\t pmu_metric_iter_fn fn, +\t\t\t\t\t void *data) { - struct pmu_metric pm = { - .pmu = &big_c_string[pmu->pmu_name.offset], - }; - int low = 0, high = pmu->num_entries - 1; +\tstruct pmu_metric pm = { +\t\t.pmu = &big_c_string[pmu->pmu_name.offset], +\t}; +\tint low = 0, high = pmu->num_entries - 1; - while (low <= high) { - int cmp, mid = (low + high) / 2; +\twhile (low <= high) { +\t\tint cmp, mid = (low + high) / 2; - decompress_metric(pmu->entries[mid].offset, &pm); +\t\tdecompress_metric(pmu->entries[mid].offset, &pm); - if (!pm.metric_name && !metric) - goto do_call; +\t\tif (!pm.metric_name && !metric) +\t\t\tgoto do_call; - if (!pm.metric_name && metric) { - low = mid + 1; - continue; - } - if (pm.metric_name && !metric) { - high = mid - 1; - continue; - } +\t\tif (!pm.metric_name && metric) { +\t\t\tlow = mid + 1; +\t\t\tcontinue; +\t\t} +\t\tif (pm.metric_name && !metric) { +\t\t\thigh = mid - 1; +\t\t\tcontinue; +\t\t} - cmp = strcmp(pm.metric_name, metric); - if (cmp < 0) { - low = mid + 1; - continue; - } - if (cmp > 0) { - high = mid - 1; - continue; - } +\t\tcmp = strcmp(pm.metric_name, metric); +\t\tif (cmp < 0) { +\t\t\tlow = mid + 1; +\t\t\tcontinue; +\t\t} +\t\tif (cmp > 0) { +\t\t\thigh = mid - 1; +\t\t\tcontinue; +\t\t} do_call: - return fn ? fn(&pm, table, data) : 0; - } - return PMU_METRICS__NOT_FOUND; +\t\treturn fn ? fn(&pm, table, data) : 0; +\t} +\treturn PMU_METRICS__NOT_FOUND; } int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, - pmu_metric_iter_fn fn, - void *data) +\t\t\t\t pmu_metric_iter_fn fn, +\t\t\t\t void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - int ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], - fn, data); +\tif (!table) +\t\treturn 0; +\tfor (size_t i = 0; i < table->num_pmus; i++) { +\t\tint ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], fn, data); - if (ret) - return ret; - } - return 0; +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } int pmu_metrics_table__find_metric(const struct pmu_metrics_table *table, - struct perf_pmu *pmu, - const char *metric, - pmu_metric_iter_fn fn, - void *data) +\t\t\t\t struct perf_pmu *pmu, +\t\t\t\t const char *metric, +\t\t\t\t pmu_metric_iter_fn fn, +\t\t\t\t void *data) { - if (!table) - return 0; - for (size_t i = 0; i < table->num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &table->pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - int ret; +\tif (!table) +\t\treturn 0; +\tfor (size_t i = 0; i < table->num_pmus; i++) { +\t\tconst struct pmu_table_entry *table_pmu = &table->pmus[i]; +\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; +\t\tint ret; - if (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) - continue; +\t\tif (pmu && !perf_pmu__name_wildcard_match(pmu, pmu_name)) +\t\t\tcontinue; - ret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); - if (ret != PMU_METRICS__NOT_FOUND) - return ret; - } - return PMU_METRICS__NOT_FOUND; +\t\tret = pmu_metrics_table__find_metric_pmu(table, table_pmu, metric, fn, data); +\t\tif (ret != PMU_METRICS__NOT_FOUND) +\t\t\treturn ret; +\t} +\treturn PMU_METRICS__NOT_FOUND; } static const struct pmu_events_map *map_for_cpu(struct perf_cpu cpu) { - static struct { - const struct pmu_events_map *map; - struct perf_cpu cpu; - } last_result; - static struct { - const struct pmu_events_map *map; - char *cpuid; - } last_map_search; - static bool has_last_result, has_last_map_search; - const struct pmu_events_map *map = NULL; - char *cpuid = NULL; - size_t i; +\tstatic struct { +\t\tconst struct pmu_events_map *map; +\t\tstruct perf_cpu cpu; +\t} last_result; +\tstatic struct { +\t\tconst struct pmu_events_map *map; +\t\tchar *cpuid; +\t} last_map_search; +\tstatic bool has_last_result, has_last_map_search; +\tconst struct pmu_events_map *map = NULL; +\tchar *cpuid = NULL; +\tsize_t i; - if (has_last_result && last_result.cpu.cpu == cpu.cpu) - return last_result.map; +\tif (has_last_result && last_result.cpu.cpu == cpu.cpu) +\t\treturn last_result.map; - cpuid = get_cpuid_allow_env_override(cpu); +\tcpuid = get_cpuid_allow_env_override(cpu); - /* - * On some platforms which uses cpus map, cpuid can be NULL for - * PMUs other than CORE PMUs. - */ - if (!cpuid) - goto out_update_last_result; +\t/* +\t * On some platforms which uses cpus map, cpuid can be NULL for +\t * PMUs other than CORE PMUs. +\t */ +\tif (!cpuid) +\t\tgoto out_update_last_result; - if (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { - map = last_map_search.map; - free(cpuid); - } else { - i = 0; - for (;;) { - map = &pmu_events_map[i++]; +\tif (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { +\t\tmap = last_map_search.map; +\t\tfree(cpuid); +\t} else { +\t\ti = 0; +\t\tfor (;;) { +\t\t\tmap = &pmu_events_map[i++]; - if (!map->arch) { - map = NULL; - break; - } +\t\t\tif (!map->arch) { +\t\t\t\tmap = NULL; +\t\t\t\tbreak; +\t\t\t} - if (!strcmp_cpuid_str(map->cpuid, cpuid)) - break; - } - free(last_map_search.cpuid); - last_map_search.cpuid = cpuid; - last_map_search.map = map; - has_last_map_search = true; - } +\t\t\tif (!strcmp_cpuid_str(map->cpuid, cpuid)) +\t\t\t\tbreak; +\t\t} +\t\tfree(last_map_search.cpuid); +\t\tlast_map_search.cpuid = cpuid; +\t\tlast_map_search.map = map; +\t\thas_last_map_search = true; +\t} out_update_last_result: - last_result.cpu = cpu; - last_result.map = map; - has_last_result = true; - return map; +\tlast_result.cpu = cpu; +\tlast_result.map = map; +\thas_last_result = true; +\treturn map; } static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) { - struct perf_cpu cpu = {-1}; +\tstruct perf_cpu cpu = { -1 }; - if (pmu) { - for (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { - const char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; +\tif (pmu) { +\t\tfor (size_t i = 0; i < ARRAY_SIZE(pmu_events__common); i++) { +\t\t\tconst char *pmu_name = &big_c_string[pmu_events__common[i].pmu_name.offset]; - if (!strcmp(pmu_name, pmu->name)) { - const struct pmu_events_map *map = &pmu_events_map[0]; +\t\t\tif (!strcmp(pmu_name, pmu->name)) { +\t\t\t\tconst struct pmu_events_map *map = &pmu_events_map[0]; - while (strcmp("common", map->arch)) - map++; - return map; - } - } - cpu = perf_cpu_map__min(pmu->cpus); - } - return map_for_cpu(cpu); +\t\t\t\twhile (strcmp("common", map->arch)) +\t\t\t\t\tmap++; +\t\t\t\treturn map; +\t\t\t} +\t\t} +\t\tcpu = perf_cpu_map__min(pmu->cpus); +\t} +\treturn map_for_cpu(cpu); } const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { - const struct pmu_events_map *map = map_for_pmu(pmu); +\tconst struct pmu_events_map *map = map_for_pmu(pmu); - if (!map) - return NULL; +\tif (!map) +\t\treturn NULL; - if (!pmu) - return &map->event_table; +\tif (!pmu) +\t\treturn &map->event_table; - for (size_t i = 0; i < map->event_table.num_pmus; i++) { - const struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; - const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; +\tfor (size_t i = 0; i < map->event_table.num_pmus; i++) { +\t\tconst struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; +\t\tconst char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (perf_pmu__name_wildcard_match(pmu, pmu_name)) - return &map->event_table; - } - return NULL; +\t\tif (perf_pmu__name_wildcard_match(pmu, pmu_name)) +\t\t\treturn &map->event_table; +\t} +\treturn NULL; } const struct pmu_events_table *perf_pmu__default_core_events_table(void) { - int i = 0; +\tint i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; +\tfor (;;) { +\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; - if (!map->arch) - break; +\t\tif (!map->arch) +\t\t\tbreak; - if (!strcmp(map->cpuid, "common")) - return &map->event_table; - } - return NULL; +\t\tif (!strcmp(map->cpuid, "common")) +\t\t\treturn &map->event_table; +\t} +\treturn NULL; } const struct pmu_metrics_table *pmu_metrics_table__find(void) { - struct perf_cpu cpu = {-1}; - const struct pmu_events_map *map = map_for_cpu(cpu); +\tstruct perf_cpu cpu = { -1 }; +\tconst struct pmu_events_map *map = map_for_cpu(cpu); - return map ? &map->metric_table : NULL; +\treturn map ? &map->metric_table : NULL; } const struct pmu_metrics_table *pmu_metrics_table__default(void) { - int i = 0; +\tint i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; +\tfor (;;) { +\t\tconst struct pmu_events_map *map = &pmu_events_map[i++]; - if (!map->arch) - break; +\t\tif (!map->arch) +\t\t\tbreak; - if (!strcmp(map->cpuid, "common")) - return &map->metric_table; - } - return NULL; +\t\tif (!strcmp(map->cpuid, "common")) +\t\t\treturn &map->metric_table; +\t} +\treturn NULL; } const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->event_table; - } - return NULL; +\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; +\t tables->arch; +\t tables++) { +\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) +\t\t\treturn &tables->event_table; +\t} +\treturn NULL; } const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->metric_table; - } - return NULL; +\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; +\t tables->arch; +\t tables++) { +\t\tif (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) +\t\t\treturn &tables->metric_table; +\t} +\treturn NULL; } int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - int ret = pmu_events_table__for_each_event(&tables->event_table, - /*pmu=*/ NULL, fn, data); +\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; +\t tables->arch; +\t tables++) { +\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, +\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); - if (ret) - return ret; - } - return 0; +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); +\tfor (const struct pmu_events_map *tables = &pmu_events_map[0]; +\t tables->arch; +\t tables++) { +\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); - if (ret) - return ret; - } - return 0; +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } const struct pmu_events_table *find_sys_events_table(const char *name) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - if (!strcmp(tables->name, name)) - return &tables->event_table; - } - return NULL; +\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; +\t tables->name; +\t tables++) { +\t\tif (!strcmp(tables->name, name)) +\t\t\treturn &tables->event_table; +\t} +\treturn NULL; } int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - int ret = pmu_events_table__for_each_event(&tables->event_table, - /*pmu=*/ NULL, fn, data); +\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; +\t tables->name; +\t tables++) { +\t\tint ret = pmu_events_table__for_each_event(&tables->event_table, +\t\t\t\t\t\t\t /*pmu=*/NULL, fn, data); - if (ret) - return ret; - } - return 0; +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); +\tfor (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; +\t tables->name; +\t tables++) { +\t\tint ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); - if (ret) - return ret; - } - return 0; +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; } """) @@ -1362,22 +1361,21 @@ static const int metricgroups[][2] = { const char *describe_metricgroup(const char *group) { - int low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; +\tint low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; - while (low <= high) { - int mid = (low + high) / 2; - const char *mgroup = &big_c_string[metricgroups[mid][0]]; - int cmp = strcmp(mgroup, group); +\twhile (low <= high) { +\t\tint mid = (low + high) / 2; +\t\tconst char *mgroup = &big_c_string[metricgroups[mid][0]]; +\t\tint cmp = strcmp(mgroup, group); - if (cmp == 0) { - return &big_c_string[metricgroups[mid][1]]; - } else if (cmp < 0) { - low = mid + 1; - } else { - high = mid - 1; - } - } - return NULL; +\t\tif (cmp == 0) +\t\t\treturn &big_c_string[metricgroups[mid][1]]; +\t\telse if (cmp < 0) +\t\t\tlow = mid + 1; +\t\telse +\t\t\thigh = mid - 1; +\t} +\treturn NULL; } """) @@ -1426,9 +1424,8 @@ such as "arm/cortex-a34".''', 'output_string_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=None) _args = ap.parse_args() - _args.output_file.write(f""" -/* SPDX-License-Identifier: GPL-2.0 */ -/* THIS FILE WAS AUTOGENERATED BY jevents.py arch={_args.arch} model={_args.model} ! */ + _args.output_file.write(f"""/* SPDX-License-Identifier: GPL-2.0 */ +/* THIS FILE WAS AUTOGENERATED BY `jevents.py arch={_args.arch} model={_args.model}` ! */ """) _args.output_file.write(""" #include @@ -1438,13 +1435,13 @@ such as "arm/cortex-a34".''', #include struct compact_pmu_event { - int offset; +\tint offset; }; struct pmu_table_entry { - const struct compact_pmu_event *entries; - uint32_t num_entries; - struct compact_pmu_event pmu_name; +\tconst struct compact_pmu_event *entries; +\tuint32_t num_entries; +\tstruct compact_pmu_event pmu_name; }; """) @@ -1465,6 +1462,7 @@ struct pmu_table_entry { ftw(arch_path, [], preprocess_one_file) _bcs.compute() + _args.output_file.write('/* clang-format off */\n') if not _args.output_string_file: _args.output_file.write('static const char *const big_c_string =\n') for s in _bcs.big_string: @@ -1487,6 +1485,7 @@ struct pmu_table_entry { print_mapping_table(archs) print_system_mapping_table() + _args.output_file.write('/* clang-format on */\n') print_metricgroups() _args.output_file.close() if _args.output_string_file: From 53247b207bd02116cefd3409494382e7adf32849 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:14 -0700 Subject: [PATCH 2800/5214] perf pmu-events: Add API to get metric table name and iterate tables Add name field to struct pmu_metrics_table and populate it in generated tables. Add pmu_metrics_table__name() to retrieve the name. Add pmu_metrics_table__for_each_table() to iterate over all known metric tables. This will be used to break apart slow metric tests per table. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/empty-pmu-events.c | 46 ++++++++++++++++++- tools/perf/pmu-events/jevents.py | 56 +++++++++++++++++++++++- tools/perf/pmu-events/pmu-events.h | 5 +++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index ad5ade37adb0..2af4865713be 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -5403,6 +5403,7 @@ struct pmu_events_table { /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { + const char *name; const struct pmu_table_entry *pmus; uint32_t num_pmus; }; @@ -5435,6 +5436,7 @@ static const struct pmu_events_map pmu_events_map[] = { .num_pmus = ARRAY_SIZE(pmu_events__common), }, .metric_table = { + .name = "common", .pmus = pmu_metrics__common, .num_pmus = ARRAY_SIZE(pmu_metrics__common), }, @@ -5447,6 +5449,7 @@ static const struct pmu_events_map pmu_events_map[] = { .num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), }, .metric_table = { + .name = "test_soc_cpu", .pmus = pmu_metrics__test_soc_cpu, .num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), } @@ -5455,7 +5458,7 @@ static const struct pmu_events_map pmu_events_map[] = { .arch = 0, .cpuid = 0, .event_table = { 0, 0 }, - .metric_table = { 0, 0 }, + .metric_table = { 0 }, } }; @@ -5475,7 +5478,7 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { }, { .event_table = { 0, 0 }, - .metric_table = { 0, 0 }, + .metric_table = { 0 }, }, }; @@ -5990,6 +5993,45 @@ int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) } /* clang-format on */ +const char *pmu_metrics_table__name(const struct pmu_metrics_table *table) +{ + return table ? table->name : NULL; +} + +int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data) +{ + size_t i; + int ret; + + for (i = 0; pmu_events_map[i].cpuid; i++) { + size_t j; + bool found = false; + + if (!pmu_events_map[i].metric_table.pmus) + continue; + for (j = 0; j < i; j++) { + if (pmu_events_map[j].metric_table.pmus == + pmu_events_map[i].metric_table.pmus) { + found = true; + break; + } + } + if (found) + continue; + ret = fn(&pmu_events_map[i].metric_table, data); + if (ret) + return ret; + } + for (i = 0; pmu_sys_event_tables[i].name; i++) { + if (!pmu_sys_event_tables[i].metric_table.pmus) + continue; + ret = fn(&pmu_sys_event_tables[i].metric_table, data); + if (ret) + return ret; + } + return 0; +} + static const int metricgroups[][2] = { }; diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 4279e385f243..376dc2d24162 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -712,6 +712,7 @@ struct pmu_events_table { /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { +\tconst char *name; \tconst struct pmu_table_entry *pmus; \tuint32_t num_pmus; }; @@ -747,6 +748,7 @@ static const struct pmu_events_map pmu_events_map[] = { \t\t.num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), \t}, \t.metric_table = { +\t\t.name = "test_soc_cpu", \t\t.pmus = pmu_metrics__test_soc_cpu, \t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), \t} @@ -761,6 +763,7 @@ static const struct pmu_events_map pmu_events_map[] = { \t\t.num_pmus = ARRAY_SIZE(pmu_events__common), \t}, \t.metric_table = { +\t\t.name = "common", \t\t.pmus = pmu_metrics__common, \t\t.num_pmus = ARRAY_SIZE(pmu_metrics__common), \t}, @@ -781,8 +784,10 @@ static const struct pmu_events_map pmu_events_map[] = { event_size = '0' metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_')) if metric_tblname in _metric_tables: + metric_name = f'"{metric_tblname.replace("pmu_metrics__", "")}"' metric_size = f'ARRAY_SIZE({metric_tblname})' else: + metric_name = 'NULL' metric_tblname = 'NULL' metric_size = '0' if event_size == '0' and metric_size == '0': @@ -796,6 +801,7 @@ static const struct pmu_events_map pmu_events_map[] = { \t\t.num_pmus = {event_size} \t}}, \t.metric_table = {{ +\t\t.name = {metric_name}, \t\t.pmus = {metric_tblname}, \t\t.num_pmus = {metric_size} \t}} @@ -807,12 +813,55 @@ static const struct pmu_events_map pmu_events_map[] = { \t.arch = 0, \t.cpuid = 0, \t.event_table = { 0, 0 }, -\t.metric_table = { 0, 0 }, +\t.metric_table = { 0 }, } }; """) +def print_metric_table_functions() -> None: + _args.output_file.write(""" +const char *pmu_metrics_table__name(const struct pmu_metrics_table *table) +{ +\treturn table ? table->name : NULL; +} + +int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data) +{ +\tsize_t i; +\tint ret; + +\tfor (i = 0; pmu_events_map[i].cpuid; i++) { +\t\tsize_t j; +\t\tbool found = false; + +\t\tif (!pmu_events_map[i].metric_table.pmus) +\t\t\tcontinue; +\t\tfor (j = 0; j < i; j++) { +\t\t\tif (pmu_events_map[j].metric_table.pmus == +\t\t\t pmu_events_map[i].metric_table.pmus) { +\t\t\t\tfound = true; +\t\t\t\tbreak; +\t\t\t} +\t\t} +\t\tif (found) +\t\t\tcontinue; +\t\tret = fn(&pmu_events_map[i].metric_table, data); +\t\tif (ret) +\t\t\treturn ret; +\t} +\tfor (i = 0; pmu_sys_event_tables[i].name; i++) { +\t\tif (!pmu_sys_event_tables[i].metric_table.pmus) +\t\t\tcontinue; +\t\tret = fn(&pmu_sys_event_tables[i].metric_table, data); +\t\tif (ret) +\t\t\treturn ret; +\t} +\treturn 0; +} +""") + + def print_system_mapping_table() -> None: """C struct mapping table array for tables from /sys directories.""" _args.output_file.write(""" @@ -835,6 +884,7 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { if metric_tblname in _sys_metric_tables: _args.output_file.write(f""" \t\t.metric_table = {{ +\t\t\t.name = "{metric_tblname.replace('pmu_metrics__', '')}", \t\t\t.pmus = {metric_tblname}, \t\t\t.num_pmus = ARRAY_SIZE({metric_tblname}) \t\t}},""") @@ -848,6 +898,7 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { continue _args.output_file.write(f"""\t{{ \t\t.metric_table = {{ +\t\t\t.name = "{tblname.replace('pmu_metrics__', '')}", \t\t\t.pmus = {tblname}, \t\t\t.num_pmus = ARRAY_SIZE({tblname}) \t\t}}, @@ -856,7 +907,7 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { """) _args.output_file.write("""\t{ \t\t.event_table = { 0, 0 }, -\t\t.metric_table = { 0, 0 }, +\t\t.metric_table = { 0 }, \t}, }; @@ -1486,6 +1537,7 @@ struct pmu_table_entry { print_mapping_table(archs) print_system_mapping_table() _args.output_file.write('/* clang-format on */\n') + print_metric_table_functions() print_metricgroups() _args.output_file.close() if _args.output_string_file: diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index d3b24014c6ff..cb55c9fbca43 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -91,6 +91,9 @@ typedef int (*pmu_metric_iter_fn)(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data); +typedef int (*pmu_metrics_table_iter_t)(const struct pmu_metrics_table *table, + void *data); + int pmu_events_table__for_each_event(const struct pmu_events_table *table, struct perf_pmu *pmu, pmu_event_iter_fn fn, @@ -112,6 +115,8 @@ size_t pmu_events_table__num_events(const struct pmu_events_table *table, int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, void *data); +const char *pmu_metrics_table__name(const struct pmu_metrics_table *table); +int pmu_metrics_table__iterate_tables(pmu_metrics_table_iter_t fn, void *data); /* * Search for a table and entry matching with pmu__name_wildcard_match or any * tables if pmu is NULL. Each matching metric has fn called on it. 0 implies to From 744af598719776b2ca8b0f5388b51d2493cc94b7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:15 -0700 Subject: [PATCH 2801/5214] perf test: Drain pipe after child finishes to avoid losing output When running tests in parallel, the parent process reads output from the child's pipe. However, it might exit the loop as soon as the child is detected as finished, potentially missing data that arrived in the pipe just after the last poll or before the loop terminated. Address this by draining the pipe after the main loop in finish_test. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index f2c135891477..7946878195b7 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -483,6 +483,16 @@ static void finish_test(struct child_test **child_tests, int running_test, int c if (err_done) err_done = check_if_command_finished(&child_test->process); } + /* Drain any remaining data from the pipe. */ + if (err > 0) { + char buf[512]; + ssize_t len; + + while ((len = read(err, buf, sizeof(buf) - 1)) > 0) { + buf[len] = '\0'; + strbuf_addstr(&err_output, buf); + } + } if (perf_use_color_default && last_running != -1) { /* Erase "Running (.. active)" line printed before poll/sleep. */ fprintf(debug_file(), PERF_COLOR_DELETE_LINE); From 8c8d61c38d8e1755a4325f8acb396137bbd5371a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:16 -0700 Subject: [PATCH 2802/5214] perf test: Support dynamic test suites with setup callback and private data Add void *priv to struct test_case to allow passing per-test context. Add int (*setup)(struct test_suite *) to struct test_suite to allow dynamic generation of test cases. Update build_suites() to invoke the setup callback for each suite if present, ensuring dynamic cases are available before listing or running. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 17 ++++++++++++++++- tools/perf/tests/tests.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 7946878195b7..485ff75ab880 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -765,10 +765,21 @@ static struct test_suite **build_suites(void) for (size_t i = 0, j = 0; i < ARRAY_SIZE(suites); i++, j = 0) \ while ((suite = suites[i][j++]) != NULL) - for_each_suite(t) + for_each_suite(t) { + if (t->setup) { + int ret = t->setup(t); + + if (ret < 0) { + errno = -ret; + return NULL; + } + } num_suites++; + } result = calloc(num_suites + 1, sizeof(struct test_suite *)); + if (!result) + return NULL; for (int pass = 1; pass <= 2; pass++) { for_each_suite(t) { @@ -831,6 +842,8 @@ int cmd_test(int argc, const char **argv) argc = parse_options_subcommand(argc, argv, test_options, test_subcommands, test_usage, 0); if (argc >= 1 && !strcmp(argv[0], "list")) { suites = build_suites(); + if (!suites) + return errno ? -errno : -ENOMEM; ret = perf_test__list(stdout, suites, argc - 1, argv + 1); free(suites); return ret; @@ -863,6 +876,8 @@ int cmd_test(int argc, const char **argv) rlimit__bump_memlock(); suites = build_suites(); + if (!suites) + return errno ? -errno : -ENOMEM; ret = __cmd_test(suites, argc, argv, skiplist); free(suites); return ret; diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index ee00518bf36f..9bcf1dbb0663 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -38,12 +38,14 @@ struct test_case { const char *skip_reason; test_fnptr run_case; bool exclusive; + void *priv; }; struct test_suite { const char *desc; struct test_case *test_cases; void *priv; + int (*setup)(struct test_suite *suite); }; #define DECLARE_SUITE(name) \ From 8ae26f6ae7013f0ba753feed723eb0613796cb67 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:17 -0700 Subject: [PATCH 2803/5214] perf test pmu-events: A sub-test per metric table Break apart the slow "Parsing of PMU event table metrics" tests into one pair of tests (real and fake PMU) per metric table found, storing the specific table pointer in priv data. Implement setup_pmu_events_suite() to dynamically allocate and populate these test cases. Split static parser tests out into a separate test__parsing_fake_static() test case. Update test__parsing() and test__parsing_fake() to retrieve the specific table from priv data and test only that table, maintaining fallback compatibility if priv is NULL. Running these individual tests in parallel significantly reduces overall test execution time. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu-events.c | 156 ++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 8 deletions(-) diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index b1609a7e1d8c..fd5630f0a13c 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -923,13 +923,20 @@ static int test__parsing_callback(const struct pmu_metric *pm, return err; } -static int test__parsing(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) +static int test__parsing(struct test_suite *test, int subtest) { int failures = 0; + const struct pmu_metrics_table *table = NULL; - pmu_for_each_core_metric(test__parsing_callback, &failures); - pmu_for_each_sys_metric(test__parsing_callback, &failures); + if (test->test_cases) + table = test->test_cases[subtest].priv; + + if (table) { + pmu_metrics_table__for_each_metric(table, test__parsing_callback, &failures); + } else { + pmu_for_each_core_metric(test__parsing_callback, &failures); + pmu_for_each_sys_metric(test__parsing_callback, &failures); + } return failures == 0 ? TEST_OK : TEST_FAIL; } @@ -1020,8 +1027,8 @@ static int test__parsing_fake_callback(const struct pmu_metric *pm, * Parse all the metrics for current architecture, or all defined cpus via the * 'fake_pmu' in parse_events. */ -static int test__parsing_fake(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) +static int test__parsing_fake_static(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) { int err = 0; @@ -1031,6 +1038,26 @@ static int test__parsing_fake(struct test_suite *test __maybe_unused, return err; } + return 0; +} + +static int test__parsing_fake(struct test_suite *test, int subtest) +{ + int err = 0; + const struct pmu_metrics_table *table = NULL; + + if (test->test_cases) + table = test->test_cases[subtest].priv; + + if (table) + return pmu_metrics_table__for_each_metric(table, test__parsing_fake_callback, NULL); + + for (size_t i = 0; i < ARRAY_SIZE(metrics); i++) { + err = metric_parse_fake("", metrics[i].str); + if (err) + return err; + } + err = pmu_for_each_core_metric(test__parsing_fake_callback, NULL); if (err) return err; @@ -1059,17 +1086,130 @@ static int test__parsing_threshold(struct test_suite *test __maybe_unused, return pmu_for_each_sys_metric(test__parsing_threshold_callback, NULL); } +struct populate_cb_data { + struct test_case *test_cases; + size_t curr; +}; + +static int count_metrics_tables_cb(const struct pmu_metrics_table *table __maybe_unused, void *data) +{ + size_t *count = data; + (*count)++; + return 0; +} + +static int populate_metrics_tables_cb(const struct pmu_metrics_table *table, void *data) +{ + struct populate_cb_data *cb_data = data; + const char *table_name = pmu_metrics_table__name(table); + char *desc_real, *desc_fake; + + if (!table_name) + table_name = "unknown"; + + if (asprintf(&desc_real, "PMU metric parsing: %s", table_name) < 0) + return -ENOMEM; + if (asprintf(&desc_fake, "PMU metric parsing with fake PMU: %s", table_name) < 0) { + free(desc_real); + return -ENOMEM; + } + + cb_data->test_cases[cb_data->curr++] = (struct test_case){ + .name = "parsing", + .desc = desc_real, + .run_case = test__parsing, + .priv = (void *)table, + .skip_reason = "some metrics failed", + }; + + cb_data->test_cases[cb_data->curr++] = (struct test_case){ + .name = "parsing_fake", + .desc = desc_fake, + .run_case = test__parsing_fake, + .priv = (void *)table, + }; + + return 0; +} + +static struct test_case pmu_events_tests[]; + +static int setup_pmu_events_suite(struct test_suite *suite) +{ + size_t num_tables = 0; + size_t num_fixed_tests = 4; + size_t tests_per_table = 2; + size_t total_tests; + struct test_case *test_cases; + size_t curr = 0; + struct populate_cb_data cb_data; + int ret; + + if (suite->test_cases != pmu_events_tests) + return 0; + + ret = pmu_metrics_table__iterate_tables(count_metrics_tables_cb, &num_tables); + if (ret) + return ret; + + total_tests = num_fixed_tests + (num_tables * tests_per_table) + 1; + + test_cases = calloc(total_tests, sizeof(*test_cases)); + if (!test_cases) + return -ENOMEM; + + test_cases[curr++] = (struct test_case){ + .name = "pmu_event_table", + .desc = "PMU event table sanity", + .run_case = test__pmu_event_table, + }; + test_cases[curr++] = (struct test_case){ + .name = "aliases", + .desc = "PMU event map aliases", + .run_case = test__aliases, + }; + test_cases[curr++] = (struct test_case){ + .name = "parsing_fake_static", + .desc = "Parsing of static metrics with fake PMU", + .run_case = test__parsing_fake_static, + }; + test_cases[curr++] = (struct test_case){ + .name = "parsing_threshold", + .desc = "Parsing of metric thresholds with fake PMU", + .run_case = test__parsing_threshold, + }; + + cb_data = (struct populate_cb_data){ + .test_cases = test_cases, + .curr = curr, + }; + + ret = pmu_metrics_table__iterate_tables(populate_metrics_tables_cb, &cb_data); + if (ret) { + size_t i; + + for (i = num_fixed_tests; i < cb_data.curr; i++) + free((char *)test_cases[i].desc); + free(test_cases); + return ret; + } + + suite->test_cases = test_cases; + return 0; +} + static struct test_case pmu_events_tests[] = { TEST_CASE("PMU event table sanity", pmu_event_table), TEST_CASE("PMU event map aliases", aliases), TEST_CASE_REASON("Parsing of PMU event table metrics", parsing, "some metrics failed"), - TEST_CASE("Parsing of PMU event table metrics with fake PMUs", parsing_fake), - TEST_CASE("Parsing of metric thresholds with fake PMUs", parsing_threshold), + TEST_CASE("Parsing of PMU event table metrics with fake PMU", parsing_fake), + TEST_CASE("Parsing of metric thresholds with fake PMU", parsing_threshold), { .name = NULL, } }; struct test_suite suite__pmu_events = { .desc = "PMU JSON event tests", .test_cases = pmu_events_tests, + .setup = setup_pmu_events_suite, }; From c7dacfb87beed63ffe360c73c2a284d9e334f135 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:18 -0700 Subject: [PATCH 2804/5214] tools subcmd: Robust fallback and existence checks for process reaping Update check_if_command_finished() and wait_or_whine() to handle invalid PIDs gracefully (<= 0) by setting cmd->finished = 1 and returning early. This avoids executing waitpid(-1, ...) or waitpid(0, ...) downstream, which can block or reap parallel tests' exit status causing state corruption. Introduce a fallback mechanism in check_if_command_finished() using waitpid(..., WNOHANG) when /proc//status is inaccessible (e.g. due to EMFILE/ENFILE) to safely check and reap finished children. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/subcmd/run-command.c | 69 ++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c index b7510f83209a..bd21b8bfd58b 100644 --- a/tools/lib/subcmd/run-command.c +++ b/tools/lib/subcmd/run-command.c @@ -169,8 +169,18 @@ int start_command(struct child_process *cmd) static int wait_or_whine(struct child_process *cmd, bool block) { - bool finished = cmd->finished; - int result = cmd->finish_result; + bool finished; + int result; + + if (cmd->pid <= 0) { + cmd->finished = 1; + if (cmd->pid < 0 && cmd->finish_result == 0) + cmd->finish_result = -ERR_RUN_COMMAND_FORK; + return cmd->finish_result; + } + + finished = cmd->finished; + result = cmd->finish_result; while (!finished) { int status, code; @@ -233,7 +243,18 @@ int check_if_command_finished(struct child_process *cmd) char filename[6 + MAX_STRLEN_TYPE(typeof(cmd->pid)) + 7 + 1]; char status_line[256]; FILE *status_file; +#endif + if (cmd->finished) + return 1; + if (cmd->pid <= 0) { + cmd->finished = 1; + if (cmd->pid < 0 && cmd->finish_result == 0) + cmd->finish_result = -ERR_RUN_COMMAND_FORK; + return 1; + } + +#ifdef __linux__ /* * Check by reading /proc//status as calling waitpid causes * stdout/stderr to be closed and data lost. @@ -241,8 +262,48 @@ int check_if_command_finished(struct child_process *cmd) sprintf(filename, "/proc/%u/status", cmd->pid); status_file = fopen(filename, "r"); if (status_file == NULL) { - /* Open failed assume finish_command was called. */ - return true; + int status; + pid_t waiting; + + /* + * fopen() can fail with ENOENT if the process has been reaped. + * It can also fail with EMFILE/ENFILE if RLIMIT_NOFILE is reached. + * In those cases, use waitpid(..., WNOHANG) to robustly check + * and reap the process if it has exited. + */ + if (errno == ENOENT) + return 1; + + waiting = waitpid(cmd->pid, &status, WNOHANG); + if (waiting == cmd->pid) { + int result; + int code; + + cmd->finished = 1; + if (WIFSIGNALED(status)) { + result = -ERR_RUN_COMMAND_WAITPID_SIGNAL; + } else if (!WIFEXITED(status)) { + result = -ERR_RUN_COMMAND_WAITPID_NOEXIT; + } else { + code = WEXITSTATUS(status); + switch (code) { + case 127: + result = -ERR_RUN_COMMAND_EXEC; + break; + case 0: + result = 0; + break; + default: + result = -code; + break; + } + } + cmd->finish_result = result; + return 1; + } + if (waiting < 0 && (errno == ECHILD || errno == ESRCH)) + return 1; + return 0; } while (fgets(status_line, sizeof(status_line), status_file) != NULL) { char *p; From f35450738e789b80b540f41487b9053defd0bb8d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:19 -0700 Subject: [PATCH 2805/5214] perf test: Refactor parallel poll loop to drain all pipes simultaneously When running tests in parallel with verbose output (-v), child processes write to pipes. If a test produces significant output (e.g. Granite Rapids metric parsing printing hundreds of lines), it fills the 64KB pipe buffer and blocks. Previously, the parent harness (finish_test) only polled the pipe of the current test waiting to be printed. Other children blocked indefinitely until the parent reached them, severely sequentializing execution. Address this by implementing finish_tests_parallel() to poll and drain output pipes from all running children simultaneously into per-child buffers, employing safe strbuf_addstr string operations alongside thorough variable orderings for strict ISO C90 compliance. Reaping occurs out of order as children finish, while final result printing remains strictly in order. This drops parallel verbose execution time for the PMU events suite from ~35 seconds down to ~5.9 seconds. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 267 +++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 8 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 485ff75ab880..0479184c3dda 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -302,6 +302,9 @@ struct child_test { struct test_suite *test; int suite_num; int test_case_num; + struct strbuf err_output; + int result; + bool done; }; static jmp_buf run_test_jmp_buf; @@ -356,6 +359,11 @@ static int run_test_child(struct child_process *process) #define TEST_RUNNING -3 +static struct pollfd *global_pfds; +static size_t *global_pfd_indices; + +static int strbuf_addstr_safe(struct strbuf *sb, const char *s); + static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case, int result, int width, int running) { @@ -422,7 +430,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c * Busy loop reading from the child's stdout/stderr that are set to be * non-blocking until EOF. */ - if (err > 0) + if (err >= 0) fcntl(err, F_SETFL, O_NONBLOCK); if (verbose > 1) { if (test_suite__num_test_cases(t) > 1) @@ -476,7 +484,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c if (len > 0) { err_done = false; buf[len] = '\0'; - strbuf_addstr(&err_output, buf); + strbuf_addstr_safe(&err_output, buf); } } } @@ -484,13 +492,13 @@ static void finish_test(struct child_test **child_tests, int running_test, int c err_done = check_if_command_finished(&child_test->process); } /* Drain any remaining data from the pipe. */ - if (err > 0) { + if (err >= 0) { char buf[512]; ssize_t len; while ((len = read(err, buf, sizeof(buf) - 1)) > 0) { buf[len] = '\0'; - strbuf_addstr(&err_output, buf); + strbuf_addstr_safe(&err_output, buf); } } if (perf_use_color_default && last_running != -1) { @@ -499,16 +507,253 @@ static void finish_test(struct child_test **child_tests, int running_test, int c } /* Clean up child process. */ ret = finish_command(&child_test->process); + child_test->process.pid = 0; + if (child_test->err_output.len > 0) { + struct strbuf merged = STRBUF_INIT; + + if (child_test->err_output.buf) + strbuf_addstr_safe(&merged, child_test->err_output.buf); + if (err_output.buf) + strbuf_addstr_safe(&merged, err_output.buf); + strbuf_release(&err_output); + err_output = merged; + } if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL)) fprintf(stderr, "%s", err_output.buf); strbuf_release(&err_output); + strbuf_release(&child_test->err_output); print_test_result(t, curr_suite, curr_test_case, ret, width, /*running=*/0); if (err > 0) close(err); zfree(&child_tests[running_test]); } +static int strbuf_addstr_safe(struct strbuf *sb, const char *s) +{ + sigset_t set, oldset; + int ret; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + pthread_sigmask(SIG_BLOCK, &set, &oldset); + ret = strbuf_addstr(sb, s); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return ret; +} + +static void drain_child_process_err(struct child_test *child) +{ + char buf[512]; + ssize_t len; + + while ((len = read(child->process.err, buf, sizeof(buf) - 1)) > 0) { + buf[len] = '\0'; + strbuf_addstr_safe(&child->err_output, buf); + } +} + +static void handle_child_pipe_activity(struct child_test *child, short revents) +{ + if (!revents) + return; + + drain_child_process_err(child); + /* + * If the child closed its end of the pipe (EOF) or encountered + * an error, close the file descriptor immediately and set it + * to -1. This removes it from the pfds array for subsequent + * iterations, preventing a tight CPU busy-loop while waiting + * for the process itself to exit. + */ + if (revents & (POLLHUP | POLLERR | POLLNVAL)) { + close(child->process.err); + child->process.err = -1; + } +} + +static int finish_tests_parallel(struct child_test **child_tests, size_t num_tests, int width) +{ + size_t next_to_print = 0; + struct pollfd *pfds; + size_t *pfd_indices; + size_t num_pfds = 0; + int last_running = -1; + size_t i; + int last_suite_printed = -1; + sigset_t set, oldset; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + + pthread_sigmask(SIG_BLOCK, &set, &oldset); + global_pfds = calloc(num_tests, sizeof(*pfds)); + global_pfd_indices = calloc(num_tests, sizeof(*pfd_indices)); + pfds = global_pfds; + pfd_indices = global_pfd_indices; + if (!pfds || !pfd_indices) { + free(pfds); + free(pfd_indices); + global_pfds = NULL; + global_pfd_indices = NULL; + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return -ENOMEM; + } + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + for (i = 0; i < num_tests; i++) { + struct child_test *child = child_tests[i]; + + if (!child) + continue; + strbuf_init(&child->err_output, 0); + if (child->process.err >= 0) + fcntl(child->process.err, F_SETFL, O_NONBLOCK); + } + + while (next_to_print < num_tests) { + size_t running_count = 0; + size_t p; + + while (next_to_print < num_tests && + (!child_tests[next_to_print] || child_tests[next_to_print]->done)) + next_to_print++; + + if (next_to_print >= num_tests) + break; + + num_pfds = 0; + + for (i = next_to_print; i < num_tests; i++) { + struct child_test *child = child_tests[i]; + + if (!child || child->done) + continue; + + if (!check_if_command_finished(&child->process)) + running_count++; + + if (child->process.err >= 0) { + pfds[num_pfds].fd = child->process.err; + pfds[num_pfds].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + pfd_indices[num_pfds] = i; + num_pfds++; + } + } + + if (perf_use_color_default && running_count != (size_t)last_running) { + struct child_test *next_child = child_tests[next_to_print]; + + if (last_running != -1) + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); + + if (next_child) { + if (test_suite__num_test_cases(next_child->test) > 1 && + last_suite_printed != next_child->suite_num) { + pr_info("%3d: %-*s:\n", next_child->suite_num + 1, width, + test_description(next_child->test, -1)); + last_suite_printed = next_child->suite_num; + } + print_test_result(next_child->test, next_child->suite_num, + next_child->test_case_num, TEST_RUNNING, width, + running_count); + } + last_running = running_count; + } + + if (num_pfds == 0) { + if (running_count > 0) + usleep(10 * 1000); + } else { + int pret = poll(pfds, num_pfds, 100); + + if (pret > 0) { + for (p = 0; p < num_pfds; p++) { + size_t idx = pfd_indices[p]; + + handle_child_pipe_activity(child_tests[idx], + pfds[p].revents); + } + } + } + + for (i = next_to_print; i < num_tests; i++) { + struct child_test *child = child_tests[i]; + + if (!child || child->done) + continue; + + if (check_if_command_finished(&child->process)) { + if (child->process.err >= 0) { + drain_child_process_err(child); + close(child->process.err); + child->process.err = -1; + } + child->result = finish_command(&child->process); + child->process.pid = 0; + child->done = true; + } + } + + while (next_to_print < num_tests) { + struct child_test *child = child_tests[next_to_print]; + + if (!child) { + next_to_print++; + continue; + } + if (!child->done) + break; + + if (perf_use_color_default && last_running != -1) { + fprintf(debug_file(), PERF_COLOR_DELETE_LINE); + last_running = -1; + } + + if (test_suite__num_test_cases(child->test) > 1 && + last_suite_printed != child->suite_num) { + pr_info("%3d: %-*s:\n", child->suite_num + 1, width, + test_description(child->test, -1)); + last_suite_printed = child->suite_num; + } + + if (verbose > 1) { + if (test_suite__num_test_cases(child->test) > 1) { + pr_info("%3d.%1d: %s:\n", child->suite_num + 1, + child->test_case_num + 1, + test_description(child->test, + child->test_case_num)); + } else { + pr_info("%3d: %s:\n", child->suite_num + 1, + test_description(child->test, -1)); + } + } + + if (verbose > 1 || (verbose == 1 && child->result == TEST_FAIL)) + fprintf(stderr, "%s", child->err_output.buf); + + print_test_result(child->test, child->suite_num, child->test_case_num, + child->result, width, 0); + pthread_sigmask(SIG_BLOCK, &set, &oldset); + strbuf_release(&child->err_output); + child_tests[next_to_print] = NULL; + zfree(&child); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + next_to_print++; + } + } + + pthread_sigmask(SIG_BLOCK, &set, &oldset); + free(global_pfds); + free(global_pfd_indices); + global_pfds = NULL; + global_pfd_indices = NULL; + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return 0; +} + static int start_test(struct test_suite *test, int curr_suite, int curr_test_case, struct child_test **child, int width, int pass) { @@ -542,13 +787,14 @@ static int start_test(struct test_suite *test, int curr_suite, int curr_test_cas (*child)->test_case_num = curr_test_case; (*child)->process.pid = -1; (*child)->process.no_stdin = 1; + (*child)->process.in = -1; + (*child)->process.out = -1; + (*child)->process.err = -1; if (verbose <= 0) { (*child)->process.no_stdout = 1; (*child)->process.no_stderr = 1; } else { (*child)->process.stdout_to_stderr = 1; - (*child)->process.out = -1; - (*child)->process.err = -1; } (*child)->process.no_exec_cmd = run_test_child; if (sequential || pass == 2) { @@ -671,8 +917,9 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], } if (!sequential) { /* Parallel mode starts tests but doesn't finish them. Do that now. */ - for (size_t x = 0; x < num_tests; x++) - finish_test(child_tests, x, num_tests, width); + err = finish_tests_parallel(child_tests, num_tests, width); + if (err) + goto err_out; } } err_out: @@ -683,6 +930,10 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], for (size_t x = 0; x < num_tests; x++) finish_test(child_tests, x, num_tests, width); } + free(global_pfds); + free(global_pfd_indices); + global_pfds = NULL; + global_pfd_indices = NULL; free(child_tests); return err; } From b5a4a361f5cfb8f6f6f0a59536fd5fad11ed9f5f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:20 -0700 Subject: [PATCH 2806/5214] perf test: Show snippet failure output for verbose=1 Currently, when running tests in verbose mode (-v), if a test case fails, the entire raw standard error buffer is dumped to stderr via fprintf(stderr, "%s", child->err_output.buf). For tests that generate massive amounts of debugging or logging output before dying, this results in multi-page terminal dumps where highly critical diagnostic keywords (error, fail, segv) are easily lost. Implement a smart, bounded snippet string processor to improve failure triaging: 1. Introduce a configurable quota limit static unsigned int failure_snippet_lines = 10; accessible via a new command-line option --failure-snippet-lines . 2. Parse the raw error buffer dynamically into lines and run a three-pass extraction algorithm: - Pass 0: Always select the very first line of the log as an initial outline marker. - Pass 1: Scan forward from the top of the log to pick up to N lines that contain case-insensitive failure keywords (error, fail, segv, abort) to isolate the root cause. Automatically pull in the immediate subsequent line as highly-prioritized context. Allow adjacent matching lines to overlap without dropping context by evaluating keywords for all lines (e.g. when "Failed to report" is followed by "Error:"). - Pass 2: If quota remains, scan backward from the absolute tail of the log to capture trailing crash or abort context. 3. Output the selected lines in their original chronological order, inserting a clear ... separator between non-contiguous line jumps. 4. Wrap matched failure keywords dynamically in bold red (PERF_COLOR_RED) to immediately draw the eye to failures. 5. Invoke the smart processor purely when verbose == 1 && ret == TEST_FAIL in both finish_test and finish_tests_parallel, leaving raw full-output dumping completely untouched when running highly verbose (-vv). Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 203 +++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0479184c3dda..3401d79a1d24 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -48,6 +48,8 @@ static bool dont_fork; static bool sequential; /* Number of times each test is run. */ static unsigned int runs_per_test = 1; +/* Number of lines to include in failure snippet. */ +static unsigned int failure_snippet_lines = 10; const char *dso_to_test; const char *test_objdump_path = "objdump"; @@ -344,7 +346,7 @@ static int run_test_child(struct child_process *process) for (size_t i = 0; i < ARRAY_SIZE(signals); i++) signal(signals[i], child_test_sig_handler); - pr_debug("--- start ---\n"); + pr_debug("---- start ----\n"); pr_debug("test child forked, pid %d\n", getpid()); err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num); pr_debug("---- end(%d) ----\n", err); @@ -400,6 +402,195 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test return 0; } +static const char * const fail_keywords[] = { + "error", "fail", "segv", "abort", + "signal", "fatal", "panic", "corrupt", NULL +}; + +static const char *find_next_keyword(const char *str, size_t max_len, size_t *kw_len) +{ + const char *best = NULL; + size_t best_len = 0; + int k; + + for (k = 0; fail_keywords[k]; k++) { + const char *s = str; + size_t len = strlen(fail_keywords[k]); + + while ((size_t)(s - str) + len <= max_len) { + size_t i; + + if (best && s >= best) + break; + + for (i = 0; i < len; i++) { + if (tolower(s[i]) != fail_keywords[k][i]) + break; + } + if (i == len) { + if (!best || s < best) { + best = s; + best_len = len; + } + break; + } + s++; + } + } + if (best) { + *kw_len = best_len; + return best; + } + return NULL; +} + +static void print_line_highlighted(FILE *fp, const char *line, size_t len) +{ + const char *s = line; + + while (len > 0) { + size_t kw_len = 0; + const char *match = find_next_keyword(s, len, &kw_len); + + if (!match) { + fwrite(s, 1, len, fp); + break; + } + if (match > s) + fwrite(s, 1, match - s, fp); + if (perf_use_color_default) + fprintf(fp, "%s", PERF_COLOR_RED); + fwrite(match, 1, kw_len, fp); + if (perf_use_color_default) + fprintf(fp, "%s", PERF_COLOR_RESET); + + len -= (match + kw_len) - s; + s = match + kw_len; + } +} + + +static void print_test_failure_snippet(FILE *fp, const char *buf) +{ + size_t num_lines = 0; + size_t max_lines = 128; + const char **lines = calloc(max_lines, sizeof(const char *)); + size_t *line_lens = calloc(max_lines, sizeof(size_t)); + const char *s = buf; + size_t i; + unsigned int picked_count = 0; + bool *pick; + int last_printed = -1; + + if (!lines || !line_lens) { + free(lines); free(line_lens); + fprintf(fp, "%s", buf); + return; + } + + while (*s) { + const char *eol = strchr(s, '\n'); + size_t len; + + if (eol) + len = eol - s + 1; + else + len = strlen(s); + + if (num_lines == max_lines) { + const char **new_lines; + size_t *new_lens; + + max_lines *= 2; + new_lines = realloc(lines, max_lines * sizeof(const char *)); + if (!new_lines) { + free(lines); free(line_lens); + fprintf(fp, "%s", buf); + return; + } + lines = new_lines; + + new_lens = realloc(line_lens, max_lines * sizeof(size_t)); + if (!new_lens) { + free(lines); free(line_lens); + fprintf(fp, "%s", buf); + return; + } + line_lens = new_lens; + } + lines[num_lines] = s; + line_lens[num_lines] = len; + num_lines++; + s += len; + } + + if (num_lines <= failure_snippet_lines) { + for (i = 0; i < num_lines; i++) + print_line_highlighted(fp, lines[i], line_lens[i]); + free(lines); free(line_lens); + return; + } + + pick = calloc(num_lines, sizeof(bool)); + if (!pick) { + for (i = 0; i < num_lines; i++) + print_line_highlighted(fp, lines[i], line_lens[i]); + free(lines); free(line_lens); + return; + } + + /* Pass 0: Always pick the very first line */ + if (num_lines > 0 && picked_count < failure_snippet_lines) { + pick[0] = true; + picked_count++; + } + + /* Pass 1: Pick lines with failure keywords from start (Highest Priority) */ + for (i = 0; i < num_lines && picked_count < failure_snippet_lines; i++) { + size_t dummy; + + if (find_next_keyword(lines[i], line_lens[i], &dummy)) { + if (!pick[i]) { + pick[i] = true; + picked_count++; + } + /* Prioritize getting the immediate next line for context */ + if (i + 1 < num_lines && !pick[i + 1] && + picked_count < failure_snippet_lines) { + pick[i + 1] = true; + picked_count++; + } + } + } + + /* Pass 2: Fill remaining quota from the end backwards */ + i = num_lines; + while (i > 0 && picked_count < failure_snippet_lines) { + i--; + if (!pick[i]) { + pick[i] = true; + picked_count++; + } + } + + for (i = 0; i < num_lines; i++) { + if (!pick[i]) + continue; + if (last_printed != -1 && (int)i > last_printed + 1) { + if (perf_use_color_default) + fprintf(fp, "%s...%s\n", PERF_COLOR_BLUE, PERF_COLOR_RESET); + else + fprintf(fp, "...\n"); + } + print_line_highlighted(fp, lines[i], line_lens[i]); + last_printed = i; + } + + free(pick); + free(lines); + free(line_lens); +} + static void finish_test(struct child_test **child_tests, int running_test, int child_test_num, int width) { @@ -518,8 +709,10 @@ static void finish_test(struct child_test **child_tests, int running_test, int c strbuf_release(&err_output); err_output = merged; } - if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL)) + if (verbose > 1) fprintf(stderr, "%s", err_output.buf); + else if (verbose == 1 && ret == TEST_FAIL) + print_test_failure_snippet(stderr, err_output.buf); strbuf_release(&err_output); strbuf_release(&child_test->err_output); @@ -731,8 +924,10 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes } } - if (verbose > 1 || (verbose == 1 && child->result == TEST_FAIL)) + if (verbose > 1) fprintf(stderr, "%s", child->err_output.buf); + else if (verbose == 1 && child->result == TEST_FAIL) + print_test_failure_snippet(stderr, child->err_output.buf); print_test_result(child->test, child->suite_num, child->test_case_num, child->result, width, 0); @@ -1075,6 +1270,8 @@ int cmd_test(int argc, const char **argv) OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"), OPT_STRING(0, "objdump", &test_objdump_path, "path", "objdump binary to use for disassembly and annotations"), + OPT_UINTEGER(0, "failure-snippet-lines", &failure_snippet_lines, + "Number of lines to include in failure snippet, default 10"), OPT_END() }; const char * const test_subcommands[] = { "list", NULL }; From 33f20342ba525ef75fd9734db71e9823fe65e769 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:21 -0700 Subject: [PATCH 2807/5214] perf test: Add summary reporting Currently, when running test suites (perf test), users must scroll through hundreds of lines of console output to manually tally the number of passed, skipped, or failed test cases. Introduce an automated, global execution summary printed at the absolute tail of the test run: 1. Track counts mid-flight inside the print_test_result() accumulator, clearly separating pass counts into standalone main tests vs. individual subtests (where num_test_cases > 1). 2. Accumulate the precise descriptions of all failed test cases directly into a global string buffer, formatted with their suite indices (e.g., 3.1: Parse event definition strings) for effortless cross-referencing. 3. Define a summary printer function print_tests_summary() that emits a colored outline of the final pass, skip, and fail totals, followed by the explicit list of failed tests. 4. Invoke the summary printer right before freeing the test array at the absolute tail of __cmd_test(), guaranteeing that the summary is successfully printed even if an internal emergency signal cleanup occurs or if the user interrupts the run early. Example output: ``` $ sudo perf test -v 1: vmlinux symtab matches kallsyms : Skip 2: Detect openat syscall event : Ok 3: Detect openat syscall event on all cpus : Ok ... 163: perf trace summary : Ok === Test Summary === Passed main tests : 123 Passed subtests : 145 Skipped tests : 22 Failed tests : 6 List of failed tests: 92: perf kvm tests 95: kernel lock contention analysis test 120: perf metrics value validation 124: Check branch stack sampling 143: perftool-testsuite_probe 158: test Intel TPEBS counting mode ``` Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 89 +++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 3401d79a1d24..8883d4744057 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -363,8 +363,14 @@ static int run_test_child(struct child_process *process) static struct pollfd *global_pfds; static size_t *global_pfd_indices; +static unsigned int summary_tests_passed; +static unsigned int summary_subtests_passed; +static unsigned int summary_tests_skipped; +static unsigned int summary_tests_failed; +static struct strbuf summary_failed_tests_buf = STRBUF_INIT; static int strbuf_addstr_safe(struct strbuf *sb, const char *s); +static int __printf(2, 3) strbuf_addf_safe(struct strbuf *sb, const char *fmt, ...); static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case, int result, int width, int running) @@ -382,11 +388,16 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test color_fprintf(stderr, PERF_COLOR_YELLOW, " Running (%d active)\n", running); break; case TEST_OK: + if (test_suite__num_test_cases(t) > 1) + summary_subtests_passed++; + else + summary_tests_passed++; pr_info(" Ok\n"); break; case TEST_SKIP: { const char *reason = skip_reason(t, curr_test_case); + summary_tests_skipped++; if (reason) color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (%s)\n", reason); else @@ -395,6 +406,15 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test break; case TEST_FAIL: default: + summary_tests_failed++; + if (test_suite__num_test_cases(t) > 1) + strbuf_addf_safe(&summary_failed_tests_buf, " %3d.%1d: %s\n", + curr_suite + 1, curr_test_case + 1, + test_description(t, curr_test_case)); + else + strbuf_addf_safe(&summary_failed_tests_buf, " %3d: %s\n", + curr_suite + 1, + test_description(t, curr_test_case)); color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); break; } @@ -736,6 +756,47 @@ static int strbuf_addstr_safe(struct strbuf *sb, const char *s) return ret; } +static int __printf(2, 3) strbuf_addf_safe(struct strbuf *sb, const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + int len; + sigset_t set, oldset; + int ret; + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, &oldset); + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (len < 0) { + sigprocmask(SIG_SETMASK, &oldset, NULL); + return len; + } + if ((size_t)len >= sizeof(buf)) { + char *dynamic_buf = malloc(len + 1); + + if (!dynamic_buf) { + sigprocmask(SIG_SETMASK, &oldset, NULL); + return -ENOMEM; + } + va_start(ap, fmt); + vsnprintf(dynamic_buf, len + 1, fmt, ap); + va_end(ap); + ret = strbuf_addstr(sb, dynamic_buf); + free(dynamic_buf); + } else { + ret = strbuf_addstr(sb, buf); + } + + sigprocmask(SIG_SETMASK, &oldset, NULL); + return ret; +} + static void drain_child_process_err(struct child_test *child) { char buf[512]; @@ -1013,6 +1074,23 @@ static void cmd_test_sig_handler(int sig) siglongjmp(cmd_test_jmp_buf, sig); } +static void print_tests_summary(void) +{ + pr_info("\n=== Test Summary ===\n"); + pr_info("Passed main tests : %u\n", summary_tests_passed); + pr_info("Passed subtests : %u\n", summary_subtests_passed); + pr_info("Skipped tests : %u\n", summary_tests_skipped); + if (summary_tests_failed > 0) { + color_fprintf(stderr, PERF_COLOR_RED, "Failed tests : %u\n", + summary_tests_failed); + pr_info("List of failed tests:\n"); + pr_info("%s", summary_failed_tests_buf.buf); + } else { + color_fprintf(stderr, PERF_COLOR_GREEN, "Failed tests : 0\n"); + } + strbuf_release(&summary_failed_tests_buf); +} + static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], struct intlist *skiplist) { @@ -1090,9 +1168,13 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], } if (intlist__find(skiplist, curr_suite + 1)) { - pr_info("%3d: %-*s:", curr_suite + 1, width, - test_description(*t, -1)); - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); + if (pass == 1) { + pr_info("%3d: %-*s:", curr_suite + 1, width, + test_description(*t, -1)); + color_fprintf(stderr, PERF_COLOR_YELLOW, + " Skip (user override)\n"); + summary_tests_skipped++; + } continue; } @@ -1125,6 +1207,7 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], for (size_t x = 0; x < num_tests; x++) finish_test(child_tests, x, num_tests, width); } + print_tests_summary(); free(global_pfds); free(global_pfd_indices); global_pfds = NULL; From 94ac3ce427c8f80d699909c85a7cb70a589c562d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:22 -0700 Subject: [PATCH 2808/5214] perf test: Fix subtest status alignment for multi-digit indexes When running perf test, the status column (: Ok) became misaligned when subtest indexes reached 2 or 3 digits (e.g. 9.100 vs 9.9 vs 10.1). This occurred because the subtest description field width (subw) was statically fixed to width - 2, assuming all subtest index prefixes were exactly 7 characters wide. Dynamically calculate subw based on the exact character length of the test suite and subtest index prefix. This ensures the status column is perfectly aligned vertically across all test outputs regardless of subtest index digit count. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 8883d4744057..4773000f3199 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -376,10 +376,12 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test int result, int width, int running) { if (test_suite__num_test_cases(t) > 1) { - int subw = width > 2 ? width - 2 : width; + char prefix[32]; + int len = snprintf(prefix, sizeof(prefix), "%3d.%1d:", + curr_suite + 1, curr_test_case + 1); + int subw = len >= 4 ? width + 4 - len : width; - pr_info("%3d.%1d: %-*s:", curr_suite + 1, curr_test_case + 1, subw, - test_description(t, curr_test_case)); + pr_info("%s %-*s:", prefix, subw, test_description(t, curr_test_case)); } else pr_info("%3d: %-*s:", curr_suite + 1, width, test_description(t, curr_test_case)); From 85fe2fdb2726e8e85b0dc1217260d7b5bf0a5dca Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:23 -0700 Subject: [PATCH 2809/5214] perf test: Skip shebang and SPDX comments in shell test descriptions When extracting shell test descriptions in tests-scripts.c, the parser skipped the first line assuming it was the shebang (#!/bin/sh) and then read the first comment line on line 2 as the test description. However, checkpatch.pl expects shell scripts to declare their SPDX license identifier on line 2 (# SPDX-License-Identifier: ...). This caused the test harness to extract the SPDX license string as the test description. Refactor shell_test__description to use io__getline, skipping both shebang and SPDX comment lines. This allows shell tests to include standard SPDX headers without breaking test suite description extraction. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/tests-scripts.c | 80 ++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c index f18c4cd337c8..0370bc701373 100644 --- a/tools/perf/tests/tests-scripts.c +++ b/tools/perf/tests/tests-scripts.c @@ -31,6 +31,7 @@ static int shell_tests__dir_fd(void) { struct stat st; char path[PATH_MAX], path2[PATH_MAX], *exec_path; + ssize_t len; static const char * const devel_dirs[] = { "./tools/perf/tests/shell", "./tests/shell", @@ -47,13 +48,17 @@ static int shell_tests__dir_fd(void) } /* Use directory of executable */ - if (readlink("/proc/self/exe", path2, sizeof path2) < 0) + len = readlink("/proc/self/exe", path2, sizeof(path2) - 1); + if (len < 0) return -1; + path2[len] = '\0'; /* Follow another level of symlink if there */ if (lstat(path2, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) { - scnprintf(path, sizeof(path), path2); - if (readlink(path, path2, sizeof path2) < 0) + scnprintf(path, sizeof(path), "%s", path2); + len = readlink(path, path2, sizeof(path2) - 1); + if (len < 0) return -1; + path2[len] = '\0'; } /* Get directory */ p = strrchr(path2, '/'); @@ -78,43 +83,50 @@ static int shell_tests__dir_fd(void) static char *shell_test__description(int dir_fd, const char *name) { struct io io; - char buf[128], desc[256]; - int ch, pos = 0; + char buf[128], *line = NULL; + size_t line_len = 0; + ssize_t len; + char *desc = NULL; + const char *spdx = "SPDX-License"; io__init(&io, openat(dir_fd, name, O_RDONLY), buf, sizeof(buf)); if (io.fd < 0) return NULL; - /* Skip first line - should be #!/bin/bash Shebang */ - if (io__get_char(&io) != '#') - goto err_out; - if (io__get_char(&io) != '!') - goto err_out; - do { - ch = io__get_char(&io); - if (ch < 0) - goto err_out; - } while (ch != '\n'); + while ((len = io__getline(&io, &line, &line_len)) > 0) { + char *p = line; - do { - ch = io__get_char(&io); - if (ch < 0) - goto err_out; - } while (ch == '#' || isspace(ch)); - while (ch > 0 && ch != '\n') { - desc[pos++] = ch; - if (pos >= (int)sizeof(desc) - 1) + /* Skip leading whitespace */ + while (*p && isspace(*p)) + p++; + + /* Must be a comment */ + if (*p != '#') + continue; + p++; + + /* Skip shebang or SPDX lines */ + if (*p == '!' || (strstr(p, spdx) && strstr(p, "-Identifier:"))) + continue; + + /* Skip whitespace after # */ + while (*p && isspace(*p)) + p++; + + /* If we found non-empty text, this is the description! */ + if (*p && *p != '\n') { + char *end = p + strlen(p); + + while (end > p && isspace(end[-1])) + end--; + *end = '\0'; + desc = strdup(p); break; - ch = io__get_char(&io); + } } - while (pos > 0 && isspace(desc[--pos])) - ; - desc[++pos] = '\0'; + free(line); close(io.fd); - return strdup(desc); -err_out: - close(io.fd); - return NULL; + return desc; } /* Is this full file path a shell script */ @@ -178,9 +190,9 @@ static void append_script(int dir_fd, const char *name, char *desc, char *exclusive; snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd); - len = readlink(link, filename, sizeof(filename)); - if (len < 0) { - pr_err("Failed to readlink %s", link); + len = readlink(link, filename, sizeof(filename) - 1); + if (len < 0 || (size_t)len > sizeof(filename) - strlen(name) - 2) { + pr_err("Failed to readlink %s or path too long", link); return; } filename[len++] = '/'; From a540f031bbbceb6e390a71eb9f7f8d65363356ba Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:24 -0700 Subject: [PATCH 2810/5214] perf test: Split monolithic 'util' test suite into sub-tests Refactor the monolithic 'util' test suite into distinct 'String replacement' and 'BLAKE2s hash' sub-tests using the struct test_case framework. This improves test reporting granularity and is used in a subsequent perf test for JUnit XML test result reporting. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/util.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/util.c b/tools/perf/tests/util.c index bf2c5b133884..f9abd9911e6c 100644 --- a/tools/perf/tests/util.c +++ b/tools/perf/tests/util.c @@ -86,7 +86,12 @@ static int test_blake2s(void) return 0; } -static int test__util(struct test_suite *t __maybe_unused, int subtest __maybe_unused) +static int test__blake2s_case(struct test_suite *t __maybe_unused, int subtest __maybe_unused) +{ + return test_blake2s(); +} + +static int test__strreplace(struct test_suite *t __maybe_unused, int subtest __maybe_unused) { TEST_ASSERT_VAL("empty string", test_strreplace(' ', "", "123", "")); TEST_ASSERT_VAL("no match", test_strreplace('5', "123", "4", "123")); @@ -95,7 +100,16 @@ static int test__util(struct test_suite *t __maybe_unused, int subtest __maybe_u TEST_ASSERT_VAL("replace long", test_strreplace('a', "abcabc", "longlong", "longlongbclonglongbc")); - return test_blake2s(); + return 0; } -DEFINE_SUITE("util", util); +static struct test_case tests__util[] = { + TEST_CASE("String replacement", strreplace), + TEST_CASE("BLAKE2s hash", blake2s_case), + { .name = NULL, } +}; + +struct test_suite suite__util = { + .desc = "util", + .test_cases = tests__util, +}; From e2c545737bf4387d1217d3608274823421e1dbf2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:25 -0700 Subject: [PATCH 2811/5214] perf test: Add -j/--junit option for JUnit XML test reports Add a -j/--junit command line option to generate standard JUnit XML format test reports. The generated file defaults to 'test.xml' if no filename is specified, but allows users to override the path (e.g. -jmytest.xml). The XML report captures individual test suite and subtest execution latency, alongside XML-escaped failure logs and skip reasons, while preserving the full multi-process concurrency speed of parallel test execution. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20260602174129.3192312-15-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 148 ++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 4773000f3199..37b91f4e9273 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "builtin.h" #include "config.h" #include "hist.h" @@ -39,6 +40,9 @@ #include "tests-scripts.h" +static const char *junit_filename; +static struct strbuf junit_xml_buf = STRBUF_INIT; + /* * Command line option to not fork the test running in the same process and * making them easier to debug. @@ -307,6 +311,8 @@ struct child_test { struct strbuf err_output; int result; bool done; + struct timespec start_time; + struct timespec end_time; }; static jmp_buf run_test_jmp_buf; @@ -372,8 +378,34 @@ static struct strbuf summary_failed_tests_buf = STRBUF_INIT; static int strbuf_addstr_safe(struct strbuf *sb, const char *s); static int __printf(2, 3) strbuf_addf_safe(struct strbuf *sb, const char *fmt, ...); +static char *xml_escape(const char *str) +{ + struct strbuf buf = STRBUF_INIT; + const char *p; + char *res; + + if (!str) + return strdup(""); + + for (p = str; *p; p++) { + if (*p == '&') + strbuf_addstr(&buf, "&"); + else if (*p == '<') + strbuf_addstr(&buf, "<"); + else if (*p == '>') + strbuf_addstr(&buf, ">"); + else if (*p == '"') + strbuf_addstr(&buf, """); + else if ((unsigned char)*p >= 32 || *p == '\n' || *p == '\t') + strbuf_addch(&buf, *p); + } + res = strbuf_detach(&buf, NULL); + return res ? res : strdup(""); +} + static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case, - int result, int width, int running) + int result, int width, int running, + const char *err_output, double elapsed) { if (test_suite__num_test_cases(t) > 1) { char prefix[32]; @@ -421,6 +453,34 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test break; } + if (junit_filename && result != TEST_RUNNING) { + const char *classname = t->desc; + const char *testname = test_description(t, curr_test_case); + char *escaped_err = xml_escape(err_output); + char *escaped_class = xml_escape(classname); + char *escaped_test = xml_escape(testname); + + strbuf_addf(&junit_xml_buf, + " \n", + escaped_class, escaped_test, elapsed); + if (result != TEST_OK && result != TEST_SKIP) { + strbuf_addf(&junit_xml_buf, + " \n%s\n \n", + escaped_err); + } else if (result == TEST_SKIP) { + const char *reason = skip_reason(t, curr_test_case); + char *escaped_reason = xml_escape(reason ? reason : "Skip"); + + strbuf_addf(&junit_xml_buf, " \n", + escaped_reason); + free(escaped_reason); + } + strbuf_addstr(&junit_xml_buf, " \n"); + free(escaped_err); + free(escaped_class); + free(escaped_test); + } + return 0; } @@ -623,6 +683,8 @@ static void finish_test(struct child_test **child_tests, int running_test, int c struct strbuf err_output = STRBUF_INIT; int last_running = -1; int ret; + struct timespec end_time; + double elapsed; if (child_test == NULL) { /* Test wasn't started. */ @@ -676,7 +738,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c fprintf(debug_file(), PERF_COLOR_DELETE_LINE); } print_test_result(t, curr_suite, curr_test_case, TEST_RUNNING, - width, running); + width, running, NULL, 0.0); last_running = running; } } @@ -736,9 +798,14 @@ static void finish_test(struct child_test **child_tests, int running_test, int c else if (verbose == 1 && ret == TEST_FAIL) print_test_failure_snippet(stderr, err_output.buf); + clock_gettime(CLOCK_MONOTONIC, &end_time); + elapsed = (end_time.tv_sec - child_test->start_time.tv_sec) + + (end_time.tv_nsec - child_test->start_time.tv_nsec) / 1000000000.0; + + print_test_result(t, curr_suite, curr_test_case, ret, width, /*running=*/0, + err_output.buf, elapsed); strbuf_release(&err_output); strbuf_release(&child_test->err_output); - print_test_result(t, curr_suite, curr_test_case, ret, width, /*running=*/0); if (err > 0) close(err); zfree(&child_tests[running_test]); @@ -914,7 +981,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes } print_test_result(next_child->test, next_child->suite_num, next_child->test_case_num, TEST_RUNNING, width, - running_count); + running_count, NULL, 0.0); } last_running = running_count; } @@ -949,12 +1016,14 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes } child->result = finish_command(&child->process); child->process.pid = 0; + clock_gettime(CLOCK_MONOTONIC, &child->end_time); child->done = true; } } while (next_to_print < num_tests) { struct child_test *child = child_tests[next_to_print]; + double elapsed; if (!child) { next_to_print++; @@ -992,8 +1061,12 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes else if (verbose == 1 && child->result == TEST_FAIL) print_test_failure_snippet(stderr, child->err_output.buf); + elapsed = (child->end_time.tv_sec - child->start_time.tv_sec) + + (child->end_time.tv_nsec - + child->start_time.tv_nsec) / 1000000000.0; + print_test_result(child->test, child->suite_num, child->test_case_num, - child->result, width, 0); + child->result, width, 0, child->err_output.buf, elapsed); pthread_sigmask(SIG_BLOCK, &set, &oldset); strbuf_release(&child->err_output); child_tests[next_to_print] = NULL; @@ -1020,11 +1093,18 @@ static int start_test(struct test_suite *test, int curr_suite, int curr_test_cas *child = NULL; if (dont_fork) { if (pass == 1) { + struct timespec start_time, end_time; + double elapsed; + + clock_gettime(CLOCK_MONOTONIC, &start_time); pr_debug("--- start ---\n"); err = test_function(test, curr_test_case)(test, curr_test_case); pr_debug("---- end ----\n"); + clock_gettime(CLOCK_MONOTONIC, &end_time); + elapsed = (end_time.tv_sec - start_time.tv_sec) + + (end_time.tv_nsec - start_time.tv_nsec) / 1000000000.0; print_test_result(test, curr_suite, curr_test_case, err, width, - /*running=*/0); + /*running=*/0, NULL, elapsed); } return 0; } @@ -1090,6 +1170,41 @@ static void print_tests_summary(void) } else { color_fprintf(stderr, PERF_COLOR_GREEN, "Failed tests : 0\n"); } + + if (junit_filename) { + int fd; + FILE *fp; + + fd = open(junit_filename, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0644); + if (fd >= 0) { + fp = fdopen(fd, "w"); + if (fp) { + unsigned int total = summary_tests_passed + + summary_subtests_passed + + summary_tests_skipped + + summary_tests_failed; + fprintf(fp, "\n"); + fprintf(fp, "\n"); + fprintf(fp, + " \n", + total, summary_tests_failed, + summary_tests_skipped); + fprintf(fp, "%s", junit_xml_buf.buf); + fprintf(fp, " \n"); + fprintf(fp, "\n"); + fclose(fp); + pr_info("Wrote junit XML output to %s\n", junit_filename); + } else { + close(fd); + pr_err("Failed to associate stream with fd for %s: %s\n", + junit_filename, strerror(errno)); + } + } else { + pr_err("Failed to open %s for writing junit XML output: %s\n", + junit_filename, strerror(errno)); + } + } + strbuf_release(&junit_xml_buf); strbuf_release(&summary_failed_tests_buf); } @@ -1176,6 +1291,25 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[], color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); summary_tests_skipped++; + if (junit_filename) { + char *escaped_class = + xml_escape((const char *) + test_description(*t, -1)); + char *escaped_test = xml_escape("override"); + char *escaped_reason = + xml_escape("user override"); + + strbuf_addf(&junit_xml_buf, + " \n", + escaped_class, escaped_test); + strbuf_addf(&junit_xml_buf, + " \n", + escaped_reason); + strbuf_addstr(&junit_xml_buf, " \n"); + free(escaped_reason); + free(escaped_test); + free(escaped_class); + } } continue; } @@ -1357,6 +1491,8 @@ int cmd_test(int argc, const char **argv) "objdump binary to use for disassembly and annotations"), OPT_UINTEGER(0, "failure-snippet-lines", &failure_snippet_lines, "Number of lines to include in failure snippet, default 10"), + OPT_STRING_OPTARG('j', "junit", &junit_filename, "file", + "Generate junit XML output, default test.xml", "test.xml"), OPT_END() }; const char * const test_subcommands[] = { "list", NULL }; From de296b53775cd1ccb6b448b4cc8d843c8858e1df Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 7 May 2026 16:46:06 +0300 Subject: [PATCH 2812/5214] dt-bindings: embedded-controller: Document Surface RT EC Document Embedded Controller used in Microsoft Surface RT tablets for monitoring battery properties and charger status. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260507134608.76222-2-clamor95@gmail.com Signed-off-by: Sebastian Reichel --- .../microsoft,surface-rt-ec.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/embedded-controller/microsoft,surface-rt-ec.yaml diff --git a/Documentation/devicetree/bindings/embedded-controller/microsoft,surface-rt-ec.yaml b/Documentation/devicetree/bindings/embedded-controller/microsoft,surface-rt-ec.yaml new file mode 100644 index 000000000000..0fee574a3015 --- /dev/null +++ b/Documentation/devicetree/bindings/embedded-controller/microsoft,surface-rt-ec.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/embedded-controller/microsoft,surface-rt-ec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microsoft Surface RT fuel gauge and charger EC + +maintainers: + - Jonas Schwöbel + - Svyatoslav Ryhel + +description: + An Embedded Controller used in Microsoft Surface RT for monitoring + battery properties and charger status. + +allOf: + - $ref: /schemas/power/supply/power-supply.yaml# + +properties: + compatible: + const: microsoft,surface-rt-ec + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + enable-gpios: + maxItems: 1 + + monitored-battery: true + +required: + - compatible + - reg + - interrupts + - enable-gpios + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + embedded-controller@a { + compatible = "microsoft,surface-rt-ec"; + reg = <0x0a>; + + interrupt-parent = <&gpio>; + interrupts = <74 IRQ_TYPE_EDGE_RISING>; + + enable-gpios = <&gpio 88 GPIO_ACTIVE_HIGH>; + monitored-battery = <&battery>; + }; + }; +... From 3d6529b837de49294cea0d4600144353f5f084dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Schw=C3=B6bel?= Date: Thu, 7 May 2026 16:46:07 +0300 Subject: [PATCH 2813/5214] power: supply: Add support for Surface RT battery and charger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Embedded Controller found in the Microsoft Surface RT and used to monitor battery cell and charger input status and properties. Controller works both for UEFI and APX booting. [wmjb: added POWER_SUPPLY_PROP_CHARGE_NOW support] Signed-off-by: Jethro Bull Signed-off-by: Jonas Schwöbel Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260507134608.76222-3-clamor95@gmail.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/surface-rt-ec.c | 389 +++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 drivers/power/supply/surface-rt-ec.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 83392ed6a8da..c56d7ace7d55 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -1135,6 +1135,17 @@ config BATTERY_UG3105 device is off or suspended, the functionality of this driver is limited to reporting capacity only. +config BATTERY_CHARGER_SURFACE_RT + tristate "Battery & Charger driver for Microsoft Surface RT" + depends on I2C && GPIOLIB + help + UEFI/APX driver for the 1st-generation Microsoft Surface RT + battery. Driver supports reading battery properties and + charger status. + + This driver can also be built as a module. If so, the module + will be called surface-rt-ec. + config CHARGER_QCOM_SMB2 tristate "Qualcomm PMI8998 PMIC charger driver" depends on MFD_SPMI_PMIC diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 7ee839dca7f3..b55612d626c8 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o +obj-$(CONFIG_BATTERY_CHARGER_SURFACE_RT) += surface-rt-ec.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o diff --git a/drivers/power/supply/surface-rt-ec.c b/drivers/power/supply/surface-rt-ec.c new file mode 100644 index 000000000000..a728ae0db858 --- /dev/null +++ b/drivers/power/supply/surface-rt-ec.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Addresses (B=byte; W=word; S=string) */ +#define REGB_STATUS 0x02 +#define REGW_VOLTAGE_NOW 0x20 +#define REGW_CURRENT_NOW 0x24 +#define REGW_CAPACITY 0x28 +#define REGW_CHARGE_NOW 0x2a +#define REGW_CHARGE_FULL 0x2c +#define REGW_CYCLE_COUNT 0x3a +#define REGW_CHARGE_FULL_DESIGN 0x3c +#define REGW_VOLTAGE_MAX_DESIGN 0x3e +#define REGW_SERIAL_NUMBER 0x44 +#define REGS_MANUFACTURER 0x46 +#define REGS_MODEL_NAME 0x52 +#define REGS_TECHNOLOGY 0x5a +#define REGB_ONLINE 0x67 + +struct srt_ec_device { + struct i2c_client *client; + + struct power_supply *bat; + struct power_supply *psy; + + struct gpio_desc *enable_gpiod; + struct delayed_work poll_work; + + unsigned int technology; + unsigned int capacity; + + const char *serial; + char manufacturer[13]; + char model_name[10]; +}; + +static const enum power_supply_property srt_bat_power_supply_props[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static const enum power_supply_property srt_psy_power_supply_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, +}; + +static int srt_bat_get_value(struct i2c_client *client, int reg, int *val) +{ + int ret; + + switch (reg) { + case REGW_CHARGE_NOW: + case REGW_CHARGE_FULL_DESIGN: + case REGW_CHARGE_FULL: + case REGW_VOLTAGE_MAX_DESIGN: + case REGW_VOLTAGE_NOW: + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + *val = ret * 1000; + break; + + case REGW_CURRENT_NOW: + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + *val = (s16)ret * 1000; + break; + + case REGW_CAPACITY: + case REGW_CYCLE_COUNT: + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + *val = ret; + break; + + case REGB_STATUS: + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + + if (ret & BIT(0)) + *val = POWER_SUPPLY_STATUS_CHARGING; + else + *val = POWER_SUPPLY_STATUS_DISCHARGING; + break; + + case REGB_ONLINE: + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + + *val = (ret & BIT(1)) >> 1; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int srt_bat_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct srt_ec_device *srt = power_supply_get_drvdata(psy); + struct i2c_client *client = srt->client; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = srt->manufacturer; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = srt->model_name; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = srt->serial; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = srt_bat_get_value(client, REGW_CAPACITY, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = srt_bat_get_value(client, REGW_CHARGE_NOW, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = srt_bat_get_value(client, REGW_CHARGE_FULL, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = srt_bat_get_value(client, REGW_CHARGE_FULL_DESIGN, + &val->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = srt_bat_get_value(client, REGW_CURRENT_NOW, &val->intval); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = srt_bat_get_value(client, REGW_CYCLE_COUNT, &val->intval); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = srt_bat_get_value(client, REGB_ONLINE, &val->intval); + break; + case POWER_SUPPLY_PROP_STATUS: + if (srt->capacity < 100) + ret = srt_bat_get_value(client, REGB_STATUS, &val->intval); + else + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = srt->technology; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + ret = srt_bat_get_value(client, REGW_VOLTAGE_MAX_DESIGN, + &val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = srt_bat_get_value(client, REGW_VOLTAGE_NOW, &val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int srt_psy_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct srt_ec_device *srt = power_supply_get_drvdata(psy); + struct i2c_client *client = srt->client; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_PRESENT: + ret = i2c_smbus_read_byte_data(client, REGB_ONLINE); + if (ret < 0) + return ret; + + val->intval = ret & BIT(0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void srt_bat_poll_work(struct work_struct *work) +{ + struct srt_ec_device *srt = + container_of(work, struct srt_ec_device, poll_work.work); + int ret, capacity; + + ret = srt_bat_get_value(srt->client, REGW_CAPACITY, &capacity); + if (!ret && capacity != srt->capacity) { + srt->capacity = capacity; + power_supply_changed(srt->bat); + } + + /* continuously send uevent notification */ + schedule_delayed_work(&srt->poll_work, 30 * HZ); +} + +static irqreturn_t srt_psy_detect_irq(int irq, void *dev_id) +{ + struct srt_ec_device *srt = dev_id; + + power_supply_changed(srt->psy); + + return IRQ_HANDLED; +} + +static const struct power_supply_desc srt_bat_power_supply_desc = { + .name = "surface-rt-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = srt_bat_power_supply_props, + .num_properties = ARRAY_SIZE(srt_bat_power_supply_props), + .get_property = srt_bat_power_supply_get_property, + .external_power_changed = power_supply_changed, +}; + +static const struct power_supply_desc srt_psy_power_supply_desc = { + .name = "surface-rt-ac-adapter", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = srt_psy_power_supply_props, + .num_properties = ARRAY_SIZE(srt_psy_power_supply_props), + .get_property = srt_psy_power_supply_get_property, +}; + +static char *battery_supplied_to[] = { "surface-rt-battery" }; + +static int srt_ec_probe(struct i2c_client *client) +{ + struct power_supply_config bat_cfg = {}; + struct power_supply_config psy_cfg = {}; + struct device *dev = &client->dev; + struct srt_ec_device *srt; + char str_buf[4]; + int ret; + + srt = devm_kzalloc(dev, sizeof(*srt), GFP_KERNEL); + if (!srt) + return -ENOMEM; + + i2c_set_clientdata(client, srt); + srt->client = client; + + srt->enable_gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(srt->enable_gpiod)) + return dev_err_probe(dev, PTR_ERR(srt->enable_gpiod), + "failed to get enable gpio\n"); + + /* wait till EC is ready */ + usleep_range(1000, 1500); + + ret = i2c_smbus_read_word_data(client, REGW_SERIAL_NUMBER); + if (ret < 0) + return ret; + + srt->serial = devm_kasprintf(dev, GFP_KERNEL, "%04x", ret); + if (!srt->serial) + return -ENOMEM; + + ret = i2c_smbus_read_i2c_block_data(client, REGS_MANUFACTURER, + sizeof(srt->manufacturer) - 1, + srt->manufacturer); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_i2c_block_data(client, REGS_MODEL_NAME, + sizeof(srt->model_name) - 1, + srt->model_name); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_i2c_block_data(client, REGS_TECHNOLOGY, + sizeof(str_buf), str_buf); + if (ret < 0) + return ret; + + if (!strncmp(str_buf, "LION", 4)) + srt->technology = POWER_SUPPLY_TECHNOLOGY_LION; + else + srt->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + + bat_cfg.drv_data = srt; + bat_cfg.fwnode = dev_fwnode(dev); + + srt->bat = devm_power_supply_register(dev, &srt_bat_power_supply_desc, + &bat_cfg); + if (IS_ERR(srt->bat)) + return dev_err_probe(dev, PTR_ERR(srt->bat), + "failed to register battery power supply\n"); + + psy_cfg.drv_data = srt; + psy_cfg.fwnode = dev_fwnode(dev); + psy_cfg.supplied_to = battery_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to); + + srt->psy = devm_power_supply_register(dev, &srt_psy_power_supply_desc, + &psy_cfg); + if (IS_ERR(srt->psy)) + return dev_err_probe(dev, PTR_ERR(srt->psy), + "failed to register AC power supply\n"); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, srt_psy_detect_irq, + IRQF_ONESHOT, client->name, srt); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + + ret = devm_delayed_work_autocancel(dev, &srt->poll_work, srt_bat_poll_work); + if (ret < 0) + return ret; + + schedule_delayed_work(&srt->poll_work, HZ); + + return 0; +} + +static int srt_ec_suspend(struct device *dev) +{ + struct srt_ec_device *srt = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&srt->poll_work); + + return 0; +} + +static int srt_ec_resume(struct device *dev) +{ + struct srt_ec_device *srt = dev_get_drvdata(dev); + + schedule_delayed_work(&srt->poll_work, HZ); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(srt_ec_pm_ops, srt_ec_suspend, srt_ec_resume); + +static const struct of_device_id srt_ec_of_match[] = { + { .compatible = "microsoft,surface-rt-ec" }, + { } +}; +MODULE_DEVICE_TABLE(of, srt_ec_of_match); + +static struct i2c_driver srt_ec_driver = { + .driver = { + .name = "surface-rt-ec", + .of_match_table = srt_ec_of_match, + .pm = &srt_ec_pm_ops, + }, + .probe = srt_ec_probe, +}; +module_i2c_driver(srt_ec_driver); + +MODULE_AUTHOR("Jonas Schwöbel "); +MODULE_DESCRIPTION("Surface RT Embedded Controller driver"); +MODULE_LICENSE("GPL"); From 0dd6025171436822e54ae556dc48aed89c42b6a9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:26 -0700 Subject: [PATCH 2814/5214] perf test: Add shell test to validate JUnit XML reporting output Add a shell test script (test_test_junit_output.sh) to execute perf test with the -j/--junit option and validate that the generated test report complies perfectly with standard XML formatting using Python's ElementTree XML parser. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- .../tests/shell/test_test_junit_output.sh | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100755 tools/perf/tests/shell/test_test_junit_output.sh diff --git a/tools/perf/tests/shell/test_test_junit_output.sh b/tools/perf/tests/shell/test_test_junit_output.sh new file mode 100755 index 000000000000..5104ac1e1e6d --- /dev/null +++ b/tools/perf/tests/shell/test_test_junit_output.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# perf test junit XML output validation + +set -e + +err=0 + +shelldir=$(dirname "$0") +# shellcheck source=lib/setup_python.sh +. "${shelldir}"/lib/setup_python.sh + +result=$(mktemp /tmp/__perf_test.output.xml.XXXXX) + +cleanup() +{ + rm -f "${result}" + trap - exit term int +} + +trap_cleanup() +{ + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup exit term int + +test_junit_output() +{ + echo "Testing perf test JUnit XML output command" + perf test -v -j"$result" util || true + if [ -s "$result" ] ; then + echo "perf test JUnit XML output command [SUCCESS]" + else + echo "perf test JUnit XML output command [FAILED]" + err=1 + fi +} + +validate_xml_format() +{ + echo "Validating perf test converted JUnit XML file" + if [ -f "$result" ] ; then + if $PYTHON -c \ + "import xml.etree.ElementTree as ET; ET.parse('$result')" \ + >/dev/null 2>&1 ; then + echo "The file contains valid XML format [SUCCESS]" + else + echo "The file does not contain valid XML format [FAILED]" + err=1 + fi + else + echo "File not found [FAILED]" + err=1 + fi +} + +test_junit_output +validate_xml_format + +cleanup +exit ${err} From a77ecea7ced2fef7cc0a8ad0323542f781ad9788 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 2 Jun 2026 10:41:27 -0700 Subject: [PATCH 2815/5214] perf test: Remove /usr/bin/cc dependency from Intel PT shell test In test_intel_pt.sh, the test script compiled two external C programs at runtime using /usr/bin/cc (a thread loop workload and a JIT self- modifying workload). Relying on external C compilers inside shell tests frequently causes failures in continuous integration environments. Create a built-in 'jitdump' workload and switch test_intel_pt.sh to use 'perf test -w thloop' and 'perf test -w jitdump'. Also add multi- architecture compatibility without external C compiler dependencies, the workload instruction arrays dynamically encode CHK_BYTE into opcodes across x86, ARM32, ARM64, RISC-V, PowerPC, MIPS, LoongArch, and s390x. Some minor include fixes for util/jitdump.h. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/shell/test_intel_pt.sh | 169 +------------------ tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 1 + tools/perf/tests/workloads/jitdump.c | 210 ++++++++++++++++++++++++ tools/perf/util/jitdump.h | 3 +- 6 files changed, 217 insertions(+), 168 deletions(-) create mode 100644 tools/perf/tests/workloads/jitdump.c diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 37b91f4e9273..b64fc2204f22 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -161,6 +161,7 @@ static struct test_workload *workloads[] = { &workload__landlock, &workload__traploop, &workload__inlineloop, + &workload__jitdump, #ifdef HAVE_RUST_SUPPORT &workload__code_with_type, diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/shell/test_intel_pt.sh index 8ee761f03c38..26243ff760ec 100755 --- a/tools/perf/tests/shell/test_intel_pt.sh +++ b/tools/perf/tests/shell/test_intel_pt.sh @@ -21,9 +21,7 @@ tmpfile="${temp_dir}/tmp-perf.data" perfdatafile="${temp_dir}/test-perf.data" outfile="${temp_dir}/test-out.txt" errfile="${temp_dir}/test-err.txt" -workload="${temp_dir}/workload" awkscript="${temp_dir}/awkscript" -jitdump_workload="${temp_dir}/jitdump_workload" maxbrstack="${temp_dir}/maxbrstack.py" cleanup() @@ -60,37 +58,6 @@ perf_record_no_bpf() perf record --no-bpf-event "$@" } -have_workload=false -cat << _end_of_file_ | /usr/bin/cc -o "${workload}" -xc - -pthread && have_workload=true -#include -#include - -void work(void) { - struct timespec tm = { - .tv_nsec = 1000000, - }; - int i; - - /* Run for about 30 seconds */ - for (i = 0; i < 30000; i++) - nanosleep(&tm, NULL); -} - -void *threadfunc(void *arg) { - work(); - return NULL; -} - -int main(void) { - pthread_t th; - - pthread_create(&th, NULL, threadfunc, NULL); - work(); - pthread_join(th, NULL); - return 0; -} -_end_of_file_ - can_cpu_wide() { echo "Checking for CPU-wide recording on CPU $1" @@ -145,11 +112,6 @@ test_per_thread() echo "--- Test per-thread ${desc}recording ---" - if ! $have_workload ; then - echo "No workload, so skipping" - return 2 - fi - if [ "${k}" = "k" ] ; then can_kernel || return 2 fi @@ -252,9 +214,9 @@ test_per_thread() } _end_of_file_ - $workload & + perf test -w thloop 30 2 & w1=$! - $workload & + perf test -w thloop 30 2 & w2=$! echo "Workload PIDs are $w1 and $w2" wait_for_threads ${w1} 2 @@ -283,139 +245,14 @@ test_jitdump() { echo "--- Test tracing self-modifying code that uses jitdump ---" - script_path=$(realpath "$0") - script_dir=$(dirname "$script_path") - jitdump_incl_dir="${script_dir}/../../util" - jitdump_h="${jitdump_incl_dir}/jitdump.h" - if ! perf check feature -q libelf ; then echo "SKIP: libelf is needed for jitdump" return 2 fi - if [ ! -e "${jitdump_h}" ] ; then - echo "SKIP: Include file jitdump.h not found" - return 2 - fi - - if [ -z "${have_jitdump_workload}" ] ; then - have_jitdump_workload=false - # Create a workload that uses self-modifying code and generates its own jitdump file - cat <<- "_end_of_file_" | /usr/bin/cc -o "${jitdump_workload}" -I "${jitdump_incl_dir}" -xc - -pthread && have_jitdump_workload=true - #define _GNU_SOURCE - #include - #include - #include - #include - #include - #include - #include - - #include "jitdump.h" - - #define CHK_BYTE 0x5a - - static inline uint64_t rdtsc(void) - { - unsigned int low, high; - - asm volatile("rdtsc" : "=a" (low), "=d" (high)); - - return low | ((uint64_t)high) << 32; - } - - static FILE *open_jitdump(void) - { - struct jitheader header = { - .magic = JITHEADER_MAGIC, - .version = JITHEADER_VERSION, - .total_size = sizeof(header), - .pid = getpid(), - .timestamp = rdtsc(), - .flags = JITDUMP_FLAGS_ARCH_TIMESTAMP, - }; - char filename[256]; - FILE *f; - void *m; - - snprintf(filename, sizeof(filename), "jit-%d.dump", getpid()); - f = fopen(filename, "w+"); - if (!f) - goto err; - /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */ - m = mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0); - if (m == MAP_FAILED) - goto err_close; - munmap(m, 4096); - if (fwrite(&header,sizeof(header),1,f) != 1) - goto err_close; - return f; - - err_close: - fclose(f); - err: - return NULL; - } - - static int write_jitdump(FILE *f, void *addr, const uint8_t *dat, size_t sz, uint64_t *idx) - { - struct jr_code_load rec = { - .p.id = JIT_CODE_LOAD, - .p.total_size = sizeof(rec) + sz, - .p.timestamp = rdtsc(), - .pid = getpid(), - .tid = gettid(), - .vma = (unsigned long)addr, - .code_addr = (unsigned long)addr, - .code_size = sz, - .code_index = ++*idx, - }; - - if (fwrite(&rec,sizeof(rec),1,f) != 1 || - fwrite(dat, sz, 1, f) != 1) - return -1; - return 0; - } - - static void close_jitdump(FILE *f) - { - fclose(f); - } - - int main() - { - /* Get a memory page to store executable code */ - void *addr = mmap(0, 4096, PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - /* Code to execute: mov CHK_BYTE, %eax ; ret */ - uint8_t dat[] = {0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3}; - FILE *f = open_jitdump(); - uint64_t idx = 0; - int ret = 1; - - if (!f) - return 1; - /* Copy executable code to executable memory page */ - memcpy(addr, dat, sizeof(dat)); - /* Record it in the jitdump file */ - if (write_jitdump(f, addr, dat, sizeof(dat), &idx)) - goto out_close; - /* Call it */ - ret = ((int (*)(void))addr)() - CHK_BYTE; - out_close: - close_jitdump(f); - return ret; - } - _end_of_file_ - fi - - if ! $have_jitdump_workload ; then - echo "SKIP: No jitdump workload" - return 2 - fi - # Change to temp_dir so jitdump collateral files go there cd "${temp_dir}" - perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u "${jitdump_workload}" + perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u perf test -w jitdump perf inject -i "${tmpfile}" -o "${perfdatafile}" --jit decode_br_cnt=$(perf script -i "${perfdatafile}" --itrace=b | wc -l) # Note that overflow and lost errors are suppressed for the error count diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 9bcf1dbb0663..bf8ff7d54727 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -244,6 +244,7 @@ DECLARE_WORKLOAD(datasym); DECLARE_WORKLOAD(landlock); DECLARE_WORKLOAD(traploop); DECLARE_WORKLOAD(inlineloop); +DECLARE_WORKLOAD(jitdump); #ifdef HAVE_RUST_SUPPORT DECLARE_WORKLOAD(code_with_type); diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 2ef97f7affce..0eb6d99528eb 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -9,6 +9,7 @@ perf-test-y += datasym.o perf-test-y += landlock.o perf-test-y += traploop.o perf-test-y += inlineloop.o +perf-test-y += jitdump.o ifeq ($(CONFIG_RUST_SUPPORT),y) perf-test-y += code_with_type.o diff --git a/tools/perf/tests/workloads/jitdump.c b/tools/perf/tests/workloads/jitdump.c new file mode 100644 index 000000000000..01cbbbd564e8 --- /dev/null +++ b/tools/perf/tests/workloads/jitdump.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "util/jitdump.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../tests.h" + +#ifndef HAVE_GETTID +#include +static inline pid_t gettid(void) +{ + return (pid_t)syscall(SYS_gettid); +} +#endif + +#define CHK_BYTE 0x5a + +static inline uint64_t get_timestamp(void) +{ +#if defined(__x86_64__) || defined(__i386__) + unsigned int low, high; + + asm volatile("rdtsc" : "=a"(low), "=d"(high)); + + return low | ((uint64_t)high) << 32; +#else + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret) + return 0; + + return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec; +#endif +} + +static FILE *open_jitdump(void) +{ + struct jitheader header = { + .magic = JITHEADER_MAGIC, + .version = JITHEADER_VERSION, + .total_size = sizeof(header), + .pid = getpid(), + .timestamp = get_timestamp(), + .flags = +#if defined(__x86_64__) || defined(__i386__) + JITDUMP_FLAGS_ARCH_TIMESTAMP, +#else + 0, +#endif + }; + char filename[256]; + int fd; + FILE *f; + void *m; + + snprintf(filename, sizeof(filename), "jit-%d.dump", getpid()); + /* Securely open using O_CREAT | O_EXCL to prevent symlink attacks. */ + fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644); + if (fd < 0) { + pr_err("Failed to open jitdump '%s': %s\n", filename, strerror(errno)); + return NULL; + } + f = fdopen(fd, "w+"); + if (!f) { + pr_err("Failed to associate stream with fd for '%s'\n", filename); + close(fd); + unlink(filename); + return NULL; + } + /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */ + m = mmap(0, getpagesize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0); + if (m == MAP_FAILED) { + pr_err("mmap failed: %s\n", strerror(errno)); + fclose(f); + unlink(filename); + return NULL; + } + munmap(m, getpagesize()); + + if (fwrite(&header, sizeof(header), 1, f) != 1) { + pr_err("Error writing jitdump header\n"); + fclose(f); + unlink(filename); + return NULL; + } + return f; +} + +static int write_jitdump(FILE *f, void *addr, const void *dat, size_t sz, uint64_t *idx) +{ + const char *sym = "jit_workload"; + size_t sym_len = strlen(sym) + 1; + struct jr_code_load rec = { + .p.id = JIT_CODE_LOAD, + .p.total_size = sizeof(rec) + sym_len + sz, + .p.timestamp = get_timestamp(), + .pid = getpid(), + .tid = gettid(), + .vma = (unsigned long)addr, + .code_addr = (unsigned long)addr, + .code_size = sz, + .code_index = ++*idx, + }; + + if (fwrite(&rec, sizeof(rec), 1, f) != 1 || + fwrite(sym, sym_len, 1, f) != 1 || + fwrite(dat, sz, 1, f) != 1) + return -1; + return 0; +} + +static void close_jitdump(FILE *f) +{ + fclose(f); +} + +static int jitdump(int argc __maybe_unused, const char **argv __maybe_unused) +{ +#if defined(__x86_64__) || defined(__i386__) + /* Code to execute: mov CHK_BYTE, %eax ; ret */ + uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 }; +#elif defined(__aarch64__) + /* Code to execute: mov w0, #CHK_BYTE ; ret */ + uint8_t dat[] = { + (CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52, + 0xc0, 0x03, 0x5f, 0xd6 + }; +#elif defined(__riscv) + /* Code to execute: li a0, CHK_BYTE ; ret */ + uint8_t dat[] = { + 0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff, + 0x67, 0x80, 0x00, 0x00 + }; +#elif defined(__powerpc__) + /* Code to execute: li r3, CHK_BYTE ; blr */ + uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 }; +#elif defined(__s390x__) + /* Code to execute: lhi %r2, CHK_BYTE ; br %r14 */ + uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe }; +#elif defined(__arm__) + /* Code to execute: mov r0, #CHK_BYTE ; bx lr */ + uint8_t dat[] = { + CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3, + 0x1e, 0xff, 0x2f, 0xe1 + }; +#elif defined(__mips__) + /* Code to execute: addiu $v0, $zero, CHK_BYTE ; jr $ra ; nop */ + uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 }; +#elif defined(__loongarch__) + /* Code to execute: addi.w $a0, $zero, CHK_BYTE ; jirl $zero, $ra, 0 */ + uint32_t dat[] = { 0x02800004 | ((CHK_BYTE & 0xfff) << 10), 0x4c000020 }; +#else + uint32_t dat[0]; +#endif + void *addr; + FILE *f; + uint64_t idx = 0; + int ret = 1; + + /* Reachable fallback check for unsupported architectures right at start. */ + if (sizeof(dat) == 0) { + pr_err("JITDUMP workload not supported on this architecture\n"); + return 1; + } + + /* Get a memory page to store executable code. */ + addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (addr == MAP_FAILED) { + pr_err("Failed to map 1 -rwx page\n"); + return 1; + } + + f = open_jitdump(); + if (!f) { + pr_err("Failed to open JITDUMP\n"); + munmap(addr, getpagesize()); + return 1; + } + /* Copy executable code to executable memory page. */ + memcpy(addr, dat, sizeof(dat)); + /* Synchronize the Instruction and Data caches. */ + __builtin___clear_cache(addr, (char *)addr + sizeof(dat)); + + /* Record it in the jitdump file */ + if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) { + int (*fn)(void) = addr; + + /* Call the function. */ + ret = fn() - CHK_BYTE; + } + close_jitdump(f); + munmap(addr, getpagesize()); + return ret; +} + +DEFINE_WORKLOAD(jitdump); diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index ab2842def83d..f57bfebb20ff 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h @@ -11,9 +11,8 @@ #ifndef JITDUMP_H #define JITDUMP_H -#include -#include #include +#include /* JiTD */ #define JITHEADER_MAGIC 0x4A695444 From e0779713a1e2f891aeec53e629dbbd33f423c629 Mon Sep 17 00:00:00 2001 From: Yadu M G Date: Thu, 4 Jun 2026 17:54:18 +0530 Subject: [PATCH 2816/5214] PCI: qcom: Initialize DWC MSI lock for firmware-managed ECAM hosts A lockdep warning is observed during boot on a Qcom firmware-managed platform: INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator. ... Call trace: register_lock_class+0x128/0x4d8 __lock_acquire+0x110/0x1db0 lock_acquire+0x278/0x3d8 _raw_spin_lock_irq+0x6c/0xc0 dw_pcie_irq_domain_alloc+0x48/0x190 irq_domain_alloc_irqs_parent+0x2c/0x48 msi_domain_alloc+0x90/0x160 ... dw_pcie_irq_domain_alloc() takes pp->lock while allocating MSI interrupts. pp->lock is normally initialized by dw_pcie_host_init(), but Qcom firmware-managed hosts use the ECAM init path instead: pci_host_common_ecam_create() pci_ecam_create() qcom_pcie_ecam_host_init() dw_pcie_msi_host_init() dw_pcie_allocate_domains() That path constructs a fresh struct dw_pcie_rp and calls dw_pcie_msi_host_init() directly, without going through dw_pcie_host_init(). As a result, pp->lock was not initialized, which triggers the warning. Initialize pp->lock in qcom_pcie_ecam_host_init() before registering the MSI domains so the firmware-managed ECAM path matches the normal DWC host initialization sequence. Fixes: 7d944c0f1469 ("PCI: qcom: Add support for Qualcomm SA8255p based PCIe Root Complex") Signed-off-by: Yadu M G [mani: added fixes tag and CCed stable] Signed-off-by: Manivannan Sadhasivam Cc: stable@kernel.org Link: https://patch.msgid.link/20260604122418.727274-1-yadu.mg@oss.qualcomm.com --- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 8cf0a27d166b..4f76d42457c2 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1782,6 +1782,12 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) pci->dbi_base = cfg->win; pp->num_vectors = MSI_DEF_NUM_VECTORS; + /* + * dw_pcie_msi_host_init() is called directly here, bypassing + * dw_pcie_host_init() where pp->lock is normally initialized. + */ + raw_spin_lock_init(&pp->lock); + ret = dw_pcie_msi_host_init(pp); if (ret) return ret; From 731712403ddb39d1a76a11abf339a0615bc85de7 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:53 +0300 Subject: [PATCH 2817/5214] dmaengine: sh: rz-dmac: Move interrupt request after everything is set up Once the interrupt is requested, the interrupt handler may run immediately. Since the IRQ handler can access channel->ch_base, which is initialized only after requesting the IRQ, this may lead to invalid memory access. Likewise, the IRQ thread may access uninitialized data (the ld_free, ld_queue, and ld_active lists), which may also lead to issues. Request the interrupts only after everything is set up. To keep the error path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent(). Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 88 +++++++++++++++------------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 625ff29024de..9f206a33dcc6 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -981,25 +981,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->index = index; channel->mid_rid = -EINVAL; - /* Request the channel interrupt. */ - scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); - irq = platform_get_irq_byname(pdev, pdev_irqname); - if (irq < 0) - return irq; - - irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", - dev_name(dmac->dev), index); - if (!irqname) - return -ENOMEM; - - ret = devm_request_threaded_irq(dmac->dev, irq, rz_dmac_irq_handler, - rz_dmac_irq_handler_thread, 0, - irqname, channel); - if (ret) { - dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret); - return ret; - } - /* Set io base address for each channel */ if (index < 8) { channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET + @@ -1012,9 +993,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, } /* Allocate descriptors */ - lmdesc = dma_alloc_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - &channel->lmdesc.base_dma, GFP_KERNEL); + lmdesc = dmam_alloc_coherent(&pdev->dev, + sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, + &channel->lmdesc.base_dma, GFP_KERNEL); if (!lmdesc) { dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n"); return -ENOMEM; @@ -1030,7 +1011,24 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, INIT_LIST_HEAD(&channel->ld_free); INIT_LIST_HEAD(&channel->ld_active); - return 0; + /* Request the channel interrupt. */ + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); + irq = platform_get_irq_byname(pdev, pdev_irqname); + if (irq < 0) + return irq; + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", + dev_name(dmac->dev), index); + if (!irqname) + return -ENOMEM; + + ret = devm_request_threaded_irq(dmac->dev, irq, rz_dmac_irq_handler, + rz_dmac_irq_handler_thread, 0, + irqname, channel); + if (ret) + dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret); + + return ret; } static void rz_dmac_put_device(void *_dev) @@ -1099,7 +1097,6 @@ static int rz_dmac_probe(struct platform_device *pdev) const char *irqname = "error"; struct dma_device *engine; struct rz_dmac *dmac; - int channel_num; int ret; int irq; u8 i; @@ -1132,18 +1129,6 @@ static int rz_dmac_probe(struct platform_device *pdev) return PTR_ERR(dmac->ext_base); } - /* Register interrupt handler for error */ - irq = platform_get_irq_byname_optional(pdev, irqname); - if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, - irqname, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", - irq, ret); - return ret; - } - } - /* Initialize the channels. */ INIT_LIST_HEAD(&dmac->engine.channels); @@ -1169,6 +1154,18 @@ static int rz_dmac_probe(struct platform_device *pdev) goto err; } + /* Register interrupt handler for error */ + irq = platform_get_irq_byname_optional(pdev, irqname); + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, + irqname, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", + irq, ret); + goto err; + } + } + /* Register the DMAC as a DMA provider for DT. */ ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate, NULL); @@ -1210,16 +1207,6 @@ static int rz_dmac_probe(struct platform_device *pdev) dma_register_err: of_dma_controller_free(pdev->dev.of_node); err: - channel_num = i ? i - 1 : 0; - for (i = 0; i < channel_num; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } - reset_control_assert(dmac->rstc); err_pm_runtime_put: pm_runtime_put(&pdev->dev); @@ -1232,18 +1219,9 @@ static int rz_dmac_probe(struct platform_device *pdev) static void rz_dmac_remove(struct platform_device *pdev) { struct rz_dmac *dmac = platform_get_drvdata(pdev); - unsigned int i; dma_async_device_unregister(&dmac->engine); of_dma_controller_free(pdev->dev.of_node); - for (i = 0; i < dmac->n_channels; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } reset_control_assert(dmac->rstc); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); From 5fbf3a2a3b96ef5810e6e0fbc601f82067629bc5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:54 +0300 Subject: [PATCH 2818/5214] dmaengine: sh: rz-dmac: Fix incorrect NULL check for list_first_entry() list_first_entry() does not return NULL when the list is empty, making the existing NULL check invalid. Use list_first_entry_or_null() instead. Fixes: 21323b118c16 ("dmaengine: sh: rz-dmac: Add device_tx_status() callback") Cc: stable@vger.kernel.org Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-3-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 9f206a33dcc6..6d80cb668957 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -723,8 +723,8 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, u32 crla, crtb, i; /* Get current processing virtual descriptor */ - current_desc = list_first_entry(&channel->ld_active, - struct rz_dmac_desc, node); + current_desc = list_first_entry_or_null(&channel->ld_active, + struct rz_dmac_desc, node); if (!current_desc) return 0; From 89975baaa9ea2490b75d69842561a32ca888b7e5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:55 +0300 Subject: [PATCH 2819/5214] dmaengine: sh: rz-dmac: Use list_first_entry_or_null() Use list_first_entry_or_null() instead of open-coding it with a list_empty() check and list_first_entry(). This simplifies the code. Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-4-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 6d80cb668957..1717b407ab9e 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -503,11 +503,10 @@ rz_dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, __func__, channel->index, &src, &dest, len); scoped_guard(spinlock_irqsave, &channel->vc.lock) { - if (list_empty(&channel->ld_free)) + desc = list_first_entry_or_null(&channel->ld_free, struct rz_dmac_desc, node); + if (!desc) return NULL; - desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); - desc->type = RZ_DMAC_DESC_MEMCPY; desc->src = src; desc->dest = dest; @@ -533,11 +532,10 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, int i = 0; scoped_guard(spinlock_irqsave, &channel->vc.lock) { - if (list_empty(&channel->ld_free)) + desc = list_first_entry_or_null(&channel->ld_free, struct rz_dmac_desc, node); + if (!desc) return NULL; - desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); - for_each_sg(sgl, sg, sg_len, i) dma_length += sg_dma_len(sg); From 38d4d021228386b8e3fbef2bca5f1e91eacd4fe6 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:56 +0300 Subject: [PATCH 2820/5214] dmaengine: sh: rz-dmac: Use rz_dmac_disable_hw() Use rz_dmac_disable_hw() instead of open coding it. This unifies the code and prepares it for the addition of suspend to RAM and cyclic DMA. The rz_dmac_disable_hw() from rz_dmac_chan_probe() was moved after vchan_init() as it initializes the channel->vc.chan.device used in rz_dmac_disable_hw(). Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-5-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 1717b407ab9e..40ddf534c094 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -873,7 +873,7 @@ static void rz_dmac_irq_handle_channel(struct rz_dmac_chan *channel) channel->index, chstat); scoped_guard(spinlock_irqsave, &channel->vc.lock) - rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1); + rz_dmac_disable_hw(channel); return; } @@ -1000,15 +1000,15 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, } rz_lmdesc_setup(channel, lmdesc); - /* Initialize register for each channel */ - rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1); - channel->vc.desc_free = rz_dmac_virt_desc_free; vchan_init(&channel->vc, &dmac->engine); INIT_LIST_HEAD(&channel->ld_queue); INIT_LIST_HEAD(&channel->ld_free); INIT_LIST_HEAD(&channel->ld_active); + /* Initialize register for each channel */ + rz_dmac_disable_hw(channel); + /* Request the channel interrupt. */ scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); irq = platform_get_irq_byname(pdev, pdev_irqname); From 32a69f1487819766d2084ed32b1350b18f971c10 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:57 +0300 Subject: [PATCH 2821/5214] dmaengine: sh: rz-dmac: Add helper to compute the lmdesc address Add a the rz_dmac_lmdesc_addr() helper function to compute the lmdesc address, to make the code easier to understand. The helper will be used in subsequent patches. Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-6-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 40ddf534c094..c48858b68dee 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -259,6 +259,12 @@ static void rz_lmdesc_setup(struct rz_dmac_chan *channel, * Descriptors preparation */ +static u32 rz_dmac_lmdesc_addr(struct rz_dmac_chan *channel, struct rz_lmdesc *lmdesc) +{ + return channel->lmdesc.base_dma + + (sizeof(struct rz_lmdesc) * (lmdesc - channel->lmdesc.base)); +} + static void rz_dmac_lmdesc_recycle(struct rz_dmac_chan *channel) { struct rz_lmdesc *lmdesc = channel->lmdesc.head; @@ -284,9 +290,7 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) rz_dmac_lmdesc_recycle(channel); - nxla = channel->lmdesc.base_dma + - (sizeof(struct rz_lmdesc) * (channel->lmdesc.head - - channel->lmdesc.base)); + nxla = rz_dmac_lmdesc_addr(channel, channel->lmdesc.head); chstat = rz_dmac_ch_readl(channel, CHSTAT, 1); if (!(chstat & CHSTAT_EN)) { From e21aa306e82067457f2297ae56af4c91db86c59a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:58 +0300 Subject: [PATCH 2822/5214] dmaengine: sh: rz-dmac: Save the start LM descriptor Save the start LM descriptor to avoid starting from the beginning of the channel's LM descriptor list in rz_dmac_calculate_residue_bytes_in_vd(). This avoids unnecessary iterations. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-7-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index c48858b68dee..d3926ecd63ac 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -58,6 +58,7 @@ struct rz_dmac_desc { /* For slave sg */ struct scatterlist *sg; unsigned int sgcount; + struct rz_lmdesc *start_lmdesc; }; #define to_rz_dmac_desc(d) container_of(d, struct rz_dmac_desc, vd) @@ -343,6 +344,8 @@ static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) struct rz_dmac_desc *d = channel->desc; u32 chcfg = CHCFG_MEM_COPY; + d->start_lmdesc = lmdesc; + /* prepare descriptor */ lmdesc->sa = d->src; lmdesc->da = d->dest; @@ -377,6 +380,7 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) } lmdesc = channel->lmdesc.tail; + d->start_lmdesc = lmdesc; for (i = 0, sg = sgl; i < sg_len; i++, sg = sg_next(sg)) { if (d->direction == DMA_DEV_TO_MEM) { @@ -693,9 +697,10 @@ rz_dmac_get_next_lmdesc(struct rz_lmdesc *base, struct rz_lmdesc *lmdesc) return next; } -static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, u32 crla) +static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, + struct rz_dmac_desc *desc, u32 crla) { - struct rz_lmdesc *lmdesc = channel->lmdesc.head; + struct rz_lmdesc *lmdesc = desc->start_lmdesc; struct dma_chan *chan = &channel->vc.chan; struct rz_dmac *dmac = to_rz_dmac(chan->device); u32 residue = 0, i = 0; @@ -794,7 +799,7 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, * Calculate number of bytes transferred in processing virtual descriptor. * One virtual descriptor can have many lmdesc. */ - return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, crla); + return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, current_desc, crla); } static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, From 7a94c109a5def4f0f25705a82ed5870f794ff4ed Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:46:59 +0300 Subject: [PATCH 2823/5214] dmaengine: sh: rz-dmac: Add helper to check if the channel is enabled Add the rz_dmac_chan_is_enabled() helper to check if a channel is enabled. This helper will be reused in subsequent patches. Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-8-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index d3926ecd63ac..76bac11c217c 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -279,6 +279,13 @@ static void rz_dmac_lmdesc_recycle(struct rz_dmac_chan *channel) channel->lmdesc.head = lmdesc; } +static bool rz_dmac_chan_is_enabled(struct rz_dmac_chan *channel) +{ + u32 val = rz_dmac_ch_readl(channel, CHSTAT, 1); + + return !!(val & CHSTAT_EN); +} + static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; @@ -840,8 +847,7 @@ static int rz_dmac_device_pause(struct dma_chan *chan) guard(spinlock_irqsave)(&channel->vc.lock); - val = rz_dmac_ch_readl(channel, CHSTAT, 1); - if (!(val & CHSTAT_EN)) + if (!rz_dmac_chan_is_enabled(channel)) return 0; rz_dmac_ch_writel(channel, CHCTRL_SETSUS, CHCTRL, 1); From 1dddc864dfa844efaf36345eb58b121b2cdffa5f Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:00 +0300 Subject: [PATCH 2824/5214] dmaengine: sh: rz-dmac: Add helper to check if the channel is paused Add the rz_dmac_chan_is_paused() helper to check if the channel is paused. This helper will be reused in subsequent patches. Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-9-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 76bac11c217c..217657513fa7 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -286,6 +286,13 @@ static bool rz_dmac_chan_is_enabled(struct rz_dmac_chan *channel) return !!(val & CHSTAT_EN); } +static bool rz_dmac_chan_is_paused(struct rz_dmac_chan *channel) +{ + u32 val = rz_dmac_ch_readl(channel, CHSTAT, 1); + + return !!(val & CHSTAT_SUS); +} + static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; @@ -822,12 +829,9 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, return status; scoped_guard(spinlock_irqsave, &channel->vc.lock) { - u32 val; - residue = rz_dmac_chan_get_residue(channel, cookie); - val = rz_dmac_ch_readl(channel, CHSTAT, 1); - if (val & CHSTAT_SUS) + if (rz_dmac_chan_is_paused(channel)) status = DMA_PAUSED; } From daa6d4617bee722e83f7d8584416e83b709c958a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:01 +0300 Subject: [PATCH 2825/5214] dmaengine: sh: rz-dmac: Use virt-dma APIs for channel descriptor processing The driver used a mix of virt-dma APIs and driver specific logic to process descriptors. It maintained three internal queues: ld_free, ld_queue, and ld_active as follows: - ld_free: stores the descriptors pre-allocated at probe time - ld_queue: stores descriptors after they are taken from ld_free and prepared. At the same time, vchan_tx_prep() queues them to vc->desc_allocated. The vc->desc_allocated list is then checked in rz_dmac_issue_pending() and rz_dmac_irq_handler_thread() before starting a new transfer via rz_dmac_xfer_desc(). In turn, rz_dmac_xfer_desc() grabs the next descriptor from vc->desc_issued and submits it for transfer - ld_active: stores the descriptors currently being transferred The interrupt handler moved a completed descriptor to ld_free before invoking its completion callback. Once returned to ld_free, the descriptor can be reused to prepare a new transfer. In theory, this means the descriptor could be re-prepared before its completion callback is called. Commit fully back the driver by the virt-dma APIs. With this, only ld_free need to be kept to track how many free descriptors are available. This is now done as follows: - the prepare stage removes the first descriptor from the ld_free and prepares it - the completion calls for it vc->desc_free() (rz_dmac_virt_desc_free()) which re-adds the descriptor at the end of ld_free With this, the critical areas in prepare callbacks were minimized to only getting the descriptor from the ld_free list. Introduce struct rz_dmac_chan::desc to keep track of the currently transferred descriptor. It is cleared in rz_dmac_terminate_all(), referenced from rz_dmac_issue_pending() to determine whether a new transfer can be started, and from rz_dmac_irq_handler_thread() once a descriptor has completed. Finally, the rz_dmac_device_synchronize() was updated with vchan_synchronize() call to ensure the terminated descriptor is freed and the tasklet is killed. With this, residue computation is also simplified, as it can now be handled entirely through the virt-dma APIs. The spin_lock/unlock operations from rz_dmac_irq_handler_thread() were replaced by guard as the final code after rework is simpler this way. As subsequent commits will set the Link End bit on the last descriptor of a transfer, rz_dmac_enable_hw() is also adjusted as part of the full conversion to virt-dma APIs. It no longer checks the channel enable status itself; instead, its callers verify whether the channel is enabled and whether the previous transfer has completed before starting a new one. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-10-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 237 +++++++++++++++------------------------ 1 file changed, 88 insertions(+), 149 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 217657513fa7..1f884ec101f8 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -79,8 +79,6 @@ struct rz_dmac_chan { int mid_rid; struct list_head ld_free; - struct list_head ld_queue; - struct list_head ld_active; struct { struct rz_lmdesc *base; @@ -299,7 +297,6 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) struct rz_dmac *dmac = to_rz_dmac(chan->device); u32 nxla; u32 chctrl; - u32 chstat; dev_dbg(dmac->dev, "%s channel %d\n", __func__, channel->index); @@ -307,14 +304,11 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) nxla = rz_dmac_lmdesc_addr(channel, channel->lmdesc.head); - chstat = rz_dmac_ch_readl(channel, CHSTAT, 1); - if (!(chstat & CHSTAT_EN)) { - chctrl = (channel->chctrl | CHCTRL_SETEN); - rz_dmac_ch_writel(channel, nxla, NXLA, 1); - rz_dmac_ch_writel(channel, channel->chcfg, CHCFG, 1); - rz_dmac_ch_writel(channel, CHCTRL_SWRST, CHCTRL, 1); - rz_dmac_ch_writel(channel, chctrl, CHCTRL, 1); - } + chctrl = (channel->chctrl | CHCTRL_SETEN); + rz_dmac_ch_writel(channel, nxla, NXLA, 1); + rz_dmac_ch_writel(channel, channel->chcfg, CHCFG, 1); + rz_dmac_ch_writel(channel, CHCTRL_SWRST, CHCTRL, 1); + rz_dmac_ch_writel(channel, chctrl, CHCTRL, 1); } static void rz_dmac_disable_hw(struct rz_dmac_chan *channel) @@ -426,18 +420,20 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) channel->chctrl = CHCTRL_SETEN; } -static int rz_dmac_xfer_desc(struct rz_dmac_chan *chan) +static void rz_dmac_xfer_desc(struct rz_dmac_chan *chan) { - struct rz_dmac_desc *d = chan->desc; struct virt_dma_desc *vd; vd = vchan_next_desc(&chan->vc); - if (!vd) - return 0; + if (!vd) { + chan->desc = NULL; + return; + } list_del(&vd->node); + chan->desc = to_rz_dmac_desc(vd); - switch (d->type) { + switch (chan->desc->type) { case RZ_DMAC_DESC_MEMCPY: rz_dmac_prepare_desc_for_memcpy(chan); break; @@ -445,14 +441,9 @@ static int rz_dmac_xfer_desc(struct rz_dmac_chan *chan) case RZ_DMAC_DESC_SLAVE_SG: rz_dmac_prepare_descs_for_slave_sg(chan); break; - - default: - return -EINVAL; } rz_dmac_enable_hw(chan); - - return 0; } /* @@ -494,8 +485,6 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) rz_lmdesc_setup(channel, channel->lmdesc.base); rz_dmac_disable_hw(channel); - list_splice_tail_init(&channel->ld_active, &channel->ld_free); - list_splice_tail_init(&channel->ld_queue, &channel->ld_free); if (channel->mid_rid >= 0) { clear_bit(channel->mid_rid, dmac->modules); @@ -504,13 +493,19 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&channel->vc.lock, flags); + vchan_free_chan_resources(&channel->vc); + + spin_lock_irqsave(&channel->vc.lock, flags); + list_for_each_entry_safe(desc, _desc, &channel->ld_free, node) { + list_del(&desc->node); kfree(desc); channel->descs_allocated--; } INIT_LIST_HEAD(&channel->ld_free); - vchan_free_chan_resources(&channel->vc); + + spin_unlock_irqrestore(&channel->vc.lock, flags); } static struct dma_async_tx_descriptor * @@ -529,15 +524,15 @@ rz_dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, if (!desc) return NULL; - desc->type = RZ_DMAC_DESC_MEMCPY; - desc->src = src; - desc->dest = dest; - desc->len = len; - desc->direction = DMA_MEM_TO_MEM; - - list_move_tail(channel->ld_free.next, &channel->ld_queue); + list_del(&desc->node); } + desc->type = RZ_DMAC_DESC_MEMCPY; + desc->src = src; + desc->dest = dest; + desc->len = len; + desc->direction = DMA_MEM_TO_MEM; + return vchan_tx_prep(&channel->vc, &desc->vd, flags); } @@ -558,23 +553,23 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (!desc) return NULL; - for_each_sg(sgl, sg, sg_len, i) - dma_length += sg_dma_len(sg); - - desc->type = RZ_DMAC_DESC_SLAVE_SG; - desc->sg = sgl; - desc->sgcount = sg_len; - desc->len = dma_length; - desc->direction = direction; - - if (direction == DMA_DEV_TO_MEM) - desc->src = channel->src_per_address; - else - desc->dest = channel->dst_per_address; - - list_move_tail(channel->ld_free.next, &channel->ld_queue); + list_del(&desc->node); } + for_each_sg(sgl, sg, sg_len, i) + dma_length += sg_dma_len(sg); + + desc->type = RZ_DMAC_DESC_SLAVE_SG; + desc->sg = sgl; + desc->sgcount = sg_len; + desc->len = dma_length; + desc->direction = direction; + + if (direction == DMA_DEV_TO_MEM) + desc->src = channel->src_per_address; + else + desc->dest = channel->dst_per_address; + return vchan_tx_prep(&channel->vc, &desc->vd, flags); } @@ -588,8 +583,11 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) rz_dmac_disable_hw(channel); rz_lmdesc_setup(channel, channel->lmdesc.base); - list_splice_tail_init(&channel->ld_active, &channel->ld_free); - list_splice_tail_init(&channel->ld_queue, &channel->ld_free); + if (channel->desc) { + vchan_terminate_vdesc(&channel->desc->vd); + channel->desc = NULL; + } + vchan_get_all_descriptors(&channel->vc, &head); spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_dma_desc_free_list(&channel->vc, &head); @@ -600,25 +598,16 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) static void rz_dmac_issue_pending(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); - struct rz_dmac *dmac = to_rz_dmac(chan->device); - struct rz_dmac_desc *desc; unsigned long flags; spin_lock_irqsave(&channel->vc.lock, flags); - if (!list_empty(&channel->ld_queue)) { - desc = list_first_entry(&channel->ld_queue, - struct rz_dmac_desc, node); - channel->desc = desc; - if (vchan_issue_pending(&channel->vc)) { - if (rz_dmac_xfer_desc(channel) < 0) - dev_warn(dmac->dev, "ch: %d couldn't issue DMA xfer\n", - channel->index); - else - list_move_tail(channel->ld_queue.next, - &channel->ld_active); - } - } + /* + * Issue the descriptor. If another transfer is already in progress, the + * issued descriptor will be handled after the current transfer finishes. + */ + if (vchan_issue_pending(&channel->vc) && !channel->desc) + rz_dmac_xfer_desc(channel); spin_unlock_irqrestore(&channel->vc.lock, flags); } @@ -676,13 +665,13 @@ static int rz_dmac_config(struct dma_chan *chan, static void rz_dmac_virt_desc_free(struct virt_dma_desc *vd) { - /* - * Place holder - * Descriptor allocation is done during alloc_chan_resources and - * get freed during free_chan_resources. - * list is used to manage the descriptors and avoid any memory - * allocation/free during DMA read/write. - */ + struct rz_dmac_chan *channel = to_rz_dmac_chan(vd->tx.chan); + struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan); + struct rz_dmac_desc *desc = to_rz_dmac_desc(vd); + + guard(spinlock_irqsave)(&vc->lock); + + list_add_tail(&desc->node, &channel->ld_free); } static void rz_dmac_device_synchronize(struct dma_chan *chan) @@ -692,6 +681,8 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan) u32 chstat; int ret; + vchan_synchronize(&channel->vc); + ret = read_poll_timeout(rz_dmac_ch_readl, chstat, !(chstat & CHSTAT_EN), 100, 100000, false, channel, CHSTAT, 1); if (ret < 0) @@ -739,58 +730,22 @@ static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, dma_cookie_t cookie) { - struct rz_dmac_desc *current_desc, *desc; - enum dma_status status; + struct rz_dmac_desc *desc = NULL; + struct virt_dma_desc *vd; u32 crla, crtb, i; - /* Get current processing virtual descriptor */ - current_desc = list_first_entry_or_null(&channel->ld_active, - struct rz_dmac_desc, node); - if (!current_desc) - return 0; + vd = vchan_find_desc(&channel->vc, cookie); + if (vd) { + /* Descriptor has been issued but not yet processed. */ + desc = to_rz_dmac_desc(vd); + return desc->len; + } else if (channel->desc && channel->desc->vd.tx.cookie == cookie) { + /* Descriptor is currently processed. */ + desc = channel->desc; + } - /* - * If the cookie corresponds to a descriptor that has been completed - * there is no residue. The same check has already been performed by the - * caller but without holding the channel lock, so the descriptor could - * now be complete. - */ - status = dma_cookie_status(&channel->vc.chan, cookie, NULL); - if (status == DMA_COMPLETE) - return 0; - - /* - * If the cookie doesn't correspond to the currently processing virtual - * descriptor then the descriptor hasn't been processed yet, and the - * residue is equal to the full descriptor size. Also, a client driver - * is possible to call this function before rz_dmac_irq_handler_thread() - * runs. In this case, the running descriptor will be the next - * descriptor, and will appear in the done list. So, if the argument - * cookie matches the done list's cookie, we can assume the residue is - * zero. - */ - if (cookie != current_desc->vd.tx.cookie) { - list_for_each_entry(desc, &channel->ld_free, node) { - if (cookie == desc->vd.tx.cookie) - return 0; - } - - list_for_each_entry(desc, &channel->ld_queue, node) { - if (cookie == desc->vd.tx.cookie) - return desc->len; - } - - list_for_each_entry(desc, &channel->ld_active, node) { - if (cookie == desc->vd.tx.cookie) - return desc->len; - } - - /* - * No descriptor found for the cookie, there's thus no residue. - * This shouldn't happen if the calling driver passes a correct - * cookie value. - */ - WARN(1, "No descriptor for cookie!"); + if (!desc) { + /* Descriptor was not found. May be already completed by now. */ return 0; } @@ -813,7 +768,7 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, * Calculate number of bytes transferred in processing virtual descriptor. * One virtual descriptor can have many lmdesc. */ - return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, current_desc, crla); + return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, desc, crla); } static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, @@ -824,21 +779,17 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, enum dma_status status; u32 residue; - status = dma_cookie_status(chan, cookie, txstate); - if (status == DMA_COMPLETE || !txstate) - return status; - scoped_guard(spinlock_irqsave, &channel->vc.lock) { + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE || !txstate) + return status; + residue = rz_dmac_chan_get_residue(channel, cookie); - if (rz_dmac_chan_is_paused(channel)) + if (status == DMA_IN_PROGRESS && rz_dmac_chan_is_paused(channel)) status = DMA_PAUSED; } - /* if there's no residue and no paused, the cookie is complete */ - if (!residue && status != DMA_PAUSED) - return DMA_COMPLETE; - dma_set_residue(txstate, residue); return status; @@ -918,28 +869,18 @@ static irqreturn_t rz_dmac_irq_handler(int irq, void *dev_id) static irqreturn_t rz_dmac_irq_handler_thread(int irq, void *dev_id) { struct rz_dmac_chan *channel = dev_id; - struct rz_dmac_desc *desc = NULL; - unsigned long flags; + struct rz_dmac_desc *desc; - spin_lock_irqsave(&channel->vc.lock, flags); + guard(spinlock_irqsave)(&channel->vc.lock); - if (list_empty(&channel->ld_active)) { - /* Someone might have called terminate all */ - goto out; - } + desc = channel->desc; + if (!desc) + return IRQ_HANDLED; - desc = list_first_entry(&channel->ld_active, struct rz_dmac_desc, node); vchan_cookie_complete(&desc->vd); - list_move_tail(channel->ld_active.next, &channel->ld_free); - if (!list_empty(&channel->ld_queue)) { - desc = list_first_entry(&channel->ld_queue, struct rz_dmac_desc, - node); - channel->desc = desc; - if (rz_dmac_xfer_desc(channel) == 0) - list_move_tail(channel->ld_queue.next, &channel->ld_active); - } -out: - spin_unlock_irqrestore(&channel->vc.lock, flags); + channel->desc = NULL; + + rz_dmac_xfer_desc(channel); return IRQ_HANDLED; } @@ -1021,9 +962,7 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->vc.desc_free = rz_dmac_virt_desc_free; vchan_init(&channel->vc, &dmac->engine); - INIT_LIST_HEAD(&channel->ld_queue); INIT_LIST_HEAD(&channel->ld_free); - INIT_LIST_HEAD(&channel->ld_active); /* Initialize register for each channel */ rz_dmac_disable_hw(channel); From dc86e47ca9b1021e258c366a5a9aa15d71c814a5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:02 +0300 Subject: [PATCH 2826/5214] dmaengine: sh: rz-dmac: Refactor pause/resume code Subsequent patches will add suspend/resume and cyclic DMA support to the rz-dmac driver. This support needs to work on SoCs where power to most components (including DMA) is turned off during system suspend. For this, some channels (for example cyclic ones) may need to be paused and resumed manually by the DMA driver during system suspend/resume. Refactor the pause/resume support so the same code can be reused in the system suspend/resume path. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-11-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 75 +++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 1f884ec101f8..557364443a5f 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,14 @@ struct rz_dmac_desc { #define to_rz_dmac_desc(d) container_of(d, struct rz_dmac_desc, vd) +/** + * enum rz_dmac_chan_status: RZ DMAC channel status + * @RZ_DMAC_CHAN_STATUS_PAUSED: Channel is paused though DMA engine callbacks + */ +enum rz_dmac_chan_status { + RZ_DMAC_CHAN_STATUS_PAUSED, +}; + struct rz_dmac_chan { struct virt_dma_chan vc; void __iomem *ch_base; @@ -74,6 +83,8 @@ struct rz_dmac_chan { dma_addr_t src_per_address; dma_addr_t dst_per_address; + unsigned long status; + u32 chcfg; u32 chctrl; int mid_rid; @@ -491,6 +502,8 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) channel->mid_rid = -EINVAL; } + channel->status = 0; + spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_free_chan_resources(&channel->vc); @@ -589,6 +602,9 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) } vchan_get_all_descriptors(&channel->vc, &head); + + channel->status = 0; + spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_dma_desc_free_list(&channel->vc, &head); @@ -795,35 +811,70 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, return status; } -static int rz_dmac_device_pause(struct dma_chan *chan) +static int rz_dmac_device_pause_set(struct rz_dmac_chan *channel, + unsigned long set_bitmask) { - struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + int ret = 0; u32 val; - guard(spinlock_irqsave)(&channel->vc.lock); + lockdep_assert_held(&channel->vc.lock); if (!rz_dmac_chan_is_enabled(channel)) return 0; + if (rz_dmac_chan_is_paused(channel)) + goto set_bit; + rz_dmac_ch_writel(channel, CHCTRL_SETSUS, CHCTRL, 1); - return read_poll_timeout_atomic(rz_dmac_ch_readl, val, - (val & CHSTAT_SUS), 1, 1024, - false, channel, CHSTAT, 1); + ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val, + (val & CHSTAT_SUS), 1, 1024, false, + channel, CHSTAT, 1); + +set_bit: + channel->status |= set_bitmask; + + return ret; +} + +static int rz_dmac_device_pause(struct dma_chan *chan) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + + guard(spinlock_irqsave)(&channel->vc.lock); + + return rz_dmac_device_pause_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); +} + +static int rz_dmac_device_resume_set(struct rz_dmac_chan *channel, + unsigned long clear_bitmask) +{ + int ret = 0; + u32 val; + + lockdep_assert_held(&channel->vc.lock); + + /* Do not check CHSTAT_SUS but rely on HW capabilities. */ + + rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1); + ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val, + !(val & CHSTAT_SUS), 1, 1024, false, + channel, CHSTAT, 1); + + channel->status &= ~clear_bitmask; + + return ret; } static int rz_dmac_device_resume(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); - u32 val; guard(spinlock_irqsave)(&channel->vc.lock); - /* Do not check CHSTAT_SUS but rely on HW capabilities. */ + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED))) + return 0; - rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1); - return read_poll_timeout_atomic(rz_dmac_ch_readl, val, - !(val & CHSTAT_SUS), 1, 1024, - false, channel, CHSTAT, 1); + return rz_dmac_device_resume_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); } /* From e8baee1d1cddc8e2be7bc362d6dc3fcb2021e873 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:03 +0300 Subject: [PATCH 2827/5214] dmaengine: sh: rz-dmac: Drop the update of channel->chctrl with CHCTRL_SETEN The CHCTRL_SETEN bit is explicitly set in rz_dmac_enable_hw(). Updating struct rz_dmac_chan::chctrl with this bit in rz_dmac_prepare_desc_for_memcpy() and rz_dmac_prepare_descs_for_slave_sg() is unnecessary in the current code base. Moreover, it conflicts with the configuration sequence that will be used for cyclic DMA channels during suspend to RAM. Cyclic DMA support will be introduced in subsequent commits. This is a preparatory commit for cyclic DMA suspend to RAM support. Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-12-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 557364443a5f..c9c00650ddd5 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -377,7 +377,7 @@ static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) rz_dmac_set_dma_req_no(dmac, channel->index, dmac->info->default_dma_req_no); channel->chcfg = chcfg; - channel->chctrl = CHCTRL_STG | CHCTRL_SETEN; + channel->chctrl = CHCTRL_STG; } static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) @@ -428,7 +428,7 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); - channel->chctrl = CHCTRL_SETEN; + channel->chctrl = 0; } static void rz_dmac_xfer_desc(struct rz_dmac_chan *chan) From 172bfb57481c65fcc94ebcae3a730f6df2f953d4 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:04 +0300 Subject: [PATCH 2828/5214] dmaengine: sh: rz-dmac: Add cyclic DMA support Add cyclic DMA support to the RZ DMAC driver. A per-channel status bit is introduced to mark cyclic channels and is set during the DMA prepare callback. The IRQ handler checks this status bit and calls vchan_cyclic_callback() accordingly. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-13-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 136 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 6 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index c9c00650ddd5..8fd8a4bd9cc9 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -35,6 +35,7 @@ enum rz_dmac_prep_type { RZ_DMAC_DESC_MEMCPY, RZ_DMAC_DESC_SLAVE_SG, + RZ_DMAC_DESC_CYCLIC, }; struct rz_lmdesc { @@ -67,9 +68,11 @@ struct rz_dmac_desc { /** * enum rz_dmac_chan_status: RZ DMAC channel status * @RZ_DMAC_CHAN_STATUS_PAUSED: Channel is paused though DMA engine callbacks + * @RZ_DMAC_CHAN_STATUS_CYCLIC: Channel is cyclic */ enum rz_dmac_chan_status { RZ_DMAC_CHAN_STATUS_PAUSED, + RZ_DMAC_CHAN_STATUS_CYCLIC, }; struct rz_dmac_chan { @@ -191,6 +194,7 @@ struct rz_dmac { /* LINK MODE DESCRIPTOR */ #define HEADER_LV BIT(0) +#define HEADER_WBD BIT(2) #define RZ_DMAC_MAX_CHAN_DESCRIPTORS 16 #define RZ_DMAC_MAX_CHANNELS 16 @@ -431,6 +435,57 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) channel->chctrl = 0; } +static void rz_dmac_prepare_descs_for_cyclic(struct rz_dmac_chan *channel) +{ + struct dma_chan *chan = &channel->vc.chan; + struct rz_dmac *dmac = to_rz_dmac(chan->device); + struct rz_dmac_desc *d = channel->desc; + size_t period_len = d->sgcount; + struct rz_lmdesc *lmdesc; + size_t buf_len = d->len; + size_t periods = buf_len / period_len; + + lockdep_assert_held(&channel->vc.lock); + + channel->chcfg |= CHCFG_SEL(channel->index) | CHCFG_DMS; + + if (d->direction == DMA_DEV_TO_MEM) { + channel->chcfg |= CHCFG_SAD; + channel->chcfg &= ~CHCFG_REQD; + } else { + channel->chcfg |= CHCFG_DAD | CHCFG_REQD; + } + + lmdesc = channel->lmdesc.tail; + d->start_lmdesc = lmdesc; + + for (size_t i = 0; i < periods; i++) { + if (d->direction == DMA_DEV_TO_MEM) { + lmdesc->sa = d->src; + lmdesc->da = d->dest + (i * period_len); + } else { + lmdesc->sa = d->src + (i * period_len); + lmdesc->da = d->dest; + } + + lmdesc->tb = period_len; + lmdesc->chitvl = 0; + lmdesc->chext = 0; + lmdesc->chcfg = channel->chcfg; + lmdesc->header = HEADER_LV | HEADER_WBD; + + if (i == periods - 1) + lmdesc->nxla = rz_dmac_lmdesc_addr(channel, d->start_lmdesc); + + if (++lmdesc >= (channel->lmdesc.base + DMAC_NR_LMDESC)) + lmdesc = channel->lmdesc.base; + } + + channel->lmdesc.tail = lmdesc; + + rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); +} + static void rz_dmac_xfer_desc(struct rz_dmac_chan *chan) { struct virt_dma_desc *vd; @@ -452,6 +507,10 @@ static void rz_dmac_xfer_desc(struct rz_dmac_chan *chan) case RZ_DMAC_DESC_SLAVE_SG: rz_dmac_prepare_descs_for_slave_sg(chan); break; + + case RZ_DMAC_DESC_CYCLIC: + rz_dmac_prepare_descs_for_cyclic(chan); + break; } rz_dmac_enable_hw(chan); @@ -586,6 +645,55 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return vchan_tx_prep(&channel->vc, &desc->vd, flags); } +static struct dma_async_tx_descriptor * +rz_dmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, + size_t buf_len, size_t period_len, + enum dma_transfer_direction direction, + unsigned long flags) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac_desc *desc; + size_t periods; + + if (!is_slave_direction(direction)) + return NULL; + + if (!period_len || !buf_len) + return NULL; + + periods = buf_len / period_len; + if (!periods || periods > DMAC_NR_LMDESC) + return NULL; + + scoped_guard(spinlock_irqsave, &channel->vc.lock) { + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC)) + return NULL; + + desc = list_first_entry_or_null(&channel->ld_free, struct rz_dmac_desc, node); + if (!desc) + return NULL; + + list_del(&desc->node); + + channel->status |= BIT(RZ_DMAC_CHAN_STATUS_CYCLIC); + } + + desc->type = RZ_DMAC_DESC_CYCLIC; + desc->sgcount = period_len; + desc->len = buf_len; + desc->direction = direction; + + if (direction == DMA_DEV_TO_MEM) { + desc->src = channel->src_per_address; + desc->dest = buf_addr; + } else { + desc->src = buf_addr; + desc->dest = channel->dst_per_address; + } + + return vchan_tx_prep(&channel->vc, &desc->vd, flags); +} + static int rz_dmac_terminate_all(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); @@ -733,9 +841,18 @@ static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, } /* Calculate residue from next lmdesc to end of virtual desc */ - while (lmdesc->chcfg & CHCFG_DEM) { - residue += lmdesc->tb; - lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc); + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC)) { + u32 start_lmdesc_addr = rz_dmac_lmdesc_addr(channel, desc->start_lmdesc); + + while (lmdesc->nxla != start_lmdesc_addr) { + residue += lmdesc->tb; + lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc); + } + } else { + while (lmdesc->chcfg & CHCFG_DEM) { + residue += lmdesc->tb; + lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc); + } } dev_dbg(dmac->dev, "%s: VD residue is %u\n", __func__, residue); @@ -928,10 +1045,14 @@ static irqreturn_t rz_dmac_irq_handler_thread(int irq, void *dev_id) if (!desc) return IRQ_HANDLED; - vchan_cookie_complete(&desc->vd); - channel->desc = NULL; + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC)) { + vchan_cyclic_callback(&desc->vd); + } else { + vchan_cookie_complete(&desc->vd); + channel->desc = NULL; - rz_dmac_xfer_desc(channel); + rz_dmac_xfer_desc(channel); + } return IRQ_HANDLED; } @@ -1183,6 +1304,8 @@ static int rz_dmac_probe(struct platform_device *pdev) engine = &dmac->engine; dma_cap_set(DMA_SLAVE, engine->cap_mask); dma_cap_set(DMA_MEMCPY, engine->cap_mask); + dma_cap_set(DMA_CYCLIC, engine->cap_mask); + engine->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_0_7_COMMON_BASE + DCTRL); rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_8_15_COMMON_BASE + DCTRL); @@ -1194,6 +1317,7 @@ static int rz_dmac_probe(struct platform_device *pdev) engine->device_tx_status = rz_dmac_tx_status; engine->device_prep_slave_sg = rz_dmac_prep_slave_sg; engine->device_prep_dma_memcpy = rz_dmac_prep_dma_memcpy; + engine->device_prep_dma_cyclic = rz_dmac_prep_dma_cyclic; engine->device_config = rz_dmac_config; engine->device_terminate_all = rz_dmac_terminate_all; engine->device_issue_pending = rz_dmac_issue_pending; From 16ba40151b1e6a52b28296a2173457bc6c31f022 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:05 +0300 Subject: [PATCH 2829/5214] dmaengine: sh: rz-dmac: Adjust rz_dmac_chan_get_residue() to return error codes Adjust rz_dmac_chan_get_residue() to return error codes on failure and provide the residue to callers through the residue parameter. This prepares the code for the addition of runtime PM support. Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-14-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 8fd8a4bd9cc9..93394b9934c8 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -860,8 +860,8 @@ static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, return residue; } -static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, - dma_cookie_t cookie) +static int rz_dmac_chan_get_residue(struct device *dev, struct rz_dmac_chan *channel, + dma_cookie_t cookie, u32 *residue) { struct rz_dmac_desc *desc = NULL; struct virt_dma_desc *vd; @@ -871,7 +871,8 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, if (vd) { /* Descriptor has been issued but not yet processed. */ desc = to_rz_dmac_desc(vd); - return desc->len; + *residue = desc->len; + return 0; } else if (channel->desc && channel->desc->vd.tx.cookie == cookie) { /* Descriptor is currently processed. */ desc = channel->desc; @@ -879,6 +880,7 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, if (!desc) { /* Descriptor was not found. May be already completed by now. */ + *residue = 0; return 0; } @@ -901,7 +903,9 @@ static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, * Calculate number of bytes transferred in processing virtual descriptor. * One virtual descriptor can have many lmdesc. */ - return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, desc, crla); + *residue = crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, desc, crla); + + return 0; } static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, @@ -909,15 +913,20 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); enum dma_status status; u32 residue; scoped_guard(spinlock_irqsave, &channel->vc.lock) { + int ret; + status = dma_cookie_status(chan, cookie, txstate); if (status == DMA_COMPLETE || !txstate) return status; - residue = rz_dmac_chan_get_residue(channel, cookie); + ret = rz_dmac_chan_get_residue(dmac->dev, channel, cookie, &residue); + if (ret) + return DMA_ERROR; if (status == DMA_IN_PROGRESS && rz_dmac_chan_is_paused(channel)) status = DMA_PAUSED; From 7c27a4d54d48d0774518390e4ce6cf3309aac141 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:06 +0300 Subject: [PATCH 2830/5214] dmaengine: sh: rz-dmac: Add runtime PM support Protect the driver exposed APIs with runtime PM suspend/resume calls before accessing HW registers. As the current driver leaves runtime PM enabled in probe, the purpose of the changes in this patch is to avoid accessing HW registers after a failed system suspend leaves the runtime PM state of the device improperly reinitialized. In that case, the driver remains bound to the device, the APIs are still exposed, and any access to HW registers without runtime resuming the device may lead to synchronous aborts. To avoid leaking resources in case of runtime PM failures, save the error code returned by PM_RUNTIME_ACQUIRE_ERR() in rz_dmac_terminate_all() and return it only at the end of the function to allow the cleanup code to run. A similar approach is used in rz_dmac_free_chan_resources(). Because some exposed APIs (e.g. ->device_terminate_all()) may be called from atomic context according to the documentation, mark the DMA device as pm_runtime_irq_safe(). This patch prepares the driver for suspend-to-RAM support. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-15-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 60 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 93394b9934c8..bd4ca8e939f1 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -549,12 +549,22 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) struct rz_dmac *dmac = to_rz_dmac(chan->device); struct rz_dmac_desc *desc, *_desc; unsigned long flags; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(dmac->dev, "RPM resume failed for channel %s, ret=%d\n!", + dma_chan_name(chan), ret); + } spin_lock_irqsave(&channel->vc.lock, flags); rz_lmdesc_setup(channel, channel->lmdesc.base); - rz_dmac_disable_hw(channel); + /* Skip touching HW if RPM resume failed. Let the cleanup do its jobs. */ + if (!ret) + rz_dmac_disable_hw(channel); if (channel->mid_rid >= 0) { clear_bit(channel->mid_rid, dmac->modules); @@ -697,11 +707,22 @@ rz_dmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, static int rz_dmac_terminate_all(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); unsigned long flags; LIST_HEAD(head); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(dmac->dev, "RPM resume failed for channel %s, ret=%d\n!", + dma_chan_name(chan), ret); + } spin_lock_irqsave(&channel->vc.lock, flags); - rz_dmac_disable_hw(channel); + /* Don't return if RPM failed. Let the cleanup do its jobs. */ + if (!ret) + rz_dmac_disable_hw(channel); rz_lmdesc_setup(channel, channel->lmdesc.base); if (channel->desc) { @@ -716,13 +737,20 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_dma_desc_free_list(&channel->vc, &head); - return 0; + return ret; } static void rz_dmac_issue_pending(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); unsigned long flags; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return; spin_lock_irqsave(&channel->vc.lock, flags); @@ -807,6 +835,11 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan) vchan_synchronize(&channel->vc); + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return; + ret = read_poll_timeout(rz_dmac_ch_readl, chstat, !(chstat & CHSTAT_EN), 100, 100000, false, channel, CHSTAT, 1); if (ret < 0) @@ -866,6 +899,7 @@ static int rz_dmac_chan_get_residue(struct device *dev, struct rz_dmac_chan *cha struct rz_dmac_desc *desc = NULL; struct virt_dma_desc *vd; u32 crla, crtb, i; + int ret; vd = vchan_find_desc(&channel->vc, cookie); if (vd) { @@ -884,6 +918,11 @@ static int rz_dmac_chan_get_residue(struct device *dev, struct rz_dmac_chan *cha return 0; } + PM_RUNTIME_ACQUIRE_IF_ENABLED(dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + /* * We need to read two registers. Make sure the hardware does not move * to next lmdesc while reading the current lmdesc. Trying it 3 times @@ -965,6 +1004,13 @@ static int rz_dmac_device_pause_set(struct rz_dmac_chan *channel, static int rz_dmac_device_pause(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; guard(spinlock_irqsave)(&channel->vc.lock); @@ -994,6 +1040,13 @@ static int rz_dmac_device_resume_set(struct rz_dmac_chan *channel, static int rz_dmac_device_resume(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; guard(spinlock_irqsave)(&channel->vc.lock); @@ -1274,6 +1327,7 @@ static int rz_dmac_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(dmac->rstc), "failed to get resets\n"); + pm_runtime_irq_safe(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { From c13ce43e70719dead7009e7e708971ba1c447568 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:07 +0300 Subject: [PATCH 2831/5214] dmaengine: sh: rz-dmac: Add suspend to RAM support The Renesas RZ/G3S SoC supports a power saving mode in which power to most of the SoC components is turned off, including the DMA IP. Add suspend to RAM support to save and restore the DMA IP registers. Cyclic DMA channels require special handling. Since they can be paused and resumed during system suspend/resume, the driver restores additional registers for these channels during the system resume phase. If a channel was not explicitly paused during suspend, the driver ensures that it is paused and resumed as part of the system suspend/resume flow. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-16-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 180 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index bd4ca8e939f1..2a7124e4aea3 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -69,10 +69,12 @@ struct rz_dmac_desc { * enum rz_dmac_chan_status: RZ DMAC channel status * @RZ_DMAC_CHAN_STATUS_PAUSED: Channel is paused though DMA engine callbacks * @RZ_DMAC_CHAN_STATUS_CYCLIC: Channel is cyclic + * @RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL: Channel is paused through driver internal logic */ enum rz_dmac_chan_status { RZ_DMAC_CHAN_STATUS_PAUSED, RZ_DMAC_CHAN_STATUS_CYCLIC, + RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL, }; struct rz_dmac_chan { @@ -92,6 +94,10 @@ struct rz_dmac_chan { u32 chctrl; int mid_rid; + struct { + u32 nxla; + } pm_state; + struct list_head ld_free; struct { @@ -1017,20 +1023,57 @@ static int rz_dmac_device_pause(struct dma_chan *chan) return rz_dmac_device_pause_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); } +static int rz_dmac_device_pause_internal(struct rz_dmac_chan *channel) +{ + lockdep_assert_held(&channel->vc.lock); + + /* Skip channels explicitly paused by consummers or disabled. */ + if (channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED) || + !rz_dmac_chan_is_enabled(channel)) + return 0; + + return rz_dmac_device_pause_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL)); +} + static int rz_dmac_device_resume_set(struct rz_dmac_chan *channel, unsigned long clear_bitmask) { - int ret = 0; u32 val; + int ret; lockdep_assert_held(&channel->vc.lock); - /* Do not check CHSTAT_SUS but rely on HW capabilities. */ + /* + * We can be: + * + * 1/ after the channel was paused by a consummer and now it + * needs to be resummed + * 2/ after the channel was paused internally (as a result of + * a system suspend with power loss or not) + * 3/ after the channel was paused by a consummer, the system + * went through a system suspend (with power loss or not) + * and the consummer wants to resume the channel + * + * To cover all the above cases we set both CLRSUS and SETEN. + * + * In case 1/ setting SETEN while the channel is still enabled + * is harmless for the controller. + * + * In case 2/ the channel is disabled when calling this function + * and setting CLRSUS is harmless for the controller as the + * channel is disabled anyway. + * + * In case 3/ the channel is disabled/enabled if the system + * went though a suspend with power loss/or not and setting + * CLRSUS/SETEN is harmless for the controller as the channel + * is enabled/disabled anyway. + */ + + rz_dmac_ch_writel(channel, CHCTRL_CLRSUS | CHCTRL_SETEN, CHCTRL, 1); - rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1); ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val, - !(val & CHSTAT_SUS), 1, 1024, false, - channel, CHSTAT, 1); + ((val & (CHSTAT_SUS | CHSTAT_EN)) == CHSTAT_EN), + 1, 1024, false, channel, CHSTAT, 1); channel->status &= ~clear_bitmask; @@ -1056,6 +1099,16 @@ static int rz_dmac_device_resume(struct dma_chan *chan) return rz_dmac_device_resume_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); } +static int rz_dmac_device_resume_internal(struct rz_dmac_chan *channel) +{ + lockdep_assert_held(&channel->vc.lock); + + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL))) + return 0; + + return rz_dmac_device_resume_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED_INTERNAL)); +} + /* * ----------------------------------------------------------------------------- * IRQ handling @@ -1421,6 +1474,122 @@ static void rz_dmac_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static void rz_dmac_suspend_recover(struct rz_dmac *dmac) +{ + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED(dmac->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return; + + for (unsigned int i = 0; i < dmac->n_channels; i++) { + struct rz_dmac_chan *channel = &dmac->channels[i]; + + guard(spinlock_irqsave)(&channel->vc.lock); + + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC))) + continue; + + rz_dmac_device_resume_internal(channel); + } +} + +static int rz_dmac_suspend(struct device *dev) +{ + struct rz_dmac *dmac = dev_get_drvdata(dev); + int ret = 0; + + for (unsigned int i = 0; i < dmac->n_channels; i++) { + struct rz_dmac_chan *channel = &dmac->channels[i]; + + guard(spinlock_irqsave)(&channel->vc.lock); + + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC))) + continue; + + ret = rz_dmac_device_pause_internal(channel); + if (ret) { + dev_err(dev, "Failed to suspend channel %s\n", + dma_chan_name(&channel->vc.chan)); + break; + } + + channel->pm_state.nxla = rz_dmac_ch_readl(channel, NXLA, 1); + } + + if (ret) + goto suspend_recover; + + ret = reset_control_assert(dmac->rstc); + if (ret) + goto suspend_recover; + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + goto reset_deassert; + + return 0; + +reset_deassert: + reset_control_deassert(dmac->rstc); +suspend_recover: + rz_dmac_suspend_recover(dmac); + return ret; +} + +static int rz_dmac_resume(struct device *dev) +{ + struct rz_dmac *dmac = dev_get_drvdata(dev); + int errors = 0, ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = reset_control_deassert(dmac->rstc); + if (ret) { + /* + * Do not put runtime PM here and keep the same state as in + * probe. As subsequent suspend/resume cycles may follow, leave + * the runtime PM as is, here, to avoid imbalances. + */ + return ret; + } + + rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_0_7_COMMON_BASE + DCTRL); + rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_8_15_COMMON_BASE + DCTRL); + + for (unsigned int i = 0; i < dmac->n_channels; i++) { + struct rz_dmac_chan *channel = &dmac->channels[i]; + + guard(spinlock_irqsave)(&channel->vc.lock); + + rz_dmac_disable_hw(&dmac->channels[i]); + + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_CYCLIC))) + continue; + + rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); + + rz_dmac_ch_writel(channel, channel->pm_state.nxla, NXLA, 1); + rz_dmac_ch_writel(channel, channel->chcfg, CHCFG, 1); + rz_dmac_ch_writel(channel, CHCTRL_SWRST, CHCTRL, 1); + rz_dmac_ch_writel(channel, channel->chctrl, CHCTRL, 1); + + ret = rz_dmac_device_resume_internal(channel); + if (ret) { + errors = ret; + dev_err(dev, "Failed to resume channel %s, ret=%d\n", + dma_chan_name(&channel->vc.chan), ret); + } + } + + return errors ? : 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rz_dmac_pm_ops, rz_dmac_suspend, rz_dmac_resume); + static const struct rz_dmac_info rz_dmac_v2h_info = { .icu_register_dma_req = rzv2h_icu_register_dma_req, .default_dma_req_no = RZV2H_ICU_DMAC_REQ_NO_DEFAULT, @@ -1447,6 +1616,7 @@ static struct platform_driver rz_dmac_driver = { .driver = { .name = "rz-dmac", .of_match_table = of_rz_dmac_match, + .pm = pm_ptr(&rz_dmac_pm_ops), }, .probe = rz_dmac_probe, .remove = rz_dmac_remove, From b4d34819a53964648bc53cabaa3ba9890d4fdf9c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:08 +0300 Subject: [PATCH 2832/5214] ASoC: renesas: rz-ssi: Add pause support Add pause support as a preparatory step to switch to PCM dmaengine APIs. Acked-by: Mark Brown Tested-by: John Madieu Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260526084710.3491480-17-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- sound/soc/renesas/rz-ssi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 71e434cfe07b..d4e1dded3a9c 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -847,6 +847,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = rz_ssi_trigger_resume(ssi, strm); if (ret) return ret; @@ -888,6 +889,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, break; case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: rz_ssi_stop(ssi, strm); break; @@ -1054,7 +1056,8 @@ static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_RESUME, + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE, .buffer_bytes_max = PREALLOC_BUFFER, .period_bytes_min = 32, .period_bytes_max = 8192, From 9fcaec81ac56c9d2c5d779ffb5a76b622b4d0590 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:09 +0300 Subject: [PATCH 2833/5214] ASoC: renesas: rz-ssi: Use generic PCM dmaengine APIs On Renesas RZ/G2L and RZ/G3S SoCs (where this was tested), captured audio files occasionally contained random spikes when viewed with a tool such as Audacity. These spikes were also audible as popping noises. Using cyclic DMA resolves this issue. The driver was reworked to use the existing support provided by the generic PCM dmaengine APIs. In addition to eliminating the random spikes, the following issues were addressed: - blank periods at the beginning of recorded files, which occurred intermittently, are no longer present - no overruns or underruns were observed when continuously recording short audio files (e.g. 5 seconds long) in a loop - concurrency issues in the SSI driver when enqueuing DMA requests were eliminated; previously, DMA requests could be prepared and submitted both from the DMA completion callback and the interrupt handler, which led to crashes after several hours of testing - the SSI driver logic is simplified - the number of generated interrupts is reduced by approximately 250% In the SSI platform driver probe function, the following changes were made: - the driver-specific DMA configuration was removed in favor of the generic PCM dmaengine APIs. As a result, explicit cleanup goto labels are no longer required and the driver remove callback was dropped, since resource management is now handled via devres helpers - special handling was added for IP variants operating in half-duplex mode, where the DMA channel name in the device tree is "rt"; this DMA channel name is taken into account and passed to the generic PCM dmaengine configuration data All code previously responsible for preparing and completing DMA transfers was removed, as this functionality is now handled entirely by the generic PCM dmaengine APIs. Since DMA channels must be paused and resumed during recovery paths (overruns and underruns reported by the hardware), the DMA channel references are stored in rz_ssi_hw_params(). The logic in rz_ssi_is_dma_enabled() was updated to reflect that the driver no longer manages DMA transfers directly. To avoid software reported underruns (e.g. when running aplay during consecutive suspend/resume cycles, or when the CPU is nearly 100% loaded), rz_ssi_pcm_hardware.buffer_bytes_max was increased to 192K. At the same time, rz_ssi_pcm_hardware.period_bytes_max was set to 48K to reduce interrupt overhead. Finally, rz_ssi_stream_is_play() was removed, as it had only a single remaining user after this rework, and its logic was inlined at the call site. Acked-by: Mark Brown Tested-by: John Madieu Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260526084710.3491480-18-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- sound/soc/renesas/Kconfig | 1 + sound/soc/renesas/rz-ssi.c | 394 +++++++++++++------------------------ 2 files changed, 136 insertions(+), 259 deletions(-) diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig index 11c2027c88a7..6520217e7407 100644 --- a/sound/soc/renesas/Kconfig +++ b/sound/soc/renesas/Kconfig @@ -56,6 +56,7 @@ config SND_SOC_MSIOF config SND_SOC_RZ tristate "RZ/G2L series SSIF-2 support" depends on ARCH_RZG2L || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM help This option enables RZ/G2L SSIF-2 sound support. diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index d4e1dded3a9c..9fe8a639c47c 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -87,8 +89,6 @@ struct rz_ssi_stream { struct rz_ssi_priv *priv; struct snd_pcm_substream *substream; int fifo_sample_size; /* sample capacity of SSI FIFO */ - int dma_buffer_pos; /* The address for the next DMA descriptor */ - int completed_dma_buf_pos; /* The address of the last completed DMA descriptor. */ int period_counter; /* for keeping track of periods transferred */ int buffer_pos; /* current frame position in the buffer */ int running; /* 0=stopped, 1=running */ @@ -96,8 +96,6 @@ struct rz_ssi_stream { int uerr_num; int oerr_num; - struct dma_chan *dma_ch; - int (*transfer)(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm); }; @@ -108,7 +106,6 @@ struct rz_ssi_priv { struct clk *sfr_clk; struct clk *clk; - phys_addr_t phys; int irq_int; int irq_tx; int irq_rx; @@ -148,9 +145,10 @@ struct rz_ssi_priv { unsigned int sample_width; unsigned int sample_bits; } hw_params_cache; -}; -static void rz_ssi_dma_complete(void *data); + struct snd_dmaengine_dai_dma_data dma_dais[SNDRV_PCM_STREAM_LAST + 1]; + struct dma_chan *dmas[SNDRV_PCM_STREAM_LAST + 1]; +}; static void rz_ssi_reg_writel(struct rz_ssi_priv *priv, uint reg, u32 data) { @@ -172,11 +170,6 @@ static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg, writel(val, (priv->base + reg)); } -static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream) -{ - return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -} - static inline struct rz_ssi_stream * rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream) { @@ -185,7 +178,7 @@ rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream) static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) { - return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch)); + return !ssi->playback.transfer && !ssi->capture.transfer; } static void rz_ssi_set_substream(struct rz_ssi_stream *strm, @@ -215,8 +208,6 @@ static void rz_ssi_stream_init(struct rz_ssi_stream *strm, struct snd_pcm_substream *substream) { rz_ssi_set_substream(strm, substream); - strm->dma_buffer_pos = 0; - strm->completed_dma_buf_pos = 0; strm->period_counter = 0; strm->buffer_pos = 0; @@ -242,12 +233,13 @@ static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, dev_info(dev, "underrun = %d\n", strm->uerr_num); } -static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, - unsigned int channels) +static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream, + unsigned int rate, unsigned int channels) { static u8 ckdv[] = { 1, 2, 4, 8, 16, 32, 64, 128, 6, 12, 24, 48, 96 }; unsigned int channel_bits = 32; /* System Word Length */ unsigned long bclk_rate = rate * channels * channel_bits; + struct snd_dmaengine_dai_dma_data *dma_dai; unsigned int div; unsigned int i; u32 ssicr = 0; @@ -290,6 +282,8 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, return -EINVAL; } + dma_dai = &ssi->dma_dais[substream->stream]; + /* * DWL: Data Word Length = {16, 24, 32} bits * SWL: System Word Length = 32 bits @@ -298,12 +292,15 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, switch (ssi->hw_params_cache.sample_width) { case 16: ssicr |= SSICR_DWL(1); + dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case 24: ssicr |= SSICR_DWL(5) | SSICR_PDTA; + dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; case 32: ssicr |= SSICR_DWL(6); + dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: dev_err(ssi->dev, "Not support %u data width", @@ -344,7 +341,7 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { - bool is_play = rz_ssi_stream_is_play(strm->substream); + bool is_play = strm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK; bool is_full_duplex; u32 ssicr, ssifcr; @@ -423,14 +420,6 @@ static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) /* Disable TX/RX */ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); - /* Cancel all remaining DMA transactions */ - if (rz_ssi_is_dma_enabled(ssi)) { - if (ssi->playback.dma_ch) - dmaengine_terminate_async(ssi->playback.dma_ch); - if (ssi->capture.dma_ch) - dmaengine_terminate_async(ssi->capture.dma_ch); - } - rz_ssi_set_idle(ssi); return 0; @@ -458,10 +447,6 @@ static void rz_ssi_pointer_update(struct rz_ssi_stream *strm, int frames) snd_pcm_period_elapsed(strm->substream); strm->period_counter = current_period; } - - strm->completed_dma_buf_pos += runtime->period_size; - if (strm->completed_dma_buf_pos >= runtime->buffer_size) - strm->completed_dma_buf_pos = 0; } static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) @@ -606,12 +591,6 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data) if (irq == ssi->irq_int) { /* error or idle */ bool is_stopped = !!(ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ)); - int i, count; - - if (rz_ssi_is_dma_enabled(ssi)) - count = 4; - else - count = 1; if (ssi->capture.substream && is_stopped) { if (ssisr & SSISR_RUIRQ) @@ -631,19 +610,41 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data) rz_ssi_stop(ssi, strm_playback); } + if (!rz_ssi_is_stream_running(&ssi->playback) && + !rz_ssi_is_stream_running(&ssi->capture) && + rz_ssi_is_dma_enabled(ssi) && is_stopped) { + if (ssi->playback.substream && + ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]) + dmaengine_pause(ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]); + if (ssi->capture.substream && + ssi->dmas[SNDRV_PCM_STREAM_CAPTURE] && + /* Avoid calling pause twice in case of half duplex. */ + ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK] != + ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]) + dmaengine_pause(ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]); + } + /* Clear all flags */ rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | SSISR_RUIRQ, 0); /* Add/remove more data */ if (ssi->capture.substream && is_stopped) { - for (i = 0; i < count; i++) + if (rz_ssi_is_dma_enabled(ssi)) { + if (ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]) + dmaengine_resume(ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]); + } else { strm_capture->transfer(ssi, strm_capture); + } } if (ssi->playback.substream && is_stopped) { - for (i = 0; i < count; i++) + if (rz_ssi_is_dma_enabled(ssi)) { + if (ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]) + dmaengine_resume(ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]); + } else { strm_playback->transfer(ssi, strm_playback); + } } /* Resume */ @@ -679,153 +680,11 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data) return IRQ_HANDLED; } -static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi, - struct dma_chan *dma_ch, bool is_play) -{ - struct dma_slave_config cfg; - - memset(&cfg, 0, sizeof(cfg)); - - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - cfg.dst_addr = ssi->phys + SSIFTDR; - cfg.src_addr = ssi->phys + SSIFRDR; - if (ssi->hw_params_cache.sample_width == 16) { - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - } else { - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - } - - return dmaengine_slave_config(dma_ch, &cfg); -} - -static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm) -{ - struct snd_pcm_substream *substream = strm->substream; - struct dma_async_tx_descriptor *desc; - struct snd_pcm_runtime *runtime; - enum dma_transfer_direction dir; - u32 dma_paddr, dma_size; - int amount; - - if (!rz_ssi_stream_is_valid(ssi, strm)) - return -EINVAL; - - runtime = substream->runtime; - if (runtime->state == SNDRV_PCM_STATE_DRAINING) - /* - * Stream is ending, so do not queue up any more DMA - * transfers otherwise we play partial sound clips - * because we can't shut off the DMA quick enough. - */ - return 0; - - dir = rz_ssi_stream_is_play(substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - - /* Always transfer 1 period */ - amount = runtime->period_size; - - /* DMA physical address and size */ - dma_paddr = runtime->dma_addr + frames_to_bytes(runtime, - strm->dma_buffer_pos); - dma_size = frames_to_bytes(runtime, amount); - desc = dmaengine_prep_slave_single(strm->dma_ch, dma_paddr, dma_size, - dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(ssi->dev, "dmaengine_prep_slave_single() fail\n"); - return -ENOMEM; - } - - desc->callback = rz_ssi_dma_complete; - desc->callback_param = strm; - - if (dmaengine_submit(desc) < 0) { - dev_err(ssi->dev, "dmaengine_submit() fail\n"); - return -EIO; - } - - /* Update DMA pointer */ - strm->dma_buffer_pos += amount; - if (strm->dma_buffer_pos >= runtime->buffer_size) - strm->dma_buffer_pos = 0; - - /* Start DMA */ - dma_async_issue_pending(strm->dma_ch); - - return 0; -} - -static void rz_ssi_dma_complete(void *data) -{ - struct rz_ssi_stream *strm = (struct rz_ssi_stream *)data; - - if (!strm->running || !strm->substream || !strm->substream->runtime) - return; - - /* Note that next DMA transaction has probably already started */ - rz_ssi_pointer_update(strm, strm->substream->runtime->period_size); - - /* Queue up another DMA transaction */ - rz_ssi_dma_transfer(strm->priv, strm); -} - -static void rz_ssi_release_dma_channels(struct rz_ssi_priv *ssi) -{ - if (ssi->playback.dma_ch) { - dma_release_channel(ssi->playback.dma_ch); - ssi->playback.dma_ch = NULL; - if (ssi->dma_rt) - ssi->dma_rt = false; - } - - if (ssi->capture.dma_ch) { - dma_release_channel(ssi->capture.dma_ch); - ssi->capture.dma_ch = NULL; - } -} - -static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev) -{ - ssi->playback.dma_ch = dma_request_chan(dev, "tx"); - if (IS_ERR(ssi->playback.dma_ch)) - ssi->playback.dma_ch = NULL; - - ssi->capture.dma_ch = dma_request_chan(dev, "rx"); - if (IS_ERR(ssi->capture.dma_ch)) - ssi->capture.dma_ch = NULL; - - if (!ssi->playback.dma_ch && !ssi->capture.dma_ch) { - ssi->playback.dma_ch = dma_request_chan(dev, "rt"); - if (IS_ERR(ssi->playback.dma_ch)) { - ssi->playback.dma_ch = NULL; - goto no_dma; - } - - ssi->dma_rt = true; - } - - if (!rz_ssi_is_dma_enabled(ssi)) - goto no_dma; - - return 0; - -no_dma: - rz_ssi_release_dma_channels(ssi); - - return -ENODEV; -} - static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { struct snd_pcm_substream *substream = strm->substream; - struct snd_pcm_runtime *runtime = substream->runtime; int ret; - strm->dma_buffer_pos = strm->completed_dma_buf_pos + runtime->period_size; - if (rz_ssi_is_stream_running(&ssi->playback) || rz_ssi_is_stream_running(&ssi->capture)) return 0; @@ -834,7 +693,7 @@ static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi, struct rz_ssi_stream * if (ret) return ret; - return rz_ssi_clk_setup(ssi, ssi->hw_params_cache.rate, + return rz_ssi_clk_setup(ssi, substream, ssi->hw_params_cache.rate, ssi->hw_params_cache.channels); } @@ -843,7 +702,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, { struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); - int ret = 0, i, num_transfer = 1; + int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: @@ -858,28 +717,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (cmd == SNDRV_PCM_TRIGGER_START) rz_ssi_stream_init(strm, substream); - if (rz_ssi_is_dma_enabled(ssi)) { - bool is_playback = rz_ssi_stream_is_play(substream); - - if (ssi->dma_rt) - ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, - is_playback); - else - ret = rz_ssi_dma_slave_config(ssi, strm->dma_ch, - is_playback); - - /* Fallback to pio */ - if (ret < 0) { - ssi->playback.transfer = rz_ssi_pio_send; - ssi->capture.transfer = rz_ssi_pio_recv; - rz_ssi_release_dma_channels(ssi); - } else { - /* For DMA, queue up multiple DMA descriptors */ - num_transfer = 4; - } - } - - for (i = 0; i < num_transfer; i++) { + if (!rz_ssi_is_dma_enabled(ssi)) { ret = strm->transfer(ssi, strm); if (ret) return ret; @@ -975,6 +813,8 @@ static void rz_ssi_shutdown(struct snd_pcm_substream *substream, ssi->dup.tx_active = false; else ssi->dup.rx_active = false; + + ssi->dmas[substream->stream] = NULL; } static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, @@ -1026,6 +866,12 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* Save the DMA channels for recovery. */ + if (rz_ssi_is_dma_enabled(ssi)) + ssi->dmas[substream->stream] = snd_dmaengine_pcm_get_chan(substream); + else + ssi->dmas[substream->stream] = NULL; + if (rz_ssi_is_stream_running(&ssi->playback) || rz_ssi_is_stream_running(&ssi->capture)) { if (rz_ssi_is_valid_hw_params(ssi, rate, channels, sample_width, sample_bits)) @@ -1041,10 +887,21 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - return rz_ssi_clk_setup(ssi, rate, channels); + return rz_ssi_clk_setup(ssi, substream, rate, channels); +} + +static int rz_ssi_dai_probe(struct snd_soc_dai *dai) +{ + struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &ssi->dma_dais[SNDRV_PCM_STREAM_PLAYBACK], + &ssi->dma_dais[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; } static const struct snd_soc_dai_ops rz_ssi_dai_ops = { + .probe = rz_ssi_dai_probe, .startup = rz_ssi_startup, .shutdown = rz_ssi_shutdown, .trigger = rz_ssi_dai_trigger, @@ -1058,9 +915,9 @@ static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE, - .buffer_bytes_max = PREALLOC_BUFFER, + .buffer_bytes_max = 192 * 1024, .period_bytes_min = 32, - .period_bytes_max = 8192, + .period_bytes_max = 48 * 1024, .channels_min = SSI_CHAN_MIN, .channels_max = SSI_CHAN_MAX, .periods_min = 1, @@ -1068,8 +925,8 @@ static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { .fifo_size = 32 * 2, }; -static int rz_ssi_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static int rz_ssi_pcm_open_pio(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_soc_set_runtime_hwparams(substream, &rz_ssi_pcm_hardware); @@ -1077,6 +934,13 @@ static int rz_ssi_pcm_open(struct snd_soc_component *component, SNDRV_PCM_HW_PARAM_PERIODS); } +static int rz_ssi_pcm_open_dma(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -1093,7 +957,8 @@ static int rz_ssi_pcm_new(struct snd_soc_component *component, { snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, rtd->card->snd_card->dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + rz_ssi_pcm_hardware.buffer_bytes_max, + rz_ssi_pcm_hardware.buffer_bytes_max); return 0; } @@ -1116,16 +981,30 @@ static struct snd_soc_dai_driver rz_ssi_soc_dai[] = { }, }; -static const struct snd_soc_component_driver rz_ssi_soc_component = { +static const struct snd_soc_component_driver rz_ssi_soc_component_pio = { .name = "rz-ssi", - .open = rz_ssi_pcm_open, + .open = rz_ssi_pcm_open_pio, .pointer = rz_ssi_pcm_pointer, .pcm_new = rz_ssi_pcm_new, .legacy_dai_naming = 1, }; +static const struct snd_soc_component_driver rz_ssi_soc_component_dma = { + .name = "rz-ssi", + .open = rz_ssi_pcm_open_dma, + .legacy_dai_naming = 1, +}; + +static const struct snd_dmaengine_pcm_config rz_ssi_dmaengine_pcm_conf = { + .pcm_hardware = &rz_ssi_pcm_hardware, + .prealloc_buffer_size = 192 * 1024, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + static int rz_ssi_probe(struct platform_device *pdev) { + const struct snd_soc_component_driver *component_driver; + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct rz_ssi_priv *ssi; struct clk *audio_clk; @@ -1141,7 +1020,6 @@ static int rz_ssi_probe(struct platform_device *pdev) if (IS_ERR(ssi->base)) return PTR_ERR(ssi->base); - ssi->phys = res->start; ssi->clk = devm_clk_get(dev, "ssi"); if (IS_ERR(ssi->clk)) return PTR_ERR(ssi->clk); @@ -1165,16 +1043,43 @@ static int rz_ssi_probe(struct platform_device *pdev) ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2; - /* Detect DMA support */ - ret = rz_ssi_dma_request(ssi, dev); - if (ret < 0) { + ssi->dma_dais[SNDRV_PCM_STREAM_PLAYBACK].addr = (dma_addr_t)res->start + SSIFTDR; + ssi->dma_dais[SNDRV_PCM_STREAM_CAPTURE].addr = (dma_addr_t)res->start + SSIFRDR; + + if (of_property_present(np, "dma-names")) { + struct snd_dmaengine_pcm_config *config; + unsigned int flags = 0; + + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->pcm_hardware = rz_ssi_dmaengine_pcm_conf.pcm_hardware; + config->prealloc_buffer_size = rz_ssi_dmaengine_pcm_conf.prealloc_buffer_size; + config->prepare_slave_config = rz_ssi_dmaengine_pcm_conf.prepare_slave_config; + + if (of_property_match_string(np, "dma-names", "rt") == 0) { + flags = SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; + config->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "rt"; + } else { + config->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "tx"; + config->chan_names[SNDRV_PCM_STREAM_CAPTURE] = "rx"; + } + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, config, flags); + } else { + ret = -ENODEV; + } + + if (ret == -EPROBE_DEFER) { + return ret; + } else if (ret) { dev_warn(dev, "DMA not available, using PIO\n"); ssi->playback.transfer = rz_ssi_pio_send; ssi->capture.transfer = rz_ssi_pio_recv; + component_driver = &rz_ssi_soc_component_pio; } else { - dev_info(dev, "DMA enabled"); - ssi->playback.transfer = rz_ssi_dma_transfer; - ssi->capture.transfer = rz_ssi_dma_transfer; + dev_info(dev, "DMA enabled\n"); + component_driver = &rz_ssi_soc_component_dma; } ssi->playback.priv = ssi; @@ -1185,17 +1090,13 @@ static int rz_ssi_probe(struct platform_device *pdev) /* Error Interrupt */ ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); - if (ssi->irq_int < 0) { - ret = ssi->irq_int; - goto err_release_dma_chs; - } + if (ssi->irq_int < 0) + return ssi->irq_int; ret = devm_request_irq(dev, ssi->irq_int, rz_ssi_interrupt, 0, dev_name(dev), ssi); - if (ret < 0) { - dev_err_probe(dev, ret, "irq request error (int_req)\n"); - goto err_release_dma_chs; - } + if (ret < 0) + return dev_err_probe(dev, ret, "irq request error (int_req)\n"); if (!rz_ssi_is_dma_enabled(ssi)) { /* Tx and Rx interrupts (pio only) */ @@ -1236,43 +1137,19 @@ static int rz_ssi_probe(struct platform_device *pdev) } ssi->rstc = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(ssi->rstc)) { - ret = PTR_ERR(ssi->rstc); - goto err_release_dma_chs; - } + if (IS_ERR(ssi->rstc)) + return dev_err_probe(dev, PTR_ERR(ssi->rstc), "Failed to get reset\n"); /* Default 0 for power saving. Can be overridden via sysfs. */ pm_runtime_set_autosuspend_delay(dev, 0); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); - if (ret < 0) { - dev_err(dev, "Failed to enable runtime PM!\n"); - goto err_release_dma_chs; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n"); - ret = devm_snd_soc_register_component(dev, &rz_ssi_soc_component, - rz_ssi_soc_dai, - ARRAY_SIZE(rz_ssi_soc_dai)); - if (ret < 0) { - dev_err(dev, "failed to register snd component\n"); - goto err_release_dma_chs; - } - - return 0; - -err_release_dma_chs: - rz_ssi_release_dma_channels(ssi); - - return ret; -} - -static void rz_ssi_remove(struct platform_device *pdev) -{ - struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev); - - rz_ssi_release_dma_channels(ssi); - - reset_control_assert(ssi->rstc); + return devm_snd_soc_register_component(dev, component_driver, + rz_ssi_soc_dai, + ARRAY_SIZE(rz_ssi_soc_dai)); } static const struct of_device_id rz_ssi_of_match[] = { @@ -1307,7 +1184,6 @@ static struct platform_driver rz_ssi_driver = { .pm = pm_ptr(&rz_ssi_pm_ops), }, .probe = rz_ssi_probe, - .remove = rz_ssi_remove, }; module_platform_driver(rz_ssi_driver); From cd2d36e8ae61832aaac3bddf5aafdab72821e6b9 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 26 May 2026 11:47:10 +0300 Subject: [PATCH 2834/5214] dmaengine: sh: rz-dmac: Set the Link End (LE) bit on the last descriptor On an RZ/G2L-based system, it has been observed that when the DMA channels for all enabled IPs are active (TX and RX for one serial IP, TX and RX for one audio IP, and TX and RX for one SPI IP), shortly after all of them are started, the system can become irrecoverably blocked. In one debug session the system did not block, and the DMA HW registers were inspected. It was found that the DER (Descriptor Error) bit in the CHSTAT register for one of the SPI DMA channels was set. According to the RZ/G2L HW Manual, Rev. 1.30, chapter 14.4.7 Channel Status Register n/nS (CHSTAT_n/nS), description of the DER bit, the DER bit is set when the LV (Link Valid) value loaded with a descriptor in link mode is 0. This means that the DMA engine has loaded an invalid descriptor (as defined in Table 14.14, Header Area, of the same manual). The same chapter states that when a descriptor error occurs, the transfer is stopped, but no DMA error interrupt is generated. Set the LE bit on the last descriptor of a transfer. This informs the DMA engine that this is the final descriptor for the transfer. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-19-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 2a7124e4aea3..f1174d25da84 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -200,6 +200,7 @@ struct rz_dmac { /* LINK MODE DESCRIPTOR */ #define HEADER_LV BIT(0) +#define HEADER_LE BIT(1) #define HEADER_WBD BIT(2) #define RZ_DMAC_MAX_CHAN_DESCRIPTORS 16 @@ -382,7 +383,7 @@ static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) lmdesc->chcfg = chcfg; lmdesc->chitvl = 0; lmdesc->chext = 0; - lmdesc->header = HEADER_LV; + lmdesc->header = HEADER_LV | HEADER_LE; rz_dmac_set_dma_req_no(dmac, channel->index, dmac->info->default_dma_req_no); @@ -425,7 +426,7 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) lmdesc->chext = 0; if (i == (sg_len - 1)) { lmdesc->chcfg = (channel->chcfg & ~CHCFG_DEM); - lmdesc->header = HEADER_LV; + lmdesc->header = HEADER_LV | HEADER_LE; } else { lmdesc->chcfg = channel->chcfg; lmdesc->header = HEADER_LV; From 574eda81d0a7238af3268e88f9effe22953460bc Mon Sep 17 00:00:00 2001 From: Li Ming Date: Thu, 23 Apr 2026 19:19:49 +0800 Subject: [PATCH 2835/5214] cxl/memdev: Hold memdev lock during memdev poison injection/clear cxl_dpa_to_region() assumes that it is running a context where it is not racing changes to "cxlmd->dev.driver". Acquire the memdev device lock in the debugfs entry points to preclude debugfs usage racing cxl_mem driver detach. Suggested-by: Dan Williams Reviewed-by: Dave Jiang Reviewed-by: Alison Schofield Reviewed-by: Dan Williams Signed-off-by: Li Ming Link: https://patch.msgid.link/20260423111949.177399-1-ming.li@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/mem.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index fcffe24dcb42..ab88eaa31d1d 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -48,6 +48,11 @@ static int cxl_mem_dpa_show(struct seq_file *file, void *data) static int cxl_debugfs_poison_inject(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_inject_poison(cxlmd, dpa); } @@ -58,6 +63,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_inject_fops, NULL, static int cxl_debugfs_poison_clear(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_clear_poison(cxlmd, dpa); } From 16329b510f76e5b824e05bf8add8b29850f1f16f Mon Sep 17 00:00:00 2001 From: Koba Ko Date: Tue, 14 Apr 2026 10:45:27 +0800 Subject: [PATCH 2836/5214] cxl/region: Validate partition index before array access construct_region() reads cxled->part and uses it to index cxlds->part[] without checking for a negative value. If the partition was never resolved, part remains at its initial value of -1, causing an out-of-bounds array access. Add a guard to return -EBUSY when part is negative. The check was dropped during a merge. Signed-off-by: Koba Ko Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260414024527.3399590-1-kobak@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e50dc716d4e8..cc41c08c0c0c 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -3714,6 +3714,9 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, int rc, part = READ_ONCE(cxled->part); struct cxl_region *cxlr; + if (part < 0) + return ERR_PTR(-EBUSY); + do { cxlr = __create_region(cxlrd, cxlds->part[part].mode, atomic_read(&cxlrd->region_id), From d40745cd06f35095a7b2925ea3217bf7ef764832 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Wed, 27 May 2026 17:03:32 +0800 Subject: [PATCH 2837/5214] cxl/test: Enforce PMD alignment for volatile mock regions cxl_test allocates synthetic CFMWS HPA windows from a gen_pool with SZ_256M alignment. On arm64 with CONFIG_ARM64_64K_PAGES=y and CONFIG_PGTABLE_LEVELS=3, PMD_SIZE is 512M, so every CXL region carved from a volatile window inherits a non-PMD-aligned start, and cxl_dax_region_probe() -> alloc_dax_region() fails: """ cxl_dax_region dax_region1: probe with driver cxl_dax_region failed with error -12 """ Enforce that every volatile mock CFMWS is PMD-aligned in both start and size Reviewed-by: Dave Jiang Acked-by: Kai-Heng Feng Signed-off-by: Richard Cheng Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260527090332.30002-1-icheng@nvidia.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 296516eecfd6..4281d34cd0e7 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -318,7 +318,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = FAKE_QTG_ID, - .window_size = SZ_256M, + .window_size = SZ_256M > PMD_SIZE ? SZ_256M : PMD_SIZE, }, .target = { 3 }, }, @@ -495,9 +495,12 @@ static int populate_cedt(void) for (i = cfmws_start; i <= cfmws_end; i++) { struct acpi_cedt_cfmws *window = mock_cfmws[i]; + int align = SZ_256M; cfmws_elc_update(window, i); - res = alloc_mock_res(window->window_size, SZ_256M); + if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) + align = max_t(int, SZ_256M, PMD_SIZE); + res = alloc_mock_res(window->window_size, align); if (!res) return -ENOMEM; window->base_hpa = res->range.start; @@ -1819,6 +1822,12 @@ static __init int cxl_test_init(void) int rc, i; struct range mappable; + if (!IS_ALIGNED(mock_auto_region_size, PMD_SIZE)) { + pr_err_once("mock_auto_region_size %d must be PMD-aligned\n", + mock_auto_region_size); + return -EINVAL; + } + cxl_acpi_test(); cxl_core_test(); cxl_mem_test(); From 7d178454d0fb555f88d5732a76adf47390ae629d Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 26 May 2026 17:13:03 -0700 Subject: [PATCH 2838/5214] MAINTAINERS: Add CXL reviewer Add Li Ming as CXL subsystem reviewer. Thanks to Li Ming for all the CXL bugs they've found and fixed, and looking forward to many more prevented! Signed-off-by: Alison Schofield Acked-by: Jonathan Cameron Reviewed-by: Li Ming Acked-by: Dave Jiang Link: https://patch.msgid.link/20260527001305.533170-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9ec290e38b44..635b056a20c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6437,6 +6437,7 @@ M: Alison Schofield M: Vishal Verma M: Ira Weiny M: Dan Williams +R: Li Ming L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/driver-api/cxl From 6c9d2e87df40d606f1c85143e9acb1ecff463d5e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:03 -0700 Subject: [PATCH 2839/5214] cxl/fwctl: Fix __fortify_panic Fix a runtime assertion in cxlctl_get_supported_features(). Fortify complains that it is potentially overflowing the entries array per __counted_by_le(num_entries). Quiet the false positive by initializing @num_entries earlier. memcpy: detected buffer overflow: 48 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#7: fwctl/1398 RIP: 0010:__fortify_report+0x50/0xa0 Call Trace: __fortify_panic+0xd/0xf cxlctl_get_supported_features.cold+0x23/0x35 [cxl_core] Fixes: 4d1c09cef2c2 ("cxl: Add support for fwctl RPC command to enable CXL feature commands") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-2-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 3435db9ea6b1..85185af46b72 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -423,6 +423,7 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, rpc_out->size = struct_size(feat_out, ents, requested); feat_out = &rpc_out->get_sup_feats_out; + feat_out->num_entries = cpu_to_le16(requested); for (i = start, pos = &feat_out->ents[0]; i < cxlfs->entries->num_features; i++, pos++) { @@ -444,7 +445,6 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, } } - feat_out->num_entries = cpu_to_le16(requested); feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; *out_len = out_size; From 08326b92c7a414a73b5b308d1daf0e91e0134dfc Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:04 -0700 Subject: [PATCH 2840/5214] cxl/test: Fix __fortify_panic Fix a runtime assertion in setup_xor_mapping(). Fortify complains that it is potentially overflowing the xormaps array per __counted_by(nr_maps). Quiet the false positive by initializing @nr_maps earlier. memcpy: detected buffer overflow: 32 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#8: modprobe/2728 Call Trace: __fortify_panic+0xd/0xf setup_xor_mapping+0x6c/0xa0 [cxl_translate] [ dj: Fixed up @nr_entries to @nr_maps in commit log. ] Fixes: 06377c54a133 ("cxl/test: Add cxl_translate module for address translation testing") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-3-djbw@kernel.org Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl_translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c index 16328b2112b2..25a27e01ac21 100644 --- a/tools/testing/cxl/test/cxl_translate.c +++ b/tools/testing/cxl/test/cxl_translate.c @@ -236,8 +236,8 @@ static int setup_xor_mapping(void) if (!cximsd) return -ENOMEM; - memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); cximsd->nr_maps = nr_maps; + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); return 0; } From 7e541f6dbd05921d0bbb99646028cb9982535707 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:42 +0530 Subject: [PATCH 2841/5214] power: supply: add support for Samsung S2M series PMIC charger device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a driver for charger controllers found in certain Samsung S2M series PMICs. The driver has very basic support for the device, with only charger online reporting working, and USB 2.0 device negotiations working. The driver includes initial support for the S2MU005 PMIC charger. Co-developed-by: Łukasz Lebiedziński Signed-off-by: Łukasz Lebiedziński Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-10-73f9702fb461@disroot.org Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 10 + drivers/power/supply/Makefile | 1 + drivers/power/supply/s2m-charger.c | 313 +++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/power/supply/s2m-charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index c56d7ace7d55..f0ede1cecb6a 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -856,6 +856,16 @@ config CHARGER_RK817 help Say Y to include support for Rockchip RK817 Battery Charger. +config CHARGER_S2M + tristate "Samsung S2M series PMIC battery charger support" + depends on EXTCON_S2M + depends on MFD_SEC_CORE + help + This option enables support for charger devices found in + certain Samsung S2M series PMICs, such as the S2MU005. These + devices provide USB power supply information and also required + for USB OTG role switching. + config CHARGER_SMB347 tristate "Summit Microelectronics SMB3XX Battery Charger" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b55612d626c8..31fe4a145929 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o +obj-$(CONFIG_CHARGER_S2M) += s2m-charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o diff --git a/drivers/power/supply/s2m-charger.c b/drivers/power/supply/s2m-charger.c new file mode 100644 index 000000000000..4d1f2c2c7144 --- /dev/null +++ b/drivers/power/supply/s2m-charger.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Battery Charger Driver for Samsung S2M series PMICs. + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * Copyright (c) 2026 Kaustabh Chakraborty + * Copyright (c) 2026 Łukasz Lebiedziński + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct s2m_chgr { + struct device *dev; + struct regmap *regmap; + struct power_supply *psy; + struct extcon_dev *extcon; + struct work_struct extcon_work; + struct notifier_block extcon_nb; +}; + +static int s2mu005_chgr_get_online(struct s2m_chgr *priv, int *value) +{ + u32 val; + int ret; + + ret = regmap_read(priv->regmap, S2MU005_REG_CHGR_STATUS0, &val); + if (ret) { + dev_err(priv->dev, "failed to read register (%d)\n", ret); + return ret; + } + + *value = !!(val & S2MU005_CHGR_CHG); + + return 0; +} + +static void s2mu005_chgr_get_usb_type(struct s2m_chgr *priv, int *value) +{ + if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_CDP) > 0) + *value = POWER_SUPPLY_USB_TYPE_CDP; + else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_SDP) > 0) + *value = POWER_SUPPLY_USB_TYPE_SDP; + else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_DCP) > 0) + *value = POWER_SUPPLY_USB_TYPE_DCP; + else + *value = POWER_SUPPLY_USB_TYPE_UNKNOWN; +} + +static int s2mu005_chgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct s2m_chgr *priv = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = s2mu005_chgr_get_online(priv, &val->intval); + if (ret) + return ret; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + s2mu005_chgr_get_usb_type(priv, &val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int s2mu005_chgr_mode_set_host(struct s2m_chgr *priv) +{ + int ret; + + /* set mode to OTG */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0, + S2MU005_CHGR_OP_MODE, + FIELD_PREP(S2MU005_CHGR_OP_MODE, + S2MU005_CHGR_OP_MODE_OTG)); + if (ret) { + dev_err(priv->dev, "failed to set OTG mode (%d)\n", ret); + return ret; + } + + /* set boost frequency to 2MHz */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL11, + S2MU005_CHGR_OSC_BOOST, + FIELD_PREP(S2MU005_CHGR_OSC_BOOST, + S2MU005_CHGR_OSC_BOOST_2MHZ)); + if (ret) { + dev_err(priv->dev, "failed to set boost frequency (%d)\n", ret); + return ret; + } + + /* set OTG current limit to 1.5 A */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4, + S2MU005_CHGR_OTG_OCP, + FIELD_PREP(S2MU005_CHGR_OTG_OCP, + S2MU005_CHGR_OTG_OCP_1P5A)); + if (ret) { + dev_err(priv->dev, "failed to set OTG current limit (%d)\n", ret); + return ret; + } + + /* VBUS switches are OFF when OTG over-current happens */ + ret = regmap_set_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4, + S2MU005_CHGR_OTG_OCP_OFF); + if (ret) { + dev_err(priv->dev, "failed to set OTG OCP switch (%d)\n", ret); + return ret; + } + + /* set OTG voltage to 5.1 V */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL5, + S2MU005_CHGR_VMID_BOOST, + FIELD_PREP(S2MU005_CHGR_VMID_BOOST, + S2MU005_CHGR_VMID_BOOST_5P1V)); + if (ret) { + dev_err(priv->dev, "failed to set OTG voltage (%d)\n", ret); + return ret; + } + + /* turn on OTG */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15, + S2MU005_CHGR_OTG_EN, + FIELD_PREP(S2MU005_CHGR_OTG_EN, + S2MU005_CHGR_OTG_EN_ON)); + if (ret) { + dev_err(priv->dev, "failed to turn on OTG (%d)\n", ret); + return ret; + } + + return 0; +} + +static int s2mu005_chgr_mode_set_charger(struct s2m_chgr *priv) +{ + int ret; + + /* first reset to mode 0 */ + ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0, + S2MU005_CHGR_OP_MODE); + if (ret) { + dev_err(priv->dev, "failed to reset opmode (%d)\n", ret); + return ret; + } + + /* wait for the charger to settle before switching to charging mode */ + msleep(50); + /* then set to charging mode */ + ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0, + S2MU005_CHGR_OP_MODE, + FIELD_PREP(S2MU005_CHGR_OP_MODE, + S2MU005_CHGR_OP_MODE_CHG)); + if (ret) { + dev_err(priv->dev, "failed to set opmode to charging (%d)\n", ret); + return ret; + } + + return 0; +} + +static int s2mu005_chgr_mode_unset(struct s2m_chgr *priv) +{ + int ret; + + /* turn off OTG */ + ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15, + S2MU005_CHGR_OTG_EN); + if (ret) { + dev_err(priv->dev, "failed to turn off OTG (%d)\n", ret); + return ret; + } + + /* reset operation mode */ + ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0, + S2MU005_CHGR_OP_MODE); + if (ret) { + dev_err(priv->dev, "failed to reset opmode (%d)\n", ret); + return ret; + } + + return 0; +} + +static void s2mu005_chgr_extcon_work(struct work_struct *work) +{ + struct s2m_chgr *priv = container_of(work, struct s2m_chgr, extcon_work); + + if (extcon_get_state(priv->extcon, EXTCON_USB_HOST) > 0) + s2mu005_chgr_mode_set_host(priv); + else if (extcon_get_state(priv->extcon, EXTCON_USB) > 0) + s2mu005_chgr_mode_set_charger(priv); + else + s2mu005_chgr_mode_unset(priv); + + power_supply_changed(priv->psy); +} + +static const enum power_supply_property s2mu005_chgr_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, +}; + +static const struct power_supply_desc s2mu005_chgr_psy_desc = { + .name = "s2mu005-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = s2mu005_chgr_properties, + .num_properties = ARRAY_SIZE(s2mu005_chgr_properties), + .get_property = s2mu005_chgr_get_property, + .usb_types = BIT(POWER_SUPPLY_USB_TYPE_CDP) | + BIT(POWER_SUPPLY_USB_TYPE_SDP) | + BIT(POWER_SUPPLY_USB_TYPE_DCP) | + BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), +}; + +static int s2m_chgr_extcon_notifier(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct s2m_chgr *priv = container_of(nb, struct s2m_chgr, extcon_nb); + + schedule_work(&priv->extcon_work); + + return NOTIFY_OK; +} + +static int s2m_chgr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent); + struct s2m_chgr *priv; + struct device_node *extcon_node __free(device_node) = NULL; + struct power_supply_config psy_cfg = {}; + const struct power_supply_desc *psy_desc; + work_func_t extcon_work_func; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + priv->regmap = pmic_drvdata->regmap_pmic; + + switch (platform_get_device_id(pdev)->driver_data) { + case S2MU005: + psy_desc = &s2mu005_chgr_psy_desc; + extcon_work_func = s2mu005_chgr_extcon_work; + break; + default: + return dev_err_probe(dev, -ENODEV, + "device type %d is not supported by driver\n", + pmic_drvdata->device_type); + } + + /* MUIC is mandatory. If unavailable, request probe deferral */ + extcon_node = of_get_child_by_name(dev->parent->of_node, "muic"); + if (!extcon_node) + return dev_err_probe(dev, -ENODEV, "MUIC node required but not found\n"); + + priv->extcon = extcon_find_edev_by_node(extcon_node); + if (IS_ERR(priv->extcon)) + return -EPROBE_DEFER; + + psy_cfg.drv_data = priv; + psy_cfg.fwnode = dev_fwnode(dev->parent); + priv->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg); + if (IS_ERR(priv->psy)) + return dev_err_probe(dev, PTR_ERR(priv->psy), + "failed to register power supply subsystem\n"); + + ret = devm_work_autocancel(dev, &priv->extcon_work, extcon_work_func); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize extcon work\n"); + + priv->extcon_nb.notifier_call = s2m_chgr_extcon_notifier; + ret = devm_extcon_register_notifier_all(dev, priv->extcon, &priv->extcon_nb); + if (ret) + return dev_err_probe(dev, ret, "failed to register extcon notifier\n"); + + return 0; +} + +static const struct platform_device_id s2m_chgr_id_table[] = { + { "s2mu005-charger", S2MU005 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, s2m_chgr_id_table); + +static struct platform_driver s2m_chgr_driver = { + .driver = { + .name = "s2m-charger", + }, + .probe = s2m_chgr_probe, + .id_table = s2m_chgr_id_table, +}; +module_platform_driver(s2m_chgr_driver); + +MODULE_DESCRIPTION("Battery Charger Driver For Samsung S2M Series PMICs"); +MODULE_AUTHOR("Kaustabh Chakraborty "); +MODULE_AUTHOR("Łukasz Lebiedziński "); +MODULE_LICENSE("GPL"); From 1ff3f528e67d20e2b1483dcaba899dc7832b2e6b Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 14:32:47 -0400 Subject: [PATCH 2842/5214] rpmsg: char: Fix use-after-free on probe error path rpmsg_chrdev_probe() stores the newly allocated eptdev in the default endpoint's priv pointer before calling rpmsg_chrdev_eptdev_add(). If rpmsg_chrdev_eptdev_add() then fails, its error path frees eptdev while the default endpoint may still dispatch callbacks with the stale priv pointer. Avoid publishing eptdev through the default endpoint until rpmsg_chrdev_eptdev_add() succeeds. Messages received before the priv pointer is published should be ignored by rpmsg_ept_cb(). Flow-control updates can hit rpmsg_ept_flow_cb() in the same window, so make both callbacks return success when priv is NULL. Fixes: bc69d1066569 ("rpmsg: char: Introduce the "rpmsg-raw" channel") Signed-off-by: Yuho Choi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260601183247.1962010-1-dbgh9129@gmail.com Signed-off-by: Mathieu Poirier --- drivers/rpmsg/rpmsg_char.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index ca9cf8858a5e..bff5aefee212 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -104,6 +104,9 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, struct rpmsg_eptdev *eptdev = priv; struct sk_buff *skb; + if (!eptdev) + return 0; + skb = alloc_skb(len, GFP_ATOMIC); if (!skb) return -ENOMEM; @@ -124,6 +127,9 @@ static int rpmsg_ept_flow_cb(struct rpmsg_device *rpdev, void *priv, bool enable { struct rpmsg_eptdev *eptdev = priv; + if (!eptdev) + return 0; + eptdev->remote_flow_restricted = enable; eptdev->remote_flow_updated = true; @@ -490,6 +496,7 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) struct rpmsg_channel_info chinfo; struct rpmsg_eptdev *eptdev; struct device *dev = &rpdev->dev; + int ret; memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); chinfo.src = rpdev->src; @@ -502,13 +509,17 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) /* Set the default_ept to the rpmsg device endpoint */ eptdev->default_ept = rpdev->ept; + ret = rpmsg_chrdev_eptdev_add(eptdev, chinfo); + + if (ret) + return ret; /* * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context. - * Storedit in default_ept *priv field. + * Stored it in default_ept *priv field. */ eptdev->default_ept->priv = eptdev; - return rpmsg_chrdev_eptdev_add(eptdev, chinfo); + return 0; } static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) From 95ce5ffd54cf66098f91892f98606c3bd33846fe Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 18 May 2026 09:38:45 +0200 Subject: [PATCH 2843/5214] ecryptfs: use kasprintf in ecryptfs_crypto_api_algify_cipher_name Use kasprintf() to simplify ecryptfs_crypto_api_algify_cipher_name(). Use const char * for the read-only cipher name and chaining modifier while at it. Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 5ef67b2674ee..74b02b55e3f6 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -49,25 +49,15 @@ void ecryptfs_from_hex(char *dst, char *src, int dst_size) } static int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, - char *cipher_name, - char *chaining_modifier) + const char *cipher_name, + const char *chaining_modifier) { - int cipher_name_len = strlen(cipher_name); - int chaining_modifier_len = strlen(chaining_modifier); - int algified_name_len; - int rc; + (*algified_name) = kasprintf(GFP_KERNEL, "%s(%s)", chaining_modifier, + cipher_name); + if (!(*algified_name)) + return -ENOMEM; - algified_name_len = (chaining_modifier_len + cipher_name_len + 3); - (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); - if (!(*algified_name)) { - rc = -ENOMEM; - goto out; - } - snprintf((*algified_name), algified_name_len, "%s(%s)", - chaining_modifier, cipher_name); - rc = 0; -out: - return rc; + return 0; } /** From b72ab69175157aac2f3ff1ebc0765e3899ba295b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 4 Jun 2026 10:28:39 -0700 Subject: [PATCH 2844/5214] perf lock contention: Allow 'mmap_lock' in -L/--lock-filter The -L/--lock-filter option is to specify target locks by name or address. It's basically for global locks where name or address is known and fixed. But 'mmap_lock' is a per-process lock so it cannot be used for the -L option. $ sudo perf lock con -ab -L mmap_lock ignore unknown symbol: mmap_lock libbpf: map 'addr_filter': failed to create: -EINVAL libbpf: failed to load BPF skeleton 'lock_contention_bpf': -EINVAL Failed to load lock-contention BPF skeleton lock contention BPF setup failed However, it's still a common source of contention especially in a large process so we want to use it for the -L/--lock-filter option. As there is check_lock_type() to check mmap_lock at runtime, let's used it to filter mmap_locks as a special case. Of course, this only works with -b/--use-bpf option. $ sudo perf lock con -b -L mmap_lock -- perf bench mem mmap -f demand -t 2 # Running 'mem/mmap' benchmark: # function 'demand' (Demand loaded mmap()) # Copying 1MB bytes ... 2.679184 GB/sec/thread ( +- 1.78% ) contended total wait max wait avg wait type caller 1 15.22 us 15.22 us 15.22 us rwsem:W __vm_munmap+0x7e 1 7.72 us 7.72 us 7.72 us rwsem:R lock_mm_and_find_vma+0x97 Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Suchit Karunakaran Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lock_contention.sh | 11 ++++++++ tools/perf/util/bpf_lock_contention.c | 9 ++++++- .../perf/util/bpf_skel/lock_contention.bpf.c | 25 +++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index 6dd90519f45c..52e8b9db9fbd 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -208,6 +208,17 @@ test_lock_filter() err=1 exit fi + + perf lock con -b -L mmap_lock -q -- perf bench mem mmap -t 2 -l 10 > /dev/null 2> ${result} + + # find out the type of mmap_lock + test_lock_filter_type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//') + + if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then + echo "[Fail] BPF result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")" + err=1 + exit + fi } test_stack_filter() diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index cbd7435579fe..eb8e29b8064b 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -186,6 +186,7 @@ int lock_contention_prepare(struct lock_contention *con) int ncpus = 1, ntasks = 1, ntypes = 1, naddrs = 1, ncgrps = 1, nslabs = 1; struct evlist *evlist = con->evlist; struct target *target = con->target; + bool has_mmap_lock = false; /* make sure it loads the kernel map before lookup */ map__load(machine__kernel_map(con->machine)); @@ -244,6 +245,11 @@ int lock_contention_prepare(struct lock_contention *con) unsigned long *addrs; for (i = 0; i < con->filters->nr_syms; i++) { + if (!strcmp(con->filters->syms[i], "mmap_lock")) { + has_mmap_lock = true; + continue; + } + sym = machine__find_kernel_symbol_by_name(con->machine, con->filters->syms[i], &kmap); @@ -263,7 +269,7 @@ int lock_contention_prepare(struct lock_contention *con) addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start); con->filters->addrs = addrs; } - naddrs = con->filters->nr_addrs; + naddrs = con->filters->nr_addrs ?: has_mmap_lock; skel->rodata->has_addr = 1; } @@ -298,6 +304,7 @@ int lock_contention_prepare(struct lock_contention *con) skel->rodata->aggr_mode = con->aggr_mode; skel->rodata->needs_callstack = con->save_callstack; skel->rodata->lock_owner = con->owner; + skel->rodata->has_mmap_lock = has_mmap_lock; if (con->aggr_mode == LOCK_AGGR_CGROUP || con->filters->nr_cgrps) { if (cgroup_is_v2("perf_event")) diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index 5c8431be674a..d4186ae9f85c 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -191,6 +191,7 @@ const volatile int has_type; const volatile int has_addr; const volatile int has_cgroup; const volatile int has_slab; +const volatile int has_mmap_lock; const volatile int needs_callstack; const volatile int stack_skip; const volatile int lock_owner; @@ -221,6 +222,8 @@ int data_map_full; struct task_struct *bpf_task_from_pid(s32 pid) __ksym __weak; void bpf_task_release(struct task_struct *p) __ksym __weak; +static inline __u32 check_lock_type(__u64 lock, __u32 flags); + static inline __u64 get_current_cgroup_id(void) { struct task_struct *task; @@ -246,6 +249,8 @@ static inline __u64 get_current_cgroup_id(void) static inline int can_record(u64 *ctx) { + bool is_addr_ok = false; + if (has_cpu) { __u32 cpu = bpf_get_smp_processor_id(); __u8 *ok; @@ -278,8 +283,10 @@ static inline int can_record(u64 *ctx) __u64 addr = ctx[0]; ok = bpf_map_lookup_elem(&addr_filter, &addr); - if (!ok && !has_slab) + if (!ok && !has_slab && !has_mmap_lock) return 0; + + is_addr_ok = !!ok; } if (has_cgroup) { @@ -291,6 +298,10 @@ static inline int can_record(u64 *ctx) return 0; } + if (is_addr_ok) + return 1; + + /* slab and mmap_lock are part of the addr_filter */ if (has_slab && bpf_get_kmem_cache) { __u8 *ok; __u64 addr = ctx[0]; @@ -298,7 +309,17 @@ static inline int can_record(u64 *ctx) kmem_cache_addr = (long)bpf_get_kmem_cache(addr); ok = bpf_map_lookup_elem(&slab_filter, &kmem_cache_addr); - if (!ok) + if (ok) + return 1; + else if (!has_mmap_lock) + return 0; + } + + if (has_mmap_lock) { + __u64 lock = ctx[0]; + __u32 flag = ctx[1]; + + if (check_lock_type(lock, flag) != LCD_F_MMAP_LOCK) return 0; } From 536e81f07450562caba8e62022763a78fd00a0a7 Mon Sep 17 00:00:00 2001 From: Michael Jeanson Date: Thu, 4 Jun 2026 13:17:05 -0400 Subject: [PATCH 2845/5214] perf data ctf: replace libbabeltrace with babeltrace2-ctf-writer The 1.x branch of Babeltrace has been superseded by 2.x in 2020 and has been unmaintained since 2022, efforts have started to remove it from popular distributions. Babeltrace 2.x offers a very similar 'ctf-writer' library that can be used with minimal changes for the '--to-ctf' feature and has been packaged since Debian 11 and Fedora 32. This patch replaces the 'libbabeltrace' build feature with 'babeltrace2-ctf-writer' using pkgconfig detection, adjusts the naming of the public headers and applies minor API cleanups. There is no changes to the output ctf traces, the ctf-writer API still implements version 1.8 of the CTF specification that can be read by either Babeltrace 1 / 2 or any CTF compliant reader. Also remove some ifdefs in the cli option parsing to allow printing the helpful error message with '--to-ctf' when built without babeltrace2. Reviewed-by: Ian Rogers Signed-off-by: Michael Jeanson Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Derek Foreman Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 5 +-- tools/build/feature/Makefile | 6 ++-- tools/build/feature/test-all.c | 6 ++-- ...ltrace.c => test-babeltrace2-ctf-writer.c} | 3 +- tools/perf/Makefile.config | 27 +++++++-------- tools/perf/Makefile.perf | 2 +- tools/perf/builtin-check.c | 2 +- tools/perf/builtin-data.c | 18 +++------- tools/perf/tests/make | 4 +-- .../shell/test_perf_data_converter_ctf.sh | 8 ++--- tools/perf/util/Build | 2 +- tools/perf/util/data-convert-bt.c | 33 +++++++++---------- tools/perf/util/data-convert.h | 4 +-- 13 files changed, 53 insertions(+), 67 deletions(-) rename tools/build/feature/{test-libbabeltrace.c => test-babeltrace2-ctf-writer.c} (59%) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index af79c6cf0229..ed1374af31c1 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -116,7 +116,7 @@ FEATURE_TESTS_EXTRA := \ gtk2 \ gtk2-infobar \ hello \ - libbabeltrace \ + babeltrace2-ctf-writer \ libcapstone \ libcheck \ libbfd-liberty \ @@ -169,6 +169,7 @@ FEATURE_GROUP_MEMBERS-libbfd = libbfd-liberty libbfd-liberty-z # Declare list of feature dependency packages that provide pkg-config files. # FEATURE_PKG_CONFIG ?= \ + babeltrace2-ctf-writer \ libtraceevent \ libtracefs @@ -217,7 +218,7 @@ ifeq ($(feature-all), 1) $(call feature_check,compile-32) $(call feature_check,compile-x32) $(call feature_check,bionic) - $(call feature_check,libbabeltrace) + $(call feature_check,babeltrace2-ctf-writer) $(call feature_check,libunwind) $(call feature_check,libunwind-debug-frame) $(foreach arch,$(LIBUNWIND_ARCHS),$(call feature_check,libunwind-$(arch))) diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 9da5a4fd956a..62909a9c799d 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -45,7 +45,7 @@ FILES= \ test-pthread-barrier.bin \ test-stackprotector-all.bin \ test-timerfd.bin \ - test-libbabeltrace.bin \ + test-babeltrace2-ctf-writer.bin \ test-libcapstone.bin \ test-libcheck.bin \ test-compile-32.bin \ @@ -305,8 +305,8 @@ $(OUTPUT)test-backtrace.bin: $(OUTPUT)test-timerfd.bin: $(BUILD) -$(OUTPUT)test-libbabeltrace.bin: - $(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace) +$(OUTPUT)test-babeltrace2-ctf-writer.bin: + $(BUILD) # -lbabeltrace2-ctf-writer provided by $(FEATURE_CHECK_LDFLAGS-babeltrace2-ctf-writer) $(OUTPUT)test-libcapstone.bin: $(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone) diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 1488bf6e6078..544563d62950 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -100,13 +100,13 @@ # if 0 /* - * Disable libbabeltrace check for test-all, because the requested + * Disable babeltrace2-ctf-writer check for test-all, because the requested * library version is not released yet in most distributions. Will * reenable later. */ -#define main main_test_libbabeltrace -# include "test-libbabeltrace.c" +#define main main_test_babeltrace2_ctf_writer +# include "test-babeltrace2-ctf-writer.c" #undef main #endif diff --git a/tools/build/feature/test-libbabeltrace.c b/tools/build/feature/test-babeltrace2-ctf-writer.c similarity index 59% rename from tools/build/feature/test-libbabeltrace.c rename to tools/build/feature/test-babeltrace2-ctf-writer.c index 10bb69d55694..9c89082e9f88 100644 --- a/tools/build/feature/test-libbabeltrace.c +++ b/tools/build/feature/test-babeltrace2-ctf-writer.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include +#include int main(void) { diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index c531b9315609..6e7b15fab2ec 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -184,14 +184,11 @@ endif FEATURE_CHECK_CFLAGS-libdw := $(LIBDW_CFLAGS) FEATURE_CHECK_LDFLAGS-libdw := $(LIBDW_LDFLAGS) $(DWARFLIBS) -# for linking with debug library, run like: -# make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/ -ifdef LIBBABELTRACE_DIR - LIBBABELTRACE_CFLAGS := -I$(LIBBABELTRACE_DIR)/include - LIBBABELTRACE_LDFLAGS := -L$(LIBBABELTRACE_DIR)/lib +ifneq ($(NO_BABELTRACE2),1) + ifeq ($(call get-executable,$(PKG_CONFIG)),) + $(error Error: $(PKG_CONFIG) needed by babeltrace2 is missing on this system, please install it) + endif endif -FEATURE_CHECK_CFLAGS-libbabeltrace := $(LIBBABELTRACE_CFLAGS) -FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf # for linking with debug library, run like: # make DEBUG=1 LIBCAPSTONE_DIR=/opt/capstone/ @@ -1035,15 +1032,15 @@ else NO_PERF_READ_VDSOX32 := 1 endif -ifndef NO_LIBBABELTRACE - $(call feature_check,libbabeltrace) - ifeq ($(feature-libbabeltrace), 1) - CFLAGS += -DHAVE_LIBBABELTRACE_SUPPORT $(LIBBABELTRACE_CFLAGS) - LDFLAGS += $(LIBBABELTRACE_LDFLAGS) - EXTLIBS += -lbabeltrace-ctf - $(call detected,CONFIG_LIBBABELTRACE) +ifndef NO_BABELTRACE2 + $(call feature_check,babeltrace2-ctf-writer) + ifeq ($(feature-babeltrace2-ctf-writer), 1) + CFLAGS += -DHAVE_BABELTRACE2_CTF_WRITER_SUPPORT $(shell $(PKG_CONFIG) --cflags babeltrace2-ctf-writer) + LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L babeltrace2-ctf-writer) + EXTLIBS += $(shell $(PKG_CONFIG) --libs-only-l babeltrace2-ctf-writer) + $(call detected,CONFIG_BABELTRACE2_CTF_WRITER) else - $(warning No libbabeltrace found, disables 'perf data' CTF format support, please install libbabeltrace-dev[el]/libbabeltrace-ctf-dev) + $(warning No babeltrace2 found, disables 'perf data' CTF format support, please install libbabeltrace2-dev[el]) endif endif diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 4ac2a0cec9ee..ab661a1d271c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -79,7 +79,7 @@ include ../scripts/utilities.mak # # Define NO_ZLIB if you do not want to support compressed kernel modules # -# Define NO_LIBBABELTRACE if you do not want libbabeltrace support +# Define NO_BABELTRACE2 if you do not want babeltrace2-ctf-writer support # for CTF data format. # # Define NO_CAPSTONE if you do not want libcapstone support diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 3641d263b345..60437650c50f 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -43,7 +43,7 @@ struct feature_status supported_features[] = { FEATURE_STATUS("dwarf_getlocations", HAVE_LIBDW_SUPPORT), FEATURE_STATUS("dwarf-unwind", HAVE_DWARF_UNWIND_SUPPORT), FEATURE_STATUS_TIP("libbfd", HAVE_LIBBFD_SUPPORT, "Deprecated, license incompatibility, use BUILD_NONDISTRO=1 and install binutils-dev[el]"), - FEATURE_STATUS("libbabeltrace", HAVE_LIBBABELTRACE_SUPPORT), + FEATURE_STATUS("babeltrace2-ctf-writer", HAVE_BABELTRACE2_CTF_WRITER_SUPPORT), FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT), FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT), FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT), diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c index 4c08ccb8c06b..1dd73ed6bdcb 100644 --- a/tools/perf/builtin-data.c +++ b/tools/perf/builtin-data.c @@ -40,10 +40,8 @@ static const struct option data_options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_STRING(0, "to-json", &to_json, NULL, "Convert to JSON format"), -#ifdef HAVE_LIBBABELTRACE_SUPPORT OPT_STRING(0, "to-ctf", &to_ctf, NULL, "Convert to CTF format"), OPT_BOOLEAN(0, "tod", &opts.tod, "Convert time to wall clock time"), -#endif OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"), OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"), OPT_STRING(0, "time", &opts.time_str, "str", @@ -65,29 +63,21 @@ static int cmd_data_convert(int argc, const char **argv) pr_err("You cannot specify both --to-ctf and --to-json.\n"); return -1; } -#ifdef HAVE_LIBBABELTRACE_SUPPORT if (!to_json && !to_ctf) { pr_err("You must specify one of --to-ctf or --to-json.\n"); return -1; } -#else - if (!to_json) { - pr_err("You must specify --to-json.\n"); - return -1; -} -#endif if (to_json) return bt_convert__perf2json(input_name, to_json, &opts); if (to_ctf) { -#if defined(HAVE_LIBBABELTRACE_SUPPORT) && defined(HAVE_LIBTRACEEVENT) +#if defined(HAVE_BABELTRACE2_CTF_WRITER_SUPPORT) && defined(HAVE_LIBTRACEEVENT) return bt_convert__perf2ctf(input_name, to_ctf, &opts); #else - pr_err("The libbabeltrace support is not compiled in. perf should be " - "compiled with environment variables LIBBABELTRACE=1 and " - "LIBBABELTRACE_DIR=/path/to/libbabeltrace/.\n" - "Check also if libbtraceevent devel files are available.\n"); + pr_err("The babeltrace2 ctf support is not compiled in. Ensure you have both\n" + "libbabeltrace2-dev[el] and libtraceevent-dev[el] installed or set\n" + "PKG_CONFIG_PATH to find a local installation of those libraries.\n"); return -1; #endif } diff --git a/tools/perf/tests/make b/tools/perf/tests/make index dbd7c86a2dcc..d2c2f526e1db 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -91,7 +91,7 @@ make_no_libbpf := NO_LIBBPF=1 make_libbpf_dynamic := LIBBPF_DYNAMIC=1 make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1 make_no_libllvm := NO_LIBLLVM=1 -make_with_babeltrace:= LIBBABELTRACE=1 +make_no_babeltrace2 := NO_BABELTRACE2=1 make_with_coresight := CORESIGHT=1 make_no_sdt := NO_SDT=1 make_no_libpfm4 := NO_LIBPFM4=1 @@ -166,7 +166,7 @@ run += make_no_libbpf_DEBUG run += make_no_libllvm run += make_no_sdt run += make_no_syscall_tbl -run += make_with_babeltrace +run += make_no_babeltrace2 run += make_with_coresight run += make_with_clangllvm run += make_no_libpfm4 diff --git a/tools/perf/tests/shell/test_perf_data_converter_ctf.sh b/tools/perf/tests/shell/test_perf_data_converter_ctf.sh index 334eebc9945e..bc58a8dabf4c 100755 --- a/tools/perf/tests/shell/test_perf_data_converter_ctf.sh +++ b/tools/perf/tests/shell/test_perf_data_converter_ctf.sh @@ -24,11 +24,11 @@ trap_cleanup() } trap trap_cleanup exit term int -check_babeltrace_support() +check_babeltrace2_support() { - if ! perf check feature libbabeltrace + if ! perf check feature babeltrace2-ctf-writer then - echo "perf not linked with libbabeltrace, skipping test" + echo "perf not linked with babeltrace2-ctf-writer, skipping test" exit 2 fi } @@ -97,7 +97,7 @@ test_ctf_converter_pipe() fi } -check_babeltrace_support +check_babeltrace2_support test_ctf_converter_file test_ctf_converter_pipe diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 4bbc78b1f741..b22cdc24082a 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -230,7 +230,7 @@ perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/ ifeq ($(CONFIG_LIBTRACEEVENT),y) - perf-util-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o + perf-util-$(CONFIG_BABELTRACE2_CTF_WRITER) += data-convert-bt.o endif perf-util-y += data-convert-json.o diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index cc51b8677c8e..5ff46bfcd0e1 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -11,14 +11,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "asm/bug.h" #include "data-convert.h" #include "session.h" @@ -121,13 +120,13 @@ static int value_set(struct bt_ctf_field_type *type, } if (sign) { - ret = bt_ctf_field_signed_integer_set_value(field, val); + ret = bt_ctf_field_integer_signed_set_value(field, val); if (ret) { pr_err("failed to set field value %s\n", name); goto err; } } else { - ret = bt_ctf_field_unsigned_integer_set_value(field, val); + ret = bt_ctf_field_integer_unsigned_set_value(field, val); if (ret) { pr_err("failed to set field value %s\n", name); goto err; @@ -374,10 +373,10 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, data + offset + i * len, len); if (!(flags & TEP_FIELD_IS_SIGNED)) - ret = bt_ctf_field_unsigned_integer_set_value( + ret = bt_ctf_field_integer_unsigned_set_value( field, value_int); else - ret = bt_ctf_field_signed_integer_set_value( + ret = bt_ctf_field_integer_signed_set_value( field, adjust_signedness(value_int, len)); } @@ -471,7 +470,7 @@ add_bpf_output_values(struct bt_ctf_event_class *event_class, goto put_len_type; } - ret = bt_ctf_field_unsigned_integer_set_value(len_field, nr_elements); + ret = bt_ctf_field_integer_unsigned_set_value(len_field, nr_elements); if (ret) { pr_err("failed to set field value for raw_len\n"); goto put_len_field; @@ -500,7 +499,7 @@ add_bpf_output_values(struct bt_ctf_event_class *event_class, struct bt_ctf_field *elem_field = bt_ctf_field_sequence_get_field(seq_field, i); - ret = bt_ctf_field_unsigned_integer_set_value(elem_field, + ret = bt_ctf_field_integer_unsigned_set_value(elem_field, ((u32 *)(sample->raw_data))[i]); bt_ctf_field_put(elem_field); @@ -545,7 +544,7 @@ add_callchain_output_values(struct bt_ctf_event_class *event_class, goto put_len_type; } - ret = bt_ctf_field_unsigned_integer_set_value(len_field, nr_elements); + ret = bt_ctf_field_integer_unsigned_set_value(len_field, nr_elements); if (ret) { pr_err("failed to set field value for perf_callchain_size\n"); goto put_len_field; @@ -575,7 +574,7 @@ add_callchain_output_values(struct bt_ctf_event_class *event_class, struct bt_ctf_field *elem_field = bt_ctf_field_sequence_get_field(seq_field, i); - ret = bt_ctf_field_unsigned_integer_set_value(elem_field, + ret = bt_ctf_field_integer_unsigned_set_value(elem_field, ((u64 *)(callchain->ips))[i]); bt_ctf_field_put(elem_field); @@ -728,7 +727,7 @@ static struct ctf_stream *ctf_stream__create(struct ctf_writer *cw, int cpu) goto out; } - ret = bt_ctf_field_unsigned_integer_set_value(cpu_field, (u32) cpu); + ret = bt_ctf_field_integer_unsigned_set_value(cpu_field, (u32) cpu); if (ret) { pr_err("Failed to update CPU number\n"); goto out; diff --git a/tools/perf/util/data-convert.h b/tools/perf/util/data-convert.h index ee651fa680a1..a96240f15671 100644 --- a/tools/perf/util/data-convert.h +++ b/tools/perf/util/data-convert.h @@ -11,10 +11,10 @@ struct perf_data_convert_opts { const char *time_str; }; -#ifdef HAVE_LIBBABELTRACE_SUPPORT +#ifdef HAVE_BABELTRACE2_CTF_WRITER_SUPPORT int bt_convert__perf2ctf(const char *input_name, const char *to_ctf, struct perf_data_convert_opts *opts); -#endif /* HAVE_LIBBABELTRACE_SUPPORT */ +#endif /* HAVE_BABELTRACE2_CTF_WRITER_SUPPORT */ int bt_convert__perf2json(const char *input_name, const char *to_ctf, struct perf_data_convert_opts *opts); From be694c488a1e96e728517b26de9f15fed56b2e74 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 4 Jun 2026 09:36:26 -0700 Subject: [PATCH 2846/5214] perf pmu: Recognize 'default_core' as a core PMU and document matching The is_pmu_core function checks if a PMU name corresponds to a core CPU PMU. However, it currently fails to recognize "default_core" as a core PMU. When "default_core" is used, the PMU scanning fallback in pmus.c scans the "other_pmus" list. This scan is slow and always misses because "default_core" is a core PMU, leading to unnecessary overhead. Update is_pmu_core to recognize "default_core" directly. Additionally, document the different matching approaches (exact name for x86/s390, sysfs-based cpus file check for ARM/hybrid) to clarify how core PMUs are classified. Also, explicitly treat "default_core" as `all_pmus` in `setup_metric_events()` to preserve the original metric resolution behavior for this pseudo-PMU. Assisted-by: Gemini-CLI:Google Gemini 3.1 Pro Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/metricgroup.c | 3 ++- tools/perf/util/pmu.c | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 5a489e97c413..c2ce3e53aaee 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -295,7 +295,8 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids, const char *metric_id; struct evsel *ev; size_t ids_size, matched_events, i; - bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu); + bool all_pmus = !strcmp(pmu, "all") || !strcmp(pmu, "default_core") || + perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu); *out_metric_events = NULL; ids_size = hashmap__size(ids); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 23337d2fa281..9994709ef12b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -2029,9 +2029,26 @@ int perf_pmu__for_each_format(struct perf_pmu *pmu, void *state, pmu_format_call return 0; } +/** + * is_pmu_core() - Check if the given PMU name corresponds to a core CPU PMU. + * @name: The PMU name to check. + * + * Core PMUs can be identified by: + * 1. Exact name match: + * - "cpu": Typically used on x86 architectures. + * - "cpum_cf": Typically used on s390 architectures (CPU Measurement Counter Facility). + * - "default_core": A generic name used to refer to the default core PMU. + * 2. Sysfs file existence check (is_sysfs_pmu_core): + * - Typically used on ARM systems or Intel hybrid architectures (e.g., "cpu_atom", + * "cpu_core"). This approach checks if the sysfs directory for the PMU + * contains a "cpus" file. + */ bool is_pmu_core(const char *name) { - return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name); + return !strcmp(name, "cpu") || + !strcmp(name, "cpum_cf") || + !strcmp(name, "default_core") || + is_sysfs_pmu_core(name); } bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu) From 824b18f607d82503a956d3e00f9e9c0b24efcbca Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 31 May 2026 01:29:40 +0530 Subject: [PATCH 2847/5214] perf lock contention: Enable end-timestamp accounting for cgroup aggregation update_lock_stat() handles lock contentions that start but never reach a contention_end event (e.g., locks still held when profiling stops), but previously treated LOCK_AGGR_CGROUP as a no-op due to missing cgroup context in userspace. Fix this by adding a cgroup_id field to struct tstamp_data, recording it at contention_begin using get_current_cgroup_id() when aggr_mode is LOCK_AGGR_CGROUP. Capturing it at contention_begin is semantically correct, the contention cost is incurred by the task that had to wait, not by whatever task happens to be running at contention_end. It is also preferable from a performance standpoint, as contention_end runs just before the task enters the critical section. Update contention_end to use pelem->cgroup_id instead of calling get_current_cgroup_id() dynamically, ensuring both complete and incomplete contention events attribute the wait time to the cgroup at wait-start time consistently. Reviewed-by: Namhyung Kim Signed-off-by: Suchit Karunakaran Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Cc: Tycho Andersen (AMD) Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_lock_contention.c | 4 ++-- tools/perf/util/bpf_skel/lock_contention.bpf.c | 4 +++- tools/perf/util/bpf_skel/lock_data.h | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index eb8e29b8064b..b1cfa63a488f 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -470,8 +470,8 @@ static void update_lock_stat(int map_fd, int pid, u64 end_ts, stat_key.lock_addr_or_cgroup = ts_data->lock; break; case LOCK_AGGR_CGROUP: - /* TODO */ - return; + stat_key.lock_addr_or_cgroup = ts_data->cgroup_id; + break; default: return; } diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index d4186ae9f85c..0d9c6f55050e 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -597,6 +597,8 @@ int contention_begin(u64 *ctx) pelem->timestamp = bpf_ktime_get_ns(); pelem->lock = (__u64)ctx[0]; pelem->flags = (__u32)ctx[1]; + if (aggr_mode == LOCK_AGGR_CGROUP) + pelem->cgroup_id = get_current_cgroup_id(); if (needs_callstack) { u32 i = 0; @@ -832,7 +834,7 @@ int contention_end(u64 *ctx) key.stack_id = pelem->stack_id; break; case LOCK_AGGR_CGROUP: - key.lock_addr_or_cgroup = get_current_cgroup_id(); + key.lock_addr_or_cgroup = pelem->cgroup_id; break; default: /* should not happen */ diff --git a/tools/perf/util/bpf_skel/lock_data.h b/tools/perf/util/bpf_skel/lock_data.h index 28c5e5aced7f..652e114e6b87 100644 --- a/tools/perf/util/bpf_skel/lock_data.h +++ b/tools/perf/util/bpf_skel/lock_data.h @@ -13,6 +13,7 @@ struct owner_tracing_data { struct tstamp_data { u64 timestamp; u64 lock; + u64 cgroup_id; u32 flags; s32 stack_id; }; From a5498ccf8079fc91c938f122ff9697b0c526b2fd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:06 -0300 Subject: [PATCH 2848/5214] perf tools: Guard test_bit from out-of-bounds sample CPU When PERF_SAMPLE_CPU is absent from a perf.data file, sample->cpu is initialized to (u32)-1 by evsel__parse_sample(). Five call sites pass this value directly to test_bit(sample->cpu, cpu_bitmap), reading massively out of bounds past the DECLARE_BITMAP(..., MAX_NR_CPUS) allocation of 4096 bits. Add a sample->cpu >= MAX_NR_CPUS guard before each test_bit() call, matching the existing safe pattern in builtin-kwork.c. This catches both the (u32)-1 sentinel and any corrupted CPU value exceeding the bitmap size. Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") Cc: Anton Blanchard Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 3 ++- tools/perf/builtin-diff.c | 3 ++- tools/perf/builtin-report.c | 3 ++- tools/perf/builtin-sched.c | 6 ++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b918f9eed5fd..8a0eb30eac24 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -295,7 +295,8 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; } - if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) + if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, ann->cpu_bitmap))) goto out_put; if (!al.filtered && diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 9592f44b6545..9fa8e900637b 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -416,7 +416,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, goto out; } - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) { ret = 0; goto out; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6f044c3df893..dd1309c32094 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -298,7 +298,8 @@ static int process_sample_event(const struct perf_tool *tool, if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; - if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) + if (rep->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, rep->cpu_bitmap))) goto out_put; if (sort__mode == SORT_MODE__BRANCH) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 4de2baf03c50..e7bd3f331cb8 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2192,7 +2192,8 @@ static void timehist_print_sample(struct perf_sched *sched, char nstr[30]; u64 wait_time; - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) return; timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); @@ -2871,7 +2872,8 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (!sched->idle_hist || thread__tid(thread) == 0) { - if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) + if (!cpu_list || (sample->cpu < MAX_NR_CPUS && + test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); if (sched->idle_hist) { From 66ea9de60396a4dea5276bc87025884691876c36 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:29 -0300 Subject: [PATCH 2849/5214] perf sched: Fix thread reference leak in latency_switch_event In latency_switch_event(), after acquiring thread references for sched_out and sched_in via machine__findnew_thread(), the first add_sched_out_event() failure path does 'return -1', bypassing the out_put label that calls thread__put() on both references. The second and third add_sched_out_event() failures correctly use 'goto out_put'. Fix the first one to match. Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index e7bd3f331cb8..13b801496a01 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1180,7 +1180,7 @@ static int latency_switch_event(struct perf_sched *sched, } } if (add_sched_out_event(out_events, prev_state, timestamp)) - return -1; + goto out_put; in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); if (!in_events) { From 8cbca8a480e15f6326ce94287570993f27a4b2d5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:56:02 -0300 Subject: [PATCH 2850/5214] perf sched: Fix NULL dereference in latency_runtime_event latency_runtime_event() passes the return value of machine__findnew_thread() directly to thread_atoms_search() at line 1216, before checking for NULL at line 1220. thread_atoms_search() calls pid_cmp() which dereferences the thread pointer via thread__tid(), causing a NULL pointer dereference if the allocation fails. All other callers of thread_atoms_search() in this file (latency_switch_event, latency_wakeup_event, latency_migrate_task_event) correctly check for NULL first. Move the atoms assignment after the NULL check to match the pattern used by the other callers. Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 13b801496a01..36da451447b5 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1213,13 +1213,15 @@ static int latency_runtime_event(struct perf_sched *sched, const u32 pid = perf_sample__intval(sample, "pid"); const u64 runtime = perf_sample__intval(sample, "runtime"); struct thread *thread = machine__findnew_thread(machine, -1, pid); - struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); + struct work_atoms *atoms; u64 timestamp = sample->time; int cpu = sample->cpu, err = -1; if (thread == NULL) return -1; + atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); + /* perf.data is untrusted input — CPU may be absent or corrupted */ if (cpu >= MAX_CPUS || cpu < 0) { pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", From c8b04142078a9ccb9e402ab7c38de7123256f4a5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 13:05:10 -0300 Subject: [PATCH 2851/5214] perf sched: Fix comp_cpus heap overflow with cross-machine recordings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup_map_cpus() allocates comp_cpus based on sysconf(_SC_NPROCESSORS_CONF), the host machine's CPU count. But map_switch_event() indexes comp_cpus using cpus_nr derived from bitmap_weight(comp_cpus_mask, MAX_CPUS), where comp_cpus_mask is declared as DECLARE_BITMAP(..., MAX_CPUS) with MAX_CPUS=4096. When analyzing a perf.data recording from a machine with more CPUs than the analysis host (e.g. 128-CPU server recording analyzed on an 8-CPU laptop), cpus_nr exceeds the allocation size, causing a heap buffer overflow. Also fix a type mismatch: comp_cpus is 'struct perf_cpu *' (2 bytes per element) but was allocated with sizeof(int) (4 bytes per element). Allocate comp_cpus with MAX_CPUS entries using the correct element size, matching the comp_cpus_mask bitmap bounds. Remove the sysconf(_SC_NPROCESSORS_CONF) initialization of max_cpu — its only consumer was the comp_cpus allocation, and max_cpu is dynamically updated from the recording's events during processing. Fix the non-compact path to use max_cpu.cpu + 1 as cpus_nr, converting from 0-based index to count — sysconf() returned a count which masked this off-by-one. Fixes: 99623c628f54 ("perf sched: Add compact display option") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 36da451447b5..4aa7833cae6e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1670,7 +1670,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_sample *sampl new_cpu = true; } } else - cpus_nr = sched->max_cpu.cpu; + cpus_nr = sched->max_cpu.cpu + 1; timestamp0 = sched->cpu_last_switched[this_cpu.cpu]; sched->cpu_last_switched[this_cpu.cpu] = timestamp; @@ -3573,10 +3573,8 @@ static int perf_sched__lat(struct perf_sched *sched) static int setup_map_cpus(struct perf_sched *sched) { - sched->max_cpu.cpu = sysconf(_SC_NPROCESSORS_CONF); - if (sched->map.comp) { - sched->map.comp_cpus = calloc(sched->max_cpu.cpu, sizeof(int)); + sched->map.comp_cpus = calloc(MAX_CPUS, sizeof(*sched->map.comp_cpus)); if (!sched->map.comp_cpus) return -1; } From 6414f790f21d2ba648d4d2a713d61f9014123fcf Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Wed, 20 May 2026 17:17:12 -0400 Subject: [PATCH 2852/5214] MAINTAINERS: add more files to PAGE CACHE section Add include/linux/writeback.h and include/trace/events/{filemap.h,readahead.h,writeback.h}. Link: https://lore.kernel.org/20260520-page-cache-maintainers-v1-1-f93438d2186d@columbia.edu Signed-off-by: Tal Zussman Cc: Jan Kara Cc: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- MAINTAINERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 782ed63e4e67..1e94e8cc6ad1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20174,6 +20174,10 @@ T: git git://git.infradead.org/users/willy/pagecache.git F: Documentation/filesystems/locking.rst F: Documentation/filesystems/vfs.rst F: include/linux/pagemap.h +F: include/linux/writeback.h +F: include/trace/events/filemap.h +F: include/trace/events/readahead.h +F: include/trace/events/writeback.h F: mm/filemap.c F: mm/page-writeback.c F: mm/readahead.c From 4c0ed883e0516aee79496b6277cbea63a08b2676 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Wed, 20 May 2026 12:22:28 +0000 Subject: [PATCH 2853/5214] mm/page_alloc: fix defrag_mode for non-reclaimable allocations When defrag_mode is enabled, ALLOC_NOFRAGMENT is enforced to prevent migratetype fallbacks and keep pageblocks clean. The allocator relies on reclaim and compaction to free pages of the correct type before allowing fallback as a last resort. However, non-reclaimable allocations such as GFP_ATOMIC cannot invoke direct reclaim or compaction. With defrag_mode=1, these allocations hit the !can_direct_reclaim bailout in __alloc_pages_slowpath() with ALLOC_NOFRAGMENT still set, and fail without ever attempting a fallback. This causes a large number of SLUB allocation failures for skbuff_head_cache under network-heavy workloads, despite free memory being available in other migratetype freelists. We observed it on a few of the Meta workloads that adopted defrag_mode=1. For the service under load there were 85509 SLUB allocation failures messages in dmesg within 2 hours. All of them are GFP_ATOMIC allocations for skbuff_head_cache, despite free pages being available in other migratetype freelists (~13 GB free). Since it is networking path from the practical point of view, this means dropped packets, failed RPC requests, tail latency spikes and overall service degradation. Clear ALLOC_NOFRAGMENT and retry for allocations that request kswapd reclaim but cannot do direct reclaim themselves (GFP_ATOMIC). Purely speculative allocations like GFP_TRANSHUGE_LIGHT that don't set __GFP_KSWAPD_RECLAIM are left to fail, since they have reasonable fallbacks and should not cause fragmentation. Link: https://lore.kernel.org/20260520122228.201550-1-d@ilvokhin.com Fixes: e3aa7df331bc ("mm: page_alloc: defrag_mode") Signed-off-by: Dmitry Ilvokhin Acked-by: Johannes Weiner Acked-by: Vlastimil Babka (SUSE) Cc: Brendan Jackman Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0ebffb0bb98b..7e3c79e79e5b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4853,8 +4853,19 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, } /* Caller is not willing to reclaim, we can't balance anything */ - if (!can_direct_reclaim) + if (!can_direct_reclaim) { + /* + * Reclaim/compaction cannot run, so defrag_mode's strategy + * of enforcing ALLOC_NOFRAGMENT cannot be fulfilled. Allow + * fallbacks rather than failing the allocation outright. + */ + if (defrag_mode && (alloc_flags & ALLOC_NOFRAGMENT) && + (gfp_mask & __GFP_KSWAPD_RECLAIM)) { + alloc_flags &= ~ALLOC_NOFRAGMENT; + goto retry; + } goto nopage; + } /* Avoid recursion of direct reclaim */ if (current->flags & PF_MEMALLOC) From df0d6a6d4b33b4d9468538954bd2fc2a69b40ea3 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Wed, 20 May 2026 02:03:36 +0000 Subject: [PATCH 2854/5214] selftests/mm/split_huge_page_test.c: close fd on write error When create_pagecache_thp_and_fd() write returns error on /proc/sys/vm/dropcache, it just "goto err_out_unlink", which left fd still open. Use "goto err_out_close" to close the fd. Link: https://lore.kernel.org/20260520020336.28914-1-richard.weiyang@gmail.com Signed-off-by: Wei Yang Reviewed-by: Dev Jain Reviewed-by: SeongJae Park Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Lance Yang Cc: "Liam R. Howlett" Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Wei Yang Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/split_huge_page_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index a8725942ee51..40a5093917e7 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -625,7 +625,7 @@ static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, } if (write(*fd, "3", 1) != 1) { ksft_perror("write to drop_caches"); - goto err_out_unlink; + goto err_out_close; } close(*fd); From 17986198a7b99485d7b2bc4eb8d700fbf8c8629e Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Tue, 2 Jun 2026 12:06:25 +0100 Subject: [PATCH 2855/5214] drivers/char/mem: eliminate unnecessary use of success_hook Patch series "remove mmap_action success, error hooks", v3. The mmap_action->success_hook was a strange beast added to enable code which appeared to absolutely require access to a VMA pointer to work correctly. Primarily this was for hugetlb, however a different approach will be taken there, as clearly more work is required to figure out a sensible way of converting hugetlb to use mmap_prepare. The other user was the memory char driver, specifically /dev/zero which has the unusual property of explicitly setting file-backed VMAs anonymous. Providing the success hook was always foolish, as it allowed drivers a way to workaround the restriction that they should not access a pointer to a not-yet-correctly-initialised VMA - which defeats the purpose of the mmap_prepare work. We can achieve the same thing in memory char driver without needing the success hook, so this series removes that, then removes the success hook altogether. The error hook is also unnecessary - the motivation for this was for functions which need to override the error code when performing an mmap action in order to avoid breaking userspace. We can achieve this by just providing a field for the error code. Doing this means we don't have to worry about the hook doing anything odd. We also add a check to ensure the error code is in fact valid. Again the memory char driver is the only current user of this, so this series updates it to use that. After this change mmap_action has no custom hooks at all, which seems rather more cromulent than before. This patch (of 3): /dev/zero, uniquely, marks memory mapped there as anonymous. This is currently achieved using the mmap_action->success_hook. However this hook circumvents the abstraction of VMA initialisation so it's preferable to do things a different way. To achieve this, this patch firstly defaults the VMA descriptor's vm_ops field to the dummy VMA operations, which is what file-backed VMAs default this field to. That way, we can detect whether a driver sets this field to NULL in order to mark it anonymous. We then introduce vma_desc_set_anonymous() to do this explicitly, and invoke it in mmap_zero_prepare(). This way, any driver which does not explicitly set desc->vm_ops, retains the dummy vm_ops as they would previously. We also update set_vma_user_defined_fields() to make clear that we are either setting vma->vm_ops to what is provided by the driver (or defaulting to dummy_vm_ops if not set), or setting the VMA anonymous. This lays the groundwork for removing the success hook. Link: https://lore.kernel.org/cover.1780397980.git.ljs@kernel.org Link: https://lore.kernel.org/010579cca6787cf7bb057ab1f7228978b10601c8.1780397980.git.ljs@kernel.org Signed-off-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Jann Horn Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- drivers/char/mem.c | 17 +++++------------ include/linux/mm.h | 5 +++++ mm/util.c | 1 + mm/vma.c | 3 +++ tools/testing/vma/include/dup.h | 1 + 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 5fd421e48c04..a4297eb39887 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -504,17 +504,6 @@ static ssize_t read_zero(struct file *file, char __user *buf, return cleared; } -static int mmap_zero_private_success(const struct vm_area_struct *vma) -{ - /* - * This is a highly unique situation where we mark a MAP_PRIVATE mapping - * of /dev/zero anonymous, despite it not being. - */ - vma_set_anonymous((struct vm_area_struct *)vma); - - return 0; -} - static int mmap_zero_prepare(struct vm_area_desc *desc) { #ifndef CONFIG_MMU @@ -523,7 +512,11 @@ static int mmap_zero_prepare(struct vm_area_desc *desc) if (vma_desc_test(desc, VMA_SHARED_BIT)) return shmem_zero_setup_desc(desc); - desc->action.success_hook = mmap_zero_private_success; + /* + * This is a highly unique situation where we mark a MAP_PRIVATE mapping + * of /dev/zero anonymous, despite it not being. + */ + vma_desc_set_anonymous(desc); return 0; } diff --git a/include/linux/mm.h b/include/linux/mm.h index 11f440e9d7cd..0f2612a70fb1 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1489,6 +1489,11 @@ static inline void vma_set_anonymous(struct vm_area_struct *vma) vma->vm_ops = NULL; } +static inline void vma_desc_set_anonymous(struct vm_area_desc *desc) +{ + desc->vm_ops = NULL; +} + static inline bool vma_is_anonymous(struct vm_area_struct *vma) { return !vma->vm_ops; diff --git a/mm/util.c b/mm/util.c index 3cc949a0b7ed..2b2a9df689d7 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1192,6 +1192,7 @@ void compat_set_desc_from_vma(struct vm_area_desc *desc, desc->vm_file = vma->vm_file; desc->vma_flags = vma->flags; desc->page_prot = vma->vm_page_prot; + desc->vm_ops = vma->vm_ops; /* Default. */ desc->action.type = MMAP_NOTHING; diff --git a/mm/vma.c b/mm/vma.c index d90791b00a7b..9eea2850818a 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -2697,6 +2697,8 @@ static void set_vma_user_defined_fields(struct vm_area_struct *vma, { if (map->vm_ops) vma->vm_ops = map->vm_ops; + else /* Only /dev/zero should do this. */ + vma_set_anonymous(vma); vma->vm_private_data = map->vm_private_data; } @@ -2744,6 +2746,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, .action = { .type = MMAP_NOTHING, /* Default to no further action. */ }, + .vm_ops = &vma_dummy_vm_ops, }; bool allocated_new = false; int error; diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index 9e0dfd3a85b0..306171d061e7 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -1303,6 +1303,7 @@ static inline void compat_set_desc_from_vma(struct vm_area_desc *desc, desc->vm_file = vma->vm_file; desc->vma_flags = vma->flags; desc->page_prot = vma->vm_page_prot; + desc->vm_ops = vma->vm_ops; /* Default. */ desc->action.type = MMAP_NOTHING; From 8876dc0780f23eb499b42cc84df2dd795aada6be Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Tue, 2 Jun 2026 12:06:26 +0100 Subject: [PATCH 2856/5214] mm/vma: remove mmap_action->success_hook This hook was introduced to work around code that seemed to absolutely require access to a VMA pointer upon mmap(). However, providing this hook leaves a backdoor to drivers getting access to the very thing mmap_prepare eliminates - a pointer to the VMA. Let's solve this contradiction by removing it. The key intended user was hugetlb, however it seems that the best course now is to avoid allowing all drivers the ability to work around mmap_prepare, and find a different solution there. Link: https://lore.kernel.org/f79434e6d30af6d92999be6b76e197f1847105fa.1780397980.git.ljs@kernel.org Signed-off-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Jann Horn Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/mm_types.h | 10 ---------- mm/util.c | 2 -- tools/testing/vma/include/dup.h | 10 ---------- 3 files changed, 22 deletions(-) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index a308e2c23b82..945c0a5386d6 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -843,16 +843,6 @@ struct mmap_action { }; enum mmap_action_type type; - /* - * If specified, this hook is invoked after the selected action has been - * successfully completed. Note that the VMA write lock still held. - * - * The absolute minimum ought to be done here. - * - * Returns 0 on success, or an error code. - */ - int (*success_hook)(const struct vm_area_struct *vma); - /* * If specified, this hook is invoked when an error occurred when * attempting the selected action. diff --git a/mm/util.c b/mm/util.c index 2b2a9df689d7..4e172990afcd 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1397,8 +1397,6 @@ static int mmap_action_finish(struct vm_area_struct *vma, if (!err) err = call_vma_mapped(vma); - if (!err && action->success_hook) - err = action->success_hook(vma); /* do_munmap() might take rmap lock, so release if held. */ maybe_rmap_unlock_action(vma, action); diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index 306171d061e7..fddfd1b57c09 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -482,16 +482,6 @@ struct mmap_action { }; enum mmap_action_type type; - /* - * If specified, this hook is invoked after the selected action has been - * successfully completed. Note that the VMA write lock still held. - * - * The absolute minimum ought to be done here. - * - * Returns 0 on success, or an error code. - */ - int (*success_hook)(const struct vm_area_struct *vma); - /* * If specified, this hook is invoked when an error occurred when * attempting the selection action. From 4f5b8759262e5e65373638346307836de1290b22 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Tue, 2 Jun 2026 12:06:27 +0100 Subject: [PATCH 2857/5214] mm/vma: eliminate mmap_action->error_hook, introduce error_override Rather than providing a hook, simplify things by providing the ability to override mmap action errors. This allows us to more carefully validate the value provided and thus ensure only a valid error code is specified, and simplifies the interface. This way, we eliminate all hooks but mmap_prepare and allow only mmap actions to be specified (which core mm controls). This significantly improves robustness and eliminates any unnecessary code duplication in driver mmap hooks. We also update the /dev/mem logic (the only user) to use mmap_action->error_override instead. Link: https://lore.kernel.org/55d13f7d016b827c459946d46a56105635be111c.1780397980.git.ljs@kernel.org Signed-off-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Jann Horn Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- drivers/char/mem.c | 8 +------- include/linux/mm_types.h | 9 +++------ mm/util.c | 29 +++++++++++++++++++++-------- tools/testing/vma/include/dup.h | 9 +++------ 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index a4297eb39887..63253d1de5d7 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -322,11 +322,6 @@ static const struct vm_operations_struct mmap_mem_ops = { #endif }; -static int mmap_filter_error(int err) -{ - return -EAGAIN; -} - static int mmap_mem_prepare(struct vm_area_desc *desc) { struct file *file = desc->file; @@ -362,8 +357,7 @@ static int mmap_mem_prepare(struct vm_area_desc *desc) /* Remap-pfn-range will mark the range with the I/O flag. */ mmap_action_remap_full(desc, desc->pgoff); - /* We filter remap errors to -EAGAIN. */ - desc->action.error_hook = mmap_filter_error; + desc->action.error_override = -EAGAIN; return 0; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 945c0a5386d6..5ef78617ce93 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -844,13 +844,10 @@ struct mmap_action { enum mmap_action_type type; /* - * If specified, this hook is invoked when an error occurred when - * attempting the selected action. - * - * The hook can return an error code in order to filter the error, but - * it is not valid to clear the error here. + * If non-zero, replace errors that arise from mmap actions with this + * value instead. Only valid error codes may be specified. */ - int (*error_hook)(int err); + int error_override; /* * This should be set in rare instances where the operation required diff --git a/mm/util.c b/mm/util.c index 4e172990afcd..af2c2103f0d9 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1414,16 +1414,22 @@ static int mmap_action_finish(struct vm_area_struct *vma, */ len = vma_pages(vma) << PAGE_SHIFT; do_munmap(current->mm, vma->vm_start, len, NULL); - if (action->error_hook) { - /* We may want to filter the error. */ - err = action->error_hook(err); - /* The caller should not clear the error. */ - VM_WARN_ON_ONCE(!err); - } - return err; + + return action->error_override ?: err; } #ifdef CONFIG_MMU + +static int check_mmap_action(struct mmap_action *action) +{ + const unsigned long override = action->error_override; + + if (WARN_ON_ONCE(override && !IS_ERR_VALUE(override))) + return -EINVAL; + + return 0; +} + /** * mmap_action_prepare - Perform preparatory setup for an VMA descriptor * action which need to be performed. @@ -1433,7 +1439,14 @@ static int mmap_action_finish(struct vm_area_struct *vma, */ int mmap_action_prepare(struct vm_area_desc *desc) { - switch (desc->action.type) { + struct mmap_action *action = &desc->action; + int err; + + err = check_mmap_action(action); + if (err) + return err; + + switch (action->type) { case MMAP_NOTHING: return 0; case MMAP_REMAP_PFN: diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index fddfd1b57c09..bf26b3f48d3a 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -483,13 +483,10 @@ struct mmap_action { enum mmap_action_type type; /* - * If specified, this hook is invoked when an error occurred when - * attempting the selection action. - * - * The hook can return an error code in order to filter the error, but - * it is not valid to clear the error here. + * If non-zero, replace errors that arise from mmap actions with this + * value instead. Only valid error codes may be specified. */ - int (*error_hook)(int err); + int error_override; /* * This should be set in rare instances where the operation required From ce71e5aa8dc83e703a5301644cef57dfc3caaf44 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:12 -0700 Subject: [PATCH 2858/5214] mm/damon/core: safely handle no region case in damon_set_regions() Patch series "mm/damon: minor improvements for code readability and tests". Implement minor improvements on code readability and tests for DAMON. First seven patches are for DAMON code readability and resulting maintenance. Patches 1 and 2 make damon_set_regions() safer and easier to read. Patches 3 and 4 remove fragmented DAMON API use cases. Patches 5-7 hides unused core functions that are unnecessarily exposed to API callers. The following seven patches are for DAMON tests improvement. Patches 8 and 9 adds and removes DAMON_DEBUG_SANITY verifications to ensure reasonable test coverage without too high overhead. Patch 10 adds a new kunit test for damon_set_regions(). Patch 11 makes sysfs.py selftest more gracefully finishes under test failures. Patches 12-13 adds simple sysfs.sh test cases for the monitoring intervals goal directory, the addr_unit file and the pause file. This patch (of 14): damon_set_regions() calls damon_first_region() regardless of the number of DAMON regions in a given DAMON target. damon_first_region() internally uses list_first_entry(), which clearly documents the list is expected to be not empty. Due to the internal implementation of the macro, damon_set_regions() is safe for now. But the internal implementation of the macro can be changed in future. Refactor the function to explicitly and safely handle the empty region list case without depending on the internal implementation. No behavioral change is intended. Link: https://lore.kernel.org/20260522154026.80546-1-sj@kernel.org Link: https://lore.kernel.org/20260522154026.80546-2-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index 68b3b4bbc8fc..8360cb4c506e 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -356,6 +356,19 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, damon_destroy_region(r, t); } + if (!damon_nr_regions(t)) { + for (i = 0; i < nr_ranges; i++) { + r = damon_new_region( + ALIGN_DOWN(ranges[i].start, + min_region_sz), + ALIGN(ranges[i].end, min_region_sz)); + if (!r) + return -ENOMEM; + damon_add_region(r, t); + } + return 0; + } + r = damon_first_region(t); /* Add new regions or resize existing regions to fit in the ranges */ for (i = 0; i < nr_ranges; i++) { From b23dbda659b645482e3234ad10773d991a288e2f Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:13 -0700 Subject: [PATCH 2859/5214] mm/damon/core: do not use region out of a loop in damon_set_regions() damon_set_regions() assumes the DAMON region iterator is referencing the last region after the region iteration loop is completed. The code is indeed implemented in the way, but that is not a documented safe behavior. Hence it is unreliable and difficult to read. Cleanup the code to avoid the case. No behavioral change is intended. Link: https://lore.kernel.org/20260522154026.80546-3-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 8360cb4c506e..c9946ac8e279 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -374,6 +374,7 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, for (i = 0; i < nr_ranges; i++) { struct damon_region *first = NULL, *last, *newr; struct damon_addr_range *range; + bool insert_before_r = false; range = &ranges[i]; /* Get the first/last regions intersecting with the range */ @@ -383,8 +384,10 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, first = r; last = r; } - if (r->ar.start >= range->end) + if (r->ar.start >= range->end) { + insert_before_r = true; break; + } } if (!first) { /* no region intersects with this range */ @@ -394,7 +397,11 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, ALIGN(range->end, min_region_sz)); if (!newr) return -ENOMEM; - damon_insert_region(newr, damon_prev_region(r), r, t); + if (insert_before_r) + damon_insert_region(newr, damon_prev_region(r), + r, t); + else + damon_add_region(newr, t); } else { /* resize intersecting regions to fit in this range */ first->ar.start = ALIGN_DOWN(range->start, From cd036cc8c384b8593b5020a82b4b73ee3d10e22c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:14 -0700 Subject: [PATCH 2860/5214] samples/damon/mtier: replace damon_add_region() with damon_set_regions() mtier DAMON sample module and DAMON virtual address operation set (vaddr) unit tests are using damon_add_region() for setup of DAMON monitoring target region boundaries setup. But, damon_set_regions() is designed for exactly the purpose. All other DAMON API callers use the function for the purpose. Replace damon_add_region() usage in mtier sample module with damon_set_regions(), for unifying the use case and reducing the maintenance cost. Link: https://lore.kernel.org/20260522154026.80546-4-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- samples/damon/mtier.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index 775838a23d93..eb1143de8df1 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -75,11 +75,11 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) struct damon_ctx *ctx; struct damon_attrs attrs; struct damon_target *target; - struct damon_region *region; struct damos *scheme; struct damos_quota_goal *quota_goal; struct damos_filter *filter; struct region_range addr; + struct damon_addr_range range; int ret; ctx = damon_new_ctx(); @@ -120,10 +120,12 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) addr.end = promote ? node1_end_addr : node0_end_addr; } - region = damon_new_region(addr.start, addr.end); - if (!region) + range.start = addr.start; + range.end = addr.end; + + ret = damon_set_regions(target, &range, 1, DAMON_MIN_REGION_SZ); + if (ret) goto free_out; - damon_add_region(region, target); scheme = damon_new_scheme( /* access pattern */ From 9ace949ad8f58f7eb175b88cc20a1d1c11a2d40f Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:15 -0700 Subject: [PATCH 2861/5214] mm/damon/tests/vaddr-kunit: replace damon_add_region() with damon_set_regions() DAMON virtual address operation set (vaddr) unit tests is using damon_add_region() for setup of DAMON monitoring target region boundaries setup. But, damon_set_regions() is designed for exactly the purpose. All other DAMON API callers use the function for the purpose. Replace damon_add_region() usage in the unit tests with damon_set_regions(), for unifying the use case and reducing the maintenance cost. Link: https://lore.kernel.org/20260522154026.80546-5-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/tests/vaddr-kunit.h | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/mm/damon/tests/vaddr-kunit.h b/mm/damon/tests/vaddr-kunit.h index 98e734d77d51..563fbc7e3f44 100644 --- a/mm/damon/tests/vaddr-kunit.h +++ b/mm/damon/tests/vaddr-kunit.h @@ -132,22 +132,35 @@ static void damon_do_test_apply_three_regions(struct kunit *test, unsigned long *expected, int nr_expected) { struct damon_target *t; + struct damon_addr_range *ranges; struct damon_region *r; int i; t = damon_new_target(); if (!t) kunit_skip(test, "target alloc fail"); - for (i = 0; i < nr_regions / 2; i++) { - r = damon_new_region(regions[i * 2], regions[i * 2 + 1]); - if (!r) { - damon_destroy_target(t, NULL); - kunit_skip(test, "region alloc fail"); - } - damon_add_region(r, t); - } - damon_set_regions(t, three_regions, 3, DAMON_MIN_REGION_SZ); + ranges = kmalloc_array(nr_regions / 2, sizeof(*ranges), GFP_KERNEL); + if (!ranges) { + damon_destroy_target(t, NULL); + kunit_skip(test, "ranges alloc fail"); + } + for (i = 0; i < nr_regions / 2; i++) { + ranges[i].start = regions[i * 2]; + ranges[i].end = regions[i * 2 + 1]; + } + if (damon_set_regions(t, ranges, nr_regions / 2, + DAMON_MIN_REGION_SZ)) { + kfree(ranges); + damon_destroy_target(t, NULL); + kunit_skip(test, "damon_set_regions() fail"); + } + kfree(ranges); + + if (damon_set_regions(t, three_regions, 3, DAMON_MIN_REGION_SZ)) { + damon_destroy_target(t, NULL); + kunit_skip(test, "second damon_set_regions() fail"); + } for (i = 0; i < nr_expected / 2; i++) { r = __nth_region_of(t, i); From 9cf7ef2d6665dff35b3b522c84509c2d256bf3aa Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:16 -0700 Subject: [PATCH 2862/5214] mm/damon/core: hide damon_add_region() damon_add_region() is being used by only DAMON core, but exposed to DAMON API callers. Exposing something that is not really being used by others will only increase the maintenance cost. Hide it. Link: https://lore.kernel.org/20260522154026.80546-6-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- include/linux/damon.h | 1 - mm/damon/core.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 4014fd0d463c..b9370c1779cb 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1024,7 +1024,6 @@ static inline void damon_insert_region(struct damon_region *r, t->nr_regions++; } -void damon_add_region(struct damon_region *r, struct damon_target *t); void damon_destroy_region(struct damon_region *r, struct damon_target *t); int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, unsigned int nr_ranges, unsigned long min_region_sz); diff --git a/mm/damon/core.c b/mm/damon/core.c index c9946ac8e279..1dd900814ae8 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -250,7 +250,7 @@ struct damon_region *damon_new_region(unsigned long start, unsigned long end) return region; } -void damon_add_region(struct damon_region *r, struct damon_target *t) +static void damon_add_region(struct damon_region *r, struct damon_target *t) { list_add_tail(&r->list, &t->regions_list); t->nr_regions++; From 26d6f6960ff91ebb267cd80efe7772c6427b4cc1 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:17 -0700 Subject: [PATCH 2863/5214] mm/damon/core: hide damon_insert_region() damon_insert_region() is being used by only DAMON core, but exposed to DAMON API callers. Exposing something that is not really being used by others will only increase the maintenance cost. Hide it. Link: https://lore.kernel.org/20260522154026.80546-7-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- include/linux/damon.h | 11 ----------- mm/damon/core.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index b9370c1779cb..3acca7deb169 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1013,17 +1013,6 @@ void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe); struct damon_region *damon_new_region(unsigned long start, unsigned long end); -/* - * Add a region between two other regions - */ -static inline void damon_insert_region(struct damon_region *r, - struct damon_region *prev, struct damon_region *next, - struct damon_target *t) -{ - __list_add(&r->list, &prev->list, &next->list); - t->nr_regions++; -} - void damon_destroy_region(struct damon_region *r, struct damon_target *t); int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, unsigned int nr_ranges, unsigned long min_region_sz); diff --git a/mm/damon/core.c b/mm/damon/core.c index 1dd900814ae8..d1e7b441f2bf 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -256,6 +256,17 @@ static void damon_add_region(struct damon_region *r, struct damon_target *t) t->nr_regions++; } +/* + * Add a region between two other regions + */ +static inline void damon_insert_region(struct damon_region *r, + struct damon_region *prev, struct damon_region *next, + struct damon_target *t) +{ + __list_add(&r->list, &prev->list, &next->list); + t->nr_regions++; +} + #ifdef CONFIG_DAMON_DEBUG_SANITY static void damon_verify_del_region(struct damon_target *t) { From 50d2dec8af1a09056b6b29b54a30e32281b30e2c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:18 -0700 Subject: [PATCH 2864/5214] mm/damon/core: hide damon_destroy_region() damon_destroy_region() is being used by only DAMON core, but exposed to DAMON API callers. Exposing something that is not really being used by others will only increase the maintenance cost. Hide it. Link: https://lore.kernel.org/20260522154026.80546-8-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- include/linux/damon.h | 1 - mm/damon/core.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 3acca7deb169..638ee65f88dc 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1013,7 +1013,6 @@ void damon_add_probe(struct damon_ctx *ctx, struct damon_probe *probe); struct damon_region *damon_new_region(unsigned long start, unsigned long end); -void damon_destroy_region(struct damon_region *r, struct damon_target *t); int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, unsigned int nr_ranges, unsigned long min_region_sz); void damon_update_region_access_rate(struct damon_region *r, bool accessed, diff --git a/mm/damon/core.c b/mm/damon/core.c index d1e7b441f2bf..d816679dd702 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -291,7 +291,8 @@ static void damon_free_region(struct damon_region *r) kmem_cache_free(damon_region_cache, r); } -void damon_destroy_region(struct damon_region *r, struct damon_target *t) +static void damon_destroy_region(struct damon_region *r, + struct damon_target *t) { damon_del_region(r, t); damon_free_region(r); From 8f793f1ad5bd9f4f7e9c4fa734a7995ef2a2401f Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:19 -0700 Subject: [PATCH 2865/5214] mm/damon/core: add kdamond_call() debug_sanity check kdamond_call() is the place where DAMON API callers are allowed to access the DAMON context's public internal state including the monitoring results. Hence it is important to ensure it is called with the expected DAMON context state. Do the check under DAMON_DEBUG_SANITY. Link: https://lore.kernel.org/20260522154026.80546-9-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/core.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index d816679dd702..00e2997524ec 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -3329,6 +3329,37 @@ static void kdamond_usleep(unsigned long usecs) usleep_range_idle(usecs, usecs + 1); } +#ifdef CONFIG_DAMON_DEBUG_SANITY +static void damon_verify_ctx(struct damon_ctx *c) +{ + struct damon_target *t; + struct damon_region *r; + + damon_for_each_target(t, c) { + struct damon_region *prev_r = NULL; + unsigned int nr_regions = 0; + + damon_for_each_region(r, t) { + WARN_ONCE(r->ar.start >= r->ar.end, + "region start (%lu) >= end (%lu)\n", + r->ar.start, r->ar.end); + WARN_ONCE(prev_r && prev_r->ar.end > r->ar.start, + "region overlap (%lu > %lu)\n", + prev_r->ar.end, r->ar.start); + prev_r = r; + nr_regions++; + } + WARN_ONCE(damon_nr_regions(t) != nr_regions, + "nr_regions mismatch: %u != %u\n", + damon_nr_regions(t), nr_regions); + } +} +#else +static void damon_verify_ctx(struct damon_ctx *c) +{ +} +#endif + /* * kdamond_call() - handle damon_call_control objects. * @ctx: The &struct damon_ctx of the kdamond. @@ -3344,6 +3375,8 @@ static void kdamond_call(struct damon_ctx *ctx, bool cancel) struct damon_call_control *control, *next; LIST_HEAD(controls); + damon_verify_ctx(ctx); + mutex_lock(&ctx->call_controls_lock); list_splice_tail_init(&ctx->call_controls, &controls); mutex_unlock(&ctx->call_controls_lock); From b8db646fe9a77845c37680ca416847ced5763c06 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:20 -0700 Subject: [PATCH 2866/5214] mm/damon/core: remove damon_verify_nr_regions() When CONFIG_DAMON_DEBUG_SANITY is enabled, damon_verify_nr_regions() is called for each damon_nr_regions() invocation. damon_veify_nr_regions() iterates all regions. damon_nr_regions() is called for each region in kdamond_reset_aggregated() and damos_apply_scheme(). Hence it imposes O(n**2) overhead where n is the number of regions. Though the verification is enabled only under DAMON_DEBUG_SANITY, which is not for production use cases, it could be too high overhead. Meanwhile, damon_verify_ctx() is doing the damon_nr_regions() test. Because damon_verify_ctx() is called for each kdamond_call(), the test coverage from damon_verify_ctx() could be sufficient. Remove damon_nr_regions() verification. Link: https://lore.kernel.org/20260522154026.80546-10-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/core.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 00e2997524ec..b33920873871 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -686,27 +686,8 @@ void damon_destroy_target(struct damon_target *t, struct damon_ctx *ctx) damon_free_target(t); } -#ifdef CONFIG_DAMON_DEBUG_SANITY -static void damon_verify_nr_regions(struct damon_target *t) -{ - struct damon_region *r; - unsigned int count = 0; - - damon_for_each_region(r, t) - count++; - WARN_ONCE(count != t->nr_regions, "t->nr_regions (%u) != count (%u)\n", - t->nr_regions, count); -} -#else -static void damon_verify_nr_regions(struct damon_target *t) -{ -} -#endif - unsigned int damon_nr_regions(struct damon_target *t) { - damon_verify_nr_regions(t); - return t->nr_regions; } From 2ceda82a15c1bc8c6b1f1915743e938703b57e4c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:21 -0700 Subject: [PATCH 2867/5214] mm/damon/tests/core-kunit: add damon_set_regions() test cases damon_set_regions() is one of the main DAMON kernel API functions that set up the monitoring target memory region boundaries. Implement unit tests for verifying its basic functionalities. Link: https://lore.kernel.org/20260522154026.80546-11-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- mm/damon/tests/core-kunit.h | 142 ++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 22 deletions(-) diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 866f716e5760..1cfb8c176b87 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -390,41 +390,139 @@ static void damon_test_ops_registration(struct kunit *test) } } -static void damon_test_set_regions(struct kunit *test) +static void damon_test_set_regions_for(struct kunit *test, + struct damon_addr_range *old_ranges, int sz_old_ranges, + struct damon_addr_range *new_ranges, int sz_new_ranges, + unsigned long min_region_sz, + struct damon_addr_range *expect_ranges, int sz_expect_ranges) { - struct damon_target *t = damon_new_target(); - struct damon_region *r1, *r2; - struct damon_addr_range range = {.start = 8, .end = 28}; - unsigned long expects[] = {8, 16, 16, 24, 24, 28}; - int expect_idx = 0; + struct damon_target *t; struct damon_region *r; + int i; + t = damon_new_target(); if (!t) kunit_skip(test, "target alloc fail"); - r1 = damon_new_region(4, 16); - if (!r1) { - damon_free_target(t); - kunit_skip(test, "region alloc fail"); - } - r2 = damon_new_region(24, 32); - if (!r2) { - damon_free_target(t); - damon_free_region(r1); - kunit_skip(test, "second region alloc fail"); + for (i = 0; i < sz_old_ranges; i++) { + r = damon_new_region(old_ranges[i].start, old_ranges[i].end); + if (!r) { + damon_destroy_target(t, NULL); + kunit_skip(test, "%d-th r alloc fail\n", i); + } + damon_add_region(r, t); } - damon_add_region(r1, t); - damon_add_region(r2, t); - damon_set_regions(t, &range, 1, 1); + damon_set_regions(t, new_ranges, sz_new_ranges, min_region_sz); - KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 3); + KUNIT_EXPECT_EQ(test, damon_nr_regions(t), sz_expect_ranges); + if (damon_nr_regions(t) != sz_expect_ranges) { + damon_destroy_target(t, NULL); + return; + } + i = 0; damon_for_each_region(r, t) { - KUNIT_EXPECT_EQ(test, r->ar.start, expects[expect_idx++]); - KUNIT_EXPECT_EQ(test, r->ar.end, expects[expect_idx++]); + KUNIT_EXPECT_EQ(test, r->ar.start, expect_ranges[i].start); + KUNIT_EXPECT_EQ(test, r->ar.end, expect_ranges[i++].end); } + damon_destroy_target(t, NULL); } +static void damon_test_set_regions(struct kunit *test) +{ + /* Initial build up on empty target. */ + damon_test_set_regions_for(test, + (struct damon_addr_range[]){}, 0, + (struct damon_addr_range[]){ + {.start = 5, .end = 15}, + {.start = 15, .end = 25}, + }, 2, + 1, + (struct damon_addr_range[]){ + {.start = 5, .end = 15}, + {.start = 15, .end = 25}, + }, 2); + /* Un-intersecting regions should be removed. */ + damon_test_set_regions_for(test, + (struct damon_addr_range[]){ + {.start = 4, .end = 16}, + {.start = 24, .end = 32}, + }, 2, + (struct damon_addr_range[]){ + {.start = 18, .end = 23}, + }, 1, + 1, + (struct damon_addr_range[]){ + {.start = 18, .end = 23}, + }, 1); + /* + * Holes should be filled up with new regions. + * + * old: [4, 16) [24, 32) + * new: [8, 28) + * expect: [8, 16)[16,24),[24, 28) + */ + damon_test_set_regions_for(test, + (struct damon_addr_range[]){ + {.start = 4, .end = 16}, + {.start = 24, .end = 32}, + }, 2, + (struct damon_addr_range[]){ + {.start = 8, .end = 28}, + }, 1, + 1, + (struct damon_addr_range[]){ + {.start = 8, .end = 16}, + {.start = 16, .end = 24}, + {.start = 24, .end = 28}, + }, 3); + /* + * New regions should be able to be appended. + * + * old: [0, 4)[4, 17) + * new: [0, 15) [25, 40) + * expect: [0, 4)[4, 15) [25, 40) + */ + damon_test_set_regions_for(test, + (struct damon_addr_range[]){ + {.start = 0, .end = 4}, + {.start = 4, .end = 17}, + }, 2, + (struct damon_addr_range[]){ + {.start = 0, .end = 15}, + {.start = 25, .end = 40}, + }, 2, + 1, + (struct damon_addr_range[]){ + {.start = 0, .end = 4}, + {.start = 4, .end = 15}, + {.start = 25, .end = 40}, + }, 3); + /* + * New regions should be able to be inserted. + * + * old: [0, 4) [42, 52) + * new: [0, 15) [25, 40) [44, 50) + * expect: [0, 15) [25, 40) [44, 50) + */ + damon_test_set_regions_for(test, + (struct damon_addr_range[]){ + {.start = 0, .end = 4}, + {.start = 42, .end = 52}, + }, 2, + (struct damon_addr_range[]){ + {.start = 0, .end = 15}, + {.start = 25, .end = 40}, + {.start = 44, .end = 50}, + }, 3, + 1, + (struct damon_addr_range[]){ + {.start = 0, .end = 15}, + {.start = 25, .end = 40}, + {.start = 44, .end = 50}, + }, 3); +} + static void damon_test_nr_accesses_to_accesses_bp(struct kunit *test) { struct damon_attrs attrs = { From ae819edb97012b29db0a78f314d80d20959d77c9 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:22 -0700 Subject: [PATCH 2868/5214] selftests/damon/sysfs.py: stop kdamonds before failing When an assertion is failed, sysfs.py DAMON selftest immediately exits the test program leaving the DAMON running behind. Many of the following tests need to start DAMON on their own. But because DAMON that was started by sysfs.py is still running, those start attempts fail, and the tests are failed or skipped. Update sysfs.py to stop DAMON before exiting the test program due to the assertion failure. Link: https://lore.kernel.org/20260522154026.80546-12-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index cd4d82c85211..aa03a1187489 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -24,9 +24,12 @@ def dump_damon_status_dict(pid): except Exception as e: return None, 'json.load fail (%s)' % e +kdamonds = None def fail(expectation, status): print('unexpected %s' % expectation) print(json.dumps(status, indent=4)) + if kdamonds is not None: + kdamonds.stop() exit(1) def assert_true(condition, expectation, status): @@ -248,6 +251,7 @@ def assert_ctxs_committed(kdamonds): ctx.pause = False def main(): + global kdamonds kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( contexts=[_damon_sysfs.DamonCtx( From b6404e44aac2e51c552691d8861c7686be762d42 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:23 -0700 Subject: [PATCH 2869/5214] selftests/damon/sysfs.sh: test monitoring intervals goal dir sysfs.sh DAMON selftest is not testing monitoring intervals goal directory. Add the test. Link: https://lore.kernel.org/20260522154026.80546-13-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index 1ac3e2ce8e44..b3418214ed35 100755 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -282,6 +282,17 @@ test_targets() ensure_dir "$targets_dir/1" "not_exist" } + +test_intervals_goal() +{ + goal_dir=$1 + ensure_dir "$goal_dir" "exist" + ensure_file "$goal_dir/access_bp" "exist" "600" + ensure_file "$goal_dir/aggrs" "exist" "600" + ensure_file "$goal_dir/min_sample_us" "exist" "600" + ensure_file "$goal_dir/max_sample_us" "exist" "600" +} + test_intervals() { intervals_dir=$1 @@ -289,6 +300,7 @@ test_intervals() ensure_file "$intervals_dir/aggr_us" "exist" "600" ensure_file "$intervals_dir/sample_us" "exist" "600" ensure_file "$intervals_dir/update_us" "exist" "600" + test_intervals_goal "$intervals_dir/intervals_goal" } test_damon_filter() From a8f30ccf23f520bb071657ece5dcf534fda8e53b Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:24 -0700 Subject: [PATCH 2870/5214] selftests/damon/sysfs.sh: test addr_unit file existence sysfs.sh DAMON selftest is not testing the existence of addr_unit sysfs file. Add the test. Link: https://lore.kernel.org/20260522154026.80546-14-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index b3418214ed35..92b44c86818a 100755 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -365,6 +365,7 @@ test_context() ensure_dir "$context_dir" "exist" ensure_file "$context_dir/avail_operations" "exit" 400 ensure_file "$context_dir/operations" "exist" 600 + ensure_file "$context_dir/addr_unit" "exist" 600 test_monitoring_attrs "$context_dir/monitoring_attrs" test_targets "$context_dir/targets" test_schemes "$context_dir/schemes" From 1f9f7e72da1b3262616b7e191db8bae8225f2435 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 22 May 2026 08:40:25 -0700 Subject: [PATCH 2871/5214] selftests/damon/sysfs.sh: test pause file existence sysfs.sh DAMON selftest is not testing the existence of the 'pause' sysfs file. Add the test. Link: https://lore.kernel.org/20260522154026.80546-15-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index 92b44c86818a..78f4badb5beb 100755 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -366,6 +366,7 @@ test_context() ensure_file "$context_dir/avail_operations" "exit" 400 ensure_file "$context_dir/operations" "exist" 600 ensure_file "$context_dir/addr_unit" "exist" 600 + ensure_file "$context_dir/pause" "exist" 600 test_monitoring_attrs "$context_dir/monitoring_attrs" test_targets "$context_dir/targets" test_schemes "$context_dir/schemes" From d63e9d829e42b29201ebbcf0a070acea8ed40b2c Mon Sep 17 00:00:00 2001 From: Maksym Shcherba Date: Thu, 21 May 2026 23:20:19 +0300 Subject: [PATCH 2872/5214] mm/damon: fix missing parens in macro arguments Patch series "mm/damon: fix macro arguments and clarify quota goals doc", v2. This patch (of 2): The DAMON iterator macros do not wrap their pointer arguments with parentheses. This can cause build failures when the argument is a complex expression due to operator precedence issues. Add missing parentheses around the arguments in the following macros to prevent potential build failures: - damon_for_each_region() - damon_for_each_region_from() - damon_for_each_region_safe() - damos_for_each_quota_goal() Link: https://lore.kernel.org/20260521202020.126500-1-maksym.shcherba@lnu.edu.ua Link: https://lore.kernel.org/20260521202020.126500-2-maksym.shcherba@lnu.edu.ua Signed-off-by: Maksym Shcherba Reviewed-by: SeongJae Park Assisted-by: Antigravity:Gemini-3.1-Pro Signed-off-by: Andrew Morton --- include/linux/damon.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 638ee65f88dc..6f7edb3590ef 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -963,13 +963,13 @@ static inline unsigned long damon_sz_region(struct damon_region *r) list_for_each_entry_safe(p, next, &(ctx)->probes, list) #define damon_for_each_region(r, t) \ - list_for_each_entry(r, &t->regions_list, list) + list_for_each_entry(r, &(t)->regions_list, list) #define damon_for_each_region_from(r, t) \ - list_for_each_entry_from(r, &t->regions_list, list) + list_for_each_entry_from(r, &(t)->regions_list, list) #define damon_for_each_region_safe(r, next, t) \ - list_for_each_entry_safe(r, next, &t->regions_list, list) + list_for_each_entry_safe(r, next, &(t)->regions_list, list) #define damon_for_each_target(t, ctx) \ list_for_each_entry(t, &(ctx)->adaptive_targets, list) @@ -984,7 +984,7 @@ static inline unsigned long damon_sz_region(struct damon_region *r) list_for_each_entry_safe(s, next, &(ctx)->schemes, list) #define damos_for_each_quota_goal(goal, quota) \ - list_for_each_entry(goal, "a->goals, list) + list_for_each_entry(goal, &(quota)->goals, list) #define damos_for_each_quota_goal_safe(goal, next, quota) \ list_for_each_entry_safe(goal, next, &(quota)->goals, list) From 83b25befc1ab1f6e331691c4caf41f919fd082d2 Mon Sep 17 00:00:00 2001 From: Maksym Shcherba Date: Thu, 21 May 2026 23:20:20 +0300 Subject: [PATCH 2873/5214] Docs/admin-guide/mm/damon/usage: clarify current_value of quota goals The sysfs interface for DAMON quota goals includes a `current_value` file. This file is not updated by the kernel and only serves to receive user input. Clarify in the documentation that the kernel does not update `current_value`, and that reading it only has meaning when `target_metric` is set to `user_input`. While at it, fix missing commas in the goal files list. Link: https://lore.kernel.org/20260521202020.126500-3-maksym.shcherba@lnu.edu.ua Signed-off-by: Maksym Shcherba Reviewed-by: SeongJae Park Assisted-by: Antigravity:Gemini-3.1-Pro Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/damon/usage.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst index d46875e603d8..011296f1e7c2 100644 --- a/Documentation/admin-guide/mm/damon/usage.rst +++ b/Documentation/admin-guide/mm/damon/usage.rst @@ -474,10 +474,12 @@ to ``N-1``. Each directory represents each goal and current achievement. Among the multiple feedback, the best one is used. Each goal directory contains five files, namely ``target_metric``, -``target_value``, ``current_value`` ``nid`` and ``path``. Users can set and +``target_value``, ``current_value``, ``nid``, and ``path``. Users can set and get the five parameters for the quota auto-tuning goals that specified on the :ref:`design doc ` by writing to and -reading from each of the files. Note that users should further write +reading from each of the files. Because the kernel does not update +``current_value``, reading it only makes sense when ``target_metric`` is +``user_input``. Note that users should further write ``commit_schemes_quota_goals`` to the ``state`` file of the :ref:`kdamond directory ` to pass the feedback to DAMON. From 7e6cc35f5283eab81a14231a64ecd640b690c48c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 20 May 2026 08:03:10 -0700 Subject: [PATCH 2874/5214] mm/damon/core: trace esz at first setup DAMON traces effective size quota from the second update, only if a change has been made by the update. Tracing only changed updates was an intentional decision to avoid unnecessary same value tracing. Always skipping the first value is just an unintended mistake. The mistake makes the tracepoint based investigation incomplete, because the first effective size quota is never traced. It is not a big issue when the 'consist' quota tuner is used, because it keeps changing the quota in the usual setup. However, when the 'temporal' tuner is used, the quota value is not changed before the goal achievement status is completely changed. For example, if the DAMOS scheme is started with an under-achieved goal, the quota is set to the maximum value, and kept the same value until the goal is achieved. Because DAMON skips the first value, the user cannot know what effective quota the current scheme is using. Only after the goal is achieved, the effective quota is changed to zero, and traced. Unconditionally trace the initial quota value to fix this problem. Note that the 'temporal' quota tuner was introduced by commit af738a6a00c1 ("mm/damon/core: introduce DAMOS_QUOTA_GOAL_TUNER_TEMPORAL"), which was added to 7.1-rc1. But even with the 'consist' quota tuner, the tracing is unintentionally incomplete. Hence this commit marks the introduction of the trace event as the broken commit. Link: https://lore.kernel.org/20260520150311.80925-1-sj@kernel.org Fixes: a86d695193bf ("mm/damon: add trace event for effective size quota") Signed-off-by: SeongJae Park Cc: # 6.17.x Signed-off-by: Andrew Morton --- mm/damon/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index b33920873871..265d51ade25b 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2899,6 +2899,8 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) if (!quota->total_charged_sz && !quota->charged_from) { quota->charged_from = jiffies; damos_set_effective_quota(c, s); + if (trace_damos_esz_enabled()) + damos_trace_esz(c, s, quota); } /* New charge window starts */ From 7c2ebe0fe06e84a5a1fcbc358111735080bdb141 Mon Sep 17 00:00:00 2001 From: Wang Wensheng Date: Sun, 24 May 2026 11:10:53 +0800 Subject: [PATCH 2875/5214] kasan/test: only do kmalloc_double_kzfree for generic mode kmalloc_double_kzfree() would corrupt kernel memory when the just freed memory were allocated by another thread before the second call to kfree_sensitive() and the new allocation tag happened to match the old one. This could not happen in GENERIC mode as it uses quarantine. Link: https://lore.kernel.org/20260524031053.381776-1-wsw9603@163.com Signed-off-by: Wang Wensheng Reviewed-by: Andrey Konovalov Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Dmitry Vyukov Cc: Vincenzo Frascino Signed-off-by: Andrew Morton --- mm/kasan/kasan_test_c.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c index 32d06cbf6a31..3f4ed29178b3 100644 --- a/mm/kasan/kasan_test_c.c +++ b/mm/kasan/kasan_test_c.c @@ -874,6 +874,16 @@ static void kmalloc_double_kzfree(struct kunit *test) char *ptr; size_t size = 16; + /* + * With the tag-based KASAN modes, if the memory happens to be + * reallocated between the two frees and the new allocation tag happens + * to match the old one, the second free will cause a memory corruption. + * Resolving https://bugzilla.kernel.org/show_bug.cgi?id=212177 would + * help to deal with this. With Generic KASAN, it's effectively + * impossible for the memory to get reallocated due to the quarantine. + */ + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC); + ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); From 6cbdd9726fb50d749b06ab45a8ef81dff02e69b8 Mon Sep 17 00:00:00 2001 From: "Barry Song (Xiaomi)" Date: Tue, 26 May 2026 21:09:38 +0800 Subject: [PATCH 2876/5214] mm/mglru: use folio_mark_accessed to replace folio_set_active MGLRU gives high priority to folios mapped in page tables. As a result, folio_set_active() is invoked for all folios read during page faults. In practice, however, readahead can bring in many folios that are never accessed via page tables. A previous attempt by Lei Liu proposed introducing a separate LRU for readahead[1] to make readahead pages easier to reclaim, but that approach is likely over-engineered. Before commit 4d5d14a01e2c ("mm/mglru: rework workingset protection"), folios with PG_active were always placed in the youngest generation, leading to over-protection and increased refaults. After that commit, PG_active folios are placed in the second youngest generation, which is still too optimistic given the presence of readahead. In contrast, the classic active/inactive scheme is more conservative. This patch switches to using folio_mark_accessed() and begins prefaulted file folios from the second oldest generation instead of active generations. We should also adjust the following accordingly: - WORKINGSET_ACTIVATE: aligned with setting active for refaulted workingset folios; - lru_gen_folio_seq(): place (pre)faulted file folios into the second oldest generation; - promote second-scanned folios to workingset in folio_check_references(): we now have to depend on folio_lru_refs() > 1, since we previously relied on PG_referenced being set during the first scan, but PG_referenced is now set earlier. On x86, running a kernel build inside a memcg with a 1GB memory limit using 20 threads. w/o patch: real 1m50.764s user 25m32.305s sys 4m0.012s pswpin: 1333245 pswpout: 4366443 pgpgin: 6962592 pgpgout: 17780712 swpout_zero: 1019603 swpin_zero: 14764 refault_file: 287794 refault_anon: 1347963 w/ patch: real 1m48.879s user 25m29.224s sys 3m37.421s pswpin: 568480 pswpout: 2322657 pgpgin: 4073416 pgpgout: 9613408 swpout_zero: 593275 swpin_zero: 9118 refault_file: 262505 refault_anon: 577550 active/inactive LRU: real 1m49.928s user 25m28.196s sys 3m40.740s pswpin: 463452 pswpout: 2309119 pgpgin: 4438856 pgpgout: 9568628 swpout_zero: 743704 swpin_zero: 7244 refault_file: 562555 refault_anon: 470694 Lance and Xueyuan made a huge contribution to this patch through testing. Link: https://lore.kernel.org/20260526130938.66253-1-baohua@kernel.org Link: https://lore.kernel.org/linux-mm/20250916072226.220426-1-liulei.rjpt@vivo.com/ [1] Signed-off-by: Barry Song (Xiaomi) Tested-by: Lance Yang Tested-by: Xueyuan Chen Cc: Pedro Falcato Cc: Kairui Song Cc: Qi Zheng Cc: Shakeel Butt Cc: wangzicheng Cc: Suren Baghdasaryan Cc: Lei Liu Cc: Matthew Wilcox (Oracle) Cc: Axel Rasmussen Cc: Yuanchu Xie Cc: Wei Xu Cc: Will Deacon Cc: Kalesh Singh Signed-off-by: Andrew Morton --- include/linux/mm_inline.h | 2 +- mm/swap.c | 16 +++++++++++++--- mm/vmscan.c | 6 +++++- mm/workingset.c | 10 ++++++---- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h index a171070e15f0..a8430a7ae054 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h @@ -247,7 +247,7 @@ static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec, (folio_test_dirty(folio) || folio_test_writeback(folio)))) gen = MIN_NR_GENS; else - gen = MAX_NR_GENS - folio_test_workingset(folio); + gen = MAX_NR_GENS - (folio_test_workingset(folio) || folio_test_referenced(folio)); return max(READ_ONCE(lrugen->max_seq) - gen + 1, READ_ONCE(lrugen->min_seq[type])); } diff --git a/mm/swap.c b/mm/swap.c index 2dd84813f4dd..588f50d8f1a8 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -544,10 +544,20 @@ void folio_add_lru(struct folio *folio) folio_test_unevictable(folio), folio); VM_BUG_ON_FOLIO(folio_test_lru(folio), folio); - /* see the comment in lru_gen_folio_seq() */ + /* + * For refaulted workingset folios, set PG_active so they + * can be added to active generations. + * For prefaulted file folios, folio_mark_accessed() sets + * PG_referenced so lru_gen_folio_seq() places them into + * the second oldest generation. + */ if (lru_gen_enabled() && !folio_test_unevictable(folio) && - lru_gen_in_fault() && !(current->flags & PF_MEMALLOC)) - folio_set_active(folio); + lru_gen_in_fault() && !(current->flags & PF_MEMALLOC)) { + if (folio_test_workingset(folio)) + folio_set_active(folio); + else if (!folio_test_referenced(folio)) + folio_mark_accessed(folio); + } folio_batch_add_and_move(folio, lru_add); } diff --git a/mm/vmscan.c b/mm/vmscan.c index 3c856a78c0a5..76193a84a2af 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -850,7 +850,11 @@ static bool lru_gen_set_refs(struct folio *folio) return false; } - set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset)); + /* Promote on second access */ + if (folio_lru_refs(folio) > 1) + set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset)); + else + folio_mark_accessed(folio); return true; } #else diff --git a/mm/workingset.c b/mm/workingset.c index 07e6836d0502..f351798e723a 100644 --- a/mm/workingset.c +++ b/mm/workingset.c @@ -319,11 +319,13 @@ static void lru_gen_refault(struct folio *folio, void *shadow) atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]); - /* see folio_add_lru() where folio_set_active() will be called */ - if (lru_gen_in_fault()) - mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta); - if (workingset) { + /* + * see folio_add_lru(), where folio_set_active() is + * called for workingset folios + */ + if (lru_gen_in_fault()) + mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta); folio_set_workingset(folio); mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta); } else From 62f272d2fbffa7494e4d01c35a3a7b30d71b30a1 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Tue, 26 May 2026 11:28:36 +0000 Subject: [PATCH 2877/5214] mm/page_alloc: remove VM_BUG_ON()s from pindex helpers Vlastimil pointed out that the VM_BUG_ON()s have fallen out of favour, so remove them. Link: https://lore.kernel.org/20260526-page_alloc-unmapped-prep-v2-1-412f4d486115@google.com Signed-off-by: Brendan Jackman Suggested-by: Vlastimil Babka (SUSE) Link: https://lore.kernel.org/all/4074a816-9e75-45a6-8141-25459bcc106b@kernel.org/ Reviewed-by: Vlastimil Babka (SUSE) Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 7e3c79e79e5b..97cb95820592 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -653,13 +653,8 @@ static inline unsigned int order_to_pindex(int migratetype, int order) if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { bool movable = migratetype == MIGRATE_MOVABLE; - if (order > PAGE_ALLOC_COSTLY_ORDER) { - VM_BUG_ON(!is_pmd_order(order)); - + if (order > PAGE_ALLOC_COSTLY_ORDER) return NR_LOWORDER_PCP_LISTS + movable; - } - } else { - VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); } return (MIGRATE_PCPTYPES * order) + migratetype; @@ -672,8 +667,6 @@ static inline int pindex_to_order(unsigned int pindex) if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { if (pindex >= NR_LOWORDER_PCP_LISTS) order = HPAGE_PMD_ORDER; - } else { - VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); } return order; From 7bc5e747bba29d5b77b2278e8c696eea0c796706 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Sat, 23 May 2026 20:37:58 +0300 Subject: [PATCH 2878/5214] userfaultfd: merge fs/userfaultfd.c into mm/userfaultfd.c Patch series "userfaultfd: merge fs/userfaultfd.c into mm/userfaultfd.c", v3. These patches merge fs/userfaultfd.c into mm/userfaultfd.c and make functions used only inside mm/userfaultfd.c static. This patch (of 2): Historically userfaultfd implementation has been split between fs/userfaultfd.c and mm/userfaultfd.c. The mm/ part implemented memory management operations, while the fs/ part implemented file descriptor handling and called into the mm/ part for the actual memory management work. This separation is quite artificial and fs/userfaultfd.c does not seem to belong to fs/ because it's only a user if vfs APIs and like for other users, for example, memfd and secretmem, the file descriptor handling could live in mm/ as well. "Append" fs/userfaultfd.c to mm/userfaultfd and update fs/Makefile and MAINTAINERS accordingly. No intended functional changes. Link: https://lore.kernel.org/20260523173759.3964908-1-rppt@kernel.org Link: https://lore.kernel.org/20260523173759.3964908-2-rppt@kernel.org Assisted-by: Copilot:claude-opus-4-6 Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Christian Brauner (Amutable) Cc: Al Viro Cc: David Hildenbrand Cc: Jan Kara Cc: "Kirill A. Shutemov" Cc: Peter Xu Signed-off-by: Andrew Morton --- MAINTAINERS | 1 - fs/Makefile | 1 - fs/userfaultfd.c | 2233 ---------------------------------------------- mm/userfaultfd.c | 2215 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2215 insertions(+), 2235 deletions(-) delete mode 100644 fs/userfaultfd.c diff --git a/MAINTAINERS b/MAINTAINERS index 1e94e8cc6ad1..48c2265f00a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17085,7 +17085,6 @@ R: Peter Xu L: linux-mm@kvack.org S: Maintained F: Documentation/admin-guide/mm/userfaultfd.rst -F: fs/userfaultfd.c F: include/asm-generic/pgtable_uffd.h F: include/linux/userfaultfd_k.h F: include/uapi/linux/userfaultfd.h diff --git a/fs/Makefile b/fs/Makefile index ae1b07f9c6a0..89a8a9d207d1 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -27,7 +27,6 @@ obj-y += anon_inodes.o obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o -obj-$(CONFIG_USERFAULTFD) += userfaultfd.o obj-$(CONFIG_AIO) += aio.o obj-$(CONFIG_FS_DAX) += dax.o obj-$(CONFIG_FS_ENCRYPTION) += crypto/ diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c deleted file mode 100644 index 390e4b7d9cb9..000000000000 --- a/fs/userfaultfd.c +++ /dev/null @@ -1,2233 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * fs/userfaultfd.c - * - * Copyright (C) 2007 Davide Libenzi - * Copyright (C) 2008-2009 Red Hat, Inc. - * Copyright (C) 2015 Red Hat, Inc. - * - * Some part derived from fs/eventfd.c (anon inode setup) and - * mm/ksm.c (mm hashing). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int sysctl_unprivileged_userfaultfd __read_mostly; - -#ifdef CONFIG_SYSCTL -static const struct ctl_table vm_userfaultfd_table[] = { - { - .procname = "unprivileged_userfaultfd", - .data = &sysctl_unprivileged_userfaultfd, - .maxlen = sizeof(sysctl_unprivileged_userfaultfd), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ZERO, - .extra2 = SYSCTL_ONE, - }, -}; -#endif - -static struct kmem_cache *userfaultfd_ctx_cachep __ro_after_init; - -struct userfaultfd_fork_ctx { - struct userfaultfd_ctx *orig; - struct userfaultfd_ctx *new; - struct list_head list; -}; - -struct userfaultfd_unmap_ctx { - struct userfaultfd_ctx *ctx; - unsigned long start; - unsigned long end; - struct list_head list; -}; - -struct userfaultfd_wait_queue { - struct uffd_msg msg; - wait_queue_entry_t wq; - struct userfaultfd_ctx *ctx; - bool waken; -}; - -struct userfaultfd_wake_range { - unsigned long start; - unsigned long len; -}; - -/* internal indication that UFFD_API ioctl was successfully executed */ -#define UFFD_FEATURE_INITIALIZED (1u << 31) - -static bool userfaultfd_is_initialized(struct userfaultfd_ctx *ctx) -{ - return ctx->features & UFFD_FEATURE_INITIALIZED; -} - -static bool userfaultfd_wp_async_ctx(struct userfaultfd_ctx *ctx) -{ - return ctx && (ctx->features & UFFD_FEATURE_WP_ASYNC); -} - -/* - * Whether WP_UNPOPULATED is enabled on the uffd context. It is only - * meaningful when userfaultfd_wp()==true on the vma and when it's - * anonymous. - */ -bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma) -{ - struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; - - if (!ctx) - return false; - - return ctx->features & UFFD_FEATURE_WP_UNPOPULATED; -} - -static int userfaultfd_wake_function(wait_queue_entry_t *wq, unsigned mode, - int wake_flags, void *key) -{ - struct userfaultfd_wake_range *range = key; - int ret; - struct userfaultfd_wait_queue *uwq; - unsigned long start, len; - - uwq = container_of(wq, struct userfaultfd_wait_queue, wq); - ret = 0; - /* len == 0 means wake all */ - start = range->start; - len = range->len; - if (len && (start > uwq->msg.arg.pagefault.address || - start + len <= uwq->msg.arg.pagefault.address)) - goto out; - WRITE_ONCE(uwq->waken, true); - /* - * The Program-Order guarantees provided by the scheduler - * ensure uwq->waken is visible before the task is woken. - */ - ret = wake_up_state(wq->private, mode); - if (ret) { - /* - * Wake only once, autoremove behavior. - * - * After the effect of list_del_init is visible to the other - * CPUs, the waitqueue may disappear from under us, see the - * !list_empty_careful() in handle_userfault(). - * - * try_to_wake_up() has an implicit smp_mb(), and the - * wq->private is read before calling the extern function - * "wake_up_state" (which in turns calls try_to_wake_up). - */ - list_del_init(&wq->entry); - } -out: - return ret; -} - -/** - * userfaultfd_ctx_get - Acquires a reference to the internal userfaultfd - * context. - * @ctx: [in] Pointer to the userfaultfd context. - */ -static void userfaultfd_ctx_get(struct userfaultfd_ctx *ctx) -{ - refcount_inc(&ctx->refcount); -} - -/** - * userfaultfd_ctx_put - Releases a reference to the internal userfaultfd - * context. - * @ctx: [in] Pointer to userfaultfd context. - * - * The userfaultfd context reference must have been previously acquired either - * with userfaultfd_ctx_get() or userfaultfd_ctx_fdget(). - */ -static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx) -{ - if (refcount_dec_and_test(&ctx->refcount)) { - VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_pending_wqh.lock)); - VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_pending_wqh)); - VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_wqh.lock)); - VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_wqh)); - VM_WARN_ON_ONCE(spin_is_locked(&ctx->event_wqh.lock)); - VM_WARN_ON_ONCE(waitqueue_active(&ctx->event_wqh)); - VM_WARN_ON_ONCE(spin_is_locked(&ctx->fd_wqh.lock)); - VM_WARN_ON_ONCE(waitqueue_active(&ctx->fd_wqh)); - mmdrop(ctx->mm); - kmem_cache_free(userfaultfd_ctx_cachep, ctx); - } -} - -static inline void msg_init(struct uffd_msg *msg) -{ - BUILD_BUG_ON(sizeof(struct uffd_msg) != 32); - /* - * Must use memset to zero out the paddings or kernel data is - * leaked to userland. - */ - memset(msg, 0, sizeof(struct uffd_msg)); -} - -static inline struct uffd_msg userfault_msg(unsigned long address, - unsigned long real_address, - unsigned int flags, - unsigned long reason, - unsigned int features) -{ - struct uffd_msg msg; - - msg_init(&msg); - msg.event = UFFD_EVENT_PAGEFAULT; - - msg.arg.pagefault.address = (features & UFFD_FEATURE_EXACT_ADDRESS) ? - real_address : address; - - /* - * These flags indicate why the userfault occurred: - * - UFFD_PAGEFAULT_FLAG_WP indicates a write protect fault. - * - UFFD_PAGEFAULT_FLAG_MINOR indicates a minor fault. - * - Neither of these flags being set indicates a MISSING fault. - * - * Separately, UFFD_PAGEFAULT_FLAG_WRITE indicates it was a write - * fault. Otherwise, it was a read fault. - */ - if (flags & FAULT_FLAG_WRITE) - msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WRITE; - if (reason & VM_UFFD_WP) - msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WP; - if (reason & VM_UFFD_MINOR) - msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_MINOR; - if (features & UFFD_FEATURE_THREAD_ID) - msg.arg.pagefault.feat.ptid = task_pid_vnr(current); - return msg; -} - -#ifdef CONFIG_HUGETLB_PAGE -/* - * Same functionality as userfaultfd_must_wait below with modifications for - * hugepmd ranges. - */ -static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, - struct vm_fault *vmf, - unsigned long reason) -{ - struct vm_area_struct *vma = vmf->vma; - pte_t *ptep, pte; - - assert_fault_locked(vmf); - - ptep = hugetlb_walk(vma, vmf->address, vma_mmu_pagesize(vma)); - if (!ptep) - return true; - - pte = huge_ptep_get(vma->vm_mm, vmf->address, ptep); - - /* - * Lockless access: we're in a wait_event so it's ok if it - * changes under us. - */ - - /* Entry is still missing, wait for userspace to resolve the fault. */ - if (huge_pte_none(pte)) - return true; - /* UFFD PTE markers require userspace to resolve the fault. */ - if (pte_is_uffd_marker(pte)) - return true; - /* - * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to - * resolve the fault. - */ - if (!huge_pte_write(pte) && (reason & VM_UFFD_WP)) - return true; - - return false; -} -#else -static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, - struct vm_fault *vmf, - unsigned long reason) -{ - /* Should never get here. */ - VM_WARN_ON_ONCE(1); - return false; -} -#endif /* CONFIG_HUGETLB_PAGE */ - -/* - * Verify the pagetables are still not ok after having registered into - * the fault_pending_wqh to avoid userland having to UFFDIO_WAKE any - * userfault that has already been resolved, if userfaultfd_read_iter and - * UFFDIO_COPY|ZEROPAGE are being run simultaneously on two different - * threads. - */ -static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx, - struct vm_fault *vmf, - unsigned long reason) -{ - struct mm_struct *mm = ctx->mm; - unsigned long address = vmf->address; - pgd_t *pgd; - p4d_t *p4d; - pud_t *pud; - pmd_t *pmd, _pmd; - pte_t *pte; - pte_t ptent; - bool ret; - - assert_fault_locked(vmf); - - pgd = pgd_offset(mm, address); - if (!pgd_present(*pgd)) - return true; - p4d = p4d_offset(pgd, address); - if (!p4d_present(*p4d)) - return true; - pud = pud_offset(p4d, address); - if (!pud_present(*pud)) - return true; - pmd = pmd_offset(pud, address); -again: - _pmd = pmdp_get_lockless(pmd); - if (pmd_none(_pmd)) - return true; - - /* - * A race could arise which would result in a softleaf entry such as - * migration entry unexpectedly being present in the PMD, so explicitly - * check for this and bail out if so. - */ - if (!pmd_present(_pmd)) - return false; - - if (pmd_trans_huge(_pmd)) - return !pmd_write(_pmd) && (reason & VM_UFFD_WP); - - pte = pte_offset_map(pmd, address); - if (!pte) - goto again; - - /* - * Lockless access: we're in a wait_event so it's ok if it - * changes under us. - */ - ptent = ptep_get(pte); - - ret = true; - /* Entry is still missing, wait for userspace to resolve the fault. */ - if (pte_none(ptent)) - goto out; - /* UFFD PTE markers require userspace to resolve the fault. */ - if (pte_is_uffd_marker(ptent)) - goto out; - /* - * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to - * resolve the fault. - */ - if (!pte_write(ptent) && (reason & VM_UFFD_WP)) - goto out; - - ret = false; -out: - pte_unmap(pte); - return ret; -} - -static inline unsigned int userfaultfd_get_blocking_state(unsigned int flags) -{ - if (flags & FAULT_FLAG_INTERRUPTIBLE) - return TASK_INTERRUPTIBLE; - - if (flags & FAULT_FLAG_KILLABLE) - return TASK_KILLABLE; - - return TASK_UNINTERRUPTIBLE; -} - -/* - * The locking rules involved in returning VM_FAULT_RETRY depending on - * FAULT_FLAG_ALLOW_RETRY, FAULT_FLAG_RETRY_NOWAIT and - * FAULT_FLAG_KILLABLE are not straightforward. The "Caution" - * recommendation in __lock_page_or_retry is not an understatement. - * - * If FAULT_FLAG_ALLOW_RETRY is set, the mmap_lock must be released - * before returning VM_FAULT_RETRY only if FAULT_FLAG_RETRY_NOWAIT is - * not set. - * - * If FAULT_FLAG_ALLOW_RETRY is set but FAULT_FLAG_KILLABLE is not - * set, VM_FAULT_RETRY can still be returned if and only if there are - * fatal_signal_pending()s, and the mmap_lock must be released before - * returning it. - */ -vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason) -{ - struct vm_area_struct *vma = vmf->vma; - struct mm_struct *mm = vma->vm_mm; - struct userfaultfd_ctx *ctx; - struct userfaultfd_wait_queue uwq; - vm_fault_t ret = VM_FAULT_SIGBUS; - bool must_wait; - unsigned int blocking_state; - - /* - * We don't do userfault handling for the final child pid update - * and when coredumping (faults triggered by get_dump_page()). - */ - if (current->flags & (PF_EXITING|PF_DUMPCORE)) - goto out; - - assert_fault_locked(vmf); - - ctx = vma->vm_userfaultfd_ctx.ctx; - if (!ctx) - goto out; - - VM_WARN_ON_ONCE(ctx->mm != mm); - - /* Any unrecognized flag is a bug. */ - VM_WARN_ON_ONCE(reason & ~__VM_UFFD_FLAGS); - /* 0 or > 1 flags set is a bug; we expect exactly 1. */ - VM_WARN_ON_ONCE(!reason || (reason & (reason - 1))); - - if (ctx->features & UFFD_FEATURE_SIGBUS) - goto out; - if (!(vmf->flags & FAULT_FLAG_USER) && (ctx->flags & UFFD_USER_MODE_ONLY)) - goto out; - - /* - * Check that we can return VM_FAULT_RETRY. - * - * NOTE: it should become possible to return VM_FAULT_RETRY - * even if FAULT_FLAG_TRIED is set without leading to gup() - * -EBUSY failures, if the userfaultfd is to be extended for - * VM_UFFD_WP tracking and we intend to arm the userfault - * without first stopping userland access to the memory. For - * VM_UFFD_MISSING userfaults this is enough for now. - */ - if (unlikely(!(vmf->flags & FAULT_FLAG_ALLOW_RETRY))) { - /* - * Validate the invariant that nowait must allow retry - * to be sure not to return SIGBUS erroneously on - * nowait invocations. - */ - VM_WARN_ON_ONCE(vmf->flags & FAULT_FLAG_RETRY_NOWAIT); -#ifdef CONFIG_DEBUG_VM - if (printk_ratelimit()) { - pr_warn("FAULT_FLAG_ALLOW_RETRY missing %x\n", - vmf->flags); - dump_stack(); - } -#endif - goto out; - } - - /* - * Handle nowait, not much to do other than tell it to retry - * and wait. - */ - ret = VM_FAULT_RETRY; - if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) - goto out; - - if (unlikely(READ_ONCE(ctx->released))) { - /* - * If a concurrent release is detected, do not return - * VM_FAULT_SIGBUS or VM_FAULT_NOPAGE, but instead always - * return VM_FAULT_RETRY with lock released proactively. - * - * If we were to return VM_FAULT_SIGBUS here, the non - * cooperative manager would be instead forced to - * always call UFFDIO_UNREGISTER before it can safely - * close the uffd, to avoid involuntary SIGBUS triggered. - * - * If we were to return VM_FAULT_NOPAGE, it would work for - * the fault path, in which the lock will be released - * later. However for GUP, faultin_page() does nothing - * special on NOPAGE, so GUP would spin retrying without - * releasing the mmap read lock, causing possible livelock. - * - * Here only VM_FAULT_RETRY would make sure the mmap lock - * be released immediately, so that the thread concurrently - * releasing the userfault would always make progress. - */ - release_fault_lock(vmf); - goto out; - } - - /* take the reference before dropping the mmap_lock */ - userfaultfd_ctx_get(ctx); - - init_waitqueue_func_entry(&uwq.wq, userfaultfd_wake_function); - uwq.wq.private = current; - uwq.msg = userfault_msg(vmf->address, vmf->real_address, vmf->flags, - reason, ctx->features); - uwq.ctx = ctx; - uwq.waken = false; - - blocking_state = userfaultfd_get_blocking_state(vmf->flags); - - /* - * Take the vma lock now, in order to safely call - * userfaultfd_huge_must_wait() later. Since acquiring the - * (sleepable) vma lock can modify the current task state, that - * must be before explicitly calling set_current_state(). - */ - if (is_vm_hugetlb_page(vma)) - hugetlb_vma_lock_read(vma); - - spin_lock_irq(&ctx->fault_pending_wqh.lock); - /* - * After the __add_wait_queue the uwq is visible to userland - * through poll/read(). - */ - __add_wait_queue(&ctx->fault_pending_wqh, &uwq.wq); - /* - * The smp_mb() after __set_current_state prevents the reads - * following the spin_unlock to happen before the list_add in - * __add_wait_queue. - */ - set_current_state(blocking_state); - spin_unlock_irq(&ctx->fault_pending_wqh.lock); - - if (is_vm_hugetlb_page(vma)) { - must_wait = userfaultfd_huge_must_wait(ctx, vmf, reason); - hugetlb_vma_unlock_read(vma); - } else { - must_wait = userfaultfd_must_wait(ctx, vmf, reason); - } - - release_fault_lock(vmf); - - if (likely(must_wait && !READ_ONCE(ctx->released))) { - wake_up_poll(&ctx->fd_wqh, EPOLLIN); - schedule(); - } - - __set_current_state(TASK_RUNNING); - - /* - * Here we race with the list_del; list_add in - * userfaultfd_ctx_read(), however because we don't ever run - * list_del_init() to refile across the two lists, the prev - * and next pointers will never point to self. list_add also - * would never let any of the two pointers to point to - * self. So list_empty_careful won't risk to see both pointers - * pointing to self at any time during the list refile. The - * only case where list_del_init() is called is the full - * removal in the wake function and there we don't re-list_add - * and it's fine not to block on the spinlock. The uwq on this - * kernel stack can be released after the list_del_init. - */ - if (!list_empty_careful(&uwq.wq.entry)) { - spin_lock_irq(&ctx->fault_pending_wqh.lock); - /* - * No need of list_del_init(), the uwq on the stack - * will be freed shortly anyway. - */ - list_del(&uwq.wq.entry); - spin_unlock_irq(&ctx->fault_pending_wqh.lock); - } - - /* - * ctx may go away after this if the userfault pseudo fd is - * already released. - */ - userfaultfd_ctx_put(ctx); - -out: - return ret; -} - -static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx, - struct userfaultfd_wait_queue *ewq) -{ - struct userfaultfd_ctx *release_new_ctx; - - if (WARN_ON_ONCE(current->flags & PF_EXITING)) - goto out; - - ewq->ctx = ctx; - init_waitqueue_entry(&ewq->wq, current); - release_new_ctx = NULL; - - spin_lock_irq(&ctx->event_wqh.lock); - /* - * After the __add_wait_queue the uwq is visible to userland - * through poll/read(). - */ - __add_wait_queue(&ctx->event_wqh, &ewq->wq); - for (;;) { - set_current_state(TASK_KILLABLE); - if (ewq->msg.event == 0) - break; - if (READ_ONCE(ctx->released) || - fatal_signal_pending(current)) { - /* - * &ewq->wq may be queued in fork_event, but - * __remove_wait_queue ignores the head - * parameter. It would be a problem if it - * didn't. - */ - __remove_wait_queue(&ctx->event_wqh, &ewq->wq); - if (ewq->msg.event == UFFD_EVENT_FORK) { - struct userfaultfd_ctx *new; - - new = (struct userfaultfd_ctx *) - (unsigned long) - ewq->msg.arg.reserved.reserved1; - release_new_ctx = new; - } - break; - } - - spin_unlock_irq(&ctx->event_wqh.lock); - - wake_up_poll(&ctx->fd_wqh, EPOLLIN); - schedule(); - - spin_lock_irq(&ctx->event_wqh.lock); - } - __set_current_state(TASK_RUNNING); - spin_unlock_irq(&ctx->event_wqh.lock); - - if (release_new_ctx) { - userfaultfd_release_new(release_new_ctx); - userfaultfd_ctx_put(release_new_ctx); - } - - /* - * ctx may go away after this if the userfault pseudo fd is - * already released. - */ -out: - atomic_dec(&ctx->mmap_changing); - VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); - userfaultfd_ctx_put(ctx); -} - -static void userfaultfd_event_complete(struct userfaultfd_ctx *ctx, - struct userfaultfd_wait_queue *ewq) -{ - ewq->msg.event = 0; - wake_up_locked(&ctx->event_wqh); - __remove_wait_queue(&ctx->event_wqh, &ewq->wq); -} - -int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs) -{ - struct userfaultfd_ctx *ctx = NULL, *octx; - struct userfaultfd_fork_ctx *fctx; - - octx = vma->vm_userfaultfd_ctx.ctx; - if (!octx) - return 0; - - if (!(octx->features & UFFD_FEATURE_EVENT_FORK)) { - userfaultfd_reset_ctx(vma); - return 0; - } - - list_for_each_entry(fctx, fcs, list) - if (fctx->orig == octx) { - ctx = fctx->new; - break; - } - - if (!ctx) { - fctx = kmalloc_obj(*fctx); - if (!fctx) - return -ENOMEM; - - ctx = kmem_cache_alloc(userfaultfd_ctx_cachep, GFP_KERNEL); - if (!ctx) { - kfree(fctx); - return -ENOMEM; - } - - refcount_set(&ctx->refcount, 1); - ctx->flags = octx->flags; - ctx->features = octx->features; - ctx->released = false; - init_rwsem(&ctx->map_changing_lock); - atomic_set(&ctx->mmap_changing, 0); - ctx->mm = vma->vm_mm; - mmgrab(ctx->mm); - - userfaultfd_ctx_get(octx); - down_write(&octx->map_changing_lock); - atomic_inc(&octx->mmap_changing); - up_write(&octx->map_changing_lock); - fctx->orig = octx; - fctx->new = ctx; - list_add_tail(&fctx->list, fcs); - } - - vma->vm_userfaultfd_ctx.ctx = ctx; - return 0; -} - -static void dup_fctx(struct userfaultfd_fork_ctx *fctx) -{ - struct userfaultfd_ctx *ctx = fctx->orig; - struct userfaultfd_wait_queue ewq; - - msg_init(&ewq.msg); - - ewq.msg.event = UFFD_EVENT_FORK; - ewq.msg.arg.reserved.reserved1 = (unsigned long)fctx->new; - - userfaultfd_event_wait_completion(ctx, &ewq); -} - -void dup_userfaultfd_complete(struct list_head *fcs) -{ - struct userfaultfd_fork_ctx *fctx, *n; - - list_for_each_entry_safe(fctx, n, fcs, list) { - dup_fctx(fctx); - list_del(&fctx->list); - kfree(fctx); - } -} - -void dup_userfaultfd_fail(struct list_head *fcs) -{ - struct userfaultfd_fork_ctx *fctx, *n; - - /* - * An error has occurred on fork, we will tear memory down, but have - * allocated memory for fctx's and raised reference counts for both the - * original and child contexts (and on the mm for each as a result). - * - * These would ordinarily be taken care of by a user handling the event, - * but we are no longer doing so, so manually clean up here. - * - * mm tear down will take care of cleaning up VMA contexts. - */ - list_for_each_entry_safe(fctx, n, fcs, list) { - struct userfaultfd_ctx *octx = fctx->orig; - struct userfaultfd_ctx *ctx = fctx->new; - - atomic_dec(&octx->mmap_changing); - VM_WARN_ON_ONCE(atomic_read(&octx->mmap_changing) < 0); - userfaultfd_ctx_put(octx); - userfaultfd_ctx_put(ctx); - - list_del(&fctx->list); - kfree(fctx); - } -} - -void mremap_userfaultfd_prep(struct vm_area_struct *vma, - struct vm_userfaultfd_ctx *vm_ctx) -{ - struct userfaultfd_ctx *ctx; - - ctx = vma->vm_userfaultfd_ctx.ctx; - - if (!ctx) - return; - - if (ctx->features & UFFD_FEATURE_EVENT_REMAP) { - vm_ctx->ctx = ctx; - userfaultfd_ctx_get(ctx); - down_write(&ctx->map_changing_lock); - atomic_inc(&ctx->mmap_changing); - up_write(&ctx->map_changing_lock); - } else { - /* Drop uffd context if remap feature not enabled */ - userfaultfd_reset_ctx(vma); - } -} - -void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx *vm_ctx, - unsigned long from, unsigned long to, - unsigned long len) -{ - struct userfaultfd_ctx *ctx = vm_ctx->ctx; - struct userfaultfd_wait_queue ewq; - - if (!ctx) - return; - - msg_init(&ewq.msg); - - ewq.msg.event = UFFD_EVENT_REMAP; - ewq.msg.arg.remap.from = from; - ewq.msg.arg.remap.to = to; - ewq.msg.arg.remap.len = len; - - userfaultfd_event_wait_completion(ctx, &ewq); -} - -void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) -{ - struct userfaultfd_ctx *ctx = vm_ctx->ctx; - - if (!ctx) - return; - - atomic_dec(&ctx->mmap_changing); - VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); - userfaultfd_ctx_put(ctx); -} - -bool userfaultfd_remove(struct vm_area_struct *vma, - unsigned long start, unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - struct userfaultfd_ctx *ctx; - struct userfaultfd_wait_queue ewq; - - ctx = vma->vm_userfaultfd_ctx.ctx; - if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_REMOVE)) - return true; - - userfaultfd_ctx_get(ctx); - down_write(&ctx->map_changing_lock); - atomic_inc(&ctx->mmap_changing); - up_write(&ctx->map_changing_lock); - mmap_read_unlock(mm); - - msg_init(&ewq.msg); - - ewq.msg.event = UFFD_EVENT_REMOVE; - ewq.msg.arg.remove.start = start; - ewq.msg.arg.remove.end = end; - - userfaultfd_event_wait_completion(ctx, &ewq); - - return false; -} - -static bool has_unmap_ctx(struct userfaultfd_ctx *ctx, struct list_head *unmaps, - unsigned long start, unsigned long end) -{ - struct userfaultfd_unmap_ctx *unmap_ctx; - - list_for_each_entry(unmap_ctx, unmaps, list) - if (unmap_ctx->ctx == ctx && unmap_ctx->start == start && - unmap_ctx->end == end) - return true; - - return false; -} - -int userfaultfd_unmap_prep(struct vm_area_struct *vma, unsigned long start, - unsigned long end, struct list_head *unmaps) -{ - struct userfaultfd_unmap_ctx *unmap_ctx; - struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; - - if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_UNMAP) || - has_unmap_ctx(ctx, unmaps, start, end)) - return 0; - - unmap_ctx = kzalloc_obj(*unmap_ctx); - if (!unmap_ctx) - return -ENOMEM; - - userfaultfd_ctx_get(ctx); - down_write(&ctx->map_changing_lock); - atomic_inc(&ctx->mmap_changing); - up_write(&ctx->map_changing_lock); - unmap_ctx->ctx = ctx; - unmap_ctx->start = start; - unmap_ctx->end = end; - list_add_tail(&unmap_ctx->list, unmaps); - - return 0; -} - -void userfaultfd_unmap_complete(struct mm_struct *mm, struct list_head *uf) -{ - struct userfaultfd_unmap_ctx *ctx, *n; - struct userfaultfd_wait_queue ewq; - - list_for_each_entry_safe(ctx, n, uf, list) { - msg_init(&ewq.msg); - - ewq.msg.event = UFFD_EVENT_UNMAP; - ewq.msg.arg.remove.start = ctx->start; - ewq.msg.arg.remove.end = ctx->end; - - userfaultfd_event_wait_completion(ctx->ctx, &ewq); - - list_del(&ctx->list); - kfree(ctx); - } -} - -static int userfaultfd_release(struct inode *inode, struct file *file) -{ - struct userfaultfd_ctx *ctx = file->private_data; - struct mm_struct *mm = ctx->mm; - /* len == 0 means wake all */ - struct userfaultfd_wake_range range = { .len = 0, }; - - WRITE_ONCE(ctx->released, true); - - userfaultfd_release_all(mm, ctx); - - /* - * After no new page faults can wait on this fault_*wqh, flush - * the last page faults that may have been already waiting on - * the fault_*wqh. - */ - spin_lock_irq(&ctx->fault_pending_wqh.lock); - __wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &range); - __wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, &range); - spin_unlock_irq(&ctx->fault_pending_wqh.lock); - - /* Flush pending events that may still wait on event_wqh */ - wake_up_all(&ctx->event_wqh); - - wake_up_poll(&ctx->fd_wqh, EPOLLHUP); - userfaultfd_ctx_put(ctx); - return 0; -} - -/* fault_pending_wqh.lock must be hold by the caller */ -static inline struct userfaultfd_wait_queue *find_userfault_in( - wait_queue_head_t *wqh) -{ - wait_queue_entry_t *wq; - struct userfaultfd_wait_queue *uwq; - - lockdep_assert_held(&wqh->lock); - - uwq = NULL; - if (!waitqueue_active(wqh)) - goto out; - /* walk in reverse to provide FIFO behavior to read userfaults */ - wq = list_last_entry(&wqh->head, typeof(*wq), entry); - uwq = container_of(wq, struct userfaultfd_wait_queue, wq); -out: - return uwq; -} - -static inline struct userfaultfd_wait_queue *find_userfault( - struct userfaultfd_ctx *ctx) -{ - return find_userfault_in(&ctx->fault_pending_wqh); -} - -static inline struct userfaultfd_wait_queue *find_userfault_evt( - struct userfaultfd_ctx *ctx) -{ - return find_userfault_in(&ctx->event_wqh); -} - -static __poll_t userfaultfd_poll(struct file *file, poll_table *wait) -{ - struct userfaultfd_ctx *ctx = file->private_data; - __poll_t ret; - - poll_wait(file, &ctx->fd_wqh, wait); - - if (!userfaultfd_is_initialized(ctx)) - return EPOLLERR; - - /* - * poll() never guarantees that read won't block. - * userfaults can be waken before they're read(). - */ - if (unlikely(!(file->f_flags & O_NONBLOCK))) - return EPOLLERR; - /* - * lockless access to see if there are pending faults - * __pollwait last action is the add_wait_queue but - * the spin_unlock would allow the waitqueue_active to - * pass above the actual list_add inside - * add_wait_queue critical section. So use a full - * memory barrier to serialize the list_add write of - * add_wait_queue() with the waitqueue_active read - * below. - */ - ret = 0; - smp_mb(); - if (waitqueue_active(&ctx->fault_pending_wqh)) - ret = EPOLLIN; - else if (waitqueue_active(&ctx->event_wqh)) - ret = EPOLLIN; - - return ret; -} - -static const struct file_operations userfaultfd_fops; - -static int resolve_userfault_fork(struct userfaultfd_ctx *new, - struct inode *inode, - struct uffd_msg *msg) -{ - int fd; - - fd = anon_inode_create_getfd("[userfaultfd]", &userfaultfd_fops, new, - O_RDONLY | (new->flags & UFFD_SHARED_FCNTL_FLAGS), inode); - if (fd < 0) - return fd; - - msg->arg.reserved.reserved1 = 0; - msg->arg.fork.ufd = fd; - return 0; -} - -static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait, - struct uffd_msg *msg, struct inode *inode) -{ - ssize_t ret; - DECLARE_WAITQUEUE(wait, current); - struct userfaultfd_wait_queue *uwq; - /* - * Handling fork event requires sleeping operations, so - * we drop the event_wqh lock, then do these ops, then - * lock it back and wake up the waiter. While the lock is - * dropped the ewq may go away so we keep track of it - * carefully. - */ - LIST_HEAD(fork_event); - struct userfaultfd_ctx *fork_nctx = NULL; - - /* always take the fd_wqh lock before the fault_pending_wqh lock */ - spin_lock_irq(&ctx->fd_wqh.lock); - __add_wait_queue(&ctx->fd_wqh, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock(&ctx->fault_pending_wqh.lock); - uwq = find_userfault(ctx); - if (uwq) { - /* - * Use a seqcount to repeat the lockless check - * in wake_userfault() to avoid missing - * wakeups because during the refile both - * waitqueue could become empty if this is the - * only userfault. - */ - write_seqcount_begin(&ctx->refile_seq); - - /* - * The fault_pending_wqh.lock prevents the uwq - * to disappear from under us. - * - * Refile this userfault from - * fault_pending_wqh to fault_wqh, it's not - * pending anymore after we read it. - * - * Use list_del() by hand (as - * userfaultfd_wake_function also uses - * list_del_init() by hand) to be sure nobody - * changes __remove_wait_queue() to use - * list_del_init() in turn breaking the - * !list_empty_careful() check in - * handle_userfault(). The uwq->wq.head list - * must never be empty at any time during the - * refile, or the waitqueue could disappear - * from under us. The "wait_queue_head_t" - * parameter of __remove_wait_queue() is unused - * anyway. - */ - list_del(&uwq->wq.entry); - add_wait_queue(&ctx->fault_wqh, &uwq->wq); - - write_seqcount_end(&ctx->refile_seq); - - /* careful to always initialize msg if ret == 0 */ - *msg = uwq->msg; - spin_unlock(&ctx->fault_pending_wqh.lock); - ret = 0; - break; - } - spin_unlock(&ctx->fault_pending_wqh.lock); - - spin_lock(&ctx->event_wqh.lock); - uwq = find_userfault_evt(ctx); - if (uwq) { - *msg = uwq->msg; - - if (uwq->msg.event == UFFD_EVENT_FORK) { - fork_nctx = (struct userfaultfd_ctx *) - (unsigned long) - uwq->msg.arg.reserved.reserved1; - list_move(&uwq->wq.entry, &fork_event); - /* - * fork_nctx can be freed as soon as - * we drop the lock, unless we take a - * reference on it. - */ - userfaultfd_ctx_get(fork_nctx); - spin_unlock(&ctx->event_wqh.lock); - ret = 0; - break; - } - - userfaultfd_event_complete(ctx, uwq); - spin_unlock(&ctx->event_wqh.lock); - ret = 0; - break; - } - spin_unlock(&ctx->event_wqh.lock); - - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - if (no_wait) { - ret = -EAGAIN; - break; - } - spin_unlock_irq(&ctx->fd_wqh.lock); - schedule(); - spin_lock_irq(&ctx->fd_wqh.lock); - } - __remove_wait_queue(&ctx->fd_wqh, &wait); - __set_current_state(TASK_RUNNING); - spin_unlock_irq(&ctx->fd_wqh.lock); - - if (!ret && msg->event == UFFD_EVENT_FORK) { - ret = resolve_userfault_fork(fork_nctx, inode, msg); - spin_lock_irq(&ctx->event_wqh.lock); - if (!list_empty(&fork_event)) { - /* - * The fork thread didn't abort, so we can - * drop the temporary refcount. - */ - userfaultfd_ctx_put(fork_nctx); - - uwq = list_first_entry(&fork_event, - typeof(*uwq), - wq.entry); - /* - * If fork_event list wasn't empty and in turn - * the event wasn't already released by fork - * (the event is allocated on fork kernel - * stack), put the event back to its place in - * the event_wq. fork_event head will be freed - * as soon as we return so the event cannot - * stay queued there no matter the current - * "ret" value. - */ - list_del(&uwq->wq.entry); - __add_wait_queue(&ctx->event_wqh, &uwq->wq); - - /* - * Leave the event in the waitqueue and report - * error to userland if we failed to resolve - * the userfault fork. - */ - if (likely(!ret)) - userfaultfd_event_complete(ctx, uwq); - } else { - /* - * Here the fork thread aborted and the - * refcount from the fork thread on fork_nctx - * has already been released. We still hold - * the reference we took before releasing the - * lock above. If resolve_userfault_fork - * failed we've to drop it because the - * fork_nctx has to be freed in such case. If - * it succeeded we'll hold it because the new - * uffd references it. - */ - if (ret) - userfaultfd_ctx_put(fork_nctx); - } - spin_unlock_irq(&ctx->event_wqh.lock); - } - - return ret; -} - -static ssize_t userfaultfd_read_iter(struct kiocb *iocb, struct iov_iter *to) -{ - struct file *file = iocb->ki_filp; - struct userfaultfd_ctx *ctx = file->private_data; - ssize_t _ret, ret = 0; - struct uffd_msg msg; - struct inode *inode = file_inode(file); - bool no_wait; - - if (!userfaultfd_is_initialized(ctx)) - return -EINVAL; - - no_wait = file->f_flags & O_NONBLOCK || iocb->ki_flags & IOCB_NOWAIT; - for (;;) { - if (iov_iter_count(to) < sizeof(msg)) - return ret ? ret : -EINVAL; - _ret = userfaultfd_ctx_read(ctx, no_wait, &msg, inode); - if (_ret < 0) - return ret ? ret : _ret; - _ret = !copy_to_iter_full(&msg, sizeof(msg), to); - if (_ret) - return ret ? ret : -EFAULT; - ret += sizeof(msg); - /* - * Allow to read more than one fault at time but only - * block if waiting for the very first one. - */ - no_wait = true; - } -} - -static void __wake_userfault(struct userfaultfd_ctx *ctx, - struct userfaultfd_wake_range *range) -{ - spin_lock_irq(&ctx->fault_pending_wqh.lock); - /* wake all in the range and autoremove */ - if (waitqueue_active(&ctx->fault_pending_wqh)) - __wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, - range); - if (waitqueue_active(&ctx->fault_wqh)) - __wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, range); - spin_unlock_irq(&ctx->fault_pending_wqh.lock); -} - -static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx, - struct userfaultfd_wake_range *range) -{ - unsigned seq; - bool need_wakeup; - - /* - * To be sure waitqueue_active() is not reordered by the CPU - * before the pagetable update, use an explicit SMP memory - * barrier here. PT lock release or mmap_read_unlock(mm) still - * have release semantics that can allow the - * waitqueue_active() to be reordered before the pte update. - */ - smp_mb(); - - /* - * Use waitqueue_active because it's very frequent to - * change the address space atomically even if there are no - * userfaults yet. So we take the spinlock only when we're - * sure we've userfaults to wake. - */ - do { - seq = read_seqcount_begin(&ctx->refile_seq); - need_wakeup = waitqueue_active(&ctx->fault_pending_wqh) || - waitqueue_active(&ctx->fault_wqh); - cond_resched(); - } while (read_seqcount_retry(&ctx->refile_seq, seq)); - if (need_wakeup) - __wake_userfault(ctx, range); -} - -static __always_inline int validate_unaligned_range( - struct mm_struct *mm, __u64 start, __u64 len) -{ - __u64 task_size = mm->task_size; - - if (len & ~PAGE_MASK) - return -EINVAL; - if (!len) - return -EINVAL; - if (start >= task_size) - return -EINVAL; - if (len > task_size - start) - return -EINVAL; - if (start + len <= start) - return -EINVAL; - return 0; -} - -static __always_inline int validate_range(struct mm_struct *mm, - __u64 start, __u64 len) -{ - if (start & ~PAGE_MASK) - return -EINVAL; - - return validate_unaligned_range(mm, start, len); -} - -static int userfaultfd_register(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - struct mm_struct *mm = ctx->mm; - struct vm_area_struct *vma, *cur; - int ret; - struct uffdio_register uffdio_register; - struct uffdio_register __user *user_uffdio_register; - vm_flags_t vm_flags; - bool found; - bool basic_ioctls; - unsigned long start, end; - struct vma_iterator vmi; - bool wp_async = userfaultfd_wp_async_ctx(ctx); - - user_uffdio_register = (struct uffdio_register __user *) arg; - - ret = -EFAULT; - if (copy_from_user(&uffdio_register, user_uffdio_register, - sizeof(uffdio_register)-sizeof(__u64))) - goto out; - - ret = -EINVAL; - if (!uffdio_register.mode) - goto out; - if (uffdio_register.mode & ~UFFD_API_REGISTER_MODES) - goto out; - vm_flags = 0; - if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING) - vm_flags |= VM_UFFD_MISSING; - if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP) { - if (!pgtable_supports_uffd_wp()) - goto out; - - vm_flags |= VM_UFFD_WP; - } - if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) { -#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR - goto out; -#endif - vm_flags |= VM_UFFD_MINOR; - } - - ret = validate_range(mm, uffdio_register.range.start, - uffdio_register.range.len); - if (ret) - goto out; - - start = uffdio_register.range.start; - end = start + uffdio_register.range.len; - - ret = -ENOMEM; - if (!mmget_not_zero(mm)) - goto out; - - ret = -EINVAL; - mmap_write_lock(mm); - vma_iter_init(&vmi, mm, start); - vma = vma_find(&vmi, end); - if (!vma) - goto out_unlock; - - /* - * If the first vma contains huge pages, make sure start address - * is aligned to huge page size. - */ - if (is_vm_hugetlb_page(vma)) { - unsigned long vma_hpagesize = vma_kernel_pagesize(vma); - - if (start & (vma_hpagesize - 1)) - goto out_unlock; - } - - /* - * Search for not compatible vmas. - */ - found = false; - basic_ioctls = false; - cur = vma; - do { - cond_resched(); - - VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ - !!(cur->vm_flags & __VM_UFFD_FLAGS)); - - /* check not compatible vmas */ - ret = -EINVAL; - if (!vma_can_userfault(cur, vm_flags, wp_async)) - goto out_unlock; - - /* - * UFFDIO_COPY will fill file holes even without - * PROT_WRITE. This check enforces that if this is a - * MAP_SHARED, the process has write permission to the backing - * file. If VM_MAYWRITE is set it also enforces that on a - * MAP_SHARED vma: there is no F_WRITE_SEAL and no further - * F_WRITE_SEAL can be taken until the vma is destroyed. - */ - ret = -EPERM; - if (unlikely(!(cur->vm_flags & VM_MAYWRITE))) - goto out_unlock; - - /* - * If this vma contains ending address, and huge pages - * check alignment. - */ - if (is_vm_hugetlb_page(cur) && end <= cur->vm_end && - end > cur->vm_start) { - unsigned long vma_hpagesize = vma_kernel_pagesize(cur); - - ret = -EINVAL; - - if (end & (vma_hpagesize - 1)) - goto out_unlock; - } - if ((vm_flags & VM_UFFD_WP) && !(cur->vm_flags & VM_MAYWRITE)) - goto out_unlock; - - /* - * Check that this vma isn't already owned by a - * different userfaultfd. We can't allow more than one - * userfaultfd to own a single vma simultaneously or we - * wouldn't know which one to deliver the userfaults to. - */ - ret = -EBUSY; - if (cur->vm_userfaultfd_ctx.ctx && - cur->vm_userfaultfd_ctx.ctx != ctx) - goto out_unlock; - - /* - * Note vmas containing huge pages - */ - if (is_vm_hugetlb_page(cur)) - basic_ioctls = true; - - found = true; - } for_each_vma_range(vmi, cur, end); - VM_WARN_ON_ONCE(!found); - - ret = userfaultfd_register_range(ctx, vma, vm_flags, start, end, - wp_async); - -out_unlock: - mmap_write_unlock(mm); - mmput(mm); - if (!ret) { - __u64 ioctls_out; - - ioctls_out = basic_ioctls ? UFFD_API_RANGE_IOCTLS_BASIC : - UFFD_API_RANGE_IOCTLS; - - /* - * Declare the WP ioctl only if the WP mode is - * specified and all checks passed with the range - */ - if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_WP)) - ioctls_out &= ~((__u64)1 << _UFFDIO_WRITEPROTECT); - - /* CONTINUE ioctl is only supported for MINOR ranges. */ - if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR)) - ioctls_out &= ~((__u64)1 << _UFFDIO_CONTINUE); - - /* - * Now that we scanned all vmas we can already tell - * userland which ioctls methods are guaranteed to - * succeed on this range. - */ - if (put_user(ioctls_out, &user_uffdio_register->ioctls)) - ret = -EFAULT; - } -out: - return ret; -} - -static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - struct mm_struct *mm = ctx->mm; - struct vm_area_struct *vma, *prev, *cur; - int ret; - struct uffdio_range uffdio_unregister; - bool found; - unsigned long start, end, vma_end; - const void __user *buf = (void __user *)arg; - struct vma_iterator vmi; - bool wp_async = userfaultfd_wp_async_ctx(ctx); - - ret = -EFAULT; - if (copy_from_user(&uffdio_unregister, buf, sizeof(uffdio_unregister))) - goto out; - - ret = validate_range(mm, uffdio_unregister.start, - uffdio_unregister.len); - if (ret) - goto out; - - start = uffdio_unregister.start; - end = start + uffdio_unregister.len; - - ret = -ENOMEM; - if (!mmget_not_zero(mm)) - goto out; - - mmap_write_lock(mm); - ret = -EINVAL; - vma_iter_init(&vmi, mm, start); - vma = vma_find(&vmi, end); - if (!vma) - goto out_unlock; - - /* - * If the first vma contains huge pages, make sure start address - * is aligned to huge page size. - */ - if (is_vm_hugetlb_page(vma)) { - unsigned long vma_hpagesize = vma_kernel_pagesize(vma); - - if (start & (vma_hpagesize - 1)) - goto out_unlock; - } - - /* - * Search for not compatible vmas. - */ - found = false; - cur = vma; - do { - cond_resched(); - - VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ - !!(cur->vm_flags & __VM_UFFD_FLAGS)); - - /* - * Prevent unregistering through a different userfaultfd than - * the one used for registration. - */ - if (cur->vm_userfaultfd_ctx.ctx && - cur->vm_userfaultfd_ctx.ctx != ctx) - goto out_unlock; - - /* - * Check not compatible vmas, not strictly required - * here as not compatible vmas cannot have an - * userfaultfd_ctx registered on them, but this - * provides for more strict behavior to notice - * unregistration errors. - */ - if (!vma_can_userfault(cur, cur->vm_flags, wp_async)) - goto out_unlock; - - found = true; - } for_each_vma_range(vmi, cur, end); - VM_WARN_ON_ONCE(!found); - - vma_iter_set(&vmi, start); - prev = vma_prev(&vmi); - if (vma->vm_start < start) - prev = vma; - - ret = 0; - for_each_vma_range(vmi, vma, end) { - cond_resched(); - - /* VMA not registered with userfaultfd. */ - if (!vma->vm_userfaultfd_ctx.ctx) - goto skip; - - VM_WARN_ON_ONCE(vma->vm_userfaultfd_ctx.ctx != ctx); - VM_WARN_ON_ONCE(!vma_can_userfault(vma, vma->vm_flags, wp_async)); - VM_WARN_ON_ONCE(!(vma->vm_flags & VM_MAYWRITE)); - - if (vma->vm_start > start) - start = vma->vm_start; - vma_end = min(end, vma->vm_end); - - if (userfaultfd_missing(vma)) { - /* - * Wake any concurrent pending userfault while - * we unregister, so they will not hang - * permanently and it avoids userland to call - * UFFDIO_WAKE explicitly. - */ - struct userfaultfd_wake_range range; - range.start = start; - range.len = vma_end - start; - wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range); - } - - vma = userfaultfd_clear_vma(&vmi, prev, vma, - start, vma_end); - if (IS_ERR(vma)) { - ret = PTR_ERR(vma); - break; - } - - skip: - prev = vma; - start = vma->vm_end; - } - -out_unlock: - mmap_write_unlock(mm); - mmput(mm); -out: - return ret; -} - -/* - * userfaultfd_wake may be used in combination with the - * UFFDIO_*_MODE_DONTWAKE to wakeup userfaults in batches. - */ -static int userfaultfd_wake(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - int ret; - struct uffdio_range uffdio_wake; - struct userfaultfd_wake_range range; - const void __user *buf = (void __user *)arg; - - ret = -EFAULT; - if (copy_from_user(&uffdio_wake, buf, sizeof(uffdio_wake))) - goto out; - - ret = validate_range(ctx->mm, uffdio_wake.start, uffdio_wake.len); - if (ret) - goto out; - - range.start = uffdio_wake.start; - range.len = uffdio_wake.len; - - /* - * len == 0 means wake all and we don't want to wake all here, - * so check it again to be sure. - */ - VM_WARN_ON_ONCE(!range.len); - - wake_userfault(ctx, &range); - ret = 0; - -out: - return ret; -} - -static int userfaultfd_copy(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - __s64 ret; - struct uffdio_copy uffdio_copy; - struct uffdio_copy __user *user_uffdio_copy; - struct userfaultfd_wake_range range; - uffd_flags_t flags = 0; - - user_uffdio_copy = (struct uffdio_copy __user *) arg; - - ret = -EAGAIN; - if (unlikely(atomic_read(&ctx->mmap_changing))) { - if (unlikely(put_user(ret, &user_uffdio_copy->copy))) - return -EFAULT; - goto out; - } - - ret = -EFAULT; - if (copy_from_user(&uffdio_copy, user_uffdio_copy, - /* don't copy "copy" last field */ - sizeof(uffdio_copy)-sizeof(__s64))) - goto out; - - ret = validate_unaligned_range(ctx->mm, uffdio_copy.src, - uffdio_copy.len); - if (ret) - goto out; - ret = validate_range(ctx->mm, uffdio_copy.dst, uffdio_copy.len); - if (ret) - goto out; - - ret = -EINVAL; - if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|UFFDIO_COPY_MODE_WP)) - goto out; - if (uffdio_copy.mode & UFFDIO_COPY_MODE_WP) - flags |= MFILL_ATOMIC_WP; - if (mmget_not_zero(ctx->mm)) { - ret = mfill_atomic_copy(ctx, uffdio_copy.dst, uffdio_copy.src, - uffdio_copy.len, flags); - mmput(ctx->mm); - } else { - return -ESRCH; - } - if (unlikely(put_user(ret, &user_uffdio_copy->copy))) - return -EFAULT; - if (ret < 0) - goto out; - VM_WARN_ON_ONCE(!ret); - /* len == 0 would wake all */ - range.len = ret; - if (!(uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE)) { - range.start = uffdio_copy.dst; - wake_userfault(ctx, &range); - } - ret = range.len == uffdio_copy.len ? 0 : -EAGAIN; -out: - return ret; -} - -static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - __s64 ret; - struct uffdio_zeropage uffdio_zeropage; - struct uffdio_zeropage __user *user_uffdio_zeropage; - struct userfaultfd_wake_range range; - - user_uffdio_zeropage = (struct uffdio_zeropage __user *) arg; - - ret = -EAGAIN; - if (unlikely(atomic_read(&ctx->mmap_changing))) { - if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage))) - return -EFAULT; - goto out; - } - - ret = -EFAULT; - if (copy_from_user(&uffdio_zeropage, user_uffdio_zeropage, - /* don't copy "zeropage" last field */ - sizeof(uffdio_zeropage)-sizeof(__s64))) - goto out; - - ret = validate_range(ctx->mm, uffdio_zeropage.range.start, - uffdio_zeropage.range.len); - if (ret) - goto out; - ret = -EINVAL; - if (uffdio_zeropage.mode & ~UFFDIO_ZEROPAGE_MODE_DONTWAKE) - goto out; - - if (mmget_not_zero(ctx->mm)) { - ret = mfill_atomic_zeropage(ctx, uffdio_zeropage.range.start, - uffdio_zeropage.range.len); - mmput(ctx->mm); - } else { - return -ESRCH; - } - if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage))) - return -EFAULT; - if (ret < 0) - goto out; - /* len == 0 would wake all */ - VM_WARN_ON_ONCE(!ret); - range.len = ret; - if (!(uffdio_zeropage.mode & UFFDIO_ZEROPAGE_MODE_DONTWAKE)) { - range.start = uffdio_zeropage.range.start; - wake_userfault(ctx, &range); - } - ret = range.len == uffdio_zeropage.range.len ? 0 : -EAGAIN; -out: - return ret; -} - -static int userfaultfd_writeprotect(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - int ret; - struct uffdio_writeprotect uffdio_wp; - struct uffdio_writeprotect __user *user_uffdio_wp; - struct userfaultfd_wake_range range; - bool mode_wp, mode_dontwake; - - if (atomic_read(&ctx->mmap_changing)) - return -EAGAIN; - - user_uffdio_wp = (struct uffdio_writeprotect __user *) arg; - - if (copy_from_user(&uffdio_wp, user_uffdio_wp, - sizeof(struct uffdio_writeprotect))) - return -EFAULT; - - ret = validate_range(ctx->mm, uffdio_wp.range.start, - uffdio_wp.range.len); - if (ret) - return ret; - - if (uffdio_wp.mode & ~(UFFDIO_WRITEPROTECT_MODE_DONTWAKE | - UFFDIO_WRITEPROTECT_MODE_WP)) - return -EINVAL; - - mode_wp = uffdio_wp.mode & UFFDIO_WRITEPROTECT_MODE_WP; - mode_dontwake = uffdio_wp.mode & UFFDIO_WRITEPROTECT_MODE_DONTWAKE; - - if (mode_wp && mode_dontwake) - return -EINVAL; - - if (mmget_not_zero(ctx->mm)) { - ret = mwriteprotect_range(ctx, uffdio_wp.range.start, - uffdio_wp.range.len, mode_wp); - mmput(ctx->mm); - } else { - return -ESRCH; - } - - if (ret) - return ret; - - if (!mode_wp && !mode_dontwake) { - range.start = uffdio_wp.range.start; - range.len = uffdio_wp.range.len; - wake_userfault(ctx, &range); - } - return ret; -} - -static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg) -{ - __s64 ret; - struct uffdio_continue uffdio_continue; - struct uffdio_continue __user *user_uffdio_continue; - struct userfaultfd_wake_range range; - uffd_flags_t flags = 0; - - user_uffdio_continue = (struct uffdio_continue __user *)arg; - - ret = -EAGAIN; - if (unlikely(atomic_read(&ctx->mmap_changing))) { - if (unlikely(put_user(ret, &user_uffdio_continue->mapped))) - return -EFAULT; - goto out; - } - - ret = -EFAULT; - if (copy_from_user(&uffdio_continue, user_uffdio_continue, - /* don't copy the output fields */ - sizeof(uffdio_continue) - (sizeof(__s64)))) - goto out; - - ret = validate_range(ctx->mm, uffdio_continue.range.start, - uffdio_continue.range.len); - if (ret) - goto out; - - ret = -EINVAL; - if (uffdio_continue.mode & ~(UFFDIO_CONTINUE_MODE_DONTWAKE | - UFFDIO_CONTINUE_MODE_WP)) - goto out; - if (uffdio_continue.mode & UFFDIO_CONTINUE_MODE_WP) - flags |= MFILL_ATOMIC_WP; - - if (mmget_not_zero(ctx->mm)) { - ret = mfill_atomic_continue(ctx, uffdio_continue.range.start, - uffdio_continue.range.len, flags); - mmput(ctx->mm); - } else { - return -ESRCH; - } - - if (unlikely(put_user(ret, &user_uffdio_continue->mapped))) - return -EFAULT; - if (ret < 0) - goto out; - - /* len == 0 would wake all */ - VM_WARN_ON_ONCE(!ret); - range.len = ret; - if (!(uffdio_continue.mode & UFFDIO_CONTINUE_MODE_DONTWAKE)) { - range.start = uffdio_continue.range.start; - wake_userfault(ctx, &range); - } - ret = range.len == uffdio_continue.range.len ? 0 : -EAGAIN; - -out: - return ret; -} - -static inline int userfaultfd_poison(struct userfaultfd_ctx *ctx, unsigned long arg) -{ - __s64 ret; - struct uffdio_poison uffdio_poison; - struct uffdio_poison __user *user_uffdio_poison; - struct userfaultfd_wake_range range; - - user_uffdio_poison = (struct uffdio_poison __user *)arg; - - ret = -EAGAIN; - if (unlikely(atomic_read(&ctx->mmap_changing))) { - if (unlikely(put_user(ret, &user_uffdio_poison->updated))) - return -EFAULT; - goto out; - } - - ret = -EFAULT; - if (copy_from_user(&uffdio_poison, user_uffdio_poison, - /* don't copy the output fields */ - sizeof(uffdio_poison) - (sizeof(__s64)))) - goto out; - - ret = validate_range(ctx->mm, uffdio_poison.range.start, - uffdio_poison.range.len); - if (ret) - goto out; - - ret = -EINVAL; - if (uffdio_poison.mode & ~UFFDIO_POISON_MODE_DONTWAKE) - goto out; - - if (mmget_not_zero(ctx->mm)) { - ret = mfill_atomic_poison(ctx, uffdio_poison.range.start, - uffdio_poison.range.len, 0); - mmput(ctx->mm); - } else { - return -ESRCH; - } - - if (unlikely(put_user(ret, &user_uffdio_poison->updated))) - return -EFAULT; - if (ret < 0) - goto out; - - /* len == 0 would wake all */ - VM_WARN_ON_ONCE(!ret); - range.len = ret; - if (!(uffdio_poison.mode & UFFDIO_POISON_MODE_DONTWAKE)) { - range.start = uffdio_poison.range.start; - wake_userfault(ctx, &range); - } - ret = range.len == uffdio_poison.range.len ? 0 : -EAGAIN; - -out: - return ret; -} - -bool userfaultfd_wp_async(struct vm_area_struct *vma) -{ - return userfaultfd_wp_async_ctx(vma->vm_userfaultfd_ctx.ctx); -} - -static inline unsigned int uffd_ctx_features(__u64 user_features) -{ - /* - * For the current set of features the bits just coincide. Set - * UFFD_FEATURE_INITIALIZED to mark the features as enabled. - */ - return (unsigned int)user_features | UFFD_FEATURE_INITIALIZED; -} - -static int userfaultfd_move(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - __s64 ret; - struct uffdio_move uffdio_move; - struct uffdio_move __user *user_uffdio_move; - struct userfaultfd_wake_range range; - struct mm_struct *mm = ctx->mm; - - user_uffdio_move = (struct uffdio_move __user *) arg; - - ret = -EAGAIN; - if (unlikely(atomic_read(&ctx->mmap_changing))) { - if (unlikely(put_user(ret, &user_uffdio_move->move))) - return -EFAULT; - goto out; - } - - if (copy_from_user(&uffdio_move, user_uffdio_move, - /* don't copy "move" last field */ - sizeof(uffdio_move)-sizeof(__s64))) - return -EFAULT; - - /* Do not allow cross-mm moves. */ - if (mm != current->mm) - return -EINVAL; - - ret = validate_range(mm, uffdio_move.dst, uffdio_move.len); - if (ret) - return ret; - - ret = validate_range(mm, uffdio_move.src, uffdio_move.len); - if (ret) - return ret; - - if (uffdio_move.mode & ~(UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES| - UFFDIO_MOVE_MODE_DONTWAKE)) - return -EINVAL; - - if (mmget_not_zero(mm)) { - ret = move_pages(ctx, uffdio_move.dst, uffdio_move.src, - uffdio_move.len, uffdio_move.mode); - mmput(mm); - } else { - return -ESRCH; - } - - if (unlikely(put_user(ret, &user_uffdio_move->move))) - return -EFAULT; - if (ret < 0) - goto out; - - /* len == 0 would wake all */ - VM_WARN_ON(!ret); - range.len = ret; - if (!(uffdio_move.mode & UFFDIO_MOVE_MODE_DONTWAKE)) { - range.start = uffdio_move.dst; - wake_userfault(ctx, &range); - } - ret = range.len == uffdio_move.len ? 0 : -EAGAIN; - -out: - return ret; -} - -/* - * userland asks for a certain API version and we return which bits - * and ioctl commands are implemented in this kernel for such API - * version or -EINVAL if unknown. - */ -static int userfaultfd_api(struct userfaultfd_ctx *ctx, - unsigned long arg) -{ - struct uffdio_api uffdio_api; - void __user *buf = (void __user *)arg; - unsigned int ctx_features; - int ret; - __u64 features; - - ret = -EFAULT; - if (copy_from_user(&uffdio_api, buf, sizeof(uffdio_api))) - goto out; - features = uffdio_api.features; - ret = -EINVAL; - if (uffdio_api.api != UFFD_API) - goto err_out; - ret = -EPERM; - if ((features & UFFD_FEATURE_EVENT_FORK) && !capable(CAP_SYS_PTRACE)) - goto err_out; - - /* WP_ASYNC relies on WP_UNPOPULATED, choose it unconditionally */ - if (features & UFFD_FEATURE_WP_ASYNC) - features |= UFFD_FEATURE_WP_UNPOPULATED; - - /* report all available features and ioctls to userland */ - uffdio_api.features = UFFD_API_FEATURES; -#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR - uffdio_api.features &= - ~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM); -#endif - if (!pgtable_supports_uffd_wp()) - uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP; - - if (!uffd_supports_wp_marker()) { - uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM; - uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED; - uffdio_api.features &= ~UFFD_FEATURE_WP_ASYNC; - } - - ret = -EINVAL; - if (features & ~uffdio_api.features) - goto err_out; - - uffdio_api.ioctls = UFFD_API_IOCTLS; - ret = -EFAULT; - if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api))) - goto out; - - /* only enable the requested features for this uffd context */ - ctx_features = uffd_ctx_features(features); - ret = -EINVAL; - if (cmpxchg(&ctx->features, 0, ctx_features) != 0) - goto err_out; - - ret = 0; -out: - return ret; -err_out: - memset(&uffdio_api, 0, sizeof(uffdio_api)); - if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api))) - ret = -EFAULT; - goto out; -} - -static long userfaultfd_ioctl(struct file *file, unsigned cmd, - unsigned long arg) -{ - int ret = -EINVAL; - struct userfaultfd_ctx *ctx = file->private_data; - - if (cmd != UFFDIO_API && !userfaultfd_is_initialized(ctx)) - return -EINVAL; - - switch(cmd) { - case UFFDIO_API: - ret = userfaultfd_api(ctx, arg); - break; - case UFFDIO_REGISTER: - ret = userfaultfd_register(ctx, arg); - break; - case UFFDIO_UNREGISTER: - ret = userfaultfd_unregister(ctx, arg); - break; - case UFFDIO_WAKE: - ret = userfaultfd_wake(ctx, arg); - break; - case UFFDIO_COPY: - ret = userfaultfd_copy(ctx, arg); - break; - case UFFDIO_ZEROPAGE: - ret = userfaultfd_zeropage(ctx, arg); - break; - case UFFDIO_MOVE: - ret = userfaultfd_move(ctx, arg); - break; - case UFFDIO_WRITEPROTECT: - ret = userfaultfd_writeprotect(ctx, arg); - break; - case UFFDIO_CONTINUE: - ret = userfaultfd_continue(ctx, arg); - break; - case UFFDIO_POISON: - ret = userfaultfd_poison(ctx, arg); - break; - } - return ret; -} - -#ifdef CONFIG_PROC_FS -static void userfaultfd_show_fdinfo(struct seq_file *m, struct file *f) -{ - struct userfaultfd_ctx *ctx = f->private_data; - wait_queue_entry_t *wq; - unsigned long pending = 0, total = 0; - - spin_lock_irq(&ctx->fault_pending_wqh.lock); - list_for_each_entry(wq, &ctx->fault_pending_wqh.head, entry) { - pending++; - total++; - } - list_for_each_entry(wq, &ctx->fault_wqh.head, entry) { - total++; - } - spin_unlock_irq(&ctx->fault_pending_wqh.lock); - - /* - * If more protocols will be added, there will be all shown - * separated by a space. Like this: - * protocols: aa:... bb:... - */ - seq_printf(m, "pending:\t%lu\ntotal:\t%lu\nAPI:\t%Lx:%x:%Lx\n", - pending, total, UFFD_API, ctx->features, - UFFD_API_IOCTLS|UFFD_API_RANGE_IOCTLS); -} -#endif - -static const struct file_operations userfaultfd_fops = { -#ifdef CONFIG_PROC_FS - .show_fdinfo = userfaultfd_show_fdinfo, -#endif - .release = userfaultfd_release, - .poll = userfaultfd_poll, - .read_iter = userfaultfd_read_iter, - .unlocked_ioctl = userfaultfd_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .llseek = noop_llseek, -}; - -static void init_once_userfaultfd_ctx(void *mem) -{ - struct userfaultfd_ctx *ctx = (struct userfaultfd_ctx *) mem; - - init_waitqueue_head(&ctx->fault_pending_wqh); - init_waitqueue_head(&ctx->fault_wqh); - init_waitqueue_head(&ctx->event_wqh); - init_waitqueue_head(&ctx->fd_wqh); - seqcount_spinlock_init(&ctx->refile_seq, &ctx->fault_pending_wqh.lock); -} - -static int new_userfaultfd(int flags) -{ - struct userfaultfd_ctx *ctx __free(kfree) = NULL; - - VM_WARN_ON_ONCE(!current->mm); - - /* Check the UFFD_* constants for consistency. */ - BUILD_BUG_ON(UFFD_USER_MODE_ONLY & UFFD_SHARED_FCNTL_FLAGS); - - if (flags & ~(UFFD_SHARED_FCNTL_FLAGS | UFFD_USER_MODE_ONLY)) - return -EINVAL; - - ctx = kmem_cache_alloc(userfaultfd_ctx_cachep, GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - refcount_set(&ctx->refcount, 1); - ctx->flags = flags; - ctx->features = 0; - ctx->released = false; - init_rwsem(&ctx->map_changing_lock); - atomic_set(&ctx->mmap_changing, 0); - ctx->mm = current->mm; - - FD_PREPARE(fdf, flags & UFFD_SHARED_FCNTL_FLAGS, - anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx, - O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS), - NULL)); - if (fdf.err) - return fdf.err; - - /* prevent the mm struct to be freed */ - mmgrab(ctx->mm); - fd_prepare_file(fdf)->f_mode |= FMODE_NOWAIT; - retain_and_null_ptr(ctx); - return fd_publish(fdf); -} - -static inline bool userfaultfd_syscall_allowed(int flags) -{ - /* Userspace-only page faults are always allowed */ - if (flags & UFFD_USER_MODE_ONLY) - return true; - - /* - * The user is requesting a userfaultfd which can handle kernel faults. - * Privileged users are always allowed to do this. - */ - if (capable(CAP_SYS_PTRACE)) - return true; - - /* Otherwise, access to kernel fault handling is sysctl controlled. */ - return sysctl_unprivileged_userfaultfd; -} - -SYSCALL_DEFINE1(userfaultfd, int, flags) -{ - if (!userfaultfd_syscall_allowed(flags)) - return -EPERM; - - return new_userfaultfd(flags); -} - -static long userfaultfd_dev_ioctl(struct file *file, unsigned int cmd, unsigned long flags) -{ - if (cmd != USERFAULTFD_IOC_NEW) - return -EINVAL; - - return new_userfaultfd(flags); -} - -static const struct file_operations userfaultfd_dev_fops = { - .unlocked_ioctl = userfaultfd_dev_ioctl, - .compat_ioctl = userfaultfd_dev_ioctl, - .owner = THIS_MODULE, - .llseek = noop_llseek, -}; - -static struct miscdevice userfaultfd_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "userfaultfd", - .fops = &userfaultfd_dev_fops -}; - -static int __init userfaultfd_init(void) -{ - int ret; - - ret = misc_register(&userfaultfd_misc); - if (ret) - return ret; - - userfaultfd_ctx_cachep = kmem_cache_create("userfaultfd_ctx_cache", - sizeof(struct userfaultfd_ctx), - 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, - init_once_userfaultfd_ctx); -#ifdef CONFIG_SYSCTL - register_sysctl_init("vm", vm_userfaultfd_table); -#endif - return 0; -} -__initcall(userfaultfd_init); diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 80cc8be5725f..74af5682f3fb 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2,7 +2,12 @@ /* * mm/userfaultfd.c * + * Copyright (C) 2007 Davide Libenzi + * Copyright (C) 2008-2009 Red Hat, Inc. * Copyright (C) 2015 Red Hat, Inc. + * + * Some part derived from fs/eventfd.c (anon inode setup) and + * mm/ksm.c (mm hashing). */ #include @@ -14,6 +19,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -2305,3 +2321,2202 @@ void userfaultfd_release_all(struct mm_struct *mm, mmap_write_unlock(mm); mmput(mm); } + +static int sysctl_unprivileged_userfaultfd __read_mostly; + +#ifdef CONFIG_SYSCTL +static const struct ctl_table vm_userfaultfd_table[] = { + { + .procname = "unprivileged_userfaultfd", + .data = &sysctl_unprivileged_userfaultfd, + .maxlen = sizeof(sysctl_unprivileged_userfaultfd), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +}; +#endif + +static struct kmem_cache *userfaultfd_ctx_cachep __ro_after_init; + +struct userfaultfd_fork_ctx { + struct userfaultfd_ctx *orig; + struct userfaultfd_ctx *new; + struct list_head list; +}; + +struct userfaultfd_unmap_ctx { + struct userfaultfd_ctx *ctx; + unsigned long start; + unsigned long end; + struct list_head list; +}; + +struct userfaultfd_wait_queue { + struct uffd_msg msg; + wait_queue_entry_t wq; + struct userfaultfd_ctx *ctx; + bool waken; +}; + +struct userfaultfd_wake_range { + unsigned long start; + unsigned long len; +}; + +/* internal indication that UFFD_API ioctl was successfully executed */ +#define UFFD_FEATURE_INITIALIZED (1u << 31) + +static bool userfaultfd_is_initialized(struct userfaultfd_ctx *ctx) +{ + return ctx->features & UFFD_FEATURE_INITIALIZED; +} + +static bool userfaultfd_wp_async_ctx(struct userfaultfd_ctx *ctx) +{ + return ctx && (ctx->features & UFFD_FEATURE_WP_ASYNC); +} + +/* + * Whether WP_UNPOPULATED is enabled on the uffd context. It is only + * meaningful when userfaultfd_wp()==true on the vma and when it's + * anonymous. + */ +bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma) +{ + struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; + + if (!ctx) + return false; + + return ctx->features & UFFD_FEATURE_WP_UNPOPULATED; +} + +static int userfaultfd_wake_function(wait_queue_entry_t *wq, unsigned mode, + int wake_flags, void *key) +{ + struct userfaultfd_wake_range *range = key; + int ret; + struct userfaultfd_wait_queue *uwq; + unsigned long start, len; + + uwq = container_of(wq, struct userfaultfd_wait_queue, wq); + ret = 0; + /* len == 0 means wake all */ + start = range->start; + len = range->len; + if (len && (start > uwq->msg.arg.pagefault.address || + start + len <= uwq->msg.arg.pagefault.address)) + goto out; + WRITE_ONCE(uwq->waken, true); + /* + * The Program-Order guarantees provided by the scheduler + * ensure uwq->waken is visible before the task is woken. + */ + ret = wake_up_state(wq->private, mode); + if (ret) { + /* + * Wake only once, autoremove behavior. + * + * After the effect of list_del_init is visible to the other + * CPUs, the waitqueue may disappear from under us, see the + * !list_empty_careful() in handle_userfault(). + * + * try_to_wake_up() has an implicit smp_mb(), and the + * wq->private is read before calling the extern function + * "wake_up_state" (which in turns calls try_to_wake_up). + */ + list_del_init(&wq->entry); + } +out: + return ret; +} + +/** + * userfaultfd_ctx_get - Acquires a reference to the internal userfaultfd + * context. + * @ctx: [in] Pointer to the userfaultfd context. + */ +static void userfaultfd_ctx_get(struct userfaultfd_ctx *ctx) +{ + refcount_inc(&ctx->refcount); +} + +/** + * userfaultfd_ctx_put - Releases a reference to the internal userfaultfd + * context. + * @ctx: [in] Pointer to userfaultfd context. + * + * The userfaultfd context reference must have been previously acquired either + * with userfaultfd_ctx_get() or userfaultfd_ctx_fdget(). + */ +static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx) +{ + if (refcount_dec_and_test(&ctx->refcount)) { + VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_pending_wqh.lock)); + VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_pending_wqh)); + VM_WARN_ON_ONCE(spin_is_locked(&ctx->fault_wqh.lock)); + VM_WARN_ON_ONCE(waitqueue_active(&ctx->fault_wqh)); + VM_WARN_ON_ONCE(spin_is_locked(&ctx->event_wqh.lock)); + VM_WARN_ON_ONCE(waitqueue_active(&ctx->event_wqh)); + VM_WARN_ON_ONCE(spin_is_locked(&ctx->fd_wqh.lock)); + VM_WARN_ON_ONCE(waitqueue_active(&ctx->fd_wqh)); + mmdrop(ctx->mm); + kmem_cache_free(userfaultfd_ctx_cachep, ctx); + } +} + +static inline void msg_init(struct uffd_msg *msg) +{ + BUILD_BUG_ON(sizeof(struct uffd_msg) != 32); + /* + * Must use memset to zero out the paddings or kernel data is + * leaked to userland. + */ + memset(msg, 0, sizeof(struct uffd_msg)); +} + +static inline struct uffd_msg userfault_msg(unsigned long address, + unsigned long real_address, + unsigned int flags, + unsigned long reason, + unsigned int features) +{ + struct uffd_msg msg; + + msg_init(&msg); + msg.event = UFFD_EVENT_PAGEFAULT; + + msg.arg.pagefault.address = (features & UFFD_FEATURE_EXACT_ADDRESS) ? + real_address : address; + + /* + * These flags indicate why the userfault occurred: + * - UFFD_PAGEFAULT_FLAG_WP indicates a write protect fault. + * - UFFD_PAGEFAULT_FLAG_MINOR indicates a minor fault. + * - Neither of these flags being set indicates a MISSING fault. + * + * Separately, UFFD_PAGEFAULT_FLAG_WRITE indicates it was a write + * fault. Otherwise, it was a read fault. + */ + if (flags & FAULT_FLAG_WRITE) + msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WRITE; + if (reason & VM_UFFD_WP) + msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WP; + if (reason & VM_UFFD_MINOR) + msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_MINOR; + if (features & UFFD_FEATURE_THREAD_ID) + msg.arg.pagefault.feat.ptid = task_pid_vnr(current); + return msg; +} + +#ifdef CONFIG_HUGETLB_PAGE +/* + * Same functionality as userfaultfd_must_wait below with modifications for + * hugepmd ranges. + */ +static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, + struct vm_fault *vmf, + unsigned long reason) +{ + struct vm_area_struct *vma = vmf->vma; + pte_t *ptep, pte; + + assert_fault_locked(vmf); + + ptep = hugetlb_walk(vma, vmf->address, vma_mmu_pagesize(vma)); + if (!ptep) + return true; + + pte = huge_ptep_get(vma->vm_mm, vmf->address, ptep); + + /* + * Lockless access: we're in a wait_event so it's ok if it + * changes under us. + */ + + /* Entry is still missing, wait for userspace to resolve the fault. */ + if (huge_pte_none(pte)) + return true; + /* UFFD PTE markers require userspace to resolve the fault. */ + if (pte_is_uffd_marker(pte)) + return true; + /* + * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to + * resolve the fault. + */ + if (!huge_pte_write(pte) && (reason & VM_UFFD_WP)) + return true; + + return false; +} +#else +static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, + struct vm_fault *vmf, + unsigned long reason) +{ + /* Should never get here. */ + VM_WARN_ON_ONCE(1); + return false; +} +#endif /* CONFIG_HUGETLB_PAGE */ + +/* + * Verify the pagetables are still not ok after having registered into + * the fault_pending_wqh to avoid userland having to UFFDIO_WAKE any + * userfault that has already been resolved, if userfaultfd_read_iter and + * UFFDIO_COPY|ZEROPAGE are being run simultaneously on two different + * threads. + */ +static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx, + struct vm_fault *vmf, + unsigned long reason) +{ + struct mm_struct *mm = ctx->mm; + unsigned long address = vmf->address; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd, _pmd; + pte_t *pte; + pte_t ptent; + bool ret; + + assert_fault_locked(vmf); + + pgd = pgd_offset(mm, address); + if (!pgd_present(*pgd)) + return true; + p4d = p4d_offset(pgd, address); + if (!p4d_present(*p4d)) + return true; + pud = pud_offset(p4d, address); + if (!pud_present(*pud)) + return true; + pmd = pmd_offset(pud, address); +again: + _pmd = pmdp_get_lockless(pmd); + if (pmd_none(_pmd)) + return true; + + /* + * A race could arise which would result in a softleaf entry such as + * migration entry unexpectedly being present in the PMD, so explicitly + * check for this and bail out if so. + */ + if (!pmd_present(_pmd)) + return false; + + if (pmd_trans_huge(_pmd)) + return !pmd_write(_pmd) && (reason & VM_UFFD_WP); + + pte = pte_offset_map(pmd, address); + if (!pte) + goto again; + + /* + * Lockless access: we're in a wait_event so it's ok if it + * changes under us. + */ + ptent = ptep_get(pte); + + ret = true; + /* Entry is still missing, wait for userspace to resolve the fault. */ + if (pte_none(ptent)) + goto out; + /* UFFD PTE markers require userspace to resolve the fault. */ + if (pte_is_uffd_marker(ptent)) + goto out; + /* + * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to + * resolve the fault. + */ + if (!pte_write(ptent) && (reason & VM_UFFD_WP)) + goto out; + + ret = false; +out: + pte_unmap(pte); + return ret; +} + +static inline unsigned int userfaultfd_get_blocking_state(unsigned int flags) +{ + if (flags & FAULT_FLAG_INTERRUPTIBLE) + return TASK_INTERRUPTIBLE; + + if (flags & FAULT_FLAG_KILLABLE) + return TASK_KILLABLE; + + return TASK_UNINTERRUPTIBLE; +} + +/* + * The locking rules involved in returning VM_FAULT_RETRY depending on + * FAULT_FLAG_ALLOW_RETRY, FAULT_FLAG_RETRY_NOWAIT and + * FAULT_FLAG_KILLABLE are not straightforward. The "Caution" + * recommendation in __lock_page_or_retry is not an understatement. + * + * If FAULT_FLAG_ALLOW_RETRY is set, the mmap_lock must be released + * before returning VM_FAULT_RETRY only if FAULT_FLAG_RETRY_NOWAIT is + * not set. + * + * If FAULT_FLAG_ALLOW_RETRY is set but FAULT_FLAG_KILLABLE is not + * set, VM_FAULT_RETRY can still be returned if and only if there are + * fatal_signal_pending()s, and the mmap_lock must be released before + * returning it. + */ +vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason) +{ + struct vm_area_struct *vma = vmf->vma; + struct mm_struct *mm = vma->vm_mm; + struct userfaultfd_ctx *ctx; + struct userfaultfd_wait_queue uwq; + vm_fault_t ret = VM_FAULT_SIGBUS; + bool must_wait; + unsigned int blocking_state; + + /* + * We don't do userfault handling for the final child pid update + * and when coredumping (faults triggered by get_dump_page()). + */ + if (current->flags & (PF_EXITING|PF_DUMPCORE)) + goto out; + + assert_fault_locked(vmf); + + ctx = vma->vm_userfaultfd_ctx.ctx; + if (!ctx) + goto out; + + VM_WARN_ON_ONCE(ctx->mm != mm); + + /* Any unrecognized flag is a bug. */ + VM_WARN_ON_ONCE(reason & ~__VM_UFFD_FLAGS); + /* 0 or > 1 flags set is a bug; we expect exactly 1. */ + VM_WARN_ON_ONCE(!reason || (reason & (reason - 1))); + + if (ctx->features & UFFD_FEATURE_SIGBUS) + goto out; + if (!(vmf->flags & FAULT_FLAG_USER) && (ctx->flags & UFFD_USER_MODE_ONLY)) + goto out; + + /* + * Check that we can return VM_FAULT_RETRY. + * + * NOTE: it should become possible to return VM_FAULT_RETRY + * even if FAULT_FLAG_TRIED is set without leading to gup() + * -EBUSY failures, if the userfaultfd is to be extended for + * VM_UFFD_WP tracking and we intend to arm the userfault + * without first stopping userland access to the memory. For + * VM_UFFD_MISSING userfaults this is enough for now. + */ + if (unlikely(!(vmf->flags & FAULT_FLAG_ALLOW_RETRY))) { + /* + * Validate the invariant that nowait must allow retry + * to be sure not to return SIGBUS erroneously on + * nowait invocations. + */ + VM_WARN_ON_ONCE(vmf->flags & FAULT_FLAG_RETRY_NOWAIT); +#ifdef CONFIG_DEBUG_VM + if (printk_ratelimit()) { + pr_warn("FAULT_FLAG_ALLOW_RETRY missing %x\n", + vmf->flags); + dump_stack(); + } +#endif + goto out; + } + + /* + * Handle nowait, not much to do other than tell it to retry + * and wait. + */ + ret = VM_FAULT_RETRY; + if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) + goto out; + + if (unlikely(READ_ONCE(ctx->released))) { + /* + * If a concurrent release is detected, do not return + * VM_FAULT_SIGBUS or VM_FAULT_NOPAGE, but instead always + * return VM_FAULT_RETRY with lock released proactively. + * + * If we were to return VM_FAULT_SIGBUS here, the non + * cooperative manager would be instead forced to + * always call UFFDIO_UNREGISTER before it can safely + * close the uffd, to avoid involuntary SIGBUS triggered. + * + * If we were to return VM_FAULT_NOPAGE, it would work for + * the fault path, in which the lock will be released + * later. However for GUP, faultin_page() does nothing + * special on NOPAGE, so GUP would spin retrying without + * releasing the mmap read lock, causing possible livelock. + * + * Here only VM_FAULT_RETRY would make sure the mmap lock + * be released immediately, so that the thread concurrently + * releasing the userfault would always make progress. + */ + release_fault_lock(vmf); + goto out; + } + + /* take the reference before dropping the mmap_lock */ + userfaultfd_ctx_get(ctx); + + init_waitqueue_func_entry(&uwq.wq, userfaultfd_wake_function); + uwq.wq.private = current; + uwq.msg = userfault_msg(vmf->address, vmf->real_address, vmf->flags, + reason, ctx->features); + uwq.ctx = ctx; + uwq.waken = false; + + blocking_state = userfaultfd_get_blocking_state(vmf->flags); + + /* + * Take the vma lock now, in order to safely call + * userfaultfd_huge_must_wait() later. Since acquiring the + * (sleepable) vma lock can modify the current task state, that + * must be before explicitly calling set_current_state(). + */ + if (is_vm_hugetlb_page(vma)) + hugetlb_vma_lock_read(vma); + + spin_lock_irq(&ctx->fault_pending_wqh.lock); + /* + * After the __add_wait_queue the uwq is visible to userland + * through poll/read(). + */ + __add_wait_queue(&ctx->fault_pending_wqh, &uwq.wq); + /* + * The smp_mb() after __set_current_state prevents the reads + * following the spin_unlock to happen before the list_add in + * __add_wait_queue. + */ + set_current_state(blocking_state); + spin_unlock_irq(&ctx->fault_pending_wqh.lock); + + if (is_vm_hugetlb_page(vma)) { + must_wait = userfaultfd_huge_must_wait(ctx, vmf, reason); + hugetlb_vma_unlock_read(vma); + } else { + must_wait = userfaultfd_must_wait(ctx, vmf, reason); + } + + release_fault_lock(vmf); + + if (likely(must_wait && !READ_ONCE(ctx->released))) { + wake_up_poll(&ctx->fd_wqh, EPOLLIN); + schedule(); + } + + __set_current_state(TASK_RUNNING); + + /* + * Here we race with the list_del; list_add in + * userfaultfd_ctx_read(), however because we don't ever run + * list_del_init() to refile across the two lists, the prev + * and next pointers will never point to self. list_add also + * would never let any of the two pointers to point to + * self. So list_empty_careful won't risk to see both pointers + * pointing to self at any time during the list refile. The + * only case where list_del_init() is called is the full + * removal in the wake function and there we don't re-list_add + * and it's fine not to block on the spinlock. The uwq on this + * kernel stack can be released after the list_del_init. + */ + if (!list_empty_careful(&uwq.wq.entry)) { + spin_lock_irq(&ctx->fault_pending_wqh.lock); + /* + * No need of list_del_init(), the uwq on the stack + * will be freed shortly anyway. + */ + list_del(&uwq.wq.entry); + spin_unlock_irq(&ctx->fault_pending_wqh.lock); + } + + /* + * ctx may go away after this if the userfault pseudo fd is + * already released. + */ + userfaultfd_ctx_put(ctx); + +out: + return ret; +} + +static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx, + struct userfaultfd_wait_queue *ewq) +{ + struct userfaultfd_ctx *release_new_ctx; + + if (WARN_ON_ONCE(current->flags & PF_EXITING)) + goto out; + + ewq->ctx = ctx; + init_waitqueue_entry(&ewq->wq, current); + release_new_ctx = NULL; + + spin_lock_irq(&ctx->event_wqh.lock); + /* + * After the __add_wait_queue the uwq is visible to userland + * through poll/read(). + */ + __add_wait_queue(&ctx->event_wqh, &ewq->wq); + for (;;) { + set_current_state(TASK_KILLABLE); + if (ewq->msg.event == 0) + break; + if (READ_ONCE(ctx->released) || + fatal_signal_pending(current)) { + /* + * &ewq->wq may be queued in fork_event, but + * __remove_wait_queue ignores the head + * parameter. It would be a problem if it + * didn't. + */ + __remove_wait_queue(&ctx->event_wqh, &ewq->wq); + if (ewq->msg.event == UFFD_EVENT_FORK) { + struct userfaultfd_ctx *new; + + new = (struct userfaultfd_ctx *) + (unsigned long) + ewq->msg.arg.reserved.reserved1; + release_new_ctx = new; + } + break; + } + + spin_unlock_irq(&ctx->event_wqh.lock); + + wake_up_poll(&ctx->fd_wqh, EPOLLIN); + schedule(); + + spin_lock_irq(&ctx->event_wqh.lock); + } + __set_current_state(TASK_RUNNING); + spin_unlock_irq(&ctx->event_wqh.lock); + + if (release_new_ctx) { + userfaultfd_release_new(release_new_ctx); + userfaultfd_ctx_put(release_new_ctx); + } + + /* + * ctx may go away after this if the userfault pseudo fd is + * already released. + */ +out: + atomic_dec(&ctx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); + userfaultfd_ctx_put(ctx); +} + +static void userfaultfd_event_complete(struct userfaultfd_ctx *ctx, + struct userfaultfd_wait_queue *ewq) +{ + ewq->msg.event = 0; + wake_up_locked(&ctx->event_wqh); + __remove_wait_queue(&ctx->event_wqh, &ewq->wq); +} + +int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs) +{ + struct userfaultfd_ctx *ctx = NULL, *octx; + struct userfaultfd_fork_ctx *fctx; + + octx = vma->vm_userfaultfd_ctx.ctx; + if (!octx) + return 0; + + if (!(octx->features & UFFD_FEATURE_EVENT_FORK)) { + userfaultfd_reset_ctx(vma); + return 0; + } + + list_for_each_entry(fctx, fcs, list) + if (fctx->orig == octx) { + ctx = fctx->new; + break; + } + + if (!ctx) { + fctx = kmalloc_obj(*fctx); + if (!fctx) + return -ENOMEM; + + ctx = kmem_cache_alloc(userfaultfd_ctx_cachep, GFP_KERNEL); + if (!ctx) { + kfree(fctx); + return -ENOMEM; + } + + refcount_set(&ctx->refcount, 1); + ctx->flags = octx->flags; + ctx->features = octx->features; + ctx->released = false; + init_rwsem(&ctx->map_changing_lock); + atomic_set(&ctx->mmap_changing, 0); + ctx->mm = vma->vm_mm; + mmgrab(ctx->mm); + + userfaultfd_ctx_get(octx); + down_write(&octx->map_changing_lock); + atomic_inc(&octx->mmap_changing); + up_write(&octx->map_changing_lock); + fctx->orig = octx; + fctx->new = ctx; + list_add_tail(&fctx->list, fcs); + } + + vma->vm_userfaultfd_ctx.ctx = ctx; + return 0; +} + +static void dup_fctx(struct userfaultfd_fork_ctx *fctx) +{ + struct userfaultfd_ctx *ctx = fctx->orig; + struct userfaultfd_wait_queue ewq; + + msg_init(&ewq.msg); + + ewq.msg.event = UFFD_EVENT_FORK; + ewq.msg.arg.reserved.reserved1 = (unsigned long)fctx->new; + + userfaultfd_event_wait_completion(ctx, &ewq); +} + +void dup_userfaultfd_complete(struct list_head *fcs) +{ + struct userfaultfd_fork_ctx *fctx, *n; + + list_for_each_entry_safe(fctx, n, fcs, list) { + dup_fctx(fctx); + list_del(&fctx->list); + kfree(fctx); + } +} + +void dup_userfaultfd_fail(struct list_head *fcs) +{ + struct userfaultfd_fork_ctx *fctx, *n; + + /* + * An error has occurred on fork, we will tear memory down, but have + * allocated memory for fctx's and raised reference counts for both the + * original and child contexts (and on the mm for each as a result). + * + * These would ordinarily be taken care of by a user handling the event, + * but we are no longer doing so, so manually clean up here. + * + * mm tear down will take care of cleaning up VMA contexts. + */ + list_for_each_entry_safe(fctx, n, fcs, list) { + struct userfaultfd_ctx *octx = fctx->orig; + struct userfaultfd_ctx *ctx = fctx->new; + + atomic_dec(&octx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&octx->mmap_changing) < 0); + userfaultfd_ctx_put(octx); + userfaultfd_ctx_put(ctx); + + list_del(&fctx->list); + kfree(fctx); + } +} + +void mremap_userfaultfd_prep(struct vm_area_struct *vma, + struct vm_userfaultfd_ctx *vm_ctx) +{ + struct userfaultfd_ctx *ctx; + + ctx = vma->vm_userfaultfd_ctx.ctx; + + if (!ctx) + return; + + if (ctx->features & UFFD_FEATURE_EVENT_REMAP) { + vm_ctx->ctx = ctx; + userfaultfd_ctx_get(ctx); + down_write(&ctx->map_changing_lock); + atomic_inc(&ctx->mmap_changing); + up_write(&ctx->map_changing_lock); + } else { + /* Drop uffd context if remap feature not enabled */ + userfaultfd_reset_ctx(vma); + } +} + +void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx *vm_ctx, + unsigned long from, unsigned long to, + unsigned long len) +{ + struct userfaultfd_ctx *ctx = vm_ctx->ctx; + struct userfaultfd_wait_queue ewq; + + if (!ctx) + return; + + msg_init(&ewq.msg); + + ewq.msg.event = UFFD_EVENT_REMAP; + ewq.msg.arg.remap.from = from; + ewq.msg.arg.remap.to = to; + ewq.msg.arg.remap.len = len; + + userfaultfd_event_wait_completion(ctx, &ewq); +} + +void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) +{ + struct userfaultfd_ctx *ctx = vm_ctx->ctx; + + if (!ctx) + return; + + atomic_dec(&ctx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); + userfaultfd_ctx_put(ctx); +} + +bool userfaultfd_remove(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + struct userfaultfd_ctx *ctx; + struct userfaultfd_wait_queue ewq; + + ctx = vma->vm_userfaultfd_ctx.ctx; + if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_REMOVE)) + return true; + + userfaultfd_ctx_get(ctx); + down_write(&ctx->map_changing_lock); + atomic_inc(&ctx->mmap_changing); + up_write(&ctx->map_changing_lock); + mmap_read_unlock(mm); + + msg_init(&ewq.msg); + + ewq.msg.event = UFFD_EVENT_REMOVE; + ewq.msg.arg.remove.start = start; + ewq.msg.arg.remove.end = end; + + userfaultfd_event_wait_completion(ctx, &ewq); + + return false; +} + +static bool has_unmap_ctx(struct userfaultfd_ctx *ctx, struct list_head *unmaps, + unsigned long start, unsigned long end) +{ + struct userfaultfd_unmap_ctx *unmap_ctx; + + list_for_each_entry(unmap_ctx, unmaps, list) + if (unmap_ctx->ctx == ctx && unmap_ctx->start == start && + unmap_ctx->end == end) + return true; + + return false; +} + +int userfaultfd_unmap_prep(struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct list_head *unmaps) +{ + struct userfaultfd_unmap_ctx *unmap_ctx; + struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; + + if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_UNMAP) || + has_unmap_ctx(ctx, unmaps, start, end)) + return 0; + + unmap_ctx = kzalloc_obj(*unmap_ctx); + if (!unmap_ctx) + return -ENOMEM; + + userfaultfd_ctx_get(ctx); + down_write(&ctx->map_changing_lock); + atomic_inc(&ctx->mmap_changing); + up_write(&ctx->map_changing_lock); + unmap_ctx->ctx = ctx; + unmap_ctx->start = start; + unmap_ctx->end = end; + list_add_tail(&unmap_ctx->list, unmaps); + + return 0; +} + +void userfaultfd_unmap_complete(struct mm_struct *mm, struct list_head *uf) +{ + struct userfaultfd_unmap_ctx *ctx, *n; + struct userfaultfd_wait_queue ewq; + + list_for_each_entry_safe(ctx, n, uf, list) { + msg_init(&ewq.msg); + + ewq.msg.event = UFFD_EVENT_UNMAP; + ewq.msg.arg.remove.start = ctx->start; + ewq.msg.arg.remove.end = ctx->end; + + userfaultfd_event_wait_completion(ctx->ctx, &ewq); + + list_del(&ctx->list); + kfree(ctx); + } +} + +static int userfaultfd_release(struct inode *inode, struct file *file) +{ + struct userfaultfd_ctx *ctx = file->private_data; + struct mm_struct *mm = ctx->mm; + /* len == 0 means wake all */ + struct userfaultfd_wake_range range = { .len = 0, }; + + WRITE_ONCE(ctx->released, true); + + userfaultfd_release_all(mm, ctx); + + /* + * After no new page faults can wait on this fault_*wqh, flush + * the last page faults that may have been already waiting on + * the fault_*wqh. + */ + spin_lock_irq(&ctx->fault_pending_wqh.lock); + __wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &range); + __wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, &range); + spin_unlock_irq(&ctx->fault_pending_wqh.lock); + + /* Flush pending events that may still wait on event_wqh */ + wake_up_all(&ctx->event_wqh); + + wake_up_poll(&ctx->fd_wqh, EPOLLHUP); + userfaultfd_ctx_put(ctx); + return 0; +} + +/* fault_pending_wqh.lock must be hold by the caller */ +static inline struct userfaultfd_wait_queue *find_userfault_in( + wait_queue_head_t *wqh) +{ + wait_queue_entry_t *wq; + struct userfaultfd_wait_queue *uwq; + + lockdep_assert_held(&wqh->lock); + + uwq = NULL; + if (!waitqueue_active(wqh)) + goto out; + /* walk in reverse to provide FIFO behavior to read userfaults */ + wq = list_last_entry(&wqh->head, typeof(*wq), entry); + uwq = container_of(wq, struct userfaultfd_wait_queue, wq); +out: + return uwq; +} + +static inline struct userfaultfd_wait_queue *find_userfault( + struct userfaultfd_ctx *ctx) +{ + return find_userfault_in(&ctx->fault_pending_wqh); +} + +static inline struct userfaultfd_wait_queue *find_userfault_evt( + struct userfaultfd_ctx *ctx) +{ + return find_userfault_in(&ctx->event_wqh); +} + +static __poll_t userfaultfd_poll(struct file *file, poll_table *wait) +{ + struct userfaultfd_ctx *ctx = file->private_data; + __poll_t ret; + + poll_wait(file, &ctx->fd_wqh, wait); + + if (!userfaultfd_is_initialized(ctx)) + return EPOLLERR; + + /* + * poll() never guarantees that read won't block. + * userfaults can be waken before they're read(). + */ + if (unlikely(!(file->f_flags & O_NONBLOCK))) + return EPOLLERR; + /* + * lockless access to see if there are pending faults + * __pollwait last action is the add_wait_queue but + * the spin_unlock would allow the waitqueue_active to + * pass above the actual list_add inside + * add_wait_queue critical section. So use a full + * memory barrier to serialize the list_add write of + * add_wait_queue() with the waitqueue_active read + * below. + */ + ret = 0; + smp_mb(); + if (waitqueue_active(&ctx->fault_pending_wqh)) + ret = EPOLLIN; + else if (waitqueue_active(&ctx->event_wqh)) + ret = EPOLLIN; + + return ret; +} + +static const struct file_operations userfaultfd_fops; + +static int resolve_userfault_fork(struct userfaultfd_ctx *new, + struct inode *inode, + struct uffd_msg *msg) +{ + int fd; + + fd = anon_inode_create_getfd("[userfaultfd]", &userfaultfd_fops, new, + O_RDONLY | (new->flags & UFFD_SHARED_FCNTL_FLAGS), inode); + if (fd < 0) + return fd; + + msg->arg.reserved.reserved1 = 0; + msg->arg.fork.ufd = fd; + return 0; +} + +static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait, + struct uffd_msg *msg, struct inode *inode) +{ + ssize_t ret; + DECLARE_WAITQUEUE(wait, current); + struct userfaultfd_wait_queue *uwq; + /* + * Handling fork event requires sleeping operations, so + * we drop the event_wqh lock, then do these ops, then + * lock it back and wake up the waiter. While the lock is + * dropped the ewq may go away so we keep track of it + * carefully. + */ + LIST_HEAD(fork_event); + struct userfaultfd_ctx *fork_nctx = NULL; + + /* always take the fd_wqh lock before the fault_pending_wqh lock */ + spin_lock_irq(&ctx->fd_wqh.lock); + __add_wait_queue(&ctx->fd_wqh, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock(&ctx->fault_pending_wqh.lock); + uwq = find_userfault(ctx); + if (uwq) { + /* + * Use a seqcount to repeat the lockless check + * in wake_userfault() to avoid missing + * wakeups because during the refile both + * waitqueue could become empty if this is the + * only userfault. + */ + write_seqcount_begin(&ctx->refile_seq); + + /* + * The fault_pending_wqh.lock prevents the uwq + * to disappear from under us. + * + * Refile this userfault from + * fault_pending_wqh to fault_wqh, it's not + * pending anymore after we read it. + * + * Use list_del() by hand (as + * userfaultfd_wake_function also uses + * list_del_init() by hand) to be sure nobody + * changes __remove_wait_queue() to use + * list_del_init() in turn breaking the + * !list_empty_careful() check in + * handle_userfault(). The uwq->wq.head list + * must never be empty at any time during the + * refile, or the waitqueue could disappear + * from under us. The "wait_queue_head_t" + * parameter of __remove_wait_queue() is unused + * anyway. + */ + list_del(&uwq->wq.entry); + add_wait_queue(&ctx->fault_wqh, &uwq->wq); + + write_seqcount_end(&ctx->refile_seq); + + /* careful to always initialize msg if ret == 0 */ + *msg = uwq->msg; + spin_unlock(&ctx->fault_pending_wqh.lock); + ret = 0; + break; + } + spin_unlock(&ctx->fault_pending_wqh.lock); + + spin_lock(&ctx->event_wqh.lock); + uwq = find_userfault_evt(ctx); + if (uwq) { + *msg = uwq->msg; + + if (uwq->msg.event == UFFD_EVENT_FORK) { + fork_nctx = (struct userfaultfd_ctx *) + (unsigned long) + uwq->msg.arg.reserved.reserved1; + list_move(&uwq->wq.entry, &fork_event); + /* + * fork_nctx can be freed as soon as + * we drop the lock, unless we take a + * reference on it. + */ + userfaultfd_ctx_get(fork_nctx); + spin_unlock(&ctx->event_wqh.lock); + ret = 0; + break; + } + + userfaultfd_event_complete(ctx, uwq); + spin_unlock(&ctx->event_wqh.lock); + ret = 0; + break; + } + spin_unlock(&ctx->event_wqh.lock); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + if (no_wait) { + ret = -EAGAIN; + break; + } + spin_unlock_irq(&ctx->fd_wqh.lock); + schedule(); + spin_lock_irq(&ctx->fd_wqh.lock); + } + __remove_wait_queue(&ctx->fd_wqh, &wait); + __set_current_state(TASK_RUNNING); + spin_unlock_irq(&ctx->fd_wqh.lock); + + if (!ret && msg->event == UFFD_EVENT_FORK) { + ret = resolve_userfault_fork(fork_nctx, inode, msg); + spin_lock_irq(&ctx->event_wqh.lock); + if (!list_empty(&fork_event)) { + /* + * The fork thread didn't abort, so we can + * drop the temporary refcount. + */ + userfaultfd_ctx_put(fork_nctx); + + uwq = list_first_entry(&fork_event, + typeof(*uwq), + wq.entry); + /* + * If fork_event list wasn't empty and in turn + * the event wasn't already released by fork + * (the event is allocated on fork kernel + * stack), put the event back to its place in + * the event_wq. fork_event head will be freed + * as soon as we return so the event cannot + * stay queued there no matter the current + * "ret" value. + */ + list_del(&uwq->wq.entry); + __add_wait_queue(&ctx->event_wqh, &uwq->wq); + + /* + * Leave the event in the waitqueue and report + * error to userland if we failed to resolve + * the userfault fork. + */ + if (likely(!ret)) + userfaultfd_event_complete(ctx, uwq); + } else { + /* + * Here the fork thread aborted and the + * refcount from the fork thread on fork_nctx + * has already been released. We still hold + * the reference we took before releasing the + * lock above. If resolve_userfault_fork + * failed we've to drop it because the + * fork_nctx has to be freed in such case. If + * it succeeded we'll hold it because the new + * uffd references it. + */ + if (ret) + userfaultfd_ctx_put(fork_nctx); + } + spin_unlock_irq(&ctx->event_wqh.lock); + } + + return ret; +} + +static ssize_t userfaultfd_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct userfaultfd_ctx *ctx = file->private_data; + ssize_t _ret, ret = 0; + struct uffd_msg msg; + struct inode *inode = file_inode(file); + bool no_wait; + + if (!userfaultfd_is_initialized(ctx)) + return -EINVAL; + + no_wait = file->f_flags & O_NONBLOCK || iocb->ki_flags & IOCB_NOWAIT; + for (;;) { + if (iov_iter_count(to) < sizeof(msg)) + return ret ? ret : -EINVAL; + _ret = userfaultfd_ctx_read(ctx, no_wait, &msg, inode); + if (_ret < 0) + return ret ? ret : _ret; + _ret = !copy_to_iter_full(&msg, sizeof(msg), to); + if (_ret) + return ret ? ret : -EFAULT; + ret += sizeof(msg); + /* + * Allow to read more than one fault at time but only + * block if waiting for the very first one. + */ + no_wait = true; + } +} + +static void __wake_userfault(struct userfaultfd_ctx *ctx, + struct userfaultfd_wake_range *range) +{ + spin_lock_irq(&ctx->fault_pending_wqh.lock); + /* wake all in the range and autoremove */ + if (waitqueue_active(&ctx->fault_pending_wqh)) + __wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, + range); + if (waitqueue_active(&ctx->fault_wqh)) + __wake_up(&ctx->fault_wqh, TASK_NORMAL, 1, range); + spin_unlock_irq(&ctx->fault_pending_wqh.lock); +} + +static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx, + struct userfaultfd_wake_range *range) +{ + unsigned seq; + bool need_wakeup; + + /* + * To be sure waitqueue_active() is not reordered by the CPU + * before the pagetable update, use an explicit SMP memory + * barrier here. PT lock release or mmap_read_unlock(mm) still + * have release semantics that can allow the + * waitqueue_active() to be reordered before the pte update. + */ + smp_mb(); + + /* + * Use waitqueue_active because it's very frequent to + * change the address space atomically even if there are no + * userfaults yet. So we take the spinlock only when we're + * sure we've userfaults to wake. + */ + do { + seq = read_seqcount_begin(&ctx->refile_seq); + need_wakeup = waitqueue_active(&ctx->fault_pending_wqh) || + waitqueue_active(&ctx->fault_wqh); + cond_resched(); + } while (read_seqcount_retry(&ctx->refile_seq, seq)); + if (need_wakeup) + __wake_userfault(ctx, range); +} + +static __always_inline int validate_unaligned_range( + struct mm_struct *mm, __u64 start, __u64 len) +{ + __u64 task_size = mm->task_size; + + if (len & ~PAGE_MASK) + return -EINVAL; + if (!len) + return -EINVAL; + if (start >= task_size) + return -EINVAL; + if (len > task_size - start) + return -EINVAL; + if (start + len <= start) + return -EINVAL; + return 0; +} + +static __always_inline int validate_range(struct mm_struct *mm, + __u64 start, __u64 len) +{ + if (start & ~PAGE_MASK) + return -EINVAL; + + return validate_unaligned_range(mm, start, len); +} + +static int userfaultfd_register(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + struct mm_struct *mm = ctx->mm; + struct vm_area_struct *vma, *cur; + int ret; + struct uffdio_register uffdio_register; + struct uffdio_register __user *user_uffdio_register; + vm_flags_t vm_flags; + bool found; + bool basic_ioctls; + unsigned long start, end; + struct vma_iterator vmi; + bool wp_async = userfaultfd_wp_async_ctx(ctx); + + user_uffdio_register = (struct uffdio_register __user *) arg; + + ret = -EFAULT; + if (copy_from_user(&uffdio_register, user_uffdio_register, + sizeof(uffdio_register)-sizeof(__u64))) + goto out; + + ret = -EINVAL; + if (!uffdio_register.mode) + goto out; + if (uffdio_register.mode & ~UFFD_API_REGISTER_MODES) + goto out; + vm_flags = 0; + if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING) + vm_flags |= VM_UFFD_MISSING; + if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP) { + if (!pgtable_supports_uffd_wp()) + goto out; + + vm_flags |= VM_UFFD_WP; + } + if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) { +#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR + goto out; +#endif + vm_flags |= VM_UFFD_MINOR; + } + + ret = validate_range(mm, uffdio_register.range.start, + uffdio_register.range.len); + if (ret) + goto out; + + start = uffdio_register.range.start; + end = start + uffdio_register.range.len; + + ret = -ENOMEM; + if (!mmget_not_zero(mm)) + goto out; + + ret = -EINVAL; + mmap_write_lock(mm); + vma_iter_init(&vmi, mm, start); + vma = vma_find(&vmi, end); + if (!vma) + goto out_unlock; + + /* + * If the first vma contains huge pages, make sure start address + * is aligned to huge page size. + */ + if (is_vm_hugetlb_page(vma)) { + unsigned long vma_hpagesize = vma_kernel_pagesize(vma); + + if (start & (vma_hpagesize - 1)) + goto out_unlock; + } + + /* + * Search for not compatible vmas. + */ + found = false; + basic_ioctls = false; + cur = vma; + do { + cond_resched(); + + VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ + !!(cur->vm_flags & __VM_UFFD_FLAGS)); + + /* check not compatible vmas */ + ret = -EINVAL; + if (!vma_can_userfault(cur, vm_flags, wp_async)) + goto out_unlock; + + /* + * UFFDIO_COPY will fill file holes even without + * PROT_WRITE. This check enforces that if this is a + * MAP_SHARED, the process has write permission to the backing + * file. If VM_MAYWRITE is set it also enforces that on a + * MAP_SHARED vma: there is no F_WRITE_SEAL and no further + * F_WRITE_SEAL can be taken until the vma is destroyed. + */ + ret = -EPERM; + if (unlikely(!(cur->vm_flags & VM_MAYWRITE))) + goto out_unlock; + + /* + * If this vma contains ending address, and huge pages + * check alignment. + */ + if (is_vm_hugetlb_page(cur) && end <= cur->vm_end && + end > cur->vm_start) { + unsigned long vma_hpagesize = vma_kernel_pagesize(cur); + + ret = -EINVAL; + + if (end & (vma_hpagesize - 1)) + goto out_unlock; + } + if ((vm_flags & VM_UFFD_WP) && !(cur->vm_flags & VM_MAYWRITE)) + goto out_unlock; + + /* + * Check that this vma isn't already owned by a + * different userfaultfd. We can't allow more than one + * userfaultfd to own a single vma simultaneously or we + * wouldn't know which one to deliver the userfaults to. + */ + ret = -EBUSY; + if (cur->vm_userfaultfd_ctx.ctx && + cur->vm_userfaultfd_ctx.ctx != ctx) + goto out_unlock; + + /* + * Note vmas containing huge pages + */ + if (is_vm_hugetlb_page(cur)) + basic_ioctls = true; + + found = true; + } for_each_vma_range(vmi, cur, end); + VM_WARN_ON_ONCE(!found); + + ret = userfaultfd_register_range(ctx, vma, vm_flags, start, end, + wp_async); + +out_unlock: + mmap_write_unlock(mm); + mmput(mm); + if (!ret) { + __u64 ioctls_out; + + ioctls_out = basic_ioctls ? UFFD_API_RANGE_IOCTLS_BASIC : + UFFD_API_RANGE_IOCTLS; + + /* + * Declare the WP ioctl only if the WP mode is + * specified and all checks passed with the range + */ + if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_WP)) + ioctls_out &= ~((__u64)1 << _UFFDIO_WRITEPROTECT); + + /* CONTINUE ioctl is only supported for MINOR ranges. */ + if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR)) + ioctls_out &= ~((__u64)1 << _UFFDIO_CONTINUE); + + /* + * Now that we scanned all vmas we can already tell + * userland which ioctls methods are guaranteed to + * succeed on this range. + */ + if (put_user(ioctls_out, &user_uffdio_register->ioctls)) + ret = -EFAULT; + } +out: + return ret; +} + +static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + struct mm_struct *mm = ctx->mm; + struct vm_area_struct *vma, *prev, *cur; + int ret; + struct uffdio_range uffdio_unregister; + bool found; + unsigned long start, end, vma_end; + const void __user *buf = (void __user *)arg; + struct vma_iterator vmi; + bool wp_async = userfaultfd_wp_async_ctx(ctx); + + ret = -EFAULT; + if (copy_from_user(&uffdio_unregister, buf, sizeof(uffdio_unregister))) + goto out; + + ret = validate_range(mm, uffdio_unregister.start, + uffdio_unregister.len); + if (ret) + goto out; + + start = uffdio_unregister.start; + end = start + uffdio_unregister.len; + + ret = -ENOMEM; + if (!mmget_not_zero(mm)) + goto out; + + mmap_write_lock(mm); + ret = -EINVAL; + vma_iter_init(&vmi, mm, start); + vma = vma_find(&vmi, end); + if (!vma) + goto out_unlock; + + /* + * If the first vma contains huge pages, make sure start address + * is aligned to huge page size. + */ + if (is_vm_hugetlb_page(vma)) { + unsigned long vma_hpagesize = vma_kernel_pagesize(vma); + + if (start & (vma_hpagesize - 1)) + goto out_unlock; + } + + /* + * Search for not compatible vmas. + */ + found = false; + cur = vma; + do { + cond_resched(); + + VM_WARN_ON_ONCE(!!cur->vm_userfaultfd_ctx.ctx ^ + !!(cur->vm_flags & __VM_UFFD_FLAGS)); + + /* + * Prevent unregistering through a different userfaultfd than + * the one used for registration. + */ + if (cur->vm_userfaultfd_ctx.ctx && + cur->vm_userfaultfd_ctx.ctx != ctx) + goto out_unlock; + + /* + * Check not compatible vmas, not strictly required + * here as not compatible vmas cannot have an + * userfaultfd_ctx registered on them, but this + * provides for more strict behavior to notice + * unregistration errors. + */ + if (!vma_can_userfault(cur, cur->vm_flags, wp_async)) + goto out_unlock; + + found = true; + } for_each_vma_range(vmi, cur, end); + VM_WARN_ON_ONCE(!found); + + vma_iter_set(&vmi, start); + prev = vma_prev(&vmi); + if (vma->vm_start < start) + prev = vma; + + ret = 0; + for_each_vma_range(vmi, vma, end) { + cond_resched(); + + /* VMA not registered with userfaultfd. */ + if (!vma->vm_userfaultfd_ctx.ctx) + goto skip; + + VM_WARN_ON_ONCE(vma->vm_userfaultfd_ctx.ctx != ctx); + VM_WARN_ON_ONCE(!vma_can_userfault(vma, vma->vm_flags, wp_async)); + VM_WARN_ON_ONCE(!(vma->vm_flags & VM_MAYWRITE)); + + if (vma->vm_start > start) + start = vma->vm_start; + vma_end = min(end, vma->vm_end); + + if (userfaultfd_missing(vma)) { + /* + * Wake any concurrent pending userfault while + * we unregister, so they will not hang + * permanently and it avoids userland to call + * UFFDIO_WAKE explicitly. + */ + struct userfaultfd_wake_range range; + range.start = start; + range.len = vma_end - start; + wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range); + } + + vma = userfaultfd_clear_vma(&vmi, prev, vma, + start, vma_end); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + break; + } + +skip: + prev = vma; + start = vma->vm_end; + } + +out_unlock: + mmap_write_unlock(mm); + mmput(mm); +out: + return ret; +} + +/* + * userfaultfd_wake may be used in combination with the + * UFFDIO_*_MODE_DONTWAKE to wakeup userfaults in batches. + */ +static int userfaultfd_wake(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + int ret; + struct uffdio_range uffdio_wake; + struct userfaultfd_wake_range range; + const void __user *buf = (void __user *)arg; + + ret = -EFAULT; + if (copy_from_user(&uffdio_wake, buf, sizeof(uffdio_wake))) + goto out; + + ret = validate_range(ctx->mm, uffdio_wake.start, uffdio_wake.len); + if (ret) + goto out; + + range.start = uffdio_wake.start; + range.len = uffdio_wake.len; + + /* + * len == 0 means wake all and we don't want to wake all here, + * so check it again to be sure. + */ + VM_WARN_ON_ONCE(!range.len); + + wake_userfault(ctx, &range); + ret = 0; + +out: + return ret; +} + +static int userfaultfd_copy(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + __s64 ret; + struct uffdio_copy uffdio_copy; + struct uffdio_copy __user *user_uffdio_copy; + struct userfaultfd_wake_range range; + uffd_flags_t flags = 0; + + user_uffdio_copy = (struct uffdio_copy __user *) arg; + + ret = -EAGAIN; + if (unlikely(atomic_read(&ctx->mmap_changing))) { + if (unlikely(put_user(ret, &user_uffdio_copy->copy))) + return -EFAULT; + goto out; + } + + ret = -EFAULT; + if (copy_from_user(&uffdio_copy, user_uffdio_copy, + /* don't copy "copy" last field */ + sizeof(uffdio_copy)-sizeof(__s64))) + goto out; + + ret = validate_unaligned_range(ctx->mm, uffdio_copy.src, + uffdio_copy.len); + if (ret) + goto out; + ret = validate_range(ctx->mm, uffdio_copy.dst, uffdio_copy.len); + if (ret) + goto out; + + ret = -EINVAL; + if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|UFFDIO_COPY_MODE_WP)) + goto out; + if (uffdio_copy.mode & UFFDIO_COPY_MODE_WP) + flags |= MFILL_ATOMIC_WP; + if (mmget_not_zero(ctx->mm)) { + ret = mfill_atomic_copy(ctx, uffdio_copy.dst, uffdio_copy.src, + uffdio_copy.len, flags); + mmput(ctx->mm); + } else { + return -ESRCH; + } + if (unlikely(put_user(ret, &user_uffdio_copy->copy))) + return -EFAULT; + if (ret < 0) + goto out; + VM_WARN_ON_ONCE(!ret); + /* len == 0 would wake all */ + range.len = ret; + if (!(uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE)) { + range.start = uffdio_copy.dst; + wake_userfault(ctx, &range); + } + ret = range.len == uffdio_copy.len ? 0 : -EAGAIN; +out: + return ret; +} + +static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + __s64 ret; + struct uffdio_zeropage uffdio_zeropage; + struct uffdio_zeropage __user *user_uffdio_zeropage; + struct userfaultfd_wake_range range; + + user_uffdio_zeropage = (struct uffdio_zeropage __user *) arg; + + ret = -EAGAIN; + if (unlikely(atomic_read(&ctx->mmap_changing))) { + if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage))) + return -EFAULT; + goto out; + } + + ret = -EFAULT; + if (copy_from_user(&uffdio_zeropage, user_uffdio_zeropage, + /* don't copy "zeropage" last field */ + sizeof(uffdio_zeropage)-sizeof(__s64))) + goto out; + + ret = validate_range(ctx->mm, uffdio_zeropage.range.start, + uffdio_zeropage.range.len); + if (ret) + goto out; + ret = -EINVAL; + if (uffdio_zeropage.mode & ~UFFDIO_ZEROPAGE_MODE_DONTWAKE) + goto out; + + if (mmget_not_zero(ctx->mm)) { + ret = mfill_atomic_zeropage(ctx, uffdio_zeropage.range.start, + uffdio_zeropage.range.len); + mmput(ctx->mm); + } else { + return -ESRCH; + } + if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage))) + return -EFAULT; + if (ret < 0) + goto out; + /* len == 0 would wake all */ + VM_WARN_ON_ONCE(!ret); + range.len = ret; + if (!(uffdio_zeropage.mode & UFFDIO_ZEROPAGE_MODE_DONTWAKE)) { + range.start = uffdio_zeropage.range.start; + wake_userfault(ctx, &range); + } + ret = range.len == uffdio_zeropage.range.len ? 0 : -EAGAIN; +out: + return ret; +} + +static int userfaultfd_writeprotect(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + int ret; + struct uffdio_writeprotect uffdio_wp; + struct uffdio_writeprotect __user *user_uffdio_wp; + struct userfaultfd_wake_range range; + bool mode_wp, mode_dontwake; + + if (atomic_read(&ctx->mmap_changing)) + return -EAGAIN; + + user_uffdio_wp = (struct uffdio_writeprotect __user *) arg; + + if (copy_from_user(&uffdio_wp, user_uffdio_wp, + sizeof(struct uffdio_writeprotect))) + return -EFAULT; + + ret = validate_range(ctx->mm, uffdio_wp.range.start, + uffdio_wp.range.len); + if (ret) + return ret; + + if (uffdio_wp.mode & ~(UFFDIO_WRITEPROTECT_MODE_DONTWAKE | + UFFDIO_WRITEPROTECT_MODE_WP)) + return -EINVAL; + + mode_wp = uffdio_wp.mode & UFFDIO_WRITEPROTECT_MODE_WP; + mode_dontwake = uffdio_wp.mode & UFFDIO_WRITEPROTECT_MODE_DONTWAKE; + + if (mode_wp && mode_dontwake) + return -EINVAL; + + if (mmget_not_zero(ctx->mm)) { + ret = mwriteprotect_range(ctx, uffdio_wp.range.start, + uffdio_wp.range.len, mode_wp); + mmput(ctx->mm); + } else { + return -ESRCH; + } + + if (ret) + return ret; + + if (!mode_wp && !mode_dontwake) { + range.start = uffdio_wp.range.start; + range.len = uffdio_wp.range.len; + wake_userfault(ctx, &range); + } + return ret; +} + +static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg) +{ + __s64 ret; + struct uffdio_continue uffdio_continue; + struct uffdio_continue __user *user_uffdio_continue; + struct userfaultfd_wake_range range; + uffd_flags_t flags = 0; + + user_uffdio_continue = (struct uffdio_continue __user *)arg; + + ret = -EAGAIN; + if (unlikely(atomic_read(&ctx->mmap_changing))) { + if (unlikely(put_user(ret, &user_uffdio_continue->mapped))) + return -EFAULT; + goto out; + } + + ret = -EFAULT; + if (copy_from_user(&uffdio_continue, user_uffdio_continue, + /* don't copy the output fields */ + sizeof(uffdio_continue) - (sizeof(__s64)))) + goto out; + + ret = validate_range(ctx->mm, uffdio_continue.range.start, + uffdio_continue.range.len); + if (ret) + goto out; + + ret = -EINVAL; + if (uffdio_continue.mode & ~(UFFDIO_CONTINUE_MODE_DONTWAKE | + UFFDIO_CONTINUE_MODE_WP)) + goto out; + if (uffdio_continue.mode & UFFDIO_CONTINUE_MODE_WP) + flags |= MFILL_ATOMIC_WP; + + if (mmget_not_zero(ctx->mm)) { + ret = mfill_atomic_continue(ctx, uffdio_continue.range.start, + uffdio_continue.range.len, flags); + mmput(ctx->mm); + } else { + return -ESRCH; + } + + if (unlikely(put_user(ret, &user_uffdio_continue->mapped))) + return -EFAULT; + if (ret < 0) + goto out; + + /* len == 0 would wake all */ + VM_WARN_ON_ONCE(!ret); + range.len = ret; + if (!(uffdio_continue.mode & UFFDIO_CONTINUE_MODE_DONTWAKE)) { + range.start = uffdio_continue.range.start; + wake_userfault(ctx, &range); + } + ret = range.len == uffdio_continue.range.len ? 0 : -EAGAIN; + +out: + return ret; +} + +static inline int userfaultfd_poison(struct userfaultfd_ctx *ctx, unsigned long arg) +{ + __s64 ret; + struct uffdio_poison uffdio_poison; + struct uffdio_poison __user *user_uffdio_poison; + struct userfaultfd_wake_range range; + + user_uffdio_poison = (struct uffdio_poison __user *)arg; + + ret = -EAGAIN; + if (unlikely(atomic_read(&ctx->mmap_changing))) { + if (unlikely(put_user(ret, &user_uffdio_poison->updated))) + return -EFAULT; + goto out; + } + + ret = -EFAULT; + if (copy_from_user(&uffdio_poison, user_uffdio_poison, + /* don't copy the output fields */ + sizeof(uffdio_poison) - (sizeof(__s64)))) + goto out; + + ret = validate_range(ctx->mm, uffdio_poison.range.start, + uffdio_poison.range.len); + if (ret) + goto out; + + ret = -EINVAL; + if (uffdio_poison.mode & ~UFFDIO_POISON_MODE_DONTWAKE) + goto out; + + if (mmget_not_zero(ctx->mm)) { + ret = mfill_atomic_poison(ctx, uffdio_poison.range.start, + uffdio_poison.range.len, 0); + mmput(ctx->mm); + } else { + return -ESRCH; + } + + if (unlikely(put_user(ret, &user_uffdio_poison->updated))) + return -EFAULT; + if (ret < 0) + goto out; + + /* len == 0 would wake all */ + VM_WARN_ON_ONCE(!ret); + range.len = ret; + if (!(uffdio_poison.mode & UFFDIO_POISON_MODE_DONTWAKE)) { + range.start = uffdio_poison.range.start; + wake_userfault(ctx, &range); + } + ret = range.len == uffdio_poison.range.len ? 0 : -EAGAIN; + +out: + return ret; +} + +bool userfaultfd_wp_async(struct vm_area_struct *vma) +{ + return userfaultfd_wp_async_ctx(vma->vm_userfaultfd_ctx.ctx); +} + +static inline unsigned int uffd_ctx_features(__u64 user_features) +{ + /* + * For the current set of features the bits just coincide. Set + * UFFD_FEATURE_INITIALIZED to mark the features as enabled. + */ + return (unsigned int)user_features | UFFD_FEATURE_INITIALIZED; +} + +static int userfaultfd_move(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + __s64 ret; + struct uffdio_move uffdio_move; + struct uffdio_move __user *user_uffdio_move; + struct userfaultfd_wake_range range; + struct mm_struct *mm = ctx->mm; + + user_uffdio_move = (struct uffdio_move __user *) arg; + + ret = -EAGAIN; + if (unlikely(atomic_read(&ctx->mmap_changing))) { + if (unlikely(put_user(ret, &user_uffdio_move->move))) + return -EFAULT; + goto out; + } + + if (copy_from_user(&uffdio_move, user_uffdio_move, + /* don't copy "move" last field */ + sizeof(uffdio_move)-sizeof(__s64))) + return -EFAULT; + + /* Do not allow cross-mm moves. */ + if (mm != current->mm) + return -EINVAL; + + ret = validate_range(mm, uffdio_move.dst, uffdio_move.len); + if (ret) + return ret; + + ret = validate_range(mm, uffdio_move.src, uffdio_move.len); + if (ret) + return ret; + + if (uffdio_move.mode & ~(UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES| + UFFDIO_MOVE_MODE_DONTWAKE)) + return -EINVAL; + + if (mmget_not_zero(mm)) { + ret = move_pages(ctx, uffdio_move.dst, uffdio_move.src, + uffdio_move.len, uffdio_move.mode); + mmput(mm); + } else { + return -ESRCH; + } + + if (unlikely(put_user(ret, &user_uffdio_move->move))) + return -EFAULT; + if (ret < 0) + goto out; + + /* len == 0 would wake all */ + VM_WARN_ON(!ret); + range.len = ret; + if (!(uffdio_move.mode & UFFDIO_MOVE_MODE_DONTWAKE)) { + range.start = uffdio_move.dst; + wake_userfault(ctx, &range); + } + ret = range.len == uffdio_move.len ? 0 : -EAGAIN; + +out: + return ret; +} + +/* + * userland asks for a certain API version and we return which bits + * and ioctl commands are implemented in this kernel for such API + * version or -EINVAL if unknown. + */ +static int userfaultfd_api(struct userfaultfd_ctx *ctx, + unsigned long arg) +{ + struct uffdio_api uffdio_api; + void __user *buf = (void __user *)arg; + unsigned int ctx_features; + int ret; + __u64 features; + + ret = -EFAULT; + if (copy_from_user(&uffdio_api, buf, sizeof(uffdio_api))) + goto out; + features = uffdio_api.features; + ret = -EINVAL; + if (uffdio_api.api != UFFD_API) + goto err_out; + ret = -EPERM; + if ((features & UFFD_FEATURE_EVENT_FORK) && !capable(CAP_SYS_PTRACE)) + goto err_out; + + /* WP_ASYNC relies on WP_UNPOPULATED, choose it unconditionally */ + if (features & UFFD_FEATURE_WP_ASYNC) + features |= UFFD_FEATURE_WP_UNPOPULATED; + + /* report all available features and ioctls to userland */ + uffdio_api.features = UFFD_API_FEATURES; +#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR + uffdio_api.features &= + ~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM); +#endif + if (!pgtable_supports_uffd_wp()) + uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP; + + if (!uffd_supports_wp_marker()) { + uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM; + uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED; + uffdio_api.features &= ~UFFD_FEATURE_WP_ASYNC; + } + + ret = -EINVAL; + if (features & ~uffdio_api.features) + goto err_out; + + uffdio_api.ioctls = UFFD_API_IOCTLS; + ret = -EFAULT; + if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api))) + goto out; + + /* only enable the requested features for this uffd context */ + ctx_features = uffd_ctx_features(features); + ret = -EINVAL; + if (cmpxchg(&ctx->features, 0, ctx_features) != 0) + goto err_out; + + ret = 0; +out: + return ret; +err_out: + memset(&uffdio_api, 0, sizeof(uffdio_api)); + if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api))) + ret = -EFAULT; + goto out; +} + +static long userfaultfd_ioctl(struct file *file, unsigned cmd, + unsigned long arg) +{ + int ret = -EINVAL; + struct userfaultfd_ctx *ctx = file->private_data; + + if (cmd != UFFDIO_API && !userfaultfd_is_initialized(ctx)) + return -EINVAL; + + switch (cmd) { + case UFFDIO_API: + ret = userfaultfd_api(ctx, arg); + break; + case UFFDIO_REGISTER: + ret = userfaultfd_register(ctx, arg); + break; + case UFFDIO_UNREGISTER: + ret = userfaultfd_unregister(ctx, arg); + break; + case UFFDIO_WAKE: + ret = userfaultfd_wake(ctx, arg); + break; + case UFFDIO_COPY: + ret = userfaultfd_copy(ctx, arg); + break; + case UFFDIO_ZEROPAGE: + ret = userfaultfd_zeropage(ctx, arg); + break; + case UFFDIO_MOVE: + ret = userfaultfd_move(ctx, arg); + break; + case UFFDIO_WRITEPROTECT: + ret = userfaultfd_writeprotect(ctx, arg); + break; + case UFFDIO_CONTINUE: + ret = userfaultfd_continue(ctx, arg); + break; + case UFFDIO_POISON: + ret = userfaultfd_poison(ctx, arg); + break; + } + return ret; +} + +#ifdef CONFIG_PROC_FS +static void userfaultfd_show_fdinfo(struct seq_file *m, struct file *f) +{ + struct userfaultfd_ctx *ctx = f->private_data; + wait_queue_entry_t *wq; + unsigned long pending = 0, total = 0; + + spin_lock_irq(&ctx->fault_pending_wqh.lock); + list_for_each_entry(wq, &ctx->fault_pending_wqh.head, entry) { + pending++; + total++; + } + list_for_each_entry(wq, &ctx->fault_wqh.head, entry) { + total++; + } + spin_unlock_irq(&ctx->fault_pending_wqh.lock); + + /* + * If more protocols will be added, there will be all shown + * separated by a space. Like this: + * protocols: aa:... bb:... + */ + seq_printf(m, "pending:\t%lu\ntotal:\t%lu\nAPI:\t%Lx:%x:%Lx\n", + pending, total, UFFD_API, ctx->features, + UFFD_API_IOCTLS|UFFD_API_RANGE_IOCTLS); +} +#endif + +static const struct file_operations userfaultfd_fops = { +#ifdef CONFIG_PROC_FS + .show_fdinfo = userfaultfd_show_fdinfo, +#endif + .release = userfaultfd_release, + .poll = userfaultfd_poll, + .read_iter = userfaultfd_read_iter, + .unlocked_ioctl = userfaultfd_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .llseek = noop_llseek, +}; + +static void init_once_userfaultfd_ctx(void *mem) +{ + struct userfaultfd_ctx *ctx = (struct userfaultfd_ctx *) mem; + + init_waitqueue_head(&ctx->fault_pending_wqh); + init_waitqueue_head(&ctx->fault_wqh); + init_waitqueue_head(&ctx->event_wqh); + init_waitqueue_head(&ctx->fd_wqh); + seqcount_spinlock_init(&ctx->refile_seq, &ctx->fault_pending_wqh.lock); +} + +static int new_userfaultfd(int flags) +{ + struct userfaultfd_ctx *ctx __free(kfree) = NULL; + + VM_WARN_ON_ONCE(!current->mm); + + /* Check the UFFD_* constants for consistency. */ + BUILD_BUG_ON(UFFD_USER_MODE_ONLY & UFFD_SHARED_FCNTL_FLAGS); + + if (flags & ~(UFFD_SHARED_FCNTL_FLAGS | UFFD_USER_MODE_ONLY)) + return -EINVAL; + + ctx = kmem_cache_alloc(userfaultfd_ctx_cachep, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + refcount_set(&ctx->refcount, 1); + ctx->flags = flags; + ctx->features = 0; + ctx->released = false; + init_rwsem(&ctx->map_changing_lock); + atomic_set(&ctx->mmap_changing, 0); + ctx->mm = current->mm; + + FD_PREPARE(fdf, flags & UFFD_SHARED_FCNTL_FLAGS, + anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx, + O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS), + NULL)); + if (fdf.err) + return fdf.err; + + /* prevent the mm struct to be freed */ + mmgrab(ctx->mm); + fd_prepare_file(fdf)->f_mode |= FMODE_NOWAIT; + retain_and_null_ptr(ctx); + return fd_publish(fdf); +} + +static inline bool userfaultfd_syscall_allowed(int flags) +{ + /* Userspace-only page faults are always allowed */ + if (flags & UFFD_USER_MODE_ONLY) + return true; + + /* + * The user is requesting a userfaultfd which can handle kernel faults. + * Privileged users are always allowed to do this. + */ + if (capable(CAP_SYS_PTRACE)) + return true; + + /* Otherwise, access to kernel fault handling is sysctl controlled. */ + return sysctl_unprivileged_userfaultfd; +} + +SYSCALL_DEFINE1(userfaultfd, int, flags) +{ + if (!userfaultfd_syscall_allowed(flags)) + return -EPERM; + + return new_userfaultfd(flags); +} + +static long userfaultfd_dev_ioctl(struct file *file, unsigned int cmd, unsigned long flags) +{ + if (cmd != USERFAULTFD_IOC_NEW) + return -EINVAL; + + return new_userfaultfd(flags); +} + +static const struct file_operations userfaultfd_dev_fops = { + .unlocked_ioctl = userfaultfd_dev_ioctl, + .compat_ioctl = userfaultfd_dev_ioctl, + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +static struct miscdevice userfaultfd_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "userfaultfd", + .fops = &userfaultfd_dev_fops +}; + +static int __init userfaultfd_init(void) +{ + int ret; + + ret = misc_register(&userfaultfd_misc); + if (ret) + return ret; + + userfaultfd_ctx_cachep = kmem_cache_create("userfaultfd_ctx_cache", + sizeof(struct userfaultfd_ctx), + 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, + init_once_userfaultfd_ctx); +#ifdef CONFIG_SYSCTL + register_sysctl_init("vm", vm_userfaultfd_table); +#endif + return 0; +} +__initcall(userfaultfd_init); From b182633f8ce52172a40097ccc0c60047e58c2320 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Sat, 23 May 2026 20:37:59 +0300 Subject: [PATCH 2879/5214] userfaultfd: make functions that are not used outside uffd static After merging fs/userfaultfd.c into mm/userfaultfd.c, several functions that were previously shared between the two files are now only used within mm/userfaultfd.c. Make them static and remove their declarations from include/linux/userfaultfd_k.h. Link: https://lore.kernel.org/20260523173759.3964908-3-rppt@kernel.org Assisted-by: Copilot:claude-opus-4-6 Signed-off-by: Mike Rapoport (Microsoft) Cc: Al Viro Cc: Christian Brauner Cc: David Hildenbrand Cc: Jan Kara Cc: "Kirill A. Shutemov" Cc: Peter Xu Signed-off-by: Andrew Morton --- include/linux/userfaultfd_k.h | 36 ----------------------------------- mm/userfaultfd.c | 24 +++++++++++------------ 2 files changed, 12 insertions(+), 48 deletions(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index d2920f98ab86..3ec8e1071673 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -147,26 +147,12 @@ static inline uffd_flags_t uffd_flags_set_mode(uffd_flags_t flags, enum mfill_at /* Flags controlling behavior. These behavior changes are mode-independent. */ #define MFILL_ATOMIC_WP MFILL_ATOMIC_FLAG(0) -extern ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned long dst_start, - unsigned long src_start, unsigned long len, - uffd_flags_t flags); -extern ssize_t mfill_atomic_zeropage(struct userfaultfd_ctx *ctx, - unsigned long dst_start, - unsigned long len); -extern ssize_t mfill_atomic_continue(struct userfaultfd_ctx *ctx, unsigned long dst_start, - unsigned long len, uffd_flags_t flags); -extern ssize_t mfill_atomic_poison(struct userfaultfd_ctx *ctx, unsigned long start, - unsigned long len, uffd_flags_t flags); -extern int mwriteprotect_range(struct userfaultfd_ctx *ctx, unsigned long start, - unsigned long len, bool enable_wp); extern long uffd_wp_range(struct vm_area_struct *vma, unsigned long start, unsigned long len, bool enable_wp); /* move_pages */ void double_pt_lock(spinlock_t *ptl1, spinlock_t *ptl2); void double_pt_unlock(spinlock_t *ptl1, spinlock_t *ptl2); -ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, - unsigned long src_start, unsigned long len, __u64 flags); int move_pages_huge_pmd(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd, pmd_t dst_pmdval, struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma, @@ -239,9 +225,6 @@ static inline bool userfaultfd_armed(struct vm_area_struct *vma) return vma->vm_flags & __VM_UFFD_FLAGS; } -bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, - bool wp_async); - static inline bool vma_has_uffd_without_event_remap(struct vm_area_struct *vma) { struct userfaultfd_ctx *uffd_ctx = vma->vm_userfaultfd_ctx.ctx; @@ -271,25 +254,6 @@ extern void userfaultfd_unmap_complete(struct mm_struct *mm, extern bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma); extern bool userfaultfd_wp_async(struct vm_area_struct *vma); -void userfaultfd_reset_ctx(struct vm_area_struct *vma); - -struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi, - struct vm_area_struct *prev, - struct vm_area_struct *vma, - unsigned long start, - unsigned long end); - -int userfaultfd_register_range(struct userfaultfd_ctx *ctx, - struct vm_area_struct *vma, - vm_flags_t vm_flags, - unsigned long start, unsigned long end, - bool wp_async); - -void userfaultfd_release_new(struct userfaultfd_ctx *ctx); - -void userfaultfd_release_all(struct mm_struct *mm, - struct userfaultfd_ctx *ctx); - static inline bool userfaultfd_wp_use_markers(struct vm_area_struct *vma) { /* Only wr-protect mode uses pte markers */ diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 74af5682f3fb..c86daf38d154 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -1033,7 +1033,7 @@ static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx, return copied ? copied : err; } -ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned long dst_start, +static ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned long dst_start, unsigned long src_start, unsigned long len, uffd_flags_t flags) { @@ -1041,7 +1041,7 @@ ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned long dst_start, uffd_flags_set_mode(flags, MFILL_ATOMIC_COPY)); } -ssize_t mfill_atomic_zeropage(struct userfaultfd_ctx *ctx, +static ssize_t mfill_atomic_zeropage(struct userfaultfd_ctx *ctx, unsigned long start, unsigned long len) { @@ -1049,7 +1049,7 @@ ssize_t mfill_atomic_zeropage(struct userfaultfd_ctx *ctx, uffd_flags_set_mode(0, MFILL_ATOMIC_ZEROPAGE)); } -ssize_t mfill_atomic_continue(struct userfaultfd_ctx *ctx, unsigned long start, +static ssize_t mfill_atomic_continue(struct userfaultfd_ctx *ctx, unsigned long start, unsigned long len, uffd_flags_t flags) { @@ -1065,7 +1065,7 @@ ssize_t mfill_atomic_continue(struct userfaultfd_ctx *ctx, unsigned long start, uffd_flags_set_mode(flags, MFILL_ATOMIC_CONTINUE)); } -ssize_t mfill_atomic_poison(struct userfaultfd_ctx *ctx, unsigned long start, +static ssize_t mfill_atomic_poison(struct userfaultfd_ctx *ctx, unsigned long start, unsigned long len, uffd_flags_t flags) { return mfill_atomic(ctx, start, 0, len, @@ -1101,7 +1101,7 @@ long uffd_wp_range(struct vm_area_struct *dst_vma, return ret; } -int mwriteprotect_range(struct userfaultfd_ctx *ctx, unsigned long start, +static int mwriteprotect_range(struct userfaultfd_ctx *ctx, unsigned long start, unsigned long len, bool enable_wp) { struct mm_struct *dst_mm = ctx->mm; @@ -1931,7 +1931,7 @@ static void uffd_move_unlock(struct vm_area_struct *dst_vma, * in the regions or not, but preventing the risk of having to split * the hugepmd during the remap. */ -ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, +static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, unsigned long src_start, unsigned long len, __u64 mode) { struct mm_struct *mm = ctx->mm; @@ -2106,7 +2106,7 @@ ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, return moved ? moved : err; } -bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, +static bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, bool wp_async) { const struct vm_uffd_ops *ops = vma_uffd_ops(vma); @@ -2163,12 +2163,12 @@ static void userfaultfd_set_ctx(struct vm_area_struct *vma, (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags); } -void userfaultfd_reset_ctx(struct vm_area_struct *vma) +static void userfaultfd_reset_ctx(struct vm_area_struct *vma) { userfaultfd_set_ctx(vma, NULL, 0); } -struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi, +static struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi, struct vm_area_struct *prev, struct vm_area_struct *vma, unsigned long start, @@ -2207,7 +2207,7 @@ struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi, } /* Assumes mmap write lock taken, and mm_struct pinned. */ -int userfaultfd_register_range(struct userfaultfd_ctx *ctx, +static int userfaultfd_register_range(struct userfaultfd_ctx *ctx, struct vm_area_struct *vma, vm_flags_t vm_flags, unsigned long start, unsigned long end, @@ -2271,7 +2271,7 @@ int userfaultfd_register_range(struct userfaultfd_ctx *ctx, return 0; } -void userfaultfd_release_new(struct userfaultfd_ctx *ctx) +static void userfaultfd_release_new(struct userfaultfd_ctx *ctx) { struct mm_struct *mm = ctx->mm; struct vm_area_struct *vma; @@ -2286,7 +2286,7 @@ void userfaultfd_release_new(struct userfaultfd_ctx *ctx) mmap_write_unlock(mm); } -void userfaultfd_release_all(struct mm_struct *mm, +static void userfaultfd_release_all(struct mm_struct *mm, struct userfaultfd_ctx *ctx) { struct vm_area_struct *vma, *prev; From 0491e9f75c1515ecff3dfb7d7bd4243e6f47027d Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:52 +0800 Subject: [PATCH 2880/5214] mm/mglru: consolidate common code for retrieving evictable size Patch series "mm/mglru: improve reclaim loop and dirty folio", v7. This series cleans up and slightly improves MGLRU's reclaim loop and dirty writeback handling. As a result, we can see an up to ~30% increase in some workloads like MongoDB with YCSB and a huge decrease in file refault, no swap involved. Other common benchmarks have no regression, and LOC is reduced, with less unexpected OOM, too. Some of the problems were found in our production environment, and others were mostly exposed while stress testing during the development of the LSM/MM/BPF topic on improving MGLRU [1]. This series cleans up the code base and fixes several performance issues, preparing for further work. MGLRU's reclaim loop is a bit complex, and hence these problems are somehow related to each other. The aging, scan number calculation, and reclaim loop are coupled together, and the dirty folio handling logic is quite different, making the reclaim loop hard to follow and the dirty flush ineffective. This series slightly cleans up and improves these issues using a scan budget by calculating the number of folios to scan at the beginning of the loop, and decouples aging from the reclaim calculation helpers. Then, move the dirty flush logic inside the reclaim loop so it can kick in more effectively. These issues are somehow related, and this series handles them and improves MGLRU reclaim in many ways. Test results: All tests are done on a 48c96t NUMA machine with 2 nodes and a 128G memory machine using NVME as storage. Classical (non-MGLRU) LRU numbers are included as "MGLRU disabled" for each benchmark below; see [8] and [9] for the longer write-up. MongoDB ======= Running YCSB workloadb [2] (recordcount:20000000 operationcount:6000000, threads:32), which does 95% read and 5% update to generate mixed read and dirty writeback. MongoDB is set up in a 10G cgroup using Docker, and the WiredTiger cache size is set to 4.5G, using NVME as storage. This is close to the case we observed regressing in our production environment: mixed read and writeback pressure, so it is a practical case for evaluation. Not using SWAP. The intent is to isolate the file LRU writeback path. Enabling SWAP would just add noise from anonymous reclaim. MGLRU Before: Throughput(ops/sec): 60653.502655 workingset_refault_file 12904916 pgpgin 165366622 pgpgout 5219588 MGLRU After: Throughput(ops/sec): 82384.354760 (+35.8%, higher is better) workingset_refault_file 7128285 (-44.7%, lower is better) pgpgin 113170693 (-31.5%, lower is better) pgpgout 5639724 MGLRU Disabled: Throughput(ops/sec): 93713.640901 workingset_refault_file 15013443 pgpgin 85365614 pgpgout 5866508 We can see a significant performance improvement after this series. The test is done on NVME and the performance gap would be even larger for slow devices, such as HDD or network storage. We observed over 100% gain for some workloads with slow IO. Note, classical LRU is still faster for this benchmark, MGLRU may catch up later with further work [7]. Chrome & Node.js [3] ==================== Using Yu Zhao's test script [3], testing on a x86_64 NUMA machine with 2 nodes and 128G memory, using 256G ZRAM as swap and spawn 32 memcg 64 workers. Many memcgs each applying roughly equal pressure exercises the LRU's ability to detect/protect each tenant's working set and to balance reclamation fairly between tenants, which makes this a meaningful test for the reclaim mechanism. Fairness is reported via Jain's fairness index (1.0 means all tenants get exactly equal allocation, lower is worse). Under equal pressure, all memcgs should make roughly equal forward progress. See [8] for the longer rationale and per-memcg breakdown. MGLRU before: Total requests: 81898 Per-worker mean: 1279.7 Per-worker 95% CI (mean): [ 1259.0, 1300.4] Jain's fairness index: 0.995893 (1.0 = perfectly fair) Latency: Bucket Count Pct Cumul [0,1)s 28392 34.67% 34.67% [1,2)s 8022 9.80% 44.46% [2,4)s 6130 7.48% 51.95% [4,8)s 39354 48.05% 100.00% MGLRU after: Total requests: 82901 Per-worker mean: 1295.3 Per-worker 95% CI (mean): [ 1265.3, 1325.4] Jain's fairness index: 0.991607 (1.0 = perfectly fair) Latency: Bucket Count Pct Cumul [0,1)s 28128 33.93% 33.93% [1,2)s 8756 10.56% 44.49% [2,4)s 7028 8.48% 52.97% [4,8)s 38989 47.03% 100.00% MGLRU disabled: Total requests: 62399 Per-worker mean: 975.0 Per-worker 95% CI (mean): [ 941.9, 1008.1] Jain's fairness index: 0.982156 (1.0 = perfectly fair) Latency: Bucket Count Pct Cumul [0,1)s 20051 32.13% 32.13% [1,2)s 2255 3.61% 35.75% [2,4)s 6149 9.85% 45.60% [4,8)s 33927 54.37% 99.97% [8,16)s 17 0.03% 100.00% Reclaim is still fair and effective, total requests number seems slightly better. OOM issue with aging and throttling =================================== For the throttling OOM issue, it can be easily reproduced using dd and cgroup limit as demonstrated and fixed by a later patch in this series. The aging OOM is a bit tricky, a specific reproducer can be used to simulate what we encountered in production environment [4]: Spawns multiple workers that keep reading the given file using mmap, and pauses for 120ms after one file read batch. It also spawns another set of workers that keep allocating and freeing a given size of anonymous memory. The total memory size exceeds the memory limit (eg. 14G anon + 8G file, which is 22G vs a 16G memcg limit). - MGLRU disabled: Finished 128 iterations. - MGLRU enabled: OOM with following info after about ~10-20 iterations: [ 62.624130] file_anon_mix_p invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0 [ 62.624999] memory: usage 16777216kB, limit 16777216kB, failcnt 24460 [ 62.640200] swap: usage 0kB, limit 9007199254740988kB, failcnt 0 [ 62.640823] Memory cgroup stats for /demo: [ 62.641017] anon 10604879872 [ 62.641941] file 6574858240 OOM occurs despite there being still evictable file folios. - MGLRU enabled after this series: Finished 128 iterations. Worth noting there is another OOM related issue reported in V1 of this series, which is tested and looking OK now [5]. MySQL: ====== Testing with innodb_buffer_pool_size=26106127360, in a 2G memcg, using ZRAM as swap and test command: sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-db=sb \ --tables=48 --table-size=2000000 --threads=48 --time=600 run A 24G InnoDB buffer pool inside a 2G memcg with ZRAM as swap forces aggressive eviction of cached database anon pages, which exercises the LRU's hot page detection and the eviction path under swap pressure. The workload is practical, and the pressure is higher than what we usually see in production but it is intended to expose the extreme case. MGLRU before: 17313.688333 tps MGLRU after: 17286.195000 tps MGLRU disabled: 16245.330000 tps Seems only noise level changes, no regression. FIO: ==== Testing with the following command, where /mnt/ramdisk is a 64G EXT4 ramdisk, each test file is 3G, in a 10G memcg, 6 test run each: fio --directory=/mnt/ramdisk --filename_format='test.$jobnum.img' \ --name=cached --numjobs=16 --size=3072M --buffered=1 --ioengine=mmap \ --rw=randread --norandommap --time_based \ --ramp_time=1m --runtime=5m --group_reporting Random buffered mmap read on a ramdisk strips out storage variance and stresses purely the LRU's ability to evict and recycle the page cache under heavy random read pressure. MGLRU before: 9033.91 MB/s MGLRU after: 9065.72 MB/s MGLRU disabled: 8254.54 MB/s Also seem only noise level changes and no regression or slightly better. Build kernel: ============= Build kernel test using ZRAM as swap, kernel source on tmpfs, in a memcg with memory.max=3G, using make -j96 and defconfig, measuring system time, 6 test run each. Building the kernel is a classical mixed anon + file workload (lots of small file reads/writes plus parallel anon allocations from cc/ld) and is representative of many real compilation jobs. MGLRU before: 2823.13s MGLRU after: 2801.26s MGLRU disabled: 5023.50s Also seem only noise level changes, no regression or very slightly better. Android: ======== Xinyu reported a performance gain on Android, too, with this series. The test consisted of cold-starting multiple applications sequentially under moderate system load [6]; this is a real Android user-visible scenario, dominated by the LRU's ability to keep the right working set resident and re-fault launch-critical pages quickly. Before: Launch Time Summary (all apps, all runs) Mean 868.0ms P50 888.0ms P90 1274.2ms P95 1399.0ms After: Launch Time Summary (all apps, all runs) Mean 850.5ms (-2.07%) P50 861.5ms (-3.04%) P90 1179.0ms (-8.05%) P95 1228.0ms (-12.2%) This patch (of 15): Merge commonly used code for counting evictable folios in a lruvec. No behavior change. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-0-02fabb92dc43@tencent.com Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-1-02fabb92dc43@tencent.com Link: https://lore.kernel.org/linux-mm/CAMgjq7BoekNjg-Ra3C8M7=8=75su38w=HD782T5E_cxyeCeH_g@mail.gmail.com/ [1] Link: https://github.com/brianfrankcooper/YCSB/blob/master/workloads/workloadb [2] Link: https://lore.kernel.org/all/20221220214923.1229538-1-yuzhao@google.com/ [3] Link: https://github.com/ryncsn/emm-test-project/tree/master/file-anon-mix-pressure [4] Link: https://lore.kernel.org/linux-mm/acgNCzRDVmSbXrOE@KASONG-MC4/ [5] Link: https://lore.kernel.org/linux-mm/20260417025123.2971253-1-wxy2009nrrr@163.com/ [6] Link: https://lore.kernel.org/linux-mm/20260502-mglru-fg-v1-0-913619b014d9@tencent.com/ [7] Link: https://lore.kernel.org/linux-mm/CAMgjq7BzQAPp8u_3-9e3ueXmRCoW=2sydok0hFM=MYL7VC1YYg@mail.gmail.com/ [8] Link: https://lore.kernel.org/linux-mm/CAMgjq7D+4QmiWe73OPFuH0s+ZKCUJoo+MfcWOdJcV+VO-T2Wmg@mail.gmail.com/ [9] Signed-off-by: Kairui Song Acked-by: Yuanchu Xie Reviewed-by: Barry Song Reviewed-by: Chen Ridong Reviewed-by: Axel Rasmussen Reviewed-by: Baolin Wang Acked-by: Shakeel Butt Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yu Zhao Cc: Leno Hou Signed-off-by: Andrew Morton --- mm/vmscan.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 76193a84a2af..5901219dd7fc 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4088,27 +4088,33 @@ static void set_initial_priority(struct pglist_data *pgdat, struct scan_control sc->priority = clamp(priority, DEF_PRIORITY / 2, DEF_PRIORITY); } -static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc) +static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness) { int gen, type, zone; - unsigned long total = 0; - int swappiness = get_swappiness(lruvec, sc); + unsigned long seq, total = 0; struct lru_gen_folio *lrugen = &lruvec->lrugen; - struct mem_cgroup *memcg = lruvec_memcg(lruvec); DEFINE_MAX_SEQ(lruvec); DEFINE_MIN_SEQ(lruvec); for_each_evictable_type(type, swappiness) { - unsigned long seq; - for (seq = min_seq[type]; seq <= max_seq; seq++) { gen = lru_gen_from_seq(seq); - for (zone = 0; zone < MAX_NR_ZONES; zone++) total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); } } + return total; +} + +static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc) +{ + unsigned long total; + int swappiness = get_swappiness(lruvec, sc); + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + + total = lruvec_evictable_size(lruvec, swappiness); + /* whether the size is big enough to be helpful */ return mem_cgroup_online(memcg) ? (total >> sc->priority) : total; } @@ -4913,9 +4919,6 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, int swappiness, unsigned long *nr_to_scan) { - int gen, type, zone; - unsigned long size = 0; - struct lru_gen_folio *lrugen = &lruvec->lrugen; DEFINE_MIN_SEQ(lruvec); *nr_to_scan = 0; @@ -4923,18 +4926,7 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq) return true; - for_each_evictable_type(type, swappiness) { - unsigned long seq; - - for (seq = min_seq[type]; seq <= max_seq; seq++) { - gen = lru_gen_from_seq(seq); - - for (zone = 0; zone < MAX_NR_ZONES; zone++) - size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); - } - } - - *nr_to_scan = size; + *nr_to_scan = lruvec_evictable_size(lruvec, swappiness); /* better to run aging even though eviction is still possible */ return evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq; } From 790d3abeca092523621bd358f2d3362a95c571bf Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:53 +0800 Subject: [PATCH 2881/5214] mm/mglru: rename variables related to aging and rotation The current variable name isn't helpful. Make the variable names more meaningful. Only naming change, no behavior change. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-2-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Suggested-by: Barry Song Reviewed-by: Baolin Wang Reviewed-by: Chen Ridong Reviewed-by: Barry Song Reviewed-by: Axel Rasmussen Acked-by: Shakeel Butt Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 5901219dd7fc..9c47a4aa825a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4938,7 +4938,7 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, */ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int swappiness) { - bool success; + bool need_aging; unsigned long nr_to_scan; struct mem_cgroup *memcg = lruvec_memcg(lruvec); DEFINE_MAX_SEQ(lruvec); @@ -4946,7 +4946,7 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int s if (mem_cgroup_below_min(sc->target_mem_cgroup, memcg)) return -1; - success = should_run_aging(lruvec, max_seq, swappiness, &nr_to_scan); + need_aging = should_run_aging(lruvec, max_seq, swappiness, &nr_to_scan); /* try to scrape all its memory if this memcg was deleted */ if (nr_to_scan && !mem_cgroup_online(memcg)) @@ -4955,7 +4955,7 @@ static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int s nr_to_scan = apply_proportional_protection(memcg, sc, nr_to_scan); /* try to get away with not aging at the default priority */ - if (!success || sc->priority == DEF_PRIORITY) + if (!need_aging || sc->priority == DEF_PRIORITY) return nr_to_scan >> sc->priority; /* stop scanning this lruvec as it's low on cold folios */ @@ -5044,7 +5044,7 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) { - bool success; + bool need_rotate; unsigned long scanned = sc->nr_scanned; unsigned long reclaimed = sc->nr_reclaimed; struct mem_cgroup *memcg = lruvec_memcg(lruvec); @@ -5062,7 +5062,7 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) memcg_memory_event(memcg, MEMCG_LOW); } - success = try_to_shrink_lruvec(lruvec, sc); + need_rotate = try_to_shrink_lruvec(lruvec, sc); shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority); @@ -5072,10 +5072,10 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) flush_reclaim_state(sc); - if (success && mem_cgroup_online(memcg)) + if (need_rotate && mem_cgroup_online(memcg)) return MEMCG_LRU_YOUNG; - if (!success && lruvec_is_sizable(lruvec, sc)) + if (!need_rotate && lruvec_is_sizable(lruvec, sc)) return 0; /* one retry if offlined or too small */ From aa6ef5b159dcc646d3add48c1580ba8e70df6c64 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:54 +0800 Subject: [PATCH 2882/5214] mm/mglru: relocate the LRU scan batch limit to callers Same as active / inactive LRU, MGLRU isolates and scans folios in batches. The batch split is done hidden deep in the helper, which makes the code harder to follow. The helper's arguments are also confusing since callers usually request more folios than the batch size, so the helper almost never processes the full requested amount. Move the batch splitting into the top loop to make it cleaner, there should be no behavior change. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-3-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Baolin Wang Reviewed-by: Barry Song Reviewed-by: Chen Ridong Acked-by: Shakeel Butt Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 9c47a4aa825a..abe2ace8e326 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4699,10 +4699,10 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, int scanned = 0; int isolated = 0; int skipped = 0; - int scan_batch = min(nr_to_scan, MAX_LRU_BATCH); - int remaining = scan_batch; + unsigned long remaining = nr_to_scan; struct lru_gen_folio *lrugen = &lruvec->lrugen; + VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH); VM_WARN_ON_ONCE(!list_empty(list)); if (get_nr_gens(lruvec, type) == MIN_NR_GENS) @@ -4755,7 +4755,7 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, mod_lruvec_state(lruvec, item, isolated); mod_lruvec_state(lruvec, PGREFILL, sorted); mod_lruvec_state(lruvec, PGSCAN_ANON + type, isolated); - trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, scan_batch, + trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scanned, skipped, isolated, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); if (type == LRU_GEN_FILE) @@ -4991,7 +4991,7 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) { - long nr_to_scan; + long nr_batch, nr_to_scan; unsigned long scanned = 0; int swappiness = get_swappiness(lruvec, sc); @@ -5002,7 +5002,8 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) if (nr_to_scan <= 0) break; - delta = evict_folios(nr_to_scan, lruvec, sc, swappiness); + nr_batch = min(nr_to_scan, MAX_LRU_BATCH); + delta = evict_folios(nr_batch, lruvec, sc, swappiness); if (!delta) break; @@ -5627,6 +5628,7 @@ static int run_aging(struct lruvec *lruvec, unsigned long seq, static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc, int swappiness, unsigned long nr_to_reclaim) { + int nr_batch; DEFINE_MAX_SEQ(lruvec); if (seq + MIN_NR_GENS > max_seq) @@ -5643,8 +5645,8 @@ static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_co if (sc->nr_reclaimed >= nr_to_reclaim) return 0; - if (!evict_folios(nr_to_reclaim - sc->nr_reclaimed, lruvec, sc, - swappiness)) + nr_batch = min(nr_to_reclaim - sc->nr_reclaimed, MAX_LRU_BATCH); + if (!evict_folios(nr_batch, lruvec, sc, swappiness)) return 0; cond_resched(); From 163bc3d68c9f373a96827ecefe370ff989f26747 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:55 +0800 Subject: [PATCH 2883/5214] mm/mglru: restructure the reclaim loop The current loop will calculate the scan number on each iteration. The number of folios to scan is based on the LRU length, with some unclear behaviors, e.g, the scan number is only shifted by reclaim priority when aging is not needed or when at the default priority, and it couples the number calculation with aging and rotation. Adjust, simplify it, and decouple aging and rotation. Just calculate the scan number for once at the beginning of the reclaim, always respect the reclaim priority, and make the aging and rotation more explicit. This slightly changes how aging and offline memcg reclaim works: Previously, aging was skipped at DEF_PRIORITY even when eviction was no longer possible, so the reclaimer wasted an iteration until the priority escalated. Now aging runs immediately whenever it is needed to make progress; the DEF_PRIORITY skip only applies when eviction is still viable. This may avoid wasted iterations that over-reclaim slab and break reclaim balance in multi-cgroup setups. Similar for offline memcg. Previously, offline memcg wouldn't be aged unless it didn't have any evictable folios. Now, we might age it if it has only 3 generations, which should be fine. On one hand, offline memcg might still hold long-term folios, and in fact, a long-existing offline memcg must be pinned by some long-term folios like shmem. These folios might be used by other memcg, so aging them as ordinary memcg seems correct. Besides, aging enables further reclaim of an offlined memcg, which will certainly happen if we keep shrinking it. And offline memcg might soon be no longer an issue with reparenting. Overall, the memcg LRU rotation, as described in mmzone.h, remains the same. Note that because the scan budget is now pinned at loop entry, tiny lruvec might skip this reclaim pass, also skipping aging, which could be beneficial as aging is not helpful since it will still be un-reclaimable after aging. Reclaim will go on as usual once priority escalates. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-4-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Acked-by: Shakeel Butt Cc: Baolin Wang Cc: Barry Song Cc: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 72 ++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index abe2ace8e326..66ddf211e3ca 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4917,49 +4917,37 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, } static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, - int swappiness, unsigned long *nr_to_scan) + struct scan_control *sc, int swappiness) { DEFINE_MIN_SEQ(lruvec); - *nr_to_scan = 0; /* have to run aging, since eviction is not possible anymore */ if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq) return true; - *nr_to_scan = lruvec_evictable_size(lruvec, swappiness); + /* try to avoid aging, do gentle reclaim at the default priority */ + if (sc->priority == DEF_PRIORITY) + return false; + /* better to run aging even though eviction is still possible */ return evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq; } -/* - * For future optimizations: - * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg - * reclaim. - */ -static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int swappiness) +static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, + struct mem_cgroup *memcg, int swappiness) { - bool need_aging; - unsigned long nr_to_scan; - struct mem_cgroup *memcg = lruvec_memcg(lruvec); - DEFINE_MAX_SEQ(lruvec); + unsigned long nr_to_scan, evictable; - if (mem_cgroup_below_min(sc->target_mem_cgroup, memcg)) - return -1; - - need_aging = should_run_aging(lruvec, max_seq, swappiness, &nr_to_scan); + evictable = lruvec_evictable_size(lruvec, swappiness); /* try to scrape all its memory if this memcg was deleted */ - if (nr_to_scan && !mem_cgroup_online(memcg)) - return nr_to_scan; + if (!mem_cgroup_online(memcg)) + return evictable; - nr_to_scan = apply_proportional_protection(memcg, sc, nr_to_scan); + nr_to_scan = apply_proportional_protection(memcg, sc, evictable); + nr_to_scan >>= sc->priority; - /* try to get away with not aging at the default priority */ - if (!need_aging || sc->priority == DEF_PRIORITY) - return nr_to_scan >> sc->priority; - - /* stop scanning this lruvec as it's low on cold folios */ - return try_to_inc_max_seq(lruvec, max_seq, swappiness, false) ? -1 : 0; + return nr_to_scan; } static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) @@ -4989,31 +4977,44 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) return true; } +/* + * For future optimizations: + * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg + * reclaim. + */ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) { + bool need_rotate = false; long nr_batch, nr_to_scan; - unsigned long scanned = 0; int swappiness = get_swappiness(lruvec, sc); + struct mem_cgroup *memcg = lruvec_memcg(lruvec); - while (true) { + nr_to_scan = get_nr_to_scan(lruvec, sc, memcg, swappiness); + while (nr_to_scan > 0) { int delta; + DEFINE_MAX_SEQ(lruvec); - nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); - if (nr_to_scan <= 0) + if (mem_cgroup_below_min(sc->target_mem_cgroup, memcg)) { + need_rotate = true; break; + } + + if (should_run_aging(lruvec, max_seq, sc, swappiness)) { + if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false)) + need_rotate = true; + /* stop scanning as it's low on cold folios */ + break; + } nr_batch = min(nr_to_scan, MAX_LRU_BATCH); delta = evict_folios(nr_batch, lruvec, sc, swappiness); if (!delta) break; - scanned += delta; - if (scanned >= nr_to_scan) - break; - if (should_abort_scan(lruvec, sc)) break; + nr_to_scan -= delta; cond_resched(); } @@ -5039,8 +5040,7 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); } - /* whether this lruvec should be rotated */ - return nr_to_scan < 0; + return need_rotate; } static int shrink_one(struct lruvec *lruvec, struct scan_control *sc) From 3a72e078b4a32ffd88f416d278882034b3321481 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:56 +0800 Subject: [PATCH 2884/5214] mm/mglru: scan and count the exact number of folios Make the scan helpers return the exact number of folios being scanned or isolated. Since the reclaim loop now has a natural scan budget that controls the scan progress, returning the scan number and consuming the budget makes the scan more accurate and easier to follow. The number of scanned folios for each iteration is always larger than 0, unless the reclaim must stop for a forced aging, so there is no more need for any special handling when there is no progress made: - `return isolated || !remaining ? scanned : 0` in scan_folios: both the function and the call now just return the exact scan count, combined with the scan budget introduced in the previous commit to avoid livelock or under scan. - `scanned += try_to_inc_min_seq` in evict_folios: adding a bool as a scan count was kind of confusing and no longer needed, as scan number should never be zero as long as there are still evictable gens. We may encounter a empty old gen that returns 0 scan count, to avoid that, do a try_to_inc_min_seq before toisolation which have slight to none overhead in most cases. - `evictable_min_seq + MIN_NR_GENS > max_seq` guard in evict_folios: the per-type get_nr_gens == MIN_NR_GENS check in scan_folios naturally returns 0 when only two gens remain and breaks the loop. Also change try_to_inc_min_seq to return void, as its return value is no longer used by any caller. Call it before isolate_folios to flush any empty gens left by external folio freeing, and again after isolate_folios when scanning moved or protected folios may have emptied the oldest gen. The scan still stops if only two gens are left, as the scan number will be zero. This matches the previous behavior. This forced gen protection may be removed or softened later to improve reclaim further. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-5-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Chen Ridong Reviewed-by: Baolin Wang Cc: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 66ddf211e3ca..adfe3e6645d6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3882,10 +3882,9 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness) return true; } -static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) +static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) { int gen, type, zone; - bool success = false; bool seq_inc_flag = false; struct lru_gen_folio *lrugen = &lruvec->lrugen; DEFINE_MIN_SEQ(lruvec); @@ -3911,11 +3910,10 @@ static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) /* * If min_seq[type] of both anonymous and file is not increased, - * we can directly return false to avoid unnecessary checking - * overhead later. + * return here to avoid unnecessary checking overhead later. */ if (!seq_inc_flag) - return success; + return; /* see the comment on lru_gen_folio */ if (swappiness && swappiness <= MAX_SWAPPINESS) { @@ -3933,10 +3931,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) reset_ctrl_pos(lruvec, type, true); WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); - success = true; } - - return success; } static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness) @@ -4690,7 +4685,7 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct scan_control *sc, int type, int tier, - struct list_head *list) + struct list_head *list, int *isolatedp) { int i; int gen; @@ -4760,11 +4755,9 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); if (type == LRU_GEN_FILE) sc->nr.file_taken += isolated; - /* - * There might not be eligible folios due to reclaim_idx. Check the - * remaining to prevent livelock if it's not making progress. - */ - return isolated || !remaining ? scanned : 0; + + *isolatedp = isolated; + return scanned; } static int get_tier_idx(struct lruvec *lruvec, int type) @@ -4808,33 +4801,36 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness) static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct scan_control *sc, int swappiness, - int *type_scanned, struct list_head *list) + struct list_head *list, int *isolated, + int *isolate_type, int *isolate_scanned) { int i; + int total_scanned = 0; int type = get_type_to_scan(lruvec, swappiness); for_each_evictable_type(i, swappiness) { int scanned; int tier = get_tier_idx(lruvec, type); - *type_scanned = type; + scanned = scan_folios(nr_to_scan, lruvec, sc, + type, tier, list, isolated); - scanned = scan_folios(nr_to_scan, lruvec, sc, type, tier, list); - if (scanned) - return scanned; + total_scanned += scanned; + if (*isolated) { + *isolate_type = type; + *isolate_scanned = scanned; + break; + } type = !type; } - return 0; + return total_scanned; } static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct scan_control *sc, int swappiness) { - int type; - int scanned; - int reclaimed; LIST_HEAD(list); LIST_HEAD(clean); struct folio *folio; @@ -4842,19 +4838,23 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, enum node_stat_item item; struct reclaim_stat stat; struct lru_gen_mm_walk *walk; + int scanned, reclaimed; + int isolated = 0, type, type_scanned; bool skip_retry = false; - struct lru_gen_folio *lrugen = &lruvec->lrugen; struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); lruvec_lock_irq(lruvec); - scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, &type, &list); + /* In case folio deletion left empty old gens, flush them */ + try_to_inc_min_seq(lruvec, swappiness); - scanned += try_to_inc_min_seq(lruvec, swappiness); + scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, + &list, &isolated, &type, &type_scanned); - if (evictable_min_seq(lrugen->min_seq, swappiness) + MIN_NR_GENS > lrugen->max_seq) - scanned = 0; + /* Scanning may have emptied the oldest gen, flush it */ + if (scanned) + try_to_inc_min_seq(lruvec, swappiness); lruvec_unlock_irq(lruvec); @@ -4865,7 +4865,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, sc->nr.unqueued_dirty += stat.nr_unqueued_dirty; sc->nr_reclaimed += reclaimed; trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id, - scanned, reclaimed, &stat, sc->priority, + type_scanned, reclaimed, &stat, sc->priority, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); list_for_each_entry_safe_reverse(folio, next, &list, lru) { From 16b475d2ac3c7994370254644015ae2e5dd5210b Mon Sep 17 00:00:00 2001 From: "Barry Song (Xiaomi)" Date: Tue, 28 Apr 2026 02:06:57 +0800 Subject: [PATCH 2885/5214] mm/mglru: avoid reclaim type fall back when isolation makes no progress While isolation makes no progress in scan_folios(), we quickly fall back to the other type in isolate_folios(). This is incorrect, as the current type may still have sufficient folios. Falling back can undermine the positive_ctrl_err() result from get_type_to_scan(), which is derived from swappiness. So just continue scanning this type for another round. Worth noting if the cold generations are all reclaimed, scan will no longer make any progress either, which may undermine the swappiness again. This is not a new issue and hence better be fixed later [1]. Link: https://lore.kernel.org/linux-mm/CAGsJ_4zjdOYEtuO6gNjABm7NDxW0skzBFNRNee-k2D6VwsYEQA@mail.gmail.com/ [1] Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-6-02fabb92dc43@tencent.com Signed-off-by: Barry Song (Xiaomi) Signed-off-by: Kairui Song Reviewed-by: Kairui Song Cc: Axel Rasmussen Cc: Baolin Wang Cc: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index adfe3e6645d6..32ffbb557e15 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4821,8 +4821,13 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec, *isolate_scanned = scanned; break; } - - type = !type; + /* + * If scanned > 0 and isolated == 0, avoid falling back to the + * other type, as this type remains sufficient. Falling back + * too readily can disrupt the positive_ctrl_err() bias. + */ + if (!scanned) + type = !type; } return total_scanned; From 6e9be217a3cecbd8e8a5beec2aeba4ae9ebd2af9 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:58 +0800 Subject: [PATCH 2886/5214] mm/mglru: use a smaller batch for reclaim With a fixed number to reclaim calculated at the beginning, making each following step smaller should reduce the lock contention and avoid over-aggressive reclaim of folios, as it will abort earlier when the number of folios to be reclaimed is reached. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-7-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Chen Ridong Reviewed-by: Baolin Wang Reviewed-by: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 32ffbb557e15..6128b191b81d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -5011,7 +5011,7 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) break; } - nr_batch = min(nr_to_scan, MAX_LRU_BATCH); + nr_batch = min(nr_to_scan, MIN_LRU_BATCH); delta = evict_folios(nr_batch, lruvec, sc, swappiness); if (!delta) break; From 12316f7902f850e2770d26a91fb13728b5ade065 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:06:59 +0800 Subject: [PATCH 2887/5214] mm/mglru: don't abort scan immediately right after aging Right now, if eviction triggers aging, the reclaimer will abort. This is not the optimal strategy for several reasons. Aborting the reclaim early wastes a reclaim cycle when under pressure, and for concurrent reclaim, if the LRU is under aging, all concurrent reclaimers might fail. And if the age has just finished, new cold folios exposed by the aging are not reclaimed until the next reclaim iteration. What's more, the current aging trigger is quite lenient, having 3 gens with a reclaim priority lower than default will trigger aging, and blocks reclaiming from one memcg. This wastes reclaim retry cycles easily. And in the worst case, if the reclaim is making slower progress and all following attempts fail due to being blocked by aging, it triggers unexpected early OOM. And if a lruvec requires aging, it doesn't mean it's hot. Instead, the lruvec could be idle for quite a while, and hence it might contain lots of cold folios to be reclaimed. While it's helpful to rotate memcg LRU after aging for global reclaim, as global reclaim fairness is coupled with the rotation in shrink_many, memcg fairness is instead handled by cgroup iteration in shrink_node_memcgs. So, for memcg level pressure, this abort is not the key part for keeping the fairness. And in most cases, there is no need to age, and fairness must be achieved by upper-level reclaim control. So instead, just keep the scanning going unless one whole batch of folios failed to be isolated or enough folios have been scanned, which is triggered by evict_folios returning 0. And only abort for global reclaim after one batch, so when there are fewer memcgs, progress is still made, and the fairness mechanism described above still works fine. And in most cases, the one more batch attempt for global reclaim might just be enough to satisfy what the reclaimer needs, hence improving global reclaim performance by reducing reclaim retry cycles. Rotation is still there after the reclaim is done, which still follows the comment in mmzone.h. And fairness still looking good. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-8-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Chen Ridong Reviewed-by: Barry Song Cc: Baolin Wang Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 6128b191b81d..daad01a07e33 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4989,7 +4989,7 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) */ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) { - bool need_rotate = false; + bool need_rotate = false, should_age = false; long nr_batch, nr_to_scan; int swappiness = get_swappiness(lruvec, sc); struct mem_cgroup *memcg = lruvec_memcg(lruvec); @@ -5007,8 +5007,7 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) if (should_run_aging(lruvec, max_seq, sc, swappiness)) { if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false)) need_rotate = true; - /* stop scanning as it's low on cold folios */ - break; + should_age = true; } nr_batch = min(nr_to_scan, MIN_LRU_BATCH); @@ -5019,6 +5018,13 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) if (should_abort_scan(lruvec, sc)) break; + /* + * Root reclaim needs rotation when low on cold folio for better + * fairness. Cgroup reclaim gets fairness from the iterator. + */ + if (root_reclaim(sc) && should_age) + break; + nr_to_scan -= delta; cond_resched(); } From acd22fbb9f4714d9beb1796aa27ac7e92d6ab9b3 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:00 +0800 Subject: [PATCH 2888/5214] mm/mglru: remove redundant swap constrained check upon isolation Remove the swap-constrained early reject check upon isolation. This check is a micro optimization when swap IO is not allowed, so folios are rejected early. But it is redundant and overly broad since shrink_folio_list() already handles all these cases with proper granularity. Notably, this check wrongly rejected lazyfree folios, and it doesn't cover all rejection cases. shrink_folio_list() uses may_enter_fs(), which distinguishes non-SWP_FS_OPS devices from filesystem-backed swap and does all the checks after folio is locked, so flags like swap cache are stable. This check also covers dirty file folios, which are not a problem now since sort_folio() already bumps dirty file folios to the next generation, but causes trouble for unifying dirty folio writeback handling. And there should be no performance impact from removing it. We may have lost a micro optimization, but unblocked lazyfree reclaim for NOIO contexts, which is not a common case in the first place. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-9-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Baolin Wang Reviewed-by: Chen Ridong Reviewed-by: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index daad01a07e33..c5de863aeceb 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4654,12 +4654,6 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca { bool success; - /* swap constrained */ - if (!(sc->gfp_mask & __GFP_IO) && - (folio_test_dirty(folio) || - (folio_test_anon(folio) && !folio_test_swapcache(folio)))) - return false; - /* raced with release_pages() */ if (!folio_try_get(folio)) return false; From 75d4c3f5fb980de1b620adede47e43dff4d6a5f3 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:01 +0800 Subject: [PATCH 2889/5214] mm/mglru: use the common routine for dirty/writeback reactivation Currently MGLRU will move the dirty writeback folios to the second oldest gen instead of reactivate them like the classical LRU. This might help to reduce the LRU contention as it skipped the isolation. But as a result we will see these folios at the LRU tail more frequently leading to inefficient reclaim. Besides, the dirty / writeback check after isolation in shrink_folio_list is more accurate and covers more cases. So instead, just drop the special handling for dirty writeback, use the common routine and re-activate it like the classical LRU. This should in theory improve the scan efficiency. These folios will be rotated back to LRU tail once writeback is done so there is no risk of hotness inversion. And now each reclaim loop will have a higher success rate. This also prepares for unifying the writeback and throttling mechanism with classical LRU, we keep these folios far from tail so detecting the tail batch will have a similar pattern with classical LRU. The micro optimization that avoids LRU contention by skipping the isolation is gone, which should be fine. Compared to IO and writeback cost, the isolation overhead is trivial. And using the common routine also keeps the folio's referenced bits (tier bits), which could improve metrics in the long term. Also no more need to clean reclaim bit as the common routine will make use of it. Note the common routine updates a few throttling and writeback counters, which are not used, and never have been for the MGLRU case. We will start making use of these in later commits. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-10-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Barry Song Reviewed-by: Baolin Wang Cc: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index c5de863aeceb..e699425c5b06 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4582,7 +4582,6 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c int tier_idx) { bool success; - bool dirty, writeback; int gen = folio_lru_gen(folio); int type = folio_is_file_lru(folio); int zone = folio_zonenum(folio); @@ -4632,21 +4631,6 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c return true; } - dirty = folio_test_dirty(folio); - writeback = folio_test_writeback(folio); - if (type == LRU_GEN_FILE && dirty) { - sc->nr.file_taken += delta; - if (!writeback) - sc->nr.unqueued_dirty += delta; - } - - /* waiting for writeback */ - if (writeback || (type == LRU_GEN_FILE && dirty)) { - gen = folio_inc_gen(lruvec, folio, true); - list_move(&folio->lru, &lrugen->folios[gen][type][zone]); - return true; - } - return false; } @@ -4668,9 +4652,6 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca if (!folio_test_referenced(folio)) set_mask_bits(&folio->flags.f, LRU_REFS_MASK, 0); - /* for shrink_folio_list() */ - folio_clear_reclaim(folio); - success = lru_gen_del_folio(lruvec, folio, true); VM_WARN_ON_ONCE_FOLIO(!success, folio); From f37d3708b676574379a00f8dafe6c89d92f166e9 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:02 +0800 Subject: [PATCH 2890/5214] mm/mglru: simplify and improve dirty writeback handling Right now the flusher wakeup mechanism for MGLRU is less responsive and unlikely to trigger compared to classical LRU. The classical LRU wakes the flusher if one batch of folios passed to shrink_folio_list is unevictable due to under writeback. MGLRU instead check and handle this after the whole reclaim loop is done. We previously even saw OOM problems due to passive flusher, which were fixed but still not perfect [1]. We have just unified the dirty folio counting and activation routine, now just move the dirty flush into the loop right after shrink_folio_list. This improves the performance a lot for workloads involving heavy writeback and prepares for throttling too. Test with YCSB workloadb showed a major performance improvement: Before this series: Throughput(ops/sec): 62485.02962831822 AverageLatency(us): 500.9746963330107 pgpgin 159347462 workingset_refault_file 34522071 After this commit: Throughput(ops/sec): 80857.08510208207 AverageLatency(us): 386.653262968934 pgpgin 112233121 workingset_refault_file 19516246 The performance is a lot better with significantly lower refault. We also observed similar or higher performance gain for other real-world workloads. We were concerned that the dirty flush could cause more wear for SSD: that should not be the problem here, since the wakeup condition is when the dirty folios have been pushed to the tail of LRU, which indicates that memory pressure is so high that writeback is blocking the workload already. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-11-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Link: https://lore.kernel.org/linux-mm/20241026115714.1437435-1-jingxiangzeng.cas@gmail.com/ [1] Reviewed-by: Baolin Wang Cc: Barry Song Cc: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index e699425c5b06..d26c89546542 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4728,8 +4728,6 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scanned, skipped, isolated, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); - if (type == LRU_GEN_FILE) - sc->nr.file_taken += isolated; *isolatedp = isolated; return scanned; @@ -4842,12 +4840,27 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, return scanned; retry: reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg); - sc->nr.unqueued_dirty += stat.nr_unqueued_dirty; sc->nr_reclaimed += reclaimed; trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id, type_scanned, reclaimed, &stat, sc->priority, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); + /* + * If too many file cache in the coldest generation can't be evicted + * due to being dirty, wake up the flusher. + */ + if (stat.nr_unqueued_dirty == isolated) { + wakeup_flusher_threads(WB_REASON_VMSCAN); + + /* + * For cgroupv1 dirty throttling is achieved by waking up + * the kernel flusher here and later waiting on folios + * which are in writeback to finish (see shrink_folio_list()). + */ + if (!writeback_throttling_sane(sc)) + reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); + } + list_for_each_entry_safe_reverse(folio, next, &list, lru) { DEFINE_MIN_SEQ(lruvec); @@ -5004,28 +5017,6 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) cond_resched(); } - /* - * If too many file cache in the coldest generation can't be evicted - * due to being dirty, wake up the flusher. - */ - if (sc->nr.unqueued_dirty && sc->nr.unqueued_dirty == sc->nr.file_taken) { - struct pglist_data *pgdat = lruvec_pgdat(lruvec); - - wakeup_flusher_threads(WB_REASON_VMSCAN); - - /* - * For cgroupv1 dirty throttling is achieved by waking up - * the kernel flusher here and later waiting on folios - * which are in writeback to finish (see shrink_folio_list()). - * - * Flusher may not be able to issue writeback quickly - * enough for cgroupv1 writeback throttling to work - * on a large system. - */ - if (!writeback_throttling_sane(sc)) - reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); - } - return need_rotate; } From 32d87083ee973adf44c11f61c3eb1440d275f314 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:03 +0800 Subject: [PATCH 2891/5214] mm/mglru: remove no longer used reclaim argument for folio protection Now dirty reclaim folios are handled after isolation, not before, since dirty reactivation must take the folio off LRU first, and that helps to unify the dirty handling logic. So this argument is no longer needed. Just remove it. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-12-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Cc: Baolin Wang Cc: Barry Song Cc: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index d26c89546542..22c78509c2c8 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3224,7 +3224,7 @@ static int folio_update_gen(struct folio *folio, int gen) } /* protect pages accessed multiple times through file descriptors */ -static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclaiming) +static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio) { int type = folio_is_file_lru(folio); struct lru_gen_folio *lrugen = &lruvec->lrugen; @@ -3243,9 +3243,6 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclai new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS); new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; - /* for folio_end_writeback() */ - if (reclaiming) - new_flags |= BIT(PG_reclaim); } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags)); lru_gen_update_size(lruvec, folio, old_gen, new_gen); @@ -3859,7 +3856,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness) VM_WARN_ON_ONCE_FOLIO(folio_is_file_lru(folio) != type, folio); VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone, folio); - new_gen = folio_inc_gen(lruvec, folio, false); + new_gen = folio_inc_gen(lruvec, folio); list_move_tail(&folio->lru, &lrugen->folios[new_gen][type][zone]); /* don't count the workingset being lazily promoted */ @@ -4611,7 +4608,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c /* protected */ if (tier > tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) { - gen = folio_inc_gen(lruvec, folio, false); + gen = folio_inc_gen(lruvec, folio); list_move(&folio->lru, &lrugen->folios[gen][type][zone]); /* don't count the workingset being lazily promoted */ @@ -4626,7 +4623,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c /* ineligible */ if (zone > sc->reclaim_idx) { - gen = folio_inc_gen(lruvec, folio, false); + gen = folio_inc_gen(lruvec, folio); list_move_tail(&folio->lru, &lrugen->folios[gen][type][zone]); return true; } From e621a24e10bb07998dab930009eadbd220253d4e Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:04 +0800 Subject: [PATCH 2892/5214] mm/vmscan: remove sc->file_taken No one is using it now, just remove it. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-13-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Reviewed-by: Axel Rasmussen Reviewed-by: Baolin Wang Reviewed-by: Chen Ridong Cc: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 22c78509c2c8..e464928252fa 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -173,7 +173,6 @@ struct scan_control { unsigned int congested; unsigned int writeback; unsigned int immediate; - unsigned int file_taken; unsigned int taken; } nr; @@ -2044,8 +2043,6 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, sc->nr.writeback += stat.nr_writeback; sc->nr.immediate += stat.nr_immediate; sc->nr.taken += nr_taken; - if (file) - sc->nr.file_taken += nr_taken; trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id, nr_scanned, nr_reclaimed, &stat, sc->priority, file); From 183ff2f9ec4875e010c00984374e51a545f6b169 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:05 +0800 Subject: [PATCH 2893/5214] mm/vmscan: remove sc->unqueued_dirty No one is using it now, just remove it. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-14-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Suggested-by: Axel Rasmussen Reviewed-by: Baolin Wang Reviewed-by: Axel Rasmussen Reviewed-by: Barry Song Reviewed-by: Chen Ridong Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Leno Hou Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index e464928252fa..7494ac73e3f1 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -169,7 +169,6 @@ struct scan_control { struct { unsigned int dirty; - unsigned int unqueued_dirty; unsigned int congested; unsigned int writeback; unsigned int immediate; @@ -2039,7 +2038,6 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, sc->nr.dirty += stat.nr_dirty; sc->nr.congested += stat.nr_congested; - sc->nr.unqueued_dirty += stat.nr_unqueued_dirty; sc->nr.writeback += stat.nr_writeback; sc->nr.immediate += stat.nr_immediate; sc->nr.taken += nr_taken; From 39376b9cac1cef505e1fa9a0a6105cf0de7c6734 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 28 Apr 2026 02:07:06 +0800 Subject: [PATCH 2894/5214] mm/vmscan: unify writeback reclaim statistic and throttling Currently MGLRU and non-MGLRU handle the reclaim statistic and writeback handling very differently, especially throttling. Basically MGLRU just ignored the throttling part. Let's just unify this part, use a helper to deduplicate the code so both setups will share the same behavior. Test using following reproducer using bash: echo "Setup a slow device using dm delay" dd if=/dev/zero of=/var/tmp/backing bs=1M count=2048 LOOP=$(losetup --show -f /var/tmp/backing) mkfs.ext4 -q $LOOP echo "0 $(blockdev --getsz $LOOP) delay $LOOP 0 0 $LOOP 0 1000" | \ dmsetup create slow_dev mkdir -p /mnt/slow && mount /dev/mapper/slow_dev /mnt/slow echo "Start writeback pressure" sync && echo 3 > /proc/sys/vm/drop_caches mkdir /sys/fs/cgroup/test_wb echo 128M > /sys/fs/cgroup/test_wb/memory.max (echo $BASHPID > /sys/fs/cgroup/test_wb/cgroup.procs && \ dd if=/dev/zero of=/mnt/slow/testfile bs=1M count=192) echo "Clean up" echo "0 $(blockdev --getsz $LOOP) error" | dmsetup load slow_dev dmsetup resume slow_dev umount -l /mnt/slow && sync dmsetup remove slow_dev Before this commit, `dd` will get OOM killed immediately if MGLRU is enabled. Classic LRU is fine. After this commit, throttling is now effective and no more spin on LRU or premature OOM. Stress test on other workloads also looks good. Global throttling is not here yet, we will fix that separately later. Link: https://lore.kernel.org/20260428-mglru-reclaim-v7-15-02fabb92dc43@tencent.com Signed-off-by: Kairui Song Suggested-by: Chen Ridong Tested-by: Leno Hou Reviewed-by: Axel Rasmussen Reviewed-by: Baolin Wang Cc: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Stevens Cc: Johannes Weiner Cc: Kalesh Singh Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vernon Yang Cc: Wei Xu Cc: Yafang Cc: Yuanchu Xie Cc: Yu Zhao Signed-off-by: Andrew Morton --- mm/vmscan.c | 92 +++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 7494ac73e3f1..e8a90911bf88 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1946,6 +1946,44 @@ static int current_may_throttle(void) return !(current->flags & PF_LOCAL_THROTTLE); } +static void handle_reclaim_writeback(unsigned long nr_taken, + struct pglist_data *pgdat, + struct scan_control *sc, + struct reclaim_stat *stat) +{ + /* + * If dirty folios are scanned that are not queued for IO, it + * implies that flushers are not doing their job. This can + * happen when memory pressure pushes dirty folios to the end of + * the LRU before the dirty limits are breached and the dirty + * data has expired. It can also happen when the proportion of + * dirty folios grows not through writes but through memory + * pressure reclaiming all the clean cache. And in some cases, + * the flushers simply cannot keep up with the allocation + * rate. Nudge the flusher threads in case they are asleep. + */ + if (stat->nr_unqueued_dirty == nr_taken) { + wakeup_flusher_threads(WB_REASON_VMSCAN); + /* + * For cgroupv1 dirty throttling is achieved by waking up + * the kernel flusher here and later waiting on folios + * which are in writeback to finish (see shrink_folio_list()). + * + * Flusher may not be able to issue writeback quickly + * enough for cgroupv1 writeback throttling to work + * on a large system. + */ + if (!writeback_throttling_sane(sc)) + reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); + } + + sc->nr.dirty += stat->nr_dirty; + sc->nr.congested += stat->nr_congested; + sc->nr.writeback += stat->nr_writeback; + sc->nr.immediate += stat->nr_immediate; + sc->nr.taken += nr_taken; +} + /* * shrink_inactive_list() is a helper for shrink_node(). It returns the number * of reclaimed pages @@ -2009,39 +2047,7 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, lruvec_lock_irq(lruvec); lru_note_cost_unlock_irq(lruvec, file, stat.nr_pageout, nr_scanned - nr_reclaimed); - - /* - * If dirty folios are scanned that are not queued for IO, it - * implies that flushers are not doing their job. This can - * happen when memory pressure pushes dirty folios to the end of - * the LRU before the dirty limits are breached and the dirty - * data has expired. It can also happen when the proportion of - * dirty folios grows not through writes but through memory - * pressure reclaiming all the clean cache. And in some cases, - * the flushers simply cannot keep up with the allocation - * rate. Nudge the flusher threads in case they are asleep. - */ - if (stat.nr_unqueued_dirty == nr_taken) { - wakeup_flusher_threads(WB_REASON_VMSCAN); - /* - * For cgroupv1 dirty throttling is achieved by waking up - * the kernel flusher here and later waiting on folios - * which are in writeback to finish (see shrink_folio_list()). - * - * Flusher may not be able to issue writeback quickly - * enough for cgroupv1 writeback throttling to work - * on a large system. - */ - if (!writeback_throttling_sane(sc)) - reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); - } - - sc->nr.dirty += stat.nr_dirty; - sc->nr.congested += stat.nr_congested; - sc->nr.writeback += stat.nr_writeback; - sc->nr.immediate += stat.nr_immediate; - sc->nr.taken += nr_taken; - + handle_reclaim_writeback(nr_taken, pgdat, sc, &stat); trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id, nr_scanned, nr_reclaimed, &stat, sc->priority, file); return nr_reclaimed; @@ -4833,26 +4839,13 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, retry: reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg); sc->nr_reclaimed += reclaimed; + /* Retry pass is only meant for clean folios without new isolation */ + if (isolated) + handle_reclaim_writeback(isolated, pgdat, sc, &stat); trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id, type_scanned, reclaimed, &stat, sc->priority, type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON); - /* - * If too many file cache in the coldest generation can't be evicted - * due to being dirty, wake up the flusher. - */ - if (stat.nr_unqueued_dirty == isolated) { - wakeup_flusher_threads(WB_REASON_VMSCAN); - - /* - * For cgroupv1 dirty throttling is achieved by waking up - * the kernel flusher here and later waiting on folios - * which are in writeback to finish (see shrink_folio_list()). - */ - if (!writeback_throttling_sane(sc)) - reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); - } - list_for_each_entry_safe_reverse(folio, next, &list, lru) { DEFINE_MIN_SEQ(lruvec); @@ -4895,6 +4888,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, if (!list_empty(&list)) { skip_retry = true; + isolated = 0; goto retry; } From ea3085dd7a4ec212d6c4b50efca584e0928caa72 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Sat, 25 Apr 2026 23:27:16 -0700 Subject: [PATCH 2895/5214] fs/proc/task_mmu: read proc/pid/{smaps|numa_maps} under per-vma lock Patch series "use vma locks for proc/pid/{smaps|numa_maps} reads", v2. Use per-vma locks when reading /proc/pid/smaps and /proc/pid/numa_maps similar to /proc/pid/maps to reduce contention on central mmap_lock. One major difference between maps and smaps/numa_maps reading is that the latter executes page table walk which can't be done under RCU due to a possibility of sleeping. Therefore we drop RCU read lock before this walk while keeping the VMA locked. After the walk we retake RCU read lock, reset VMA iterator and proceed with the next VMA. The last two patches extend /proc/pid/maps test to cover /proc/pid/smaps reading during concurrent address space modification. This patch (of 3): proc/pid/{smaps|numa_maps} can be read using the combination of RCU and VMA read locks, similar to proc/pid/maps. RCU is required to safely traverse the VMA tree and VMA lock stabilizes the VMA being processed and the pagetable walk. Link: https://lore.kernel.org/20260426062718.1238437-1-surenb@google.com Link: https://lore.kernel.org/20260426062718.1238437-2-surenb@google.com Signed-off-by: Suren Baghdasaryan Reviewed-by: Liam R. Howlett Reviewed-by: Lorenzo Stoakes Cc: Jann Horn Cc: Matthew Wilcox (Oracle) Cc: "Paul E . McKenney" Cc: Pedro Falcato Cc: Shuah Khan Cc: Wei Yang Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 195 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 156 insertions(+), 39 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 751b9ba160fb..1e3a15bf46f4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -132,6 +132,22 @@ static void release_task_mempolicy(struct proc_maps_private *priv) #ifdef CONFIG_PER_VMA_LOCK +static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) +{ + int ret = mmap_read_lock_killable(lock_ctx->mm); + + if (!ret) + lock_ctx->mmap_locked = true; + + return ret; +} + +static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) +{ + mmap_read_unlock(lock_ctx->mm); + lock_ctx->mmap_locked = false; +} + static void reset_lock_ctx(struct proc_maps_locking_ctx *lock_ctx) { lock_ctx->locked_vma = NULL; @@ -146,25 +162,11 @@ static void unlock_ctx_vma(struct proc_maps_locking_ctx *lock_ctx) } } -static const struct seq_operations proc_pid_maps_op; - static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx) { - /* - * smaps and numa_maps perform page table walk, therefore require - * mmap_lock but maps can be read with locking just the vma and - * walking the vma tree under rcu read protection. - */ - if (m->op != &proc_pid_maps_op) { - if (mmap_read_lock_killable(lock_ctx->mm)) - return false; - - lock_ctx->mmap_locked = true; - } else { - rcu_read_lock(); - reset_lock_ctx(lock_ctx); - } + rcu_read_lock(); + reset_lock_ctx(lock_ctx); return true; } @@ -172,7 +174,7 @@ static inline bool lock_vma_range(struct seq_file *m, static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx) { if (lock_ctx->mmap_locked) { - mmap_read_unlock(lock_ctx->mm); + unlock_ctx_mm(lock_ctx); } else { unlock_ctx_vma(lock_ctx); rcu_read_unlock(); @@ -213,17 +215,45 @@ static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, return true; } +static inline void drop_rcu(struct proc_maps_private *priv) +{ + if (priv->lock_ctx.mmap_locked) + return; + + rcu_read_unlock(); +} + +static inline void reacquire_rcu(struct proc_maps_private *priv) +{ + if (priv->lock_ctx.mmap_locked) + return; + + rcu_read_lock(); + /* Reinitialize the iterator. */ + vma_iter_set(&priv->iter, priv->lock_ctx.locked_vma->vm_end); +} + #else /* CONFIG_PER_VMA_LOCK */ +static inline int lock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) +{ + return mmap_read_lock_killable(lock_ctx->mm); +} + +static inline void unlock_ctx_mm(struct proc_maps_locking_ctx *lock_ctx) +{ + mmap_read_unlock(lock_ctx->mm); +} + static inline bool lock_vma_range(struct seq_file *m, struct proc_maps_locking_ctx *lock_ctx) { - return mmap_read_lock_killable(lock_ctx->mm) == 0; + return lock_ctx_mm(lock_ctx) == 0; } static inline void unlock_vma_range(struct proc_maps_locking_ctx *lock_ctx) { - mmap_read_unlock(lock_ctx->mm); + unlock_ctx_mm(lock_ctx); } static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, @@ -238,6 +268,9 @@ static inline bool fallback_to_mmap_lock(struct proc_maps_private *priv, return false; } +static inline void drop_rcu(struct proc_maps_private *priv) {} +static inline void reacquire_rcu(struct proc_maps_private *priv) {} + #endif /* CONFIG_PER_VMA_LOCK */ static struct vm_area_struct *proc_get_vma(struct seq_file *m, loff_t *ppos) @@ -538,12 +571,10 @@ static int query_vma_setup(struct proc_maps_locking_ctx *lock_ctx) static void query_vma_teardown(struct proc_maps_locking_ctx *lock_ctx) { - if (lock_ctx->mmap_locked) { - mmap_read_unlock(lock_ctx->mm); - lock_ctx->mmap_locked = false; - } else { + if (lock_ctx->mmap_locked) + unlock_ctx_mm(lock_ctx); + else unlock_ctx_vma(lock_ctx); - } } static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ctx *lock_ctx, @@ -1280,21 +1311,75 @@ static const struct mm_walk_ops smaps_shmem_walk_ops = { .walk_lock = PGWALK_RDLOCK, }; +#ifdef CONFIG_PER_VMA_LOCK + +static const struct mm_walk_ops smaps_walk_vma_lock_ops = { + .pmd_entry = smaps_pte_range, + .hugetlb_entry = smaps_hugetlb_range, + .walk_lock = PGWALK_VMA_RDLOCK_VERIFY, +}; + +static const struct mm_walk_ops smaps_shmem_walk_vma_lock_ops = { + .pmd_entry = smaps_pte_range, + .hugetlb_entry = smaps_hugetlb_range, + .pte_hole = smaps_pte_hole, + .walk_lock = PGWALK_VMA_RDLOCK_VERIFY, +}; + +static inline const struct mm_walk_ops * +get_smaps_walk_ops(struct proc_maps_private *priv) +{ + if (priv->lock_ctx.mmap_locked) + return &smaps_walk_ops; + return &smaps_walk_vma_lock_ops; +} + +static inline const struct mm_walk_ops * +get_smaps_shmem_walk_ops(struct proc_maps_private *priv) +{ + if (priv->lock_ctx.mmap_locked) + return &smaps_shmem_walk_ops; + return &smaps_shmem_walk_vma_lock_ops; +} + +#else /* CONFIG_PER_VMA_LOCK */ + +static inline const struct mm_walk_ops * +get_smaps_walk_ops(struct proc_maps_private *priv) +{ + return &smaps_walk_ops; +} + +static inline const struct mm_walk_ops * +get_smaps_shmem_walk_ops(struct proc_maps_private *priv) +{ + return &smaps_shmem_walk_ops; +} + +#endif /* CONFIG_PER_VMA_LOCK */ + /* * Gather mem stats from @vma with the indicated beginning * address @start, and keep them in @mss. * * Use vm_start of @vma as the beginning address if @start is 0. */ -static void smap_gather_stats(struct vm_area_struct *vma, - struct mem_size_stats *mss, unsigned long start) +static void smap_gather_stats(struct proc_maps_private *priv, + struct vm_area_struct *vma, + struct mem_size_stats *mss, unsigned long start) { - const struct mm_walk_ops *ops = &smaps_walk_ops; + const struct mm_walk_ops *ops = get_smaps_walk_ops(priv); /* Invalid start */ if (start >= vma->vm_end) return; + if (vma == get_gate_vma(priv->lock_ctx.mm)) + return; + + /* Might sleep. Drop RCU read lock but keep the VMA locked. */ + drop_rcu(priv); + if (vma->vm_file && shmem_mapping(vma->vm_file->f_mapping)) { /* * For shared or readonly shmem mappings we know that all @@ -1312,15 +1397,16 @@ static void smap_gather_stats(struct vm_area_struct *vma, !(vma->vm_flags & VM_WRITE))) { mss->swap += shmem_swapped; } else { - ops = &smaps_shmem_walk_ops; + ops = get_smaps_shmem_walk_ops(priv); } } - /* mmap_lock is held in m_start */ if (!start) walk_page_vma(vma, ops, mss); else walk_page_range(vma->vm_mm, start, vma->vm_end, ops, mss); + + reacquire_rcu(priv); } #define SEQ_PUT_DEC(str, val) \ @@ -1369,10 +1455,11 @@ static void __show_smap(struct seq_file *m, const struct mem_size_stats *mss, static int show_smap(struct seq_file *m, void *v) { + struct proc_maps_private *priv = m->private; struct vm_area_struct *vma = v; struct mem_size_stats mss = {}; - smap_gather_stats(vma, &mss, 0); + smap_gather_stats(priv, vma, &mss, 0); show_map_vma(m, vma); @@ -1413,7 +1500,7 @@ static int show_smaps_rollup(struct seq_file *m, void *v) goto out_put_task; } - ret = mmap_read_lock_killable(mm); + ret = lock_ctx_mm(&priv->lock_ctx); if (ret) goto out_put_mm; @@ -1425,7 +1512,7 @@ static int show_smaps_rollup(struct seq_file *m, void *v) vma_start = vma->vm_start; do { - smap_gather_stats(vma, &mss, 0); + smap_gather_stats(priv, vma, &mss, 0); last_vma_end = vma->vm_end; /* @@ -1434,8 +1521,8 @@ static int show_smaps_rollup(struct seq_file *m, void *v) */ if (mmap_lock_is_contended(mm)) { vma_iter_invalidate(&vmi); - mmap_read_unlock(mm); - ret = mmap_read_lock_killable(mm); + unlock_ctx_mm(&priv->lock_ctx); + ret = lock_ctx_mm(&priv->lock_ctx); if (ret) { release_task_mempolicy(priv); goto out_put_mm; @@ -1484,14 +1571,14 @@ static int show_smaps_rollup(struct seq_file *m, void *v) /* Case 1 and 2 above */ if (vma->vm_start >= last_vma_end) { - smap_gather_stats(vma, &mss, 0); + smap_gather_stats(priv, vma, &mss, 0); last_vma_end = vma->vm_end; continue; } /* Case 4 above */ if (vma->vm_end > last_vma_end) { - smap_gather_stats(vma, &mss, last_vma_end); + smap_gather_stats(priv, vma, &mss, last_vma_end); last_vma_end = vma->vm_end; } } @@ -1505,7 +1592,7 @@ static int show_smaps_rollup(struct seq_file *m, void *v) __show_smap(m, &mss, true); release_task_mempolicy(priv); - mmap_read_unlock(mm); + unlock_ctx_mm(&priv->lock_ctx); out_put_mm: mmput(mm); @@ -3291,6 +3378,31 @@ static const struct mm_walk_ops show_numa_ops = { .walk_lock = PGWALK_RDLOCK, }; +#ifdef CONFIG_PER_VMA_LOCK +static const struct mm_walk_ops show_numa_vma_lock_ops = { + .hugetlb_entry = gather_hugetlb_stats, + .pmd_entry = gather_pte_stats, + .walk_lock = PGWALK_VMA_RDLOCK_VERIFY, +}; + +static inline const struct mm_walk_ops * +get_show_numa_ops(struct proc_maps_private *priv) +{ + if (priv->lock_ctx.mmap_locked) + return &show_numa_ops; + return &show_numa_vma_lock_ops; +} + +#else /* CONFIG_PER_VMA_LOCK */ + +static inline const struct mm_walk_ops * +get_show_numa_ops(struct proc_maps_private *priv) +{ + return &show_numa_ops; +} + +#endif /* CONFIG_PER_VMA_LOCK */ + /* * Display pages allocated per node and memory policy via /proc. */ @@ -3335,8 +3447,13 @@ static int show_numa_map(struct seq_file *m, void *v) if (is_vm_hugetlb_page(vma)) seq_puts(m, " huge"); - /* mmap_lock is held by m_start */ - walk_page_vma(vma, &show_numa_ops, md); + /* Skip walking pages if gate VMA */ + if (vma != get_gate_vma(proc_priv->lock_ctx.mm)) { + /* Might sleep. Drop RCU read lock but keep the VMA locked. */ + drop_rcu(proc_priv); + walk_page_vma(vma, get_show_numa_ops(proc_priv), md); + reacquire_rcu(proc_priv); + } if (!md->pages) goto out; From ba98fca6a345805f7a4bdc5635ce6e8403770db5 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Sat, 25 Apr 2026 23:27:17 -0700 Subject: [PATCH 2896/5214] selftests/proc: ensure the test is performed at the right page boundary When running tearing tests we need to ensure the pages we use include VMAs that were mapped by the child process for this test. Currently we always use the first two pages, checking VMAs at their boundaries and this works, however once we add tests for /proc/pid/smaps, the first two pages might not contain the VMAs that child modifies. Locate the page that contains the first VMA mapped by the child and use that and the next page for the test. Link: https://lore.kernel.org/20260426062718.1238437-3-surenb@google.com Signed-off-by: Suren Baghdasaryan Reviewed-by: Liam R. Howlett Cc: Jann Horn Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: "Paul E . McKenney" Cc: Pedro Falcato Cc: Shuah Khan Cc: Wei Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/proc/proc-maps-race.c | 121 +++++++++++++++--- 1 file changed, 101 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/proc/proc-maps-race.c b/tools/testing/selftests/proc/proc-maps-race.c index a734553718da..5eb350c23da4 100644 --- a/tools/testing/selftests/proc/proc-maps-race.c +++ b/tools/testing/selftests/proc/proc-maps-race.c @@ -39,6 +39,13 @@ #include #include +#define min(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + /* /proc/pid/maps parsing routines */ struct page_content { char *data; @@ -77,6 +84,7 @@ FIXTURE(proc_maps_race) struct line_content first_line; unsigned long duration_sec; int shared_mem_size; + int skip_pages; int page_size; int vma_count; bool verbose; @@ -105,38 +113,102 @@ struct vma_modifier_info { void *child_mapped_addr[]; }; - -static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) +static bool read_page(FIXTURE_DATA(proc_maps_race) *self, + struct page_content *page) { ssize_t bytes_read; - if (lseek(self->maps_fd, 0, SEEK_SET) < 0) - return false; - - bytes_read = read(self->maps_fd, self->page1.data, self->page_size); + bytes_read = read(self->maps_fd, page->data, self->page_size); if (bytes_read <= 0) return false; - self->page1.size = bytes_read; - - bytes_read = read(self->maps_fd, self->page2.data, self->page_size); - if (bytes_read <= 0) + /* Make sure data always ends with a newline character. */ + if (page->data[bytes_read - 1] != '\n') return false; - self->page2.size = bytes_read; + page->size = bytes_read; return true; } -static void copy_first_line(struct page_content *page, char *first_line) +static bool parse_vma_line(char *line_start, char *line_end, + unsigned long *start, unsigned long *end) { - char *pos = strchr(page->data, '\n'); + bool found; - strncpy(first_line, page->data, pos - page->data); - first_line[pos - page->data] = '\0'; + *line_end = '\0'; /* stop sscanf at the EOL */ + found = (sscanf(line_start, "%lx-%lx", start, end) == 2); + *line_end = '\n'; + + return found; } -static void copy_last_line(struct page_content *page, char *last_line) +static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self, + unsigned long addr, unsigned long size) +{ + unsigned long start, end; + int page = 0; + + if (lseek(self->maps_fd, 0, SEEK_SET) < 0) + return -1; + + while (true) { + char *curr_pos; + char *end_pos; + + if (!read_page(self, &self->page1)) + return -1; + + curr_pos = self->page1.data; + end_pos = self->page1.data + self->page1.size; + while (curr_pos < end_pos) { + char *line_end; + + line_end = strchr(curr_pos, '\n'); + if (!line_end) + break; + + if (parse_vma_line(curr_pos, line_end, &start, &end) && + start == addr && end == addr + size) + return page; + + curr_pos = line_end + 1; + } + page++; + } + + return 0; +} + +static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) +{ + if (lseek(self->maps_fd, 0, SEEK_SET) < 0) + return false; + + for (int i = 0; i < self->skip_pages; i++) + if (!read_page(self, &self->page1)) + return false; + + return read_page(self, &self->page1) && read_page(self, &self->page2); +} + +static void copy_line(const char *line_start, const char *line_end, + char *buf, size_t buf_size) +{ + size_t len = min(line_end - line_start, buf_size - 1); + + strncpy(buf, line_start, len); + buf[len] = '\0'; +} + +static void copy_first_line(struct page_content *page, char *first_line, + size_t line_size) +{ + copy_line(page->data, strchr(page->data, '\n'), first_line, line_size); +} + +static void copy_last_line(struct page_content *page, char *last_line, + size_t line_size) { /* Get the last line in the first page */ const char *end = page->data + page->size - 1; @@ -146,8 +218,8 @@ static void copy_last_line(struct page_content *page, char *last_line) /* search previous newline */ while (pos[-1] != '\n') pos--; - strncpy(last_line, pos, end - pos); - last_line[end - pos] = '\0'; + + copy_line(pos, end, last_line, line_size); } /* Read the last line of the first page and the first line of the second page */ @@ -158,8 +230,8 @@ static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, if (!read_two_pages(self)) return false; - copy_last_line(&self->page1, last_line->text); - copy_first_line(&self->page2, first_line->text); + copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE); + copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE); return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, &last_line->end_addr) == 2 && @@ -418,6 +490,8 @@ FIXTURE_SETUP(proc_maps_race) struct vma_modifier_info *mod_info; pthread_mutexattr_t mutex_attr; pthread_condattr_t cond_attr; + unsigned long first_map_addr; + unsigned long last_map_addr; unsigned long duration_sec; char fname[32]; @@ -502,6 +576,13 @@ FIXTURE_SETUP(proc_maps_race) self->page2.data = malloc(self->page_size); ASSERT_NE(self->page2.data, NULL); + first_map_addr = (unsigned long)mod_info->child_mapped_addr[0]; + last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1]; + + self->skip_pages = locate_containing_page(self, + min(first_map_addr, last_map_addr), + self->page_size * 3); + ASSERT_NE(self->skip_pages, -1); ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line)); /* From 6d536ed691485fa5aa6417252d357c65eb474b75 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Sat, 25 Apr 2026 23:27:18 -0700 Subject: [PATCH 2897/5214] selftests/proc: add /proc/pid/smaps tearing tests Add tearing tests for /proc/pid/smaps file. New tests reuse the same logic as with maps file but skipping all the data except for the VMA addresses, which are the only part relevant for the tearing tests. Skip PROCMAP_QUERY parts of the tests because smaps does not implement that ioctl. Link: https://lore.kernel.org/20260426062718.1238437-4-surenb@google.com Signed-off-by: Suren Baghdasaryan Reviewed-by: Liam R. Howlett Cc: Jann Horn Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: "Paul E . McKenney" Cc: Pedro Falcato Cc: Shuah Khan Cc: Wei Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/proc/proc-maps-race.c | 178 +++++++++++++----- 1 file changed, 133 insertions(+), 45 deletions(-) diff --git a/tools/testing/selftests/proc/proc-maps-race.c b/tools/testing/selftests/proc/proc-maps-race.c index 5eb350c23da4..1026d8c400e1 100644 --- a/tools/testing/selftests/proc/proc-maps-race.c +++ b/tools/testing/selftests/proc/proc-maps-race.c @@ -17,8 +17,8 @@ */ /* * Fork a child that concurrently modifies address space while the main - * process is reading /proc/$PID/maps and verifying the results. Address - * space modifications include: + * process is reading /proc/$PID/maps and /proc/$PID/smaps, verifying the + * results. Address space modifications include: * VMA splitting and merging * */ @@ -73,6 +73,11 @@ enum test_state { TEST_DONE, }; +enum maps_file { + MAPS, + SMAPS, +}; + struct vma_modifier_info; FIXTURE(proc_maps_race) @@ -83,6 +88,7 @@ FIXTURE(proc_maps_race) struct line_content last_line; struct line_content first_line; unsigned long duration_sec; + enum maps_file maps_file; int shared_mem_size; int skip_pages; int page_size; @@ -92,6 +98,19 @@ FIXTURE(proc_maps_race) pid_t pid; }; +FIXTURE_VARIANT(proc_maps_race) +{ + const enum maps_file maps_file; +}; + +FIXTURE_VARIANT_ADD(proc_maps_race, maps) { + .maps_file = MAPS, +}; + +FIXTURE_VARIANT_ADD(proc_maps_race, smaps) { + .maps_file = SMAPS, +}; + typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self); typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line, struct line_content *mod_first_line, @@ -222,6 +241,57 @@ static void copy_last_line(struct page_content *page, char *last_line, copy_line(pos, end, last_line, line_size); } +static bool copy_first_entry(struct page_content *page, char *first_line, + size_t line_size) +{ + char *start_pos = page->data; + + while (start_pos < page->data + page->size) { + unsigned long start_addr; + unsigned long end_addr; + char *end_pos; + + end_pos = strchr(start_pos, '\n'); + if (!end_pos) + break; + + if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { + copy_line(start_pos, end_pos, first_line, line_size); + return true; + } + + start_pos = end_pos + 1; + } + + return false; +} + +static bool copy_last_entry(struct page_content *page, char *last_line, + size_t line_size) +{ + char *end_pos = page->data + page->size - 1; + char *start_pos; + + while (end_pos > page->data) { + unsigned long start_addr; + unsigned long end_addr; + + /* skip last newline */ + start_pos = end_pos - 1; + /* search previous newline */ + while (start_pos > page->data && start_pos[-1] != '\n') + start_pos--; + if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) { + copy_line(start_pos, end_pos, last_line, line_size); + return true; + } + + end_pos = start_pos - 1; + } + + return false; +} + /* Read the last line of the first page and the first line of the second page */ static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, struct line_content *last_line, @@ -230,8 +300,16 @@ static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, if (!read_two_pages(self)) return false; - copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE); - copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE); + if (self->maps_file == MAPS) { + copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE); + copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE); + } else if (self->maps_file == SMAPS) { + if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) || + !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE)) + return false; + } else { + return false; + } return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr, &last_line->end_addr) == 2 && @@ -497,6 +575,7 @@ FIXTURE_SETUP(proc_maps_race) self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); self->verbose = verbose && !strncmp(verbose, "1", 1); + self->maps_file = variant->maps_file; duration_sec = duration ? atol(duration) : 0; self->duration_sec = duration_sec ? duration_sec : 5UL; @@ -563,7 +642,16 @@ FIXTURE_SETUP(proc_maps_race) exit(0); } - sprintf(fname, "/proc/%d/maps", self->pid); + switch (self->maps_file) { + case MAPS: + sprintf(fname, "/proc/%d/maps", self->pid); + break; + case SMAPS: + sprintf(fname, "/proc/%d/smaps", self->pid); + break; + default: + ksft_exit_fail(); + } self->maps_fd = open(fname, O_RDONLY); ASSERT_NE(self->maps_fd, -1); @@ -608,7 +696,6 @@ FIXTURE_SETUP(proc_maps_race) ASSERT_TRUE(mod_info->addr && mod_info->next_addr); signal_state(mod_info, PARENT_READY); - } FIXTURE_TEARDOWN(proc_maps_race) @@ -698,20 +785,20 @@ TEST_F(proc_maps_race, test_maps_tearing_from_split) last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0; first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0; ASSERT_EQ(last_line_changed, first_line_changed); - - /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ - ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, - &vma_start, &vma_end)); - /* - * The vma at the split address can be either the same as - * original one (if read before the split) or the same as the - * first line in the second page (if read after the split). - */ - ASSERT_TRUE((vma_start == self->last_line.start_addr && - vma_end == self->last_line.end_addr) || - (vma_start == split_first_line.start_addr && - vma_end == split_first_line.end_addr)); - + if (self->maps_file == MAPS) { + /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ + ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, + &vma_start, &vma_end)); + /* + * The vma at the split address can be either the same as + * original one (if read before the split) or the same as the + * first line in the second page (if read after the split). + */ + ASSERT_TRUE((vma_start == self->last_line.start_addr && + vma_end == self->last_line.end_addr) || + (vma_start == split_first_line.start_addr && + vma_end == split_first_line.end_addr)); + } clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); end_test_iteration(&end_ts, self->verbose); } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); @@ -781,17 +868,18 @@ TEST_F(proc_maps_race, test_maps_tearing_from_resize) strcmp(new_first_line.text, restored_first_line.text), "Expand result invalid", self)); } - - /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ - ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, &vma_start, &vma_end)); - /* - * The vma should stay at the same address and have either the - * original size of 3 pages or 1 page if read after shrinking. - */ - ASSERT_TRUE(vma_start == self->last_line.start_addr && - (vma_end - vma_start == self->page_size * 3 || - vma_end - vma_start == self->page_size)); - + if (self->maps_file == MAPS) { + /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ + ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, + &vma_start, &vma_end)); + /* + * The vma should stay at the same address and have either the + * original size of 3 pages or 1 page if read after shrinking. + */ + ASSERT_TRUE(vma_start == self->last_line.start_addr && + (vma_end - vma_start == self->page_size * 3 || + vma_end - vma_start == self->page_size)); + } clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); end_test_iteration(&end_ts, self->verbose); } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); @@ -861,20 +949,20 @@ TEST_F(proc_maps_race, test_maps_tearing_from_remap) strcmp(new_first_line.text, restored_first_line.text), "Remap restore result invalid", self)); } - - /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ - ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, - &vma_start, &vma_end)); - /* - * The vma should either stay at the same address and have the - * original size of 3 pages or we should find the remapped vma - * at the remap destination address with size of 1 page. - */ - ASSERT_TRUE((vma_start == self->last_line.start_addr && - vma_end - vma_start == self->page_size * 3) || - (vma_start == self->last_line.start_addr + self->page_size && - vma_end - vma_start == self->page_size)); - + if (self->maps_file == MAPS) { + /* Check if PROCMAP_QUERY ioclt() finds the right VMA */ + ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size, + &vma_start, &vma_end)); + /* + * The vma should either stay at the same address and have the + * original size of 3 pages or we should find the remapped vma + * at the remap destination address with size of 1 page. + */ + ASSERT_TRUE((vma_start == self->last_line.start_addr && + vma_end - vma_start == self->page_size * 3) || + (vma_start == self->last_line.start_addr + self->page_size && + vma_end - vma_start == self->page_size)); + } clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); end_test_iteration(&end_ts, self->verbose); } while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec); From eb4c458a9803c3c75ee27d567a3a2ff0cc66da98 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Mon, 25 May 2026 07:57:51 -0700 Subject: [PATCH 2898/5214] mm: make mmap_miss accounting symmetric for VM_SEQ_READ do_sync_mmap_readahead() skips both the mmap_miss increment and the MMAP_LOTSAMISS check for VM_SEQ_READ mappings, since sequential access is non-speculative and should always read ahead. The two decrement sites in do_async_mmap_readahead() and filemap_map_pages() do not mirror this skip, so concurrent faults on a VM_SEQ_READ mapping can still drive ra->mmap_miss down to zero through the decrement paths even though nothing in the sync path ever increments it. The counter itself is per-file (file->f_ra.mmap_miss), so it can be moved by any VMA mapping the file, not just the one currently faulting. Skip the decrement for VM_SEQ_READ in both decrement sites so the counter only moves for mappings that also participate in the increment side. No functional change for VM_SEQ_READ users, since the increment-side gate already prevents the counter from being consulted on their behalf, but it stops a VM_SEQ_READ mapping from biasing the counter for other mappings of the same file. Link: https://lore.kernel.org/20260525145751.2671248-1-usama.arif@linux.dev Signed-off-by: Usama Arif Closes: https://lore.kernel.org/all/8edc8cd0-f65c-4456-9b3f-362e744c9a96@linux.dev/ Reviewed-by: William Kucharski Reviewed-by: Jan Kara Cc: David Hildenbrand Cc: Johannes Weiner Cc: Matthew Wilcox (Oracle) Cc: Shakeel Butt Signed-off-by: Andrew Morton --- mm/filemap.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 4263d9775998..6bf0b540ef19 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3434,8 +3434,13 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf, * Don't touch the mmap_miss counter to avoid decreasing it multiple * times for a single folio and break the balance with mmap_miss * increase in do_sync_mmap_readahead(). + * + * VM_SEQ_READ mappings skip the mmap_miss increment in + * do_sync_mmap_readahead(), so skip the decrement here as well to + * keep the counter symmetric. */ - if (likely(!folio_test_locked(folio))) { + if (likely(!folio_test_locked(folio)) && + !(vmf->vma->vm_flags & VM_SEQ_READ)) { mmap_miss = READ_ONCE(ra->mmap_miss); if (mmap_miss) WRITE_ONCE(ra->mmap_miss, --mmap_miss); @@ -3936,10 +3941,15 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, * In such situation, read-ahead is only a waste of IO. * Don't decrease mmap_miss in this scenario to make sure * we can stop read-ahead. + * + * VM_SEQ_READ mappings skip the mmap_miss increment in + * do_sync_mmap_readahead(), so skip the decrement here as + * well to keep the counter symmetric. */ if ((map_ret & VM_FAULT_NOPAGE) && !(vmf->flags & FAULT_FLAG_TRIED) && - !folio_test_workingset(folio)) { + !folio_test_workingset(folio) && + !(vma->vm_flags & VM_SEQ_READ)) { unsigned short mmap_miss; mmap_miss = READ_ONCE(file->f_ra.mmap_miss); From ad1cee3940d51c8e0d03a3f45d9803aa8f2154a4 Mon Sep 17 00:00:00 2001 From: Ran Xiaokai Date: Mon, 25 May 2026 10:26:59 +0000 Subject: [PATCH 2899/5214] mm: shmem: refactor thpsize_shmem_enabled_store() with sysfs_match_string() Patch series "refactors thpsize_shmem_enabled_store() and thpsize_shmem_enabled_show()", v4. This patch (of 2): Inspired by commit 82d9ff648c6c ("mm: huge_memory: refactor anon_enabled_store() with set_anon_enabled_mode()"), refactor thpsize_shmem_enabled_store() using sysfs_match_string(). This eliminates the duplicated spin_lock/unlock(), set/clear_bit(), calls across all branches, reducing code duplication. Behavioral change: Call start_stop_khugepaged() only when the mode actually changes. If unchanged, call set_recommended_min_free_kbytes() to preserve legacy watermark behavior. This avoids unnecessary khugepaged restarts. Tested with selftests ./run_kselftest.sh -t mm:ksft_thp.sh, all test cases passed. Link: https://lore.kernel.org/20260525102700.68707-1-ranxiaokai627@163.com Link: https://lore.kernel.org/20260525102700.68707-2-ranxiaokai627@163.com Signed-off-by: Ran Xiaokai Reviewed-by: Baolin Wang Reviewed-by: Barry Song Tested-by: Baolin Wang Tested-by: Lance Yang Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Breno Leitao Cc: Dev Jain Cc: Hugh Dickins Cc: Liam R. Howlett Cc: Nico Pache Cc: Ran Xiaokai Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/shmem.c | 105 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 77a3e28e5160..748b135d04fb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -5463,6 +5463,29 @@ static ssize_t shmem_enabled_store(struct kobject *kobj, struct kobj_attribute shmem_enabled_attr = __ATTR_RW(shmem_enabled); static DEFINE_SPINLOCK(huge_shmem_orders_lock); +enum huge_mode { + HUGE_SHMEM_ENABLED_ALWAYS = 0, + HUGE_SHMEM_ENABLED_INHERIT, + HUGE_SHMEM_ENABLED_WITHIN_SIZE, + HUGE_SHMEM_ENABLED_ADVISE, + HUGE_SHMEM_ENABLED_NEVER, +}; + +static const char * const huge_mode_strings[] = { + [HUGE_SHMEM_ENABLED_ALWAYS] = "always", + [HUGE_SHMEM_ENABLED_INHERIT] = "inherit", + [HUGE_SHMEM_ENABLED_WITHIN_SIZE] = "within_size", + [HUGE_SHMEM_ENABLED_ADVISE] = "advise", + [HUGE_SHMEM_ENABLED_NEVER] = "never", +}; + +static unsigned long * const huge_mode_orders[] = { + [HUGE_SHMEM_ENABLED_ALWAYS] = &huge_shmem_orders_always, + [HUGE_SHMEM_ENABLED_INHERIT] = &huge_shmem_orders_inherit, + [HUGE_SHMEM_ENABLED_WITHIN_SIZE] = &huge_shmem_orders_within_size, + [HUGE_SHMEM_ENABLED_ADVISE] = &huge_shmem_orders_madvise, +}; + static ssize_t thpsize_shmem_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -5483,63 +5506,53 @@ static ssize_t thpsize_shmem_enabled_show(struct kobject *kobj, return sysfs_emit(buf, "%s\n", output); } +static bool set_shmem_enabled_mode(int order, enum huge_mode mode) +{ + bool changed = false; + enum huge_mode idx; + + spin_lock(&huge_shmem_orders_lock); + for (idx = 0; idx < ARRAY_SIZE(huge_mode_orders); idx++) { + if (idx == mode) + changed |= !__test_and_set_bit(order, huge_mode_orders[idx]); + else + changed |= __test_and_clear_bit(order, huge_mode_orders[idx]); + } + spin_unlock(&huge_shmem_orders_lock); + + return changed; +} + static ssize_t thpsize_shmem_enabled_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int order = to_thpsize(kobj)->order; - ssize_t ret = count; + int mode; - if (sysfs_streq(buf, "always")) { - spin_lock(&huge_shmem_orders_lock); - clear_bit(order, &huge_shmem_orders_inherit); - clear_bit(order, &huge_shmem_orders_madvise); - clear_bit(order, &huge_shmem_orders_within_size); - set_bit(order, &huge_shmem_orders_always); - spin_unlock(&huge_shmem_orders_lock); - } else if (sysfs_streq(buf, "inherit")) { - /* Do not override huge allocation policy with non-PMD sized mTHP */ - if (shmem_huge == SHMEM_HUGE_FORCE && !is_pmd_order(order)) - return -EINVAL; + mode = sysfs_match_string(huge_mode_strings, buf); + if (mode < 0) + return mode; - spin_lock(&huge_shmem_orders_lock); - clear_bit(order, &huge_shmem_orders_always); - clear_bit(order, &huge_shmem_orders_madvise); - clear_bit(order, &huge_shmem_orders_within_size); - set_bit(order, &huge_shmem_orders_inherit); - spin_unlock(&huge_shmem_orders_lock); - } else if (sysfs_streq(buf, "within_size")) { - spin_lock(&huge_shmem_orders_lock); - clear_bit(order, &huge_shmem_orders_always); - clear_bit(order, &huge_shmem_orders_inherit); - clear_bit(order, &huge_shmem_orders_madvise); - set_bit(order, &huge_shmem_orders_within_size); - spin_unlock(&huge_shmem_orders_lock); - } else if (sysfs_streq(buf, "advise")) { - spin_lock(&huge_shmem_orders_lock); - clear_bit(order, &huge_shmem_orders_always); - clear_bit(order, &huge_shmem_orders_inherit); - clear_bit(order, &huge_shmem_orders_within_size); - set_bit(order, &huge_shmem_orders_madvise); - spin_unlock(&huge_shmem_orders_lock); - } else if (sysfs_streq(buf, "never")) { - spin_lock(&huge_shmem_orders_lock); - clear_bit(order, &huge_shmem_orders_always); - clear_bit(order, &huge_shmem_orders_inherit); - clear_bit(order, &huge_shmem_orders_within_size); - clear_bit(order, &huge_shmem_orders_madvise); - spin_unlock(&huge_shmem_orders_lock); - } else { - ret = -EINVAL; - } + /* Do not override huge allocation policy with non-PMD sized mTHP */ + if (mode == HUGE_SHMEM_ENABLED_INHERIT && + shmem_huge == SHMEM_HUGE_FORCE && !is_pmd_order(order)) + return -EINVAL; - if (ret > 0) { + if (set_shmem_enabled_mode(order, mode)) { int err = start_stop_khugepaged(); - if (err) - ret = err; + return err; + } else { + /* + * Recalculate watermarks even when the mode hasn't changed + * to preserve the legacy behavior, as this is always called + * inside start_stop_khugepaged(). + */ + set_recommended_min_free_kbytes(); } - return ret; + + return count; } struct kobj_attribute thpsize_shmem_enabled_attr = From bf7033eb7c2f892580f060554ae4ea92bd52b9fb Mon Sep 17 00:00:00 2001 From: Ran Xiaokai Date: Mon, 25 May 2026 10:27:00 +0000 Subject: [PATCH 2900/5214] mm: shmem: refactor thpsize_shmem_enabled_show() with helper arrays Replace the hardcoded if/else chain of test_bit() calls and string literals in thpsize_shmem_enabled_show() with a loop over huge_shmem_orders_by_mode[] and huge_shmem_enabled_mode_strings[] arrays. This makes thpsize_shmem_enabled_show() consistent with thpsize_shmem_enabled_store() and eliminates duplicated mode name strings. Link: https://lore.kernel.org/20260525102700.68707-3-ranxiaokai627@163.com Signed-off-by: Ran Xiaokai Reviewed-by: Baolin Wang Reviewed-by: Barry Song Reviewed-by: Breno Leitao Reviewed-by: Lorenzo Stoakes Tested-by: Baolin Wang Tested-by: Lance Yang Acked-by: David Hildenbrand (arm) Cc: Dev Jain Cc: Hugh Dickins Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/shmem.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 748b135d04fb..56c23a7b15c7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -5490,20 +5490,30 @@ static ssize_t thpsize_shmem_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int order = to_thpsize(kobj)->order; - const char *output; + int active = HUGE_SHMEM_ENABLED_NEVER; + int len = 0; + int i; - if (test_bit(order, &huge_shmem_orders_always)) - output = "[always] inherit within_size advise never"; - else if (test_bit(order, &huge_shmem_orders_inherit)) - output = "always [inherit] within_size advise never"; - else if (test_bit(order, &huge_shmem_orders_within_size)) - output = "always inherit [within_size] advise never"; - else if (test_bit(order, &huge_shmem_orders_madvise)) - output = "always inherit within_size [advise] never"; - else - output = "always inherit within_size advise [never]"; + for (i = 0; i < ARRAY_SIZE(huge_mode_orders); i++) { + if (test_bit(order, huge_mode_orders[i])) { + active = i; + break; + } + } - return sysfs_emit(buf, "%s\n", output); + for (i = 0; i < ARRAY_SIZE(huge_mode_strings); i++) { + if (i == active) + len += sysfs_emit_at(buf, len, "[%s] ", + huge_mode_strings[i]); + else + len += sysfs_emit_at(buf, len, "%s ", + huge_mode_strings[i]); + } + + /* Replace trailing space with newline */ + buf[len - 1] = '\n'; + + return len; } static bool set_shmem_enabled_mode(int order, enum huge_mode mode) From 528db7d37e08dc7eae70d046cda5a2ee30208448 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Sun, 24 May 2026 22:35:56 +0300 Subject: [PATCH 2901/5214] selftests/memfd: fix -Wmaybe-uninitialized warning in memfd_test Patch series "selftests/memfd: fix compilation warnings". This patchset fixes warnings about unused but initialized variables, and unused dummy buffer passed to pwrite() syscall in the tests. This patch (of 2): memfd_test.c: In function 'mfd_fail_grow_write.part.0': memfd_test.c:685:13: warning: '' may be used uninitialized [-Wmaybe-uninitialized] 685 | l = pwrite(fd, buf, mfd_def_size * 8, 0); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ pwrite() is declared with attribute 'access (read_only, 2, 3)', so GCC knows it reads from the buffer. malloc() returns uninitialized memory, hence the warning. Use calloc() to zero-initialize the buffer. The actual contents don't matter here since the test verifies that pwrite() fails on a sealed memfd. Link: https://lore.kernel.org/20260524193732.48853-1-eva.kurchatova@virtuozzo.com Link: https://lore.kernel.org/20260524193732.48853-2-eva.kurchatova@virtuozzo.com Signed-off-by: Konstantin Khorenko Signed-off-by: Eva Kurchatova Cc: Aristeu Rozanski Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/memfd/memfd_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 2ca07ea7202a..cdab3a837624 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -688,9 +688,9 @@ static void mfd_assert_grow_write(int fd) if (hugetlbfs_test) return; - buf = malloc(mfd_def_size * 8); + buf = calloc(1, mfd_def_size * 8); if (!buf) { - printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); + printf("calloc(1, %zu) failed: %m\n", mfd_def_size * 8); abort(); } From 952923ff200817506a9a5fd1dd1b811745f25746 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Sun, 24 May 2026 22:35:57 +0300 Subject: [PATCH 2902/5214] selftests/memfd: remove unused variable 'sig' in fuse_test fuse_test.c: In function 'sealing_thread_fn': fuse_test.c:165:13: warning: unused variable 'sig' [-Wunused-variable] 165 | int sig, r; | ^~~ Remove unused 'sig' to fix -Wunused-variable warning. Link: https://lore.kernel.org/20260524193732.48853-3-eva.kurchatova@virtuozzo.com Signed-off-by: Konstantin Khorenko Cc: Aristeu Rozanski Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/memfd/fuse_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c index dbc171a3806d..510056c1b0d0 100644 --- a/tools/testing/selftests/memfd/fuse_test.c +++ b/tools/testing/selftests/memfd/fuse_test.c @@ -162,7 +162,7 @@ static void *global_p = NULL; static int sealing_thread_fn(void *arg) { - int sig, r; + int r; /* * This thread first waits 200ms so any pending operation in the parent From e0d4e7405f267ae31ffafd5673ce14d0d9e4cbe0 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Mon, 25 May 2026 20:39:28 -0700 Subject: [PATCH 2903/5214] memcg: store node_id instead of pglist_data pointer Patch series "memcg: shrink obj_stock_pcp and cache multiple objcgs", v3. Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") split a memcg's single obj_cgroup into one per NUMA node so that reparenting LRU folios can take per-node lru locks. As a side effect, the per-CPU obj_stock_pcp -- which caches a single cached_objcg pointer -- thrashes on workloads where threads of the same memcg run on different NUMA nodes. The kernel test robot reported a 67.7% regression on stress-ng.switch.ops_per_sec from this pattern. Commit d0211878ce06 ("memcg: cache obj_stock by memcg, not by objcg pointer") landed as a temporary fix by treating sibling per-node objcgs as equivalent for the cache lookup, intended to be reverted once per-node kmem accounting is introduced. This series takes a more general approach: cache multiple objcgs per CPU using the multi-slot pattern memcg_stock_pcp already uses, so the per-node objcg variants of one memcg can all coexist in the stock without ever forcing a drain. The temporary fix can then be reverted. To avoid increasing the per-CPU cache footprint, the first three patches shrink the existing single-slot obj_stock_pcp fields. The final patch converts cached_objcg and nr_bytes into NR_OBJ_STOCK=5 slot arrays and reorders the struct so the entire consume/refill/account hot path fits within a single 64-byte cache line on non-debug 64-bit builds (verified with pahole). This patch (of 4): The struct obj_stock_pcp stores a pointer to pglist_data for the slab stats cached on the cpu. On 64-bit machines, this costs 8 bytes. The pointer is not strictly required: NODE_DATA() can recover it from the node id. Replace cached_pgdat with int16_t node_id and use NUMA_NO_NODE as the "no stats cached" sentinel. At the moment all the archs limit MAX_NUMNODES to 1024 so int16_t is plenty; a BUILD_BUG_ON() makes sure we notice if that ever changes. Link: https://lore.kernel.org/20260526033931.1760588-1-shakeel.butt@linux.dev Link: https://lore.kernel.org/20260526033931.1760588-2-shakeel.butt@linux.dev Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") Signed-off-by: Shakeel Butt Tested-by: kernel test robot Acked-by: Muchun Song Reviewed-by: Harry Yoo (Oracle) Acked-by: Qi Zheng Cc: Alexandre Ghiti Cc: Johannes Weiner Cc: Joshua Hahn Cc: Michal Hocko Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/memcontrol.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 92269740eef1..e983fa590af8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2022,7 +2022,7 @@ struct obj_stock_pcp { local_trylock_t lock; unsigned int nr_bytes; struct obj_cgroup *cached_objcg; - struct pglist_data *cached_pgdat; + int16_t node_id; int nr_slab_reclaimable_b; int nr_slab_unreclaimable_b; @@ -2032,6 +2032,7 @@ struct obj_stock_pcp { static DEFINE_PER_CPU_ALIGNED(struct obj_stock_pcp, obj_stock) = { .lock = INIT_LOCAL_TRYLOCK(lock), + .node_id = NUMA_NO_NODE, }; static DEFINE_MUTEX(percpu_charge_mutex); @@ -3162,6 +3163,13 @@ static void __account_obj_stock(struct obj_cgroup *objcg, { int *bytes; + /* + * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make + * sure it does not exceed S16_MAX otherwise we need to fix node_id type + * in struct obj_stock_pcp. + */ + BUILD_BUG_ON(MAX_NUMNODES >= S16_MAX); + if (!stock || READ_ONCE(stock->cached_objcg) != objcg) goto direct; @@ -3169,9 +3177,11 @@ static void __account_obj_stock(struct obj_cgroup *objcg, * Save vmstat data in stock and skip vmstat array update unless * accumulating over a page of vmstat data or when pgdat changes. */ - if (stock->cached_pgdat != pgdat) { + if (stock->node_id == NUMA_NO_NODE) { + stock->node_id = pgdat->node_id; + } else if (stock->node_id != pgdat->node_id) { /* Flush the existing cached vmstat data */ - struct pglist_data *oldpg = stock->cached_pgdat; + struct pglist_data *oldpg = NODE_DATA(stock->node_id); if (stock->nr_slab_reclaimable_b) { mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B, @@ -3183,7 +3193,7 @@ static void __account_obj_stock(struct obj_cgroup *objcg, stock->nr_slab_unreclaimable_b); stock->nr_slab_unreclaimable_b = 0; } - stock->cached_pgdat = pgdat; + stock->node_id = pgdat->node_id; } bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b @@ -3279,19 +3289,21 @@ static void drain_obj_stock(struct obj_stock_pcp *stock) * Flush the vmstat data in current stock */ if (stock->nr_slab_reclaimable_b || stock->nr_slab_unreclaimable_b) { + struct pglist_data *oldpg = NODE_DATA(stock->node_id); + if (stock->nr_slab_reclaimable_b) { - mod_objcg_mlstate(old, stock->cached_pgdat, + mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B, stock->nr_slab_reclaimable_b); stock->nr_slab_reclaimable_b = 0; } if (stock->nr_slab_unreclaimable_b) { - mod_objcg_mlstate(old, stock->cached_pgdat, + mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B, stock->nr_slab_unreclaimable_b); stock->nr_slab_unreclaimable_b = 0; } - stock->cached_pgdat = NULL; + stock->node_id = NUMA_NO_NODE; } WRITE_ONCE(stock->cached_objcg, NULL); From 37a7f91e44f41f1b4cd60d1f89a4de7cf871d158 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Mon, 25 May 2026 20:39:29 -0700 Subject: [PATCH 2904/5214] memcg: uint16_t for nr_bytes in obj_stock_pcp Currently struct obj_stock_pcp stores nr_bytes in an 'unsigned int' which is 4 bytes on 64-bit machines. Switch the field to uint16_t to shrink the per-CPU cache. The kernel supports PAGE_SIZE_4KB, _8KB, _16KB, _32KB, _64KB and _256KB (see HAVE_PAGE_SIZE_* in arch/Kconfig). After the PAGE_SIZE-aligned flush in __refill_obj_stock(), the sub-page remainder fits in uint16_t up through 64KiB pages where PAGE_SIZE - 1 == U16_MAX, but on 256KiB pages PAGE_SIZE - 1 == 0x3FFFF exceeds U16_MAX. The accumulator also needs to stay within uint16_t between page-aligned flushes on 64KiB pages where PAGE_SIZE itself is U16_MAX + 1. Accumulate the new total in an 'unsigned int' local, then on PAGE_SHIFT <= 16 flush whenever the accumulator would hit U16_MAX; together with the existing allow_uncharge flush at PAGE_SIZE this keeps the uint16_t safe. On configs with PAGE_SHIFT > 16 (PAGE_SIZE_256KB on hexagon and powerpc 44x, both 32-bit), uint16_t cannot represent the sub-page remainder. Define obj_stock_bytes_t as 'unsigned int' on those archs so nr_bytes can hold the full remainder and the normal page-boundary flush in __refill_obj_stock() and the page extraction in drain_obj_stock() both work correctly. The single-cache-line layout target only applies to PAGE_SHIFT <= 16; those archs are 32-bit embedded and not the optimization target. Link: https://lore.kernel.org/20260526033931.1760588-3-shakeel.butt@linux.dev Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") Signed-off-by: Shakeel Butt Tested-by: kernel test robot Reviewed-by: Harry Yoo (Oracle) Acked-by: Qi Zheng Acked-by: Muchun Song Cc: Alexandre Ghiti Cc: Johannes Weiner Cc: Joshua Hahn Cc: Michal Hocko Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/memcontrol.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e983fa590af8..8bbcc7bc42e3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2020,8 +2020,17 @@ static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = { struct obj_stock_pcp { local_trylock_t lock; - unsigned int nr_bytes; struct obj_cgroup *cached_objcg; +#if PAGE_SHIFT > 16 + /* + * On rare archs with 256KiB base page size (hexagon and powerpc 44x) + * keep nr_bytes to unsigned int as uint16_t cannot represent the full + * sub-page remainder. + */ + unsigned int nr_bytes; +#else + uint16_t nr_bytes; +#endif int16_t node_id; int nr_slab_reclaimable_b; int nr_slab_unreclaimable_b; @@ -3334,6 +3343,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg, bool allow_uncharge) { unsigned int nr_pages = 0; + unsigned int stock_nr_bytes; if (!stock) { nr_pages = nr_bytes >> PAGE_SHIFT; @@ -3342,21 +3352,24 @@ static void __refill_obj_stock(struct obj_cgroup *objcg, goto out; } + stock_nr_bytes = stock->nr_bytes; if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */ drain_obj_stock(stock); obj_cgroup_get(objcg); - stock->nr_bytes = atomic_read(&objcg->nr_charged_bytes) + stock_nr_bytes = atomic_read(&objcg->nr_charged_bytes) ? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0; WRITE_ONCE(stock->cached_objcg, objcg); allow_uncharge = true; /* Allow uncharge when objcg changes */ } - stock->nr_bytes += nr_bytes; + stock_nr_bytes += nr_bytes; - if (allow_uncharge && (stock->nr_bytes > PAGE_SIZE)) { - nr_pages = stock->nr_bytes >> PAGE_SHIFT; - stock->nr_bytes &= (PAGE_SIZE - 1); + if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) || + stock_nr_bytes > U16_MAX) { + nr_pages = stock_nr_bytes >> PAGE_SHIFT; + stock_nr_bytes &= (PAGE_SIZE - 1); } + stock->nr_bytes = stock_nr_bytes; out: if (nr_pages) From 7a09fb91c285ba1b253f7c72f86cf37b373afb10 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Mon, 25 May 2026 20:39:30 -0700 Subject: [PATCH 2905/5214] memcg: int16_t for cached slab stats Currently struct obj_stock_pcp stores cached slab stats in 'int' which is 4 bytes per counter on 64-bit machines. Switch them to int16_t to shrink the cached metadata. The existing PAGE_SIZE flush in __account_obj_stock() bounds *bytes at PAGE_SIZE on 4KiB and 16KiB page archs, well within int16_t. On 64KiB pages PAGE_SIZE is well above S16_MAX so that flush never fires, and a sufficiently long run of accumulations would overflow the cache. Add an explicit S16_MAX guard before each add: when the next add would push abs(*bytes) past S16_MAX, fold the cached value into @nr and flush directly via mod_objcg_mlstate() before the accumulation. Link: https://lore.kernel.org/20260526033931.1760588-4-shakeel.butt@linux.dev Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") Signed-off-by: Shakeel Butt Tested-by: kernel test robot Reviewed-by: Harry Yoo (Oracle) Acked-by: Qi Zheng Acked-by: Muchun Song Cc: Alexandre Ghiti Cc: Johannes Weiner Cc: Joshua Hahn Cc: Michal Hocko Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/memcontrol.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 8bbcc7bc42e3..ac7c99e32f99 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2032,8 +2032,8 @@ struct obj_stock_pcp { uint16_t nr_bytes; #endif int16_t node_id; - int nr_slab_reclaimable_b; - int nr_slab_unreclaimable_b; + int16_t nr_slab_reclaimable_b; + int16_t nr_slab_unreclaimable_b; struct work_struct work; unsigned long flags; @@ -3170,7 +3170,7 @@ static void __account_obj_stock(struct obj_cgroup *objcg, struct obj_stock_pcp *stock, int nr, struct pglist_data *pgdat, enum node_stat_item idx) { - int *bytes; + int16_t *bytes; /* * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make @@ -3207,21 +3207,20 @@ static void __account_obj_stock(struct obj_cgroup *objcg, bytes = (idx == NR_SLAB_RECLAIMABLE_B) ? &stock->nr_slab_reclaimable_b : &stock->nr_slab_unreclaimable_b; + /* - * Even for large object >= PAGE_SIZE, the vmstat data will still be - * cached locally at least once before pushing it out. + * Fold @nr into the cached value and decide whether to keep it cached + * or flush it directly. Cache the combined value when it fits in the + * int16_t storage and either the cache was empty (so even a value + * above PAGE_SIZE gets a chance to be canceled by a paired delta) or + * the combined value is within the PAGE_SIZE flush threshold. */ - if (!*bytes) { + nr += *bytes; + if (abs(nr) <= S16_MAX && (!*bytes || abs(nr) <= PAGE_SIZE)) { *bytes = nr; nr = 0; } else { - *bytes += nr; - if (abs(*bytes) > PAGE_SIZE) { - nr = *bytes; - *bytes = 0; - } else { - nr = 0; - } + *bytes = 0; } direct: if (nr) From 29a1ea41456b79d657e5f5deced1239477d03af1 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Mon, 25 May 2026 20:39:31 -0700 Subject: [PATCH 2906/5214] memcg: multi objcg charge support Commit 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") split a memcg's single obj_cgroup into one per NUMA node so that reparenting LRU folios can take per-node lru locks. As a side effect, the per-CPU obj_stock_pcp -- which caches exactly one cached_objcg -- thrashes on workloads where threads of the same memcg run on different NUMA nodes. The kernel test robot reported a 67.7% regression on stress-ng.switch.ops_per_sec from this pattern. Mirror the multi-slot pattern already used by memcg_stock_pcp: turn nr_bytes and cached_objcg into NR_OBJ_STOCK-element arrays, scan all slots on consume/refill/account, prefer empty slots when inserting, and evict a slot round-robin only when full. With multiple slots a CPU can hold the per-node objcg variants of one memcg plus a few siblings without ever forcing a drain. A single int8_t index records which slot the cached slab stats belong to; the stats are flushed on slot or pgdat change. With NR_OBJ_STOCK = 5 the layout (verified with pahole) is: offset 0 : lock(1) + index(1) + node_id(2) + slab stats(4) = 8B offset 8 : nr_bytes[5] = 10B offset 18 : padding = 6B offset 24 : cached[5] = 40B offset 64 : (line 2) work_struct + flags (cold) so consume_obj_stock, refill_obj_stock and the slab account path each touch exactly one 64-byte cache line on non-debug 64-bit builds. Link: https://lore.kernel.org/20260526033931.1760588-5-shakeel.butt@linux.dev Signed-off-by: Shakeel Butt Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202605121641.b6a60cb0-lkp@intel.com Fixes: 01b9da291c49 ("mm: memcontrol: convert objcg to be per-memcg per-node type") Tested-by: kernel test robot Reviewed-by: Harry Yoo (Oracle) Cc: Alexandre Ghiti Cc: Johannes Weiner Cc: Joshua Hahn Cc: Michal Hocko Cc: Muchun Song Cc: Qi Zheng Cc: Roman Gushchin Signed-off-by: Andrew Morton --- mm/memcontrol.c | 200 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 58 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index ac7c99e32f99..e24114a4493a 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -150,15 +150,15 @@ static void obj_cgroup_release(struct percpu_ref *ref) * However, it can be PAGE_SIZE or (x * PAGE_SIZE). * * The following sequence can lead to it: - * 1) CPU0: objcg == stock->cached_objcg + * 1) CPU0: objcg cached in one of stock->cached[i] * 2) CPU1: we do a small allocation (e.g. 92 bytes), * PAGE_SIZE bytes are charged * 3) CPU1: a process from another memcg is allocating something, * the stock if flushed, * objcg->nr_charged_bytes = PAGE_SIZE - 92 - * 5) CPU0: we do release this object, - * 92 bytes are added to stock->nr_bytes - * 6) CPU0: stock is flushed, + * 4) CPU0: we do release this object, + * 92 bytes are added to stock->nr_bytes[i] + * 5) CPU0: stock is flushed, * 92 bytes are added to objcg->nr_charged_bytes * * In the result, nr_charged_bytes == PAGE_SIZE. @@ -2018,34 +2018,49 @@ static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = { .lock = INIT_LOCAL_TRYLOCK(lock), }; +/* + * NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp + * (lock, accounting metadata, nr_bytes[] and cached[]) fits within a + * single 64-byte cache line on non-debug 64-bit builds. With 5 slots: + * lock(1) + index(1) + node_id(2) + slab stats(4) + nr_bytes(10) + * + pad(6) + cached(40) == 64 bytes. + * A CPU can thus consume/refill/account against five different objcgs + * (typically per-node variants of the same memcg) while incurring at + * most one cache miss on the stock. + */ +#define NR_OBJ_STOCK 5 struct obj_stock_pcp { local_trylock_t lock; - struct obj_cgroup *cached_objcg; + int8_t index; + int16_t node_id; + int16_t nr_slab_reclaimable_b; + int16_t nr_slab_unreclaimable_b; #if PAGE_SHIFT > 16 /* * On rare archs with 256KiB base page size (hexagon and powerpc 44x) * keep nr_bytes to unsigned int as uint16_t cannot represent the full - * sub-page remainder. +e patches/memcg-uint16_t-for-nr_bytes-in-obj_stock_pcp.patch * sub-page remainder. Such archs are not cacheline optimization target. */ - unsigned int nr_bytes; + unsigned int nr_bytes[NR_OBJ_STOCK]; #else - uint16_t nr_bytes; + uint16_t nr_bytes[NR_OBJ_STOCK]; #endif - int16_t node_id; - int16_t nr_slab_reclaimable_b; - int16_t nr_slab_unreclaimable_b; + struct obj_cgroup *cached[NR_OBJ_STOCK]; struct work_struct work; unsigned long flags; + uint8_t drain_idx; }; static DEFINE_PER_CPU_ALIGNED(struct obj_stock_pcp, obj_stock) = { .lock = INIT_LOCAL_TRYLOCK(lock), + .index = -1, .node_id = NUMA_NO_NODE, }; static DEFINE_MUTEX(percpu_charge_mutex); +static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i); static void drain_obj_stock(struct obj_stock_pcp *stock); static bool obj_stock_flush_required(struct obj_stock_pcp *stock, struct mem_cgroup *root_memcg); @@ -3165,12 +3180,13 @@ static void unlock_stock(struct obj_stock_pcp *stock) local_unlock(&obj_stock.lock); } -/* Call after __refill_obj_stock() to ensure stock->cached_objg == objcg */ +/* Call after __refill_obj_stock() so a slot for objcg exists in the stock */ static void __account_obj_stock(struct obj_cgroup *objcg, struct obj_stock_pcp *stock, int nr, struct pglist_data *pgdat, enum node_stat_item idx) { int16_t *bytes; + int i; /* * Though at the moment MAX_NUMNODES <= 1024 in all archs but let's make @@ -3179,29 +3195,39 @@ static void __account_obj_stock(struct obj_cgroup *objcg, */ BUILD_BUG_ON(MAX_NUMNODES >= S16_MAX); - if (!stock || READ_ONCE(stock->cached_objcg) != objcg) + if (!stock) + goto direct; + + for (i = 0; i < NR_OBJ_STOCK; ++i) { + if (READ_ONCE(stock->cached[i]) == objcg) + break; + } + if (i == NR_OBJ_STOCK) goto direct; /* * Save vmstat data in stock and skip vmstat array update unless - * accumulating over a page of vmstat data or when pgdat changes. + * accumulating over a page of vmstat data or when the objcg slot or + * pgdat the stats belong to changes. */ - if (stock->node_id == NUMA_NO_NODE) { + if (stock->index < 0) { + stock->index = i; stock->node_id = pgdat->node_id; - } else if (stock->node_id != pgdat->node_id) { - /* Flush the existing cached vmstat data */ + } else if (stock->index != i || stock->node_id != pgdat->node_id) { + struct obj_cgroup *old = READ_ONCE(stock->cached[stock->index]); struct pglist_data *oldpg = NODE_DATA(stock->node_id); if (stock->nr_slab_reclaimable_b) { - mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B, + mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B, stock->nr_slab_reclaimable_b); stock->nr_slab_reclaimable_b = 0; } if (stock->nr_slab_unreclaimable_b) { - mod_objcg_mlstate(objcg, oldpg, NR_SLAB_UNRECLAIMABLE_B, + mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B, stock->nr_slab_unreclaimable_b); stock->nr_slab_unreclaimable_b = 0; } + stock->index = i; stock->node_id = pgdat->node_id; } @@ -3231,10 +3257,16 @@ static bool __consume_obj_stock(struct obj_cgroup *objcg, struct obj_stock_pcp *stock, unsigned int nr_bytes) { - if (objcg == READ_ONCE(stock->cached_objcg) && - stock->nr_bytes >= nr_bytes) { - stock->nr_bytes -= nr_bytes; - return true; + int i; + + for (i = 0; i < NR_OBJ_STOCK; ++i) { + if (READ_ONCE(stock->cached[i]) != objcg) + continue; + if (stock->nr_bytes[i] >= nr_bytes) { + stock->nr_bytes[i] -= nr_bytes; + return true; + } + return false; } return false; @@ -3255,16 +3287,42 @@ static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes) return ret; } -static void drain_obj_stock(struct obj_stock_pcp *stock) +/* Flush the cached slab stats (if any) back to their owning objcg/pgdat. */ +static void drain_obj_stock_stats(struct obj_stock_pcp *stock) { - struct obj_cgroup *old = READ_ONCE(stock->cached_objcg); + struct obj_cgroup *old; + struct pglist_data *oldpg; + + if (stock->index < 0) + return; + + old = READ_ONCE(stock->cached[stock->index]); + oldpg = NODE_DATA(stock->node_id); + + if (stock->nr_slab_reclaimable_b) { + mod_objcg_mlstate(old, oldpg, NR_SLAB_RECLAIMABLE_B, + stock->nr_slab_reclaimable_b); + stock->nr_slab_reclaimable_b = 0; + } + if (stock->nr_slab_unreclaimable_b) { + mod_objcg_mlstate(old, oldpg, NR_SLAB_UNRECLAIMABLE_B, + stock->nr_slab_unreclaimable_b); + stock->nr_slab_unreclaimable_b = 0; + } + stock->index = -1; + stock->node_id = NUMA_NO_NODE; +} + +static void drain_obj_stock_slot(struct obj_stock_pcp *stock, int i) +{ + struct obj_cgroup *old = READ_ONCE(stock->cached[i]); if (!old) return; - if (stock->nr_bytes) { - unsigned int nr_pages = stock->nr_bytes >> PAGE_SHIFT; - unsigned int nr_bytes = stock->nr_bytes & (PAGE_SIZE - 1); + if (stock->nr_bytes[i]) { + unsigned int nr_pages = stock->nr_bytes[i] >> PAGE_SHIFT; + unsigned int nr_bytes = stock->nr_bytes[i] & (PAGE_SIZE - 1); if (nr_pages) { struct mem_cgroup *memcg; @@ -3290,46 +3348,43 @@ static void drain_obj_stock(struct obj_stock_pcp *stock) * so it might be changed in the future. */ atomic_add(nr_bytes, &old->nr_charged_bytes); - stock->nr_bytes = 0; + stock->nr_bytes[i] = 0; } - /* - * Flush the vmstat data in current stock - */ - if (stock->nr_slab_reclaimable_b || stock->nr_slab_unreclaimable_b) { - struct pglist_data *oldpg = NODE_DATA(stock->node_id); + /* Flush vmstat data when its owning slot is being drained. */ + if (stock->index == i) + drain_obj_stock_stats(stock); - if (stock->nr_slab_reclaimable_b) { - mod_objcg_mlstate(old, oldpg, - NR_SLAB_RECLAIMABLE_B, - stock->nr_slab_reclaimable_b); - stock->nr_slab_reclaimable_b = 0; - } - if (stock->nr_slab_unreclaimable_b) { - mod_objcg_mlstate(old, oldpg, - NR_SLAB_UNRECLAIMABLE_B, - stock->nr_slab_unreclaimable_b); - stock->nr_slab_unreclaimable_b = 0; - } - stock->node_id = NUMA_NO_NODE; - } - - WRITE_ONCE(stock->cached_objcg, NULL); + WRITE_ONCE(stock->cached[i], NULL); obj_cgroup_put(old); } +static void drain_obj_stock(struct obj_stock_pcp *stock) +{ + int i; + + for (i = 0; i < NR_OBJ_STOCK; ++i) + drain_obj_stock_slot(stock, i); +} + static bool obj_stock_flush_required(struct obj_stock_pcp *stock, struct mem_cgroup *root_memcg) { - struct obj_cgroup *objcg = READ_ONCE(stock->cached_objcg); + struct obj_cgroup *objcg; struct mem_cgroup *memcg; bool flush = false; + int i; rcu_read_lock(); - if (objcg) { + for (i = 0; i < NR_OBJ_STOCK; ++i) { + objcg = READ_ONCE(stock->cached[i]); + if (!objcg) + continue; memcg = obj_cgroup_memcg(objcg); - if (memcg && mem_cgroup_is_descendant(memcg, root_memcg)) + if (memcg && mem_cgroup_is_descendant(memcg, root_memcg)) { flush = true; + break; + } } rcu_read_unlock(); @@ -3343,6 +3398,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg, { unsigned int nr_pages = 0; unsigned int stock_nr_bytes; + int i, slot = -1, empty_slot = -1; if (!stock) { nr_pages = nr_bytes >> PAGE_SHIFT; @@ -3351,16 +3407,44 @@ static void __refill_obj_stock(struct obj_cgroup *objcg, goto out; } - stock_nr_bytes = stock->nr_bytes; - if (READ_ONCE(stock->cached_objcg) != objcg) { /* reset if necessary */ - drain_obj_stock(stock); + for (i = 0; i < NR_OBJ_STOCK; ++i) { + struct obj_cgroup *cached = READ_ONCE(stock->cached[i]); + + if (!cached) { + if (empty_slot == -1) + empty_slot = i; + continue; + } + if (cached == objcg) { + slot = i; + break; + } + } + + if (slot == -1) { + slot = empty_slot; + if (slot == -1) { + slot = stock->drain_idx++; + if (stock->drain_idx == NR_OBJ_STOCK) + stock->drain_idx = 0; + drain_obj_stock_slot(stock, slot); + } obj_cgroup_get(objcg); + /* + * Keep the xchg result in the unsigned int local; storing + * it directly into stock->nr_bytes[slot] (uint16_t) would + * silently truncate values >= U16_MAX and bypass the flush + * guard below, leaking page-counter charges. + */ stock_nr_bytes = atomic_read(&objcg->nr_charged_bytes) ? atomic_xchg(&objcg->nr_charged_bytes, 0) : 0; - WRITE_ONCE(stock->cached_objcg, objcg); + WRITE_ONCE(stock->cached[slot], objcg); allow_uncharge = true; /* Allow uncharge when objcg changes */ + } else { + stock_nr_bytes = stock->nr_bytes[slot]; } + stock_nr_bytes += nr_bytes; if ((allow_uncharge && (stock_nr_bytes > PAGE_SIZE)) || @@ -3368,7 +3452,7 @@ static void __refill_obj_stock(struct obj_cgroup *objcg, nr_pages = stock_nr_bytes >> PAGE_SHIFT; stock_nr_bytes &= (PAGE_SIZE - 1); } - stock->nr_bytes = stock_nr_bytes; + stock->nr_bytes[slot] = stock_nr_bytes; out: if (nr_pages) From 3e8d8eb8d7f5b1ec3993ad4dbb8140a55f789f90 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 26 May 2026 11:27:16 +0900 Subject: [PATCH 2907/5214] zram: do not leak blk idx at the end of writeback zram_writeback_slots() loop can terminate with valid reserved backing device blk_idx. The problem is that cleanup code doesn't release that reserved blk_idx before zram_writeback_slots() returns, which leads to blk_idx leak (it becomes permanently busy and can not be used for actual writeback.) This does not lead to any system instabilities, it only means that we can writeback less pages. The scenario is hard to hit in practice as it requires writeabck to race with modification (slot-free or overwrite) of the final post-processing slot. Release reserved but unused blk_idx before returning from zram_writeback_slots(). Link: https://lore.kernel.org/20260526022754.2377730-2-senozhatsky@chromium.org Fixes: f405066a1f0db ("zram: introduce writeback bio batching") Signed-off-by: Sergey Senozhatsky Suggested-by: Brian Geffon Cc: Jens Axboe Cc: Minchan Kim Cc: Richard Chang Signed-off-by: Andrew Morton --- drivers/block/zram/zram_drv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 07111455eecf..602abfe23797 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1127,6 +1127,9 @@ static int zram_writeback_slots(struct zram *zram, if (req) release_wb_req(req); + if (blk_idx != INVALID_BDEV_BLOCK) + zram_release_bdev_block(zram, blk_idx); + while (atomic_read(&wb_ctl->num_inflight) > 0) { wait_event(wb_ctl->done_wait, !list_empty(&wb_ctl->done_reqs)); err = zram_complete_done_reqs(zram, wb_ctl); From 3bf1c285dc406067eae5b3a7072afad81ad4a4fc Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 26 May 2026 11:27:17 +0900 Subject: [PATCH 2908/5214] zram: clear trailing bytes of compressed writeback pages Patch series "zram: writeback fixes", v2. Brian (privately) reported a "leak" of writeback bitmap in certain cases, so that backing device can store less pages; and a theoretical data leak in the trailing bytes of compressed writeback pages. Both issues are low risk. This patch (of 2): When compressed writeback is available writtenback pages contain "garbage" in PAGE_SIZE - obj_size trailing bytes. That "garbage" is, basically, whatever data that page held before we got it for writeback. To get advantage of it an attacker needs to be able to read from active backing swap device, which is already catastrophic. Still, just in case, zero out those trailing bytes before writeback to a backing device so that we only store swap-ed out data there. Link: https://lore.kernel.org/20260526022754.2377730-1-senozhatsky@chromium.org Link: https://lore.kernel.org/20260526022754.2377730-3-senozhatsky@chromium.org Fixes: d38fab605c66 ("zram: introduce compressed data writeback") Signed-off-by: Sergey Senozhatsky Suggested-by: Brian Geffon Cc: Jens Axboe Cc: Minchan Kim Cc: Richard Chang Signed-off-by: Andrew Morton --- drivers/block/zram/zram_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 602abfe23797..7917fc7a2a29 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2134,6 +2134,8 @@ static int read_from_zspool_raw(struct zram *zram, struct page *page, u32 index) zs_obj_read_end(zram->mem_pool, handle, size, src); zcomp_stream_put(zstrm); + memzero_page(page, size, PAGE_SIZE - size); + return 0; } #endif From 088a2353d714591d2eadb9870767910b9c67b32d Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Tue, 26 May 2026 20:56:48 +0100 Subject: [PATCH 2909/5214] mm: remove mentions of PageWriteback Update two comments to refer to writeback in general instead of the specific flag. Convert the large comment in memory.c to be entirely folio-based. Link: https://lore.kernel.org/20260526195650.353196-1-willy@infradead.org Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/compaction.c | 2 +- mm/memory.c | 20 ++++++++++---------- mm/migrate.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index 168e63940b78..8f664fb09f24 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1123,7 +1123,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, * To minimise LRU disruption, the caller can indicate with * ISOLATE_ASYNC_MIGRATE that it only wants to isolate pages * it will be able to migrate without blocking - clean pages - * for the most part. PageWriteback would require blocking. + * for the most part. Writeback would require blocking. */ if ((mode & ISOLATE_ASYNC_MIGRATE) && folio_test_writeback(folio)) goto isolate_fail_put; diff --git a/mm/memory.c b/mm/memory.c index 7c020995eafc..5a365492a9a2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5398,18 +5398,18 @@ static vm_fault_t __do_fault(struct vm_fault *vmf) vm_fault_t ret; /* - * Preallocate pte before we take page_lock because this might lead to - * deadlocks for memcg reclaim which waits for pages under writeback: - * lock_page(A) - * SetPageWriteback(A) - * unlock_page(A) - * lock_page(B) - * lock_page(B) + * Preallocate pte before we take folio lock because this might lead to + * deadlocks for memcg reclaim which waits for folios under writeback: + * folio_lock(A) + * folio_set_writeback(A) + * folio_unlock(A) + * folio_lock(B) + * folio_lock(B) * pte_alloc_one * shrink_folio_list - * wait_on_page_writeback(A) - * SetPageWriteback(B) - * unlock_page(B) + * folio_wait_writeback(A) + * folio_set_writeback(B) + * folio_unlock(B) * # flush A, B to clear the writeback */ if (pmd_none(*vmf->pmd) && !vmf->prealloc_pte) { diff --git a/mm/migrate.c b/mm/migrate.c index 0c6a0ab6ecce..d8090cdda4f9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1256,7 +1256,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio, if (folio_test_writeback(src)) { /* * Only in the case of a full synchronous migration is it - * necessary to wait for PageWriteback. In the async case, + * necessary to wait for writeback. In the async case, * the retry loop is too short and in the sync-light case, * the overhead of stalling is too much */ From 9c87962f85106a4d330a91b26b054376245f47c0 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Tue, 26 May 2026 21:00:30 +0100 Subject: [PATCH 2910/5214] mm: document the folio refcount a little better Expand the documentation of folio_ref_count() to talk about expected, temporary and spurious refcounts as well as the concept of freezing. Link: https://lore.kernel.org/20260526200032.353868-1-willy@infradead.org Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- include/linux/page_ref.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/linux/page_ref.h b/include/linux/page_ref.h index 94d3f0e71c06..9f5c75d06f76 100644 --- a/include/linux/page_ref.h +++ b/include/linux/page_ref.h @@ -71,6 +71,12 @@ static inline int page_ref_count(const struct page *page) * folio_ref_count - The reference count on this folio. * @folio: The folio. * + * Folios contain a reference count. When that reference count reaches + * zero, the folio is referred to as frozen. At this point, it will + * usually be returned to the memory allocator, but some parts of the + * kernel freeze folios in order to perform unusual operations on them + * such as splitting or migration. + * * The refcount is usually incremented by calls to folio_get() and * decremented by calls to folio_put(). Some typical users of the * folio refcount: @@ -82,6 +88,18 @@ static inline int page_ref_count(const struct page *page) * - Pipes * - Direct IO which references this page in the process address space * + * The reference count has three components: expected, temporary and + * spurious. The expected reference count of a folio is that which + * we would logically expect it to be from just reading the code. + * Temporary refcounts are gained by threads which need a temporary + * reference to make sure the folio isn't reallocated while they use it. + * Spurious refcounts are gained by threads which, thanks to RCU walks + * of the page tables or file cache, find a stale pointer to a folio. + * These threads will drop the refcount after discoveering the pointer + * is stale, but it can surprise other users to see the spurious refcount + * on a freshly allocated folio (eg they may see a refcount of 2 instead + * of 1). + * * Return: The number of references to this folio. */ static inline int folio_ref_count(const struct folio *folio) From 13f77972b94c51f6e5b94d672025601363440a94 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 26 May 2026 16:42:11 +0200 Subject: [PATCH 2911/5214] mm/migrate: find_mm_struct: fix race between security checks and suid exec The target task can execute a setuid binary between ptrace_may_access() and get_task_mm(). Protect this critical section with exec_update_lock. I don't think cpuset_mems_allowed(task) should be called under exec_update_lock, but this patch just tries to add the minimal fix. Perhaps we can later add a common helper which can be used by find_mm_struct() and kernel_migrate_pages(). Link: https://lore.kernel.org/ahWxQ3JxdR5ff2qf@redhat.com Signed-off-by: Oleg Nesterov Reviewed-by: Gregory Price Cc: Alistair Popple Cc: Byungchul Park Cc: David Hildenbrand Cc: "Huang, Ying" Cc: Jann Horn Cc: Joshua Hahn Cc: Kees Cook Cc: Matthew Brost Cc: Rakie Kim Cc: Ying Huang Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/migrate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mm/migrate.c b/mm/migrate.c index d8090cdda4f9..d9b23909d716 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -2555,24 +2555,29 @@ static struct mm_struct *find_mm_struct(pid_t pid, nodemask_t *mem_nodes) } task = find_get_task_by_vpid(pid); - if (!task) { + if (!task) return ERR_PTR(-ESRCH); - } + if (down_read_killable(&task->signal->exec_update_lock)) { + mm = ERR_PTR(-EINTR); + goto out; + } /* * Check if this process has the right to modify the specified * process. Use the regular "ptrace_may_access()" checks. */ if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) { mm = ERR_PTR(-EPERM); - goto out; + goto unlock; } mm = ERR_PTR(security_task_movememory(task)); if (IS_ERR(mm)) - goto out; + goto unlock; *mem_nodes = cpuset_mems_allowed(task); mm = get_task_mm(task); +unlock: + up_read(&task->signal->exec_update_lock); out: put_task_struct(task); if (!mm) From 8f42b751e7d7f73dbb2b59281cef891bfb7ae40c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 28 May 2026 09:56:14 -0400 Subject: [PATCH 2912/5214] MAINTAINERS: add vm.rst to memory management core The vm.rst file is currently not listed in the MAINTAINERS file, so let's go ahead and add to the MM core subsystem so that the maintainers are CCed when changes to the documentation are proposed. Link: https://lore.kernel.org/20260528-mm-vm-rst-maintainers-file-v1-1-306631c0a610@redhat.com Signed-off-by: Brian Masney Reviewed-by: Lorenzo Stoakes Reviewed-by: Oscar Salvador (SUSE) Reviewed-by: Liam R. Howlett (Oracle) Reviewed-by: SeongJae Park Acked-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 48c2265f00a9..ef31a8dd9e5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16781,6 +16781,7 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/admin-guide/sysctl/vm.rst F: include/linux/folio_batch.h F: include/linux/gfp.h F: include/linux/gfp_types.h From a195cf013e98b02d3c129e2cccd7efa98aabf546 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 28 May 2026 09:45:10 -0400 Subject: [PATCH 2913/5214] docs: mm: clarify that user_reserve_kbytes has no effect when overcommit_memory is set to 0 or 1 Looking at __vm_enough_memory() in mm/util.c, user_reserve_kbytes has no effect when overcommit_memory is set to 0 or 1. The documentation for overcommit_memory already references user_reserve_kbytes when the flag is set to 2. Let's go ahead and add a clarification to user_reserve_kbytes in vm.rst that it has no effect when overcommit_memory is set to 0 or 1. Link: https://lore.kernel.org/20260528-mm-clarify-docs-v1-1-aa88e83b4bfd@redhat.com Signed-off-by: Brian Masney Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Shuah Khan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- Documentation/admin-guide/sysctl/vm.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst index 97e12359775c..b9b0c218bfb4 100644 --- a/Documentation/admin-guide/sysctl/vm.rst +++ b/Documentation/admin-guide/sysctl/vm.rst @@ -1034,6 +1034,8 @@ min(3% of current process size, user_reserve_kbytes) of free memory. This is intended to prevent a user from starting a single memory hogging process, such that they cannot recover (kill the hog). +This setting has no effect when overcommit_memory is set to 0 or 1. + user_reserve_kbytes defaults to min(3% of the current process size, 128MB). If this is reduced to zero, then the user will be allowed to allocate From 93612d48fa42b3d1a637eb9279e15281c611c000 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 25 May 2026 12:17:26 +0800 Subject: [PATCH 2914/5214] ocfs2: rebase copied fsdlm LVB pointers in locking_state The locking_state debugfs iterator snapshots struct ocfs2_lock_res by value under ocfs2_dlm_tracking_lock and later formats that copy in ocfs2_dlm_seq_show(). That is fine for the inline fields, but the userspace fsdlm stack stores the LVB through lksb_fsdlm.sb_lvbptr. Once the iterator drops the tracking lock, a copied non-NULL sb_lvbptr still points into the original lockres owner, so teardown can free that container before the debugfs dump walks the raw LVB bytes. Rebase the copied sb_lvbptr to the copied l_lksb before dumping the raw LVB. The seq snapshot already carries the inline LVB storage reserved in struct ocfs2_dlm_lksb, so the debugfs reader can dump the copied bytes without borrowing the original lockres lifetime. The buggy scenario involves two paths, with each column showing the order within that path: locking_state reader: lockres teardown: 1. ocfs2_dlm_seq_start()/next() 1. file release or another owner copies struct ocfs2_lock_res teardown reaches 2. ocfs2_dlm_seq_show() formats ocfs2_lock_res_free() the copied row 2. the lockres is removed from the 3. ocfs2_dlm_lvb() follows the tracking list copied sb_lvbptr 3. the owner frees the original lockres container Validation reproduced this kernel report: KASAN slab-use-after-free in ocfs2_dlm_seq_show+0x1bd/0x430 RIP: 0033:0x7f8ec4b1e29d The buggy address belongs to the object at ffff88810a1e0800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 368 bytes inside of freed 1024-byte region [ffff88810a1e0800, ffff88810a1e0c00) Read of size 1 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ocfs2_dlm_seq_show+0x1bd/0x430 (fs/ocfs2/dlmglue.c:3137) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 seq_read_iter+0x29d/0x790 seq_read+0x20a/0x280 find_held_lock+0x2b/0x80 rcu_read_unlock+0x18/0x70 full_proxy_read+0x9e/0xd0 vfs_read+0x12c/0x590 ksys_read+0xd2/0x170 do_user_addr_fault+0x65a/0x890 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 ocfs2_file_open+0x13e/0x300 do_dentry_open+0x233/0x7f0 vfs_open+0x5a/0x1b0 path_openat+0x66d/0x1540 do_file_open+0x186/0x2b0 do_sys_openat2+0xce/0x150 __x64_sys_openat+0xd0/0x140 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 ocfs2_file_release+0x138/0x260 __fput+0x1df/0x4b0 fput_close_sync+0xd2/0x170 __x64_sys_close+0x55/0x90 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Link: https://lore.kernel.org/20260525041726.4112882-1-rollkingzzc@gmail.com Fixes: cf4d8d75d8ab ("ocfs2: add fsdlm to stackglue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/dlmglue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 7283bb2c5a31..a23dd8f86c89 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3134,6 +3134,22 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos) * - Add last pr/ex unlock times and first lock wait time in usecs */ #define OCFS2_DLM_DEBUG_STR_VERSION 4 + +/* + * The debug iterator snapshots lockres by value, so a userspace-stack LVB + * pointer copied from the original lockres must be rebased to the copied + * lksb before the dump walks the raw bytes. + */ +static void ocfs2_dlm_seq_rebase_lvb(struct ocfs2_lock_res *lockres) +{ + if (!ocfs2_stack_supports_plocks()) + return; + + if (lockres->l_lksb.lksb_fsdlm.sb_lvbptr) + lockres->l_lksb.lksb_fsdlm.sb_lvbptr = + (char *)&lockres->l_lksb + sizeof(struct dlm_lksb); +} + static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) { int i; @@ -3191,6 +3207,7 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) lockres->l_blocking); /* Dump the raw LVB */ + ocfs2_dlm_seq_rebase_lvb(lockres); lvb = ocfs2_dlm_lvb(&lockres->l_lksb); for(i = 0; i < DLM_LVB_LEN; i++) seq_printf(m, "0x%x\t", lvb[i]); From 9a79524d1420e6b79a6868208c264f4518d1318e Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Tue, 26 May 2026 13:47:15 +0200 Subject: [PATCH 2915/5214] kcov: use WRITE_ONCE() for selftest mode stores The KCOV selftest enables coverage by setting current->kcov_mode to KCOV_MODE_TRACE_PC without installing a coverage area. If an interrupt records coverage in that window, the access should fault and expose the bug. When building for QEMU raspi0 (Raspberry Pi Zero, ARMv6, CONFIG_CPU_V6K=y, CONFIG_CURRENT_POINTER_IN_TPIDRURO=y) with GCC 13.3.0, the store that enables the mode is removed. The generated kcov_init() code only stores zero after the wait loop: mrc 15, 0, r3, cr13, cr0, {3} str r4, [r3, #2028] where r4 is zero. There is no store of KCOV_MODE_TRACE_PC before the loop, so the selftest reports success without exercising coverage. Use WRITE_ONCE() for the temporary mode stores. With the same compiler and config, kcov_init() contains the intended mode store: mov r3, #2 mrc 15, 0, r2, cr13, cr0, {3} str r3, [r2, #2028] Now that the KCOV selftest is actually executed, it may expose KCOV instrumentation issues depending on the kernel config. That is expected for a selftest that was intended to catch coverage from interrupt paths. Link: https://lore.kernel.org/20260526114715.38280-1-kmehltretter@gmail.com Fixes: 6cd0dd934b03 ("kcov: Add interrupt handling self test") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Reviewed-by: Alexander Potapenko Cc: Andrey Konovalov Cc: Dmitry Vyukov Cc: Kees Cook Cc: Marco Elver Cc: Peter Zijlstra Cc: Signed-off-by: Andrew Morton --- kernel/kcov.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index fd2503030729..1df373fb562b 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1119,10 +1119,10 @@ static void __init selftest(void) * potentially traced functions in this region. */ start = jiffies; - current->kcov_mode = KCOV_MODE_TRACE_PC; + WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) ; - current->kcov_mode = 0; + WRITE_ONCE(current->kcov_mode, 0); pr_err("done running self test\n"); } #endif From 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:18:41 +0200 Subject: [PATCH 2916/5214] err.h: use __always_inline on all error pointer helpers While testing randconfig builds on s390, I came across a link failure with CONFIG_DMA_SHARED_BUFFER disabled: ERROR: modpost: "dma_buf_put" [drivers/iommu/iommufd/iommufd.ko] undefined! The problem here is that IS_ERR() is not inlined and dead code elimination fails as a consequence. The err.h helpers all turn into a trivial assignment of a bit mask and should never result in a function call, so force them to always be inline. This should generally result in better object code aside from avoiding the link failure above. Link: https://lore.kernel.org/20260526101851.2495110-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Alexander Lobakin Reviewed-by: Nathan Chancellor Tested-by: Tamir Duberstein Cc: Alexander Gordeev Cc: Andriy Shevchenko Cc: Ansuel Smith Cc: Bjorn Andersson Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Signed-off-by: Andrew Morton --- include/linux/err.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/err.h b/include/linux/err.h index 8c37be0620ab..d3e38d5b3a98 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -36,7 +36,7 @@ * * Return: A pointer with @error encoded within its value. */ -static inline void * __must_check ERR_PTR(long error) +static __always_inline void * __must_check ERR_PTR(long error) { return (void *) error; } @@ -60,7 +60,7 @@ static inline void * __must_check ERR_PTR(long error) * @ptr: An error pointer. * Return: The error code within @ptr. */ -static inline long __must_check PTR_ERR(__force const void *ptr) +static __always_inline long __must_check PTR_ERR(__force const void *ptr) { return (long) ptr; } @@ -73,7 +73,7 @@ static inline long __must_check PTR_ERR(__force const void *ptr) * @ptr: The pointer to check. * Return: true if @ptr is an error pointer, false otherwise. */ -static inline bool __must_check IS_ERR(__force const void *ptr) +static __always_inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } @@ -87,7 +87,7 @@ static inline bool __must_check IS_ERR(__force const void *ptr) * * Like IS_ERR(), but also returns true for a null pointer. */ -static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) +static __always_inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr); } @@ -99,7 +99,7 @@ static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ -static inline void * __must_check ERR_CAST(__force const void *ptr) +static __always_inline void * __must_check ERR_CAST(__force const void *ptr) { /* cast away the const */ return (void *) ptr; @@ -122,7 +122,7 @@ static inline void * __must_check ERR_CAST(__force const void *ptr) * * Return: The error code within @ptr if it is an error pointer; 0 otherwise. */ -static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) +static __always_inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); From 9ac9a08e4ac4bc063e56e1aff266c5e8aa5c6c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 26 May 2026 18:43:40 +0200 Subject: [PATCH 2917/5214] lib: kunit_iov_iter: repeatedly call alloc_pages_bulk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alloc_pages_bulk() is not guaranteed to return all requested pages in a single call. Call it repeatedly until all pages have been allocated or no more progress is being made. Link: https://lore.kernel.org/20260526-kunit_iov_iter-alloc_bulk-v2-1-24fbcd995c61@weissschuh.net Fixes: 2d71340ff1d4 ("iov_iter: Kunit tests for copying to/from an iterator") Signed-off-by: Thomas Weißschuh Cc: "Christian A. Ehrhardt" Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/tests/kunit_iov_iter.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index f02f7b7aa796..1e6fce9cb255 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -53,7 +53,7 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, size_t npages) { struct page **pages; - unsigned long got; + unsigned long got, last; void *buffer; unsigned int i; @@ -61,7 +61,15 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; - got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + got = 0; + while (true) { + last = got; + got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + + if (last == got || got == npages) + break; + } + if (got != npages) { release_pages(pages, got); kvfree(pages); From 9bd541e09dffff27e5bec0f9f45b0228173a5375 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 24 May 2026 19:12:48 +0800 Subject: [PATCH 2918/5214] ocfs2: reject oversized group bitmap descriptors ocfs2_validate_gd_parent() only bounds bg_bits against the parent allocator's chain geometry. A malicious descriptor can still claim a bg_size/bg_bits pair that exceeds the bitmap bytes that physically fit in the group descriptor block, so later bitmap scans and bit updates can run past bg_bitmap. Add a physical-cap check based on ocfs2_group_bitmap_size() for the parent allocator type and reject descriptors whose bg_size or bg_bits exceed that capacity. Keep the existing chain geometry check so both the on-disk bitmap layout and the allocator metadata must agree before the descriptor is used. Validation reproduced this kernel report: KASAN use-after-free in _find_next_bit+0x7f/0xc0 Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) _find_next_bit+0x7f/0xc0 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) ocfs2_find_max_contig_free_bits+0x35/0x70 (fs/ocfs2/suballoc.c:1375) ocfs2_block_group_set_bits+0x472/0x4b0 (fs/ocfs2/suballoc.c:1457) ocfs2_cluster_group_search+0x16b/0x440 (fs/ocfs2/suballoc.c:86) ocfs2_bg_discontig_fix_result+0x1ef/0x230 (fs/ocfs2/suballoc.c:1786) ocfs2_search_chain+0x8f8/0x10a0 (fs/ocfs2/suballoc.c:1886) get_page_from_freelist+0x70e/0x2370 (?:?) lock_release+0xc6/0x290 (?:?) do_raw_spin_unlock+0x9a/0x100 (?:?) kasan_unpoison+0x27/0x60 (?:?) __bfs+0x147/0x240 (?:?) get_page_from_freelist+0x83d/0x2370 (?:?) ocfs2_claim_suballoc_bits+0x38c/0xe70 (fs/ocfs2/suballoc.c:96) sched_domains_numa_masks_clear+0x70/0xd0 (?:?) check_irq_usage+0xe8/0xb70 (?:?) __ocfs2_claim_clusters+0x18d/0x4c0 (fs/ocfs2/suballoc.c:2497) check_path+0x24/0x50 (?:?) rcu_is_watching+0x20/0x50 (?:?) check_prev_add+0xfd/0xd00 (?:?) ocfs2_add_clusters_in_btree+0x17d/0x810 (fs/ocfs2/suballoc.c:?) __folio_batch_add_and_move+0x1f5/0x3d0 (?:?) ocfs2_add_inode_data+0xd9/0x120 (fs/ocfs2/suballoc.c:?) filemap_add_folio+0x105/0x1f0 (?:?) ocfs2_write_begin_nolock+0x29f7/0x2f80 (fs/ocfs2/suballoc.c:3043) ocfs2_read_inode_block+0xb5/0x110 (fs/ocfs2/suballoc.c:?) down_write+0xf5/0x180 (?:?) ocfs2_write_begin+0x180/0x240 (fs/ocfs2/suballoc.c:?) __mark_inode_dirty+0x758/0x9a0 (?:?) inode_to_bdi+0x41/0x90 (?:?) balance_dirty_pages_ratelimited_flags+0xf8/0x1d0 (?:?) generic_perform_write+0x252/0x440 (?:?) mnt_put_write_access_file+0x16/0x70 (?:?) file_update_time_flags+0xe4/0x200 (?:?) ocfs2_file_write_iter+0x80a/0x1320 (fs/ocfs2/suballoc.c:?) lock_acquire+0x184/0x2f0 (?:?) ksys_write+0xd2/0x170 (?:?) apparmor_file_permission+0xf5/0x310 (?:?) read_zero+0x8d/0x140 (?:?) lock_is_held_type+0x8f/0x100 (?:?) Link: https://lore.kernel.org/20260524111248.1429884-1-rollkingzzc@gmail.com Fixes: ccd979bdbce9 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/suballoc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index d284e0e37252..a4a2b87a45fe 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -231,8 +231,16 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, int resize) { unsigned int max_bits; + unsigned int max_bitmap_bits; + unsigned int max_bitmap_size; + int suballocator; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data; + suballocator = le64_to_cpu(di->i_blkno) != OCFS2_SB(sb)->bitmap_blkno; + max_bitmap_size = ocfs2_group_bitmap_size(sb, suballocator, + OCFS2_SB(sb)->s_feature_incompat); + max_bitmap_bits = max_bitmap_size * 8; + if (di->i_blkno != gd->bg_parent_dinode) { do_error("Group descriptor #%llu has bad parent pointer (%llu, expected %llu)\n", (unsigned long long)bh->b_blocknr, @@ -240,6 +248,20 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, (unsigned long long)le64_to_cpu(di->i_blkno)); } + if (le16_to_cpu(gd->bg_size) > max_bitmap_size) { + do_error("Group descriptor #%llu has bitmap size %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_size), + max_bitmap_size); + } + + if (le16_to_cpu(gd->bg_bits) > max_bitmap_bits) { + do_error("Group descriptor #%llu has bit count %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_bits), + max_bitmap_bits); + } + max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc); if (le16_to_cpu(gd->bg_bits) > max_bits) { do_error("Group descriptor #%llu has bit count of %u\n", From d280a26a983f62bfeff3f134f9d5ba4035356b43 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 12:53:00 +0300 Subject: [PATCH 2919/5214] xor: use kmalloc() in calibrate_xor_blocks() Patch series "lib/raid: replace __get_free_pages() call with kmalloc()", v4. The xor benchmark allocates 4 pages for a scratch buffer that is used purely as a CPU-only XOR working area. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). This patch (of 2): The xor benchmark allocates 4 pages for a scratch buffer that is used purely as a CPU-only XOR working area. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). Link: https://lore.kernel.org/20260528-lib-v4-0-4e3ad1277279@kernel.org Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/20260528-lib-v4-1-4e3ad1277279@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Cc: Christoph Hellwig Cc: Li Nan Cc: Song Liu Signed-off-by: Andrew Morton --- lib/raid/xor/xor-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c index bd4e6e434418..50931fbf0324 100644 --- a/lib/raid/xor/xor-core.c +++ b/lib/raid/xor/xor-core.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -114,7 +115,7 @@ static int __init calibrate_xor_blocks(void) if (forced_template) return 0; - b1 = (void *) __get_free_pages(GFP_KERNEL, 2); + b1 = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); if (!b1) { pr_warn("xor: Yikes! No memory available.\n"); return -ENOMEM; @@ -132,7 +133,7 @@ static int __init calibrate_xor_blocks(void) pr_info("xor: using function: %s (%d MB/sec)\n", fastest->name, fastest->speed); - free_pages((unsigned long)b1, 2); + kfree(b1); return 0; } From 19c000ba93d609e15e95fed76a9e3b8055833098 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 12:53:01 +0300 Subject: [PATCH 2920/5214] raid6: use kmalloc() in raid6_select_algo() raid6_select_algo() allocates 8 pages for buffer that is used as a scratch area for selection of the best algorithm. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/20260528-lib-v4-2-4e3ad1277279@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Christoph Hellwig Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Li Nan Cc: Song Liu Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index a600d5853672..6f5c89ab2b17 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "algos.h" @@ -153,7 +154,6 @@ EXPORT_SYMBOL_GPL(raid6_recov_datap); #define RAID6_TIME_JIFFIES_LG2 4 #define RAID6_TEST_DISKS 8 -#define RAID6_TEST_DISKS_ORDER 3 static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) @@ -247,7 +247,7 @@ static int __init raid6_select_algo(void) } /* prepare the buffer and fill it circularly with gfmul table */ - disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); + disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL); if (!disk_ptr) { pr_err("raid6: Yikes! No memory available.\n"); return -ENOMEM; @@ -269,7 +269,7 @@ static int __init raid6_select_algo(void) /* select raid gen_syndrome function */ error = raid6_choose_gen(&dptrs, disks); - free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); + kfree(disk_ptr); return error; } From 6371a07148ee979af22a9d6f4c277462953a9a4a Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 29 May 2026 12:41:28 +0300 Subject: [PATCH 2921/5214] ocfs2: fix buffer head management in ocfs2_read_blocks() In ocfs2_read_blocks(), caller should't assume that buffer head returned by 'sb_getblk()' is exclusively owned and so 'put_bh()' always drops b_count from 1 to 0. If it is not so, buffer head remains on hold and likely to be returned by the next call to 'sb_getblk()' unchanged - that is, with BH_Uptodate bit set even if it has failed validation previously, thus allowing to insert that buffer head into OCFS2 metadata cache and submit it to upper layers. To avoid such a scenario, BH_Uptodate should be cleared immediately after 'validate()' callback has detected some data inconsistency. Link: https://lore.kernel.org/20260529094128.494293-1-dmantipov@yandex.ru Fixes: cf76c78595ca ("ocfs2: don't put and assigning null to bh allocated outside") Signed-off-by: Dmitry Antipov Reported-by: syzbot+caacd220635a9cc3bac9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=caacd220635a9cc3bac9 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/buffer_head_io.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index 701d27d908d4..6114299b121e 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -350,8 +350,6 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, wait_on_buffer(bh); put_bh(bh); bhs[i] = NULL; - } else if (bh && buffer_uptodate(bh)) { - clear_buffer_uptodate(bh); } continue; } @@ -380,8 +378,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, BUG_ON(buffer_jbd(bh)); clear_buffer_needs_validate(bh); status = validate(sb, bh); - if (status) + if (status) { + if (buffer_uptodate(bh)) + clear_buffer_uptodate(bh); goto read_failure; + } } } From a291c77c034b7a81849ce9b71cc9ecda9e587d89 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Sun, 31 May 2026 21:16:45 +0800 Subject: [PATCH 2922/5214] ocfs2: add journal NULL check in ocfs2_checkpoint_inode() During unmount, ocfs2_journal_shutdown() frees the journal and sets osb->journal to NULL. Later, when VFS evicts remaining cached inodes, ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode() -> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a NULL pointer dereference. Fix this by adding a NULL check for osb->journal in ocfs2_checkpoint_inode(). If the journal is NULL, it has already been fully flushed and destroyed during shutdown, so there is nothing to checkpoint. Link: https://lore.kernel.org/20260531131645.3650299-1-joseph.qi@linux.alibaba.com Reported-by: Farhad Alemi Fixes: da5e7c87827e ("ocfs2: cleanup journal init and shutdown") Signed-off-by: Joseph Qi Tested-by: Farhad Alemi Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/journal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 6397170f302f..f8b3b2a3d630 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) if (ocfs2_mount_local(osb)) return; + if (!osb->journal) + return; + if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) { /* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more From e234973f286ed2e8961a24561ec91594ec3e3ff8 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:30 +0800 Subject: [PATCH 2923/5214] ocfs2: validate fast symlink target during inode read ocfs2_validate_inode_block() already rejects several inconsistent self-contained dinodes before they are exposed to the rest of the filesystem. Fast symlinks need the same treatment. A zero-cluster symlink is treated as a fast symlink and later read through page_get_link() and ocfs2_fast_symlink_read_folio(). That path uses strnlen() on the inline payload and then copies len + 1 bytes into the folio. If a corrupt dinode stores an i_size that does not fit the inline area or omits the terminating NUL at i_size, that copy reads past the end of the inode block buffer. Reject zero-cluster symlink dinodes whose i_size exceeds the inline fast-symlink capacity or whose inline payload is not NUL-terminated exactly at i_size when the inode block is validated. This keeps malformed fast symlinks from reaching the read path. Validation reproduced this kernel report: KASAN use-after-free in ocfs2_fast_symlink_read_folio+0x12c/0x1f0 RIP: 0033:0x7f5c6d859aa7 Read of size 3905 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) ocfs2_fast_symlink_read_folio+0x12c/0x1f0 (fs/ocfs2/inode.c:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x19f/0x330 (?:?) kasan_report+0xe0/0x110 (?:?) kasan_check_range+0x105/0x1b0 (?:?) __asan_memcpy+0x23/0x60 (?:?) filemap_read_folio+0x27/0xe0 (?:?) filemap_read_folio+0x35/0xe0 (?:?) do_read_cache_folio+0x138/0x230 (?:?) __page_get_link+0x26/0x110 (?:?) page_get_link+0x2e/0x70 (?:?) vfs_readlink+0x15e/0x250 (?:?) touch_atime+0x4d/0x370 (?:?) do_readlinkat+0x186/0x200 (?:?) do_user_addr_fault+0x65a/0x890 (?:?) __x64_sys_readlink+0x46/0x60 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260528151230.361127-1-rollkingzzc@gmail.com Fixes: ea022dfb3c2a ("ocfs: simplify symlink handling") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Gui-Dong Han <2045gemini@gmail.com> Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 432eac01c176..6fc29920ecb2 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1639,6 +1639,29 @@ int ocfs2_validate_inode_block(struct super_block *sb, } } + if (S_ISLNK(le16_to_cpu(di->i_mode)) && + !le32_to_cpu(di->i_clusters)) { + int max_inline = ocfs2_fast_symlink_chars(sb); + u64 i_size = le64_to_cpu(di->i_size); + + if (i_size >= max_inline) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink i_size %llu exceeds max %d\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size, + max_inline - 1); + goto bail; + } + + if (strnlen((char *)di->id2.i_symlink, i_size + 1) != i_size) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink is not NUL-terminated at i_size %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size); + goto bail; + } + } + if (le32_to_cpu(di->i_flags) & OCFS2_CHAIN_FL) { struct ocfs2_chain_list *cl = &di->id2.i_chain; u16 bpc = 1 << (OCFS2_SB(sb)->s_clustersize_bits - From ca1afd88f5eaaff9168e1466e5401385edf59543 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:47 +0800 Subject: [PATCH 2924/5214] ocfs2: reject FITRIM ranges shorter than a cluster ocfs2_trim_mainbm() trims the global bitmap in cluster units, but its too-short range validation only checks sb->s_blocksize. On filesystems with a cluster size larger than the block size, a FITRIM range that is at least one block but shorter than one cluster is accepted and shifted down to len == 0. The later start + len - 1 and len -= ... arithmetic then underflows and can drive trimming past the requested range. Reject ranges shorter than s_clustersize instead. That preserves the existing -EINVAL behavior for requests that cannot discard even one allocation unit and keeps zero-cluster trims out of the group walk. Link: https://lore.kernel.org/20260528151247.361854-1-rollkingzzc@gmail.com Fixes: aa89762c5480 ("ocfs2: return EINVAL if the given range to discard is less than block size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 6e5fd3f12a84..be09e766ac1f 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -7576,7 +7576,7 @@ int ocfs2_trim_mainbm(struct super_block *sb, struct fstrim_range *range) len = range->len >> osb->s_clustersize_bits; minlen = range->minlen >> osb->s_clustersize_bits; - if (minlen >= osb->bitmap_cpg || range->len < sb->s_blocksize) + if (minlen >= osb->bitmap_cpg || range->len < osb->s_clustersize) return -EINVAL; trace_ocfs2_trim_mainbm(start, len, minlen); From 03ad858ce8064861ea580021976dc19b7aabb549 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 31 May 2026 12:47:14 +0800 Subject: [PATCH 2925/5214] ocfs2/dlm: require a ref for locking_state debugfs open debug_lockres_open() copies inode->i_private into struct debug_lockres and debug_lockres_release() later drops that pointer with dlm_put(). That only works if open successfully pins the struct dlm_ctxt. Today open calls dlm_grab(dlm) but ignores its return value. Once the last domain unregister has removed the context from dlm_domains, dlm_grab() returns NULL, yet open still stores the raw pointer and returns success. The later release path is outside the debugfs removal barrier, so it can call dlm_put() after dlm_free_ctxt_mem() has freed the context. KASAN reports this as a slab-use-after-free in dlm_put() called from debug_lockres_release(). Fail the open when dlm_grab() cannot acquire the reference and unwind the seq_file private state before returning. That keeps locking_state from handing out a file descriptor whose release path does not own the dlm_ctxt. The buggy scenario involves two paths, with each column showing the order within that path: locking_state debugfs open: last domain unregister: 1. debug_lockres_open() reads 1. dlm_unregister_domain() calls inode->i_private. dlm_complete_dlm_shutdown(). 2. debug_lockres_open() calls 2. shutdown removes the dlm_ctxt from dlm_grab(dlm) and gets NULL. dlm_domains. 3. open still stores the raw dlm 3. final teardown reaches pointer in dl->dl_ctxt and dlm_free_ctxt_mem() and frees it. returns success. 4. debug_lockres_release() later calls dlm_put(dl->dl_ctxt). Validation reproduced this kernel report: KASAN slab-use-after-free in dlm_put+0x82/0x200 RIP: 0033:0x7f4d349bc9e0 The buggy address belongs to the object at ffff888103a3c000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 816 bytes inside of freed 2048-byte region [ffff888103a3c000, ffff888103a3c800) Write of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) dlm_put+0x82/0x200 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) kasan_check_range+0x105/0x1b0 (?:?) debug_lockres_release+0x53/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) dlm_put+0x9/0x200 (?:?) debug_lockres_release+0x5c/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) full_proxy_release+0x67/0x90 (?:?) __fput+0x1df/0x4b0 (?:?) do_raw_spin_lock+0x10f/0x1b0 (?:?) fput_close_sync+0xd2/0x170 (?:?) __x64_sys_close+0x55/0x90 (?:?) do_syscall_64+0x10c/0x640 (arch/x86/entry/syscall_64.c:87) irqentry_exit+0xac/0x6e0 (?:?) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Freed by task stack: kasan_save_stack+0x33/0x60 (?:?) kasan_save_track+0x14/0x30 (?:?) kasan_save_free_info+0x3b/0x60 (?:?) __kasan_slab_free+0x5f/0x80 (?:?) kfree+0x30f/0x580 (?:?) dlm_put+0x1ce/0x200 (?:?) dlm_unregister_domain+0xf6/0xb30 (?:?) o2cb_cluster_disconnect+0x6b/0x90 (?:?) ocfs2_cluster_disconnect+0x41/0x70 (?:?) ocfs2_dlm_shutdown+0x1c4/0x220 (?:?) ocfs2_dismount_volume+0x38a/0x550 (?:?) generic_shutdown_super+0xc3/0x220 (?:?) kill_block_super+0x29/0x60 (?:?) deactivate_locked_super+0x66/0xe0 (?:?) cleanup_mnt+0x13d/0x210 (?:?) task_work_run+0xfa/0x170 (?:?) exit_to_user_mode_loop+0xd6/0x430 (?:?) do_syscall_64+0x3cb/0x640 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260531044714.1640172-1-rollkingzzc@gmail.com Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/dlm/dlmdebug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index fe4fdd09bae3..564567358620 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -560,6 +560,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file) struct dlm_ctxt *dlm = inode->i_private; struct debug_lockres *dl; void *buf; + int status = -ENOMEM; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -572,16 +573,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file) dl->dl_len = PAGE_SIZE; dl->dl_buf = buf; - dlm_grab(dlm); - dl->dl_ctxt = dlm; + /* ->release uses dl_ctxt after open, so it needs a real pin. */ + dl->dl_ctxt = dlm_grab(dlm); + if (!dl->dl_ctxt) { + status = -ENOENT; + goto bailseq; + } return 0; +bailseq: + seq_release_private(inode, file); bailfree: kfree(buf); bail: - mlog_errno(-ENOMEM); - return -ENOMEM; + if (status != -ENOENT) + mlog_errno(status); + return status; } static int debug_lockres_release(struct inode *inode, struct file *file) From 57dcfd9049d497c31151787a0696d59f0a98f8e6 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Mon, 1 Jun 2026 20:16:18 +0800 Subject: [PATCH 2926/5214] ocfs2: fix race between ocfs2_control_install_private() and ocfs2_control_release() Move atomic_inc(&ocfs2_control_opened) and the handshake state update inside ocfs2_control_lock to close a race window where ocfs2_control_release() can observe ocfs2_control_opened dropping to zero (resetting ocfs2_control_this_node and running_proto) while ocfs2_control_install_private() is about to bump the counter and mark the connection valid. Link: https://lore.kernel.org/20260601121618.1263346-1-joseph.qi@linux.alibaba.com Fixes: 3cfd4ab6b6b4 ("ocfs2: Add the local node id to the handshake.") Signed-off-by: Joseph Qi Reported-by: Ginger Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/stack_user.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 5803f1dee679..91e19d33847c 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -327,18 +327,14 @@ static int ocfs2_control_install_private(struct file *file) ocfs2_control_this_node = p->op_this_node; running_proto.pv_major = p->op_proto.pv_major; running_proto.pv_minor = p->op_proto.pv_minor; - } - -out_unlock: - mutex_unlock(&ocfs2_control_lock); - - if (!rc && set_p) { - /* We set the global values successfully */ atomic_inc(&ocfs2_control_opened); ocfs2_control_set_handshake_state(file, OCFS2_CONTROL_HANDSHAKE_VALID); } +out_unlock: + mutex_unlock(&ocfs2_control_lock); + return rc; } From 1ec3cca2d8b6b9ff6584ca626d4c8918bbf48d44 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Mon, 1 Jun 2026 13:44:33 -0500 Subject: [PATCH 2927/5214] ocfs2: fix out-of-bounds write in ocfs2_remove_refcount_extent [BUG] Unlinking a refcounted file whose refcount tree has leaf blocks triggers a fortify panic due to an out-of-bounds write. [CAUSE] When the last leaf block is removed from a refcount tree, ocfs2_remove_refcount_extent() converts the root back to leaf mode with a bulk memset on &rb->rf_records. rf_records sits in an anonymous union with rf_list. rf_list.l_tree_depth aliases rf_records.rl_count, and is 0 for a single-level tree. With rl_count equal to 0, the memset writes past the 16-byte declared size of rf_records, which the fortify checker catches. [FIX] Replace the bulk memset on &rb->rf_records with a correctly-bounded memset on rl_recs[] alone, after setting rl_count to the correct value. Link: https://lore.kernel.org/ah3TESOsEO9j_JLU@dev Fixes: 2f26f58df041 ("ocfs2: annotate flexible array members with __counted_by_le()") Signed-off-by: Ian Bridges Reported-by: syzbot+3ef989aae096b30f1663@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3ef989aae096b30f1663 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/refcounttree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 8eee5be4d1ed..7323bde70caa 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -2131,10 +2131,15 @@ static int ocfs2_remove_refcount_extent(handle_t *handle, rb->rf_flags = 0; rb->rf_parent = 0; rb->rf_cpos = 0; - memset(&rb->rf_records, 0, sb->s_blocksize - - offsetof(struct ocfs2_refcount_block, rf_records)); + rb->rf_records.rl_used = 0; + rb->rf_records.rl_reserved2 = 0; + rb->rf_records.rl_reserved1 = 0; + /* rl_count determines the memset size and fortify object size. */ rb->rf_records.rl_count = cpu_to_le16(ocfs2_refcount_recs_per_rb(sb)); + memset(rb->rf_records.rl_recs, 0, + le16_to_cpu(rb->rf_records.rl_count) * + sizeof(*rb->rf_records.rl_recs)); } ocfs2_journal_dirty(handle, ref_root_bh); From 7ccd2e6cecd5bb02a5a15f0dd7199d1e81380bda Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:11:41 -0300 Subject: [PATCH 2928/5214] perf tools: Guard remaining test_bit calls from OOB sample CPU auxtrace.c:filter_cpu() and builtin-script.c:filter_cpu() call test_bit(cpu, cpu_bitmap) where cpu_bitmap is declared with MAX_NR_CPUS bits. When the CPU value from a perf.data event is corrupt or absent (e.g. negative or >= MAX_NR_CPUS), test_bit reads out of bounds. Add bounds checks before test_bit(): >= 0 for the int16_t cpu.cpu in auxtrace (which also covers the -1 sentinel), and < MAX_NR_CPUS for both sites. Matches the pattern applied in the previous series for builtin-annotate.c, builtin-diff.c, builtin-report.c, and builtin-sched.c. Fixes: 644e0840ad46 ("perf auxtrace: Add CPU filter support") Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Anton Blanchard Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 2 +- tools/perf/util/auxtrace.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f4aa255fc329..9ac29bdc3cd5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2646,7 +2646,7 @@ static int cleanup_scripting(void) static bool filter_cpu(struct perf_sample *sample) { - if (cpu_list && sample->cpu != (u32)-1) + if (cpu_list && sample->cpu != (u32)-1 && sample->cpu < MAX_NR_CPUS) return !test_bit(sample->cpu, cpu_bitmap); return false; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 5f4aa1701aef..4cd2caf54015 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -372,7 +372,8 @@ static bool filter_cpu(struct perf_session *session, struct perf_cpu cpu) { unsigned long *cpu_bitmap = session->itrace_synth_opts->cpu_bitmap; - return cpu_bitmap && cpu.cpu != -1 && !test_bit(cpu.cpu, cpu_bitmap); + return cpu_bitmap && cpu.cpu >= 0 && cpu.cpu < MAX_NR_CPUS && + !test_bit(cpu.cpu, cpu_bitmap); } static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues, From 1e7921d7227de5da0dfc167943092c823ec7e49b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:14:23 -0300 Subject: [PATCH 2929/5214] perf tools: Add bounds check to cpu__get_node() cpu__get_node() accesses cpunode_map[cpu.cpu] without checking against max_cpu_num, the allocation size of cpunode_map. Callers such as builtin-kmem.c:evsel__process_alloc_event() pass sample->cpu from perf.data events, which may exceed the host's CPU count when analyzing cross-machine recordings. Add a bounds check against max_cpu_num before indexing, returning -1 for out-of-range values. This is a central fix that protects all callers. Fixes: 86895b480a2f ("perf stat: Add --per-node agregation support") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index b1e5c29c6e3e..d3432622b2ad 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -576,6 +576,10 @@ int cpu__get_node(struct perf_cpu cpu) return -1; } + /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ + if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) + return -1; + return cpunode_map[cpu.cpu]; } From fa20c1f8f4e094abe0169d39fce8181bc26d6dab Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:18:05 -0300 Subject: [PATCH 2930/5214] perf sched: Fix thread reference leaks in timehist_get_thread() timehist_get_thread() acquires a thread reference via machine__findnew_thread() and an idle thread reference via get_idle_thread() (which calls thread__get()). Two error paths in the idle_hist block return NULL without releasing these references: - When get_idle_thread() fails, the thread reference leaks. - When thread__priv(idle) returns NULL, both idle and thread leak. Additionally, the idle thread reference acquired on the success path is never released, leaking a reference on every sample when --idle-hist is active. Add thread__put() calls on both error paths and release the idle reference after use on the success path. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 4aa7833cae6e..7bd61028327b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2546,12 +2546,16 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, idle = get_idle_thread(sample->cpu); if (idle == NULL) { pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu); + thread__put(thread); return NULL; } itr = thread__priv(idle); - if (itr == NULL) + if (itr == NULL) { + thread__put(idle); + thread__put(thread); return NULL; + } thread__put(itr->last_thread); itr->last_thread = thread__get(thread); @@ -2559,6 +2563,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, /* copy task callchain when entering to idle */ if (perf_sample__intval(sample, "next_pid") == 0) save_idle_callchain(sched, itr, sample); + + thread__put(idle); } } From 66782cfa0085369e2d8c861042f7c6d43431bdb3 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 4 Jun 2026 11:01:53 -0700 Subject: [PATCH 2931/5214] cxl/pci: Fix the incorrect check of pci_read_config_word() return pci_read_config_word() returns PCIBIOS_* status on error which are positive values. The check should be for non-zero values to indicate error. Fix cxl_set_mem_enable() to check for non-zero return value instead of negative value. While fixing this, also convert the error to negative errno value when returning on error path. Fixes: 34e37b4c432c ("cxl/port: Enable HDM Capability after validating DVSEC Ranges") Reviewed-by: Richard Cheng Reviewed-by: Jonathan Cameron Reviewed-by: Alison Schofield Assisted-by: Claude:claude-opus-4-8 Link: https://patch.msgid.link/20260604180154.1925149-2-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index d1f487b3d809..43885c59a7f2 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -187,8 +187,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) int rc; rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) == val) return 1; @@ -196,8 +196,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) ctrl |= val; rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); return 0; } From 26aa60e0276272ae61b843a05a91748dcb1130f9 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 4 Jun 2026 11:01:54 -0700 Subject: [PATCH 2932/5214] cxl/pci: Convert PCIBIOS errors to errno on DVSEC config accesses PCI config space accessors return positive PCIBIOS_* status codes on failure that are positive integers. Several DVSEC accesses in the CXL core propagated these raw values to callers that test for failure against less than 0. Thus silently misinterpret the return value as success. Convert the positive error values to negative errno values so the checks are correct on error paths. While the chances of a config access failure are low, fix for correctness and to avoid confusion in the future when more DVSEC accesses are added. Fixes: 14d788740774 ("cxl/mem: Consolidate CXL DVSEC Range enumeration in the core") Fixes: ce17ad0d5498 ("cxl: Wait Memory_Info_Valid before access memory related info") Reviewed-by: Richard Cheng Reviewed-by: Jonathan Cameron Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260604180154.1925149-3-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/pci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 43885c59a7f2..e4338fd7e01b 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -89,7 +89,7 @@ static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id) d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); valid = FIELD_GET(PCI_DVSEC_CXL_MEM_INFO_VALID, temp); if (valid) @@ -123,7 +123,7 @@ static int cxl_dvsec_mem_range_active(struct cxl_dev_state *cxlds, int id) rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); active = FIELD_GET(PCI_DVSEC_CXL_MEM_ACTIVE, temp); if (active) @@ -156,7 +156,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds) rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); hdm_count = FIELD_GET(PCI_DVSEC_CXL_HDM_COUNT, cap); for (i = 0; i < hdm_count; i++) { @@ -275,7 +275,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); if (!(cap & PCI_DVSEC_CXL_MEM_CAPABLE)) { dev_dbg(dev, "Not MEM Capable\n"); @@ -299,7 +299,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, */ rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); if (rc) - return rc; + return pcibios_err_to_errno(rc); info->mem_enabled = FIELD_GET(PCI_DVSEC_CXL_MEM_ENABLE, ctrl); if (!info->mem_enabled) @@ -316,14 +316,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size |= temp & PCI_DVSEC_CXL_MEM_SIZE_LOW; if (!size) { @@ -333,14 +333,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base |= temp & PCI_DVSEC_CXL_MEM_BASE_LOW; From 06cb687a5132fcffe624c0070576ab852ac6b568 Mon Sep 17 00:00:00 2001 From: Mirela Rabulea Date: Fri, 22 May 2026 17:31:20 +0300 Subject: [PATCH 2933/5214] media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor() The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL. This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver). Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro: #define v4l2_async_register_subdev_sensor(sd) \ __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal. Fixes: aef69d54755d ("media: v4l: fwnode: Add a convenience function for registering sensors") Cc: stable@vger.kernel.org Suggested-by: Frank Li Link: https://lore.kernel.org/linux-media/20240315073125.275501-2-sakari.ailus@linux.intel.com/ Signed-off-by: Mirela Rabulea Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-fwnode.c | 6 +++--- include/media/v4l2-async.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 77f3298821b5..62a3a452f788 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1256,7 +1256,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev, return 0; } -int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module) { struct v4l2_async_notifier *notifier; int ret; @@ -1282,7 +1282,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (ret < 0) goto out_cleanup; - ret = v4l2_async_register_subdev(sd); + ret = __v4l2_async_register_subdev(sd, module); if (ret < 0) goto out_unregister; @@ -1300,7 +1300,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor); MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index f26c323e9c96..54a2d9620ed5 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module); * An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor(sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) int __must_check -v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module); /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous From 14270179806b876fecefcbf35905e512b56a5867 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Jun 2026 18:16:37 +0300 Subject: [PATCH 2934/5214] pinctrl: renesas: rzg2l: Use raw_spinlock_irqsave() on power source update The rest of the driver uses raw_spin_lock_irqsave() and raw_spin_unlock_irqrestore() for locking. To avoid concurrency issues or deadlocks, use raw_spinlock_irqsave() via the scoped_guard() helper for power source updates as well. Fixes: bbe2277dedbe ("pinctrl: renesas: rzg2l: Add support for selecting power source for {WDT,AWO,ISO}") Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260603151642.4075678-2-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 83c61dcb24b1..be52d47d77ae 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1137,7 +1137,7 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps return pwr_reg; if (pwr_reg == OTHER_POC) { - scoped_guard(raw_spinlock, &pctrl->lock) { + scoped_guard(raw_spinlock_irqsave, &pctrl->lock) { val = readb(pctrl->base + pwr_reg); if (poc_val) val |= mask; From 851334760330dafa68ab8f371e804ea6ad56ff9a Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 28 May 2026 19:38:39 +0800 Subject: [PATCH 2935/5214] RISC-V: KVM: Enhance the logging check for mmu mapping When enabling dirty ring, the dirty bitmap is disable, and the logging check is always false as the RISC-V architecture does not select "NEED_KVM_DIRTY_RING_WITH_BITMAP". Although the dirty log is recorded since the write path already trying to add the dirty log, the logic for logging check is broken and some side effect will occurs. Enhance the logging check for mmu mapping so it can check both the dirty ring and the dirty bitmap. Signed-off-by: Inochi Amaoto Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260528113840.2629186-1-inochiama@gmail.com Signed-off-by: Anup Patel --- arch/riscv/kvm/mmu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index c847b101c73e..38d3eaa1c5e5 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -157,9 +157,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, enum kvm_mr_change change) { /* - * At this point memslot has been committed and there is an - * allocated dirty_bitmap[], dirty pages will be tracked while - * the memory slot is write protected. + * At this point memslot has been committed and dirty pages will be + * tracked while the memory slot is write protected. */ if (change != KVM_MR_DELETE && new->flags & KVM_MEM_LOG_DIRTY_PAGES) { if (kvm_dirty_log_manual_protect_and_init_set(kvm)) @@ -521,8 +520,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, struct vm_area_struct *vma; struct kvm *kvm = vcpu->kvm; struct kvm_mmu_memory_cache *pcache = &vcpu->arch.mmu_page_cache; - bool logging = (memslot->dirty_bitmap && - !(memslot->flags & KVM_MEM_READONLY)) ? true : false; + bool logging = kvm_slot_dirty_track_enabled(memslot) && + !(memslot->flags & KVM_MEM_READONLY); unsigned long vma_pagesize, mmu_seq; struct kvm_gstage gstage; struct page *page; From a9523a7d3b24b3a6b25ec1eb668ee6618cacf05e Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Thu, 21 May 2026 19:17:49 +0900 Subject: [PATCH 2936/5214] ntfs: free volume-wide resources on fill_super failure ntfs_fill_super()'s err_out_now path frees only the volume struct via kfree(vol), leaving several vol-owned allocations behind on every mount failure: - vol->nls_map, loaded by ntfs_init_fs_context() via load_nls_default() (or replaced by an explicit nls= option in ntfs_parse_param()), is never unload_nls()'d. - vol->volume_label, allocated by load_system_files() through ntfs_ucstonls() once the $Volume name attribute has been parsed, is not released by load_system_files()'s own error labels nor by the fill_super() inline cleanup that only runs on d_make_root() failure. Any later failure inside load_system_files() leaks it. - vol->lcn_empty_bits_per_page was kvfree()'d in unl_upcase_iput_tmp_ino_err_out_now without clearing the pointer, so it could not be folded into a single common cleanup. Because the failure paths never call ntfs_volume_free() and never reach the d_make_root() inline cleanup block (it sits above the label and is jumped over by the load_system_files() / kvmalloc failure gotos), these resources accumulate per failed mount attempt with no chance of recovery short of unloading the module. This is a silent leak: the inodes loaded prior to failure remain hashed but generic_shutdown_super() skips evict_inodes() when sb->s_root is unset, so no CHECK_DATA_CORRUPTION warning is emitted either. Move the per-volume frees down to err_out_now and drop the lcn_empty_bits_per_page kvfree() from the upper label so the cleanup is performed exactly once on every failure path. Using unconditional kvfree() / kfree() / unload_nls() is safe because they all accept NULL and the upper labels that previously freed nls_map (the d_make_root() inline cleanup) already clear the pointer. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 9e321cc2febe..7e3561265b47 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2530,8 +2530,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) } /* Error exit code path. */ unl_upcase_iput_tmp_ino_err_out_now: - if (vol->lcn_empty_bits_per_page) - kvfree(vol->lcn_empty_bits_per_page); /* * Decrease the number of upcase users and destroy the global default * upcase table if necessary. @@ -2551,6 +2549,9 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) /* Errors at this stage are irrelevant. */ err_out_now: sb->s_fs_info = NULL; + kvfree(vol->lcn_empty_bits_per_page); + kfree(vol->volume_label); + unload_nls(vol->nls_map); kfree(vol); ntfs_debug("Failed, returning -EINVAL."); lockdep_on(); From 8f4b6e8bda121ae3d4d8e332b789664604d3e9d2 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Thu, 21 May 2026 19:17:51 +0900 Subject: [PATCH 2937/5214] ntfs: only alias volume $UpCase to default on exact match load_and_init_upcase() currently aliases vol->upcase to the global default upcase whenever the shared prefix matches, and then truncates vol->upcase_len to that shorter prefix. The result is correct only by accident: upcase[] accesses in name collation are gated by upcase_len, so the prefix-equality alias produces the same fold output as keeping the volume's own shorter table. Still, prefix equality is not equality: the volume table is logically distinct from the default and should not be replaced by it unless they are byte-for-byte identical. Use memcmp() to compare the complete table in one expression and drop the now-redundant upcase_len rewrite. No user-visible change is expected for compliant volumes whose $UpCase has exactly default_upcase_len entries; shorter volume tables are no longer aliased to the default. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 7e3561265b47..e51573be2182 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1323,7 +1323,6 @@ static bool load_and_init_upcase(struct ntfs_volume *vol) u8 *addr; pgoff_t index, max_index; unsigned int size; - int i, max; ntfs_debug("Entering."); /* Read upcase table and setup vol->upcase and vol->upcase_len. */ @@ -1374,16 +1373,11 @@ static bool load_and_init_upcase(struct ntfs_volume *vol) mutex_unlock(&ntfs_lock); return true; } - max = default_upcase_len; - if (max > vol->upcase_len) - max = vol->upcase_len; - for (i = 0; i < max; i++) - if (vol->upcase[i] != default_upcase[i]) - break; - if (i == max) { + if (default_upcase_len == vol->upcase_len && + !memcmp(vol->upcase, default_upcase, + default_upcase_len * sizeof(*default_upcase))) { kvfree(vol->upcase); vol->upcase = default_upcase; - vol->upcase_len = max; ntfs_nr_upcase_users++; mutex_unlock(&ntfs_lock); ntfs_debug("Volume specified $UpCase matches default. Using default."); From 76bc14c7097ff678b2b5dbfd4fa33b46897d87ce Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 21 May 2026 14:37:03 +0900 Subject: [PATCH 2938/5214] ntfs: skip extent mft records in writeback to prevent deadlock This patch fixes the ABBA deadlock between extent_lock and extent mrec_lock triggered by xfstests generic/113, that occurs since the commit 6994acf33bae ("ntfs: use base mft_no when looking up base inode for extent record"). Path A (inode writeback): VFS writeback -> ntfs_write_inode() -> __ntfs_write_inode() -> mutex_lock(&ni->extent_lock) -> mutex_lock(&tni->mrec_lock) Path B (MFT folio writeback): VFS writeback of $MFT dirty folios -> ntfs_mft_writepages() -> ntfs_write_mft_block() -> ntfs_may_write_mft_record() -> holds one extent mrec_lock from a previous iteration -> tries to acquire another base inode extent_lock By removing all extent_lock and extent mrec_lock acquisition from the MFT folio writeback path, the ABBA lock ordering is eliminated: Path A: __ntfs_write_inode(): extent_lock -> mrec_lock Path B (removed): ntfs_write_mft_block(): mrec_lock -> extent_lock Path B is always redundant for extent records because: 1. mark_mft_record_dirty(ext_ni) does NOT dirty the MFT folio. It only sets NInoDirty(ext_ni) and marks the base VFS inode dirty via __mark_inode_dirty(I_DIRTY_DATASYNC), which triggers Path A. Therefore, normal extent modifications never create a situation where the MFT folio is dirty and Path B is not scheduled. 2. The MFT folio only gets dirtied via ntfs_mft_mark_dirty() inside ntfs_mft_record_alloc(). But all identified callers in attrib.c (ntfs_attr_add, ntfs_attr_record_move_away, ntfs_attr_make_non_resident, ntfs_attr_record_resize) follow through with mark_mft_record_dirty(), which triggers Path A to write the complete record. 3. ntfs_evict_big_inode() calls ntfs_commit_inode() before freeing extent inodes, ensuring all dirty extents are flushed via Path A before the base inode leaves the icache. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/mft.c | 129 ++------------------------------------------------ 1 file changed, 4 insertions(+), 125 deletions(-) diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index a7d10ee41b34..a5019e80951b 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -743,23 +743,6 @@ static int ntfs_test_inode_wb(struct inode *vi, u64 ino, void *data) * * If the mft record is not a FILE record or it is a base mft record, we can * safely write it and return 'true'. - * - * We now know the mft record is an extent mft record. We check if the inode - * corresponding to its base mft record is in icache. If it is not, we cannot - * safely determine the state of the extent inode, so we return 'false'. - * - * We now have the base inode for the extent mft record. We check if it has an - * ntfs inode for the extent mft record attached. If not, it is safe to write - * the extent mft record and we return 'true'. - * - * If the extent inode is attached, we check if it is dirty. If so, we return - * 'false' (letting the standard write_inode path handle it). - * - * If it is not dirty, we attempt to lock the extent mft record. If the lock - * was already taken, it is not safe to write and we return 'false'. - * - * If we manage to obtain the lock we have exclusive access to the extent mft - * record. We set @locked_ni to the now locked ntfs inode and return 'true'. */ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, const struct mft_record *m, struct ntfs_inode **locked_ni, @@ -768,8 +751,7 @@ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, struct super_block *sb = vol->sb; struct inode *mft_vi = vol->mft_ino; struct inode *vi; - struct ntfs_inode *ni, *eni, **extent_nis; - int i; + struct ntfs_inode *ni; struct ntfs_attr na = {0}; ntfs_debug("Entering for inode 0x%llx.", mft_no); @@ -849,100 +831,10 @@ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, mft_no); return true; } - /* - * This is an extent mft record. Check if the inode corresponding to - * its base mft record is in icache and obtain a reference to it if it - * is. - */ - na.mft_no = MREF_LE(m->base_mft_record); - na.state = 0; - ntfs_debug("Mft record 0x%llx is an extent record. Looking for base inode 0x%llx in icache.", - mft_no, na.mft_no); - if (!na.mft_no) { - /* Balance the below iput(). */ - vi = igrab(mft_vi); - WARN_ON(vi != mft_vi); - } else { - vi = find_inode_nowait(sb, na.mft_no, ntfs_test_inode_wb, &na); - if (na.state == NI_BeingDeleted || na.state == NI_BeingCreated) - return false; - } - if (!vi) - return false; - ntfs_debug("Base inode 0x%llx is in icache.", na.mft_no); - /* - * The base inode is in icache. Check if it has the extent inode - * corresponding to this extent mft record attached. - */ - ni = NTFS_I(vi); - mutex_lock(&ni->extent_lock); - if (ni->nr_extents <= 0) { - /* - * The base inode has no attached extent inodes, write this - * extent mft record. - */ - mutex_unlock(&ni->extent_lock); - *ref_vi = vi; - ntfs_debug("Base inode 0x%llx has no attached extent inodes, write the extent record.", - na.mft_no); - return true; - } - /* Iterate over the attached extent inodes. */ - extent_nis = ni->ext.extent_ntfs_inos; - for (eni = NULL, i = 0; i < ni->nr_extents; ++i) { - if (mft_no == extent_nis[i]->mft_no) { - /* - * Found the extent inode corresponding to this extent - * mft record. - */ - eni = extent_nis[i]; - break; - } - } - /* - * If the extent inode was not attached to the base inode, write this - * extent mft record. - */ - if (!eni) { - mutex_unlock(&ni->extent_lock); - *ref_vi = vi; - ntfs_debug("Extent inode 0x%llx is not attached to its base inode 0x%llx, write the extent record.", - mft_no, na.mft_no); - return true; - } - ntfs_debug("Extent inode 0x%llx is attached to its base inode 0x%llx.", - mft_no, na.mft_no); - /* Take a reference to the extent ntfs inode. */ - atomic_inc(&eni->count); - mutex_unlock(&ni->extent_lock); - - /* if extent inode is dirty, write_inode will write it */ - if (NInoDirty(eni)) { - atomic_dec(&eni->count); - *ref_vi = vi; - return false; - } - - /* - * Found the extent inode coresponding to this extent mft record. - * Try to take the mft record lock. - */ - if (unlikely(!mutex_trylock(&eni->mrec_lock))) { - atomic_dec(&eni->count); - *ref_vi = vi; - ntfs_debug("Extent mft record 0x%llx is already locked, do not write it.", - mft_no); - return false; - } - ntfs_debug("Managed to lock extent mft record 0x%llx, write it.", - mft_no); - /* - * The write has to occur while we hold the mft record lock so return - * the locked extent ntfs inode. - */ - *locked_ni = eni; - return true; + ntfs_debug("Mft record 0x%llx is an extent record, skip it.", + mft_no); + return false; } static const char *es = " Leaving inconsistent metadata. Unmount and run chkdsk."; @@ -2791,19 +2683,6 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w unsigned int mft_record_off = 0; s64 vcn_off = vcn; - /* - * Skip $MFT extent mft records and let them being written - * by writeback to avioid deadlocks. the $MFT runlist - * lock must be taken before $MFT extent mrec_lock is taken. - */ - if (tni && tni->nr_extents < 0 && - tni->ext.base_ntfs_ino == NTFS_I(vol->mft_ino)) { - mutex_unlock(&tni->mrec_lock); - atomic_dec(&tni->count); - iput(vol->mft_ino); - continue; - } - /* * The record should be written. If a locked ntfs * inode was returned, add it to the array of locked From c05132077df57a384919f61d7f8a8e76d748a6d4 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Fri, 22 May 2026 23:20:48 +0900 Subject: [PATCH 2939/5214] ntfs: avoid heap allocation for free-cluster readahead state get_nr_free_clusters() allocates a temporary file_ra_state before it publishes the precomputed free cluster count, sets NVolFreeClusterKnown(), and wakes vol->free_waitq. If that allocation fails, the worker returns without setting the flag or waking waiters, so callers waiting for the free count can block indefinitely. The readahead state is only used synchronously while scanning the bitmap. Keep it on the stack and pass it by address to the readahead helper. This eliminates the early allocation failure path instead of adding a special case that publishes a conservative count and wakes the waitqueue. Zero-initialize the on-stack state because file_ra_state_init() only sets ra_pages and prev_pos. Apply the same treatment to __get_nr_free_mft_records(), which scans the MFT bitmap with the same short-lived readahead state. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index e51573be2182..e30fcce628c2 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1948,7 +1948,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) struct address_space *mapping = vol->lcnbmp_ino->i_mapping; struct folio *folio; pgoff_t index, max_index; - struct file_ra_state *ra; + struct file_ra_state ra = { 0 }; ntfs_debug("Entering."); /* Serialize accesses to the cluster bitmap. */ @@ -1956,11 +1956,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) if (NVolFreeClusterKnown(vol)) return atomic64_read(&vol->free_clusters); - ra = kzalloc(sizeof(*ra), GFP_NOFS); - if (!ra) - return 0; - - file_ra_state_init(ra, mapping); + file_ra_state_init(&ra, mapping); /* * Convert the number of bits into bytes rounded up, then convert into @@ -1979,7 +1975,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + folio = ntfs_get_locked_folio(mapping, index, max_index, &ra); /* Ignore pages which errored synchronously. */ if (IS_ERR(folio)) { @@ -2018,7 +2014,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol) else atomic64_set(&vol->free_clusters, nr_free); - kfree(ra); NVolSetFreeClusterKnown(vol); wake_up_all(&vol->free_waitq); ntfs_debug("Exiting."); @@ -2073,15 +2068,11 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, struct address_space *mapping = vol->mftbmp_ino->i_mapping; struct folio *folio; pgoff_t index; - struct file_ra_state *ra; + struct file_ra_state ra = { 0 }; ntfs_debug("Entering."); - ra = kzalloc(sizeof(*ra), GFP_NOFS); - if (!ra) - return 0; - - file_ra_state_init(ra, mapping); + file_ra_state_init(&ra, mapping); /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */ ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.", @@ -2093,7 +2084,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, * Get folio from page cache, getting it from backing store * if necessary, and increment the use count. */ - folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + folio = ntfs_get_locked_folio(mapping, index, max_index, &ra); /* Ignore pages which errored synchronously. */ if (IS_ERR(folio)) { @@ -2125,7 +2116,6 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, else atomic64_set(&vol->free_mft_records, nr_free); - kfree(ra); ntfs_debug("Exiting."); return nr_free; } From 14bc34fe948523dc2b0174691f9af9e74eb4f3fd Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:20 +0900 Subject: [PATCH 2940/5214] ntfs: validate index block header more strictly Modify ntfs_index_block_inconsisent() to perform stricter validation of INDEX_HEADER geometry in INDX blocks, and update ntfs_lookup_inode_by_name() to use that function to validate INDX blocks. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/dir.c | 38 ++++-------------- fs/ntfs/index.c | 101 ++++++++++++++++++++++++++++++++++-------------- fs/ntfs/index.h | 3 ++ 3 files changed, 81 insertions(+), 61 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 20f5c7074bdd..6745a0e6e3e7 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -342,43 +342,19 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, dir_ni->mft_no); goto unm_err_out; } - /* Catch multi sector transfer fixup errors. */ - if (unlikely(!ntfs_is_indx_record(ia->magic))) { - ntfs_error(sb, - "Directory index record with vcn 0x%llx is corrupt. Corrupt inode 0x%llx. Run chkdsk.", - vcn, dir_ni->mft_no); - goto unm_err_out; - } - if (le64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_error(sb, - "Actual VCN (0x%llx) of index buffer is different from expected VCN (0x%llx). Directory inode 0x%llx is corrupt or driver bug.", - le64_to_cpu(ia->index_block_vcn), - vcn, dir_ni->mft_no); - goto unm_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != - dir_ni->itype.index.block_size) { - ntfs_error(sb, - "Index buffer (VCN 0x%llx) of directory inode 0x%llx has a size (%u) differing from the directory specified size (%u). Directory inode is corrupt or driver bug.", - vcn, dir_ni->mft_no, - le32_to_cpu(ia->index.allocated_size) + 0x18, - dir_ni->itype.index.block_size); - goto unm_err_out; - } index_end = (u8 *)ia + dir_ni->itype.index.block_size; if (index_end > kaddr + PAGE_SIZE) { ntfs_error(sb, - "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.", - vcn, dir_ni->mft_no); + "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.", + vcn, dir_ni->mft_no); goto unm_err_out; } + err = ntfs_index_block_inconsistent(vol, ia, + dir_ni->itype.index.block_size, + vcn, dir_ni->mft_no); + if (err) + goto unm_err_out; index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8 *)ia + dir_ni->itype.index.block_size) { - ntfs_error(sb, - "Size of index buffer (VCN 0x%llx) of directory inode 0x%llx exceeds maximum size.", - vcn, dir_ni->mft_no); - goto unm_err_out; - } /* The first index entry. */ ie = (struct index_entry *)((u8 *)&ia->index + le32_to_cpu(ia->index.entries_offset)); diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 146e011c1a41..9713b082b03d 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -303,6 +303,55 @@ static int ntfs_ie_end(struct index_entry *ie) return ie->flags & INDEX_ENTRY_END || !ie->length; } +static int ntfs_index_header_inconsistent(struct ntfs_volume *vol, + const struct index_header *ih, + u32 bytes_available, u64 inum) +{ + u32 entries_offset, index_length, allocated_size; + + if (bytes_available < sizeof(struct index_header)) { + ntfs_error(vol->sb, + "index block in inode %llu is smaller than an index header.", + (unsigned long long)inum); + return -EIO; + } + + entries_offset = le32_to_cpu(ih->entries_offset); + index_length = le32_to_cpu(ih->index_length); + allocated_size = le32_to_cpu(ih->allocated_size); + + if (entries_offset < sizeof(struct index_header) || + entries_offset > bytes_available) { + ntfs_error(vol->sb, + "Invalid index entry offset in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (index_length <= entries_offset) { + ntfs_error(vol->sb, + "No space for index entries in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (allocated_size < index_length) { + ntfs_error(vol->sb, + "Index entries overflow in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (allocated_size > bytes_available || index_length > bytes_available) { + ntfs_error(vol->sb, + "Index entries in inode %llu exceed the available buffer.", + (unsigned long long)inum); + return -EIO; + } + + return 0; +} + /* * Find the last entry in the index block */ @@ -437,7 +486,7 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) * The size of block is assumed to have been checked to be what is * defined in the index root. * - * Returns 0 if no error was found -1 otherwise (with errno unchanged) + * Returns 0 if no error was found, -EIO otherwise * * |<--->| offsetof(struct index_block, index) * | |<--->| sizeof(struct index_header) @@ -452,21 +501,20 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) * * size(struct index_header) <= ent_offset < ind_length <= alloc_size < bk_size */ -static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx, - struct index_block *ib, s64 vcn) +int ntfs_index_block_inconsistent(struct ntfs_volume *vol, + const struct index_block *ib, + u32 block_size, s64 vcn, u64 inum) { u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) + offsetof(struct index_block, index); - struct super_block *sb = icx->idx_ni->vol->sb; - unsigned long long inum = icx->idx_ni->mft_no; + struct super_block *sb = vol->sb; ntfs_debug("Entering\n"); if (!ntfs_is_indx_record(ib->magic)) { - ntfs_error(sb, "Corrupt index block signature: vcn %lld inode %llu\n", - vcn, (unsigned long long)icx->idx_ni->mft_no); - return -1; + vcn, (unsigned long long)inum); + return -EIO; } if (le64_to_cpu(ib->index_block_vcn) != vcn) { @@ -474,30 +522,21 @@ static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx, "Corrupt index block: s64 (%lld) is different from expected s64 (%lld) in inode %llu\n", (long long)le64_to_cpu(ib->index_block_vcn), vcn, inum); - return -1; + return -EIO; } - if (ib_size != icx->block_size) { + if (ib_size != block_size) { ntfs_error(sb, - "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n", - vcn, inum, ib_size, icx->block_size); - return -1; + "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n", + vcn, inum, ib_size, block_size); + return -EIO; } - if (le32_to_cpu(ib->index.entries_offset) < sizeof(struct index_header)) { - ntfs_error(sb, "Invalid index entry offset in inode %lld\n", inum); - return -1; - } - if (le32_to_cpu(ib->index.index_length) <= - le32_to_cpu(ib->index.entries_offset)) { - ntfs_error(sb, "No space for index entries in inode %lld\n", inum); - return -1; - } - if (le32_to_cpu(ib->index.allocated_size) < - le32_to_cpu(ib->index.index_length)) { - ntfs_error(sb, "Index entries overflow in inode %lld\n", inum); - return -1; - } + if (ntfs_index_header_inconsistent(vol, &ib->index, + block_size - + offsetof(struct index_block, index), + inum)) + return -EIO; return 0; } @@ -665,12 +704,14 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl else ntfs_error(icx->idx_ni->vol->sb, "Failed to read full index block at %lld\n", pos); - return -1; + return -EIO; } post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size); - if (ntfs_index_block_inconsistent(icx, dst, vcn)) - return -1; + if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst, + icx->block_size, vcn, + icx->idx_ni->mft_no)) + return -EIO; return 0; } diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index e68d6fabaf9f..3451ec8a1c4e 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -89,6 +89,9 @@ struct ntfs_index_context { bool sync_write; }; +int ntfs_index_block_inconsistent(struct ntfs_volume *vol, + const struct index_block *ib, + u32 block_size, s64 vcn, u64 inum); int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol, const struct index_entry *ie, __le32 collation_rule, u64 inum); struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name, From 8b97b302f553a480fb76d2afd53cd6c0635a9dcd Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:21 +0900 Subject: [PATCH 2941/5214] ntfs: centalize $INDEX_ROOT header validation Add a dedicated helper to perform stricter validation of $INDEX_ROOT and use it for both directory inodes and named index inodes. This keeps the root size and header geometry checks consistent across both read paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/index.c | 18 ++++++++++++++++++ fs/ntfs/index.h | 3 +++ fs/ntfs/inode.c | 11 ++--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 9713b082b03d..97c0e7d6a580 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -541,6 +541,24 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, return 0; } +int ntfs_index_root_inconsistent(struct ntfs_volume *vol, + const struct attr_record *a, + const struct index_root *ir, u64 inum) +{ + u32 value_length = le32_to_cpu(a->data.resident.value_length); + + if (value_length < offsetof(struct index_root, index)) { + ntfs_error(vol->sb, "$INDEX_ROOT in inode %llu is too small.", + (unsigned long long)inum); + return -EIO; + } + + return ntfs_index_header_inconsistent(vol, &ir->index, + value_length - + offsetof(struct index_root, index), + inum); +} + static struct index_root *ntfs_ir_lookup(struct ntfs_inode *ni, __le16 *name, u32 name_len, struct ntfs_attr_search_ctx **ctx) { diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index 3451ec8a1c4e..cad78568d8b3 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -89,6 +89,9 @@ struct ntfs_index_context { bool sync_write; }; +int ntfs_index_root_inconsistent(struct ntfs_volume *vol, + const struct attr_record *a, + const struct index_root *ir, u64 inum); int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, u32 block_size, s64 vcn, u64 inum); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 360bebd1ee3f..63ee7acff4fc 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -890,7 +890,6 @@ static int ntfs_read_locked_inode(struct inode *vi) */ if (S_ISDIR(vi->i_mode)) { struct index_root *ir; - u8 *ir_end, *index_end; view_index_meta: /* It is a directory, find index root attribute. */ @@ -940,10 +939,7 @@ static int ntfs_read_locked_inode(struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); - index_end = (u8 *)&ir->index + - le32_to_cpu(ir->index.index_length); - if (index_end > ir_end) { + if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) { ntfs_error(vi->i_sb, "Directory index is corrupt."); goto unm_err_out; } @@ -1483,7 +1479,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) struct attr_record *a; struct ntfs_attr_search_ctx *ctx; struct index_root *ir; - u8 *ir_end, *index_end; int err = 0; ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); @@ -1534,9 +1529,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); - index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length); - if (index_end > ir_end) { + if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) { ntfs_error(vi->i_sb, "Index is corrupt."); goto unm_err_out; } From 2221b691d7b2e17f08153f95848dacaa5d87e21d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:22 +0900 Subject: [PATCH 2942/5214] ntfs: validate index entries on reading Validate index entries immediately after reading an index root or index block from disk. This eliminates repeated checks in lookup and readdir, and reduce the risk of missing checks in those paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/dir.c | 28 ++--------------- fs/ntfs/index.c | 84 +++++++++++++++++++++++++++---------------------- fs/ntfs/index.h | 8 +++-- fs/ntfs/inode.c | 8 +++-- 4 files changed, 60 insertions(+), 68 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 6745a0e6e3e7..4b6bd5f30c65 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -135,10 +135,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, /* Key length should not be zero if it is not last entry. */ if (!ie->key_length) goto dir_err_out; - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, - dir_ni->mft_no)) - goto dir_err_out; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. @@ -351,7 +347,8 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, } err = ntfs_index_block_inconsistent(vol, ia, dir_ni->itype.index.block_size, - vcn, dir_ni->mft_no); + vcn, COLLATION_FILE_NAME, + dir_ni->mft_no); if (err) goto unm_err_out; index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); @@ -364,15 +361,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, * reach the last entry. */ for (;; ie = (struct index_entry *)((u8 *)ie + le16_to_cpu(ie->length))) { - /* Bounds checks. */ - if ((u8 *)ie < (u8 *)ia || - (u8 *)ie + sizeof(struct index_entry_header) > index_end || - (u8 *)ie + sizeof(struct index_entry_header) + le16_to_cpu(ie->key_length) > - index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) { - ntfs_error(sb, "Index entry out of bounds in directory inode 0x%llx.", - dir_ni->mft_no); - goto unm_err_out; - } /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. @@ -382,10 +370,6 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, /* Key length should not be zero if it is not last entry. */ if (!ie->key_length) goto unm_err_out; - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, - dir_ni->mft_no)) - goto unm_err_out; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. @@ -868,6 +852,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) ictx->vcn_size_bits = vol->cluster_size_bits; else ictx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + ictx->cr = ir->collation_rule; /* The first index entry. */ next = (struct index_entry *)((u8 *)&ir->index + @@ -905,13 +890,6 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) if (!next) break; nextdir: - /* Check the consistency of an index entry */ - if (ntfs_index_entry_inconsistent(ictx, vol, next, COLLATION_FILE_NAME, - ndir->mft_no)) { - err = -EIO; - goto out; - } - if (ie_pos < actor->pos) { ie_pos += le16_to_cpu(next->length); continue; diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 97c0e7d6a580..00e17637f771 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -28,41 +28,10 @@ * length must have been checked beforehand to not overflow from the * index record. */ -int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, - struct ntfs_volume *vol, const struct index_entry *ie, - __le32 collation_rule, u64 inum) +static int ntfs_index_entry_inconsistent(const struct ntfs_volume *vol, + const struct index_entry *ie, + __le32 collation_rule, u64 inum) { - if (icx) { - struct index_header *ih; - u8 *ie_start, *ie_end; - - if (icx->is_in_root) - ih = &icx->ir->index; - else - ih = &icx->ib->index; - - if ((le32_to_cpu(ih->index_length) > le32_to_cpu(ih->allocated_size)) || - (le32_to_cpu(ih->index_length) > icx->block_size)) { - ntfs_error(vol->sb, "%s Index entry(0x%p)'s length is too big.", - icx->is_in_root ? "Index root" : "Index block", - (u8 *)icx->entry); - return -EINVAL; - } - - ie_start = (u8 *)ih + le32_to_cpu(ih->entries_offset); - ie_end = (u8 *)ih + le32_to_cpu(ih->index_length); - - if (ie_start > (u8 *)ie || - ie_end <= (u8 *)ie + le16_to_cpu(ie->length) || - le16_to_cpu(ie->length) > le32_to_cpu(ih->allocated_size) || - le16_to_cpu(ie->length) > icx->block_size) { - ntfs_error(vol->sb, "Index entry(0x%p) is out of range from %s", - (u8 *)icx->entry, - icx->is_in_root ? "index root" : "index block"); - return -EIO; - } - } - if (ie->key_length && ((le16_to_cpu(ie->key_length) + offsetof(struct index_entry, key)) > le16_to_cpu(ie->length))) { @@ -352,6 +321,44 @@ static int ntfs_index_header_inconsistent(struct ntfs_volume *vol, return 0; } +int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol, + const struct index_header *ih, + __le32 collation_rule, u64 inum) +{ + struct index_entry *ie; + u8 *index_end = (u8 *)ih + le32_to_cpu(ih->index_length); + + for (ie = ntfs_ie_get_first((struct index_header *)ih); + ; ie = ntfs_ie_get_next(ie)) { + if ((u8 *)ie + sizeof(struct index_entry_header) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + ntfs_error(vol->sb, + "Index entry out of bounds in inode %llu.", + (unsigned long long)inum); + return -EIO; + } + + if (le16_to_cpu(ie->length) < sizeof(struct index_entry_header)) { + ntfs_error(vol->sb, + "Index etnry too small in inode %llu.", + inum); + return -EIO; + } + + if (ntfs_ie_end(ie)) + break; + + if (!ie->key_length) + return -EIO; + + if (ntfs_index_entry_inconsistent(vol, ie, + collation_rule, inum)) + return -EIO; + } + + return 0; +} + /* * Find the last entry in the index block */ @@ -503,7 +510,8 @@ static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) */ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, - u32 block_size, s64 vcn, u64 inum) + u32 block_size, s64 vcn, __le32 cr, + u64 inum) { u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) + offsetof(struct index_block, index); @@ -537,7 +545,8 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol, offsetof(struct index_block, index), inum)) return -EIO; - + if (ntfs_index_entries_inconsistent(vol, &ib->index, cr, inum)) + return -EIO; return 0; } @@ -727,10 +736,9 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size); if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst, - icx->block_size, vcn, + icx->block_size, vcn, icx->cr, icx->idx_ni->mft_no)) return -EIO; - return 0; } diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h index cad78568d8b3..9a03f53bba47 100644 --- a/fs/ntfs/index.h +++ b/fs/ntfs/index.h @@ -94,9 +94,11 @@ int ntfs_index_root_inconsistent(struct ntfs_volume *vol, const struct index_root *ir, u64 inum); int ntfs_index_block_inconsistent(struct ntfs_volume *vol, const struct index_block *ib, - u32 block_size, s64 vcn, u64 inum); -int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol, - const struct index_entry *ie, __le32 collation_rule, u64 inum); + u32 block_size, s64 vcn, + __le32 cr, u64 inum); +int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol, + const struct index_header *ih, + __le32 collation_rule, u64 inum); struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name, u32 name_len); void ntfs_index_ctx_put(struct ntfs_index_context *ictx); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 63ee7acff4fc..9717fb5b4709 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -939,7 +939,9 @@ static int ntfs_read_locked_inode(struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) { + if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no) || + ntfs_index_entries_inconsistent(ni->vol, &ir->index, + ir->collation_rule, ni->mft_no)) { ntfs_error(vi->i_sb, "Directory index is corrupt."); goto unm_err_out; } @@ -1529,7 +1531,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) } ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); - if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) { + if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no) || + ntfs_index_entries_inconsistent(vol, &ir->index, + ir->collation_rule, ni->mft_no)) { ntfs_error(vi->i_sb, "Index is corrupt."); goto unm_err_out; } From 937282f7d15b593d0be765fa2ced164130ec87f7 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sat, 23 May 2026 13:14:23 +0900 Subject: [PATCH 2943/5214] ntfs: add bounds check before accessing EA entries in ntfs_ea_lookup and ntfs_listxattr, this verifies that there is enough space in the EA entry before accessing the next_entry_offset field of the EA entry. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/ea.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c index c4a4a3e3e599..0cd192752b7c 100644 --- a/fs/ntfs/ea.c +++ b/fs/ntfs/ea.c @@ -53,11 +53,11 @@ static int ntfs_ea_lookup(char *ea_buf, s64 ea_buf_size, const char *name, loff_t offset, p_ea_size; unsigned int next; - if (ea_buf_size < sizeof(struct ea_attr)) - goto out; - offset = 0; do { + if (ea_buf_size - offset < sizeof(struct ea_attr)) + break; + p_ea = (const struct ea_attr *)&ea_buf[offset]; next = le32_to_cpu(p_ea->next_entry_offset); p_ea_size = next ? next : (ea_buf_size - offset); @@ -479,13 +479,13 @@ ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) if (ea_info_qsize > ea_buf_size || ea_info_qsize == 0) goto out; - if (ea_info_qsize < sizeof(struct ea_attr)) { - err = -EIO; - goto out; - } - offset = 0; do { + if (ea_info_qsize - offset < sizeof(struct ea_attr)) { + err = -EIO; + goto out; + } + p_ea = (const struct ea_attr *)&ea_buf[offset]; next = le32_to_cpu(p_ea->next_entry_offset); ea_size = next ? next : (ea_info_qsize - offset); From 0283841f0c1efb25682e7ba061135758228005c1 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 11 May 2026 11:50:33 +0200 Subject: [PATCH 2944/5214] ntfs: use str_plural in ntfs_attr_make_non_resident Replace the manual ternary "s" pluralization with str_plural() to simplify the code. Signed-off-by: Thorsten Blum Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 421c6cdcbb53..9d676375f25c 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -16,6 +16,7 @@ * Copyright (c) 2010 Erik Larsson */ +#include #include #include @@ -1855,7 +1856,7 @@ int ntfs_attr_make_non_resident(struct ntfs_inode *ni, const u32 data_size) if (IS_ERR(rl)) { err = PTR_ERR(rl); ntfs_debug("Failed to allocate cluster%s, error code %i.", - ntfs_bytes_to_cluster(vol, new_size) > 1 ? "s" : "", + str_plural(ntfs_bytes_to_cluster(vol, new_size)), err); goto folio_err_out; } From cbe287fd65f1a51eb1720e93ceca7ee8d8b011bc Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sun, 17 May 2026 12:44:47 +0900 Subject: [PATCH 2945/5214] ntfs: remove unsupported quota handling The ntfs driver does not implement quota accounting. It creates new inodes with the NTFS 1.2 $STANDARD_INFORMATION layout and does not maintain the NTFS 3.x owner_id/quota_charged fields or the $Quota usage records that Windows would need for meaningful quota accounting. The only runtime quota path left in the driver is the remount-rw code that tries to mark $Quota/$Q out of date, plus the mount-time code that loads $Quota and its $Q index solely to support that marker. Since the driver does not maintain the per-file quota metadata, setting QUOTA_FLAG_OUT_OF_DATE does not make the quota state meaningful, and failures in this unsupported path can unnecessarily block remount-rw or force a mount read-only. Remove the quota marker, the $Quota/$Q loading state, and the unused quota volume flag. Keep the on-disk quota layout definitions in layout.h so the documented NTFS structures remain available. Suggested-by: Hyunchul Lee Link: https://lore.kernel.org/all/CANFS6bYTzioqZjYt=51Kb9RdR3MKXaez_fh_WCLoym093VxFmg@mail.gmail.com/ Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/Makefile | 2 +- fs/ntfs/quota.c | 95 ----------------------------------------- fs/ntfs/quota.h | 15 ------- fs/ntfs/super.c | 109 +---------------------------------------------- fs/ntfs/volume.h | 7 --- 5 files changed, 2 insertions(+), 226 deletions(-) delete mode 100644 fs/ntfs/quota.c delete mode 100644 fs/ntfs/quota.h diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 0ce4d9a9388a..e120c2e69862 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o ntfs-y := aops.o attrib.o collate.o dir.o file.o index.o inode.o \ mft.o mst.o namei.o runlist.o super.o unistr.o attrlist.o ea.o \ upcase.o bitmap.o lcnalloc.o logfile.o reparse.o compress.o \ - iomap.o debug.o sysctl.o quota.o object_id.o bdev-io.o + iomap.o debug.o sysctl.o object_id.o bdev-io.o ccflags-$(CONFIG_NTFS_DEBUG) += -DDEBUG diff --git a/fs/ntfs/quota.c b/fs/ntfs/quota.c deleted file mode 100644 index b443243b58fb..000000000000 --- a/fs/ntfs/quota.c +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * NTFS kernel quota ($Quota) handling. - * - * Copyright (c) 2004 Anton Altaparmakov - */ - -#include "index.h" -#include "quota.h" -#include "debug.h" -#include "ntfs.h" - -/* - * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume - * @vol: ntfs volume on which to mark the quotas out of date - * - * Mark the quotas out of date on the ntfs volume @vol and return 'true' on - * success and 'false' on error. - */ -bool ntfs_mark_quotas_out_of_date(struct ntfs_volume *vol) -{ - struct ntfs_index_context *ictx; - struct quota_control_entry *qce; - const __le32 qid = QUOTA_DEFAULTS_ID; - int err; - - ntfs_debug("Entering."); - if (NVolQuotaOutOfDate(vol)) - goto done; - if (!vol->quota_ino || !vol->quota_q_ino) { - ntfs_error(vol->sb, "Quota inodes are not open."); - return false; - } - inode_lock(vol->quota_q_ino); - ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino), I30, 4); - if (!ictx) { - ntfs_error(vol->sb, "Failed to get index context."); - goto err_out; - } - err = ntfs_index_lookup(&qid, sizeof(qid), ictx); - if (err) { - if (err == -ENOENT) - ntfs_error(vol->sb, "Quota defaults entry is not present."); - else - ntfs_error(vol->sb, "Lookup of quota defaults entry failed."); - goto err_out; - } - if (ictx->data_len < offsetof(struct quota_control_entry, sid)) { - ntfs_error(vol->sb, "Quota defaults entry size is invalid. Run chkdsk."); - goto err_out; - } - qce = (struct quota_control_entry *)ictx->data; - if (le32_to_cpu(qce->version) != QUOTA_VERSION) { - ntfs_error(vol->sb, - "Quota defaults entry version 0x%x is not supported.", - le32_to_cpu(qce->version)); - goto err_out; - } - ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags)); - /* If quotas are already marked out of date, no need to do anything. */ - if (qce->flags & QUOTA_FLAG_OUT_OF_DATE) - goto set_done; - /* - * If quota tracking is neither requested, nor enabled and there are no - * pending deletes, no need to mark the quotas out of date. - */ - if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED | - QUOTA_FLAG_TRACKING_REQUESTED | - QUOTA_FLAG_PENDING_DELETES))) - goto set_done; - /* - * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date. - * This is verified on WinXP to be sufficient to cause windows to - * rescan the volume on boot and update all quota entries. - */ - qce->flags |= QUOTA_FLAG_OUT_OF_DATE; - /* Ensure the modified flags are written to disk. */ - ntfs_index_entry_mark_dirty(ictx); -set_done: - ntfs_index_ctx_put(ictx); - inode_unlock(vol->quota_q_ino); - /* - * We set the flag so we do not try to mark the quotas out of date - * again on remount. - */ - NVolSetQuotaOutOfDate(vol); -done: - ntfs_debug("Done."); - return true; -err_out: - if (ictx) - ntfs_index_ctx_put(ictx); - inode_unlock(vol->quota_q_ino); - return false; -} diff --git a/fs/ntfs/quota.h b/fs/ntfs/quota.h deleted file mode 100644 index 4b7322661a32..000000000000 --- a/fs/ntfs/quota.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Defines for NTFS kernel quota ($Quota) handling. - * - * Copyright (c) 2004 Anton Altaparmakov - */ - -#ifndef _LINUX_NTFS_QUOTA_H -#define _LINUX_NTFS_QUOTA_H - -#include "volume.h" - -bool ntfs_mark_quotas_out_of_date(struct ntfs_volume *vol); - -#endif /* _LINUX_NTFS_QUOTA_H */ diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index e30fcce628c2..312acb41c2ed 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -17,7 +17,6 @@ #include "sysctl.h" #include "logfile.h" -#include "quota.h" #include "index.h" #include "ntfs.h" #include "ea.h" @@ -240,8 +239,7 @@ static int ntfs_reconfigure(struct fs_context *fc) * flags are set. Also, empty the logfile journal as it would become * stale as soon as something is written to the volume and mark the * volume dirty so that chkdsk is run if the volume is not umounted - * cleanly. Finally, mark the quotas out of date so Windows rescans - * the volume on boot and updates them. + * cleanly. * * When remounting read-only, mark the volume clean if no volume errors * have occurred. @@ -274,12 +272,6 @@ static int ntfs_reconfigure(struct fs_context *fc) NVolSetErrors(vol); return -EROFS; } - if (!ntfs_mark_quotas_out_of_date(vol)) { - ntfs_error(sb, "Failed to mark quotas out of date%s", - es); - NVolSetErrors(vol); - return -EROFS; - } } else if (!sb_rdonly(sb) && (fc->sb_flags & SB_RDONLY)) { /* Remounting read-only. */ if (!NVolErrors(vol)) { @@ -1175,73 +1167,6 @@ static int check_windows_hibernation_status(struct ntfs_volume *vol) return ret; } -/* - * load_and_init_quota - load and setup the quota file for a volume if present - * @vol: ntfs super block describing device whose quota file to load - * - * Return 'true' on success or 'false' on error. If $Quota is not present, we - * leave vol->quota_ino as NULL and return success. - */ -static bool load_and_init_quota(struct ntfs_volume *vol) -{ - static const __le16 Quota[7] = { cpu_to_le16('$'), - cpu_to_le16('Q'), cpu_to_le16('u'), - cpu_to_le16('o'), cpu_to_le16('t'), - cpu_to_le16('a'), 0 }; - static __le16 Q[3] = { cpu_to_le16('$'), - cpu_to_le16('Q'), 0 }; - struct ntfs_name *name = NULL; - u64 mref; - struct inode *tmp_ino; - - ntfs_debug("Entering."); - /* - * Find the inode number for the quota file by looking up the filename - * $Quota in the extended system files directory $Extend. - */ - inode_lock(vol->extend_ino); - mref = ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), Quota, 6, - &name); - inode_unlock(vol->extend_ino); - kfree(name); - if (IS_ERR_MREF(mref)) { - /* - * If the file does not exist, quotas are disabled and have - * never been enabled on this volume, just return success. - */ - if (MREF_ERR(mref) == -ENOENT) { - ntfs_debug("$Quota not present. Volume does not have quotas enabled."); - /* - * No need to try to set quotas out of date if they are - * not enabled. - */ - NVolSetQuotaOutOfDate(vol); - return true; - } - /* A real error occurred. */ - ntfs_error(vol->sb, "Failed to find inode number for $Quota."); - return false; - } - /* Get the inode. */ - tmp_ino = ntfs_iget(vol->sb, MREF(mref)); - if (IS_ERR(tmp_ino)) { - if (!IS_ERR(tmp_ino)) - iput(tmp_ino); - ntfs_error(vol->sb, "Failed to load $Quota."); - return false; - } - vol->quota_ino = tmp_ino; - /* Get the $Q index allocation attribute. */ - tmp_ino = ntfs_index_iget(vol->quota_ino, Q, 2); - if (IS_ERR(tmp_ino)) { - ntfs_error(vol->sb, "Failed to load $Quota/$Q index."); - return false; - } - vol->quota_q_ino = tmp_ino; - ntfs_debug("Done."); - return true; -} - /* * load_and_init_attrdef - load the attribute definitions table for a volume * @vol: ntfs super block describing device whose attrdef to load @@ -1653,18 +1578,6 @@ static bool load_system_files(struct ntfs_volume *vol) ntfs_error(sb, "Failed to load $Extend."); goto iput_sec_err_out; } - /* Find the quota file, load it if present, and set it up. */ - if (!load_and_init_quota(vol) && - vol->on_errors == ON_ERRORS_REMOUNT_RO) { - static const char *es1 = "Failed to load $Quota"; - static const char *es2 = ". Run chkdsk."; - - sb->s_flags |= SB_RDONLY; - ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); - /* This will prevent a read-write remount. */ - NVolSetErrors(vol); - } - return true; iput_sec_err_out: @@ -1762,10 +1675,6 @@ static void ntfs_put_super(struct super_block *sb) /* NTFS 3.0+ specific. */ if (vol->major_ver >= 3) { - if (vol->quota_q_ino) - ntfs_commit_inode(vol->quota_q_ino); - if (vol->quota_ino) - ntfs_commit_inode(vol->quota_ino); if (vol->extend_ino) ntfs_commit_inode(vol->extend_ino); if (vol->secure_ino) @@ -1814,14 +1723,6 @@ static void ntfs_put_super(struct super_block *sb) /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >= 3) { - if (vol->quota_q_ino) { - iput(vol->quota_q_ino); - vol->quota_q_ino = NULL; - } - if (vol->quota_ino) { - iput(vol->quota_ino); - vol->quota_ino = NULL; - } if (vol->extend_ino) { iput(vol->extend_ino); vol->extend_ino = NULL; @@ -2460,14 +2361,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) vol->vol_ino = NULL; /* NTFS 3.0+ specific clean up. */ if (vol->major_ver >= 3) { - if (vol->quota_q_ino) { - iput(vol->quota_q_ino); - vol->quota_q_ino = NULL; - } - if (vol->quota_ino) { - iput(vol->quota_ino); - vol->quota_ino = NULL; - } if (vol->extend_ino) { iput(vol->extend_ino); vol->extend_ino = NULL; diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index af41427ec622..e13e1423b2a9 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -76,8 +76,6 @@ * @root_ino: The VFS inode of the root directory. * @secure_ino: The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL). * @extend_ino: The VFS inode of $Extend (NTFS3.0+ only, otherwise NULL). - * @quota_ino: The VFS inode of $Quota. - * @quota_q_ino: Attribute inode for $Quota/$Q. * @nls_map: NLS (National Language Support) table. * @nls_utf8: NLS table for UTF-8. * @free_waitq: Wait queue for threads waiting for free clusters or MFT records. @@ -141,8 +139,6 @@ struct ntfs_volume { struct inode *root_ino; struct inode *secure_ino; struct inode *extend_ino; - struct inode *quota_ino; - struct inode *quota_q_ino; struct nls_table *nls_map; bool nls_utf8; wait_queue_head_t free_waitq; @@ -165,7 +161,6 @@ struct ntfs_volume { * Otherwise be case insensitive but still * create file names in POSIX namespace. * NV_LogFileEmpty LogFile journal is empty. - * NV_QuotaOutOfDate Quota is out of date. * NV_UsnJrnlStamped UsnJrnl has been stamped. * NV_ReadOnly Volume is mounted read-only. * NV_Compression Volume supports compression. @@ -186,7 +181,6 @@ enum { NV_ShowSystemFiles, NV_CaseSensitive, NV_LogFileEmpty, - NV_QuotaOutOfDate, NV_UsnJrnlStamped, NV_ReadOnly, NV_Compression, @@ -223,7 +217,6 @@ DEFINE_NVOL_BIT_OPS(Errors) DEFINE_NVOL_BIT_OPS(ShowSystemFiles) DEFINE_NVOL_BIT_OPS(CaseSensitive) DEFINE_NVOL_BIT_OPS(LogFileEmpty) -DEFINE_NVOL_BIT_OPS(QuotaOutOfDate) DEFINE_NVOL_BIT_OPS(UsnJrnlStamped) DEFINE_NVOL_BIT_OPS(ReadOnly) DEFINE_NVOL_BIT_OPS(Compression) From 949ba99cfe92868acd35935b7e0d91f01ca283f1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 21 May 2026 21:27:41 +0900 Subject: [PATCH 2946/5214] ntfs: remove unnecessary ternary boolean conversion Coccinelle warned about unnecessary patterns when assigning to bool variables. Simply assign the condition directly. Reported-by: kernel test robot Signed-off-by: Namjae Jeon --- fs/ntfs/runlist.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index e7de3d01257e..f27d78013856 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -1817,7 +1817,7 @@ struct runlist_element *ntfs_rl_punch_hole(struct runlist_element *dst_rl, int d !ntfs_rle_contain(s_rl, start_vcn)) return ERR_PTR(-EINVAL); - begin_split = s_rl->vcn != start_vcn ? true : false; + begin_split = s_rl->vcn != start_vcn; e_rl = ntfs_rl_find_vcn_nolock(dst_rl, end_vcn); if (!e_rl || @@ -1825,10 +1825,10 @@ struct runlist_element *ntfs_rl_punch_hole(struct runlist_element *dst_rl, int d !ntfs_rle_contain(e_rl, end_vcn)) return ERR_PTR(-EINVAL); - end_split = e_rl->vcn + e_rl->length - 1 != end_vcn ? true : false; + end_split = e_rl->vcn + e_rl->length - 1 != end_vcn; /* @s_rl has to be split into left, punched hole, and right */ - one_split_3 = e_rl == s_rl && begin_split && end_split ? true : false; + one_split_3 = e_rl == s_rl && begin_split && end_split; punch_cnt = (int)(e_rl - s_rl) + 1; @@ -1968,7 +1968,7 @@ struct runlist_element *ntfs_rl_collapse_range(struct runlist_element *dst_rl, i !ntfs_rle_contain(s_rl, start_vcn)) return ERR_PTR(-EINVAL); - begin_split = s_rl->vcn != start_vcn ? true : false; + begin_split = s_rl->vcn != start_vcn; e_rl = ntfs_rl_find_vcn_nolock(dst_rl, end_vcn); if (!e_rl || @@ -1976,10 +1976,10 @@ struct runlist_element *ntfs_rl_collapse_range(struct runlist_element *dst_rl, i !ntfs_rle_contain(e_rl, end_vcn)) return ERR_PTR(-EINVAL); - end_split = e_rl->vcn + e_rl->length - 1 != end_vcn ? true : false; + end_split = e_rl->vcn + e_rl->length - 1 != end_vcn; /* @s_rl has to be split into left, collapsed, and right */ - one_split_3 = e_rl == s_rl && begin_split && end_split ? true : false; + one_split_3 = e_rl == s_rl && begin_split && end_split; punch_cnt = (int)(e_rl - s_rl) + 1; *punch_rl = kvcalloc(punch_cnt + 1, sizeof(struct runlist_element), From 3964169d68ed282ccf0f7aac80f6606723280e04 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 21 May 2026 21:30:01 +0900 Subject: [PATCH 2947/5214] ntfs: remove unnecessary NULL checks before kfree NULL check before kfree() is unnecessary and triggers coccinelle warnings. Reported-by: kernel test robot Signed-off-by: Namjae Jeon --- fs/ntfs/logfile.c | 3 +-- fs/ntfs/super.c | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index d3f25d8e29f9..9df8c3095ca4 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -622,8 +622,7 @@ bool ntfs_check_logfile(struct inode *log_vi, struct restart_page_header **rp) ntfs_debug("Done."); return true; err_out: - if (rstr1_ph) - kvfree(rstr1_ph); + kvfree(rstr1_ph); return false; } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 312acb41c2ed..2a5ad7d56bc2 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1634,7 +1634,7 @@ static void ntfs_volume_free(struct ntfs_volume *vol) vol->upcase = NULL; } - if (!ntfs_nr_upcase_users && default_upcase) { + if (!ntfs_nr_upcase_users) { kvfree(default_upcase); default_upcase = NULL; } @@ -1649,8 +1649,7 @@ static void ntfs_volume_free(struct ntfs_volume *vol) unload_nls(vol->nls_map); - if (vol->lcn_empty_bits_per_page) - kvfree(vol->lcn_empty_bits_per_page); + kvfree(vol->lcn_empty_bits_per_page); kfree(vol->volume_label); kfree(vol); } From 8488c4d066e6a52937fa5d82ab131c7554ddc9d8 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sun, 24 May 2026 14:42:37 +0900 Subject: [PATCH 2948/5214] ntfs: free link name from ntfs_name_cache ntfs_link() converts the new link name with ntfs_nlstoucs() using NTFS_MAX_NAME_LEN. In this case ntfs_nlstoucs() allocates the result from ntfs_name_cache, and its contract requires callers to release the buffer with kmem_cache_free(ntfs_name_cache, ...). All other ntfs_nlstoucs() callers in namei.c do that, but ntfs_link() uses kfree(), which mismatches the allocator for successfully converted names. The conversion failure path reaches the common out label with uname == NULL. That was harmless for kfree(), but kmem_cache_free() does not provide the same NULL contract. Return directly on conversion failure and free successful conversions with ntfs_name_cache. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/namei.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index c4f82846c58c..9c1c36acfad2 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1532,8 +1532,7 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir, if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(sb, "Failed to convert name to unicode."); - err = -ENOMEM; - goto out; + return -ENOMEM; } if (!(vol->vol_flags & VOLUME_IS_DIRTY)) @@ -1563,7 +1562,7 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir, mutex_unlock(&ni->mrec_lock); out: - kfree(uname); + kmem_cache_free(ntfs_name_cache, uname); return err; } From 9974fe1db2b5d8b188000e0c2e15d1607b8f17f3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 27 May 2026 08:49:41 +0100 Subject: [PATCH 2949/5214] ntfs: Fix spelling mistake "etnry" -> "entry" There is a spelling mistake in a ntfs_error message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Namjae Jeon --- fs/ntfs/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 00e17637f771..8371ff4303e7 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -340,7 +340,7 @@ int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol, if (le16_to_cpu(ie->length) < sizeof(struct index_entry_header)) { ntfs_error(vol->sb, - "Index etnry too small in inode %llu.", + "Index entry too small in inode %llu.", inum); return -EIO; } From 0aad21570197973af4a1b25b3fb8ed3aeb9e7670 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 28 May 2026 11:15:35 +0900 Subject: [PATCH 2950/5214] ntfs: not change 0-byte $DATA attribute to non-resident When ntfs_resident_attr_resize() cannot grow a resident attribute in place, it retries after converting other resident attributes to non-resident to free space in the MFT recrord. Do not select zero-length resident $DATA attributes for this conversion. fsck treats 0-byte non-resident $DATA attribute as corruptions. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 9d676375f25c..2872b4ce1835 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -4537,10 +4537,12 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) { struct inode *tvi; struct attr_record *a; + u32 value_len; a = ctx->attr; if (a->non_resident || a->type == AT_ATTRIBUTE_LIST) continue; + value_len = le32_to_cpu(a->data.resident.value_length); if (ntfs_attr_can_be_non_resident(vol, a->type)) continue; @@ -4552,6 +4554,8 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi if (le32_to_cpu(a->length) <= (sizeof(struct attr_record) - sizeof(s64)) + ((a->name_length * sizeof(__le16) + 7) & ~7) + 8) continue; + if (a->type == AT_DATA && !value_len) + continue; if (a->type == AT_DATA) tvi = ntfs_iget(sb, base_ni->mft_no); @@ -4564,8 +4568,7 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi continue; } - if (ntfs_attr_make_non_resident(NTFS_I(tvi), - le32_to_cpu(ctx->attr->data.resident.value_length))) { + if (ntfs_attr_make_non_resident(NTFS_I(tvi), value_len)) { iput(tvi); continue; } From 18760a74ef7c28df93726445b5595162e62ed341 Mon Sep 17 00:00:00 2001 From: Ron de Bruijn Date: Sat, 30 May 2026 09:19:18 +0900 Subject: [PATCH 2951/5214] ntfs: fix off-by-one in mapping pairs decoding bounds checks In ntfs_mapping_pairs_decompress(), attr_end points one byte past the end of the attribute record: attr_end = (u8 *)attr + le32_to_cpu(attr->length); The two bounds checks validating that mapping pair data bytes fit within the attribute use strict greater-than (>), which allows a one-byte out-of-bounds read when the data extends exactly to attr_end: b = *buf & 0xf; if (b) { if (unlikely(buf + b > attr_end)) // off-by-one goto io_error; for (deltaxcn = (s8)buf[b--]; b; b--) deltaxcn = (deltaxcn << 8) + buf[b]; } When buf + b == attr_end, the check evaluates to false and buf[b] reads one byte past the valid attribute boundary. The same pattern appears in the LCN delta bytes check. Fix both checks to use >= so that buf[b] at exactly attr_end is correctly rejected as out of bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Ron de Bruijn Signed-off-by: Namjae Jeon --- fs/ntfs/runlist.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index f27d78013856..d8e8aa7b5bc0 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -763,7 +763,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * buf = (u8 *)attr + le16_to_cpu(attr->data.non_resident.mapping_pairs_offset); attr_end = (u8 *)attr + le32_to_cpu(attr->length); - if (unlikely(buf < (u8 *)attr || buf > attr_end)) { + if (unlikely(buf < (u8 *)attr || buf >= attr_end)) { ntfs_error(vol->sb, "Corrupt attribute."); return ERR_PTR(-EIO); } @@ -811,7 +811,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * */ b = *buf & 0xf; if (b) { - if (unlikely(buf + b > attr_end)) + if (unlikely(buf + b >= attr_end)) goto io_error; for (deltaxcn = (s8)buf[b--]; b; b--) deltaxcn = (deltaxcn << 8) + buf[b]; @@ -855,7 +855,7 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * u8 b2 = *buf & 0xf; b = b2 + ((*buf >> 4) & 0xf); - if (buf + b > attr_end) + if (buf + b >= attr_end) goto io_error; for (deltaxcn = (s8)buf[b--]; b > b2; b--) deltaxcn = (deltaxcn << 8) + buf[b]; From e9e50ce4f13dc721014af622613409455c734942 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 2 Jun 2026 13:53:24 +0900 Subject: [PATCH 2952/5214] ntfs: serialize volume label accesses Protect vol->volume_label with a mutex and snaphost the label before copy_to_user. This prevent a use-after-free when FS_IOC_SETFSLABEL replaces the vol->volume_label and FS_IOC_GETTSLABEL reads it concurrently. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/file.c | 17 +++++++++++++---- fs/ntfs/super.c | 21 ++++++++++++++------- fs/ntfs/volume.h | 2 ++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index e8bea22b81a7..264cf8404385 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -707,12 +707,21 @@ static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg) { struct ntfs_volume *vol = NTFS_SB(file_inode(filp)->i_sb); char __user *buf = (char __user *)arg; + char label[FSLABEL_MAX]; + ssize_t len; + mutex_lock(&vol->volume_label_lock); if (!vol->volume_label) { - if (copy_to_user(buf, "", 1)) - return -EFAULT; - } else if (copy_to_user(buf, vol->volume_label, - MIN(FSLABEL_MAX, strlen(vol->volume_label) + 1))) + label[0] = '\0'; + len = 0; + } else { + len = strscpy(label, vol->volume_label, sizeof(label)); + if (len == -E2BIG) + len = FSLABEL_MAX - 1; + } + mutex_unlock(&vol->volume_label_lock); + + if (copy_to_user(buf, label, len + 1)) return -EFAULT; return 0; } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 2a5ad7d56bc2..045656fa44f8 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -452,17 +452,23 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, (u8 *)uname, uname_len * sizeof(__le16), 0); out: + if (ret >= 0) { + char *old_label; + + mutex_lock(&vol->volume_label_lock); + old_label = vol->volume_label; + vol->volume_label = new_label; + mutex_unlock(&vol->volume_label_lock); + + kfree(old_label); + mark_inode_dirty_sync(vol->vol_ino); + ret = 0; + } mutex_unlock(&vol_ni->mrec_lock); kvfree(uname); - if (ret >= 0) { - kfree(vol->volume_label); - vol->volume_label = new_label; - mark_inode_dirty_sync(vol->vol_ino); - ret = 0; - } else { + if (ret < 0) kfree(new_label); - } return ret; } @@ -2508,6 +2514,7 @@ static int ntfs_init_fs_context(struct fs_context *fc) NVolSetCaseSensitive(vol); init_rwsem(&vol->mftbmp_lock); init_rwsem(&vol->lcnbmp_lock); + mutex_init(&vol->volume_label_lock); fc->s_fs_info = vol; fc->ops = &ntfs_context_ops; diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index e13e1423b2a9..3348394dbc0d 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -72,6 +72,7 @@ * @vol_flags: Volume flags. * @major_ver: Ntfs major version of volume. * @minor_ver: Ntfs minor version of volume. + * @volume_label_lock: protects @volume_label. * @volume_label: volume label. * @root_ino: The VFS inode of the root directory. * @secure_ino: The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL). @@ -131,6 +132,7 @@ struct ntfs_volume { struct inode *logfile_ino; struct inode *lcnbmp_ino; struct rw_semaphore lcnbmp_lock; + struct mutex volume_label_lock; struct inode *vol_ino; __le16 vol_flags; u8 major_ver; From 38e8db370843b518ff9bee4af46c6b800684cc78 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 14 May 2026 15:54:08 +0200 Subject: [PATCH 2953/5214] ntfs: Add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") The refactoring is going to alter the default behavior of alloc_workqueue() to be unbound by default. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. For more details see the Link tag below. In order to keep alloc_workqueue() behavior identical, explicitly request WQ_PERCPU. Cc: stable@vger.kernel.org # v7.1 Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 045656fa44f8..34d0040b385a 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2533,7 +2533,7 @@ MODULE_ALIAS_FS("ntfs"); static int ntfs_workqueue_init(void) { - ntfs_wq = alloc_workqueue("ntfs-bg-io", 0, 0); + ntfs_wq = alloc_workqueue("ntfs-bg-io", WQ_PERCPU, 0); if (!ntfs_wq) return -ENOMEM; return 0; From ec4f061f2219e0f0c6465d56d0380bf749235a53 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 3 Jun 2026 17:41:09 +0000 Subject: [PATCH 2954/5214] ntfs: detect mapping-pairs LCN accumulator overflow The NTFS mapping-pairs parser accumulates relative LCN deltas in a signed integer. A corrupted attribute can drive that addition past the representable range. One corrupt runlist shape sets the accumulated LCN to S64_MAX and then adds a delta of 1 in the next mapping-pairs entry. Signed overflow is undefined and can turn an invalid runlist into a different set of physical clusters. Check the LCN addition for overflow before storing the next run. Cc: stable@vger.kernel.org # v7.1 Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/runlist.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index d8e8aa7b5bc0..cbb6576cf725 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -860,7 +860,11 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * for (deltaxcn = (s8)buf[b--]; b > b2; b--) deltaxcn = (deltaxcn << 8) + buf[b]; /* Change the current lcn to its new value. */ - lcn += deltaxcn; + if (unlikely(check_add_overflow(lcn, deltaxcn, &lcn))) { + ntfs_error(vol->sb, + "LCN overflow in mapping pairs array."); + goto err_out; + } #ifdef DEBUG /* * On NTFS 1.2-, apparently can have lcn == -1 to From d5803e3345dae9c6470bb61869885236276b9a35 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:09 +0900 Subject: [PATCH 2955/5214] ntfs: validate attribute values on lookup ntfs_attr_find() and ntfs_external_attr_find() check that generic resident attribute values fit in their attribute records and that fixed-size resident values are large enough. For variable-length resident formats, however, the fixed part is not enough: embedded length fields can still point callers past the resident value. A crafted image can set a small resident $FILE_NAME value_length while leaving file_name_length large. Callers then trust file_name_length and read past the resident value when converting or comparing the name. This was reproduced with a crafted image under KASAN as a slab-out-of-bounds read from the kmalloc-1k MFT record copy. The stack included ntfs_lookup(), ntfs_iget(), ntfs_read_locked_inode(), ntfs_attr_name_get(), ntfs_ucstonls(), and utf16s_to_utf8s(). Add a shared attribute value validator and use it before a lookup path can return an attribute, including the AT_UNUSED enumeration case where callers inspect returned attributes directly. The helper validates resident value bounds, minimum resident value sizes, variable-length $FILE_NAME fields, and non-resident mapping-pairs metadata that was previously checked separately in both lookup paths. This also preserves the intended resident @val matching semantics in the external attribute lookup path. The old duplicated validation block overwrote the actual resident value length with the type-specific minimum length before comparing @val, so variable-length resident values could fail to match even when the bytes were identical. Keep the comparison on the actual value length, and make ntfs_attrlist_entry_add() compare resident attributes with lowest_vcn zero instead of reading the non-resident union member after a successful resident match. Reject non-resident $FILE_NAME records too: the format requires $FILE_NAME to be resident and callers treat returned records as resident. Cc: stable@vger.kernel.org # v7.1 Fixes: 6ceb4cc81ef3 ("ntfs: add bound checking to ntfs_attr_find") Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 160 ++++++++++++++++++++++++++++----------------- fs/ntfs/attrlist.c | 11 +++- 2 files changed, 107 insertions(+), 64 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 2872b4ce1835..71704ab58fa8 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -596,6 +596,97 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) } } +static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length) +{ + const struct file_name_attr *fn; + u32 file_name_size; + + fn = (const struct file_name_attr *)value; + file_name_size = fn->file_name_length * sizeof(__le16); + + return file_name_size <= + value_length - offsetof(struct file_name_attr, file_name); +} + +struct ntfs_resident_attr_value { + const u8 *data; + u32 len; +}; + +static bool ntfs_resident_attr_value_get(const struct attr_record *a, + struct ntfs_resident_attr_value *value) +{ + u32 attr_len; + u16 value_offset; + + attr_len = le32_to_cpu(a->length); + if (attr_len < offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved)) + return false; + + value->len = le32_to_cpu(a->data.resident.value_length); + value_offset = le16_to_cpu(a->data.resident.value_offset); + + if (value->len > attr_len || value_offset > attr_len - value->len) + return false; + + value->data = (const u8 *)a + value_offset; + return true; +} + +static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a) +{ + u32 attr_len; + u32 min_len; + u16 mp_offset; + + attr_len = le32_to_cpu(a->length); + min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + + sizeof(a->data.non_resident.initialized_size); + if (attr_len < min_len) + return false; + + mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + return mp_offset >= min_len && mp_offset <= attr_len; +} + +static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, + const struct attr_record *a, + const u64 mft_no) +{ + struct ntfs_resident_attr_value value; + u32 min_len; + + if (a->non_resident) { + if (a->type == AT_FILE_NAME) + goto corrupt; + if (!ntfs_non_resident_attr_value_is_valid(a)) + goto corrupt; + return true; + } + + if (!ntfs_resident_attr_value_get(a, &value)) + goto corrupt; + + min_len = ntfs_resident_attr_min_value_length(a->type); + if (min_len && value.len < min_len) + goto corrupt; + + switch (a->type) { + case AT_FILE_NAME: + if (!ntfs_file_name_attr_value_is_valid(value.data, value.len)) + goto corrupt; + break; + } + return true; + +corrupt: + ntfs_error(vol->sb, + "Corrupt %#x attribute in MFT record %llu\n", + le32_to_cpu(a->type), mft_no); + return false; +} + /* * ntfs_attr_find - find (next) attribute in mft record * @type: attribute type to find @@ -706,8 +797,11 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name, } } - if (type == AT_UNUSED) + if (type == AT_UNUSED) { + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; return 0; + } if (a->type != type) continue; /* @@ -748,37 +842,8 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name, } } - /* Validate attribute's value offset/length */ - if (!a->non_resident) { - u32 min_len; - u32 value_length = le32_to_cpu(a->data.resident.value_length); - u16 value_offset = le16_to_cpu(a->data.resident.value_offset); - - if (value_length > le32_to_cpu(a->length) || - value_offset > le32_to_cpu(a->length) - value_length) - break; - - min_len = ntfs_resident_attr_min_value_length(a->type); - if (min_len && value_length < min_len) { - ntfs_error(vol->sb, - "Too small %#x resident attribute value in MFT record %lld\n", - le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no); - break; - } - } else { - u32 min_len; - u16 mp_offset; - - min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + - sizeof(a->data.non_resident.initialized_size); - if (le32_to_cpu(a->length) < min_len) - break; - - mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); - if (mp_offset < min_len || - mp_offset > le32_to_cpu(a->length)) - break; - } + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; /* * The names match or @name not present and attribute is @@ -1253,22 +1318,8 @@ static int ntfs_external_attr_find(const __le32 type, ctx->attr = a; - if (a->non_resident) { - u32 min_len; - u16 mp_offset; - - min_len = offsetof(struct attr_record, - data.non_resident.initialized_size) + - sizeof(a->data.non_resident.initialized_size); - - if (le32_to_cpu(a->length) < min_len) - break; - - mp_offset = - le16_to_cpu(a->data.non_resident.mapping_pairs_offset); - if (mp_offset < min_len || mp_offset > attr_len) - break; - } + if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no)) + break; /* * If no @val specified or @val specified and it matches, we @@ -1280,19 +1331,6 @@ static int ntfs_external_attr_find(const __le32 type, u32 value_length = le32_to_cpu(a->data.resident.value_length); u16 value_offset = le16_to_cpu(a->data.resident.value_offset); - if (attr_len < offsetof(struct attr_record, data.resident.reserved) + - sizeof(a->data.resident.reserved)) - break; - if (value_length > attr_len || value_offset > attr_len - value_length) - break; - - value_length = ntfs_resident_attr_min_value_length(a->type); - if (value_length && le32_to_cpu(a->data.resident.value_length) < - value_length) { - pr_err("Too small resident attribute value in MFT record %lld, type %#x\n", - (long long)ctx->ntfs_ino->mft_no, a->type); - break; - } if (value_length == val_len && !memcmp((u8 *)a + value_offset, val, val_len)) { attr_found: diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c index c2594d4c83b0..afb13038ba42 100644 --- a/fs/ntfs/attrlist.c +++ b/fs/ntfs/attrlist.c @@ -118,6 +118,7 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) int entry_len, entry_offset, err; struct mft_record *ni_mrec; u8 *old_al; + __le64 lowest_vcn; if (!ni || !attr) { ntfs_debug("Invalid arguments.\n"); @@ -158,17 +159,21 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) ntfs_error(ni->vol->sb, "Failed to get search context"); goto err_out; } + if (attr->non_resident) + lowest_vcn = attr->data.non_resident.lowest_vcn; + else + lowest_vcn = 0; err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *) ((u8 *)attr + le16_to_cpu(attr->name_offset)) : AT_UNNAMED, attr->name_length, CASE_SENSITIVE, - (attr->non_resident) ? le64_to_cpu(attr->data.non_resident.lowest_vcn) : - 0, (attr->non_resident) ? NULL : ((u8 *)attr + + le64_to_cpu(lowest_vcn), + (attr->non_resident) ? NULL : ((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ? 0 : le32_to_cpu(attr->data.resident.value_length), ctx); if (!err) { /* Found some extent, check it to be before new extent. */ - if (ctx->al_entry->lowest_vcn == attr->data.non_resident.lowest_vcn) { + if (ctx->al_entry->lowest_vcn == lowest_vcn) { err = -EEXIST; ntfs_debug("Such attribute already present in the attribute list.\n"); ntfs_attr_put_search_ctx(ctx); From 40d88020d0797f96a93edd2e8edc413c2e2d8f84 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:10 +0900 Subject: [PATCH 2956/5214] ntfs: do not replace volume name after lookup errors ntfs_write_volume_label() removes an existing $VOLUME_NAME attribute and then adds the replacement. The old code only distinguished lookup success from all other results, so any lookup error was treated like an absent label and the add path still ran. That is unsafe once lookup-time validation rejects corrupt $VOLUME_NAME records with -EIO: the corrupt record would remain in place and a second $VOLUME_NAME record could be appended next to it. Only add the replacement after the old label was removed successfully or after lookup returned -ENOENT. Propagate all other lookup errors, and also stop if removing the old attribute fails. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 34d0040b385a..081a29583868 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -444,10 +444,15 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) goto out; } - if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, - ctx)) - ntfs_attr_record_rm(ctx); + ret = ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, + ctx); + if (!ret) + ret = ntfs_attr_record_rm(ctx); + else if (ret == -ENOENT) + ret = 0; ntfs_attr_put_search_ctx(ctx); + if (ret) + goto out; ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, (u8 *)uname, uname_len * sizeof(__le16), 0); From 45dd046ced0f5982a6d64ca449de3a61f5f15669 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:11 +0900 Subject: [PATCH 2957/5214] ntfs: reinit search context before volume information lookup On mount the volume inode is searched for $VOLUME_NAME and then, reusing the same search context, for $VOLUME_INFORMATION. The $VOLUME_NAME lookup is optional and its result is otherwise ignored. Once lookup-time validation can reject a corrupt $VOLUME_NAME with -EIO, the search context is left in an undefined state: ntfs_attr_find() documents that on an actual error @ctx->attr is undefined. Continuing the $VOLUME_INFORMATION search from that context is not contractually valid. Reinitialize the search context before the $VOLUME_INFORMATION lookup so it always starts from a well-defined state regardless of the $VOLUME_NAME lookup outcome. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 081a29583868..ca882e946a22 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1461,6 +1461,7 @@ static bool load_system_files(struct ntfs_volume *vol) vol->volume_label = NULL; } + ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx) || ctx->attr->non_resident || ctx->attr->flags) { ntfs_attr_put_search_ctx(ctx); From b3f6cd1d54aa279cc4f47aa27939ebe517a2c390 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Sat, 30 May 2026 23:35:12 +0900 Subject: [PATCH 2958/5214] ntfs: validate resident volume name values on lookup The shared lookup-time attribute validator now has a safe caller path for $VOLUME_NAME corruption: ntfs_write_volume_label() no longer treats lookup errors as an absent label, and the mount path reinitializes its search context before continuing to $VOLUME_INFORMATION. Add $VOLUME_NAME-specific resident value validation. A volume name is stored as a UTF-16LE string, so reject odd byte lengths, and reject values longer than the NTFS volume label limit. Empty labels remain valid. Also reject non-resident $VOLUME_NAME records. $VOLUME_NAME is required to be resident, like $FILE_NAME; a crafted non-resident record would otherwise pass lookup and ntfs_write_volume_label() would remove it as if it were a normal resident attribute. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 71704ab58fa8..5e1ad1cd0118 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -608,6 +608,14 @@ static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_ value_length - offsetof(struct file_name_attr, file_name); } +static bool ntfs_volume_name_attr_value_is_valid(const u32 value_length) +{ + if (value_length & 1) + return false; + + return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16); +} + struct ntfs_resident_attr_value { const u8 *data; u32 len; @@ -658,7 +666,7 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, u32 min_len; if (a->non_resident) { - if (a->type == AT_FILE_NAME) + if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME) goto corrupt; if (!ntfs_non_resident_attr_value_is_valid(a)) goto corrupt; @@ -677,6 +685,10 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, if (!ntfs_file_name_attr_value_is_valid(value.data, value.len)) goto corrupt; break; + case AT_VOLUME_NAME: + if (!ntfs_volume_name_attr_value_is_valid(value.len)) + goto corrupt; + break; } return true; From 63224b02748ddc6ee0ad0ee691768151da4a752d Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 1 Jun 2026 03:26:22 -0700 Subject: [PATCH 2959/5214] KVM: RISC-V: SBI FWFT: Mark vCPU CSRs dirty after setting feature value Mark the vCPU CSRs as dirty after successfully setting an FWFT feature value. FWFT features may modify CSRs (e.g., pointer masking modifies henvcfg.PMM), and failing to mark them dirty can lead to the guest observing stale CSR state after vCPU scheduling or migration. Fixes: 1323a5cfe52c ("KVM: riscv: Skip CSR restore if VCPU is reloaded on the same core") Signed-off-by: Yong-Xuan Wang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260601-kvm-get_reg_list-v2-v5-1-415d08a2813b@sifive.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_sbi_fwft.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index 2eab15339694..5e4aafb0cbf1 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -521,6 +521,7 @@ static int kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num break; case 2: ret = conf->feature->set(vcpu, conf, true, value); + vcpu->arch.csr_dirty = true; break; default: return -ENOENT; From 86957e5b4bff60c2f97d79c922e5dfafe0d94151 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 1 Jun 2026 03:26:23 -0700 Subject: [PATCH 2960/5214] KVM: RISC-V: SBI FWFT: Add optional init() callback for hardware probing Add an optional init() callback to separate one-time hardware probing from runtime availability checks. For pointer masking, this allows probing supported PMM lengths during initialization while checking ISA extension availability at runtime. Fix try_to_set_pmm() to restore the previous HENVCFG.PMM value after probing, preventing side effects from hardware detection. Add preemption protection to ensure CSR probe sequences complete atomically on the same CPU. Fixes: 6f576fc0aeb9 ("RISC-V: KVM: Add support for SBI_FWFT_POINTER_MASKING_PMLEN") Signed-off-by: Yong-Xuan Wang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260601-kvm-get_reg_list-v2-v5-2-415d08a2813b@sifive.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_sbi_fwft.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index 5e4aafb0cbf1..aee951f2b8e6 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -35,6 +35,16 @@ struct kvm_sbi_fwft_feature { */ bool (*supported)(struct kvm_vcpu *vcpu); + /** + * @init: Probe and initialize the feature on the vcpu + * + * This callback is optional. If provided, it will be called during + * vcpu initialization to probe the feature availability and perform + * any necessary initialization. Returns true if the feature is supported + * and initialized successfully, false otherwise. + */ + bool (*init)(struct kvm_vcpu *vcpu); + /** * @reset: Reset the feature value irrespective whether feature is supported or not * @@ -131,19 +141,30 @@ static long kvm_sbi_fwft_get_misaligned_delegation(struct kvm_vcpu *vcpu, static bool try_to_set_pmm(unsigned long value) { + unsigned long prev; + bool ret; + + prev = csr_read_clear(CSR_HENVCFG, ENVCFG_PMM); csr_set(CSR_HENVCFG, value); - return (csr_read_clear(CSR_HENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; + ret = (csr_read_clear(CSR_HENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; + csr_write(CSR_HENVCFG, prev); + + return ret; } static bool kvm_sbi_fwft_pointer_masking_pmlen_supported(struct kvm_vcpu *vcpu) +{ + return riscv_isa_extension_available(vcpu->arch.isa, SMNPM); +} + +static bool kvm_sbi_fwft_pointer_masking_pmlen_init(struct kvm_vcpu *vcpu) { struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu); - if (!riscv_isa_extension_available(vcpu->arch.isa, SMNPM)) - return false; - + preempt_disable(); fwft->have_vs_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); fwft->have_vs_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); + preempt_enable(); return fwft->have_vs_pmlen_7 || fwft->have_vs_pmlen_16; } @@ -231,6 +252,7 @@ static const struct kvm_sbi_fwft_feature features[] = { .first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, pointer_masking.enable) / sizeof(unsigned long), .supported = kvm_sbi_fwft_pointer_masking_pmlen_supported, + .init = kvm_sbi_fwft_pointer_masking_pmlen_init, .reset = kvm_sbi_fwft_reset_pointer_masking_pmlen, .set = kvm_sbi_fwft_set_pointer_masking_pmlen, .get = kvm_sbi_fwft_get_pointer_masking_pmlen, @@ -365,6 +387,9 @@ static int kvm_sbi_ext_fwft_init(struct kvm_vcpu *vcpu) else conf->supported = true; + if (conf->supported && feature->init) + conf->supported = feature->init(vcpu); + conf->enabled = conf->supported; conf->feature = feature; } From e659112a39cd93150d93c4661444a439d512e1ca Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 1 Jun 2026 03:26:24 -0700 Subject: [PATCH 2961/5214] KVM: RISC-V: SBI FWFT: Fix stale feature exposure after runtime extension changes Fix a bug where FWFT features could be incorrectly exposed to guests after userspace disables their dependent ISA extensions at runtime. The 'supported' field in kvm_sbi_fwft_config was set once during vCPU initialization based on the initial hardware/extension availability. However, when userspace subsequently disables ISA extensions via the KVM ONE_REG interface, the 'supported' field was not updated. This caused the following issues: 1. FWFT features would remain visible and accessible to guests even after their prerequisite ISA extensions were disabled 2. Guests could configure FWFT features that depend on disabled extensions, leading to undefined behavior 3. The static 'supported' flag and the dynamic supported() callback could disagree about feature availability The fix introduces a two-layer checking mechanism: 1. Add an optional init() callback to the kvm_sbi_fwft_feature structure for features that require hardware probing during initialization. This separates the one-time hardware detection logic from the runtime availability check. 2. Add runtime checks in all FWFT-related functions that call feature->supported(vcpu) if the callback exists. This ensures feature availability is re-evaluated based on the current ISA extension state. This approach maintains the cached 'supported' field for initialization- time decisions while ensuring runtime availability is always determined by the current vCPU configuration, not initialization-time snapshots. Fixes: 6b72fd170592 ("RISC-V: KVM: add support for FWFT SBI extension") Signed-off-by: Yong-Xuan Wang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260601-kvm-get_reg_list-v2-v5-3-415d08a2813b@sifive.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_sbi_fwft.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index aee951f2b8e6..ab39ac464ffd 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -303,6 +303,8 @@ static int kvm_fwft_get_feature(struct kvm_vcpu *vcpu, u32 feature, if (!tconf->supported || !tconf->enabled) return SBI_ERR_NOT_SUPPORTED; + else if (tconf->feature->supported && !tconf->feature->supported(vcpu)) + return SBI_ERR_NOT_SUPPORTED; *conf = tconf; @@ -433,6 +435,8 @@ static unsigned long kvm_sbi_ext_fwft_get_reg_count(struct kvm_vcpu *vcpu) conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) continue; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + continue; ret++; } @@ -455,6 +459,8 @@ static int kvm_sbi_ext_fwft_get_reg_id(struct kvm_vcpu *vcpu, int index, u64 *re conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) continue; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + continue; if (index == idx) { *reg_id = KVM_REG_RISCV | @@ -490,6 +496,8 @@ static int kvm_sbi_ext_fwft_get_reg(struct kvm_vcpu *vcpu, unsigned long reg_num conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) return -ENOENT; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + return -ENOENT; switch (reg_num - feature->first_reg_num) { case 0: @@ -527,6 +535,8 @@ static int kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) return -ENOENT; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + return -ENOENT; switch (reg_num - feature->first_reg_num) { case 0: From 85fc710600ca44cbc90e69ee2b4fd712ae7f991a Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 1 Jun 2026 03:26:25 -0700 Subject: [PATCH 2962/5214] KVM: riscv: selftests: Refactor ISA and SBI extension sublist macros Refactor the get-reg-list test to use unified sublist macros for ISA and SBI extensions, eliminating code duplication and improving maintainability. Previously, each extension had its own hand-coded sublist definition (e.g., SUBLIST_ZICBOM, SUBLIST_AIA, etc.) and the config structures repeated the same pattern. This made the code verbose and error-prone. Signed-off-by: Yong-Xuan Wang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260601-kvm-get_reg_list-v2-v5-4-415d08a2813b@sifive.com Signed-off-by: Anup Patel --- .../selftests/kvm/riscv/get-reg-list.c | 76 +++++++------------ 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c index 8d6fdb5d38b8..5033c09201ef 100644 --- a/tools/testing/selftests/kvm/riscv/get-reg-list.c +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -1013,7 +1013,7 @@ static __u64 fp_d_regs[] = { }; /* Define a default vector registers with length. This will be overwritten at runtime */ -static __u64 vector_regs[] = { +static __u64 v_regs[] = { KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_VECTOR | KVM_REG_RISCV_VECTOR_CSR_REG(vstart), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_VECTOR | KVM_REG_RISCV_VECTOR_CSR_REG(vl), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_VECTOR | KVM_REG_RISCV_VECTOR_CSR_REG(vtype), @@ -1057,37 +1057,17 @@ static __u64 vector_regs[] = { #define SUBLIST_BASE \ {"base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), \ .skips_set = base_skips_set, .skips_set_n = ARRAY_SIZE(base_skips_set),} -#define SUBLIST_SBI_BASE \ - {"sbi-base", .feature_type = VCPU_FEATURE_SBI_EXT, .feature = KVM_RISCV_SBI_EXT_V01, \ - .regs = sbi_base_regs, .regs_n = ARRAY_SIZE(sbi_base_regs),} -#define SUBLIST_SBI_STA \ - {"sbi-sta", .feature_type = VCPU_FEATURE_SBI_EXT, .feature = KVM_RISCV_SBI_EXT_STA, \ - .regs = sbi_sta_regs, .regs_n = ARRAY_SIZE(sbi_sta_regs),} -#define SUBLIST_SBI_FWFT \ - {"sbi-fwft", .feature_type = VCPU_FEATURE_SBI_EXT, .feature = KVM_RISCV_SBI_EXT_FWFT, \ - .regs = sbi_fwft_regs, .regs_n = ARRAY_SIZE(sbi_fwft_regs),} -#define SUBLIST_ZICBOM \ - {"zicbom", .feature = KVM_RISCV_ISA_EXT_ZICBOM, .regs = zicbom_regs, .regs_n = ARRAY_SIZE(zicbom_regs),} -#define SUBLIST_ZICBOP \ - {"zicbop", .feature = KVM_RISCV_ISA_EXT_ZICBOP, .regs = zicbop_regs, .regs_n = ARRAY_SIZE(zicbop_regs),} -#define SUBLIST_ZICBOZ \ - {"zicboz", .feature = KVM_RISCV_ISA_EXT_ZICBOZ, .regs = zicboz_regs, .regs_n = ARRAY_SIZE(zicboz_regs),} -#define SUBLIST_AIA \ - {"aia", .feature = KVM_RISCV_ISA_EXT_SSAIA, .regs = aia_regs, .regs_n = ARRAY_SIZE(aia_regs),} -#define SUBLIST_SMSTATEEN \ - {"smstateen", .feature = KVM_RISCV_ISA_EXT_SMSTATEEN, .regs = smstateen_regs, .regs_n = ARRAY_SIZE(smstateen_regs),} -#define SUBLIST_FP_F \ - {"fp_f", .feature = KVM_RISCV_ISA_EXT_F, .regs = fp_f_regs, \ - .regs_n = ARRAY_SIZE(fp_f_regs),} -#define SUBLIST_FP_D \ - {"fp_d", .feature = KVM_RISCV_ISA_EXT_D, .regs = fp_d_regs, \ - .regs_n = ARRAY_SIZE(fp_d_regs),} -#define SUBLIST_V \ - {"v", .feature = KVM_RISCV_ISA_EXT_V, .regs = vector_regs, .regs_n = ARRAY_SIZE(vector_regs),} +#define SUBLIST_ISA(ext, extu) \ + { \ + .name = #ext, \ + .feature = KVM_RISCV_ISA_EXT_##extu, \ + .regs = ext##_regs, \ + .regs_n = ARRAY_SIZE(ext##_regs), \ + } #define KVM_ISA_EXT_SIMPLE_CONFIG(ext, extu) \ -static __u64 regs_##ext[] = { \ +static __u64 ext##_regs[] = { \ KVM_REG_RISCV | KVM_REG_SIZE_ULONG | \ KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | \ KVM_RISCV_ISA_EXT_##extu, \ @@ -1095,18 +1075,22 @@ static __u64 regs_##ext[] = { \ static struct vcpu_reg_list config_##ext = { \ .sublists = { \ SUBLIST_BASE, \ - { \ - .name = #ext, \ - .feature = KVM_RISCV_ISA_EXT_##extu, \ - .regs = regs_##ext, \ - .regs_n = ARRAY_SIZE(regs_##ext), \ - }, \ + SUBLIST_ISA(ext, extu), \ {0}, \ }, \ } \ +#define SUBLIST_SBI(ext, extu) \ + { \ + .name = "sbi-"#ext, \ + .feature_type = VCPU_FEATURE_SBI_EXT, \ + .feature = KVM_RISCV_SBI_EXT_##extu, \ + .regs = sbi_##ext##_regs, \ + .regs_n = ARRAY_SIZE(sbi_##ext##_regs), \ + } + #define KVM_SBI_EXT_SIMPLE_CONFIG(ext, extu) \ -static __u64 regs_sbi_##ext[] = { \ +static __u64 sbi_##ext##_regs[] = { \ KVM_REG_RISCV | KVM_REG_SIZE_ULONG | \ KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | \ KVM_RISCV_SBI_EXT_##extu, \ @@ -1114,13 +1098,7 @@ static __u64 regs_sbi_##ext[] = { \ static struct vcpu_reg_list config_sbi_##ext = { \ .sublists = { \ SUBLIST_BASE, \ - { \ - .name = "sbi-"#ext, \ - .feature_type = VCPU_FEATURE_SBI_EXT, \ - .feature = KVM_RISCV_SBI_EXT_##extu, \ - .regs = regs_sbi_##ext, \ - .regs_n = ARRAY_SIZE(regs_sbi_##ext), \ - }, \ + SUBLIST_SBI(ext, extu), \ {0}, \ }, \ } \ @@ -1129,7 +1107,7 @@ static struct vcpu_reg_list config_sbi_##ext = { \ static struct vcpu_reg_list config_##ext = { \ .sublists = { \ SUBLIST_BASE, \ - SUBLIST_##extu, \ + SUBLIST_ISA(ext, extu), \ {0}, \ }, \ } \ @@ -1138,14 +1116,14 @@ static struct vcpu_reg_list config_##ext = { \ static struct vcpu_reg_list config_sbi_##ext = { \ .sublists = { \ SUBLIST_BASE, \ - SUBLIST_SBI_##extu, \ + SUBLIST_SBI(ext, extu), \ {0}, \ }, \ } \ /* Note: The below list is alphabetically sorted. */ -KVM_SBI_EXT_SUBLIST_CONFIG(base, BASE); +KVM_SBI_EXT_SUBLIST_CONFIG(base, V01); KVM_SBI_EXT_SUBLIST_CONFIG(sta, STA); KVM_SBI_EXT_SIMPLE_CONFIG(pmu, PMU); KVM_SBI_EXT_SIMPLE_CONFIG(dbcn, DBCN); @@ -1153,9 +1131,9 @@ KVM_SBI_EXT_SIMPLE_CONFIG(susp, SUSP); KVM_SBI_EXT_SIMPLE_CONFIG(mpxy, MPXY); KVM_SBI_EXT_SUBLIST_CONFIG(fwft, FWFT); -KVM_ISA_EXT_SUBLIST_CONFIG(aia, AIA); -KVM_ISA_EXT_SUBLIST_CONFIG(fp_f, FP_F); -KVM_ISA_EXT_SUBLIST_CONFIG(fp_d, FP_D); +KVM_ISA_EXT_SUBLIST_CONFIG(aia, SSAIA); +KVM_ISA_EXT_SUBLIST_CONFIG(fp_f, F); +KVM_ISA_EXT_SUBLIST_CONFIG(fp_d, D); KVM_ISA_EXT_SUBLIST_CONFIG(v, V); KVM_ISA_EXT_SIMPLE_CONFIG(h, H); KVM_ISA_EXT_SIMPLE_CONFIG(smnpm, SMNPM); From 64e50ab0ed84383eec398ced43b8ea40b249804b Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 1 Jun 2026 03:26:26 -0700 Subject: [PATCH 2963/5214] KVM: riscv: selftests: Split SBI FWFT into separate feature-specific sublists Divide the monolithic SBI FWFT (Firmware Features) register list into separate sublists, each testing a specific FWFT feature independently with proper dependency checking. Previously, all FWFT features were tested together in a single sublist. This caused issues because: 1. Not all FWFT features are available on all platforms 2. Some features depend on specific ISA extensions (e.g., pointer_masking requires Smnpm) 3. Tests would fail if any single feature was unavailable Add the feature-specific SBI FWFT sublists with the following improvements: - Add check_fwft_feature() helper to verify FWFT feature availability at runtime - Update filter_reg() to handle per-feature FWFT register filtering Signed-off-by: Yong-Xuan Wang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260601-kvm-get_reg_list-v2-v5-5-415d08a2813b@sifive.com Signed-off-by: Anup Patel --- .../selftests/kvm/riscv/get-reg-list.c | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c index 5033c09201ef..cb86cb6b3635 100644 --- a/tools/testing/selftests/kvm/riscv/get-reg-list.c +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -27,6 +27,7 @@ enum { }; static bool isa_ext_cant_disable[KVM_RISCV_ISA_EXT_MAX]; +static bool sbi_ext_enabled[KVM_RISCV_SBI_EXT_MAX]; bool filter_reg(__u64 reg) { @@ -149,6 +150,14 @@ bool filter_reg(__u64 reg) case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1h): case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2h): return isa_ext_cant_disable[KVM_RISCV_ISA_EXT_SSAIA]; + /* + * FWFT misaligned delegation registers are always visible when the SBI FWFT + * extension is enable and the host supports the misaligned delegation. + */ + case KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.enable): + case KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.flags): + case KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.value): + return sbi_ext_enabled[KVM_RISCV_SBI_EXT_FWFT]; default: break; } @@ -193,6 +202,27 @@ static int override_vector_reg_size(struct kvm_vcpu *vcpu, struct vcpu_reg_subli return 0; } +void check_fwft_feature(struct kvm_vcpu *vcpu, struct vcpu_reg_sublist *s, u64 feature) +{ + unsigned long value; + int rc; + + /* Enable SBI FWFT extension so that we can check the supported register */ + rc = __vcpu_set_reg(vcpu, feature, 1); + if (rc) + return; + + for (int i = 0; i < s->regs_n; i++) { + if ((s->regs[i] & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_SBI_STATE) { + rc = __vcpu_get_reg(vcpu, s->regs[i], &value); + __TEST_REQUIRE(!rc, "%s not available, skipping tests", s->name); + } + } + + /* We should assert if disabling failed here while enabling succeeded before */ + vcpu_set_reg(vcpu, feature, 0); +} + void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) { unsigned long isa_ext_state[KVM_RISCV_ISA_EXT_MAX] = { 0 }; @@ -235,6 +265,9 @@ void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) break; case VCPU_FEATURE_SBI_EXT: feature = RISCV_SBI_EXT_REG(s->feature); + if (s->feature == KVM_RISCV_SBI_EXT_FWFT) + check_fwft_feature(vcpu, s, feature); + sbi_ext_enabled[s->feature] = true; break; default: TEST_FAIL("Unknown feature type"); @@ -897,11 +930,15 @@ static __u64 sbi_sta_regs[] = { KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_STA | KVM_REG_RISCV_SBI_STA_REG(shmem_hi), }; -static __u64 sbi_fwft_regs[] = { +static __u64 sbi_fwft_misaligned_deleg_regs[] = { KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_FWFT, KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.enable), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.flags), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(misaligned_deleg.value), +}; + +static __u64 sbi_fwft_pointer_masking_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_FWFT, KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(pointer_masking.enable), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(pointer_masking.flags), KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_STATE | KVM_REG_RISCV_SBI_FWFT | KVM_REG_RISCV_SBI_FWFT_REG(pointer_masking.value), @@ -1129,7 +1166,6 @@ KVM_SBI_EXT_SIMPLE_CONFIG(pmu, PMU); KVM_SBI_EXT_SIMPLE_CONFIG(dbcn, DBCN); KVM_SBI_EXT_SIMPLE_CONFIG(susp, SUSP); KVM_SBI_EXT_SIMPLE_CONFIG(mpxy, MPXY); -KVM_SBI_EXT_SUBLIST_CONFIG(fwft, FWFT); KVM_ISA_EXT_SUBLIST_CONFIG(aia, SSAIA); KVM_ISA_EXT_SUBLIST_CONFIG(fp_f, F); @@ -1206,6 +1242,23 @@ KVM_ISA_EXT_SIMPLE_CONFIG(zvksed, ZVKSED); KVM_ISA_EXT_SIMPLE_CONFIG(zvksh, ZVKSH); KVM_ISA_EXT_SIMPLE_CONFIG(zvkt, ZVKT); +static struct vcpu_reg_list config_sbi_fwft_misaligned_deleg = { + .sublists = { + SUBLIST_BASE, + SUBLIST_SBI(fwft_misaligned_deleg, FWFT), + {0}, + }, +}; + +static struct vcpu_reg_list config_sbi_fwft_pointer_masking = { + .sublists = { + SUBLIST_BASE, + SUBLIST_ISA(smnpm, SMNPM), + SUBLIST_SBI(fwft_pointer_masking, FWFT), + {0}, + }, +}; + struct vcpu_reg_list *vcpu_configs[] = { &config_sbi_base, &config_sbi_sta, @@ -1213,7 +1266,8 @@ struct vcpu_reg_list *vcpu_configs[] = { &config_sbi_dbcn, &config_sbi_susp, &config_sbi_mpxy, - &config_sbi_fwft, + &config_sbi_fwft_misaligned_deleg, + &config_sbi_fwft_pointer_masking, &config_aia, &config_fp_f, &config_fp_d, From 4acc0138c4f2fba453da5a5076ad2bba08a7463b Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 4 Jun 2026 22:52:28 -0700 Subject: [PATCH 2964/5214] power: supply: max17042_battery: Use modern PM ops to clear up warning When building for a platform that does not have power management, such as s390, there is an unused function warning, as max17042_suspend_soc_alerts() is only used in max17042_suspend(), which is under a CONFIG_PM_SLEEP #ifdef. drivers/power/supply/max17042_battery.c:957:13: error: 'max17042_suspend_soc_alerts' defined but not used [-Werror=unused-function] 957 | static void max17042_suspend_soc_alerts(struct max17042_chip *chip) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the modern DEFINE_SIMPLE_DEV_PM_OPS(), which allows the compiler to see the functions as used while allowing it to eliminate them as unused during the optimization phase. Use pm_ptr() to allow the compiler to drop max17042_pm_ops when there is no PM support. Fixes: 601885ffb5e9 ("power: supply: max17042_battery: Keep only critical alerts during suspend") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260604-max17042_battery-fix-unused-suspend_soc_alerts-v1-1-3562a68e6f36@kernel.org Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 0fb46c1c203f..639dacdb9b31 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1296,7 +1296,6 @@ static int max17042_platform_probe(struct platform_device *pdev) return max17042_probe(i2c, dev, irq, id->driver_data); } -#ifdef CONFIG_PM_SLEEP static int max17042_suspend(struct device *dev) { struct max17042_chip *chip = dev_get_drvdata(dev); @@ -1327,10 +1326,9 @@ static int max17042_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, - max17042_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, + max17042_resume); #ifdef CONFIG_ACPI static const struct acpi_device_id max17042_acpi_match[] = { @@ -1397,7 +1395,7 @@ static struct i2c_driver max17042_i2c_driver = { .name = "max17042", .acpi_match_table = ACPI_PTR(max17042_acpi_match), .of_match_table = of_match_ptr(max17042_dt_match), - .pm = &max17042_pm_ops, + .pm = pm_ptr(&max17042_pm_ops), }, .probe = max17042_i2c_probe, .id_table = max17042_id, @@ -1407,7 +1405,7 @@ static struct platform_driver max17042_platform_driver = { .driver = { .name = "max17042", .acpi_match_table = ACPI_PTR(max17042_acpi_match), - .pm = &max17042_pm_ops, + .pm = pm_ptr(&max17042_pm_ops), }, .probe = max17042_platform_probe, .id_table = max17042_platform_id, From 06e7994427ab56e32699a5e45d048ff0826f3d53 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:23:35 -0300 Subject: [PATCH 2965/5214] perf sched: Cap max_cpu at MAX_CPUS in timehist sample processing perf_timehist__process_sample() updates sched->max_cpu from the sample CPU without bounds checking. Later code uses max_cpu + 1 as an iteration count over arrays allocated with MAX_CPUS entries (curr_thread, cpu_last_switched). A recording with CPU IDs >= MAX_CPUS causes out-of-bounds array accesses. Also cap the env->nr_cpus_online initialization of max_cpu in perf_sched__timehist(), which could exceed MAX_CPUS on very large systems. Add bounds checks before both max_cpu updates, matching the pattern already used in map_switch_event(). Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reviewed-by: David Ahern Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 7bd61028327b..87a1f4cf8760 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3215,7 +3215,9 @@ static int perf_timehist__process_sample(const struct perf_tool *tool, .cpu = sample->cpu, }; - if (this_cpu.cpu > sched->max_cpu.cpu) + /* max_cpu indexes arrays allocated with MAX_CPUS entries */ + if (this_cpu.cpu >= 0 && this_cpu.cpu < MAX_CPUS && + this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (evsel->handler != NULL) { @@ -3385,8 +3387,8 @@ static int perf_sched__timehist(struct perf_sched *sched) perf_session__set_tracepoints_handlers(session, migrate_handlers)) goto out; - /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu.cpu = env->nr_cpus_online; + /* pre-allocate struct for per-CPU idle stats; cap to array bounds */ + sched->max_cpu.cpu = min(env->nr_cpus_online, MAX_CPUS); if (sched->max_cpu.cpu == 0) sched->max_cpu.cpu = 4; if (init_idle_threads(sched->max_cpu.cpu)) From 5949d339f5ec98752d56dcd4e36f619a59d513a5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:25:57 -0300 Subject: [PATCH 2966/5214] perf sched: Fix register_pid() overflow, strcpy, and BUG_ON register_pid() has several issues when processing untrusted perf.data: 1. Integer overflow: (pid + 1) * sizeof(struct task_desc *) can wrap to a small value on 32-bit systems when pid is large (e.g. 0x40000000), causing realloc to return a tiny buffer followed by out-of-bounds writes in the initialization loop. 2. Heap buffer overflow: strcpy(task->comm, comm) copies the untrusted comm string into a fixed 20-byte COMM_LEN buffer with no length check. 3. BUG_ON on allocation failure: perf.data is untrusted input, so allocation failures should be handled gracefully rather than killing the process. 4. Realloc of sched->tasks assigned directly back, leaking the old pointer on failure; nr_tasks incremented before the realloc, leaving corrupted state on failure. Cap pid at PID_MAX_LIMIT (4194304, matching the kernel's maximum on 64-bit), replace strcpy with strlcpy, guard against NULL comm, replace BUG_ON with NULL returns using safe realloc patterns, and add NULL checks in callers that dereference the result. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 87a1f4cf8760..21fb820b625b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -55,6 +55,7 @@ #define COMM_LEN 20 #define SYM_LEN 129 #define MAX_PID 1024000 +#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */ #define MAX_PRIO 140 #define SEP_LEN 100 @@ -448,17 +449,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta static struct task_desc *register_pid(struct perf_sched *sched, unsigned long pid, const char *comm) { - struct task_desc *task; + struct task_desc *task, **tasks_p; static int pid_max; + /* perf.data is untrusted — cap pid to prevent overflow in size calculations */ + if (pid >= PID_MAX_LIMIT) { + pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT); + return NULL; + } + if (sched->pid_to_task == NULL) { if (sysctl__read_int("kernel/pid_max", &pid_max) < 0) pid_max = MAX_PID; - BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL); + sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *)); + if (sched->pid_to_task == NULL) + return NULL; } if (pid >= (unsigned long)pid_max) { - BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) * - sizeof(struct task_desc *))) == NULL); + void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *)); + + if (p == NULL) + return NULL; + sched->pid_to_task = p; while (pid >= (unsigned long)pid_max) sched->pid_to_task[pid_max++] = NULL; } @@ -469,9 +481,11 @@ static struct task_desc *register_pid(struct perf_sched *sched, return task; task = zalloc(sizeof(*task)); + if (task == NULL) + return NULL; task->pid = pid; - task->nr = sched->nr_tasks; - strcpy(task->comm, comm); + if (comm) + strlcpy(task->comm, comm, sizeof(task->comm)); /* * every task starts in sleeping state - this gets ignored * if there's no wakeup pointing to this sleep state: @@ -479,10 +493,12 @@ static struct task_desc *register_pid(struct perf_sched *sched, add_sched_event_sleep(sched, task, 0); sched->pid_to_task[pid] = task; - sched->nr_tasks++; - sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *)); - BUG_ON(!sched->tasks); - sched->tasks[task->nr] = task; + tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *)); + if (!tasks_p) + return NULL; + sched->tasks = tasks_p; + sched->tasks[sched->nr_tasks] = task; + task->nr = sched->nr_tasks++; if (verbose > 0) printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); @@ -841,6 +857,8 @@ replay_wakeup_event(struct perf_sched *sched, waker = register_pid(sched, sample->tid, ""); wakee = register_pid(sched, pid, comm); + if (waker == NULL || wakee == NULL) + return -1; add_sched_event_wakeup(sched, waker, sample->time, wakee); return 0; @@ -881,6 +899,8 @@ static int replay_switch_event(struct perf_sched *sched, prev = register_pid(sched, prev_pid, prev_comm); next = register_pid(sched, next_pid, next_comm); + if (prev == NULL || next == NULL) + return -1; sched->cpu_last_switched[cpu] = timestamp; From f32dc302a090f48893477ef297a888db109ec0bd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:56:33 -0300 Subject: [PATCH 2967/5214] perf mmap: Guard cpu__get_node() return in aio_bind() perf_mmap__aio_bind() passes the cpu__get_node() return value directly to an unsigned long variable (node_index). When cpu__get_node() returns -1 for an unknown CPU, the implicit int-to-unsigned-long conversion sign-extends it to ULONG_MAX. This causes bitmap_zalloc(ULONG_MAX + 1) which wraps to bitmap_zalloc(0), returning a zero-sized allocation. The subsequent __set_bit(ULONG_MAX, node_mask) then writes massively out of bounds. Check the return value in a signed temporary before assigning to node_index, and skip the NUMA binding when the node is unknown. Fixes: c44a8b44ca9f ("perf record: Bind the AIO user space buffers to nodes") Reported-by: sashiko-bot Cc: Alexey Budankov Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/mmap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index b69f926d314b..4404a99eee45 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -104,9 +104,15 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, i int err = 0; if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) { + int node; + data = map->aio.data[idx]; mmap_len = mmap__mmap_len(map); - node_index = cpu__get_node(cpu); + node = cpu__get_node(cpu); + /* -1 sign-extends to ULONG_MAX, wrapping bitmap_zalloc(0) and OOB __set_bit */ + if (node < 0) + return 0; + node_index = node; node_mask = bitmap_zalloc(node_index + 1); if (!node_mask) { pr_err("Failed to allocate node mask for mbind: error %m\n"); From 52e69b1c5b606b513d403dd4addc784c27a0c8e2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:59:11 -0300 Subject: [PATCH 2968/5214] perf stat: Bounds-check CPU index in topology aggregation callbacks Six perf_env__get_*_aggr_by_cpu() functions access env->cpu[cpu.cpu] after only checking cpu.cpu != -1. env->cpu[] is allocated with env->nr_cpus_avail entries, so a CPU index from an untrusted perf.data file that exceeds that count causes an out-of-bounds heap read. Replace the != -1 guard with >= 0 && < env->nr_cpus_avail in all six functions. The >= 0 check also catches -1 and any other negative values that could bypass the old check. Affected functions: - perf_env__get_socket_aggr_by_cpu() - perf_env__get_die_aggr_by_cpu() - perf_env__get_cache_aggr_by_cpu() - perf_env__get_cluster_aggr_by_cpu() - perf_env__get_core_aggr_by_cpu() - perf_env__get_cpu_aggr_by_cpu() Fixes: 68d702f7a120 ("perf stat report: Add support to initialize aggr_map from file") Reported-by: sashiko-bot Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99d7db372b48..9a045811c419 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1638,7 +1638,8 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) + /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) id.socket = env->cpu[cpu.cpu].socket_id; return id; @@ -1649,7 +1650,7 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * die_id is relative to socket, so start * with the socket ID and then add die to @@ -1705,7 +1706,7 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; id.socket = env->cpu[cpu.cpu].socket_id; @@ -1722,7 +1723,7 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { id.socket = env->cpu[cpu.cpu].socket_id; id.die = env->cpu[cpu.cpu].die_id; id.cluster = env->cpu[cpu.cpu].cluster_id; @@ -1736,7 +1737,7 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. @@ -1755,7 +1756,7 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket and die, * we need a global id. So we set From 65117c3da50f749f4c22eb7a7effc53453dff57f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:05:13 -0300 Subject: [PATCH 2969/5214] perf c2c: Bounds-check CPU and node IDs before bitmap and array access c2c_he__set_cpu() passes sample->cpu directly to __set_bit(cpu, cpuset) after only checking for the (u32)-1 sentinel. The cpuset bitmap is allocated with c2c.cpus_cnt bits (from env->nr_cpus_avail), so a crafted perf.data with CPU IDs exceeding that count causes out-of-bounds heap writes. c2c_he__set_node() similarly passes the node ID from mem2node__node() to __set_bit(node, nodeset) after only checking for negative values. The nodeset bitmap is sized to c2c.nodes_cnt (from env->nr_numa_nodes), so a node ID exceeding that causes OOB writes. process_sample_event() indexes c2c.cpu2node[cpu] and c2c_he->node_stats[node] without bounds checking. Both arrays are sized to c2c.cpus_cnt and c2c.nodes_cnt respectively. Add bounds checks in all three paths: - c2c_he__set_cpu(): return if sample->cpu >= c2c.cpus_cnt - c2c_he__set_node(): return if node >= c2c.nodes_cnt - process_sample_event(): clamp cpu to 0 if >= cpus_cnt, guard node_stats access with bounds check Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 3dd45a550fdb..f060dfbe11c2 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -245,6 +245,10 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, "WARNING: no sample cpu value")) return; + /* cpuset bitmap has c2c.cpus_cnt bits from env->nr_cpus_avail */ + if (sample->cpu >= (unsigned int)c2c.cpus_cnt) + return; + __set_bit(sample->cpu, c2c_he->cpuset); } @@ -262,6 +266,10 @@ static void c2c_he__set_node(struct c2c_hist_entry *c2c_he, if (WARN_ONCE(node < 0, "WARNING: failed to find node\n")) return; + /* nodeset bitmap has c2c.nodes_cnt bits from env->nr_numa_nodes */ + if (node >= c2c.nodes_cnt) + return; + __set_bit(node, c2c_he->nodeset); if (c2c_he->paddr != sample->phys_addr) { @@ -391,7 +399,12 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, * Doing node stats only for single callchain data. */ int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu; - int node = c2c.cpu2node[cpu]; + int node; + + /* cpu2node[] has c2c.cpus_cnt entries; large u32 wraps signed negative */ + if (cpu < 0 || cpu >= c2c.cpus_cnt) + cpu = 0; + node = c2c.cpu2node[cpu]; c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2, machine->env); if (!c2c_hists) { @@ -410,7 +423,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, c2c_he = container_of(he, struct c2c_hist_entry, he); c2c_add_stats(&c2c_he->stats, &stats); c2c_add_stats(&c2c_hists->stats, &stats); - c2c_add_stats(&c2c_he->node_stats[node], &stats); + /* node_stats[] has c2c.nodes_cnt entries */ + if (node >= 0 && node < c2c.nodes_cnt) + c2c_add_stats(&c2c_he->node_stats[node], &stats); compute_stats(c2c_he, &stats, sample->weight); From 5fb2e6ad8c5d6b3b380f94c7456595511f3731be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:06:30 -0300 Subject: [PATCH 2970/5214] perf c2c: Bounds-check CPU IDs in setup_nodes() topology loop setup_nodes() iterates CPU maps from the perf.data topology header and uses cpu.cpu directly as an array index into cpu2node[] (allocated with c2c.cpus_cnt = env->nr_cpus_avail entries) and __set_bit(cpu.cpu, set) (bitmap also sized to c2c.cpus_cnt). A crafted perf.data with topology CPU IDs exceeding nr_cpus_avail causes out-of-bounds heap writes into both the cpu2node array and the per-node bitmap. Add a bounds check to skip CPU IDs that fall outside the valid range. Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index f060dfbe11c2..cfc1ebe8c0af 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2371,6 +2371,10 @@ static int setup_nodes(struct perf_session *session) nodes[node] = set; perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { + /* topology CPU IDs from perf.data may exceed nr_cpus_avail */ + if (cpu.cpu < 0 || cpu.cpu >= c2c.cpus_cnt) + continue; + __set_bit(cpu.cpu, set); if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) From cda5a94ad9181cd60cbf04be11d524201bf489a2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:12:08 -0300 Subject: [PATCH 2971/5214] perf sched: Clean up idle_threads entry on init failure get_idle_thread() allocates a thread via thread__new() and stores it in idle_threads[cpu], then calls init_idle_thread() to set up the private data. If init_idle_thread() fails (e.g. OOM for the idle_thread_runtime struct), the function returns NULL but leaves the partially initialized thread in idle_threads[cpu]. On subsequent calls for the same CPU, get_idle_thread() finds a non-NULL idle_threads[cpu], skips allocation, and returns thread__get() on a thread that has no priv data. Callers then get a thread whose thread__priv() returns NULL, leading to unexpected behavior. Release the thread and reset the slot to NULL on init failure so the entry doesn't persist in a corrupted state. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 21fb820b625b..e4378cc9ab3e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2514,8 +2514,11 @@ static struct thread *get_idle_thread(int cpu) idle_threads[cpu] = thread__new(0, 0); if (idle_threads[cpu]) { - if (init_idle_thread(idle_threads[cpu]) < 0) + if (init_idle_thread(idle_threads[cpu]) < 0) { + /* clean up so next call doesn't find a half-initialized thread */ + thread__zput(idle_threads[cpu]); return NULL; + } } } From c9b3054c99cafd5f5f92158101760992a83e5a5e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 19:01:35 -0300 Subject: [PATCH 2972/5214] perf sched: Use is_idle_sample() for idle thread runtime cast guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit timehist_sched_change_event() uses thread__tid(thread) == 0 to decide whether to cast thread_runtime to idle_thread_runtime. However, a crafted perf.data can set common_pid=0 and common_tid=0 (the perf_sample fields) while prev_pid != 0 (the tracepoint field). is_idle_sample() returns false (it checks prev_pid for sched_switch), so timehist_get_thread() goes through machine__findnew_thread() and returns the machine's TID 0 thread — whose priv data is a regular thread_runtime, not the larger idle_thread_runtime allocated by init_idle_thread(). The subsequent cast to idle_thread_runtime reads past the thread_runtime allocation, accessing itr->last_thread, itr->cursor, and itr->callchain from adjacent heap memory. Writing to itr->last_thread corrupts the heap; calling thread__put() on the OOB value frees an arbitrary pointer. Replace the thread__tid() == 0 check with is_idle_sample(), which uses the tracepoint-specific prev_pid field and correctly identifies whether the sample originated from an idle thread with idle_thread_runtime priv. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index e4378cc9ab3e..4600d70b4861 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2902,7 +2902,13 @@ static int timehist_sched_change_event(const struct perf_tool *tool, t = ptime->end; } - if (!sched->idle_hist || thread__tid(thread) == 0) { + /* + * Use is_idle_sample() not thread__tid() == 0: a crafted perf.data + * can set common_pid=0 with prev_pid!=0, giving us a machine thread + * whose priv is thread_runtime, not idle_thread_runtime — the cast + * below would read past the allocation. + */ + if (!sched->idle_hist || is_idle_sample(sample)) { if (!cpu_list || (sample->cpu < MAX_NR_CPUS && test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); From 662d56d48e527ee21a0b03082ee318258a6f7919 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:15:58 -0300 Subject: [PATCH 2973/5214] perf sched: Fix thread reference leak in idle hist processing timehist_sched_change_event() sets itr->last_thread to NULL at the end of idle hist processing without calling thread__put() first. The thread reference was acquired via thread__get() in timehist_get_thread() (line 2581), so every idle context switch leaks a thread reference when --idle-hist is active. Use thread__zput() to properly release the reference before clearing the pointer. Fixes: 5d8f17fb5822 ("perf sched timehist: Add -I/--idle-hist option") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 4600d70b4861..c0cd3cbb602a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2940,7 +2940,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, if (itr->cursor.nr) callchain_append(&itr->callchain, &itr->cursor, t - tprev); - itr->last_thread = NULL; + thread__zput(itr->last_thread); } if (!sched->summary_only) From a99d6394cd48fed75b1d24733d5afe6837a61a3f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:17:03 -0300 Subject: [PATCH 2974/5214] perf sched: Use thread__put() in free_idle_threads() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit free_idle_threads() calls thread__delete() directly instead of thread__put(), bypassing the reference counting lifecycle. Under REFCNT_CHECKING builds, this leaks the pointer handle since thread__delete() frees the object without going through the refcount wrapper. The idle threads are created via thread__new() (refcount=1) in get_idle_thread(). Callers get additional references via thread__get() which they release with thread__put(). free_idle_threads() drops the base reference — thread__put() is the correct call, matching the thread__new() acquisition. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c0cd3cbb602a..732c65008a8a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2481,7 +2481,7 @@ static void free_idle_threads(void) if (itr) thread__put(itr->last_thread); - thread__delete(idle); + thread__put(idle); } } From 75eafe4a3a93a0143a20c0cc286bfb9008ac1478 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:26:28 -0300 Subject: [PATCH 2975/5214] perf sched: Replace BUG_ON and add NULL checks in replay event helpers get_new_event() has three issues: 1. The zalloc() result is dereferenced without a NULL check, crashing on allocation failure. 2. BUG_ON(!task->atoms) kills the process when realloc() fails. Since perf.data is untrusted input, this should be a graceful error. 3. The realloc pattern assigns directly to task->atoms, losing the old pointer on failure. task->nr_events is also incremented before the realloc, leaving corrupted state on failure. Fix get_new_event() to: - Check the zalloc() result before dereferencing - Use a temporary for realloc() to avoid losing the old pointer - Increment nr_events only after successful realloc - Return NULL instead of calling BUG_ON on failure Also fix add_sched_event_wakeup() where zalloc() for wait_sem is passed to sem_init() without a NULL check. Update all callers (add_sched_event_run, add_sched_event_wakeup, add_sched_event_sleep) to handle NULL returns by returning early. The replay may produce incomplete output on OOM but will not crash. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 732c65008a8a..b7ccdc6a985d 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -365,14 +365,25 @@ get_new_event(struct task_desc *task, u64 timestamp) struct sched_atom *event = zalloc(sizeof(*event)); unsigned long idx = task->nr_events; size_t size; + struct sched_atom **atoms_p; + + if (event == NULL) { + pr_err("ERROR: sched: failed to allocate event\n"); + return NULL; + } event->timestamp = timestamp; event->nr = idx; + size = sizeof(struct sched_atom *) * (task->nr_events + 1); + atoms_p = realloc(task->atoms, size); + if (!atoms_p) { + pr_err("ERROR: sched: failed to grow atoms array\n"); + free(event); + return NULL; + } + task->atoms = atoms_p; task->nr_events++; - size = sizeof(struct sched_atom *) * task->nr_events; - task->atoms = realloc(task->atoms, size); - BUG_ON(!task->atoms); task->atoms[idx] = event; @@ -403,6 +414,8 @@ static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task } event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_RUN; event->duration = duration; @@ -416,6 +429,8 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t struct sched_atom *event, *wakee_event; event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_WAKEUP; event->wakee = wakee; @@ -430,6 +445,10 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t } wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); + if (!wakee_event->wait_sem) { + pr_err("ERROR: sched: failed to allocate semaphore\n"); + return; + } sem_init(wakee_event->wait_sem, 0, 0); event->wait_sem = wakee_event->wait_sem; @@ -441,6 +460,9 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta { struct sched_atom *event = get_new_event(task, timestamp); + if (event == NULL) + return; + event->type = SCHED_EVENT_SLEEP; sched->nr_sleep_events++; From 25627346b10e6a564610ea2c49dc6dd54812226d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:03:29 -0300 Subject: [PATCH 2976/5214] perf mmap: Fix NULL deref in aio cleanup on alloc failure perf_mmap__aio_mmap() sets map->aio.nr_cblocks before allocating the data array. If calloc() for aiocb or cblocks fails before the data array is allocated, the return -1 path leads to perf_mmap__aio_munmap() which loops nr_cblocks times calling perf_mmap__aio_free(). Both versions of perf_mmap__aio_free() (NUMA and non-NUMA) dereference map->aio.data[idx] without checking if data is NULL, causing a NULL pointer dereference. Add NULL checks for map->aio.data at the top of both perf_mmap__aio_free() variants so the cleanup path is safe when allocation fails partway through perf_mmap__aio_mmap(). Fixes: d3d1af6f011a553a ("perf record: Enable asynchronous trace writing") Reported-by: sashiko-bot Cc: Alexey Budankov Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/mmap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 4404a99eee45..d64aec6c7c84 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -89,10 +89,10 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { - if (map->aio.data[idx]) { - munmap(map->aio.data[idx], mmap__mmap_len(map)); - map->aio.data[idx] = NULL; - } + if (!map->aio.data || !map->aio.data[idx]) + return; + munmap(map->aio.data[idx], mmap__mmap_len(map)); + map->aio.data[idx] = NULL; } static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, int affinity) @@ -141,6 +141,8 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { + if (!map->aio.data) + return; zfree(&(map->aio.data[idx])); } From afa4363a91a19dff65dceb7fbce7bba689bbc854 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:17:45 -0300 Subject: [PATCH 2977/5214] perf stat: Introduce perf_env__get_cpu_topology() to guard NULL env->cpu process_cpu_topology() in header.c frees env->cpu on old-format perf.data files that predate topology information, but leaves nr_cpus_avail set. The six perf_env__get_*_aggr_by_cpu() functions in builtin-stat.c pass the bounds check but dereference a NULL env->cpu pointer, crashing on old recordings. Introduce perf_env__get_cpu_topology() as a safe accessor that validates env->cpu, cpu.cpu >= 0, and cpu.cpu < nr_cpus_avail in one place, returning a struct cpu_topology_map pointer or NULL. Convert all six topology aggregation callbacks to use it. Fixes: 88031a0de7d68d13 ("perf stat: Switch to cpu version of cpu_map__get()") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 51 +++++++++++++++++++++------------------ tools/perf/util/env.h | 14 +++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9a045811c419..a04466ea3b0a 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1637,10 +1637,10 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) - id.socket = env->cpu[cpu.cpu].socket_id; + if (topo) + id.socket = topo->socket_id; return id; } @@ -1649,15 +1649,16 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * die_id is relative to socket, so start * with the socket ID and then add die to * make a unique ID. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; } return id; @@ -1705,12 +1706,13 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; perf_env__get_cache_id_for_cpu(cpu, env, cache_level, &id); } @@ -1722,11 +1724,12 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; + if (topo) { + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; } return id; @@ -1736,16 +1739,17 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; - id.core = env->cpu[cpu.cpu].core_id; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; + id.core = topo->core_id; } return id; @@ -1755,18 +1759,19 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket and die, * we need a global id. So we set * socket, die id and core id */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.core = env->cpu[cpu.cpu].core_id; - id.cpu = cpu; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.core = topo->core_id; } + id.cpu = cpu; return id; } diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 7621d1f73b83..7acca39b42ff 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -187,6 +187,20 @@ const char *perf_env__pmu_mappings(struct perf_env *env); int perf_env__read_cpu_topology_map(struct perf_env *env); +/* + * Safe accessor for env->cpu[] topology array. env->cpu can be NULL when + * reading old-format perf.data that predates topology information — + * process_cpu_topology() in header.c frees it while nr_cpus_avail remains + * set, so callers must not index env->cpu[] without this check. + */ +static inline struct cpu_topology_map * +perf_env__get_cpu_topology(struct perf_env *env, struct perf_cpu cpu) +{ + if (env->cpu && cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) + return &env->cpu[cpu.cpu]; + return NULL; +} + void cpu_cache_level__free(struct cpu_cache_level *cache); uint16_t perf_env__e_machine_nocache(struct perf_env *env, uint32_t *e_flags); From 5e5e6196d737c5be03d20647428316b36621608d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:19:10 -0300 Subject: [PATCH 2978/5214] perf c2c: Fix use-after-free in he__get_c2c_hists() error path he__get_c2c_hists() assigns c2c_he->hists before calling c2c_hists__init(). If init fails, the error path calls free(hists) but leaves c2c_he->hists pointing to freed memory. On teardown, c2c_he_free() finds the non-NULL pointer and calls hists__delete_entries() on it, causing a use-after-free. Set c2c_he->hists to NULL before freeing so teardown skips the already-freed allocation. Fixes: b2252ae67b687d2b ("perf c2c report: Decode c2c_stats for hist entries") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index cfc1ebe8c0af..e205f58b2f3d 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -225,6 +225,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + c2c_he->hists = NULL; free(hists); return NULL; } From e2496db45bfd8dfb6154ec415798fee330f1cc0a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:21:32 -0300 Subject: [PATCH 2979/5214] perf timechart: Fix cpu2y() OOB read on untrusted CPU index cpu2y() indexes topology_map[cpu] without bounds checking. The array is allocated with nr_cpus entries (from env->nr_cpus_online), but callers pass sample CPU values from perf.data which can exceed that size with cross-machine recordings. Track the topology_map allocation size and bounds-check the CPU argument in cpu2y() before indexing. Out-of-bounds CPUs fall back to the identity mapping (cpu2slot(cpu)), which is the same behavior as when no topology is available. Fixes: c507999790438cde ("perf timechart: Add support for topology") Reported-by: sashiko-bot Cc: Stanislav Fomichev Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/svghelper.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index e360e7736c7b..826bd2577344 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -47,13 +47,13 @@ static double cpu2slot(int cpu) } static int *topology_map; +static int topology_map_size; static double cpu2y(int cpu) { - if (topology_map) + if (topology_map && cpu >= 0 && cpu < topology_map_size) return cpu2slot(topology_map[cpu]) * SLOT_MULT; - else - return cpu2slot(cpu) * SLOT_MULT; + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -736,7 +736,8 @@ static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) return -1; perf_cpu_map__for_each_cpu(cpu, idx, map) { - if (cpu.cpu >= nr_cpus) { + /* perf_cpu_map__new("") returns cpu.cpu == -1 */ + if (cpu.cpu < 0 || cpu.cpu >= nr_cpus) { ret = -1; break; } @@ -794,6 +795,7 @@ int svg_build_topology_map(struct perf_env *env) fprintf(stderr, "topology: no memory\n"); goto exit; } + topology_map_size = nr_cpus; for (i = 0; i < nr_cpus; i++) topology_map[i] = -1; From 33fa2bf5608fc36bc25231592145f4738f14f11b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:25:40 -0300 Subject: [PATCH 2980/5214] perf tools: Fix int16_t truncation of max_cpu_num in set_max_cpu_num() set_max_cpu_num() assigns the sysfs "possible" CPU count to max_cpu_num.cpu which is int16_t (struct perf_cpu). On systems with >32767 possible CPUs the value silently truncates, potentially wrapping negative. This causes cpunode_map to be underallocated and subsequent cpu__get_node() calls to read out of bounds. The matching check for max_present_cpu_num was added by commit c760174401f6 ("perf cpumap: Reduce cpu size from int to int16_t") but max_cpu_num was missed. Add the same INT16_MAX guard. Fixes: c760174401f605cf ("perf cpumap: Reduce cpu size from int to int16_t") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index d3432622b2ad..21fa781b03cc 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -494,6 +494,16 @@ static void set_max_cpu_num(void) if (ret) goto out; + /* + * struct perf_cpu.cpu is int16_t (libperf ABI) — clamp to avoid + * truncation to negative. See tools/lib/perf/TODO for the ABI + * widening plan. + */ + if (max > INT16_MAX) { + pr_warning("WARNING: max possible cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; + } max_cpu_num.cpu = max; /* get the highest present cpu number for a sparse allocation */ @@ -506,11 +516,12 @@ static void set_max_cpu_num(void) ret = get_max_num(path, &max); if (!ret && max > INT16_MAX) { - pr_err("Read out of bounds max cpus of %d\n", max); - ret = -1; + pr_warning("WARNING: max present cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; } if (!ret) - max_present_cpu_num.cpu = (int16_t)max; + max_present_cpu_num.cpu = max; out: if (ret) pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); @@ -647,7 +658,9 @@ int cpu__setup_cpunode_map(void) while ((dent2 = readdir(dir2)) != NULL) { if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) continue; - cpunode_map[cpu] = mem; + /* cpunode_map allocated for max_cpu_num entries */ + if (cpu < (unsigned int)max_cpu_num.cpu) + cpunode_map[cpu] = mem; } closedir(dir2); } From c3e51ed45ffa7547495a851e33ce332f81ef3665 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:33:41 -0300 Subject: [PATCH 2981/5214] perf sched: Free callchain nodes in idle thread cleanup free_idle_threads() relies on the thread priv destructor (free()) to clean up idle_thread_runtime structs. But free() doesn't walk the callchain_cursor linked list or the callchain_root tree allocated by callchain_cursor__copy() and callchain_append() during --idle-hist processing. Every idle thread with callchain data leaks these nodes. Introduce callchain_cursor_cleanup() to free the cursor's linked list of callchain_cursor_node entries, and call it together with free_callchain() in free_idle_threads() before thread__put(). Fixes: 225b24f569980ac9 ("perf sched timehist: Save callchain when entering idle") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 5 ++++- tools/perf/util/callchain.c | 15 +++++++++++++++ tools/perf/util/callchain.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index b7ccdc6a985d..1ff01f03d2ad 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2500,8 +2500,11 @@ static void free_idle_threads(void) struct idle_thread_runtime *itr; itr = thread__priv(idle); - if (itr) + if (itr) { thread__put(itr->last_thread); + free_callchain(&itr->callchain); + callchain_cursor_cleanup(&itr->cursor); + } thread__put(idle); } diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 8981ae879ebb..31c675cbab63 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1578,6 +1578,21 @@ void free_callchain(struct callchain_root *root) free_callchain_node(&root->node); } +void callchain_cursor_cleanup(struct callchain_cursor *cursor) +{ + struct callchain_cursor_node *node, *next; + + callchain_cursor_reset(cursor); + + for (node = cursor->first; node; node = next) { + next = node->next; + free(node); + } + cursor->first = NULL; + cursor->last = &cursor->first; + cursor->curr = NULL; +} + static u64 decay_callchain_node(struct callchain_node *node) { struct callchain_node *child; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 0eb5d7a1a41d..8b1e405d1cdf 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -286,6 +286,7 @@ int callchain_list_counts__printf_value(struct callchain_list *clist, FILE *fp, char *bf, int bfsize); void free_callchain(struct callchain_root *root); +void callchain_cursor_cleanup(struct callchain_cursor *cursor); void decay_callchain(struct callchain_root *root); int callchain_node__make_parent_list(struct callchain_node *node); From f54ce06049bf716f6b1e3a78e72360d583ea2acb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 14:42:09 -0300 Subject: [PATCH 2982/5214] libperf: Document struct perf_cpu int16_t ABI limitation struct perf_cpu.cpu is int16_t, limiting perf to 32767 CPUs. This is part of the libperf ABI (returned by value from perf_cpu_map__cpu() and friends), so widening it requires an ABI bump. Add a comment on the struct definition noting this, and create a TODO file to collect future ABI changes so they can be batched into a single version bump. Cc: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/TODO | 22 ++++++++++++++++++++++ tools/lib/perf/include/perf/cpumap.h | 8 +++++++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tools/lib/perf/TODO diff --git a/tools/lib/perf/TODO b/tools/lib/perf/TODO new file mode 100644 index 000000000000..486dd95dc572 --- /dev/null +++ b/tools/lib/perf/TODO @@ -0,0 +1,22 @@ +Future ABI changes +================== + +This file collects items that require a libperf ABI bump. Each entry +should describe the current limitation, the desired end state, and the +scope of the change so that a future ABI revision can batch them +together. + +1. Widen struct perf_cpu.cpu from int16_t to int + - Current limit: 32767 CPUs. No architecture exceeds this today + (x86_64 max is 8192, arm64 is 4096), but NR_CPUS limits keep + growing. perf clamps to INT16_MAX in set_max_cpu_num() as a + safety net. + - Scope: struct perf_cpu is embedded everywhere — perf_cpu_map__cpu(), + perf_cpu_map__min(), perf_cpu_map__max(), perf_cpu_map__has(), the + for_each_cpu macros, and all internal callers. The perf_cpu_map + internal array (RC_CHK_ACCESS(map)->map[]) stores struct perf_cpu + directly. Widening changes the struct layout and every function + that returns or accepts struct perf_cpu by value. + - Migration: bump LIBPERF version in libperf.map, audit all + sizeof(struct perf_cpu) assumptions, update perf.data + serialization if needed. diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h index a1dd25db65b6..e1a0b0d27210 100644 --- a/tools/lib/perf/include/perf/cpumap.h +++ b/tools/lib/perf/include/perf/cpumap.h @@ -6,7 +6,13 @@ #include #include -/** A wrapper around a CPU to avoid confusion with the perf_cpu_map's map's indices. */ +/** + * struct perf_cpu - wrapper around a CPU number. + * @cpu: CPU number, -1 for the "any CPU"/dummy value. + * + * int16_t limits this to 32767 CPUs. Widening to int requires a libperf + * ABI bump — see tools/lib/perf/TODO for the full scope. + */ struct perf_cpu { int16_t cpu; }; From 92e6b06858caf79227c19a4846b47a4e700e47d4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 2 Jun 2026 22:42:17 -0700 Subject: [PATCH 2983/5214] Input: xilinx_ps2 - remove driver Remove the Xilinx XPS PS/2 controller driver. This driver supports an old Xilinx EDK IP core that is no longer in active use. The hardware is not available on modern platforms, and the driver has no users here. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Acked-by: Michal Simek Link: https://patch.msgid.link/20260603054217.442016-1-rosenp@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/serio/Kconfig | 10 - drivers/input/serio/Makefile | 1 - drivers/input/serio/xilinx_ps2.c | 363 ------------------------------- 3 files changed, 374 deletions(-) delete mode 100644 drivers/input/serio/xilinx_ps2.c diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 5f15a6462056..bacab1f58400 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -188,16 +188,6 @@ config SERIO_RAW To compile this driver as a module, choose M here: the module will be called serio_raw. -config SERIO_XILINX_XPS_PS2 - tristate "Xilinx XPS PS/2 Controller Support" - depends on PPC || MICROBLAZE - help - This driver supports XPS PS/2 IP from the Xilinx EDK on - PowerPC platform. - - To compile this driver as a module, choose M here: the - module will be called xilinx_ps2. - config SERIO_ALTERA_PS2 tristate "Altera UP PS/2 controller" depends on HAS_IOMEM diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 8ab98f4aa28d..7f5137dc02e2 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_SERIO_SGI_IOC3) += ioc3kbd.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o -obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_APBPS2) += apbps2.o diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c deleted file mode 100644 index 411d55ca1a66..000000000000 --- a/drivers/input/serio/xilinx_ps2.c +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Xilinx XPS PS/2 device driver - * - * (c) 2005 MontaVista Software, Inc. - * (c) 2008 Xilinx, Inc. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "xilinx_ps2" - -/* Register offsets for the xps2 device */ -#define XPS2_SRST_OFFSET 0x00000000 /* Software Reset register */ -#define XPS2_STATUS_OFFSET 0x00000004 /* Status register */ -#define XPS2_RX_DATA_OFFSET 0x00000008 /* Receive Data register */ -#define XPS2_TX_DATA_OFFSET 0x0000000C /* Transmit Data register */ -#define XPS2_GIER_OFFSET 0x0000002C /* Global Interrupt Enable reg */ -#define XPS2_IPISR_OFFSET 0x00000030 /* Interrupt Status register */ -#define XPS2_IPIER_OFFSET 0x00000038 /* Interrupt Enable register */ - -/* Reset Register Bit Definitions */ -#define XPS2_SRST_RESET 0x0000000A /* Software Reset */ - -/* Status Register Bit Positions */ -#define XPS2_STATUS_RX_FULL 0x00000001 /* Receive Full */ -#define XPS2_STATUS_TX_FULL 0x00000002 /* Transmit Full */ - -/* - * Bit definitions for ISR/IER registers. Both the registers have the same bit - * definitions and are only defined once. - */ -#define XPS2_IPIXR_WDT_TOUT 0x00000001 /* Watchdog Timeout Interrupt */ -#define XPS2_IPIXR_TX_NOACK 0x00000002 /* Transmit No ACK Interrupt */ -#define XPS2_IPIXR_TX_ACK 0x00000004 /* Transmit ACK (Data) Interrupt */ -#define XPS2_IPIXR_RX_OVF 0x00000008 /* Receive Overflow Interrupt */ -#define XPS2_IPIXR_RX_ERR 0x00000010 /* Receive Error Interrupt */ -#define XPS2_IPIXR_RX_FULL 0x00000020 /* Receive Data Interrupt */ - -/* Mask for all the Transmit Interrupts */ -#define XPS2_IPIXR_TX_ALL (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK) - -/* Mask for all the Receive Interrupts */ -#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \ - XPS2_IPIXR_RX_FULL) - -/* Mask for all the Interrupts */ -#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \ - XPS2_IPIXR_WDT_TOUT) - -/* Global Interrupt Enable mask */ -#define XPS2_GIER_GIE_MASK 0x80000000 - -struct xps2data { - int irq; - spinlock_t lock; - void __iomem *base_address; /* virt. address of control registers */ - unsigned int flags; - struct serio *serio; /* serio */ - struct device *dev; -}; - -/************************************/ -/* XPS PS/2 data transmission calls */ -/************************************/ - -/** - * xps2_recv() - attempts to receive a byte from the PS/2 port. - * @drvdata: pointer to ps2 device private data structure - * @byte: address where the read data will be copied - * - * If there is any data available in the PS/2 receiver, this functions reads - * the data, otherwise it returns error. - */ -static int xps2_recv(struct xps2data *drvdata, u8 *byte) -{ - u32 sr; - int status = -1; - - /* If there is data available in the PS/2 receiver, read it */ - sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); - if (sr & XPS2_STATUS_RX_FULL) { - *byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET); - status = 0; - } - - return status; -} - -/*********************/ -/* Interrupt handler */ -/*********************/ -static irqreturn_t xps2_interrupt(int irq, void *dev_id) -{ - struct xps2data *drvdata = dev_id; - u32 intr_sr; - u8 c; - int status; - - /* Get the PS/2 interrupts and clear them */ - intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET); - out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr); - - /* Check which interrupt is active */ - if (intr_sr & XPS2_IPIXR_RX_OVF) - dev_warn(drvdata->dev, "receive overrun error\n"); - - if (intr_sr & XPS2_IPIXR_RX_ERR) - drvdata->flags |= SERIO_PARITY; - - if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT)) - drvdata->flags |= SERIO_TIMEOUT; - - if (intr_sr & XPS2_IPIXR_RX_FULL) { - status = xps2_recv(drvdata, &c); - - /* Error, if a byte is not received */ - if (status) { - dev_err(drvdata->dev, - "wrong rcvd byte count (%d)\n", status); - } else { - serio_interrupt(drvdata->serio, c, drvdata->flags); - drvdata->flags = 0; - } - } - - return IRQ_HANDLED; -} - -/*******************/ -/* serio callbacks */ -/*******************/ - -/** - * sxps2_write() - sends a byte out through the PS/2 port. - * @pserio: pointer to the serio structure of the PS/2 port - * @c: data that needs to be written to the PS/2 port - * - * This function checks if the PS/2 transmitter is empty and sends a byte. - * Otherwise it returns error. Transmission fails only when nothing is connected - * to the PS/2 port. Thats why, we do not try to resend the data in case of a - * failure. - */ -static int sxps2_write(struct serio *pserio, unsigned char c) -{ - struct xps2data *drvdata = pserio->port_data; - u32 sr; - - guard(spinlock_irqsave)(&drvdata->lock); - - /* If the PS/2 transmitter is empty send a byte of data */ - sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET); - if (sr & XPS2_STATUS_TX_FULL) - return -EAGAIN; - - out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c); - return 0; -} - -/** - * sxps2_open() - called when a port is opened by the higher layer. - * @pserio: pointer to the serio structure of the PS/2 device - * - * This function requests irq and enables interrupts for the PS/2 device. - */ -static int sxps2_open(struct serio *pserio) -{ - struct xps2data *drvdata = pserio->port_data; - int error; - u8 c; - - error = request_irq(drvdata->irq, &xps2_interrupt, 0, - DRIVER_NAME, drvdata); - if (error) { - dev_err(drvdata->dev, - "Couldn't allocate interrupt %d\n", drvdata->irq); - return error; - } - - /* start reception by enabling the interrupts */ - out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK); - out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL); - (void)xps2_recv(drvdata, &c); - - return 0; /* success */ -} - -/** - * sxps2_close() - frees the interrupt. - * @pserio: pointer to the serio structure of the PS/2 device - * - * This function frees the irq and disables interrupts for the PS/2 device. - */ -static void sxps2_close(struct serio *pserio) -{ - struct xps2data *drvdata = pserio->port_data; - - /* Disable the PS2 interrupts */ - out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00); - out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00); - free_irq(drvdata->irq, drvdata); -} - -/** - * xps2_of_probe - probe method for the PS/2 device. - * @ofdev: pointer to OF device structure - * - * This function probes the PS/2 device in the device tree. - * It initializes the driver data structure and the hardware. - * It returns 0, if the driver is bound to the PS/2 device, or a negative - * value if there is an error. - */ -static int xps2_of_probe(struct platform_device *ofdev) -{ - struct resource r_mem; /* IO mem resources */ - struct xps2data *drvdata; - struct serio *serio; - struct device *dev = &ofdev->dev; - resource_size_t remap_size, phys_addr; - unsigned int irq; - int error; - - dev_info(dev, "Device Tree Probing \'%pOFn\'\n", dev->of_node); - - /* Get iospace for the device */ - error = of_address_to_resource(dev->of_node, 0, &r_mem); - if (error) { - dev_err(dev, "invalid address\n"); - return error; - } - - /* Get IRQ for the device */ - irq = irq_of_parse_and_map(dev->of_node, 0); - if (!irq) { - dev_err(dev, "no IRQ found\n"); - return -ENODEV; - } - - drvdata = kzalloc_obj(*drvdata); - serio = kzalloc_obj(*serio); - if (!drvdata || !serio) { - error = -ENOMEM; - goto failed1; - } - - spin_lock_init(&drvdata->lock); - drvdata->irq = irq; - drvdata->serio = serio; - drvdata->dev = dev; - - phys_addr = r_mem.start; - remap_size = resource_size(&r_mem); - if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) { - dev_err(dev, "Couldn't lock memory region at 0x%08llX\n", - (unsigned long long)phys_addr); - error = -EBUSY; - goto failed1; - } - - /* Fill in configuration data and add them to the list */ - drvdata->base_address = ioremap(phys_addr, remap_size); - if (drvdata->base_address == NULL) { - dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n", - (unsigned long long)phys_addr); - error = -EFAULT; - goto failed2; - } - - /* Disable all the interrupts, just in case */ - out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0); - - /* - * Reset the PS2 device and abort any current transaction, - * to make sure we have the PS2 in a good state. - */ - out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET); - - dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n", - (unsigned long long)phys_addr, drvdata->base_address, - drvdata->irq); - - serio->id.type = SERIO_8042; - serio->write = sxps2_write; - serio->open = sxps2_open; - serio->close = sxps2_close; - serio->port_data = drvdata; - serio->dev.parent = dev; - snprintf(serio->name, sizeof(serio->name), - "Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr); - snprintf(serio->phys, sizeof(serio->phys), - "xilinxps2/serio at %08llX", (unsigned long long)phys_addr); - - serio_register_port(serio); - - platform_set_drvdata(ofdev, drvdata); - return 0; /* success */ - -failed2: - release_mem_region(phys_addr, remap_size); -failed1: - kfree(serio); - kfree(drvdata); - - return error; -} - -/** - * xps2_of_remove - unbinds the driver from the PS/2 device. - * @of_dev: pointer to OF device structure - * - * This function is called if a device is physically removed from the system or - * if the driver module is being unloaded. It frees any resources allocated to - * the device. - */ -static void xps2_of_remove(struct platform_device *of_dev) -{ - struct xps2data *drvdata = platform_get_drvdata(of_dev); - struct resource r_mem; /* IO mem resources */ - - serio_unregister_port(drvdata->serio); - iounmap(drvdata->base_address); - - /* Get iospace of the device */ - if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem)) - dev_err(drvdata->dev, "invalid address\n"); - else - release_mem_region(r_mem.start, resource_size(&r_mem)); - - kfree(drvdata); -} - -/* Match table for of_platform binding */ -static const struct of_device_id xps2_of_match[] = { - { .compatible = "xlnx,xps-ps2-1.00.a", }, - { /* end of list */ }, -}; -MODULE_DEVICE_TABLE(of, xps2_of_match); - -static struct platform_driver xps2_of_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = xps2_of_match, - }, - .probe = xps2_of_probe, - .remove = xps2_of_remove, -}; -module_platform_driver(xps2_of_driver); - -MODULE_AUTHOR("Xilinx, Inc."); -MODULE_DESCRIPTION("Xilinx XPS PS/2 driver"); -MODULE_LICENSE("GPL"); - From 391a47efbb31c48c839d3efde7756e0a00bf11f4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 12:24:15 -0700 Subject: [PATCH 2984/5214] Input: apbps2 - simplify resource mapping and IRQ retrieval Simplify resource mapping by using devm_platform_ioremap_resource() instead of the longer devm_platform_get_and_ioremap_resource() helper as the last argument is NULL. Additionally, use platform_get_irq() to retrieve the interrupt instead of irq_of_parse_and_map() and propagate its error code on failure. irq_of_parse_and_map() requires irq_dispose_mapping, which is missing. Assisted-by: Antigravity:Gemini-3.5-Flash Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260603192415.6679-1-rosenp@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/serio/apbps2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c index 0aa4ab00af35..5f21acdd4113 100644 --- a/drivers/input/serio/apbps2.c +++ b/drivers/input/serio/apbps2.c @@ -140,7 +140,7 @@ static int apbps2_of_probe(struct platform_device *ofdev) } /* Find device address */ - priv->regs = devm_platform_get_and_ioremap_resource(ofdev, 0, NULL); + priv->regs = devm_platform_ioremap_resource(ofdev, 0); if (IS_ERR(priv->regs)) return PTR_ERR(priv->regs); @@ -148,7 +148,10 @@ static int apbps2_of_probe(struct platform_device *ofdev) iowrite32be(0, &priv->regs->ctrl); /* IRQ */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + irq = platform_get_irq(ofdev, 0); + if (irq < 0) + return irq; + err = devm_request_irq(&ofdev->dev, irq, apbps2_isr, IRQF_SHARED, "apbps2", priv); if (err) { From 44098c590bc74b98c2ba175d653ea496d342440f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 29 May 2026 22:52:39 -0700 Subject: [PATCH 2985/5214] Input: atlas_btns - modernize the driver Rework the driver to use modern style: - Remove global state by introducing a per-device structure - Use devm for resource management (input device allocation) - Use dev_err_probe() for error reporting in the probe path - Clean up unused definitions and headers. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/ahp6qt_viW3l-NlX@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/atlas_btns.c | 109 ++++++++++++++++---------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 835ad45a9d65..5805a1a3bb05 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -15,17 +15,18 @@ #include #include #include -#include #define ACPI_ATLAS_NAME "Atlas ACPI" -#define ACPI_ATLAS_CLASS "Atlas" -static unsigned short atlas_keymap[16]; -static struct input_dev *input_dev; +struct atlas_btns { + struct input_dev *input_dev; + unsigned short keymap[16]; +}; /* button handling code */ static acpi_status acpi_atlas_button_setup(acpi_handle region_handle, - u32 function, void *handler_context, void **return_context) + u32 function, void *handler_context, + void **return_context) { *return_context = (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL; @@ -34,33 +35,37 @@ static acpi_status acpi_atlas_button_setup(acpi_handle region_handle, } static acpi_status acpi_atlas_button_handler(u32 function, - acpi_physical_address address, - u32 bit_width, u64 *value, - void *handler_context, void *region_context) + acpi_physical_address address, + u32 bit_width, u64 *value, + void *handler_context, + void *region_context) { - acpi_status status; + struct atlas_btns *atlas = region_context; if (function == ACPI_WRITE) { int code = address & 0x0f; int key_down = !(address & 0x10); - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, atlas_keymap[code], key_down); - input_sync(input_dev); + input_event(atlas->input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(atlas->input_dev, atlas->keymap[code], + key_down); + input_sync(atlas->input_dev); - status = AE_OK; - } else { - pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n", - function, (unsigned long)address, (u32)*value); - status = AE_BAD_PARAMETER; + return AE_OK; } - return status; + dev_warn(atlas->input_dev->dev.parent, + "unexpected function: function=%x,address=%lx,value=%x\n", + function, (unsigned long)address, (u32)*value); + + return AE_BAD_PARAMETER; } static int atlas_acpi_button_probe(struct platform_device *pdev) { struct acpi_device *device; + struct atlas_btns *atlas; + struct input_dev *input_dev; acpi_status status; int i; int err; @@ -69,65 +74,62 @@ static int atlas_acpi_button_probe(struct platform_device *pdev) if (!device) return -ENODEV; - input_dev = input_allocate_device(); - if (!input_dev) { - pr_err("unable to allocate input device\n"); + atlas = devm_kzalloc(&pdev->dev, sizeof(*atlas), GFP_KERNEL); + if (!atlas) return -ENOMEM; - } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + atlas->input_dev = input_dev; input_dev->name = "Atlas ACPI button driver"; input_dev->phys = "ASIM0000/atlas/input0"; input_dev->id.bustype = BUS_HOST; - input_dev->keycode = atlas_keymap; - input_dev->keycodesize = sizeof(unsigned short); - input_dev->keycodemax = ARRAY_SIZE(atlas_keymap); + input_dev->keycode = atlas->keymap; + input_dev->keycodesize = sizeof(atlas->keymap[0]); + input_dev->keycodemax = ARRAY_SIZE(atlas->keymap); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) { + for (i = 0; i < ARRAY_SIZE(atlas->keymap); i++) { if (i < 9) { - atlas_keymap[i] = KEY_F1 + i; - __set_bit(KEY_F1 + i, input_dev->keybit); - } else - atlas_keymap[i] = KEY_RESERVED; + atlas->keymap[i] = KEY_F1 + i; + input_set_capability(input_dev, EV_KEY, KEY_F1 + i); + } else { + atlas->keymap[i] = KEY_RESERVED; + } } err = input_register_device(input_dev); - if (err) { - pr_err("couldn't register input device\n"); - input_free_device(input_dev); - return err; - } + if (err) + return dev_err_probe(&pdev->dev, err, + "couldn't register input device\n"); /* hookup button handler */ status = acpi_install_address_space_handler(device->handle, - 0x81, &acpi_atlas_button_handler, - &acpi_atlas_button_setup, device); - if (ACPI_FAILURE(status)) { - pr_err("error installing addr spc handler\n"); - input_unregister_device(input_dev); - err = -EINVAL; - } + 0x81, + &acpi_atlas_button_handler, + &acpi_atlas_button_setup, + atlas); + if (ACPI_FAILURE(status)) + return dev_err_probe(&pdev->dev, -EINVAL, + "error installing addr spc handler\n"); - return err; + return 0; } static void atlas_acpi_button_remove(struct platform_device *pdev) { struct acpi_device *device = ACPI_COMPANION(&pdev->dev); - acpi_status status; - status = acpi_remove_address_space_handler(device->handle, - 0x81, &acpi_atlas_button_handler); - if (ACPI_FAILURE(status)) - pr_err("error removing addr spc handler\n"); - - input_unregister_device(input_dev); + acpi_remove_address_space_handler(device->handle, 0x81, + &acpi_atlas_button_handler); } static const struct acpi_device_id atlas_device_ids[] = { - {"ASIM0000", 0}, - {"", 0}, + { "ASIM0000" }, + { "" } }; MODULE_DEVICE_TABLE(acpi, atlas_device_ids); @@ -144,4 +146,3 @@ module_platform_driver(atlas_acpi_driver); MODULE_AUTHOR("Jaya Kumar"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Atlas button driver"); - From 9ae38c69196e7edd367fe55a3db676a33cc735dc Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:26 +0530 Subject: [PATCH 2986/5214] dt-bindings: clock: qcom: Add X1P42100 video clock controller Add device tree bindings for the video clock controller on Qualcomm X1P42100 (Purwa) SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-1-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/clock/qcom,sm8450-videocc.yaml | 3 ++ .../dt-bindings/clock/qcom,x1p42100-videocc.h | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,x1p42100-videocc.h diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml index 7bbf120d928c..5d77029bfaf8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml @@ -20,6 +20,7 @@ description: | include/dt-bindings/clock/qcom,sm8450-videocc.h include/dt-bindings/clock/qcom,sm8650-videocc.h include/dt-bindings/clock/qcom,sm8750-videocc.h + include/dt-bindings/clock/qcom,x1p42100-videocc.h properties: compatible: @@ -32,6 +33,7 @@ properties: - qcom,sm8650-videocc - qcom,sm8750-videocc - qcom,x1e80100-videocc + - qcom,x1p42100-videocc clocks: items: @@ -70,6 +72,7 @@ allOf: - qcom,sm8450-videocc - qcom,sm8550-videocc - qcom,sm8750-videocc + - qcom,x1p42100-videocc then: required: - required-opps diff --git a/include/dt-bindings/clock/qcom,x1p42100-videocc.h b/include/dt-bindings/clock/qcom,x1p42100-videocc.h new file mode 100644 index 000000000000..996408d1a0c3 --- /dev/null +++ b/include/dt-bindings/clock/qcom,x1p42100-videocc.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_VIDEO_CC_X1P42100_H +#define _DT_BINDINGS_CLK_QCOM_VIDEO_CC_X1P42100_H + +/* VIDEO_CC clocks */ +#define VIDEO_CC_MVS0_CLK 0 +#define VIDEO_CC_MVS0_CLK_SRC 1 +#define VIDEO_CC_MVS0_DIV_CLK_SRC 2 +#define VIDEO_CC_MVS0C_CLK 3 +#define VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC 4 +#define VIDEO_CC_MVS1_CLK 5 +#define VIDEO_CC_MVS1_CLK_SRC 6 +#define VIDEO_CC_MVS1_DIV_CLK_SRC 7 +#define VIDEO_CC_MVS1C_CLK 8 +#define VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC 9 +#define VIDEO_CC_PLL0 10 +#define VIDEO_CC_PLL1 11 +#define VIDEO_CC_MVS0_SHIFT_CLK 12 +#define VIDEO_CC_MVS0C_SHIFT_CLK 13 +#define VIDEO_CC_MVS1_SHIFT_CLK 14 +#define VIDEO_CC_MVS1C_SHIFT_CLK 15 +#define VIDEO_CC_XO_CLK_SRC 16 +#define VIDEO_CC_MVS0_BSE_CLK 17 +#define VIDEO_CC_MVS0_BSE_CLK_SRC 18 +#define VIDEO_CC_MVS0_BSE_DIV4_DIV_CLK_SRC 19 + +/* VIDEO_CC power domains */ +#define VIDEO_CC_MVS0C_GDSC 0 +#define VIDEO_CC_MVS0_GDSC 1 +#define VIDEO_CC_MVS1C_GDSC 2 +#define VIDEO_CC_MVS1_GDSC 3 + +/* VIDEO_CC resets */ +#define CVP_VIDEO_CC_INTERFACE_BCR 0 +#define CVP_VIDEO_CC_MVS0_BCR 1 +#define CVP_VIDEO_CC_MVS0C_BCR 2 +#define CVP_VIDEO_CC_MVS1_BCR 3 +#define CVP_VIDEO_CC_MVS1C_BCR 4 +#define VIDEO_CC_MVS0C_CLK_ARES 5 +#define VIDEO_CC_MVS1C_CLK_ARES 6 +#define VIDEO_CC_XO_CLK_ARES 7 +#define VIDEO_CC_MVS0_BSE_BCR 8 + +#endif From 97a5e120be5d3d7cf7d221b8703921046b73f0d2 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:27 +0530 Subject: [PATCH 2987/5214] dt-bindings: clock: qcom: Add X1P42100 camera clock controller Add X1P42100 camera clock controller support and clock bindings for camera QDSS debug clocks which are applicable for both X1E80100 and X1P42100 platforms. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-2-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/clock/qcom,x1e80100-camcc.yaml | 1 + include/dt-bindings/clock/qcom,x1e80100-camcc.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml index 938a2f1ff3fc..b28614186cc0 100644 --- a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - qcom,x1e80100-camcc + - qcom,x1p42100-camcc reg: maxItems: 1 diff --git a/include/dt-bindings/clock/qcom,x1e80100-camcc.h b/include/dt-bindings/clock/qcom,x1e80100-camcc.h index d72fdfb06a7c..06c316022fb0 100644 --- a/include/dt-bindings/clock/qcom,x1e80100-camcc.h +++ b/include/dt-bindings/clock/qcom,x1e80100-camcc.h @@ -115,6 +115,9 @@ #define CAM_CC_SLEEP_CLK_SRC 105 #define CAM_CC_SLOW_AHB_CLK_SRC 106 #define CAM_CC_XO_CLK_SRC 107 +#define CAM_CC_QDSS_DEBUG_CLK 108 +#define CAM_CC_QDSS_DEBUG_CLK_SRC 109 +#define CAM_CC_QDSS_DEBUG_XO_CLK 110 /* CAM_CC power domains */ #define CAM_CC_BPS_GDSC 0 From cfc34906768cb8ee2c6ab0dc83f0a57cc6410d59 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:28 +0530 Subject: [PATCH 2988/5214] clk: qcom: videocc-x1p42100: Add support for video clock controller Add support for the video clock controller for video clients to be able to request for videocc clocks on X1P42100 platform. Although X1P42100 is derived from X1E80100, the video clock controller differs significantly. The BSE clocks are newly added, several cdiv clocks have been removed, and most RCG frequency tables have been updated. Initial PLL configurations also require changes, hence introduce a separate videocc driver for X1P42100 platform. Reviewed-by: Taniya Das Reviewed-by: Konrad Dybcio Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-3-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Kconfig | 11 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/videocc-x1p42100.c | 585 ++++++++++++++++++++++++++++ 3 files changed, 597 insertions(+) create mode 100644 drivers/clk/qcom/videocc-x1p42100.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index d9cff5b0281d..e83a46000a2d 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -209,6 +209,17 @@ config CLK_X1P42100_GPUCC Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config CLK_X1P42100_VIDEOCC + tristate "X1P42100 Video Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_X1E80100_GCC + default m if ARCH_QCOM + help + Support for the video clock controller on Qualcomm Technologies, Inc. + X1P42100 devices. + Say Y if you want to support video devices and functionality such as + video encode/decode. + config CLK_QCM2290_GPUCC tristate "QCM2290 Graphics Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index e100cfd6a52d..eb6096b189f6 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GPUCC) += gpucc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcsrcc-x1e80100.o obj-$(CONFIG_CLK_X1P42100_GPUCC) += gpucc-x1p42100.o +obj-$(CONFIG_CLK_X1P42100_VIDEOCC) += videocc-x1p42100.o obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o obj-$(CONFIG_IPQ_APSS_5424) += apss-ipq5424.o diff --git a/drivers/clk/qcom/videocc-x1p42100.c b/drivers/clk/qcom/videocc-x1p42100.c new file mode 100644 index 000000000000..2bb40ac6fcc5 --- /dev/null +++ b/drivers/clk/qcom/videocc-x1p42100.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, +}; + +enum { + P_BI_TCXO, + P_VIDEO_CC_PLL0_OUT_MAIN, + P_VIDEO_CC_PLL1_OUT_MAIN, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +/* 420.0 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll0_config = { + .l = 0x15, + .alpha = 0xe000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll video_cc_pll0 = { + .offset = 0x0, + .config = &video_cc_pll0_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +/* 1050.0 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll1_config = { + .l = 0x36, + .alpha = 0xb000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll video_cc_pll1 = { + .offset = 0x1000, + .config = &video_cc_pll1_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct parent_map video_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map video_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_CC_PLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data video_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_cc_pll0.clkr.hw }, +}; + +static const struct parent_map video_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_CC_PLL1_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data video_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_cc_pll1.clkr.hw }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_bse_clk_src[] = { + F(420000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(600000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(670000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(848000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(920000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs0_bse_clk_src = { + .cmd_rcgr = 0x8154, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_mvs0_bse_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_clk_src", + .parent_data = video_cc_parent_data_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { + F(210000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(300000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(335000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(424000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(460000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs0_clk_src = { + .cmd_rcgr = 0x8000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_mvs0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk_src", + .parent_data = video_cc_parent_data_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs1_clk_src[] = { + F(1050000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1350000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1650000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs1_clk_src = { + .cmd_rcgr = 0x8018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_2, + .freq_tbl = ftbl_video_cc_mvs1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_clk_src", + .parent_data = video_cc_parent_data_2, + .num_parents = ARRAY_SIZE(video_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_xo_clk_src = { + .cmd_rcgr = 0x810c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .freq_tbl = ftbl_video_cc_xo_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_xo_clk_src", + .parent_data = video_cc_parent_data_0, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs0_bse_div4_div_clk_src = { + .reg = 0x817c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_div4_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_bse_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs1_div_clk_src = { + .reg = 0x80ec, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs1c_div2_div_clk_src = { + .reg = 0x809c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_div2_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch video_cc_mvs0_bse_clk = { + .halt_reg = 0x8170, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8170, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_bse_div4_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_clk = { + .halt_reg = 0x80b8, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x80b8, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x80b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_shift_clk = { + .halt_reg = 0x8128, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8128, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_clk = { + .halt_reg = 0x8064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_shift_clk = { + .halt_reg = 0x812c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x812c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_clk = { + .halt_reg = 0x80e0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x80e0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x80e0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_shift_clk = { + .halt_reg = 0x8130, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8130, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1c_clk = { + .halt_reg = 0x8090, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1c_div2_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1c_shift_clk = { + .halt_reg = 0x8134, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8134, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc video_cc_mvs0c_gdsc = { + .gdscr = 0x804c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs0_gdsc = { + .gdscr = 0x80a4, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &video_cc_mvs0c_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs1c_gdsc = { + .gdscr = 0x8078, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "video_cc_mvs1c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs1_gdsc = { + .gdscr = 0x80cc, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "video_cc_mvs1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &video_cc_mvs1c_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *video_cc_x1p42100_clocks[] = { + [VIDEO_CC_MVS0_BSE_CLK] = &video_cc_mvs0_bse_clk.clkr, + [VIDEO_CC_MVS0_BSE_CLK_SRC] = &video_cc_mvs0_bse_clk_src.clkr, + [VIDEO_CC_MVS0_BSE_DIV4_DIV_CLK_SRC] = &video_cc_mvs0_bse_div4_div_clk_src.clkr, + [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, + [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, + [VIDEO_CC_MVS0_SHIFT_CLK] = &video_cc_mvs0_shift_clk.clkr, + [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr, + [VIDEO_CC_MVS0C_SHIFT_CLK] = &video_cc_mvs0c_shift_clk.clkr, + [VIDEO_CC_MVS1_CLK] = &video_cc_mvs1_clk.clkr, + [VIDEO_CC_MVS1_CLK_SRC] = &video_cc_mvs1_clk_src.clkr, + [VIDEO_CC_MVS1_DIV_CLK_SRC] = &video_cc_mvs1_div_clk_src.clkr, + [VIDEO_CC_MVS1_SHIFT_CLK] = &video_cc_mvs1_shift_clk.clkr, + [VIDEO_CC_MVS1C_CLK] = &video_cc_mvs1c_clk.clkr, + [VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC] = &video_cc_mvs1c_div2_div_clk_src.clkr, + [VIDEO_CC_MVS1C_SHIFT_CLK] = &video_cc_mvs1c_shift_clk.clkr, + [VIDEO_CC_PLL0] = &video_cc_pll0.clkr, + [VIDEO_CC_PLL1] = &video_cc_pll1.clkr, + [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr, +}; + +static struct gdsc *video_cc_x1p42100_gdscs[] = { + [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, + [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, + [VIDEO_CC_MVS1_GDSC] = &video_cc_mvs1_gdsc, + [VIDEO_CC_MVS1C_GDSC] = &video_cc_mvs1c_gdsc, +}; + +static const struct qcom_reset_map video_cc_x1p42100_resets[] = { + [CVP_VIDEO_CC_INTERFACE_BCR] = { 0x80f0 }, + [CVP_VIDEO_CC_MVS0_BCR] = { 0x80a0 }, + [CVP_VIDEO_CC_MVS0C_BCR] = { 0x8048 }, + [CVP_VIDEO_CC_MVS1_BCR] = { 0x80c8 }, + [CVP_VIDEO_CC_MVS1C_BCR] = { 0x8074 }, + [VIDEO_CC_MVS0_BSE_BCR] = { 0x816c }, + [VIDEO_CC_MVS0C_CLK_ARES] = { 0x8064, 2 }, + [VIDEO_CC_MVS1C_CLK_ARES] = { 0x8090, 2 }, + [VIDEO_CC_XO_CLK_ARES] = { 0x8124, 2 }, +}; + +static struct clk_alpha_pll *video_cc_x1p42100_plls[] = { + &video_cc_pll0, + &video_cc_pll1, +}; + +static u32 video_cc_x1p42100_critical_cbcrs[] = { + 0x80f4, /* VIDEO_CC_AHB_CLK */ + 0x8150, /* VIDEO_CC_SLEEP_CLK */ + 0x8124, /* VIDEO_CC_XO_CLK */ +}; + +static const struct regmap_config video_cc_x1p42100_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9f54, + .fast_io = true, +}; + +static struct qcom_cc_driver_data video_cc_x1p42100_driver_data = { + .alpha_plls = video_cc_x1p42100_plls, + .num_alpha_plls = ARRAY_SIZE(video_cc_x1p42100_plls), + .clk_cbcrs = video_cc_x1p42100_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_x1p42100_critical_cbcrs), +}; + +static const struct qcom_cc_desc video_cc_x1p42100_desc = { + .config = &video_cc_x1p42100_regmap_config, + .clks = video_cc_x1p42100_clocks, + .num_clks = ARRAY_SIZE(video_cc_x1p42100_clocks), + .resets = video_cc_x1p42100_resets, + .num_resets = ARRAY_SIZE(video_cc_x1p42100_resets), + .gdscs = video_cc_x1p42100_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_x1p42100_gdscs), + .use_rpm = true, + .driver_data = &video_cc_x1p42100_driver_data, +}; + +static const struct of_device_id video_cc_x1p42100_match_table[] = { + { .compatible = "qcom,x1p42100-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_x1p42100_match_table); + +static int video_cc_x1p42100_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &video_cc_x1p42100_desc); +} + +static struct platform_driver video_cc_x1p42100_driver = { + .probe = video_cc_x1p42100_probe, + .driver = { + .name = "videocc-x1p42100", + .of_match_table = video_cc_x1p42100_match_table, + }, +}; + +module_platform_driver(video_cc_x1p42100_driver); + +MODULE_DESCRIPTION("QTI VIDEOCC X1P42100 Driver"); +MODULE_LICENSE("GPL"); From 1e6ae74ac6f28ace7a0eb84897c6e17bb044e5de Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:29 +0530 Subject: [PATCH 2989/5214] clk: qcom: camcc-x1e80100: Add support for camera QDSS debug clocks Add support for camera QDSS debug clocks on X1E80100 platform which are required to be voted for camera icp and cpas usecases. This change aligns the camcc driver to the new ABI exposed from X1E80100 camcc bindings that supports these camcc QDSS debug clocks. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Signed-off-by: Jagadeesh Kona Fixes: 76126a5129b5 ("clk: qcom: Add camcc clock driver for x1e80100") Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-4-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/camcc-x1e80100.c | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index 81f579ff6993..c12994af42cf 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -1052,6 +1052,31 @@ static struct clk_rcg2 cam_cc_mclk7_clk_src = { }, }; +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_CAM_CC_PLL8_OUT_EVEN, 8, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + static const struct freq_tbl ftbl_cam_cc_sfe_0_clk_src[] = { F(345600000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), F(432000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), @@ -2182,6 +2207,42 @@ static struct clk_branch cam_cc_mclk7_clk = { }, }; +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch cam_cc_sfe_0_clk = { .halt_reg = 0x133c0, .halt_check = BRANCH_HALT, @@ -2398,6 +2459,9 @@ static struct clk_regmap *cam_cc_x1e80100_clocks[] = { [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, [CAM_CC_PLL8] = &cam_cc_pll8.clkr, [CAM_CC_PLL8_OUT_EVEN] = &cam_cc_pll8_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, [CAM_CC_SFE_0_CLK] = &cam_cc_sfe_0_clk.clkr, [CAM_CC_SFE_0_CLK_SRC] = &cam_cc_sfe_0_clk_src.clkr, [CAM_CC_SFE_0_FAST_AHB_CLK] = &cam_cc_sfe_0_fast_ahb_clk.clkr, From 10524682d1b8e1cf2e83afe3bcabd2cc69a0a5c4 Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:30 +0530 Subject: [PATCH 2990/5214] clk: qcom: camcc-x1p42100: Add support for camera clock controller Add support for the camera clock controller for camera clients to be able to request for camcc clocks on X1P42100 platform. Although X1P42100 is derived from X1E80100, the camera clock controller driver differs significantly. Few PLLs, clocks and GDSC's are removed, there is delta in frequency tables for most RCG's and parent data structures also changed for few RCG's. Hence introduce a separate camcc driver for X1P42100 platform. Reviewed-by: Konrad Dybcio Reviewed-by: Taniya Das Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-5-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/Kconfig | 11 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/camcc-x1p42100.c | 2223 +++++++++++++++++++++++++++++ 3 files changed, 2235 insertions(+) create mode 100644 drivers/clk/qcom/camcc-x1p42100.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index e83a46000a2d..7d84c2f1d911 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -200,6 +200,17 @@ config CLK_X1E80100_TCSRCC Support for the TCSR clock controller on X1E80100 devices. Say Y if you want to use peripheral devices such as SD/UFS. +config CLK_X1P42100_CAMCC + tristate "X1P42100 Camera Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_X1E80100_GCC + default m if ARCH_QCOM + help + Support for the camera clock controller on Qualcomm Technologies, Inc. + X1P42100 devices. + Say Y if you want to support camera devices and camera functionality + such as capturing pictures. + config CLK_X1P42100_GPUCC tristate "X1P42100 Graphics Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index eb6096b189f6..58f9a5eb6fd7 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GPUCC) += gpucc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcsrcc-x1e80100.o +obj-$(CONFIG_CLK_X1P42100_CAMCC) += camcc-x1p42100.o obj-$(CONFIG_CLK_X1P42100_GPUCC) += gpucc-x1p42100.o obj-$(CONFIG_CLK_X1P42100_VIDEOCC) += videocc-x1p42100.o obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o diff --git a/drivers/clk/qcom/camcc-x1p42100.c b/drivers/clk/qcom/camcc-x1p42100.c new file mode 100644 index 000000000000..c1a61c267919 --- /dev/null +++ b/drivers/clk/qcom/camcc-x1p42100.c @@ -0,0 +1,2223 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_IFACE, + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_BI_TCXO_AO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL0_OUT_ODD, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL2_OUT_EVEN, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_EVEN, + P_CAM_CC_PLL6_OUT_EVEN, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +static const struct pll_vco rivian_ole_vco[] = { + { 777000000, 1285000000, 0 }, +}; + +/* 1200.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x3e, + .alpha = 0x8000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00008400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .config = &cam_cc_pll0_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = { + { 0x2, 3 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = { + .offset = 0x0, + .post_div_shift = 14, + .post_div_table = post_div_table_cam_cc_pll0_out_odd, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_odd", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 728.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x25, + .alpha = 0xeaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .config = &cam_cc_pll1_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll1_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll1_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll1.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 960.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x10000030, + .config_ctl_hi_val = 0x80890263, + .config_ctl_hi1_val = 0x00000217, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00100000, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .config = &cam_cc_pll2_config, + .vco_table = rivian_ole_vco, + .num_vco = ARRAY_SIZE(rivian_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_rivian_evo_ops, + }, + }, +}; + +/* 864.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .config = &cam_cc_pll3_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll3_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = { + .offset = 0x3000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll3_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll3_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll3.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 960.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll6_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll6 = { + .offset = 0x6000, + .config = &cam_cc_pll6_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll6_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll6_out_even = { + .offset = 0x6000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll6_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll6_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll6.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_EVEN, 2 }, + { P_CAM_CC_PLL0_OUT_ODD, 3 }, + { P_CAM_CC_PLL6_OUT_EVEN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll6_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EVEN, 3 }, + { P_CAM_CC_PLL2_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, + { .hw = &cam_cc_pll2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL3_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll3_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_EVEN, 4 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x10278, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_camnoc_axi_rt_clk_src[] = { + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_camnoc_axi_rt_clk_src = { + .cmd_rcgr = 0x138f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_camnoc_axi_rt_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_rt_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0x1365c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0x1378c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x11164, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x150e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x15104, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x15124, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x15258, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi4phytimer_clk_src = { + .cmd_rcgr = 0x1538c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi5phytimer_clk_src = { + .cmd_rcgr = 0x154c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csid_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csid_clk_src = { + .cmd_rcgr = 0x138d4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csid_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x10018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0x13520, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x11018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_lite_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0x13000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0x1313c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_nps_clk_src[] = { + F(364000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(500000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(700000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_nps_clk_src = { + .cmd_rcgr = 0x103cc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_ipe_nps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_jpeg_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0x133dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_jpeg_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_MAIN, 10, 1, 4), + F(68571429, P_CAM_CC_PLL2_OUT_MAIN, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x15000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x1501c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x15038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x15054, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x15070, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk5_clk_src = { + .cmd_rcgr = 0x1508c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk6_clk_src = { + .cmd_rcgr = 0x150a8, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk7_clk_src = { + .cmd_rcgr = 0x150c4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sleep_clk_src = { + .cmd_rcgr = 0x13aa0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sleep_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x10148, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_xo_clk_src = { + .cmd_rcgr = 0x13a84, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_xo_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_xo_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x10274, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10274, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x103a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x103a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_fast_ahb_clk = { + .halt_reg = 0x10144, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_nrt_clk = { + .halt_reg = 0x13920, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x13920, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x13920, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_nrt_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_rt_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_rt_clk = { + .halt_reg = 0x13910, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13910, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_rt_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_rt_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_dcd_xo_clk = { + .halt_reg = 0x1392c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1392c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_dcd_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_xo_clk = { + .halt_reg = 0x13930, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13930, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0x13788, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13788, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0x138b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0x13a80, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x13a80, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0x138bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_bps_clk = { + .halt_reg = 0x103b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x103b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_fast_ahb_clk = { + .halt_reg = 0x138c8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138c8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_0_clk = { + .halt_reg = 0x11150, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11150, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_lite_clk = { + .halt_reg = 0x13138, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13138, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_lite_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ipe_nps_clk = { + .halt_reg = 0x10504, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10504, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ipe_nps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x150f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x1511c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1511c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x15250, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15250, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x15384, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15384, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi3phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi4phytimer_clk = { + .halt_reg = 0x154b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x154b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi4phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi5phytimer_clk = { + .halt_reg = 0x155ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x155ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi5phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_clk = { + .halt_reg = 0x138ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_csiphy_rx_clk = { + .halt_reg = 0x15100, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15100, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_csiphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x150fc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150fc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x15120, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15120, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x15254, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15254, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x15388, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15388, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy4_clk = { + .halt_reg = 0x154bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x154bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy5_clk = { + .halt_reg = 0x155f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x155f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy5_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ahb_clk = { + .halt_reg = 0x13658, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13658, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0x1364c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1364c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x11144, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x11154, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11154, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_fast_ahb_clk = { + .halt_reg = 0x11160, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11160, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_ahb_clk = { + .halt_reg = 0x13278, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13278, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0x1312c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1312c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0x13274, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13274, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0x13268, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13268, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_ahb_clk = { + .halt_reg = 0x1051c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1051c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_clk = { + .halt_reg = 0x104f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x104f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_fast_ahb_clk = { + .halt_reg = 0x10520, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10520, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_clk = { + .halt_reg = 0x10508, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10508, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_fast_ahb_clk = { + .halt_reg = 0x10524, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10524, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0x13508, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13508, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_jpeg_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x15018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x15034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x15050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x1506c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1506c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x15088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk5_clk = { + .halt_reg = 0x150a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk6_clk = { + .halt_reg = 0x150c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk7_clk = { + .halt_reg = 0x150dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc cam_cc_titan_top_gdsc = { + .gdscr = 0x13a6c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_titan_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_bps_gdsc = { + .gdscr = 0x10004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_bps_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_ife_0_gdsc = { + .gdscr = 0x11004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_ife_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_ipe_0_gdsc = { + .gdscr = 0x103b8, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_ipe_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *cam_cc_x1p42100_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_BPS_FAST_AHB_CLK] = &cam_cc_bps_fast_ahb_clk.clkr, + [CAM_CC_CAMNOC_AXI_NRT_CLK] = &cam_cc_camnoc_axi_nrt_clk.clkr, + [CAM_CC_CAMNOC_AXI_RT_CLK] = &cam_cc_camnoc_axi_rt_clk.clkr, + [CAM_CC_CAMNOC_AXI_RT_CLK_SRC] = &cam_cc_camnoc_axi_rt_clk_src.clkr, + [CAM_CC_CAMNOC_DCD_XO_CLK] = &cam_cc_camnoc_dcd_xo_clk.clkr, + [CAM_CC_CAMNOC_XO_CLK] = &cam_cc_camnoc_xo_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPAS_BPS_CLK] = &cam_cc_cpas_bps_clk.clkr, + [CAM_CC_CPAS_FAST_AHB_CLK] = &cam_cc_cpas_fast_ahb_clk.clkr, + [CAM_CC_CPAS_IFE_0_CLK] = &cam_cc_cpas_ife_0_clk.clkr, + [CAM_CC_CPAS_IFE_LITE_CLK] = &cam_cc_cpas_ife_lite_clk.clkr, + [CAM_CC_CPAS_IPE_NPS_CLK] = &cam_cc_cpas_ipe_nps_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSI4PHYTIMER_CLK] = &cam_cc_csi4phytimer_clk.clkr, + [CAM_CC_CSI4PHYTIMER_CLK_SRC] = &cam_cc_csi4phytimer_clk_src.clkr, + [CAM_CC_CSI5PHYTIMER_CLK] = &cam_cc_csi5phytimer_clk.clkr, + [CAM_CC_CSI5PHYTIMER_CLK_SRC] = &cam_cc_csi5phytimer_clk_src.clkr, + [CAM_CC_CSID_CLK] = &cam_cc_csid_clk.clkr, + [CAM_CC_CSID_CLK_SRC] = &cam_cc_csid_clk_src.clkr, + [CAM_CC_CSID_CSIPHY_RX_CLK] = &cam_cc_csid_csiphy_rx_clk.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_CSIPHY4_CLK] = &cam_cc_csiphy4_clk.clkr, + [CAM_CC_CSIPHY5_CLK] = &cam_cc_csiphy5_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_0_FAST_AHB_CLK] = &cam_cc_ife_0_fast_ahb_clk.clkr, + [CAM_CC_IFE_LITE_AHB_CLK] = &cam_cc_ife_lite_ahb_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_NPS_AHB_CLK] = &cam_cc_ipe_nps_ahb_clk.clkr, + [CAM_CC_IPE_NPS_CLK] = &cam_cc_ipe_nps_clk.clkr, + [CAM_CC_IPE_NPS_CLK_SRC] = &cam_cc_ipe_nps_clk_src.clkr, + [CAM_CC_IPE_NPS_FAST_AHB_CLK] = &cam_cc_ipe_nps_fast_ahb_clk.clkr, + [CAM_CC_IPE_PPS_CLK] = &cam_cc_ipe_pps_clk.clkr, + [CAM_CC_IPE_PPS_FAST_AHB_CLK] = &cam_cc_ipe_pps_fast_ahb_clk.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_MCLK5_CLK] = &cam_cc_mclk5_clk.clkr, + [CAM_CC_MCLK5_CLK_SRC] = &cam_cc_mclk5_clk_src.clkr, + [CAM_CC_MCLK6_CLK] = &cam_cc_mclk6_clk.clkr, + [CAM_CC_MCLK6_CLK_SRC] = &cam_cc_mclk6_clk_src.clkr, + [CAM_CC_MCLK7_CLK] = &cam_cc_mclk7_clk.clkr, + [CAM_CC_MCLK7_CLK_SRC] = &cam_cc_mclk7_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr, + [CAM_CC_PLL6] = &cam_cc_pll6.clkr, + [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, + [CAM_CC_SLEEP_CLK_SRC] = &cam_cc_sleep_clk_src.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr, +}; + +static struct gdsc *cam_cc_x1p42100_gdscs[] = { + [CAM_CC_BPS_GDSC] = &cam_cc_bps_gdsc, + [CAM_CC_IFE_0_GDSC] = &cam_cc_ife_0_gdsc, + [CAM_CC_IPE_0_GDSC] = &cam_cc_ipe_0_gdsc, + [CAM_CC_TITAN_TOP_GDSC] = &cam_cc_titan_top_gdsc, +}; + +static const struct qcom_reset_map cam_cc_x1p42100_resets[] = { + [CAM_CC_BPS_BCR] = { 0x10000 }, + [CAM_CC_ICP_BCR] = { 0x1351c }, + [CAM_CC_IFE_0_BCR] = { 0x11000 }, + [CAM_CC_IPE_0_BCR] = { 0x103b4 }, +}; + +static struct clk_alpha_pll *cam_cc_x1p42100_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll6, +}; + +static u32 cam_cc_x1p42100_critical_cbcrs[] = { + 0x13a9c, /* CAM_CC_GDSC_CLK */ + 0x13ab8, /* CAM_CC_SLEEP_CLK */ +}; + +static const struct regmap_config cam_cc_x1p42100_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1603c, + .fast_io = true, +}; + +static struct qcom_cc_driver_data cam_cc_x1p42100_driver_data = { + .alpha_plls = cam_cc_x1p42100_plls, + .num_alpha_plls = ARRAY_SIZE(cam_cc_x1p42100_plls), + .clk_cbcrs = cam_cc_x1p42100_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_x1p42100_critical_cbcrs), +}; + +static struct qcom_cc_desc cam_cc_x1p42100_desc = { + .config = &cam_cc_x1p42100_regmap_config, + .clks = cam_cc_x1p42100_clocks, + .num_clks = ARRAY_SIZE(cam_cc_x1p42100_clocks), + .resets = cam_cc_x1p42100_resets, + .num_resets = ARRAY_SIZE(cam_cc_x1p42100_resets), + .gdscs = cam_cc_x1p42100_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_x1p42100_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_x1p42100_driver_data, +}; + +static const struct of_device_id cam_cc_x1p42100_match_table[] = { + { .compatible = "qcom,x1p42100-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_x1p42100_match_table); + +static int cam_cc_x1p42100_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &cam_cc_x1p42100_desc); +} + +static struct platform_driver cam_cc_x1p42100_driver = { + .probe = cam_cc_x1p42100_probe, + .driver = { + .name = "camcc-x1p42100", + .of_match_table = cam_cc_x1p42100_match_table, + }, +}; + +module_platform_driver(cam_cc_x1p42100_driver); + +MODULE_DESCRIPTION("QTI CAMCC X1P42100 Driver"); +MODULE_LICENSE("GPL"); From 771ed1b12942dbf592c34554c81f25a627fd254e Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 11:18:29 +0200 Subject: [PATCH 2991/5214] interconnect: Add devm_of_icc_get_by_index() as exported API for users Users can use devm version of of_icc_get_by_index() to benefit from automatic resource release. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Acked-by: Georgi Djakov Link: https://lore.kernel.org/r/20260501-milos-camcc-icc-v2-1-bb83c1256cc3@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/interconnect/core.c | 20 ++++++++++++++++++++ include/linux/interconnect.h | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 8569b78a1851..bc2e416dbcb2 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -443,6 +443,26 @@ struct icc_path *devm_of_icc_get(struct device *dev, const char *name) } EXPORT_SYMBOL_GPL(devm_of_icc_get); +struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx) +{ + struct icc_path **ptr, *path; + + ptr = devres_alloc(devm_icc_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + path = of_icc_get_by_index(dev, idx); + if (!IS_ERR(path)) { + *ptr = path; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return path; +} +EXPORT_SYMBOL_GPL(devm_of_icc_get_by_index); + /** * of_icc_get_by_index() - get a path handle from a DT node based on index * @dev: device pointer for the consumer device diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h index 4b12821528a6..75a32ad0482e 100644 --- a/include/linux/interconnect.h +++ b/include/linux/interconnect.h @@ -47,6 +47,7 @@ struct icc_path *of_icc_get(struct device *dev, const char *name); struct icc_path *devm_of_icc_get(struct device *dev, const char *name); int devm_of_icc_bulk_get(struct device *dev, int num_paths, struct icc_bulk_data *paths); struct icc_path *of_icc_get_by_index(struct device *dev, int idx); +struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx); void icc_put(struct icc_path *path); int icc_enable(struct icc_path *path); int icc_disable(struct icc_path *path); @@ -79,6 +80,11 @@ static inline struct icc_path *of_icc_get_by_index(struct device *dev, int idx) return NULL; } +static inline struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx) +{ + return NULL; +} + static inline void icc_put(struct icc_path *path) { } From 7e622e74d2700da4d6ed3aa2a4d7e1b7d7293768 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 11:18:30 +0200 Subject: [PATCH 2992/5214] dt-bindings: clock: qcom,milos-camcc: Document interconnect path Document an interconnect path for camcc which needs to be enabled so that the CAMSS_TOP_GDSC power domain can turn on successfully. Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260501-milos-camcc-icc-v2-2-bb83c1256cc3@fairphone.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/clock/qcom,milos-camcc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml index f63149ecf3e1..707b25d2c11e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml @@ -25,6 +25,10 @@ properties: - description: Sleep clock source - description: Camera AHB clock from GCC + interconnects: + items: + - description: Interconnect path to enable the MultiMedia NoC + required: - compatible - clocks @@ -37,12 +41,16 @@ unevaluatedProperties: false examples: - | #include + #include + #include clock-controller@adb0000 { compatible = "qcom,milos-camcc"; reg = <0x0adb0000 0x40000>; clocks = <&bi_tcxo_div2>, <&sleep_clk>, <&gcc GCC_CAMERA_AHB_CLK>; + interconnects = <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &mmss_noc SLAVE_MNOC_HF_MEM_NOC QCOM_ICC_TAG_ALWAYS>; #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; From bd09d87c55d6e7783ee2394c30061d66cc9df299 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 11:18:31 +0200 Subject: [PATCH 2993/5214] clk: qcom: gdsc: Support enabling interconnect path for power domain On newer SoCs like Milos the CAMSS_TOP_GDSC power domains requires the enablement of the multimedia NoC, otherwise the GDSC will be stuck on 'off'. Add support for getting an interconnect path as specified in the SoC clock driver, and enabling/disabling that interconnect path when the GDSC is being enabled/disabled. Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20260501-milos-camcc-icc-v2-3-bb83c1256cc3@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/gdsc.c | 33 +++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 5 +++++ 2 files changed, 38 insertions(+) diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 95aa07120245..ee5f86ca50cb 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, return ret; } + if (status == GDSC_ON) { + ret = icc_set_bw(sc->icc_path, 1, 1); + if (ret) + goto err_disable_supply; + } + ret = gdsc_update_collapse_bit(sc, status == GDSC_OFF); /* If disabling votable gdscs, don't poll on status */ @@ -177,6 +184,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, ret = gdsc_poll_status(sc, status); WARN(ret, "%s status stuck at 'o%s'", sc->pd.name, status ? "ff" : "n"); + if (!ret && status == GDSC_OFF) { + ret = icc_set_bw(sc->icc_path, 0, 0); + if (ret) + return ret; + } + if (!ret && status == GDSC_OFF && sc->rsupply) { ret = regulator_disable(sc->rsupply); if (ret < 0) @@ -184,6 +197,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, } return ret; + +err_disable_supply: + if (status == GDSC_ON && sc->rsupply) + regulator_disable(sc->rsupply); + + return ret; } static inline int gdsc_deassert_reset(struct gdsc *sc) @@ -584,6 +603,20 @@ int gdsc_register(struct gdsc_desc *desc, if (!data->domains) return -ENOMEM; + for (i = 0; i < num; i++) { + if (!scs[i] || !scs[i]->needs_icc) + continue; + + scs[i]->icc_path = devm_of_icc_get_by_index(dev, scs[i]->icc_path_index); + if (IS_ERR(scs[i]->icc_path)) { + ret = PTR_ERR(scs[i]->icc_path); + if (ret != -ENODEV) + return ret; + + scs[i]->icc_path = NULL; + } + } + for (i = 0; i < num; i++) { if (!scs[i] || !scs[i]->supply) continue; diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index dd843e86c05b..92ff6bcce7b1 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -9,6 +9,7 @@ #include #include +struct icc_path; struct regmap; struct regulator; struct reset_controller_dev; @@ -74,6 +75,10 @@ struct gdsc { const char *supply; struct regulator *rsupply; + + bool needs_icc; + unsigned int icc_path_index; + struct icc_path *icc_path; }; struct gdsc_desc { From 205aefa0db8bff56f08d0e06a0ca628555758805 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 11:18:32 +0200 Subject: [PATCH 2994/5214] clk: qcom: camcc-milos: Declare icc path dependency for CAMSS_TOP_GDSC This GDSC requires an interconnect path to be enabled, otherwise the GDSC will be stuck on 'off' and can't be enabled. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20260501-milos-camcc-icc-v2-4-bb83c1256cc3@fairphone.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/camcc-milos.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/clk/qcom/camcc-milos.c b/drivers/clk/qcom/camcc-milos.c index 409d47098c10..579b71e0e089 100644 --- a/drivers/clk/qcom/camcc-milos.c +++ b/drivers/clk/qcom/camcc-milos.c @@ -30,6 +30,11 @@ enum { DT_IFACE, }; +/* Need to match the order of interconnects in DT binding */ +enum { + DT_ICC_TOP_GDSC, +}; + enum { P_BI_TCXO, P_CAM_CC_PLL0_OUT_EVEN, @@ -1971,6 +1976,8 @@ static struct gdsc cam_cc_camss_top_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, + .needs_icc = true, + .icc_path_index = DT_ICC_TOP_GDSC, }; static struct clk_regmap *cam_cc_milos_clocks[] = { From 1e01a786a904274d308a99b00a0f20e0d288eeff Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:51 -0600 Subject: [PATCH 2995/5214] riscv/purgatory: return bool from verify_sha256_digest Change the function's return type from int to bool and return the result of memcmp() directly to simplify the code. While at it, cast ->start to 'const u8 *' to better match the expected type. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260509073850.44595-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/purgatory/purgatory.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/riscv/purgatory/purgatory.c b/arch/riscv/purgatory/purgatory.c index bbd5cfa4d741..745deeb6e3a5 100644 --- a/arch/riscv/purgatory/purgatory.c +++ b/arch/riscv/purgatory/purgatory.c @@ -17,7 +17,7 @@ u8 purgatory_sha256_digest[SHA256_DIGEST_SIZE] __section(".kexec-purgatory"); struct kexec_sha_region purgatory_sha_regions[KEXEC_SEGMENT_MAX] __section(".kexec-purgatory"); -static int verify_sha256_digest(void) +static bool verify_sha256_digest(void) { struct kexec_sha_region *ptr, *end; struct sha256_ctx sctx; @@ -26,11 +26,10 @@ static int verify_sha256_digest(void) sha256_init(&sctx); end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions); for (ptr = purgatory_sha_regions; ptr < end; ptr++) - sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len); + sha256_update(&sctx, (const u8 *)(ptr->start), ptr->len); sha256_final(&sctx, digest); - if (memcmp(digest, purgatory_sha256_digest, sizeof(digest)) != 0) - return 1; - return 0; + + return memcmp(digest, purgatory_sha256_digest, sizeof(digest)) == 0; } /* workaround for a warning with -Wmissing-prototypes */ @@ -38,7 +37,7 @@ void purgatory(void); void purgatory(void) { - if (verify_sha256_digest()) + if (!verify_sha256_digest()) for (;;) /* loop forever */ ; From a24c7f3523ff1112e8dc6c45cea7d9397345a627 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 2996/5214] riscv/purgatory: add asm/purgatory.h Add arch/riscv/include/asm/purgatory.h and provide the purgatory() prototype via the architecture header, mirroring the x86 layout. Remove the workaround from arch/riscv/purgatory/purgatory.c. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260509073850.44595-4-thorsten.blum@linux.dev [pjw@kernel.org: drop superfluous extern in header file] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/purgatory.h | 11 +++++++++++ arch/riscv/purgatory/purgatory.c | 5 +---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 arch/riscv/include/asm/purgatory.h diff --git a/arch/riscv/include/asm/purgatory.h b/arch/riscv/include/asm/purgatory.h new file mode 100644 index 000000000000..5a827e6752b7 --- /dev/null +++ b/arch/riscv/include/asm/purgatory.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_RISCV_PURGATORY_H +#define _ASM_RISCV_PURGATORY_H + +#ifndef __ASSEMBLER__ +#include + +void purgatory(void); +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_RISCV_PURGATORY_H */ diff --git a/arch/riscv/purgatory/purgatory.c b/arch/riscv/purgatory/purgatory.c index 745deeb6e3a5..3474e3abe5d7 100644 --- a/arch/riscv/purgatory/purgatory.c +++ b/arch/riscv/purgatory/purgatory.c @@ -8,9 +8,9 @@ * */ -#include #include #include +#include #include u8 purgatory_sha256_digest[SHA256_DIGEST_SIZE] __section(".kexec-purgatory"); @@ -32,9 +32,6 @@ static bool verify_sha256_digest(void) return memcmp(digest, purgatory_sha256_digest, sizeof(digest)) == 0; } -/* workaround for a warning with -Wmissing-prototypes */ -void purgatory(void); - void purgatory(void) { if (!verify_sha256_digest()) From 1a9e47c2c38ad2af1916cdd4a0119e87ecb5baae Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 2997/5214] riscv: ptdump: Replace unbounded sprintf() in dump_prot() Replace the unbounded sprintf("%s", ...) with the faster and safer strscpy(). Replace all other sprintf() calls with the safer snprintf(). Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260127110543.436242-1-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/mm/ptdump.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/riscv/mm/ptdump.c b/arch/riscv/mm/ptdump.c index 34299c2b231f..f4b4a9fcbbd8 100644 --- a/arch/riscv/mm/ptdump.c +++ b/arch/riscv/mm/ptdump.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -213,21 +214,21 @@ static void dump_prot(struct pg_state *st) val = st->current_prot & pte_bits[i].mask; if (val) { if (pte_bits[i].mask == _PAGE_SOFT) - sprintf(s, pte_bits[i].set, val >> 8); + snprintf(s, sizeof(s), pte_bits[i].set, val >> 8); #ifdef CONFIG_64BIT else if (pte_bits[i].mask == _PAGE_MTMASK_SVPBMT) { if (val == _PAGE_NOCACHE_SVPBMT) - sprintf(s, pte_bits[i].set, "NC"); + snprintf(s, sizeof(s), pte_bits[i].set, "NC"); else if (val == _PAGE_IO_SVPBMT) - sprintf(s, pte_bits[i].set, "IO"); + snprintf(s, sizeof(s), pte_bits[i].set, "IO"); else - sprintf(s, pte_bits[i].set, "??"); + snprintf(s, sizeof(s), pte_bits[i].set, "??"); } #endif else - sprintf(s, "%s", pte_bits[i].set); + strscpy(s, pte_bits[i].set); } else { - sprintf(s, "%s", pte_bits[i].clear); + strscpy(s, pte_bits[i].clear); } pt_dump_seq_printf(st->seq, " %s", s); From b954dba17b2ee53dadd34a7aacf55a33a775448f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 2998/5214] riscv: pi: replace strlcat with strscpy in get_early_cmdline Use the return value of strscpy() instead of calling strlen(fdt_cmdline) again and return early on string truncation. Drop the explicit size argument since early_cmdline has a fixed length, which strscpy() determines using sizeof() when the argument is omitted. Replace strlcat() with strscpy() to append CONFIG_CMDLINE. Also remove the unnecessary fdt_cmdline NULL initialization. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260504154924.141566-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/pi/cmdline_early.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/pi/cmdline_early.c b/arch/riscv/kernel/pi/cmdline_early.c index 389d086a0718..0afbe4077cb8 100644 --- a/arch/riscv/kernel/pi/cmdline_early.c +++ b/arch/riscv/kernel/pi/cmdline_early.c @@ -12,8 +12,8 @@ static char early_cmdline[COMMAND_LINE_SIZE]; static char *get_early_cmdline(uintptr_t dtb_pa) { - const char *fdt_cmdline = NULL; - unsigned int fdt_cmdline_size = 0; + const char *fdt_cmdline; + ssize_t fdt_cmdline_size = 0; int chosen_node; if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { @@ -22,18 +22,18 @@ static char *get_early_cmdline(uintptr_t dtb_pa) fdt_cmdline = fdt_getprop((void *)dtb_pa, chosen_node, "bootargs", NULL); if (fdt_cmdline) { - fdt_cmdline_size = strlen(fdt_cmdline); - strscpy(early_cmdline, fdt_cmdline, - COMMAND_LINE_SIZE); + fdt_cmdline_size = strscpy(early_cmdline, fdt_cmdline); + if (fdt_cmdline_size < 0) + return early_cmdline; } } } if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || IS_ENABLED(CONFIG_CMDLINE_FORCE) || - fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) { - strlcat(early_cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); - } + fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) + strscpy(early_cmdline + fdt_cmdline_size, CONFIG_CMDLINE, + COMMAND_LINE_SIZE - fdt_cmdline_size); return early_cmdline; } From 7d0dec57e52c7b5e14ba4ab6f86e9c17b850f1ec Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 2999/5214] riscv: use sysfs_emit in cpu_show_ghostwrite Replace sprintf() with sysfs_emit() in cpu_show_ghostwrite(), which is preferred for formatting sysfs output because it provides safer bounds checking. While the current code only emits fixed strings that fit easily within PAGE_SIZE, use sysfs_emit() to follow secure coding best practices. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260510165420.109453-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/bugs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/bugs.c b/arch/riscv/kernel/bugs.c index 3655fe7d678c..e5758e3f1c7e 100644 --- a/arch/riscv/kernel/bugs.c +++ b/arch/riscv/kernel/bugs.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -46,15 +46,15 @@ ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, c if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR)) { switch (ghostwrite_state) { case UNAFFECTED: - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); case MITIGATED: - return sprintf(buf, "Mitigation: xtheadvector disabled\n"); + return sysfs_emit(buf, "Mitigation: xtheadvector disabled\n"); case VULNERABLE: fallthrough; default: - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } - } else { - return sprintf(buf, "Not affected\n"); } + + return sysfs_emit(buf, "Not affected\n"); } From 13fe217f64a7ee71aa58d871c6021a721a5a0c6f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 3000/5214] riscv: propagate insert_resource result from add_resource Currently, add_resource() returns 1 on success, even though its callers only check for negative values. Instead, propagate the insert_resource() result from add_resource() to align with standard kernel return-value conventions (0 on success, negative errno on failure). Use %pR to print the full resource range while at it. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260512172034.328405-4-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/setup.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index c89cc272440b..52d1d2b8f338 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -71,16 +71,13 @@ static struct resource *standard_resources; static int __init add_resource(struct resource *parent, struct resource *res) { - int ret = 0; + int ret; ret = insert_resource(parent, res); - if (ret < 0) { - pr_err("Failed to add a %s resource at %llx\n", - res->name, (unsigned long long) res->start); - return ret; - } + if (ret < 0) + pr_err("Failed to add resource %s %pR\n", res->name, res); - return 1; + return ret; } static int __init add_kernel_resources(void) From c0db0690f7fae7bd8cf1493e2f6b2a672e0c0de8 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 3001/5214] selftests/riscv: fix compiler output flag spacing in all Makefiles Standardize the compiler output flag format across all RISC-V selftests by adding a space between '-o' and '$@'. Although '-o$@' is perfectly valid for GCC/Clang to parse, changing it to '-o $@' with a space aligns with the GNU official documentation conventions, improves readability by visually separating the flag from the target variable, and ensures consistency with other architectures. Currently, RISC-V selftests use '-o$@' (without space) in 13 instances across 6 Makefiles, while all other architectures consistently use '-o $@' (with space). This inconsistency makes RISC-V an outlier in the kernel's selftest infrastructure. Signed-off-by: Zong Li Link: https://patch.msgid.link/20260511032917.3542802-1-zong.li@sifive.com [pjw@kernel.org: cleaned up patch description] Signed-off-by: Paul Walmsley --- tools/testing/selftests/riscv/abi/Makefile | 2 +- tools/testing/selftests/riscv/cfi/Makefile | 2 +- tools/testing/selftests/riscv/hwprobe/Makefile | 6 +++--- tools/testing/selftests/riscv/mm/Makefile | 2 +- tools/testing/selftests/riscv/sigreturn/Makefile | 2 +- tools/testing/selftests/riscv/vector/Makefile | 12 ++++++------ 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile index ed82ff9c664e..041114675ad5 100644 --- a/tools/testing/selftests/riscv/abi/Makefile +++ b/tools/testing/selftests/riscv/abi/Makefile @@ -7,4 +7,4 @@ TEST_GEN_PROGS := pointer_masking include ../../lib.mk $(OUTPUT)/pointer_masking: pointer_masking.c - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/cfi/Makefile b/tools/testing/selftests/riscv/cfi/Makefile index 93b4738c0e2e..418b4b5325a5 100644 --- a/tools/testing/selftests/riscv/cfi/Makefile +++ b/tools/testing/selftests/riscv/cfi/Makefile @@ -16,7 +16,7 @@ ifeq ($(shell $(CC) $(CFLAGS) -nostdlib -xc /dev/null -o /dev/null > /dev/null 2 TEST_GEN_PROGS := cfitests $(OUTPUT)/cfitests: cfitests.c shadowstack.c - $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ else $(shell echo "Toolchain doesn't support CFI, skipping CFI kselftest." >&2) diff --git a/tools/testing/selftests/riscv/hwprobe/Makefile b/tools/testing/selftests/riscv/hwprobe/Makefile index cec81610a5f2..71e3f26c541b 100644 --- a/tools/testing/selftests/riscv/hwprobe/Makefile +++ b/tools/testing/selftests/riscv/hwprobe/Makefile @@ -9,10 +9,10 @@ TEST_GEN_PROGS := hwprobe cbo which-cpus include ../../lib.mk $(OUTPUT)/hwprobe: hwprobe.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/cbo: cbo.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/which-cpus: which-cpus.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/mm/Makefile b/tools/testing/selftests/riscv/mm/Makefile index 4664ed79e20b..24122453e3d0 100644 --- a/tools/testing/selftests/riscv/mm/Makefile +++ b/tools/testing/selftests/riscv/mm/Makefile @@ -12,4 +12,4 @@ TEST_PROGS := run_mmap.sh include ../../lib.mk $(OUTPUT)/mm: mmap_default.c mmap_bottomup.c mmap_tests.h - $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/sigreturn/Makefile b/tools/testing/selftests/riscv/sigreturn/Makefile index eb8bac9279a8..8c77508641f3 100644 --- a/tools/testing/selftests/riscv/sigreturn/Makefile +++ b/tools/testing/selftests/riscv/sigreturn/Makefile @@ -9,4 +9,4 @@ TEST_GEN_PROGS := sigreturn include ../../lib.mk $(OUTPUT)/sigreturn: sigreturn.c - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile index 326dafd739bf..7e0017b3fb8b 100644 --- a/tools/testing/selftests/riscv/vector/Makefile +++ b/tools/testing/selftests/riscv/vector/Makefile @@ -11,29 +11,29 @@ include ../../lib.mk TEST_GEN_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(TEST_GEN_LIBS)) $(OUTPUT)/sys_hwprobe.o: ../hwprobe/sys_hwprobe.S - $(CC) -static -c -o$@ $(CFLAGS) $^ + $(CC) -static -c -o $@ $(CFLAGS) $^ $(OUTPUT)/v_helpers.o: v_helpers.c - $(CC) -static -c -o$@ $(CFLAGS) $^ + $(CC) -static -c -o $@ $(CFLAGS) $^ $(OUTPUT)/vstate_prctl: vstate_prctl.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/vstate_exec_nolibc: vstate_exec_nolibc.c $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \ -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \ -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc $(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/validate_v_ptrace: validate_v_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ EXTRA_CLEAN += $(TEST_GEN_OBJ) From 84894ceb3c2ef5c5404359efd4edc6c438aa6d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 3002/5214] riscv: Implement ARCH_HAS_CC_CAN_LINK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic CC_CAN_LINK detection relies on -m32/-m64 compiler flags. These are not supported by riscv compilers. Use architecture-specific logic using -mabi instead. Prefer the 'd' ABI variant when possible as todays toolchains are most likely to provide a libc for that one. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260316-cc-can-link-riscv-v4-1-64c072b456dd@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5754942cf85..a5e4f438e381 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -24,6 +24,7 @@ config RISCV select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_BINFMT_FLAT + select ARCH_HAS_CC_CAN_LINK select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_VM_PGTABLE @@ -1353,6 +1354,20 @@ config PORTABLE config ARCH_PROC_KCORE_TEXT def_bool y +config ARCH_CC_CAN_LINK + bool + default $(cc_can_link_user,-march=rv64g -mabi=lp64d) if 64BIT && FPU + default $(cc_can_link_user,-march=rv64g -mabi=lp64) if 64BIT + default $(cc_can_link_user,-march=rv32g -mabi=ilp32d) if FPU + default $(cc_can_link_user,-march=rv32g -mabi=ilp32) + +config ARCH_USERFLAGS + string + default "-march=rv64g -mabi=lp64d" if 64BIT && FPU + default "-march=rv64g -mabi=lp64" if 64BIT + default "-march=rv32g -mabi=ilp32d" if FPU + default "-march=rv32g -mabi=ilp32" + menu "Power management options" source "kernel/power/Kconfig" From ecbf894165a2e86b0830eb82be49f861da2a9e0b Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3003/5214] riscv: Fix ftrace_graph_ret_addr() to use the correct task pointer The walk_stackframe() function is used to unwind the stack of a given task. When function graph tracing is enabled, ftrace_graph_ret_addr() is called to resolve the original return address if it was modified by the tracer. The current code incorrectly passes 'current' instead of 'task' to ftrace_graph_ret_addr(). This causes incorrect return address resolution when unwinding a stack of a different task (e.g., when the task is blocked in __switch_to). Fix this by passing 'task' instead of 'current' to match the behavior of other architectures (arm64, loongarch, powerpc, s390, x86). Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260408092915.46408-1-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index b41b6255751c..2692d3a06afa 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -87,7 +87,7 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, } else { fp = READ_ONCE_TASK_STACK(task, frame->fp); pc = READ_ONCE_TASK_STACK(task, frame->ra); - pc = ftrace_graph_ret_addr(current, &graph_idx, pc, + pc = ftrace_graph_ret_addr(task, &graph_idx, pc, &frame->ra); if (pc >= (unsigned long)handle_exception && pc < (unsigned long)&ret_from_exception_end) { From 3e17a4b443bbaecc723a2d51faeaa1a0097e43e9 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3004/5214] riscv: module: Use generic cmp_int() instead of custom cmp_3way() The module-sections.c file defines a custom cmp_3way() macro to perform 3-way comparisons during relocation sorting. Instead of maintaining our own implementation, use the generic cmp_int() macro provided by the already included . This removes redundant code and relies on standard kernel interfaces. Signed-off-by: Florian Schmaus Link: https://patch.msgid.link/20260512063231.708256-1-florian.schmaus@codasip.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/module-sections.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c index 98eaac6f6606..b3b11b7f7ed9 100644 --- a/arch/riscv/kernel/module-sections.c +++ b/arch/riscv/kernel/module-sections.c @@ -56,17 +56,15 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val) return (unsigned long)&plt[i]; } -#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b)) - static int cmp_rela(const void *a, const void *b) { const Elf_Rela *x = a, *y = b; int i; /* sort by type, symbol index and addend */ - i = cmp_3way(x->r_info, y->r_info); + i = cmp_int(x->r_info, y->r_info); if (i == 0) - i = cmp_3way(x->r_addend, y->r_addend); + i = cmp_int(x->r_addend, y->r_addend); return i; } From 77d980d06f854e77f21b9ffe48da266af9f44a1b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3005/5214] riscv: Implement _THIS_IP_ using inline asm Both GCC [1] and Clang [2] consider the generic version of _THIS_IP_ to be broken: #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) In particular, the address of a label is only expected to be used with a computed goto. While the generic version more or less works today, it is known to be brittle and may break with current and future optimizations. For example, Clang -O2 always returns 1 when this function is inlined: static inline unsigned long get_ip(void) { return ({ __label__ __here; __here: (unsigned long)&&__here; }); } Fix it by overriding _THIS_IP_ in (which is included by ) using an architecture-specific inline asm version. Additionally, avoiding taking the address of a label prevents compilers from emitting spurious indirect branch targets (e.g. ENDBR or BTI) under control-flow integrity schemes. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120071 [1] Link: https://github.com/llvm/llvm-project/issues/138272 [2] Signed-off-by: Marco Elver Link: https://patch.msgid.link/20260521000436.3931067-1-elver@google.com Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/linkage.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/include/asm/linkage.h b/arch/riscv/include/asm/linkage.h index 9e88ba23cd2b..7e0210ef4eb4 100644 --- a/arch/riscv/include/asm/linkage.h +++ b/arch/riscv/include/asm/linkage.h @@ -9,4 +9,6 @@ #define __ALIGN .balign 4 #define __ALIGN_STR ".balign 4" +#define _THIS_IP_ ({ unsigned long __ip; asm volatile("auipc %0, 0" : "=r" (__ip)); __ip; }) + #endif /* _ASM_RISCV_LINKAGE_H */ From 001428ea4d2c371107cb984108e266adf99f1f1e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 31 May 2026 21:42:35 -0700 Subject: [PATCH 3006/5214] Input: ims-pcu - only expose sysfs attributes on control interface When the driver was converted to use the driver core to instantiate device attributes (via .dev_groups in the usb_driver structure), the attributes started appearing on all interfaces bound to the driver. Since the ims-pcu driver manually claims the secondary data interface during probe, the driver core automatically creates the sysfs attributes for that interface as well. However, the driver only supports these attributes on the primary control interface. Data interfaces lack the necessary descriptors and internal state to handle these requests, and accessing them can lead to unexpected behavior or crashes. Fix this by updating the is_visible() callbacks for both the main and OFN attribute groups to verify that the interface being accessed is indeed the control interface. Fixes: 204d18a7a0c6 ("Input: ims-pcu - use driver core to instantiate device attributes") Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ahp23lj4_vXIeUBl@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 7a1cb9333f53..1383db382bc9 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1249,6 +1250,9 @@ static umode_t ims_pcu_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return 0; + if (pcu->bootloader_mode) { if (attr != &dev_attr_update_firmware_status.attr && attr != &dev_attr_update_firmware.attr && @@ -1488,6 +1492,9 @@ static umode_t ims_pcu_ofn_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return SYSFS_GROUP_INVISIBLE; + /* * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor. */ From 2c9b85a14abb4811e8d4773ccd13559e59792efb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:41 -0700 Subject: [PATCH 3007/5214] Input: ims-pcu - fix logic error in packet reset ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies that the start-of-packet delimiter has already been received. This causes the protocol parser to skip waiting for the next STX byte and potentially process garbage data. Correctly set have_stx to false when resetting the packet state. Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 1383db382bc9..32b5b01b2f2e 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -441,7 +441,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) static void ims_pcu_reset_packet(struct ims_pcu *pcu) { - pcu->have_stx = true; + pcu->have_stx = false; pcu->have_dle = false; pcu->read_pos = 0; pcu->check_sum = 0; From 441c510a649c8ddce38aa0311334ed8bb546b36c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:22:55 -0700 Subject: [PATCH 3008/5214] Input: ims-pcu - release data interface on disconnect During probe the driver claims the data interface, but it never releases it. Release it in disconnect to avoid leaving it permanently claimed. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 32b5b01b2f2e..75a0cadf7be9 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2097,6 +2097,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) ims_pcu_destroy_application_mode(pcu); ims_pcu_buffers_free(pcu); + usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf); kfree(pcu); } From 462a999917755a3bf77448dfd64307963cf0a9f0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:24:47 -0700 Subject: [PATCH 3009/5214] Input: ims-pcu - fix use-after-free and double-free in disconnect ims_pcu_disconnect() only intended to perform cleanup when the primary (control) interface is unbound. However, it currently relies on the interface class to distinguish between control and data interfaces. A malicious device could present a data interface with the same class as the control interface, leading to premature cleanup and potential use-after-free or double-free. Switch to verifying that the interface being disconnected is indeed the control interface. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 75a0cadf7be9..694490b24629 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2078,7 +2078,6 @@ static int ims_pcu_probe(struct usb_interface *intf, static void ims_pcu_disconnect(struct usb_interface *intf) { struct ims_pcu *pcu = usb_get_intfdata(intf); - struct usb_host_interface *alt = intf->cur_altsetting; usb_set_intfdata(intf, NULL); @@ -2086,7 +2085,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) * See if we are dealing with control or data interface. The cleanup * happens when we unbind primary (control) interface. */ - if (alt->desc.bInterfaceClass != USB_CLASS_COMM) + if (intf != pcu->ctrl_intf) return; ims_pcu_stop_io(pcu); From ca459e237bc49567649c56bc72e4c602fb92fd67 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:11 -0700 Subject: [PATCH 3010/5214] Input: ims-pcu - fix type confusion in CDC union descriptor parsing The driver currently trusts the bMasterInterface0 from the CDC union descriptor without verifying that it matches the interface being probed. This could lead to the driver overwriting the private data of another interface. Validate that the control interface found in the descriptor is indeed the one we are probing. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 694490b24629..ea11368834a3 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1700,7 +1700,7 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, union_desc->bMasterInterface0); - if (!pcu->ctrl_intf) + if (pcu->ctrl_intf != intf) return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; From d48795b5cd6828d36b707e8d62fc9e5c90e004ab Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:31 -0700 Subject: [PATCH 3011/5214] Input: ims-pcu - fix firmware leak in async update The firmware object was not being released if validation failed. Use __free(firmware) to ensure the firmware is always released. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index ea11368834a3..4f8265c0772f 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -935,9 +935,10 @@ static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, return retval; } -static void ims_pcu_process_async_firmware(const struct firmware *fw, +static void ims_pcu_process_async_firmware(const struct firmware *_fw, void *context) { + const struct firmware *fw __free(firmware) = _fw; struct ims_pcu *pcu = context; int error; @@ -957,8 +958,6 @@ static void ims_pcu_process_async_firmware(const struct firmware *fw, scoped_guard(mutex, &pcu->cmd_mutex) ims_pcu_handle_firmware_update(pcu, fw); - release_firmware(fw); - out: complete(&pcu->async_firmware_done); } From 411b8c4b274737c3bf08e1e025801161603cfffc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:04 -0700 Subject: [PATCH 3012/5214] Input: ims-pcu - fix race condition in reset_device sysfs callback The ims_pcu_reset_device() sysfs callback calls ims_pcu_execute_command() without acquiring pcu->cmd_mutex. This can lead to data races and corruption of the shared command buffer if triggered concurrently with other commands. Acquire pcu->cmd_mutex before calling ims_pcu_execute_command(). Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4f8265c0772f..39bc02ef3e53 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1153,6 +1153,8 @@ static ssize_t ims_pcu_reset_device(struct device *dev, dev_info(pcu->dev, "Attempting to reset device\n"); + guard(mutex)(&pcu->cmd_mutex); + error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); if (error) { dev_info(pcu->dev, From baf56975806534268e24acf9a8abb1c447ce11e9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:26 -0700 Subject: [PATCH 3013/5214] Input: ims-pcu - validate control endpoint type The driver currently assumes that the first endpoint of the control interface is an interrupt IN endpoint without verifying it. A malicious device could provide a different endpoint type, which would then be passed to usb_fill_int_urb(), potentially leading to kernel warnings or undefined behavior. Verify that the control endpoint is an interrupt IN endpoint. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 39bc02ef3e53..2b49d1a5473f 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1710,6 +1710,12 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc return -ENODEV; pcu->ep_ctrl = &alt->endpoint[0].desc; + if (!usb_endpoint_is_int_in(pcu->ep_ctrl)) { + dev_err(pcu->dev, + "Control endpoint is not INTERRUPT IN\n"); + return -EINVAL; + } + pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); pcu->data_intf = usb_ifnum_to_if(pcu->udev, From 403b0a6970b1084bb27907c0f8225801fdd0fe1d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:21 -0700 Subject: [PATCH 3014/5214] Input: ims-pcu - fix out-of-bounds read in ims_pcu_irq() debug logging The debug logging in ims_pcu_irq() unconditionally prints data from pcu->urb_in_buf. However, if the interrupt fired for pcu->urb_ctrl, the actual data resides in pcu->urb_ctrl_buf. If urb->actual_length for the control URB exceeds pcu->max_in_size, this leads to an out-of-bounds read. Fix this by printing from the correct buffer associated with the URB. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 2b49d1a5473f..6bacd7e56e68 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1536,7 +1536,7 @@ static void ims_pcu_irq(struct urb *urb) } dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__, - urb->actual_length, urb->actual_length, pcu->urb_in_buf); + urb->actual_length, urb->actual_length, urb->transfer_buffer); if (urb == pcu->urb_in) ims_pcu_process_data(pcu, urb); From 8adf4289d945e8990e4336436a97da71d21d2cae Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:44 -0700 Subject: [PATCH 3015/5214] Input: ims-pcu - fix DMA mapping violation in line setup In ims_pcu_line_setup(), the driver uses pcu->cmd_buf as a transfer buffer for usb_control_msg(). However, pcu->cmd_buf is embedded in the struct ims_pcu allocation, which violates DMA mapping rules regarding cacheline alignment. Use a heap-allocated buffer for the line coding data instead. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 6bacd7e56e68..371c605efcf4 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1797,11 +1797,16 @@ static void ims_pcu_stop_io(struct ims_pcu *pcu) static int ims_pcu_line_setup(struct ims_pcu *pcu) { struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; - struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; + struct usb_cdc_line_coding *line __free(kfree) = + kmalloc(sizeof(*line), GFP_KERNEL); int error; - memset(line, 0, sizeof(*line)); + if (!line) + return -ENOMEM; + line->dwDTERate = cpu_to_le32(57600); + line->bCharFormat = USB_CDC_1_STOP_BITS; + line->bParityType = USB_CDC_NO_PARITY; line->bDataBits = 8; error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), From 48c9d92fd4ee3a8f5d2cb46c802a0eff8e67c79c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:40:54 -0700 Subject: [PATCH 3016/5214] Input: ims-pcu - add response length checks The driver processes response data from device buffers without verifying that the device actually sent enough data. This can lead to out-of-bounds reads or processing stale data. Add checks for the expected response length before accessing the buffers. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 87 +++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 371c605efcf4..a730cc2690d5 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -407,7 +407,16 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) static void ims_pcu_report_events(struct ims_pcu *pcu) { - u32 data = get_unaligned_be32(&pcu->read_buf[3]); + u32 data; + + /* 6-axis setting (1 byte) + button data + checksum */ + if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) { + dev_warn(pcu->dev, "Short buttons report: %d bytes\n", + pcu->read_pos); + return; + } + + data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]); ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); if (pcu->gamepad) @@ -687,11 +696,19 @@ static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu, return error; } - if (expected_response && pcu->cmd_buf[2] != expected_response) { - dev_err(pcu->dev, - "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", - pcu->cmd_buf[2], expected_response); - return -EINVAL; + if (expected_response) { + if (pcu->cmd_buf_len < 3) { + dev_err(pcu->dev, "Short response from bootloader: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + + if (pcu->cmd_buf[2] != expected_response) { + dev_err(pcu->dev, + "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", + pcu->cmd_buf[2], expected_response); + return -EINVAL; + } } return 0; @@ -719,6 +736,12 @@ static int ims_pcu_get_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + IMS_PCU_SET_INFO_SIZE + 1) { + dev_err(pcu->dev, "Short GET_INFO response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + memcpy(pcu->part_number, &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], sizeof(pcu->part_number)); @@ -816,6 +839,12 @@ static int ims_pcu_verify_block(struct ims_pcu *pcu, return error; } + if (pcu->cmd_buf_len < IMS_PCU_BL_DATA_OFFSET + sizeof(*fragment) + len + 1) { + dev_err(pcu->dev, "Short READ_APP response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; if (get_unaligned_le32(&fragment->addr) != addr || fragment->len != len) { @@ -1009,6 +1038,10 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev) error); /* Assume the LED is OFF */ brightness = LED_OFF; + } else if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short GET_BRIGHTNESS response: %d bytes\n", + pcu->cmd_buf_len); + brightness = LED_OFF; } else { brightness = get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1287,6 +1320,12 @@ static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_GET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1307,6 +1346,12 @@ static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_SET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1850,6 +1895,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_FW_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->fw_version, sizeof(pcu->fw_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1862,6 +1913,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_BL_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->bl_version, sizeof(pcu->bl_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1874,6 +1931,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short RESET_REASON response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->reset_reason, sizeof(pcu->reset_reason), "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1900,6 +1963,12 @@ static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short GET_DEVICE_ID response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id); @@ -1991,6 +2060,12 @@ static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 15 + 4 + 1) { + dev_err(pcu->dev, "Short QUERY_DEVICE response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + pcu->fw_start_addr = get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); pcu->fw_end_addr = From d4579af29e67ca8722db0a1194227f8015c8981d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 21:42:39 -0700 Subject: [PATCH 3017/5214] Input: ims-pcu - fix potential infinite loop in CDC union descriptor parsing The driver parses CDC union descriptors in ims_pcu_get_cdc_union_desc() by iterating through the extra descriptor data. However, it does not verify that the bLength of each descriptor is at least 2. A malicious device could provide a descriptor with bLength = 0, leading to an infinite loop in the driver. Add a check to ensure bLength is at least 2 before proceeding with parsing. Fixes: 628329d52474 (Input: add IMS Passenger Control Unit driver) Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index a730cc2690d5..b1ff8c70877f 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1709,8 +1709,9 @@ ims_pcu_get_cdc_union_desc(struct usb_interface *intf) while (buflen >= sizeof(*union_desc)) { union_desc = (struct usb_cdc_union_desc *)buf; - if (union_desc->bLength > buflen) { - dev_err(&intf->dev, "Too large descriptor\n"); + if (union_desc->bLength < 2 || union_desc->bLength > buflen) { + dev_err(&intf->dev, "Invalid descriptor length: %d\n", + union_desc->bLength); return NULL; } From 49476d58f2171afc2e899da8040710d2c37760af Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Thu, 4 Jun 2026 22:26:01 +0800 Subject: [PATCH 3018/5214] KVM: riscv: Check hugetlb block mappings against memslot bounds RISC-V KVM has used the hugetlb VMA size directly as the G-stage mapping size since stage-2 page table support was added. That is safe only if the block covered by the fault is fully contained in the memslot and the userspace address has the same offset as the GPA within that block. The THP path already checks those constraints before installing a PMD block mapping. The hugetlb path did not, so an unaligned memslot could make KVM install a PMD or PUD sized G-stage block that covers memory outside the slot or maps the wrong host pages. Pass the target mapping size into fault_supports_gstage_huge_mapping(). The same helper can be used for both THP PMD mappings and hugetlb PMD/PUD mappings. Select hugetlb mapping sizes through the same memslot-boundary check, falling back from PUD to PMD to PAGE_SIZE. When a smaller hugetlb mapping size is selected, fault the GFN aligned to that selected size instead of the original VMA size. Also keep hugetlb mappings out of transparent_hugepage_adjust(). Once the hugetlb path has chosen PAGE_SIZE, promoting it again through the THP helper would miss the hugetlb fallback decision. Fixes: 9d05c1fee837 ("RISC-V: KVM: Implement stage2 page table programming") Signed-off-by: Jinyu Tang Reviewed-by: Nutty Liu Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260604142602.3582602-2-tjytimi@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/mmu.c | 56 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 38d3eaa1c5e5..082f9b261733 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -302,7 +302,8 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) } static bool fault_supports_gstage_huge_mapping(struct kvm_memory_slot *memslot, - unsigned long hva) + unsigned long hva, + unsigned long map_size) { hva_t uaddr_start, uaddr_end; gpa_t gpa_start; @@ -316,8 +317,8 @@ static bool fault_supports_gstage_huge_mapping(struct kvm_memory_slot *memslot, /* * Pages belonging to memslots that don't have the same alignment - * within a PMD for userspace and GPA cannot be mapped with g-stage - * PMD entries, because we'll end up mapping the wrong pages. + * within a huge page for userspace and GPA cannot be mapped with + * g-stage block entries, because we'll end up mapping the wrong pages. * * Consider a layout like the following: * @@ -337,7 +338,7 @@ static bool fault_supports_gstage_huge_mapping(struct kvm_memory_slot *memslot, * e -> g * f -> h */ - if ((gpa_start & (PMD_SIZE - 1)) != (uaddr_start & (PMD_SIZE - 1))) + if ((gpa_start & (map_size - 1)) != (uaddr_start & (map_size - 1))) return false; /* @@ -352,7 +353,8 @@ static bool fault_supports_gstage_huge_mapping(struct kvm_memory_slot *memslot, * userspace_addr or the base_gfn, as both are equally aligned (per * the check above) and equally sized. */ - return (hva >= ALIGN(uaddr_start, PMD_SIZE)) && (hva < ALIGN_DOWN(uaddr_end, PMD_SIZE)); + return (hva >= ALIGN(uaddr_start, map_size)) && + (hva < ALIGN_DOWN(uaddr_end, map_size)); } static int get_hva_mapping_size(struct kvm *kvm, @@ -420,7 +422,7 @@ static unsigned long transparent_hugepage_adjust(struct kvm *kvm, * sure that the HVA and GPA are sufficiently aligned and that the * block map is contained within the memslot. */ - if (fault_supports_gstage_huge_mapping(memslot, hva)) { + if (fault_supports_gstage_huge_mapping(memslot, hva, PMD_SIZE)) { int sz; sz = get_hva_mapping_size(kvm, hva); @@ -437,6 +439,28 @@ static unsigned long transparent_hugepage_adjust(struct kvm *kvm, return PAGE_SIZE; } +static unsigned long hugetlb_mapping_size(struct kvm_memory_slot *memslot, + unsigned long hva, + unsigned long map_size) +{ + switch (map_size) { +#ifndef CONFIG_32BIT + case PUD_SIZE: + if (fault_supports_gstage_huge_mapping(memslot, hva, PUD_SIZE)) + return PUD_SIZE; + fallthrough; +#endif + case PMD_SIZE: + if (fault_supports_gstage_huge_mapping(memslot, hva, PMD_SIZE)) + return PMD_SIZE; + fallthrough; + case PAGE_SIZE: + return PAGE_SIZE; + default: + return map_size; + } +} + static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm, struct kvm_memory_slot *memslot, gpa_t gpa, @@ -514,6 +538,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, { int ret; kvm_pfn_t hfn; + bool is_hugetlb; bool writable; short vma_pageshift; gfn_t gfn = gpa >> PAGE_SHIFT; @@ -551,16 +576,23 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, return -EFAULT; } - if (is_vm_hugetlb_page(vma)) + is_hugetlb = is_vm_hugetlb_page(vma); + if (is_hugetlb) vma_pageshift = huge_page_shift(hstate_vma(vma)); else vma_pageshift = PAGE_SHIFT; vma_pagesize = 1ULL << vma_pageshift; if (logging || (vma->vm_flags & VM_PFNMAP)) vma_pagesize = PAGE_SIZE; + else if (is_hugetlb) + vma_pagesize = hugetlb_mapping_size(memslot, hva, vma_pagesize); + /* + * For hugetlb mappings, vma_pagesize might have been reduced from the + * VMA size to a smaller safe mapping size. + */ if (vma_pagesize == PMD_SIZE || vma_pagesize == PUD_SIZE) - gfn = (gpa & huge_page_mask(hstate_vma(vma))) >> PAGE_SHIFT; + gfn = ALIGN_DOWN(gpa, vma_pagesize) >> PAGE_SHIFT; /* * Read mmu_invalidate_seq so that KVM can detect if the results of @@ -602,8 +634,12 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, if (mmu_invalidate_retry(kvm, mmu_seq)) goto out_unlock; - /* Check if we are backed by a THP and thus use block mapping if possible */ - if (!logging && (vma_pagesize == PAGE_SIZE)) + /* + * Check if we are backed by a THP and thus use block mapping if + * possible. Hugetlb mappings already selected their target size above, + * so do not promote them through the THP helper. + */ + if (!logging && !is_hugetlb && vma_pagesize == PAGE_SIZE) vma_pagesize = transparent_hugepage_adjust(kvm, memslot, hva, &hfn, &gpa); if (writable) { From 3a358c78093f98a70d84c934b7054f636bc846f2 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Fri, 5 Jun 2026 18:06:44 +0200 Subject: [PATCH 3019/5214] docs: memfd_preservation: fix rendering of ABI documentation The "memfd Live Update ABI" section in include/linux/kho/abi/memfd.h currently does not render in the exported documentation. This is because it should not include the "DOC:" in its reference. Drop it to ensure correct rendering. Tested by running make htmldocs. Fixes: 15fc11bb2cb6 ("docs: add documentation for memfd preservation via LUO") Signed-off-by: Pratyush Yadav (Google) Tested-by: Randy Dunlap Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260605160645.3650271-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- Documentation/mm/memfd_preservation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/mm/memfd_preservation.rst b/Documentation/mm/memfd_preservation.rst index a8a5b476afd3..c908a12dffa7 100644 --- a/Documentation/mm/memfd_preservation.rst +++ b/Documentation/mm/memfd_preservation.rst @@ -11,7 +11,7 @@ Memfd Preservation ABI ====================== .. kernel-doc:: include/linux/kho/abi/memfd.h - :doc: DOC: memfd Live Update ABI + :doc: memfd Live Update ABI .. kernel-doc:: include/linux/kho/abi/memfd.h :internal: From c8aafbebfc95d9b65b9b86573dc47723fa621358 Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Thu, 4 Jun 2026 22:26:02 +0800 Subject: [PATCH 3020/5214] KVM: selftests: Add a hugetlb memslot alignment test mode kvm_page_table_test can already exercise hugetlb-backed guest memory, but it always creates the test memslot with GPA alignment matching the hugetlb backing size. That misses the case where a valid hugetlb memslot is later moved so that the memslot GPA and HVA no longer have the same offset within the backing huge page. Add a -u option that moves the test memslot GPA by one guest page after creating the hugetlb memslot. The memslot is created through the normal helper first, so the backing allocation remains valid and hugetlb aligned. Moving the memslot then creates a deliberate HVA/GPA offset mismatch before the guest mapping is installed. This mode is useful for checking that architecture MMUs do not install a block mapping when the block would map the wrong host pages or cover memory outside the memslot. The option is restricted to hugetlb-backed test memory because it's specifically about hugetlb block mapping eligibility. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260604142602.3582602-3-tjytimi@163.com Signed-off-by: Anup Patel --- .../selftests/kvm/kvm_page_table_test.c | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/kvm_page_table_test.c b/tools/testing/selftests/kvm/kvm_page_table_test.c index fc5242fb956f..a910e3abb8c7 100644 --- a/tools/testing/selftests/kvm/kvm_page_table_test.c +++ b/tools/testing/selftests/kvm/kvm_page_table_test.c @@ -230,6 +230,7 @@ struct test_params { u64 phys_offset; u64 test_mem_size; enum vm_mem_backing_src_type src_type; + bool misalign_slot_gpa; }; static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) @@ -244,6 +245,7 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) u64 guest_num_pages; u64 alignment; void *host_test_mem; + struct userspace_mem_region *region; struct kvm_vm *vm; /* Align up the test memory size */ @@ -276,13 +278,22 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) /* Add an extra memory slot with specified backing src type */ vm_userspace_mem_region_add(vm, src_type, guest_test_phys_mem, TEST_MEM_SLOT_INDEX, guest_num_pages, 0); + region = memslot2region(vm, TEST_MEM_SLOT_INDEX); + host_test_mem = region->host_mem; + + if (p->misalign_slot_gpa) { + TEST_ASSERT(is_backing_src_hugetlb(src_type), + "Memslot GPA misalignment requires hugetlb backing"); + TEST_ASSERT(guest_num_pages > 1, + "Need at least two guest pages to misalign memslot GPA"); + + guest_test_phys_mem += guest_page_size; + vm_mem_region_move(vm, TEST_MEM_SLOT_INDEX, guest_test_phys_mem); + } /* Do mapping(GVA->GPA) for the testing memory slot */ virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages); - /* Cache the HVA pointer of the region */ - host_test_mem = addr_gpa2hva(vm, (gpa_t)guest_test_phys_mem); - /* Export shared structure test_args to guest */ sync_global_to_guest(vm, test_args); @@ -417,8 +428,8 @@ static void run_test(enum vm_guest_mode mode, void *arg) static void help(char *name) { puts(""); - printf("usage: %s [-h] [-p offset] [-m mode] " - "[-b mem-size] [-v vcpus] [-s mem-type]\n", name); + printf("usage: %s [-h] [-p offset] [-m mode] [-b mem-size]\n", name); + printf(" [-v vcpus] [-s mem-type] [-u]\n"); puts(""); printf(" -p: specify guest physical test memory offset\n" " Warning: a low offset can conflict with the loaded test code.\n"); @@ -428,6 +439,8 @@ static void help(char *name) printf(" -v: specify the number of vCPUs to run\n" " (default: 1)\n"); backing_src_help("-s"); + printf(" -u: move the test memslot GPA by one guest page after creating\n" + " the memslot, forcing a hugetlb HVA/GPA offset mismatch\n"); puts(""); } @@ -442,7 +455,7 @@ int main(int argc, char *argv[]) guest_modes_append_default(); - while ((opt = getopt(argc, argv, "hp:m:b:v:s:")) != -1) { + while ((opt = getopt(argc, argv, "hp:m:b:v:s:u")) != -1) { switch (opt) { case 'p': p.phys_offset = strtoull(optarg, NULL, 0); @@ -461,6 +474,9 @@ int main(int argc, char *argv[]) case 's': p.src_type = parse_backing_src_type(optarg); break; + case 'u': + p.misalign_slot_gpa = true; + break; case 'h': default: help(argv[0]); From 3b60f96b852a139ae8256426f2a69e2410f82c9c Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 3 May 2026 05:03:31 +0100 Subject: [PATCH 3021/5214] riscv: replace select with dependency for visible RELOCATABLE RANDOMIZE_BASE currently selects RELOCATABLE even though RELOCATABLE is visible to users. Some other architectures, like x86, use 'depends on' for RELOCATABLE in their definition of RANDOMIZE_BASE, so let's do the same here. This select-visible Kconfig misusage was detected by Kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Link: https://patch.msgid.link/20260503040331.71875-1-julianbraha@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a5e4f438e381..b1101c336c7c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -1164,7 +1164,7 @@ config RELOCATABLE config RANDOMIZE_BASE bool "Randomize the address of the kernel image" - select RELOCATABLE + depends on RELOCATABLE depends on MMU && 64BIT help Randomizes the virtual address at which the kernel image is From 9f19a42083f5c1bcb7b0c330fa385d77fbd424fb Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3022/5214] riscv: dead code cleanup in kconfig for RISCV_PROBE_VECTOR_UNALIGNED_ACCESS The same Kconfig statement 'depends on RISCV_ISA_V' appears twice for RISCV_PROBE_VECTOR_UNALIGNED_ACCESS. The first instance is in its choice menu, "Vector unaligned Accesses Support", making the second instance in its specific Kconfig definition dead code. I propose removing this second instance. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Reviewed-by: Jesse Taube Link: https://patch.msgid.link/20260329203249.563434-1-julianbraha@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b1101c336c7c..bf4bc39c76b8 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -1024,7 +1024,6 @@ choice config RISCV_PROBE_VECTOR_UNALIGNED_ACCESS bool "Probe speed of vector unaligned accesses" select RISCV_VECTOR_MISALIGNED - depends on RISCV_ISA_V help During boot, the kernel will run a series of tests to determine the speed of vector unaligned accesses if they are supported. This probing From 9fd44698b11dbdf4d29fdef0f0da25f320c13854 Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3023/5214] riscv: ftrace: select HAVE_BUILDTIME_MCOUNT_SORT RISC-V already satisfies all prerequisites for build-time mcount sorting: the sorttable host tool handles EM_RISCV in its machine-type dispatch, and the __mcount_loc section entries are stored as direct virtual addresses in the final vmlinux binary, so no relocation processing is required during the sort step. Select HAVE_BUILDTIME_MCOUNT_SORT so that BUILDTIME_MCOUNT_SORT is automatically enabled when DYNAMIC_FTRACE is configured. This allows sorttable to sort the __mcount_loc section at link time, making the run-time ftrace initialisation path skip the software sort and reducing kernel startup overhead. Verified with CONFIG_FTRACE_SORT_STARTUP_TEST=y, which confirms that the section produced by the build is already in ascending order: [ 0.000000] ftrace section at ffffffff81015a60 sorted properly Signed-off-by: Chen Pei Tested-by: Guo Ren Link: https://patch.msgid.link/20260409114736.907-1-cp0613@linux.alibaba.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index bf4bc39c76b8..ce5f4342e2f2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -152,6 +152,7 @@ config RISCV select HAVE_ARCH_USERFAULTFD_WP if 64BIT && MMU && USERFAULTFD && RISCV_ISA_SVRSW60T59B select HAVE_ARCH_VMAP_STACK if MMU && 64BIT select HAVE_ASM_MODVERSIONS + select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_CONTEXT_TRACKING_USER select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU From 063e01ded573b62aa90e4a60a73cff9070bb96b9 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 3024/5214] riscv: kexec_elf: Remove unused pr_fmt definition Remove the pr_fmt macro as no pr_*() calls exist in this file. The prefix string "kexec_image: " is also not appropriate for kexec_elf.c, if pr_fmt is needed in the future, referring to kexec_image.c, a more appropriate prefix like "kexec_file(elf): " can be added at that time. Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260525115159.100177-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kexec_elf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c index 531d348db84d..05fd33104f3d 100644 --- a/arch/riscv/kernel/kexec_elf.c +++ b/arch/riscv/kernel/kexec_elf.c @@ -10,8 +10,6 @@ * for kernel. */ -#define pr_fmt(fmt) "kexec_image: " fmt - #include #include #include From 2b2b207e1162e577cd6208e184d3d3a0fcfa9cca Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 3025/5214] riscv: cpu_ops: Change return value type of cpu_is_stopped() to bool In the original sbi_cpu_is_stopped(), if rc doesn't equal to the SBI_HSM_STATE_STOPPED, it will return rc to the caller directly. But there is a hidden problem, the rc could be SBI_HSM_STATE_STARTED, if so, this function will report cpu stopped while the cpu isn't really stopped. Furthermore, from the name of cpu_is_stopped(), it gives a sense the return value is a bool type, true means the cpu is stopped, conversely false means the cpu is not stopped. Here change the return value type to bool and change the callers accordingly. This could fix the above two issues. Fixes: f1e58583b9c7c ("RISC-V: Support cpu hotplug") Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-1-hui.wang@canonical.com [pjw@kernel.org: cleaned up some of the pr_warn() messages] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cpu_ops.h | 2 +- arch/riscv/kernel/cpu-hotplug.c | 4 ++-- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/cpu_ops.h b/arch/riscv/include/asm/cpu_ops.h index 176b570ef982..065811fca594 100644 --- a/arch/riscv/include/asm/cpu_ops.h +++ b/arch/riscv/include/asm/cpu_ops.h @@ -24,7 +24,7 @@ struct cpu_operations { struct task_struct *tidle); #ifdef CONFIG_HOTPLUG_CPU void (*cpu_stop)(void); - int (*cpu_is_stopped)(unsigned int cpu); + bool (*cpu_is_stopped)(unsigned int cpu); #endif }; diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index a0ee426f6d93..0bc56d8381b6 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -57,8 +57,8 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) /* Verify from the firmware if the cpu is really stopped*/ if (cpu_ops->cpu_is_stopped) ret = cpu_ops->cpu_is_stopped(cpu); - if (ret) - pr_warn("CPU%u may not have stopped: %d\n", cpu, ret); + if (!ret) + pr_warn("CPU%u may not have stopped\n", cpu); } /* diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 00aff669f5f2..146ceab1011f 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -88,16 +88,19 @@ static void sbi_cpu_stop(void) pr_crit("Unable to stop the cpu %d (%d)\n", smp_processor_id(), ret); } -static int sbi_cpu_is_stopped(unsigned int cpuid) +static bool sbi_cpu_is_stopped(unsigned int cpuid) { int rc; unsigned long hartid = cpuid_to_hartid_map(cpuid); rc = sbi_hsm_hart_get_status(hartid); - if (rc == SBI_HSM_STATE_STOPPED) - return 0; - return rc; + if (rc != SBI_HSM_STATE_STOPPED) { + pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc); + return false; + } + + return true; } #endif From edccaf7be062f3400cdadb45b0e2b4db4cdd8c26 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 3026/5214] riscv: cpu_ops_sbi: No need to be bothered to check ret.error If the ret.error equals to 0, the sbi_err_map_linux_errno() can also handle it, i.e. if ret.error is SBI_SUCCESS, it will return 0 immediately, so no need to be bothered to check ret.error here. Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-2-hui.wang@canonical.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 146ceab1011f..ee6e4b5cc39e 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -30,10 +30,8 @@ static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, saddr, priv, 0, 0, 0); - if (ret.error) - return sbi_err_map_linux_errno(ret.error); - else - return 0; + + return sbi_err_map_linux_errno(ret.error); } #ifdef CONFIG_HOTPLUG_CPU @@ -43,10 +41,7 @@ static int sbi_hsm_hart_stop(void) ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0); - if (ret.error) - return sbi_err_map_linux_errno(ret.error); - else - return 0; + return sbi_err_map_linux_errno(ret.error); } static int sbi_hsm_hart_get_status(unsigned long hartid) From dea5db8c5775704f24519d130482d9ceaf66c441 Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 3027/5214] riscv: defconfig: Enable Eswin SoCs Enable support for Eswin SoCs in the default configuration, so that defconfig kernel/DTs are bootable on Hifive Premier P550 Board. Signed-off-by: Anirudh Srinivasan Link: https://patch.msgid.link/20260603-eswin_defconfig-v1-1-8471691d3153@oss.tenstorrent.com Signed-off-by: Paul Walmsley --- arch/riscv/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index c2c37327b987..74ba5acc12a4 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -24,6 +24,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_PROFILING=y CONFIG_ARCH_ANDES=y CONFIG_ARCH_ANLOGIC=y +CONFIG_ARCH_ESWIN=y CONFIG_ARCH_MICROCHIP=y CONFIG_ARCH_SIFIVE=y CONFIG_ARCH_SOPHGO=y From f3336b48cf9d3f2d1fc78e3289c0ded2f00876ee Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 3028/5214] riscv: mm: Define DIRECT_MAP_PHYSMEM_END On RISC-V, the actual mappable range of physical address space is dependent on the current MMU mode i.e. satp_mode (See Documentation/arch/riscv/vm-layout.rst). Define the DIRECT_MAP_PHYSMEM_END macro based on the existing virtual address space layout macros to expose this information to get_free_mem_region(). Otherwise, it returns a region that couldn't be mapped, which breaks ZONE_DEVICE. Cc: stable@vger.kernel.org # v6.13+ Tested-by: Han Gao # SG2044 Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260309-riscv-sparsemem-vmemmap-limits-v1-2-f40efe18e3cd@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a1a7c6520a09..5d5756bda82e 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -93,6 +93,16 @@ */ #define vmemmap ((struct page *)VMEMMAP_START - vmemmap_start_pfn) +/* Needed to limit get_free_mem_region() */ +#if defined(CONFIG_FLATMEM) +#define DIRECT_MAP_PHYSMEM_END (phys_ram_base + KERN_VIRT_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM_VMEMMAP) +#define DIRECT_MAP_PHYSMEM_END \ + ((vmemmap_start_pfn + VMEMMAP_SIZE / sizeof(struct page)) * PAGE_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM) +/* DIRECT_MAP_PHYSMEM_END is not limited by VA space assignment in this case */ +#endif + #define PCI_IO_SIZE SZ_16M #define PCI_IO_END VMEMMAP_START #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) From b67a1ee0db0094c6cc158b087be6c334ad881a41 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 3029/5214] riscv: kexec_file: Constrain segment placement to direct map When kexec_file_load places segments with buf_max=ULONG_MAX and top_down=true, they land at the highest available physical addresses. On RISC-V the size of the linear mapping is determined by the active VM mode: SV39 caps the direct map at roughly 128GB, while SV48/SV57 extend the range substantially further. When the installed physical memory exceeds the direct map size of the active mode, top-down placement puts DTB/initrd at physical addresses outside the linearly mapped region. The kexec'd kernel cannot reach them during early boot, triggering a page fault at memcmp in start_kernel. Fix by constraining buf_max to PFN_PHYS(max_low_pfn), which reflects the runtime direct map boundary for the active VM mode (SV39/SV48/ SV57). This keeps all kexec segments within the linearly mapped region while preserving the upstream top_down allocation strategy. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519170641.123517-1-gaohan@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/machine_kexec_file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index 54e2d9552e93..59d4bbc848a8 100644 --- a/arch/riscv/kernel/machine_kexec_file.c +++ b/arch/riscv/kernel/machine_kexec_file.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -266,7 +267,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start, kbuf.image = image; kbuf.buf_min = kernel_start + kernel_len; - kbuf.buf_max = ULONG_MAX; + kbuf.buf_max = PFN_PHYS(max_low_pfn); #ifdef CONFIG_CRASH_DUMP /* Add elfcorehdr */ From bf4a195f063b0a0805c1417f6aad1dd32ea48f0f Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 3030/5214] riscv: cacheinfo: Fix node reference leak in populate_cache_leaves Currently, the while loop drops the reference to prev in each iteration. If the loop terminates early due to a break, the final of_node_put(np) correctly drops the reference to the current node. However, if the loop terminates naturally because np == NULL, calling of_node_put(np) is a no-op. This leaves the last valid node stored in prev without its reference dropped, resulting in a node reference leak. Fix this by changing the final `of_node_put(np)` to `of_node_put(prev)`. Fixes: 94f9bf118f1e ("RISC-V: Fix of_node_* refcount") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Zishun Yi Link: https://patch.msgid.link/20260509074040.1747800-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 26b085dbdd07..6c9a1ef2d45a 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -133,7 +133,7 @@ int populate_cache_leaves(unsigned int cpu) ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); levels = level; } - of_node_put(np); + of_node_put(prev); return 0; } From 8ac35bac70e7e581d673b76878f7691cdadc33b8 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:59 -0600 Subject: [PATCH 3031/5214] riscv: stacktrace: Remove bogus -0x4 offset in non-FP walk_stackframe In the non-frame-pointer version of walk_stackframe, each value read from the stack is treated as a potential return address and has 0x4 subtracted before being used as the program counter. This was intended to convert the return address (the instruction after a call) back to the call site, but it is incorrect: 1. RISC-V has variable-length instructions due to the RVC (compressed instruction) extension. A call instruction can be either 4 bytes (regular) or 2 bytes (compressed, e.g. c.jal). Subtracting a fixed 0x4 assumes all call instructions are 4 bytes, which is wrong for compressed instructions. 2. Stack traces conventionally report return addresses, not call sites. Other architectures (ARM64, x86, ARM) do not subtract instruction size from return addresses in their stack unwinding code. 3. The frame-pointer version of walk_stackframe already dropped the -0x4 offset. Commit b785ec129bd9 ("riscv/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support") replaced "pc = frame->ra - 0x4" with ftrace_graph_ret_addr(), and the commit message explicitly noted that "the original calculation, pc = frame->ra - 4, is buggy when the instruction at the return address happened to be a compressed inst." The non-FP version was simply overlooked. Remove the bogus -0x4 offset to match the FP version and the conventions used by other architectures. Fixes: 5d8544e2d007 ("RISC-V: Generic library routines and assembly") Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260603115329.791603-2-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index 2692d3a06afa..c7555447149b 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task, while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) break; - pc = READ_ONCE_NOCHECK(*ksp++) - 0x4; + pc = READ_ONCE_NOCHECK(*ksp++); } } From 9ee25d0a70ff4494b4e1d266b962d0a574ef318a Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:45 +0800 Subject: [PATCH 3032/5214] riscv: mm: Extract helper mark_new_valid_map() In preparation of a future patch using the same mechanism for non-vmalloc addresses, extract the mark_new_valid_map() helper from flush_cache_vmap(). No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-1-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 0092513c3376..b1a2ac665792 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -43,20 +43,23 @@ do { \ #ifdef CONFIG_64BIT extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; +static inline void mark_new_valid_map(void) +{ + int i; + + /* + * We don't care if concurrently a cpu resets this value since + * the only place this can happen is in handle_exception() where + * an sfence.vma is emitted. + */ + for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) + new_vmalloc[i] = -1ULL; +} #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) { - int i; - - /* - * We don't care if concurrently a cpu resets this value since - * the only place this can happen is in handle_exception() where - * an sfence.vma is emitted. - */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; - } + if (is_vmalloc_or_module_addr((void *)start)) + mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) #endif From 8d6c8c40e733b3fcaf92fed0a078bba2f6941a3b Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:46 +0800 Subject: [PATCH 3033/5214] riscv: kfence: Call mark_new_valid_map() for kfence_unprotect() In kfence_protect_page(), which kfence_unprotect() calls, we cannot send IPIs to other CPUs to ask them to flush TLB. This may lead to those CPUs spuriously faulting on a recently allocated kfence object despite it being valid, leading to false positive use-after-free reports. Fix this by calling mark_new_valid_map() so that the page fault handling code path notices the spurious fault and flushes TLB then retries the access. Update the comment in handle_exception to indicate that new_valid_map_cpus_check also handles kfence_unprotect() spurious faults. Note that kfence_protect() has the same stale TLB entries problem, but that leads to false negatives, which is fine with kfence. Cc: stable@vger.kernel.org Reported-by: Yanko Kaneti Fixes: b3431a8bb336 ("riscv: Fix IPIs usage in kfence_protect_page()") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-2-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/kfence.h | 7 +++++-- arch/riscv/kernel/entry.S | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/riscv/include/asm/kfence.h b/arch/riscv/include/asm/kfence.h index d08bf7fb3aee..29cb3a6ee113 100644 --- a/arch/riscv/include/asm/kfence.h +++ b/arch/riscv/include/asm/kfence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include static inline bool arch_kfence_init_pool(void) @@ -17,10 +18,12 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect) { pte_t *pte = virt_to_kpte(addr); - if (protect) + if (protect) { set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~_PAGE_PRESENT)); - else + } else { set_pte(pte, __pte(pte_val(ptep_get(pte)) | _PAGE_PRESENT)); + mark_new_valid_map(); + } preempt_disable(); local_flush_tlb_kernel_range(addr, addr + PAGE_SIZE); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index d011fb51c59a..6c14ed7a5613 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -136,8 +136,10 @@ SYM_CODE_START(handle_exception) #ifdef CONFIG_64BIT /* - * The RISC-V kernel does not eagerly emit a sfence.vma after each - * new vmalloc mapping, which may result in exceptions: + * The RISC-V kernel does not flush TLBs on all CPUS after each new + * vmalloc mapping or kfence_unprotect(), which may result in + * exceptions: + * * - if the uarch caches invalid entries, the new mapping would not be * observed by the page table walker and an invalidation is needed. * - if the uarch does not cache invalid entries, a reordered access From a50940adfb8f390337d6ef9afe15b66ec498aceb Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:47 +0800 Subject: [PATCH 3034/5214] riscv: mm: Rename new_vmalloc into new_valid_map_cpus Since this mechanism is now used for the kfence pool, which comes from the linear mapping and not vmalloc, rename new_vmalloc into new_valid_map_cpus to avoid misleading readers. No functional change intended. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-3-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 6 ++--- arch/riscv/kernel/entry.S | 38 ++++++++++++++--------------- arch/riscv/mm/init.c | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index b1a2ac665792..8c7a0ef2635a 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,7 +41,7 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; static inline void mark_new_valid_map(void) { @@ -52,8 +52,8 @@ static inline void mark_new_valid_map(void) * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; + for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) + new_valid_map_cpus[i] = -1ULL; } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 6c14ed7a5613..ebf6918b4e9b 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -20,44 +20,44 @@ .section .irqentry.text, "ax" -.macro new_vmalloc_check +.macro new_valid_map_cpus_check REG_S a0, TASK_TI_A0(tp) csrr a0, CSR_CAUSE /* Exclude IRQs */ - blt a0, zero, .Lnew_vmalloc_restore_context_a0 + blt a0, zero, .Lnew_valid_map_cpus_restore_context_a0 REG_S a1, TASK_TI_A1(tp) - /* Only check new_vmalloc if we are in page/protection fault */ + /* Only check new_valid_map_cpus if we are in page/protection fault */ li a1, EXC_LOAD_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_STORE_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_INST_PAGE_FAULT - bne a0, a1, .Lnew_vmalloc_restore_context_a1 + bne a0, a1, .Lnew_valid_map_cpus_restore_context_a1 -.Lnew_vmalloc_kernel_address: +.Lnew_valid_map_cpus_kernel_address: /* Is it a kernel address? */ csrr a0, CSR_TVAL - bge a0, zero, .Lnew_vmalloc_restore_context_a1 + bge a0, zero, .Lnew_valid_map_cpus_restore_context_a1 /* Check if a new vmalloc mapping appeared that could explain the trap */ REG_S a2, TASK_TI_A2(tp) /* * Computes: - * a0 = &new_vmalloc[BIT_WORD(cpu)] + * a0 = &new_valid_map_cpus[BIT_WORD(cpu)] * a1 = BIT_MASK(cpu) */ lw a2, TASK_TI_CPU(tp) /* - * Compute the new_vmalloc element position: + * Compute the new_valid_map_cpus element position: * (cpu / 64) * 8 = (cpu >> 6) << 3 */ srli a1, a2, 6 slli a1, a1, 3 - la a0, new_vmalloc + la a0, new_valid_map_cpus add a0, a0, a1 /* - * Compute the bit position in the new_vmalloc element: + * Compute the bit position in the new_valid_map_cpus element: * bit_pos = cpu % 64 = cpu - (cpu / 64) * 64 = cpu - (cpu >> 6) << 6 * = cpu - ((cpu >> 6) << 3) << 3 */ @@ -67,12 +67,12 @@ li a2, 1 sll a1, a2, a1 - /* Check the value of new_vmalloc for this cpu */ + /* Check the value of new_valid_map_cpus for this cpu */ REG_L a2, 0(a0) and a2, a2, a1 - beq a2, zero, .Lnew_vmalloc_restore_context + beq a2, zero, .Lnew_valid_map_cpus_restore_context - /* Atomically reset the current cpu bit in new_vmalloc */ + /* Atomically reset the current cpu bit in new_valid_map_cpus */ amoxor.d a0, a1, (a0) /* Only emit a sfence.vma if the uarch caches invalid entries */ @@ -84,11 +84,11 @@ csrw CSR_SCRATCH, x0 sret -.Lnew_vmalloc_restore_context: +.Lnew_valid_map_cpus_restore_context: REG_L a2, TASK_TI_A2(tp) -.Lnew_vmalloc_restore_context_a1: +.Lnew_valid_map_cpus_restore_context_a1: REG_L a1, TASK_TI_A1(tp) -.Lnew_vmalloc_restore_context_a0: +.Lnew_valid_map_cpus_restore_context_a0: REG_L a0, TASK_TI_A0(tp) .endm @@ -146,7 +146,7 @@ SYM_CODE_START(handle_exception) * could "miss" the new mapping and traps: in that case, we only need * to retry the access, no sfence.vma is required. */ - new_vmalloc_check + new_valid_map_cpus_check #endif REG_S sp, TASK_TI_KERNEL_SP(tp) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index fa8d2f6f554b..bede6845f767 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 26c171fc48539ac88d3697792b1fde9334af836c Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:48 +0800 Subject: [PATCH 3035/5214] riscv: mm: Use the bitmap API for new_valid_map_cpus The bitmap was defined with incorrect size. Fix it by using the proper bitmap API in C code. The corresponding assembly code is still okay and remains unchanged. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-4-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 8 +++----- arch/riscv/mm/init.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8c7a0ef2635a..8cfe59483a8f 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,19 +41,17 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +/* This is accessed in assembly code. cpumask_var_t would be too complex. */ +extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; static inline void mark_new_valid_map(void) { - int i; - /* * We don't care if concurrently a cpu resets this value since * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) - new_valid_map_cpus[i] = -1ULL; + bitmap_fill(new_valid_map_cpus, NR_CPUS); } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index bede6845f767..ea8766c4f3a2 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 1b2c6b56a9fa0dcbef461039937de22b1cbecc7d Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:49 +0800 Subject: [PATCH 3036/5214] riscv: mm: Unconditionally sfence.vma for spurious fault Svvptc does not guarantee that it's safe to just return here. Since we have already cleared our bit, if, theoretically, the bounded timeframe for the accessed page to become valid still hasn't happened after sret, we could fault again and actually crash. Hopefully, these spurious faults should be rare enough that this is an acceptable slowdown. Cc: stable@vger.kernel.org Fixes: 503638e0babf ("riscv: Stop emitting preventive sfence.vma for new vmalloc mappings") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-5-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/entry.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index ebf6918b4e9b..c6988983cdf7 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -75,8 +75,11 @@ /* Atomically reset the current cpu bit in new_valid_map_cpus */ amoxor.d a0, a1, (a0) - /* Only emit a sfence.vma if the uarch caches invalid entries */ - ALTERNATIVE("sfence.vma", "nop", 0, RISCV_ISA_EXT_SVVPTC, 1) + /* + * A sfence.vma is required here. Even if we had Svvptc, there's no + * guarantee that after returning we wouldn't just fault again. + */ + sfence.vma REG_L a0, TASK_TI_A0(tp) REG_L a1, TASK_TI_A1(tp) From 0d9dc20842dbe0a9c66be9bb1638f3e221d87bb0 Mon Sep 17 00:00:00 2001 From: Wu Fei Date: Fri, 5 Jun 2026 07:03:14 +0800 Subject: [PATCH 3037/5214] RISC-V: KVM: Fix skip of valid pages in kvm_riscv_gstage_wp_range The current gstage range walker unconditionally advances by 'page_size' when a leaf PTE is not found, e.g. when the range to wp is [0xfffff01fc000, 0xfffff023c000) and page_size is 2MB, if found_leaf of 0xfffff01fc000 returns false, it skip the whole range, but it's possible to have valid entries in [0xfffff0200000, 0xfffff023c000). Signed-off-by: Wu Fei Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260604230317.30501-2-atwufei@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/gstage.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index b83b85d1c016..30ce40e8809f 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -455,14 +455,14 @@ bool kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end if (ret) break; - if (!found_leaf) - goto next; - - addr = ALIGN_DOWN(addr, page_size); - flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, - ptep_level, GSTAGE_OP_WP); -next: - addr += page_size; + if (!found_leaf) { + addr = ALIGN(addr + 1, page_size); + } else { + addr = ALIGN_DOWN(addr, page_size); + flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, + ptep_level, GSTAGE_OP_WP); + addr += page_size; + } } return flush; From a5f8307e2eaaf5d3acae6554d84fcd2683613cc4 Mon Sep 17 00:00:00 2001 From: Wu Fei Date: Fri, 5 Jun 2026 07:03:15 +0800 Subject: [PATCH 3038/5214] RISC-V: KVM: Fix skip of valid pages in kvm_riscv_gstage_unmap_range Same as kvm_riscv_gstage_wp_range, the possible valid pages should not be skipped if !found_leaf. Different from wp case, which can write-protect more than asked, unmap can't do that, no splitting is added right now but a warning is logged instead. Signed-off-by: Wu Fei Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260604230317.30501-3-atwufei@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/gstage.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index 30ce40e8809f..c4c3b79567f1 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -418,15 +418,19 @@ bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, if (ret) break; - if (!found_leaf) - goto next; + if (!found_leaf) { + addr = ALIGN(addr + 1, page_size); + } else { + if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) + flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, + ptep_level, GSTAGE_OP_CLEAR); + else { + WARN_ONCE(1, "Skip unmap range addr: %#llx, end: %#llx, page_size: %#lx\n", + addr, end, page_size); + } - if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) - flush |= kvm_riscv_gstage_op_pte(gstage, addr, ptep, - ptep_level, GSTAGE_OP_CLEAR); - -next: - addr += page_size; + addr += page_size; + } /* * If the range is too large, release the kvm->mmu_lock From 2abd0dba562551d5c27f97ce560684f534c0cf3a Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Sat, 4 Apr 2026 20:55:20 +0200 Subject: [PATCH 3039/5214] riscv: panic if IRQ handler stacks cannot be allocated init_irq_stacks() and init_irq_scs() may fail when arch_alloc_vmap_stack or scs_alloc return NULL, call panic() in this case. Signed-off-by: Osama Abdelkader Link: https://patch.msgid.link/20260404185522.21767-1-osama.abdelkader@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/irq.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index b6af20bc300f..017d42e778be 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -75,28 +75,34 @@ DECLARE_PER_CPU(ulong *, irq_shadow_call_stack_ptr); DEFINE_PER_CPU(ulong *, irq_shadow_call_stack_ptr); #endif -static void init_irq_scs(void) +static void __init init_irq_scs(void) { int cpu; + void *s; if (!scs_is_enabled()) return; - for_each_possible_cpu(cpu) - per_cpu(irq_shadow_call_stack_ptr, cpu) = - scs_alloc(cpu_to_node(cpu)); + for_each_possible_cpu(cpu) { + s = scs_alloc(cpu_to_node(cpu)); + if (!s) + panic("Failed to allocate IRQ shadow call stack resources\n"); + per_cpu(irq_shadow_call_stack_ptr, cpu) = s; + } } DEFINE_PER_CPU(ulong *, irq_stack_ptr); #ifdef CONFIG_VMAP_STACK -static void init_irq_stacks(void) +static void __init init_irq_stacks(void) { int cpu; ulong *p; for_each_possible_cpu(cpu) { p = arch_alloc_vmap_stack(IRQ_STACK_SIZE, cpu_to_node(cpu)); + if (!p) + panic("Failed to allocate IRQ stack resources\n"); per_cpu(irq_stack_ptr, cpu) = p; } } @@ -104,7 +110,7 @@ static void init_irq_stacks(void) /* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */ DEFINE_PER_CPU_ALIGNED(ulong [IRQ_STACK_SIZE/sizeof(ulong)], irq_stack); -static void init_irq_stacks(void) +static void __init init_irq_stacks(void) { int cpu; @@ -129,8 +135,8 @@ void do_softirq_own_stack(void) #endif /* CONFIG_SOFTIRQ_ON_OWN_STACK */ #else -static void init_irq_scs(void) {} -static void init_irq_stacks(void) {} +static void __init init_irq_scs(void) {} +static void __init init_irq_stacks(void) {} #endif /* CONFIG_IRQ_STACKS */ int arch_show_interrupts(struct seq_file *p, int prec) From e0fd2599f50025d83a0f2ec01c63d204de2c173e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 3 Jun 2026 00:47:22 +0200 Subject: [PATCH 3040/5214] riscv: kexec: use min to simplify riscv_kexec_elf_load Use min() to replace the open-coded version and assign the result directly to kbuf.bufsz. Drop the now-unused local size variable. Signed-off-by: Thorsten Blum Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260602224725.1088385-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kexec_elf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c index 05fd33104f3d..3e9a32acb8f2 100644 --- a/arch/riscv/kernel/kexec_elf.c +++ b/arch/riscv/kernel/kexec_elf.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, @@ -25,7 +26,6 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, { int i; int ret = 0; - size_t size; struct kexec_buf kbuf = {}; const struct elf_phdr *phdr; @@ -36,12 +36,8 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, if (phdr->p_type != PT_LOAD) continue; - size = phdr->p_filesz; - if (size > phdr->p_memsz) - size = phdr->p_memsz; - kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset; - kbuf.bufsz = size; + kbuf.bufsz = min(phdr->p_filesz, phdr->p_memsz); kbuf.buf_align = phdr->p_align; kbuf.mem = phdr->p_paddr - old_pbase + new_pbase; kbuf.memsz = phdr->p_memsz; From 499578e22ae0cc33d05803f22e5c71ffb7077b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:50 +0200 Subject: [PATCH 3041/5214] riscv: vdso: Always declare vdso_start symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the declarations of vdso_start and its related symbols always visible. With that their users don't have to use ifdeffery but can use the better IS_ENABLED() compile-time checks. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-1-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/vdso.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index 35bf830a5576..f7998d9ad9b2 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h @@ -12,12 +12,15 @@ * All systems with an MMU have a VDSO, but systems without an MMU don't * support shared libraries and therefore don't have one. */ -#ifdef CONFIG_MMU #define __VDSO_PAGES 4 #ifndef __ASSEMBLER__ + +#ifdef CONFIG_MMU #include +#endif + #ifdef CONFIG_RISCV_USER_CFI #include #endif @@ -38,15 +41,12 @@ #define COMPAT_VDSO_SYMBOL(base, name) \ (void __user *)((unsigned long)(base) + compat__vdso_##name##_offset) -extern char compat_vdso_start[], compat_vdso_end[]; - #endif /* CONFIG_COMPAT */ extern char vdso_start[], vdso_end[]; extern char vdso_cfi_start[], vdso_cfi_end[]; +extern char compat_vdso_start[], compat_vdso_end[]; #endif /* !__ASSEMBLER__ */ -#endif /* CONFIG_MMU */ - #endif /* _ASM_RISCV_VDSO_H */ From 4fd6505f189d447abbed1f1f6fe6b82649f27a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:51 +0200 Subject: [PATCH 3042/5214] riscv: alternative: Use IS_ENABLED() over ifdeffery for apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IS_ENABLED() allows better compilation coverage while still optimizing away all the dead code. Also it will make some upcoming changes easier. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-2-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 7642704c7f18..59991922a5dc 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,7 +173,6 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -#ifdef CONFIG_MMU static void __init apply_vdso_alternatives(void) { const Elf_Ehdr *hdr; @@ -194,9 +193,6 @@ static void __init apply_vdso_alternatives(void) (struct alt_entry *)end, RISCV_ALTERNATIVES_BOOT); } -#else -static void __init apply_vdso_alternatives(void) { } -#endif void __init apply_boot_alternatives(void) { @@ -207,7 +203,8 @@ void __init apply_boot_alternatives(void) (struct alt_entry *)__alt_end, RISCV_ALTERNATIVES_BOOT); - apply_vdso_alternatives(); + if (IS_ENABLED(CONFIG_MMU)) + apply_vdso_alternatives(); } /* From 6386161abb02880ace2cc965e73f7857b351706c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:52 +0200 Subject: [PATCH 3043/5214] riscv: alternative: Pass vDSO start as parameter to apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO with CFI should also be patched in the same way. To prepare for that move the currently hardcoded vDSO start symbol into a parameter. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-3-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 59991922a5dc..89c283a5cec7 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,14 +173,14 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -static void __init apply_vdso_alternatives(void) +static void __init apply_vdso_alternatives(void *start) { const Elf_Ehdr *hdr; const Elf_Shdr *shdr; const Elf_Shdr *alt; struct alt_entry *begin, *end; - hdr = (Elf_Ehdr *)vdso_start; + hdr = (Elf_Ehdr *)start; shdr = (void *)hdr + hdr->e_shoff; alt = find_section(hdr, shdr, ".alternative"); if (!alt) @@ -204,7 +204,7 @@ void __init apply_boot_alternatives(void) RISCV_ALTERNATIVES_BOOT); if (IS_ENABLED(CONFIG_MMU)) - apply_vdso_alternatives(); + apply_vdso_alternatives(vdso_start); } /* From d3e0634787a234b40a740b9c398fd320a68db81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:53 +0200 Subject: [PATCH 3044/5214] riscv: alternative: Also patch the CFI vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO for CFI-enabled userspace can also contain alternative entries. Patch those, too. Fixes: ccad8c1336b6 ("arch/riscv: add dual vdso creation logic and select vdso based on hw") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-4-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 89c283a5cec7..104dc0862c5c 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -205,6 +205,9 @@ void __init apply_boot_alternatives(void) if (IS_ENABLED(CONFIG_MMU)) apply_vdso_alternatives(vdso_start); + + if (IS_ENABLED(CONFIG_RISCV_USER_CFI)) + apply_vdso_alternatives(vdso_cfi_start); } /* From bce35135fecc7a73c60aaa9d2ec699ead1e32661 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:55:46 +0800 Subject: [PATCH 3045/5214] riscv: also select ARCH_KEEP_MEMBLOCK if kexec is selected On RISC-V, also select ARCH_KEEP_MEMBLOCK if kexec is selected, not only if ACPI is selected. This is because kexec requires the memblock areas to be kept after boot to initialize the secondary kernel. This is needed for both Device Tree and ACPI platforms. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519165546.123105-1-gaohan@iscas.ac.cn [pjw@kernel.org: change to add the dependency on kexec, rather than making it unconditional; rewrite the patch description accordingly] Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ce5f4342e2f2..ca7dfbbefca0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -56,7 +56,7 @@ config RISCV select ARCH_HAS_UBSAN select ARCH_HAS_VDSO_ARCH_DATA if HAVE_GENERIC_VDSO select ARCH_HAVE_NMI_SAFE_CMPXCHG - select ARCH_KEEP_MEMBLOCK if ACPI + select ARCH_KEEP_MEMBLOCK if ACPI || KEXEC select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE if 64BIT && MMU select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT From e8042f6e1d7befb2fb6b10a75918642bcd0acf9a Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 7 Jun 2026 02:56:10 +0900 Subject: [PATCH 3046/5214] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU flush_hyp_vcpu() copies the host vCPU context into the hyp's private vCPU on every run. ctxt_to_vcpu() expects a guest context to have a NULL __hyp_running_vcpu, which is only ever set on the host context, so that it resolves the vCPU via container_of(). While this is generally the case, flush_hyp_vcpu() copies the context verbatim and does not enforce this, so a value provided by the host is dereferenced at EL2 (host -> EL2). Fix by clearing __hyp_running_vcpu after the copy. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-2-imv4bel@gmail.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 06db299c37a8..02c5d6e5abcb 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -128,6 +128,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; + /* __hyp_running_vcpu must be NULL in a guest context. */ + hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL; + hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & From 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 7 Jun 2026 02:56:11 +0900 Subject: [PATCH 3047/5214] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU on every run. The vGIC list register save and restore use used_lrs as their loop bound and expect it to stay within the number of implemented list registers. While this is generally the case, flush_hyp_vcpu() copies vgic_v3 verbatim and does not enforce this, so a value provided by the host is used at EL2 to index vgic_lr[] and access ICH_LR_EL2 (host -> EL2). Fix by clamping used_lrs to the number of implemented list registers after the copy, as the trusted path already does in vgic_flush_lr_state(). The number of implemented list registers is constant after init, so it is replicated once from kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on every entry. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-3-imv4bel@gmail.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kvm/arm.c | 2 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 8d06b62e7188..e9b2b0c40ec6 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); extern unsigned long kvm_nvhe_sym(hyp_nr_cpus); +extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 8bb2c7422cc8..74965b358cdf 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -2423,6 +2423,8 @@ static int __init init_subsystems(void) switch (err) { case 0: vgic_present = true; + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr; break; case -ENODEV: case -ENXIO: diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 02c5d6e5abcb..a0da08caa6c2 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -24,6 +24,9 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); +/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */ +unsigned int hyp_gicv3_nr_lr; + void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) @@ -142,6 +145,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; + /* Bound used_lrs by the number of implemented list registers. */ + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs = + min_t(unsigned int, + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, + hyp_gicv3_nr_lr); + hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid; } From 832dfa237f836549b202d3eebc0bc29b8a719608 Mon Sep 17 00:00:00 2001 From: "tabba@google.com" Date: Sun, 31 May 2026 16:45:48 +0100 Subject: [PATCH 3048/5214] KVM: arm64: Flush HCR_EL2.VSE to deliver SErrors to pKVM guests With pKVM enabled, the host injects a virtual SError by setting HCR_EL2.VSE on its vCPU copy, but flush_hyp_vcpu() only flows TWI/TWE into the hyp vCPU that runs, so VSE never reaches it and a deferred (masked) SError is never delivered. VSE is a host-owned injection control, not a trap-configuration bit, so restricting the host's trap-register values should not have dropped it. Flow it on entry; sync_hyp_vcpu() already copies hcr_el2 back, so delivery is reflected to the host. THis makes it consistent with the existing forwarding of VSESR_EL2, which qualifies the Serror. Fixes: b56680de9c648 ("KVM: arm64: Initialize trap register values in hyp in pKVM") Reported-by: Sashiko (local):gemini-3.1-pro Signed-off-by: Fuad Tabba Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260531154548.1505799-1-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index a0da08caa6c2..1d01c6e547f5 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -135,9 +135,14 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL; hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; - hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); + /* + * HCR_EL2.VSE is host-owned (a pending virtual SError to inject), not a + * trap-control bit, so it must flow to the hyp vCPU alongside TWI/TWE + * for the vSError to be delivered. sync_hyp_vcpu() reflects it back. + */ + hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE | HCR_VSE); hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & - (HCR_TWI | HCR_TWE); + (HCR_TWI | HCR_TWE | HCR_VSE); hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags; From 63336d57a26904f58e4ff2cf584ef9958564a7c6 Mon Sep 17 00:00:00 2001 From: "tabba@google.com" Date: Fri, 29 May 2026 13:17:53 +0100 Subject: [PATCH 3049/5214] KVM: arm64: Free hyp-share tracking node when share hypercall fails share_pfn_hyp() inserts a tracking node into hyp_shared_pfns and then invokes __pkvm_host_share_hyp. If the hypercall rejects the share (page-state mismatch at EL2), the node stays in the tree with refcount 1: a phantom share that leaks the allocation and that a later unshare will trust. Erase the node and free it on hypercall failure. Fixes: a83e2191b7f1 ("KVM: arm64: pkvm: Refcount the pages shared with EL2") Reported-by: Sashiko (local):gemini-3.1-pro Suggested-by: Vincent Donnefort Signed-off-by: Fuad Tabba Reviewed-by: Vincent Donnefort Link: https://patch.msgid.link/20260529121755.2923500-2-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/mmu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index d089c107d9b7..0abf3a2d587b 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -501,6 +501,10 @@ static int share_pfn_hyp(u64 pfn) rb_link_node(&this->node, parent, node); rb_insert_color(&this->node, &hyp_shared_pfns); ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn); + if (ret) { + rb_erase(&this->node, &hyp_shared_pfns); + kfree(this); + } unlock: mutex_unlock(&hyp_shared_pfns_lock); From bd2618780ab4584a33ab1049338294a50690d149 Mon Sep 17 00:00:00 2001 From: "tabba@google.com" Date: Fri, 29 May 2026 13:17:54 +0100 Subject: [PATCH 3050/5214] KVM: arm64: Avoid host/hyp share desync on unshare hypercall failure unshare_pfn_hyp() erases the tracking node from hyp_shared_pfns and frees it before invoking __pkvm_host_unshare_hyp. If the hypercall fails (e.g. EL2 refcount still held, or page-state mismatch), the host loses its record while EL2 still holds the share, breaking later share/unshare attempts on the same pfn. Invoke the hypercall first; erase and free only on success. Document at the kvm_unshare_hyp() call site that the WARN_ON() is left non-fatal: a failed unshare leaks the page (it stays shared with the hypervisor) but breaks no isolation guarantee. Fixes: 52b28657ebd7 ("KVM: arm64: pkvm: Unshare guest structs during teardown") Reported-by: Sashiko (local):gemini-3.1-pro Suggested-by: Vincent Donnefort Signed-off-by: Fuad Tabba Reviewed-by: Vincent Donnefort Link: https://patch.msgid.link/20260529121755.2923500-3-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/mmu.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 0abf3a2d587b..c82d4ececab8 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -524,13 +524,17 @@ static int unshare_pfn_hyp(u64 pfn) goto unlock; } - this->count--; - if (this->count) + if (this->count > 1) { + this->count--; + goto unlock; + } + + ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn); + if (ret) goto unlock; rb_erase(&this->node, &hyp_shared_pfns); kfree(this); - ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn); unlock: mutex_unlock(&hyp_shared_pfns_lock); @@ -581,6 +585,11 @@ void kvm_unshare_hyp(void *from, void *to) end = PAGE_ALIGN(__pa(to)); for (cur = start; cur < end; cur += PAGE_SIZE) { pfn = __phys_to_pfn(cur); + /* + * A failed unshare leaks the page: it stays shared with the + * hypervisor and is no longer reusable for pKVM. No isolation + * guarantee is broken, and this is not expected in practice. + */ WARN_ON(unshare_pfn_hyp(pfn)); } } From f4411f9308c0187c211577b7c489545b0bdae455 Mon Sep 17 00:00:00 2001 From: "tabba@google.com" Date: Fri, 29 May 2026 13:17:55 +0100 Subject: [PATCH 3051/5214] KVM: arm64: Roll back partial shares on kvm_share_hyp() failure kvm_share_hyp() shares a range one page at a time. If share_pfn_hyp() fails partway through, the pages already shared by this call are left shared, while the caller treats the whole range as failed and never unshares them. Unshare those pages before returning the error. If an unshare itself fails the page is leaked: it stays shared with the hypervisor and is no longer reusable for pKVM, but no isolation guarantee is broken, so WARN and continue. Not expected in practice. Fixes: a83e2191b7f1 ("KVM: arm64: pkvm: Refcount the pages shared with EL2") Suggested-by: Vincent Donnefort Signed-off-by: Fuad Tabba Reviewed-by: Vincent Donnefort Link: https://patch.msgid.link/20260529121755.2923500-4-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/mmu.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index c82d4ececab8..f18b287c98b6 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -544,8 +544,8 @@ static int unshare_pfn_hyp(u64 pfn) int kvm_share_hyp(void *from, void *to) { phys_addr_t start, end, cur; + int ret = 0; u64 pfn; - int ret; if (is_kernel_in_hyp_mode()) return 0; @@ -567,10 +567,24 @@ int kvm_share_hyp(void *from, void *to) pfn = __phys_to_pfn(cur); ret = share_pfn_hyp(pfn); if (ret) - return ret; + break; } - return 0; + if (!ret) + return 0; + + /* + * Roll back the pages shared by this call. A failed unshare leaks + * the page (it stays shared with the hypervisor and is no longer + * reusable for pKVM) but breaks no isolation guarantee, so warn and + * continue. Not expected in practice. + */ + for (end = cur, cur = start; cur < end; cur += PAGE_SIZE) { + pfn = __phys_to_pfn(cur); + WARN_ON(unshare_pfn_hyp(pfn)); + } + + return ret; } void kvm_unshare_hyp(void *from, void *to) From 3190bd7d36d71ab595409fd116e80928919b5bd4 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Wed, 3 Jun 2026 12:03:12 +0100 Subject: [PATCH 3052/5214] KVM: arm64: Set a Linux errno on SMCCC error in kvm_call_hyp_nvhe() If kvm_call_hyp_nvhe() fails with an SMCCC error code, we WARN(). However, the returned value isn't initialized and the caller might get garbage or 0 which is likely to be interpreted as success. Set a default -EOPNOTSUPP error value, ensuring all callers get the message when hypercalls fail. Signed-off-by: Vincent Donnefort Acked-by: Will Deacon Reviewed-by: Fuad Tabba Link: https://patch.msgid.link/20260603110312.2909844-1-vdonnefort@google.com [maz: changed error value to -EOPNOTSUPP as suggested by Will, tidied up change log] Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 65eead8362e0..9221d7dc331e 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1273,13 +1273,14 @@ void kvm_arm_resume_guest(struct kvm *kvm); #define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid)) #ifndef __KVM_NVHE_HYPERVISOR__ -#define kvm_call_hyp_nvhe(f, ...) \ +#define kvm_call_hyp_nvhe(f, ...) \ ({ \ struct arm_smccc_res res; \ \ arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \ ##__VA_ARGS__, &res); \ - WARN_ON(res.a0 != SMCCC_RET_SUCCESS); \ + if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \ + res.a1 = -EOPNOTSUPP; \ \ res.a1; \ }) From 6bef47288ce1cb8302c84753164b8f8f6d63e0b3 Mon Sep 17 00:00:00 2001 From: Wei-Lin Chang Date: Fri, 5 Jun 2026 19:52:55 +0100 Subject: [PATCH 3053/5214] KVM: arm64: Fix block mapping validity check in stage-1 walker For the 64K granule size, FEAT_LPA determines whether a level 1 mapping is allowed. Using the result of has_52bit_pa() is too restrictive, as it also checks the selected output addressi size in TCR.(I)PS. Fix it by only checking FEAT_LPA. Fixes: 5da3a3b27a01 ("KVM: arm64: Expand valid block mappings to FEAT_LPA/LPA2 support") Signed-off-by: Wei-Lin Chang Link: https://patch.msgid.link/20260605185255.2431996-1-weilin.chang@arm.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/at.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 4d4285e60fce..7663df5e03b7 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -495,15 +495,18 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, /* Block mapping, check the validity of the level */ if (!(desc & BIT(1))) { bool valid_block = false; + bool lpa = kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, PARANGE, 52); switch (BIT(wi->pgshift)) { case SZ_4K: valid_block = level == 1 || level == 2 || (wi->pa52bit && level == 0); break; case SZ_16K: - case SZ_64K: valid_block = level == 2 || (wi->pa52bit && level == 1); break; + case SZ_64K: + valid_block = level == 2 || (lpa && level == 1); + break; } if (!valid_block) From c14efec1452098ec97d30423e4120e4fa90e0663 Mon Sep 17 00:00:00 2001 From: Mukesh R Date: Wed, 3 Jun 2026 15:50:10 -0700 Subject: [PATCH 3054/5214] iommu/hyperv: Create hyperv subdirectory under drivers/iommu Create hyperv subdirectory under drivers/iommu in anticipation of more Hyper-V related files from upcoming PCI passthrough and PV-IOMMU patches. Also, the current file hyperv-iommu.c actually implements irq remapping on x86, so rename to more appropriate hv-irq-remap-x86.c and move it under the new hyperv subdirectory. Since this file implements irq_remap_ops exposed by drivers/iommu/irq_remapping.h, it cannot be relocated to the irq directory. This is in sync with other backend directories like amd and intel there. Lastly, this file should not be tied to CONFIG_HYPERV_IOMMU, but to CONFIG_HYPERV and CONFIG_IRQ_REMAP. Signed-off-by: Mukesh R Reviewed-by: Jacob Pan Signed-off-by: Wei Liu --- MAINTAINERS | 2 +- drivers/iommu/Kconfig | 9 --------- drivers/iommu/Makefile | 2 +- drivers/iommu/hyperv/Makefile | 2 ++ .../iommu/{hyperv-iommu.c => hyperv/hv-irq-remap-x86.c} | 6 +----- drivers/iommu/irq_remapping.c | 2 +- 6 files changed, 6 insertions(+), 17 deletions(-) create mode 100644 drivers/iommu/hyperv/Makefile rename drivers/iommu/{hyperv-iommu.c => hyperv/hv-irq-remap-x86.c} (99%) diff --git a/MAINTAINERS b/MAINTAINERS index b539be153f6a..93a7105e9cef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11988,7 +11988,7 @@ F: drivers/clocksource/hyperv_timer.c F: drivers/hid/hid-hyperv.c F: drivers/hv/ F: drivers/input/serio/hyperv-keyboard.c -F: drivers/iommu/hyperv-iommu.c +F: drivers/iommu/hyperv/ F: drivers/net/ethernet/microsoft/ F: drivers/net/hyperv/ F: drivers/pci/controller/pci-hyperv-intf.c diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index f86262b11416..1becc0f20222 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -351,15 +351,6 @@ config MTK_IOMMU_V1 if unsure, say N here. -config HYPERV_IOMMU - bool "Hyper-V IRQ Handling" - depends on HYPERV && X86 - select IOMMU_API - default HYPERV - help - Stub IOMMU driver to handle IRQs to support Hyper-V Linux - guest and root partitions. - config VIRTIO_IOMMU tristate "Virtio IOMMU driver" depends on VIRTIO diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 0275821f4ef9..d9683422aecb 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd/ obj-$(CONFIG_INTEL_IOMMU) += intel/ obj-$(CONFIG_RISCV_IOMMU) += riscv/ obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/ +obj-$(CONFIG_HYPERV) += hyperv/ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o @@ -30,7 +31,6 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o -obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile new file mode 100644 index 000000000000..6ef0ef97f3dd --- /dev/null +++ b/drivers/iommu/hyperv/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_IRQ_REMAP) += hv-irq-remap-x86.o diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv/hv-irq-remap-x86.c similarity index 99% rename from drivers/iommu/hyperv-iommu.c rename to drivers/iommu/hyperv/hv-irq-remap-x86.c index 479103261ae6..4963c9ce05dc 100644 --- a/drivers/iommu/hyperv-iommu.c +++ b/drivers/iommu/hyperv/hv-irq-remap-x86.c @@ -22,9 +22,7 @@ #include #include -#include "irq_remapping.h" - -#ifdef CONFIG_IRQ_REMAP +#include "../irq_remapping.h" /* * According 82093AA IO-APIC spec , IO APIC has a 24-entry Interrupt @@ -330,5 +328,3 @@ static const struct irq_domain_ops hyperv_root_ir_domain_ops = { .alloc = hyperv_root_irq_remapping_alloc, .free = hyperv_root_irq_remapping_free, }; - -#endif diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index c2443659812a..41bf65e4ea88 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -108,7 +108,7 @@ int __init irq_remapping_prepare(void) else if (IS_ENABLED(CONFIG_AMD_IOMMU) && amd_iommu_irq_ops.prepare() == 0) remap_ops = &amd_iommu_irq_ops; - else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) && + else if (IS_ENABLED(CONFIG_HYPERV) && hyperv_irq_remap_ops.prepare() == 0) remap_ops = &hyperv_irq_remap_ops; else From 982cb55bec4a8aa73569dba4739a3de1ee2c25d9 Mon Sep 17 00:00:00 2001 From: Mukesh R Date: Mon, 1 Jun 2026 15:51:16 -0700 Subject: [PATCH 3055/5214] x86/hyperv: Cosmetic changes in irqdomain.c for readability Make cosmetic changes: o Rename struct pci_dev *dev to *pdev since there are cases of struct device *dev in the file and all over the kernel o Rename hv_build_pci_dev_id to hv_build_devid_type_pci in anticipation of building different types of device ids o Fix checkpatch.pl issues with return and extraneous printk o Replace spaces with tabs o Rename struct hv_devid *xxx to struct hv_devid *hv_devid given code paths involve many types of device ids o Fix indentation in a large if block by using goto. There are no functional changes. Reviewed-by: Souradeep Chakrabarti Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Mukesh R Signed-off-by: Wei Liu --- arch/x86/hyperv/irqdomain.c | 198 +++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 94 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index 365e364268d9..b3ad50a874dc 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 - /* * Irqdomain for Linux to run as the root partition on Microsoft Hypervisor. * @@ -14,8 +13,8 @@ #include #include -static int hv_map_interrupt(union hv_device_id device_id, bool level, - int cpu, int vector, struct hv_interrupt_entry *entry) +static int hv_map_interrupt(union hv_device_id hv_devid, bool level, + int cpu, int vector, struct hv_interrupt_entry *ret_entry) { struct hv_input_map_device_interrupt *input; struct hv_output_map_device_interrupt *output; @@ -32,7 +31,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, intr_desc = &input->interrupt_descriptor; memset(input, 0, sizeof(*input)); input->partition_id = hv_current_partition_id; - input->device_id = device_id.as_uint64; + input->device_id = hv_devid.as_uint64; intr_desc->interrupt_type = HV_X64_INTERRUPT_TYPE_FIXED; intr_desc->vector_count = 1; intr_desc->target.vector = vector; @@ -44,7 +43,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, intr_desc->target.vp_set.valid_bank_mask = 0; intr_desc->target.vp_set.format = HV_GENERIC_SET_SPARSE_4K; - nr_bank = cpumask_to_vpset(&(intr_desc->target.vp_set), cpumask_of(cpu)); + nr_bank = cpumask_to_vpset(&intr_desc->target.vp_set, cpumask_of(cpu)); if (nr_bank < 0) { local_irq_restore(flags); pr_err("%s: unable to generate VP set\n", __func__); @@ -61,7 +60,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, status = hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT, 0, var_size, input, output); - *entry = output->interrupt_entry; + *ret_entry = output->interrupt_entry; local_irq_restore(flags); @@ -71,21 +70,19 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level, return hv_result_to_errno(status); } -static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry) +static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *irq_entry) { unsigned long flags; struct hv_input_unmap_device_interrupt *input; - struct hv_interrupt_entry *intr_entry; u64 status; local_irq_save(flags); input = *this_cpu_ptr(hyperv_pcpu_input_arg); memset(input, 0, sizeof(*input)); - intr_entry = &input->interrupt_entry; input->partition_id = hv_current_partition_id; input->device_id = id; - *intr_entry = *old_entry; + input->interrupt_entry = *irq_entry; status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL); local_irq_restore(flags); @@ -115,67 +112,71 @@ static int get_rid_cb(struct pci_dev *pdev, u16 alias, void *data) return 0; } -static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev) +static union hv_device_id hv_build_devid_type_pci(struct pci_dev *pdev) { - union hv_device_id dev_id; + int pos; + union hv_device_id hv_devid; struct rid_data data = { .bridge = NULL, - .rid = PCI_DEVID(dev->bus->number, dev->devfn) + .rid = PCI_DEVID(pdev->bus->number, pdev->devfn) }; - pci_for_each_dma_alias(dev, get_rid_cb, &data); + pci_for_each_dma_alias(pdev, get_rid_cb, &data); - dev_id.as_uint64 = 0; - dev_id.device_type = HV_DEVICE_TYPE_PCI; - dev_id.pci.segment = pci_domain_nr(dev->bus); + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_PCI; + hv_devid.pci.segment = pci_domain_nr(pdev->bus); - dev_id.pci.bdf.bus = PCI_BUS_NUM(data.rid); - dev_id.pci.bdf.device = PCI_SLOT(data.rid); - dev_id.pci.bdf.function = PCI_FUNC(data.rid); - dev_id.pci.source_shadow = HV_SOURCE_SHADOW_NONE; + hv_devid.pci.bdf.bus = PCI_BUS_NUM(data.rid); + hv_devid.pci.bdf.device = PCI_SLOT(data.rid); + hv_devid.pci.bdf.function = PCI_FUNC(data.rid); + hv_devid.pci.source_shadow = HV_SOURCE_SHADOW_NONE; - if (data.bridge) { - int pos; + if (data.bridge == NULL) + goto out; - /* - * Microsoft Hypervisor requires a bus range when the bridge is - * running in PCI-X mode. - * - * To distinguish conventional vs PCI-X bridge, we can check - * the bridge's PCI-X Secondary Status Register, Secondary Bus - * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge - * Specification Revision 1.0 5.2.2.1.3. - * - * Value zero means it is in conventional mode, otherwise it is - * in PCI-X mode. - */ + /* + * Microsoft Hypervisor requires a bus range when the bridge is + * running in PCI-X mode. + * + * To distinguish conventional vs PCI-X bridge, we can check + * the bridge's PCI-X Secondary Status Register, Secondary Bus + * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge + * Specification Revision 1.0 5.2.2.1.3. + * + * Value zero means it is in conventional mode, otherwise it is + * in PCI-X mode. + */ - pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX); - if (pos) { - u16 status; + pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX); + if (pos) { + u16 status; - pci_read_config_word(data.bridge, pos + - PCI_X_BRIDGE_SSTATUS, &status); + pci_read_config_word(data.bridge, pos + PCI_X_BRIDGE_SSTATUS, + &status); - if (status & PCI_X_SSTATUS_FREQ) { - /* Non-zero, PCI-X mode */ - u8 sec_bus, sub_bus; + if (status & PCI_X_SSTATUS_FREQ) { + /* Non-zero, PCI-X mode */ + u8 sec_bus, sub_bus; - dev_id.pci.source_shadow = HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE; + hv_devid.pci.source_shadow = + HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE; - pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, &sec_bus); - dev_id.pci.shadow_bus_range.secondary_bus = sec_bus; - pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, &sub_bus); - dev_id.pci.shadow_bus_range.subordinate_bus = sub_bus; - } + pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, + &sec_bus); + hv_devid.pci.shadow_bus_range.secondary_bus = sec_bus; + pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, + &sub_bus); + hv_devid.pci.shadow_bus_range.subordinate_bus = sub_bus; } } - return dev_id; +out: + return hv_devid; } -/** - * hv_map_msi_interrupt() - "Map" the MSI IRQ in the hypervisor. +/* + * hv_map_msi_interrupt() - Map the MSI IRQ in the hypervisor. * @data: Describes the IRQ * @out_entry: Hypervisor (MSI) interrupt entry (can be NULL) * @@ -188,22 +189,23 @@ int hv_map_msi_interrupt(struct irq_data *data, { struct irq_cfg *cfg = irqd_cfg(data); struct hv_interrupt_entry dummy; - union hv_device_id device_id; + union hv_device_id hv_devid; struct msi_desc *msidesc; - struct pci_dev *dev; + struct pci_dev *pdev; int cpu; msidesc = irq_data_get_msi_desc(data); - dev = msi_desc_to_pci_dev(msidesc); - device_id = hv_build_pci_dev_id(dev); + pdev = msi_desc_to_pci_dev(msidesc); + hv_devid = hv_build_devid_type_pci(pdev); cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); - return hv_map_interrupt(device_id, false, cpu, cfg->vector, + return hv_map_interrupt(hv_devid, false, cpu, cfg->vector, out_entry ? out_entry : &dummy); } EXPORT_SYMBOL_GPL(hv_map_msi_interrupt); -static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg) +static void entry_to_msi_msg(struct hv_interrupt_entry *entry, + struct msi_msg *msg) { /* High address is always 0 */ msg->address_hi = 0; @@ -211,17 +213,19 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi msg->data = entry->msi_entry.data.as_uint32; } -static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry); +static int hv_unmap_msi_interrupt(struct pci_dev *pdev, + struct hv_interrupt_entry *irq_entry); + static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { struct hv_interrupt_entry *stored_entry; struct irq_cfg *cfg = irqd_cfg(data); struct msi_desc *msidesc; - struct pci_dev *dev; + struct pci_dev *pdev; int ret; msidesc = irq_data_get_msi_desc(data); - dev = msi_desc_to_pci_dev(msidesc); + pdev = msi_desc_to_pci_dev(msidesc); if (!cfg) { pr_debug("%s: cfg is NULL", __func__); @@ -240,7 +244,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) stored_entry = data->chip_data; data->chip_data = NULL; - ret = hv_unmap_msi_interrupt(dev, stored_entry); + ret = hv_unmap_msi_interrupt(pdev, stored_entry); kfree(stored_entry); @@ -249,10 +253,8 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) } stored_entry = kzalloc_obj(*stored_entry, GFP_ATOMIC); - if (!stored_entry) { - pr_debug("%s: failed to allocate chip data\n", __func__); + if (!stored_entry) return; - } ret = hv_map_msi_interrupt(data, stored_entry); if (ret) { @@ -262,18 +264,21 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) data->chip_data = stored_entry; entry_to_msi_msg(data->chip_data, msg); - - return; } -static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry) +static int hv_unmap_msi_interrupt(struct pci_dev *pdev, + struct hv_interrupt_entry *irq_entry) { - return hv_unmap_interrupt(hv_build_pci_dev_id(dev).as_uint64, old_entry); + union hv_device_id hv_devid; + + hv_devid = hv_build_devid_type_pci(pdev); + return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry); } -static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) +/* NB: during map, hv_interrupt_entry is saved via data->chip_data */ +static void hv_teardown_msi_irq(struct pci_dev *pdev, struct irq_data *irqd) { - struct hv_interrupt_entry old_entry; + struct hv_interrupt_entry irq_entry; struct msi_msg msg; if (!irqd->chip_data) { @@ -281,13 +286,13 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd) return; } - old_entry = *(struct hv_interrupt_entry *)irqd->chip_data; - entry_to_msi_msg(&old_entry, &msg); + irq_entry = *(struct hv_interrupt_entry *)irqd->chip_data; + entry_to_msi_msg(&irq_entry, &msg); kfree(irqd->chip_data); irqd->chip_data = NULL; - (void)hv_unmap_msi_interrupt(dev, &old_entry); + (void)hv_unmap_msi_interrupt(pdev, &irq_entry); } /* @@ -302,7 +307,8 @@ static struct irq_chip hv_pci_msi_controller = { }; static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain, - struct irq_domain *real_parent, struct msi_domain_info *info) + struct irq_domain *real_parent, + struct msi_domain_info *info) { struct irq_chip *chip = info->chip; @@ -317,7 +323,8 @@ static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain, } #define HV_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX) -#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS) +#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) static struct msi_parent_ops hv_msi_parent_ops = { .supported_flags = HV_MSI_FLAGS_SUPPORTED, @@ -329,14 +336,14 @@ static struct msi_parent_ops hv_msi_parent_ops = { .init_dev_msi_info = hv_init_dev_msi_info, }; -static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, - void *arg) +/* Allocate nr_irqs IRQs for the given irq domain */ +static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) { /* - * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e. everything except - * entry_to_msi_msg() should be in here. + * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e. + * everything except entry_to_msi_msg() should be in here. */ - int ret; ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, arg); @@ -344,13 +351,15 @@ static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned return ret; for (int i = 0; i < nr_irqs; ++i) { - irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller, NULL, - handle_edge_irq, NULL, "edge"); + irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller, + NULL, handle_edge_irq, NULL, "edge"); } + return 0; } -static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs) +static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs) { for (int i = 0; i < nr_irqs; ++i) { struct irq_data *irqd = irq_domain_get_irq_data(d, virq); @@ -362,6 +371,7 @@ static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned hv_teardown_msi_irq(to_pci_dev(desc->dev), irqd); } + irq_domain_free_irqs_top(d, virq, nr_irqs); } @@ -394,25 +404,25 @@ struct irq_domain * __init hv_create_pci_msi_domain(void) int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry) { - union hv_device_id device_id; + union hv_device_id hv_devid; - device_id.as_uint64 = 0; - device_id.device_type = HV_DEVICE_TYPE_IOAPIC; - device_id.ioapic.ioapic_id = (u8)ioapic_id; + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC; + hv_devid.ioapic.ioapic_id = (u8)ioapic_id; - return hv_unmap_interrupt(device_id.as_uint64, entry); + return hv_unmap_interrupt(hv_devid.as_uint64, entry); } EXPORT_SYMBOL_GPL(hv_unmap_ioapic_interrupt); int hv_map_ioapic_interrupt(int ioapic_id, bool level, int cpu, int vector, struct hv_interrupt_entry *entry) { - union hv_device_id device_id; + union hv_device_id hv_devid; - device_id.as_uint64 = 0; - device_id.device_type = HV_DEVICE_TYPE_IOAPIC; - device_id.ioapic.ioapic_id = (u8)ioapic_id; + hv_devid.as_uint64 = 0; + hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC; + hv_devid.ioapic.ioapic_id = (u8)ioapic_id; - return hv_map_interrupt(device_id, level, cpu, vector, entry); + return hv_map_interrupt(hv_devid, level, cpu, vector, entry); } EXPORT_SYMBOL_GPL(hv_map_ioapic_interrupt); From 145613eb15eaed537825363a59523a0dcfa2d0f3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 5 Jun 2026 14:44:54 +0200 Subject: [PATCH 3056/5214] hv_balloon: Simplify data output in hv_balloon_debug_show() Move the specification for a line break from a seq_puts() call to a seq_printf() call. The source code was transformed by using the Coccinelle software. Signed-off-by: Markus Elfring Reviewed-by: Sahil Chandna Signed-off-by: Wei Liu --- drivers/hv/hv_balloon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 9a55f5c43307..42ce27be344d 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1862,9 +1862,7 @@ static int hv_balloon_debug_show(struct seq_file *f, void *offset) if (hot_add_enabled()) seq_puts(f, " hot_add"); - seq_puts(f, "\n"); - - seq_printf(f, "%-22s: %u", "state", dm->state); + seq_printf(f, "\n%-22s: %u", "state", dm->state); switch (dm->state) { case DM_INITIALIZING: sname = "Initializing"; From a4ffc59238be84dd1c26bf1c001543e832674fc6 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 16 Apr 2026 22:18:05 +0800 Subject: [PATCH 3057/5214] mshv: add bounds check on vp_index in mshv_intercept_isr() mshv_intercept_isr() extracts vp_index from the hypervisor message payload and uses it directly to index into pt_vp_array without validation. handle_bitset_message() and handle_pair_message() already validate vp_index against MSHV_MAX_VPS before array access. Add the same MSHV_MAX_VPS bounds check for consistency with the other message handlers. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Wei Liu --- drivers/hv/mshv_synic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..fe591d159766 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -383,6 +383,11 @@ mshv_intercept_isr(struct hv_message *msg) */ vp_index = ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index; + /* This shouldn't happen, but just in case. */ + if (unlikely(vp_index >= MSHV_MAX_VPS)) { + pr_debug("VP index %u out of bounds\n", vp_index); + goto unlock_out; + } vp = partition->pt_vp_array[vp_index]; if (unlikely(!vp)) { pr_debug("failed to find VP %u\n", vp_index); From 27aa791db7e7fe9e405a2143f2ddccdcd0d1c283 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 5 Jun 2026 09:12:31 +0200 Subject: [PATCH 3058/5214] pinctrl: Move Airoha driver to dedicated directory In preparation for additional SoC support, move the Airoha pinctrl driver for AN7581 SoC to a dedicated directory. This is to tidy things up and keep code organized without polluting the Mediatek driver directory. The driver doesn't depend on any generic or common code from the Mediatek codebase so it can be safely moved without any modification. Signed-off-by: Christian Marangi Acked-by: Lorenzo Bianconi Signed-off-by: Linus Walleij --- MAINTAINERS | 2 +- drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/airoha/Kconfig | 20 +++++++++++++++++++ drivers/pinctrl/airoha/Makefile | 3 +++ .../{mediatek => airoha}/pinctrl-airoha.c | 0 drivers/pinctrl/mediatek/Kconfig | 17 +--------------- drivers/pinctrl/mediatek/Makefile | 1 - 8 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 drivers/pinctrl/airoha/Kconfig create mode 100644 drivers/pinctrl/airoha/Makefile rename drivers/pinctrl/{mediatek => airoha}/pinctrl-airoha.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 47e8f12480b0..12aae45a302c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20969,7 +20969,7 @@ M: Lorenzo Bianconi L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml -F: drivers/pinctrl/mediatek/pinctrl-airoha.c +F: drivers/pinctrl/airoha/pinctrl-airoha.c PIN CONTROLLER - AMD M: Basavaraj Natikar diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f4ffe1f3b720..de1554809eec 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -689,6 +689,7 @@ config PINCTRL_RP1 multi function device. source "drivers/pinctrl/actions/Kconfig" +source "drivers/pinctrl/airoha/Kconfig" source "drivers/pinctrl/aspeed/Kconfig" source "drivers/pinctrl/bcm/Kconfig" source "drivers/pinctrl/berlin/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 1438a246b93f..5536e05e5edc 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o obj-y += actions/ +obj-y += airoha/ obj-$(CONFIG_PINCTRL_ASPEED) += aspeed/ obj-y += bcm/ obj-$(CONFIG_PINCTRL_BERLIN) += berlin/ diff --git a/drivers/pinctrl/airoha/Kconfig b/drivers/pinctrl/airoha/Kconfig new file mode 100644 index 000000000000..03adaeae8fc3 --- /dev/null +++ b/drivers/pinctrl/airoha/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "Airoha pinctrl drivers" + depends on ARCH_AIROHA || COMPILE_TEST + +config PINCTRL_AIROHA + tristate "Airoha EN7581 pin control" + depends on OF + depends on ARM64 || COMPILE_TEST + select PINMUX + select GENERIC_PINCONF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GPIOLIB + select GPIOLIB_IRQCHIP + select REGMAP_MMIO + help + Say yes here to support pin controller and gpio driver + on Airoha EN7581 SoC. + +endmenu diff --git a/drivers/pinctrl/airoha/Makefile b/drivers/pinctrl/airoha/Makefile new file mode 100644 index 000000000000..a25b744dd7a8 --- /dev/null +++ b/drivers/pinctrl/airoha/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PINCTRL_AIROHA) += pinctrl-airoha.o diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c similarity index 100% rename from drivers/pinctrl/mediatek/pinctrl-airoha.c rename to drivers/pinctrl/airoha/pinctrl-airoha.c diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig index 4819617d9368..97980cc28b9c 100644 --- a/drivers/pinctrl/mediatek/Kconfig +++ b/drivers/pinctrl/mediatek/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "MediaTek pinctrl drivers" - depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST + depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST config EINT_MTK tristate "MediaTek External Interrupt Support" @@ -126,21 +126,6 @@ config PINCTRL_MT8127 select PINCTRL_MTK # For ARMv8 SoCs -config PINCTRL_AIROHA - tristate "Airoha EN7581 pin control" - depends on OF - depends on ARM64 || COMPILE_TEST - select PINMUX - select GENERIC_PINCONF - select GENERIC_PINCTRL_GROUPS - select GENERIC_PINMUX_FUNCTIONS - select GPIOLIB - select GPIOLIB_IRQCHIP - select REGMAP_MMIO - help - Say yes here to support pin controller and gpio driver - on Airoha EN7581 SoC. - config PINCTRL_MT2712 bool "MediaTek MT2712 pin control" depends on OF diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile index ae765bd99965..6dc17b0c23f9 100644 --- a/drivers/pinctrl/mediatek/Makefile +++ b/drivers/pinctrl/mediatek/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE) += pinctrl-moore.o obj-$(CONFIG_PINCTRL_MTK_PARIS) += pinctrl-paris.o # SoC Drivers -obj-$(CONFIG_PINCTRL_AIROHA) += pinctrl-airoha.o obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o obj-$(CONFIG_PINCTRL_MT76X8) += pinctrl-mt76x8.o From be3ee6e6710ee766448b55a6041e73a393cfff79 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Fri, 5 Jun 2026 14:38:09 +0800 Subject: [PATCH 3059/5214] pinctrl: aspeed: Fix GPIO mux value for ADC-capable balls aspeed_g7_soc1_gpio_request_enable() unconditionally writes mux function 0 to route the requested pin to GPIO. This is wrong for the ADC-capable balls W17 through AB19 (ADC0-ADC15), where function 0 selects the ADC input and function 1 selects GPIO. Requesting one of those GPIOs therefore muxed the ball to ADC instead. Write mux value 1 for balls W17 through AB19 so the GPIO function is actually selected. Fixes: 4af4eb66aac3 ("pinctrl: aspeed: Add AST2700 SoC1 support") Signed-off-by: Billy Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c index a1ef52ad5c75..50027d69c342 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c @@ -691,12 +691,21 @@ static int aspeed_g7_soc1_gpio_request_enable(struct pinctrl_dev *pctldev, { struct aspeed_g7_soc1_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct aspeed_g7_field field; + unsigned int val = 0; int ret = -ENOTSUPP; if (pin <= AC24) { + /* + * Balls W17 through AB19 are the ADC-capable pins: mux + * function 0 selects the ADC input and function 1 selects + * GPIO, unlike all other pins where function 0 is GPIO. + */ + if (pin >= W17 && pin <= AB19) + val = 1; field = aspeed_g7_soc1_pinmux_field_from_pin(pin); ret = regmap_update_bits(pctl->regmap, field.reg, - field.mask << field.shift, 0); + field.mask << field.shift, + val << field.shift); } return ret; From f0c565076d56407b4a1b9e75328df0f3adaa0194 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Sat, 23 May 2026 02:16:43 +0530 Subject: [PATCH 3060/5214] pinctrl: qcom: lpass-lpi: Enable runtime PM hooks on LPASS LPI SoCs The LPASS LPI core conversion to PM clock framework relies on variant drivers wiring runtime PM callbacks. Hook up runtime PM callbacks for the LPASS LPI variant drivers touched in this patch so they are prepared for the shared core conversion. This commit is a preparatory NOP on its own, as runtime PM is still disabled on these devices until the following core conversion patch. This is a mechanical per-variant driver update that relies on the same generic PM clock flow (of_pm_clk_add_clks() + pm_clk_suspend/ pm_clk_resume()) and DT-provided clocks. Runtime behavior was validated on Kodiak (sc7280). Suggested-by: Konrad Dybcio Signed-off-by: Ajay Kumar Nandam Signed-off-by: Linus Walleij --- .../pinctrl/qcom/pinctrl-milos-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c | 19 +++++++++++++------ .../pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c | 15 +++++++++++---- .../pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c | 7 +++++++ .../pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c | 15 +++++++++++---- .../pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c | 15 +++++++++++---- .../pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c | 15 +++++++++++---- .../pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c | 15 +++++++++++---- 12 files changed, 110 insertions(+), 26 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c index 3bf6fe0cf1bb..72b8ffd97860 100644 --- a/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -203,10 +205,15 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-milos-lpass-lpi-pinctrl", .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c index 750f410311a8..a61df10d46cb 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -129,20 +131,25 @@ static const struct lpi_pinctrl_variant_data sc7280_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sc7280-lpass-lpi-pinctrl", - .data = &sc7280_lpi_data, + .compatible = "qcom,sc7280-lpass-lpi-pinctrl", + .data = &sc7280_lpi_data, }, { - .compatible = "qcom,sm8350-lpass-lpi-pinctrl", - .data = &sc7280_lpi_data, + .compatible = "qcom,sm8350-lpass-lpi-pinctrl", + .data = &sc7280_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sc7280-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sc7280-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c index 0e839b6aaaf4..27c47710365e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -166,17 +168,22 @@ static const struct lpi_pinctrl_variant_data sc8280xp_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sc8280xp-lpass-lpi-pinctrl", - .data = &sc8280xp_lpi_data, + .compatible = "qcom,sc8280xp-lpass-lpi-pinctrl", + .data = &sc8280xp_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sc8280xp-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sc8280xp-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c index 65411abfbfac..7b5aacaae7d7 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm660-lpass-lpi.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "pinctrl-lpass-lpi.h" @@ -145,10 +147,15 @@ static const struct of_device_id sdm660_lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, sdm660_lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver sdm660_lpi_pinctrl_driver = { .driver = { .name = "qcom-sdm660-lpass-lpi-pinctrl", .of_match_table = sdm660_lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c index 858146c408d0..0a31f7ad2e0d 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include "pinctrl-lpass-lpi.h" @@ -151,10 +153,15 @@ static const struct of_device_id sdm670_lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, sdm670_lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver sdm670_lpi_pinctrl_driver = { .driver = { .name = "qcom-sdm670-lpass-lpi-pinctrl", .of_match_table = sdm670_lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c index c0e178be9cfc..75bafa62426a 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm4250-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -221,10 +223,15 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-sm4250-lpass-lpi-pinctrl", .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c index b7d9186861a2..05435ea6e17a 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -141,10 +143,15 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-sm6115-lpass-lpi-pinctrl", .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c index 4d06abcfedfd..946b23084304 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6350-lpass-lpi.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -135,10 +137,15 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-sm6350-lpass-lpi-pinctrl", .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c index c27452eece3e..454de788be21 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -127,17 +129,22 @@ static const struct lpi_pinctrl_variant_data sm8250_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sm8250-lpass-lpi-pinctrl", - .data = &sm8250_lpi_data, + .compatible = "qcom,sm8250-lpass-lpi-pinctrl", + .data = &sm8250_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sm8250-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sm8250-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c index 439f6541622e..834eee8dcce9 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -195,17 +197,22 @@ static const struct lpi_pinctrl_variant_data sm8450_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sm8450-lpass-lpi-pinctrl", - .data = &sm8450_lpi_data, + .compatible = "qcom,sm8450-lpass-lpi-pinctrl", + .data = &sm8450_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sm8450-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sm8450-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c index 73065919c8c2..875e04e5d2b9 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -203,17 +205,22 @@ static const struct lpi_pinctrl_variant_data sm8550_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sm8550-lpass-lpi-pinctrl", - .data = &sm8550_lpi_data, + .compatible = "qcom,sm8550-lpass-lpi-pinctrl", + .data = &sm8550_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sm8550-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sm8550-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c index f9fcedf5a65d..bc7889c993d0 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "pinctrl-lpass-lpi.h" @@ -210,17 +212,22 @@ static const struct lpi_pinctrl_variant_data sm8650_lpi_data = { static const struct of_device_id lpi_pinctrl_of_match[] = { { - .compatible = "qcom,sm8650-lpass-lpi-pinctrl", - .data = &sm8650_lpi_data, + .compatible = "qcom,sm8650-lpass-lpi-pinctrl", + .data = &sm8650_lpi_data, }, { } }; MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); +static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + static struct platform_driver lpi_pinctrl_driver = { .driver = { - .name = "qcom-sm8650-lpass-lpi-pinctrl", - .of_match_table = lpi_pinctrl_of_match, + .name = "qcom-sm8650-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + .pm = pm_ptr(&lpi_pinctrl_pm_ops), }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, From b719ede389d8a6b3fb24d3a6641fec2e46d8ff36 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Sat, 23 May 2026 02:16:44 +0530 Subject: [PATCH 3061/5214] pinctrl: qcom: lpass-lpi: Switch to PM clock framework for runtime PM Convert the LPASS LPI pinctrl driver to use the PM clock framework for runtime power management. This allows the LPASS LPI pinctrl driver to drop clock votes when idle, improves power efficiency on platforms using LPASS LPI island mode, and aligns the driver with common runtime PM patterns used across Qualcomm LPASS subsystems. Guard GPIO register read/write helpers and slew-rate register programming with synchronous runtime PM calls so the device is active during MMIO operations whenever autosuspend is enabled. Make PINCTRL_LPASS_LPI depend on PM_CLK, since this patch introduces direct PM clock API use in the shared core. Suggested-by: Konrad Dybcio Signed-off-by: Ajay Kumar Nandam Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/Kconfig | 1 + drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 144 +++++++++++++++-------- 2 files changed, 99 insertions(+), 46 deletions(-) diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index a09e840a01c6..18db350222b9 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -55,6 +55,7 @@ config PINCTRL_LPASS_LPI select PINCONF select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS + depends on PM_CLK depends on GPIOLIB help This is the pinctrl, pinmux, pinconf and gpiolib driver for the diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 15ced5027579..4d758fd117c4 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -11,10 +11,13 @@ #include #include #include +#include #include #include #include +#include +#include #include "../pinctrl-utils.h" @@ -22,7 +25,6 @@ #define MAX_NR_GPIO 32 #define GPIO_FUNC 0 -#define MAX_LPI_NUM_CLKS 2 struct lpi_pinctrl { struct device *dev; @@ -31,15 +33,14 @@ struct lpi_pinctrl { struct pinctrl_desc desc; char __iomem *tlmm_base; char __iomem *slew_base; - struct clk_bulk_data clks[MAX_LPI_NUM_CLKS]; /* Protects from concurrent register updates */ struct mutex lock; DECLARE_BITMAP(ever_gpio, MAX_NR_GPIO); const struct lpi_pinctrl_variant_data *data; }; -static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin, - unsigned int addr) +static void __iomem *lpi_gpio_reg(struct lpi_pinctrl *state, + unsigned int pin, unsigned int addr) { u32 pin_offset; @@ -48,22 +49,48 @@ static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin, else pin_offset = LPI_TLMM_REG_OFFSET * pin; - return ioread32(state->tlmm_base + pin_offset + addr); + return state->tlmm_base + pin_offset + addr; +} + +static void __lpi_gpio_read(struct lpi_pinctrl *state, + unsigned int pin, unsigned int addr, u32 *val) +{ + *val = ioread32(lpi_gpio_reg(state, pin, addr)); +} + +static void __lpi_gpio_write(struct lpi_pinctrl *state, + unsigned int pin, unsigned int addr, + unsigned int val) +{ + iowrite32(val, lpi_gpio_reg(state, pin, addr)); +} + +static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin, + unsigned int addr, u32 *val) +{ + int ret; + + ret = pm_runtime_resume_and_get(state->dev); + if (ret < 0) + return ret; + + __lpi_gpio_read(state, pin, addr, val); + + return pm_runtime_put_autosuspend(state->dev); } static int lpi_gpio_write(struct lpi_pinctrl *state, unsigned int pin, unsigned int addr, unsigned int val) { - u32 pin_offset; + int ret; - if (state->data->flags & LPI_FLAG_USE_PREDEFINED_PIN_OFFSET) - pin_offset = state->data->groups[pin].pin_offset; - else - pin_offset = LPI_TLMM_REG_OFFSET * pin; + ret = pm_runtime_resume_and_get(state->dev); + if (ret < 0) + return ret; - iowrite32(val, state->tlmm_base + pin_offset + addr); + __lpi_gpio_write(state, pin, addr, val); - return 0; + return pm_runtime_put_autosuspend(state->dev); } static const struct pinctrl_ops lpi_gpio_pinctrl_ops = { @@ -107,8 +134,8 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, { struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); const struct lpi_pingroup *g = &pctrl->data->groups[group]; - u32 val; - int i, pin = g->pin; + u32 io_val, val; + int i, pin = g->pin, ret; for (i = 0; i < g->nfuncs; i++) { if (g->funcs[i] == function) @@ -118,8 +145,12 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, if (WARN_ON(i == g->nfuncs)) return -EINVAL; - mutex_lock(&pctrl->lock); - val = lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG); + ret = pm_runtime_resume_and_get(pctrl->dev); + if (ret < 0) + return ret; + + guard(mutex)(&pctrl->lock); + __lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG, &val); /* * If this is the first time muxing to GPIO and the direction is @@ -129,24 +160,25 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, */ if (i == GPIO_FUNC && (val & LPI_GPIO_OE_MASK) && !test_and_set_bit(group, pctrl->ever_gpio)) { - u32 io_val = lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG); + __lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG, &io_val); if (io_val & LPI_GPIO_VALUE_IN_MASK) { if (!(io_val & LPI_GPIO_VALUE_OUT_MASK)) - lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, - io_val | LPI_GPIO_VALUE_OUT_MASK); + __lpi_gpio_write(pctrl, group, + LPI_GPIO_VALUE_REG, + io_val | LPI_GPIO_VALUE_OUT_MASK); } else { if (io_val & LPI_GPIO_VALUE_OUT_MASK) - lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, - io_val & ~LPI_GPIO_VALUE_OUT_MASK); + __lpi_gpio_write(pctrl, group, + LPI_GPIO_VALUE_REG, + io_val & ~LPI_GPIO_VALUE_OUT_MASK); } } u32p_replace_bits(&val, i, LPI_GPIO_FUNCTION_MASK); - lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val); - mutex_unlock(&pctrl->lock); + __lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val); - return 0; + return pm_runtime_put_autosuspend(pctrl->dev); } static const struct pinmux_ops lpi_gpio_pinmux_ops = { @@ -162,11 +194,15 @@ static int lpi_config_get(struct pinctrl_dev *pctldev, unsigned int param = pinconf_to_config_param(*config); struct lpi_pinctrl *state = dev_get_drvdata(pctldev->dev); unsigned int arg = 0; + u32 ctl_reg; int is_out; int pull; - u32 ctl_reg; + int ret; + + ret = lpi_gpio_read(state, pin, LPI_GPIO_CFG_REG, &ctl_reg); + if (ret) + return ret; - ctl_reg = lpi_gpio_read(state, pin, LPI_GPIO_CFG_REG); is_out = ctl_reg & LPI_GPIO_OE_MASK; pull = FIELD_GET(LPI_GPIO_PULL_MASK, ctl_reg); @@ -197,6 +233,7 @@ static int lpi_config_get(struct pinctrl_dev *pctldev, } *config = pinconf_to_config_packed(param, arg); + return 0; } @@ -206,7 +243,7 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, { unsigned long sval; void __iomem *reg; - int slew_offset; + int slew_offset, ret; if (slew > LPI_SLEW_RATE_MAX) { dev_err(pctrl->dev, "invalid slew rate %u for pin: %d\n", @@ -225,6 +262,10 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, else reg = pctrl->slew_base + LPI_SLEW_RATE_CTL_REG; + ret = pm_runtime_resume_and_get(pctrl->dev); + if (ret < 0) + return ret; + mutex_lock(&pctrl->lock); sval = ioread32(reg); @@ -234,7 +275,7 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, mutex_unlock(&pctrl->lock); - return 0; + return pm_runtime_put_autosuspend(pctrl->dev); } static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, @@ -244,8 +285,8 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned int param, arg, pullup = LPI_GPIO_BIAS_DISABLE, strength = 2; bool value, output_enabled = false; const struct lpi_pingroup *g; - int i, ret; u32 val; + int i, ret; g = &pctrl->data->groups[group]; for (i = 0; i < nconfs; i++) { @@ -289,23 +330,26 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, * As per Hardware Programming Guide, when configuring pin as output, * set the pin value before setting output-enable (OE). */ + ret = pm_runtime_resume_and_get(pctrl->dev); + if (ret < 0) + return ret; + + guard(mutex)(&pctrl->lock); if (output_enabled) { val = u32_encode_bits(value ? 1 : 0, LPI_GPIO_VALUE_OUT_MASK); - lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val); + __lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val); } - mutex_lock(&pctrl->lock); - val = lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG); + __lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG, &val); u32p_replace_bits(&val, pullup, LPI_GPIO_PULL_MASK); u32p_replace_bits(&val, LPI_GPIO_DS_TO_VAL(strength), LPI_GPIO_OUT_STRENGTH_MASK); u32p_replace_bits(&val, output_enabled, LPI_GPIO_OE_MASK); - lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val); - mutex_unlock(&pctrl->lock); + __lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val); - return 0; + return pm_runtime_put_autosuspend(pctrl->dev); } static const struct pinconf_ops lpi_gpio_pinconf_ops = { @@ -354,9 +398,14 @@ static int lpi_gpio_direction_output(struct gpio_chip *chip, static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin) { struct lpi_pinctrl *state = gpiochip_get_data(chip); + u32 val; + int ret; - return lpi_gpio_read(state, pin, LPI_GPIO_VALUE_REG) & - LPI_GPIO_VALUE_IN_MASK; + ret = lpi_gpio_read(state, pin, LPI_GPIO_VALUE_REG, &val); + if (ret) + return ret; + + return val & LPI_GPIO_VALUE_IN_MASK; } static int lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) @@ -399,7 +448,9 @@ static void lpi_gpio_dbg_show_one(struct seq_file *s, pctldev = pctldev ? : state->ctrl; pindesc = pctldev->desc->pins[offset]; - ctl_reg = lpi_gpio_read(state, offset, LPI_GPIO_CFG_REG); + if (lpi_gpio_read(state, offset, LPI_GPIO_CFG_REG, &ctl_reg)) + return; + is_out = ctl_reg & LPI_GPIO_OE_MASK; func = FIELD_GET(LPI_GPIO_FUNCTION_MASK, ctl_reg); @@ -482,9 +533,6 @@ int lpi_pinctrl_probe(struct platform_device *pdev) pctrl->data = data; pctrl->dev = &pdev->dev; - pctrl->clks[0].id = "core"; - pctrl->clks[1].id = "audio"; - pctrl->tlmm_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pctrl->tlmm_base)) return dev_err_probe(dev, PTR_ERR(pctrl->tlmm_base), @@ -497,13 +545,19 @@ int lpi_pinctrl_probe(struct platform_device *pdev) "Slew resource not provided\n"); } - ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks); + ret = devm_pm_clk_create(dev); if (ret) return ret; - ret = clk_bulk_prepare_enable(MAX_LPI_NUM_CLKS, pctrl->clks); + ret = of_pm_clk_add_clks(dev); + if (ret < 0 && ret != -ENODEV) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - return dev_err_probe(dev, ret, "Can't enable clocks\n"); + return ret; pctrl->desc.pctlops = &lpi_gpio_pinctrl_ops; pctrl->desc.pmxops = &lpi_gpio_pinmux_ops; @@ -542,7 +596,6 @@ int lpi_pinctrl_probe(struct platform_device *pdev) err_pinctrl: mutex_destroy(&pctrl->lock); - clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks); return ret; } @@ -554,7 +607,6 @@ void lpi_pinctrl_remove(struct platform_device *pdev) int i; mutex_destroy(&pctrl->lock); - clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks); for (i = 0; i < pctrl->data->npins; i++) pinctrl_generic_remove_group(pctrl->ctrl, i); From 052f67a1ae6ffa6e43c82193bf2e6c0dfd2e6d8f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Jun 2026 10:26:26 +0200 Subject: [PATCH 3062/5214] powerpc/8xx: implement get_direction() in cpm1 The lack of get_direction() callbacks in this driver causes GPIOLIB to emit a warning. Implement them for 16- and 32-bit variants. Reported-by: Christophe Leroy Closes: https://lore.kernel.org/all/63487206f6e5a93eaf9f41784317fe99d394312f.1780399750.git.chleroy@kernel.org/ Signed-off-by: Bartosz Golaszewski Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Tested-by: Christophe Leroy (CS GROUP) Reviewed-by: Linus Walleij Reviewed-by: Christophe Leroy (CS GROUP) [Maddy: Fixed the Fixes tag] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260603-powerpc-8xx-cpm1-get-dir-v1-1-2ae1c9a5b992@oss.qualcomm.com --- arch/powerpc/platforms/8xx/cpm1.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index f00734f0590c..b31376bf6778 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -472,6 +472,18 @@ static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio16_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport16 __iomem *iop = cpm1_gc->regs; + u16 pin_mask = 1 << (15 - gpio); + + if (in_be16(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add16(struct device *dev) { struct device_node *np = dev->of_node; @@ -498,6 +510,7 @@ int cpm1_gpiochip_add16(struct device *dev) gc->ngpio = 16; gc->direction_input = cpm1_gpio16_dir_in; gc->direction_output = cpm1_gpio16_dir_out; + gc->get_direction = cpm1_gpio16_get_direction; gc->get = cpm1_gpio16_get; gc->set = cpm1_gpio16_set; gc->to_irq = cpm1_gpio16_to_irq; @@ -604,6 +617,18 @@ static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio32_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport32b __iomem *iop = cpm1_gc->regs; + u32 pin_mask = 1 << (31 - gpio); + + if (in_be32(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add32(struct device *dev) { struct device_node *np = dev->of_node; @@ -621,6 +646,7 @@ int cpm1_gpiochip_add32(struct device *dev) gc->ngpio = 32; gc->direction_input = cpm1_gpio32_dir_in; gc->direction_output = cpm1_gpio32_dir_out; + gc->get_direction = cpm1_gpio32_get_direction; gc->get = cpm1_gpio32_get; gc->set = cpm1_gpio32_set; gc->parent = dev; From 334f3f6d7a1660fd65597bad9960bfa33a716d35 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Wed, 3 Jun 2026 18:40:54 +0530 Subject: [PATCH 3063/5214] powerpc/entry: Disable interrupts before irqentry_exit Venkat reported a panic on powerpc-next tree where GENERIC_ENTRY has been enabled. kernel BUG at kernel/sched/core.c:7512! NIP preempt_schedule_irq+0x44/0x118 LR dynamic_irqentry_exit_cond_resched+0x40/0x1a4 Call Trace: dynamic_irqentry_exit_cond_resched+0x40/0x1a4 do_page_fault+0xc0/0x104 data_access_common_virt+0x210/0x220 This happens since __do_page_fault ends up enabling the interrupts and it could take significant time such that need_resched could be set. This leads to schedule call in irqentry_exit leading to the bug. There are many such irq handlers which enables the interrupts. Fix it by disabling the irq before calling irqentry_exit. The same pattern exists today in interrupt_exit_kernel_prepare. Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") Reported-by: Venkat Rao Bagalkote Closes: https://lore.kernel.org/all/7904105b-9dfa-4efd-a5ef-bc0276ed255d@linux.ibm.com/ Signed-off-by: Shrikanth Hegde Tested-by: Venkat Rao Bagalkote Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260603131054.216235-1-sshegde@linux.ibm.com --- arch/powerpc/include/asm/entry-common.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index de5601282755..fc636c42e89a 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -260,9 +260,10 @@ static inline void arch_interrupt_exit_prepare(struct pt_regs *regs) * AMR can only have been unlocked if we interrupted the kernel. */ kuap_assert_locked(); - - local_irq_disable(); } + + /* irqentry_exit expects to be called with interrupts disabled */ + local_irq_disable(); } static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs) From 90b45dbf59d50e32c4f86072545ff44bec9cb28e Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Wed, 3 Jun 2026 12:20:14 +0200 Subject: [PATCH 3064/5214] powerpc: Simplify access_ok() With the implementation of masked user access, we always have a memory gap between user memory space and kernel memory space, so use it to simplify access_ok() by relying on access fault in case of an access in the gap. Most of the time the size is known at build time. On powerpc64, the kernel space starts at 0x8000000000000000 which is always more than two times TASK_USER_MAX so when the size is known at build time and lower than TASK_USER_MAX, only the address needs to be verified. If not, a binary or of address and size must be lower than TASK_USER_MAX. As TASK_USER_MAX is a power of 2, just check that there is no bit set outside of TASK_USER_MAX - 1 mask. On powerpc32, there is a garanteed gap of 128KB so when the size is known at build time and not greater than 128KB, just check that the address is below TASK_SIZE. Otherwise use the original formula. Signed-off-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/f496ba4f309a2fb9fd0d8811e941bbcaf5d196cf.1780482002.git.chleroy@kernel.org --- arch/powerpc/include/asm/uaccess.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index e98c628e3899..7b8c56962c31 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -18,8 +18,34 @@ /* Threshold above which VMX copy path is used */ #define VMX_COPY_THRESHOLD 3328 +#define __access_ok __access_ok + #include +/* + * On powerpc64, TASK_SIZE_MAX is 0x0010000000000000 then even if both ptr and size + * are TASK_SIZE_MAX we are still inside the memory gap. So make it simple. + */ +static __always_inline int __access_ok(const void __user *ptr, unsigned long size) +{ + unsigned long addr = (unsigned long)ptr; + + if (IS_ENABLED(CONFIG_PPC64)) { + BUILD_BUG_ON(!is_power_of_2(TASK_SIZE_MAX)); + BUILD_BUG_ON(TASK_SIZE_MAX > 0x0010000000000000); + + if (statically_true(size > TASK_SIZE_MAX)) + return false; + if (statically_true(size <= TASK_SIZE_MAX)) + return !(addr & ~(TASK_SIZE_MAX - 1)); + return !((size | addr) & ~(TASK_SIZE_MAX - 1)); + } else { + if (statically_true(size <= SZ_128K)) + return addr < TASK_SIZE; + return size <= TASK_SIZE && addr <= TASK_SIZE - size; + } +} + /* * These are the main single-value transfer routines. They automatically * use the right size if we just have the right pointer type. From c1bef05763c94ae284ee2881c03bf0753f8d213a Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 8 Jun 2026 14:31:47 +0800 Subject: [PATCH 3065/5214] thunderbolt: debugfs: Fix sideband write size check sb_regs_write() looks up the matching sideband register entry before validating the number of bytes to write. However, the size check uses sb_regs->size, which is the size of the first entry in the register table, instead of the matched entry. This rejects valid writes to larger sideband registers such as USB4_SB_DEBUG or USB4_SB_DATA. Use the matched register entry for the size check. Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 369904287497..f5cf0e177f40 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -366,7 +366,7 @@ static ssize_t sb_regs_write(struct tb_port *port, const struct sb_reg *sb_regs, if (!sb_reg) return -EINVAL; - if (bytes_read > sb_regs->size) + if (bytes_read > sb_reg->size) return -E2BIG; ret = usb4_port_sb_write(port, target, index, sb_reg->reg, data, From c4441b95ae8012a99c6fe22b4f56155e0ddbd042 Mon Sep 17 00:00:00 2001 From: Devendra K Verma Date: Fri, 5 Jun 2026 16:58:29 +0530 Subject: [PATCH 3066/5214] dmaengine: dw-edma: Add Xilinx CPM6-DMA DeviceID Add Device ID for AMD (Xilinx) CPM6 DMA IP. This IP enables 64 Read and 64 Write Channels. Adding the relevant dw_edma_pcie_data to use 8 Read and 8 Write channels for initial commit. Signed-off-by: Devendra K Verma Reviewed-by: Frank Li Link: https://patch.msgid.link/20260605112829.679697-1-devendra.verma@amd.com Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-pcie.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 0b30ce138503..2082d0021a8d 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -27,6 +27,7 @@ /* AMD MDB (Xilinx) specific defines */ #define PCI_DEVICE_ID_XILINX_B054 0xb054 +#define PCI_DEVICE_ID_XILINX_B00F 0xb00f #define DW_PCIE_XILINX_MDB_VSEC_DMA_ID 0x6 #define DW_PCIE_XILINX_MDB_VSEC_ID 0x20 @@ -125,6 +126,19 @@ static const struct dw_edma_pcie_data xilinx_mdb_data = { .rd_ch_cnt = 8, }; +static const struct dw_edma_pcie_data xilinx_cpm6_dma_data = { + /* MDB registers location */ + .rg.bar = BAR_0, + .rg.off = SZ_4K, /* 4 Kbytes */ + .rg.sz = SZ_8K, /* 8 Kbytes */ + + /* Other */ + .mf = EDMA_MF_HDMA_NATIVE, + .irqs = 1, + .wr_ch_cnt = 8, + .rd_ch_cnt = 8, +}; + static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata, enum pci_barno bar, off_t start_off, off_t ll_off_gap, size_t ll_size, @@ -547,6 +561,8 @@ static const struct pci_device_id dw_edma_pcie_id_table[] = { { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) }, { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054), (kernel_ulong_t)&xilinx_mdb_data }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B00F), + .driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_data }, { } }; MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table); From b55bfcc677dd58d808a53173e0574b466dc27b9f Mon Sep 17 00:00:00 2001 From: Sheetal Date: Sun, 17 May 2026 16:30:45 +0000 Subject: [PATCH 3067/5214] dmaengine: tegra210-adma: Add error logging on failure paths Add dev_err/dev_err_probe logging across failure paths to improve debuggability of DMA errors during runtime and probe. Use return dev_err_probe() pattern where no cleanup is required in the probe function. On error paths that need explicit unwind, store the dev_err_probe() return value in ret before jumping to the cleanup label. Also convert existing dev_err calls in probe to dev_err_probe for consistency, and use dev_err in non-probe functions. Keep explicit runtime PM and DMA registration unwind instead of managed or scoped cleanup. The scoped runtime PM guard releases the usage count with pm_runtime_put(), while this probe error path needs pm_runtime_put_sync() before pm_runtime_disable(). The OF DMA registration failure path also needs to unregister the DMA engine before dropping the runtime PM reference. Signed-off-by: Sheetal Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260517163045.363444-1-sheetal@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra210-adma.c | 63 ++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index 14e0c408ed1e..ceaee1e33e68 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -335,8 +335,16 @@ static int tegra_adma_request_alloc(struct tegra_adma_chan *tdc, struct tegra_adma *tdma = tdc->tdma; unsigned int sreq_index = tdc->sreq_index; - if (tdc->sreq_reserved) - return tdc->sreq_dir == direction ? 0 : -EINVAL; + if (tdc->sreq_reserved) { + if (tdc->sreq_dir != direction) { + dev_err(tdma->dev, + "DMA request direction mismatch: reserved=%s, requested=%s\n", + dmaengine_get_direction_text(tdc->sreq_dir), + dmaengine_get_direction_text(direction)); + return -EINVAL; + } + return 0; + } if (sreq_index > tdma->cdata->ch_req_max) { dev_err(tdma->dev, "invalid DMA request\n"); @@ -665,8 +673,11 @@ static int tegra_adma_set_xfer_params(struct tegra_adma_chan *tdc, const struct tegra_adma_chip_data *cdata = tdc->tdma->cdata; unsigned int burst_size, adma_dir, fifo_size_shift; - if (desc->num_periods > ADMA_CH_CONFIG_MAX_BUFS) + if (desc->num_periods > ADMA_CH_CONFIG_MAX_BUFS) { + dev_err(tdc2dev(tdc), "invalid DMA periods %zu (max %u)\n", + desc->num_periods, ADMA_CH_CONFIG_MAX_BUFS); return -EINVAL; + } switch (direction) { case DMA_MEM_TO_DEV: @@ -1029,8 +1040,8 @@ static int tegra_adma_probe(struct platform_device *pdev) cdata = of_device_get_match_data(&pdev->dev); if (!cdata) { - dev_err(&pdev->dev, "device match data not found\n"); - return -ENODEV; + return dev_err_probe(&pdev->dev, -ENODEV, + "device match data not found\n"); } tdma = devm_kzalloc(&pdev->dev, @@ -1056,7 +1067,8 @@ static int tegra_adma_probe(struct platform_device *pdev) unsigned int ch_base_offset; if (res_page->start < res_base->start) - return -EINVAL; + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid page/global resource order\n"); page_offset = res_page->start - res_base->start; ch_base_offset = cdata->ch_base_offset; if (!ch_base_offset) @@ -1064,7 +1076,9 @@ static int tegra_adma_probe(struct platform_device *pdev) page_no = div_u64(page_offset, ch_base_offset); if (!page_no || page_no > INT_MAX) - return -EINVAL; + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid page number %llu\n", + (unsigned long long)page_no); tdma->ch_page_no = page_no - 1; tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base); @@ -1079,7 +1093,8 @@ static int tegra_adma_probe(struct platform_device *pdev) if (IS_ERR(tdma->base_addr)) return PTR_ERR(tdma->base_addr); } else { - return -ENODEV; + return dev_err_probe(&pdev->dev, -ENODEV, + "failed to get memory resource\n"); } tdma->ch_base_addr = tdma->base_addr + cdata->ch_base_offset; @@ -1087,8 +1102,8 @@ static int tegra_adma_probe(struct platform_device *pdev) tdma->ahub_clk = devm_clk_get(&pdev->dev, "d_audio"); if (IS_ERR(tdma->ahub_clk)) { - dev_err(&pdev->dev, "Error: Missing ahub controller clock\n"); - return PTR_ERR(tdma->ahub_clk); + return dev_err_probe(&pdev->dev, PTR_ERR(tdma->ahub_clk), + "failed to get ahub clock\n"); } tdma->dma_chan_mask = devm_kzalloc(&pdev->dev, @@ -1104,8 +1119,8 @@ static int tegra_adma_probe(struct platform_device *pdev) (u32 *)tdma->dma_chan_mask, BITS_TO_U32(tdma->nr_channels)); if (ret < 0 && (ret != -EINVAL)) { - dev_err(&pdev->dev, "dma-channel-mask is not complete.\n"); - return ret; + return dev_err_probe(&pdev->dev, ret, + "dma-channel-mask is not complete.\n"); } INIT_LIST_HEAD(&tdma->dma_dev.channels); @@ -1127,11 +1142,13 @@ static int tegra_adma_probe(struct platform_device *pdev) cdata->global_ch_config_base + (4 * i); } - tdc->irq = of_irq_get(pdev->dev.of_node, i); - if (tdc->irq <= 0) { - ret = tdc->irq ?: -ENXIO; + ret = of_irq_get(pdev->dev.of_node, i); + if (ret <= 0) { + ret = dev_err_probe(&pdev->dev, ret ?: -ENXIO, + "failed to get IRQ for channel %d\n", i); goto irq_dispose; } + tdc->irq = ret; vchan_init(&tdc->vc, &tdma->dma_dev); tdc->vc.desc_free = tegra_adma_desc_free; @@ -1141,12 +1158,18 @@ static int tegra_adma_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret < 0) + if (ret < 0) { + ret = dev_err_probe(&pdev->dev, ret, + "runtime PM resume failed\n"); goto rpm_disable; + } ret = tegra_adma_init(tdma); - if (ret) + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "failed to initialize ADMA\n"); goto rpm_put; + } dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); @@ -1172,14 +1195,16 @@ static int tegra_adma_probe(struct platform_device *pdev) ret = dma_async_device_register(&tdma->dma_dev); if (ret < 0) { - dev_err(&pdev->dev, "ADMA registration failed: %d\n", ret); + ret = dev_err_probe(&pdev->dev, ret, + "ADMA registration failed\n"); goto rpm_put; } ret = of_dma_controller_register(pdev->dev.of_node, tegra_dma_of_xlate, tdma); if (ret < 0) { - dev_err(&pdev->dev, "ADMA OF registration failed %d\n", ret); + ret = dev_err_probe(&pdev->dev, ret, + "ADMA OF registration failed\n"); goto dma_remove; } From cc4fea19daeb0460fe3569e0a2d523f427b2bac1 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 19:08:43 -0700 Subject: [PATCH 3068/5214] dmaengine: ste_dma40: turn d40_base phy_chans into a flexible array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the separately-offset phy_chans pointer to a C99 flexible array member at the end of struct d40_base, and switch the allocation to struct_size(). The log_chans and memcpy_chans slots continue to live in the same allocation immediately after phy_chans, indexed via base->log_chans. This removes the hand-rolled pointer fixup that recomputed phy_chans from base + ALIGN(sizeof(struct d40_base), 4). The ALIGN(sizeof(struct d40_base), 4) requirement is met implicitly by the C compiler when using a flexible array member. With struct d40_chan phy_chans[] as the last member, the C standard guarantees sizeof(struct d40_base) includes trailing padding to satisfy the alignment of the flexible array element type (struct d40_chan). Since struct d40_chan contains members like spinlock_t, pointers, and struct dma_chan — all with alignment ≥ 4 — the compiler ensures sizeof(struct d40_base) is already a multiple of _Alignof(struct d40_chan) >= 4. The struct_size() macro then computes sizeof(struct d40_base) + sizeof(struct d40_chan) * num_phy_chans, so phy_chans[0] lands at a properly aligned offset without needing the manual ALIGN. Assisted-by: Claude:Opus-4.7 Signed-off-by: Rosen Penev Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260531020843.594892-1-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 9b803c0aec25..0d9ffa3e2663 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -602,7 +602,6 @@ struct d40_base { struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; - struct d40_chan *phy_chans; struct d40_chan *log_chans; struct d40_chan **lookup_log_chans; struct d40_chan **lookup_phy_chans; @@ -621,6 +620,7 @@ struct d40_base { u32 *regs_interrupt; u16 gcc_pwr_off_mask; struct d40_gen_dmac gen_dmac; + struct d40_chan phy_chans[]; }; static struct device *chan2dev(struct d40_chan *d40c) @@ -3128,6 +3128,7 @@ static int __init d40_hw_detect_init(struct platform_device *pdev, struct clk *clk; void __iomem *virtbase; struct d40_base *base; + size_t alloc_size; int num_log_chans; int num_phy_chans; int num_memcpy_chans; @@ -3185,22 +3186,24 @@ static int __init d40_hw_detect_init(struct platform_device *pdev, else num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + num_phy_chans = min(num_phy_chans, STEDMA40_MAX_PHYS); + /* The number of channels used for memcpy */ if (plat_data->num_of_memcpy_chans) num_memcpy_chans = plat_data->num_of_memcpy_chans; else num_memcpy_chans = ARRAY_SIZE(dma40_memcpy_channels); + num_memcpy_chans = min(num_memcpy_chans, D40_MEMCPY_MAX_CHANS); num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY; dev_info(dev, "hardware rev: %d with %d physical and %d logical channels\n", rev, num_phy_chans, num_log_chans); - base = devm_kzalloc(dev, - ALIGN(sizeof(struct d40_base), 4) + - (num_phy_chans + num_log_chans + num_memcpy_chans) * - sizeof(struct d40_chan), GFP_KERNEL); + alloc_size = struct_size(base, phy_chans, num_phy_chans); + alloc_size += sizeof(*base->log_chans) * (num_log_chans + num_memcpy_chans); + base = devm_kzalloc(dev, alloc_size, GFP_KERNEL); if (!base) return -ENOMEM; @@ -3213,7 +3216,6 @@ static int __init d40_hw_detect_init(struct platform_device *pdev, base->virtbase = virtbase; base->plat_data = plat_data; base->dev = dev; - base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; if (base->plat_data->num_of_phy_chans == 14) { From 4e351f408743354d54ee1af5193fc78234f2044e Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 2 Jun 2026 15:03:44 +0800 Subject: [PATCH 3069/5214] dmaengine: qcom: gpi: set DMA_PRIVATE capability The GPI DMA controller is only responsible for QUP peripherals, and cannot work as a general-purpose DMA accelerator. Set DMA_PRIVATE capability for it. This fixes error messages about GPI being shown when an async-tx consumer is loaded. Fixes: 5d0c3533a19f ("dmaengine: qcom: Add GPI dma driver") Signed-off-by: Icenowy Zheng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260602070344.3707256-1-zhengxingda@iscas.ac.cn Signed-off-by: Vinod Koul --- drivers/dma/qcom/gpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index c9a6f610ffd9..a5055a6273af 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -2260,6 +2260,7 @@ static int gpi_probe(struct platform_device *pdev) /* clear and Set capabilities */ dma_cap_zero(gpi_dev->dma_device.cap_mask); dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask); /* configure dmaengine apis */ gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); From 87aab0781cb35baa2214d0a95ff01e786e428226 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 13:03:22 -0700 Subject: [PATCH 3070/5214] dmaengine: dmatest: split struct dmatest_info from variable declaration Combining the struct definition with its variable initializer confuses the kernel-doc parser because __MUTEX_INITIALIZER() expands to contain braces, breaking brace counting and causing: Warning: drivers/dma/dmatest.c:152 struct member '' not described in 'dmatest_info' Split into separate struct definition and variable declaration, which is the standard kernel pattern. Assisted-by: Opencode:Big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260530200322.7584-1-rosenp@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index df38681a1ff4..2ae3469397f3 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -137,7 +137,7 @@ struct dmatest_params { * @did_init: module has been initialized completely * @last_error: test has faced configuration issues */ -static struct dmatest_info { +struct dmatest_info { /* Test parameters */ struct dmatest_params params; @@ -147,7 +147,9 @@ static struct dmatest_info { int last_error; struct mutex lock; bool did_init; -} test_info = { +}; + +static struct dmatest_info test_info = { .channels = LIST_HEAD_INIT(test_info.channels), .lock = __MUTEX_INITIALIZER(test_info.lock), }; From 1d736d76c7d16359bae042b8c9fbb0fdac158721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 15:50:10 +0200 Subject: [PATCH 3071/5214] dmaengine: cirrus: Drop left-over from platform probing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 2e7f55ce4302 ("dmaengine: cirrus: Convert to DT for Cirrus EP93xx") the driver cannot probe devices using the traditional platform device way any more. Thus the driver's .id_table serves no purpose any more and can be dropped. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/c3830cb95b0bb939f9cc9543dfa3047e41532c47.1779976024.git.ukleinek@kernel.org Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 8eceb96d058c..a3395cfcf5dd 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1587,18 +1587,11 @@ static const struct of_device_id ep93xx_dma_of_ids[] = { }; MODULE_DEVICE_TABLE(of, ep93xx_dma_of_ids); -static const struct platform_device_id ep93xx_dma_driver_ids[] = { - { "ep93xx-dma-m2p", 0 }, - { "ep93xx-dma-m2m", 1 }, - { }, -}; - static struct platform_driver ep93xx_dma_driver = { .driver = { .name = "ep93xx-dma", .of_match_table = ep93xx_dma_of_ids, }, - .id_table = ep93xx_dma_driver_ids, .probe = ep93xx_dma_probe, }; From ead262712217296cad7d9db1707e504f16e2cb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 12:55:33 +0200 Subject: [PATCH 3072/5214] dmaengine: nbpfaxi: Drop unused platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dma-nbpf driver only probes devices from device tree and fails to probe devices relying on the traditional platform device probe path. So the platform_device_id array is unused apart from providing misleading module meta data. Drop it. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/5f7380828873e2375e319ef091178d11a277a0ac.1779965563.git.u.kleine-koenig@baylibre.com Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 334425faac00..05d7321629cc 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1486,20 +1486,6 @@ static void nbpf_remove(struct platform_device *pdev) clk_disable_unprepare(nbpf->clk); } -static const struct platform_device_id nbpf_ids[] = { - {"nbpfaxi64dmac1b4", (kernel_ulong_t)&nbpf_cfg[NBPF1B4]}, - {"nbpfaxi64dmac1b8", (kernel_ulong_t)&nbpf_cfg[NBPF1B8]}, - {"nbpfaxi64dmac1b16", (kernel_ulong_t)&nbpf_cfg[NBPF1B16]}, - {"nbpfaxi64dmac4b4", (kernel_ulong_t)&nbpf_cfg[NBPF4B4]}, - {"nbpfaxi64dmac4b8", (kernel_ulong_t)&nbpf_cfg[NBPF4B8]}, - {"nbpfaxi64dmac4b16", (kernel_ulong_t)&nbpf_cfg[NBPF4B16]}, - {"nbpfaxi64dmac8b4", (kernel_ulong_t)&nbpf_cfg[NBPF8B4]}, - {"nbpfaxi64dmac8b8", (kernel_ulong_t)&nbpf_cfg[NBPF8B8]}, - {"nbpfaxi64dmac8b16", (kernel_ulong_t)&nbpf_cfg[NBPF8B16]}, - {}, -}; -MODULE_DEVICE_TABLE(platform, nbpf_ids); - static int nbpf_runtime_suspend(struct device *dev) { struct nbpf_device *nbpf = dev_get_drvdata(dev); @@ -1523,7 +1509,6 @@ static struct platform_driver nbpf_driver = { .of_match_table = nbpf_match, .pm = pm_ptr(&nbpf_pm_ops), }, - .id_table = nbpf_ids, .probe = nbpf_probe, .remove = nbpf_remove, }; From 57e766bd3ddb2495d80952ad4fc723fb538e1d43 Mon Sep 17 00:00:00 2001 From: Devendra K Verma Date: Tue, 26 May 2026 11:01:10 +0530 Subject: [PATCH 3073/5214] dmaengine: dw-edma: Remove dw_edma_add_irq_mask() Function dw_edma_add_irq_mask() sets the mask of the interrupts alloted to read / write channels in a variable. The mask set for read / write channels is niether used nor this function is called else where, making it redundant. The redundant function can be removed safely as it is not affecting anything. Signed-off-by: Devendra K Verma Reviewed-by: Frank Li Link: https://patch.msgid.link/20260526053111.3244488-1-devverma@amd.com Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-core.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index c2feb3adc79f..89a4c498a17b 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -988,20 +988,12 @@ static inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt) } } -static inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt) -{ - while (*mask * alloc < cnt) - (*mask)++; -} - static int dw_edma_irq_request(struct dw_edma *dw, u32 *wr_alloc, u32 *rd_alloc) { struct dw_edma_chip *chip = dw->chip; struct device *dev = dw->chip->dev; struct msi_desc *msi_desc; - u32 wr_mask = 1; - u32 rd_mask = 1; int i, err = 0; u32 ch_cnt; int irq; @@ -1038,9 +1030,6 @@ static int dw_edma_irq_request(struct dw_edma *dw, dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt); } - dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt); - dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt); - for (i = 0; i < (*wr_alloc + *rd_alloc); i++) { irq = chip->ops->irq_vector(dev, i); err = request_irq(irq, From 5d596b9139f59ce412f41283baadaf809936eaf4 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Mon, 25 May 2026 11:07:49 +0000 Subject: [PATCH 3074/5214] irqchip/renesas-rzv2h: Add DMA ACK signal routing support Some peripherals on RZ/G3E SoCs (SSIU, SPDIF, SCU/SRC, DVC) require explicit ACK signal routing through the ICU via the ICU_DMACKSELk registers for level-based DMA handshaking. Add rzv2h_icu_register_dma_ack() to configure ICU_DMACKSELk, routing a DMAC channel's ACK signal to the specified peripheral. Signed-off-by: John Madieu Acked-by: Thomas Gleixner Link: https://patch.msgid.link/20260525110750.4020112-2-john.madieu.xa@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/irqchip/irq-renesas-rzv2h.c | 40 +++++++++++++++++++++++ include/linux/irqchip/irq-renesas-rzv2h.h | 5 +++ 2 files changed, 45 insertions(+) diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 31c543c876b1..971ac83eee90 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -151,6 +151,12 @@ struct rzv2h_hw_info { #define ICU_DMAC_PREP_DMAREQ(sel, up) (FIELD_PREP(ICU_DMAC_DkRQ_SEL_MASK, (sel)) \ << ICU_DMAC_DMAREQ_SHIFT(up)) +/* DMAC ACK routing - 4 x 7-bit fields per 32-bit register, 8-bit spacing */ +#define ICU_DMAC_DACK_SEL_MASK GENMASK(6, 0) +#define ICU_DMAC_DACK_SHIFT(n) ((n) * 8) +#define ICU_DMAC_DACK_FIELD_MASK(n) (ICU_DMAC_DACK_SEL_MASK << ICU_DMAC_DACK_SHIFT(n)) +#define ICU_DMAC_PREP_DACK(val, n) (((val) & ICU_DMAC_DACK_SEL_MASK) << ICU_DMAC_DACK_SHIFT(n)) + /** * struct rzv2h_icu_priv - Interrupt Control Unit controller private data structure. * @base: Controller's base address @@ -188,6 +194,40 @@ void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, } EXPORT_SYMBOL_GPL(rzv2h_icu_register_dma_req); +/** + * rzv2h_icu_register_dma_ack - Configure DMA ACK signal routing + * @icu_dev: ICU platform device + * @dmac_index: DMAC instance index (0-4) + * @dmac_channel: DMAC channel number (0-15), or RZV2H_ICU_DMAC_ACK_NO_DEFAULT + * to disconnect routing for a given ack_no + * @ack_no: Peripheral ACK number (0-88) per RZ/G3E manual Table 4.6-28, + * used as index into ICU_DMACKSELk + * + * Routes the ACK signal of the peripheral identified by @ack_no to DMAC + * channel @dmac_channel of instance @dmac_index. When @dmac_channel is + * RZV2H_ICU_DMAC_ACK_NO_DEFAULT the field is reset, disconnecting any + * previously configured routing for that peripheral. + */ +void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index, + u8 dmac_channel, u16 ack_no) +{ + struct rzv2h_icu_priv *priv = platform_get_drvdata(icu_dev); + u8 reg_idx = ack_no / 4; + u8 field_idx = ack_no & 0x3; + u8 dmac_ack_src = (dmac_channel == RZV2H_ICU_DMAC_ACK_NO_DEFAULT) ? + RZV2H_ICU_DMAC_ACK_NO_DEFAULT : + (dmac_index * 16 + dmac_channel); + u32 val; + + guard(raw_spinlock_irqsave)(&priv->lock); + + val = readl(priv->base + ICU_DMACKSELk(reg_idx)); + val &= ~ICU_DMAC_DACK_FIELD_MASK(field_idx); + val |= ICU_DMAC_PREP_DACK(dmac_ack_src, field_idx); + writel(val, priv->base + ICU_DMACKSELk(reg_idx)); +} +EXPORT_SYMBOL_GPL(rzv2h_icu_register_dma_ack); + static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data) { return data->domain->host_data; diff --git a/include/linux/irqchip/irq-renesas-rzv2h.h b/include/linux/irqchip/irq-renesas-rzv2h.h index 618a60d2eac0..4ffa898eaaf2 100644 --- a/include/linux/irqchip/irq-renesas-rzv2h.h +++ b/include/linux/irqchip/irq-renesas-rzv2h.h @@ -11,13 +11,18 @@ #include #define RZV2H_ICU_DMAC_REQ_NO_DEFAULT 0x3ff +#define RZV2H_ICU_DMAC_ACK_NO_DEFAULT 0x7f #ifdef CONFIG_RENESAS_RZV2H_ICU void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, u16 req_no); +void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index, + u8 dmac_channel, u16 ack_no); #else static inline void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, u16 req_no) { } +static inline void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index, + u8 dmac_channel, u16 ack_no) { } #endif #endif /* __LINUX_IRQ_RENESAS_RZV2H */ From c0a207898fca8cbb4fad0da1e950d477b6afbf64 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Mon, 25 May 2026 11:07:50 +0000 Subject: [PATCH 3075/5214] dmaengine: sh: rz-dmac: Add DMA ACK signal routing support Some peripherals on RZ/G3E SoCs (SSIU, SPDIF, SCU/SRC, DVC, PFC) require explicit ACK signal routing through the ICU for level-based DMA handshaking. Rather than extending the DT binding with an optional second #dma-cells (which would require all DMA consumers to supply two cells even when ACK routing is not needed), derive the ACK signal number directly from the MID/RID request number using the linear mapping defined in RZ/G3E hardware manual Table 4.6-28: PFC external DMA pins (DREQ0..DREQ4): req_no 0x000-0x004 -> ACK No. 84-88 SSIU BUSIFs (ssip00..ssip93): req_no 0x161-0x198 -> ACK No. 28-83 SPDIF (CH0..CH2) + SCU SRC (sr0..sr9) + DVC (cmd0..cmd1): req_no 0x199-0x1b4 -> ACK No. 0-27 ACK routing is programmed when a channel is prepared for transfer and cleared when the channel is released or the transfer times out, following the same pattern as MID/RID request routing. Signed-off-by: John Madieu Link: https://patch.msgid.link/20260525110750.4020112-3-john.madieu.xa@bp.renesas.com [fixes subsystem name tag] Signed-off-by: Vinod Koul --- drivers/dma/sh/rz-dmac.c | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index f1174d25da84..ca76f1bb45c4 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -93,6 +93,7 @@ struct rz_dmac_chan { u32 chcfg; u32 chctrl; int mid_rid; + int dmac_ack; struct { u32 nxla; @@ -118,6 +119,9 @@ struct rz_dmac_icu { struct rz_dmac_info { void (*icu_register_dma_req)(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, u16 req_no); + void (*icu_register_dma_ack)(struct platform_device *icu_dev, + u8 dmac_index, u8 dmac_channel, u16 ack_no); + u16 default_dma_ack_no; u16 default_dma_req_no; }; @@ -366,6 +370,60 @@ static void rz_dmac_set_dma_req_no(struct rz_dmac *dmac, unsigned int index, rz_dmac_set_dmars_register(dmac, index, req_no); } +/* + * Map MID/RID request number (bits[0:9] of DMA specifier) to the ICU + * DMA ACK signal number, per RZ/G3E hardware manual Table 4.6-28. + * + * Three peripheral groups cover all ACK-capable peripherals: + * + * PFC external DMA pins (DREQ0..DREQ4): + * req_no 0x000-0x004 -> ACK No. 84-88 (ack = req_no + 84) + * + * SSIU BUSIFs (ssip00..ssip93): + * req_no 0x161-0x198 -> ACK No. 28-83 (ack = req_no - 0x145) + * + * SPDIF (CH0..CH2) + SCU SRC (sr0..sr9) + DVC (cmd0..cmd1): + * req_no 0x199-0x1b4 -> ACK No. 0-27 (ack = req_no - 0x199) + */ +static int rz_dmac_get_ack_no(const struct rz_dmac_info *info, u16 req_no) +{ + if (!info->icu_register_dma_ack) + return -EINVAL; + + switch (req_no) { + case 0x000 ... 0x004: + /* PFC external DMA pins: ACK No. 84-88 */ + return req_no + 84; + case 0x161 ... 0x198: + /* SSIU BUSIFs: ACK No. 28-83 */ + return req_no - 0x145; + case 0x199 ... 0x1b4: + /* SPDIF + SCU SRC + DVC: ACK No. 0-27 */ + return req_no - 0x199; + default: + return -EINVAL; + } +} + +static void rz_dmac_set_dma_ack_no(struct rz_dmac *dmac, unsigned int index, + int ack_no) +{ + if (ack_no < 0 || !dmac->info->icu_register_dma_ack) + return; + + dmac->info->icu_register_dma_ack(dmac->icu.pdev, dmac->icu.dmac_index, + index, ack_no); +} + +static void rz_dmac_reset_dma_ack_no(struct rz_dmac *dmac, int ack_no) +{ + if (ack_no < 0 || !dmac->info->icu_register_dma_ack) + return; + + dmac->info->icu_register_dma_ack(dmac->icu.pdev, dmac->icu.dmac_index, + dmac->info->default_dma_ack_no, ack_no); +} + static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; @@ -438,6 +496,7 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) channel->lmdesc.tail = lmdesc; rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); + rz_dmac_set_dma_ack_no(dmac, channel->index, channel->dmac_ack); channel->chctrl = 0; } @@ -491,6 +550,7 @@ static void rz_dmac_prepare_descs_for_cyclic(struct rz_dmac_chan *channel) channel->lmdesc.tail = lmdesc; rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); + rz_dmac_set_dma_ack_no(dmac, channel->index, channel->dmac_ack); } static void rz_dmac_xfer_desc(struct rz_dmac_chan *chan) @@ -579,6 +639,8 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) } channel->status = 0; + rz_dmac_reset_dma_ack_no(dmac, channel->dmac_ack); + channel->dmac_ack = -EINVAL; spin_unlock_irqrestore(&channel->vc.lock, flags); @@ -853,6 +915,7 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan) dev_warn(dmac->dev, "DMA Timeout"); rz_dmac_set_dma_req_no(dmac, channel->index, dmac->info->default_dma_req_no); + rz_dmac_reset_dma_ack_no(dmac, channel->dmac_ack); } static struct rz_lmdesc * @@ -1190,6 +1253,8 @@ static bool rz_dmac_chan_filter(struct dma_chan *chan, void *arg) channel->chcfg = CHCFG_FILL_TM(ch_cfg) | CHCFG_FILL_AM(ch_cfg) | CHCFG_FILL_LVL(ch_cfg) | CHCFG_FILL_HIEN(ch_cfg); + channel->dmac_ack = rz_dmac_get_ack_no(dmac->info, channel->mid_rid); + return !test_and_set_bit(channel->mid_rid, dmac->modules); } @@ -1226,6 +1291,7 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->index = index; channel->mid_rid = -EINVAL; + channel->dmac_ack = -EINVAL; /* Set io base address for each channel */ if (index < 8) { @@ -1572,6 +1638,7 @@ static int rz_dmac_resume(struct device *dev) continue; rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); + rz_dmac_set_dma_ack_no(dmac, channel->index, channel->dmac_ack); rz_dmac_ch_writel(channel, channel->pm_state.nxla, NXLA, 1); rz_dmac_ch_writel(channel, channel->chcfg, CHCFG, 1); @@ -1593,6 +1660,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(rz_dmac_pm_ops, rz_dmac_suspend, rz_dmac_resume) static const struct rz_dmac_info rz_dmac_v2h_info = { .icu_register_dma_req = rzv2h_icu_register_dma_req, + .icu_register_dma_ack = rzv2h_icu_register_dma_ack, + .default_dma_ack_no = RZV2H_ICU_DMAC_ACK_NO_DEFAULT, .default_dma_req_no = RZV2H_ICU_DMAC_REQ_NO_DEFAULT, }; From 11d7cfe0c119691b2dafbb699bbca90258c678aa Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 21 May 2026 23:21:51 +0900 Subject: [PATCH 3076/5214] dmaengine: dw-edma-pcie: Reject devices without driver data dw_edma_pcie_probe() treats the PCI device ID driver_data as the template for the controller layout and copies it unconditionally. A device bound dynamically via sysfs can match the driver without that data, which leads to a NULL pointer dereference. Reject such matches before enabling the device. Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260521142153.2957432-3-den@valinux.co.jp Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-pcie.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 2082d0021a8d..791c46e8ae4c 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -328,6 +328,9 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, int i, mask; bool non_ll = false; + if (!pdata) + return -ENODEV; + struct dw_edma_pcie_data *vsec_data __free(kfree) = kmalloc_obj(*vsec_data); if (!vsec_data) From 8ffba0171c6bbce5f093c6dba5a02c0805b31203 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 21 May 2026 23:21:53 +0900 Subject: [PATCH 3077/5214] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA channels, and modifying them requires a read-modify-write sequence. Because this operation is not atomic, concurrent calls to dw_edma_v0_core_start() can introduce race conditions if two channels update these registers simultaneously. Add a spinlock to serialize access to these registers and prevent race conditions. Fixes: 7e4b8a4fbe2c ("dmaengine: Add Synopsys eDMA IP version 0 support") Cc: stable@vger.kernel.org Signed-off-by: Frank Li [den: update dw_edma.lock comment] Link: https://lore.kernel.org/dmaengine/20260109-edma_ll-v2-1-5c0b27b2c664@nxp.com/ Signed-off-by: Koichiro Den Link: https://patch.msgid.link/20260521142153.2957432-5-den@valinux.co.jp Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-core.h | 2 +- drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 902574b1ba86..6474cacf7195 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -109,7 +109,7 @@ struct dw_edma { struct dw_edma_chan *chan; - raw_spinlock_t lock; /* Only for legacy */ + raw_spinlock_t lock; /* Protect v0 shared registers */ struct dw_edma_chip *chip; diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 69e8279adec8..cfdd6463252e 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; + unsigned long flags; u32 tmp; dw_edma_v0_core_write_chunk(chunk); @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) } } /* Interrupt unmask - done, abort */ + raw_spin_lock_irqsave(&dw->lock, flags); + tmp = GET_RW_32(dw, chan->dir, int_mask); tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + + raw_spin_unlock_irqrestore(&dw->lock, flags); + /* Channel control */ SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); From 92f853f0645aebf1d05d333e97ab7c342ace1892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:14 +0100 Subject: [PATCH 3078/5214] dmaengine: Fix possible use after free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In dma_release_channel(), check chan->device->privatecnt after call dma_chan_put(). However, dma_chan_put() call dma_device_put() which could release the last reference of the device if the DMA provider is already gone and hence free it. Fixes it by moving dma_chan_put() after the check. Fixes: 0f571515c332 ("dmaengine: Add privatecnt to revert DMA_PRIVATE property") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-1-90f43412fdc0@analog.com Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 405bd2fbb4a3..9049171df857 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -905,11 +905,12 @@ void dma_release_channel(struct dma_chan *chan) mutex_lock(&dma_list_mutex); WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); - dma_chan_put(chan); /* drop PRIVATE cap enabled by __dma_request_channel() */ if (--chan->device->privatecnt == 0) dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask); + dma_chan_put(chan); + if (chan->slave) { sysfs_remove_link(&chan->dev->device.kobj, DMA_SLAVE_NAME); sysfs_remove_link(&chan->slave->kobj, chan->name); From 4910ce1b3b35687bb2a5e742c4bfbea3c647c980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:15 +0100 Subject: [PATCH 3079/5214] dmaengine: dma-axi-dmac: Properly free struct axi_dmac_desc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use axi_dmac_free_desc() to free fully the descriptor at fail path when call axi_dmac_alloc_desc() in axi_dmac_prep_peripheral_dma_vec(). Fixes: 74609e568670 ("dmaengine: dma-axi-dmac: Implement device_prep_peripheral_dma_vec") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-2-90f43412fdc0@analog.com Signed-off-by: Vinod Koul --- drivers/dma/dma-axi-dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 45c2c8e4bc45..127c3cf80a0e 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -769,7 +769,7 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs, for (i = 0; i < nb; i++) { if (!axi_dmac_check_addr(chan, vecs[i].addr) || !axi_dmac_check_len(chan, vecs[i].len)) { - kfree(desc); + axi_dmac_free_desc(desc); return NULL; } From a725ac2055271fe8123fa854bfeff6e349f7cf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:16 +0100 Subject: [PATCH 3080/5214] dmaengine: dma-axi-dmac: Drop struct clk from main struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no reason to keep struct clk in struct axi_dmac. Hence, use a local clk variable in .probe() and drop it from struct axi_dmac. Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-3-90f43412fdc0@analog.com Signed-off-by: Vinod Koul --- drivers/dma/dma-axi-dmac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 127c3cf80a0e..41898d594be7 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -170,8 +170,6 @@ struct axi_dmac { void __iomem *base; int irq; - struct clk *clk; - struct dma_device dma_dev; struct axi_dmac_chan chan; }; @@ -1198,6 +1196,7 @@ static int axi_dmac_probe(struct platform_device *pdev) { struct dma_device *dma_dev; struct axi_dmac *dmac; + struct clk *clk; struct regmap *regmap; unsigned int version; u32 irq_mask = 0; @@ -1217,9 +1216,9 @@ static int axi_dmac_probe(struct platform_device *pdev) if (IS_ERR(dmac->base)) return PTR_ERR(dmac->base); - dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(dmac->clk)) - return PTR_ERR(dmac->clk); + clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION); From 9e942c8579130e62734c14338e9f451780669164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:17 +0100 Subject: [PATCH 3081/5214] dmaengine: dma-axi-dmac: use DMA pool to manange DMA descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For architectures like Microblaze or arm64 (where this IP is used), DMA_DIRECT_REMAP is set which means that dma_alloc_coherent() might remap (and hence vmalloc()) some memory. This became visible in a design where dma_direct_use_pool() is not possible. With the above, when calling dma_free_coherent(), vunmap() would be called from softirq context and thus leading to a BUG(). To fix it, use a dma pool that is allocated in .device_alloc_chan_resources() and allocate blocks from it. The key point is that now dma_pool_free() is used in axi_dmac_free_desc() to free the blocks and that just frees the blocks from the pool in the sense they can be used again. In other words, no actual call to dma_free_coherent() happens. That only happens when destroying the pool in axi_dmac_free_chan_resources() which does not happen in any interrupt context. Fixes: 3f8fd25936ee ("dmaengine: axi-dmac: Allocate hardware descriptors") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-4-90f43412fdc0@analog.com Signed-off-by: Vinod Koul --- drivers/dma/dma-axi-dmac.c | 66 +++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 41898d594be7..d47ff27e1408 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,7 @@ struct axi_dmac_chan { struct virt_dma_chan vchan; struct axi_dmac_desc *next_desc; + void *pool; struct list_head active_descs; enum dma_transfer_direction direction; @@ -648,11 +650,17 @@ static void axi_dmac_issue_pending(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } +static void axi_dmac_free_desc(struct axi_dmac_desc *desc) +{ + for (unsigned int i = 0; i < desc->num_sgs; i++) + dma_pool_free(desc->chan->pool, desc->sg[i].hw, desc->sg[i].hw_phys); + + kfree(desc); +} + static struct axi_dmac_desc * axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) { - struct axi_dmac *dmac = chan_to_axi_dmac(chan); - struct device *dev = dmac->dma_dev.dev; struct axi_dmac_hw_desc *hws; struct axi_dmac_desc *desc; dma_addr_t hw_phys; @@ -664,22 +672,22 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) desc->num_sgs = num_sgs; desc->chan = chan; - hws = dma_alloc_coherent(dev, PAGE_ALIGN(num_sgs * sizeof(*hws)), - &hw_phys, GFP_ATOMIC); - if (!hws) { - kfree(desc); - return NULL; - } - for (i = 0; i < num_sgs; i++) { - desc->sg[i].hw = &hws[i]; - desc->sg[i].hw_phys = hw_phys + i * sizeof(*hws); + hws = dma_pool_zalloc(chan->pool, GFP_NOWAIT, &hw_phys); + if (!hws) { + desc->num_sgs = i; + axi_dmac_free_desc(desc); + return NULL; + } - hws[i].id = AXI_DMAC_SG_UNUSED; - hws[i].flags = 0; + desc->sg[i].hw = hws; + desc->sg[i].hw_phys = hw_phys; + + hws->id = AXI_DMAC_SG_UNUSED; /* Link hardware descriptors */ - hws[i].next_sg_addr = hw_phys + (i + 1) * sizeof(*hws); + if (i) + desc->sg[i - 1].hw->next_sg_addr = hw_phys; } /* The last hardware descriptor will trigger an interrupt */ @@ -688,18 +696,6 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) return desc; } -static void axi_dmac_free_desc(struct axi_dmac_desc *desc) -{ - struct axi_dmac *dmac = chan_to_axi_dmac(desc->chan); - struct device *dev = dmac->dma_dev.dev; - struct axi_dmac_hw_desc *hw = desc->sg[0].hw; - dma_addr_t hw_phys = desc->sg[0].hw_phys; - - dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), - hw, hw_phys); - kfree(desc); -} - static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, enum dma_transfer_direction direction, dma_addr_t addr, unsigned int num_periods, unsigned int period_len, @@ -933,9 +929,26 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } +static int axi_dmac_alloc_chan_resources(struct dma_chan *c) +{ + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + struct device *dev = c->device->dev; + + chan->pool = dma_pool_create(dev_name(dev), dev, + sizeof(struct axi_dmac_hw_desc), + __alignof__(struct axi_dmac_hw_desc), 0); + if (!chan->pool) + return -ENOMEM; + + return 0; +} + static void axi_dmac_free_chan_resources(struct dma_chan *c) { + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + vchan_free_chan_resources(to_virt_chan(c)); + dma_pool_destroy(chan->pool); } static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) @@ -1238,6 +1251,7 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask); dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask); + dma_dev->device_alloc_chan_resources = axi_dmac_alloc_chan_resources; dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources; dma_dev->device_tx_status = dma_cookie_status; dma_dev->device_issue_pending = axi_dmac_issue_pending; From b2d44b3ea95e10315559d8deedd8af2977c7f534 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 14 Jan 2026 07:14:58 +0200 Subject: [PATCH 3082/5214] dmaengine: iop32x-adma: Remove a leftover header file The Intel IOPx3xx platform was completely removed in commit b91a69d162aa ("ARM: iop32x: remove the platform"), and it'd be safe to remove an unused and leftover platform data specific header file dma-iop32x.h also. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Frank Li Link: https://patch.msgid.link/20260114051508.3908807-1-vz@mleia.com [vkoul: fixed subsystem tag] Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-iop32x.h | 110 ----------------------- 1 file changed, 110 deletions(-) delete mode 100644 include/linux/platform_data/dma-iop32x.h diff --git a/include/linux/platform_data/dma-iop32x.h b/include/linux/platform_data/dma-iop32x.h deleted file mode 100644 index ac83cff89549..000000000000 --- a/include/linux/platform_data/dma-iop32x.h +++ /dev/null @@ -1,110 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright © 2006, Intel Corporation. - */ -#ifndef IOP_ADMA_H -#define IOP_ADMA_H -#include -#include -#include - -#define IOP_ADMA_SLOT_SIZE 32 -#define IOP_ADMA_THRESHOLD 4 -#ifdef DEBUG -#define IOP_PARANOIA 1 -#else -#define IOP_PARANOIA 0 -#endif -#define iop_paranoia(x) BUG_ON(IOP_PARANOIA && (x)) - -#define DMA0_ID 0 -#define DMA1_ID 1 -#define AAU_ID 2 - -/** - * struct iop_adma_device - internal representation of an ADMA device - * @pdev: Platform device - * @id: HW ADMA Device selector - * @dma_desc_pool: base of DMA descriptor region (DMA address) - * @dma_desc_pool_virt: base of DMA descriptor region (CPU address) - * @common: embedded struct dma_device - */ -struct iop_adma_device { - struct platform_device *pdev; - int id; - dma_addr_t dma_desc_pool; - void *dma_desc_pool_virt; - struct dma_device common; -}; - -/** - * struct iop_adma_chan - internal representation of an ADMA device - * @pending: allows batching of hardware operations - * @lock: serializes enqueue/dequeue operations to the slot pool - * @mmr_base: memory mapped register base - * @chain: device chain view of the descriptors - * @device: parent device - * @common: common dmaengine channel object members - * @last_used: place holder for allocation to continue from where it left off - * @all_slots: complete domain of slots usable by the channel - * @slots_allocated: records the actual size of the descriptor slot pool - * @irq_tasklet: bottom half where iop_adma_slot_cleanup runs - */ -struct iop_adma_chan { - int pending; - spinlock_t lock; /* protects the descriptor slot pool */ - void __iomem *mmr_base; - struct list_head chain; - struct iop_adma_device *device; - struct dma_chan common; - struct iop_adma_desc_slot *last_used; - struct list_head all_slots; - int slots_allocated; - struct tasklet_struct irq_tasklet; -}; - -/** - * struct iop_adma_desc_slot - IOP-ADMA software descriptor - * @slot_node: node on the iop_adma_chan.all_slots list - * @chain_node: node on the op_adma_chan.chain list - * @hw_desc: virtual address of the hardware descriptor chain - * @phys: hardware address of the hardware descriptor chain - * @group_head: first operation in a transaction - * @slot_cnt: total slots used in an transaction (group of operations) - * @slots_per_op: number of slots per operation - * @idx: pool index - * @tx_list: list of descriptors that are associated with one operation - * @async_tx: support for the async_tx api - * @group_list: list of slots that make up a multi-descriptor transaction - * for example transfer lengths larger than the supported hw max - * @xor_check_result: result of zero sum - * @crc32_result: result crc calculation - */ -struct iop_adma_desc_slot { - struct list_head slot_node; - struct list_head chain_node; - void *hw_desc; - struct iop_adma_desc_slot *group_head; - u16 slot_cnt; - u16 slots_per_op; - u16 idx; - struct list_head tx_list; - struct dma_async_tx_descriptor async_tx; - union { - u32 *xor_check_result; - u32 *crc32_result; - u32 *pq_check_result; - }; -}; - -struct iop_adma_platform_data { - int hw_id; - dma_cap_mask_t cap_mask; - size_t pool_size; -}; - -#define to_iop_sw_desc(addr_hw_desc) \ - container_of(addr_hw_desc, struct iop_adma_desc_slot, hw_desc) -#define iop_hw_desc_slot_idx(hw_desc, idx) \ - ( (void *) (((unsigned long) hw_desc) + ((idx) << 5)) ) -#endif From 4651df83b6c796daead3447e8fd874322918ee4f Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Wed, 22 Apr 2026 12:11:34 +0530 Subject: [PATCH 3083/5214] dmaengine: tegra: Fix burst size calculation Currently, the Tegra GPC DMA hardware requires the transfer length to be a multiple of the max burst size configured for the channel. When a client requests a transfer where the length is not evenly divisible by the configured max burst size, the DMA hangs with partial burst at the end. Fix this by reducing the burst size to the largest power-of-2 value that evenly divides the transfer length. For example, a 40-byte transfer with a 16-byte max burst will now use an 8-byte burst (40 / 8 = 5 complete bursts) instead of causing a hang. This issue was observed with the PL011 UART driver where TX DMA transfers of arbitrary lengths were stuck. Fixes: ee17028009d4 ("dmaengine: tegra: Add tegra gpcdma driver") Cc: stable@vger.kernel.org Signed-off-by: Kartik Rajput Reviewed-by: Frank Li Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260422064134.1323610-1-kkartik@nvidia.com Signed-off-by: Vinod Koul --- drivers/dma/tegra186-gpc-dma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index c364c278a8b0..64cedef1050a 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -835,6 +835,13 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc, * len to calculate the optimum burst size */ burst_byte = burst_size ? burst_size * slave_bw : len; + + /* + * Find the largest burst size that evenly divides the transfer length. + * The hardware requires the transfer length to be a multiple of the + * burst size - partial bursts are not supported. + */ + burst_byte = min(burst_byte, 1U << __ffs(len)); burst_mmio_width = burst_byte / 4; if (burst_mmio_width < TEGRA_GPCDMA_MMIOSEQ_BURST_MIN) From 30b1f763bfc1b0db42f58904ec378066cc886c44 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 15 May 2026 17:15:55 -0500 Subject: [PATCH 3084/5214] firmware: stratix10-rsu: avoid blocking reboot_image sysfs when busy Writes to the reboot_image sysfs attribute went through rsu_send_msg(), which unconditionally takes priv->lock with mutex_lock(). If another RSU operation is in flight (e.g. a DCMF status query from probe or a concurrent sysfs read path), userspace writers get stuck in the kernel waiting on the mutex instead of being told the device is busy. Split rsu_send_msg() into an inner __rsu_send_msg_locked() helper that performs the SMC transaction with the caller holding priv->lock, plus two thin wrappers: rsu_send_msg() preserves the original blocking behaviour for existing callers, and rsu_try_send_msg() uses mutex_trylock() and returns -EBUSY immediately when the lock is held. Use rsu_try_send_msg() from reboot_image_store() so the write returns -EBUSY without blocking when an RSU operation is already running. Userspace can retry on -EBUSY. No functional change for other sysfs attributes. This keeps blocking rsu_send_msg() for existing callers, add rsu_try_send_msg() with -EBUSY only for reboot_image_store(). That matches the original goal (avoid a second reboot_image write blocking behind priv->lock) without changing sysfs behaviour for the other attributes. The earlier idea of using mutex_trylock() in all of rsu_send_msg() and returning -EAGAIN would have been harder to justify for userspace (echo does not retry on that). Tze Yee tested the patch on an Agilex SoC devkit. [Test 1] Idle reboot_image write (success path) Result: # insmod stratix10-rsu.ko # echo 0x01000000 > .../reboot_image # echo "exit=$?" exit=0 # ./rsu_client --log VERSION: 0x00000202 STATE: 0x00000000 CURRENT IMAGE: 0x0000000001000000 FAIL IMAGE: 0x0000000000000000 ERROR LOC: 0x00000000 ERROR DETAILS: 0x00000000 RETRY COUNTER: 0x00000000 Operation completed [Test 2] reboot_image while priv->lock is held (-EBUSY path) To get a deterministic busy window without flooding the service layer, add a local debug helper (module parameter debug_hold_lock_sec + kthread that holds priv->lock for N seconds after probe). Result: # insmod stratix10-rsu.ko debug_hold_lock_sec=60 [ 121.220904] stratix10-rsu stratix10-rsu.0: TEST: RSU lock held for 60 s - try reboot_image now # echo 0x01000000 > .../reboot_image -sh: echo: write error: Device or resource busy # echo "during hold: exit=$?" during hold: exit=1 [ 183.268706] stratix10-rsu stratix10-rsu.0: TEST: RSU lock released # echo 0x01000000 > .../reboot_image # echo "after release: exit=$?" after release: exit=0 Together, these results match the intended behaviour: reboot_image fails fast with -EBUSY when the RSU mutex is already held, and succeeds once the lock is available. Assisted-by: Claude:claude-opus-4-7 Tested-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-rsu.c | 85 +++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index e1912108a0fe..018a933943fb 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -244,27 +244,26 @@ static void rsu_async_get_spt_table_callback(struct device *dev, } /** - * rsu_send_msg() - send a message to Intel service layer + * __rsu_send_msg_locked() - send a message to Intel service layer * @priv: pointer to rsu private data * @command: RSU status or update command * @arg: the request argument, the bitstream address or notify status * @callback: function pointer for the callback (status or update) * - * Start an Intel service layer transaction to perform the SMC call that - * is necessary to get RSU boot log or set the address of bitstream to - * boot after reboot. + * Perform the actual SMC transaction. The caller must hold @priv->lock. * - * Returns 0 on success or -ETIMEDOUT on error. + * Returns 0 on success or a negative errno on failure. */ -static int rsu_send_msg(struct stratix10_rsu_priv *priv, - enum stratix10_svc_command_code command, - unsigned long arg, - rsu_callback callback) +static int __rsu_send_msg_locked(struct stratix10_rsu_priv *priv, + enum stratix10_svc_command_code command, + unsigned long arg, + rsu_callback callback) { struct stratix10_svc_client_msg msg; int ret; - mutex_lock(&priv->lock); + lockdep_assert_held(&priv->lock); + reinit_completion(&priv->completion); priv->client.receive_cb = callback; @@ -293,6 +292,59 @@ static int rsu_send_msg(struct stratix10_rsu_priv *priv, status_done: stratix10_svc_done(priv->chan); + return ret; +} + +/** + * rsu_send_msg() - send a message to Intel service layer + * @priv: pointer to rsu private data + * @command: RSU status or update command + * @arg: the request argument, the bitstream address or notify status + * @callback: function pointer for the callback (status or update) + * + * Start an Intel service layer transaction to perform the SMC call that + * is necessary to get RSU boot log or set the address of bitstream to + * boot after reboot. This call will block until the RSU lock can be + * acquired. + * + * Returns 0 on success or a negative errno on failure. + */ +static int rsu_send_msg(struct stratix10_rsu_priv *priv, + enum stratix10_svc_command_code command, + unsigned long arg, + rsu_callback callback) +{ + int ret; + + mutex_lock(&priv->lock); + ret = __rsu_send_msg_locked(priv, command, arg, callback); + mutex_unlock(&priv->lock); + return ret; +} + +/** + * rsu_try_send_msg() - non-blocking variant of rsu_send_msg() + * @priv: pointer to rsu private data + * @command: RSU status or update command + * @arg: the request argument, the bitstream address or notify status + * @callback: function pointer for the callback (status or update) + * + * Same as rsu_send_msg() but returns -EBUSY immediately when another + * RSU operation is already in flight, instead of waiting for the lock. + * + * Returns 0 on success, -EBUSY if the RSU is busy, or another negative + * errno on failure. + */ +static int rsu_try_send_msg(struct stratix10_rsu_priv *priv, + enum stratix10_svc_command_code command, + unsigned long arg, + rsu_callback callback) +{ + int ret; + + if (!mutex_trylock(&priv->lock)) + return -EBUSY; + ret = __rsu_send_msg_locked(priv, command, arg, callback); mutex_unlock(&priv->lock); return ret; } @@ -595,8 +647,17 @@ static ssize_t reboot_image_store(struct device *dev, if (ret) return ret; - ret = rsu_send_msg(priv, COMMAND_RSU_UPDATE, - address, rsu_command_callback); + /* + * Use the non-blocking variant so a write to this sysfs attribute + * does not stall the caller while another RSU operation is in + * flight. Userspace can retry on -EBUSY. + */ + ret = rsu_try_send_msg(priv, COMMAND_RSU_UPDATE, + address, rsu_command_callback); + if (ret == -EBUSY) { + dev_dbg(dev, "RSU busy, reboot_image write rejected\n"); + return ret; + } if (ret) { dev_err(dev, "Error, RSU update returned %i\n", ret); return ret; From 71e6a19fe2669faf7dcb96ba8a4fde927485e60e Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Tue, 2 Jun 2026 22:28:42 -0700 Subject: [PATCH 3085/5214] firmware: stratix10-svc: Add support to query Arm Trusted Firmware (ATF) version Add entry in Stratix10 service layer that allow client to retrieve the ATF version at runtime, which is useful for system diagnostics, compatibility checks, and ensuring the correct secure firmware is in use. The change introduces: - A new service command definition in the Stratix10 service layer to initiate the ATF version query. - A corresponding macro definition in the header file to expose the command ID for use by other components. The service layer uses a Secure Monitor Call (SMC) to communicate with the ATF and retrieve the version string, which can then be logged or validated by client application. Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 12 ++++++++++++ include/linux/firmware/intel/stratix10-smc.h | 19 +++++++++++++++++++ .../firmware/intel/stratix10-svc-client.h | 7 ++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 282211d2612b..00e134e663c8 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -486,6 +486,12 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, cb_data->kaddr1 = &res.a1; cb_data->kaddr2 = &res.a2; break; + case COMMAND_SMC_ATF_BUILD_VER: + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = &res.a1; + cb_data->kaddr2 = &res.a2; + cb_data->kaddr3 = &res.a3; + break; case COMMAND_RSU_DCMF_VERSION: cb_data->status = BIT(SVC_STATUS_OK); cb_data->kaddr1 = &res.a1; @@ -704,6 +710,12 @@ static int svc_normal_to_secure_thread(void *data) a1 = 0; a2 = 0; break; + case COMMAND_SMC_ATF_BUILD_VER: + a0 = INTEL_SIP_SMC_ATF_BUILD_VER; + a1 = 0; + a2 = 0; + a3 = 0; + break; case COMMAND_MBOX_SEND_CMD: a0 = INTEL_SIP_SMC_MBOX_SEND_CMD; a1 = pdata->arg[0]; diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 36ea619ea2ad..9116512169dc 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -514,6 +514,25 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_SVC_VERSION \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_SVC_FUNCID_VERSION) +/** + * Request INTEL_SIP_SMC_ATF_BUILD_VER + * + * Sync call used to query the ATF Build Version + * + * Call register usage: + * a0 INTEL_SIP_SMC_ATF_BUILD_VER + * a1-a7 not used + * + * Return status: + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 Major + * a2 Minor + * a3 Patch + */ +#define INTEL_SIP_SMC_ATF_BUILD_VERSION 155 +#define INTEL_SIP_SMC_ATF_BUILD_VER \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_ATF_BUILD_VERSION) + /** * SMC call protocol for FPGA Crypto Service (FCS) * FUNCID starts from 90 diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 91013161e9db..3edd93502bf8 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -154,6 +154,10 @@ struct stratix10_svc_chan; * * @COMMAND_HWMON_READVOLT: query the voltage from the hardware monitor, * return status is SVC_STATUS_OK or SVC_STATUS_ERROR + * + * @COMMAND_SMC_ATF_BUILD_VER: Non-mailbox SMC ATF Build Version, + * return status is SVC_STATUS_OK + * */ enum stratix10_svc_command_code { /* for FPGA */ @@ -187,7 +191,8 @@ enum stratix10_svc_command_code { COMMAND_SMC_SVC_VERSION = 200, /* for HWMON */ COMMAND_HWMON_READTEMP, - COMMAND_HWMON_READVOLT + COMMAND_HWMON_READVOLT, + COMMAND_SMC_ATF_BUILD_VER }; /** From 7d19e1ffee084c4f7d321a360c14ba43404f7cc8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 3086/5214] ntfs: validate resident attribute lists and harden the validator A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list() only on the non-resident path; ntfs_read_locked_inode() copies a *resident* attribute list into ni->attr_list with a plain memcpy() and no validation at all. Every subsequent walk of ni->attr_list -- ntfs_external_attr_find(), ntfs_inode_attach_all_extents() and ntfs_attrlist_need() -- then trusts the entries are well-formed and reads attr_list_entry fixed-header fields (lowest_vcn at offset 8, mft_reference at offset 16, and the name) with bounds that assume validation already happened. A crafted resident attribute list therefore reaches those walks unvalidated and can drive out-of-bounds reads of the attribute-list buffer. load_attribute_list() itself reads ale->name_offset (offset 7), ale->mft_reference (offset 16) and the name length under only an "al < al_start + size" bound, so its own validation loop can over-read the fixed header of a truncated trailing entry by a few bytes. Factor the per-entry validation into ntfs_attr_list_entry_is_valid(), which requires each entry's fixed header (offsetof(struct attr_list_entry, name)) to be in range before any field is dereferenced, that ale->length is a multiple of 8 covering the fixed header plus the name, and that the entry is in use and carries a live MFT reference. ntfs_attr_list_is_valid() walks the buffer with it and checks the entries tile it exactly. Use the list validator in load_attribute_list() (replacing the open-coded loop, closing its own over-read) and on the resident path in ntfs_read_locked_inode() (which previously skipped validation entirely); patches 2/3 reuse the per-entry helper at the other two attribute-list walks. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 78 ++++++++++++++++++++++++++++++++++++++---------- fs/ntfs/attrib.h | 4 +++ fs/ntfs/inode.c | 6 ++++ 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 5e1ad1cd0118..2d675bf99ca7 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -921,11 +921,71 @@ char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, return NULL; } +/* + * ntfs_attr_list_entry_is_valid - sanity check one $ATTRIBUTE_LIST entry + * @ale: the attribute-list entry to check + * @al_end: end of the attribute-list buffer @ale lives in + * + * Verify that @ale is a well-formed attr_list_entry wholly contained in + * [.., @al_end): its fixed header must lie in range before any field is + * dereferenced, its length must be a multiple of 8 that covers the fixed + * header plus the name, the name must lie within the buffer, the entry must + * be in use and carry a live MFT reference. Return true if valid. + */ +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end) +{ + const u8 *al = (const u8 *)ale; + u16 ale_len; + + /* The fixed header must be in bounds before it is parsed. */ + if (al + offsetof(struct attr_list_entry, name) > al_end) + return false; + ale_len = le16_to_cpu(ale->length); + /* On-disk entries are 8-byte aligned (see struct attr_list_entry). */ + if (ale_len & 7) + return false; + if (ale->name_offset != sizeof(struct attr_list_entry)) + return false; + if ((u32)ale->name_offset + + (u32)ale->name_length * sizeof(__le16) > ale_len || + al + ale_len > al_end) + return false; + if (ale->type == AT_UNUSED) + return false; + if (MSEQNO_LE(ale->mft_reference) == 0) + return false; + return true; +} + +/* + * ntfs_attr_list_is_valid - sanity check an in-memory $ATTRIBUTE_LIST + * @al_start: start of the attribute list buffer + * @size: length of the attribute list in bytes + * + * Verify that [@al_start, @al_start + @size) is a sequence of valid + * attr_list_entry records (see ntfs_attr_list_entry_is_valid()) that tile the + * buffer exactly. Return true if valid, false otherwise. + */ +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size) +{ + const u8 *al = al_start; + const u8 *al_end = al_start + size; + + while (al < al_end) { + const struct attr_list_entry *ale = + (const struct attr_list_entry *)al; + + if (!ntfs_attr_list_entry_is_valid(ale, al_end)) + return false; + al += le16_to_cpu(ale->length); + } + return al == al_end; +} + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size) { struct inode *attr_vi = NULL; - u8 *al; - struct attr_list_entry *ale; if (!al_start || size <= 0) return -EINVAL; @@ -947,19 +1007,7 @@ int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size } iput(attr_vi); - for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) { - ale = (struct attr_list_entry *)al; - if (ale->name_offset != sizeof(struct attr_list_entry)) - break; - if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length || - al + le16_to_cpu(ale->length) > al_start + size) - break; - if (ale->type == AT_UNUSED) - break; - if (MSEQNO_LE(ale->mft_reference) == 0) - break; - } - if (al != al_start + size) { + if (!ntfs_attr_list_is_valid(al_start, size)) { ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu", base_ni->mft_no); return -EIO; diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h index f7acc7986b09..e2224fbfaabe 100644 --- a/fs/ntfs/attrib.h +++ b/fs/ntfs/attrib.h @@ -71,6 +71,10 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name, const u32 name_len, const u32 ic, const s64 lowest_vcn, const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx); +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end); +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size); + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 9717fb5b4709..8a7798d7f5fc 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -848,6 +848,12 @@ static int ntfs_read_locked_inode(struct inode *vi) a->data.resident.value_offset), le32_to_cpu( a->data.resident.value_length)); + /* A resident list is not validated on load; check it now. */ + if (!ntfs_attr_list_is_valid(ni->attr_list, + ni->attr_list_size)) { + ntfs_error(vi->i_sb, "Corrupt attribute list."); + goto unm_err_out; + } } } skip_attr_list_load: From 344b18f389f9934d59c7b0cf3d20541ea2e0da58 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 3087/5214] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() When resolving an attribute lookup with a non-zero @lowest_vcn, ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to decide whether to keep searching, but bounds that not-yet-validated entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" with an attacker-controlled, non-8-aligned length. It then reads next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at next_al_entry->name_offset, both of which can lie past al_end -- the exact end of the kvmalloc'd attribute-list buffer (allocated at the on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST whose last entry sits a few bytes before al_end therefore yields a slab out-of-bounds read when the inode is read. Validate the look-ahead entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing lowest_vcn and the name, so the same fixed-header, length and name bounds the main attribute-list walk uses now guard this read too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 2d675bf99ca7..1e0076ef81ef 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1263,9 +1263,8 @@ static int ntfs_external_attr_find(const __le32 type, * we have reached the right one or the search has failed. */ if (lowest_vcn && (u8 *)next_al_entry >= al_start && - (u8 *)next_al_entry + 6 < al_end && - (u8 *)next_al_entry + le16_to_cpu( - next_al_entry->length) <= al_end && + ntfs_attr_list_entry_is_valid(next_al_entry, + al_end) && le64_to_cpu(next_al_entry->lowest_vcn) <= lowest_vcn && next_al_entry->type == al_entry->type && From 98634df5b1cb56c26299b7409227025ddb0167d8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 20:30:00 +0000 Subject: [PATCH 3088/5214] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() The $MFT attribute-list walk in ntfs_read_inode_mount() validates each entry only with "(u8 *)al_entry + 6 > al_end" and "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length is attacker-controlled and only required non-zero, so a short entry (e.g. length 8) placed at the tail passes both checks while the lowest_vcn / mft_reference reads fall past al_end. al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding usually absorbs the over-read -- but when attr_list_size is a multiple of SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an out-of-bounds read at mount time. Validate the entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing it, matching the bound the other attribute-list walks now use. The validator already requires the length to cover the fixed header, which makes the separate "!al_entry->length" check redundant, so drop it too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 8a7798d7f5fc..2f2634baa285 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1997,10 +1997,7 @@ int ntfs_read_inode_mount(struct inode *vi) /* Catch the end of the attribute list. */ if ((u8 *)al_entry == al_end) goto em_put_err_out; - if (!al_entry->length) - goto em_put_err_out; - if ((u8 *)al_entry + 6 > al_end || - (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) + if (!ntfs_attr_list_entry_is_valid(al_entry, al_end)) goto em_put_err_out; next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + le16_to_cpu(al_entry->length)); From 390936fb15053d8d8991ca3a22776e251a5a7f2f Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 8 Jun 2026 23:00:45 +0900 Subject: [PATCH 3089/5214] ntfs: fix u16 truncation of restart-area length check ntfs_check_restart_area() validates that the $LogFile restart area and its trailing log client record array fit within the system page size: u16 ra_ofs, ra_len, ca_ofs; ... ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * sizeof(struct log_client_record); if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || ...) return false; ra_len is u16, but the right-hand side is computed in size_t (sizeof(struct log_client_record) == 160). Both ca_ofs and log_clients come straight from the on-disk restart area. With an on-disk log_clients of 410 the product 410 * 160 = 65600; adding ca_ofs and storing into the u16 ra_len truncates modulo 65536 (e.g. ca_ofs 64 gives ra_len 128), so the "fits in the page" check passes even though the client array described by log_clients extends far beyond the page. ntfs_check_log_client_array() then walks the array bounded only by the on-disk log_clients count: cr = ca + idx; if (cr->prev_client != LOGFILE_NO_CLIENT) ... For log_clients 410 it dereferences records up to ca + 409 * 160, ~64 KiB past the kvzalloc(system_page_size) restart-page buffer -- an out-of-bounds read of attacker-controlled extent, reachable when a crafted NTFS image is mounted (load_and_check_logfile() at mount time). This is the in-kernel analogue of CVE-2022-30789, fixed in the ntfs-3g userspace driver but never in this revived classic driver. Compute the restart-area length in a u32 so the existing bounds check rejects an over-large client array instead of being defeated by the truncation. Widen ra_ofs and ca_ofs to u32 as well: both are loaded from __le16 on-disk fields and every comparison already promotes to int/size_t, so this changes no result and keeps the declaration uniform. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas Signed-off-by: Namjae Jeon --- fs/ntfs/logfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index 9df8c3095ca4..024ddee42dc8 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -132,7 +132,7 @@ static bool ntfs_check_restart_area(struct inode *vi, struct restart_page_header { u64 file_size; struct restart_area *ra; - u16 ra_ofs, ra_len, ca_ofs; + u32 ra_ofs, ra_len, ca_ofs; u8 fs_bits; ntfs_debug("Entering."); From bb56147ea9fce98ebde1d367335ba006cba61fbd Mon Sep 17 00:00:00 2001 From: Phillip Varney Date: Fri, 5 Jun 2026 00:55:45 +0000 Subject: [PATCH 3090/5214] clk: qcom: a53: Corrected frequency multiplier for 1152MHz The 1152MHz frequency entry for the a53 currently selects a multiplier of 62, giving 1190MHz. This changes the mulitiplier to 60 giving the intended 1152MHz. Signed-off-by: Phillip Varney Reviewed-by: Konrad Dybcio Fixes: 0c6ab1b8f894 ("clk: qcom: Add A53 PLL support") Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260605005502.313928-1-pbvarney@protonmail.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/a53-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c index 724a642311e5..0549b214fcfc 100644 --- a/drivers/clk/qcom/a53-pll.c +++ b/drivers/clk/qcom/a53-pll.c @@ -20,7 +20,7 @@ static const struct pll_freq_tbl a53pll_freq[] = { { 998400000, 52, 0x0, 0x1, 0 }, { 1094400000, 57, 0x0, 0x1, 0 }, - { 1152000000, 62, 0x0, 0x1, 0 }, + { 1152000000, 60, 0x0, 0x1, 0 }, { 1209600000, 63, 0x0, 0x1, 0 }, { 1248000000, 65, 0x0, 0x1, 0 }, { 1363200000, 71, 0x0, 0x1, 0 }, From e108373c54fbc844b7f541c6fd7ecb31772afd3c Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 9 Apr 2026 13:57:45 +0200 Subject: [PATCH 3091/5214] clk: qcom: regmap-phy-mux: Rework the implementation The sole reason this hw exists is to let the branch clock downstream of it keep running, with the PHY disengaged. This is not possible with the current implementation, as the enabled status is hijacked to mean "enabled" = "use fast/PHY source" and "disabled" = "use XO source". This is an issue, since the mux enable state follows that of the child branch, making the desired "child enabled, MUX @ XO" combination impossible. Solve that by implementing ratesetting. Because PHY clock rates may change at runtime and aren't really deterministic from Linux, assume ULONG_MAX as "fast clock" and 19.2 MHz as XO. All the branches in question already set CLK_SET_RATE_PARENT, so everything works out. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260409-topic-phy_fastclk-v1-1-6b4aaee56b90@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-regmap-phy-mux.c | 58 ++++++++++++++++++--------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/clk/qcom/clk-regmap-phy-mux.c b/drivers/clk/qcom/clk-regmap-phy-mux.c index 7b7243b7107d..b7d1c69d62f7 100644 --- a/drivers/clk/qcom/clk-regmap-phy-mux.c +++ b/drivers/clk/qcom/clk-regmap-phy-mux.c @@ -15,48 +15,66 @@ #define PHY_MUX_PHY_SRC 0 #define PHY_MUX_REF_SRC 2 +#define XO_RATE 19200000UL + static inline struct clk_regmap_phy_mux *to_clk_regmap_phy_mux(struct clk_regmap *clkr) { return container_of(clkr, struct clk_regmap_phy_mux, clkr); } -static int phy_mux_is_enabled(struct clk_hw *hw) +static unsigned long phy_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - unsigned int val; + u32 val; regmap_read(clkr->regmap, phy_mux->reg, &val); - val = FIELD_GET(PHY_MUX_MASK, val); - WARN_ON(val != PHY_MUX_PHY_SRC && val != PHY_MUX_REF_SRC); - - return val == PHY_MUX_PHY_SRC; + switch (FIELD_GET(PHY_MUX_MASK, val)) { + case PHY_MUX_PHY_SRC: + return ULONG_MAX; + case PHY_MUX_REF_SRC: + return XO_RATE; + default: + return 0; + } } -static int phy_mux_enable(struct clk_hw *hw) +static int phy_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + if (req->rate == XO_RATE || req->rate == ULONG_MAX) + return 0; + + return -EINVAL; +} + +static int phy_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); + u32 val; - return regmap_update_bits(clkr->regmap, phy_mux->reg, - PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, PHY_MUX_PHY_SRC)); -} - -static void phy_mux_disable(struct clk_hw *hw) -{ - struct clk_regmap *clkr = to_clk_regmap(hw); - struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); + switch (rate) { + case XO_RATE: + val = PHY_MUX_REF_SRC; + break; + case ULONG_MAX: + val = PHY_MUX_PHY_SRC; + break; + default: + return -EINVAL; + } regmap_update_bits(clkr->regmap, phy_mux->reg, PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, PHY_MUX_REF_SRC)); + FIELD_PREP(PHY_MUX_MASK, val)); + + return 0; } const struct clk_ops clk_regmap_phy_mux_ops = { - .enable = phy_mux_enable, - .disable = phy_mux_disable, - .is_enabled = phy_mux_is_enabled, + .recalc_rate = phy_mux_recalc_rate, + .determine_rate = phy_mux_determine_rate, + .set_rate = phy_mux_set_rate, }; EXPORT_SYMBOL_GPL(clk_regmap_phy_mux_ops); From 0cad7630425f4c9ee0dfa376ff8bf60c88ff2566 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 12 May 2026 12:12:42 -0400 Subject: [PATCH 3092/5214] nfs: store the full NFS fileid in inode->i_ino Now that inode->i_ino is a 64-bit value, store the full NFS fileid in it directly instead of an XOR-folded hash. This makes NFS_FILEID() and set_nfs_fileid() operate on inode->i_ino rather than the separate nfsi->fileid field. Since iget5_locked() and ilookup5() now accept a u64 hashval, pass the full fileid as the hash parameter directly. Convert direct nfsi->fileid accesses in nfs_check_inode_attributes(), nfs_update_inode(), and nfs_same_file() to use inode->i_ino. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 2 +- fs/nfs/inode.c | 25 ++++++++++--------------- include/linux/nfs_fs.h | 4 ++-- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e9ce1883288c..5f8c3ea0bce3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -650,7 +650,7 @@ int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) return 0; nfsi = NFS_I(inode); - if (entry->fattr->fileid != nfsi->fileid) + if (entry->fattr->fileid != inode->i_ino) return 0; if (entry->fh->size && nfs_compare_fh(entry->fh, &nfsi->fh) != 0) return 0; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e26030e73696..dd9e378c36fb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -66,10 +66,10 @@ static int nfs_update_inode(struct inode *, struct nfs_fattr *); static struct kmem_cache * nfs_inode_cachep; -static inline unsigned long +static inline u64 nfs_fattr_to_ino_t(struct nfs_fattr *fattr) { - return nfs_fileid_to_ino_t(fattr->fileid); + return fattr->fileid; } int nfs_wait_bit_killable(struct wait_bit_key *key, int mode) @@ -313,8 +313,7 @@ struct nfs_find_desc { }; /* - * In NFSv3 we can have 64bit inode numbers. In order to support - * this, and re-exported directories (also seen in NFSv2) + * For re-exported directories (also seen in NFSv2) * we are forced to allow 2 different inodes to have the same * i_ino. */ @@ -413,7 +412,7 @@ nfs_ilookup(struct super_block *sb, struct nfs_fattr *fattr, struct nfs_fh *fh) .fattr = fattr, }; struct inode *inode; - unsigned long hash; + u64 hash; if (!(fattr->valid & NFS_ATTR_FATTR_FILEID) || !(fattr->valid & NFS_ATTR_FATTR_TYPE)) @@ -456,7 +455,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) }; struct inode *inode = ERR_PTR(-ENOENT); u64 fattr_supported = NFS_SB(sb)->fattr_valid; - unsigned long hash; + u64 hash; nfs_attr_check_mountpoint(sb, fattr); @@ -479,10 +478,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) struct nfs_inode *nfsi = NFS_I(inode); unsigned long now = jiffies; - /* We set i_ino for the few things that still rely on it, - * such as stat(2) */ - inode->i_ino = hash; - /* We can't support update_atime(), since the server will reset it */ inode->i_flags |= S_NOATIME|S_NOCMTIME; inode->i_mode = fattr->mode; @@ -1672,10 +1667,10 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) return 0; /* Has the inode gone and changed behind our back? */ - } else if (nfsi->fileid != fattr->fileid) { + } else if (inode->i_ino != fattr->fileid) { /* Is this perhaps the mounted-on fileid? */ if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) && - nfsi->fileid == fattr->mounted_on_fileid) + inode->i_ino == fattr->mounted_on_fileid) return 0; return -ESTALE; } @@ -2262,15 +2257,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) return 0; /* Has the inode gone and changed behind our back? */ - } else if (nfsi->fileid != fattr->fileid) { + } else if (inode->i_ino != fattr->fileid) { /* Is this perhaps the mounted-on fileid? */ if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) && - nfsi->fileid == fattr->mounted_on_fileid) + inode->i_ino == fattr->mounted_on_fileid) return 0; printk(KERN_ERR "NFS: server %s error: fileid changed\n" "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", NFS_SERVER(inode)->nfs_client->cl_hostname, - inode->i_sb->s_id, (long long)nfsi->fileid, + inode->i_sb->s_id, (long long)inode->i_ino, (long long)fattr->fileid); goto out_err; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4623262da3c0..8e48053b3069 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -396,12 +396,12 @@ static inline int NFS_STALE(const struct inode *inode) static inline __u64 NFS_FILEID(const struct inode *inode) { - return NFS_I(inode)->fileid; + return inode->i_ino; } static inline void set_nfs_fileid(struct inode *inode, __u64 fileid) { - NFS_I(inode)->fileid = fileid; + inode->i_ino = fileid; } static inline void nfs_mark_for_revalidate(struct inode *inode) From 0e06a884f5ba6226829441bfc656ff9f5e9e90ac Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 12 May 2026 12:12:43 -0400 Subject: [PATCH 3093/5214] nfs: remove nfs_compat_user_ino64() and deprecate enable_ino64 Now that inode->i_ino stores the full 64-bit NFS fileid, the nfs_compat_user_ino64() function is no longer needed. generic_fillattr() already copies inode->i_ino into stat->ino, so the explicit override in nfs_getattr() is also redundant. Also remove the now-unused nfs_fileid_to_ino_t() and nfs_fattr_to_ino_t() helper functions that were used to XOR-fold 64-bit fileids into the old unsigned long i_ino. Keep the enable_ino64 module parameter as a deprecated stub that accepts but ignores the value, logging a notice when set. This avoids breaking existing configurations that pass nfs.enable_ino64 on the kernel command line or in modprobe.d. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Anna Schumaker --- .../admin-guide/kernel-parameters.txt | 7 --- fs/nfs/dir.c | 2 +- fs/nfs/inode.c | 50 ++++++------------- include/linux/nfs_fs.h | 10 ---- 4 files changed, 15 insertions(+), 54 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 97007f4f69d4..2e6a6b8f1d8d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4290,13 +4290,6 @@ Kernel parameters Only applies if the softerr mount option is enabled, and the specified value is >= 0. - nfs.enable_ino64= - [NFS] enable 64-bit inode numbers. - If zero, the NFS client will fake up a 32-bit inode - number for the readdir() and stat() syscalls instead - of returning the full 64-bit number. - The default is to return 64-bit inode numbers. - nfs.idmap_cache_timeout= [NFS] set the maximum lifetime for idmapper cache entries. diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5f8c3ea0bce3..659e39b1562b 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1106,7 +1106,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc, ent = &array->array[i]; if (!dir_emit(desc->ctx, ent->name, ent->name_len, - nfs_compat_user_ino64(ent->ino), ent->d_type)) { + ent->ino, ent->d_type)) { desc->eob = true; break; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index dd9e378c36fb..a21ed1c7f89d 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -57,21 +57,23 @@ #define NFSDBG_FACILITY NFSDBG_VFS -#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1 +static bool enable_ino64; -/* Default is to see 64-bit inode numbers */ -static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED; +static int param_set_enable_ino64(const char *val, const struct kernel_param *kp) +{ + pr_notice("enable_ino64 is deprecated and has no effect\n"); + return 0; +} + +static const struct kernel_param_ops param_ops_enable_ino64 = { + .set = param_set_enable_ino64, + .get = param_get_bool, +}; static int nfs_update_inode(struct inode *, struct nfs_fattr *); static struct kmem_cache * nfs_inode_cachep; -static inline u64 -nfs_fattr_to_ino_t(struct nfs_fattr *fattr) -{ - return fattr->fileid; -} - int nfs_wait_bit_killable(struct wait_bit_key *key, int mode) { if (unlikely(nfs_current_task_exiting())) @@ -83,29 +85,6 @@ int nfs_wait_bit_killable(struct wait_bit_key *key, int mode) } EXPORT_SYMBOL_GPL(nfs_wait_bit_killable); -/** - * nfs_compat_user_ino64 - returns the user-visible inode number - * @fileid: 64-bit fileid - * - * This function returns a 32-bit inode number if the boot parameter - * nfs.enable_ino64 is zero. - */ -u64 nfs_compat_user_ino64(u64 fileid) -{ -#ifdef CONFIG_COMPAT - compat_ulong_t ino; -#else - unsigned long ino; -#endif - - if (enable_ino64) - return fileid; - ino = fileid; - if (sizeof(ino) < sizeof(fileid)) - ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8; - return ino; -} - int nfs_drop_inode(struct inode *inode) { return NFS_STALE(inode) || inode_generic_drop(inode); @@ -418,7 +397,7 @@ nfs_ilookup(struct super_block *sb, struct nfs_fattr *fattr, struct nfs_fh *fh) !(fattr->valid & NFS_ATTR_FATTR_TYPE)) return NULL; - hash = nfs_fattr_to_ino_t(fattr); + hash = fattr->fileid; inode = ilookup5(sb, hash, nfs_find_actor, &desc); dprintk("%s: returning %p\n", __func__, inode); @@ -466,7 +445,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0) goto out_no_inode; - hash = nfs_fattr_to_ino_t(fattr); + hash = fattr->fileid; inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc); if (inode == NULL) { @@ -1061,7 +1040,6 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->result_mask = nfs_get_valid_attrmask(inode) | request_mask; generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); - stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); stat->change_cookie = inode_peek_iversion_raw(inode); stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC; if (server->change_attr_type != NFS4_CHANGE_TYPE_IS_UNDEFINED) @@ -2793,7 +2771,7 @@ static void __exit exit_nfs_fs(void) MODULE_AUTHOR("Olaf Kirch "); MODULE_DESCRIPTION("NFS client support"); MODULE_LICENSE("GPL"); -module_param(enable_ino64, bool, 0644); +module_param_cb(enable_ino64, ¶m_ops_enable_ino64, &enable_ino64, 0644); module_init(init_nfs_fs) module_exit(exit_nfs_fs) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8e48053b3069..6d6fa62ede10 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -473,7 +473,6 @@ extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context extern void nfs_file_clear_open_context(struct file *flip); extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx); extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx); -extern u64 nfs_compat_user_ino64(u64 fileid); extern void nfs_fattr_init(struct nfs_fattr *fattr); extern void nfs_fattr_set_barrier(struct nfs_fattr *fattr); extern unsigned long nfs_inc_attr_generation_counter(void); @@ -668,15 +667,6 @@ static inline loff_t nfs_size_to_loff_t(__u64 size) return min_t(u64, size, OFFSET_MAX); } -static inline ino_t -nfs_fileid_to_ino_t(u64 fileid) -{ - ino_t ino = (ino_t) fileid; - if (sizeof(ino_t) < sizeof(u64)) - ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8; - return ino; -} - static inline void nfs_ooo_clear(struct nfs_inode *nfsi) { nfsi->cache_validity &= ~NFS_INO_DATA_INVAL_DEFER; From 2026c8a96afe6e5783497860c4af0a0e1a53991b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 12 May 2026 12:12:44 -0400 Subject: [PATCH 3094/5214] nfs: replace NFS_FILEID() and nfsi->fileid with inode->i_ino Now that inode->i_ino stores the full 64-bit NFS fileid, replace all uses of NFS_FILEID(), set_nfs_fileid(), and direct nfsi->fileid accesses with inode->i_ino throughout the NFS client. Remove the NFS_FILEID() and set_nfs_fileid() helper functions from include/linux/nfs_fs.h since they are no longer needed. Also fix two pre-existing truncation bugs in nfs4trace.h where fileid trace fields were declared as u32 instead of u64. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Anna Schumaker --- fs/nfs/export.c | 6 +- fs/nfs/filelayout/filelayout.c | 4 +- fs/nfs/flexfilelayout/flexfilelayout.c | 6 +- fs/nfs/inode.c | 16 ++--- fs/nfs/nfs4proc.c | 4 +- fs/nfs/nfs4trace.h | 79 +++++++++++------------- fs/nfs/nfstrace.h | 84 +++++++++++++------------- fs/nfs/pagelist.c | 2 +- fs/nfs/pnfs.c | 2 +- fs/nfs/unlink.c | 2 +- fs/nfs/write.c | 2 +- include/linux/nfs_fs.h | 10 --- 12 files changed, 99 insertions(+), 118 deletions(-) diff --git a/fs/nfs/export.c b/fs/nfs/export.c index a10dd5f9d078..8fb08bce0623 100644 --- a/fs/nfs/export.c +++ b/fs/nfs/export.c @@ -49,14 +49,14 @@ nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent) return FILEID_INVALID; } - p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32; - p[FILEID_LOW_OFF] = NFS_FILEID(inode); + p[FILEID_HIGH_OFF] = inode->i_ino >> 32; + p[FILEID_LOW_OFF] = inode->i_ino; p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT; p[len - 1] = 0; /* Padding */ nfs_copy_fh(clnt_fh, server_fh); *max_len = len; dprintk("%s: result fh fileid %llu mode %u size %d\n", - __func__, NFS_FILEID(inode), inode->i_mode, *max_len); + __func__, inode->i_ino, inode->i_mode, *max_len); return *max_len; } diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index e85380e3b11d..f0f53f5dc871 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -95,7 +95,7 @@ static void filelayout_reset_write(struct nfs_pgio_header *hdr) "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, hdr->task.tk_pid, hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); @@ -112,7 +112,7 @@ static void filelayout_reset_read(struct nfs_pgio_header *hdr) "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, hdr->task.tk_pid, hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 8b1559171fe3..6a84d85e0651 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -1230,7 +1230,7 @@ static void ff_layout_reset_write(struct nfs_pgio_header *hdr, bool retry_pnfs) "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, hdr->task.tk_pid, hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); @@ -1243,7 +1243,7 @@ static void ff_layout_reset_write(struct nfs_pgio_header *hdr, bool retry_pnfs) "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, hdr->task.tk_pid, hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); @@ -1283,7 +1283,7 @@ static void ff_layout_reset_read(struct nfs_pgio_header *hdr) "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, hdr->task.tk_pid, hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index a21ed1c7f89d..0d9451a2ad8e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -303,7 +303,7 @@ nfs_find_actor(struct inode *inode, void *opaque) struct nfs_fh *fh = desc->fh; struct nfs_fattr *fattr = desc->fattr; - if (NFS_FILEID(inode) != fattr->fileid) + if (inode->i_ino != fattr->fileid) return 0; if (inode_wrong_type(inode, fattr->mode)) return 0; @@ -320,7 +320,7 @@ nfs_init_locked(struct inode *inode, void *opaque) struct nfs_find_desc *desc = opaque; struct nfs_fattr *fattr = desc->fattr; - set_nfs_fileid(inode, fattr->fileid); + inode->i_ino = fattr->fileid; inode->i_mode = fattr->mode; nfs_copy_fh(NFS_FH(inode), desc->fh); return 0; @@ -580,7 +580,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) } dprintk("NFS: nfs_fhget(%s/%Lu fh_crc=0x%08x ct=%d)\n", inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(inode), + (unsigned long long)inode->i_ino, nfs_display_fhandle_hash(fh), icount_read(inode)); @@ -1343,7 +1343,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) struct nfs_inode *nfsi = NFS_I(inode); dfprintk(PAGECACHE, "NFS: revalidating (%s/%Lu)\n", - inode->i_sb->s_id, (unsigned long long)NFS_FILEID(inode)); + inode->i_sb->s_id, (unsigned long long)inode->i_ino); trace_nfs_revalidate_inode_enter(inode); @@ -1373,7 +1373,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) if (status != 0) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) getattr failed, error=%d\n", inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(inode), status); + (unsigned long long)inode->i_ino, status); switch (status) { case -ETIMEDOUT: /* A soft timeout occurred. Use cached information? */ @@ -1393,7 +1393,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) refresh failed, error=%d\n", inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(inode), status); + (unsigned long long)inode->i_ino, status); goto out; } @@ -1404,7 +1404,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) dfprintk(PAGECACHE, "NFS: (%s/%Lu) revalidation complete\n", inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(inode)); + (unsigned long long)inode->i_ino); out: nfs_free_fattr(fattr); @@ -1453,7 +1453,7 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n", inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(inode)); + (unsigned long long)inode->i_ino); return 0; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a9b8d482d289..60024b978ee0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -377,7 +377,7 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent *p++ = htonl(attrs); /* bitmap */ *p++ = htonl(12); /* attribute buffer length */ *p++ = htonl(NF4DIR); - p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry))); + p = xdr_encode_hyper(p, d_inode(dentry)->i_ino); } *p++ = xdr_one; /* next */ @@ -391,7 +391,7 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent *p++ = htonl(12); /* attribute buffer length */ *p++ = htonl(NF4DIR); spin_lock(&dentry->d_lock); - p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry->d_parent))); + p = xdr_encode_hyper(p, d_inode(dentry->d_parent)->i_ino); spin_unlock(&dentry->d_lock); readdir->pgbase = (char *)p - (char *)start; diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index c939533b9881..1ed677810d9d 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -597,13 +597,13 @@ DECLARE_EVENT_CLASS(nfs4_open_event, __entry->openstateid_hash = 0; } if (inode != NULL) { - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); } else { __entry->fileid = 0; __entry->fhandle = 0; } - __entry->dir = NFS_FILEID(d_inode(ctx->dentry->d_parent)); + __entry->dir = d_inode(ctx->dentry->d_parent)->i_ino; __assign_str(name); ), @@ -658,7 +658,7 @@ TRACE_EVENT(nfs4_cached_open, const struct inode *inode = state->inode; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->fmode = (__force unsigned int)state->state; __entry->stateid_seq = @@ -703,7 +703,7 @@ TRACE_EVENT(nfs4_close, const struct inode *inode = state->inode; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->fmode = (__force unsigned int)state->state; __entry->error = error < 0 ? -error : 0; @@ -759,7 +759,7 @@ DECLARE_EVENT_CLASS(nfs4_lock_event, __entry->start = request->fl_start; __entry->end = request->fl_end; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->stateid_seq = be32_to_cpu(state->stateid.seqid); @@ -831,7 +831,7 @@ TRACE_EVENT(nfs4_set_lock, __entry->start = request->fl_start; __entry->end = request->fl_end; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->stateid_seq = be32_to_cpu(state->stateid.seqid); @@ -922,7 +922,7 @@ TRACE_EVENT(nfs4_state_lock_reclaim, const struct inode *inode = state->inode; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->state_flags = state->flags; __entry->lock_flags = lock->ls_flags; @@ -960,7 +960,7 @@ DECLARE_EVENT_CLASS(nfs4_set_delegation_event, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->fmode = (__force unsigned int)fmode; ), @@ -1087,7 +1087,7 @@ DECLARE_EVENT_CLASS(nfs4_test_stateid_event, __entry->error = error < 0 ? -error : 0; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->stateid_seq = be32_to_cpu(state->stateid.seqid); @@ -1137,7 +1137,7 @@ DECLARE_EVENT_CLASS(nfs4_lookup_event, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->error = -error; __assign_str(name); ), @@ -1185,7 +1185,7 @@ TRACE_EVENT(nfs4_lookupp, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->ino = NFS_FILEID(inode); + __entry->ino = inode->i_ino; __entry->error = error < 0 ? -error : 0; ), @@ -1220,8 +1220,8 @@ TRACE_EVENT(nfs4_rename, TP_fast_assign( __entry->dev = olddir->i_sb->s_dev; - __entry->olddir = NFS_FILEID(olddir); - __entry->newdir = NFS_FILEID(newdir); + __entry->olddir = olddir->i_ino; + __entry->newdir = newdir->i_ino; __entry->error = error < 0 ? -error : 0; __assign_str(oldname); __assign_str(newname); @@ -1258,7 +1258,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_event, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->error = error < 0 ? -error : 0; ), @@ -1311,7 +1311,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_stateid_event, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->error = error < 0 ? -error : 0; __entry->stateid_seq = @@ -1421,7 +1421,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_callback_event, __entry->error = error < 0 ? -error : 0; __entry->fhandle = nfs_fhandle_hash(fhandle); if (!IS_ERR_OR_NULL(inode)) { - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; } else { __entry->fileid = 0; @@ -1478,7 +1478,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_stateid_callback_event, __entry->error = error < 0 ? -error : 0; __entry->fhandle = nfs_fhandle_hash(fhandle); if (!IS_ERR_OR_NULL(inode)) { - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; } else { __entry->fileid = 0; @@ -1655,7 +1655,7 @@ DECLARE_EVENT_CLASS(nfs4_read_event, const struct pnfs_layout_segment *lseg = hdr->lseg; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; @@ -1727,7 +1727,7 @@ DECLARE_EVENT_CLASS(nfs4_write_event, const struct pnfs_layout_segment *lseg = hdr->lseg; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset = hdr->args.offset; __entry->arg_count = hdr->args.count; @@ -1795,7 +1795,7 @@ DECLARE_EVENT_CLASS(nfs4_commit_event, const struct pnfs_layout_segment *lseg = data->lseg; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset = data->args.offset; __entry->count = data->args.count; @@ -1857,7 +1857,7 @@ TRACE_EVENT(nfs4_layoutget, const struct inode *inode = d_inode(ctx->dentry); const struct nfs4_state *state = ctx->state; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->iomode = args->iomode; __entry->offset = args->offset; @@ -1957,7 +1957,7 @@ TRACE_EVENT(pnfs_update_layout, ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->pos = pos; __entry->count = count; @@ -2012,7 +2012,7 @@ DECLARE_EVENT_CLASS(pnfs_layout_event, ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->pos = pos; __entry->count = count; @@ -2194,7 +2194,7 @@ DECLARE_EVENT_CLASS(nfs4_flexfiles_io_event, __entry->error = -error; __entry->nfs_error = hdr->res.op_status; __entry->fhandle = nfs_fhandle_hash(hdr->args.fh); - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; __entry->offset = hdr->args.offset; __entry->count = hdr->args.count; @@ -2258,7 +2258,7 @@ TRACE_EVENT(ff_layout_commit_error, __entry->error = -error; __entry->nfs_error = data->res.op_status; __entry->fhandle = nfs_fhandle_hash(data->args.fh); - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; __entry->offset = data->args.offset; __entry->count = data->args.count; @@ -2423,7 +2423,7 @@ TRACE_EVENT(nfs4_llseek, TP_STRUCT__entry( __field(unsigned long, error) __field(u32, fhandle) - __field(u32, fileid) + __field(u64, fileid) __field(dev_t, dev) __field(int, stateid_seq) __field(u32, stateid_hash) @@ -2434,10 +2434,9 @@ TRACE_EVENT(nfs4_llseek, ), TP_fast_assign( - const struct nfs_inode *nfsi = NFS_I(inode); const struct nfs_fh *fh = args->sa_fh; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset_s = args->sa_offset; @@ -2499,7 +2498,7 @@ DECLARE_EVENT_CLASS(nfs4_sparse_event, __entry->offset = args->falloc_offset; __entry->len = args->falloc_length; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __entry->stateid_seq = be32_to_cpu(args->falloc_stateid.seqid); @@ -2568,14 +2567,11 @@ TRACE_EVENT(nfs4_copy, ), TP_fast_assign( - const struct nfs_inode *src_nfsi = NFS_I(src_inode); - const struct nfs_inode *dst_nfsi = NFS_I(dst_inode); - - __entry->src_fileid = src_nfsi->fileid; + __entry->src_fileid = src_inode->i_ino; __entry->src_dev = src_inode->i_sb->s_dev; __entry->src_fhandle = nfs_fhandle_hash(args->src_fh); __entry->src_offset = args->src_pos; - __entry->dst_fileid = dst_nfsi->fileid; + __entry->dst_fileid = dst_inode->i_ino; __entry->dst_dev = dst_inode->i_sb->s_dev; __entry->dst_fhandle = nfs_fhandle_hash(args->dst_fh); __entry->dst_offset = args->dst_pos; @@ -2666,14 +2662,11 @@ TRACE_EVENT(nfs4_clone, ), TP_fast_assign( - const struct nfs_inode *src_nfsi = NFS_I(src_inode); - const struct nfs_inode *dst_nfsi = NFS_I(dst_inode); - - __entry->src_fileid = src_nfsi->fileid; + __entry->src_fileid = src_inode->i_ino; __entry->src_dev = src_inode->i_sb->s_dev; __entry->src_fhandle = nfs_fhandle_hash(args->src_fh); __entry->src_offset = args->src_offset; - __entry->dst_fileid = dst_nfsi->fileid; + __entry->dst_fileid = dst_inode->i_ino; __entry->dst_dev = dst_inode->i_sb->s_dev; __entry->dst_fhandle = nfs_fhandle_hash(args->dst_fh); __entry->dst_offset = args->dst_offset; @@ -2724,7 +2717,7 @@ TRACE_EVENT(nfs4_copy_notify, TP_STRUCT__entry( __field(unsigned long, error) __field(u32, fhandle) - __field(u32, fileid) + __field(u64, fileid) __field(dev_t, dev) __field(int, stateid_seq) __field(u32, stateid_hash) @@ -2733,9 +2726,7 @@ TRACE_EVENT(nfs4_copy_notify, ), TP_fast_assign( - const struct nfs_inode *nfsi = NFS_I(inode); - - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->dev = inode->i_sb->s_dev; __entry->fhandle = nfs_fhandle_hash(args->cna_src_fh); __entry->stateid_seq = @@ -2830,7 +2821,7 @@ DECLARE_EVENT_CLASS(nfs4_xattr_event, TP_fast_assign( __entry->error = error < 0 ? -error : 0; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); __assign_str(name); ), diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index ff467959f733..4ada21f4eebd 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -80,7 +80,7 @@ DECLARE_EVENT_CLASS(nfs_inode_event, TP_fast_assign( const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->cache_validity = nfsi->cache_validity; @@ -121,7 +121,7 @@ DECLARE_EVENT_CLASS(nfs_inode_event_done, const struct nfs_inode *nfsi = NFS_I(inode); __entry->error = error < 0 ? -error : 0; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->type = nfs_umode_to_dtype(inode->i_mode); __entry->version = inode_peek_iversion_raw(inode); @@ -211,7 +211,7 @@ TRACE_EVENT(nfs_access_exit, const struct nfs_inode *nfsi = NFS_I(inode); __entry->error = error < 0 ? -error : 0; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->type = nfs_umode_to_dtype(inode->i_mode); __entry->version = inode_peek_iversion_raw(inode); @@ -265,7 +265,7 @@ DECLARE_EVENT_CLASS(nfs_update_size_class, __entry->dev = inode->i_sb->s_dev; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->version = inode_peek_iversion_raw(inode); __entry->cur_size = i_size_read(inode); __entry->new_size = new_size; @@ -317,7 +317,7 @@ DECLARE_EVENT_CLASS(nfs_inode_range_event, __entry->dev = inode->i_sb->s_dev; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->version = inode_peek_iversion_raw(inode); __entry->range_start = range_start; __entry->range_end = range_end; @@ -371,7 +371,7 @@ DECLARE_EVENT_CLASS(nfs_readdir_event, const struct nfs_inode *nfsi = NFS_I(dir); __entry->dev = dir->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = dir->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(dir); if (cookie != 0) @@ -429,9 +429,9 @@ DECLARE_EVENT_CLASS(nfs_lookup_event, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->flags = flags; - __entry->fileid = d_is_negative(dentry) ? 0 : NFS_FILEID(d_inode(dentry)); + __entry->fileid = d_is_negative(dentry) ? 0 : d_inode(dentry)->i_ino; __assign_str(name); ), @@ -476,10 +476,10 @@ DECLARE_EVENT_CLASS(nfs_lookup_event_done, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->error = error < 0 ? -error : 0; __entry->flags = flags; - __entry->fileid = d_is_negative(dentry) ? 0 : NFS_FILEID(d_inode(dentry)); + __entry->fileid = d_is_negative(dentry) ? 0 : d_inode(dentry)->i_ino; __assign_str(name); ), @@ -532,7 +532,7 @@ TRACE_EVENT(nfs_atomic_open_enter, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->flags = flags; __entry->fmode = (__force unsigned long)ctx->mode; __assign_str(name); @@ -571,7 +571,7 @@ TRACE_EVENT(nfs_atomic_open_exit, TP_fast_assign( __entry->error = -error; __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->flags = flags; __entry->fmode = (__force unsigned long)ctx->mode; __assign_str(name); @@ -608,7 +608,7 @@ TRACE_EVENT(nfs_create_enter, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->flags = flags; __assign_str(name); ), @@ -644,7 +644,7 @@ TRACE_EVENT(nfs_create_exit, TP_fast_assign( __entry->error = -error; __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->flags = flags; __assign_str(name); ), @@ -676,7 +676,7 @@ DECLARE_EVENT_CLASS(nfs_directory_event, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __assign_str(name); ), @@ -714,7 +714,7 @@ DECLARE_EVENT_CLASS(nfs_directory_event_done, TP_fast_assign( __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->error = error < 0 ? -error : 0; __assign_str(name); ), @@ -768,8 +768,8 @@ TRACE_EVENT(nfs_link_enter, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); - __entry->dir = NFS_FILEID(dir); + __entry->fileid = inode->i_ino; + __entry->dir = dir->i_ino; __assign_str(name); ), @@ -803,8 +803,8 @@ TRACE_EVENT(nfs_link_exit, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; - __entry->fileid = NFS_FILEID(inode); - __entry->dir = NFS_FILEID(dir); + __entry->fileid = inode->i_ino; + __entry->dir = dir->i_ino; __entry->error = error < 0 ? -error : 0; __assign_str(name); ), @@ -840,8 +840,8 @@ DECLARE_EVENT_CLASS(nfs_rename_event, TP_fast_assign( __entry->dev = old_dir->i_sb->s_dev; - __entry->old_dir = NFS_FILEID(old_dir); - __entry->new_dir = NFS_FILEID(new_dir); + __entry->old_dir = old_dir->i_ino; + __entry->new_dir = new_dir->i_ino; __assign_str(old_name); __assign_str(new_name); ), @@ -889,8 +889,8 @@ DECLARE_EVENT_CLASS(nfs_rename_event_done, TP_fast_assign( __entry->dev = old_dir->i_sb->s_dev; __entry->error = -error; - __entry->old_dir = NFS_FILEID(old_dir); - __entry->new_dir = NFS_FILEID(new_dir); + __entry->old_dir = old_dir->i_ino; + __entry->new_dir = new_dir->i_ino; __assign_str(old_name); __assign_str(new_name); ), @@ -943,7 +943,7 @@ TRACE_EVENT(nfs_sillyrename_unlink, struct inode *dir = d_inode(data->dentry->d_parent); size_t len = data->args.name.len; __entry->dev = dir->i_sb->s_dev; - __entry->dir = NFS_FILEID(dir); + __entry->dir = dir->i_ino; __entry->error = -error; memcpy(__get_str(name), data->args.name.name, len); @@ -981,7 +981,7 @@ DECLARE_EVENT_CLASS(nfs_folio_event, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->offset = offset; @@ -1031,7 +1031,7 @@ DECLARE_EVENT_CLASS(nfs_folio_event_done, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->offset = offset; @@ -1109,7 +1109,7 @@ DECLARE_EVENT_CLASS(nfs_kiocb_event, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->offset = iocb->ki_pos; @@ -1160,7 +1160,7 @@ TRACE_EVENT(nfs_aop_readahead, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->offset = pos; @@ -1199,7 +1199,7 @@ TRACE_EVENT(nfs_aop_readahead_done, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->version = inode_peek_iversion_raw(inode); __entry->nr_pages = nr_pages; @@ -1239,7 +1239,7 @@ TRACE_EVENT(nfs_initiate_read, __entry->offset = hdr->args.offset; __entry->count = hdr->args.count; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1284,7 +1284,7 @@ TRACE_EVENT(nfs_readpage_done, __entry->res_count = hdr->res.count; __entry->eof = hdr->res.eof; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1330,7 +1330,7 @@ TRACE_EVENT(nfs_readpage_short, __entry->res_count = hdr->res.count; __entry->eof = hdr->res.eof; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1377,7 +1377,7 @@ TRACE_EVENT(nfs_pgio_error, __entry->arg_count = hdr->args.count; __entry->res_count = hdr->res.count; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1416,7 +1416,7 @@ TRACE_EVENT(nfs_initiate_write, __entry->count = hdr->args.count; __entry->stable = hdr->args.stable; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1467,7 +1467,7 @@ TRACE_EVENT(nfs_writeback_done, &verf->verifier, NFS4_VERIFIER_SIZE); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1507,7 +1507,7 @@ DECLARE_EVENT_CLASS(nfs_page_class, const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->req = req; __entry->offset = req_offset(req); @@ -1555,7 +1555,7 @@ DECLARE_EVENT_CLASS(nfs_page_error_class, TP_fast_assign( const struct nfs_inode *nfsi = NFS_I(inode); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(&nfsi->fh); __entry->offset = req_offset(req); __entry->count = req->wb_bytes; @@ -1609,7 +1609,7 @@ TRACE_EVENT(nfs_initiate_commit, __entry->offset = data->args.offset; __entry->count = data->args.count; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1655,7 +1655,7 @@ TRACE_EVENT(nfs_commit_done, &verf->verifier, NFS4_VERIFIER_SIZE); __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); ), @@ -1701,7 +1701,7 @@ DECLARE_EVENT_CLASS(nfs_direct_req_class, const struct nfs_fh *fh = &nfsi->fh; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset = dreq->io_start; __entry->count = dreq->count; @@ -1765,7 +1765,7 @@ DECLARE_EVENT_CLASS(nfs_local_dio_class, const struct nfs_fh *fh = &nfsi->fh; __entry->dev = inode->i_sb->s_dev; - __entry->fileid = nfsi->fileid; + __entry->fileid = inode->i_ino; __entry->fhandle = nfs_fhandle_hash(fh); __entry->offset = offset; __entry->count = count; diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 4a87b2fdb2e6..7dd478ffc2fa 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -759,7 +759,7 @@ int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr, dprintk("NFS: initiated pgio call " "(req %s/%llu, %u bytes @ offset %llu)\n", hdr->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(hdr->inode), + (unsigned long long)hdr->inode->i_ino, hdr->args.count, (unsigned long long)hdr->args.offset); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 743467e9ba20..fdedeff5f6cc 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2373,7 +2373,7 @@ pnfs_update_layout(struct inode *ino, dprintk("%s: inode %s/%llu pNFS layout segment %s for " "(%s, offset: %llu, length: %llu)\n", __func__, ino->i_sb->s_id, - (unsigned long long)NFS_FILEID(ino), + (unsigned long long)ino->i_ino, IS_ERR_OR_NULL(lseg) ? "not found" : "found", iomode==IOMODE_RW ? "read/write" : "read-only", (unsigned long long)pos, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index df3ca4669df6..7f2e84eaaa9f 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -461,7 +461,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out; - fileid = NFS_FILEID(d_inode(dentry)); + fileid = d_inode(dentry)->i_ino; sdentry = NULL; do { diff --git a/fs/nfs/write.c b/fs/nfs/write.c index d7c399763ad9..fcffb8c9e9df 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1817,7 +1817,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) dprintk("NFS: commit (%s/%llu %d@%lld)", nfs_req_openctx(req)->dentry->d_sb->s_id, - (unsigned long long)NFS_FILEID(d_inode(nfs_req_openctx(req)->dentry)), + (unsigned long long)d_inode(nfs_req_openctx(req)->dentry)->i_ino, req->wb_bytes, (long long)req_offset(req)); if (status < 0) { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 6d6fa62ede10..83063f4ab488 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -394,16 +394,6 @@ static inline int NFS_STALE(const struct inode *inode) return test_bit(NFS_INO_STALE, &NFS_I(inode)->flags); } -static inline __u64 NFS_FILEID(const struct inode *inode) -{ - return inode->i_ino; -} - -static inline void set_nfs_fileid(struct inode *inode, __u64 fileid) -{ - inode->i_ino = fileid; -} - static inline void nfs_mark_for_revalidate(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); From bd5e6b056bd0900d8efde90da7a5877dc50842c7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 12 May 2026 12:12:45 -0400 Subject: [PATCH 3095/5214] nfs: remove fileid field from struct nfs_inode Now that all NFS client code uses inode->i_ino directly to store and access the 64-bit NFS fileid, the separate fileid field in struct nfs_inode is unused. Remove it to save 8 bytes per NFS inode. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Anna Schumaker --- include/linux/nfs_fs.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 83063f4ab488..ec17e602c979 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -145,11 +145,6 @@ struct nfs4_xattr_cache; * nfs fs inode data in memory */ struct nfs_inode { - /* - * The 64bit 'inode number' - */ - __u64 fileid; - /* * NFS file handle */ From 37957478be021b92981aa4c99b69f308d3b784d0 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 13 May 2026 17:28:59 +0800 Subject: [PATCH 3096/5214] sunrpc: Fix error handling in rpc_sysfs_xprt_switch_add_xprt_store() xprt_create_transport() never returns NULL, only valid pointers or error pointers. Using IS_ERR_OR_NULL() is incorrect, and PTR_ERR(NULL) would return 0, which indicates EOF in a sysfs store function. Fix this by using IS_ERR() instead of IS_ERR_OR_NULL(). Fixes: df210d9b0951 ("sunrpc: Add a sysfs file for adding a new xprt") Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index a90480f80154..49686bf740e6 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -348,7 +348,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, xprt_create_args.reconnect_timeout = xprt->max_reconnect_timeout; new = xprt_create_transport(&xprt_create_args); - if (IS_ERR_OR_NULL(new)) { + if (IS_ERR(new)) { count = PTR_ERR(new); goto out_put_xprt; } From 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 13 May 2026 12:26:56 -0400 Subject: [PATCH 3097/5214] NFSv4/flexfiles: reject zero filehandle version count ff_layout_alloc_lseg() decodes the filehandle-version array count from the flexfiles layout body. The value is used as the count for kzalloc_objs(), and the current code only rejects NULL. A zero count yields ZERO_SIZE_PTR, which can be stored in dss_info->fh_versions even though later flexfiles paths assume that at least one filehandle version exists. Reject fh_count == 0 before the allocation, matching the existing zero version_count validation in the flexfiles GETDEVICEINFO parser. A QEMU/KASAN run with a malformed flexfiles layout hit: KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750 ff_layout_encode_layoutreturn+0x683/0x970 nfs4_xdr_enc_layoutreturn+0x278/0x3a0 Kernel panic - not syncing: Fatal exception The patched kernel rejects the malformed layout without KASAN/oops/panic, and a valid fh_count=1 regression still opens, reads, and unmounts cleanly. Cc: stable@vger.kernel.org Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker --- fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 6a84d85e0651..99caa8d28c25 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -551,6 +551,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_err_free; fh_count = be32_to_cpup(p); + if (fh_count == 0) { + rc = -EINVAL; + goto out_err_free; + } dss_info->fh_versions = kzalloc_objs(struct nfs_fh, fh_count, gfp_flags); From 238e9b51aa29f48b6243212a3b75c8e48d6b96fd Mon Sep 17 00:00:00 2001 From: Igor Raits Date: Wed, 29 Apr 2026 12:49:38 +0200 Subject: [PATCH 3098/5214] NFSv4: clear exception state on successful mkdir retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a server returns NFS4ERR_DELAY for an NFSv4 CREATE issued by mkdir(2), the client correctly waits and retries. When the retry succeeds, however, mkdir(2) can still surface -EEXIST to userspace even though the directory was just created on the server. Reproducer (random 16-hex names so collisions are not the cause) against an in-kernel Linux nfsd; reproduces under both NFSv4.0 and NFSv4.2: N=2000000; base=/var/gdc/export for ((i=1; i<=N; i++)); do d=$base/$(openssl rand -hex 8) mkdir "$d" 2>/dev/null || echo "$(date +%T) failed loop=$i $d" rmdir "$d" 2>/dev/null done Failures cluster at the cadence at which the server-side auth/export cache refresh path causes nfsd to return NFS4ERR_DELAY for CREATE. A wire trace of one failure (the three CREATE RPCs all come from a single mkdir(2), generated by the do-while in nfs4_proc_mkdir()): client -> server CREATE name=... -> NFS4ERR_DELAY ~100 ms later client -> server CREATE name=... -> NFS4_OK (dir created) ~80 us later client -> server CREATE name=... -> NFS4ERR_EXIST (correct) Since commit dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()"), nfs4_handle_exception() is called only when _nfs4_proc_mkdir() returned an error. That gate breaks retry-state hygiene: nfs4_do_handle_exception() resets exception.{delay,recovering, retry} to 0 on entry, so calling it on success is what previously cleared the retry flag set by the preceding NFS4ERR_DELAY iteration. With the gate in place, exception.retry stays at 1 after the successful retry, the loop runs once more, and the resulting CREATE for an already-created name yields NFS4ERR_EXIST -> -EEXIST to userspace. Drop the conditional and call nfs4_handle_exception() unconditionally, matching every other do-while in fs/nfs/nfs4proc.c (nfs4_proc_symlink(), nfs4_proc_link(), etc.). The dentry/status separation introduced by that commit is preserved. Fixes: dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()") Reported-and-tested-by: Jan Čípa Closes: https://lore.kernel.org/linux-nfs/CA+9S74hSp_tJu2Ffe2BPNC2T25gfkhgjjDkdgSsF5c2rnJq_wA@mail.gmail.com/ Reviewed-by: NeilBrown Cc: stable@vger.kernel.org Signed-off-by: Igor Raits Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 60024b978ee0..0d77d50f4de7 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5302,10 +5302,9 @@ static struct dentry *nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, do { alias = _nfs4_proc_mkdir(dir, dentry, sattr, label, &err); trace_nfs4_mkdir(dir, &dentry->d_name, err); + err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); if (err) - alias = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), - err, - &exception)); + alias = ERR_PTR(err); } while (exception.retry); nfs4_label_release_security(label); From ea7338471620030582e6a8d3f0fda767fdaee768 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 7 May 2026 15:01:17 +0200 Subject: [PATCH 3099/5214] xprtrdma: Move long delayed work on system_dfl_long_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_long_wq. This workqueue should be used when long works are expected and it is a per-cpu workqueue. The function(s) end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Long work shouldn't stick to a single CPU. Recently, a new unbound workqueue specific for long running work has been added:     c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works") Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_long_wq with system_dfl_long_wq so that the work may benefit from scheduler task placement. Signed-off-by: Marco Crivellari Reviewed-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/transport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 61706df5e485..1a54993f7ffb 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -484,7 +484,8 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task) xprt_reconnect_backoff(xprt, RPCRDMA_INIT_REEST_TO); } trace_xprtrdma_op_connect(r_xprt, delay); - queue_delayed_work(system_long_wq, &r_xprt->rx_connect_worker, delay); + queue_delayed_work(system_dfl_long_wq, &r_xprt->rx_connect_worker, + delay); } /** From 91668417d4e925c98cae4a55b1b9860380ddbf16 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 13 May 2026 09:58:24 +0300 Subject: [PATCH 3100/5214] pNFS/filelayout: fix cheking if a layout is striped A layout can still be striped with num_fh = 1 as it is perfectly possible that both MDS and DSs can handle the same filehandle. Hence check according to stripe_count > 1, which is the correct check to begin with. We should not be called with flseg->dsaddr = NULL, but if for some reason we do, return our best guess with is flseg->num_fh > 1. Fixes: a6b9d2fa0024 ("pNFS/filelayout: Fix coalescing test for single DS") Signed-off-by: Sagi Grimberg Signed-off-by: Anna Schumaker --- fs/nfs/filelayout/filelayout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index f0f53f5dc871..72e20b56fbc7 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -778,6 +778,8 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, static bool filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg) { + if (flseg->dsaddr) + return flseg->dsaddr->stripe_count > 1; return flseg->num_fh > 1; } From f74ad9eee7221ca67fa489b44c3d065c54e65d4d Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 28 Apr 2026 11:15:14 +0300 Subject: [PATCH 3101/5214] NFS: show redacted cert_serial and privkey_serial in mount options mount output should not reveal the contents of the serials, but indicate they were provided. Signed-off-by: Sagi Grimberg Signed-off-by: Anna Schumaker --- fs/nfs/super.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 4cd420b14ce3..ff798b6685f8 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -509,6 +509,10 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, default: break; } + if (clp->cl_xprtsec.cert_serial) + seq_puts(m, ",cert_serial="); + if (clp->cl_xprtsec.privkey_serial) + seq_puts(m, ",privkey_serial="); if (version != 4) nfs_show_mountd_options(m, nfss, showdefaults); From 35168eb947f230aaa35fd8416a30563ef89f5421 Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Thu, 23 Apr 2026 10:52:10 -0700 Subject: [PATCH 3102/5214] NFS: fix eof updates after NFSv4.2 fallocate/zero-range Generic/075 reliably exposes a regression when the client holds an NFSv4 write delegation: ZERO_RANGE/ALLOCATE extends the file on the server, but the local inode keeps the old i_size. The test then fails with 'Size error' because the post-op attribute refresh refuses to touch i_size while a delegation is outstanding, and the cached EOF was never marked stale. Update _nfs42_proc_fallocate() so that on success it: - bumps i_size when the operation extends the file, and - marks NFS_INO_INVALID_BLOCKS since the block count can also change Tested with xfstests generic/075 over NFSv4.2. Signed-off-by: Dai Ngo Signed-off-by: Anna Schumaker --- fs/nfs/nfs42proc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 7602ede6f75f..ab86246fc364 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -81,12 +81,17 @@ static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) { - if (nfs_should_remove_suid(inode)) { - spin_lock(&inode->i_lock); + loff_t newsize = offset + len; + + spin_lock(&inode->i_lock); + if (newsize > i_size_read(inode)) + i_size_write(inode, newsize); + nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS); + if (nfs_should_remove_suid(inode)) nfs_set_cache_invalid(inode, - NFS_INO_REVAL_FORCED | NFS_INO_INVALID_MODE); - spin_unlock(&inode->i_lock); - } + NFS_INO_REVAL_FORCED | + NFS_INO_INVALID_MODE); + spin_unlock(&inode->i_lock); status = nfs_post_op_update_inode_force_wcc(inode, res.falloc_fattr); } From 13e198a90ca4050f4bee8a3f23680389a6563ccc Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 18 May 2026 13:10:36 +0000 Subject: [PATCH 3103/5214] pNFS: Fix use-after-free in pnfs_update_layout() When hitting the NFS_LAYOUT_RETURN branch in pnfs_update_layout(), the code calls pnfs_prepare_to_retry_layoutget(lo). If it succeeds, pnfs_put_layout_hdr(lo) is called before trace_pnfs_update_layout(), which still references 'lo'. This results in a use-after-free when the tracepoint accesses lo's fields. Fix this by moving the tracepoint call before pnfs_put_layout_hdr(lo). Fixes: 2c8d5fc37fe2 ("pNFS: Stricter ordering of layoutget and layoutreturn") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Signed-off-by: Anna Schumaker --- fs/nfs/pnfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index fdedeff5f6cc..cb203821a397 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2229,11 +2229,11 @@ pnfs_update_layout(struct inode *ino, dprintk("%s wait for layoutreturn\n", __func__); lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo)); if (!IS_ERR(lseg)) { - pnfs_put_layout_hdr(lo); dprintk("%s retrying\n", __func__); trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY); + pnfs_put_layout_hdr(lo); goto lookup_again; } trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, From 2797ae7c929610fb2d2303a996a08173fa096730 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:01 -0400 Subject: [PATCH 3104/5214] xprtrdma: Use sendctx DMA state for Send signaling Send signaling matters only when the prepared Send has page mappings to unmap. Today that test is expressed indirectly with rl_kref, because the Send-side reference is taken only for Sends with mapped SGEs. Split the SGE DMA unmap loop into its own helper and use sc_unmap_count directly for the signaling decision. This keeps the current behavior but removes one dependency on the old rl_kref semantics before the request lifetime rules are changed. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/frwr_ops.c | 2 +- net/sunrpc/xprtrdma/rpc_rdma.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 7f79a0a2601e..e5c71cf705a3 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -474,7 +474,7 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) ++num_wrs; } - if ((kref_read(&req->rl_kref) > 1) || num_wrs > ep->re_send_count) { + if (req->rl_sendctx->sc_unmap_count || num_wrs > ep->re_send_count) { send_wr->send_flags |= IB_SEND_SIGNALED; ep->re_send_count = min_t(unsigned int, ep->re_send_batch, num_wrs - ep->re_send_count); diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 0e0f21974710..16b9987858d6 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -477,19 +477,11 @@ static void rpcrdma_sendctx_done(struct kref *kref) rep->rr_rxprt->rx_stats.reply_waits_for_send++; } -/** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer - * @sc: sendctx containing SGEs to unmap - * - */ -void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; struct ib_sge *sge; - if (!sc->sc_unmap_count) - return; - /* The first two SGEs contain the transport header and * the inline buffer. These are always left mapped so * they can be cheaply re-used. @@ -498,7 +490,19 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) ++sge, --sc->sc_unmap_count) ib_dma_unmap_page(rdmab_device(rb), sge->addr, sge->length, DMA_TO_DEVICE); +} +/** + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * @sc: sendctx containing SGEs to unmap + * + */ +void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +{ + if (!sc->sc_unmap_count) + return; + + rpcrdma_sendctx_dma_unmap(sc); kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); } From e786233d2e0bbff9a82e43f02ae3a46ab4b08ec3 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:02 -0400 Subject: [PATCH 3105/5214] xprtrdma: Decouple req recycling from RPC completion rl_kref formerly served two distinct lifetimes through a single refcount: it gated when a Reply could wake its RPC task, and it gated when an rpcrdma_req could return to its free pool. The marshal path took the Send-side reference only when SGEs needed DMA-unmap (sc_unmap_count > 0), which made a Send carrying only pre-registered buffers an exception: the Reply handler dropped rl_kref from 1 to 0 and freed the req while the HCA might still be DMA-reading from its send buffer. Give rl_kref a narrower job. The RPC layer takes one reference when slot allocation hands a req out. rpcrdma_prepare_send_sges() takes a Send-side reference unconditionally after WR preparation succeeds. xprt_rdma_free_slot() and xprt_rdma_bc_free_rqst() drop the RPC-layer reference; rpcrdma_sendctx_unmap() drops the Send-side reference. The req returns to its free pool only after both owners have signed off. The existing kref_init(&req->rl_kref) call in rpcrdma_prepare_send_sges() is removed. Initialization moves to the slot-allocation paths (xprt_rdma_alloc_slot and rpcrdma_bc_rqst_get), and the release callback re-arms rl_kref before the req returns to a free pool. A re-init in the marshal path would discard the RPC-layer reference that already exists on entry. Three invariants follow: - Any rpcrdma_req held by an rpc_rqst has rl_kref >= 1. xprt_rdma_alloc_slot(), rpcrdma_bc_rqst_get(), and the backlog-wake branch in xprt_rdma_alloc_slot() each kref_init rl_kref before publishing the req. Without this invariant, an RPC task that aborts between slot allocation and marshal (gss_refresh failure or signal during call_connect, for example) would drive xprt_release() -> xprt_rdma_free_slot() -> kref_put against a refcount of zero, saturating refcount_t and stranding the slot. - The Send-side reference is taken only after WR prep succeeds. A mapping failure in rpcrdma_prepare_send_sges() runs rpcrdma_sendctx_cancel(), which DMA-unmaps the sendctx and clears sc_req without touching rl_kref. The sendctx ring walks in rpcrdma_sendctx_put_locked() and rpcrdma_sendctxs_destroy() skip entries with sc_req == NULL, so a burst of -EIO marshal failures cannot hold reqs off rb_send_bufs. - The release callback re-arms rl_kref so the next consumer enters with the invariant satisfied. Replies now complete the RPC directly. rpcrdma_reply_handler() calls rpcrdma_complete_rqst() in place of kref_put on the non-LocalInv branch. The LocalInv branch already completes the RPC from frwr_unmap_async() and is unaffected. Because Send-side references can now outlive RPC completion, connection teardown drains sendctx entries whose unsignaled Sends never had a later signaled completion to walk the ring. rpcrdma_sendctxs_destroy() walks the active range and runs rpcrdma_sendctx_unmap() on each entry with a non-NULL sc_req before the request buffers are reset, and is moved ahead of rpcrdma_reqs_reset() in rpcrdma_xprt_disconnect() so the reqs are still in their pre-reset state when the Send-side refs are released. The drain creates a teardown-ordering hazard on the backchannel path. With the new lifetime, releasing a bc_prealloc req from rpcrdma_req_release() re-adds it to bc_pa_list. The disconnect in xprt_rdma_destroy() runs after xprt_destroy_backchannel() has already emptied bc_pa_list, so the drained reqs would otherwise leak. xprt_rdma_destroy() now runs xprt_rdma_bc_destroy(xprt, 0) a second time after the disconnect to reclaim them. Fixes: 0ab115237025 ("xprtrdma: Wake RPCs directly in rpcrdma_wc_send path") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/backchannel.c | 5 ++- net/sunrpc/xprtrdma/rpc_rdma.c | 47 +++++++++++--------------- net/sunrpc/xprtrdma/transport.c | 55 ++++++++++++++++++++++++++++--- net/sunrpc/xprtrdma/verbs.c | 29 +++++++++++++--- net/sunrpc/xprtrdma/xprt_rdma.h | 2 +- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 2f0f9618dd05..e5b3463da25f 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -159,9 +159,7 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst) rpcrdma_rep_put(&r_xprt->rx_buf, rep); req->rl_reply = NULL; - spin_lock(&xprt->bc_pa_lock); - list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); - spin_unlock(&xprt->bc_pa_lock); + rpcrdma_req_put(req); xprt_put(xprt); } @@ -203,6 +201,7 @@ static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt) rqst->rq_xprt = xprt; __set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); xdr_buf_init(&rqst->rq_snd_buf, rdmab_data(req->rl_sendbuf), size); + kref_init(&req->rl_kref); return rqst; } diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 16b9987858d6..69380f9dfa49 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -467,16 +467,6 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, return 0; } -static void rpcrdma_sendctx_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - struct rpcrdma_rep *rep = req->rl_reply; - - rpcrdma_complete_rqst(rep); - rep->rr_rxprt->rx_stats.reply_waits_for_send++; -} - static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; @@ -493,17 +483,26 @@ static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) } /** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer and release Send owner * @sc: sendctx containing SGEs to unmap * */ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) { - if (!sc->sc_unmap_count) - return; + struct rpcrdma_req *req = sc->sc_req; rpcrdma_sendctx_dma_unmap(sc); - kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); + sc->sc_req = NULL; + rpcrdma_req_put(req); +} + +/* No Send was posted. Release DMA mappings prepared for this + * sendctx, but leave the request reference count alone. + */ +static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc) +{ + rpcrdma_sendctx_dma_unmap(sc); + sc->sc_req = NULL; } /* Prepare an SGE for the RPC-over-RDMA transport header. @@ -695,8 +694,6 @@ static bool rpcrdma_prepare_noch_mapped(struct rpcrdma_xprt *r_xprt, tail->iov_len)) return false; - if (req->rl_sendctx->sc_unmap_count) - kref_get(&req->rl_kref); return true; } @@ -726,7 +723,6 @@ static bool rpcrdma_prepare_readch(struct rpcrdma_xprt *r_xprt, len -= len & 3; if (!rpcrdma_prepare_tail_iov(req, xdr, page_base, len)) return false; - kref_get(&req->rl_kref); } return true; @@ -755,7 +751,6 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_nosc; req->rl_sendctx->sc_unmap_count = 0; req->rl_sendctx->sc_req = req; - kref_init(&req->rl_kref); req->rl_wr.wr_cqe = &req->rl_sendctx->sc_cqe; req->rl_wr.sg_list = req->rl_sendctx->sc_sges; req->rl_wr.num_sge = 0; @@ -783,10 +778,14 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_unmap; } + /* The Send-side owner releases this reference when the + * Send has completed. + */ + kref_get(&req->rl_kref); return 0; out_unmap: - rpcrdma_sendctx_unmap(req->rl_sendctx); + rpcrdma_sendctx_cancel(req->rl_sendctx); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; @@ -1364,14 +1363,6 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } -static void rpcrdma_reply_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - - rpcrdma_complete_rqst(req->rl_reply); -} - /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process @@ -1443,7 +1434,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) frwr_unmap_async(r_xprt, req); /* LocalInv completion will complete the RPC */ else - kref_put(&req->rl_kref, rpcrdma_reply_done); + rpcrdma_complete_rqst(rep); out_post: rpcrdma_post_recvs(r_xprt, diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 1a54993f7ffb..875ad852a11d 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -279,6 +279,13 @@ xprt_rdma_destroy(struct rpc_xprt *xprt) cancel_delayed_work_sync(&r_xprt->rx_connect_worker); rpcrdma_xprt_disconnect(r_xprt); + + /* The disconnect's sendctx drain can return bc_prealloc reqs + * to bc_pa_list after xprt_destroy_backchannel() emptied it. + */ +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + xprt_rdma_bc_destroy(xprt, 0); +#endif rpcrdma_buffer_destroy(&r_xprt->rx_buf); xprt_rdma_free_addresses(xprt); @@ -488,6 +495,45 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task) delay); } +/* rl_kref has two owners while a Send is outstanding: the rpc_rqst + * owner and the sendctx. Replies complete the RPC but do not drop + * either reference. The req returns to its free pool only after + * xprt_rdma_free_slot() or xprt_rdma_bc_free_rqst() has dropped the + * RPC-layer reference and rpcrdma_sendctx_unmap() has dropped the + * Send-side reference. + */ +static void rpcrdma_req_release(struct kref *kref) +{ + struct rpcrdma_req *req = + container_of(kref, struct rpcrdma_req, rl_kref); + struct rpc_rqst *rqst = &req->rl_slot; + struct rpc_xprt *xprt = rqst->rq_xprt; + struct rpcrdma_xprt *r_xprt; + + kref_init(&req->rl_kref); + +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + if (bc_prealloc(rqst)) { + spin_lock(&xprt->bc_pa_lock); + list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); + spin_unlock(&xprt->bc_pa_lock); + return; + } +#endif + + if (xprt_wake_up_backlog(xprt, rqst)) + return; + + r_xprt = rpcx_to_rdmax(xprt); + memset(rqst, 0, sizeof(*rqst)); + rpcrdma_buffer_put(&r_xprt->rx_buf, req); +} + +void rpcrdma_req_put(struct rpcrdma_req *req) +{ + kref_put(&req->rl_kref, rpcrdma_req_release); +} + /** * xprt_rdma_alloc_slot - allocate an rpc_rqst * @xprt: controlling RPC transport @@ -506,6 +552,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) req = rpcrdma_buffer_get(&r_xprt->rx_buf); if (!req) goto out_sleep; + kref_init(&req->rl_kref); task->tk_rqstp = &req->rl_slot; task->tk_status = 0; return; @@ -521,6 +568,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) if (req) { struct rpc_rqst *rqst = &req->rl_slot; + kref_init(&req->rl_kref); if (!xprt_wake_up_backlog(xprt, rqst)) { memset(rqst, 0, sizeof(*rqst)); rpcrdma_buffer_put(&r_xprt->rx_buf, req); @@ -541,10 +589,7 @@ xprt_rdma_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *rqst) container_of(xprt, struct rpcrdma_xprt, rx_xprt); rpcrdma_reply_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - if (!xprt_wake_up_backlog(xprt, rqst)) { - memset(rqst, 0, sizeof(*rqst)); - rpcrdma_buffer_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - } + rpcrdma_req_put(rpcr_to_rdmar(rqst)); } static bool rpcrdma_check_regbuf(struct rpcrdma_xprt *r_xprt, @@ -717,7 +762,7 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) r_xprt->rx_stats.mrs_allocated, r_xprt->rx_stats.local_inv_needed, r_xprt->rx_stats.empty_sendctx_q, - r_xprt->rx_stats.reply_waits_for_send); + 0LU); /* was reply_waits_for_send; column preserved */ } static int diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index aecf9c0a153f..97b8b2376602 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -65,6 +65,8 @@ static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt); static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt); +static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, + unsigned long item); static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_sendctx *sc); static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt); @@ -571,9 +573,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt) rpcrdma_xprt_drain(r_xprt); rpcrdma_reps_unmap(r_xprt); + rpcrdma_sendctxs_destroy(r_xprt); rpcrdma_reqs_reset(r_xprt); rpcrdma_mrs_destroy(r_xprt); - rpcrdma_sendctxs_destroy(r_xprt); if (rpcrdma_ep_put(ep)) rdma_destroy_id(id); @@ -605,6 +607,20 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) if (!buf->rb_sc_ctxs) return; + + /* The QP is drained, but the final unsignaled Sends might not + * have been walked by a signaled Send completion. Release those + * Send owners before request buffers are reset. + */ + for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); + i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); + i = rpcrdma_sendctx_next(buf, i)) { + struct rpcrdma_sendctx *sc = buf->rb_sc_ctxs[i]; + + if (sc && sc->sc_req) + rpcrdma_sendctx_unmap(sc); + } + for (i = 0; i <= buf->rb_sc_last; i++) kfree(buf->rb_sc_ctxs[i]); kfree(buf->rb_sc_ctxs); @@ -739,15 +755,20 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_buffer *buf = &r_xprt->rx_buf; unsigned long next_tail; - /* Unmap SGEs of previously completed but unsignaled - * Sends by walking up the queue until @sc is found. + /* Release previously completed but unsignaled Sends by walking + * up the queue until @sc is found. Entries left behind by a + * failed rpcrdma_prepare_send_sges() have sc_req cleared. */ next_tail = buf->rb_sc_tail; do { + struct rpcrdma_sendctx *cur; + next_tail = rpcrdma_sendctx_next(buf, next_tail); /* ORDER: item must be accessed _before_ tail is updated */ - rpcrdma_sendctx_unmap(buf->rb_sc_ctxs[next_tail]); + cur = buf->rb_sc_ctxs[next_tail]; + if (cur->sc_req) + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index f53a77472724..f879d9b9f57e 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -427,7 +427,6 @@ struct rpcrdma_stats { /* accessed when receiving a reply */ unsigned long long total_rdma_reply; unsigned long long fixup_copy_count; - unsigned long reply_waits_for_send; unsigned long local_inv_needed; unsigned long nomsg_call_count; unsigned long bcall_count; @@ -505,6 +504,7 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep); void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); +void rpcrdma_req_put(struct rpcrdma_req *req); bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags); From 64bf6892057b746c55bcc045b9492741b72d8d27 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:03 -0400 Subject: [PATCH 3106/5214] xprtrdma: Add request-pool slack for delayed recycling After the previous patch gates req recycling on Send completion, a completed RPC's rpcrdma_req can remain pinned by the sendctx ring until the next signaled Send completion releases it. The transmitted-RPC ceiling is unchanged: xprt_request_get_cong() gates Sends against xprt->cwnd, the RPC/RDMA credit window fed by server-granted credits and capped at re_max_requests. The req pool, however, must exceed max_reqs by enough that this recycle delay does not stall a slot allocation that the credit window would admit. The headroom is bounded. frwr_open() sets re_send_batch to re_max_requests >> 3 -- one in every eight Sends is signaled -- so at most re_send_batch unsignaled Sends can be outstanding before the next signaled completion releases them. That equals max_reqs / 8 reqs in the worst case, with a one-slot floor for small max_reqs values where the right-shift rounds to zero. The sendctx ring and the hardware Send Queue are not enlarged to match. Both are sized in rpcrdma_sendctxs_create() and frwr_query_device() for re_max_requests in-flight Sends, which is the ceiling the credit window enforces. The pool slack does not raise that ceiling -- it only lets allocation keep pace with the credit window during the brief interval in which earlier reqs are pinned waiting for the next signaled completion. At any moment, at most re_send_batch sendctxes are held by unswept unsignaled Sends, leaving the rest of the ring available for newly admitted Sends. Allocate max_reqs + DIV_ROUND_UP(max_reqs, 8) request objects and name the slack calculation at the allocation site so the 1/8 bound stays tied to the Send-signaling batch size. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 97b8b2376602..98bd965787e6 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1080,6 +1080,22 @@ static void rpcrdma_reps_destroy(struct rpcrdma_buffer *buf) spin_unlock(&buf->rb_lock); } +static unsigned int rpcrdma_req_pool_slack(unsigned int max_reqs) +{ + /* The sendctx ring can hold up to one Send-signaling batch + * (re_send_batch, set by frwr_open() to re_max_requests >> 3) + * of unfinished Sends. Each pins its req until a signaled Send + * completion releases the sendctx. Size the pool above max_reqs + * by that batch so the recycle delay does not stall a slot + * allocation that the RPC/RDMA credit window would admit. + * + * Round up: re_max_requests >> 3 is zero when max_reqs < 8, but + * a single unsignaled Send is still enough to pin one req. One + * slack slot covers that case. + */ + return DIV_ROUND_UP(max_reqs, 8); +} + /** * rpcrdma_buffer_create - Create initial set of req/rep objects * @r_xprt: transport instance to (re)initialize @@ -1089,6 +1105,7 @@ static void rpcrdma_reps_destroy(struct rpcrdma_buffer *buf) int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt) { struct rpcrdma_buffer *buf = &r_xprt->rx_buf; + unsigned int max_reqs; int i, rc; buf->rb_bc_srv_max_requests = 0; @@ -1102,7 +1119,9 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt) INIT_LIST_HEAD(&buf->rb_all_reps); rc = -ENOMEM; - for (i = 0; i < r_xprt->rx_xprt.max_reqs; i++) { + max_reqs = r_xprt->rx_xprt.max_reqs; + max_reqs += rpcrdma_req_pool_slack(max_reqs); + for (i = 0; i < max_reqs; i++) { struct rpcrdma_req *req; req = rpcrdma_req_create(r_xprt, From 2ae8e7afbc63bf84243367f89eb43571f0345a74 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:04 -0400 Subject: [PATCH 3107/5214] xprtrdma: Clear receive-side ownership pointers on release Three small ownership-state cleanups land the transport in a state that lets future reviewers reason about each pointer locally rather than tracing the whole reply path: rpcrdma_rep_put() clears rep->rr_rqst before the rep enters rb_free_reps so that no rep on the free list still carries a stale rqst pointer. rpcrdma_reply_handler() and rpcrdma_unpin_rqst() are the only sites that set rr_rqst; rpcrdma_reply_handler() hands the rep through rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst directly because its error path abandons the rep for teardown cleanup rather than returning it to rb_free_reps. rpcrdma_reply_put() NULLs req->rl_reply before calling rpcrdma_rep_put(). The previous order placed the rep on rb_free_reps while req->rl_reply still pointed at it; the window was harmless because xprt_rdma_free_slot() holds the req exclusively across the pair, but closing it makes the invariant 'rep on rb_free_reps implies no req references it' strictly checkable. rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear req->rl_sendctx after dropping the sendctx pointer in the sendctx ring. Without this, req->rl_sendctx survives across Send completion and points at a sendctx that may already have been reassigned by rpcrdma_sendctx_get_locked() to a different req. No caller dereferences the stale pointer today -- rpcrdma_prepare_send_sges() overwrites it before the next Send -- but a NULL is a more honest representation of 'the Send is no longer outstanding' and lets the assertion patch that follows trip on any future regression. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 ++++ net/sunrpc/xprtrdma/verbs.c | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 69380f9dfa49..f4b4abefc4e0 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -493,6 +493,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) rpcrdma_sendctx_dma_unmap(sc); sc->sc_req = NULL; + req->rl_sendctx = NULL; rpcrdma_req_put(req); } @@ -501,8 +502,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) */ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc) { + struct rpcrdma_req *req = sc->sc_req; + rpcrdma_sendctx_dma_unmap(sc); sc->sc_req = NULL; + req->rl_sendctx = NULL; } /* Prepare an SGE for the RPC-over-RDMA transport header. diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 98bd965787e6..60cbc14c5299 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1043,9 +1043,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf) * @buf: buffer pool * @rep: rep to release * + * The rep's transient association with an rpc_rqst, established + * by rpcrdma_reply_handler() and torn down here, must not survive + * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free + * list to re-post them, and a non-NULL rr_rqst on a free-listed rep + * would imply the rep is still referenced by a req. */ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep) { + rep->rr_rqst = NULL; llist_add(&rep->rr_node, &buf->rb_free_reps); } @@ -1247,9 +1253,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt) */ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) { - if (req->rl_reply) { - rpcrdma_rep_put(buffers, req->rl_reply); + struct rpcrdma_rep *rep = req->rl_reply; + + if (rep) { req->rl_reply = NULL; + rpcrdma_rep_put(buffers, rep); } } From 797943e8bd1ffcc63bfe79d24faad9a77054ec40 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:05 -0400 Subject: [PATCH 3108/5214] xprtrdma: Document and assert reply-handler invariants The xprtrdma reply path has been the subject of recurring LLM-driven review claims that 'an RPC can complete while receive buffers are still DMA-mapped' or that 'the req can be freed while the HCA still owns the send buffer.' No runtime reproducer has surfaced, but the absence of a written-down invariant set lets each pass of automated review reach the same hypothetical conclusion. Subsequent fixes against ce2f9a4d9ccc ('xprtrdma: Decouple req recycling from RPC completion') closed the underlying races but did not document the closure where future readers will look for it. State the invariants explicitly in a comment above rpcrdma_reply_handler() and back four of them with WARN_ON_ONCE() probes positioned where each invariant is locally checkable on the previous patch's cleaned-up ownership state: - I1 (Receive WR ownership): WARN at rpcrdma_post_recvs() that a rep pulled from rb_free_reps carries rr_rqst == NULL. - I2 (rep attachment): WARN at rpcrdma_reply_put() that req->rl_reply was NULLed before the matching rep_put. - I3 (Registered-MR fence): WARN at rpcrdma_complete_rqst() that req->rl_registered is empty. Strong send-queue ordering of the LocalInv WR chain makes the last completion observe the ib_dma_unmap_sg() of every earlier MR, so 'list empty' implies 'all MRs unmapped'. - I4 (Send-buffer release): WARN at rpcrdma_req_release() that req->rl_sendctx is NULL. Reaching the kref release callback requires both the RPC-layer and Send-side references to have dropped; the Send-side drop runs in rpcrdma_sendctx_unmap(), which clears rl_sendctx (previous patch). A non-NULL rl_sendctx here would mean the Send-side owner had not run -- a contradiction. The XXX comment in xprt_rdma_free() about signal-driven release racing the Send completion described the pre-decouple state. Replace it with a one-line note pointing at the invariant set, since the kref scheme now holds the req across the in-flight Send regardless of which path released the rpc_task. I5 (req lifecycle) is stated in the comment but not probed: making it locally assertible would require moving kref_init out of rpcrdma_req_release(), which in turn requires adding kref_init to the bc_pa_list and backlog-wake reuse paths. That restructuring is deferred -- the invariant is unchanged either way. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 69 +++++++++++++++++++++++++++++++++ net/sunrpc/xprtrdma/transport.c | 13 +++++-- net/sunrpc/xprtrdma/verbs.c | 6 +++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index f4b4abefc4e0..626cadec4555 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1336,6 +1336,11 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) struct rpc_rqst *rqst = rep->rr_rqst; int status; + /* I3: every registered MR has been invalidated and + * ib_dma_unmap_sg()'d before complete_rqst runs. + */ + WARN_ON_ONCE(!list_empty(&rpcr_to_rdmar(rqst)->rl_registered)); + switch (rep->rr_proc) { case rdma_msg: status = rpcrdma_decode_msg(r_xprt, rep, rqst); @@ -1367,6 +1372,70 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } +/* Reply-side ownership invariants + * + * I1 (Receive WR ownership). A struct rpcrdma_rep is owned by the + * HCA between ib_post_recv() and the matching Receive completion. + * After ib_dma_sync_single_for_cpu() in rpcrdma_wc_receive() it is + * owned by the CPU until rpcrdma_rep_put() returns it to + * rb_free_reps; a rep on rb_free_reps is not re-posted until + * rpcrdma_post_recvs() pulls it off. Asserted: rpcrdma_post_recvs() + * WARNs that a pulled rep has rr_rqst == NULL. + * + * I2 (rep attachment). While req->rl_reply == rep, the rep cannot be + * re-posted. rpcrdma_reply_put() NULLs req->rl_reply before handing + * the rep to rpcrdma_rep_put(). Asserted: rpcrdma_reply_put() WARNs + * that rl_reply is NULL after the put. + * + * I3 (Registered-MR fence). On entry to rpcrdma_complete_rqst() every + * MR that was on req->rl_registered has had its rkey invalidated + * (remotely via IB_WC_WITH_INVALIDATE or locally via IB_WR_LOCAL_INV) + * and its pages ib_dma_unmap_sg()'d. The LocalInv chain is posted + * on a single QP; strong send-queue ordering makes the last + * completion (frwr_wc_localinv_done) observe the + * ib_dma_unmap_sg() that ran from each earlier completion's + * frwr_mr_put() before complete_rqst is called. The inline + * frwr_reminv() path unmaps its one MR synchronously before + * rpcrdma_reply_handler() reaches complete_rqst. Asserted: + * rpcrdma_complete_rqst() WARNs that rl_registered is empty. + * + * I4 (Send-buffer release). req->rl_kref carries two unconditional + * owners while a Send is outstanding: the RPC-layer reference (set + * at xprt_rdma_alloc_slot / xprt_rdma_bc_rqst_get / rpcrdma_req_release + * pool-entry) and the Send-side reference (kref_get() in + * rpcrdma_prepare_send_sges()). rpcrdma_req_release() runs only + * after both have dropped, so the req does not return to its free + * pool until rpcrdma_sendctx_unmap() has fired -- the HCA has + * released the send buffer before the req can be reused. Asserted: + * rpcrdma_req_release() WARNs that rl_sendctx is NULL. + * + * I5 (req lifecycle). A req is owned by the RPC layer between slot + * acquisition and the matching xprt_rdma_free_slot() (or, for the + * backchannel, xprt_rdma_bc_free_rqst()). While owned, rl_kref >= 1. + * The pools (rb_send_bufs, bc_pa_list, backlog wake target) never + * contain a req with outstanding Send-side or Reply-side work. + * + * Non-hazards. The following claims have been raised by adversarial + * review and are each closed by the invariants above: + * + * * "Reply completes the RPC while the HCA still holds the send + * buffer" -- excluded by I4. The Send-side kref reference is held + * until rpcrdma_sendctx_unmap() runs from Send completion. + * + * * "Signal-driven release races the in-flight Send" -- same + * resolution. xprt_rdma_free() does not touch rl_kref; the + * Send-side reference keeps the req out of its pool until Send + * completion fires. + * + * * "Receive completion races rep reuse" -- excluded by I1. A rep + * is on rb_free_reps only after rpcrdma_rep_put() has been called + * and rpcrdma_post_recvs() owns the next transition back to the HCA. + * + * * "Pages still DMA-mapped when call_decode reads them" -- excluded + * by I3. The matching ib_dma_unmap_sg() for every MR has run on + * the same CPU thread that calls rpcrdma_complete_rqst(). + */ + /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 875ad852a11d..d4e6746d8ecd 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -510,6 +510,11 @@ static void rpcrdma_req_release(struct kref *kref) struct rpc_xprt *xprt = rqst->rq_xprt; struct rpcrdma_xprt *r_xprt; + /* I4: both the RPC-layer and Send-side owners have dropped, + * so rpcrdma_sendctx_unmap() has cleared rl_sendctx. + */ + WARN_ON_ONCE(req->rl_sendctx); + kref_init(&req->rl_kref); #if defined(CONFIG_SUNRPC_BACKCHANNEL) @@ -653,10 +658,10 @@ xprt_rdma_free(struct rpc_task *task) frwr_unmap_sync(rpcx_to_rdmax(rqst->rq_xprt), req); } - /* XXX: If the RPC is completing because of a signal and - * not because a reply was received, we ought to ensure - * that the Send completion has fired, so that memory - * involved with the Send is not still visible to the NIC. + /* The Send-side rl_kref owner keeps req out of its free pool + * until rpcrdma_sendctx_unmap() has fired -- see I4 above + * rpcrdma_reply_handler() -- so signal-driven release here + * does not let the HCA touch a recycled send buffer. */ } diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 60cbc14c5299..da2c6fa44154 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1259,6 +1259,10 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) req->rl_reply = NULL; rpcrdma_rep_put(buffers, rep); } + /* I2: rl_reply NULL after the put closes the + * 'rep on rb_free_reps still referenced by req' window. + */ + WARN_ON_ONCE(req->rl_reply); } /** @@ -1435,6 +1439,8 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) rep = rpcrdma_rep_create(r_xprt); if (!rep) break; + /* I1: a rep on rb_free_reps must carry no rqst pointer. */ + WARN_ON_ONCE(rep->rr_rqst); if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From 04dd9cdbe59a9056b4cc8de07e1585db77661cbc Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 1 Jun 2026 13:54:12 -0400 Subject: [PATCH 3109/5214] xprtrdma: Fix I3 invariant comment in rpcrdma_complete_rqst frwr_unmap_sync() and frwr_unmap_async() drain rl_registered via rpcrdma_mr_pop() before posting invalidation Work Requests to hardware. The WARN_ON_ONCE verifies that the list-drain step has occurred, not that hardware unmapping has completed. Reword the comment to match what the assertion actually checks. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 626cadec4555..f115baba6d56 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1336,8 +1336,8 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) struct rpc_rqst *rqst = rep->rr_rqst; int status; - /* I3: every registered MR has been invalidated and - * ib_dma_unmap_sg()'d before complete_rqst runs. + /* I3: rl_registered has been drained by frwr_unmap before + * complete_rqst runs. */ WARN_ON_ONCE(!list_empty(&rpcr_to_rdmar(rqst)->rl_registered)); From bf822d717edecfbf2e6b3eec1bef3813cbeacdd4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 1 Jun 2026 13:54:13 -0400 Subject: [PATCH 3110/5214] xprtrdma: Remove tautological I2 assertion in rpcrdma_reply_put rpcrdma_reply_put() sets req->rl_reply to NULL when it is non-NULL, and skips the block when it is already NULL. The WARN_ON_ONCE(req->rl_reply) that follows can never fire because both paths leave rl_reply NULL. Remove the dead assertion and its comment. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index da2c6fa44154..92c691d2521f 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1259,10 +1259,6 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) req->rl_reply = NULL; rpcrdma_rep_put(buffers, rep); } - /* I2: rl_reply NULL after the put closes the - * 'rep on rb_free_reps still referenced by req' window. - */ - WARN_ON_ONCE(req->rl_reply); } /** From 77b160b2d863d37f36b6c38e80a7d259ce939e69 Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 26 May 2026 16:29:53 -0700 Subject: [PATCH 3111/5214] NFSv4/pnfs: defer return_range callbacks until after inode unlock Sometimes unmounting an NFS filesystem mounted with pNFS SCSI layouts triggers the following warning: BUG: scheduling while atomic: umount.nfs4/... __schedule_bug+0xbd/0x100 schedule_debug.constprop.0+0x19f/0x220 __schedule+0x10d/0x10a0 schedule+0x74/0x190 schedule_timeout+0xf5/0x220 io_schedule_timeout+0xd5/0x160 __wait_for_common+0x186/0x4b0 blk_execute_rq+0x2ef/0x3a0 scsi_execute_cmd+0x1ff/0x700 sd_pr_out_command.isra.0+0x242/0x380 [sd_mod] bl_unregister_scsi.constprop.0+0x109/0x3c0 [blocklayoutdriver] bl_unregister_dev+0x175/0x1c0 [blocklayoutdriver] bl_free_device+0x1f/0x1b0 [blocklayoutdriver] bl_free_deviceid_node+0x12/0x30 [blocklayoutdriver] nfs4_put_deviceid_node+0x171/0x360 [nfsv4] ext_tree_remove+0x11c/0x1d0 [blocklayoutdriver] _pnfs_return_layout+0x416/0x900 [nfsv4] nfs4_evict_inode+0x108/0x130 [nfsv4] evict+0x316/0x750 dispose_list+0xf1/0x1a0 evict_inodes+0x33f/0x440 generic_shutdown_super+0xc9/0x4e0 kill_anon_super+0x3a/0x90 nfs_kill_super+0x44/0x60 [nfs] deactivate_locked_super+0xb8/0x1b0 cleanup_mnt+0x25a/0x380 task_work_run+0x13e/0x210 exit_to_user_mode_loop+0x169/0x400 do_syscall_64+0x467/0x1550 entry_SYSCALL_64_after_hwframe+0x76/0x7e The warning occurs because the block layout driver unregisters the SCSI device while the inode lock is still held. Device unregistration issues a SCSI PR command, which may sleep, resulting in a "scheduling while atomic" warning. During layout return, ext_tree_remove() invokes the layout driver's return_range callback while holding the inode lock. For block layouts, this callback eventually calls bl_unregister_scsi(), which may block in scsi_execute_cmd() while issuing PR commands to the device. Fix this by deferring the return_range callbacks until after the inode lock has been released. The layout header reference count is incremented before invoking return_range(), ensuring that the layout header remains valid while the layout driver removes extents from the extent tree. Fixes: c88953d87f5c8 ("pnfs: add return_range method") Signed-off-by: Dai Ngo Signed-off-by: Anna Schumaker --- fs/nfs/callback_proc.c | 9 +++++---- fs/nfs/pnfs.c | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 4ea9221ded42..10f2354ba304 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -257,6 +257,7 @@ static u32 initiate_file_draining(struct nfs_client *clp, struct pnfs_layout_hdr *lo; u32 rv = NFS4ERR_NOMATCHING_LAYOUT; LIST_HEAD(free_me_list); + bool return_range = false; ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid); if (IS_ERR(ino)) { @@ -301,13 +302,13 @@ static u32 initiate_file_draining(struct nfs_client *clp, /* Embrace your forgetfulness! */ rv = NFS4ERR_NOMATCHING_LAYOUT; - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) { - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, - &args->cbl_range); - } + return_range = true; } unlock: spin_unlock(&ino->i_lock); + if (return_range && NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, + &args->cbl_range); pnfs_free_lseg_list(&free_me_list); /* Free all lsegs that are attached to commit buckets */ nfs_commit_inode(ino, 0); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index cb203821a397..7715e2bd5871 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1463,8 +1463,6 @@ _pnfs_return_layout(struct inode *ino) pnfs_clear_layoutcommit(ino, &tmp_list); pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0); - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); /* Don't send a LAYOUTRETURN if list was initially empty */ if (!test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) || @@ -1476,6 +1474,8 @@ _pnfs_return_layout(struct inode *ino) send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL); spin_unlock(&ino->i_lock); + if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); if (send) status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, 0); From a01183bd6fc66910e2837830aa21dd8da2b7795e Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:25 +0200 Subject: [PATCH 3112/5214] ima: Remove ima_h_table structure The ima_h_table structure is a collection of IMA measurement list metadata - number of records in the IMA measurement list, number of integrity violations, and a hash table containing the IMA template data hash, needed to prevent measurement list record duplication. Removing records from the measurement list needs to be reflected in the hash table. As a pre-req to removing records from the measurement list, separate those counters from the hash table, remove the ima_h_table structure, and just replace the hash table pointer. Finally, rename ima_show_htable_value(), ima_show_htable_violations() and ima_htable_violations_ops respectively to ima_show_counter(), ima_show_num_violations() and ima_num_violations_ops. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 11 +++++------ security/integrity/ima/ima_api.c | 2 +- security/integrity/ima/ima_fs.c | 20 +++++++++----------- security/integrity/ima/ima_kexec.c | 2 +- security/integrity/ima/ima_queue.c | 15 ++++++++------- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 69e9bf0b82c6..b3ad7eac6a1e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -324,12 +324,11 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, */ extern spinlock_t ima_queue_lock; -struct ima_h_table { - atomic_long_t len; /* number of stored measurements in the list */ - atomic_long_t violations; - struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; -}; -extern struct ima_h_table ima_htable; +/* Total number of measurement list records since hard boot. */ +extern atomic_long_t ima_num_records; +/* Total number of violations since hard boot. */ +extern atomic_long_t ima_num_violations; +extern struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE]; static inline unsigned int ima_hash_key(u8 *digest) { diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 0916f24f005f..122d127e108d 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -146,7 +146,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename, int result; /* can overflow, only indicator */ - atomic_long_inc(&ima_htable.violations); + atomic_long_inc(&ima_num_violations); result = ima_alloc_init_template(&event_data, &entry, NULL); if (result < 0) { diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index ca4931a95098..523d3e81f631 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -38,8 +38,8 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup); static int valid_policy = 1; -static ssize_t ima_show_htable_value(char __user *buf, size_t count, - loff_t *ppos, atomic_long_t *val) +static ssize_t ima_show_counter(char __user *buf, size_t count, loff_t *ppos, + atomic_long_t *val) { char tmpbuf[32]; /* greater than largest 'long' string value */ ssize_t len; @@ -48,15 +48,14 @@ static ssize_t ima_show_htable_value(char __user *buf, size_t count, return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); } -static ssize_t ima_show_htable_violations(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) +static ssize_t ima_show_num_violations(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) { - return ima_show_htable_value(buf, count, ppos, &ima_htable.violations); + return ima_show_counter(buf, count, ppos, &ima_num_violations); } -static const struct file_operations ima_htable_violations_ops = { - .read = ima_show_htable_violations, +static const struct file_operations ima_num_violations_ops = { + .read = ima_show_num_violations, .llseek = generic_file_llseek, }; @@ -64,8 +63,7 @@ static ssize_t ima_show_measurements_count(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - return ima_show_htable_value(buf, count, ppos, &ima_htable.len); - + return ima_show_counter(buf, count, ppos, &ima_num_records); } static const struct file_operations ima_measurements_count_ops = { @@ -545,7 +543,7 @@ int __init ima_fs_init(void) } dentry = securityfs_create_file("violations", S_IRUSR | S_IRGRP, - ima_dir, NULL, &ima_htable_violations_ops); + ima_dir, NULL, &ima_num_violations_ops); if (IS_ERR(dentry)) { ret = PTR_ERR(dentry); goto out; diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 36a34c54de58..77ad370dbc37 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -43,7 +43,7 @@ void ima_measure_kexec_event(const char *event_name) int n; buf_size = ima_get_binary_runtime_size(); - len = atomic_long_read(&ima_htable.len); + len = atomic_long_read(&ima_num_records); n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 319522450854..6bdaefc790c3 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -32,11 +32,12 @@ static unsigned long binary_runtime_size; static unsigned long binary_runtime_size = ULONG_MAX; #endif +atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0); +atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0); + /* key: inode (before secure-hashing a file) */ -struct ima_h_table ima_htable = { - .len = ATOMIC_LONG_INIT(0), - .violations = ATOMIC_LONG_INIT(0), - .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT +struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE] = { + [0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT }; /* mutex protects atomicity of extending measurement list @@ -61,7 +62,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, key = ima_hash_key(digest_value); rcu_read_lock(); - hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { + hlist_for_each_entry_rcu(qe, &ima_htable[key], hnext) { rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest, digest_value, hash_digest_size[ima_hash_algo]); if ((rc == 0) && (qe->entry->pcr == pcr)) { @@ -113,10 +114,10 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, INIT_LIST_HEAD(&qe->later); list_add_tail_rcu(&qe->later, &ima_measurements); - atomic_long_inc(&ima_htable.len); + atomic_long_inc(&ima_num_records); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); - hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); + hlist_add_head_rcu(&qe->hnext, &ima_htable[key]); } if (binary_runtime_size != ULONG_MAX) { From 7bc01800a7739972626e366766f54c3e76cc3e69 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:26 +0200 Subject: [PATCH 3113/5214] ima: Replace static htable queue with dynamically allocated array The IMA hash table is a fixed-size array of hlist_head buckets: struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE]; IMA_MEASURE_HTABLE_SIZE is (1 << IMA_HASH_BITS) = 1024 buckets, each a struct hlist_head (one pointer, 8 bytes on 64-bit). That is 8 KiB allocated in BSS for every kernel, regardless of whether IMA is ever used, and regardless of how many measurements are actually made. Replace the fixed-size array with a RCU-protected pointer to a dynamically allocated array that is initialized in ima_init_htable(), which is called from ima_init() during early boot. ima_init_htable() calls the static function ima_alloc_replace_htable() which, other than initializing the hash table the first time, can also hot-swap the existing hash table with a blank one. The allocation in ima_alloc_replace_htable() uses kcalloc() so the buckets are zero-initialised (equivalent to HLIST_HEAD_INIT { .first = NULL }). Callers of ima_alloc_replace_htable() must call synchronize_rcu() and free the returned hash table. Finally, access the hash table with rcu_dereference() in ima_lookup_digest_entry() (reader side) and with rcu_dereference_protected() in ima_add_digest_entry() (writer side). No functional change: bucket count, hash function, and all locking remain identical. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 3 +- security/integrity/ima/ima_init.c | 5 ++++ security/integrity/ima/ima_queue.c | 48 ++++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index b3ad7eac6a1e..0e41c2113efd 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -311,6 +311,7 @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); int ima_measurements_show(struct seq_file *m, void *v); +int __init ima_init_htable(void); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); void ima_init_template_list(void); @@ -328,7 +329,7 @@ extern spinlock_t ima_queue_lock; extern atomic_long_t ima_num_records; /* Total number of violations since hard boot. */ extern atomic_long_t ima_num_violations; -extern struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE]; +extern struct hlist_head __rcu *ima_htable; static inline unsigned int ima_hash_key(u8 *digest) { diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index a2f34f2d8ad7..7e0aa09a12e6 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -140,6 +140,11 @@ int __init ima_init(void) rc = ima_init_digests(); if (rc != 0) return rc; + + rc = ima_init_htable(); + if (rc != 0) + return rc; + rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */ if (rc != 0) return rc; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 6bdaefc790c3..a31b75d9302b 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -36,9 +36,7 @@ atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0); atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0); /* key: inode (before secure-hashing a file) */ -struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE] = { - [0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT -}; +struct hlist_head __rcu *ima_htable; /* mutex protects atomicity of extending measurement list * and extending the TPM PCR aggregate. Since tpm_extend can take @@ -52,17 +50,53 @@ static DEFINE_MUTEX(ima_extend_list_mutex); */ static bool ima_measurements_suspended; +/* Callers must call synchronize_rcu() and free the hash table. */ +static struct hlist_head *ima_alloc_replace_htable(void) +{ + struct hlist_head *old_htable, *new_htable; + + /* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */ + new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head), + GFP_KERNEL); + if (!new_htable) + return ERR_PTR(-ENOMEM); + + old_htable = rcu_replace_pointer(ima_htable, new_htable, + lockdep_is_held(&ima_extend_list_mutex)); + + return old_htable; +} + +int __init ima_init_htable(void) +{ + struct hlist_head *old_htable; + + mutex_lock(&ima_extend_list_mutex); + old_htable = ima_alloc_replace_htable(); + mutex_unlock(&ima_extend_list_mutex); + + if (IS_ERR(old_htable)) + return PTR_ERR(old_htable); + + /* Synchronize_rcu() and kfree() not necessary, only for robustness. */ + synchronize_rcu(); + kfree(old_htable); + return 0; +} + /* lookup up the digest value in the hash table, and return the entry */ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, int pcr) { struct ima_queue_entry *qe, *ret = NULL; + struct hlist_head *htable; unsigned int key; int rc; key = ima_hash_key(digest_value); rcu_read_lock(); - hlist_for_each_entry_rcu(qe, &ima_htable[key], hnext) { + htable = rcu_dereference(ima_htable); + hlist_for_each_entry_rcu(qe, &htable[key], hnext) { rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest, digest_value, hash_digest_size[ima_hash_algo]); if ((rc == 0) && (qe->entry->pcr == pcr)) { @@ -102,6 +136,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, bool update_htable) { struct ima_queue_entry *qe; + struct hlist_head *htable; unsigned int key; qe = kmalloc_obj(*qe); @@ -114,10 +149,13 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, INIT_LIST_HEAD(&qe->later); list_add_tail_rcu(&qe->later, &ima_measurements); + htable = rcu_dereference_protected(ima_htable, + lockdep_is_held(&ima_extend_list_mutex)); + atomic_long_inc(&ima_num_records); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); - hlist_add_head_rcu(&qe->hnext, &ima_htable[key]); + hlist_add_head_rcu(&qe->hnext, &htable[key]); } if (binary_runtime_size != ULONG_MAX) { From 2fcebcd2aad24c13c27a6881a0866629f3ec57b2 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:27 +0200 Subject: [PATCH 3114/5214] ima: Introduce per binary measurements list type ima_num_records counter Make ima_num_records as an array, to have separate counters per binary measurements list type. Currently, define the BINARY type for the existing binary measurements list. No functional change: the BINARY type is equivalent to the value without the array. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 9 ++++++++- security/integrity/ima/ima_fs.c | 2 +- security/integrity/ima/ima_kexec.c | 2 +- security/integrity/ima/ima_queue.c | 6 ++++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0e41c2113efd..8f457f2c7b79 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -28,6 +28,13 @@ enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN, IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII }; enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 }; +/* + * BINARY: current binary measurements list + */ +enum binary_lists { + BINARY, BINARY__LAST +}; + /* digest size for IMA, fits SHA1 or MD5 */ #define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE #define IMA_EVENT_NAME_LEN_MAX 255 @@ -326,7 +333,7 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, extern spinlock_t ima_queue_lock; /* Total number of measurement list records since hard boot. */ -extern atomic_long_t ima_num_records; +extern atomic_long_t ima_num_records[BINARY__LAST]; /* Total number of violations since hard boot. */ extern atomic_long_t ima_num_violations; extern struct hlist_head __rcu *ima_htable; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 523d3e81f631..fcfcf7b6eae2 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -63,7 +63,7 @@ static ssize_t ima_show_measurements_count(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - return ima_show_counter(buf, count, ppos, &ima_num_records); + return ima_show_counter(buf, count, ppos, &ima_num_records[BINARY]); } static const struct file_operations ima_measurements_count_ops = { diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 77ad370dbc37..1a0211a12ea4 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -43,7 +43,7 @@ void ima_measure_kexec_event(const char *event_name) int n; buf_size = ima_get_binary_runtime_size(); - len = atomic_long_read(&ima_num_records); + len = atomic_long_read(&ima_num_records[BINARY]); n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index a31b75d9302b..012e725ed4fc 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -32,7 +32,9 @@ static unsigned long binary_runtime_size; static unsigned long binary_runtime_size = ULONG_MAX; #endif -atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0); +atomic_long_t ima_num_records[BINARY__LAST] = { + [0 ... BINARY__LAST - 1] = ATOMIC_LONG_INIT(0) +}; atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0); /* key: inode (before secure-hashing a file) */ @@ -152,7 +154,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, htable = rcu_dereference_protected(ima_htable, lockdep_is_held(&ima_extend_list_mutex)); - atomic_long_inc(&ima_num_records); + atomic_long_inc(&ima_num_records[BINARY]); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); hlist_add_head_rcu(&qe->hnext, &htable[key]); From 8f19da70f794f380a4b5aacfec681315a0a325c5 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:28 +0200 Subject: [PATCH 3115/5214] ima: Introduce per binary measurements list type binary_runtime_size value Make binary_runtime_size as an array, to have separate counters per binary measurements list type. Currently, define the BINARY type for the existing binary measurements list. Introduce ima_update_binary_runtime_size() to facilitate updating a binary_runtime_size value with a given binary measurement list type. Also add the binary measurements list type parameter to ima_get_binary_runtime_size(), to retrieve the desired value. Retrieving the value is now done under the ima_extend_list_mutex, since there can be concurrent updates. No functional change (except for the mutex usage, that fixes the concurrency issue): the BINARY array element is equivalent to the old binary_runtime_size. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_kexec.c | 5 ++-- security/integrity/ima/ima_queue.c | 40 +++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8f457f2c7b79..c00c133a140f 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -319,7 +319,7 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); int ima_measurements_show(struct seq_file *m, void *v); int __init ima_init_htable(void); -unsigned long ima_get_binary_runtime_size(void); +unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list); int ima_init_template(void); void ima_init_template_list(void); int __init ima_init_digests(void); diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 1a0211a12ea4..8dc9459622b3 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -42,7 +42,7 @@ void ima_measure_kexec_event(const char *event_name) long len; int n; - buf_size = ima_get_binary_runtime_size(); + buf_size = ima_get_binary_runtime_size(BINARY); len = atomic_long_read(&ima_num_records[BINARY]); n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, @@ -159,7 +159,8 @@ void ima_add_kexec_buffer(struct kimage *image) else extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; - binary_runtime_size = ima_get_binary_runtime_size() + extra_memory; + binary_runtime_size = ima_get_binary_runtime_size(BINARY) + + extra_memory; if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) kexec_segment_size = ULONG_MAX; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 012e725ed4fc..618694d5c082 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -27,9 +27,11 @@ static struct tpm_digest *digests; LIST_HEAD(ima_measurements); /* list of all measurements */ #ifdef CONFIG_IMA_KEXEC -static unsigned long binary_runtime_size; +static unsigned long binary_runtime_size[BINARY__LAST]; #else -static unsigned long binary_runtime_size = ULONG_MAX; +static unsigned long binary_runtime_size[BINARY__LAST] = { + [0 ... BINARY__LAST - 1] = ULONG_MAX +}; #endif atomic_long_t ima_num_records[BINARY__LAST] = { @@ -128,6 +130,20 @@ static int get_binary_runtime_size(struct ima_template_entry *entry) return size; } +static void ima_update_binary_runtime_size(struct ima_template_entry *entry, + enum binary_lists binary_list) +{ + int size; + + if (binary_runtime_size[binary_list] == ULONG_MAX) + return; + + size = get_binary_runtime_size(entry); + binary_runtime_size[binary_list] = + (binary_runtime_size[binary_list] < ULONG_MAX - size) ? + binary_runtime_size[binary_list] + size : ULONG_MAX; +} + /* ima_add_template_entry helper function: * - Add template entry to the measurement list and hash table, for * all entries except those carried across kexec. @@ -160,13 +176,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, hlist_add_head_rcu(&qe->hnext, &htable[key]); } - if (binary_runtime_size != ULONG_MAX) { - int size; - - size = get_binary_runtime_size(entry); - binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ? - binary_runtime_size + size : ULONG_MAX; - } + ima_update_binary_runtime_size(entry, BINARY); return 0; } @@ -175,12 +185,18 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, * entire binary_runtime_measurement list, including the ima_kexec_hdr * structure. */ -unsigned long ima_get_binary_runtime_size(void) +unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list) { - if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr))) + unsigned long val; + + mutex_lock(&ima_extend_list_mutex); + val = binary_runtime_size[binary_list]; + mutex_unlock(&ima_extend_list_mutex); + + if (val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr))) return ULONG_MAX; else - return binary_runtime_size + sizeof(struct ima_kexec_hdr); + return val + sizeof(struct ima_kexec_hdr); } static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr) From cb431ff6a92fc62d91ba64f04c7af3bb54017a1d Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:29 +0200 Subject: [PATCH 3116/5214] ima: Introduce _ima_measurements_start() and _ima_measurements_next() Introduce _ima_measurements_start() and _ima_measurements_next(), renamed from ima_measurements_start() and ima_measurements_next(), to include the list head as an additional parameter, so that iteration on different lists can be implemented by calling those functions. No functional change: ima_measurements_start() and ima_measurements_next() pass the ima_measurements list head, used before. They become wrappers for the new functions. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_fs.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index fcfcf7b6eae2..dcdc4cb8fa0f 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -72,14 +72,15 @@ static const struct file_operations ima_measurements_count_ops = { }; /* returns pointer to hlist_node */ -static void *ima_measurements_start(struct seq_file *m, loff_t *pos) +static void *_ima_measurements_start(struct seq_file *m, loff_t *pos, + struct list_head *head) { loff_t l = *pos; struct ima_queue_entry *qe; /* we need a lock since pos could point beyond last element */ rcu_read_lock(); - list_for_each_entry_rcu(qe, &ima_measurements, later) { + list_for_each_entry_rcu(qe, head, later) { if (!l--) { rcu_read_unlock(); return qe; @@ -89,7 +90,13 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) return NULL; } -static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) +static void *ima_measurements_start(struct seq_file *m, loff_t *pos) +{ + return _ima_measurements_start(m, pos, &ima_measurements); +} + +static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos, + struct list_head *head) { struct ima_queue_entry *qe = v; @@ -101,7 +108,12 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) rcu_read_unlock(); (*pos)++; - return (&qe->later == &ima_measurements) ? NULL : qe; + return (&qe->later == head) ? NULL : qe; +} + +static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) +{ + return _ima_measurements_next(m, v, pos, &ima_measurements); } static void ima_measurements_stop(struct seq_file *m, void *v) From 51bedcd803e0f140ee39e70a930d01223e1afb58 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:30 +0200 Subject: [PATCH 3117/5214] ima: Mediate open/release method of the measurements list Introduce the ima_measure_users counter, to implement a semaphore-like locking scheme where the binary and ASCII measurements list interfaces can be concurrently opened by multiple readers, or alternatively by a single writer. In addition, allow the same writer to open the other interfaces for write or read/write, so that it can see the same measurement state across all the interfaces. A semaphore cannot be used because the kernel cannot return to user space with a lock held. Introduce the ima_measure_lock() and ima_measure_unlock() primitives, to respectively lock/unlock the interfaces (safely with the ima_measure_users counter, without holding a lock). Finally, introduce _ima_measurements_open() to lock the interface before seq_open(), and call it from ima_measurements_open() and ima_ascii_measurements_open(). And, introduce ima_measurements_release(), to unlock the interface. Require CAP_SYS_ADMIN if the interface is opened for write (not possible for the current measurements interfaces, since they only have read permission). No functional changes: multiple readers are allowed as before. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_fs.c | 102 ++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index dcdc4cb8fa0f..91bd831d070f 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -25,6 +25,10 @@ #include "ima.h" static DEFINE_MUTEX(ima_write_mutex); +static DEFINE_MUTEX(ima_measure_mutex); +static long ima_measure_users; +static struct task_struct *measure_writer; +static long measure_writer_extra_writes; bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -209,16 +213,105 @@ static const struct seq_operations ima_measurments_seqops = { .show = ima_measurements_show }; +static int ima_measure_lock(bool write) +{ + mutex_lock(&ima_measure_mutex); + /* Overflow check. */ + if (!write && ima_measure_users == LONG_MAX) { + mutex_unlock(&ima_measure_mutex); + return -ENFILE; + } + + /* Same writer can do additional writes or read/writes. */ + if (write && current == measure_writer) { + measure_writer_extra_writes++; + mutex_unlock(&ima_measure_mutex); + return 0; + } + + /* + * ima_measure_users: > 0 open readers + * ima_measure_users: == -1 open writer + */ + if ((write && ima_measure_users != 0) || + (!write && ima_measure_users < 0)) { + mutex_unlock(&ima_measure_mutex); + return -EBUSY; + } + + if (write) { + ima_measure_users--; + /* Pointer valid, no reuse while the file descriptor is open. */ + measure_writer = current; + } else { + ima_measure_users++; + } + mutex_unlock(&ima_measure_mutex); + return 0; +} + +static void ima_measure_unlock(bool write) +{ + mutex_lock(&ima_measure_mutex); + /* Decrement additional writes or read/writes. */ + if (write && current == measure_writer && + measure_writer_extra_writes != 0) { + measure_writer_extra_writes--; + mutex_unlock(&ima_measure_mutex); + return; + } + if (write) { + ima_measure_users++; + measure_writer = NULL; + } else { + ima_measure_users--; + } + mutex_unlock(&ima_measure_mutex); +} + +static int _ima_measurements_open(struct inode *inode, struct file *file, + const struct seq_operations *seq_ops) +{ + bool write = (file->f_mode & FMODE_WRITE); + int ret; + + if (write && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = ima_measure_lock(write); + if (ret < 0) + return ret; + + ret = seq_open(file, seq_ops); + if (ret < 0) + ima_measure_unlock(write); + + return ret; +} + static int ima_measurements_open(struct inode *inode, struct file *file) { - return seq_open(file, &ima_measurments_seqops); + return _ima_measurements_open(inode, file, &ima_measurments_seqops); +} + +static int ima_measurements_release(struct inode *inode, struct file *file) +{ + bool write = (file->f_mode & FMODE_WRITE); + int ret; + + /* seq_release() always returns zero. */ + ret = seq_release(inode, file); + + ima_measure_unlock(write); + + return ret; } static const struct file_operations ima_measurements_ops = { .open = ima_measurements_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ima_measurements_release, }; void ima_print_digest(struct seq_file *m, u8 *digest, u32 size) @@ -283,14 +376,15 @@ static const struct seq_operations ima_ascii_measurements_seqops = { static int ima_ascii_measurements_open(struct inode *inode, struct file *file) { - return seq_open(file, &ima_ascii_measurements_seqops); + return _ima_measurements_open(inode, file, + &ima_ascii_measurements_seqops); } static const struct file_operations ima_ascii_measurements_ops = { .open = ima_ascii_measurements_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ima_measurements_release, }; static ssize_t ima_read_policy(char *path) From 56275ec7667adda1eea102911f76fb822dbfebc4 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:31 +0200 Subject: [PATCH 3118/5214] ima: Use snprintf() in create_securityfs_measurement_lists Use the more secure snprintf() function (accepting the buffer size) in create_securityfs_measurement_lists(). No functional change: sprintf() and snprintf() have the same behavior. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_fs.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 91bd831d070f..f6ecee2d7699 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -503,11 +503,13 @@ static int __init create_securityfs_measurement_lists(void) struct dentry *dentry; if (algo == HASH_ALGO__LAST) - sprintf(file_name, "ascii_runtime_measurements_tpm_alg_%x", - ima_tpm_chip->allocated_banks[i].alg_id); + snprintf(file_name, sizeof(file_name), + "ascii_runtime_measurements_tpm_alg_%x", + ima_tpm_chip->allocated_banks[i].alg_id); else - sprintf(file_name, "ascii_runtime_measurements_%s", - hash_algo_name[algo]); + snprintf(file_name, sizeof(file_name), + "ascii_runtime_measurements_%s", + hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, ima_dir, (void *)(uintptr_t)i, &ima_ascii_measurements_ops); @@ -515,11 +517,13 @@ static int __init create_securityfs_measurement_lists(void) return PTR_ERR(dentry); if (algo == HASH_ALGO__LAST) - sprintf(file_name, "binary_runtime_measurements_tpm_alg_%x", - ima_tpm_chip->allocated_banks[i].alg_id); + snprintf(file_name, sizeof(file_name), + "binary_runtime_measurements_tpm_alg_%x", + ima_tpm_chip->allocated_banks[i].alg_id); else - sprintf(file_name, "binary_runtime_measurements_%s", - hash_algo_name[algo]); + snprintf(file_name, sizeof(file_name), + "binary_runtime_measurements_%s", + hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, ima_dir, (void *)(uintptr_t)i, &ima_measurements_ops); From 292bc492f3d31ffd858600a331d599f1956bf612 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:32 +0200 Subject: [PATCH 3119/5214] ima: Introduce ima_dump_measurement() Introduce ima_dump_measurement() to simplify the code of ima_dump_measurement_list() and to avoid repeating the ima_dump_measurement() code block if iteration occurs on multiple lists. No functional change: only code moved to a separate function. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_kexec.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 8dc9459622b3..26d41974429e 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -80,6 +80,17 @@ static int ima_alloc_kexec_file_buf(size_t segment_size) return 0; } +static int ima_dump_measurement(struct ima_kexec_hdr *khdr, + struct ima_queue_entry *qe) +{ + if (ima_kexec_file.count >= ima_kexec_file.size) + return -EINVAL; + + khdr->count++; + ima_measurements_show(&ima_kexec_file, qe); + return 0; +} + static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, unsigned long segment_size) { @@ -97,13 +108,9 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, khdr.version = 1; /* This is an append-only list, no need to hold the RCU read lock */ list_for_each_entry_rcu(qe, &ima_measurements, later, true) { - if (ima_kexec_file.count < ima_kexec_file.size) { - khdr.count++; - ima_measurements_show(&ima_kexec_file, qe); - } else { - ret = -EINVAL; + ret = ima_dump_measurement(&khdr, qe); + if (ret < 0) break; - } } /* From e9b491e27bf6b9401e2e521955787a7a6e2bf808 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:33 +0200 Subject: [PATCH 3120/5214] ima: Add support for staging measurements with prompt Introduce the ability of staging the IMA measurement list and deleting them with a prompt. Staging means moving the current measurement list records to a separate location, and allowing users to read and delete it. This causes the current measurement list to be emptied (since records were moved) and new measurements to be added on the empty list. Staging can be done only once at a time. In the event of kexec(), staging is aborted and staged records will be carried over to the new kernel. Introduce ascii_runtime_measurements__staged and binary_runtime_measurements__staged interfaces to access and delete the measurements. Use 'echo A > ' and 'echo D > ' to respectively stage and delete the entire measurements list. Locking of these interfaces is also mediated with a call to _ima_measurements_open() and with ima_measurements_release(). Implement the staging functionality by introducing the new global measurements list ima_measurements_staged, and ima_queue_stage() and ima_queue_staged_delete_all() to respectively move measurements from the current measurements list to the staged one, and to move staged measurements to the ima_measurements_trim list for deletion. Introduce ima_queue_delete() to delete the measurements. Staging is forbidden after measurement is suspended, and between staging and deleting, so that walking the staged and current measurements list can be done locklessly in ima_dump_measurement_list(). Strict ordering of suspending and dumping is enforced by two reboot notifiers with different priority. Refusing to delete staged measurements also signals to user space that those measurements are already carried over to the secondary kernel, so that it does not save them twice. Finally, introduce the BINARY_STAGED and BINARY_FULL binary measurements list types, to maintain the counters and the binary size of staged measurements and the full measurements list (including records that were staged). BINARY still represents the current binary measurements list. Use the binary size for the BINARY + BINARY_STAGED types in ima_add_kexec_buffer(), since both measurements list types are copied to the secondary kernel during kexec. Use BINARY_FULL in ima_measure_kexec_event(), to generate a critical data record. It should be noted that the BINARY_FULL counter is not passed through kexec. Thus, the number of records included in the kexec critical data records refers to the records since the critical data records generated from the previous kexec event. Note: This code derives from the Alt-IMA Huawei project, whose license is GPL-2.0 OR MIT. Link: https://github.com/linux-integrity/linux/issues/1 Suggested-by: Gregory Lumen (staging revert) Signed-off-by: Roberto Sassu Tested-by: Stefan Berger Signed-off-by: Mimi Zohar --- security/integrity/ima/Kconfig | 12 ++ security/integrity/ima/ima.h | 7 +- security/integrity/ima/ima_fs.c | 174 ++++++++++++++++++++++++++--- security/integrity/ima/ima_kexec.c | 20 +++- security/integrity/ima/ima_queue.c | 140 ++++++++++++++++++++++- 5 files changed, 333 insertions(+), 20 deletions(-) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 862fbee2b174..02436670f746 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -332,4 +332,16 @@ config IMA_KEXEC_EXTRA_MEMORY_KB If set to the default value of 0, an extra half page of memory for those additional measurements will be allocated. +config IMA_STAGING + bool "Support for staging the measurements list" + default n + help + Add support for staging the measurements list. + + It allows user space to stage the measurements list for deletion and + to delete the staged measurements after confirmation. + + On kexec, staging is aborted and any staged measurement records are + copied to the secondary kernel. + endif diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c00c133a140f..3892d2a6c2e2 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -30,9 +30,11 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 }; /* * BINARY: current binary measurements list + * BINARY_STAGED: staged binary measurements list + * BINARY_FULL: binary measurements list since IMA init (lost after kexec) */ enum binary_lists { - BINARY, BINARY__LAST + BINARY, BINARY_STAGED, BINARY_FULL, BINARY__LAST }; /* digest size for IMA, fits SHA1 or MD5 */ @@ -125,6 +127,7 @@ struct ima_queue_entry { struct ima_template_entry *entry; }; extern struct list_head ima_measurements; /* list of all measurements */ +extern struct list_head ima_measurements_staged; /* list of staged meas. */ /* Some details preceding the binary serialized measurement list */ struct ima_kexec_hdr { @@ -315,6 +318,8 @@ struct ima_template_desc *ima_template_desc_current(void); struct ima_template_desc *ima_template_desc_buf(void); struct ima_template_desc *lookup_template_desc(const char *name); bool ima_template_has_modsig(const struct ima_template_desc *ima_template); +int ima_queue_stage(void); +int ima_queue_staged_delete_all(void); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); int ima_measurements_show(struct seq_file *m, void *v); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index f6ecee2d7699..96d7503a605b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -24,6 +24,13 @@ #include "ima.h" +/* + * Requests: + * 'A\n': stage the entire measurements list + * 'D\n': delete all staged measurements + */ +#define STAGED_REQ_LENGTH 21 + static DEFINE_MUTEX(ima_write_mutex); static DEFINE_MUTEX(ima_measure_mutex); static long ima_measure_users; @@ -99,6 +106,11 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) return _ima_measurements_start(m, pos, &ima_measurements); } +static void *ima_measurements_staged_start(struct seq_file *m, loff_t *pos) +{ + return _ima_measurements_start(m, pos, &ima_measurements_staged); +} + static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos, struct list_head *head) { @@ -120,6 +132,12 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) return _ima_measurements_next(m, v, pos, &ima_measurements); } +static void *ima_measurements_staged_next(struct seq_file *m, void *v, + loff_t *pos) +{ + return _ima_measurements_next(m, v, pos, &ima_measurements_staged); +} + static void ima_measurements_stop(struct seq_file *m, void *v) { } @@ -213,6 +231,13 @@ static const struct seq_operations ima_measurments_seqops = { .show = ima_measurements_show }; +static const struct seq_operations ima_measurments_staged_seqops = { + .start = ima_measurements_staged_start, + .next = ima_measurements_staged_next, + .stop = ima_measurements_stop, + .show = ima_measurements_show +}; + static int ima_measure_lock(bool write) { mutex_lock(&ima_measure_mutex); @@ -307,6 +332,60 @@ static int ima_measurements_release(struct inode *inode, struct file *file) return ret; } +static int ima_measurements_staged_open(struct inode *inode, struct file *file) +{ + return _ima_measurements_open(inode, file, + &ima_measurments_staged_seqops); +} + +static ssize_t _ima_measurements_write(struct file *file, + const char __user *buf, size_t datalen, + loff_t *ppos, bool staged_interface) +{ + char req[STAGED_REQ_LENGTH]; + int ret; + + if (datalen < 2 || datalen > STAGED_REQ_LENGTH) + return -EINVAL; + + if (copy_from_user(req, buf, datalen) != 0) + return -EFAULT; + + if (req[datalen - 1] != '\n') + return -EINVAL; + + req[datalen - 1] = '\0'; + + switch (req[0]) { + case 'A': + if (datalen != 2 || !staged_interface) + return -EINVAL; + + ret = ima_queue_stage(); + break; + case 'D': + if (datalen != 2 || !staged_interface) + return -EINVAL; + + ret = ima_queue_staged_delete_all(); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return datalen; +} + +static ssize_t ima_measurements_staged_write(struct file *file, + const char __user *buf, + size_t datalen, loff_t *ppos) +{ + return _ima_measurements_write(file, buf, datalen, ppos, true); +} + static const struct file_operations ima_measurements_ops = { .open = ima_measurements_open, .read = seq_read, @@ -314,6 +393,14 @@ static const struct file_operations ima_measurements_ops = { .release = ima_measurements_release, }; +static const struct file_operations ima_measurements_staged_ops = { + .open = ima_measurements_staged_open, + .read = seq_read, + .write = ima_measurements_staged_write, + .llseek = seq_lseek, + .release = ima_measurements_release, +}; + void ima_print_digest(struct seq_file *m, u8 *digest, u32 size) { u32 i; @@ -387,6 +474,28 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = ima_measurements_release, }; +static const struct seq_operations ima_ascii_measurements_staged_seqops = { + .start = ima_measurements_staged_start, + .next = ima_measurements_staged_next, + .stop = ima_measurements_stop, + .show = ima_ascii_measurements_show +}; + +static int ima_ascii_measurements_staged_open(struct inode *inode, + struct file *file) +{ + return _ima_measurements_open(inode, file, + &ima_ascii_measurements_staged_seqops); +} + +static const struct file_operations ima_ascii_measurements_staged_ops = { + .open = ima_ascii_measurements_staged_open, + .read = seq_read, + .write = ima_measurements_staged_write, + .llseek = seq_lseek, + .release = ima_measurements_release, +}; + static ssize_t ima_read_policy(char *path) { void *data = NULL; @@ -490,10 +599,21 @@ static const struct seq_operations ima_policy_seqops = { }; #endif -static int __init create_securityfs_measurement_lists(void) +static int __init create_securityfs_measurement_lists(bool staging) { + const struct file_operations *ascii_ops = &ima_ascii_measurements_ops; + const struct file_operations *binary_ops = &ima_measurements_ops; + umode_t permissions = (S_IRUSR | S_IRGRP); + const char *file_suffix = ""; int count = NR_BANKS(ima_tpm_chip); + if (staging) { + ascii_ops = &ima_ascii_measurements_staged_ops; + binary_ops = &ima_measurements_staged_ops; + permissions |= (S_IWUSR | S_IWGRP); + file_suffix = "_staged"; + } + if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) count++; @@ -504,29 +624,32 @@ static int __init create_securityfs_measurement_lists(void) if (algo == HASH_ALGO__LAST) snprintf(file_name, sizeof(file_name), - "ascii_runtime_measurements_tpm_alg_%x", - ima_tpm_chip->allocated_banks[i].alg_id); + "ascii_runtime_measurements_tpm_alg_%x%s", + ima_tpm_chip->allocated_banks[i].alg_id, + file_suffix); else snprintf(file_name, sizeof(file_name), - "ascii_runtime_measurements_%s", - hash_algo_name[algo]); - dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, + "ascii_runtime_measurements_%s%s", + hash_algo_name[algo], file_suffix); + dentry = securityfs_create_file(file_name, permissions, ima_dir, (void *)(uintptr_t)i, - &ima_ascii_measurements_ops); + ascii_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (algo == HASH_ALGO__LAST) snprintf(file_name, sizeof(file_name), - "binary_runtime_measurements_tpm_alg_%x", - ima_tpm_chip->allocated_banks[i].alg_id); + "binary_runtime_measurements_tpm_alg_%x%s", + ima_tpm_chip->allocated_banks[i].alg_id, + file_suffix); else snprintf(file_name, sizeof(file_name), - "binary_runtime_measurements_%s", - hash_algo_name[algo]); - dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, + "binary_runtime_measurements_%s%s", + hash_algo_name[algo], file_suffix); + + dentry = securityfs_create_file(file_name, permissions, ima_dir, (void *)(uintptr_t)i, - &ima_measurements_ops); + binary_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); } @@ -534,6 +657,23 @@ static int __init create_securityfs_measurement_lists(void) return 0; } +static int __init create_securityfs_staging_links(void) +{ + struct dentry *dentry; + + dentry = securityfs_create_symlink("binary_runtime_measurements_staged", + ima_dir, "binary_runtime_measurements_sha1_staged", NULL); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + dentry = securityfs_create_symlink("ascii_runtime_measurements_staged", + ima_dir, "ascii_runtime_measurements_sha1_staged", NULL); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + return 0; +} + /* * ima_open_policy: sequentialize access to the policy file */ @@ -626,7 +766,13 @@ int __init ima_fs_init(void) goto out; } - ret = create_securityfs_measurement_lists(); + ret = create_securityfs_measurement_lists(false); + if (ret == 0 && IS_ENABLED(CONFIG_IMA_STAGING)) { + ret = create_securityfs_measurement_lists(true); + if (ret == 0) + ret = create_securityfs_staging_links(); + } + if (ret != 0) goto out; diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 26d41974429e..0d845693a1f7 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -42,8 +42,8 @@ void ima_measure_kexec_event(const char *event_name) long len; int n; - buf_size = ima_get_binary_runtime_size(BINARY); - len = atomic_long_read(&ima_num_records[BINARY]); + buf_size = ima_get_binary_runtime_size(BINARY_FULL); + len = atomic_long_read(&ima_num_records[BINARY_FULL]); n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" @@ -106,13 +106,24 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, memset(&khdr, 0, sizeof(khdr)); khdr.version = 1; - /* This is an append-only list, no need to hold the RCU read lock */ - list_for_each_entry_rcu(qe, &ima_measurements, later, true) { + /* + * Lockless walks possible due to strict ordering of the reboot + * notifiers, suspending measurement before dump, and forbidding + * staging/deleting (list mutations) after suspend. + */ + list_for_each_entry(qe, &ima_measurements_staged, later) { ret = ima_dump_measurement(&khdr, qe); if (ret < 0) break; } + list_for_each_entry(qe, &ima_measurements, later) { + if (!ret) + ret = ima_dump_measurement(&khdr, qe); + if (ret < 0) + break; + } + /* * fill in reserved space with some buffer details * (eg. version, buffer size, number of measurements) @@ -167,6 +178,7 @@ void ima_add_kexec_buffer(struct kimage *image) extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; binary_runtime_size = ima_get_binary_runtime_size(BINARY) + + ima_get_binary_runtime_size(BINARY_STAGED) + extra_memory; if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 618694d5c082..cdc21e1b929b 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -26,6 +26,7 @@ static struct tpm_digest *digests; LIST_HEAD(ima_measurements); /* list of all measurements */ +LIST_HEAD(ima_measurements_staged); /* list of staged measurements */ #ifdef CONFIG_IMA_KEXEC static unsigned long binary_runtime_size[BINARY__LAST]; #else @@ -42,7 +43,7 @@ atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0); /* key: inode (before secure-hashing a file) */ struct hlist_head __rcu *ima_htable; -/* mutex protects atomicity of extending measurement list +/* mutex protects atomicity of extending and staging measurement list * and extending the TPM PCR aggregate. Since tpm_extend can take * long (and the tpm driver uses a mutex), we can't use the spinlock. */ @@ -171,12 +172,16 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, lockdep_is_held(&ima_extend_list_mutex)); atomic_long_inc(&ima_num_records[BINARY]); + atomic_long_inc(&ima_num_records[BINARY_FULL]); + if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); hlist_add_head_rcu(&qe->hnext, &htable[key]); } ima_update_binary_runtime_size(entry, BINARY); + ima_update_binary_runtime_size(entry, BINARY_FULL); + return 0; } @@ -277,6 +282,139 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, return result; } +/** + * ima_queue_stage - Stage all measurements + * + * If the staged measurements list is empty, the current measurements list is + * not empty, and measurement is not suspended, move the measurements from the + * current list to the staged one, and update the number of records and binary + * run-time size accordingly. + * + * Do not allow staging after measurement is suspended, so that dumping + * measurements can be done in a lockless way. + * + * Return: Zero on success, a negative value otherwise. + */ +int ima_queue_stage(void) +{ + int ret = 0; + + mutex_lock(&ima_extend_list_mutex); + if (!list_empty(&ima_measurements_staged)) { + ret = -EEXIST; + goto out_unlock; + } + + if (list_empty(&ima_measurements)) { + ret = -ENOENT; + goto out_unlock; + } + + if (ima_measurements_suspended) { + ret = -EACCES; + goto out_unlock; + } + + list_replace(&ima_measurements, &ima_measurements_staged); + INIT_LIST_HEAD(&ima_measurements); + + atomic_long_set(&ima_num_records[BINARY_STAGED], + atomic_long_read(&ima_num_records[BINARY])); + atomic_long_set(&ima_num_records[BINARY], 0); + + if (IS_ENABLED(CONFIG_IMA_KEXEC)) { + binary_runtime_size[BINARY_STAGED] = + binary_runtime_size[BINARY]; + binary_runtime_size[BINARY] = 0; + } +out_unlock: + mutex_unlock(&ima_extend_list_mutex); + return ret; +} + +static void ima_queue_delete(struct list_head *head); + +/** + * ima_queue_staged_delete_all - Delete staged measurements + * + * Move staged measurements to a temporary list, ima_measurements_trim, update + * the number of records and the binary run-time size accordingly. Finally, + * delete measurements in the temporary list. + * + * Refuse to delete staged measurements if measurement is suspended, so that + * dump can be done in a lockless way and user space is notified about staged + * measurements being carried over to the secondary kernel, so that it does not + * save them twice. + * + * Return: Zero on success, a negative value otherwise. + */ +int ima_queue_staged_delete_all(void) +{ + LIST_HEAD(ima_measurements_trim); + + mutex_lock(&ima_extend_list_mutex); + if (list_empty(&ima_measurements_staged)) { + mutex_unlock(&ima_extend_list_mutex); + return -ENOENT; + } + + if (ima_measurements_suspended) { + mutex_unlock(&ima_extend_list_mutex); + return -ESTALE; + } + + list_replace(&ima_measurements_staged, &ima_measurements_trim); + INIT_LIST_HEAD(&ima_measurements_staged); + + atomic_long_set(&ima_num_records[BINARY_STAGED], 0); + + if (IS_ENABLED(CONFIG_IMA_KEXEC)) + binary_runtime_size[BINARY_STAGED] = 0; + + mutex_unlock(&ima_extend_list_mutex); + + ima_queue_delete(&ima_measurements_trim); + return 0; +} + +/** + * ima_queue_delete - Delete measurements + * @head: List head measurements are deleted from + * + * Delete the measurements from the passed list head completely if the + * hash table is not enabled, or partially (only the template data), if the + * hash table is used. + */ +static void ima_queue_delete(struct list_head *head) +{ + struct ima_queue_entry *qe, *qe_tmp; + unsigned int i; + + list_for_each_entry_safe(qe, qe_tmp, head, later) { + /* + * Safe to free template_data here without synchronize_rcu() + * because the only htable reader, ima_lookup_digest_entry(), + * accesses only entry->digests, not template_data. If new + * htable readers are added that access template_data, a + * synchronize_rcu() is required here. + */ + for (i = 0; i < qe->entry->template_desc->num_fields; i++) { + kfree(qe->entry->template_data[i].data); + qe->entry->template_data[i].data = NULL; + qe->entry->template_data[i].len = 0; + } + + list_del(&qe->later); + + /* No leak if condition is false, referenced by ima_htable. */ + if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) { + kfree(qe->entry->digests); + kfree(qe->entry); + kfree(qe); + } + } +} + int ima_restore_measurement_entry(struct ima_template_entry *entry) { int result = 0; From c26d9d9246cc66e3472a2bbd186152d0572d7aab Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:34 +0200 Subject: [PATCH 3121/5214] ima: Add support for flushing the hash table when staging measurements During staging and delete, measurements are not completely deallocated. Their entry digest portion is kept and is still reachable with the hash table to detect duplicate records. If the number of records is significant, this reduces the memory saving benefit of staging. Some users might be interested in achieving the best memory saving (the measurements are completely deallocated) at the cost of having duplicate records across the staged measurement lists. Duplicate records are still avoided within the current measurement list. Introduce the new kernel option ima_flush_htable to decide whether or not the digests of staged measurement records are flushed from the hash table, when they are deleted, to achieve the maximum memory saving. When the option is enabled, replace the old hash table with a new one, by calling ima_alloc_replace_htable(), and completely delete the measurements records. Note: This code derives from the Alt-IMA Huawei project, whose license is GPL-2.0 OR MIT. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- .../admin-guide/kernel-parameters.txt | 6 +++ security/integrity/ima/ima_queue.c | 41 ++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4d0f545fb3ec..aad318803f82 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2343,6 +2343,12 @@ Kernel parameters Use the canonical format for the binary runtime measurements, instead of host native format. + ima_flush_htable [IMA] + Flush the IMA hash table when deleting all the + staged measurement records, to achieve maximum + memory saving at the cost of having duplicate + records across the staged measurement lists. + ima_hash= [IMA] Format: { md5 | sha1 | rmd160 | sha256 | sha384 | sha512 | ... } diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index cdc21e1b929b..df1e81ea7a36 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -22,6 +22,20 @@ #define AUDIT_CAUSE_LEN_MAX 32 +static bool ima_flush_htable; + +static int __init ima_flush_htable_setup(char *str) +{ + if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) { + pr_warn("Hash table not enabled, ignoring request to flush\n"); + return 1; + } + + ima_flush_htable = true; + return 1; +} +__setup("ima_flush_htable", ima_flush_htable_setup); + /* pre-allocated array of tpm_digest structures to extend a PCR */ static struct tpm_digest *digests; @@ -332,7 +346,7 @@ int ima_queue_stage(void) return ret; } -static void ima_queue_delete(struct list_head *head); +static void ima_queue_delete(struct list_head *head, bool flush_htable); /** * ima_queue_staged_delete_all - Delete staged measurements @@ -350,6 +364,7 @@ static void ima_queue_delete(struct list_head *head); */ int ima_queue_staged_delete_all(void) { + struct hlist_head *old_queue = NULL; LIST_HEAD(ima_measurements_trim); mutex_lock(&ima_extend_list_mutex); @@ -371,21 +386,35 @@ int ima_queue_staged_delete_all(void) if (IS_ENABLED(CONFIG_IMA_KEXEC)) binary_runtime_size[BINARY_STAGED] = 0; + if (ima_flush_htable) { + old_queue = ima_alloc_replace_htable(); + if (IS_ERR(old_queue)) { + mutex_unlock(&ima_extend_list_mutex); + return PTR_ERR(old_queue); + } + } + mutex_unlock(&ima_extend_list_mutex); - ima_queue_delete(&ima_measurements_trim); + if (ima_flush_htable) { + synchronize_rcu(); + kfree(old_queue); + } + + ima_queue_delete(&ima_measurements_trim, ima_flush_htable); return 0; } /** * ima_queue_delete - Delete measurements * @head: List head measurements are deleted from + * @flush_htable: Whether or not the hash table is being flushed * * Delete the measurements from the passed list head completely if the - * hash table is not enabled, or partially (only the template data), if the - * hash table is used. + * hash table is not enabled or is being flushed, or partially (only the + * template data), if the hash table is used. */ -static void ima_queue_delete(struct list_head *head) +static void ima_queue_delete(struct list_head *head, bool flush_htable) { struct ima_queue_entry *qe, *qe_tmp; unsigned int i; @@ -407,7 +436,7 @@ static void ima_queue_delete(struct list_head *head) list_del(&qe->later); /* No leak if condition is false, referenced by ima_htable. */ - if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) { + if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) || flush_htable) { kfree(qe->entry->digests); kfree(qe->entry); kfree(qe); From fcb0318a29696c13c9f8af0109855793a34371e6 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:35 +0200 Subject: [PATCH 3122/5214] ima: Support staging and deleting N measurements records Add support for sending a value N between 1 and ULONG_MAX to the IMA original measurement interface. This value represents the number of measurements that should be deleted from the current measurements list. In this case, measurements are staged in an internal non-user visible list, and immediately deleted. This staging method allows the remote attestation agents to easily separate the measurements that were verified (staged and deleted) from those that weren't due to the race between taking a TPM quote and reading the measurements list. In order to minimize the locking time of ima_extend_list_mutex, deleting N records is realized by doing a lockless walk in the current measurements list to determine the N-th entry to cut, to cut the current measurements list under the lock, and by deleting the excess records after releasing the lock. Flushing the hash table is not supported for N records, since it would require removing the N records one by one from the hash table under the ima_extend_list_mutex lock, which would increase the locking time. Link: https://github.com/linux-integrity/linux/issues/1 Co-developed-by: Steven Chen Signed-off-by: Steven Chen Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- security/integrity/ima/Kconfig | 3 ++ security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_fs.c | 32 +++++++++++++-- security/integrity/ima/ima_queue.c | 65 +++++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 02436670f746..f4d25e045808 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -341,6 +341,9 @@ config IMA_STAGING It allows user space to stage the measurements list for deletion and to delete the staged measurements after confirmation. + Or, alternatively, it allows user space to specify N measurements + records to stage internally, so that they can be immediately deleted. + On kexec, staging is aborted and any staged measurement records are copied to the secondary kernel. diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3892d2a6c2e2..caaedd4b58fd 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -320,6 +320,7 @@ struct ima_template_desc *lookup_template_desc(const char *name); bool ima_template_has_modsig(const struct ima_template_desc *ima_template); int ima_queue_stage(void); int ima_queue_staged_delete_all(void); +int ima_queue_delete_partial(unsigned long req_value); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); int ima_measurements_show(struct seq_file *m, void *v); @@ -342,6 +343,7 @@ extern atomic_long_t ima_num_records[BINARY__LAST]; /* Total number of violations since hard boot. */ extern atomic_long_t ima_num_violations; extern struct hlist_head __rcu *ima_htable; +extern bool ima_flush_htable; static inline unsigned int ima_hash_key(u8 *digest) { diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 96d7503a605b..174a94740da1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -28,6 +28,7 @@ * Requests: * 'A\n': stage the entire measurements list * 'D\n': delete all staged measurements + * '[1, ULONG_MAX]\n' delete N measurements records */ #define STAGED_REQ_LENGTH 21 @@ -343,6 +344,7 @@ static ssize_t _ima_measurements_write(struct file *file, loff_t *ppos, bool staged_interface) { char req[STAGED_REQ_LENGTH]; + unsigned long req_value; int ret; if (datalen < 2 || datalen > STAGED_REQ_LENGTH) @@ -370,7 +372,24 @@ static ssize_t _ima_measurements_write(struct file *file, ret = ima_queue_staged_delete_all(); break; default: - ret = -EINVAL; + if (staged_interface) + return -EINVAL; + + if (ima_flush_htable) { + pr_debug("Deleting staged N measurements not supported when flushing the hash table is requested\n"); + return -EINVAL; + } + + ret = kstrtoul(req, 10, &req_value); + if (ret < 0) + return ret; + + if (req_value == 0) { + pr_debug("Must delete at least one entry\n"); + return -EINVAL; + } + + ret = ima_queue_delete_partial(req_value); } if (ret < 0) @@ -379,6 +398,12 @@ static ssize_t _ima_measurements_write(struct file *file, return datalen; } +static ssize_t ima_measurements_write(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + return _ima_measurements_write(file, buf, datalen, ppos, false); +} + static ssize_t ima_measurements_staged_write(struct file *file, const char __user *buf, size_t datalen, loff_t *ppos) @@ -389,6 +414,7 @@ static ssize_t ima_measurements_staged_write(struct file *file, static const struct file_operations ima_measurements_ops = { .open = ima_measurements_open, .read = seq_read, + .write = ima_measurements_write, .llseek = seq_lseek, .release = ima_measurements_release, }; @@ -470,6 +496,7 @@ static int ima_ascii_measurements_open(struct inode *inode, struct file *file) static const struct file_operations ima_ascii_measurements_ops = { .open = ima_ascii_measurements_open, .read = seq_read, + .write = ima_measurements_write, .llseek = seq_lseek, .release = ima_measurements_release, }; @@ -603,14 +630,13 @@ static int __init create_securityfs_measurement_lists(bool staging) { const struct file_operations *ascii_ops = &ima_ascii_measurements_ops; const struct file_operations *binary_ops = &ima_measurements_ops; - umode_t permissions = (S_IRUSR | S_IRGRP); + umode_t permissions = (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP); const char *file_suffix = ""; int count = NR_BANKS(ima_tpm_chip); if (staging) { ascii_ops = &ima_ascii_measurements_staged_ops; binary_ops = &ima_measurements_staged_ops; - permissions |= (S_IWUSR | S_IWGRP); file_suffix = "_staged"; } diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index df1e81ea7a36..f89f0ca3d4ed 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -22,7 +22,7 @@ #define AUDIT_CAUSE_LEN_MAX 32 -static bool ima_flush_htable; +bool ima_flush_htable; static int __init ima_flush_htable_setup(char *str) { @@ -405,6 +405,69 @@ int ima_queue_staged_delete_all(void) return 0; } +/** + * ima_queue_delete_partial - Delete current measurements + * @req_value: Number of measurements to delete + * + * Delete the requested number of measurements from the current measurements + * list, and update the number of records and the binary run-time size + * accordingly. + * + * Refuse to delete current measurements if measurement is suspended, so that + * dump can be done in a lockless way and user space is notified about current + * measurements being carried over to the secondary kernel, so that it does not + * save them twice. + * + * Return: Zero on success, a negative value otherwise. + */ +int ima_queue_delete_partial(unsigned long req_value) +{ + unsigned long req_value_copy = req_value; + unsigned long size_to_remove = 0, num_to_remove = 0; + LIST_HEAD(ima_measurements_trim); + struct ima_queue_entry *qe; + int ret = 0; + + /* + * list_for_each_entry_rcu() without rcu_read_lock() is fine because + * only list append can happen concurrently. No list replace due to the + * staging/delete writers mutual exclusion. + */ + list_for_each_entry_rcu(qe, &ima_measurements, later, true) { + size_to_remove += get_binary_runtime_size(qe->entry); + num_to_remove++; + + if (--req_value_copy == 0) + break; + } + + /* Not enough records to delete. */ + if (req_value_copy > 0) + return -ENOENT; + + mutex_lock(&ima_extend_list_mutex); + if (ima_measurements_suspended) { + mutex_unlock(&ima_extend_list_mutex); + return -ESTALE; + } + + /* + * qe remains valid because ima_fs.c enforces single-writer exclusion. + */ + __list_cut_position(&ima_measurements_trim, &ima_measurements, + &qe->later); + + atomic_long_sub(num_to_remove, &ima_num_records[BINARY]); + + if (IS_ENABLED(CONFIG_IMA_KEXEC)) + binary_runtime_size[BINARY] -= size_to_remove; + + mutex_unlock(&ima_extend_list_mutex); + + ima_queue_delete(&ima_measurements_trim, false); + return ret; +} + /** * ima_queue_delete - Delete measurements * @head: List head measurements are deleted from From 35d6f5e788dae0dcc4c42d1280360f19aef9ab52 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Fri, 5 Jun 2026 19:22:36 +0200 Subject: [PATCH 3123/5214] doc: security: Add documentation of exporting and deleting IMA measurements Add the documentation of exporting and deleting IMA measurements in Documentation/security/IMA-export-delete.rst. Also add the missing Documentation/security/IMA-templates.rst file in MAINTAINERS. Link: https://github.com/linux-integrity/linux/issues/1 Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar --- Documentation/security/IMA-export-delete.rst | 203 +++++++++++++++++++ Documentation/security/index.rst | 1 + MAINTAINERS | 2 + 3 files changed, 206 insertions(+) create mode 100644 Documentation/security/IMA-export-delete.rst diff --git a/Documentation/security/IMA-export-delete.rst b/Documentation/security/IMA-export-delete.rst new file mode 100644 index 000000000000..1600ead03b03 --- /dev/null +++ b/Documentation/security/IMA-export-delete.rst @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================== +IMA Measurements Export and Delete +================================== + + +Introduction +============ + +The IMA measurements list is currently stored in the kernel memory. Memory +occupation grows linearly with the number of records, and can become a +problem especially in environments with reduced resources. + +While there is an advantage in keeping the IMA measurements list in kernel +memory, so that it is always available for reading from the securityfs +interfaces, storing it elsewhere would make it possible to free precious +memory for other kernel usage. + +The IMA measurements list needs to be retained and safely stored for new +attestation servers to validate it. Assuming the IMA measurements list is +properly saved, storing it outside the kernel does not introduce security +issues, since its integrity is anyway protected by the TPM. + +Hence, the new IMA staging mechanism is introduced to export IMA +measurements to user space and delete them from kernel space. + +Staging consists in atomically moving the current measurements list to a +temporary list, so that measurements can be deleted afterwards. The staging +operation locks the hot path (racing with addition of new measurements) for +a very short time, only for swapping the list pointers. Deletion of the +measurements instead is done locklessly, away from the hot path. + +There are two flavors of the staging mechanism. In the staging with prompt, +all current measurements are staged, read and deleted upon confirmation. In +the staging and deleting flavor, N measurements are staged from the +beginning of the current measurements list and immediately deleted without +confirmation. + + +Management of Staged Measurements +================================= + +Since with the staging mechanism measurement records are removed from the +kernel, the staged measurements need to be saved in a storage and +concatenated together, so that they can be presented during remote +attestation as if staging was never done. This task can be accomplished by +a remote attestation agent modified to support staging, or a system +service. + +Coordination is necessary in the case where there are multiple actors +requesting measurements to be staged. + +In the staging with prompt case, the measurement interfaces can be accessed +only by one actor (writer) at a time, so the others will get an error until +the former closes it. Since the actors don't care about N, when they gain +access to the interface, they will get all the staged measurements at the +time of their request. + +In the case of staging and deleting, coordination is more important, since +there is the risk that two actors unaware of each other compute the value N +on the current measurements list and request IMA to stage N twice. + + +Remote Attestation Agent Workflow +================================= + +Remote attestation agents can be configured to always present all the +measurements to the remote verifiers or, alternatively, to only provide the +measurements that have not been verified yet by the remote verifiers. + +In the latter case, determining which measurements need to be sent and +verified must solely depend on the remote verifier. The remote attestation +agent can proactively send partial measurements, at the condition that they +are the ones that the remote verifier needs. + +An agent can rely on one of the supported staging methods to proactively +send to a remote verifier the measurements since the previous request up +to the ones that verify the TPM quote obtained in the current request. +The workflow with each staging method is the following. + +With staging with prompt, the agent stages the current measurements list, +reads and stores the measurements in a storage and immediately requests +IMA to delete the staged measurements from kernel memory. Afterwards, it +calculates N by replaying the PCR extend on the stored measurements until +the calculated PCRs match the quoted PCRs. It then keeps the measurements +in excess for the next attestation request. + +At the next attestation request, the agent performs the same steps above, +and concatenates the new measurements to the ones in excess from the +previous request. Also in this case, the agent replays the PCR extend until +it matches the currently quoted PCRs, keeps the measurements in excess and +presents the new N measurement records to the remote attestation server. + +With the staging and deleting method, the agent reads the current +measurements list, calculates N and requests IMA to delete only those. The +measurements in excess are kept in the IMA measurements list and can be +retrieved at the next remote attestation request. + +While keeping only the excess measurements in the storage could be +sufficient to serve the requests of a remote verifier, it is advised to +keep all the obtained measurements locally, as they might be needed for the +attestation with a different remote verifier. + + +Usage +===== + +The IMA staging mechanism can be enabled from the kernel configuration with +the CONFIG_IMA_STAGING option. This option prevents inadvertently removing +the IMA measurement list on systems which do not properly save it. + +If the option is enabled, IMA duplicates the current securityfs +measurements interfaces (both binary and ASCII), by adding the ``_staged`` +file suffix. Both the original and the staging interfaces gain the write +permission for the root user and group, but require the process to have +CAP_SYS_ADMIN set. + +The staging mechanism supports two flavors. + + +Staging with prompt +~~~~~~~~~~~~~~~~~~~ + +The current measurements list is moved to a temporary staging area, +allowing it to be saved to external storage, before being deleted upon +confirmation. + +This staging process is achieved with the following steps. + + 1. ``echo A > <_staged interface>``: the user requests IMA to stage the + entire measurements list; + 2. ``cat <_staged interface>``: the user reads the staged measurements; + 3. ``echo D > <_staged interface>``: the user requests IMA to delete + staged measurements. + + +Staging and deleting +~~~~~~~~~~~~~~~~~~~~ + +N measurements are staged to a temporary staging area, and immediately +deleted without further confirmation. + +This staging process is achieved with the following steps. + + 1. ``cat ``: the user reads the current measurements + list and determines what the value N for staging should be; + 2. ``echo N > ``: the user requests IMA to delete N + measurements from the current measurements list. + + +Interface Access +================ + +In order to avoid the IMA measurements list being suddenly truncated by the +staging mechanism during a read, or having multiple concurrent staging, a +semaphore-like locking scheme has been implemented on all the measurements +list interfaces. + +Multiple readers can access concurrently the original and staged +interfaces, and they can be in mutual exclusion with one writer. In order +to see the same state across all the measurement interfaces, the same +writer is allowed to open multiple interfaces for write or read/write. + +If an illegal access occurs, the open to the measurements list interface is +denied. + + +Kexec +===== + +In the event a kexec() system call occurs between staging and deleting, the +staged measurement records are marshalled before the current measurements +list, so that they are both available when the secondary kernel starts. + +If measurement is suspended before requesting to delete staged or current +measurements, IMA returns an error to user space to let it know that +marshalling is already in progress, so that it does not save the +measurements twice. + +IMA also disallows staging when suspending measurement, to avoid the +situation where neither measurements are carried over to the secondary +kernel, nor they are saved by user space to the storage. + + +Hash table +========== + +By default, the template digest of staged measurement records are kept in +kernel memory (only template data are freed), to be able to detect +duplicate records independently of staging. + +The new kernel option ``ima_flush_htable`` has been introduced to +explicitly request a complete deletion of the staged measurements, for +maximum kernel memory saving. If the option has been specified, duplicate +records are still avoided on records of the current measurements list, +but there can be duplicates between different groups of staged +measurements. + +Flushing the hash table is supported only for the staging with prompt +flavor. For the staging and deleting flavor, it would have been necessary +to lock the hot path adding new measurements for the time needed to remove +each selected measurement individually. diff --git a/Documentation/security/index.rst b/Documentation/security/index.rst index 3e0a7114a862..00650dcf38cb 100644 --- a/Documentation/security/index.rst +++ b/Documentation/security/index.rst @@ -8,6 +8,7 @@ Security Documentation credentials snp-tdx-threat-model IMA-templates + IMA-export-delete keys/index lsm lsm-development diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..2bbfd7bbbab3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12740,6 +12740,8 @@ R: Eric Snowberg L: linux-integrity@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git +F: Documentation/security/IMA-export-delete.rst +F: Documentation/security/IMA-templates.rst F: include/linux/secure_boot.h F: security/integrity/ F: security/integrity/ima/ From 3ff72e1cdf5c337b6acfcf3fcef748c5b9a5316b Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Tue, 2 Jun 2026 19:04:38 +0800 Subject: [PATCH 3124/5214] nfs: keep PG_UPTODATE clear after read errors in page groups When a read request is split into multiple subrequests, earlier completions may advance PG_UPTODATE state for the page group once their bytes fall within hdr->good_bytes. If a later subrequest in the same group then completes with NFS_IOHDR_ERROR, the read path needs to clear any accumulated PG_UPTODATE state and keep later completions from rebuilding it. Otherwise, a subsequent successful subrequest can re-enter nfs_page_group_set_uptodate(), restore the page-group sync state, and leave stale PG_UPTODATE behind for nfs_page_group_destroy() to trip over in nfs_free_request(). Add a sticky page-group read-failed flag. Once any subrequest in the group is known to be bad, mark the group failed, clear any accumulated PG_UPTODATE state, and refuse further PG_UPTODATE synchronization for the rest of the completion walk. Fixes: 67d0338edd71 ("nfs: page group syncing in read path") Signed-off-by: Clark Wang Signed-off-by: Anna Schumaker --- fs/nfs/read.c | 25 ++++++++++++++++++++++++- include/linux/nfs_page.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index e1fe78d7b8d0..2b70bd2b934b 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -132,10 +132,32 @@ static void nfs_readpage_release(struct nfs_page *req, int error) static void nfs_page_group_set_uptodate(struct nfs_page *req) { - if (nfs_page_group_sync_on_bit(req, PG_UPTODATE)) + bool uptodate = false; + + nfs_page_group_lock(req); + if (!test_bit(PG_READ_FAILED, &req->wb_head->wb_flags) && + nfs_page_group_sync_on_bit_locked(req, PG_UPTODATE)) + uptodate = true; + nfs_page_group_unlock(req); + + if (uptodate) folio_mark_uptodate(nfs_page_to_folio(req)); } +static void nfs_page_group_mark_read_failed(struct nfs_page *req) +{ + struct nfs_page *tmp; + + nfs_page_group_lock(req); + set_bit(PG_READ_FAILED, &req->wb_head->wb_flags); + tmp = req; + do { + clear_bit(PG_UPTODATE, &tmp->wb_flags); + tmp = tmp->wb_this_page; + } while (tmp != req); + nfs_page_group_unlock(req); +} + static void nfs_read_completion(struct nfs_pgio_header *hdr) { unsigned long bytes = 0; @@ -172,6 +194,7 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr) if (bytes <= hdr->good_bytes) nfs_page_group_set_uptodate(req); else { + nfs_page_group_mark_read_failed(req); error = hdr->error; xchg(&nfs_req_openctx(req)->error, error); } diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index afe1d8f09d89..4b9a35dbc062 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -33,6 +33,7 @@ enum { PG_TEARDOWN, /* page group sync for destroy */ PG_UNLOCKPAGE, /* page group sync bit in read path */ PG_UPTODATE, /* page group sync bit in read path */ + PG_READ_FAILED, /* page group saw a read error */ PG_WB_END, /* page group sync bit in write path */ PG_REMOVE, /* page group sync bit in write path */ PG_CONTENDED1, /* Is someone waiting for a lock? */ From 17d90b68c3a3d7d7e95b49e1fe9381a723f637a8 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 3 Jun 2026 09:36:52 +0800 Subject: [PATCH 3125/5214] sunrpc: fix uninitialized xprt_create_args structure The xprt_create_args structure is allocated on the stack without initialization in rpc_sysfs_xprt_switch_add_xprt_store(). While some fields are manually populated, critical fields like srcaddr, bc_xps, and flags contain uninitialized stack garbage. This can lead to: 1. Kernel panic when xs_setup_xprt() dereferences garbage srcaddr 2. Information leak if srcaddr points to sensitive stack data 3. Unpredictable behavior if flags has random bits set The fix is to zero-initialize the structure to ensure all unused fields are NULL/0, preventing the transport setup code from acting on garbage data. Cc: stable@vger.kernel.org Suggested-by: Jeff Layton Reviewed-by: Jeff Layton Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index 49686bf740e6..e638b92b7ad1 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -327,7 +327,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, { struct rpc_xprt_switch *xprt_switch = rpc_sysfs_xprt_switch_kobj_get_xprt(kobj); - struct xprt_create xprt_create_args; + struct xprt_create xprt_create_args = {}; struct rpc_xprt *xprt, *new; if (!xprt_switch) From 7a375cafc14ed151508f908ea5681caf0a9cc1d6 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:02 -0400 Subject: [PATCH 3126/5214] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS on fatal DS connect errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") teaches ff_layout_{read,write}_pagelist() to return PNFS_NOT_ATTEMPTED when nfs4_ff_layout_prepare_ds() fails with a nfs_error_is_fatal() errno (e.g. -ETIMEDOUT from a SOFTCONN connect deadline, -ENOMEM, -ERESTARTSYS), so that the client gives up instead of spinning. pnfs_do_{read,write}() then dispatches the I/O through pnfs_{read,write}_through_mds() → nfs_pageio_reset_{read,write}_mds(). That fallback is unconditional and silently violates FF_FLAGS_NO_IO_THRU_MDS: when the layout segment carries the flag (typically single-mirror appliance layouts where MDS I/O is explicitly forbidden), the out_failed: path's \`&& !ds_fatal_error\` clause overrides the flag's short-circuit through ff_layout_avoid_mds_available_ds() and routes the I/O to the MDS file handle anyway. This is reachable in practice during a data-server restart: SOFTCONN exhaustion produces -ETIMEDOUT, which is fatal per nfs_error_is_fatal(), which triggers PNFS_NOT_ATTEMPTED, which silently goes to MDS. Preserve the upstream "don't spin on fatal errors" intent for layouts that permit MDS fallback. For layouts with FF_FLAGS_NO_IO_THRU_MDS set, mark the layout for return and request PNFS_TRY_AGAIN instead; if the server cannot supply a usable layout the failure now surfaces cleanly via pnfs_update_layout(), rather than via silent MDS I/O that contradicts the flag. Fixes: f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/flexfilelayout/flexfilelayout.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 99caa8d28c25..0a4a06d93e6a 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2204,6 +2204,14 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_read_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_READ, NFS_I(hdr->inode)->layout, lseg); @@ -2289,6 +2297,14 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_write_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_RW, NFS_I(hdr->inode)->layout, lseg); From 1d62e659c0bf11649cf48e002c2a55d148f2610a Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:03 -0400 Subject: [PATCH 3127/5214] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS in pg_get_mirror_count_write The FF_FLAGS_NO_IO_THRU_MDS flag lives on each lseg, so any fallback decision made when there is no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) cannot run the per-lseg check. Introduce a sticky hdr-level ditto for FF_FLAGS_NO_IO_THRU_MDS in struct nfs4_flexfile_layout::flags (NFS4_FF_HDR_NO_IO_THRU_MDS bit), set whenever ff_layout_alloc_lseg() parses an lseg with the flag. The bit is never cleared for the lifetime of the layout hdr; the server is assumed to be consistent in its no-fallback policy per file. kzalloc() in ff_layout_alloc_layout_hdr() zero-initializes the field. Use the new ff_layout_hdr_no_fallback_to_mds() helper to gate ff_layout_pg_get_mirror_count_write(): when pnfs_update_layout() returns NULL (e.g. NFS_LAYOUT_BULK_RECALL, pnfs_layout_io_test_failed, pnfs_layoutgets_blocked) the existing code unconditionally calls nfs_pageio_reset_write_mds(). This is a source of unwanted WRITE to MDS. Fix it by checking NFS4_FF_HDR_NO_IO_THRU_MDS bit, and if set surface -EAGAIN instead; the writepage-side caller (nfs_do_writepage() for buffered, nfs_direct_write_reschedule() for O_DIRECT) then redirties the request so writeback retries via pNFS. Fixes: 260074cd8413 ("pNFS/flexfiles: Add support for FF_FLAGS_NO_IO_THRU_MDS") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker --- fs/nfs/flexfilelayout/flexfilelayout.c | 13 +++++++++++++ fs/nfs/flexfilelayout/flexfilelayout.h | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 0a4a06d93e6a..c4aa995026f6 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -636,6 +636,9 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_sort_mirrors; fls->flags = be32_to_cpup(p); + if (fls->flags & FF_FLAGS_NO_IO_THRU_MDS) + set_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lh)->flags); p = xdr_inline_decode(&stream, 4); if (!p) @@ -1185,6 +1188,16 @@ ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio, 0, NFS4_MAX_UINT64, IOMODE_RW, NFS_I(pgio->pg_inode)->layout, pgio->pg_lseg); + if (NFS_I(pgio->pg_inode)->layout && + ff_layout_hdr_no_fallback_to_mds(NFS_I(pgio->pg_inode)->layout)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: no current lseg but the server's + * policy forbids MDS fallback. Surface -EAGAIN so writeback + * retries rather than silently issuing the WRITE via MDS. + */ + pgio->pg_error = -EAGAIN; + goto out; + } /* no lseg means that pnfs is not in use, so no mirroring here */ nfs_pageio_reset_write_mds(pgio); out: diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h index 17a008c8e97c..a5bd00f69e82 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.h +++ b/fs/nfs/flexfilelayout/flexfilelayout.h @@ -112,12 +112,16 @@ struct nfs4_ff_layout_segment { struct nfs4_ff_layout_mirror *mirror_array[] __counted_by(mirror_array_cnt); }; +/* nfs4_flexfile_layout::flags bit indices */ +#define NFS4_FF_HDR_NO_IO_THRU_MDS 0 /* any lseg has had FF_FLAGS_NO_IO_THRU_MDS */ + struct nfs4_flexfile_layout { struct pnfs_layout_hdr generic_hdr; struct pnfs_ds_commit_info commit_info; struct list_head mirrors; struct list_head error_list; /* nfs4_ff_layout_ds_err */ ktime_t last_report_time; /* Layoutstat report times */ + unsigned long flags; }; struct nfs4_flexfile_layoutreturn_args { @@ -184,6 +188,18 @@ ff_layout_no_fallback_to_mds(struct pnfs_layout_segment *lseg) return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_IO_THRU_MDS; } +/* + * Sticky hdr-level mirror of FF_FLAGS_NO_IO_THRU_MDS so callers that have + * no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) can + * still honor the no-MDS-fallback policy. + */ +static inline bool +ff_layout_hdr_no_fallback_to_mds(struct pnfs_layout_hdr *lo) +{ + return test_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lo)->flags); +} + static inline bool ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg) { From e3a78029444777b7bb75693cfa8090b189e47cdc Mon Sep 17 00:00:00 2001 From: Dylan Yudaken Date: Sun, 7 Jun 2026 08:31:54 +0100 Subject: [PATCH 3128/5214] nfs: add nowait version of nfs_start_io_direct nfs_start_io_direct might block on existing operations to the same inode. In order to support NOWAIT O_DIRECT reads, add a non-blocking version of this nfs_start_io_direct that just returns -EAGAIN if locks could not be taken. Signed-off-by: Dylan Yudaken Signed-off-by: Anna Schumaker --- fs/nfs/internal.h | 1 + fs/nfs/io.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 18d46b0e71dd..0c9aca624353 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -532,6 +532,7 @@ extern void nfs_end_io_read(struct inode *inode); extern __must_check int nfs_start_io_write(struct inode *inode); extern void nfs_end_io_write(struct inode *inode); extern __must_check int nfs_start_io_direct(struct inode *inode); +extern __must_check int nfs_start_io_direct_nowait(struct inode *inode); extern void nfs_end_io_direct(struct inode *inode); static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi) diff --git a/fs/nfs/io.c b/fs/nfs/io.c index 8337f0ae852d..2faf2003faf6 100644 --- a/fs/nfs/io.c +++ b/fs/nfs/io.c @@ -109,6 +109,16 @@ static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode) } } +static int nfs_block_buffered_nowait(struct nfs_inode *nfsi, struct inode *inode) +{ + if (!test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { + if (inode->i_mapping->nrpages != 0) + return 1; + set_bit(NFS_INO_ODIRECT, &nfsi->flags); + } + return 0; +} + /** * nfs_start_io_direct - declare the file is being used for direct i/o * @inode: file inode @@ -149,6 +159,37 @@ nfs_start_io_direct(struct inode *inode) return 0; } +/** + * nfs_start_io_direct_nowait - non-blocking variant of nfs_start_io_direct() + * @inode: file inode + * + * Try to declare that a direct I/O operation is about to start without + * blocking. + * Ensure all buffered I/O is blocked. + * If this could not be done without blocking then returns -EAGAIN. + */ +int +nfs_start_io_direct_nowait(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (!down_read_trylock(&inode->i_rwsem)) + return -EAGAIN; + if (test_bit(NFS_INO_ODIRECT, &nfsi->flags)) + return 0; + up_read(&inode->i_rwsem); + + /* Slow path: try to flip NFS_INO_ODIRECT without blocking. */ + if (!down_write_trylock(&inode->i_rwsem)) + return -EAGAIN; + if (nfs_block_buffered_nowait(nfsi, inode)) { + up_write(&inode->i_rwsem); + return -EAGAIN; + } + downgrade_write(&inode->i_rwsem); + return 0; +} + /** * nfs_end_io_direct - declare that the direct i/o operation is done * @inode: file inode From ef74e4453856716dbdaba06eaee5251e37e6882e Mon Sep 17 00:00:00 2001 From: Dylan Yudaken Date: Sun, 7 Jun 2026 08:31:55 +0100 Subject: [PATCH 3129/5214] nfs: expose FMODE_NOWAIT for read-only files NFS O_DIRECT reads already (mostly) handle async requests, with the exception of locking the inode for direct. Handle async requests properly by using nfs_start_io_direct_nowait, and then expose FMODE_NOWAIT since it's now supported for direct reads. Signed-off-by: Dylan Yudaken Signed-off-by: Anna Schumaker --- fs/nfs/direct.c | 12 ++++++++++-- fs/nfs/file.c | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 48d89716193a..e626c72495e6 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -466,14 +466,22 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, goto out_release; } dreq->l_ctx = l_ctx; - if (!is_sync_kiocb(iocb)) + if (!is_sync_kiocb(iocb)) { dreq->iocb = iocb; + } else if (iocb->ki_flags & IOCB_NOWAIT) { + result = -EAGAIN; + nfs_direct_req_release(dreq); + goto out_release; + } if (user_backed_iter(iter)) dreq->flags = NFS_ODIRECT_SHOULD_DIRTY; if (!swap) { - result = nfs_start_io_direct(inode); + if (iocb->ki_flags & IOCB_NOWAIT) + result = nfs_start_io_direct_nowait(inode); + else + result = nfs_start_io_direct(inode); if (result) { /* release the reference that would usually be * consumed by nfs_direct_read_schedule_iovec() diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 25048a3c2364..a0d8f1c1cf10 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -72,8 +72,12 @@ nfs_file_open(struct inode *inode, struct file *filp) return res; res = nfs_open(inode, filp); - if (res == 0) + if (res == 0) { filp->f_mode |= FMODE_CAN_ODIRECT; + /* flag NOWAIT on read-only files only */ + if (!(filp->f_mode & FMODE_WRITE)) + filp->f_mode |= FMODE_NOWAIT; + } return res; } @@ -166,6 +170,10 @@ nfs_file_read(struct kiocb *iocb, struct iov_iter *to) if (iocb->ki_flags & IOCB_DIRECT) return nfs_file_direct_read(iocb, to, false); + /* NOWAIT only supported on direct reads */ + if (iocb->ki_flags & IOCB_NOWAIT) + return -EAGAIN; + dprintk("NFS: read(%pD2, %zu@%lu)\n", iocb->ki_filp, iov_iter_count(to), (unsigned long) iocb->ki_pos); @@ -705,6 +713,12 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from) trace_nfs_file_write(iocb, from); + /* + * FMODE_NOWAIT is not set for writable files + */ + if (WARN_ON_ONCE(iocb->ki_flags & IOCB_NOWAIT)) + return -EAGAIN; + result = nfs_key_timeout_notify(file, inode); if (result) return result; From 48dbe473219832788c3940c84faa7f1df4375d5d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 3 Jun 2026 11:57:33 -0400 Subject: [PATCH 3130/5214] KVM: guest_memfd: fix NUMA interleave index double-counting kvm_gmem_get_policy() sets the interleave index (the output param that's typically named "ilx") to the full page offset (vm_pgoff + vma offset). But get_vma_policy() adds the page offset on top of the interleave index, and so the offset is counted twice. This causes NUMA interleaving to skip nodes: for order-0 pages the effective index jumps by 2 for each consecutive page. The vm_op.get_policy() implementation should return only a per-file bias in the interleave index (like shmem_get_policy does with inode->i_ino), letting get_vma_policy() add the page-offset component. Fix by setting the output interleave index to the inode number (a la shmem) instead of the full page offset, as the index is intended to be a constant, semi-random value for a given file, e.g. so that interleaving doesn't start at the same node for every file, and so that allocations are round-robined across nodes based on the page offset (the selected node would bounce/skip around if the index isn't constant). Found by Sashiko (sashiko.dev) AI code review. Fixes: ed1ffa810bd6 ("KVM: guest_memfd: Enforce NUMA mempolicy using shared policy") Cc: Sean Christopherson Cc: Paolo Bonzini Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Michael S. Tsirkin Acked-by: David Hildenbrand (Arm) Reviewed-by: Shivank Garg Tested-by: Shivank Garg Fixes: 7f3779a3ac3e ("mm/filemap: Add NUMA mempolicy support to filemap_alloc_folio()") Link: https://patch.msgid.link/0eff0a90667b900bee837d06b5db5025e1f304b5.1780501924.git.mst@redhat.com [sean: use reverse fir-tree, massage changelog] Signed-off-by: Sean Christopherson --- virt/kvm/guest_memfd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index f54502640b08..557aac91fc0a 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -438,11 +438,12 @@ static int kvm_gmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpo } static struct mempolicy *kvm_gmem_get_policy(struct vm_area_struct *vma, - unsigned long addr, pgoff_t *pgoff) + unsigned long addr, pgoff_t *ilx) { + pgoff_t pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); struct inode *inode = file_inode(vma->vm_file); - *pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); + *ilx = inode->i_ino; /* * Return the memory policy for this index, or NULL if none is set. @@ -453,7 +454,7 @@ static struct mempolicy *kvm_gmem_get_policy(struct vm_area_struct *vma, * can then replace NULL with the default memory policy instead of the * current task's memory policy. */ - return mpol_shared_policy_lookup(&GMEM_I(inode)->policy, *pgoff); + return mpol_shared_policy_lookup(&GMEM_I(inode)->policy, pgoff); } #endif /* CONFIG_NUMA */ From 4721f8160f17554b003e8928bb61e6c9b2fe92a3 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sat, 6 Jun 2026 23:44:52 +0900 Subject: [PATCH 3131/5214] KVM: x86: hyper-v: Bound the bank index when querying sparse banks When checking if a VP ID is included in a sparse bank set, explicitly check that the ID can actually be contained in a sparse bank (the TLFS allows for a maximum of 64 banks of 64 vCPUs each). When handling a paravirtual TLB flush for L2, the VP ID is copied verbatim from the enlightened VMCS, without any bounds check, i.e. isn't guaranteed to be under the limit of 4096. Failure to check the bounds of the VP ID leads to an out-of-bounds read when testing the sparse bank, and super strictly speaking could lead to KVM performing an unnecessary TLB flush for an L2 vCPU. ================================================================== BUG: KASAN: use-after-free in hv_is_vp_in_sparse_set+0x85/0x100 [kvm] Read of size 8 at addr ffff88811ba5f598 by task hyperv_evmcs/2802 CPU: 12 UID: 1000 PID: 2802 Comm: hyperv_evmcs Not tainted 7.1.0-rc2 #7 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Call Trace: dump_stack_lvl+0x51/0x60 print_report+0xcb/0x5d0 kasan_report+0xb4/0xe0 kasan_check_range+0x35/0x1b0 hv_is_vp_in_sparse_set+0x85/0x100 [kvm] kvm_hv_flush_tlb+0xe9e/0x16c0 [kvm] kvm_hv_hypercall+0xe6b/0x1e60 [kvm] vmx_handle_exit+0x485/0x1b60 [kvm_intel] kvm_arch_vcpu_ioctl_run+0x22e3/0x5070 [kvm] kvm_vcpu_ioctl+0x5d0/0x10c0 [kvm] __x64_sys_ioctl+0x129/0x1a0 do_syscall_64+0xb9/0xcf0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f0e62d1a9bf The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffffffffffffffff pfn:0x11ba5f flags: 0x4000000000000000(zone=1) raw: 4000000000000000 0000000000000000 00000000ffffffff 0000000000000000 raw: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88811ba5f480: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f500: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff >ffff88811ba5f580: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff88811ba5f600: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f680: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ================================================================== Disabling lock debugging due to kernel taint Opportunistically add a compile time assertion to ensure the maximum number of sparse banks exactly matches the number of possible bits in the passed in mask. Cc: stable@vger.kernel.org Fixes: c58a318f6090 ("KVM: x86: hyper-v: L2 TLB flush") Signed-off-by: Hyunwoo Kim Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/aiQyZIJtO-2Aj_xN@v4bel [sean: add KASAN splat, drop comment, add assert, massage changelog] Signed-off-by: Sean Christopherson --- arch/x86/kvm/hyperv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 3551af9a9453..fd4eb1e561f7 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1839,6 +1839,11 @@ static bool hv_is_vp_in_sparse_set(u32 vp_id, u64 valid_bank_mask, u64 sparse_ba int valid_bit_nr = vp_id / HV_VCPUS_PER_SPARSE_BANK; unsigned long sbank; + BUILD_BUG_ON(BITS_PER_TYPE(valid_bank_mask) != HV_MAX_SPARSE_VCPU_BANKS); + + if (valid_bit_nr >= HV_MAX_SPARSE_VCPU_BANKS) + return false; + if (!test_bit(valid_bit_nr, (unsigned long *)&valid_bank_mask)) return false; From 9c6e2463c9f3b506f89a5a557b9416286db6e532 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 3 Jun 2026 16:19:04 -0700 Subject: [PATCH 3132/5214] KVM: x86/pmu: Use hardware value when reprogramming for FIXED_CTR_CTRL changes When (conditionally) reprogramming fixed counters, use the hardware value of FIXED_CTR_CTRL to detect changes, not the guest's original value. For guests with a mediated PMU, overwriting fixed_ctr_ctrl_hw at the start of reprogramming without actually reacting to changes in fixed_ctr_ctrl_hw can lead to KVM ignoring PMU event filters. E.g. if the guest attempts to enable a fixed PMC that is disallowed, and then toggles a different PMC in a subsequent WRMSR, KVM will update pmu->fixed_ctr_ctrl_hw and reprogram the PMC that is changing, but not the others that are now effectively enabled in pmu->fixed_ctr_ctrl_hw. Note, the perf-based PMU is unaffected, as it doesn't use fixed_ctr_ctrl_hw (which is also why keying off fixed_ctr_ctrl_hw works for both PMUs. Note #2, fixed_ctr_ctrl_hw won't mess up pmc_in_use either, because the latter isn't used by the mediated PMU. Its purpose is solely to release perf events that are no longer being actively used, and the meadiated PMU obviously doesn't create perf events. Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260528005419.0228F1F00A3A@smtp.kernel.org Link: https://patch.msgid.link/20260603231905.1738487-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/pmu_intel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 27eb76e6b6a0..3281cc3e95c6 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -56,8 +56,16 @@ static struct x86_pmu_lbr *vcpu_to_lbr_records(struct kvm_vcpu *vcpu) static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data) { + /* + * Compare against the value the mediated PMU shoves into hardware, not + * the guest's desired value. For the emulated PMU (proxied via perf), + * they are one and the same (fixed_ctr_ctrl_hw isn't used other than + * here). For the mediated PMU, KVM needs to reprogram the actual MSR, + * and so needs to react to potential changes in the value shoved into + * hardware, e.g. to ensure the event filter is enforced. + */ + u64 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl_hw; struct kvm_pmc *pmc; - u64 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl; int i; pmu->fixed_ctr_ctrl = data; From 2c725bd2802fe7afef77318654cf589ff6588743 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 3 Jun 2026 16:19:05 -0700 Subject: [PATCH 3133/5214] KVM: selftests: Add regression test for mediated PMU fixed counter filter bug Add a regression test where KVM would inadvertently ignore PMU event filters on writes that change _some_ bits in FIXED_CTR_CTRL, but not the enable bits for PMCs that are denied to the guest. Link: https://patch.msgid.link/20260603231905.1738487-3-seanjc@google.com Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/x86/pmu_event_filter_test.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c index c1232344fda8..84e4c6ca67a3 100644 --- a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c @@ -731,6 +731,8 @@ static void test_filter_ioctl(struct kvm_vcpu *vcpu) static void intel_run_fixed_counter_guest_code(u8 idx) { + u8 nr_fixed_counters = this_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + for (;;) { wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); wrmsr(MSR_CORE_PERF_FIXED_CTR0 + idx, 0); @@ -738,6 +740,10 @@ static void intel_run_fixed_counter_guest_code(u8 idx) /* Only OS_EN bit is enabled for fixed counter[idx]. */ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, FIXED_PMC_CTRL(idx, FIXED_PMC_KERNEL)); wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, FIXED_PMC_GLOBAL_CTRL_ENABLE(idx)); + if (nr_fixed_counters > 1) + wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, + FIXED_PMC_CTRL(idx, FIXED_PMC_KERNEL) | + FIXED_PMC_CTRL((idx + 1) % nr_fixed_counters, FIXED_PMC_KERNEL)); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); From 65aa483f32ec674f0506658329d43403cafe5eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Wed, 3 Jun 2026 13:45:04 +0200 Subject: [PATCH 3134/5214] Documentation: KVM: Synchronize x86 VM types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM has reflected KVM_X86_SNP_VM to userspace since 1dfe571c12cf ("KVM: SEV: Add initial SEV-SNP support"), and KVM_X86_TDX_VM since 161d34609f9b ("KVM: TDX: Make TDX VM type supported"). Update the documentation to reflect this fact. Fixes: 1dfe571c12cf ("KVM: SEV: Add initial SEV-SNP support") Fixes: 161d34609f9b ("KVM: TDX: Make TDX VM type supported") Signed-off-by: Carlos López Reviewed-by: Binbin Wu Link: https://patch.msgid.link/20260603114504.814647-2-clopez@suse.de [sean: use one tab instead of two] Signed-off-by: Sean Christopherson --- Documentation/virt/kvm/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 1a5a82ab0d66..7d55aac04c15 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -9366,6 +9366,8 @@ means the VM type with value @n is supported. Possible values of @n are:: #define KVM_X86_SW_PROTECTED_VM 1 #define KVM_X86_SEV_VM 2 #define KVM_X86_SEV_ES_VM 3 + #define KVM_X86_SNP_VM 4 + #define KVM_X86_TDX_VM 5 Note, KVM_X86_SW_PROTECTED_VM is currently only for development and testing. Do not use KVM_X86_SW_PROTECTED_VM for "real" VMs, and especially not in From d585018a9258efed01514bae369ab3d4f21e7b1a Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 28 May 2026 23:12:30 +0200 Subject: [PATCH 3135/5214] riscv: misaligned: Fix fast_unaligned_access_speed_key init When booting with unaligned_scalar_speed=fast, fast_unaligned_access_speed_key is initialized incorrectly. The key is currently derived from the fast_misaligned_access cpumask, but that mask is only populated when the unaligned access speed probe runs. Specifying unaligned_scalar_speed=fast skips the probe entirely, leaving the mask uninitialized. The information tracked by fast_misaligned_access is already available in the misaligned_access_speed per-CPU variable. Use that to initialize fast_unaligned_access_speed_key instead and remove the redundant cpumask. Signed-off-by: Nam Cao Link: https://patch.msgid.link/2468816ceb433394099a00d7822f819745276b49.1780002199.git.namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 69 +++++++--------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 11c781a4de73..bb57eb5d19df 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -27,8 +27,6 @@ DEFINE_PER_CPU(long, vector_misaligned_access) = RISCV_HWPROBE_MISALIGNED_VECTOR static long unaligned_scalar_speed_param = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; static long unaligned_vector_speed_param = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; -static cpumask_t fast_misaligned_access; - static u64 __maybe_unused measure_cycles(void (*func)(void *dst, const void *src, size_t len), void *dst, void *src, size_t len) @@ -131,13 +129,10 @@ static int check_unaligned_access(struct page *page) * Set the value of fast_misaligned_access of a CPU. These operations * are atomic to avoid race conditions. */ - if (ret) { + if (ret) per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_FAST; - cpumask_set_cpu(cpu, &fast_misaligned_access); - } else { + else per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW; - cpumask_clear_cpu(cpu, &fast_misaligned_access); - } return 0; } @@ -192,49 +187,24 @@ static void __init check_unaligned_access_speed_all_cpus(void) DEFINE_STATIC_KEY_FALSE(fast_unaligned_access_speed_key); -static void modify_unaligned_access_branches(cpumask_t *mask, int weight) +static void modify_unaligned_access_branches(const cpumask_t *mask) { - if (cpumask_weight(mask) == weight) + bool fast = true; + int cpu; + + for_each_cpu(cpu, mask) { + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_FAST) { + fast = false; + break; + } + } + + if (fast) static_branch_enable_cpuslocked(&fast_unaligned_access_speed_key); else static_branch_disable_cpuslocked(&fast_unaligned_access_speed_key); } -static void set_unaligned_access_static_branches_except_cpu(int cpu) -{ - /* - * Same as set_unaligned_access_static_branches, except excludes the - * given CPU from the result. When a CPU is hotplugged into an offline - * state, this function is called before the CPU is set to offline in - * the cpumask, and thus the CPU needs to be explicitly excluded. - */ - - cpumask_t fast_except_me; - - cpumask_and(&fast_except_me, &fast_misaligned_access, cpu_online_mask); - cpumask_clear_cpu(cpu, &fast_except_me); - - modify_unaligned_access_branches(&fast_except_me, num_online_cpus() - 1); -} - -static void set_unaligned_access_static_branches(void) -{ - /* - * This will be called after check_unaligned_access_all_cpus so the - * result of unaligned access speed for all CPUs will be available. - * - * To avoid the number of online cpus changing between reading - * cpu_online_mask and calling num_online_cpus, cpus_read_lock must be - * held before calling this function. - */ - - cpumask_t fast_and_online; - - cpumask_and(&fast_and_online, &fast_misaligned_access, cpu_online_mask); - - modify_unaligned_access_branches(&fast_and_online, num_online_cpus()); -} - static int riscv_online_cpu(unsigned int cpu) { int ret = cpu_online_unaligned_access_init(cpu); @@ -266,14 +236,19 @@ static int riscv_online_cpu(unsigned int cpu) #endif exit: - set_unaligned_access_static_branches(); + modify_unaligned_access_branches(cpu_online_mask); return 0; } static int riscv_offline_cpu(unsigned int cpu) { - set_unaligned_access_static_branches_except_cpu(cpu); + cpumask_t mask; + + cpumask_copy(&mask, cpu_online_mask); + cpumask_clear_cpu(cpu, &mask); + + modify_unaligned_access_branches(&mask); return 0; } @@ -430,7 +405,7 @@ static int __init check_unaligned_access_all_cpus(void) riscv_online_cpu_vec, NULL); cpus_read_lock(); - set_unaligned_access_static_branches(); + modify_unaligned_access_branches(cpu_online_mask); cpus_read_unlock(); return 0; From 319fafd9a3743b617b8547d81c41c99fb67857b1 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 28 May 2026 23:12:31 +0200 Subject: [PATCH 3136/5214] riscv: traps_misaligned: Avoid redundant unaligned access speed probe When a CPU is taken offline and then is brought back online, unaligned access speed probe always runs even though the unaligned access speed is already known, wasting CPU cycles. This is because when a CPU becomes online, the following happen: 1. check_unaligned_access_emulated() is called, which clears misaligned_access_speed if there is no emulation. 2. check_unaligned_access() is called because misaligned_access_speed is cleared, wasting CPU cycles determining something already previous known. Avoid the redundant access speed probe by stop clearing misaligned_access_speed in (1). If access speed is already known, just reuse it. On my Visionfive 2, this reduces CPU bring-up time from 26ms to 0.8ms. Signed-off-by: Nam Cao Link: https://patch.msgid.link/aa5755142537d462a9e3d2074d82ad4eef6774ba.1780002199.git.namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/traps_misaligned.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index 81b7682e6c6d..6e8ae6c66322 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -522,10 +522,10 @@ static bool unaligned_ctl __read_mostly; static void check_unaligned_access_emulated(void *arg __always_unused) { int cpu = smp_processor_id(); - long *mas_ptr = per_cpu_ptr(&misaligned_access_speed, cpu); unsigned long tmp_var, tmp_val; - *mas_ptr = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN) + return; __asm__ __volatile__ ( " "REG_L" %[tmp], 1(%[ptr])\n" From d616d8bec3b11962735c9c9ff53fb4972162b324 Mon Sep 17 00:00:00 2001 From: Lei Yin Date: Fri, 24 Apr 2026 09:26:41 +0000 Subject: [PATCH 3137/5214] NFSv4.1/pNFS: fix LAYOUTCOMMIT retry loop on OLD_STATEID Handle -NFS4ERR_OLD_STATEID in nfs4_layoutcommit_done(). This issue was reproduced on NFSv4.2. Without refreshing data->args.stateid, LAYOUTCOMMIT can keep retrying with the same stale stateid after OLD_STATEID, resulting in an unbounded retry loop. Refresh the layout stateid with nfs4_layout_refresh_old_stateid() and restart the RPC only after a successful refresh. Changes since v1: update refreshed stateid in inode layout header. Signed-off-by: Lei Yin [Anna: Fix up dprintk() format specifier] Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0d77d50f4de7..393ce50274d2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -9988,6 +9988,38 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) case -NFS4ERR_GRACE: /* loca_recalim always false */ task->tk_status = 0; break; + case -NFS4ERR_OLD_STATEID: { + u32 old_seqid = be32_to_cpu(data->args.stateid.seqid); + struct pnfs_layout_range range = { + .iomode = IOMODE_ANY, + .offset = 0, + .length = NFS4_MAX_UINT64, + }; + + if (nfs4_layout_refresh_old_stateid(&data->args.stateid, + &range, + data->args.inode)) { + struct pnfs_layout_hdr *lo; + + spin_lock(&data->args.inode->i_lock); + lo = NFS_I(data->args.inode)->layout; + if (lo && pnfs_layout_is_valid(lo) && + nfs4_stateid_match_other(&data->args.stateid, + &lo->plh_stateid)) + pnfs_set_layout_stateid(lo, &data->args.stateid, + NULL, false); + spin_unlock(&data->args.inode->i_lock); + + dprintk("%s: refreshed OLD_STATEID inode %llu seq %u->%u\n", + __func__, data->args.inode->i_ino, + old_seqid, + be32_to_cpu(data->args.stateid.seqid)); + + rpc_restart_call_prepare(task); + return; + } + fallthrough; + } case 0: break; default: From 4837fb36219e6c08b666bc31a86841bad8526358 Mon Sep 17 00:00:00 2001 From: Yang Erkun Date: Thu, 26 Feb 2026 09:22:03 +0800 Subject: [PATCH 3138/5214] nfs: use nfsi->rwsem to protect traversal of the file lock list Lingfeng identified a bug and suggested two solutions, but both appear to have issues. Generally, we cannot release flc_lock while iterating over the file lock list to avoid use-after-free (UAF) problems with file locks. However, functions like nfs_delegation_claim_locks and nfs4_reclaim_locks cannot adhere to this rule because recover_lock or nfs4_lock_delegation_recall may take a long time. To resolve this, NFS switches to using nfsi->rwsem for the same protection, and nfs_reclaim_locks follows this approach. Although nfs_delegation_claim_locks uses so_delegreturn_mutex instead, this is inadequate since a single inode can have multiple nfs4_state instances. Therefore, the fix is to also use nfsi->rwsem in this case. Furthermore, after commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), the functions nfs4_locku_done and nfs4_lock_done also break this rule because they call locks_lock_inode_wait without holding nfsi->rwsem. Simply adding this protection could cause many deadlocks, so instead, the call to locks_lock_inode_wait is moved into _nfs4_proc_setlk. Regarding the bug fixed by commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), it has been resolved after commit 0460253913e5 ("NFSv4: nfs4_do_open() is incorrectly triggering state recovery") because all slots are drained before calling nfs4_do_reclaim, which prevents concurrent stateid changes along this path. Also, nfs_delegation_claim_locks does not cause this concurrency either since when _nfs4_proc_setlk is called with NFS_DELEGATED_STATE, no RPC is sent, so nfs4_lock_done is not called. Therefore, nfs4_lock_delegation_recall from nfs_delegation_claim_locks is the first time the stateid is set. Reported-by: Li Lingfeng Closes: https://lore.kernel.org/all/20250419085709.1452492-1-lilingfeng3@huawei.com/ Closes: https://lore.kernel.org/all/20250715030559.2906634-1-lilingfeng3@huawei.com/ Fixes: c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update") Signed-off-by: Yang Erkun Reviewed-by: Jeff Layton Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 9 ++++++++- fs/nfs/nfs4proc.c | 22 +++++++++++----------- include/linux/nfs_xdr.h | 1 - 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 122fb3f14ffb..9546d2195c25 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -173,6 +173,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t type) static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) { struct inode *inode = state->inode; + struct nfs_inode *nfsi = NFS_I(inode); struct file_lock *fl; struct file_lock_context *flctx = locks_inode_context(inode); struct list_head *list; @@ -182,6 +183,9 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto out; list = &flctx->flc_posix; + + /* Guard against reclaim and new lock/unlock calls */ + down_write(&nfsi->rwsem); spin_lock(&flctx->flc_lock); restart: for_each_file_lock(fl, list) { @@ -189,8 +193,10 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state continue; spin_unlock(&flctx->flc_lock); status = nfs4_lock_delegation_recall(fl, state, stateid); - if (status < 0) + if (status < 0) { + up_write(&nfsi->rwsem); goto out; + } spin_lock(&flctx->flc_lock); } if (list == &flctx->flc_posix) { @@ -198,6 +204,7 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto restart; } spin_unlock(&flctx->flc_lock); + up_write(&nfsi->rwsem); out: return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 393ce50274d2..d089ac262df3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7084,7 +7084,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) switch (task->tk_status) { case 0: renew_lease(calldata->server, calldata->timestamp); - locks_lock_inode_wait(calldata->lsp->ls_state->inode, &calldata->fl); if (nfs4_update_lock_stateid(calldata->lsp, &calldata->res.stateid)) break; @@ -7352,11 +7351,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) case 0: renew_lease(NFS_SERVER(d_inode(data->ctx->dentry)), data->timestamp); - if (data->arg.new_lock && !data->cancelled) { - data->fl.c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); - if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) - goto out_restart; - } if (data->arg.new_lock_owner != 0) { nfs_confirm_seqid(&lsp->ls_seqid, 0); nfs4_stateid_copy(&lsp->ls_stateid, &data->res.stateid); @@ -7467,11 +7461,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; - if (recovery_type > NFS_LOCK_NEW) { - if (recovery_type == NFS_LOCK_RECLAIM) - data->arg.reclaim = NFS_LOCK_RECLAIM; - } else - data->arg.new_lock = 1; + + if (recovery_type == NFS_LOCK_RECLAIM) + data->arg.reclaim = NFS_LOCK_RECLAIM; + task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -7581,6 +7574,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock up_read(&nfsi->rwsem); mutex_unlock(&sp->so_delegreturn_mutex); status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); + if (status) + goto out; + + down_read(&nfsi->rwsem); + request->c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); + status = locks_lock_inode_wait(state->inode, request); + up_read(&nfsi->rwsem); out: request->c.flc_flags = flags; return status; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index fcbd21b5685f..40417e3a7f85 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -580,7 +580,6 @@ struct nfs_lock_args { struct nfs_lowner lock_owner; unsigned char block : 1; unsigned char reclaim : 1; - unsigned char new_lock : 1; unsigned char new_lock_owner : 1; }; From ba80761235bb526e7700468baaa9bddffebc3320 Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Fri, 29 May 2026 18:25:44 +0530 Subject: [PATCH 3139/5214] pinctrl: qcom: Modify MSM_PULL_MASK to accurately represent PULL bits MSM_PULL_MASK currently spans bits [2:0], but the GPIO_PULL field in the GPIO_CFG register only occupies bits [1:0]. Bit 2 belongs to FUNC_SEL. MSM_PULL_MASK is used to isolate the GPIO_PULL bits before writing the pull configuration (PULL_DOWN: 0x1, PULL_UP: 0x3) to the GPIO_CFG register. Narrow it to bits [1:0] to prevent unintended modification of the FUNC_SEL field. This causes no functional change since the driver currently does not modify the FUNC_SEL bit, but align the mask with hardware configuration nonetheless. Signed-off-by: Sneh Mankad Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/tlmm-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/tlmm-test.c b/drivers/pinctrl/qcom/tlmm-test.c index 7d7fff538755..b655de5b4c5f 100644 --- a/drivers/pinctrl/qcom/tlmm-test.c +++ b/drivers/pinctrl/qcom/tlmm-test.c @@ -33,7 +33,7 @@ * dynamically, rather then relying on e.g. Devicetree and phandles. */ -#define MSM_PULL_MASK GENMASK(2, 0) +#define MSM_PULL_MASK GENMASK(1, 0) #define MSM_PULL_DOWN 1 #define MSM_PULL_UP 3 #define TLMM_REG_SIZE 0x1000 From 4e31ab47997540d0ff4c9de801741bed03c1ab3f Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Fri, 29 May 2026 18:25:45 +0530 Subject: [PATCH 3140/5214] pinctrl: qcom: Fix resolving register base address from device node Commit 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") added reg-names property based register reading. However multiple platforms are not using the reg-names as they have only single TLMM register region. Commit tried to handle this using the default_region module parameter, however this condition is unreachable as the error return precedes it by just checking if reg-names property exists or not, making it impossible to use tlmm-test for the SoCs (x1e80100) which don't have reg-names property in TLMM device. Fix this by moving the default_region check at the start of the tlmm_reg_base(). Fixes: 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") Signed-off-by: Sneh Mankad Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/tlmm-test.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/tlmm-test.c b/drivers/pinctrl/qcom/tlmm-test.c index b655de5b4c5f..007d6539cece 100644 --- a/drivers/pinctrl/qcom/tlmm-test.c +++ b/drivers/pinctrl/qcom/tlmm-test.c @@ -581,6 +581,9 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) int ret; int i; + if (!strcmp(tlmm_reg_name, "default_region")) + return of_address_to_resource(tlmm, 0, res); + count = of_property_count_strings(tlmm, "reg-names"); if (count <= 0) { pr_err("failed to find tlmm reg name\n"); @@ -597,18 +600,14 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) return -EINVAL; } - if (!strcmp(tlmm_reg_name, "default_region")) { - ret = of_address_to_resource(tlmm, 0, res); - } else { - for (i = 0; i < count; i++) { - if (!strcmp(reg_names[i], tlmm_reg_name)) { - ret = of_address_to_resource(tlmm, i, res); - break; - } + for (i = 0; i < count; i++) { + if (!strcmp(reg_names[i], tlmm_reg_name)) { + ret = of_address_to_resource(tlmm, i, res); + break; } - if (i == count) - ret = -EINVAL; } + if (i == count) + ret = -EINVAL; kfree(reg_names); From 0560183f91773312aff9855997d27577e7b44729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 4 Jun 2026 18:50:28 +0200 Subject: [PATCH 3141/5214] w1: ds2482: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260518171456.872736-2-u.kleine-koenig@baylibre.com Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260604165027.83922-4-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/ds2482.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e2a568c9a43a..0069e6f854d7 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -539,8 +539,8 @@ static void ds2482_remove(struct i2c_client *client) * Driver data (common to all clients) */ static const struct i2c_device_id ds2482_id[] = { - { "ds2482" }, - { "ds2484" }, + { .name = "ds2482" }, + { .name = "ds2484" }, { } }; MODULE_DEVICE_TABLE(i2c, ds2482_id); From 6fe0687245e8406bf26143bd45eb16441bbe5280 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 7 Jun 2026 14:10:58 +0800 Subject: [PATCH 3142/5214] nvme-apple: Prevent shared tags across queues on Apple A11 On Apple A11, tags of pending commands must be unique across the admin and IO queues, else the firmware crashes with "duplicate tag error for tag N", with N being the tag. Apply the existing workaround for M1 of reserving two tags for the admin queue to A11. Cc: stable@vger.kernel.org Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support") Reviewed-by: Sven Peter Signed-off-by: Nick Chan Signed-off-by: Keith Busch --- drivers/nvme/host/apple.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index de0d5126458f..79e1fe2a23f9 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -225,7 +225,7 @@ static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q) { struct apple_nvme *anv = queue_to_apple_nvme(q); - if (q->is_adminq && anv->hw->has_lsq_nvmmu) + if (q->is_adminq) return APPLE_NVME_AQ_DEPTH; return anv->hw->max_queue_depth; @@ -303,7 +303,7 @@ static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q, memcpy((void *)q->sqes + (q->sq_tail << APPLE_NVME_IOSQES), cmd, sizeof(*cmd)); - if (++q->sq_tail == anv->hw->max_queue_depth) + if (++q->sq_tail == apple_nvme_queue_depth(q)) q->sq_tail = 0; writel(q->sq_tail, q->sq_db); @@ -1138,10 +1138,7 @@ static void apple_nvme_reset_work(struct work_struct *work) } /* Setup the admin queue */ - if (anv->hw->has_lsq_nvmmu) - aqa = APPLE_NVME_AQ_DEPTH - 1; - else - aqa = anv->hw->max_queue_depth - 1; + aqa = APPLE_NVME_AQ_DEPTH - 1; aqa |= aqa << 16; writel(aqa, anv->mmio_nvme + NVME_REG_AQA); writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ); @@ -1324,8 +1321,7 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) * both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which * must be marked as reserved in the IO queue. */ - if (anv->hw->has_lsq_nvmmu) - anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; + anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; anv->tagset.queue_depth = anv->hw->max_queue_depth - 1; anv->tagset.timeout = NVME_IO_TIMEOUT; anv->tagset.numa_node = NUMA_NO_NODE; From b65b608eb8ff3507676491b28acaf496b1c347f2 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 2 Jun 2026 10:51:25 -0700 Subject: [PATCH 3143/5214] scsi: mailmap: Update Avri Altman's email address Avri Altman's email address changed from @wdc.com into @sandisk.com. Add this information in the .mailmap file such that scripts/get_maintainer.pl produces the correct email address for UFS kernel patches. Cc: Avri Altman Cc: Avri Altman Signed-off-by: Bart Van Assche Acked-by: Avri Altman Link: https://patch.msgid.link/b71be634e78d3a51048ec28fac2eaedb52d6cb09.1780422652.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 34acd34bbf9b..d5844d97b3ff 100644 --- a/.mailmap +++ b/.mailmap @@ -116,6 +116,7 @@ Asutosh Das Atish Patra Atish Patra Avaneesh Kumar Dwivedi +Avri Altman Axel Dyks Axel Lin Balakrishna Godavarthi From 63977ab3c6a07e5f50c95d0c8fd83d37d56812a8 Mon Sep 17 00:00:00 2001 From: Palash Kambar Date: Tue, 26 May 2026 14:39:55 +0530 Subject: [PATCH 3144/5214] scsi: ufs: qcom: dt-bindings: Document the Hawi UFS controller Document the UFS Controller on the Hawi Platform. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Palash Kambar Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260526090956.2340262-3-palash.kambar@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml index 900d93b675cd..b441f0d26081 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml @@ -16,6 +16,7 @@ select: contains: enum: - qcom,eliza-ufshc + - qcom,hawi-ufshc - qcom,kaanapali-ufshc - qcom,nord-ufshc - qcom,sm8650-ufshc @@ -28,6 +29,7 @@ properties: items: - enum: - qcom,eliza-ufshc + - qcom,hawi-ufshc - qcom,kaanapali-ufshc - qcom,nord-ufshc - qcom,sm8650-ufshc From 3c08f6034d7459f6d4d5c1599de7bb42c1d89521 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 4 Jun 2026 23:43:56 +0000 Subject: [PATCH 3145/5214] scsi: scsi_debug: Fix one-partition tape setup bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tape setup path uses one tape_block entry as the end-of-data marker after the usable tape blocks. For the one-partition layout, partition 0 uses all TAPE_UNITS data slots and partition 1's marker is written at tape_blocks[0] + TAPE_UNITS. Only TAPE_UNITS entries are allocated, so that marker write is one element past the allocation during device initialization before any command is issued. Allocate one extra tape_block entry for the marker. This keeps the existing partitioning paths unchanged while providing backing storage for the sentinel. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Kai Mäkisara Link: https://patch.msgid.link/20260604234724.1936118-1-sam.moelius@trailofbits.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index a2f85ee1ae57..bb6b0e7fb910 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -6640,7 +6640,7 @@ static int scsi_debug_sdev_configure(struct scsi_device *sdp, if (sdebug_ptype == TYPE_TAPE) { if (!devip->tape_blocks[0]) { devip->tape_blocks[0] = - kzalloc_objs(struct tape_block, TAPE_UNITS); + kzalloc_objs(struct tape_block, TAPE_UNITS + 1); if (!devip->tape_blocks[0]) return 1; } From 7e161211f1dd5288b4ea802b30e70ef919ebc3da Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 5 Jun 2026 22:16:47 +1000 Subject: [PATCH 3146/5214] scsi: target: Fix hexadecimal CHAP_I handling A mutual CHAP handshake requires target processing of an initiator-sent CHAP_I identifier. The RFC 3720 specification states: 11.1.4. Challenge Handshake Authentication Protocol (CHAP) ... CHAP_A= CHAP_I= CHAP_C= ... Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, Algorithm, Identifier, Challenge, and Response as defined in [RFC1994], N is a text string, A,A1,A2, and I are numbers CHAP_I parsing currently calls extract_param(), which returns the @identifier string (stripped of any 0b/0B or 0x/0X prefix) and a @type which indicates DECIMAL, HEX, or BASE64 encoding (based on any stripped prefix). Any HEX encoded CHAP_I string is further processed via: ret = kstrtoul(&identifier[2], 0, &id); This is incorrect for two reasons: * The @identifier string has already been stripped of the 0x/0X prefix, so skipping the first two bytes omits part of the number. * The kstrtoul() call specifies a base of 0, which will see &identifier[2] parsed as a decimal, unless a '0x' or (octal) '0' is erroneously present at that offset. Fix this by passing the (zero-offset) identifier string to kstrtoul() along with a base=16 parameter. Also add an explicit error handler for BASE64 encoding. Hex-encoded CHAP_I handling can be testing using the libiscsi EncodedI test linked below. Reported-by: Sashiko (gemini/gemini-3.1-pro-preview) Link: https://sashiko.dev/#/patchset/20260521151121.808477-1-hossu.alexandru%40gmail.com Link: https://github.com/sahlberg/libiscsi/pull/473 Fixes: 85db7391310b ("scsi: target: iscsi: Validate CHAP_R length before base64 decode") Signed-off-by: David Disseldorp Reviewed-by: Lee Duncan Reviewed-by: John Garry Link: https://patch.msgid.link/20260605122019.24146-2-ddiss@suse.de Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_auth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index a3ad2d244dbe..5858cc308979 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -438,9 +438,11 @@ static int chap_server_compute_hash( } if (type == HEX) - ret = kstrtoul(&identifier[2], 0, &id); + ret = kstrtoul(identifier, 16, &id); + else if (type == DECIMAL) + ret = kstrtoul(identifier, 10, &id); else - ret = kstrtoul(identifier, 0, &id); + ret = -EINVAL; if (ret < 0) { pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); From cf14fc2be868840c4c9e0e46a472995798b59712 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 5 Jun 2026 22:16:48 +1000 Subject: [PATCH 3147/5214] scsi: target: Use constant-time crypto_memneq() for CHAP digests A constant-time memory comparison is more suitable than plain memcmp() for authentication digest comparison. CHAP digests use an authenticator-provided random challenge, so any timing side-channel shouldn't be easily exploitable. Reported-by: Sashiko (gemini/gemini-3.1-pro-preview) Link: https://sashiko.dev/#/patchset/20260521151121.808477-1-hossu.alexandru%40gmail.com Signed-off-by: David Disseldorp Reviewed-by: Lee Duncan Link: https://patch.msgid.link/20260605122019.24146-3-ddiss@suse.de Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_auth.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 5858cc308979..f3c0cdd31830 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -9,6 +9,7 @@ ******************************************************************************/ #include +#include #include #include #include @@ -408,7 +409,7 @@ static int chap_server_compute_hash( pr_debug("[server] %s Server Digest: %s\n", chap->digest_name, response); - if (memcmp(server_digest, client_digest, chap->digest_size) != 0) { + if (crypto_memneq(server_digest, client_digest, chap->digest_size)) { pr_debug("[server] %s Digests do not match!\n\n", chap->digest_name); goto out; From 92f58587a04c94985fd4a9e3575720b054c432bf Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 4 Jun 2026 14:58:40 +0000 Subject: [PATCH 3148/5214] nvme: quieten sparse warning in valid LBA size check Currently building with C=1 generates the following warning: CC drivers/nvme/host/core.o CHECK drivers/nvme/host/core.c drivers/nvme/host/core.c:2426:13: warning: unsigned value that used to be signed checked against zero? drivers/nvme/host/core.c:2426:13: signed value source This issue was introduced when using check_shl_overflow() to check for invalid LBA size. Sparse is having trouble dealing with __bitwise __le64 conversion when passing to check_shl_overflow(). Resolve the issue by moving the check_shl_overflow() call to a separate function, where types are not converted. The id->lbaf[lbaf].ds < SECTOR_SHIFT check is dropped as check_shl_overflow() is able to detect negative shifts. Reviewed-by: Christoph Hellwig Signed-off-by: John Garry Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index efaddab8296e..d37fc70fe48a 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2379,6 +2379,11 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info) return ret; } +static bool nvme_invalid_lba_sz(u64 nsze, signed int shift, sector_t *capacity) +{ + return check_shl_overflow(nsze, shift, capacity); +} + static int nvme_update_ns_info_block(struct nvme_ns *ns, struct nvme_ns_info *info) { @@ -2422,10 +2427,8 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, goto out; } - if (id->lbaf[lbaf].ds < SECTOR_SHIFT || - check_shl_overflow(le64_to_cpu(id->nsze), - id->lbaf[lbaf].ds - SECTOR_SHIFT, - &capacity)) { + if (nvme_invalid_lba_sz(le64_to_cpu(id->nsze), + id->lbaf[lbaf].ds - SECTOR_SHIFT, &capacity)) { dev_warn_once(ns->ctrl->device, "invalid LBA data size %u, skipping namespace\n", id->lbaf[lbaf].ds); From 5d5221f8a4064a256b9499485a9f8c6f530f21dc Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Fri, 29 May 2026 22:56:02 +0200 Subject: [PATCH 3149/5214] scsi: devinfo: Broaden Promise VTrak E310/E610 identification The Promise VTrak Ex10 series share the same hardware base and firmware. Consequently all interface variants, whether fibre channel ("f") or SAS ("s") in dual/single controller, exhibit the same SCSI behavior. Instead of adding separate blacklist entries for every specific model variant (such as E610f, E610s, E310f, E310s), consolidate and broaden the match strings to "VTrak E310" and "VTrak E610". Cc: Alexander Perlis Cc: Nikkos Svoboda Cc: Martin Wilck Cc: Benjamin Marzinski Cc: Christophe Varoqui Cc: Christoph Hellwig Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: SCSI-ML Cc: DM_DEVEL-ML Signed-off-by: Xose Vazquez Perez Reviewed-by: Martin Wilck Link: https://patch.msgid.link/20260529205602.177515-1-xose.vazquez@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index c6defe1c3152..15ffbe93ac72 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -218,8 +218,8 @@ static struct { {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"Promise", "VTrak E310f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, - {"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, + {"Promise", "VTrak E310", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, + {"Promise", "VTrak E610", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "", NULL, BLIST_SPARSELUN}, {"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES}, {"QNAP", "iSCSI Storage", NULL, BLIST_MAX_1024}, From 01d5e237b33931b970dd190dd6a19c5ef32c105d Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Fri, 5 Jun 2026 19:20:34 +0800 Subject: [PATCH 3150/5214] scsi: ufs: core: Handle PM commands timeout before SCSI EH A PM START STOP sent from the UFS well-known LU resume path can race with SCSI EH: The "wl resume" task flow is: __ufshcd_wl_resume() ufshcd_set_dev_pwr_mode(UFS_ACTIVE_PWR_MODE) ufshcd_execute_start_stop() scsi_execute_cmd() blk_execute_rq <-- wait scsi_check_passthrough() <-- may retry START STOP If the first START STOP time out, SCSI EH may already recover the link and reset the device before scsi_execute_cmd() returns: scsi_timeout() scsi_eh_scmd_add() scsi_error_handler() scsi_unjam_host() scsi_eh_ready_devs() scsi_eh_host_reset() ufshcd_eh_host_reset_handler() if (hba->pm_op_in_progress) ufshcd_link_recovery() ufshcd_device_reset() ufshcd_host_reset_and_restore() ... scsi_eh_flush_done_q() <-- wakeup "wl resume" task ... <-- host still in SHOST_RECOVERY scsi_restart_operations() A later passthrough retry can then run while the host is still in SHOST_RECOVERY and hit the SCMD_FAIL_IF_RECOVERING path: scsi_queue_rq() if (scsi_host_in_recovery(shost) && cmd->flags & SCMD_FAIL_IF_RECOVERING) return BLK_STS_OFFLINE That retry completes with DID_ERROR or DID_NO_CONNECT even though EH may already have restored the device to an operational ACTIVE state. Handle these PM timeouts directly from ufshcd_eh_timed_out() instead. After ufshcd_link_recovery(), complete the timed-out command immediately if it has not been completed already. For regular SCSI commands, complete them with DID_REQUEUE to match the existing MCQ force-completion semantics and allow scsi_execute_cmd() to retry if needed. For reserved internal device-management commands, finish the request with DID_TIME_OUT without calling ufshcd_release_scsi_cmd() since those commands use different resource lifetime rules. The system_suspending flag is no longer needed because PM command timeout handling now uses pm_op_in_progress. Fixes: b8c3a7bac9b6 ("scsi: ufs: Have midlayer retry start stop errors") Signed-off-by: Hongjie Fang Reviewed-by: Bart Van Assche Reviewed-by: Peter Wang Link: https://patch.msgid.link/20260605112034.3802540-1-hongjiefang@asrmicro.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 34 +++++++++++++++++++++++++++------- include/ufs/ufshcd.h | 3 --- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 60ec2c63c2d8..2bbab3b2f656 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9484,22 +9484,44 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd) { struct ufs_hba *hba = shost_priv(scmd->device->host); - if (!hba->system_suspending) { + if (!hba->pm_op_in_progress) { /* Activate the error handler in the SCSI core. */ return SCSI_EH_NOT_HANDLED; } /* - * If we get here we know that no TMFs are outstanding and also that - * the only pending command is a START STOP UNIT command. Handle the - * timeout of that command directly to prevent a deadlock between + * Handle the timeout directly to prevent a deadlock between * ufshcd_set_dev_pwr_mode() and ufshcd_err_handler(). */ ufshcd_link_recovery(hba); dev_info(hba->dev, "%s() finished; outstanding_tasks = %#lx.\n", __func__, hba->outstanding_tasks); - return scsi_host_busy(hba->host) ? SCSI_EH_RESET_TIMER : SCSI_EH_DONE; + /* + * ufshcd_link_recovery() may already have completed @scmd, e.g. via + * the existing MCQ force-completion path. + */ + if (!test_bit(SCMD_STATE_COMPLETE, &scmd->state)) { + if (!hba->mcq_enabled) { + unsigned long flags; + struct request *rq = scsi_cmd_to_rq(scmd); + + spin_lock_irqsave(&hba->outstanding_lock, flags); + __clear_bit(rq->tag, &hba->outstanding_reqs); + spin_unlock_irqrestore(&hba->outstanding_lock, flags); + } + + if (ufshcd_is_scsi_cmd(scmd)) { + set_host_byte(scmd, DID_REQUEUE); + ufshcd_release_scsi_cmd(hba, scmd); + } else { + set_host_byte(scmd, DID_TIME_OUT); + } + + scsi_done(scmd); + } + + return SCSI_EH_DONE; } static const struct attribute_group *ufshcd_driver_groups[] = { @@ -10523,7 +10545,6 @@ static int ufshcd_wl_suspend(struct device *dev) hba = shost_priv(sdev->host); down(&hba->host_sem); - hba->system_suspending = true; if (pm_runtime_suspended(dev)) goto out; @@ -10565,7 +10586,6 @@ static int ufshcd_wl_resume(struct device *dev) hba->curr_dev_pwr_mode, hba->uic_link_state); if (!ret) hba->is_sys_suspended = false; - hba->system_suspending = false; up(&hba->host_sem); return ret; } diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index 3eaae082329c..248d0a5bef40 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -1029,8 +1029,6 @@ enum ufshcd_mcq_opr { * @caps: bitmask with information about UFS controller capabilities * @devfreq: frequency scaling information owned by the devfreq core * @clk_scaling: frequency scaling information owned by the UFS driver - * @system_suspending: system suspend has been started and system resume has - * not yet finished. * @is_sys_suspended: UFS device has been suspended because of system suspend * @urgent_bkops_lvl: keeps track of urgent bkops level for device * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for @@ -1206,7 +1204,6 @@ struct ufs_hba { struct devfreq *devfreq; struct ufs_clk_scaling clk_scaling; - bool system_suspending; bool is_sys_suspended; enum bkops_status urgent_bkops_lvl; From 9d87e0db00e9a2d281d11ae226eaf4d9b47376e2 Mon Sep 17 00:00:00 2001 From: Rajeshkumar Sambandham Date: Tue, 2 Jun 2026 15:29:31 +0530 Subject: [PATCH 3151/5214] scsi: ufs: ufs-pci: Add AMD device ID support Add PCI device ID 0x1022:0x1B29 for AMD UFS controllers. Signed-off-by: Rajeshkumar Sambandham Reviewed-by: Adrian Hunter Link: https://patch.msgid.link/20260602095931.2869516-1-Rajeshkumar.Sambandham@amd.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufshcd-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 13293e83064c..f2433879b0eb 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -694,6 +694,7 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0xE447), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4D47), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { PCI_VDEVICE(INTEL, 0xD335), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(AMD, 0x1B29), .driver_data = 0 }, { } /* terminate list */ }; From fc6f9719e68d4133f2a373e1c6826fdef8bb1a34 Mon Sep 17 00:00:00 2001 From: William Theesfeld Date: Tue, 2 Jun 2026 07:19:12 -0400 Subject: [PATCH 3152/5214] scsi: lpfc: Fix spelling mistakes in comments Comment-only changes across the lpfc driver, found by running scripts/checkpatch.pl with the kernel's scripts/spelling.txt list against drivers/scsi/lpfc/. No functional impact. v1 covered a single site in lpfc_bsg.c. v2 expands to all checkpatch-detected comment misspellings across the driver, per review feedback from Justin Tee on the v1 thread. Identifiers that happen to match common-typo entries (e.g. LSEXP_CANT_GIVE_DATA, LPFC_FC_LA_TOP_UNKOWN) are intentionally left untouched, as renaming them would change the driver's internal API. Signed-off-by: William Theesfeld Reviewed-by: Justin Tee Link: https://patch.msgid.link/20260602111912.23864-1-william@theesfeld.net Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 10 +++++----- drivers/scsi/lpfc/lpfc_bsg.c | 6 +++--- drivers/scsi/lpfc/lpfc_els.c | 2 +- drivers/scsi/lpfc/lpfc_hbadisc.c | 12 ++++++------ drivers/scsi/lpfc/lpfc_hw4.h | 2 +- drivers/scsi/lpfc/lpfc_init.c | 12 ++++++------ drivers/scsi/lpfc/lpfc_scsi.c | 12 ++++++------ drivers/scsi/lpfc/lpfc_sli.c | 22 +++++++++++----------- drivers/scsi/lpfc/lpfc_sli4.h | 2 +- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index c91fa44b12d4..f4e8164b94ab 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -84,7 +84,7 @@ const char *const trunk_errmsg[] = { /* map errcode */ }; /** - * lpfc_jedec_to_ascii - Hex to ascii convertor according to JEDEC rules + * lpfc_jedec_to_ascii - Hex to ascii converter according to JEDEC rules * @incr: integer to convert. * @hdw: ascii string holding converted integer plus a string terminator. * @@ -1748,12 +1748,12 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, } /** - * lpfc_sli4_pdev_status_reg_wait - Wait for pdev status register for readyness + * lpfc_sli4_pdev_status_reg_wait - Wait for pdev status register for readiness * @phba: lpfc_hba pointer. * * Description: * SLI4 interface type-2 device to wait on the sliport status register for - * the readyness after performing a firmware reset. + * the readiness after performing a firmware reset. * * Returns: * zero for success, -EPERM when port does not have privilege to perform the @@ -5403,7 +5403,7 @@ lpfc_vport_param_store(max_scsicmpl_time); static DEVICE_ATTR_RW(lpfc_max_scsicmpl_time); /* -# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value +# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgment. Value # range is [0,1]. Default value is 0. */ LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); @@ -5482,7 +5482,7 @@ LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary " /* # lpfc_multi_ring_rctl: If lpfc_multi_ring_support is enabled, this # identifies what rctl value to configure the additional ring for. -# Value range is [1,0xff]. Default value is 4 (Unsolicated Data). +# Value range is [1,0xff]. Default value is 4 (Unsolicited Data). */ LPFC_ATTR_R(multi_ring_rctl, FC_RCTL_DD_UNSOL_DATA, 1, 255, "Identifies RCTL for additional ring configuration"); diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 7406dfa60016..c95165905483 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -2066,12 +2066,12 @@ lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job) if (rc) goto job_done; - /* indicate we are in loobpack diagnostic mode */ + /* indicate we are in loopback diagnostic mode */ spin_lock_irq(&phba->hbalock); phba->link_flag |= LS_LOOPBACK_MODE; spin_unlock_irq(&phba->hbalock); - /* reset port to start frome scratch */ + /* reset port to start from scratch */ rc = lpfc_selective_reset(phba); if (rc) goto job_done; @@ -5003,7 +5003,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job, } else if (phba->sli_rev == LPFC_SLI_REV4) { /* Let type 4 (well known data) through because the data is * returned in varwords[4-8] - * otherwise check the recieve length and fetch the buffer addr + * otherwise check the receive length and fetch the buffer addr */ if ((pmb->mbxCommand == MBX_DUMP_MEMORY) && (pmb->un.varDmp.type != DMP_WELL_KNOWN)) { diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4e3fe89283e4..52fc5058976d 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -4850,7 +4850,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, case IOSTAT_LS_RJT: stat.un.ls_rjt_error_be = cpu_to_be32(ulp_word4); - /* Added for Vendor specifc support + /* Added for Vendor specific support * Just keep retrying for these Rsn / Exp codes */ if (test_bit(FC_PT2PT, &vport->fc_flag) && diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index f3a85f6c796e..c9b02d2c6305 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1845,7 +1845,7 @@ lpfc_copy_fcf_record(struct lpfc_fcf_rec *fcf_rec, * @flag: flag bits to be set to the driver fcf record. * * This routine updates the driver FCF record from the new HBA FCF record - * together with the address mode, vlan_id, and other informations. This + * together with the address mode, vlan_id, and other information. This * routine is called with the hbalock held. **/ static void @@ -2120,7 +2120,7 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, /** * lpfc_check_pending_fcoe_event - Check if there is pending fcoe event. * @phba: pointer to lpfc hba data structure. - * @unreg_fcf: Unregister FCF if FCF table need to be re-scaned. + * @unreg_fcf: Unregister FCF if FCF table need to be re-scanned. * * This function check if there is any fcoe event pending while driver * scan FCF entries. If there is any pending event, it will restart the @@ -2290,7 +2290,7 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, * @vlan_id: the lowest vlan identifier associated to this fcf record. * @next_fcf_index: the index to the next fcf record in hba's fcf table. * - * This routine logs the detailed FCF record if the LOG_FIP loggin is + * This routine logs the detailed FCF record if the LOG_FIP login is * enabled. **/ static void @@ -3475,7 +3475,7 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) phba->fc_edtov = ed_tov; phba->fc_ratov = (2 * ed_tov) / 1000; if (phba->fc_ratov < FF_DEF_RATOV) { - /* RA_TOV should be atleast 10sec for initial flogi */ + /* RA_TOV should be at least 10sec for initial flogi */ phba->fc_ratov = FF_DEF_RATOV; } @@ -5239,7 +5239,7 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport, * Free rpi associated with LPFC_NODELIST entry. * This routine is called if the driver initiates a LOGO that completes * successfully, and we are waiting to PLOGI back to the remote NPort. - * In addition, it is called after we receive and unsolicated ELS cmd, + * In addition, it is called after we receive and unsolicited ELS cmd, * send back a rsp, the rsp completes and we are waiting to PLOGI back * to the remote NPort. */ @@ -6551,7 +6551,7 @@ lpfc_nlp_init(struct lpfc_vport *vport, uint32_t did) return ndlp; } -/* This routine releases all resources associated with a specifc NPort's ndlp +/* This routine releases all resources associated with a specific NPort's ndlp * and mempool_free's the nodelist. */ static void diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index f91bde4a6c38..41fa8f3329da 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -3256,7 +3256,7 @@ struct lpfc_mbx_memory_dump_type3 { /* - * Tranceiver codes Fibre Channel SFF-8472 + * Transceiver codes Fibre Channel SFF-8472 * Table 3.5. */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 968a25235a2d..82af59c913e9 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4103,7 +4103,7 @@ lpfc_sli4_els_sgl_update(struct lpfc_hba *phba) &phba->sli4_hba.lpfc_els_sgl_list); spin_unlock_irq(&phba->sli4_hba.sgl_list_lock); } else if (els_xri_cnt < phba->sli4_hba.els_xri_cnt) { - /* els xri-sgl shrinked */ + /* els xri-sgl shrunk */ xri_cnt = phba->sli4_hba.els_xri_cnt - els_xri_cnt; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "3158 ELS xri-sgl count decreased from " @@ -8024,7 +8024,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) * Initialize driver internal slow-path work queues */ - /* Driver internel slow-path CQ Event pool */ + /* Driver internal slow-path CQ Event pool */ INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool); /* Response IOCB work queue list */ INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event); @@ -9740,7 +9740,7 @@ lpfc_create_bootstrap_mbox(struct lpfc_hba *phba) * Initialize the bootstrap mailbox pointers now so that the register * operations are simple later. The mailbox dma address is required * to be 16-byte aligned. Also align the virtual memory as each - * maibox is copied into the bmbx mailbox region before issuing the + * mailbox is copied into the bmbx mailbox region before issuing the * command to the port. */ phba->sli4_hba.bmbx.dmabuf = dmabuf; @@ -10206,7 +10206,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) goto read_cfg_out; } - /* search for fc_fcoe resrouce descriptor */ + /* search for fc_fcoe resource descriptor */ get_func_cfg = &pmb->u.mqe.un.get_func_cfg; pdesc_0 = (char *)&get_func_cfg->func_cfg.desc[0]; @@ -10414,7 +10414,7 @@ lpfc_alloc_io_wq_cq(struct lpfc_hba *phba, int idx) * * Return codes * 0 - successful - * -ENOMEM - No availble memory + * -ENOMEM - No available memory * -EIO - The mailbox failed to complete successfully. **/ int @@ -12490,7 +12490,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) /* If so, find a new_cpup that is on the SAME * phys_id as cpup. start_cpu will start where we - * left off so all unassigned entries don't get assgined + * left off so all unassigned entries don't get assigned * the IRQ of the first entry. */ new_cpu = start_cpu; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 1dce33b79beb..f2cab134af7f 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -2497,7 +2497,7 @@ lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba, /* * If we are in DIF Type 1 mode every data block has a 8 byte - * DIF (trailer) attached to it. Must ajust FCP data length + * DIF (trailer) attached to it. Must adjust FCP data length * to account for the protection data. */ fcpdl += (fcpdl / scsi_prot_interval(sc)) * 8; @@ -2596,7 +2596,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, lpfc_cmd->prot_seg_cnt = protsegcnt; /* - * There is a minimun of 4 BPLs used for every + * There is a minimum of 4 BPLs used for every * protection data segment. */ if ((lpfc_cmd->prot_seg_cnt * 4) > @@ -2678,7 +2678,7 @@ lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, /* * This function calcuates the T10 DIF guard tag - * on the specified data using a CRC algorithmn + * on the specified data using a CRC algorithm * using crc_t10dif. */ static uint16_t @@ -2694,7 +2694,7 @@ lpfc_bg_crc(uint8_t *data, int count) /* * This function calcuates the T10 DIF guard tag - * on the specified data using a CSUM algorithmn + * on the specified data using a CSUM algorithm * using ip_compute_csum. */ static uint16_t @@ -3378,7 +3378,7 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, lpfc_cmd->prot_seg_cnt = protsegcnt; /* - * There is a minimun of 3 SGEs used for every + * There is a minimum of 3 SGEs used for every * protection data segment. */ if (((lpfc_cmd->prot_seg_cnt * 3) > @@ -5712,7 +5712,7 @@ lpfc_taskmgmt_name(uint8_t task_mgmt_cmd) * @vport: The virtual port for which this call is being executed. * @lpfc_cmd: Pointer to lpfc_io_buf data structure. * - * This routine checks the FCP RSP INFO to see if the tsk mgmt command succeded + * This routine checks the FCP RSP INFO to see if the tsk mgmt command succeeded * * Return code : * 0x2003 - Error diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 0e56e7034566..62a30a92b792 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1155,9 +1155,9 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * * This function takes the hbalock. * The active bit is always set in the active rrq xri_bitmap even - * if there is no slot avaiable for the other rrq information. + * if there is no slot available for the other rrq information. * - * returns 0 rrq actived for this xri + * returns 0 rrq activated for this xri * < 0 No memory or invalid ndlp. **/ int @@ -4827,11 +4827,11 @@ lpfc_sli_brdready_s4(struct lpfc_hba *phba, uint32_t mask) } /** - * lpfc_sli_brdready - Wrapper func for checking the hba readyness + * lpfc_sli_brdready - Wrapper func for checking the hba readiness * @phba: Pointer to HBA context object. * @mask: Bit mask to be checked. * - * This routine wraps the actual SLI3 or SLI4 hba readyness check routine + * This routine wraps the actual SLI3 or SLI4 hba readiness check routine * from the API jump table function pointer from the lpfc_hba struct. **/ int @@ -8512,7 +8512,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) if (unlikely(rc)) return -ENODEV; - /* Check the HBA Host Status Register for readyness */ + /* Check the HBA Host Status Register for readiness */ rc = lpfc_sli4_post_status_check(phba); if (unlikely(rc)) return -ENODEV; @@ -9984,7 +9984,7 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->sli.mbox_active = mboxq; spin_unlock_irqrestore(&phba->hbalock, iflag); - /* wait for bootstrap mbox register for readyness */ + /* wait for bootstrap mbox register for readiness */ rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq); if (rc) goto exit; @@ -13955,7 +13955,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) * device-level interrupt handler. When the PCI slot is in error recovery * or the HBA is undergoing initialization, the interrupt handler will not * process the interrupt. The SCSI FCP fast-path ring event are handled in - * the intrrupt context. This function is called without any lock held. + * the interrupt context. This function is called without any lock held. * It gets the hbalock to access and update SLI data structures. * * This function returns IRQ_HANDLED when interrupt is handled else it @@ -15561,7 +15561,7 @@ lpfc_sli4_dly_hba_process_cq(struct work_struct *work) * device-level interrupt handler. When the PCI slot is in error recovery * or the HBA is undergoing initialization, the interrupt handler will not * process the interrupt. The SCSI FCP fast-path ring event are handled in - * the intrrupt context. This function is called without any lock held. + * the interrupt context. This function is called without any lock held. * It gets the hbalock to access and update SLI data structures. Note that, * the FCP EQ to FCP CQ are one-to-one map such that the FCP EQ index is * equal to that of FCP CQ index. @@ -16003,7 +16003,7 @@ lpfc_dpp_wc_map(struct lpfc_hba *phba, uint8_t dpp_barset) * On success this function will return a zero. If unable to allocate * enough memory this function will return -ENOMEM. If a mailbox command * fails this function will return -ENXIO. Note: on ENXIO, some EQs may - * have had their delay multipler changed. + * have had their delay multiplier changed. **/ void lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq, @@ -20452,7 +20452,7 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba) /* * If next fcf index is not found check if there are lower * Priority level fcf's in the fcf_priority list. - * Set up the rr_bmask with all of the avaiable fcf bits + * Set up the rr_bmask with all of the available fcf bits * at that level and continue the selection process. */ } while (lpfc_check_next_fcf_pri_level(phba)); @@ -21696,7 +21696,7 @@ void lpfc_adjust_high_watermark(struct lpfc_hba *phba, u32 hwqid) * @phba: pointer to lpfc hba data structure. * @hwqid: belong to which HWQ. * - * This routine is called from hearbeat timer when pvt_pool is idle. + * This routine is called from heartbeat timer when pvt_pool is idle. * All free XRIs are moved from private to public pool on hwqid with 2 steps. * The first step moves (all - low_watermark) amount of XRIs. * The second step moves the rest of XRIs. diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index e2b95fb50d55..cf61370ad025 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -336,7 +336,7 @@ struct lpfc_fcf { #define FCF_AVAILABLE 0x01 /* FCF available for discovery */ #define FCF_REGISTERED 0x02 /* FCF registered with FW */ #define FCF_SCAN_DONE 0x04 /* FCF table scan done */ -#define FCF_IN_USE 0x08 /* Atleast one discovery completed */ +#define FCF_IN_USE 0x08 /* At Least one discovery completed */ #define FCF_INIT_DISC 0x10 /* Initial FCF discovery */ #define FCF_DEAD_DISC 0x20 /* FCF DEAD fast FCF failover discovery */ #define FCF_ACVL_DISC 0x40 /* All CVL fast FCF failover discovery */ From 7c08d430835a90414cd962e3a9602e5b002dee3b Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 30 May 2026 00:23:47 -0500 Subject: [PATCH 3153/5214] scsi: target: Remove tcm_loop target reset handling tcm_loop_target_reset is supposed to handle all the LUNs on a target but it's only doing a TMR_LUN_RESET so only that one LUN is handled. This will cause us to return early while IOs to other LUNs are still hung in lower layers. This just removes the target reset handler for the driver because LIO doesn't support target resets and for the common case where this is run from the scsi-ml error hamdler we have already tried an abort and lun reset so waiting again is most likely useless. Fixes: 1333eee56cdf ("scsi: target: tcm_loop: Drain commands in target_reset handler") Signed-off-by: Mike Christie Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260530052349.5134-1-michael.christie@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/loopback/tcm_loop.c | 64 ------------------------------ 1 file changed, 64 deletions(-) diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index a25fd826b542..f8902087c6c9 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -270,69 +270,6 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; } -static bool tcm_loop_flush_work_iter(struct request *rq, void *data) -{ - struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq); - struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc); - struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; - - flush_work(&se_cmd->work); - return true; -} - -static int tcm_loop_target_reset(struct scsi_cmnd *sc) -{ - struct tcm_loop_hba *tl_hba; - struct tcm_loop_tpg *tl_tpg; - struct Scsi_Host *sh = sc->device->host; - int ret; - - /* - * Locate the tcm_loop_hba_t pointer - */ - tl_hba = *(struct tcm_loop_hba **)shost_priv(sh); - if (!tl_hba) { - pr_err("Unable to perform device reset without active I_T Nexus\n"); - return FAILED; - } - /* - * Locate the tl_tpg pointer from TargetID in sc->device->id - */ - tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; - if (!tl_tpg) - return FAILED; - - /* - * Issue a LUN_RESET to drain all commands that the target core - * knows about. This handles commands not yet marked CMD_T_COMPLETE. - */ - ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET); - if (ret != TMR_FUNCTION_COMPLETE) - return FAILED; - - /* - * Flush any deferred target core completion work that may still be - * queued. Commands that already had CMD_T_COMPLETE set before the TMR - * are skipped by the TMR drain, but their async completion work - * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) - * may still be pending in target_completion_wq. - * - * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery - * commands (e.g. TUR) immediately after this handler returns SUCCESS — - * if deferred work is still pending, the memset in queuecommand would - * zero the se_cmd while the work accesses it, leaking the LUN - * percpu_ref and hanging configfs unlink forever. - * - * Use blk_mq_tagset_busy_iter() to find all started requests and - * flush_work() on each — the same pattern used by mpi3mr, scsi_debug, - * and other SCSI drivers to drain outstanding commands during reset. - */ - blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL); - - tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; - return SUCCESS; -} - static const struct scsi_host_template tcm_loop_driver_template = { .show_info = tcm_loop_show_info, .proc_name = "tcm_loopback", @@ -341,7 +278,6 @@ static const struct scsi_host_template tcm_loop_driver_template = { .change_queue_depth = scsi_change_queue_depth, .eh_abort_handler = tcm_loop_abort_task, .eh_device_reset_handler = tcm_loop_device_reset, - .eh_target_reset_handler = tcm_loop_target_reset, .this_id = -1, .sg_tablesize = 256, .max_sectors = 0xFFFF, From 1c3044cab23a056ea28da47da1cdd667a39df0b8 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:22:30 +0100 Subject: [PATCH 3154/5214] pinctrl: mediatek: mt8516: Fix Schmitt trigger register offset of pins 34-39 The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Signed-off-by: Luca Leonardo Scorcia Fixes: 264667112ef0 ("pinctrl: mediatek: Add MT8516 Pinctrl driver") Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mt8516.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c index abda75d4354e..68d6638e7f4b 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8516_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From 439bc91d20188901dac698bed4921caac76d9074 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:23:32 +0100 Subject: [PATCH 3155/5214] pinctrl: mediatek: mt8167: Fix Schmitt trigger register offset of pins 34-39 The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Value was verified with SoC data sheet. Signed-off-by: Luca Leonardo Scorcia Fixes: 82d70627e94a ("pinctrl: mediatek: Add MT8167 Pinctrl driver") Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mt8167.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8167.c b/drivers/pinctrl/mediatek/pinctrl-mt8167.c index 143c26622272..c812d614e9d4 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8167.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8167.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8167_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From 69397c92de77525f70aa43cf3a47256cef409382 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 5 Jun 2026 10:46:10 -0700 Subject: [PATCH 3156/5214] KVM: x86/mmu: Recursively zap orphaned nested TDP shadow pages on emulated writes Recursively zap orphaned nested TDP shadow pages when emulating a guest write to a shadowed page table, regardless of whether or not the associated (parent) shadow page will be zapped, e.g. due to detected write-flooding. This plugs a hole where KVM fails to reclaim defunct, unsync shadow pages for select L1 hypervisor patterns. Commit 2de4085cccea ("KVM: x86/MMU: Recursively zap nested TDP SPs when zapping last/only parent") modified KVM to recursively zap synchronized shadow pages (KVM already recursively zaps unsync children) when a child is orphaned. But the fix effectively only applied the logic to kvm_mmu_page_unlink_children(), i.e. only performs the recursive zap when KVM is already zapping a parent SP and processing its children. If L1 zaps SPTEs bottom-up (4KiB => 2MiB => ...), as KVM's TDP MMU does with CONFIG_KVM_PROVE_MMU=n since commit 8ca983631f3c ("KVM: x86/mmu: Zap invalidated TDP MMU roots at 4KiB granularity"), then KVM (as L0) will leak upwards of 4 shadow pages per GiB of L2 guest memory. Over hundreds or thousands of L2 boots, if the VM is "lucky" enough to escape write-flooding detection, i.e. not trigger reclaim of the orphaned shadow pages by dumb luck, then it's possible to end up with tens or even hundreds of thousands of unsync shadow pages and associated rmap entries. Polluting the hash table and rmap entries with a horde of stale entries can eventually degrade L2 guest boot time by an order of magnitude, especially if there is any antagonistic activity in the host, i.e. anything that will contend for mmu_lock and/or needs to walk rmaps. With "top"-down zapping, where "top" is 1GiB or above, then L0 KVM is effectively limited to leaking 4 shadow pages per 256 GiB of memory, as KVM's write flooding detection will kick in on the third write to an L1 TDP PUD, and thus recursively zap the entire 256 GiB range of the parent PGD. I.e. even though L1 KVM still recursively zaps 2MiB => 4KiB SPTEs when zapping each 1GiB SPTE, KVM only gets through two of the 1GiB SPTEs before dropping everything. E.g. hacking tracing into L0 KVM's kvm_mmu_track_write(), the top-down zapping of L1's TDP MMU for an L2 with 16GiB of memory leads to: gpa = 107407000, old = 800000010741bd07, new = 8000000000000000, level = 3, flood = 0 gpa = 10741b000, old = 8000000112fb2d07, new = 80000000000001a0, level = 2, flood = 0 gpa = 10741b008, old = 800000012509cd07, new = 80000000000001a0, level = 2, flood = 1 gpa = 10741b010, old = 80000001114b9d07, new = 80000000000001a0, level = 2, flood = 2 gpa = 107407008, old = 8000000112fb5d07, new = 8000000000000000, level = 3, flood = 1 gpa = 112fb5298, old = 8000000106f43d07, new = 80000000000001a0, level = 2, flood = 0 gpa = 112fb52a0, old = 8000000106f4dd07, new = 80000000000001a0, level = 2, flood = 1 gpa = 112fb5ea0, old = 8000000120490d07, new = 80000000000001a0, level = 2, flood = 2 gpa = 107407010, old = 8000000106df2d07, new = 8000000000000000, level = 3, flood = 2 gpa = 107410000, old = 8000000107408d07, new = 8000000000000000, level = 5, flood = 0 gpa = 107408000, old = 8000000107407d07, new = 80000000000001a0, level = 4, flood = 0 Contrast that with a bottom-up zap, which effectively allows all 2MiB SPTEs in L1 to leak their children. gpa = 167939000, old = 800000011c8f4d07, new = 8000000000000000, level = 2, flood = 0 gpa = 167939020, old = 8000000104407d07, new = 8000000000000000, level = 2, flood = 1 gpa = 167939028, old = 800000011ed20d07, new = 8000000000000000, level = 2, flood = 2 gpa = 118c70bb0, old = 8000000167ab9d07, new = 8000000000000000, level = 2, flood = 0 gpa = 118c70bb8, old = 8000000163913d07, new = 8000000000000000, level = 2, flood = 1 gpa = 118c70de8, old = 800000011cc9dd07, new = 8000000000000000, level = 2, flood = 2 gpa = 160be7fb0, old = 800000011d322d07, new = 8000000000000000, level = 2, flood = 1 gpa = 160be7fb8, old = 8000000126b1bd07, new = 8000000000000000, level = 2, flood = 2 gpa = 1634ab000, old = 800000010e984d07, new = 8000000000000000, level = 2, flood = 0 gpa = 1634ab008, old = 800000016879fd07, new = 8000000000000000, level = 2, flood = 1 gpa = 1634ab010, old = 800000016879ed07, new = 8000000000000000, level = 2, flood = 2 gpa = 11e3f1e48, old = 8000000168a33d07, new = 8000000000000000, level = 2, flood = 0 gpa = 11e3f1e50, old = 80000001664dcd07, new = 8000000000000000, level = 2, flood = 1 gpa = 1167eacb8, old = 8000000166544d07, new = 8000000000000000, level = 2, flood = 0 gpa = 1167eacc0, old = 800000015c16bd07, new = 8000000000000000, level = 2, flood = 1 gpa = 1689e89b8, old = 800000015f296d07, new = 8000000000000000, level = 2, flood = 0 gpa = 1689e89c0, old = 8000000167ca8d07, new = 8000000000000000, level = 2, flood = 1 gpa = 107b35eb8, old = 8000000161e71d07, new = 8000000000000000, level = 2, flood = 0 gpa = 107b35ec0, old = 8000000118cf3d07, new = 8000000000000000, level = 2, flood = 1 gpa = 118cf2d48, old = 8000000118cf1d07, new = 8000000000000000, level = 2, flood = 0 gpa = 118cf2d50, old = 8000000118cf0d07, new = 8000000000000000, level = 2, flood = 1 gpa = 118dcb770, old = 8000000118dcad07, new = 8000000000000000, level = 2, flood = 0 gpa = 118dcb778, old = 8000000118dc9d07, new = 8000000000000000, level = 2, flood = 1 gpa = 118dc87e8, old = 8000000126997d07, new = 8000000000000000, level = 2, flood = 0 gpa = 118dc87f0, old = 8000000126996d07, new = 8000000000000000, level = 2, flood = 1 gpa = 126995148, old = 8000000126994d07, new = 8000000000000000, level = 2, flood = 0 gpa = 126995150, old = 8000000103477d07, new = 8000000000000000, level = 2, flood = 1 gpa = 1034764c8, old = 8000000103475d07, new = 8000000000000000, level = 2, flood = 0 gpa = 1034764d0, old = 8000000103474d07, new = 8000000000000000, level = 2, flood = 1 gpa = 10ea4b788, old = 800000010ea4ad07, new = 8000000000000000, level = 2, flood = 0 gpa = 10ea4b790, old = 800000010ea49d07, new = 8000000000000000, level = 2, flood = 1 gpa = 10ea48928, old = 800000011a5bfd07, new = 8000000000000000, level = 2, flood = 0 gpa = 10ea48930, old = 800000011a5bed07, new = 8000000000000000, level = 2, flood = 1 gpa = 11a5bd0d8, old = 800000011a5bcd07, new = 8000000000000000, level = 2, flood = 0 gpa = 11a5bd0e0, old = 800000011d323d07, new = 8000000000000000, level = 2, flood = 1 gpa = 122ce2b40, old = 800000011fe0bd07, new = 8000000000000000, level = 2, flood = 0 gpa = 122ce2b48, old = 800000010e985d07, new = 8000000000000000, level = 2, flood = 1 gpa = 122ce2b50, old = 8000000161c9dd07, new = 8000000000000000, level = 2, flood = 2 gpa = 16864c000, old = 8000000167939d07, new = 8000000000000000, level = 3, flood = 0 gpa = 16864c008, old = 8000000118c70d07, new = 8000000000000000, level = 3, flood = 1 gpa = 16864c010, old = 80000001688a6d07, new = 8000000000000000, level = 3, flood = 2 gpa = 11c8f7000, old = 80000001608a7d07, new = 8000000000000000, level = 5, flood = 0 gpa = 1608a7000, old = 800000016864cd07, new = 80000000000001a0, level = 4, flood = 0 Note, in the shadow MMU, "level" describes the level a shadow page "points" at, not the level of its associated SPTE. I.e. when write-flooding of 1GiB PUD entries is detected, KVM recursively zaps shadow pages covering 256GiB worth of memory. And as shown above, KVM's write-flooding detection operates at all levels, so a single PMD (in L1) can effectively only leak two unsync children (4KiB shadow pages) before it gets recursively zapped. As a result, for the top-down zap, L0 KVM will leak at most 4 unsync shadow pages per 256GiB of L2 memory. The top-down zap also makes it more likely that L1 will self-heal (to some extent), as any shadow pages that are "rediscovered" by future runs of L2 can get reclaimed by a recursive zap, whereas bottom-up zapping orphans shadow pages over and over. Note, in theory, there is some risk of over-zapping, e.g. due to zapping a a large branch of the paging tree that L1 is only temporarily removing. In practice, the usage patterns of hypervisors are highly unlikely to trigger false positives. E.g. temporarily changing paging protections is typically done at the leaf, not on a non-leaf entry. And if the L1 hypervisor is updating large swaths of PTEs, e.g. to (temporarily?) remove chunks of memory from L2, then L0 KVM's write-flooding detection will kick in, and the children would be zapped anyways. Fixes: 2de4085cccea ("KVM: x86/MMU: Recursively zap nested TDP SPs when zapping last/only parent") Cc: Yosry Ahmed Cc: Jim Mattson Cc: James Houghton Reviewed-by: Jim Mattson Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260605174611.2222504-2-seanjc@google.com Signed-off-by: Sean Christopherson --- arch/x86/kvm/mmu/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f8aa7eda661e..6e881c40f823 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -6357,7 +6357,7 @@ void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, while (npte--) { entry = *spte; - mmu_page_zap_pte(vcpu->kvm, sp, spte, NULL); + mmu_page_zap_pte(vcpu->kvm, sp, spte, &invalid_list); if (gentry && sp->role.level != PG_LEVEL_4K) ++vcpu->kvm->stat.mmu_pde_zapped; if (is_shadow_present_pte(entry)) From 248ff9624dc09deed69c718c092d78e04d3bda17 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Mon, 1 Jun 2026 17:26:42 +0200 Subject: [PATCH 3157/5214] dt-bindings: pinctrl: mediatek: mt6795: document the slew-rate property The driver for MT6795 pinctrl already supports the slew-rate property. Add its description to the documentation. Signed-off-by: Luca Leonardo Scorcia Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../bindings/pinctrl/mediatek,mt6795-pinctrl.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml index 68e91c05f122..9a937f414cc9 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml @@ -152,6 +152,14 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3] + slew-rate: + description: | + Set the slew rate. Valid arguments are described as below: + 0: Normal slew rate + 1: Slower slew + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + required: - pinmux From 388d2fe16283d68e4be8c5317809a4a218ebeace Mon Sep 17 00:00:00 2001 From: Yu-Chun Lin Date: Mon, 1 Jun 2026 15:52:29 +0800 Subject: [PATCH 3158/5214] dt-bindings: pinctrl: realtek,rtd1625: Fix input voltage property name The property 'input-voltage-microvolt' is a typo. Rename it to 'input-threshold-voltage-microvolt' to align with the standard pin configuration defined in pincfg-node.yaml and parsed by pinconf-generic.c. Fixes: f6ea7004e926 ("dt-bindings: pinctrl: realtek: Add RTD1625 pinctrl binding") Signed-off-by: Yu-Chun Lin Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml index 9562a043707e..adc5955a2047 100644 --- a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml @@ -110,7 +110,7 @@ patternProperties: input-schmitt-disable: true - input-voltage-microvolt: + input-threshold-voltage-microvolt: description: | Select the input receiver voltage domain for the pin. Valid arguments are: From fd9490bdd4536a8e0911578f62164176b9000552 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 2 Jun 2026 16:11:16 -0500 Subject: [PATCH 3159/5214] pinctrl: PINCTRL_STMFX should depend on CONFIG_OF Commit e785c990adcc ("pinctrl: Kconfig: drop unneeded dependencies on OF_GPIO") removed a redundant dependecy on CONFIG_OF_GPIO for several pinctrl drivers, but this change also removed a dependency on CONFIG_OF for some of those drivers. Normally, this wouldn't be a problem, but PINCTRL_STMFX also selected MFD_STMFX, which does depend on CONFIG_OF. This conflict allows MFD_STMFX to be enabled even if CONFIG_OF is disabled. Fix this by also having PINCTRL_STMFX depend on CONFIG_OF. This is okay because the pinctrl-stmfx driver actually does depend on CONFIG_OF functions. Fixes: e785c990adcc ("pinctrl: Kconfig: drop unneeded dependencies on OF_GPIO") Signed-off-by: Timur Tabi Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index de1554809eec..3624cfe4f2a2 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -558,6 +558,7 @@ config PINCTRL_ST config PINCTRL_STMFX tristate "STMicroelectronics STMFX GPIO expander pinctrl driver" + depends on OF depends on I2C depends on HAS_IOMEM select GENERIC_PINCONF From f5cf8c92a2b9fd176d90b6e217ed50fbb5d1f48d Mon Sep 17 00:00:00 2001 From: Joshua Hahn Date: Fri, 29 May 2026 13:27:54 -0700 Subject: [PATCH 3160/5214] mm/nodemask: correctly describe nodemask operation return types Commit 0dfe54071d7c8 ("nodemask: Fix return values to be unsigned") changed a number of nodemask operations that used to return int to returning a bool instead. However, it did not update the comment block that described these functions, leaving the documentation incorrect. Fix the comment block to accurately describe the functions. Also fix a typo (unsigend --> unsigned), and fix a callsite in mempolicy.c that did not get updated during the conversion. No functional changes intended; changes are purely cosmetic. Link: https://lore.kernel.org/20260529202755.1846800-1-joshua.hahnjy@gmail.com Signed-off-by: Joshua Hahn Reviewed-by: SeongJae Park Cc: Alistair Popple Cc: Byungchul Park Cc: David Hildenbrand Cc: Gregory Price Cc: Matthew Brost Cc: Rakie Kim Cc: Rasmus Villemoes Cc: Ying Huang Cc: Yury Norov (NVIDIA) Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/nodemask.h | 18 +++++++++--------- mm/mempolicy.c | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index 204c92462f3c..b842aa525546 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h @@ -24,23 +24,23 @@ * void nodes_setall(mask) set all bits * void nodes_clear(mask) clear all bits * int node_isset(node, mask) true iff bit 'node' set in mask - * int node_test_and_set(node, mask) test and set bit 'node' in mask + * bool node_test_and_set(node, mask) test and set bit 'node' in mask * - * void nodes_and(dst, src1, src2) dst = src1 & src2 [intersection] + * bool nodes_and(dst, src1, src2) dst = src1 & src2 [intersection] * void nodes_or(dst, src1, src2) dst = src1 | src2 [union] * void nodes_xor(dst, src1, src2) dst = src1 ^ src2 - * void nodes_andnot(dst, src1, src2) dst = src1 & ~src2 + * bool nodes_andnot(dst, src1, src2) dst = src1 & ~src2 * void nodes_complement(dst, src) dst = ~src * - * int nodes_equal(mask1, mask2) Does mask1 == mask2? - * int nodes_intersects(mask1, mask2) Do mask1 and mask2 intersect? - * int nodes_subset(mask1, mask2) Is mask1 a subset of mask2? - * int nodes_empty(mask) Is mask empty (no bits sets)? - * int nodes_full(mask) Is mask full (all bits sets)? + * bool nodes_equal(mask1, mask2) Does mask1 == mask2? + * bool nodes_intersects(mask1, mask2) Do mask1 and mask2 intersect? + * bool nodes_subset(mask1, mask2) Is mask1 a subset of mask2? + * bool nodes_empty(mask) Is mask empty (no bits sets)? + * bool nodes_full(mask) Is mask full (all bits sets)? * int nodes_weight(mask) Hamming weight - number of set bits * * unsigned int first_node(mask) Number lowest set bit, or MAX_NUMNODES - * unsigend int next_node(node, mask) Next node past 'node', or MAX_NUMNODES + * unsigned int next_node(node, mask) Next node past 'node', or MAX_NUMNODES * unsigned int next_node_in(node, mask) Next node past 'node', or wrap to first, * or MAX_NUMNODES * unsigned int first_unset_node(mask) First node not set in mask, or diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 4e4421b22b59..36699fabd3c2 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2865,7 +2865,7 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b) case MPOL_PREFERRED: case MPOL_PREFERRED_MANY: case MPOL_WEIGHTED_INTERLEAVE: - return !!nodes_equal(a->nodes, b->nodes); + return nodes_equal(a->nodes, b->nodes); case MPOL_LOCAL: return true; default: From 79a031583ca5bb3d5484178f579fea97706f1ed6 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:08 -0400 Subject: [PATCH 3161/5214] mm: list_lru: fix set_shrinker_bit() call during race with cgroup deletion Patch series "mm: switch THP shrinker to list_lru", v5. The open-coded deferred split queue has issues. It's not NUMA-aware (when cgroup is enabled), and it's more complicated in the callsites interacting with it. Switching to list_lru fixes the NUMA problem and streamlines things. It also simplifies planned shrinker work. Patch 1 fixes a pre-existing list_lru bug where the shrinker bit is set on the caller's memcg rather than the ancestor whose sublist the item actually lands on after a walk-up. Standalone, backportable; the rest of the series depends on it. Patches 2-5 are cleanups and small refactors in list_lru code. They're basically independent, but make the THP shrinker conversion easier. Patch 6 extends the list_lru API to allow the caller to control the locking scope. The THP shrinker has private state it needs to keep synchronized with the LRU state. Patch 7 extends the list_lru API with a convenience helper to do list_lru head allocation (memcg_list_lru_alloc) when coming from a folio. Anon THPs are instantiated in several places, and with the folio reparenting patches pending, folio_memcg() access is now a more delicate dance. This avoids having to replicate that dance everywhere. Patch 8 flattens the alloc_anon_folio() retry loop so the next patch's list_lru hook lands as a clean addition rather than nested deep inside an if (folio) block. Patch 9 finally switches the deferred_split_queue to list_lru. This patch (of 9): When list_lru_add() races with cgroup deletion, the shrinker bit is set on the wrong group and lost. This can cause a shrinker run to miss the cgroup that actually has the object. When the passed in memcg is dead, the function finds the first non-dead parent from the passed in memcg and adds the object there; but the shrinker bit is set on the memcg that was passed in. This bug is as old as the shrinker bitmap itself. Fix it by returning the "effective" memcg from the locking function, and have the caller use that. Link: https://lore.kernel.org/20260527204757.2544958-1-hannes@cmpxchg.org Link: https://lore.kernel.org/20260527204757.2544958-2-hannes@cmpxchg.org Fixes: fae91d6d8be5 ("mm/list_lru.c: set bit in memcg shrinker bitmap on first list_lru item appearance") Signed-off-by: Johannes Weiner Reported-by: Usama Arif Reported-by: Sashiko Acked-by: Usama Arif Reviewed-by: Wei Yang Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: David Hildenbrand Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Shakeel Butt Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/list_lru.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index dd29bcf8eb5f..45d1b97737ea 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -77,14 +77,14 @@ static inline bool lock_list_lru(struct list_lru_one *l, bool irq) } static inline struct list_lru_one * -lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup *memcg, - bool irq, bool skip_empty) +lock_list_lru_of_memcg(struct list_lru *lru, int nid, + struct mem_cgroup **memcg, bool irq, bool skip_empty) { struct list_lru_one *l; rcu_read_lock(); again: - l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(memcg)); + l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(*memcg)); if (likely(l) && lock_list_lru(l, irq)) { rcu_read_unlock(); return l; @@ -97,8 +97,8 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup *memcg, rcu_read_unlock(); return NULL; } - VM_WARN_ON(!css_is_dying(&memcg->css)); - memcg = parent_mem_cgroup(memcg); + VM_WARN_ON(!css_is_dying(&(*memcg)->css)); + *memcg = parent_mem_cgroup(*memcg); goto again; } @@ -135,8 +135,8 @@ list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx) } static inline struct list_lru_one * -lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup *memcg, - bool irq, bool skip_empty) +lock_list_lru_of_memcg(struct list_lru *lru, int nid, + struct mem_cgroup **memcg, bool irq, bool skip_empty) { struct list_lru_one *l = &lru->node[nid].lru; @@ -164,12 +164,16 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, struct list_lru_node *nlru = &lru->node[nid]; struct list_lru_one *l; - l = lock_list_lru_of_memcg(lru, nid, memcg, false, false); + l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); if (!l) return false; if (list_empty(item)) { list_add_tail(item, &l->list); - /* Set shrinker bit if the first element was added */ + /* + * Set shrinker bit on the memcg that owns the locked + * sublist - lock_list_lru_of_memcg() may have walked up + * past a dying memcg, and the bit must be set there. + */ if (!l->nr_items++) set_shrinker_bit(memcg, nid, lru_shrinker_id(lru)); unlock_list_lru(l, false); @@ -204,7 +208,7 @@ bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid, { struct list_lru_node *nlru = &lru->node[nid]; struct list_lru_one *l; - l = lock_list_lru_of_memcg(lru, nid, memcg, false, false); + l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); if (!l) return false; if (!list_empty(item)) { @@ -288,7 +292,7 @@ __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg, unsigned long isolated = 0; restart: - l = lock_list_lru_of_memcg(lru, nid, memcg, irq_off, true); + l = lock_list_lru_of_memcg(lru, nid, &memcg, irq_off, true); if (!l) return isolated; list_for_each_safe(item, n, &l->list) { From 1923b1d76b964adc055b5a4bd877dda50550298f Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:09 -0400 Subject: [PATCH 3162/5214] mm: list_lru: lock_list_lru_of_memcg() cannot return NULL if !skip_empty skip_empty is only for the shrinker to abort and skip a list that's empty or whose cgroup is being deleted. For list additions and deletions, the cgroup hierarchy is walked upwards until a valid list_lru head is found, or it will fall back to the node list. Acquiring the lock won't fail. Remove the NULL checks in those callers. Link: https://lore.kernel.org/20260527204757.2544958-3-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Liam R. Howlett (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/list_lru.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index 45d1b97737ea..77999ed78fa5 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -165,8 +165,6 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, struct list_lru_one *l; l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); - if (!l) - return false; if (list_empty(item)) { list_add_tail(item, &l->list); /* @@ -208,9 +206,8 @@ bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid, { struct list_lru_node *nlru = &lru->node[nid]; struct list_lru_one *l; + l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); - if (!l) - return false; if (!list_empty(item)) { list_del_init(item); l->nr_items--; From 82d8bca1c715e9ed31eaeb5197a0ba00bf8be597 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:10 -0400 Subject: [PATCH 3163/5214] mm: list_lru: deduplicate unlock_list_lru() The MEMCG and !MEMCG variants are the same. lock_list_lru() has the same pattern when bailing. Consolidate into a common implementation. Link: https://lore.kernel.org/20260527204757.2544958-4-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Liam R. Howlett (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/list_lru.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index 77999ed78fa5..5497034e80f3 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -15,6 +15,14 @@ #include "slab.h" #include "internal.h" +static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off) +{ + if (irq_off) + spin_unlock_irq(&l->lock); + else + spin_unlock(&l->lock); +} + #ifdef CONFIG_MEMCG static LIST_HEAD(memcg_list_lrus); static DEFINE_MUTEX(list_lrus_mutex); @@ -67,10 +75,7 @@ static inline bool lock_list_lru(struct list_lru_one *l, bool irq) else spin_lock(&l->lock); if (unlikely(READ_ONCE(l->nr_items) == LONG_MIN)) { - if (irq) - spin_unlock_irq(&l->lock); - else - spin_unlock(&l->lock); + unlock_list_lru(l, irq); return false; } return true; @@ -101,14 +106,6 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, *memcg = parent_mem_cgroup(*memcg); goto again; } - -static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off) -{ - if (irq_off) - spin_unlock_irq(&l->lock); - else - spin_unlock(&l->lock); -} #else static void list_lru_register(struct list_lru *lru) { @@ -147,14 +144,6 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, return l; } - -static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off) -{ - if (irq_off) - spin_unlock_irq(&l->lock); - else - spin_unlock(&l->lock); -} #endif /* CONFIG_MEMCG */ /* The caller must ensure the memcg lifetime. */ From 8b98cfe2c52d7492a024b655e0978545845646cb Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:11 -0400 Subject: [PATCH 3164/5214] mm: list_lru: move list dead check to lock_list_lru_of_memcg() Only the MEMCG variant of lock_list_lru() needs to check if there is a race with cgroup deletion and list reparenting. Move the check to the caller, so that the next patch can unify the lock_list_lru() variants. Link: https://lore.kernel.org/20260527204757.2544958-5-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Liam R. Howlett (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/list_lru.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index 5497034e80f3..7d0523e44010 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -68,17 +68,12 @@ list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx) return &lru->node[nid].lru; } -static inline bool lock_list_lru(struct list_lru_one *l, bool irq) +static inline void lock_list_lru(struct list_lru_one *l, bool irq) { if (irq) spin_lock_irq(&l->lock); else spin_lock(&l->lock); - if (unlikely(READ_ONCE(l->nr_items) == LONG_MIN)) { - unlock_list_lru(l, irq); - return false; - } - return true; } static inline struct list_lru_one * @@ -90,9 +85,13 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, rcu_read_lock(); again: l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(*memcg)); - if (likely(l) && lock_list_lru(l, irq)) { - rcu_read_unlock(); - return l; + if (likely(l)) { + lock_list_lru(l, irq); + if (likely(READ_ONCE(l->nr_items) != LONG_MIN)) { + rcu_read_unlock(); + return l; + } + unlock_list_lru(l, irq); } /* * Caller may simply bail out if raced with reparenting or From bc7adb3b3f6ad3f1cf8a030be0034f61e7580fe4 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:12 -0400 Subject: [PATCH 3165/5214] mm: list_lru: deduplicate lock_list_lru() The MEMCG and !MEMCG paths have the same pattern. Share the code. Link: https://lore.kernel.org/20260527204757.2544958-6-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Liam R. Howlett (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/list_lru.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/mm/list_lru.c b/mm/list_lru.c index 7d0523e44010..fdb3fe2ea64f 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -15,6 +15,14 @@ #include "slab.h" #include "internal.h" +static inline void lock_list_lru(struct list_lru_one *l, bool irq) +{ + if (irq) + spin_lock_irq(&l->lock); + else + spin_lock(&l->lock); +} + static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off) { if (irq_off) @@ -68,14 +76,6 @@ list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx) return &lru->node[nid].lru; } -static inline void lock_list_lru(struct list_lru_one *l, bool irq) -{ - if (irq) - spin_lock_irq(&l->lock); - else - spin_lock(&l->lock); -} - static inline struct list_lru_one * lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup **memcg, bool irq, bool skip_empty) @@ -136,10 +136,7 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, { struct list_lru_one *l = &lru->node[nid].lru; - if (irq) - spin_lock_irq(&l->lock); - else - spin_lock(&l->lock); + lock_list_lru(l, irq); return l; } From 1479b44c7203b2cad3393533c64aa16d42056310 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:13 -0400 Subject: [PATCH 3166/5214] mm: list_lru: introduce caller locking for additions and deletions Locking is currently internal to the list_lru API. However, a caller might want to keep auxiliary state synchronized with the LRU state. For example, the THP shrinker uses the lock of its custom LRU to keep PG_partially_mapped and vmstats consistent. To allow the THP shrinker to switch to list_lru, provide normal and irqsafe locking primitives as well as caller-locked variants of the addition and deletion functions. Link: https://lore.kernel.org/20260527204757.2544958-7-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Liam R. Howlett (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/list_lru.h | 43 +++++++++++++ mm/list_lru.c | 133 ++++++++++++++++++++++++++++++--------- 2 files changed, 145 insertions(+), 31 deletions(-) diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h index fe739d35a864..134cb3e5652a 100644 --- a/include/linux/list_lru.h +++ b/include/linux/list_lru.h @@ -83,6 +83,46 @@ int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru, gfp_t gfp); void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *parent); +/** + * list_lru_lock: lock the sublist for the given node and memcg + * @lru: the lru pointer + * @nid: the node id of the sublist to lock. + * @memcg: pointer to the cgroup of the sublist to lock. On return, + * updated to the cgroup whose sublist was actually locked, + * which may be an ancestor if the original memcg was dying. + * + * Returns the locked list_lru_one sublist. The caller must call + * list_lru_unlock() when done. + * + * You must ensure that the memcg is not freed during this call (e.g., with + * rcu or by taking a css refcnt). + * + * Return: the locked list_lru_one, or NULL on failure + */ +struct list_lru_one *list_lru_lock(struct list_lru *lru, int nid, + struct mem_cgroup **memcg); + +/** + * list_lru_unlock: unlock a sublist locked by list_lru_lock() + * @l: the list_lru_one to unlock + */ +void list_lru_unlock(struct list_lru_one *l); + +struct list_lru_one *list_lru_lock_irq(struct list_lru *lru, int nid, + struct mem_cgroup **memcg); +void list_lru_unlock_irq(struct list_lru_one *l); + +struct list_lru_one *list_lru_lock_irqsave(struct list_lru *lru, int nid, + struct mem_cgroup **memcg, unsigned long *irq_flags); +void list_lru_unlock_irqrestore(struct list_lru_one *l, + unsigned long *irq_flags); + +/* Caller-locked variants, see list_lru_add() etc for documentation */ +bool __list_lru_add(struct list_lru *lru, struct list_lru_one *l, + struct list_head *item, int nid, struct mem_cgroup *memcg); +bool __list_lru_del(struct list_lru *lru, struct list_lru_one *l, + struct list_head *item, int nid); + /** * list_lru_add: add an element to the lru list's tail * @lru: the lru pointer @@ -115,6 +155,9 @@ void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *paren bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, struct mem_cgroup *memcg); +bool list_lru_add_irq(struct list_lru *lru, struct list_head *item, int nid, + struct mem_cgroup *memcg); + /** * list_lru_add_obj: add an element to the lru list's tail * @lru: the lru pointer diff --git a/mm/list_lru.c b/mm/list_lru.c index fdb3fe2ea64f..402bb028114d 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -15,17 +15,23 @@ #include "slab.h" #include "internal.h" -static inline void lock_list_lru(struct list_lru_one *l, bool irq) +static inline void lock_list_lru(struct list_lru_one *l, bool irq, + unsigned long *irq_flags) { - if (irq) + if (irq_flags) + spin_lock_irqsave(&l->lock, *irq_flags); + else if (irq) spin_lock_irq(&l->lock); else spin_lock(&l->lock); } -static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off) +static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off, + unsigned long *irq_flags) { - if (irq_off) + if (irq_flags) + spin_unlock_irqrestore(&l->lock, *irq_flags); + else if (irq_off) spin_unlock_irq(&l->lock); else spin_unlock(&l->lock); @@ -78,7 +84,8 @@ list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx) static inline struct list_lru_one * lock_list_lru_of_memcg(struct list_lru *lru, int nid, - struct mem_cgroup **memcg, bool irq, bool skip_empty) + struct mem_cgroup **memcg, bool irq, + unsigned long *irq_flags, bool skip_empty) { struct list_lru_one *l; @@ -86,12 +93,12 @@ lock_list_lru_of_memcg(struct list_lru *lru, int nid, again: l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(*memcg)); if (likely(l)) { - lock_list_lru(l, irq); + lock_list_lru(l, irq, irq_flags); if (likely(READ_ONCE(l->nr_items) != LONG_MIN)) { rcu_read_unlock(); return l; } - unlock_list_lru(l, irq); + unlock_list_lru(l, irq, irq_flags); } /* * Caller may simply bail out if raced with reparenting or @@ -132,24 +139,58 @@ list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx) static inline struct list_lru_one * lock_list_lru_of_memcg(struct list_lru *lru, int nid, - struct mem_cgroup **memcg, bool irq, bool skip_empty) + struct mem_cgroup **memcg, bool irq, + unsigned long *irq_flags, bool skip_empty) { struct list_lru_one *l = &lru->node[nid].lru; - lock_list_lru(l, irq); + lock_list_lru(l, irq, irq_flags); return l; } #endif /* CONFIG_MEMCG */ -/* The caller must ensure the memcg lifetime. */ -bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, - struct mem_cgroup *memcg) +struct list_lru_one *list_lru_lock(struct list_lru *lru, int nid, + struct mem_cgroup **memcg) { - struct list_lru_node *nlru = &lru->node[nid]; - struct list_lru_one *l; + return lock_list_lru_of_memcg(lru, nid, memcg, /*irq=*/false, + /*irq_flags=*/NULL, /*skip_empty=*/false); +} - l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); +void list_lru_unlock(struct list_lru_one *l) +{ + unlock_list_lru(l, /*irq_off=*/false, /*irq_flags=*/NULL); +} + +struct list_lru_one *list_lru_lock_irq(struct list_lru *lru, int nid, + struct mem_cgroup **memcg) +{ + return lock_list_lru_of_memcg(lru, nid, memcg, /*irq=*/true, + /*irq_flags=*/NULL, /*skip_empty=*/false); +} + +void list_lru_unlock_irq(struct list_lru_one *l) +{ + unlock_list_lru(l, /*irq_off=*/true, /*irq_flags=*/NULL); +} + +struct list_lru_one *list_lru_lock_irqsave(struct list_lru *lru, int nid, + struct mem_cgroup **memcg, + unsigned long *flags) +{ + return lock_list_lru_of_memcg(lru, nid, memcg, /*irq=*/true, + /*irq_flags=*/flags, /*skip_empty=*/false); +} + +void list_lru_unlock_irqrestore(struct list_lru_one *l, unsigned long *flags) +{ + unlock_list_lru(l, /*irq_off=*/true, /*irq_flags=*/flags); +} + +bool __list_lru_add(struct list_lru *lru, struct list_lru_one *l, + struct list_head *item, int nid, + struct mem_cgroup *memcg) +{ if (list_empty(item)) { list_add_tail(item, &l->list); /* @@ -159,15 +200,50 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, */ if (!l->nr_items++) set_shrinker_bit(memcg, nid, lru_shrinker_id(lru)); - unlock_list_lru(l, false); - atomic_long_inc(&nlru->nr_items); + atomic_long_inc(&lru->node[nid].nr_items); return true; } - unlock_list_lru(l, false); return false; } EXPORT_SYMBOL_GPL(list_lru_add); +bool __list_lru_del(struct list_lru *lru, struct list_lru_one *l, + struct list_head *item, int nid) +{ + if (!list_empty(item)) { + list_del_init(item); + l->nr_items--; + atomic_long_dec(&lru->node[nid].nr_items); + return true; + } + return false; +} + +/* The caller must ensure the memcg lifetime. */ +bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid, + struct mem_cgroup *memcg) +{ + struct list_lru_one *l; + bool ret; + + l = list_lru_lock(lru, nid, &memcg); + ret = __list_lru_add(lru, l, item, nid, memcg); + list_lru_unlock(l); + return ret; +} + +bool list_lru_add_irq(struct list_lru *lru, struct list_head *item, + int nid, struct mem_cgroup *memcg) +{ + struct list_lru_one *l; + bool ret; + + l = list_lru_lock_irq(lru, nid, &memcg); + ret = __list_lru_add(lru, l, item, nid, memcg); + list_lru_unlock_irq(l); + return ret; +} + bool list_lru_add_obj(struct list_lru *lru, struct list_head *item) { bool ret; @@ -189,19 +265,13 @@ EXPORT_SYMBOL_GPL(list_lru_add_obj); bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid, struct mem_cgroup *memcg) { - struct list_lru_node *nlru = &lru->node[nid]; struct list_lru_one *l; + bool ret; - l = lock_list_lru_of_memcg(lru, nid, &memcg, false, false); - if (!list_empty(item)) { - list_del_init(item); - l->nr_items--; - unlock_list_lru(l, false); - atomic_long_dec(&nlru->nr_items); - return true; - } - unlock_list_lru(l, false); - return false; + l = list_lru_lock(lru, nid, &memcg); + ret = __list_lru_del(lru, l, item, nid); + list_lru_unlock(l); + return ret; } bool list_lru_del_obj(struct list_lru *lru, struct list_head *item) @@ -274,7 +344,8 @@ __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg, unsigned long isolated = 0; restart: - l = lock_list_lru_of_memcg(lru, nid, &memcg, irq_off, true); + l = lock_list_lru_of_memcg(lru, nid, &memcg, /*irq=*/irq_off, + /*irq_flags=*/NULL, /*skip_empty=*/true); if (!l) return isolated; list_for_each_safe(item, n, &l->list) { @@ -315,7 +386,7 @@ __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg, BUG(); } } - unlock_list_lru(l, irq_off); + unlock_list_lru(l, irq_off, NULL); out: return isolated; } From ae64f07a6a4018c73111b3cd4e1c5598ce5cfa84 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:14 -0400 Subject: [PATCH 3167/5214] mm: list_lru: introduce folio_memcg_list_lru_alloc() memcg_list_lru_alloc() is called every time an object that may end up on the list_lru is created. It needs to quickly check if the list_lru heads for the memcg already exist, and allocate them when they don't. Doing this with folio objects is tricky: folio_memcg() is not stable and requires either RCU protection or pinning the cgroup. But it's desirable to make the existence check lightweight under RCU, and only pin the memcg when we need to allocate list_lru heads and may block. In preparation for switching the THP shrinker to list_lru, add a helper function for allocating list_lru heads coming from a folio. Link: https://lore.kernel.org/20260527204757.2544958-8-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reviewed-by: David Hildenbrand (Arm) Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Usama Arif Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/list_lru.h | 27 +++++++++++++++++++++++++++ mm/list_lru.c | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h index 134cb3e5652a..a450fffe1550 100644 --- a/include/linux/list_lru.h +++ b/include/linux/list_lru.h @@ -81,6 +81,33 @@ static inline int list_lru_init_memcg_key(struct list_lru *lru, struct shrinker int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru, gfp_t gfp); + +#ifdef CONFIG_MEMCG +/** + * folio_memcg_list_lru_alloc - allocate list_lru heads for shrinkable folio + * @folio: the newly allocated & charged folio + * @lru: the list_lru this might be queued on + * @gfp: gfp mask + * + * Allocate list_lru heads (per-memcg, per-node) needed to queue this + * particular folio down the line. + * + * This does memcg_list_lru_alloc(), but on the memcg that @folio is + * associated with. Handles folio_memcg() access rules in the fast + * path (list_lru heads allocated) and the allocation slowpath. + * + * Returns 0 on success, a negative error value otherwise. + */ +int folio_memcg_list_lru_alloc(struct folio *folio, struct list_lru *lru, + gfp_t gfp); +#else +static inline int folio_memcg_list_lru_alloc(struct folio *folio, + struct list_lru *lru, gfp_t gfp) +{ + return 0; +} +#endif + void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *parent); /** diff --git a/mm/list_lru.c b/mm/list_lru.c index 402bb028114d..41a811966063 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -568,17 +568,14 @@ static inline bool memcg_list_lru_allocated(struct mem_cgroup *memcg, return idx < 0 || xa_load(&lru->xa, idx); } -int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru, - gfp_t gfp) +static int __memcg_list_lru_alloc(struct mem_cgroup *memcg, + struct list_lru *lru, gfp_t gfp) { unsigned long flags; struct list_lru_memcg *mlru = NULL; struct mem_cgroup *pos, *parent; XA_STATE(xas, &lru->xa, 0); - if (!list_lru_memcg_aware(lru) || memcg_list_lru_allocated(memcg, lru)) - return 0; - gfp &= GFP_RECLAIM_MASK; /* * Because the list_lru can be reparented to the parent cgroup's @@ -619,6 +616,38 @@ int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru, return xas_error(&xas); } + +int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru, + gfp_t gfp) +{ + if (!list_lru_memcg_aware(lru) || memcg_list_lru_allocated(memcg, lru)) + return 0; + return __memcg_list_lru_alloc(memcg, lru, gfp); +} + +int folio_memcg_list_lru_alloc(struct folio *folio, struct list_lru *lru, + gfp_t gfp) +{ + struct mem_cgroup *memcg; + int res; + + if (!list_lru_memcg_aware(lru)) + return 0; + + /* Fast path when list_lru heads already exist */ + rcu_read_lock(); + memcg = folio_memcg(folio); + res = memcg_list_lru_allocated(memcg, lru); + rcu_read_unlock(); + if (likely(res)) + return 0; + + /* Allocation may block, pin the memcg */ + memcg = get_mem_cgroup_from_folio(folio); + res = __memcg_list_lru_alloc(memcg, lru, gfp); + mem_cgroup_put(memcg); + return res; +} #else static inline void memcg_init_list_lru(struct list_lru *lru, bool memcg_aware) { From 65180e9663c782e45ed1c76276dc64d96615da9d Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:15 -0400 Subject: [PATCH 3168/5214] mm: memory: flatten alloc_anon_folio() retry loop alloc_anon_folio() uses a top-level if (folio) that buries the success path four levels deep. This makes for awkward long lines and wrapping. The next patch will add more code here, so flatten this now to keep things clean and simple. The next label is already there, use it for !folio. No functional change intended. Link: https://lore.kernel.org/20260527204757.2544958-9-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Suggested-by: Lorenzo Stoakes (Oracle) Acked-by: Usama Arif Acked-by: Shakeel Butt Reviewed-by: Dev Jain Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: David Hildenbrand (Arm) Cc: Kairui Song Cc: Lance Yang Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/memory.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 5a365492a9a2..1d8e09d9b3c9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5215,24 +5215,24 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf) while (orders) { addr = ALIGN_DOWN(vmf->address, PAGE_SIZE << order); folio = vma_alloc_folio(gfp, order, vma, addr); - if (folio) { - if (mem_cgroup_charge(folio, vma->vm_mm, gfp)) { - count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); - folio_put(folio); - goto next; - } - folio_throttle_swaprate(folio, gfp); - /* - * When a folio is not zeroed during allocation - * (__GFP_ZERO not used) or user folios require special - * handling, folio_zero_user() is used to make sure - * that the page corresponding to the faulting address - * will be hot in the cache after zeroing. - */ - if (user_alloc_needs_zeroing()) - folio_zero_user(folio, vmf->address); - return folio; + if (!folio) + goto next; + if (mem_cgroup_charge(folio, vma->vm_mm, gfp)) { + count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); + folio_put(folio); + goto next; } + folio_throttle_swaprate(folio, gfp); + /* + * When a folio is not zeroed during allocation + * (__GFP_ZERO not used) or user folios require special + * handling, folio_zero_user() is used to make sure + * that the page corresponding to the faulting address + * will be hot in the cache after zeroing. + */ + if (user_alloc_needs_zeroing()) + folio_zero_user(folio, vmf->address); + return folio; next: count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK); order = next_order(&orders, order); From fafaeceb89a5e2e856ff04c2cacb6cae4a2ecb67 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 27 May 2026 16:45:16 -0400 Subject: [PATCH 3169/5214] mm: switch deferred split shrinker to list_lru The deferred split queue handles cgroups in a suboptimal fashion. The queue is per-NUMA node or per-cgroup, not the intersection. That means on a cgrouped system, a node-restricted allocation entering reclaim can end up splitting large pages on other nodes: alloc/unmap deferred_split_folio() list_add_tail(memcg->split_queue) set_shrinker_bit(memcg, node, deferred_shrinker_id) for_each_zone_zonelist_nodemask(restricted_nodes) mem_cgroup_iter() shrink_slab(node, memcg) shrink_slab_memcg(node, memcg) if test_shrinker_bit(memcg, node, deferred_shrinker_id) deferred_split_scan() walks memcg->split_queue The shrinker bit adds an imperfect guard rail. As soon as the cgroup has a single large page on the node of interest, all large pages owned by that memcg, including those on other nodes, will be split. list_lru properly sets up per-node, per-cgroup lists. As a bonus, it streamlines a lot of the list operations and reclaim walks. It's used widely by other major shrinkers already. Convert the deferred split queue as well. The list_lru per-memcg heads are instantiated on demand when the first object of interest is allocated for a cgroup, by calling folio_memcg_alloc_deferred(). Add calls to where splittable pages are created: anon faults, swapin faults, khugepaged collapse. These calls create all possible node heads for the cgroup at once, so the migration code (between nodes) doesn't need any special care. [akpm@linux-foundation.org: fix build with CONFIG_TRANSPARENT_HUGEPAGE=n] Link: https://lore.kernel.org/202605281620.lc3rtkBm-lkp@intel.com [hannes@cmpxchg.org: fix cgroup.memory=nokmem handling] Link: https://lore.kernel.org/ah9PGv12mqai84ES@cmpxchg.org Link: https://lore.kernel.org/20260527204757.2544958-10-hannes@cmpxchg.org Signed-off-by: Johannes Weiner Reported-by: Mikhail Zaslonko Tested-by: Mikhail Zaslonko Acked-by: Shakeel Butt Reviewed-by: Lorenzo Stoakes (Oracle) Acked-by: Usama Arif Reviewed-by: Kairui Song Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Michal Hocko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Cc: kernel test robot Signed-off-by: Andrew Morton --- include/linux/huge_mm.h | 17 +- include/linux/memcontrol.h | 4 - include/linux/mmzone.h | 12 -- mm/huge_memory.c | 367 +++++++++++++------------------------ mm/internal.h | 2 +- mm/khugepaged.c | 5 + mm/memcontrol.c | 12 +- mm/memory.c | 4 + mm/mm_init.c | 15 -- mm/swap_state.c | 10 + 10 files changed, 160 insertions(+), 288 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 58382e97a66d..c0d223d0c556 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -439,10 +439,10 @@ static inline int split_huge_page(struct page *page) { return split_huge_page_to_list_to_order(page, NULL, 0); } + +int folio_memcg_alloc_deferred(struct folio *folio); + void deferred_split_folio(struct folio *folio, bool partially_mapped); -#ifdef CONFIG_MEMCG -void reparent_deferred_split_queue(struct mem_cgroup *memcg); -#endif void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, unsigned long address, bool freeze); @@ -679,8 +679,15 @@ static inline int try_folio_split_to_order(struct folio *folio, return -EINVAL; } -static inline void deferred_split_folio(struct folio *folio, bool partially_mapped) {} -static inline void reparent_deferred_split_queue(struct mem_cgroup *memcg) {} +static inline int folio_memcg_alloc_deferred(struct folio *folio) +{ + return 0; +} + +static inline void deferred_split_folio(struct folio *folio, bool partially_mapped) +{ +} + #define split_huge_pmd(__vma, __pmd, __address) \ do { } while (0) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 8f2662db166b..e1f46a0016fc 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -278,10 +278,6 @@ struct mem_cgroup { struct memcg_cgwb_frn cgwb_frn[MEMCG_CGWB_FRN_CNT]; #endif -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - struct deferred_split deferred_split_queue; -#endif - #ifdef CONFIG_LRU_GEN_WALKS_MMU /* per-memcg mm_struct list */ struct lru_gen_mm_list mm_list; diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 1331a7b93f33..8e449f524f26 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -1431,14 +1431,6 @@ struct zonelist { */ extern struct page *mem_map; -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -struct deferred_split { - spinlock_t split_queue_lock; - struct list_head split_queue; - unsigned long split_queue_len; -}; -#endif - #ifdef CONFIG_MEMORY_FAILURE /* * Per NUMA node memory failure handling statistics. @@ -1564,10 +1556,6 @@ typedef struct pglist_data { unsigned long first_deferred_pfn; #endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - struct deferred_split deferred_split_queue; -#endif - #ifdef CONFIG_NUMA_BALANCING /* start time in ms of current promote rate limit period */ unsigned int nbp_rl_start; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1f14c5c48b4a..6927f66b2eb2 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,8 @@ unsigned long transparent_hugepage_flags __read_mostly = (1<count_objects = deferred_split_count; deferred_split_shrinker->scan_objects = deferred_split_scan; shrinker_register(deferred_split_shrinker); @@ -962,6 +978,7 @@ static int __init thp_shrinker_init(void) huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero"); if (!huge_zero_folio_shrinker) { shrinker_free(deferred_split_shrinker); + list_lru_destroy(&deferred_split_lru); return -ENOMEM; } @@ -976,6 +993,7 @@ static void __init thp_shrinker_exit(void) { shrinker_free(huge_zero_folio_shrinker); shrinker_free(deferred_split_shrinker); + list_lru_destroy(&deferred_split_lru); } static int __init hugepage_init(void) @@ -1155,119 +1173,6 @@ pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma) return pmd; } -static struct deferred_split *split_queue_node(int nid) -{ - struct pglist_data *pgdata = NODE_DATA(nid); - - return &pgdata->deferred_split_queue; -} - -#ifdef CONFIG_MEMCG -static inline -struct mem_cgroup *folio_split_queue_memcg(struct folio *folio, - struct deferred_split *queue) -{ - if (mem_cgroup_disabled()) - return NULL; - if (split_queue_node(folio_nid(folio)) == queue) - return NULL; - return container_of(queue, struct mem_cgroup, deferred_split_queue); -} - -static struct deferred_split *memcg_split_queue(int nid, struct mem_cgroup *memcg) -{ - return memcg ? &memcg->deferred_split_queue : split_queue_node(nid); -} -#else -static inline -struct mem_cgroup *folio_split_queue_memcg(struct folio *folio, - struct deferred_split *queue) -{ - return NULL; -} - -static struct deferred_split *memcg_split_queue(int nid, struct mem_cgroup *memcg) -{ - return split_queue_node(nid); -} -#endif - -static struct deferred_split *split_queue_lock(int nid, struct mem_cgroup *memcg) -{ - struct deferred_split *queue; - -retry: - queue = memcg_split_queue(nid, memcg); - spin_lock(&queue->split_queue_lock); - /* - * There is a period between setting memcg to dying and reparenting - * deferred split queue, and during this period the THPs in the deferred - * split queue will be hidden from the shrinker side. - */ - if (unlikely(memcg_is_dying(memcg))) { - spin_unlock(&queue->split_queue_lock); - memcg = parent_mem_cgroup(memcg); - goto retry; - } - - return queue; -} - -static struct deferred_split * -split_queue_lock_irqsave(int nid, struct mem_cgroup *memcg, unsigned long *flags) -{ - struct deferred_split *queue; - -retry: - queue = memcg_split_queue(nid, memcg); - spin_lock_irqsave(&queue->split_queue_lock, *flags); - if (unlikely(memcg_is_dying(memcg))) { - spin_unlock_irqrestore(&queue->split_queue_lock, *flags); - memcg = parent_mem_cgroup(memcg); - goto retry; - } - - return queue; -} - -static struct deferred_split *folio_split_queue_lock(struct folio *folio) -{ - struct deferred_split *queue; - - rcu_read_lock(); - queue = split_queue_lock(folio_nid(folio), folio_memcg(folio)); - /* - * The memcg destruction path is acquiring the split queue lock for - * reparenting. Once you have it locked, it's safe to drop the rcu lock. - */ - rcu_read_unlock(); - - return queue; -} - -static struct deferred_split * -folio_split_queue_lock_irqsave(struct folio *folio, unsigned long *flags) -{ - struct deferred_split *queue; - - rcu_read_lock(); - queue = split_queue_lock_irqsave(folio_nid(folio), folio_memcg(folio), flags); - rcu_read_unlock(); - - return queue; -} - -static inline void split_queue_unlock(struct deferred_split *queue) -{ - spin_unlock(&queue->split_queue_lock); -} - -static inline void split_queue_unlock_irqrestore(struct deferred_split *queue, - unsigned long flags) -{ - spin_unlock_irqrestore(&queue->split_queue_lock, flags); -} - static inline bool is_transparent_hugepage(const struct folio *folio) { if (!folio_test_large(folio)) @@ -1368,6 +1273,14 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma, count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); return NULL; } + + if (folio_memcg_alloc_deferred(folio)) { + folio_put(folio); + count_vm_event(THP_FAULT_FALLBACK); + count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK); + return NULL; + } + folio_throttle_swaprate(folio, gfp); /* @@ -3903,34 +3816,43 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n struct folio *end_folio = folio_next(folio); struct folio *new_folio, *next; int old_order = folio_order(folio); + struct list_lru_one *lru; + bool dequeue_deferred; int ret = 0; - struct deferred_split *ds_queue; VM_WARN_ON_ONCE(!mapping && end); - /* Prevent deferred_split_scan() touching ->_refcount */ - ds_queue = folio_split_queue_lock(folio); + /* + * If this folio can be on the deferred split queue, lock out + * the shrinker before freezing the ref. If the shrinker sees + * a 0-ref folio, it assumes it beat folio_put() to the list + * lock and must clean up the LRU state - the same dequeue we + * will do below as part of the split. + */ + dequeue_deferred = folio_test_anon(folio) && old_order > 1; + if (dequeue_deferred) { + struct mem_cgroup *memcg; + + rcu_read_lock(); + memcg = folio_memcg(folio); + lru = list_lru_lock(&deferred_split_lru, + folio_nid(folio), &memcg); + } if (folio_ref_freeze(folio, folio_cache_ref_count(folio) + 1)) { struct swap_cluster_info *ci = NULL; struct lruvec *lruvec; - if (old_order > 1) { - if (!list_empty(&folio->_deferred_list)) { - ds_queue->split_queue_len--; - /* - * Reinitialize page_deferred_list after removing the - * page from the split_queue, otherwise a subsequent - * split will see list corruption when checking the - * page_deferred_list. - */ - list_del_init(&folio->_deferred_list); - } + if (dequeue_deferred) { + __list_lru_del(&deferred_split_lru, lru, + &folio->_deferred_list, folio_nid(folio)); if (folio_test_partially_mapped(folio)) { folio_clear_partially_mapped(folio); mod_mthp_stat(old_order, MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1); } + list_lru_unlock(lru); + rcu_read_unlock(); } - split_queue_unlock(ds_queue); + if (mapping) { int nr = folio_nr_pages(folio); @@ -4031,7 +3953,10 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n if (ci) swap_cluster_unlock(ci); } else { - split_queue_unlock(ds_queue); + if (dequeue_deferred) { + list_lru_unlock(lru); + rcu_read_unlock(); + } return -EAGAIN; } @@ -4397,33 +4322,37 @@ int split_folio_to_list(struct folio *folio, struct list_head *list) * queueing THP splits, and that list is (racily observed to be) non-empty. * * It is unsafe to call folio_unqueue_deferred_split() until folio refcount is - * zero: because even when split_queue_lock is held, a non-empty _deferred_list - * might be in use on deferred_split_scan()'s unlocked on-stack list. + * zero: because even when the list_lru lock is held, a non-empty + * _deferred_list might be in use on deferred_split_scan()'s unlocked + * on-stack list. * - * If memory cgroups are enabled, split_queue_lock is in the mem_cgroup: it is - * therefore important to unqueue deferred split before changing folio memcg. + * The list_lru sublist is determined by folio's memcg: it is therefore + * important to unqueue deferred split before changing folio memcg. */ bool __folio_unqueue_deferred_split(struct folio *folio) { - struct deferred_split *ds_queue; + struct mem_cgroup *memcg; + struct list_lru_one *lru; + int nid = folio_nid(folio); unsigned long flags; bool unqueued = false; WARN_ON_ONCE(folio_ref_count(folio)); WARN_ON_ONCE(!mem_cgroup_disabled() && !folio_memcg_charged(folio)); - ds_queue = folio_split_queue_lock_irqsave(folio, &flags); - if (!list_empty(&folio->_deferred_list)) { - ds_queue->split_queue_len--; + rcu_read_lock(); + memcg = folio_memcg(folio); + lru = list_lru_lock_irqsave(&deferred_split_lru, nid, &memcg, &flags); + if (__list_lru_del(&deferred_split_lru, lru, &folio->_deferred_list, nid)) { if (folio_test_partially_mapped(folio)) { folio_clear_partially_mapped(folio); mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1); } - list_del_init(&folio->_deferred_list); unqueued = true; } - split_queue_unlock_irqrestore(ds_queue, flags); + list_lru_unlock_irqrestore(lru, &flags); + rcu_read_unlock(); return unqueued; /* useful for debug warnings */ } @@ -4431,7 +4360,9 @@ bool __folio_unqueue_deferred_split(struct folio *folio) /* partially_mapped=false won't clear PG_partially_mapped folio flag */ void deferred_split_folio(struct folio *folio, bool partially_mapped) { - struct deferred_split *ds_queue; + struct list_lru_one *lru; + int nid; + struct mem_cgroup *memcg; unsigned long flags; /* @@ -4454,7 +4385,11 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) if (folio_test_swapcache(folio)) return; - ds_queue = folio_split_queue_lock_irqsave(folio, &flags); + nid = folio_nid(folio); + + rcu_read_lock(); + memcg = folio_memcg(folio); + lru = list_lru_lock_irqsave(&deferred_split_lru, nid, &memcg, &flags); if (partially_mapped) { if (!folio_test_partially_mapped(folio)) { folio_set_partially_mapped(folio); @@ -4462,36 +4397,20 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) count_vm_event(THP_DEFERRED_SPLIT_PAGE); count_mthp_stat(folio_order(folio), MTHP_STAT_SPLIT_DEFERRED); mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, 1); - } } else { /* partially mapped folios cannot become non-partially mapped */ VM_WARN_ON_FOLIO(folio_test_partially_mapped(folio), folio); } - if (list_empty(&folio->_deferred_list)) { - struct mem_cgroup *memcg; - - memcg = folio_split_queue_memcg(folio, ds_queue); - list_add_tail(&folio->_deferred_list, &ds_queue->split_queue); - ds_queue->split_queue_len++; - if (memcg) - set_shrinker_bit(memcg, folio_nid(folio), - shrinker_id(deferred_split_shrinker)); - } - split_queue_unlock_irqrestore(ds_queue, flags); + __list_lru_add(&deferred_split_lru, lru, &folio->_deferred_list, nid, memcg); + list_lru_unlock_irqrestore(lru, &flags); + rcu_read_unlock(); } static unsigned long deferred_split_count(struct shrinker *shrink, struct shrink_control *sc) { - struct pglist_data *pgdata = NODE_DATA(sc->nid); - struct deferred_split *ds_queue = &pgdata->deferred_split_queue; - -#ifdef CONFIG_MEMCG - if (sc->memcg) - ds_queue = &sc->memcg->deferred_split_queue; -#endif - return READ_ONCE(ds_queue->split_queue_len); + return list_lru_shrink_count(&deferred_split_lru, sc); } static bool thp_underused(struct folio *folio) @@ -4521,45 +4440,49 @@ static bool thp_underused(struct folio *folio) return false; } +static enum lru_status deferred_split_isolate(struct list_head *item, + struct list_lru_one *lru, + void *cb_arg) +{ + struct folio *folio = container_of(item, struct folio, _deferred_list); + struct list_head *freeable = cb_arg; + + if (folio_try_get(folio)) { + list_lru_isolate_move(lru, item, freeable); + return LRU_REMOVED; + } + + /* + * We lost race with folio_put(). Read folio state before the + * isolate: folio_unqueue_deferred_split() checks list_empty() + * locklessly, so once removed the folio can be freed any time. + */ + if (folio_test_partially_mapped(folio)) { + folio_clear_partially_mapped(folio); + mod_mthp_stat(folio_order(folio), + MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1); + } + list_lru_isolate(lru, item); + return LRU_REMOVED; +} + static unsigned long deferred_split_scan(struct shrinker *shrink, struct shrink_control *sc) { - struct deferred_split *ds_queue; - unsigned long flags; + LIST_HEAD(dispose); struct folio *folio, *next; - int split = 0, i; - struct folio_batch fbatch; + int split = 0; + unsigned long isolated; - folio_batch_init(&fbatch); + isolated = list_lru_shrink_walk_irq(&deferred_split_lru, sc, + deferred_split_isolate, &dispose); -retry: - ds_queue = split_queue_lock_irqsave(sc->nid, sc->memcg, &flags); - /* Take pin on all head pages to avoid freeing them under us */ - list_for_each_entry_safe(folio, next, &ds_queue->split_queue, - _deferred_list) { - if (folio_try_get(folio)) { - folio_batch_add(&fbatch, folio); - } else if (folio_test_partially_mapped(folio)) { - /* We lost race with folio_put() */ - folio_clear_partially_mapped(folio); - mod_mthp_stat(folio_order(folio), - MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1); - } - list_del_init(&folio->_deferred_list); - ds_queue->split_queue_len--; - if (!--sc->nr_to_scan) - break; - if (!folio_batch_space(&fbatch)) - break; - } - split_queue_unlock_irqrestore(ds_queue, flags); - - for (i = 0; i < folio_batch_count(&fbatch); i++) { + list_for_each_entry_safe(folio, next, &dispose, _deferred_list) { bool did_split = false; bool underused = false; - struct deferred_split *fqueue; - folio = fbatch.folios[i]; + list_del_init(&folio->_deferred_list); + if (!folio_test_partially_mapped(folio)) { /* * See try_to_map_unused_to_zeropage(): we cannot @@ -4588,63 +4511,23 @@ static unsigned long deferred_split_scan(struct shrinker *shrink, * underused, then consider it used and don't add it back to * split_queue. */ - if (did_split || !folio_test_partially_mapped(folio)) - continue; + if (!did_split && folio_test_partially_mapped(folio)) { requeue: - /* - * Add back partially mapped folios, or underused folios that - * we could not lock this round. - */ - fqueue = folio_split_queue_lock_irqsave(folio, &flags); - if (list_empty(&folio->_deferred_list)) { - list_add_tail(&folio->_deferred_list, &fqueue->split_queue); - fqueue->split_queue_len++; + rcu_read_lock(); + list_lru_add_irq(&deferred_split_lru, + &folio->_deferred_list, + folio_nid(folio), + folio_memcg(folio)); + rcu_read_unlock(); } - split_queue_unlock_irqrestore(fqueue, flags); - } - folios_put(&fbatch); - - if (sc->nr_to_scan && !list_empty(&ds_queue->split_queue)) { - cond_resched(); - goto retry; + folio_put(folio); } - /* - * Stop shrinker if we didn't split any page, but the queue is empty. - * This can happen if pages were freed under us. - */ - if (!split && list_empty(&ds_queue->split_queue)) + if (!split && !isolated) return SHRINK_STOP; return split; } -#ifdef CONFIG_MEMCG -void reparent_deferred_split_queue(struct mem_cgroup *memcg) -{ - struct mem_cgroup *parent = parent_mem_cgroup(memcg); - struct deferred_split *ds_queue = &memcg->deferred_split_queue; - struct deferred_split *parent_ds_queue = &parent->deferred_split_queue; - int nid; - - spin_lock_irq(&ds_queue->split_queue_lock); - spin_lock_nested(&parent_ds_queue->split_queue_lock, SINGLE_DEPTH_NESTING); - - if (!ds_queue->split_queue_len) - goto unlock; - - list_splice_tail_init(&ds_queue->split_queue, &parent_ds_queue->split_queue); - parent_ds_queue->split_queue_len += ds_queue->split_queue_len; - ds_queue->split_queue_len = 0; - - for_each_node(nid) - set_shrinker_bit(parent, nid, shrinker_id(deferred_split_shrinker)); - -unlock: - spin_unlock(&parent_ds_queue->split_queue_lock); - spin_unlock_irq(&ds_queue->split_queue_lock); -} -#endif - #ifdef CONFIG_DEBUG_FS static void split_huge_pages_all(void) { diff --git a/mm/internal.h b/mm/internal.h index 5602393054f3..181e79f1d6a2 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -852,7 +852,7 @@ static inline bool folio_unqueue_deferred_split(struct folio *folio) /* * At this point, there is no one trying to add the folio to * deferred_list. If folio is not in deferred_list, it's safe - * to check without acquiring the split_queue_lock. + * to check without acquiring the list_lru lock. */ if (data_race(list_empty(&folio->_deferred_list))) return false; diff --git a/mm/khugepaged.c b/mm/khugepaged.c index a4b97ec8ce56..73e262cb30dd 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1123,6 +1123,11 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a if (result != SCAN_SUCCEED) goto out_nolock; + if (folio_memcg_alloc_deferred(folio)) { + result = SCAN_ALLOC_HUGE_PAGE_FAIL; + goto out_nolock; + } + mmap_read_lock(mm); result = hugepage_vma_revalidate(mm, address, true, &vma, cc); if (result != SCAN_SUCCEED) { diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e24114a4493a..56cd4af08232 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4143,11 +4143,6 @@ static struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent) for (i = 0; i < MEMCG_CGWB_FRN_CNT; i++) memcg->cgwb_frn[i].done = __WB_COMPLETION_INIT(&memcg_cgwb_frn_waitq); -#endif -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - spin_lock_init(&memcg->deferred_split_queue.split_queue_lock); - INIT_LIST_HEAD(&memcg->deferred_split_queue.split_queue); - memcg->deferred_split_queue.split_queue_len = 0; #endif lru_gen_init_memcg(memcg); return memcg; @@ -4299,11 +4294,10 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) zswap_memcg_offline_cleanup(memcg); memcg_offline_kmem(memcg); - reparent_deferred_split_queue(memcg); /* - * The reparenting of objcg must be after the reparenting of the - * list_lru and deferred_split_queue above, which ensures that they will - * not mistakenly get the parent list_lru and deferred_split_queue. + * The reparenting of objcg must be after the reparenting of + * the list_lru in memcg_offline_kmem(), which ensures that + * they will not mistakenly get the parent list_lru. */ memcg_reparent_objcgs(memcg); reparent_shrinker_deferred(memcg); diff --git a/mm/memory.c b/mm/memory.c index 1d8e09d9b3c9..56be920c56d7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5222,6 +5222,10 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf) folio_put(folio); goto next; } + if (order > 1 && folio_memcg_alloc_deferred(folio)) { + folio_put(folio); + goto fallback; + } folio_throttle_swaprate(folio, gfp); /* * When a folio is not zeroed during allocation diff --git a/mm/mm_init.c b/mm/mm_init.c index db5568cf36e1..c0a7f1cf6fef 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1373,19 +1373,6 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat, pr_debug("On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages); } -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -static void pgdat_init_split_queue(struct pglist_data *pgdat) -{ - struct deferred_split *ds_queue = &pgdat->deferred_split_queue; - - spin_lock_init(&ds_queue->split_queue_lock); - INIT_LIST_HEAD(&ds_queue->split_queue); - ds_queue->split_queue_len = 0; -} -#else -static void pgdat_init_split_queue(struct pglist_data *pgdat) {} -#endif - #ifdef CONFIG_COMPACTION static void pgdat_init_kcompactd(struct pglist_data *pgdat) { @@ -1401,8 +1388,6 @@ static void __meminit pgdat_init_internals(struct pglist_data *pgdat) pgdat_resize_init(pgdat); pgdat_kswapd_lock_init(pgdat); - - pgdat_init_split_queue(pgdat); pgdat_init_kcompactd(pgdat); init_waitqueue_head(&pgdat->kswapd_wait); diff --git a/mm/swap_state.c b/mm/swap_state.c index 04f5ce992401..9c3a5cf99778 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -465,6 +465,16 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci, return ERR_PTR(-ENOMEM); } + if (order > 1 && folio_memcg_alloc_deferred(folio)) { + spin_lock(&ci->lock); + __swap_cache_do_del_folio(ci, folio, entry, shadow); + spin_unlock(&ci->lock); + folio_unlock(folio); + /* nr_pages refs from swap cache, 1 from allocation */ + folio_put_refs(folio, nr_pages + 1); + return ERR_PTR(-ENOMEM); + } + /* memsw uncharges swap when folio is added to swap cache */ memcg1_swapin(folio); if (shadow) From 25fcea21302237641ddd5816b5b2a20f368d1027 Mon Sep 17 00:00:00 2001 From: Lance Yang Date: Tue, 2 Jun 2026 12:34:53 +0800 Subject: [PATCH 3170/5214] mm/thp: clear deferred split shrinker bits when queues drain deferred_split_count() returns the raw list_lru count. When the per-memcg, per-node list is empty, that count is 0. That skips scanning, but it does not tell memcg reclaim that the shrinker is empty. shrink_slab_memcg() only clears the memcg shrinker bit when the count callback reports SHRINK_EMPTY. Return SHRINK_EMPTY for an empty deferred split list, so the bit can be cleared once the queue has drained. Link: https://lore.kernel.org/20260602043453.67597-1-lance.yang@linux.dev Signed-off-by: Lance Yang Reviewed-by: David Hildenbrand (Arm) Acked-by: Usama Arif Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Johannes Weiner Cc: Kairui Song Cc: Kefeng Wang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mikhail Zaslonko Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Shakeel Butt Cc: Vasily Gorbik Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 6927f66b2eb2..da851a5696d5 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4410,7 +4410,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) static unsigned long deferred_split_count(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_shrink_count(&deferred_split_lru, sc); + unsigned long count; + + count = list_lru_shrink_count(&deferred_split_lru, sc); + return count ?: SHRINK_EMPTY; } static bool thp_underused(struct folio *folio) From ed384eb3a3e121c1d6d5c5d36950fbd286b92026 Mon Sep 17 00:00:00 2001 From: fujunjie Date: Tue, 26 May 2026 12:22:41 +0000 Subject: [PATCH 3171/5214] mm/compaction: respect cpusets when checking retry suitability should_compact_retry() handles COMPACT_SKIPPED by asking compaction_zonelist_suitable() whether reclaim can make a later compaction attempt worthwhile. That answer is used for the current allocation, so it should follow the same zone eligibility rules as the allocation itself. When cpusets are enabled, allocator slowpath decisions are marked with ALLOC_CPUSET. The allocation path, direct compaction and reclaim retry all skip zones rejected by __cpuset_zone_allowed(). compaction_zonelist_suitable() does not apply that filter. It only walks ac->zonelist/ac->nodemask, so it can return true because a zone that is not usable for the current allocation would pass __compaction_suitable(). That does not let the allocation use the disallowed zone. Later allocation and direct compaction paths still apply cpuset filtering. However, it can make should_compact_retry() retry based on memory that this allocation cannot use. Pass gfp_mask down and apply the same ALLOC_CPUSET check in compaction_zonelist_suitable(). This keeps the retry decision aligned with the zones that the allocation is allowed to use. A temporary debugfs probe was also used to call the old and new compaction_zonelist_suitable() predicates in the same two-node NUMA guest. The task was restricted to mems=0 while ac->nodemask covered nodes 0-1. After putting pressure on node0, node0 failed __compaction_suitable() for order-10 and node1 passed it, but node1 was rejected by __cpuset_zone_allowed(). In that state the old predicate returned true and the patched predicate returned false. Link: https://lore.kernel.org/tencent_F59F2BA2CC5779308E10DF54593C736D3E0A@qq.com Fixes: 435b3894e742 ("mm:page_alloc: fix the NULL ac->nodemask in __alloc_pages_slowpath()") Signed-off-by: fujunjie Reviewed-by: Vlastimil Babka (SUSE) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/compaction.h | 2 +- mm/compaction.c | 6 +++++- mm/page_alloc.c | 15 +++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 173d9c07a895..c829c48d1c71 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -101,7 +101,7 @@ extern void compaction_defer_reset(struct zone *zone, int order, bool alloc_success); bool compaction_zonelist_suitable(struct alloc_context *ac, int order, - int alloc_flags); + int alloc_flags, gfp_t gfp_mask); extern void __meminit kcompactd_run(int nid); extern void __meminit kcompactd_stop(int nid); diff --git a/mm/compaction.c b/mm/compaction.c index 8f664fb09f24..b776f35ad020 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -2448,7 +2448,7 @@ bool compaction_suitable(struct zone *zone, int order, unsigned long watermark, /* Used by direct reclaimers */ bool compaction_zonelist_suitable(struct alloc_context *ac, int order, - int alloc_flags) + int alloc_flags, gfp_t gfp_mask) { struct zone *zone; struct zoneref *z; @@ -2461,6 +2461,10 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order, ac->highest_zoneidx, ac->nodemask) { unsigned long available; + if (cpusets_enabled() && (alloc_flags & ALLOC_CPUSET) && + !__cpuset_zone_allowed(zone, gfp_mask)) + continue; + /* * Do not consider all the reclaimable memory because we do not * want to trash just for a single high order allocation which diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 97cb95820592..dd2d3d5ac1b1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4198,7 +4198,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, } static inline bool -should_compact_retry(struct alloc_context *ac, int order, int alloc_flags, +should_compact_retry(gfp_t gfp_mask, struct alloc_context *ac, int order, + int alloc_flags, enum compact_result compact_result, enum compact_priority *compact_priority, int *compaction_retries) @@ -4220,7 +4221,8 @@ should_compact_retry(struct alloc_context *ac, int order, int alloc_flags, * migration targets. Continue if reclaim can help. */ if (compact_result == COMPACT_SKIPPED) { - ret = compaction_zonelist_suitable(ac, order, alloc_flags); + ret = compaction_zonelist_suitable(ac, order, alloc_flags, + gfp_mask); goto out; } @@ -4273,7 +4275,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, } static inline bool -should_compact_retry(struct alloc_context *ac, int order, int alloc_flags, +should_compact_retry(gfp_t gfp_mask, struct alloc_context *ac, int order, + int alloc_flags, enum compact_result compact_result, enum compact_priority *compact_priority, int *compaction_retries) @@ -4940,9 +4943,9 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, * of free memory (see __compaction_suitable) */ if (did_some_progress > 0 && can_compact && - should_compact_retry(ac, order, alloc_flags, - compact_result, &compact_priority, - &compaction_retries)) + should_compact_retry(gfp_mask, ac, order, alloc_flags, + compact_result, &compact_priority, + &compaction_retries)) goto retry; /* Reclaim/compaction failed to prevent the fallback */ From 2f5e0477276bb87a407edc75f3d65012e6f63c68 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Mon, 1 Jun 2026 03:21:17 -0700 Subject: [PATCH 3172/5214] mm: bypass mmap_miss heuristic for VM_EXEC readahead Patch series "mm: improve large folio readahead for exec memory", v7. Two checks in do_sync_mmap_readahead() limit large-folio readahead: 1. The mmap_miss heuristic is meant to throttle wasteful speculative readahead. It is currently also applied to the VM_EXEC readahead path, which is targeted rather than speculative. Once mmap_miss exceeds MMAP_LOTSAMISS, exec readahead - including the large-folio order requested by exec_folio_order() - is disabled. On configurations where the mmap_miss decrement paths are not active (see patch 1) the counter only grows, so exec readahead is permanently disabled after the first 100 faults. 2. The force_thp_readahead path is gated only on HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER and always drives the readahead at HPAGE_PMD_ORDER. Configurations where HPAGE_PMD_ORDER exceeds MAX_PAGECACHE_ORDER never reach this path, even when the mapping itself supports usefully large folios well below the cap. Both issues are most visible on arm64 with a 64K base page size, where HPAGE_PMD_ORDER is 13 (512MB) -- above MAX_PAGECACHE_ORDER (11) -- and where fault_around_pages collapses to 1 disabling should_fault_around() (one of the two mmap_miss decrement sites). However the fixes are architecture-agnostic: patch 1 reflects the nature of VM_EXEC readahead regardless of base page size, and patch 2 generalises the gate so any mapping advertising a usefully large maximum folio order can benefit. I created a benchmark that mmaps a large executable file madvises it as huge and calls RET-stub functions at PAGE_SIZE offsets across it. "Cold" measures fault + readahead cost. "Random" first faults in all pages with a sequential sweep (not measured), then measures time for calling random offsets, isolating iTLB miss cost for scattered execution. The benchmark results on Neoverse V2 (Grace), arm64 with 64K base pages, 512MB executable file on ext4, averaged over 3 runs: Phase | Baseline | Patched | Improvement -----------|--------------|--------------|------------------ Cold fault | 83.4 ms | 41.3 ms | 50% faster Random | 76.0 ms | 58.3 ms | 23% faster This patch (of 2): The mmap_miss heuristic is intended to stop speculative mmap readahead when a file looks like a random-access workload. That does not fit the VM_EXEC path very well. VM_EXEC readahead is already constrained differently from ordinary mmap read-around: it is bounded by the VMA, uses exec_folio_order() to choose an order useful for executable mappings, and sets async_size to 0 so it does not create follow-on readahead. When VM_HUGEPAGE is also present, the larger readahead is an explicit userspace opt-in. The mmap_miss counter is decremented from cache-hit paths in do_async_mmap_readahead() and filemap_map_pages(). Those paths are not always enough to balance the synchronous miss increments for executable mappings. In particular, when fault-around is effectively disabled, such as configurations where fault_around_pages is 1, filemap_map_pages() is not reached from the fault path. The counter can then become a stale throttle for VM_EXEC mappings and suppress the readahead behavior that the executable-specific path is trying to provide. Skip both mmap_miss increments and decrements for VM_EXEC mappings, matching the existing VM_SEQ_READ treatment and keeping the counter accounting symmetric. Link: https://lore.kernel.org/20260601102205.3985788-1-usama.arif@linux.dev Link: https://lore.kernel.org/20260601102205.3985788-2-usama.arif@linux.dev Signed-off-by: Usama Arif Reviewed-by: Jan Kara Reviewed-by: Kiryl Shutsemau (Meta) Reviewed-by: Oscar Salvador (SUSE) Reviewed-by: Pedro Falcato Cc: Alistair Popple Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Catalin Marinas Cc: Christian Brauner Cc: David Hildenbrand Cc: Dev Jain Cc: Heiher Cc: Johannes Weiner Cc: Kees Cook Cc: Kevin Brodsky Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Pasha Tatashin Cc: Rohan McLure Cc: Ryan Roberts Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/filemap.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 6bf0b540ef19..58d8ba867b52 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3340,7 +3340,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) } } - if (!(vm_flags & VM_SEQ_READ)) { + if (!(vm_flags & (VM_SEQ_READ | VM_EXEC))) { /* Avoid banging the cache line if not needed */ mmap_miss = READ_ONCE(ra->mmap_miss); if (mmap_miss < MMAP_LOTSAMISS * 10) @@ -3435,12 +3435,12 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf, * times for a single folio and break the balance with mmap_miss * increase in do_sync_mmap_readahead(). * - * VM_SEQ_READ mappings skip the mmap_miss increment in + * VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss increment in * do_sync_mmap_readahead(), so skip the decrement here as well to * keep the counter symmetric. */ if (likely(!folio_test_locked(folio)) && - !(vmf->vma->vm_flags & VM_SEQ_READ)) { + !(vmf->vma->vm_flags & (VM_SEQ_READ | VM_EXEC))) { mmap_miss = READ_ONCE(ra->mmap_miss); if (mmap_miss) WRITE_ONCE(ra->mmap_miss, --mmap_miss); @@ -3942,14 +3942,14 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, * Don't decrease mmap_miss in this scenario to make sure * we can stop read-ahead. * - * VM_SEQ_READ mappings skip the mmap_miss increment in - * do_sync_mmap_readahead(), so skip the decrement here as - * well to keep the counter symmetric. + * VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss + * increment in do_sync_mmap_readahead(), so skip the + * decrement here as well to keep the counter symmetric. */ if ((map_ret & VM_FAULT_NOPAGE) && !(vmf->flags & FAULT_FLAG_TRIED) && !folio_test_workingset(folio) && - !(vma->vm_flags & VM_SEQ_READ)) { + !(vma->vm_flags & (VM_SEQ_READ | VM_EXEC))) { unsigned short mmap_miss; mmap_miss = READ_ONCE(file->f_ra.mmap_miss); From 8732e14b719129b77e24d9003a506ec949d9427c Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Mon, 1 Jun 2026 03:21:18 -0700 Subject: [PATCH 3173/5214] mm: use mapping_max_folio_order() for force_thp_readahead order The force_thp_readahead path in do_sync_mmap_readahead() is gated on HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER and always requests HPAGE_PMD_ORDER / HPAGE_PMD_NR. On configurations where HPAGE_PMD_ORDER exceeds MAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size, VM_HUGEPAGE mappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios. Enable forced readahead for mappings that support large folios and request the max folio order supported by the mapping, capped at 2M. 2MB is chosen as the cap because it matches the PMD size on x86_64 and on arm64 with 4K base pages, so the size/memory-pressure tradeoff for folios of that size is already well understood. On arm64 with 16K and 64K base page sizes, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path. This will result in 32M folios not being faulted in with 16K base page size for arm64, but with contpte, the performance difference should be negligible. The final allocation order may still be clamped by page_cache_ra_order() to the mapping and request geometry, but this gives VM_HUGEPAGE mappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead. Link: https://lore.kernel.org/20260601102205.3985788-3-usama.arif@linux.dev Signed-off-by: Usama Arif Reviewed-by: Jan Kara Reviewed-by: Pedro Falcato Cc: Alistair Popple Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Catalin Marinas Cc: Christian Brauner Cc: David Hildenbrand Cc: Dev Jain Cc: Heiher Cc: Johannes Weiner Cc: Kees Cook Cc: Kevin Brodsky Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Pasha Tatashin Cc: Rohan McLure Cc: Ryan Roberts Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Kiryl Shutsemau (Meta) Cc: Oscar Salvador (SUSE) Signed-off-by: Andrew Morton --- mm/filemap.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 58d8ba867b52..98434acc69c1 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3313,14 +3313,26 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) struct file *fpin = NULL; vm_flags_t vm_flags = vmf->vma->vm_flags; bool force_thp_readahead = false; + unsigned int thp_order = 0; unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff + vma_pages(vmf->vma) - 1; /* Use the readahead code, even if readahead is disabled */ - if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && - (vm_flags & VM_HUGEPAGE) && HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER) - force_thp_readahead = true; + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { + /* + * Cap max THP order at 2MB: this is the common PMD-sized + * hugepage size, and it avoids memory pressure from very + * large forced readahead when mapping_max_folio_order() is + * high (for example, 128MB with 64K base pages on arm64). + */ + if (mapping_large_folio_support(mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, + mapping_max_folio_order(mapping), + get_order(SZ_2M)); + } + } if (!force_thp_readahead) { /* @@ -3355,17 +3367,19 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) } if (force_thp_readahead) { + unsigned long folio_nr_pages = 1UL << thp_order; + fpin = maybe_unlock_mmap_for_io(vmf, fpin); - ractl._index &= ~((unsigned long)HPAGE_PMD_NR - 1); - ra->size = HPAGE_PMD_NR; + ractl._index &= ~(folio_nr_pages - 1); + ra->size = folio_nr_pages; /* - * Fetch two PMD folios, so we get the chance to actually + * Fetch two folios so we get the chance to actually * readahead, unless we've been told not to. */ if (!(vm_flags & VM_RAND_READ)) ra->size *= 2; - ra->async_size = HPAGE_PMD_NR; - ra->order = HPAGE_PMD_ORDER; + ra->async_size = folio_nr_pages; + ra->order = thp_order; page_cache_ra_order(&ractl, ra); return fpin; } From 3d4f1a54160046d5059ec6c5f2152e054e7b12d7 Mon Sep 17 00:00:00 2001 From: fujunjie Date: Tue, 26 May 2026 09:12:48 +0000 Subject: [PATCH 3174/5214] mm/page_alloc: fix deferred compaction accounting COMPACT_DEFERRED means compaction did not start because past failures caused the zone to be deferred. try_to_compact_pages() returns the maximum result seen while walking the zonelist, so a final COMPACT_DEFERRED result means no later zone reported that compaction actually ran. __alloc_pages_direct_compact() skips COMPACTSTALL and COMPACTFAIL accounting when try_to_compact_pages() returns COMPACT_SKIPPED, but not when it returns COMPACT_DEFERRED. A deferred-only direct compaction attempt can therefore look like a stall, and then a failure if the allocation still cannot be satisfied. Treat COMPACT_DEFERRED like COMPACT_SKIPPED in this accounting path. If a later zone runs compaction and returns a result above COMPACT_DEFERRED, or compact_zone_order() reports COMPACT_SUCCESS for a captured page, the final result is not COMPACT_DEFERRED and the existing accounting still runs. Link: https://lore.kernel.org/tencent_368AF1F3821E46232637BE16D65C45CF3308@qq.com Fixes: 06dac2f467fe ("mm: compaction: update the COMPACT[STALL|FAIL] events properly") Signed-off-by: fujunjie Reviewed-by: Vlastimil Babka (SUSE) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index dd2d3d5ac1b1..f7db8f049bd2 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4161,7 +4161,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, psi_memstall_leave(&pflags); delayacct_compact_end(); - if (*compact_result == COMPACT_SKIPPED) + if (*compact_result == COMPACT_SKIPPED || + *compact_result == COMPACT_DEFERRED) return NULL; /* * At least in one zone compaction wasn't deferred or skipped, so let's From 3f08b20eb12f7a05824295d083561cfddfdf76c2 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 28 May 2026 09:55:07 +0200 Subject: [PATCH 3175/5214] mm/page_vma_mapped_walk: use ptep_get_lockless() for lockless access When not holding the lock, there is a chance that the pte gets modified under our feet, so we need to use the lockless API to make sure that the entries remain consistent during the read." Switch from ptep_get() to ptep_get_lockless() accessor for PTE reads when no lock is taken. [osalvador@suse.de: changelog addition] Link: https://lore.kernel.org/ahhNq0pFKvSKZQbR@localhost.localdomain Link: https://lore.kernel.org/20260528075507.1821939-1-agordeev@linux.ibm.com Signed-off-by: Alexander Gordeev Reviewed-by: Oscar Salvador (SUSE) Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Anshuman Khandual Cc: Harry Yoo Cc: Jann Horn Cc: Liam Howlett Cc: Rik van Riel Cc: Vlastimil Babka Cc: Wei Yang Signed-off-by: Andrew Morton --- mm/page_vma_mapped.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index a4d52fdb3056..2ccbabfb2cc1 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c @@ -41,7 +41,7 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp, if (!pvmw->pte) return false; - ptent = ptep_get(pvmw->pte); + ptent = ptep_get_lockless(pvmw->pte); if (pte_none(ptent)) { return false; @@ -183,6 +183,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) struct mm_struct *mm = vma->vm_mm; unsigned long end; spinlock_t *ptl; + pte_t pteval; pgd_t *pgd; p4d_t *p4d; pud_t *pud; @@ -310,7 +311,11 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) goto restart; } pvmw->pte++; - } while (pte_none(ptep_get(pvmw->pte))); + if (!pvmw->ptl) + pteval = ptep_get_lockless(pvmw->pte); + else + pteval = ptep_get(pvmw->pte); + } while (pte_none(pteval)); if (!pvmw->ptl) { spin_lock(ptl); From d1aba985984781947ad67c1b44ac64bd498c8f27 Mon Sep 17 00:00:00 2001 From: Cunlong Li Date: Thu, 28 May 2026 10:48:45 +0800 Subject: [PATCH 3176/5214] zram: drop unused bio parameter from write helpers After "zram: fix use-after-free in zram_bvec_write_partial()", zram_bvec_write_partial() always passes NULL to zram_read_page() and no longer needs the parent bio. Mirror the read side (zram_bvec_read_partial() has not taken a bio since commit 4e3c87b9421d ("zram: fix synchronous reads")) and drop the parameter from zram_bvec_write_partial() and zram_bvec_write(). No functional change. Link: https://lore.kernel.org/20260528-zram-v3-2-cab86eef8764@gmail.com Signed-off-by: Cunlong Li Reviewed-by: Christoph Hellwig Reviewed-by: Sergey Senozhatsky Cc: Jens Axboe Cc: Minchan Kim Cc: Yisheng Xie Signed-off-by: Andrew Morton --- drivers/block/zram/zram_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 7917fc7a2a29..fd12604ff8d7 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2334,7 +2334,7 @@ static int zram_write_page(struct zram *zram, struct page *page, u32 index) * This is a partial IO. Read the full page before writing the changes. */ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, - u32 index, int offset, struct bio *bio) + u32 index, int offset) { struct page *page = alloc_page(GFP_NOIO); int ret; @@ -2352,10 +2352,10 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, } static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, - u32 index, int offset, struct bio *bio) + u32 index, int offset) { if (is_partial_io(bvec)) - return zram_bvec_write_partial(zram, bvec, index, offset, bio); + return zram_bvec_write_partial(zram, bvec, index, offset); return zram_write_page(zram, bvec->bv_page, index); } @@ -2752,7 +2752,7 @@ static void zram_bio_write(struct zram *zram, struct bio *bio) bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset); - if (zram_bvec_write(zram, &bv, index, offset, bio) < 0) { + if (zram_bvec_write(zram, &bv, index, offset) < 0) { atomic64_inc(&zram->stats.failed_writes); bio->bi_status = BLK_STS_IOERR; break; From 8f7275c174bc5bcc8fc1bec8024e2b3e6fe17f46 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 28 May 2026 09:13:36 +0800 Subject: [PATCH 3177/5214] lib/test_hmm: fix memory leak in dmirror_migrate_to_system() Move the kvcalloc() calls after the early return checks to avoid leaking src_pfns and dst_pfns when end < start or mmget_not_zero() fails. Link: https://lore.kernel.org/20260528011336.20797-1-hao.ge@linux.dev Fixes: 775465fd26a3 ("lib/test_hmm: add zone device private THP test infrastructure") Signed-off-by: Hao Ge Reviewed-by: Alistair Popple Reported-by: Sashiko Reviewed-by: Balbir Singh Cc: Jason Gunthorpe Cc: Leon Romanovsky Signed-off-by: Andrew Morton --- lib/test_hmm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 63bf77dee987..35f774ed2d99 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1111,9 +1111,6 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror, unsigned long *src_pfns; unsigned long *dst_pfns; - src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); - dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); - start = cmd->addr; end = start + size; if (end < start) @@ -1123,6 +1120,9 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror, if (!mmget_not_zero(mm)) return -EINVAL; + src_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); + dst_pfns = kvcalloc(PTRS_PER_PTE, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); + cmd->cpages = 0; mmap_read_lock(mm); for (addr = start; addr < end; addr = next) { From cdea4acce026f4dc6a1689cb991a2bf3a4333ecd Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Mon, 1 Jun 2026 11:40:09 +0000 Subject: [PATCH 3178/5214] mm: delete stale comment about cachelines These comments have been wrong since commit a211c6550efc ("mm: page_alloc: defrag_mode kswapd/kcompactd watermarks") added NR_FREE_PAGES_BLOCKS. Since nobody has complained about it in the last year, it seems unlikely these comments were particularly useful anyway, so delete them. Link: https://lore.kernel.org/20260601-zone_stat_item-comment-v1-1-f452dd91d5eb@google.com Signed-off-by: Brendan Jackman Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Acked-by: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton --- include/linux/mmzone.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 8e449f524f26..ca2712187147 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -214,7 +214,6 @@ enum numa_stat_item { #endif enum zone_stat_item { - /* First 128 byte cacheline (assuming 64 bit words) */ NR_FREE_PAGES, NR_FREE_PAGES_BLOCKS, NR_ZONE_LRU_BASE, /* Used only for compaction and reclaim retry */ @@ -225,7 +224,6 @@ enum zone_stat_item { NR_ZONE_UNEVICTABLE, NR_ZONE_WRITE_PENDING, /* Count of dirty, writeback and unstable pages */ NR_MLOCK, /* mlock()ed pages found and moved off LRU */ - /* Second 128 byte cacheline */ #if IS_ENABLED(CONFIG_ZSMALLOC) NR_ZSPAGES, /* allocated in zsmalloc */ #endif From 3862816c98152553106dd762c66c0f390337fa38 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 1 Jun 2026 16:55:04 -0700 Subject: [PATCH 3179/5214] MAINTAINERS: add testing ABI documents for mm A few mm subsystem entries in MAINTAINERS are missing their testing ABI documents. Add those. Link: https://lore.kernel.org/20260601235506.85123-1-sj@kernel.org Signed-off-by: SeongJae Park Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: SeongJae Park Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ef31a8dd9e5b..63fa4f9fa4c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16854,6 +16854,7 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/ABI/testing/sysfs-kernel-mm-ksm F: Documentation/admin-guide/mm/ksm.rst F: Documentation/mm/ksm.rst F: include/linux/ksm.h @@ -16876,6 +16877,8 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy +F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave F: include/linux/mempolicy.h F: include/uapi/linux/mempolicy.h F: include/linux/migrate.h @@ -16918,6 +16921,10 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/ABI/testing/sysfs-kernel-mm +F: Documentation/ABI/testing/sysfs-kernel-mm-cma +F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers +F: Documentation/ABI/testing/sysfs-kernel-mm-numa F: Documentation/admin-guide/mm/ F: Documentation/mm/ F: include/linux/cma.h @@ -17041,6 +17048,7 @@ R: Barry Song R: Youngjun Park L: linux-mm@kvack.org S: Maintained +F: Documentation/ABI/testing/sysfs-kernel-mm-swap F: Documentation/mm/swap-table.rst F: include/linux/swap.h F: include/linux/swapfile.h @@ -17068,6 +17076,7 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/ABI/testing/sysfs-kernel-mm-transparent-hugepage F: Documentation/admin-guide/mm/transhuge.rst F: include/linux/huge_mm.h F: include/linux/khugepaged.h From 04718f7c9290f95385f0dd328758753dc1c36dec Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:25 +0100 Subject: [PATCH 3180/5214] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race Patch series "userfaultfd/pagemap: pre-existing fixes". These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this. All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review . They are independent of RWP, apply to mm-new directly, and carry Cc: stable@. 1: fs/proc/task_mmu: a missing huge_ptep_modify_prot_start() in make_uffd_wp_huge_pte() can lose hardware Dirty/Accessed updates when PAGEMAP_SCAN write-protects a hugetlb PTE. 2: fs/proc/task_mmu: pagemap_scan_hugetlb_entry() compares the range against HPAGE_SIZE rather than the hstate page size, so it never write-protects gigantic hugetlb pages. 3: fs/proc/task_mmu: PAGEMAP_SCAN with PM_SCAN_WP_MATCHING over an unpopulated hugetlb range self-deadlocks -- pagemap_scan_pte_hole() calls uffd_wp_range() while walk_hugetlb_range() holds the hugetlb vma lock for read, and hugetlb_change_protection() then takes it for write. Install the marker inline instead. 4: mm/huge_memory: change_non_present_huge_pmd() drops pmd_swp_uffd_wp on a device-private PMD permission downgrade, silently losing the uffd-wp marker. 5: userfaultfd: must_wait() applies pte_write() to a locklessly read PTE without checking pte_present(), so swap/migration entries decode random offset bits and a thread can stay parked on a stale fault. 6: userfaultfd: __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT (41) to mk_vma_flags() unconditionally, an out-of-bounds write into the single-word vma_flags_t on 32-bit. Build the mask from config-gated per-mode masks so an unavailable bit is never materialised. This patch (of 6): make_uffd_wp_huge_pte() arms the UFFD_WP bit on a present HugeTLB PTE by calling huge_ptep_modify_prot_commit() with a ptent snapshot that was fetched without the corresponding huge_ptep_modify_prot_start(). The start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, and huge_ptep_modify_prot_commit() (whose generic implementation just calls set_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update. The non-hugetlb sibling make_uffd_wp_pte() does this correctly via ptep_modify_prot_start() / ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against. Link: https://lore.kernel.org/20260529172331.356655-1-kas@kernel.org Link: https://lore.kernel.org/20260529172331.356655-2-kas@kernel.org Link: https://lore.kernel.org/all/20260526130509.2748441-1-kirill@shutemov.name/ [1] Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 1e3a15bf46f4..e21a38ac745b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2610,12 +2610,16 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma, if (softleaf_is_hwpoison(entry) || softleaf_is_marker(entry)) return; - if (softleaf_is_migration(entry)) + if (softleaf_is_migration(entry)) { set_huge_pte_at(vma->vm_mm, addr, ptep, pte_swp_mkuffd_wp(ptent), psize); - else - huge_ptep_modify_prot_commit(vma, addr, ptep, ptent, - huge_pte_mkuffd_wp(ptent)); + } else { + pte_t old_pte, new_pte; + + old_pte = huge_ptep_modify_prot_start(vma, addr, ptep); + new_pte = huge_pte_mkuffd_wp(old_pte); + huge_ptep_modify_prot_commit(vma, addr, ptep, old_pte, new_pte); + } } #endif /* CONFIG_HUGETLB_PAGE */ From 1b074e3270e1c061c829150c742eb83bad4dddd1 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:26 +0100 Subject: [PATCH 3181/5214] fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry() The partial-page check compares against HPAGE_SIZE (PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start + HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size. Link: https://lore.kernel.org/20260529172331.356655-3-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index e21a38ac745b..1489c67e88f7 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2960,7 +2960,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, if (~categories & PAGE_IS_WRITTEN) goto out_unlock; - if (end != start + HPAGE_SIZE) { + if (end != start + huge_page_size(hstate_vma(vma))) { /* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range(p, start, end); p->arg.walk_end = start; From e92d92bbafb264dc0518d52b846a3c07ed8d523f Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:27 +0100 Subject: [PATCH 3182/5214] fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole() A PAGEMAP_SCAN ioctl requesting PM_SCAN_WP_MATCHING on a hugetlb VMA hangs the calling thread, unkillably, as soon as the scan reaches an unpopulated part of the range: do_pagemap_scan() walk_page_range() walk_hugetlb_range() hugetlb_vma_lock_read() # take the vma lock for read ... pagemap_scan_pte_hole() # ... ->pte_hole() for a hole uffd_wp_range() change_protection() hugetlb_change_protection() hugetlb_vma_lock_write() # ... and block taking it for write walk_hugetlb_range() holds the hugetlb vma lock for read across the whole walk. A present entry goes to ->hugetlb_entry(); an unpopulated one goes to ->pte_hole(), i.e. pagemap_scan_pte_hole(). To write-protect the hole that handler calls uffd_wp_range(), which on a hugetlb VMA reaches hugetlb_change_protection() and takes the same vma lock for write. The thread then blocks in down_write() waiting for the read lock it is itself holding. The populated path avoids this: pagemap_scan_hugetlb_entry() write-protects the entry inline under the page-table lock and never enters hugetlb_change_protection(). Do the same for holes. Fault in the page table and install the uffd-wp marker directly with make_uffd_wp_huge_pte() under the page-table lock, rather than routing through uffd_wp_range(). That is the same sequence hugetlb_change_protection() runs for an unpopulated entry, minus the vma write lock -- which is safe to skip because PMD sharing is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() runs at registration), leaving nothing for that lock to serialise against. Link: https://lore.kernel.org/20260529172331.356655-4-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Assisted-by: Claude:claude-opus-4-8 Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 59 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 1489c67e88f7..06fb94a965ff 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2977,8 +2977,62 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, return ret; } + +/* + * Write-protect the unpopulated hugetlb entries covering [addr, end) by + * installing uffd-wp markers inline, exactly as pagemap_scan_hugetlb_entry() + * does for populated entries. + * + * walk_hugetlb_range() currently calls ->pte_hole() once per huge page, so the + * loop normally runs a single iteration; it is written to cover the full range + * in case the walker ever coalesces adjacent holes. + * + * The obvious route -- uffd_wp_range() -> hugetlb_change_protection() -- + * cannot be used here: it takes hugetlb_vma_lock_write(), but the page-table + * walker (walk_hugetlb_range()) already holds hugetlb_vma_lock_read() on the + * same VMA, so the scanning thread would deadlock against itself. PMD sharing + * is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() at registration), so + * the vma lock guards nothing that matters for these entries anyway. + */ +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + struct hstate *h = hstate_vma(vma); + unsigned long psize = huge_page_size(h); + struct mm_struct *mm = vma->vm_mm; + spinlock_t *ptl; + pte_t *ptep; + pte_t pte; + + for (addr = ALIGN_DOWN(addr, psize); addr < end; addr += psize) { + ptep = huge_pte_alloc(mm, vma, addr, psize); + if (!ptep) + return -ENOMEM; + + i_mmap_lock_write(vma->vm_file->f_mapping); + ptl = huge_pte_lock(h, mm, ptep); + pte = huge_ptep_get(mm, addr, ptep); + make_uffd_wp_huge_pte(vma, addr, ptep, pte); + /* + * A none entry has no cached translation, so installing the + * marker needs no TLB flush. Flush only if a fault populated + * the entry between huge_pte_alloc() and the page table lock. + */ + if (!huge_pte_none(pte)) + flush_hugetlb_tlb_range(vma, addr, addr + psize); + spin_unlock(ptl); + i_mmap_unlock_write(vma->vm_file->f_mapping); + } + + return 0; +} #else #define pagemap_scan_hugetlb_entry NULL +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + return 0; +} #endif static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, @@ -2998,7 +3052,10 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, if (~p->arg.flags & PM_SCAN_WP_MATCHING) return ret; - err = uffd_wp_range(vma, addr, end - addr, true); + if (is_vm_hugetlb_page(vma)) + err = pagemap_scan_hugetlb_hole_wp(vma, addr, end); + else + err = uffd_wp_range(vma, addr, end - addr, true); if (err < 0) ret = err; From f7e2c21bd1f57cd5350eecdfdb5d6025ca6afbab Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:28 +0100 Subject: [PATCH 3183/5214] mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD downgrade change_non_present_huge_pmd() rewrites a writable device-private PMD swap entry into a readable one without carrying pmd_swp_uffd_wp() across. The PTE-level change_softleaf_pte() does this correctly; mirror that here, matching what copy_huge_pmd() does for the fork path. Without the carry, a plain mprotect() over a UFFD_WP-marked device-private THP strips the bit and the trap is bypassed on swap-in. Link: https://lore.kernel.org/20260529172331.356655-5-kas@kernel.org Fixes: 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Balbir Singh Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index da851a5696d5..a5176653ba1f 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2565,6 +2565,8 @@ static void change_non_present_huge_pmd(struct mm_struct *mm, } else if (softleaf_is_device_private_write(entry)) { entry = make_readable_device_private_entry(swp_offset(entry)); newpmd = swp_entry_to_pmd(entry); + if (pmd_swp_uffd_wp(*pmd)) + newpmd = pmd_swp_mkuffd_wp(newpmd); } else { newpmd = *pmd; } From 8e80af52db652fbc41320eee45a4f73bc029faf2 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:29 +0100 Subject: [PATCH 3184/5214] userfaultfd: gate must_wait writability check on pte_present() userfaultfd_must_wait() and userfaultfd_huge_must_wait() read the PTE without taking the page table lock and then apply pte_write() / huge_pte_write() to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result. The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a UFFDIO_WAKE that may never arrive. Gate the writability check on pte_present() so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry. Link: https://lore.kernel.org/20260529172331.356655-6-kas@kernel.org Fixes: 369cd2121be4 ("userfaultfd: hugetlbfs: userfaultfd_huge_must_wait for hugepmd ranges") Fixes: 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton --- mm/userfaultfd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index c86daf38d154..246af12bf801 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2542,6 +2542,15 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, /* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker(pte)) return true; + /* + * Concurrent migration may have replaced the present PTE with a + * non-marker swap entry between fault delivery and this lockless + * re-check. huge_pte_write() on a swap entry decodes random offset + * bits, so gate it on pte_present(). The migration completion path + * will re-deliver the fault if it still needs userspace. + */ + if (!pte_present(pte)) + return false; /* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. @@ -2628,6 +2637,17 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx, /* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker(ptent)) goto out; + /* + * Concurrent swap-out / migration may have replaced the present PTE + * with a non-marker swap entry between fault delivery and this + * lockless re-check. pte_write() on a swap entry decodes random + * offset bits, so gate it on pte_present(). The page-in path will + * re-deliver the fault if it still needs userspace. + */ + if (!pte_present(ptent)) { + ret = false; + goto out; + } /* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. From cc7a9f6e57c4f71e8e1fee3274b1ae8770f2a743 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:30 +0100 Subject: [PATCH 3185/5214] userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks The VMA flags bitmap is a single word today: NUM_VMA_FLAG_BITS is BITS_PER_LONG, so on 32-bit vma_flags_t holds only 32 bits. (The bitmap type exists so this can grow past BITS_PER_LONG later; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally above BITS_PER_LONG -- VMA_UFFD_MINOR_BIT is 41, with VM_UFFD_MINOR == VM_NONE on 32-bit so no VMA actually carries the bit. __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT to mk_vma_flags() unconditionally. On 32-bit that becomes __set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in __VMA_UFFD_FLAGS so the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask. Rather than feed bit numbers that may not exist on the current build to mk_vma_flags(), build the mask from whole per-mode masks that collapse to EMPTY_VMA_FLAGS when their feature is unavailable. Add mk_vma_flags_from_masks() for that, and define VMA_UFFD_MISSING / _WP / _MINOR alongside the VM_UFFD_* flags, gating VMA_UFFD_MINOR on the same config as VM_UFFD_MINOR (which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant. Link: https://lore.kernel.org/20260529172331.356655-7-kas@kernel.org Fixes: 9ea35a25d51b ("mm: introduce VMA flags bitmap type") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Suggested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Assisted-by: Claude:claude-opus-4-8 Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton --- include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++ include/linux/userfaultfd_k.h | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 0f2612a70fb1..485df9c2dbdd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -496,6 +496,21 @@ enum { #else #define VM_UFFD_MINOR VM_NONE #endif + +/* + * vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on + * the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits + * -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose + * bitmap cannot hold it. + */ +#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT) +#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT) +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR +#define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT) +#else +#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS +#endif + #ifdef CONFIG_64BIT #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED) #define VM_SEALED INIT_VM_FLAG(SEALED) @@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_set_mask(vma_flags_t *flags, #define vma_flags_set(flags, ...) \ vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count, + const vma_flags_t *masks) +{ + vma_flags_t flags = EMPTY_VMA_FLAGS; + size_t i; + + for (i = 0; i < count; i++) + vma_flags_set_mask(&flags, masks[i]); + return flags; +} + +/* + * Combine pre-computed vma_flags_t masks into one value, e.g.: + * + * vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR); + * + * Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks -- + * each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a + * bit that does not exist on the current build is never materialised. + */ +#define mk_vma_flags_from_masks(...) \ + __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flags_t []){__VA_ARGS__}) + /* Clear all of the to-clear flags in flags, non-atomically. */ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 3ec8e1071673..68edac4dcd78 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -23,8 +23,8 @@ /* The set of all possible UFFD-related VM flags. */ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR) -#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \ - VMA_UFFD_MINOR_BIT) +#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \ + VMA_UFFD_MINOR) /* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining From a71204ec911d0c0e9be20e8e7cadda54e4464e8b Mon Sep 17 00:00:00 2001 From: Nakamura Shuta Date: Fri, 29 May 2026 17:53:16 +0900 Subject: [PATCH 3186/5214] rust: page: mark Page::nid as inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building the kernel, the following Rust symbol is generated: $ nm vmlinux | grep ' _R'.*Page | rustfilt ::nid `Page::nid` is a trivial wrapper around the C function `page_to_nid`. It does not make sense to go through a trivial wrapper for this function, so mark it inline. This follows commit 878620c5a93a ("rust: page: optimize rust symbol generation for Page"), which did the same for `alloc_page` and `drop`. Link: https://github.com/Rust-for-Linux/linux/issues/1145 Link: https://lore.kernel.org/20260529085316.27432-1-nakamura.shuta@gmail.com Signed-off-by: Nakamura Shuta Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Cc: Andreas Hindborg Cc: Björn Roy Baron Cc: Danilo Krummrich Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Miguel Ojeda Cc: Trevor Gross Signed-off-by: Andrew Morton --- rust/kernel/page.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index adecb200c654..764bb5acc90a 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -193,6 +193,7 @@ pub fn as_ptr(&self) -> *mut bindings::page { } /// Get the node id containing this page. + #[inline] pub fn nid(&self) -> i32 { // SAFETY: Always safe to call with a valid page. unsafe { bindings::page_to_nid(self.as_ptr()) } From 0b6073ff1574efcdb291bc3d33342f22283f9817 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 1 Jun 2026 16:48:40 +0800 Subject: [PATCH 3187/5214] mm/sparse-vmemmap: provide generic vmemmap_set_pmd() and vmemmap_check_pmd() Patch series "mm/sparse-vmemmap: Provide generic vmemmap_set_pmd() and vmemmap_check_pmd()", v3. The weak vmemmap_set_pmd() and vmemmap_check_pmd() hooks are currently no-ops in the generic code, which leaves architectures that need PMD-level handling to open-code the same logic locally. This series provides generic implementations for both helpers in mm/sparse-vmemmap.c. vmemmap_set_pmd() installs a huge PMD with PAGE_KERNEL protection, and vmemmap_check_pmd() verifies a present leaf PMD before reusing the existing vmemmap_verify() helper. With those generic helpers in place, patches 2-5 remove the now redundant arch-specific implementations from arm64, riscv, loongarch, and sparc. This patch (of 5): The two weak functions are currently no-ops on every architecture, forcing each platform that needs them to duplicate the same handful of lines. Provide a generic implementation: - vmemmap_set_pmd() simply sets a huge PMD with PAGE_KERNEL protection. - vmemmap_check_pmd() verifies that the PMD is present and leaf, then calls the existing vmemmap_verify() helper. Architectures that need special handling can continue to override the weak symbols; everyone else gets the standard version for free. Link: https://lore.kernel.org/20260601084845.3792171-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260601084845.3792171-2-songmuchun@bytedance.com Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador (SUSE) Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andreas Larsson Cc: Catalin Marinas Cc: David S. Miller Cc: Huacai Chen Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- mm/sparse-vmemmap.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 112ccf9c71ca..99e2be39671b 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -386,12 +386,17 @@ int __meminit vmemmap_populate_hvo(unsigned long addr, unsigned long end, void __weak __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node, unsigned long addr, unsigned long next) { + WARN_ON_ONCE(!pmd_set_huge(pmd, virt_to_phys(p), PAGE_KERNEL)); } int __weak __meminit vmemmap_check_pmd(pmd_t *pmd, int node, unsigned long addr, unsigned long next) { - return 0; + if (!pmd_leaf(pmdp_get(pmd))) + return 0; + vmemmap_verify((pte_t *)pmd, node, addr, next); + + return 1; } int __meminit vmemmap_populate_hugepages(unsigned long start, unsigned long end, From f521f198b50adc71171697bf2b7d49c50101def1 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 1 Jun 2026 16:48:41 +0800 Subject: [PATCH 3188/5214] arm64/mm: drop vmemmap_pmd helpers and use generic code The generic implementations now suffice; remove the arm64 copies. Link: https://lore.kernel.org/20260601084845.3792171-3-songmuchun@bytedance.com Signed-off-by: Muchun Song Acked-by: Will Deacon Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andreas Larsson Cc: Catalin Marinas Cc: David S. Miller Cc: Huacai Chen Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: WANG Xuerui Signed-off-by: Andrew Morton --- arch/arm64/mm/mmu.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index e5a42b7a0160..6bbdd400fd46 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1775,20 +1775,6 @@ static void free_empty_tables(unsigned long addr, unsigned long end, } #endif -void __meminit vmemmap_set_pmd(pmd_t *pmdp, void *p, int node, - unsigned long addr, unsigned long next) -{ - pmd_set_huge(pmdp, __pa(p), __pgprot(PROT_SECT_NORMAL)); -} - -int __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, - unsigned long addr, unsigned long next) -{ - vmemmap_verify((pte_t *)pmdp, node, addr, next); - - return pmd_leaf(READ_ONCE(*pmdp)); -} - int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { From abff0ecf7602a0881ac9c7b4644aed829d2d20e9 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 1 Jun 2026 16:48:42 +0800 Subject: [PATCH 3189/5214] riscv/mm: drop vmemmap_pmd helpers and use generic code The generic implementations now suffice; remove the riscv copies. Link: https://lore.kernel.org/20260601084845.3792171-4-songmuchun@bytedance.com Signed-off-by: Muchun Song Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andreas Larsson Cc: Catalin Marinas Cc: David S. Miller Cc: Huacai Chen Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- arch/riscv/mm/init.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 885f1db4e9bf..5f680eb83e86 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1359,19 +1359,6 @@ void __init misc_mem_init(void) } #ifdef CONFIG_SPARSEMEM_VMEMMAP -void __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node, - unsigned long addr, unsigned long next) -{ - pmd_set_huge(pmd, virt_to_phys(p), PAGE_KERNEL); -} - -int __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, - unsigned long addr, unsigned long next) -{ - vmemmap_verify((pte_t *)pmdp, node, addr, next); - return 1; -} - int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { From ecca7da924b11775b9d45a6888ac655a9b33ace0 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 1 Jun 2026 16:48:43 +0800 Subject: [PATCH 3190/5214] loongarch/mm: drop vmemmap_check_pmd helper and use generic code The generic implementations now suffice; remove the loongarch copy. Link: https://lore.kernel.org/20260601084845.3792171-5-songmuchun@bytedance.com Signed-off-by: Muchun Song Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andreas Larsson Cc: Catalin Marinas Cc: David S. Miller Cc: Huacai Chen Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- arch/loongarch/mm/init.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 687980b6e91f..3407030f3e7a 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -140,17 +140,6 @@ void __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node, set_pmd_at(&init_mm, addr, pmd, entry); } -int __meminit vmemmap_check_pmd(pmd_t *pmd, int node, - unsigned long addr, unsigned long next) -{ - int huge = pmd_val(pmdp_get(pmd)) & _PAGE_HUGE; - - if (huge) - vmemmap_verify((pte_t *)pmd, node, addr, next); - - return huge; -} - int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { From d3d58e9469008dc706863a7681fb9ae1856c8a4b Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 1 Jun 2026 16:48:44 +0800 Subject: [PATCH 3191/5214] sparc/mm: drop vmemmap_check_pmd helper and use generic code The generic implementations now suffice; remove the sparc copy. Link: https://lore.kernel.org/20260601084845.3792171-6-songmuchun@bytedance.com Signed-off-by: Muchun Song Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andreas Larsson Cc: Catalin Marinas Cc: David S. Miller Cc: Huacai Chen Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- arch/sparc/mm/init_64.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 3b679b1d1d72..103db4683b16 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2559,17 +2559,6 @@ void __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node, pmd_val(*pmd) = pte_base | __pa(p); } -int __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, - unsigned long addr, unsigned long next) -{ - int large = pmd_leaf(*pmdp); - - if (large) - vmemmap_verify((pte_t *)pmdp, node, addr, next); - - return large; -} - int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend, int node, struct vmem_altmap *altmap) { From c55dd3b46c1208d6d2ea737a8aefef4aa4c70cb8 Mon Sep 17 00:00:00 2001 From: Hui Zhu Date: Fri, 29 May 2026 09:41:30 +0800 Subject: [PATCH 3192/5214] vmalloc: fix NULL pointer dereference in is_vm_area_hugepages() find_vm_area() can return NULL if the given address is not a valid vmalloc area. Check the return value before dereferencing it to avoid a kernel crash. Link: https://lore.kernel.org/20260529014130.671291-1-hui.zhu@linux.dev Fixes: 121e6f3258fe ("mm/vmalloc: hugepage vmalloc mappings") Signed-off-by: Hui Zhu Reviewed-by: Dev Jain Reviewed-by: Uladzislau Rezki (Sony) Cc: Nicholas Piggin Signed-off-by: Andrew Morton --- include/linux/vmalloc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 3b02c0c6b371..d87dc7f77f4e 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -265,7 +265,9 @@ static inline bool is_vm_area_hugepages(const void *addr) * allocated in the vmalloc layer. */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC - return find_vm_area(addr)->page_order > 0; + struct vm_struct *area = find_vm_area(addr); + + return area && area->page_order > 0; #else return false; #endif From a51cbdf02aec619f90db7e9f06e295adb8009d4d Mon Sep 17 00:00:00 2001 From: tanze Date: Mon, 1 Jun 2026 19:04:23 +0800 Subject: [PATCH 3193/5214] mm/filemap: use folio_next_index() for start Use folio_next_index() instead of open-coding folio->index + folio_nr_pages(folio) when updating @start in filemap_get_folios_contig(), filemap_get_folios_tag(), and filemap_get_folios_dirty(). Link: https://lore.kernel.org/20260601110425.44784-1-tanze@kylinos.cn Signed-off-by: tanze Reviewed-by: Jan Kara Reviewed-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton --- mm/filemap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 98434acc69c1..5d9f9b36e9d8 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2284,8 +2284,7 @@ unsigned filemap_get_folios_contig(struct address_space *mapping, goto put_folio; if (!folio_batch_add(fbatch, folio)) { - nr = folio_nr_pages(folio); - *start = folio->index + nr; + *start = folio_next_index(folio); goto out; } xas_advance(&xas, folio_next_index(folio) - 1); @@ -2345,8 +2344,7 @@ unsigned filemap_get_folios_tag(struct address_space *mapping, pgoff_t *start, if (xa_is_value(folio)) continue; if (!folio_batch_add(fbatch, folio)) { - unsigned long nr = folio_nr_pages(folio); - *start = folio->index + nr; + *start = folio_next_index(folio); goto out; } } @@ -2404,8 +2402,7 @@ unsigned filemap_get_folios_dirty(struct address_space *mapping, pgoff_t *start, } } if (!folio_batch_add(fbatch, folio)) { - unsigned long nr = folio_nr_pages(folio); - *start = folio->index + nr; + *start = folio_next_index(folio); goto out; } } From c13a0316aef5f4b73e8b4bf6943737f836d65e1d Mon Sep 17 00:00:00 2001 From: Youngjun Park Date: Tue, 24 Mar 2026 01:08:21 +0900 Subject: [PATCH 3194/5214] mm/swap, PM: hibernate: fix swapoff race in uswsusp by pinning swap device Patch series "mm/swap, PM: hibernate: fix swapoff race in uswsusp by pinning swap device", v8. Currently, in the uswsusp path, only the swap type value is retrieved at lookup time without holding a reference. If swapoff races after the type is acquired, subsequent slot allocations operate on a stale swap device. Additionally, grabbing and releasing the swap device reference on every slot allocation is inefficient across the entire hibernation swap path. This patch series addresses these issues: - Patch 1: Fixes the swapoff race in uswsusp by pinning the swap device from the point it is looked up until the session completes. - Patch 2: Removes the overhead of per-slot reference counting in alloc/free paths and cleans up the redundant SWP_WRITEOK check. This patch (of 2): Hibernation via uswsusp (/dev/snapshot ioctls) has a race window: after selecting the resume swap area but before user space is frozen, swapoff may run and invalidate the selected swap device. Fix this by pinning the swap device with SWP_HIBERNATION while it is in use. The pin is exclusive, which is sufficient since hibernate_acquire() already prevents concurrent hibernation sessions. The kernel swsusp path (sysfs-based hibernate/resume) uses find_hibernation_swap_type() which is not affected by the pin. It freezes user space before touching swap, so swapoff cannot race. Introduce dedicated helpers: - pin_hibernation_swap_type(): Look up and pin the swap device. Used by the uswsusp path. - find_hibernation_swap_type(): Lookup without pinning. Used by the kernel swsusp path. - unpin_hibernation_swap_type(): Clear the hibernation pin. While a swap device is pinned, swapoff is prevented from proceeding. Link: https://lore.kernel.org/20260323160822.1409904-1-youngjun.park@lge.com Link: https://lore.kernel.org/20260323160822.1409904-2-youngjun.park@lge.com Signed-off-by: Youngjun Park Reviewed-by: Kairui Song Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: Kemeng Shi Cc: Nhat Pham Cc: "Rafael J . Wysocki" Signed-off-by: Andrew Morton --- include/linux/swap.h | 5 +- kernel/power/swap.c | 2 +- kernel/power/user.c | 15 ++++- mm/swapfile.c | 137 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 137 insertions(+), 22 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 8c43bc3055c9..8f0f68e245ba 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -213,6 +213,7 @@ enum { SWP_PAGE_DISCARD = (1 << 10), /* freed swap page-cluster discards */ SWP_STABLE_WRITES = (1 << 11), /* no overwrite PG_writeback pages */ SWP_SYNCHRONOUS_IO = (1 << 12), /* synchronous IO is efficient */ + SWP_HIBERNATION = (1 << 13), /* pinned for hibernation */ /* add others here before... */ }; @@ -432,7 +433,9 @@ static inline long get_nr_swap_pages(void) } extern void si_swapinfo(struct sysinfo *); -int swap_type_of(dev_t device, sector_t offset); +extern int pin_hibernation_swap_type(dev_t device, sector_t offset); +extern void unpin_hibernation_swap_type(int type); +extern int find_hibernation_swap_type(dev_t device, sector_t offset); int find_first_swap(dev_t *device); extern unsigned int count_swap_pages(int, int); extern sector_t swapdev_block(int, pgoff_t); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 2e64869bb5a0..cc4764149e8f 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -341,7 +341,7 @@ static int swsusp_swap_check(void) * This is called before saving the image. */ if (swsusp_resume_device) - res = swap_type_of(swsusp_resume_device, swsusp_resume_block); + res = find_hibernation_swap_type(swsusp_resume_device, swsusp_resume_block); else res = find_first_swap(&swsusp_resume_device); if (res < 0) diff --git a/kernel/power/user.c b/kernel/power/user.c index be77f3556bd7..d0fcfba7ac23 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -71,7 +71,7 @@ static int snapshot_open(struct inode *inode, struct file *filp) memset(&data->handle, 0, sizeof(struct snapshot_handle)); if ((filp->f_flags & O_ACCMODE) == O_RDONLY) { /* Hibernating. The image device should be accessible. */ - data->swap = swap_type_of(swsusp_resume_device, 0); + data->swap = pin_hibernation_swap_type(swsusp_resume_device, 0); data->mode = O_RDONLY; data->free_bitmaps = false; error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); @@ -90,8 +90,10 @@ static int snapshot_open(struct inode *inode, struct file *filp) data->free_bitmaps = !error; } } - if (error) + if (error) { + unpin_hibernation_swap_type(data->swap); hibernate_release(); + } data->frozen = false; data->ready = false; @@ -115,6 +117,7 @@ static int snapshot_release(struct inode *inode, struct file *filp) data = filp->private_data; data->dev = 0; free_all_swap_pages(data->swap); + unpin_hibernation_swap_type(data->swap); if (data->frozen) { pm_restore_gfp_mask(); free_basic_memory_bitmaps(); @@ -235,11 +238,17 @@ static int snapshot_set_swap_area(struct snapshot_data *data, offset = swap_area.offset; } + /* + * Unpin the swap device if a swap area was already + * set by SNAPSHOT_SET_SWAP_AREA. + */ + unpin_hibernation_swap_type(data->swap); + /* * User space encodes device types as two-byte values, * so we need to recode them */ - data->swap = swap_type_of(swdev, offset); + data->swap = pin_hibernation_swap_type(swdev, offset); if (data->swap < 0) return swdev ? -ENODEV : -EINVAL; data->dev = swdev; diff --git a/mm/swapfile.c b/mm/swapfile.c index 615d90867111..5e1e605ad9a1 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -132,7 +132,7 @@ static DEFINE_PER_CPU(struct percpu_swap_cluster, percpu_swap_cluster) = { /* May return NULL on invalid type, caller must check for NULL return */ static struct swap_info_struct *swap_type_to_info(int type) { - if (type >= MAX_SWAPFILES) + if (type < 0 || type >= MAX_SWAPFILES) return NULL; return READ_ONCE(swap_info[type]); /* rcu_dereference() */ } @@ -2199,22 +2199,15 @@ void swap_free_hibernation_slot(swp_entry_t entry) put_swap_device(si); } -/* - * Find the swap type that corresponds to given device (if any). - * - * @offset - number of the PAGE_SIZE-sized block of the device, starting - * from 0, in which the swap header is expected to be located. - * - * This is needed for the suspend to disk (aka swsusp). - */ -int swap_type_of(dev_t device, sector_t offset) +static int __find_hibernation_swap_type(dev_t device, sector_t offset) { int type; - if (!device) - return -1; + lockdep_assert_held(&swap_lock); + + if (!device) + return -EINVAL; - spin_lock(&swap_lock); for (type = 0; type < nr_swapfiles; type++) { struct swap_info_struct *sis = swap_info[type]; @@ -2224,16 +2217,118 @@ int swap_type_of(dev_t device, sector_t offset) if (device == sis->bdev->bd_dev) { struct swap_extent *se = first_se(sis); - if (se->start_block == offset) { - spin_unlock(&swap_lock); + if (se->start_block == offset) return type; - } } } - spin_unlock(&swap_lock); return -ENODEV; } +/** + * pin_hibernation_swap_type - Pin the swap device for hibernation + * @device: Block device containing the resume image + * @offset: Offset identifying the swap area + * + * Locate the swap device for @device/@offset and mark it as pinned + * for hibernation. While pinned, swapoff() is prevented. + * + * Only one uswsusp context may pin a swap device at a time. + * If already pinned, this function returns -EBUSY. + * + * Return: + * >= 0 on success (swap type). + * -EINVAL if @device is invalid. + * -ENODEV if the swap device is not found. + * -EBUSY if the device is already pinned for hibernation. + */ +int pin_hibernation_swap_type(dev_t device, sector_t offset) +{ + int type; + struct swap_info_struct *si; + + spin_lock(&swap_lock); + + type = __find_hibernation_swap_type(device, offset); + if (type < 0) { + spin_unlock(&swap_lock); + return type; + } + + si = swap_type_to_info(type); + if (WARN_ON_ONCE(!si)) { + spin_unlock(&swap_lock); + return -ENODEV; + } + + /* + * hibernate_acquire() prevents concurrent hibernation sessions. + * This check additionally guards against double-pinning within + * the same session. + */ + if (WARN_ON_ONCE(si->flags & SWP_HIBERNATION)) { + spin_unlock(&swap_lock); + return -EBUSY; + } + + si->flags |= SWP_HIBERNATION; + + spin_unlock(&swap_lock); + return type; +} + +/** + * unpin_hibernation_swap_type - Unpin the swap device for hibernation + * @type: Swap type previously returned by pin_hibernation_swap_type() + * + * Clear the hibernation pin on the given swap device, allowing + * swapoff() to proceed normally. + * + * If @type does not refer to a valid swap device, this function + * does nothing. + */ +void unpin_hibernation_swap_type(int type) +{ + struct swap_info_struct *si; + + spin_lock(&swap_lock); + si = swap_type_to_info(type); + if (!si) { + spin_unlock(&swap_lock); + return; + } + si->flags &= ~SWP_HIBERNATION; + spin_unlock(&swap_lock); +} + +/** + * find_hibernation_swap_type - Find swap type for hibernation + * @device: Block device containing the resume image + * @offset: Offset within the device identifying the swap area + * + * Locate the swap device corresponding to @device and @offset. + * + * Unlike pin_hibernation_swap_type(), this function only performs a + * lookup and does not mark the swap device as pinned for hibernation. + * + * This is safe in the sysfs-based hibernation path where user space + * is already frozen and swapoff() cannot run concurrently. + * + * Return: + * A non-negative swap type on success. + * -EINVAL if @device is invalid. + * -ENODEV if no matching swap device is found. + */ +int find_hibernation_swap_type(dev_t device, sector_t offset) +{ + int type; + + spin_lock(&swap_lock); + type = __find_hibernation_swap_type(device, offset); + spin_unlock(&swap_lock); + + return type; +} + int find_first_swap(dev_t *device) { int type; @@ -2996,6 +3091,14 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) spin_unlock(&swap_lock); goto out_dput; } + + /* Refuse swapoff while the device is pinned for hibernation */ + if (p->flags & SWP_HIBERNATION) { + err = -EBUSY; + spin_unlock(&swap_lock); + goto out_dput; + } + if (!security_vm_enough_memory_mm(current->mm, p->pages)) vm_unacct_memory(p->pages); else { From 0d97349679c5fe9941d283715ca109d61bbdc06e Mon Sep 17 00:00:00 2001 From: Youngjun Park Date: Tue, 24 Mar 2026 01:08:22 +0900 Subject: [PATCH 3195/5214] mm/swap: remove redundant swap device reference in alloc/free In the previous commit, uswsusp was modified to pin the swap device when the swap type is determined, ensuring the device remains valid throughout the hibernation I/O path. Therefore, it is no longer necessary to repeatedly get and put the swap device reference for each swap slot allocation and free operation. For hibernation via the sysfs interface, user-space tasks are frozen before swap allocation begins, so swapoff cannot race with allocation. After resume, tasks remain frozen while swap slots are freed, so additional reference management is not required there either. Remove the redundant swap device get/put operations from the hibernation swap allocation and free paths. Also remove the SWP_WRITEOK check before allocation, as the cluster allocation logic already validates the swap device state. Update function comments to document the caller's responsibility for ensuring swap device stability. Link: https://lore.kernel.org/20260323160822.1409904-3-youngjun.park@lge.com Signed-off-by: Youngjun Park Reviewed-by: Kairui Song Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: Kemeng Shi Cc: Nhat Pham Cc: "Rafael J . Wysocki" Signed-off-by: Andrew Morton --- mm/swapfile.c | 68 +++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 5e1e605ad9a1..78b49b0658ad 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2138,7 +2138,16 @@ void swap_put_entries_direct(swp_entry_t entry, int nr) } #ifdef CONFIG_HIBERNATION -/* Allocate a slot for hibernation */ +/** + * swap_alloc_hibernation_slot() - Allocate a swap slot for hibernation. + * @type: swap device type index to allocate from. + * + * The caller must ensure the swap device is stable, either by pinning + * it (SWP_HIBERNATION) or by freezing user-space. + * + * Return: a valid swp_entry_t on success, or an empty entry (val == 0) + * on failure. + */ swp_entry_t swap_alloc_hibernation_slot(int type) { struct swap_info_struct *pcp_si, *si = swap_type_to_info(type); @@ -2149,46 +2158,42 @@ swp_entry_t swap_alloc_hibernation_slot(int type) if (!si) goto fail; - /* This is called for allocating swap entry, not cache */ - if (get_swap_device_info(si)) { - if (si->flags & SWP_WRITEOK) { - /* - * Try the local cluster first if it matches the device. If - * not, try grab a new cluster and override local cluster. - */ - local_lock(&percpu_swap_cluster.lock); - pcp_si = this_cpu_read(percpu_swap_cluster.si[0]); - pcp_offset = this_cpu_read(percpu_swap_cluster.offset[0]); - if (pcp_si == si && pcp_offset) { - ci = swap_cluster_lock(si, pcp_offset); - if (cluster_is_usable(ci, 0)) - offset = alloc_swap_scan_cluster(si, ci, NULL, pcp_offset); - else - swap_cluster_unlock(ci); - } - if (!offset) - offset = cluster_alloc_swap_entry(si, NULL); - local_unlock(&percpu_swap_cluster.lock); - if (offset) - entry = swp_entry(si->type, offset); - } - put_swap_device(si); + /* + * Try the local cluster first if it matches the device. If + * not, try grab a new cluster and override local cluster. + */ + local_lock(&percpu_swap_cluster.lock); + pcp_si = this_cpu_read(percpu_swap_cluster.si[0]); + pcp_offset = this_cpu_read(percpu_swap_cluster.offset[0]); + if (pcp_si == si && pcp_offset) { + ci = swap_cluster_lock(si, pcp_offset); + if (cluster_is_usable(ci, 0)) + offset = alloc_swap_scan_cluster(si, ci, NULL, pcp_offset); + else + swap_cluster_unlock(ci); } + if (!offset) + offset = cluster_alloc_swap_entry(si, NULL); + local_unlock(&percpu_swap_cluster.lock); + if (offset) + entry = swp_entry(si->type, offset); + fail: return entry; } -/* Free a slot allocated by swap_alloc_hibernation_slot */ +/** + * swap_free_hibernation_slot() - Free a swap slot allocated for hibernation. + * @entry: swap entry to free. + * + * The caller must ensure the swap device is stable. + */ void swap_free_hibernation_slot(swp_entry_t entry) { - struct swap_info_struct *si; + struct swap_info_struct *si = __swap_entry_to_info(entry); struct swap_cluster_info *ci; pgoff_t offset = swp_offset(entry); - si = get_swap_device(entry); - if (WARN_ON(!si)) - return; - ci = swap_cluster_lock(si, offset); __swap_cluster_put_entry(ci, offset % SWAPFILE_CLUSTER); __swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1); @@ -2196,7 +2201,6 @@ void swap_free_hibernation_slot(swp_entry_t entry) /* In theory readahead might add it to the swap cache by accident */ __try_to_reclaim_swap(si, offset, TTRS_ANYWAY); - put_swap_device(si); } static int __find_hibernation_swap_type(dev_t device, sector_t offset) From 32a2b73ec232b284b029d34bcfaa9a7f424151d2 Mon Sep 17 00:00:00 2001 From: JP Kobryn Date: Wed, 3 Jun 2026 23:17:25 -0700 Subject: [PATCH 3196/5214] mm/compaction: cap compact_gap() at COMPACT_CLUSTER_MAX compact_gap() returns 2 << order, which is used as watermark headroom in __compaction_suitable() and as a threshold in kswapd reclaim decisions. The computed value scales exponentially by order. For order-9 THP allocations this evaluates to 1024 pages, but the compaction free scanner's working set is bounded by COMPACT_CLUSTER_MAX (32 pages). The scanner stops isolating free pages once it matches the migration batch. The current gap over-reserves by 32x. On fragmented production hosts, kswapd will try to reclaim up to the gap, but it only reaches that threshold in 18% of attempts. As a result, reclaim continues in the majority of cases despite many lower-order free pages being available. The over-sized gap also causes 46% of order-9 compaction suitability checks to fail unnecessarily: the zone has sufficient free pages for the scanner to operate, but not enough to clear the inflated threshold. Cap compact_gap() at COMPACT_CLUSTER_MAX so the watermark headroom reflects the scanner's actual capacity. This function is used by two key heuristics. The first is when kswapd can stop high-order reclaim and downgrade to order-0 balancing, allowing kcompactd to be woken for the original higher allocation order. The second is zone suitability checking, where the smaller gap allows compaction to start sooner. Note that orders 0-4 are unaffected since their gap is already less than or equal to COMPACT_CLUSTER_MAX. A/B test on v6.13-based instagram production hosts (64GB, 60s measurement): Unpatched (43 hosts) pgscan_kswapd (mean/host): ~1.6M reclaim efficiency (steal/scan): 83.8% per-compaction success (success/stall): 2.1% THP success (alloc/alloc+fallback): 4.9% forced lru_add_drain (mean/host): ~107K Patched (59 hosts) pgscan_kswapd (mean/host): ~449K reclaim efficiency (steal/scan): 91.0% per-compaction success (success/stall): 28.3% THP success (alloc/alloc+fallback): 17.2% forced lru_add_drain (mean/host): ~64K Additional tests were also performed using a workload of similar shape and based on mm-new at the time of testing. Across three 60s runs, the patch showed improvements consistent with the previous test: reduced kswapd reclaim and fewer THP fault fallbacks. Unpatched kswapd_shrink_node downgrade to order-0 (mean): 0 thp_fault_fallback (mean): 1217 pgscan_kswapd (mean): 6328 pgsteal_kswapd (mean): 5657 Patched kswapd_shrink_node downgrade to order-0 (mean): 28 thp_fault_fallback (mean): 738 pgscan_kswapd (mean): 3773 pgsteal_kswapd (mean): 3243 Link: https://lore.kernel.org/20260604061725.13800-1-jp.kobryn@linux.dev Signed-off-by: JP Kobryn (Meta) Reviewed-by: Vlastimil Babka (SUSE) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Zi Yan Signed-off-by: Andrew Morton --- include/linux/compaction.h | 8 ++++---- mm/vmscan.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index c829c48d1c71..f29ef0653546 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -2,6 +2,8 @@ #ifndef _LINUX_COMPACTION_H #define _LINUX_COMPACTION_H +#include + /* * Determines how hard direct compaction should try to succeed. * Lower value means higher priority, analogically to reclaim priority. @@ -73,11 +75,9 @@ static inline unsigned long compact_gap(unsigned int order) * effectively limited by COMPACT_CLUSTER_MAX, as that's the maximum * that the migrate scanner can have isolated on migrate list, and free * scanner is only invoked when the number of isolated free pages is - * lower than that. But it's not worth to complicate the formula here - * as a bigger gap for higher orders than strictly necessary can also - * improve chances of compaction success. + * lower than that. */ - return 2UL << order; + return min(2UL << order, COMPACT_CLUSTER_MAX); } static inline int current_is_kcompactd(void) diff --git a/mm/vmscan.c b/mm/vmscan.c index e8a90911bf88..3f3ff25e561a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -7014,7 +7014,7 @@ static bool kswapd_shrink_node(pg_data_t *pgdat, /* * Fragmentation may mean that the system cannot be rebalanced for - * high-order allocations. If twice the allocation size has been + * high-order allocations. If at least the compaction gap has been * reclaimed then recheck watermarks only at order-0 to prevent * excessive reclaim. Assume that a process requested a high-order * can direct reclaim/compact. From 8198657c74170ea78808d1f1d886c7d35fd3694e Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Thu, 21 May 2026 10:18:58 +0800 Subject: [PATCH 3197/5214] lib/test_hmm: check alloc_page_vma() return value and handle OOM Check alloc_page_vma() return status for page allocation failures, free allocated pages and return VM_FAULT_OOM on error. Handle return codes of dmirror_devmem_fault_alloc_and_copy(), call migrate_vma_finalize() to remove migration entries from migrate_vma_setup(). Link: https://lore.kernel.org/20260521021858.21511-1-liuqiangneo@163.com Signed-off-by: Qiang Liu Cc: Alistair Popple Cc: Jason Gunthorpe Cc: Leon Romanovsky [akpm@linux-foundation.org: fix dmirror_devmem_fault_alloc_and_copy() retval handling] Link: https://lore.kernel.org/oe-kbuild-all/202606011329.zWs2BKy4-lkp@intel.com/ Signed-off-by: Andrew Morton --- lib/test_hmm.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 35f774ed2d99..9c59d1ceb5b5 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -1063,6 +1063,25 @@ static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args, /* Try with smaller pages if large allocation fails */ if (!dpage && order) { dpage = alloc_page_vma(GFP_HIGHUSER_MOVABLE, args->vma, addr); + if (!dpage) { + /* Unlock and free pages already allocated. */ + while (i > 0) { + struct page *fpage; + + fpage = migrate_pfn_to_page(dst[--i]); + unlock_page(fpage); + __free_page(fpage); + } + /* Clear remaining dst entries to avoid + * migrate_vma_pages/finalize() using + * uninitialized values. + */ + while (i < (1 << order)) { + dst[i] = 0; + i++; + } + return VM_FAULT_OOM; + } lock_page(dpage); dst[i] = migrate_pfn(page_to_pfn(dpage)); dst_page = pfn_to_page(page_to_pfn(dpage)); @@ -1148,7 +1167,11 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror, goto out; pr_debug("Migrating from device mem to sys mem\n"); - dmirror_devmem_fault_alloc_and_copy(&args, dmirror); + if (dmirror_devmem_fault_alloc_and_copy(&args, dmirror)) { + migrate_vma_finalize(&args); + ret = -ENOMEM; + goto out; + } migrate_vma_pages(&args); cmd->cpages += dmirror_successful_migrated_pages(&args); @@ -1689,8 +1712,10 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf) } ret = dmirror_devmem_fault_alloc_and_copy(&args, dmirror); - if (ret) + if (ret) { + migrate_vma_finalize(&args); goto err; + } migrate_vma_pages(&args); /* * No device finalize step is needed since From cd1fc0e3c1f67c0c31dfc215e5d9b771133dedc0 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Thu, 4 Jun 2026 05:53:05 +0000 Subject: [PATCH 3198/5214] fs/proc/task_mmu: do not warn on seeing non-migration pmd entry Patch series "mm/hmm: A fix and a selftest", v3. Patch 1 fixes a stale warning present from the time when only migration softleaf entries were supported at the PMD level. Patch 2 adds some code into hmm-tests.c which exercises the pagemap path for PMD device-private entries. This patch (of 2): pagemap_pmd_range_thp() warns if a non-present PMD is not a migration entry. This became false once device-private entries at the PMD level were added. Therefore, remove the stale migration-only assertion. Link: https://lore.kernel.org/20260604055308.1947679-1-dev.jain@arm.com Link: https://lore.kernel.org/20260604055308.1947679-2-dev.jain@arm.com Fixes: a30b48bf1b24 ("mm/migrate_device: implement THP migration of zone device pages") Signed-off-by: Dev Jain Reviewed-by: Balbir Singh Reviewed-by: Lorenzo Stoakes Tested-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Oscar Salvador (SUSE) Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 06fb94a965ff..d32408f7cd5e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2129,7 +2129,6 @@ static int pagemap_pmd_range_thp(pmd_t *pmdp, unsigned long addr, flags |= PM_SOFT_DIRTY; if (pmd_swp_uffd_wp(pmd)) flags |= PM_UFFD_WP; - VM_WARN_ON_ONCE(!pmd_is_migration_entry(pmd)); page = softleaf_to_page(entry); } From e3d8707358ea76b78bdec9928937bb9a797f2c8f Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Thu, 4 Jun 2026 05:53:06 +0000 Subject: [PATCH 3199/5214] selftests/mm/hmm-tests: test pagemap reads of PMD device-private entries To cover pagemap paths scanning PMD entries, add assertions to check whether a device-private PMD entry has the correct pagemap information - the PM_SWAP bit must be on in the pagemap entry. Before that, we must assert through HMM_DMIRROR_SNAPSHOT snapshot that the leaf entry is at PMD level and not PTE level. Link: https://lore.kernel.org/20260604055308.1947679-3-dev.jain@arm.com Signed-off-by: Dev Jain Reviewed-by: Lorenzo Stoakes Cc: Balbir Singh Cc: David Hildenbrand (Arm) Cc: Oscar Salvador (SUSE) Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 34 ++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 7a4daadfb0c8..6a23c09ac2da 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -2274,8 +2274,11 @@ TEST_F(hmm, migrate_anon_huge_fault) unsigned long npages; unsigned long size; unsigned long i; + unsigned char *m; + uint64_t entry; void *old_ptr; void *map; + int pagemap_fd; int *ptr; int ret; @@ -2298,8 +2301,6 @@ TEST_F(hmm, migrate_anon_huge_fault) npages = size >> self->page_shift; map = (void *)ALIGN((uintptr_t)buffer->ptr, size); - ret = madvise(map, size, MADV_HUGEPAGE); - ASSERT_EQ(ret, 0); old_ptr = buffer->ptr; buffer->ptr = map; @@ -2307,6 +2308,9 @@ TEST_F(hmm, migrate_anon_huge_fault) for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) ptr[i] = i; + ret = madvise(map, size, MADV_COLLAPSE); + ASSERT_EQ(ret, 0); + /* Migrate memory to device. */ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); ASSERT_EQ(ret, 0); @@ -2316,6 +2320,32 @@ TEST_F(hmm, migrate_anon_huge_fault) for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i], i); + if (!hmm_is_coherent_type(variant->device_number)) { + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, + buffer, npages); + ASSERT_EQ(ret, 0); + ASSERT_EQ(buffer->cpages, npages); + + m = buffer->mirror; + for (i = 0; i < npages; ++i) + ASSERT_EQ(m[i], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL | + HMM_DMIRROR_PROT_WRITE | + HMM_DMIRROR_PROT_PMD); + + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + ASSERT_GE(pagemap_fd, 0); + + for (i = 0; i < npages; ++i) { + entry = pagemap_get_entry(pagemap_fd, + (char *)buffer->ptr + i * self->page_size); + + ASSERT_NE(entry & PM_SWAP, 0); + ASSERT_FALSE(PAGEMAP_PRESENT(entry)); + } + + close(pagemap_fd); + } + /* Fault pages back to system memory and check them. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i], i); From 0550d9828df4d937a40dd2574b1d125509a4e8a1 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Tue, 28 Apr 2026 00:36:13 +0530 Subject: [PATCH 3200/5214] dt-bindings: remoteproc: qcom,sm8550-pas: Add Hawi ADSP compatible Document compatible string for the ADSP Peripheral Authentication Service on the Hawi SoC, which is compatible with the Qualcomm SM8550 ADSP PAS and can fallback to SM8550 except for the one additional interrupt ("shutdown-ack"). Signed-off-by: Mukesh Ojha Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260427190614.3679937-1-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index 1e4db0c9fcf9..161e9b55cb3e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -30,6 +30,7 @@ properties: - items: - enum: - qcom,glymur-adsp-pas + - qcom,hawi-adsp-pas - qcom,kaanapali-adsp-pas - qcom,sm8750-adsp-pas - const: qcom,sm8550-adsp-pas @@ -104,6 +105,7 @@ allOf: enum: - qcom,glymur-adsp-pas - qcom,glymur-cdsp-pas + - qcom,hawi-adsp-pas - qcom,kaanapali-adsp-pas - qcom,kaanapali-cdsp-pas - qcom,sm8750-adsp-pas From a9697e35887e7e3dc1135dd97efd39d46d910e73 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Tue, 28 Apr 2026 00:36:14 +0530 Subject: [PATCH 3201/5214] dt-bindings: remoteproc: qcom,sm8550-pas: Add Hawi CDSP compatible Document compatible string for the CDSP Peripheral Authentication Service on the Hawi SoC, which is compatible with the Qualcomm SM8550 SoC except for the one additional interrupt ("shutdown-ack") and similar to the Qualcomm Kaanapali SoC, "global_sync_mem" is not managed by the kernel so it remains unlisted. Signed-off-by: Mukesh Ojha Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260427190614.3679937-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index 161e9b55cb3e..9f30a38152a3 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -37,6 +37,7 @@ properties: - items: - enum: - qcom,glymur-cdsp-pas + - qcom,hawi-cdsp-pas - qcom,kaanapali-cdsp-pas - const: qcom,sm8550-cdsp-pas - items: @@ -106,6 +107,7 @@ allOf: - qcom,glymur-adsp-pas - qcom,glymur-cdsp-pas - qcom,hawi-adsp-pas + - qcom,hawi-cdsp-pas - qcom,kaanapali-adsp-pas - qcom,kaanapali-cdsp-pas - qcom,sm8750-adsp-pas From 6ad61d0acd41044a949e84f96a5f8e02284d350f Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 8 Dec 2025 16:33:14 -0600 Subject: [PATCH 3202/5214] remoteproc: qcom_q6v5_wcss: drop redundant wcss_q6_bcr_reset The wcss_q6_bcr_reset used on QCS404, and wcss_q6_reset used on IPQ are the same. "BCR reset" is redundant, and likely a mistake. Use the documented "wcss_q6_reset" instead. Drop ".wcss_q6_reset_required" from the descriptor, since all targets now need it. This changes the bindings expectations, however, it actually fixes the driver to consume the intended ones (qcom,q6v5.txt), which lists "wcss_q6_reset" and *not* "wcss_q6_bcr_reset" Fixes: 0af65b9b915e ("remoteproc: qcom: wcss: Add non pas wcss Q6 support for QCS404") Reviewed-by: Konrad Dybcio Signed-off-by: Alexandru Gagniuc Link: https://lore.kernel.org/r/20251208223315.3540680-1-mr.nuke.me@gmail.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_wcss.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index c27200159a88..b391724cfd08 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -96,7 +96,6 @@ struct wcss_data { unsigned int crash_reason_smem; u32 version; bool aon_reset_required; - bool wcss_q6_reset_required; const char *ssr_name; const char *sysmon_name; int ssctl_id; @@ -134,7 +133,6 @@ struct q6v5_wcss { struct reset_control *wcss_aon_reset; struct reset_control *wcss_reset; struct reset_control *wcss_q6_reset; - struct reset_control *wcss_q6_bcr_reset; struct qcom_q6v5 q6v5; @@ -309,7 +307,7 @@ static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss) return ret; /* Remove reset to the WCNSS QDSP6SS */ - reset_control_deassert(wcss->wcss_q6_bcr_reset); + reset_control_deassert(wcss->wcss_q6_reset); /* Enable Q6SSTOP_AHBFABRIC_CBCR clock */ ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk); @@ -803,19 +801,10 @@ static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss, return PTR_ERR(wcss->wcss_reset); } - if (desc->wcss_q6_reset_required) { - wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset"); - if (IS_ERR(wcss->wcss_q6_reset)) { - dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); - return PTR_ERR(wcss->wcss_q6_reset); - } - } - - wcss->wcss_q6_bcr_reset = devm_reset_control_get_optional_exclusive(dev, - "wcss_q6_bcr_reset"); - if (IS_ERR(wcss->wcss_q6_bcr_reset)) { - dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); - return PTR_ERR(wcss->wcss_q6_bcr_reset); + wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset"); + if (IS_ERR(wcss->wcss_q6_reset)) { + dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); + return PTR_ERR(wcss->wcss_q6_reset); } return 0; @@ -1062,7 +1051,6 @@ static const struct wcss_data wcss_ipq8074_res_init = { .firmware_name = "IPQ8074/q6_fw.mdt", .crash_reason_smem = WCSS_CRASH_REASON, .aon_reset_required = true, - .wcss_q6_reset_required = true, .ops = &q6v5_wcss_ipq8074_ops, .requires_force_stop = true, }; @@ -1072,7 +1060,6 @@ static const struct wcss_data wcss_qcs404_res_init = { .firmware_name = "wcnss.mdt", .version = WCSS_QCS404, .aon_reset_required = false, - .wcss_q6_reset_required = false, .ssr_name = "mpss", .sysmon_name = "wcnss", .ssctl_id = 0x12, From ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d Mon Sep 17 00:00:00 2001 From: Wasim Nazir Date: Wed, 18 Mar 2026 17:19:16 +0530 Subject: [PATCH 3203/5214] remoteproc: qcom: Fix leak when custom dump_segments addition fails Free allocated minidump_region 'name' in qcom_add_minidump_segments() when failing before adding the region to 'dump_segments'. Otherwise, the 'name' is not tracked and is never freed by qcom_minidump_cleanup(). Return error when adding to 'dump_segments' fails. Cc: stable@vger.kernel.org # v5.11 Fixes: 8ed8485c4f05 ("remoteproc: qcom: Add capability to collect minidumps") Reviewed-by: Mukesh Ojha Signed-off-by: Wasim Nazir Link: https://lore.kernel.org/r/20260318-rproc-memleak-v2-1-ade70ab858f2@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_common.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index fd2b6824ad26..e1a955476c9b 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -109,6 +109,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy struct minidump_region __iomem *ptr; struct minidump_region region; int seg_cnt, i; + int ret = 0; dma_addr_t da; size_t size; char *name; @@ -129,17 +130,22 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) { name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL); if (!name) { - iounmap(ptr); - return -ENOMEM; + ret = -ENOMEM; + break; } da = le64_to_cpu(region.address); size = le64_to_cpu(region.size); - rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name); + ret = rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, + name); + if (ret) { + kfree(name); + break; + } } } iounmap(ptr); - return 0; + return ret; } void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, From 955e3852ad4fb65effbe1064d9f967964d6ee542 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Apr 2026 10:33:35 +0200 Subject: [PATCH 3204/5214] remoteproc: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260422083334.84294-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index c78e431b7b2d..c521c744e7db 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -176,7 +176,7 @@ config QCOM_Q6V5_COMMON depends on QCOM_SMEM config QCOM_Q6V5_ADSP - tristate "Qualcomm Technology Inc ADSP Peripheral Image Loader" + tristate "Qualcomm ADSP Peripheral Image Loader" depends on OF && ARCH_QCOM depends on QCOM_SMEM depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n From 8752c396ce3b2136b3d4c906fe103f6efb6782d9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 12 May 2026 11:13:03 +0200 Subject: [PATCH 3205/5214] hwspinlock: qcom: avoid uninitialized struct members The reg_field is allocated on stack, so using the REG_FIELD macro will ensure that unused members do not have uninitialized values. Fixes: 19a0f61224d2 ("hwspinlock: qcom: Add support for Qualcomm HW Mutex block") Link: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com Signed-off-by: Wolfram Sang Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260512091339.31085-2-wsa+renesas@sang-engineering.com Signed-off-by: Bjorn Andersson --- drivers/hwspinlock/qcom_hwspinlock.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 0390979fd765..712003a4640c 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -202,7 +202,6 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, static int qcom_hwspinlock_probe(struct platform_device *pdev) { struct hwspinlock_device *bank; - struct reg_field field; struct regmap *regmap; size_t array_size; u32 stride; @@ -224,9 +223,7 @@ static int qcom_hwspinlock_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bank); for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { - field.reg = base + i * stride; - field.lsb = 0; - field.msb = 31; + struct reg_field field = REG_FIELD(base + i * stride, 0, 31); bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, regmap, field); From 181836fb3aa6715e3dcd899c9283daa9dfbb91a0 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Fri, 8 May 2026 16:10:47 +0530 Subject: [PATCH 3206/5214] dt-bindings: remoteproc: Add Shikra RPM processor compatible Add compatible for the Qualcomm Shikra RPM processor. Signed-off-by: Komal Bajaj Signed-off-by: Sneh Mankad Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260508-shikra_mailbox_and_rpm_changes-v3-2-698f8e5fb339@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/remoteproc/qcom,rpm-proc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,rpm-proc.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,rpm-proc.yaml index 540bdfca53d9..823304afaa98 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,rpm-proc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,rpm-proc.yaml @@ -87,6 +87,7 @@ properties: - qcom,qcm2290-rpm-proc - qcom,qcs404-rpm-proc - qcom,sdm660-rpm-proc + - qcom,shikra-rpm-proc - qcom,sm6115-rpm-proc - qcom,sm6125-rpm-proc - qcom,sm6375-rpm-proc From 39176cdac9c7206ad4e70f22f134a6984a89be8b Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Thu, 21 May 2026 18:51:37 +0530 Subject: [PATCH 3207/5214] dt-bindings: remoteproc: qcom,shikra-pas: Document Shikra PAS remoteprocs Document the bindings for the CDSP, LPAICP and MPSS PAS on the Shikra SoC. Signed-off-by: Bibek Kumar Patro Signed-off-by: Komal Bajaj Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260521-shikra-rproc-v3-1-2fca0bbe1ad7@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../bindings/remoteproc/qcom,shikra-pas.yaml | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml new file mode 100644 index 000000000000..253b14eb2b59 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,shikra-pas.yaml @@ -0,0 +1,167 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,shikra-pas.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shikra SoC Peripheral Authentication Service + +maintainers: + - Bibek Kumar Patro + - Komal Bajaj + +description: + Qualcomm Shikra SoC Peripheral Authentication Service loads and boots + firmware on the Qualcomm DSP Hexagon cores. + +properties: + compatible: + enum: + - qcom,shikra-cdsp-pas + - qcom,shikra-lpaicp-pas + - qcom,shikra-mpss-pas + + reg: + maxItems: 1 + + clocks: + items: + - description: XO clock + + clock-names: + items: + - const: xo + + memory-region: + minItems: 1 + maxItems: 2 + + smd-edge: false + + firmware-name: + minItems: 1 + items: + - description: Firmware name of the Hexagon core + - description: Firmware name of the Hexagon Devicetree + + glink-edge: + $ref: /schemas/remoteproc/qcom,glink-edge.yaml# + description: + Qualcomm G-Link subnode which represents communication edge, channels + and devices related to the remoteproc core. + unevaluatedProperties: false + + qcom,smem-states: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: States used by the AP to signal the Hexagon core + items: + - description: Stop the remote processor + items: + - description: Phandle to the Shared Memory Point 2 Point device + handling the communication with a remote processor + - description: Single bit index to toggle in the value sent to + the remote processor + maximum: 32 + + qcom,smem-state-names: + description: The names of the state bits used for SMP2P output + items: + - const: stop + +required: + - compatible + - reg + - memory-region + +allOf: + - $ref: /schemas/remoteproc/qcom,pas-common.yaml# + + - if: + properties: + compatible: + enum: + - qcom,shikra-cdsp-pas + - qcom,shikra-mpss-pas + then: + properties: + interrupts: + minItems: 6 + interrupt-names: + minItems: 6 + memory-region: + maxItems: 1 + firmware-name: + maxItems: 1 + power-domains: + items: + - description: CX power domain + power-domain-names: + items: + - const: cx + + - if: + properties: + compatible: + enum: + - qcom,shikra-lpaicp-pas + then: + properties: + interrupts: + maxItems: 5 + interrupt-names: + maxItems: 5 + memory-region: + minItems: 2 + firmware-name: + minItems: 2 + power-domains: false + power-domain-names: false + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + #include + + remoteproc@b300000 { + compatible = "qcom,shikra-cdsp-pas"; + reg = <0x0b300000 0x100000>; + + interrupts-extended = <&intc GIC_SPI 265 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 3 IRQ_TYPE_EDGE_RISING>, + <&cdsp_smp2p_in 7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", "fatal", "ready", + "handover", "stop-ack", "shutdown-ack"; + + clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "xo"; + + interconnects = <&mem_noc MASTER_AMPSS_M0 RPM_ALWAYS_TAG + &mc_virt SLAVE_EBI_CH0 RPM_ALWAYS_TAG>; + + power-domains = <&rpmpd RPMHPD_CX>; + power-domain-names = "cx"; + + memory-region = <&cdsp_mem>; + + qcom,smem-states = <&cdsp_smp2p_out 0>; + qcom,smem-state-names = "stop"; + + firmware-name = "qcom/shikra/cdsp.mbn"; + + glink-edge { + interrupts = ; + mboxes = <&apcs_glb 4>; + qcom,remote-pid = <5>; + label = "cdsp"; + }; + }; From 23dd0092bc150b55a62347ed4814c14fda7a32c2 Mon Sep 17 00:00:00 2001 From: Bibek Kumar Patro Date: Thu, 21 May 2026 18:51:38 +0530 Subject: [PATCH 3208/5214] remoteproc: qcom: pas: Add Shikra remoteproc support Add the CDSP, LPAICP and MPSS Peripheral Authentication Service support for the Qualcomm Shikra SoC. Signed-off-by: Bibek Kumar Patro Reviewed-by: Dmitry Baryshkov Signed-off-by: Komal Bajaj Link: https://lore.kernel.org/r/20260521-shikra-rproc-v3-2-2fca0bbe1ad7@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index da27d1d3c9da..0101f1db6458 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -1457,6 +1457,51 @@ static const struct qcom_pas_data sc7280_wpss_resource = { .ssctl_id = 0x19, }; +static const struct qcom_pas_data shikra_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mbn", + .pas_id = 18, + .minidump_id = 7, + .auto_boot = true, + .proxy_pd_names = (char *[]){ + "cx", + NULL + }, + .load_state = "cdsp", + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, + .smem_host_id = 5, +}; + +static const struct qcom_pas_data shikra_lpaicp_resource = { + .crash_reason_smem = 682, + .firmware_name = "lpaicp.mbn", + .dtb_firmware_name = "lpaicp_dtb.mbn", + .pas_id = 0x56, + .dtb_pas_id = 0x57, + .minidump_id = 0, + .auto_boot = true, + .ssr_name = "lpaicp", + .sysmon_name = "lpaicp", +}; + +static const struct qcom_pas_data shikra_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "qdsp6sw.mbn", + .pas_id = 4, + .minidump_id = 3, + .auto_boot = false, + .proxy_pd_names = (char *[]){ + "cx", + NULL + }, + .load_state = "modem", + .ssr_name = "mpss", + .sysmon_name = "modem", + .ssctl_id = 0x12, +}; + static const struct qcom_pas_data sm8650_cdsp_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", @@ -1571,6 +1616,9 @@ static const struct of_device_id qcom_pas_of_match[] = { { .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init }, { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource }, { .compatible = "qcom,sdx75-mpss-pas", .data = &sm8650_mpss_resource }, + { .compatible = "qcom,shikra-cdsp-pas", .data = &shikra_cdsp_resource }, + { .compatible = "qcom,shikra-lpaicp-pas", .data = &shikra_lpaicp_resource }, + { .compatible = "qcom,shikra-mpss-pas", .data = &shikra_mpss_resource }, { .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,sm6115-mpss-pas", .data = &sc8180x_mpss_resource }, From b5bfc7d039bb775730186a9c38d0f01afd729638 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 May 2026 15:38:36 +0800 Subject: [PATCH 3209/5214] remoteproc: qcom: pas: Drop start/stop completion from struct qcom_pas The completion start_done and stop_done are leftover from commit 6103b1a616ab ("remoteproc: qcom: adsp: Use common q6v5 helpers"). Clean them up. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20260525073836.1579375-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 0101f1db6458..808e9609988d 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -92,9 +92,6 @@ struct qcom_pas { const struct firmware *firmware; const struct firmware *dtb_firmware; - struct completion start_done; - struct completion stop_done; - phys_addr_t mem_phys; phys_addr_t dtb_mem_phys; phys_addr_t mem_reloc; From 0b6573e23acc7bca808e539e3edea49683f106de Mon Sep 17 00:00:00 2001 From: Alexander Egorov Date: Tue, 19 May 2026 18:51:24 +0300 Subject: [PATCH 3210/5214] platform/x86: oxpec: add support for OneXPlayer Super X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OneXPlayer Super X identifies itself via DMI as: board vendor: ONE-NETBOOK board name: ONEXPLAYER SUPER X product name: ONEXPLAYER SUPER X Current mainline oxpec does not contain a matching DMI entry for this system, so the in-tree driver is not auto-loaded. The tested Super X fan, PWM, turbo-toggle, and battery charge-control EC layout matches the existing ONEXPLAYER G1 A handling. Add a DMI match for OneXPlayer Super X and reuse the oxp_g1_a board data. Reviewed-by: Derek J Clark Reviewed-by: Antheas Kapenekakis Signed-off-by: Alexander Egorov Link: https://patch.msgid.link/20260519155124.3240359-1-begeebe@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 6d4a53a2ed60..99c0dfcf393b 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -205,6 +205,13 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_g1_a, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER SUPER X"), + }, + .driver_data = (void *)oxp_g1_a, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), From a221557958e3a82d8565729d445a7385963f30b6 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 21 May 2026 21:08:47 +0800 Subject: [PATCH 3211/5214] platform/x86/intel/tpmi: use cleanup helpers in mem_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mem_write(), the temporary array returned by parse_int_array_user() must be released on all exit paths. Convert the array variable to use cleanup.h scope-based cleanup so it is freed automatically on return. This also moves the array declaration next to parse_int_array_user() as required by cleanup.h usage guidelines. Fixes: 8e0a2fc68ec3 ("platform/x86/intel/tpmi: Use 32 bit aligned address for debugfs mem write") Cc: stable@vger.kernel.org Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260521130848.2860219-1-zhaojinming@uniontech.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec_tpmi.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 8c4e2474b30c..3b76cccb975f 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -473,7 +474,7 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l struct seq_file *m = file->private_data; struct intel_tpmi_pm_feature *pfs = m->private; u32 addr, value, punit, size; - u32 num_elems, *array; + u32 num_elems; void __iomem *mem; int ret; @@ -481,15 +482,14 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!size) return -EIO; + u32 *array __free(kfree) = NULL; ret = parse_int_array_user(userbuf, len, (int **)&array); if (ret < 0) return ret; num_elems = *array; - if (num_elems != 3) { - ret = -EINVAL; - goto exit_write; - } + if (num_elems != 3) + return -EINVAL; punit = array[1]; addr = array[2]; @@ -498,15 +498,11 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!IS_ALIGNED(addr, sizeof(u32))) return -EINVAL; - if (punit >= pfs->pfs_header.num_entries) { - ret = -EINVAL; - goto exit_write; - } + if (punit >= pfs->pfs_header.num_entries) + return -EINVAL; - if (addr >= size) { - ret = -EINVAL; - goto exit_write; - } + if (addr >= size) + return -EINVAL; mutex_lock(&tpmi_dev_lock); @@ -525,9 +521,6 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l unlock_mem_write: mutex_unlock(&tpmi_dev_lock); -exit_write: - kfree(array); - return ret; } From abefbbfc71f5ee50f9e549a2d143f23694d65fc2 Mon Sep 17 00:00:00 2001 From: yahia ahmed Date: Fri, 22 May 2026 23:34:18 +0300 Subject: [PATCH 3212/5214] platform/x86: hp-wmi: Add thermal support for board 8B2F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added thermal support for board ID 8B2F. Signed-off-by: yahia ahmed Link: https://patch.msgid.link/20260522203418.28784-1-yahia.a.abdrabou@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 48292cc02ee2..5042d124dea0 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -205,6 +205,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") }, .driver_data = (void *)&omen_v1_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8B2F") }, + .driver_data = (void *)&victus_s_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") }, .driver_data = (void *)&victus_s_thermal_params, From ceb8678adfd68b33d5e8a52cb2e96cfa90d81761 Mon Sep 17 00:00:00 2001 From: Mike Bommarito Date: Sat, 23 May 2026 07:45:17 -0400 Subject: [PATCH 3213/5214] platform/x86/intel/pmc: rate-limit LTR scale-factor warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit convert_ltr_scale() emits an unconditional pr_warn() whenever an LTR row encoded by hardware has a scale-factor field of 6 or 7 (reserved values per the PCIe LTR ECN). The function is called twice per LTR row (snoop + non-snoop) by pmc_core_ltr_show(), which is invoked on every read of /sys/kernel/debug/pmc_core/ltr_show as well as during certain platform driver activity. On a Meteor Lake laptop with an Intel AX210 Wi-Fi card, this produces 4-12 "Invalid LTR scale factor." lines per second in dmesg, with no context to help identify which PMC IP / row carries the bad value. Switch to pr_warn_once() so the warning still flags the spec violation once per boot, and include the offending scale value in the message. Identifying the originating LTR row would require restructuring the caller's loop to perform the validity check itself; that is left as a separate change. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Bommarito Link: https://patch.msgid.link/20260523114517.101305-1-michael.bommarito@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 3adae8fe8b30..825ba5fa0bcb 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -623,7 +623,8 @@ static u32 convert_ltr_scale(u32 val) * ---------------------------------------------- */ if (val > 5) { - pr_warn("Invalid LTR scale factor.\n"); + pr_warn_once("Invalid LTR scale factor %u (only 0-5 are valid per PCIe spec)\n", + val); return 0; } From 0aab31d47c2e857ca05028d718d1e0d239e683ad Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 25 May 2026 15:52:26 +0530 Subject: [PATCH 3214/5214] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8D26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-ap0xxx (board ID: 8D26) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8D26 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Alberto Escaño Reported-by: Alberto Escaño Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221514 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260525102226.56300-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 5042d124dea0..079db53bf10c 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -253,6 +253,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") }, .driver_data = (void *)&victus_s_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, From bfe91a80b13f8068f6fa07aa8c468d284150d4ad Mon Sep 17 00:00:00 2001 From: Gleb Sonichev Date: Mon, 25 May 2026 13:00:47 +0300 Subject: [PATCH 3215/5214] platform/x86: dell-laptop: add Inspiron N5110 to touchpad LED quirk table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Inspiron N5110 needs the touchpad LED quirk (Vostro V130 quirk) to properly control the touchpad LED. Add its DMI identifier to the existing quirk table, next to the similar Inspiron M5110 entry. Tested on Dell Inspiron N5110. The touchpad LED works correctly with this quirk enabled. Signed-off-by: Gleb Sonichev Acked-by: Pali Rohár Link: https://patch.msgid.link/20260525100047.20046-1-sonichev555@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 57748c3ea24f..7fc3bbb8c4a4 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -222,6 +222,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = { }, .driver_data = &quirk_dell_vostro_v130, }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron N5110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N5110"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, { .callback = dmi_matched, .ident = "Dell Vostro 3360", From ab7be7ed913086e076bfd8aba79f614f415cd6dc Mon Sep 17 00:00:00 2001 From: Luis de Carlos Date: Wed, 27 May 2026 15:47:50 +0200 Subject: [PATCH 3216/5214] platform/x86: msi-ec: Add support for MSI Pulse GL66 12th Gen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the firmware string '1583EMS1.109' to the ALLOWED_FW_10 array. This enables Embedded Controller support, including battery charge thresholds, for the MSI Pulse GL66 12UEK (MS-1583) laptop. Signed-off-by: Luis de Carlos Link: https://patch.msgid.link/20260527134750.25263-1-reskoldo73@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/msi-ec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c index f19504dbf164..0157e233e430 100644 --- a/drivers/platform/x86/msi-ec.c +++ b/drivers/platform/x86/msi-ec.c @@ -823,6 +823,7 @@ static struct msi_ec_conf CONF9 __initdata = { static const char * const ALLOWED_FW_10[] __initconst = { "1582EMS1.107", // GF66 11UC + "1583EMS1.109", // Pulse GL66 12UEK NULL }; From 3e127829e57f5190f612412ece4541cb96d5ec7a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 05:51:35 -0400 Subject: [PATCH 3217/5214] fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation} In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove destination is `Add2Ptr(e, le16_to_cpu(e->view.data_off))`, where e->view.data_off comes from an on-disk NTFS_DE inside an INDEX_ROOT or INDEX_BUFFER. Neither case validates view.data_off + dlen against e->size; the existing check_if_index_root / check_if_alloc_index helpers walk the entry chain and validate the entry's offset, but not its internal view fields. The neighbouring read sites (e.g., fs/ntfs3/index.c when iterating view entries) check view.data_off + view.data_size <= e->size. Apply the same bound at the two memmove sites. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe instrumentation: with view.data_off forced to 0xFFFC, the memmove writes 32 bytes past the end of the NTFS_DE. This is similar in shape to Pavitra Jha's 2026-05-02 patch "fs/ntfs3: prevent oob in case UpdateRecordDataRoot" (<20260502105008.21827-1-jhapavitra98@gmail.com>) which proposes calling ntfs3_bad_de_range(); that helper does not exist in mainline. This patch uses inline checks. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Reported-by: Pavitra Jha Closes: https://lore.kernel.org/ntfs3/20260502105008.21827-1-jhapavitra98@gmail.com/ Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 95e1cdb47475..e1f1c6a26785 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3525,6 +3525,18 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); + /* + * e->view.data_off and dlen come from the on-disk + * INDEX_ROOT entry / LRH. The neighbouring read sites + * (e.g. fs/ntfs3/index.c) check that + * view.data_off + view.data_size <= e->size; mirror that + * bound here so the memmove cannot reach past the entry. + */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); mi->dirty = true; @@ -3731,6 +3743,12 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, goto dirty_vol; } + /* See UpdateRecordDataRoot for the rationale. */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); a_dirty = true; From 9611f644302c07d21bc8af97e3e06a3d30064253 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 19:41:40 -0400 Subject: [PATCH 3218/5214] ntfs3: cap RESTART_TABLE free-chain walker at rt->used A crafted NTFS3 disk image triggers an in-kernel infinite loop at mount time, hanging the mounting thread and firing the soft-lockup watchdog within ~22s on multi-CPU hosts (panic with kernel.softlockup_panic=1). The bug is reachable from desktop USB auto-mount on distributions where udisks2 routes the NTFS signature to the in-tree ntfs3 driver (Arch family and an increasing fraction of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual mount elsewhere. check_rstbl()'s second walker iterates the free-entry singly-linked list headed by rt->first_free with no upper bound on iteration count: for (off = ff; off;) { if (off == RESTART_ENTRY_ALLOCATED) return false; off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); if (off > ts - sizeof(__le32)) return false; } The existing guards cover three exits: end-of-list (off == 0), the in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds (off > ts - sizeof(__le32)). None of the three prevents an in-bounds cycle. A crafted on-disk RESTART_TABLE whose free chain contains a self-loop or A->B->A cycle whose offsets satisfy: - in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)] - (off - sizeof(struct RESTART_TABLE)) % rsize == 0 passes all existing guards and spins the mount-time thread forever. Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18 stores 0x18 as its next pointer; mount of the forged image with the in-tree ntfs3 driver never returns. Bound the walker by rt->used. Each entry on a legitimate free chain is unique, and the total slot count is ne = le16_to_cpu (rt->used). A traversal that visits more than ne slots is by construction malformed; reject it as a corrupt RESTART_TABLE. After this patch, mount of the forged image returns with -EINVAL and a log_replay failure message, and mkntfs-produced legitimate images mount cleanly (verified in the same UML harness). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index e1f1c6a26785..e5ff99a2e6a2 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -764,8 +764,19 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) /* * Walk through the list headed by the first entry to make * sure none of the entries are currently being used. + * + * Bound traversal by ne (rt->used) to defeat a crafted on-disk + * cycle in the free chain. Each entry in a legitimate free + * list is unique, so a chain that visits more than ne slots + * is malformed. Without this guard, an attacker-controlled + * RESTART_TABLE with a self-loop or A->B->A cycle whose + * offsets satisfy the existing alignment + in-bounds guards + * spins forever at mount time. */ - for (off = ff; off;) { + for (off = ff, i = 0; off; i++) { + if (i > ne) + return false; + if (off == RESTART_ENTRY_ALLOCATED) return false; From 56b7981c6f21670c0a1a62e6d2f9afb380e2596d Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 8 Jun 2026 19:12:55 +0530 Subject: [PATCH 3219/5214] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8E35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-ap0xxx (board ID: 8E35) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8E35 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Ahmet Öztürk Reported-by: Ahmet Öztürk Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221523 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260608134255.36280-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 079db53bf10c..8ba286ed8721 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -265,6 +265,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, {}, }; From 3b9f95b5a45786f1ca3feff7a736f30f60af08c7 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 8 Jun 2026 16:23:48 +0800 Subject: [PATCH 3220/5214] platform: arm64: qcom-hamoa-ec: Fix indentation in comment tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With tab=8 (Linux Kernel coding style), there are a couple of lines misaligned in the comment ASCII tables. Fix indentation for them. Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260608082348.92575-1-shengchao.guo@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/arm64/qcom-hamoa-ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c index a018f7bf35d2..89374ab3e600 100644 --- a/drivers/platform/arm64/qcom-hamoa-ec.c +++ b/drivers/platform/arm64/qcom-hamoa-ec.c @@ -149,7 +149,7 @@ static int qcom_ec_read_fw_version(struct device *dev) * | 0x00 | Byte count | Number of bytes in response | * | | | (excluding byte count) | * ------------------------------------------------------------------------------ - * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | + * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | * | 0x03 | Capabilities | Bit 2-4: Type of fan | * | | | Bit 5-6: Reserved | * | | | Bit 7: Data Valid/Invalid | @@ -273,7 +273,7 @@ static int qcom_ec_fan_get_cur_state(struct thermal_cooling_device *cdev, unsign * | | | Bit 1: Fan On/Off (0 - Off, 1 - ON) | * | | | Bit 2: Debug Type (0 - RPM, 1 - PWM) | * -------------------------------------------------------------------------------------- - * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | + * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | * | 0x05 | | | * -------------------------------------------------------------------------------------- * | 0x06 | Speed in PWM | PWM value, if mode selected is PWM (0 - 255) | From 6736b1801908acfa64ef2b651c5bb78389a8a4c6 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 21 May 2026 21:08:48 +0800 Subject: [PATCH 3221/5214] platform/x86/intel/tpmi: convert mutex in mem_write() to guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the explicit mutex_lock/mutex_unlock pair in mem_write() into a cleanup.h guard(mutex)() scope-based lock acquisition. This removes the remaining goto-based cleanup path and keeps the lock held until the end of the mem_write() scope. Suggested-by: Ilpo Järvinen Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260521130848.2860219-2-zhaojinming@uniontech.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec_tpmi.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 3b76cccb975f..0153dd57838e 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -504,24 +504,17 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (addr >= size) return -EINVAL; - mutex_lock(&tpmi_dev_lock); + guard(mutex)(&tpmi_dev_lock); mem = ioremap(pfs->vsec_offset + punit * size, size); - if (!mem) { - ret = -ENOMEM; - goto unlock_mem_write; - } + if (!mem) + return -ENOMEM; writel(value, mem + addr); iounmap(mem); - ret = len; - -unlock_mem_write: - mutex_unlock(&tpmi_dev_lock); - - return ret; + return len; } static int mem_write_show(struct seq_file *s, void *unused) From 097cdfd0a55df5af82c9753833f39a8bfadbcfcb Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:14 +0900 Subject: [PATCH 3222/5214] ntfs: reject non-resident records for resident-only attributes The shared lookup-time attribute validator rejects non-resident $FILE_NAME and $VOLUME_NAME records because their formats require resident values and callers handle returned records as resident attributes. Other resident-only attribute types still pass through the generic non-resident mapping-pairs checks. That leaves real resident/non-resident union confusion paths. Inode load looks up $STANDARD_INFORMATION and then reads data.resident.value_offset without checking a->non_resident. ntfs_inode_sync_standard_information() does the same when updating the standard information value. ntfs_write_volume_flags() also looks up $VOLUME_INFORMATION and reads data.resident.value_offset directly. $INDEX_ROOT callers in dir.c and index.c depend on the same lookup contract before consuming the resident index root value. Reject non-resident records for all resident-only attribute types in the shared validator. Keep the existing $FILE_NAME and $VOLUME_NAME behavior, but factor it through a helper and extend it to $STANDARD_INFORMATION, $OBJECT_ID, $VOLUME_INFORMATION, $INDEX_ROOT, and $EA_INFORMATION. For $OBJECT_ID and $EA_INFORMATION this is contract hardening for resident-only formats; this patch only rejects the non-resident form and does not add new resident value validation for those types. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 1e0076ef81ef..1cd6664567ce 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -596,6 +596,22 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) } } +static bool ntfs_attr_type_is_resident_only(const __le32 type) +{ + switch (type) { + case AT_STANDARD_INFORMATION: + case AT_FILE_NAME: + case AT_OBJECT_ID: + case AT_VOLUME_NAME: + case AT_VOLUME_INFORMATION: + case AT_INDEX_ROOT: + case AT_EA_INFORMATION: + return true; + default: + return false; + } +} + static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length) { const struct file_name_attr *fn; @@ -666,7 +682,7 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, u32 min_len; if (a->non_resident) { - if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME) + if (ntfs_attr_type_is_resident_only(a->type)) goto corrupt; if (!ntfs_non_resident_attr_value_is_valid(a)) goto corrupt; From 0bb508fb3b97e4802ec727fd2af4d608f65dd190 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:15 +0900 Subject: [PATCH 3223/5214] ntfs: grow index root value before reparent header update ntfs_ir_reparent() moves the resident index root entries into an index block and leaves a small root stub containing the child VCN. That root stub can be larger than the existing resident value. For example, an empty root with value_length 48 has an index area of 32 bytes, while the large-index root stub needs index_length and allocated_size of 40 bytes. The current code publishes the larger index.index_length and index.allocated_size before resizing the resident value. If the resize returns -ENOSPC, the recovery path can call ntfs_inode_add_attrlist(), which looks attributes up again while the root header says allocated_size 40 but the resident value still only provides 32 bytes of index area. Lookup-time $INDEX_ROOT validation then correctly rejects that transient layout as corrupt. This reproduces as a generic/013 failure under qemu. In the failing run, the transient root had value_len=48, index_size=32, index_length=40, and allocated_size=40, and ntfsprogs-plus ntfsck reported "Corrupt index root in MFT record 1177". When the root stub grows, resize the resident value before publishing the larger root header. If the resize fails, the old root remains valid for recovery lookups. Keep the existing header-before-resize ordering for shrink or same-size cases so the resident value never temporarily exposes an allocated_size beyond its bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/index.c | 80 ++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 8371ff4303e7..052d80fddbbc 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -1240,6 +1240,8 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) struct index_entry *ie; struct index_block *ib = NULL; s64 new_ib_vcn; + u32 index_length; + u32 old_value_length; int ix_root_size; int ret = 0; @@ -1287,6 +1289,21 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) goto clear_bmp; } + old_value_length = le32_to_cpu(ctx->attr->data.resident.value_length); + index_length = le32_to_cpu(ir->index.entries_offset) + + sizeof(struct index_entry_header) + sizeof(s64); + ix_root_size = offsetof(struct index_root, index) + index_length; + /* Grow the resident value before publishing the larger root header. */ + if (ix_root_size > old_value_length) { + ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); + if (ret) + goto resize_failed; + + icx->idx_ni->data_size = ix_root_size; + icx->idx_ni->initialized_size = ix_root_size; + icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; + } + ntfs_ir_nill(ir); ie = ntfs_ie_get_first(&ir->index); @@ -1295,48 +1312,49 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) ir->index.flags = LARGE_INDEX; NInoSetIndexAllocPresent(icx->idx_ni); - ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + - le16_to_cpu(ie->length)); + ir->index.index_length = cpu_to_le32(index_length); ir->index.allocated_size = ir->index.index_length; - ix_root_size = sizeof(struct index_root) - sizeof(struct index_header) + - le32_to_cpu(ir->index.allocated_size); - ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); - if (ret) { - /* - * When there is no space to build a non-resident - * index, we may have to move the root to an extent - */ - if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) { - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); - if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - - le32_to_cpu(ctx->attr->data.resident.value_length))) { - if (ntfs_attrlist_update(ctx->base_ntfs_ino ? - ctx->base_ntfs_ino : ctx->ntfs_ino)) - goto clear_bmp; - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - goto retry; - } - } - goto clear_bmp; - } else { - icx->idx_ni->data_size = icx->idx_ni->initialized_size = ix_root_size; - icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; + if (ix_root_size <= old_value_length) { + ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); + if (ret) + goto resize_failed; + + icx->idx_ni->data_size = ix_root_size; + icx->idx_ni->initialized_size = ix_root_size; + icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; } ntfs_ie_set_vcn(ie, new_ib_vcn); + goto err_out; +resize_failed: + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); + if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - + le32_to_cpu(ctx->attr->data.resident.value_length))) { + if (ntfs_attrlist_update(ctx->base_ntfs_ino ? + ctx->base_ntfs_ino : ctx->ntfs_ino)) + goto clear_bmp; + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto retry; + } + } +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; err_out: kvfree(ib); if (ctx) ntfs_attr_put_search_ctx(ctx); out: return ret; -clear_bmp: - ntfs_ibm_clear(icx, new_ib_vcn); - goto err_out; } /* From e782ca90ceb798bb1811b214bf814216f11aae6a Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:16 +0900 Subject: [PATCH 3224/5214] ntfs: update index root allocated size before shrink ntfs_ir_truncate() currently shrinks the resident $INDEX_ROOT value first and only updates index.allocated_size after re-looking up the attribute. During that relookup, the resident value_length can already be smaller while index.allocated_size still contains the old larger size. That leaves a transiently inconsistent $INDEX_ROOT layout and prevents lookup-time $INDEX_ROOT validation from being enabled: validation can correctly reject allocated_size extending past the newly shrunk resident value. When shrinking, lower index.allocated_size before shrinking value_length. If the truncate fails, restore the old allocated_size. Keep the existing grow ordering because the old allocated_size remains within the enlarged resident value until it is updated after the relookup. The shrink path is safe because the new value_length still covers struct index_root, so the index.allocated_size field remains present while it is updated first. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/index.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 052d80fddbbc..c5f2cf75b750 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -1365,9 +1365,16 @@ static int ntfs_ir_reparent(struct ntfs_index_context *icx) static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size) { int ret; + u32 old_allocated_size; + bool shrink; ntfs_debug("Entering\n"); + old_allocated_size = le32_to_cpu(icx->ir->index.allocated_size); + shrink = data_size < old_allocated_size; + if (shrink) + icx->ir->index.allocated_size = cpu_to_le32(data_size); + /* * INDEX_ROOT must be resident and its entries can be moved to * struct index_block, so ENOSPC isn't a real error. @@ -1379,9 +1386,14 @@ static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size) if (!icx->ir) return -ENOENT; - icx->ir->index.allocated_size = cpu_to_le32(data_size); - } else if (ret != -ENOSPC) - ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT"); + if (!shrink) + icx->ir->index.allocated_size = cpu_to_le32(data_size); + } else { + if (shrink) + icx->ir->index.allocated_size = cpu_to_le32(old_allocated_size); + if (ret != -ENOSPC) + ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT"); + } return ret; } From fcf5bf0e8570798970e3ae8c95d04765ba2c5b97 Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 9 Jun 2026 00:49:17 +0900 Subject: [PATCH 3225/5214] ntfs: validate resident index root values on lookup Resident $INDEX_ROOT values carry index header fields that callers consume after lookup. Some callers already validate parts of the layout before walking entries, but those checks are scattered and do not cover all root header invariants, such as entries_offset alignment and lower bound, index_length, and allocated_size consistency. The resident root resize paths now keep these header fields consistent while the value size changes: ntfs_ir_truncate() lowers index.allocated_size before shrinking the resident value, and ntfs_ir_reparent() grows the resident value before publishing a larger root header. Lookup-time validation can therefore cover these invariants without tripping over the driver's own resize paths. Add $INDEX_ROOT to the minimum resident value size table and validate the resident index header fields before returning the attribute from lookup. Require 8-byte aligned index header fields, a sane entries_offset, an index_length within allocated_size, allocated_size within the resident value, and enough entry space for at least an index entry header. The shared validator already rejects non-resident records for resident-only attribute types, including $INDEX_ROOT. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang Reviewed-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 1cd6664567ce..dd8828098511 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -589,6 +589,8 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type) sizeof(__le16) * 1; case AT_VOLUME_INFORMATION: return sizeof(struct volume_information); + case AT_INDEX_ROOT: + return sizeof(struct index_root); case AT_EA_INFORMATION: return sizeof(struct ea_information); default: @@ -632,6 +634,31 @@ static bool ntfs_volume_name_attr_value_is_valid(const u32 value_length) return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16); } +static bool ntfs_index_root_attr_value_is_valid(const u8 *value, const u32 value_length) +{ + const struct index_root *ir; + u32 index_size; + u32 entries_offset; + u32 index_length; + u32 allocated_size; + + ir = (const struct index_root *)value; + index_size = value_length - offsetof(struct index_root, index); + entries_offset = le32_to_cpu(ir->index.entries_offset); + index_length = le32_to_cpu(ir->index.index_length); + allocated_size = le32_to_cpu(ir->index.allocated_size); + + if ((entries_offset | index_length | allocated_size) & 7 || + entries_offset < sizeof(struct index_header) || + entries_offset > index_length || + index_length > allocated_size || + allocated_size > index_size || + index_length - entries_offset < sizeof(struct index_entry_header)) + return false; + + return true; +} + struct ntfs_resident_attr_value { const u8 *data; u32 len; @@ -705,6 +732,10 @@ static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, if (!ntfs_volume_name_attr_value_is_valid(value.len)) goto corrupt; break; + case AT_INDEX_ROOT: + if (!ntfs_index_root_attr_value_is_valid(value.data, value.len)) + goto corrupt; + break; } return true; From 5aec1efb11ab2a87d1e4be063830268f7980ec4a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 9 Jun 2026 21:16:50 +0900 Subject: [PATCH 3226/5214] ntfs: use direct pointer for inline data to avoid redundant allocation Previously, NTFS used page allocation for IOMAP_INLINE to ensure that the inline_data pointer was page-aligned, avoiding strict boundary checks in the iomap core. Since the previous patch has removed the over-strict PAGE_SIZE boundary check in iomap, NTFS can now safely point iomap::inline_data directly to the MFT record. This change eliminates redundant memory allocations and memcpy operations in both read and write paths. It also simplifies the iomap_ops by removing the need for a iomap_end callback that was previously used to free the temporary page. Reviewed-by: Christoph Hellwig Signed-off-by: Namjae Jeon --- fs/ntfs/iomap.c | 77 +++++-------------------------------------------- 1 file changed, 7 insertions(+), 70 deletions(-) diff --git a/fs/ntfs/iomap.c b/fs/ntfs/iomap.c index dc7d8c893a69..52eecf5cb256 100644 --- a/fs/ntfs/iomap.c +++ b/fs/ntfs/iomap.c @@ -89,7 +89,6 @@ static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, lo u32 attr_len; int err = 0; char *kattr; - struct page *ipage; if (NInoAttr(ni)) base_ni = ni->ext.base_ntfs_ino; @@ -130,18 +129,10 @@ static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, lo kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); - ipage = alloc_page(GFP_NOFS | __GFP_ZERO); - if (!ipage) { - err = -ENOMEM; - goto out; - } - - memcpy(page_address(ipage), kattr, attr_len); iomap->type = IOMAP_INLINE; - iomap->inline_data = page_address(ipage); + iomap->inline_data = kattr; iomap->offset = 0; iomap->length = attr_len; - iomap->private = ipage; out: if (ctx) @@ -286,21 +277,8 @@ static int ntfs_read_iomap_begin(struct inode *inode, loff_t offset, loff_t leng srcmap, true); } -static int ntfs_read_iomap_end(struct inode *inode, loff_t pos, loff_t length, - ssize_t written, unsigned int flags, struct iomap *iomap) -{ - if (iomap->type == IOMAP_INLINE) { - struct page *ipage = iomap->private; - - put_page(ipage); - } - - return written; -} - const struct iomap_ops ntfs_read_iomap_ops = { .iomap_begin = ntfs_read_iomap_begin, - .iomap_end = ntfs_read_iomap_end, }; /* @@ -358,7 +336,6 @@ static const struct iomap_ops ntfs_zero_read_iomap_ops = { const struct iomap_ops ntfs_seek_iomap_ops = { .iomap_begin = ntfs_seek_iomap_begin, - .iomap_end = ntfs_read_iomap_end, }; int ntfs_dio_zero_range(struct inode *inode, loff_t offset, loff_t length) @@ -659,7 +636,6 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset, u32 attr_len; int err = 0; char *kattr; - struct page *ipage; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { @@ -680,24 +656,18 @@ static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset, attr_len = le32_to_cpu(a->data.resident.value_length); kattr = (u8 *)a + le16_to_cpu(a->data.resident.value_offset); - ipage = alloc_page(GFP_NOFS | __GFP_ZERO); - if (!ipage) { - err = -ENOMEM; - goto out; - } - - memcpy(page_address(ipage), kattr, attr_len); iomap->type = IOMAP_INLINE; - iomap->inline_data = page_address(ipage); + iomap->inline_data = kattr; iomap->offset = 0; - /* iomap requires there is only one INLINE_DATA extent */ iomap->length = attr_len; - iomap->private = ipage; out: if (ctx) ntfs_attr_put_search_ctx(ctx); - mutex_unlock(&ni->mrec_lock); + + if (err) + mutex_unlock(&ni->mrec_lock); + return err; } @@ -778,43 +748,10 @@ static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos, unsigned int flags, struct iomap *iomap) { struct ntfs_inode *ni = NTFS_I(inode); - struct ntfs_attr_search_ctx *ctx; - u32 attr_len; - int err; - char *kattr; - struct page *ipage = iomap->private; - mutex_lock(&ni->mrec_lock); - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - written = -ENOMEM; - goto err_out; - } - - err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx); - if (err) { - if (err == -ENOENT) - err = -EIO; - written = err; - goto err_out; - } - - /* The total length of the attribute value. */ - attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); - if (pos >= attr_len || pos + written > attr_len) - goto err_out; - - kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); - memcpy(kattr + pos, iomap_inline_data(iomap, pos), written); - mark_mft_record_dirty(ctx->ntfs_ino); -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - put_page(ipage); + mark_mft_record_dirty(ni); mutex_unlock(&ni->mrec_lock); return written; - } static int ntfs_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, From 2b40d72de9354a76f5e3bb71230a4210eaa92849 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:31 +0100 Subject: [PATCH 3227/5214] pwm: rzg2l-gpt: Fix period_ticks type from u32 to u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit period_ticks is used to store PWM period values that can exceed the 32-bit range, so change its type from u32 to u64 to prevent overflow. Cc: stable@kernel.org Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-2-biju.das.jz@bp.renesas.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-rzg2l-gpt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 4856af080e8e..c9dfa59bc1ea 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -81,7 +81,7 @@ struct rzg2l_gpt_chip { void __iomem *mmio; struct mutex lock; /* lock to protect shared channel resources */ unsigned long rate_khz; - u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u64 period_ticks[RZG2L_MAX_HW_CHANNELS]; u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; }; From 282305d7e9c0e27fd8b4df34b7cd5506a1eccdd6 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 May 2026 20:55:52 -0400 Subject: [PATCH 3228/5214] PCI: mediatek: Fix operator precedence in PCIE_FTS_NUM_L0 macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original PCIE_FTS_NUM_L0(x) macro was buggy due to improper operator precedence, where ((x) & 0xff << 8) was evaluated as ((x) & 0xff00). Instead of just fixing the parentheses, use the standard FIELD_PREP() macro. This makes the code more robust by automatically handling masks and shifts, while also adding compile-time type and range checking to ensure the value fits within PCIE_FTS_NUM_MASK. Fixes: 637cfacae96f ("PCI: mediatek: Add MediaTek PCIe host controller support") Signed-off-by: Li RongQing [mani: added the bitfield header include spotted by Sashiko] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260515005552.2343-1-lirongqing@baidu.com --- drivers/pci/controller/pcie-mediatek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 75722524fe74..dcfc7408340f 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -7,6 +7,7 @@ * Honghui Zhang */ +#include #include #include #include @@ -61,7 +62,7 @@ /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c #define PCIE_FTS_NUM_MASK GENMASK(15, 8) -#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) +#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x) #define PCIE_FC_CREDIT 0x73c #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) From 898ab0f30e008e411ce93ddf81c4099abd9d4e46 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:34 +0100 Subject: [PATCH 3229/5214] pwm: rzg2l-gpt: Add missing newlines to dev_err_probe() messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev_err_probe() internally calls dev_err() which uses pr_fmt() and printk(). Kernel log messages should end with a newline character to ensure proper log formatting. Add missing '\n' at the end of the error strings in rzg2l_gpt_probe(). Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-5-biju.das.jz@bp.renesas.com Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-rzg2l-gpt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index c9dfa59bc1ea..dfa1d11a48a8 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -408,14 +408,14 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) rate = clk_get_rate(clk); if (!rate) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0\n"); /* * Refuse clk rates > 1 GHz to prevent overflow later for computing * period and duty cycle. */ if (rate > NSEC_PER_SEC) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz\n"); /* * Rate is in MHz and is always integer for peripheral clk @@ -424,7 +424,7 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) */ rzg2l_gpt->rate_khz = rate / KILO; if (rzg2l_gpt->rate_khz * KILO != rate) - return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000\n"); mutex_init(&rzg2l_gpt->lock); From 286db45fb7ef5fe950a60a47c963156d6ece356f Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 16 May 2026 23:36:55 +0800 Subject: [PATCH 3230/5214] PCI: Add common TLP type macros and convert aspeed/mediatek Introduce a set of unified TLP type macros in pci.h according to PCIe spec r7.0, sec 2.2.1: - PCIE_TLP_TYPE_MEM_RDWR (0x00) for Memory Read/Write - PCIE_TLP_TYPE_IO_RDWR (0x02) for I/O Read/Write - PCIE_TLP_TYPE_CFG0_RDWR (0x04) for Type 0 Config Read/Write - PCIE_TLP_TYPE_CFG1_RDWR (0x05) for Type 1 Config Read/Write - PCIE_TLP_TYPE_MSG (0x10) for Message Request (routing to RC) These replace the old per-driver hardcoded values or local macros, and also replace the previous PCIE_TLP_TYPE_CFG0_RD/WR and PCIE_TLP_TYPE_CFG1_RD/WR definitions which had identical numeric values. The read/write distinction is already handled by the TLP Format field (Fmt), so a single type macro suffices. Convert the aspeed and mediatek drivers to use the new macros, and remove the obsolete definitions from pci.h. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260516153657.65214-2-18255117159@163.com --- drivers/pci/controller/pcie-aspeed.c | 8 ++++---- drivers/pci/controller/pcie-mediatek.c | 8 ++------ drivers/pci/pci.h | 9 +++++---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c index 6acfae7d026e..9aa9e14c6148 100644 --- a/drivers/pci/controller/pcie-aspeed.c +++ b/drivers/pci/controller/pcie-aspeed.c @@ -127,19 +127,19 @@ #define CFG0_READ_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ - PCIE_TLP_TYPE_CFG0_RD)) + PCIE_TLP_TYPE_CFG0_RDWR)) #define CFG0_WRITE_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ - PCIE_TLP_TYPE_CFG0_WR)) + PCIE_TLP_TYPE_CFG0_RDWR)) #define CFG1_READ_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ - PCIE_TLP_TYPE_CFG1_RD)) + PCIE_TLP_TYPE_CFG1_RDWR)) #define CFG1_WRITE_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ - PCIE_TLP_TYPE_CFG1_WR)) + PCIE_TLP_TYPE_CFG1_RDWR)) #define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */ #define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4))) #define TLP_GET_VALUE(x, y, z) \ diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 75722524fe74..fda4658ba9f1 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -111,10 +111,6 @@ #define APP_CFG_REQ BIT(0) #define APP_CPL_STATUS GENMASK(7, 5) -#define CFG_WRRD_TYPE_0 4 -#define CFG_WR_FMT 2 -#define CFG_RD_FMT 0 - #define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0)) #define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24)) #define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29)) @@ -295,7 +291,7 @@ static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, u32 tmp; /* Write PCIe configuration transaction header for Cfgrd */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT), + writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_NO_DATA), port->base + PCIE_CFG_HEADER0); writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), @@ -325,7 +321,7 @@ static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, int where, int size, u32 val) { /* Write PCIe configuration transaction header for Cfgwr */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT), + writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_DATA), port->base + PCIE_CFG_HEADER0); writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4a14f88e543a..23ad51e4c434 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -71,10 +71,11 @@ struct pcie_tlp_log; #define PCIE_TLP_FMT_4DW_DATA 0x03 /* 4DW header, with data */ /* Type of TLP; PCIe r7.0, sec 2.2.1 */ -#define PCIE_TLP_TYPE_CFG0_RD 0x04 /* Config Type 0 Read Request */ -#define PCIE_TLP_TYPE_CFG0_WR 0x04 /* Config Type 0 Write Request */ -#define PCIE_TLP_TYPE_CFG1_RD 0x05 /* Config Type 1 Read Request */ -#define PCIE_TLP_TYPE_CFG1_WR 0x05 /* Config Type 1 Write Request */ +#define PCIE_TLP_TYPE_MEM_RDWR 0x00 /* Memory Read/Write Request */ +#define PCIE_TLP_TYPE_IO_RDWR 0x02 /* I/O Read/Write Request */ +#define PCIE_TLP_TYPE_CFG0_RDWR 0x04 /* Config Type 0 Read/Write Request */ +#define PCIE_TLP_TYPE_CFG1_RDWR 0x05 /* Config Type 1 Read/Write Request */ +#define PCIE_TLP_TYPE_MSG 0x10 /* Message With/Without data Request */ /* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */ #define PCIE_MSG_TYPE_R_RC 0 From 3136184508d2889a11092dbaa097ed6580bc4214 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 16 May 2026 23:36:56 +0800 Subject: [PATCH 3231/5214] PCI: dwc: Replace ATU type macros with common TLP type macros The dwc driver defines its own ATU type macros (PCIE_ATU_TYPE_MEM, PCIE_ATU_TYPE_IO, PCIE_ATU_TYPE_CFG0, PCIE_ATU_TYPE_CFG1, PCIE_ATU_TYPE_MSG) with the same numerical values as the newly introduced common TLP type macros. Remove the local definitions and switch all DWC users to the common PCIE_TLP_TYPE_* macros. This eliminates redundancy and improves consistency across PCI controller drivers. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260516153657.65214-3-18255117159@163.com --- .../pci/controller/dwc/pcie-designware-ep.c | 6 +++--- .../pci/controller/dwc/pcie-designware-host.c | 20 +++++++++---------- drivers/pci/controller/dwc/pcie-designware.c | 2 +- drivers/pci/controller/dwc/pcie-designware.h | 5 ----- .../pci/controller/dwc/pcie-tegra194-acpi.c | 4 ++-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index d4dc3b24da60..461f7fc62e85 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -584,9 +584,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, config_atu: if (!(flags & PCI_BASE_ADDRESS_SPACE)) - type = PCIE_ATU_TYPE_MEM; + type = PCIE_TLP_TYPE_MEM_RDWR; else - type = PCIE_ATU_TYPE_IO; + type = PCIE_TLP_TYPE_IO_RDWR; if (epf_bar->num_submap) ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); @@ -659,7 +659,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie_ob_atu_cfg atu = { 0 }; atu.func_no = func_no; - atu.type = PCIE_ATU_TYPE_MEM; + atu.type = PCIE_TLP_TYPE_MEM_RDWR; atu.parent_bus_addr = addr - pci->parent_bus_offset; atu.pci_addr = pci_addr; atu.size = size; diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c9517a348836..b0ff421dbb5b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -434,7 +434,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) * remaining buses need type 1 iATU configuration. */ atu.index = 0; - atu.type = PCIE_ATU_TYPE_CFG0; + atu.type = PCIE_TLP_TYPE_CFG0_RDWR; atu.parent_bus_addr = pp->cfg0_base + SZ_1M; /* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */ atu.size = SZ_1M; @@ -450,7 +450,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) /* Configure remaining buses in type 1 iATU configuration */ atu.index = 1; - atu.type = PCIE_ATU_TYPE_CFG1; + atu.type = PCIE_TLP_TYPE_CFG1_RDWR; atu.parent_bus_addr = pp->cfg0_base + SZ_2M; atu.size = (SZ_1M * bus_range_max) - SZ_2M; atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; @@ -745,9 +745,9 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, PCIE_ATU_FUNC(PCI_FUNC(devfn)); if (pci_is_root_bus(bus->parent)) - type = PCIE_ATU_TYPE_CFG0; + type = PCIE_TLP_TYPE_CFG0_RDWR; else - type = PCIE_ATU_TYPE_CFG1; + type = PCIE_TLP_TYPE_CFG1_RDWR; atu.type = type; atu.parent_bus_addr = pp->cfg0_base - pci->parent_bus_offset; @@ -774,7 +774,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -800,7 +800,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -908,7 +908,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (resource_type(entry->res) != IORESOURCE_MEM) continue; - atu.type = PCIE_ATU_TYPE_MEM; + atu.type = PCIE_TLP_TYPE_MEM_RDWR; atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; @@ -951,7 +951,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pp->io_size) { if (ob_iatu_index < pci->num_ob_windows) { atu.index = ob_iatu_index; - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -1013,7 +1013,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) window_size = MIN(pci->region_limit + 1, res_size); ret = dw_pcie_prog_inbound_atu(pci, ib_iatu_index, - PCIE_ATU_TYPE_MEM, res_start, + PCIE_TLP_TYPE_MEM_RDWR, res_start, res_start - entry->offset, window_size); if (ret) { dev_err(pci->dev, "Failed to set DMA range %pr\n", @@ -1194,7 +1194,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) atu.code = PCIE_MSG_CODE_PME_TURN_OFF; atu.routing = PCIE_MSG_TYPE_R_BC; - atu.type = PCIE_ATU_TYPE_MSG; + atu.type = PCIE_TLP_TYPE_MSG; atu.size = resource_size(pci->pp.msg_res); atu.index = pci->pp.msg_atu_index; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..813f4baa7c62 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -568,7 +568,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); val = PCIE_ATU_ENABLE | atu->ctrl2; - if (atu->type == PCIE_ATU_TYPE_MSG) { + if (atu->type == PCIE_TLP_TYPE_MSG) { /* The data-less messages only for now */ val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..d8d83156fb9d 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -170,11 +170,6 @@ #define PCIE_ATU_VIEWPORT_SIZE 0x2C #define PCIE_ATU_REGION_CTRL1 0x000 #define PCIE_ATU_INCREASE_REGION_SIZE BIT(13) -#define PCIE_ATU_TYPE_MEM 0x0 -#define PCIE_ATU_TYPE_IO 0x2 -#define PCIE_ATU_TYPE_CFG0 0x4 -#define PCIE_ATU_TYPE_CFG1 0x5 -#define PCIE_ATU_TYPE_MSG 0x10 #define PCIE_ATU_TD BIT(8) #define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20) #define PCIE_ATU_REGION_CTRL2 0x004 diff --git a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c index 55f61914a986..2d737b49ea8f 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c +++ b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c @@ -86,11 +86,11 @@ static void __iomem *tegra194_map_bus(struct pci_bus *bus, if (bus->parent->number == cfg->busr.start) { if (PCI_SLOT(devfn) == 0) - type = PCIE_ATU_TYPE_CFG0; + type = PCIE_TLP_TYPE_CFG0_RDWR; else return NULL; } else { - type = PCIE_ATU_TYPE_CFG1; + type = PCIE_TLP_TYPE_CFG1_RDWR; } program_outbound_atu(pcie_ecam, 0, type, cfg->res.start, busdev, From 52015335e5db7e9b0d62d1ee1178de1b601ac7ba Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 16 May 2026 23:36:57 +0800 Subject: [PATCH 3232/5214] PCI: cadence: Use common TLP type macros The Cadence HPA driver uses hardcoded constants (0x0, 0x2, 0x4, 0x5, 0x10) to program the outbound region type. Replace them with the newly introduced common TLP type macros from pci.h for better readability and maintainability. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260516153657.65214-4-18255117159@163.com --- .../pci/controller/cadence/pcie-cadence-hpa-regs.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h index 026e131600de..72299121fd44 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h +++ b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h @@ -14,6 +14,8 @@ #include #include +#include "../../pci.h" + /* High Performance Architecture (HPA) PCIe controller registers */ #define CDNS_PCIE_HPA_IP_REG_BANK 0x01000000 #define CDNS_PCIE_HPA_IP_CFG_CTRL_REG_BANK 0x01003C00 @@ -119,15 +121,15 @@ #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r) (0x1008 + ((r) & 0x1F) * 0x0080) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(28, 24) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x0) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MEM_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x2) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_IO_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x4) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG0_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x5) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG1_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x10) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MSG) /* Region r Outbound PCIe Descriptor Register */ #define CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r) (0x100C + ((r) & 0x1F) * 0x0080) From 9f22b92259bb5ac43e2b9007103787d4418fec56 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Fri, 22 May 2026 09:06:46 +0200 Subject: [PATCH 3233/5214] PCI: Avoid FLR for MediaTek MT7925 WiFi The MediaTek MT7925 WiFi device advertises FLR capability, but it does not work correctly. This manifests in VFIO passthrough scenarios. Normal VM operation works fine, including clean shutdown/reboot. However, when the VM terminates uncleanly (crash, force-off), VFIO attempts to reset the device before it can be assigned to another VM. Because FLR is broken, the reset fails, preventing reuse. This is similar to its predecessor MT7922 (see 81f64e925c29 ("PCI: Avoid FLR for Mediatek MT7922 WiFi")), but with different symptoms. The MT7922 issue manifests as config read failures (returning ~0) after FLR. The MT7925 shows different behavior: config reads work correctly after FLR, but firmware communication fails. First VM start with MT7925 works fine: mt7925e 0000:08:00.0: ASIC revision: 79250000 mt7925e 0000:08:00.0: WM Firmware Version: ____000000, Build Time: 20260106153120 After force reset or VM crash, when VFIO attempts FLR to reset the device for reassignment, firmware initialization fails: mt7925e 0000:08:00.0: ASIC revision: 79250000 mt7925e 0000:08:00.0: Message 00000010 (seq 1) timeout mt7925e 0000:08:00.0: Failed to get patch semaphore [Repeats with increasing sequence numbers 2-10] mt7925e 0000:08:00.0: hardware init failed The driver cannot acquire the patch semaphore needed for firmware initialization, indicating that FLR does not properly reset the firmware state. The device remains in this broken state until physical power cycle. Disable FLR for MT7925 so the PCI core falls back to other reset methods, e.g., Secondary Bus Reset, which successfully resets the device and allows reinitialization for VFIO passthrough reuse. Signed-off-by: Jose Ignacio Tornos Martinez Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260522070646.203115-1-jtornosm@redhat.com --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index caaed1a01dc0..e49136ac5dbf 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5605,6 +5605,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap); * Intel 82579LM Gigabit Ethernet Controller 0x1502 * Intel 82579V Gigabit Ethernet Controller 0x1503 * Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter + * Mediatek MT7925 802.11be PCI Express Wireless Network Adapter */ static void quirk_no_flr(struct pci_dev *dev) { @@ -5619,6 +5620,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr); /* FLR may cause the SolidRun SNET DPU (rev 0x1) to hang */ static void quirk_no_flr_snet(struct pci_dev *dev) From 29fbf582e75015c031e7965fdd4084af123b9ca2 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:40 +0800 Subject: [PATCH 3234/5214] PCI: Add pci_host_common_link_train_delay() helper PCIe r6.0, sec 6.6.1 (Conventional Reset) requires that for a Downstream Port supporting Link speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms after Link training completes before sending any Configuration Request. Introduce a static inline helper pci_host_common_link_train_delay() that checks the given max_link_speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, etc.) and calls msleep(100) only when the speed is greater than 5.0 GT/s. This allows multiple host controller drivers to share the same mandatory delay without duplicating the logic. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260518004246.1384532-2-18255117159@163.com --- drivers/pci/controller/pci-host-common.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index b5075d4bd7eb..d709f7e3e11a 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -10,6 +10,9 @@ #ifndef _PCI_HOST_COMMON_H #define _PCI_HOST_COMMON_H +#include +#include "../pci.h" + struct pci_ecam_ops; int pci_host_common_probe(struct platform_device *pdev); @@ -20,4 +23,18 @@ void pci_host_common_remove(struct platform_device *pdev); struct pci_config_window *pci_host_common_ecam_create(struct device *dev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); + +/** + * pci_host_common_link_train_delay - Wait 100 ms if link speed > 5 GT/s + * @max_link_speed: the maximum link speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, ...) + * + * Must be called after Link training completes and before the first + * Configuration Request is sent. + */ +static inline void pci_host_common_link_train_delay(int max_link_speed) +{ + if (max_link_speed > 2) + msleep(PCIE_RESET_CONFIG_WAIT_MS); +} + #endif From 869317b95fd735684057666a65dd8ef95d4bd669 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:41 +0800 Subject: [PATCH 3235/5214] PCI: cadence: Add post-link delay for LGA and j721e glue driver The Cadence LGA (Legacy Architecture IP) PCIe host controller currently lacks the mandatory 100 ms delay after link training completes for speeds > 5.0 GT/s, as required by PCIe r6.0 sec 6.6.1. Add a 'max_link_speed' field to struct cdns_pcie. In the common host layer function cdns_pcie_host_start_link(), after the link has been successfully established, call pci_host_common_link_train_delay() to insert the required delay. For the j721e glue driver, set cdns_pcie.max_link_speed from the existing link speed logic. For other LGA-based glue drivers (sky1, sg2042), the common LGA host setup (pcie-cadence-host.c) provides a fallback reading of the device tree property "max-link-speed" when available. This ensures that the delay is not missed on those platforms once they enable the property. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260518004246.1384532-3-18255117159@163.com --- drivers/pci/controller/cadence/pci-j721e.c | 1 + drivers/pci/controller/cadence/pcie-cadence-host-common.c | 4 ++++ drivers/pci/controller/cadence/pcie-cadence-host.c | 4 ++++ drivers/pci/controller/cadence/pcie-cadence.h | 2 ++ 4 files changed, 11 insertions(+) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index bfdfe98d5aba..ae916e7b1927 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -206,6 +206,7 @@ static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, (pcie_get_link_speed(link_speed) == PCI_SPEED_UNKNOWN)) link_speed = 2; + pcie->cdns_pcie->max_link_speed = link_speed; val = link_speed - 1; ret = regmap_update_bits(syscon, offset, GENERATION_SEL_MASK, val); if (ret) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c index 2b0211870f02..18e4b6c760b5 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c @@ -14,6 +14,7 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../pci-host-common.h" #define LINK_RETRAIN_TIMEOUT HZ @@ -115,6 +116,9 @@ int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc, if (!ret && rc->quirk_retrain_flag) ret = cdns_pcie_retrain(pcie, pcie_link_up); + if (!ret) + pci_host_common_link_train_delay(pcie->max_link_speed); + return ret; } EXPORT_SYMBOL_GPL(cdns_pcie_host_start_link); diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 0bc9e6e90e0e..058e4e619654 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -13,6 +13,7 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../../pci.h" static u8 bar_aperture_mask[] = { [RP_BAR0] = 0x1F, @@ -397,6 +398,9 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) rc->device_id = 0xffff; of_property_read_u32(np, "device-id", &rc->device_id); + if (pcie->max_link_speed < 1) + pcie->max_link_speed = of_pci_get_max_link_speed(np); + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); if (IS_ERR(pcie->reg_base)) { dev_err(dev, "missing \"reg\"\n"); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 574e9cf4d003..042a4c49bb9a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -86,6 +86,7 @@ struct cdns_plat_pcie_of_data { * @ops: Platform-specific ops to control various inputs from Cadence PCIe * wrapper * @cdns_pcie_reg_offsets: Register bank offsets for different SoC + * @max_link_speed: Maximum supported link speed */ struct cdns_pcie { void __iomem *reg_base; @@ -98,6 +99,7 @@ struct cdns_pcie { struct device_link **link; const struct cdns_pcie_ops *ops; const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets; + int max_link_speed; }; /** From 782fec4ab945e1f3b9f0ba383fc00c67641d9767 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 9 Jun 2026 17:09:27 +0200 Subject: [PATCH 3236/5214] KVM: s390: Add module parameter to fence 2G hugepages Add the hpage_2g module parameter to KVM to allow enabling or disabling 2G hugepages in KVM. If hpage_2g is enabled but hpage is not enabled, print a message and disable hpage_2g. Opportunistically fix the comment for the hpage module parameter. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260609150930.665370-2-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f6521f16532a..abc7941d7bb4 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -204,11 +204,16 @@ static int nested; module_param(nested, int, S_IRUGO); MODULE_PARM_DESC(nested, "Nested virtualization support"); -/* allow 1m huge page guest backing, if !nested */ +/* allow 1m huge page guest backing */ static int hpage; module_param(hpage, int, 0444); MODULE_PARM_DESC(hpage, "1m huge page backing support"); +/* allow 2g huge page guest backing */ +static int hpage_2g; +module_param(hpage_2g, int, 0444); +MODULE_PARM_DESC(hpage_2g, "2g huge page backing support"); + /* maximum percentage of steal time for polling. >100 is treated like 100 */ static u8 halt_poll_max_steal = 10; module_param(halt_poll_max_steal, byte, 0644); @@ -5842,6 +5847,11 @@ static int __init kvm_s390_init(void) return -ENODEV; } + if (hpage_2g && !hpage) { + hpage_2g = 0; + pr_info("Disabling 2G hugepage support, since 1M hugepage support is not enabled.\n"); + } + for (i = 0; i < 16; i++) kvm_s390_fac_base[i] |= stfle_fac_list[i] & nonhyp_mask(i); From 103ff3a50e3a50a97e2b123d4dccbc8665eb1552 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 9 Jun 2026 17:09:28 +0200 Subject: [PATCH 3237/5214] KVM: s390: Add capability to support 2G hugepages Add KVM_CAP_S390_HPAGE_2G to signal to userspace that 2G hugepages may be used to back the guest; restrictions apply similar to 1M hugepages. Enable the (for now still ignored) GMAP_FLAG_ALLOW_HPAGE_2G flag for the guest gmap, and propagate / disable it as necessary. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260609150930.665370-3-imbrenda@linux.ibm.com> --- arch/s390/kvm/gmap.c | 5 +++++ arch/s390/kvm/kvm-s390.c | 26 ++++++++++++++++++++++++++ arch/s390/kvm/pv.c | 5 ++++- include/uapi/linux/kvm.h | 1 + 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index fe138d17caaf..f8bd6c135e3c 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -105,6 +105,11 @@ static void gmap_add_child(struct gmap *parent, struct gmap *child) else clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &child->flags); + if (test_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &parent->flags)) + set_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &child->flags); + else + clear_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &child->flags); + if (kvm_is_ucontrol(parent->kvm)) clear_bit(GMAP_FLAG_OWNS_PAGETABLES, &child->flags); list_add(&child->list, &parent->children); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index abc7941d7bb4..6de36421548c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -646,6 +646,11 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) if (hpage && !(kvm && kvm_is_ucontrol(kvm))) r = 1; break; + case KVM_CAP_S390_HPAGE_2G: + r = 0; + if (hpage_2g && !(kvm && kvm_is_ucontrol(kvm))) + r = 1; + break; case KVM_CAP_S390_MEM_OP: r = MEM_OP_MAX_SIZE; break; @@ -902,6 +907,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) VM_EVENT(kvm, 3, "ENABLE: CAP_S390_HPAGE %s", r ? "(not available)" : "(success)"); break; + case KVM_CAP_S390_HPAGE_2G: + mutex_lock(&kvm->lock); + if (kvm->created_vcpus) { + r = -EBUSY; + } else if (!hpage_2g || kvm->arch.use_cmma || kvm_is_ucontrol(kvm)) { + r = -EINVAL; + } else { + r = 0; + set_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &kvm->arch.gmap->flags); + /* + * We might have to create fake 4k page + * tables. To avoid that the hardware works on + * stale PGSTEs, we emulate these instructions. + */ + kvm->arch.use_skf = 0; + kvm->arch.use_pfmfi = 0; + } + mutex_unlock(&kvm->lock); + VM_EVENT(kvm, 3, "ENABLE: CAP_S390_HPAGE_2G %s", + r ? "(not available)" : "(success)"); + break; case KVM_CAP_S390_USER_STSI: VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_STSI"); kvm->arch.user_stsi = 1; diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index c2dafd812a3b..b9a23df96f7d 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -721,7 +721,10 @@ int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) uvcb.flags.ap_allow_instr = kvm->arch.model.uv_feat_guest.ap; uvcb.flags.ap_instr_intr = kvm->arch.model.uv_feat_guest.ap_intr; - clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &kvm->arch.gmap->flags); + scoped_guard(write_lock, &kvm->mmu_lock) { + clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &kvm->arch.gmap->flags); + clear_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &kvm->arch.gmap->flags); + } gmap_split_huge_pages(kvm->arch.gmap); cc = uv_call_sched(0, (u64)&uvcb); diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 6c8afa2047bf..419011097fa8 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -996,6 +996,7 @@ struct kvm_enable_cap { #define KVM_CAP_S390_USER_OPEREXEC 246 #define KVM_CAP_S390_KEYOP 247 #define KVM_CAP_S390_VSIE_ESAMODE 248 +#define KVM_CAP_S390_HPAGE_2G 249 struct kvm_irq_routing_irqchip { __u32 irqchip; From 46756c086310057c92cf17c8d07e0c63a7ba687d Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 9 Jun 2026 17:09:29 +0200 Subject: [PATCH 3238/5214] KVM: s390: Allow for 2G hugepages Change gmap_2g_allowed() to perform the necessary checks to allow for 2G hugepages to be used, instead of returning false. The GMAP_FLAG_ALLOW_HPAGE_2G gmap flag is now taken into account. Also add appropriate kerneldoc comments. Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260609150930.665370-4-imbrenda@linux.ibm.com> --- arch/s390/kvm/gmap.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index f8bd6c135e3c..402573cb2590 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -624,10 +624,27 @@ int gmap_try_fixup_minor(struct gmap *gmap, struct guest_fault *fault) return rc; } +/** + * gmap_2g_allowed() - Check whether a 2G hugepage is allowed. + * @gmap: The gmap of the guest. + * @f: Describes the fault that is being resolved. + * @slot: The memslot the faulting address belongs to. + * + * The function checks whether the GMAP_FLAG_ALLOW_HPAGE_2G flag is set for + * @gmap, whether the offset of the address in the 2G virtual frame is the + * same as the offset in the physical 2G frame, and finally whether the whole + * 2G page would fit in the given memslot. + * + * Return: true if a 2G hugepage is allowed to back the faulting address, false + * otherwise. + */ static inline bool gmap_2g_allowed(struct gmap *gmap, struct guest_fault *f, struct kvm_memory_slot *slot) { - return false; + return test_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &gmap->flags) && + !((f->gfn ^ f->pfn) & ~_REGION3_FR_MASK) && + slot->base_gfn <= ALIGN_DOWN(f->gfn, _PAGES_PER_REGION3) && + slot->base_gfn + slot->npages >= ALIGN(f->gfn + 1, _PAGES_PER_REGION3); } /** From 9030da050c9fe7d10150c9f93765e464f6c7da3c Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Tue, 9 Jun 2026 17:09:30 +0200 Subject: [PATCH 3239/5214] KVM: s390: Document the KVM_CAP_S390_HPAGE_2G capability Document the KVM_CAP_S390_HPAGE_2G capability, which behaves very similarly to the existing KVM_CAP_S390_HPAGE_1M. Signed-off-by: Claudio Imbrenda Message-ID: <20260609150930.665370-5-imbrenda@linux.ibm.com> --- Documentation/virt/kvm/api.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index e7998feaa940..dfde0bfcbce6 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8905,6 +8905,21 @@ helpful if user space wants to emulate instructions which are not This capability can be enabled dynamically even if VCPUs were already created and are running. +7.47 KVM_CAP_S390_HPAGE_2G +-------------------------- + +:Architectures: s390 +:Parameters: none +:Returns: 0 on success; -EINVAL if hpage_2g module parameter was not set, + cmma is enabled, or the VM has the KVM_VM_S390_UCONTROL + flag set; -EBUSY if vCPUs were already created for the VM. + +With this capability the KVM support for memory backing with 2g pages +through hugetlbfs can be enabled for a VM. After the capability is +enabled, cmma can't be enabled anymore and pfmfi and the storage key +interpretation are disabled. If cmma has already been enabled or the +hpage_2g module parameter is not set to 1, -EINVAL is returned. + 8. Other capabilities. ====================== From 0c26b1c34d12d4debfb5363cc0be6cdf68e87ba2 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 18 May 2026 15:27:14 +0800 Subject: [PATCH 3240/5214] PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95 According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in the Common Block Signals section selects the reference clock source connected to the PHY pads. Per the specification, any change to this input must be followed by a PHY reset assertion to take effect. Move the REF_USE_PAD configuration before the PHY reset toggle to comply with the required initialization sequence. Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95 PCIe") Signed-off-by: Richard Zhu [mani: renamed the callback and helper to match the usecase] Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518072715.3166514-2-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 773ab65b2afa..b32748816ac8 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -138,6 +138,7 @@ struct imx_pcie_drvdata { const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; const u32 mode_mask[IMX_PCIE_MAX_INSTANCES]; const struct pci_epc_features *epc_features; + int (*select_ref_clk_src)(struct imx_pcie *pcie); int (*init_phy)(struct imx_pcie *pcie); int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -249,6 +250,24 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } +static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) +{ + bool ext = imx_pcie->enable_ext_refclk; + + /* + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is + * used to select reference clock connected to a pair of pads. + * + * Any change in this input must be followed by phy_reset assertion. + */ + + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); + + return 0; +} + static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { bool ext = imx_pcie->enable_ext_refclk; @@ -271,9 +290,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, - ext ? IMX95_PCIE_REF_USE_PAD : 0); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, ext ? 0 : IMX95_PCIE_REF_CLKEN); @@ -1351,6 +1367,9 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) pp->bridge->disable_device = imx_pcie_disable_device; } + if (imx_pcie->drvdata->select_ref_clk_src) + imx_pcie->drvdata->select_ref_clk_src(imx_pcie); + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->drvdata->init_phy) @@ -2051,6 +2070,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .enable_ref_clk = imx95_pcie_enable_ref_clk, .clr_clkreq_override = imx95_pcie_clr_clkreq_override, @@ -2106,6 +2126,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .init_phy = imx95_pcie_init_phy, .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, From 9dda3f83ba677b9cc2613cecd9120123000ae50f Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Mon, 18 May 2026 15:27:15 +0800 Subject: [PATCH 3241/5214] PCI: imx6: Assert ref_clk_en after reference clock stabilizes on i.MX95 According to the PHY Databook Common Block Signals section, the ref_clk_en signal must remain de-asserted until the reference clock is running at the appropriate frequency. Once the clock is stable, ref_clk_en can be asserted. For lower power states where the reference clock to the PHY is disabled, ref_clk_en should also be de-asserted. Move the ref_clk_en bit manipulation into imx95_pcie_enable_ref_clk() to ensure the reference clock stabilizes before ref_clk_en is asserted and before the PHY reset is de-asserted. This aligns with the timing requirements specified in the PHY documentation. Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support") Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518072715.3166514-3-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index b32748816ac8..cce78a7a1624 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -270,8 +270,6 @@ static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { - bool ext = imx_pcie->enable_ext_refclk; - /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -290,10 +288,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, - IMX95_PCIE_REF_CLKEN, - ext ? 0 : IMX95_PCIE_REF_CLKEN); - return 0; } @@ -742,7 +736,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { + bool ext = imx_pcie->enable_ext_refclk; + imx95_pcie_clkreq_override(imx_pcie, enable); + /* + * The ref_clk_en signal must remain de-asserted until the + * reference clock is running at appropriate frequency, at which + * point this bit can be asserted. For lower power states where + * the reference clock to the PHY is disabled, it may also be + * de-asserted. + * +------------------- -+--------+----------------+ + * | External clock mode | Enable | PCIE_REF_CLKEN | + * +---------------------+--------+----------------+ + * | TRUE | X | 1b'0 | + * +---------------------+--------+----------------+ + * | FALSE | TRUE | 1b'1 | + * +---------------------+--------+----------------+ + * | FALSE | FALSE | 1b'0 | + * +---------------------+--------+----------------+ + */ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, + ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN); + return 0; } From b12341b98d5ac52f48ca1390e1e371aed81346c8 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 13:49:40 +0545 Subject: [PATCH 3242/5214] PCI: meson: Propagate devm_add_action_or_reset() failure meson_pcie_probe_clock() enables a clock and then registers a devres action to disable it during teardown. If devm_add_action_or_reset() fails, it runs the action immediately, disabling the clock. The return value is currently ignored, so on that failure path, meson_pcie_probe_clock() returns the disabled clock and probe continues. Return the error so the existing probe error path unwinds normally. Fixes: 9c0ef6d34fdbf ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/177909148011.9588.6639767953842842291@gmail.com --- drivers/pci/controller/dwc/pci-meson.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 0694084f612b..8d495bcc3a41 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -204,7 +204,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev, return ERR_PTR(ret); } - devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + if (ret) + return ERR_PTR(ret); return clk; } From 4b0dc84b293984f75598881809fb2d3daf54a2a8 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:18 +0545 Subject: [PATCH 3243/5214] PCI: meson: Add missing remove callback meson_pcie_probe() powers on the PHY and registers the DesignWare host bridge with dw_pcie_host_init(), but the driver has no remove callback. On driver unbind or module unload, the driver core therefore proceeds to devres cleanup without first unregistering the host bridge or powering off the PHY. Add a remove callback that deinitializes the DesignWare host bridge and powers off the PHY while device-managed resources are still valid. Fixes: 9c0ef6d34fdb ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/1a0c86ab264cdc1c79c917e984b90991af51d827.1779123847.git.shuvampandey1@gmail.com --- drivers/pci/controller/dwc/pci-meson.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 8d495bcc3a41..225d887cd0a3 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -453,6 +453,14 @@ static int meson_pcie_probe(struct platform_device *pdev) return ret; } +static void meson_pcie_remove(struct platform_device *pdev) +{ + struct meson_pcie *mp = platform_get_drvdata(pdev); + + dw_pcie_host_deinit(&mp->pci.pp); + meson_pcie_power_off(mp); +} + static const struct of_device_id meson_pcie_of_match[] = { { .compatible = "amlogic,axg-pcie", @@ -466,6 +474,7 @@ MODULE_DEVICE_TABLE(of, meson_pcie_of_match); static struct platform_driver meson_pcie_driver = { .probe = meson_pcie_probe, + .remove = meson_pcie_remove, .driver = { .name = "meson-pcie", .of_match_table = meson_pcie_of_match, From 34b9a83c50660148bde01cde16451dbe78369749 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 09:55:05 +0000 Subject: [PATCH 3244/5214] nvmet: fix refcount leak in nvmet_sq_create() In nvmet_sq_create(), a reference on the ctrl is taken via kref_get_unless_zero() before calling nvmet_check_sqid(). If nvmet_check_sqid() fails, the function returns the error directly without releasing the reference, leading to a leak. Fix this by jumping to the "ctrl_put" label, which already performs the necessary nvmet_ctrl_put(ctrl). This ensures the reference is properly released on this error path. Cc: stable@vger.kernel.org Fixes: 1eb380caf527 ("nvmet: Introduce nvmet_sq_create() and nvmet_cq_create()") Signed-off-by: Wentao Liang Signed-off-by: Keith Busch --- drivers/nvme/target/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 62dd59b9aa4f..4477c4d6b1ee 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -944,7 +944,7 @@ u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, status = nvmet_check_sqid(ctrl, sqid, true); if (status != NVME_SC_SUCCESS) - return status; + goto ctrl_put; ret = nvmet_sq_init(sq, cq); if (ret) { From c268f949e219f9e179558e836f457f6c5fbec416 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Fri, 5 Jun 2026 13:06:10 -0500 Subject: [PATCH 3245/5214] cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size The CXL r4.0 8.2.4.17.7 RAS Capability Structure has total length 0x58 bytes (CXL_RAS_CAPABILITY_LENGTH); the Header Log occupies the trailing 64 bytes at offset 0x18. CXL_HEADERLOG_SIZE was defined as SZ_512, eight times the actual on-device size. header_log_copy() reads CXL_HEADERLOG_SIZE_U32 (128) dwords from the RAS capability iomap, overrunning the 88-byte mapping by 448 bytes. The cxl_aer_uncorrectable_error trace event memcpy()s CXL_HEADERLOG_SIZE (512) bytes from its source. For the CPER caller the source is struct cxl_ras_capability_regs::header_log[16] (64 bytes) embedded in a stack-local cxl_cper_prot_err_work_data, so the memcpy reads 448 bytes of kernel stack into the trace event ring buffer where userspace can read it via tracefs. Set CXL_HEADERLOG_SIZE to 64 and derive CXL_HEADERLOG_SIZE_U32 from it, bringing all iomap readers into agreement on 16 dwords. Userspace tools such as rasdaemon have grown a dependency on the buggy 512-byte (128 u32) header_log layout in the cxl_aer_uncorrectable_error trace event. Add CXL_HEADERLOG_TRACE_SIZE_U32 = 128 and use it for the trace event __array and its memcpy to preserve that ABI. Both callers now pass a zero-filled u32[CXL_HEADERLOG_TRACE_SIZE_U32] staging buffer with only the first CXL_HEADERLOG_SIZE_U32 (16) entries populated from hardware; the remaining 112 u32s are zero-padded, keeping the 512-byte trace ring buffer layout intact. [ dj: Replaced 64 with SZ_64 per RichardC ] Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors") Fixes: 2905cb5236cb ("cxl/pci: Add (hopeful) error handling support") Cc: stable@vger.kernel.org Reported-by: Sashiko Signed-off-by: Terry Bowman Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260605180610.2249458-1-terry.bowman@amd.com Signed-off-by: Dave Jiang --- drivers/cxl/core/ras.c | 27 ++++++++++++++++++++------- drivers/cxl/core/trace.h | 24 ++++++++++++++++-------- drivers/cxl/cxl.h | 14 ++++++++++++-- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 006c6ffc2f56..99fb00949c2f 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -8,6 +8,10 @@ #include #include "trace.h" +/* Check that UCE header definition is maintained to keep ABI intact */ +static_assert(CXL_HEADERLOG_TRACE_SIZE_U32 == 128, + "rasdaemon ABI requires exactly 128 u32s"); + static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { @@ -19,6 +23,7 @@ static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -28,8 +33,8 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, else fe = status; - trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, - ras_cap.header_log); + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, hl); } static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, @@ -44,6 +49,7 @@ static void cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -53,8 +59,15 @@ cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, else fe = status; - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, - ras_cap.header_log); + /* + * ras_cap.header_log[] holds CXL_HEADERLOG_SIZE_U32 (16) hardware + * dwords. Copy them into the front of a zero-filled + * CXL_HEADERLOG_TRACE_SIZE_U32 (128) u32 staging buffer so the trace + * event memcpy sees a full 512-byte source and the userspace ABI + * (rasdaemon) is preserved. + */ + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl); } static int match_memdev_by_parent(struct device *dev, const void *uport) @@ -204,12 +217,12 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) { void __iomem *addr; u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + int i; addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; log_addr = log; - for (i = 0; i < log_u32_size; i++) { + for (i = 0; i < CXL_HEADERLOG_SIZE_U32; i++) { *log_addr = readl(addr); log_addr++; addr += sizeof(u32); @@ -222,7 +235,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) */ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base) { - u32 hl[CXL_HEADERLOG_SIZE_U32]; + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; void __iomem *addr; u32 status; u32 fe; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a972e4ef1936..d37876096dd7 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -56,7 +56,7 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __string(host, dev_name(dev->parent)) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(device); @@ -64,10 +64,14 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("device=%s host=%s status: '%s' first_error: '%s'", __get_str(device), __get_str(host), @@ -85,7 +89,7 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __field(u64, serial) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(memdev); @@ -94,10 +98,14 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("memdev=%s host=%s serial=%lld: status: '%s' first_error: '%s'", __get_str(memdev), __get_str(host), __entry->serial, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 1297594beaec..21fc89d3aeea 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -158,8 +158,18 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0) #define CXL_RAS_HEADER_LOG_OFFSET 0x18 #define CXL_RAS_CAPABILITY_LENGTH 0x58 -#define CXL_HEADERLOG_SIZE SZ_512 -#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32) +#define CXL_HEADERLOG_SIZE SZ_64 +#define CXL_HEADERLOG_SIZE_U32 (CXL_HEADERLOG_SIZE / sizeof(u32)) + +/* + * The RAS UCE trace event header array was originally sized at SZ_512/sizeof(u32) + * = 128 u32s due to a bug. Userspace tools (rasdaemon) have grown a dependency + * on that 512-byte layout. Keep the trace array at 128 u32s to preserve the + * ABI; only CXL_HEADERLOG_SIZE_U32 (16) dwords are valid hardware data, the + * remainder are zero-filled. + */ +#define CXL_HEADERLOG_TRACE_SIZE SZ_512 +#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32)) /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 From f865a57896bd92d7662eb2818d8f48872e2cbbc7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 23:16:17 +0530 Subject: [PATCH 3246/5214] PCI: mediatek: Fix IRQ domain leak when port fails to enable When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port from pcie->ports and frees the port structure. However, the IRQ domains set up earlier by mtk_pcie_init_irq_domain() are never freed. Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in the probe error path (during resume, child devices may have active MSI mappings and the NOIRQ context prohibits sleeping locks), mtk_pcie_enable_port() is changed to return an error code so callers can distinguish the two paths and act accordingly. This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC support series. Fixes: b099631df160 ("PCI: mediatek: Add controller support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org # 5.10 Cc: Caleb James DeLisle Link: https://patch.msgid.link/20260521174617.17692-1-mani@kernel.org --- drivers/pci/controller/pcie-mediatek.c | 65 ++++++++++++++++---------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index dcfc7408340f..f34d91e495bc 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -530,23 +530,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) writel(val, port->base + PCIE_INT_MASK); } +static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port) +{ + irq_set_chained_handler_and_data(port->irq, NULL, NULL); + + if (port->irq_domain) + irq_domain_remove(port->irq_domain); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (port->inner_domain) + irq_domain_remove(port->inner_domain); + } + + irq_dispose_mapping(port->irq); +} + static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) { struct mtk_pcie_port *port, *tmp; - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - irq_set_chained_handler_and_data(port->irq, NULL, NULL); - - if (port->irq_domain) - irq_domain_remove(port->irq_domain); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (port->inner_domain) - irq_domain_remove(port->inner_domain); - } - - irq_dispose_mapping(port->irq); - } + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_irq_teardown_port(port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, @@ -866,7 +870,7 @@ static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port) return mtk_pcie_startup_port_v2(port); } -static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +static int mtk_pcie_enable_port(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; struct device *dev = pcie->dev; @@ -875,7 +879,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) err = clk_prepare_enable(port->sys_ck); if (err) { dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); - goto err_sys_clk; + return err; } err = clk_prepare_enable(port->ahb_ck); @@ -923,11 +927,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) goto err_phy_on; } - if (!pcie->soc->startup(port)) - return; + err = pcie->soc->startup(port); + if (err) { + dev_info(dev, "Port%d link down\n", port->slot); + goto err_soc_startup; + } - dev_info(dev, "Port%d link down\n", port->slot); + return 0; +err_soc_startup: phy_power_off(port->phy); err_phy_on: phy_exit(port->phy); @@ -943,8 +951,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) clk_disable_unprepare(port->ahb_ck); err_ahb_clk: clk_disable_unprepare(port->sys_ck); -err_sys_clk: - mtk_pcie_port_free(port); + + return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie, @@ -1110,8 +1118,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) return err; /* enable each port, and then check link status */ - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) { + mtk_pcie_irq_teardown_port(port); + mtk_pcie_port_free(port); + } + } /* power down PCIe subsys if slots are all empty (link down) */ if (list_empty(&pcie->ports)) @@ -1210,14 +1223,18 @@ static int mtk_pcie_resume_noirq(struct device *dev) { struct mtk_pcie *pcie = dev_get_drvdata(dev); struct mtk_pcie_port *port, *tmp; + int err; if (list_empty(&pcie->ports)) return 0; clk_prepare_enable(pcie->free_ck); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) + mtk_pcie_port_free(port); + } /* In case of EP was removed while system suspend. */ if (list_empty(&pcie->ports)) From 26acdaa357cded33a37f575cd5f6bae1033b3a5d Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Mon, 8 Jun 2026 17:53:57 +0200 Subject: [PATCH 3247/5214] nvme: fix crash and memory leak during invalid cdev teardown In the NVMe multipath code, if nvme_add_ns_head_cdev() fails during nvme_mpath_set_live(), the error is ignored. However, during teardown, nvme_remove_head() unconditionally calls nvme_cdev_del(). This teardown asymmetry leads to a kernel panic if the character device was never successfully initialized. BUG: kernel NULL pointer dereference, address: 00000000000000d0 device_del+0x39/0x3c0 cdev_device_del+0x15/0x50 nvme_cdev_del+0xe/0x20 [nvme_core] nvme_mpath_shutdown_disk+0x38/0x60 [nvme_core] nvme_ns_remove+0x177/0x1f0 [nvme_core] nvme_remove_namespaces+0xdc/0x130 [nvme_core] nvme_do_delete_ctrl+0x71/0xd0 [nvme_core] Additionally, a memory leak exists in the nvme_cdev_add() failure path. Previously, dev_set_name() was called before ida_alloc(). If ida_alloc() subsequently failed, device_initialize() was never called, meaning put_device() could not be used to clean up the kobject, leaking the memory allocated by dev_set_name(). * Introduces the NVME_NSHEAD_CDEV_LIVE and NVME_NS_CDEV_LIVE bits to track the successful creation of the character devices. Teardown routines now check these bits before attempting deletion. * Refactor nvme_cdev_add() to accept the formatted device name as a parameter, moving dev_set_name() after the IDA allocation and immediately before device_initialize(). This ensures any internally allocated strings are safely cleaned up by put_device() upon failure. Signed-off-by: Maurizio Lombardi Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 33 ++++++++++++++++++++++++--------- drivers/nvme/host/multipath.c | 19 +++++++++++++------ drivers/nvme/host/nvme.h | 5 ++++- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index d37fc70fe48a..c6930e43cfea 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3894,7 +3894,8 @@ void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device) put_device(cdev_device); } -int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device, +int nvme_cdev_add(const char *name, struct cdev *cdev, + struct device *cdev_device, const struct file_operations *fops, struct module *owner) { int minor, ret; @@ -3902,6 +3903,12 @@ int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device, minor = ida_alloc(&nvme_ns_chr_minor_ida, GFP_KERNEL); if (minor < 0) return minor; + + ret = dev_set_name(cdev_device, name); + if (ret) { + ida_free(&nvme_ns_chr_minor_ida, minor); + return ret; + } cdev_device->devt = MKDEV(MAJOR(nvme_ns_chr_devt), minor); cdev_device->class = &nvme_ns_chr_class; cdev_device->release = nvme_cdev_rel; @@ -3939,15 +3946,21 @@ static const struct file_operations nvme_ns_chr_fops = { static int nvme_add_ns_cdev(struct nvme_ns *ns) { int ret; + char name[32]; ns->cdev_device.parent = ns->ctrl->device; - ret = dev_set_name(&ns->cdev_device, "ng%dn%d", - ns->ctrl->instance, ns->head->instance); - if (ret) - return ret; + snprintf(name, sizeof(name), "ng%dn%d", ns->ctrl->instance, + ns->head->instance); - return nvme_cdev_add(&ns->cdev, &ns->cdev_device, &nvme_ns_chr_fops, - ns->ctrl->ops->module); + ret = nvme_cdev_add(name, &ns->cdev, &ns->cdev_device, + &nvme_ns_chr_fops, ns->ctrl->ops->module); + if (ret) { + dev_err(ns->ctrl->device, "Unable to create the %s device\n", + name); + } else { + set_bit(NVME_NS_CDEV_LIVE, &ns->flags); + } + return ret; } static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, @@ -4323,8 +4336,10 @@ static void nvme_ns_remove(struct nvme_ns *ns) /* guarantee not available in head->list */ synchronize_srcu(&ns->head->srcu); - if (!nvme_ns_head_multipath(ns->head)) - nvme_cdev_del(&ns->cdev, &ns->cdev_device); + if (!nvme_ns_head_multipath(ns->head)) { + if (test_and_clear_bit(NVME_NS_CDEV_LIVE, &ns->flags)) + nvme_cdev_del(&ns->cdev, &ns->cdev_device); + } nvme_mpath_remove_sysfs_link(ns); diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index e033ede953cc..9cd49e2f760d 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -642,14 +642,20 @@ static const struct file_operations nvme_ns_head_chr_fops = { static int nvme_add_ns_head_cdev(struct nvme_ns_head *head) { int ret; + char name[32]; head->cdev_device.parent = &head->subsys->dev; - ret = dev_set_name(&head->cdev_device, "ng%dn%d", - head->subsys->instance, head->instance); - if (ret) - return ret; - ret = nvme_cdev_add(&head->cdev, &head->cdev_device, + snprintf(name, sizeof(name), "ng%dn%d", head->subsys->instance, + head->instance); + + ret = nvme_cdev_add(name, &head->cdev, &head->cdev_device, &nvme_ns_head_chr_fops, THIS_MODULE); + if (ret) { + dev_err(disk_to_dev(head->disk), + "Unable to create the %s device\n", name); + } else { + set_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags); + } return ret; } @@ -694,7 +700,8 @@ static void nvme_remove_head(struct nvme_ns_head *head) */ kblockd_schedule_work(&head->requeue_work); - nvme_cdev_del(&head->cdev, &head->cdev_device); + if (test_and_clear_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags)) + nvme_cdev_del(&head->cdev, &head->cdev_device); synchronize_srcu(&head->srcu); del_gendisk(head->disk); } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index b367c67dcb37..824651cc898d 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -573,6 +573,7 @@ struct nvme_ns_head { atomic_long_t io_fail_no_available_path_count; #define NVME_NSHEAD_DISK_LIVE 0 #define NVME_NSHEAD_QUEUE_IF_NO_PATH 1 +#define NVME_NSHEAD_CDEV_LIVE 2 struct nvme_ns __rcu *current_path[]; #endif }; @@ -611,6 +612,7 @@ struct nvme_ns { #define NVME_NS_FORCE_RO 3 #define NVME_NS_READY 4 #define NVME_NS_SYSFS_ATTR_LINK 5 +#define NVME_NS_CDEV_LIVE 6 struct cdev cdev; struct device cdev_device; @@ -995,7 +997,8 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi, void *log, size_t size, u64 offset); bool nvme_tryget_ns_head(struct nvme_ns_head *head); void nvme_put_ns_head(struct nvme_ns_head *head); -int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device, +int nvme_cdev_add(const char *name, struct cdev *cdev, + struct device *cdev_device, const struct file_operations *fops, struct module *owner); void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device); int nvme_ioctl(struct block_device *bdev, blk_mode_t mode, From 1e1edc973c64307821ee22049908e7ded8f973c2 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 4 Jun 2026 21:05:01 -0700 Subject: [PATCH 3248/5214] cxl/region: Avoid variable shadowing in region attach paths A couple of symbol declarations shadow earlier variables in the region attach paths. Shadowing makes it harder to tell which object is being referenced and can obscure future bugs. Reuse the existing 'cxld' variable in cxl_port_attach_region() and rename the endpoint decoder iterator in cxl_region_attach() to avoid shadowing the function parameter. No functional change. Found with sparse. Signed-off-by: Alison Schofield Reviewed-by: Li Ming Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260605040504.865728-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index cc41c08c0c0c..f5cd20f48d2b 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1224,8 +1224,6 @@ static int cxl_port_attach_region(struct cxl_port *port, nr_targets_inc = true; } } else { - struct cxl_decoder *cxld; - cxld = cxl_port_pick_region_decoder(port, cxled, cxlr); if (!cxld) { dev_dbg(&cxlr->dev, "%s: no decoder available\n", @@ -2189,14 +2187,14 @@ static int cxl_region_attach(struct cxl_region *cxlr, * will fail when presented as CXL_REGION_F_AUTO. */ for (int i = 0; i < p->nr_targets; i++) { - struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_endpoint_decoder *target = p->targets[i]; int test_pos; - test_pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); - dev_dbg(&cxled->cxld.dev, - "Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n", - (test_pos == cxled->pos) ? "success" : "fail", - test_pos, cxled->pos); + test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range); + dev_dbg(&target->cxld.dev, + "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n", + (test_pos == target->pos) ? "success" : "fail", + test_pos, target->pos); } return 0; From 4fd1f5f6a659886a4ef3a380b2a07207c94a7a24 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:12:23 -0700 Subject: [PATCH 3249/5214] nvme: target: allocate ana_state with port Use a flexible array member to remove one allocation. Simplifies code slightly. Signed-off-by: Rosen Penev Signed-off-by: Keith Busch --- drivers/nvme/target/configfs.c | 9 +-------- drivers/nvme/target/nvmet.h | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index b88f897f06e2..2b69ffcfc8df 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -2007,7 +2007,6 @@ static void nvmet_port_release(struct config_item *item) list_del(&port->global_entry); key_put(port->keyring); - kfree(port->ana_state); kfree(port); } @@ -2047,16 +2046,10 @@ static struct config_group *nvmet_ports_make(struct config_group *group, if (kstrtou16(name, 0, &portid)) return ERR_PTR(-EINVAL); - port = kzalloc_obj(*port); + port = kzalloc_flex(*port, ana_state, NVMET_MAX_ANAGRPS + 1); if (!port) return ERR_PTR(-ENOMEM); - port->ana_state = kzalloc_objs(*port->ana_state, NVMET_MAX_ANAGRPS + 1); - if (!port->ana_state) { - kfree(port); - return ERR_PTR(-ENOMEM); - } - if (IS_ENABLED(CONFIG_NVME_TARGET_TCP_TLS) && nvme_keyring_id()) { port->keyring = key_lookup(nvme_keyring_id()); if (IS_ERR(port->keyring)) { diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 3305a88684ec..aaba745e3c21 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -208,7 +208,6 @@ struct nvmet_port { struct list_head global_entry; struct config_group ana_groups_group; struct nvmet_ana_group ana_default_group; - enum nvme_ana_state *ana_state; struct key *keyring; void *priv; bool enabled; @@ -217,6 +216,7 @@ struct nvmet_port { int mdts; const struct nvmet_fabrics_ops *tr_ops; bool pi_enable; + enum nvme_ana_state ana_state[]; }; static inline struct nvmet_port *to_nvmet_port(struct config_item *item) From 7cf5e4fc36d3a77467dd0d02a45371ea4350410b Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 8 Jun 2026 19:56:04 -0700 Subject: [PATCH 3250/5214] NFS: correct CONFIG_NFS_V4 macro name in #endif comment A comment in fs/nfs/dir.c incorrectly refers to CONFIG_NFSV4 instead of CONFIG_NFS_V4. Correct it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 659e39b1562b..fb8633fa2ac4 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2299,7 +2299,7 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name, return nfs_do_lookup_revalidate(dir, name, dentry, flags); } -#endif /* CONFIG_NFSV4 */ +#endif /* CONFIG_NFS_V4 */ int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry, struct file *file, unsigned int open_flags, From 48c0162f647bb47e6084ffbc71b8f213f5e2f4f8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 19:36:54 +0000 Subject: [PATCH 3251/5214] nvmet-rdma: handle inline data with a nonzero offset nvmet_rdma_use_inline_sg() maps the host-controlled inline data offset into the per-command inline scatterlist. The bounds check admits any offset with off + len <= inline_data_size, but the mapping still assumes the data begins in the first inline page: sg->offset = off; sg->length = min_t(int, len, PAGE_SIZE - off); When a port is configured with inline_data_size > PAGE_SIZE (settable up to max(SZ_16K, PAGE_SIZE)), an offset in (PAGE_SIZE, inline_data_size] makes "PAGE_SIZE - off" underflow, so sg->length is set to ~4 GiB and the block backend reads far past the first inline page. num_pages(len) also ignores the offset, so an in-bounds offset whose [off, off+len) span crosses a page boundary under-counts the scatterlist. Map the offset properly: split it into a page index and an in-page offset, start the scatterlist at that page, and size the page count from page_off + len. Because the request scatterlist may now start at inline_sg[page_idx] rather than inline_sg[0], generalize the inline-SGL identity test in nvmet_rdma_release_rsp() to a range test; otherwise the persistent inline scatterlist is mistaken for an allocated one and nvmet_req_free_sgls() frees an inline page (and warns in free_large_kmalloc()). Fixes: 0d5ee2b2ab4f ("nvmet-rdma: support max(16KB, PAGE_SIZE) inline data") Cc: stable@vger.kernel.org Suggested-by: Keith Busch Reported-by: Bryam Vargas Signed-off-by: Bryam Vargas Signed-off-by: Keith Busch --- drivers/nvme/target/rdma.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index ac26f4f774c4..ea1185b8267e 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -666,7 +666,8 @@ static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp) if (rsp->n_rdma) nvmet_rdma_rw_ctx_destroy(rsp); - if (rsp->req.sg != rsp->cmd->inline_sg) + if (rsp->req.sg < rsp->cmd->inline_sg || + rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count) nvmet_req_free_sgls(&rsp->req); if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list))) @@ -821,24 +822,25 @@ static void nvmet_rdma_write_data_done(struct ib_cq *cq, struct ib_wc *wc) static void nvmet_rdma_use_inline_sg(struct nvmet_rdma_rsp *rsp, u32 len, u64 off) { - int sg_count = num_pages(len); + u64 page_off = off % PAGE_SIZE; + u64 page_idx = off / PAGE_SIZE; + int sg_count = num_pages(page_off + len); struct scatterlist *sg; int i; - sg = rsp->cmd->inline_sg; + sg = &rsp->cmd->inline_sg[page_idx]; for (i = 0; i < sg_count; i++, sg++) { if (i < sg_count - 1) sg_unmark_end(sg); else sg_mark_end(sg); - sg->offset = off; - sg->length = min_t(int, len, PAGE_SIZE - off); + sg->offset = page_off; + sg->length = min_t(u64, len, PAGE_SIZE - page_off); len -= sg->length; - if (!i) - off = 0; + page_off = 0; } - rsp->req.sg = rsp->cmd->inline_sg; + rsp->req.sg = &rsp->cmd->inline_sg[page_idx]; rsp->req.sg_cnt = sg_count; } From 0ba76b19fd4c7256787eab0283c759b18eb76876 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 4 Jun 2026 17:12:08 +0200 Subject: [PATCH 3252/5214] PCI/P2PDMA: Add Intel QAT, DSA, IAA devices to whitelist The first device on a PCI root bus determines whether the host bridge is whitelisted for P2PDMA. All Intel Xeon chips since Ice Lake (ICX, 2021) expose a device with ID 0x09a2 as first device. It is loosely associated with the IOMMU. All these Xeon chips support P2PDMA, so since the addition of the device with commit feaea1fe8b36 ("PCI/P2PDMA: Add Intel 3rd Gen Intel Xeon Scalable Processors to whitelist"), P2PDMA has been allowed on all new Xeons without the need to amend the whitelist: Xeons with Performance Cores: Sapphire Rapids (SPR, 2023) Emerald Rapids (EMR, 2023) Granite Rapids (GNR, 2024) Diamond Rapids (DMR, 2026) Xeons with Efficiency Cores: Sierra Forest (SRF, 2024) Clearwater Forest (CWF, 2026) However these Xeons also expose accelerators as first device on a root bus of its own: QuickAssist Technology (QAT, crypto & compression accelerator) Data Streaming Accelerator (DSA, dma engine) In-Memory Analytics Accelerator (IAA, compression accelerator) Whitelist them for P2PDMA as well. Move their Device ID macros from the accelerator drivers to for reuse by P2PDMA code. Unfortunately the Device IDs vary across Xeon generations as additional features were added to the accelerators. This currently necessitates an amendment for each new Xeon chip. For future chips, this need shall be avoided by an ongoing effort to extend ACPI HMAT with PCIe P2PDMA characteristics (latency, bandwidth, ordering constraints). The PCI core will be able look up in this BIOS-provided ACPI table whether P2PDMA is supported, instead of relying on a whitelist that needs to be amended continuously. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Acked-by: Vinicius Costa Gomes Acked-by: Giovanni Cabiddu # QAT Cc: stable@vger.kernel.org Link: https://patch.msgid.link/6aac4922b5fe7070b11874427a9285e42ddd05a4.1780585518.git.lukas@wunner.de --- .../crypto/intel/qat/qat_common/adf_accel_devices.h | 5 ----- drivers/dma/idxd/registers.h | 3 --- drivers/pci/p2pdma.c | 10 ++++++++++ include/linux/pci_ids.h | 8 ++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 03a4e9690208..cbd1d1eda5a3 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -28,15 +28,10 @@ #define ADF_4XXX_DEVICE_NAME "4xxx" #define ADF_420XX_DEVICE_NAME "420xx" #define ADF_6XXX_DEVICE_NAME "6xxx" -#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 #define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941 -#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 #define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943 -#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 #define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945 -#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 #define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947 -#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949 #define ADF_DEVICE_FUSECTL_OFFSET 0x40 diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index f95411363ea9..1dce26d4da83 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -10,9 +10,6 @@ #endif /* PCI Config */ -#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb -#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 -#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d #define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index adb17a4f6939..b2d5266f8653 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -552,6 +552,16 @@ static const struct pci_p2pdma_whitelist_entry { {PCI_VENDOR_ID_INTEL, 0x2033, 0}, {PCI_VENDOR_ID_INTEL, 0x2020, 0}, {PCI_VENDOR_ID_INTEL, 0x09a2, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_GNRD, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0}, /* Google SoCs. */ {PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0}, {} diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 24cb42f66e4b..1c9d40e09107 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2732,6 +2732,9 @@ #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_SST_TNG 0x119a +#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb +#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 +#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e @@ -3052,6 +3055,11 @@ #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 #define PCI_DEVICE_ID_INTEL_HDA_TGL_H 0x43c8 #define PCI_DEVICE_ID_INTEL_HDA_DG1 0x490d +#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 +#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 +#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 +#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 +#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_HDA_EHL_0 0x4b55 #define PCI_DEVICE_ID_INTEL_HDA_EHL_3 0x4b58 #define PCI_DEVICE_ID_INTEL_HDA_WCL 0x4d28 From ead562291438b5657d7e4d5e8d6d54611b132370 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 9 Jun 2026 14:35:32 -0700 Subject: [PATCH 3253/5214] Input: ipaq-micro-keys - simplify allocation Embed the keycode array in the struct to have a single allocation. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260609213532.25181-1-rosenp@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ipaq-micro-keys.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c index ca7ec054b1ce..695ef3c2081a 100644 --- a/drivers/input/keyboard/ipaq-micro-keys.c +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -20,12 +20,6 @@ #include #include -struct ipaq_micro_keys { - struct ipaq_micro *micro; - struct input_dev *input; - u16 *codes; -}; - static const u16 micro_keycodes[] = { KEY_RECORD, /* 1: Record button */ KEY_CALENDAR, /* 2: Calendar */ @@ -38,6 +32,12 @@ static const u16 micro_keycodes[] = { KEY_DOWN, /* 9: Down */ }; +struct ipaq_micro_keys { + struct ipaq_micro *micro; + struct input_dev *input; + u16 codes[ARRAY_SIZE(micro_keycodes)]; +}; + static void micro_key_receive(void *data, int len, unsigned char *msg) { struct ipaq_micro_keys *keys = data; @@ -102,10 +102,7 @@ static int micro_key_probe(struct platform_device *pdev) keys->input->keycodesize = sizeof(micro_keycodes[0]); keys->input->keycodemax = ARRAY_SIZE(micro_keycodes); - keys->codes = devm_kmemdup_array(&pdev->dev, micro_keycodes, keys->input->keycodemax, - keys->input->keycodesize, GFP_KERNEL); - if (!keys->codes) - return -ENOMEM; + memcpy(keys->codes, micro_keycodes, sizeof(keys->codes)); keys->input->keycode = keys->codes; From 57ac2831c8e0f168090d38e3de758c6a59db44db Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 26 May 2026 16:08:04 +0800 Subject: [PATCH 3254/5214] fs/ntfs3: prevent potential lcn remains uninitialized The target VCN being sought was not found within runs[0], causing run_lookup() to return false. This causes run_lookup_entry() to return false, which in turn results in a len value of 0, and the new parameter passed to attr_data_get_block() is NULL. Collectively, these factors ultimately cause attr_data_get_block_locked() to exit prematurely without initializing lcn, thereby triggering [1]. To prevent [1], the clen check within ni_seek_data_or_hole() has been moved to occur before the lcn check. [1] BUG: KMSAN: uninit-value in ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ntfs_llseek+0x22a/0x4a0 fs/ntfs3/file.c:1530 vfs_llseek fs/read_write.c:391 [inline] Fixes: c61326967728 ("fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs") Reported-by: syzbot+c2cfe997245202e46f10@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c2cfe997245202e46f10 Signed-off-by: Edward Adam Davis Signed-off-by: Konstantin Komarov --- fs/ntfs3/frecord.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index abab45f6f180..2b49bc077558 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2859,6 +2859,11 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) return err; } + if (!clen) { + /* Corrupted file. */ + return -EINVAL; + } + if (lcn == RESIDENT_LCN) { /* clen - resident size in bytes. clen == ni->vfs_inode.i_size */ if (offset >= clen) { @@ -2915,10 +2920,6 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) } } - if (!clen) { - /* Corrupted file. */ - return -EINVAL; - } } vbo = (u64)vcn << cluster_bits; From 5a35454179fe1041d9cd286f5d320ce0d448c12a Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Thu, 4 Jun 2026 21:19:30 -0700 Subject: [PATCH 3255/5214] fs/ntfs3: resize log->one_page_buf when adopting on-disk page size log_replay() allocates log->one_page_buf using the page size that was chosen from the host PAGE_SIZE: log->one_page_buf = kmalloc(log->page_size, GFP_NOFS); Later, when a restart area is found, the log page size recorded on disk is adopted: t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); } If the on-disk page size is larger than the size used for the initial allocation, log->page_size grows but one_page_buf is left at its original, smaller size. A subsequent unaligned read_log_page() then reads log->page_size bytes into the undersized scratch buffer: page_buf = page_off ? log->one_page_buf : *buffer; err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf, log->page_size, NULL, &log->read_ahead); overflowing the allocation. This is reachable when mounting a dirty NTFS volume whose log was formatted with a page size larger than the buffer initially allocated on the mounting host (for example a 64K-log volume mounted on a host that allocated a 4K scratch buffer). Grow one_page_buf when the adopted on-disk page size exceeds the size used for the initial allocation. On krealloc() failure the original buffer is left intact and freed by the existing error path. Fixes: b46acd6a6a627 ("fs/ntfs3: Add NTFS journal") Reported-by: Carol L Soto Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index e5ff99a2e6a2..f038c799e7ac 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4005,9 +4005,28 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) */ t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { + u32 old_page_size = log->page_size; + log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); + + /* + * If the adopted on-disk page size is larger than the size used + * to allocate one_page_buf above, grow the scratch buffer so a + * later read_log_page() cannot overflow it. + */ + if (log->page_size > old_page_size) { + void *buf; + + buf = krealloc(log->one_page_buf, log->page_size, + GFP_NOFS); + if (!buf) { + err = -ENOMEM; + goto out; + } + log->one_page_buf = buf; + } } if (log->page_size != t32 || From 5b08dccecf825cbf905f348bc6ccb497507e28e2 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Wed, 10 Jun 2026 12:31:01 +0200 Subject: [PATCH 3256/5214] ntfs3: reject direct userspace writes to reserved $LX* xattrs NTFS3 uses $LXUID, $LXGID, $LXMOD and $LXDEV as internal WSL permission metadata and reloads them into i_uid, i_gid and i_mode from ntfs_get_wsl_perm(). Because the empty-prefix xattr handler also lets file owners call setxattr() on these names directly, an unprivileged writer on a writable ntfs3 mount can plant root ownership and S_ISUID on their own file and gain euid 0 after inode reload. Reject direct userspace writes to the reserved $LX* names. Internal ntfs3 metadata updates are unchanged because ntfs_save_wsl_perm() writes them via ntfs_set_ea() directly. Signed-off-by: Zhen Yan [almaz.alexandrovich@paragon-software.com: added an additional check for non privileged users] Signed-off-by: Konstantin Komarov --- fs/ntfs3/xattr.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 7e5118247660..04814dd29375 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -851,6 +851,12 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, return err; } +static bool ntfs_is_reserved_lxattr(const char *name) +{ + return !strcmp(name, "$LXUID") || !strcmp(name, "$LXGID") || + !strcmp(name, "$LXMOD") || !strcmp(name, "$LXDEV"); +} + /* * ntfs_setxattr - inode_operations::setxattr */ @@ -957,6 +963,12 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, goto out; } + /* Do not allow non privileged users to change $LXUID/$LXGID... */ + if (ntfs_is_reserved_lxattr(name) && !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto out; + } + /* Deal with NTFS extended attribute. */ err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0, NULL); From 4be6cbeb93d26994bd1827ddbce391e3c4395c8f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 7 Jun 2026 18:57:45 +0100 Subject: [PATCH 3257/5214] KVM: arm64: nv: Avoid dereferencing NULL VNCR pseudo-TLB VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and either can race against a vcpu not being onlined yet (no pseudo-TLB allocated). Similarly, the TLB might be invalid, and the invalidation should be skipped in this case. Both kvm_invalidate_vncr_ipa() and kvm_invalidate_vncr_va() are expected to perform the same checks, except that the latter doesn't check for the allocation and blindly dereferences the pointer. Solve this by introducing a new iterator built on top of the usual kvm_for_each_vcpu() that checks for both of the above conditions, and convert the two users to it. Reported-by: Hyunwoo Kim Link: https://lore.kernel.org/r/aiUvSbrWndQeUPc8@v4bel Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260607175745.297793-1-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index f8d3f3a72328..690b8e856416 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -908,9 +908,21 @@ static void invalidate_vncr(struct vncr_tlb *vt) clear_fixmap(vncr_fixmap(vt->cpu)); } +/* + * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and + * either can race against a vcpu not being onlined yet (no pseudo-TLB + * allocated). Similarly, the TLB might be invalid. Skip those, as they + * obviously don't participate in the invalidation at this stage. + */ +#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm) \ + kvm_for_each_vcpu(idx, vcpup, kvm) \ + if (((tlbp) = vcpup->arch.vncr_tlb) && \ + (tlbp)->valid) + static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); @@ -918,24 +930,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) return; - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 ipa_start, ipa_end, ipa_size; - /* - * Careful here: We end-up here from an MMU notifier, - * and this can race against a vcpu not being onlined - * yet, without the pseudo-TLB being allocated. - * - * Skip those, as they obviously don't participate in - * the invalidation at this stage. - */ - if (!vt) - continue; - - if (!vt->valid) - continue; - ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); ipa_start = vt->wr.pa & ~(ipa_size - 1); @@ -965,17 +962,14 @@ static void invalidate_vncr_va(struct kvm *kvm, struct s1e2_tlbi_scope *scope) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 va_start, va_end, va_size; - if (!vt->valid) - continue; - va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); va_start = vt->gva & ~(va_size - 1); From c39023ca9a447f09c072080efc84d6874c2275c9 Mon Sep 17 00:00:00 2001 From: Nikolay Metchev Date: Tue, 9 Jun 2026 22:33:09 +0100 Subject: [PATCH 3258/5214] platform/x86: intel-hid: Add HP ProBook x360 440 G1 to button_array_table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The volume rocker buttons on the HP ProBook x360 440 G1 convertible emit events 0xc4-0xc7 via the intel-hid ACPI device (INT33D5). These codes are only present in intel_array_keymap, which is used when the "5 button array" input device exists. On this machine button_array_present() returns false because the firmware does not advertise the array through the HEBC method, so notify_handler() routes the events to a NULL priv->array and they are dropped as "unknown event 0xc4". As a result the side volume keys do nothing. Add the machine to button_array_table so the array device is created and the volume rocker emits KEY_VOLUMEUP / KEY_VOLUMEDOWN. This is equivalent to booting with the enable_5_button_array=1 module parameter, which was used to confirm the fix on the affected hardware. Signed-off-by: Nikolay Metchev Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260609213309.445019-1-nikolaymetchev@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/hid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 085093506dda..73874d4369c1 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -156,6 +156,13 @@ static const struct dmi_system_id button_array_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 4"), }, }, + { + .ident = "HP ProBook x360 440 G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 440 G1"), + }, + }, { } }; From c3a2521bcc8e4913a246132f276e5ce1251dd1cd Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 9 Jun 2026 20:09:51 +0530 Subject: [PATCH 3259/5214] platform/x86/amd/pmc: Use per-SoC cpu_info struct for SMU mailbox and IP info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the scattered per-field assignments in amd_pmc_get_ip_info() with a single amd_pmc_cpu_info struct capturing all SoC-specific parameters such as SMU offsets, IP block table, and OS hint. Define static const instances per SoC variant and embed them as driver_data in the PCI ID table via PCI_DEVICE_DATA(). Consolidate pci_match_id() into amd_pmc_set_cpu_info(), which assigns driver_data directly to cpu_info, the switch falls through only for 1Ah M20H/M60H variants requiring boot_cpu_data.x86_model detection to distinguish the M70 sub-variant. Add scratch_reg to amd_pmc_cpu_info and populate it for each SoC variant, allowing amd_pmc_idlemask_read() to drop its cpu_id switch in favour of a single cpu_info->scratch_reg lookup. Move dev->cpu_id assignment into amd_pmc_set_cpu_info() so it is valid before the switch statement. Handle SP/SHP directly in the switch since their NULL driver_data bypasses the early return, and remove the duplicate check from probe. Remove amd_pmc_get_os_hint() and use cpu_info->os_hint directly at call sites and rename AMD_CPU_ID_* to PCI_DEVICE_ID_AMD_CPU_ID_* with backward compatibility aliases. Reviewed-by: Mario Limonciello (AMD) Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://patch.msgid.link/20260609143952.2999707-2-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 231 +++++++++++++++++------------ drivers/platform/x86/amd/pmc/pmc.h | 66 ++++++--- 2 files changed, 177 insertions(+), 120 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index cae3fcafd4d7..92e36a6161a9 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -85,6 +85,89 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"VPE", BIT(21)}, }; +/* CPU info structures for different SoC variants */ +static const struct amd_pmc_cpu_info amd_pco_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_PCO, +}; + +static const struct amd_pmc_cpu_info amd_czn_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .scratch_reg = AMD_PMC_SCRATCH_REG_CZN, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_vg_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_yc_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .scratch_reg = AMD_PMC_SCRATCH_REG_YC, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_ps_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 21, + .scratch_reg = AMD_PMC_SCRATCH_REG_YC, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_1ah_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = ARRAY_SIZE(soc15_ip_blk), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_1ah_m70_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = ARRAY_SIZE(soc15_ip_blk_v2), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk_v2, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_DEVICE_DATA(AMD, CPU_ID_PCO, &amd_pco_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_CZN, &amd_czn_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_VG, &amd_vg_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_YC, &amd_yc_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_CB, &amd_yc_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_PS, &amd_ps_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_SP, NULL) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_SHP, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, NULL) }, + { } +}; + static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); @@ -101,35 +184,40 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3 iowrite32(val, dev->regbase + reg_offset); } -static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) +static int amd_pmc_set_cpu_info(struct amd_pmc_dev *dev, struct pci_dev *rdev) { + const struct pci_device_id *id; + + id = pci_match_id(pmc_pci_ids, rdev); + if (!id) + return -ENODEV; + + dev->cpu_id = rdev->device; + + if (id->driver_data) { + dev->cpu_info = (const struct amd_pmc_cpu_info *)id->driver_data; + return 0; + } + + /* Special case: 1Ah M20H/M60H needs x86_model detection */ switch (dev->cpu_id) { - case AMD_CPU_ID_PCO: - case AMD_CPU_ID_RN: - case AMD_CPU_ID_VG: - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - dev->num_ips = 12; - dev->ips_ptr = soc15_ip_blk; - dev->smu_msg = 0x538; - break; - case AMD_CPU_ID_PS: - dev->num_ips = 21; - dev->ips_ptr = soc15_ip_blk; - dev->smu_msg = 0x538; - break; case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - if (boot_cpu_data.x86_model == 0x70) { - dev->num_ips = ARRAY_SIZE(soc15_ip_blk_v2); - dev->ips_ptr = soc15_ip_blk_v2; - } else { - dev->num_ips = ARRAY_SIZE(soc15_ip_blk); - dev->ips_ptr = soc15_ip_blk; - } - dev->smu_msg = 0x938; + if (boot_cpu_data.x86_model == 0x70) + dev->cpu_info = &amd_1ah_m70_cpu_info; + else + dev->cpu_info = &amd_1ah_cpu_info; break; + case AMD_CPU_ID_SP: + case AMD_CPU_ID_SHP: + dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); + return -ENODEV; + default: + dev_err(dev->dev, "Unknown CPU ID: 0x%x\n", dev->cpu_id); + return -ENODEV; } + + return 0; } static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev) @@ -296,9 +384,9 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) table.timeto_resume_to_os_lastcapture); seq_puts(s, "\n=== Active time (in us) ===\n"); - for (idx = 0 ; idx < dev->num_ips ; idx++) { - if (dev->ips_ptr[idx].bit_mask & dev->active_ips) - seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name, + for (idx = 0 ; idx < dev->cpu_info->num_ips ; idx++) { + if (dev->cpu_info->ips_ptr[idx].bit_mask & dev->active_ips) + seq_printf(s, "%-8s : %lld\n", dev->cpu_info->ips_ptr[idx].name, table.timecondition_notmet_lastcapture[idx]); } @@ -347,32 +435,22 @@ static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, u32 val; int rc; - switch (pdev->cpu_id) { - case AMD_CPU_ID_CZN: - /* we haven't yet read SMU version */ + /* we haven't yet read SMU version */ + if (pdev->cpu_id == AMD_CPU_ID_CZN) { if (!pdev->major) { rc = amd_pmc_get_smu_version(pdev); if (rc) return rc; } - if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)) - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); - else + if (!(pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))) return -EINVAL; - break; - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); - break; - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_1AH); - break; - default: - return -EINVAL; } + if (!pdev->cpu_info->scratch_reg) + return -EINVAL; + + val = amd_pmc_reg_read(pdev, pdev->cpu_info->scratch_reg); + if (dev) pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val); @@ -425,9 +503,9 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) argument = dev->stb_arg.arg; response = dev->stb_arg.resp; } else { - message = dev->smu_msg; - argument = AMD_PMC_REGISTER_ARGUMENT; - response = AMD_PMC_REGISTER_RESPONSE; + message = dev->cpu_info->smu_msg; + argument = dev->cpu_info->smu_arg; + response = dev->cpu_info->smu_rsp; } value = amd_pmc_reg_read(dev, response); @@ -452,9 +530,9 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r argument = dev->stb_arg.arg; response = dev->stb_arg.resp; } else { - message = dev->smu_msg; - argument = AMD_PMC_REGISTER_ARGUMENT; - response = AMD_PMC_REGISTER_RESPONSE; + message = dev->cpu_info->smu_msg; + argument = dev->cpu_info->smu_arg; + response = dev->cpu_info->smu_rsp; } /* Wait until we get a valid response */ @@ -512,23 +590,6 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r return rc; } -static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) -{ - switch (dev->cpu_id) { - case AMD_CPU_ID_PCO: - return MSG_OS_HINT_PCO; - case AMD_CPU_ID_RN: - case AMD_CPU_ID_VG: - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - return MSG_OS_HINT_RN; - } - return -EINVAL; -} - static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev) { struct device *d; @@ -602,7 +663,6 @@ static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; int rc; - u8 msg; u32 arg = 1; /* Reset and Start SMU logging - to monitor the s0i3 stats */ @@ -617,8 +677,7 @@ static void amd_pmc_s2idle_prepare(void) } } - msg = amd_pmc_get_os_hint(pdev); - rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false); + rc = amd_pmc_send_cmd(pdev, arg, NULL, pdev->cpu_info->os_hint, false); if (rc) { dev_err(pdev->dev, "suspend failed: %d\n", rc); return; @@ -659,10 +718,8 @@ static void amd_pmc_s2idle_restore(void) { struct amd_pmc_dev *pdev = &pmc; int rc; - u8 msg; - msg = amd_pmc_get_os_hint(pdev); - rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false); + rc = amd_pmc_send_cmd(pdev, 0, NULL, pdev->cpu_info->os_hint, false); if (rc) dev_err(pdev->dev, "resume failed: %d\n", rc); @@ -709,22 +766,6 @@ static const struct dev_pm_ops amd_pmc_pm = { .suspend = amd_pmc_suspend_handler, }; -static const struct pci_device_id pmc_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SHP) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_VG) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) }, - { } -}; - static int amd_pmc_probe(struct platform_device *pdev) { struct amd_pmc_dev *dev = &pmc; @@ -736,17 +777,14 @@ static int amd_pmc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); - if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) { + if (!rdev) { err = -ENODEV; goto err_pci_dev_put; } - dev->cpu_id = rdev->device; - if (dev->cpu_id == AMD_CPU_ID_SP || dev->cpu_id == AMD_CPU_ID_SHP) { - dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); - err = -ENODEV; + err = amd_pmc_set_cpu_info(dev, rdev); + if (err) goto err_pci_dev_put; - } dev->rdev = rdev; err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); @@ -778,9 +816,6 @@ static int amd_pmc_probe(struct platform_device *pdev) if (err) goto err_pci_dev_put; - /* Get num of IP blocks within the SoC */ - amd_pmc_get_ip_info(dev); - platform_set_drvdata(pdev, dev); if (IS_ENABLED(CONFIG_SUSPEND)) { err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index fe3f53eb5955..616faddc70b7 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -17,6 +17,10 @@ /* SMU communication registers */ #define AMD_PMC_REGISTER_RESPONSE 0x980 #define AMD_PMC_REGISTER_ARGUMENT 0x9BC +#define AMD_PMC_REGISTER_MESSAGE 0x538 + +/* SMU communication registers for 1Ah 20h SoC */ +#define AMD_PMC_REGISTER_MSG_1AH_20H 0x938 /* PMC Scratch Registers */ #define AMD_PMC_SCRATCH_REG_CZN 0x94 @@ -90,6 +94,22 @@ struct stb_arg { u32 resp; }; +struct amd_pmc_bit_map { + const char *name; + u32 bit_mask; +}; + +/* SoC-specific information */ +struct amd_pmc_cpu_info { + u32 smu_msg; + u32 smu_arg; + u32 smu_rsp; + u32 num_ips; + u32 scratch_reg; + const struct amd_pmc_bit_map *ips_ptr; + u8 os_hint; +}; + struct amd_pmc_dev { void __iomem *regbase; void __iomem *smu_virt_addr; @@ -99,9 +119,6 @@ struct amd_pmc_dev { u32 cpu_id; u32 dram_size; u32 active_ips; - const struct amd_pmc_bit_map *ips_ptr; - u32 num_ips; - u32 smu_msg; /* SMU version information */ u8 smu_program; u8 major; @@ -116,11 +133,7 @@ struct amd_pmc_dev { bool disable_8042_wakeup; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; -}; - -struct amd_pmc_bit_map { - const char *name; - u32 bit_mask; + const struct amd_pmc_cpu_info *cpu_info; }; struct smu_metrics { @@ -151,20 +164,29 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); -/* List of supported CPU ids */ -#define AMD_CPU_ID_RV 0x15D0 -#define AMD_CPU_ID_RN 0x1630 -#define AMD_CPU_ID_PCO AMD_CPU_ID_RV -#define AMD_CPU_ID_CZN AMD_CPU_ID_RN -#define AMD_CPU_ID_VG 0x1645 -#define AMD_CPU_ID_YC 0x14B5 -#define AMD_CPU_ID_CB 0x14D8 -#define AMD_CPU_ID_PS 0x14E8 -#define AMD_CPU_ID_SP 0x14A4 -#define AMD_CPU_ID_SHP 0x153A -#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 -#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 -#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c +/* List of supported CPU/device IDs */ +#define PCI_DEVICE_ID_AMD_CPU_ID_PCO 0x15D0 +#define PCI_DEVICE_ID_AMD_CPU_ID_CZN 0x1630 +#define PCI_DEVICE_ID_AMD_CPU_ID_VG 0x1645 +#define PCI_DEVICE_ID_AMD_CPU_ID_YC 0x14B5 +#define PCI_DEVICE_ID_AMD_CPU_ID_CB 0x14D8 +#define PCI_DEVICE_ID_AMD_CPU_ID_PS 0x14E8 +#define PCI_DEVICE_ID_AMD_CPU_ID_SP 0x14A4 +#define PCI_DEVICE_ID_AMD_CPU_ID_SHP 0x153A + +/* Backward compatibility aliases */ +#define AMD_CPU_ID_PCO PCI_DEVICE_ID_AMD_CPU_ID_PCO +#define AMD_CPU_ID_CZN PCI_DEVICE_ID_AMD_CPU_ID_CZN +#define AMD_CPU_ID_VG PCI_DEVICE_ID_AMD_CPU_ID_VG +#define AMD_CPU_ID_YC PCI_DEVICE_ID_AMD_CPU_ID_YC +#define AMD_CPU_ID_CB PCI_DEVICE_ID_AMD_CPU_ID_CB +#define AMD_CPU_ID_PS PCI_DEVICE_ID_AMD_CPU_ID_PS +#define AMD_CPU_ID_SP PCI_DEVICE_ID_AMD_CPU_ID_SP +#define AMD_CPU_ID_SHP PCI_DEVICE_ID_AMD_CPU_ID_SHP + +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 +#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 +#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c int amd_stb_s2d_init(struct amd_pmc_dev *dev); int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf); From 043af31c8d3031bbeb77dfdc6373005ef3b8a23b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 9 Jun 2026 20:09:52 +0530 Subject: [PATCH 3260/5214] platform/x86/amd/pmc: Add PMC driver support for AMD 1Ah M80H SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 1Ah M80H SoC uses a different set of SMU mailbox register offsets compared to the existing 1Ah variants: message at 0xA10, argument at 0xA18, and response at 0xA14. Add amd_1ah_m80_cpu_info with these offsets, wire it into the PCI ID table via PCI_DEVICE_DATA(), populate scratch_reg field with AMD_PMC_SCRATCH_REG_1AH and add the corresponding ACPI ID AMDI000C. Reviewed-by: Mario Limonciello (AMD) Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://patch.msgid.link/20260609143952.2999707-3-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 12 ++++++++++++ drivers/platform/x86/amd/pmc/pmc.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 92e36a6161a9..ccb37383b337 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -154,6 +154,16 @@ static const struct amd_pmc_cpu_info amd_1ah_m70_cpu_info = { .os_hint = MSG_OS_HINT_RN, }; +static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_80H, + .smu_arg = AMD_PMC_REGISTER_ARG_1AH_80H, + .smu_rsp = AMD_PMC_REGISTER_RSP_1AH_80H, + .num_ips = ARRAY_SIZE(soc15_ip_blk), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE_DATA(AMD, CPU_ID_PCO, &amd_pco_cpu_info) }, { PCI_DEVICE_DATA(AMD, CPU_ID_CZN, &amd_czn_cpu_info) }, @@ -165,6 +175,7 @@ static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE_DATA(AMD, CPU_ID_SHP, NULL) }, { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, NULL) }, { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_1ah_m80_cpu_info) }, { } }; @@ -860,6 +871,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = { {"AMDI0009", 0}, {"AMDI000A", 0}, {"AMDI000B", 0}, + {"AMDI000C", 0}, {"AMD0004", 0}, {"AMD0005", 0}, { } diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 616faddc70b7..36756e25b4bd 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -22,6 +22,11 @@ /* SMU communication registers for 1Ah 20h SoC */ #define AMD_PMC_REGISTER_MSG_1AH_20H 0x938 +/* SMU communication registers for 1Ah 80h SoC */ +#define AMD_PMC_REGISTER_MSG_1AH_80H 0xA10 +#define AMD_PMC_REGISTER_ARG_1AH_80H 0xA18 +#define AMD_PMC_REGISTER_RSP_1AH_80H 0xA14 + /* PMC Scratch Registers */ #define AMD_PMC_SCRATCH_REG_CZN 0x94 #define AMD_PMC_SCRATCH_REG_YC 0xD14 @@ -186,6 +191,7 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); #define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 #define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 +#define PCI_DEVICE_ID_AMD_1AH_M80H_ROOT 0x115b #define PCI_DEVICE_ID_AMD_MP2_STB 0x172c int amd_stb_s2d_init(struct amd_pmc_dev *dev); From 6e9cab2247e5b243ae2d907ce7c948a8a9c8d61a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 9 Jun 2026 16:14:19 +0800 Subject: [PATCH 3261/5214] platform/x86: dell-laptop: fix missing cleanups in init error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dell_init() initializes several resources after dell_setup_rfkill(), including the optional touchpad LED, keyboard backlight LED, battery hook, debugfs directory and dell-laptop notifier. If a later LED or backlight registration fails, the error path only tears down the battery hook and rfkill resources. This leaves the notifier, debugfs directory, keyboard backlight LED and optional touchpad LED registered after dell_init() returns an error. Add the missing cleanup calls before tearing down rfkill. Fixes: 9c656b07997f ("platform/x86: dell-*: Call new led hw_changed API on kbd brightness change") Fixes: 037accfa14b2 ("dell-laptop: Add debugfs support") Fixes: 2d8b90be4f1c ("dell-laptop: support Synaptics/Alps touchpad led") Fixes: 6cff8d60aa0a ("platform: x86: dell-laptop: Add support for keyboard backlight") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260609081419.1995169-1-lihaoxiang@isrc.iscas.ac.cn Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-laptop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 7fc3bbb8c4a4..89e85c7f7132 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2560,7 +2560,12 @@ static int __init dell_init(void) if (mute_led_registered) led_classdev_unregister(&mute_led_cdev); fail_led: + dell_laptop_unregister_notifier(&dell_laptop_notifier); + debugfs_remove_recursive(dell_laptop_dir); dell_battery_exit(); + kbd_led_exit(); + if (quirks && quirks->touchpad_led) + touchpad_led_exit(); dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); From 375bbbbd112af028ee0b45d833a6233c23d19bbf Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 11:31:49 -0700 Subject: [PATCH 3262/5214] platform/x86/intel/vsec: Restore BAR fallback for header walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base_addr refactor changed intel_vsec_walk_header() to pass info->base_addr as the discovery-table base address. For the PCI VSEC driver this info comes from driver_data, but exported callers may provide their own static headers and leave base_addr unset. For xe, this made the discovery-table base address zero instead of the BAR selected by header->tbir, preventing PMT endpoints from being created. Restore the previous behavior for the header-walk path by falling back to pci_resource_start(pdev, header->tbir) when base_addr is not specified. Keep explicit base_addr override behavior unchanged. This preserves the refactor structure while fixing the functional regression in manual-header users. Fixes: 904b333fc51c ("platform/x86/intel/vsec: Refactor base_addr handling") Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260529183150.129744-1-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 1657834fd275..3ae4557b32b4 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -482,10 +482,25 @@ static int intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; + u64 base_addr; int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(dev, *header, info, info->base_addr); + if (info->base_addr) { + base_addr = info->base_addr; + } else { + struct pci_dev *pdev; + + if (!dev_is_pci(dev)) { + dev_err(dev, "non-PCI device without a base address\n"); + return -EINVAL; + } + + pdev = to_pci_dev(dev); + base_addr = pci_resource_start(pdev, (*header)->tbir); + } + + ret = intel_vsec_register_device(dev, *header, info, base_addr); if (ret) return ret; } From c085d82613d5618814b84406c8b2d64f1bc305e7 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 02:49:05 +0900 Subject: [PATCH 3263/5214] platform/x86: intel-hid: Protect ACPI notify handler against recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") ACPI notify handlers like the intel-hid notify_handler() may run on multiple CPU cores racing with themselves. On convertibles and detachables (matched by DMI chassis-type 31 and 32 in dmi_auto_add_switch[]) the SW_TABLET_MODE input device is registered lazily from notify_handler() on the first tablet-mode event, via intel_hid_switches_setup(). When two such events race on different CPUs both can pass the !priv->switches check and register the priv->switches input device twice, resulting in a duplicate sysfs entry and a subsequent NULL pointer dereference. This is the same class of bug fixed by commit e075c3b13a0a ("platform/x86: intel-vbtn: Protect ACPI notify handler against recursion") for the sibling intel-vbtn driver. Protect intel-hid notify_handler() from racing with itself with a mutex to fix this. Fixes: e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") Cc: stable@vger.kernel.org Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260605174905.131095-1-sammiee5311@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/hid.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 73874d4369c1..acd42e639fa4 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -7,11 +7,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -237,6 +239,7 @@ static const struct dmi_system_id dmi_auto_add_switch[] = { }; struct intel_hid_priv { + struct mutex mutex; /* Avoid notify_handler() racing with itself */ struct input_dev *input_dev; struct input_dev *array; struct input_dev *switches; @@ -572,6 +575,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) struct key_entry *ke; int err; + guard(mutex)(&priv->mutex); + /* * Some convertible have unreliable VGBS return which could cause incorrect * SW_TABLET_MODE report, in these cases we enable support when receiving @@ -727,6 +732,10 @@ static int intel_hid_probe(struct platform_device *device) return -ENOMEM; dev_set_drvdata(&device->dev, priv); + err = devm_mutex_init(&device->dev, &priv->mutex); + if (err) + return err; + /* See dual_accel_detect.h for more info on the dual_accel check. */ if (enable_sw_tablet_mode == TABLET_SW_AUTO) { if (dmi_check_system(dmi_vgbs_allow_list)) From 6b63520ed14b17bbe9c2103debbd2152dde1fba3 Mon Sep 17 00:00:00 2001 From: Guixiong Wei Date: Tue, 2 Jun 2026 10:07:52 +0800 Subject: [PATCH 3264/5214] platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the last CPU of a legacy uncore die goes offline, uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug re-add, uncore_freq_add_entry() still populates sysfs attributes before assigning the new control CPU. As a result, the current frequency read returns -ENXIO and current_freq_khz is omitted from the recreated sysfs group. Assign control_cpu before the initial read paths and before create_attr_group() so sysfs recreation uses the new online CPU. If sysfs creation fails, restore control_cpu to -1 to keep the error path state consistent. Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") Cc: stable@vger.kernel.org Signed-off-by: Guixiong Wei Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 3b554418a7a3..4bcafedf68ef 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -285,15 +285,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) data->package_id, data->die_id); } + /* + * Set the control CPU before any read path so entry recreation after CPU + * hotplug can populate read-only attributes from the new online CPU. + */ + data->control_cpu = cpu; uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); ret = create_attr_group(data, data->name); if (ret) { + data->control_cpu = -1; if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) ida_free(&intel_uncore_ida, data->seqnum_id); } else { - data->control_cpu = cpu; data->valid = true; } From 4b54e2374d1bd82031cef9784e125a7100a32499 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 8 Jun 2026 09:11:08 +0100 Subject: [PATCH 3265/5214] KVM: arm64: nv: Hold kvm->mmu_lock while initialising vcpu->arch.vncr_tlb Sashiko reports that there is a race between initialising vncr_tlb and making use of it, as we don't hold the mmu_lock at this point. Additionally, it identifies a memory leak, should userspace repeatedly invokes the KVM_RUN ioctl after a failure of kvm_arch_vcpu_run_pid_change(), as we assign vncr_tlb blindly on first run, irrespective of prior allocations. Slap the two bugs in one go by taking the kvm->mmu_lock on assigning vncr_tlb, preventing the race for good, and by checking that vncr_tlb is indeed NULL prior to allocation. Reported-by: Sashiko Link: https://lore.kernel.org/r/20260607180815.85FBC1F00893@smtp.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260608081108.2244133-1-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 690b8e856416..326adf404d98 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1253,8 +1253,20 @@ int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu) if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) return 0; - vcpu->arch.vncr_tlb = kzalloc_obj(*vcpu->arch.vncr_tlb, - GFP_KERNEL_ACCOUNT); + if (!vcpu->arch.vncr_tlb) { + struct vncr_tlb *vt = kzalloc_obj(*vcpu->arch.vncr_tlb, + GFP_KERNEL_ACCOUNT); + + /* + * Taking the lock on assignment ensures that the TLB is + * seen as initialised when following the pointer (release + * semantics of the unlock), and avoids having acquires on + * each user which already take the lock. + */ + scoped_guard(write_lock, &vcpu->kvm->mmu_lock) + vcpu->arch.vncr_tlb = vt; + } + if (!vcpu->arch.vncr_tlb) return -ENOMEM; From 2565a28cdcdcb035e151d285efcba26bccb3726e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 28 May 2026 13:45:21 -0700 Subject: [PATCH 3266/5214] platform/x86: ISST: Restore SST-PP control to all domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SST-PP control offset is only restored to power domain 0 after resume. During suspend, control values are read and stored for all power domains. Use pd_info->sst_base instead of power_domain_info->sst_base, which only points to power domain 0 base address. Fixes: dc7901b5a156 ("platform/x86: ISST: Store and restore all domains data") Reported-by: Yi Lai Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260528204521.3531456-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index b804cb753f94..24334ae70d82 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1804,7 +1804,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE)) continue; - writeq(pd_info->saved_pp_control, power_domain_info->sst_base + + writeq(pd_info->saved_pp_control, pd_info->sst_base + pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); } } From 85c1fcfa740d4c737f5575fc7251883e54227a51 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Wed, 20 May 2026 16:48:57 +0800 Subject: [PATCH 3267/5214] PCI: imx6: Integrate new pwrctrl API Integrate the PCI pwrctrl framework into the pci-imx6 driver to provide standardized power management for PCI devices. Legacy regulator handling (vpcie-supply at controller level) is maintained for backward compatibility with existing device trees. New device trees should specify power supplies at the Root Port level to utilize the pwrctrl framework. Signed-off-by: Sherry Sun Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://patch.msgid.link/20260520084904.2424253-2-sherry.sun@oss.nxp.com --- drivers/pci/controller/dwc/Kconfig | 1 + drivers/pci/controller/dwc/pci-imx6.c | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index f2fde13107f2..327b0dc65550 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -114,6 +114,7 @@ config PCI_IMX6_HOST depends on PCI_MSI select PCIE_DW_HOST select PCI_IMX6 + select PCI_PWRCTRL_GENERIC help Enables support for the PCIe controller in the i.MX SoCs to work in Root Complex mode. The PCI controller on i.MX is based diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index cce78a7a1624..63637fa5f108 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1363,6 +1364,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) return ret; } + /* Legacy regulator handling for DT backward compatibility. */ if (imx_pcie->vpcie) { ret = regulator_enable(imx_pcie->vpcie); if (ret) { @@ -1372,10 +1374,22 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } } + ret = pci_pwrctrl_create_devices(dev); + if (ret) { + dev_err(dev, "failed to create pwrctrl devices\n"); + goto err_reg_disable; + } + + ret = pci_pwrctrl_power_on_devices(dev); + if (ret) { + dev_err(dev, "failed to power on pwrctrl devices\n"); + goto err_pwrctrl_destroy; + } + ret = imx_pcie_clk_enable(imx_pcie); if (ret) { dev_err(dev, "unable to enable pcie clocks: %d\n", ret); - goto err_reg_disable; + goto err_pwrctrl_power_off; } if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) { @@ -1437,6 +1451,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) phy_exit(imx_pcie->phy); err_clk_disable: imx_pcie_clk_disable(imx_pcie); +err_pwrctrl_power_off: + pci_pwrctrl_power_off_devices(dev); +err_pwrctrl_destroy: + if (ret != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(dev); err_reg_disable: if (imx_pcie->vpcie) regulator_disable(imx_pcie->vpcie); @@ -1455,6 +1474,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) } imx_pcie_clk_disable(imx_pcie); + pci_pwrctrl_power_off_devices(pci->dev); if (imx_pcie->vpcie) regulator_disable(imx_pcie->vpcie); } @@ -1966,6 +1986,8 @@ static void imx_pcie_shutdown(struct platform_device *pdev) /* bring down link, so bootloader gets clean state in case of reboot */ imx_pcie_assert_core_reset(imx_pcie); imx_pcie_assert_perst(imx_pcie, true); + pci_pwrctrl_power_off_devices(&pdev->dev); + pci_pwrctrl_destroy_devices(&pdev->dev); } static const struct imx_pcie_drvdata drvdata[] = { From ac48c49116d3de84fabc224c6e43f08740b1460d Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 10 Jun 2026 08:53:32 +0000 Subject: [PATCH 3268/5214] nvme: make some sysfs diagnostic structures static Building with C=1 generates the following warnings: drivers/nvme/host/sysfs.c:397:25: warning: symbol 'dev_attr_io_errors' was not declared. Should it be static? drivers/nvme/host/sysfs.c:444:30: warning: symbol 'nvme_ns_diag_attr_group' was not declared. Should it be static? drivers/nvme/host/sysfs.c:1150:25: warning: symbol 'dev_attr_adm_errors' was not declared. Should it be static? Make those structures static. Closes: https://lore.kernel.org/oe-kbuild-all/202606101329.T3zXNqdy-lkp@intel.com/ Reviewed-by: Nilay Shroff Reviewed-by: Christoph Hellwig Signed-off-by: John Garry Signed-off-by: Keith Busch --- drivers/nvme/host/sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index 933a5adfb7af..75b2d69b5957 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -394,7 +394,7 @@ static ssize_t nvme_io_errors_store(struct device *dev, return count; } -struct device_attribute dev_attr_io_errors = +static struct device_attribute dev_attr_io_errors = __ATTR(command_error_count, 0644, nvme_io_errors_show, nvme_io_errors_store); @@ -441,7 +441,7 @@ static umode_t nvme_ns_diag_attrs_are_visible(struct kobject *kobj, return a->mode; } -const struct attribute_group nvme_ns_diag_attr_group = { +static const struct attribute_group nvme_ns_diag_attr_group = { .name = "diag", .attrs = nvme_ns_diag_attrs, .is_visible = nvme_ns_diag_attrs_are_visible, @@ -1147,7 +1147,7 @@ static ssize_t nvme_adm_errors_store(struct device *dev, return count; } -struct device_attribute dev_attr_adm_errors = +static struct device_attribute dev_attr_adm_errors = __ATTR(command_error_count, 0644, nvme_adm_errors_show, nvme_adm_errors_store); From 869567bcbe2dcc790860e05fc0e0c5e415bb22c2 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 10 Jun 2026 11:16:08 +0000 Subject: [PATCH 3269/5214] nvme: make nvme_add_ns{_head}_cdev return void The return code from nvme_add_ns_head_cdev() and nvme_add_ns_cdev() is never checked, so make those functions return void. A cdev add failure is tolerated during initialization, and flags NVME_NS_CDEV_LIVE and NVME_NSHEAD_CDEV_LIVE are for determining whether a cdev needs to be deleted during un-initialization. Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: John Garry Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 13 +++++-------- drivers/nvme/host/multipath.c | 13 +++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index c6930e43cfea..eb268148acec 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3943,24 +3943,21 @@ static const struct file_operations nvme_ns_chr_fops = { .uring_cmd_iopoll = nvme_ns_chr_uring_cmd_iopoll, }; -static int nvme_add_ns_cdev(struct nvme_ns *ns) +static void nvme_add_ns_cdev(struct nvme_ns *ns) { - int ret; char name[32]; ns->cdev_device.parent = ns->ctrl->device; snprintf(name, sizeof(name), "ng%dn%d", ns->ctrl->instance, ns->head->instance); - ret = nvme_cdev_add(name, &ns->cdev, &ns->cdev_device, - &nvme_ns_chr_fops, ns->ctrl->ops->module); - if (ret) { + if (nvme_cdev_add(name, &ns->cdev, &ns->cdev_device, + &nvme_ns_chr_fops, ns->ctrl->ops->module)) { dev_err(ns->ctrl->device, "Unable to create the %s device\n", name); - } else { - set_bit(NVME_NS_CDEV_LIVE, &ns->flags); + return; } - return ret; + set_bit(NVME_NS_CDEV_LIVE, &ns->flags); } static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 9cd49e2f760d..9b9a657fa330 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -639,24 +639,21 @@ static const struct file_operations nvme_ns_head_chr_fops = { .uring_cmd_iopoll = nvme_ns_chr_uring_cmd_iopoll, }; -static int nvme_add_ns_head_cdev(struct nvme_ns_head *head) +static void nvme_add_ns_head_cdev(struct nvme_ns_head *head) { - int ret; char name[32]; head->cdev_device.parent = &head->subsys->dev; snprintf(name, sizeof(name), "ng%dn%d", head->subsys->instance, head->instance); - ret = nvme_cdev_add(name, &head->cdev, &head->cdev_device, - &nvme_ns_head_chr_fops, THIS_MODULE); - if (ret) { + if (nvme_cdev_add(name, &head->cdev, &head->cdev_device, + &nvme_ns_head_chr_fops, THIS_MODULE)) { dev_err(disk_to_dev(head->disk), "Unable to create the %s device\n", name); - } else { - set_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags); + return; } - return ret; + set_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags); } static void nvme_partition_scan_work(struct work_struct *work) From ee38469f88492df99e1d97f03aa40ecfd218934f Mon Sep 17 00:00:00 2001 From: Mohamed Khalfella Date: Thu, 28 May 2026 11:27:34 +0200 Subject: [PATCH 3270/5214] nvme-fc: Do not cancel requests in io target before it is initialized A new nvme-fc controller in CONNECTING state sees admin request timeout schedules ctrl->ioerr_work to abort inflight requests. This ends up calling __nvme_fc_abort_outstanding_ios() which aborts requests in both admin and io tagsets. In case fc_ctrl->tag_set was not initialized we see the warning below. This is because ctrl.queue_count is initialized early in nvme_fc_alloc_ctrl(). nvme nvme0: NVME-FC{0}: starting error recovery Connectivity Loss INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe lpfc 0000:ab:00.0: queue 0 connect admin queue failed (-6). you didn't initialize this object before use? turning off the locking correctness validator. Workqueue: nvme-reset-wq nvme_fc_ctrl_ioerr_work [nvme_fc] Call Trace: dump_stack_lvl+0x57/0x80 register_lock_class+0x567/0x580 __lock_acquire+0x330/0xb90 lock_acquire.part.0+0xad/0x210 blk_mq_tagset_busy_iter+0xf9/0xc00 __nvme_fc_abort_outstanding_ios+0x23f/0x320 [nvme_fc] nvme_fc_ctrl_ioerr_work+0x172/0x210 [nvme_fc] process_one_work+0x82c/0x1450 worker_thread+0x5ee/0xfd0 kthread+0x3a0/0x750 ret_from_fork+0x439/0x670 ret_from_fork_asm+0x1a/0x30 Update the check in __nvme_fc_abort_outstanding_ios() confirm that io tagset was created before iterating over busy requests. Also make sure to cancel ctrl->ioerr_work before removing io tagset. Reviewed-by: Randy Jennings Reviewed-by: Hannes Reinecke Reviewed-by: Daniel Wagner Reviewed-by: Christoph Hellwig Signed-off-by: Mohamed Khalfella Signed-off-by: James Smart Signed-off-by: Maurizio Lombardi Signed-off-by: Keith Busch --- drivers/nvme/host/fc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 2c9a6d3c9797..04363b9c4489 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2461,7 +2461,7 @@ __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) * io requests back to the block layer as part of normal completions * (but with error status). */ - if (ctrl->ctrl.queue_count > 1) { + if (ctrl->ctrl.queue_count > 1 && ctrl->ctrl.tagset) { nvme_quiesce_io_queues(&ctrl->ctrl); nvme_sync_io_queues(&ctrl->ctrl); blk_mq_tagset_busy_iter(&ctrl->tag_set, @@ -2900,6 +2900,11 @@ nvme_fc_create_io_queues(struct nvme_fc_ctrl *ctrl) out_delete_hw_queues: nvme_fc_delete_hw_io_queues(ctrl); out_cleanup_tagset: + /* + * In CONNECTING state ctrl->ioerr_work will abort both admin + * and io tagsets. Cancel it first before removing io tagset. + */ + cancel_work_sync(&ctrl->ioerr_work); nvme_remove_io_tag_set(&ctrl->ctrl); nvme_fc_free_io_queues(ctrl); From 0a012113bb3a44482c163f16f4db03ccaa37a339 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:37:52 -0300 Subject: [PATCH 3271/5214] perf tools: Fix get_max_num() size_t underflow on empty sysfs file get_max_num() reads a sysfs file (cpu/possible, cpu/present, or node/possible) and scans backward from the end to find the last number. If the file is empty, filename__read_str() returns num == 0. The loop `while (--num)` decrements the size_t from 0 to SIZE_MAX, reading backward across the heap until a comma or hyphen is found or unmapped memory is hit. Add an early return for empty files before the backward scan. Fixes: 7780c25bae59fd04 ("perf tools: Allow ability to map cpus to nodes easily") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Don Zickus Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 21fa781b03cc..1fab00ec4a59 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -448,6 +448,12 @@ static int get_max_num(char *path, int *max) buf[num] = '\0'; + /* empty file — nothing to parse */ + if (num == 0) { + err = -1; + goto out; + } + /* start on the right, to find highest node num */ while (--num) { if ((buf[num] == ',') || (buf[num] == '-')) { From 7953a3a9b8e02e98c6e6958f291d0ae22393e46a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:43:52 -0300 Subject: [PATCH 3272/5214] perf tools: Use scnprintf() in cpu_map__snprint() to prevent overflow cpu_map__snprint() accumulates snprintf() return values in ret. snprintf() returns the number of characters that *would have been written* on truncation, not the actual count. When a fragmented CPU list exceeds the buffer, ret grows past size, causing `size - ret` to underflow (both are size_t), and subsequent snprintf() calls write past the end of the caller's stack buffer. Switch to scnprintf() which returns the actual number of characters written, making ret accumulation safe by construction. Fixes: a24020e6b7cf6eb8 ("perf tools: Change cpu_map__fprintf output") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 1fab00ec4a59..23ebe9b97f8e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -692,21 +692,21 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) if (start == -1) { start = i; if (last) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, i).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, i).cpu); } } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { int end = i - 1; if (start == end) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, start).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, start).cpu); } else { - ret += snprintf(buf + ret, size - ret, - "%s%d-%d", COMMA, - perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d-%d", COMMA, + perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); } first = false; start = i; From 5484b43a0ec8231c36fba6ead654cb72dbba8b8f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:57:49 -0300 Subject: [PATCH 3273/5214] perf tools: Use perf_env__get_cpu_topology() in machine__resolve() machine__resolve() accesses env->cpu[al->cpu].socket_id after checking al->cpu >= 0 and env->cpu != NULL, but without validating al->cpu against env->nr_cpus_avail. Since al->cpu comes from the untrusted perf.data sample, a crafted file with a large CPU index causes an out-of-bounds heap read. Use perf_env__get_cpu_topology() which validates both NULL and bounds. Also bounds-check al->cpu before the cast to struct perf_cpu (int16_t): without this, values like 65536 silently truncate to 0, bypassing the accessor's internal check and returning CPU 0's topology. Fixes: 0c4c4debb0adda4c ("perf tools: Add processor socket info to hist_entry and addr_location") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Kan Liang Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 66f4843bb235..ea75816d126a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -14,6 +14,7 @@ #include #include "cpumap.h" #include "dso.h" +#include "env.h" #include "event.h" #include "debug.h" #include "hist.h" @@ -836,8 +837,18 @@ int machine__resolve(struct machine *machine, struct addr_location *al, if (al->cpu >= 0) { struct perf_env *env = machine->env; - if (env && env->cpu) - al->socket = env->cpu[al->cpu].socket_id; + /* + * Bounds-check al->cpu (s32) before casting to struct perf_cpu + * (int16_t): without this, e.g. 65536 truncates to 0 and silently + * returns CPU 0's topology. Can go once perf_cpu.cpu is widened. + */ + if (env && al->cpu < env->nr_cpus_avail) { + struct cpu_topology_map *topo; + + topo = perf_env__get_cpu_topology(env, (struct perf_cpu){ al->cpu }); + if (topo) + al->socket = topo->socket_id; + } } /* Account for possible out-of-order switch events. */ From 779575bc35c687697ba69e904f2cd22e60112534 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 9 Jun 2026 14:24:31 -0400 Subject: [PATCH 3274/5214] nvmet-auth: reject short AUTH_RECEIVE buffers nvmet_execute_auth_receive() trusts the AUTH_RECEIVE allocation length after checking only that it is nonzero and matches the transfer length. In the SUCCESS1 and FAILURE1/default states, that lets a remote NVMe-oF initiator reach the fixed-size DH-HMAC-CHAP response builders with a kmalloc() buffer shorter than the response, so nvmet_auth_success1() and nvmet_auth_failure1() write past the allocation; both only WARN_ON the short length and then format the message anyway. Impact: A remote NVMe-oF initiator with access to an auth-enabled target can trigger a 16-byte heap out-of-bounds write via a one-byte AUTH_RECEIVE allocation length. Compute the minimum response length for the current DH-HMAC-CHAP step in nvmet_auth_receive_data_len() and report a zero data length when the host-supplied allocation length is shorter, so the existing zero-length check in nvmet_execute_auth_receive() rejects the command before any builder runs. The SUCCESS1 minimum is sizeof(struct nvmf_auth_dhchap_success1_data) plus the HMAC hash length, because the response hash is written into the rval[] flexible-array tail, so the minimum is state dependent rather than a flat sizeof. CHALLENGE keeps its existing variable-length guard in nvmet_auth_challenge(). This is reachable only when in-band DH-HMAC-CHAP authentication is configured on the target. Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Hannes Reinecke Signed-off-by: Michael Bommarito Signed-off-by: Keith Busch --- drivers/nvme/target/fabrics-cmd-auth.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 0a85acf1e5c7..45820a12750d 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -493,7 +493,31 @@ static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) u32 nvmet_auth_receive_data_len(struct nvmet_req *req) { - return le32_to_cpu(req->cmd->auth_receive.al); + struct nvmet_ctrl *ctrl = req->sq->ctrl; + u32 al = le32_to_cpu(req->cmd->auth_receive.al); + u32 min_len; + + /* + * Reject too-short al before kmalloc(al), since the SUCCESS1 and + * FAILURE1/default builders write fixed response headers into it. + */ + switch (req->sq->dhchap_step) { + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: + return al; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: + min_len = sizeof(struct nvmf_auth_dhchap_success1_data); + if (req->sq->dhchap_c2) + min_len += nvme_auth_hmac_hash_len(ctrl->shash_id); + break; + default: + min_len = sizeof(struct nvmf_auth_dhchap_failure_data); + break; + } + + if (al < min_len) + return 0; + + return al; } void nvmet_execute_auth_receive(struct nvmet_req *req) From 7d953c75f0a3f905aadf3675c9394a5b9d9897bf Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Mon, 16 Mar 2026 17:44:41 +0100 Subject: [PATCH 3275/5214] nvmet-tcp: handle TCP_CLOSING state in nvmet_tcp_state_change When an NVMe/TCP connection shuts down, the underlying TCP socket can enter the TCP_CLOSING state (state 11). Currently, the nvmet_tcp_state_change() callback does not explicitly handle this state, which results in harmless but noisy kernel warnings: nvmet_tcp: queue 2 unhandled state 11 Add TCP_CLOSING to the switch statement alongside TCP_FIN_WAIT2 and TCP_LAST_ACK to silently ignore the state transition. Signed-off-by: Maurizio Lombardi Signed-off-by: Keith Busch --- drivers/nvme/target/tcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 3568fa9a0905..08140f2476d2 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1678,6 +1678,7 @@ static void nvmet_tcp_state_change(struct sock *sk) switch (sk->sk_state) { case TCP_FIN_WAIT2: case TCP_LAST_ACK: + case TCP_CLOSING: break; case TCP_FIN_WAIT1: case TCP_CLOSE_WAIT: From a9e900bc5c5914aca750afafa459363e575d3046 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:31:51 -0300 Subject: [PATCH 3276/5214] perf tools: NULL bitmap pointers after bitmap_free() Two call sites free bitmaps without NULLing the pointer, risking double-free if the structure is reused or cleanup is called twice: - mmap__munmap(): map->affinity_mask.bits - record__mmap_cpu_mask_free(): mask->bits Set each pointer to NULL after bitmap_free(). Fixes: 8384a2600c7ddfc8 ("perf record: Adapt affinity to machines with #CPUs > 1K") Fixes: f466e5ed6c356d1d ("perf record: Extend --threads command line option") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Alexey Budankov Cc: Alexey Bayduraev Cc: Arnaldo Carvalho de Melo Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 1 + tools/perf/util/mmap.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a33c78f030d9..e91539055675 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3084,6 +3084,7 @@ static int record__mmap_cpu_mask_alloc(struct mmap_cpu_mask *mask, int nr_bits) static void record__mmap_cpu_mask_free(struct mmap_cpu_mask *mask) { bitmap_free(mask->bits); + mask->bits = NULL; mask->nbits = 0; } diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index d64aec6c7c84..358e70c4f3ed 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -238,6 +238,8 @@ static void perf_mmap__aio_munmap(struct mmap *map __maybe_unused) void mmap__munmap(struct mmap *map) { bitmap_free(map->affinity_mask.bits); + map->affinity_mask.bits = NULL; + map->affinity_mask.nbits = 0; zstd_fini(&map->zstd_data); From 6ba90ce2069ae923b0ec787aebdf2d786e5d2a58 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 21 May 2026 10:12:56 +0100 Subject: [PATCH 3277/5214] PCI: rcar-host: Remove unused LIST_HEAD(res) Remove the unused LIST_HEAD(res) declaration from rcar_pcie_hw_enable(). The macro instantiation defines an unused 'struct list_head res' variable, which conflicts with a valid resource loop-local 'struct resource *res' declaration further down in the function, triggering a compiler variable shadowing warning: drivers/pci/controller/pcie-rcar-host.c:357:34: warning: declaration of 'res' shadows a previous local [-Wshadow] 357 | struct resource *res = win->res; Fixes: ce351636c67f75a9 ("PCI: rcar: Add suspend/resume") Signed-off-by: Lad Prabhakar Signed-off-by: Manivannan Sadhasivam Reviewed-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Link: https://patch.msgid.link/20260521091256.15737-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/pci/controller/pcie-rcar-host.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 213028052aa5..cd9171eebc28 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -346,7 +346,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) struct rcar_pcie *pcie = &host->pcie; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; - LIST_HEAD(res); int i = 0; /* Try setting 5 GT/s link speed */ From 76a5a0042df2a3da353beda6dc505e28506cbe3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 9 Jun 2026 16:53:25 +0200 Subject: [PATCH 3278/5214] Input: Drop unused assignments from pnp_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly assigning .driver_data in drivers that don't use this member is silly and a bit irritating. Drop these. Also simplify the list terminator entry to be just empty to match what most other device_id tables do. There is no changed semantic, not even a change in the compiled result. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/f987c14dea1d3236d3889e5cf96c01eef6a2445d.1781016727.git.u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/ns558.c | 46 +++++++++++----------- drivers/input/serio/i8042-acpipnpio.h | 56 +++++++++++++-------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index fdece6ec1df3..f70a96c4f1fd 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -148,29 +148,29 @@ static int ns558_isa_probe(int io) #ifdef CONFIG_PNP static const struct pnp_device_id pnp_devids[] = { - { .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */ - { .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */ - { .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */ - { .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */ - { .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */ - { .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */ - { .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */ - { .id = "CSC0001", .driver_data = 0 }, /* CS4232 */ - { .id = "CSC000f", .driver_data = 0 }, /* CS4236 */ - { .id = "CSC0101", .driver_data = 0 }, /* CS4327 */ - { .id = "CTL7001", .driver_data = 0 }, /* SB16 */ - { .id = "CTL7002", .driver_data = 0 }, /* AWE64 */ - { .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */ - { .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */ - { .id = "ESS0001", .driver_data = 0 }, /* ES1869 */ - { .id = "ESS0005", .driver_data = 0 }, /* ES1878 */ - { .id = "ESS6880", .driver_data = 0 }, /* ES688 */ - { .id = "IBM0012", .driver_data = 0 }, /* CS4232 */ - { .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */ - { .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */ - { .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */ - { .id = "PNPb02f", .driver_data = 0 }, /* Generic */ - { .id = "", }, + { .id = "@P@0001" }, /* ALS 100 */ + { .id = "@P@0020" }, /* ALS 200 */ + { .id = "@P@1001" }, /* ALS 100+ */ + { .id = "@P@2001" }, /* ALS 120 */ + { .id = "ASB16fd" }, /* AdLib NSC16 */ + { .id = "AZT3001" }, /* AZT1008 */ + { .id = "CDC0001" }, /* Opl3-SAx */ + { .id = "CSC0001" }, /* CS4232 */ + { .id = "CSC000f" }, /* CS4236 */ + { .id = "CSC0101" }, /* CS4327 */ + { .id = "CTL7001" }, /* SB16 */ + { .id = "CTL7002" }, /* AWE64 */ + { .id = "CTL7005" }, /* Vibra16 */ + { .id = "ENS2020" }, /* SoundscapeVIVO */ + { .id = "ESS0001" }, /* ES1869 */ + { .id = "ESS0005" }, /* ES1878 */ + { .id = "ESS6880" }, /* ES688 */ + { .id = "IBM0012" }, /* CS4232 */ + { .id = "OPT0001" }, /* OPTi Audio16 */ + { .id = "YMH0006" }, /* Opl3-SA */ + { .id = "YMH0022" }, /* Opl3-SAx */ + { .id = "PNPb02f" }, /* Generic */ + { } }; MODULE_DEVICE_TABLE(pnp, pnp_devids); diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index 8ebdf4fb9030..412f82d7a303 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -1539,22 +1539,22 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * } static const struct pnp_device_id pnp_kbd_devids[] = { - { .id = "PNP0300", .driver_data = 0 }, - { .id = "PNP0301", .driver_data = 0 }, - { .id = "PNP0302", .driver_data = 0 }, - { .id = "PNP0303", .driver_data = 0 }, - { .id = "PNP0304", .driver_data = 0 }, - { .id = "PNP0305", .driver_data = 0 }, - { .id = "PNP0306", .driver_data = 0 }, - { .id = "PNP0309", .driver_data = 0 }, - { .id = "PNP030a", .driver_data = 0 }, - { .id = "PNP030b", .driver_data = 0 }, - { .id = "PNP0320", .driver_data = 0 }, - { .id = "PNP0343", .driver_data = 0 }, - { .id = "PNP0344", .driver_data = 0 }, - { .id = "PNP0345", .driver_data = 0 }, - { .id = "CPQA0D7", .driver_data = 0 }, - { .id = "", }, + { .id = "PNP0300" }, + { .id = "PNP0301" }, + { .id = "PNP0302" }, + { .id = "PNP0303" }, + { .id = "PNP0304" }, + { .id = "PNP0305" }, + { .id = "PNP0306" }, + { .id = "PNP0309" }, + { .id = "PNP030a" }, + { .id = "PNP030b" }, + { .id = "PNP0320" }, + { .id = "PNP0343" }, + { .id = "PNP0344" }, + { .id = "PNP0345" }, + { .id = "CPQA0D7" }, + { } }; MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids); @@ -1569,18 +1569,18 @@ static struct pnp_driver i8042_pnp_kbd_driver = { }; static const struct pnp_device_id pnp_aux_devids[] = { - { .id = "AUI0200", .driver_data = 0 }, - { .id = "FJC6000", .driver_data = 0 }, - { .id = "FJC6001", .driver_data = 0 }, - { .id = "PNP0f03", .driver_data = 0 }, - { .id = "PNP0f0b", .driver_data = 0 }, - { .id = "PNP0f0e", .driver_data = 0 }, - { .id = "PNP0f12", .driver_data = 0 }, - { .id = "PNP0f13", .driver_data = 0 }, - { .id = "PNP0f19", .driver_data = 0 }, - { .id = "PNP0f1c", .driver_data = 0 }, - { .id = "SYN0801", .driver_data = 0 }, - { .id = "", }, + { .id = "AUI0200" }, + { .id = "FJC6000" }, + { .id = "FJC6001" }, + { .id = "PNP0f03" }, + { .id = "PNP0f0b" }, + { .id = "PNP0f0e" }, + { .id = "PNP0f12" }, + { .id = "PNP0f13" }, + { .id = "PNP0f19" }, + { .id = "PNP0f1c" }, + { .id = "SYN0801" }, + { }, }; MODULE_DEVICE_TABLE(pnp, pnp_aux_devids); From 0e7041eb96fa9c07ec57dd8b4088f03b7939cdd3 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 21:21:01 -0700 Subject: [PATCH 3279/5214] cxl/port: update reference to removed CONFIG_PROVE_CXL_LOCKING A comment in drivers/cxl/port.c refers to CONFIG_PROVE_CXL_LOCKING, which was removed in commit 38a34e10768c ("cxl: Drop cxl_device_lock()"). That commit switched CXL subsystem locking to custom lock classes, which can be validated via the standard CONFIG_PROVE_LOCKING option. Update the comment to reflect this. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Dan Williams Reviewed-by: Richard Cheng Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260610042101.222349-1-enelsonmoore@gmail.com Signed-off-by: Dave Jiang --- drivers/cxl/port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index ada51948d52f..99cf77b6b699 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -18,7 +18,7 @@ * firmware) are managed in this drivers context. Each driver instance * is responsible for tearing down the driver context of immediate * descendant ports. The locking for this is validated by - * CONFIG_PROVE_CXL_LOCKING. + * CONFIG_PROVE_LOCKING. * * The primary service this driver provides is presenting APIs to other * drivers to utilize the decoders, and indicating to userspace (via bind From 71a1def165267bc0947d4236f7336f490739c379 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:15:08 -0700 Subject: [PATCH 3280/5214] cxl/test: Verify cmd->size_in before accessing payload Several mock mailbox handlers access input payload fields before verifying that cmd->size_in is large enough for the corresponding structure. To ensure invalid commands are rejected before any payload data is consumed, add missing size checks and move existing checks ahead of the first payload field access. [dj: Updated commit log per Alison's comments. ] Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Fixes: d1dca858f058 ("cxl/test: Add generic mock events") Fixes: f6448cb5f2f3 ("tools/testing/cxl: add firmware update emulation to CXL memdevs") Fixes: e77e9c107978 ("cxl/test: Add Get Feature support to cxl_test") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Suggested-by: sashiko-bot Tested-by: Alison Schofield Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 39 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32..2e9a5f151e98 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -312,12 +312,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; + struct cxl_mbox_clear_event_payload *pl; struct mock_event_log *log; - u8 log_type = pl->event_log; + u8 log_type; u16 handle; int nr; + if (cmd->size_in < sizeof(*pl)) + return -EINVAL; + + pl = cmd->payload_in; + log_type = pl->event_log; if (log_type >= CXL_EVENT_TYPE_MAX) return -EINVAL; @@ -574,14 +579,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd) static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; - struct cxl_mbox_get_log *gl = cmd->payload_in; - u32 offset = le32_to_cpu(gl->offset); - u32 length = le32_to_cpu(gl->length); uuid_t uuid = DEFINE_CXL_CEL_UUID; + struct cxl_mbox_get_log *gl; void *data = &mock_cel; + u32 offset; + u32 length; if (cmd->size_in < sizeof(*gl)) return -EINVAL; + + gl = cmd->payload_in; + offset = le32_to_cpu(gl->offset); + length = le32_to_cpu(gl->length); + if (length > cxl_mbox->payload_size) return -EINVAL; if (offset + length > sizeof(mock_cel)) @@ -1336,10 +1346,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata, static int mock_transfer_fw(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; + struct cxl_mbox_transfer_fw *transfer; void *fw = mdata->fw; size_t offset, length; + if (cmd->size_in < sizeof(*transfer)) + return -EINVAL; + + transfer = cmd->payload_in; offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; length = cmd->size_in - sizeof(*transfer); if (offset + length > FW_SIZE) @@ -1415,11 +1429,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { struct vendor_test_feat *output = cmd->payload_out; - struct cxl_mbox_get_feat_in *input = cmd->payload_in; - u16 offset = le16_to_cpu(input->offset); - u16 count = le16_to_cpu(input->count); + struct cxl_mbox_get_feat_in *input; + u16 offset; + u16 count; u8 *ptr; + if (cmd->size_in < sizeof(*input)) + return -EINVAL; + + input = cmd->payload_in; + offset = le16_to_cpu(input->offset); + count = le16_to_cpu(input->count); + if (offset > sizeof(*output)) { cmd->return_code = CXL_MBOX_CMD_RC_INPUT; return -EINVAL; From 17222f981c16efa201c6806c0308a9d362a2388c Mon Sep 17 00:00:00 2001 From: Elliot Tester Date: Thu, 14 May 2026 21:33:02 +0200 Subject: [PATCH 3281/5214] Input: remove changelogs There is no need to keep changelogs in driver sources, they are tracked in git. Signed-off-by: Elliot Tester Link: https://patch.msgid.link/20260514193302.117488-1-elliotctester1@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/yealink.c | 9 --------- drivers/input/tablet/aiptek.c | 31 ------------------------------- 2 files changed, 40 deletions(-) diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 8786ed8b3565..560f895a00cd 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -22,15 +22,6 @@ * - Olivier Vandorpe, for providing the usbb2k-api. * - Martin Diehl, for spotting my memory allocation bug. * - * History: - * 20050527 henk First version, functional keyboard. Keyboard events - * will pop-up on the ../input/eventX bus. - * 20050531 henk Added led, LCD, dialtone and sysfs interface. - * 20050610 henk Cleanups, make it ready for public consumption. - * 20050630 henk Cleanups, fixes in response to comments. - * 20050701 henk sysfs write serialisation, fix potential unload races - * 20050801 henk Added ringtone, restructure USB - * 20050816 henk Merge 2.6.13-rc6 */ #include diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index c850b5890070..0c1acb9e6b43 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -16,37 +16,6 @@ * * Many thanks to Oliver Kuechemann for his support. * - * ChangeLog: - * v0.1 - Initial release - * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) - * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) - * Released to Linux 2.4.19 and 2.5.x - * v0.4 - Rewrote substantial portions of the code to deal with - * corrected control sequences, timing, dynamic configuration, - * support of 6000U - 12000U, procfs, and macro key support - * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) - * v1.0 - Added support for diagnostic messages, count of messages - * received from URB - Mar-8-2003, Bryan W. Headley - * v1.1 - added support for tablet resolution, changed DV and proximity - * some corrections - Jun-22-2003, martin schneebacher - * - Added support for the sysfs interface, deprecating the - * procfs interface for 2.5.x kernel. Also added support for - * Wheel command. Bryan W. Headley July-15-2003. - * v1.2 - Reworked jitter timer as a kernel thread. - * Bryan W. Headley November-28-2003/Jan-10-2004. - * v1.3 - Repaired issue of kernel thread going nuts on single-processor - * machines, introduced programmableDelay as a command line - * parameter. Feb 7 2004, Bryan W. Headley. - * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of - * Rene van Paassen. Added reporting of physical pointer device - * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know - * for reports 1, 6.) - * what physical device reports for reports 1, 6.) Also enabled - * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser". - * Feb 20, 2004, Bryan W. Headley. - * v1.5 - Added previousJitterable, so we don't do jitter delay when the - * user is holding a button down for periods of time. - * * NOTE: * This kernel driver is augmented by the "Aiptek" XFree86 input * driver for your X server, as well as the Gaiptek GUI Front-end From 81eafcada109b653977c4dfbd2b6a72470025a01 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 10:12:38 -0700 Subject: [PATCH 3282/5214] cxl/test: Fix integer overflow in mock LSA bounds checks Pre-existing issue discovered by sashiko-bot. mock_get_lsa() and mock_set_lsa() validate the requested LSA range with "offset + length > LSA_SIZE". Both offset and length are u32 and, in mock_get_lsa(), both are taken directly from the user-supplied payload. The addition is evaluated modulo 2^32, so a large offset combined with a small length wraps around and passes the check. Rewrite the checks to first bound offset, then compare length against the remaining LSA size. Suggested-by: sashiko-bot Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 2e9a5f151e98..9a7cd3f46a1e 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1063,7 +1063,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1083,7 +1083,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); From 60f065dbaf46e65830da62a0041761f0c039e086 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:44:26 -0700 Subject: [PATCH 3283/5214] cxl/test: Zero out LSA backing memory to avoid leaking to user Memory through vmalloc() is not zeroed out. When this memory is copied into output payload, it leaks memory content to user. Use vzalloc() instead to zero out the memory. Suggested-by: sashiko-bot Link: https://lore.kernel.org/linux-cxl/20260605173146.2B9A31F00893@smtp.kernel.org/ Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260605184426.4070913-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 9a7cd3f46a1e..739343cd5802 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1724,7 +1724,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); From 4477dc01fcfc7f404772a67e0c1e056541ceb61d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:48:13 -0300 Subject: [PATCH 3284/5214] perf sched: Bounds-check prio before test_bit() in timehist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit timehist_skip_sample() reads prio from untrusted tracepoint data via perf_sample__intval(sample, "prev_prio") without bounds validation. A crafted perf.data with prev_prio >= MAX_PRIO (140) causes test_bit() to read past the end of the prio_bitmap, which is only MAX_PRIO bits. Add a prio >= 0 guard before the test_bit() call and skip out-of-range values (>= MAX_PRIO) that can never match the user's filter set. The original prio != -1 already let all negatives other than -1 through (after an undefined-behavior bitmap read); the new prio >= 0 guard preserves that pass-through behavior — negative means "no priority info", so the event is shown unfiltered — while fixing the OOB. Values >= MAX_PRIO are skipped because they cannot be represented in the filter bitmap. Fixes: 9b3a48bbe20d9692 ("perf sched timehist: Add --prio option") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Yang Jihong Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 1ff01f03d2ad..caf05863a54e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2645,7 +2645,9 @@ static bool timehist_skip_sample(struct perf_sched *sched, else if (evsel__name_is(sample->evsel, "sched:sched_switch")) prio = perf_sample__intval(sample, "prev_prio"); - if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { + /* negative prio means no info; out-of-range prio can't match the filter */ + if (prio >= 0 && + (prio >= MAX_PRIO || !test_bit(prio, sched->prio_bitmap))) { rc = true; sched->skipped_samples++; } From d9b99dc8148e0c1f5da3942131b47e0d21187a32 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:49:16 -0300 Subject: [PATCH 3285/5214] perf sched: Fix idle-hist callchain display using wrong rb_first variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit timehist_print_idlehist_callchain() calls rb_first_cached() on sorted_root, but the sort function (callchain_param.sort) populates it via rb_insert_color() on the plain rb_root member — not the cached variant. This means rb_leftmost is never set, so rb_first_cached() always returns NULL and the entire callchain summary is silently dropped from --idle-hist output. The original code in ba957ebb54893aca ("perf sched timehist: Show callchains for idle stat") was correct — it used struct rb_root and rb_first(). The bug was introduced when sorted_root was converted to rb_root_cached without converting the sort insertion path to use rb_insert_color_cached(). Use rb_first(&root->rb_root) to match how the tree was populated. Fixes: cb4c13a5137766c3 ("perf sched: Use cached rbtrees") Reported-by: sashiko-bot Cc: Davidlohr Bueso Cc: Namhyung Kim Acked-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index caf05863a54e..5f16caebd964 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3130,7 +3130,8 @@ static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root) size_t ret = 0; FILE *fp = stdout; struct callchain_node *chain; - struct rb_node *rb_node = rb_first_cached(root); + /* sort() uses rb_insert_color() on rb_root, not rb_root_cached */ + struct rb_node *rb_node = rb_first(&root->rb_root); printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains"); printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line, From b145137fec13dc8fc7fcb14193ce395a1164e3a1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:51:42 -0300 Subject: [PATCH 3286/5214] perf tools: Add O_CLOEXEC to open() calls in DSO and ELF code open() calls in dso.c and symbol-elf.c omit O_CLOEXEC, which leaks file descriptors to child processes spawned during symbol resolution (e.g., addr2line, objdump). This can exhaust the fd limit during long profiling sessions or when processing many DSOs. Add O_CLOEXEC to all open() calls in both files (12 call sites). Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 4 ++-- tools/perf/util/symbol-elf.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 7dced896c64e..fb2e78fe2aa8 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -344,7 +344,7 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) - return open(name, O_RDONLY); + return open(name, O_RDONLY | O_CLOEXEC); fd = mkstemp(tmpbuf); if (fd < 0) { @@ -1911,7 +1911,7 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename, int saved_errno; nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - fd = open(symfs_filename, O_RDONLY); + fd = open(symfs_filename, O_RDONLY | O_CLOEXEC); saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd < 0) { diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 186e6d92ac3d..c2bdfd0003df 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -217,7 +217,7 @@ bool filename__has_section(const char *filename, const char *sec) GElf_Shdr shdr; bool found = false; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) return false; @@ -872,7 +872,7 @@ static int read_build_id(const char *filename, struct build_id *bid) if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -935,7 +935,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) size_t size = sizeof(bid->data); int fd, err = -1; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -995,7 +995,7 @@ int filename__read_debuglink(const char *filename, char *debuglink, if (err >= 0) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -1153,7 +1153,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, type = dso__symtab_type(dso); } else { - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_CLOEXEC); if (fd < 0) { *dso__load_errno(dso) = errno; return -1; @@ -1952,7 +1952,7 @@ static int kcore__open(struct kcore *kcore, const char *filename) { GElf_Ehdr *ehdr; - kcore->fd = open(filename, O_RDONLY); + kcore->fd = open(filename, O_RDONLY | O_CLOEXEC); if (kcore->fd == -1) return -1; @@ -1985,7 +1985,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, if (temp) kcore->fd = mkstemp(filename); else - kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) return -1; @@ -2461,11 +2461,11 @@ static int kcore_copy__compare_files(const char *from_filename, { int from, to, err = -1; - from = open(from_filename, O_RDONLY); + from = open(from_filename, O_RDONLY | O_CLOEXEC); if (from < 0) return -1; - to = open(to_filename, O_RDONLY); + to = open(to_filename, O_RDONLY | O_CLOEXEC); if (to < 0) goto out_close_from; @@ -2883,7 +2883,7 @@ int get_sdt_note_list(struct list_head *head, const char *target) Elf *elf; int fd, ret; - fd = open(target, O_RDONLY); + fd = open(target, O_RDONLY | O_CLOEXEC); if (fd < 0) return -EBADF; From cab3a9331ed0b3f884dd61c8a25b3cf123705982 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:23:15 -0300 Subject: [PATCH 3287/5214] perf bpf: Use scnprintf() in snprintf_hex() and synthesize_bpf_prog_name() Both functions accumulate formatted output via ret += snprintf(buf + ret, size - ret, ...). If the buffer is too small and snprintf() returns more than the remaining space, ret exceeds size and the next 'size - ret' underflows, causing snprintf() to write past the buffer end. Switch to scnprintf() which returns the actual number of bytes written, making the accumulation safe. Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index a27945c279ef..2c09842469f1 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -36,7 +36,7 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) size_t i; for (i = 0; i < len; i++) - ret += snprintf(buf + ret, size - ret, "%02x", data[i]); + ret += scnprintf(buf + ret, size - ret, "%02x", data[i]); return ret; } @@ -140,7 +140,7 @@ static int synthesize_bpf_prog_name(char *buf, int size, const struct btf_type *t; int name_len; - name_len = snprintf(buf, size, "bpf_prog_"); + name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); if (btf) { @@ -153,9 +153,10 @@ static int synthesize_bpf_prog_name(char *buf, int size, short_name = info->name; } else short_name = "F"; - if (short_name) - name_len += snprintf(buf + name_len, size - name_len, - "_%s", short_name); + if (short_name) { + name_len += scnprintf(buf + name_len, size - name_len, + "_%s", short_name); + } return name_len; } From 227a8748742f0263f1fe3131449b44563b77a209 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:35:28 -0300 Subject: [PATCH 3288/5214] perf hists: Fix snprintf() in hists__scnprintf_title() UID filter path hists__scnprintf_title() accumulates formatted output into a buffer using scnprintf() for all filter clauses except the UID filter, which uses snprintf(). If the buffer fills up and snprintf() returns more than the remaining space, printed exceeds size and the next 'size - printed' underflows, causing later scnprintf() calls to write past the buffer. Switch the UID filter clause to scnprintf() to match the rest of the function. Fixes: 25c312dbf88ca402 ("perf hists: Move hists__scnprintf_title() away from the TUI code") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Arnaldo Carvalho de Melo Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 811d68fa6770..df978c996b6c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2963,9 +2963,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); - if (hists->uid_filter_str) - printed += snprintf(bf + printed, size - printed, - ", UID: %s", hists->uid_filter_str); + if (hists->uid_filter_str) { + printed += scnprintf(bf + printed, size - printed, + ", UID: %s", hists->uid_filter_str); + } if (thread) { if (hists__has(hists, thread)) { printed += scnprintf(bf + printed, size - printed, From e33711d5e757011bb6d3506af4d6c97dad412b8f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:36:57 -0300 Subject: [PATCH 3289/5214] perf tools: Use scnprintf() in build_id__snprintf() and hwmon read_events() build_id__snprintf() and hwmon_pmu__read_events() accumulate formatted output via snprintf(), which returns the would-have-been-written count on truncation. In build_id__snprintf(), this inflates the return value beyond the buffer size. In hwmon_pmu__read_events(), len overshoots out_buf_len and the next 'out_buf_len - len' underflows. Switch both to scnprintf() which returns actual bytes written. In build_id__snprintf(), also tighten the loop guard from 'offs < bf_size' to 'offs + 1 < bf_size': since scnprintf() returns at most size-1, offs never reaches bf_size, and the original condition would spin doing zero-byte writes once the buffer fills. Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 7 +++++-- tools/perf/util/hwmon_pmu.c | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 8c0a9ae932aa..eb95ab90f974 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -93,8 +93,11 @@ int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size return 0; } - for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) - offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); + if (bf_size > 0) + bf[0] = '\0'; + + for (size_t i = 0; i < build_id->size && offs + 1 < bf_size; ++i) + offs += scnprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); return offs; } diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index fb3ffa8d32ad..dbf6a71af47f 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -442,12 +442,12 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si buf[read_len] = '\0'; val = strtoll(buf, /*endptr=*/NULL, 10); - len += snprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s", - len == 0 ? " " : ", ", - hwmon_item_strs[bit], - is_alarm ? "_alarm" : "", - (double)val / 1000.0, - hwmon_units[key.type]); + len += scnprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s", + len == 0 ? " " : ", ", + hwmon_item_strs[bit], + is_alarm ? "_alarm" : "", + (double)val / 1000.0, + hwmon_units[key.type]); } close(fd); } From 7435c4069a546a5d2421407b3d43fdc1790ce6cc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 15:32:32 -0300 Subject: [PATCH 3290/5214] libperf: Document code simplification case for widening struct perf_cpu Add a bullet point to the libperf ABI TODO explaining the code simplification benefit of widening struct perf_cpu.cpu from int16_t to int: the narrow type forces defensive truncation checks at every boundary where wider CPU indices are narrowed, and values > 32767 silently wrap to negative numbers (two's complement), bypassing bounds validation without them. Acked-by: Ian Rogers Cc: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/TODO | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/lib/perf/TODO b/tools/lib/perf/TODO index 486dd95dc572..e179728697d8 100644 --- a/tools/lib/perf/TODO +++ b/tools/lib/perf/TODO @@ -11,6 +11,14 @@ together. (x86_64 max is 8192, arm64 is 4096), but NR_CPUS limits keep growing. perf clamps to INT16_MAX in set_max_cpu_num() as a safety net. + - Code simplification: the int16_t forces defensive truncation + checks at every boundary where a wider CPU index (int from + sample->cpu, al->cpu, etc.) is narrowed into struct perf_cpu. + Without these checks, values > 32767 silently wrap to negative + numbers (two's complement), bypassing bounds validation. + Widening to int eliminates this entire class of silent + truncation bugs and removes the need for the INT16_MAX clamp + in set_max_cpu_num(). - Scope: struct perf_cpu is embedded everywhere — perf_cpu_map__cpu(), perf_cpu_map__min(), perf_cpu_map__max(), perf_cpu_map__has(), the for_each_cpu macros, and all internal callers. The perf_cpu_map From 7698e338f49a436072c3800183e7b37394958e58 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 19 Apr 2026 19:18:47 +0300 Subject: [PATCH 3291/5214] Input: ads7846 - restore half-duplex support On some boards, the SPI controller is limited to half-duplex and the driver fails spamming "ads7846 spi2.1: spi_sync --> -22". Restore half-duplex support with multiple SPI transfers. Fixes: 9c9509717b53 ("Input: ads7846 - convert to full duplex") Signed-off-by: Aaro Koskinen Link: https://patch.msgid.link/20260419161848.825831-2-aaro.koskinen@iki.fi Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 168 +++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 4b39f7212d35..d3b529333ca2 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -134,6 +134,9 @@ struct ads7846 { bool disabled; /* P: lock */ bool suspended; /* P: lock */ + int (*setup_spi_msg)(struct ads7846 *ts, + const struct ads7846_platform_data *pdata); + void (*read_state)(struct ads7846 *ts); int (*filter)(void *data, int data_idx, int *val); void *filter_data; int (*get_pendown_state)(void); @@ -789,6 +792,22 @@ static int ads7846_filter(struct ads7846 *ts) return 0; } +static int ads7846_filter_one(struct ads7846 *ts, unsigned int cmd_idx) +{ + struct ads7846_packet *packet = ts->packet; + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + int action, val; + + val = ads7846_get_value(&packet->rx[l->offset + l->count - 1]); + action = ts->filter(ts->filter_data, cmd_idx, &val); + if (action == ADS7846_FILTER_REPEAT) + return -EAGAIN; + else if (action != ADS7846_FILTER_OK) + return -EIO; + ads7846_set_cmd_val(ts, cmd_idx, val); + return 0; +} + static void ads7846_wait_for_hsync(struct ads7846 *ts) { if (ts->wait_for_sync) { @@ -811,6 +830,45 @@ static void ads7846_wait_for_hsync(struct ads7846 *ts) cpu_relax(); } +static void ads7846_halfd_read_state(struct ads7846 *ts) +{ + struct ads7846_packet *packet = ts->packet; + int msg_idx = 0; + + packet->ignore = false; + + while (msg_idx < ts->msg_count) { + int error; + + ads7846_wait_for_hsync(ts); + + error = spi_sync(ts->spi, &ts->msg[msg_idx]); + if (error) { + dev_err_ratelimited(&ts->spi->dev, "spi_sync --> %d\n", + error); + packet->ignore = true; + return; + } + + /* + * Last message is power down request, no need to convert + * or filter the value. + */ + if (msg_idx == ts->msg_count - 1) + break; + + error = ads7846_filter_one(ts, msg_idx); + if (error == -EAGAIN) { + continue; + } else if (error) { + packet->ignore = true; + msg_idx = ts->msg_count - 1; + } else { + msg_idx++; + } + } +} + static void ads7846_read_state(struct ads7846 *ts) { struct ads7846_packet *packet = ts->packet; @@ -939,7 +997,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) while (!ts->stopped && get_pendown_state(ts)) { /* pen is down, continue with the measurement */ - ads7846_read_state(ts); + ts->read_state(ts); if (!ts->stopped) ads7846_report_state(ts); @@ -1022,6 +1080,102 @@ static int ads7846_setup_pendown(struct spi_device *spi, return 0; } +/* + * Set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ +static int ads7846_halfd_spi_msg(struct ads7846 *ts, + const struct ads7846_platform_data *pdata) +{ + struct spi_message *m = ts->msg; + struct spi_transfer *x = ts->xfer; + struct ads7846_packet *packet = ts->packet; + int vref = pdata->keep_vref_on; + unsigned int offset = 0; + unsigned int cmd_idx, b; + size_t size = 0; + + if (pdata->settle_delay_usecs) + packet->count = 2; + else + packet->count = 1; + + if (ts->model == 7846) + packet->cmds = 5; /* x, y, z1, z2, pwdown */ + else + packet->cmds = 3; /* x, y, pwdown */ + + for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) { + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + unsigned int max_count; + + if (cmd_idx == packet->cmds - 1) { + cmd_idx = ADS7846_PWDOWN; + max_count = 1; + } else { + max_count = packet->count; + } + + l->offset = offset; + offset += max_count; + l->count = max_count; + l->skip = 0; + size += sizeof(*packet->rx) * max_count; + } + + /* We use two transfers per command. */ + if (ARRAY_SIZE(ts->xfer) < offset * 2) + return -ENOMEM; + + packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL); + if (!packet->rx) + return -ENOMEM; + + if (ts->model == 7873) { + /* + * The AD7873 is almost identical to the ADS7846 + * keep VREF off during differential/ratiometric + * conversion modes. + */ + ts->model = 7846; + vref = 0; + } + + ts->msg_count = 0; + + for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) { + struct ads7846_buf_layout *l = &packet->l[cmd_idx]; + u8 cmd; + + ts->msg_count++; + spi_message_init(m); + m->context = ts; + + if (cmd_idx == packet->cmds - 1) + cmd_idx = ADS7846_PWDOWN; + + cmd = ads7846_get_cmd(cmd_idx, vref); + + for (b = 0; b < l->count; b++) { + packet->rx[l->offset + b].cmd = cmd; + x->tx_buf = &packet->rx[l->offset + b].cmd; + x->len = 1; + spi_message_add_tail(x, m); + x++; + x->rx_buf = &packet->rx[l->offset + b].data; + x->len = 2; + if (b < l->count - 1 && l->count > 1) { + x->delay.value = pdata->settle_delay_usecs; + x->delay.unit = SPI_DELAY_UNIT_USECS; + } + spi_message_add_tail(x, m); + x++; + } + m++; + } + return 0; +} + /* * Set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. @@ -1236,6 +1390,14 @@ static int ads7846_probe(struct spi_device *spi) if (!ts) return -ENOMEM; + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { + ts->setup_spi_msg = ads7846_halfd_spi_msg; + ts->read_state = ads7846_halfd_read_state; + } else { + ts->setup_spi_msg = ads7846_setup_spi_msg; + ts->read_state = ads7846_read_state; + } + packet = devm_kzalloc(dev, sizeof(struct ads7846_packet), GFP_KERNEL); if (!packet) return -ENOMEM; @@ -1330,7 +1492,9 @@ static int ads7846_probe(struct spi_device *spi) ts->core_prop.swap_x_y = true; } - ads7846_setup_spi_msg(ts, pdata); + err = ts->setup_spi_msg(ts, pdata); + if (err) + return err; ts->reg = devm_regulator_get(dev, "vcc"); if (IS_ERR(ts->reg)) { From 6a4f64c3a3ada43e71ef1e06da89beb36bdaeefa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Wed, 3 Jun 2026 12:58:53 +0200 Subject: [PATCH 3292/5214] PCI: Avoid SBR for Qualcomm WCN6855/WCN7850 WiFi, SDX62/SDX65 modems Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems) do not properly support Secondary Bus Reset (SBR). Testing confirms this is device-specific, not deployment-specific: MediaTek MT7925e successfully uses bus reset through the same passive M.2-to-PCIe adapters where Qualcomm devices fail, proving PERST# is properly wired through the adapters. Prevent use of Secondary Bus Reset for these devices. Signed-off-by: Jose Ignacio Tornos Martinez Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/all/20260609163649.319755-4-jtornosm@redhat.com --- drivers/pci/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e49136ac5dbf..431c021d7414 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3790,6 +3790,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003e, quirk_no_bus_reset); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1103, quirk_no_bus_reset); /* WCN6855 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1107, quirk_no_bus_reset); /* WCN7850 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x0308, quirk_no_bus_reset); /* SDX62/SDX65 */ /* * Root port on some Cavium CN8xxx chips do not successfully complete a bus From 417efc520207615df1083567f674085be697b845 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 5 Jun 2026 16:40:33 -0400 Subject: [PATCH 3293/5214] xprtrdma: Convert send buffer free list to llist rpcrdma_buffer_get() and rpcrdma_buffer_put() both take rb_lock to pop/push from the rb_send_bufs free list. Under high I/O concurrency (e.g., nconnect=N with small random writes), this spinlock is contended between the request submission path and the transport completion path. Replace the list_head with an llist_head. The put side uses lockless llist_add(), which is safe for concurrent producers. The get side retains the spinlock to satisfy the llist single-consumer contract portably; submitters continue to serialize there. Completion handlers returning buffers no longer contend on rb_lock, eliminating contention on the return path. rb_lock remains for the MR free list and the tracking lists used during setup and teardown. rb_free_reps already uses llist_head, so the llist idiom is established in this structure. The precedent is the data structure, not the locking: rb_free_reps serializes its single consumer through the re_receiving gate in rpcrdma_post_recvs, whereas rb_send_bufs serializes its consumer with rb_lock. Both satisfy the llist single-consumer contract. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 32 ++++++++++++++------------------ net/sunrpc/xprtrdma/xprt_rdma.h | 8 ++++---- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 92c691d2521f..993bc5c444a4 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1120,7 +1120,7 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt) INIT_LIST_HEAD(&buf->rb_all_mrs); INIT_WORK(&buf->rb_refresh_worker, rpcrdma_mr_refresh_worker); - INIT_LIST_HEAD(&buf->rb_send_bufs); + init_llist_head(&buf->rb_send_bufs); INIT_LIST_HEAD(&buf->rb_allreqs); INIT_LIST_HEAD(&buf->rb_all_reps); @@ -1134,7 +1134,7 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt) RPCRDMA_V1_DEF_INLINE_SIZE * 2); if (!req) goto out; - list_add(&req->rl_list, &buf->rb_send_bufs); + llist_add(&req->rl_node, &buf->rb_send_bufs); } init_llist_head(&buf->rb_free_reps); @@ -1214,16 +1214,14 @@ static void rpcrdma_mrs_destroy(struct rpcrdma_xprt *r_xprt) void rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf) { + struct rpcrdma_req *req, *next; + struct llist_node *node; + rpcrdma_reps_destroy(buf); - while (!list_empty(&buf->rb_send_bufs)) { - struct rpcrdma_req *req; - - req = list_first_entry(&buf->rb_send_bufs, - struct rpcrdma_req, rl_list); - list_del(&req->rl_list); + node = llist_del_all(&buf->rb_send_bufs); + llist_for_each_entry_safe(req, next, node, rl_node) rpcrdma_req_destroy(req); - } } /** @@ -1270,15 +1268,15 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) struct rpcrdma_req * rpcrdma_buffer_get(struct rpcrdma_buffer *buffers) { - struct rpcrdma_req *req; + struct llist_node *node; + /* Calls to llist_del_first are required to be serialized */ spin_lock(&buffers->rb_lock); - req = list_first_entry_or_null(&buffers->rb_send_bufs, - struct rpcrdma_req, rl_list); - if (req) - list_del_init(&req->rl_list); + node = llist_del_first(&buffers->rb_send_bufs); spin_unlock(&buffers->rb_lock); - return req; + if (!node) + return NULL; + return llist_entry(node, struct rpcrdma_req, rl_node); } /** @@ -1291,9 +1289,7 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) { rpcrdma_reply_put(buffers, req); - spin_lock(&buffers->rb_lock); - list_add(&req->rl_list, &buffers->rb_send_bufs); - spin_unlock(&buffers->rb_lock); + llist_add(&req->rl_node, &buffers->rb_send_bufs); } /* Returns a pointer to a rpcrdma_regbuf object, or NULL. diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index f879d9b9f57e..ae036719f840 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -332,7 +332,7 @@ enum { struct rpcrdma_buffer; struct rpcrdma_req { - struct list_head rl_list; + struct llist_node rl_node; struct rpc_rqst rl_slot; struct rpcrdma_rep *rl_reply; struct xdr_stream rl_stream; @@ -374,14 +374,14 @@ rpcrdma_mr_pop(struct list_head *list) } /* - * struct rpcrdma_buffer -- holds list/queue of pre-registered memory for - * inline requests/replies, and client/server credits. + * struct rpcrdma_buffer -- holds pre-registered memory for inline + * requests/replies, and client/server credits. * * One of these is associated with a transport instance */ struct rpcrdma_buffer { spinlock_t rb_lock; - struct list_head rb_send_bufs; + struct llist_head rb_send_bufs; struct list_head rb_mrs; unsigned long rb_sc_head; From af9b65b29af341932625c4283dc7a23cdb62688a Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:33 -0400 Subject: [PATCH 3294/5214] xprtrdma: Fix ep kref imbalance on ADDR_CHANGE rpcrdma_cm_event_handler() falls through to the disconnected: label on RDMA_CM_EVENT_ADDR_CHANGE and calls rpcrdma_ep_put() with no matching get when the event arrives before RDMA_CM_EVENT_ESTABLISHED. The kref then underflows during connect teardown and rpcrdma_xprt_disconnect() operates on a freed ep. Reference counts across a normal connection lifecycle: rpcrdma_ep_create() kref_init ->1 rpcrdma_xprt_connect() ep_get ->2 (before post_recvs) RDMA_CM_EVENT_ESTABLISHED ep_get ->3 RDMA_CM_EVENT_DISCONNECTED ep_put ->2 rpcrdma_xprt_drain() ep_put ->1 rpcrdma_xprt_disconnect() tail ep_put ->0 (ep_destroy) The connect-time get in rpcrdma_xprt_connect(), taken just before rpcrdma_post_recvs() "while there are outstanding Receives," is balanced by rpcrdma_xprt_drain. ADDR_CHANGE before ESTABLISHED has no get to consume, so its put drops the count to 1 and the drain put then frees the ep while rpcrdma_xprt_disconnect() still holds a pointer to it. Fix by dispatching on the prior re_connect_status via xchg(): for prev == 0 (pre-ESTABLISHED) wake the connect waiter and return with no put; for prev == 1 call rpcrdma_force_disconnect() and return. The case-1 arm relies on the subsequent RDMA_CM_EVENT_DISCONNECTED event -- reliably delivered when rdma_disconnect() is called on a still-connected cm_id -- to balance the ESTABLISHED get; rpcrdma_xprt_drain() continues to balance only that connect-time get. Any other prior value means teardown is already in flight. Fixes: 2acc5cae2923 ("xprtrdma: Prevent dereferencing r_xprt->rx_ep after it is freed") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 993bc5c444a4..7ddab4ed5d03 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -245,8 +245,17 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) complete(&ep->re_done); return 0; case RDMA_CM_EVENT_ADDR_CHANGE: - ep->re_connect_status = -ENODEV; - goto disconnected; + switch (xchg(&ep->re_connect_status, -ENODEV)) { + case 0: + goto wake_connect_worker; + case 1: + /* The later DISCONNECTED event balances the + * ESTABLISHED get; do not put here. + */ + rpcrdma_force_disconnect(ep); + return 0; + } + return 0; case RDMA_CM_EVENT_ESTABLISHED: rpcrdma_ep_get(ep); ep->re_connect_status = 1; @@ -269,7 +278,6 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) return 0; case RDMA_CM_EVENT_DISCONNECTED: ep->re_connect_status = -ECONNABORTED; -disconnected: rpcrdma_force_disconnect(ep); return rpcrdma_ep_put(ep); default: From bb7caa63e1db22fd03e8dc591b12169e99169dff Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:34 -0400 Subject: [PATCH 3295/5214] xprtrdma: Initialize re_id before removal registration rpcrdma_create_id() registers ep->re_rn with the rpcrdma ib_client before returning the new rdma_cm_id to rpcrdma_ep_create(). However rpcrdma_ep_create() currently stores that pointer in ep->re_id only after rpcrdma_create_id() returns. A local administrator can race an NFS/RDMA mount against RDMA device removal. If rpcrdma_remove_one() observes the just-registered notification before rpcrdma_ep_create() assigns ep->re_id, rpcrdma_ep_removal_done() calls trace_xprtrdma_device_removal(NULL). The tracepoint dereferences id->device->name and copies id->route.addr.dst_addr, so the callback can crash the kernel with a NULL pointer dereference. Store the rdma_cm_id in ep->re_id immediately before publishing ep->re_rn. The existing error path still destroys the id directly if registration fails; ep is then freed by the caller without using ep->re_id. Remove the later duplicate assignment in rpcrdma_ep_create(). Fixes: 3f4eb9ff9234 ("xprtrdma: Handle device removal outside of the CM event handler") Assisted-by: kres:openai-gpt-5 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 7ddab4ed5d03..a192b77ce739 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -334,6 +334,7 @@ static struct rdma_cm_id *rpcrdma_create_id(struct rpcrdma_xprt *r_xprt, if (rc) goto out; + ep->re_id = id; rc = rpcrdma_rn_register(id->device, &ep->re_rn, rpcrdma_ep_removal_done); if (rc) goto out; @@ -406,7 +407,6 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt) } __module_get(THIS_MODULE); device = id->device; - ep->re_id = id; reinit_completion(&ep->re_done); ep->re_max_requests = r_xprt->rx_xprt.max_reqs; From 0f13fc7c7d2e0427517e63c739277a4cd338b0c5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:35 -0400 Subject: [PATCH 3296/5214] xprtrdma: Check frwr_wp_create() during connect frwr_wp_create() creates the singleton Memory Region used to encode padding for Write chunks whose payload length is not XDR-aligned. Its failure paths return a negative errno and leave ep->re_write_pad_mr set to NULL. rpcrdma_xprt_connect() currently ignores that return value. If frwr_wp_create() fails after the rest of the connection setup succeeds, xprt_rdma_connect_worker() treats the connection attempt as successful and sets XPRT_CONNECTED. A later NFS/RDMA read with a non-4-byte-aligned receive page length reaches rpcrdma_encode_write_list(), passes the NULL write-pad MR to encode_rdma_segment(), and dereferences it. This is locally triggerable on an NFS/RDMA client after a connect or reconnect hits a local MR allocation, DMA-map, MR-map, or post-send failure; a remote peer alone cannot force the local MR setup failure. Check the return value and fail the connect as -ENOTCONN, matching the adjacent setup failures. This keeps XPRT_CONNECTED clear and lets the normal reconnect path retry. Fixes: 21037b8c2258 ("xprtrdma: Provide a buffer to pad Write chunks of unaligned length") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index a192b77ce739..74d173e5681d 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -549,7 +549,17 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt) goto out; } rpcrdma_mrs_create(r_xprt); - frwr_wp_create(r_xprt); + + /* + * rpcrdma_encode_write_list() dereferences the write-pad + * MR with no NULL check, so fail the connect rather than + * publish a transport whose write-pad MR is NULL. + */ + rc = frwr_wp_create(r_xprt); + if (rc) { + rc = -ENOTCONN; + goto out; + } out: trace_xprtrdma_connect(r_xprt, rc); From 234c0ff695ef3ffb656931000e6b823d0c2f30fd Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:36 -0400 Subject: [PATCH 3297/5214] xprtrdma: Resize reply buffers before reposting receives Commit 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") made rpcrdma_rep objects survive disconnects. That is normally fine, but it also means their receive regbufs keep the size they had when they were first allocated. Each rep's receive buffer is sized to ep->re_inline_recv when the rep is created. rpcrdma_ep_create() resets that threshold to the rdma_max_inline_read ceiling for every new endpoint, and the connect handshake then shrinks it to the peer's advertised inline send size. A rep allocated under a smaller negotiated threshold keeps that size: on disconnect, rpcrdma_xprt_disconnect() drains and DMA-unmaps the surviving reps but does not free or resize them. The threshold can come back larger on the next connection. The first peer may supply no RPC-over-RDMA CM private data, defaulting its send size to 1024, while the reconnect target is an ordinary server offering 4096; or, with rdma_max_inline_read raised above its default, the reconnect target may advertise a larger svcrdma_max_req_size than the first. rpcrdma_post_recvs() then reposts a surviving rep whose SGE length is still the old, smaller value, and a larger inline Reply hits a receive length error and forces another disconnect. The undersized rep returns to the free list when its failed Receive flushes, so the following reconnect reposts the same rep and fails the same way. The transport flaps without making forward progress for as long as the peer keeps advertising the larger inline size. This is local/admin-triggerable rather than remote-triggerable: a local administrator must create and maintain the NFS/RDMA mount, while the server or reconnect target has to advertise a larger inline send size and return a reply that uses it. Fix this by checking each rep before it is reposted. If the receive regbuf is smaller than the current endpoint's inline receive size, reallocate it on the current RDMA device's NUMA node and reinitialize the rep's xdr_buf before DMA-mapping and posting the Receive WR. Fixes: 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/verbs.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 74d173e5681d..8392ba4bcdca 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -81,6 +81,8 @@ rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction, int node); static struct rpcrdma_regbuf * rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction); +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node); static void rpcrdma_regbuf_dma_unmap(struct rpcrdma_regbuf *rb); static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb); @@ -1353,10 +1355,16 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction) * returned, @rb is left untouched. */ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) +{ + return rpcrdma_regbuf_realloc_node(rb, size, flags, NUMA_NO_NODE); +} + +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node) { void *buf; - buf = kmalloc(size, flags); + buf = kmalloc_node(size, flags, node); if (!buf) return false; @@ -1368,6 +1376,23 @@ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) return true; } +static bool rpcrdma_rep_resize(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_rep *rep) +{ + struct rpcrdma_regbuf *rb = rep->rr_rdmabuf; + struct rpcrdma_ep *ep = r_xprt->rx_ep; + size_t size = ep->re_inline_recv; + + if (likely(rdmab_length(rb) >= size)) + return true; + if (!rpcrdma_regbuf_realloc_node(rb, size, XPRTRDMA_GFP_FLAGS, + ibdev_to_node(ep->re_id->device))) + return false; + + xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rb), rdmab_length(rb)); + return true; +} + /** * __rpcrdma_regbuf_dma_map - DMA-map a regbuf * @r_xprt: controlling transport instance @@ -1451,6 +1476,10 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) break; /* I1: a rep on rb_free_reps must carry no rqst pointer. */ WARN_ON_ONCE(rep->rr_rqst); + if (!rpcrdma_rep_resize(r_xprt, rep)) { + rpcrdma_rep_put(buf, rep); + break; + } if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From c7653d5cebc8492c77ec0415b5e9c0fb3e644bc6 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:37 -0400 Subject: [PATCH 3298/5214] xprtrdma: Fix bcall rep leak and unbounded peek rpcrdma_is_bcall() decodes a reply's first words to decide whether the frame is a backchannel call. Two issues in that decode path let a short or malformed reply leak the receive buffer and drain the Receive queue. First, the speculative peek p = xdr_inline_decode(xdr, 0); /* five p++ reads follow */ asks xdr_inline_decode() for zero bytes, which returns xdr->p without consulting xdr->end. The five subsequent __be32 reads can then walk up to 20 bytes past the wire payload into stale regbuf contents and misclassify the reply as a backchannel call. Second, after the post-peek p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) return true; the short-header arm returns true without calling rpcrdma_bc_receive_call(). The contract with the caller is that a true return transfers ownership of rep to the backchannel path: rpcrdma_reply_handler() if (rpcrdma_is_bcall(r_xprt, rep)) return; /* bare return, skips out_post */ ... out_post: rpcrdma_post_recvs(r_xprt, credits + ...); Because rpcrdma_bc_receive_call() never ran, no one took rep, but rpcrdma_reply_handler still bare-returns past rpcrdma_rep_put() and rpcrdma_post_recvs(). The rep, with its persistently DMA-mapped receive buffer, is orphaned on rb_all_reps and freed only at transport teardown. This completion reposts nothing, so its slot is reclaimed only when a later forward-channel reply reaches out_post and rpcrdma_post_recvs() allocates a fresh rep to backfill; absent that traffic the Receive queue drains and the peer's Sends draw RNR NAKs. Fix by consulting xdr->end after the zero-length peek so the five __be32 reads cannot run unless 20 bytes of wire payload remain. A byte-precise comparison against xdr->end is required because a non-4-aligned receive rounds the stream's word count up past the true payload. Also return false from the short-header arm so the reply falls through the normal out_norqst cleanup chain (rpcrdma_rep_put() plus rpcrdma_post_recvs()). Fixes: 41c8f70f5a3d ("xprtrdma: Harden backchannel call decoding") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index f115baba6d56..63e64d53e289 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1088,6 +1088,8 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) /* Peek at stream contents without advancing. */ p = xdr_inline_decode(xdr, 0); + if ((char *)xdr->end - (char *)p < 5 * XDR_UNIT) + return false; /* Chunk lists */ if (xdr_item_is_present(p++)) @@ -1112,7 +1114,7 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) */ p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) - return true; + return false; rpcrdma_bc_receive_call(r_xprt, rep); return true; From c3a628aab2dc8f5fd7bff86ceaeae64de590e60a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:38 -0400 Subject: [PATCH 3299/5214] xprtrdma: Sanitize the reply credit grant after parsing The out_norqst exit in rpcrdma_reply_handler() branches away before the credit clamp, so a reply that matches no pending request reaches out_post carrying the raw credit value parsed from the wire. rpcrdma_post_recvs() does not bound its @needed argument: the refill loop allocates and chains Receive WRs until the count is satisfied or allocation fails. A peer that sends a well-formed reply carrying an unknown XID and an inflated credit grant therefore drives rep allocation and Receive posting past re_max_requests on every such reply. Move the clamp to immediately after the credit field is parsed, ahead of the first branch that can reach out_post, so every later consumer sees a sanitized value. The cwnd update stays on the matched-request path. Fixes: 704f3f640f72 ("xprtrdma: Post receive buffers after RPC completion") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 63e64d53e289..5ff086ccd259 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1472,6 +1472,14 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) credits = be32_to_cpu(*p++); rep->rr_proc = *p++; + /* The credit grant from the wire is not trustworthy; + * sanitize it before any code path consumes it. + */ + if (credits == 0) + credits = 1; /* don't deadlock */ + else if (credits > r_xprt->rx_ep->re_max_requests) + credits = r_xprt->rx_ep->re_max_requests; + if (rep->rr_vers != rpcrdma_version) goto out_badversion; @@ -1488,10 +1496,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) xprt_pin_rqst(rqst); spin_unlock(&xprt->queue_lock); - if (credits == 0) - credits = 1; /* don't deadlock */ - else if (credits > r_xprt->rx_ep->re_max_requests) - credits = r_xprt->rx_ep->re_max_requests; if (buf->rb_credits != credits) rpcrdma_update_cwnd(r_xprt, credits); From abc011ddaf1617e3e82d8a1e87daa7ddbfb9bac5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:39 -0400 Subject: [PATCH 3300/5214] xprtrdma: Repost Receive buffers for malformed replies rpcrdma_wc_receive() decrements the transport's Receive count for every completion before it dispatches a successful Receive to rpcrdma_reply_handler(). The handler must post a replacement Receive WR before returning unless ownership of the rep has moved elsewhere, as on the backchannel path. Commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") moved the Receive refill out of rpcrdma_wc_receive(), where it had run ahead of every reply, into rpcrdma_reply_handler() so that the responder's credit grant could be parsed before reposting. The bad-version and short-reply exits never reach that refill: they recycle the rep and return without calling rpcrdma_post_recvs(). A remote peer can therefore drain the client's posted Receive queue by sending a sustained stream of replies that are shorter than the fixed transport header or that carry an unrecognized RPC/RDMA version. Each such reply consumes one posted Receive without replacing it. Once the queue empties, the peer's next Send finds no posted Receive and the transport stalls until reconnect. Route both malformed-reply exits through the shared repost tail after recycling the rep, refilling against buf->rb_credits, the most recent accepted credit grant. Neither exit updates the congestion window, so RPCs admitted under the previous grant remain in flight awaiting replies. A smaller refill target would let a stream of malformed replies ratchet the posted Receive count down to the batch floor while the congestion window still admits rb_credits RPCs; a burst of valid replies to those RPCs could then overrun the posted Receives, and because the client connects with rnr_retry_count of zero, a single RNR NAK terminates the connection. Refilling against rb_credits also restores the target that applied to malformed replies before commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") when rpcrdma_post_recvs() computed it from rb_credits internally. rb_credits is at least one from connection establishment onward, so the repost path always keeps Receives posted. Fixes: 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 5ff086ccd259..3f50828802de 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1528,11 +1528,13 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) out_badversion: trace_xprtrdma_reply_vers_err(rep); - goto out; + rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; out_shortreply: trace_xprtrdma_reply_short_err(rep); - -out: rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; } From 60e7870052f417d83965db144f70ae21fcfcf37f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:40 -0400 Subject: [PATCH 3301/5214] xprtrdma: Return sendctx slot after Send preparation failure rpcrdma_prepare_send_sges() gets a sendctx before it maps the SGEs for the Send WR. If one of the mapping helpers fails, no Send WR is posted, so no Send completion is guaranteed to advance rb_sc_tail. Current cleanup clears sc_req so a later completion can sweep over that slot, but a consecutive run of preparation failures can still advance rb_sc_head until the ring appears full. At that point rpcrdma_sendctx_get_locked() returns NULL and no Send can be posted to produce the completion needed to recover the ring. The trigger requires CONFIG_SUNRPC_XPRT_RDMA and an NFS/RDMA mount. Mount setup and reliable DMA-map fault injection require local admin authority. Unprivileged I/O on an existing mount can exercise the send path, but a remote peer alone cannot force this local DMA-map failure. Add rpcrdma_sendctx_unget_locked() for the single-consumer send path to rewind rb_sc_head when the just-acquired sendctx is canceled before ib_post_send(). Wake waiters after making the slot available again. After the rewind, every slot the completion sweep visits belongs to a posted Send, so rpcrdma_sendctx_put_locked() no longer needs to test sc_req before unmapping. Fixes: ae72950abf99 ("xprtrdma: Add data structure to manage RDMA Send arguments") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/xprtrdma/rpc_rdma.c | 5 ++++- net/sunrpc/xprtrdma/verbs.c | 40 +++++++++++++++++++++++++++++---- net/sunrpc/xprtrdma/xprt_rdma.h | 2 ++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 3f50828802de..1285f04cdac1 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -747,6 +747,7 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdr, enum rpcrdma_chunktype rtype) { + struct rpcrdma_sendctx *sc; int ret; ret = -EAGAIN; @@ -789,7 +790,9 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, return 0; out_unmap: - rpcrdma_sendctx_cancel(req->rl_sendctx); + sc = req->rl_sendctx; + rpcrdma_sendctx_cancel(sc); + rpcrdma_sendctx_unget_locked(r_xprt, sc); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 8392ba4bcdca..04b286223b24 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -631,6 +631,11 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) /* The QP is drained, but the final unsignaled Sends might not * have been walked by a signaled Send completion. Release those * Send owners before request buffers are reset. + * + * Unlike the completion sweep, this walk can visit slots with + * no Send posted: after a partial rpcrdma_sendctxs_create() + * failure on reconnect, rb_sc_head and rb_sc_tail are stale, + * and slots between them can be NULL or have sc_req clear. */ for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); @@ -703,6 +708,12 @@ static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, return likely(item < buf->rb_sc_last) ? item + 1 : 0; } +static unsigned long rpcrdma_sendctx_prev(struct rpcrdma_buffer *buf, + unsigned long item) +{ + return item > 0 ? item - 1 : buf->rb_sc_last; +} + /** * rpcrdma_sendctx_get_locked - Acquire a send context * @r_xprt: controlling transport instance @@ -759,6 +770,29 @@ struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt) return NULL; } +/** + * rpcrdma_sendctx_unget_locked - Release an unposted send context + * @r_xprt: controlling transport instance + * @sc: send context to release + * + * Usage: Called when no Send is posted for the sendctx most + * recently returned by rpcrdma_sendctx_get_locked(). + * + * The caller serializes calls to this function and to + * rpcrdma_sendctx_get_locked() (per transport). + */ +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc) +{ + struct rpcrdma_buffer *buf = &r_xprt->rx_buf; + + if (WARN_ON_ONCE(buf->rb_sc_ctxs[buf->rb_sc_head] != sc)) + return; + + buf->rb_sc_head = rpcrdma_sendctx_prev(buf, buf->rb_sc_head); + xprt_write_space(&r_xprt->rx_xprt); +} + /** * rpcrdma_sendctx_put_locked - Release a send context * @r_xprt: controlling transport instance @@ -776,8 +810,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, unsigned long next_tail; /* Release previously completed but unsignaled Sends by walking - * up the queue until @sc is found. Entries left behind by a - * failed rpcrdma_prepare_send_sges() have sc_req cleared. + * up the queue until @sc is found. */ next_tail = buf->rb_sc_tail; do { @@ -787,8 +820,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, /* ORDER: item must be accessed _before_ tail is updated */ cur = buf->rb_sc_ctxs[next_tail]; - if (cur->sc_req) - rpcrdma_sendctx_unmap(cur); + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index ae036719f840..4cbc941e4a3e 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -495,6 +495,8 @@ void rpcrdma_req_destroy(struct rpcrdma_req *req); int rpcrdma_buffer_create(struct rpcrdma_xprt *); void rpcrdma_buffer_destroy(struct rpcrdma_buffer *); struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt); +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc); struct rpcrdma_mr *rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt); void rpcrdma_mrs_refresh(struct rpcrdma_xprt *r_xprt); From d5312a7ef79f5de574bce7b140ea1e48ce7e9262 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 9 Jun 2026 10:13:08 +0200 Subject: [PATCH 3302/5214] perf dwarf: Avoid redefinition warnings for REG_DWARFNUM_NAME dwarf-regs.c includes an arch-specific dwarf-regs-table.h for several architectures. This pulls in different definitions of REG_DWARFNUM_NAME and causes compiler warnings for W=1 builds. In file included from util/dwarf-regs.c:23: .../dwarf-regs-table.h:5: error: "REG_DWARFNUM_NAME" redefined [-Werror] #define REG_DWARFNUM_NAME(reg, idx) [idx] = reg Undefine REG_DWARFNUM_NAME before each new definition. Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Martin Kaiser Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/include/dwarf-regs-table.h | 1 + tools/perf/arch/riscv/include/dwarf-regs-table.h | 1 + tools/perf/arch/s390/include/dwarf-regs-table.h | 1 + 3 files changed, 3 insertions(+) diff --git a/tools/perf/arch/powerpc/include/dwarf-regs-table.h b/tools/perf/arch/powerpc/include/dwarf-regs-table.h index 66dc015a733d..7e746cb31b66 100644 --- a/tools/perf/arch/powerpc/include/dwarf-regs-table.h +++ b/tools/perf/arch/powerpc/include/dwarf-regs-table.h @@ -7,6 +7,7 @@ * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html * http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf */ +#undef REG_DWARFNUM_NAME #define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg static const char * const powerpc_regstr_tbl[] = { diff --git a/tools/perf/arch/riscv/include/dwarf-regs-table.h b/tools/perf/arch/riscv/include/dwarf-regs-table.h index a45b63a6d5a8..009a4e3c51ab 100644 --- a/tools/perf/arch/riscv/include/dwarf-regs-table.h +++ b/tools/perf/arch/riscv/include/dwarf-regs-table.h @@ -2,6 +2,7 @@ #ifdef DEFINE_DWARF_REGSTR_TABLE /* This is included in perf/util/dwarf-regs.c */ +#undef REG_DWARFNUM_NAME #define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg static const char * const riscv_regstr_tbl[] = { diff --git a/tools/perf/arch/s390/include/dwarf-regs-table.h b/tools/perf/arch/s390/include/dwarf-regs-table.h index 671553525f41..e90b63157702 100644 --- a/tools/perf/arch/s390/include/dwarf-regs-table.h +++ b/tools/perf/arch/s390/include/dwarf-regs-table.h @@ -2,6 +2,7 @@ #ifndef S390_DWARF_REGS_TABLE_H #define S390_DWARF_REGS_TABLE_H +#undef REG_DWARFNUM_NAME #define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg /* From 8d28a56cae9b298a351b361d8d374cb4a9089e3f Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 9 Jun 2026 10:13:09 +0200 Subject: [PATCH 3303/5214] perf riscv: fix register name strings On risc-v, pref probe generates an invalid syntax for a named register in a kprobe. $ perf probe --debug verbose --add "n_tty_write tty" ... Writing event: p:probe/n_tty_write _text+8922528 tty=%"%a0":x64 Failed to write event: Invalid argument The problem is the combination of #define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg and entries such as REG_DWARFNUM_NAME("%a0", 10) where #reg will escape the quotes of the first macro parameter. Update the macro definition to produce the correct syntax for a named register in a kprobe, i.e. the unquoted register name with only one leading %. Fixes: a90c4519186dfc08 ("perf riscv: Remove dwarf-regs.c and add dwarf-regs-table.h") Reviewed-by: Ian Rogers Signed-off-by: Martin Kaiser Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/riscv/include/dwarf-regs-table.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/riscv/include/dwarf-regs-table.h b/tools/perf/arch/riscv/include/dwarf-regs-table.h index 009a4e3c51ab..8c42806d34f1 100644 --- a/tools/perf/arch/riscv/include/dwarf-regs-table.h +++ b/tools/perf/arch/riscv/include/dwarf-regs-table.h @@ -3,7 +3,7 @@ /* This is included in perf/util/dwarf-regs.c */ #undef REG_DWARFNUM_NAME -#define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg +#define REG_DWARFNUM_NAME(reg, idx) [idx] = reg static const char * const riscv_regstr_tbl[] = { REG_DWARFNUM_NAME("%zero", 0), From 7158c5b67e92e52c2ca9a3617a7e768a84031da1 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 10 Jun 2026 12:55:25 -0700 Subject: [PATCH 3304/5214] kbuild: Use ld.lld for linking host programs when LLVM is set Currently, host programs are linked with HOSTCC using the toolchain's default linker. This can result in confusing behavior when using the LLVM Kbuild variable (which states that the user would like to build with the LLVM toolchain instead of the GNU one), as clang's default linker is ld for most platforms, not ld.lld. The documentation mentions HOSTLD=ld.lld is set but this variable is not used by Kbuild proper, only within some tools/ projects. Kbuild provides the HOSTLDFLAGS variable, which allow users to provide the '-fuse-ld' or '--ld-path' flags to customize what linker is used, but this is not super obvious to folks not familiar with Kbuild. If the user has not customized the linker already using one of these flags, default to ld.lld when using the LLVM variable, which is more in line with user expectations when using that variable. Closes: https://github.com/ClangBuiltLinux/linux/issues/2167 Link: https://patch.msgid.link/20260610-kbuild-use-lld-for-linking-hostprogs-v1-1-70396fe42ee3@kernel.org Signed-off-by: Nathan Chancellor --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 37fdb454b637..ae263f964e82 100644 --- a/Makefile +++ b/Makefile @@ -525,6 +525,9 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX) READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX) STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX) +ifeq ($(filter -fuse-ld=% --ld-path=%,$(KBUILD_HOSTLDFLAGS)),) +KBUILD_HOSTLDFLAGS += -fuse-ld=lld +endif else CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld From 41c1ce28bbc1d4f7a1b1b711b86448480d2838d2 Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Mon, 8 Jun 2026 18:06:13 +0200 Subject: [PATCH 3305/5214] perf build: Respect V=1 for Python extension builds Make util/setup.py respect the verbose build flag (V=1) by conditionally passing --quiet only when not in verbose mode. This eases debugging of Python extension compilation issues and aligns with the existing perf build system behavior. Reviewed-by: Ian Rogers Signed-off-by: Jens Remus Tested-by: Jan Polensky Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Heiko Carstens Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index ab661a1d271c..bdedf6620758 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -401,6 +401,10 @@ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf*.so +ifneq ($(quiet),) +python_setup_quiet=--quiet +endif + # Use the detected configuration -include $(OUTPUT).config-detected @@ -526,7 +530,7 @@ $(OUTPUT)python/perf$(PYTHON_EXTENSION_SUFFIX): util/python.c util/setup.py $(PE $(QUIET_GEN)LDSHARED="$(CC) -pthread -shared" \ CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS) $(LIBS_PY)' \ $(PYTHON_WORD) util/setup.py \ - --quiet build_ext; \ + $(python_setup_quiet) build_ext; \ cp $(PYTHON_EXTBUILD_LIB)perf*.so $(OUTPUT)python/ python_perf_target: @@ -903,7 +907,7 @@ install-bin: install-tools install-tests install: install-bin try-install-man install-python_ext: - $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' + $(PYTHON_WORD) util/setup.py $(python_setup_quiet) install --root='/$(DESTDIR_SQ)' # 'make install-doc' should call 'make -C Documentation install' $(INSTALL_DOC_TARGETS): From bcd2cc4405bdb9ac77210d0df5990d46c311be66 Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Wed, 10 Jun 2026 13:23:43 +0200 Subject: [PATCH 3306/5214] perf build: Do not duplicate CFLAGS in Python extension builds setuptools already uses CFLAGS. Passing CFLAGS with additional flags as extra compile arguments causes CFLAGS to effectively get passed twice: $ make -C tools/perf V=1 JOBS=1 ... building 'perf' extension gcc [CFLAGS] -fPIC -Iutil/include -I/usr/include/python3.14 \ -c /root/linux/tools/perf/util/python.c \ -o python_ext_build/tmp/root/linux/tools/perf/util/python.o \ [CFLAGS] \ -fno-strict-aliasing -Wno-write-strings -Wno-unused-parameter \ -Wno-redundant-decls -Wno-cast-function-type \ -Wno-declaration-after-statement Reviewed-by: James Clark Signed-off-by: Jens Remus Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ian Rogers Cc: Ingo Molnar Cc: Jan Polensky Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/setup.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index b65b1792ca05..a0ce76624a23 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -74,18 +74,17 @@ class install_lib(_install_lib): self.build_dir = build_lib -cflags = getenv('CFLAGS', '').split() # switch off several checks (need to be at the end of cflags list) -cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter', '-Wno-redundant-decls' ] +extra_cflags = ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter', '-Wno-redundant-decls' ] if cc_is_clang: - cflags += ["-Wno-unused-command-line-argument" ] + extra_cflags += ["-Wno-unused-command-line-argument" ] if clang_has_option("-Wno-cast-function-type-mismatch"): - cflags += ["-Wno-cast-function-type-mismatch" ] + extra_cflags += ["-Wno-cast-function-type-mismatch" ] else: - cflags += ['-Wno-cast-function-type' ] + extra_cflags += ['-Wno-cast-function-type' ] # The python headers have mixed code with declarations (decls after asserts, for instance) -cflags += [ "-Wno-declaration-after-statement" ] +extra_cflags += [ "-Wno-declaration-after-statement" ] src_perf = f'{srctree}/tools/perf' build_lib = getenv('PYTHON_EXTBUILD_LIB') @@ -94,7 +93,7 @@ build_tmp = getenv('PYTHON_EXTBUILD_TMP') perf = Extension('perf', sources = [ src_perf + '/util/python.c' ], include_dirs = ['util/include'], - extra_compile_args = cflags, + extra_compile_args = extra_cflags, ) setup(name='perf', From 49f5f6ae67dec54014584bb3126a5a94f14e2a5c Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Wed, 10 Jun 2026 13:24:51 +0200 Subject: [PATCH 3307/5214] perf s390: Fix TEXTREL in Python extension by compiling as PIC On s390 the Python extension build fails as follows when using a linker that is configured to treat text relocations (TEXTREL) in shared libraries as error by default: GEN python/perf.cpython-314-s390x-linux-gnu.so /usr/bin/ld.bfd: error: read-only segment has dynamic relocations This occurrs because util/llvm-c-helpers.o is erroneously built from util/llvm-c-helpers.cpp without compiler option -fPIC but linked into the shared library (via libperf-util.a(perf-util-in.o)). On s390, object files must be compiled as position-indepedent code (PIC) in order to be linked into shared libraries. Commit a9a3f1d18a6c ("perf s390: Always build with -fPIC") added compiler option -fPIC to CFLAGS for s390, which is used in C compiles. Add -fPIC to CXXFLAGS for s390 as well, so that it is also used in C++ compiles. Fixes: a9a3f1d18a6c9ccf ("perf s390: Always build with -fPIC") Reported-by: Thomas Richter Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Jens Remus Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ingo Molnar Cc: Jan Polensky Cc: Jiri Olsa Cc: Justin Stitt Cc: Mark Rutland Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 6e7b15fab2ec..0ba307e78fe1 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -77,6 +77,7 @@ endif ifeq ($(ARCH),s390) CFLAGS += -fPIC + CXXFLAGS += -fPIC endif # Unconditionally set up the libunwind feature build flags as a From 2857a5dca750ea989c6fb70b4c14e801e4b7b4ad Mon Sep 17 00:00:00 2001 From: Tanushree Shah Date: Sat, 6 Jun 2026 17:45:29 +0530 Subject: [PATCH 3308/5214] perf data convert json: Fix addr_location leak on time-filtered samples When samples are skipped due to time filtering in process_sample_event(), the early return path bypasses addr_location__exit(), causing memory leaks of thread, map, and maps references acquired by machine__resolve(). These references must be released through addr_location__exit() before returning. Fixes: 8e746e95c3e4eb56 ("perf data: Allow filtering conversion by time range") Reviewed-by: Ian Rogers Signed-off-by: Tanushree Shah Cc: Adrian Hunter Cc: Athira Rajeev Cc: Derek Foreman Cc: Hari Bathini Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Namhyung Kim Cc: Shivani.Nittor@ibm.com Cc: Tanushree.Shah@ibm.com Cc: Tejas.Manhas1@ibm.com Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-json.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index c71dfb77c697..40412c3dbdb2 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -177,6 +177,7 @@ static int process_sample_event(const struct perf_tool *tool, if (perf_time__ranges_skip_sample(c->ptime_range, c->range_num, sample->time)) { ++c->skipped; + addr_location__exit(&al); return 0; } From 4bd109899fb51594e0d868238ada109c12d13a74 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Tue, 9 Jun 2026 19:13:31 +0530 Subject: [PATCH 3309/5214] perf tools: Fix the check for parameterized field in event term The format_alias() function in util/pmu.c has a check to detect whether the event has parameterized field ( =? ). The string alias->terms contains the event and if the event has user configurable parameter, there will be presence of sub string "=?" in the alias->terms. Snippet of code: /* Paramemterized events have the parameters shown. */ if (strstr(alias->terms, "=?")) { /* No parameters. */ snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name); if "strstr" contains the substring, it returns a pointer and hence enters the above check which is not the expected check. And hence "perf list" doesn't have the parameterized fields in the result. Fix this check to use: if (!strstr(alias->terms, "=?")) { With this change, perf list shows the events correctly with the strings showing parameters. Before the fix: # ./perf list|grep -w PM_PAU_CYC hv_24x7/PM_PAU_CYC/ [Kernel PMU event] With this fix: # ./perf list|grep -w PM_PAU_CYC hv_24x7/PM_PAU_CYC,chip=?/ [Kernel PMU event] Reviewed-by: Ian Rogers Signed-off-by: Athira Rajeev Tested-by: Venkat Rao Bagalkote Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Hari Bathini Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Shivani Nittor Cc: Tanushree.Shah@ibm.com Cc: Tejas.Manhas1@ibm.com Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 9994709ef12b..e765a7ffb0d6 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -2134,7 +2134,7 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, skip_duplicate_pmus); /* Paramemterized events have the parameters shown. */ - if (strstr(alias->terms, "=?")) { + if (!strstr(alias->terms, "=?")) { /* No parameters. */ snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name); return buf; From d798af0a0f271e6d1e000e9fed579d25317091c3 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Wed, 10 Jun 2026 23:30:12 +0530 Subject: [PATCH 3310/5214] pinctrl: qcom: lpass-lpi: drop unused runtime-PM write helper lpi_gpio_write() became unused after the PM clock runtime conversion switched write paths to register helper calls inside callers that already hold an active runtime-PM reference. With -Werror this triggers: error: 'lpi_gpio_write' defined but not used [-Wunused-function] Drop the dead wrapper and rename the low-level MMIO helpers from __lpi_gpio_* to lpi_gpio_*_reg for neutral register-accessor naming. Fixes: b719ede389d8 ("pinctrl: qcom: lpass-lpi: Switch to PM clock framework for runtime PM") Reported-by: Nathan Chancellor Closes: https://lore.kernel.org/all/f03850f6-186d-4988-a450-e6e95f24a551@kernel.org/ Signed-off-by: Ajay Kumar Nandam Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 50 +++++++++--------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 4d758fd117c4..5fd4a4eba654 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -52,15 +52,15 @@ static void __iomem *lpi_gpio_reg(struct lpi_pinctrl *state, return state->tlmm_base + pin_offset + addr; } -static void __lpi_gpio_read(struct lpi_pinctrl *state, - unsigned int pin, unsigned int addr, u32 *val) +static void lpi_gpio_read_reg(struct lpi_pinctrl *state, + unsigned int pin, unsigned int addr, u32 *val) { *val = ioread32(lpi_gpio_reg(state, pin, addr)); } -static void __lpi_gpio_write(struct lpi_pinctrl *state, - unsigned int pin, unsigned int addr, - unsigned int val) +static void lpi_gpio_write_reg(struct lpi_pinctrl *state, + unsigned int pin, unsigned int addr, + unsigned int val) { iowrite32(val, lpi_gpio_reg(state, pin, addr)); } @@ -74,21 +74,7 @@ static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin, if (ret < 0) return ret; - __lpi_gpio_read(state, pin, addr, val); - - return pm_runtime_put_autosuspend(state->dev); -} - -static int lpi_gpio_write(struct lpi_pinctrl *state, unsigned int pin, - unsigned int addr, unsigned int val) -{ - int ret; - - ret = pm_runtime_resume_and_get(state->dev); - if (ret < 0) - return ret; - - __lpi_gpio_write(state, pin, addr, val); + lpi_gpio_read_reg(state, pin, addr, val); return pm_runtime_put_autosuspend(state->dev); } @@ -150,7 +136,7 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, return ret; guard(mutex)(&pctrl->lock); - __lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG, &val); + lpi_gpio_read_reg(pctrl, pin, LPI_GPIO_CFG_REG, &val); /* * If this is the first time muxing to GPIO and the direction is @@ -160,23 +146,23 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, */ if (i == GPIO_FUNC && (val & LPI_GPIO_OE_MASK) && !test_and_set_bit(group, pctrl->ever_gpio)) { - __lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG, &io_val); + lpi_gpio_read_reg(pctrl, group, LPI_GPIO_VALUE_REG, &io_val); if (io_val & LPI_GPIO_VALUE_IN_MASK) { if (!(io_val & LPI_GPIO_VALUE_OUT_MASK)) - __lpi_gpio_write(pctrl, group, - LPI_GPIO_VALUE_REG, - io_val | LPI_GPIO_VALUE_OUT_MASK); + lpi_gpio_write_reg(pctrl, group, + LPI_GPIO_VALUE_REG, + io_val | LPI_GPIO_VALUE_OUT_MASK); } else { if (io_val & LPI_GPIO_VALUE_OUT_MASK) - __lpi_gpio_write(pctrl, group, - LPI_GPIO_VALUE_REG, - io_val & ~LPI_GPIO_VALUE_OUT_MASK); + lpi_gpio_write_reg(pctrl, group, + LPI_GPIO_VALUE_REG, + io_val & ~LPI_GPIO_VALUE_OUT_MASK); } } u32p_replace_bits(&val, i, LPI_GPIO_FUNCTION_MASK); - __lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val); + lpi_gpio_write_reg(pctrl, pin, LPI_GPIO_CFG_REG, val); return pm_runtime_put_autosuspend(pctrl->dev); } @@ -337,17 +323,17 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, guard(mutex)(&pctrl->lock); if (output_enabled) { val = u32_encode_bits(value ? 1 : 0, LPI_GPIO_VALUE_OUT_MASK); - __lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val); + lpi_gpio_write_reg(pctrl, group, LPI_GPIO_VALUE_REG, val); } - __lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG, &val); + lpi_gpio_read_reg(pctrl, group, LPI_GPIO_CFG_REG, &val); u32p_replace_bits(&val, pullup, LPI_GPIO_PULL_MASK); u32p_replace_bits(&val, LPI_GPIO_DS_TO_VAL(strength), LPI_GPIO_OUT_STRENGTH_MASK); u32p_replace_bits(&val, output_enabled, LPI_GPIO_OE_MASK); - __lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val); + lpi_gpio_write_reg(pctrl, group, LPI_GPIO_CFG_REG, val); return pm_runtime_put_autosuspend(pctrl->dev); } From 84c0fe4a2085f424fe672a296056c8baf6cfb977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Sun, 17 May 2026 15:48:35 +0200 Subject: [PATCH 3311/5214] pinctrl: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace in the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-aw9523.c | 2 +- drivers/pinctrl/pinctrl-cy8c95x0.c | 6 +++--- drivers/pinctrl/pinctrl-mcp23s08_i2c.c | 6 +++--- drivers/pinctrl/pinctrl-sx150x.c | 20 ++++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/pinctrl/pinctrl-aw9523.c b/drivers/pinctrl/pinctrl-aw9523.c index 02a24ac87ea4..bc94003512cf 100644 --- a/drivers/pinctrl/pinctrl-aw9523.c +++ b/drivers/pinctrl/pinctrl-aw9523.c @@ -1033,7 +1033,7 @@ static void aw9523_remove(struct i2c_client *client) } static const struct i2c_device_id aw9523_i2c_id_table[] = { - { "aw9523_i2c" }, + { .name = "aw9523_i2c" }, { } }; MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-cy8c95x0.c index ace0be5ec679..093ae7c1dae5 100644 --- a/drivers/pinctrl/pinctrl-cy8c95x0.c +++ b/drivers/pinctrl/pinctrl-cy8c95x0.c @@ -1461,9 +1461,9 @@ static int cy8c95x0_probe(struct i2c_client *client) } static const struct i2c_device_id cy8c95x0_id[] = { - { "cy8c9520", 20 }, - { "cy8c9540", 40 }, - { "cy8c9560", 60 }, + { .name = "cy8c9520", .driver_data = 20 }, + { .name = "cy8c9540", .driver_data = 40 }, + { .name = "cy8c9560", .driver_data = 60 }, { } }; MODULE_DEVICE_TABLE(i2c, cy8c95x0_id); diff --git a/drivers/pinctrl/pinctrl-mcp23s08_i2c.c b/drivers/pinctrl/pinctrl-mcp23s08_i2c.c index 94e1add6ddd7..f3dffa3c74d3 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08_i2c.c +++ b/drivers/pinctrl/pinctrl-mcp23s08_i2c.c @@ -67,9 +67,9 @@ static const struct mcp23s08_info mcp23018_i2c = { }; static const struct i2c_device_id mcp230xx_id[] = { - { "mcp23008", (kernel_ulong_t)&mcp23008_i2c }, - { "mcp23017", (kernel_ulong_t)&mcp23017_i2c }, - { "mcp23018", (kernel_ulong_t)&mcp23018_i2c }, + { .name = "mcp23008", .driver_data = (kernel_ulong_t)&mcp23008_i2c }, + { .name = "mcp23017", .driver_data = (kernel_ulong_t)&mcp23017_i2c }, + { .name = "mcp23018", .driver_data = (kernel_ulong_t)&mcp23018_i2c }, { } }; MODULE_DEVICE_TABLE(i2c, mcp230xx_id); diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index 1d6760ffe809..b59d913513d5 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -839,16 +839,16 @@ static const struct pinconf_ops sx150x_pinconf_ops = { }; static const struct i2c_device_id sx150x_id[] = { - {"sx1501q", (kernel_ulong_t) &sx1501q_device_data }, - {"sx1502q", (kernel_ulong_t) &sx1502q_device_data }, - {"sx1503q", (kernel_ulong_t) &sx1503q_device_data }, - {"sx1504q", (kernel_ulong_t) &sx1504q_device_data }, - {"sx1505q", (kernel_ulong_t) &sx1505q_device_data }, - {"sx1506q", (kernel_ulong_t) &sx1506q_device_data }, - {"sx1507q", (kernel_ulong_t) &sx1507q_device_data }, - {"sx1508q", (kernel_ulong_t) &sx1508q_device_data }, - {"sx1509q", (kernel_ulong_t) &sx1509q_device_data }, - {} + { .name = "sx1501q", .driver_data = (kernel_ulong_t)&sx1501q_device_data }, + { .name = "sx1502q", .driver_data = (kernel_ulong_t)&sx1502q_device_data }, + { .name = "sx1503q", .driver_data = (kernel_ulong_t)&sx1503q_device_data }, + { .name = "sx1504q", .driver_data = (kernel_ulong_t)&sx1504q_device_data }, + { .name = "sx1505q", .driver_data = (kernel_ulong_t)&sx1505q_device_data }, + { .name = "sx1506q", .driver_data = (kernel_ulong_t)&sx1506q_device_data }, + { .name = "sx1507q", .driver_data = (kernel_ulong_t)&sx1507q_device_data }, + { .name = "sx1508q", .driver_data = (kernel_ulong_t)&sx1508q_device_data }, + { .name = "sx1509q", .driver_data = (kernel_ulong_t)&sx1509q_device_data }, + { } }; static const struct of_device_id sx150x_of_match[] = { From e191518a9e72ec0b9d0254d537a48a4c8d18dc4b Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 9 Jun 2026 15:02:49 +0300 Subject: [PATCH 3312/5214] pinctrl: qcom: eliza: Add missing sdc2 pin function mappings GPIOs 38, 39, 48 and 49 support the SDC2 DATA function, while GPIO 51 supports SDC2 CMD and GPIO 62 supports SDC2 CLK. However, the sdc2 pin function is not listed in the corresponding pingroup definitions, preventing these pins from being muxed for SDC2 operation. Add the missing sdc2 function mappings. Fixes: 6f26989e15fb ("pinctrl: qcom: Add Eliza pinctrl driver") Signed-off-by: Abel Vesa Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-eliza.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c index 4a2ef7b2a28c..32711db0c82e 100644 --- a/drivers/pinctrl/qcom/pinctrl-eliza.c +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -1402,8 +1402,8 @@ static const struct msm_pingroup eliza_groups[] = { [35] = PINGROUP(35, qup1_se1, qup1_se5, tb_trig_sdc2, gcc_gp2, qdss_gpio_tracedata, _, _, _, _, _, _), [36] = PINGROUP(36, qup1_se4_01, qup1_se4_23, ibi_i3c, _, _, _, _, _, _, _, _), [37] = PINGROUP(37, qup1_se4_01, qup1_se4_23, ibi_i3c, _, _, _, _, _, _, _, _), - [38] = PINGROUP(38, _, _, _, _, _, _, _, _, _, _, _), - [39] = PINGROUP(39, _, _, _, _, _, _, _, _, _, _, _), + [38] = PINGROUP(38, sdc2, _, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, sdc2, _, _, _, _, _, _, _, _, _, _), [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6_l3_mira, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), [41] = PINGROUP(41, _, _, _, _, _, _, _, _, _, _, _), [42] = PINGROUP(42, qup1_se6, qup1_se2, qup1_se6_l1_mira, qdss_gpio_tracedata, gnss_adc0, ddr_pxi1, _, _, _, _, _), @@ -1412,10 +1412,10 @@ static const struct msm_pingroup eliza_groups[] = { [45] = PINGROUP(45, qup1_se3, _, _, _, _, _, _, _, _, _, _), [46] = PINGROUP(46, qup1_se3, hdmi_tx_cec, _, _, _, _, _, _, _, _, _), [47] = PINGROUP(47, qup1_se3, hdmi_hot_plug, _, _, _, _, _, _, _, _, _), - [48] = PINGROUP(48, _, _, _, _, _, _, _, _, _, _, _), - [49] = PINGROUP(49, _, _, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, sdc2, _, _, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, sdc2, _, _, _, _, _, _, _, _, _, _), [50] = PINGROUP(50, sdc2_fb_clk, _, _, _, _, _, _, _, _, _, _), - [51] = PINGROUP(51, _, _, _, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, sdc2, _, _, _, _, _, _, _, _, _, _), [52] = PINGROUP(52, qup1_se2, pcie1_clk_req_n, qup1_se2_l2_mirb, ddr_bist_complete, qdss_gpio_tracedata, _, vsense_trigger_mirnat, _, _, _, _), [53] = PINGROUP(53, qup1_se2, qup1_se2_l3_mirb, gcc_gp1, ddr_bist_stop, _, qdss_gpio_tracedata, _, _, _, _, _), [54] = PINGROUP(54, qup1_se2_l2_mira, qup1_se6_l1_mirb, qdss_gpio_tracedata, gnss_adc1, atest_usb, ddr_pxi0, _, _, _, _, _), @@ -1426,7 +1426,7 @@ static const struct msm_pingroup eliza_groups[] = { [59] = PINGROUP(59, _, _, _, _, _, _, _, _, _, _, _), [60] = PINGROUP(60, i2s0_sck, _, _, _, _, _, _, _, _, _, _), [61] = PINGROUP(61, i2s0_ws, _, _, _, _, _, _, _, _, _, _), - [62] = PINGROUP(62, _, _, _, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, sdc2, _, _, _, _, _, _, _, _, _, _), [63] = PINGROUP(63, resout_gpio, i2s0_data1, cci_timer, vfr_0, _, _, _, _, _, _, _), [64] = PINGROUP(64, i2s0_data0, _, _, _, _, _, _, _, _, _, _), [65] = PINGROUP(65, cam_mclk, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _), From 4aa4d246c81f360d2609c6ffe0b72f2895973447 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Tue, 9 Jun 2026 19:13:32 +0530 Subject: [PATCH 3313/5214] perf pmu: Use scnprintf in buffer offset calculations Replace snprintf with scnprintf in buffer offset calculations to ensure the 'used' count will not exceed the "len". The current logic in perf_pmu__for_each_event uses an unconditional + 1 increment to buf_used to account for null terminators. This can cause a stack buffer overflow in the subsequent scnprintf call. When the local stack buffer buf (1024 bytes) is full, buf_used can reach 1025. This causes the subsequent remaining space calculation sizeof(buf) - buf_used to underflow. Use sub_non_neg() to see if space actually existed, and only increment the offset if remaining space is present. Changes includes: - Use sub_non_neg to check if space exists - Replacing snprintf with scnprintf to ensure the return value reflects the actual bytes written into the buffer. - Only increment buf_used by 1 if space exists - If a parameterized event uses a built-in perf keyword for its parameter name (eg, config=?), the lexer parses it as a predefined term token, which sets term->config to NULL. Add check to use parse_events__term_type_str() if term->config is NULL. Signed-off-by: Athira Rajeev Cc: Adrian Hunter Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Namhyung Kim Cc: Shivani Nittor Cc: Tanushree.Shah@ibm.com Cc: Tejas.Manhas1@ibm.com Cc: Thomas Richter Cc: Venkat Rao Bagalkote Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index e765a7ffb0d6..1539960ba23b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -2146,15 +2146,19 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, pr_err("Failure to parse '%s' terms '%s': %d\n", alias->name, alias->terms, ret); parse_events_terms__exit(&terms); - snprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name); + scnprintf(buf, len, "%.*s/%s/", (int)pmu_name_len, pmu->name, alias->name); return buf; } - used = snprintf(buf, len, "%.*s/%s", (int)pmu_name_len, pmu->name, alias->name); + used = scnprintf(buf, len, "%.*s/%s", (int)pmu_name_len, pmu->name, alias->name); list_for_each_entry(term, &terms.terms, list) { + const char *name = term->config; + + if (!name) + name = parse_events__term_type_str(term->type_term); if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) - used += snprintf(buf + used, sub_non_neg(len, used), - ",%s=%s", term->config, + used += scnprintf(buf + used, sub_non_neg(len, used), + ",%s=%s", name, term->val.str); } parse_events_terms__exit(&terms); @@ -2218,6 +2222,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, int ret = 0; struct hashmap_entry *entry; size_t bkt; + size_t size_rem; if (perf_pmu__is_tracepoint(pmu)) return tp_pmu__for_each_event(pmu, state, cb); @@ -2251,17 +2256,30 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, } buf_used = strlen(buf) + 1; } + info.scale_unit = NULL; if (strlen(event->unit) || event->scale != 1.0) { - info.scale_unit = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%G%s", event->scale, event->unit) + 1; + /* Check the remaining space */ + size_rem = sub_non_neg(sizeof(buf), buf_used); + + if (size_rem > 0) { + info.scale_unit = buf + buf_used; + buf_used += scnprintf(buf + buf_used, size_rem, "%G%s", + event->scale, event->unit) + 1; + } } info.desc = event->desc; info.long_desc = event->long_desc; - info.encoding_desc = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%.*s/%s/", (int)pmu_name_len, info.pmu_name, event->terms) + 1; + info.encoding_desc = NULL; + + /* Check the remaining space */ + size_rem = sub_non_neg(sizeof(buf), buf_used); + if (size_rem > 0) { + info.encoding_desc = buf + buf_used; + buf_used += scnprintf(buf + buf_used, size_rem, "%.*s/%s/", + (int)pmu_name_len, info.pmu_name, event->terms) + 1; + } + info.str = event->terms; info.topic = event->topic; info.deprecated = perf_pmu_alias__check_deprecated(pmu, event); @@ -2271,7 +2289,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, } if (pmu->selectable) { info.name = buf; - snprintf(buf, sizeof(buf), "%s//", pmu->name); + scnprintf(buf, sizeof(buf), "%s//", pmu->name); info.alias = NULL; info.scale_unit = NULL; info.desc = NULL; From 68ca50bc0fa64841cd73b8a1538df1d7f7eb4108 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:06 +0100 Subject: [PATCH 3314/5214] perf cs-etm: Queue context packets for frontend PE_CONTEXT elements update the context ID and exception level, but the decoder may still have prior packets cached for frontend processing. Updating the context immediately in the decoder backend can make those cached packets get consumed with the wrong thread or EL state. Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend, this keeps context changes ordered with the rest of the packet stream and avoids mismatches when synthesizing samples from cached packets. Separate the memory access function into one for the frontend and one for decoding. The frontend also needs memory access to attach the instruction to samples. Because the frontend does memory access for both previous and current packets, change all the frontend memory access function signatures to take both a tidq and packet. But backend always uses the current backend EL and thread from the tidq. Treat context packets as a boundary for branch sample generation and remove tidq->prev_packet_thread because it's not possible to branch to a different thread, so only tracking the current thread is required for sample generation. Fixes: e573e978fb12e160 ("perf cs-etm: Inject capabilitity for CoreSight traces") Reported-by: Amir Ayupov Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/ Co-authored-by: James Clark Signed-off-by: Leo Yan Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Robert Walker Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/cs-etm-decoder/cs-etm-decoder.c | 21 +- tools/perf/util/cs-etm.c | 236 +++++++++++------- tools/perf/util/cs-etm.h | 8 +- 3 files changed, 163 insertions(+), 102 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index dee3020ceaa9..26940f1f1b0b 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -402,6 +402,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq, packet_queue->packet_buffer[et].flags = 0; packet_queue->packet_buffer[et].exception_number = UINT32_MAX; packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id; + packet_queue->packet_buffer[et].el = ocsd_EL_unknown; + packet_queue->packet_buffer[et].tid = -1; if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1) return OCSD_RESP_WAIT; @@ -449,6 +451,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq, packet->last_instr_type = elem->last_i_type; packet->last_instr_subtype = elem->last_i_subtype; packet->last_instr_cond = elem->last_instr_cond; + packet->el = elem->context.exception_level; if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT) packet->last_instr_taken_branch = elem->last_instr_exec; @@ -525,7 +528,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, const ocsd_generic_trace_elem *elem, const uint8_t trace_chan_id) { + struct cs_etm_packet *packet; pid_t tid = -1; + int ret; /* * Process the PE_CONTEXT packets if we have a valid contextID or VMID. @@ -546,12 +551,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, break; } - if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id, - elem->context.exception_level)) + if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id, + elem->context.exception_level, tid)) return OCSD_RESP_FATAL_SYS_ERR; - if (tid == -1) - return OCSD_RESP_CONT; + ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, + CS_ETM_CONTEXT); + if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) + return ret; + + packet = &packet_queue->packet_buffer[packet_queue->tail]; + packet->tid = tid; + packet->el = elem->context.exception_level; /* * A timestamp is generated after a PE_CONTEXT element so make sure @@ -559,7 +570,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, */ cs_etm_decoder__reset_timestamp(packet_queue); - return OCSD_RESP_CONT; + return ret; } static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 40c6ddfa8c8d..5e92359f51a7 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -85,15 +85,22 @@ struct cs_etm_traceid_queue { u64 period_instructions; size_t last_branch_pos; union perf_event *event_buf; - struct thread *thread; - struct thread *prev_packet_thread; - ocsd_ex_level prev_packet_el; - ocsd_ex_level el; struct branch_stack *last_branch; struct branch_stack *last_branch_rb; struct cs_etm_packet *prev_packet; struct cs_etm_packet *packet; struct cs_etm_packet_queue packet_queue; + + struct thread *decode_thread; + ocsd_ex_level decode_el; + + /* + * The frontend accesses the EL from '[prev_]packet' because it needs + * previous EL for branch and current EL for instruction samples. It's + * not possible to change thread in a single branch sample so no need to + * store or access the thread through the packet. + */ + struct thread *frontend_thread; }; enum cs_etm_format { @@ -614,10 +621,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; tidq->trace_chan_id = trace_chan_id; - tidq->el = tidq->prev_packet_el = ocsd_EL_unknown; - tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1, + tidq->decode_el = ocsd_EL_unknown; + tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1, + queue->tid); + tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1, queue->tid); - tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host); tidq->packet = zalloc(sizeof(struct cs_etm_packet)); if (!tidq->packet) @@ -750,21 +758,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, /* * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for * the next incoming packet. - * - * Threads and exception levels are also tracked for both the - * previous and current packets. This is because the previous - * packet is used for the 'from' IP for branch samples, so the - * thread at that time must also be assigned to that sample. - * Across discontinuity packets the thread can change, so by - * tracking the thread for the previous packet the branch sample - * will have the correct info. */ tmp = tidq->packet; tidq->packet = tidq->prev_packet; tidq->prev_packet = tmp; - tidq->prev_packet_el = tidq->el; - thread__put(tidq->prev_packet_thread); - tidq->prev_packet_thread = thread__get(tidq->thread); } } @@ -937,8 +934,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) /* Free this traceid_queue from the array */ tidq = etmq->traceid_queues[idx]; - thread__zput(tidq->thread); - thread__zput(tidq->prev_packet_thread); + thread__zput(tidq->frontend_thread); + thread__zput(tidq->decode_thread); zfree(&tidq->event_buf); zfree(&tidq->last_branch); zfree(&tidq->last_branch_rb); @@ -1083,47 +1080,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address, } } -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, - u64 address, size_t size, u8 *buffer, - const ocsd_mem_space_acc_t mem_space) +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space, + ocsd_ex_level el, struct thread *thread) { u8 cpumode; u64 offset; int len; struct addr_location al; struct dso *dso; - struct cs_etm_traceid_queue *tidq; int ret = 0; if (!etmq) return 0; addr_location__init(&al); - tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); - if (!tidq) - goto out; /* - * We've already tracked EL along side the PID in cs_etm__set_thread() - * so double check that it matches what OpenCSD thinks as well. It - * doesn't distinguish between EL0 and EL1 for this mem access callback - * so we had to do the extra tracking. Skip validation if it's any of - * the 'any' values. + * We track EL for the frontend and the backend when receiving context + * and range packets. OpenCSD doesn't distinguish between EL0 and EL1 + * for this mem access callback so we had to do the extra tracking. Skip + * validation if it's any of the 'any' values. */ if (!(mem_space == OCSD_MEM_SPACE_ANY || mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) { if (mem_space & OCSD_MEM_SPACE_EL1N) { /* Includes both non secure EL1 and EL0 */ - assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0); + assert(el == ocsd_EL1 || el == ocsd_EL0); } else if (mem_space & OCSD_MEM_SPACE_EL2) - assert(tidq->el == ocsd_EL2); + assert(el == ocsd_EL2); else if (mem_space & OCSD_MEM_SPACE_EL3) - assert(tidq->el == ocsd_EL3); + assert(el == ocsd_EL3); } - cpumode = cs_etm__cpu_mode(etmq, address, tidq->el); + cpumode = cs_etm__cpu_mode(etmq, address, el); - if (!thread__find_map(tidq->thread, cpumode, address, &al)) + if (!thread__find_map(thread, cpumode, address, &al)) goto out; dso = map__dso(al.map); @@ -1138,7 +1131,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, map__load(al.map); - len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)), + len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), offset, buffer, size); if (len <= 0) { @@ -1158,6 +1151,30 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, return ret; } +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, + u64 address, size_t size, u8 *buffer) +{ + return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el, + tidq->frontend_thread); +} + +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space) +{ + struct cs_etm_traceid_queue *tidq; + + tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); + if (!tidq) + return 0; + + return __cs_etm__mem_access(etmq, address, size, buffer, + mem_space, tidq->decode_el, + tidq->decode_thread); +} + static struct cs_etm_queue *cs_etm__alloc_queue(void) { struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); @@ -1333,12 +1350,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq) } static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, - u8 trace_chan_id, u64 addr) + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr) { u8 instrBytes[2]; - cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes), - instrBytes, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + ARRAY_SIZE(instrBytes), instrBytes); /* * T32 instruction size is indicated by bits[15:11] of the first * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111 @@ -1371,16 +1389,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet) } static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 offset) { if (packet->isa == CS_ETM_ISA_T32) { u64 addr = packet->start_addr; while (offset) { - addr += cs_etm__t32_instr_size(etmq, - trace_chan_id, addr); + addr += cs_etm__t32_instr_size(etmq, tidq, packet, + addr); offset--; } return addr; @@ -1490,34 +1508,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq) return etmq->buf_len; } -static void cs_etm__set_thread(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, pid_t tid, - ocsd_ex_level el) +/* + * Convert a raw thread number to a thread struct and assign it to **thread. + */ +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq, + ocsd_ex_level el, pid_t tid, + struct thread **thread) { struct machine *machine = cs_etm__get_machine(etmq, el); + if (!machine || !*thread) + return -EINVAL; + if (tid != -1) { - thread__zput(tidq->thread); - tidq->thread = machine__find_thread(machine, -1, tid); + thread__zput(*thread); + *thread = machine__find_thread(machine, -1, tid); } /* Couldn't find a known thread */ - if (!tidq->thread) - tidq->thread = machine__idle_thread(machine); + if (!*thread) + *thread = machine__idle_thread(machine); - tidq->el = el; + return 0; } -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el) +/* + * Set the thread and EL of the decode context which is ahead in time of the + * frontend context. + */ +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, + ocsd_ex_level el, pid_t tid) { struct cs_etm_traceid_queue *tidq; + int ret; tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); if (!tidq) return -EINVAL; - cs_etm__set_thread(etmq, tidq, tid, el); + ret = cs_etm__etmq_update_thread(etmq, el, tid, + &tidq->decode_thread); + if (ret) + return ret; + + tidq->decode_el = el; return 0; } @@ -1527,8 +1562,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq) } static void cs_etm__copy_insn(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, struct perf_sample *sample) { /* @@ -1545,14 +1580,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq, * cs_etm__t32_instr_size(). */ if (packet->isa == CS_ETM_ISA_T32) - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id, + sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet, sample->ip); /* Otherwise, A64 and A32 instruction size are always 32-bit. */ else sample->insn_len = 4; - cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len, - (void *)sample->insn, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip, + sample->insn_len, (void *)sample->insn); } u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) @@ -1579,6 +1614,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq, static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr, u64 period) { int ret = 0; @@ -1588,23 +1624,23 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, perf_sample__init(&sample, /*all=*/true); event->sample.header.type = PERF_RECORD_SAMPLE; - event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el); + event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = addr; - sample.pid = thread__pid(tidq->thread); - sample.tid = thread__tid(tidq->thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.id = etmq->etm->instructions_id; sample.stream_id = etmq->etm->instructions_id; sample.period = period; - sample.cpu = tidq->packet->cpu; + sample.cpu = packet->cpu; sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample); + cs_etm__copy_insn(etmq, tidq, packet, &sample); if (etm->synth_opts.last_branch) sample.branch_stack = tidq->last_branch; @@ -1649,15 +1685,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, event->sample.header.type = PERF_RECORD_SAMPLE; event->sample.header.misc = cs_etm__cpu_mode(etmq, ip, - tidq->prev_packet_el); + tidq->prev_packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = ip; - sample.pid = thread__pid(tidq->prev_packet_thread); - sample.tid = thread__tid(tidq->prev_packet_thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.addr = cs_etm__first_executed_instr(tidq->packet); sample.id = etmq->etm->branches_id; sample.stream_id = etmq->etm->branches_id; @@ -1666,8 +1702,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet, - &sample); + cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample); /* * perf report cannot handle events without a branch stack @@ -1788,7 +1823,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, { struct cs_etm_auxtrace *etm = etmq->etm; int ret; - u8 trace_chan_id = tidq->trace_chan_id; u64 instrs_prev; /* Get instructions remainder from previous packet */ @@ -1874,10 +1908,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, * been executed, but PC has not advanced to next * instruction) */ - addr = cs_etm__instr_addr(etmq, trace_chan_id, - tidq->packet, offset - 1); + addr = cs_etm__instr_addr(etmq, tidq, tidq->packet, + offset - 1); ret = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->packet, addr, etm->instructions_sample_period); if (ret) return ret; @@ -1959,7 +1993,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2014,7 +2048,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2051,9 +2085,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq) return etmq->buf_len; } -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, - struct cs_etm_packet *packet, - u64 end_addr) +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 end_addr) { /* Initialise to keep compiler happy */ u16 instr16 = 0; @@ -2075,8 +2109,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * so below only read 2 bytes as instruction size for T32. */ addr = end_addr - 2; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16), - (u8 *)&instr16, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr16), (u8 *)&instr16); if ((instr16 & 0xFF00) == 0xDF00) return true; @@ -2091,8 +2125,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +---------+---------+-------------------------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0x0F000000) == 0x0F000000 && (instr32 & 0xF0000000) != 0xF0000000) return true; @@ -2108,8 +2142,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +-----------------------+---------+-----------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0xFFE0001F) == 0xd4000001) return true; @@ -2125,7 +2159,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2140,7 +2173,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, */ if (magic == __perf_cs_etmv4_magic) { if (packet->exception_number == CS_ETMV4_EXC_CALL && - cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; } @@ -2178,7 +2211,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2204,7 +2236,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, * (SMC, HVC) are taken as sync exceptions. */ if (packet->exception_number == CS_ETMV4_EXC_CALL && - !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + !cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; @@ -2228,7 +2260,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, { struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; - u8 trace_chan_id = tidq->trace_chan_id; u64 magic; int ret; @@ -2309,11 +2340,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, if (prev_packet->flags == (PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT) && - cs_etm__is_svc_instr(etmq, trace_chan_id, - packet, packet->start_addr)) + cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) { prev_packet->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET; + } break; case CS_ETM_DISCONTINUITY: /* @@ -2394,6 +2425,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT; break; + case CS_ETM_CONTEXT: case CS_ETM_EMPTY: default: break; @@ -2469,6 +2501,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, */ cs_etm__sample(etmq, tidq); break; + case CS_ETM_CONTEXT: + /* + * Update context but don't swap packet. Keep the + * previous one for branch source address info, if + * tracing the kernel the context packet will be emitted + * between two ranges. + */ + ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, + tidq->packet->tid, + &tidq->frontend_thread); + if (ret) + goto out; + break; case CS_ETM_EXCEPTION: case CS_ETM_EXCEPTION_RET: /* @@ -2497,6 +2542,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, } } +out: return ret; } @@ -2620,7 +2666,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, if (!tidq) continue; - if (tid == -1 || thread__tid(tidq->thread) == tid) + if (tid == -1 || thread__tid(tidq->frontend_thread) == tid) cs_etm__run_per_thread_timeless_decoder(etmq); } else cs_etm__run_per_cpu_timeless_decoder(etmq); @@ -3328,7 +3374,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) */ if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, 0x0L, ((u64) -1L), - cs_etm__mem_access)) + cs_etm__decoder_mem_access)) goto out_free_decoder; zfree(&t_params); diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index aa9bb4a32eca..b81099c2b301 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -158,6 +158,7 @@ enum cs_etm_sample_type { CS_ETM_DISCONTINUITY, CS_ETM_EXCEPTION, CS_ETM_EXCEPTION_RET, + CS_ETM_CONTEXT, }; enum cs_etm_isa { @@ -184,6 +185,8 @@ struct cs_etm_packet { u8 last_instr_size; u8 trace_chan_id; int cpu; + int el; + pid_t tid; }; #define CS_ETM_PACKET_MAX_BUFFER 1024 @@ -259,8 +262,9 @@ enum cs_etm_pid_fmt { #include int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el); +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, ocsd_ex_level el, + pid_t tid); bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq); void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, u8 trace_chan_id); From d281fb9dfdaf30af4dfc68262b6b4997439a6aea Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:07 +0100 Subject: [PATCH 3315/5214] perf test: Add workload-ctl option Add a --workload-ctl=fifo:ctl-fifo[,ack-fifo] option for 'perf test -w'. When set, run_workload() opens the named FIFO, writes enable before invoking the builtin workload, writes disable before returning, and waits for ack responses when an ack FIFO is provided to ensure that the workload doesn't run until the events are enabled. This can be used to limit the scope of the recording to only the workload execution and avoid recording Perf setup and teardown code if Perf record is started with events disabled (-D 1). Assisted-by: Codex:GPT-5.5 Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Leo Yan Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-test.txt | 12 ++ tools/perf/tests/builtin-test.c | 184 ++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 32da0d1fa86a..2f4a91f5b9dc 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -69,3 +69,15 @@ OPTIONS --list-workloads:: List the available workloads to use with -w/--workload. + +--record-ctl=fifo:ctl-fifo[,ack-fifo]:: + This option is used to communicate with a perf record session in order + to control the recording scope to only the workload and avoid recording + setup and teardown code. When specifying this option, the same FIFO path + must be specified in the record session via: + + perf record -D -1 --control=fifo:ctl-fifo[,ack-fifo] ... + + Perf test sends 'enable' and 'disable' commands through ctl-fifo to + control event recording. If 'ack-fifo' is provided, the workload runner + waits for an 'ack' response after each command. diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index b64fc2204f22..86ea427eb0aa 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -56,6 +56,7 @@ static unsigned int runs_per_test = 1; static unsigned int failure_snippet_lines = 10; const char *dso_to_test; const char *test_objdump_path = "objdump"; +static const char *workload_control; /* * List of architecture specific tests. Not a weak symbol as the array length is @@ -168,6 +169,11 @@ static struct test_workload *workloads[] = { #endif }; +struct workload_control { + int ctl_fd; + int ack_fd; +}; + #define workloads__for_each(workload) \ for (unsigned i = 0; i < ARRAY_SIZE(workloads) && ({ workload = workloads[i]; 1; }); i++) @@ -1387,13 +1393,185 @@ static int workloads__fprintf_list(FILE *fp) return printed; } +static int perf_control_open_fifo(struct workload_control *ctl, const char *str) +{ + char *s, *p; + int ret; + + if (strncmp(str, "fifo:", 5)) + return -EINVAL; + + str += 5; + if (!*str || *str == ',') + return -EINVAL; + + s = strdup(str); + if (!s) + return -ENOMEM; + + p = strchr(s, ','); + if (p) + *p = '\0'; + + ctl->ctl_fd = open(s, O_WRONLY | O_CLOEXEC); + if (ctl->ctl_fd < 0) { + ret = -errno; + pr_err("Failed to open workload control FIFO '%s': %m\n", s); + free(s); + return ret; + } + + if (p && *++p) { + ctl->ack_fd = open(p, O_RDONLY | O_CLOEXEC); + if (ctl->ack_fd < 0) { + ret = -errno; + pr_err("Failed to open workload control ack FIFO '%s': %m\n", p); + close(ctl->ctl_fd); + ctl->ctl_fd = -1; + free(s); + return ret; + } + } + + free(s); + return 0; +} + +static int perf_control_open(struct workload_control *ctl) +{ + int ret; + + if (!workload_control) + return 0; + + ret = perf_control_open_fifo(ctl, workload_control); + + if (ret == -EINVAL) { + pr_err("Unsupported workload control spec '%s', expected fifo:ctl-fifo[,ack-fifo]\n", + workload_control); + } + + return ret; +} + +static void perf_control_close(struct workload_control *ctl) +{ + if (ctl->ctl_fd >= 0) { + close(ctl->ctl_fd); + ctl->ctl_fd = -1; + } + if (ctl->ack_fd >= 0) { + close(ctl->ack_fd); + ctl->ack_fd = -1; + } +} + +static int perf_control_write_cmd(int fd, const char *cmd) +{ + size_t len = strlen(cmd); + ssize_t ret; + + while (len) { + ret = write(fd, cmd, len); + if (ret < 0) { + if (errno == EINTR) + continue; + pr_err("Failed to write perf control command: %m\n"); + return -1; + } + + if (!ret) { + pr_err("Failed to write perf control command: short write\n"); + return -1; + } + + cmd += ret; + len -= ret; + } + + return 0; +} + +static int perf_control_read_ack(int fd) +{ + char buf[16]; + ssize_t ret; + + do { + ret = read(fd, buf, sizeof(buf) - 1); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + pr_err("Failed to read perf control ack: %m\n"); + return -1; + } + + if (!ret) { + pr_err("Unexpected EOF while reading perf control ack\n"); + return -1; + } + + buf[ret] = '\0'; + for (ssize_t i = 0; i < ret; i++) { + if (buf[i] == '\n' || buf[i] == '\0') { + buf[i] = '\0'; + break; + } + } + + if (strcmp(buf, "ack")) { + pr_err("Unexpected perf control ack: %s\n", buf); + return -1; + } + + return 0; +} + +static int perf_control_send(struct workload_control *ctl, const char *cmd) +{ + if (ctl->ctl_fd < 0) + return 0; + + if (perf_control_write_cmd(ctl->ctl_fd, cmd)) + return -1; + + if (ctl->ack_fd >= 0 && perf_control_read_ack(ctl->ack_fd)) + return -1; + + return 0; +} + static int run_workload(const char *work, int argc, const char **argv) { struct test_workload *twl; workloads__for_each(twl) { - if (!strcmp(twl->name, work)) - return twl->func(argc, argv); + struct workload_control ctl = { + .ctl_fd = -1, + .ack_fd = -1, + }; + int control_ret, ret; + + if (strcmp(twl->name, work)) + continue; + + ret = perf_control_open(&ctl); + if (ret) + return ret; + + if (perf_control_send(&ctl, "enable\n")) { + perf_control_close(&ctl); + return -1; + } + + ret = twl->func(argc, argv); + + control_ret = perf_control_send(&ctl, "disable\n"); + perf_control_close(&ctl); + if (control_ret) + return -1; + + return ret; } pr_info("No workload found: %s\n", work); @@ -1486,6 +1664,8 @@ int cmd_test(int argc, const char **argv) OPT_UINTEGER('r', "runs-per-test", &runs_per_test, "Run each test the given number of times, default 1"), OPT_STRING('w', "workload", &workload, "work", "workload to run for testing, use '--list-workloads' to list the available ones."), + OPT_STRING(0, "record-ctl", &workload_control, "fifo:ctl-fifo[,ack-fifo]", + "Write enable to the fifo just before running the workload and disable after, with optional ack from ack-fifo"), OPT_BOOLEAN(0, "list-workloads", &list_workloads, "List the available builtin workloads to use with -w/--workload"), OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"), OPT_STRING(0, "objdump", &test_objdump_path, "path", From 4cb5dd0379999af455941ab87d0b30c2ba7d9d66 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:08 +0100 Subject: [PATCH 3316/5214] perf test: Add a workload that forces context switches This workload launches two processes that block when reading and writing to each other forcing the other process to be scheduled for each read/write pair. Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Leo Yan Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-test.txt | 7 +- tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 1 + .../tests/workloads/context_switch_loop.c | 110 ++++++++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 tools/perf/tests/workloads/context_switch_loop.c diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 2f4a91f5b9dc..213eb62603eb 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -55,15 +55,16 @@ OPTIONS -w:: --workload=:: - Run a built-in workload, to list them use '--list-workloads', current ones include: - noploop, thloop, leafloop, sqrtloop, brstack, datasym and landlock. + Run a built-in workload, to list them use '--list-workloads', current + ones include: noploop, thloop, leafloop, sqrtloop, brstack, datasym, + context_switch_loop and landlock. Used with the shell script regression tests. Some accept an extra parameter: seconds: leafloop, noploop, sqrtloop, thloop - nrloops: brstack + nrloops: brstack, context_switch_loop The datasym and landlock workloads don't accept any. diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 86ea427eb0aa..9284f897de3c 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -163,6 +163,7 @@ static struct test_workload *workloads[] = { &workload__traploop, &workload__inlineloop, &workload__jitdump, + &workload__context_switch_loop, #ifdef HAVE_RUST_SUPPORT &workload__code_with_type, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index bf8ff7d54727..7cd4da4e96d3 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -245,6 +245,7 @@ DECLARE_WORKLOAD(landlock); DECLARE_WORKLOAD(traploop); DECLARE_WORKLOAD(inlineloop); DECLARE_WORKLOAD(jitdump); +DECLARE_WORKLOAD(context_switch_loop); #ifdef HAVE_RUST_SUPPORT DECLARE_WORKLOAD(code_with_type); diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 0eb6d99528eb..7134a031cb7c 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -10,6 +10,7 @@ perf-test-y += landlock.o perf-test-y += traploop.o perf-test-y += inlineloop.o perf-test-y += jitdump.o +perf-test-y += context_switch_loop.o ifeq ($(CONFIG_RUST_SUPPORT),y) perf-test-y += code_with_type.o diff --git a/tools/perf/tests/workloads/context_switch_loop.c b/tools/perf/tests/workloads/context_switch_loop.c new file mode 100644 index 000000000000..5431af6147e6 --- /dev/null +++ b/tools/perf/tests/workloads/context_switch_loop.c @@ -0,0 +1,110 @@ + +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include "../tests.h" + +static int loops = 100; +static char buf; +int context_switch_loop_work = 1234; + +#define write_block(fd) \ + do { \ + if (write(fd, &buf, 1) <= 0) \ + return 1; \ + } while (0) + +#define read_block(fd) \ + do { \ + if (read(fd, &buf, 1) <= 0) \ + return 1; \ + } while (0) + +/* Not static to avoid LTO clobbering the function name */ +int context_switch_loop_proc1(int in_fd, int out_fd); +int context_switch_loop_proc1(int in_fd, int out_fd) +{ + for (int i = 0; i < loops; i++) { + read_block(in_fd); + context_switch_loop_work += i * 3; + write_block(out_fd); + } + return 0; +} + +int context_switch_loop_proc2(int in_fd, int out_fd); +int context_switch_loop_proc2(int in_fd, int out_fd) +{ + for (int i = 0; i < loops; i++) { + write_block(out_fd); + context_switch_loop_work += i * 7; + read_block(in_fd); + } + return 0; +} + +/* + * Launches two processes that take turns to execute a multiplication N times + */ +static int context_switch_loop(int argc, const char **argv) +{ + int a_to_b[2], b_to_a[2]; + pid_t proc1_pid; + int status; + int ret; + + if (argc > 0) { + loops = atoi(argv[0]); + if (loops < 0) { + fprintf(stderr, "Invalid number of loops: %s\n", argv[0]); + return 1; + } + } + + if (pipe(a_to_b) || pipe(b_to_a)) { + perror("Pipe error"); + return 1; + } + + proc1_pid = fork(); + if (proc1_pid < 0) { + perror("Fork error"); + return 1; + } + + if (!proc1_pid) { + close(a_to_b[0]); + close(b_to_a[1]); + prctl(PR_SET_NAME, "proc1", 0, 0, 0); + ret = context_switch_loop_proc1(b_to_a[0], a_to_b[1]); + close(a_to_b[1]); + close(b_to_a[0]); + exit(ret); + } + + close(a_to_b[1]); + close(b_to_a[0]); + prctl(PR_SET_NAME, "proc2", 0, 0, 0); + ret = context_switch_loop_proc2(a_to_b[0], b_to_a[1]); + close(a_to_b[0]); + close(b_to_a[1]); + + if (ret) { + kill(proc1_pid, SIGKILL); + return ret; + } + + if (waitpid(proc1_pid, &status, 0) != proc1_pid || !WIFEXITED(status) || + WEXITSTATUS(status)) + return 1; + + return 0; +} + +DEFINE_WORKLOAD(context_switch_loop); From be7ef892f306fc9bb07e2d1a0699de14159fe219 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:09 +0100 Subject: [PATCH 3317/5214] perf test cs-etm: Test process attribution Run the context switch workload on one CPU and trace it to test that symbols are attributed to the correct process and that the attribution changes at the exact point that the context switch happened. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/coresight/context_switch_thread.sh | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 tools/perf/tests/shell/coresight/context_switch_thread.sh diff --git a/tools/perf/tests/shell/coresight/context_switch_thread.sh b/tools/perf/tests/shell/coresight/context_switch_thread.sh new file mode 100755 index 000000000000..2b9c44b86c59 --- /dev/null +++ b/tools/perf/tests/shell/coresight/context_switch_thread.sh @@ -0,0 +1,69 @@ +#!/bin/bash -e +# CoreSight context switch thread attribution (exclusive) + +# SPDX-License-Identifier: GPL-2.0 + +# If CoreSight is not available, skip the test +perf list pmu | grep -q cs_etm || exit 2 + +if [ "$(id -u)" != 0 ]; then + # Requires root for "-C 0" in record command + echo "[Skip] No root permission" + exit 2 +fi + +tmpdir=$(mktemp -d /tmp/__perf_test.coresight_context_switch.XXXXX) + +cleanup() { + rm -rf "${tmpdir}" + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +check_samples() { + owner_samples=$(grep -c "proc1.*context_switch_loop_proc1" "$tmpdir/script" || true) + next_samples=$(grep -c "proc2.*context_switch_loop_proc2" "$tmpdir/script" || true) + + if [ "$owner_samples" -eq 0 ] || [ "$next_samples" -eq 0 ]; then + echo "No samples found" + cleanup + exit 1 + fi + + if grep "proc2.*context_switch_loop_proc1" "$tmpdir/script"; then + echo "Thread1 symbol was attributed to proc2" + cleanup + exit 1 + fi + + if grep "proc1.*context_switch_loop_proc2" "$tmpdir/script"; then + echo "Thread2 symbol was attributed to proc1" + cleanup + exit 1 + fi +} + +cf="$tmpdir/ctl" +af="$tmpdir/ack" +mkfifo "$cf" "$af" + +# Pin to one CPU so the two threads alternate running but record into the same +# trace buffer. Start disabled and use the control FIFO to only record the +# workload and not startup. +perf record -o "$tmpdir/data" -e cs_etm/timestamp=0/u -C 0 -D -1 --control fifo:"$cf","$af" -- \ + taskset --cpu-list 0 perf test --record-ctl fifo:"$cf","$af" \ + -w context_switch_loop > /dev/null 2>&1 + +# Test both instruction and branch sample generation modes. +perf script -i "$tmpdir/data" --itrace=i4 -F comm,pid,tid,ip,sym > "$tmpdir/script" 2>/dev/null +check_samples +perf script -i "$tmpdir/data" --itrace=b -F comm,pid,tid,ip,sym > "$tmpdir/script" 2>/dev/null +check_samples + +cleanup +exit 0 From 3fdf30607e28290d914c44b035f35a9a92512562 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:10 +0100 Subject: [PATCH 3318/5214] perf test: Add deterministic workload Add a workload that does the same thing every time for testing CPU trace decoding. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-test.txt | 4 +-- tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 2 ++ tools/perf/tests/workloads/deterministic.c | 39 ++++++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tools/perf/tests/workloads/deterministic.c diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 213eb62603eb..c50a4b2d2d29 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -57,7 +57,7 @@ OPTIONS --workload=:: Run a built-in workload, to list them use '--list-workloads', current ones include: noploop, thloop, leafloop, sqrtloop, brstack, datasym, - context_switch_loop and landlock. + context_switch_loop, deterministic and landlock. Used with the shell script regression tests. @@ -66,7 +66,7 @@ OPTIONS seconds: leafloop, noploop, sqrtloop, thloop nrloops: brstack, context_switch_loop - The datasym and landlock workloads don't accept any. + The datasym, landlock and deterministic workloads don't accept any. --list-workloads:: List the available workloads to use with -w/--workload. diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 9284f897de3c..ef7e3f52a383 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -164,6 +164,7 @@ static struct test_workload *workloads[] = { &workload__inlineloop, &workload__jitdump, &workload__context_switch_loop, + &workload__deterministic, #ifdef HAVE_RUST_SUPPORT &workload__code_with_type, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 7cd4da4e96d3..bcfe9c33fc66 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -246,6 +246,7 @@ DECLARE_WORKLOAD(traploop); DECLARE_WORKLOAD(inlineloop); DECLARE_WORKLOAD(jitdump); DECLARE_WORKLOAD(context_switch_loop); +DECLARE_WORKLOAD(deterministic); #ifdef HAVE_RUST_SUPPORT DECLARE_WORKLOAD(code_with_type); diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 7134a031cb7c..90f2d8aa4941 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -11,6 +11,7 @@ perf-test-y += traploop.o perf-test-y += inlineloop.o perf-test-y += jitdump.o perf-test-y += context_switch_loop.o +perf-test-y += deterministic.o ifeq ($(CONFIG_RUST_SUPPORT),y) perf-test-y += code_with_type.o @@ -23,3 +24,4 @@ CFLAGS_brstack.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_datasym.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_inlineloop.o = -g -O2 +CFLAGS_deterministic.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE diff --git a/tools/perf/tests/workloads/deterministic.c b/tools/perf/tests/workloads/deterministic.c new file mode 100644 index 000000000000..8a78519fd075 --- /dev/null +++ b/tools/perf/tests/workloads/deterministic.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "../tests.h" + +int dt_work = 1234; + +static void function1(void) +{ + dt_work += 7; + dt_work += 7; + dt_work += 7; +} + +static void function2(void) +{ + dt_work += 7; + dt_work += 7; + dt_work += 7; +} + +static int deterministic(int argc __maybe_unused, + const char **argv __maybe_unused) +{ + dt_work += 7; + dt_work += 7; + dt_work += 7; + + function1(); + + dt_work += 7; + dt_work += 7; + dt_work += 7; + + function2(); + + return 0; +} + +DEFINE_WORKLOAD(deterministic); From ccf62a267a660cbc2c5ca0208a94fee4ef256926 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:11 +0100 Subject: [PATCH 3319/5214] perf test cs-etm: Replace unroll loop thread with deterministic decode test Testing a long sequence without branches seems like it would be better as a decoder unit test, and this test doesn't test decoding either, so it's not clear what bugs this is trying to catch. The new deterministic workload has somewhat long sequences when built unoptimized, and we can always increase them later if we want to. But now we test that decoding always gives the same result for the same sequence of code which we've never had before. Signed-off-by: James Clark Tested-by: Leo Yan Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../tests/shell/coresight/deterministic.sh | 72 +++++++++++++++++++ .../shell/coresight/unroll_loop_thread_10.sh | 22 ------ 2 files changed, 72 insertions(+), 22 deletions(-) create mode 100755 tools/perf/tests/shell/coresight/deterministic.sh delete mode 100755 tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh diff --git a/tools/perf/tests/shell/coresight/deterministic.sh b/tools/perf/tests/shell/coresight/deterministic.sh new file mode 100755 index 000000000000..75d4973056f0 --- /dev/null +++ b/tools/perf/tests/shell/coresight/deterministic.sh @@ -0,0 +1,72 @@ +#!/bin/bash -e +# CoreSight deterministic workload decode (exclusive) + +# SPDX-License-Identifier: GPL-2.0 + +# If CoreSight is not available, skip the test +perf list pmu | grep -q cs_etm || exit 2 + +tmpdir=$(mktemp -d /tmp/__perf_test.coresight_deterministic.XXXXX) + +cleanup() { + rm -rf "${tmpdir}" + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +cf="$tmpdir/ctl" +af="$tmpdir/ack" +mkfifo "$cf" "$af" + +# Start disabled and use the control FIFO to only record the workload and not +# startup. +perf record -o "$tmpdir/data" -e cs_etm//u -D -1 --control fifo:"$cf","$af" -- \ + perf test --record-ctl fifo:"$cf","$af" -w deterministic > /dev/null 2>&1 + +perf script -i "$tmpdir/data" --itrace=i1i -F ip,srcline | \ + grep "deterministic.c" | uniq > "$tmpdir/script" 2>/dev/null + + +# Remove brace lines and call sites as they may not be hit or may have +# extra hits after returning, depending on the compiler. +sed -i \ + -e '/deterministic.c:8$/d' \ + -e '/deterministic.c:12$/d' \ + -e '/deterministic.c:15$/d' \ + -e '/deterministic.c:19$/d' \ + -e '/deterministic.c:23$/d' \ + -e '/deterministic.c:28$/d' \ + -e '/deterministic.c:34$/d' \ + -e '/deterministic.c:36$/d' \ + -e '/deterministic.c:37$/d' \ + "$tmpdir/script" + +cat > "$tmpdir/expected" << EOF + deterministic.c:24 + deterministic.c:25 + deterministic.c:26 + deterministic.c:9 + deterministic.c:10 + deterministic.c:11 + deterministic.c:30 + deterministic.c:31 + deterministic.c:32 + deterministic.c:16 + deterministic.c:17 + deterministic.c:18 +EOF + +if ! diff -q "$tmpdir/script" "$tmpdir/expected"; then + echo "FAIL: line numbers don't match expected: " + head -n 100 "$tmpdir/script" + cleanup + exit 1 +fi + +cleanup +exit 0 diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh deleted file mode 100755 index cb3e97a0a89f..000000000000 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -e -# CoreSight / Unroll Loop Thread 10 (exclusive) - -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -TEST="unroll_loop_thread" - -# shellcheck source=../lib/coresight.sh -. "$(dirname $0)"/../lib/coresight.sh - -ARGS="10" -DATV="10" -# shellcheck disable=SC2153 -DATA="$DATD/perf-$TEST-$DATV.data" - -perf record $PERFRECOPT -o "$DATA" "$BIN" $ARGS - -perf_dump_aux_verify "$DATA" 10 10 10 - -err=$? -exit $err From f6708947e686281400e0c026f0d2c58d83a8c0e0 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:12 +0100 Subject: [PATCH 3320/5214] perf test cs-etm: Remove asm_pure_loop test It's not obvious what this test is for so remove it. It's not a stress test because it doesn't output lots of data and it's not a functional test because it only looks for raw trace output. It seems to imply that a program written in assembly influences whether trace would be generated by the CPU or not, but the CPU doesn't know what language the program is written in. We already have lots of Coresight tests that test the full pipeline including decoding, and in many more modes of operation than this one, so if no trace was collected they will already fail leaving this one redundant. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../tests/shell/coresight/asm_pure_loop.sh | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100755 tools/perf/tests/shell/coresight/asm_pure_loop.sh diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop.sh b/tools/perf/tests/shell/coresight/asm_pure_loop.sh deleted file mode 100755 index 0301904b9637..000000000000 --- a/tools/perf/tests/shell/coresight/asm_pure_loop.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -e -# CoreSight / ASM Pure Loop (exclusive) - -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -TEST="asm_pure_loop" - -# shellcheck source=../lib/coresight.sh -. "$(dirname $0)"/../lib/coresight.sh - -ARGS="" -DATV="out" -# shellcheck disable=SC2153 -DATA="$DATD/perf-$TEST-$DATV.data" - -perf record $PERFRECOPT -o "$DATA" "$BIN" $ARGS - -perf_dump_aux_verify "$DATA" 10 10 10 - -err=$? -exit $err From 6948b32c3a74706a1da451191b1d792615f3f91d Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:13 +0100 Subject: [PATCH 3321/5214] perf test cs-etm: Replace memcpy test with raw dump stress test Like asm_pure_loop, this memcpy test only checks that 10 of each of a few trace packet types occur after recording a lot of trace, which isn't more specific than other existing Coresight tests. Assume it was supposed to be a stress test for dumping and replace it with one that doesn't require a custom binary and checks for a specific amount of raw output. Don't bother checking for packets because the other tests that test decoding will catch issues with malformed data. This also adds coverage for exit snapshot mode which was missing. Signed-off-by: James Clark Tested-by: Leo Yan Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/coresight/memcpy_thread_16k_10.sh | 22 ------- .../tests/shell/coresight/raw_dump_stress.sh | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+), 22 deletions(-) delete mode 100755 tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh create mode 100755 tools/perf/tests/shell/coresight/raw_dump_stress.sh diff --git a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh deleted file mode 100755 index 1f765d69acc3..000000000000 --- a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -e -# CoreSight / Memcpy 16k 10 Threads (exclusive) - -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -TEST="memcpy_thread" - -# shellcheck source=../lib/coresight.sh -. "$(dirname $0)"/../lib/coresight.sh - -ARGS="16 10 1" -DATV="16k_10" -# shellcheck disable=SC2153 -DATA="$DATD/perf-$TEST-$DATV.data" - -perf record $PERFRECOPT -o "$DATA" "$BIN" $ARGS - -perf_dump_aux_verify "$DATA" 10 10 10 - -err=$? -exit $err diff --git a/tools/perf/tests/shell/coresight/raw_dump_stress.sh b/tools/perf/tests/shell/coresight/raw_dump_stress.sh new file mode 100755 index 000000000000..bea70d825596 --- /dev/null +++ b/tools/perf/tests/shell/coresight/raw_dump_stress.sh @@ -0,0 +1,65 @@ +#!/bin/bash -e +# CoreSight raw dump stress (exclusive) + +# SPDX-License-Identifier: GPL-2.0 + +if [ "$(id -u)" != 0 ]; then + # Requires root for larger buffer size + echo "[Skip] No root permission" + exit 2 +fi + +# If CoreSight is not available, skip the test +perf list pmu | grep -q cs_etm || exit 2 + +tmpdir=$(mktemp -d /tmp/__perf_test.coresight_raw_dump_stress.XXXXX) + +cleanup() { + rm -r "${tmpdir}" + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# Use exit snapshot to record 2M of trace to make about 80MB of raw dump data. +echo "Recording..." +perf record -e cs_etm/timestamp=0/u -m,2M -Se -o "$tmpdir/data" -- \ + perf test -w brstack 20000 > /dev/null 2>&1 + +# Test raw dump runs to completion but don't decode because that's too slow for +# a test +echo "Dumping raw trace..." +perf report --dump-raw-trace -i "$tmpdir/data" 2>/dev/null > "$tmpdir/rawdump" + +# Get the size and offset of the first AUXTRACE buffer and the index of the last +# packet in the raw dump. +read -r size offset last_idx <<< "$(awk ' + found && /PERF_RECORD_/ { exit } + /PERF_RECORD_AUXTRACE / { found = 1; size = $7; offset = $9; next } + found && /Idx:/ { last_idx = $1; gsub(/Idx:|;/, "", last_idx) } + END { if (last_idx) print size, offset, last_idx } +' "$tmpdir/rawdump")" + +# The last Idx minus start offset should equal the size of the buffer if +# everything was dumped. Allow 48 bytes difference to cover 3 frames: current +# frame length, a partial frame and a final empty one, all of which aren't +# dumped. +# +# TODO: for a single snapshot, offset should always be zero. However, we +# currently output AUX records in snapshot mode when we shouldn't, which +# increments the offset. Allow for that until it's fixed so we can test raw +# dumping. +decode_size=$((1 + last_idx - offset)) +if [ "$decode_size" -gt "$((size - 48))" ] && [ "$decode_size" -le "$((size))" ]; then + echo "PASS: AUXTRACE buffer length matches dumped packet index" + cleanup + exit 0 +fi + +echo "FAIL: AUXTRACE buffer length mismatch: size=$size offset=$offset last_idx=$last_idx decode_size=$decode_size" +cleanup +exit 1 From 2540d48595b9289f1fe6288e080c329a0fd7ce47 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:14 +0100 Subject: [PATCH 3322/5214] perf test: Add named_threads workload Add a workload that runs X threads that run a unique function named "named_threads_thread[x]" which performs a multiplication in a loop for Y loops. Each thread sets its name to "thread[x]". This can be used to test that processor trace decoding handles concurrent threads correctly and the correct symbols and thread names are assigned to samples. Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Leo Yan Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-test.txt | 5 +- tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 1 + tools/perf/tests/workloads/named_threads.c | 109 +++++++++++++++++++++ 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tools/perf/tests/workloads/named_threads.c diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index c50a4b2d2d29..81c8525f5946 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt @@ -57,7 +57,7 @@ OPTIONS --workload=:: Run a built-in workload, to list them use '--list-workloads', current ones include: noploop, thloop, leafloop, sqrtloop, brstack, datasym, - context_switch_loop, deterministic and landlock. + context_switch_loop, deterministic, named_threads and landlock. Used with the shell script regression tests. @@ -66,6 +66,9 @@ OPTIONS seconds: leafloop, noploop, sqrtloop, thloop nrloops: brstack, context_switch_loop + 'named_threads' accepts the number of threads and the number of loops to + do in each thread. + The datasym, landlock and deterministic workloads don't accept any. --list-workloads:: diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index ef7e3f52a383..afc06cec4954 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -155,6 +155,7 @@ static struct test_suite *generic_tests[] = { static struct test_workload *workloads[] = { &workload__noploop, &workload__thloop, + &workload__named_threads, &workload__leafloop, &workload__sqrtloop, &workload__brstack, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index bcfe9c33fc66..7cedf05be544 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -237,6 +237,7 @@ struct test_workload workload__##work = { \ /* The list of test workloads */ DECLARE_WORKLOAD(noploop); DECLARE_WORKLOAD(thloop); +DECLARE_WORKLOAD(named_threads); DECLARE_WORKLOAD(leafloop); DECLARE_WORKLOAD(sqrtloop); DECLARE_WORKLOAD(brstack); diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 90f2d8aa4941..75b377934a0e 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -2,6 +2,7 @@ perf-test-y += noploop.o perf-test-y += thloop.o +perf-test-y += named_threads.o perf-test-y += leafloop.o perf-test-y += sqrtloop.o perf-test-y += brstack.o diff --git a/tools/perf/tests/workloads/named_threads.c b/tools/perf/tests/workloads/named_threads.c new file mode 100644 index 000000000000..d051d41a3cfe --- /dev/null +++ b/tools/perf/tests/workloads/named_threads.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include "../tests.h" + +#define MAX_THREADS 25 + +static int iterations = 500; +int named_threads_work = 1234; + +typedef void *(*thread_fn_t)(void *); + +#define DEFINE_THREAD(n) \ +noinline void *named_threads_thread##n(void *arg __maybe_unused) \ +{ \ + pthread_setname_np(pthread_self(), "thread" #n); \ + for (int i = 0; i < iterations; i++) \ + named_threads_work += 3; \ + \ + return NULL; \ +} + +#define THREAD_LIST(macro) \ + macro(1) \ + macro(2) \ + macro(3) \ + macro(4) \ + macro(5) \ + macro(6) \ + macro(7) \ + macro(8) \ + macro(9) \ + macro(10) \ + macro(11) \ + macro(12) \ + macro(13) \ + macro(14) \ + macro(15) \ + macro(16) \ + macro(17) \ + macro(18) \ + macro(19) \ + macro(20) \ + macro(21) \ + macro(22) \ + macro(23) \ + macro(24) \ + macro(25) + +#define DECLARE_THREAD(n) void *named_threads_thread##n(void *arg); + +THREAD_LIST(DECLARE_THREAD) +THREAD_LIST(DEFINE_THREAD) + +#define THREAD_ENTRY(n) named_threads_thread##n, + +static thread_fn_t thread_fns[MAX_THREADS] = { + THREAD_LIST(THREAD_ENTRY) +}; + +/* + * Creates argv[0] threads that run a unique function named "thread[x]" which performs + * a multiplication in a loop for argv[1] loops. + */ +static int named_threads(int argc, const char **argv) +{ + pthread_t threads[MAX_THREADS]; + int nr_threads = 1; + int err = 0; + + if (argc > 0) + nr_threads = atoi(argv[0]); + + if (nr_threads <= 0 || nr_threads > MAX_THREADS) { + fprintf(stderr, "Error: num threads must be 1 - %d\n", MAX_THREADS); + return 1; + } + + if (argc > 1) + iterations = atoi(argv[1]); + + if (iterations < 0) { + fprintf(stderr, "Error: iterations must be non-negative\n"); + return 1; + } + + for (int i = 0; i < nr_threads; i++) { + int ret; + + ret = pthread_create(&threads[i], NULL, thread_fns[i], NULL); + if (ret) { + fprintf(stderr, "Error: failed to create thread%d: %s\n", + i + 1, strerror(ret)); + return 1; + } + } + + for (int i = 0; i < nr_threads; i++) + pthread_join(threads[i], NULL); + + return err; +} + +DEFINE_WORKLOAD(named_threads); From 0b6d6376d7408130b1b3840a948e1a250a02c547 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:15 +0100 Subject: [PATCH 3323/5214] perf test cs-etm: Test decoding for concurrent threads test The thread_loop test only looks for context IDs in the raw trace. There's a lot more that can go wrong when decoding these, so replace it with a test that looks at the final output for matching thread names and symbols. In the future we might use timestamps and context switch events to track threads, so looking at context IDs in the raw trace wouldn't always work. Reviewed-by: Leo Yan Signed-off-by: James Clark Tested-by: Leo Yan Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/coresight/concurrent_threads.sh | 45 +++++++++++++++++++ .../coresight/thread_loop_check_tid_10.sh | 23 ---------- .../coresight/thread_loop_check_tid_2.sh | 23 ---------- 3 files changed, 45 insertions(+), 46 deletions(-) create mode 100755 tools/perf/tests/shell/coresight/concurrent_threads.sh delete mode 100755 tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh delete mode 100755 tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh diff --git a/tools/perf/tests/shell/coresight/concurrent_threads.sh b/tools/perf/tests/shell/coresight/concurrent_threads.sh new file mode 100755 index 000000000000..3349fff8c767 --- /dev/null +++ b/tools/perf/tests/shell/coresight/concurrent_threads.sh @@ -0,0 +1,45 @@ +#!/bin/bash -e +# CoreSight concurrent threads (exclusive) + +# SPDX-License-Identifier: GPL-2.0 + +# If CoreSight is not available, skip the test +perf list pmu | grep -q cs_etm || exit 2 + +tmpdir=$(mktemp -d /tmp/__perf_test.coresight_concurrent_threads.XXXXX) + +cleanup() { + rm -rf "${tmpdir}" + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +cf="$tmpdir/ctl" +af="$tmpdir/ack" +mkfifo "$cf" "$af" + +nthreads=10 + +# Timestamps off to reduce trace size, start disabled and use the control FIFO +# to only record the workload and not startup. +perf record -o "$tmpdir/data" -e cs_etm/timestamp=0/u -D -1 --control fifo:"$cf","$af" \ + -- perf test --record-ctl fifo:"$cf","$af" -w named_threads $nthreads 1 > /dev/null 2>&1 + +perf script -i "$tmpdir/data" > "$tmpdir/script" 2>/dev/null + +# Check all threads were traced and they have the correct thread name and symbol +for i in $(seq 1 $nthreads); do + if ! grep -q "thread${i} .* named_threads_thread${i}" "$tmpdir/script"; then + echo "Error: thread${i} missing" >&2 + cleanup + exit 1 + fi +done + +cleanup +exit 0 diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh deleted file mode 100755 index 7f43a93a2ac2..000000000000 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -e -# CoreSight / Thread Loop 10 Threads - Check TID (exclusive) - -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -TEST="thread_loop" - -# shellcheck source=../lib/coresight.sh -. "$(dirname $0)"/../lib/coresight.sh - -ARGS="10 1" -DATV="check-tid-10th" -# shellcheck disable=SC2153 -DATA="$DATD/perf-$TEST-$DATV.data" -STDO="$DATD/perf-$TEST-$DATV.stdout" - -SHOW_TID=1 perf record -s $PERFRECOPT -o "$DATA" "$BIN" $ARGS > $STDO - -perf_dump_aux_tid_verify "$DATA" "$STDO" - -err=$? -exit $err diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh deleted file mode 100755 index a94d2079ed06..000000000000 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -e -# CoreSight / Thread Loop 2 Threads - Check TID (exclusive) - -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -TEST="thread_loop" - -# shellcheck source=../lib/coresight.sh -. "$(dirname $0)"/../lib/coresight.sh - -ARGS="2 20" -DATV="check-tid-2th" -# shellcheck disable=SC2153 -DATA="$DATD/perf-$TEST-$DATV.data" -STDO="$DATD/perf-$TEST-$DATV.stdout" - -SHOW_TID=1 perf record -s $PERFRECOPT -o "$DATA" "$BIN" $ARGS > $STDO - -perf_dump_aux_tid_verify "$DATA" "$STDO" - -err=$? -exit $err From f8fabdccdeb4adea3b29e2678bf8289f477f6763 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:16 +0100 Subject: [PATCH 3324/5214] perf test cs-etm: Remove duplicate branch tests We already test branch output in perf script mode, but then retest it in Perf report mode. This is more of a test of Perf itself than Coresight because Perf uses the same samples to generate both outputs. Also we're already testing instruction output in Perf report mode. Remove this test for a speedup. On the systemwide test also remove the Perf report test because systemwide mode records a lot more data so running multiple tests on it has a big runtime impact. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight.sh | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index bbf89e944e7b..39553702c1f3 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -52,17 +52,6 @@ perf_script_branch_samples() { grep -E " +$1 +[0-9]+ .* +branches:(.*:)? +" > /dev/null 2>&1 } -perf_report_branch_samples() { - echo "Looking at perf.data file for reporting branch samples:" - - # Below is an example of the branch samples reporting: - # 73.04% 73.04% touch libc-2.27.so [.] _dl_addr - # 7.71% 7.71% touch libc-2.27.so [.] getenv - # 2.59% 2.59% touch ld-2.27.so [.] strcmp - perf report --stdio -i ${perfdata} 2>&1 | \ - grep -E " +[0-9]+\.[0-9]+% +[0-9]+\.[0-9]+% +$1 " > /dev/null 2>&1 -} - perf_report_instruction_samples() { echo "Looking at perf.data file for instruction samples:" @@ -123,7 +112,6 @@ arm_cs_iterate_devices() { record_touch_file $device_name $2 && perf_script_branch_samples touch && - perf_report_branch_samples touch && perf_report_instruction_samples touch err=$? @@ -154,9 +142,7 @@ arm_cs_etm_system_wide_test() { # System-wide mode should include perf samples so test for that # instead of ls - perf_script_branch_samples perf && - perf_report_branch_samples perf && - perf_report_instruction_samples perf + perf_script_branch_samples perf err=$? arm_cs_report "CoreSight system wide testing" $err @@ -179,7 +165,6 @@ arm_cs_etm_snapshot_test() { wait $PERFPID perf_script_branch_samples dd && - perf_report_branch_samples dd && perf_report_instruction_samples dd err=$? @@ -191,7 +176,6 @@ arm_cs_etm_basic_test() { perf record -o ${perfdata} "$@" -m,8M -- ls > /dev/null 2>&1 perf_script_branch_samples ls && - perf_report_branch_samples ls && perf_report_instruction_samples ls err=$? From ac390ed34317144589cc8b6b374dbed137c7f076 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:17 +0100 Subject: [PATCH 3325/5214] perf test cs-etm: Skip if not root Use the common idiom for skipping tests if not running as root, which is required for these tests. Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Leo Yan Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight.sh | 6 ++++++ tools/perf/tests/shell/test_arm_coresight_disasm.sh | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index 39553702c1f3..8ed2c934c87d 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -20,6 +20,12 @@ skip_if_no_cs_etm_event() { skip_if_no_cs_etm_event || exit 2 +if [ "$(id -u)" != 0 ]; then + # Requires root for -C and system wide tests + echo "[Skip] No root permission" + exit 2 +fi + perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) file=$(mktemp /tmp/temporary_file.XXXXX) diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh index 0dfb4fadf531..339ae4831868 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -42,7 +42,7 @@ sep="\s\|\s" branch_search="\sbl${sep}b${sep}b.ne${sep}b.eq${sep}cbz\s" ## Test kernel ## -if [ -e /proc/kcore ]; then +if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then echo "Testing kernel disassembly" perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ @@ -50,8 +50,8 @@ if [ -e /proc/kcore ]; then grep -q -e ${branch_search} ${file} echo "Found kernel branches" else - # kcore is required for correct kernel decode due to runtime code patching - echo "No kcore, skipping kernel test" + # Root and kcore are required for correct kernel decode due to runtime code patching + echo "No root or kcore, skipping kernel test" fi ## Test user ## From c13fe5f07518526416284d63acc1c50af88b03e3 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:18 +0100 Subject: [PATCH 3326/5214] perf test cs-etm: Reduce snapshot size The default buffer size for root is 4MB which is very slow to decode. We only need a few KB to verify that the dd process is hit so reduce the size to 128KB. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index 8ed2c934c87d..da2f599393e2 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -156,7 +156,7 @@ arm_cs_etm_system_wide_test() { arm_cs_etm_snapshot_test() { echo "Recording trace with snapshot mode" - perf record -o ${perfdata} -e cs_etm// -S \ + perf record -o ${perfdata} -e cs_etm// -S -m,128K \ -- dd if=/dev/zero of=/dev/null > /dev/null 2>&1 & PERFPID=$! From 04d8a36625efe5ad20d7c2c04cdae960af70d118 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:19 +0100 Subject: [PATCH 3327/5214] perf test cs-etm: Speed up basic test Like the name says, this should be the most basic test possible. Kernel recording is slow and already has coverage on the systemwide test. Perf report output also has coverage elsewhere. 'ls' also produces more trace than 'true'. We only want to test if the combination of recording options works at all, so fix all of these things to make it as fast as possible. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/test_arm_coresight.sh index da2f599393e2..83295a8fe179 100755 --- a/tools/perf/tests/shell/test_arm_coresight.sh +++ b/tools/perf/tests/shell/test_arm_coresight.sh @@ -179,10 +179,9 @@ arm_cs_etm_snapshot_test() { arm_cs_etm_basic_test() { echo "Recording trace with '$*'" - perf record -o ${perfdata} "$@" -m,8M -- ls > /dev/null 2>&1 + perf record -o ${perfdata} "$@" -- true > /dev/null 2>&1 - perf_script_branch_samples ls && - perf_report_instruction_samples ls + perf_script_branch_samples true err=$? arm_cs_report "CoreSight basic testing with '$*'" $err @@ -246,12 +245,12 @@ arm_cs_etm_snapshot_test # Test all combinations of per-thread, system-wide and normal mode with # and without timestamps -arm_cs_etm_basic_test -e cs_etm/timestamp=0/ --per-thread -arm_cs_etm_basic_test -e cs_etm/timestamp=1/ --per-thread -arm_cs_etm_basic_test -e cs_etm/timestamp=0/ -a -arm_cs_etm_basic_test -e cs_etm/timestamp=1/ -a -arm_cs_etm_basic_test -e cs_etm/timestamp=0/ -arm_cs_etm_basic_test -e cs_etm/timestamp=1/ +arm_cs_etm_basic_test -e cs_etm/timestamp=0/u --per-thread +arm_cs_etm_basic_test -e cs_etm/timestamp=1/u --per-thread +arm_cs_etm_basic_test -e cs_etm/timestamp=0/u -a +arm_cs_etm_basic_test -e cs_etm/timestamp=1/u -a +arm_cs_etm_basic_test -e cs_etm/timestamp=0/u +arm_cs_etm_basic_test -e cs_etm/timestamp=1/u arm_cs_etm_sparse_cpus_test From 2f36f22f2b440c20cb63ff2917fc9a36e7462143 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:20 +0100 Subject: [PATCH 3328/5214] perf test cs-etm: Remove unused Coresight workloads These are now unused and had various issues like not working with out of source builds and being slow to compile. Delete them. Signed-off-by: James Clark Tested-by: Leo Yan Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../trace/coresight/coresight-perf.rst | 76 +--------- MAINTAINERS | 1 - tools/perf/Makefile.perf | 14 +- tools/perf/tests/shell/coresight/Makefile | 29 ---- .../tests/shell/coresight/Makefile.miniconfig | 14 -- .../shell/coresight/asm_pure_loop/.gitignore | 1 - .../shell/coresight/asm_pure_loop/Makefile | 34 ----- .../coresight/asm_pure_loop/asm_pure_loop.S | 30 ---- .../shell/coresight/memcpy_thread/.gitignore | 1 - .../shell/coresight/memcpy_thread/Makefile | 33 ----- .../coresight/memcpy_thread/memcpy_thread.c | 80 ----------- .../shell/coresight/thread_loop/.gitignore | 1 - .../shell/coresight/thread_loop/Makefile | 33 ----- .../shell/coresight/thread_loop/thread_loop.c | 85 ----------- .../coresight/unroll_loop_thread/.gitignore | 1 - .../coresight/unroll_loop_thread/Makefile | 33 ----- .../unroll_loop_thread/unroll_loop_thread.c | 75 ---------- tools/perf/tests/shell/lib/coresight.sh | 134 ------------------ 18 files changed, 4 insertions(+), 671 deletions(-) delete mode 100644 tools/perf/tests/shell/coresight/Makefile delete mode 100644 tools/perf/tests/shell/coresight/Makefile.miniconfig delete mode 100644 tools/perf/tests/shell/coresight/asm_pure_loop/.gitignore delete mode 100644 tools/perf/tests/shell/coresight/asm_pure_loop/Makefile delete mode 100644 tools/perf/tests/shell/coresight/asm_pure_loop/asm_pure_loop.S delete mode 100644 tools/perf/tests/shell/coresight/memcpy_thread/.gitignore delete mode 100644 tools/perf/tests/shell/coresight/memcpy_thread/Makefile delete mode 100644 tools/perf/tests/shell/coresight/memcpy_thread/memcpy_thread.c delete mode 100644 tools/perf/tests/shell/coresight/thread_loop/.gitignore delete mode 100644 tools/perf/tests/shell/coresight/thread_loop/Makefile delete mode 100644 tools/perf/tests/shell/coresight/thread_loop/thread_loop.c delete mode 100644 tools/perf/tests/shell/coresight/unroll_loop_thread/.gitignore delete mode 100644 tools/perf/tests/shell/coresight/unroll_loop_thread/Makefile delete mode 100644 tools/perf/tests/shell/coresight/unroll_loop_thread/unroll_loop_thread.c delete mode 100644 tools/perf/tests/shell/lib/coresight.sh diff --git a/Documentation/trace/coresight/coresight-perf.rst b/Documentation/trace/coresight/coresight-perf.rst index 30be89320621..0a77741a431e 100644 --- a/Documentation/trace/coresight/coresight-perf.rst +++ b/Documentation/trace/coresight/coresight-perf.rst @@ -112,78 +112,6 @@ Example for triggering AUX pause and resume with PMU event:: Perf test - Verify kernel and userspace perf CoreSight work ----------------------------------------------------------- -When you run perf test, it will do a lot of self tests. Some of those -tests will cover CoreSight (only if enabled and on ARM64). You -generally would run perf test from the tools/perf directory in the -kernel tree. Some tests will check some internal perf support like: +There are a set of Perf tests for CoreSight which can be run with:: - Check Arm CoreSight trace data recording and synthesized samples - Check Arm SPE trace data recording and synthesized samples - -Some others will actually use perf record and some test binaries that -are in tests/shell/coresight and will collect traces to ensure a -minimum level of functionality is met. The scripts that launch these -tests are in the same directory. These will all look like: - - CoreSight / ASM Pure Loop - CoreSight / Memcpy 16k 10 Threads - CoreSight / Thread Loop 10 Threads - Check TID - etc. - -These perf record tests will not run if the tool binaries do not exist -in tests/shell/coresight/\*/ and will be skipped. If you do not have -CoreSight support in hardware then either do not build perf with -CoreSight support or remove these binaries in order to not have these -tests fail and have them skip instead. - -These tests will log historical results in the current working -directory (e.g. tools/perf) and will be named stats-\*.csv like: - - stats-asm_pure_loop-out.csv - stats-memcpy_thread-16k_10.csv - ... - -These statistic files log some aspects of the AUX data sections in -the perf data output counting some numbers of certain encodings (a -good way to know that it's working in a very simple way). One problem -with CoreSight is that given a large enough amount of data needing to -be logged, some of it can be lost due to the processor not waking up -in time to read out all the data from buffers etc.. You will notice -that the amount of data collected can vary a lot per run of perf test. -If you wish to see how this changes over time, simply run perf test -multiple times and all these csv files will have more and more data -appended to it that you can later examine, graph and otherwise use to -figure out if things have become worse or better. - -This means sometimes these tests fail as they don't capture all the -data needed. This is about tracking quality and amount of data -produced over time and to see when changes to the Linux kernel improve -quality of traces. - -Be aware that some of these tests take quite a while to run, specifically -in processing the perf data file and dumping contents to then examine what -is inside. - -You can change where these csv logs are stored by setting the -PERF_TEST_CORESIGHT_STATDIR environment variable before running perf -test like:: - - export PERF_TEST_CORESIGHT_STATDIR=/var/tmp - perf test - -They will also store resulting perf output data in the current -directory for later inspection like:: - - perf-asm_pure_loop-out.data - perf-memcpy_thread-16k_10.data - ... - -You can alter where the perf data files are stored by setting the -PERF_TEST_CORESIGHT_DATADIR environment variable such as:: - - PERF_TEST_CORESIGHT_DATADIR=/var/tmp - perf test - -You may wish to set these above environment variables if you wish to -keep the output of tests outside of the current working directory for -longer term storage and examination. + sudo perf test coresight diff --git a/MAINTAINERS b/MAINTAINERS index b539be153f6a..7efb893edcbb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2751,7 +2751,6 @@ F: tools/perf/arch/arm/util/cs-etm.h F: tools/perf/arch/arm/util/pmu.c F: tools/perf/tests/shell/*coresight* F: tools/perf/tests/shell/coresight/* -F: tools/perf/tests/shell/lib/*coresight* F: tools/perf/util/cs-etm-decoder/* F: tools/perf/util/cs-etm.* diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index bdedf6620758..476b8dcaef58 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -512,16 +512,7 @@ arm64-sysreg-defs-clean: $(Q)$(MAKE) -C $(arm64_gen_sysreg_dir) O=$(arm64_gen_sysreg_outdir) \ prefix= subdir= clean > /dev/null -TESTS_CORESIGHT_DIR := $(srctree)/tools/perf/tests/shell/coresight - -tests-coresight-targets: FORCE - $(Q)$(MAKE) -C $(TESTS_CORESIGHT_DIR) - -tests-coresight-targets-clean: - $(call QUIET_CLEAN, coresight) - $(Q)$(MAKE) -C $(TESTS_CORESIGHT_DIR) O=$(OUTPUT) clean >/dev/null - -all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) tests-coresight-targets +all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) # Create python binding output directory if not already present $(shell [ -d '$(OUTPUT)python' ] || mkdir -p '$(OUTPUT)python') @@ -900,7 +891,6 @@ install-tests: all install-gtk $(INSTALL) tests/shell/base_report/*.txt '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/base_report'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/coresight' ; \ $(INSTALL) tests/shell/coresight/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/coresight' - $(Q)$(MAKE) -C tests/shell/coresight install-tests install-bin: install-tools install-tests @@ -943,7 +933,7 @@ endif clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(LIBPERF)-clean \ arm64-sysreg-defs-clean fixdep-clean python-clean bpf-skel-clean \ - tests-coresight-targets-clean pmu-events-clean + pmu-events-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive \ $(OUTPUT)perf-iostat $(LANG_BINDINGS) $(Q)find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '*.a' -delete -o \ diff --git a/tools/perf/tests/shell/coresight/Makefile b/tools/perf/tests/shell/coresight/Makefile deleted file mode 100644 index fa08fd9a5991..000000000000 --- a/tools/perf/tests/shell/coresight/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Carsten Haitzler , 2021 -include ../../../../../tools/scripts/Makefile.include -include ../../../../../tools/scripts/Makefile.arch -include ../../../../../tools/scripts/utilities.mak - -SUBDIRS = \ - asm_pure_loop \ - memcpy_thread \ - thread_loop \ - unroll_loop_thread - -all: $(SUBDIRS) -$(SUBDIRS): - @$(MAKE) -C $@ >/dev/null - -INSTALLDIRS = $(SUBDIRS:%=install-%) - -install-tests: $(INSTALLDIRS) -$(INSTALLDIRS): - @$(MAKE) -C $(@:install-%=%) install-tests >/dev/null - -CLEANDIRS = $(SUBDIRS:%=clean-%) - -clean: $(CLEANDIRS) -$(CLEANDIRS): - $(call QUIET_CLEAN, test-$(@:clean-%=%)) $(MAKE) -C $(@:clean-%=%) clean >/dev/null - -.PHONY: all clean $(SUBDIRS) $(CLEANDIRS) $(INSTALLDIRS) diff --git a/tools/perf/tests/shell/coresight/Makefile.miniconfig b/tools/perf/tests/shell/coresight/Makefile.miniconfig deleted file mode 100644 index 5f72a9cb43f3..000000000000 --- a/tools/perf/tests/shell/coresight/Makefile.miniconfig +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Carsten Haitzler , 2021 - -ifndef DESTDIR -prefix ?= $(HOME) -endif - -DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) -INSTALL = install -INSTDIR_SUB = tests/shell/coresight - -include ../../../../../scripts/Makefile.include -include ../../../../../scripts/Makefile.arch -include ../../../../../scripts/utilities.mak diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop/.gitignore b/tools/perf/tests/shell/coresight/asm_pure_loop/.gitignore deleted file mode 100644 index 468673ac32e8..000000000000 --- a/tools/perf/tests/shell/coresight/asm_pure_loop/.gitignore +++ /dev/null @@ -1 +0,0 @@ -asm_pure_loop diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop/Makefile b/tools/perf/tests/shell/coresight/asm_pure_loop/Makefile deleted file mode 100644 index 206849e92bc9..000000000000 --- a/tools/perf/tests/shell/coresight/asm_pure_loop/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -include ../Makefile.miniconfig - -# Binary to produce -BIN=asm_pure_loop -# Any linking/libraries needed for the binary - empty if none needed -LIB= - -all: $(BIN) - -$(BIN): $(BIN).S -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Build line - this is raw asm with no libc to have an always exact binary - $(Q)$(CC) $(BIN).S -nostdlib -static -o $(BIN) $(LIB) -endif -endif - -install-tests: all -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Install the test tool in the right place - $(call QUIET_INSTALL, tests) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)'; \ - $(INSTALL) $(BIN) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)/$(BIN)' -endif -endif - -clean: - $(Q)$(RM) -f $(BIN) - -.PHONY: all clean install-tests diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop/asm_pure_loop.S b/tools/perf/tests/shell/coresight/asm_pure_loop/asm_pure_loop.S deleted file mode 100644 index 577760046772..000000000000 --- a/tools/perf/tests/shell/coresight/asm_pure_loop/asm_pure_loop.S +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Tamas Zsoldos , 2021 */ - -.globl _start -_start: - mov x0, 0x0000ffff - mov x1, xzr -loop: - nop - nop - cbnz x1, noskip - nop - nop - adrp x2, skip - add x2, x2, :lo12:skip - br x2 - nop - nop -noskip: - nop - nop -skip: - sub x0, x0, 1 - cbnz x0, loop - - mov x0, #0 - mov x8, #93 // __NR_exit syscall - svc #0 - -.section .note.GNU-stack, "", @progbits diff --git a/tools/perf/tests/shell/coresight/memcpy_thread/.gitignore b/tools/perf/tests/shell/coresight/memcpy_thread/.gitignore deleted file mode 100644 index f8217e56091e..000000000000 --- a/tools/perf/tests/shell/coresight/memcpy_thread/.gitignore +++ /dev/null @@ -1 +0,0 @@ -memcpy_thread diff --git a/tools/perf/tests/shell/coresight/memcpy_thread/Makefile b/tools/perf/tests/shell/coresight/memcpy_thread/Makefile deleted file mode 100644 index 2db637eb2c26..000000000000 --- a/tools/perf/tests/shell/coresight/memcpy_thread/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 -include ../Makefile.miniconfig - -# Binary to produce -BIN=memcpy_thread -# Any linking/libraries needed for the binary - empty if none needed -LIB=-pthread - -all: $(BIN) - -$(BIN): $(BIN).c -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Build line - $(Q)$(CC) $(BIN).c -o $(BIN) $(LIB) -endif -endif - -install-tests: all -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Install the test tool in the right place - $(call QUIET_INSTALL, tests) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)'; \ - $(INSTALL) $(BIN) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)/$(BIN)' -endif -endif - -clean: - $(Q)$(RM) -f $(BIN) - -.PHONY: all clean install-tests diff --git a/tools/perf/tests/shell/coresight/memcpy_thread/memcpy_thread.c b/tools/perf/tests/shell/coresight/memcpy_thread/memcpy_thread.c deleted file mode 100644 index 7e879217be30..000000000000 --- a/tools/perf/tests/shell/coresight/memcpy_thread/memcpy_thread.c +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Carsten Haitzler , 2021 -#include -#include -#include -#include -#include - -struct args { - unsigned long loops; - unsigned long size; - pthread_t th; - void *ret; -}; - -static void *thrfn(void *arg) -{ - struct args *a = arg; - unsigned long i, len = a->loops; - unsigned char *src, *dst; - - src = malloc(a->size * 1024); - dst = malloc(a->size * 1024); - if ((!src) || (!dst)) { - printf("ERR: Can't allocate memory\n"); - exit(1); - } - for (i = 0; i < len; i++) - memcpy(dst, src, a->size * 1024); - - return NULL; -} - -static pthread_t new_thr(void *(*fn) (void *arg), void *arg) -{ - pthread_t t; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_create(&t, &attr, fn, arg); - return t; -} - -int main(int argc, char **argv) -{ - unsigned long i, len, size, thr; - struct args args[256]; - long long v; - - if (argc < 4) { - printf("ERR: %s [copysize Kb] [numthreads] [numloops (hundreds)]\n", argv[0]); - exit(1); - } - - v = atoll(argv[1]); - if ((v < 1) || (v > (1024 * 1024))) { - printf("ERR: max memory 1GB (1048576 KB)\n"); - exit(1); - } - size = v; - thr = atol(argv[2]); - if ((thr < 1) || (thr > 256)) { - printf("ERR: threads 1-256\n"); - exit(1); - } - v = atoll(argv[3]); - if ((v < 1) || (v > 40000000000ll)) { - printf("ERR: loops 1-40000000000 (hundreds)\n"); - exit(1); - } - len = v * 100; - for (i = 0; i < thr; i++) { - args[i].loops = len; - args[i].size = size; - args[i].th = new_thr(thrfn, &(args[i])); - } - for (i = 0; i < thr; i++) - pthread_join(args[i].th, &(args[i].ret)); - return 0; -} diff --git a/tools/perf/tests/shell/coresight/thread_loop/.gitignore b/tools/perf/tests/shell/coresight/thread_loop/.gitignore deleted file mode 100644 index 6d4c33eaa9e8..000000000000 --- a/tools/perf/tests/shell/coresight/thread_loop/.gitignore +++ /dev/null @@ -1 +0,0 @@ -thread_loop diff --git a/tools/perf/tests/shell/coresight/thread_loop/Makefile b/tools/perf/tests/shell/coresight/thread_loop/Makefile deleted file mode 100644 index ea846c038e7a..000000000000 --- a/tools/perf/tests/shell/coresight/thread_loop/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 -include ../Makefile.miniconfig - -# Binary to produce -BIN=thread_loop -# Any linking/libraries needed for the binary - empty if none needed -LIB=-pthread - -all: $(BIN) - -$(BIN): $(BIN).c -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Build line - $(Q)$(CC) $(BIN).c -o $(BIN) $(LIB) -endif -endif - -install-tests: all -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Install the test tool in the right place - $(call QUIET_INSTALL, tests) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)'; \ - $(INSTALL) $(BIN) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)/$(BIN)' -endif -endif - -clean: - $(Q)$(RM) -f $(BIN) - -.PHONY: all clean install-tests diff --git a/tools/perf/tests/shell/coresight/thread_loop/thread_loop.c b/tools/perf/tests/shell/coresight/thread_loop/thread_loop.c deleted file mode 100644 index 86f3f548b006..000000000000 --- a/tools/perf/tests/shell/coresight/thread_loop/thread_loop.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Carsten Haitzler , 2021 - -// define this for gettid() -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#ifndef SYS_gettid -// gettid is 178 on arm64 -# define SYS_gettid 178 -#endif -#define gettid() syscall(SYS_gettid) - -struct args { - unsigned int loops; - pthread_t th; - void *ret; -}; - -static void *thrfn(void *arg) -{ - struct args *a = arg; - int i = 0, len = a->loops; - - if (getenv("SHOW_TID")) { - unsigned long long tid = gettid(); - - printf("%llu\n", tid); - } - asm volatile( - "loop:\n" - "add %w[i], %w[i], #1\n" - "cmp %w[i], %w[len]\n" - "blt loop\n" - : /* out */ - : /* in */ [i] "r" (i), [len] "r" (len) - : /* clobber */ - ); - return (void *)(long)i; -} - -static pthread_t new_thr(void *(*fn) (void *arg), void *arg) -{ - pthread_t t; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_create(&t, &attr, fn, arg); - return t; -} - -int main(int argc, char **argv) -{ - unsigned int i, len, thr; - struct args args[256]; - - if (argc < 3) { - printf("ERR: %s [numthreads] [numloops (millions)]\n", argv[0]); - exit(1); - } - - thr = atoi(argv[1]); - if ((thr < 1) || (thr > 256)) { - printf("ERR: threads 1-256\n"); - exit(1); - } - len = atoi(argv[2]); - if ((len < 1) || (len > 4000)) { - printf("ERR: max loops 4000 (millions)\n"); - exit(1); - } - len *= 1000000; - for (i = 0; i < thr; i++) { - args[i].loops = len; - args[i].th = new_thr(thrfn, &(args[i])); - } - for (i = 0; i < thr; i++) - pthread_join(args[i].th, &(args[i].ret)); - return 0; -} diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread/.gitignore b/tools/perf/tests/shell/coresight/unroll_loop_thread/.gitignore deleted file mode 100644 index 2cb4e996dbf3..000000000000 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread/.gitignore +++ /dev/null @@ -1 +0,0 @@ -unroll_loop_thread diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread/Makefile b/tools/perf/tests/shell/coresight/unroll_loop_thread/Makefile deleted file mode 100644 index 6264c4e3abd1..000000000000 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 -include ../Makefile.miniconfig - -# Binary to produce -BIN=unroll_loop_thread -# Any linking/libraries needed for the binary - empty if none needed -LIB=-pthread - -all: $(BIN) - -$(BIN): $(BIN).c -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Build line - $(Q)$(CC) $(BIN).c -o $(BIN) $(LIB) -endif -endif - -install-tests: all -ifdef CORESIGHT -ifeq ($(ARCH),arm64) -# Install the test tool in the right place - $(call QUIET_INSTALL, tests) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)'; \ - $(INSTALL) $(BIN) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$(INSTDIR_SUB)/$(BIN)/$(BIN)' -endif -endif - -clean: - $(Q)$(RM) -f $(BIN) - -.PHONY: all clean install-tests diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread/unroll_loop_thread.c b/tools/perf/tests/shell/coresight/unroll_loop_thread/unroll_loop_thread.c deleted file mode 100644 index 8f4e1c985ca3..000000000000 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread/unroll_loop_thread.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Carsten Haitzler , 2021 -#include -#include -#include -#include -#include - -struct args { - pthread_t th; - unsigned int in; - void *ret; -}; - -static void *thrfn(void *arg) -{ - struct args *a = arg; - unsigned int i, in = a->in; - - for (i = 0; i < 10000; i++) { - asm volatile ( -// force an unroll of thia add instruction so we can test long runs of code -#define SNIP1 "add %w[in], %w[in], #1\n" -// 10 -#define SNIP2 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 SNIP1 -// 100 -#define SNIP3 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 SNIP2 -// 1000 -#define SNIP4 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 SNIP3 -// 10000 -#define SNIP5 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 SNIP4 -// 100000 - SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 SNIP5 - : /* out */ - : /* in */ [in] "r" (in) - : /* clobber */ - ); - } - - return NULL; -} - -static pthread_t new_thr(void *(*fn) (void *arg), void *arg) -{ - pthread_t t; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_create(&t, &attr, fn, arg); - return t; -} - -int main(int argc, char **argv) -{ - unsigned int i, thr; - struct args args[256]; - - if (argc < 2) { - printf("ERR: %s [numthreads]\n", argv[0]); - exit(1); - } - - thr = atoi(argv[1]); - if ((thr > 256) || (thr < 1)) { - printf("ERR: threads 1-256\n"); - exit(1); - } - for (i = 0; i < thr; i++) { - args[i].in = rand(); - args[i].th = new_thr(thrfn, &(args[i])); - } - for (i = 0; i < thr; i++) - pthread_join(args[i].th, &(args[i].ret)); - return 0; -} diff --git a/tools/perf/tests/shell/lib/coresight.sh b/tools/perf/tests/shell/lib/coresight.sh deleted file mode 100644 index 184d62e7e5bd..000000000000 --- a/tools/perf/tests/shell/lib/coresight.sh +++ /dev/null @@ -1,134 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Carsten Haitzler , 2021 - -# This is sourced from a driver script so no need for #!/bin... etc. at the -# top - the assumption below is that it runs as part of sourcing after the -# test sets up some basic env vars to say what it is. - -# This currently works with ETMv4 / ETF not any other packet types at thi -# point. This will need changes if that changes. - -# perf record options for the perf tests to use -PERFRECMEM="-m ,16M" -PERFRECOPT="$PERFRECMEM -e cs_etm//u" - -TOOLS=$(dirname $0) -DIR="$TOOLS/$TEST" -BIN="$DIR/$TEST" -# If the test tool/binary does not exist and is executable then skip the test -if ! test -x "$BIN"; then exit 2; fi -# If CoreSight is not available, skip the test -perf list pmu | grep -q cs_etm || exit 2 -DATD="." -# If the data dir env is set then make the data dir use that instead of ./ -if test -n "$PERF_TEST_CORESIGHT_DATADIR"; then - DATD="$PERF_TEST_CORESIGHT_DATADIR"; -fi -# If the stat dir env is set then make the data dir use that instead of ./ -STATD="." -if test -n "$PERF_TEST_CORESIGHT_STATDIR"; then - STATD="$PERF_TEST_CORESIGHT_STATDIR"; -fi - -# Called if the test fails - error code 1 -err() { - echo "$1" - exit 1 -} - -# Check that some statistics from our perf -check_val_min() { - STATF="$4" - if test "$2" -lt "$3"; then - echo ", FAILED" >> "$STATF" - err "Sanity check number of $1 is too low ($2 < $3)" - fi -} - -perf_dump_aux_verify() { - # Some basic checking that the AUX chunk contains some sensible data - # to see that we are recording something and at least a minimum - # amount of it. We should almost always see Fn packets in just about - # anything but certainly we will see some trace info and async - # packets - DUMP="$DATD/perf-tmp-aux-dump.txt" - perf report --stdio --dump -i "$1" | \ - grep -o -e I_ATOM_F -e I_ASYNC -e I_TRACE_INFO > "$DUMP" - # Simply count how many of these packets we find to see that we are - # producing a reasonable amount of data - exact checks are not sane - # as this is a lossy process where we may lose some blocks and the - # compiler may produce different code depending on the compiler and - # optimization options, so this is rough just to see if we're - # either missing almost all the data or all of it - ATOM_FX_NUM=$(grep -c I_ATOM_F "$DUMP") - ASYNC_NUM=$(grep -c I_ASYNC "$DUMP") - TRACE_INFO_NUM=$(grep -c I_TRACE_INFO "$DUMP") - rm -f "$DUMP" - - # Arguments provide minimums for a pass - CHECK_FX_MIN="$2" - CHECK_ASYNC_MIN="$3" - CHECK_TRACE_INFO_MIN="$4" - - # Write out statistics, so over time you can track results to see if - # there is a pattern - for example we have less "noisy" results that - # produce more consistent amounts of data each run, to see if over - # time any techinques to minimize data loss are having an effect or - # not - STATF="$STATD/stats-$TEST-$DATV.csv" - if ! test -f "$STATF"; then - echo "ATOM Fx Count, Minimum, ASYNC Count, Minimum, TRACE INFO Count, Minimum" > "$STATF" - fi - echo -n "$ATOM_FX_NUM, $CHECK_FX_MIN, $ASYNC_NUM, $CHECK_ASYNC_MIN, $TRACE_INFO_NUM, $CHECK_TRACE_INFO_MIN" >> "$STATF" - - # Actually check to see if we passed or failed. - check_val_min "ATOM_FX" "$ATOM_FX_NUM" "$CHECK_FX_MIN" "$STATF" - check_val_min "ASYNC" "$ASYNC_NUM" "$CHECK_ASYNC_MIN" "$STATF" - check_val_min "TRACE_INFO" "$TRACE_INFO_NUM" "$CHECK_TRACE_INFO_MIN" "$STATF" - echo ", Ok" >> "$STATF" -} - -perf_dump_aux_tid_verify() { - # Specifically crafted test will produce a list of Tread ID's to - # stdout that need to be checked to see that they have had trace - # info collected in AUX blocks in the perf data. This will go - # through all the TID's that are listed as CID=0xabcdef and see - # that all the Thread IDs the test tool reports are in the perf - # data AUX chunks - - # The TID test tools will print a TID per stdout line that are being - # tested - TIDS=$(cat "$2") - # Scan the perf report to find the TIDs that are actually CID in hex - # and build a list of the ones found - FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \ - grep -o "CID=0x[0-9a-z]\+" | sed 's/CID=//g' | \ - uniq | sort | uniq) - # No CID=xxx found - maybe your kernel is reporting these as - # VMID=xxx so look there - if test -z "$FOUND_TIDS"; then - FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \ - grep -o "VMID=0x[0-9a-z]\+" | sed 's/VMID=//g' | \ - uniq | sort | uniq) - fi - - # Iterate over the list of TIDs that the test says it has and find - # them in the TIDs found in the perf report - MISSING="" - for TID2 in $TIDS; do - FOUND="" - for TIDHEX in $FOUND_TIDS; do - TID=$(printf "%i" $TIDHEX) - if test "$TID" -eq "$TID2"; then - FOUND="y" - break - fi - done - if test -z "$FOUND"; then - MISSING="$MISSING $TID" - fi - done - if test -n "$MISSING"; then - err "Thread IDs $MISSING not found in perf AUX data" - fi -} From 3c84ec94a68ad73663b9c1ac7543b59453f64678 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:21 +0100 Subject: [PATCH 3329/5214] perf test cs-etm: Make disassembly test use kcore Hits in modules return empty disassembly with vmlinux as an input to objdump. Make the disassembly test more reliable by always using kcore. And update the comments to say that this is supported by the script. Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Leo Yan Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- .../scripts/python/arm-cs-trace-disasm.py | 20 +++++++++---------- .../tests/shell/test_arm_coresight_disasm.sh | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/perf/scripts/python/arm-cs-trace-disasm.py b/tools/perf/scripts/python/arm-cs-trace-disasm.py index ba208c90d631..8f6fa4a007b4 100755 --- a/tools/perf/scripts/python/arm-cs-trace-disasm.py +++ b/tools/perf/scripts/python/arm-cs-trace-disasm.py @@ -18,29 +18,29 @@ from perf_trace_context import perf_sample_srccode, perf_config_get # Below are some example commands for using this script. # Note a --kcore recording is required for accurate decode -# due to the alternatives patching mechanism. However this -# script only supports reading vmlinux for disassembly dump, -# meaning that any patched instructions will appear -# as unpatched, but the instruction ranges themselves will -# be correct. In addition to this, source line info comes -# from Perf, and when using kcore there is no debug info. The -# following lists the supported features in each mode: +# due to the alternatives patching mechanism. In addition to this, +# source line info comes from Perf, and when using kcore there is +# no debug info. The following lists the supported features in each mode: # # +-----------+-----------------+------------------+------------------+ # | Recording | Accurate decode | Source line dump | Disassembly dump | # +-----------+-----------------+------------------+------------------+ # | --kcore | yes | no | yes | -# | normal | no | yes | yes | +# | normal | no | yes (inaccurate) | yes (inaccurate) | # +-----------+-----------------+------------------+------------------+ # # Output disassembly with objdump and auto detect vmlinux -# (when running on same machine.) +# (when running on same machine.): # perf script -s scripts/python/arm-cs-trace-disasm.py -d # # Output disassembly with llvm-objdump: # perf script -s scripts/python/arm-cs-trace-disasm.py \ # -- -d llvm-objdump-11 -k path/to/vmlinux # +# Output accurate disassembly by passing kcore to script: +# perf script -s scripts/python/arm-cs-trace-disasm.py \ +# -- -d -k perf.data/kcore_dir/kcore +# # Output only source line and symbols: # perf script -s scripts/python/arm-cs-trace-disasm.py @@ -57,7 +57,7 @@ def int_arg(v): args = argparse.ArgumentParser() args.add_argument("-k", "--vmlinux", - help="Set path to vmlinux file. Omit to autodetect if running on same machine") + help="Set path to vmlinux or kcore file. Omit to autodetect if running on same machine") args.add_argument("-d", "--objdump", nargs="?", const=default_objdump(), help="Show disassembly. Can also be used to change the objdump path"), args.add_argument("-v", "--verbose", action="store_true", help="Enable debugging log") diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh index 339ae4831868..87797d239f76 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -46,7 +46,7 @@ if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then echo "Testing kernel disassembly" perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ - -d --stop-sample=30 2> /dev/null > ${file} + -d --stop-sample=30 -k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file} grep -q -e ${branch_search} ${file} echo "Found kernel branches" else From 0f900110c7b49cae863c19960991033e55c0ad2e Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:22 +0100 Subject: [PATCH 3330/5214] perf test cs-etm: Add all branch instructions to test If we reduce the number of samples searched to speed up the test, then there will be less chance of hitting one of these branches. Extend the regex to cover all branches so the test will always pass. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight_disasm.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh index 87797d239f76..f78dfb6bf73e 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -38,8 +38,7 @@ cleanup_files() trap cleanup_files EXIT TERM INT # Ranges start and end on branches, so check for some likely branch instructions -sep="\s\|\s" -branch_search="\sbl${sep}b${sep}b.ne${sep}b.eq${sep}cbz\s" +branch_search='[[:space:]](bl|b(\.(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al))?|br|blr|ret|cbz|cbnz|tbz|tbnz|svc|eret)([[:space:]]|$)' ## Test kernel ## if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then @@ -47,7 +46,7 @@ if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ -d --stop-sample=30 -k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file} - grep -q -e ${branch_search} ${file} + grep -q -E ${branch_search} ${file} echo "Found kernel branches" else # Root and kcore are required for correct kernel decode due to runtime code patching @@ -59,7 +58,7 @@ echo "Testing userspace disassembly" perf record -o ${perfdata} -e cs_etm//u -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ -d --stop-sample=30 2> /dev/null > ${file} -grep -q -e ${branch_search} ${file} +grep -q -E ${branch_search} ${file} echo "Found userspace branches" glb_err=0 From 0e16b544165822cd1452d446308fa3ec9311d05e Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:23 +0100 Subject: [PATCH 3331/5214] perf test cs-etm: Speed up disassembly test We can use exit snapshot to limit the amount of trace to decode here too. Also each call to objdump is quite expensive on kcore so limit it to 2 samples instead of 30. We only want to see if there is no data at all. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_arm_coresight_disasm.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/test_arm_coresight_disasm.sh index f78dfb6bf73e..f2fb1aa92252 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/test_arm_coresight_disasm.sh @@ -43,9 +43,9 @@ branch_search='[[:space:]](bl|b(\.(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al) ## Test kernel ## if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then echo "Testing kernel disassembly" - perf record -o ${perfdata} -e cs_etm//k --kcore -- touch $file > /dev/null 2>&1 + perf record -o ${perfdata} -e cs_etm//k --kcore -Se -m,64K -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ - -d --stop-sample=30 -k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file} + -d --stop-sample=2 -k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file} grep -q -E ${branch_search} ${file} echo "Found kernel branches" else @@ -55,9 +55,9 @@ fi ## Test user ## echo "Testing userspace disassembly" -perf record -o ${perfdata} -e cs_etm//u -- touch $file > /dev/null 2>&1 +perf record -o ${perfdata} -e cs_etm//u -Se -m,64K -- touch $file > /dev/null 2>&1 perf script -i ${perfdata} -s python:${script_path} -- \ - -d --stop-sample=30 2> /dev/null > ${file} + -d --stop-sample=2 2> /dev/null > ${file} grep -q -E ${branch_search} ${file} echo "Found userspace branches" From 46510981d6c5e6cfa963e924b9e65b2f56222431 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:24 +0100 Subject: [PATCH 3332/5214] perf test cs-etm: Move existing tests to coresight folder There is a subfolder for Coresight tests so might as well keep them all in here. Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Amir Ayupov Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: Arnaldo Carvalho de Melo --- MAINTAINERS | 1 - tools/perf/tests/shell/{ => coresight}/test_arm_coresight.sh | 0 .../tests/shell/{ => coresight}/test_arm_coresight_disasm.sh | 2 +- 3 files changed, 1 insertion(+), 2 deletions(-) rename tools/perf/tests/shell/{ => coresight}/test_arm_coresight.sh (100%) rename tools/perf/tests/shell/{ => coresight}/test_arm_coresight_disasm.sh (96%) diff --git a/MAINTAINERS b/MAINTAINERS index 7efb893edcbb..ff8935b459ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2749,7 +2749,6 @@ F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h F: tools/perf/arch/arm/util/pmu.c -F: tools/perf/tests/shell/*coresight* F: tools/perf/tests/shell/coresight/* F: tools/perf/util/cs-etm-decoder/* F: tools/perf/util/cs-etm.* diff --git a/tools/perf/tests/shell/test_arm_coresight.sh b/tools/perf/tests/shell/coresight/test_arm_coresight.sh similarity index 100% rename from tools/perf/tests/shell/test_arm_coresight.sh rename to tools/perf/tests/shell/coresight/test_arm_coresight.sh diff --git a/tools/perf/tests/shell/test_arm_coresight_disasm.sh b/tools/perf/tests/shell/coresight/test_arm_coresight_disasm.sh similarity index 96% rename from tools/perf/tests/shell/test_arm_coresight_disasm.sh rename to tools/perf/tests/shell/coresight/test_arm_coresight_disasm.sh index f2fb1aa92252..ccb90dda2475 100755 --- a/tools/perf/tests/shell/test_arm_coresight_disasm.sh +++ b/tools/perf/tests/shell/coresight/test_arm_coresight_disasm.sh @@ -24,7 +24,7 @@ perfdata_dir=$(mktemp -d /tmp/__perf_test.perf.data.XXXXX) perfdata=${perfdata_dir}/perf.data file=$(mktemp /tmp/temporary_file.XXXXX) # Relative path works whether it's installed or running from repo -script_path=$(dirname "$0")/../../scripts/python/arm-cs-trace-disasm.py +script_path=$(dirname "$0")/../../../scripts/python/arm-cs-trace-disasm.py cleanup_files() { From 2f4e244b5968b1f7392d7de31dc961ef80f2e42e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 4 Jun 2026 23:49:32 -0700 Subject: [PATCH 3333/5214] perf jitdump: Fix a build error with ASAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I found this error when I pass EXTRA_CFLAGS=-fsanitize=address on Fedora 44 with GCC 16. Fix it by copying one less byte. CC util/jitdump.o util/jitdump.c: In function ‘jit_process’: util/jitdump.c:237:9: error: ‘__builtin_strncpy’ specified bound 4096 equals destination size [-Werror=stringop-truncation] 237 | strncpy(jd->dir, name, PATH_MAX); | ^ cc1: all warnings being treated as errors make[4]: *** [tools/build/Makefile.build:95: util/jitdump.o] Error 1 make[4]: *** Waiting for unfinished jobs.... make[3]: *** [tools/build/Makefile.build:158: util] Error 2 make[2]: *** [Makefile.perf:578: perf-util-in.o] Error 2 make[1]: *** [Makefile.perf:288: sub-make] Error 2 make: *** [Makefile:76: all] Error 2 Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/jitdump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 18fd84a82153..83005b30b9bf 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -234,7 +234,7 @@ jit_open(struct jit_buf_desc *jd, const char *name) /* * keep dirname for generating files and mmap records */ - strncpy(jd->dir, name, PATH_MAX); + strncpy(jd->dir, name, PATH_MAX - 1); jd->dir[PATH_MAX - 1] = '\0'; dirname(jd->dir); free(buf); From 836455e6dbd34eb3d12eeab5e2d2b9a7f1512459 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:01:43 -0300 Subject: [PATCH 3334/5214] perf pmu: Fix pmu_id() heap underwrite on empty identifier file pmu_id() calls filename__read_str() then strips the trailing newline via str[len - 1] = 0. If the PMU identifier file is empty, filename__read_str() succeeds with len = 0. len - 1 underflows size_t to SIZE_MAX, writing a null byte before the heap allocation. Add a len == 0 check before the newline stripping. Fixes: 51d548471510843e ("perf pmu: Add pmu_id()") Reported-by: sashiko-bot Cc: John Garry Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 1539960ba23b..f588cce60194 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -865,6 +865,12 @@ static char *pmu_id(const char *name) if (filename__read_str(path, &str, &len) < 0) return NULL; + /* empty identifier file — nothing useful */ + if (len == 0) { + free(str); + return NULL; + } + str[len - 1] = 0; /* remove line feed */ return str; From 33035f7dd4e49f3f117e70c5e36c8c1ae88d37f2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:13 -0300 Subject: [PATCH 3335/5214] perf pmu: Fix perf_pmu__parse_scale/unit() OOB access on empty sysfs file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf_pmu__parse_scale() reads a PMU scale file then accesses scale[sret - 1] to strip a trailing newline. Only sret < 0 is guarded, so an empty file (sret == 0) causes scale[-1] — a stack buffer underflow that reads and potentially writes out of bounds. perf_pmu__parse_unit() has the same pattern: alias->unit[sret - 1] with sret == 0 accesses the byte before the struct member, which may corrupt the adjacent pmu_name pointer field. Change both guards from sret < 0 to sret <= 0 so that empty files are treated as read errors. Fixes: 410136f5dd96b601 ("tools/perf/stat: Add event unit and scale support") Reported-by: sashiko-bot Cc: Stephane Eranian Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index f588cce60194..a550f030b85d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -314,7 +314,7 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al goto error; sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) + if (sret <= 0) goto error; if (scale[sret - 1] == '\n') @@ -346,7 +346,7 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali return -1; sret = read(fd, alias->unit, UNIT_MAX_LEN); - if (sret < 0) + if (sret <= 0) goto error; close(fd); From 52b1f9678499b13b7aeb0186d9c6f486c043283f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:55 -0300 Subject: [PATCH 3336/5214] tools lib api: Fix missing null termination in filename__read_int/ull() filename__read_int() passes a stack buffer to read() using the full sizeof(line) and then hands it to atoi() without null-terminating. If a sysfs file fills the 64-byte buffer exactly, atoi() reads past the array into uninitialized stack memory. filename__read_ull_base() has the same issue with strtoull(). Fix both by reading sizeof(line) - 1 bytes and explicitly null-terminating after a successful read. Fixes: 3a351127cbc682c3 ("tools lib fs: Adopt filename__read_int from tools/perf/") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/fs/fs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index edec23406dbc..3cc302d4c47b 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -294,11 +294,14 @@ int filename__read_int(const char *filename, int *value) { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = atoi(line); err = 0; } @@ -312,11 +315,14 @@ static int filename__read_ull_base(const char *filename, { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = strtoull(line, NULL, base); if (*value != ULLONG_MAX) err = 0; From 6eaa8ee3e2abe5112e80e94c27196bb175689469 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:04:50 -0300 Subject: [PATCH 3337/5214] perf symbols: Fix signed overflow in sysfs__read_build_id() size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sysfs__read_build_id() reads ELF note headers from sysfs files. The note's namesz and descsz fields are used to compute the skip size: int n = namesz + descsz; if (n > (int)sizeof(bf)) Both namesz and descsz are size_t from NOTE_ALIGN() of 32-bit note header fields. Their sum can exceed INT_MAX, overflowing the signed int n to a negative value. The check n > sizeof(bf) then evaluates false (negative < positive in signed comparison), and read(fd, bf, n) reinterprets the negative n as a huge size_t count — the kernel writes up to MAX_RW_COUNT bytes into the 8192-byte stack buffer. In practice the overflow is bounded by the sysfs file's actual size, so a real sysfs notes file won't trigger it organically. But crafted input (e.g. via a mounted debugfs/sysfs image) could. Fix by validating namesz and descsz individually against the buffer size before summing, and change n to size_t to avoid the signed overflow entirely. Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c2bdfd0003df..8fb25a5692b5 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -964,14 +964,17 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else if (read(fd, bf, descsz) != (ssize_t)descsz) break; } else { - int n = namesz + descsz; + size_t n; - if (n > (int)sizeof(bf)) { + /* int sum of namesz+descsz can overflow negative, bypassing size check */ + if (namesz > sizeof(bf) || descsz > sizeof(bf) - namesz) { n = sizeof(bf); pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", __func__, filename, nhdr.n_namesz, nhdr.n_descsz); + } else { + n = namesz + descsz; } - if (read(fd, bf, n) != n) + if (read(fd, bf, n) != (ssize_t)n) break; } } From 9c74f0aab398cb32ab250401f323c0fdc9a3a496 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:05:15 -0300 Subject: [PATCH 3338/5214] perf symbols: Bounds-check .gnu_debuglink section data filename__read_debuglink() copies .gnu_debuglink section data into a caller-provided buffer via: strncpy(debuglink, data->d_buf, size); where size is PATH_MAX. If the ELF section is smaller than size and lacks a null terminator, strncpy reads past data->d_buf into adjacent memory. A malformed ELF file can trigger this, potentially causing a segfault or leaking heap data. Additionally, strncpy does not guarantee null termination when the source fills the buffer. Replace with an explicit memcpy bounded by both the output buffer size and the actual section data size (data->d_size), followed by explicit null termination. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 8fb25a5692b5..51e7cfe0f593 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1027,7 +1027,14 @@ int filename__read_debuglink(const char *filename, char *debuglink, goto out_elf_end; /* the start of this section is a zero-terminated string */ - strncpy(debuglink, data->d_buf, size); + if (data->d_size > 0) { + size_t len = min(size - 1, data->d_size); + + memcpy(debuglink, data->d_buf, len); + debuglink[len] = '\0'; + } else { + debuglink[0] = '\0'; + } err = 0; From b8acc68f1382a3f380a0e7320a95d328ac5e9027 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:06:12 -0300 Subject: [PATCH 3339/5214] perf tools: Use mkostemp() for O_CLOEXEC on temporary files mkstemp() creates file descriptors without the close-on-exec flag. These fds leak to child processes spawned during symbol resolution (addr2line, objdump), wasting descriptors and potentially exposing temporary file contents. Replace mkstemp() with mkostemp(tmpbuf, O_CLOEXEC) at all three call sites: - filename__decompress() in dso.c - read_gnu_debugdata() in symbol-elf.c - kcore__init() in symbol-elf.c Fixes: 42b3fa670825983f ("perf tools: Introduce dso__decompress_kmodule_{fd,path}") Fixes: b10f74308e130527 ("perf symbol: Support .gnu_debugdata for symbols") Fixes: afba19d9dc8eba66 ("perf symbols: Workaround objdump difficulties with kcore") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Namhyung Kim Cc: Stephen Brennan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 2 +- tools/perf/util/symbol-elf.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index fb2e78fe2aa8..ee06a252a54d 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -346,7 +346,7 @@ int filename__decompress(const char *name, char *pathname, if (!compressions[comp].is_compressed(name)) return open(name, O_RDONLY | O_CLOEXEC); - fd = mkstemp(tmpbuf); + fd = mkostemp(tmpbuf, O_CLOEXEC); if (fd < 0) { *err = errno; return -1; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 51e7cfe0f593..36a0304707e1 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1119,9 +1119,9 @@ static Elf *read_gnu_debugdata(struct dso *dso, Elf *elf, const char *name, int return NULL; } - temp_fd = mkstemp(temp_filename); + temp_fd = mkostemp(temp_filename, O_CLOEXEC); if (temp_fd < 0) { - pr_debug("%s: mkstemp: %m\n", __func__); + pr_debug("%s: mkostemp: %m\n", __func__); *dso__load_errno(dso) = -errno; fclose(wrapped); return NULL; @@ -1993,7 +1993,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, kcore->elfclass = elfclass; if (temp) - kcore->fd = mkstemp(filename); + kcore->fd = mkostemp(filename, O_CLOEXEC); else kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) From b6bb3b005dcdd960b8e0b7f9d6869132b3de08d5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:06:54 -0300 Subject: [PATCH 3340/5214] perf intel-pt: Fix snprintf size tracking bug in insn decoder dump_insn() tracks remaining buffer space with a 'left' variable, but the loop subtracts the cumulative offset 'n' each iteration instead of just the per-iteration delta: n += snprintf(x->out + n, left, "%02x ", inbuf[i]); left -= n; /* BUG: n is cumulative, not the delta */ After two iterations left goes massively negative, wrapping to a huge value when passed as size_t to snprintf(), disabling all bounds checking for the rest of the loop. Switch to scnprintf() accumulation using sizeof(x->out) - n as the remaining space, which is always correct and eliminates the separate 'left' variable entirely. Fixes: 48d02a1d5c137d36 ("perf script: Add 'brstackinsn' for branch stacks") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Andi Kleen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- .../util/intel-pt-decoder/intel-pt-insn-decoder.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 72c7a4e15d61..f90fcbc4302d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -220,7 +220,6 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, { struct insn insn; int n, i, ret; - int left; ret = insn_decode(&insn, inbuf, inlen, x->is64bit ? INSN_MODE_64 : INSN_MODE_32); @@ -229,13 +228,9 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, return ""; if (lenp) *lenp = insn.length; - left = sizeof(x->out); - n = snprintf(x->out, left, "insn: "); - left -= n; - for (i = 0; i < insn.length; i++) { - n += snprintf(x->out + n, left, "%02x ", inbuf[i]); - left -= n; - } + n = scnprintf(x->out, sizeof(x->out), "insn: "); + for (i = 0; i < insn.length; i++) + n += scnprintf(x->out + n, sizeof(x->out) - n, "%02x ", inbuf[i]); return x->out; } From 31d596054550f793508abe7dd593853ece47d428 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:37:55 -0300 Subject: [PATCH 3341/5214] perf tools: Fix thread__set_comm_from_proc() on empty comm file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thread__set_comm_from_proc() calls procfs__read_str() then strips the trailing newline via comm[sz - 1] = '\0'. procfs__read_str() allocates the buffer before reading, so on an empty /proc/pid/comm (reachable during late exit teardown) it returns success with sz = 0 and an unterminated heap buffer. The sz - 1 underflow was the original sashiko finding: it writes a null byte before the allocation. But even with a sz > 0 guard on the newline strip, the unterminated buffer would still be passed to thread__set_comm() which calls strlen() — an unbounded heap read. Fix by treating sz == 0 as failure: free the buffer and return -1. This is consistent with pmu.c's perf_pmu__parse_scale/unit which already treat len == 0 from filename__read_str as an error. Fixes: 2f3027ac28bf6bc3 ("perf thread: Introduce method to set comm from /proc/pid/self") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/thread.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index ba33c0dfc18f..e483ffcb5d93 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -295,6 +295,11 @@ int thread__set_comm_from_proc(struct thread *thread) if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && procfs__read_str(path, &comm, &sz) == 0) { + /* sz==0: read got nothing, e.g. race during exit teardown */ + if (sz == 0) { + free(comm); + return -1; + } comm[sz - 1] = '\0'; err = thread__set_comm(thread, comm, 0); } From 1847c5fae344a0fb9cc0f95be40b378359c6fc3b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:26 -0300 Subject: [PATCH 3342/5214] perf hwmon: Fix off-by-one null termination on sysfs reads Three functions read sysfs files into fixed-size stack buffers using the full buffer size, then null-terminate at buf[read_len]. If the read fills the buffer exactly, read_len equals sizeof(buf) and the null byte writes one past the array, corrupting an adjacent stack variable. Fix all three by reading sizeof(buf) - 1 bytes, reserving space for the null terminator: - hwmon_pmu__read_events(): buf[128] - hwmon_pmu__describe_items(): buf[64] - evsel__hwmon_pmu_read(): buf[32] Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index dbf6a71af47f..d895cd74f2a0 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -289,7 +289,7 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) if (fd < 0) continue; - read_len = read(fd, buf, sizeof(buf)); + read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -432,7 +432,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); if (fd > 0) { - ssize_t read_len = read(fd, buf, sizeof(buf)); + ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -816,7 +816,7 @@ int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) count = perf_counts(evsel->counts, cpu_map_idx, thread); fd = FD(evsel, cpu_map_idx, thread); - len = pread(fd, buf, sizeof(buf), 0); + len = pread(fd, buf, sizeof(buf) - 1, 0); if (len <= 0) { count->lost++; return -EINVAL; From ae75956c166fe169b8c137bf375305fc97820a62 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:48 -0300 Subject: [PATCH 3343/5214] perf hwmon: Use scnprintf() in hwmon_pmu__for_each_event() hwmon_pmu__for_each_event() formats description strings via: len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", ...); len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len, ...); If value->label is long enough to cause snprintf() to truncate, it returns the would-have-been-written count, making len exceed sizeof(desc_buf). The subsequent sizeof(desc_buf) - len underflows to a huge size_t value, disabling bounds checking in hwmon_pmu__describe_items(). The alias_buf snprintf has the same issue. Switch both to scnprintf() which returns actual bytes written. Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index d895cd74f2a0..fbfb872ceb18 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -514,14 +514,14 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb int ret; size_t len; - len = snprintf(alias_buf, sizeof(alias_buf), "%s%d", - hwmon_type_strs[key.type], key.num); + scnprintf(alias_buf, sizeof(alias_buf), "%s%d", + hwmon_type_strs[key.type], key.num); if (!info.name) { info.name = info.alias; info.alias = NULL; } - len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", + len = scnprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", hwmon_desc[key.type], pmu->name + 6, value->label ?: info.name); From e1a2c9d70b312acc262f6be936dd5bbd9bbc6236 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:39:53 -0300 Subject: [PATCH 3344/5214] perf hwmon: Fix parse_hwmon_filename() strlcpy buffer overflow parse_hwmon_filename() strips the "_alarm" suffix from event names by copying into a 24-byte stack buffer: strlcpy(fn_type, fn_item, fn_item_len - 5); The third argument is the source length minus the suffix, not the destination buffer capacity. A long event name ending in "_alarm" can have fn_item_len - 5 > sizeof(fn_type), causing strlcpy() to write past the 24-byte fn_type[] array. The assert() only validates that the longest *valid* hwmon item fits, but does not protect against crafted input. Clamp the strlcpy size to min(fn_item_len - 5, sizeof(fn_type)). Fixes: 4810b761f812da3c ("perf hwmon_pmu: Add hwmon filename parser") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hwmon_pmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index fbfb872ceb18..bdcbe887579a 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -202,7 +202,8 @@ bool parse_hwmon_filename(const char *filename, fn_item_len = strlen(fn_item); if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) { assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type)); - strlcpy(fn_type, fn_item, fn_item_len - 5); + /* fn_item_len - 5 strips "_alarm"; clamp to buffer size */ + strlcpy(fn_type, fn_item, min_t(size_t, fn_item_len - 5, sizeof(fn_type))); fn_item = fn_type; *alarm = true; } From 1b4e9fbdeabc549965e70ac0cd8095d57ff6df06 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:40:20 -0300 Subject: [PATCH 3345/5214] perf symbols: Bounds-check descsz in sysfs__read_build_id() GNU fallback When sysfs__read_build_id() matches NT_GNU_BUILD_ID with the right namesz but the name content is not "GNU", it falls back to reading descsz bytes into the stack buffer bf[BUFSIZ]: } else if (read(fd, bf, descsz) != (ssize_t)descsz) Unlike the else branch which validates namesz + descsz against sizeof(bf), this path passes descsz directly to read() without any bounds check. A crafted sysfs file with a large n_descsz overflows the 8192-byte stack buffer. Add a descsz > sizeof(bf) check before the read, breaking out of the loop on oversized values. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 36a0304707e1..06cfb84f86eb 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -961,8 +961,13 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) err = 0; break; } - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - break; + } else { + /* descsz from untrusted file — clamp to buffer */ + if (descsz > sizeof(bf)) + break; + if (read(fd, bf, descsz) != (ssize_t)descsz) + break; + } } else { size_t n; From 51cdb188edeaf389e4377859b9c483c19ce5a259 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:43:09 -0300 Subject: [PATCH 3346/5214] perf tools: Fix uninitialized pathname on uncompressed fallback in filename__decompress() filename__decompress() has an early return path for files that are not actually compressed. This path returns the fd from open() directly but never writes to the pathname output parameter, leaving the caller with an uninitialized buffer despite a successful return. Callers like dso__decompress_kmodule_path() pass pathname to decompress_kmodule() which uses it to set the decompressed file path. If pathname is uninitialized, subsequent operations on the path produce undefined behavior. Fix by setting pathname to an empty string on the uncompressed path. Callers already check for an empty pathname to distinguish temporary decompressed files (which need unlink) from the original file. Reported-by: sashiko-bot Fixes: 7ac22b088afe26a4 ("perf tools: Add filename__decompress function") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/code-reading.c | 7 +++++-- tools/perf/util/disasm.c | 7 +++++-- tools/perf/util/dso.c | 12 +++++++++--- tools/perf/util/symbol-elf.c | 6 ++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 47043a3a2fb4..e82ecdc95777 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -471,8 +471,11 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, goto out; } - decomp = true; - objdump_name = decomp_name; + /* empty pathname means file wasn't actually compressed */ + if (decomp_name[0] != '\0') { + decomp = true; + objdump_name = decomp_name; + } } /* Read the object code using objdump */ diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 59ba88e1f744..0a1a7e9cf3ef 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1577,8 +1577,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) return -1; - decomp = true; - strcpy(symfs_filename, tmp); + /* empty pathname means file wasn't actually compressed */ + if (tmp[0] != '\0') { + decomp = true; + strcpy(symfs_filename, tmp); + } } /* diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index ee06a252a54d..6a34717c9f31 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -343,8 +343,11 @@ int filename__decompress(const char *name, char *pathname, * To keep this transparent, we detect this and return the file * descriptor to the uncompressed file. */ - if (!compressions[comp].is_compressed(name)) + if (!compressions[comp].is_compressed(name)) { + if (pathname && len > 0) + pathname[0] = '\0'; return open(name, O_RDONLY | O_CLOEXEC); + } fd = mkostemp(tmpbuf, O_CLOEXEC); if (fd < 0) { @@ -598,8 +601,11 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, goto out; } - *decomp = true; - strcpy(name, newpath); + /* empty pathname means file wasn't actually compressed */ + if (newpath[0] != '\0') { + *decomp = true; + strcpy(name, newpath); + } } return name; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 06cfb84f86eb..10902a5dc6db 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -920,12 +920,14 @@ int filename__read_build_id(const char *filename, struct build_id *bid) return -1; } close(fd); - filename = path; + /* non-empty path means a temp file was created */ + if (path[0] != '\0') + filename = path; } err = read_build_id(filename, bid); - if (m.comp) + if (m.comp && filename == path) unlink(filename); return err; } From 34d3d93fac6d92237cb9d730ca04c37ed361c7a6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:03:04 -0300 Subject: [PATCH 3347/5214] perf hwmon: Guard label read against empty or failed reads hwmon_pmu__read_events() reads label files with read() into a stack buffer, strips trailing newlines, then checks buf[0] == '\0'. When read() returns 0 (empty file) or -1 (error), the buffer is never written, so buf[0] reads uninitialized stack memory. If the garbage byte is non-zero, the code falls through to strdup(buf) which copies arbitrary stack data as the label string. Fix by checking read_len <= 0 before accessing buf contents, closing the fd and skipping the entry. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hwmon_pmu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index bdcbe887579a..efd3067a2e59 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -295,8 +295,11 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; - if (read_len > 0) - buf[read_len] = '\0'; + if (read_len <= 0) { + close(fd); + continue; + } + buf[read_len] = '\0'; if (buf[0] == '\0') { pr_debug("hwmon_pmu: empty label file %s %s\n", From 2ea64782a428bed74f595961e651ceb8c4c5bf22 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:04:25 -0300 Subject: [PATCH 3348/5214] perf tools: Use snprintf() in dso__read_running_kernel_build_id() dso__read_running_kernel_build_id() uses sprintf() to format a sysfs path from machine->root_dir into a PATH_MAX buffer. If root_dir is close to PATH_MAX in length, appending "/sys/kernel/notes" (18 bytes) overflows the stack buffer. Switch to snprintf() with sizeof(path) to prevent the overflow. Reported-by: sashiko-bot Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 6a34717c9f31..5d0179758738 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1779,7 +1779,7 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); + snprintf(path, sizeof(path), "%s/sys/kernel/notes", machine->root_dir); sysfs__read_build_id(path, &bid); dso__set_build_id(dso, &bid); } From 438ece06185696e14c63c6113d5e2d34ec0a9680 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:05:19 -0300 Subject: [PATCH 3349/5214] tools lib api: Fix filename__write_int() writing uninitialized stack data filename__write_int() formats an integer into a 64-byte buffer with sprintf() then passes sizeof(buf) (64) as the write length. This writes all 64 bytes including uninitialized stack data past the formatted string. Most sysfs files reject the oversized write, making the function always return -1. Fix by capturing the sprintf() return value and using it as the write length. Reported-by: sashiko-bot Fixes: 3b00ea938653d136 ("tools lib api fs: Add sysfs__write_int function") Cc: Kan Liang Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/fs/fs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index 3cc302d4c47b..d16911818d4d 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -376,12 +376,13 @@ int filename__write_int(const char *filename, int value) { int fd = open(filename, O_WRONLY), err = -1; char buf[64]; + int len; if (fd < 0) return -errno; - sprintf(buf, "%d", value); - if (write(fd, buf, sizeof(buf)) == sizeof(buf)) + len = sprintf(buf, "%d", value); + if (write(fd, buf, len) == len) err = 0; close(fd); From fd1f70776add263f8ef38a87ae593c75303f1dcd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:18 -0300 Subject: [PATCH 3350/5214] tools lib api: Fix mount_overload() snprintf truncation and toupper range mount_overload() builds an environment variable name like "PERF_SYSFS_ENVIRONMENT" from fs->name. Two bugs: 1) snprintf() uses name_len as the buffer size instead of sizeof(upper_name). For fs->name = "sysfs" (len=5), the output is truncated to "PERF" (4 chars + null), so getenv() never finds the intended variable. 2) mem_toupper() only uppercases name_len bytes, converting just the "PERF" prefix rather than the full string including the filesystem name portion. Fix by using sizeof(upper_name) for snprintf and strlen(upper_name) for mem_toupper, so the full "PERF_SYSFS_ENVIRONMENT" string is correctly formatted and uppercased. Reported-by: sashiko-bot Fixes: 73ca85ad364769ff ("tools lib api fs: Add FSTYPE__mount() method") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/fs/fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index d16911818d4d..cbd8eab0d1df 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -261,8 +261,8 @@ static const char *mount_overload(struct fs *fs) /* "PERF_" + name + "_ENVIRONMENT" + '\0' */ char upper_name[5 + name_len + 12 + 1]; - snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name); - mem_toupper(upper_name, name_len); + snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name); + mem_toupper(upper_name, strlen(upper_name)); return getenv(upper_name) ?: *fs->mounts; } From 903b0526dcf86d030c5970b4b0a67f9c227368e2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:43 -0300 Subject: [PATCH 3351/5214] perf bpf: Add NULL check for btf__type_by_id() in synthesize_bpf_prog_name() synthesize_bpf_prog_name() calls btf__type_by_id() and immediately dereferences the result via t->name_off without checking for NULL. btf__type_by_id() returns NULL when the type_id is invalid or out of range. When processing perf.data files, finfo->type_id comes from untrusted input, so an invalid ID causes a NULL pointer dereference. Fix by checking t for NULL before dereferencing. Reported-by: sashiko-bot Fixes: fc462ac75b36daaa ("perf bpf: Extract logic to create program names from perf_event__synthesize_one_bpf_prog()") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 2c09842469f1..fe01551dc3e6 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -146,7 +146,8 @@ static int synthesize_bpf_prog_name(char *buf, int size, if (btf) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); - short_name = btf__name_by_offset(btf, t->name_off); + if (t) + short_name = btf__name_by_offset(btf, t->name_off); } else if (sub_id == 0 && sub_prog_cnt == 1) { /* no subprog */ if (info->name[0]) From aece2b8966fc8de5be46ee9287d0f60d6690c300 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:11:28 -0300 Subject: [PATCH 3352/5214] perf bpf: Fix map data leak in bpf_metadata_create() on alloc failure bpf_metadata_create() calls bpf_metadata_read_map_data() which allocates map.btf and map.rodata. If the subsequent bpf_metadata_alloc() fails, the code does 'continue' which skips bpf_metadata_free_map_data(), permanently leaking both allocations. Fix by calling bpf_metadata_free_map_data() before continue. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index fe01551dc3e6..b65ad28cd950 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -395,8 +395,10 @@ static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) continue; metadata = bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); - if (!metadata) + if (!metadata) { + bpf_metadata_free_map_data(&map); continue; + } bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); From a0e4362a3e7b592f1d58949ffe3d6decad39a17c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:04 -0300 Subject: [PATCH 3353/5214] perf bpf: Fix metadata leak in perf_env__add_bpf_info() on duplicate insert When perf_env__insert_bpf_prog_info() returns false (duplicate program), the error path frees info_linear and info_node but not info_node->metadata. If bpf_metadata_create() had succeeded, the metadata allocation is permanently leaked. Fix by calling bpf_metadata_free() on info_node->metadata before freeing info_node. bpf_metadata_free() handles NULL, so this is safe even when bpf_metadata_create() returned NULL. Reported-by: sashiko-bot Fixes: fdc3441f2d317b40 ("perf record: collect BPF metadata from new programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index b65ad28cd950..c4594969d767 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -873,6 +873,7 @@ static int perf_env__add_bpf_info(struct perf_env *env, u32 id) if (!perf_env__insert_bpf_prog_info(env, info_node)) { pr_debug("%s: duplicate add bpf info request for id %u\n", __func__, btf_id); + bpf_metadata_free(info_node->metadata); free(info_linear); free(info_node); goto out; From acc56d3941fc2997a5a21ea9233a8ac3d87c4f2f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:34 -0300 Subject: [PATCH 3354/5214] perf symbols: Add bounds checks to elf_read_build_id() note iteration elf_read_build_id() iterates ELF notes using pointer arithmetic driven by n_namesz and n_descsz from the note headers. Neither the note header read nor the subsequent name/desc advances are checked against the section boundary. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add two bounds checks: verify the note header fits within the remaining section data, and verify that namesz + descsz (after alignment) fits before advancing the pointer. Fixes: fd7a346ea292074e ("perf symbols: Filename__read_build_id should look at .notes section too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 10902a5dc6db..d84e2e031d43 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -835,10 +835,24 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz, descsz, remaining; const char *name; + /* ensure the note header fits within the section */ + if (ptr + sizeof(*nhdr) > data->d_buf + data->d_size) + break; + + namesz = NOTE_ALIGN(nhdr->n_namesz); + descsz = NOTE_ALIGN(nhdr->n_descsz); + + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = data->d_buf + data->d_size - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From 52e582e316c48c53bb3082c29f7862ebc554087e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 16:09:45 -0300 Subject: [PATCH 3355/5214] perf symbols: Add bounds checks to read_build_id() note iteration in minimal build symbol-minimal.c's read_build_id() iterates ELF notes with the same pattern as symbol-elf.c's elf_read_build_id(): pointer arithmetic driven by n_namesz and n_descsz from 32-bit note header fields, without validating that the name and desc fit within the note section data. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add the same bounds check as the libelf path: validate namesz and descsz individually against remaining data before advancing the pointer, avoiding size_t overflow on 32-bit. Fixes: b691f64360ecec49 ("perf symbols: Implement poor man's ELF parser") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-minimal.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 8221dc9868f7..091071d06416 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,3 +1,4 @@ +#include "debug.h" #include "dso.h" #include "symbol.h" #include "symsrc.h" @@ -44,7 +45,7 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, ptr = note_data; while ((ptr + sizeof(*nhdr)) < (note_data + note_len)) { const char *name; - size_t namesz, descsz; + size_t namesz, descsz, remaining; nhdr = ptr; if (need_swap) { @@ -56,6 +57,14 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, namesz = NOTE_ALIGN(nhdr->n_namesz); descsz = NOTE_ALIGN(nhdr->n_descsz); + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = note_data + note_len - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From 856668312685d9a8f32d49a135a89c429d309f81 Mon Sep 17 00:00:00 2001 From: Kris Bahnsen Date: Thu, 7 May 2026 16:49:43 +0000 Subject: [PATCH 3356/5214] Input: ads7846 - don't use scratch for tx_buf when clearing register The workaround for XPT2046 clears the command register, giving the touchscreen controller a NOP. The change incorrectly re-uses the req->scratch variable which is used as rx_buf for xfer[5], so by the time xfer[6] occurs, the contents of req->scratch may not be 0. It was found that the touchscreen controller can end up in a completely unresponsive state due to it being given a command the driver does not expect. Instead, rely on the spi_transfer behavior of tx_buf being NULL to transmit all 0 bits and use the scratch variable for the rx_buf for both the 1 byte command to and 2 byte response from the controller. Also relocates the scratch member of struct ser_req to force it into a different cache line to prevent any potential issues of DMA stepping on unrelated data in other struct members due to sharing the same cache line. This change was tested on real TSC2046 and ADS7843 controllers, but not the XPT2046 the workaround was originally created for. Confirming that the original modification to clear the command register does not impact either real controller. Fixes: 781a07da9bb94 ("Input: ads7846 - add dummy command register clearing cycle") Cc: stable@vger.kernel.org Co-developed-by: Mark Featherston Signed-off-by: Mark Featherston Signed-off-by: Kris Bahnsen Link: https://patch.msgid.link/20260507164943.760009-1-kris@embeddedTS.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index d3b529333ca2..8d6416ac283e 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -328,7 +328,6 @@ struct ser_req { u8 ref_on; u8 command; u8 ref_off; - u16 scratch; struct spi_message msg; struct spi_transfer xfer[8]; /* @@ -336,6 +335,7 @@ struct ser_req { * transfer buffers to live in their own cache lines. */ __be16 sample ____cacheline_aligned; + u16 scratch; }; struct ads7845_ser_req { @@ -406,8 +406,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) spi_message_add_tail(&req->xfer[5], &req->msg); /* clear the command register */ - req->scratch = 0; - req->xfer[6].tx_buf = &req->scratch; + req->xfer[6].rx_buf = &req->scratch; req->xfer[6].len = 1; spi_message_add_tail(&req->xfer[6], &req->msg); From 6251f7d3472c0409e30f8d6a24f10d33d12e3f9a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 16:41:16 -0700 Subject: [PATCH 3357/5214] Input: synaptics-rmi4 - unregister function handlers on physical driver registration failure If rmi_register_physical_driver() fails, the current error path unregisters only the RMI bus. The function handlers registered earlier remain registered with the driver core. Add a separate error path to unregister the function handlers before unregistering the bus in this failure case. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Signed-off-by: Haoxiang Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260610064633.2837084-1-haoxiang_li2024@163.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 687cb987bc13..ade57e2a7201 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -455,11 +455,13 @@ static int __init rmi_bus_init(void) if (error) { pr_err("%s: error registering the RMI physical driver: %d\n", __func__, error); - goto err_unregister_bus; + goto err_unregister_function_handlers; } return 0; +err_unregister_function_handlers: + rmi_unregister_function_handlers(); err_unregister_bus: bus_unregister(&rmi_bus_type); return error; From 75b28d74f90c79e788e0e86caf0173fc0b2e92aa Mon Sep 17 00:00:00 2001 From: Joy Zou Date: Wed, 11 Feb 2026 17:28:24 +0800 Subject: [PATCH 3358/5214] dt-bindings: dma: fsl-edma: add dma-channel-mask property description Add documentation for the dma-channel-mask property in the fsl-edma binding. This property uses an inverted bit definition: bit value 0 indicates the channel is available, while bit value 1 indicates unavailable. That was already used widely for i.MX8, i.MX9. Correcting the definition will break backward compatibility. This reversal only impacts the eDMA dts node and driver, and doesn't impact DMA consumer. Therefore, keep the inverted definition. Also add a note at the top of the binding to highlight this inverted definition to prevent confusion. Signed-off-by: Joy Zou Reviewed-by: Frank Li Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260211-b4-imx95-v2x-v4-1-10852754b267@nxp.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/fsl,edma.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/fsl,edma.yaml b/Documentation/devicetree/bindings/dma/fsl,edma.yaml index fa4248e2f1b9..f609038e35ae 100644 --- a/Documentation/devicetree/bindings/dma/fsl,edma.yaml +++ b/Documentation/devicetree/bindings/dma/fsl,edma.yaml @@ -12,6 +12,9 @@ description: | DMAMUX0 and DMAMUX1, specific DMA request source can only be multiplexed by any channel of certain group, DMAMUX0 or DMAMUX1, but not both. + This binding has an inverted dma-channel-mask definition compared to + the common DMA binding for historical reasons. + maintainers: - Peng Fan @@ -95,6 +98,12 @@ properties: eDMA are implemented in big endian mode, otherwise in little mode. type: boolean + dma-channel-mask: + description: | + Bitmask of available DMA channels (inverted definition). + Bit semantics: 0 means channel available, 1 means channel unavailable + default: 0 + required: - "#dma-cells" - compatible From 94963138cce29f85605d76c94fa1d43a0335ead9 Mon Sep 17 00:00:00 2001 From: Adrian Ng Ho Yin Date: Mon, 25 May 2026 01:37:30 -0700 Subject: [PATCH 3359/5214] dmaengine: altera-msgdma: Use memcpy_toio for descriptor FIFO writes The descriptor FIFO requires that all words of a descriptor are written in order, with the control word written last to flush it into the DMA engine. Using memcpy() with __force to __iomem is not the correct API and does not guarantee appropriate MMIO access on all architectures. Replace the descriptor body copy with memcpy_toio(), using offsetof(struct msgdma_extended_desc, control) to exclude the control word. This matches the previous sizeof(desc->hw_desc) - sizeof(u32) length only when control is the last struct member; add a static_assert to enforce that layout so a future field after control cannot silently break FIFO ordering. Keep writing the control word separately with write barriers, so it remains the final word pushed into the FIFO. Signed-off-by: Adrian Ng Ho Yin Signed-off-by: Tze Yee Ng Link: https://patch.msgid.link/f6f3b4a2e2eb0eb1a51976de3f5d1ef5bab9bd76.1779697226.git.tze.yee.ng@altera.com Signed-off-by: Vinod Koul --- drivers/dma/altera-msgdma.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c index b46999c81df0..e23e5b441a24 100644 --- a/drivers/dma/altera-msgdma.c +++ b/drivers/dma/altera-msgdma.c @@ -496,6 +496,11 @@ static void msgdma_copy_one(struct msgdma_device *mdev, { void __iomem *hw_desc = mdev->desc; + /* Ensure control is the last field — required for correct FIFO flush ordering */ + static_assert(offsetof(struct msgdma_extended_desc, control) == + sizeof(struct msgdma_extended_desc) - sizeof(u32), + "control must be the last field in msgdma_extended_desc"); + /* * Check if the DESC FIFO it not full. If its full, we need to wait * for at least one entry to become free again @@ -504,17 +509,18 @@ static void msgdma_copy_one(struct msgdma_device *mdev, MSGDMA_CSR_STAT_DESC_BUF_FULL) mdelay(1); + /* Ensure control is the last field — required for correct FIFO flush ordering */ + static_assert(offsetof(struct msgdma_extended_desc, control) == + sizeof(struct msgdma_extended_desc) - sizeof(u32), + "control must be the last field in msgdma_extended_desc"); + /* - * The descriptor needs to get copied into the descriptor FIFO - * of the DMA controller. The descriptor will get flushed to the - * FIFO, once the last word (control word) is written. Since we - * are not 100% sure that memcpy() writes all word in the "correct" - * order (address from low to high) on all architectures, we make - * sure this control word is written last by single coding it and - * adding some write-barriers here. + * Copy the descriptor into the descriptor FIFO of the DMA controller, + * excluding the control word. The FIFO is flushed and the descriptor + * becomes valid once the control word is written last. */ - memcpy((void __force *)hw_desc, &desc->hw_desc, - sizeof(desc->hw_desc) - sizeof(u32)); + memcpy_toio(hw_desc, &desc->hw_desc, + offsetof(struct msgdma_extended_desc, control)); /* Write control word last to flush this descriptor into the FIFO */ mdev->idle = false; From dc6d681e1571c89cd38145926fb2513d70a633e1 Mon Sep 17 00:00:00 2001 From: Niravkumar L Rabara Date: Mon, 25 May 2026 00:10:21 -0700 Subject: [PATCH 3360/5214] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start axi_chan_block_xfer_start() runs after the controller is already enabled, so calling axi_dma_enable() again is unnecessary. Remove the redundant enable call to keep the transfer start path clean and avoid repeated no-op programming. Signed-off-by: Niravkumar L Rabara Signed-off-by: Tze Yee Ng Link: https://patch.msgid.link/060733464e19298f670cd269d4849f2092644923.1779688569.git.tze.yee.ng@altera.com Signed-off-by: Vinod Koul --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 4d53f077e9d2..f7a50f470461 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -437,8 +437,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan, return; } - axi_dma_enable(chan->chip); - config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC; From df0c2dc68770cf43f15df40b184df030b850ea05 Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Mon, 25 May 2026 00:10:22 -0700 Subject: [PATCH 3361/5214] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc The driver only had runtime PM callbacks. If a channel stayed allocated across system suspend/resume, the runtime usage count could remain non-zero while hardware state (DMAC_CFG, clocks) was lost, and axi_dma_runtime_resume() would not run to restore it. Add system-sleep PM ops that use pm_runtime_force_suspend() and pm_runtime_force_resume() so suspend/resume reuses the existing axi_dma_suspend() and axi_dma_resume() paths. Replace pm_runtime_get() with pm_runtime_resume_and_get() in dma_chan_alloc_chan_resources() so clocks are enabled before a client can immediately submit a transfer and touch MMIO. Signed-off-by: Tze Yee Ng Link: https://patch.msgid.link/18bf778a3a1cc2f377ef8eb0d1508d8ac6371896.1779688569.git.tze.yee.ng@altera.com Signed-off-by: Vinod Koul --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index f7a50f470461..bcefaff03b5c 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -516,11 +516,17 @@ static void dw_axi_dma_synchronize(struct dma_chan *dchan) static int dma_chan_alloc_chan_resources(struct dma_chan *dchan) { struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); + int ret; + + ret = pm_runtime_resume_and_get(chan->chip->dev); + if (ret < 0) + return ret; /* ASSERT: channel is idle */ if (axi_chan_is_hw_enable(chan)) { dev_err(chan2dev(chan), "%s is non-idle!\n", axi_chan_name(chan)); + pm_runtime_put(chan->chip->dev); return -EBUSY; } @@ -531,12 +537,11 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan) 64, 0); if (!chan->desc_pool) { dev_err(chan2dev(chan), "No memory for descriptors\n"); + pm_runtime_put(chan->chip->dev); return -ENOMEM; } dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan)); - pm_runtime_get(chan->chip->dev); - return 0; } @@ -1663,6 +1668,8 @@ static void dw_remove(struct platform_device *pdev) } static const struct dev_pm_ops dw_axi_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) }; From 26f926b44dbfc035d5ba0ccfc4387a40aa9947c1 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sun, 7 Jun 2026 16:31:19 +0000 Subject: [PATCH 3362/5214] dmaengine: qcom: hidma: use sysfs_emit() in sysfs show callbacks Replace sprintf() and strlen() patterns in sysfs show callbacks with sysfs_emit(). sysfs_emit() is the preferred helper for formatting sysfs output and simplifies the implementation. Signed-off-by: Hungyu Lin Reviewed-by: Frank Li Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260607163119.78717-1-dennylin0707@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/qcom/hidma.c | 6 ++---- drivers/dma/qcom/hidma_mgmt_sys.c | 23 ++++++++++------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 5a8dca8db5ce..7a7f302a9699 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -624,12 +624,10 @@ static ssize_t hidma_show_values(struct device *dev, { struct hidma_dev *mdev = dev_get_drvdata(dev); - buf[0] = 0; - if (strcmp(attr->attr.name, "chid") == 0) - sprintf(buf, "%d\n", mdev->chidx); + return sysfs_emit(buf, "%d\n", mdev->chidx); - return strlen(buf); + return 0; } static inline void hidma_sysfs_uninit(struct hidma_dev *dev) diff --git a/drivers/dma/qcom/hidma_mgmt_sys.c b/drivers/dma/qcom/hidma_mgmt_sys.c index 930eae0a6257..9672ef9ee8fc 100644 --- a/drivers/dma/qcom/hidma_mgmt_sys.c +++ b/drivers/dma/qcom/hidma_mgmt_sys.c @@ -102,15 +102,12 @@ static ssize_t show_values(struct device *dev, struct device_attribute *attr, struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev); unsigned int i; - buf[0] = 0; - for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) { - if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) { - sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev)); - break; - } + if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) + return sysfs_emit(buf, "%d\n", + hidma_mgmt_files[i].get(mdev)); } - return strlen(buf); + return 0; } static ssize_t set_values(struct device *dev, struct device_attribute *attr, @@ -143,15 +140,15 @@ static ssize_t show_values_channel(struct kobject *kobj, struct hidma_chan_attr *chattr; struct hidma_mgmt_dev *mdev; - buf[0] = 0; chattr = container_of(attr, struct hidma_chan_attr, attr); mdev = chattr->mdev; - if (strcmp(attr->attr.name, "priority") == 0) - sprintf(buf, "%d\n", mdev->priority[chattr->index]); - else if (strcmp(attr->attr.name, "weight") == 0) - sprintf(buf, "%d\n", mdev->weight[chattr->index]); - return strlen(buf); + if (strcmp(attr->attr.name, "priority") == 0) + return sysfs_emit(buf, "%d\n", mdev->priority[chattr->index]); + else if (strcmp(attr->attr.name, "weight") == 0) + return sysfs_emit(buf, "%d\n", mdev->weight[chattr->index]); + + return 0; } static ssize_t set_values_channel(struct kobject *kobj, From 0fbf772fabe93b52f1ecd9ea193dbc90a6042c4d Mon Sep 17 00:00:00 2001 From: Xueyao An Date: Mon, 8 Jun 2026 18:40:21 +0530 Subject: [PATCH 3363/5214] dt-bindings: dma: qcom,gpi: Document GPI DMA engine for Shikra SoC Document the GPI DMA engine on Shikra platform. It is fully compatible with the GPI DMA engine found on SM6350, thus using qcom,sm6350-gpi-dma as fallback compatible. Signed-off-by: Xueyao An Acked-by: Krzysztof Kozlowski Signed-off-by: Komal Bajaj Link: https://patch.msgid.link/20260608-shikra-dt-m1-v4-1-2114300594a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/qcom,gpi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml index 8f9a552fe30e..54dca623223d 100644 --- a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -37,6 +37,7 @@ properties: - qcom,sc7280-gpi-dma - qcom,sc8280xp-gpi-dma - qcom,sdx75-gpi-dma + - qcom,shikra-gpi-dma - qcom,sm6115-gpi-dma - qcom,sm6375-gpi-dma - qcom,sm8350-gpi-dma From 29d0b2696d4602d36d91dc68a2a5a022ca485a5f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 Apr 2026 19:36:03 +0200 Subject: [PATCH 3364/5214] dmaengine: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260423173602.92503-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/dma/qcom/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig index ace75d7b835a..c71b0b5d536b 100644 --- a/drivers/dma/qcom/Kconfig +++ b/drivers/dma/qcom/Kconfig @@ -11,7 +11,7 @@ config QCOM_ADM and on-chip peripheral devices. config QCOM_BAM_DMA - tristate "QCOM BAM DMA support" + tristate "Qualcomm BAM DMA support" depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) select DMA_ENGINE select DMA_VIRTUAL_CHANNELS @@ -20,7 +20,7 @@ config QCOM_BAM_DMA provides DMA capabilities for a variety of on-chip devices. config QCOM_GPI_DMA - tristate "Qualcomm Technologies GPI DMA support" + tristate "Qualcomm GPI DMA support" depends on ARCH_QCOM select DMA_ENGINE select DMA_VIRTUAL_CHANNELS @@ -32,7 +32,7 @@ config QCOM_GPI_DMA transfer data between DDR and peripheral. config QCOM_HIDMA_MGMT - tristate "Qualcomm Technologies HIDMA Management support" + tristate "Qualcomm HIDMA Management support" depends on HAS_IOMEM select DMA_ENGINE help @@ -44,7 +44,7 @@ config QCOM_HIDMA_MGMT host would run the QCOM_HIDMA_MGMT management driver. config QCOM_HIDMA - tristate "Qualcomm Technologies HIDMA Channel support" + tristate "Qualcomm HIDMA Channel support" depends on HAS_IOMEM select DMA_ENGINE help From 12933e2bc5e07e1500ee69821ab23ad443c3e649 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 5 May 2026 19:46:05 +0300 Subject: [PATCH 3365/5214] MAINTAINERS: dmaengine/ti: Remove myself and add Vignesh as maintainer As I cannot spend adequate time to fulfill my role as maintainer for the TI DMA drivers, it is for the better if I resign and hand over the role to Vignesh Raghavendra. Signed-off-by: Peter Ujfalusi Acked-by: Vignesh Raghavendra Link: https://patch.msgid.link/20260505164605.15878-1-peter.ujfalusi@gmail.com Signed-off-by: Vinod Koul --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..3e7b2d9e9c24 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26349,7 +26349,7 @@ F: sound/soc/codecs/tlv320*.* F: sound/soc/codecs/tpa6130a2.* TEXAS INSTRUMENTS DMA DRIVERS -M: Peter Ujfalusi +M: Vignesh Raghavendra L: dmaengine@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt From 7524fe142b5a772f8421aeee2132cf7e21a00103 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Mon, 11 May 2026 14:38:16 +0800 Subject: [PATCH 3366/5214] dt-bindings: dma: snps,dw-axi-dmac: Add fallback compatible for CV1800B The previous version of the binding change only add compatible string without adding the fallback compatible, this breaks backward compatibility. Add the needed fallback compatible to fix this. Fixes: be3e2a0419c6 ("dt-bindings: dma: snps,dw-axi-dmac: Add CV1800B compatible") Signed-off-by: Inochi Amaoto Acked-by: Conor Dooley Link: https://patch.msgid.link/20260511063818.463877-2-inochiama@gmail.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 804514732dbe..0a30a455b0ee 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -21,11 +21,12 @@ properties: - enum: - snps,axi-dma-1.01a - intel,kmb-axi-dma - - sophgo,cv1800b-axi-dma - starfive,jh7110-axi-dma - starfive,jh8100-axi-dma - items: - - const: altr,agilex5-axi-dma + - enum: + - altr,agilex5-axi-dma + - sophgo,cv1800b-axi-dma - const: snps,axi-dma-1.01a reg: From b3ee497970c63cea37976aeaa84bac39611fe0eb Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Jun 2026 11:45:12 +0200 Subject: [PATCH 3367/5214] dt-bindings: phy: sc8280xp-qmp-pcie: Disallow bifurcation register on Purwa Neither of the two Gen4x4 PHYs found on Purwa supports bifurcation. The PHY is however physically laid out as if it were to, since there are two separate ports (A/B). Split out a new if-then block to un-require the bifurcation register handle to squash this warning: purwa-iot-evk.dtb: phy@1bd4000 (qcom,x1p42100-qmp-gen4x4-pcie-phy): 'qcom,4ln-config-sel' is a required property Fixes: 2e1ffd4c1805 ("dt-bindings: phy: qcom,qmp-pcie: Add X1P42100 PCIe Gen4x4 PHY") Reported-by: Rob Herring Closes: https://lore.kernel.org/linux-arm-msm/176857775469.1631885.16133311938753588148.robh@kernel.org/ Signed-off-by: Konrad Dybcio Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260610-topic-purwa_phy_shutup_warning-v2-1-951c1fbfe9b2@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 3a35120a77ec..431e8cb5df84 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -136,13 +136,22 @@ allOf: items: - description: port a - description: port b - required: - - qcom,4ln-config-sel else: properties: reg: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-qmp-gen3x4-pcie-phy + - qcom,x1e80100-qmp-gen4x4-pcie-phy + then: + required: + - qcom,4ln-config-sel + - if: properties: compatible: From 4904117935319233b1ec8f0528560dc91e8ae4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 17:19:57 +0200 Subject: [PATCH 3368/5214] phy: nxp-ptn3222: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260519151957.1593214-2-u.kleine-koenig@baylibre.com Signed-off-by: Vinod Koul --- drivers/phy/phy-nxp-ptn3222.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-nxp-ptn3222.c b/drivers/phy/phy-nxp-ptn3222.c index c6179d8701e6..6cb79b5c4fdb 100644 --- a/drivers/phy/phy-nxp-ptn3222.c +++ b/drivers/phy/phy-nxp-ptn3222.c @@ -97,7 +97,7 @@ static int ptn3222_probe(struct i2c_client *client) } static const struct i2c_device_id ptn3222_table[] = { - { "ptn3222" }, + { .name = "ptn3222" }, { } }; MODULE_DEVICE_TABLE(i2c, ptn3222_table); From afdf104ec11681aacba0865863647f725f47ab90 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Mon, 8 Jun 2026 14:18:13 +0530 Subject: [PATCH 3369/5214] dt-bindings: phy: sc8280xp-qmp-pcie: Document Eliza PCIe phy Add compatibles for the Eliza PCIe QMP PHY's, which supports Gen3x1 and Gen3x2 configurations. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Krishna Chaitanya Chundru Acked-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260608-eliza-v3-1-9bdeb7434b28@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 431e8cb5df84..108cf9dc86ea 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -16,6 +16,8 @@ description: properties: compatible: enum: + - qcom,eliza-qmp-gen3x1-pcie-phy + - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy - qcom,kaanapali-qmp-gen3x2-pcie-phy @@ -190,6 +192,8 @@ allOf: compatible: contains: enum: + - qcom,eliza-qmp-gen3x1-pcie-phy + - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy - qcom,qcs8300-qmp-gen4x2-pcie-phy @@ -215,6 +219,8 @@ allOf: compatible: contains: enum: + - qcom,eliza-qmp-gen3x1-pcie-phy + - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy - qcom,kaanapali-qmp-gen3x2-pcie-phy From fdd2913a4e505716dce5c9f89c268628d9a019d5 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Mon, 8 Jun 2026 14:18:15 +0530 Subject: [PATCH 3370/5214] phy: qcom: qmp-pcie: Add QMP PCIe PHY support for Eliza Add QMP PCIe PHY support for the Eliza SoC. Introduce a new Gen3x1 PHY configuration with Eliza-specific initialization tables, and reuse the existing sm8550 Gen3x2 configuration for the Gen3x2 PHY instance. Also add the missing QPHY_PCIE_V6_PCS_PCIE_INT_AUX_CLK_CONFIG1 register definition to the PCIe V6 PCS header. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krishna Chaitanya Chundru Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260608-eliza-v3-3-9bdeb7434b28@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 139 ++++++++++++++++++ .../phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h | 1 + 2 files changed, 140 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 75afbd15aaf4..d3effad7a074 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -198,6 +198,112 @@ static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), }; +static const struct qmp_phy_init_tbl eliza_qmp_gen3x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0x93), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADDITIONAL_MISC_3, 0x0f), +}; + +static const struct qmp_phy_init_tbl eliza_qmp_gen3x1_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_REFGEN_REQ_CONFIG1, 0x05), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RATE_SLEW_CNTRL1, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_EQ_CONFIG2, 0x0f), +}; + +static const struct qmp_phy_init_tbl eliza_qmp_gen3x1_pcie_misc_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG2, 0x1d), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_RXEQEVAL_TIME, 0x27), +}; + +static const struct qmp_phy_init_tbl eliza_qmp_gen3x1_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_1, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_PI_QEC_CTRL, 0x02), +}; + +static const struct qmp_phy_init_tbl eliza_qmp_gen3x1_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_PI_CONTROLS, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_AUX_DATA_TCOARSE_TFINE, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_TX_ADAPT_POST_THRESH, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_GM_CAL, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIDGET_ENABLES, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH3, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH4, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH2, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH4, 0x89), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH2, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH4, 0x89), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_TRIM, 0x08), +}; + static const struct qmp_phy_init_tbl ipq6018_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER1, 0x7d), QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER2, 0x01), @@ -3532,6 +3638,33 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v8_50 = { .txrxz = 0xd000, }; +static const struct qmp_phy_cfg eliza_qmp_gen3x1_pciephy_cfg = { + .lanes = 1, + + .offsets = &qmp_pcie_offsets_v5, + + .tbls = { + .serdes = eliza_qmp_gen3x1_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(eliza_qmp_gen3x1_pcie_serdes_tbl), + .tx = eliza_qmp_gen3x1_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(eliza_qmp_gen3x1_pcie_tx_tbl), + .rx = eliza_qmp_gen3x1_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(eliza_qmp_gen3x1_pcie_rx_tbl), + .pcs = eliza_qmp_gen3x1_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(eliza_qmp_gen3x1_pcie_pcs_tbl), + .pcs_misc = eliza_qmp_gen3x1_pcie_misc_pcs_tbl, + .pcs_misc_num = ARRAY_SIZE(eliza_qmp_gen3x1_pcie_misc_pcs_tbl), + }, + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v6_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, +}; + static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .lanes = 1, @@ -5399,6 +5532,12 @@ static int qmp_pcie_probe(struct platform_device *pdev) static const struct of_device_id qmp_pcie_of_match_table[] = { { + .compatible = "qcom,eliza-qmp-gen3x1-pcie-phy", + .data = &eliza_qmp_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,eliza-qmp-gen3x2-pcie-phy", + .data = &sm8550_qmp_gen3x2_pciephy_cfg, + }, { .compatible = "qcom,glymur-qmp-gen4x2-pcie-phy", .data = &glymur_qmp_gen4x2_pciephy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h index 45397cb3c0c6..17a0f9d18acf 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v6.h @@ -12,6 +12,7 @@ #define QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG2 0x0c #define QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG4 0x14 #define QPHY_PCIE_V6_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20 +#define QPHY_PCIE_V6_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x54 #define QPHY_PCIE_V6_PCS_PCIE_OSC_DTCT_ACTIONS 0x94 #define QPHY_PCIE_V6_PCS_LANE1_INSIG_SW_CTRL2 0x024 From ebc1d9906894703286d12306a6f242d90cfb49e8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 17:19:49 +0000 Subject: [PATCH 3371/5214] PCI: mediatek: Use actual physical address instead of virt_to_phys() The driver previously used virt_to_phys() on the ioremapped register base (port->base) to compute the MSI message address. Using virt_to_phys() on an IO mapped address is incorrect because it expects a kernel virtual address. To fix it, store the physical start of the I/O register region in mtk_pcie_port->phys_base and use it to build the MSI address. This replaces the incorrect virt_to_phys() usage and ensures MSI addresses are generated correctly. Fixes: 43e6409db64d ("PCI: mediatek: Add MSI support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Tested-by: Caleb James DeLisle Link: https://patch.msgid.link/20260521171951.1495781-2-cjd@cjdns.fr --- drivers/pci/controller/pcie-mediatek.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index f34d91e495bc..1bb8839c3cb0 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -176,6 +176,7 @@ struct mtk_pcie_soc { /** * struct mtk_pcie_port - PCIe port information * @base: IO mapped register base + * @phys_base: Physical address of the I/O register base region * @list: port list * @pcie: pointer to PCIe host info * @reset: pointer to port reset control @@ -197,6 +198,7 @@ struct mtk_pcie_soc { */ struct mtk_pcie_port { void __iomem *base; + phys_addr_t phys_base; struct list_head list; struct mtk_pcie *pcie; struct reset_control *reset; @@ -406,7 +408,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) phys_addr_t addr; /* MT2712/MT7622 only support 32-bit MSI addresses */ - addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + addr = port->phys_base + PCIE_MSI_VECTOR; msg->address_hi = 0; msg->address_lo = lower_32_bits(addr); @@ -521,7 +523,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) u32 val; phys_addr_t msg_addr; - msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + msg_addr = port->phys_base + PCIE_MSI_VECTOR; val = lower_32_bits(msg_addr); writel(val, port->base + PCIE_IMSI_ADDR); @@ -962,6 +964,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); + struct resource *res; char name[20]; int err; @@ -970,7 +973,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); - port->base = devm_platform_ioremap_resource_byname(pdev, name); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(dev, "failed to get port%d base\n", slot); + return -EINVAL; + } + + port->phys_base = res->start; + port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { dev_err(dev, "failed to map port%d base\n", slot); return PTR_ERR(port->base); From 843044971a5167087a9484f8f6eec81da30f2a71 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Thu, 21 May 2026 17:19:50 +0000 Subject: [PATCH 3372/5214] dt-bindings: PCI: mediatek: Add support for EcoNet EN7528 Introduce EcoNet EN7528 SoC compatible in MediaTek PCIe controller binding. EcoNet PCIe controller has the same configuration model as Mediatek v2 but is initialized more similarly to an MT7621 PCIe. Signed-off-by: Caleb James DeLisle Signed-off-by: Manivannan Sadhasivam Acked-by: Conor Dooley Link: https://patch.msgid.link/20260521171951.1495781-3-cjd@cjdns.fr --- .../bindings/pci/mediatek-pcie.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml index 0b8c78ec4f91..c009a7a52bc6 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml @@ -14,6 +14,7 @@ properties: oneOf: - enum: - airoha,an7583-pcie + - econet,en7528-pcie - mediatek,mt2712-pcie - mediatek,mt7622-pcie - mediatek,mt7629-pcie @@ -226,6 +227,31 @@ allOf: mediatek,pbus-csr: false + - if: + properties: + compatible: + contains: + const: econet,en7528-pcie + then: + properties: + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + + resets: false + + reset-names: false + + power-domains: false + + mediatek,pbus-csr: false + + required: + - phys + - phy-names + unevaluatedProperties: false examples: From 493f8fc57bf685e23d3c924dbd787249d47fb972 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:37 +0300 Subject: [PATCH 3373/5214] phy: lynx-28g: avoid returning NULL in of_xlate() function Sashiko points out that _of_phy_get() does not support a NULL returned output from phy_provider->of_xlate(), just a valid pointer or a pointer-encoded error. When lynx_28g_probe() -> for_each_available_child_of_node() skips over lanes which have OF nodes with status = "disabled", the priv->lane[idx].phy pointer will remain NULL. This NULL pointer may be propagated to lynx_28g_xlate() if the device tree contains a phandle to the disabled lane AND fw_devlink did not block probing for the consumer. In that case, the PHY core will crash when trying to dereference the NULL phy pointer. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-2-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 92bfc5f65e0b..cacc128dc96a 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1435,7 +1435,7 @@ static struct phy *lynx_28g_xlate(struct device *dev, idx < priv->info->first_lane)) return ERR_PTR(-EINVAL); - return priv->lane[idx].phy; + return priv->lane[idx].phy ?: ERR_PTR(-ENODEV); } static int lynx_28g_probe_lane(struct lynx_28g_priv *priv, int id, From a45f9dac20b6757487502109feac0298ec78f3bd Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:38 +0300 Subject: [PATCH 3374/5214] phy: lynx-28g: reject probing on devices with unsupported OF nodes It is possible to bind the lynx-28g driver to an arbitrary device with an OF node, using the driver_override mechanism that is available for the platform bus, and trigger a crash this way: $ echo 1ea0000.serdes > /sys/bus/platform/drivers/lynx-10g/unbind $ echo lynx-28g > /sys/bus/platform/devices/1ea0000.serdes/driver_override $ echo 1ea0000.serdes > /sys/bus/platform/drivers/lynx-28g/bind Internal error: Oops: 0000000096000004 [#1] SMP Hardware name: LS1028A RDB Board (DT) pc : lynx_probe+0x118/0x4fc lr : lynx_probe+0x110/0x4fc Call trace: lynx_probe+0x118/0x4fc (P) lynx_28g_probe+0x54/0x7c platform_probe+0x68/0xa4 really_probe+0x14c/0x2ec __driver_probe_device+0xc8/0x170 device_driver_attach+0x58/0xa8 bind_store+0xd8/0x118 drv_attr_store+0x24/0x38 The crash is caused by the fact that of_device_get_match_data() returns NULL (the bound device has a different compatible string) and this is not checked. There was a previous attempt to avoid this in commit c9d80e861034 ("phy: lynx-28g: require an OF node to probe"), but the mechanism was not fully understood and it only covered the case where the driver was bound to a device with no OF node. The issue was found during Sashiko review. Elevated privilege is required to override the driver for a device, so the real life impact of the issue should not be very high. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-3-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index cacc128dc96a..1f5cb02931f5 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1477,6 +1477,9 @@ static int lynx_28g_probe(struct platform_device *pdev) priv->dev = dev; priv->info = of_device_get_match_data(dev); + if (!priv->info) + return -ENODEV; + dev_set_drvdata(dev, priv); spin_lock_init(&priv->pcc_lock); INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); From ac0ffe7cf1221182f4068d8aaf825b01655c7ca9 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:39 +0300 Subject: [PATCH 3375/5214] phy: lynx-28g: move lane mode helpers to new core module Do some preparation work for the introduction of the lynx-10g driver, which will share a common backbone with the 28G Lynx SerDes. This is just trivial stuff which can be moved without any surgery, and is easy to follow but otherwise pollutes more serious changes. The lane modes themselves are exported to a public header, because on the 10G Lynx, the hardware requires implementing a procedure called "RCW override". This requires coordination with drivers/soc/fsl/guts.c to tell it that a SerDes lane needs to be switched to a different protocol (enum lynx_lane_mode). Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-4-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/Kconfig | 4 ++ drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-fsl-lynx-28g.c | 56 ++--------------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 44 ++++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 24 ++++++++++ include/soc/fsl/phy-fsl-lynx.h | 16 +++++++ 6 files changed, 92 insertions(+), 53 deletions(-) create mode 100644 drivers/phy/freescale/phy-fsl-lynx-core.c create mode 100644 drivers/phy/freescale/phy-fsl-lynx-core.h create mode 100644 include/soc/fsl/phy-fsl-lynx.h diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 81f53564ee15..ac575d531db7 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -51,11 +51,15 @@ config PHY_FSL_SAMSUNG_HDMI_PHY Enable this to add support for the Samsung HDMI PHY in i.MX8MP. endif +config PHY_FSL_LYNX_CORE + tristate + config PHY_FSL_LYNX_28G tristate "Freescale Layerscape Lynx 28G SerDes PHY support" depends on OF depends on ARCH_LAYERSCAPE || COMPILE_TEST select GENERIC_PHY + select PHY_FSL_LYNX_CORE help Enable this to add support for the Lynx SerDes 28G PHY as found on NXP's Layerscape platforms such as LX2160A. diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index 658eac7d0a62..d7aa62cdeb39 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o obj-$(CONFIG_PHY_FSL_IMX8QM_HSIO) += phy-fsl-imx8qm-hsio.o +obj-$(CONFIG_PHY_FSL_LYNX_CORE) += phy-fsl-lynx-core.o obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 1f5cb02931f5..d3f49d96340f 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -9,6 +9,8 @@ #include #include +#include "phy-fsl-lynx-core.h" + #define LYNX_28G_NUM_LANE 8 #define LYNX_28G_NUM_PLL 2 @@ -282,15 +284,6 @@ enum lynx_28g_proto_sel { PROTO_SEL_25G_50G_100G = 0x1a, }; -enum lynx_lane_mode { - LANE_MODE_UNKNOWN, - LANE_MODE_1000BASEX_SGMII, - LANE_MODE_10GBASER, - LANE_MODE_USXGMII, - LANE_MODE_25GBASER, - LANE_MODE_MAX, -}; - struct lynx_28g_proto_conf { /* LNaGCR0 */ int proto_sel; @@ -463,12 +456,6 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { }, }; -struct lynx_pccr { - int offset; - int width; - int shift; -}; - struct lynx_28g_priv; struct lynx_28g_pll { @@ -487,11 +474,6 @@ struct lynx_28g_lane { enum lynx_lane_mode mode; }; -struct lynx_info { - bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); - int first_lane; -}; - struct lynx_28g_priv { void __iomem *base; struct device *dev; @@ -531,39 +513,6 @@ static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off, #define lynx_28g_pll_read(pll, reg) \ ioread32((pll)->priv->base + reg((pll)->id)) -static const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) -{ - switch (lane_mode) { - case LANE_MODE_1000BASEX_SGMII: - return "1000Base-X/SGMII"; - case LANE_MODE_10GBASER: - return "10GBase-R"; - case LANE_MODE_USXGMII: - return "USXGMII"; - case LANE_MODE_25GBASER: - return "25GBase-R"; - default: - return "unknown"; - } -} - -static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) -{ - switch (intf) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - return LANE_MODE_1000BASEX_SGMII; - case PHY_INTERFACE_MODE_10GBASER: - return LANE_MODE_10GBASER; - case PHY_INTERFACE_MODE_USXGMII: - return LANE_MODE_USXGMII; - case PHY_INTERFACE_MODE_25GBASER: - return LANE_MODE_25GBASER; - default: - return LANE_MODE_UNKNOWN; - } -} - /* A lane mode is supported if we have a PLL that can provide its required * clock net, and if there is a protocol converter for that mode on that lane. */ @@ -1572,6 +1521,7 @@ static struct platform_driver lynx_28g_driver = { }; module_platform_driver(lynx_28g_driver); +MODULE_IMPORT_NS("PHY_FSL_LYNX"); MODULE_AUTHOR("Ioana Ciornei "); MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c new file mode 100644 index 000000000000..d56f189c162d --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025-2026 NXP */ + +#include + +#include "phy-fsl-lynx-core.h" + +const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + return "1000Base-X/SGMII"; + case LANE_MODE_10GBASER: + return "10GBase-R"; + case LANE_MODE_USXGMII: + return "USXGMII"; + case LANE_MODE_25GBASER: + return "25GBase-R"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_NS_GPL(lynx_lane_mode_str, "PHY_FSL_LYNX"); + +enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) +{ + switch (intf) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + return LANE_MODE_1000BASEX_SGMII; + case PHY_INTERFACE_MODE_10GBASER: + return LANE_MODE_10GBASER; + case PHY_INTERFACE_MODE_USXGMII: + return LANE_MODE_USXGMII; + case PHY_INTERFACE_MODE_25GBASER: + return LANE_MODE_25GBASER; + default: + return LANE_MODE_UNKNOWN; + } +} +EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h new file mode 100644 index 000000000000..fe15986482b0 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025-2026 NXP */ + +#ifndef _PHY_FSL_LYNX_CORE_H +#define _PHY_FSL_LYNX_CORE_H + +#include +#include + +struct lynx_pccr { + int offset; + int width; + int shift; +}; + +struct lynx_info { + bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); + int first_lane; +}; + +const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode); +enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf); + +#endif diff --git a/include/soc/fsl/phy-fsl-lynx.h b/include/soc/fsl/phy-fsl-lynx.h new file mode 100644 index 000000000000..92e8272d5ae1 --- /dev/null +++ b/include/soc/fsl/phy-fsl-lynx.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2023-2026 NXP */ + +#ifndef __PHY_FSL_LYNX_H_ +#define __PHY_FSL_LYNX_H_ + +enum lynx_lane_mode { + LANE_MODE_UNKNOWN, + LANE_MODE_1000BASEX_SGMII, + LANE_MODE_10GBASER, + LANE_MODE_USXGMII, + LANE_MODE_25GBASER, + LANE_MODE_MAX, +}; + +#endif /* __PHY_FSL_LYNX_H_ */ From b7021a4d3129aeb0e25c3edcaf3a36199e73f652 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:40 +0300 Subject: [PATCH 3376/5214] phy: lynx-28g: move data structures to core The goal is to avoid duplicating the core data structures when introducing the new lynx-10g driver. We move the following to phy-fsl-lynx-core: - struct lynx_28g_pll -> struct lynx_pll. This has some hardware-specific register fields which need to become hardware agnostic (the PLL register layout is different for Lynx 10G), So: - PLLnRSTCTL_DIS(pll->rstctl) becomes !pll->enabled - PLLnRSTCTL_LOCK(pll->rstctl) becomes pll->locked - FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1) becomes pll->frate_sel - FIELD_GET(PLLnCR0_REFCLK_SEL, pll->cr0) becomes pll->refclk_sel - struct lynx_28g_lane -> struct lynx_lane - struct lynx_28g_priv -> struct lynx_priv - field lane[LYNX_28G_NUM_LANE] has to be dynamically allocated. Not all Lynx 10G SerDes blocks have 8 lanes. - LYNX_28G_NUM_PLL -> LYNX_NUM_PLL. This is an architectural constant which is the same for Lynx 10G as well. To avoid major noise in the lynx-28g driver, we keep compatibility shims (for now) where the old lynx_28g names are preserved, but translate to the common data structures. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-5-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 135 +++++++--------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 23 ++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 64 ++++++++++ 3 files changed, 129 insertions(+), 93 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index d3f49d96340f..570fa74daba0 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -12,7 +12,7 @@ #include "phy-fsl-lynx-core.h" #define LYNX_28G_NUM_LANE 8 -#define LYNX_28G_NUM_PLL 2 +#define LYNX_28G_NUM_PLL LYNX_NUM_PLL /* SoC IP wrapper for protocol converters */ #define PCC8 0x10a0 @@ -43,8 +43,8 @@ /* Per PLL registers */ #define PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0) -#define PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24) -#define PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23) +#define PLLnRSTCTL_DIS BIT(24) +#define PLLnRSTCTL_LOCK BIT(23) #define PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4) #define PLLnCR0_REFCLK_SEL GENMASK(20, 16) @@ -269,6 +269,17 @@ #define LYNX_28G_LANE_STOP_SLEEP_US 100 #define LYNX_28G_LANE_STOP_TIMEOUT_US 1000000 +#define lynx_28g_read lynx_read +#define lynx_28g_write lynx_write +#define lynx_28g_lane_rmw lynx_lane_rmw +#define lynx_28g_lane_read lynx_lane_read +#define lynx_28g_lane_write lynx_lane_write +#define lynx_28g_pll_read lynx_pll_read + +#define lynx_28g_priv lynx_priv +#define lynx_28g_lane lynx_lane +#define lynx_28g_pll lynx_pll + enum lynx_28g_eq_type { EQ_TYPE_NO_EQ = 0, EQ_TYPE_2TAP = 1, @@ -456,86 +467,6 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { }, }; -struct lynx_28g_priv; - -struct lynx_28g_pll { - struct lynx_28g_priv *priv; - u32 rstctl, cr0, cr1; - int id; - DECLARE_BITMAP(supported, LANE_MODE_MAX); -}; - -struct lynx_28g_lane { - struct lynx_28g_priv *priv; - struct phy *phy; - bool powered_up; - bool init; - unsigned int id; - enum lynx_lane_mode mode; -}; - -struct lynx_28g_priv { - void __iomem *base; - struct device *dev; - const struct lynx_info *info; - /* Serialize concurrent access to registers shared between lanes, - * like PCCn - */ - spinlock_t pcc_lock; - struct lynx_28g_pll pll[LYNX_28G_NUM_PLL]; - struct lynx_28g_lane lane[LYNX_28G_NUM_LANE]; - - struct delayed_work cdr_check; -}; - -static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off, - u32 val, u32 mask) -{ - void __iomem *reg = priv->base + off; - u32 orig, tmp; - - orig = ioread32(reg); - tmp = orig & ~mask; - tmp |= val; - iowrite32(tmp, reg); -} - -#define lynx_28g_read(priv, off) \ - ioread32((priv)->base + (off)) -#define lynx_28g_write(priv, off, val) \ - iowrite32(val, (priv)->base + (off)) -#define lynx_28g_lane_rmw(lane, reg, val, mask) \ - lynx_28g_rmw((lane)->priv, reg(lane->id), val, mask) -#define lynx_28g_lane_read(lane, reg) \ - ioread32((lane)->priv->base + reg((lane)->id)) -#define lynx_28g_lane_write(lane, reg, val) \ - iowrite32(val, (lane)->priv->base + reg((lane)->id)) -#define lynx_28g_pll_read(pll, reg) \ - ioread32((pll)->priv->base + reg((pll)->id)) - -/* A lane mode is supported if we have a PLL that can provide its required - * clock net, and if there is a protocol converter for that mode on that lane. - */ -static bool lynx_28g_supports_lane_mode(struct lynx_28g_lane *lane, - enum lynx_lane_mode mode) -{ - struct lynx_28g_priv *priv = lane->priv; - int i; - - if (!priv->info->lane_supports_mode(lane->id, mode)) - return false; - - for (i = 0; i < LYNX_28G_NUM_PLL; i++) { - if (PLLnRSTCTL_DIS(priv->pll[i].rstctl)) - continue; - - if (test_bit(mode, priv->pll[i].supported)) - return true; - } - - return false; -} - static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, enum lynx_lane_mode mode) { @@ -545,7 +476,7 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, for (i = 0; i < LYNX_28G_NUM_PLL; i++) { pll = &priv->pll[i]; - if (PLLnRSTCTL_DIS(pll->rstctl)) + if (!pll->enabled) continue; if (test_bit(mode, pll->supported)) @@ -553,7 +484,7 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, } /* no pll supports requested mode, either caller forgot to check - * lynx_28g_supports_lane_mode, or this is a bug. + * lynx_lane_supports_mode(), or this is a bug. */ dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n", lynx_lane_mode_str(mode)); @@ -564,7 +495,7 @@ static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, struct lynx_28g_pll *pll, enum lynx_lane_mode lane_mode) { - switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) { + switch (pll->frate_sel) { case PLLnCR1_FRATE_5G_10GVCO: case PLLnCR1_FRATE_5G_25GVCO: switch (lane_mode) { @@ -879,27 +810,33 @@ static bool lynx_28g_compat_lane_supports_mode(int lane, static const struct lynx_info lynx_info_compat = { .lane_supports_mode = lynx_28g_compat_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes1 = { .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes2 = { .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes3 = { .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2162a_serdes1 = { .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, .first_lane = 4, + .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2162a_serdes2 = { .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, }; static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, @@ -1168,7 +1105,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(lane, lane_mode)) + if (!lynx_lane_supports_mode(lane, lane_mode)) return -EOPNOTSUPP; if (lane_mode == lane->mode) @@ -1210,7 +1147,7 @@ static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(lane, lane_mode)) + if (!lynx_lane_supports_mode(lane, lane_mode)) return -EOPNOTSUPP; return 0; @@ -1262,6 +1199,7 @@ static const struct phy_ops lynx_28g_ops = { static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) { struct lynx_28g_pll *pll; + u32 val; int i; for (i = 0; i < LYNX_28G_NUM_PLL; i++) { @@ -1269,14 +1207,20 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) pll->priv = priv; pll->id = i; - pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL); - pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0); - pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1); + val = lynx_28g_pll_read(pll, PLLnRSTCTL); + pll->enabled = !(val & PLLnRSTCTL_DIS); + pll->locked = !!(val & PLLnRSTCTL_LOCK); - if (PLLnRSTCTL_DIS(pll->rstctl)) + val = lynx_28g_pll_read(pll, PLLnCR0); + pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val); + + val = lynx_28g_pll_read(pll, PLLnCR1); + pll->frate_sel = FIELD_GET(PLLnCR1_FRATE_SEL, val); + + if (!pll->enabled) continue; - switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) { + switch (pll->frate_sel) { case PLLnCR1_FRATE_5G_10GVCO: case PLLnCR1_FRATE_5G_25GVCO: /* 5GHz clock net */ @@ -1440,6 +1384,11 @@ static int lynx_28g_probe(struct platform_device *pdev) if (priv->info == &lynx_info_compat) dev_warn(dev, "Please update device tree to use per-device compatible strings\n"); + priv->lane = devm_kcalloc(dev, priv->info->num_lanes, + sizeof(*priv->lane), GFP_KERNEL); + if (!priv->lane) + return -ENOMEM; + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index d56f189c162d..de45b14d3fb6 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -40,5 +40,28 @@ enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) } EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX"); +/* A lane mode is supported if we have a PLL that can provide its required + * clock net, and if there is a protocol converter for that mode on that lane. + */ +bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) +{ + struct lynx_priv *priv = lane->priv; + int i; + + if (!priv->info->lane_supports_mode(lane->id, mode)) + return false; + + for (i = 0; i < LYNX_NUM_PLL; i++) { + if (!priv->pll[i].enabled) + continue; + + if (test_bit(mode, priv->pll[i].supported)) + return true; + } + + return false; +} +EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index fe15986482b0..f0cb3e805235 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -7,18 +7,82 @@ #include #include +#define LYNX_NUM_PLL 2 + struct lynx_pccr { int offset; int width; int shift; }; +struct lynx_priv; + +struct lynx_pll { + struct lynx_priv *priv; + int id; + int refclk_sel; + int frate_sel; + bool enabled; + bool locked; + DECLARE_BITMAP(supported, LANE_MODE_MAX); +}; + +struct lynx_lane { + struct lynx_priv *priv; + struct phy *phy; + bool powered_up; + bool init; + unsigned int id; + enum lynx_lane_mode mode; +}; + struct lynx_info { bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); int first_lane; + int num_lanes; }; +struct lynx_priv { + void __iomem *base; + struct device *dev; + const struct lynx_info *info; + /* Serialize concurrent access to registers shared between lanes, + * like PCCn + */ + spinlock_t pcc_lock; + struct lynx_pll pll[LYNX_NUM_PLL]; + struct lynx_lane *lane; + + struct delayed_work cdr_check; +}; + +static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val, + u32 mask) +{ + void __iomem *reg = priv->base + off; + u32 orig, tmp; + + orig = ioread32(reg); + tmp = orig & ~mask; + tmp |= val; + iowrite32(tmp, reg); +} + +#define lynx_read(priv, off) \ + ioread32((priv)->base + (off)) +#define lynx_write(priv, off, val) \ + iowrite32(val, (priv)->base + (off)) +#define lynx_lane_rmw(lane, reg, val, mask) \ + lynx_rmw((lane)->priv, reg(lane->id), val, mask) +#define lynx_lane_read(lane, reg) \ + ioread32((lane)->priv->base + reg((lane)->id)) +#define lynx_lane_write(lane, reg, val) \ + iowrite32(val, (lane)->priv->base + reg((lane)->id)) +#define lynx_pll_read(pll, reg) \ + ioread32((pll)->priv->base + reg((pll)->id)) + const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode); enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf); +bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode); #endif From d4550ea4b161202607e31b0b6c6c34fef7e2b797 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:41 +0300 Subject: [PATCH 3377/5214] phy: lynx-28g: common lynx_pll_get() The logic should be absolutely unchanged in the new 10G Lynx SerDes driver, so let's move this to phy-fsl-lynx-core.c and update the 28G Lynx driver to use the common variant. While at it, update the call site, lynx_28g_lane_remap_pll(), to use the new data structures, and refactor the NULL pll pointer check (the current form triggers a checkpatch CHECK). Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-6-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 34 ++++------------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 24 ++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 2 ++ 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 570fa74daba0..5c473d6c233e 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -467,30 +467,6 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { }, }; -static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, - enum lynx_lane_mode mode) -{ - struct lynx_28g_pll *pll; - int i; - - for (i = 0; i < LYNX_28G_NUM_PLL; i++) { - pll = &priv->pll[i]; - - if (!pll->enabled) - continue; - - if (test_bit(mode, pll->supported)) - return pll; - } - - /* no pll supports requested mode, either caller forgot to check - * lynx_lane_supports_mode(), or this is a bug. - */ - dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n", - lynx_lane_mode_str(mode)); - return NULL; -} - static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, struct lynx_28g_pll *pll, enum lynx_lane_mode lane_mode) @@ -927,15 +903,15 @@ static int lynx_pcvt_rmw(struct lynx_28g_lane *lane, return lynx_pcvt_write(lane, lane_mode, cr, tmp); } -static void lynx_28g_lane_remap_pll(struct lynx_28g_lane *lane, +static void lynx_28g_lane_remap_pll(struct lynx_lane *lane, enum lynx_lane_mode lane_mode) { - struct lynx_28g_priv *priv = lane->priv; - struct lynx_28g_pll *pll; + struct lynx_priv *priv = lane->priv; + struct lynx_pll *pll; /* Switch to the PLL that works with this interface type */ - pll = lynx_28g_pll_get(priv, lane_mode); - if (unlikely(pll == NULL)) + pll = lynx_pll_get(priv, lane_mode); + if (unlikely(!pll)) return; lynx_28g_lane_set_pll(lane, pll); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index de45b14d3fb6..5e5bcaa54d09 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -63,5 +63,29 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) } EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX"); +struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode) +{ + struct lynx_pll *pll; + int i; + + for (i = 0; i < LYNX_NUM_PLL; i++) { + pll = &priv->pll[i]; + + if (!pll->enabled) + continue; + + if (test_bit(mode, pll->supported)) + return pll; + } + + /* no pll supports requested mode, either caller forgot to check + * lynx_lane_supports_mode(), or this is a bug. + */ + dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n", + lynx_lane_mode_str(mode)); + return NULL; +} +EXPORT_SYMBOL_NS_GPL(lynx_pll_get, "PHY_FSL_LYNX"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index f0cb3e805235..b726ff21972b 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -85,4 +85,6 @@ const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode); enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf); bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode); +struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode); + #endif From 51c25f4b0c883c1b7eee5f8f3aa0c2245eb6351a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:42 +0300 Subject: [PATCH 3378/5214] phy: lynx-28g: generalize protocol converter accessors The protocol converters on the 10G Lynx are architecturally similar, but different in layout from the 28G Lynx ones. Move lynx_pccr_read(), lynx_pccr_write(), lynx_pcvt_read() and lynx_pcvt_write() from the 28G Lynx driver to the common module, and permit each SerDes driver to provide just its own bits in order to use this common API. Currently, that just means that the direct calls to lynx_28g_get_pcvt_offset() are modified to go through the lynx->info->get_pcvt_offset() indirect function call, and similarly, lynx_28g_get_pccr() through lynx->info->get_pccr(). Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-7-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 102 +++------------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 90 +++++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 13 +++ 3 files changed, 115 insertions(+), 90 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 5c473d6c233e..1de663283faf 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -269,8 +269,6 @@ #define LYNX_28G_LANE_STOP_SLEEP_US 100 #define LYNX_28G_LANE_STOP_TIMEOUT_US 1000000 -#define lynx_28g_read lynx_read -#define lynx_28g_write lynx_write #define lynx_28g_lane_rmw lynx_lane_rmw #define lynx_28g_lane_read lynx_lane_read #define lynx_28g_lane_write lynx_lane_write @@ -785,124 +783,48 @@ static bool lynx_28g_compat_lane_supports_mode(int lane, } static const struct lynx_info lynx_info_compat = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lynx_28g_compat_lane_supports_mode, .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes1 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes2 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2160a_serdes3 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2162a_serdes1 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, .first_lane = 4, .num_lanes = LYNX_28G_NUM_LANE, }; static const struct lynx_info lynx_info_lx2162a_serdes2 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, .num_lanes = LYNX_28G_NUM_LANE, }; -static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, - u32 *val) -{ - struct lynx_28g_priv *priv = lane->priv; - struct lynx_pccr pccr; - u32 tmp; - int err; - - err = lynx_28g_get_pccr(mode, lane->id, &pccr); - if (err) - return err; - - tmp = lynx_28g_read(priv, pccr.offset); - *val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0); - - return 0; -} - -static int lynx_pccr_write(struct lynx_28g_lane *lane, - enum lynx_lane_mode lane_mode, u32 val) -{ - struct lynx_28g_priv *priv = lane->priv; - struct lynx_pccr pccr; - u32 old, tmp, mask; - int err; - - err = lynx_28g_get_pccr(lane_mode, lane->id, &pccr); - if (err) - return err; - - old = lynx_28g_read(priv, pccr.offset); - mask = GENMASK(pccr.width - 1, 0) << pccr.shift; - tmp = (old & ~mask) | (val << pccr.shift); - lynx_28g_write(priv, pccr.offset, tmp); - - dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n", - pccr.offset, old, tmp); - - return 0; -} - -static int lynx_pcvt_read(struct lynx_28g_lane *lane, - enum lynx_lane_mode lane_mode, int cr, u32 *val) -{ - struct lynx_28g_priv *priv = lane->priv; - int offset; - - offset = lynx_28g_get_pcvt_offset(lane->id, lane_mode); - if (offset < 0) - return offset; - - *val = lynx_28g_read(priv, offset + cr); - - return 0; -} - -static int lynx_pcvt_write(struct lynx_28g_lane *lane, - enum lynx_lane_mode lane_mode, int cr, u32 val) -{ - struct lynx_28g_priv *priv = lane->priv; - int offset; - - offset = lynx_28g_get_pcvt_offset(lane->id, lane_mode); - if (offset < 0) - return offset; - - lynx_28g_write(priv, offset + cr, val); - - return 0; -} - -static int lynx_pcvt_rmw(struct lynx_28g_lane *lane, - enum lynx_lane_mode lane_mode, - int cr, u32 val, u32 mask) -{ - int err; - u32 tmp; - - err = lynx_pcvt_read(lane, lane_mode, cr, &tmp); - if (err) - return err; - - tmp &= ~mask; - tmp |= val; - - return lynx_pcvt_write(lane, lane_mode, cr, tmp); -} - static void lynx_28g_lane_remap_pll(struct lynx_lane *lane, enum lynx_lane_mode lane_mode) { diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 5e5bcaa54d09..f49d594622cb 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -87,5 +87,95 @@ struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode) } EXPORT_SYMBOL_NS_GPL(lynx_pll_get, "PHY_FSL_LYNX"); +int lynx_pccr_read(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 *val) +{ + struct lynx_priv *priv = lane->priv; + struct lynx_pccr pccr; + u32 tmp; + int err; + + err = priv->info->get_pccr(mode, lane->id, &pccr); + if (err) + return err; + + tmp = lynx_read(priv, pccr.offset); + *val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_pccr_read, "PHY_FSL_LYNX"); + +int lynx_pccr_write(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 val) +{ + struct lynx_priv *priv = lane->priv; + struct lynx_pccr pccr; + u32 old, tmp, mask; + int err; + + err = priv->info->get_pccr(mode, lane->id, &pccr); + if (err) + return err; + + old = lynx_read(priv, pccr.offset); + mask = GENMASK(pccr.width - 1, 0) << pccr.shift; + tmp = (old & ~mask) | (val << pccr.shift); + lynx_write(priv, pccr.offset, tmp); + + dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n", + pccr.offset, old, tmp); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_pccr_write, "PHY_FSL_LYNX"); + +int lynx_pcvt_read(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 *val) +{ + struct lynx_priv *priv = lane->priv; + int offset; + + offset = priv->info->get_pcvt_offset(lane->id, mode); + if (offset < 0) + return offset; + + *val = lynx_read(priv, offset + cr); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_pcvt_read, "PHY_FSL_LYNX"); + +int lynx_pcvt_write(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 val) +{ + struct lynx_priv *priv = lane->priv; + int offset; + + offset = priv->info->get_pcvt_offset(lane->id, mode); + if (offset < 0) + return offset; + + lynx_write(priv, offset + cr, val); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_pcvt_write, "PHY_FSL_LYNX"); + +int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 val, u32 mask) +{ + int err; + u32 tmp; + + err = lynx_pcvt_read(lane, mode, cr, &tmp); + if (err) + return err; + + tmp &= ~mask; + tmp |= val; + + return lynx_pcvt_write(lane, mode, cr, tmp); +} +EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index b726ff21972b..5cd86c9543cb 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -4,6 +4,7 @@ #ifndef _PHY_FSL_LYNX_CORE_H #define _PHY_FSL_LYNX_CORE_H +#include #include #include @@ -37,6 +38,9 @@ struct lynx_lane { }; struct lynx_info { + int (*get_pccr)(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr); + int (*get_pcvt_offset)(int lane, enum lynx_lane_mode mode); bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); int first_lane; int num_lanes; @@ -87,4 +91,13 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode); struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode); +int lynx_pccr_read(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 *val); +int lynx_pccr_write(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 val); +int lynx_pcvt_read(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 *val); +int lynx_pcvt_write(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 val); +int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, + u32 val, u32 mask); + #endif From 09697afa157df0a420fb6b107a71bb45dd32aabf Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:43 +0300 Subject: [PATCH 3379/5214] phy: lynx-28g: provide default lynx_lane_supports_mode() implementation For the 28G Lynx, there are situations where a protocol is not supported on a lane despite there being a PCCR register and protocol converter available: - LX2160A SerDes 1: reference manual documents PCCD fields E25GC_CFG and E25GD_CFG and protocol converter registers E25GCCR1..E25GCCR3 / E25GDCR1..E25GDCR3, but nonetheless, Table 289. SerDes 1 protocol mapping shows no RCW[SRDS_PRTCL_S1] value for which lanes C and D support 25G - when using the "fsl,lynx-28g" fallback compatible string, we don't want to offer 25GbE because we don't know if the lane supports it, even though we know how to reach the PCCR and protocol converter registers for it. But for the upcoming 10G Lynx SerDes, the above situations don't exist. There, if we know how to reach the PCCR and protocol converter registers on a lane, we implicitly know that the protocol is supported there, so implementing priv->info->lane_supports_mode() would be redundant. Implement lynx_lane_supports_mode_default() which decides whether a lane mode is supported just based on priv->info->get_pccr() and priv->info->get_pcvt_offset(). Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-8-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-core.c | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index f49d594622cb..802e32dc6dca 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -40,6 +40,27 @@ enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) } EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX"); +/* By default, assume that if we know how to get the PCCR register and + * protocol converter for a lane, that protocol is supported. + */ +static bool lynx_lane_supports_mode_default(struct lynx_lane *lane, + enum lynx_lane_mode mode) +{ + struct lynx_priv *priv = lane->priv; + struct lynx_pccr pccr; + + if (!priv->info->get_pccr || !priv->info->get_pcvt_offset) + return false; + + if (priv->info->get_pccr(mode, lane->id, &pccr) < 0) + return false; + + if (priv->info->get_pcvt_offset(lane->id, mode) < 0) + return false; + + return true; +} + /* A lane mode is supported if we have a PLL that can provide its required * clock net, and if there is a protocol converter for that mode on that lane. */ @@ -48,8 +69,12 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) struct lynx_priv *priv = lane->priv; int i; - if (!priv->info->lane_supports_mode(lane->id, mode)) + if (priv->info->lane_supports_mode) { + if (!priv->info->lane_supports_mode(lane->id, mode)) + return false; + } else if (!lynx_lane_supports_mode_default(lane, mode)) { return false; + } for (i = 0; i < LYNX_NUM_PLL; i++) { if (!priv->pll[i].enabled) From 9515c35eaac9fc51717c8dbb5d18ec5a923b8342 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:44 +0300 Subject: [PATCH 3380/5214] phy: lynx-28g: move struct lynx_info definitions downwards We need to be able to reference more function pointers in upcoming patches. The struct lynx_info definitions are currently placed a bit up in lynx-28g.c in order to be able to do that without function prototype forward declarations, so move them downward to avoid that situation. No functional change intended. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-9-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 86 ++++++++++++------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 1de663283faf..a3fbd31d4dbf 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -782,49 +782,6 @@ static bool lynx_28g_compat_lane_supports_mode(int lane, } } -static const struct lynx_info lynx_info_compat = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lynx_28g_compat_lane_supports_mode, - .num_lanes = LYNX_28G_NUM_LANE, -}; - -static const struct lynx_info lynx_info_lx2160a_serdes1 = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, - .num_lanes = LYNX_28G_NUM_LANE, -}; - -static const struct lynx_info lynx_info_lx2160a_serdes2 = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, - .num_lanes = LYNX_28G_NUM_LANE, -}; - -static const struct lynx_info lynx_info_lx2160a_serdes3 = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, - .num_lanes = LYNX_28G_NUM_LANE, -}; - -static const struct lynx_info lynx_info_lx2162a_serdes1 = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, - .first_lane = 4, - .num_lanes = LYNX_28G_NUM_LANE, -}; - -static const struct lynx_info lynx_info_lx2162a_serdes2 = { - .get_pccr = lynx_28g_get_pccr, - .get_pcvt_offset = lynx_28g_get_pcvt_offset, - .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, - .num_lanes = LYNX_28G_NUM_LANE, -}; - static void lynx_28g_lane_remap_pll(struct lynx_lane *lane, enum lynx_lane_mode lane_mode) { @@ -1248,6 +1205,49 @@ static int lynx_28g_probe_lane(struct lynx_28g_priv *priv, int id, return 0; } +static const struct lynx_info lynx_info_compat = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lynx_28g_compat_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes1 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes2 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes3 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes1 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, + .first_lane = 4, + .num_lanes = LYNX_28G_NUM_LANE, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes2 = { + .get_pccr = lynx_28g_get_pccr, + .get_pcvt_offset = lynx_28g_get_pcvt_offset, + .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, + .num_lanes = LYNX_28G_NUM_LANE, +}; + static int lynx_28g_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; From b1c4d866edb7aa62048f5e373586ac8580ef0ec8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:45 +0300 Subject: [PATCH 3381/5214] phy: lynx-28g: make lynx_28g_pll_read_configuration() callable per PLL In a future change, lynx_28g_pll_read_configuration() and lynx_28g_lane_read_configuration() will be made methods of struct lynx_info. There is no functional reason, but lynx_28g_lane_read_configuration() is called per lane and lynx_28g_pll_read_configuration() iterates over PLLs internally. So the API exported by the lynx_info structure would not be uniform. Change lynx_28g_pll_read_configuration() to also permit reading the PLL configuration individually, and move the for loop at the call site. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-10-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 73 ++++++++++++------------ 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index a3fbd31d4dbf..dbbadaa08cb6 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -272,7 +272,6 @@ #define lynx_28g_lane_rmw lynx_lane_rmw #define lynx_28g_lane_read lynx_lane_read #define lynx_28g_lane_write lynx_lane_write -#define lynx_28g_pll_read lynx_pll_read #define lynx_28g_priv lynx_priv #define lynx_28g_lane lynx_lane @@ -1051,49 +1050,41 @@ static const struct phy_ops lynx_28g_ops = { .owner = THIS_MODULE, }; -static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) +static void lynx_28g_pll_read_configuration(struct lynx_pll *pll) { - struct lynx_28g_pll *pll; u32 val; - int i; - for (i = 0; i < LYNX_28G_NUM_PLL; i++) { - pll = &priv->pll[i]; - pll->priv = priv; - pll->id = i; + val = lynx_pll_read(pll, PLLnRSTCTL); + pll->enabled = !(val & PLLnRSTCTL_DIS); + pll->locked = !!(val & PLLnRSTCTL_LOCK); - val = lynx_28g_pll_read(pll, PLLnRSTCTL); - pll->enabled = !(val & PLLnRSTCTL_DIS); - pll->locked = !!(val & PLLnRSTCTL_LOCK); + val = lynx_pll_read(pll, PLLnCR0); + pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val); - val = lynx_28g_pll_read(pll, PLLnCR0); - pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val); + val = lynx_pll_read(pll, PLLnCR1); + pll->frate_sel = FIELD_GET(PLLnCR1_FRATE_SEL, val); - val = lynx_28g_pll_read(pll, PLLnCR1); - pll->frate_sel = FIELD_GET(PLLnCR1_FRATE_SEL, val); + if (!pll->enabled) + return; - if (!pll->enabled) - continue; - - switch (pll->frate_sel) { - case PLLnCR1_FRATE_5G_10GVCO: - case PLLnCR1_FRATE_5G_25GVCO: - /* 5GHz clock net */ - __set_bit(LANE_MODE_1000BASEX_SGMII, pll->supported); - break; - case PLLnCR1_FRATE_10G_20GVCO: - /* 10.3125GHz clock net */ - __set_bit(LANE_MODE_10GBASER, pll->supported); - __set_bit(LANE_MODE_USXGMII, pll->supported); - break; - case PLLnCR1_FRATE_12G_25GVCO: - /* 12.890625GHz clock net */ - __set_bit(LANE_MODE_25GBASER, pll->supported); - break; - default: - /* 6GHz, 8GHz */ - break; - } + switch (pll->frate_sel) { + case PLLnCR1_FRATE_5G_10GVCO: + case PLLnCR1_FRATE_5G_25GVCO: + /* 5GHz clock net */ + __set_bit(LANE_MODE_1000BASEX_SGMII, pll->supported); + break; + case PLLnCR1_FRATE_10G_20GVCO: + /* 10.3125GHz clock net */ + __set_bit(LANE_MODE_10GBASER, pll->supported); + __set_bit(LANE_MODE_USXGMII, pll->supported); + break; + case PLLnCR1_FRATE_12G_25GVCO: + /* 12.890625GHz clock net */ + __set_bit(LANE_MODE_25GBASER, pll->supported); + break; + default: + /* 6GHz, 8GHz */ + break; } } @@ -1291,7 +1282,13 @@ static int lynx_28g_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - lynx_28g_pll_read_configuration(priv); + for (int i = 0; i < LYNX_28G_NUM_PLL; i++) { + struct lynx_28g_pll *pll = &priv->pll[i]; + + pll->priv = priv; + pll->id = i; + lynx_28g_pll_read_configuration(pll); + } if (of_get_child_count(dn)) { struct device_node *child; From 719a2da392349db052532db89637622a71d14d49 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:46 +0300 Subject: [PATCH 3382/5214] phy: lynx-28g: common probe() and remove() Factor the device-agnostic logic from lynx_28g_probe() and lynx_28g_remove() into lynx_probe() and lynx_remove() inside phy-fsl-lynx-core.c. These will be shared with the 10G Lynx driver. Since the PLL configuration, lane configuration and CDR lock detection procedure are going to be different, introduce lynx_info function pointers so that this code remains in the 28G Lynx driver. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-11-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 225 +++++----------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 170 ++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 12 +- 3 files changed, 227 insertions(+), 180 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index dbbadaa08cb6..50b991870edb 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -12,7 +12,6 @@ #include "phy-fsl-lynx-core.h" #define LYNX_28G_NUM_LANE 8 -#define LYNX_28G_NUM_PLL LYNX_NUM_PLL /* SoC IP wrapper for protocol converters */ #define PCC8 0x10a0 @@ -781,6 +780,30 @@ static bool lynx_28g_compat_lane_supports_mode(int lane, } } +static void lynx_28g_cdr_lock_check(struct lynx_lane *lane) +{ + u32 rrstctl; + int err; + + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + if (!!(rrstctl & LNaRRSTCTL_CDR_LOCK)) + return; + + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ, + LNaRRSTCTL_RST_REQ); + + err = read_poll_timeout(lynx_28g_lane_read, rrstctl, + !!(rrstctl & LNaRRSTCTL_RST_DONE), + LYNX_28G_LANE_RESET_SLEEP_US, + LYNX_28G_LANE_RESET_TIMEOUT_US, + false, lane, LNaRRSTCTL); + if (err) { + dev_warn_once(&lane->phy->dev, + "Lane %c receiver reset failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } +} + static void lynx_28g_lane_remap_pll(struct lynx_lane *lane, enum lynx_lane_mode lane_mode) { @@ -1088,50 +1111,6 @@ static void lynx_28g_pll_read_configuration(struct lynx_pll *pll) } } -#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work) - -static void lynx_28g_cdr_lock_check(struct work_struct *work) -{ - struct lynx_28g_priv *priv = work_to_lynx(work); - struct lynx_28g_lane *lane; - u32 rrstctl; - int err, i; - - for (i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { - lane = &priv->lane[i]; - if (!lane->phy) - continue; - - mutex_lock(&lane->phy->mutex); - - if (!lane->init || !lane->powered_up) { - mutex_unlock(&lane->phy->mutex); - continue; - } - - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - if (!(rrstctl & LNaRRSTCTL_CDR_LOCK)) { - lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ, - LNaRRSTCTL_RST_REQ); - - err = read_poll_timeout(lynx_28g_lane_read, rrstctl, - !!(rrstctl & LNaRRSTCTL_RST_DONE), - LYNX_28G_LANE_RESET_SLEEP_US, - LYNX_28G_LANE_RESET_TIMEOUT_US, - false, lane, LNaRRSTCTL); - if (err) { - dev_warn_once(&lane->phy->dev, - "Lane %c receiver reset failed: %pe\n", - 'A' + lane->id, ERR_PTR(err)); - } - } - - mutex_unlock(&lane->phy->mutex); - } - queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, - msecs_to_jiffies(1000)); -} - static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) { u32 pccr, pss, protocol; @@ -1157,49 +1136,13 @@ static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) } } -static struct phy *lynx_28g_xlate(struct device *dev, - const struct of_phandle_args *args) -{ - struct lynx_28g_priv *priv = dev_get_drvdata(dev); - int idx; - - if (args->args_count == 0) - return of_phy_simple_xlate(dev, args); - else if (args->args_count != 1) - return ERR_PTR(-ENODEV); - - idx = args->args[0]; - - if (WARN_ON(idx >= LYNX_28G_NUM_LANE || - idx < priv->info->first_lane)) - return ERR_PTR(-EINVAL); - - return priv->lane[idx].phy ?: ERR_PTR(-ENODEV); -} - -static int lynx_28g_probe_lane(struct lynx_28g_priv *priv, int id, - struct device_node *dn) -{ - struct lynx_28g_lane *lane = &priv->lane[id]; - struct phy *phy; - - phy = devm_phy_create(priv->dev, dn, &lynx_28g_ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); - - lane->priv = priv; - lane->phy = phy; - lane->id = id; - phy_set_drvdata(phy, lane); - lynx_28g_lane_read_configuration(lane); - - return 0; -} - static const struct lynx_info lynx_info_compat = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lynx_28g_compat_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .num_lanes = LYNX_28G_NUM_LANE, }; @@ -1207,6 +1150,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes1 = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .num_lanes = LYNX_28G_NUM_LANE, }; @@ -1214,6 +1160,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes2 = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .num_lanes = LYNX_28G_NUM_LANE, }; @@ -1221,6 +1170,9 @@ static const struct lynx_info lynx_info_lx2160a_serdes3 = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .num_lanes = LYNX_28G_NUM_LANE, }; @@ -1228,6 +1180,9 @@ static const struct lynx_info lynx_info_lx2162a_serdes1 = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .first_lane = 4, .num_lanes = LYNX_28G_NUM_LANE, }; @@ -1236,112 +1191,26 @@ static const struct lynx_info lynx_info_lx2162a_serdes2 = { .get_pccr = lynx_28g_get_pccr, .get_pcvt_offset = lynx_28g_get_pcvt_offset, .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, + .pll_read_configuration = lynx_28g_pll_read_configuration, + .lane_read_configuration = lynx_28g_lane_read_configuration, + .cdr_lock_check = lynx_28g_cdr_lock_check, .num_lanes = LYNX_28G_NUM_LANE, }; static int lynx_28g_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct phy_provider *provider; - struct lynx_28g_priv *priv; - struct device_node *dn; - int err; - - dn = dev_of_node(dev); - if (!dn) { - dev_err(dev, "Device requires an OF node\n"); - return -EINVAL; - } - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->dev = dev; - priv->info = of_device_get_match_data(dev); - if (!priv->info) - return -ENODEV; - - dev_set_drvdata(dev, priv); - spin_lock_init(&priv->pcc_lock); - INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + const struct lynx_info *info; /* * If we get here it means we probed on a device tree where * "fsl,lynx-28g" wasn't the fallback, but the sole compatible string. */ - if (priv->info == &lynx_info_compat) + info = of_device_get_match_data(dev); + if (info == &lynx_info_compat) dev_warn(dev, "Please update device tree to use per-device compatible strings\n"); - priv->lane = devm_kcalloc(dev, priv->info->num_lanes, - sizeof(*priv->lane), GFP_KERNEL); - if (!priv->lane) - return -ENOMEM; - - priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - for (int i = 0; i < LYNX_28G_NUM_PLL; i++) { - struct lynx_28g_pll *pll = &priv->pll[i]; - - pll->priv = priv; - pll->id = i; - lynx_28g_pll_read_configuration(pll); - } - - if (of_get_child_count(dn)) { - struct device_node *child; - - for_each_available_child_of_node(dn, child) { - u32 reg; - - /* PHY subnode name must be 'phy'. */ - if (!(of_node_name_eq(child, "phy"))) - continue; - - if (of_property_read_u32(child, "reg", ®)) { - dev_err(dev, "No \"reg\" property for %pOF\n", child); - of_node_put(child); - return -EINVAL; - } - - if (reg < priv->info->first_lane || reg >= LYNX_28G_NUM_LANE) { - dev_err(dev, "\"reg\" property out of range for %pOF\n", child); - of_node_put(child); - return -EINVAL; - } - - err = lynx_28g_probe_lane(priv, reg, child); - if (err) { - of_node_put(child); - return err; - } - } - } else { - for (int i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { - err = lynx_28g_probe_lane(priv, i, NULL); - if (err) - return err; - } - } - - provider = devm_of_phy_provider_register(dev, lynx_28g_xlate); - if (IS_ERR(provider)) - return PTR_ERR(provider); - - queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, - msecs_to_jiffies(1000)); - - return 0; -} - -static void lynx_28g_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct lynx_28g_priv *priv = dev_get_drvdata(dev); - - cancel_delayed_work_sync(&priv->cdr_check); + return lynx_probe(pdev, info, &lynx_28g_ops); } static const struct of_device_id lynx_28g_of_match_table[] = { @@ -1357,7 +1226,7 @@ MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table); static struct platform_driver lynx_28g_driver = { .probe = lynx_28g_probe, - .remove = lynx_28g_remove, + .remove = lynx_remove, .driver = { .name = "lynx-28g", .of_match_table = lynx_28g_of_match_table, diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 802e32dc6dca..3fb89bb4b0d6 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -2,6 +2,7 @@ /* Copyright 2025-2026 NXP */ #include +#include #include "phy-fsl-lynx-core.h" @@ -202,5 +203,174 @@ int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, } EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX"); +#define work_to_lynx(w) container_of((w), struct lynx_priv, cdr_check.work) + +static void lynx_cdr_lock_check(struct work_struct *work) +{ + struct lynx_priv *priv = work_to_lynx(work); + struct lynx_lane *lane; + + for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) { + lane = &priv->lane[i]; + if (!lane->phy) + continue; + + mutex_lock(&lane->phy->mutex); + + if (!lane->init || !lane->powered_up) { + mutex_unlock(&lane->phy->mutex); + continue; + } + + priv->info->cdr_lock_check(lane); + + mutex_unlock(&lane->phy->mutex); + } + + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); +} + +static struct phy *lynx_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct lynx_priv *priv = dev_get_drvdata(dev); + int idx; + + if (args->args_count == 0) + return of_phy_simple_xlate(dev, args); + else if (args->args_count != 1) + return ERR_PTR(-ENODEV); + + idx = args->args[0]; + + if (WARN_ON(idx >= priv->info->num_lanes || + idx < priv->info->first_lane)) + return ERR_PTR(-EINVAL); + + return priv->lane[idx].phy ?: ERR_PTR(-ENODEV); +} + +static int lynx_probe_lane(struct lynx_priv *priv, int id, + struct device_node *dn, + const struct phy_ops *phy_ops) +{ + struct lynx_lane *lane = &priv->lane[id]; + struct phy *phy; + + phy = devm_phy_create(priv->dev, dn, phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + lane->priv = priv; + lane->phy = phy; + lane->id = id; + phy_set_drvdata(phy, lane); + priv->info->lane_read_configuration(lane); + + return 0; +} + +int lynx_probe(struct platform_device *pdev, const struct lynx_info *info, + const struct phy_ops *phy_ops) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct device_node *dn; + struct lynx_priv *priv; + int err; + + dn = dev_of_node(dev); + if (!dn) { + dev_err(dev, "Device requires an OF node\n"); + return -EINVAL; + } + + if (!info) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->info = info; + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->pcc_lock); + INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check); + + priv->lane = devm_kcalloc(dev, priv->info->num_lanes, + sizeof(*priv->lane), GFP_KERNEL); + if (!priv->lane) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + for (int i = 0; i < LYNX_NUM_PLL; i++) { + struct lynx_pll *pll = &priv->pll[i]; + + pll->priv = priv; + pll->id = i; + priv->info->pll_read_configuration(pll); + } + + if (of_get_child_count(dn)) { + struct device_node *child; + + for_each_available_child_of_node(dn, child) { + u32 reg; + + /* PHY subnode name must be 'phy'. */ + if (!(of_node_name_eq(child, "phy"))) + continue; + + if (of_property_read_u32(child, "reg", ®)) { + dev_err(dev, "No \"reg\" property for %pOF\n", child); + of_node_put(child); + return -EINVAL; + } + + if (reg < priv->info->first_lane || reg >= priv->info->num_lanes) { + dev_err(dev, "\"reg\" property out of range for %pOF\n", child); + of_node_put(child); + return -EINVAL; + } + + err = lynx_probe_lane(priv, reg, child, phy_ops); + if (err) { + of_node_put(child); + return err; + } + } + } else { + for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) { + err = lynx_probe_lane(priv, i, NULL, phy_ops); + if (err) + return err; + } + } + + provider = devm_of_phy_provider_register(dev, lynx_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, + msecs_to_jiffies(1000)); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_probe, "PHY_FSL_LYNX"); + +void lynx_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lynx_priv *priv = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&priv->cdr_check); +} +EXPORT_SYMBOL_NS_GPL(lynx_remove, "PHY_FSL_LYNX"); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index 5cd86c9543cb..e8b280cc9b38 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -10,14 +10,15 @@ #define LYNX_NUM_PLL 2 +struct lynx_priv; +struct lynx_lane; + struct lynx_pccr { int offset; int width; int shift; }; -struct lynx_priv; - struct lynx_pll { struct lynx_priv *priv; int id; @@ -42,6 +43,9 @@ struct lynx_info { struct lynx_pccr *pccr); int (*get_pcvt_offset)(int lane, enum lynx_lane_mode mode); bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); + void (*pll_read_configuration)(struct lynx_pll *pll); + void (*lane_read_configuration)(struct lynx_lane *lane); + void (*cdr_lock_check)(struct lynx_lane *lane); int first_lane; int num_lanes; }; @@ -85,6 +89,10 @@ static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val, #define lynx_pll_read(pll, reg) \ ioread32((pll)->priv->base + reg((pll)->id)) +int lynx_probe(struct platform_device *pdev, const struct lynx_info *info, + const struct phy_ops *phy_ops); +void lynx_remove(struct platform_device *pdev); + const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode); enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf); bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode); From c6c1d7dfd59b181b96b0909b63e140b0aa61ac59 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:47 +0300 Subject: [PATCH 3383/5214] phy: lynx-28g: add support for big endian register maps Some 10G Lynx SerDes blocks are big endian and require byte swapping because the CPUs are little endian armv8 (LS1046A). Parse the "big-endian" device tree property, and modify the base lynx_read() and lynx_write() accessors to test this property before issuing either the ioread32() or ioread32be() variants (as per Documentation/driver-api/device-io.rst). All other accessors - lynx_rmw(), lynx_lane_read(), lynx_lane_write(), lynx_lane_rmw(), lynx_pll_read() - need to go through these endian-aware helpers. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-12-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-core.c | 1 + drivers/phy/freescale/phy-fsl-lynx-core.h | 36 ++++++++++++++++------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 3fb89bb4b0d6..226b2af2b599 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -295,6 +295,7 @@ int lynx_probe(struct platform_device *pdev, const struct lynx_info *info, priv->dev = dev; priv->info = info; + priv->big_endian = device_property_read_bool(dev, "big-endian"); dev_set_drvdata(dev, priv); spin_lock_init(&priv->pcc_lock); INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index e8b280cc9b38..d82e529fa65a 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -58,36 +58,52 @@ struct lynx_priv { * like PCCn */ spinlock_t pcc_lock; + bool big_endian; struct lynx_pll pll[LYNX_NUM_PLL]; struct lynx_lane *lane; struct delayed_work cdr_check; }; +static inline u32 lynx_read(struct lynx_priv *priv, unsigned long off) +{ + void __iomem *reg = priv->base + off; + + if (priv->big_endian) + return ioread32be(reg); + + return ioread32(reg); +} + +static inline void lynx_write(struct lynx_priv *priv, unsigned long off, u32 val) +{ + void __iomem *reg = priv->base + off; + + if (priv->big_endian) + return iowrite32be(val, reg); + + return iowrite32(val, reg); +} + static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val, u32 mask) { - void __iomem *reg = priv->base + off; u32 orig, tmp; - orig = ioread32(reg); + orig = lynx_read(priv, off); tmp = orig & ~mask; tmp |= val; - iowrite32(tmp, reg); + lynx_write(priv, off, tmp); } -#define lynx_read(priv, off) \ - ioread32((priv)->base + (off)) -#define lynx_write(priv, off, val) \ - iowrite32(val, (priv)->base + (off)) #define lynx_lane_rmw(lane, reg, val, mask) \ lynx_rmw((lane)->priv, reg(lane->id), val, mask) #define lynx_lane_read(lane, reg) \ - ioread32((lane)->priv->base + reg((lane)->id)) + lynx_read((lane)->priv, reg((lane)->id)) #define lynx_lane_write(lane, reg, val) \ - iowrite32(val, (lane)->priv->base + reg((lane)->id)) + lynx_write((lane)->priv, reg((lane)->id), val) #define lynx_pll_read(pll, reg) \ - ioread32((pll)->priv->base + reg((pll)->id)) + lynx_read((pll)->priv, reg((pll)->id)) int lynx_probe(struct platform_device *pdev, const struct lynx_info *info, const struct phy_ops *phy_ops); From f64ef1995dd6f902da0bd3bc60c72c825098b652 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:48 +0300 Subject: [PATCH 3384/5214] phy: lynx-28g: optimize read-modify-write operation It is unnecessary to rewrite a register if the masked field already contains the desired value upon reading. The hardware behaviour does not depend upon register writes with identical values. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-13-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index d82e529fa65a..3d9508dfb2c1 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -93,7 +93,8 @@ static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val, orig = lynx_read(priv, off); tmp = orig & ~mask; tmp |= val; - lynx_write(priv, off, tmp); + if (orig != tmp) + lynx_write(priv, off, tmp); } #define lynx_lane_rmw(lane, reg, val, mask) \ From f124b54b19223c85dabd9421ec622d8256e240de Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:49 +0300 Subject: [PATCH 3385/5214] phy: lynx-28g: improve phy_validate() procedure lynx_28g_validate() suffers from the following shortcomings: - Changing the protocol should not be possible if the source protocol of the lane is unsupported. This is because lynx_28g_proto_conf[] only covers the register deltas between any pair of supported lane modes, but that delta is probably incomplete if the source protocol is, say, PCIe (which is currently assimilated by the driver to LANE_MODE_UNKNOWN). lynx_28g_proto_conf() does refuse changing the protocol if the current one is unsupported, but we shouldn't advertise it via phy_validate() at all. The phy_set_mode_ext() call should perform the exact same verifications as phy_validate() did, in case the caller bypassed phy_validate(). So we need to centralize the logic into a common validation. But lynx_28g_set_mode() later needs the lane_mode that this validation needs to compute anyway, so name the common helper lynx_phy_mode_to_lane_mode() and let it return that lane_mode. - Future core sanity checks on phy_validate() will want to differentiate the case where this optional method is not implemented from the case where the mode/submode is really not supported. So we shouldn't return -EOPNOTSUPP from lynx_28g_validate(), but -EINVAL to signal that we do implement the operation: https://lore.kernel.org/linux-phy/aY2lFTIALH7qEJmM@shell.armlinux.org.uk/ Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-14-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 38 +++++++---------------- drivers/phy/freescale/phy-fsl-lynx-core.c | 30 ++++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.h | 2 ++ 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 50b991870edb..38afcd081a2a 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -968,22 +968,22 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, return err; } +static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + return lynx_phy_mode_to_lane_mode(phy, mode, submode, NULL); +} + static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) { - struct lynx_28g_lane *lane = phy_get_drvdata(phy); + struct lynx_lane *lane = phy_get_drvdata(phy); int powered_up = lane->powered_up; enum lynx_lane_mode lane_mode; - int err = 0; + int err; - if (mode != PHY_MODE_ETHERNET) - return -EOPNOTSUPP; - - if (lane->mode == LANE_MODE_UNKNOWN) - return -EOPNOTSUPP; - - lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_lane_supports_mode(lane, lane_mode)) - return -EOPNOTSUPP; + err = lynx_phy_mode_to_lane_mode(phy, mode, submode, &lane_mode); + if (err) + return err; if (lane_mode == lane->mode) return 0; @@ -1014,22 +1014,6 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) return err; } -static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, - union phy_configure_opts *opts __always_unused) -{ - struct lynx_28g_lane *lane = phy_get_drvdata(phy); - enum lynx_lane_mode lane_mode; - - if (mode != PHY_MODE_ETHERNET) - return -EOPNOTSUPP; - - lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_lane_supports_mode(lane, lane_mode)) - return -EOPNOTSUPP; - - return 0; -} - static int lynx_28g_init(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 226b2af2b599..1e411bfab404 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -89,6 +89,36 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) } EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX"); +/* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a + * lane_mode and return 0 if it is supported and we can transition to it from + * the current lane mode, or return negative error otherwise. + */ +int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode, + int submode, enum lynx_lane_mode *lane_mode) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + enum lynx_lane_mode tmp_lane_mode; + + /* The protocol configuration tables are incomplete for full lane + * reconfiguration from an arbitrary protocol. + */ + if (lane->mode == LANE_MODE_UNKNOWN) + return -EINVAL; + + if (mode != PHY_MODE_ETHERNET) + return -EINVAL; + + tmp_lane_mode = phy_interface_to_lane_mode(submode); + if (!lynx_lane_supports_mode(lane, tmp_lane_mode)) + return -EINVAL; + + if (lane_mode) + *lane_mode = tmp_lane_mode; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lynx_phy_mode_to_lane_mode, "PHY_FSL_LYNX"); + struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode) { struct lynx_pll *pll; diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index 3d9508dfb2c1..37fa4b544faa 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -113,6 +113,8 @@ void lynx_remove(struct platform_device *pdev); const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode); enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf); bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode); +int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode, + int submode, enum lynx_lane_mode *lane_mode); struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode); From a7dc4e95d31c6bc674b24697aacadf7c6f1cbe6a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:50 +0300 Subject: [PATCH 3386/5214] dt-bindings: phy: lynx-10g: initial document Add a schema for the 10G Lynx SerDes. This is very similar to the modern form of the 28G Lynx SerDes, which is very much the intention. There is intentionally no generic fsl,lynx-10g compatible string due to the hardware inability to report its capabilities, despite having a common register map. We allow both forms of #phy-cells = <1> in the top-level provider and #phy-cells = <0> in the per-lane provider for more flexibility to consumers, and because the kernel code is shared with the 28G Lynx which already has that support for compatibility reasons. Signed-off-by: Vladimir Oltean Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260610151952.2141019-15-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml new file mode 100644 index 000000000000..2e7d0abfa71a --- /dev/null +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Lynx 10G SerDes PHY + +maintainers: + - Vladimir Oltean + +description: + The 10G Lynx is a multi-protocol SerDes block which handles networking, PCIe, + SATA and other high-speed interfaces. It is present on most QorIQ and + Layerscape SoCs. The register map is common, but the integration is + SoC-specific, with the differences consisting in register endianness, the + number of lanes, protocol converters available per lane and their location in + the PCCR registers. Some SoCs have multiple SerDes blocks and those differ in + their protocol capabilities per lane. + +properties: + compatible: + enum: + - fsl,ls1028a-serdes + - fsl,ls1046a-serdes1 + - fsl,ls1046a-serdes2 + - fsl,ls1088a-serdes1 + - fsl,ls1088a-serdes2 + - fsl,ls2088a-serdes1 + - fsl,ls2088a-serdes2 + + reg: + maxItems: 1 + + big-endian: true + + "#phy-cells": + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^phy@[0-7]$": + type: object + description: SerDes lane (single RX/TX differential pair) + + properties: + reg: + minimum: 0 + maximum: 7 + description: Lane index as seen in register map + + "#phy-cells": + const: 0 + + required: + - reg + - "#phy-cells" + + additionalProperties: false + +required: + - compatible + - reg + - "#phy-cells" + - "#address-cells" + - "#size-cells" + +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,ls1028a-serdes + - fsl,ls1046a-serdes1 + - fsl,ls1046a-serdes2 + - fsl,ls1088a-serdes1 + - fsl,ls1088a-serdes2 + then: + patternProperties: + "^phy@[0-7]$": + properties: + reg: + minimum: 0 + maximum: 3 + - if: + properties: + compatible: + enum: + - fsl,ls1046a-serdes1 + - fsl,ls1046a-serdes2 + then: + required: + - big-endian + else: + properties: + big-endian: false + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + serdes@1ea0000 { + compatible = "fsl,ls1028a-serdes"; + reg = <0x0 0x1ea0000 0x0 0xffff>; + #address-cells = <1>; + #size-cells = <0>; + #phy-cells = <1>; + + phy@0 { + reg = <0>; + #phy-cells = <0>; + }; + + phy@1 { + reg = <1>; + #phy-cells = <0>; + }; + + phy@2 { + reg = <2>; + #phy-cells = <0>; + }; + + phy@3 { + reg = <3>; + #phy-cells = <0>; + }; + }; + }; From 6dc92ba487a21b923eb409357ffe966a6e9d2343 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:51 +0300 Subject: [PATCH 3387/5214] phy: lynx-10g: new driver Introduce a driver for the networking lanes of the 10G Lynx SerDes block, present on the majority of Layerscape and QorIQ (Freescale/NXP) SoCs. As with the 28G Lynx, the SerDes lanes come pre-initialized out of reset and the consumers use them that way outside the Generic PHY framework (for networking, the static configuration remains for the entire SoC lifetime, whereas for SATA and PCIe, the hardware reconfigures itself automatically for other link speeds). The need for the Generic PHY framework comes specifically for networking use cases where a static lane configuration is not sufficient. For example a network MAC is connected to an SFP cage, where various SFP or SFP+ modules can be connected. Each of them may require a different SerDes protocol (SGMII, 1000Base-X, 10GBase-R), which phylink + sfp-bus are responsible of figuring out. The phylink drivers are: - enetc - felix - dpaa_eth (fman_memac) - dpaa2-eth - dpaa2-switch and they all need to reconfigure the SerDes for the requested link mode, using phy_set_mode_ext() (and phy_validate() to see if it is supported in the first place). Note that SerDes 2 on LS1088A is exclusively non-networking, so there is currently no need for this driver. Therefore we skip matching on its compatible string and do not probe on that device. Co-developed-by: Ioana Ciornei Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-16-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/Kconfig | 10 + drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-fsl-lynx-10g.c | 1321 +++++++++++++++++++++ drivers/phy/freescale/phy-fsl-lynx-core.c | 38 + drivers/phy/freescale/phy-fsl-lynx-core.h | 4 + include/soc/fsl/phy-fsl-lynx.h | 27 + 6 files changed, 1401 insertions(+) create mode 100644 drivers/phy/freescale/phy-fsl-lynx-10g.c diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index ac575d531db7..5bf3864fbe64 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -54,6 +54,16 @@ endif config PHY_FSL_LYNX_CORE tristate +config PHY_FSL_LYNX_10G + tristate "Freescale Layerscape Lynx 10G SerDes PHY support" + depends on OF + depends on ARCH_LAYERSCAPE || COMPILE_TEST + select GENERIC_PHY + select PHY_FSL_LYNX_CORE + help + Enable this to add support for the Lynx 10G SerDes PHY as found on + NXP's Layerscape platform such as LS1088A or LS1028A. + config PHY_FSL_LYNX_28G tristate "Freescale Layerscape Lynx 28G SerDes PHY support" depends on OF diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index d7aa62cdeb39..5b0e180d6972 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o obj-$(CONFIG_PHY_FSL_IMX8QM_HSIO) += phy-fsl-imx8qm-hsio.o obj-$(CONFIG_PHY_FSL_LYNX_CORE) += phy-fsl-lynx-core.o +obj-$(CONFIG_PHY_FSL_LYNX_10G) += phy-fsl-lynx-10g.o obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c new file mode 100644 index 000000000000..38def160ef1a --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c @@ -0,0 +1,1321 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2021-2026 NXP */ + +#include +#include +#include +#include +#include +#include +#include + +#include "phy-fsl-lynx-core.h" + +/* SoC IP wrapper for protocol converters */ +#define PCCR8 0x220 +#define PCCR8_SGMIIa_KX BIT(3) +#define PCCR8_SGMIIa_CFG GENMASK(2, 0) + +#define PCCR9 0x224 +#define PCCR9_QSGMIIa_CFG GENMASK(2, 0) +#define PCCR9_QXGMIIa_CFG GENMASK(2, 0) + +#define PCCRB 0x22c +#define PCCRB_XFIa_CFG GENMASK(2, 0) +#define PCCRB_SXGMIIa_CFG GENMASK(2, 0) + +#define SGMII_CFG(id) (28 - (id) * 4) +#define QSGMII_CFG(id) (28 - (id) * 4) +#define SXGMII_CFG(id) (28 - (id) * 4) +#define QXGMII_CFG(id) (12 - (id) * 4) +#define XFI_CFG(id) (28 - (id) * 4) + +#define CR(x) ((x) * 4) + +#define A 0 +#define B 1 +#define C 2 +#define D 3 +#define E 4 +#define F 5 +#define G 6 +#define H 7 + +#define SGMIIaCR0(id) (0x1800 + (id) * 0x10) +#define QSGMIIaCR0(id) (0x1880 + (id) * 0x10) +#define XAUIaCR0(id) (0x1900 + (id) * 0x10) +#define XFIaCR0(id) (0x1980 + (id) * 0x10) +#define SXGMIIaCR0(id) (0x1a80 + (id) * 0x10) +#define QXGMIIaCR0(id) (0x1b00 + (id) * 0x20) + +#define SGMIIaCR0_RST_SGM BIT(31) +#define SGMIIaCR0_RST_SGM_OFF SGMIIaCR0_RST_SGM +#define SGMIIaCR0_RST_SGM_ON 0 +#define SGMIIaCR0_PD_SGM BIT(30) +#define SGMIIaCR1_SGPCS_EN BIT(11) +#define SGMIIaCR1_SGPCS_DIS 0x0 + +#define QSGMIIaCR0_RST_QSGM BIT(31) +#define QSGMIIaCR0_RST_QSGM_OFF QSGMIIaCR0_RST_QSGM +#define QSGMIIaCR0_RST_QSGM_ON 0 +#define QSGMIIaCR0_PD_QSGM BIT(30) + +/* Per PLL registers */ +#define PLLnCR0(pll) ((pll) * 0x20 + 0x4) + +#define PLLnCR0_POFF BIT(31) + +#define PLLnCR0_REFCLK_SEL GENMASK(30, 28) +#define PLLnCR0_REFCLK_SEL_100MHZ 0x0 +#define PLLnCR0_REFCLK_SEL_125MHZ 0x1 +#define PLLnCR0_REFCLK_SEL_156MHZ 0x2 +#define PLLnCR0_REFCLK_SEL_150MHZ 0x3 +#define PLLnCR0_REFCLK_SEL_161MHZ 0x4 +#define PLLnCR0_PLL_LCK BIT(23) +#define PLLnCR0_FRATE_SEL GENMASK(19, 16) +#define PLLnCR0_FRATE_5G 0x0 +#define PLLnCR0_FRATE_5_15625G 0x6 +#define PLLnCR0_FRATE_4G 0x7 +#define PLLnCR0_FRATE_3_125G 0x9 +#define PLLnCR0_FRATE_3G 0xa + +/* Per SerDes lane registers */ + +/* Lane a Protocol Select status register */ +#define LNaPSSR0(lane) (0x100 + (lane) * 0x20) +#define LNaPSSR0_TYPE GENMASK(30, 26) +#define LNaPSSR0_IS_QUAD GENMASK(25, 24) +#define LNaPSSR0_MAC GENMASK(19, 16) +#define LNaPSSR0_PCS GENMASK(10, 8) +#define LNaPSSR0_LANE GENMASK(2, 0) + +/* Lane a General Control Register */ +#define LNaGCR0(lane) (0x800 + (lane) * 0x40 + 0x0) +#define LNaGCR0_RPLL_PLLF BIT(31) +#define LNaGCR0_RPLL_PLLS 0x0 +#define LNaGCR0_RPLL_MSK BIT(31) +#define LNaGCR0_RRAT_SEL GENMASK(29, 28) +#define LNaGCR0_TRAT_SEL GENMASK(25, 24) +#define LNaGCR0_TPLL_PLLF BIT(27) +#define LNaGCR0_TPLL_PLLS 0x0 +#define LNaGCR0_TPLL_MSK BIT(27) +#define LNaGCR0_RRST_OFF LNaGCR0_RRST +#define LNaGCR0_TRST_OFF LNaGCR0_TRST +#define LNaGCR0_RRST_ON 0x0 +#define LNaGCR0_TRST_ON 0x0 +#define LNaGCR0_RRST BIT(22) +#define LNaGCR0_TRST BIT(21) +#define LNaGCR0_RX_PD BIT(20) +#define LNaGCR0_TX_PD BIT(19) +#define LNaGCR0_IF20BIT_EN BIT(18) +#define LNaGCR0_PROTS GENMASK(11, 7) + +#define LNaGCR1(lane) (0x800 + (lane) * 0x40 + 0x4) +#define LNaGCR1_RDAT_INV BIT(31) +#define LNaGCR1_TDAT_INV BIT(30) +#define LNaGCR1_OPAD_CTL BIT(26) +#define LNaGCR1_REIDL_TH GENMASK(22, 20) +#define LNaGCR1_REIDL_EX_SEL GENMASK(19, 18) +#define LNaGCR1_REIDL_ET_SEL GENMASK(17, 16) +#define LNaGCR1_REIDL_EX_MSB BIT(15) +#define LNaGCR1_REIDL_ET_MSB BIT(14) +#define LNaGCR1_REQ_CTL_SNP BIT(13) +#define LNaGCR1_REQ_CDR_SNP BIT(12) +#define LNaGCR1_TRSTDIR BIT(7) +#define LNaGCR1_REQ_BIN_SNP BIT(6) +#define LNaGCR1_ISLEW_RCTL GENMASK(5, 4) +#define LNaGCR1_OSLEW_RCTL GENMASK(1, 0) + +#define LNaRECR0(lane) (0x800 + (lane) * 0x40 + 0x10) +#define LNaRECR0_RXEQ_BST BIT(28) +#define LNaRECR0_GK2OVD GENMASK(27, 24) +#define LNaRECR0_GK3OVD GENMASK(19, 16) +#define LNaRECR0_GK2OVD_EN BIT(15) +#define LNaRECR0_GK3OVD_EN BIT(14) +#define LNaRECR0_OSETOVD_EN BIT(13) +#define LNaRECR0_BASE_WAND GENMASK(11, 10) +#define LNaRECR0_OSETOVD GENMASK(6, 0) + +#define LNaTECR0(lane) (0x800 + (lane) * 0x40 + 0x18) +#define LNaTECR0_TEQ_TYPE GENMASK(29, 28) +#define LNaTECR0_SGN_PREQ BIT(26) +#define LNaTECR0_RATIO_PREQ GENMASK(25, 22) +#define LNaTECR0_SGN_POST1Q BIT(21) +#define LNaTECR0_RATIO_PST1Q GENMASK(20, 16) +#define LNaTECR0_ADPT_EQ GENMASK(13, 8) +#define LNaTECR0_AMP_RED GENMASK(5, 0) + +#define LNaTTLCR0(lane) (0x800 + (lane) * 0x40 + 0x20) +#define LNaTTLCR1(lane) (0x800 + (lane) * 0x40 + 0x24) +#define LNaTTLCR2(lane) (0x800 + (lane) * 0x40 + 0x28) + +#define LNaTCSR3(lane) (0x800 + (lane) * 0x40 + 0x3C) +#define LNaTCSR3_CDR_LCK BIT(27) + +enum lynx_10g_rat_sel { + RAT_SEL_FULL = 0x0, + RAT_SEL_HALF = 0x1, + RAT_SEL_QUARTER = 0x2, + RAT_SEL_DOUBLE = 0x3, +}; + +enum lynx_10g_eq_type { + EQ_TYPE_NO_EQ = 0, + EQ_TYPE_2TAP = 1, + EQ_TYPE_3TAP = 2, +}; + +enum lynx_10g_proto_sel { + PROTO_SEL_PCIE = 0, + PROTO_SEL_SGMII_BASEX_KX_QSGMII = 1, + PROTO_SEL_SATA = 2, + PROTO_SEL_XAUI = 4, + PROTO_SEL_XFI_10GBASER_KR_SXGMII = 0xa, +}; + +struct lynx_10g_proto_conf { + int proto_sel; + int if20bit_en; + int reidl_th; + int reidl_et_msb; + int reidl_et_sel; + int reidl_ex_msb; + int reidl_ex_sel; + int islew_rctl; + int oslew_rctl; + int rxeq_bst; + int gk2ovd; + int gk3ovd; + int gk2ovd_en; + int gk3ovd_en; + int base_wand; + int teq_type; + int sgn_preq; + int ratio_preq; + int sgn_post1q; + int ratio_post1q; + int adpt_eq; + int amp_red; + int ttlcr0; +}; + +static const struct lynx_10g_proto_conf lynx_10g_proto_conf[LANE_MODE_MAX] = { + [LANE_MODE_1000BASEX_SGMII] = { + .proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII, + .reidl_th = 1, + .reidl_ex_sel = 3, + .reidl_et_msb = 1, + .islew_rctl = 1, + .oslew_rctl = 1, + .gk2ovd = 15, + .gk3ovd = 15, + .gk2ovd_en = 1, + .gk3ovd_en = 1, + .teq_type = EQ_TYPE_NO_EQ, + .adpt_eq = 48, + .amp_red = 6, + .ttlcr0 = 0x39000400, + }, + [LANE_MODE_2500BASEX] = { + .proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII, + .islew_rctl = 2, + .oslew_rctl = 2, + .teq_type = EQ_TYPE_2TAP, + .sgn_post1q = 1, + .ratio_post1q = 6, + .adpt_eq = 48, + .ttlcr0 = 0x00000400, + }, + [LANE_MODE_QSGMII] = { + .proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII, + .islew_rctl = 1, + .oslew_rctl = 1, + .teq_type = EQ_TYPE_2TAP, + .sgn_post1q = 1, + .ratio_post1q = 6, + .adpt_eq = 48, + .amp_red = 2, + .ttlcr0 = 0x00000400, + }, + [LANE_MODE_10G_QXGMII] = { + .proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII, + .if20bit_en = 1, + .islew_rctl = 1, + .oslew_rctl = 1, + .base_wand = 1, + .teq_type = EQ_TYPE_NO_EQ, + .adpt_eq = 48, + .ttlcr0 = 0x00000400, + }, + [LANE_MODE_USXGMII] = { + .proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII, + .if20bit_en = 1, + .islew_rctl = 1, + .oslew_rctl = 1, + .base_wand = 1, + .teq_type = EQ_TYPE_NO_EQ, + .sgn_post1q = 1, + .adpt_eq = 48, + .ttlcr0 = 0x00000400, + }, + [LANE_MODE_10GBASER] = { + .proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII, + .if20bit_en = 1, + .islew_rctl = 2, + .oslew_rctl = 2, + .rxeq_bst = 1, + .base_wand = 1, + .teq_type = EQ_TYPE_2TAP, + .sgn_post1q = 1, + .ratio_post1q = 3, + .adpt_eq = 48, + .amp_red = 7, + .ttlcr0 = 0x00000400, + }, +}; + +static void lynx_10g_cdr_lock_check(struct lynx_lane *lane) +{ + u32 tcsr3 = lynx_lane_read(lane, LNaTCSR3); + + if (tcsr3 & LNaTCSR3_CDR_LCK) + return; + + dev_dbg(&lane->phy->dev, + "Lane %c CDR unlocked, resetting receiver...\n", + 'A' + lane->id); + + lynx_lane_rmw(lane, LNaGCR0, LNaGCR0_RRST_ON, LNaGCR0_RRST); + usleep_range(1, 2); + lynx_lane_rmw(lane, LNaGCR0, LNaGCR0_RRST_OFF, LNaGCR0_RRST); + + usleep_range(1, 2); +} + +static void lynx_10g_pll_read_configuration(struct lynx_pll *pll) +{ + u32 val; + + val = lynx_pll_read(pll, PLLnCR0); + pll->frate_sel = FIELD_GET(PLLnCR0_FRATE_SEL, val); + pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val); + pll->enabled = !(val & PLLnCR0_POFF); + pll->locked = !!(val & PLLnCR0_PLL_LCK); + + if (!pll->enabled) + return; + + switch (pll->frate_sel) { + case PLLnCR0_FRATE_5G: + /* 5GHz clock net */ + __set_bit(LANE_MODE_1000BASEX_SGMII, pll->supported); + __set_bit(LANE_MODE_QSGMII, pll->supported); + break; + case PLLnCR0_FRATE_3_125G: + __set_bit(LANE_MODE_2500BASEX, pll->supported); + break; + case PLLnCR0_FRATE_5_15625G: + /* 10.3125GHz clock net */ + __set_bit(LANE_MODE_10GBASER, pll->supported); + __set_bit(LANE_MODE_USXGMII, pll->supported); + __set_bit(LANE_MODE_10G_QXGMII, pll->supported); + break; + default: + break; + } +} + +/* On LS1028A, SGMIIA_CFG, SGMIIB_CFG, and SGMIIC_CFG from PCCR8 have the + * ability to map either an ENETC PCS (PCCR8_SGMIIa_CFG=2) or a Felix switch + * PCS (PCCR8_SGMIIa_CFG=1) to the same lane. + * + * On LS1088A, the same QSGMII PCS B can be connected to SerDes lane 1 + * (PCCR9_QSGMIIa_CFG=1) or to lane 3 (PCCR9_QSGMIIa_CFG=2). + * + * The PHY API lacks the capability to distinguish anything about the consumer, + * so we don't support changing the initial muxing done by the RCW. + * + * However, after disabling a PCS through PCCR8, we need to properly restore + * the original value to keep the same muxing, and for that we need to back + * it up (here). + */ +static void lynx_10g_backup_pccr_val(struct lynx_lane *lane) +{ + u32 val; + int err; + + if (lane->mode == LANE_MODE_UNKNOWN) + return; + + err = lynx_pccr_read(lane, lane->mode, &val); + if (err) { + dev_warn(&lane->phy->dev, + "The driver doesn't know how to access the PCCR for lane mode %s\n", + lynx_lane_mode_str(lane->mode)); + lane->mode = LANE_MODE_UNKNOWN; + return; + } + + lane->default_pccr[lane->mode] = val; + + /* 1000Base-X, 1000Base-KX, 2500Base-KX and SGMII use the same PCCR8. + * Only the KX bit differs (set for 1000Base-KX). Since we back up PCCR + * values per lane mode, make sure to not back up the PCCR8 value with + * the KX bit set for the non-KX modes, if the lane was in KX mode at + * boot time. Just preserve bits 2:0, which tell whether the (and + * which) 1G PCS was enabled. + */ + switch (lane->mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + lane->default_pccr[LANE_MODE_1000BASEX_SGMII] = val & ~PCCR8_SGMIIa_KX; + lane->default_pccr[LANE_MODE_2500BASEX] = val & ~PCCR8_SGMIIa_KX; + break; + default: + break; + } +} + +/* Is the PCS enabled, according to the value backed up from the PCCR register + * for this lane mode? + * + * Normally we'd need to ask "what lane mode are we talking about?", but the + * answer is invariably the same regardless - PCCR8_SGMIIa_CFG has the same + * layout as PCCR9_QSGMIIa_CFG, PCCRB_XFIa_CFG etc etc, and the value 0 + * universally means "PCS disabled". So this is just a shorthand answer. + */ +static bool lynx_10g_pccr_val_enabled(u32 pccr) +{ + return FIELD_PREP(PCCR8_SGMIIa_CFG, pccr) != 0; +} + +static bool lynx_10g_lane_is_3_125g(struct lynx_lane *lane) +{ + struct lynx_priv *priv = lane->priv; + struct lynx_pll *pll; + u32 gcr0; + + gcr0 = lynx_lane_read(lane, LNaGCR0); + + if (gcr0 & LNaGCR0_TPLL_PLLF) + pll = &priv->pll[0]; + else + pll = &priv->pll[1]; + + if (pll->frate_sel != PLLnCR0_FRATE_3_125G) + return false; + + if (FIELD_GET(LNaGCR0_TRAT_SEL, gcr0) != RAT_SEL_FULL || + FIELD_GET(LNaGCR0_RRAT_SEL, gcr0) != RAT_SEL_FULL) + return false; + + return true; +} + +static void lynx_10g_lane_read_configuration(struct lynx_lane *lane) +{ + u32 pssr0 = lynx_lane_read(lane, LNaPSSR0); + struct lynx_priv *priv = lane->priv; + int proto; + + proto = FIELD_GET(LNaPSSR0_TYPE, pssr0); + switch (proto) { + case PROTO_SEL_SGMII_BASEX_KX_QSGMII: + if (lynx_10g_lane_is_3_125g(lane)) + lane->mode = LANE_MODE_2500BASEX; + else if (FIELD_GET(LNaPSSR0_IS_QUAD, pssr0)) + lane->mode = LANE_MODE_QSGMII; + else + lane->mode = LANE_MODE_1000BASEX_SGMII; + break; + case PROTO_SEL_XFI_10GBASER_KR_SXGMII: + if (FIELD_GET(LNaPSSR0_IS_QUAD, pssr0)) + lane->mode = LANE_MODE_10G_QXGMII; + else if (priv->info->quirks & LYNX_QUIRK_HAS_HARDCODED_USXGMII) + lane->mode = LANE_MODE_USXGMII; + else + lane->mode = LANE_MODE_10GBASER; + break; + case PROTO_SEL_PCIE: + case PROTO_SEL_SATA: + case PROTO_SEL_XAUI: + break; + default: + dev_warn(&lane->phy->dev, "Unknown lane protocol 0x%x\n", + proto); + } + + lynx_10g_backup_pccr_val(lane); +} + +static int ls1028a_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); + break; + case LANE_MODE_QSGMII: + if (lane != 1) + return -EINVAL; + + pccr->offset = PCCR9; + pccr->width = 3; + pccr->shift = QSGMII_CFG(A); + break; + case LANE_MODE_10G_QXGMII: + if (lane != 1) + return -EINVAL; + + pccr->offset = PCCR9; + pccr->width = 3; + pccr->shift = QXGMII_CFG(A); + break; + case LANE_MODE_USXGMII: + if (lane != 0) + return -EINVAL; + + pccr->offset = PCCRB; + pccr->width = 3; + pccr->shift = SXGMII_CFG(A); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls1028a_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + return SGMIIaCR0(lane); + case LANE_MODE_QSGMII: + return lane == 1 ? QSGMIIaCR0(A) : -EINVAL; + case LANE_MODE_USXGMII: + return lane == 0 ? SXGMIIaCR0(A) : -EINVAL; + case LANE_MODE_10G_QXGMII: + return lane == 1 ? QXGMIIaCR0(A) : -EINVAL; + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls1028a = { + .get_pccr = ls1028a_get_pccr, + .get_pcvt_offset = ls1028a_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 4, + .index = 1, + .quirks = LYNX_QUIRK_HAS_HARDCODED_USXGMII, +}; + +static int ls1046a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); + break; + case LANE_MODE_QSGMII: + if (lane != 1) + return -EINVAL; + + pccr->offset = PCCR9; + pccr->width = 3; + pccr->shift = QSGMII_CFG(B); + break; + case LANE_MODE_10GBASER: + switch (lane) { + case 2: + pccr->shift = XFI_CFG(A); + break; + case 3: + pccr->shift = XFI_CFG(B); + break; + default: + return -EINVAL; + } + + pccr->offset = PCCRB; + pccr->width = 3; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls1046a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + return SGMIIaCR0(lane); + case LANE_MODE_QSGMII: + if (lane != 1) + return -EINVAL; + + return QSGMIIaCR0(B); + case LANE_MODE_10GBASER: + switch (lane) { + case 2: + return XFIaCR0(A); + case 3: + return XFIaCR0(B); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls1046a_serdes1 = { + .get_pccr = ls1046a_serdes1_get_pccr, + .get_pcvt_offset = ls1046a_serdes1_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 4, + .index = 1, +}; + +static int ls1046a_serdes2_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + if (lane != 1) + return -EINVAL; + + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(B); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls1046a_serdes2_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + if (lane != 1) + return -EINVAL; + + return SGMIIaCR0(B); + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls1046a_serdes2 = { + .get_pccr = ls1046a_serdes2_get_pccr, + .get_pcvt_offset = ls1046a_serdes2_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 4, + .index = 2, +}; + +static int ls1088a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); + break; + case LANE_MODE_QSGMII: + switch (lane) { + case 0: + pccr->shift = QSGMII_CFG(A); + break; + case 1: + case 3: + pccr->shift = QSGMII_CFG(B); + break; + default: + return -EINVAL; + } + + pccr->offset = PCCR9; + pccr->width = 3; + break; + case LANE_MODE_10GBASER: + switch (lane) { + case 2: + pccr->shift = XFI_CFG(A); + break; + case 3: + pccr->shift = XFI_CFG(B); + break; + default: + return -EINVAL; + } + + pccr->offset = PCCRB; + pccr->width = 3; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls1088a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + return SGMIIaCR0(lane); + case LANE_MODE_QSGMII: + switch (lane) { + case 0: + return QSGMIIaCR0(A); + case 1: + case 3: + return QSGMIIaCR0(B); + default: + return -EINVAL; + } + case LANE_MODE_10GBASER: + switch (lane) { + case 2: + return XFIaCR0(A); + case 3: + return XFIaCR0(B); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls1088a_serdes1 = { + .get_pccr = ls1088a_serdes1_get_pccr, + .get_pcvt_offset = ls1088a_serdes1_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 4, + .index = 1, +}; + +static int ls2088a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); + break; + case LANE_MODE_QSGMII: + switch (lane) { + case 2: + case 6: + pccr->shift = QSGMII_CFG(A); + break; + case 7: + pccr->shift = QSGMII_CFG(B); + break; + case 0: + case 4: + pccr->shift = QSGMII_CFG(C); + break; + case 1: + case 5: + pccr->shift = QSGMII_CFG(D); + break; + default: + return -EINVAL; + } + + pccr->offset = PCCR9; + pccr->width = 3; + break; + case LANE_MODE_10GBASER: + pccr->offset = PCCRB; + pccr->width = 3; + pccr->shift = XFI_CFG(lane); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls2088a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + return SGMIIaCR0(lane); + case LANE_MODE_QSGMII: + switch (lane) { + case 2: + case 6: + return QSGMIIaCR0(A); + case 7: + return QSGMIIaCR0(B); + case 0: + case 4: + return QSGMIIaCR0(C); + case 1: + case 5: + return QSGMIIaCR0(D); + default: + return -EINVAL; + } + case LANE_MODE_10GBASER: + return XFIaCR0(lane); + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls2088a_serdes1 = { + .get_pccr = ls2088a_serdes1_get_pccr, + .get_pcvt_offset = ls2088a_serdes1_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 8, + .index = 1, +}; + +static int ls2088a_serdes2_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + pccr->offset = PCCR8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ls2088a_serdes2_get_pcvt_offset(int lane, enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + return SGMIIaCR0(lane); + default: + return -EINVAL; + } +} + +static const struct lynx_info lynx_info_ls2088a_serdes2 = { + .get_pccr = ls2088a_serdes2_get_pccr, + .get_pcvt_offset = ls2088a_serdes2_get_pcvt_offset, + .pll_read_configuration = lynx_10g_pll_read_configuration, + .lane_read_configuration = lynx_10g_lane_read_configuration, + .cdr_lock_check = lynx_10g_cdr_lock_check, + .num_lanes = 8, + .index = 2, +}; + +/* Halting puts the lane in a mode in which it can be reconfigured */ +static void lynx_10g_lane_halt(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + /* Issue a reset request */ + lynx_lane_rmw(lane, LNaGCR0, + LNaGCR0_RRST_ON | LNaGCR0_TRST_ON, + LNaGCR0_RRST | LNaGCR0_TRST); + + /* The RM says to wait for at least 50ns */ + usleep_range(1, 2); +} + +static void lynx_10g_lane_reset(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + /* Finalize the reset request */ + lynx_lane_rmw(lane, LNaGCR0, + LNaGCR0_RRST_OFF | LNaGCR0_TRST_OFF, + LNaGCR0_RRST | LNaGCR0_TRST); +} + +static int lynx_10g_power_off(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + if (!lane->powered_up) + return 0; + + /* Issue a reset request with the power down bits set */ + lynx_lane_rmw(lane, LNaGCR0, + LNaGCR0_RRST_ON | LNaGCR0_TRST_ON | + LNaGCR0_RX_PD | LNaGCR0_TX_PD, + LNaGCR0_RRST | LNaGCR0_TRST | + LNaGCR0_RX_PD | LNaGCR0_TX_PD); + + /* The RM says to wait for at least 50ns */ + usleep_range(1, 2); + + lane->powered_up = false; + + return 0; +} + +static int lynx_10g_power_on(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + if (lane->powered_up) + return 0; + + /* RM says that to enable a previously powered down lane, set + * LNmGCR0[{R,T}X_PD]=0, wait 15 us, then set LNmGCR0[{R,T}RST]=1. + */ + lynx_lane_rmw(lane, LNaGCR0, 0, LNaGCR0_RX_PD | LNaGCR0_TX_PD); + usleep_range(150, 300); + lynx_10g_lane_reset(phy); + + lane->powered_up = true; + + return 0; +} + +static void lynx_10g_lane_set_nrate(struct lynx_lane *lane, + struct lynx_pll *pll, + enum lynx_lane_mode mode) +{ + enum lynx_10g_rat_sel nrate; + + switch (pll->frate_sel) { + case PLLnCR0_FRATE_5G: + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + nrate = RAT_SEL_QUARTER; + break; + case LANE_MODE_QSGMII: + nrate = RAT_SEL_FULL; + break; + default: + return; + } + break; + case PLLnCR0_FRATE_3_125G: + switch (mode) { + case LANE_MODE_2500BASEX: + nrate = RAT_SEL_FULL; + break; + default: + return; + } + break; + case PLLnCR0_FRATE_5_15625G: + switch (mode) { + case LANE_MODE_10GBASER: + case LANE_MODE_USXGMII: + case LANE_MODE_10G_QXGMII: + nrate = RAT_SEL_DOUBLE; + break; + default: + return; + } + break; + default: + return; + } + + lynx_lane_rmw(lane, LNaGCR0, + FIELD_PREP(LNaGCR0_TRAT_SEL, nrate) | + FIELD_PREP(LNaGCR0_RRAT_SEL, nrate), + LNaGCR0_RRAT_SEL | LNaGCR0_TRAT_SEL); +} + +static void lynx_10g_lane_set_pll(struct lynx_lane *lane, + struct lynx_pll *pll) +{ + if (pll->id == 0) { + lynx_lane_rmw(lane, LNaGCR0, + LNaGCR0_RPLL_PLLF | LNaGCR0_TPLL_PLLF, + LNaGCR0_RPLL_MSK | LNaGCR0_TPLL_MSK); + } else { + lynx_lane_rmw(lane, LNaGCR0, + LNaGCR0_RPLL_PLLS | LNaGCR0_TPLL_PLLS, + LNaGCR0_RPLL_MSK | LNaGCR0_TPLL_MSK); + } +} + +static void lynx_10g_lane_remap_pll(struct lynx_lane *lane, + enum lynx_lane_mode lane_mode) +{ + struct lynx_priv *priv = lane->priv; + struct lynx_pll *pll; + + /* Switch to the PLL that works with this interface type */ + pll = lynx_pll_get(priv, lane_mode); + if (unlikely(!pll)) + return; + + lynx_10g_lane_set_pll(lane, pll); + + /* Choose the portion of clock net to be used on this lane */ + lynx_10g_lane_set_nrate(lane, pll, lane_mode); +} + +static void lynx_10g_lane_change_proto_conf(struct lynx_lane *lane, + enum lynx_lane_mode mode) +{ + const struct lynx_10g_proto_conf *conf = &lynx_10g_proto_conf[mode]; + + lynx_lane_rmw(lane, LNaGCR0, + FIELD_PREP(LNaGCR0_PROTS, conf->proto_sel) | + FIELD_PREP(LNaGCR0_IF20BIT_EN, conf->if20bit_en), + LNaGCR0_PROTS | LNaGCR0_IF20BIT_EN); + lynx_lane_rmw(lane, LNaGCR1, + FIELD_PREP(LNaGCR1_REIDL_TH, conf->reidl_th) | + FIELD_PREP(LNaGCR1_REIDL_ET_MSB, conf->reidl_et_msb) | + FIELD_PREP(LNaGCR1_REIDL_ET_SEL, conf->reidl_et_sel) | + FIELD_PREP(LNaGCR1_REIDL_EX_MSB, conf->reidl_ex_msb) | + FIELD_PREP(LNaGCR1_REIDL_EX_SEL, conf->reidl_ex_sel) | + FIELD_PREP(LNaGCR1_ISLEW_RCTL, conf->islew_rctl) | + FIELD_PREP(LNaGCR1_OSLEW_RCTL, conf->oslew_rctl), + LNaGCR1_REIDL_TH | + LNaGCR1_REIDL_ET_MSB | LNaGCR1_REIDL_ET_SEL | + LNaGCR1_REIDL_EX_MSB | LNaGCR1_REIDL_EX_SEL | + LNaGCR1_ISLEW_RCTL | LNaGCR1_OSLEW_RCTL); + lynx_lane_rmw(lane, LNaRECR0, + FIELD_PREP(LNaRECR0_RXEQ_BST, conf->rxeq_bst) | + FIELD_PREP(LNaRECR0_GK2OVD, conf->gk2ovd) | + FIELD_PREP(LNaRECR0_GK3OVD, conf->gk3ovd) | + FIELD_PREP(LNaRECR0_GK2OVD_EN, conf->gk2ovd_en) | + FIELD_PREP(LNaRECR0_GK3OVD_EN, conf->gk3ovd_en) | + FIELD_PREP(LNaRECR0_BASE_WAND, conf->base_wand), + LNaRECR0_RXEQ_BST | LNaRECR0_GK2OVD | LNaRECR0_GK3OVD | + LNaRECR0_GK2OVD_EN | LNaRECR0_GK3OVD_EN | + LNaRECR0_BASE_WAND); + lynx_lane_rmw(lane, LNaTECR0, + FIELD_PREP(LNaTECR0_TEQ_TYPE, conf->teq_type) | + FIELD_PREP(LNaTECR0_SGN_PREQ, conf->sgn_preq) | + FIELD_PREP(LNaTECR0_RATIO_PREQ, conf->ratio_preq) | + FIELD_PREP(LNaTECR0_SGN_POST1Q, conf->sgn_post1q) | + FIELD_PREP(LNaTECR0_RATIO_PST1Q, conf->ratio_post1q) | + FIELD_PREP(LNaTECR0_ADPT_EQ, conf->adpt_eq) | + FIELD_PREP(LNaTECR0_AMP_RED, conf->amp_red), + LNaTECR0_TEQ_TYPE | LNaTECR0_SGN_PREQ | + LNaTECR0_RATIO_PREQ | LNaTECR0_SGN_POST1Q | + LNaTECR0_RATIO_PST1Q | LNaTECR0_ADPT_EQ | + LNaTECR0_AMP_RED); + lynx_lane_write(lane, LNaTTLCR0, conf->ttlcr0); +} + +static int lynx_10g_lane_disable_pcvt(struct lynx_lane *lane, + enum lynx_lane_mode mode) +{ + struct lynx_priv *priv = lane->priv; + int err; + + spin_lock(&priv->pcc_lock); + + err = lynx_pccr_write(lane, mode, 0); + if (err) + goto out; + + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + err = lynx_pcvt_rmw(lane, mode, CR(1), SGMIIaCR1_SGPCS_DIS, + SGMIIaCR1_SGPCS_EN); + if (err) + goto out; + + lynx_pcvt_rmw(lane, mode, CR(0), + SGMIIaCR0_RST_SGM_ON | SGMIIaCR0_PD_SGM, + SGMIIaCR0_RST_SGM | SGMIIaCR0_PD_SGM); + break; + case LANE_MODE_QSGMII: + err = lynx_pcvt_rmw(lane, mode, CR(0), + QSGMIIaCR0_RST_QSGM_ON | QSGMIIaCR0_PD_QSGM, + QSGMIIaCR0_RST_QSGM | QSGMIIaCR0_PD_QSGM); + if (err) + goto out; + break; + default: + err = 0; + } + +out: + spin_unlock(&priv->pcc_lock); + + return err; +} + +static int lynx_10g_lane_enable_pcvt(struct lynx_lane *lane, + enum lynx_lane_mode mode) +{ + struct lynx_priv *priv = lane->priv; + u32 val; + int err; + + spin_lock(&priv->pcc_lock); + + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + err = lynx_pcvt_rmw(lane, mode, CR(1), SGMIIaCR1_SGPCS_EN, + SGMIIaCR1_SGPCS_EN); + if (err) + goto out; + + lynx_pcvt_rmw(lane, mode, CR(0), SGMIIaCR0_RST_SGM_OFF, + SGMIIaCR0_RST_SGM | SGMIIaCR0_PD_SGM); + break; + case LANE_MODE_QSGMII: + err = lynx_pcvt_rmw(lane, mode, CR(0), QSGMIIaCR0_RST_QSGM_OFF, + QSGMIIaCR0_RST_QSGM | QSGMIIaCR0_PD_QSGM); + if (err) + goto out; + break; + default: + err = 0; + } + + /* If the PCS was enabled at boot time, use the backed up PCCR value to + * re-enable it here, to preserve the muxing. + */ + if (lynx_10g_pccr_val_enabled(lane->default_pccr[mode])) { + err = lynx_pccr_write(lane, mode, lane->default_pccr[mode]); + goto out; + } + + /* If the PCS was not enabled, set the PCCR to a default value which + * enables it (1). The assumption is that this is the only PCS <-> + * SerDes lane muxing value possible. + * + * This is mostly useful for SGMII <-> 10GBase-R major protocol + * reconfiguration, where at boot time, either the SGMII or the + * 10GBase-R PCS is enabled for the lane, but not both. + * + * In fact, if there are multiple lane muxing options, this function + * will most likely not choose the right one. For correct functionality + * there, we assume that the PCS we are enabling here was found enabled + * at boot time (reset default, or through PBL, or...), and we preserve + * its muxing through the default_pccr branch above. + */ + val = 0; + + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + val |= FIELD_PREP(PCCR8_SGMIIa_CFG, 1); + break; + case LANE_MODE_QSGMII: + val |= FIELD_PREP(PCCR9_QSGMIIa_CFG, 1); + break; + case LANE_MODE_10G_QXGMII: + val |= FIELD_PREP(PCCR9_QXGMIIa_CFG, 1); + break; + case LANE_MODE_10GBASER: + val |= FIELD_PREP(PCCRB_XFIa_CFG, 1); + break; + case LANE_MODE_USXGMII: + val |= FIELD_PREP(PCCRB_SXGMIIa_CFG, 1); + break; + default: + err = 0; + goto out; + } + + err = lynx_pccr_write(lane, mode, val); +out: + spin_unlock(&priv->pcc_lock); + + return err; +} + +static bool lynx_10g_lane_mode_needs_rcw_override(struct lynx_lane *lane, + enum lynx_lane_mode new) +{ + enum lynx_lane_mode curr = lane->mode; + + /* Major protocol changes, which involve changing the PCS connection to + * the GMII MAC with the one to the XGMII MAC, require an RCW override + * procedure to reconfigure an internal mux, as documented here: + * https://lore.kernel.org/linux-phy/20230810102631.bvozjer3t67r67iy@skbuf/ + * This is SoC-specific, and not yet implemented in drivers/soc/fsl/guts.c. + * + * So the supported set of protocols depends on the initial lane mode. + * + * Minor protocol changes (SGMII <-> 1000Base-X <-> 2500Base-X or + * 10GBase-R <-> USXGMII) are supported. + */ + if ((lynx_lane_mode_uses_gmii_mac(curr) && + lynx_lane_mode_uses_xgmii_mac(new)) || + (lynx_lane_mode_uses_xgmii_mac(curr) && + lynx_lane_mode_uses_gmii_mac(new))) + return true; + + return false; +} + +static int lynx_10g_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + enum lynx_lane_mode lane_mode; + int err; + + err = lynx_phy_mode_to_lane_mode(phy, mode, submode, &lane_mode); + if (err) + return err; + + if (lynx_10g_lane_mode_needs_rcw_override(lane, lane_mode)) + return -EINVAL; + + return 0; +} + +static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + bool powered_up = lane->powered_up; + enum lynx_lane_mode lane_mode; + int err; + + err = lynx_10g_validate(phy, mode, submode, NULL); + if (err) + return err; + + lane_mode = phy_interface_to_lane_mode(submode); + /* lynx_10g_validate() already made sure the lane_mode is supported */ + + if (lane_mode == lane->mode) + return 0; + + /* If the lane is powered up, put the lane into the halt state while + * the reconfiguration is being done. + */ + if (powered_up) + lynx_10g_lane_halt(phy); + + err = lynx_10g_lane_disable_pcvt(lane, lane->mode); + if (err) + goto out; + + lynx_10g_lane_change_proto_conf(lane, lane_mode); + lynx_10g_lane_remap_pll(lane, lane_mode); + WARN_ON(lynx_10g_lane_enable_pcvt(lane, lane_mode)); + + lane->mode = lane_mode; + +out: + if (powered_up) { + /* The RM says to wait for at least 120 ns */ + usleep_range(1, 2); + lynx_10g_lane_reset(phy); + } + + return err; +} + +static int lynx_10g_init(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + /* Mark the fact that the lane was init */ + lane->init = true; + + /* SerDes lanes are powered on at boot time. Any lane that is + * managed by this driver will get powered off when its consumer + * calls phy_init(). + */ + lane->powered_up = true; + lynx_10g_power_off(phy); + + return 0; +} + +static int lynx_10g_exit(struct phy *phy) +{ + struct lynx_lane *lane = phy_get_drvdata(phy); + + /* The lane returns to the state where it isn't managed by the + * consumer, so we must treat is as if it isn't initialized, and always + * powered on. + */ + lane->init = false; + lane->powered_up = false; + lynx_10g_power_on(phy); + + return 0; +} + +static const struct phy_ops lynx_10g_ops = { + .init = lynx_10g_init, + .exit = lynx_10g_exit, + .power_on = lynx_10g_power_on, + .power_off = lynx_10g_power_off, + .set_mode = lynx_10g_set_mode, + .validate = lynx_10g_validate, + .owner = THIS_MODULE, +}; + +static int lynx_10g_probe(struct platform_device *pdev) +{ + return lynx_probe(pdev, of_device_get_match_data(&pdev->dev), + &lynx_10g_ops); +} + +static const struct of_device_id lynx_10g_of_match_table[] = { + { .compatible = "fsl,ls1028a-serdes", .data = &lynx_info_ls1028a }, + { .compatible = "fsl,ls1046a-serdes1", .data = &lynx_info_ls1046a_serdes1 }, + { .compatible = "fsl,ls1046a-serdes2", .data = &lynx_info_ls1046a_serdes2 }, + { .compatible = "fsl,ls1088a-serdes1", .data = &lynx_info_ls1088a_serdes1 }, + { .compatible = "fsl,ls2088a-serdes1", .data = &lynx_info_ls2088a_serdes1 }, + { .compatible = "fsl,ls2088a-serdes2", .data = &lynx_info_ls2088a_serdes2 }, + {} +}; +MODULE_DEVICE_TABLE(of, lynx_10g_of_match_table); + +static struct platform_driver lynx_10g_driver = { + .probe = lynx_10g_probe, + .remove = lynx_remove, + .driver = { + .name = "lynx-10g", + .of_match_table = lynx_10g_of_match_table, + }, +}; +module_platform_driver(lynx_10g_driver); + +MODULE_IMPORT_NS("PHY_FSL_LYNX"); +MODULE_AUTHOR("Ioana Ciornei "); +MODULE_AUTHOR("Vladimir Oltean "); +MODULE_DESCRIPTION("Lynx 10G SerDes PHY driver for Layerscape SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c index 1e411bfab404..2cfe9236ffc5 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.c +++ b/drivers/phy/freescale/phy-fsl-lynx-core.c @@ -11,6 +11,12 @@ const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) switch (lane_mode) { case LANE_MODE_1000BASEX_SGMII: return "1000Base-X/SGMII"; + case LANE_MODE_2500BASEX: + return "2500Base-X"; + case LANE_MODE_QSGMII: + return "QSGMII"; + case LANE_MODE_10G_QXGMII: + return "10G-QXGMII"; case LANE_MODE_10GBASER: return "10GBase-R"; case LANE_MODE_USXGMII: @@ -29,6 +35,12 @@ enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: return LANE_MODE_1000BASEX_SGMII; + case PHY_INTERFACE_MODE_2500BASEX: + return LANE_MODE_2500BASEX; + case PHY_INTERFACE_MODE_QSGMII: + return LANE_MODE_QSGMII; + case PHY_INTERFACE_MODE_10G_QXGMII: + return LANE_MODE_10G_QXGMII; case PHY_INTERFACE_MODE_10GBASER: return LANE_MODE_10GBASER; case PHY_INTERFACE_MODE_USXGMII: @@ -89,6 +101,29 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) } EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX"); +/* The quad protocols are fixed because the lane has multiple consumers, and + * one phy_set_mode_ext() affects the other consumers as well. We have no use + * case for dynamic protocol changing here, so disallow it. + */ +static enum lynx_lane_mode lynx_fixed_protocols[] = { + LANE_MODE_QSGMII, + LANE_MODE_10G_QXGMII, +}; + +static bool lynx_lane_restrict_fixed_mode_change(struct lynx_lane *lane, + enum lynx_lane_mode new) +{ + enum lynx_lane_mode curr = lane->mode; + + for (int i = 0; i < ARRAY_SIZE(lynx_fixed_protocols); i++) + if ((curr == lynx_fixed_protocols[i] || + new == lynx_fixed_protocols[i]) && + curr != new) + return true; + + return false; +} + /* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a * lane_mode and return 0 if it is supported and we can transition to it from * the current lane mode, or return negative error otherwise. @@ -112,6 +147,9 @@ int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode, if (!lynx_lane_supports_mode(lane, tmp_lane_mode)) return -EINVAL; + if (lynx_lane_restrict_fixed_mode_change(lane, tmp_lane_mode)) + return -EINVAL; + if (lane_mode) *lane_mode = tmp_lane_mode; diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h index 37fa4b544faa..a60429ba9324 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-core.h +++ b/drivers/phy/freescale/phy-fsl-lynx-core.h @@ -9,6 +9,7 @@ #include #define LYNX_NUM_PLL 2 +#define LYNX_QUIRK_HAS_HARDCODED_USXGMII BIT(0) struct lynx_priv; struct lynx_lane; @@ -36,6 +37,7 @@ struct lynx_lane { bool init; unsigned int id; enum lynx_lane_mode mode; + u32 default_pccr[LANE_MODE_MAX]; }; struct lynx_info { @@ -48,6 +50,8 @@ struct lynx_info { void (*cdr_lock_check)(struct lynx_lane *lane); int first_lane; int num_lanes; + int index; + unsigned long quirks; }; struct lynx_priv { diff --git a/include/soc/fsl/phy-fsl-lynx.h b/include/soc/fsl/phy-fsl-lynx.h index 92e8272d5ae1..ff5a7d1835b5 100644 --- a/include/soc/fsl/phy-fsl-lynx.h +++ b/include/soc/fsl/phy-fsl-lynx.h @@ -7,10 +7,37 @@ enum lynx_lane_mode { LANE_MODE_UNKNOWN, LANE_MODE_1000BASEX_SGMII, + LANE_MODE_2500BASEX, + LANE_MODE_QSGMII, + LANE_MODE_10G_QXGMII, LANE_MODE_10GBASER, LANE_MODE_USXGMII, LANE_MODE_25GBASER, LANE_MODE_MAX, }; +static inline bool lynx_lane_mode_uses_gmii_mac(enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_2500BASEX: + case LANE_MODE_QSGMII: + case LANE_MODE_10G_QXGMII: + return true; + default: + return false; + } +} + +static inline bool lynx_lane_mode_uses_xgmii_mac(enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_10GBASER: + case LANE_MODE_USXGMII: + return true; + default: + return false; + } +} + #endif /* __PHY_FSL_LYNX_H_ */ From 61df73e12eb245adc54606de287cf8898b3a66c4 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 10 Jun 2026 18:19:52 +0300 Subject: [PATCH 3388/5214] MAINTAINERS: expand Lynx 28G entry to cover Lynx 10G SerDes The lynx-28g and lynx-10g drivers share code and hardware architecture, so let them be covered by a single MAINTAINERS entry. Add myself as a second maintainer alongside Ioana Ciornei. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260610151952.2141019-17-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- MAINTAINERS | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 06563ce3f459..50f45492a600 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15284,12 +15284,18 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/liteon,ltr390.yaml F: drivers/iio/light/ltr390.c -LYNX 28G SERDES PHY DRIVER +LYNX SERDES PHY DRIVERS M: Ioana Ciornei +M: Vladimir Oltean L: netdev@vger.kernel.org S: Supported +F: Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml F: Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml +F: drivers/phy/freescale/phy-fsl-lynx-10g.c F: drivers/phy/freescale/phy-fsl-lynx-28g.c +F: drivers/phy/freescale/phy-fsl-lynx-core.c +F: drivers/phy/freescale/phy-fsl-lynx-core.h +F: include/soc/fsl/phy-fsl-lynx.h LYNX PCS MODULE M: Ioana Ciornei From 4f2692a5383e4bdd43ae940cda012360f7217a2d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 1 Jun 2026 19:07:23 -0700 Subject: [PATCH 3389/5214] mtd: rawnand: ndfc: use ioread32be/iowrite32be and allow COMPILE_TEST Replace ppc4xx-specific in_be32/out_be32 with generic ioread32be/ iowrite32be to make the driver portable. Add COMPILE_TEST dependency to get build coverage on non-ppc4xx architectures. While at it, replace 4xx with 44x. The latter was removed a while ago and is only kept for compatibility. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- drivers/mtd/nand/raw/ndfc.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 7408f34f0c68..ee01b845694d 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -71,7 +71,7 @@ config MTD_NAND_AU1550 config MTD_NAND_NDFC tristate "IBM/MCC 4xx NAND controller" - depends on 4xx + depends on 44x || COMPILE_TEST select MTD_NAND_ECC_SW_HAMMING select MTD_NAND_ECC_SW_HAMMING_SMC help diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index 7ad8bc04be1a..a937ca3eeff5 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -44,13 +44,13 @@ static void ndfc_select_chip(struct nand_chip *nchip, int chip) uint32_t ccr; struct ndfc_controller *ndfc = nand_get_controller_data(nchip); - ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); + ccr = ioread32be(ndfc->ndfcbase + NDFC_CCR); if (chip >= 0) { ccr &= ~NDFC_CCR_BS_MASK; ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); } else ccr |= NDFC_CCR_RESET_CE; - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); + iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR); } static void ndfc_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl) @@ -70,7 +70,7 @@ static int ndfc_ready(struct nand_chip *chip) { struct ndfc_controller *ndfc = nand_get_controller_data(chip); - return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; + return ioread32be(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; } static void ndfc_enable_hwecc(struct nand_chip *chip, int mode) @@ -78,9 +78,9 @@ static void ndfc_enable_hwecc(struct nand_chip *chip, int mode) uint32_t ccr; struct ndfc_controller *ndfc = nand_get_controller_data(chip); - ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); + ccr = ioread32be(ndfc->ndfcbase + NDFC_CCR); ccr |= NDFC_CCR_RESET_ECC; - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); + iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR); wmb(); } @@ -92,7 +92,7 @@ static int ndfc_calculate_ecc(struct nand_chip *chip, uint8_t *p = (uint8_t *)&ecc; wmb(); - ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); + ecc = ioread32be(ndfc->ndfcbase + NDFC_ECC); /* The NDFC uses Smart Media (SMC) bytes order */ ecc_code[0] = p[1]; ecc_code[1] = p[2]; @@ -114,7 +114,7 @@ static void ndfc_read_buf(struct nand_chip *chip, uint8_t *buf, int len) uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) - *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); + *p++ = ioread32be(ndfc->ndfcbase + NDFC_DATA); } static void ndfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len) @@ -123,7 +123,7 @@ static void ndfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len) uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) - out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); + iowrite32be(*p++, ndfc->ndfcbase + NDFC_DATA); } /* @@ -223,13 +223,13 @@ static int ndfc_probe(struct platform_device *ofdev) if (reg) ccr |= be32_to_cpup(reg); - out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); + iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR); /* Set the bank settings if given */ reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL); if (reg) { int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); - out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg)); + iowrite32be(be32_to_cpup(reg), ndfc->ndfcbase + offset); } err = ndfc_chip_init(ndfc, ofdev->dev.of_node); From 36f1648644d769c496a8e47e53603e863e358d73 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 16:45:27 +0800 Subject: [PATCH 3390/5214] mtd: slram: remove failed entries from the device list register_device() links a new slram_mtdlist entry before allocating all of the state needed by the entry. If a later allocation, memremap(), or mtd_device_register() fails, the partially initialized entry remains on the global list. A later cleanup can then dereference or free invalid state from that failed entry. Unwind the partially initialized entry and clear the list tail on each failure path after the entry has been linked. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Miquel Raynal --- drivers/mtd/devices/slram.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 69cb63d99f57..48c2bc6b65ee 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; + int ret = -ENOMEM; curmtd = &slram_mtdlist; while (*curmtd) { @@ -155,14 +156,15 @@ static int register_device(char *name, unsigned long start, unsigned long length if (!(*curmtd)->mtdinfo) { E("slram: Cannot allocate new MTD device.\n"); - return(-ENOMEM); + goto err_free_list; } if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = memremap(start, length, MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) { E("slram: memremap failed\n"); - return -EIO; + ret = -EIO; + goto err_free_priv; } ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; @@ -183,10 +185,8 @@ static int register_device(char *name, unsigned long start, unsigned long length if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); - memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); - kfree((*curmtd)->mtdinfo->priv); - kfree((*curmtd)->mtdinfo); - return(-EAGAIN); + ret = -EAGAIN; + goto err_unmap; } T("slram: Registered device %s from %luKiB to %luKiB\n", name, (start / 1024), ((start + length) / 1024)); @@ -194,6 +194,16 @@ static int register_device(char *name, unsigned long start, unsigned long length ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); return(0); + +err_unmap: + memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); +err_free_priv: + kfree((*curmtd)->mtdinfo->priv); +err_free_list: + kfree((*curmtd)->mtdinfo); + kfree(*curmtd); + *curmtd = NULL; + return ret; } static void unregister_devices(void) From 4fe97a54daddb221009964161fb362b2b930d657 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 16:45:28 +0800 Subject: [PATCH 3391/5214] mtd: slram: simplify register_device() cleanup Use local variables for the list entry, mtd_info, and private data while initializing a new device. This keeps the initialization path easier to read and publishes the new list entry only after mtd_device_register() has succeeded. Signed-off-by: Ruoyu Wang Signed-off-by: Miquel Raynal --- drivers/mtd/devices/slram.c | 84 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 48c2bc6b65ee..aa38ecdffc97 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -129,6 +129,9 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; + slram_mtd_list_t *new_mtd; + struct mtd_info *mtdinfo; + slram_priv_t *priv; int ret = -ENOMEM; curmtd = &slram_mtdlist; @@ -136,73 +139,68 @@ static int register_device(char *name, unsigned long start, unsigned long length curmtd = &(*curmtd)->next; } - *curmtd = kmalloc_obj(slram_mtd_list_t); - if (!(*curmtd)) { + new_mtd = kmalloc_obj(slram_mtd_list_t); + if (!new_mtd) { E("slram: Cannot allocate new MTD device.\n"); return(-ENOMEM); } - (*curmtd)->mtdinfo = kzalloc_obj(struct mtd_info); - (*curmtd)->next = NULL; + new_mtd->next = NULL; - if ((*curmtd)->mtdinfo) { - (*curmtd)->mtdinfo->priv = - kzalloc_obj(slram_priv_t); - - if (!(*curmtd)->mtdinfo->priv) { - kfree((*curmtd)->mtdinfo); - (*curmtd)->mtdinfo = NULL; - } - } - - if (!(*curmtd)->mtdinfo) { + mtdinfo = kzalloc_obj(struct mtd_info); + if (!mtdinfo) { E("slram: Cannot allocate new MTD device.\n"); goto err_free_list; } + new_mtd->mtdinfo = mtdinfo; - if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = - memremap(start, length, - MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) { + priv = kzalloc_obj(slram_priv_t); + if (!priv) { + E("slram: Cannot allocate new MTD device.\n"); + goto err_free_mtdinfo; + } + mtdinfo->priv = priv; + + priv->start = memremap(start, length, + MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC); + if (!priv->start) { E("slram: memremap failed\n"); ret = -EIO; goto err_free_priv; } - ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = - ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; + priv->end = priv->start + length; + mtdinfo->name = name; + mtdinfo->size = length; + mtdinfo->flags = MTD_CAP_RAM; + mtdinfo->_erase = slram_erase; + mtdinfo->_point = slram_point; + mtdinfo->_unpoint = slram_unpoint; + mtdinfo->_read = slram_read; + mtdinfo->_write = slram_write; + mtdinfo->owner = THIS_MODULE; + mtdinfo->type = MTD_RAM; + mtdinfo->erasesize = SLRAM_BLK_SZ; + mtdinfo->writesize = 1; - (*curmtd)->mtdinfo->name = name; - (*curmtd)->mtdinfo->size = length; - (*curmtd)->mtdinfo->flags = MTD_CAP_RAM; - (*curmtd)->mtdinfo->_erase = slram_erase; - (*curmtd)->mtdinfo->_point = slram_point; - (*curmtd)->mtdinfo->_unpoint = slram_unpoint; - (*curmtd)->mtdinfo->_read = slram_read; - (*curmtd)->mtdinfo->_write = slram_write; - (*curmtd)->mtdinfo->owner = THIS_MODULE; - (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; - (*curmtd)->mtdinfo->writesize = 1; - - if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { + if (mtd_device_register(mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); ret = -EAGAIN; goto err_unmap; } + *curmtd = new_mtd; T("slram: Registered device %s from %luKiB to %luKiB\n", name, (start / 1024), ((start + length) / 1024)); - T("slram: Mapped from 0x%p to 0x%p\n", - ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, - ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); - return(0); + T("slram: Mapped from 0x%p to 0x%p\n", priv->start, priv->end); + return 0; err_unmap: - memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); + memunmap(priv->start); err_free_priv: - kfree((*curmtd)->mtdinfo->priv); + kfree(priv); +err_free_mtdinfo: + kfree(mtdinfo); err_free_list: - kfree((*curmtd)->mtdinfo); - kfree(*curmtd); - *curmtd = NULL; + kfree(new_mtd); return ret; } From cf4ff717af69cfe2dff8ddb843dce36205cb3c40 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 10 Jun 2026 09:15:52 +0200 Subject: [PATCH 3392/5214] mtd: cfi: Use common error handling code in two functions Use additional labels so that a bit of exception handling can be better reused at the end of two function implementations. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Miquel Raynal --- drivers/mtd/chips/cfi_cmdset_0002.c | 13 +++++++------ drivers/mtd/chips/cfi_cmdset_0020.c | 24 +++++++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a38aceb6612d..517db2f2707f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -661,8 +661,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) extp->MajorVersion, extp->MinorVersion, extp->MajorVersion, extp->MinorVersion); kfree(extp); - kfree(mtd); - return NULL; + goto free_mtd; } printk(KERN_INFO " Amd/Fujitsu Extended Query version %c.%c.\n", @@ -714,10 +713,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) } cfi_fixup(mtd, cfi_nopri_fixup_table); - if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { - kfree(mtd); - return NULL; - } + if (!cfi->addr_unlock1 || !cfi->addr_unlock2) + goto free_mtd; } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { @@ -755,6 +752,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) map->fldrv = &cfi_amdstd_chipdrv; return cfi_amdstd_setup(mtd); + +free_mtd: + kfree(mtd); + return NULL; } struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 6b5727eaae69..593ac65a7e2f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -174,11 +174,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) mtd = kzalloc_obj(*mtd); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); - - if (!mtd) { - kfree(cfi->cmdset_priv); - return NULL; - } + if (!mtd) + goto free_cmdset_priv; mtd->priv = map; mtd->type = MTD_NORFLASH; @@ -187,11 +184,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; mtd->eraseregions = kmalloc_objs(struct mtd_erase_region_info, mtd->numeraseregions); - if (!mtd->eraseregions) { - kfree(cfi->cmdset_priv); - kfree(mtd); - return NULL; - } + if (!mtd->eraseregions) + goto free_mtd; for (i=0; icfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; @@ -213,9 +207,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) /* Argh */ printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); kfree(mtd->eraseregions); - kfree(cfi->cmdset_priv); - kfree(mtd); - return NULL; + goto free_mtd; } for (i=0; inumeraseregions;i++){ @@ -242,6 +234,12 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) __module_get(THIS_MODULE); mtd->name = map->name; return mtd; + +free_mtd: + kfree(mtd); +free_cmdset_priv: + kfree(cfi->cmdset_priv); + return NULL; } From b28ec8ce03d8f9a0f7a9ec84f1ed9b5a6f393791 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 9 Jun 2026 22:48:50 +0800 Subject: [PATCH 3393/5214] phy: freescale: phy-fsl-imx8qm-lvds-phy: Use synchronous PM runtime put in reset The mixel_lvds_phy_reset() function pairs pm_runtime_resume_and_get() with pm_runtime_put(). The asynchronous variant queues a work item to handle the idle check and potential suspend, which can be cancelled by a subsequent pm_runtime_disable() call if probe fails after the reset. Switch to pm_runtime_put_sync() to run the idle check and suspend synchronously. Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260605-lvds-v2-1-3ce7539d1104%40gmail.com Signed-off-by: Felix Gu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260609-lvds-phy-v1-1-6ad790c6d0ea@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index ece357443521..e2a1645000ae 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -286,7 +286,7 @@ static int mixel_lvds_phy_reset(struct device *dev) regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); - pm_runtime_put(dev); + pm_runtime_put_sync(dev); return 0; } From a9a9bae2174bbad63fc73a0d445b7437f63b2498 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Mon, 8 Jun 2026 16:03:43 +0530 Subject: [PATCH 3394/5214] dt-bindings: phy: qcom,qusb2: Document IPQ5210 compatible Document the QUSB2 PHY compatible for the IPQ5210 SoC. The IPQ5210 PHY is compatible with the IPQ6018 QUSB2 PHY, so allow it to use qcom,ipq6018-qusb2-phy as the fallback compatible. Signed-off-by: Varadarajan Narayanan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260608103344.2740174-2-varadarajan.narayanan@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 39851ba9de43..449c2a7e5fec 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -30,6 +30,10 @@ properties: - qcom,sdm660-qusb2-phy - qcom,sm4250-qusb2-phy - qcom,sm6115-qusb2-phy + - items: + - enum: + - qcom,ipq5210-qusb2-phy + - const: qcom,ipq6018-qusb2-phy - items: - enum: - qcom,sc7180-qusb2-phy From 609878c1b684ea3f77ab72237511eb9bec927102 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Mon, 8 Jun 2026 16:03:44 +0530 Subject: [PATCH 3395/5214] dt-bindings: phy: qcom,qmp-usb: Add ipq5210 USB3 PHY Add dt-bindings for the USB3 QMP PHY found on the Qualcomm IPQ5210 SoC. The IPQ5210 PHY is compatible with the IPQ9574 PHY, so add it as a fallback- compatible entry using a oneOf construct rather than a plain enum entry. Signed-off-by: Varadarajan Narayanan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260608103344.2740174-3-varadarajan.narayanan@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml index 623c2f8c7d22..01342823e57f 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml @@ -15,26 +15,32 @@ description: properties: compatible: - enum: - - qcom,glymur-qmp-usb3-uni-phy - - qcom,ipq5424-qmp-usb3-phy - - qcom,ipq6018-qmp-usb3-phy - - qcom,ipq8074-qmp-usb3-phy - - qcom,ipq9574-qmp-usb3-phy - - qcom,msm8996-qmp-usb3-phy - - qcom,qcs8300-qmp-usb3-uni-phy - - qcom,qdu1000-qmp-usb3-uni-phy - - qcom,sa8775p-qmp-usb3-uni-phy - - qcom,sc8180x-qmp-usb3-uni-phy - - qcom,sc8280xp-qmp-usb3-uni-phy - - qcom,sdm845-qmp-usb3-uni-phy - - qcom,sdx55-qmp-usb3-uni-phy - - qcom,sdx65-qmp-usb3-uni-phy - - qcom,sdx75-qmp-usb3-uni-phy - - qcom,sm8150-qmp-usb3-uni-phy - - qcom,sm8250-qmp-usb3-uni-phy - - qcom,sm8350-qmp-usb3-uni-phy - - qcom,x1e80100-qmp-usb3-uni-phy + oneOf: + - items: + - enum: + - qcom,glymur-qmp-usb3-uni-phy + - qcom,ipq5424-qmp-usb3-phy + - qcom,ipq6018-qmp-usb3-phy + - qcom,ipq8074-qmp-usb3-phy + - qcom,ipq9574-qmp-usb3-phy + - qcom,msm8996-qmp-usb3-phy + - qcom,qcs8300-qmp-usb3-uni-phy + - qcom,qdu1000-qmp-usb3-uni-phy + - qcom,sa8775p-qmp-usb3-uni-phy + - qcom,sc8180x-qmp-usb3-uni-phy + - qcom,sc8280xp-qmp-usb3-uni-phy + - qcom,sdm845-qmp-usb3-uni-phy + - qcom,sdx55-qmp-usb3-uni-phy + - qcom,sdx65-qmp-usb3-uni-phy + - qcom,sdx75-qmp-usb3-uni-phy + - qcom,sm8150-qmp-usb3-uni-phy + - qcom,sm8250-qmp-usb3-uni-phy + - qcom,sm8350-qmp-usb3-uni-phy + - qcom,x1e80100-qmp-usb3-uni-phy + - items: + - enum: + - qcom,ipq5210-qmp-usb3-phy + - const: qcom,ipq9574-qmp-usb3-phy reg: maxItems: 1 From eaf84ff673409fa3dfc390c6afb53b641ee5acba Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:04 +0000 Subject: [PATCH 3396/5214] pinctrl: sunxi: a523: Remove unneeded IRQ remuxing flag The Allwinner A10 and H3 SoCs cannot read the state of a GPIO line when that line is muxed for IRQ triggering (muxval 6), but only if it's explicitly muxed for GPIO input (muxval 0). Other SoCs do not show this behaviour, so we added a optional workaround, triggered by a quirk bit, which triggers remuxing the pin when it's configured for IRQ, while we need to read its value. For some reasons this quirk flag was copied over to newer SoCs, even though they don't show this behaviour, and the GPIO data register reflects the true GPIO state even with a pin muxed to IRQ trigger. Remove the unneeded quirk from the A523 family, where it's definitely not needed (confirmed by experiments), and where it actually breaks, because the workaround is not compatible with the newer generation pinctrl IP used in that chip. Together with a DT change this fixes GPIO IRQ operation on the A523 family of SoCs, as for instance used for the SD card detection. Signed-off-by: Andre Przywara Fixes: b8a51e95b376 ("pinctrl: sunxi: Add support for the secondary A523 GPIO ports") Reviewed-by: Jernej Skrabec Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c | 1 - drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c index 69cd2b4ebd7d..462aa1c4a5fa 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c @@ -26,7 +26,6 @@ static const u8 a523_r_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_r_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_r_irq_bank_map), .irq_bank_map = a523_r_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, .pin_base = PL_BASE, }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c index 7d2308c37d29..b6f78f1f30ac 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c @@ -26,7 +26,6 @@ static const u8 a523_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_irq_bank_map), .irq_bank_map = a523_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, }; From 11f25ac56372560762ae1cd6f8880d903f6d5825 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:05 +0000 Subject: [PATCH 3397/5214] dt-bindings: pinctrl: sun55i-a523: increase IRQ banks number The Allwinner A523 SoC implements 10 GPIO banks in the first pinctrl instance, but it skips the first bank (PortA), so their index goes from 1 to 10. The same is actually true for the IRQ banks: there are registers for 11 banks, though the first bank is not implemented (RAZ/WI). In contrast to previous SoCs, the count of the IRQ banks starts with this first unimplemented bank, so we need to provide an interrupt for it. And indeed the A523 user manual lists an interrupt number for PortA, so we need to increase the maximum number of interrupts per pin controller to 11, to be able to assign the correct interrupt number for each bank. Signed-off-by: Andre Przywara Acked-by: Rob Herring (Arm) Reviewed-by: Jernej Skrabec Reviewed-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- .../bindings/pinctrl/allwinner,sun55i-a523-pinctrl.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sun55i-a523-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/allwinner,sun55i-a523-pinctrl.yaml index 154e03da8ce9..f87b8274cc37 100644 --- a/Documentation/devicetree/bindings/pinctrl/allwinner,sun55i-a523-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sun55i-a523-pinctrl.yaml @@ -34,7 +34,7 @@ properties: interrupts: minItems: 2 - maxItems: 10 + maxItems: 11 description: One interrupt per external interrupt bank supported on the controller, sorted by bank number ascending order. @@ -61,7 +61,7 @@ properties: bank found in the controller $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 2 - maxItems: 10 + maxItems: 11 patternProperties: # It's pretty scary, but the basic idea is that: @@ -130,8 +130,8 @@ allOf: then: properties: interrupts: - minItems: 10 - maxItems: 10 + minItems: 11 + maxItems: 11 - if: properties: From f79413b2dde10829a1b526543e725dd5c1847e9a Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:46 +0200 Subject: [PATCH 3398/5214] KVM: s390: Silence potential warnings in _gmap_crstep_xchg_atomic() While dat_crstep_xchg_atomic() is marked as __must_check, in this particular case the return value should be ignored. Silence potential compiler warnings with a pointless check, and add a comment to explain the situation. Fixes: d1adc098ce08 ("KVM: s390: Fix _gmap_crstep_xchg_atomic()") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-2-imbrenda@linux.ibm.com> --- arch/s390/kvm/gmap.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 5374f21aaf8d..20881e3ce9d8 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -279,7 +279,16 @@ static inline bool __must_check _gmap_crstep_xchg_atomic(struct gmap *gmap, unio gmap_handle_vsie_unshadow_event(gmap, gfn); else _gmap_handle_vsie_unshadow_event(gmap, gfn); - dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, gmap->asce); + if (!dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, gmap->asce)) + return false; + /* + * Return false even if the swap was successful, as it only + * indicates that the best effort clearing of the vsie_notif + * bit was successful. The caller will have to try again + * regardless, since the desired value has not been set. + * This pointless check is needed to silence a potential + * __must_check warning. + */ return false; } if (!oldcrste.s.fc1.d && newcrste.s.fc1.d && !newcrste.s.fc1.s) From 5670b7f927f8d98685f3f5873dbf9f8d7a5a63f3 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:47 +0200 Subject: [PATCH 3399/5214] KVM: s390: Fix unlikely race in try_get_locked_pte() Fix an unlikely race in try_get_locked_pte(), which could have happened if puds or pmds get unmapped between the p?dp_get() and p?d_offset() functions. Fixes: 89fa757931dc ("KVM: s390: Avoid potentially sleeping while atomic when zapping pages") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-3-imbrenda@linux.ibm.com> --- arch/s390/mm/gmap_helpers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c index 1cfe4724fbe2..ee3f37af8aee 100644 --- a/arch/s390/mm/gmap_helpers.c +++ b/arch/s390/mm/gmap_helpers.c @@ -51,15 +51,15 @@ pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long vmaddr, spinlock_t pgd = pgdp_get(pgdp); if (pgd_none(pgd) || !pgd_present(pgd)) return NULL; - p4dp = p4d_offset(pgdp, vmaddr); + p4dp = p4d_offset_lockless(pgdp, pgd, vmaddr); p4d = p4dp_get(p4dp); if (p4d_none(p4d) || !p4d_present(p4d)) return NULL; - pudp = pud_offset(p4dp, vmaddr); + pudp = pud_offset_lockless(p4dp, p4d, vmaddr); pud = pudp_get(pudp); if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud)) return NULL; - pmdp = pmd_offset(pudp, vmaddr); + pmdp = pmd_offset_lockless(pudp, pud, vmaddr); pmd = pmdp_get_lockless(pmdp); if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd)) return NULL; From 505fcce0c64957f475f9b11fb1403821b7f33e7e Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:48 +0200 Subject: [PATCH 3400/5214] KVM: s390: vsie: Fix allocation of struct vsie_rmap The allocation size for struct vsie_rmap in kvm_s390_mmu_cache_topup() was wrong due to a copy-paste error. Fix it by using the type name. Fixes: 12f2f61a9e1a ("KVM: s390: KVM page table management functions: allocation") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-4-imbrenda@linux.ibm.com> --- arch/s390/kvm/dat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c index 4a41c0247ffa..a4fe664f65ee 100644 --- a/arch/s390/kvm/dat.c +++ b/arch/s390/kvm/dat.c @@ -45,7 +45,7 @@ int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc) mc->pts[mc->n_pts] = o; } for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) { - o = kzalloc_obj(*mc->rmaps[0], GFP_KERNEL_ACCOUNT); + o = kzalloc_obj(struct vsie_rmap, GFP_KERNEL_ACCOUNT); if (!o) return -ENOMEM; mc->rmaps[mc->n_rmaps] = o; From 668e70cc545e2659f3c4adad20a0883533042473 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:49 +0200 Subject: [PATCH 3401/5214] KVM: s390: vsie: Add missing radix_tree_preload() in _gaccess_shadow_fault() Add missing radix_tree_preload() in _gaccess_shadow_fault() to guarantee forward progress. The core of _gaccess_shadow_fault() has been split into ___gaccess_shadow_fault() in order to simplify locking. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-5-imbrenda@linux.ibm.com> --- arch/s390/kvm/gaccess.c | 59 +++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 20e28b183c1a..0584fc91606f 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1582,35 +1582,48 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg, return _do_shadow_crste(sg, saddr, host, table, entries + LEVEL_MEM, w->p); } -static inline int _gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, - unsigned long seq, struct pgtwalk *walk) +static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, + unsigned long seq, struct pgtwalk *walk) { struct gmap *parent; int rc; + if (kvm_s390_array_needs_retry_safe(vcpu->kvm, seq, walk->raw_entries)) + return -EAGAIN; + parent = READ_ONCE(sg->parent); + if (!parent) + return -EAGAIN; + scoped_guard(spinlock, &parent->children_lock) { + if (READ_ONCE(sg->parent) != parent) + return -EAGAIN; + sg->invalidated = false; + rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk); + } + if (!rc) + kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false); + return rc; +} + +static inline int _gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, + unsigned long seq, struct pgtwalk *walk) +{ + int rc; + if (kvm_s390_array_needs_retry_unsafe(vcpu->kvm, seq, walk->raw_entries)) return -EAGAIN; -again: - rc = kvm_s390_mmu_cache_topup(vcpu->arch.mc); - if (rc) - return rc; - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) { - if (kvm_s390_array_needs_retry_safe(vcpu->kvm, seq, walk->raw_entries)) - return -EAGAIN; - parent = READ_ONCE(sg->parent); - if (!parent) - return -EAGAIN; - scoped_guard(spinlock, &parent->children_lock) { - if (READ_ONCE(sg->parent) != parent) - return -EAGAIN; - sg->invalidated = false; - rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk); - } - if (rc == -ENOMEM) - goto again; - if (!rc) - kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false); - } + + do { + rc = kvm_s390_mmu_cache_topup(vcpu->arch.mc); + if (rc) + return rc; + rc = radix_tree_preload(GFP_KERNEL); + if (rc) + return rc; + scoped_guard(read_lock, &vcpu->kvm->mmu_lock) + rc = ___gaccess_shadow_fault(vcpu, sg, saddr, seq, walk); + radix_tree_preload_end(); + } while (rc == -ENOMEM); + return rc; } From abeb7eb57f1671d9185ddf11236c784f07bdb928 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 11 Jun 2026 12:48:50 +0200 Subject: [PATCH 3402/5214] KVM: s390: vsie: Use mmu cache to allocate rmap Use kvm_s390_mmu_cache_alloc_rmap() to allocate the rmap in gmap_insert_rmap(), instead of a normal kzalloc_obj() with GFP_ATOMIC. This guarantees forward progress. Fixes: a2c17f9270cc ("KVM: s390: New gmap code") CC: stable@vger.kernel.org # 7.1 Signed-off-by: Claudio Imbrenda Message-ID: <20260611104850.110313-6-imbrenda@linux.ibm.com> --- arch/s390/kvm/gaccess.c | 16 ++++++++-------- arch/s390/kvm/gmap.c | 7 ++++--- arch/s390/kvm/gmap.h | 3 ++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 0584fc91606f..36102b2727fb 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1419,8 +1419,8 @@ static int walk_guest_tables(struct gmap *sg, unsigned long saddr, struct pgtwal return kvm_s390_get_guest_page(kvm, entries + LEVEL_MEM, table.pte.pfra, wr); } -static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union pte *ptep, - struct guest_fault *f, bool p) +static int _do_shadow_pte(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gpa_t raddr, + union pte *ptep_h, union pte *ptep, struct guest_fault *f, bool p) { union pgste pgste; union pte newpte; @@ -1430,7 +1430,7 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union lockdep_assert_held(&sg->parent->children_lock); scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, f->gfn, gpa_to_gfn(raddr), TABLE_TYPE_PAGE_TABLE); + rc = gmap_insert_rmap(mc, sg, f->gfn, gpa_to_gfn(raddr), TABLE_TYPE_PAGE_TABLE); if (rc) return rc; @@ -1462,8 +1462,8 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union return 0; } -static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, union crste *table, - struct guest_fault *f, bool p) +static int _do_shadow_crste(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gpa_t raddr, + union crste *host, union crste *table, struct guest_fault *f, bool p) { union crste newcrste, oldcrste; unsigned long mask; @@ -1476,7 +1476,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni mask = is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK; r_gfn = gpa_to_gfn(raddr) & mask; scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, f->gfn & mask, r_gfn, host->h.tt); + rc = gmap_insert_rmap(mc, sg, f->gfn & mask, r_gfn, host->h.tt); if (rc) return rc; @@ -1578,8 +1578,8 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg, if (KVM_BUG_ON(l > TABLE_TYPE_REGION3, sg->kvm)) return -EFAULT; if (l == TABLE_TYPE_PAGE_TABLE) - return _do_shadow_pte(sg, saddr, ptep_h, ptep, entries + LEVEL_MEM, w->p); - return _do_shadow_crste(sg, saddr, host, table, entries + LEVEL_MEM, w->p); + return _do_shadow_pte(mc, sg, saddr, ptep_h, ptep, entries + LEVEL_MEM, w->p); + return _do_shadow_crste(mc, sg, saddr, host, table, entries + LEVEL_MEM, w->p); } static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, gpa_t saddr, diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 52d55ddea8d4..1d289f8fa3b2 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -1000,7 +1000,8 @@ int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interr return 0; } -int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level) +int gmap_insert_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, + gfn_t r_gfn, int level) { struct vsie_rmap *rmap __free(kvfree) = NULL; struct vsie_rmap *temp; @@ -1010,7 +1011,7 @@ int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level) KVM_BUG_ON(!is_shadow(sg), sg->kvm); lockdep_assert_held(&sg->host_to_rmap_lock); - rmap = kzalloc_obj(*rmap, GFP_ATOMIC); + rmap = kvm_s390_mmu_cache_alloc_rmap(mc); if (!rmap) return -ENOMEM; @@ -1057,7 +1058,7 @@ int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gf if (level <= TABLE_TYPE_REGION1) { bitmask = -1UL << (8 + 11 * level); scoped_guard(spinlock, &sg->host_to_rmap_lock) - rc = gmap_insert_rmap(sg, p_gfn, r_gfn & bitmask, level); + rc = gmap_insert_rmap(mc, sg, p_gfn, r_gfn & bitmask, level); } if (rc) return rc; diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 20881e3ce9d8..1c040472f56d 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -100,7 +100,8 @@ int gmap_ucas_map(struct gmap *gmap, gfn_t p_gfn, gfn_t c_gfn, unsigned long cou void gmap_ucas_unmap(struct gmap *gmap, gfn_t c_gfn, unsigned long count); int gmap_enable_skeys(struct gmap *gmap); int gmap_pv_destroy_range(struct gmap *gmap, gfn_t start, gfn_t end, bool interruptible); -int gmap_insert_rmap(struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, int level); +int gmap_insert_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, + gfn_t r_gfn, int level); int gmap_protect_rmap(struct kvm_s390_mmu_cache *mc, struct gmap *sg, gfn_t p_gfn, gfn_t r_gfn, kvm_pfn_t pfn, int level, bool wr); void gmap_set_cmma_all_dirty(struct gmap *gmap); From 711104ab5ed500f155e4a53c56571b9b24a9a9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Figza=C5=82?= Date: Wed, 10 Jun 2026 18:49:42 +0200 Subject: [PATCH 3403/5214] platform/x86: asus-wmi: add keystone dongle support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ASUS Keystone is a physical NFC-like dongle that slots into supported ASUS laptops. The EC fires WMI notify code 0xB4 on insert/remove events. Expose the current insert state via a sysfs attribute by querying WMI device ID 0x00120091 (DSTS). This devid does not follow the standard DSTS convention: PRESENCE_BIT (0x00010000) encodes the insert state rather than feature presence, and STATUS_BIT is never set. Presence of a keystone slot is detected by a successful DSTS call. Reviewed-by: Denis Benato Signed-off-by: Dariusz Figzał Link: https://patch.msgid.link/20260610164942.74956-1-dariuszfigzal@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../ABI/testing/sysfs-platform-asus-wmi | 9 +++ drivers/platform/x86/asus-wmi.c | 65 +++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 7 ++ 3 files changed, 81 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 89acb6638df8..f9825c6150b5 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -58,6 +58,15 @@ Description: * 1 - overboost, * 2 - silent +What: /sys/devices/platform//keystone +Date: Jun 2026 +KernelVersion: 7.2 +Contact: "Dariusz Figzał" +Description: + Reports the Keystone dongle insert state (read-only): + * 0 - not inserted + * 1 - inserted + What: /sys/devices/platform//gpu_mux_mode Date: Aug 2022 KernelVersion: 6.1 diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 80144c412b90..3c9ef826551d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,7 @@ module_param(fnlock_default, bool, 0444); #define NOTIFY_KBD_TTP 0xae #define NOTIFY_LID_FLIP 0xfa #define NOTIFY_LID_FLIP_ROG 0xbd +#define NOTIFY_KEYSTONE 0xb4 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -279,6 +280,8 @@ struct asus_wmi { u32 tablet_switch_dev_id; bool tablet_switch_inverted; + bool keystone_available; + enum fan_type fan_type; enum fan_type gpu_fan_type; enum fan_type mid_fan_type; @@ -646,6 +649,55 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); } +/* Keystone *******************************************************************/ + +static int keystone_check_present(struct asus_wmi *asus) +{ + u32 retval; + int err; + + asus->keystone_available = false; + + /* + * Use a raw devstate call rather than asus_wmi_dev_is_present(). + * For this devid, PRESENCE_BIT encodes current insert state, not + * feature presence, so asus_wmi_dev_is_present() would return false + * whenever the dongle is absent at boot, even on machines that have + * a keystone slot. + * -ENODEV means the firmware doesn't know this devid at all. + * retval is not examined here, only the return code matters. + */ + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval); + if (err == -ENODEV) + return 0; + if (err) + return err; + + asus->keystone_available = true; + return 0; +} + +static ssize_t keystone_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + u32 retval; + int err; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", !!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)); +} + +static DEVICE_ATTR_RO(keystone); + +static void asus_wmi_keystone_notify(struct asus_wmi *asus) +{ + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "keystone"); +} + /* Input **********************************************************************/ static void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value) { @@ -4575,6 +4627,12 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + if (code == NOTIFY_KEYSTONE) { + if (asus->keystone_available) + asus_wmi_keystone_notify(asus); + return; + } + if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) { if (asus->fan_boost_mode_available) fan_boost_mode_switch_next(asus); @@ -4698,6 +4756,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, + &dev_attr_keystone.attr, #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) &dev_attr_charge_mode.attr, &dev_attr_egpu_enable.attr, @@ -4741,6 +4800,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_ALS_ENABLE; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; + else if (attr == &dev_attr_keystone.attr) + ok = asus->keystone_available; #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) if (attr == &dev_attr_charge_mode.attr) @@ -5081,6 +5142,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform_profile_setup; + err = keystone_check_present(asus); + if (err) + dev_warn(&pdev->dev, "Failed to check Keystone presence: %d\n", err); + err = asus_wmi_sysfs_init(asus->platform_device); if (err) goto fail_sysfs; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 554f41b827e1..c29962d5baac 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -147,6 +147,13 @@ #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 #define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026 +/* Keystone dongle insert/remove state. + * PRESENCE_BIT (0x00010000) encodes insert state: + * 0x00010000 = inserted, 0x00000000 = absent. STATUS_BIT is never set. + * 0xFFFFFFFE means no keystone slot on this machine. + */ +#define ASUS_WMI_DEVID_KEYSTONE 0x00120091 + /* TUF laptop RGB modes/colours */ #define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 #define ASUS_WMI_DEVID_TUF_RGB_MODE2 0x0010005A From bdc95d7e8de3eefa9fc062302625259c0b79136d Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:32 +0300 Subject: [PATCH 3404/5214] pinctrl: airoha: an7581: add missed gpio32 pin group gpio32 pin group is missed for an7581 SoC. This patch add it. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index e5a6a60fb3c6..f0689ad8646f 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -539,6 +539,7 @@ static const int en7581_gpio28_pins[] = { 41 }; static const int en7581_gpio29_pins[] = { 42 }; static const int en7581_gpio30_pins[] = { 43 }; static const int en7581_gpio31_pins[] = { 44 }; +static const int en7581_gpio32_pins[] = { 45 }; static const int en7581_gpio33_pins[] = { 46 }; static const int en7581_gpio34_pins[] = { 47 }; static const int en7581_gpio35_pins[] = { 48 }; @@ -623,6 +624,7 @@ static const struct pingroup en7581_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio29", en7581_gpio29), PINCTRL_PIN_GROUP("gpio30", en7581_gpio30), PINCTRL_PIN_GROUP("gpio31", en7581_gpio31), + PINCTRL_PIN_GROUP("gpio32", en7581_gpio32), PINCTRL_PIN_GROUP("gpio33", en7581_gpio33), PINCTRL_PIN_GROUP("gpio34", en7581_gpio34), PINCTRL_PIN_GROUP("gpio35", en7581_gpio35), From 81cc2285cea84e3ed8688d353e1250cf8899c80a Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:33 +0300 Subject: [PATCH 3405/5214] pinctrl: airoha: an7583: add missed gpio32 pin group gpio32 pin group is missed for an7583 SoC. This patch add it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index f0689ad8646f..6ad44545675d 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -758,6 +758,7 @@ static const int an7583_gpio28_pins[] = { 30 }; static const int an7583_gpio29_pins[] = { 31 }; static const int an7583_gpio30_pins[] = { 32 }; static const int an7583_gpio31_pins[] = { 33 }; +static const int an7583_gpio32_pins[] = { 34 }; static const int an7583_gpio33_pins[] = { 35 }; static const int an7583_gpio34_pins[] = { 36 }; static const int an7583_gpio35_pins[] = { 37 }; @@ -836,6 +837,7 @@ static const struct pingroup an7583_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio29", an7583_gpio29), PINCTRL_PIN_GROUP("gpio30", an7583_gpio30), PINCTRL_PIN_GROUP("gpio31", an7583_gpio31), + PINCTRL_PIN_GROUP("gpio32", an7583_gpio32), PINCTRL_PIN_GROUP("gpio33", an7583_gpio33), PINCTRL_PIN_GROUP("gpio34", an7583_gpio34), PINCTRL_PIN_GROUP("gpio35", an7583_gpio35), From 08a39a0617ff32a7c3962bbc38a9eee41b14659a Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:34 +0300 Subject: [PATCH 3406/5214] pinctrl: airoha: an7581: fix misprint in gpio19 pinconf Pin 32 (gpio19) duplicate pinconf settings of pin 31. Fix it using a proper bit number in the configuration register. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 6ad44545675d..0725a7ece25d 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -1798,7 +1798,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), @@ -1915,7 +1915,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), @@ -2032,7 +2032,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), @@ -2149,7 +2149,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), From a7f3e2b7730fc1d6c7431af49e0dc1ee97589795 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:35 +0300 Subject: [PATCH 3407/5214] pinctrl: airoha: an7583: fix misprint in gpio19 pinconf Pin 21 (gpio19) duplicate pinconf settings of pin 20. Fix it using a proper bit number in the configuration register. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 0725a7ece25d..a966144bad21 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -1851,7 +1851,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(22)), @@ -1968,7 +1968,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(22)), @@ -2085,7 +2085,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(22)), @@ -2202,7 +2202,7 @@ static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(16)), PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(17)), PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(18)), - PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(19)), PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(20)), PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(21)), PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(22)), From e20c85c79cc2f45b87eb3dab38d4c641bbf83ed6 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:36 +0300 Subject: [PATCH 3408/5214] pinctrl: airoha: an7581: fix incorrect led mapping in phy4_led1 pin function phy4_led1 pin function maps led incorrectly. It uses the same map as phy3_led1. PHY{X} should map to LAN{N}_PHY_LED_MAP(X-1). Fixes: 579839c9548c ("pinctrl: airoha: convert PHY LED GPIO to macro") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index a966144bad21..bdfeea390998 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -1622,13 +1622,13 @@ static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, - LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), + LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, - LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), + LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, - LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, - LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), }; static const struct airoha_pinctrl_func_group an7583_phy1_led0_func_group[] = { From a3602577fdfc49c6dc08d67304426d5ef6d7dec6 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:37 +0300 Subject: [PATCH 3409/5214] pinctrl: airoha: an7583: fix incorrect led mapping in phy4_led1 pin function phy4_led1 pin function maps led incorrectly. It uses the same map as phy3_led1. PHY{X} should map to LAN{N}_PHY_LED_MAP(X-1). Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index bdfeea390998..4b83aa64b49d 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -1710,13 +1710,13 @@ static const struct airoha_pinctrl_func_group an7583_phy3_led1_func_group[] = { static const struct airoha_pinctrl_func_group an7583_phy4_led1_func_group[] = { AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, - LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), + LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, - LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), + LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, - LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, - LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), }; static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { From 08a5af468e613b6d8cd9725d284c9e6be288d364 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:38 +0300 Subject: [PATCH 3410/5214] pinctrl: airoha: fix pwm pin function for an7581 and an7583 AN7581 have 47 valid GPIOs only (gpio0-gpio46), so gpio47 is a fiction. AN7583 have 49 valid GPIOs (gpio0-gpio48), so gpio48 is missed To fix an issue * create AN7583 specific pwm pin function, * remove gpio47 from AN7581 pwm pin function. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 74 ++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 4b83aa64b49d..6b3b78801097 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -906,7 +906,30 @@ static const char *const pwm_groups[] = { "gpio0", "gpio1", "gpio40", "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", - "gpio46", "gpio47" }; + "gpio46" }; +static const char *const an7583_pwm_groups[] = { "gpio0", "gpio1", + "gpio2", "gpio3", + "gpio4", "gpio5", + "gpio6", "gpio7", + "gpio8", "gpio9", + "gpio10", "gpio11", + "gpio12", "gpio13", + "gpio14", "gpio15", + "gpio16", "gpio17", + "gpio18", "gpio19", + "gpio20", "gpio21", + "gpio22", "gpio23", + "gpio24", "gpio25", + "gpio26", "gpio27", + "gpio28", "gpio29", + "gpio30", "gpio31", + "gpio36", "gpio37", + "gpio38", "gpio39", + "gpio40", "gpio41", + "gpio42", "gpio43", + "gpio44", "gpio45", + "gpio46", "gpio47", + "gpio48" }; static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", "gpio35", "gpio42" }; static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", @@ -1504,7 +1527,54 @@ static const struct airoha_pinctrl_func_group pwm_func_group[] = { AIROHA_PINCTRL_PWM_EXT("gpio44", GPIO44_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio45", GPIO45_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio46", GPIO46_FLASH_MODE_CFG), +}; + +static const struct airoha_pinctrl_func_group an7583_pwm_func_group[] = { + AIROHA_PINCTRL_PWM("gpio0", GPIO0_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio1", GPIO1_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio2", GPIO2_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio3", GPIO3_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio4", GPIO4_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio5", GPIO5_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio6", GPIO6_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio7", GPIO7_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio8", GPIO8_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio9", GPIO9_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio10", GPIO10_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio11", GPIO11_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio12", GPIO12_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio13", GPIO13_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio14", GPIO14_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM("gpio15", GPIO15_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio16", GPIO16_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio17", GPIO17_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio18", GPIO18_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio19", GPIO19_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio20", GPIO20_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio21", GPIO21_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio22", GPIO22_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio23", GPIO23_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio24", GPIO24_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio25", GPIO25_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio26", GPIO26_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio27", GPIO27_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio28", GPIO28_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio29", GPIO29_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio30", GPIO30_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio31", GPIO31_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio36", GPIO36_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio37", GPIO37_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio38", GPIO38_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio39", GPIO39_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio40", GPIO40_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio41", GPIO41_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio42", GPIO42_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio43", GPIO43_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio44", GPIO44_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio45", GPIO45_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio46", GPIO46_FLASH_MODE_CFG), AIROHA_PINCTRL_PWM_EXT("gpio47", GPIO47_FLASH_MODE_CFG), + AIROHA_PINCTRL_PWM_EXT("gpio48", GPIO48_FLASH_MODE_CFG), }; #define AIROHA_PINCTRL_PHY_LED0(gpio, mux_val, map_mask, map_val) \ @@ -1759,7 +1829,7 @@ static const struct airoha_pinctrl_func an7583_pinctrl_funcs[] = { PINCTRL_FUNC_DESC("emmc", emmc), PINCTRL_FUNC_DESC("pnand", pnand), PINCTRL_FUNC_DESC("pcie_reset", an7583_pcie_reset), - PINCTRL_FUNC_DESC("pwm", pwm), + PINCTRL_FUNC_DESC("pwm", an7583_pwm), PINCTRL_FUNC_DESC("phy1_led0", an7583_phy1_led0), PINCTRL_FUNC_DESC("phy2_led0", an7583_phy2_led0), PINCTRL_FUNC_DESC("phy3_led0", an7583_phy3_led0), From abf92c45cc82e9a01aa581f9fbc790e78250a4d4 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:39 +0300 Subject: [PATCH 3411/5214] pinctrl: airoha: an7583: fix gpio21 pin group gpio21 pin group refers to gpio22 pin, this is wrong. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 6b3b78801097..097bf939b868 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -748,7 +748,7 @@ static const int an7583_gpio17_pins[] = { 19 }; static const int an7583_gpio18_pins[] = { 20 }; static const int an7583_gpio19_pins[] = { 21 }; static const int an7583_gpio20_pins[] = { 22 }; -static const int an7583_gpio21_pins[] = { 24 }; +static const int an7583_gpio21_pins[] = { 23 }; static const int an7583_gpio23_pins[] = { 25 }; static const int an7583_gpio24_pins[] = { 26 }; static const int an7583_gpio25_pins[] = { 27 }; From 9ef86358855d5fd89db019ace33c097d2d752b9d Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:40 +0300 Subject: [PATCH 3412/5214] pinctrl: airoha: an7583: add missed gpio22 pin group gpio22 pin group is missed, fix it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 097bf939b868..7dfc8e3c083f 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -749,6 +749,7 @@ static const int an7583_gpio18_pins[] = { 20 }; static const int an7583_gpio19_pins[] = { 21 }; static const int an7583_gpio20_pins[] = { 22 }; static const int an7583_gpio21_pins[] = { 23 }; +static const int an7583_gpio22_pins[] = { 24 }; static const int an7583_gpio23_pins[] = { 25 }; static const int an7583_gpio24_pins[] = { 26 }; static const int an7583_gpio25_pins[] = { 27 }; @@ -828,6 +829,7 @@ static const struct pingroup an7583_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio19", an7583_gpio19), PINCTRL_PIN_GROUP("gpio20", an7583_gpio20), PINCTRL_PIN_GROUP("gpio21", an7583_gpio21), + PINCTRL_PIN_GROUP("gpio22", an7583_gpio22), PINCTRL_PIN_GROUP("gpio23", an7583_gpio23), PINCTRL_PIN_GROUP("gpio24", an7583_gpio24), PINCTRL_PIN_GROUP("gpio25", an7583_gpio25), From dbe28a2a22a3455d1adbf9fd61d3537603ac3072 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:41 +0300 Subject: [PATCH 3413/5214] pinctrl: airoha: an7583: fix phy1_led1 pin function phy1_led1 pin function wrongly refers to gpio1 instead of gpio11. Fix it. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 7dfc8e3c083f..3a452abc0b87 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -1754,7 +1754,7 @@ static const struct airoha_pinctrl_func_group an7583_phy1_led1_func_group[] = { LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), - AIROHA_PINCTRL_PHY_LED1("gpio1", GPIO_LAN3_LED1_MODE_MASK, + AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), }; From 7b87a686a5ab138b3f8ca3d7e3489d8371c02695 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:42 +0300 Subject: [PATCH 3414/5214] pinctrl: airoha: an7583: remove undefined groups from pcm_spi pin function pcm_spi_int, pcm_spi_cs2, pcm_spi_cs3, pcm_spi_cs4 pin groups are not defined, so pcm_spi function can't be applied to these groups. Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Linus Walleij --- drivers/pinctrl/airoha/pinctrl-airoha.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c index 3a452abc0b87..04b4424c688b 100644 --- a/drivers/pinctrl/airoha/pinctrl-airoha.c +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -877,10 +877,8 @@ static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", "pcm_spi_cs2_p156", "pcm_spi_cs2_p128", "pcm_spi_cs3", "pcm_spi_cs4" }; -static const char *const an7583_pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", - "pcm_spi_rst", "pcm_spi_cs1", - "pcm_spi_cs2", "pcm_spi_cs3", - "pcm_spi_cs4" }; +static const char *const an7583_pcm_spi_groups[] = { "pcm_spi", + "pcm_spi_rst", "pcm_spi_cs1" }; static const char *const i2s_groups[] = { "i2s" }; static const char *const emmc_groups[] = { "emmc" }; static const char *const pnand_groups[] = { "pnand" }; From 6b3bbe770f4ca0439710b7c42f88b9f6eeebabd0 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:21:30 +0200 Subject: [PATCH 3415/5214] platform/x86: asus-armoury: add support for G614PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP power limits and fan curve requirements for the ASUS ROG Strix G16 G614PR laptop model. The ASUS ROG Strix G16 G614PR requires specific AC/DC power limits (PPT PL1/PL2/PL3, dynamic boost, and NV TGP targets) to function correctly under various power profiles. Without these limits, the Asus Armoury driver cannot configure the correct power envelopes or enable custom fan curves, leading to suboptimal performance or noise management. This patch adds the corresponding DMI board name matching entry ("G614PR") under the power_limits table in asus-armoury.h, populating the AC and DC limits based on the platform's hardware specification. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Reviewed-by: Denis Benato Link: https://patch.msgid.link/20260610152130.25892-1-scardracs@disroot.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 692978b61959..5be34b6a92db 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1898,6 +1898,40 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G614PR"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 30, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_def = 110, + .ppt_pl2_sppt_max = 125, + .ppt_pl3_fppt_min = 65, + .ppt_pl3_fppt_def = 110, + .ppt_pl3_fppt_max = 125, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_tgp_min = 65, + .nv_tgp_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 75, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "G615LR"), From 25fbff92dd485eb15eaf1aa7252a99fcaabfef5e Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Jun 2026 17:12:34 -0700 Subject: [PATCH 3416/5214] platform/x86: dell-privacy: correct CONFIG_DELL_WMI_PRIVACY macro name in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comments in drivers/platform/x86/dell/dell-wmi-privacy.h incorrectly refer to CONFIG_DELL_PRIVACY instead of CONFIG_DELL_WMI_PRIVACY. Correct them. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260611001238.391045-1-enelsonmoore@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-privacy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.h b/drivers/platform/x86/dell/dell-wmi-privacy.h index 50c9b943dd47..ca242e1fe53f 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.h +++ b/drivers/platform/x86/dell/dell-wmi-privacy.h @@ -13,7 +13,7 @@ bool dell_privacy_has_mic_mute(void); bool dell_privacy_process_event(int type, int code, int status); int dell_privacy_register_driver(void); void dell_privacy_unregister_driver(void); -#else /* CONFIG_DELL_PRIVACY */ +#else /* CONFIG_DELL_WMI_PRIVACY */ static inline bool dell_privacy_has_mic_mute(void) { return false; @@ -32,5 +32,5 @@ static inline int dell_privacy_register_driver(void) static inline void dell_privacy_unregister_driver(void) { } -#endif /* CONFIG_DELL_PRIVACY */ +#endif /* CONFIG_DELL_WMI_PRIVACY */ #endif From 7156d900a22e9240741ba2b182d573239fab7e74 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 8 Jun 2026 09:41:21 +0000 Subject: [PATCH 3417/5214] dt-bindings: pinctrl: tegra238: add missing AON pin groups Add 24 pin groups, and their matching drive groups, on ports EE, FF, GG and HH to the Tegra238 AON pinmux binding. These groups are present on the AON pin controller, so device trees that mux these pins through it validate against the schema. Fixes: 9323f8a0e12c ("dt-bindings: pinctrl: Document Tegra238 pin controllers") Signed-off-by: Prathamesh Shete Acked-by: Conor Dooley Signed-off-by: Linus Walleij --- .../pinctrl/nvidia,tegra238-pinmux-aon.yaml | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml index ab9264d87c88..2b2e1a82880e 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra238-pinmux-aon.yaml @@ -38,8 +38,16 @@ patternProperties: gen2_i2c_sda_pdd0, gen8_i2c_scl_pdd1, gen8_i2c_sda_pdd2, touch_clk_pdd3, dmic1_clk_pdd4, dmic1_dat_pdd5, soc_gpio19_pdd6, pwm2_pdd7, - pwm3_pee0, pwm7_pee1, - # drive groups (ordered PAA, PBB, PCC, PDD, PEE) + pwm3_pee0, pwm7_pee1, soc_gpio49_pee2, + soc_gpio82_pee3, soc_gpio50_pee4, soc_gpio83_pee5, + soc_gpio69_pff0, soc_gpio70_pff1, soc_gpio71_pff2, + soc_gpio72_pff3, soc_gpio73_pff4, soc_gpio74_pff5, + soc_gpio80_pff6, soc_gpio76_pff7, soc_gpio77_pgg0, + soc_gpio84_pgg1, uart2_tx_pgg2, uart2_rx_pgg3, + uart2_rts_pgg4, uart2_cts_pgg5, soc_gpio85_pgg6, + uart5_tx_pgg7, uart5_rx_phh0, uart5_rts_phh1, + uart5_cts_phh2, soc_gpio86_phh3, + # drive groups (ordered PAA, PBB, PCC, PDD, PEE, PFF, PGG, PHH) drive_bootv_ctl_n_paa0, drive_soc_gpio00_paa1, drive_vcomp_alert_paa2, drive_pwm1_paa3, drive_batt_oc_paa4, drive_soc_gpio04_paa5, @@ -53,7 +61,19 @@ patternProperties: drive_gen8_i2c_sda_pdd2, drive_touch_clk_pdd3, drive_dmic1_clk_pdd4, drive_dmic1_dat_pdd5, drive_soc_gpio19_pdd6, drive_pwm2_pdd7, - drive_pwm3_pee0, drive_pwm7_pee1 ] + drive_pwm3_pee0, drive_pwm7_pee1, + drive_soc_gpio49_pee2, drive_soc_gpio50_pee4, + drive_soc_gpio82_pee3, drive_soc_gpio71_pff2, + drive_soc_gpio76_pff7, drive_soc_gpio74_pff5, + drive_soc_gpio86_phh3, drive_soc_gpio72_pff3, + drive_soc_gpio77_pgg0, drive_soc_gpio80_pff6, + drive_soc_gpio84_pgg1, drive_soc_gpio83_pee5, + drive_soc_gpio73_pff4, drive_soc_gpio70_pff1, + drive_soc_gpio85_pgg6, drive_soc_gpio69_pff0, + drive_uart5_tx_pgg7, drive_uart5_rx_phh0, + drive_uart2_tx_pgg2, drive_uart2_rx_pgg3, + drive_uart2_cts_pgg5, drive_uart2_rts_pgg4, + drive_uart5_cts_phh2, drive_uart5_rts_phh1 ] required: - compatible From 221bb09654714d6c52de4fc82260b5eb13f7489e Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Mon, 8 Jun 2026 09:41:22 +0000 Subject: [PATCH 3418/5214] pinctrl: tegra238: add missing AON pin groups Add 24 pin groups on ports EE, FF, GG and HH to the AON pin controller group table (tegra238_aon_groups[]). Their pin arrays, drive-group macros and pin descriptors were already defined, but the matching PINGROUP() entries were not present, so these pins could not be muxed or configured through the AON pin controller. The pin arrays were not referenced, so the build emitted -Wunused-const-variable warnings, and commit 119de2c33d96 ("pinctrl: tegra238: remove unused entries") removed three of them. Restore those arrays and add the full set of PINGROUP() entries to make the pins usable. Fixes: 25cac7292d49 ("pinctrl: tegra: Add Tegra238 pinmux driver") Signed-off-by: Prathamesh Shete Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/pinctrl-tegra238.c | 120 +++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/drivers/pinctrl/tegra/pinctrl-tegra238.c b/drivers/pinctrl/tegra/pinctrl-tegra238.c index c765b6b880e5..d3809594a5b5 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra238.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra238.c @@ -1074,6 +1074,102 @@ static const unsigned int pwm7_pee1_pins[] = { TEGRA_PIN_PWM7_PEE1, }; +static const unsigned int soc_gpio49_pee2_pins[] = { + TEGRA_PIN_SOC_GPIO49_PEE2, +}; + +static const unsigned int soc_gpio82_pee3_pins[] = { + TEGRA_PIN_SOC_GPIO82_PEE3, +}; + +static const unsigned int soc_gpio50_pee4_pins[] = { + TEGRA_PIN_SOC_GPIO50_PEE4, +}; + +static const unsigned int soc_gpio83_pee5_pins[] = { + TEGRA_PIN_SOC_GPIO83_PEE5, +}; + +static const unsigned int soc_gpio69_pff0_pins[] = { + TEGRA_PIN_SOC_GPIO69_PFF0, +}; + +static const unsigned int soc_gpio70_pff1_pins[] = { + TEGRA_PIN_SOC_GPIO70_PFF1, +}; + +static const unsigned int soc_gpio71_pff2_pins[] = { + TEGRA_PIN_SOC_GPIO71_PFF2, +}; + +static const unsigned int soc_gpio72_pff3_pins[] = { + TEGRA_PIN_SOC_GPIO72_PFF3, +}; + +static const unsigned int soc_gpio73_pff4_pins[] = { + TEGRA_PIN_SOC_GPIO73_PFF4, +}; + +static const unsigned int soc_gpio74_pff5_pins[] = { + TEGRA_PIN_SOC_GPIO74_PFF5, +}; + +static const unsigned int soc_gpio80_pff6_pins[] = { + TEGRA_PIN_SOC_GPIO80_PFF6, +}; + +static const unsigned int soc_gpio76_pff7_pins[] = { + TEGRA_PIN_SOC_GPIO76_PFF7, +}; + +static const unsigned int soc_gpio77_pgg0_pins[] = { + TEGRA_PIN_SOC_GPIO77_PGG0, +}; + +static const unsigned int soc_gpio84_pgg1_pins[] = { + TEGRA_PIN_SOC_GPIO84_PGG1, +}; + +static const unsigned int uart2_tx_pgg2_pins[] = { + TEGRA_PIN_UART2_TX_PGG2, +}; + +static const unsigned int uart2_rx_pgg3_pins[] = { + TEGRA_PIN_UART2_RX_PGG3, +}; + +static const unsigned int uart2_rts_pgg4_pins[] = { + TEGRA_PIN_UART2_RTS_PGG4, +}; + +static const unsigned int uart2_cts_pgg5_pins[] = { + TEGRA_PIN_UART2_CTS_PGG5, +}; + +static const unsigned int soc_gpio85_pgg6_pins[] = { + TEGRA_PIN_SOC_GPIO85_PGG6, +}; + +static const unsigned int uart5_tx_pgg7_pins[] = { + TEGRA_PIN_UART5_TX_PGG7, +}; + +static const unsigned int uart5_rx_phh0_pins[] = { + TEGRA_PIN_UART5_RX_PHH0, +}; + +static const unsigned int uart5_rts_phh1_pins[] = { + TEGRA_PIN_UART5_RTS_PHH1, +}; + +static const unsigned int uart5_cts_phh2_pins[] = { + TEGRA_PIN_UART5_CTS_PHH2, +}; + +static const unsigned int soc_gpio86_phh3_pins[] = { + TEGRA_PIN_SOC_GPIO86_PHH3, +}; + static const unsigned int sdmmc1_comp_pins[] = { TEGRA_PIN_SDMMC1_COMP, }; @@ -1890,6 +1986,30 @@ static const struct tegra_pingroup tegra238_aon_groups[] = { PINGROUP(dmic1_clk_pdd4, DMIC1_CLK, RSVD1, DMIC5_CLK, RSVD3, 0x11d0, 1, Y, -1, 7, 6, 8, -1, 10, 12), PINGROUP(dmic1_dat_pdd5, DMIC1_DAT, RSVD1, DMIC5_DAT, RSVD3, 0x11d8, 1, Y, -1, 7, 6, 8, -1, 10, 12), PINGROUP(soc_gpio19_pdd6, RSVD0, WDT_RESET_OUTB, RSVD2, RSVD3, 0x10f8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio49_pee2, RSVD0, RSVD1, RSVD2, RSVD3, 0x10c0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio50_pee4, RSVD0, RSVD1, RSVD2, RSVD3, 0x10c8, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio82_pee3, RSVD0, RSVD1, RSVD2, RSVD3, 0x10d0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio71_pff2, PPC_MODE_1, RSVD1, RSVD2, RSVD3, 0x10d8, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio76_pff7, RSVD0, RSVD1, TSC_EDGE_OUT0, TSC_EDGE_OUT0A, 0x10e0, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio74_pff5, PPC_READY, PPC_I2C_DAT, RSVD2, RSVD3, 0x10e8, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio86_phh3, RSVD0, SPI5_CS1, TSC_EDGE_OUT3, TSC_EDGE_OUT0D, 0x1100, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio72_pff3, PPC_MODE_2, RSVD1, RSVD2, RSVD3, 0x1108, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio77_pgg0, RSVD0, RSVD1, TSC_EDGE_OUT1, TSC_EDGE_OUT0B, 0x1110, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio80_pff6, RSVD0, PPC_RST_N, RSVD2, RSVD3, 0x1118, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio84_pgg1, RSVD0, RSVD1, TSC_EDGE_OUT2, TSC_EDGE_OUT0C, 0x1120, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio83_pee5, RSVD0, RSVD1, RSVD2, RSVD3, 0x1128, 1, Y, -1, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio73_pff4, PPC_CC, PPC_I2C_CLK, RSVD2, RSVD3, 0x1130, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio70_pff1, PPC_MODE_0, RSVD1, RSVD2, RSVD3, 0x1138, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio85_pgg6, RSVD0, SPI4_CS1, RSVD2, RSVD3, 0x1148, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(soc_gpio69_pff0, PPC_INT_N, RSVD1, RSVD2, RSVD3, 0x1150, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart5_tx_pgg7, UARTE_TXD, SPI5_SCK, RSVD2, RSVD3, 0x1168, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart5_rx_phh0, UARTE_RXD, SPI5_MISO, RSVD2, RSVD3, 0x1170, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart2_tx_pgg2, UARTB_TXD, SPI4_SCK, RSVD2, RSVD3, 0x1178, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart2_rx_pgg3, UARTB_RXD, SPI4_MISO, RSVD2, RSVD3, 0x1180, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart2_cts_pgg5, UARTB_CTS, SPI4_CS0, RSVD2, RSVD3, 0x1188, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart2_rts_pgg4, UARTB_RTS, SPI4_MOSI, RSVD2, RSVD3, 0x1190, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart5_cts_phh2, UARTE_CTS, SPI5_CS0, RSVD2, RSVD3, 0x1198, 1, Y, 5, 7, 6, 8, -1, 10, 12), + PINGROUP(uart5_rts_phh1, UARTE_RTS, SPI5_MOSI, RSVD2, RSVD3, 0x11a0, 1, Y, 5, 7, 6, 8, -1, 10, 12), PINGROUP(pwm2_pdd7, GP_PWM2, LED_BLINK, RSVD2, RSVD3, 0x11b0, 1, Y, 5, 7, 6, 8, -1, 10, 12), PINGROUP(pwm3_pee0, GP_PWM3, RSVD1, RSVD2, RSVD3, 0x11b8, 1, Y, 5, 7, 6, 8, -1, 10, 12), PINGROUP(pwm7_pee1, GP_PWM7, RSVD1, RSVD2, RSVD3, 0x11a8, 1, Y, 5, 7, 6, 8, -1, 10, 12), From 5f899621d34e2fd79229622805a659b13b63c98f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 9 Jun 2026 17:08:34 +0200 Subject: [PATCH 3419/5214] pinctrl: tegra: PINCTRL_TEGRA238 should depend on ARCH_TEGRA The NVIDIA Tegra238 MAIN and AON pin controllers are only present on NVIDIA Tegra238 SoCs. Hence add a dependency on ARCH_TEGRA, to prevent asking the user about this driver when configuring a kernel without NVIDIA Tegra SoC support. Fixes: 25cac7292d49f4fc ("pinctrl: tegra: Add Tegra238 pinmux driver") Signed-off-by: Geert Uytterhoeven Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index c7507193044f..eea7ec9688b6 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -39,6 +39,7 @@ config PINCTRL_TEGRA234 config PINCTRL_TEGRA238 tristate "NVIDIA Tegra238 pinctrl driver" + depends on ARCH_TEGRA || COMPILE_TEST default m if ARCH_TEGRA_238_SOC select PINCTRL_TEGRA help From 32b515861e41f37347b2a86d2bf00ff0ca2fe7dd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 9 Jun 2026 17:08:35 +0200 Subject: [PATCH 3420/5214] pinctrl: tegra: PINCTRL_TEGRA264 should depend on ARCH_TEGRA The NVIDIA Tegra264 MAIN, AON, and UPHY pin controllers are only present on NVIDIA Tegra264 SoCs. Hence add a dependency on ARCH_TEGRA, to prevent asking the user about this driver when configuring a kernel without NVIDIA Tegra SoC support. Fixes: c98506206912dd0d ("pinctrl: tegra: Add Tegra264 pinmux driver") Signed-off-by: Geert Uytterhoeven Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij --- drivers/pinctrl/tegra/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig index eea7ec9688b6..39a93733efa2 100644 --- a/drivers/pinctrl/tegra/Kconfig +++ b/drivers/pinctrl/tegra/Kconfig @@ -50,6 +50,7 @@ config PINCTRL_TEGRA238 config PINCTRL_TEGRA264 tristate "NVIDIA Tegra264 pinctrl driver" + depends on ARCH_TEGRA || COMPILE_TEST default m if ARCH_TEGRA_264_SOC select PINCTRL_TEGRA help From 40fcc5ac4ceec6a0b0c90e8eb0f4c210ea9d39b4 Mon Sep 17 00:00:00 2001 From: Navya Malempati Date: Fri, 29 May 2026 16:13:01 +0530 Subject: [PATCH 3421/5214] pinctrl: qcom: Remove unused macro definitions The macros QUP_I3C and UFS_RESET are defined in some platforms and yet not used. Remove these macros as they are unnecessary. Signed-off-by: Navya Malempati Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-qcs8300.c | 6 ------ drivers/pinctrl/qcom/pinctrl-qdu1000.c | 24 ------------------------ drivers/pinctrl/qcom/pinctrl-sa8775p.c | 6 ------ 3 files changed, 36 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs8300.c b/drivers/pinctrl/qcom/pinctrl-qcs8300.c index 852cd36df6d5..9a904d809e11 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs8300.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs8300.c @@ -100,12 +100,6 @@ .intr_detection_width = -1, \ } -#define QUP_I3C(qup_mode, qup_offset) \ - { \ - .mode = qup_mode, \ - .offset = qup_offset, \ - } - #define QUP_I3C_6_MODE_OFFSET 0xaf000 #define QUP_I3C_7_MODE_OFFSET 0xb0000 #define QUP_I3C_13_MODE_OFFSET 0xb1000 diff --git a/drivers/pinctrl/qcom/pinctrl-qdu1000.c b/drivers/pinctrl/qcom/pinctrl-qdu1000.c index 77da87aa72aa..1ef77f82820c 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdu1000.c +++ b/drivers/pinctrl/qcom/pinctrl-qdu1000.c @@ -75,30 +75,6 @@ .intr_detection_width = -1, \ } -#define UFS_RESET(pg_name, offset) \ - { \ - .grp = PINCTRL_PINGROUP(#pg_name, \ - pg_name##_pins, \ - ARRAY_SIZE(pg_name##_pins)), \ - .ctl_reg = offset, \ - .io_reg = offset + 0x4, \ - .intr_cfg_reg = 0, \ - .intr_status_reg = 0, \ - .mux_bit = -1, \ - .pull_bit = 3, \ - .drv_bit = 0, \ - .oe_bit = -1, \ - .in_bit = -1, \ - .out_bit = 0, \ - .intr_enable_bit = -1, \ - .intr_status_bit = -1, \ - .intr_target_bit = -1, \ - .intr_raw_status_bit = -1, \ - .intr_polarity_bit = -1, \ - .intr_detection_bit = -1, \ - .intr_detection_width = -1, \ - } - static const struct pinctrl_pin_desc qdu1000_pins[] = { PINCTRL_PIN(0, "GPIO_0"), PINCTRL_PIN(1, "GPIO_1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c index e9a510d3583f..5dbaa50cbff0 100644 --- a/drivers/pinctrl/qcom/pinctrl-sa8775p.c +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -101,12 +101,6 @@ .intr_detection_width = -1, \ } -#define QUP_I3C(qup_mode, qup_offset) \ - { \ - .mode = qup_mode, \ - .offset = qup_offset, \ - } - #define QUP_I3C_6_MODE_OFFSET 0xAF000 #define QUP_I3C_7_MODE_OFFSET 0xB0000 #define QUP_I3C_13_MODE_OFFSET 0xB1000 From db5d2b8f5249bad43d607b550365f80bc7cad831 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:39 +0800 Subject: [PATCH 3422/5214] LoongArch: KVM: Add separate KVM_REQ_LBT_LOAD bit There are different structures with FPU and LBT register restore, with FPU the structure is vcpu::arch::fpu, with LBT the structure is vcpu:: arch::lbt. Moreover, FPU/LSX/LASX saving and restoring share the common structure vcpu::arch::fpu. New request bit KVM_REQ_LBT_LOAD is used for LBT register restore, and rename KVM_REQ_AUX_LOAD with KVM_REQ_FPU_LOAD for FPU register restore. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 3 ++- arch/loongarch/kvm/exit.c | 12 +++++------- arch/loongarch/kvm/vcpu.c | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 776bc487a705..e76a49d4233b 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -38,7 +38,8 @@ #define KVM_REQ_TLB_FLUSH_GPA KVM_ARCH_REQ(0) #define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(1) #define KVM_REQ_PMU KVM_ARCH_REQ(2) -#define KVM_REQ_AUX_LOAD KVM_ARCH_REQ(3) +#define KVM_REQ_FPU_LOAD KVM_ARCH_REQ(3) +#define KVM_REQ_LBT_LOAD KVM_ARCH_REQ(4) #define KVM_GUESTDBG_SW_BP_MASK \ (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 3b95cd0f989b..b839c989d3dc 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -756,7 +756,7 @@ static int kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu, int ecode) } vcpu->arch.aux_ldtype = KVM_LARCH_FPU; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); return RESUME_GUEST; } @@ -798,7 +798,7 @@ static int kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu, int ecode) kvm_queue_exception(vcpu, EXCCODE_INE, 0); else { vcpu->arch.aux_ldtype = KVM_LARCH_LSX; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); } return RESUME_GUEST; @@ -818,7 +818,7 @@ static int kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu, int ecode) kvm_queue_exception(vcpu, EXCCODE_INE, 0); else { vcpu->arch.aux_ldtype = KVM_LARCH_LASX; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); + kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); } return RESUME_GUEST; @@ -828,10 +828,8 @@ static int kvm_handle_lbt_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lbt(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LBT; - kvm_make_request(KVM_REQ_AUX_LOAD, vcpu); - } + else + kvm_make_request(KVM_REQ_LBT_LOAD, vcpu); return RESUME_GUEST; } diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e28084c49e68..c28aa37f37a9 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -239,7 +239,7 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) vcpu->arch.flush_gpa = INVALID_GPA; } - if (kvm_check_request(KVM_REQ_AUX_LOAD, vcpu)) { + if (kvm_check_request(KVM_REQ_FPU_LOAD, vcpu)) { switch (vcpu->arch.aux_ldtype) { case KVM_LARCH_FPU: kvm_own_fpu(vcpu); @@ -250,15 +250,15 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) case KVM_LARCH_LASX: kvm_own_lasx(vcpu); break; - case KVM_LARCH_LBT: - kvm_own_lbt(vcpu); - break; default: break; } vcpu->arch.aux_ldtype = 0; } + + if (kvm_check_request(KVM_REQ_LBT_LOAD, vcpu)) + kvm_own_lbt(vcpu); } /* From f1d9dda01aacfae22044c5cb07b50556cfa75eb1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3423/5214] LoongArch: KVM: Enable FPU with max supported FPU type There are three FPU types FPU/LSX/LASX, which represents FPU64, FPU128 and FPU256, and now lazy FPU method is used with FPU enabling. There are three different HW FPU exception code with different FPU type. The exising method is to enable specified FPU type with responding FPU exeception. Supposing application uses FPU64 and FPU256, there will be FPU256 exception when FPU256 type is used. Here enable FPU with the max VM supported type directly, so it can avoid unnecessary FPU exception in future if further FPU type is used. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 1 - arch/loongarch/kvm/exit.c | 9 ++------- arch/loongarch/kvm/vcpu.c | 19 +++++-------------- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index e76a49d4233b..bad16c5f963c 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -204,7 +204,6 @@ struct kvm_vcpu_arch { /* Which auxiliary state is loaded (KVM_LARCH_*) */ unsigned int aux_inuse; - unsigned int aux_ldtype; /* FPU state */ struct loongarch_fpu fpu FPU_ALIGN; diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index b839c989d3dc..c8492670f169 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -755,7 +755,6 @@ static int kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu, int ecode) return RESUME_HOST; } - vcpu->arch.aux_ldtype = KVM_LARCH_FPU; kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); return RESUME_GUEST; @@ -796,10 +795,8 @@ static int kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lsx(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LSX; + else kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); - } return RESUME_GUEST; } @@ -816,10 +813,8 @@ static int kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu, int ecode) { if (!kvm_guest_has_lasx(&vcpu->arch)) kvm_queue_exception(vcpu, EXCCODE_INE, 0); - else { - vcpu->arch.aux_ldtype = KVM_LARCH_LASX; + else kvm_make_request(KVM_REQ_FPU_LOAD, vcpu); - } return RESUME_GUEST; } diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index c28aa37f37a9..c65518b4e18e 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -240,21 +240,12 @@ static void kvm_late_check_requests(struct kvm_vcpu *vcpu) } if (kvm_check_request(KVM_REQ_FPU_LOAD, vcpu)) { - switch (vcpu->arch.aux_ldtype) { - case KVM_LARCH_FPU: - kvm_own_fpu(vcpu); - break; - case KVM_LARCH_LSX: - kvm_own_lsx(vcpu); - break; - case KVM_LARCH_LASX: + if (kvm_guest_has_lasx(&vcpu->arch)) kvm_own_lasx(vcpu); - break; - default: - break; - } - - vcpu->arch.aux_ldtype = 0; + else if (kvm_guest_has_lsx(&vcpu->arch)) + kvm_own_lsx(vcpu); + else if (kvm_guest_has_fpu(&vcpu->arch)) + kvm_own_fpu(vcpu); } if (kvm_check_request(KVM_REQ_LBT_LOAD, vcpu)) From 22e36941b54c33a3c45e14b1d016866c801832ed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3424/5214] LoongArch: KVM: Remove some middle FPU states With max VM supported FPU type enabled, if VM supports LASX, there is only NONE --> LASX, no middle FPU state such as NONE --> FPU --> LASX or NONE --> FPU --> LSX --> LASX. Here remove the middle FPU states in function kvm_own_lsx() and kvm_own_lasx(). And it becomes simpler than before. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index c65518b4e18e..e875efb26cb9 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1389,24 +1389,10 @@ int kvm_own_lsx(struct kvm_vcpu *vcpu) /* Enable LSX for guest */ kvm_check_fcsr(vcpu, vcpu->arch.fpu.fcsr); set_csr_euen(CSR_EUEN_LSXEN | CSR_EUEN_FPEN); - switch (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { - case KVM_LARCH_FPU: - /* - * Guest FPU state already loaded, - * only restore upper LSX state - */ - _restore_lsx_upper(&vcpu->arch.fpu); - break; - default: - /* Neither FP or LSX already active, - * restore full LSX state - */ - kvm_restore_lsx(&vcpu->arch.fpu); - break; - } - trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); + kvm_restore_lsx(&vcpu->arch.fpu); vcpu->arch.aux_inuse |= KVM_LARCH_LSX | KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); return 0; } @@ -1418,25 +1404,10 @@ int kvm_own_lasx(struct kvm_vcpu *vcpu) { kvm_check_fcsr(vcpu, vcpu->arch.fpu.fcsr); set_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); - switch (vcpu->arch.aux_inuse & (KVM_LARCH_FPU | KVM_LARCH_LSX)) { - case KVM_LARCH_LSX: - case KVM_LARCH_LSX | KVM_LARCH_FPU: - /* Guest LSX state already loaded, only restore upper LASX state */ - _restore_lasx_upper(&vcpu->arch.fpu); - break; - case KVM_LARCH_FPU: - /* Guest FP state already loaded, only restore upper LSX & LASX state */ - _restore_lsx_upper(&vcpu->arch.fpu); - _restore_lasx_upper(&vcpu->arch.fpu); - break; - default: - /* Neither FP or LSX already active, restore full LASX state */ - kvm_restore_lasx(&vcpu->arch.fpu); - break; - } - trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); + kvm_restore_lasx(&vcpu->arch.fpu); vcpu->arch.aux_inuse |= KVM_LARCH_LASX | KVM_LARCH_LSX | KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); return 0; } From 848c55a2c9703fc477bc65a2acc5d680ea174c4b Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3425/5214] LoongArch: KVM: Remove KVM_LARCH_LSX and KVM_LARCH_LASX In kvm_lose_fpu() FPU state is save in vcpu::arch::fpu, its FPU status comes from vcpu->arch.aux_inuse. Instead existing API vm_guest_has_xxx() can be used also, moreover, the bits KVM_LARCH_LSX and KVM_LARCH_LASX in arch.aux_inuse are removed. It makes the logic simpler than ever. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_host.h | 10 ++++------ arch/loongarch/kvm/vcpu.c | 17 ++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index bad16c5f963c..23cfbecebbd7 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -158,12 +158,10 @@ enum emulation_result { }; #define KVM_LARCH_FPU (0x1 << 0) -#define KVM_LARCH_LSX (0x1 << 1) -#define KVM_LARCH_LASX (0x1 << 2) -#define KVM_LARCH_LBT (0x1 << 3) -#define KVM_LARCH_PMU (0x1 << 4) -#define KVM_LARCH_SWCSR_LATEST (0x1 << 5) -#define KVM_LARCH_HWCSR_USABLE (0x1 << 6) +#define KVM_LARCH_LBT (0x1 << 1) +#define KVM_LARCH_PMU (0x1 << 2) +#define KVM_LARCH_SWCSR_LATEST (0x1 << 3) +#define KVM_LARCH_HWCSR_USABLE (0x1 << 4) #define LOONGARCH_PV_FEAT_UPDATED BIT_ULL(63) #define LOONGARCH_PV_FEAT_MASK (BIT(KVM_FEATURE_IPI) | \ diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index e875efb26cb9..27a738cb53f2 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1391,7 +1391,7 @@ int kvm_own_lsx(struct kvm_vcpu *vcpu) set_csr_euen(CSR_EUEN_LSXEN | CSR_EUEN_FPEN); kvm_restore_lsx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse |= KVM_LARCH_LSX | KVM_LARCH_FPU; + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LSX); return 0; @@ -1406,7 +1406,7 @@ int kvm_own_lasx(struct kvm_vcpu *vcpu) set_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); kvm_restore_lasx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse |= KVM_LARCH_LASX | KVM_LARCH_LSX | KVM_LARCH_FPU; + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); return 0; @@ -1418,29 +1418,32 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) { preempt_disable(); + if (!(vcpu->arch.aux_inuse & KVM_LARCH_FPU)) + goto done; + kvm_check_fcsr_alive(vcpu); - if (vcpu->arch.aux_inuse & KVM_LARCH_LASX) { + if (kvm_guest_has_lasx(&vcpu->arch)) { kvm_save_lasx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~(KVM_LARCH_LSX | KVM_LARCH_FPU | KVM_LARCH_LASX); trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_LASX); /* Disable LASX & LSX & FPU */ clear_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); - } else if (vcpu->arch.aux_inuse & KVM_LARCH_LSX) { + } else if (kvm_guest_has_lsx(&vcpu->arch)) { kvm_save_lsx(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~(KVM_LARCH_LSX | KVM_LARCH_FPU); trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_LSX); /* Disable LSX & FPU */ clear_csr_euen(CSR_EUEN_FPEN | CSR_EUEN_LSXEN); } else if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { kvm_save_fpu(&vcpu->arch.fpu); - vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU; trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU); /* Disable FPU */ clear_csr_euen(CSR_EUEN_FPEN); } + vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU; + +done: kvm_lose_lbt(vcpu); preempt_enable(); From f4caaac76379daf4a617d9134b6087fb2636fb31 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3426/5214] LoongArch: KVM: Fix FPU register width with user access API At the beginning, only 64 bit FPU is supported. With FPU register get interface, 64 bit FPU data is copied to user space, the same with FPU register set API. However with LSX and LASX supported in later, there should be FPU data copied with bigger width. So here fixes this issue, copy the whole 256 bit FPU data from/to user space. Cc: stable@vger.kernel.org Fixes: db1ecca22edf ("LoongArch: KVM: Add LSX (128bit SIMD) support") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 27a738cb53f2..ab0b0da270ca 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1303,7 +1303,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) fpu->fcc = vcpu->arch.fpu.fcc; fpu->fcsr = vcpu->arch.fpu.fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], sizeof(union fpureg)); return 0; } @@ -1315,7 +1315,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu->arch.fpu.fcc = fpu->fcc; vcpu->arch.fpu.fcsr = fpu->fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], sizeof(union fpureg)); return 0; } From 1ef045effba8b85953fb9eccdfaf80e6f300b612 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3427/5214] LoongArch: KVM: Use existing macro about interrupt bit mask With interrupt post, register CSR.GINTC and CSR.ESTAT is used, and CSR.ESTAT is used for percpu interrupt injection and CSR.GINTC is for external hardware interrupt injection. Here use existing macro about interrupt bit of register CSR.GINTC and CSR.ESTAT, rather than hard coded constant value. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/kvm_vcpu.h | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/arch/loongarch/include/asm/kvm_vcpu.h b/arch/loongarch/include/asm/kvm_vcpu.h index 3784ab4ccdb5..a70a32103523 100644 --- a/arch/loongarch/include/asm/kvm_vcpu.h +++ b/arch/loongarch/include/asm/kvm_vcpu.h @@ -10,22 +10,37 @@ #include /* Controlled by 0x5 guest estat */ -#define CPU_SIP0 (_ULCAST_(1)) -#define CPU_SIP1 (_ULCAST_(1) << 1) -#define CPU_PMU (_ULCAST_(1) << 10) -#define CPU_TIMER (_ULCAST_(1) << 11) -#define CPU_IPI (_ULCAST_(1) << 12) -#define CPU_AVEC (_ULCAST_(1) << 14) +#define CPU_SIP0 BIT(INT_SWI0) +#define CPU_SIP1 BIT(INT_SWI1) +#define CPU_HWI0 BIT(INT_HWI0) +#define CPU_HWI1 BIT(INT_HWI1) +#define CPU_HWI2 BIT(INT_HWI2) +#define CPU_HWI3 BIT(INT_HWI3) +#define CPU_HWI4 BIT(INT_HWI4) +#define CPU_HWI5 BIT(INT_HWI5) +#define CPU_HWI6 BIT(INT_HWI6) +#define CPU_HWI7 BIT(INT_HWI7) +#define CPU_PMU BIT(INT_PCOV) +#define CPU_TIMER BIT(INT_TI) +#define CPU_IPI BIT(INT_IPI) +#define CPU_AVEC BIT(INT_AVEC) +#define KVM_ESTAT_INTI_MASK (CPU_SIP0 | CPU_SIP1 | CPU_PMU | CPU_TIMER \ + | CPU_IPI | CPU_AVEC) +#define KVM_ESTAT_EXTI_MASK (CPU_HWI0 | CPU_HWI1 | CPU_HWI2 | CPU_HWI3 \ + | CPU_HWI4 | CPU_HWI5 | CPU_HWI6 | CPU_HWI7) /* Controlled by 0x52 guest exception VIP aligned to estat bit 5~12 */ -#define CPU_IP0 (_ULCAST_(1)) -#define CPU_IP1 (_ULCAST_(1) << 1) -#define CPU_IP2 (_ULCAST_(1) << 2) -#define CPU_IP3 (_ULCAST_(1) << 3) -#define CPU_IP4 (_ULCAST_(1) << 4) -#define CPU_IP5 (_ULCAST_(1) << 5) -#define CPU_IP6 (_ULCAST_(1) << 6) -#define CPU_IP7 (_ULCAST_(1) << 7) +#define VIP_DELTA (INT_HWI0 - CSR_GINTC_VIP_SHIFT) +#define CPU_IP0 BIT(INT_HWI0 - VIP_DELTA) +#define CPU_IP1 BIT(INT_HWI1 - VIP_DELTA) +#define CPU_IP2 BIT(INT_HWI2 - VIP_DELTA) +#define CPU_IP3 BIT(INT_HWI3 - VIP_DELTA) +#define CPU_IP4 BIT(INT_HWI4 - VIP_DELTA) +#define CPU_IP5 BIT(INT_HWI5 - VIP_DELTA) +#define CPU_IP6 BIT(INT_HWI6 - VIP_DELTA) +#define CPU_IP7 BIT(INT_HWI7 - VIP_DELTA) +#define KVM_GINTC_IRQ_MASK (CPU_IP0 | CPU_IP1 | CPU_IP2 | CPU_IP3 \ + | CPU_IP4 | CPU_IP5 | CPU_IP6 | CPU_IP7) #define MNSEC_PER_SEC (NSEC_PER_SEC >> 20) From 09b318ab77b7a4fc9987fd98d1525fc55ddc2617 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 3428/5214] LoongArch: KVM: Check irq validity in kvm_vcpu_ioctl_interrupt() Function kvm_vcpu_ioctl_interrupt() can be called from userspace, here add irq validility cheking in kvm_vcpu_ioctl_interrupt(). Cc: stable@vger.kernel.org Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index ab0b0da270ca..4235bf8e455e 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1452,6 +1452,10 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { int intr = (int)irq->irq; + unsigned int vector = abs(intr); + + if (vector >= EXCCODE_INT_NUM) + return -EINVAL; if (intr > 0) kvm_queue_irq(vcpu, intr); From 33c6da26d8a7f831f6d714ae43ba5a427572ed8f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:41 +0800 Subject: [PATCH 3429/5214] LoongArch: KVM: Check msgint feature in interrupt post Interrupt AVEC is valid only if VM has msgint feature, and this feature is checked in interrupt handling. Since interrupt handling is executing in VM context switch, and it is hot path, here move the feature checking in interrupt post rather than interrupt handling. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/interrupt.c | 5 ----- arch/loongarch/kvm/vcpu.c | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index a18c60dffbba..48dd56aa4dc5 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -36,8 +36,6 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) switch (priority) { case INT_AVEC: - if (!kvm_guest_has_msgint(&vcpu->arch)) - break; dmsintc_inject_irq(vcpu); fallthrough; case INT_TI: @@ -75,9 +73,6 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) switch (priority) { case INT_AVEC: - if (!kvm_guest_has_msgint(&vcpu->arch)) - break; - fallthrough; case INT_TI: case INT_IPI: case INT_SWI0: diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 4235bf8e455e..cb42f43afcc8 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1457,6 +1457,9 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) if (vector >= EXCCODE_INT_NUM) return -EINVAL; + if (!kvm_guest_has_msgint(&vcpu->arch) && (vector == INT_AVEC)) + return -EINVAL; + if (intr > 0) kvm_queue_irq(vcpu, intr); else if (intr < 0) From 3179253ad52f9d0e1c44873f0a2a4f0792e7103d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:42 +0800 Subject: [PATCH 3430/5214] LoongArch: KVM: Inject interrupts with batch method With bitmask method, interrupts can be injected with batch mode rather than one by one. Also remove unused array priority_to_irq[] here. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/interrupt.c | 93 ++++++++++------------------------ 1 file changed, 27 insertions(+), 66 deletions(-) diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index 48dd56aa4dc5..055ac9235b03 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -9,39 +9,16 @@ #include #include -static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { - [INT_TI] = CPU_TIMER, - [INT_IPI] = CPU_IPI, - [INT_SWI0] = CPU_SIP0, - [INT_SWI1] = CPU_SIP1, - [INT_HWI0] = CPU_IP0, - [INT_HWI1] = CPU_IP1, - [INT_HWI2] = CPU_IP2, - [INT_HWI3] = CPU_IP3, - [INT_HWI4] = CPU_IP4, - [INT_HWI5] = CPU_IP5, - [INT_HWI6] = CPU_IP6, - [INT_HWI7] = CPU_IP7, - [INT_AVEC] = CPU_AVEC, -}; - -static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) +static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask) { - unsigned int irq = 0; + unsigned long irq; unsigned long old, new; - clear_bit(priority, &vcpu->arch.irq_pending); - if (priority < EXCCODE_INT_NUM) - irq = priority_to_irq[priority]; - - switch (priority) { - case INT_AVEC: + if (mask & CPU_AVEC) dmsintc_inject_irq(vcpu); - fallthrough; - case INT_TI: - case INT_IPI: - case INT_SWI0: - case INT_SWI1: + + irq = mask & KVM_ESTAT_INTI_MASK; + if (irq) { old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); set_gcsr_estat(irq); new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); @@ -49,34 +26,20 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) /* Inject TI if TVAL inverted */ if (new > old) set_gcsr_estat(CPU_TIMER); - break; - - case INT_HWI0 ... INT_HWI7: - set_csr_gintc(irq); - break; - - default: - break; } - return 1; + irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + if (irq) + set_csr_gintc(irq); } -static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) +static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask) { - unsigned int irq = 0; + unsigned long irq; unsigned long old, new; - clear_bit(priority, &vcpu->arch.irq_clear); - if (priority < EXCCODE_INT_NUM) - irq = priority_to_irq[priority]; - - switch (priority) { - case INT_AVEC: - case INT_TI: - case INT_IPI: - case INT_SWI0: - case INT_SWI1: + irq = mask & KVM_ESTAT_INTI_MASK; + if (irq) { old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); clear_gcsr_estat(irq); new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); @@ -84,30 +47,28 @@ static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) /* Inject TI if TVAL inverted */ if (new > old) set_gcsr_estat(CPU_TIMER); - break; - - case INT_HWI0 ... INT_HWI7: - clear_csr_gintc(irq); - break; - - default: - break; } - return 1; + irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + if (irq) + clear_csr_gintc(irq); } void kvm_deliver_intr(struct kvm_vcpu *vcpu) { - unsigned int priority; - unsigned long *pending = &vcpu->arch.irq_pending; - unsigned long *pending_clr = &vcpu->arch.irq_clear; + unsigned long mask; - for_each_set_bit(priority, pending_clr, EXCCODE_INT_NUM) - kvm_irq_clear(vcpu, priority); + mask = READ_ONCE(vcpu->arch.irq_clear); + if (mask) { + mask = xchg_relaxed(&vcpu->arch.irq_clear, 0); + kvm_irq_clear(vcpu, mask); + } - for_each_set_bit(priority, pending, EXCCODE_INT_NUM) - kvm_irq_deliver(vcpu, priority); + mask = READ_ONCE(vcpu->arch.irq_pending); + if (mask) { + mask = xchg_relaxed(&vcpu->arch.irq_pending, 0); + kvm_irq_deliver(vcpu, mask); + } } int kvm_pending_timer(struct kvm_vcpu *vcpu) From 42985883613cb316d9a1520365ff6c1e07ef5c11 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:42 +0800 Subject: [PATCH 3431/5214] LoongArch: KVM: Add valid bit check when set CSR.ESTAT register When set CSR.ESTAT register in function _kvm_setcsr(), valid bit check is added here. Also interrupt CPU_AVEC is checked by msgint feature. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index cb42f43afcc8..89aa49df276a 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -593,7 +593,7 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid) static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) { - unsigned long gintc; + unsigned long estat, gintc; struct loongarch_csrs *csr = vcpu->arch.csr; if (get_gcsr_flag(id) & INVALID_GCSR) @@ -612,8 +612,9 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) preempt_enable(); /* ESTAT IP0~IP7 get from GINTC */ - gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff; - *val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) | (gintc << 2); + gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK; + estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK; + *val = estat | (gintc << VIP_DELTA); return 0; } @@ -628,7 +629,8 @@ static int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *val) static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val) { - int ret = 0, gintc; + int ret = 0; + unsigned long estat, gintc; struct loongarch_csrs *csr = vcpu->arch.csr; if (get_gcsr_flag(id) & INVALID_GCSR) @@ -639,11 +641,16 @@ static int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val) if (id == LOONGARCH_CSR_ESTAT) { /* ESTAT IP0~IP7 inject through GINTC */ - gintc = (val >> 2) & 0xff; - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc); + gintc = (val >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; + gintc |= kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & ~KVM_GINTC_IRQ_MASK; + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc); - gintc = val & ~(0xffUL << 2); - kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc); + /* Only set valid ESTAT bits */ + estat = val & ~KVM_ESTAT_EXTI_MASK; + estat &= CSR_ESTAT_IS | CSR_ESTAT_EXC | CSR_ESTAT_ESUBCODE; + if (!kvm_guest_has_msgint(&vcpu->arch)) + estat &= ~CPU_AVEC; + kvm_write_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, estat); return ret; } From 8c5cd2b9d6a724998c1ab9af06ac897b6152d568 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 3432/5214] LoongArch: KVM: Deliver interrupt after IN_GUEST_MODE is set Interrupt delivery should be called after IN_GUEST_MODE is set. Other threads may be posting interrupt however does not send IPI to the vCPU, since the vCPU is not in IN_GUEST_MODE yet. Here move function call with kvm_deliver_intr() after IN_GUEST_MODE is set, and set mode with OUTSIDE_GUEST_MODE with atomic method. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 89aa49df276a..33aefd90d228 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -299,10 +299,10 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) * check vmid before vcpu enter guest */ local_irq_disable(); - kvm_deliver_intr(vcpu); kvm_deliver_exception(vcpu); /* Make sure the vcpu mode has been written */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); + kvm_deliver_intr(vcpu); kvm_check_vpid(vcpu); /* @@ -339,7 +339,7 @@ static int kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) u32 intr = estat & CSR_ESTAT_IS; u32 ecode = (estat & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT; - vcpu->mode = OUTSIDE_GUEST_MODE; + smp_store_mb(vcpu->mode, OUTSIDE_GUEST_MODE); /* Set a default exit reason */ run->exit_reason = KVM_EXIT_UNKNOWN; From 83551ccedffda28b1fa7aeb8fcf5e00489443816 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 3433/5214] LoongArch: KVM: Remove timer interrupt injection when SW timer expired The software timer emulation is to wake up vCPU when the vCPU executes idle instruction and gives up host CPU, the vCPU timer tick value and interrupt is set when vCPU is scheduled in. It is not necessary to inject timer interrupt when SW timer is expired. Here remove it, also use common API kvm_vcpu_wake_up() to wake up vCPU. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/timer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c index 8356fce0043f..3829f35a4070 100644 --- a/arch/loongarch/kvm/timer.c +++ b/arch/loongarch/kvm/timer.c @@ -30,8 +30,7 @@ enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) struct kvm_vcpu *vcpu; vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); - kvm_queue_irq(vcpu, INT_TI); - rcuwait_wake_up(&vcpu->wait); + kvm_vcpu_wake_up(vcpu); return HRTIMER_NORESTART; } From fb89e0fe2dc4246c86ad0eb0fa2b7cb2f8ba9728 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 3434/5214] LoongArch: KVM: Check the return values for put_user() put_user() may return -EFAULT, so, when the user space address is invalid, the caller should return -EFAULT. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 33aefd90d228..20c207d80e31 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1106,7 +1106,8 @@ static int kvm_loongarch_cpucfg_get_attr(struct kvm_vcpu *vcpu, return -ENXIO; } - put_user(val, uaddr); + if (put_user(val, uaddr)) + return -EFAULT; return ret; } From ebd50de14f1a06b7e0206083904bcc62b9ba65be Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 3435/5214] LoongArch: KVM: Return full old CSR value from kvm_emu_xchg_csr() The LoongArch CSRXCHG instruction returns the full old CSR value in rd after applying the masked update. kvm_emu_xchg_csr() currently masks the saved value before returning it to the guest, so rd receives only the bits selected by the write mask. That breaks the architectural behavior and makes a zero mask return 0 instead of the previous CSR value. So, keep the masked CSR update, but return the unmodified old CSR value. Cc: stable@vger.kernel.org Fixes: da50f5a693ff ("LoongArch: KVM: Implement handle csr exception") Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen --- arch/loongarch/kvm/exit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index c8492670f169..8572b63478bb 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -103,7 +103,6 @@ static unsigned long kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, old = kvm_read_sw_gcsr(csr, csrid); val = (old & ~csr_mask) | (val & csr_mask); kvm_write_sw_gcsr(csr, csrid, val); - old = old & csr_mask; } else pr_warn_once("Unsupported csrxchg 0x%x with pc %lx\n", csrid, vcpu->arch.pc); From 3474037904c20ff915e3ebab0ab5c1e41bbe549e Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 3436/5214] LoongArch: KVM: Validate irqchip index in irqfd routing Sashiko reported that the irqchip index is not validated for LoongArch. Add validation and reject out-of-range irqchip indexes to avoid indexing past the routing table's chip array. Cc: stable@vger.kernel.org Fixes: 1928254c5ccb ("LoongArch: KVM: Add irqfd support") Closes: https://lore.kernel.org/kvm/20260525051714.485D51F000E9@smtp.kernel.org/ Reported-by: Sashiko Reviewed-by: Bibo Mao Signed-off-by: Yanfei Xu Signed-off-by: Huacai Chen --- arch/loongarch/kvm/irqfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c index f4f953b22419..40ed1081c4b6 100644 --- a/arch/loongarch/kvm/irqfd.c +++ b/arch/loongarch/kvm/irqfd.c @@ -51,7 +51,8 @@ int kvm_set_routing_entry(struct kvm *kvm, e->irqchip.irqchip = ue->u.irqchip.irqchip; e->irqchip.pin = ue->u.irqchip.pin; - if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS || + e->irqchip.irqchip >= KVM_NR_IRQCHIPS) return -EINVAL; return 0; From aeded601d6aceb57cdda4b2701d2ee00c43a8b69 Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Thu, 11 Jun 2026 20:46:44 +0800 Subject: [PATCH 3437/5214] LoongArch: KVM: Add missing slots_lock for device register/unregister kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c and pch_pic.c were also missing this protection. Add it to match the register side. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Zeng Chi Signed-off-by: Huacai Chen --- arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ arch/loongarch/kvm/intc/ipi.c | 2 ++ arch/loongarch/kvm/intc/pch_pic.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 2ab7fafa86d5..2b14485d14a7 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -645,10 +645,14 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) device = &s->device_vext; kvm_iodevice_init(device, &kvm_eiointc_virt_ops); + mutex_lock(&kvm->slots_lock); ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); + mutex_unlock(&kvm->slots_lock); if (ret < 0) { + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); return ret; } @@ -667,8 +671,10 @@ static void kvm_eiointc_destroy(struct kvm_device *dev) kvm = dev->kvm; eiointc = kvm->arch.eiointc; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); + mutex_unlock(&kvm->slots_lock); kfree(eiointc); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 1f6ebbd0af5c..4fa0897d7bdb 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -447,7 +447,9 @@ static void kvm_ipi_destroy(struct kvm_device *dev) kvm = dev->kvm; ipi = kvm->arch.ipi; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); + mutex_unlock(&kvm->slots_lock); kfree(ipi); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index aa0ed59ae8cf..175a630aceb4 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -481,7 +481,9 @@ static void kvm_pch_pic_destroy(struct kvm_device *dev) kvm = dev->kvm; s = kvm->arch.pch_pic; /* unregister pch pic device and free it's memory */ + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); kfree(dev); } From e98a9c61721c14bcd29f11f4802e52e908701f7a Mon Sep 17 00:00:00 2001 From: Tarun Sahu Date: Thu, 11 Jun 2026 09:30:40 +0000 Subject: [PATCH 3438/5214] liveupdate: Document that retrieve failure is permanent Signed-off-by: Tarun Sahu Reviewed-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/faec5df2a3e90240cde5897bae2250cd7d44aeac.1781170056.git.tarunsahu@google.com Signed-off-by: Mike Rapoport (Microsoft) --- include/uapi/linux/liveupdate.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h index 3a9ff53b02e0..4043d4038712 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -169,7 +169,9 @@ struct liveupdate_session_preserve_fd { * associated with the token and populates the @fd field with a new file * descriptor referencing the restored resource in the current (new) kernel. * This operation must be performed *before* signaling completion via - * %LIVEUPDATE_IOCTL_FINISH. + * %LIVEUPDATE_IOCTL_FINISH. If a retrieve of a token fails, subsequent + * attempts to retrieve the token fail with the same error code. Failed + * retrieves are not retried. * * Return: 0 on success, negative error code on failure (e.g., invalid token). */ From dac30db978c71af999403f42e4fad68eda5ed09e Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 10 Jun 2026 13:29:55 +0800 Subject: [PATCH 3439/5214] dt-bindings: pinctrl: Add UltraRISC DP1000 pinctrl controller Add doc for the pinctrl controllers on the UltraRISC DP1000 RISC-V SoC. Signed-off-by: Jia Wang Reviewed-by: Conor Dooley Signed-off-by: Linus Walleij --- .../pinctrl/ultrarisc,dp1000-pinctrl.yaml | 130 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 136 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml new file mode 100644 index 000000000000..c2332e6e60c2 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/ultrarisc,dp1000-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UltraRISC DP1000 Pin Controller + +maintainers: + - Jia Wang + +description: | + UltraRISC RISC-V SoC DP1000 pin controller. + The controller manages ports A, B, C, D and LPC. Ports A-D default to + GPIO and provide additional SPI, UART, I2C, and PWM mux functions. + LPC pins default to the LPC interface and can be muxed to eSPI. + All pins also support pin configuration, including drive strength, + pull-up, and pull-down settings. + +properties: + compatible: + const: ultrarisc,dp1000-pinctrl + + reg: + items: + - description: pin controller registers + +required: + - compatible + - reg + +patternProperties: + '.*-pins$': + type: object + unevaluatedProperties: false + allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml# + - $ref: /schemas/pinctrl/pinmux-node.yaml# + - if: + properties: + pins: + items: + minimum: 40 + maximum: 52 + then: + properties: + function: + enum: + - lpc + - espi + else: + properties: + pins: + items: + maximum: 39 + function: + enum: + - gpio + - i2c + - pwm + - spi + - uart + + properties: + pins: + description: | + List of pins affected by this state node, using numeric pin IDs. + Pins 0-39 correspond to ports A-D, and pins 40-52 correspond + to LPC0-LPC12. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + uniqueItems: true + items: + minimum: 0 + maximum: 52 + + function: + description: | + Mux function to select for the listed pins. Supported functions + depend on the selected pins and match the DP1000 hardware mux + table. + enum: + - gpio + - i2c + - pwm + - spi + - uart + - lpc + - espi + + bias-disable: true + bias-high-impedance: true + bias-pull-up: true + bias-pull-down: true + + drive-strength: + description: Output drive strength in mA. + enum: [20, 27, 33, 40] + + required: + - pins + - function + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + pinctrl@11081000 { + compatible = "ultrarisc,dp1000-pinctrl"; + reg = <0x0 0x11081000 0x0 0x1000>; + + i2c0-pins { + pins = <12 13>; + function = "i2c"; + bias-pull-up; + drive-strength = <33>; + }; + + uart0-pins { + pins = <8 9>; + function = "uart"; + bias-pull-up; + drive-strength = <33>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 12aae45a302c..fe5c274f6947 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27313,6 +27313,12 @@ S: Maintained F: drivers/usb/common/ulpi.c F: include/linux/ulpi/ +ULTRARISC DP1000 PINCTRL DRIVER +M: Jia Wang +L: linux-gpio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml + ULTRATRONIK BOARD SUPPORT M: Goran Rađenović M: Börge Strümpfel From cb7037924836a352e767f69f1aa65b82f3e815f4 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Wed, 10 Jun 2026 13:29:56 +0800 Subject: [PATCH 3440/5214] pinctrl: ultrarisc: Add UltraRISC DP1000 pinctrl driver Add support for the pin controller on the UltraRISC DP1000 SoC. The controller provides mux selection for pins in ports A, B, C, D, and LPC. Ports A-D default to GPIO and support peripheral muxing. LPC pins can be switched to eSPI, but are not available as GPIOs. Basic pin configuration controls such as drive strength, pull-up, and pull-down are also supported. Signed-off-by: Jia Wang Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/ultrarisc/Kconfig | 20 + drivers/pinctrl/ultrarisc/Makefile | 4 + drivers/pinctrl/ultrarisc/pinctrl-dp1000.c | 168 ++++++ drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.c | 517 ++++++++++++++++++ drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.h | 63 +++ 8 files changed, 775 insertions(+) create mode 100644 drivers/pinctrl/ultrarisc/Kconfig create mode 100644 drivers/pinctrl/ultrarisc/Makefile create mode 100644 drivers/pinctrl/ultrarisc/pinctrl-dp1000.c create mode 100644 drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.c create mode 100644 drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.h diff --git a/MAINTAINERS b/MAINTAINERS index fe5c274f6947..0617efbbd882 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27318,6 +27318,7 @@ M: Jia Wang L: linux-gpio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pinctrl/ultrarisc,dp1000-pinctrl.yaml +F: drivers/pinctrl/ultrarisc/* ULTRATRONIK BOARD SUPPORT M: Goran Rađenović diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 3624cfe4f2a2..c2cdd7b2c49b 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -720,6 +720,7 @@ source "drivers/pinctrl/sunplus/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/tegra/Kconfig" source "drivers/pinctrl/ti/Kconfig" +source "drivers/pinctrl/ultrarisc/Kconfig" source "drivers/pinctrl/uniphier/Kconfig" source "drivers/pinctrl/visconti/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 5536e05e5edc..a35d71135abf 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -97,6 +97,7 @@ obj-y += sunplus/ obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ obj-y += tegra/ obj-y += ti/ +obj-$(CONFIG_PINCTRL_ULTRARISC) += ultrarisc/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_VISCONTI) += visconti/ obj-$(CONFIG_PINCTRL_WMT) += vt8500/ diff --git a/drivers/pinctrl/ultrarisc/Kconfig b/drivers/pinctrl/ultrarisc/Kconfig new file mode 100644 index 000000000000..80ac997f6c51 --- /dev/null +++ b/drivers/pinctrl/ultrarisc/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config PINCTRL_ULTRARISC + tristate + depends on OF + depends on ARCH_ULTRARISC || COMPILE_TEST + select GENERIC_PINCTRL + select PINMUX + +config PINCTRL_ULTRARISC_DP1000 + tristate "UltraRISC DP1000 SoC Pinctrl driver" + select PINCTRL_ULTRARISC + depends on OF && HAS_IOMEM + depends on ARCH_ULTRARISC || COMPILE_TEST + default ARCH_ULTRARISC + help + Say Y to select the pinctrl driver for UltraRISC DP1000 SoC. + This pin controller allows selecting the mux function for + each pin. This driver can also be built as a module called + pinctrl-dp1000. diff --git a/drivers/pinctrl/ultrarisc/Makefile b/drivers/pinctrl/ultrarisc/Makefile new file mode 100644 index 000000000000..5d49ce1c0af9 --- /dev/null +++ b/drivers/pinctrl/ultrarisc/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_PINCTRL_ULTRARISC) += pinctrl-ultrarisc.o +obj-$(CONFIG_PINCTRL_ULTRARISC_DP1000) += pinctrl-dp1000.o diff --git a/drivers/pinctrl/ultrarisc/pinctrl-dp1000.c b/drivers/pinctrl/ultrarisc/pinctrl-dp1000.c new file mode 100644 index 000000000000..f9c85c8c4433 --- /dev/null +++ b/drivers/pinctrl/ultrarisc/pinctrl-dp1000.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + * + * Author: Jia Wang + */ + +#include +#include +#include + +#include "pinctrl-ultrarisc.h" + +/* Port indices. */ +#define UR_DP1000_PORT_A_IDX 0 +#define UR_DP1000_PORT_B_IDX 1 +#define UR_DP1000_PORT_C_IDX 2 +#define UR_DP1000_PORT_D_IDX 3 +#define UR_DP1000_PORT_LPC_IDX 4 + +/* Port mux register offsets. */ +#define UR_DP1000_PORTA_FUNC_OFFSET 0x2c0 +#define UR_DP1000_PORTB_FUNC_OFFSET 0x2c4 +#define UR_DP1000_PORTC_FUNC_OFFSET 0x2c8 +#define UR_DP1000_PORTD_FUNC_OFFSET 0x2cc +#define UR_DP1000_PORTLPC_FUNC_OFFSET 0x2d0 + +/* Port pinconf register offsets. */ +#define UR_DP1000_PORTA_CONF_OFFSET 0x310 +#define UR_DP1000_PORTB_CONF_OFFSET 0x318 +#define UR_DP1000_PORTC_CONF_OFFSET 0x31c +#define UR_DP1000_PORTD_CONF_OFFSET 0x320 +#define UR_DP1000_PORTLPC_CONF_OFFSET 0x324 + +/* Pin ranges for function descriptors. */ +#define UR_DP1000_PINS_ABCD GENMASK_ULL(39, 0) +#define UR_DP1000_PINS_LPC GENMASK_ULL(52, 40) + +/* Static table entry helpers. */ +#define UR_DP1000_PORT(_base, _npins, _func, _conf, _modes, _gpio) \ + { .pin_base = (_base), .npins = (_npins), .func_offset = (_func), \ + .conf_offset = (_conf), .supported_modes = (_modes), \ + .supports_gpio = (_gpio) } + +#define UR_DP1000_PIN(_nr, _name, _port) \ + { .number = (_nr), .name = (_name), .drv_data = (void *)&ur_dp1000_ports[_port] } + +static const struct ur_func_route ur_dp1000_routes[] = { + { "gpio", UR_FUNC_DEFAULT, UR_DP1000_PINS_ABCD }, + { "i2c", UR_FUNC_0, GENMASK_ULL(13, 12) }, + { "i2c", UR_FUNC_0, GENMASK_ULL(23, 22) }, + { "i2c", UR_FUNC_0, GENMASK_ULL(25, 24) }, + { "i2c", UR_FUNC_0, GENMASK_ULL(27, 26) }, + { "pwm", UR_FUNC_0, GENMASK_ULL(19, 16) }, + { "spi", UR_FUNC_1, GENMASK_ULL(39, 32) }, + { "spi", UR_FUNC_0, GENMASK_ULL(6, 0) }, + { "uart", UR_FUNC_1, GENMASK_ULL(9, 8) }, + { "uart", UR_FUNC_0, GENMASK_ULL(21, 20) }, + { "uart", UR_FUNC_0, GENMASK_ULL(29, 28) }, + { "uart", UR_FUNC_0, GENMASK_ULL(31, 30) }, + { "lpc", UR_FUNC_DEFAULT, UR_DP1000_PINS_LPC }, + { "espi", UR_FUNC_0, UR_DP1000_PINS_LPC }, +}; + +static const struct ur_port_desc ur_dp1000_ports[] = { + UR_DP1000_PORT(0, 16, UR_DP1000_PORTA_FUNC_OFFSET, + UR_DP1000_PORTA_CONF_OFFSET, + UR_FUNC_0 | UR_FUNC_1, true), + UR_DP1000_PORT(16, 8, UR_DP1000_PORTB_FUNC_OFFSET, + UR_DP1000_PORTB_CONF_OFFSET, + UR_FUNC_0 | UR_FUNC_1, true), + UR_DP1000_PORT(24, 8, UR_DP1000_PORTC_FUNC_OFFSET, + UR_DP1000_PORTC_CONF_OFFSET, + UR_FUNC_0 | UR_FUNC_1, true), + UR_DP1000_PORT(32, 8, UR_DP1000_PORTD_FUNC_OFFSET, + UR_DP1000_PORTD_CONF_OFFSET, + UR_FUNC_0 | UR_FUNC_1, true), + UR_DP1000_PORT(40, 13, UR_DP1000_PORTLPC_FUNC_OFFSET, + UR_DP1000_PORTLPC_CONF_OFFSET, + UR_FUNC_0, false), +}; + +static const struct pinctrl_pin_desc ur_dp1000_pins[] = { + UR_DP1000_PIN(0, "PA0", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(1, "PA1", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(2, "PA2", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(3, "PA3", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(4, "PA4", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(5, "PA5", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(6, "PA6", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(7, "PA7", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(8, "PA8", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(9, "PA9", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(10, "PA10", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(11, "PA11", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(12, "PA12", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(13, "PA13", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(14, "PA14", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(15, "PA15", UR_DP1000_PORT_A_IDX), + UR_DP1000_PIN(16, "PB0", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(17, "PB1", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(18, "PB2", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(19, "PB3", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(20, "PB4", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(21, "PB5", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(22, "PB6", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(23, "PB7", UR_DP1000_PORT_B_IDX), + UR_DP1000_PIN(24, "PC0", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(25, "PC1", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(26, "PC2", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(27, "PC3", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(28, "PC4", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(29, "PC5", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(30, "PC6", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(31, "PC7", UR_DP1000_PORT_C_IDX), + UR_DP1000_PIN(32, "PD0", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(33, "PD1", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(34, "PD2", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(35, "PD3", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(36, "PD4", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(37, "PD5", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(38, "PD6", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(39, "PD7", UR_DP1000_PORT_D_IDX), + UR_DP1000_PIN(40, "LPC0", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(41, "LPC1", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(42, "LPC2", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(43, "LPC3", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(44, "LPC4", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(45, "LPC5", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(46, "LPC6", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(47, "LPC7", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(48, "LPC8", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(49, "LPC9", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(50, "LPC10", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(51, "LPC11", UR_DP1000_PORT_LPC_IDX), + UR_DP1000_PIN(52, "LPC12", UR_DP1000_PORT_LPC_IDX), +}; + +static const struct ur_pinctrl_data ur_dp1000_pinctrl_data = { + .pins = ur_dp1000_pins, + .npins = ARRAY_SIZE(ur_dp1000_pins), + .routes = ur_dp1000_routes, + .num_routes = ARRAY_SIZE(ur_dp1000_routes), +}; + +static const struct of_device_id ur_pinctrl_of_match[] = { + { .compatible = "ultrarisc,dp1000-pinctrl" }, + { } +}; +MODULE_DEVICE_TABLE(of, ur_pinctrl_of_match); + +static int ur_dp1000_pinctrl_probe(struct platform_device *pdev) +{ + return ur_pinctrl_probe(pdev, &ur_dp1000_pinctrl_data); +} + +static struct platform_driver ur_pinctrl_driver = { + .driver = { + .name = "ultrarisc-pinctrl-dp1000", + .of_match_table = ur_pinctrl_of_match, + }, + .probe = ur_dp1000_pinctrl_probe, +}; + +module_platform_driver(ur_pinctrl_driver); + +MODULE_DESCRIPTION("UltraRISC DP1000 pinctrl driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.c b/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.c new file mode 100644 index 000000000000..8fb5b0ea5b93 --- /dev/null +++ b/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + * + * Author: Jia Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../devicetree.h" +#include "../pinconf.h" +#include "../pinmux.h" + +#include "pinctrl-ultrarisc.h" + +#define UR_CONF_BIT_PER_PIN 4 +#define UR_CONF_PIN_PER_REG (32 / UR_CONF_BIT_PER_PIN) +static const u32 ur_drive_strengths[] = { 20, 27, 33, 40 }; + +static const struct ur_port_desc *ur_get_pin_port(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + + if (!desc || !desc->drv_data) + return NULL; + + return desc->drv_data; +} + +static u32 ur_get_pin_conf_offset(const struct ur_port_desc *port_desc, u32 pin) +{ + return port_desc->conf_offset + + (pin / UR_CONF_PIN_PER_REG) * sizeof(u32); +} + +static int ur_read_pin_conf(struct ur_pinctrl *pctrl, unsigned int pin, u32 *conf) +{ + const struct ur_port_desc *port_desc; + u32 pin_offset; + u32 reg_offset; + u32 shift; + u32 mask; + + port_desc = ur_get_pin_port(pctrl->pctl_dev, pin); + if (!port_desc) + return -EINVAL; + + pin_offset = pin - port_desc->pin_base; + reg_offset = ur_get_pin_conf_offset(port_desc, pin_offset); + shift = (pin_offset % UR_CONF_PIN_PER_REG) * UR_CONF_BIT_PER_PIN; + mask = GENMASK(UR_CONF_BIT_PER_PIN - 1, 0) << shift; + *conf = field_get(mask, readl_relaxed(pctrl->base + reg_offset)); + + return 0; +} + +static int ur_write_pin_conf(struct ur_pinctrl *pctrl, unsigned int pin, u32 conf) +{ + const struct ur_port_desc *port_desc; + void __iomem *reg; + u32 pin_offset; + u32 reg_offset; + u32 shift; + u32 mask; + u32 val; + + port_desc = ur_get_pin_port(pctrl->pctl_dev, pin); + if (!port_desc) + return -EINVAL; + + pin_offset = pin - port_desc->pin_base; + reg_offset = ur_get_pin_conf_offset(port_desc, pin_offset); + reg = pctrl->base + reg_offset; + shift = (pin_offset % UR_CONF_PIN_PER_REG) * UR_CONF_BIT_PER_PIN; + mask = GENMASK(UR_CONF_BIT_PER_PIN - 1, 0) << shift; + + scoped_guard(raw_spinlock_irqsave, &pctrl->lock) { + val = readl_relaxed(reg); + val = (val & ~mask) | field_prep(mask, conf); + writel_relaxed(val, reg); + } + + return 0; +} + +static int ur_set_pin_mux(struct ur_pinctrl *pctrl, + const struct ur_port_desc *port_desc, + u32 pin_offset, u32 mode) +{ + void __iomem *reg = pctrl->base + port_desc->func_offset; + u32 val; + + if (WARN_ON(pin_offset >= UR_MAX_PINS_PER_PORT)) + return -EINVAL; + + scoped_guard(raw_spinlock_irqsave, &pctrl->lock) { + val = readl_relaxed(reg); + val &= ~((UR_FUNC_0 | UR_FUNC_1) << pin_offset); + val |= mode << pin_offset; + writel_relaxed(val, reg); + } + + return 0; +} + +static int ur_set_pin_mux_by_num(struct ur_pinctrl *pctrl, unsigned int pin, u32 mode) +{ + const struct ur_port_desc *port_desc = ur_get_pin_port(pctrl->pctl_dev, pin); + u32 pin_offset; + + if (!port_desc) + return -EINVAL; + + if (mode != UR_FUNC_DEFAULT && !(port_desc->supported_modes & mode)) + return -EINVAL; + + pin_offset = pin - port_desc->pin_base; + + return ur_set_pin_mux(pctrl, port_desc, pin_offset, mode); +} + +static int ur_hw_to_config(unsigned long *config, u32 conf) +{ + enum pin_config_param param = pinconf_to_config_param(*config); + u32 drive = FIELD_GET(UR_DRIVE_MASK, conf); + u32 pull = FIELD_GET(UR_PULL_MASK, conf); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + if (pull != UR_PULL_DIS) + return -EINVAL; + *config = pinconf_to_config_packed(param, 1); + return 0; + case PIN_CONFIG_BIAS_PULL_UP: + if (pull != UR_PULL_UP) + return -EINVAL; + *config = pinconf_to_config_packed(param, 1); + return 0; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + if (pull != UR_PULL_DOWN) + return -EINVAL; + *config = pinconf_to_config_packed(param, 1); + return 0; + case PIN_CONFIG_DRIVE_STRENGTH: + if (drive >= ARRAY_SIZE(ur_drive_strengths)) + return -EINVAL; + *config = pinconf_to_config_packed(param, ur_drive_strengths[drive]); + return 0; + default: + return -EINVAL; + } +} + +static int ur_config_to_hw(unsigned long config, u32 *conf) +{ + enum pin_config_param param = pinconf_to_config_param(config); + u32 arg = pinconf_to_config_argument(config); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + FIELD_MODIFY(UR_PULL_MASK, conf, UR_PULL_DIS); + return 0; + case PIN_CONFIG_BIAS_PULL_UP: + FIELD_MODIFY(UR_PULL_MASK, conf, UR_PULL_UP); + return 0; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + FIELD_MODIFY(UR_PULL_MASK, conf, UR_PULL_DOWN); + return 0; + case PIN_CONFIG_DRIVE_STRENGTH: + for (u32 i = 0; i < ARRAY_SIZE(ur_drive_strengths); i++) { + if (ur_drive_strengths[i] != arg) + continue; + FIELD_MODIFY(UR_DRIVE_MASK, conf, i); + return 0; + } + return -EINVAL; + case PIN_CONFIG_PERSIST_STATE: + /* + * For PIN_CONFIG_PERSIST_STATE, gpiolib only treats + * -ENOTSUPP as an optional unsupported result. + * Do not use -EOPNOTSUPP here. + */ + return -ENOTSUPP; + default: + return -EOPNOTSUPP; + } +} + +/** + * ur_find_group_route() - Find the route matching a function and pin set + * @pctrl: pin controller instance + * @function: mux function name + * @group_mask: bitmap of pins in the selected group + * @route_out: returned route entry on success + * + * A mux function may be associated with multiple hardware routing options, + * each valid for a specific set of pins. This helper finds the unique route + * whose valid_pins mask covers all pins in @group_mask. + * + * The routing table must guarantee that a given function plus pin set + * resolves to exactly one route. If multiple routes match, the routing + * description is considered ambiguous and the lookup fails. + * + * Return: 0 on success, or -EINVAL if no matching route exists or if the + * routing table contains ambiguous entries. + */ +static int ur_find_group_route(struct ur_pinctrl *pctrl, + const char *function, + u64 group_mask, + const struct ur_func_route **route_out) +{ + const struct ur_func_route *match = NULL; + + for (u32 i = 0; i < pctrl->data->num_routes; i++) { + const struct ur_func_route *route = &pctrl->data->routes[i]; + + if (strcmp(route->function, function)) + continue; + + if ((route->valid_pins & group_mask) != group_mask) + continue; + + if (match) { + dev_err(pctrl->dev, + "ambiguous route for function %s group_mask=%#llx\n", + function, (unsigned long long)group_mask); + return -EINVAL; + } + + match = route; + } + + if (match) { + *route_out = match; + return 0; + } + + return -EINVAL; +} + +static const char *ur_get_group_function(struct pinctrl_dev *pctldev, + unsigned int group_selector, + unsigned int pin_index) +{ + const struct group_desc *group; + const char * const *functions; + + group = pinctrl_generic_get_group(pctldev, group_selector); + if (!group || pin_index >= group->grp.npins || !group->data) + return NULL; + + functions = group->data; + + return functions[pin_index]; +} + +static int ur_resolve_group_mux(struct pinctrl_dev *pctldev, + struct ur_pinctrl *pctrl, + unsigned int group_selector, + const unsigned int *pins, + unsigned int npins, + const struct ur_func_route **route_out) +{ + const char *function; + u64 group_mask = 0; + + if (!npins) + return -EINVAL; + + function = ur_get_group_function(pctldev, group_selector, 0); + if (!function) + return -EINVAL; + + for (u32 i = 0; i < npins; i++) + group_mask |= BIT_ULL(pins[i]); + + return ur_find_group_route(pctrl, function, group_mask, route_out); +} + +static bool ur_function_is_gpio(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + const struct function_desc *function; + + function = pinmux_generic_get_function(pctldev, selector); + if (!function) + return false; + + for (u32 i = 0; i < function->func->ngroups; i++) { + const char *func_name; + int group_selector; + + group_selector = pinctrl_get_group_selector(pctldev, + function->func->groups[i]); + if (group_selector < 0) + return false; + + func_name = ur_get_group_function(pctldev, group_selector, 0); + if (!func_name || strcmp(func_name, "gpio")) + return false; + } + + return true; +} + +static const struct pinctrl_ops ur_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .dt_node_to_map = pinctrl_generic_pins_function_dt_node_to_map, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static int ur_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct ur_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct ur_port_desc *port_desc; + const struct ur_func_route *route; + int ret; + + (void)range; + + port_desc = ur_get_pin_port(pctldev, offset); + if (!port_desc || !port_desc->supports_gpio) + return -EINVAL; + + ret = ur_find_group_route(pctrl, "gpio", BIT_ULL(offset), &route); + if (ret) + return ret; + + return ur_set_pin_mux_by_num(pctrl, offset, route->mode); +} + +static int ur_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, + unsigned int group_selector) +{ + struct ur_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct ur_func_route *route; + const unsigned int *pins; + unsigned int npins; + int ret; + + (void)func_selector; + + ret = pinctrl_generic_get_group_pins(pctldev, group_selector, &pins, &npins); + if (ret) + return ret; + + ret = ur_resolve_group_mux(pctldev, pctrl, group_selector, pins, npins, + &route); + if (ret) + return ret; + + for (u32 i = 0; i < npins; i++) { + ret = ur_set_pin_mux_by_num(pctrl, pins[i], route->mode); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinmux_ops ur_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .function_is_gpio = ur_function_is_gpio, + .set_mux = ur_set_mux, + .gpio_request_enable = ur_gpio_request_enable, + .strict = true, +}; + +static int ur_pin_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *config) +{ + struct ur_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + u32 conf; + int ret; + + ret = ur_read_pin_conf(pctrl, pin, &conf); + if (ret) + return ret; + + return ur_hw_to_config(config, conf); +} + +static int ur_pin_config_set(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *configs, + unsigned int num_configs) +{ + struct ur_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + u32 conf; + int ret; + + ret = ur_read_pin_conf(pctrl, pin, &conf); + if (ret) + return ret; + + for (u32 i = 0; i < num_configs; i++) { + ret = ur_config_to_hw(configs[i], &conf); + if (ret) + return ret; + } + + return ur_write_pin_conf(pctrl, pin, conf); +} + +static int ur_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, selector, &pins, &npins); + if (ret || !npins) + return ret ?: -EINVAL; + + return ur_pin_config_get(pctldev, pins[0], config); +} + +static int ur_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, selector, &pins, &npins); + if (ret) + return ret; + + for (u32 i = 0; i < npins; i++) { + ret = ur_pin_config_set(pctldev, pins[i], configs, num_configs); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinconf_ops ur_pinconf_ops = { + .pin_config_get = ur_pin_config_get, + .pin_config_set = ur_pin_config_set, + .pin_config_group_get = ur_pin_config_group_get, + .pin_config_group_set = ur_pin_config_group_set, + .is_generic = true, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +int ur_pinctrl_probe(struct platform_device *pdev, + const struct ur_pinctrl_data *data) +{ + struct pinctrl_desc *desc; + struct ur_pinctrl *pctrl; + int ret; + + if (!data) + return -ENODEV; + + desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pctrl->base)) + return PTR_ERR(pctrl->base); + pctrl->dev = &pdev->dev; + pctrl->data = data; + + raw_spin_lock_init(&pctrl->lock); + + desc->name = dev_name(&pdev->dev); + desc->owner = THIS_MODULE; + desc->pins = data->pins; + desc->npins = data->npins; + desc->pctlops = &ur_pinctrl_ops; + desc->pmxops = &ur_pinmux_ops; + desc->confops = &ur_pinconf_ops; + + ret = devm_pinctrl_register_and_init(&pdev->dev, desc, pctrl, &pctrl->pctl_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register pinctrl\n"); + + platform_set_drvdata(pdev, pctrl); + + return pinctrl_enable(pctrl->pctl_dev); +} +EXPORT_SYMBOL_GPL(ur_pinctrl_probe); + +MODULE_DESCRIPTION("UltraRISC pinctrl core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.h b/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.h new file mode 100644 index 000000000000..c874688aafca --- /dev/null +++ b/drivers/pinctrl/ultrarisc/pinctrl-ultrarisc.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + * + * Author: Jia Wang + */ + +#ifndef __PINCTRL_ULTRARISC_H__ +#define __PINCTRL_ULTRARISC_H__ + +#include +#include +#include + +struct platform_device; + +#define UR_FUNC_DEFAULT 0U +#define UR_FUNC_0 1U +#define UR_FUNC_1 0x10000U + +#define UR_MAX_PINS_PER_PORT 16 + +#define UR_BIAS_MASK 0x0000000F +#define UR_PULL_MASK 0x0C +#define UR_PULL_DIS 0 +#define UR_PULL_UP 1 +#define UR_PULL_DOWN 2 +#define UR_DRIVE_MASK 0x03 + +struct ur_port_desc { + u32 pin_base; + u32 npins; + u32 func_offset; + u32 conf_offset; + u32 supported_modes; + bool supports_gpio; +}; + +struct ur_func_route { + const char *function; + u32 mode; + u64 valid_pins; +}; + +struct ur_pinctrl_data { + const struct pinctrl_pin_desc *pins; + u32 npins; + const struct ur_func_route *routes; + u32 num_routes; +}; + +struct ur_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl_dev; + const struct ur_pinctrl_data *data; + void __iomem *base; + raw_spinlock_t lock; /* Protects mux and conf registers */ +}; + +int ur_pinctrl_probe(struct platform_device *pdev, + const struct ur_pinctrl_data *data); + +#endif From afa0c07131d8829ea0ebbcd8267c85aa178ce52c Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Thu, 11 Jun 2026 07:10:49 +0000 Subject: [PATCH 3441/5214] pinctrl: meson: amlogic-a4: use nolock get range Use pinctrl_find_gpio_range_from_pin_nolock() instead of pinctrl_find_gpio_range_from_pin() when configuring a pin or setting a GPIO value. This avoids taking the lock and allows the code to be safely called from interrupt context. Signed-off-by: Xianwei Zhao Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index 88c5408f51cf..de10790d7d85 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -300,7 +300,7 @@ static int aml_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned int fselector, int i; for (i = 0; i < group->npins; i++) { - range = pinctrl_find_gpio_range_from_pin(pctldev, group->pins[i]); + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, group->pins[i]); aml_pctl_set_function(info, range, group->pins[i], group->func[i]); } @@ -499,7 +499,7 @@ static int aml_pinconf_disable_bias(struct aml_pinctrl *info, unsigned int pin) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit = 0; @@ -512,7 +512,7 @@ static int aml_pinconf_enable_bias(struct aml_pinctrl *info, unsigned int pin, bool pull_up) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit, val = 0; int ret; @@ -534,7 +534,7 @@ static int aml_pinconf_set_drive_strength(struct aml_pinctrl *info, u16 drive_strength_ua) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit, ds_val; @@ -569,7 +569,7 @@ static int aml_pinconf_set_gpio_bit(struct aml_pinctrl *info, bool arg) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit; From 981aefd53b3cdafae0e45332a1023b80d67f52be Mon Sep 17 00:00:00 2001 From: Alexandre MINETTE Date: Tue, 19 May 2026 09:16:33 +0200 Subject: [PATCH 3442/5214] pinctrl: qcom: Register functions before enabling pinctrl pinctrl consumers can request states while the pinctrl core enables the controller. On Qualcomm pinctrl drivers this can happen before the SoC function list has been registered, which leaves the function table incomplete during state lookup. On APQ8064 this can fail while claiming pinctrl hogs: apq8064-pinctrl 800000.pinctrl: invalid function ps_hold in map table apq8064-pinctrl 800000.pinctrl: error claiming hogs: -22 apq8064-pinctrl 800000.pinctrl: could not claim hogs: -22 Register Qualcomm pinctrl with devm_pinctrl_register_and_init(), add the SoC pin functions, and only then enable the pinctrl device. Signed-off-by: Alexandre MINETTE Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-msm.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 6771f5eb29e4..11db6564c44d 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1591,11 +1591,11 @@ int msm_pinctrl_probe(struct platform_device *pdev, pctrl->desc.pins = pctrl->soc->pins; pctrl->desc.npins = pctrl->soc->npins; - pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); - if (IS_ERR(pctrl->pctrl)) { - dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); - return PTR_ERR(pctrl->pctrl); - } + ret = devm_pinctrl_register_and_init(&pdev->dev, &pctrl->desc, + pctrl, &pctrl->pctrl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Couldn't register pinctrl driver\n"); for (i = 0; i < soc_data->nfunctions; i++) { func = &soc_data->functions[i]; @@ -1605,6 +1605,11 @@ int msm_pinctrl_probe(struct platform_device *pdev, return ret; } + ret = pinctrl_enable(pctrl->pctrl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Couldn't enable pinctrl driver\n"); + ret = msm_gpio_init(pctrl); if (ret) return ret; From c7dda3d0f869dc97223448a06c9a2e5235928e48 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 11 Jun 2026 12:50:36 +0200 Subject: [PATCH 3443/5214] KVM: s390: Initialize KVM_S390_GET_CMMA_BITS memory kvm_s390_get_cmma_bits() allocates its output buffer with vmalloc(), which does not zero the returned pages: values = vmalloc(args->count); In the non-peek (migration) path, dat_get_cmma() reports a byte count spanning from the first to the last dirty page, but __dat_get_cmma_pte() writes values[gfn - start] only for pages whose CMMA dirty bit is set. The walk uses DAT_WALK_IGN_HOLES, so clean and unmapped pages that lie between two dirty pages within the reported span are visited but never store their byte. Those gaps (up to KVM_S390_MAX_BIT_DISTANCE pages each) stay uninitialized yet fall inside [0, count) and are copied out by copy_to_user(), disclosing stale kernel memory to user space. Before the switch to the new gmap implementation the buffer was fully populated for every gfn in the span, so no uninitialized bytes were exposed; the dirty-only walk introduced the leak. Use vzalloc() so the gaps read back as zero. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Cc: stable@vger.kernel.org Signed-off-by: Christian Borntraeger Reviewed-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260611105036.11491-1-borntraeger@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 6de36421548c..d503e4d0072a 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2263,7 +2263,7 @@ static int kvm_s390_get_cmma_bits(struct kvm *kvm, return 0; } - values = vmalloc(args->count); + values = vzalloc(args->count); if (!values) return -ENOMEM; From 01ffffc4d0bd5b44a3e5fc07ec232266a1601c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 10 Jun 2026 16:32:59 +0200 Subject: [PATCH 3444/5214] platform/x86: apple-gmux: Drop unused assignment of pnp_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct pnp_device_id to zero without relying on that value. Drop these unused assignments. While touching this array use a named initializer for .id for improved readability and simplify the list terminator. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with an x86 build. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/dd6e70d3075205a1d5c1fa324db7a822f37e2349.1781101905.git.u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/apple-gmux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index fbc30f1f8abd..9c728ac4e045 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -1013,8 +1013,8 @@ static void gmux_remove(struct pnp_dev *pnp) } static const struct pnp_device_id gmux_device_ids[] = { - {GMUX_ACPI_HID, 0}, - {"", 0} + { .id = GMUX_ACPI_HID }, + { } }; static const struct dev_pm_ops gmux_dev_pm_ops = { From 92b69eff8012f1c8d79f2153857208f36277e0e5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 9 Jun 2026 18:06:34 +0200 Subject: [PATCH 3445/5214] pmdomain: core: fix early domain registration A recent change switching to a dynamically allocated root device broke platforms like rcar-sysc that registers PM domains before the PM domain bus itself has been registered (cf. commit c5ae5a0c6112 ("pmdomain: renesas: rcar-sysc: Add genpd OF provider at postcore_initcall")). Defer the assignment of the parent root device until the domain is registered with driver core to avoid it being left unset. Fixes: a96e40f4afdc ("pmdomain: core: switch to dynamic root device") Reported-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/CAMuHMdUHabMGJyJ7e7yp7DLC+JJc9k6NK9p4anj2wRKNuwZUng@mail.gmail.com Signed-off-by: Johan Hovold Tested-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 7c96e88302d3..7796042e3f37 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2323,7 +2323,6 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) device_initialize(&genpd->dev); genpd->dev.release = genpd_provider_release; genpd->dev.bus = &genpd_provider_bus_type; - genpd->dev.parent = genpd_provider_bus; if (!genpd_is_dev_name_fw(genpd)) { dev_set_name(&genpd->dev, "%s", genpd->name); @@ -2688,6 +2687,7 @@ int of_genpd_add_provider_simple(struct device_node *np, if (!genpd_present(genpd)) return -EINVAL; + genpd->dev.parent = genpd_provider_bus; genpd->dev.of_node = np; fwnode = of_fwnode_handle(np); @@ -2782,6 +2782,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, if (!genpd_present(genpd)) goto error; + genpd->dev.parent = genpd_provider_bus; genpd->dev.of_node = np; if (sync_state && !genpd_is_no_sync_state(genpd)) { From b3c8bb2e69979e3aadcbfc5ac53424ecf26a9277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 13:12:03 +0200 Subject: [PATCH 3446/5214] backlight: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace in the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Daniel Thompson (RISCstar) Link: https://patch.msgid.link/20260518111203.639603-2-u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- drivers/video/backlight/adp8860_bl.c | 6 +++--- drivers/video/backlight/adp8870_bl.c | 2 +- drivers/video/backlight/arcxcnn_bl.c | 2 +- drivers/video/backlight/aw99706.c | 2 +- drivers/video/backlight/bd6107.c | 2 +- drivers/video/backlight/ktz8866.c | 4 ++-- drivers/video/backlight/lm3509_bl.c | 4 ++-- drivers/video/backlight/lm3630a_bl.c | 4 ++-- drivers/video/backlight/lm3639_bl.c | 4 ++-- drivers/video/backlight/lp855x_bl.c | 14 +++++++------- drivers/video/backlight/lv5207lp.c | 2 +- drivers/video/backlight/mp3309c.c | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index d4bbd7a7406b..09dd67702431 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -790,9 +790,9 @@ static SIMPLE_DEV_PM_OPS(adp8860_i2c_pm_ops, adp8860_i2c_suspend, adp8860_i2c_resume); static const struct i2c_device_id adp8860_id[] = { - { "adp8860", adp8860 }, - { "adp8861", adp8861 }, - { "adp8863", adp8863 }, + { .name = "adp8860", .driver_data = adp8860 }, + { .name = "adp8861", .driver_data = adp8861 }, + { .name = "adp8863", .driver_data = adp8863 }, { } }; MODULE_DEVICE_TABLE(i2c, adp8860_id); diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index e09e20492e7c..d009f2c8a11d 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -962,7 +962,7 @@ static SIMPLE_DEV_PM_OPS(adp8870_i2c_pm_ops, adp8870_i2c_suspend, adp8870_i2c_resume); static const struct i2c_device_id adp8870_id[] = { - { "adp8870" }, + { .name = "adp8870" }, { } }; MODULE_DEVICE_TABLE(i2c, adp8870_id); diff --git a/drivers/video/backlight/arcxcnn_bl.c b/drivers/video/backlight/arcxcnn_bl.c index 1d5a570cfe02..f46eeab02e90 100644 --- a/drivers/video/backlight/arcxcnn_bl.c +++ b/drivers/video/backlight/arcxcnn_bl.c @@ -382,7 +382,7 @@ static const struct of_device_id arcxcnn_dt_ids[] = { MODULE_DEVICE_TABLE(of, arcxcnn_dt_ids); static const struct i2c_device_id arcxcnn_ids[] = { - {"arc2c0608", ARC2C0608}, + { .name = "arc2c0608", .driver_data = ARC2C0608 }, { } }; MODULE_DEVICE_TABLE(i2c, arcxcnn_ids); diff --git a/drivers/video/backlight/aw99706.c b/drivers/video/backlight/aw99706.c index 938f352aaab7..18299faf06ad 100644 --- a/drivers/video/backlight/aw99706.c +++ b/drivers/video/backlight/aw99706.c @@ -443,7 +443,7 @@ static int aw99706_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(aw99706_pm_ops, aw99706_suspend, aw99706_resume); static const struct i2c_device_id aw99706_ids[] = { - { "aw99706" }, + { .name = "aw99706" }, { } }; MODULE_DEVICE_TABLE(i2c, aw99706_ids); diff --git a/drivers/video/backlight/bd6107.c b/drivers/video/backlight/bd6107.c index 74567af84e97..6778b4030b02 100644 --- a/drivers/video/backlight/bd6107.c +++ b/drivers/video/backlight/bd6107.c @@ -179,7 +179,7 @@ static void bd6107_remove(struct i2c_client *client) } static const struct i2c_device_id bd6107_ids[] = { - { "bd6107" }, + { .name = "bd6107" }, { } }; MODULE_DEVICE_TABLE(i2c, bd6107_ids); diff --git a/drivers/video/backlight/ktz8866.c b/drivers/video/backlight/ktz8866.c index 351c2b4d63ed..53c1301dbb8c 100644 --- a/drivers/video/backlight/ktz8866.c +++ b/drivers/video/backlight/ktz8866.c @@ -179,8 +179,8 @@ static void ktz8866_remove(struct i2c_client *client) } static const struct i2c_device_id ktz8866_ids[] = { - { "ktz8866" }, - {} + { .name = "ktz8866" }, + { } }; MODULE_DEVICE_TABLE(i2c, ktz8866_ids); diff --git a/drivers/video/backlight/lm3509_bl.c b/drivers/video/backlight/lm3509_bl.c index 24e1a19ff72d..53136c5e1460 100644 --- a/drivers/video/backlight/lm3509_bl.c +++ b/drivers/video/backlight/lm3509_bl.c @@ -311,8 +311,8 @@ static void lm3509_remove(struct i2c_client *client) } static const struct i2c_device_id lm3509_id[] = { - { LM3509_NAME }, - {} + { .name = LM3509_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3509_id); diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 37651c2b9393..8f49e59ce374 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -596,8 +596,8 @@ static void lm3630a_remove(struct i2c_client *client) } static const struct i2c_device_id lm3630a_id[] = { - { LM3630A_NAME }, - {} + { .name = LM3630A_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3630a_id); diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c index 37ccc631c498..ea748b80b737 100644 --- a/drivers/video/backlight/lm3639_bl.c +++ b/drivers/video/backlight/lm3639_bl.c @@ -403,8 +403,8 @@ static void lm3639_remove(struct i2c_client *client) } static const struct i2c_device_id lm3639_id[] = { - { LM3639_NAME }, - {} + { .name = LM3639_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3639_id); diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index d191560ce285..43a2123d3a4d 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -570,13 +570,13 @@ static const struct of_device_id lp855x_dt_ids[] __maybe_unused = { MODULE_DEVICE_TABLE(of, lp855x_dt_ids); static const struct i2c_device_id lp855x_ids[] = { - {"lp8550", LP8550}, - {"lp8551", LP8551}, - {"lp8552", LP8552}, - {"lp8553", LP8553}, - {"lp8555", LP8555}, - {"lp8556", LP8556}, - {"lp8557", LP8557}, + { .name = "lp8550", .driver_data = LP8550 }, + { .name = "lp8551", .driver_data = LP8551 }, + { .name = "lp8552", .driver_data = LP8552 }, + { .name = "lp8553", .driver_data = LP8553 }, + { .name = "lp8555", .driver_data = LP8555 }, + { .name = "lp8556", .driver_data = LP8556 }, + { .name = "lp8557", .driver_data = LP8557 }, { } }; MODULE_DEVICE_TABLE(i2c, lp855x_ids); diff --git a/drivers/video/backlight/lv5207lp.c b/drivers/video/backlight/lv5207lp.c index a205f004eab2..e643ab9c3536 100644 --- a/drivers/video/backlight/lv5207lp.c +++ b/drivers/video/backlight/lv5207lp.c @@ -131,7 +131,7 @@ static void lv5207lp_remove(struct i2c_client *client) } static const struct i2c_device_id lv5207lp_ids[] = { - { "lv5207lp" }, + { .name = "lv5207lp" }, { } }; MODULE_DEVICE_TABLE(i2c, lv5207lp_ids); diff --git a/drivers/video/backlight/mp3309c.c b/drivers/video/backlight/mp3309c.c index 9337110ce6e5..413cfe27dfd9 100644 --- a/drivers/video/backlight/mp3309c.c +++ b/drivers/video/backlight/mp3309c.c @@ -400,7 +400,7 @@ static const struct of_device_id mp3309c_match_table[] = { MODULE_DEVICE_TABLE(of, mp3309c_match_table); static const struct i2c_device_id mp3309c_id[] = { - { "mp3309c" }, + { .name = "mp3309c" }, { } }; MODULE_DEVICE_TABLE(i2c, mp3309c_id); From 9e3fcab6fbecebbcffeafeb5db612a57688cb7f4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 4 Jun 2026 15:06:27 -0700 Subject: [PATCH 3447/5214] perf test: Truncate printed test descriptions dynamically to avoid terminal wrapping When test descriptions are extremely long (e.g., the truncated perf.data graceful handling test is 103 characters long), they wrap across terminal boundaries. Because the ANSI escape code to delete the line (PERF_COLOR_DELETE_LINE) only clears a single terminal line, visual wrapping leaves orphan wrapped lines on the screen, which results in the test description being printed multiple times. Resolve this by checking the terminal width (get_term_dimensions) and dynamically truncating the printed test description to fit within the available columns, leaving safety space for the prefix index and status suffix. Also, remove the width padding from the test suite headers which do not display inline status messages. This prevents their trailing colons from wrapping onto new lines on standard width terminals. Finally, avoid GCC 16's -Wformat-truncation warnings by delegating the description padding to pr_info's %-*s format specifier instead of padding within a temporary buffer, and clamp the truncation limit to the temporary buffer's size. JUnit XML output and the failure summary report still print the full, untruncated test descriptions. Assisted-by: Gemini-CLI:Google Gemini 3.1 Pro Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 66 +++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index afc06cec4954..7e75f590f225 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include "util/term.h" #include "builtin.h" #include "config.h" #include "hist.h" @@ -413,19 +415,69 @@ static char *xml_escape(const char *str) return res ? res : strdup(""); } +static const char *format_test_description(const char *desc, int max_desc_width, + char *buf, size_t buf_sz) +{ + int len = strlen(desc); + + /* + * Clamp to buf_sz to prevent GCC format-truncation warnings + * when terminal width is very large. + */ + if (max_desc_width >= (int)buf_sz) + max_desc_width = buf_sz - 1; + + if (len > max_desc_width) { + snprintf(buf, buf_sz, "%.*s...", max_desc_width - 3, desc); + return buf; + } + return desc; +} + static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case, int result, int width, int running, const char *err_output, double elapsed) { + char desc_buf[256]; + const char *desc = test_description(t, curr_test_case); + struct winsize ws; + int max_desc_area_width; + int target_desc_area_width; + int desc_padding; + + get_term_dimensions(&ws); + /* + * Total terminal columns minus space for status e.g. " Running (12 active)" + * which is 20 chars, plus a margin of 3 chars = 23 chars. + */ + max_desc_area_width = ws.ws_col - 23; + if (max_desc_area_width < 40) + max_desc_area_width = 40; + + /* Standard test has prefix "%3d: " which is 5 chars */ + target_desc_area_width = width + 5; + if (target_desc_area_width > max_desc_area_width) + target_desc_area_width = max_desc_area_width; + if (test_suite__num_test_cases(t) > 1) { char prefix[32]; int len = snprintf(prefix, sizeof(prefix), "%3d.%1d:", curr_suite + 1, curr_test_case + 1); - int subw = len >= 4 ? width + 4 - len : width; - pr_info("%s %-*s:", prefix, subw, test_description(t, curr_test_case)); - } else - pr_info("%3d: %-*s:", curr_suite + 1, width, test_description(t, curr_test_case)); + desc_padding = target_desc_area_width - (len + 1); + if (desc_padding < 20) + desc_padding = 20; + + desc = format_test_description(desc, desc_padding, desc_buf, sizeof(desc_buf)); + pr_info("%s %-*s:", prefix, desc_padding, desc); + } else { + desc_padding = target_desc_area_width - 5; + if (desc_padding < 20) + desc_padding = 20; + + desc = format_test_description(desc, desc_padding, desc_buf, sizeof(desc_buf)); + pr_info("%3d: %-*s:", curr_suite + 1, desc_padding, desc); + } switch (result) { case TEST_RUNNING: @@ -709,7 +761,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c * sub test names. */ if (test_suite__num_test_cases(t) > 1 && curr_test_case == 0) - pr_info("%3d: %-*s:\n", curr_suite + 1, width, test_description(t, -1)); + pr_info("%3d: %s:\n", curr_suite + 1, test_description(t, -1)); /* * Busy loop reading from the child's stdout/stderr that are set to be @@ -985,7 +1037,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes if (next_child) { if (test_suite__num_test_cases(next_child->test) > 1 && last_suite_printed != next_child->suite_num) { - pr_info("%3d: %-*s:\n", next_child->suite_num + 1, width, + pr_info("%3d: %s:\n", next_child->suite_num + 1, test_description(next_child->test, -1)); last_suite_printed = next_child->suite_num; } @@ -1049,7 +1101,7 @@ static int finish_tests_parallel(struct child_test **child_tests, size_t num_tes if (test_suite__num_test_cases(child->test) > 1 && last_suite_printed != child->suite_num) { - pr_info("%3d: %-*s:\n", child->suite_num + 1, width, + pr_info("%3d: %s:\n", child->suite_num + 1, test_description(child->test, -1)); last_suite_printed = child->suite_num; } From 799e7cf2f0b50b34660b5ffce0f7d8dec376a0d5 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 5 Jun 2026 19:57:20 +0800 Subject: [PATCH 3448/5214] phy: freescale: phy-fsl-imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(), the function returns directly without calling pm_runtime_disable(), leaving runtime PM permanently enabled for the device. Fix this by using devm_pm_runtime_enable() so that cleanup is automatic on any probe failure or driver unbind. This also allows removing the manual err label and the .remove callback. Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support") Acked-by: Liu Ying Signed-off-by: Felix Gu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260605-lvds-v2-1-3ce7539d1104@gmail.com Signed-off-by: Vinod Koul --- .../phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index e2a1645000ae..368679436d86 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -345,7 +345,9 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = mixel_lvds_phy_reset(dev); if (ret) { @@ -355,17 +357,15 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) for (i = 0; i < PHY_NUM; i++) { lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); - if (!lvds_phy) { - ret = -ENOMEM; - goto err; - } + if (!lvds_phy) + return -ENOMEM; phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); if (IS_ERR(phy)) { ret = PTR_ERR(phy); dev_err(dev, "failed to create PHY for channel%d: %d\n", i, ret); - goto err; + return ret; } lvds_phy->phy = phy; @@ -379,19 +379,10 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) if (IS_ERR(phy_provider)) { ret = PTR_ERR(phy_provider); dev_err(dev, "failed to register PHY provider: %d\n", ret); - goto err; + return ret; } return 0; -err: - pm_runtime_disable(dev); - - return ret; -} - -static void mixel_lvds_phy_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); } static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) @@ -432,7 +423,6 @@ MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); static struct platform_driver mixel_lvds_phy_driver = { .probe = mixel_lvds_phy_probe, - .remove = mixel_lvds_phy_remove, .driver = { .pm = &mixel_lvds_phy_pm_ops, .name = "mixel-lvds-phy", From baacd0af457c2505137c4774e71efe044c11b26d Mon Sep 17 00:00:00 2001 From: Dimitri Fedrau Date: Tue, 2 Jun 2026 10:25:37 +0200 Subject: [PATCH 3449/5214] dt-bindings: phy: add support for NXPs TJA1145 CAN transceiver Adding documentation for NXPs TJA1145 CAN transceiver, which resides like the ti,tcan104x-can.yaml in the same directory as other generic PHY subsystem bindings. At the moment there is only support for simple PHYs by using regulator bindings in combination with can-transceiver.yaml or PHYs that implement the generic PHY subsystem like the NXP TJA1145. Reviewed-by: Conor Dooley Signed-off-by: Dimitri Fedrau Link: https://patch.msgid.link/20260602-tja1145-support-v6-1-0e0ffc8ee63d@liebherr.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/nxp,tja1145.yaml | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/nxp,tja1145.yaml diff --git a/Documentation/devicetree/bindings/phy/nxp,tja1145.yaml b/Documentation/devicetree/bindings/phy/nxp,tja1145.yaml new file mode 100644 index 000000000000..fb068f7d774f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nxp,tja1145.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/nxp,tja1145.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TJA1145 CAN transceiver + +maintainers: + - Dimitri Fedrau + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: nxp,tja1145 + + interrupts: + maxItems: 1 + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + spi-cpha: true + + spi-max-frequency: + maximum: 4000000 + + spi-cs-setup-delay-ns: + minimum: 50 + default: 50 + + spi-cs-hold-delay-ns: + minimum: 50 + default: 50 + + spi-cs-inactive-delay-ns: + minimum: 250 + default: 250 + + vcc-supply: + description: + CAN transceiver supply voltage + + vio-supply: + description: + Supply voltage for I/O level adaptor + + vbat-supply: + description: + Battery supply voltage + +required: + - compatible + - reg + - "#phy-cells" + - spi-cpha + +additionalProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + can-phy@0 { + compatible = "nxp,tja1145"; + interrupt-parent = <&gpio0>; + interrupts = <31 IRQ_TYPE_LEVEL_LOW>; + reg = <0>; + #phy-cells = <0>; + spi-cpha; + spi-max-frequency = <4000000>; + spi-cs-setup-delay-ns = <50>; + spi-cs-hold-delay-ns = <50>; + spi-cs-inactive-delay-ns = <250>; + vcc-supply = <®_5v0>; + vio-supply = <®_3v3>; + vbat-supply = <®_24v0>; + }; + }; From e5a9c1c917b59a4aff066b9f317501834c6d5af2 Mon Sep 17 00:00:00 2001 From: Dimitri Fedrau Date: Tue, 2 Jun 2026 10:25:38 +0200 Subject: [PATCH 3450/5214] phy: add basic support for NXPs TJA1145 CAN transceiver Add basic driver support for NXPs TJA1145 CAN transceiver which brings the PHY up/down by switching to normal/standby mode using SPI commands. Tested-by: lee.lockhey@gmail.com Reviewed-by: Marc Kleine-Budde Signed-off-by: Dimitri Fedrau Link: https://patch.msgid.link/20260602-tja1145-support-v6-2-0e0ffc8ee63d@liebherr.com Signed-off-by: Vinod Koul --- drivers/phy/Kconfig | 10 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-nxp-tja1145.c | 184 ++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/phy/phy-nxp-tja1145.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 7ef342f4bbb6..19f3b7d12b7d 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -136,6 +136,16 @@ config PHY_NXP_PTN3222 schemes. It supports all three USB 2.0 data rates: Low Speed, Full Speed and High Speed. +config PHY_NXP_TJA1145 + tristate "NXP TJA1145 CAN transceiver PHY" + select GENERIC_PHY + select REGMAP_SPI + depends on SPI + help + This option enables support for NXPs TJA1145 CAN transceiver as a PHY. + This driver provides function for putting the transceiver in various + functional modes using SPI commands. + config PHY_PISTACHIO_USB tristate "IMG Pistachio USB2.0 PHY driver" depends on MIPS || COMPILE_TEST diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 1e69ed50fb17..d7aa516bcc49 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_GOOGLE_USB) += phy-google-usb.o obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o +obj-$(CONFIG_PHY_NXP_TJA1145) += phy-nxp-tja1145.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_SNPS_EUSB2) += phy-snps-eusb2.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o diff --git a/drivers/phy/phy-nxp-tja1145.c b/drivers/phy/phy-nxp-tja1145.c new file mode 100644 index 000000000000..1e8bd169743a --- /dev/null +++ b/drivers/phy/phy-nxp-tja1145.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH + */ +#include +#include + +#include +#include + +#define TJA1145_MODE_CTRL 0x01 +#define TJA1145_MODE_CTRL_MC GENMASK(2, 0) +#define TJA1145_MODE_CTRL_STBY BIT(2) +#define TJA1145_MODE_CTRL_NORMAL TJA1145_MODE_CTRL_MC + +#define TJA1145_CAN_CTRL 0x20 +#define TJA1145_CAN_CTRL_CMC GENMASK(1, 0) +#define TJA1145_CAN_CTRL_ACTIVE BIT(1) + +#define TJA1145_IDENT 0x7e +#define TJA1145_IDENT_TJA1145T 0x70 + +#define TJA1145_SPI_READ_BIT BIT(0) +#define TJA1145T_MAX_BITRATE 1000000 + +static int tja1145_phy_power_on(struct phy *phy) +{ + struct regmap *map = phy_get_drvdata(phy); + int ret; + + /* + * Switch operating mode to normal which is the active operating mode. + * In this mode, the device is fully operational. + */ + ret = regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC, + TJA1145_MODE_CTRL_NORMAL); + if (ret) + return ret; + + /* + * Switch to CAN operating mode active where the PHY can transmit and + * receive data. + */ + return regmap_update_bits(map, TJA1145_CAN_CTRL, TJA1145_CAN_CTRL_CMC, + TJA1145_CAN_CTRL_ACTIVE); +} + +static int tja1145_phy_power_off(struct phy *phy) +{ + struct regmap *map = phy_get_drvdata(phy); + + /* + * Switch to operating mode standby, the PHY is unable to transmit or + * receive data in standby mode. + */ + return regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC, + TJA1145_MODE_CTRL_STBY); +} + +static const struct phy_ops tja1145_phy_ops = { + .power_on = tja1145_phy_power_on, + .power_off = tja1145_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct regmap_range tja1145_wr_holes_ranges[] = { + regmap_reg_range(0x00, 0x00), + regmap_reg_range(0x02, 0x03), + regmap_reg_range(0x05, 0x05), + regmap_reg_range(0x0b, 0x1f), + regmap_reg_range(0x21, 0x22), + regmap_reg_range(0x24, 0x25), + regmap_reg_range(0x30, 0x4b), + regmap_reg_range(0x4d, 0x60), + regmap_reg_range(0x62, 0x62), + regmap_reg_range(0x65, 0x67), + regmap_reg_range(0x70, 0xff), +}; + +static const struct regmap_access_table tja1145_wr_table = { + .no_ranges = tja1145_wr_holes_ranges, + .n_no_ranges = ARRAY_SIZE(tja1145_wr_holes_ranges), +}; + +static const struct regmap_range tja1145_rd_holes_ranges[] = { + regmap_reg_range(0x00, 0x00), + regmap_reg_range(0x02, 0x02), + regmap_reg_range(0x05, 0x05), + regmap_reg_range(0x0b, 0x1f), + regmap_reg_range(0x21, 0x21), + regmap_reg_range(0x24, 0x25), + regmap_reg_range(0x30, 0x4a), + regmap_reg_range(0x4d, 0x5f), + regmap_reg_range(0x62, 0x62), + regmap_reg_range(0x65, 0x67), + regmap_reg_range(0x70, 0x7d), + regmap_reg_range(0x7f, 0xff), +}; + +static const struct regmap_access_table tja1145_rd_table = { + .no_ranges = tja1145_rd_holes_ranges, + .n_no_ranges = ARRAY_SIZE(tja1145_rd_holes_ranges), +}; + +static const struct regmap_config tja1145_regmap_config = { + .reg_bits = 8, + .reg_shift = -1, + .val_bits = 8, + .wr_table = &tja1145_wr_table, + .rd_table = &tja1145_rd_table, + .read_flag_mask = TJA1145_SPI_READ_BIT, + .max_register = TJA1145_IDENT, +}; + +static int tja1145_check_ident(struct device *dev, struct regmap *map) +{ + unsigned int val; + int ret; + + ret = regmap_read(map, TJA1145_IDENT, &val); + if (ret) + return ret; + + if (val != TJA1145_IDENT_TJA1145T) { + dev_err(dev, "Expected device id: 0x%02x, got: 0x%02x\n", + TJA1145_IDENT_TJA1145T, val); + return -ENODEV; + } + + return 0; +} + +static int tja1145_probe(struct spi_device *spi) +{ + struct phy_provider *phy_provider; + struct device *dev = &spi->dev; + struct regmap *map; + struct phy *phy; + int ret; + + map = devm_regmap_init_spi(spi, &tja1145_regmap_config); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), "failed to init regmap\n"); + + ret = tja1145_check_ident(dev, map); + if (ret) + return dev_err_probe(dev, ret, "failed to identify device\n"); + + phy = devm_phy_create(dev, dev->of_node, &tja1145_phy_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), "failed to create PHY\n"); + + phy->attrs.max_link_rate = TJA1145T_MAX_BITRATE; + phy_set_drvdata(phy, map); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct spi_device_id tja1145_spi_id[] = { + { "tja1145" }, + { } +}; +MODULE_DEVICE_TABLE(spi, tja1145_spi_id); + +static const struct of_device_id tja1145_of_match[] = { + { .compatible = "nxp,tja1145" }, + { } +}; +MODULE_DEVICE_TABLE(of, tja1145_of_match); + +static struct spi_driver tja1145_driver = { + .driver = { + .name = "tja1145", + .of_match_table = tja1145_of_match, + }, + .probe = tja1145_probe, + .id_table = tja1145_spi_id, +}; +module_spi_driver(tja1145_driver); + +MODULE_DESCRIPTION("NXP TJA1145 CAN transceiver PHY driver"); +MODULE_AUTHOR("Dimitri Fedrau "); +MODULE_LICENSE("GPL"); From 1e8065dfac34d1217f961e079959b71c69123417 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:29:14 +0200 Subject: [PATCH 3451/5214] phy: Move MODULE_DEVICE_TABLE next to the table itself By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the privers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260505102913.188406-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/phy-bcm-ns-usb3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c index 6e56498d0644..9240600aeef6 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c @@ -65,6 +65,7 @@ static const struct of_device_id bcm_ns_usb3_id_table[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, u16 value); @@ -242,4 +243,3 @@ mdio_module_driver(bcm_ns_usb3_mdio_driver); MODULE_DESCRIPTION("Broadcom Northstar USB 3.0 PHY Driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); From 2ace2e949979b82f82f12dd76d7c5a6145246ca3 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 20 May 2026 12:28:59 +0200 Subject: [PATCH 3452/5214] phy: rockchip: inno-usb2: Add missing clkout_ctl_phy kerneldoc Add the missing documentation for the newly added clkout_ctl_phy field. Fixes: 2775541de058 ("phy: rockchip: inno-usb2: Add clkout_ctl_phy support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605150315.MyBNQOPB-lkp@intel.com/ Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260520102859.1357411-1-heiko@sntech.de Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 133cfd6624e8..7d8a533f24ae 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -170,7 +170,8 @@ struct rockchip_usb2phy_port_cfg { * @reg: the address offset of grf for usb-phy config. * @num_ports: specify how many ports that the phy has. * @phy_tuning: phy default parameters tuning. - * @clkout_ctl: keep on/turn off output clk of phy. + * @clkout_ctl: register to enable output clk of phy, when set in GRF + * @clkout_ctl_phy: register to enable output clk of phy, when set inside phy * @port_cfgs: usb-phy port configurations. * @chg_det: charger detection registers. */ From 3bdd6fc11fbfa8249483f4b716ead51e43e3a0cd Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:23 +0200 Subject: [PATCH 3453/5214] platform/x86/amd/pmc: Check for intermediate wakeup in function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor code introduced by commit 9f5595d5f03f ("pmc: Require at least 2.5 seconds between HW sleep cycles") to allow adding different conditions for that delay in an upcoming change. Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index ccb37383b337..25509099a958 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -670,6 +670,19 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) return rc; } +static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) +{ + /* + * Starting a new HW sleep cycle right after waking from one + * can cause electrical problems triggering the over voltage protection. + * That is avoided by delaying the next suspend a bit, see also + * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/ + */ + struct smu_metrics table; + + return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -702,11 +715,9 @@ static void amd_pmc_s2idle_prepare(void) static void amd_pmc_s2idle_check(void) { struct amd_pmc_dev *pdev = &pmc; - struct smu_metrics table; int rc; - /* Avoid triggering OVP */ - if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status) + if (amd_pmc_intermediate_wakeup_need_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ From 9b9e60dd31da054a37d601e9fcabdfd8a2bff354 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:24 +0200 Subject: [PATCH 3454/5214] platform/x86/amd/pmc: Delay suspend for some Lenovo Laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some IdeaPad Slim 3 devices and similar with AMD CPUs have a nonfunctional keyboard and lid switch after s2idle. It helps to delay suspend by 2.5 seconds so the EC has some time to do whatever it needs to get done before suspend - unfortunately at least on my 16ABR8 waking it with a timer (wakealarm) still triggers the issue, but at least normal resume via keypress or lid works fine. On the 14ARP10 wakealarm has been reported to also work fine with this patch. This issue has been reported for many different devices, this patch has been tested with the Zen3-based IdeaPad Slim 3 16ABR8 (82XR) and the Zen3+-based IdeaPad Slim 3 14ARP10 (83K6) and IdeaPad Slim 3 15ARP10 (83MM). Reported-by: Sindre Henriksen Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Tested-by: Sindre Henriksen Suggested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-3-daniel@gibson.sh Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 39 +++++++++++++++++++++++ drivers/platform/x86/amd/pmc/pmc.c | 24 +++++++++++++- drivers/platform/x86/amd/pmc/pmc.h | 1 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index 24506e342943..74ddf1d8289a 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -18,6 +18,7 @@ struct quirk_entry { u32 s2idle_bug_mmio; bool spurious_8042; + bool need_suspend_delay; }; static struct quirk_entry quirk_s2idle_bug = { @@ -33,6 +34,10 @@ static struct quirk_entry quirk_s2idle_spurious_8042 = { .spurious_8042 = true, }; +static struct quirk_entry quirk_s2idle_need_suspend_delay = { + .need_suspend_delay = true, +}; + static const struct dmi_system_id fwbug_list[] = { { .ident = "L14 Gen2 AMD", @@ -203,6 +208,35 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ + { + .ident = "Zen3-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + /* + * Note: there are also some Zen2-based 82X* devices that + * need different quirks, they're already handled above + */ + DMI_MATCH(DMI_PRODUCT_NAME, "82X"), + } + }, + { + .ident = "Zen3+-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83K"), + } + }, + { + .ident = "IdeaPad Slim 3 15ARP10 (83MM)", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83MM"), + } + }, /* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */ { .ident = "Thinkpad L14 Gen3", @@ -356,6 +390,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); } +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev) +{ + return dev->quirks && dev->quirks->need_suspend_delay; +} + void amd_pmc_quirks_init(struct amd_pmc_dev *dev) { const struct dmi_system_id *dmi_id; diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 25509099a958..758abcf9f094 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -683,6 +683,27 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; } +static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) +{ + /* + * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some + * me-time before sleeping or they get uncooperative after waking + * up and don't send events for keyboard and lid switch anymore. + * + * Unfortunately this doesn't entirely fix the problem: It can still + * happen when resuming with a timer (wakealarm), but at least the + * more common usecases (wakeup by opening lid or pressing a key) + * work fine with this workaround. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 + */ + if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + return false; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -717,7 +738,8 @@ static void amd_pmc_s2idle_check(void) struct amd_pmc_dev *pdev = &pmc; int rc; - if (amd_pmc_intermediate_wakeup_need_delay(pdev)) + if (amd_pmc_intermediate_wakeup_need_delay(pdev) || + amd_pmc_want_suspend_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 36756e25b4bd..1ef182bb240d 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -165,6 +165,7 @@ enum amd_pmc_def { }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev); void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); From 428b9fd2dce50b4dc5cd9ade10b92efcf57ce7aa Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:25 +0200 Subject: [PATCH 3455/5214] platform/x86/amd/pmc: Add delay_suspend module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling the new delay_suspend module parameter delays suspend for 2.5 seconds which is known to help for some AMD-based Lenovo Laptops that otherwise failed to send/receive events for key presses or the lid switch after s2idle. Apparently the EC needs to do some things in the background before suspend or it gets into a bad state. There are many reports of AMD-based laptops (mostly but not exclusively IdeaPads) about similar issues on the web; this parameter gives affected users an easy way to try out if their issues have the same root cause and to work around them until their specific device is added to the quirks list. The parameter description has a note encouraging users to report their device so it can be added to the quirks list, inspired by a similar request in parameter descriptions of the ideapad-laptop module. The module parameter can be set to "1" to explicitly enable it, "0" to disable it even on devices that are assumed to be affected, or -1 (the default) to enable it if the device is assumed to be affected (according to fwbug_list[]) Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-4-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 758abcf9f094..ce97c27bc362 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,11 @@ static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static int delay_suspend = -1; +module_param(delay_suspend, int, 0644); +MODULE_PARM_DESC(delay_suspend, + "Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org"); + static struct amd_pmc_dev pmc; static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) @@ -697,8 +703,23 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ - if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (amd_pmc_quirk_need_suspend_delay(pdev)) { + /* + * delay_suspend=1 force-enables this, otherwise it can be + * disabled with disable_workarounds or delay_suspend=0 + */ + if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + } else if (delay_suspend == 1) { + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; From 037f0b03c663a247366673a807834389107995b7 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:26 +0200 Subject: [PATCH 3456/5214] platform/x86/amd/pmc: Don't log during intermediate wakeups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ECs in the IdeaPads that need the delay_suspend quirk send lots of messages when charging, which not only causes intermediate wakeups when suspended, but also prevents the device from reaching the deepest suspend state. Because of this amd_pmc_intermediate_wakeup_need_delay() returns false during intermediate wakeups and amd_pmc_want_suspend_delay() is called. So far it always logged its "Delaying suspend by 2.5s ..." messages then, which spams dmesg. This commit makes sure that those messages are only logged once per suspend. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-5-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 39 ++++++++++++++++++++++++------ drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index ce97c27bc362..b2eb9909f6a4 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -691,6 +691,20 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) { + /* + * intermediate_wakeup implies that the machine didn't get to deepest sleep + * state before - otherwise this function isn't called in amd_pmc_s2idle_check() + * because amd_pmc_intermediate_wakeup_need_delay() returns true first. + * On some IdeaPads that happens when charging, because the EC seems + * to send lots of messages then that wake the machine. + * + * But even in that case, the sleep here is necessary (on those IdeaPads), + * otherwise they wake up completely (resume) after a few seconds. + * So this variable is only used to avoid spamming dmesg on each + * intermediate wakeup. + */ + bool intermediate_wakeup = !pdev->is_first_check_after_suspend; + /* * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some * me-time before sleeping or they get uncooperative after waking @@ -709,17 +723,20 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * disabled with disable_workarounds or delay_suspend=0 */ if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); return true; } - dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); } else if (delay_suspend == 1) { - dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; @@ -731,6 +748,9 @@ static void amd_pmc_s2idle_prepare(void) int rc; u32 arg = 1; + /* Reset this variable because this is a fresh suspend */ + pdev->is_first_check_after_suspend = true; + /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_setup_smu_logging(pdev); @@ -769,6 +789,9 @@ static void amd_pmc_s2idle_check(void) rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); + + /* remember that first check after suspend is done (until next prepare) */ + pdev->is_first_check_after_suspend = false; } static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 1ef182bb240d..6973a639d7e3 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -136,6 +136,7 @@ struct amd_pmc_dev { struct dentry *dbgfs_dir; struct quirk_entry *quirks; bool disable_8042_wakeup; + bool is_first_check_after_suspend; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; const struct amd_pmc_cpu_info *cpu_info; From aae636b5463d9957fadba71e83e7cfc157416974 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Wed, 3 Jun 2026 09:52:21 -0500 Subject: [PATCH 3457/5214] fs: fat: inode: replace sprintf() with scnprintf() The kernel documentation notes that sprintf() is deprecated and unsafe. Replace it with the more preferred scnprintf() to help with hardening. Link: https://lore.kernel.org/20260603145222.59012-1-m32285159@gmail.com Signed-off-by: Maxwell Doose Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 28f78df086ef..b032bbc6855c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1786,7 +1786,7 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, */ error = -EINVAL; - sprintf(buf, "cp%d", sbi->options.codepage); + scnprintf(buf, sizeof(buf), "cp%d", sbi->options.codepage); sbi->nls_disk = load_nls(buf); if (!sbi->nls_disk) { fat_msg(sb, KERN_ERR, "codepage %s not found", buf); From 0f1f777b309bf9ac90f9ad2a93ae6ab68a1376a1 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 4 Jun 2026 00:58:40 +0200 Subject: [PATCH 3458/5214] mailmap: update Alexander Sverdlin's Email addresses - add kernel.org address - add sverdlin.org address (as primary address) - drop siemens.com address (for accounting purposes only) Link: https://lore.kernel.org/20260603225847.1849399-1-asv@kernel.org Signed-off-by: Alexander Sverdlin Signed-off-by: Andrew Morton --- .mailmap | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.mailmap b/.mailmap index cf67173dcd51..ba94d4a1fe55 100644 --- a/.mailmap +++ b/.mailmap @@ -36,13 +36,14 @@ Alexander Lobakin Alexander Mikhalitsyn Alexander Mikhalitsyn Alexander Mikhalitsyn -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin Alexandre Belloni Alexandre Ghiti Alexei Avshalom Lazar From 5ef8a1c7aa7f095b0913aed643df4a6da0d8b275 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Fri, 5 Jun 2026 10:54:23 +0200 Subject: [PATCH 3459/5214] MAINTAINERS: add Alexander as a kcov reviewer Also move Dmitry Vyukov to the bottom of the list. Link: https://lore.kernel.org/20260605085423.1962700-1-glider@google.com Signed-off-by: Alexander Potapenko Reviewed-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Marco Elver Signed-off-by: Andrew Morton --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 49a264b999e2..7e1856a3f13c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13838,8 +13838,9 @@ F: fs/proc/kcore.c F: include/linux/kcore.h KCOV -R: Dmitry Vyukov R: Andrey Konovalov +R: Alexander Potapenko +R: Dmitry Vyukov L: kasan-dev@googlegroups.com S: Maintained B: https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%20Management From 66cbd504306bf354231b1fbe43585b9aaa242d28 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Fri, 5 Jun 2026 14:57:04 +0800 Subject: [PATCH 3460/5214] checkpatch: cuppress warnings when Reported-by: is followed by Link: > The tag should be followed by a Closes: tag pointing to the report, > unless the report is not available on the web. The Link: tag can be > used instead of Closes: if the patch fixes a part of the issue(s) > being reported. According to Documentation/process/submitting-patches.rst, Link: is also acceptable to follow a Reported-by:, if the patch fixes a part of the issue(s) being reported. Link: https://lore.kernel.org/20260605-checkpatch-v1-1-8c68ae618513@linux.dev Signed-off-by: Cryolitia PukNgae Reviewed-by: Petr Vorel Cc: Andy Whitcroft Cc: Cheng Nie Cc: Dwaipayan Ray Cc: Joe Perches Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3727156e4cca..d9af266c63df 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3253,10 +3253,10 @@ sub process { if ($sign_off =~ /^reported(?:|-and-tested)-by:$/i) { if (!defined $lines[$linenr]) { WARN("BAD_REPORTED_BY_LINK", - "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . "\n"); - } elsif ($rawlines[$linenr] !~ /^closes:\s*/i) { + "Reported-by: should be immediately followed by Closes: or Link: with a URL to the report\n" . $herecurr . "\n"); + } elsif ($rawlines[$linenr] !~ /^(closes|link):\s*/i) { WARN("BAD_REPORTED_BY_LINK", - "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n"); + "Reported-by: should be immediately followed by Closes: or Link: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n"); } } } From 89009392c80da5da00876c8334ff20028e6e3eb6 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 4 Jun 2026 22:52:51 -0500 Subject: [PATCH 3461/5214] fs: efs: remove unneeded debug prints The current code uses debug prints conditionally compiled with #ifdef DEBUG. However, that code, when compiled, causes compiler errors due to incompatible formatters and undefined variables, notably: fs/efs/file.c: In function `efs_get_block': fs/efs/file.c:26:35: error: `block' undeclared (first use in this function); did you mean `iblock'? 26 | __func__, block, inode->i_blocks, inode->i_size); | ^~~~~ and: fs/efs/file.c: In function `efs_bmap': ./include/linux/kern_levels.h:5:25: error: format `%ld' expects argument of type `long int', but argument 4 has type `blkcnt_t' {aka `long long unsigned int'} [-Werror=format=] 5 | #define KERN_SOH "\001" /* ASCII Start Of Header */ | ^~~~~~ which also extends to the other formatters. As this part of the code has been dead for just about 14 years now, it has not been modernized to stay compatible with the most recent gcc compilers. Fix these issues by removing the debug prints. Link: https://lore.kernel.org/20260605035251.89305-2-m32285159@gmail.com Fixes: f403d1dbac6d ("fs/efs: add pr_fmt / use __func__") Signed-off-by: Maxwell Doose Suggested-by: Andrew Morton Cc: Fabian Frederick Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/efs/file.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/fs/efs/file.c b/fs/efs/file.c index 9e641da6fab2..9153dfe79bbc 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -18,16 +18,9 @@ int efs_get_block(struct inode *inode, sector_t iblock, if (create) return error; - if (iblock >= inode->i_blocks) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (iblock >= inode->i_blocks) return 0; - } + phys = efs_map_block(inode, iblock); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -42,16 +35,8 @@ int efs_bmap(struct inode *inode, efs_block_t block) { } /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (!(block < inode->i_blocks)) return 0; - } return efs_map_block(inode, block); } From f2737dc40d2ef3e9f3f9395d61f53f6668306a71 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 00:30:37 +0000 Subject: [PATCH 3462/5214] lib/test_firmware: allocate the configured into_buf size The batched into_buf test path allocates TEST_FIRMWARE_BUF_SIZE bytes unconditionally, but then passes test_fw_config->buf_size to request_firmware_into_buf() or request_partial_firmware_into_buf(). Userspace can set config_buf_size above TEST_FIRMWARE_BUF_SIZE before triggering a batched request. If the firmware file is large enough, the firmware loader writes past the end of the 1 KiB test buffer. Allocate the buffer with the same size that the test passes to the firmware API so config_buf_size remains the actual buffer size under test. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260605003038.2005840-1-sam.moelius@trailofbits.com Signed-off-by: Samuel Moelius Reviewed-by: Andrew Morton Cc: Kees Cook Cc: Luis R. Rodriguez Cc: Scott Branden Signed-off-by: Andrew Morton --- lib/test_firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index b471d720879a..7459bba65444 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -867,7 +867,7 @@ static int test_fw_run_batch_request(void *data) if (test_fw_config->into_buf) { void *test_buf; - test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); + test_buf = kzalloc(test_fw_config->buf_size, GFP_KERNEL); if (!test_buf) return -ENOMEM; From ed0428e92812b5d4e9d5c4cbcab9a4f107137dfc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 9 Jun 2026 23:05:42 -0700 Subject: [PATCH 3463/5214] Input: ipaq-micro-keys - fix potential deadlock The driver acquires the micro->lock spinlock in process context (in micro_key_start() and micro_key_stop()) without disabling interrupts. However, this lock is also acquired in hardirq context by the MFD core rx handler (micro_rx_msg()) which is called from the serial ISR. This can lead to a lock inversion deadlock if the interrupt fires on the same CPU while the process context holds the lock. Fix this by using guard(spinlock_irq) instead of guard(spinlock) in micro_key_start() and micro_key_stop() to disable interrupts while holding the lock. Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/aij-pfaKK-Nna7wf@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ipaq-micro-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c index 695ef3c2081a..3c7d6aa0fe29 100644 --- a/drivers/input/keyboard/ipaq-micro-keys.c +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -54,7 +54,7 @@ static void micro_key_receive(void *data, int len, unsigned char *msg) static void micro_key_start(struct ipaq_micro_keys *keys) { - guard(spinlock)(&keys->micro->lock); + guard(spinlock_irq)(&keys->micro->lock); keys->micro->key = micro_key_receive; keys->micro->key_data = keys; @@ -62,7 +62,7 @@ static void micro_key_start(struct ipaq_micro_keys *keys) static void micro_key_stop(struct ipaq_micro_keys *keys) { - guard(spinlock)(&keys->micro->lock); + guard(spinlock_irq)(&keys->micro->lock); keys->micro->key = NULL; keys->micro->key_data = NULL; From ff25a3b1cd607b268b7b3e47f068460ea1eeb956 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 10 Jun 2026 16:02:42 -0700 Subject: [PATCH 3464/5214] Input: ipaq-micro-keys - add length check in micro_key_receive The driver accesses the message payload (msg[0]) without checking if the length is greater than zero. The parent MFD driver can produce a payload with a length of 0, in which case msg[0] would be uninitialized or stale. Add a check to return early if len is less than 1. Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/aintAvTyw4CVb5hG@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ipaq-micro-keys.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c index 3c7d6aa0fe29..ebd991de70f8 100644 --- a/drivers/input/keyboard/ipaq-micro-keys.c +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -43,6 +43,9 @@ static void micro_key_receive(void *data, int len, unsigned char *msg) struct ipaq_micro_keys *keys = data; int key, down; + if (len < 1) + return; + down = 0x80 & msg[0]; key = 0x7f & msg[0]; From 26b67fa10ef84ea667942491b50e6261a45f098d Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:17 +0545 Subject: [PATCH 3465/5214] PCI: dwc: Avoid dwc_pcie_rasdes_debugfs_deinit() NULL dereference when no RAS DES capability dwc_pcie_rasdes_debugfs_init() returns success when the controller has no RAS DES capability, leaving pci->debugfs->rasdes_info unset. The common debugfs teardown path still calls dwc_pcie_rasdes_debugfs_deinit(), which dereferences rasdes_info unconditionally. Return early when no RAS DES state was allocated. In that case no RAS DES mutex was initialized, so there is nothing to destroy. Fixes: 4fbfa17f9a07 ("PCI: dwc: Add debugfs based Silicon Debug support for DWC") Signed-off-by: Shuvam Pandey [mani: reworded subject] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/0f97352506d8d813f70f441de4d63fcd5b7d1c3e.1779123847.git.shuvampandey1@gmail.com --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index ddbce69b2e9f..c24cbc4677a3 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -550,6 +550,9 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + if (!rinfo) + return; + mutex_destroy(&rinfo->reg_event_lock); } From 9b2ffcfb32e27a936da867e14b29e3648f3d4ac4 Mon Sep 17 00:00:00 2001 From: Chunkai Deng Date: Tue, 26 May 2026 11:38:03 +0800 Subject: [PATCH 3466/5214] dt-bindings: mailbox: qcom: Add IPCC support for Maili Platform Document the Inter-Processor Communication Controller on the Qualcomm Maili Platform, which will be used to route interrupts across various subsystems found on the SoC. Signed-off-by: Chunkai Deng Reviewed-by: Manivannan Sadhasivam Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index fae20a6615f6..3839e1f5f904 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -28,6 +28,7 @@ properties: - qcom,glymur-ipcc - qcom,hawi-ipcc - qcom,kaanapali-ipcc + - qcom,maili-ipcc - qcom,milos-ipcc - qcom,qcs8300-ipcc - qcom,qdu1000-ipcc From 904708945c07b2b27558b6ffe8923b3df99448f4 Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 11 Jun 2026 12:13:46 +0100 Subject: [PATCH 3467/5214] perf test: Compile named_threads workload with -O0 The work loop relies on the compiler not optimizing it away, although named_threads_work is not static for that reason, the compiler could still do it. Fix it by compiling without optimization. Also add -fno-inline for consistency and in case anyone wants to look at callstacks. Fixes: b5dd510be55e8670 ("perf test: Add named_threads workload") Closes: https://lore.kernel.org/all/20260609160001.2739E1F00893@smtp.kernel.org Reported-by: sashiko-bot Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/workloads/Build | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 75b377934a0e..7bb4b9829ba2 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -26,3 +26,4 @@ CFLAGS_datasym.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_inlineloop.o = -g -O2 CFLAGS_deterministic.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE +CFLAGS_named_threads.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE From 325aabebbbc0520f95db3974d46e13a081cf6c28 Mon Sep 17 00:00:00 2001 From: Chun-Tse Shao Date: Thu, 11 Jun 2026 14:56:32 -0700 Subject: [PATCH 3468/5214] perf stat: Fix false NMI watchdog warning in aggregation modes In aggregation modes (e.g. --per-socket, --per-die, etc.), a counter might not be scheduled or counted on specific aggregate groups if it was not assigned to the CPUs belonging to those groups. However, the printout() check triggers the "print_free_counters_hint" logic unconditionally for any supported counter with a missing count. This results in a false "Some events weren't counted. Try disabling the NMI watchdog" warning. Furthermore, the NMI watchdog only reserves performance counters on core PMUs. Uncore PMU events (e.g. CHA, IMC) are not affected by the NMI watchdog, but their failures also falsely triggered this warning. This warning was originally introduced in commit 02d492e5dcb72c00 ("perf stat: Issue a HW watchdog disable hint") To fix this, restrict setting of print_free_counters_hint to only trigger for core PMU events by checking counter->pmu and counter->pmu->is_core. Example before/after: $ perf stat -M lpm_miss_lat --metric-only --per-socket -a -- sleep 1 Before: Performance counter stats for 'system wide': ns lpm_miss_lat_rem ns lpm_miss_lat_loc S0 126 202.3 207.9 S1 126 231.9 259.3 1.006029831 seconds time elapsed Some events weren't counted. Try disabling the NMI watchdog: echo 0 > /proc/sys/kernel/nmi_watchdog perf stat ... echo 1 > /proc/sys/kernel/nmi_watchdog After: Performance counter stats for 'system wide': ns lpm_miss_lat_rem ns lpm_miss_lat_loc S0 126 202.3 207.9 S1 126 231.9 259.3 1.006029831 seconds time elapsed Assisted-by: Gemini:gemini-next Reviewed-by: Ian Rogers Signed-off-by: Chun-Tse Shao Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat-display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 2b69d238858c..0a5750bb59fa 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -821,9 +821,9 @@ static void printout(struct perf_stat_config *config, struct outstate *os, ok = false; if (counter->supported) { - if (!evlist__has_hybrid_pmus(counter->evlist)) { + if (!evlist__has_hybrid_pmus(counter->evlist) && + counter->pmu && counter->pmu->is_core) config->print_free_counters_hint = 1; - } } } From 50cc34be04a0ea7522b739c9c7a71367cfbc489c Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:55 -0700 Subject: [PATCH 3469/5214] cxl/test: Unregister cxl_acpi in cxl_test_init() error path In cxl_test_init(), Once cxl_mock_platform_device_add() succeeds, all error paths after needs to call platform_device_unregister() instead of platform_device_put() to clean up. Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reported-by: sashiko-bot Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230355.198912-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 4281d34cd0e7..7351fb87c7ab 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1960,7 +1960,7 @@ static __init int cxl_test_init(void) err_mem: cxl_mem_exit(); err_root: - platform_device_put(cxl_acpi); + platform_device_unregister(cxl_acpi); err_rch: cxl_rch_topo_exit(); err_single: From dfe28c8592538152e9611341dae6f7be1735b3f1 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:05 -0700 Subject: [PATCH 3470/5214] cxl/test: Add check after kzalloc() memory in alloc_mock_res() alloc_mock_res() calls kzalloc() without checking the return value. Add scope based resource management to deal with the allocated memory cleanly. Reported-by: sashiko-bot Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230305.197390-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 7351fb87c7ab..9a0faf70e9b1 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -433,12 +433,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -453,7 +457,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } /* Only update CFMWS0 as this is used by the auto region. */ From a98518e72439fd42cbfe641c2896543cb088e3d1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:31 -0700 Subject: [PATCH 3471/5214] Input: rmi4 - fix register descriptor address calculation When reading the register descriptor, the base address is incremented by 1 to read the presence register block. However, after reading the presence register block, the address is incorrectly incremented by only 1 byte (++addr) instead of the actual size of the presence block (size_presence_reg). This causes the subsequent structure block read to read from the wrong memory location if the presence block is larger than 1 byte. Fix this by advancing the address by size_presence_reg. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index ccd9338a44db..06f5e3000cf0 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -594,7 +594,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, ret = rmi_read_block(d, addr, buf, size_presence_reg); if (ret) return ret; - ++addr; + addr += size_presence_reg; if (buf[0] == 0) { presense_offset = 3; From 0adb483fbf2dc43c875cd7550a58b41e92efc52d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:32 -0700 Subject: [PATCH 3472/5214] Input: rmi4 - refactor register descriptor parsing Factor out parsing a register descriptor item from rmi_read_register_desc() and ensure there are no out-of-bounds accesses. Use get_unaligned_le16() and get_unaligned_le32() for reading multi-byte values. Reported-by: Greg Kroah-Hartman Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 124 +++++++++++++++++++------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 06f5e3000cf0..75949fb1a922 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "rmi_bus.h" #include "rmi_driver.h" @@ -558,30 +559,74 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, return retval < 0 ? retval : 0; } +static int rmi_parse_register_desc_item(struct rmi_register_desc_item *item, + const u8 *buf, size_t size) +{ + unsigned int offset = 0; + unsigned int map_offset = 0; + int b; + + if (offset >= size) + return -EIO; + + item->reg_size = buf[offset++]; + if (item->reg_size == 0) { + if (size - offset < 2) + return -EIO; + item->reg_size = get_unaligned_le16(&buf[offset]); + offset += 2; + } + + if (item->reg_size == 0) { + if (size - offset < 4) + return -EIO; + item->reg_size = get_unaligned_le32(&buf[offset]); + offset += 4; + } + + do { + if (offset >= size) + return -EIO; + + for (b = 0; b < 7; b++) { + if (buf[offset] & BIT(b)) { + if (map_offset >= RMI_REG_DESC_SUBPACKET_BITS) + return -EIO; + __set_bit(map_offset, item->subpacket_map); + } + ++map_offset; + } + } while (buf[offset++] & BIT(7)); + + item->num_subpackets = bitmap_weight(item->subpacket_map, + RMI_REG_DESC_SUBPACKET_BITS); + + return offset; +} + int rmi_read_register_desc(struct rmi_device *d, u16 addr, - struct rmi_register_descriptor *rdesc) + struct rmi_register_descriptor *rdesc) { int ret; u8 size_presence_reg; u8 buf[35]; - int presense_offset = 1; - u8 *struct_buf; - int reg; - int offset = 0; - int map_offset = 0; + unsigned int presence_offset; + unsigned int map_offset; + unsigned int offset; + unsigned int reg; int i; int b; /* * The first register of the register descriptor is the size of - * the register descriptor's presense register. + * the register descriptor's presence register. */ ret = rmi_read(d, addr, &size_presence_reg); if (ret) return ret; ++addr; - if (size_presence_reg < 0 || size_presence_reg > 35) + if (size_presence_reg < 1 || size_presence_reg > 35) return -EIO; memset(buf, 0, sizeof(buf)); @@ -597,16 +642,23 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, addr += size_presence_reg; if (buf[0] == 0) { - presense_offset = 3; - rdesc->struct_size = buf[1] | (buf[2] << 8); + if (size_presence_reg < 3) + return -EIO; + presence_offset = 3; + rdesc->struct_size = get_unaligned_le16(&buf[1]); } else { + presence_offset = 1; rdesc->struct_size = buf[0]; } - for (i = presense_offset; i < size_presence_reg; i++) { + map_offset = 0; + for (i = presence_offset; i < size_presence_reg; i++) { for (b = 0; b < 8; b++) { - if (buf[i] & (0x1 << b)) + if (buf[i] & BIT(b)) { + if (map_offset >= RMI_REG_DESC_PRESENSE_BITS) + return -EIO; bitmap_set(rdesc->presense_map, map_offset, 1); + } ++map_offset; } } @@ -626,7 +678,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, * I'm not using devm_kzalloc here since it will not be retained * after exiting this function */ - struct_buf = kzalloc(rdesc->struct_size, GFP_KERNEL); + u8 *struct_buf __free(kfree) = kzalloc(rdesc->struct_size, GFP_KERNEL); if (!struct_buf) return -ENOMEM; @@ -638,56 +690,32 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, */ ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size); if (ret) - goto free_struct_buff; + return ret; reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS); + offset = 0; for (i = 0; i < rdesc->num_registers; i++) { struct rmi_register_desc_item *item = &rdesc->registers[i]; - int reg_size = struct_buf[offset]; + int item_size; - ++offset; - if (reg_size == 0) { - reg_size = struct_buf[offset] | - (struct_buf[offset + 1] << 8); - offset += 2; - } - - if (reg_size == 0) { - reg_size = struct_buf[offset] | - (struct_buf[offset + 1] << 8) | - (struct_buf[offset + 2] << 16) | - (struct_buf[offset + 3] << 24); - offset += 4; - } + item_size = rmi_parse_register_desc_item(item, + &struct_buf[offset], + rdesc->struct_size - offset); + if (item_size < 0) + return item_size; item->reg = reg; - item->reg_size = reg_size; - - map_offset = 0; - - do { - for (b = 0; b < 7; b++) { - if (struct_buf[offset] & (0x1 << b)) - bitmap_set(item->subpacket_map, - map_offset, 1); - ++map_offset; - } - } while (struct_buf[offset++] & 0x80); - - item->num_subpackets = bitmap_weight(item->subpacket_map, - RMI_REG_DESC_SUBPACKET_BITS); + offset += item_size; rmi_dbg(RMI_DEBUG_CORE, &d->dev, "%s: reg: %d reg size: %ld subpackets: %d\n", __func__, item->reg, item->reg_size, item->num_subpackets); reg = find_next_bit(rdesc->presense_map, - RMI_REG_DESC_PRESENSE_BITS, reg + 1); + RMI_REG_DESC_PRESENSE_BITS, reg + 1); } -free_struct_buff: - kfree(struct_buf); - return ret; + return 0; } const struct rmi_register_desc_item *rmi_get_register_desc_item( From a0a87e441238e07c5f7e3de133ef77a9d4229f01 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:33 -0700 Subject: [PATCH 3473/5214] Input: rmi4 - fix type overflow in register counts The number of registers in the RMI4 register descriptor is populated by counting the bits in the presence map using bitmap_weight(). Since the presence map can contain up to 256 bits (RMI_REG_DESC_PRESENSE_BITS), storing this count in a u8 can overflow to 0 if all 256 bits are set. Change the num_registers field in struct rmi_register_descriptor from u8 to u16 to prevent potential integer overflow and ensure safe processing of devices reporting large descriptors. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index e84495caab15..5f769fcc758d 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -65,7 +65,7 @@ struct rmi_register_desc_item { struct rmi_register_descriptor { unsigned long struct_size; unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)]; - u8 num_registers; + u16 num_registers; struct rmi_register_desc_item *registers; }; From 2b4b482d5c4c23c668b998a7da985aea0fa4a978 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:34 -0700 Subject: [PATCH 3474/5214] Input: rmi4 - fix num_subpackets overflow in register descriptor RMI_REG_DESC_SUBPACKET_BITS is defined as 296 (37 * BITS_PER_BYTE). This may overflow num_subpackets in struct rmi_register_desc_item which is defined as a u8. Fix this by changing the type of num_subpackets to u16. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-4-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.h | 2 +- drivers/input/rmi4/rmi_f12.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 5f769fcc758d..6952059bf4f5 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -53,7 +53,7 @@ struct pdt_entry { struct rmi_register_desc_item { u16 reg; unsigned long reg_size; - u8 num_subpackets; + u16 num_subpackets; unsigned long subpacket_map[BITS_TO_LONGS( RMI_REG_DESC_SUBPACKET_BITS)]; }; diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 8246fe77114b..c2b07c6905d7 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -467,6 +467,13 @@ static int rmi_f12_probe(struct rmi_function *fn) f12->data1 = item; f12->data1_offset = data_offset; data_offset += item->reg_size; + + if (item->num_subpackets > 255) { + dev_err(&fn->dev, "Too many fingers declared: %d\n", + item->num_subpackets); + return -EINVAL; + } + sensor->nbr_fingers = item->num_subpackets; sensor->report_abs = 1; sensor->attn_size += item->reg_size; From 6e752b35b65228a948fc9e7dea51abb70da8b114 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 10 Jun 2026 18:28:33 -0700 Subject: [PATCH 3475/5214] Input: rmi4 - initialize attn_fifo properly attn_fifo is allocated as part of struct rmi_driver_data using devm_kzalloc in rmi_driver_probe. However, it is never initialized. A zero-initialized kfifo has its mask set to 0, which effectively limits its capacity to 1 element instead of the declared 16. This can lead to lost attention data and memory leaks of the attention data payload if multiple attention events are received before the threaded interrupt handler can process them. Initialize attn_fifo using INIT_KFIFO after allocating rmi_driver_data. Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 75949fb1a922..0a21f6fa3195 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -1161,6 +1161,7 @@ static int rmi_driver_probe(struct device *dev) return -ENOMEM; INIT_LIST_HEAD(&data->function_list); + INIT_KFIFO(data->attn_fifo); data->rmi_dev = rmi_dev; dev_set_drvdata(&rmi_dev->dev, data); From a55a683a8e2bddb5467baab3e597a93022d4ee05 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:35 -0700 Subject: [PATCH 3476/5214] Input: rmi4 - fix memory leak in rmi_set_attn_data() kfifo_put() returns 0 if the FIFO is full. In this case, we must free the memory allocated for the attention data to avoid a leak. Fixes: b908d3cd812a ("Input: synaptics-rmi4 - allow to add attention data") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 0a21f6fa3195..3dc7ab61c269 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -183,7 +183,11 @@ void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, attn_data.size = size; attn_data.data = fifo_data; - kfifo_put(&drvdata->attn_fifo, attn_data); + if (!kfifo_put(&drvdata->attn_fifo, attn_data)) { + dev_warn_ratelimited(&rmi_dev->dev, + "Failed to enqueue attention data, FIFO full\n"); + kfree(fifo_data); + } } EXPORT_SYMBOL_GPL(rmi_set_attn_data); From b6ca982afd0e8fbcbb340092d3c6d3b4a217686c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:36 -0700 Subject: [PATCH 3477/5214] Input: rmi4 - iterative IRQ handler The current IRQ handler uses recursion to drain the attention FIFO, which can lead to stack overflow on deep queues. Convert it to a loop. Fixes: b908d3cd812a ("Input: synaptics-rmi4 - allow to add attention data") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 3dc7ab61c269..5eda11bcb59c 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -198,24 +198,24 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id) struct rmi4_attn_data attn_data = {0}; int ret, count; - count = kfifo_get(&drvdata->attn_fifo, &attn_data); - if (count) { - *(drvdata->irq_status) = attn_data.irq_status; - drvdata->attn_data = attn_data; - } + do { + count = kfifo_get(&drvdata->attn_fifo, &attn_data); + if (count) { + *drvdata->irq_status = attn_data.irq_status; + drvdata->attn_data = attn_data; + } - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", + ret); - if (count) { - kfree(attn_data.data); - drvdata->attn_data.data = NULL; - } - - if (!kfifo_is_empty(&drvdata->attn_fifo)) - return rmi_irq_fn(irq, dev_id); + if (count) { + kfree(attn_data.data); + drvdata->attn_data.data = NULL; + } + } while (!kfifo_is_empty(&drvdata->attn_fifo)); return IRQ_HANDLED; } From f22dbbcbd1f70ed004a7bf8837e0f0c3cc230b78 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:37 -0700 Subject: [PATCH 3478/5214] Input: rmi4 - fix bit count in bitmap_copy() bitmap_copy() takes number of bits, not bytes (or longs). Correct the bit count in rmi_driver_set_irq_bits() and rmi_driver_clear_irq_bits(). Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-7-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 5eda11bcb59c..e77fa460fbd1 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -388,9 +388,8 @@ static int rmi_driver_set_irq_bits(struct rmi_device *rmi_dev, __func__); goto error_unlock; } - bitmap_copy(data->current_irq_mask, data->new_irq_mask, - data->num_of_irq_regs); + bitmap_copy(data->current_irq_mask, data->new_irq_mask, data->irq_count); bitmap_or(data->fn_irq_bits, data->fn_irq_bits, mask, data->irq_count); error_unlock: @@ -419,8 +418,8 @@ static int rmi_driver_clear_irq_bits(struct rmi_device *rmi_dev, __func__); goto error_unlock; } - bitmap_copy(data->current_irq_mask, data->new_irq_mask, - data->num_of_irq_regs); + + bitmap_copy(data->current_irq_mask, data->new_irq_mask, data->irq_count); error_unlock: mutex_unlock(&data->irq_mutex); From 97b16e777ba49a25a1d3883ff7f7d38e61648bb0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:38 -0700 Subject: [PATCH 3479/5214] Input: rmi4 - fix limit in rmi_register_desc_has_subpacket() rmi_register_desc_has_subpacket() should use RMI_REG_DESC_SUBPACKET_BITS, not RMI_REG_DESC_PRESENCE_BITS, as the limit for subpacket_map. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-8-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index e77fa460fbd1..d15d04a66636 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -769,7 +769,7 @@ int rmi_register_desc_calc_reg_offset( bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, u8 subpacket) { - return find_next_bit(item->subpacket_map, RMI_REG_DESC_PRESENSE_BITS, + return find_next_bit(item->subpacket_map, RMI_REG_DESC_SUBPACKET_BITS, subpacket) == subpacket; } From 4e5336f303e2845a82735b5578f7c0e4a175f8c5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:39 -0700 Subject: [PATCH 3480/5214] Input: rmi4 - use local presence map in rmi_read_register_desc() The presence map is only used during the parsing of the register descriptor, so we can make it a local variable instead of storing it in struct rmi_register_descriptor. Also fix the spelling of the constant and the variable name (presence instead of presense). Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-9-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 22 ++++++++++++---------- drivers/input/rmi4/rmi_driver.h | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index d15d04a66636..94b49e4dd0fa 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -610,15 +610,16 @@ static int rmi_parse_register_desc_item(struct rmi_register_desc_item *item, int rmi_read_register_desc(struct rmi_device *d, u16 addr, struct rmi_register_descriptor *rdesc) { - int ret; + DECLARE_BITMAP(presence_map, RMI_REG_DESC_PRESENCE_BITS); + u8 buf[RMI_REG_DESC_PRESENCE_REGS_MAX]; u8 size_presence_reg; - u8 buf[35]; unsigned int presence_offset; unsigned int map_offset; unsigned int offset; unsigned int reg; int i; int b; + int ret; /* * The first register of the register descriptor is the size of @@ -629,7 +630,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, return ret; ++addr; - if (size_presence_reg < 1 || size_presence_reg > 35) + if (size_presence_reg < 1 || size_presence_reg > RMI_REG_DESC_PRESENCE_REGS_MAX) return -EIO; memset(buf, 0, sizeof(buf)); @@ -654,20 +655,21 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, rdesc->struct_size = buf[0]; } + memset(presence_map, 0, sizeof(presence_map)); map_offset = 0; for (i = presence_offset; i < size_presence_reg; i++) { for (b = 0; b < 8; b++) { if (buf[i] & BIT(b)) { - if (map_offset >= RMI_REG_DESC_PRESENSE_BITS) + if (map_offset >= RMI_REG_DESC_PRESENCE_BITS) return -EIO; - bitmap_set(rdesc->presense_map, map_offset, 1); + bitmap_set(presence_map, map_offset, 1); } ++map_offset; } } - rdesc->num_registers = bitmap_weight(rdesc->presense_map, - RMI_REG_DESC_PRESENSE_BITS); + rdesc->num_registers = bitmap_weight(presence_map, + RMI_REG_DESC_PRESENCE_BITS); rdesc->registers = devm_kcalloc(&d->dev, rdesc->num_registers, @@ -695,7 +697,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, if (ret) return ret; - reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS); + reg = find_first_bit(presence_map, RMI_REG_DESC_PRESENCE_BITS); offset = 0; for (i = 0; i < rdesc->num_registers; i++) { struct rmi_register_desc_item *item = &rdesc->registers[i]; @@ -714,8 +716,8 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, "%s: reg: %d reg size: %ld subpackets: %d\n", __func__, item->reg, item->reg_size, item->num_subpackets); - reg = find_next_bit(rdesc->presense_map, - RMI_REG_DESC_PRESENSE_BITS, reg + 1); + reg = find_next_bit(presence_map, + RMI_REG_DESC_PRESENCE_BITS, reg + 1); } return 0; diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 6952059bf4f5..b93905a6a43a 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -46,7 +46,8 @@ struct pdt_entry { u8 function_number; }; -#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE) +#define RMI_REG_DESC_PRESENCE_BITS (32 * BITS_PER_BYTE) +#define RMI_REG_DESC_PRESENCE_REGS_MAX (3 + RMI_REG_DESC_PRESENCE_BITS / 8) #define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE) /* describes a single packet register */ @@ -64,7 +65,6 @@ struct rmi_register_desc_item { */ struct rmi_register_descriptor { unsigned long struct_size; - unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)]; u16 num_registers; struct rmi_register_desc_item *registers; }; From 58d42ec10b7324fa2965d74b9bfbacac3af55c70 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:40 -0700 Subject: [PATCH 3481/5214] Input: rmi4 - refactor function allocation and registration Currently, rmi_create_function() allocates memory for the rmi_function structure, but rmi_register_function() initializes the device via device_initialize(). This split of ownership makes error handling in rmi_create_function() confusing because the caller must be aware that if rmi_register_function() fails, it has already called put_device() to clean up the memory. To make the memory lifecycle explicit and fix potential leaks cleanly introduce rmi_alloc_function() to handle memory allocation and device initialization, and make the caller of rmi_register_function() responsible for cleanup. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-10-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_bus.c | 40 ++++++++++++++++++++------------- drivers/input/rmi4/rmi_bus.h | 1 + drivers/input/rmi4/rmi_driver.c | 10 ++++----- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index ade57e2a7201..c58866df555d 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -237,36 +237,46 @@ static int rmi_function_remove(struct device *dev) return 0; } +struct rmi_function *rmi_alloc_function(struct rmi_device *rmi_dev, u8 id) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *fn; + + fn = kzalloc(sizeof(*fn) + + BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long), + GFP_KERNEL); + if (!fn) + return NULL; + + device_initialize(&fn->dev); + + dev_set_name(&fn->dev, "%s.fn%02x", dev_name(&rmi_dev->dev), id); + + fn->dev.parent = &rmi_dev->dev; + fn->dev.type = &rmi_function_type; + fn->dev.bus = &rmi_bus_type; + fn->rmi_dev = rmi_dev; + + return fn; +} + int rmi_register_function(struct rmi_function *fn) { struct rmi_device *rmi_dev = fn->rmi_dev; int error; - device_initialize(&fn->dev); - - dev_set_name(&fn->dev, "%s.fn%02x", - dev_name(&rmi_dev->dev), fn->fd.function_number); - - fn->dev.parent = &rmi_dev->dev; - fn->dev.type = &rmi_function_type; - fn->dev.bus = &rmi_bus_type; - error = device_add(&fn->dev); if (error) { dev_err(&rmi_dev->dev, - "Failed device_register function device %s\n", + "Failed to register function device %s\n", dev_name(&fn->dev)); - goto err_put_device; + return error; } rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Registered F%02X.\n", fn->fd.function_number); return 0; - -err_put_device: - put_device(&fn->dev); - return error; } void rmi_unregister_function(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index d4d0d82c69aa..90122df21f74 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -49,6 +49,7 @@ struct rmi_function { bool rmi_is_function_device(struct device *dev); +struct rmi_function *rmi_alloc_function(struct rmi_device *rmi_dev, u8 id); int __must_check rmi_register_function(struct rmi_function *); void rmi_unregister_function(struct rmi_function *); diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 94b49e4dd0fa..2ac82cd3ff6d 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -872,9 +872,7 @@ static int rmi_create_function(struct rmi_device *rmi_dev, rmi_dbg(RMI_DEBUG_CORE, dev, "Initializing F%02X.\n", pdt->function_number); - fn = kzalloc(sizeof(struct rmi_function) + - BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long), - GFP_KERNEL); + fn = rmi_alloc_function(rmi_dev, pdt->function_number); if (!fn) { dev_err(dev, "Failed to allocate memory for F%02X\n", pdt->function_number); @@ -884,8 +882,6 @@ static int rmi_create_function(struct rmi_device *rmi_dev, INIT_LIST_HEAD(&fn->node); rmi_driver_copy_pdt_to_fd(pdt, &fn->fd); - fn->rmi_dev = rmi_dev; - fn->num_of_irqs = pdt->interrupt_source_count; fn->irq_pos = *current_irq_count; *current_irq_count += fn->num_of_irqs; @@ -894,8 +890,10 @@ static int rmi_create_function(struct rmi_device *rmi_dev, set_bit(fn->irq_pos + i, fn->irq_mask); error = rmi_register_function(fn); - if (error) + if (error) { + put_device(&fn->dev); return error; + } if (pdt->function_number == 0x01) data->f01_container = fn; From f7e1920f6ad997e98345af246d30f46493691242 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:41 -0700 Subject: [PATCH 3482/5214] Input: rmi4 - use kzalloc_flex() for struct rmi_function struct rmi_function contains a flexible array member irq_mask. Convert the manual kzalloc size calculation to use the kzalloc_flex() macro. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-11-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_bus.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index c58866df555d..560c85f53327 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -242,9 +242,7 @@ struct rmi_function *rmi_alloc_function(struct rmi_device *rmi_dev, u8 id) struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct rmi_function *fn; - fn = kzalloc(sizeof(*fn) + - BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long), - GFP_KERNEL); + fn = kzalloc_flex(*fn, irq_mask, BITS_TO_LONGS(data->irq_count)); if (!fn) return NULL; From 3d353b06d1151badda2fed7e02704f058cfbc812 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:42 -0700 Subject: [PATCH 3483/5214] Input: rmi4 - refactor F12 probe function The F12 probe function contains highly repetitive logic for parsing register descriptors and their individual data items. Refactor the function to use loops to eliminate redundancy, and clarify the code. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-12-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 201 +++++++++++++++-------------------- 1 file changed, 83 insertions(+), 118 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index c2b07c6905d7..973288103b6a 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -61,6 +61,36 @@ struct f12_data { unsigned long *rel_mask; }; +static int rmi_f12_read_register_descs(struct rmi_function *fn, + struct f12_data *f12, u16 query_addr) +{ + struct { + struct rmi_register_descriptor *desc; + const char *name; + } descriptors[] = { + { &f12->query_reg_desc, "Query" }, + { &f12->control_reg_desc, "Control" }, + { &f12->data_reg_desc, "Data" }, + }; + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + int i; + + for (i = 0; i < ARRAY_SIZE(descriptors); i++) { + error = rmi_read_register_desc(rmi_dev, query_addr, + descriptors[i].desc); + if (error) { + dev_err(&fn->dev, + "Failed to read the %s Register Descriptor: %d\n", + descriptors[i].name, error); + return error; + } + query_addr += 3; + } + + return 0; +} + static int rmi_f12_read_sensor_tuning(struct f12_data *f12) { const struct rmi_register_desc_item *item; @@ -351,6 +381,7 @@ static int rmi_f12_probe(struct rmi_function *fn) struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); u16 data_offset = 0; int mask_size; + int i; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); @@ -393,35 +424,9 @@ static int rmi_f12_probe(struct rmi_function *fn) f12->sensor_pdata = pdata->sensor_pdata; } - ret = rmi_read_register_desc(rmi_dev, query_addr, - &f12->query_reg_desc); - if (ret) { - dev_err(&fn->dev, - "Failed to read the Query Register Descriptor: %d\n", - ret); + ret = rmi_f12_read_register_descs(fn, f12, query_addr); + if (ret) return ret; - } - query_addr += 3; - - ret = rmi_read_register_desc(rmi_dev, query_addr, - &f12->control_reg_desc); - if (ret) { - dev_err(&fn->dev, - "Failed to read the Control Register Descriptor: %d\n", - ret); - return ret; - } - query_addr += 3; - - ret = rmi_read_register_desc(rmi_dev, query_addr, - &f12->data_reg_desc); - if (ret) { - dev_err(&fn->dev, - "Failed to read the Data Register Descriptor: %d\n", - ret); - return ret; - } - query_addr += 3; sensor = &f12->sensor; sensor->fn = fn; @@ -452,101 +457,61 @@ static int rmi_f12_probe(struct rmi_function *fn) return ret; /* - * Figure out what data is contained in the data registers. HID devices - * may have registers defined, but their data is not reported in the - * HID attention report. Registers which are not reported in the HID - * attention report check to see if the device is receiving data from - * HID attention reports. + * Identify available data registers and calculate their offsets within + * the attention report. For HID devices, only Data1 and Data5 are + * included in the report; other registers may be described but are + * not transmitted in the attention packet and thus skipped here. */ - item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; + for (i = 0; i < 16; i++) { + item = rmi_get_register_desc_item(&f12->data_reg_desc, i); + if (!item) + continue; - item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); - if (item) { - f12->data1 = item; - f12->data1_offset = data_offset; - data_offset += item->reg_size; + /* HID attention reports only contain Data1 and Data5 */ + if (drvdata->attn_data.data && i != 1 && i != 5) + continue; - if (item->num_subpackets > 255) { - dev_err(&fn->dev, "Too many fingers declared: %d\n", - item->num_subpackets); - return -EINVAL; + switch (i) { + case 1: + f12->data1 = item; + f12->data1_offset = data_offset; + + if (item->num_subpackets > 255) { + dev_err(&fn->dev, + "Too many fingers declared: %d\n", + item->num_subpackets); + return -EINVAL; + } + + sensor->nbr_fingers = item->num_subpackets; + sensor->report_abs = 1; + sensor->attn_size += item->reg_size; + break; + + case 5: + f12->data5 = item; + f12->data5_offset = data_offset; + sensor->attn_size += item->reg_size; + break; + + case 6: + f12->data6 = item; + f12->data6_offset = data_offset; + break; + + case 9: + f12->data9 = item; + f12->data9_offset = data_offset; + if (!sensor->report_abs) + sensor->report_rel = 1; + break; + + case 15: + f12->data15 = item; + f12->data15_offset = data_offset; + break; } - sensor->nbr_fingers = item->num_subpackets; - sensor->report_abs = 1; - sensor->attn_size += item->reg_size; - } - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); - if (item) { - f12->data5 = item; - f12->data5_offset = data_offset; - data_offset += item->reg_size; - sensor->attn_size += item->reg_size; - } - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); - if (item && !drvdata->attn_data.data) { - f12->data6 = item; - f12->data6_offset = data_offset; - data_offset += item->reg_size; - } - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); - if (item && !drvdata->attn_data.data) { - f12->data9 = item; - f12->data9_offset = data_offset; - data_offset += item->reg_size; - if (!sensor->report_abs) - sensor->report_rel = 1; - } - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); - if (item && !drvdata->attn_data.data) - data_offset += item->reg_size; - - item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); - if (item && !drvdata->attn_data.data) { - f12->data15 = item; - f12->data15_offset = data_offset; data_offset += item->reg_size; } From 5fcb6013dd2e2c9b4db3c5a512639371cc04d04e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:43 -0700 Subject: [PATCH 3484/5214] Input: rmi4 - change reg_size type to u32 Change reg_size from unsigned long to u32 to save space and ensure consistent size across 32-bit and 64-bit architectures, and use DECLARE_BITMAP() for subpacket_map. Also pack the structure by rearranging the members to avoid holes, and use size_add() to prevent potential integer overflows when calculating the total size of registers. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-13-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_2d_sensor.h | 4 ++-- drivers/input/rmi4/rmi_driver.c | 4 ++-- drivers/input/rmi4/rmi_driver.h | 5 ++--- drivers/input/rmi4/rmi_f11.c | 2 +- drivers/input/rmi4/rmi_f12.c | 25 +++++++++++++++++++------ 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 61a99c8a7a26..f9d9c1dd5eb0 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -56,8 +56,8 @@ struct rmi_2d_sensor { u16 max_y; u8 nbr_fingers; u8 *data_pkt; - int pkt_size; - int attn_size; + u32 pkt_size; + u32 attn_size; bool topbuttonpad; enum rmi_sensor_type sensor_type; struct input_dev *input; diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 2ac82cd3ff6d..49a59da6a841 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -713,7 +713,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, offset += item_size; rmi_dbg(RMI_DEBUG_CORE, &d->dev, - "%s: reg: %d reg size: %ld subpackets: %d\n", __func__, + "%s: reg: %d reg size: %u subpackets: %d\n", __func__, item->reg, item->reg_size, item->num_subpackets); reg = find_next_bit(presence_map, @@ -746,7 +746,7 @@ size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) for (i = 0; i < rdesc->num_registers; i++) { item = &rdesc->registers[i]; - size += item->reg_size; + size = size_add(size, item->reg_size); } return size; } diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index b93905a6a43a..abeafb77a483 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -52,11 +52,10 @@ struct pdt_entry { /* describes a single packet register */ struct rmi_register_desc_item { + u32 reg_size; u16 reg; - unsigned long reg_size; u16 num_subpackets; - unsigned long subpacket_map[BITS_TO_LONGS( - RMI_REG_DESC_SUBPACKET_BITS)]; + DECLARE_BITMAP(subpacket_map, RMI_REG_DESC_SUBPACKET_BITS); }; /* diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 49ca9168685a..9ade74b36edb 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1304,7 +1304,7 @@ static irqreturn_t rmi_f11_attention(int irq, void *ctx) struct f11_data *f11 = dev_get_drvdata(&fn->dev); u16 data_base_addr = fn->fd.data_base_addr; int error; - int valid_bytes = f11->sensor.pkt_size; + u32 valid_bytes = f11->sensor.pkt_size; if (drvdata->attn_data.data) { /* diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 973288103b6a..b179980003f1 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "rmi_driver.h" #include "rmi_2d_sensor.h" @@ -118,7 +119,7 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) if (item->reg_size > sizeof(buf)) { dev_err(&fn->dev, - "F12 control8 should be no bigger than %zd bytes, not: %ld\n", + "F12 control8 should be no bigger than %zd bytes, not: %u\n", sizeof(buf), item->reg_size); return -ENODEV; } @@ -256,7 +257,7 @@ static irqreturn_t rmi_f12_attention(int irq, void *ctx) struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; - int valid_bytes = sensor->pkt_size; + u32 valid_bytes = sensor->pkt_size; if (drvdata->attn_data.data) { if (sensor->attn_size > drvdata->attn_data.size) @@ -310,7 +311,7 @@ static int rmi_f12_write_control_regs(struct rmi_function *fn) * on the existence of subpacket 0. If control 20 is * larger then 3 bytes, just read the first 3. */ - control_size = min(item->reg_size, 3UL); + control_size = min(item->reg_size, 3U); ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + control_offset, buf, control_size); @@ -379,7 +380,8 @@ static int rmi_f12_probe(struct rmi_function *fn) struct rmi_2d_sensor *sensor; struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); - u16 data_offset = 0; + size_t data_offset = 0; + size_t pkt_size; int mask_size; int i; @@ -431,7 +433,12 @@ static int rmi_f12_probe(struct rmi_function *fn) sensor = &f12->sensor; sensor->fn = fn; f12->data_addr = fn->fd.data_base_addr; - sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc); + pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc); + if (pkt_size > SZ_1M) { + dev_err(&fn->dev, "Invalid data packet size: %zu\n", pkt_size); + return -EINVAL; + } + sensor->pkt_size = pkt_size; sensor->axis_align = f12->sensor_pdata.axis_align; @@ -444,7 +451,7 @@ static int rmi_f12_probe(struct rmi_function *fn) sensor->sensor_type = f12->sensor_pdata.sensor_type; - rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__, + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %u\n", __func__, sensor->pkt_size); sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL); if (!sensor->data_pkt) @@ -471,6 +478,12 @@ static int rmi_f12_probe(struct rmi_function *fn) if (drvdata->attn_data.data && i != 1 && i != 5) continue; + if (data_offset > U16_MAX) { + dev_err(&fn->dev, "Invalid offset for data%d: %zu\n", + i, data_offset); + return -EINVAL; + } + switch (i) { case 1: f12->data1 = item; From f764f0f98c35766d2337cfce118731735339358b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:44 -0700 Subject: [PATCH 3485/5214] Input: rmi4 - use unaligned access helpers in F12 Use get_unaligned_le16() instead of manual bit shifts to construct 16-bit values for max_x, max_y, pitch_x, pitch_y, and object coordinates in the F12 parsing logic. This simplifies the code and makes the endianness explicit. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-14-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index b179980003f1..752c1d137da0 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "rmi_driver.h" #include "rmi_2d_sensor.h" @@ -131,8 +132,8 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) offset = 0; if (rmi_register_desc_has_subpacket(item, 0)) { - sensor->max_x = (buf[offset + 1] << 8) | buf[offset]; - sensor->max_y = (buf[offset + 3] << 8) | buf[offset + 2]; + sensor->max_x = get_unaligned_le16(&buf[offset]); + sensor->max_y = get_unaligned_le16(&buf[offset + 2]); offset += 4; } @@ -140,8 +141,8 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) sensor->max_x, sensor->max_y); if (rmi_register_desc_has_subpacket(item, 1)) { - pitch_x = (buf[offset + 1] << 8) | buf[offset]; - pitch_y = (buf[offset + 3] << 8) | buf[offset + 2]; + pitch_x = get_unaligned_le16(&buf[offset]); + pitch_y = get_unaligned_le16(&buf[offset + 2]); offset += 4; } @@ -227,8 +228,8 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) break; } - obj->x = (data1[2] << 8) | data1[1]; - obj->y = (data1[4] << 8) | data1[3]; + obj->x = get_unaligned_le16(&data1[1]); + obj->y = get_unaligned_le16(&data1[3]); obj->z = data1[5]; obj->wx = data1[6]; obj->wy = data1[7]; From bcaea0fe87d521c044a3efc4ba3b78914072ed03 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:45 -0700 Subject: [PATCH 3486/5214] Input: rmi4 - use flexible array member for IRQ masks in F12 Use a flexible array member to allocate the IRQ masks at the end of the f12_data structure, and use the struct_size() helper to calculate the allocation size safely. This replaces manual pointer arithmetic. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-15-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 752c1d137da0..b9cd70422b68 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -59,8 +59,7 @@ struct f12_data { const struct rmi_register_desc_item *data15; u16 data15_offset; - unsigned long *abs_mask; - unsigned long *rel_mask; + unsigned long irq_mask[]; }; static int rmi_f12_read_register_descs(struct rmi_function *fn, @@ -350,17 +349,21 @@ static int rmi_f12_config(struct rmi_function *fn) { struct rmi_driver *drv = fn->rmi_dev->driver; struct f12_data *f12 = dev_get_drvdata(&fn->dev); + struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); + int irq_mask_size = BITS_TO_LONGS(drvdata->irq_count); + unsigned long *abs_mask = f12->irq_mask; + unsigned long *rel_mask = f12->irq_mask + irq_mask_size; struct rmi_2d_sensor *sensor; int ret; sensor = &f12->sensor; if (!sensor->report_abs) - drv->clear_irq_bits(fn->rmi_dev, f12->abs_mask); + drv->clear_irq_bits(fn->rmi_dev, abs_mask); else - drv->set_irq_bits(fn->rmi_dev, f12->abs_mask); + drv->set_irq_bits(fn->rmi_dev, abs_mask); - drv->clear_irq_bits(fn->rmi_dev, f12->rel_mask); + drv->clear_irq_bits(fn->rmi_dev, rel_mask); ret = rmi_f12_write_control_regs(fn); if (ret) @@ -383,12 +386,12 @@ static int rmi_f12_probe(struct rmi_function *fn) struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); size_t data_offset = 0; size_t pkt_size; - int mask_size; + int irq_mask_size; int i; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); - mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long); + irq_mask_size = BITS_TO_LONGS(drvdata->irq_count); ret = rmi_read(fn->rmi_dev, query_addr, &buf); if (ret < 0) { @@ -404,18 +407,13 @@ static int rmi_f12_probe(struct rmi_function *fn) return -ENODEV; } - f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data) + mask_size * 2, - GFP_KERNEL); + f12 = devm_kzalloc(&fn->dev, struct_size(f12, irq_mask, irq_mask_size * 2), + GFP_KERNEL); if (!f12) return -ENOMEM; - f12->abs_mask = (unsigned long *)((char *)f12 - + sizeof(struct f12_data)); - f12->rel_mask = (unsigned long *)((char *)f12 - + sizeof(struct f12_data) + mask_size); - - set_bit(fn->irq_pos, f12->abs_mask); - set_bit(fn->irq_pos + 1, f12->rel_mask); + set_bit(fn->irq_pos, f12->irq_mask); + set_bit(fn->irq_pos + 1, f12->irq_mask + irq_mask_size); f12->has_dribble = !!(buf & BIT(3)); From 1ea51794d7e67f54ffe003bb04dd7e43107644d1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:46 -0700 Subject: [PATCH 3487/5214] Input: rmi4 - use devm_kmalloc for F12 data packet buffer The sensor->data_pkt buffer is used exclusively to store incoming hardware data during the attention handler, where it is entirely overwritten by either memcpy() or rmi_read_block(). Therefore, there is no need to zero-initialize it during probe. Switch to devm_kmalloc() to avoid the unnecessary memset overhead. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-16-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index b9cd70422b68..01838907c64b 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -452,7 +452,7 @@ static int rmi_f12_probe(struct rmi_function *fn) rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %u\n", __func__, sensor->pkt_size); - sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL); + sensor->data_pkt = devm_kmalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL); if (!sensor->data_pkt) return -ENOMEM; From 57d9212421e3a10fc336bed732e70b8521fea1e6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:47 -0700 Subject: [PATCH 3488/5214] Input: rmi4 - use sizeof(*ptr) and idiomatic checks in f12 allocators Using sizeof(*ptr) is preferred over sizeof(struct) because it is more robust against type changes. Also switch to checking for allocation failure immediately after each call, and update formatting. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-17-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 01838907c64b..34ed02b7f30f 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -528,16 +528,21 @@ static int rmi_f12_probe(struct rmi_function *fn) } /* allocate the in-kernel tracking buffers */ - sensor->tracking_pos = devm_kcalloc(&fn->dev, - sensor->nbr_fingers, sizeof(struct input_mt_pos), - GFP_KERNEL); - sensor->tracking_slots = devm_kcalloc(&fn->dev, - sensor->nbr_fingers, sizeof(int), GFP_KERNEL); - sensor->objs = devm_kcalloc(&fn->dev, - sensor->nbr_fingers, - sizeof(struct rmi_2d_sensor_abs_object), - GFP_KERNEL); - if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) + sensor->tracking_pos = devm_kcalloc(&fn->dev, sensor->nbr_fingers, + sizeof(*sensor->tracking_pos), + GFP_KERNEL); + if (!sensor->tracking_pos) + return -ENOMEM; + + sensor->tracking_slots = devm_kcalloc(&fn->dev, sensor->nbr_fingers, + sizeof(*sensor->tracking_slots), + GFP_KERNEL); + if (!sensor->tracking_slots) + return -ENOMEM; + + sensor->objs = devm_kcalloc(&fn->dev, sensor->nbr_fingers, + sizeof(*sensor->objs), GFP_KERNEL); + if (!sensor->objs) return -ENOMEM; ret = rmi_2d_sensor_configure_input(fn, sensor); From 7d8be1ecf44f032f3fb444088b43b933ead9b00d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:48 -0700 Subject: [PATCH 3489/5214] Input: rmi4 - simplify size calculations in F12 Use min_t() to simplify the clamping logic when calculating the number of objects to process and the number of valid bytes in the attention handler. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-18-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 34ed02b7f30f..ea72d5ce8050 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -195,14 +195,11 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) return 0; } -static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) +static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, u32 size) { - int i; struct rmi_2d_sensor *sensor = &f12->sensor; - int objects = f12->data1->num_subpackets; - - if ((f12->data1->num_subpackets * F12_DATA1_BYTES_PER_OBJ) > size) - objects = size / F12_DATA1_BYTES_PER_OBJ; + u32 objects = min(f12->data1->num_subpackets, size / F12_DATA1_BYTES_PER_OBJ); + int i; for (i = 0; i < objects; i++) { struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i]; @@ -260,10 +257,7 @@ static irqreturn_t rmi_f12_attention(int irq, void *ctx) u32 valid_bytes = sensor->pkt_size; if (drvdata->attn_data.data) { - if (sensor->attn_size > drvdata->attn_data.size) - valid_bytes = drvdata->attn_data.size; - else - valid_bytes = sensor->attn_size; + valid_bytes = min_t(u32, sensor->attn_size, drvdata->attn_data.size); memcpy(sensor->data_pkt, drvdata->attn_data.data, valid_bytes); drvdata->attn_data.data += valid_bytes; From 0655e58946e480cb99fb276e87280d0715b32ada Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:49 -0700 Subject: [PATCH 3490/5214] Input: rmi4 - propagate proper error code in F12 sensor tuning Propagate the actual error code returned by rmi_read() in rmi_f12_read_sensor_tuning() instead of hardcoding -ENODEV. Also, since rmi_read() returns 0 on success, use 'if (ret)' instead of 'if (ret < 0)'. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260505045952.1570713-19-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index ea72d5ce8050..bf8c4a0e10de 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -166,9 +166,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) RMI_F12_QUERY_RESOLUTION); query_dpm_addr = fn->fd.query_base_addr + offset; ret = rmi_read(fn->rmi_dev, query_dpm_addr, buf); - if (ret < 0) { + if (ret) { dev_err(&fn->dev, "Failed to read DPM value: %d\n", ret); - return -ENODEV; + return ret; } dpm_resolution = buf[0]; From 040b099a58aad06e436cd3a46475fa983efd6c93 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 21:59:50 -0700 Subject: [PATCH 3491/5214] Input: rmi4 - update formatting in F12 Clean up various style and formatting issues in the F12 code. Link: https://patch.msgid.link/20260505045952.1570713-20-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f12.c | 114 +++++++++++++++++------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index bf8c4a0e10de..88c28089de99 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -51,7 +51,6 @@ struct f12_data { const struct rmi_register_desc_item *data6; u16 data6_offset; - /* F12 Data9 reports relative data */ const struct rmi_register_desc_item *data9; u16 data9_offset; @@ -124,8 +123,8 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) return -ENODEV; } - ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, buf, - item->reg_size); + ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, + buf, item->reg_size); if (ret) return ret; @@ -163,7 +162,7 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) if (rmi_get_register_desc_item(&f12->query_reg_desc, RMI_F12_QUERY_RESOLUTION)) { offset = rmi_register_desc_calc_reg_offset(&f12->query_reg_desc, - RMI_F12_QUERY_RESOLUTION); + RMI_F12_QUERY_RESOLUTION); query_dpm_addr = fn->fd.query_base_addr + offset; ret = rmi_read(fn->rmi_dev, query_dpm_addr, buf); if (ret) { @@ -248,18 +247,17 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, u32 size) static irqreturn_t rmi_f12_attention(int irq, void *ctx) { - int retval; struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; u32 valid_bytes = sensor->pkt_size; + int retval; if (drvdata->attn_data.data) { valid_bytes = min_t(u32, sensor->attn_size, drvdata->attn_data.size); - memcpy(sensor->data_pkt, drvdata->attn_data.data, - valid_bytes); + memcpy(sensor->data_pkt, drvdata->attn_data.data, valid_bytes); drvdata->attn_data.data += valid_bytes; drvdata->attn_data.size -= valid_bytes; } else { @@ -273,70 +271,74 @@ static irqreturn_t rmi_f12_attention(int irq, void *ctx) } if (f12->data1) - rmi_f12_process_objects(f12, - &sensor->data_pkt[f12->data1_offset], valid_bytes); + rmi_f12_process_objects(f12, &sensor->data_pkt[f12->data1_offset], + valid_bytes); input_mt_sync_frame(sensor->input); return IRQ_HANDLED; } -static int rmi_f12_write_control_regs(struct rmi_function *fn) +static int rmi_f12_update_dribble(struct rmi_function *fn, struct f12_data *f12) { - int ret; const struct rmi_register_desc_item *item; struct rmi_device *rmi_dev = fn->rmi_dev; - struct f12_data *f12 = dev_get_drvdata(&fn->dev); - int control_size; - char buf[3]; - u16 control_offset = 0; u8 subpacket_offset = 0; + u16 control_offset; + u32 control_size; + int error; + u8 buf[3]; - if (f12->has_dribble - && (f12->sensor.dribble != RMI_REG_STATE_DEFAULT)) { - item = rmi_get_register_desc_item(&f12->control_reg_desc, 20); - if (item) { - control_offset = rmi_register_desc_calc_reg_offset( - &f12->control_reg_desc, 20); + item = rmi_get_register_desc_item(&f12->control_reg_desc, 20); + if (!item) + return 0; - /* - * The byte containing the EnableDribble bit will be - * in either byte 0 or byte 2 of control 20. Depending - * on the existence of subpacket 0. If control 20 is - * larger then 3 bytes, just read the first 3. - */ - control_size = min(item->reg_size, 3U); + control_offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 20); - ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr - + control_offset, buf, control_size); - if (ret) - return ret; + /* + * The byte containing the EnableDribble bit will be + * in either byte 0 or byte 2 of control 20. Depending + * on the existence of subpacket 0. If control 20 is + * larger then 3 bytes, just read the first 3. + */ + control_size = min(item->reg_size, 3U); - if (rmi_register_desc_has_subpacket(item, 0)) - subpacket_offset += 1; + error = rmi_read_block(rmi_dev, fn->fd.control_base_addr + control_offset, + buf, control_size); + if (error) + return error; - switch (f12->sensor.dribble) { - case RMI_REG_STATE_OFF: - buf[subpacket_offset] &= ~BIT(2); - break; - case RMI_REG_STATE_ON: - buf[subpacket_offset] |= BIT(2); - break; - case RMI_REG_STATE_DEFAULT: - default: - break; - } + if (rmi_register_desc_has_subpacket(item, 0)) + subpacket_offset += 1; - ret = rmi_write_block(rmi_dev, - fn->fd.control_base_addr + control_offset, - buf, control_size); - if (ret) - return ret; - } + switch (f12->sensor.dribble) { + case RMI_REG_STATE_OFF: + buf[subpacket_offset] &= ~BIT(2); + break; + case RMI_REG_STATE_ON: + buf[subpacket_offset] |= BIT(2); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; } - return 0; + error = rmi_write_block(rmi_dev, fn->fd.control_base_addr + control_offset, + buf, control_size); + if (error) + return error; + return 0; +} + +static int rmi_f12_write_control_regs(struct rmi_function *fn) +{ + struct f12_data *f12 = dev_get_drvdata(&fn->dev); + + if (f12->has_dribble && f12->sensor.dribble != RMI_REG_STATE_DEFAULT) + return rmi_f12_update_dribble(fn, f12); + + return 0; } static int rmi_f12_config(struct rmi_function *fn) @@ -362,7 +364,7 @@ static int rmi_f12_config(struct rmi_function *fn) ret = rmi_f12_write_control_regs(fn); if (ret) dev_warn(&fn->dev, - "Failed to write F12 control registers: %d\n", ret); + "Failed to write F12 control registers: %d\n", ret); return 0; } @@ -433,16 +435,14 @@ static int rmi_f12_probe(struct rmi_function *fn) } sensor->pkt_size = pkt_size; - sensor->axis_align = - f12->sensor_pdata.axis_align; + sensor->axis_align = f12->sensor_pdata.axis_align; sensor->x_mm = f12->sensor_pdata.x_mm; sensor->y_mm = f12->sensor_pdata.y_mm; sensor->dribble = f12->sensor_pdata.dribble; if (sensor->sensor_type == rmi_sensor_default) - sensor->sensor_type = - f12->sensor_pdata.sensor_type; + sensor->sensor_type = f12->sensor_pdata.sensor_type; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %u\n", __func__, sensor->pkt_size); From 355fbcbdc2539cca7890b0d0914d4ce0f985ad74 Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 7 Jun 2026 16:47:34 +0000 Subject: [PATCH 3492/5214] xfrm: use compat translator only for u64 alignment mismatch The XFRM compat layer (CONFIG_XFRM_USER_COMPAT) translates 32-bit xfrm netlink and setsockopt messages into the native 64-bit layout. It is only needed on architectures where the 32-bit and 64-bit ABIs disagree on u64 alignment, which the kernel encodes as COMPAT_FOR_U64_ALIGNMENT. That symbol is defined only by arch/x86. XFRM_USER_COMPAT depends on it, so the translator can never be built on any other architecture, including arm64, which still provides a 32-bit compat ABI (CONFIG_COMPAT) for AArch32 EL0 userspace. On arm64 the AArch32 EABI already aligns u64 to 8 bytes, identical to the AArch64 ABI, so no translation is required and the native code path is correct for 32-bit tasks. However, xfrm_user_rcv_msg() and xfrm_user_policy() gate on in_compat_syscall() alone and then call xfrm_get_translator(), which returns NULL when no translator is registered. On arm64 that is always the case, so every xfrm netlink message and the XFRM_POLICY setsockopt issued by a 32-bit task returns -EOPNOTSUPP. A 32-bit userspace process on arm64 (and on any other arch with CONFIG_COMPAT but without COMPAT_FOR_U64_ALIGNMENT) therefore cannot configure XFRM state or policy through the XFRM_USER netlink API, and cannot use the XFRM_POLICY setsockopt path, because both fail before reaching the native parser. The translator series replaced the blanket compat rejection with a translator lookup. That made the path usable on x86 when the translator is available, but left architectures that cannot build the translator permanently rejected even when their compat layout already matches the native layout. Let those architectures use the native parser instead. Gate the translator requirement on COMPAT_FOR_U64_ALIGNMENT instead of on in_compat_syscall() alone. Gating on the ABI property rather than on CONFIG_XFRM_USER_COMPAT is deliberate: on x86 with IA32_EMULATION=y but XFRM_USER_COMPAT=n, a 32-bit task must still be rejected rather than routed through the native parser, which would misread genuinely 4-byte-aligned x86-32 messages. COMPAT_FOR_U64_ALIGNMENT is the ABI property that makes the XFRM translator mandatory. Only the receive/input direction needs the guard. The send, dump and notification paths already call the translator as "if (xtr) { ... }" with no error on NULL, so on arches without a translator they no-op and the kernel emits native 64-bit-layout messages, which is what an AArch32 task expects. Tested on Juniper SRX hardware: with the fix, 32-bit IPsec userspace netlink and XFRM_POLICY setsockopt operations that previously failed with -EOPNOTSUPP now succeed; x86 behaviour is unchanged by inspection. Fixes: 5106f4a8acff ("xfrm/compat: Add 32=>64-bit messages translator") Fixes: 96392ee5a13b ("xfrm/compat: Translate 32-bit user_policy from sockptr") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 589c3b6e4679..d8457ceaf28c 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2976,7 +2976,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) if (IS_ERR(data)) return PTR_ERR(data); - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) { diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 71a4b7278eba..3b1cf29bc402 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3472,7 +3472,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (!netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) From d129c3177d7b1138fd5066fcc63a698b3ba415b0 Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Mon, 8 Jun 2026 07:44:41 -0700 Subject: [PATCH 3493/5214] net: af_key: initialize alg_key_len for IPComp states pfkey_msg2xfrm_state() handles the IPComp (SADB_X_SATYPE_IPCOMP) case by allocating x->calg and copying only the algorithm name: x->calg = kmalloc_obj(*x->calg); if (!x->calg) { err = -ENOMEM; goto out; } strcpy(x->calg->alg_name, a->name); x->props.calgo = sa->sadb_sa_encrypt; Unlike the authentication (x->aalg) and encryption (x->ealg) branches of the same function, the compression branch never initializes calg->alg_key_len. IPComp carries no key and the allocation only reserves sizeof(struct xfrm_algo) (i.e. no room for a key), so the field is left containing uninitialized slab data. calg->alg_key_len is later used as a length by xfrm_algo_clone() when an IPComp state is cloned during XFRM_MSG_MIGRATE: xfrm_state_migrate() xfrm_state_clone_and_setup() x->calg = xfrm_algo_clone(orig->calg); kmemdup(orig, xfrm_alg_len(orig)); where xfrm_alg_len() returns sizeof(*alg) + (alg_key_len + 7) / 8. With a non-zero garbage alg_key_len, kmemdup() reads past the end of the 68-byte calg object. Adding an IPComp SA via PF_KEY and then migrating it triggers (net-next, KASAN, init_on_alloc=0): BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x44/0x60 Read of size 4164 at addr ff11000025a74980 by task diag2/9287 CPU: 3 UID: 0 PID: 9287 Comm: diag2 7.1.0-rc6-g903db046d557 #1 Call Trace: dump_stack_lvl+0x10e/0x1f0 print_report+0xf7/0x600 kasan_report+0xe4/0x120 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x44/0x60 xfrm_state_migrate+0x70a/0x1da0 xfrm_migrate+0x753/0x18a0 xfrm_do_migrate+0xb47/0xf10 xfrm_user_rcv_msg+0x411/0xb50 netlink_rcv_skb+0x158/0x420 xfrm_netlink_rcv+0x71/0x90 netlink_unicast+0x584/0x850 netlink_sendmsg+0x8b0/0xdc0 ____sys_sendmsg+0x9f7/0xb90 ___sys_sendmsg+0x134/0x1d0 __sys_sendmsg+0x16d/0x220 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 9287: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 pfkey_add+0x2652/0x2ea0 pfkey_process+0x6d0/0x830 pfkey_sendmsg+0x42c/0x850 __sys_sendto+0x461/0x4b0 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ff11000025a74980 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes inside of allocated 68-byte region [ff11000025a74980, ff11000025a749c4) Depending on the uninitialized value the same field can instead request an oversized kmemdup() allocation and make the migration clone fail. The XFRM netlink path is not affected: verify_one_alg() rejects an XFRMA_ALG_COMP attribute shorter than xfrm_alg_len(), so a calg added via XFRM_MSG_NEWSA is always self-consistent. Initialize calg->alg_key_len to 0, matching the aalg/ealg branches. Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)") Cc: stable@vger.kernel.org Signed-off-by: Zijing Yin Reviewed-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/key/af_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/key/af_key.c b/net/key/af_key.c index 9cffeef18cd9..3216f897a305 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1218,6 +1218,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, goto out; } strcpy(x->calg->alg_name, a->name); + x->calg->alg_key_len = 0; x->props.calgo = sa->sadb_sa_encrypt; } else { int keysize = 0; From 8045c0df98d4f14c54e5cb875f1c9c0ce89fe4ff Mon Sep 17 00:00:00 2001 From: Dong Chenchen Date: Tue, 9 Jun 2026 17:21:17 +0800 Subject: [PATCH 3494/5214] xfrm: Fix dev use-after-free in xfrm async resumption xfrm async resumption hold skb->dev refcnt until after transport_finish. However, xfrm_rcv_cb may modify skb->dev to tunnel dev without taking device reference, such as vti_rcv_cb. The subsequent async resumption will decrement the tunnel device's reference count, which lead to uaf of tunnel dev and refcnt leak of orig dev as below: unregister_netdevice: waiting for vti1 to become free. Usage count = -2 Stash the original skb->dev to fix refcnt imbalance. The new skb->dev set by xfrm_rcv_cb can race with device teardown. Extend rcu protection over xfrm_rcv_cb and transport_finish to prevent races. Fixes: 1c428b038400 ("xfrm: hold dev ref until after transport_finish NF_HOOK") Reported-by: Xu Chunxiao Signed-off-by: Dong Chenchen Signed-off-by: Steffen Klassert --- net/ipv4/xfrm4_input.c | 2 -- net/ipv6/xfrm6_input.c | 2 -- net/xfrm/xfrm_input.c | 29 ++++++++++++++++------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index c2eac844bcdb..f6f2a8ef3f88 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -76,8 +76,6 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async) NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, dev_net(dev), NULL, skb, dev, NULL, xfrm4_rcv_encap_finish); - if (async) - dev_put(dev); return 0; } diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 699a001ac166..89d0443b5307 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -71,8 +71,6 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, dev_net(dev), NULL, skb, dev, NULL, xfrm6_transport_finish2); - if (async) - dev_put(dev); return 0; } diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index e4c2cd24936d..eecab337bd0a 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -467,6 +467,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) { const struct xfrm_state_afinfo *afinfo; struct net *net = dev_net(skb->dev); + struct net_device *dev = skb->dev; int err; __be32 seq; __be32 seq_hi; @@ -493,7 +494,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) LINUX_MIB_XFRMINSTATEINVALID); if (encap_type == -1) - dev_put(skb->dev); + dev_put(dev); goto drop; } @@ -655,16 +656,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (!crypto_done) { spin_unlock(&x->lock); - dev_hold(skb->dev); + dev_hold(dev); nexthdr = x->type->input(x, skb); if (nexthdr == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } - dev_put(skb->dev); + dev_put(dev); spin_lock(&x->lock); } resume: @@ -699,7 +700,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) err = xfrm_inner_mode_input(x, skb); if (err == -EINPROGRESS) { if (async) - dev_put(skb->dev); + dev_put(dev); return 0; } else if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); @@ -726,9 +727,12 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) crypto_done = false; } while (!err); + rcu_read_lock(); err = xfrm_rcv_cb(skb, family, x->type->proto, 0); - if (err) + if (err) { + rcu_read_unlock(); goto drop; + } nf_reset_ct(skb); @@ -739,8 +743,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (skb_valid_dst(skb)) skb_dst_drop(skb); if (async) - dev_put(skb->dev); + dev_put(dev); gro_cells_receive(&gro_cells, skb); + rcu_read_unlock(); return 0; } else { xo = xfrm_offload(skb); @@ -748,23 +753,21 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) xfrm_gro = xo->flags & XFRM_GRO; err = -EAFNOSUPPORT; - rcu_read_lock(); afinfo = xfrm_state_afinfo_get_rcu(x->props.family); if (likely(afinfo)) err = afinfo->transport_finish(skb, xfrm_gro || async); - rcu_read_unlock(); if (xfrm_gro) { sp = skb_sec_path(skb); if (sp) sp->olen = 0; if (skb_valid_dst(skb)) skb_dst_drop(skb); - if (async) - dev_put(skb->dev); gro_cells_receive(&gro_cells, skb); - return err; } + if (async) + dev_put(dev); + rcu_read_unlock(); return err; } @@ -772,7 +775,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) spin_unlock(&x->lock); drop: if (async) - dev_put(skb->dev); + dev_put(dev); xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; From 650c4704b9e9ca7c97b29fdaac0f140aa0e8157f Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Thu, 4 Jun 2026 15:51:47 +0800 Subject: [PATCH 3495/5214] KVM: arm64: vgic-its: Make ABI commit helpers return void The return values of vgic_its_set_abi() and vgic_its_commit_v0() are always 0 and do not carry useful error information. Simplify by changing them to void. Suggested-by: Oliver Upton Signed-off-by: Jackie Liu Reviewed-by: Oliver Upton Reviewed-by: Eric Auger Link: https://patch.msgid.link/20260604075147.53299-1-liu.yun@linux.dev Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-its.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 2ea9f1c7ebcd..67d107e9a77d 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -27,7 +27,7 @@ static struct kvm_device_ops kvm_arm_vgic_its_ops; static int vgic_its_save_tables_v0(struct vgic_its *its); static int vgic_its_restore_tables_v0(struct vgic_its *its); -static int vgic_its_commit_v0(struct vgic_its *its); +static void vgic_its_commit_v0(struct vgic_its *its); static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq, struct kvm_vcpu *filter_vcpu, bool needs_inv); @@ -168,7 +168,7 @@ struct vgic_its_abi { int ite_esz; int (*save_tables)(struct vgic_its *its); int (*restore_tables)(struct vgic_its *its); - int (*commit)(struct vgic_its *its); + void (*commit)(struct vgic_its *its); }; #define ABI_0_ESZ 8 @@ -192,13 +192,13 @@ inline const struct vgic_its_abi *vgic_its_get_abi(struct vgic_its *its) return &its_table_abi_versions[its->abi_rev]; } -static int vgic_its_set_abi(struct vgic_its *its, u32 rev) +static void vgic_its_set_abi(struct vgic_its *its, u32 rev) { const struct vgic_its_abi *abi; its->abi_rev = rev; abi = vgic_its_get_abi(its); - return abi->commit(its); + abi->commit(its); } /* @@ -472,7 +472,8 @@ static int vgic_mmio_uaccess_write_its_iidr(struct kvm *kvm, if (rev >= NR_ITS_ABIS) return -EINVAL; - return vgic_its_set_abi(its, rev); + vgic_its_set_abi(its, rev); + return 0; } static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm, @@ -1888,14 +1889,11 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) its->baser_coll_table = INITIAL_BASER_VALUE | ((u64)GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT); dev->kvm->arch.vgic.propbaser = INITIAL_PROPBASER_VALUE; - dev->private = its; - ret = vgic_its_set_abi(its, NR_ITS_ABIS - 1); - + vgic_its_set_abi(its, NR_ITS_ABIS - 1); mutex_unlock(&dev->kvm->arch.config_lock); - - return ret; + return 0; } static void vgic_its_destroy(struct kvm_device *kvm_dev) @@ -2606,7 +2604,7 @@ static int vgic_its_restore_tables_v0(struct vgic_its *its) return ret; } -static int vgic_its_commit_v0(struct vgic_its *its) +static void vgic_its_commit_v0(struct vgic_its *its) { const struct vgic_its_abi *abi; @@ -2619,7 +2617,6 @@ static int vgic_its_commit_v0(struct vgic_its *its) its->baser_device_table |= (GIC_ENCODE_SZ(abi->dte_esz, 5) << GITS_BASER_ENTRY_SIZE_SHIFT); - return 0; } static void vgic_its_reset(struct kvm *kvm, struct vgic_its *its) From 8503953e266c78e4bbe281ea3a0a06b1a37b7836 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:55:41 -0400 Subject: [PATCH 3496/5214] KVM: x86: remove nested_mmu from mmu_is_nested() nested_mmu is always stored into vcpu->arch.walk_mmu at the same time as guest_mmu is stored into vcpu->arch.mmu. But nested_mmu is not even a proper MMU, it is only used for page walking; plus the fact that walk_mmu has to be switched at all is just an implementation detail. In the end what matters here is whether the guest is using nested page tables; vmx/nested.c and svm/nested.c check it to see if they are in nEPT or nNPT context respectively. So switch to checking root_mmu vs. guest_mmu, which is a more cogent test. Signed-off-by: Paolo Bonzini Message-ID: <20260511150648.685374-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini Message-ID: <20260530165545.25599-2-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/x86.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 38a905fa86de..60ff064de12f 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -290,7 +290,7 @@ static inline bool x86_exception_has_error_code(unsigned int vector) static inline bool mmu_is_nested(struct kvm_vcpu *vcpu) { - return vcpu->arch.walk_mmu == &vcpu->arch.nested_mmu; + return vcpu->arch.mmu == &vcpu->arch.guest_mmu; } static inline bool is_pae(struct kvm_vcpu *vcpu) From 9eff3e99a81c20148b934d5fe882bcfe7c6078ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:55:42 -0400 Subject: [PATCH 3497/5214] KVM: nVMX: remove unnecessary code in prepare_vmcs02_rare The early vmwrite of the PDPTRs in prepare_vmcs02_rare() is redundant, because every write it does will be performed by prepare_vmcs02() if it is actually needed. In any case where the emulator or the processor need the PDPTR, either is_pae_paging() is true on vmentry, or a write of CR0, CR4 or EFER will cause a vmexit to L0. The next vmentry will refresh the PDPTRs in the vmcs02 from vmcs12. In fact, the original version[1] of what ended up being commit c7554efc8335 ("KVM: nVMX: Copy PDPTRs to/from vmcs12 only when necessary"), the writes in what is now prepare_vmcs02_rare() were removed. When the mega-collection of optimizations was posted[2], the removal of that code got dropped as a rebase good, so reinstate it. [1] https://lore.kernel.org/all/20190507160640.4812-16-sean.j.christopherson@intel.com [2] https://lore.kernel.org/all/1560445409-17363-31-git-send-email-pbonzini@redhat.com Suggested-by: Sean Christopherson Signed-off-by: Paolo Bonzini Message-ID: <20260530165545.25599-3-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 4690a4d23709..1bd0839146fd 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2623,17 +2623,6 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) vmcs_writel(GUEST_SYSENTER_ESP, vmcs12->guest_sysenter_esp); vmcs_writel(GUEST_SYSENTER_EIP, vmcs12->guest_sysenter_eip); - /* - * L1 may access the L2's PDPTR, so save them to construct - * vmcs12 - */ - if (enable_ept) { - vmcs_write64(GUEST_PDPTR0, vmcs12->guest_pdptr0); - vmcs_write64(GUEST_PDPTR1, vmcs12->guest_pdptr1); - vmcs_write64(GUEST_PDPTR2, vmcs12->guest_pdptr2); - vmcs_write64(GUEST_PDPTR3, vmcs12->guest_pdptr3); - } - if (kvm_mpx_supported() && vmx->vcpu.arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS)) vmcs_write64(GUEST_BNDCFGS, vmcs12->guest_bndcfgs); From 62bad2b2ccf3ebfcf1055f0cd76673ea95f724bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:55:43 -0400 Subject: [PATCH 3498/5214] KVM: nSVM: invalidate cached PDPTRs across nested NPT transitions When L2 runs under nested NPT and uses PAE paging, KVM's cached PDPTRs in mmu->pdptrs[] can hold stale or wrong values after nested transitions and across migration restore, because both nested_svm_load_cr3() and svm_get_nested_state_pages() only refresh PDPTRs on the !nested_npt path. The user-visible bug is on migration restore of an L2 running with nested NPT and 32-bit PAE paging, if userspace uses KVM_SET_SREGS rather than KVM_SET_SREGS2. In that case, load_pdptrs() leaves VCPU_EXREG_PDPTR marked as available, and kvm_pdptr_read() will use a stale translation that used L1 GPAs instead of L2 nGPAs. svm_get_nested_state_pages() runs on first KVM_RUN but skips the refresh because nested_npt_enabled() is true. The CPU itself reads L2's PDPTRs correctly from memory via L1's NPT, but KVM-side walking of guest PAE page tables uses the bogus cached values. Unlike Intel's GUEST_PDPTR0..3 fields in the VMCS, SVM has no VMCB-cached PDPTR state: the in-memory PDPTEs at the current CR3 are the only source of truth, and svm_cache_reg(VCPU_EXREG_PDPTR) simply reloads them from memory via load_pdptrs(). Clearing the avail bit (and the dirty bit because !avail/dirty is invalid) to force a reload when PDPTRs as needed fixes the bug. Do the same for nested_svm_load_cr3()'s nested_npt branch, so that the invariant "PDPTRs need reloading" is handled similarly for both immediate and deferred loading. Signed-off-by: Paolo Bonzini Message-ID: <20260530165545.25599-4-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/kvm_cache_regs.h | 8 ++++++++ arch/x86/kvm/svm/nested.c | 27 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 2ae492ad6412..6bae5db5a54e 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -77,6 +77,14 @@ static inline bool kvm_register_is_dirty(struct kvm_vcpu *vcpu, return test_bit(reg, vcpu->arch.regs_dirty); } +static inline void kvm_register_mark_for_reload(struct kvm_vcpu *vcpu, + enum kvm_reg reg) +{ + kvm_assert_register_caching_allowed(vcpu); + __clear_bit(reg, vcpu->arch.regs_avail); + __clear_bit(reg, vcpu->arch.regs_dirty); +} + static inline void kvm_register_mark_available(struct kvm_vcpu *vcpu, enum kvm_reg reg) { diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 1bf3e4804ad0..e6aa54d43730 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -690,9 +690,12 @@ static int nested_svm_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, if (CC(!kvm_vcpu_is_legal_cr3(vcpu, cr3))) return -EINVAL; - if (reload_pdptrs && !nested_npt && is_pae_paging(vcpu) && - CC(!load_pdptrs(vcpu, cr3))) - return -EINVAL; + if (reload_pdptrs && is_pae_paging(vcpu)) { + if (nested_npt) + kvm_register_mark_for_reload(vcpu, VCPU_REG_PDPTR); + else if (CC(!load_pdptrs(vcpu, cr3))) + return -EINVAL; + } vcpu->arch.cr3 = cr3; @@ -2040,15 +2043,21 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) if (WARN_ON(!is_guest_mode(vcpu))) return true; - if (!vcpu->arch.pdptrs_from_userspace && - !nested_npt_enabled(to_svm(vcpu)) && is_pae_paging(vcpu)) + if (is_pae_paging(vcpu)) { /* - * Reload the guest's PDPTRs since after a migration - * the guest CR3 might be restored prior to setting the nested - * state which can lead to a load of wrong PDPTRs. + * After migration, CR3 may have been restored before + * KVM_SET_NESTED_STATE, so the PDPTR load into mmu->pdptrs[] + * may have treated CR3 as an L1 GPA. For nNPT, drop the + * cache so the next access reloads them with the proper + * nGPA translation. For !nNPT, reload eagerly unless userspace + * already supplied authoritative PDPTRs via KVM_SET_SREGS2. */ - if (CC(!load_pdptrs(vcpu, vcpu->arch.cr3))) + if (nested_npt_enabled(to_svm(vcpu))) + kvm_register_mark_for_reload(vcpu, VCPU_REG_PDPTR); + else if (!vcpu->arch.pdptrs_from_userspace && + CC(!load_pdptrs(vcpu, vcpu->arch.cr3))) return false; + } if (!nested_svm_merge_msrpm(vcpu)) { vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; From af7b2ff7d46b4a2a58081c8072055e951c52774f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:55:44 -0400 Subject: [PATCH 3499/5214] KVM: x86: check that kvm_handle_invpcid is only invoked with shadow paging This is true for both Intel and AMD. On Intel, "enable INVPCID" is set unconditionally if supported, but the vmexit is triggered by the "INVLPG exiting" control which is disabled by enable_ept. On AMD, KVM can intercept INVPCID if NPT is enabled but only in order to inject #UD in the guest. Signed-off-by: Paolo Bonzini Message-ID: <20260530165545.25599-5-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/x86.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a5e09bf431ce..e369e291f7a4 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -14289,6 +14289,9 @@ int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva) return 1; } + if (WARN_ON_ONCE(tdp_enabled)) + return 0; + pcid_enabled = kvm_is_cr4_bit_set(vcpu, X86_CR4_PCIDE); switch (type) { From 8b9ef3220050e19a076f3fa12fa12b01f9f33446 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 30 May 2026 12:55:45 -0400 Subject: [PATCH 3500/5214] KVM: x86/mmu: move pdptrs out of the MMU PDPTRs are part of the CPU state. A bit unconventionally, they are reached via vcpu->arch.walk_mmu instead of being stored in vcpu->arch directly. That is nice in principle---it would allow TDP shadow paging to have its own PDPTRs---but it is not necessary, because EPT has no PDPTRs and NPT does not cache them. Since kvm_pdptr_read does not otherwise need the MMU, drop the pdptrs from the MMU altogether. There is however something to be careful about, in that PDPTRs are now not stored separately in root_mmu and nested_mmu for L1 and L2 guests. In practice this was already not an issue: - for EPT the VMCS0x has to keep them up to date; and for the purpose of emulation they are always loaded from the VMCS on vmentry/vmexit, thanks to the clearing of dirty and available register bitmaps in vmx_switch_vmcs() - for NPT, VCPU_EXREG_PDPTR is similarly cleared for nNPT, which does not cache the PDPTRs; while for non-nNPT the PDPTRs are loaded together with the load of CR3. Note that page table PDPTRs are not affected, since they are stored in pae_root. Signed-off-by: Paolo Bonzini Message-ID: <20260530165545.25599-6-pbonzini@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 5 ++--- arch/x86/kvm/kvm_cache_regs.h | 4 ++-- arch/x86/kvm/svm/svm.c | 2 +- arch/x86/kvm/vmx/vmx.c | 20 ++++++++------------ arch/x86/kvm/x86.c | 6 +++--- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5fdd8cb3680f..1238cb002afc 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -522,10 +522,7 @@ struct kvm_mmu { * the bits spte never used. */ struct rsvd_bits_validate shadow_zero_check; - struct rsvd_bits_validate guest_rsvd_check; - - u64 pdptrs[4]; /* pae */ }; enum pmc_type { @@ -883,6 +880,8 @@ struct kvm_vcpu_arch { */ struct kvm_mmu *walk_mmu; + u64 pdptrs[4]; /* pae */ + struct kvm_mmu_memory_cache mmu_pte_list_desc_cache; struct kvm_mmu_memory_cache mmu_shadow_page_cache; struct kvm_mmu_memory_cache mmu_shadowed_info_cache; diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h index 6bae5db5a54e..2a93e8c45c1a 100644 --- a/arch/x86/kvm/kvm_cache_regs.h +++ b/arch/x86/kvm/kvm_cache_regs.h @@ -192,12 +192,12 @@ static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index) if (!kvm_register_is_available(vcpu, VCPU_REG_PDPTR)) kvm_x86_call(cache_reg)(vcpu, VCPU_REG_PDPTR); - return vcpu->arch.walk_mmu->pdptrs[index]; + return vcpu->arch.pdptrs[index]; } static inline void kvm_pdptr_write(struct kvm_vcpu *vcpu, int index, u64 value) { - vcpu->arch.walk_mmu->pdptrs[index] = value; + vcpu->arch.pdptrs[index] = value; } static inline ulong kvm_read_cr0_bits(struct kvm_vcpu *vcpu, ulong mask) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 81e5a889a794..92fc9e0f133f 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1537,7 +1537,7 @@ static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) switch (reg) { case VCPU_REG_PDPTR: /* - * When !npt_enabled, mmu->pdptrs[] is already available since + * When !npt_enabled, vcpu->pdptrs[] is already available since * it is always updated per SDM when moving to CRs. */ if (npt_enabled) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 9324d6083941..4f81564a8bcb 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3366,30 +3366,26 @@ void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu) void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu) { - struct kvm_mmu *mmu = vcpu->arch.walk_mmu; - if (!kvm_register_is_dirty(vcpu, VCPU_REG_PDPTR)) return; if (is_pae_paging(vcpu)) { - vmcs_write64(GUEST_PDPTR0, mmu->pdptrs[0]); - vmcs_write64(GUEST_PDPTR1, mmu->pdptrs[1]); - vmcs_write64(GUEST_PDPTR2, mmu->pdptrs[2]); - vmcs_write64(GUEST_PDPTR3, mmu->pdptrs[3]); + vmcs_write64(GUEST_PDPTR0, vcpu->arch.pdptrs[0]); + vmcs_write64(GUEST_PDPTR1, vcpu->arch.pdptrs[1]); + vmcs_write64(GUEST_PDPTR2, vcpu->arch.pdptrs[2]); + vmcs_write64(GUEST_PDPTR3, vcpu->arch.pdptrs[3]); } } void ept_save_pdptrs(struct kvm_vcpu *vcpu) { - struct kvm_mmu *mmu = vcpu->arch.walk_mmu; - if (WARN_ON_ONCE(!is_pae_paging(vcpu))) return; - mmu->pdptrs[0] = vmcs_read64(GUEST_PDPTR0); - mmu->pdptrs[1] = vmcs_read64(GUEST_PDPTR1); - mmu->pdptrs[2] = vmcs_read64(GUEST_PDPTR2); - mmu->pdptrs[3] = vmcs_read64(GUEST_PDPTR3); + vcpu->arch.pdptrs[0] = vmcs_read64(GUEST_PDPTR0); + vcpu->arch.pdptrs[1] = vmcs_read64(GUEST_PDPTR1); + vcpu->arch.pdptrs[2] = vmcs_read64(GUEST_PDPTR2); + vcpu->arch.pdptrs[3] = vmcs_read64(GUEST_PDPTR3); kvm_register_mark_available(vcpu, VCPU_REG_PDPTR); } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e369e291f7a4..e26bdbdbef6a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1065,7 +1065,7 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) gpa_t real_gpa; int i; int ret; - u64 pdpte[ARRAY_SIZE(mmu->pdptrs)]; + u64 pdpte[ARRAY_SIZE(vcpu->arch.pdptrs)]; /* * If the MMU is nested, CR3 holds an L2 GPA and needs to be translated @@ -1094,10 +1094,10 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) * Marking VCPU_REG_PDPTR dirty doesn't work for !tdp_enabled. * Shadow page roots need to be reconstructed instead. */ - if (!tdp_enabled && memcmp(mmu->pdptrs, pdpte, sizeof(mmu->pdptrs))) + if (!tdp_enabled && memcmp(vcpu->arch.pdptrs, pdpte, sizeof(vcpu->arch.pdptrs))) kvm_mmu_free_roots(vcpu->kvm, mmu, KVM_MMU_ROOT_CURRENT); - memcpy(mmu->pdptrs, pdpte, sizeof(mmu->pdptrs)); + memcpy(vcpu->arch.pdptrs, pdpte, sizeof(vcpu->arch.pdptrs)); kvm_register_mark_dirty(vcpu, VCPU_REG_PDPTR); kvm_make_request(KVM_REQ_LOAD_MMU_PGD, vcpu); vcpu->arch.pdptrs_from_userspace = false; From b93062b6d8a1b2d9bad235cac25558a909819026 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Thu, 28 May 2026 22:48:07 +0530 Subject: [PATCH 3501/5214] serial: qcom_geni: Fix RX DMA stall when SE_DMA_RX_LEN_IN is zero In qcom_geni_serial_handle_rx_dma(), geni_se_rx_dma_unprep() clears port->rx_dma_addr before SE_DMA_RX_LEN_IN is read. If the register is zero, for example when the RX stale counter fires on an idle line, the handler returns without calling geni_se_rx_dma_prep(). The next RX DMA interrupt then hits the !port->rx_dma_addr guard and returns immediately, so the RX DMA buffer is never rearmed and later input is lost. Keep the handler on the rearm path when rx_in is zero. Warn about the unexpected zero-length DMA completion, skip received-data handling, and always call geni_se_rx_dma_prep(). Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Cc: stable@vger.kernel.org Reviewed-by: Bartosz Golaszewski Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260528-serial-rx-0-byte-fix-v2-1-b4195cfe342f@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index d81b539cff7f..7ead87b4eb65 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -905,12 +905,9 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) port->rx_dma_addr = 0; rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); - if (!rx_in) { - dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n"); - return; - } - - if (!drop) + if (!rx_in) + dev_warn_ratelimited(uport->dev, "serial engine reports 0 RX bytes in!\n"); + else if (!drop) handle_rx_uart(uport, rx_in); ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, From a287620312dc6dcb9a093417a0e589bf30fcf38a Mon Sep 17 00:00:00 2001 From: Yi Yang Date: Thu, 4 Jun 2026 06:07:34 +0000 Subject: [PATCH 3502/5214] vc_screen: fix null-ptr-deref in vcs_notifier() during concurrent vcs_write A KASAN null-ptr-deref was observed in vcs_notifier(): BUG: KASAN: null-ptr-deref in vcs_notifier+0x98/0x130 Read of size 2 at addr qmp_cmd_name: qmp_capabilities, arguments: {} The issue is a race condition in vcs_write(). When the console_lock is temporarily dropped (to copy data from userspace), the vc_data pointer obtained from vcs_vc() may become stale. After re-acquiring the lock, vcs_vc() is called again to re-validate the pointer. If the vc has been deallocated in the meantime, vcs_vc() returns NULL, and the while loop breaks (with written > 0). However, after the loop, vcs_scr_updated(vc) is still called with the now-NULL vc pointer, leading to a null pointer dereference in the notifier chain (vcs_notifier dereferences param->vc). Fix this by adding a NULL check for vc before calling vcs_scr_updated(). Fixes: 8fb9ea65c9d1 ("vc_screen: reload load of struct vc_data pointer in vcs_write() to avoid UAF") Cc: stable@vger.kernel.org Signed-off-by: Yi Yang Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20260604060734.2914976-1-yiyang13@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 4d2d46c95fef..7d40eacc21b3 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -686,7 +686,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) } *ppos += written; ret = written; - if (written) + if (written && vc) vcs_scr_updated(vc); return ret; From 426e83cab1f5d53069ac7030cb03e2d7c6367ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 3 Jun 2026 11:56:16 +0200 Subject: [PATCH 3503/5214] serial: 8250_pci: Don't specify conflicting values to pci_device_id members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCI_VDEVICE macro assigns 0 to .class and .class_mask to allow the next value in the initializer to define the value for .driver_data. So the construct { PCI_VDEVICE(INTASHIELD, 0x0D21), .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, .class_mask = 0xffff00, .driver_data = pbn_b2_4_115200, }, introduced in commit 44e55f1f3088 ("serial: 8250_pci: Consistently define pci_device_ids using named initializers") has conflicting assignments. In only some configurations (i.e. W=1 for me) that makes the compiler unhappy. So convert the two affected items to PCI_DEVICE which doesn't have that hidden assigment to .class and .class_mask. Fixes: 44e55f1f3088 ("serial: 8250_pci: Consistently define pci_device_ids using named initializers") Signed-off-by: Uwe Kleine-König (The Capable Hub) Reported-by: Andy Shevchenko Closes: https://lore.kernel.org/linux-serial/ah_5qVKOf8LXG1Xo@ashevche-desk.local/T/#ma6eab90ca801b4292639f5c255a89b4033b33d21 Tested-by: Andy Shevchenko Link: https://patch.msgid.link/20260603095616.937968-2-u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 3e5bc9e8d269..58b4e525bdb6 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -5394,12 +5394,12 @@ static const struct pci_device_id serial_pci_tbl[] = { * Brainboxes UC-260/271/701/756 */ { - PCI_VDEVICE(INTASHIELD, 0x0D21), + PCI_DEVICE(PCI_VENDOR_ID_INTASHIELD, 0x0D21), .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, .class_mask = 0xffff00, .driver_data = pbn_b2_4_115200, }, { - PCI_VDEVICE(INTASHIELD, 0x0E34), + PCI_DEVICE(PCI_VENDOR_ID_INTASHIELD, 0x0E34), .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, .class_mask = 0xffff00, .driver_data = pbn_b2_4_115200, From a7532c505f7f77e14bcc530cb452c73ff1d0272b Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 4 May 2026 20:13:20 +0200 Subject: [PATCH 3504/5214] sparc: vio: use sysfs_emit in sysfs show functions Replace sprintf() and scnprintf() with sysfs_emit() in sysfs show functions. sysfs_emit() is preferred to format sysfs output as it provides better bounds checking. Signed-off-by: Thorsten Blum Reviewed-by: Andreas Larsson Tested-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/kernel/vio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c index 8b4f55047716..b7b06752a038 100644 --- a/arch/sparc/kernel/vio.c +++ b/arch/sparc/kernel/vio.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -121,7 +122,7 @@ static ssize_t devspec_show(struct device *dev, else if (!strcmp(vdev->type, "vdc-port")) str = "vdisk"; - return sprintf(buf, "%s\n", str); + return sysfs_emit(buf, "%s\n", str); } static DEVICE_ATTR_RO(devspec); @@ -129,7 +130,7 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vio_dev *vdev = to_vio_dev(dev); - return sprintf(buf, "%s\n", vdev->type); + return sysfs_emit(buf, "%s\n", vdev->type); } static DEVICE_ATTR_RO(type); @@ -138,7 +139,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, { const struct vio_dev *vdev = to_vio_dev(dev); - return sprintf(buf, "vio:T%sS%s\n", vdev->type, vdev->compat); + return sysfs_emit(buf, "vio:T%sS%s\n", vdev->type, vdev->compat); } static DEVICE_ATTR_RO(modalias); @@ -192,7 +193,7 @@ show_pciobppath_attr(struct device *dev, struct device_attribute *attr, vdev = to_vio_dev(dev); dp = vdev->dp; - return scnprintf(buf, PAGE_SIZE, "%pOF\n", dp); + return sysfs_emit(buf, "%pOF\n", dp); } static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, From 84e8d9c34b3d4d59e5fedfd49df8a2617aa09db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 14 Mar 2026 13:17:49 +0100 Subject: [PATCH 3505/5214] sparc: uapi: Add ucontext.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On SPARC the standard ucontext.h UAPI header is named 'uctx.h'. Add an alias for the standard name. Signed-off-by: Thomas Weißschuh Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/include/uapi/asm/ucontext.h | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 arch/sparc/include/uapi/asm/ucontext.h diff --git a/arch/sparc/include/uapi/asm/ucontext.h b/arch/sparc/include/uapi/asm/ucontext.h new file mode 100644 index 000000000000..e9b5dbbedefb --- /dev/null +++ b/arch/sparc/include/uapi/asm/ucontext.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#include From 2cdd477261a989278fea3e1127cea472b6eca52a Mon Sep 17 00:00:00 2001 From: Kexin Sun Date: Sat, 21 Mar 2026 18:59:35 +0800 Subject: [PATCH 3506/5214] sparc32: remove deadwood swift_flush_tlb_page() debug code Remove an #if 0 block that has been dead since at least Linux 2.6.12. The block was marked "P3: deadwood to debug precise flushes on Swift" and contained a never-compiled alternative implementation of swift_flush_tlb_page(). It also referenced the since-removed srmmu_flush_tlb_page(), dropped in commit 3d5f7d37c8b4 ("sparc32: drop unused functions in pgtsrmmu.h"). Assisted-by: unnamed:deepseek-v3.2 coccinelle Signed-off-by: Kexin Sun Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/mm/srmmu.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 1b24c5e8d73d..9a74902ad181 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -581,35 +581,6 @@ extern void swift_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); -#if 0 /* P3: deadwood to debug precise flushes on Swift. */ -void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - int cctx, ctx1; - - page &= PAGE_MASK; - if ((ctx1 = vma->vm_mm->context) != -1) { - cctx = srmmu_get_context(); -/* Is context # ever different from current context? P3 */ - if (cctx != ctx1) { - printk("flush ctx %02x curr %02x\n", ctx1, cctx); - srmmu_set_context(ctx1); - swift_flush_page(page); - __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : - "r" (page), "i" (ASI_M_FLUSH_PROBE)); - srmmu_set_context(cctx); - } else { - /* Rm. prot. bits from virt. c. */ - /* swift_flush_cache_all(); */ - /* swift_flush_cache_page(vma, page); */ - swift_flush_page(page); - - __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : - "r" (page), "i" (ASI_M_FLUSH_PROBE)); - /* same as above: srmmu_flush_tlb_page() */ - } - } -} -#endif /* * The following are all MBUS based SRMMU modules, and therefore could From 3774fd535fc2e54cdc4e915050f787a8b0bc6f8c Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 4 May 2026 17:09:12 -0700 Subject: [PATCH 3507/5214] sparc: remove unused SERIAL_CONSOLE config option There are no references to this option in SPARC or architecture-independent code. Remove it to simplify the configuration process. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/Kconfig | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index a6b787efc2c4..fb53b21a8696 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -327,30 +327,6 @@ config SPARC_LED by reading /proc/led and its blinking mode can be changed via writes to /proc/led -config SERIAL_CONSOLE - bool - depends on SPARC32 - default y - help - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). This could be useful if some terminal or printer is connected - to that serial port. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyS1". (Try "man bootparam" or see the documentation of - your boot loader (silo) about how to pass options to the kernel at - boot time.) - - If you don't have a graphics card installed and you say Y here, the - kernel will automatically use the first serial line, /dev/ttyS0, as - system console. - - If unsure, say N. - config SPARC_LEON bool "Sparc Leon processor family" depends on SPARC32 From 5b0eee4cd812bd6547eea393cb9b5c0322f26c88 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 5 May 2026 20:18:15 -0700 Subject: [PATCH 3508/5214] sparc64: uprobes: add missing break Missing fallthrough causes failure with newer compilers: arch/sparc/kernel/uprobes.c:284:2: error: unannotated fall-through between switch labels [-Werror,-Wimplicit-fallthrough] 284 | default: | ^ arch/sparc/kernel/uprobes.c:284:2: note: insert 'break;' to avoid fall-through 284 | default: | ^ | break; Signed-off-by: Rosen Penev Reviewed-by: Masami Hiramatsu (Google) Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/kernel/uprobes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sparc/kernel/uprobes.c b/arch/sparc/kernel/uprobes.c index 305017bec164..c8cac64e9988 100644 --- a/arch/sparc/kernel/uprobes.c +++ b/arch/sparc/kernel/uprobes.c @@ -280,6 +280,7 @@ int arch_uprobe_exception_notify(struct notifier_block *self, case DIE_SSTEP: if (uprobe_post_sstep_notifier(args->regs)) ret = NOTIFY_STOP; + break; default: break; From 1919c0e50000e9afb76dbbf4a1261ec3238e3ae4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 22 May 2026 21:20:26 -0700 Subject: [PATCH 3509/5214] sparc: add _mcount() prototype sparc64 defconfig told me WARNING: modpost: EXPORT symbol "_mcount" [vmlinux] version generation failed, symbol will not be versioned. Is "_mcount" prototyped in ? so I added it. Cc: David S. Miller Cc: Andreas Larsson Signed-off-by: Andrew Morton Reviewed-by: Andreas Larsson Tested-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/include/asm/asm-prototypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sparc/include/asm/asm-prototypes.h b/arch/sparc/include/asm/asm-prototypes.h index bbd1a8afaabf..270c51017212 100644 --- a/arch/sparc/include/asm/asm-prototypes.h +++ b/arch/sparc/include/asm/asm-prototypes.h @@ -25,6 +25,7 @@ void *memcpy(void *dest, const void *src, size_t n); void *memset(void *s, int c, size_t n); typedef int TItype __attribute__((mode(TI))); TItype __multi3(TItype a, TItype b); +void _mcount(void); s64 __ashldi3(s64, int); s64 __lshrdi3(s64, int); From 9bc0bd9617964804dc02e6ee2413af42c4a50d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 25 May 2026 10:36:21 +0200 Subject: [PATCH 3510/5214] sparc: Avoid -Wunused-but-set-parameter in clear_user_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop in clear_user_pages() iterates over all pages and calls clear_user_page() for each of them. During the loop "vaddr" is modified. However on sparc clear_user() is a macro which does not use "vaddr". The compiler sees a variable which is modified but never used and emits a warning for that: include/linux/highmem.h: In function 'clear_user_pages': include/linux/highmem.h:234:63: warning: parameter 'vaddr' set but not used [-Wunused-but-set-parameter=] static inline void clear_user_pages(void *addr, unsigned long vaddr, Other architectures use an inline function for clear_user_page() which avoids the warning. This is not possible on sparc, as sparc_flush_page_to_ram() is not yet declared where clear_user_page() is defined. Including cacheflush_32.h will trigger recursive and lots of other issues. So hide the warning with a cast to (void) instead. While we are here, do the same for copy_user_page(). Fixes: 62a9f5a85b98 ("mm: introduce clear_pages() and clear_user_pages()") Signed-off-by: Thomas Weißschuh Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/include/asm/page_32.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sparc/include/asm/page_32.h b/arch/sparc/include/asm/page_32.h index c1bccbedf567..9f0b54f70908 100644 --- a/arch/sparc/include/asm/page_32.h +++ b/arch/sparc/include/asm/page_32.h @@ -20,10 +20,12 @@ #define clear_user_page(addr, vaddr, page) \ do { clear_page(addr); \ sparc_flush_page_to_ram(page); \ + (void)(vaddr); \ } while (0) #define copy_user_page(to, from, vaddr, page) \ do { copy_page(to, from); \ sparc_flush_page_to_ram(page); \ + (void)(vaddr); \ } while (0) /* The following structure is used to hold the physical From d20457b46eca76b9bb716dd31af591cad21607b5 Mon Sep 17 00:00:00 2001 From: Muralidhara M K Date: Fri, 12 Jun 2026 09:56:08 +0530 Subject: [PATCH 3511/5214] platform/x86/amd/hsmp: Clamp ioctl/send_message indices (Spectre v1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although validate_message() checks msg_id, a mispredicted branch can still allow speculative indexing into hsmp_msg_desc_table[]. Clamp msg.msg_id with array_index_nospec() at entry to hsmp_ioctl_msg() so downstream dereferences (including via is_get_msg() and hsmp_send_message()) see a bounded index. Similarly, hsmp_send_message() bounds-checks msg->sock_ind before indexing hsmp_pdev.sock[], but a mispredicted branch can still speculatively use the raw index (Spectre v1, CVE-2017-5753). Apply array_index_nospec() after the check so every caller that reaches hsmp_pdev.sock[] through this helper sees a clamped socket index—including hsmp_ioctl_msg() and any other path that hands a user-derived struct hsmp_message to hsmp_send_message(). Reviewed-by: Muthusamy Ramalingam Signed-off-by: Muralidhara M K Link: https://patch.msgid.link/20260612042610.1629037-7-muralidhara.mk@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp/hsmp.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 631ffc0978d1..6a26937fc2b5 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -202,6 +202,7 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { struct hsmp_socket *sock; + unsigned int sock_ind; int ret; if (!msg) @@ -212,7 +213,15 @@ int hsmp_send_message(struct hsmp_message *msg) if (!hsmp_pdev.sock || msg->sock_ind >= hsmp_pdev.num_sockets) return -ENODEV; - sock = &hsmp_pdev.sock[msg->sock_ind]; + + /* + * Sanitize sock_ind after the bounds check. A mispredicted branch can + * still let the CPU speculatively use msg->sock_ind as an index into + * hsmp_pdev.sock[] (Spectre v1, CVE-2017-5753), including for callers + * other than hsmp_ioctl_msg() that pass a user-derived socket index. + */ + sock_ind = array_index_nospec(msg->sock_ind, hsmp_pdev.num_sockets); + sock = &hsmp_pdev.sock[sock_ind]; ret = down_interruptible(&sock->hsmp_sem); if (ret < 0) @@ -308,6 +317,19 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) return -ENOMSG; + /* + * Sanitize the user-controlled msg_id against speculative + * execution. The bounds check above retires the out-of-range + * case with -ENOMSG, but a mispredicted branch can still let the + * CPU speculatively use msg_id as an index into + * hsmp_msg_desc_table[] (here and in validate_message() / + * is_get_msg() called downstream via hsmp_send_message()), and + * pull arbitrary kernel memory into the cache (Spectre v1, + * CVE-2017-5753). Clamp once into msg.msg_id so every downstream + * dereference sees the sanitized value. + */ + msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); + switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { case FMODE_WRITE: /* From 0b38a42bf59fc3ba97dd6f02bfc257dffa0e1d6d Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:06 +0000 Subject: [PATCH 3512/5214] platform/x86: asus-armoury: add support for GA402NJ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GA402NJ. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-2-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 5be34b6a92db..b53288687659 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -950,6 +950,38 @@ static const struct dmi_system_id power_limits[] = { .dc_data = NULL, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA402NJ"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 35, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_def = 65, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_def = 10, + .nv_dynamic_boost_max = 15, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, { .matches = { // This model is full AMD. No Nvidia dGPU. From a0bb64f3170adccb5780cf8778e601392d94f244 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:07 +0000 Subject: [PATCH 3513/5214] platform/x86: asus-armoury: add support for GA403UM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GA403UM. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-3-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index b53288687659..52c122b01abd 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1065,6 +1065,38 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA403UM"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 0, + .nv_dynamic_boost_max = 15, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GA403UV"), From 72dd918bbe008d64a2c2352bdf14671f1ac068c7 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:08 +0000 Subject: [PATCH 3514/5214] platform/x86: asus-armoury: add support for FX608JPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model FX608JPR. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-4-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 52c122b01abd..65166b50a2c3 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -936,6 +936,34 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FX608JPR"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 100, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 10, + .nv_dynamic_boost_max = 15, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 100, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 53, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GA401Q"), From 0f2d6a308210caaa5e0ebf9c085d87f4a2c06bfa Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:45 +0200 Subject: [PATCH 3515/5214] platform/x86: dell-descriptor: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the descriptor. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/dell/dell-wmi-descriptor.c | 110 ++++++++---------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-descriptor.c b/drivers/platform/x86/dell/dell-wmi-descriptor.c index c2a180202719..179d5678b3ad 100644 --- a/drivers/platform/x86/dell/dell-wmi-descriptor.c +++ b/drivers/platform/x86/dell/dell-wmi-descriptor.c @@ -7,14 +7,35 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include +#include #include #include +#include #include #include "dell-wmi-descriptor.h" #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" +/* + * Descriptor buffer is 128 byte long and contains: + * + * Name Offset Length Value + * Vendor Signature 0 4 "DELL" + * Object Signature 4 4 " WMI" + * WMI Interface Version 8 4 + * WMI buffer length 12 4 + * WMI hotfix number 16 4 + */ +struct descriptor { + /* Both fields are NOT null-terminated */ + char vendor_signature[4] __nonstring; + char object_signature[4] __nonstring; + __le32 interface_version; + __le32 buffer_length; + __le32 hotfix_number; +} __packed; + struct descriptor_priv { struct list_head list; u32 interface_version; @@ -88,77 +109,46 @@ bool dell_wmi_get_hotfix(u32 *hotfix) } EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix); -/* - * Descriptor buffer is 128 byte long and contains: - * - * Name Offset Length Value - * Vendor Signature 0 4 "DELL" - * Object Signature 4 4 " WMI" - * WMI Interface Version 8 4 - * WMI buffer length 12 4 - * WMI hotfix number 16 4 - */ -static int dell_wmi_descriptor_probe(struct wmi_device *wdev, - const void *context) +static int dell_wmi_descriptor_probe(struct wmi_device *wdev, const void *context) { - union acpi_object *obj = NULL; struct descriptor_priv *priv; - u32 *buffer; + struct wmi_buffer buffer; int ret; - obj = wmidev_block_query(wdev, 0); - if (!obj) { - dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); - ret = -EIO; - goto out; + ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct descriptor)); + if (ret < 0) { + descriptor_valid = ret; + return ret; } - if (obj->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); - ret = -EINVAL; - descriptor_valid = ret; - goto out; + struct descriptor *desc __free(kfree) = buffer.data; + + if (strncmp(desc->vendor_signature, "DELL", sizeof(desc->vendor_signature))) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid vendor signature (%4ph)\n", + desc->vendor_signature); + descriptor_valid = -ENOMSG; + return -ENOMSG; } - /* Although it's not technically a failure, this would lead to - * unexpected behavior - */ - if (obj->buffer.length != 128) { - dev_err(&wdev->dev, - "Dell descriptor buffer has unexpected length (%d)\n", - obj->buffer.length); - ret = -EINVAL; - descriptor_valid = ret; - goto out; - } - - buffer = (u32 *)obj->buffer.pointer; - - if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) { - dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n", - buffer); - ret = -EINVAL; - descriptor_valid = ret; - goto out; + if (strncmp(desc->object_signature, " WMI", sizeof(desc->object_signature))) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid object signature (%4ph)\n", + desc->object_signature); + descriptor_valid = -ENOMSG; + return -ENOMSG; } descriptor_valid = 0; - if (buffer[2] != 0 && buffer[2] != 1) - dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n", - (unsigned long) buffer[2]); + if (le32_to_cpu(desc->interface_version) > 1) + dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n", + le32_to_cpu(desc->interface_version)); - priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv), - GFP_KERNEL); + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - if (!priv) { - ret = -ENOMEM; - goto out; - } - - priv->interface_version = buffer[2]; - priv->size = buffer[3]; - priv->hotfix = buffer[4]; - ret = 0; + priv->interface_version = le32_to_cpu(desc->interface_version); + priv->size = le32_to_cpu(desc->buffer_length); + priv->hotfix = le32_to_cpu(desc->hotfix_number); dev_set_drvdata(&wdev->dev, priv); mutex_lock(&list_mutex); list_add_tail(&priv->list, &wmi_list); @@ -169,8 +159,6 @@ static int dell_wmi_descriptor_probe(struct wmi_device *wdev, (unsigned long) priv->size, (unsigned long) priv->hotfix); -out: - kfree(obj); return ret; } From 1719340487e63da13f2ac25e488c72737a545772 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:46 +0200 Subject: [PATCH 3516/5214] platform/x86: dell-privacy: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the device state. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-privacy.c | 77 +++++++++----------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c index ed099a431ea4..f9d275b2f900 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.c +++ b/drivers/platform/x86/dell/dell-wmi-privacy.c @@ -9,11 +9,14 @@ #include #include +#include +#include #include #include #include #include #include +#include #include #include "dell-wmi-privacy.h" @@ -25,6 +28,26 @@ #define DELL_PRIVACY_CAMERA_EVENT 0x2 #define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev) +/* + * Describes the Device State class exposed by BIOS which can be consumed by + * various applications interested in knowing the Privacy feature capabilities. + * class DeviceState + * { + * [key, read] string InstanceName; + * [read] boolean ReadOnly; + * + * [WmiDataId(1), read] uint32 DevicesSupported; + * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen + * + * [WmiDataId(2), read] uint32 CurrentState; + * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen + * }; + */ +struct device_state { + __le32 devices_supported; + __le32 current_state; +} __packed; + /* * The wmi_list is used to store the privacy_priv struct with mutex protecting */ @@ -185,60 +208,28 @@ static struct attribute *privacy_attrs[] = { }; ATTRIBUTE_GROUPS(privacy); -/* - * Describes the Device State class exposed by BIOS which can be consumed by - * various applications interested in knowing the Privacy feature capabilities. - * class DeviceState - * { - * [key, read] string InstanceName; - * [read] boolean ReadOnly; - * - * [WmiDataId(1), read] uint32 DevicesSupported; - * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen - * - * [WmiDataId(2), read] uint32 CurrentState; - * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen - * }; - */ static int get_current_status(struct wmi_device *wdev) { struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev); - union acpi_object *obj_present; - u32 *buffer; - int ret = 0; + struct wmi_buffer buffer; + int ret; if (!priv) { dev_err(&wdev->dev, "dell privacy priv is NULL\n"); return -EINVAL; } + /* check privacy support features and device states */ - obj_present = wmidev_block_query(wdev, 0); - if (!obj_present) { - dev_err(&wdev->dev, "failed to read Binary MOF\n"); - return -EIO; - } + ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct device_state)); + if (ret < 0) + return ret; - if (obj_present->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Binary MOF is not a buffer!\n"); - ret = -EIO; - goto obj_free; - } - /* Although it's not technically a failure, this would lead to - * unexpected behavior - */ - if (obj_present->buffer.length != 8) { - dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n", - obj_present->buffer.length); - ret = -EINVAL; - goto obj_free; - } - buffer = (u32 *)obj_present->buffer.pointer; - priv->features_present = buffer[0]; - priv->last_status = buffer[1]; + struct device_state *state __free(kfree) = buffer.data; -obj_free: - kfree(obj_present); - return ret; + priv->features_present = le32_to_cpu(state->devices_supported); + priv->last_status = le32_to_cpu(state->current_state); + + return 0; } static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev, From 6f918e3d95c9b0b4e6a6881692a9b5f9bf0f36b0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:47 +0200 Subject: [PATCH 3517/5214] platform/x86: dell-smbios-wmi: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for returning the results of a SMBIOS call. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-smbios-wmi.c | 40 +++++++++------------ 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-wmi.c b/drivers/platform/x86/dell/dell-smbios-wmi.c index a7dca8c59d60..64d0871b706e 100644 --- a/drivers/platform/x86/dell/dell-smbios-wmi.c +++ b/drivers/platform/x86/dell/dell-smbios-wmi.c @@ -50,38 +50,32 @@ static inline struct wmi_smbios_priv *get_first_smbios_priv(void) static int run_smbios_call(struct wmi_device *wdev) { - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - struct wmi_smbios_priv *priv; - struct acpi_buffer input; - union acpi_object *obj; - acpi_status status; - - priv = dev_get_drvdata(&wdev->dev); - input.length = priv->req_buf_size - sizeof(u64); - input.pointer = &priv->buf->std; + struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); + const struct wmi_buffer input = { + .length = priv->req_buf_size - sizeof(u64), + .data = &priv->buf->std, + }; + struct wmi_buffer output; + int ret; dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n", priv->buf->std.cmd_class, priv->buf->std.cmd_select, priv->buf->std.input[0], priv->buf->std.input[1], priv->buf->std.input[2], priv->buf->std.input[3]); - status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); - if (ACPI_FAILURE(status)) - return -EIO; - obj = (union acpi_object *)output.pointer; - if (obj->type != ACPI_TYPE_BUFFER) { - dev_dbg(&wdev->dev, "received type: %d\n", obj->type); - if (obj->type == ACPI_TYPE_INTEGER) - dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n", - obj->integer.value); - kfree(output.pointer); - return -EIO; - } - memcpy(input.pointer, obj->buffer.pointer, obj->buffer.length); + /* + * The output buffer returned by the WMI method should have at least the size + * of the input buffer. + */ + ret = wmidev_invoke_method(wdev, 0, 1, &input, &output, input.length); + if (ret < 0) + return ret; + + memcpy(input.data, output.data, input.length); + kfree(output.data); dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n", priv->buf->std.output[0], priv->buf->std.output[1], priv->buf->std.output[2], priv->buf->std.output[3]); - kfree(output.pointer); return 0; } From 982b0e683aa3b1d20b1512cde53207ba1c80e22b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:48 +0200 Subject: [PATCH 3518/5214] platform/x86: dell-wmi-base: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the event data. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-base.c | 60 +++++++++++------------ 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c index 2a5804efd3ea..997383ba1846 100644 --- a/drivers/platform/x86/dell/dell-wmi-base.c +++ b/drivers/platform/x86/dell/dell-wmi-base.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -414,7 +415,8 @@ static void dell_wmi_switch_event(struct input_dev **subdev, input_sync(*subdev); } -static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining) +static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, __le16 *buffer, + int remaining) { struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); const struct key_entry *key; @@ -446,15 +448,15 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 } else if (type == 0x0011 && code == 0xe070 && remaining > 0) { dell_wmi_switch_event(&priv->tabletswitch_dev, "Dell tablet mode switch", - SW_TABLET_MODE, !buffer[0]); + SW_TABLET_MODE, !le16_to_cpu(buffer[0])); return 1; } else if (type == 0x0012 && code == 0x000c && remaining > 0) { /* Eprivacy toggle, switch to "on" key entry for on events */ - if (buffer[0] == 2) + if (le16_to_cpu(buffer[0]) == 2) key++; used = 1; } else if (type == 0x0012 && code == 0x000d && remaining > 0) { - value = (buffer[2] == 2); + value = (le16_to_cpu(buffer[2]) == 2); used = 1; } @@ -463,24 +465,17 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 return used; } -static void dell_wmi_notify(struct wmi_device *wdev, - union acpi_object *obj) +static void dell_wmi_notify(struct wmi_device *wdev, const struct wmi_buffer *buffer) { struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - u16 *buffer_entry, *buffer_end; - acpi_size buffer_size; + __le16 *buffer_entry, *buffer_end; + size_t buffer_size; int len, i; - if (obj->type != ACPI_TYPE_BUFFER) { - pr_warn("bad response type %x\n", obj->type); - return; - } + pr_debug("Received WMI event (%*ph)\n", (int)buffer->length, buffer->data); - pr_debug("Received WMI event (%*ph)\n", - obj->buffer.length, obj->buffer.pointer); - - buffer_entry = (u16 *)obj->buffer.pointer; - buffer_size = obj->buffer.length/2; + buffer_entry = buffer->data; + buffer_size = buffer->length / 2; buffer_end = buffer_entry + buffer_size; /* @@ -496,12 +491,12 @@ static void dell_wmi_notify(struct wmi_device *wdev, * one event on devices with WMI interface version 0. */ if (priv->interface_version == 0 && buffer_entry < buffer_end) - if (buffer_end > buffer_entry + buffer_entry[0] + 1) - buffer_end = buffer_entry + buffer_entry[0] + 1; + if (buffer_end > buffer_entry + le16_to_cpu(buffer_entry[0]) + 1) + buffer_end = buffer_entry + le16_to_cpu(buffer_entry[0]) + 1; while (buffer_entry < buffer_end) { - len = buffer_entry[0]; + len = le16_to_cpu(buffer_entry[0]); if (len == 0) break; @@ -514,11 +509,11 @@ static void dell_wmi_notify(struct wmi_device *wdev, pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); - switch (buffer_entry[1]) { + switch (le16_to_cpu(buffer_entry[1])) { case 0x0000: /* One key pressed or event occurred */ if (len > 2) - dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[2], + dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[2]), buffer_entry + 3, len - 3); /* Extended data is currently ignored */ @@ -526,22 +521,23 @@ static void dell_wmi_notify(struct wmi_device *wdev, case 0x0010: /* Sequence of keys pressed */ case 0x0011: /* Sequence of events occurred */ for (i = 2; i < len; ++i) - i += dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[i], + i += dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[i]), buffer_entry + i, len - i - 1); break; case 0x0012: - if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3], - buffer_entry[4])) + if ((len > 4) && dell_privacy_process_event(le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[3]), + le16_to_cpu(buffer_entry[4]))) /* dell_privacy_process_event has handled the event */; else if (len > 2) - dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2], + dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[2]), buffer_entry + 3, len - 3); break; default: /* Unknown event */ - pr_info("Unknown WMI event type 0x%x\n", - (int)buffer_entry[1]); + pr_info("Unknown WMI event type 0x%x\n", le16_to_cpu(buffer_entry[1])); break; } @@ -825,10 +821,10 @@ static struct wmi_driver dell_wmi_driver = { .name = "dell-wmi", }, .id_table = dell_wmi_id_table, - .min_event_size = sizeof(u16), + .min_event_size = sizeof(__le16), .probe = dell_wmi_probe, .remove = dell_wmi_remove, - .notify = dell_wmi_notify, + .notify_new = dell_wmi_notify, }; static int __init dell_wmi_init(void) From 7fced293bbd00ee8d20eaf4654849ac9ff332973 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:49 +0200 Subject: [PATCH 3519/5214] platform/x86: dell-ddv: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI intergers/strings/packages for exchanging data. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-6-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-ddv.c | 179 +++++++++++------------ 1 file changed, 85 insertions(+), 94 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 62e3d060f038..736d9b1fdcfb 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -7,8 +7,9 @@ #define pr_format(fmt) KBUILD_MODNAME ": " fmt -#include #include +#include +#include #include #include #include @@ -99,6 +100,11 @@ enum dell_ddv_method { DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22, }; +struct dell_wmi_buffer { + __le32 raw_size; + u8 raw_data[]; +} __packed; + struct fan_sensor_entry { u8 type; __le16 rpm; @@ -126,7 +132,7 @@ struct dell_wmi_ddv_sensors { bool active; struct mutex lock; /* protect caching */ unsigned long timestamp; - union acpi_object *obj; + struct dell_wmi_buffer *buffer; u64 entries; }; @@ -158,105 +164,96 @@ static const char * const fan_dock_labels[] = { "Docking Chipset Fan", }; -static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, - union acpi_object **result, acpi_object_type type) +static int dell_wmi_ddv_query(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, + struct wmi_buffer *output, size_t min_size) { - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; - const struct acpi_buffer in = { - .length = sizeof(arg), - .pointer = &arg, + __le32 arg2 = cpu_to_le32(arg); + const struct wmi_buffer input = { + .length = sizeof(arg2), + .data = &arg2, }; - union acpi_object *obj; - acpi_status ret; - ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); - if (ACPI_FAILURE(ret)) - return -EIO; - - obj = out.pointer; - if (!obj) - return -ENODATA; - - if (obj->type != type) { - kfree(obj); - return -ENOMSG; - } - - *result = obj; - - return 0; + return wmidev_invoke_method(wdev, 0x0, method, &input, output, min_size); } static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, u32 *res) { - union acpi_object *obj; + struct wmi_buffer output; int ret; - ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER); + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(__le32)); if (ret < 0) return ret; - if (obj->integer.value <= U32_MAX) - *res = (u32)obj->integer.value; - else - ret = -ERANGE; + __le32 *argr __free(kfree) = output.data; - kfree(obj); + *res = le32_to_cpu(*argr); - return ret; + return 0; } static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method, - u32 arg, union acpi_object **result) + u32 arg, struct dell_wmi_buffer **result) { - union acpi_object *obj; - u64 buffer_size; + struct dell_wmi_buffer *buffer; + struct wmi_buffer output; + size_t buffer_size; int ret; - ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE); + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(*buffer)); if (ret < 0) return ret; - if (obj->package.count != 2 || - obj->package.elements[0].type != ACPI_TYPE_INTEGER || - obj->package.elements[1].type != ACPI_TYPE_BUFFER) { - ret = -ENOMSG; - - goto err_free; - } - - buffer_size = obj->package.elements[0].integer.value; - - if (!buffer_size) { + buffer = output.data; + if (!le32_to_cpu(buffer->raw_size)) { ret = -ENODATA; goto err_free; } - if (buffer_size > obj->package.elements[1].buffer.length) { + buffer_size = struct_size(buffer, raw_data, le32_to_cpu(buffer->raw_size)); + if (buffer_size > output.length) { dev_warn(&wdev->dev, - FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n", - buffer_size, obj->package.elements[1].buffer.length); + FW_WARN "Dell WMI buffer size (%zu) exceeds WMI buffer size (%zu)\n", + buffer_size, output.length); ret = -EMSGSIZE; goto err_free; } - *result = obj; + *result = buffer; return 0; err_free: - kfree(obj); + kfree(output.data); return ret; } -static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, - u32 arg, union acpi_object **result) +static ssize_t dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, char *buf, size_t length) { - return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING); + struct wmi_buffer output; + size_t str_size; + int ret; + + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(struct wmi_string)); + if (ret < 0) + return ret; + + struct wmi_string *str __free(kfree) = output.data; + + str_size = sizeof(*str) + le16_to_cpu(str->length); + if (str_size > output.length) { + dev_warn(&wdev->dev, + FW_WARN "WMI string size (%zu) exceeds WMI buffer size (%zu)\n", + str_size, output.length); + return -EMSGSIZE; + } + + return wmi_string_to_utf8s(str, buf, length); } /* @@ -265,28 +262,26 @@ static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_meth static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_method method, struct dell_wmi_ddv_sensors *sensors, size_t entry_size) { + struct dell_wmi_buffer *buffer; u64 buffer_size, rem, entries; - union acpi_object *obj; - u8 *buffer; int ret; - if (sensors->obj) { + if (sensors->buffer) { if (time_before(jiffies, sensors->timestamp + HZ)) return 0; - kfree(sensors->obj); - sensors->obj = NULL; + kfree(sensors->buffer); + sensors->buffer = NULL; } - ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &obj); + ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &buffer); if (ret < 0) return ret; /* buffer format sanity check */ - buffer_size = obj->package.elements[0].integer.value; - buffer = obj->package.elements[1].buffer.pointer; + buffer_size = le32_to_cpu(buffer->raw_size); entries = div64_u64_rem(buffer_size, entry_size, &rem); - if (rem != 1 || buffer[buffer_size - 1] != 0xff) { + if (rem != 1 || buffer->raw_data[buffer_size - 1] != 0xff) { ret = -ENOMSG; goto err_free; } @@ -296,14 +291,14 @@ static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_me goto err_free; } - sensors->obj = obj; + sensors->buffer = buffer; sensors->entries = entries; sensors->timestamp = jiffies; return 0; err_free: - kfree(obj); + kfree(buffer); return ret; } @@ -328,7 +323,7 @@ static int dell_wmi_ddv_fan_read_channel(struct dell_wmi_ddv_data *data, u32 att if (channel >= data->fans.entries) return -ENXIO; - entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer; + entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data; switch (attr) { case hwmon_fan_input: *val = get_unaligned_le16(&entry[channel].rpm); @@ -354,7 +349,7 @@ static int dell_wmi_ddv_temp_read_channel(struct dell_wmi_ddv_data *data, u32 at if (channel >= data->temps.entries) return -ENXIO; - entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer; + entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data; switch (attr) { case hwmon_temp_input: *val = entry[channel].now * 1000; @@ -411,7 +406,7 @@ static int dell_wmi_ddv_fan_read_string(struct dell_wmi_ddv_data *data, int chan if (channel >= data->fans.entries) return -ENXIO; - entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer; + entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data; type = entry[channel].type; switch (type) { case 0x00 ... 0x07: @@ -442,7 +437,7 @@ static int dell_wmi_ddv_temp_read_string(struct dell_wmi_ddv_data *data, int cha if (channel >= data->temps.entries) return -ENXIO; - entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer; + entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data; switch (entry[channel].type) { case 0x00: *str = "CPU"; @@ -553,8 +548,8 @@ static void dell_wmi_ddv_hwmon_cache_invalidate(struct dell_wmi_ddv_sensors *sen return; mutex_lock(&sensors->lock); - kfree(sensors->obj); - sensors->obj = NULL; + kfree(sensors->buffer); + sensors->buffer = NULL; mutex_unlock(&sensors->lock); } @@ -564,7 +559,7 @@ static void dell_wmi_ddv_hwmon_cache_destroy(void *data) sensors->active = false; mutex_destroy(&sensors->lock); - kfree(sensors->obj); + kfree(sensors->buffer); } static struct hwmon_channel_info *dell_wmi_ddv_channel_init(struct wmi_device *wdev, @@ -750,7 +745,7 @@ static void dell_wmi_battery_invalidate(struct dell_wmi_ddv_data *data, static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr); - union acpi_object *obj; + ssize_t count; u32 index; int ret; @@ -758,19 +753,19 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha if (ret < 0) return ret; - ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj); + count = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, buf, + PAGE_SIZE); + if (count < 0) + return count; + + if (count != DELL_EPPID_LENGTH && count != DELL_EPPID_EXT_LENGTH) + dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%zd)\n", count); + + ret = sysfs_emit_at(buf, count, "\n"); if (ret < 0) return ret; - if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH) - dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n", - obj->string.length); - - ret = sysfs_emit(buf, "%s\n", obj->string.pointer); - - kfree(obj); - - return ret; + return count + ret; } static int dell_wmi_ddv_get_health(struct dell_wmi_ddv_data *data, u32 index, @@ -994,19 +989,15 @@ static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method m { struct device *dev = seq->private; struct dell_wmi_ddv_data *data = dev_get_drvdata(dev); - union acpi_object *obj; - u64 size; - u8 *buf; + struct dell_wmi_buffer *buffer; int ret; - ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj); + ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &buffer); if (ret < 0) return ret; - size = obj->package.elements[0].integer.value; - buf = obj->package.elements[1].buffer.pointer; - ret = seq_write(seq, buf, size); - kfree(obj); + ret = seq_write(seq, buffer->raw_data, le32_to_cpu(buffer->raw_size)); + kfree(buffer); return ret; } From ab1eb37dd63ff3555e4f98918f1bd3498522f765 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:50 +0200 Subject: [PATCH 3520/5214] hwmon: (dell-smm) Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for returning the results of a SMM call. Acked-by: Guenter Roeck Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-7-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/hwmon/dell-smm-hwmon.c | 49 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 038edffc1ac7..6ca2ea4bfe7d 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -12,8 +12,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include #include +#include +#include #include #include #include @@ -36,10 +38,10 @@ #include #include #include +#include #include #include -#include #define I8K_SMM_FN_STATUS 0x0025 #define I8K_SMM_POWER_STATUS 0x0069 @@ -232,7 +234,7 @@ static const struct dell_smm_ops i8k_smm_ops = { /* * Call the System Management Mode BIOS over WMI. */ -static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg) +static ssize_t wmi_parse_register(u8 *buffer, size_t length, unsigned int *reg) { __le32 value; u32 reg_size; @@ -253,7 +255,7 @@ static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg) return reg_size + sizeof(reg_size); } -static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) +static int wmi_parse_response(u8 *buffer, size_t length, struct smm_regs *regs) { unsigned int *registers[] = { ®s->eax, @@ -261,7 +263,7 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) ®s->ecx, ®s->edx }; - u32 offset = 0; + size_t offset = 0; ssize_t ret; int i; @@ -273,19 +275,16 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) if (ret < 0) return ret; - offset += ret; + /* WMI aligns u32 integers on a 4 byte boundary */ + offset = ALIGN(offset + ret, 4); } - if (offset != length) - return -ENOMSG; - return 0; } static int wmi_smm_call(struct device *dev, struct smm_regs *regs) { struct wmi_device *wdev = container_of(dev, struct wmi_device, dev); - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; u32 wmi_payload[] = { sizeof(regs->eax), regs->eax, @@ -296,34 +295,20 @@ static int wmi_smm_call(struct device *dev, struct smm_regs *regs) sizeof(regs->edx), regs->edx }; - const struct acpi_buffer in = { + const struct wmi_buffer in = { .length = sizeof(wmi_payload), - .pointer = &wmi_payload, + .data = &wmi_payload, }; - union acpi_object *obj; - acpi_status status; + struct wmi_buffer out; int ret; - status = wmidev_evaluate_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out); - if (ACPI_FAILURE(status)) - return -EIO; + ret = wmidev_invoke_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out, sizeof(__le32)); + if (ret < 0) + return ret; - obj = out.pointer; - if (!obj) - return -ENODATA; + u8 *response __free(kfree) = out.data; - if (obj->type != ACPI_TYPE_BUFFER) { - ret = -ENOMSG; - - goto err_free; - } - - ret = wmi_parse_response(obj->buffer.pointer, obj->buffer.length, regs); - -err_free: - kfree(obj); - - return ret; + return wmi_parse_response(response, out.length, regs); } static int dell_smm_call(const struct dell_smm_ops *ops, struct smm_regs *regs) From b79ad5e8ba5cfda93a83e0cf71d4743829cc9f83 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:51 +0200 Subject: [PATCH 3521/5214] platform/wmi: Make wmi_bus_class const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions class_register()/_unregister() and device_create() both support taking a const pointer to the class struct. Use this to mark wmi_bus_class as const so that it can be placed into read-only memory for better security. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20260610203453.816254-8-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 5a2ffcbab6af..0782ebc33c00 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -1088,7 +1088,7 @@ static void wmi_dev_shutdown(struct device *dev) } } -static struct class wmi_bus_class = { +static const struct class wmi_bus_class = { .name = "wmi_bus", }; From c3cbac4be03d769571f32e7f27241b2c58f722f5 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:52 +0200 Subject: [PATCH 3522/5214] platform/wmi: Make sysfs attributes const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sysfs core supports const attributes. Use this to mark all sysfs attributes as const so that they can be placed into read-only memory for better security. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-9-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 0782ebc33c00..529825dcfbfe 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -858,7 +858,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid); } -static DEVICE_ATTR_RO(modalias); + +static const DEVICE_ATTR_RO(modalias); static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -867,7 +868,8 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid); } -static DEVICE_ATTR_RO(guid); + +static const DEVICE_ATTR_RO(guid); static ssize_t instance_count_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -876,7 +878,8 @@ static ssize_t instance_count_show(struct device *dev, return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count); } -static DEVICE_ATTR_RO(instance_count); + +static const DEVICE_ATTR_RO(instance_count); static ssize_t expensive_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -886,9 +889,10 @@ static ssize_t expensive_show(struct device *dev, return sysfs_emit(buf, "%d\n", (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); } -static DEVICE_ATTR_RO(expensive); -static struct attribute *wmi_attrs[] = { +static const DEVICE_ATTR_RO(expensive); + +static const struct attribute * const wmi_attrs[] = { &dev_attr_modalias.attr, &dev_attr_guid.attr, &dev_attr_instance_count.attr, @@ -904,9 +908,10 @@ static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); } -static DEVICE_ATTR_RO(notify_id); -static struct attribute *wmi_event_attrs[] = { +static const DEVICE_ATTR_RO(notify_id); + +static const struct attribute * const wmi_event_attrs[] = { &dev_attr_notify_id.attr, NULL }; @@ -920,7 +925,8 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0], wblock->gblock.object_id[1]); } -static DEVICE_ATTR_RO(object_id); + +static const DEVICE_ATTR_RO(object_id); static ssize_t setable_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -929,16 +935,17 @@ static ssize_t setable_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%d\n", (int)wdev->setable); } -static DEVICE_ATTR_RO(setable); -static struct attribute *wmi_data_attrs[] = { +static const DEVICE_ATTR_RO(setable); + +static const struct attribute * const wmi_data_attrs[] = { &dev_attr_object_id.attr, &dev_attr_setable.attr, NULL }; ATTRIBUTE_GROUPS(wmi_data); -static struct attribute *wmi_method_attrs[] = { +static const struct attribute * const wmi_method_attrs[] = { &dev_attr_object_id.attr, NULL }; From 3429ae7b2f02a4a6ad40d36ee06641d433d75a1b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:53 +0200 Subject: [PATCH 3523/5214] modpost: Handle malformed WMI GUID strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some WMI GUIDs found inside binary MOF files contain both uppercase and lowercase characters. Blindly copying such GUIDs will prevent the associated WMI driver from loading automatically because the WMI GUID found inside WMI device ids always contains uppercase characters. Avoid this issue by always converting WMI GUID strings to uppercase. Also verify that the WMI GUID string actually looks like a valid GUID. Signed-off-by: Armin Wolf Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20260610203453.816254-10-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/driver-development-guide.rst | 2 +- scripts/mod/file2alias.c | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst index 387f508d57ad..6290c448f5e7 100644 --- a/Documentation/wmi/driver-development-guide.rst +++ b/Documentation/wmi/driver-development-guide.rst @@ -54,7 +54,7 @@ to matching WMI devices using a struct wmi_device_id table: :: static const struct wmi_device_id foo_id_table[] = { - /* Only use uppercase letters! */ + /* Using only uppercase letters is recommended */ { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL }, { } }; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 4e99393a35f1..20e542a888c4 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1253,6 +1253,8 @@ static void do_tee_entry(struct module *mod, void *symval) static void do_wmi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, wmi_device_id, guid_string); + char result[sizeof(*guid_string)]; + int i; if (strlen(*guid_string) != UUID_STRING_LEN) { warn("Invalid WMI device id 'wmi:%s' in '%s'\n", @@ -1260,7 +1262,31 @@ static void do_wmi_entry(struct module *mod, void *symval) return; } - module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string); + for (i = 0; i < UUID_STRING_LEN; i++) { + char value = (*guid_string)[i]; + bool valid = false; + + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (value == '-') + valid = true; + } else { + if (isxdigit(value)) + valid = true; + } + + if (!valid) { + warn("Invalid character %c inside WMI GUID string '%s' in '%s'\n", + value, *guid_string, mod->name); + return; + } + + /* Some GUIDs from BMOF definitions contain lowercase characters */ + result[i] = toupper(value); + } + + result[i] = '\0'; + + module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", result); } /* Looks like: mhi:S */ From 20dcfaf2d3261ef84631114ae37213f5ebd3eb37 Mon Sep 17 00:00:00 2001 From: Nina Schoetterl-Glausch Date: Fri, 12 Jun 2026 14:23:01 +0200 Subject: [PATCH 3524/5214] KVM: s390: Minor refactor of base/ext facility lists Directly use the size of the arrays instead of going through the indirection of kvm_s390_fac_size(). Don't use magic number for the number of entries in the non hypervisor managed facility bit mask list. Make the constraint of that number on kvm_s390_fac_base obvious. Get rid of implicit double anding of stfle_fac_list. Signed-off-by: Nina Schoetterl-Glausch Co-developed-by: Christoph Schlameuss Signed-off-by: Christoph Schlameuss Reviewed-by: Janosch Frank Reviewed-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260612-vsie-alter-stfle-fac-v4-1-74f0e1559929@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 44 +++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index d503e4d0072a..7334c160d019 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -237,33 +237,25 @@ static int async_destroy = 1; module_param(async_destroy, int, 0444); MODULE_PARM_DESC(async_destroy, "Asynchronous destroy for protected guests"); -/* - * For now we handle at most 16 double words as this is what the s390 base - * kernel handles and stores in the prefix page. If we ever need to go beyond - * this, this requires changes to code, but the external uapi can stay. - */ -#define SIZE_INTERNAL 16 - +#define HMFAI_DWORDS 16 /* * Base feature mask that defines default mask for facilities. Consists of the * defines in FACILITIES_KVM and the non-hypervisor managed bits. */ -static unsigned long kvm_s390_fac_base[SIZE_INTERNAL] = { FACILITIES_KVM }; +static unsigned long kvm_s390_fac_base[HMFAI_DWORDS] = { FACILITIES_KVM }; +static_assert(ARRAY_SIZE(((long[]){ FACILITIES_KVM })) <= HMFAI_DWORDS); +static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= S390_ARCH_FAC_MASK_SIZE_U64); +static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= S390_ARCH_FAC_LIST_SIZE_U64); +static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= ARRAY_SIZE(stfle_fac_list)); + /* * Extended feature mask. Consists of the defines in FACILITIES_KVM_CPUMODEL * and defines the facilities that can be enabled via a cpu model. */ -static unsigned long kvm_s390_fac_ext[SIZE_INTERNAL] = { FACILITIES_KVM_CPUMODEL }; - -static unsigned long kvm_s390_fac_size(void) -{ - BUILD_BUG_ON(SIZE_INTERNAL > S390_ARCH_FAC_MASK_SIZE_U64); - BUILD_BUG_ON(SIZE_INTERNAL > S390_ARCH_FAC_LIST_SIZE_U64); - BUILD_BUG_ON(SIZE_INTERNAL * sizeof(unsigned long) > - sizeof(stfle_fac_list)); - - return SIZE_INTERNAL; -} +static const unsigned long kvm_s390_fac_ext[] = { FACILITIES_KVM_CPUMODEL }; +static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= S390_ARCH_FAC_MASK_SIZE_U64); +static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= S390_ARCH_FAC_LIST_SIZE_U64); +static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= ARRAY_SIZE(stfle_fac_list)); /* available cpu features supported by kvm */ static DECLARE_BITMAP(kvm_s390_available_cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS); @@ -3249,13 +3241,16 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.sie_page2->kvm = kvm; kvm->arch.model.fac_list = kvm->arch.sie_page2->fac_list; - for (i = 0; i < kvm_s390_fac_size(); i++) { + for (i = 0; i < ARRAY_SIZE(kvm_s390_fac_base); i++) { kvm->arch.model.fac_mask[i] = stfle_fac_list[i] & - (kvm_s390_fac_base[i] | - kvm_s390_fac_ext[i]); + kvm_s390_fac_base[i]; kvm->arch.model.fac_list[i] = stfle_fac_list[i] & kvm_s390_fac_base[i]; } + for (i = 0; i < ARRAY_SIZE(kvm_s390_fac_ext); i++) { + kvm->arch.model.fac_mask[i] |= stfle_fac_list[i] & + kvm_s390_fac_ext[i]; + } kvm->arch.model.subfuncs = kvm_s390_available_subfunc; /* we are always in czam mode - even on pre z14 machines */ @@ -5878,9 +5873,8 @@ static int __init kvm_s390_init(void) pr_info("Disabling 2G hugepage support, since 1M hugepage support is not enabled.\n"); } - for (i = 0; i < 16; i++) - kvm_s390_fac_base[i] |= - stfle_fac_list[i] & nonhyp_mask(i); + for (i = 0; i < HMFAI_DWORDS; i++) + kvm_s390_fac_base[i] |= nonhyp_mask(i); r = __kvm_s390_init(); if (r) From a8ceca7d8c00479a650bc5fb1372d40dd5e535a2 Mon Sep 17 00:00:00 2001 From: Nina Schoetterl-Glausch Date: Fri, 12 Jun 2026 14:23:02 +0200 Subject: [PATCH 3525/5214] s390/sclp: Detect ASTFLEIE 2 facility Detect alternate STFLE interpretive execution facility 2. Signed-off-by: Nina Schoetterl-Glausch Signed-off-by: Christoph Schlameuss Reviewed-by: Janosch Frank Reviewed-by: Hendrik Brueckner Reviewed-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260612-vsie-alter-stfle-fac-v4-2-74f0e1559929@linux.ibm.com> --- arch/s390/include/asm/sclp.h | 1 + drivers/s390/char/sclp_early.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 0f184dbdbe5e..0f21501d3e86 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -104,6 +104,7 @@ struct sclp_info { unsigned char has_aisii : 1; unsigned char has_aeni : 1; unsigned char has_aisi : 1; + unsigned char has_astfleie2 : 1; unsigned int ibc; unsigned int mtid; unsigned int mtid_cp; diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 6bf501ad8ff0..22dd797e6229 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -61,8 +61,10 @@ static void __init sclp_early_facilities_detect(void) sclp.has_sipl = !!(sccb->cbl & 0x4000); sclp.has_sipl_eckd = !!(sccb->cbl & 0x2000); } - if (sccb->cpuoff > 139) + if (sccb->cpuoff > 139) { sclp.has_diag324 = !!(sccb->byte_139 & 0x80); + sclp.has_astfleie2 = !!(sccb->byte_139 & 0x40); + } sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; sclp.rzm <<= 20; From bf8f3cec939db68e6dc32705327acfabdbcf9e69 Mon Sep 17 00:00:00 2001 From: Nina Schoetterl-Glausch Date: Fri, 12 Jun 2026 14:23:03 +0200 Subject: [PATCH 3526/5214] KVM: s390: vsie: Refactor handle_stfle Use switch case in anticipation of handling format-1 and format-2 facility list designations in the future. As the alternate STFLE facilities are not enabled, only case 0 is possible. No functional change intended. Signed-off-by: Nina Schoetterl-Glausch Co-developed-by: Christoph Schlameuss Signed-off-by: Christoph Schlameuss Reviewed-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260612-vsie-alter-stfle-fac-v4-3-74f0e1559929@linux.ibm.com> --- arch/s390/include/uapi/asm/kvm.h | 1 + arch/s390/kvm/vsie.c | 53 ++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index 60345dd2cba2..4192769b5ce0 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h @@ -444,6 +444,7 @@ struct kvm_s390_vm_cpu_machine { #define KVM_S390_VM_CPU_FEAT_PFMFI 11 #define KVM_S390_VM_CPU_FEAT_SIGPIF 12 #define KVM_S390_VM_CPU_FEAT_KSS 13 +#define KVM_S390_VM_CPU_FEAT_ASTFLEIE2 14 struct kvm_s390_vm_cpu_feat { __u64 feat[16]; }; diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index e5a23f1c9749..c7dcdd460dd1 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -6,12 +6,15 @@ * * Author(s): David Hildenbrand */ +#include #include #include #include +#include #include #include #include +#include #include #include @@ -1000,6 +1003,23 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page) clear_vsie_icpt(vsie_page); } +static int handle_stfle_0(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, + u32 fac_list_origin) +{ + struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; + + /* + * format-0 -> size of nested guest's facility list == guest's size + * guest's size == host's size, since STFLE is interpretatively executed + * using a format-0 for the guest, too. + */ + if (read_guest_real(vcpu, fac_list_origin, &vsie_page->fac, + stfle_size() * sizeof(u64))) + return set_validity_icpt(scb_s, 0x1090U); + scb_s->fac = (u32)virt_to_phys(&vsie_page->fac); + return 0; +} + /* * Try to shadow + enable the guest 2 provided facility list. * Retry instruction execution if enabled for and provided by guest 2. @@ -1009,29 +1029,30 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page) */ static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { - struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; - __u32 fac = READ_ONCE(vsie_page->scb_o->fac); + bool has_astfleie2 = test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ASTFLEIE2); + u32 fac = READ_ONCE(vsie_page->scb_o->fac); + int format_mask, format; + u32 origin; + + BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct vsie_page, fac), 8)); - /* - * Alternate-STFLE-Interpretive-Execution facilities are not supported - * -> format-0 flcb - */ if (fac && test_kvm_facility(vcpu->kvm, 7)) { retry_vsie_icpt(vsie_page); /* * The facility list origin (FLO) is in bits 1 - 28 of the FLD * so we need to mask here before reading. */ - fac = fac & 0x7ffffff8U; - /* - * format-0 -> size of nested guest's facility list == guest's size - * guest's size == host's size, since STFLE is interpretatively executed - * using a format-0 for the guest, too. - */ - if (read_guest_real(vcpu, fac, &vsie_page->fac, - stfle_size() * sizeof(u64))) - return set_validity_icpt(scb_s, 0x1090U); - scb_s->fac = (u32)virt_to_phys(&vsie_page->fac); + origin = fac & 0x7ffffff8U; + format_mask = has_astfleie2 ? 3 : 0; + format = fac & format_mask; + switch (format) { + case 0: + return handle_stfle_0(vcpu, vsie_page, origin); + case 1: + case 2: + case 3: + unreachable(); + } } return 0; } From 0e9704f1bca006ea09cab4a73dbcbeec8c77573e Mon Sep 17 00:00:00 2001 From: Nina Schoetterl-Glausch Date: Fri, 12 Jun 2026 14:23:04 +0200 Subject: [PATCH 3527/5214] KVM: s390: vsie: Implement ASTFLEIE facility 2 Implement shadowing of format-2 facility list when running in VSIE. ASTFLEIE2 is available since IBM z16. To function G1 has to run this KVM code and G1 and G2 have to run QEMU with ASTFLEIE2 support. Signed-off-by: Nina Schoetterl-Glausch Co-developed-by: Christoph Schlameuss Signed-off-by: Christoph Schlameuss Reviewed-by: Claudio Imbrenda [imbrenda@linux.ibm.com: Fix typo in comment] Signed-off-by: Claudio Imbrenda Message-ID: <20260612-vsie-alter-stfle-fac-v4-4-74f0e1559929@linux.ibm.com> --- arch/s390/include/asm/kvm_host.h | 12 +++++++++++ arch/s390/kvm/kvm-s390.c | 2 ++ arch/s390/kvm/vsie.c | 34 ++++++++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 8a4f4a39f7a2..aa4c4685f95c 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -504,6 +504,18 @@ struct kvm_s390_cpu_model { struct kvm_s390_vm_cpu_uv_feat uv_feat_guest; }; +#define S390_ARCH_FAC_FORMAT_2 2 +struct kvm_s390_flcb2 { + union { + struct { + u8 reserved0[7]; + u8 length; + }; + u64 header_val; + }; + u64 facilities[S390_ARCH_FAC_LIST_SIZE_U64]; +}; + typedef int (*crypto_hook)(struct kvm_vcpu *vcpu); struct kvm_s390_crypto { diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 7334c160d019..de28ee1f7882 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -465,6 +465,8 @@ static void __init kvm_s390_cpu_feat_init(void) allow_cpu_feat(KVM_S390_VM_CPU_FEAT_IBS); if (sclp.has_kss) allow_cpu_feat(KVM_S390_VM_CPU_FEAT_KSS); + if (sclp.has_astfleie2) + allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ASTFLEIE2); /* * KVM_S390_VM_CPU_FEAT_SKEY: Wrong shadow of PTE.I bits will make * all skey handling functions read/set the skey from the PGSTE diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index c7dcdd460dd1..eea24562e7db 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -65,9 +65,9 @@ struct vsie_page { gpa_t scb_gpa; /* 0x0258 */ /* the shadow gmap in use by the vsie_page */ struct gmap_cache gmap_cache; /* 0x0260 */ - __u8 reserved[0x0700 - 0x0278]; /* 0x0278 */ - struct kvm_s390_crypto_cb crycb; /* 0x0700 */ - __u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE]; /* 0x0800 */ + __u8 reserved[0x06f8 - 0x0278]; /* 0x0278 */ + struct kvm_s390_crypto_cb crycb; /* 0x06f8 */ + __u8 fac[8 + S390_ARCH_FAC_LIST_SIZE_BYTE];/* 0x07f8 */ }; static_assert(sizeof(struct vsie_page) == PAGE_SIZE); @@ -1020,6 +1020,28 @@ static int handle_stfle_0(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, return 0; } +static int handle_stfle_2(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, u32 fac_list_origin) +{ + struct kvm_s390_flcb2 *flcb_s = (struct kvm_s390_flcb2 *)vsie_page->fac; + struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; + u64 len; + + if (read_guest_real(vcpu, fac_list_origin, &len, sizeof(len))) + return set_validity_icpt(scb_s, 0x1090U); + + /* discard reserved bits */ + len = (len & U8_MAX); + flcb_s->header_val = len; + len += 1; + + if (read_guest_real(vcpu, fac_list_origin + offsetof(struct kvm_s390_flcb2, facilities), + &flcb_s->facilities, len * sizeof(u64))) + return set_validity_icpt(scb_s, 0x1090U); + + scb_s->fac = (u32)virt_to_phys(&vsie_page->fac) | S390_ARCH_FAC_FORMAT_2; + return 0; +} + /* * Try to shadow + enable the guest 2 provided facility list. * Retry instruction execution if enabled for and provided by guest 2. @@ -1034,6 +1056,8 @@ static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) int format_mask, format; u32 origin; + /* assert no overflow with maximum len */ + BUILD_BUG_ON(sizeof(vsie_page->fac) < ((S390_ARCH_FAC_LIST_SIZE_U64 + 1) * sizeof(u64))); BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct vsie_page, fac), 8)); if (fac && test_kvm_facility(vcpu->kvm, 7)) { @@ -1049,9 +1073,11 @@ static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) case 0: return handle_stfle_0(vcpu, vsie_page, origin); case 1: + return set_validity_icpt(&vsie_page->scb_s, 0x1330U); case 2: + return handle_stfle_2(vcpu, vsie_page, origin); case 3: - unreachable(); + return set_validity_icpt(&vsie_page->scb_s, 0x1330U); } } return 0; From 661c092f983975842da8fa6281e4a1a70f357699 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 4 Jun 2026 21:07:59 -0700 Subject: [PATCH 3528/5214] cxl: Align interleave decode/encode helpers with their callers The interleave conversion helpers translate between encoded HDM interleave values and the granularity and way values used by the driver. These helpers have been a recurring source of static analysis complaints that expose type mismatches and potentially uninitialized outputs. Fix those issues in the helpers so callers inherit the consistent behavior automatically. The decode and encode helpers have different interface issues. The decode helpers return values through unsigned int pointers, but the decoded values are ultimately represented as int throughout the driver. Align the helper interfaces with their callers by changing the out-parameters to int * and updating the handful of affected locals to match. The encode helpers leave their out-parameters unchanged on error. That means callers that ignore the return value may observe uninitialized encoded values. Initialize the outputs so failed conversions leave defined values. This issue was originally reported by Purva and the helper-side fix was suggested by Dan [1]. Tidy up a related, pre-existing, printk format specifier mismatch in cxl_validate_translation_params(). No functional change for valid interleave parameters. [1] https://lore.kernel.org/linux-cxl/20250419203530.45594-1-purvayeshi550@gmail.com/ Reported-by: Purva Yeshi Suggested-by: Dan Williams Signed-off-by: Alison Schofield Reviewed-by: Li Ming Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260605040801.865965-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/acpi.c | 10 +++++----- drivers/cxl/core/region.c | 4 ++-- drivers/cxl/cxl.h | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 127537628817..3b818adbd38b 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -101,8 +101,8 @@ static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg, struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct device *dev = ctx->dev; struct cxl_cxims_data *cximsd; - unsigned int hbig, nr_maps; - int rc; + unsigned int nr_maps; + int hbig, rc; rc = eig_to_granularity(cxims->hbig, &hbig); if (rc) @@ -160,7 +160,7 @@ static int cxl_acpi_cfmws_verify(struct device *dev, struct acpi_cedt_cfmws *cfmws) { int rc, expected_len; - unsigned int ways; + int ways; if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO && cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { @@ -405,7 +405,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, struct cxl_cxims_context cxims_ctx; struct device *dev = ctx->dev; struct cxl_decoder *cxld; - unsigned int ways, i, ig; + int ways, i, ig; int rc; rc = cxl_acpi_cfmws_verify(dev, cfmws); @@ -464,7 +464,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, if (rc < 0) return rc; if (!cxlrd->platform_data) { - dev_err(dev, "No CXIMS for HBIG %u\n", ig); + dev_err(dev, "No CXIMS for HBIG %d\n", ig); return -EINVAL; } } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index f5cd20f48d2b..1d4d3b005178 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -3058,7 +3058,7 @@ int cxl_validate_translation_params(u8 eiw, u16 eig, int pos) return -EINVAL; } if (pos < 0 || pos >= ways) { - pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos, + pr_debug("%s: invalid pos=%d for ways=%d\n", __func__, pos, ways); return -EINVAL; } @@ -3104,7 +3104,7 @@ EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate"); int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig) { - unsigned int ways = 0; + int ways = 0; u64 shifted, rem; int pos, ret; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 21fc89d3aeea..4a884821ff7c 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -91,7 +91,7 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr) } /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ -static inline int eig_to_granularity(u16 eig, unsigned int *granularity) +static inline int eig_to_granularity(u16 eig, int *granularity) { if (eig > CXL_DECODER_MAX_ENCODED_IG) return -EINVAL; @@ -100,7 +100,7 @@ static inline int eig_to_granularity(u16 eig, unsigned int *granularity) } /* Encode defined in CXL ECN "3, 6, 12 and 16-way memory Interleaving" */ -static inline int eiw_to_ways(u8 eiw, unsigned int *ways) +static inline int eiw_to_ways(u8 eiw, int *ways) { switch (eiw) { case 0 ... 4: @@ -118,6 +118,7 @@ static inline int eiw_to_ways(u8 eiw, unsigned int *ways) static inline int granularity_to_eig(int granularity, u16 *eig) { + *eig = 0; if (granularity > SZ_16K || granularity < CXL_DECODER_MIN_GRANULARITY || !is_power_of_2(granularity)) return -EINVAL; @@ -127,6 +128,7 @@ static inline int granularity_to_eig(int granularity, u16 *eig) static inline int ways_to_eiw(unsigned int ways, u8 *eiw) { + *eiw = 0; if (ways > 16) return -EINVAL; if (is_power_of_2(ways)) { From 769f0b350c81ab147fff37b92637e12190f1be29 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 12 Jun 2026 09:12:27 +0800 Subject: [PATCH 3529/5214] tools/testing/cxl: Resolve auto-region decoder targets like real HW The mock auto-region created at module load wrote switch and host-bridge decoder target[] directly, in addition to target_map[]. Real HW programs only target_map[] and resolves target[] as dports enumerate, via update_decoder_targets(). Region replay already follows that ordering, the initial auto-region did not. Drop the direct target[] writes and call cxl_port_update_decoder_targets() so target[] is resolved the same way as real HW and region replay, exercising more of the auto-region driver path. This is inspired by the discussion [1] below: [1]: https://lore.kernel.org/all/20260521084806.28232-1-icheng@nvidia.com/ Suggested-by: Alison Schofield Signed-off-by: Richard Cheng Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Tested-by: Dave Jiang Link: https://patch.msgid.link/20260612011227.4220-1-icheng@nvidia.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 9a0faf70e9b1..ef92dd35e030 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1188,15 +1188,11 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxlsd = to_cxl_switch_decoder(dev); if (i == 0) { /* put cxl_mem.4 second in the decode order */ - if (pdev->id == 4) { - cxlsd->target[1] = dport; + if (pdev->id == 4) cxlsd->cxld.target_map[1] = dport->port_id; - } else { - cxlsd->target[0] = dport; + else cxlsd->cxld.target_map[0] = dport->port_id; - } } else { - cxlsd->target[0] = dport; cxlsd->cxld.target_map[0] = dport->port_id; } cxld = &cxlsd->cxld; @@ -1219,6 +1215,16 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + /* + * Only target_map[] is programmed above, mimicking + * firmware. On real hardware target[] is populated as + * dports enumerate, via update_decoder_targets(). The + * mock's dports are already bound by now, so fire that + * resolution explicitly here rather than stamping + * target[] directly. + */ + cxl_port_update_decoder_targets(iter, dport); + cxld_registry_update(cxld); put_device(dev); } From cbda6a2c2bec2a5fb30a2ce85baeab15b5fc7db3 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:00 +0800 Subject: [PATCH 3530/5214] cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach() In cxl_cancel_auto_attach(), it assumes cxled->pos is a valid index for accessing p->targets[]. However, cxled->pos can be set to negative errno in cxl_region_sort_targets() if cxl_calc_interleave_pos() fails. This causes the driver to use a negative index to access p->targets[], resulting in out-of-bounds access. Fix it by walking p->targets[] instead of using cxled->pos directly. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-1-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 1d4d3b005178..690ae991a80f 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2009,8 +2009,9 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr) cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); /* * Record that sorting failed, but still continue to calc - * cxled->pos so that follow-on code paths can reliably - * do p->targets[cxled->pos] to self-reference their entry. + * cxled->pos so that cxl_calc_interleave_pos() emits its + * dev_dbg() for every member. which is useful for auto + * discovery debug. */ if (cxled->pos < 0) rc = -ENXIO; @@ -2200,18 +2201,30 @@ static int cxl_region_attach(struct cxl_region *cxlr, return 0; } -static int cxl_region_by_target(struct device *dev, const void *data) +static int cxl_region_remove_target(struct device *dev, void *data) { - const struct cxl_endpoint_decoder *cxled = data; + struct cxl_endpoint_decoder *cxled = data; struct cxl_region_params *p; struct cxl_region *cxlr; + int i; if (!is_cxl_region(dev)) return 0; cxlr = to_cxl_region(dev); p = &cxlr->params; - return p->targets[cxled->pos] == cxled; + for (i = 0; i < p->interleave_ways; i++) { + if (p->targets[i] == cxled) { + p->nr_targets--; + cxled->state = CXL_DECODER_STATE_AUTO; + cxled->pos = -1; + p->targets[i] = NULL; + + return 1; + } + } + + return 0; } /* @@ -2220,25 +2233,10 @@ static int cxl_region_by_target(struct device *dev, const void *data) */ static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) { - struct cxl_region_params *p; - struct cxl_region *cxlr; - int pos = cxled->pos; - if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) return; - struct device *dev __free(put_device) = - bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); - if (!dev) - return; - - cxlr = to_cxl_region(dev); - p = &cxlr->params; - - p->nr_targets--; - cxled->state = CXL_DECODER_STATE_AUTO; - cxled->pos = -1; - p->targets[pos] = NULL; + bus_for_each_dev(&cxl_bus_type, NULL, cxled, cxl_region_remove_target); } static struct cxl_region * From aa8a76711c15041ec1e42c3a74c15c2df0bd31f6 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:01 +0800 Subject: [PATCH 3531/5214] cxl/region: Fill first free targets[] slot during auto-discovery Any invalid endpoint decoder pointer in the target array of an active region is not allowed by cxl driver. This means cxl driver always assumes the first p->nr_targets entries of the target array in an auto-assembly region are valid. However, there are scenarios that could leave NULL endpoint decoder pointer holes in the target array. 1. When cxl_cancel_auto_attach() removes an endpoint decoder from a target array, the target slot is set to NULL. If the removed endpoint decoder is not the last element in the target array, the target array will contain a NULL hole. 2. When a auto-assembly region removes an assigned endpoint decoder, if the removed endpoint decoder is not the last element in the target array, always remains a NULL hole in the target array. When a NULL pointer hole exists in a region's target array, it introduces two potential problems: 1. Access an endpoint decoder via a NULL pointer. it always trigger calltrace like that. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000008: 0000 [#1] SMP KASAN PTI RIP: 0010:cxl_calc_interleave_pos+0x26/0x810 [cxl_core] Call Trace: cxl_region_attach+0xc50/0x2140 [cxl_core] cxl_add_to_region+0x321/0x2330 [cxl_core] discover_region+0x92/0x150 [cxl_port] device_for_each_child+0xf3/0x170 cxl_port_probe+0x150/0x200 [cxl_port] cxl_bus_probe+0x4f/0xa0 [cxl_core] really_probe+0x1c8/0x960 __driver_probe_device+0x323/0x450 driver_probe_device+0x45/0x120 __device_attach_driver+0x15d/0x280 bus_for_each_drv+0x10f/0x190 2. Not having enough valid endpoint decoders attached to an auto-assembly region. if an auto-assembly region is created with lock flag or assigned endpoint decoder with lock flag, which means assigned endpoint decoder will not be reset during detaching, they could re-attach to the auto-assembly region again. But cxl region driver relies on p->nr_targets to verify whether the required number of endpoint decoders has been attached, and NULL endpoint decoder pointers are still counted in that case. To fix above issues, adjust cxl_region_attach_auto() logic to find the first free target slot for endpoint decoder attachment, this ensures NULL holes in the target array are filled, rather than adding new endpoint decoders at the tail of the target array. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Fixes: 2230c4bdc412 ("cxl: Add handling of locked CXL decoder") Suggested-by: Alison Schofield Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-2-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 690ae991a80f..66c328d1c14e 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1846,8 +1846,21 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr, * this means that userspace can view devices in the wrong position * before the region activates, and must be careful to understand when * it might be racing region autodiscovery. + * + * The endpoint decoder will be recorded into the first free slot of + * the target array. */ - pos = p->nr_targets; + for (pos = 0; pos < p->interleave_ways; pos++) { + if (!p->targets[pos]) + break; + } + + if (pos == p->interleave_ways) { + dev_err(&cxlr->dev, "%s: unable to find a free target slot\n", + dev_name(&cxled->cxld.dev)); + return -ENXIO; + } + p->targets[pos] = cxled; cxled->pos = pos; cxled->state = CXL_DECODER_STATE_AUTO_STAGED; From 1c2b66a7d7257d2652aa41f9a860ecb96dde27dd Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Sun, 7 Jun 2026 18:18:44 +0800 Subject: [PATCH 3532/5214] usb: ucsi: huawei_gaokun: support mode switching The USB PHY (QMP Combo PHY) is always initialized in USB3+DP mode. In the past, there was no MUX, and it was unnecessary to set it, since MSM only supported 2-lane DP. But now, MST and 4-lane DP support has been added to MSM, and a MUX has been added to the PHY. To support 4-lane DP and mode switching for gaokun, get the MUX and set it. Signed-off-by: Pengyu Luo Acked-by: Heikki Krogerus Link: https://patch.msgid.link/20260607101844.820064-1-mitltlatltl@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 53 ++++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index ca749fde49bd..ad669d2f8b9c 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "ucsi.h" @@ -82,6 +83,8 @@ struct gaokun_ucsi_port { struct gaokun_ucsi *ucsi; struct auxiliary_device *bridge; + struct typec_mux *typec_mux; + int idx; enum gaokun_ucsi_ccx ccx; enum gaokun_ucsi_mux mux; @@ -226,19 +229,18 @@ static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port, port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi); port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi); - /* Mode and SVID are unused; keeping them to make things clearer */ switch (port->mode) { case USBC_DPAM_PAN_C: case USBC_DPAM_PAN_C_REVERSE: - port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */ + port->mode = TYPEC_DP_STATE_C; /* correct it for usb later */ break; case USBC_DPAM_PAN_D: case USBC_DPAM_PAN_D_REVERSE: - port->mode = DP_PIN_ASSIGN_D; + port->mode = TYPEC_DP_STATE_D; break; case USBC_DPAM_PAN_E: case USBC_DPAM_PAN_E_REVERSE: - port->mode = DP_PIN_ASSIGN_E; + port->mode = TYPEC_DP_STATE_E; break; case USBC_DPAM_PAN_NONE: port->mode = TYPEC_STATE_SAFE; @@ -287,18 +289,32 @@ static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec) return idx; } -static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port) +static void gaokun_ucsi_handle_usb_mode(struct gaokun_ucsi_port *port) { struct gaokun_ucsi *uec = port->ucsi; - int idx = port->idx; + struct typec_mux_state state = {}; + struct typec_altmode dp_alt = {}; + int idx = port->idx, ret; - if (idx >= uec->ucsi->cap.num_connectors) { + /* + * For every typec port on this platform, the only mode-switch is + * controlled by its qmp combo phy which consumes svid and mode only. + */ + dp_alt.svid = port->svid; + state.mode = port->mode; + state.alt = &dp_alt; + + if (idx >= uec->num_ports) { dev_warn(uec->dev, "altmode port out of range: %d\n", idx); return; } + ret = typec_mux_set(port->typec_mux, &state); + if (ret) + dev_err(uec->dev, "failed to set mux %d\n", ret); + /* UCSI callback .connector_status() have set orientation */ - if (port->bridge) + if (port->bridge && port->svid == USB_TYPEC_DP_SID) drm_aux_hpd_bridge_notify(&port->bridge->dev, port->hpd_state ? connector_status_connected : @@ -307,7 +323,7 @@ static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port) gaokun_ec_ucsi_pan_ack(uec->ec, port->idx); } -static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec) +static void gaokun_ucsi_usb_notify_ind(struct gaokun_ucsi *uec) { int idx; @@ -320,7 +336,7 @@ static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec) if (idx == GAOKUN_UCSI_NO_PORT_UPDATE) gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */ else - gaokun_ucsi_handle_altmode(&uec->ports[idx]); + gaokun_ucsi_handle_usb_mode(&uec->ports[idx]); } /* @@ -352,7 +368,7 @@ static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx) port = &uec->ports[idx]; if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) { dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT"); - gaokun_ucsi_altmode_notify_ind(uec); + gaokun_ucsi_usb_notify_ind(uec); } } @@ -366,7 +382,7 @@ static int gaokun_ucsi_notify(struct notifier_block *nb, switch (action) { case EC_EVENT_USB: gaokun_ucsi_complete_usb_ack(uec); - gaokun_ucsi_altmode_notify_ind(uec); + gaokun_ucsi_usb_notify_ind(uec); return NOTIFY_OK; case EC_EVENT_UCSI: @@ -429,8 +445,15 @@ static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec) fwnode_handle_put(fwnode); return PTR_ERR(ucsi_port->bridge); } - } + ucsi_port->typec_mux = fwnode_typec_mux_get(fwnode); + if (IS_ERR(ucsi_port->typec_mux)) { + fwnode_handle_put(fwnode); + return dev_err_probe(dev, PTR_ERR(ucsi_port->typec_mux), + "failed to acquire mode-switch for port: %d\n", + port); + } + } for (i = 0; i < num_ports; i++) { if (!uec->ports[i].bridge) continue; @@ -502,10 +525,14 @@ static int gaokun_ucsi_probe(struct auxiliary_device *adev, static void gaokun_ucsi_remove(struct auxiliary_device *adev) { struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); + int i; disable_delayed_work_sync(&uec->work); gaokun_ec_unregister_notify(uec->ec, &uec->nb); ucsi_unregister(uec->ucsi); + for (i = 0; i < uec->num_ports; ++i) + typec_mux_put(uec->ports[i].typec_mux); + ucsi_destroy(uec->ucsi); } From 4a0dcc6a15f94de3dee90bf52234d330cc3aad4e Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 12 Jun 2026 11:00:38 -0400 Subject: [PATCH 3533/5214] KVM: selftests: access_tracking_perf_test: bump number of NUMA nodes to 32 It's rare to find a system that has more than 4 sockets, but a system can have more than 4 NUMA nodes if each socket exposes its chiplets as separate NUMA nodes. In particular, our CI caught a failure in this test on a system with two sockets, each containing an 'AMD EPYC 7601 32-Core Processor'. Bump the limit to 32, just in case. Signed-off-by: Maxim Levitsky Message-ID: <20260612150038.1277394-1-mlevitsk@redhat.com> Signed-off-by: Paolo Bonzini --- tools/testing/selftests/kvm/include/lru_gen_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/lru_gen_util.h b/tools/testing/selftests/kvm/include/lru_gen_util.h index d32ff5d8ffd0..49c8139d398c 100644 --- a/tools/testing/selftests/kvm/include/lru_gen_util.h +++ b/tools/testing/selftests/kvm/include/lru_gen_util.h @@ -14,7 +14,7 @@ #include "test_util.h" #define MAX_NR_GENS 16 /* MAX_NR_GENS in include/linux/mmzone.h */ -#define MAX_NR_NODES 4 /* Maximum number of nodes supported by the test */ +#define MAX_NR_NODES 32 /* Maximum number of nodes supported by the test */ #define LRU_GEN_DEBUGFS "/sys/kernel/debug/lru_gen" #define LRU_GEN_ENABLED_PATH "/sys/kernel/mm/lru_gen/enabled" From d91feb88692e81b00cd22f0125cfcd04970b4a0b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:54 -0700 Subject: [PATCH 3534/5214] cxl/region: Block region delete during region creation Expand the range lock, rename it "regions_lock", to disable region deletion in the critical period between construct_region() and attach_target(), as well as the period between device_add() and registering the remove actions. Otherwise, userspace can confuse the kernel. It can violate the assumption the region stays registered through the completion of cxl_add_to_region(). It can violate the assumption that devm_add_action_or_reset() is working with a live 'struct cxl_region'. It is ok for the region to disappear outside of those windows as that mirrors device hotplug flows where the proper locks are held. Fixes: a32320b71f08 ("cxl/region: Add region autodiscovery") Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-2-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/port.c | 2 +- drivers/cxl/core/region.c | 12 ++++++++++-- drivers/cxl/cxl.h | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index c5aacd7054f1..6e7a70d51cfe 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -2016,7 +2016,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, return ERR_PTR(rc); } - mutex_init(&cxlrd->range_lock); + mutex_init(&cxlrd->regions_lock); cxld = &cxlsd->cxld; cxld->dev.type = &cxl_decoder_root_type; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e50dc716d4e8..b5601e89e302 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2779,6 +2779,10 @@ static ssize_t create_region_store(struct device *dev, const char *buf, if (rc != 1) return -EINVAL; + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; + cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); if (IS_ERR(cxlr)) return PTR_ERR(cxlr); @@ -2838,6 +2842,11 @@ static ssize_t delete_region_store(struct device *dev, struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_region *cxlr; + int rc; + + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; cxlr = cxl_find_region_by_name(cxlrd, buf); if (IS_ERR(cxlr)) @@ -3776,12 +3785,11 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) * for the HPA range, one does the construction and the others * add to that. */ - mutex_lock(&cxlrd->range_lock); + guard(mutex)(&cxlrd->regions_lock); struct cxl_region *cxlr __free(put_cxl_region) = cxl_find_region_by_range(cxlrd, &ctx.hpa_range); if (!cxlr) cxlr = construct_region(cxlrd, &ctx); - mutex_unlock(&cxlrd->range_lock); rc = PTR_ERR_OR_ZERO(cxlr); if (rc) diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 1297594beaec..3900a0778571 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -359,7 +359,7 @@ struct cxl_rd_ops { * @cache_size: extended linear cache size if exists, otherwise zero. * @region_id: region id for next region provisioning event * @platform_data: platform specific configuration data - * @range_lock: sync region autodiscovery by address range + * @regions_lock: sync region discovery, construction, and deletion * @qos_class: QoS performance class cookie * @ops: CXL root decoder operations * @cxlsd: base cxl switch decoder @@ -369,7 +369,7 @@ struct cxl_root_decoder { resource_size_t cache_size; atomic_t region_id; void *platform_data; - struct mutex range_lock; + struct mutex regions_lock; int qos_class; struct cxl_rd_ops ops; struct cxl_switch_decoder cxlsd; From 4dd86ca99ffcc413cbf79063fd9956ef54e0ca91 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:55 -0700 Subject: [PATCH 3535/5214] cxl/region: Resolve region deletion races Sungwoo noticed that the sysfs trigger to delete a region may try to delete a region multiple times. It also has no exclusion relative to the kernel releasing the region via CXL root device teardown. Instead of installing new cxl root devres actions per region, use the existing root decoder unregistration event to remove all remaining regions. An xarray of regions replaces a devres list of regions. This handles 3 separate issues with the old approach: 1/ sysfs users racing to delete the same region: no longer possible now that the regions_lock is held over the lookup and deletion. 2/ multiple actions triggering deletion of the same region: solved by erasing regions while holding @regions_lock, and only proceeding on successful erasure. 3/ userspace racing devres_release_all() to trigger the devres not found warning: solved by sysfs unregistration not requiring a release action Fixes: 779dd20cfb56 ("cxl/region: Add region creation support") Reported-by: Sungwoo Kim Closes: http://lore.kernel.org/20260427032010.916681-2-iam@sung-woo.kim Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-3-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/core.h | 2 ++ drivers/cxl/core/port.c | 5 ++++ drivers/cxl/core/region.c | 60 +++++++++++++++++++++------------------ drivers/cxl/cxl.h | 4 +++ 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 82ca3a476708..07555ae63859 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -52,6 +52,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa); int devm_cxl_add_dax_region(struct cxl_region *cxlr); int devm_cxl_add_pmem_region(struct cxl_region *cxlr); +void kill_regions(struct cxl_root_decoder *cxlrd); #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, @@ -81,6 +82,7 @@ static inline int cxl_region_init(void) static inline void cxl_region_exit(void) { } +static inline void kill_regions(struct cxl_root_decoder *cxlrd) { }; #define CXL_REGION_ATTR(x) NULL #define CXL_REGION_TYPE(x) NULL #define SET_CXL_REGION_ATTR(x) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 6e7a70d51cfe..1215ee4f4035 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -458,6 +458,8 @@ static void cxl_root_decoder_release(struct device *dev) if (atomic_read(&cxlrd->region_id) >= 0) memregion_free(atomic_read(&cxlrd->region_id)); + mutex_destroy(&cxlrd->regions_lock); + xa_destroy(&cxlrd->regions); __cxl_decoder_release(&cxlrd->cxlsd.cxld); kfree(cxlrd); } @@ -2017,6 +2019,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, } mutex_init(&cxlrd->regions_lock); + xa_init(&cxlrd->regions); cxld = &cxlsd->cxld; cxld->dev.type = &cxl_decoder_root_type; @@ -2192,6 +2195,8 @@ static void cxld_unregister(void *dev) if (is_endpoint_decoder(dev)) cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1, DETACH_INVALIDATE); + if (is_root_decoder(dev)) + kill_regions(to_cxl_root_decoder(dev)); device_unregister(dev); } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index b5601e89e302..faf9785c0509 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2537,12 +2537,13 @@ static struct cxl_region *to_cxl_region(struct device *dev) return container_of(dev, struct cxl_region, dev); } -static void unregister_region(void *_cxlr) +static void unregister_region(struct cxl_region *cxlr) { - struct cxl_region *cxlr = _cxlr; + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_region_params *p = &cxlr->params; int i; + xa_erase(&cxlrd->regions, cxlr->id); device_del(&cxlr->dev); /* @@ -2673,6 +2674,19 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb, return NOTIFY_STOP; } +/* unwind all remaining regions */ +void kill_regions(struct cxl_root_decoder *cxlrd) +{ + unsigned long index; + struct cxl_region *cxlr; + + guard(mutex)(&cxlrd->regions_lock); + /* no more region creation */ + cxlrd->dead = true; + xa_for_each(&cxlrd->regions, index, cxlr) + unregister_region(cxlr); +} + /** * devm_cxl_add_region - Adds a region to a decoder * @cxlrd: root decoder @@ -2711,14 +2725,15 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, if (rc) goto err; - rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr); - if (rc) + rc = xa_insert(&cxlrd->regions, cxlr->id, cxlr, GFP_KERNEL); + if (rc) { + unregister_region(cxlr); return ERR_PTR(rc); + } dev_dbg(port->uport_dev, "%s: created %s\n", dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev)); return cxlr; - err: put_device(dev); return ERR_PTR(rc); @@ -2747,6 +2762,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, { int rc; + if (cxlrd->dead) + return ERR_PTR(-ENXIO); + switch (mode) { case CXL_PARTMODE_RAM: case CXL_PARTMODE_PMEM: @@ -2822,38 +2840,27 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr, } DEVICE_ATTR_RO(region); -static struct cxl_region * -cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name) -{ - struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; - struct device *region_dev; - - region_dev = device_find_child_by_name(&cxld->dev, name); - if (!region_dev) - return ERR_PTR(-ENODEV); - - return to_cxl_region(region_dev); -} - static ssize_t delete_region_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); - struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_region *cxlr; - int rc; + int rc, id; ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) return rc; - cxlr = cxl_find_region_by_name(cxlrd, buf); - if (IS_ERR(cxlr)) - return PTR_ERR(cxlr); + rc = sscanf(buf, "region%d\n", &id); + if (rc != 1) + return -EINVAL; - devm_release_action(port->uport_dev, unregister_region, cxlr); - put_device(&cxlr->dev); + cxlr = xa_load(&cxlrd->regions, id); + if (!cxlr || !sysfs_streq(buf, dev_name(&cxlr->dev))) + return -ENODEV; + + unregister_region(cxlr); return len; } @@ -3718,7 +3725,6 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, { struct cxl_endpoint_decoder *cxled = ctx->cxled; struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); - struct cxl_port *port = cxlrd_to_port(cxlrd); struct cxl_dev_state *cxlds = cxlmd->cxlds; int rc, part = READ_ONCE(cxled->part); struct cxl_region *cxlr; @@ -3739,7 +3745,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, rc = __construct_region(cxlr, ctx); if (rc) { - devm_release_action(port->uport_dev, unregister_region, cxlr); + unregister_region(cxlr); return ERR_PTR(rc); } diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 3900a0778571..f43abd1903ce 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -360,6 +360,8 @@ struct cxl_rd_ops { * @region_id: region id for next region provisioning event * @platform_data: platform specific configuration data * @regions_lock: sync region discovery, construction, and deletion + * @regions: regions to remove at root decoder destruct time + * @dead: root decoder dead to region creation * @qos_class: QoS performance class cookie * @ops: CXL root decoder operations * @cxlsd: base cxl switch decoder @@ -370,6 +372,8 @@ struct cxl_root_decoder { atomic_t region_id; void *platform_data; struct mutex regions_lock; + struct xarray regions; + bool dead; int qos_class; struct cxl_rd_ops ops; struct cxl_switch_decoder cxlsd; From bd3a6ff4b84e0fc3fca8556d338270603df13f2e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:56 -0700 Subject: [PATCH 3536/5214] cxl/memdev: Pin parents for entire memdev lifetime In order to be able to manage the driver that uses a memdev attach mechanism the parent needs to stick around for the device_release_driver(cxlmd->dev.parent) event. Fixes: 29317f8dc6ed ("cxl/mem: Introduce cxl_memdev_attach for CXL-dependent operation") Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-4-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/memdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 80e65690eb77..91c99eeea92c 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -25,9 +25,11 @@ static DEFINE_IDA(cxl_memdev_ida); static void cxl_memdev_release(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct device *parent = dev->parent; ida_free(&cxl_memdev_ida, cxlmd->id); kfree(cxlmd); + put_device(parent); } static char *cxl_memdev_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, @@ -707,7 +709,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, dev = &cxlmd->dev; device_initialize(dev); lockdep_set_class(&dev->mutex, &cxl_memdev_key); - dev->parent = cxlds->dev; + dev->parent = get_device(cxlds->dev); dev->bus = &cxl_bus_type; dev->devt = MKDEV(cxl_mem_major, cxlmd->id); dev->type = &cxl_memdev_type; From 2ed519c21bb4fbac5d544ef4b1f98d515b18036d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:57 -0700 Subject: [PATCH 3537/5214] cxl/memdev: Introduce cxl_class_memdev_type In preparation for memdev's without mailbox related infrastructure, introduce cxl_class_memdev_type as a superset of a cxl_memdev_type. Effectively the only difference is that cxl_class_memdev_type exports common sysfs attributes where cxl_memdev_type has none. Related to this is all the cxl_mem_probe() paths that assume the presence of a class device mailbox are updated to skip that requirement. Co-developed-by: Alejandro Lucero Signed-off-by: Alejandro Lucero Signed-off-by: Dan Williams Tested-by: ALejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-5-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/memdev.c | 16 +++++++++++--- drivers/cxl/mem.c | 45 +++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 91c99eeea92c..33a3d2e7b13a 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -574,16 +574,23 @@ void cxl_memdev_update_perf(struct cxl_memdev *cxlmd) } EXPORT_SYMBOL_NS_GPL(cxl_memdev_update_perf, "CXL"); -static const struct device_type cxl_memdev_type = { +static const struct device_type cxl_class_memdev_type = { .name = "cxl_memdev", .release = cxl_memdev_release, .devnode = cxl_memdev_devnode, .groups = cxl_memdev_attribute_groups, }; +static const struct device_type cxl_memdev_type = { + .name = "cxl_memdev", + .release = cxl_memdev_release, + .devnode = cxl_memdev_devnode, +}; + bool is_cxl_memdev(const struct device *dev) { - return dev->type == &cxl_memdev_type; + return (dev->type == &cxl_class_memdev_type || + dev->type == &cxl_memdev_type); } EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "CXL"); @@ -712,7 +719,10 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, dev->parent = get_device(cxlds->dev); dev->bus = &cxl_bus_type; dev->devt = MKDEV(cxl_mem_major, cxlmd->id); - dev->type = &cxl_memdev_type; + if (cxlds->type == CXL_DEVTYPE_DEVMEM) + dev->type = &cxl_memdev_type; + else + dev->type = &cxl_class_memdev_type; device_set_pm_not_required(dev); INIT_WORK(&cxlmd->detach_work, detach_memdev); diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index fcffe24dcb42..ff858318091f 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -65,6 +65,26 @@ static int cxl_debugfs_poison_clear(void *data, u64 dpa) DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL, cxl_debugfs_poison_clear, "%llx\n"); +static void cxl_memdev_poison_enable(struct cxl_memdev_state *mds, + struct cxl_memdev *cxlmd, + struct dentry *dentry) +{ + /* + * Avoid poison debugfs for DEVMEM aka accelerators as they rely on + * cxl_memdev_state. + */ + if (!mds) + return; + + if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds)) + debugfs_create_file("inject_poison", 0200, dentry, cxlmd, + &cxl_poison_inject_fops); + + if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds)) + debugfs_create_file("clear_poison", 0200, dentry, cxlmd, + &cxl_poison_clear_fops); +} + static int cxl_mem_probe(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); @@ -92,12 +112,7 @@ static int cxl_mem_probe(struct device *dev) dentry = cxl_debugfs_create_dir(dev_name(dev)); debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show); - if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds)) - debugfs_create_file("inject_poison", 0200, dentry, cxlmd, - &cxl_poison_inject_fops); - if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds)) - debugfs_create_file("clear_poison", 0200, dentry, cxlmd, - &cxl_poison_clear_fops); + cxl_memdev_poison_enable(mds, cxlmd, dentry); rc = devm_add_action_or_reset(dev, remove_debugfs, dentry); if (rc) @@ -206,16 +221,24 @@ static ssize_t trigger_poison_list_store(struct device *dev, } static DEVICE_ATTR_WO(trigger_poison_list); -static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) +static bool cxl_poison_attr_visible(struct kobject *kobj, struct attribute *a) { struct device *dev = kobj_to_dev(kobj); struct cxl_memdev *cxlmd = to_cxl_memdev(dev); struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); - if (a == &dev_attr_trigger_poison_list.attr) - if (!test_bit(CXL_POISON_ENABLED_LIST, - mds->poison.enabled_cmds)) - return 0; + if (!mds || + !test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) + return false; + + return true; +} + +static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) +{ + if (a == &dev_attr_trigger_poison_list.attr && + !cxl_poison_attr_visible(kobj, a)) + return 0; return a->mode; } From d8dcb0b74b045e36d627935a959c3cf4c8cb2f7c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 14:01:58 -0700 Subject: [PATCH 3538/5214] cxl/region: Introduce devm_cxl_probe_mem() To date, platform firmware maps accelerator memory and accelerator drivers simply want an address range that they can map themselves. This typically results in a single region being auto-assembled upon registration of a memory device. Use the @attach mechanism of devm_cxl_add_memdev() parameter to retrieve that region while also adhering to CXL subsystem locking and lifetime rules. As part of adhering to current object lifetime rules, if the region or the CXL port topology is invalidated, the CXL core arranges for the accelertor driver to be detached as well. The locking and lifetime rules were validated with Dave's work-in-progress cxl-type-2 support for cxl_test. devm_cxl_add_classdev() supports the general memory expansion flow where region assembly is optional, dynamic, and user controlled. Cc: Alejandro Lucero Signed-off-by: Dan Williams Reviewed-by: Alejandro Lucero Tested-by: Alejandro Lucero Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519210158.1499795-6-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 124 +++++++++++++++++++++++++++++++++++ drivers/cxl/cxlmem.h | 27 ++++++-- drivers/cxl/mem.c | 50 +++++++++++--- drivers/cxl/pci.c | 2 +- include/cxl/cxl.h | 3 + tools/testing/cxl/test/mem.c | 2 +- 6 files changed, 191 insertions(+), 17 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index faf9785c0509..ce99f0650764 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1148,6 +1148,19 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr, static void cxl_region_setup_flags(struct cxl_region *cxlr, struct cxl_decoder *cxld) { + if (is_endpoint_decoder(&cxld->dev)) { + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev); + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + + /* + * When a region's memdevs specify an @attach method the attach + * provider is responsible for dispositioning the region for + * both probe and userspace management + */ + if (cxlmd->attach) + set_bit(CXL_REGION_F_LOCK, &cxlr->flags); + } + if (cxld->flags & CXL_DECODER_F_LOCK) { set_bit(CXL_REGION_F_LOCK, &cxlr->flags); clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags); @@ -2560,6 +2573,17 @@ static void unregister_region(struct cxl_region *cxlr) put_device(&cxlr->dev); } +static void endpoint_unregister_region(void *_cxlr) +{ + struct cxl_region *cxlr = _cxlr; + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + + guard(mutex)(&cxlrd->regions_lock); + if (xa_load(&cxlrd->regions, cxlr->id)) + unregister_region(cxlr); + put_device(&cxlr->dev); +} + static struct lock_class_key cxl_region_key; static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id) @@ -4057,6 +4081,103 @@ static int cxl_region_can_probe(struct cxl_region *cxlr) return 0; } +static int first_mapped_decoder(struct device *dev, const void *data) +{ + struct cxl_endpoint_decoder *cxled; + + if (!is_endpoint_decoder(dev)) + return 0; + + cxled = to_cxl_endpoint_decoder(dev); + if (cxled->cxld.region) + return 1; + + return 0; +} + +/* + * Runs in cxl_mem_probe context after successful endpoint probe, assumes the + * simple case of single mapped decoder per memdev. + */ +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd) +{ + struct cxl_attach_region *attach = + container_of(cxlmd->attach, typeof(*attach), attach); + struct cxl_port *endpoint = cxlmd->endpoint; + struct cxl_endpoint_decoder *cxled; + struct cxl_region *cxlr; + int rc; + + /* hold endpoint lock to setup autoremove of the region */ + guard(device)(&endpoint->dev); + if (!endpoint->dev.driver) + return -ENXIO; + guard(rwsem_read)(&cxl_rwsem.region); + guard(rwsem_read)(&cxl_rwsem.dpa); + + /* + * TODO auto-instantiate a region, for now assume this will find an + * auto-region + */ + struct device *dev __free(put_device) = + device_find_child(&endpoint->dev, NULL, first_mapped_decoder); + + if (!dev) { + dev_dbg(cxlmd->cxlds->dev, "no region found for memdev %s\n", + dev_name(&cxlmd->dev)); + return -ENXIO; + } + + cxled = to_cxl_endpoint_decoder(dev); + cxlr = cxled->cxld.region; + + if (cxlr->params.state < CXL_CONFIG_COMMIT) { + dev_dbg(cxlmd->cxlds->dev, + "region %s not committed for memdev %s\n", + dev_name(&cxlr->dev), dev_name(&cxlmd->dev)); + return -ENXIO; + } + + if (cxlr->params.nr_targets > 1) { + dev_dbg(cxlmd->cxlds->dev, + "Only attach to local non-interleaved region\n"); + return -ENXIO; + } + + /* Only teardown regions that pass validation, ignore the rest */ + get_device(&cxlr->dev); + rc = devm_add_action_or_reset(&endpoint->dev, + endpoint_unregister_region, cxlr); + if (rc) + return rc; + + attach->hpa_range = (struct range) { + .start = cxlr->params.res->start, + .end = cxlr->params.res->end, + }; + return 0; +} +EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_attach_region, "cxl_mem"); + +/* + * The presence of an attach method indicates that the region is designated for + * a purpose outside of CXL core memory expansion defaults. + */ +static bool cxl_region_has_memdev_attach(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + + for (int i = 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + + if (cxlmd->attach) + return true; + } + + return false; +} + static int cxl_region_probe(struct device *dev) { struct cxl_region *cxlr = to_cxl_region(dev); @@ -4088,6 +4209,9 @@ static int cxl_region_probe(struct device *dev) if (rc) return rc; + if (cxl_region_has_memdev_attach(cxlr)) + return 0; + switch (cxlr->mode) { case CXL_PARTMODE_PMEM: rc = devm_cxl_region_edac_register(cxlr); diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 776c50d1db51..d3bdd00f94b3 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -34,10 +34,6 @@ (FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \ CXLMDEV_RESET_NEEDED_NOT) -struct cxl_memdev_attach { - int (*probe)(struct cxl_memdev *cxlmd); -}; - /** * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device * @dev: driver core device object @@ -101,10 +97,29 @@ static inline bool is_cxl_endpoint(struct cxl_port *port) return is_cxl_memdev(port->uport_dev); } +struct cxl_memdev_attach { + int (*probe)(struct cxl_memdev *cxlmd); +}; + +/** + * struct cxl_attach_region - coordinate mapping a region at memdev registration + * @attach: common core attachment descriptor + * @hpa_range: physical address range of the region + * + * For the common simple case of a CXL device with private (non-general purpose + * / "accelerator") memory, enumerate firmware instantiated region, or + * instantiate a region for the device's capacity. Destroy the region on detach. + */ +struct cxl_attach_region { + struct cxl_memdev_attach attach; + struct range hpa_range; +}; + +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd); + +struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds); struct cxl_memdev *__devm_cxl_add_memdev(struct cxl_dev_state *cxlds, const struct cxl_memdev_attach *attach); -struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds, - const struct cxl_memdev_attach *attach); int devm_cxl_sanitize_setup_notifier(struct device *host, struct cxl_memdev *cxlmd); struct cxl_memdev_state; diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index ff858318091f..67d31482f06b 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -183,27 +183,59 @@ static int cxl_mem_probe(struct device *dev) } /** - * devm_cxl_add_memdev - Add a CXL memory device + * devm_cxl_add_classdev - Add a CXL memory class-code device * @cxlds: CXL device state to associate with the memdev - * @attach: Caller depends on CXL topology attachment * * Upon return the device will have had a chance to attach to the * cxl_mem driver, but may fail to attach if the CXL topology is not ready * (hardware CXL link down, or software platform CXL root not attached). * - * When @attach is NULL it indicates the caller wants the memdev to remain - * registered even if it does not immediately attach to the CXL hierarchy. When - * @attach is provided a cxl_mem_probe() failure leads to failure of this routine. + * The parent of the resulting device and the devm context for allocations is + * @cxlds->dev. + */ +struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds) +{ + return __devm_cxl_add_memdev(cxlds, NULL); +} +EXPORT_SYMBOL_NS_GPL(devm_cxl_add_classdev, "CXL"); + +/** + * devm_cxl_probe_mem - Add a CXL memory device and probe its region + * @cxlds: CXL device state to associate with the memdev + * @hpa_range: CXL.mem physical address range result + * + * Upon return the device will have had a chance to attach to the + * cxl_mem driver, but may fail to attach if the CXL topology is not ready + * (hardware CXL link down, or software platform CXL root not attached). + * + * Failure to probe the memdev and/or setup a region for the memdev + * results in this function failing. * * The parent of the resulting device and the devm context for allocations is * @cxlds->dev. */ -struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds, - const struct cxl_memdev_attach *attach) +struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds, + struct range *hpa_range) { - return __devm_cxl_add_memdev(cxlds, attach); + struct cxl_attach_region *attach = + devm_kmalloc(cxlds->dev, sizeof(*attach), GFP_KERNEL); + struct cxl_memdev *cxlmd; + + if (!attach) + return ERR_PTR(-ENOMEM); + + *attach = (struct cxl_attach_region) { + .attach = { + .probe = cxl_memdev_attach_region, + }, + .hpa_range = { 0, -1 }, + }; + + cxlmd = __devm_cxl_add_memdev(cxlds, &attach->attach); + *hpa_range = attach->hpa_range; + return cxlmd; } -EXPORT_SYMBOL_NS_GPL(devm_cxl_add_memdev, "CXL"); +EXPORT_SYMBOL_NS_GPL(devm_cxl_probe_mem, "CXL"); static ssize_t trigger_poison_list_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index bace662dc988..267c679b0b3c 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -878,7 +878,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) dev_dbg(&pdev->dev, "No CXL Features discovered\n"); - cxlmd = devm_cxl_add_memdev(cxlds, NULL); + cxlmd = devm_cxl_add_classdev(cxlds); if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h index fa7269154620..016c74fb747c 100644 --- a/include/cxl/cxl.h +++ b/include/cxl/cxl.h @@ -223,4 +223,7 @@ struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, (drv_struct *)_devm_cxl_dev_state_create(parent, type, serial, dvsec, \ sizeof(drv_struct), mbox); \ }) + +struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds, + struct range *range); #endif /* __CXL_CXL_H__ */ diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32..095ca544ac02 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1769,7 +1769,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) cxl_mock_add_event_logs(&mdata->mes); - cxlmd = devm_cxl_add_memdev(cxlds, NULL); + cxlmd = devm_cxl_add_classdev(cxlds); if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); From 383f69656359191d2236ef5ec259984c844fde9a Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 9 Jun 2026 17:13:24 -0700 Subject: [PATCH 3539/5214] cxl: Add dummy function for cxl_memdev_attach_region for !CONFIG_CXL_REGION Add a dummy function that returns -EOPNOTSUPP for cxl_memdev_attach_region when CONFIG_CXL_REGION is not enabled. This allow sbuilding when cxl/core/region.o isn't built. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606100401.GOjzpKHo-lkp@intel.com/ Fixes: 9b1e70e8f9ec ("cxl/region: Introduce devm_cxl_probe_mem()") Reviewed-by: Alison Schofield Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260610001324.260268-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/cxlmem.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index d3bdd00f94b3..ed419d0c59f2 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -115,7 +115,14 @@ struct cxl_attach_region { struct range hpa_range; }; +#ifdef CONFIG_CXL_REGION int cxl_memdev_attach_region(struct cxl_memdev *cxlmd); +#else +static inline int cxl_memdev_attach_region(struct cxl_memdev *cxlmd) +{ + return -EOPNOTSUPP; +} +#endif struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds); struct cxl_memdev *__devm_cxl_add_memdev(struct cxl_dev_state *cxlds, From ba61aed9a34671222d1149acfc2f0179a9ce7e80 Mon Sep 17 00:00:00 2001 From: Lucas Tsai Date: Tue, 9 Jun 2026 19:44:03 +0800 Subject: [PATCH 3540/5214] power: supply: core: fix supplied_from allocations If dts property power-supplies has multiple values, then accessing to psy->supplied_from[i-1] in __power_supply_populate_supplied_from will overrun supplied_from array. Fixes: f6e0b081fb30 ("power_supply: Populate supplied_from hierarchy from the device tree") Signed-off-by: Lucas Tsai Link: https://patch.msgid.link/20260609114403.3896073-1-lucas_tsai@richtek.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index a446d3d086fc..2532e221b2e1 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -292,18 +292,13 @@ static int power_supply_check_supplies(struct power_supply *psy) if (cnt == 1) return 0; - /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from), + /* All supplies found, allocate char * array for filling */ + psy->supplied_from = devm_kcalloc(&psy->dev, + cnt - 1, sizeof(*psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) return -ENOMEM; - *psy->supplied_from = devm_kcalloc(&psy->dev, - cnt - 1, sizeof(**psy->supplied_from), - GFP_KERNEL); - if (!*psy->supplied_from) - return -ENOMEM; - return power_supply_populate_supplied_from(psy); } #else From 4373cfa38ead58f980362c841b0d0bdf8c4d956c Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 08:53:21 +0800 Subject: [PATCH 3541/5214] power: supply: charger-manager: fix refcount leak in is_full_charged() In is_full_charged(), power_supply_get_by_name() is called to obtain a reference to the fuel_gauge power supply. If the voltage check (uV >= desc->fullbatt_uV) succeeds, the function returns true directly without releasing the reference, leaking the refcount. Fix this by setting a flag and jumping to the out label where power_supply_put() properly drops the reference. Cc: stable@vger.kernel.org Fixes: e132fc6bb89b ("power: supply: charger-manager: Make decisions focussed on battery status") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611005322.53096-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel --- drivers/power/supply/charger-manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 5dc9cd37d697..10158b1bf8e2 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -303,8 +303,10 @@ static bool is_full_charged(struct charger_manager *cm) if (cm->battery_status == POWER_SUPPLY_STATUS_FULL && desc->fullbatt_vchkdrop_uV) uV += desc->fullbatt_vchkdrop_uV; - if (uV >= desc->fullbatt_uV) - return true; + if (uV >= desc->fullbatt_uV) { + is_full = true; + goto out; + } } } From a888754e51e915731c8974c4d6d62709facb35d3 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 26 Apr 2026 16:27:05 -0700 Subject: [PATCH 3542/5214] Documentation: ABI: sysfs-class-reboot-mode-reboot_modes: fix doc warnings Repair the docs build warnings in this file by unindenting the description, adding blank lines, and using `` to quote *arg. WARNING: Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:36: abi_sys_class_reboot_mode_driver_reboot_modes doesn't have a description Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: WARNING: Inline emphasis start-string without end-string. [docutils] Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes:1: ERROR: Unexpected indentation. [docutils] Fixes: d3da03025e6d ("Documentation: ABI: Add sysfs-class-reboot-mode-reboot_modes") Signed-off-by: Randy Dunlap Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260426232705.422938-1-rdunlap@infradead.org Signed-off-by: Sebastian Reichel --- .../testing/sysfs-class-reboot-mode-reboot_modes | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes index a16c54ab841b..4306966b7fcc 100644 --- a/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes +++ b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes @@ -2,33 +2,36 @@ What: /sys/class/reboot-mode//reboot_modes Date: March 2026(TBD) KernelVersion: TBD Contact: linux-pm@vger.kernel.org - Description: +Description: This interface exposes the reboot-mode arguments registered with the reboot-mode framework. It is a read-only interface and provides a space separated list of reboot-mode arguments supported on the current platform. Example: + recovery fastboot bootloader The exact sysfs path may vary depending on the name of the driver that registers the arguments. - Example: + Example:: + /sys/class/reboot-mode/nvmem-reboot-mode/reboot_modes /sys/class/reboot-mode/syscon-reboot-mode/reboot_modes /sys/class/reboot-mode/qcom-pon/reboot_modes The supported arguments can be used by userspace to invoke device reset using the standard reboot() system - call interface, with the "argument" as string to "*arg" - parameter along with LINUX_REBOOT_CMD_RESTART2. + call interface, with the "argument" as string to ``*arg`` + parameter along with ``LINUX_REBOOT_CMD_RESTART2``. A driver can expose the supported arguments by registering them with the reboot-mode framework using the property names that follow the mode- format. Example: - mode-bootloader, mode-recovery. + + mode-bootloader, mode-recovery This attribute is useful for scripts or initramfs logic that need to programmatically determine From 894a81b9c4fbb36032db3cad0c5697aecb2bd694 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:49:38 -0500 Subject: [PATCH 3543/5214] pinctrl: Match DT helper types The affected pinctrl drivers either check for the presence of a standard property or read a property documented with an 8-bit cell encoding. Using boolean or u32 helpers for those cases disagrees with the binding. Use a presence helper for "gpio-ranges" and read "microchip,spi-present-mask" with the u8 helper documented by the binding. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Signed-off-by: Linus Walleij --- drivers/pinctrl/bcm/pinctrl-iproc-gpio.c | 2 +- drivers/pinctrl/pinctrl-mcp23s08_spi.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 8c353676f2af..e20f7dc79d43 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -868,7 +868,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) gc->set = iproc_gpio_set; gc->get = iproc_gpio_get; - chip->pinmux_is_supported = of_property_read_bool(dev->of_node, + chip->pinmux_is_supported = of_property_present(dev->of_node, "gpio-ranges"); /* optional GPIO interrupt support */ diff --git a/drivers/pinctrl/pinctrl-mcp23s08_spi.c b/drivers/pinctrl/pinctrl-mcp23s08_spi.c index 54f61c8cb1c0..76d4c135db11 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08_spi.c +++ b/drivers/pinctrl/pinctrl-mcp23s08_spi.c @@ -143,13 +143,13 @@ static int mcp23s08_probe(struct spi_device *spi) unsigned int addr; int chips; int ret; - u32 v; + u8 v; info = spi_get_device_match_data(spi); - ret = device_property_read_u32(dev, "microchip,spi-present-mask", &v); + ret = device_property_read_u8(dev, "microchip,spi-present-mask", &v); if (ret) { - ret = device_property_read_u32(dev, "mcp,spi-present-mask", &v); + ret = device_property_read_u8(dev, "mcp,spi-present-mask", &v); if (ret) { dev_err(dev, "missing spi-present-mask"); return ret; From e954726de56963754c8ac9d9e76b3f59d12fef17 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Tue, 9 Jun 2026 14:13:35 +0800 Subject: [PATCH 3544/5214] firewire: core: Open-code topology list walk A later change will make list_for_each_entry() cache the next element before entering the loop body. for_each_fw_node() intentionally appends newly discovered child nodes to the temporary walk list while the list is being traversed. Keep the loop open-coded so the next node is looked up only after children have been appended. This preserves the current breadth-first traversal semantics and prepares the code for the list iterator update. Signed-off-by: Kaitao Cheng Link: https://lore.kernel.org/r/20260609061347.93688-3-kaitao.cheng@linux.dev Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index bb2d2db30795..df2ac0dab106 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -272,7 +272,9 @@ static void for_each_fw_node(struct fw_card *card, struct fw_node *root, fw_node_get(root); list_add_tail(&root->link, &list); parent = NULL; - list_for_each_entry(node, &list, link) { + for (node = list_first_entry(&list, typeof(*node), link); + !list_entry_is_head(node, &list, link); + node = list_next_entry(node, link)) { node->color = card->color; for (i = 0; i < node->port_count; i++) { From df97a7107b16375a10a36d7a63e9b4291a8ac680 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 7 Jun 2026 14:34:28 +0200 Subject: [PATCH 3545/5214] batman-adv: gw: don't deselect gateway with active hardif The batadv_hardif_cnt() was previously checking if there is an batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since batadv_hardif_disable_interface() was resetting the batadv_hard_iface->mesh_iface after this check, it had to verify whether *1* interface was still part of the mesh_iface before it started the gateway deselection. But after batadv_hardif_cnt() is now checking the lower interfaces of mesh_iface and batadv_hardif_disable_interface() already removed the interface via netdev_upper_dev_unlink() earlier in this function, the check must now make sure that *0* interfaces can be found by batadv_hardif_cnt() before selected gateway must be deselected. Otherwise the deselection would already happen one batadv_hard_iface too early. Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty list, it is possible to replace the counting with a simple list_empty(). Cc: stable@kernel.org Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data") Reviewed-by: Nora Schiffer Signed-off-by: Sven Eckelmann --- net/batman-adv/hard-interface.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 60cee2c2f2f4..03d01c20a954 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -814,30 +814,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, return ret; } -/** - * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface - * @mesh_iface: mesh interface to check - * - * This function is only using RCU for locking - the result can therefore be - * off when another function is modifying the list at the same time. The - * caller can use the rtnl_lock to make sure that the count is accurate. - * - * Return: number of connected/enslaved hard interfaces - */ -static size_t batadv_hardif_cnt(struct net_device *mesh_iface) -{ - struct batadv_hard_iface *hard_iface; - struct list_head *iter; - size_t count = 0; - - rcu_read_lock(); - netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) - count++; - rcu_read_unlock(); - - return count; -} - /** * batadv_hardif_disable_interface() - Remove hard interface from mesh interface * @hard_iface: hard interface to be removed @@ -878,8 +854,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface); batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface); - /* nobody uses this interface anymore */ - if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1) + /* nobody uses this mesh interface anymore */ + if (list_empty(&hard_iface->mesh_iface->adj_list.lower)) batadv_gw_check_client_stop(bat_priv); hard_iface->mesh_iface = NULL; From 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 10 Jun 2026 13:33:19 +0200 Subject: [PATCH 3546/5214] batman-adv: ensure bcast is writable before modifying TTL Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or used skb_cow() to ensure that the data part is not shared. The old implementation used a shared queue and created copies before attempting to write to it. But with the new implementation, the broadcast packet is already modified when it gets received. Potentially writing to shared buffers in this process. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to rebroadcast the packet. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Signed-off-by: Sven Eckelmann --- net/batman-adv/routing.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index cd4368b846ad..7b4acd1ad991 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) goto free_skb; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto free_skb; + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + if (bcast_packet->ttl-- < 2) goto free_skb; From e728bbdf32660c8f32b8f5e8d09427a2c131ad60 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 10 Jun 2026 09:52:22 +0200 Subject: [PATCH 3547/5214] batman-adv: fix (m|b)cast csum after decrementing TTL The broadcast and multicast packets can be received at the same time by the local system and forwarded to other nodes. Both are simply decrementing the TTL at the beginning of the receive path - independent of chosen paths (receive/forward). But such a modification of the data conflicts with the hw csum. This is not a problem when the packet is directly forwarded but can cause errors in the local receive path. Such a problem can then trigger a "hw csum failure". The receiver path must therefore ensure that the csum is fixed for each modification of the payload before batadv_interface_rx() is reached. Since all batman-adv packet types with a ttl have it as u8 at offset 2, a helper can be used for all of them. But it is only used at the moment for batadv_bcast_packet and batadv_mcast_packet because they are the only ones which deliver the packet locally but unconditionally modify the TTL. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann --- net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 7b4acd1ad991..8786b66c8a92 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, return true; } +/** + * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe + * @skb: the received packet with @skb->data pointing to the batman-adv header + * + * Supports the following packet types, all of which carry the TTL at offset 2: + * + * - batadv_ogm_packet + * - batadv_ogm2_packet + * - batadv_icmp_header + * - batadv_icmp_packet + * - batadv_icmp_tp_packet + * - batadv_icmp_packet_rr + * - batadv_unicast_packet + * - batadv_frag_packet + * - batadv_bcast_packet + * - batadv_mcast_packet + * - batadv_coded_packet + * - batadv_unicast_tvlv_packet + * + * Return: true if the packet may be forwarded (ttl decremented), + * false if it must be dropped (ttl would expire) + */ +static bool batadv_skb_decrement_ttl(struct sk_buff *skb) +{ + static const size_t ttl_offset = 2; + u8 *ttl_pos; + + BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); + + ttl_pos = skb->data + ttl_offset; + + /* would expire on this hop -> drop, leave header + csum untouched */ + if (*ttl_pos < 2) + return false; + + skb_postpull_rcsum(skb, ttl_pos, 1); + (*ttl_pos)--; + skb_postpush_rcsum(skb, ttl_pos, 1); + + return true; +} + /** * batadv_recv_my_icmp_packet() - receive an icmp packet locally * @bat_priv: the bat priv with all the mesh interface information @@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, bcast_packet = (struct batadv_bcast_packet *)skb->data; - if (bcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); @@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, goto free_skb; mcast_packet = (struct batadv_mcast_packet *)skb->data; - if (mcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; tvlv_buff = (unsigned char *)(skb->data + hdr_size); From b7293c6e8c15b2db77809b25cf8389e35331b27a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 22:14:54 +0200 Subject: [PATCH 3548/5214] batman-adv: frag: ensure fragment is writable before modifying TTL Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or use skb_cow() to ensure that the data part is not shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to forward the fragment. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 15 ++++++++++++++- net/batman-adv/fragmentation.h | 3 ++- net/batman-adv/routing.c | 3 +-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 1e42cf99f8b3..f311a42203d2 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -386,6 +386,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, * @skb: skb to forward * @recv_if: interface that the skb is received on * @orig_node_src: originator that the skb is received from + * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and + * NET_RX_DROP when it was dropped; only valid when true is returned * * Look up the next-hop of the fragments payload and check if the merged packet * will exceed the MTU towards the next-hop. If so, the fragment is forwarded @@ -395,7 +397,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, */ bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src) + struct batadv_orig_node *orig_node_src, + int *rx_result) { struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface); struct batadv_neigh_node *neigh_node = NULL; @@ -414,12 +417,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + + packet = (struct batadv_frag_packet *)skb->data; + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, skb->len + ETH_HLEN); packet->ttl--; batadv_send_unicast_skb(skb, neigh_node); + *rx_result = NET_RX_SUCCESS; ret = true; } diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index dbf0871f8703..51e281027ab6 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, bool (*check_cb)(struct batadv_frag_table_entry *)); bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src); + struct batadv_orig_node *orig_node_src, + int *rx_result); bool batadv_frag_skb_buffer(struct sk_buff **skb, struct batadv_orig_node *orig_node); int batadv_frag_send_packet(struct sk_buff *skb, diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 8786b66c8a92..9db57fd36e7d 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, /* Route the fragment if it is not for us and too big to be merged. */ if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && - batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { /* skb was consumed */ skb = NULL; - ret = NET_RX_SUCCESS; goto put_orig_node; } From 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 22:14:54 +0200 Subject: [PATCH 3549/5214] batman-adv: frag: avoid underflow of TTL Packets with a TTL are using it to limit the amount of time this packet can be forwarded. But for batadv_frag_packet, the TTL was always only reduced but it was never evaluated. It could even underflow without any effect. Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for forwarding. This keeps it in sync with the not fragmented unicast packet. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index f311a42203d2..8a006a0473a8 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -417,6 +417,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (packet->ttl < 2) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + if (skb_cow(skb, ETH_HLEN) < 0) { kfree_skb(skb); *rx_result = NET_RX_DROP; From d11c00b95b2a3b3934007fc003dccc6fdcc061ad Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 21:47:28 +0200 Subject: [PATCH 3550/5214] batman-adv: v: prevent OGM aggregation on disabled hardif When an interface gets disabled, the worker is correctly disabled by batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). In this process, the skb aggr_list is also freed. But batadv_v_ogm_send_meshif() can still queue new skbs (via batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all cores can no longer find the RCU protected list of hard interfaces. These queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. The batadv_v_ogm_iface_disable() function must block batadv_v_ogm_queue_on_if() to avoid leak of skbs. Cc: stable@kernel.org Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") Signed-off-by: Sven Eckelmann --- net/batman-adv/bat_v.c | 1 + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ net/batman-adv/types.h | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index fe7c0113d0df..db6f5bdcaa98 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) hard_iface->bat_v.aggr_len = 0; skb_queue_head_init(&hard_iface->bat_v.aggr_list); + hard_iface->bat_v.aggr_list_enabled = false; INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, batadv_v_ogm_aggr_work); /* make sure it doesn't run until interface gets enabled */ diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 81926ef9c02c..95efd8a43c79 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, } spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + if (!hard_iface->bat_v.aggr_list_enabled) { + kfree_skb(skb); + goto unlock; + } + if (!batadv_v_ogm_queue_left(skb, hard_iface)) batadv_v_ogm_aggr_send(bat_priv, hard_iface); hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb); __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb); + +unlock: spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } @@ -415,6 +422,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = true; + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + enable_delayed_work(&hard_iface->bat_v.aggr_wq); batadv_v_ogm_start_queue_timer(hard_iface); @@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) disable_delayed_work_sync(&hard_iface->bat_v.aggr_wq); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = false; batadv_v_ogm_aggr_list_free(hard_iface); spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 5fd5bd358a24..5e81c93b8217 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -145,6 +145,12 @@ struct batadv_hard_iface_bat_v { /** @aggr_list: queue for to be aggregated OGM packets */ struct sk_buff_head aggr_list; + /** + * @aggr_list_enabled: aggr_list is active and new skbs can be + * enqueued. Protected by aggr_list.lock after initialization + */ + bool aggr_list_enabled:1; + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ unsigned int aggr_len; From e7c775110e1858e5a7471a23a9c9658c0af9df89 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 10 Jun 2026 21:36:15 +0200 Subject: [PATCH 3551/5214] batman-adv: tp_meter: restrict number of unacked list entries When the unacked_list is unbound, an attacker could send messages with small lengths and appropriated seqno + gaps to force the receiver to allocate more and more unacked_list entries. And the end either causing an out-of-memory situation or increase the management overhead for the (large) list that significant portions of CPU cycles are wasted in searching through the list. When limiting the list to a specific number, it is important to still correctly add a new entry to the list. But if the list became larger than the limit, the last entry of the list (with the highest seqno) must be dropped to still allow the earlier seqnos to finish and therefore to continue the process. Otherwise, the process might get stuck with too high seqnos which are not handled by batadv_tp_ack_unordered(). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- net/batman-adv/types.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 7e98cbfbbb70..259ac8c30735 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -87,6 +87,11 @@ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ sizeof(struct batadv_unicast_packet)) +/** + * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack + */ +#define BATADV_TP_MAX_UNACKED 100 + static u8 batadv_tp_prerandom[4096] __read_mostly; /** @@ -1303,6 +1308,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { list_del(&un->list); kfree(un); + tp_vars->common.unacked_count--; } spin_unlock_bh(&tp_vars->common.unacked_lock); @@ -1416,6 +1422,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->common.unacked_list)) { list_add(&new->list, &tp_vars->common.unacked_list); + tp_vars->common.unacked_count++; goto out; } @@ -1446,12 +1453,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, */ list_add(&new->list, &un->list); added = true; + tp_vars->common.unacked_count++; break; } /* received packet with smallest seqno out of order; add it to front */ - if (!added) + if (!added) { list_add(&new->list, &tp_vars->common.unacked_list); + tp_vars->common.unacked_count++; + } + + /* remove the last (biggest) unacked seqno when list is too large */ + if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) { + un = list_last_entry(&tp_vars->common.unacked_list, + struct batadv_tp_unacked, list); + list_del(&un->list); + kfree(un); + tp_vars->common.unacked_count--; + } out: spin_unlock_bh(&tp_vars->common.unacked_lock); @@ -1488,6 +1507,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) list_del(&un->list); kfree(un); + tp_vars->common.unacked_count--; } spin_unlock_bh(&tp_vars->common.unacked_lock); } @@ -1537,6 +1557,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, spin_lock_init(&tp_vars->common.unacked_lock); INIT_LIST_HEAD(&tp_vars->common.unacked_list); + tp_vars->common.unacked_count = 0; kref_get(&tp_vars->common.refcount); timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 5e81c93b8217..9fa8e73ff6e5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1366,6 +1366,9 @@ struct batadv_tp_vars_common { /** @unacked_lock: protect unacked_list */ spinlock_t unacked_lock; + /** @unacked_count: number of unacked entries */ + size_t unacked_count; + /** @refcount: number of context where the object is used */ struct kref refcount; From d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 10:28:33 +0200 Subject: [PATCH 3552/5214] batman-adv: tp_meter: annotate last_recv_time access with READ/WRITE_ONCE The last_recv_time field for batadv_tp_receiver tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 259ac8c30735..fb87fa141e32 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1290,7 +1290,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) bat_priv = tp_vars->common.bat_priv; /* if there is recent activity rearm the timer */ - if (!batadv_has_timed_out(tp_vars->last_recv_time, + if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), BATADV_TP_RECV_TIMEOUT)) { /* reset the receiver shutdown timer */ batadv_tp_reset_receiver_timer(tp_vars); @@ -1532,7 +1532,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig, icmp->session); if (tp_vars) { - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); goto out_unlock; } @@ -1562,7 +1562,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, kref_get(&tp_vars->common.refcount); timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0); - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); kref_get(&tp_vars->common.refcount); hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_receiver_list); @@ -1613,7 +1613,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, goto out; } - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); } /* if the packet is a duplicate, it may be the case that an ACK has been From 6dde0cfcb36e4d5b3de35b75696937478441eed4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 08:04:13 +0200 Subject: [PATCH 3553/5214] batman-adv: tp_meter: prevent parallel modifications of last_recv When last_recv is updated to store the last receive sequence number, it is assuming that nothing is modifying in parallel while: * check for outdated packets is done * out of order check is performed (and packets are stored in out-of-order queue) * the out-of-order queue was searched for closed gaps * sequence number for next ack is calculated Nothing of that was actually protected. It could therefore happen that the last_recv was updated multiple times in parallel and the final sequence number was calculated with deltas which had no connection to the sequence number they were added to. Lock this whole region with the same lock which was already used to protect the unacked (out-of-order) list. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 22 +++++++++++++--------- net/batman-adv/types.h | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index fb87fa141e32..055aa1ee6ac5 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1402,6 +1402,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, const struct sk_buff *skb) + __must_hold(&tp_vars->common.unacked_lock) { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; @@ -1418,12 +1419,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, payload_len = skb->len - sizeof(struct batadv_unicast_packet); new->len = payload_len; - spin_lock_bh(&tp_vars->common.unacked_lock); /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->common.unacked_list)) { list_add(&new->list, &tp_vars->common.unacked_list); tp_vars->common.unacked_count++; - goto out; + return true; } /* otherwise loop over the list and either drop the packet because this @@ -1472,9 +1472,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, tp_vars->common.unacked_count--; } -out: - spin_unlock_bh(&tp_vars->common.unacked_lock); - return true; } @@ -1484,6 +1481,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) + __must_hold(&tp_vars->common.unacked_lock) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1491,7 +1489,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - spin_lock_bh(&tp_vars->common.unacked_lock); list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of @@ -1509,7 +1506,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) kfree(un); tp_vars->common.unacked_count--; } - spin_unlock_bh(&tp_vars->common.unacked_lock); } /** @@ -1588,6 +1584,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_receiver *tp_vars; size_t packet_size; + u32 to_ack; u32 seqno; icmp = (struct batadv_icmp_tp_packet *)skb->data; @@ -1616,6 +1613,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, WRITE_ONCE(tp_vars->last_recv_time, jiffies); } + spin_lock_bh(&tp_vars->common.unacked_lock); + /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ @@ -1627,8 +1626,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) + if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + spin_unlock_bh(&tp_vars->common.unacked_lock); goto out; + } /* send a duplicate ACK */ goto send_ack; @@ -1642,11 +1643,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, batadv_tp_ack_unordered(tp_vars); send_ack: + to_ack = tp_vars->last_recv; + spin_unlock_bh(&tp_vars->common.unacked_lock); + /* send the ACK. If the received packet was out of order, the ACK that * is going to be sent is a duplicate (the sender will count them and * possibly enter Fast Retransmit as soon as it has reached 3) */ - batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, + batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, icmp->timestamp, icmp->session, icmp->uid); out: batadv_tp_receiver_put(tp_vars); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 9fa8e73ff6e5..c1b3f989566f 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1363,7 +1363,7 @@ struct batadv_tp_vars_common { /** @unacked_list: list of unacked packets (meta-info only) */ struct list_head unacked_list; - /** @unacked_lock: protect unacked_list */ + /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ spinlock_t unacked_lock; /** @unacked_count: number of unacked entries */ From cbde75c38b21f022891525078622587ad557b7c1 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 11 Jun 2026 00:21:37 +0200 Subject: [PATCH 3554/5214] batman-adv: tp_meter: handle overlapping packets If the size of the packets would change during the transmission, it could happen that some retries of packets are overlapping. In this case, precise comparisons of sequence numbers by the receiver would be wrong. It is then necessary to check if the start sequence number to the end sequence number ("seqno + length") would contain a new range. If this is the case then this is enough to accept this packet. In all other cases, the packet still has to be dropped (and not acked). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 055aa1ee6ac5..c2eea7dbc488 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1392,7 +1392,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, /** * batadv_tp_handle_out_of_order() - store an out of order packet * @tp_vars: the private data of the current TP meter session - * @skb: the buffer containing the received packet + * @seqno: sequence number of new received packet + * @payload_len: length of the received packet * * Store the out of order packet in the unacked list for late processing. This * packets are kept in this list so that they can be ACKed at once as soon as @@ -1401,22 +1402,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, * Return: true if the packed has been successfully processed, false otherwise */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, - const struct sk_buff *skb) + u32 seqno, u32 payload_len) __must_hold(&tp_vars->common.unacked_lock) { - const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; - u32 payload_len; bool added = false; new = kmalloc_obj(*new, GFP_ATOMIC); if (unlikely(!new)) return false; - icmp = (struct batadv_icmp_tp_packet *)skb->data; - - new->seqno = ntohl(icmp->seqno); - payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->seqno = seqno; new->len = payload_len; /* if the list is empty immediately attach this new object */ @@ -1583,7 +1579,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_receiver *tp_vars; - size_t packet_size; + u32 payload_len; u32 to_ack; u32 seqno; @@ -1618,15 +1614,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ - if (batadv_seq_before(seqno, tp_vars->last_recv)) + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + to_ack = seqno + payload_len; + if (batadv_seq_before(to_ack, tp_vars->last_recv)) goto send_ack; /* if the packet is out of order enqueue it */ - if (ntohl(icmp->seqno) != tp_vars->last_recv) { + if (batadv_seq_before(tp_vars->last_recv, seqno)) { /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { spin_unlock_bh(&tp_vars->common.unacked_lock); goto out; } @@ -1636,8 +1634,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, } /* if everything was fine count the ACKed bytes */ - packet_size = skb->len - sizeof(struct batadv_unicast_packet); - tp_vars->last_recv += packet_size; + tp_vars->last_recv = to_ack; /* check if this ordered message filled a gap.... */ batadv_tp_ack_unordered(tp_vars); From c3382d1e69136ce0aa755fbe09512e03cdd95944 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 2 Jun 2026 21:26:23 -0700 Subject: [PATCH 3555/5214] apparmor: add Georgia Garcia as co-maintainer of apparmor Georgia has agreed to help maintaining apparmor. Signed-off-by: John Johansen --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..f58c79c7ee4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1923,6 +1923,7 @@ F: include/uapi/linux/apm_bios.h APPARMOR SECURITY MODULE M: John Johansen M: John Johansen +M: Georgia Garcia L: apparmor@lists.ubuntu.com (moderated for non-subscribers) S: Supported W: apparmor.net From f64e03da0d83cb173743888bff4a7e61476a8fc2 Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Thu, 11 Jun 2026 10:59:01 +0800 Subject: [PATCH 3556/5214] Revert "PCI/MSI: Unmap MSI-X region on error" This reverts commit 1a8d4c6ecb4c81261bcdf13556abd4a958eca202. Commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") added an iounmap(dev->msix_base) on the error path of msix_capability_init() to release the MSI-X region when msix_setup_interrupts() fails. When msix_setup_interrupts() fails, the call chain is: msix_setup_interrupts() -> __msix_setup_interrupts() struct pci_dev *dev __free(free_msi_irqs) = __dev; ... return ret; // __free cleanup fires on error The __free(free_msi_irqs) cleanup calls pci_free_msi_irqs(), which already handles the unmap: void pci_free_msi_irqs(struct pci_dev *dev) { pci_msi_teardown_msi_irqs(dev); if (dev->msix_base) { iounmap(dev->msix_base); // already unmapped here dev->msix_base = NULL; // and set to NULL } } So dev->msix_base is unmapped and set to NULL before msix_setup_interrupts() returns to msix_capability_init(). The "goto out_unmap" introduced by commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") then calls iounmap() a second time on a NULL pointer. This was reproduced on Intel Emerald Rapids (192 CPUs) while running tools/testing/selftests/kexec/test_kexec_jump.sh: WARNING: CPU#44 at iounmap+0x2a/0xe0 RIP: 0010:iounmap+0x2a/0xe0 RDI: 0000000000000000 Call Trace: msix_capability_init+0x317/0x3f0 __pci_enable_msix_range+0x21d/0x2c0 pci_alloc_irq_vectors_affinity+0xa9/0x130 nvme_setup_io_queues+0x2a8/0x420 [nvme] nvme_reset_work+0x151/0x340 [nvme] ... RDI=0 confirms iounmap() is called with NULL. Restore the original "goto out_disable" and leave the unmap to the existing __free(free_msi_irqs) cleanup. Fixes: 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") Reported-by: Guenter Roeck Signed-off-by: Yuanhe Shu Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20260610194406.GA380991@bhelgaas/ Link: https://patch.msgid.link/20260611025901.1105209-1-xiangzao@linux.alibaba.com Closes: https://lore.kernel.org/all/4fc6208d-513b-4f41-a13a-4a0829ab50ad@roeck-us.net/ --- drivers/pci/msi/msi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 81d24a270a79..a3e0daff0988 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -749,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, ret = msix_setup_interrupts(dev, entries, nvec, affd); if (ret) - goto out_unmap; + goto out_disable; /* Disable INTX */ pci_intx_for_msi(dev, 0); @@ -770,8 +770,6 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, pcibios_free_irq(dev); return 0; -out_unmap: - iounmap(dev->msix_base); out_disable: dev->msix_enabled = 0; pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0); From d936e1a9170f9cadaa5f37586b1dfe6f20f98799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Fri, 12 Jun 2026 19:27:55 +0200 Subject: [PATCH 3557/5214] landlock: Set audit_net.sk for socket access checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set audit_net.sk in current_check_access_socket() to provide the socket object to audit_log_lsm_data(). This makes Landlock consistent with AppArmor, which always sets .sk for socket operations, and with SELinux's generic socket permission checks. The socket's local and foreign address information (laddr, lport, faddr, fport) is logged by the shared lsm_audit.c infrastructure when the socket has bound or connected state. Fields with zero values are suppressed by print_ipv4_addr()/print_ipv6_addr(), so the audit output is unchanged for the common case of bind denials on unbound sockets. For connect denials after a prior bind, the bound local address (laddr, lport) appears before the existing sockaddr fields (daddr, dest). No existing fields are removed or reordered, and the new field names (laddr, lport, faddr, fport) are standard audit fields already emitted by other LSMs through the same lsm_audit.c code path. Add a connect_tcp_bound audit test that binds to an allowed port and then connects to a denied one, verifying that the denial record reports laddr/lport from the bound socket in addition to the connect destination. Cc: Günther Noack Cc: Tingmao Wang Cc: stable@vger.kernel.org Fixes: 9f74411a40ce ("landlock: Log TCP bind and connect denials") Link: https://patch.msgid.link/20260612172757.1003481-1-mic@digikod.net Signed-off-by: Mickaël Salaün --- security/landlock/net.c | 1 + tools/testing/selftests/landlock/net_test.c | 62 +++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/security/landlock/net.c b/security/landlock/net.c index c368649985c5..a38bdfcffc22 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -198,6 +198,7 @@ static int current_check_access_socket(struct socket *const sock, return 0; audit_net.family = address->sa_family; + audit_net.sk = sock->sk; landlock_log_denial(subject, &(struct landlock_request){ .type = LANDLOCK_REQUEST_NET_ACCESS, diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 4c528154ea92..0c256e7c8675 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -2026,4 +2026,66 @@ TEST_F(audit, connect) EXPECT_EQ(0, close(sock_fd)); } +static int matches_log_tcp_bound(int audit_fd, const char *const addr, + __u16 lport, __u16 dport) +{ + static const char log_template[] = REGEX_LANDLOCK_PREFIX + " blockers=net\\.connect_tcp laddr=%s lport=%u daddr=%s dest=%u$"; + /* Slack for two addresses and two port numbers. */ + char log_match[sizeof(log_template) + 40]; + int log_match_len; + + log_match_len = snprintf(log_match, sizeof(log_match), log_template, + addr, lport, addr, dport); + if (log_match_len > sizeof(log_match)) + return -E2BIG; + + return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, + NULL); +} + +/* + * After a bind() to an allowed port, a denied connect must report laddr/lport + * from the bound socket (made available through audit_net.sk) in addition to + * the connect sockaddr's daddr/dest. + */ +TEST_F(audit, connect_tcp_bound) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + const struct landlock_net_port_attr rule_bind = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + .port = self->srv0.port, + }; + struct service_fixture srv_remote; + struct audit_records records; + int ruleset_fd, sock_fd; + + /* Uses a second port as the denied connect target. */ + ASSERT_EQ(0, set_service(&srv_remote, variant->prot, 1)); + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule_bind, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv0); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(0, bind_variant(sock_fd, &self->srv0)); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &srv_remote)); + EXPECT_EQ(0, matches_log_tcp_bound(self->audit_fd, variant->addr, + self->srv0.port, srv_remote.port)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(1, records.domain); + + EXPECT_EQ(0, close(sock_fd)); +} + TEST_HARNESS_MAIN From b232bd12789fa57405b5092f28788be97aae9999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 20:03:08 +0200 Subject: [PATCH 3558/5214] landlock: Account all audit data allocations to user space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark the kzalloc_flex() of struct landlock_details with GFP_KERNEL_ACCOUNT so the allocation is charged to the calling task, like the other Landlock per-domain allocations which have used GFP_KERNEL_ACCOUNT forever. Every property of landlock_details is caller-attributable: allocated by landlock_restrict_self(2), owned by the caller's landlock_hierarchy, contents are the caller's pid, uid, comm, and exe_path, lifetime bounded by the caller's domain. While the caller may not know nor control the size of this allocation (i.e. exe_path), this data should still be accounted for it. The deciding factor is whether userspace can trigger the allocation, not whether the size of the data is known nor controlled by the caller. This aligns with the kmemcg accounting policy established by commit 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg"). No new failure modes: the hierarchy and ruleset are allocated before details and are already accounted, so landlock_restrict_self(2) already returns -ENOMEM under memcg pressure. This change widens that existing failure window slightly; it does not introduce a new error code. Cc: Günther Noack Cc: Paul Moore Cc: stable@vger.kernel.org Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status") Link: https://patch.msgid.link/20260513180309.165840-1-mic@digikod.net Signed-off-by: Mickaël Salaün --- security/landlock/domain.c | 9 +++++---- security/landlock/domain.h | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/security/landlock/domain.c b/security/landlock/domain.c index 06b6bd845060..04b69b43fbf5 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -90,11 +90,12 @@ static struct landlock_details *get_current_details(void) return ERR_CAST(buffer); /* - * Create the new details according to the path's length. Do not - * allocate with GFP_KERNEL_ACCOUNT because it is independent from the - * caller. + * Create the new details according to the path's length. Account to + * the calling task's memcg, like the other Landlock per-domain + * allocations, even if it may not control the related size. */ - details = kzalloc_flex(*details, exe_path, path_size); + details = + kzalloc_flex(*details, exe_path, path_size, GFP_KERNEL_ACCOUNT); if (!details) return ERR_PTR(-ENOMEM); diff --git a/security/landlock/domain.h b/security/landlock/domain.h index a9d57db0120d..35cac8f6daee 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -33,10 +33,7 @@ enum landlock_log_status { * Rarely accessed, mainly when logging the first domain's denial. * * The contained pointers are initialized at the domain creation time and never - * changed again. Contrary to most other Landlock object types, this one is - * not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the - * caller's control (e.g. unknown exe_path) and the data is not explicitly - * requested nor used by tasks. + * changed again. */ struct landlock_details { /** From 143c656e2588b60e69df4287131413dab93ff53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 17:18:53 +0200 Subject: [PATCH 3559/5214] landlock: Demonstrate best-effort allowed_access filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Landlock provides best-effort sandboxing across ABI versions: applications request the rights they need, and on older kernels the unsupported rights are silently dropped from handled_access_* by the documented compatibility switch. The recommended pattern for landlock_add_rule(2) calls is to mirror this filtering at the rule level, which wasn't explicitly described in the exemple. Show the pattern explicitly in the filesystem and network rule examples by masking each rule's allowed_access against the ruleset's handled_access_* and adding the rule only when at least one bit remains set. This makes the recommended best-effort pattern self-documenting. Reviewed-by: Günther Noack Link: https://patch.msgid.link/20260513151856.148423-1-mic@digikod.net Signed-off-by: Mickaël Salaün --- Documentation/userspace-api/landlock.rst | 48 +++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index fd8b78c31f2f..45861fa75685 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -8,7 +8,7 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: March 2026 +:Date: May 2026 The goal of Landlock is to enable restriction of ambient rights (e.g. global filesystem or network access) for a set of processes. Because Landlock @@ -155,7 +155,7 @@ this file descriptor. .. code-block:: c - int err; + int err = 0; struct landlock_path_beneath_attr path_beneath = { .allowed_access = LANDLOCK_ACCESS_FS_EXECUTE | @@ -163,25 +163,29 @@ this file descriptor. LANDLOCK_ACCESS_FS_READ_DIR, }; - path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC); - if (path_beneath.parent_fd < 0) { - perror("Failed to open file"); - close(ruleset_fd); - return 1; - } - err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, - &path_beneath, 0); - close(path_beneath.parent_fd); - if (err) { - perror("Failed to update ruleset"); - close(ruleset_fd); - return 1; + path_beneath.allowed_access &= ruleset_attr.handled_access_fs; + if (path_beneath.allowed_access) { + path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC); + if (path_beneath.parent_fd < 0) { + perror("Failed to open file"); + close(ruleset_fd); + return 1; + } + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath, 0); + close(path_beneath.parent_fd); + if (err) { + perror("Failed to update ruleset"); + close(ruleset_fd); + return 1; + } } -It may also be required to create rules following the same logic as explained -for the ruleset creation, by filtering access rights according to the Landlock -ABI version. In this example, this is not required because all of the requested -``allowed_access`` rights are already available in ABI 1. +As shown above, masking the rule's ``allowed_access`` against the ruleset's +``handled_access_*`` is the recommended best-effort pattern: rights the running +kernel does not support are dropped (the compatibility switch above already +cleared them in ``handled_access_*``), and the rule is skipped if no supported +right remains. For network access-control, we can add a set of rules that allow to use a port number for a specific action: HTTPS connections. @@ -193,8 +197,10 @@ number for a specific action: HTTPS connections. .port = 443, }; - err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &net_port, 0); + net_port.allowed_access &= ruleset_attr.handled_access_net; + if (net_port.allowed_access) + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &net_port, 0); When passing a non-zero ``flags`` argument to ``landlock_restrict_self()``, a similar backwards compatibility check is needed for the restrict flags From 4b80320ca7ed03d6e683f95b6066565dc97b9f92 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:16:56 +0000 Subject: [PATCH 3560/5214] landlock: Fix LANDLOCK_SCOPE_SIGNAL bypass on the SIGIO path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LANDLOCK_SCOPE_SIGNAL must prevent a sandboxed process from signaling processes outside its Landlock domain. It can be bypassed through the asynchronous SIGIO delivery path. A sandboxed process that owns any file or socket can arm it with fcntl(fd, F_SETOWN, -pgid), fcntl(fd, F_SETSIG, SIGKILL) and O_ASYNC, so that an I/O event makes the kernel deliver the chosen signal to the whole process group. As the head of its process group's task list (the default position right after fork()) that group can also hold the non-sandboxed process that launched it, e.g. a supervisor or a security monitor. The sandbox can thus kill or signal the processes LANDLOCK_SCOPE_SIGNAL is meant to protect from it. The scope is enforced in hook_file_send_sigiotask() against the Landlock domain recorded at F_SETOWN time, not the live domain of the sender. control_current_fowner() decides whether to record that domain and skips recording it when the fowner target is in the caller's thread group, which is safe only for a single-task target (PIDTYPE_PID, PIDTYPE_TGID). For a process group (PIDTYPE_PGID) pid_task() returns only one member; recording is skipped whenever that member shares the caller's thread group, and hook_file_send_sigiotask() then lets the signal fan out to the whole group unchecked. Record the domain for every non single-process target so the scope is enforced against each group member at delivery time. That recording is necessary but not sufficient on its own: the kernel signals a process group through its members' thread-group leaders, and the leader of the registrant's own process can carry a different Landlock domain than the sibling thread that armed the owner. domain_is_scoped() would then deny that leader, even though commit 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") requires same-process delivery to be allowed. hook_task_kill() avoids this by evaluating same_thread_group() live, per recipient; the SIGIO path instead delegates the whole decision to a single registration-time check, which a process-group fan-out cannot honor. So also record the registrant's thread group next to its domain and exempt it at delivery: hook_file_send_sigiotask() allows the signal whenever the recipient belongs to the registrant's own process, restoring the same-process guarantee while keeping out-of-domain group members blocked. The direct kill() path (hook_task_kill) already evaluates the live domain and is unaffected. Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/56bffc24f3d0d08b45a686a48e99766b0a0821fa.1780614610.git.hexlabsecurity@proton.me [mic: Check pid_type earlier and improve comment, fix commit message, fix comment formatting] Signed-off-by: Mickaël Salaün --- security/landlock/fs.c | 14 ++++++++++++++ security/landlock/fs.h | 10 ++++++++++ security/landlock/task.c | 11 +++++++++++ 3 files changed, 35 insertions(+) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index c1ecfe239032..664962a416d7 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1900,6 +1900,14 @@ static bool control_current_fowner(struct fown_struct *const fown) */ lockdep_assert_held(&fown->lock); + /* + * A process-group or session owner (PIDTYPE_PGID/PIDTYPE_SID) fans the + * signal out to every member at delivery time, so record the domain and + * let hook_file_send_sigiotask() check the live scope per recipient. + */ + if (fown->pid_type != PIDTYPE_PID && fown->pid_type != PIDTYPE_TGID) + return true; + /* * Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side * critical section. @@ -1916,6 +1924,7 @@ static void hook_file_set_fowner(struct file *file) { struct landlock_ruleset *prev_dom; struct landlock_cred_security fown_subject = {}; + struct pid *prev_tg, *fown_tg = NULL; size_t fown_layer = 0; if (control_current_fowner(file_f_owner(file))) { @@ -1928,21 +1937,26 @@ static void hook_file_set_fowner(struct file *file) if (new_subject) { landlock_get_ruleset(new_subject->domain); fown_subject = *new_subject; + fown_tg = get_pid(task_tgid(current)); } } prev_dom = landlock_file(file)->fown_subject.domain; + prev_tg = landlock_file(file)->fown_tg; landlock_file(file)->fown_subject = fown_subject; + landlock_file(file)->fown_tg = fown_tg; #ifdef CONFIG_AUDIT landlock_file(file)->fown_layer = fown_layer; #endif /* CONFIG_AUDIT*/ /* May be called in an RCU read-side critical section. */ landlock_put_ruleset_deferred(prev_dom); + put_pid(prev_tg); } static void hook_file_free_security(struct file *file) { + put_pid(landlock_file(file)->fown_tg); landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain); } diff --git a/security/landlock/fs.h b/security/landlock/fs.h index bf9948941f2f..911b83669e20 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -78,6 +78,16 @@ struct landlock_file_security { * euid. */ struct landlock_cred_security fown_subject; + /** + * @fown_tg: Thread group of the task that set the file owner, pinned + * while @fown_subject holds a domain. It lets + * hook_file_send_sigiotask() always allow a SIGIO delivered to the + * owner's own process -- e.g. the thread-group leader reached through a + * process-group owner -- matching the same-process exemption of + * hook_task_kill(). NULL when no domain is recorded. Protected by + * file->f_owner->lock, like @fown_subject. + */ + struct pid *fown_tg; }; #ifdef CONFIG_AUDIT diff --git a/security/landlock/task.c b/security/landlock/task.c index 6d46042132ce..7ddf211f75c3 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -411,6 +411,17 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, if (!subject->domain) return 0; + /* + * Always allow delivery to the file owner's own process, including a + * thread-group leader reached through a process-group owner. This + * mirrors hook_task_kill()'s same-process exemption and preserves the + * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals + * between threads of the same process"), which the registration-time + * check cannot honor for a process-group target. + */ + if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg) + return 0; + scoped_guard(rcu) { is_scoped = domain_is_scoped(subject->domain, From 76579d09beedaffe7fe76e9c05644f73983e1ceb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:17:05 +0000 Subject: [PATCH 3561/5214] selftests/landlock: Test SCOPE_SIGNAL on the SIGIO/fowner pgid path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add regression tests for the LANDLOCK_SCOPE_SIGNAL handling of the asynchronous SIGIO delivery path (fcntl(F_SETOWN)) with a process-group owner. sigio_to_pgid_members covers the bypass: a sandboxed process at the head of its process group's PGID hlist (the default after fork()) arms F_SETOWN(-pgrp) + O_ASYNC and triggers the fan-out; the in-domain owner must be signaled (proving the trigger fired) while the non-sandboxed member of the group, outside the domain, must not. sigio_to_pgid_self covers the same-process guarantee: the owner is registered from a sandboxed non-leader thread, whose domain differs from the thread-group leader the kernel signals for a process-group owner. That leader belongs to the owner's own process and must still be signaled. Without the fix the first test sees the out-of-domain member signaled and the second sees the owner's own leader denied. Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/43370e89f7a896a583bf33d1cd171d02630e61bf.1780614610.git.hexlabsecurity@proton.me [mic: Fix comment] Signed-off-by: Mickaël Salaün --- .../selftests/landlock/scoped_signal_test.c | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index d8bf33417619..f24f2c28f62e 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -559,4 +559,186 @@ TEST_F(fown, sigurg_socket) _metadata->exit_code = KSFT_FAIL; } +/* + * Checks that LANDLOCK_SCOPE_SIGNAL is enforced on the asynchronous SIGIO + * delivery path (fcntl(F_SETOWN)) when the file owner is a process group. + * + * A sandboxed process sitting at the head of its process group's PID hlist (the + * default position right after fork()) used to escape the fcntl(F_SETOWN, + * -pgrp) domain recording: pid_task(pgrp, PIDTYPE_PGID) resolved to the process + * itself, so the same-thread-group exemption skipped recording its Landlock + * domain. At SIGIO time that domain was then unset and the signal fanned out + * to every group member, including non-sandboxed processes outside the domain. + */ +TEST(sigio_to_pgid_members) +{ + int trigger[2], sync_child[2]; + char buf; + pid_t child; + int status, i; + + drop_caps(_metadata); + + /* + * Isolates the test in its own process group so the SIGIO fan-out stays + * bounded to this parent and the child forked below. + */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed parent is the protected (out-of-domain) target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(sync_child, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + /* + * The child inherits the parent's new process group and, just + * attached with hlist_add_head_rcu(), is now the head of the + * pgid hlist: this is the case that used to skip the recording. + */ + EXPECT_EQ(0, close(sync_child[0])); + + /* In-domain positive control: the child must be signaled. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); + + /* Owns the SIGIO source for the whole process group. */ + ASSERT_EQ(0, fcntl(trigger[0], F_SETSIG, SIGURG)); + ASSERT_EQ(0, fcntl(trigger[0], F_SETOWN, -getpgrp())); + ASSERT_EQ(0, fcntl(trigger[0], F_SETFL, O_ASYNC)); + + /* Fans SIGURG out to every member of the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + /* + * The sandboxed child is in its own domain and must always be + * signaled: this proves the SIGIO actually fired. + */ + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + EXPECT_EQ(1, signal_received); + + ASSERT_EQ(1, write(sync_child[1], ".", 1)); + EXPECT_EQ(0, close(sync_child[1])); + + _exit(_metadata->exit_code); + return; + } + EXPECT_EQ(0, close(sync_child[1])); + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); + + /* Waits for the child to generate the SIGIO. */ + ASSERT_EQ(1, read(sync_child[0], &buf, 1)); + EXPECT_EQ(0, close(sync_child[0])); + + /* Lets a delivered-but-pending signal run our handler, if any. */ + for (i = 0; i < 100 && !signal_received; i++) + usleep(1000); + + /* + * SCOPE_SIGNAL must block the fan-out to this non-sandboxed parent, + * which is outside the child's Landlock domain. Before the fix the + * parent was signaled here. + */ + EXPECT_EQ(0, signal_received); + + ASSERT_EQ(child, waitpid(child, &status, 0)); + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + +static void *thread_setown_scoped(void *arg) +{ + const int fd = *(int *)arg; + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + + /* Sandboxes only this non-leader thread (no thread syncing). */ + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) + return (void *)THREAD_ERROR; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || + landlock_restrict_self(ruleset_fd, 0)) { + close(ruleset_fd); + return (void *)THREAD_ERROR; + } + close(ruleset_fd); + + /* Makes this process group own the SIGIO source. */ + if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || + fcntl(fd, F_SETFL, O_ASYNC)) + return (void *)THREAD_ERROR; + + return (void *)THREAD_SUCCESS; +} + +/* + * Checks that the SIGIO fan-out is still delivered to the file owner's own + * process when fcntl(F_SETOWN, -pgrp) was issued from a sandboxed non-leader + * thread. + * + * The Landlock domain is recorded for a process-group owner (so out-of-domain + * members stay blocked, see sigio_to_pgid_members), but the kernel signals a + * process group through its members' thread-group leaders. Here the leader is + * not sandboxed and thus has a different domain than the registering thread, so + * the registration-time check cannot tell that it belongs to the owner's own + * process. hook_file_send_sigiotask() must recognize it through the recorded + * thread group and allow the delivery, matching the same-process guarantee of + * commit 18eb75f3af40. Without that exemption the leader is wrongly denied and + * never signaled. + */ +TEST(sigio_to_pgid_self) +{ + int trigger[2]; + pthread_t thread; + enum thread_return ret = THREAD_INVALID; + int i; + + drop_caps(_metadata); + + /* Bounds the SIGIO fan-out to this process. */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed thread-group leader is the SIGIO target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + + /* + * Registers the process-group fowner from a sibling thread that + * sandboxes only itself, so its domain differs from the leader's. + */ + ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, + &trigger[0])); + ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(THREAD_SUCCESS, ret); + + /* Fans SIGURG out to the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + + /* + * Same-process delivery must always be allowed, even though the owner + * was registered from a sandboxed sibling thread. + */ + EXPECT_EQ(1, signal_received); + + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); +} + TEST_HARNESS_MAIN From 0302cd72fe196aee933e3fb76f6d175d1ab0e843 Mon Sep 17 00:00:00 2001 From: Maximilian Heyne Date: Fri, 29 May 2026 20:03:41 +0000 Subject: [PATCH 3562/5214] selftests/landlock: Explicitly disable audit in teardowns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm seeing sporadic selftest failures, such as # RUN scoped_audit.connect_to_child ... # scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8) # connect_to_child: Test failed # FAIL scoped_audit.connect_to_child not ok 19 scoped_audit.connect_to_child This seems similar to what commit 3647a4977fb73d ("selftests/landlock: Drain stale audit records on init") tried to fix. However, the added drain loop is not effective. When setting the AUDIT_STATUS_PID, the kauditd_thread is woken up starting to send messages from the hold queue to the netlink. Depending on scheduling of this kthread not all messages might be send via the netlink in the 1 us interval. Therefore, instead of trying to drain the queue, let's just disable audit when running non-audit tests or more precisely disable it after audit-tests. This way we won't generate any new audit message that could interfere with the other tests. The comment saying that on process exit audit will be disabled is wrong. The closed file descriptor just causes an auditd_reset(), not a disablement. So future messages will be queued in the hold queue. Cc: stable@vger.kernel.org Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Signed-off-by: Maximilian Heyne Link: https://patch.msgid.link/20260529-welsh-nagoya-b4d9ca60@mheyne-amazon [mic: Fix FD leak, update subject, call audit_cleanup() in audit_exec teardown] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/audit.h | 21 +++++++------------ tools/testing/selftests/landlock/audit_test.c | 4 +--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 936fe20f020e..f45fdef35681 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -553,10 +553,9 @@ static int audit_init_filter_exe(struct audit_filter *filter, const char *path) static int audit_cleanup(int audit_fd, struct audit_filter *filter) { struct audit_filter new_filter; + int err = 0; if (audit_fd < 0 || !filter) { - int err; - /* * Simulates audit_init_with_exe_filter() when called from * FIXTURE_TEARDOWN_PARENT(). @@ -567,23 +566,19 @@ static int audit_cleanup(int audit_fd, struct audit_filter *filter) filter = &new_filter; err = audit_init_filter_exe(filter, NULL); - if (err) { - close(audit_fd); - return err; - } + if (err) + goto err_close; } /* Filters might not be in place. */ audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE); audit_filter_drop(audit_fd, AUDIT_DEL_RULE); - /* - * Because audit_cleanup() might not be called by the test auditd - * process, it might not be possible to explicitly set it. Anyway, - * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd - * process will exit. - */ - return close(audit_fd); + err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0); + +err_close: + close(audit_fd); + return err; } static int audit_init_with_exe_filter(struct audit_filter *filter) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 758cf2368281..bd9f207b36e4 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -850,10 +850,8 @@ FIXTURE_SETUP(audit_exec) FIXTURE_TEARDOWN(audit_exec) { set_cap(_metadata, CAP_AUDIT_CONTROL); - EXPECT_EQ(0, audit_filter_exe(self->audit_fd, &self->audit_filter, - AUDIT_DEL_RULE)); + EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter)); clear_cap(_metadata, CAP_AUDIT_CONTROL); - EXPECT_EQ(0, close(self->audit_fd)); } TEST_F(audit_exec, signal_and_open) From 0ce4243509d1580349dd0d50624036d6b097e958 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Tue, 9 Jun 2026 23:15:10 +0200 Subject: [PATCH 3563/5214] landlock: Fix unmarked concurrent access to socket family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Socket family is read (twice) in a context where the socket is not locked, so another thread can setsockopt(IPV6_ADDRFORM) to write it concurrently. Add needed READ_ONCE() annotation. Use the proper macro to access __sk_common.skc_family like everywhere else. Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260609211511.85630-1-matthieu@buffet.re Link: https://patch.msgid.link/20260609211511.85630-2-matthieu@buffet.re [mic: Squash two patches, move variable to ease backport, fix comment formatting] Signed-off-by: Mickaël Salaün --- security/landlock/net.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/security/landlock/net.c b/security/landlock/net.c index a38bdfcffc22..4ee4002a8f56 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -46,6 +46,7 @@ static int current_check_access_socket(struct socket *const sock, const int addrlen, access_mask_t access_request) { + unsigned short sock_family; __be16 port; struct layer_access_masks layer_masks = {}; const struct landlock_rule *rule; @@ -66,6 +67,12 @@ static int current_check_access_socket(struct socket *const sock, if (addrlen < offsetofend(typeof(*address), sa_family)) return -EINVAL; + /* + * The socket is not locked, so sk_family can change concurrently due to + * e.g. setsockopt(IPV6_ADDRFORM). + */ + sock_family = READ_ONCE(sock->sk->sk_family); + switch (address->sa_family) { case AF_UNSPEC: if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { @@ -102,7 +109,7 @@ static int current_check_access_socket(struct socket *const sock, * these checks, but it is safer to return a proper * error and test consistency thanks to kselftest. */ - if (sock->sk->__sk_common.skc_family == AF_INET) { + if (sock_family == AF_INET) { const struct sockaddr_in *const sockaddr = (struct sockaddr_in *)address; @@ -180,7 +187,7 @@ static int current_check_access_socket(struct socket *const sock, * check, but it is safer to return a proper error and test * consistency thanks to kselftest. */ - if (address->sa_family != sock->sk->__sk_common.skc_family && + if (address->sa_family != sock_family && address->sa_family != AF_UNSPEC) return -EINVAL; From 9a8ed15ce22472fe0363e33738b4317d06b13c3a Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:01 +0200 Subject: [PATCH 3564/5214] landlock: Add UDP bind() access control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for a first fine-grained UDP access right. LANDLOCK_ACCESS_NET_BIND_UDP controls the ability to set the local port of a UDP socket (via bind()). It will be useful for servers (to start receiving datagrams), and for some clients that need to use a specific source port (e.g. mDNS requires to use port 5353) For obvious performance concerns, access control is only enforced when configuring sockets, not when using them for common send/recv operations. Bump ABI to allow userspace to detect and use this new right. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-2-matthieu@buffet.re [mic: Fix comment formatting] Signed-off-by: Mickaël Salaün --- include/uapi/linux/landlock.h | 14 ++++++++++---- security/landlock/audit.c | 1 + security/landlock/limits.h | 2 +- security/landlock/net.c | 18 ++++++++++++------ security/landlock/syscalls.c | 2 +- tools/testing/selftests/landlock/base_test.c | 4 ++-- tools/testing/selftests/landlock/net_test.c | 5 +++-- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 10a346e55e95..f2927681e92d 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -200,10 +200,10 @@ struct landlock_net_port_attr { * (also used for IPv6), and within that range, on a per-socket basis * with ``setsockopt(IP_LOCAL_PORT_RANGE)``. * - * A Landlock rule with port 0 and the %LANDLOCK_ACCESS_NET_BIND_TCP - * right means that requesting to bind on port 0 is allowed and it will - * automatically translate to binding on a kernel-assigned ephemeral - * port. + * A Landlock rule with port 0 and the %LANDLOCK_ACCESS_NET_BIND_TCP or + * %LANDLOCK_ACCESS_NET_BIND_UDP right means that requesting to bind on + * port 0 is allowed and it will automatically translate to binding on a + * kernel-assigned ephemeral port. */ __u64 port; }; @@ -373,10 +373,16 @@ struct landlock_net_port_attr { * port. Support added in Landlock ABI version 4. * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect TCP sockets to the given * remote port. Support added in Landlock ABI version 4. + * + * And similarly for UDP port numbers: + * + * - %LANDLOCK_ACCESS_NET_BIND_UDP: Bind UDP sockets to the given local + * port. Support added in Landlock ABI version 10. */ /* clang-format off */ #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0) #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) +#define LANDLOCK_ACCESS_NET_BIND_UDP (1ULL << 2) /* clang-format on */ /** diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 8d0edf94037d..e676ebffeebe 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -45,6 +45,7 @@ static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS); static const char *const net_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_TCP)] = "net.bind_tcp", [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_TCP)] = "net.connect_tcp", + [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_UDP)] = "net.bind_udp", }; static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET); diff --git a/security/landlock/limits.h b/security/landlock/limits.h index b454ad73b15e..c0f30a4591b8 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -23,7 +23,7 @@ #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) -#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP +#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_BIND_UDP #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) diff --git a/security/landlock/net.c b/security/landlock/net.c index 4ee4002a8f56..f57fe2a44f0d 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -88,15 +88,17 @@ static int current_check_access_socket(struct socket *const sock, * inconsistencies and return -EINVAL if needed. */ return 0; - } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { + } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || + access_request == LANDLOCK_ACCESS_NET_BIND_UDP) { /* * Binding to an AF_UNSPEC address is treated * differently by IPv4 and IPv6 sockets. The socket's * family may change under our feet due to * setsockopt(IPV6_ADDRFORM), but that's ok: we either - * reject entirely or require - * %LANDLOCK_ACCESS_NET_BIND_TCP for the given port, so - * it cannot be used to bypass the policy. + * reject entirely for IPv6 or require + * %LANDLOCK_ACCESS_NET_BIND_TCP or + * %LANDLOCK_ACCESS_NET_BIND_UDP for IPv4, so it cannot + * be used to bypass the policy. * * IPv4 sockets map AF_UNSPEC to AF_INET for * retrocompatibility for bind accesses, only if the @@ -142,7 +144,8 @@ static int current_check_access_socket(struct socket *const sock, if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { audit_net.dport = port; audit_net.v4info.daddr = addr4->sin_addr.s_addr; - } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { + } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || + access_request == LANDLOCK_ACCESS_NET_BIND_UDP) { audit_net.sport = port; audit_net.v4info.saddr = addr4->sin_addr.s_addr; } else { @@ -164,7 +167,8 @@ static int current_check_access_socket(struct socket *const sock, if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { audit_net.dport = port; audit_net.v6info.daddr = addr6->sin6_addr; - } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { + } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || + access_request == LANDLOCK_ACCESS_NET_BIND_UDP) { audit_net.sport = port; audit_net.v6info.saddr = addr6->sin6_addr; } else { @@ -224,6 +228,8 @@ static int hook_socket_bind(struct socket *const sock, if (sk_is_tcp(sock->sk)) access_request = LANDLOCK_ACCESS_NET_BIND_TCP; + else if (sk_is_udp(sock->sk)) + access_request = LANDLOCK_ACCESS_NET_BIND_UDP; else return 0; diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index accfd2e5a0cd..d45469d5d464 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = { * If the change involves a fix that requires userspace awareness, also update * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version = 9; +const int landlock_abi_version = 10; /** * sys_landlock_create_ruleset - Create a new ruleset diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 30d37234086c..6c8113c2ded1 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -76,8 +76,8 @@ TEST(abi_version) const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, }; - ASSERT_EQ(9, landlock_create_ruleset(NULL, 0, - LANDLOCK_CREATE_RULESET_VERSION)); + ASSERT_EQ(10, landlock_create_ruleset(NULL, 0, + LANDLOCK_CREATE_RULESET_VERSION)); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, LANDLOCK_CREATE_RULESET_VERSION)); diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 0c256e7c8675..135b09fd1880 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -1326,11 +1326,12 @@ FIXTURE_TEARDOWN(mini) /* clang-format off */ -#define ACCESS_LAST LANDLOCK_ACCESS_NET_CONNECT_TCP +#define ACCESS_LAST LANDLOCK_ACCESS_NET_BIND_UDP #define ACCESS_ALL ( \ LANDLOCK_ACCESS_NET_BIND_TCP | \ - LANDLOCK_ACCESS_NET_CONNECT_TCP) + LANDLOCK_ACCESS_NET_CONNECT_TCP | \ + LANDLOCK_ACCESS_NET_BIND_UDP) /* clang-format on */ From e61247a2e694d17236149135b2d22f0f7d19578c Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:02 +0200 Subject: [PATCH 3565/5214] landlock: Add UDP send+connect access control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for a second fine-grained UDP access right. LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP controls the ability to set the remote port of a socket (via connect()) and to specify an explicit destination when sending a datagram, to override any remote peer set on a UDP socket (e.g. in sendto() or sendmsg()). It will be useful for applications that send datagrams, and for some servers too (those creating per-client sockets, which want to receive traffic only from a specific address). Similarly as for bind(), this access control is performed when configuring sockets, not in hot code paths. Add detection of when autobind is about to be required, and deny the operation if the process would not be allowed to call bind(0) explicitly. Autobind can only be performed in udp_lib_get_port() from code paths already controlled by LSM hooks: when connect()ing, sending a first datagram, and in some splice() EOF edge case which, afaiu, can only happen after a remote peer has been set. This invariant needs to be preserved to keep bind policies actually enforced. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-3-matthieu@buffet.re [mic: Add quick return for non-sandboxed tasks, fix sa_family dereferencing, fix comment formatting] Signed-off-by: Mickaël Salaün --- include/uapi/linux/landlock.h | 23 +++ security/landlock/audit.c | 2 + security/landlock/limits.h | 2 +- security/landlock/net.c | 148 +++++++++++++++++--- tools/testing/selftests/landlock/net_test.c | 5 +- 5 files changed, 160 insertions(+), 20 deletions(-) diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index f2927681e92d..811ec77f9105 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -378,11 +378,34 @@ struct landlock_net_port_attr { * * - %LANDLOCK_ACCESS_NET_BIND_UDP: Bind UDP sockets to the given local * port. Support added in Landlock ABI version 10. + * - %LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP: Set the remote port of UDP + * sockets to the given port, or send datagrams to the given remote port + * ignoring any destination pre-set on a socket. Support added in + * Landlock ABI version 10. + * + * .. note:: Setting a remote address or sending a first datagram + * auto-binds UDP sockets to an ephemeral local source port if not + * already bound. To allow this if both %LANDLOCK_ACCESS_NET_BIND_UDP + * and %LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP are handled, you need to + * either: + * + * - use a socket already bound to a port before the ruleset started + * being enforced; + * - or grant %LANDLOCK_ACCESS_NET_BIND_UDP on port 0, meaning "any + * port in the ephemeral port range"; + * - or grant %LANDLOCK_ACCESS_NET_BIND_UDP on a specific port, and + * call :manpage:`bind(2)` on that port before trying to + * :manpage:`connect(2)` or send datagrams. + * + * .. note:: Sending datagrams to an ``AF_UNSPEC`` destination address + * family is not supported for IPv6 UDP sockets: you will need to use a + * ``NULL`` address instead. */ /* clang-format off */ #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0) #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) #define LANDLOCK_ACCESS_NET_BIND_UDP (1ULL << 2) +#define LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP (1ULL << 3) /* clang-format on */ /** diff --git a/security/landlock/audit.c b/security/landlock/audit.c index e676ebffeebe..851647197a01 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -46,6 +46,8 @@ static const char *const net_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_TCP)] = "net.bind_tcp", [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_TCP)] = "net.connect_tcp", [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_UDP)] = "net.bind_udp", + [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)] = + "net.connect_send_udp", }; static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET); diff --git a/security/landlock/limits.h b/security/landlock/limits.h index c0f30a4591b8..a4d908b240a2 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -23,7 +23,7 @@ #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1) #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS) -#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_BIND_UDP +#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) diff --git a/security/landlock/net.c b/security/landlock/net.c index f57fe2a44f0d..942f856433c9 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -44,7 +44,8 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset, static int current_check_access_socket(struct socket *const sock, struct sockaddr *const address, const int addrlen, - access_mask_t access_request) + access_mask_t access_request, + bool connecting) { unsigned short sock_family; __be16 port; @@ -75,19 +76,50 @@ static int current_check_access_socket(struct socket *const sock, switch (address->sa_family) { case AF_UNSPEC: - if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { + if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP || + (access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP && + connecting)) { /* - * Connecting to an address with AF_UNSPEC dissolves - * the TCP association, which have the same effect as - * closing the connection while retaining the socket - * object (i.e., the file descriptor). As for dropping - * privileges, closing connections is always allowed. - * - * For a TCP access control system, this request is - * legitimate. Let the network stack handle potential - * inconsistencies and return -EINVAL if needed. + * Connecting to an address with AF_UNSPEC dissolves the + * remote association while retaining the socket object + * (i.e., the file descriptor). For TCP, it has the same + * effect as closing the connection. For UDP, it removes + * any preset remote address. As for dropping + * privileges, these actions are always allowed. Let + * the network stack handle potential inconsistencies + * and return -EINVAL if needed. */ return 0; + } else if (access_request == + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) { + if (sock_family == AF_INET6) { + /* + * We cannot allow sending UDP datagrams to an + * explicit AF_UNSPEC address on IPv6 sockets, + * even if AF_UNSPEC is treated as "no address" + * on such sockets (so it should always be + * allowed). That's because the socket's family + * can change under our feet (if another thread + * calls setsockopt(IPV6_ADDRFORM)) to IPv4, + * which would then treat AF_UNSPEC as AF_INET. + */ + audit_net.family = AF_UNSPEC; + audit_net.sk = sock->sk; + landlock_init_layer_masks( + subject->domain, access_request, + &layer_masks, LANDLOCK_KEY_NET_PORT); + landlock_log_denial( + subject, + &(struct landlock_request){ + .type = LANDLOCK_REQUEST_NET_ACCESS, + .audit.type = + LSM_AUDIT_DATA_NET, + .audit.u.net = &audit_net, + .access = access_request, + .layer_masks = &layer_masks, + }); + return -EACCES; + } } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || access_request == LANDLOCK_ACCESS_NET_BIND_UDP) { /* @@ -130,7 +162,11 @@ static int current_check_access_socket(struct socket *const sock, } else { WARN_ON_ONCE(1); } - /* Only for bind(AF_UNSPEC+INADDR_ANY) on IPv4 socket. */ + /* + * AF_UNSPEC is treated as AF_INET only in + * bind(AF_UNSPEC+INADDR_ANY) on IPv4 sockets and when sending + * to AF_UNSPEC addresses on IPv4 sockets. + */ fallthrough; case AF_INET: { const struct sockaddr_in *addr4; @@ -141,7 +177,8 @@ static int current_check_access_socket(struct socket *const sock, addr4 = (struct sockaddr_in *)address; port = addr4->sin_port; - if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { + if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP || + access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) { audit_net.dport = port; audit_net.v4info.daddr = addr4->sin_addr.s_addr; } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || @@ -164,7 +201,8 @@ static int current_check_access_socket(struct socket *const sock, addr6 = (struct sockaddr_in6 *)address; port = addr6->sin6_port; - if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { + if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP || + access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) { audit_net.dport = port; audit_net.v6info.daddr = addr6->sin6_addr; } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP || @@ -221,6 +259,44 @@ static int current_check_access_socket(struct socket *const sock, return -EACCES; } +static int current_check_autobind_udp_socket(struct socket *const sock) +{ + const struct access_masks bind_udp = { + .net = LANDLOCK_ACCESS_NET_BIND_UDP, + }; + struct sockaddr_storage port0 = {}; + unsigned short num; + bool slow; + + /* Quick return for non-Landlocked tasks. */ + if (!landlock_get_applicable_subject(current_cred(), bind_udp, NULL)) + return 0; + + /* + * On UDP sockets, if a local port has not already been bound, calling + * connect() or sending a first datagram has the side effect of + * autobinding an ephemeral port: we also have to check that the process + * would have had the right to bind(0) explicitly. Hold the socket lock + * around the inet_num read to exclude udp_lib_get_port()'s transient + * inet_num = snum write that is reverted to 0 on a failing reuseport + * bind. + */ + slow = lock_sock_fast(sock->sk); + num = inet_sk(sock->sk)->inet_num; + unlock_sock_fast(sock->sk, slow); + if (num != 0) + return 0; + + /* + * Construct a struct sockaddr* with port 0 to pretend the process tried + * to bind() on that address. + */ + port0.ss_family = READ_ONCE(sock->sk->sk_family); + + return current_check_access_socket(sock, (struct sockaddr *)&port0, + sizeof(port0), bind_udp.net, false); +} + static int hook_socket_bind(struct socket *const sock, struct sockaddr *const address, const int addrlen) { @@ -234,7 +310,7 @@ static int hook_socket_bind(struct socket *const sock, return 0; return current_check_access_socket(sock, address, addrlen, - access_request); + access_request, false); } static int hook_socket_connect(struct socket *const sock, @@ -242,19 +318,57 @@ static int hook_socket_connect(struct socket *const sock, const int addrlen) { access_mask_t access_request; + int ret = 0; if (sk_is_tcp(sock->sk)) access_request = LANDLOCK_ACCESS_NET_CONNECT_TCP; + else if (sk_is_udp(sock->sk)) + access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP; else return 0; - return current_check_access_socket(sock, address, addrlen, - access_request); + ret = current_check_access_socket(sock, address, addrlen, + access_request, true); + + /* + * connect()ing to an AF_UNSPEC address does not trigger an autobind and + * should never be restricted. + */ + if (ret == 0 && sk_is_udp(sock->sk) && + addrlen >= offsetofend(typeof(*address), sa_family) && + address->sa_family != AF_UNSPEC) + ret = current_check_autobind_udp_socket(sock); + + return ret; +} + +static int hook_socket_sendmsg(struct socket *const sock, + struct msghdr *const msg, const int size) +{ + struct sockaddr *const address = msg->msg_name; + const int addrlen = msg->msg_namelen; + access_mask_t access_request; + int ret = 0; + + if (sk_is_udp(sock->sk)) + access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP; + else + return 0; + + if (address != NULL) + ret = current_check_access_socket(sock, address, addrlen, + access_request, false); + + if (ret == 0) + ret = current_check_autobind_udp_socket(sock); + + return ret; } static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(socket_bind, hook_socket_bind), LSM_HOOK_INIT(socket_connect, hook_socket_connect), + LSM_HOOK_INIT(socket_sendmsg, hook_socket_sendmsg), }; __init void landlock_add_net_hooks(void) diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 135b09fd1880..23d860e76372 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -1326,12 +1326,13 @@ FIXTURE_TEARDOWN(mini) /* clang-format off */ -#define ACCESS_LAST LANDLOCK_ACCESS_NET_BIND_UDP +#define ACCESS_LAST LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP #define ACCESS_ALL ( \ LANDLOCK_ACCESS_NET_BIND_TCP | \ LANDLOCK_ACCESS_NET_CONNECT_TCP | \ - LANDLOCK_ACCESS_NET_BIND_UDP) + LANDLOCK_ACCESS_NET_BIND_UDP | \ + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) /* clang-format on */ From 42afa5c467239642ca34efd7d76d034609abbaf6 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:03 +0200 Subject: [PATCH 3566/5214] selftests/landlock: Add tests for UDP bind/connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make basic changes to the existing bind() and connect() test suite to cover UDP restriction. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-4-matthieu@buffet.re [mic: Update audit.connect_bound, fix comment formatting] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/net_test.c | 541 ++++++++++++++++---- 1 file changed, 435 insertions(+), 106 deletions(-) diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 23d860e76372..72e6fbdc2ea2 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -35,6 +35,7 @@ enum sandbox_type { NO_SANDBOX, /* This may be used to test rules that allow *and* deny accesses. */ TCP_SANDBOX, + UDP_SANDBOX, }; static int set_service(struct service_fixture *const srv, @@ -93,23 +94,53 @@ static bool prot_is_tcp(const struct protocol_variant *const prot) (prot->protocol == IPPROTO_TCP || prot->protocol == IPPROTO_IP); } +static bool prot_is_udp(const struct protocol_variant *const prot) +{ + return (prot->domain == AF_INET || prot->domain == AF_INET6) && + prot->type == SOCK_DGRAM && + (prot->protocol == IPPROTO_UDP || prot->protocol == IPPROTO_IP); +} + static bool is_restricted(const struct protocol_variant *const prot, const enum sandbox_type sandbox) { if (sandbox == TCP_SANDBOX) return prot_is_tcp(prot); + else if (sandbox == UDP_SANDBOX) + return prot_is_udp(prot); return false; } static int socket_variant(const struct service_fixture *const srv) { + /* Arbitrary value just to not block other tests indefinitely. */ + const struct timeval timeout = { + .tv_sec = 0, + .tv_usec = 100000, + }; + int sockfd; int ret; - ret = socket(srv->protocol.domain, srv->protocol.type | SOCK_CLOEXEC, - srv->protocol.protocol); - if (ret < 0) + sockfd = socket(srv->protocol.domain, srv->protocol.type | SOCK_CLOEXEC, + srv->protocol.protocol); + if (sockfd < 0) return -errno; - return ret; + + ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout)); + if (ret != 0) { + ret = -errno; + close(sockfd); + return ret; + } + ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(timeout)); + if (ret != 0) { + ret = -errno; + close(sockfd); + return ret; + } + return sockfd; } #ifndef SIN6_LEN_RFC2133 @@ -271,10 +302,9 @@ FIXTURE_VARIANT(protocol) FIXTURE_SETUP(protocol) { - const struct protocol_variant prot_unspec = { - .domain = AF_UNSPEC, - .type = SOCK_STREAM, - }; + struct protocol_variant prot_unspec = variant->prot; + + prot_unspec.domain = AF_UNSPEC; disable_caps(_metadata); @@ -510,6 +540,92 @@ FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_unix_datagram) { }, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv4_udp1) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET, + .type = SOCK_DGRAM, + .protocol = IPPROTO_UDP, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv4_udp2) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET, + .type = SOCK_DGRAM, + /* IPPROTO_IP == 0 */ + .protocol = IPPROTO_IP, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv6_udp1) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET6, + .type = SOCK_DGRAM, + .protocol = IPPROTO_UDP, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv6_udp2) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET6, + .type = SOCK_DGRAM, + /* IPPROTO_IP == 0 */ + .protocol = IPPROTO_IP, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv4_tcp) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET, + .type = SOCK_STREAM, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_ipv6_tcp) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET6, + .type = SOCK_STREAM, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_unix_stream) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_UNIX, + .type = SOCK_STREAM, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_unix_datagram) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_UNIX, + .type = SOCK_DGRAM, + }, +}; + static void test_bind_and_connect(struct __test_metadata *const _metadata, const struct service_fixture *const srv, const bool deny_bind, const bool deny_connect) @@ -602,7 +718,7 @@ static void test_bind_and_connect(struct __test_metadata *const _metadata, ret = connect_variant(connect_fd, srv); if (deny_connect) { EXPECT_EQ(-EACCES, ret); - } else if (deny_bind) { + } else if (deny_bind && srv->protocol.type == SOCK_STREAM) { /* No listening server. */ EXPECT_EQ(-ECONNREFUSED, ret); } else { @@ -641,18 +757,25 @@ static void test_bind_and_connect(struct __test_metadata *const _metadata, TEST_F(protocol, bind) { - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { + const __u64 bind_access = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const __u64 conn_access = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = bind_access | conn_access, }; - const struct landlock_net_port_attr tcp_bind_connect_p0 = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr bind_connect_p0 = { + .allowed_access = bind_access | conn_access, .port = self->srv0.port, }; - const struct landlock_net_port_attr tcp_connect_p1 = { - .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr connect_p1 = { + .allowed_access = conn_access, .port = self->srv1.port, }; int ruleset_fd; @@ -664,12 +787,26 @@ TEST_F(protocol, bind) /* Allows connect and bind for the first port. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_connect_p0, 0)); + &bind_connect_p0, 0)); /* Allows connect and denies bind for the second port. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_connect_p1, 0)); + &connect_p1, 0)); + + /* + * For UDP sockets, allows binding to ephemeral ports (required + * to connect or send a first datagram) + */ + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_net_port_attr bind_ephemeral = { + .allowed_access = bind_access, + .port = 0, + }; + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &bind_ephemeral, 0)); + } enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -691,18 +828,25 @@ TEST_F(protocol, bind) TEST_F(protocol, connect) { - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { + const __u64 bind_access = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const __u64 conn_access = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = bind_access | conn_access, }; - const struct landlock_net_port_attr tcp_bind_connect_p0 = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr bind_connect_p0 = { + .allowed_access = bind_access | conn_access, .port = self->srv0.port, }; - const struct landlock_net_port_attr tcp_bind_p1 = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + const struct landlock_net_port_attr bind_p1 = { + .allowed_access = bind_access, .port = self->srv1.port, }; int ruleset_fd; @@ -714,12 +858,26 @@ TEST_F(protocol, connect) /* Allows connect and bind for the first port. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_connect_p0, 0)); + &bind_connect_p0, 0)); /* Allows bind and denies connect for the second port. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_p1, 0)); + &bind_p1, 0)); + + /* + * For UDP sockets, allows binding to ephemeral ports (required + * to connect or send a first datagram) + */ + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_net_port_attr bind_ephemeral = { + .allowed_access = bind_access, + .port = 0, + }; + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &bind_ephemeral, 0)); + } enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -737,16 +895,20 @@ TEST_F(protocol, connect) TEST_F(protocol, bind_unspec) { + const __u64 bind_access = (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP, + .handled_access_net = bind_access, }; - const struct landlock_net_port_attr tcp_bind = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + const struct landlock_net_port_attr rule_bind = { + .allowed_access = bind_access, .port = self->srv0.port, }; int bind_fd, ret; - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { const int ruleset_fd = landlock_create_ruleset( &ruleset_attr, sizeof(ruleset_attr), 0); ASSERT_LE(0, ruleset_fd); @@ -754,7 +916,7 @@ TEST_F(protocol, bind_unspec) /* Allows bind. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind, 0)); + &rule_bind, 0)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); } @@ -782,7 +944,8 @@ TEST_F(protocol, bind_unspec) } EXPECT_EQ(0, close(bind_fd)); - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { const int ruleset_fd = landlock_create_ruleset( &ruleset_attr, sizeof(ruleset_attr), 0); ASSERT_LE(0, ruleset_fd); @@ -828,11 +991,21 @@ TEST_F(protocol, bind_unspec) TEST_F(protocol, connect_unspec) { - const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP, + const __u64 connect_right = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + const __u64 bind_right = (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const struct landlock_ruleset_attr ruleset_conn = { + .handled_access_net = connect_right, }; - const struct landlock_net_port_attr tcp_connect = { - .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_ruleset_attr ruleset_conn_bind = { + .handled_access_net = connect_right | bind_right, + }; + const struct landlock_net_port_attr rule_connect = { + .allowed_access = connect_right, .port = self->srv0.port, }; int bind_fd, client_fd, status; @@ -865,15 +1038,16 @@ TEST_F(protocol, connect_unspec) EXPECT_EQ(0, ret); } - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { const int ruleset_fd = landlock_create_ruleset( - &ruleset_attr, sizeof(ruleset_attr), 0); + &ruleset_conn, sizeof(ruleset_conn), 0); ASSERT_LE(0, ruleset_fd); /* Allows connect. */ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_connect, 0)); + &rule_connect, 0)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); } @@ -896,12 +1070,14 @@ TEST_F(protocol, connect_unspec) EXPECT_EQ(0, ret); } - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { const int ruleset_fd = landlock_create_ruleset( - &ruleset_attr, sizeof(ruleset_attr), 0); + &ruleset_conn_bind, sizeof(ruleset_conn_bind), + 0); ASSERT_LE(0, ruleset_fd); - /* Denies connect. */ + /* Denies connect and bind. */ enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); } @@ -975,6 +1151,13 @@ FIXTURE_VARIANT_ADD(ipv4, tcp_sandbox_with_tcp) { .type = SOCK_STREAM, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(ipv4, udp_sandbox_with_tcp) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .type = SOCK_STREAM, +}; + /* clang-format off */ FIXTURE_VARIANT_ADD(ipv4, no_sandbox_with_udp) { /* clang-format on */ @@ -989,6 +1172,13 @@ FIXTURE_VARIANT_ADD(ipv4, tcp_sandbox_with_udp) { .type = SOCK_DGRAM, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(ipv4, udp_sandbox_with_udp) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .type = SOCK_DGRAM, +}; + FIXTURE_SETUP(ipv4) { const struct protocol_variant prot = { @@ -1012,14 +1202,19 @@ TEST_F(ipv4, from_unix_to_inet) { int unix_stream_fd, unix_dgram_fd; - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { + const __u64 access_rights = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = access_rights, }; const struct landlock_net_port_attr tcp_bind_connect_p0 = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .allowed_access = access_rights, .port = self->srv0.port, }; int ruleset_fd; @@ -1680,6 +1875,7 @@ TEST_F(ipv4_tcp, with_fs) FIXTURE(port_specific) { struct service_fixture srv0; + struct service_fixture cli1; }; FIXTURE_VARIANT(port_specific) @@ -1699,7 +1895,7 @@ FIXTURE_VARIANT_ADD(port_specific, no_sandbox_with_ipv4) { }; /* clang-format off */ -FIXTURE_VARIANT_ADD(port_specific, sandbox_with_ipv4) { +FIXTURE_VARIANT_ADD(port_specific, tcp_sandbox_with_ipv4) { /* clang-format on */ .sandbox = TCP_SANDBOX, .prot = { @@ -1708,6 +1904,16 @@ FIXTURE_VARIANT_ADD(port_specific, sandbox_with_ipv4) { }, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(port_specific, udp_sandbox_with_ipv4) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET, + .type = SOCK_DGRAM, + }, +}; + /* clang-format off */ FIXTURE_VARIANT_ADD(port_specific, no_sandbox_with_ipv6) { /* clang-format on */ @@ -1719,7 +1925,7 @@ FIXTURE_VARIANT_ADD(port_specific, no_sandbox_with_ipv6) { }; /* clang-format off */ -FIXTURE_VARIANT_ADD(port_specific, sandbox_with_ipv6) { +FIXTURE_VARIANT_ADD(port_specific, tcp_sandbox_with_ipv6) { /* clang-format on */ .sandbox = TCP_SANDBOX, .prot = { @@ -1728,11 +1934,22 @@ FIXTURE_VARIANT_ADD(port_specific, sandbox_with_ipv6) { }, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(port_specific, udp_sandbox_with_ipv6) { + /* clang-format on */ + .sandbox = UDP_SANDBOX, + .prot = { + .domain = AF_INET6, + .type = SOCK_DGRAM, + }, +}; + FIXTURE_SETUP(port_specific) { disable_caps(_metadata); ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0)); + ASSERT_EQ(0, set_service(&self->cli1, variant->prot, 1)); setup_loopback(_metadata); }; @@ -1747,14 +1964,19 @@ TEST_F(port_specific, bind_connect_zero) uint16_t port; /* Adds a rule layer with bind and connect actions. */ - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { + const __u64 access_rights = + (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP + .handled_access_net = access_rights, }; - const struct landlock_net_port_attr tcp_bind_connect_zero = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr bind_connect_zero = { + .allowed_access = access_rights, .port = 0, }; int ruleset_fd; @@ -1766,7 +1988,7 @@ TEST_F(port_specific, bind_connect_zero) /* Checks zero port value on bind and connect actions. */ EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_connect_zero, 0)); + &bind_connect_zero, 0)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -1787,11 +2009,16 @@ TEST_F(port_specific, bind_connect_zero) ret = bind_variant(bind_fd, &self->srv0); EXPECT_EQ(0, ret); - EXPECT_EQ(0, listen(bind_fd, backlog)); + if (variant->prot.type == SOCK_STREAM) + EXPECT_EQ(0, listen(bind_fd, backlog)); /* Connects on port 0. */ ret = connect_variant(connect_fd, &self->srv0); - EXPECT_EQ(-ECONNREFUSED, ret); + if (variant->prot.type == SOCK_STREAM) { + EXPECT_EQ(-ECONNREFUSED, ret); + } else { + EXPECT_EQ(0, ret); + } /* Sets binded port for both protocol families. */ port = get_binded_port(bind_fd, &variant->prot); @@ -1815,23 +2042,35 @@ TEST_F(port_specific, bind_connect_1023) int bind_fd, connect_fd, ret; /* Adds a rule layer with bind and connect actions. */ - if (variant->sandbox == TCP_SANDBOX) { + if (variant->sandbox == TCP_SANDBOX || + variant->sandbox == UDP_SANDBOX) { + const __u64 bind_right = (variant->sandbox == TCP_SANDBOX ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const __u64 access_rights = + (variant->sandbox == TCP_SANDBOX ? + (LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP) : + (LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP + .handled_access_net = access_rights, }; /* A rule with port value less than 1024. */ - const struct landlock_net_port_attr tcp_bind_connect_low_range = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr bind_connect_low_range = { + .allowed_access = access_rights, .port = 1023, }; /* A rule with 1024 port. */ - const struct landlock_net_port_attr tcp_bind_connect = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + const struct landlock_net_port_attr bind_connect = { + .allowed_access = access_rights, .port = 1024, }; + /* A rule with cli1's port, to use as source port. */ + const struct landlock_net_port_attr srcport = { + .allowed_access = bind_right, + .port = self->cli1.port, + }; int ruleset_fd; ruleset_fd = landlock_create_ruleset(&ruleset_attr, @@ -1840,10 +2079,15 @@ TEST_F(port_specific, bind_connect_1023) ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_connect_low_range, 0)); + &bind_connect_low_range, 0)); ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &tcp_bind_connect, 0)); + &bind_connect, 0)); + if (variant->sandbox == UDP_SANDBOX) { + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &srcport, 0)); + } enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -1852,9 +2096,6 @@ TEST_F(port_specific, bind_connect_1023) bind_fd = socket_variant(&self->srv0); ASSERT_LE(0, bind_fd); - connect_fd = socket_variant(&self->srv0); - ASSERT_LE(0, connect_fd); - /* Sets address port to 1023 for both protocol families. */ set_port(&self->srv0, 1023); /* Binds on port 1023. */ @@ -1867,8 +2108,19 @@ TEST_F(port_specific, bind_connect_1023) ret = bind_variant(bind_fd, &self->srv0); clear_cap(_metadata, CAP_NET_BIND_SERVICE); EXPECT_EQ(0, ret); - EXPECT_EQ(0, listen(bind_fd, backlog)); + if (variant->prot.type == SOCK_STREAM) + EXPECT_EQ(0, listen(bind_fd, backlog)); + connect_fd = socket_variant(&self->srv0); + ASSERT_LE(0, connect_fd); + if (variant->prot.type == SOCK_DGRAM) { + /* + * We are about to connect(), but bind() is restricted, so for + * UDP sockets we need to use cli1's port as source port (the + * only one we are allowed to use). + */ + EXPECT_EQ(0, bind_variant(connect_fd, &self->cli1)); + } /* Connects on the binded port 1023. */ ret = connect_variant(connect_fd, &self->srv0); EXPECT_EQ(0, ret); @@ -1887,7 +2139,10 @@ TEST_F(port_specific, bind_connect_1023) /* Binds on port 1024. */ ret = bind_variant(bind_fd, &self->srv0); EXPECT_EQ(0, ret); - EXPECT_EQ(0, listen(bind_fd, backlog)); + if (variant->prot.type == SOCK_STREAM) + EXPECT_EQ(0, listen(bind_fd, backlog)); + if (variant->prot.type == SOCK_DGRAM) + EXPECT_EQ(0, bind_variant(connect_fd, &self->cli1)); /* Connects on the binded port 1024. */ ret = connect_variant(connect_fd, &self->srv0); @@ -1897,23 +2152,30 @@ TEST_F(port_specific, bind_connect_1023) EXPECT_EQ(0, close(bind_fd)); } -static int matches_log_tcp(const int audit_fd, const char *const blockers, - const char *const dir_addr, const char *const addr, - const char *const dir_port) +static int matches_auditlog(const int audit_fd, const char *const blockers, + const char *const dir_addr, const char *const addr, + const char *const dir_port) { - static const char log_template[] = REGEX_LANDLOCK_PREFIX + static const char log_with_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX " blockers=%s %s=%s %s=1024$"; + static const char log_without_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX + " blockers=%s"; /* * Max strlen(blockers): 16 * Max strlen(dir_addr): 5 * Max strlen(addr): 12 * Max strlen(dir_port): 4 */ - char log_match[sizeof(log_template) + 37]; + char log_match[sizeof(log_with_addrport_tmpl) + 37]; int log_match_len; - log_match_len = snprintf(log_match, sizeof(log_match), log_template, - blockers, dir_addr, addr, dir_port); + if (addr == NULL) + log_match_len = snprintf(log_match, sizeof(log_match), + log_without_addrport_tmpl, blockers); + else + log_match_len = snprintf(log_match, sizeof(log_match), + log_with_addrport_tmpl, blockers, + dir_addr, addr, dir_port); if (log_match_len > sizeof(log_match)) return -E2BIG; @@ -1924,6 +2186,7 @@ static int matches_log_tcp(const int audit_fd, const char *const blockers, FIXTURE(audit) { struct service_fixture srv0; + struct service_fixture srv1; struct audit_filter audit_filter; int audit_fd; }; @@ -1935,7 +2198,7 @@ FIXTURE_VARIANT(audit) }; /* clang-format off */ -FIXTURE_VARIANT_ADD(audit, ipv4) { +FIXTURE_VARIANT_ADD(audit, ipv4_tcp) { /* clang-format on */ .addr = "127\\.0\\.0\\.1", .prot = { @@ -1945,7 +2208,17 @@ FIXTURE_VARIANT_ADD(audit, ipv4) { }; /* clang-format off */ -FIXTURE_VARIANT_ADD(audit, ipv6) { +FIXTURE_VARIANT_ADD(audit, ipv4_udp) { + /* clang-format on */ + .addr = "127\\.0\\.0\\.1", + .prot = { + .domain = AF_INET, + .type = SOCK_DGRAM, + }, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit, ipv6_tcp) { /* clang-format on */ .addr = "::1", .prot = { @@ -1954,9 +2227,21 @@ FIXTURE_VARIANT_ADD(audit, ipv6) { }, }; +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit, ipv6_udp) { + /* clang-format on */ + .addr = "::1", + .prot = { + .domain = AF_INET6, + .type = SOCK_DGRAM, + }, +}; + FIXTURE_SETUP(audit) { ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0)); + ASSERT_EQ(0, set_service(&self->srv1, variant->prot, 1)); + setup_loopback(_metadata); set_cap(_metadata, CAP_AUDIT_CONTROL); @@ -1974,9 +2259,17 @@ FIXTURE_TEARDOWN(audit) TEST_F(audit, bind) { + const char *audit_evt = (variant->prot.type == SOCK_STREAM ? + "net\\.bind_tcp" : + "net\\.bind_udp"); + const __u64 access_rights = + (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = access_rights, }; struct audit_records records; int ruleset_fd, sock_fd; @@ -1990,8 +2283,8 @@ TEST_F(audit, bind) sock_fd = socket_variant(&self->srv0); ASSERT_LE(0, sock_fd); EXPECT_EQ(-EACCES, bind_variant(sock_fd, &self->srv0)); - EXPECT_EQ(0, matches_log_tcp(self->audit_fd, "net\\.bind_tcp", "saddr", - variant->addr, "src")); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "saddr", + variant->addr, "src")); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); @@ -2002,9 +2295,22 @@ TEST_F(audit, bind) TEST_F(audit, connect) { + const char *audit_evt = (variant->prot.type == SOCK_STREAM ? + "net\\.connect_tcp" : + "net\\.connect_send_udp"); + const __u64 bind_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const __u64 conn_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + const __u64 access_rights = bind_right | conn_right; const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = access_rights, + }; + const struct landlock_net_port_attr rule_connect_p1 = { + .allowed_access = conn_right, + .port = self->srv1.port, }; struct audit_records records; int ruleset_fd, sock_fd; @@ -2012,33 +2318,47 @@ TEST_F(audit, connect) ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule_connect_p1, 0)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); sock_fd = socket_variant(&self->srv0); ASSERT_LE(0, sock_fd); EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv0)); - EXPECT_EQ(0, matches_log_tcp(self->audit_fd, "net\\.connect_tcp", - "daddr", variant->addr, "dest")); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "daddr", + variant->addr, "dest")); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); EXPECT_EQ(1, records.domain); + if (variant->prot.type == SOCK_DGRAM) { + /* Check that autobind generates a denied bind event. */ + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv1)); + + EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", + NULL, NULL, NULL)); + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); + } + EXPECT_EQ(0, close(sock_fd)); } -static int matches_log_tcp_bound(int audit_fd, const char *const addr, - __u16 lport, __u16 dport) +static int matches_log_connect_bound(int audit_fd, const char *const blockers, + const char *const addr, __u16 lport, + __u16 dport) { static const char log_template[] = REGEX_LANDLOCK_PREFIX - " blockers=net\\.connect_tcp laddr=%s lport=%u daddr=%s dest=%u$"; - /* Slack for two addresses and two port numbers. */ - char log_match[sizeof(log_template) + 40]; + " blockers=%s laddr=%s lport=%u daddr=%s dest=%u$"; + /* Slack for the blockers, two addresses and two port numbers. */ + char log_match[sizeof(log_template) + 60]; int log_match_len; log_match_len = snprintf(log_match, sizeof(log_match), log_template, - addr, lport, addr, dport); + blockers, addr, lport, addr, dport); if (log_match_len > sizeof(log_match)) return -E2BIG; @@ -2051,14 +2371,22 @@ static int matches_log_tcp_bound(int audit_fd, const char *const addr, * from the bound socket (made available through audit_net.sk) in addition to * the connect sockaddr's daddr/dest. */ -TEST_F(audit, connect_tcp_bound) +TEST_F(audit, connect_bound) { + const __u64 bind_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const __u64 conn_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + const char *const audit_evt = (variant->prot.type == SOCK_STREAM ? + "net\\.connect_tcp" : + "net\\.connect_send_udp"); const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + .handled_access_net = bind_right | conn_right, }; const struct landlock_net_port_attr rule_bind = { - .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + .allowed_access = bind_right, .port = self->srv0.port, }; struct service_fixture srv_remote; @@ -2080,8 +2408,9 @@ TEST_F(audit, connect_tcp_bound) ASSERT_LE(0, sock_fd); EXPECT_EQ(0, bind_variant(sock_fd, &self->srv0)); EXPECT_EQ(-EACCES, connect_variant(sock_fd, &srv_remote)); - EXPECT_EQ(0, matches_log_tcp_bound(self->audit_fd, variant->addr, - self->srv0.port, srv_remote.port)); + EXPECT_EQ(0, matches_log_connect_bound(self->audit_fd, audit_evt, + variant->addr, self->srv0.port, + srv_remote.port)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); From cf393de90f93bcc962436062e0a6d3afc0285d03 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:04 +0200 Subject: [PATCH 3567/5214] selftests/landlock: Add tests for UDP send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests specific to UDP sendmsg() in the protocol_* variants to ensure behaviour is consistent across AF_INET, AF_INET6 and AF_UNIX. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-5-matthieu@buffet.re [mic: Fix comment formatting, rebase] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/net_test.c | 653 +++++++++++++++++++- 1 file changed, 652 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 72e6fbdc2ea2..fb244aaef86f 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -289,9 +289,163 @@ static int connect_variant(const int sock_fd, return connect_variant_addrlen(sock_fd, srv, get_addrlen(srv, false)); } +static int sendto_variant_addrlen(const int sock_fd, + const struct service_fixture *const srv, + const socklen_t addrlen, void *buf, + size_t len, size_t flags) +{ + const struct sockaddr *dst = NULL; + ssize_t ret; + + /* + * We never want our processes to be killed by SIGPIPE: we check return + * codes and errno, so that we have actual error messages. + */ + flags |= MSG_NOSIGNAL; + + if (srv != NULL) { + switch (srv->protocol.domain) { + case AF_UNSPEC: + case AF_INET: + dst = (const struct sockaddr *)&srv->ipv4_addr; + break; + + case AF_INET6: + dst = (const struct sockaddr *)&srv->ipv6_addr; + break; + + case AF_UNIX: + dst = (const struct sockaddr *)&srv->unix_addr; + break; + + default: + errno = EAFNOSUPPORT; + return -errno; + } + } + + ret = sendto(sock_fd, buf, len, flags, dst, addrlen); + if (ret < 0) + return -errno; + + /* errno is not set in cases of partial writes. */ + if (ret != len) + return -EINTR; + + return 0; +} + +static int sendto_variant(const int sock_fd, + const struct service_fixture *const srv, void *buf, + size_t len, size_t flags) +{ + socklen_t addrlen = 0; + + if (srv != NULL) + addrlen = get_addrlen(srv, false); + + return sendto_variant_addrlen(sock_fd, srv, addrlen, buf, len, flags); +} + +static int test_sendmsg(struct __test_metadata *const _metadata, + const struct protocol_variant *prot, int client_fd, + int server_fd, const struct service_fixture *srv, + bool bind_denied, bool send_denied) +{ + int ret; + socklen_t opt_len; + int sock_type; + int addr_family; + struct sockaddr_storage peer_addr = { 0 }; + bool has_remote_port; + bool needs_autobind; + char read_buf[1] = { 0 }; + + /* + * Prepare the test by inspecting the socket type and whether it has a + * local/remote address set (all of which determine the expected + * outcomes). + */ + opt_len = sizeof(sock_type); + ASSERT_EQ(0, getsockopt(client_fd, SOL_SOCKET, SO_TYPE, &sock_type, + &opt_len)); + opt_len = sizeof(addr_family); + ASSERT_EQ(0, getsockopt(client_fd, SOL_SOCKET, SO_DOMAIN, &addr_family, + &opt_len)); + opt_len = sizeof(peer_addr); + has_remote_port = (getpeername(client_fd, (struct sockaddr *)&peer_addr, + &opt_len) == 0); + needs_autobind = (addr_family == AF_INET || addr_family == AF_INET6) && + get_binded_port(client_fd, prot) == 0; + + /* First, check error code with truncated explicit address. */ + if (srv != NULL) { + ret = sendto_variant_addrlen( + client_fd, srv, get_addrlen(srv, true) - 1, "A", 1, 0); + if (sock_type == SOCK_STREAM && !has_remote_port) { + EXPECT_EQ(-EPIPE, ret) + { + return -1; + } + } else if (bind_denied && needs_autobind) { + EXPECT_EQ(-EACCES, ret) + { + return -1; + } + } else { + EXPECT_EQ(-EINVAL, ret) + { + return -1; + } + } + } + + /* With or without explicit destination address (srv can be NULL). */ + ret = sendto_variant(client_fd, srv, "B", 1, 0); + if (sock_type == SOCK_STREAM && !has_remote_port) { + EXPECT_EQ(-EPIPE, ret) + { + return -1; + } + } else if ((send_denied && srv != NULL) || + (bind_denied && needs_autobind)) { + ASSERT_EQ(-EACCES, ret) + { + return -1; + } + } else if (srv == NULL && !has_remote_port) { + if (addr_family == AF_UNIX) { + ASSERT_EQ(-ENOTCONN, ret) + { + return -1; + } + } else if (sock_type == SOCK_STREAM) { + ASSERT_EQ(-EPIPE, ret) + { + return -1; + } + } else { + ASSERT_EQ(-EDESTADDRREQ, ret) + { + return -1; + } + } + } else { + ASSERT_EQ(0, ret); + ASSERT_EQ(1, recv(server_fd, read_buf, 1, 0)); + ASSERT_EQ(read_buf[0], 'B') + { + return -1; + } + } + + return 0; +} + FIXTURE(protocol) { - struct service_fixture srv0, srv1, srv2, unspec_any0, unspec_srv0; + struct service_fixture srv0, srv1, srv2; + struct service_fixture unspec_any0, unspec_srv0, unspec_srv1; }; FIXTURE_VARIANT(protocol) @@ -313,6 +467,7 @@ FIXTURE_SETUP(protocol) ASSERT_EQ(0, set_service(&self->srv2, variant->prot, 2)); ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0)); + ASSERT_EQ(0, set_service(&self->unspec_srv1, prot_unspec, 1)); ASSERT_EQ(0, set_service(&self->unspec_any0, prot_unspec, 0)); self->unspec_any0.ipv4_addr.sin_addr.s_addr = htonl(INADDR_ANY); @@ -1126,6 +1281,441 @@ TEST_F(protocol, connect_unspec) EXPECT_EQ(0, close(bind_fd)); } +TEST_F(protocol, sendmsg_stream) +{ + int srv0_fd, tmp_fd, client_fd, res; + char read_buf[1] = { 0 }; + + /* + * Simple test for stream sockets: just deny all connect()/ + * send(explicit addr)/bind(), and make sure we don't interfere with any + * operation. + */ + if (variant->prot.type != SOCK_STREAM) + return; + + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0)); + ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0)); + ASSERT_EQ(0, listen(srv0_fd, backlog)); + + /* Send on a non-connected socket. */ + res = sendto_variant(client_fd, NULL, "A", 1, 0); + if (variant->prot.domain == AF_UNIX) { + EXPECT_EQ(-ENOTCONN, res); + } else { + EXPECT_EQ(-EPIPE, res); + } + + /* Send to a truncated (invalid) address on a non-connected socket. */ + res = sendto_variant_addrlen(client_fd, &self->srv0, + get_addrlen(&self->srv0, true) - 1, "B", 1, + 0); + if (variant->prot.domain == AF_UNIX) { + EXPECT_EQ(-EOPNOTSUPP, res); + } else { + EXPECT_EQ(-EPIPE, res); + } + + /* Connect. */ + ASSERT_EQ(0, connect_variant(client_fd, &self->srv0)); + tmp_fd = accept(srv0_fd, NULL, 0); + ASSERT_LE(0, tmp_fd); + EXPECT_EQ(0, close(srv0_fd)); + srv0_fd = tmp_fd; + + /* Send without an explicit address. */ + EXPECT_EQ(0, sendto_variant(client_fd, NULL, "C", 1, 0)); + EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0)) + { + TH_LOG("recv() failed: %s", strerror(errno)); + } + EXPECT_EQ(read_buf[0], 'C'); + + /* Send to a truncated (invalid) address. */ + res = sendto_variant_addrlen(client_fd, &self->srv0, + get_addrlen(&self->srv0, true) - 1, "D", 1, + 0); + if (variant->prot.domain == AF_UNIX) { + EXPECT_EQ(-EISCONN, res); + } else { + ASSERT_EQ(0, res); + EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0)) + { + TH_LOG("recv() failed: %s", strerror(errno)); + } + EXPECT_EQ(read_buf[0], 'D'); + } + + /* Send to a valid but different address. */ + res = sendto_variant(client_fd, &self->srv1, "E", 1, 0); + if (variant->prot.domain == AF_UNIX) { + EXPECT_EQ(-EISCONN, res); + } else { + ASSERT_EQ(0, res); + EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0)) + { + TH_LOG("recv() failed: %s", strerror(errno)); + } + EXPECT_EQ(read_buf[0], 'E'); + } + + EXPECT_EQ(0, close(client_fd)); +} + +TEST_F(protocol, sendmsg_dgram) +{ + const bool restricted = is_restricted(&variant->prot, variant->sandbox); + int srv0_fd, srv1_fd, client_fd, child, status, res; + + if (variant->prot.type != SOCK_DGRAM) + return; + + /* Prepare server on port #0 to be allowed. */ + ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0)); + ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0)); + + /* And another server on port #1 to be denied. */ + ASSERT_LE(0, srv1_fd = socket_variant(&self->srv1)); + ASSERT_EQ(0, bind_variant(srv1_fd, &self->srv1)); + + /* + * Check that sockets connected before restrictions are not impacted in + * any way. + */ + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + ASSERT_EQ(0, connect_variant(client_fd, &self->srv0)); + if (variant->sandbox == UDP_SANDBOX) { + /* Deny all connect()/send(explicit addr)/bind(). */ + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + EXPECT_EQ(0, + test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, NULL, restricted, restricted)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, &self->srv0, restricted, + restricted)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv1_fd, &self->srv1, restricted, + restricted)); + EXPECT_EQ(0, close(client_fd)); + _exit(_metadata->exit_code); + } + EXPECT_EQ(child, waitpid(child, &status, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + /* + * Restrict connect/send, but not bind(). Then try sending with no + * destination (and no remote peer set), an allowed destination, then a + * denied destination. + */ + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + }; + const struct landlock_net_port_attr send_p0 = { + .allowed_access = + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + .port = self->srv0.port, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &send_p0, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + -1, NULL, false, false)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, &self->srv0, false, false)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv1_fd, &self->srv1, false, + restricted)); + EXPECT_EQ(0, close(client_fd)); + _exit(_metadata->exit_code); + return; + } + EXPECT_EQ(child, waitpid(child, &status, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + /* + * Rest of this test is just for autobind enforcement, which only exists + * in IP sockets. + */ + if (variant->prot.domain != AF_INET && variant->prot.domain != AF_INET6) + return; + + /* Restrict bind() to explicit calls with an arbitrary (non-0) port. */ + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + const uint16_t allowed_src_port = 42424; + struct service_fixture allowed_src; + + allowed_src = self->srv0; + set_port(&allowed_src, allowed_src_port); + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_BIND_UDP, + }; + const struct landlock_net_port_attr rule = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_UDP, + .port = allowed_src_port, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &rule, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + + /* Check that implicit bind(0) in sendmsg() is denied. */ + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, &self->srv0, restricted, + false)); + + /* Same thing for autobind in connect(). */ + res = connect_variant(client_fd, &self->srv0); + if (restricted) { + EXPECT_EQ(-EACCES, res); + } else { + EXPECT_EQ(0, res); + } + EXPECT_EQ(0, close(client_fd)); + + /* Make sendmsg() work by explicitly binding to the only allowed port. */ + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + EXPECT_EQ(0, bind_variant(client_fd, &allowed_src)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, &self->srv0, restricted, + false)); + EXPECT_EQ(0, close(client_fd)); + + /* Make connect() work by explicitly binding to the only allowed port. */ + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + EXPECT_EQ(0, bind_variant(client_fd, &allowed_src)); + EXPECT_EQ(0, connect_variant(client_fd, &self->srv0)); + EXPECT_EQ(0, close(client_fd)); + + _exit(_metadata->exit_code); + return; + } + EXPECT_EQ(child, waitpid(child, &status, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + /* + * Check that %LANDLOCK_ACCESS_NET_BIND_UDP on port 0 allows implicit + * autobinds. + */ + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_BIND_UDP, + }; + const struct landlock_net_port_attr rule = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_UDP, + .port = 0, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, + LANDLOCK_RULE_NET_PORT, + &rule, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd, + srv0_fd, &self->srv0, false, false)); + EXPECT_EQ(0, close(client_fd)); + _exit(_metadata->exit_code); + } + EXPECT_EQ(child, waitpid(child, &status, 0)); + EXPECT_EQ(1, WIFEXITED(status)); + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); +} + +TEST_F(protocol, sendmsg_unspec) +{ + const bool restricted = is_restricted(&variant->prot, variant->sandbox); + int client_fd, srv0_fd, srv1_fd, res; + char read_buf[1] = { 0 }; + + /* + * We already test for the absence of influence on sendmsg for other + * socket types and other address families, there's no point in adapting + * this test for stream sockets too. + */ + if (variant->prot.type != SOCK_DGRAM) + return; + + /* Prepare client of the right family. */ + ASSERT_LE(0, client_fd = socket_variant(&self->srv0)); + + /* Prepare server on port #0 to be allowed. */ + ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0)); + ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0)); + + /* And another server on port #1 to be denied. */ + ASSERT_LE(0, srv1_fd = socket_variant(&self->srv1)); + ASSERT_EQ(0, bind_variant(srv1_fd, &self->srv1)); + + if (variant->sandbox == UDP_SANDBOX) { + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + }; + const struct landlock_net_port_attr rule = { + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + .port = self->srv0.port, + }; + const int ruleset_fd = landlock_create_ruleset( + &ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, + landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + } + + /* Explicit AF_UNSPEC address but truncated. */ + EXPECT_EQ(-EINVAL, sendto_variant_addrlen( + client_fd, &self->unspec_srv0, + get_addrlen(&self->unspec_srv0, true) - 1, + "A", 1, 0)); + + /* + * Explicit AF_UNSPEC address, should be treated as AF_INET by IPv4 + * sockets (and thus map to srv0, allowed), but be denied by IPv6 + * sockets. + */ + res = sendto_variant(client_fd, &self->unspec_srv0, "B", 1, 0); + if (variant->prot.domain == AF_INET6) { + if (restricted) { + /* Always denied on IPv6 socket. */ + EXPECT_EQ(-EACCES, res); + } else { + /* IPv6 sockets treat AF_UNSPEC as a NULL address. */ + EXPECT_EQ(-EDESTADDRREQ, res); + } + } else if (variant->prot.domain == AF_INET) { + ASSERT_EQ(0, res); + EXPECT_EQ(1, read(srv0_fd, read_buf, 1)) + { + TH_LOG("read() failed: %s", strerror(errno)); + } + EXPECT_EQ(read_buf[0], 'B'); + } else { + /* Unix sockets don't accept AF_UNSPEC. */ + EXPECT_EQ(-EINVAL, res); + } + + /* + * Explicit AF_UNSPEC address, should be treated as AF_INET on IPv4 + * sockets (and thus map to srv1, denied), and be denied on IPv6 sockets + * as always. + */ + res = sendto_variant(client_fd, &self->unspec_srv1, "C", 1, 0); + if (variant->prot.domain == AF_INET6) { + if (restricted) { + /* Always denied on IPv6 socket. */ + EXPECT_EQ(-EACCES, res); + } else { + /* IPv6 sockets treat AF_UNSPEC as a NULL address. */ + EXPECT_EQ(-EDESTADDRREQ, res); + } + } else if (variant->prot.domain == AF_INET) { + if (restricted) { + /* Sending to srv1 is not allowed, only srv0. */ + EXPECT_EQ(-EACCES, res); + } else { + ASSERT_EQ(0, res); + EXPECT_EQ(1, read(srv1_fd, read_buf, 1)) + { + TH_LOG("read() failed: %s", strerror(errno)); + } + EXPECT_EQ(read_buf[0], 'C'); + } + } else { + /* Unix sockets don't accept AF_UNSPEC. */ + EXPECT_EQ(-EINVAL, res); + } + + ASSERT_EQ(0, connect_variant(client_fd, &self->srv0)); + + /* Minimal explicit AF_UNSPEC address (just the sa_family_t field) */ + res = sendto_variant_addrlen(client_fd, &self->unspec_srv0, + get_addrlen(&self->unspec_srv0, true), "D", + 1, 0); + if (variant->prot.domain == AF_INET6) { + if (restricted) { + /* AF_UNSPEC is always denied in IPv6. */ + EXPECT_EQ(-EACCES, res); + } else { + /* + * IPv6 sockets treat AF_UNSPEC as a NULL address, + * falling back to the connected address. + */ + ASSERT_EQ(0, res); + EXPECT_EQ(1, read(srv0_fd, read_buf, 1)); + EXPECT_EQ(read_buf[0], 'D'); + } + } else { + /* + * IPv4 socket will expect a struct sockaddr_in, our address is + * considered truncated. And Unix sockets don't accept + * AF_UNSPEC at all. + */ + EXPECT_EQ(-EINVAL, res); + } +} + FIXTURE(ipv4) { struct service_fixture srv0, srv1; @@ -2187,6 +2777,7 @@ FIXTURE(audit) { struct service_fixture srv0; struct service_fixture srv1; + struct service_fixture unspec_srv0; struct audit_filter audit_filter; int audit_fd; }; @@ -2239,8 +2830,13 @@ FIXTURE_VARIANT_ADD(audit, ipv6_udp) { FIXTURE_SETUP(audit) { + struct protocol_variant prot_unspec = variant->prot; + + prot_unspec.domain = AF_UNSPEC; + ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0)); ASSERT_EQ(0, set_service(&self->srv1, variant->prot, 1)); + ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0)); setup_loopback(_metadata); @@ -2419,4 +3015,59 @@ TEST_F(audit, connect_bound) EXPECT_EQ(0, close(sock_fd)); } +TEST_F(audit, sendmsg) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP | + LANDLOCK_ACCESS_NET_BIND_UDP, + }; + const struct landlock_net_port_attr rule = { + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + .port = self->srv1.port, + }; + struct audit_records records; + int ruleset_fd; + int sock_fd; + + /* Sendmsg on stream sockets is never denied. */ + if (variant->prot.type != SOCK_DGRAM) + return; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv0); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv0, "A", 1, 0)); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp", + "daddr", variant->addr, "dest")); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(1, records.domain); + + /* Check that autobind generates a denied bind event. */ + EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv1, "A", 1, 0)); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", NULL, + NULL, NULL)); + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); + + EXPECT_EQ(-EACCES, + sendto_variant(sock_fd, &self->unspec_srv0, "B", 1, 0)); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp", + "daddr", NULL, "dest")); + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); + + EXPECT_EQ(0, close(sock_fd)); +} + TEST_HARNESS_MAIN From a1b0b278455a70e2567bdf77e6fda23438280987 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:05 +0200 Subject: [PATCH 3568/5214] samples/landlock: Add sandboxer UDP access control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add environment variables to control associated access rights: - LL_UDP_BIND - LL_UDP_CONNECT_SEND Each one takes a list of ports separated by colons, like other list options. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-6-matthieu@buffet.re Signed-off-by: Mickaël Salaün --- samples/landlock/sandboxer.c | 41 ++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index 66e56ae275c6..f44db2857bbf 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -62,6 +62,8 @@ static inline int landlock_restrict_self(const int ruleset_fd, #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" #define ENV_SCOPED_NAME "LL_SCOPED" #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG" +#define ENV_UDP_BIND_NAME "LL_UDP_BIND" +#define ENV_UDP_CONNECT_SEND_NAME "LL_UDP_CONNECT_SEND" #define ENV_DELIMITER ":" static int str2num(const char *numstr, __u64 *num_dst) @@ -301,7 +303,7 @@ static bool check_ruleset_scope(const char *const env_var, /* clang-format on */ -#define LANDLOCK_ABI_LAST 9 +#define LANDLOCK_ABI_LAST 10 #define XSTR(s) #s #define STR(s) XSTR(s) @@ -324,6 +326,11 @@ static const char help[] = "means an empty list):\n" "* " ENV_TCP_BIND_NAME ": ports allowed to bind (server)\n" "* " ENV_TCP_CONNECT_NAME ": ports allowed to connect (client)\n" + "* " ENV_UDP_BIND_NAME ": local UDP ports allowed to bind (server: " + "prepare to receive on port / client: set as source port)\n" + "* " ENV_UDP_CONNECT_SEND_NAME ": remote UDP ports allowed to connect " + "or send to (client: use as destination port / server: receive only from it)\n" + "(caution: sending requires being able to bind to a local source port)\n" "* " ENV_SCOPED_NAME ": actions denied on the outside of the landlock domain\n" " - \"a\" to restrict opening abstract unix sockets\n" " - \"s\" to restrict sending signals\n" @@ -336,6 +343,7 @@ static const char help[] = ENV_FS_RW_NAME "=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " ENV_TCP_BIND_NAME "=\"9418\" " ENV_TCP_CONNECT_NAME "=\"80:443\" " + ENV_UDP_CONNECT_SEND_NAME "=\"53\" " ENV_SCOPED_NAME "=\"a:s\" " "%1$s bash -i\n" "\n" @@ -356,7 +364,9 @@ int main(const int argc, char *const argv[], char *const *const envp) struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs = access_fs_rw, .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + LANDLOCK_ACCESS_NET_CONNECT_TCP | + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL, }; @@ -444,6 +454,13 @@ int main(const int argc, char *const argv[], char *const *const envp) /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */ ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX; + __attribute__((fallthrough)); + case 9: + /* Removes UDP support for ABI < 10 */ + ruleset_attr.handled_access_net &= + ~(LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */ fprintf(stderr, "Hint: You should update the running kernel " @@ -475,6 +492,18 @@ int main(const int argc, char *const argv[], char *const *const envp) ruleset_attr.handled_access_net &= ~LANDLOCK_ACCESS_NET_CONNECT_TCP; } + /* Removes UDP bind access control if not supported by a user. */ + env_port_name = getenv(ENV_UDP_BIND_NAME); + if (!env_port_name) { + ruleset_attr.handled_access_net &= + ~LANDLOCK_ACCESS_NET_BIND_UDP; + } + /* Removes UDP connect/send access control if not supported by a user. */ + env_port_name = getenv(ENV_UDP_CONNECT_SEND_NAME); + if (!env_port_name) { + ruleset_attr.handled_access_net &= + ~LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP; + } if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr)) return 1; @@ -519,6 +548,14 @@ int main(const int argc, char *const argv[], char *const *const envp) LANDLOCK_ACCESS_NET_CONNECT_TCP)) { goto err_close_ruleset; } + if (populate_ruleset_net(ENV_UDP_BIND_NAME, ruleset_fd, + LANDLOCK_ACCESS_NET_BIND_UDP)) { + goto err_close_ruleset; + } + if (populate_ruleset_net(ENV_UDP_CONNECT_SEND_NAME, ruleset_fd, + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)) { + goto err_close_ruleset; + } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("Failed to restrict privileges"); From 4483efe4f21510b30c24bc97d9fd0e8feab94125 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Oct 2025 23:46:19 -0700 Subject: [PATCH 3569/5214] apparmor: fix shadowing of plabel that prevents cache from being updated Unfortunately the plabel was being shadowed by an unused local var. This didn't affect the mediation check but did cauase the cache to not correctly be updated resulting in extra mediation checks. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen --- security/apparmor/af_unix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index fdb4a9f212c3..09ce3f6da20c 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -758,11 +758,10 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, unix_fs_perm(op, request, subj_cred, label, is_unix_fs(peer_sk) ? &peer_path : NULL)); } else if (!is_sk_fs) { - struct aa_label *plabel; struct aa_sk_ctx *pctx = aa_sock(peer_sk); rcu_read_lock(); - plabel = aa_get_label_rcu(&pctx->label); + plabel = aa_get_newest_label(pctx->label); rcu_read_unlock(); /* no fs check of aa_unix_peer_perm because conditions above * ensure they will never be done From b1aea2c1960771a276d7e68c7424168eccd0c3da Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:25:38 -0700 Subject: [PATCH 3570/5214] apparmor: fix race in unix socket mediation when peer_path is used The holding a reference to the peer_sk is not enough to ensure access to the peer sk path. Accessing the path outside of the state lock allows for a race with unix_release_sock(). Fix this by taking the state lock and getting a reference to the path under lock. Ideally for connected sockets we would cache this information so we don't have to take the lock here. But for now just fix the race. Fixes: bc6e5f6933b8e ("apparmor: Remove use of the double lock") Signed-off-by: John Johansen --- security/apparmor/af_unix.c | 58 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 09ce3f6da20c..23753cb2096e 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -748,41 +748,47 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, if (!peer_sk) goto out; - peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + if (!is_sk_fs) { + bool is_peer_fs = is_unix_fs(peer_sk); - struct path peer_path; + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + if (is_peer_fs) { + struct path peer_path; - peer_path = unix_sk(peer_sk)->path; - if (!is_sk_fs && is_unix_fs(peer_sk)) { - last_error(error, - unix_fs_perm(op, request, subj_cred, label, - is_unix_fs(peer_sk) ? &peer_path : NULL)); - } else if (!is_sk_fs) { - struct aa_sk_ctx *pctx = aa_sock(peer_sk); + unix_state_lock(peer_sk); + peer_path = unix_sk(peer_sk)->path; + if (peer_path.dentry) + path_get(&peer_path); + unix_state_unlock(peer_sk); - rcu_read_lock(); - plabel = aa_get_newest_label(pctx->label); - rcu_read_unlock(); - /* no fs check of aa_unix_peer_perm because conditions above - * ensure they will never be done - */ - last_error(error, - xcheck(unix_peer_perm(subj_cred, label, op, + last_error(error, + unix_fs_perm(op, request, subj_cred, label, + &peer_path)); + if (peer_path.dentry) + path_put(&peer_path); + } else { + struct aa_sk_ctx *pctx = aa_sock(peer_sk); + + rcu_read_lock(); + plabel = aa_get_newest_label(pctx->label); + rcu_read_unlock(); + /* no fs check of aa_unix_peer_perm because conditions + * above ensure they will never be done + */ + last_error(error, + xcheck(unix_peer_perm(subj_cred, label, op, MAY_READ | MAY_WRITE, sock->sk, is_sk_fs ? &path : NULL, peer_addr, peer_addrlen, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - plabel), - unix_peer_perm(file->f_cred, plabel, op, + NULL, plabel), + unix_peer_perm(file->f_cred, plabel, op, MAY_READ | MAY_WRITE, peer_sk, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - addr, addrlen, + NULL, addr, addrlen, is_sk_fs ? &path : NULL, label))); - if (!error && !__aa_subj_label_is_cached(plabel, label)) - update_peer_ctx(peer_sk, pctx, label); + if (!error && !__aa_subj_label_is_cached(plabel, label)) + update_peer_ctx(peer_sk, pctx, label); + } } sock_put(peer_sk); From 6d25e7b47616cb2db43351210929c8f19dc305a3 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:59:51 -0700 Subject: [PATCH 3571/5214] apparmor: fix refcount leak when updating the sk_ctx Currently update_sk_ctx() transfers the plabel reference, unfortunately it is also unconditionally put in the caller. Ideally we would make the caller conditionally put the reference based on whether it was transferred but for now just fix the bug by getting a reference. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen --- security/apparmor/af_unix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 23753cb2096e..834a3b1c2f0a 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -674,9 +674,11 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label, old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock)); if (old == plabel) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); } else if (aa_label_is_subset(plabel, old)) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); rcu_assign_pointer(ctx->peer, aa_get_label(plabel)); aa_put_label(old); } /* else race or a subset - don't update */ From f86ee868fd54c372255519284e1c0f4f7707c045 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 17 Apr 2026 00:51:22 -0700 Subject: [PATCH 3572/5214] apparmor: add a conditional version of get_newest_label get_newest_label() will always return a refcount, on the profile it returns. However there are cases where we only need the refcount if the label is stale and get_newest_label() will return a different label. Optimize this by making the get/put happen conditionally, by keeping a flag indicating if the get was performed and a put is needed. Signed-off-by: John Johansen --- security/apparmor/include/label.h | 32 +++++++++++++++++++++++++++++++ security/apparmor/label.c | 22 ++++++++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 335f21930702..b5a722a47fd2 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -423,6 +423,38 @@ static inline struct aa_label *aa_get_newest_label(struct aa_label *l) return aa_get_label(l); } +/** + * aa_get_newest_label_condref - find the newest version of @l + * @l: the label to check for newer versions of + * @needput: returns whether the reference needs put + * + * Returns: refcounted newest version of @l taking into account + * replacement, renames and removals + * return @l. + */ +static inline struct aa_label *aa_get_newest_label_condref(struct aa_label *l, + bool *needput) +{ + if (l && unlikely(label_is_stale(l))) { + struct aa_label *tmp; + + AA_BUG(!l->proxy); + AA_BUG(!l->proxy->label); + /* BUG: only way this can happen is @l ref count and its + * replacement count have gone to 0 and are on their way + * to destruction. ie. we have a refcounting error + */ + tmp = aa_get_label_rcu(&l->proxy->label); + AA_BUG(!tmp); + + *needput = true; + return tmp; + } + + *needput = false; + return l; +} + static inline void aa_put_label(struct aa_label *l) { if (l) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 3a721fdf1833..a8850d118c9c 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -1176,22 +1176,21 @@ static struct aa_label *__label_find_merge(struct aa_labelset *ls, struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b) { struct aa_labelset *ls; - struct aa_label *label, *ar = NULL, *br = NULL; + struct aa_label *label; unsigned long flags; + bool a_needput, b_needput; AA_BUG(!a); AA_BUG(!b); - if (label_is_stale(a)) - a = ar = aa_get_newest_label(a); - if (label_is_stale(b)) - b = br = aa_get_newest_label(b); + a = aa_get_newest_label_condref(a, &a_needput); + b = aa_get_newest_label_condref(b, &b_needput); ls = labelset_of_merge(a, b); read_lock_irqsave(&ls->lock, flags); label = __label_find_merge(ls, a, b); read_unlock_irqrestore(&ls->lock, flags); - aa_put_label(ar); - aa_put_label(br); + aa_put_label_condref(a, a_needput); + aa_put_label_condref(b, b_needput); return label; } @@ -1228,9 +1227,10 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b, if (!label) { struct aa_label *new; + bool a_needput, b_needput; - a = aa_get_newest_label(a); - b = aa_get_newest_label(b); + a = aa_get_newest_label_condref(a, &a_needput); + b = aa_get_newest_label_condref(b, &b_needput); /* could use label_merge_len(a, b), but requires double * comparison for small savings @@ -1242,8 +1242,8 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b, label = label_merge_insert(new, a, b); label_free_or_put_new(label, new); out: - aa_put_label(a); - aa_put_label(b); + aa_put_label_condref(a, a_needput); + aa_put_label_condref(b, b_needput); } return label; From d62d9bfe050f44f772d05a32079dba3e3523ab2a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jun 2026 13:30:46 -0700 Subject: [PATCH 3573/5214] security/apparmor/apparmorfs.c: conditionally compile get_loaddata_common_ref() Some config did this: security/apparmor/apparmorfs.c:177:28: warning: 'get_loaddata_common_ref' defined but not used [-Wunused-function] 177 | static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) get_loaddata_common_ref() is only used if CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y. (Or of course move the function into that block if maintainers perfer) Fixes: 8e135b8aee5a0 ("apparmor: fix race between freeing data and fs accessing it") Cc: John Johansen Cc: Paul Moore Cc: James Morris Cc: "Serge E. Hallyn" Signed-off-by: Andrew Morton Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ededaf46f3ca..aeff3d2d863b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -174,6 +174,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) return NULL; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) { if (ref) @@ -181,6 +182,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) count)); return NULL; } +#endif static void aa_put_common_ref(struct aa_common_ref *ref) { From a58cafd38b46fb1a2220e2fbbcfe291ea75fa147 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Mon, 8 Jun 2026 14:36:31 +0800 Subject: [PATCH 3574/5214] apparmor: check label build before no_new_privs test aa_change_profile() builds a replacement label with fn_label_build_in_scope() before the no_new_privs subset check. The build helper can fail and return NULL or an ERR_PTR, but the result was passed to aa_label_is_unconfined_subset() before the existing IS_ERR_OR_NULL() check. Reuse the existing target-label build failure handling immediately after the build. This preserves the current audit handling while preventing the subset helper from dereferencing an invalid label. Fixes: e00b02bb6ac2a ("apparmor: move change_profile mediation to using labels") Signed-off-by: Ruoyu Wang Signed-off-by: John Johansen --- security/apparmor/domain.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index f02bf770f638..6748ac74b060 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -1527,6 +1527,8 @@ int aa_change_profile(const char *fqname, int flags) new = fn_label_build_in_scope(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); + if (IS_ERR_OR_NULL(new)) + goto build_fail; /* * no new privs prevents domain transitions that would * reduce restrictions. @@ -1545,16 +1547,8 @@ int aa_change_profile(const char *fqname, int flags) /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); - if (IS_ERR_OR_NULL(new)) { - info = "failed to build target label"; - if (!new) - error = -ENOMEM; - else - error = PTR_ERR(new); - new = NULL; - perms.allow = 0; - goto audit; - } + if (IS_ERR_OR_NULL(new)) + goto build_fail; error = aa_replace_current_label(new); } else { if (new) { @@ -1566,6 +1560,17 @@ int aa_change_profile(const char *fqname, int flags) aa_set_current_onexec(target, stack); } + goto audit; + +build_fail: + info = "failed to build target label"; + if (!new) + error = -ENOMEM; + else + error = PTR_ERR(new); + new = NULL; + perms.allow = 0; + audit: error = fn_for_each_in_scope(label, profile, aa_audit_file(subj_cred, From 654fe7505dc6889724d4094fa64f89991afabfc3 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:21:33 +0200 Subject: [PATCH 3575/5214] apparmor: aa_label_alloc use aa_label_free on alloc failure aa_label_alloc() allocates a secid before allocating or taking the label proxy. If the later proxy step fails, the error path only freed the label memory, leaking any resources initialized by aa_label_init(). Use aa_label_free() on the failure path so partially initialized labels release their secid and other label resources before the backing memory is freed. Fixes: f1bd904175e81 ("apparmor: add the base fns() for domain labels") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index a8850d118c9c..c60244ed96db 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -458,7 +458,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) return new; fail: - kfree(new); + aa_label_free(new); return NULL; } From e7501736405a39fa513625ba1f81909c847e4e70 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 17 Feb 2026 11:26:48 -0800 Subject: [PATCH 3576/5214] apparmor: enable differential encoding Differential encoding while present has not been made broadly available, pending further review and testing. Now that has happened advertise its availability to user space. Reviewed-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index aeff3d2d863b..252a7ca98a9e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2424,6 +2424,7 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = { static struct aa_sfs_entry aa_sfs_entry_policy[] = { AA_SFS_DIR("versions", aa_sfs_entry_versions), AA_SFS_FILE_BOOLEAN("set_load", 1), + AA_SFS_FILE_BOOLEAN("diff-encode", 1), /* number of out of band transitions supported */ AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), AA_SFS_FILE_U64("permstable32_version", 3), From 1c8a839442823ce5c627d645730d8c61d828aafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20B=C3=A9lair?= Date: Wed, 11 Feb 2026 14:19:32 +0100 Subject: [PATCH 3577/5214] apparmor: propagate -ENOMEM correctly in unpack_table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, if the `kvzalloc` in `unpack_table` fails, it returns NULL. This is masked by `aa_dfa_unpack` which interprets NULL as a -EPROTO, leading to confusing error messages in `apparmor_parser` [1]. The fixed behavior correctly propagates -ENOMEM on allocation failure. Link: https://gitlab.com/apparmor/apparmor/-/issues/592 Reviewed-by: Georgia Garcia Signed-off-by: Maxime Bélair Signed-off-by: John Johansen --- security/apparmor/match.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 3a2c6cf02b3c..d43ff34d705c 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -27,13 +27,13 @@ * @blob: data to unpack (NOT NULL) * @bsize: size of blob * - * Returns: pointer to table else NULL on failure + * Returns: pointer to table else ERR_PTR on failure * * NOTE: must be freed by kvfree (not kfree) */ static struct table_header *unpack_table(char *blob, size_t bsize) { - struct table_header *table = NULL; + struct table_header *table = ERR_PTR(-EPROTO); struct table_header th; size_t tsize; @@ -74,20 +74,21 @@ static struct table_header *unpack_table(char *blob, size_t bsize) else if (th.td_flags == YYTD_DATA32) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u32, __be32, get_unaligned_be32); - else - goto fail; + else { + kvfree(table); + table = ERR_PTR(-EPROTO); + goto out; + } /* if table was vmalloced make sure the page tables are synced * before it is used, as it goes live to all cpus. */ if (is_vmalloc_addr(table)) vm_unmap_aliases(); - } + } else + table = ERR_PTR(-ENOMEM); out: return table; -fail: - kvfree(table); - return NULL; } /** @@ -359,8 +360,11 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) while (size > 0) { table = unpack_table(data, size); - if (!table) + if (IS_ERR(table)) { + error = PTR_ERR(table); + table = NULL; goto fail; + } switch (table->td_id) { case YYTD_ID_ACCEPT: From 1adefbd0d5f5a2dafb3f1ee4537dc1db69fb29d4 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Thu, 12 Feb 2026 13:15:14 -0800 Subject: [PATCH 3578/5214] apparmor: use __label_make_stale in __aa_proxy_redirect The macro is equivalent to OR-ing in the bitflag manually, but using the macro consistently makes grepping for these occurrences easier. Reviewed-by: Georgia Garcia Signed-off-by: Ryan Lee Signed-off-by: John Johansen --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index c60244ed96db..3fd384d8c41a 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -83,7 +83,7 @@ void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new) tmp = rcu_dereference_protected(orig->proxy->label, &labels_ns(orig)->lock); rcu_assign_pointer(orig->proxy->label, aa_get_label(new)); - orig->flags |= FLAG_STALE; + __label_make_stale(orig); aa_put_label(tmp); } From ad213bbbc0e3e270ce7df2d9d80d4ce3826993d7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 1 Mar 2026 12:24:06 -0800 Subject: [PATCH 3579/5214] apparmor: fix rawdata_f_data implicit flex array rawdata_f_data has a blob of data that is allocated at its end but not explicitly declared. Makes sure it is correctly declared as a flex_rray. Fixes: 63c16c3a76085 ("apparmor: Initial implementation of raw policy blob compression") Reviewed-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 252a7ca98a9e..03fc18cf5fa7 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -71,10 +71,10 @@ struct rawdata_f_data { struct aa_loaddata *loaddata; + DECLARE_FLEX_ARRAY(char, data); }; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY -#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) static void rawdata_f_data_free(struct rawdata_f_data *private) { @@ -1436,7 +1436,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, struct rawdata_f_data *private = file->private_data; return simple_read_from_buffer(buf, size, ppos, - RAWDATA_F_DATA_BUF(private), + private->data, private->loaddata->size); } @@ -1469,8 +1469,7 @@ static int rawdata_open(struct inode *inode, struct file *file) private->loaddata = loaddata; error = decompress_zstd(loaddata->data, loaddata->compressed_size, - RAWDATA_F_DATA_BUF(private), - loaddata->size); + private->data, loaddata->size); if (error) goto fail_decompress; From 32e92764d6f8d251c1bca62be33793287b453a81 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 13 Feb 2026 11:29:38 -0800 Subject: [PATCH 3580/5214] apparmor: grab ns lock and refresh when looking up changehat child profiles There was a race condition involving change_hat and profile replacement in which replacement of the parent profile during a changehat operation could result in the list of children becoming empty and the changehat operation failing. To prevent this: - grab the namespace lock until we've built the hat transition, and - use aa_get_newest_profile to avoid using stale profile objects. Link: https://bugs.launchpad.net/bugs/2139664 Fixes: 89dbf1962aa63 ("apparmor: move change_hat mediation to using labels") Reviewed-by: Georgia Garcia Signed-off-by: Ryan Lee Signed-off-by: John Johansen --- security/apparmor/domain.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6748ac74b060..c2652165a588 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1109,6 +1110,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, int count, int flags) { struct aa_profile *profile, *root, *hat = NULL; + struct aa_ns *ns, *new_ns; struct aa_label *new; struct label_it it; bool sibling = false; @@ -1119,6 +1121,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred, AA_BUG(!hats); AA_BUG(count < 1); + /* + * Acquire the newest label and then hold the lock until we choose a + * hat, so that profile replacement doesn't atomically truncate the + * list of potential hats. Because we are getting the namespaces from + * the profiles and label, we can rely on the namespaces being live + * and avoid incrementing their refcounts while grabbing the lock. + */ + label = aa_get_label(label); + ns = labels_ns(label); + +retry: + mutex_lock_nested(&ns->lock, ns->level); + if (label_is_stale(label)) { + new = aa_get_newest_label(label); + new_ns = labels_ns(new); + if (new_ns != ns) { + aa_put_label(new); + mutex_unlock(&ns->lock); + ns = new_ns; + label = new; + goto retry; + } + aa_put_label(label); + label = new; + } + if (PROFILE_IS_HAT(labels_profile(label))) sibling = true; @@ -1127,7 +1155,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, name = hats[i]; label_for_each_in_scope(it, labels_ns(label), label, profile) { if (sibling && PROFILE_IS_HAT(profile)) { - root = aa_get_profile_rcu(&profile->parent); + root = aa_get_profile(profile->parent); } else if (!sibling && !PROFILE_IS_HAT(profile)) { root = aa_get_profile(profile); } else { /* conflicting change type */ @@ -1187,6 +1215,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, GLOBAL_ROOT_UID, info, error); } } + mutex_unlock(&ns->lock); return ERR_PTR(error); build: @@ -1199,7 +1228,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, error = -ENOMEM; goto fail; } /* else if (IS_ERR) build_change_hat has logged error so return new */ - + mutex_unlock(&ns->lock); return new; } From b9b864fc72367ffdbe79b7952518573e9d209844 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 1 Mar 2026 12:29:06 -0800 Subject: [PATCH 3581/5214] apparmor: free rawdata as soon as possible profiles can be pinned by file and other references, and can live long after they have been replaced/removed. The rawdata however is no longer needed, and can be freed earlier than the rest of the profile. Reviewed-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/policy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b6a5eb4021db..1739a9de9893 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -232,6 +232,13 @@ static void __remove_profile(struct aa_profile *profile) aa_label_remove(&profile->label); __aafs_profile_rmdir(profile); __list_remove_profile(profile); + /* rawdata is only ever referenced by fs lookup, that is no + * longer possible here, so put the reference to it. This will + * enable the rawdata to be freed if for some reason the profile + * is pinned and going to live for a while. + */ + aa_put_profile_loaddata(profile->rawdata); + profile->rawdata = NULL; } /** From 7b42f95813dc9ceb6bda35afcf914630909a19f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20B=C3=A9lair?= Date: Wed, 18 Feb 2026 10:27:34 +0100 Subject: [PATCH 3582/5214] apparmor: fix potential UAF in aa_replace_profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function aa_replace_profiles was accessing udata->size after calling aa_put_loaddata(udata), causing a potential UAF. Fixed this by saving the size to a local variable before dropping the reference. Fixes: 5ac8c355ae001 ("apparmor: allow introspecting the loaded policy pre internal transform") Reviewed-by: Georgia Garcia Signed-off-by: Maxime Bélair Signed-off-by: John Johansen --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 1739a9de9893..08620984d950 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1378,13 +1378,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + ssize_t udata_sz = udata->size; + aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); if (error) return error; - return udata->size; + return udata_sz; fail_lock: mutex_unlock(&ns->lock); From ed7cc1c6f240a0c2838c0617afb2b0466edd236f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 17 Feb 2026 08:54:10 -0700 Subject: [PATCH 3583/5214] apparmor: change fn_label_build() call to not return NULL Previously fn_label_build() was accepting a NULL which represented ENOMEM return and ERR_PTR for errors. Clean this up by requiring the cb fn to return an ERR_PTR or valid value. Reviewed-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/domain.c | 41 ++++++++++++++++++--------------- security/apparmor/include/lib.h | 12 +++++----- security/apparmor/mount.c | 17 +++++--------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c2652165a588..4172dedaba22 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -864,6 +864,15 @@ static int profile_onexec(const struct cred *subj_cred, } /* ensure none ns domain transitions are correctly applied with onexec */ +static struct aa_label *label_merge_wrap(struct aa_label *a, struct aa_label *b, + gfp_t gfp) +{ + struct aa_label *label = aa_label_merge(a, b, gfp); + + if (!label) + return ERR_PTR(-ENOMEM); + return label; +} static struct aa_label *handle_onexec(const struct cred *subj_cred, struct aa_label *label, @@ -891,12 +900,13 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, return ERR_PTR(error); new = fn_label_build_in_scope(label, profile, GFP_KERNEL, - stack ? aa_label_merge(&profile->label, onexec, - GFP_KERNEL) + stack ? label_merge_wrap(&profile->label, onexec, + GFP_KERNEL) : aa_get_newest_label(onexec), profile_transition(subj_cred, profile, bprm, buffer, cond, unsafe)); - if (new) + AA_BUG(!new); + if (!IS_ERR(new)) return new; /* TODO: get rid of GLOBAL_ROOT_UID */ @@ -905,7 +915,8 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, OP_CHANGE_ONEXEC, AA_MAY_ONEXEC, bprm->filename, NULL, onexec, GLOBAL_ROOT_UID, - "failed to build target label", -ENOMEM)); + "failed to build target label", + PTR_ERR(new))); return ERR_PTR(error); } @@ -968,14 +979,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) profile_transition(subj_cred, profile, bprm, buffer, &cond, &unsafe)); - AA_BUG(!new); if (IS_ERR(new)) { error = PTR_ERR(new); goto done; - } else if (!new) { - error = -ENOMEM; - goto done; } /* Policy has specified a domain transitions. If no_new_privs and @@ -1223,12 +1230,10 @@ static struct aa_label *change_hat(const struct cred *subj_cred, build_change_hat(subj_cred, profile, name, sibling), aa_get_label(&profile->label)); - if (!new) { - info = "label build failed"; - error = -ENOMEM; - goto fail; - } /* else if (IS_ERR) build_change_hat has logged error so return new */ mutex_unlock(&ns->lock); + AA_BUG(!new); + /* return new label or error ptr */ + return new; } @@ -1556,7 +1561,8 @@ int aa_change_profile(const char *fqname, int flags) new = fn_label_build_in_scope(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); - if (IS_ERR_OR_NULL(new)) + AA_BUG(!new); + if (IS_ERR(new)) goto build_fail; /* * no new privs prevents domain transitions that would @@ -1580,10 +1586,9 @@ int aa_change_profile(const char *fqname, int flags) goto build_fail; error = aa_replace_current_label(new); } else { - if (new) { - aa_put_label(new); - new = NULL; - } + /* new will be recomputed so at exec time. So discard */ + aa_put_label(new); + new = NULL; /* full transition will be built in exec path */ aa_set_current_onexec(target, stack); diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 8c6ce8484552..a093304fc998 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -282,7 +282,6 @@ void aa_policy_destroy(struct aa_policy *policy); * * Returns: new label on success * ERR_PTR if build @FN fails - * NULL if label_build fails due to low memory conditions * * @FN must return a label or ERR_PTR on failure. NULL is not allowed */ @@ -298,7 +297,7 @@ void aa_policy_destroy(struct aa_policy *policy); DEFINE_VEC(label, __lvec); \ DEFINE_VEC(profile, __pvec); \ if (vec_setup(label, __lvec, (L)->size, (GFP))) { \ - __new_ = NULL; \ + __new_ = ERR_PTR(-ENOMEM); \ goto __done; \ } \ __j = 0; \ @@ -320,23 +319,24 @@ void aa_policy_destroy(struct aa_policy *policy); if (__count > 1) { \ __new_ = aa_vec_find_or_create_label(__pvec,\ __count, (GFP)); \ - /* only fails if out of Mem */ \ if (!__new_) \ - __new_ = NULL; \ + __new_ = ERR_PTR(-ENOMEM); \ } else \ __new_ = aa_get_label(&__pvec[0]->label); \ vec_cleanup(profile, __pvec, __count); \ } else \ - __new_ = NULL; \ + __new_ = ERR_PTR(-ENOMEM); \ __do_cleanup: \ vec_cleanup(label, __lvec, (L)->size); \ } else { \ (P) = labels_profile(L); \ __new_ = (FN); \ + AA_BUG(!__new_); \ } \ __done: \ - if (!__new_) \ + if (PTR_ERR(__new_)) \ AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \ + AA_BUG(!__new_); \ (__new_); \ }) diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 523570aa1a5a..2f5d918832c1 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -735,17 +735,11 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label, build_pivotroot(subj_cred, profile, new_path, new_buffer, old_path, old_buffer)); - if (!target) { - info = "label build failed"; - error = -ENOMEM; - goto fail; - } else if (!IS_ERR(target)) { + AA_BUG(!target); + if (!IS_ERR(target)) { error = aa_replace_current_label(target); - if (error) { - /* TODO: audit target */ - aa_put_label(target); - goto out; - } + if (error) + goto fail; aa_put_label(target); } else /* already audited error */ @@ -763,7 +757,8 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label, NULL /*new_name */, NULL /* old_name */, NULL, NULL, - 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, + 0, target->hname, AA_MAY_PIVOTROOT, &nullperms, info, error)); + aa_put_label(target); goto out; } From 716d384ac7c905b719f3ce11cdb3a3d172c210fb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Tue, 24 Feb 2026 09:02:04 -0700 Subject: [PATCH 3584/5214] apparmor: make fn_label_build() capable of handling not supported Currently fn_label_build() callback fns must provide a transition or failure. Change this so that a callback can indicate it should be skipped/not be involved in the label being built. This will be useful when building object labels based on mediation flags, as to whether the label should be set. Existing callers can keep treating NULL return as an error because none of those callback fns support skipping, but instead of the old error handling replace with AA_BUG. Reviewed-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index a093304fc998..e3c8cb044a90 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -281,14 +281,15 @@ void aa_policy_destroy(struct aa_policy *policy); * @FN: fn to call for each profile transition. @P is set to the profile * * Returns: new label on success + * NULL if all callbacks decline to specify a transition * ERR_PTR if build @FN fails * - * @FN must return a label or ERR_PTR on failure. NULL is not allowed + * @FN must return a label or ERR_PTR on failure. */ #define fn_label_build(L, P, GFP, FN) \ ({ \ __label__ __do_cleanup, __done; \ - struct aa_label *__new_; \ + struct aa_label *__new_ = NULL; \ \ if ((L)->size > 1) { \ /* TODO: add cache of transitions already done */ \ @@ -303,11 +304,15 @@ void aa_policy_destroy(struct aa_policy *policy); __j = 0; \ label_for_each(__i, (L), (P)) { \ __new_ = (FN); \ - AA_BUG(!__new_); \ + if (!__new_) \ + continue; \ if (IS_ERR(__new_)) \ goto __do_cleanup; \ __lvec[__j++] = __new_; \ } \ + if (__j == 0) \ + /* no components adding to build */ \ + goto __do_cleanup; \ for (__j = __count = 0; __j < (L)->size; __j++) \ __count += __lvec[__j]->size; \ if (!vec_setup(profile, __pvec, __count, (GFP))) { \ @@ -331,12 +336,10 @@ __do_cleanup: \ } else { \ (P) = labels_profile(L); \ __new_ = (FN); \ - AA_BUG(!__new_); \ } \ __done: \ if (PTR_ERR(__new_)) \ AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \ - AA_BUG(!__new_); \ (__new_); \ }) From 7681ca43d2b1c776e62fe77e3167835fb1ab8319 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Wed, 6 May 2026 16:02:11 -0300 Subject: [PATCH 3585/5214] apparmor: fix NULL pointer dereference in unpack_pdb pdb->dfa could be NULL if unpack_dfa fails, causing a NULL pointer dereference. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Signed-off-by: Georgia Garcia Signed-off-by: Manuel Diewald Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 9f45d5513d2c..3643c058d6f8 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1045,7 +1045,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, } /* accept2 is in some cases being allocated, even with perms */ - if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + if (pdb->dfa && pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { /* add dfa flags table missing in v2 */ u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen; u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags; From 59fe6fbc4cd45582bc8893de0a382a36562317b3 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 29 Jan 2026 15:39:42 -0300 Subject: [PATCH 3586/5214] apparmor: remove or add symlinks to rawdata according to export_binary When the export_binary parameter is set, then rawdata is available and there should be a symbolic link for the rawdata in the profile directory in apparmorfs. If the parameter is unset, then the symlinks should not exist. The issue arises when changing the value of export_binary on runtime and replacing profiles. If export_binary was set when the profile was originally loaded, then changed to 0 and the profile was reloaded, then the symbolic links would still exist but would return ENOENT because the rawdata no longer exists. On the opposite side, if export_binary was unset when the profile was originally loaded, then changed to 1 and the profile was reloaded, then the symbolic links would not exist, even though the rawdata does. Fixes: d61c57fde8191 ("apparmor: make export of raw binary profile to userspace optional") Signed-off-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 102 +++++++++++++++++++------ security/apparmor/include/apparmorfs.h | 12 +++ security/apparmor/policy.c | 15 ++++ 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 03fc18cf5fa7..a59c4b83620f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1757,6 +1757,80 @@ static const struct inode_operations rawdata_link_abi_iops = { static const struct inode_operations rawdata_link_data_iops = { .get_link = rawdata_get_link_data, }; + +/* + * Requires: @profile->ns->lock held + */ +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]); + profile->dents[AAFS_PROF_RAW_HASH] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]); + profile->dents[AAFS_PROF_RAW_ABI] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]); + profile->dents[AAFS_PROF_RAW_DATA] = NULL; +} + +static inline int create_symlink_dent(struct aa_profile *profile, + const char *name, + enum aafs_prof_type type, + const struct inode_operations *iops) +{ + struct dentry *dent = NULL; + struct dentry *dir = prof_dir(profile); + + if (profile->dents[type]) + return 0; + + dent = aafs_create(name, S_IFLNK | 0444, dir, + &profile->label.proxy->count, NULL, NULL, iops); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + profile->dents[type] = dent; + return 0; +} + +/* + * Requires: @profile->ns->lock held + */ +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + int error; + + if (!profile || + (profile->dents[AAFS_PROF_RAW_HASH] && + profile->dents[AAFS_PROF_RAW_ABI] && + profile->dents[AAFS_PROF_RAW_DATA])) + return 0; + + if (!profile->rawdata) + return 0; + + if (aa_g_hash_policy) { + error = create_symlink_dent(profile, "raw_sha256", + AAFS_PROF_RAW_HASH, + &rawdata_link_sha256_iops); + if (error) + return error; + } + + error = create_symlink_dent(profile, "raw_abi", + AAFS_PROF_RAW_ABI, + &rawdata_link_abi_iops); + if (error) + return error; + + + error = create_symlink_dent(profile, "raw_data", + AAFS_PROF_RAW_DATA, + &rawdata_link_data_iops); + if (error) + return error; + + return 0; +} + #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ /* @@ -1832,31 +1906,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } -#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY - if (profile->rawdata) { - if (aa_g_hash_policy) { - dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, - NULL, &rawdata_link_sha256_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_HASH] = dent; - } - dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_abi_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_ABI] = dent; - - dent = aafs_create("raw_data", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_data_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_DATA] = dent; - } -#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + error = __aa_create_rawdata_symlink_dents(profile); + if (error) + goto fail2; list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aafs_profile_mkdir(child, prof_child_dir(profile)); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index dd580594dfb7..33243d11fd10 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -120,6 +120,8 @@ struct aa_loaddata; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile); +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile); #else static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) { @@ -131,6 +133,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns, { return 0; } + +static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + /* empty stub */ +} + +static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + return 0; +} #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 08620984d950..847b0ff450c5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1349,6 +1349,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, goto skip; } + if (!aa_g_export_binary) { + if (ent->old && ent->old->rawdata && + ent->old->dents[AAFS_LOADDATA_DIR]) { + /* remove rawdata symlinks because the symlink + * target will be removed + */ + __aa_remove_rawdata_symlink_dents(ent->old); + } + } + /* * TODO: finer dedup based on profile range in data. Load set * can differ but profile may remain unchanged @@ -1359,6 +1369,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (ent->old) { share_name(ent->old, ent->new); __replace_profile(ent->old, ent->new); + if (aa_g_export_binary) { + /* recreate rawdata symlinks */ + if (!ent->old->rawdata) + __aa_create_rawdata_symlink_dents(ent->new); + } } else { struct list_head *lh; From b7a2b49bba4e5994a476c49d662b796818079e5e Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Sun, 3 May 2026 12:12:43 +0800 Subject: [PATCH 3587/5214] apparmor: Fix return in ns_mkdir_op Return NULL instead of passing to ERR_PTR while error is zero. Fixes smatch warning: - security/apparmor/apparmorfs.c:1846 ns_mkdir_op() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Reviewed-by: Ryan Lee Signed-off-by: Hongling Zeng Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index a59c4b83620f..94b8c7979231 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1975,7 +1975,7 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir, mutex_unlock(&parent->lock); aa_put_ns(parent); - return ERR_PTR(error); + return error ? ERR_PTR(error) : NULL; } static int ns_rmdir_op(struct inode *dir, struct dentry *dentry) From 45cf568241048e560a81aa2053f06a62069f5640 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 4 May 2026 08:32:37 +0200 Subject: [PATCH 3588/5214] apparmor: fail policy unpack on accept2 allocation failure unpack_pdb() may need to allocate a missing ACCEPT2 table for older policy data. If that allocation failed, it set an error message but jumped to the success path, returning a policydb with the required table missing. Return -ENOMEM through the normal failure path when the ACCEPT2 allocation fails. Remove the now-unused out label. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 3643c058d6f8..d9dcff167c48 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -1054,7 +1054,8 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL); if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) { *info = "failed to alloc dfa flags table"; - goto out; + error = -ENOMEM; + goto fail; } pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents; pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags; @@ -1079,7 +1080,6 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, * - move free of unneeded trans table here, has to be done * after perm mapping. */ -out: *policy = pdb; return 0; From 7306c41672487a6c28430714be063bc6942c28f2 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 4 May 2026 13:13:24 +0200 Subject: [PATCH 3589/5214] apparmor: release exe file resources on path failure get_current_exe_path() takes both an exe_file reference and a path reference before resolving the path name. If aa_path_name() failed, it returned immediately and leaked both references. Route the failure through the common cleanup path so fput() and path_put() always run after the references are acquired. Fixes: 8d34e16f7f2b ("apparmor: userns: Add support for execpath in userns") Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen --- security/apparmor/task.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 0db0e81b4600..6445cb5f8526 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -313,9 +313,12 @@ static const char *get_current_exe_path(char *buffer, int buffer_size) p = exe_file->f_path; path_get(&p); - if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) - return ERR_PTR(-ENOMEM); + if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) { + path_str = ERR_PTR(-ENOMEM); + goto out; + } +out: fput(exe_file); path_put(&p); From e27bfb2ae9ad8522aea82d435fd6d73cccee7e17 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 6 May 2026 01:17:38 -0700 Subject: [PATCH 3590/5214] apparmor: remove unnecessary goto and associated label There is no need for a goto a label immediately following the conditional block when the jump is the last statement in the block. Fixes: 7306c41672487 ("apparmor: release exe file resources on path failure") Signed-off-by: John Johansen --- security/apparmor/task.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 6445cb5f8526..b9fb3738124e 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -313,12 +313,9 @@ static const char *get_current_exe_path(char *buffer, int buffer_size) p = exe_file->f_path; path_get(&p); - if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) { + if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) path_str = ERR_PTR(-ENOMEM); - goto out; - } -out: fput(exe_file); path_put(&p); From fea23bf73f0cae8ccb1d0684e4a3003874771f41 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:37:14 +0200 Subject: [PATCH 3591/5214] apparmor: aa_getprocattr free procattr leak on format failure aa_getprocattr() allocates the output string before rendering the label into it. If the second aa_label_snxprint() call fails, the function returned without freeing that allocation. Free and clear the output pointer on the uncommon formatting failure path before dropping the namespace reference. Fixes: 76a1d263aba3 ("apparmor: switch getprocattr to using label_print fns()") Reviewed-by: Tyler Hicks Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen --- security/apparmor/procattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index ce40f15d4952..c07b6e8fd9c9 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -54,6 +54,8 @@ int aa_getprocattr(struct aa_label *label, char **string, bool newline) FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED); if (len < 0) { + kfree(*string); + *string = NULL; aa_put_ns(current_ns); return len; } From 340372688bb87da45ff8d4e2f82ccfd1b64c65ff Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 5 May 2026 05:40:53 +0200 Subject: [PATCH 3592/5214] apparmor: put secmark label after secid lookup apparmor_secmark_init() parses a configured secmark label to obtain its secid. aa_label_strn_parse() returns a refcounted label, but the success path kept that reference after copying the secid. Fixes: ab9f2115081a ("apparmor: Allow filtering based on secmark policy") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen --- security/apparmor/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 44c04102062f..df9cb7c00cac 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -354,6 +354,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark) return PTR_ERR(label); secmark->secid = label->secid; + aa_put_label(label); return 0; } From add2b70038bea194bcdef8a680f9153ee7f93ac0 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 28 May 2026 16:04:12 -0300 Subject: [PATCH 3593/5214] apparmor: don't audit files pointing to aa_null.dentry In commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") there was a change to not audit files pointing to aa_null.dentry because they provide no value, but setting the error variable instead of returning -EACCES was still causing them to be audited. Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Acked-by: David Disseldorp Signed-off-by: Georgia Garcia Signed-off-by: John Johansen --- security/apparmor/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 694e157149e8..fc5abd5473c8 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -157,7 +157,7 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) - error = -EACCES; + return -EACCES; else error = aa_path_name(path, flags, buffer, name, &info, labels_profile(label)->disconnected); From bcd1b34c21748531a3febaf7440632b89d8deab7 Mon Sep 17 00:00:00 2001 From: Maciek Borzecki Date: Fri, 8 May 2026 10:30:16 +0200 Subject: [PATCH 3594/5214] apparmor: fix uninitialised pointer passed to audit_log_untrustedstring() Commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") intrdouced a small bug, where path_name() may pass a potentially uninitialized *name to aa_audit_file() if the path->dentry had been replaced with aa_null.dentry earlier on. This can lead to page fault like one observed on 7.0.2 openSUSE Tumbleweed kernel: [51692.242756] [ T24690] BUG: unable to handle page fault for address: 0000000f00000003 [51692.242762] [ T24690] #PF: supervisor read access in kernel mode [51692.242763] [ T24690] #PF: error_code(0x0000) - not-present page [51692.242765] [ T24690] PGD 0 P4D 0 [51692.242768] [ T24690] Oops: Oops: 0000 [#1] SMP NOPTI [51692.242772] [ T24690] CPU: 3 UID: 1020 PID: 24690 Comm: snap-confine Tainted: G O 7.0.2-1-default #1 PREEMPT(full) openSUSE Tumbleweed ab90b4c9940707f9cafa19bdad80b2cec52dbe51 [51692.242775] [ T24690] Tainted: [O]=OOT_MODULE [51692.242777] [ T24690] Hardware name: Framework Laptop 13 (AMD Ryzen 7040Series)/FRANMDCP05, BIOS 03.18 01/08/2026 [51692.242778] [ T24690] RIP: 0010:strlen+0x4/0x30 [51692.242783] [ T24690] Code: f7 75 ec 31 c0 e9 17 9f 00 ff 48 89 f8 e9 0f 9f 00 ff 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa <80> 3f 00 74 18 48 89 f8 0f 1f 40 00 48 83 c0 01 80 38 00 75 f7 48 [51692.242785] [ T24690] RSP: 0018:ffffd015eb1e3608 EFLAGS: 00010282 [51692.242787] [ T24690] RAX: 0000000000000000 RBX: ffff89796198a360 RCX: 0000000000000000 [51692.242788] [ T24690] RDX: 00000000000000d1 RSI: 0000000f00000003 RDI: 0000000f00000003 [51692.242790] [ T24690] RBP: ffffffffb7ede090 R08: 00000000000005f5 R09: 0000000000000000 [51692.242791] [ T24690] R10: 0000000000000000 R11: 0000000000000000 R12: ffffd015eb1e3700 [51692.242792] [ T24690] R13: ffff8977a22bc380 R14: ffffffffb7ec5190 R15: ffff8977a0c8aa80 [51692.242794] [ T24690] FS: 0000000000000000(0000) GS:ffff897f640d8000(0000) knlGS:0000000000000000 [51692.242796] [ T24690] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [51692.242797] [ T24690] CR2: 0000000f00000003 CR3: 00000006ad15f000 CR4: 0000000000f50ef0 [51692.242799] [ T24690] PKRU: 55555554 [51692.242800] [ T24690] Call Trace: [51692.242802] [ T24690] [51692.242804] [ T24690] audit_log_untrustedstring+0x1d/0x40 [51692.242811] [ T24690] common_lsm_audit+0x71/0x1d0 [51692.242816] [ T24690] aa_audit+0x5a/0x170 [51692.242819] [ T24690] aa_audit_file+0x18a/0x1b0 [51692.242825] [ T24690] path_name+0xd2/0x100 [51692.242829] [ T24690] profile_path_perm.part.0+0x58/0xb0 [51692.242832] [ T24690] aa_path_perm+0xef/0x150 [51692.242837] [ T24690] apparmor_file_open+0x153/0x2e0 [51692.242840] [ T24690] security_file_open+0x46/0xd0 [51692.242844] [ T24690] do_dentry_open+0xe9/0x4d0 [51692.242848] [ T24690] vfs_open+0x30/0x100 While here, initialise variables which are passed down to path_name(). Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Signed-off-by: Maciek Borzecki Signed-off-by: John Johansen --- security/apparmor/file.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index fc5abd5473c8..c9d55fe1086f 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -158,9 +158,9 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) return -EACCES; - else - error = aa_path_name(path, flags, buffer, name, &info, - labels_profile(label)->disconnected); + + error = aa_path_name(path, flags, buffer, name, &info, + labels_profile(label)->disconnected); if (error) { fn_for_each_confined(label, profile, aa_audit_file(subj_cred, @@ -250,7 +250,7 @@ static int profile_path_perm(const char *op, const struct cred *subj_cred, struct path_cond *cond, int flags, struct aa_perms *perms) { - const char *name; + const char *name = NULL; int error; if (profile_unconfined(profile)) @@ -328,7 +328,7 @@ static int profile_path_link(const struct cred *subj_cred, struct path_cond *cond) { struct aa_ruleset *rules = profile->label.rules[0]; - const char *lname, *tname = NULL; + const char *lname = NULL, *tname = NULL; struct aa_perms lperms = {}, perms; const char *info = NULL; u32 request = AA_MAY_LINK; From 5112ed5258b8d5e0769ae7d2bf9c9dea14c59703 Mon Sep 17 00:00:00 2001 From: Eduardo Vasconcelos Date: Thu, 21 May 2026 12:13:06 -0300 Subject: [PATCH 3595/5214] apparmor: Fix inverted comparison in cache_hold_inc() cache_hold_inc() prevents the per-CPU cache hold counter from rising above MAX_HOLD_COUNT, but the comparison is inverted (> MAX_HOLD_COUNT instead of <), so the counter never rises above 0. This breaks the cache mechanism because since the hold counter is always 0, the global pool is always attempted first before falling back to the local cache. The decrement also never occurs, thus the hold counter is effectively dead. Fix by changing > to < in cache_hold_inc(). Fixes: 0b6a6b72b329 ("apparmor: document the buffer hold, add an overflow guard") Signed-off-by: Eduardo Vasconcelos Signed-off-by: John Johansen --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3491e9f60194..b7c19805a216 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -2129,7 +2129,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) */ static void cache_hold_inc(unsigned int *hold) { - if (*hold > MAX_HOLD_COUNT) + if (*hold < MAX_HOLD_COUNT) (*hold)++; } From 6f060496d03e4dc560a40f73770bd08335cb7a27 Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 26 May 2026 00:04:46 +0200 Subject: [PATCH 3596/5214] apparmor: fix use-after-free in rawdata dedup loop aa_replace_profiles() walks ns->rawdata_list to dedup the incoming policy blob against entries already attached to existing profiles. Per the kernel-doc on struct aa_loaddata, list membership does not hold a reference: profiles hold pcount, and when the last pcount drops, do_ploaddata_rmfs() is queued on a workqueue that takes ns->lock and removes the entry. Between dropping the last pcount and the workqueue running, an entry remains on the list with pcount == 0. aa_get_profile_loaddata() is an unconditional kref_get() on pcount, so when the dedup loop hits such an entry, refcount hardening reports refcount_t: addition on 0; use-after-free. inside aa_replace_profiles(), and the poisoned counter then trips "saturated" and "underflow" warnings on the subsequent uses of the same loaddata. Before commit a0b7091c4de4 ("apparmor: fix race on rawdata dereference") the dedup path used a get_unless_zero-style helper on a single counter, so the existing "if (tmp)" guard was meaningful. The split-refcount refactor introduced aa_get_profile_loaddata(), which has plain kref_get() semantics, and the guard quietly became a no-op. Introduce aa_get_profile_loaddata_not0(), matching the existing _not0 convention used by aa_get_profile_not0(), and use it for the rawdata_list dedup lookup so dying entries are skipped. Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu 24.04 + stress-ng 0.17.06: stress-ng --apparmor 1 --klog-check --timeout 60s Without this patch the three refcount_t warnings fire within a few seconds. With it the same 60 s run is clean. Coverage is a smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN and CONFIG_PROVE_LOCKING would be welcome from anyone with the cycles. Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference") Reported-by: Colin Ian King Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513 Cc: stable@vger.kernel.org Signed-off-by: Ruslan Valiyev Signed-off-by: John Johansen --- security/apparmor/include/policy_unpack.h | 19 +++++++++++++++++++ security/apparmor/policy.c | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index e5a95dc4da1f..b9de0fdf9ee5 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data) return data; } +/** + * aa_get_profile_loaddata_not0 - get a profile reference count if not zero + * @data: reference to get a count on + * + * Like aa_get_profile_loaddata(), but safe to call on an entry that may + * be on a list (e.g. ns->rawdata_list) where the last pcount has already + * dropped and the deferred cleanup has not yet run. + * + * Returns: pointer to reference, or %NULL if @data is NULL or its + * profile refcount has already reached zero. + */ +static inline struct aa_loaddata * +aa_get_profile_loaddata_not0(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&data->pcount)) + return data; + return NULL; +} + void __aa_loaddata_update(struct aa_loaddata *data, long revision); bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); void aa_loaddata_kref(struct kref *kref); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 847b0ff450c5..b59e827747da 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1230,8 +1230,12 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (aa_rawdata_eq(rawdata_ent, udata)) { struct aa_loaddata *tmp; - tmp = aa_get_profile_loaddata(rawdata_ent); - /* check we didn't fail the race */ + /* + * Entries remain on rawdata_list with + * pcount == 0 until do_ploaddata_rmfs() + * runs; only take a live profile ref. + */ + tmp = aa_get_profile_loaddata_not0(rawdata_ent); if (tmp) { aa_put_profile_loaddata(udata); udata = tmp; From 3e4ca50ee4d88642afa38815775e1ffa90e8dd0b Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 26 May 2026 09:38:26 +0800 Subject: [PATCH 3597/5214] security: apparmor: fix two spelling mistakes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix two spelling errors in comment: - interated → interacted - dont → don't Signed-off-by: Qingshuang Fu Signed-off-by: John Johansen --- security/apparmor/domain.c | 2 +- security/apparmor/lsm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 4172dedaba22..d6958eb00e30 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -136,7 +136,7 @@ static int label_compound_match(struct aa_profile *profile, struct label_it i; struct path_cond cond = { }; - /* find first subcomponent that is in view and going to be interated with */ + /* find first subcomponent that is in view and going to be interacted with */ label_for_each(i, label, tp) { if (!aa_ns_visible(profile->ns, tp->ns, inview)) continue; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b7c19805a216..7457067e8192 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1493,7 +1493,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) * * Note: can not sleep may be called with locks held * - * dont want protocol specific in __skb_recv_datagram() + * don't want protocol specific in __skb_recv_datagram() * to deny an incoming connection socket_sock_rcv_skb() */ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) From 4e905ed27c788fbb9ea4384e93ea85b303000d57 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 20 May 2026 11:18:57 +0300 Subject: [PATCH 3598/5214] apparmor: replace get_zeroed_page() with kzalloc() multi_transaction_new() allocates memory with get_zeroed_page() and uses it as struct multi_transaction. The usage of that structure does not require struct page access and it is better to allocate multi_transaction objects with kzalloc() that provides better scalability and more debugging possibilities. Replace use of get_zeroed_page() with kzalloc(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Reviewed-by: Paul Moore Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 94b8c7979231..56155d7d5b2f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -906,7 +907,7 @@ static void multi_transaction_kref(struct kref *kref) struct multi_transaction *t; t = container_of(kref, struct multi_transaction, count); - free_page((unsigned long) t); + kfree(t); } static struct multi_transaction * @@ -949,7 +950,7 @@ static struct multi_transaction *multi_transaction_new(struct file *file, if (size > MULTI_TRANSACTION_LIMIT - 1) return ERR_PTR(-EFBIG); - t = (struct multi_transaction *)get_zeroed_page(GFP_KERNEL); + t = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!t) return ERR_PTR(-ENOMEM); kref_init(&t->count); From d0691bd5dcaec2350039ecb04fa70faa91ac142d Mon Sep 17 00:00:00 2001 From: Rodrigo Zaiden Date: Sun, 31 May 2026 16:36:28 -0300 Subject: [PATCH 3599/5214] apparmor: fix kernel-doc warnings Fix two kernel-doc warnings: - non-kernel-doc comment marked with '/**' in af_unix.c - documented symbol name mismatch for aa_get_i_loaddata() in policy_unpack.h No functional changes. Signed-off-by: Rodrigo Zaiden Signed-off-by: John Johansen --- security/apparmor/af_unix.c | 2 +- security/apparmor/include/policy_unpack.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 834a3b1c2f0a..b9b22edae202 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -615,7 +615,7 @@ static int unix_peer_perm(const struct cred *subj_cred, peer_label, &ad)); } -/** +/* * * Requires: lock held on both @sk and @peer_sk * called by unix_stream_connect, unix_may_send diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index b9de0fdf9ee5..4ea9b6479a3e 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -131,7 +131,7 @@ struct aa_loaddata { int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); /** - * aa_get_loaddata - get a reference count from a counted data reference + * aa_get_i_loaddata - get a reference count from a counted data reference * @data: reference to get a count on * * Returns: pointer to reference From 52738352a6f29279e15285fcb7b50241ef867e27 Mon Sep 17 00:00:00 2001 From: Sean Chang Date: Mon, 8 Jun 2026 23:52:52 +0800 Subject: [PATCH 3600/5214] riscv: kvm: Use endian-specific __lelong for NACL shared memory When compiling with sparse enabled (C=2), bitwise type warnings are triggered in the RISC-V KVM implementation. This occurs because the user-space data unboxing macro '__get_user_asm' performs implicit casting on restricted types without forcing the compiler's compliance. Additionally, raw 'unsigned long *' pointers are used to access the SBI NACL shared memory, whereas the RISC-V SBI specification mandates that these structures must follow little-endian byte ordering. Fix these by: 1. Adding a '__force' cast to '__get_user_asm()' to safely suppress implicit cast warnings during user-space data fetching. 2. Introducing the '__lelong' type macro, which dynamically resolves to '__le32' or '__le64' depending on XLEN, and replacing 'unsigned long *' with '__lelong *' to enforce proper compile-time endianness checks. Signed-off-by: Sean Chang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260608155252.4292-1-seanwascoding@gmail.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_nacl.h | 14 ++++++++------ arch/riscv/include/asm/uaccess.h | 2 +- arch/riscv/kvm/nacl.c | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/riscv/include/asm/kvm_nacl.h b/arch/riscv/include/asm/kvm_nacl.h index 4124d5e06a0f..f45407bcaa26 100644 --- a/arch/riscv/include/asm/kvm_nacl.h +++ b/arch/riscv/include/asm/kvm_nacl.h @@ -60,9 +60,11 @@ int kvm_riscv_nacl_init(void); #ifdef CONFIG_32BIT #define lelong_to_cpu(__x) le32_to_cpu(__x) #define cpu_to_lelong(__x) cpu_to_le32(__x) +#define __lelong __le32 #else #define lelong_to_cpu(__x) le64_to_cpu(__x) #define cpu_to_lelong(__x) cpu_to_le64(__x) +#define __lelong __le64 #endif #define nacl_shmem() \ @@ -70,7 +72,7 @@ int kvm_riscv_nacl_init(void); #define nacl_scratch_read_long(__shmem, __offset) \ ({ \ - unsigned long *__p = (__shmem) + \ + __lelong *__p = (__shmem) + \ SBI_NACL_SHMEM_SCRATCH_OFFSET + \ (__offset); \ lelong_to_cpu(*__p); \ @@ -78,7 +80,7 @@ int kvm_riscv_nacl_init(void); #define nacl_scratch_write_long(__shmem, __offset, __val) \ do { \ - unsigned long *__p = (__shmem) + \ + __lelong *__p = (__shmem) + \ SBI_NACL_SHMEM_SCRATCH_OFFSET + \ (__offset); \ *__p = cpu_to_lelong(__val); \ @@ -87,7 +89,7 @@ do { \ #define nacl_scratch_write_longs(__shmem, __offset, __array, __count) \ do { \ unsigned int __i; \ - unsigned long *__p = (__shmem) + \ + __lelong *__p = (__shmem) + \ SBI_NACL_SHMEM_SCRATCH_OFFSET + \ (__offset); \ for (__i = 0; __i < (__count); __i++) \ @@ -168,7 +170,7 @@ __kvm_riscv_nacl_hfence(__shmem, \ #define nacl_csr_read(__shmem, __csr) \ ({ \ - unsigned long *__a = (__shmem) + SBI_NACL_SHMEM_CSR_OFFSET; \ + __lelong *__a = (__shmem) + SBI_NACL_SHMEM_CSR_OFFSET; \ lelong_to_cpu(__a[SBI_NACL_SHMEM_CSR_INDEX(__csr)]); \ }) @@ -176,7 +178,7 @@ __kvm_riscv_nacl_hfence(__shmem, \ do { \ void *__s = (__shmem); \ unsigned int __i = SBI_NACL_SHMEM_CSR_INDEX(__csr); \ - unsigned long *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ + __lelong *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ u8 *__b = (__s) + SBI_NACL_SHMEM_DBITMAP_OFFSET; \ __a[__i] = cpu_to_lelong(__val); \ __b[__i >> 3] |= 1U << (__i & 0x7); \ @@ -186,7 +188,7 @@ do { \ ({ \ void *__s = (__shmem); \ unsigned int __i = SBI_NACL_SHMEM_CSR_INDEX(__csr); \ - unsigned long *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ + __lelong *__a = (__s) + SBI_NACL_SHMEM_CSR_OFFSET; \ u8 *__b = (__s) + SBI_NACL_SHMEM_DBITMAP_OFFSET; \ unsigned long __r = lelong_to_cpu(__a[__i]); \ __a[__i] = cpu_to_lelong(__val); \ diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index 11c9886c3b70..5d4ec15584cf 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -112,7 +112,7 @@ do { \ _ASM_EXTABLE_UACCESS_ERR(1b, %l2, %0) \ : "=&r" (__tmp) \ : "m" (*(ptr)) : : label); \ - (x) = (__typeof__(x))(unsigned long)__tmp; \ + (x) = (__force __typeof__(x))(unsigned long)__tmp; \ } while (0) #else /* !CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ #define __get_user_asm(insn, x, ptr, label) \ diff --git a/arch/riscv/kvm/nacl.c b/arch/riscv/kvm/nacl.c index 08a95ad9ada2..6f9f8963e9dd 100644 --- a/arch/riscv/kvm/nacl.c +++ b/arch/riscv/kvm/nacl.c @@ -20,7 +20,7 @@ void __kvm_riscv_nacl_hfence(void *shmem, unsigned long page_count) { int i, ent = -1, try_count = 5; - unsigned long *entp; + __lelong *entp; again: for (i = 0; i < SBI_NACL_SHMEM_HFENCE_ENTRY_MAX; i++) { From f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 13 Jun 2026 22:56:41 +0200 Subject: [PATCH 3601/5214] batman-adv: tt: don't merge change entries with different VIDs batadv_tt_local_event() merges/cancels events for the same client which would conflict or be duplicates. The matching of the queued events only compares the MAC address - the VLAN ID stored in each event is ignored. If a MAC would now appear on multiple VID, the two ADD change events (for VID 1 and VID 2) would be merged to a single vid event. The remote can therefore not calculate the correct TT table and desync. A full translation table exchange is required to recover from this state. A check of VID is therefore necessary to avoid such wrong merges/cancels. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 8b6c49c32c89..016ad100153b 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -447,6 +447,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, if (!batadv_compare_eth(entry->change.addr, common->addr)) continue; + if (entry->change.vid != tt_change_node->change.vid) + continue; + del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; if (del_op_requested != del_op_entry) { /* DEL+ADD in the same orig interval have no effect and From 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 13 Jun 2026 23:32:57 +0200 Subject: [PATCH 3602/5214] batman-adv: tt: track roam count per VID batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. But TT entries are for a MAC + VID. The VID was completely missed and thus leads to incorrect detection of ROAM counts when a client MAC exists in multiple VLANs. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 9 +++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 016ad100153b..4bfad36a4b70 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -3450,6 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * batadv_tt_check_roam_count() - check if a client has roamed too frequently * @bat_priv: the bat priv with all the mesh interface information * @client: mac address of the roaming client + * @vid: VLAN identifier * * This function checks whether the client already reached the * maximum number of possible roaming phases. In this case the ROAMING_ADV @@ -3457,7 +3458,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * * Return: true if the ROAMING_ADV can be sent, false otherwise */ -static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) +static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) { struct batadv_tt_roam_node *tt_roam_node; bool ret = false; @@ -3470,6 +3471,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) if (!batadv_compare_eth(tt_roam_node->addr, client)) continue; + if (tt_roam_node->vid != vid) + continue; + if (batadv_has_timed_out(tt_roam_node->first_time, BATADV_ROAMING_MAX_TIME)) continue; @@ -3491,6 +3495,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) atomic_set(&tt_roam_node->counter, BATADV_ROAMING_MAX_COUNT - 1); ether_addr_copy(tt_roam_node->addr, client); + tt_roam_node->vid = vid; list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); ret = true; @@ -3527,7 +3532,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, /* before going on we have to check whether the client has * already roamed to us too many times */ - if (!batadv_tt_check_roam_count(bat_priv, client)) + if (!batadv_tt_check_roam_count(bat_priv, client, vid)) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c1b3f989566f..3de3c1ac0244 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1961,6 +1961,9 @@ struct batadv_tt_roam_node { /** @addr: mac address of the client in the roaming phase */ u8 addr[ETH_ALEN]; + /** @vid: VLAN identifier */ + u16 vid; + /** * @counter: number of allowed roaming events per client within a single * OGM interval (changes are committed with each OGM) From 20d7658b74169f86d4ac01b9185b3eadddf71f28 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 13 Jun 2026 23:50:28 +0200 Subject: [PATCH 3603/5214] batman-adv: dat: prevent false sharing between VLANs The local hash of DAT entries is supposed to be VLAN (VID) aware. But the adding to the hash and the search in the hash were not checking the VID information of the hash entries. The entries would therefore only be correctly separated when batadv_hash_dat() didn't select the same buckets for different VIDs. Cc: stable@kernel.org Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann --- net/batman-adv/distributed-arp-table.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index aaea155b9403..ae39ceaa2e29 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work) */ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) { - const void *data1 = container_of(node, struct batadv_dat_entry, - hash_entry); + const struct batadv_dat_entry *entry1; + const struct batadv_dat_entry *entry2; - return memcmp(data1, data2, sizeof(__be32)) == 0; + entry1 = container_of(node, struct batadv_dat_entry, hash_entry); + entry2 = data2; + + return entry1->ip == entry2->ip && entry1->vid == entry2->vid; } /** @@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, if (dat_entry->ip != ip) continue; + if (dat_entry->vid != vid) + continue; + if (!kref_get_unless_zero(&dat_entry->refcount)) continue; From 32a6799255525d6ea4da0f7e9e0e521ad9560a46 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 14 Jun 2026 10:19:54 +0200 Subject: [PATCH 3604/5214] batman-adv: tvlv: enforce 2-byte alignment The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte alignment, so a following OGM must start at an even offset. As the header length is even, an odd tvlv_len would misalign it and trigger unaligned accesses on strict-alignment architectures. Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in the mesh. Therefore, reject such malformed packets. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann --- net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- net/batman-adv/bat_v_ogm.c | 11 ++++++++++- net/batman-adv/routing.c | 6 ++++++ net/batman-adv/tvlv.c | 6 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 7588e64e7ba6..bb2f012b454e 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -316,14 +316,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm_packet *ogm_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm_packet->tvlv_len); + + /* the fields of an aggregated OGM are accessed assuming (at least) + * 2-byte alignment, so a following OGM must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 95efd8a43c79..037921aad35d 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm2_packet *ogm2_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm2_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm2_packet->tvlv_len); + + /* the fields of an aggregated OGMv2 are accessed assuming (at least) + * 2-byte alignment, so a following OGMv2 must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm2_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 9db57fd36e7d..c05fcc9241ad 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, if (tvlv_buff_len > skb->len - hdr_size) goto free_skb; + /* the fields of an multicast payload are accessed assuming (at least) + * 2-byte alignment, so a following packet must start at an even offset. + */ + if (tvlv_buff_len & 1) + goto free_skb; + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, tvlv_buff, tvlv_buff_len); if (ret >= 0) { diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 403c85456870..a957555d8958 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -477,6 +477,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (tvlv_value_cont_len > tvlv_value_len) break; + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + tvlv_handler = batadv_tvlv_handler_get(bat_priv, tvlv_hdr->type, tvlv_hdr->version); From edb557b2ba38fea2c5eb710cf366c797e187218c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 14 Jun 2026 11:22:43 +0200 Subject: [PATCH 3605/5214] batman-adv: tvlv: avoid race of cifsnotfound handler state TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to signal that the OGM handler should be called (with NULL for data) when the specific TVLV container was not found in the OGM. This is used by: * DAT * GW * Multicast (OGM + Tracker) The state whether the handler was executed was stored in the struct batadv_tvlv_handler. But the TVLV processing is started without any lock. Multiple parallel contexts processing TVLVs would therefore overwrite each others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared batadv_tvlv_handler. Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, per TVLV buffer, whether a matching container was present by scanning the packet's buffer. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann --- net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- net/batman-adv/types.h | 7 ----- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index a957555d8958..1c9fb21985f6 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -411,7 +411,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, tvlv_handler->ogm_handler(bat_priv, orig_node, BATADV_NO_FLAGS, tvlv_value, tvlv_value_len); - tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; break; case BATADV_UNICAST_TVLV: if (!skb) @@ -443,6 +442,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, return NET_RX_SUCCESS; } +/** + * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * @type: tvlv container type to look for + * @version: tvlv container version to look for + * + * Return: true if a container of the given type and version is present in the + * tvlv buffer, false otherwise. + */ +static bool batadv_tvlv_containers_contain(void *tvlv_value, + u16 tvlv_value_len, u8 type, + u8 version) +{ + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = tvlv_value; + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_value = tvlv_hdr + 1; + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > tvlv_value_len) + break; + + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + + if (tvlv_hdr->type == type && tvlv_hdr->version == version) + return true; + + tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + return false; +} + /** * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the * appropriate handlers @@ -462,7 +503,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, struct sk_buff *skb, void *tvlv_value, u16 tvlv_value_len) { + u16 tvlv_value_start_len = tvlv_value_len; struct batadv_tvlv_handler *tvlv_handler; + void *tvlv_value_start = tvlv_value; struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_cont_len; u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; @@ -506,12 +549,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (!tvlv_handler->ogm_handler) continue; - if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && - !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) - tvlv_handler->ogm_handler(bat_priv, orig_node, - cifnotfound, NULL, 0); + if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) + continue; - tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + /* if the corresponding container was present then the handler + * was already called from the loop above + */ + if (batadv_tvlv_containers_contain(tvlv_value_start, + tvlv_value_start_len, + tvlv_handler->type, + tvlv_handler->version)) + continue; + + tvlv_handler->ogm_handler(bat_priv, orig_node, + cifnotfound, NULL, 0); } rcu_read_unlock(); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 3de3c1ac0244..b1f9f8964c3f 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -2294,13 +2294,6 @@ enum batadv_tvlv_handler_flags { * will call this handler even if its type was not found (with no data) */ BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), - - /** - * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the - * API marks a handler as being called, so it won't be called if the - * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set - */ - BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), }; #endif /* _NET_BATMAN_ADV_TYPES_H_ */ From 57e181af13de36571380ef3cc74000559826fa9b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:38 +0300 Subject: [PATCH 3606/5214] i3c: mipi-i3c-hci: Fix suspend behavior when bus disable falls back to software reset Software reset was introduced as a fallback if bus disable failed. The change was made in 2 places: the cleanup path and the suspend path. For the cleanup path (i3c_hci_bus_cleanup()), after software reset the function continues to do cleanup for the current I/O mode. For the suspend path (i3c_hci_rpm_suspend()), after software reset the function returns early. However software reset does not reset any Ring Headers in the Host Controller, so returning early is not the right thing to do. Instead, continue to call suspend for the current I/O mode, which for DMA mode will reset any Ring Headers. Note, although Ring Headers should not be active at this stage, performing this reset follows the procedure defined by the specification and keeps the suspend path consistent with the cleanup path. Note also, i3c_hci_sync_irq_inactive() is still called via the PIO and DMA hci->io->suspend() callbacks. Always return 0 because the device is quiesced as much as possible and returning a negative error code would unnecessarily prevent system suspend. Fixes: 9a258d1336f7 ("i3c: mipi-i3c-hci: Fallback to software reset when bus disable fails") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index b781dbed2165..afb0764b5e1f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -762,15 +762,10 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) int i3c_hci_rpm_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); - int ret; - ret = i3c_hci_bus_disable(hci); - if (ret) { - /* Fall back to software reset to disable the bus */ - ret = i3c_hci_software_reset(hci); - i3c_hci_sync_irq_inactive(hci); - return ret; - } + /* Fall back to software reset to disable the bus */ + if (i3c_hci_bus_disable(hci)) + i3c_hci_software_reset(hci); hci->io->suspend(hci); From 093eb8e73c90aa0c8cfb0421aa85bd70c23488be Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:39 +0300 Subject: [PATCH 3607/5214] i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring The MIPI I3C HCI specification does not require the DMA ring RUN bit (RUN_STOP) to be cleared when issuing an ABORT. That allows the DMA ring to continue to receive IBIs, although an IBI is anyway not lost because it can be received once the ring restarts if the I3C device has not given up. Note, currently ABORT is only used on a timeout error path so the change has very little effect in practice. In the more common case of a transfer error, the ring (bundle) operation is halted by the controller anyway. Adjust the RING_CONTROL handling to set ABORT without clearing RUN_STOP, bringing the driver into alignment with the specification. Fixes: b795e68bf3073 ("i3c: mipi-i3c-hci: Correct RING_CTRL_ABORT handling in DMA dequeue") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index e4daaa612055..bdffdd8b8923 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -554,7 +554,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, if (ring_status & RING_STATUS_RUNNING) { /* stop the ring */ reinit_completion(&rh->op_done); - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { From c9b57ad97872eff9f4ce1fa1374c932cab579285 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:40 +0300 Subject: [PATCH 3608/5214] i3c: mipi-i3c-hci: Prevent DMA enqueue while ring is aborting or in error Block the DMA enqueue path while a Ring abort is in progress or after an error condition has been detected. Previously, new transfers could be enqueued while the DMA Ring was being aborted or while error handling was underway. This allowed enqueue and error-recovery paths to run concurrently, potentially interfering with each other and corrupting Ring state. Introduce explicit enqueue blocking and a wait queue to serialize access: enqueue operations now wait until abort or error handling has completed before proceeding. Enqueue is unblocked once the Ring is safely restarted. Note, there is only 1 ring bundle configured, and a transfer error causes the controller to halt ring (bundle) operation, so there is only ever 1 outstanding error at a time. Furthermore, a later patch ensures that only the currently active transfer list can time out. Consequently, the DMA queue will not be unblocked while there are outstanding transfer errors or timeouts. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 1 + drivers/i3c/master/mipi-i3c-hci/dma.c | 25 +++++++++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index afb0764b5e1f..44617eb3a3f1 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -973,6 +973,7 @@ static int i3c_hci_probe(struct platform_device *pdev) spin_lock_init(&hci->lock); mutex_init(&hci->control_mutex); + init_waitqueue_head(&hci->enqueue_wait_queue); /* * Multi-bus instances share the same MMIO address range, but not diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index bdffdd8b8923..c3da6eab8eae 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -484,6 +484,12 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, spin_lock_irq(&hci->lock); + while (unlikely(hci->enqueue_blocked)) { + spin_unlock_irq(&hci->lock); + wait_event(hci->enqueue_wait_queue, !READ_ONCE(hci->enqueue_blocked)); + spin_lock_irq(&hci->lock); + } + if (n > rh->xfer_space) { spin_unlock_irq(&hci->lock); hci_dma_unmap_xfer(hci, xfer_list, n); @@ -539,6 +545,14 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, return 0; } +static void hci_dma_unblock_enqueue(struct i3c_hci *hci) +{ + if (hci->enqueue_blocked) { + hci->enqueue_blocked = false; + wake_up_all(&hci->enqueue_wait_queue); + } +} + static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n) { @@ -550,12 +564,17 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, guard(mutex)(&hci->control_mutex); + spin_lock_irq(&hci->lock); + ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { + hci->enqueue_blocked = true; + spin_unlock_irq(&hci->lock); /* stop the ring */ reinit_completion(&rh->op_done); rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); + spin_lock_irq(&hci->lock); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { /* @@ -567,8 +586,6 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, } } - spin_lock_irq(&hci->lock); - for (i = 0; i < n; i++) { struct hci_xfer *xfer = xfer_list + i; int idx = xfer->ring_entry; @@ -604,6 +621,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE); rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP); + hci_dma_unblock_enqueue(hci); + spin_unlock_irq(&hci->lock); return did_unqueue; @@ -647,6 +666,8 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) } if (xfer->completion) complete(xfer->completion); + if (RESP_STATUS(resp)) + hci->enqueue_blocked = true; } done_ptr = (done_ptr + 1) % rh->xfer_entries; diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f17f43494c1b..d630400ec945 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -54,6 +54,8 @@ struct i3c_hci { struct mutex control_mutex; atomic_t next_cmd_tid; bool irq_inactive; + bool enqueue_blocked; + wait_queue_head_t enqueue_wait_queue; u32 caps; unsigned int quirks; unsigned int DAT_entries; From e251e7c9fd30f494526757b3dc731ed056f23e12 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:41 +0300 Subject: [PATCH 3609/5214] i3c: mipi-i3c-hci: Wait for DMA ring restart to complete Although hci_dma_dequeue_xfer() is serialized against itself via control_mutex, this does not guarantee that a DMA ring restart triggered by a previous invocation has fully completed. When the function is called again in rapid succession, the DMA ring may still be transitioning back to the running state, which may confound or disrupt further state changes. Address this by waiting for the DMA ring restart to complete before continuing. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index c3da6eab8eae..3b14bc87bdf6 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -617,6 +617,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, } /* restart the ring */ + reinit_completion(&rh->op_done); mipi_i3c_hci_resume(hci); rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE); rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP); @@ -625,6 +626,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, spin_unlock_irq(&hci->lock); + wait_for_completion_timeout(&rh->op_done, HZ); + return did_unqueue; } From dc8691bf4fc4487e6e0618e43b7abb5ab6408a4c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:42 +0300 Subject: [PATCH 3610/5214] i3c: mipi-i3c-hci: Move hci_dma_xfer_done() definition Move hci_dma_xfer_done() earlier in the file to avoid a forward declaration needed by a subsequent change. No functional change. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-6-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 98 +++++++++++++-------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 3b14bc87bdf6..ad47bb2890d6 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -545,6 +545,55 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, return 0; } +static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) +{ + u32 op1_val, op2_val, resp, *ring_resp; + unsigned int tid, done_ptr = rh->done_ptr; + unsigned int done_cnt = 0; + struct hci_xfer *xfer; + + for (;;) { + op2_val = rh_reg_read(RING_OPERATION2); + if (done_ptr == FIELD_GET(RING_OP2_CR_DEQ_PTR, op2_val)) + break; + + ring_resp = rh->resp + rh->resp_struct_sz * done_ptr; + resp = *ring_resp; + tid = RESP_TID(resp); + dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); + + xfer = rh->src_xfers[done_ptr]; + if (!xfer) { + dev_dbg(&hci->master.dev, "orphaned ring entry"); + } else { + hci_dma_unmap_xfer(hci, xfer, 1); + rh->src_xfers[done_ptr] = NULL; + xfer->ring_entry = -1; + xfer->response = resp; + if (tid != xfer->cmd_tid) { + dev_err(&hci->master.dev, + "response tid=%d when expecting %d\n", + tid, xfer->cmd_tid); + /* TODO: do something about it? */ + } + if (xfer->completion) + complete(xfer->completion); + if (RESP_STATUS(resp)) + hci->enqueue_blocked = true; + } + + done_ptr = (done_ptr + 1) % rh->xfer_entries; + rh->done_ptr = done_ptr; + done_cnt += 1; + } + + rh->xfer_space += done_cnt; + op1_val = rh_reg_read(RING_OPERATION1); + op1_val &= ~RING_OP1_CR_SW_DEQ_PTR; + op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr); + rh_reg_write(RING_OPERATION1, op1_val); +} + static void hci_dma_unblock_enqueue(struct i3c_hci *hci) { if (hci->enqueue_blocked) { @@ -636,55 +685,6 @@ static int hci_dma_handle_error(struct i3c_hci *hci, struct hci_xfer *xfer_list, return hci_dma_dequeue_xfer(hci, xfer_list, n) ? -EIO : 0; } -static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) -{ - u32 op1_val, op2_val, resp, *ring_resp; - unsigned int tid, done_ptr = rh->done_ptr; - unsigned int done_cnt = 0; - struct hci_xfer *xfer; - - for (;;) { - op2_val = rh_reg_read(RING_OPERATION2); - if (done_ptr == FIELD_GET(RING_OP2_CR_DEQ_PTR, op2_val)) - break; - - ring_resp = rh->resp + rh->resp_struct_sz * done_ptr; - resp = *ring_resp; - tid = RESP_TID(resp); - dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); - - xfer = rh->src_xfers[done_ptr]; - if (!xfer) { - dev_dbg(&hci->master.dev, "orphaned ring entry"); - } else { - hci_dma_unmap_xfer(hci, xfer, 1); - rh->src_xfers[done_ptr] = NULL; - xfer->ring_entry = -1; - xfer->response = resp; - if (tid != xfer->cmd_tid) { - dev_err(&hci->master.dev, - "response tid=%d when expecting %d\n", - tid, xfer->cmd_tid); - /* TODO: do something about it? */ - } - if (xfer->completion) - complete(xfer->completion); - if (RESP_STATUS(resp)) - hci->enqueue_blocked = true; - } - - done_ptr = (done_ptr + 1) % rh->xfer_entries; - rh->done_ptr = done_ptr; - done_cnt += 1; - } - - rh->xfer_space += done_cnt; - op1_val = rh_reg_read(RING_OPERATION1); - op1_val &= ~RING_OP1_CR_SW_DEQ_PTR; - op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr); - rh_reg_write(RING_OPERATION1, op1_val); -} - static int hci_dma_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev, const struct i3c_ibi_setup *req) { From 2dbe7832ae9c3026442fe5249a799a2278c0ebb6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:43 +0300 Subject: [PATCH 3611/5214] i3c: mipi-i3c-hci: Call hci_dma_xfer_done() from dequeue path hci_dma_dequeue_xfer() relies on state normally updated by the DMA interrupt handler. Ensure that state is current by explicitly invoking hci_dma_xfer_done() from the dequeue path. This handles cases where the interrupt handler has not (yet) run. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index ad47bb2890d6..de0f17706ac8 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -635,6 +635,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, } } + hci_dma_xfer_done(hci, rh); + for (i = 0; i < n; i++) { struct hci_xfer *xfer = xfer_list + i; int idx = xfer->ring_entry; From f00deffd9a5dcef36f5d3ef5eea93b134ecea49f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:44 +0300 Subject: [PATCH 3612/5214] i3c: mipi-i3c-hci: Complete transfer lists immediately on error In DMA mode, transfer lists are currently completed only when the final transfer in the list completes. If an earlier transfer fails, the list is left incomplete and callers wait until timeout. There is no need to wait for a timeout, as the completion path in i3c_hci_process_xfer() already checks for error status. Complete the transfer list as soon as any transfer in the list reports an error. This avoids unnecessary delays and spurious timeouts on error. Complete a transfer list completion immediately there is an error. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 6 ++++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index de0f17706ac8..83b553e1ab0b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -502,6 +502,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer = xfer_list + i; u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; + xfer->final_xfer = xfer_list + n - 1; + /* store cmd descriptor */ *ring_data++ = xfer->cmd_desc[0]; *ring_data++ = xfer->cmd_desc[1]; @@ -576,8 +578,8 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) tid, xfer->cmd_tid); /* TODO: do something about it? */ } - if (xfer->completion) - complete(xfer->completion); + if (xfer == xfer->final_xfer || RESP_STATUS(resp)) + complete(xfer->final_xfer->completion); if (RESP_STATUS(resp)) hci->enqueue_blocked = true; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index d630400ec945..f07fc627d4d2 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -104,6 +104,7 @@ struct hci_xfer { struct { /* DMA specific */ struct i3c_dma *dma; + struct hci_xfer *final_xfer; int ring_number; int ring_entry; }; From a53891532acdc726f821e398e68c4b2d182c387d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:45 +0300 Subject: [PATCH 3613/5214] i3c: mipi-i3c-hci: Avoid restarting DMA ring after aborting wrong transfer Software ABORT of the DMA ring is used to recover from transfer list timeouts, but it is inherently racy. The intended transfer list may complete just before the ABORT takes effect, causing the subsequent transfer list to be aborted instead. In this case, an incomplete transfer list may remain in the ring and has not yet been processed by hci_dma_dequeue_xfer(). Restarting the DMA ring at that point can lead to unpredictable results. Detect when the next queued transfer is not the first entry of a transfer list and does not belong to the list currently being dequeued. In that case, skip restarting the DMA ring and defer recovery until a subsequent call to hci_dma_dequeue_xfer(), which will safely restart the ring once the incomplete list is handled. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-9-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 15 +++++++++++++++ drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 83b553e1ab0b..8e27fb6f18f5 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -503,6 +503,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; xfer->final_xfer = xfer_list + n - 1; + xfer->xfer_list_pos = i; /* store cmd descriptor */ *ring_data++ = xfer->cmd_desc[0]; @@ -669,6 +670,20 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, } } + /* + * A software ABORT may race with transfer completion and abort the next + * transfer list instead. Detect that case, and do not restart the ring. + * It will be handled by a subsequent dequeue. + */ + if (!did_unqueue) { + struct hci_xfer *xfer = rh->src_xfers[rh->done_ptr]; + + if (xfer && xfer->xfer_list_pos && xfer->final_xfer != xfer_list->final_xfer) { + spin_unlock_irq(&hci->lock); + return false; + } + } + /* restart the ring */ reinit_completion(&rh->op_done); mipi_i3c_hci_resume(hci); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f07fc627d4d2..83d4f13a68a3 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -107,6 +107,7 @@ struct hci_xfer { struct hci_xfer *final_xfer; int ring_number; int ring_entry; + int xfer_list_pos; }; }; }; From 29ca24fbd2efa7ade70e342f3ebb4a7071353b62 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:46 +0300 Subject: [PATCH 3614/5214] i3c: mipi-i3c-hci: Add DMA ring abort/reset quirk for Intel controllers Some Intel I3C HCI controllers cannot reliably restart a DMA ring after an ABORT. Additional queue resets are required to recover, and must be performed using PIO reset bits even while operating in DMA mode. This behavior is non-standard. Introduce a controller quirk to opt into the required PIO queue resets after a DMA ring abort, and enable it for Intel LPSS I3C controllers. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-10-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 15 ++++++++++++++- drivers/i3c/master/mipi-i3c-hci/dma.c | 4 ++++ drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 44617eb3a3f1..770235ad6b25 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -240,6 +240,18 @@ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci) reg_write(RESET_CONTROL, RX_FIFO_RST | TX_FIFO_RST | RESP_QUEUE_RST); } +#define ALL_QUEUES_RST (CMD_QUEUE_RST | RESP_QUEUE_RST | RX_FIFO_RST | TX_FIFO_RST | IBI_QUEUE_RST) + +void mipi_i3c_hci_pio_reset_all_queues(struct i3c_hci *hci) +{ + u32 regval; + + reg_write(RESET_CONTROL, ALL_QUEUES_RST); + if (readx_poll_timeout_atomic(reg_read, RESET_CONTROL, regval, + !(regval & ALL_QUEUES_RST), 0, 20)) + dev_err(&hci->master.dev, "%s: Reset queues failed\n", __func__); +} + /* located here rather than dct.c because needed bits are in core reg space */ void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci) { @@ -1040,7 +1052,8 @@ MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); static const struct platform_device_id i3c_hci_driver_ids[] = { { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED | HCI_QUIRK_RPM_IBI_ALLOWED | - HCI_QUIRK_RPM_PARENT_MANAGED }, + HCI_QUIRK_RPM_PARENT_MANAGED | + HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 8e27fb6f18f5..906de7dbfdb5 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -638,6 +638,10 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, } } + if ((hci->quirks & HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET) && + (rh_reg_read(RING_STATUS) & RING_STATUS_ABORTED)) + mipi_i3c_hci_pio_reset_all_queues(hci); + hci_dma_xfer_done(hci, rh); for (i = 0; i < n; i++) { diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 83d4f13a68a3..01237b12d32e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -156,10 +156,12 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */ #define HCI_QUIRK_RPM_IBI_ALLOWED BIT(6) /* IBI and Hot-Join allowed while runtime suspended */ #define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */ +#define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW resets after DMA abort */ /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); void mipi_i3c_hci_pio_reset(struct i3c_hci *hci); +void mipi_i3c_hci_pio_reset_all_queues(struct i3c_hci *hci); void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci); void amd_set_od_pp_timing(struct i3c_hci *hci); void amd_set_resp_buf_thld(struct i3c_hci *hci); From 5d3855504912073270fda859021edd4c994ad758 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:47 +0300 Subject: [PATCH 3615/5214] i3c: mipi-i3c-hci: Factor out hci_dma_abort() Factor out hci_dma_abort() from hci_dma_dequeue_xfer() in preparation for further changes. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-11-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 906de7dbfdb5..f2d33068b8df 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -597,6 +597,13 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) rh_reg_write(RING_OPERATION1, op1_val); } +static void hci_dma_abort(struct hci_rh_data *rh) +{ + reinit_completion(&rh->op_done); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); + wait_for_completion_timeout(&rh->op_done, HZ); +} + static void hci_dma_unblock_enqueue(struct i3c_hci *hci) { if (hci->enqueue_blocked) { @@ -623,9 +630,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, hci->enqueue_blocked = true; spin_unlock_irq(&hci->lock); /* stop the ring */ - reinit_completion(&rh->op_done); - rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); - wait_for_completion_timeout(&rh->op_done, HZ); + hci_dma_abort(rh); spin_lock_irq(&hci->lock); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { From 352d89067f78f27fc7b72e571fadf08d1d637ecb Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:48 +0300 Subject: [PATCH 3616/5214] i3c: mipi-i3c-hci: Add DMA ring abort quirk for Intel controllers DMA rings can be aborted either per-ring via RING_CONTROL or globally via HC_CONTROL_ABORT. The driver currently relies on the per-ring mechanism. Some Intel I3C HCI controllers require HC_CONTROL_ABORT to be asserted before a DMA ring abort is effective. This behavior is non-standard. Introduce a controller quirk to select the required abort method and enable it for Intel LPSS I3C controllers. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-12-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 18 ++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/dma.c | 17 +++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 770235ad6b25..8274c84b16be 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -231,7 +231,20 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m) void mipi_i3c_hci_resume(struct i3c_hci *hci) { - reg_set(HC_CONTROL, HC_CONTROL_RESUME); + u32 reg = reg_read(HC_CONTROL); + + reg |= HC_CONTROL_RESUME; + reg &= ~HC_CONTROL_ABORT; + reg_write(HC_CONTROL, reg); +} + +void mipi_i3c_hci_abort(struct i3c_hci *hci) +{ + u32 reg = reg_read(HC_CONTROL); + + reg &= ~HC_CONTROL_RESUME; /* Do not set resume */ + reg |= HC_CONTROL_ABORT; + reg_write(HC_CONTROL, reg); } /* located here rather than pio.c because needed bits are in core reg space */ @@ -1053,7 +1066,8 @@ static const struct platform_device_id i3c_hci_driver_ids[] = { { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED | HCI_QUIRK_RPM_IBI_ALLOWED | HCI_QUIRK_RPM_PARENT_MANAGED | - HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET }, + HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET | + HCI_QUIRK_DMA_REQUIRES_HC_ABORT }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index f2d33068b8df..f9023cb3c5a2 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -597,8 +597,21 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) rh_reg_write(RING_OPERATION1, op1_val); } -static void hci_dma_abort(struct hci_rh_data *rh) +static void hci_dma_requires_hc_abort_quirk(struct i3c_hci *hci, struct hci_rh_data *rh) { + reinit_completion(&rh->op_done); + mipi_i3c_hci_abort(hci); + wait_for_completion_timeout(&rh->op_done, HZ); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); +} + +static void hci_dma_abort(struct i3c_hci *hci, struct hci_rh_data *rh) +{ + if (hci->quirks & HCI_QUIRK_DMA_REQUIRES_HC_ABORT) { + hci_dma_requires_hc_abort_quirk(hci, rh); + return; + } + reinit_completion(&rh->op_done); rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); @@ -630,7 +643,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, hci->enqueue_blocked = true; spin_unlock_irq(&hci->lock); /* stop the ring */ - hci_dma_abort(rh); + hci_dma_abort(hci, rh); spin_lock_irq(&hci->lock); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 01237b12d32e..97c31a315a6e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -157,9 +157,11 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_RPM_IBI_ALLOWED BIT(6) /* IBI and Hot-Join allowed while runtime suspended */ #define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */ #define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW resets after DMA abort */ +#define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT to abort DMA */ /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); +void mipi_i3c_hci_abort(struct i3c_hci *hci); void mipi_i3c_hci_pio_reset(struct i3c_hci *hci); void mipi_i3c_hci_pio_reset_all_queues(struct i3c_hci *hci); void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci); From 3cfb40ba9501e35ee4b8a4a244f0b3817dfffdec Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:49 +0300 Subject: [PATCH 3617/5214] i3c: mipi-i3c-hci: Factor out reset-and-restore helper Factor the reset-and-restore sequence out of i3c_hci_rpm_resume() into a separate helper. This allows the same logic to be reused for recovery paths in subsequent changes without duplicating suspend/resume handling. No functional change. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-13-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 19 +++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 8274c84b16be..12a0122fb709 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -798,9 +798,8 @@ int i3c_hci_rpm_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(i3c_hci_rpm_suspend); -int i3c_hci_rpm_resume(struct device *dev) +static int i3c_hci_do_reset_and_restore(struct i3c_hci *hci) { - struct i3c_hci *hci = dev_get_drvdata(dev); int ret; ret = i3c_hci_reset_and_init(hci); @@ -821,6 +820,22 @@ int i3c_hci_rpm_resume(struct device *dev) return 0; } + +int i3c_hci_reset_and_restore(struct i3c_hci *hci) +{ + i3c_hci_bus_disable(hci); + + hci->io->suspend(hci); + + return i3c_hci_do_reset_and_restore(hci); +} + +int i3c_hci_rpm_resume(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + return i3c_hci_do_reset_and_restore(hci); +} EXPORT_SYMBOL_GPL(i3c_hci_rpm_resume); static int i3c_hci_runtime_suspend(struct device *dev) diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 97c31a315a6e..a3151c26827e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -175,4 +175,6 @@ int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n); int i3c_hci_rpm_suspend(struct device *dev); int i3c_hci_rpm_resume(struct device *dev); +int i3c_hci_reset_and_restore(struct i3c_hci *hci); + #endif From 5738043bade465c3019b7d33930bf24464dc8cfb Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:50 +0300 Subject: [PATCH 3618/5214] i3c: mipi-i3c-hci: Add DMA-mode recovery for internal controller errors Handle internal I3C HCI errors when operating in DMA mode by adding a simple recovery mechanism. On detection of an internal controller error, mark recovery as needed and attempt to restore operation by performing a software reset followed by state restore. To keep recovery straightforward on this unlikely error path, all currently queued transfers are terminated and completed with an error. This allows the controller to resume operation after internal failures rather than remaining permanently stuck. Note, internal errors indicated by INTR_HC_INTERNAL_ERR, cause the controller to stop. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-14-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/cmd.h | 6 ++ drivers/i3c/master/mipi-i3c-hci/core.c | 1 + drivers/i3c/master/mipi-i3c-hci/dma.c | 93 +++++++++++++++++++++++--- drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 4 files changed, 92 insertions(+), 9 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd.h b/drivers/i3c/master/mipi-i3c-hci/cmd.h index b1bf87daa651..7bada7b4b2de 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd.h +++ b/drivers/i3c/master/mipi-i3c-hci/cmd.h @@ -65,4 +65,10 @@ struct hci_cmd_ops { extern const struct hci_cmd_ops mipi_i3c_hci_cmd_v1; extern const struct hci_cmd_ops mipi_i3c_hci_cmd_v2; +static inline void hci_cmd_set_resp_err(u32 *response, int resp_err) +{ + *response &= ~RESP_ERR_FIELD; + *response |= FIELD_PREP(RESP_ERR_FIELD, resp_err); +} + #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 12a0122fb709..69dcf5dad3a5 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -668,6 +668,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) if (val & INTR_HC_INTERNAL_ERR) { dev_err(&hci->master.dev, "Host Controller Internal Error\n"); val &= ~INTR_HC_INTERNAL_ERR; + hci->recovery_needed = true; } if (val) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index f9023cb3c5a2..f39a6ce2aad5 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -258,6 +259,10 @@ static void hci_dma_init_rh(struct i3c_hci *hci, struct hci_rh_data *rh, int i) rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE); rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP); + /* + * Do not clear the entries of rh->src_xfers because the recovery uses + * them. In other cases they should be NULL anyway. + */ rh->done_ptr = 0; rh->ibi_chunk_ptr = 0; rh->xfer_space = rh->xfer_entries; @@ -362,7 +367,7 @@ static int hci_dma_init(struct i3c_hci *hci) rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz, &rh->resp_dma, GFP_KERNEL); rh->src_xfers = - kmalloc_objs(*rh->src_xfers, rh->xfer_entries); + kzalloc_objs(*rh->src_xfers, rh->xfer_entries); ret = -ENOMEM; if (!rh->xfer || !rh->resp || !rh->src_xfers) goto err_out; @@ -572,13 +577,15 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) hci_dma_unmap_xfer(hci, xfer, 1); rh->src_xfers[done_ptr] = NULL; xfer->ring_entry = -1; - xfer->response = resp; if (tid != xfer->cmd_tid) { dev_err(&hci->master.dev, "response tid=%d when expecting %d\n", tid, xfer->cmd_tid); - /* TODO: do something about it? */ + hci->recovery_needed = true; + if (!RESP_STATUS(resp)) + hci_cmd_set_resp_err(&resp, RESP_ERR_HC_TERMINATED); } + xfer->response = resp; if (xfer == xfer->final_xfer || RESP_STATUS(resp)) complete(xfer->final_xfer->completion); if (RESP_STATUS(resp)) @@ -625,6 +632,60 @@ static void hci_dma_unblock_enqueue(struct i3c_hci *hci) } } +static void hci_dma_error_out_rh(struct i3c_hci *hci, struct hci_rh_data *rh) +{ + /* + * The entries of rh->src_xfers are not cleared by + * i3c_hci_reset_and_restore(), so can be used here. Do 2 passes so + * that the final_xfer of an xfer list is always processed last. + */ + for (int pass = 0; pass < 2; pass++) + for (int i = 0; i < rh->xfer_entries; i++) { + struct hci_xfer *xfer = rh->src_xfers[i]; + + if (!xfer || (!pass && xfer == xfer->final_xfer)) + continue; + hci_dma_unmap_xfer(hci, xfer, 1); + rh->src_xfers[i] = NULL; + xfer->ring_entry = -1; + hci_cmd_set_resp_err(&xfer->response, RESP_ERR_HC_TERMINATED); + if (xfer == xfer->final_xfer) + complete(xfer->final_xfer->completion); + } +} + +static void hci_dma_error_out_all(struct i3c_hci *hci) +{ + struct hci_rings_data *rings = hci->io_data; + + for (int i = 0; i < rings->total; i++) + hci_dma_error_out_rh(hci, &rings->headers[i]); +} + +static void hci_dma_recovery(struct i3c_hci *hci) +{ + int ret; + + dev_err(&hci->master.dev, "Attempting to recover from internal errors\n"); + + for (int i = 0; i < 3; i++) { + ret = i3c_hci_reset_and_restore(hci); + if (!ret) + break; + dev_err(&hci->master.dev, "Reset and restore failed, error %d\n", ret); + /* Just in case the controller is busy, give it some time */ + msleep(1000); + } + + spin_lock_irq(&hci->lock); + hci_dma_error_out_all(hci); + hci_dma_unblock_enqueue(hci); + hci->recovery_needed = false; + spin_unlock_irq(&hci->lock); + + dev_err(&hci->master.dev, "Recovery %s\n", ret ? "failed!" : "done"); +} + static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n) { @@ -640,6 +701,17 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { + /* + * The transfer may have already completed, especially + * if recovery has just run. Do nothing in that case. + */ + hci_dma_xfer_done(hci, rh); + if (xfer_list->final_xfer->ring_entry < 0 && + !hci->recovery_needed && !hci->enqueue_blocked && + ring_status == (RING_STATUS_ENABLED | RING_STATUS_RUNNING)) { + spin_unlock_irq(&hci->lock); + return false; + } hci->enqueue_blocked = true; spin_unlock_irq(&hci->lock); /* stop the ring */ @@ -647,12 +719,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, spin_lock_irq(&hci->lock); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { - /* - * We're deep in it if ever this condition is ever met. - * Hardware might still be writing to memory, etc. - */ - dev_crit(&hci->master.dev, "unable to abort the ring\n"); - WARN_ON(1); + dev_err(&hci->master.dev, "Unable to abort the DMA ring\n"); + hci->recovery_needed = true; } } @@ -662,6 +730,13 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, hci_dma_xfer_done(hci, rh); + if (hci->recovery_needed) { + hci->enqueue_blocked = true; + spin_unlock_irq(&hci->lock); + hci_dma_recovery(hci); + return true; + } + for (i = 0; i < n; i++) { struct hci_xfer *xfer = xfer_list + i; int idx = xfer->ring_entry; diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index a3151c26827e..4bf2c66c97b4 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -55,6 +55,7 @@ struct i3c_hci { atomic_t next_cmd_tid; bool irq_inactive; bool enqueue_blocked; + bool recovery_needed; wait_queue_head_t enqueue_wait_queue; u32 caps; unsigned int quirks; From c14e53de8dd463ce96c0fde96d12189b4156d18b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:51 +0300 Subject: [PATCH 3619/5214] i3c: mipi-i3c-hci: Wait for NoOp commands to complete When a transfer list is only partially completed due to an error, hci_dma_dequeue_xfer() overwrites the remaining DMA ring entries with NoOp commands and restarts the ring to flush them out. While NoOp commands are expected to complete successfully, they may still fail to complete if the DMA ring is stuck. Explicitly wait for the NoOp commands to finish, and trigger controller recovery if they do not complete or report an error. This ensures that partially completed transfer lists are reliably resolved and that a stuck ring is recovered promptly. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-15-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 39 ++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index f39a6ce2aad5..0fd56bbb84ef 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -686,11 +686,33 @@ static void hci_dma_recovery(struct i3c_hci *hci) dev_err(&hci->master.dev, "Recovery %s\n", ret ? "failed!" : "done"); } +static bool hci_dma_wait_for_noop(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n, + int noop_pos) +{ + struct completion *done = xfer_list->final_xfer->completion; + bool timeout = !wait_for_completion_timeout(done, HZ); + u32 error = timeout; + + for (int i = noop_pos; i < n && !error; i++) + error = RESP_STATUS(xfer_list[i].response); + + if (!error) + return true; + + if (timeout) + dev_err(&hci->master.dev, "NoOp timeout error\n"); + else + dev_err(&hci->master.dev, "NoOp error %u\n", error); + + return false; +} + static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n) { struct hci_rings_data *rings = hci->io_data; struct hci_rh_data *rh = &rings->headers[xfer_list[0].ring_number]; + int noop_pos = -1; unsigned int i; bool did_unqueue = false; u32 ring_status; @@ -698,7 +720,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, guard(mutex)(&hci->control_mutex); spin_lock_irq(&hci->lock); - +restart: ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { /* @@ -757,11 +779,10 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, *ring_data++ = 0; } - /* disassociate this xfer struct */ - rh->src_xfers[idx] = NULL; - - /* and unmap it */ - hci_dma_unmap_xfer(hci, xfer, 1); + if (noop_pos < 0) { + reinit_completion(xfer->final_xfer->completion); + noop_pos = i; + } did_unqueue = true; } @@ -793,6 +814,12 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, wait_for_completion_timeout(&rh->op_done, HZ); + if (did_unqueue && !hci_dma_wait_for_noop(hci, xfer_list, n, noop_pos)) { + spin_lock_irq(&hci->lock); + hci->recovery_needed = true; + goto restart; + } + return did_unqueue; } From 9b34c4e4c849b3d1c6114ed344979fe85c27e8c4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:52 +0300 Subject: [PATCH 3620/5214] i3c: mipi-i3c-hci: Base timeouts on actual transfer start time Transfer timeouts are currently measured from the point where a transfer list is queued to the controller. This can cause transfers to time out before they have actually started, if earlier queued transfers consume the timeout interval. Fix this by recording when a transfer reaches the head of the queue and adjusting the timeout calculation to start from that point. The existing low-overhead completion-based timeout mechanism is preserved, but care is taken to ensure the transfer start time is consistently recorded for both PIO and DMA paths. This prevents premature timeouts while retaining efficient timeout handling. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-16-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 19 ++++++++++++++++++- drivers/i3c/master/mipi-i3c-hci/dma.c | 19 ++++++++++++++++++- drivers/i3c/master/mipi-i3c-hci/hci.h | 11 +++++++++++ drivers/i3c/master/mipi-i3c-hci/pio.c | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 69dcf5dad3a5..c6edbbedfdd7 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -275,13 +275,30 @@ int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) { struct completion *done = xfer[n - 1].completion; unsigned long timeout = xfer[n - 1].timeout; + unsigned long remaining_timeout = timeout; + long time_taken; + bool started; int ret; + xfer[0].started = false; + ret = hci->io->queue_xfer(hci, xfer, n); if (ret) return ret; - if (!wait_for_completion_timeout(done, timeout)) { + while (!wait_for_completion_timeout(done, remaining_timeout)) { + scoped_guard(spinlock_irqsave, &hci->lock) { + started = xfer[0].started; + time_taken = jiffies - xfer[0].start_jiffies; + } + /* Keep waiting if xfer has not started */ + if (!started) + continue; + /* Recalculate timeout based on actual start time */ + if (time_taken < timeout) { + remaining_timeout = timeout - time_taken; + continue; + } if (hci->io->dequeue_xfer(hci, xfer, n)) { dev_err(&hci->master.dev, "%s: timeout error\n", __func__); return -ETIMEDOUT; diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 0fd56bbb84ef..9a01c740760f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -543,6 +543,9 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, enqueue_ptr = (enqueue_ptr + 1) % rh->xfer_entries; } + if (rh->xfer_space == rh->xfer_entries) + hci_start_xfer(xfer_list); + rh->xfer_space -= n; op1_val &= ~RING_OP1_CR_ENQ_PTR; @@ -558,6 +561,7 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) u32 op1_val, op2_val, resp, *ring_resp; unsigned int tid, done_ptr = rh->done_ptr; unsigned int done_cnt = 0; + bool start_next = false; struct hci_xfer *xfer; for (;;) { @@ -588,8 +592,14 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) xfer->response = resp; if (xfer == xfer->final_xfer || RESP_STATUS(resp)) complete(xfer->final_xfer->completion); - if (RESP_STATUS(resp)) + else + hci_start_xfer(xfer); + if (RESP_STATUS(resp)) { hci->enqueue_blocked = true; + start_next = false; + } else { + start_next = true; + } } done_ptr = (done_ptr + 1) % rh->xfer_entries; @@ -598,6 +608,10 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) } rh->xfer_space += done_cnt; + if (start_next && rh->xfer_space < rh->xfer_entries) { + xfer = rh->src_xfers[done_ptr]; + hci_start_xfer(xfer); + } op1_val = rh_reg_read(RING_OPERATION1); op1_val &= ~RING_OP1_CR_SW_DEQ_PTR; op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr); @@ -810,6 +824,9 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, hci_dma_unblock_enqueue(hci); + if (rh->xfer_space < rh->xfer_entries) + hci_start_xfer(rh->src_xfers[rh->done_ptr]); + spin_unlock_irq(&hci->lock); wait_for_completion_timeout(&rh->op_done, HZ); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 4bf2c66c97b4..30297823ca85 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -11,6 +11,7 @@ #define HCI_H #include +#include /* 32-bit word aware bit and mask macros */ #define W0_MASK(h, l) GENMASK((h) - 0, (l) - 0) @@ -88,11 +89,13 @@ struct hci_xfer { u32 cmd_desc[4]; u32 response; bool rnw; + bool started; void *data; unsigned int data_len; unsigned int cmd_tid; struct completion *completion; unsigned long timeout; + unsigned long start_jiffies; union { struct { /* PIO specific */ @@ -123,6 +126,14 @@ static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n) kfree(xfer); } +static inline void hci_start_xfer(struct hci_xfer *xfer) +{ + if (!xfer->started) { + xfer->started = true; + xfer->start_jiffies = jiffies; + } +} + /* This abstracts PIO vs DMA operations */ struct hci_io_ops { bool (*irq_handler)(struct i3c_hci *hci); diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index 8f48a81e65ab..6b8cc5f2b4d2 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -605,6 +605,7 @@ static bool hci_pio_process_cmd(struct i3c_hci *hci, struct hci_pio_data *pio) * Finally send the command. */ hci_pio_write_cmd(hci, pio->curr_xfer); + hci_start_xfer(pio->curr_xfer); /* * And move on. */ From 1a7c9c143a20529f423cf7483b242ed8242c820a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:53 +0300 Subject: [PATCH 3621/5214] i3c: mipi-i3c-hci: Consolidate DMA ring allocation dma_alloc_coherent() allocates memory in whole pages, which can waste space when command and response queues are allocated separately. Allocate the DMA command and response queues from a single coherent allocation instead, while preserving the required 4-byte alignment. This reduces memory overhead without changing behavior. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-17-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 9a01c740760f..0136f3064ada 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -130,7 +130,7 @@ struct hci_rh_data { dma_addr_t xfer_dma, resp_dma, ibi_status_dma, ibi_data_dma; unsigned int xfer_entries, ibi_status_entries, ibi_chunks_total; unsigned int xfer_struct_sz, resp_struct_sz, ibi_status_sz, ibi_chunk_sz; - unsigned int done_ptr, ibi_chunk_ptr, xfer_space; + unsigned int xfer_alloc_sz, done_ptr, ibi_chunk_ptr, xfer_space; struct hci_xfer **src_xfers; struct completion op_done; }; @@ -187,13 +187,7 @@ static void hci_dma_free(void *data) rh = &rings->headers[i]; if (rh->xfer) - dma_free_coherent(rings->sysdev, - rh->xfer_struct_sz * rh->xfer_entries, - rh->xfer, rh->xfer_dma); - if (rh->resp) - dma_free_coherent(rings->sysdev, - rh->resp_struct_sz * rh->xfer_entries, - rh->resp, rh->resp_dma); + dma_free_coherent(rings->sysdev, rh->xfer_alloc_sz, rh->xfer, rh->xfer_dma); kfree(rh->src_xfers); if (rh->ibi_status) dma_free_coherent(rings->sysdev, @@ -359,18 +353,19 @@ static int hci_dma_init(struct i3c_hci *hci) dev_dbg(&hci->master.dev, "xfer_struct_sz = %d, resp_struct_sz = %d", rh->xfer_struct_sz, rh->resp_struct_sz); - xfers_sz = rh->xfer_struct_sz * rh->xfer_entries; + xfers_sz = round_up(rh->xfer_struct_sz * rh->xfer_entries, 4); resps_sz = rh->resp_struct_sz * rh->xfer_entries; + rh->xfer_alloc_sz = xfers_sz + resps_sz; - rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz, + rh->xfer = dma_alloc_coherent(rings->sysdev, rh->xfer_alloc_sz, &rh->xfer_dma, GFP_KERNEL); - rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz, - &rh->resp_dma, GFP_KERNEL); rh->src_xfers = kzalloc_objs(*rh->src_xfers, rh->xfer_entries); ret = -ENOMEM; - if (!rh->xfer || !rh->resp || !rh->src_xfers) + if (!rh->xfer || !rh->src_xfers) goto err_out; + rh->resp = rh->xfer + xfers_sz; + rh->resp_dma = rh->xfer_dma + xfers_sz; /* IBIs */ From e62fe9f267716a1ea0313a6ab8fb28d1cfb8aaf5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:54 +0300 Subject: [PATCH 3622/5214] i3c: mipi-i3c-hci: Increase DMA transfer ring size to maximum The DMA transfer ring is currently limited to 16 entries, despite the MIPI I3C HCI supporting up to 32 devices. When the ring lacks space for a new transfer list, the driver returns -EBUSY, which can be unexpected for clients. Increase the DMA transfer ring size to the maximum supported value of 255 entries. This effectively eliminates ring-space exhaustion in practice and avoids the complexity of adding secondary queuing mechanisms. Even at the maximum size, the memory overhead remains small (approximately 24 bytes per entry by default). Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-18-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 0136f3064ada..5c6ae2055618 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -27,7 +27,7 @@ */ #define XFER_RINGS 1 /* max: 8 */ -#define XFER_RING_ENTRIES 16 /* max: 255 */ +#define XFER_RING_ENTRIES 255 /* max: 255 */ #define IBI_RINGS 1 /* max: 8 */ #define IBI_STATUS_RING_ENTRIES 32 /* max: 255 */ From 299eccf996681273da0290f47db710de8d61c163 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Thu, 11 Jun 2026 18:21:06 +0200 Subject: [PATCH 3623/5214] landlock: Add documentation for UDP support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add example of UDP usage, without detailing the two access right. Slightly change the example used in code blocks: build a ruleset for a DNS client, so that it uses both TCP and UDP. Test coverage for security/landlock is 91.3% of 2245 lines according to LLVM 22. Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260611162107.49278-7-matthieu@buffet.re [mic: Fix doc formatting, update audit doc, add test coverage] Signed-off-by: Mickaël Salaün --- Documentation/admin-guide/LSM/landlock.rst | 4 +- Documentation/userspace-api/landlock.rst | 91 +++++++++++++++++----- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/Documentation/admin-guide/LSM/landlock.rst b/Documentation/admin-guide/LSM/landlock.rst index 9923874e2156..2dacb381c1a9 100644 --- a/Documentation/admin-guide/LSM/landlock.rst +++ b/Documentation/admin-guide/LSM/landlock.rst @@ -6,7 +6,7 @@ Landlock: system-wide management ================================ :Author: Mickaël Salaün -:Date: January 2026 +:Date: June 2026 Landlock can leverage the audit framework to log events. @@ -54,6 +54,8 @@ AUDIT_LANDLOCK_ACCESS **net.*** - Network access rights (ABI 4+): - net.bind_tcp - TCP port binding was denied - net.connect_tcp - TCP connection was denied + - net.bind_udp - UDP port binding was denied + - net.connect_send_udp - UDP connection and send was denied **scope.*** - IPC scoping restrictions (ABI 6+): - scope.abstract_unix_socket - Abstract UNIX socket connection denied diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index 45861fa75685..b5a2ab6f4766 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -8,7 +8,7 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: May 2026 +:Date: June 2026 The goal of Landlock is to enable restriction of ambient rights (e.g. global filesystem or network access) for a set of processes. Because Landlock @@ -40,8 +40,8 @@ Filesystem rules and the related filesystem actions are defined with `filesystem access rights`. -Network rules (since ABI v4) - For these rules, the object is a TCP port, +Network rules (since ABI v4 for TCP and v10 for UDP) + For these rules, the object is a TCP or UDP port, and the related actions are defined with `network access rights`. Defining and enforcing a security policy @@ -49,11 +49,11 @@ Defining and enforcing a security policy We first need to define the ruleset that will contain our rules. -For this example, the ruleset will contain rules that only allow filesystem -read actions and establish a specific TCP connection. Filesystem write -actions and other TCP actions will be denied. +For this example, the ruleset will contain rules that only allow some +filesystem read actions and some specific UDP and TCP actions. Filesystem +write actions and other TCP/UDP actions will be denied. -The ruleset then needs to handle both these kinds of actions. This is +The ruleset then needs to handle all these kinds of actions. This is required for backward and forward compatibility (i.e. the kernel and user space may not know each other's supported restrictions), hence the need to be explicit about the denied-by-default access rights. @@ -81,7 +81,9 @@ to be explicit about the denied-by-default access rights. LANDLOCK_ACCESS_FS_RESOLVE_UNIX, .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | - LANDLOCK_ACCESS_NET_CONNECT_TCP, + LANDLOCK_ACCESS_NET_CONNECT_TCP | + LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL, @@ -132,6 +134,12 @@ version, and only use the available subset of access rights: case 6 ... 8: /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */ ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX; + __attribute__((fallthrough)); + case 9: + /* Removes LANDLOCK_ACCESS_NET_*_UDP for ABI < 10 */ + ruleset_attr.handled_access_net &= + ~(LANDLOCK_ACCESS_NET_BIND_UDP | + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); } This enables the creation of an inclusive ruleset that will contain our rules. @@ -187,20 +195,52 @@ kernel does not support are dropped (the compatibility switch above already cleared them in ``handled_access_*``), and the rule is skipped if no supported right remains. -For network access-control, we can add a set of rules that allow to use a port -number for a specific action: HTTPS connections. +For network access-control, we will add a set of rules to allow DNS +queries, which requires both UDP and TCP. For TCP, we need to allow +outbound connections to port 53, which can be handled and granted starting +with ABI 4: .. code-block:: c - struct landlock_net_port_attr net_port = { + struct landlock_net_port_attr tcp_conn = { .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, - .port = 443, + .port = 53, }; - net_port.allowed_access &= ruleset_attr.handled_access_net; - if (net_port.allowed_access) + tcp_conn.allowed_access &= ruleset_attr.handled_access_net; + if (tcp_conn.allowed_access) err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &net_port, 0); + &tcp_conn, 0); + +We also need to be able to send UDP datagrams to port 53, which requires +granting ``LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP``. Since our DNS client will +emit datagrams without explicitly binding to a specific source port, its UDP +socket will automatically bind an ephemeral port. To allow this behaviour, +we also need to grant ``LANDLOCK_ACCESS_NET_BIND_UDP`` on port 0, as if +the program explicitly called :manpage:`bind(2)` on port 0. + +.. code-block:: c + + struct landlock_net_port_attr udp_send = { + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, + .port = 53, + }; + + udp_send.allowed_access &= ruleset_attr.handled_access_net; + if (udp_send.allowed_access) + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &udp_send, 0); + [...] + + struct landlock_net_port_attr udp_bind = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_UDP, + .port = 0, + }; + + udp_bind.allowed_access &= ruleset_attr.handled_access_net; + if (udp_bind.allowed_access) + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &udp_bind, 0); When passing a non-zero ``flags`` argument to ``landlock_restrict_self()``, a similar backwards compatibility check is needed for the restrict flags @@ -234,7 +274,7 @@ similar backwards compatibility check is needed for the restrict flags The next step is to restrict the current thread from gaining more privileges (e.g. through a SUID binary). We now have a ruleset with the first rule allowing read and execute access to ``/usr`` while denying all other handled -accesses for the filesystem, and a second rule allowing HTTPS connections. +accesses for the filesystem, and two more rules allowing DNS queries. .. code-block:: c @@ -722,6 +762,19 @@ Starting with the Landlock ABI version 9, it is possible to restrict connections to pathname UNIX domain sockets (:manpage:`unix(7)`) using the new ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` right. +UDP bind, connect and send* (ABI < 10) +-------------------------------------- + +Starting with the Landlock ABI version 10, it is possible to restrict +setting the local port of UDP sockets with the +``LANDLOCK_ACCESS_NET_BIND_UDP`` right. This includes restricting the +ability to trigger autobind of an ephemeral port by the kernel by e.g. +sending a first datagram or setting the remote peer of a socket. +The ``LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP`` right controls setting the +remote port of UDP sockets (via :manpage:`connect(2)`), and sending +datagrams to an explicit remote port (ignoring any destination set on +UDP sockets, via e.g. :manpage:`sendto(2)`). + .. _kernel_support: Kernel support @@ -784,10 +837,10 @@ the boot loader. Network support --------------- -To be able to explicitly allow TCP operations (e.g., adding a network rule with -``LANDLOCK_ACCESS_NET_BIND_TCP``), the kernel must support TCP +To be able to explicitly allow TCP or UDP operations (e.g., adding a network rule with +``LANDLOCK_ACCESS_NET_BIND_TCP``), the kernel must support the TCP/IP protocol suite (``CONFIG_INET=y``). Otherwise, sys_landlock_add_rule() returns an -``EAFNOSUPPORT`` error, which can safely be ignored because this kind of TCP +``EAFNOSUPPORT`` error, which can safely be ignored because this kind of TCP or UDP operation is already not possible. Questions and answers From a260c0055665fc38804400b3dbdca165d5e0aa15 Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:47 +0100 Subject: [PATCH 3624/5214] landlock: Add a place for flags to layer rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid unnecessarily increasing the size of struct landlock_layer, we make the layer level a u8 and use the space to store the flags struct. struct layer_access_masks is renamed to struct layer_masks, and a new field is added to track whether a quiet flag rule is seen for each layer. Through use of bitfields, this does not increase the size of the struct. Cc: Justin Suess Assisted-by: GitHub-Copilot:claude-opus-4.8 copilot-review Signed-off-by: Tingmao Wang Co-developed-by: Justin Suess Signed-off-by: Justin Suess Tested-by: Justin Suess Link: https://patch.msgid.link/be3fec3927bc9faaacd4ce0e7f0d1ff5474e2210.1781228815.git.m@maowtm.org [mic: Fix comment formatting] Signed-off-by: Mickaël Salaün --- security/landlock/access.h | 39 ++++++++-- security/landlock/audit.c | 20 ++--- security/landlock/audit.h | 2 +- security/landlock/domain.c | 19 ++--- security/landlock/domain.h | 2 +- security/landlock/fs.c | 147 +++++++++++++++++++----------------- security/landlock/limits.h | 3 + security/landlock/net.c | 2 +- security/landlock/ruleset.c | 37 ++++++--- security/landlock/ruleset.h | 17 ++++- 10 files changed, 176 insertions(+), 112 deletions(-) diff --git a/security/landlock/access.h b/security/landlock/access.h index c19d5bc13944..ce374a65f865 100644 --- a/security/landlock/access.h +++ b/security/landlock/access.h @@ -62,18 +62,41 @@ static_assert(sizeof(typeof_member(union access_masks_all, masks)) == sizeof(typeof_member(union access_masks_all, all))); /** - * struct layer_access_masks - A boolean matrix of layers and access rights + * struct layer_mask - The access rights and rule flags for a layer. * - * This has a bit for each combination of layer numbers and access rights. - * During access checks, it is used to represent the access rights for each - * layer which still need to be fulfilled. When all bits are 0, the access - * request is considered to be fulfilled. + * This has a bit for each access rights and rule flags. During access checks, + * it is used to represent the access rights for each layer which still need to + * be fulfilled. When all bits are 0, the access request is considered to be + * fulfilled. */ -struct layer_access_masks { +struct layer_mask { /** - * @access: The unfulfilled access rights for each layer. + * @access: The unfulfilled access rights for this layer. */ - access_mask_t access[LANDLOCK_MAX_NUM_LAYERS]; + access_mask_t access : LANDLOCK_NUM_ACCESS_MAX; +#ifdef CONFIG_AUDIT + /** + * @quiet: Whether we have encountered a rule with the quiet flag for + * this layer. Used to control logging. + */ + access_mask_t quiet : 1; +#endif /* CONFIG_AUDIT */ +} __packed __aligned(sizeof(access_mask_t)); + +/* + * Make sure that we don't increase the size of struct layer_mask when storing + * rule flags. + */ +static_assert(sizeof(struct layer_mask) == sizeof(access_mask_t)); + +/** + * struct layer_masks - An array of struct layer_mask, one per layer. + */ +struct layer_masks { + /** + * @layers: The unfulfilled access rights for each layer. + */ + struct layer_mask layers[LANDLOCK_MAX_NUM_LAYERS]; }; /* diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 851647197a01..8c56f7f6467a 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -187,11 +187,11 @@ static void test_get_hierarchy(struct kunit *const test) /* Get the youngest layer that denied the access_request. */ static size_t get_denied_layer(const struct landlock_ruleset *const domain, access_mask_t *const access_request, - const struct layer_access_masks *masks) + const struct layer_masks *masks) { - for (ssize_t i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) { - if (masks->access[i] & *access_request) { - *access_request &= masks->access[i]; + for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) { + if (masks->layers[i].access & *access_request) { + *access_request &= masks->layers[i].access; return i; } } @@ -208,12 +208,12 @@ static void test_get_denied_layer(struct kunit *const test) const struct landlock_ruleset dom = { .num_layers = 5, }; - const struct layer_access_masks masks = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_READ_DIR, - .access[1] = LANDLOCK_ACCESS_FS_READ_FILE | - LANDLOCK_ACCESS_FS_READ_DIR, - .access[2] = LANDLOCK_ACCESS_FS_REMOVE_DIR, + const struct layer_masks masks = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_DIR, + .layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR, + .layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR, }; access_mask_t access; diff --git a/security/landlock/audit.h b/security/landlock/audit.h index 56778331b58c..b85d752273ac 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -43,7 +43,7 @@ struct landlock_request { access_mask_t access; /* Required fields for requests with layer masks. */ - const struct layer_access_masks *layer_masks; + const struct layer_masks *layer_masks; /* Required fields for requests with deny masks. */ const access_mask_t all_existing_optional_access; diff --git a/security/landlock/domain.c b/security/landlock/domain.c index 04b69b43fbf5..4ae45b300071 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -184,7 +184,7 @@ static void test_get_layer_deny_mask(struct kunit *const test) deny_masks_t landlock_get_deny_masks(const access_mask_t all_existing_optional_access, const access_mask_t optional_access, - const struct layer_access_masks *const masks) + const struct layer_masks *const masks) { const unsigned long access_opt = optional_access; unsigned long access_bit; @@ -201,8 +201,9 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access, if (WARN_ON_ONCE(!access_opt)) return 0; - for (ssize_t i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) { - const access_mask_t denied = masks->access[i] & optional_access; + for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) { + const access_mask_t denied = masks->layers[i].access & + optional_access; const unsigned long newly_denied = denied & ~all_denied; if (!newly_denied) @@ -222,12 +223,12 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access, static void test_landlock_get_deny_masks(struct kunit *const test) { - const struct layer_access_masks layers1 = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_IOCTL_DEV, - .access[1] = LANDLOCK_ACCESS_FS_TRUNCATE, - .access[2] = LANDLOCK_ACCESS_FS_IOCTL_DEV, - .access[9] = LANDLOCK_ACCESS_FS_EXECUTE, + const struct layer_masks layers1 = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_IOCTL_DEV, + .layers[1].access = LANDLOCK_ACCESS_FS_TRUNCATE, + .layers[2].access = LANDLOCK_ACCESS_FS_IOCTL_DEV, + .layers[9].access = LANDLOCK_ACCESS_FS_EXECUTE, }; KUNIT_EXPECT_EQ(test, 0x1, diff --git a/security/landlock/domain.h b/security/landlock/domain.h index 35cac8f6daee..af100a8cd939 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -119,7 +119,7 @@ struct landlock_hierarchy { deny_masks_t landlock_get_deny_masks(const access_mask_t all_existing_optional_access, const access_mask_t optional_access, - const struct layer_access_masks *const masks); + const struct layer_masks *const masks); int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy); diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 664962a416d7..d7cd2d5c9057 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -406,15 +406,15 @@ static const struct access_masks any_fs = { * src_parent would result in having the same or fewer access rights if it were * moved under new_parent. */ -static bool may_refer(const struct layer_access_masks *const src_parent, - const struct layer_access_masks *const src_child, - const struct layer_access_masks *const new_parent, +static bool may_refer(const struct layer_masks *const src_parent, + const struct layer_masks *const src_child, + const struct layer_masks *const new_parent, const bool child_is_dir) { - for (size_t i = 0; i < ARRAY_SIZE(new_parent->access); i++) { - access_mask_t child_access = src_parent->access[i] & - src_child->access[i]; - access_mask_t parent_access = new_parent->access[i]; + for (size_t i = 0; i < ARRAY_SIZE(new_parent->layers); i++) { + access_mask_t child_access = src_parent->layers[i].access & + src_child->layers[i].access; + access_mask_t parent_access = new_parent->layers[i].access; if (!child_is_dir) { child_access &= ACCESS_FILE; @@ -436,11 +436,11 @@ static bool may_refer(const struct layer_access_masks *const src_parent, * that child2 may be used from parent2 to parent1 without increasing its access * rights), false otherwise. */ -static bool no_more_access(const struct layer_access_masks *const parent1, - const struct layer_access_masks *const child1, +static bool no_more_access(const struct layer_masks *const parent1, + const struct layer_masks *const child1, const bool child1_is_dir, - const struct layer_access_masks *const parent2, - const struct layer_access_masks *const child2, + const struct layer_masks *const parent2, + const struct layer_masks *const child2, const bool child2_is_dir) { if (!may_refer(parent1, child1, parent2, child1_is_dir)) @@ -459,25 +459,25 @@ static bool no_more_access(const struct layer_access_masks *const parent1, static void test_no_more_access(struct kunit *const test) { - const struct layer_access_masks rx0 = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_READ_FILE, + const struct layer_masks rx0 = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE, }; - const struct layer_access_masks mx0 = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE | - LANDLOCK_ACCESS_FS_MAKE_REG, + const struct layer_masks mx0 = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, }; - const struct layer_access_masks x0 = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE, + const struct layer_masks x0 = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE, }; - const struct layer_access_masks x1 = { - .access[1] = LANDLOCK_ACCESS_FS_EXECUTE, + const struct layer_masks x1 = { + .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE, }; - const struct layer_access_masks x01 = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE, - .access[1] = LANDLOCK_ACCESS_FS_EXECUTE, + const struct layer_masks x01 = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE, + .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE, }; - const struct layer_access_masks allows_all = {}; + const struct layer_masks allows_all = {}; /* Checks without restriction. */ NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false); @@ -565,9 +565,13 @@ static void test_no_more_access(struct kunit *const test) #undef NMA_TRUE #undef NMA_FALSE -static bool is_layer_masks_allowed(const struct layer_access_masks *masks) +static bool is_layer_masks_allowed(const struct layer_masks *masks) { - return mem_is_zero(&masks->access, sizeof(masks->access)); + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) { + if (masks->layers[i].access) + return false; + } + return true; } /* @@ -576,16 +580,16 @@ static bool is_layer_masks_allowed(const struct layer_access_masks *masks) * Returns true if the request is allowed, false otherwise. */ static bool scope_to_request(const access_mask_t access_request, - struct layer_access_masks *masks) + struct layer_masks *masks) { bool saw_unfulfilled_access = false; if (WARN_ON_ONCE(!masks)) return true; - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) { - masks->access[i] &= access_request; - if (masks->access[i]) + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) { + masks->layers[i].access &= access_request; + if (masks->layers[i].access) saw_unfulfilled_access = true; } return !saw_unfulfilled_access; @@ -596,41 +600,46 @@ static bool scope_to_request(const access_mask_t access_request, static void test_scope_to_request_with_exec_none(struct kunit *const test) { /* Allows everything. */ - struct layer_access_masks masks = {}; + struct layer_masks masks = {}; /* Checks and scopes with execute. */ KUNIT_EXPECT_TRUE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, &masks)); - KUNIT_EXPECT_EQ(test, 0, masks.access[0]); + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access); } static void test_scope_to_request_with_exec_some(struct kunit *const test) { /* Denies execute and write. */ - struct layer_access_masks masks = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE, - .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE, + struct layer_masks masks = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE, + .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE, }; /* Checks and scopes with execute. */ KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, &masks)); - KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE, masks.access[0]); - KUNIT_EXPECT_EQ(test, 0, masks.access[1]); + /* + * These casts to access_mask_t are needed because typeof(), used in + * KUNIT_EXPECT_EQ(), does not work on bitfields. + */ + KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE, + (access_mask_t)masks.layers[0].access); + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access); } static void test_scope_to_request_without_access(struct kunit *const test) { /* Denies execute and write. */ - struct layer_access_masks masks = { - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE, - .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE, + struct layer_masks masks = { + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE, + .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE, }; /* Checks and scopes without access request. */ KUNIT_EXPECT_TRUE(test, scope_to_request(0, &masks)); - KUNIT_EXPECT_EQ(test, 0, masks.access[0]); - KUNIT_EXPECT_EQ(test, 0, masks.access[1]); + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access); + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ @@ -639,15 +648,15 @@ static void test_scope_to_request_without_access(struct kunit *const test) * Returns true if there is at least one access right different than * LANDLOCK_ACCESS_FS_REFER. */ -static bool is_eacces(const struct layer_access_masks *masks, +static bool is_eacces(const struct layer_masks *masks, const access_mask_t access_request) { if (!masks) return false; - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) { + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) { /* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */ - if (masks->access[i] & access_request & + if (masks->layers[i].access & access_request & ~LANDLOCK_ACCESS_FS_REFER) return true; } @@ -661,7 +670,7 @@ static bool is_eacces(const struct layer_access_masks *masks, static void test_is_eacces_with_none(struct kunit *const test) { - const struct layer_access_masks masks = {}; + const struct layer_masks masks = {}; IE_FALSE(&masks, 0); IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER); @@ -671,8 +680,8 @@ static void test_is_eacces_with_none(struct kunit *const test) static void test_is_eacces_with_refer(struct kunit *const test) { - const struct layer_access_masks masks = { - .access[0] = LANDLOCK_ACCESS_FS_REFER, + const struct layer_masks masks = { + .layers[0].access = LANDLOCK_ACCESS_FS_REFER, }; IE_FALSE(&masks, 0); @@ -683,8 +692,8 @@ static void test_is_eacces_with_refer(struct kunit *const test) static void test_is_eacces_with_write(struct kunit *const test) { - const struct layer_access_masks masks = { - .access[0] = LANDLOCK_ACCESS_FS_WRITE_FILE, + const struct layer_masks masks = { + .layers[0].access = LANDLOCK_ACCESS_FS_WRITE_FILE, }; IE_FALSE(&masks, 0); @@ -743,11 +752,11 @@ static bool is_access_to_paths_allowed(const struct landlock_ruleset *const domain, const struct path *const path, const access_mask_t access_request_parent1, - struct layer_access_masks *layer_masks_parent1, + struct layer_masks *layer_masks_parent1, struct landlock_request *const log_request_parent1, struct dentry *const dentry_child1, const access_mask_t access_request_parent2, - struct layer_access_masks *layer_masks_parent2, + struct layer_masks *layer_masks_parent2, struct landlock_request *const log_request_parent2, struct dentry *const dentry_child2) { @@ -755,9 +764,9 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain, child1_is_directory = true, child2_is_directory = true; struct path walker_path; access_mask_t access_masked_parent1, access_masked_parent2; - struct layer_access_masks _layer_masks_child1, _layer_masks_child2; - struct layer_access_masks *layer_masks_child1 = NULL, - *layer_masks_child2 = NULL; + struct layer_masks _layer_masks_child1, _layer_masks_child2; + struct layer_masks *layer_masks_child1 = NULL, + *layer_masks_child2 = NULL; if (!access_request_parent1 && !access_request_parent2) return true; @@ -797,6 +806,10 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain, } if (unlikely(dentry_child1)) { + /* + * Get the layer masks for the child dentries for use by domain + * check later. + */ if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS, &_layer_masks_child1, LANDLOCK_KEY_INODE)) @@ -952,7 +965,7 @@ static int current_check_access_path(const struct path *const path, }; const struct landlock_cred_security *const subject = landlock_get_applicable_subject(current_cred(), masks, NULL); - struct layer_access_masks layer_masks; + struct layer_masks layer_masks; struct landlock_request request = {}; if (!subject) @@ -1029,7 +1042,7 @@ static access_mask_t maybe_remove(const struct dentry *const dentry) static bool collect_domain_accesses(const struct landlock_ruleset *const domain, const struct dentry *const mnt_root, struct dentry *dir, - struct layer_access_masks *layer_masks_dom) + struct layer_masks *layer_masks_dom) { bool ret = false; @@ -1135,8 +1148,7 @@ static int current_check_refer_path(struct dentry *const old_dentry, access_mask_t access_request_parent1, access_request_parent2; struct path mnt_dir; struct dentry *old_parent; - struct layer_access_masks layer_masks_parent1 = {}, - layer_masks_parent2 = {}; + struct layer_masks layer_masks_parent1 = {}, layer_masks_parent2 = {}; struct landlock_request request1 = {}, request2 = {}; if (!subject) @@ -1202,7 +1214,6 @@ static int current_check_refer_path(struct dentry *const old_dentry, allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); - if (allow_parent1 && allow_parent2) return 0; @@ -1580,7 +1591,7 @@ static int hook_path_truncate(const struct path *const path) */ static void unmask_scoped_access(const struct landlock_ruleset *const client, const struct landlock_ruleset *const server, - struct layer_access_masks *const masks, + struct layer_masks *const masks, const access_mask_t access) { int client_layer, server_layer; @@ -1621,9 +1632,9 @@ static void unmask_scoped_access(const struct landlock_ruleset *const client, server_walker = server_walker->parent; for (; client_layer >= 0; client_layer--) { - if (masks->access[client_layer] & access && + if (masks->layers[client_layer].access & access && client_walker == server_walker) - masks->access[client_layer] &= ~access; + masks->layers[client_layer].access &= ~access; client_walker = client_walker->parent; server_walker = server_walker->parent; @@ -1635,7 +1646,7 @@ static int hook_unix_find(const struct path *const path, struct sock *other, { const struct landlock_ruleset *dom_other; const struct landlock_cred_security *subject; - struct layer_access_masks layer_masks; + struct layer_masks layer_masks; struct landlock_request request = {}; static const struct access_masks fs_resolve_unix = { .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, @@ -1739,7 +1750,7 @@ static bool is_device(const struct file *const file) static int hook_file_open(struct file *const file) { - struct layer_access_masks layer_masks = {}; + struct layer_masks layer_masks = {}; access_mask_t open_access_request, full_access_request, allowed_access, optional_access; const struct landlock_cred_security *const subject = @@ -1780,8 +1791,8 @@ static int hook_file_open(struct file *const file) * are still unfulfilled in any of the layers. */ allowed_access = full_access_request; - for (size_t i = 0; i < ARRAY_SIZE(layer_masks.access); i++) - allowed_access &= ~layer_masks.access[i]; + for (size_t i = 0; i < ARRAY_SIZE(layer_masks.layers); i++) + allowed_access &= ~layer_masks.layers[i].access; } /* diff --git a/security/landlock/limits.h b/security/landlock/limits.h index a4d908b240a2..08d5f2f6d321 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -31,6 +31,9 @@ #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) +#define LANDLOCK_NUM_ACCESS_MAX \ + MAX(MAX(LANDLOCK_NUM_ACCESS_FS, LANDLOCK_NUM_ACCESS_NET), LANDLOCK_NUM_SCOPE) + #define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_TSYNC #define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1) diff --git a/security/landlock/net.c b/security/landlock/net.c index 942f856433c9..d7a4d116f7ee 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -49,7 +49,7 @@ static int current_check_access_socket(struct socket *const sock, { unsigned short sock_family; __be16 port; - struct layer_access_masks layer_masks = {}; + struct layer_masks layer_masks = {}; const struct landlock_rule *rule; struct landlock_id id = { .type = LANDLOCK_KEY_NET_PORT, diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 181df7736bb9..23779e473563 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -628,7 +628,7 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset, * remaining unfulfilled access rights and masks has no leftover set bits). */ bool landlock_unmask_layers(const struct landlock_rule *const rule, - struct layer_access_masks *masks) + struct layer_masks *masks) { if (!masks) return true; @@ -649,11 +649,17 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule, const struct landlock_layer *const layer = &rule->layers[i]; /* Clear the bits where the layer in the rule grants access. */ - masks->access[layer->level - 1] &= ~layer->access; + masks->layers[layer->level - 1].access &= ~layer->access; + +#ifdef CONFIG_AUDIT + /* Collect rule flags for each layer. */ + if (layer->flags.quiet) + masks->layers[layer->level - 1].quiet = true; +#endif /* CONFIG_AUDIT */ } - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) { - if (masks->access[i]) + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) { + if (masks->layers[i].access) return false; } return true; @@ -666,8 +672,9 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset, /** * landlock_init_layer_masks - Initialize layer masks from an access request * - * Populates @masks such that for each access right in @access_request, - * the bits for all the layers are set where this access right is handled. + * Populates @masks such that for each access right in @access_request, the bits + * for all the layers are set where this access right is handled. Rule flags + * are also zeroed. * * @domain: The domain that defines the current restrictions. * @access_request: The requested access rights to check. @@ -680,7 +687,7 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset, access_mask_t landlock_init_layer_masks(const struct landlock_ruleset *const domain, const access_mask_t access_request, - struct layer_access_masks *const masks, + struct layer_masks *const masks, const enum landlock_key_type key_type) { access_mask_t handled_accesses = 0; @@ -709,11 +716,19 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain, for (size_t i = 0; i < domain->num_layers; i++) { const access_mask_t handled = get_access_mask(domain, i); - masks->access[i] = access_request & handled; - handled_accesses |= masks->access[i]; + masks->layers[i].access = access_request & handled; + handled_accesses |= masks->layers[i].access; +#ifdef CONFIG_AUDIT + masks->layers[i].quiet = false; +#endif /* CONFIG_AUDIT */ + } + for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->layers); + i++) { + masks->layers[i].access = 0; +#ifdef CONFIG_AUDIT + masks->layers[i].quiet = false; +#endif /* CONFIG_AUDIT */ } - for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->access); i++) - masks->access[i] = 0; return handled_accesses; } diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 889f4b30301a..ffcb29a1c437 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -29,7 +29,18 @@ struct landlock_layer { /** * @level: Position of this layer in the layer stack. Starts from 1. */ - u16 level; + u8 level; + /** + * @flags: Bitfield for special flags attached to this rule. + */ + struct { + /** + * @quiet: Suppresses denial logs for the object covered by this + * rule in this domain. For filesystem rules, this inherits + * down the file hierarchy. + */ + u8 quiet : 1; + } flags; /** * @access: Bitfield of allowed actions on the kernel object. They are * relative to the object type (e.g. %LANDLOCK_ACTION_FS_READ). @@ -302,12 +313,12 @@ landlock_get_scope_mask(const struct landlock_ruleset *const ruleset, } bool landlock_unmask_layers(const struct landlock_rule *const rule, - struct layer_access_masks *masks); + struct layer_masks *masks); access_mask_t landlock_init_layer_masks(const struct landlock_ruleset *const domain, const access_mask_t access_request, - struct layer_access_masks *masks, + struct layer_masks *masks, const enum landlock_key_type key_type); #endif /* _SECURITY_LANDLOCK_RULESET_H */ From 29752205db5ff1793437b352c9e343b8e41fb184 Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:48 +0100 Subject: [PATCH 3625/5214] landlock: Add API support and docs for the quiet flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the UAPI for the quiet flags feature (but not the implementation yet). Even though currently LANDLOCK_ADD_RULE_QUIET only affects audit logging, in the future this can also be used as part of a supervisor mechanism, where it will also suppress denial notifications on a per-object basis. Thus the name is deliberately generic, as opposed to e.g. LANDLOCK_ADD_RULE_LOG_QUIET. According to pahole, even after adding the struct access_masks quiet_masks in struct landlock_hierarchy, the u32 log_* bitfield still only has a size of 2 bytes, so there's minimal wasted space. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Tingmao Wang [mic: Update date, fix comment formatting] Link: https://patch.msgid.link/031184748a8e74c0bb02f1fa13d7a3f10918c627.1781228815.git.m@maowtm.org Signed-off-by: Mickaël Salaün --- Documentation/admin-guide/LSM/landlock.rst | 9 ++- Documentation/userspace-api/landlock.rst | 14 ++++ include/uapi/linux/landlock.h | 60 +++++++++++++++++ security/landlock/domain.h | 5 ++ security/landlock/fs.c | 4 +- security/landlock/fs.h | 2 +- security/landlock/net.c | 5 +- security/landlock/net.h | 5 +- security/landlock/ruleset.c | 12 +++- security/landlock/ruleset.h | 12 +++- security/landlock/syscalls.c | 71 +++++++++++++++----- tools/testing/selftests/landlock/base_test.c | 2 +- 12 files changed, 170 insertions(+), 31 deletions(-) diff --git a/Documentation/admin-guide/LSM/landlock.rst b/Documentation/admin-guide/LSM/landlock.rst index 2dacb381c1a9..314052bbeb0a 100644 --- a/Documentation/admin-guide/LSM/landlock.rst +++ b/Documentation/admin-guide/LSM/landlock.rst @@ -19,8 +19,10 @@ Audit Denied access requests are logged by default for a sandboxed program if `audit` is enabled. This default behavior can be changed with the sys_landlock_restrict_self() flags (cf. -Documentation/userspace-api/landlock.rst). Landlock logs can also be masked -thanks to audit rules. Landlock can generate 2 audit record types. +Documentation/userspace-api/landlock.rst), or suppressed on a per-object +basis by using ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+). Landlock logs can +also be masked thanks to audit rules. Landlock can generate 2 audit +record types. Record types ------------ @@ -174,7 +176,8 @@ If you get spammed with audit logs related to Landlock, this is either an attack attempt or a bug in the security policy. We can put in place some filters to limit noise with two complementary ways: -- with sys_landlock_restrict_self()'s flags if we can fix the sandboxed +- with sys_landlock_restrict_self()'s flags, or + ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+) if we can fix the sandboxed programs, - or with audit rules (see :manpage:`auditctl(8)`). diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index b5a2ab6f4766..5a63d4476c1c 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -775,6 +775,20 @@ remote port of UDP sockets (via :manpage:`connect(2)`), and sending datagrams to an explicit remote port (ignoring any destination set on UDP sockets, via e.g. :manpage:`sendto(2)`). +Quiet rule flag (ABI < 10) +-------------------------- + +Starting with the Landlock ABI version 10, it is possible to selectively +suppress logs for specific denied accesses on a per-object basis with +the ``LANDLOCK_ADD_RULE_QUIET`` flag of sys_landlock_add_rule(), in +combination with the ``quiet_access_fs`` and ``quiet_access_net`` fields +of struct landlock_ruleset_attr. It is also now possible to suppress +logs for scope accesses via the ``quiet_scoped`` field of struct +landlock_ruleset_attr. The object is marked as quiet within a ruleset +when at least one sys_landlock_add_rule() call is made for it with the +``LANDLOCK_ADD_RULE_QUIET`` flag, additional add-rule calls for the same +object without this flag do not clear it. + .. _kernel_support: Kernel support diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 811ec77f9105..7ffe2ef127ee 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -32,6 +32,19 @@ * *handle* a wide range or all access rights that they know about at build time * (and that they have tested with a kernel that supported them all). * + * @quiet_access_fs and @quiet_access_net are bitmasks of actions for which a + * denial by this layer will not trigger a log if the corresponding object (or + * its children, for filesystem rules) is marked with the "quiet" bit via + * %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per + * landlock_restrict_self() flags. @quiet_scoped is similar, except that it + * does not require marking any objects as quiet - if the ruleset is created + * with any bits set in @quiet_scoped, then denial of such scoped resources will + * not trigger any log. These 3 fields are available since Landlock ABI version + * 10. + * + * @quiet_access_fs, @quiet_access_net and @quiet_scoped must be a subset of + * @handled_access_fs, @handled_access_net and @scoped respectively. + * * This structure can grow in future Landlock versions. */ struct landlock_ruleset_attr { @@ -51,6 +64,20 @@ struct landlock_ruleset_attr { * resources (e.g. IPCs). */ __u64 scoped; + /** + * @quiet_access_fs: Bitmask of filesystem actions which should not be + * logged if per-object quiet flag is set. + */ + __u64 quiet_access_fs; + /** + * @quiet_access_net: Bitmask of network actions which should not be + * logged if per-object quiet flag is set. + */ + __u64 quiet_access_net; + /** + * @quiet_scoped: Bitmask of scoped actions which should not be logged. + */ + __u64 quiet_scoped; }; /** @@ -69,6 +96,39 @@ struct landlock_ruleset_attr { #define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1) /* clang-format on */ +/** + * DOC: landlock_add_rule_flags + * + * **Flags** + * + * %LANDLOCK_ADD_RULE_QUIET + * Together with the quiet_* fields in struct landlock_ruleset_attr, + * this flag controls whether Landlock will log audit messages when + * access to the objects covered by this rule is denied by this layer. + * + * If logging is enabled, when Landlock denies an access, it will + * suppress the log if all of the following are true: + * + * - this layer is the innermost layer that denied the access; + * - all accesses denied by this layer are part of the quiet_* fields + * in the related struct landlock_ruleset_attr; + * - the object (or one of its parents, for filesystem rules) is + * marked as "quiet" via %LANDLOCK_ADD_RULE_QUIET. + * + * Because logging is only suppressed by a layer if the layer denies + * access, a sandboxed program cannot use this flag to "hide" access + * denials, without denying itself the access in the first place. + * + * The effect of this flag does not depend on the value of + * allowed_access in the passed in rule_attr. When this flag is + * present, the caller is also allowed to pass in an empty + * allowed_access. + */ + +/* clang-format off */ +#define LANDLOCK_ADD_RULE_QUIET (1U << 0) +/* clang-format on */ + /** * DOC: landlock_restrict_self_flags * diff --git a/security/landlock/domain.h b/security/landlock/domain.h index af100a8cd939..9f560f3c3bd1 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -111,6 +111,11 @@ struct landlock_hierarchy { * %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default. */ log_new_exec : 1; + /** + * @quiet_masks: Bitmasks of access that should be quieted (i.e. not + * logged) if the related object is marked as quiet. + */ + struct access_masks quiet_masks; #endif /* CONFIG_AUDIT */ }; diff --git a/security/landlock/fs.c b/security/landlock/fs.c index d7cd2d5c9057..bd68a752abbf 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -325,7 +325,7 @@ static struct landlock_object *get_inode_object(struct inode *const inode) */ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, const struct path *const path, - access_mask_t access_rights) + access_mask_t access_rights, const u32 flags) { int err; struct landlock_id id = { @@ -346,7 +346,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, if (IS_ERR(id.key.object)) return PTR_ERR(id.key.object); mutex_lock(&ruleset->lock); - err = landlock_insert_rule(ruleset, id, access_rights); + err = landlock_insert_rule(ruleset, id, access_rights, flags); mutex_unlock(&ruleset->lock); /* * No need to check for an error because landlock_insert_rule() diff --git a/security/landlock/fs.h b/security/landlock/fs.h index 911b83669e20..e4c530511360 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -136,6 +136,6 @@ __init void landlock_add_fs_hooks(void); int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, const struct path *const path, - access_mask_t access_hierarchy); + access_mask_t access_hierarchy, const u32 flags); #endif /* _SECURITY_LANDLOCK_FS_H */ diff --git a/security/landlock/net.c b/security/landlock/net.c index d7a4d116f7ee..cbff59ec3aba 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -20,7 +20,8 @@ #include "ruleset.h" int landlock_append_net_rule(struct landlock_ruleset *const ruleset, - const u16 port, access_mask_t access_rights) + const u16 port, access_mask_t access_rights, + const u32 flags) { int err; const struct landlock_id id = { @@ -35,7 +36,7 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset, ~landlock_get_net_access_mask(ruleset, 0); mutex_lock(&ruleset->lock); - err = landlock_insert_rule(ruleset, id, access_rights); + err = landlock_insert_rule(ruleset, id, access_rights, flags); mutex_unlock(&ruleset->lock); return err; diff --git a/security/landlock/net.h b/security/landlock/net.h index 09960c237a13..5c0e3b4090cb 100644 --- a/security/landlock/net.h +++ b/security/landlock/net.h @@ -16,7 +16,8 @@ __init void landlock_add_net_hooks(void); int landlock_append_net_rule(struct landlock_ruleset *const ruleset, - const u16 port, access_mask_t access_rights); + const u16 port, access_mask_t access_rights, + const u32 flags); #else /* IS_ENABLED(CONFIG_INET) */ static inline void landlock_add_net_hooks(void) { @@ -24,7 +25,7 @@ static inline void landlock_add_net_hooks(void) static inline int landlock_append_net_rule(struct landlock_ruleset *const ruleset, const u16 port, - access_mask_t access_rights) + access_mask_t access_rights, const u32 flags) { return -EAFNOSUPPORT; } diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 23779e473563..4dd09ea22c84 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "access.h" #include "domain.h" @@ -255,6 +256,7 @@ static int insert_rule(struct landlock_ruleset *const ruleset, if (WARN_ON_ONCE(this->layers[0].level != 0)) return -EINVAL; this->layers[0].access |= (*layers)[0].access; + this->layers[0].flags.quiet |= (*layers)[0].flags.quiet; return 0; } @@ -305,12 +307,15 @@ static void build_check_layer(void) /* @ruleset must be locked by the caller. */ int landlock_insert_rule(struct landlock_ruleset *const ruleset, const struct landlock_id id, - const access_mask_t access) + const access_mask_t access, const u32 flags) { struct landlock_layer layers[] = { { .access = access, /* When @level is zero, insert_rule() extends @ruleset. */ .level = 0, + .flags = { + .quiet = !!(flags & LANDLOCK_ADD_RULE_QUIET), + }, } }; build_check_layer(); @@ -351,6 +356,7 @@ static int merge_tree(struct landlock_ruleset *const dst, return -EINVAL; layers[0].access = walker_rule->layers[0].access; + layers[0].flags = walker_rule->layers[0].flags; err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers)); if (err) @@ -581,6 +587,10 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent, if (err) return ERR_PTR(err); +#ifdef CONFIG_AUDIT + new_dom->hierarchy->quiet_masks = ruleset->quiet_masks; +#endif /* CONFIG_AUDIT */ + return no_free_ptr(new_dom); } diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index ffcb29a1c437..61f3c253d5c9 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -156,8 +156,8 @@ struct landlock_ruleset { * @work_free: Enables to free a ruleset within a lockless * section. This is only used by * landlock_put_ruleset_deferred() when @usage reaches zero. - * The fields @lock, @usage, @num_rules, @num_layers and - * @access_masks are then unused. + * The fields @lock, @usage, @num_rules, @num_layers, + * @quiet_masks and @access_masks are then unused. */ struct work_struct work_free; struct { @@ -183,6 +183,12 @@ struct landlock_ruleset { * non-merged ruleset (i.e. not a domain). */ u32 num_layers; + /** + * @quiet_masks: Stores the quiet flags for an unmerged + * ruleset. For a merged domain, this is stored in each + * layer's struct landlock_hierarchy instead. + */ + struct access_masks quiet_masks; /** * @access_masks: Contains the subset of filesystem and * network actions that are restricted by a ruleset. @@ -213,7 +219,7 @@ DEFINE_FREE(landlock_put_ruleset, struct landlock_ruleset *, int landlock_insert_rule(struct landlock_ruleset *const ruleset, const struct landlock_id id, - const access_mask_t access); + const access_mask_t access, const u32 flags); struct landlock_ruleset * landlock_merge_ruleset(struct landlock_ruleset *const parent, diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index d45469d5d464..36b02892c62f 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -105,8 +105,11 @@ static void build_check_abi(void) ruleset_size = sizeof(ruleset_attr.handled_access_fs); ruleset_size += sizeof(ruleset_attr.handled_access_net); ruleset_size += sizeof(ruleset_attr.scoped); + ruleset_size += sizeof(ruleset_attr.quiet_access_fs); + ruleset_size += sizeof(ruleset_attr.quiet_access_net); + ruleset_size += sizeof(ruleset_attr.quiet_scoped); BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size); - BUILD_BUG_ON(sizeof(ruleset_attr) != 24); + BUILD_BUG_ON(sizeof(ruleset_attr) != 48); path_beneath_size = sizeof(path_beneath_attr.allowed_access); path_beneath_size += sizeof(path_beneath_attr.parent_fd); @@ -193,6 +196,9 @@ const int landlock_abi_version = 10; * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small * @size; + * - %EINVAL: quiet_access_fs, quiet_access_net, or quiet_scoped is not a + * subset of the corresponding handled_access_fs, handled_access_net, or + * scoped; * - %E2BIG: @attr or @size inconsistencies; * - %EFAULT: @attr or @size inconsistencies; * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. @@ -249,6 +255,21 @@ SYSCALL_DEFINE3(landlock_create_ruleset, if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE) return -EINVAL; + /* + * Check that quiet masks are subsets of the respective handled masks. + * Because of the checks above this is sufficient to also ensure that + * the quiet masks are valid access masks. + */ + if ((ruleset_attr.quiet_access_fs | ruleset_attr.handled_access_fs) != + ruleset_attr.handled_access_fs) + return -EINVAL; + if ((ruleset_attr.quiet_access_net | ruleset_attr.handled_access_net) != + ruleset_attr.handled_access_net) + return -EINVAL; + if ((ruleset_attr.quiet_scoped | ruleset_attr.scoped) != + ruleset_attr.scoped) + return -EINVAL; + /* Checks arguments and transforms to kernel struct. */ ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs, ruleset_attr.handled_access_net, @@ -256,6 +277,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset, if (IS_ERR(ruleset)) return PTR_ERR(ruleset); + ruleset->quiet_masks.fs = ruleset_attr.quiet_access_fs; + ruleset->quiet_masks.net = ruleset_attr.quiet_access_net; + ruleset->quiet_masks.scope = ruleset_attr.quiet_scoped; + /* Creates anonymous FD referring to the ruleset. */ ruleset_fd = anon_inode_getfd("[landlock-ruleset]", &ruleset_fops, ruleset, O_RDWR | O_CLOEXEC); @@ -320,7 +345,7 @@ static int get_path_from_fd(const s32 fd, struct path *const path) } static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, - const void __user *const rule_attr) + const void __user *const rule_attr, u32 flags) { struct landlock_path_beneath_attr path_beneath_attr; struct path path; @@ -335,9 +360,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, /* * Informs about useless rule: empty allowed_access (i.e. deny rules) - * are ignored in path walks. + * are ignored in path walks. However, the rule is not useless if it is + * there to hold a quiet flag. */ - if (!path_beneath_attr.allowed_access) + if (!flags && !path_beneath_attr.allowed_access) return -ENOMSG; /* Checks that allowed_access matches the @ruleset constraints. */ @@ -345,6 +371,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, if ((path_beneath_attr.allowed_access | mask) != mask) return -EINVAL; + /* Checks for useless quiet flag. */ + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.fs) + return -EINVAL; + /* Gets and checks the new rule. */ err = get_path_from_fd(path_beneath_attr.parent_fd, &path); if (err) @@ -352,13 +382,13 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, /* Imports the new rule. */ err = landlock_append_fs_rule(ruleset, &path, - path_beneath_attr.allowed_access); + path_beneath_attr.allowed_access, flags); path_put(&path); return err; } static int add_rule_net_port(struct landlock_ruleset *ruleset, - const void __user *const rule_attr) + const void __user *const rule_attr, u32 flags) { struct landlock_net_port_attr net_port_attr; int res; @@ -371,9 +401,10 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, /* * Informs about useless rule: empty allowed_access (i.e. deny rules) - * are ignored by network actions. + * are ignored by network actions. However, the rule is not useless if + * it is there to hold a quiet flag. */ - if (!net_port_attr.allowed_access) + if (!flags && !net_port_attr.allowed_access) return -ENOMSG; /* Checks that allowed_access matches the @ruleset constraints. */ @@ -381,13 +412,17 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, if ((net_port_attr.allowed_access | mask) != mask) return -EINVAL; + /* Checks for useless quiet flag. */ + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.net) + return -EINVAL; + /* Denies inserting a rule with port greater than 65535. */ if (net_port_attr.port > U16_MAX) return -EINVAL; /* Imports the new rule. */ return landlock_append_net_rule(ruleset, net_port_attr.port, - net_port_attr.allowed_access); + net_port_attr.allowed_access, flags); } /** @@ -398,7 +433,7 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, * @rule_type: Identify the structure type pointed to by @rule_attr: * %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_PORT. * @rule_attr: Pointer to a rule (matching the @rule_type). - * @flags: Must be 0. + * @flags: Must be 0 or %LANDLOCK_ADD_RULE_QUIET. * * This system call enables to define a new rule and add it to an existing * ruleset. @@ -408,20 +443,25 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; * - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not * supported by the running kernel; - * - %EINVAL: @flags is not 0; + * - %EINVAL: @flags is not valid; * - %EINVAL: The rule accesses are inconsistent (i.e. * &landlock_path_beneath_attr.allowed_access or * &landlock_net_port_attr.allowed_access is not a subset of the ruleset * handled accesses) * - %EINVAL: &landlock_net_port_attr.port is greater than 65535; + * - %EINVAL: LANDLOCK_ADD_RULE_QUIET is passed but the ruleset has no + * quiet access bits set for the corresponding rule type. * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is - * 0); + * 0) and no flags; * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a * member of @rule_attr is not a file descriptor as expected; * - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of * @rule_attr is not the expected file descriptor type; * - %EPERM: @ruleset_fd has no write access to the underlying ruleset; * - %EFAULT: @rule_attr was not a valid address. + * + * .. kernel-doc:: include/uapi/linux/landlock.h + * :identifiers: landlock_add_rule_flags */ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, const enum landlock_rule_type, rule_type, @@ -432,8 +472,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, if (!is_initialized()) return -EOPNOTSUPP; - /* No flag for now. */ - if (flags) + if (flags && flags != LANDLOCK_ADD_RULE_QUIET) return -EINVAL; /* Gets and checks the ruleset. */ @@ -443,9 +482,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, switch (rule_type) { case LANDLOCK_RULE_PATH_BENEATH: - return add_rule_path_beneath(ruleset, rule_attr); + return add_rule_path_beneath(ruleset, rule_attr, flags); case LANDLOCK_RULE_NET_PORT: - return add_rule_net_port(ruleset, rule_attr); + return add_rule_net_port(ruleset, rule_attr, flags); default: return -EINVAL; } diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 6c8113c2ded1..84e91fcaa1b2 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -201,7 +201,7 @@ TEST(add_rule_checks_ordering) ASSERT_LE(0, ruleset_fd); /* Checks invalid flags. */ - ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1)); + ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 100)); ASSERT_EQ(EINVAL, errno); /* Checks invalid ruleset FD. */ From 5f12f8effb5acb38a8b554ea39bd30d43d54f9f0 Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:49 +0100 Subject: [PATCH 3626/5214] landlock: Suppress logging when quiet flag is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quietness behaviour is as documented in the previous patch. For optional accesses, since the existing deny_masks can only store 2x4bit of layer index, with no way to represent "no layer", we need to either expand it or have another field to correctly handle quieting of those. This commit uses the latter approach - we add another field to store which optional access (of the 2) are covered by quiet rules in their respective layers as stored in deny_masks. Assisted-by: GitHub-Copilot:claude-opus-4.8 copilot-review Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/2510a357a94183683eefc49917dcb2240d67be96.1781228815.git.m@maowtm.org [mic: Cosmetic fixes] Signed-off-by: Mickaël Salaün --- security/landlock/access.h | 5 + security/landlock/audit.c | 269 ++++++++++++++++++++++++++++++++++--- security/landlock/audit.h | 1 + security/landlock/domain.c | 38 ++++++ security/landlock/domain.h | 4 + security/landlock/fs.c | 6 + security/landlock/fs.h | 17 ++- 7 files changed, 324 insertions(+), 16 deletions(-) diff --git a/security/landlock/access.h b/security/landlock/access.h index ce374a65f865..d926078bf0a5 100644 --- a/security/landlock/access.h +++ b/security/landlock/access.h @@ -143,4 +143,9 @@ static inline bool access_mask_subset(access_mask_t subset, return (subset | superset) == superset; } +/* A bitmask that is large enough to hold set of optional accesses. */ +typedef u8 optional_access_t; +static_assert(BITS_PER_TYPE(optional_access_t) >= + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL)); + #endif /* _SECURITY_LANDLOCK_ACCESS_H */ diff --git a/security/landlock/audit.c b/security/landlock/audit.c index 8c56f7f6467a..50536c568526 100644 --- a/security/landlock/audit.c +++ b/security/landlock/audit.c @@ -249,7 +249,9 @@ static void test_get_denied_layer(struct kunit *const test) static size_t get_layer_from_deny_masks(access_mask_t *const access_request, const access_mask_t all_existing_optional_access, - const deny_masks_t deny_masks) + const deny_masks_t deny_masks, + optional_access_t quiet_optional_accesses, + bool *quiet) { const unsigned long access_opt = all_existing_optional_access; const unsigned long access_req = *access_request; @@ -257,6 +259,7 @@ get_layer_from_deny_masks(access_mask_t *const access_request, size_t youngest_layer = 0; size_t access_index = 0; unsigned long access_bit; + bool should_quiet = false; /* This will require change with new object types. */ WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); @@ -265,20 +268,34 @@ get_layer_from_deny_masks(access_mask_t *const access_request, BITS_PER_TYPE(access_mask_t)) { if (access_req & BIT(access_bit)) { const size_t layer = - (deny_masks >> (access_index * 4)) & + (deny_masks >> + (access_index * + HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) & (LANDLOCK_MAX_NUM_LAYERS - 1); + const bool layer_has_quiet = + !!(quiet_optional_accesses & BIT(access_index)); if (layer > youngest_layer) { youngest_layer = layer; missing = BIT(access_bit); + should_quiet = layer_has_quiet; } else if (layer == youngest_layer) { missing |= BIT(access_bit); + /* + * Whether the layer has rules with quiet flag + * covering the file accessed does not depend on + * the access, and so the following + * WARN_ON_ONCE() should not fail. + */ + WARN_ON_ONCE(should_quiet && !layer_has_quiet); + should_quiet = layer_has_quiet; } } access_index++; } *access_request = missing; + *quiet = should_quiet; return youngest_layer; } @@ -288,42 +305,188 @@ static void test_get_layer_from_deny_masks(struct kunit *const test) { deny_masks_t deny_mask; access_mask_t access; + optional_access_t quiet_optional_accesses; + bool quiet; /* truncate:0 ioctl_dev:2 */ deny_mask = 0x20; + quiet_optional_accesses = 0; access = LANDLOCK_ACCESS_FS_TRUNCATE; KUNIT_EXPECT_EQ(test, 0, - get_layer_from_deny_masks(&access, - _LANDLOCK_ACCESS_FS_OPTIONAL, - deny_mask)); + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; KUNIT_EXPECT_EQ(test, 2, - get_layer_from_deny_masks(&access, - _LANDLOCK_ACCESS_FS_OPTIONAL, - deny_mask)); + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + /* layer denying truncate: quiet, ioctl: not quiet */ + quiet_optional_accesses = 0b01; + + access = LANDLOCK_ACCESS_FS_TRUNCATE; + KUNIT_EXPECT_EQ(test, 0, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, true); + + access = LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + /* Reverse order - truncate:2 ioctl_dev:0 */ + deny_mask = 0x02; + quiet_optional_accesses = 0; + + access = LANDLOCK_ACCESS_FS_TRUNCATE; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 0, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); + + /* layer denying truncate: quiet, ioctl: not quiet */ + quiet_optional_accesses = 0b01; + + access = LANDLOCK_ACCESS_FS_TRUNCATE; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, true); + + access = LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 0, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, true); + + /* layer denying truncate: not quiet, ioctl: quiet */ + quiet_optional_accesses = 0b10; + + access = LANDLOCK_ACCESS_FS_TRUNCATE; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); + + access = LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 0, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, true); + + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 2, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); /* truncate:15 ioctl_dev:15 */ deny_mask = 0xff; + quiet_optional_accesses = 0; access = LANDLOCK_ACCESS_FS_TRUNCATE; KUNIT_EXPECT_EQ(test, 15, - get_layer_from_deny_masks(&access, - _LANDLOCK_ACCESS_FS_OPTIONAL, - deny_mask)); + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, false); access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; KUNIT_EXPECT_EQ(test, 15, - get_layer_from_deny_masks(&access, - _LANDLOCK_ACCESS_FS_OPTIONAL, - deny_mask)); + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, false); + + /* Both quiet (same layer so quietness must be the same) */ + quiet_optional_accesses = 0b11; + + access = LANDLOCK_ACCESS_FS_TRUNCATE; + KUNIT_EXPECT_EQ(test, 15, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE); + KUNIT_EXPECT_EQ(test, quiet, true); + + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV; + KUNIT_EXPECT_EQ(test, 15, + get_layer_from_deny_masks( + &access, _LANDLOCK_ACCESS_FS_OPTIONAL, + deny_mask, quiet_optional_accesses, &quiet)); + KUNIT_EXPECT_EQ(test, access, + LANDLOCK_ACCESS_FS_TRUNCATE | + LANDLOCK_ACCESS_FS_IOCTL_DEV); + KUNIT_EXPECT_EQ(test, quiet, true); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ @@ -349,11 +512,34 @@ static bool is_valid_request(const struct landlock_request *const request) if (request->deny_masks) { if (WARN_ON_ONCE(!request->all_existing_optional_access)) return false; + static_assert(sizeof(request->all_existing_optional_access) == + sizeof(u32)); + if (WARN_ON_ONCE( + request->quiet_optional_accesses >= + BIT(hweight32( + request->all_existing_optional_access)))) + return false; } return true; } +static access_mask_t +pick_access_mask_for_request_type(const enum landlock_request_type type, + const struct access_masks access_masks) +{ + switch (type) { + case LANDLOCK_REQUEST_FS_ACCESS: + return access_masks.fs; + case LANDLOCK_REQUEST_NET_ACCESS: + return access_masks.net; + default: + WARN_ONCE(1, "Invalid request type %d passed to %s", type, + __func__); + return 0; + } +} + /** * landlock_log_denial - Create audit records related to a denial * @@ -367,6 +553,7 @@ void landlock_log_denial(const struct landlock_cred_security *const subject, struct landlock_hierarchy *youngest_denied; size_t youngest_layer; access_mask_t missing; + bool object_quiet_flag = false, quiet_applicable_to_access = false; if (WARN_ON_ONCE(!subject || !subject->domain || !subject->domain->hierarchy || !request)) @@ -382,10 +569,15 @@ void landlock_log_denial(const struct landlock_cred_security *const subject, youngest_layer = get_denied_layer(subject->domain, &missing, request->layer_masks); + object_quiet_flag = + request->layer_masks->layers[youngest_layer] + .quiet; } else { youngest_layer = get_layer_from_deny_masks( &missing, _LANDLOCK_ACCESS_FS_OPTIONAL, - request->deny_masks); + request->deny_masks, + request->quiet_optional_accesses, + &object_quiet_flag); } youngest_denied = get_hierarchy(subject->domain, youngest_layer); @@ -420,6 +612,53 @@ void landlock_log_denial(const struct landlock_cred_security *const subject, return; } + /* + * Checks if the object is marked quiet by the layer that denied the + * request. If it's a different layer that marked it as quiet, but that + * layer is not the one that denied the request, we should still audit + * log the denial. + */ + if (object_quiet_flag) { + /* + * We now check if the denied requests are all covered by the + * layer's quiet access bits. + */ + const access_mask_t quiet_mask = + pick_access_mask_for_request_type( + request->type, youngest_denied->quiet_masks); + + quiet_applicable_to_access = (quiet_mask & missing) == missing; + } else { + /* + * Either the object is not quiet, or this is a scope request. + * We check request->type to distinguish between the two cases. + */ + const access_mask_t quiet_mask = + youngest_denied->quiet_masks.scope; + + switch (request->type) { + case LANDLOCK_REQUEST_SCOPE_SIGNAL: + quiet_applicable_to_access = + !!(quiet_mask & LANDLOCK_SCOPE_SIGNAL); + break; + case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: + quiet_applicable_to_access = + !!(quiet_mask & + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); + break; + /* + * Leave LANDLOCK_REQUEST_PTRACE and + * LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY unhandled for now - they + * are never quiet. + */ + default: + break; + } + } + + if (quiet_applicable_to_access) + return; + /* Uses consistent allocation flags wrt common_lsm_audit(). */ ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, AUDIT_LANDLOCK_ACCESS); diff --git a/security/landlock/audit.h b/security/landlock/audit.h index b85d752273ac..620f8a24291d 100644 --- a/security/landlock/audit.h +++ b/security/landlock/audit.h @@ -48,6 +48,7 @@ struct landlock_request { /* Required fields for requests with deny masks. */ const access_mask_t all_existing_optional_access; deny_masks_t deny_masks; + optional_access_t quiet_optional_accesses; }; #ifdef CONFIG_AUDIT diff --git a/security/landlock/domain.c b/security/landlock/domain.c index 4ae45b300071..9a8355fccd26 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -157,6 +157,44 @@ get_layer_deny_mask(const access_mask_t all_existing_optional_access, << ((access_weight - 1) * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1)); } +/** + * landlock_get_quiet_optional_accesses - Get optional accesses which are + * covered by quiet rule flags. + * + * @all_existing_optional_access: Bitmask of valid optional accesses. + * @deny_masks: Domain layer levels that denied each optional access (the + * deny_masks field on struct landlock_file_security). + * @masks: The struct layer_masks collected during the path walk. + * + * Return: a bitmask of which optional accesses are denied by layers for which + * the quiet flag was collected during the path walk. + */ +optional_access_t landlock_get_quiet_optional_accesses( + const access_mask_t all_existing_optional_access, + const deny_masks_t deny_masks, const struct layer_masks *const masks) +{ + const unsigned long access_opt = all_existing_optional_access; + size_t access_index = 0; + unsigned long access_bit; + optional_access_t quiet_optional_accesses = 0; + + /* This will require change with new object types. */ + WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); + + for_each_set_bit(access_bit, &access_opt, + BITS_PER_TYPE(access_mask_t)) { + const u8 layer = + (deny_masks >> (access_index * + HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) & + (LANDLOCK_MAX_NUM_LAYERS - 1); + + if (masks->layers[layer].quiet) + quiet_optional_accesses |= BIT(access_index); + access_index++; + } + return quiet_optional_accesses; +} + #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_get_layer_deny_mask(struct kunit *const test) diff --git a/security/landlock/domain.h b/security/landlock/domain.h index 9f560f3c3bd1..2a1660e3dea7 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -126,6 +126,10 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access, const access_mask_t optional_access, const struct layer_masks *const masks); +optional_access_t landlock_get_quiet_optional_accesses( + const access_mask_t all_existing_optional_access, + const deny_masks_t deny_masks, const struct layer_masks *const masks); + int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy); static inline void diff --git a/security/landlock/fs.c b/security/landlock/fs.c index bd68a752abbf..6292887e6cef 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1805,6 +1805,10 @@ static int hook_file_open(struct file *const file) #ifdef CONFIG_AUDIT landlock_file(file)->deny_masks = landlock_get_deny_masks( _LANDLOCK_ACCESS_FS_OPTIONAL, optional_access, &layer_masks); + landlock_file(file)->quiet_optional_accesses = + landlock_get_quiet_optional_accesses( + _LANDLOCK_ACCESS_FS_OPTIONAL, + landlock_file(file)->deny_masks, &layer_masks); #endif /* CONFIG_AUDIT */ if (access_mask_subset(open_access_request, allowed_access)) @@ -1841,6 +1845,7 @@ static int hook_file_truncate(struct file *const file) .access = LANDLOCK_ACCESS_FS_TRUNCATE, #ifdef CONFIG_AUDIT .deny_masks = landlock_file(file)->deny_masks, + .quiet_optional_accesses = landlock_file(file)->quiet_optional_accesses, #endif /* CONFIG_AUDIT */ }); return -EACCES; @@ -1880,6 +1885,7 @@ static int hook_file_ioctl_common(const struct file *const file, .access = LANDLOCK_ACCESS_FS_IOCTL_DEV, #ifdef CONFIG_AUDIT .deny_masks = landlock_file(file)->deny_masks, + .quiet_optional_accesses = landlock_file(file)->quiet_optional_accesses, #endif /* CONFIG_AUDIT */ }); return -EACCES; diff --git a/security/landlock/fs.h b/security/landlock/fs.h index e4c530511360..b4421d9df68f 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -63,6 +63,13 @@ struct landlock_file_security { * _LANDLOCK_ACCESS_FS_OPTIONAL). */ deny_masks_t deny_masks; + /** + * @quiet_optional_accesses: Stores which optional accesses are covered + * by quiet rules within the layer referred to in deny_masks, one access + * per bit. Does not take into account whether the quiet access bits + * are actually set in the layer's corresponding landlock_hierarchy. + */ + optional_access_t quiet_optional_accesses; /** * @fown_layer: Layer level of @fown_subject->domain with * LANDLOCK_SCOPE_SIGNAL. @@ -96,7 +103,15 @@ struct landlock_file_security { /* clang-format off */ static_assert((typeof_member(struct landlock_file_security, fown_layer))~0 >= LANDLOCK_MAX_NUM_LAYERS); -/* clang-format off */ +/* clang-format on */ + +/* + * Make sure quiet_optional_accesses has enough bits to cover all optional + * accesses. + */ +static_assert(BITS_PER_TYPE(typeof_member(struct landlock_file_security, + quiet_optional_accesses)) >= + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL)); #endif /* CONFIG_AUDIT */ From 8faab14922b75c3c0bbc0769eedef85d4609a85a Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:50 +0100 Subject: [PATCH 3627/5214] samples/landlock: Add quiet flag support to sandboxer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds ability to set which access bits to quiet via LL_*_QUIET_ACCESS (FS, NET or SCOPED), and attach quiet flags to individual objects via LL_*_QUIET for FS and NET. Assisted-by: GitHub-Copilot:claude-opus-4.8 copilot-reviepickw Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/59b94997565032bc9870044f021214a2ed6df213.1781228815.git.m@maowtm.org [mic: Fix comment formatting] Signed-off-by: Mickaël Salaün --- samples/landlock/sandboxer.c | 138 ++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 11 deletions(-) diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index f44db2857bbf..ac71019e6212 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -58,9 +58,12 @@ static inline int landlock_restrict_self(const int ruleset_fd, #define ENV_FS_RO_NAME "LL_FS_RO" #define ENV_FS_RW_NAME "LL_FS_RW" +#define ENV_FS_QUIET_NAME "LL_FS_QUIET" #define ENV_TCP_BIND_NAME "LL_TCP_BIND" #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" +#define ENV_NET_QUIET_NAME "LL_NET_QUIET" #define ENV_SCOPED_NAME "LL_SCOPED" +#define ENV_QUIET_ACCESS_NAME "LL_QUIET_ACCESS" #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG" #define ENV_UDP_BIND_NAME "LL_UDP_BIND" #define ENV_UDP_CONNECT_SEND_NAME "LL_UDP_CONNECT_SEND" @@ -119,7 +122,7 @@ static int parse_path(char *env_path, const char ***const path_list) /* clang-format on */ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd, - const __u64 allowed_access) + const __u64 allowed_access, __u32 flags) { int num_paths, i, ret = 1; char *env_path_name; @@ -169,7 +172,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd, if (!S_ISDIR(statbuf.st_mode)) path_beneath.allowed_access &= ACCESS_FILE; if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, - &path_beneath, 0)) { + &path_beneath, flags)) { fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", path_list[i], strerror(errno)); @@ -187,7 +190,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd, } static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, - const __u64 allowed_access) + const __u64 allowed_access, __u32 flags) { int ret = 1; char *env_port_name, *env_port_name_next, *strport; @@ -215,7 +218,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, } net_port.port = port; if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, - &net_port, 0)) { + &net_port, flags)) { fprintf(stderr, "Failed to update the ruleset with port \"%llu\": %s\n", net_port.port, strerror(errno)); @@ -303,6 +306,69 @@ static bool check_ruleset_scope(const char *const env_var, /* clang-format on */ +/* + * Parses ENV_QUIET_ACCESS_NAME and sets the quiet_access_fs, quiet_access_net + * and quiet_scoped masks of @ruleset_attr accordingly. + */ +static int add_quiet_access(const char *const env_var, + struct landlock_ruleset_attr *const ruleset_attr) +{ + char *env_quiet_access, *env_quiet_access_next, *str_access; + + env_quiet_access = getenv(env_var); + if (!env_quiet_access) + return 0; + + env_quiet_access = strdup(env_quiet_access); + env_quiet_access_next = env_quiet_access; + unsetenv(env_var); + + while ((str_access = strsep(&env_quiet_access_next, ENV_DELIMITER))) { + if (strcmp(str_access, "") == 0) + continue; + else if (strcmp(str_access, "all") == 0) { + ruleset_attr->quiet_access_fs = + ruleset_attr->handled_access_fs; + ruleset_attr->quiet_access_net = + ruleset_attr->handled_access_net; + ruleset_attr->quiet_scoped = ruleset_attr->scoped; + } else if (strcmp(str_access, "read") == 0) + ruleset_attr->quiet_access_fs |= ACCESS_FS_ROUGHLY_READ; + else if (strcmp(str_access, "write") == 0) + ruleset_attr->quiet_access_fs |= + ACCESS_FS_ROUGHLY_WRITE; + else if (strcmp(str_access, "tcp_bind") == 0) + ruleset_attr->quiet_access_net |= + LANDLOCK_ACCESS_NET_BIND_TCP; + else if (strcmp(str_access, "tcp_connect") == 0) + ruleset_attr->quiet_access_net |= + LANDLOCK_ACCESS_NET_CONNECT_TCP; + else if (strcmp(str_access, "udp_bind") == 0) + ruleset_attr->quiet_access_net |= + LANDLOCK_ACCESS_NET_BIND_UDP; + else if (strcmp(str_access, "udp_connect") == 0) + ruleset_attr->quiet_access_net |= + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP; + else if (strcmp(str_access, "abstract_unix_socket") == 0) + ruleset_attr->quiet_scoped |= + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; + else if (strcmp(str_access, "signal") == 0) + ruleset_attr->quiet_scoped |= LANDLOCK_SCOPE_SIGNAL; + else { + fprintf(stderr, "Unknown quiet access \"%s\"\n", + str_access); + free(env_quiet_access); + return -1; + } + } + + free(env_quiet_access); + ruleset_attr->quiet_access_fs &= ruleset_attr->handled_access_fs; + ruleset_attr->quiet_access_net &= ruleset_attr->handled_access_net; + ruleset_attr->quiet_scoped &= ruleset_attr->scoped; + return 0; +} + #define LANDLOCK_ABI_LAST 10 #define XSTR(s) #s @@ -337,6 +403,19 @@ static const char help[] = "\n" "A sandboxer should not log denied access requests to avoid spamming logs, " "but to test audit we can set " ENV_FORCE_LOG_NAME "=1\n" + ENV_FS_QUIET_NAME " and " ENV_NET_QUIET_NAME ", both optional, can then be used " + "to make access to some denied paths or network ports not trigger audit logging.\n" + ENV_QUIET_ACCESS_NAME " can be used to specify which accesses should be quieted " + "(required when " ENV_FS_QUIET_NAME " or " ENV_NET_QUIET_NAME " is set):\n" + " - \"all\" to quiet all of the accesses below\n" + " - \"read\" to quiet all file/dir read accesses\n" + " - \"write\" to quiet all file/dir write accesses\n" + " - \"tcp_bind\" to quiet tcp bind denials\n" + " - \"tcp_connect\" to quiet tcp connect denials\n" + " - \"udp_bind\" to quiet udp bind denials\n" + " - \"udp_connect\" to quiet udp connect / send denials\n" + " - \"abstract_unix_socket\" to quiet abstract unix socket denials\n" + " - \"signal\" to quiet signal denials\n" "\n" "Example:\n" ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " @@ -369,7 +448,11 @@ int main(const int argc, char *const argv[], char *const *const envp) LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL, + .quiet_access_fs = 0, + .quiet_access_net = 0, + .quiet_scoped = 0, }; + bool quiet_supported = true; int supported_restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON; int set_restrict_flags = 0; @@ -460,6 +543,8 @@ int main(const int argc, char *const argv[], char *const *const envp) ruleset_attr.handled_access_net &= ~(LANDLOCK_ACCESS_NET_BIND_UDP | LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + /* Removes quiet flags for ABI < 10 later on. */ + quiet_supported = false; /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */ fprintf(stderr, @@ -526,6 +611,25 @@ int main(const int argc, char *const argv[], char *const *const envp) unsetenv(ENV_FORCE_LOG_NAME); } + /* Set the quiet access masks. */ + if (quiet_supported) { + if ((getenv(ENV_FS_QUIET_NAME) || getenv(ENV_NET_QUIET_NAME)) && + !getenv(ENV_QUIET_ACCESS_NAME)) { + fprintf(stderr, + "%s must be set (e.g. to \"all\") when %s or %s is used\n", + ENV_QUIET_ACCESS_NAME, ENV_FS_QUIET_NAME, + ENV_NET_QUIET_NAME); + return 1; + } + if (add_quiet_access(ENV_QUIET_ACCESS_NAME, &ruleset_attr)) + return 1; + } else if (getenv(ENV_FS_QUIET_NAME) || getenv(ENV_NET_QUIET_NAME) || + getenv(ENV_QUIET_ACCESS_NAME)) { + fprintf(stderr, + "Quiet flags not supported by current kernel\n"); + return 1; + } + ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) { @@ -533,30 +637,42 @@ int main(const int argc, char *const argv[], char *const *const envp) return 1; } - if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) { + if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro, 0)) goto err_close_ruleset; - } - if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) { + if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw, 0)) goto err_close_ruleset; + + /* Don't require this env to be present. */ + if (quiet_supported && getenv(ENV_FS_QUIET_NAME)) { + if (populate_ruleset_fs(ENV_FS_QUIET_NAME, ruleset_fd, 0, + LANDLOCK_ADD_RULE_QUIET)) + goto err_close_ruleset; } if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd, - LANDLOCK_ACCESS_NET_BIND_TCP)) { + LANDLOCK_ACCESS_NET_BIND_TCP, 0)) { goto err_close_ruleset; } if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd, - LANDLOCK_ACCESS_NET_CONNECT_TCP)) { + LANDLOCK_ACCESS_NET_CONNECT_TCP, 0)) { goto err_close_ruleset; } if (populate_ruleset_net(ENV_UDP_BIND_NAME, ruleset_fd, - LANDLOCK_ACCESS_NET_BIND_UDP)) { + LANDLOCK_ACCESS_NET_BIND_UDP, 0)) { goto err_close_ruleset; } if (populate_ruleset_net(ENV_UDP_CONNECT_SEND_NAME, ruleset_fd, - LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)) { + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, 0)) { goto err_close_ruleset; } + if (quiet_supported) { + if (populate_ruleset_net(ENV_NET_QUIET_NAME, ruleset_fd, 0, + LANDLOCK_ADD_RULE_QUIET)) { + goto err_close_ruleset; + } + } + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("Failed to restrict privileges"); goto err_close_ruleset; From 73c2f82b3253bb5448d7e26170feaa03189c436a Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:51 +0100 Subject: [PATCH 3628/5214] selftests/landlock: Replace hard-coded 16 with a constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next commit will reuse this number. Make it a shared constant to future-proof changes. Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/eff35caa9b4ac51aa83a88d67c4dd67f4f8b3a4a.1781228815.git.m@maowtm.org Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/audit_test.c | 2 +- tools/testing/selftests/landlock/common.h | 2 ++ tools/testing/selftests/landlock/fs_test.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index bd9f207b36e4..161ab5feb2b9 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -76,7 +76,7 @@ TEST_F(audit, layers) .scoped = LANDLOCK_SCOPE_SIGNAL, }; int status, ruleset_fd, i; - __u64(*domain_stack)[16]; + __u64(*domain_stack)[LANDLOCK_MAX_NUM_LAYERS]; __u64 prev_dom = 3; pid_t child; diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h index 90551650299c..7206d5105d66 100644 --- a/tools/testing/selftests/landlock/common.h +++ b/tools/testing/selftests/landlock/common.h @@ -25,6 +25,8 @@ /* TEST_F_FORK() should not be used for new tests. */ #define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name) +#define LANDLOCK_MAX_NUM_LAYERS 16 + static const char bin_sandbox_and_launch[] = "./sandbox-and-launch"; static const char bin_wait_pipe[] = "./wait-pipe"; static const char bin_wait_pipe_sandbox[] = "./wait-pipe-sandbox"; diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index cdb47fc1fc0a..10d9355ade5f 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -1441,7 +1441,7 @@ TEST_F_FORK(layout0, max_layers) }; const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); - for (i = 0; i < 16; i++) + for (i = 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) enforce_ruleset(_metadata, ruleset_fd); for (i = 0; i < 2; i++) { From c7d1c54edfd530d86cce798d46a1f058cc473e65 Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:52 +0100 Subject: [PATCH 3629/5214] selftests/landlock: Add tests for quiet flag with fs rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test various interactions of the quiet flag with filesystem rules: - Non-optional access (tested with open and rename). - Optional access (tested with truncate and ioctl). - Behaviour around mounts matches with normal Landlock rules. - Behaviour around disconnected directories matches with normal Landlock rules (test expected behaviour of 9a868cdbe66a ("landlock: Fix handling of disconnected directories") applied to the collected quiet flag). - Multiple layers works as expected. Assisted-by: GitHub-Copilot:claude-opus-4.6 copilot-review Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/0f304507dd3ebccc753e1580456bdfc909012357.1781228815.git.m@maowtm.org [mic: Fix comment formatting] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/fs_test.c | 2443 +++++++++++++++++++- 1 file changed, 2434 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 10d9355ade5f..86e08aa6e0a7 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -720,7 +720,7 @@ TEST_F_FORK(layout1, rule_with_unhandled_access) static void add_path_beneath(struct __test_metadata *const _metadata, const int ruleset_fd, const __u64 allowed_access, - const char *const path) + const char *const path, __u32 flags) { struct landlock_path_beneath_attr path_beneath = { .allowed_access = allowed_access, @@ -733,7 +733,7 @@ static void add_path_beneath(struct __test_metadata *const _metadata, strerror(errno)); } ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, - &path_beneath, 0)) + &path_beneath, flags)) { TH_LOG("Failed to update the ruleset with \"%s\": %s", path, strerror(errno)); @@ -780,7 +780,7 @@ static int create_ruleset(struct __test_metadata *const _metadata, continue; add_path_beneath(_metadata, ruleset_fd, rules[i].access, - rules[i].path); + rules[i].path, 0); } return ruleset_fd; } @@ -1310,7 +1310,7 @@ TEST_F_FORK(layout1, inherit_subset) * ANDed with the previous ones. */ add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, - dir_s1d2); + dir_s1d2, 0); /* * According to ruleset_fd, dir_s1d2 should now have the * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE @@ -1342,7 +1342,7 @@ TEST_F_FORK(layout1, inherit_subset) * Try to get more privileges by adding new access rights to the parent * directory: dir_s1d1. */ - add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); + add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1, 0); enforce_ruleset(_metadata, ruleset_fd); /* Same tests and results as above. */ @@ -1365,7 +1365,7 @@ TEST_F_FORK(layout1, inherit_subset) * that there was no rule tied to it before. */ add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, - dir_s1d3); + dir_s1d3, 0); enforce_ruleset(_metadata, ruleset_fd); ASSERT_EQ(0, close(ruleset_fd)); @@ -1417,7 +1417,7 @@ TEST_F_FORK(layout1, inherit_superset) add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR, - dir_s1d2); + dir_s1d2, 0); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -3970,7 +3970,7 @@ static int ioctl_error(struct __test_metadata *const _metadata, int fd, unsigned int cmd) { char buf[128]; /* sufficiently large */ - int res, stdinbak_fd; + int res, stdinbak_fd, err; /* * Depending on the IOCTL command, parts of the zeroed-out buffer might @@ -3985,13 +3985,14 @@ static int ioctl_error(struct __test_metadata *const _metadata, int fd, /* Invokes the IOCTL with a zeroed-out buffer. */ bzero(&buf, sizeof(buf)); res = ioctl(fd, cmd, &buf); + err = errno; /* Restores the old FD 0 and closes the backup FD. */ ASSERT_EQ(0, dup2(stdinbak_fd, 0)); ASSERT_EQ(0, close(stdinbak_fd)); if (res < 0) - return errno; + return err; return 0; } @@ -4789,6 +4790,7 @@ FIXTURE(layout1_bind) {}; static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; +static const char bind_file2_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f2"; /* Move targets for disconnected path tests. */ static const char dir_s4d1[] = TMP_DIR "/s4d1"; @@ -7764,4 +7766,2427 @@ TEST_F(audit_layout1, mount) EXPECT_EQ(1, records.domain); } +static bool debug_quiet_tests; + +FIXTURE(audit_quiet_layout1) +{ + struct audit_filter audit_filter; + int audit_fd; +}; + +FIXTURE_SETUP(audit_quiet_layout1) +{ + prepare_layout(_metadata); + create_layout1(_metadata); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); + EXPECT_LE(0, self->audit_fd); + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + if (getenv("DEBUG_QUIET_TESTS")) + debug_quiet_tests = true; +} + +FIXTURE_TEARDOWN_PARENT(audit_quiet_layout1) +{ + remove_layout1(_metadata); + cleanup_layout(_metadata); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_cleanup(-1, NULL)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); +} + +struct a_rule { + const char *path; + __u64 access; + bool quiet; +}; + +struct a_layer { + __u64 handled_access_fs; + __u64 quiet_access_fs; + struct a_rule rules[6]; + __u64 restrict_flags; +}; + +struct a_target { + /* File/dir to try open. */ + const char *target; + /* Open mode (one of O_RDONLY, O_WRONLY, or O_RDWR). */ + int open_mode; + /* Should open succeed? */ + bool expect_open_success; + /* If open fails, whether to expect an audit log for read. */ + bool audit_read_blocked; + /* If open fails, whether to expect an audit log for write. */ + bool audit_write_blocked; + /* If ftruncate() is expected to be allowed. */ + bool expect_truncate_success; + /* If ftruncate fails, whether to expect an audit log. */ + bool audit_truncate; + /* + * If ioctl() is expected to be allowed (ioctl not attempted if neither + * this nor expect_ioctl_denied is set). + */ + bool expect_ioctl_allowed; + /* If ioctl() is expected to be denied. */ + bool expect_ioctl_denied; + /* If ioctl fails, whether to expect an audit log. */ + bool audit_ioctl; +}; + +#define AUDIT_QUIET_MAX_TARGETS 10 + +FIXTURE_VARIANT(audit_quiet_layout1) +{ + struct a_layer layers[3]; + struct a_target targets[AUDIT_QUIET_MAX_TARGETS]; +}; + +#define FS_R LANDLOCK_ACCESS_FS_READ_FILE +#define FS_W LANDLOCK_ACCESS_FS_WRITE_FILE +#define FS_TRUNC LANDLOCK_ACCESS_FS_TRUNCATE +#define FS_IOCTL LANDLOCK_ACCESS_FS_IOCTL_DEV + +static int sprint_access_bits(char *buf, size_t buflen, __u64 access) +{ + size_t offset = 0; + + if (buflen < strlen("rwti make_reg remove_file refer") + 1) + abort(); + + buf[0] = '\0'; + if (access & FS_R) + offset += snprintf(buf + offset, buflen - offset, "r"); + if (access & FS_W) + offset += snprintf(buf + offset, buflen - offset, "w"); + if (access & FS_TRUNC) + offset += snprintf(buf + offset, buflen - offset, "t"); + if (access & FS_IOCTL) + offset += snprintf(buf + offset, buflen - offset, "i"); + if (access & LANDLOCK_ACCESS_FS_MAKE_REG) + offset += snprintf(buf + offset, buflen - offset, ",make_reg"); + if (access & LANDLOCK_ACCESS_FS_REMOVE_FILE) + offset += + snprintf(buf + offset, buflen - offset, ",remove_file"); + if (access & LANDLOCK_ACCESS_FS_REFER) + offset += snprintf(buf + offset, buflen - offset, ",refer"); + + if (buf[0] == ',') { + offset--; + memmove(buf, buf + 1, offset); + buf[offset] = '\0'; + } + + return offset; +} + +static int apply_a_layer(struct __test_metadata *const _metadata, + const struct a_layer *l) +{ + struct landlock_ruleset_attr rs_attr = { + .handled_access_fs = l->handled_access_fs, + .quiet_access_fs = l->quiet_access_fs, + }; + int rs_fd; + int i; + const struct a_rule *r; + char handled_access_s[33], quiet_access_s[33], rule_access_s[33]; + + if (!l->handled_access_fs) + return 0; + + rs_fd = landlock_create_ruleset(&rs_attr, sizeof(rs_attr), 0); + ASSERT_LE(0, rs_fd); + + for (i = 0; i < ARRAY_SIZE(l->rules); i++) { + r = &l->rules[i]; + if (!r->path) + continue; + + add_path_beneath(_metadata, rs_fd, r->access, r->path, + r->quiet ? LANDLOCK_ADD_RULE_QUIET : 0); + } + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + ASSERT_EQ(0, landlock_restrict_self(rs_fd, l->restrict_flags)) + { + TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); + } + ASSERT_EQ(0, close(rs_fd)); + + if (debug_quiet_tests) { + sprint_access_bits(handled_access_s, sizeof(handled_access_s), + l->handled_access_fs); + sprint_access_bits(quiet_access_s, sizeof(quiet_access_s), + l->quiet_access_fs); + TH_LOG("applied layer: handled=%s quiet=%s restrict_flags=0x%llx", + handled_access_s, quiet_access_s, + (unsigned long long)l->restrict_flags); + for (i = 0; i < ARRAY_SIZE(l->rules); i++) { + r = &l->rules[i]; + if (!r->path) + continue; + + sprint_access_bits(rule_access_s, sizeof(rule_access_s), + r->access); + TH_LOG(" rule[%d]: path=%s access=%s quiet=%d", i, + r->path, rule_access_s, r->quiet); + } + } + return 0; +} + +void audit_quiet_layout1_test_body(struct __test_metadata *const _metadata, + FIXTURE_DATA(audit_quiet_layout1) * self, + const struct a_target *targets) +{ + struct audit_records records = {}; + int i; + const struct a_target *target; + int fd = -1; + int open_mode; + int ret; + bool expect_audit; + const char *blocker; + + for (i = 0; i < AUDIT_QUIET_MAX_TARGETS; i++) { + target = &targets[i]; + if (!target->target) + continue; + + open_mode = target->open_mode & (O_RDONLY | O_WRONLY | O_RDWR); + + EXPECT_TRUE(open_mode == O_RDONLY || open_mode == O_WRONLY || + open_mode == O_RDWR); + + if (target->expect_open_success) { + EXPECT_FALSE(target->audit_read_blocked); + EXPECT_FALSE(target->audit_write_blocked); + } + if (target->expect_truncate_success) + EXPECT_TRUE(target->expect_open_success && + !target->audit_truncate); + + if (debug_quiet_tests) + TH_LOG("Try open \"%s\" with %s%s", target->target, + open_mode != O_WRONLY ? "r" : "", + open_mode != O_RDONLY ? "w" : ""); + + fd = openat(AT_FDCWD, target->target, open_mode | O_CLOEXEC); + if (target->expect_open_success) { + ASSERT_LE(0, fd) + { + TH_LOG("Failed to open \"%s\": %s", + target->target, strerror(errno)); + }; + } else { + ASSERT_EQ(-1, fd); + ASSERT_EQ(EACCES, errno); + } + + expect_audit = true; + + if (target->audit_read_blocked && target->audit_write_blocked) + blocker = "fs\\.write_file,fs\\.read_file"; + else if (target->audit_read_blocked) + blocker = "fs\\.read_file"; + else if (target->audit_write_blocked) + blocker = "fs\\.write_file"; + else + expect_audit = false; + + if (expect_audit) + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + blocker, target->target)); + + /* Check that we see no (other) logs. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); + + if (target->expect_open_success && fd >= 0) { + if (debug_quiet_tests) + TH_LOG("Try ftruncate \"%s\"", target->target); + + ret = ftruncate(fd, 0); + if (target->expect_truncate_success) { + ASSERT_EQ(0, ret); + } else { + ASSERT_EQ(-1, ret); + if (open_mode != O_RDONLY) + ASSERT_EQ(EACCES, errno); + } + + if (target->audit_truncate) + ASSERT_EQ(0, matches_log_fs(_metadata, + self->audit_fd, + "fs\\.truncate", + target->target)); + + if (target->expect_ioctl_allowed || + target->expect_ioctl_denied) { + if (debug_quiet_tests) + TH_LOG("Try ioctl FIONREAD on \"%s\"", + target->target); + + ret = ioctl_error(_metadata, fd, FIONREAD); + if (target->expect_ioctl_allowed) { + ASSERT_NE(EACCES, ret); + } else { + ASSERT_EQ(EACCES, ret); + } + } + + if (target->audit_ioctl) + ASSERT_EQ(0, matches_log_fs_extra( + _metadata, self->audit_fd, + "fs\\.ioctl_dev", + target->target, + " ioctlcmd=0x541b\\+")); + + /* Check that we see no other logs. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, + &records)); + ASSERT_EQ(0, records.access); + ASSERT_EQ(0, close(fd)); + } + } +} + +TEST_F(audit_quiet_layout1, base) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); + + audit_quiet_layout1_test_body(_metadata, self, variant->targets); +} + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_simple) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_R, + .rules = { + { .path = dir_s1d1, .access = 0, .quiet = true }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + /* Not covered by quiet */ + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + /* Access not quieted */ + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .audit_write_blocked = true, + }, + /* + * Quiet flag only takes effect if all blocked access bits are + * quieted, otherwise audit log emitted as normal (with all + * blockers) + */ + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_read) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_W, + .rules = { + { .path = dir_s1d1, .access = FS_R, .quiet = true }, + /* Quiet flags inherit down and are not overridden */ + { .path = file1_s1d1, .access = FS_R, .quiet = false }, + { .path = file1_s2d3, .access = 0, .quiet = true }, + }, + }, + }, + .targets = { + /* Read ok */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + .expect_open_success = true, + }, + /* Write quieted */ + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + }, + /* Read allowed, write quieted so no audit */ + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + /* Not covered by quiet */ + { + .target = file1_s2d2, + .open_mode = O_WRONLY, + .audit_write_blocked = true, + }, + { + .target = file1_s2d2, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + /* Single file quiet */ + { + .target = file1_s2d3, + .open_mode = O_WRONLY, + }, + /* Wrong file */ + { + .target = file2_s2d3, + .open_mode = O_WRONLY, + .audit_write_blocked = true, + }, + /* Access not quieted */ + { + .target = file1_s2d3, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + /* Some access not quieted */ + { + .target = file1_s2d3, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_write) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_R, + .rules = { + { .path = dir_s1d1, .access = FS_W, .quiet = true }, + }, + }, + }, + .targets = { + /* Read quieted */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + /* Truncate not quieted */ + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + /* Not covered by quiet */ + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + /* Write allowed, read quieted so no audit */ + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_write_quiet_trunc) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_TRUNC, + .rules = { + { .path = dir_s1d1, .access = FS_W, .quiet = true }, + { .path = dir_s2d1, .access = FS_W, .quiet = false }, + }, + }, + }, + .targets = { + /* Read not allowed and not quieted */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + /* Truncate quieted */ + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + }, + /* Not covered by quiet (truncate) */ + { + .target = file1_s2d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + /* Not covered by quiet (read/write) */ + { + .target = file1_s3d1, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_rw_quiet_trunc) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_TRUNC, + .rules = { + { .path = dir_s1d1, .access = FS_R | FS_W, .quiet = true }, + { .path = dir_s2d1, .access = FS_R | FS_W, .quiet = false }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .expect_open_success = true, + }, + { + .target = file1_s2d1, + .open_mode = O_RDWR, + .expect_open_success = true, + .audit_truncate = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_all) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { .path = dir_s1d1, .access = 0, .quiet = true }, + { .path = file1_s2d1, .access = FS_R | FS_W, .quiet = true }, + { .path = file1_s2d3, .access = 0, .quiet = true }, + { .path = dir_s3d1, .access = FS_W, .quiet = false }, + { .path = "/dev/zero", .access = FS_R, .quiet = false }, + { .path = "/dev/null", .access = FS_R, .quiet = true }, + }, + }, + }, + .targets = { + /* No logs */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + }, + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + /* Truncate quieted - no log */ + { + .target = file1_s2d1, + .open_mode = O_RDWR, + .expect_open_success = true, + }, + /* Truncate not covered by quiet */ + { + .target = file1_s3d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + /* Not covered by quiet */ + { + .target = file1_s3d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + /* Single file quiet */ + { + .target = file1_s2d3, + .open_mode = O_RDWR, + }, + /* Wrong file */ + { + .target = file2_s2d3, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + /* Ioctl quieted */ + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + /* Ioctl not quieted */ + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + .audit_ioctl = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_across_mountpoint) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_R, + .rules = { + { .path = dir_s3d1, .access = 0, .quiet = true }, + }, + }, + }, + .targets = { + { + .target = file1_s3d3, + .open_mode = O_RDONLY, + }, + /* Not covered by quiet */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .audit_read_blocked = true, + .audit_write_blocked = true, + }, + /* Access not quieted */ + { + .target = file1_s3d3, + .open_mode = O_WRONLY, + .audit_write_blocked = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_all_quiet) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = true + }, + { + .path = "/dev/null", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = true + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .expect_open_success = true, + .expect_truncate_success = true, + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_allowed = true, + }, + }, +}; + +/* + * With LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, it doesn't matter what the + * quiet flags below the layer say. + */ +FIXTURE_VARIANT_ADD(audit_quiet_layout1, subdomains_off) { + .layers = { + { + .handled_access_fs = FS_R, + .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, + .rules = { + { .path = "/", .access = FS_R, .quiet = false }, + } + }, + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R, + .rules = { + { .path = dir_s1d1, .access = 0, .quiet = true }, + { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, + { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, + { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, + { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + { + .target = file1_s2d1, + .open_mode = O_RDWR, + }, + { + .target = file1_s2d2, + .open_mode = O_RDWR, + .expect_open_success = true, + /* No audit_truncate */ + }, + { + .target = file1_s2d3, + .open_mode = O_RDWR, + .expect_open_success = true, + /* No audit_truncate */ + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + /* No audit_ioctl */ + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + /* No audit_ioctl */ + }, + }, +}; + +/* + * With LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, it doesn't matter what the + * quiet flags on the layer say. + */ +FIXTURE_VARIANT_ADD(audit_quiet_layout1, same_exec_off) { + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R, + .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, + .rules = { + { .path = dir_s1d1, .access = 0, .quiet = true }, + { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, + { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, + { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, + { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + { + .target = file1_s2d1, + .open_mode = O_RDWR, + }, + { + .target = file1_s2d2, + .open_mode = O_RDWR, + .expect_open_success = true, + /* No audit_truncate */ + }, + { + .target = file1_s2d3, + .open_mode = O_RDWR, + .expect_open_success = true, + /* No audit_truncate */ + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + /* No audit_ioctl */ + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + /* No audit_ioctl */ + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_1) { + /* Here, rules that deny access are always quiet. */ + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_W, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = "/dev/null", + .access = FS_R, + .quiet = true, + }, + { + .path = "/dev/zero", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + }, + }, + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = FS_W, + .quiet = true, + }, + { + .path = "/dev/null", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + { + .path = "/dev/zero", + .access = FS_R, + .quiet = true, + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + }, + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + }, + { + .target = file1_s2d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_2) { + /* Here, rules that deny access are never quiet. */ + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_W, + .quiet = false + }, + { + .path = dir_s2d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = true + }, + { + .path = "/dev/null", + .access = FS_R, + .quiet = false + }, + { + .path = "/dev/zero", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = true + }, + }, + }, + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = true + }, + { + .path = dir_s2d1, + .access = FS_W, + .quiet = false + }, + { + .path = "/dev/null", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = true + }, + { + .path = "/dev/zero", + .access = FS_R, + .quiet = false + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + { + .target = file1_s2d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + .audit_ioctl = true, + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + .audit_ioctl = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_3) { + /* This time only the second layer quiets things. */ + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_W, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = "/dev/null", + .access = FS_R, + .quiet = false, + }, + { + .path = "/dev/zero", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + }, + }, + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = FS_W, + .quiet = true, + }, + { + .path = "/dev/null", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + { + .path = "/dev/zero", + .access = FS_R, + .quiet = true, + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + }, + { + .target = file1_s2d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + .audit_ioctl = true, + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_quiet_access) { + /* Here, rules that deny access are always quiet. */ + .layers = { + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_W, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = "/dev/null", + .access = FS_R, + .quiet = true, + }, + { + .path = "/dev/zero", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + }, + }, + { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_IOCTL, + .rules = { + { + .path = dir_s1d1, + .access = FS_R | FS_W | FS_TRUNC, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = FS_W, + .quiet = true, + }, + { + .path = "/dev/null", + .access = FS_R | FS_W | FS_IOCTL, + .quiet = false, + }, + { + .path = "/dev/zero", + .access = FS_R, + .quiet = true, + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + }, + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + { + .target = file1_s2d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .audit_truncate = true, + }, + { + .target = "/dev/null", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + { + .target = "/dev/zero", + .open_mode = O_RDONLY, + .expect_open_success = true, + .expect_ioctl_denied = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_1) { + /* Quiet from layer 1 */ + .layers = { + { + .handled_access_fs = FS_R, + .quiet_access_fs = FS_R, + .rules = { + { + .path = file1_s1d1, + .access = FS_R, + .quiet = true, + }, + { + .path = file2_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file1_s1d2, + .access = 0, + .quiet = true, + }, + { + .path = file2_s1d2, + .access = FS_R, + .quiet = true, + }, + }, + }, + { + .handled_access_fs = FS_W, + .quiet_access_fs = FS_W, + .rules = { + { + .path = file1_s1d1, + .access = FS_W, + .quiet = false, + }, + /* Nothing for file2_s1d1 */ + { + .path = file1_s1d2, + .access = FS_W, + .quiet = false, + }, + /* Nothing for file2_s1d2 */ + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .expect_open_success = true, + .expect_truncate_success = true, + }, + /* Missing both, youngest layer denies write, not quiet */ + { + .target = file2_s1d1, + .open_mode = O_RDWR, + .audit_write_blocked = true, + }, + /* Missing read, denied and quieted by layer 1 */ + { + .target = file1_s1d2, + .open_mode = O_RDWR, + }, + /* Missing write, denied and not quieted by layer 2 */ + { + .target = file2_s1d2, + .open_mode = O_RDWR, + .audit_write_blocked = true, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_2) { + /* Quiet from layer 2 */ + .layers = { + { + .handled_access_fs = FS_R, + .quiet_access_fs = FS_R, + .rules = { + { + .path = file1_s1d1, + .access = FS_R, + .quiet = false, + }, + /* Nothing for file2_s1d1 and file1_s1d2 */ + { + .path = file2_s1d2, + .access = FS_R, + .quiet = false, + }, + }, + }, + { + .handled_access_fs = FS_W, + .quiet_access_fs = FS_W, + .rules = { + { + .path = file1_s1d1, + .access = FS_W, + .quiet = true, + }, + { + .path = file2_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file1_s1d2, + .access = FS_W, + .quiet = true, + }, + { + .path = file2_s1d2, + .access = 0, + .quiet = true, + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .expect_open_success = true, + .expect_truncate_success = true, + }, + /* Missing both, youngest layer denies write, quiet */ + { + .target = file2_s1d1, + .open_mode = O_RDWR, + }, + /* Missing read, denied and not quieted by layer 1 */ + { + .target = file1_s1d2, + .open_mode = O_RDWR, + .audit_read_blocked = true, + }, + /* Missing write, denied and quieted by layer 2 */ + { + .target = file2_s1d2, + .open_mode = O_RDWR, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_3) { + /* Quiet from both layers */ + .layers = { + { + .handled_access_fs = FS_R, + .quiet_access_fs = FS_R, + .rules = { + { + .path = file1_s1d1, + .access = FS_R, + .quiet = true, + }, + { + .path = file2_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file1_s1d2, + .access = 0, + .quiet = true, + }, + { + .path = file2_s1d2, + .access = FS_R, + .quiet = true, + }, + }, + }, + { + .handled_access_fs = FS_W, + .quiet_access_fs = FS_W, + .rules = { + { + .path = file1_s1d1, + .access = FS_W, + .quiet = true, + }, + { + .path = file2_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file1_s1d2, + .access = FS_W, + .quiet = true, + }, + { + .path = file2_s1d2, + .access = 0, + .quiet = true, + }, + }, + }, + }, + .targets = { + { + .target = file1_s1d1, + .open_mode = O_RDWR, + .expect_open_success = true, + .expect_truncate_success = true, + }, + { + .target = file2_s1d1, + .open_mode = O_RDWR, + }, + { + .target = file1_s1d2, + .open_mode = O_RDWR, + }, + { + .target = file2_s1d2, + .open_mode = O_RDWR, + }, + }, +}; + +FIXTURE_VARIANT_ADD(audit_quiet_layout1, without_quiet_then_with_quiet) { + .layers = { + { + .handled_access_fs = FS_R | FS_W, + .quiet_access_fs = FS_R, + .rules = { + { .path = dir_s1d1, .access = FS_W, .quiet = false }, + { .path = dir_s1d1, .access = 0, .quiet = true }, + }, + }, + }, + .targets = { + /* Read denied and quieted */ + { + .target = file1_s1d1, + .open_mode = O_RDONLY, + }, + /* Write ok */ + { + .target = file1_s1d1, + .open_mode = O_WRONLY, + .expect_open_success = true, + .expect_truncate_success = true, + }, + /* Write ok, read denied and quieted */ + { + .target = file1_s1d1, + .open_mode = O_RDWR, + }, + /* Not covered by quiet */ + { + .target = file1_s2d1, + .open_mode = O_RDONLY, + .audit_read_blocked = true, + }, + }, +}; + +/* + * The following TEST_F extend the above test cases to test more layers, with + * the inserted layers having varying configurations. + */ + +/* Extra allow all layers, quiet or not, does not change any behaviour. */ +TEST_F(audit_quiet_layout1, allow_all_layer) +{ + struct a_layer allow_all_layer = { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = 0, + .rules = { + { + .path = "/", + .access = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet = false, + }, + }, + }; + int i; + + ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); + + audit_quiet_layout1_test_body(_metadata, self, variant->targets); + + ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); + + audit_quiet_layout1_test_body(_metadata, self, variant->targets); + + /* + * SELF_LOG flags or quiet bits from inner allowing layers should not + * affect behaviour. + */ + allow_all_layer.quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL; + allow_all_layer.rules[0].quiet = true; + /* + * Note: this only works because we're not checking counts of domain + * alloc/dealloc logs + */ + allow_all_layer.restrict_flags = + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF; + ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); + + audit_quiet_layout1_test_body(_metadata, self, variant->targets); +} + +/* + * Add useless outer layers until we reach the layer limit. Should not change + * anything. + */ +TEST_F(audit_quiet_layout1, many_outer_layers) +{ + struct a_layer useless_layer = { + .handled_access_fs = FS_R | FS_W | FS_TRUNC, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC, + .rules = { + { .path = "/", .access = FS_R | FS_W | FS_TRUNC, .quiet = true }, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { + if (variant->layers[i].handled_access_fs == 0) + break; + } + + for (; i < LANDLOCK_MAX_NUM_LAYERS; i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &useless_layer)); + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); + + audit_quiet_layout1_test_body(_metadata, self, variant->targets); +} + +/* An inner layer that denies and quiets everything should result in no logs. */ +TEST_F(audit_quiet_layout1, deny_all_quiet_layer) +{ + struct a_layer deny_all_layer = { + .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, + .rules = { + { .path = "/", .access = 0, .quiet = true }, + }, + }; + int i; + FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; + + /* Any open should fail with no logs. */ + for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { + const struct a_target *target = &variant->targets[i]; + + variant_2.targets[i] = (struct a_target){ + .target = target->target, + .open_mode = target->open_mode, + /* We denied everything, open should always fail. */ + .expect_open_success = false, + }; + } + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); + ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); + + audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); +} + +/* + * An inner layer that denies everything without quiet should produce logs for + * all access. + */ +TEST_F(audit_quiet_layout1, deny_all_layer) +{ + struct a_layer deny_all_layer = { + .handled_access_fs = FS_R | FS_W, + .quiet_access_fs = FS_R | FS_W, + }; + int i; + FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; + bool test_has_subdomains_off = false; + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { + if (variant->layers[i].restrict_flags & + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF) { + test_has_subdomains_off = true; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { + const struct a_target *target = &variant->targets[i]; + + variant_2.targets[i] = (struct a_target){ + .target = target->target, + .open_mode = target->open_mode, + + /* We denied everything, open should always fail. */ + .expect_open_success = false, + /* Audit should always happen as long as open request contains read. */ + .audit_read_blocked = !test_has_subdomains_off && + target->open_mode != O_WRONLY, + /* Audit should always happen as long as open request contains write. */ + .audit_write_blocked = !test_has_subdomains_off && + target->open_mode != O_RDONLY, + }; + } + + for (i = 0; i < ARRAY_SIZE(variant->layers); i++) + ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); + ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); + + audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); +} + +/* Uses layout1_bind hierarchy */ +FIXTURE(audit_quiet_rename) +{ + struct audit_filter audit_filter; + int audit_fd; +}; + +FIXTURE_SETUP(audit_quiet_rename) +{ + prepare_layout(_metadata); + create_layout1(_metadata); + + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); + EXPECT_LE(0, self->audit_fd); + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + if (getenv("DEBUG_QUIET_TESTS")) + debug_quiet_tests = true; +} + +FIXTURE_TEARDOWN_PARENT(audit_quiet_rename) +{ + remove_layout1(_metadata); + cleanup_layout(_metadata); + + /* umount(dir_s2d2)) is handled by namespace lifetime. */ + + remove_path(file1_s4d1); + remove_path(file2_s4d1); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_cleanup(-1, NULL)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); +} + +static void simple_quiet_rename(struct __test_metadata *const _metadata, + FIXTURE_DATA(audit_quiet_rename) *const self, + __u64 handled_access, __u64 quiet_access, + bool source_allow, bool dest_allow, + bool source_quiet, bool dest_quiet, + const char *source_blockers, + const char *dest_blockers) +{ + /* We will move file1_s1d1 to file1_s2d1 */ + struct a_layer layer = { + .handled_access_fs = handled_access, + .quiet_access_fs = quiet_access, + .rules = { + { + .path = dir_s1d1, + .access = source_allow ? handled_access : 0, + .quiet = source_quiet, + }, + { + .path = dir_s2d1, + .access = dest_allow ? handled_access : 0, + .quiet = dest_quiet, + }, + }, + }; + struct audit_records records = {}; + int ret, err; + + /* Skip landlock_add_rule for useless rules. */ + if (!source_allow && !source_quiet) + layer.rules[0].path = NULL; + if (!dest_allow && !dest_quiet) + layer.rules[1].path = NULL; + + EXPECT_EQ(0, unlink(file1_s2d1)); + EXPECT_EQ(0, apply_a_layer(_metadata, &layer)); + + if (debug_quiet_tests) + TH_LOG("Try renameat \"%s\" to \"%s\"", file1_s1d1, file1_s2d1); + ret = renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1); + err = errno; + if (ret != 0 && debug_quiet_tests) { + TH_LOG("renameat error: %s", err == EXDEV ? "EXDEV" : + err == EACCES ? "EACCES" : + strerror(err)); + } + if (source_allow && dest_allow) { + ASSERT_EQ(0, ret); + } else { + ASSERT_EQ(-1, ret); + if (handled_access & (LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE)) { + ASSERT_EQ(EACCES, err); + } else { + ASSERT_EQ(EXDEV, err); + } + + if (source_blockers) + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + source_blockers, dir_s1d1)); + if (dest_blockers) + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + dest_blockers, dir_s2d1)); + } + /* + * No other logs. records.domain not checked per reasoning in + * audit_quiet_layout1_test_body. + */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, rename_ok) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, true, true, false, + false, NULL, NULL); +} + +TEST_F(audit_quiet_rename, no_quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, false, false, + false, false, "fs\\.remove_file,fs\\.refer", + "fs\\.make_reg,fs\\.refer"); +} + +TEST_F(audit_quiet_rename, quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, false, false, true, + true, NULL, NULL); +} + +TEST_F(audit_quiet_rename, source_no_quiet_dest_quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, false, false, + false, true, "fs\\.remove_file,fs\\.refer", NULL); +} + +TEST_F(audit_quiet_rename, source_quiet_dest_no_quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, false, false, true, + false, NULL, "fs\\.make_reg,fs\\.refer"); +} + +TEST_F(audit_quiet_rename, only_quiet_refer) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, LANDLOCK_ACCESS_FS_REFER, + false, false, true, true, + "fs\\.remove_file,fs\\.refer", + "fs\\.make_reg,fs\\.refer"); +} + +TEST_F(audit_quiet_rename, source_allow_dest_quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, true, false, false, + true, NULL, NULL); +} + +TEST_F(audit_quiet_rename, source_quiet_dest_allow) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + + simple_quiet_rename(_metadata, self, access, access, false, true, true, + false, NULL, NULL); +} + +TEST_F(audit_quiet_rename, handle_all_deny_quiet_refer) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, + .rules = { + { + .path = dir_s1d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EXDEV, errno); + + /* No logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, handle_all_deny_not_quiet_refer) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = 0, + .rules = { + { + .path = dir_s1d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = false, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EXDEV, errno); + + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", + dir_s1d1)); + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", + dir_s2d1)); + + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, handle_all_deny_refer_quiet_source_not_quiet_dest) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, + .rules = { + { + .path = dir_s1d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE, + .quiet = false, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EXDEV, errno); + + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", + dir_s2d1)); + + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_same_dir) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); + ASSERT_EQ(EACCES, errno); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = file1_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file1_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.refer", dir_s1d1)); + /* We didn't unlink destination file */ + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.make_reg,fs\\.refer", + dir_s2d1)); + + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored_same_dir) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = file1_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = file2_s1d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); + ASSERT_EQ(EACCES, errno); + + ASSERT_EQ(0, + matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.make_reg", dir_s1d1)); + + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, two_layers_different_quiet1) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer1 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = access, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct a_layer layer2 = { + .handled_access_fs = access, + .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = access, + .quiet = false, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + /* + * The youngest denial will be layer 2. Refer is quieted but we are + * also missing remove_file on source. + */ + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.refer", dir_s1d1)); + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, two_layers_different_quiet2) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer1 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = access, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct a_layer layer2 = { + .handled_access_fs = LANDLOCK_ACCESS_FS_REFER, + .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = LANDLOCK_ACCESS_FS_REFER, + .quiet = false, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + /* + * The youngest denial will be layer 2, but refer is quieted (and that + * layer does not handle any other accesses). + */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, two_layers_different_quiet3) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer1 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = access, + .quiet = false, + }, + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct a_layer layer2 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = access, + .quiet = false, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + /* + * The youngest denial will be layer 2, in which everything is quieted. + */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, + first_layer_quiet_deny_all_second_layer_not_quiet_deny_all) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer1 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct a_layer layer2 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = {}, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.refer", dir_s1d1)); + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.make_reg,fs\\.refer", dir_s2d1)); + /* No other logs. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, + first_layer_quiet_deny_all_second_layer_dest_not_quiet) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer1 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct a_layer layer2 = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file1_s2d1)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); + ASSERT_EQ(EACCES, errno); + + /* Source is quieted but destination is not. */ + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.make_reg,fs\\.refer", dir_s2d1)); + /* No other logs. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, rename_xchg) +{ + struct a_layer layer = { + .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER, + .quiet_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG, + .rules = { { + .path = dir_s1d1, + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER, + .quiet = true, + }, + { + .path = dir_s2d1, + .access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER, + .quiet = false, + } }, + }; + struct audit_records records = {}; + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1, + RENAME_EXCHANGE)); + ASSERT_EQ(EACCES, errno); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_on_parent_mount) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file2_s1d3)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, + bind_file2_s1d3)); + ASSERT_EQ(EACCES, errno); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_behind_mountpoint_ignored) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s1d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + + EXPECT_EQ(0, unlink(file2_s1d3)); + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, + bind_file2_s1d3)); + ASSERT_EQ(EACCES, errno); + ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, + "fs\\.remove_file,fs\\.make_reg", + bind_dir_s1d3)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_on_parent_mount_disconnected) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s2d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + int bind_s1d3_fd; + + EXPECT_EQ(0, unlink(file2_s1d3)); + + bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); + ASSERT_GE(bind_s1d3_fd, 0); + + /* Make s1d3 disconnected. */ + create_directory(_metadata, dir_s4d1); + ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, + renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); + ASSERT_EQ(EACCES, errno); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + +TEST_F(audit_quiet_rename, quiet_behind_mountpoint_disconnected) +{ + __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_REFER; + struct a_layer layer = { + .handled_access_fs = access, + .quiet_access_fs = access, + .rules = { + { + .path = dir_s4d1, + .access = 0, + .quiet = true, + }, + }, + }; + struct audit_records records = {}; + int bind_s1d3_fd; + + EXPECT_EQ(0, unlink(file2_s1d3)); + + bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); + ASSERT_GE(bind_s1d3_fd, 0); + + /* Make s1d3 disconnected. */ + create_directory(_metadata, dir_s4d1); + ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); + + ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); + + ASSERT_EQ(-1, + renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); + ASSERT_EQ(EACCES, errno); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + ASSERT_EQ(0, records.access); +} + TEST_HARNESS_MAIN From 624c96268bb3402bb21d889069bb4aff5d4d7aec Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:53 +0100 Subject: [PATCH 3630/5214] selftests/landlock: Add tests for quiet flag with net rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests that: - Quiet flag works on network rules - Quiet flag applied to unrelated ports has no effect - Denied access not in quiet_access_net is still logged This is not as thorough as the fs tests, but given the shared logic it should be sufficient. There is also no "optional" access for network rules. Signed-off-by: Tingmao Wang Assisted-by: GitHub-Copilot:claude-opus-4.7 copilot-review Link: https://patch.msgid.link/364fbd08081318d64bc23049d3a7721f0a3a3624.1781228815.git.m@maowtm.org Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/net_test.c | 138 ++++++++++++++++++-- 1 file changed, 128 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index fb244aaef86f..2ed1f76b7a8b 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -2742,12 +2742,22 @@ TEST_F(port_specific, bind_connect_1023) EXPECT_EQ(0, close(bind_fd)); } +/** + * matches_auditlog - Check audit log for a network access denial + * + * @audit_fd: Audit file descriptor. + * @blockers: A regex-escaped blocker string, e.g., "net\.bind_tcp". + * @dir_addr: Either "saddr" or "daddr", ignored if addr is NULL. + * @addr: A regex-escaped IP address string, or NULL. + * @dir_port: Either "src" or "dest", ignored if addr is NULL. + * @port: A port number, ignored if addr is NULL. + */ static int matches_auditlog(const int audit_fd, const char *const blockers, const char *const dir_addr, const char *const addr, - const char *const dir_port) + const char *const dir_port, const __u16 port) { static const char log_with_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX - " blockers=%s %s=%s %s=1024$"; + " blockers=%s %s=%s %s=%u$"; static const char log_without_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX " blockers=%s"; /* @@ -2755,8 +2765,9 @@ static int matches_auditlog(const int audit_fd, const char *const blockers, * Max strlen(dir_addr): 5 * Max strlen(addr): 12 * Max strlen(dir_port): 4 + * Max strlen(%u port): 5 */ - char log_match[sizeof(log_with_addrport_tmpl) + 37]; + char log_match[sizeof(log_with_addrport_tmpl) + 42]; int log_match_len; if (addr == NULL) @@ -2765,7 +2776,7 @@ static int matches_auditlog(const int audit_fd, const char *const blockers, else log_match_len = snprintf(log_match, sizeof(log_match), log_with_addrport_tmpl, blockers, - dir_addr, addr, dir_port); + dir_addr, addr, dir_port, port); if (log_match_len > sizeof(log_match)) return -E2BIG; @@ -2777,6 +2788,8 @@ FIXTURE(audit) { struct service_fixture srv0; struct service_fixture srv1; + /* srv2 has a rule with no access but quiet bit set. */ + struct service_fixture srv2; struct service_fixture unspec_srv0; struct audit_filter audit_filter; int audit_fd; @@ -2836,6 +2849,7 @@ FIXTURE_SETUP(audit) ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0)); ASSERT_EQ(0, set_service(&self->srv1, variant->prot, 1)); + ASSERT_EQ(0, set_service(&self->srv2, variant->prot, 2)); ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0)); setup_loopback(_metadata); @@ -2866,6 +2880,11 @@ TEST_F(audit, bind) LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); const struct landlock_ruleset_attr ruleset_attr = { .handled_access_net = access_rights, + .quiet_access_net = access_rights, + }; + const struct landlock_net_port_attr quiet_rule = { + .allowed_access = 0, + .port = self->srv2.port, }; struct audit_records records; int ruleset_fd, sock_fd; @@ -2873,6 +2892,8 @@ TEST_F(audit, bind) ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &quiet_rule, LANDLOCK_ADD_RULE_QUIET)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -2880,13 +2901,24 @@ TEST_F(audit, bind) ASSERT_LE(0, sock_fd); EXPECT_EQ(-EACCES, bind_variant(sock_fd, &self->srv0)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "saddr", - variant->addr, "src")); + variant->addr, "src", self->srv0.port)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); EXPECT_EQ(1, records.domain); EXPECT_EQ(0, close(sock_fd)); + + /* Bind to srv2 (with quiet rule): no new audit logs. */ + sock_fd = socket_variant(&self->srv2); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(-EACCES, bind_variant(sock_fd, &self->srv2)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); + + EXPECT_EQ(0, close(sock_fd)); } TEST_F(audit, connect) @@ -2903,11 +2935,16 @@ TEST_F(audit, connect) const __u64 access_rights = bind_right | conn_right; const struct landlock_ruleset_attr ruleset_attr = { .handled_access_net = access_rights, + .quiet_access_net = access_rights, }; const struct landlock_net_port_attr rule_connect_p1 = { .allowed_access = conn_right, .port = self->srv1.port, }; + const struct landlock_net_port_attr quiet_rule = { + .allowed_access = 0, + .port = self->srv2.port, + }; struct audit_records records; int ruleset_fd, sock_fd; @@ -2916,6 +2953,8 @@ TEST_F(audit, connect) ASSERT_LE(0, ruleset_fd); ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &rule_connect_p1, 0)); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &quiet_rule, LANDLOCK_ADD_RULE_QUIET)); enforce_ruleset(_metadata, ruleset_fd); EXPECT_EQ(0, close(ruleset_fd)); @@ -2923,7 +2962,7 @@ TEST_F(audit, connect) ASSERT_LE(0, sock_fd); EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv0)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "daddr", - variant->addr, "dest")); + variant->addr, "dest", self->srv0.port)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); @@ -2934,13 +2973,91 @@ TEST_F(audit, connect) EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv1)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", - NULL, NULL, NULL)); + NULL, NULL, NULL, 0)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); EXPECT_EQ(0, records.domain); } EXPECT_EQ(0, close(sock_fd)); + + /* Connect to srv2 (with quiet rule): no new audit logs. */ + sock_fd = socket_variant(&self->srv2); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); + + EXPECT_EQ(0, close(sock_fd)); +} + +/* Quieting bind access has no effect on connect. */ +TEST_F(audit, connect_quiet_bind) +{ + const char *audit_evt = (variant->prot.type == SOCK_STREAM ? + "net\\.connect_tcp" : + "net\\.connect_send_udp"); + const int bind_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_BIND_TCP : + LANDLOCK_ACCESS_NET_BIND_UDP); + const int conn_right = (variant->prot.type == SOCK_STREAM ? + LANDLOCK_ACCESS_NET_CONNECT_TCP : + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); + const int access_rights = bind_right | conn_right; + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = access_rights, + .quiet_access_net = bind_right, + }; + const struct landlock_ruleset_attr ruleset_attr_2 = { + .handled_access_net = access_rights, + .quiet_access_net = conn_right, + }; + const struct landlock_net_port_attr quiet_rule = { + .allowed_access = 0, + .port = self->srv2.port, + }; + struct audit_records records; + int ruleset_fd, sock_fd; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &quiet_rule, LANDLOCK_ADD_RULE_QUIET)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv2); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2)); + EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "daddr", + variant->addr, "dest", self->srv2.port)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + + EXPECT_EQ(0, close(sock_fd)); + + /* New layer that also denies connect but has the correct quiet bit. */ + ruleset_fd = landlock_create_ruleset(&ruleset_attr_2, + sizeof(ruleset_attr_2), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &quiet_rule, LANDLOCK_ADD_RULE_QUIET)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv2); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2)); + + /* Quieted - no logs expected. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + + EXPECT_EQ(0, close(sock_fd)); } static int matches_log_connect_bound(int audit_fd, const char *const blockers, @@ -3045,7 +3162,8 @@ TEST_F(audit, sendmsg) ASSERT_LE(0, sock_fd); EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv0, "A", 1, 0)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp", - "daddr", variant->addr, "dest")); + "daddr", variant->addr, "dest", + self->srv0.port)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); @@ -3054,7 +3172,7 @@ TEST_F(audit, sendmsg) /* Check that autobind generates a denied bind event. */ EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv1, "A", 1, 0)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", NULL, - NULL, NULL)); + NULL, NULL, 0)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); EXPECT_EQ(0, records.domain); @@ -3062,7 +3180,7 @@ TEST_F(audit, sendmsg) EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->unspec_srv0, "B", 1, 0)); EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp", - "daddr", NULL, "dest")); + "daddr", NULL, "dest", 0)); EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); EXPECT_EQ(0, records.domain); From 149367545331dbadb4940540f2a43e726aab046d Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:54 +0100 Subject: [PATCH 3631/5214] selftests/landlock: Add tests for quiet flag with scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance scoped_audit.connect_to_child and audit_flags.signal to test interaction with various quiet flag settings. Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/032849ca97bd45b2e14f96192b61537ed9405a0d.1781228815.git.m@maowtm.org [mic: Fix comment formatting] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/audit_test.c | 25 ++++-- .../landlock/scoped_abstract_unix_test.c | 77 ++++++++++++++++--- 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 161ab5feb2b9..72b5612375dd 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -607,30 +607,42 @@ FIXTURE(audit_flags) FIXTURE_VARIANT(audit_flags) { const int restrict_flags; + const __u64 quiet_scoped; }; /* clang-format off */ FIXTURE_VARIANT_ADD(audit_flags, default) { /* clang-format on */ .restrict_flags = 0, + .quiet_scoped = 0, }; /* clang-format off */ FIXTURE_VARIANT_ADD(audit_flags, same_exec_off) { /* clang-format on */ .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, + .quiet_scoped = 0, }; /* clang-format off */ FIXTURE_VARIANT_ADD(audit_flags, subdomains_off) { /* clang-format on */ .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, + .quiet_scoped = 0, }; /* clang-format off */ FIXTURE_VARIANT_ADD(audit_flags, cross_exec_on) { /* clang-format on */ .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON, + .quiet_scoped = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit_flags, signal_quieted) { + /* clang-format on */ + .restrict_flags = 0, + .quiet_scoped = LANDLOCK_SCOPE_SIGNAL, }; FIXTURE_SETUP(audit_flags) @@ -674,12 +686,16 @@ TEST_F(audit_flags, signal) pid_t child; struct audit_records records; __u64 deallocated_dom = 2; + bool expect_audit = !(variant->restrict_flags & + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) && + !(variant->quiet_scoped & LANDLOCK_SCOPE_SIGNAL); child = fork(); ASSERT_LE(0, child); if (child == 0) { const struct landlock_ruleset_attr ruleset_attr = { .scoped = LANDLOCK_SCOPE_SIGNAL, + .quiet_scoped = variant->quiet_scoped, }; int ruleset_fd; @@ -696,8 +712,7 @@ TEST_F(audit_flags, signal) EXPECT_EQ(-1, kill(getppid(), 0)); EXPECT_EQ(EPERM, errno); - if (variant->restrict_flags & - LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + if (!expect_audit) { EXPECT_EQ(-EAGAIN, matches_log_signal( _metadata, self->audit_fd, getppid(), self->domain_id)); @@ -724,8 +739,7 @@ TEST_F(audit_flags, signal) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); - if (variant->restrict_flags & - LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + if (!expect_audit) { EXPECT_EQ(0, records.access); } else { EXPECT_EQ(1, records.access); @@ -749,8 +763,7 @@ TEST_F(audit_flags, signal) WEXITSTATUS(status) != EXIT_SUCCESS) _metadata->exit_code = KSFT_FAIL; - if (variant->restrict_flags & - LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + if (!expect_audit) { /* * No deallocation record: denials=0 never matches a real * record. diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c index 72f97648d4a7..40fc82fbf01d 100644 --- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c @@ -293,6 +293,45 @@ FIXTURE_TEARDOWN_PARENT(scoped_audit) EXPECT_EQ(0, audit_cleanup(-1, NULL)); } +FIXTURE_VARIANT(scoped_audit) +{ + const __u64 scoped; + const __u64 quiet_scoped; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoped_audit, no_quiet) +{ + /* clang-format on */ + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + .quiet_scoped = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoped_audit, quiet_abstract_socket) +{ + /* clang-format on */ + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + .quiet_scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoped_audit, quiet_abstract_socket_2) +{ + /* clang-format on */ + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL, + .quiet_scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | + LANDLOCK_SCOPE_SIGNAL, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoped_audit, quiet_unrelated) +{ + /* clang-format on */ + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL, + .quiet_scoped = LANDLOCK_SCOPE_SIGNAL, +}; + /* python -c 'print(b"\0selftests-landlock-abstract-unix-".hex().upper())' */ #define ABSTRACT_SOCKET_PATH_PREFIX \ "0073656C6674657374732D6C616E646C6F636B2D61627374726163742D756E69782D" @@ -308,6 +347,13 @@ TEST_F(scoped_audit, connect_to_child) char buf; int dgram_client; struct audit_records records; + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = variant->scoped, + .quiet_scoped = variant->quiet_scoped, + }; + bool should_audit = + !(variant->quiet_scoped & LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); @@ -345,7 +391,14 @@ TEST_F(scoped_audit, connect_to_child) EXPECT_EQ(0, close(pipe_child[1])); EXPECT_EQ(0, close(pipe_parent[0])); - create_scoped_domain(_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd) + { + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); + } + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); /* Signals that the parent is in a domain, if any. */ ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); @@ -360,14 +413,20 @@ TEST_F(scoped_audit, connect_to_child) EXPECT_EQ(-1, err_dgram); EXPECT_EQ(EPERM, errno); - EXPECT_EQ( - 0, - audit_match_record( - self->audit_fd, AUDIT_LANDLOCK_ACCESS, - REGEX_LANDLOCK_PREFIX - " blockers=scope\\.abstract_unix_socket path=" ABSTRACT_SOCKET_PATH_PREFIX - "[0-9A-F]\\+$", - NULL)); + if (should_audit) { + EXPECT_EQ( + 0, + audit_match_record( + self->audit_fd, AUDIT_LANDLOCK_ACCESS, + REGEX_LANDLOCK_PREFIX + " blockers=scope\\.abstract_unix_socket path=" ABSTRACT_SOCKET_PATH_PREFIX + "[0-9A-F]\\+$", + NULL)); + } + + /* No other logs */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); EXPECT_EQ(0, close(dgram_client)); From 1c236e7fe740a009ad8dd40a5ee0602ec402fffe Mon Sep 17 00:00:00 2001 From: Tingmao Wang Date: Fri, 12 Jun 2026 02:48:55 +0100 Subject: [PATCH 3632/5214] selftests/landlock: Add tests for invalid use of quiet flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure that these calls return EINVAL. Test coverage for security/landlock is 91.6% of 2347 lines according to LLVM 22. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Tingmao Wang Link: https://patch.msgid.link/9401d5c6468675863d944d6c26640d97db1a1f31.1781228815.git.m@maowtm.org [mic: Add test coverage] Signed-off-by: Mickaël Salaün --- tools/testing/selftests/landlock/base_test.c | 116 +++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 84e91fcaa1b2..cbd3c1669951 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -526,4 +526,120 @@ TEST(cred_transfer) EXPECT_EQ(EACCES, errno); } +TEST(useless_quiet_rule_fs) +{ + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, + .quiet_access_fs = 0, + }; + struct landlock_path_beneath_attr path_beneath_attr = { + .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, + }; + int ruleset_fd, root_fd; + + drop_caps(_metadata); + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + + root_fd = open("/", O_PATH | O_CLOEXEC); + ASSERT_LE(0, root_fd); + path_beneath_attr.parent_fd = root_fd; + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath_attr, + LANDLOCK_ADD_RULE_QUIET)); + ASSERT_EQ(EINVAL, errno); + + /* Check that the rule had not been added. */ + ASSERT_EQ(0, close(root_fd)); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + ASSERT_EQ(EACCES, errno); +} + +TEST(useless_quiet_rule_net) +{ + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP, + .quiet_access_net = 0, + }; + struct landlock_net_port_attr net_port_attr = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + .port = 1024, + }; + int ruleset_fd; + + drop_caps(_metadata); + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + + ASSERT_EQ(-1, + landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &net_port_attr, LANDLOCK_ADD_RULE_QUIET)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(0, close(ruleset_fd)); +} + +TEST(invalid_quiet_bits_1) +{ + const struct landlock_ruleset_attr ruleset_attr_fs = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, + .quiet_access_fs = LANDLOCK_ACCESS_FS_WRITE_FILE, + }; + const struct landlock_ruleset_attr ruleset_attr_net = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP, + .quiet_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + const struct landlock_ruleset_attr ruleset_attr_scoped = { + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + .quiet_scoped = LANDLOCK_SCOPE_SIGNAL, + }; + + /* Quiet bit set but not part of the handled mask. */ + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_fs, + sizeof(ruleset_attr_fs), 0)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_net, + sizeof(ruleset_attr_net), 0)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_scoped, + sizeof(ruleset_attr_scoped), 0)); + ASSERT_EQ(EINVAL, errno); +} + +TEST(invalid_quiet_bits_2) +{ + const struct landlock_ruleset_attr ruleset_attr_fs = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, + .quiet_access_fs = 1ULL << 63, + }; + const struct landlock_ruleset_attr ruleset_attr_net = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP, + .quiet_access_net = 1ULL << 63, + }; + const struct landlock_ruleset_attr ruleset_attr_scoped = { + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + .quiet_scoped = 1ULL << 63, + }; + + /* Quiet bit outside of the valid access range. */ + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_fs, + sizeof(ruleset_attr_fs), 0)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_net, + sizeof(ruleset_attr_net), 0)); + ASSERT_EQ(EINVAL, errno); + + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr_scoped, + sizeof(ruleset_attr_scoped), 0)); + ASSERT_EQ(EINVAL, errno); +} + TEST_HARNESS_MAIN From 527756cb9ebb277dca12fff00af9fbb3b9ec8cc8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:05 +0300 Subject: [PATCH 3633/5214] i3c: master: Make hot-join workqueue freezable to block hot-join during suspend The I3C master workqueue (master->wq) is used to defer work that needs thread context and the bus maintenance lock, most notably Hot Join processing (which calls i3c_master_do_daa() to assign dynamic addresses to newly joined devices). Currently the workqueue keeps running across system suspend, which can race with the suspend path: - do_daa() may execute after the controller has been suspended, issuing bus transactions on a powered-down or otherwise unusable controller. - New I3C devices can be enumerated and added to the bus mid-suspend, registering driver model objects at a point where the I3C subsystem and its consumers are not prepared to handle them. Mark the workqueue WQ_FREEZABLE so its workers are frozen for the duration of system suspend/hibernate and resumed afterwards. This naturally defers any pending or newly queued Hot Join work until the system (and the controller) is fully resumed, closing both races without adding explicit suspend/resume synchronization in the master drivers. Update the kerneldoc for struct i3c_master_controller::wq to reflect that the workqueue is freezable. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 2 +- include/linux/i3c/master.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 6b8df8089a35..5b64e40dae67 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -3079,7 +3079,7 @@ int i3c_master_register(struct i3c_master_controller *master, if (ret) goto err_put_dev; - master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent)); + master->wq = alloc_workqueue("%s", WQ_PERCPU | WQ_FREEZABLE, 0, dev_name(parent)); if (!master->wq) { ret = -ENOMEM; goto err_put_dev; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 592b646f6134..e6112e5f6608 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -515,7 +515,7 @@ struct i3c_master_controller_ops { * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @bus: I3C bus exposed by this master - * @wq: workqueue which can be used by master + * @wq: freezable workqueue which can be used by master * drivers if they need to postpone operations that need to take place * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only From 5b130aadc36b6a935b90937dcd67b8ed4ba57831 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:06 +0300 Subject: [PATCH 3634/5214] i3c: master: Serialize i3c_set_hotjoin() with the maintenance lock i3c_set_hotjoin() dispatches the controller's enable_hotjoin() or disable_hotjoin() op and updates master->hotjoin under i3c_bus_normaluse_lock(). That lock is a read-side acquisition of bus->lock (down_read()), so it does not exclude concurrent callers. The hotjoin sysfs attribute can be opened multiple times, and writes through different opens are not serialized. Two concurrent writers to "hotjoin" can therefore race in i3c_set_hotjoin(), with the controller op and the master->hotjoin store from one call interleaving with the other. The hardware enable/disable state and the value reported by hotjoin_show() can end up out of sync. Take i3c_bus_maintenance_lock() instead. Toggling Hot Join enable changes bus state and is conceptually a maintenance operation, so the write-side acquisition of bus->lock is the appropriate lock and serializes concurrent callers against each other and against other maintenance operations. Fixes: 317bacf960a48 ("i3c: master: add enable(disable) hot join in sys entry") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5b64e40dae67..a3a790a14286 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -649,7 +649,7 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) return ret; } - i3c_bus_normaluse_lock(&master->bus); + i3c_bus_maintenance_lock(&master->bus); if (enable) ret = master->ops->enable_hotjoin(master); @@ -659,7 +659,7 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) if (!ret) master->hotjoin = enable; - i3c_bus_normaluse_unlock(&master->bus); + i3c_bus_maintenance_unlock(&master->bus); if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) i3c_master_rpm_put(master); From 828c6130235db8144f4810b329b61390dc82719b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:07 +0300 Subject: [PATCH 3635/5214] i3c: master: Consolidate Hot-Join DAA work in the core Three master drivers (dw-i3c-master, i3c-master-cdns, svc-i3c-master) each carry an essentially identical Hot-Join handler: a struct work_struct embedded in their private state, a work function that just calls i3c_master_do_daa() on the embedded i3c_master_controller, plus matching INIT_WORK()/cancel_work_sync() boilerplate in probe/remove (and shutdown for dw-i3c). The IBI/ISR paths then queue that work onto master->wq, which already lives in the core. Move this pattern into the I3C core: - Add struct work_struct hj_work to struct i3c_master_controller and initialise it in i3c_master_register() with a core-provided handler i3c_master_hj_work_fn() that performs i3c_master_do_daa(). - Cancel the work in i3c_master_unregister() so all controllers get correct teardown ordering against the workqueue for free. - Export i3c_master_queue_hotjoin() as the single entry point drivers call from their Hot-Join IBI handler. Convert the three existing users to the new API: drop their private hj_work fields, work functions, INIT_WORK() and cancel_work_sync() calls, and replace the queue_work(master->wq, &drv->hj_work) call sites with i3c_master_queue_hotjoin(&drv->base). The dw-i3c shutdown path still needs to flush pending Hot-Join work before tearing down the hardware, so it is updated to cancel master->base.hj_work directly. No functional change intended: the work is still queued on the same master->wq, runs the same i3c_master_do_daa(), and is cancelled at controller teardown. Future Hot-Join improvements now only need to be made in one place. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 21 +++++++++++++++++++++ drivers/i3c/master/dw-i3c-master.c | 15 ++------------- drivers/i3c/master/dw-i3c-master.h | 2 -- drivers/i3c/master/i3c-master-cdns.c | 14 +------------- drivers/i3c/master/svc-i3c-master.c | 14 +------------- include/linux/i3c/master.h | 4 ++++ 6 files changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index a3a790a14286..deb5cf851940 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -633,6 +633,13 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, } static DEVICE_ATTR_RO(i2c_scl_frequency); +static void i3c_master_hj_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); + + i3c_master_do_daa(master); +} + static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) { int ret; @@ -711,6 +718,18 @@ int i3c_master_disable_hotjoin(struct i3c_master_controller *master) } EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin); +/** + * i3c_master_queue_hotjoin - Queue DAA processing after a Hot-Join event + * @master: I3C master object + * + * Queue the hot-join worker on the master's workqueue. + */ +void i3c_master_queue_hotjoin(struct i3c_master_controller *master) +{ + queue_work(master->wq, &master->hj_work); +} +EXPORT_SYMBOL_GPL(i3c_master_queue_hotjoin); + static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *i3cbus = dev_to_i3cbus(dev); @@ -3084,6 +3103,7 @@ int i3c_master_register(struct i3c_master_controller *master, ret = -ENOMEM; goto err_put_dev; } + INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3146,6 +3166,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); + cancel_work_sync(&master->hj_work); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index a7593d6efac5..a60158331a8f 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1449,7 +1449,7 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) if (IBI_TYPE_SIRQ(reg)) { dw_i3c_master_handle_ibi_sir(master, reg); } else if (IBI_TYPE_HJ(reg)) { - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); } else { len = IBI_QUEUE_STATUS_DATA_LEN(reg); dev_info(&master->base.dev, @@ -1558,14 +1558,6 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = { .set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop, }; -static void dw_i3c_hj_work(struct work_struct *work) -{ - struct dw_i3c_master *master = - container_of(work, typeof(*master), hj_work); - - i3c_master_do_daa(&master->base); -} - int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { @@ -1655,8 +1647,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(&pdev->dev); - INIT_WORK(&master->hj_work, dw_i3c_hj_work); - device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1678,7 +1668,6 @@ EXPORT_SYMBOL_GPL(dw_i3c_common_probe); void dw_i3c_common_remove(struct dw_i3c_master *master) { - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); /* Balance pm_runtime_get_noresume() from probe() */ @@ -1823,7 +1812,7 @@ static void dw_i3c_shutdown(struct platform_device *pdev) return; } - cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->base.hj_work); /* Disable interrupts */ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index 306e25a08937..28e9348f2153 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -69,8 +69,6 @@ struct dw_i3c_master { /* platform-specific data */ const struct dw_i3c_platform_ops *platform_ops; - - struct work_struct hj_work; }; struct dw_i3c_platform_ops { diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 5cfec6761494..6d221596ea35 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -398,7 +398,6 @@ struct cdns_i3c_data { }; struct cdns_i3c_master { - struct work_struct hj_work; struct i3c_master_controller base; u32 free_rr_slots; unsigned int maxdevs; @@ -1357,7 +1356,7 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master) case IBIR_TYPE_HJ: WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case IBIR_TYPE_MR: @@ -1528,15 +1527,6 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = { .recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot, }; -static void cdns_i3c_master_hj(struct work_struct *work) -{ - struct cdns_i3c_master *master = container_of(work, - struct cdns_i3c_master, - hj_work); - - i3c_master_do_daa(&master->base); -} - static struct cdns_i3c_data cdns_i3c_devdata = { .thd_delay_ns = 10, }; @@ -1584,7 +1574,6 @@ static int cdns_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); - INIT_WORK(&master->hj_work, cdns_i3c_master_hj); writel(0xffffffff, master->regs + MST_IDR); writel(0xffffffff, master->regs + SLV_IDR); ret = devm_request_irq(&pdev->dev, irq, cdns_i3c_master_interrupt, 0, @@ -1627,7 +1616,6 @@ static void cdns_i3c_master_remove(struct platform_device *pdev) { struct cdns_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 57d63d299e1e..93805df8a940 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -208,7 +208,6 @@ struct svc_i3c_drvdata { * @free_slots: Bit array of available slots * @addrs: Array containing the dynamic addresses of each attached device * @descs: Array of descriptors, one per attached device - * @hj_work: Hot-join work * @irq: Main interrupt * @num_clks: I3C clock number * @fclk: Fast clock (bus) @@ -235,7 +234,6 @@ struct svc_i3c_master { u32 free_slots; u8 addrs[SVC_I3C_MAX_DEVS]; struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS]; - struct work_struct hj_work; int irq; int num_clks; struct clk *fclk; @@ -366,14 +364,6 @@ to_svc_i3c_master(struct i3c_master_controller *master) return container_of(master, struct svc_i3c_master, base); } -static void svc_i3c_master_hj_work(struct work_struct *work) -{ - struct svc_i3c_master *master; - - master = container_of(work, struct svc_i3c_master, hj_work); - i3c_master_do_daa(&master->base); -} - static struct i3c_dev_desc * svc_i3c_master_dev_from_addr(struct svc_i3c_master *master, unsigned int ibiaddr) @@ -651,7 +641,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: svc_i3c_master_emit_stop(master); if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_emit_stop(master); @@ -2039,7 +2029,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "can't enable I3C clocks\n"); - INIT_WORK(&master->hj_work, svc_i3c_master_hj_work); mutex_init(&master->lock); ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler, @@ -2098,7 +2087,6 @@ static void svc_i3c_master_remove(struct platform_device *pdev) { struct svc_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); pm_runtime_dont_use_autosuspend(&pdev->dev); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index e6112e5f6608..eb5c51608bd7 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -520,6 +520,8 @@ struct i3c_master_controller_ops { * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only * be done from a sleep-able context + * @hj_work: work item used to run DAA after a Hot-Join event is detected. + * Queued to @wq by i3c_master_queue_hotjoin() * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -543,6 +545,7 @@ struct i3c_master_controller { } boardinfo; struct i3c_bus bus; struct workqueue_struct *wq; + struct work_struct hj_work; unsigned int dev_nack_retry_count; }; @@ -623,6 +626,7 @@ int i3c_master_register(struct i3c_master_controller *master, void i3c_master_unregister(struct i3c_master_controller *master); int i3c_master_enable_hotjoin(struct i3c_master_controller *master); int i3c_master_disable_hotjoin(struct i3c_master_controller *master); +void i3c_master_queue_hotjoin(struct i3c_master_controller *master); /** * i3c_dev_get_master_data() - get master private data attached to an I3C From 8323e783dc3904839e64cb08cfcc7571ef9212c4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:08 +0300 Subject: [PATCH 3636/5214] i3c: master: Ensure Hot-Join operations are stopped on shutdown System shutdown invokes each device's bus shutdown callback to quiesce hardware, but the I3C bus type does not currently implement one. As a result, on shutdown the controller's Hot-Join work and any in-flight i3c_master_do_daa() can keep running (or be newly triggered) while the rest of the system is being torn down. A similar window exists at i3c_master_unregister() time: cancel_work_sync() on hj_work prevents queued work from completing, but does not stop a fresh Hot-Join IBI from re-queueing the worker, nor a concurrent sysfs writer from toggling Hot-Join via i3c_set_hotjoin(). Introduce a single "shutting down" gate in the I3C core, set under the bus maintenance lock so it is observed by any in-progress DAA path before pending work is cancelled. Install an i3c_bus_type shutdown callback that engages this gate for master devices during system shutdown, and use the same gate in i3c_master_unregister() so both paths get identical guarantees. Once the gate is engaged, the Hot-Join worker, i3c_master_do_daa_ext() and i3c_set_hotjoin() all bail out cleanly, so Hot-Join IBIs that race with shutdown become no-ops, direct DAA callers see -ENODEV, and sysfs writers can no longer re-enable Hot-Join through ops->enable_hotjoin() while the controller is going away. No functional change for the steady-state runtime path; the new checks only take effect once the controller has been marked as shutting down. Note, this patch depends on patch "i3c: master: Consolidate Hot-Join DAA work in the core". Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 52 +++++++++++++++++++++++++++----------- include/linux/i3c/master.h | 2 ++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index deb5cf851940..c89a1921fcf3 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -368,14 +368,6 @@ static void i3c_device_remove(struct device *dev) driver->remove(i3cdev); } -const struct bus_type i3c_bus_type = { - .name = "i3c", - .match = i3c_device_match, - .probe = i3c_device_probe, - .remove = i3c_device_remove, -}; -EXPORT_SYMBOL_GPL(i3c_bus_type); - static enum i3c_addr_slot_status i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { @@ -637,7 +629,8 @@ static void i3c_master_hj_work_fn(struct work_struct *work) { struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); - i3c_master_do_daa(master); + if (!master->shutting_down) + i3c_master_do_daa(master); } static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) @@ -658,7 +651,9 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) i3c_bus_maintenance_lock(&master->bus); - if (enable) + if (master->shutting_down) + ret = -ENODEV; + else if (enable) ret = master->ops->enable_hotjoin(master); else ret = master->ops->disable_hotjoin(master); @@ -837,6 +832,30 @@ static const struct device_type i3c_masterdev_type = { .groups = i3c_masterdev_groups, }; +static void i3c_master_shutdown(struct i3c_master_controller *master) +{ + i3c_bus_maintenance_lock(&master->bus); + master->shutting_down = true; + i3c_bus_maintenance_unlock(&master->bus); + + cancel_work_sync(&master->hj_work); +} + +static void i3c_device_shutdown(struct device *dev) +{ + if (dev->type == &i3c_masterdev_type) + i3c_master_shutdown(dev_to_i3cmaster(dev)); +} + +const struct bus_type i3c_bus_type = { + .name = "i3c", + .match = i3c_device_match, + .probe = i3c_device_probe, + .remove = i3c_device_remove, + .shutdown = i3c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i3c_bus_type); + static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate) { @@ -1846,10 +1865,13 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - - ret = master->ops->do_daa(master); + if (master->shutting_down) { + ret = -ENODEV; + } else { + if (rstdaa) + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + ret = master->ops->do_daa(master); + } i3c_bus_maintenance_unlock(&master->bus); @@ -3166,7 +3188,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); - cancel_work_sync(&master->hj_work); + i3c_master_shutdown(master); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index eb5c51608bd7..77e63082b06e 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -511,6 +511,7 @@ struct i3c_master_controller_ops { * @hotjoin: true if the master support hotjoin * @rpm_allowed: true if Runtime PM allowed * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended + * @shutting_down: set to true when master begins shutdown or unregister * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus @@ -539,6 +540,7 @@ struct i3c_master_controller { unsigned int hotjoin: 1; unsigned int rpm_allowed: 1; unsigned int rpm_ibi_allowed: 1; + bool shutting_down; struct { struct list_head i3c; struct list_head i2c; From 0cbefeafd2cb33defc706a7b8f0af247729817ff Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:09 +0300 Subject: [PATCH 3637/5214] i3c: dw: Drop redundant Hot-Join cancel_work_sync() in shutdown The I3C core now installs an i3c_bus_type shutdown callback that flushes master->hj_work (via i3c_master_shutdown()) before any driver's platform shutdown hook runs. The explicit cancel_work_sync() in dw_i3c_shutdown() is therefore redundant: by the time it executes, the Hot-Join worker has already been cancelled, and the shutting_down gate makes a new worker a no-op. Remove the now-unneeded call. No functional change. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-6-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/dw-i3c-master.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index a60158331a8f..971b429b76bc 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1812,8 +1812,6 @@ static void dw_i3c_shutdown(struct platform_device *pdev) return; } - cancel_work_sync(&master->base.hj_work); - /* Disable interrupts */ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN); From 3f79dac3ea1c30516fcc791770af034387c7f917 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:10 +0300 Subject: [PATCH 3638/5214] i3c: master: Defer new-device registration out of DAA caller context Master drivers may invoke i3c_master_do_daa_ext() during resume to re-run Dynamic Address Assignment. As well as assigning addresses to any newly arrived devices, this restores the dynamic address of devices that lost it across system suspend, so it has to run as part of the controller's resume path. A side effect of i3c_master_do_daa_ext() today is that it also registers any newly discovered I3C devices with the driver model inline, via i3c_master_register_new_i3c_devs(). Doing that from the resume path is problematic: a hot-join-capable device may join the bus during this same DAA, and registering it immediately would push driver model work (probing, sysfs, etc.) into the controller's resume context, where the rest of the system is not yet fully resumed and the controller driver is still partway through its own resume sequence. Decouple discovery from registration: add a reg_work work item to struct i3c_master_controller and have i3c_master_do_daa_ext() queue it on master->wq (the freezable workqueue) instead of calling i3c_master_register_new_i3c_devs() directly. The worker performs the registration only when the controller is not shutting_down, and is cancelled alongside hj_work in i3c_master_shutdown(). Because wq is freezable, any newly observed devices end up being registered after the system has finished resuming. i3c_master_register() also routes its initial post-bus-init registration through reg_work, using flush_work() to keep probe-time behavior synchronous. This keeps a single registration code path and ensures the worker is the only writer of desc->dev. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 27 ++++++++++++++++++++------- include/linux/i3c/master.h | 6 ++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index c89a1921fcf3..ed749f9ee413 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -839,6 +839,7 @@ static void i3c_master_shutdown(struct i3c_master_controller *master) i3c_bus_maintenance_unlock(&master->bus); cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->reg_work); } static void i3c_device_shutdown(struct device *dev) @@ -1838,6 +1839,16 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) } } +static void i3c_master_reg_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); + + i3c_bus_normaluse_lock(&master->bus); + if (!master->shutting_down) + i3c_master_register_new_i3c_devs(master); + i3c_bus_normaluse_unlock(&master->bus); +} + /** * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) * @master: controller @@ -1878,9 +1889,7 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) if (ret) goto out; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); out: i3c_master_rpm_put(master); @@ -3126,6 +3135,7 @@ int i3c_master_register(struct i3c_master_controller *master, goto err_put_dev; } INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); + INIT_WORK(&master->reg_work, i3c_master_reg_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3151,12 +3161,15 @@ int i3c_master_register(struct i3c_master_controller *master, /* * We're done initializing the bus and the controller, we can now - * register I3C devices discovered during the initial DAA. + * register I3C devices discovered during the initial DAA. Device + * registration is done via reg_work because that keeps a single + * registration code path and ensures the worker is the only writer + * of desc->dev. Flush the work to preserve synchronous probe-time + * behavior. */ master->init_done = true; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); + flush_work(&master->reg_work); if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 77e63082b06e..8cdd7be505d3 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -523,6 +523,11 @@ struct i3c_master_controller_ops { * be done from a sleep-able context * @hj_work: work item used to run DAA after a Hot-Join event is detected. * Queued to @wq by i3c_master_queue_hotjoin() + * @reg_work: work item used to register newly discovered I3C devices with + * the driver model. Queued to @wq by i3c_master_do_daa_ext() so + * that device registration is deferred out of the DAA caller's + * context (notably the resume path), and is skipped if the + * controller is shutting down * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -548,6 +553,7 @@ struct i3c_master_controller { struct i3c_bus bus; struct workqueue_struct *wq; struct work_struct hj_work; + struct work_struct reg_work; unsigned int dev_nack_retry_count; }; From 4bfba7b360da70ed209969848bbe6e750254a759 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:11 +0300 Subject: [PATCH 3639/5214] i3c: master: Export i3c_master_enec_disec_locked() The existing i3c_master_enec_locked() wrapper always treats a NACKed ENEC CCC as a failure (M2 error). However, broadcasting ENEC to enable Hot-Join is legitimately useful even when no I3C devices are currently present on the bus, in which case the broadcast will be NACKed and should not be reported as an error. The underlying helper i3c_master_enec_disec_locked() already accepts a suppress_m2 flag that lets callers ignore such NACKs. Expose it so that a subsequent patch enabling Hot-Join events can issue ENEC with M2 suppression. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 27 ++++++++++++++++++++++++--- include/linux/i3c/master.h | 2 ++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index ed749f9ee413..9f8d99636f69 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1121,9 +1121,29 @@ int i3c_master_entdaa_locked(struct i3c_master_controller *master) } EXPORT_SYMBOL_GPL(i3c_master_entdaa_locked); -static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, - u8 addr, bool enable, u8 evts, - bool suppress_m2) +/** + * i3c_master_enec_disec_locked() - send an ENEC or DISEC CCC command + * @master: master used to send frames on the bus + * @addr: a valid I3C slave address or %I3C_BROADCAST_ADDR + * @enable: true to send ENEC, false to send DISEC + * @evts: events to enable or disable + * @suppress_m2: if true, treat an M2 (NACK) error from the CCC as success + * + * Send an ENEC or DISEC CCC command to enable or disable some or all events + * coming from a specific slave, or all devices if @addr is + * %I3C_BROADCAST_ADDR. + * + * When @suppress_m2 is true, a NACK of the broadcast (which can happen when + * no devices are present on the bus) is not reported as an error. This is + * useful for callers that want to configure event reporting unconditionally, + * regardless of whether any devices are currently on the bus. + * + * This function must be called with the bus lock held in write mode. + * + * Return: 0 in case of success, or a negative error code otherwise. + */ +int i3c_master_enec_disec_locked(struct i3c_master_controller *master, u8 addr, + bool enable, u8 evts, bool suppress_m2) { struct i3c_ccc_events *events; struct i3c_ccc_cmd_dest dest; @@ -1148,6 +1168,7 @@ static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, return ret; } +EXPORT_SYMBOL_GPL(i3c_master_enec_disec_locked); /** * i3c_master_disec_locked() - send a DISEC CCC command diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 8cdd7be505d3..e2c831fb5354 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -607,6 +607,8 @@ int i3c_master_disec_locked(struct i3c_master_controller *master, u8 addr, u8 evts); int i3c_master_enec_locked(struct i3c_master_controller *master, u8 addr, u8 evts); +int i3c_master_enec_disec_locked(struct i3c_master_controller *master, u8 addr, + bool enable, u8 evts, bool suppress_m2); int i3c_master_entdaa_locked(struct i3c_master_controller *master); int i3c_master_defslvs_locked(struct i3c_master_controller *master); From 32fcf0039814d886f51fc37522c94da91759a4cc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:12 +0300 Subject: [PATCH 3640/5214] i3c: mipi-i3c-hci: Add Hot-Join support Wire the MIPI I3C HCI driver into the I3C core Hot-Join framework to allow targets to dynamically join the bus after initial DAA. HCI hardware ACKs or NACKs Hot-Join requests based on HC_CONTROL.HOT_JOIN_CTRL. This was previously left in the NACK-and-DISEC state, effectively preventing Hot-Join. Implement the ->enable_hotjoin() and ->disable_hotjoin() master operations so the core and user space can control this policy at runtime. Also issue broadcast ENEC HJ when enabling Hot-Join. This is required because the controller may have previously DISEC'ed the Hot-Join event, causing targets that were NACKed once to never retry. Acknowledged Hot-Join requests are delivered as IBIs on the reserved address 0x02. Update both the DMA and PIO IBI paths to recognise this address and forward the event to i3c_master_queue_hotjoin(). To make Hot-Join usable by default, enable it once after the initial DAA. This is gated by rpm_ibi_allowed, since otherwise keeping Hot-Join enabled prevents runtime suspend. A new hj_init_done flag ensures this one-time enablement is not repeated on subsequent DAAs. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-9-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 52 ++++++++++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/dma.c | 5 +++ drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + drivers/i3c/master/mipi-i3c-hci/pio.c | 5 +++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index c6edbbedfdd7..53797841b63f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -392,11 +392,52 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, return ret; } -static int i3c_hci_daa(struct i3c_master_controller *m) +static int i3c_hci_enable_hotjoin(struct i3c_master_controller *m) +{ + struct i3c_hci *hci = to_i3c_hci(m); + int ret; + + reg_clear(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL); + + /* + * Broadcast Hot_join enable, so that an I3C device that has previously + * had its Hot-Join request NACK'ed knows to try again. + */ + ret = i3c_master_enec_disec_locked(m, I3C_BROADCAST_ADDR, true, I3C_CCC_EVENT_HJ, true); + if (ret) { + reg_set(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL); + dev_err(&hci->master.dev, "Hot-Join ENEC CCC failed\n"); + } + + return ret; +} + +static int i3c_hci_disable_hotjoin(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); - return hci->cmd->perform_daa(hci); + reg_set(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL); + return 0; +} + +static int i3c_hci_daa(struct i3c_master_controller *m) +{ + struct i3c_hci *hci = to_i3c_hci(m); + int ret; + + ret = hci->cmd->perform_daa(hci); + + if (!hci->hj_init_done) { + hci->hj_init_done = true; + /* + * Enable Hot-Join by default after initial DAA if it does not + * prevent runtime suspend. + */ + if (m->rpm_ibi_allowed && !ret) + m->hotjoin = !i3c_hci_enable_hotjoin(m); + } + + return ret; } static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev, @@ -652,6 +693,8 @@ static const struct i3c_master_controller_ops i3c_hci_ops = { .enable_ibi = i3c_hci_enable_ibi, .disable_ibi = i3c_hci_disable_ibi, .recycle_ibi_slot = i3c_hci_recycle_ibi_slot, + .enable_hotjoin = i3c_hci_enable_hotjoin, + .disable_hotjoin = i3c_hci_disable_hotjoin, }; static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) @@ -833,8 +876,9 @@ static int i3c_hci_do_reset_and_restore(struct i3c_hci *hci) scoped_guard(spinlock_irqsave, &hci->lock) hci->irq_inactive = false; - /* Enable bus with Hot-Join disabled */ - reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL); + /* Enable bus, restoring hot-join state */ + reg_set(HC_CONTROL, + HC_CONTROL_BUS_ENABLE | (hci->master.hotjoin ? 0 : HC_CONTROL_HOT_JOIN_CTRL)); return 0; } diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 5c6ae2055618..87622d6f817e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -960,6 +960,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) } /* determine who this is for */ + if (ibi_addr == I3C_HOT_JOIN_ADDR) { + i3c_master_queue_hotjoin(&hci->master); + goto done; + } + dev = i3c_hci_addr_to_dev(hci, ibi_addr); if (!dev) { dev_err(&hci->master.dev, diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 30297823ca85..41d31a53abd3 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -57,6 +57,7 @@ struct i3c_hci { bool irq_inactive; bool enqueue_blocked; bool recovery_needed; + bool hj_init_done; wait_queue_head_t enqueue_wait_queue; u32 caps; unsigned int quirks; diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index 6b8cc5f2b4d2..b5ae1cfaa9e0 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -862,6 +862,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) ibi->seg_len = FIELD_GET(IBI_DATA_LENGTH, ibi_status); ibi->seg_cnt = ibi->seg_len; + if (ibi->addr == I3C_HOT_JOIN_ADDR) { + i3c_master_queue_hotjoin(&hci->master); + return true; + } + dev = i3c_hci_addr_to_dev(hci, ibi->addr); if (!dev) { dev_err(&hci->master.dev, From 650716f23eac488c6696babdc7805f6a6b7427ad Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:01 +0300 Subject: [PATCH 3641/5214] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() i3c_hci_addr_to_dev() walks bus->devs.i3c, which is protected by bus.lock (rwsem). However, it is invoked from the MIPI I3C HCI IRQ handler, which cannot take bus.lock. This allows concurrent device addition/removal in the I3C core to modify the list while it is being traversed, potentially leading to use-after-free or crashes. Remove the dependency on the bus device list and introduce a dedicated lookup table. Add an ibi_devs[] array indexed by DAT entry, maintained under hci->lock. Update the array when IBIs are enabled or disabled, so that it always reflects the set of devices allowed to generate IBIs. Also update when IBIs are freed, to cover the corner case when an IBI is freed without first being disabled (e.g. oldedev in i3c_master_add_i3c_dev_locked()). Move i3c_hci_addr_to_dev() into core.c, reimplement it using the new array, and add a lockdep assertion to enforce that hci->lock is held by callers. Demote a message in PIO and DMA IBI handling, from an error to a debug message, because there is a race window when the condition can arise normally. Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 37 ++++++++++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/dma.c | 7 +++-- drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 +-------- drivers/i3c/master/mipi-i3c-hci/pio.c | 7 +++-- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 53797841b63f..1e1f05aff092 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -22,6 +22,7 @@ #include "ext_caps.h" #include "cmd.h" #include "dat.h" +#include "ibi.h" /* * Host Controller Capabilities and Operation Registers @@ -124,6 +125,7 @@ static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) static int i3c_hci_bus_init(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); + struct device *dev = hci->master.dev.parent; struct i3c_device_info info; int ret; @@ -144,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) if (ret) return ret; + hci->ibi_devs = devm_kcalloc(dev, hci->DAT_entries, sizeof(*hci->ibi_devs), GFP_KERNEL); + if (!hci->ibi_devs) + return -ENOMEM; + ret = hci->io->init(hci); if (ret) return ret; @@ -639,14 +645,40 @@ static int i3c_hci_request_ibi(struct i3c_dev_desc *dev, return hci->io->request_ibi(hci, dev, req); } +static void __i3c_hci_disable_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev) +{ + struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); + + mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = NULL; +} + static void i3c_hci_free_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); + /* Must ensure the IBI has been disabled */ + __i3c_hci_disable_ibi(hci, dev); hci->io->free_ibi(hci, dev); } +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) +{ + int dat_idx; + + lockdep_assert_held(&hci->lock); + + for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { + struct i3c_dev_desc *dev = hci->ibi_devs[dat_idx]; + + if (dev && dev->info.dyn_addr == addr) + return dev; + } + return NULL; +} + static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); @@ -654,6 +686,8 @@ static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = dev; return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } @@ -661,9 +695,8 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + __i3c_hci_disable_ibi(hci, dev); return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 87622d6f817e..0672ed1132f8 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -967,8 +967,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) dev = i3c_hci_addr_to_dev(hci, ibi_addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi_addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi_addr); goto done; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 41d31a53abd3..b3d9803b1968 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -65,6 +65,7 @@ struct i3c_hci { unsigned int DAT_entry_size; void *DAT_data; struct dat_words *DAT; + struct i3c_dev_desc **ibi_devs; unsigned int DCT_entries; unsigned int DCT_entry_size; u8 version_major; diff --git a/drivers/i3c/master/mipi-i3c-hci/ibi.h b/drivers/i3c/master/mipi-i3c-hci/ibi.h index e1f98e264da0..073ca67b7d04 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ibi.h +++ b/drivers/i3c/master/mipi-i3c-hci/ibi.h @@ -26,17 +26,6 @@ #define IBI_DATA_LENGTH GENMASK(7, 0) /* handy helpers */ -static inline struct i3c_dev_desc * -i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) -{ - struct i3c_bus *bus = i3c_master_get_bus(&hci->master); - struct i3c_dev_desc *dev; - - i3c_bus_for_each_i3cdev(bus, dev) { - if (dev->info.dyn_addr == addr) - return dev; - } - return NULL; -} +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr); #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index b5ae1cfaa9e0..ff2657ee220b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -869,8 +869,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) dev = i3c_hci_addr_to_dev(hci, ibi->addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi->addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi->addr); return true; } From ad7fba5cbd6d7ff139a08e7c83edec4536314430 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:02 +0300 Subject: [PATCH 3642/5214] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disabling IBIs currently returns the result of the DISEC CCC, causing i3c_hci_disable_ibi() to fail if the transfer errors out. However, the controller has already been programmed to reject IBIs by setting DAT_0_SIR_REJECT, so the target’s IBIs are effectively disabled from the host side regardless of the outcome of the DISEC command. At this point, teardown of the IBI infrastructure can safely proceed even if DISEC fails. Note, from then on, the MIPI I3C HCI not only NACKs the target's IBI but automatically sends another DISEC command. Make i3c_hci_disable_ibi() resilient by ignoring the return value of i3c_master_disec_locked() and always returning success. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 1e1f05aff092..fffbc1775ef9 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -697,7 +697,13 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev) struct i3c_hci *hci = to_i3c_hci(m); __i3c_hci_disable_ibi(hci, dev); - return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); + /* + * The DAT entry is now set to NACK and DISEC this target's IBIs, so + * the IBI teardown can proceed even if DISEC below fails, so ignore + * errors. + */ + i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); + return 0; } static void i3c_hci_recycle_ibi_slot(struct i3c_dev_desc *dev, From b3ba8383da4d0cff15810e32ea785eceb0a80813 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:03 +0300 Subject: [PATCH 3643/5214] i3c: master: Prevent reuse of dynamic address on device add failure i3c_master_add_i3c_dev_locked() is called after a device has already been assigned a dynamic address. If the function fails, the address remains marked as free and may be reallocated to another device, leading to address conflicts on the bus. Ensure the address is not marked as free on failure, by updating the address slot state to prevent the address from being re-used. Emit an error message to inform of the failure. Opportunistically remove the !master check because it is impossible. Note, directly resetting the device's dynamic address is no longer an option, since Direct RSTDAA was deprecated from I3C starting from version 1.1 and v1.1 (or later) target devices are meant to NACK it. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9f8d99636f69..b45065ecbd48 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2345,12 +2345,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, bool enable_ibi = false; int ret; - if (!master) - return -EINVAL; - newdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(newdev)) - return PTR_ERR(newdev); + if (IS_ERR(newdev)) { + ret = PTR_ERR(newdev); + goto err_prevent_addr_reuse; + } ret = i3c_master_attach_i3c_dev(master, newdev); if (ret) @@ -2472,6 +2471,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, err_free_dev: i3c_master_free_i3c_dev(newdev); +err_prevent_addr_reuse: + /* + * Although the device has not been added, the address has been + * assigned. Prevent the address from being used again. + */ + if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE) + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV); + + dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); From c236563c8a84239d31a1e6ec4444887a7b5ed98f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:04 +0300 Subject: [PATCH 3644/5214] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA i3c_master_add_i3c_dev_locked() no longer leaves the address marked as free on failure, so aborting the DAA sequence on its error is unnecessary. Failure to register a discovered device does not invalidate the entire Dynamic Address Assignment (DAA) procedure. Align with the behavior of other I3C master drivers by ignoring errors from i3c_master_add_i3c_dev_locked() and continuing enumeration. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 4 +--- drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c index 75d452d7f6af..3b9345718d27 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -358,9 +358,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) * TODO: Extend the subsystem layer to allow for registering * new device and provide BCR/DCR/PID at the same time. */ - ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr); - if (ret) - break; + i3c_master_add_i3c_dev_locked(&hci->master, next_addr); } if (dat_idx >= 0) diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c index 39eec26a363c..8d93748e858d 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c @@ -296,9 +296,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci) * TODO: Extend the subsystem layer to allow for registering * new device and provide BCR/DCR/PID at the same time. */ - ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr); - if (ret) - break; + i3c_master_add_i3c_dev_locked(&hci->master, next_addr); } hci_free_xfer(xfer, 2); From 689d0bd8f4ada0834bd198d8af8e519683ae81d1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:05 +0300 Subject: [PATCH 3645/5214] i3c: master: Make i3c_master_add_i3c_dev_locked() return void The return value of i3c_master_add_i3c_dev_locked() is not used by any caller, and callers are not in a position to recover from failures in this path. Change the function to return void. Amend the kernel-doc accordingly, fix some grammar and remove a stale paragraph. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-6-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 17 ++++------------- include/linux/i3c/master.h | 3 +-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index b45065ecbd48..efd940a30732 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2324,19 +2324,12 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev) * @master: master used to send frames on the bus * @addr: I3C slave dynamic address assigned to the device * - * This function is instantiating an I3C device object and adding it to the - * I3C device list. All device information are automatically retrieved using - * standard CCC commands. - * - * The I3C device object is returned in case the master wants to attach - * private data to it using i3c_dev_set_master_data(). + * This function instantiates an I3C device object and adds it to the I3C device + * list. All device information is retrieved using standard CCC commands. * * This function must be called with the bus lock held in write mode. - * - * Return: a 0 in case of success, an negative error code otherwise. */ -int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, - u8 addr) +void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr) { struct i3c_device_info info = { .dyn_addr = addr }; struct i3c_dev_desc *newdev, *olddev; @@ -2460,7 +2453,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, mutex_unlock(&newdev->ibi_lock); } - return 0; + return; err_detach_dev: if (newdev->dev && newdev->dev->desc) @@ -2480,8 +2473,6 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV); dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); - - return ret; } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index e2c831fb5354..f73cede87d36 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -615,8 +615,7 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master); int i3c_master_get_free_addr(struct i3c_master_controller *master, u8 start_addr); -int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, - u8 addr); +void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr); int i3c_master_do_daa(struct i3c_master_controller *master); int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa); struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr, From 94daedb41d288d9ce798332fc2feb523875676e1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:06 +0300 Subject: [PATCH 3646/5214] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked() Relocate i3c_master_do_daa_ext() and i3c_master_do_daa() so they appear after i3c_master_add_i3c_dev_locked(). This ordering is required for upcoming changes where the DAA flow will (indirectly) rely on i3c_master_add_i3c_dev_locked() functionality. Reordering avoids forward dependency issues and keeps related code paths logically arranged. No functional change. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 128 +++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index efd940a30732..043b69c10faa 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1870,70 +1870,6 @@ static void i3c_master_reg_work_fn(struct work_struct *work) i3c_bus_normaluse_unlock(&master->bus); } -/** - * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) - * @master: controller - * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA) - * - * Perform Dynamic Address Assignment with optional support for System - * Hibernation (@rstdaa is true). - * - * After System Hibernation, Dynamic Addresses can have been reassigned at boot - * time to different values. A simple strategy is followed to handle that. - * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA - * procedure which has provision for reassigning addresses that differ from the - * previously recorded addresses. - * - * Return: a 0 in case of success, an negative error code otherwise. - */ -int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) -{ - int rstret = 0; - int ret; - - ret = i3c_master_rpm_get(master); - if (ret) - return ret; - - i3c_bus_maintenance_lock(&master->bus); - - if (master->shutting_down) { - ret = -ENODEV; - } else { - if (rstdaa) - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - ret = master->ops->do_daa(master); - } - - i3c_bus_maintenance_unlock(&master->bus); - - if (ret) - goto out; - - queue_work(master->wq, &master->reg_work); -out: - i3c_master_rpm_put(master); - - return rstret ?: ret; -} -EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext); - -/** - * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment) - * @master: master doing the DAA - * - * This function instantiates I3C device objects and adds them to the - * I3C device list. All device information is automatically retrieved using - * standard CCC commands. - * - * Return: a 0 in case of success, an negative error code otherwise. - */ -int i3c_master_do_daa(struct i3c_master_controller *master) -{ - return i3c_master_do_daa_ext(master, false); -} -EXPORT_SYMBOL_GPL(i3c_master_do_daa); - /** * i3c_master_dma_map_single() - Map buffer for single DMA transfer * @dev: device object of a device doing DMA @@ -2476,6 +2412,70 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); +/** + * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) + * @master: controller + * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA) + * + * Perform Dynamic Address Assignment with optional support for System + * Hibernation (@rstdaa is true). + * + * After System Hibernation, Dynamic Addresses can have been reassigned at boot + * time to different values. A simple strategy is followed to handle that. + * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA + * procedure which has provision for reassigning addresses that differ from the + * previously recorded addresses. + * + * Return: a 0 in case of success, an negative error code otherwise. + */ +int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) +{ + int rstret = 0; + int ret; + + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + + i3c_bus_maintenance_lock(&master->bus); + + if (master->shutting_down) { + ret = -ENODEV; + } else { + if (rstdaa) + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + ret = master->ops->do_daa(master); + } + + i3c_bus_maintenance_unlock(&master->bus); + + if (ret) + goto out; + + queue_work(master->wq, &master->reg_work); +out: + i3c_master_rpm_put(master); + + return rstret ?: ret; +} +EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext); + +/** + * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment) + * @master: master doing the DAA + * + * This function instantiates I3C device objects and adds them to the + * I3C device list. All device information is automatically retrieved using + * standard CCC commands. + * + * Return: a 0 in case of success, an negative error code otherwise. + */ +int i3c_master_do_daa(struct i3c_master_controller *master) +{ + return i3c_master_do_daa_ext(master, false); +} +EXPORT_SYMBOL_GPL(i3c_master_do_daa); + #define OF_I3C_REG1_IS_I2C_DEV BIT(31) static int From 4438609ede52b1bb1374cb2847b97dfc28800b14 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:07 +0300 Subject: [PATCH 3647/5214] i3c: master: Reconcile dynamic addresses after DAA After Dynamic Address Assignment (DAA), there may be cases where devices have been assigned dynamic addresses on the bus, but are not successfully registered in the device model. This can happen, for example, if errors occur during device addition, leaving the bus state and software state inconsistent. Introduce a reconciliation step to resolve such inconsistencies. Scan all address slots marked as I3C devices by the bus, and compare them against the set of devices currently registered. For any dynamic address that is marked occupied but has no corresponding i3c_dev_desc, probe for device presence using a GETSTATUS CCC. Retry the probe (with exponential backoff delay) to handle transient NACK conditions. If a device responds, register it via i3c_master_add_i3c_dev_locked(). Otherwise, free the address slot so it may be reused in future DAA operations. Note, i3c_master_add_i3c_dev_locked() may fail (again), in which case the dynamic address remains marked as occupied. A future DAA will try again. This also handles a corner case where a device is assigned a dynamic address but not successfully added, and subsequently loses that address (e.g. due to power management). If DAA is run again, the device may receive a new dynamic address while the old one remains marked as occupied. Repeated occurrences of this scenario could eventually exhaust the dynamic address space. The reconciliation step ensures that stale addresses are detected and freed, preventing address leakage. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 119 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 043b69c10faa..109aa50eb1f8 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -6,7 +6,9 @@ */ #include +#include #include +#include #include #include #include @@ -1613,6 +1615,57 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev) return 0; } +static int i3c_master_getstatus_locked(struct i3c_master_controller *master, + u8 addr, u16 *status) +{ + struct i3c_ccc_getstatus *getstatus; + struct i3c_ccc_cmd_dest dest; + struct i3c_ccc_cmd cmd; + int ret; + + getstatus = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*getstatus)); + if (!getstatus) + return -ENOMEM; + + i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETSTATUS, &dest, 1); + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + if (ret) + goto out; + + if (dest.payload.len != sizeof(*getstatus)) { + ret = -EIO; + goto out; + } + + if (status) + *status = be16_to_cpu(getstatus->status); +out: + i3c_ccc_cmd_dest_cleanup(&dest); + + return ret; +} + +/* Values are chosen to give the device plenty of opportunities to respond */ +#define I3C_DEV_PROBE_INITIAL_DELAY_US 20 +#define I3C_DEV_PROBE_DELAY_FACTOR 2 +#define I3C_DEV_PROBE_CNT 5 + +static bool i3c_master_i3c_dev_present(struct i3c_master_controller *master, unsigned int addr) +{ + int delay = I3C_DEV_PROBE_INITIAL_DELAY_US; + + for (int i = 0; i < I3C_DEV_PROBE_CNT; i++) { + if (i) { + fsleep(delay); + delay *= I3C_DEV_PROBE_DELAY_FACTOR; + } + if (!i3c_master_getstatus_locked(master, addr, NULL)) + return true; + } + + return false; +} + static void i3c_master_put_i3c_addrs(struct i3c_dev_desc *dev) { struct i3c_master_controller *master = i3c_dev_get_master(dev); @@ -2256,22 +2309,25 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev) } /** - * i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus + * __i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus * @master: master used to send frames on the bus * @addr: I3C slave dynamic address assigned to the device + * @probe: probe to see if the device is really present at @addr * * This function instantiates an I3C device object and adds it to the I3C device * list. All device information is retrieved using standard CCC commands. * * This function must be called with the bus lock held in write mode. */ -void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr) +static void __i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, + u8 addr, bool probe) { struct i3c_device_info info = { .dyn_addr = addr }; struct i3c_dev_desc *newdev, *olddev; u8 old_dyn_addr = addr, expected_dyn_addr; struct i3c_ibi_setup ibireq = { }; bool enable_ibi = false; + bool no_dev = false; int ret; newdev = i3c_master_alloc_i3c_dev(master, &info); @@ -2284,6 +2340,18 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr if (ret) goto err_free_dev; + /* + * When a dynamic address is first assigned, there is no need to check + * whether it is still assigned, however, if adding the device fails, + * it will be attempted again later, at which point the address may + * have been lost (e.g. due to power management), so for that case, + * probe to see if the device is still present at the assigned address. + */ + if (probe && !i3c_master_i3c_dev_present(master, addr)) { + no_dev = true; + goto err_detach_dev; + } + ret = i3c_master_retrieve_dev_info(newdev); if (ret) goto err_detach_dev; @@ -2401,6 +2469,8 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr i3c_master_free_i3c_dev(newdev); err_prevent_addr_reuse: + if (no_dev) + return; /* * Although the device has not been added, the address has been * assigned. Prevent the address from being used again. @@ -2410,8 +2480,48 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); } + +/** + * i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus + * @master: master used to send frames on the bus + * @addr: I3C slave dynamic address assigned to the device + * + * This function instantiates an I3C device object and adds it to the + * I3C device list. All device information is automatically retrieved using + * standard CCC commands. + * + * This function must be called with the bus lock held in write mode. + */ +void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr) +{ + __i3c_master_add_i3c_dev_locked(master, addr, false); +} EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); +static void i3c_master_reconcile_dyn_addrs(struct i3c_master_controller *master) +{ + DECLARE_BITMAP(dev_dyn_addrs, I2C_MAX_ADDR + 1); + enum i3c_addr_slot_status status; + struct i3c_dev_desc *desc; + + /* Mark all devices' dynamic and static addresses in the bitmap */ + bitmap_zero(dev_dyn_addrs, I2C_MAX_ADDR + 1); + i3c_bus_for_each_i3cdev(&master->bus, desc) { + if (desc->info.static_addr) + __set_bit(desc->info.static_addr, dev_dyn_addrs); + __set_bit(desc->info.dyn_addr, dev_dyn_addrs); + } + /* Reconcile the bitmap with the bus address slot status */ + for (unsigned int addr = 0; addr <= I2C_MAX_ADDR; addr++) { + status = i3c_bus_get_addr_slot_status(&master->bus, addr); + if (status != I3C_ADDR_SLOT_I3C_DEV || test_bit(addr, dev_dyn_addrs)) + continue; + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_FREE); + /* Try to add the device, but probe to see if it is really present */ + __i3c_master_add_i3c_dev_locked(master, addr, true); + } +} + /** * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) * @master: controller @@ -2445,6 +2555,11 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) if (rstdaa) rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); ret = master->ops->do_daa(master); + /* + * Handle cases where a dynamic address was assigned but the + * device was not successfully added. + */ + i3c_master_reconcile_dyn_addrs(master); } i3c_bus_maintenance_unlock(&master->bus); From 9972c11b2a1b7b59d717f37ea0644736ecc75bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 4 May 2026 16:33:15 +0200 Subject: [PATCH 3648/5214] i3c: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the various struct pci_device_id arrays were initialized by list expressions. This isn't easily readable if you're not into PCI. Using named initializers is more explicit and thus easier to parse. This change doesn't introduce changes to the compiled pci_device_id arrays. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260504143324.2122737-2-u.kleine-koenig@baylibre.com Signed-off-by: Alexandre Belloni --- .../master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c index 9468786fb853..5a9e2a43eff8 100644 --- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c @@ -461,21 +461,21 @@ static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = { static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { /* Wildcat Lake-U */ - { PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_mi_1_info}, - { PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_si_2_info}, + { PCI_VDEVICE(INTEL, 0x4d7c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, + { PCI_VDEVICE(INTEL, 0x4d6f), .driver_data = (kernel_ulong_t)&intel_si_2_info }, /* Panther Lake-H */ - { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_mi_1_info}, - { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_si_2_info}, + { PCI_VDEVICE(INTEL, 0xe37c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, + { PCI_VDEVICE(INTEL, 0xe36f), .driver_data = (kernel_ulong_t)&intel_si_2_info }, /* Panther Lake-P */ - { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_mi_1_info}, - { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_si_2_info}, + { PCI_VDEVICE(INTEL, 0xe47c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, + { PCI_VDEVICE(INTEL, 0xe46f), .driver_data = (kernel_ulong_t)&intel_si_2_info }, /* Nova Lake-S */ - { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_mi_1_info}, - { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_mi_2_info}, + { PCI_VDEVICE(INTEL, 0x6e2c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, + { PCI_VDEVICE(INTEL, 0x6e2d), .driver_data = (kernel_ulong_t)&intel_mi_2_info }, /* Nova Lake-H */ - { PCI_VDEVICE(INTEL, 0xd37c), (kernel_ulong_t)&intel_mi_1_info}, - { PCI_VDEVICE(INTEL, 0xd36f), (kernel_ulong_t)&intel_mi_2_info}, - { }, + { PCI_VDEVICE(INTEL, 0xd37c), .driver_data = (kernel_ulong_t)&intel_mi_1_info }, + { PCI_VDEVICE(INTEL, 0xd36f), .driver_data = (kernel_ulong_t)&intel_mi_2_info }, + { } }; MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); From 9092105b87afcb38913919af82b38370ba69a7ea Mon Sep 17 00:00:00 2001 From: Manikandan Muralidharan Date: Mon, 25 May 2026 14:54:01 +0530 Subject: [PATCH 3649/5214] dt-bindings: i3c: mipi-i3c-hci: add Microchip SAMA7D65 compatible Add the microchip,sama7d65-i3c-hci compatible string to the MIPI I3C HCI binding. The Microchip SAMA7D65 I3C controller is based on the MIPI HCI specification but requires two clocks, so add a conditional constraint when this compatible is present. Acked-by: Conor Dooley Reviewed-by: Frank Li Signed-off-by: Manikandan Muralidharan Link: https://patch.msgid.link/20260525092405.1514213-2-manikandan.m@microchip.com Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/i3c/mipi-i3c-hci.yaml | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml b/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml index 39bb1a1784c9..d488fb420945 100644 --- a/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml +++ b/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml @@ -9,9 +9,6 @@ title: MIPI I3C HCI maintainers: - Nicolas Pitre -allOf: - - $ref: /schemas/i3c/i3c.yaml# - description: | MIPI I3C Host Controller Interface @@ -28,9 +25,17 @@ description: | properties: compatible: - const: mipi-i3c-hci + enum: + - mipi-i3c-hci + - microchip,sama7d65-i3c-hci reg: maxItems: 1 + + clocks: + items: + - description: Peripheral bus clock + - description: System Generic clock + interrupts: maxItems: 1 @@ -39,6 +44,20 @@ required: - reg - interrupts +allOf: + - $ref: /schemas/i3c/i3c.yaml# + - if: + properties: + compatible: + contains: + const: microchip,sama7d65-i3c-hci + then: + required: + - clocks + else: + properties: + clocks: false + unevaluatedProperties: false examples: From efaa912ab0f185dbde4443946cb4b8cd832262e4 Mon Sep 17 00:00:00 2001 From: Manikandan Muralidharan Date: Mon, 25 May 2026 14:54:03 +0530 Subject: [PATCH 3650/5214] i3c: mipi-i3c-hci: add microchip sama7d65 SoC compatible with the required quirk Add support for microchip sama7d65 SoC I3C HCI master only IP with additional clock support to enable bulk clock acquisition and apply the required quirks. Reviewed-by: Adrian Hunter Signed-off-by: Manikandan Muralidharan Reviewed-by: Frank Li Link: https://patch.msgid.link/20260525092405.1514213-4-manikandan.m@microchip.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index fffbc1775ef9..3a3a9a3d4dec 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -1105,6 +1106,7 @@ static int i3c_hci_init(struct i3c_hci *hci) static int i3c_hci_probe(struct platform_device *pdev) { const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data; + struct clk_bulk_data *clks; struct i3c_hci *hci; int irq, ret; @@ -1138,6 +1140,11 @@ static int i3c_hci_probe(struct platform_device *pdev) if (!hci->quirks && platform_get_device_id(pdev)) hci->quirks = platform_get_device_id(pdev)->driver_data; + ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &clks); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to get clocks\n"); + ret = i3c_hci_init(hci); if (ret) return ret; @@ -1168,6 +1175,9 @@ static void i3c_hci_remove(struct platform_device *pdev) static const __maybe_unused struct of_device_id i3c_hci_of_match[] = { { .compatible = "mipi-i3c-hci", }, + { .compatible = "microchip,sama7d65-i3c-hci", + .data = (void *)(ulong)(HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | + HCI_QUIRK_RESP_BUF_THLD) }, {}, }; MODULE_DEVICE_TABLE(of, i3c_hci_of_match); From 478cdd736f2ce3114f90e775d7358136d3977b94 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 20:07:20 -0500 Subject: [PATCH 3651/5214] Input: touchwin - reset the packet index on every complete packet tw_interrupt() accumulates each non-zero serial byte into a fixed three-byte buffer with a running index that is only reset once a full packet has been received *and* the device's two Y bytes agree: tw->data[tw->idx++] = data; if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { ... tw->idx = 0; } The reset is gated on tw->data[1] == tw->data[2], a value the device controls. A malicious, malfunctioning or counterfeit Touchwindow peripheral can stream non-zero bytes whose 2nd and 3rd bytes differ: the index reaches TW_LENGTH without the equality holding, is never reset, and keeps growing, so tw->data[tw->idx++] walks off the end of the three-byte array and the rest of the heap-allocated struct tw, one attacker-chosen byte at a time -- an unbounded, device-driven heap out-of-bounds write. Reset the index on every completed packet and report an event only when the two Y bytes match, like the other serio touchscreen drivers do. Fixes: 11ea3173d5f2 ("Input: add driver for Touchwin serial touchscreens") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260613-b4-disp-69921bfd-v1-1-82c036899959@proton.me Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/touchwin.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c index 099fd88e65d8..3ed33378dfcd 100644 --- a/drivers/input/touchscreen/touchwin.c +++ b/drivers/input/touchscreen/touchwin.c @@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct serio *serio, if (data) { /* touch */ tw->touched = 1; tw->data[tw->idx++] = data; - /* verify length and that the two Y's are the same */ - if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { - input_report_abs(dev, ABS_X, tw->data[0]); - input_report_abs(dev, ABS_Y, tw->data[1]); - input_report_key(dev, BTN_TOUCH, 1); - input_sync(dev); + /* a full packet ends the accumulation, valid or not */ + if (tw->idx == TW_LENGTH) { + /* report only if the two Y's are the same */ + if (tw->data[1] == tw->data[2]) { + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + } tw->idx = 0; } } else if (tw->touched) { /* untouch */ From acd3b94f8604aeee2b62f8fd18e95a0474546288 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:42 -0700 Subject: [PATCH 3652/5214] platform/x86/intel/pmt: Add pre/post decode hooks around header parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add optional pre- and post-decode callbacks to the PMT class so namespaces can perform setup and cleanup steps around header parsing. - Add pmt_pre_decode() and pmt_post_decode() to struct intel_pmt_namespace. - Update intel_pmt_dev_create() to invoke, in order: pre → header_decode() → post. - Keep the existing pmt_header_decode() callback unchanged. No functional changes. This adds flexibility for upcoming decoders while preserving current behavior. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/b178a341601ca694db99c3b738fe4ed9e0c2bede.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 12 ++++++++++++ drivers/platform/x86/intel/pmt/class.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index b4c9964df807..9b315334a69b 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -381,10 +381,22 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); + if (ns->pmt_pre_decode) { + ret = ns->pmt_pre_decode(intel_vsec_dev, entry); + if (ret) + return ret; + } + ret = ns->pmt_header_decode(entry, dev); if (ret) return ret; + if (ns->pmt_post_decode) { + ret = ns->pmt_post_decode(intel_vsec_dev, entry); + if (ret) + return ret; + } + ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 1ae56a5baad2..ff39014b208c 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -62,6 +62,10 @@ struct intel_pmt_namespace { struct xarray *xa; int (*pmt_header_decode)(struct intel_pmt_entry *entry, struct device *dev); + int (*pmt_pre_decode)(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry); + int (*pmt_post_decode)(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry); int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev, struct intel_pmt_entry *entry); }; From b3de79d932bdcee4b2b9c8f9a058516699cf1c50 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:43 -0700 Subject: [PATCH 3653/5214] platform/x86/intel/pmt/crashlog: Split init into pre-decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor crashlog initialization to use the PMT namespace pre-decode hook: - Add pmt_crashlog_pre_decode() to parse type/version, select the crashlog_info, initialize the control mutex, and set entry->attr_grp. - Simplify pmt_crashlog_header_decode() to only read header fields from the discovery table. - Wire the namespace with .pmt_pre_decode = pmt_crashlog_pre_decode. This separates structural initialization from header parsing, aligning crashlog with the PMT class pre/post decode flow. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/ed8cda8456c97132cf2d2b4ff6a5cffb1ce3a666.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index b0393c9c5b4b..f936daf99e4d 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -496,11 +496,9 @@ static const struct crashlog_info *select_crashlog_info(u32 type, u32 version) return &crashlog_type1_ver2; } -static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, - struct device *dev) +static int pmt_crashlog_pre_decode(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry) { - void __iomem *disc_table = entry->disc_table; - struct intel_pmt_header *header = &entry->header; struct crashlog_entry *crashlog; u32 version; u32 type; @@ -513,6 +511,16 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, mutex_init(&crashlog->control_mutex); crashlog->info = select_crashlog_info(type, version); + entry->attr_grp = crashlog->info->attr_grp; + + return 0; +} + +static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, + struct device *dev) +{ + void __iomem *disc_table = entry->disc_table; + struct intel_pmt_header *header = &entry->header; header->access_type = GET_ACCESS(readl(disc_table)); header->guid = readl(disc_table + GUID_OFFSET); @@ -521,8 +529,6 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, /* Size is measured in DWORDS, but accessor returns bytes */ header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); - entry->attr_grp = crashlog->info->attr_grp; - return 0; } @@ -530,6 +536,7 @@ static DEFINE_XARRAY_ALLOC(crashlog_array); static struct intel_pmt_namespace pmt_crashlog_ns = { .name = "crashlog", .xa = &crashlog_array, + .pmt_pre_decode = pmt_crashlog_pre_decode, .pmt_header_decode = pmt_crashlog_header_decode, }; From 521460e6699557f2aa0e4818110e082b76809e98 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:44 -0700 Subject: [PATCH 3654/5214] platform/x86/intel/pmt/telemetry: Move overlap check to post-decode hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the telemetry namespace to use the new PMT class pre/post decode interface. The overlap check, which previously occurred during header decode, is now performed in the post-decode hook once header fields are populated. This preserves existing behavior while reusing the same header decode logic across PMT drivers. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/2f5e429a38e22eb45fcfaaca4e037fa395d4f199.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.h | 1 + drivers/platform/x86/intel/pmt/telemetry.c | 24 ++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index ff39014b208c..8a0db0ef58c1 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -37,6 +37,7 @@ struct intel_pmt_header { u32 size; u32 guid; u8 access_type; + u8 telem_type; }; struct intel_pmt_entry { diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index bdc7c24a3678..d22f633638be 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -58,14 +58,9 @@ struct pmt_telem_priv { struct intel_pmt_entry entry[]; }; -static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, - struct device *dev) +static bool pmt_telem_region_overlaps(struct device *dev, u32 guid, u32 type) { - u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); - if (intel_pmt_is_early_client_hw(dev)) { - u32 type = TELEM_TYPE(readl(entry->disc_table)); - if ((type == TELEM_TYPE_PUNIT_FIXED) || (guid == TELEM_CLIENT_FIXED_BLOCK_GUID)) return true; @@ -80,15 +75,25 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, void __iomem *disc_table = entry->disc_table; struct intel_pmt_header *header = &entry->header; - if (pmt_telem_region_overlaps(entry, dev)) - return 1; - header->access_type = TELEM_ACCESS(readl(disc_table)); header->guid = readl(disc_table + TELEM_GUID_OFFSET); header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); /* Size is measured in DWORDS, but accessor returns bytes */ header->size = TELEM_SIZE(readl(disc_table)); + header->telem_type = TELEM_TYPE(readl(entry->disc_table)); + + return 0; +} + +static int pmt_telem_post_decode(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry) +{ + struct intel_pmt_header *header = &entry->header; + struct device *dev = &ivdev->auxdev.dev; + + if (pmt_telem_region_overlaps(dev, header->guid, header->telem_type)) + return 1; /* * Some devices may expose non-functioning entries that are @@ -131,6 +136,7 @@ static struct intel_pmt_namespace pmt_telem_ns = { .name = "telem", .xa = &telem_array, .pmt_header_decode = pmt_telem_header_decode, + .pmt_post_decode = pmt_telem_post_decode, .pmt_add_endpoint = pmt_telem_add_endpoint, }; From 4a87492cd137d158779923a034d7e742f7358952 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:45 -0700 Subject: [PATCH 3655/5214] platform/x86/intel/pmt: Pass discovery index instead of resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change PMT class code to pass a discovery index rather than a direct struct resource when creating entries. This allows the class to identify the discovery source generically without assuming PCI BAR resources. For PCI devices, the index still resolves to a resource in the intel_vsec_device. Other discovery sources, such as ACPI, can use the same index without needing a struct resource. Signed-off-by: David E. Box Link: https://patch.msgid.link/8e785902c6a3ac1b5a9c3f0f65096553dc5acd4f.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 9b315334a69b..7da8279b54f8 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -206,11 +206,12 @@ EXPORT_SYMBOL_GPL(intel_pmt_class); static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, - struct resource *disc_res) + int idx) { struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; struct intel_pmt_header *header = &entry->header; + struct resource *disc_res; u8 bir; /* @@ -235,6 +236,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, * For access_type LOCAL, the base address is as follows: * base address = end of discovery region + base offset */ + disc_res = &ivdev->resource[idx]; entry->base_addr = disc_res->end + 1 + header->base_offset; /* @@ -397,7 +399,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa return ret; } - ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res); + ret = intel_pmt_populate_entry(entry, intel_vsec_dev, idx); if (ret) return ret; From 4dfc7dca6e934ca414d8d3c70a84e79d13d9e750 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:46 -0700 Subject: [PATCH 3656/5214] platform/x86/intel/pmt: Cache the telemetry discovery header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pmt_telem_header_decode() only needs the discovery header dwords, but it currently decodes them by reading directly from entry->disc_table. Cache the discovery header in intel_pmt_entry when the device is created and have telemetry decode use the cached values instead of performing MMIO reads at decode time. The DVSEC discovery resource for a namespace is sized by its per-entry entry_size (in dwords), which can be less than the 4-dword cache (e.g. telemetry uses entry_size = 3, i.e. 12 bytes). Cap the memcpy_fromio() to resource_size(disc_res) so the new cache does not read past the mapped region. Any unread dwords stay zero from the zero-initialized allocation of the containing struct. This keeps the telemetry header decode path independent of how the discovery data is backed and avoids baking a direct MMIO assumption into the feature-specific decode logic. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/f805e2ada52dc0661761cda7f692e76e6ea2d257.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 11 +++++++++++ drivers/platform/x86/intel/pmt/class.h | 1 + drivers/platform/x86/intel/pmt/telemetry.c | 12 ++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 7da8279b54f8..8d27f59d5bff 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -383,6 +384,16 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); + /* + * The mapped discovery resource may be smaller than disc_header (its + * size is the namespace's DVSEC entry_size in dwords, which can be + * less than 4). Cap the copy to the actual resource size to avoid + * reading past the mapped region; any unread dwords stay zero from + * the zero-initialized allocation of the containing struct. + */ + memcpy_fromio(entry->disc_header, entry->disc_table, + min(sizeof(entry->disc_header), resource_size(disc_res))); + if (ns->pmt_pre_decode) { ret = ns->pmt_pre_decode(intel_vsec_dev, entry); if (ret) diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 8a0db0ef58c1..84202fc7920c 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -44,6 +44,7 @@ struct intel_pmt_entry { struct telem_endpoint *ep; struct pci_dev *pcidev; struct intel_pmt_header header; + u32 disc_header[4]; struct bin_attribute pmt_bin_attr; const struct attribute_group *attr_grp; struct kobject *kobj; diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index d22f633638be..953f35b6daec 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -72,16 +72,16 @@ static bool pmt_telem_region_overlaps(struct device *dev, u32 guid, u32 type) static int pmt_telem_header_decode(struct intel_pmt_entry *entry, struct device *dev) { - void __iomem *disc_table = entry->disc_table; struct intel_pmt_header *header = &entry->header; + u32 *disc_header = entry->disc_header; - header->access_type = TELEM_ACCESS(readl(disc_table)); - header->guid = readl(disc_table + TELEM_GUID_OFFSET); - header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); + header->access_type = TELEM_ACCESS(disc_header[0]); + header->guid = disc_header[1]; + header->base_offset = disc_header[2]; /* Size is measured in DWORDS, but accessor returns bytes */ - header->size = TELEM_SIZE(readl(disc_table)); - header->telem_type = TELEM_TYPE(readl(entry->disc_table)); + header->size = TELEM_SIZE(disc_header[0]); + header->telem_type = TELEM_TYPE(disc_header[0]); return 0; } From 13793c7f9e9ff30042a54bb680662b5cfa0f58fa Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:47 -0700 Subject: [PATCH 3657/5214] platform/x86/intel/pmt: Unify header fetch and add ACPI source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the PMT class to read discovery headers from either PCI MMIO or ACPI-provided entries, depending on the discovery source. The new source-aware fetch helper caches the canonical discovery header for both paths, capping PCI MMIO reads to the mapped resource size, while keeping the mapped PCI discovery table available for users such as crashlog. Split intel_pmt_populate_entry() into source-specific resolvers: - pmt_resolve_access_pci(): handles both ACCESS_LOCAL and ACCESS_BARID for PCI-backed devices and sets entry->pcidev. Same existing functionality. - pmt_resolve_access_acpi(): handles only ACCESS_BARID for ACPI-backed devices, rejecting ACCESS_LOCAL which has no valid semantics without a physical discovery resource. This maintains existing PCI behavior and makes no functional changes for PCI devices. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/4b33b04ffaf0943b67d330f48b5d1dfcb6d1be5d.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 151 +++++++++++++++++++++---- drivers/platform/x86/intel/pmt/class.h | 2 +- include/linux/intel_vsec.h | 5 +- 3 files changed, 136 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 8d27f59d5bff..d0ab8e33c62a 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -205,9 +205,9 @@ struct class intel_pmt_class = { }; EXPORT_SYMBOL_GPL(intel_pmt_class); -static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, - struct intel_vsec_device *ivdev, - int idx) +static int pmt_resolve_access_pci(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev, + int idx) { struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; @@ -287,6 +287,81 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, } entry->pcidev = pci_dev; + + return 0; +} + +static int pmt_resolve_access_acpi(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev) +{ + struct pci_dev *pci_dev = NULL; + struct device *dev = &ivdev->auxdev.dev; + struct intel_pmt_header *header = &entry->header; + u8 bir; + + if (dev_is_pci(ivdev->dev)) + pci_dev = to_pci_dev(ivdev->dev); + + /* + * The base offset should always be 8 byte aligned. + * + * For non-local access types the lower 3 bits of base offset + * contains the index of the base address register where the + * telemetry can be found. + */ + bir = GET_BIR(header->base_offset); + + switch (header->access_type) { + case ACCESS_BARID: + /* ACPI platform drivers use base_addr */ + if (ivdev->base_addr) { + entry->base_addr = ivdev->base_addr + + GET_ADDRESS(header->base_offset); + break; + } + + /* If base_addr is not provided, then this is an ACPI companion device */ + if (!pci_dev) { + dev_err(dev, "ACCESS_BARID requires PCI BAR resources or base_addr\n"); + return -EINVAL; + } + + entry->base_addr = pci_resource_start(pci_dev, bir) + + GET_ADDRESS(header->base_offset); + break; + default: + dev_err(dev, "Unsupported access type %d for ACPI based PMT\n", + header->access_type); + return -EINVAL; + } + + return 0; +} + +static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev, + int idx) +{ + struct intel_pmt_header *header = &entry->header; + struct device *dev = &ivdev->auxdev.dev; + int ret; + + switch (ivdev->src) { + case INTEL_VSEC_DISC_PCI: + ret = pmt_resolve_access_pci(entry, ivdev, idx); + if (ret) + return ret; + break; + case INTEL_VSEC_DISC_ACPI: + ret = pmt_resolve_access_acpi(entry, ivdev); + if (ret) + return ret; + break; + default: + dev_err(dev, "Unknown discovery source: %d\n", ivdev->src); + return -EINVAL; + } + entry->guid = header->guid; entry->size = header->size; entry->cb = ivdev->priv_data; @@ -371,28 +446,66 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, return ret; } +static int pmt_get_headers(struct intel_vsec_device *ivdev, int idx, + struct intel_pmt_entry *entry) +{ + struct device *dev = &ivdev->auxdev.dev; + size_t header_bytes = sizeof(entry->disc_header); + + switch (ivdev->src) { + case INTEL_VSEC_DISC_PCI: { + struct resource *disc_res = &ivdev->resource[idx]; + void __iomem *disc_table; + + disc_table = devm_ioremap_resource(dev, disc_res); + if (IS_ERR(disc_table)) + return PTR_ERR(disc_table); + + /* + * The mapped resource is sized by the namespace's DVSEC + * entry_size (in dwords), which can be less than the default + * size (e.g. telemetry uses entry_size = 3, 12 bytes). Cap the + * copy to resource_size() to avoid reading past the mapped + * region. + */ + memset(entry->disc_header, 0, header_bytes); + memcpy_fromio(entry->disc_header, disc_table, + min(header_bytes, resource_size(disc_res))); + + /* Used by crashlog driver */ + entry->disc_table = disc_table; + + return 0; + } + case INTEL_VSEC_DISC_ACPI: { + memcpy(entry->disc_header, &ivdev->acpi_disc[idx][0], header_bytes); + /* + * No MMIO mapping exists on the ACPI source path; the cached + * headers are the only view of the discovery record. Consumers + * that dereference disc_table (e.g. crashlog) must therefore + * only be wired to namespaces backed by INTEL_VSEC_DISC_PCI. + */ + entry->disc_table = NULL; + + return 0; + } + default: + dev_err(dev, "Unknown discovery source type: %d\n", ivdev->src); + break; + } + + return -EINVAL; +} + int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, struct intel_vsec_device *intel_vsec_dev, int idx) { struct device *dev = &intel_vsec_dev->auxdev.dev; - struct resource *disc_res; int ret; - disc_res = &intel_vsec_dev->resource[idx]; - - entry->disc_table = devm_ioremap_resource(dev, disc_res); - if (IS_ERR(entry->disc_table)) - return PTR_ERR(entry->disc_table); - - /* - * The mapped discovery resource may be smaller than disc_header (its - * size is the namespace's DVSEC entry_size in dwords, which can be - * less than 4). Cap the copy to the actual resource size to avoid - * reading past the mapped region; any unread dwords stay zero from - * the zero-initialized allocation of the containing struct. - */ - memcpy_fromio(entry->disc_header, entry->disc_table, - min(sizeof(entry->disc_header), resource_size(disc_res))); + ret = pmt_get_headers(intel_vsec_dev, idx, entry); + if (ret) + return ret; if (ns->pmt_pre_decode) { ret = ns->pmt_pre_decode(intel_vsec_dev, entry); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 84202fc7920c..a0ece4fc3837 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -44,7 +44,7 @@ struct intel_pmt_entry { struct telem_endpoint *ep; struct pci_dev *pcidev; struct intel_pmt_header header; - u32 disc_header[4]; + u32 disc_header[PMT_DISC_DWORDS]; struct bin_attribute pmt_bin_attr; const struct attribute_group *attr_grp; struct kobject *kobj; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 07ea563f524e..843cda8f8644 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -28,6 +28,7 @@ #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +#define PMT_DISC_DWORDS 4 struct device; struct pci_dev; @@ -122,7 +123,7 @@ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; const struct vsec_feature_dependency *deps; - u32 (*acpi_disc)[4]; + u32 (*acpi_disc)[PMT_DISC_DWORDS]; enum intel_vsec_disc_source src; void *priv_data; unsigned long caps; @@ -153,7 +154,7 @@ struct intel_vsec_platform_info { struct intel_vsec_device { struct auxiliary_device auxdev; struct device *dev; - u32 (*acpi_disc)[4]; + u32 (*acpi_disc)[PMT_DISC_DWORDS]; enum intel_vsec_disc_source src; struct ida *ida; int num_resources; From 8ba4cf60c5ce4a3073126a6cbb09475010f8fa52 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:48 -0700 Subject: [PATCH 3658/5214] platform/x86/intel/pmc: Add PMC SSRAM Kconfig description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a proper description for the intel_pmc_ssram driver. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/bd7ba2f98450751af1de054dac0469acc1138513.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index c6ef0bcf76af..e9012b703918 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -28,3 +28,14 @@ config INTEL_PMC_CORE config INTEL_PMC_SSRAM_TELEMETRY tristate + help + This driver discovers PMC SSRAM telemetry regions through the PMC's + MMIO interface (PCI) or ACPI _DSD properties and registers them with + the Intel VSEC framework as Intel PMT telemetry devices. + + It probes the PMC SSRAM device, extracts DVSEC information from MMIO, + reads device IDs and base addresses for multiple PMCs (main, IOE, PCH), + and exposes the discovered telemetry through Intel PMT interfaces + (including sysfs). + + This option is selected by INTEL_PMC_CORE. From adc5d98d9ff8b5d37e89e11a2ac5512595541329 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:49 -0700 Subject: [PATCH 3659/5214] platform/x86/intel/pmc: Add ACPI PWRM telemetry driver for Nova Lake S MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an ACPI-based PMC PWRM telemetry driver for Nova Lake S. The driver locates PMT discovery data in _DSD under the Intel VSEC UUID, parses it, and registers telemetry regions with the PMT/VSEC framework so PMC telemetry is exposed via existing PMT interfaces. Export pmc_parse_telem_dsd() and pmc_find_telem_guid() to support ACPI discovery in other PMC drivers (e.g., ssram_telemetry) without duplicating ACPI parsing logic. Also export acpi_disc_t typedef from core.h for callers to properly declare discovery table arrays. Selected by INTEL_PMC_CORE. Existing PCI functionality is preserved. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/09b8211d8a5a79fa019ee2397137a6a43cf19430.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Kconfig | 14 ++ drivers/platform/x86/intel/pmc/Makefile | 2 + drivers/platform/x86/intel/pmc/core.h | 16 ++ .../platform/x86/intel/pmc/pwrm_telemetry.c | 216 ++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 drivers/platform/x86/intel/pmc/pwrm_telemetry.c diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index e9012b703918..561d46634ab2 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -9,6 +9,7 @@ config INTEL_PMC_CORE depends on ACPI depends on INTEL_PMT_TELEMETRY select INTEL_PMC_SSRAM_TELEMETRY + select INTEL_PMC_PWRM_TELEMETRY help The Intel Platform Controller Hub for Intel Core SoCs provides access to Power Management Controller registers via various interfaces. This @@ -39,3 +40,16 @@ config INTEL_PMC_SSRAM_TELEMETRY (including sysfs). This option is selected by INTEL_PMC_CORE. + +config INTEL_PMC_PWRM_TELEMETRY + tristate + help + This driver discovers PMC PWRM telemetry regions described in ACPI + _DSD and registers them with the Intel VSEC framework as Intel PMT + telemetry devices. + + It validates the ACPI discovery data and publishes the discovered + regions so they can be accessed through the Intel PMT telemetry + interfaces (including sysfs). + + This option is selected by INTEL_PMC_CORE. diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 23853e867c91..5b595176f812 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o # Intel PMC SSRAM driver intel_pmc_ssram_telemetry-y += ssram_telemetry.o obj-$(CONFIG_INTEL_PMC_SSRAM_TELEMETRY) += intel_pmc_ssram_telemetry.o +intel_pmc_pwrm_telemetry-y += pwrm_telemetry.o +obj-$(CONFIG_INTEL_PMC_PWRM_TELEMETRY) += intel_pmc_pwrm_telemetry.o diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 55cf567febe4..b4c7399f8369 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -14,10 +14,15 @@ #include #include +#include +#include #include +#include struct telem_endpoint; +DEFINE_FREE(pmc_acpi_free, void *, if (_T) ACPI_FREE(_T)) + #define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0) #define PMC_BASE_ADDR_DEFAULT 0xFE000000 @@ -622,6 +627,8 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc, extern const struct file_operations pmc_core_substate_req_regs_fops; extern const struct file_operations pmc_core_substate_blk_req_fops; +extern const guid_t intel_vsec_guid; + #define pmc_for_each_mode(mode, pmc) \ for (unsigned int __i = 0, __cond; \ __cond = __i < (pmc)->num_lpm_modes, \ @@ -643,4 +650,13 @@ static const struct file_operations __name ## _fops = { \ .release = single_release, \ } +struct intel_vsec_header; +union acpi_object; + +/* Avoid checkpatch warning */ +typedef u32 (*acpi_disc_t)[PMT_DISC_DWORDS]; + +acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj, + struct intel_vsec_header *header); +union acpi_object *pmc_find_telem_guid(union acpi_object *dsd); #endif /* PMC_CORE_H */ diff --git a/drivers/platform/x86/intel/pmc/pwrm_telemetry.c b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c new file mode 100644 index 000000000000..62b0e9fc7920 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel PMC PWRM ACPI driver + * + * Copyright (C) 2025, Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define ENTRY_LEN 5 + +/* DWORD2 */ +#define DVSEC_ID_MASK GENMASK(15, 0) +#define NUM_ENTRIES_MASK GENMASK(23, 16) +#define ENTRY_SIZE_MASK GENMASK(31, 24) + +/* DWORD3 */ +#define TBIR_MASK GENMASK(2, 0) +#define DISC_TBL_OFF_MASK GENMASK(31, 3) + +const guid_t intel_vsec_guid = + GUID_INIT(0x294903fb, 0x634d, 0x4fc7, 0xaf, 0x1f, 0x0f, 0xb9, + 0x56, 0xb0, 0x4f, 0xc1); + +static bool is_valid_entry(union acpi_object *pkg) +{ + int i; + + if (!pkg || pkg->type != ACPI_TYPE_PACKAGE || pkg->package.count != ENTRY_LEN) + return false; + + if (pkg->package.elements[0].type != ACPI_TYPE_STRING) + return false; + + for (i = 1; i < ENTRY_LEN; i++) + if (pkg->package.elements[i].type != ACPI_TYPE_INTEGER) + return false; + + return true; +} + +acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj, + struct intel_vsec_header *header) +{ + union acpi_object *vsec_pkg; + union acpi_object *disc_pkg; + u64 hdr0, hdr1; + int num_regions; + int i; + + if (!header) + return ERR_PTR(-EINVAL); + + if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2) + return ERR_PTR(-EINVAL); + + /* First Package is DVSEC info */ + vsec_pkg = &obj->package.elements[0]; + if (!is_valid_entry(vsec_pkg)) + return ERR_PTR(-EINVAL); + + hdr0 = vsec_pkg->package.elements[3].integer.value; + hdr1 = vsec_pkg->package.elements[4].integer.value; + + header->id = FIELD_GET(DVSEC_ID_MASK, hdr0); + header->num_entries = FIELD_GET(NUM_ENTRIES_MASK, hdr0); + header->entry_size = FIELD_GET(ENTRY_SIZE_MASK, hdr0); + header->tbir = FIELD_GET(TBIR_MASK, hdr1); + header->offset = hdr1 & DISC_TBL_OFF_MASK; + + /* Second Package contains the discovery tables */ + disc_pkg = &obj->package.elements[1]; + if (disc_pkg->type != ACPI_TYPE_PACKAGE || disc_pkg->package.count < 1) + return ERR_PTR(-EINVAL); + + num_regions = disc_pkg->package.count; + if (header->num_entries != num_regions) + return ERR_PTR(-EINVAL); + + acpi_disc_t disc __free(kfree) = kmalloc_array(num_regions, sizeof(*disc), + GFP_KERNEL); + if (!disc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_regions; i++) { + union acpi_object *pkg; + u64 value; + int j; + + pkg = &disc_pkg->package.elements[i]; + if (!is_valid_entry(pkg)) + return ERR_PTR(-EINVAL); + + /* Element 0 is a descriptive string; DWORD values start at index 1. */ + for (j = 1; j < ENTRY_LEN; j++) { + value = pkg->package.elements[j].integer.value; + if (value > U32_MAX) + return ERR_PTR(-ERANGE); + + disc[i][j - 1] = value; + } + } + + return no_free_ptr(disc); +} +EXPORT_SYMBOL_NS_GPL(pmc_parse_telem_dsd, "INTEL_PMC_CORE"); + +union acpi_object *pmc_find_telem_guid(union acpi_object *dsd) +{ + int i; + + if (!dsd || dsd->type != ACPI_TYPE_PACKAGE) + return NULL; + + for (i = 0; i + 1 < dsd->package.count; i += 2) { + union acpi_object *uuid_obj, *data_obj; + guid_t uuid; + + uuid_obj = &dsd->package.elements[i]; + data_obj = &dsd->package.elements[i + 1]; + + if (uuid_obj->type != ACPI_TYPE_BUFFER || + uuid_obj->buffer.length != 16) + continue; + + memcpy(&uuid, uuid_obj->buffer.pointer, 16); + if (guid_equal(&uuid, &intel_vsec_guid)) + return data_obj; + } + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(pmc_find_telem_guid, "INTEL_PMC_CORE"); + +static int pmc_pwrm_acpi_probe(struct platform_device *pdev) +{ + struct intel_vsec_header header; + struct intel_vsec_header *headers[2] = { &header, NULL }; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct intel_vsec_platform_info info = { }; + struct device *dev = &pdev->dev; + union acpi_object *dsd; + struct resource *res; + acpi_handle handle; + acpi_status status; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_object(handle, "_DSD", NULL, &buf); + if (ACPI_FAILURE(status)) { + return dev_err_probe(dev, -ENODEV, "Could not evaluate _DSD: %s\n", + acpi_format_exception(status)); + } + + void *dsd_buf __free(pmc_acpi_free) = buf.pointer; + + dsd = pmc_find_telem_guid(dsd_buf); + if (!dsd) + return -ENODEV; + + acpi_disc_t acpi_disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header); + if (IS_ERR(acpi_disc)) + return PTR_ERR(acpi_disc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, header.tbir); + if (!res) + return -EINVAL; + + info.headers = headers; + info.caps = VSEC_CAP_TELEMETRY; + info.acpi_disc = acpi_disc; + info.src = INTEL_VSEC_DISC_ACPI; + info.base_addr = res->start; + + return intel_vsec_register(&pdev->dev, &info); +} + +static const struct acpi_device_id pmc_pwrm_acpi_ids[] = { + { "INTC1122", 0 }, /* Nova Lake */ + { "INTC1129", 0 }, /* Nova Lake */ + { } +}; +MODULE_DEVICE_TABLE(acpi, pmc_pwrm_acpi_ids); + +static struct platform_driver pmc_pwrm_acpi_driver = { + .probe = pmc_pwrm_acpi_probe, + .driver = { + .name = "intel_pmc_pwrm_acpi", + .acpi_match_table = ACPI_PTR(pmc_pwrm_acpi_ids), + }, +}; +module_platform_driver(pmc_pwrm_acpi_driver); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMC PWRM ACPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_VSEC"); From 90fd47d0d8109ef1301a60a44a7f85581a1e6efe Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:50 -0700 Subject: [PATCH 3660/5214] platform/x86/intel/pmc/ssram: Rename probe and PCI ID table for consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename intel_pmc_ssram_telemetry_probe() to pmc_ssram_telemetry_probe() and intel_pmc_ssram_telemetry_pci_ids[] to pmc_ssram_telemetry_pci_ids[], updating the MODULE_DEVICE_TABLE() and pci_driver wiring accordingly. This aligns the symbol names with the driver filename and module name, reduces redundant intel_ prefixes, and improves readability. No functional behavior changes are intended. Reviewed-by: Ilpo Järvinen Signed-off-by: David E. Box Link: https://patch.msgid.link/6d3935858214fdd0f530f9a7c08a387fa4e5e8cd.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 6f6e83e70fc5..1deb4d71da3f 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -149,7 +149,7 @@ int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, } EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); -static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) +static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { int ret; @@ -183,7 +183,7 @@ static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct return ret; } -static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { +static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, @@ -193,14 +193,14 @@ static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, { } }; -MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); +MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids); -static struct pci_driver intel_pmc_ssram_telemetry_driver = { +static struct pci_driver pmc_ssram_telemetry_driver = { .name = "intel_pmc_ssram_telemetry", - .id_table = intel_pmc_ssram_telemetry_pci_ids, - .probe = intel_pmc_ssram_telemetry_probe, + .id_table = pmc_ssram_telemetry_pci_ids, + .probe = pmc_ssram_telemetry_probe, }; -module_pci_driver(intel_pmc_ssram_telemetry_driver); +module_pci_driver(pmc_ssram_telemetry_driver); MODULE_IMPORT_NS("INTEL_VSEC"); MODULE_AUTHOR("Xi Pardee "); From d936dd2c605a17ab41826c1e7f738424d3d0bbfd Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:51 -0700 Subject: [PATCH 3661/5214] platform/x86/intel/pmc/ssram: Add PCI platform data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add per-device platform data for SSRAM telemetry PCI IDs and route probe through a method selector driven by id->driver_data. This is a preparatory refactor for follow-on discovery methods while preserving current behavior: all supported IDs continue to use the PCI initialization path. Signed-off-by: David E. Box Link: https://patch.msgid.link/1c6180097b20dbe1337828bf6d3667854d63583a.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 71 +++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 1deb4d71da3f..05f213aa108f 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -24,6 +25,18 @@ DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) +enum resource_method { + RES_METHOD_PCI, +}; + +struct ssram_type { + enum resource_method method; +}; + +static const struct ssram_type pci_main = { + .method = RES_METHOD_PCI, +}; + static struct pmc_ssram_telemetry *pmc_ssram_telems; static bool device_probed; @@ -69,7 +82,7 @@ static inline u64 get_base(void __iomem *addr, u32 offset) } static int -pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) +pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; @@ -109,6 +122,20 @@ pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 of return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); } +static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) +{ + int ret; + + ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_MAIN, 0); + if (ret) + return ret; + + pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); + pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + + return 0; +} + /** * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information * @pmc_idx: Index of the PMC @@ -151,6 +178,8 @@ EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { + const struct ssram_type *ssram_type; + enum resource_method method; int ret; pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, @@ -160,18 +189,25 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de goto probe_finish; } + ssram_type = (const struct ssram_type *)id->driver_data; + if (!ssram_type) { + dev_dbg(&pcidev->dev, "missing driver data\n"); + ret = -EINVAL; + goto probe_finish; + } + + method = ssram_type->method; + ret = pcim_enable_device(pcidev); if (ret) { dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n"); goto probe_finish; } - ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0); - if (ret) - goto probe_finish; - - pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); - pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + if (method == RES_METHOD_PCI) + ret = pmc_ssram_telemetry_pci_init(pcidev); + else + ret = -EINVAL; probe_finish: /* @@ -184,13 +220,20 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de } static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN), + .driver_data = (kernel_ulong_t)&pci_main }, { } }; MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids); From f79e57e2677c7641e94d5a38d4b2c2165f5c385c Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:52 -0700 Subject: [PATCH 3662/5214] platform/x86/intel/pmc/ssram: Refactor DEVID/PWRMBASE extraction into helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move DEVID/PWRMBASE extraction into pmc_ssram_get_devid_pwrmbase(). This is a preparatory refactor to place functionality in a common helper for reuse by a subsequent patch. Additionally add missing bits.h include and define SSRAM_BASE_ADDR_MASK for the address extraction mask. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/75ca738c88729f37f286f342c1fe8ff86f7eafe7.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 05f213aa108f..211d842df449 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -22,6 +23,7 @@ #define SSRAM_PCH_OFFSET 0x60 #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 +#define SSRAM_BASE_ADDR_MASK GENMASK_ULL(63, 3) DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) @@ -40,6 +42,23 @@ static const struct ssram_type pci_main = { static struct pmc_ssram_telemetry *pmc_ssram_telems; static bool device_probed; +static inline u64 get_base(void __iomem *addr, u32 offset) +{ + return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK; +} + +static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_idx) +{ + u64 pwrm_base; + u16 devid; + + pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); + devid = readw(ssram + SSRAM_DEVID_OFFSET); + + pmc_ssram_telems[pmc_idx].devid = devid; + pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; +} + static int pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram) { @@ -76,18 +95,12 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem return intel_vsec_register(&pcidev->dev, &info); } -static inline u64 get_base(void __iomem *addr, u32 offset) -{ - return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3); -} - static int pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; - u64 ssram_base, pwrm_base; - u16 devid; + u64 ssram_base; ssram_base = pci_resource_start(pcidev, 0); tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); @@ -112,11 +125,7 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u3 ssram = no_free_ptr(tmp_ssram); } - pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); - devid = readw(ssram + SSRAM_DEVID_OFFSET); - - pmc_ssram_telems[pmc_idx].devid = devid; - pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; + pmc_ssram_get_devid_pwrmbase(ssram, pmc_idx); /* Find and register and PMC telemetry entries */ return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); From e3c9200a9c0e05532c495956883ba11fe23a3df0 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:53 -0700 Subject: [PATCH 3663/5214] platform/x86/intel/pmc/ssram: Switch to static array with per-index probe state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace devm-allocated pmc_ssram_telems pointer with a fixed-size static array and introduce per-index probe state tracking. This prepares the driver for later per-device probe handling where tying the PMC tracking storage to one probed PCI device is no longer suitable. The previous single global device_probed flag cannot describe the state of individual PMC indices when multiple devices can be probed independently. Replace it with per-index state (UNPROBED, PROBING, PRESENT, ABSENT) and a staging cache that publishes discovered values only after probe completes. This avoids races between probe/unbind and concurrent readers. Use marked state accesses with release/acquire ordering to prevent compiler and CPU reordering issues across concurrent probe/unbind cycles. This patch was substantially rewritten in later revisions. Originally developed from earlier work by Xi Pardee. Signed-off-by: David E. Box Assisted-by: Claude:claude-sonnet-4-5 Link: https://patch.msgid.link/a2cccf532bec04fcc370819890d936212bc1b14c.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 230 ++++++++++++++---- 1 file changed, 182 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 211d842df449..3349f65ae7bd 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -24,6 +25,7 @@ #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 #define SSRAM_BASE_ADDR_MASK GENMASK_ULL(63, 3) +#define SSRAM_PCI_PMC_MASK (BIT(PMC_IDX_MAIN) | BIT(PMC_IDX_IOE) | BIT(PMC_IDX_PCH)) DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) @@ -39,15 +41,37 @@ static const struct ssram_type pci_main = { .method = RES_METHOD_PCI, }; -static struct pmc_ssram_telemetry *pmc_ssram_telems; -static bool device_probed; +enum pmc_ssram_state { + PMC_SSRAM_UNPROBED = 0, + PMC_SSRAM_PROBING, + PMC_SSRAM_PRESENT, + PMC_SSRAM_ABSENT, +}; + +static enum pmc_ssram_state pmc_ssram_state[MAX_NUM_PMC]; +static struct pmc_ssram_telemetry pmc_ssram_telems[MAX_NUM_PMC]; + +struct pmc_ssram_probe_cache { + /* Per-index values staged by probe before publishing globally. */ + struct pmc_ssram_telemetry telems[MAX_NUM_PMC]; + /* PMCs this probe instance is responsible for publishing. */ + unsigned long owned_mask; + /* Subset of owned_mask that was discovered successfully. */ + unsigned long valid_mask; +}; + +struct pmc_ssram_drvdata { + /* PMCs published by this bound device; used to unpublish on remove. */ + unsigned long owned_mask; +}; static inline u64 get_base(void __iomem *addr, u32 offset) { return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK; } -static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_idx) +static void pmc_ssram_get_devid_pwrmbase(struct pmc_ssram_probe_cache *probe_cache, + void __iomem *ssram, unsigned int pmc_idx) { u64 pwrm_base; u16 devid; @@ -55,8 +79,45 @@ static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_i pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); devid = readw(ssram + SSRAM_DEVID_OFFSET); - pmc_ssram_telems[pmc_idx].devid = devid; - pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; + probe_cache->telems[pmc_idx].base_addr = pwrm_base; + probe_cache->telems[pmc_idx].devid = devid; +} + +static void pmc_ssram_publish_absent(unsigned int pmc_idx) +{ + /* + * Publish only the state without modifying base_addr and devid. This + * lets a reader that already observed PRESENT finish copying the + * previous values even if unbind concurrently publishes ABSENT. Readers + * that observe ABSENT return -ENODEV without accessing data. + */ + smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_ABSENT); +} + +static void pmc_ssram_publish_present(struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx) +{ + /* + * The devid and base_addr fields are read from hardware MMIO registers + * whose values are stable for a given PMC index. A reader that observed + * PRESENT from an earlier probe can safely copy them while a concurrent + * rebind republishes those fields because both probes read the same + * hardware values. + */ + pmc_ssram_telems[pmc_idx] = probe_cache->telems[pmc_idx]; + /* + * Publish base_addr and devid before publishing PRESENT. Pairs with the + * acquire load in the reader that consumes them after observing PRESENT. + */ + smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_PRESENT); +} + +static void pmc_ssram_mark_probing(unsigned long mask) +{ + unsigned long bit; + + for_each_set_bit(bit, &mask, MAX_NUM_PMC) + WRITE_ONCE(pmc_ssram_state[bit], PMC_SSRAM_PROBING); } static int @@ -96,11 +157,14 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem } static int -pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) +pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; u64 ssram_base; + int ret; ssram_base = pci_resource_start(pcidev, 0); tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); @@ -125,22 +189,38 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u3 ssram = no_free_ptr(tmp_ssram); } - pmc_ssram_get_devid_pwrmbase(ssram, pmc_idx); + pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx); /* Find and register and PMC telemetry entries */ - return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); -} - -static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) -{ - int ret; - - ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_MAIN, 0); + ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); if (ret) return ret; - pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); - pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + probe_cache->valid_mask |= BIT(pmc_idx); + + return 0; +} + +static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache) +{ + int ret; + + ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_MAIN, 0); + if (ret) + return ret; + + /* + * If MAIN PMC enumeration is successful but either IOE or PCH fail, + * don't fail probe as the MAIN PMC is still useful as it provides the + * global reset and slp_s0 counter access. Failed or missing secondary + * PMCs are left out of valid_mask and published as absent. + */ + pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_IOE, + SSRAM_IOE_OFFSET); + + pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_PCH, + SSRAM_PCH_OFFSET); return 0; } @@ -150,62 +230,106 @@ static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) * @pmc_idx: Index of the PMC * @pmc_ssram_telemetry: pmc_ssram_telemetry structure to store the PMC information * + * State flow per PMC index: + * - PMC_SSRAM_UNPROBED: Probe has not started for this index. + * - PMC_SSRAM_PROBING: Probe is currently discovering data for this index. + * - PMC_SSRAM_PRESENT: base_addr/devid were published and can be read. + * - PMC_SSRAM_ABSENT: No data is available for this index. + * + * Readers use an acquire load of the state. If state is PRESENT, reads of + * base_addr/devid are ordered after the state observation and pair with the + * writer's release-store when publishing PRESENT. + * * Return: * * 0 - Success * * -EAGAIN - Probe function has not finished yet. Try again. * * -EINVAL - Invalid pmc_idx - * * -ENODEV - PMC device is not available + * * -ENODEV - PMC device is not available (hardware absent or driver failed to initialize) */ int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, struct pmc_ssram_telemetry *pmc_ssram_telemetry) { - /* - * PMCs are discovered in probe function. If this function is called before - * probe function complete, the result would be invalid. Use device_probed - * variable to avoid this case. Return -EAGAIN to inform the consumer to call - * again later. - */ - if (!device_probed) - return -EAGAIN; + enum pmc_ssram_state state; - /* - * Memory barrier is used to ensure the correct read order between - * device_probed variable and PMC info. - */ - smp_rmb(); if (pmc_idx >= MAX_NUM_PMC) return -EINVAL; - if (!pmc_ssram_telems || !pmc_ssram_telems[pmc_idx].devid) + /* + * PMCs are discovered in probe function. If this function is called before + * probe function complete, the result would be invalid. Use per-PMC state + * to inform the consumer to call again later. + */ + state = smp_load_acquire(&pmc_ssram_state[pmc_idx]); + if (state == PMC_SSRAM_UNPROBED || state == PMC_SSRAM_PROBING) + return -EAGAIN; + + if (state == PMC_SSRAM_ABSENT) return -ENODEV; + /* + * Acquire semantics order reads of devid and base_addr after observing + * PRESENT and pair with the writer's release-store. + */ pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid; pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr; return 0; } EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); +static void pmc_ssram_publish_absent_mask(unsigned long mask) +{ + unsigned long bit; + + for_each_set_bit(bit, &mask, MAX_NUM_PMC) + pmc_ssram_publish_absent(bit); +} + +static void pmc_ssram_publish_telems(struct pmc_ssram_probe_cache *probe_cache, int ret) +{ + unsigned long bit; + + /* If probe failed, all owned indexes become absent for readers. */ + if (ret) { + pmc_ssram_publish_absent_mask(probe_cache->owned_mask); + return; + } + + /* Publish each owned index independently based on discovery result. */ + for_each_set_bit(bit, &probe_cache->owned_mask, MAX_NUM_PMC) { + if (probe_cache->valid_mask & BIT(bit)) + pmc_ssram_publish_present(probe_cache, bit); + else + pmc_ssram_publish_absent(bit); + } +} + static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { + struct pmc_ssram_probe_cache probe_cache = {}; + struct pmc_ssram_drvdata *drvdata; const struct ssram_type *ssram_type; enum resource_method method; int ret; - pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, - GFP_KERNEL); - if (!pmc_ssram_telems) { - ret = -ENOMEM; - goto probe_finish; - } - ssram_type = (const struct ssram_type *)id->driver_data; if (!ssram_type) { dev_dbg(&pcidev->dev, "missing driver data\n"); - ret = -EINVAL; - goto probe_finish; + return -EINVAL; } method = ssram_type->method; + if (method == RES_METHOD_PCI) + probe_cache.owned_mask = SSRAM_PCI_PMC_MASK; + else + return -EINVAL; + + pmc_ssram_mark_probing(probe_cache.owned_mask); + + drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + goto probe_finish; + } ret = pcim_enable_device(pcidev); if (ret) { @@ -214,20 +338,29 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de } if (method == RES_METHOD_PCI) - ret = pmc_ssram_telemetry_pci_init(pcidev); + ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache); else ret = -EINVAL; + if (!ret) { + drvdata->owned_mask = probe_cache.owned_mask; + pci_set_drvdata(pcidev, drvdata); + } + probe_finish: - /* - * Memory barrier is used to ensure the correct write order between PMC info - * and device_probed variable. - */ - smp_wmb(); - device_probed = true; + pmc_ssram_publish_telems(&probe_cache, ret); + return ret; } +static void pmc_ssram_telemetry_remove(struct pci_dev *pcidev) +{ + struct pmc_ssram_drvdata *drvdata = pci_get_drvdata(pcidev); + + if (drvdata) + pmc_ssram_publish_absent_mask(drvdata->owned_mask); +} + static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM), .driver_data = (kernel_ulong_t)&pci_main }, @@ -251,6 +384,7 @@ static struct pci_driver pmc_ssram_telemetry_driver = { .name = "intel_pmc_ssram_telemetry", .id_table = pmc_ssram_telemetry_pci_ids, .probe = pmc_ssram_telemetry_probe, + .remove = pmc_ssram_telemetry_remove, }; module_pci_driver(pmc_ssram_telemetry_driver); From 08b94937c78aaa144e3afff659c6112bffea6bf0 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:54 -0700 Subject: [PATCH 3664/5214] platform/x86/intel/pmc/ssram: Add ACPI discovery scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare the SSRAM telemetry driver for ACPI-based discovery by adding support for reading telemetry regions from ACPI _DSD properties. Add pmc_ssram_telemetry_acpi_init() to parse _DSD for telemetry discovery tables and register them with the Intel VSEC framework. Extend ssram_type with a p_index field to specify which PMC index each ACPI device owns (unlike PCI which discovers all three PMCs from one device). At this stage, no platform IDs are wired to use ACPI discovery - existing devices continue using the PCI path. Follow-on patches will add platform support. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Xi Pardee Signed-off-by: David E. Box Link: https://patch.msgid.link/54850d175993ee38aef99707f954492d24684dcc.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 3349f65ae7bd..10462fa7ebbf 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -31,14 +32,17 @@ DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) enum resource_method { RES_METHOD_PCI, + RES_METHOD_ACPI, }; struct ssram_type { enum resource_method method; + enum pmc_index p_index; }; static const struct ssram_type pci_main = { .method = RES_METHOD_PCI, + .p_index = PMC_IDX_MAIN, }; enum pmc_ssram_state { @@ -225,6 +229,73 @@ static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev, return 0; } +static int pmc_ssram_telemetry_get_pmc_acpi(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx) +{ + u64 ssram_base; + + ssram_base = pci_resource_start(pcidev, 0); + if (!ssram_base) + return -ENODEV; + + void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = + ioremap(ssram_base, SSRAM_HDR_SIZE); + if (!ssram) + return -ENOMEM; + + pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx); + probe_cache->valid_mask |= BIT(pmc_idx); + + return 0; +} + +static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + enum pmc_index index) +{ + struct intel_vsec_header header; + struct intel_vsec_header *headers[2] = { &header, NULL }; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct intel_vsec_platform_info info = { }; + union acpi_object *dsd; + acpi_handle handle; + acpi_status status; + int ret; + + handle = ACPI_HANDLE(&pcidev->dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_object(handle, "_DSD", NULL, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + + void *dsd_buf __free(pmc_acpi_free) = buf.pointer; + + dsd = pmc_find_telem_guid(dsd_buf); + if (!dsd) + return -ENODEV; + + acpi_disc_t disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header); + if (IS_ERR(disc)) + return PTR_ERR(disc); + + info.headers = headers; + info.caps = VSEC_CAP_TELEMETRY; + info.acpi_disc = disc; + info.src = INTEL_VSEC_DISC_ACPI; + + /* This is an ACPI companion device. PCI BAR will be used for base addr. */ + info.base_addr = 0; + + ret = intel_vsec_register(&pcidev->dev, &info); + if (ret) + return ret; + + return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index); +} + /** * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information * @pmc_idx: Index of the PMC @@ -309,6 +380,7 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de struct pmc_ssram_drvdata *drvdata; const struct ssram_type *ssram_type; enum resource_method method; + enum pmc_index index; int ret; ssram_type = (const struct ssram_type *)id->driver_data; @@ -317,9 +389,12 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de return -EINVAL; } + index = ssram_type->p_index; method = ssram_type->method; if (method == RES_METHOD_PCI) probe_cache.owned_mask = SSRAM_PCI_PMC_MASK; + else if (method == RES_METHOD_ACPI) + probe_cache.owned_mask = BIT(index); else return -EINVAL; @@ -339,6 +414,8 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de if (method == RES_METHOD_PCI) ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache); + else if (method == RES_METHOD_ACPI) + ret = pmc_ssram_telemetry_acpi_init(pcidev, &probe_cache, index); else ret = -EINVAL; @@ -389,6 +466,7 @@ static struct pci_driver pmc_ssram_telemetry_driver = { module_pci_driver(pmc_ssram_telemetry_driver); MODULE_IMPORT_NS("INTEL_VSEC"); +MODULE_IMPORT_NS("INTEL_PMC_CORE"); MODULE_AUTHOR("Xi Pardee "); MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver"); MODULE_LICENSE("GPL"); From 8a3607ece59fcf84cf4129bdf683cb09122e6b51 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:55 -0700 Subject: [PATCH 3665/5214] platform/x86/intel/pmc/ssram: Make PMT registration optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SSRAM telemetry driver extracts essential PMC device ID and power management base address information that intel_pmc_core depends on for core functionality. If PMT registration failure prevents this critical data from being available, intel_pmc_core operation would break entirely. Therefore, PMT registration failures must not block access to this data. Change the behavior to log a warning when PMT registration fails but continue with successful driver initialization, ensuring the primary telemetry data remains accessible to dependent drivers. Signed-off-by: David E. Box Link: https://patch.msgid.link/4f4c324977951f6082bf2218c8b911e1ae7e0a7b.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 10462fa7ebbf..9a8922045ab7 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -198,7 +198,7 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, /* Find and register and PMC telemetry entries */ ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); if (ret) - return ret; + dev_warn(&pcidev->dev, "could not register PMT\n"); probe_cache->valid_mask |= BIT(pmc_idx); @@ -291,7 +291,7 @@ static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev, ret = intel_vsec_register(&pcidev->dev, &info); if (ret) - return ret; + dev_warn(&pcidev->dev, "could not register PMT\n"); return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index); } From 50022e56dc89fbf1ec22826edf03dc2e5b9076cc Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:56 -0700 Subject: [PATCH 3666/5214] platform/x86/intel/pmc: Add NVL PCI IDs for SSRAM telemetry discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Nova Lake S PMC device IDs to enable binding of the SSRAM telemetry driver on NVL platforms, and map them to the ACPI-based discovery policy. Signed-off-by: David E. Box Link: https://patch.msgid.link/fc0e8bb00e2765fb7d145fef2ed1b0236b935c08.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 9a8922045ab7..8e280ec525a6 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -45,6 +45,16 @@ static const struct ssram_type pci_main = { .p_index = PMC_IDX_MAIN, }; +static const struct ssram_type acpi_main = { + .method = RES_METHOD_ACPI, + .p_index = PMC_IDX_MAIN, +}; + +static const struct ssram_type acpi_pch = { + .method = RES_METHOD_ACPI, + .p_index = PMC_IDX_PCH, +}; + enum pmc_ssram_state { PMC_SSRAM_UNPROBED = 0, PMC_SSRAM_PROBING, @@ -453,6 +463,12 @@ static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { .driver_data = (kernel_ulong_t)&pci_main }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN), .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDH), + .driver_data = (kernel_ulong_t)&acpi_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDS), + .driver_data = (kernel_ulong_t)&acpi_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCHS), + .driver_data = (kernel_ulong_t)&acpi_pch }, { } }; MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids); From 1ed40bd525c00d22af666016af9aef7167f8085f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 14 Jun 2026 16:16:59 -0700 Subject: [PATCH 3667/5214] apparmor: fix label can not be immediately before a declaration Fix error reported by kernel test robot security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement All errors (new ones prefixed by >>): security/apparmor/policy.c: In function 'aa_replace_profiles': >> security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement ssize_t udata_sz = udata->size; ^~~~~ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606150525.npax8WiH-lkp@intel.com/ Fixes: 7b42f95813dc9 ("apparmor: fix potential UAF in aa_replace_profiles") Signed-off-by: John Johansen --- security/apparmor/policy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b59e827747da..94b4a7e727cc 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1397,9 +1397,10 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + aa_put_ns(ns); + ssize_t udata_sz = udata->size; - aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); From e7ab91e2bf01b024691d6ce488546533943e7a6b Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 11 Jun 2026 07:51:40 +0200 Subject: [PATCH 3668/5214] accel/ivpu: fix HWS command queue leak on registration failure A command queue is considered valid and usable by the driver only when it has a doorbell ID assigned (db_id != 0), meaning both the FW cmdq creation and doorbell registration completed successfully. However, when either ivpu_register_db() or set_context_sched_properties() fails after ivpu_hws_cmdq_init() has already created the cmdq in FW, the command queue is left registered in FW while the driver treats it as uninitialized (db_id remains 0). On the next submission attempt the driver tries to register the same cmdq again, which fails because FW already has an entry for it. Fix by calling ivpu_jsm_hws_destroy_cmdq() on error paths to properly unwind FW state and allow subsequent registration attempts to succeed. Fixes: 465a3914b254 ("accel/ivpu: Add API for command queue create/destroy/submit") Reviewed-by: Andrzej Kacprowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260611055140.948684-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_job.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 521931d1f7fc..b24f31a8b567 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -208,9 +208,9 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id, priority); if (ret) - return ret; + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) @@ -281,10 +281,10 @@ static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq } ret = ivpu_register_db(file_priv, cmdq); - if (ret) - return ret; + if (ret && vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) From 2d36d3b451a94899db9c965adde15492ffe6027a Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 15 Jun 2026 15:01:15 +0800 Subject: [PATCH 3669/5214] x86/ioperm: Prevent NULL dereference on theoretical missing IO bitmap Outside the IOPL emulation path, the IO bitmap is always expected to be allocated when TIF_IO_BITMAP is set. The paranoid WARN_ON_ONCE() handles the case where the flag and the pointer got out of sync. In this theoretical scenario, which presumes some other bug in the code that triggers the WARN_ON_ONCe(), return early, instead of continuing and dereferencing a NULL pointer. [ mingo: Clarified the changelog. ] Signed-off-by: Li RongQing Signed-off-by: Ingo Molnar Reviewed-by: Sohil Mehta Cc: H. Peter Anvin Link: https://patch.msgid.link/20260615070115.4720-1-lirongqing@baidu.com --- arch/x86/kernel/process.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 4c718f8adc59..d5cd2177f18a 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -486,6 +486,7 @@ void native_tss_update_io_bitmap(void) if (WARN_ON_ONCE(!iobm)) { clear_thread_flag(TIF_IO_BITMAP); native_tss_invalidate_io_bitmap(); + return; } /* From 852fed2e8bfe195351fb0078ba7245d41154e7a5 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 7 May 2026 17:08:34 -0700 Subject: [PATCH 3670/5214] sparc: Disable compat support with LLD An LLVM=1 sparc64 allmodconfig enables COMPAT and then tries to build the 32-bit vDSO. That path cannot be linked with ld.lld: ld.lld: error: unknown emulation: elf32_sparc ld.lld does not support the 32-bit SPARC ELF emulation used for the compat vDSO, so keep COMPAT disabled when LLD is the linker. This avoids selecting an unsupported build path while leaving the existing GNU ld configuration unchanged. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Acked-by: Nathan Chancellor Reviewed-by: Andreas Larsson Link: https://lore.kernel.org/r/20260508000834.834824-1-rosenp@gmail.com Signed-off-by: Andreas Larsson --- arch/sparc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index fb53b21a8696..c3d72b433bcd 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -450,6 +450,7 @@ endmenu config COMPAT bool depends on SPARC64 + depends on !LD_IS_LLD default y select HAVE_UID16 select ARCH_WANT_OLD_COMPAT_IPC From 2416b30ec23fd23126b4747994c00d926d438d6f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 12 Jun 2026 11:15:03 -0700 Subject: [PATCH 3671/5214] sparc: Export mcount for Clang-built modules Clang emits calls to mcount for -pg on sparc64, while the existing ftrace support only exports the _mcount name. With FUNCTION_TRACER enabled, modules can therefore keep relocations against mcount and fail during modpost: ERROR: modpost: "mcount" [arch/sparc/kernel/chmc.ko] undefined! _mcount and mcount are aliases in arch/sparc/lib/mcount.S. Export the plain mcount alias as well so Clang-built modules can resolve their profiling call target. Assisted-by: Codex:GPT-5 Signed-off-by: Rosen Penev Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/include/asm/asm-prototypes.h | 1 + arch/sparc/lib/mcount.S | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/sparc/include/asm/asm-prototypes.h b/arch/sparc/include/asm/asm-prototypes.h index 270c51017212..a39a24c53216 100644 --- a/arch/sparc/include/asm/asm-prototypes.h +++ b/arch/sparc/include/asm/asm-prototypes.h @@ -26,6 +26,7 @@ void *memset(void *s, int c, size_t n); typedef int TItype __attribute__((mode(TI))); TItype __multi3(TItype a, TItype b); void _mcount(void); +void mcount(void); s64 __ashldi3(s64, int); s64 __lshrdi3(s64, int); diff --git a/arch/sparc/lib/mcount.S b/arch/sparc/lib/mcount.S index f7f7910eb41e..0309ba2c4712 100644 --- a/arch/sparc/lib/mcount.S +++ b/arch/sparc/lib/mcount.S @@ -21,6 +21,7 @@ EXPORT_SYMBOL(_mcount) .globl mcount .type mcount,#function + EXPORT_SYMBOL(mcount) _mcount: mcount: #ifdef CONFIG_FUNCTION_TRACER From 7eb475e8a738ee6fd1260aa59ddccb610fdd4300 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 13:58:50 +0800 Subject: [PATCH 3672/5214] sparc: led: avoid trimming a newline from empty writes led_proc_write() duplicates up to LED_MAX_LENGTH bytes with memdup_user_nul() and then unconditionally inspects buf[count - 1] to strip a trailing newline. A zero-length write therefore reads one byte before the duplicated buffer. The previous version rejected empty writes, but empty input already falls through to the existing default case and turns the LED off like any other unrecognized string. Preserve that behavior and only skip the newline trim when there is no input byte to inspect. Fixes: ee1858d3122d ("[SPARC]: Add sun4m LED driver.") Suggested-by: Andreas Larsson Signed-off-by: Pengpeng Hou Signed-off-by: Andreas Larsson --- arch/sparc/kernel/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c index f4fb82b019bb..9b53ac1fe533 100644 --- a/arch/sparc/kernel/led.c +++ b/arch/sparc/kernel/led.c @@ -78,7 +78,7 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer, return PTR_ERR(buf); /* work around \n when echo'ing into proc */ - if (buf[count - 1] == '\n') + if (count > 0 && buf[count - 1] == '\n') buf[count - 1] = '\0'; /* before we change anything we want to stop any running timers, From f09b59ae4414b0dc0929cf04cd8243157e0feab6 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:04 +0200 Subject: [PATCH 3673/5214] mm/slab: do not init any kfence objects on allocation When init (zeroing) on allocation is requested, for kmalloc() we generally have to zero the full object size even if a smaller size is requested, in order to provide krealloc()'s __GFP_ZERO guarantees. When we end up allocating a kfence object, kfence performs the zeroing on its own because it has its own redzone beyond the requested size. Thus slab_post_alloc_hook() has an 'init' parameter which has to be evaluated in all callers (via slab_want_init_on_alloc()) and should be false for kfence allocations. For kfence allocations in slab_alloc_node() this is achieved by subtly skipping over the slab_want_init_on_alloc() call. Other callers (i.e. kmem_cache_alloc_bulk_noprof()) however evaluate it unconditionally even if they do end up with a kfence allocation. This is only subtly not a problem, as those are not kmalloc allocations and thus the "requested size" equals s->object_size and thus it cannot interfere with kfence's redzone. There's just a unnecessary double zeroing (in both kfence and slab_post_alloc_hook()), but it's all very fragile and contradicts the comment in kfence_guarded_alloc(). Remove this subtlety and simplify the code by eliminating the init parameter from slab_post_alloc_hook() and make it call slab_want_init_on_alloc() itself. Instead add a is_kfence_address() check before performing the memset, which will start doing the right thing for all callers of slab_post_alloc_hook(). This potentially adds overhead of the is_kfence_address() check to allocation hotpath, but that one is designed to be as small as possible, and it's only evaluated if zeroing is about to happen. This means (aside from init_on_alloc hardening) only for __GFP_ZERO allocations, and the zeroing itself comes with an overhead likely larger than the added check. While at it, refactor the handling of evaluating when KASAN does the init instead of SLUB, with no intended functional changes. A non-functional change is that we don't pass kasan_init as true to kasan_slab_alloc() if kasan has no integrated init, but then the value is ignored anyway, so it's theoretically more correct. Thanks to Harry Yoo for the initial refactoring attempt, and for updated comments that are used here. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-2-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Reviewed-by: Hao Li Reviewed-by: Marco Elver Signed-off-by: Vlastimil Babka (SUSE) --- mm/kfence/core.c | 2 +- mm/slub.c | 60 ++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 655dc5ce3240..5e0b406924e9 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -500,7 +500,7 @@ static void *kfence_guarded_alloc(struct kmem_cache *cache, size_t size, gfp_t g /* * We check slab_want_init_on_alloc() ourselves, rather than letting - * SL*B do the initialization, as otherwise we might overwrite KFENCE's + * slab do the initialization, as otherwise it might overwrite KFENCE's * redzone. */ if (unlikely(slab_want_init_on_alloc(gfp, cache))) diff --git a/mm/slub.c b/mm/slub.c index e2ee8f1aaccf..d762cbe5d040 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4565,13 +4565,13 @@ struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags) static __fastpath_inline bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, - gfp_t flags, size_t size, void **p, bool init, + gfp_t flags, size_t size, void **p, unsigned int orig_size) { + bool init = slab_want_init_on_alloc(flags, s); unsigned int zero_size = s->object_size; - bool kasan_init = init; - size_t i; gfp_t init_flags = flags & gfp_allowed_mask; + bool kasan_init = false; /* * For kmalloc object, the allocated size (object_size) can be larger @@ -4588,28 +4588,33 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, zero_size = orig_size; /* - * When slab_debug is enabled, avoid memory initialization integrated - * into KASAN and instead zero out the memory via the memset below with - * the proper size. Otherwise, KASAN might overwrite SLUB redzones and - * cause false-positive reports. This does not lead to a performance + * ARM64 can set memory tags and zero the memory using a single + * instruction. Since HW_TAGS KASAN uses that while tagging the object, + * separate zeroing is unnecessary. + * + * However, KASAN never zeroes memory when slab_debug is enabled to + * avoid overwriting SLUB redzones. This does not lead to a performance * penalty on production builds, as slab_debug is not intended to be * enabled there. */ - if (__slub_debug_enabled()) - kasan_init = false; + if (kasan_has_integrated_init() && !__slub_debug_enabled()) { + kasan_init = init; + init = false; + } - /* - * As memory initialization might be integrated into KASAN, - * kasan_slab_alloc and initialization memset must be - * kept together to avoid discrepancies in behavior. - * - * As p[i] might get tagged, memset and kmemleak hook come after KASAN. - */ - for (i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { p[i] = kasan_slab_alloc(s, p[i], init_flags, kasan_init); - if (p[i] && init && (!kasan_init || - !kasan_has_integrated_init())) + + /* + * memset and hooks come after KASAN as p[i] might get tagged + * + * kfence zeroes the object instead of SLUB to avoid overwriting + * its own redzone starting at orig_size, which could happen + * with SLUB zeroing full s->object_size + */ + if (init && p[i] && !is_kfence_address(p[i])) memset(p[i], 0, zero_size); + if (gfpflags_allow_spinning(flags)) kmemleak_alloc_recursive(p[i], s->object_size, 1, s->flags, init_flags); @@ -4910,7 +4915,6 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) { void *object; - bool init = false; s = slab_pre_alloc_hook(s, gfpflags); if (unlikely(!s)) @@ -4926,16 +4930,13 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list object = __slab_alloc_node(s, gfpflags, node, addr, orig_size); maybe_wipe_obj_freeptr(s, object); - init = slab_want_init_on_alloc(gfpflags, s); out: /* - * When init equals 'true', like for kzalloc() family, only - * @orig_size bytes might be zeroed instead of s->object_size * In case this fails due to memcg_slab_post_alloc_hook(), * object is set to NULL */ - slab_post_alloc_hook(s, lru, gfpflags, 1, &object, init, orig_size); + slab_post_alloc_hook(s, lru, gfpflags, 1, &object, orig_size); return object; } @@ -5230,7 +5231,6 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache *s, gfp_t gfp, struct slab_sheaf *sheaf) { void *ret = NULL; - bool init; if (sheaf->size == 0) goto out; @@ -5240,10 +5240,8 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache *s, gfp_t gfp, if (likely(!ret)) ret = sheaf->objects[--sheaf->size]; - init = slab_want_init_on_alloc(gfp, s); - /* add __GFP_NOFAIL to force successful memcg charging */ - slab_post_alloc_hook(s, NULL, gfp | __GFP_NOFAIL, 1, &ret, init, s->object_size); + slab_post_alloc_hook(s, NULL, gfp | __GFP_NOFAIL, 1, &ret, s->object_size); out: trace_kmem_cache_alloc(_RET_IP_, ret, s, gfp, NUMA_NO_NODE); @@ -5423,8 +5421,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in success: maybe_wipe_obj_freeptr(s, ret); - slab_post_alloc_hook(s, NULL, alloc_gfp, 1, &ret, - slab_want_init_on_alloc(alloc_gfp, s), orig_size); + slab_post_alloc_hook(s, NULL, alloc_gfp, 1, &ret, orig_size); ret = kasan_kmalloc(s, ret, orig_size, alloc_gfp); return ret; @@ -7339,8 +7336,7 @@ bool kmem_cache_alloc_bulk_noprof(struct kmem_cache *s, gfp_t flags, out: /* memcg and kmem_cache debug support and memory initialization */ - return likely(slab_post_alloc_hook(s, NULL, flags, size, p, - slab_want_init_on_alloc(flags, s), s->object_size)); + return likely(slab_post_alloc_hook(s, NULL, flags, size, p, s->object_size)); } EXPORT_SYMBOL(kmem_cache_alloc_bulk_noprof); From 5fd1c77e97decf78573843f4ad1ea3ddcc1ea102 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:05 +0200 Subject: [PATCH 3674/5214] mm/slab: stop inlining __slab_alloc_node() With sheaves, this is no longer part of the allocation fastpath. For the same reason, also mark the call to it from slab_alloc_node() as unlikely(). Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-3-7190909db118@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index d762cbe5d040..8845e15cb152 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4519,8 +4519,8 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, return object; } -static __always_inline void *__slab_alloc_node(struct kmem_cache *s, - gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) +static void *__slab_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node, + unsigned long addr, size_t orig_size) { void *object; @@ -4926,7 +4926,7 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list object = alloc_from_pcs(s, gfpflags, node); - if (!object) + if (unlikely(!object)) object = __slab_alloc_node(s, gfpflags, node, addr, orig_size); maybe_wipe_obj_freeptr(s, object); From 4ff54ebe49244daaa0825fc6c7d126746b45a3de Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Jun 2026 15:01:22 -0700 Subject: [PATCH 3675/5214] MIPS: mm: remove comment referring to removed CONFIG_MIPS_CMP CMP support was removed in commit 7fb6f7b0af67 ("MIPS: Remove deprecated CONFIG_MIPS_CMP"), but a comment referring to it remained in arch/mips/mm/c-r4k.c. Remove it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/c-r4k.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 10413b6f6662..d8aadd3dc057 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -1757,11 +1757,6 @@ void r4k_cache_init(void) build_clear_page(); build_copy_page(); - /* - * We want to run CMP kernels on core with and without coherent - * caches. Therefore, do not use CONFIG_MIPS_CMP to decide whether - * or not to flush caches. - */ local_r4k___flush_cache_all(NULL); coherency_setup(); From 10fd82791fda5a52da0821eb6b50fbd09c8b3282 Mon Sep 17 00:00:00 2001 From: Catalin Iacob Date: Mon, 8 Jun 2026 17:29:17 +0300 Subject: [PATCH 3676/5214] mips: Remove remaining defconfig references to the pktcdvd driver Commit 1cea5180f2f8 ("block: remove pktcdvd driver") left behind some CONFIG_CONFIG_CDROM_PKTCDVD references in defconfigs. Remove them. Signed-off-by: Catalin Iacob Signed-off-by: Thomas Bogendoerfer --- arch/mips/configs/fuloong2e_defconfig | 1 - arch/mips/configs/ip22_defconfig | 1 - arch/mips/configs/ip27_defconfig | 1 - arch/mips/configs/ip30_defconfig | 1 - arch/mips/configs/jazz_defconfig | 1 - arch/mips/configs/malta_defconfig | 1 - arch/mips/configs/malta_kvm_defconfig | 1 - arch/mips/configs/maltaup_xpa_defconfig | 1 - arch/mips/configs/rm200_defconfig | 1 - arch/mips/configs/sb1250_swarm_defconfig | 1 - 10 files changed, 10 deletions(-) diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig index b6fe3c962464..840130a73992 100644 --- a/arch/mips/configs/fuloong2e_defconfig +++ b/arch/mips/configs/fuloong2e_defconfig @@ -89,7 +89,6 @@ CONFIG_MTD_CFI_STAA=m CONFIG_MTD_PHYSMAP=m CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=m -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index e123848f94ab..61f09cc9ac12 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -177,7 +177,6 @@ CONFIG_NET_ACT_SIMP=m CONFIG_NET_ACT_SKBEDIT=m CONFIG_RFKILL=m CONFIG_CONNECTOR=m -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_SCSI=y diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig index fea0ccee6948..60da9cf71b72 100644 --- a/arch/mips/configs/ip27_defconfig +++ b/arch/mips/configs/ip27_defconfig @@ -83,7 +83,6 @@ CONFIG_CFG80211=m CONFIG_MAC80211=m CONFIG_RFKILL=m CONFIG_BLK_DEV_LOOP=y -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y diff --git a/arch/mips/configs/ip30_defconfig b/arch/mips/configs/ip30_defconfig index 718f3060d9fa..5c2911ff9a87 100644 --- a/arch/mips/configs/ip30_defconfig +++ b/arch/mips/configs/ip30_defconfig @@ -77,7 +77,6 @@ CONFIG_NET_ACT_PEDIT=m CONFIG_NET_ACT_SKBEDIT=m # CONFIG_VGA_ARB is not set CONFIG_BLK_DEV_LOOP=y -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y diff --git a/arch/mips/configs/jazz_defconfig b/arch/mips/configs/jazz_defconfig index a790c2610fd3..dd3486b8d1fc 100644 --- a/arch/mips/configs/jazz_defconfig +++ b/arch/mips/configs/jazz_defconfig @@ -33,7 +33,6 @@ CONFIG_BLK_DEV_FD=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=m -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_SCSI=y diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 81704ec67f09..b10dac71f400 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -224,7 +224,6 @@ CONFIG_BLK_DEV_FD=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_BLK_DEV_SD=y diff --git a/arch/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig index 82a97f58bce1..bdd5d99884e3 100644 --- a/arch/mips/configs/malta_kvm_defconfig +++ b/arch/mips/configs/malta_kvm_defconfig @@ -228,7 +228,6 @@ CONFIG_BLK_DEV_FD=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_BLK_DEV_SD=y diff --git a/arch/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig index 0f9ef20744f9..523c0ff329ac 100644 --- a/arch/mips/configs/maltaup_xpa_defconfig +++ b/arch/mips/configs/maltaup_xpa_defconfig @@ -226,7 +226,6 @@ CONFIG_BLK_DEV_FD=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_BLK_DEV_SD=y diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index ad9fbd0cbb38..60054e54bc5a 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -177,7 +177,6 @@ CONFIG_PARIDE_ON26=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=m -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_SCSI=y diff --git a/arch/mips/configs/sb1250_swarm_defconfig b/arch/mips/configs/sb1250_swarm_defconfig index 4a25b8d3e507..a50a7c097542 100644 --- a/arch/mips/configs/sb1250_swarm_defconfig +++ b/arch/mips/configs/sb1250_swarm_defconfig @@ -43,7 +43,6 @@ CONFIG_FW_LOADER=m CONFIG_CONNECTOR=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=9220 -CONFIG_CDROM_PKTCDVD=m CONFIG_ATA_OVER_ETH=m CONFIG_RAID_ATTRS=m CONFIG_BLK_DEV_SD=y From 5ae158d31d02ff0419e9e127659f71a85f36a70e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:32:02 -0700 Subject: [PATCH 3677/5214] mips: dts: ar9132: fix wdt node name Fixes the following warning: $nodename:0: 'wdt@18060008' does not match '^(timer|watchdog)(@.*|-([0-9]|[1-9][0-9]+))?$' from schema $id: http://devicetree.org/schemas/watchdog/qca,ar7130-wdt.yaml# Signed-off-by: Rosen Penev Signed-off-by: Thomas Bogendoerfer --- arch/mips/boot/dts/qca/ar9132.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/boot/dts/qca/ar9132.dtsi b/arch/mips/boot/dts/qca/ar9132.dtsi index c1ca03a27b6c..b4a95b0b9b39 100644 --- a/arch/mips/boot/dts/qca/ar9132.dtsi +++ b/arch/mips/boot/dts/qca/ar9132.dtsi @@ -98,7 +98,7 @@ pll: pll-controller@18050000 { clock-output-names = "cpu", "ddr", "ahb"; }; - wdt: wdt@18060008 { + wdt: watchdog@18060008 { compatible = "qca,ar7130-wdt"; reg = <0x18060008 0x8>; From aeaa898780a4edb5802731e454caae6fb2ba33cb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 27 May 2026 15:25:04 -0700 Subject: [PATCH 3678/5214] MIPS: ath79: reduce ARCH_DMA_MINALIGN Currently, ath79 SoCs use the default ARCH_DMA_MINALIGN value of 128 bytes defined in mach-generic. This is excessive for these platforms and leads to significant memory waste in kmalloc. Override ARCH_DMA_MINALIGN to use L1_CACHE_BYTES, which is 32 bytes for ath79 SoCs. Signed-off-by: Rosen Penev Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/mach-ath79/kmalloc.h | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 arch/mips/include/asm/mach-ath79/kmalloc.h diff --git a/arch/mips/include/asm/mach-ath79/kmalloc.h b/arch/mips/include/asm/mach-ath79/kmalloc.h new file mode 100644 index 000000000000..954f5d6e0dd0 --- /dev/null +++ b/arch/mips/include/asm/mach-ath79/kmalloc.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MACH_ATH79_KMALLOC_H +#define __ASM_MACH_ATH79_KMALLOC_H + +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES + +#endif /* __ASM_MACH_ATH79_KMALLOC_H */ From 1b001b16bc88f3f7817e228acfd91ee01bdcfcce Mon Sep 17 00:00:00 2001 From: Yadan Fan Date: Mon, 25 May 2026 12:04:36 +0800 Subject: [PATCH 3679/5214] MIPS: mm: Fix out-of-bounds write in maar_res_walk() maar_res_walk() uses wi->num_cfg as the index into the fixed-size wi->cfg array, but checks whether the array is full only after it has filled the selected entry. If walk_system_ram_range() reports more than 16 memory ranges, the overflow call writes one struct maar_config past the end of the array before WARN_ON() prevents num_cfg from advancing. Move the full-array check before taking the array slot and return non-zero when the scratch array is full, so walk_system_ram_range() terminates the walk instead of invoking the callback for further ranges. Fixes: a5718fe8f70f ("MIPS: mm: Drop boot_mem_map") Signed-off-by: Yadan Fan Signed-off-by: Thomas Bogendoerfer --- arch/mips/mm/init.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 55b25e85122a..1c07ca84ee21 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -272,9 +272,15 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, void *data) { struct maar_walk_info *wi = data; - struct maar_config *cfg = &wi->cfg[wi->num_cfg]; + struct maar_config *cfg; unsigned int maar_align; + /* Ensure we don't overflow the cfg array */ + if (WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) + return -1; + + cfg = &wi->cfg[wi->num_cfg]; + /* MAAR registers hold physical addresses right shifted by 4 bits */ maar_align = BIT(MIPS_MAAR_ADDR_SHIFT + 4); @@ -283,9 +289,7 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, cfg->upper = ALIGN_DOWN(PFN_PHYS(start_pfn + nr_pages), maar_align) - 1; cfg->attrs = MIPS_MAAR_S; - /* Ensure we don't overflow the cfg array */ - if (!WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) - wi->num_cfg++; + wi->num_cfg++; return 0; } From 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Tue, 26 May 2026 10:16:51 -0400 Subject: [PATCH 3680/5214] mips: sched: Fix CPUMASK_OFFSTACK memory corruption This patch addresses a critical memory management flaw. When CONFIG_CPUMASK_OFFSTACK is enabled, cpumask_var_t is a pointer. Consequently, sizeof(new_mask) evaluates to the pointer size, causing copy_from_user() to clobber the mask pointer. Furthermore, the old logic performed copy_from_user() before allocating the mask. Fix this by allocating new_mask first. To handle variable-sized user masks correctly, use cpumask_size() to truncate overly large user masks or pad undersized masks with zeros before copying the data directly into the allocated buffer. Fixes: 295cbf6d63165 ("[MIPS] Move FPU affinity code into separate file.") Cc: stable@vger.kernel.org Signed-off-by: Aaron Tomlin Signed-off-by: Thomas Bogendoerfer --- arch/mips/kernel/mips-mt-fpaff.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c index 10172fc4f627..4fead87d2f43 100644 --- a/arch/mips/kernel/mips-mt-fpaff.c +++ b/arch/mips/kernel/mips-mt-fpaff.c @@ -71,11 +71,16 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, struct task_struct *p; int retval; - if (len < sizeof(new_mask)) - return -EINVAL; - - if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) - return -EFAULT; + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) + return -ENOMEM; + if (len < cpumask_size()) + cpumask_clear(new_mask); + else if (len > cpumask_size()) + len = cpumask_size(); + if (copy_from_user(new_mask, user_mask_ptr, len)) { + retval = -EFAULT; + goto out_free_new_mask; + } cpus_read_lock(); rcu_read_lock(); @@ -84,7 +89,8 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, if (!p) { rcu_read_unlock(); cpus_read_unlock(); - return -ESRCH; + retval = -ESRCH; + goto out_free_new_mask; } /* Prevent p going away */ @@ -95,13 +101,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, retval = -ENOMEM; goto out_put_task; } - if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) { - retval = -ENOMEM; - goto out_free_cpus_allowed; - } if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) { retval = -ENOMEM; - goto out_free_new_mask; + goto out_free_cpus_allowed; } if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) { retval = -EPERM; @@ -142,13 +144,13 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, } out_unlock: free_cpumask_var(effective_mask); -out_free_new_mask: - free_cpumask_var(new_mask); out_free_cpus_allowed: free_cpumask_var(cpus_allowed); out_put_task: put_task_struct(p); cpus_read_unlock(); +out_free_new_mask: + free_cpumask_var(new_mask); return retval; } From 8998c0904c72208fddc0b41565d6088a47b4720a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 4 Jun 2026 19:12:11 +0200 Subject: [PATCH 3681/5214] MIPS: kernel: proc: Use seq_putc() calls in show_cpuinfo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single characters should occasionally be put into a sequence. Thus use the corresponding function “seq_putc”. The source code was transformed by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Thomas Bogendoerfer --- arch/mips/kernel/proc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 8f0a0001540c..261b41ef3f5a 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -79,7 +79,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) for (i = 0; i < cpu_data[n].watch_reg_count; i++) seq_printf(m, "%s0x%04x", i ? ", " : "", cpu_data[n].watch_reg_masks[i]); - seq_puts(m, "]"); + seq_putc(m, ']'); } seq_puts(m, "\nisa\t\t\t:"); @@ -150,7 +150,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_puts(m, " loongson-ext"); if (cpu_has_loongson_ext2) seq_puts(m, " loongson-ext2"); - seq_puts(m, "\n"); + seq_putc(m, '\n'); if (cpu_has_mmips) { seq_printf(m, "micromips kernel\t: %s\n", @@ -301,7 +301,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) raw_notifier_call_chain(&proc_cpuinfo_chain, 0, &proc_cpuinfo_notifier_args); - seq_puts(m, "\n"); + seq_putc(m, '\n'); return 0; } From e1ac5f374253e65f0c8e0024a4828617b41e73fc Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 4 Jun 2026 19:15:15 +0200 Subject: [PATCH 3682/5214] MIPS: kernel: proc: Delete unnecessary braces in show_cpuinfo() Do not use curly brackets at one source code place where a single statement should be sufficient. Signed-off-by: Markus Elfring Signed-off-by: Thomas Bogendoerfer --- arch/mips/kernel/proc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 261b41ef3f5a..5401c679813a 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -152,10 +152,9 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_puts(m, " loongson-ext2"); seq_putc(m, '\n'); - if (cpu_has_mmips) { + if (cpu_has_mmips) seq_printf(m, "micromips kernel\t: %s\n", str_yes_no(read_c0_config3() & MIPS_CONF3_ISA_OE)); - } seq_puts(m, "Options implemented\t:"); if (cpu_has_tlb) From 9f3f3bdc6d9dac1a5a8262ee7ad0f2ff1527a7e7 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 8 Jun 2026 09:37:29 +0000 Subject: [PATCH 3683/5214] MIPS: smp: report dying CPU to RCU in stop_this_cpu() smp_send_stop() parks all secondary CPUs in stop_this_cpu(). The function marks the CPU offline for the scheduler via set_cpu_online(false) but never informs RCU, so RCU keeps expecting a quiescent state from CPUs that are now spinning forever with interrupts disabled. As long as nothing waits for an RCU grace period after smp_send_stop() this is harmless, which is why it went unnoticed. Since commit 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") however, irq_work_sync() calls synchronize_rcu() on architectures without an irq_work self-IPI, i.e. where arch_irq_work_has_interrupt() returns false. That is the asm-generic default used by MIPS. Any irq_work_sync() issued in the reboot/shutdown path after smp_send_stop() then blocks on a grace period that can never complete, hanging the reboot: WARNING: CPU: 0 PID: 15 at kernel/irq_work.c:144 irq_work_queue_on ... rcu: INFO: rcu_sched detected stalls on CPUs/tasks: rcu: Offline CPU 1 blocking current GP. rcu: Offline CPU 2 blocking current GP. rcu: Offline CPU 3 blocking current GP. This issue was noticed on several Realtek MIPS switch SoCs (MIPS interAptiv) and came up during kernel bump downstream in OpenWrt from 6.18.33 to 6.18.34, after the backport of the patch to the 6.18 stable branch. The patch also has been backported all the way back to 6.1. Call rcutree_report_cpu_dead() once interrupts are disabled, mirroring the generic CPU-hotplug offline path, so RCU stops waiting on the parked CPUs and grace periods can still complete. MIPS shuts down all CPUs here without going through the CPU-hotplug mechanism, so this report is not otherwise issued. Reporting a dying CPU to RCU outside the regular hotplug offline path is not unprecedented: arm64 does the same in cpu_die_early(). There it is an exception for a CPU that was coming online and is aborting bringup, rather than the default shutdown action as on MIPS. Fixes: 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") CC: stable@vger.kernel.org Signed-off-by: Jonas Jelonek Signed-off-by: Thomas Bogendoerfer --- arch/mips/kernel/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 4868e79f3b30..0f28b4a62e72 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -422,6 +423,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (1); } From 315b21cf81780acf961a5bc9eaf979003c1bf8c4 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 9 Jun 2026 18:31:21 -0700 Subject: [PATCH 3684/5214] MIPS: VDSO: Avoid including .got in dynamic segment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit 2db1ec80dfd5 ("MIPS: VDSO: Fold MIPS_DISABLE_VDSO into MIPS_GENERIC_GETTIMEOFDAY"), building ARCH=mips allnoconfig with LLVM=1 shows some warnings from llvm-readelf while checking the VDSO for dynamic relocations: llvm-readelf: warning: 'arch/mips/vdso/vdso.so.dbg.raw': invalid PT_DYNAMIC size (0xa4) llvm-readelf: warning: 'arch/mips/vdso/vdso.so.dbg.raw': PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used The blamed commit alters the link order of objects into vdso.so.raw, placing vgettimeofday.o after sigreturn.o. This ultimately results in the .text section shrinking slightly in size, which in turn changes the offset of the .dynamic section. - [ 9] .text PROGBITS 000002f0 0002f0 000930 00 AX 0 0 16 - [10] .dynamic DYNAMIC 00000c20 000c20 000090 08 A 5 0 4 + [ 9] .text PROGBITS 000002f0 0002f0 000924 00 AX 0 0 16 + [10] .dynamic DYNAMIC 00000c14 000c14 000090 08 A 5 0 4 Changing the offset of the .dynamic section causes the dynamic segment size to grow by the same amount, which triggers a warning in llvm-readelf because PT_DYNAMIC's p_filesz (0xa4) is no longer a multiple of its sh_entsize (8): - DYNAMIC 0x000c20 0x00000c20 0x00000c20 0x00098 0x00098 R 0x10 + DYNAMIC 0x000c14 0x00000c14 0x00000c14 0x000a4 0x000a4 R 0x10 The size of the dynamic segment was already incorrect before the blamed comment, as it should be 0x90 like the .dynamic section above (18 entries at 8 bytes per entry); it just so happens that 0x98 % 8 is 0, whereas 0xa4 % 8 is 4, so there was no warning. Looking at the section to segment mapping of the dynamic segment reveals that it includes the .got section, as it is implicitly placed after .dynamic by ld.lld's orphan section heuristics and inherits its segments from the linker script. [ 9] .text PROGBITS 000002f0 0002f0 000924 00 AX 0 0 16 [10] .dynamic DYNAMIC 00000c14 000c14 000090 08 A 5 0 4 [11] .got PROGBITS 00000cb0 000cb0 000008 00 WAp 0 0 16 Section to Segment mapping: Segment Sections... 00 .mips_abiflags 01 .reginfo 02 .mips_abiflags .reginfo .hash .dynsym .dynstr .gnu.version .gnu.version_d .note .text .dynamic .got 03 .dynamic .got 04 .note Explicitly describe the .got section in the MIPS VDSO linker script after .rodata, which switches back to the default text segment, resulting in a dynamic segment that is the exact size of the .dynamic section as expected with no other layout changes. - DYNAMIC 0x000c14 0x00000c14 0x00000c14 0x000a4 0x000a4 R 0x10 + DYNAMIC 0x000c14 0x00000c14 0x00000c14 0x00090 0x00090 R 0x4 - 03 .dynamic .got + 03 .dynamic Closes: https://github.com/ClangBuiltLinux/linux/issues/2166 Fixes: 2db1ec80dfd5 ("MIPS: VDSO: Fold MIPS_DISABLE_VDSO into MIPS_GENERIC_GETTIMEOFDAY") Signed-off-by: Nathan Chancellor Reviewed-by: Thomas Weißschuh Acked-by: Nick Desaulniers Signed-off-by: Thomas Bogendoerfer --- arch/mips/vdso/vdso.lds.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/vdso/vdso.lds.S b/arch/mips/vdso/vdso.lds.S index 5d08be3a6b85..187361c7a867 100644 --- a/arch/mips/vdso/vdso.lds.S +++ b/arch/mips/vdso/vdso.lds.S @@ -56,6 +56,7 @@ SECTIONS .dynamic : { *(.dynamic) } :text :dynamic .rodata : { *(.rodata*) } :text + .got : { *(.got) } _end = .; PROVIDE(end = .); From 5dcd5846f1f9ee637613b285332e9bdb90757eb5 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 8 Jun 2026 20:36:25 -0700 Subject: [PATCH 3685/5214] MIPS: lib: Remove '.hidden' for local symbols After a recent change in binutils that warns when local symbols have non-default visibility [1], there are a couple instances when building arch/mips: Assembler messages: {standard input}: Warning: local symbol `__memset' has non-default visibility Assembler messages: {standard input}: Warning: local symbol `__memcpy' has non-default visibility Remove the '.hidden' directives for these symbols to clear up the warnings, as they are pointless with a local symbol, which is by definition hidden. This results in no changes to these symbols in nm's output when assembled with various copies of binutils. Closes: https://lore.kernel.org/20260509122517.GA1108596@ax162/ Link: https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c4150acbda1b3ce0602f79cbb7700b39e577be7e [1] Signed-off-by: Nathan Chancellor Signed-off-by: Thomas Bogendoerfer --- arch/mips/lib/memcpy.S | 2 -- arch/mips/lib/memset.S | 2 -- 2 files changed, 4 deletions(-) diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index a4b4e805ff13..84f85aba6f4b 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -274,7 +274,6 @@ /* initialize __memcpy if this the first time we execute this macro */ .ifnotdef __memcpy .set __memcpy, 1 - .hidden __memcpy /* make sure it does not leak */ .endif /* @@ -538,7 +537,6 @@ .if __memcpy == 1 END(memcpy) .set __memcpy, 0 - .hidden __memcpy .endif .Ll_exc_copy\@: diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index 79405c32cc85..ab087406da66 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -89,7 +89,6 @@ /* Initialize __memset if this is the first time we call this macro */ .ifnotdef __memset .set __memset, 1 - .hidden __memset /* Make sure it does not leak */ .endif sltiu t0, a2, STORSIZE /* very small region? */ @@ -231,7 +230,6 @@ .if __memset == 1 END(memset) .set __memset, 0 - .hidden __memset .endif #ifdef CONFIG_CPU_NO_LOAD_STORE_LR From 6f25741b7565d7f82fc09947c981cae17535894d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 Apr 2026 17:56:19 +0200 Subject: [PATCH 3686/5214] mips: select legacy gpiolib interfaces where used A few old machines have not been converted away from the old-style gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY symbol so the code still works where it is needed but can be left out otherwise. This is the list of all gpio_request() calls in mips: arch/mips/alchemy/devboards/db1000.c: gpio_request(19, "sd0_cd"); arch/mips/alchemy/devboards/db1000.c: gpio_request(20, "sd1_cd"); arch/mips/alchemy/devboards/db1200.c: gpio_request(215, "otg-vbus"); arch/mips/bcm47xx/workarounds.c: err = gpio_request_one(usb_power, GPIOF_OUT_INIT_HIGH, "usb_power"); arch/mips/bcm63xx/boards/board_bcm963xx.c: gpio_request_one(board.ephy_reset_gpio, arch/mips/txx9/rbtx4927/setup.c: gpio_request(15, "sio-dtr"); Most of these should be easy enough to change to modern gpio descriptors or remove if they are no longer in use. Signed-off-by: Arnd Bergmann Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Thomas Bogendoerfer --- arch/mips/Kconfig | 5 +++++ arch/mips/alchemy/Kconfig | 1 - arch/mips/txx9/Kconfig | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 1cd8fc903387..2a568e384fd8 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -206,6 +206,8 @@ config MIPS_ALCHEMY select CSRC_R4K select IRQ_MIPS_CPU select DMA_NONCOHERENT # Au1000,1500,1100 aren't, rest is + select GPIOLIB + select GPIOLIB_LEGACY select MIPS_FIXUP_BIGPHYS_ADDR if PCI select SYS_HAS_CPU_MIPS32_R1 select SYS_SUPPORTS_32BIT_KERNEL @@ -307,6 +309,7 @@ config BCM47XX select SYS_HAS_EARLY_PRINTK select USE_GENERIC_EARLY_PRINTK_8250 select GPIOLIB + select GPIOLIB_LEGACY select LEDS_GPIO_REGISTER select BCM47XX_NVRAM select BCM47XX_SPROM @@ -330,6 +333,7 @@ config BCM63XX select SYS_HAS_CPU_BMIPS4380 select SWAP_IO_SPACE select GPIOLIB + select GPIOLIB_LEGACY select MIPS_L1_CACHE_SHIFT_4 select HAVE_LEGACY_CLK help @@ -999,6 +1003,7 @@ config MIKROTIK_RB532 select SWAP_IO_SPACE select BOOT_RAW select GPIOLIB + select GPIOLIB_LEGACY select MIPS_L1_CACHE_SHIFT_4 help Support the Mikrotik(tm) RouterBoard 532 series, diff --git a/arch/mips/alchemy/Kconfig b/arch/mips/alchemy/Kconfig index 6ca81e1bd35c..cf5ad52c0a0f 100644 --- a/arch/mips/alchemy/Kconfig +++ b/arch/mips/alchemy/Kconfig @@ -12,7 +12,6 @@ config MIPS_MTX1 config MIPS_DB1XXX bool "Alchemy DB1XXX / PB1XXX boards" - select GPIOLIB select HAVE_PCI select HAVE_PATA_PLATFORM select SYS_SUPPORTS_LITTLE_ENDIAN diff --git a/arch/mips/txx9/Kconfig b/arch/mips/txx9/Kconfig index 7335efa4d528..92b759a434c0 100644 --- a/arch/mips/txx9/Kconfig +++ b/arch/mips/txx9/Kconfig @@ -37,6 +37,7 @@ config SOC_TX4927 select IRQ_TXX9 select PCI_TX4927 select GPIO_TXX9 + select GPIOLIB_LEGACY config SOC_TX4938 bool From 05a5ff86a7f12c861e3516d3dc4d092ce620742d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:52 +0900 Subject: [PATCH 3687/5214] ntfs: fix incorrect size of symbolic link This patch fixes the issue where a symbolic link size is displayed as 0. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 2f2634baa285..efb34a5e94d9 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1199,6 +1199,9 @@ static int ntfs_read_locked_inode(struct inode *vi) else vi->i_blocks = ni->allocated_size >> 9; + if (S_ISLNK(vi->i_mode) && ni->target) + vi->i_size = strlen(ni->target); + ntfs_debug("Done."); return 0; unm_err_out: From 517cd625ad79b8bfa429092ad1536ee2dd477e68 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:53 +0900 Subject: [PATCH 3688/5214] ntfs: support following Windows native symlink with relative paths Make ntfs_make_symlink() parse native Windows symbolic link reparse payloads when the SYMLINK_FLAG_RELATIVE bit is set. Implement the following changes: * Add a dedicated on-disk layout definition for symbolic link reparse data. * validate the UTF-16 name ranges before decoding them. * convert the substitute name into the mount's NLS and normalize path separators. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 36 +++++++------ fs/ntfs/layout.h | 11 ++++ fs/ntfs/reparse.c | 129 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 148 insertions(+), 28 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index efb34a5e94d9..8894f33b46ca 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -863,8 +863,26 @@ static int ntfs_read_locked_inode(struct inode *vi) ntfs_ea_get_wsl_inode(vi, &dev, flags); } - if (m->flags & MFT_RECORD_IS_DIRECTORY) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + unsigned int mode; + + mode = ntfs_make_symlink(ni); + if (mode) + vi->i_mode |= mode; + else { + vi->i_mode &= ~S_IFLNK; + if (m->flags & MFT_RECORD_IS_DIRECTORY) + vi->i_mode |= S_IFDIR; + else + vi->i_mode |= S_IFREG; + } + } else if (m->flags & MFT_RECORD_IS_DIRECTORY) { vi->i_mode |= S_IFDIR; + } else { + vi->i_mode |= S_IFREG; + } + + if (S_ISDIR(vi->i_mode)) { /* * Apply the directory permissions mask set in the mount * options. @@ -874,18 +892,6 @@ static int ntfs_read_locked_inode(struct inode *vi) if (vi->i_nlink > 1) set_nlink(vi, 1); } else { - if (ni->flags & FILE_ATTR_REPARSE_POINT) { - unsigned int mode; - - mode = ntfs_make_symlink(ni); - if (mode) - vi->i_mode |= mode; - else { - vi->i_mode &= ~S_IFLNK; - vi->i_mode |= S_IFREG; - } - } else - vi->i_mode |= S_IFREG; /* Apply the file permissions mask set in the mount options. */ vi->i_mode &= ~vol->fmask; } @@ -894,7 +900,7 @@ static int ntfs_read_locked_inode(struct inode *vi) * If an attribute list is present we now have the attribute list value * in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes. */ - if (S_ISDIR(vi->i_mode)) { + if (m->flags & MFT_RECORD_IS_DIRECTORY) { struct index_root *ir; view_index_meta: @@ -1018,7 +1024,7 @@ static int ntfs_read_locked_inode(struct inode *vi) m = NULL; ctx = NULL; /* Setup the operations for this inode. */ - ntfs_set_vfs_operations(vi, S_IFDIR, 0); + ntfs_set_vfs_operations(vi, vi->i_mode, 0); if (ir->index.flags & LARGE_INDEX) NInoSetIndexAllocPresent(ni); } else { diff --git a/fs/ntfs/layout.h b/fs/ntfs/layout.h index d94f914e830f..94af6efa04af 100644 --- a/fs/ntfs/layout.h +++ b/fs/ntfs/layout.h @@ -2267,6 +2267,8 @@ enum { IO_REPARSE_PLUGIN_SELECT = cpu_to_le32(0xffff0fff), }; +#define SYMLINK_FLAG_RELATIVE 1 + /* * struct reparse_point - $REPARSE_POINT attribute content (0xc0)\ * @@ -2287,6 +2289,15 @@ struct reparse_point { u8 reparse_data[]; } __packed; +struct symlink_reparse_data { + __le16 substitute_name_offset; + __le16 substitute_name_length; + __le16 print_name_offset; + __le16 print_name_length; + __le32 flags; + __le16 path_buffer[]; +} __packed; + /* * struct ea_information - $EA_INFORMATION attribute content (0xd0) * diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c index 74713716813f..4714196185d9 100644 --- a/fs/ntfs/reparse.c +++ b/fs/ntfs/reparse.c @@ -24,6 +24,47 @@ struct wsl_link_reparse_data { char link[]; }; +static bool reparse_name_is_valid(size_t size, size_t name_off, u16 len) +{ + if ((name_off | len) & 1) + return false; + + return name_off + len <= size; +} + +/* + * Windows-native reparse payloads store pathnames as UTF-16 strings with '\\' + * separators. Convert the on-disk UTF-16 target into the mount's NLS and + * normalize path separators. + */ +static int ntfs_reparse_target_to_nls(struct ntfs_volume *vol, + const __le16 *uname, u16 ulen, + char **target) +{ + int err, i; + + *target = NULL; + ulen >>= 1; + if (!ulen) + return -EINVAL; + + if (!uname[ulen - 1]) + ulen--; + + err = ntfs_ucstonls(vol, uname, ulen, (unsigned char **)target, 0); + if (err < 0) { + ntfs_attr_name_free((unsigned char **)target); + return err; + } + + for (i = 0; i < err; i++) { + if ((*target)[i] == '\\') + (*target)[i] = '/'; + } + + return 0; +} + /* Index entry in $Extend/$Reparse */ struct reparse_index { struct index_entry_header header; @@ -38,8 +79,10 @@ __le16 reparse_index_name[] = {cpu_to_le16('$'), cpu_to_le16('R'), 0}; * Check if the reparse point attribute buffer is valid. * Returns true if valid, false otherwise. */ -static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, - const struct reparse_point *reparse_attr, size_t size) +static bool valid_reparse_buffer(struct ntfs_inode *ni, + const struct reparse_point *reparse_attr, + size_t size, + size_t payload_min_len) { size_t expected; @@ -50,6 +93,11 @@ static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, if (size < sizeof(struct reparse_point)) return false; + /* The payload must contain the fixed fields for the current tag. */ + if (payload_min_len && + le16_to_cpu(reparse_attr->reparse_data_length) < payload_min_len) + return false; + /* Reserved zero tag is invalid */ if (reparse_attr->reparse_tag == IO_REPARSE_TAG_RESERVED_ZERO) return false; @@ -79,24 +127,57 @@ static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, static bool valid_reparse_data(struct ntfs_inode *ni, const struct reparse_point *reparse_attr, size_t size) { - const struct wsl_link_reparse_data *wsl_reparse_data = - (const struct wsl_link_reparse_data *)reparse_attr->reparse_data; - unsigned int data_len = le16_to_cpu(reparse_attr->reparse_data_length); - - if (ntfs_is_valid_reparse_buffer(ni, reparse_attr, size) == false) + if (size < sizeof(*reparse_attr)) return false; switch (reparse_attr->reparse_tag) { - case IO_REPARSE_TAG_LX_SYMLINK: - if (data_len <= sizeof(wsl_reparse_data->type) || - wsl_reparse_data->type != cpu_to_le32(2)) + case IO_REPARSE_TAG_SYMLINK: + { + struct symlink_reparse_data *data; + size_t data_offs; + + if (!valid_reparse_buffer(ni, reparse_attr, size, + sizeof(*data))) + return false; + + data = (struct symlink_reparse_data *)reparse_attr->reparse_data; + data_offs = offsetof(struct reparse_point, reparse_data) + + offsetof(struct symlink_reparse_data, path_buffer); + + if (!reparse_name_is_valid(size, + data_offs + + le16_to_cpu(data->substitute_name_offset), + le16_to_cpu(data->substitute_name_length)) || + !reparse_name_is_valid(size, + data_offs + + le16_to_cpu(data->print_name_offset), + le16_to_cpu(data->print_name_length))) return false; break; + } + case IO_REPARSE_TAG_LX_SYMLINK: + { + struct wsl_link_reparse_data *data; + + if (!valid_reparse_buffer(ni, reparse_attr, size, + sizeof(*data))) + return false; + + data = (struct wsl_link_reparse_data *)reparse_attr->reparse_data; + + if (le16_to_cpu(reparse_attr->reparse_data_length) <= sizeof(data->type) || + data->type != cpu_to_le32(2)) + return false; + break; + } case IO_REPARSE_TAG_AF_UNIX: case IO_REPARSE_TAG_LX_FIFO: case IO_REPARSE_TAG_LX_CHR: case IO_REPARSE_TAG_LX_BLK: - if (data_len || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) + if (!valid_reparse_buffer(ni, reparse_attr, size, 0)) + return false; + if (le16_to_cpu(reparse_attr->reparse_data_length) || + !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) return false; } @@ -134,16 +215,38 @@ static unsigned int ntfs_reparse_tag_mode(struct reparse_point *reparse_attr) unsigned int ntfs_make_symlink(struct ntfs_inode *ni) { s64 attr_size = 0; + int err; unsigned int lth; struct reparse_point *reparse_attr; struct wsl_link_reparse_data *wsl_link_data; unsigned int mode = 0; + kvfree(ni->target); + ni->target = NULL; + reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0, &attr_size); - if (reparse_attr && attr_size && + if (reparse_attr && valid_reparse_data(ni, reparse_attr, attr_size)) { switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_SYMLINK: + { + struct symlink_reparse_data *data = + (struct symlink_reparse_data *)reparse_attr->reparse_data; + const __le16 *name = (const __le16 *)((u8 *)data->path_buffer + + le16_to_cpu(data->substitute_name_offset)); + + mode = ntfs_reparse_tag_mode(reparse_attr); + if (!(data->flags & cpu_to_le32(SYMLINK_FLAG_RELATIVE))) + break; + + err = ntfs_reparse_target_to_nls(ni->vol, name, + le16_to_cpu(data->substitute_name_length), + &ni->target); + if (err < 0) + mode = 0; + break; + } case IO_REPARSE_TAG_LX_SYMLINK: wsl_link_data = (struct wsl_link_reparse_data *)reparse_attr->reparse_data; @@ -184,7 +287,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi), AT_REPARSE_POINT, NULL, 0, &attr_size); - if (reparse_attr && attr_size) { + if (reparse_attr && attr_size >= sizeof(*reparse_attr)) { switch (reparse_attr->reparse_tag) { case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_LX_SYMLINK: From 2b58f93a131373117cfea02843f69a02b67a6664 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:54 +0900 Subject: [PATCH 3689/5214] ntfs: support following Windows native symlink with absolute paths Extend reparse-point handling beyond relative symlinks so NTFS can expose the Windows absolute forms used by non-relative symbolic links and junctions. * Store the reparse tag and symlink flags in the inode. * Validate junction payloads, and parse targets from substitute_name. * Add function to rewrite supported Windows absolute path into Linux path relative to the mounted NTFS volume. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/file.c | 22 +++++- fs/ntfs/inode.c | 2 + fs/ntfs/inode.h | 2 + fs/ntfs/layout.h | 8 ++ fs/ntfs/reparse.c | 194 ++++++++++++++++++++++++++++++++++++++++++---- fs/ntfs/reparse.h | 2 + 6 files changed, 212 insertions(+), 18 deletions(-) diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 264cf8404385..6b0dfc56577b 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -675,10 +675,28 @@ static int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, static const char *ntfs_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { - if (!NTFS_I(inode)->target) + struct ntfs_inode *ni = NTFS_I(inode); + char *target; + int err; + + if (!dentry) + return ERR_PTR(-ECHILD); + + if (!ni->target) return ERR_PTR(-EINVAL); - return NTFS_I(inode)->target; + if (ni->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT || + (ni->reparse_tag == IO_REPARSE_TAG_SYMLINK && + !(ni->reparse_flags & cpu_to_le32(SYMLINK_FLAG_RELATIVE)))) { + err = ntfs_translate_symlink_path(dentry, ni->target, &target); + if (err < 0) + return ERR_PTR(err); + + set_delayed_call(done, kfree_link, target); + return target; + } + + return ni->target; } static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 8894f33b46ca..07ca799a8f9a 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -488,6 +488,8 @@ void __ntfs_init_inode(struct super_block *sb, struct ntfs_inode *ni) ni->flags = 0; ni->mft_lcn[0] = LCN_RL_NOT_MAPPED; ni->mft_lcn_count = 0; + ni->reparse_tag = 0; + ni->reparse_flags = 0; ni->target = NULL; ni->i_dealloc_clusters = 0; } diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index 67942b97fac6..9aacd5787ffe 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -142,6 +142,8 @@ struct ntfs_inode { struct ntfs_inode *base_ntfs_ino; } ext; unsigned int i_dealloc_clusters; + __le32 reparse_tag; + __le32 reparse_flags; char *target; }; diff --git a/fs/ntfs/layout.h b/fs/ntfs/layout.h index 94af6efa04af..9438fd9b668e 100644 --- a/fs/ntfs/layout.h +++ b/fs/ntfs/layout.h @@ -2289,6 +2289,14 @@ struct reparse_point { u8 reparse_data[]; } __packed; +struct mount_point_reparse_data { + __le16 substitute_name_offset; + __le16 substitute_name_length; + __le16 print_name_offset; + __le16 print_name_length; + __le16 path_buffer[]; +} __packed; + struct symlink_reparse_data { __le16 substitute_name_offset; __le16 substitute_name_length; diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c index 4714196185d9..33e5a4198e9b 100644 --- a/fs/ntfs/reparse.c +++ b/fs/ntfs/reparse.c @@ -131,6 +131,29 @@ static bool valid_reparse_data(struct ntfs_inode *ni, return false; switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT: + { + struct mount_point_reparse_data *data; + size_t data_offs; + + if (!valid_reparse_buffer(ni, reparse_attr, size, sizeof(*data))) + return false; + + data = (struct mount_point_reparse_data *)reparse_attr->reparse_data; + data_offs = offsetof(struct reparse_point, reparse_data) + + offsetof(struct mount_point_reparse_data, path_buffer); + + if (!reparse_name_is_valid(size, + data_offs + + le16_to_cpu(data->substitute_name_offset), + le16_to_cpu(data->substitute_name_length)) || + !reparse_name_is_valid(size, + data_offs + + le16_to_cpu(data->print_name_offset), + le16_to_cpu(data->print_name_length))) + return false; + break; + } case IO_REPARSE_TAG_SYMLINK: { struct symlink_reparse_data *data; @@ -179,16 +202,22 @@ static bool valid_reparse_data(struct ntfs_inode *ni, if (le16_to_cpu(reparse_attr->reparse_data_length) || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) return false; + break; + default: + if (!valid_reparse_buffer(ni, reparse_attr, size, 0)) + return false; + break; } return true; } -static unsigned int ntfs_reparse_tag_mode(struct reparse_point *reparse_attr) +static unsigned int ntfs_reparse_tag_mode(__le32 reparse_tag) { unsigned int mode = 0; - switch (reparse_attr->reparse_tag) { + switch (reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT: case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_LX_SYMLINK: mode = S_IFLNK; @@ -218,17 +247,33 @@ unsigned int ntfs_make_symlink(struct ntfs_inode *ni) int err; unsigned int lth; struct reparse_point *reparse_attr; - struct wsl_link_reparse_data *wsl_link_data; unsigned int mode = 0; kvfree(ni->target); ni->target = NULL; + ni->reparse_tag = 0; + ni->reparse_flags = 0; reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0, &attr_size); if (reparse_attr && valid_reparse_data(ni, reparse_attr, attr_size)) { + err = -EINVAL; + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT: + { + struct mount_point_reparse_data *data = + (struct mount_point_reparse_data *)reparse_attr->reparse_data; + const __le16 *name = (const __le16 *)((u8 *)data->path_buffer + + le16_to_cpu(data->substitute_name_offset)); + + err = ntfs_reparse_target_to_nls(ni->vol, + name, + le16_to_cpu(data->substitute_name_length), + &ni->target); + break; + } case IO_REPARSE_TAG_SYMLINK: { struct symlink_reparse_data *data = @@ -236,33 +281,38 @@ unsigned int ntfs_make_symlink(struct ntfs_inode *ni) const __le16 *name = (const __le16 *)((u8 *)data->path_buffer + le16_to_cpu(data->substitute_name_offset)); - mode = ntfs_reparse_tag_mode(reparse_attr); - if (!(data->flags & cpu_to_le32(SYMLINK_FLAG_RELATIVE))) - break; - - err = ntfs_reparse_target_to_nls(ni->vol, name, + err = ntfs_reparse_target_to_nls(ni->vol, + name, le16_to_cpu(data->substitute_name_length), &ni->target); - if (err < 0) - mode = 0; + if (!err) + ni->reparse_flags = data->flags; break; } case IO_REPARSE_TAG_LX_SYMLINK: - wsl_link_data = + { + struct wsl_link_reparse_data *wsl_link_data = (struct wsl_link_reparse_data *)reparse_attr->reparse_data; + if (wsl_link_data->type == cpu_to_le32(2)) { lth = le16_to_cpu(reparse_attr->reparse_data_length) - - sizeof(wsl_link_data->type); + sizeof(wsl_link_data->type); ni->target = kvzalloc(lth + 1, GFP_NOFS); if (ni->target) { memcpy(ni->target, wsl_link_data->link, lth); ni->target[lth] = 0; - mode = ntfs_reparse_tag_mode(reparse_attr); + err = 0; } } break; + } default: - mode = ntfs_reparse_tag_mode(reparse_attr); + err = 0; + } + + if (!err) { + mode = ntfs_reparse_tag_mode(reparse_attr->reparse_tag); + ni->reparse_tag = reparse_attr->reparse_tag; } } else ni->flags &= ~FILE_ATTR_REPARSE_POINT; @@ -289,6 +339,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr if (reparse_attr && attr_size >= sizeof(*reparse_attr)) { switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT: case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_LX_SYMLINK: dt_type = DT_LNK; @@ -314,6 +365,109 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr return dt_type; } +/* + * ntfs_translate_symlink_path + * + * @dentry: dentry of the symlink/junction being resolved + * @target: NUL-terminated NLS target string with '\\' already normalized to '/' + * @translated: out parameter, set to a newly kmalloc'd relative path on success + * + * Windows junctions (IO_REPARSE_TAG_MOUNT_POINT) and non-relative symlinks + * (IO_REPARSE_TAG_SYMLINK without SYMLINK_FLAG_RELATIVE) store substitute + * names such as "/??/C:/foo", "//?/C:/foo", "/foo", or "C:/foo". Linux + * cannot continue pathname lookup from those syntaxes, so rewrite them as a + * path relative to the symlink's containing directory on this NTFS volume, + * anchored at the volume root via "../". + * + * Note: bind-mounted subtrees of the volume may resolve to unexpected + * locations because the computed "../" depth is relative to the NTFS volume + * root, not the bind-mounted subtree root. + * + * Return: 0 on success with *translated set to a newly allocated string the + * caller must kfree(); negative errno on failure. + */ +int ntfs_translate_symlink_path(struct dentry *dentry, const char *target, + char **translated) +{ + char *buf, *link_path, *out, *p; + const char *path, *tail; + unsigned int up_levels = 0; + size_t tail_len, out_len; + int err; + + if (!dentry || !target || !translated) + return -EINVAL; + + path = target; + /* reject UNC path. */ + if (path[0] == '/' && path[1] == '/' && + !(path[2] == '?' && path[3] == '/')) + return -EOPNOTSUPP; + + /* target starts with "/??/" or "//?/"? */ + if ((path[0] == '/' && path[1] == '?' && path[2] == '?' && path[3] == '/') || + (path[0] == '/' && path[1] == '/' && path[2] == '?' && path[3] == '/')) + path += 4; + + /* target must start with a drive character or '/'. */ + if (((path[0] >= 'A' && path[0] <= 'Z') || + (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':') { + if (path[2] && path[2] != '/') + return -EOPNOTSUPP; + tail = path + 2; + if (*tail == '/') + tail++; + } else if (*path == '/') { + tail = path + 1; + } else { + return -EOPNOTSUPP; + } + + tail_len = strlen(tail); + + buf = kmalloc(PATH_MAX, GFP_NOFS); + if (!buf) + return -ENOMEM; + + link_path = dentry_path_raw(dentry, buf, PATH_MAX); + if (IS_ERR(link_path)) { + err = PTR_ERR(link_path); + goto out; + } + + /* count '/' after the leading slash. */ + for (p = link_path + 1; *p; p++) + if (*p == '/') + up_levels++; + + /* build "./" + ("../" * up_levels) + tail. */ + out_len = 2 + up_levels * 3 + tail_len; + if (out_len >= PATH_MAX) { + err = -ENAMETOOLONG; + goto out; + } + + out = kmalloc(out_len + 1, GFP_NOFS); + if (!out) { + err = -ENOMEM; + goto out; + } + + memcpy(out, "./", 2); + p = out + 2; + while (up_levels--) { + memcpy(p, "../", 3); + p += 3; + } + memcpy(p, tail, tail_len + 1); + + *translated = out; + err = 0; +out: + kfree(buf); + return err; +} + /* * Set the index for new reparse data */ @@ -628,10 +782,14 @@ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, reparse_len); kvfree(reparse); - if (!err) + if (!err) { ni->target = utarget; - else + ni->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; + ni->reparse_flags = 0; + } else { kfree(utarget); + ni->target = NULL; + } } return err; } @@ -671,6 +829,10 @@ int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode) err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, reparse_len); kvfree(reparse); + if (!err) { + ni->reparse_tag = reparse_tag; + ni->reparse_flags = 0; + } } return err; diff --git a/fs/ntfs/reparse.h b/fs/ntfs/reparse.h index 28da40257f2a..ed7b93c359c1 100644 --- a/fs/ntfs/reparse.h +++ b/fs/ntfs/reparse.h @@ -11,6 +11,8 @@ extern __le16 reparse_index_name[]; unsigned int ntfs_make_symlink(struct ntfs_inode *ni); unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref); +int ntfs_translate_symlink_path(struct dentry *dentry, const char *target, + char **translated); int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, const __le16 *target, int target_len); int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode); From 115446b06fb725ba04e0f2ae5174029d2c60cad6 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:55 +0900 Subject: [PATCH 3690/5214] ntfs: add native_symlink mount option Because bind-mounted subtrees of the volume may resolve to unexpected locations, change converting junctions and non-relative symbolic links into paths relative to the NTFS volume to be allowed only if the native_symlink=rel mount option is specified. Add the native_symlink= mount option to configure how absolute symbolic links and mount points (junctions) are handled. The option accepts "raw" or "rel", with "raw" being the default. Under "raw", the absolute target path (ni->target) is returned as-is without translation. Under "rel", ntfs_translate_junction() is called to rewrite the absolute path as a relative path anchored at the volume root. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/file.c | 14 ++++++++------ fs/ntfs/inode.c | 4 ++++ fs/ntfs/super.c | 19 +++++++++++++++++++ fs/ntfs/volume.h | 3 +++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 6b0dfc56577b..6a7b638e523d 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -22,6 +22,7 @@ #include "ea.h" #include "iomap.h" #include "bitmap.h" +#include "volume.h" #include @@ -688,12 +689,13 @@ static const char *ntfs_get_link(struct dentry *dentry, struct inode *inode, if (ni->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT || (ni->reparse_tag == IO_REPARSE_TAG_SYMLINK && !(ni->reparse_flags & cpu_to_le32(SYMLINK_FLAG_RELATIVE)))) { - err = ntfs_translate_symlink_path(dentry, ni->target, &target); - if (err < 0) - return ERR_PTR(err); - - set_delayed_call(done, kfree_link, target); - return target; + if (NVolNativeSymlinkRel(ni->vol)) { + err = ntfs_translate_symlink_path(dentry, ni->target, &target); + if (err < 0) + return ERR_PTR(err); + set_delayed_call(done, kfree_link, target); + return target; + } } return ni->target; diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 07ca799a8f9a..76595f2e30ff 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2378,6 +2378,10 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root) seq_puts(sf, ",discard"); if (NVolDisableSparse(vol)) seq_puts(sf, ",disable_sparse"); + if (NVolNativeSymlinkRel(vol)) + seq_puts(sf, ",native_symlink=rel"); + else + seq_puts(sf, ",native_symlink=raw"); if (vol->sb->s_flags & SB_POSIXACL) seq_puts(sf, ",acl"); return 0; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index ca882e946a22..e032a247455c 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -43,6 +43,17 @@ static const struct constant_table ntfs_param_enums[] = { {} }; +enum { + NATIVE_SYMLINK_RAW, + NATIVE_SYMLINK_REL, +}; + +static const struct constant_table ntfs_native_symlink_enums[] = { + { "raw", NATIVE_SYMLINK_RAW }, + { "rel", NATIVE_SYMLINK_REL }, + {} +}; + enum { Opt_uid, Opt_gid, @@ -66,6 +77,7 @@ enum { Opt_acl, Opt_discard, Opt_nocase, + Opt_native_symlink, }; static const struct fs_parameter_spec ntfs_parameters[] = { @@ -91,6 +103,7 @@ static const struct fs_parameter_spec ntfs_parameters[] = { fsparam_flag("discard", Opt_discard), fsparam_flag("sparse", Opt_sparse), fsparam_flag("nocase", Opt_nocase), + fsparam_enum("native_symlink", Opt_native_symlink, ntfs_native_symlink_enums), {} }; @@ -215,6 +228,12 @@ static int ntfs_parse_param(struct fs_context *fc, struct fs_parameter *param) else NVolClearDisableSparse(vol); break; + case Opt_native_symlink: + if (result.uint_32 == NATIVE_SYMLINK_REL) + NVolSetNativeSymlinkRel(vol); + else + NVolClearNativeSymlinkRel(vol); + break; case Opt_sparse: break; default: diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index 3348394dbc0d..55298689a7bb 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -177,6 +177,7 @@ struct ntfs_volume { * * NV_Discard Issue discard/TRIM commands for freed clusters. * NV_DisableSparse Disable creation of sparse regions. + * NV_NativeSymlinkRel Translate absolute Windows reparse targets (native_symlink=rel). */ enum { NV_Errors, @@ -194,6 +195,7 @@ enum { NV_CheckWindowsNames, NV_Discard, NV_DisableSparse, + NV_NativeSymlinkRel, }; /* @@ -230,6 +232,7 @@ DEFINE_NVOL_BIT_OPS(HideDotFiles) DEFINE_NVOL_BIT_OPS(CheckWindowsNames) DEFINE_NVOL_BIT_OPS(Discard) DEFINE_NVOL_BIT_OPS(DisableSparse) +DEFINE_NVOL_BIT_OPS(NativeSymlinkRel) static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) { From 7266767f67e0fbf343b4ce67cb437fe4024fc55f Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:56 +0900 Subject: [PATCH 3691/5214] ntfs: clean up target name conversion for WSL symlinks WSL symlink target names are stored as narrow NLS/UTF-8 strings on disk. Converting the target name to Unicode in ntfs_symlink and converting it back to NLS in ntfs_reparse_set_wsl_symlink is redundant. Remove this conversion and pass the symname directly to the reparse data setter. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/namei.c | 17 ++------------ fs/ntfs/reparse.c | 56 +++++++++++++++++++++++------------------------ fs/ntfs/reparse.h | 2 +- 3 files changed, 30 insertions(+), 45 deletions(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 9c1c36acfad2..88c0b05dde3b 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -394,7 +394,7 @@ static int ntfs_sd_add_everyone(struct ntfs_inode *ni) static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *dir, __le16 *name, u8 name_len, mode_t mode, dev_t dev, - __le16 *target, int target_len) + const char *target, int target_len) { struct ntfs_inode *dir_ni = NTFS_I(dir); struct ntfs_volume *vol = dir_ni->vol; @@ -1409,9 +1409,7 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir, int err = 0; struct ntfs_inode *ni; __le16 *usrc; - __le16 *utarget; int usrc_len; - int utarget_len; int symlen = strlen(symname); if (NVolShutdown(vol)) @@ -1432,23 +1430,12 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir, goto out; } - utarget_len = ntfs_nlstoucs(vol, symname, symlen, &utarget, - PATH_MAX); - if (utarget_len < 0) { - if (utarget_len != -ENAMETOOLONG) - ntfs_error(sb, "Failed to convert target name to Unicode."); - err = -ENOMEM; - kmem_cache_free(ntfs_name_cache, usrc); - goto out; - } - if (!(vol->vol_flags & VOLUME_IS_DIRTY)) ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); ni = __ntfs_create(idmap, dir, usrc, usrc_len, S_IFLNK | 0777, 0, - utarget, utarget_len); + symname, symlen); kmem_cache_free(ntfs_name_cache, usrc); - kvfree(utarget); if (IS_ERR(ni)) { err = PTR_ERR(ni); goto out; diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c index 33e5a4198e9b..91ae0c75e275 100644 --- a/fs/ntfs/reparse.c +++ b/fs/ntfs/reparse.c @@ -753,43 +753,41 @@ static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t * Set reparse data for a WSL type symlink */ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, - const __le16 *target, int target_len) + const char *target, int target_len) { int err = 0; - int len; int reparse_len; - unsigned char *utarget = NULL; struct reparse_point *reparse; struct wsl_link_reparse_data *data; - len = ntfs_ucstonls(ni->vol, target, target_len, &utarget, 0); - if (len <= 0) - return -EINVAL; - - reparse_len = sizeof(struct reparse_point) + sizeof(data->type) + len; + reparse_len = sizeof(struct reparse_point) + sizeof(data->type) + + target_len; reparse = kvzalloc(reparse_len, GFP_NOFS); - if (!reparse) { - err = -ENOMEM; - kfree(utarget); - } else { - data = (struct wsl_link_reparse_data *)reparse->reparse_data; - reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; - reparse->reparse_data_length = - cpu_to_le16(sizeof(data->type) + len); - reparse->reserved = 0; - data->type = cpu_to_le32(2); - memcpy(data->link, utarget, len); - err = ntfs_set_ntfs_reparse_data(ni, - (char *)reparse, reparse_len); + if (!reparse) + return -ENOMEM; + + ni->target = kstrdup(target, GFP_NOFS); + if (!ni->target) { kvfree(reparse); - if (!err) { - ni->target = utarget; - ni->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; - ni->reparse_flags = 0; - } else { - kfree(utarget); - ni->target = NULL; - } + return -ENOMEM; + } + + data = (struct wsl_link_reparse_data *)reparse->reparse_data; + reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; + reparse->reparse_data_length = + cpu_to_le16(sizeof(data->type) + target_len); + reparse->reserved = 0; + data->type = cpu_to_le32(2); + memcpy(data->link, target, target_len); + err = ntfs_set_ntfs_reparse_data(ni, + (char *)reparse, reparse_len); + kvfree(reparse); + if (err) { + kfree(ni->target); + ni->target = NULL; + } else { + ni->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; + ni->reparse_flags = 0; } return err; } diff --git a/fs/ntfs/reparse.h b/fs/ntfs/reparse.h index ed7b93c359c1..e36557f29677 100644 --- a/fs/ntfs/reparse.h +++ b/fs/ntfs/reparse.h @@ -14,7 +14,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr int ntfs_translate_symlink_path(struct dentry *dentry, const char *target, char **translated); int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, - const __le16 *target, int target_len); + const char *target, int target_len); int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode); int ntfs_delete_reparse_index(struct ntfs_inode *ni); int ntfs_remove_ntfs_reparse_data(struct ntfs_inode *ni); From d8f1df2e133f203cae3f458cba44efa327b093d9 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:57 +0900 Subject: [PATCH 3692/5214] ntfs: support creating Windows native symlinks And introduce the symlink= mount option to configure how symbolic links are created. The option accepts "wsl" or "native", with "wsl" being the default. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- fs/ntfs/inode.c | 4 ++ fs/ntfs/namei.c | 5 +- fs/ntfs/reparse.c | 131 +++++++++++++++++++++++++++++++++++++++++++++- fs/ntfs/reparse.h | 2 + fs/ntfs/super.c | 19 +++++++ fs/ntfs/volume.h | 2 + 6 files changed, 160 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 76595f2e30ff..c2715521e562 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2382,6 +2382,10 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root) seq_puts(sf, ",native_symlink=rel"); else seq_puts(sf, ",native_symlink=raw"); + if (NVolSymlinkNative(vol)) + seq_puts(sf, ",symlink=native"); + else + seq_puts(sf, ",symlink=wsl"); if (vol->sb->s_flags & SB_POSIXACL) seq_puts(sf, ",acl"); return 0; diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 88c0b05dde3b..78c159519f9c 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -608,7 +608,10 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d goto err_out; if (S_ISLNK(mode)) { - err = ntfs_reparse_set_wsl_symlink(ni, target, target_len); + if (NVolSymlinkNative(vol)) + err = ntfs_reparse_set_native_symlink(ni, target, target_len); + else + err = ntfs_reparse_set_wsl_symlink(ni, target, target_len); if (!err) rollback_reparse = true; } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode) || diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c index 91ae0c75e275..fa523dc3691e 100644 --- a/fs/ntfs/reparse.c +++ b/fs/ntfs/reparse.c @@ -365,6 +365,13 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr return dt_type; } +static bool ntfs_is_drive_letter(const char *target) +{ + return ((target[0] >= 'A' && target[0] <= 'Z') || + (target[0] >= 'a' && target[0] <= 'z')) && + target[1] == ':'; +} + /* * ntfs_translate_symlink_path * @@ -410,8 +417,7 @@ int ntfs_translate_symlink_path(struct dentry *dentry, const char *target, path += 4; /* target must start with a drive character or '/'. */ - if (((path[0] >= 'A' && path[0] <= 'Z') || - (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':') { + if (ntfs_is_drive_letter(path)) { if (path[2] && path[2] != '/') return -EOPNOTSUPP; tail = path + 2; @@ -792,6 +798,127 @@ int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, return err; } +int ntfs_reparse_set_native_symlink(struct ntfs_inode *ni, + const char *target, int target_len) +{ + int err = 0; + bool is_absolute, prt_sub_shared = true; + char *sub_name = NULL; + char *prt_name = NULL; + __le16 *sub_name_utf16 = NULL; + __le16 *prt_name_utf16 = NULL; + int sub_len, prt_len; + int total_data_len, total_reparse_len; + struct reparse_point *reparse = NULL; + struct symlink_reparse_data *data; + int i; + + /* Determine if target is absolute (starts with drive letter like C:/ or C:\) */ + is_absolute = target_len > 2 && + ntfs_is_drive_letter(target) && + (target[2] == '/' || target[2] == '\\'); + + + /* Normalize and prepare NLS paths */ + prt_name = kstrdup(target, GFP_NOFS); + if (!prt_name) + return -ENOMEM; + + /* Replace '/' with '\' */ + for (i = 0; i < target_len; i++) { + if (prt_name[i] == '/') + prt_name[i] = '\\'; + } + + if (is_absolute) { + /* Prepend '\??\' to Substitutename */ + sub_name = kmalloc(target_len + 5, GFP_NOFS); + if (!sub_name) { + err = -ENOMEM; + goto out; + } + snprintf(sub_name, target_len + 5, "\\??\\%s", prt_name); + prt_sub_shared = false; + } else { + /* For relative symlinks (including absolute paths without drive letters), + * SubstituteName and PrintName are identical. + */ + sub_name = prt_name; + } + + /* Convert NLS paths to UTF-16 */ + sub_len = ntfs_nlstoucs(ni->vol, sub_name, strlen(sub_name), + &sub_name_utf16, PATH_MAX); + if (sub_len < 0) { + err = sub_len; + goto out; + } + + prt_len = ntfs_nlstoucs(ni->vol, prt_name, strlen(prt_name), + &prt_name_utf16, PATH_MAX); + if (prt_len < 0) { + err = prt_len; + goto out; + } + + /* Check for buffer size limits */ + total_data_len = sizeof(struct symlink_reparse_data) + + (sub_len + prt_len) * sizeof(__le16); + if (total_data_len > 16384) { /* 16KB max reparse tag size */ + err = -EFBIG; + goto out; + } + + total_reparse_len = sizeof(struct reparse_point) + total_data_len; + reparse = kvzalloc(total_reparse_len, GFP_NOFS); + if (!reparse) { + err = -ENOMEM; + goto out; + } + + /* Pack fields in reparse buffer */ + reparse->reparse_tag = IO_REPARSE_TAG_SYMLINK; + reparse->reparse_data_length = cpu_to_le16(total_data_len); + reparse->reserved = 0; + + data = (struct symlink_reparse_data *)reparse->reparse_data; + data->substitute_name_offset = 0; + data->substitute_name_length = cpu_to_le16(sub_len * sizeof(__le16)); + data->print_name_offset = data->substitute_name_length; + data->print_name_length = cpu_to_le16(prt_len * sizeof(__le16)); + data->flags = is_absolute ? 0 : cpu_to_le32(SYMLINK_FLAG_RELATIVE); + + /* Copy names to path_buffer */ + memcpy(data->path_buffer, sub_name_utf16, sub_len * sizeof(__le16)); + memcpy(data->path_buffer + sub_len, prt_name_utf16, prt_len * sizeof(__le16)); + + err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, total_reparse_len); + if (!err) { + int len = strlen(sub_name); + + for (i = 0; i < len; i++) { + if (sub_name[i] == '\\') + sub_name[i] = '/'; + } + ni->target = sub_name; + sub_name = NULL; + if (prt_sub_shared) + prt_name = NULL; + ni->reparse_tag = IO_REPARSE_TAG_SYMLINK; + ni->reparse_flags = is_absolute ? 0 : + cpu_to_le32(SYMLINK_FLAG_RELATIVE); + } + +out: + kfree(prt_name); + if (!prt_sub_shared) + kfree(sub_name); + kvfree(sub_name_utf16); + kvfree(prt_name_utf16); + kvfree(reparse); + return err; +} + /* * Set reparse data for a WSL special file other than a symlink * (socket, fifo, character or block device) diff --git a/fs/ntfs/reparse.h b/fs/ntfs/reparse.h index e36557f29677..c11a5bb7e6a5 100644 --- a/fs/ntfs/reparse.h +++ b/fs/ntfs/reparse.h @@ -15,6 +15,8 @@ int ntfs_translate_symlink_path(struct dentry *dentry, const char *target, char **translated); int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, const char *target, int target_len); +int ntfs_reparse_set_native_symlink(struct ntfs_inode *ni, + const char *symname, int symlen); int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode); int ntfs_delete_reparse_index(struct ntfs_inode *ni); int ntfs_remove_ntfs_reparse_data(struct ntfs_inode *ni); diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index e032a247455c..8abe7bee4c0d 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -54,6 +54,17 @@ static const struct constant_table ntfs_native_symlink_enums[] = { {} }; +enum { + SYMLINK_WSL, + SYMLINK_NATIVE, +}; + +static const struct constant_table ntfs_symlink_enums[] = { + { "wsl", SYMLINK_WSL }, + { "native", SYMLINK_NATIVE }, + {} +}; + enum { Opt_uid, Opt_gid, @@ -78,6 +89,7 @@ enum { Opt_discard, Opt_nocase, Opt_native_symlink, + Opt_symlink, }; static const struct fs_parameter_spec ntfs_parameters[] = { @@ -104,6 +116,7 @@ static const struct fs_parameter_spec ntfs_parameters[] = { fsparam_flag("sparse", Opt_sparse), fsparam_flag("nocase", Opt_nocase), fsparam_enum("native_symlink", Opt_native_symlink, ntfs_native_symlink_enums), + fsparam_enum("symlink", Opt_symlink, ntfs_symlink_enums), {} }; @@ -234,6 +247,12 @@ static int ntfs_parse_param(struct fs_context *fc, struct fs_parameter *param) else NVolClearNativeSymlinkRel(vol); break; + case Opt_symlink: + if (result.uint_32 == SYMLINK_NATIVE) + NVolSetSymlinkNative(vol); + else + NVolClearSymlinkNative(vol); + break; case Opt_sparse: break; default: diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index 55298689a7bb..65fd3908af26 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -196,6 +196,7 @@ enum { NV_Discard, NV_DisableSparse, NV_NativeSymlinkRel, + NV_SymlinkNative, }; /* @@ -233,6 +234,7 @@ DEFINE_NVOL_BIT_OPS(CheckWindowsNames) DEFINE_NVOL_BIT_OPS(Discard) DEFINE_NVOL_BIT_OPS(DisableSparse) DEFINE_NVOL_BIT_OPS(NativeSymlinkRel) +DEFINE_NVOL_BIT_OPS(SymlinkNative) static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) { From 3802a666f37255cc8ddc5647390e6c590b947065 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Mon, 15 Jun 2026 08:49:58 +0900 Subject: [PATCH 3693/5214] docs/fs/ntfs: add mount options to support Windows native symbolic links Introduce the "symlink=" and the "native_symlink=" mount options to configure the creation behavior of symbolic links and support creating Windows native symbolic links (reparse points with the IO_REPARSE_TAG_SYMLINK tag). Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon --- Documentation/filesystems/ntfs.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/filesystems/ntfs.rst b/Documentation/filesystems/ntfs.rst index 5c96b04a4d7a..4bfa392daec6 100644 --- a/Documentation/filesystems/ntfs.rst +++ b/Documentation/filesystems/ntfs.rst @@ -156,4 +156,17 @@ windows_names= Refuse creation/rename of files with characters or discard= Issue block device discard for clusters freed on file deletion/truncation to inform underlying storage. + +native_symlink=raw|rel Configure how absolute symbolic links and mount + points (junctions) are handled. Under "raw" + (default), the absolute target path is returned + as-is without translation. Under "rel", it is + rewritten as a relative path anchored at + the volume root. + +symlink=wsl|native Configure how symbolic links are created. Under + "wsl" (default), WSL (Windows Subsystem for + Linux) compatible symlinks are created. Under + "native", Windows native symbolic links are + created. ======================= ==================================================== From 90c0486a82e27393f9eaf3bb350f51a0bd38cb6b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 10 Jun 2026 17:15:49 +0300 Subject: [PATCH 3694/5214] drm/displayid: fix Tiled Display Topology ID size The Tiled Display Topology ID of a DisplayID Tiled Display Topology Data Block consists of three fields: - Tiled Display Manufacturer/Vendor ID Field (3 bytes) - Tiled Display Product ID Code Field (2 bytes) - Tiled Display Serial Number Field (4 bytes) i.e. a total of 9 bytes, not 8. The DisplayID Tiled Display Topology ID is used as the tile group identifier. Update both struct displayid_tiled_block topology_id member and struct drm_tile_group group_data member to full 9 bytes. The group data was missing the last byte of the serial number. I don't know whether there are known bug reports that might be linked to this, but it's plausible the last byte could be the differentiating part for the tile groups, and fewer tile groups might have been created than intended. Fixes: b49b55bd4fba ("drm/displayid: add displayid defines and edid extension (v2)") Fixes: 138f9ebb9755 ("drm: add tile_group support. (v3)") Cc: Dave Airlie Cc: stable@vger.kernel.org # v3.19+ Reviewed-by: Dave Airlie Link: https://patch.msgid.link/20260610141549.555605-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/drm_connector.c | 12 ++++++------ drivers/gpu/drm/drm_displayid_internal.h | 2 +- include/drm/drm_connector.h | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 47dc53c4a738..29634757c06d 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -3576,7 +3576,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); /** * drm_mode_get_tile_group - get a reference to an existing tile group * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Use the unique bytes to get a reference to an existing tile group. * @@ -3584,14 +3584,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); * tile group or NULL if not found. */ struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int id; mutex_lock(&dev->mode_config.idr_mutex); idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { - if (!memcmp(tg->group_data, topology, 8)) { + if (!memcmp(tg->group_data, topology_id, sizeof(tg->group_data))) { if (!kref_get_unless_zero(&tg->refcount)) tg = NULL; mutex_unlock(&dev->mode_config.idr_mutex); @@ -3606,7 +3606,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); /** * drm_mode_create_tile_group - create a tile group from a displayid description * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Create a tile group for the unique monitor, and get a unique * identifier for the tile group. @@ -3615,7 +3615,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); * new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int ret; @@ -3625,7 +3625,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, return NULL; kref_init(&tg->refcount); - memcpy(tg->group_data, topology, 8); + memcpy(tg->group_data, topology_id, sizeof(tg->group_data)); tg->dev = dev; mutex_lock(&dev->mode_config.idr_mutex); diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 5b1b32f73516..4590d6a3d821 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -109,7 +109,7 @@ struct displayid_tiled_block { u8 topo[3]; u8 tile_size[4]; u8 tile_pixel_bezel[5]; - u8 topology_id[8]; + u8 topology_id[9]; } __packed; struct displayid_detailed_timings_1 { diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f83f28cae207..877b5ca87e95 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -2608,13 +2608,13 @@ struct drm_tile_group { struct kref refcount; struct drm_device *dev; int id; - u8 group_data[8]; + u8 group_data[9]; }; struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); void drm_mode_put_tile_group(struct drm_device *dev, struct drm_tile_group *tg); From 5b2a3b1a98fb47c593144c2770e012d463952b70 Mon Sep 17 00:00:00 2001 From: Catalin Iacob Date: Mon, 8 Jun 2026 17:29:20 +0300 Subject: [PATCH 3695/5214] sparc: Remove remaining defconfig references to the pktcdvd driver Commit 1cea5180f2f8 ("block: remove pktcdvd driver") left behind some CONFIG_CONFIG_CDROM_PKTCDVD* references in defconfigs. Remove them. Signed-off-by: Catalin Iacob Reviewed-by: Andreas Larsson Signed-off-by: Andreas Larsson --- arch/sparc/configs/sparc64_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig index 632081a262ba..4abea39281cd 100644 --- a/arch/sparc/configs/sparc64_defconfig +++ b/arch/sparc/configs/sparc64_defconfig @@ -60,8 +60,6 @@ CONFIG_CONNECTOR=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m -CONFIG_CDROM_PKTCDVD=m -CONFIG_CDROM_PKTCDVD_WCACHE=y CONFIG_ATA_OVER_ETH=m CONFIG_SUNVDC=m CONFIG_ATA=y From 97f902dd4c99f8d085da5343dc58ae4f6ce7bdd9 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Mon, 25 May 2026 21:46:01 +0530 Subject: [PATCH 3696/5214] powerpc/boot: Allow text relocations for pseries wrapper with binutils 2.46+ Binutils 2.46 changed the default linker behavior from '-z notext' to '-z text', which treats dynamic relocations in read-only segments as errors rather than warnings. This causes the pseries boot wrapper build to fail with: /usr/bin/ld.bfd: arch/powerpc/boot/wrapper.a(crt0.o): warning: relocation against `_platform_stack_top' in read-only section `.text' /usr/bin/ld.bfd: error: read-only segment has dynamic relocations The pseries wrapper uses '-pie' to create position-independent code. However, crt0.S contains a pointer to '_platform_stack_top' in the .text section, which requires a dynamic relocation at runtime. This creates DT_TEXTREL (text relocations), which were allowed by default in binutils 2.45 and earlier (via implicit '-z notext') but are now rejected by binutils 2.46+. Add '-z notext' linker flag to explicitly allow text relocations for the pseries platform, similar to what is already done for the epapr platform. This restores the previous behavior and allows the boot wrapper to build successfully with binutils 2.46+. Signed-off-by: Amit Machhiwal Tested-by: Anushree Mathur Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260525161601.32097-1-amachhiw@linux.ibm.com --- arch/powerpc/boot/wrapper | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 1efd1206fcab..25321ce262e8 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -262,6 +262,7 @@ pseries) if [ "$format" != "elf32ppc" ]; then link_address= pie=-pie + notext='-z notext' fi make_space=n ;; From 20dd3185d13865214ff25b0bf7b931e8d73be1ac Mon Sep 17 00:00:00 2001 From: David Timber Date: Sun, 12 Apr 2026 08:32:51 +0900 Subject: [PATCH 3697/5214] exfat: fix handling of damaged volume in exfat_create_upcase_table() When the size of the upcase table is set to zero in the dentry for any reason(e.g. corrupted media or misbehaving device), an integer overflow causes the module to loop indefinitely. If the size of the upcase table is read zero, do not attempt to load the table. Instead, fallback to loading the default upcase table. If the size of the upcase table is zero or no upcase table is found, raise exfat_fs_error() to mark the volume read-only. Signed-off-by: David Timber Signed-off-by: Namjae Jeon --- fs/exfat/nls.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c index 57db08a5271c..055447edcf9a 100644 --- a/fs/exfat/nls.c +++ b/fs/exfat/nls.c @@ -769,13 +769,18 @@ int exfat_create_upcase_table(struct super_block *sb) tbl_clu = le32_to_cpu(ep->dentry.upcase.start_clu); tbl_size = le64_to_cpu(ep->dentry.upcase.size); - - sector = exfat_cluster_to_sector(sbi, tbl_clu); - num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; - ret = exfat_load_upcase_table(sb, sector, num_sectors, - le32_to_cpu(ep->dentry.upcase.checksum)); - + if (tbl_size) { + sector = exfat_cluster_to_sector(sbi, tbl_clu); + num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; + ret = exfat_load_upcase_table(sb, sector, num_sectors, + le32_to_cpu(ep->dentry.upcase.checksum)); + } else { + exfat_fs_error(sb, + "bad upcase table size (0 bytes). Please run fsck"); + ret = -EINVAL; + } brelse(bh); + if (ret && ret != -EIO) { /* free memory from exfat_load_upcase_table call */ exfat_free_upcase_table(sbi); @@ -790,6 +795,8 @@ int exfat_create_upcase_table(struct super_block *sb) return -EIO; } + exfat_fs_error(sb, "no upcase table entry. Please run fsck"); + load_default: /* load default upcase table */ return exfat_load_default_upcase_table(sb); From 81e3a86030462824a67d697739cf3f387f4ba350 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:10 +0530 Subject: [PATCH 3698/5214] powerpc/perf: fix preempt count underflow in fsl_emb_pmu_del fsl_emb_pmu_del() unconditionally calls put_cpu_var(cpu_hw_events) at the 'out:' label, but only calls the matching get_cpu_var() after the 'i < 0' early-return check. When event->hw.idx is negative the function jumps to 'out:' without having taken get_cpu_var(), and the trailing put_cpu_var() then issues an unmatched preempt_enable(), underflowing preempt_count. On a CONFIG_PREEMPT=y kernel preempt_count would underflow and eventually present as a 'scheduling while atomic' BUG. Move put_cpu_var() to pair with get_cpu_var() so the percpu access is correctly bracketed and the 'out:' label only handles perf_pmu_enable. Fixes: a11106544f33 ("powerpc/perf: e500 support") Reviewed-by: Shrikanth Hegde Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-2-aboorvad@linux.ibm.com --- arch/powerpc/perf/core-fsl-emb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c index 7120ab20cbfe..02b5dd74c187 100644 --- a/arch/powerpc/perf/core-fsl-emb.c +++ b/arch/powerpc/perf/core-fsl-emb.c @@ -366,9 +366,10 @@ static void fsl_emb_pmu_del(struct perf_event *event, int flags) cpuhw->n_events--; + put_cpu_var(cpu_hw_events); + out: perf_pmu_enable(event->pmu); - put_cpu_var(cpu_hw_events); } static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) From 0ecd26e93e698c8327521910fc6296f5b84a4b92 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:11 +0530 Subject: [PATCH 3699/5214] powerpc/powernv: fix preempt count leak in pnv_kexec_wait_secondaries_down pnv_kexec_wait_secondaries_down() calls get_cpu() to obtain the current CPU id but never calls the matching put_cpu(), leaking one preempt_disable() nesting level on every invocation. In practice the imbalance does not trigger a visible splat because the kexec teardown path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. The function only needs the current CPU id, and this path runs with interrupts disabled and the CPU pinned, so the preempt_disable() side-effect of get_cpu() is unnecessary. Replace it with raw_smp_processor_id(). Fixes: 298b34d7d578 ("powerpc/powernv: Fix kexec races going back to OPAL") Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-3-aboorvad@linux.ibm.com --- arch/powerpc/platforms/powernv/setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4dbb47ddbdcc..06ed5e2aa265 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -396,7 +396,8 @@ static void pnv_kexec_wait_secondaries_down(void) { int my_cpu, i, notified = -1; - my_cpu = get_cpu(); + /* Called with interrupts disabled, so the CPU is pinned. */ + my_cpu = raw_smp_processor_id(); for_each_online_cpu(i) { uint8_t status; From 3f5f8ee9917cc2b9076ac533492d8a200edcabb8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 22 Apr 2026 11:58:44 -0400 Subject: [PATCH 3700/5214] exfat: fix potential use-after-free in exfat_find_dir_entry() In exfat_find_dir_entry(), the buffer_head obtained from exfat_get_dentry() is released with brelse(bh) before the fall-through TYPE_EXTEND branch reads the directory entry through ep (which points into bh->b_data): brelse(bh); if (entry_type == TYPE_EXTEND) { ... len = exfat_extract_uni_name(ep, entry_uniname); ... } After brelse() drops our reference, nothing guarantees that the underlying page backing bh->b_data remains valid for the subsequent exfat_extract_uni_name() read. This is the same pattern fixed in commit fc961522ddbd ("exfat: Fix potential use after free in exfat_load_upcase_table()"). Move brelse(bh) so it runs after ep is no longer dereferenced on each branch. Confirmed on QEMU x86_64 with CONFIG_KASAN=y + CONFIG_DEBUG_PAGEALLOC=y + CONFIG_PAGE_POISONING=y on linux-next, using a crafted exFAT image (long filename with same-hash collisions forcing the TYPE_EXTEND path). With a debug-only invalidate_bdev() inserted between brelse(bh) and the ep read to make the stale-deref window deterministic, the unpatched kernel faults: BUG: KASAN: use-after-free in exfat_find_dir_entry+0x133b/0x15a0 BUG: unable to handle page fault for address: ffff88801a5fa0c2 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI RIP: 0010:exfat_find_dir_entry+0x1188/0x15a0 With this patch applied, the same instrumented harness completes cleanly under the same sanitizer stack. I have not reproduced a crash on an uninstrumented kernel under ordinary reclaim; the instrumented A/B establishes the lifetime violation and that the patch closes it, not an unaided triggerability claim. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Namjae Jeon --- fs/exfat/dir.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index ac008ccaa97d..561fd2349218 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1027,12 +1027,12 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - brelse(bh); if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { + brelse(bh); step = DIRENT_STEP_FILE; continue; } @@ -1043,6 +1043,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); + brelse(bh); name_len += len; unichar = *(uniname+len); @@ -1061,6 +1062,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } + brelse(bh); if (entry_type & (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { if (step == DIRENT_STEP_SECD) { From 5c86f1c1f972761a04bf22f4c0618d1aa714185b Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:12 +0530 Subject: [PATCH 3701/5214] powerpc/kexec: fix double get_cpu() imbalance in kexec_prepare_cpus kexec_prepare_cpus_wait() calls get_cpu() internally to obtain the current CPU id. kexec_prepare_cpus() calls kexec_prepare_cpus_wait() twice -- once for KEXEC_STATE_IRQS_OFF and once for KEXEC_STATE_REAL_MODE -- but only issues a single put_cpu() at the end, leaving preempt_count elevated by one extra nesting level. In practice the imbalance does not trigger a 'scheduling while atomic' splat because the kexec path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. kexec_prepare_cpus() calls local_irq_disable()/hard_irq_disable() before invoking kexec_prepare_cpus_wait(), so the CPU is already pinned and the get_cpu()/put_cpu() preempt_disable() bracketing is unnecessary. Only the current CPU id is needed, so replace get_cpu() with raw_smp_processor_id() and drop the now-unneeded put_cpu(). Fixes: 1fc711f7ffb0 ("powerpc/kexec: Fix race in kexec shutdown") Signed-off-by: Aboorva Devarajan Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-4-aboorvad@linux.ibm.com --- arch/powerpc/kexec/core_64.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c index 825ab8a88f18..58c13a59b93b 100644 --- a/arch/powerpc/kexec/core_64.c +++ b/arch/powerpc/kexec/core_64.c @@ -169,7 +169,7 @@ static void kexec_prepare_cpus_wait(int wait_state) int my_cpu, i, notified=-1; hw_breakpoint_disable(); - my_cpu = get_cpu(); + my_cpu = raw_smp_processor_id(); /* Make sure each CPU has at least made it to the state we need. * * FIXME: There is a (slim) chance of a problem if not all of the CPUs @@ -267,8 +267,6 @@ static void kexec_prepare_cpus(void) /* after we tell the others to go down */ if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0, 0); - - put_cpu(); } #else /* ! SMP */ From 623f0aa1eca5c2a94ca1e4e5de719d062eac3b6c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 24 Apr 2026 22:29:06 +0900 Subject: [PATCH 3702/5214] exfat: simplify exfat_lookup() 1) d_splice_alias() handles ERR_PTR() for inode just fine 2) no need to even look for existing aliases in case of directory inodes; just punt to d_splice_alias(), it'll do the right thing 3) no need to bother with 'd_unhashed(alias)' case - d_find_alias() would've returned that only in case of a directory, and d_splice_alias() will handle that just fine on its own. 4) exfat_d_anon_disconn() is entirely pointless now - we only get to evaluating it in case dentry->d_parent == alias->d_parent and alias being a non-directory. But in that case IS_ROOT(alias) can't possibly be true - that would've reqiured alias == alias->d_parent, i.e alias == dentry->d_parent and dentry->d_parent is guaranteed to be a directory. So exfat_d_anon_disconn() would always return false when it's called, which makes && !exfat_d_anon_disconn(alias) a no-op. Signed-off-by: Al Viro Reviewed-by: Sungjong Seo Signed-off-by: Namjae Jeon --- fs/exfat/namei.c | 56 +++++++++++------------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 94002e43db08..833b31d0f955 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -705,71 +705,44 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, return 0; } -static int exfat_d_anon_disconn(struct dentry *dentry) -{ - return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); -} - static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct super_block *sb = dir->i_sb; - struct inode *inode; + struct inode *inode = NULL; struct dentry *alias; struct exfat_dir_entry info; int err; loff_t i_pos; - mode_t i_mode; mutex_lock(&EXFAT_SB(sb)->s_lock); err = exfat_find(dir, &dentry->d_name, &info); if (err) { - if (err == -ENOENT) { - inode = NULL; - goto out; - } - goto unlock; + if (unlikely(err != -ENOENT)) + inode = ERR_PTR(err); + goto out; } i_pos = exfat_make_i_pos(&info); inode = exfat_build_inode(sb, &info, i_pos); - err = PTR_ERR_OR_ZERO(inode); - if (err) - goto unlock; + if (IS_ERR(inode) || S_ISDIR(inode->i_mode)) + goto out; - i_mode = inode->i_mode; alias = d_find_alias(inode); /* * Checking "alias->d_parent == dentry->d_parent" to make sure * FS is not corrupted (especially double linked dir). */ - if (alias && alias->d_parent == dentry->d_parent && - !exfat_d_anon_disconn(alias)) { - + if (alias && alias->d_parent == dentry->d_parent) { /* - * Unhashed alias is able to exist because of revalidate() - * called by lookup_fast. You can easily make this status - * by calling create and lookup concurrently - * In such case, we reuse an alias instead of new dentry + * This inode has a hashed alias dentry with different + * name. This means, the user did ->lookup() by an + * another name (longname vs 8.3 alias of it) in past. + * + * Switch to new one for reason of locality if possible. */ - if (d_unhashed(alias)) { - WARN_ON(alias->d_name.hash_len != - dentry->d_name.hash_len); - exfat_info(sb, "rehashed a dentry(%p) in read lookup", - alias); - d_drop(dentry); - d_rehash(alias); - } else if (!S_ISDIR(i_mode)) { - /* - * This inode has non anonymous-DCACHE_DISCONNECTED - * dentry. This means, the user did ->lookup() by an - * another name (longname vs 8.3 alias of it) in past. - * - * Switch to new one for reason of locality if possible. - */ - d_move(alias, dentry); - } + d_move(alias, dentry); iput(inode); mutex_unlock(&EXFAT_SB(sb)->s_lock); return alias; @@ -781,9 +754,6 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, exfat_d_version_set(dentry, inode_query_iversion(dir)); return d_splice_alias(inode, dentry); -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return ERR_PTR(err); } /* remove an entry, BUT don't truncate */ From f07db38084dc45162d5fe2871ed72674ddc0f9ae Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 Apr 2026 16:42:45 +0900 Subject: [PATCH 3703/5214] exfat: replace unsafe macros with static inline functions The current exFAT driver relies on various macros for unit conversions between clusters, blocks, sectors, and directory entries. These macros are structurally unsafe as they lack type enforcement and are prone to potential integer overflows during bit-shift operations, especially on 64-bit architectures. Replace all arithmetic macros with static inline functions to provide strict type checking and explicit casting. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/balloc.c | 2 +- fs/exfat/dir.c | 48 ++++++++--------- fs/exfat/exfat_fs.h | 122 ++++++++++++++++++++++++++++++++------------ fs/exfat/fatent.c | 4 +- fs/exfat/file.c | 8 +-- fs/exfat/inode.c | 19 +++---- fs/exfat/namei.c | 26 +++++----- fs/exfat/super.c | 4 +- 8 files changed, 147 insertions(+), 86 deletions(-) diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 625f2f14d4fe..e66ebf899778 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -112,7 +112,7 @@ static int exfat_allocate_bitmap(struct super_block *sb, } if (exfat_test_bitmap_range(sb, sbi->map_clu, - EXFAT_B_TO_CLU_ROUND_UP(map_size, sbi)) == false) + exfat_bytes_to_cluster_round_up(sbi, map_size)) == false) goto err_out; return 0; diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 561fd2349218..f7f0b6a5bbcf 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -76,7 +76,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF; + unsigned int dentry = exfat_bytes_to_dentries(*cpos) & 0xFFFFFFFF; struct buffer_head *bh; /* check if the given file ID is opened */ @@ -84,13 +84,13 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent return -EPERM; exfat_chain_set(&dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(inode)), ei->flags); dentries_per_clu = sbi->dentries_per_clu; - max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, - (u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi)); + max_dentries = min(MAX_EXFAT_DENTRIES, + exfat_cluster_to_dentries(sbi, sbi->num_clusters)); - clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi); + clu_offset = exfat_dentries_to_cluster(sbi, dentry); exfat_chain_dup(&clu, &dir); if (clu.flags == ALLOC_FAT_CHAIN) { @@ -147,10 +147,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent dir_entry->dir = clu; brelse(bh); - ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi); + ei->hint_bmap.off = exfat_dentries_to_cluster(sbi, dentry); ei->hint_bmap.clu = clu.dir; - *cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext); + *cpos = exfat_dentries_to_bytes(dentry + 1 + num_ext); return 0; } @@ -160,7 +160,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent out: dir_entry->namebuf.lfn[0] = '\0'; - *cpos = EXFAT_DEN_TO_B(dentry); + *cpos = exfat_dentries_to_bytes(dentry); return 0; } @@ -465,7 +465,7 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode, return; exfat_chain_set(&dir, start_clu, - EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)), + exfat_bytes_to_cluster_round_up(EXFAT_SB(sb), size), flags); exfat_free_cluster(inode, &dir); } @@ -556,10 +556,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir unsigned int off, clu = 0; struct exfat_sb_info *sbi = EXFAT_SB(sb); - off = EXFAT_DEN_TO_B(entry); + off = exfat_dentries_to_bytes(entry); clu = p_dir->dir; - ret = exfat_cluster_walk(sb, &clu, EXFAT_B_TO_CLU(off, sbi), p_dir->flags); + ret = exfat_cluster_walk(sb, &clu, exfat_bytes_to_cluster(sbi, off), + p_dir->flags); if (ret) return ret; @@ -567,7 +568,7 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir exfat_fs_error(sb, "unexpected early break in cluster chain (clu : %u, len : %d)", p_dir->dir, - EXFAT_B_TO_CLU(off, sbi)); + exfat_bytes_to_cluster(sbi, off)); return -EIO; } @@ -577,13 +578,13 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir } /* byte offset in cluster */ - off = EXFAT_CLU_OFFSET(off, sbi); + off = exfat_cluster_offset(sbi, off); /* byte offset in sector */ - *offset = EXFAT_BLK_OFFSET(off, sb); + *offset = exfat_block_offset(sb, off); /* sector offset in cluster */ - *sector = EXFAT_B_TO_BLK(off, sb); + *sector = exfat_bytes_to_block(sb, off); *sector += exfat_cluster_to_sector(sbi, clu); return 0; } @@ -593,7 +594,7 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb, { struct exfat_sb_info *sbi = EXFAT_SB(sb); unsigned int sect_per_clus = sbi->sect_per_clus; - unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE); + unsigned int dentries_per_page = exfat_bytes_to_dentries(PAGE_SIZE); int off; sector_t sec; @@ -672,8 +673,8 @@ struct exfat_dentry *exfat_get_dentry_cached( struct exfat_entry_set_cache *es, int num) { int off = es->start_off + num * DENTRY_SIZE; - struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; - char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb); + struct buffer_head *bh = es->bh[exfat_bytes_to_block(es->sb, off)]; + char *p = bh->b_data + exfat_block_offset(es->sb, off); return (struct exfat_dentry *)p; } @@ -741,7 +742,7 @@ static int __exfat_get_dentry_set(struct exfat_entry_set_cache *es, es->num_entries = num_entries; - num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb); + num_bh = exfat_bytes_to_block_round_up(sb, off + num_entries * DENTRY_SIZE); if (num_bh > ARRAY_SIZE(es->__bh)) { es->bh = kmalloc_objs(*es->bh, num_bh, GFP_NOFS); if (!es->bh) { @@ -830,7 +831,7 @@ static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es) err_used_follow_unused: off = es->start_off + (i << DENTRY_SIZE_BITS); - bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; + bh = es->bh[exfat_bytes_to_block(es->sb, off)]; exfat_fs_error(es->sb, "in sector %lld, dentry %d should be unused, but 0x%x", @@ -839,7 +840,8 @@ static int exfat_validate_empty_dentry_set(struct exfat_entry_set_cache *es) return -EIO; count_skip_entries: - es->num_entries = EXFAT_B_TO_DEN(EXFAT_BLK_TO_B(es->num_bh, es->sb) - es->start_off); + es->num_entries = + exfat_bytes_to_dentries(exfat_block_to_bytes(es->sb, es->num_bh) - es->start_off); for (; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); if (IS_EXFAT_DELETED(ep->type)) @@ -892,7 +894,7 @@ static inline void exfat_set_empty_hint(struct exfat_inode_info *ei, { if (ei->hint_femp.eidx == EXFAT_HINT_NONE || ei->hint_femp.eidx > dentry) { - int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode)); + int total_entries = exfat_bytes_to_dentries(i_size_read(&ei->vfs_inode)); if (candi_empty->count == 0) { candi_empty->cur = *clu; @@ -1217,7 +1219,7 @@ static int exfat_get_volume_label_dentry(struct super_block *sb, es->bh = es->__bh; es->bh[0] = bh; es->num_bh = 1; - es->start_off = EXFAT_DEN_TO_B(i) % sb->s_blocksize; + es->start_off = exfat_dentries_to_bytes(i) % sb->s_blocksize; return 0; } diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index aff4dcd4e75a..ad8d6a48fe1f 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -84,38 +84,6 @@ enum { (min_t(blkcnt_t, (sb)->s_bdi->ra_pages, (sb)->s_bdi->io_pages) \ << (PAGE_SHIFT - (sb)->s_blocksize_bits)) -/* - * helpers for cluster size to byte conversion. - */ -#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \ - (((b - 1) >> (sbi)->cluster_size_bits) + 1) -#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1)) - -/* - * helpers for block size to byte conversion. - */ -#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \ - (((b - 1) >> (sb)->s_blocksize_bits) + 1) -#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1)) - -/* - * helpers for block size to dentry size conversion. - */ -#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS) -#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS) - -/* - * helpers for cluster size to dentry size conversion. - */ -#define EXFAT_CLU_TO_DEN(clu, sbi) \ - ((clu) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) -#define EXFAT_DEN_TO_CLU(dentry, sbi) \ - ((dentry) >> ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) - /* * helpers for fat entry. */ @@ -149,7 +117,7 @@ enum { * The 608 bytes are in 3 sectors at most (even 512 Byte sector). */ #define DIR_CACHE_SIZE \ - (DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1) + (DIV_ROUND_UP(ES_MAX_ENTRY_NUM << DENTRY_SIZE_BITS, SECTOR_SIZE) + 1) /* Superblock flags */ #define EXFAT_FLAGS_SHUTDOWN 1 @@ -432,6 +400,94 @@ static inline loff_t exfat_ondisk_size(const struct inode *inode) return ((loff_t)inode->i_blocks) << 9; } +/* + * helpers for cluster size to byte conversion. + */ +static inline loff_t exfat_cluster_to_bytes(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return (loff_t)nr_clusters << sbi->cluster_size_bits; +} + +static inline blkcnt_t exfat_cluster_to_sectors(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return (blkcnt_t)nr_clusters << (sbi->cluster_size_bits - 9); +} + +static inline u32 exfat_bytes_to_cluster(struct exfat_sb_info *sbi, loff_t size) +{ + return (u32)(size >> sbi->cluster_size_bits); +} + +static inline u32 exfat_bytes_to_cluster_round_up(struct exfat_sb_info *sbi, + loff_t size) +{ + if (size <= 0) + return 0; + return (u32)((size - 1) >> sbi->cluster_size_bits) + 1; +} + +static inline u32 exfat_cluster_offset(struct exfat_sb_info *sbi, loff_t off) +{ + return off & (sbi->cluster_size - 1); +} + +/* + * helpers for block size to byte conversion. + */ +static inline loff_t exfat_block_to_bytes(struct super_block *sb, + sector_t block) +{ + return (loff_t)block << sb->s_blocksize_bits; +} + +static inline sector_t exfat_bytes_to_block(struct super_block *sb, loff_t size) +{ + return (sector_t)(size >> sb->s_blocksize_bits); +} + +static inline sector_t exfat_bytes_to_block_round_up(struct super_block *sb, + loff_t size) +{ + if (size <= 0) + return 0; + return (sector_t)(((size - 1) >> sb->s_blocksize_bits) + 1); +} + +static inline u32 exfat_block_offset(struct super_block *sb, loff_t off) +{ + return (u32)(off & (sb->s_blocksize - 1)); +} + +/* + * helpers for block size to dentry size conversion. + */ +static inline u32 exfat_bytes_to_dentries(loff_t b) +{ + return (u32)(b >> DENTRY_SIZE_BITS); +} + +static inline u32 exfat_dentries_to_bytes(u32 dentry) +{ + return dentry << DENTRY_SIZE_BITS; +} + +/* + * helpers for cluster size to dentry size conversion. + */ +static inline u32 exfat_cluster_to_dentries(struct exfat_sb_info *sbi, + u32 nr_clusters) +{ + return nr_clusters << (sbi->cluster_size_bits - DENTRY_SIZE_BITS); +} + +static inline u32 exfat_dentries_to_cluster(struct exfat_sb_info *sbi, + u32 dentry) +{ + return dentry >> (sbi->cluster_size_bits - DENTRY_SIZE_BITS); +} + /* super.c */ int exfat_set_volume_dirty(struct super_block *sb); int exfat_clear_volume_dirty(struct super_block *sb); diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index dce0955e689a..45b0b754a2e4 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -412,8 +412,8 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) if (IS_DIRSYNC(dir)) return sync_blockdev_range(sb->s_bdev, - EXFAT_BLK_TO_B(blknr, sb), - EXFAT_BLK_TO_B(last_blknr, sb) - 1); + exfat_block_to_bytes(sb, blknr), + exfat_block_to_bytes(sb, last_blknr) - 1); return 0; } diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 91e5511945d1..935dcc803ebb 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -34,9 +34,9 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) if (ret) return ret; - num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); /* integer overflow is already checked in inode_newsize_ok(). */ - new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi); + new_num_clusters = exfat_bytes_to_cluster_round_up(sbi, size); if (new_num_clusters == num_clusters) goto out; @@ -201,8 +201,8 @@ int __exfat_truncate(struct inode *inode) exfat_set_volume_dirty(sb); - num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); - num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters_new = exfat_bytes_to_cluster_round_up(sbi, i_size_read(inode)); + num_clusters_phys = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 1ea4c740fef9..249a35e2b4b2 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -135,7 +135,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int local_clu_offset = clu_offset; unsigned int num_to_be_allocated = 0, num_clusters; - num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi); + num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); if (clu_offset >= num_clusters) num_to_be_allocated = clu_offset - num_clusters + 1; @@ -216,7 +216,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, *clu = new_clu.dir; - inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9; + inode->i_blocks += + exfat_cluster_to_sectors(sbi, num_to_be_allocated); /* * Move *clu pointer along FAT chains (hole care) because the @@ -254,12 +255,12 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, mutex_lock(&sbi->s_lock); i_size = i_size_read(inode); - last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size, sb); + last_block = exfat_bytes_to_block_round_up(sb, i_size); if (iblock >= last_block && !create) goto done; /* Is this block already allocated? */ - count = EXFAT_B_TO_CLU_ROUND_UP(bh_result->b_size, sbi); + count = exfat_bytes_to_cluster_round_up(sbi, bh_result->b_size); err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, &cluster, &count, create); if (err) { @@ -296,9 +297,9 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, * care the last nested block if valid_size is not equal to i_size. */ if (i_size == ei->valid_size || create || !bh_result->b_folio) - valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb); + valid_blks = exfat_bytes_to_block_round_up(sb, ei->valid_size); else - valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb); + valid_blks = exfat_bytes_to_block(sb, ei->valid_size); /* The range has been fully written, map it */ if (iblock + max_blocks < valid_blks) @@ -313,7 +314,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, /* The area has not been written, map and mark as new for create case */ if (create) { set_buffer_new(bh_result); - ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb); + ei->valid_size = exfat_block_to_bytes(sb, iblock + max_blocks); mark_inode_dirty(inode); goto done; } @@ -343,7 +344,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, goto done; } - pos = EXFAT_BLK_TO_B(iblock, sb); + pos = exfat_block_to_bytes(sb, iblock); size = ei->valid_size - pos; addr = folio_address(bh_result->b_folio) + offset_in_folio(bh_result->b_folio, pos); @@ -374,7 +375,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, */ clear_buffer_mapped(bh_result); done: - bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); + bh_result->b_size = exfat_block_to_bytes(sb, max_blocks); if (err < 0) clear_buffer_mapped(bh_result); unlock_ret: diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 833b31d0f955..269993881dba 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -208,7 +208,7 @@ static int exfat_search_empty_slot(struct super_block *sb, int dentries_per_clu; struct exfat_chain clu; struct exfat_sb_info *sbi = EXFAT_SB(sb); - int total_entries = EXFAT_CLU_TO_DEN(p_dir->size, sbi); + unsigned int total_entries = exfat_cluster_to_dentries(sbi, p_dir->size); dentries_per_clu = sbi->dentries_per_clu; @@ -266,7 +266,7 @@ static int exfat_search_empty_slot(struct super_block *sb, static int exfat_check_max_dentries(struct inode *inode) { - if (EXFAT_B_TO_DEN(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { + if (exfat_bytes_to_dentries(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { /* * exFAT spec allows a dir to grow up to 8388608(256MB) * dentries @@ -314,7 +314,8 @@ int exfat_find_empty_entry(struct inode *inode, } exfat_chain_set(p_dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(inode)), + ei->flags); while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, num_entries, es)) < 0) { @@ -375,7 +376,7 @@ int exfat_find_empty_entry(struct inode *inode, hint_femp.cur.size++; p_dir->size++; - size = EXFAT_CLU_TO_B(p_dir->size, sbi); + size = exfat_cluster_to_bytes(sbi, p_dir->size); /* directory inode should be updated in here */ i_size_write(inode, size); @@ -604,7 +605,7 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, return ret; exfat_chain_set(&cdir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags); + exfat_bytes_to_cluster(sbi, i_size_read(dir)), ei->flags); /* check the validation of hint_stat and initialize it if required */ if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { @@ -681,7 +682,7 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, return -EIO; } - if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { + if (unlikely(exfat_bytes_to_cluster_round_up(sbi, info->size) > sbi->used_clusters)) { exfat_fs_error(sb, "data size is invalid(%lld)", info->size); return -EIO; } @@ -695,7 +696,8 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, if (info->type == TYPE_DIR) { exfat_chain_set(&cdir, info->start_clu, - EXFAT_B_TO_CLU(info->size, sbi), info->flags); + exfat_bytes_to_cluster(sbi, info->size), + info->flags); count = exfat_count_dir_entries(sb, &cdir); if (count < 0) return -EIO; @@ -921,7 +923,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) } exfat_chain_set(&clu_to_free, ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi), ei->flags); + exfat_bytes_to_cluster_round_up(sbi, i_size_read(inode)), ei->flags); err = exfat_check_dir_empty(sb, &clu_to_free); if (err) { @@ -1128,8 +1130,8 @@ static int __exfat_rename(struct inode *old_parent_inode, new_clu.dir = new_ei->start_clu; new_clu.size = - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi); + exfat_bytes_to_cluster_round_up(sbi, + i_size_read(new_inode)); new_clu.flags = new_ei->flags; ret = exfat_check_dir_empty(sb, &new_clu); @@ -1173,8 +1175,8 @@ static int __exfat_rename(struct inode *old_parent_inode, struct exfat_chain new_clu_to_free; exfat_chain_set(&new_clu_to_free, new_ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi), new_ei->flags); + exfat_bytes_to_cluster_round_up(sbi, i_size_read(new_inode)), + new_ei->flags); if (exfat_free_cluster(new_inode, &new_clu_to_free)) { /* just set I/O error only */ diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 95d87e2d7717..cb2f8eefff99 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -369,7 +369,7 @@ static int exfat_read_root(struct inode *inode, struct exfat_chain *root_clu) ei->hint_stat.clu = sbi->root_dir; ei->hint_femp.eidx = EXFAT_HINT_NONE; - i_size_write(inode, EXFAT_CLU_TO_B(root_clu->size, sbi)); + i_size_write(inode, exfat_cluster_to_bytes(sbi, root_clu->size)); num_subdirs = exfat_count_dir_entries(sb, root_clu); if (num_subdirs < 0) @@ -538,7 +538,7 @@ static int exfat_read_boot_sector(struct super_block *sb) * machines. */ sb->s_maxbytes = min(MAX_LFS_FILESIZE, - EXFAT_CLU_TO_B((loff_t)EXFAT_MAX_NUM_CLUSTER, sbi)); + exfat_cluster_to_bytes(sbi, (loff_t)EXFAT_MAX_NUM_CLUSTER)); /* check logical sector size */ if (exfat_calibrate_blocksize(sb, 1 << p_boot->sect_size_bits)) From 3474416bb9abafca2e79b7ff700a100b7e8bc766 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 23 Apr 2026 14:38:51 +0900 Subject: [PATCH 3704/5214] exfat: add balloc parameter to exfat_map_cluster() for iomap support In preparation for supporting the iomap infrastructure, we need to know whether a new cluster was allocated or not in exfat_map_cluster(). Add an optional 'bool *balloc' output parameter. When a new cluster is allocated, *balloc is set to true. Pass NULL from exfat_get_block() to preserve the existing behavior. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/inode.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 249a35e2b4b2..a10d4f3c66a1 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -124,7 +124,8 @@ void exfat_sync_inode(struct inode *inode) * *clu = (~0), if it's unable to allocate a new cluster */ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, - unsigned int *clu, unsigned int *count, int create) + unsigned int *clu, unsigned int *count, int create, + bool *balloc) { int ret; unsigned int last_clu; @@ -229,6 +230,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, if (exfat_cluster_walk(sb, clu, num_to_be_allocated - 1, ei->flags)) return -EIO; *count = 1; + if (balloc) + *balloc = true; } /* hint information */ @@ -262,7 +265,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, /* Is this block already allocated? */ count = exfat_bytes_to_cluster_round_up(sbi, bh_result->b_size); err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, - &cluster, &count, create); + &cluster, &count, create, NULL); if (err) { if (err != -ENOSPC) exfat_fs_error_ratelimit(sb, From a095c12ef13156cc86de858c8d127f24895e2d6e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 8 May 2026 14:33:35 +0900 Subject: [PATCH 3705/5214] exfat: add exfat_file_open() Add exfat_file_open() to handle file open operation for exFAT. This change is a preparation step before introducing iomap-based direct IO support. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/file.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 935dcc803ebb..857cd3030cae 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -813,7 +813,16 @@ static ssize_t exfat_splice_read(struct file *in, loff_t *ppos, return filemap_splice_read(in, ppos, pipe, len, flags); } +static int exfat_file_open(struct inode *inode, struct file *filp) +{ + if (unlikely(exfat_forced_shutdown(inode->i_sb))) + return -EIO; + + return generic_file_open(inode, filp); +} + const struct file_operations exfat_file_operations = { + .open = exfat_file_open, .llseek = generic_file_llseek, .read_iter = exfat_file_read_iter, .write_iter = exfat_file_write_iter, From 286ae368871d2a93b68148c7684b8cff63c9ba80 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 7 May 2026 09:05:08 +0900 Subject: [PATCH 3706/5214] exfat: add support for multi-cluster allocation Currently exfat_map_cluster() allocates and returns only one cluster at a time even when more clusters are needed. This causes multiple FAT walks and repeated allocation calls during large sequential writes or when using iomap for writes. This change exfat_map_cluster() and exfat_alloc_cluster() to be able to allocate multiple contiguous clusters. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/dir.c | 2 +- fs/exfat/exfat_fs.h | 2 +- fs/exfat/fatent.c | 26 ++++++++++++++++---------- fs/exfat/file.c | 2 +- fs/exfat/inode.c | 23 ++++++----------------- fs/exfat/namei.c | 2 +- 6 files changed, 26 insertions(+), 31 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index f7f0b6a5bbcf..8b8f6bc0c233 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -295,7 +295,7 @@ int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu) exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN); - ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode)); + ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode), false); if (ret) return ret; diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index ad8d6a48fe1f..e47fbfa1d854 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -497,7 +497,7 @@ int exfat_clear_volume_dirty(struct super_block *sb); exfat_cluster_walk(sb, (pclu), 1, ALLOC_FAT_CHAIN) int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, - struct exfat_chain *p_chain, bool sync_bmap); + struct exfat_chain *p_chain, bool sync_bmap, bool contig); int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain); int exfat_ent_get(struct super_block *sb, unsigned int loc, unsigned int *content, struct buffer_head **last); diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index 45b0b754a2e4..a8b11e2ce43f 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -419,7 +419,7 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) } int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, - struct exfat_chain *p_chain, bool sync_bmap) + struct exfat_chain *p_chain, bool sync_bmap, bool contig) { int ret = -ENOSPC; unsigned int total_cnt; @@ -470,14 +470,20 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, while ((new_clu = exfat_find_free_bitmap(sb, hint_clu)) != EXFAT_EOF_CLUSTER) { - if (new_clu != hint_clu && - p_chain->flags == ALLOC_NO_FAT_CHAIN) { - if (exfat_chain_cont_cluster(sb, p_chain->dir, - p_chain->size)) { - ret = -EIO; - goto free_cluster; + if (new_clu != hint_clu) { + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + p_chain->size)) { + ret = -EIO; + goto free_cluster; + } + p_chain->flags = ALLOC_FAT_CHAIN; + } + + if (contig && p_chain->size > 0) { + hint_clu = last_clu; + goto done; } - p_chain->flags = ALLOC_FAT_CHAIN; } /* update allocation bitmap */ @@ -507,9 +513,9 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, last_clu = new_clu; if (p_chain->size == num_alloc) { +done: sbi->clu_srch_ptr = hint_clu; - sbi->used_clusters += num_alloc; - + sbi->used_clusters += p_chain->size; mutex_unlock(&sbi->bitmap_lock); return 0; } diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 857cd3030cae..89cc3d046f0f 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -57,7 +57,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) clu.flags = ei->flags; ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters, - &clu, inode_needs_sync(inode)); + &clu, inode_needs_sync(inode), false); if (ret) return ret; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index a10d4f3c66a1..7b09d94ac464 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -137,9 +137,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int num_to_be_allocated = 0, num_clusters; num_clusters = exfat_bytes_to_cluster(sbi, exfat_ondisk_size(inode)); - - if (clu_offset >= num_clusters) - num_to_be_allocated = clu_offset - num_clusters + 1; + if (clu_offset > num_clusters || + *count > num_clusters - clu_offset) + num_to_be_allocated = clu_offset + *count - num_clusters; if (!create && (num_to_be_allocated > 0)) { *clu = EXFAT_EOF_CLUSTER; @@ -182,7 +182,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, } ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu, - inode_needs_sync(inode)); + inode_needs_sync(inode), true); if (ret) return ret; @@ -216,20 +216,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, } *clu = new_clu.dir; + *count = new_clu.size; - inode->i_blocks += - exfat_cluster_to_sectors(sbi, num_to_be_allocated); - - /* - * Move *clu pointer along FAT chains (hole care) because the - * caller of this function expect *clu to be the last cluster. - * This only works when num_to_be_allocated >= 2, - * *clu = (the first cluster of the allocated chain) => - * (the last cluster of ...) - */ - if (exfat_cluster_walk(sb, clu, num_to_be_allocated - 1, ei->flags)) - return -EIO; - *count = 1; + inode->i_blocks += exfat_cluster_to_sectors(sbi, new_clu.size); if (balloc) *balloc = true; } diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 269993881dba..78f861f23246 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -341,7 +341,7 @@ int exfat_find_empty_entry(struct inode *inode, } /* allocate a cluster */ - ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode)); + ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode), false); if (ret) return ret; From 3b9450beeb961c7ddf4cccddba37fcfa6d1fcb7a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 6 May 2026 10:52:27 +0900 Subject: [PATCH 3707/5214] exfat: add data_start_bytes and exfat_cluster_to_phys_bytes() helper This caches the data area start offset in bytes (data_start_bytes) and introduces a helper function exfat_cluster_to_phys_bytes() to compute the physical byte position of a given cluster. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/exfat_fs.h | 8 ++++++++ fs/exfat/super.c | 1 + 2 files changed, 9 insertions(+) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index e47fbfa1d854..e60b955da0d0 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -227,6 +227,7 @@ struct exfat_sb_info { unsigned long long FAT1_start_sector; /* FAT1 start sector */ unsigned long long FAT2_start_sector; /* FAT2 start sector */ unsigned long long data_start_sector; /* data area start sector */ + unsigned long long data_start_bytes; unsigned int num_FAT_sectors; /* num of FAT sectors */ unsigned int root_dir; /* root dir cluster */ unsigned int dentries_per_clu; /* num of dentries per cluster */ @@ -400,6 +401,13 @@ static inline loff_t exfat_ondisk_size(const struct inode *inode) return ((loff_t)inode->i_blocks) << 9; } +static inline loff_t exfat_cluster_to_phys_bytes(struct exfat_sb_info *sbi, + unsigned int clus) +{ + return ((loff_t)(clus - EXFAT_RESERVED_CLUSTERS) << sbi->cluster_size_bits) + + sbi->data_start_bytes; +} + /* * helpers for cluster size to byte conversion. */ diff --git a/fs/exfat/super.c b/fs/exfat/super.c index cb2f8eefff99..388db271c6bf 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -499,6 +499,7 @@ static int exfat_read_boot_sector(struct super_block *sb) if (p_boot->num_fats == 2) sbi->FAT2_start_sector += sbi->num_FAT_sectors; sbi->data_start_sector = le32_to_cpu(p_boot->clu_offset); + sbi->data_start_bytes = sbi->data_start_sector << p_boot->sect_size_bits; sbi->num_sectors = le64_to_cpu(p_boot->vol_length); /* because the cluster index starts with 2 */ sbi->num_clusters = le32_to_cpu(p_boot->clu_count) + From f0d1b2e1e02c11e29c953f22a485449e3da2a575 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 13 May 2026 10:44:46 +0900 Subject: [PATCH 3708/5214] exfat: fix implicit declaration of brelse() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit exfat_cluster_walk() calls brelse(bh) without including the header that declares the function, causing the following build error: fs/exfat/exfat_fs.h:542:9: error: implicit declaration of function ‘brelse’ [-Werror=implicit-function-declaration] Fix this by adding the missing buffer_head.h in exfat_fs.h. Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/exfat_fs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index e60b955da0d0..86ae468c965b 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -12,6 +12,7 @@ #include #include #include +#include #define EXFAT_ROOT_INO 1 From 82a81a7352bcf5f2756ac33d47ee0582737e9a85 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 15 Jun 2026 19:59:58 +0900 Subject: [PATCH 3709/5214] exfat: add iomap buffered I/O support Add full buffered I/O support using the iomap framework to the exfat filesystem. This will replaces the old exfat_get_block(), exfat_write_begin(), exfat_write_end(), and exfat_block_truncate_page() with their iomap equivalents. Buffered writes now use iomap_file_buffered_write(), read uses iomap_bio_read_folio() and iomap_bio_readahead(), and writeback is handled through iomap_writepages(). Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/Kconfig | 1 + fs/exfat/Makefile | 2 +- fs/exfat/exfat_fs.h | 6 +- fs/exfat/file.c | 138 ++++++++++++++++++---------- fs/exfat/inode.c | 121 +++++++++---------------- fs/exfat/iomap.c | 215 ++++++++++++++++++++++++++++++++++++++++++++ fs/exfat/iomap.h | 14 +++ 7 files changed, 367 insertions(+), 130 deletions(-) create mode 100644 fs/exfat/iomap.c create mode 100644 fs/exfat/iomap.h diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig index cbeca8e44d9b..e0b200902253 100644 --- a/fs/exfat/Kconfig +++ b/fs/exfat/Kconfig @@ -5,6 +5,7 @@ config EXFAT_FS select BUFFER_HEAD select NLS select LEGACY_DIRECT_IO + select FS_IOMAP help This allows you to mount devices formatted with the exFAT file system. exFAT is typically used on SD-Cards or USB sticks. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile index ed51926a4971..e06bf85870ae 100644 --- a/fs/exfat/Makefile +++ b/fs/exfat/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_EXFAT_FS) += exfat.o exfat-y := inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \ - file.o balloc.o + file.o balloc.o iomap.o diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 86ae468c965b..5f36c6892c8a 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -294,6 +294,8 @@ struct exfat_inode_info { /* on-disk position of directory entry or 0 */ loff_t i_pos; loff_t valid_size; + /* page-aligned size that has been zeroed out for mmap */ + loff_t zeroed_size; /* hash by i_location */ struct hlist_node i_hash_fat; /* protect bmap against truncate */ @@ -651,7 +653,9 @@ struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); int __exfat_write_inode(struct inode *inode, int sync); int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); void exfat_evict_inode(struct inode *inode); -int exfat_block_truncate_page(struct inode *inode, loff_t from); +int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, + unsigned int *clu, unsigned int *count, int create, + bool *balloc); /* exfat/nls.c */ unsigned short exfat_toupper(struct super_block *sb, unsigned short a); diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 89cc3d046f0f..5b667077865d 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -15,9 +15,11 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" +#include "iomap.h" static int exfat_cont_expand(struct inode *inode, loff_t size) { @@ -27,8 +29,9 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_chain clu; + loff_t oldsize = i_size_read(inode); - truncate_pagecache(inode, i_size_read(inode)); + truncate_pagecache(inode, oldsize); ret = inode_newsize_ok(inode, size); if (ret) @@ -79,6 +82,13 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); /* Expanded range not zeroed, do not update valid_size */ i_size_write(inode, size); + /* + * When extending file size, call truncate_pagecache() first, + * then update i_size, and call pagecache_isize_extended() + * to ensures the straddling folio is properly marked RO so + * page_mkwrite() is called and post-EOF area is zeroed. + */ + pagecache_isize_extended(inode, oldsize, inode->i_size); inode->i_blocks = round_up(size, sbi->cluster_size) >> 9; mark_inode_dirty(inode); @@ -237,7 +247,7 @@ int __exfat_truncate(struct inode *inode) } if (i_size_read(inode) < ei->valid_size) - ei->valid_size = i_size_read(inode); + ei->valid_size = ei->zeroed_size = i_size_read(inode); if (ei->type == TYPE_FILE) ei->attr |= EXFAT_ATTR_ARCHIVE; @@ -396,10 +406,6 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, exfat_truncate_inode_atime(inode); if (attr->ia_valid & ATTR_SIZE) { - error = exfat_block_truncate_page(inode, attr->ia_size); - if (error) - goto out; - down_write(&EXFAT_I(inode)->truncate_lock); truncate_setsize(inode, attr->ia_size); @@ -644,42 +650,26 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size) { - int err; - loff_t pos; struct exfat_inode_info *ei = EXFAT_I(inode); - struct address_space *mapping = inode->i_mapping; - const struct address_space_operations *ops = mapping->a_ops; + loff_t old_valid_size = ei->valid_size; + int ret = 0; - pos = ei->valid_size; - while (pos < new_valid_size) { - u32 len; - struct folio *folio; - unsigned long off; + if (old_valid_size < new_valid_size) { + if (i_size_read(inode) < new_valid_size) { + i_size_write(inode, new_valid_size); + mark_inode_dirty(inode); + } - len = PAGE_SIZE - (pos & (PAGE_SIZE - 1)); - if (pos + len > new_valid_size) - len = new_valid_size - pos; - - err = ops->write_begin(NULL, mapping, pos, len, &folio, NULL); - if (err) - goto out; - - off = offset_in_folio(folio, pos); - folio_zero_new_buffers(folio, off, off + len); - - err = ops->write_end(NULL, mapping, pos, len, len, folio, NULL); - if (err < 0) - goto out; - pos += len; - - balance_dirty_pages_ratelimited(mapping); - cond_resched(); + ret = iomap_zero_range(inode, old_valid_size, + new_valid_size - old_valid_size, NULL, + &exfat_write_iomap_ops, NULL, NULL); + if (ret) { + truncate_setsize(inode, old_valid_size); + exfat_truncate(inode); + } } - return 0; - -out: - return err; + return ret; } static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) @@ -690,6 +680,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) struct exfat_inode_info *ei = EXFAT_I(inode); loff_t pos = iocb->ki_pos; loff_t valid_size; + int err; if (unlikely(exfat_forced_shutdown(inode->i_sb))) return -EIO; @@ -715,6 +706,12 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) } } + err = file_modified(iocb->ki_filp); + if (err) { + ret = err; + goto unlock; + } + if (pos > valid_size) { ret = exfat_extend_valid_size(inode, pos); if (ret < 0 && ret != -ENOSPC) { @@ -726,7 +723,11 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) goto unlock; } - ret = __generic_file_write_iter(iocb, iter); + if (iocb->ki_flags & IOCB_DIRECT) + ret = __generic_file_write_iter(iocb, iter); + else + ret = iomap_file_buffered_write(iocb, iter, + &exfat_write_iomap_ops, NULL, NULL); if (ret < 0) goto unlock; @@ -762,28 +763,56 @@ static ssize_t exfat_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) { - int err; struct inode *inode = file_inode(vmf->vma->vm_file); struct exfat_inode_info *ei = EXFAT_I(inode); - loff_t new_valid_size; + vm_fault_t ret; + loff_t new_valid_size, mmap_valid_size; if (!inode_trylock(inode)) return VM_FAULT_RETRY; - new_valid_size = ((loff_t)vmf->pgoff + 1) << PAGE_SHIFT; - new_valid_size = min(new_valid_size, i_size_read(inode)); + mmap_valid_size = ((loff_t)vmf->pgoff + 1) << PAGE_SHIFT; + new_valid_size = min(mmap_valid_size, i_size_read(inode)); if (ei->valid_size < new_valid_size) { - err = exfat_extend_valid_size(inode, new_valid_size); - if (err < 0) { - inode_unlock(inode); - return vmf_fs_error(err); + if (ei->zeroed_size < mmap_valid_size) { + int err; + + /* + * Only zero the range that hasn't been zeroed yet for + * this mmap write path. zeroed_size tracks the largest + * page-aligned offset that has already been zeroed. + * + * This prevents unnecessarily zeroing out the entire + * tail page on every page fault when userspace writes + * data byte-by-byte through mmap (after a small + * fallocate). It fixes data corruption in the tail page + * while preserving the existing valid_size semantics. + */ + err = iomap_zero_range(inode, ei->zeroed_size, + mmap_valid_size - ei->zeroed_size, NULL, + &exfat_iomap_ops, NULL, NULL); + if (err < 0) { + inode_unlock(inode); + return vmf_fs_error(err); + } + ei->zeroed_size = mmap_valid_size; } + + ei->valid_size = new_valid_size; + mark_inode_dirty(inode); } + sb_start_pagefault(inode->i_sb); + file_update_time(vmf->vma->vm_file); + + filemap_invalidate_lock_shared(inode->i_mapping); + ret = iomap_page_mkwrite(vmf, &exfat_write_iomap_ops, NULL); + filemap_invalidate_unlock_shared(inode->i_mapping); + sb_end_pagefault(inode->i_sb); inode_unlock(inode); - return filemap_page_mkwrite(vmf); + return ret; } static const struct vm_operations_struct exfat_file_vm_ops = { @@ -799,6 +828,21 @@ static int exfat_file_mmap_prepare(struct vm_area_desc *desc) if (unlikely(exfat_forced_shutdown(file_inode(desc->file)->i_sb))) return -EIO; + if (vma_desc_test_all(desc, VMA_SHARED_BIT, VMA_MAYWRITE_BIT)) { + struct inode *inode = file_inode(file); + loff_t from, to; + int err; + + from = ((loff_t)desc->pgoff << PAGE_SHIFT); + to = min_t(loff_t, i_size_read(inode), + from + vma_desc_size(desc)); + if (EXFAT_I(inode)->valid_size < to) { + err = exfat_extend_valid_size(inode, to); + if (err) + return err; + } + } + file_accessed(file); desc->vm_ops = &exfat_file_vm_ops; return 0; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 7b09d94ac464..96ea243c67db 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -13,13 +13,16 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" +#include "iomap.h" int __exfat_write_inode(struct inode *inode, int sync) { unsigned long long on_disk_size; + unsigned long long on_disk_valid_size; struct exfat_dentry *ep, *ep2; struct exfat_entry_set_cache es; struct super_block *sb = inode->i_sb; @@ -74,17 +77,12 @@ int __exfat_write_inode(struct inode *inode, int sync) if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_size = 0; + /* valid_size must not exceed size in the on-disk stream entry. */ + on_disk_valid_size = min_t(unsigned long long, ei->valid_size, + on_disk_size); ep2->dentry.stream.size = cpu_to_le64(on_disk_size); - /* - * mmap write does not use exfat_write_end(), valid_size may be - * extended to the sector-aligned length in exfat_get_block(). - * So we need to fixup valid_size to the writren length. - */ - if (on_disk_size < ei->valid_size) - ep2->dentry.stream.valid_size = ep2->dentry.stream.size; - else - ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size); + ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_valid_size); if (on_disk_size) { ep2->dentry.stream.flags = ei->flags; @@ -123,7 +121,7 @@ void exfat_sync_inode(struct inode *inode) * Output: errcode, cluster number * *clu = (~0), if it's unable to allocate a new cluster */ -static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, +int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int *clu, unsigned int *count, int create, bool *balloc) { @@ -377,7 +375,13 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, static int exfat_read_folio(struct file *file, struct folio *folio) { - return mpage_read_folio(folio, exfat_get_block); + struct iomap_read_folio_ctx ctx = { + .cur_folio = folio, + .ops = &exfat_iomap_bio_read_ops, + }; + + iomap_read_folio(&exfat_iomap_ops, &ctx, NULL); + return 0; } static void exfat_readahead(struct readahead_control *rac) @@ -386,6 +390,10 @@ static void exfat_readahead(struct readahead_control *rac) struct inode *inode = mapping->host; struct exfat_inode_info *ei = EXFAT_I(inode); loff_t pos = readahead_pos(rac); + struct iomap_read_folio_ctx ctx = { + .ops = &exfat_iomap_bio_read_ops, + .rac = rac, + }; /* Range cross valid_size, read it page by page. */ if (ei->valid_size < i_size_read(inode) && @@ -393,16 +401,22 @@ static void exfat_readahead(struct readahead_control *rac) ei->valid_size < pos + readahead_length(rac)) return; - mpage_readahead(rac, exfat_get_block); + iomap_readahead(&exfat_iomap_ops, &ctx, NULL); } static int exfat_writepages(struct address_space *mapping, struct writeback_control *wbc) { + struct iomap_writepage_ctx wpc = { + .inode = mapping->host, + .wbc = wbc, + .ops = &exfat_writeback_ops, + }; + if (unlikely(exfat_forced_shutdown(mapping->host->i_sb))) return -EIO; - return mpage_writepages(mapping, wbc, exfat_get_block); + return iomap_writepages(&wpc); } static void exfat_write_failed(struct address_space *mapping, loff_t to) @@ -416,51 +430,6 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to) } } -static int exfat_write_begin(const struct kiocb *iocb, - struct address_space *mapping, - loff_t pos, unsigned int len, - struct folio **foliop, void **fsdata) -{ - int ret; - - if (unlikely(exfat_forced_shutdown(mapping->host->i_sb))) - return -EIO; - - ret = block_write_begin(mapping, pos, len, foliop, exfat_get_block); - - if (ret < 0) - exfat_write_failed(mapping, pos+len); - - return ret; -} - -static int exfat_write_end(const struct kiocb *iocb, - struct address_space *mapping, - loff_t pos, unsigned int len, unsigned int copied, - struct folio *folio, void *fsdata) -{ - struct inode *inode = mapping->host; - struct exfat_inode_info *ei = EXFAT_I(inode); - int err; - - err = generic_write_end(iocb, mapping, pos, len, copied, folio, fsdata); - if (err < len) - exfat_write_failed(mapping, pos+len); - - if (!(err < 0) && pos + err > ei->valid_size) { - ei->valid_size = pos + err; - mark_inode_dirty(inode); - } - - if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) { - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - ei->attr |= EXFAT_ATTR_ARCHIVE; - mark_inode_dirty(inode); - } - - return err; -} - static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { struct address_space *mapping = iocb->ki_filp->f_mapping; @@ -510,34 +479,23 @@ static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ down_read(&EXFAT_I(mapping->host)->truncate_lock); - blocknr = generic_block_bmap(mapping, block, exfat_get_block); + blocknr = iomap_bmap(mapping, block, &exfat_iomap_ops); up_read(&EXFAT_I(mapping->host)->truncate_lock); return blocknr; } -/* - * exfat_block_truncate_page() zeroes out a mapping from file offset `from' - * up to the end of the block which corresponds to `from'. - * This is required during truncate to physically zeroout the tail end - * of that block so it doesn't yield old data if the file is later grown. - * Also, avoid causing failure from fsx for cases of "data past EOF" - */ -int exfat_block_truncate_page(struct inode *inode, loff_t from) -{ - return block_truncate_page(inode->i_mapping, from, exfat_get_block); -} - static const struct address_space_operations exfat_aops = { - .dirty_folio = block_dirty_folio, - .invalidate_folio = block_invalidate_folio, - .read_folio = exfat_read_folio, - .readahead = exfat_readahead, - .writepages = exfat_writepages, - .write_begin = exfat_write_begin, - .write_end = exfat_write_end, - .direct_IO = exfat_direct_IO, - .bmap = exfat_aop_bmap, - .migrate_folio = buffer_migrate_folio, + .read_folio = exfat_read_folio, + .readahead = exfat_readahead, + .writepages = exfat_writepages, + .dirty_folio = iomap_dirty_folio, + .bmap = exfat_aop_bmap, + .migrate_folio = filemap_migrate_folio, + .is_partially_uptodate = iomap_is_partially_uptodate, + .error_remove_folio = generic_error_remove_folio, + .release_folio = iomap_release_folio, + .invalidate_folio = iomap_invalidate_folio, + .direct_IO = exfat_direct_IO, }; static inline unsigned long exfat_hash(loff_t i_pos) @@ -601,6 +559,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) ei->flags = info->flags; ei->type = info->type; ei->valid_size = info->valid_size; + ei->zeroed_size = info->valid_size; ei->version = 0; ei->hint_stat.eidx = 0; diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c new file mode 100644 index 000000000000..188df8cfac9a --- /dev/null +++ b/fs/exfat/iomap.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * iomap callack functions + * + * Copyright (C) 2026 Namjae Jeon + */ + +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" +#include "iomap.h" + +static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, bool may_alloc) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + unsigned int cluster, num_clusters; + loff_t cluster_offset, cluster_length; + int err; + bool balloc = false; + + if (!may_alloc) { + /* Completely beyond EOF. Treat as hole */ + if (i_size_read(inode) <= offset) { + iomap->type = IOMAP_HOLE; + iomap->addr = IOMAP_NULL_ADDR; + iomap->offset = offset; + iomap->length = length; + return 0; + } + + /* Clamp length if the requested range goes beyond i_size */ + if (offset + length > i_size_read(inode)) + length = round_up(i_size_read(inode), + i_blocksize(inode)) - offset; + } + + num_clusters = exfat_bytes_to_cluster_round_up(sbi, + offset + length) - exfat_bytes_to_cluster(sbi, offset); + + mutex_lock(&sbi->s_lock); + iomap->bdev = inode->i_sb->s_bdev; + iomap->offset = offset; + + err = exfat_map_cluster(inode, exfat_bytes_to_cluster(sbi, offset), + &cluster, &num_clusters, may_alloc, &balloc); + if (err) + goto out; + + cluster_offset = exfat_cluster_offset(sbi, offset); + cluster_length = exfat_cluster_to_bytes(sbi, num_clusters); + + iomap->length = min_t(loff_t, length, cluster_length - cluster_offset); + iomap->addr = exfat_cluster_to_phys_bytes(sbi, cluster) + cluster_offset; + iomap->type = IOMAP_MAPPED; + iomap->flags = IOMAP_F_MERGED; + + if (may_alloc || flags & IOMAP_ZERO) { + if (balloc) + iomap->flags |= IOMAP_F_NEW; + else if (iomap->offset + iomap->length >= ei->valid_size) { + /* + * This is a write that starts at or extends beyond + * the current valid_size. The region between the old + * valid_size and the end of this write needs to be + * zeroed in the page cache to prevent stale data + * exposure (see IOMAP_F_ZERO_TAIL handling in + * __iomap_write_begin()). + */ + iomap->flags |= IOMAP_F_ZERO_TAIL; + } + } else { + if (offset >= ei->valid_size) { + iomap->type = IOMAP_UNWRITTEN; + } else if (offset + iomap->length > ei->valid_size) { + iomap->length = round_up(ei->valid_size, + i_blocksize(inode)) - + iomap->offset; + } + } + + iomap->flags |= IOMAP_F_MERGED; +out: + mutex_unlock(&sbi->s_lock); + return err; +} + +static int exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, struct iomap *srcmap) +{ + return __exfat_iomap_begin(inode, offset, length, flags, iomap, false); +} + +static int exfat_write_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, struct iomap *srcmap) +{ + return __exfat_iomap_begin(inode, offset, length, flags, iomap, true); +} + +const struct iomap_ops exfat_iomap_ops = { + .iomap_begin = exfat_iomap_begin, +}; + +/* + * exfat_write_iomap_end - Update the state after write + * + * Extends ->valid_size to cover the newly written range. + * Marks the inode dirty if metadata was changed. + */ +static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, + ssize_t written, unsigned int flags, struct iomap *iomap) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + bool dirtied = false; + loff_t end; + + if (!written) + return 0; + + end = pos + written; + + if (ei->valid_size < end) { + ei->valid_size = end; + if (ei->zeroed_size < end) + ei->zeroed_size = end; + dirtied = true; + } + + if (dirtied || iomap->flags & IOMAP_F_SIZE_CHANGED) + mark_inode_dirty(inode); + + return written; +} + +const struct iomap_ops exfat_write_iomap_ops = { + .iomap_begin = exfat_write_iomap_begin, + .iomap_end = exfat_write_iomap_end, +}; + +/* + * exfat_writeback_range - Map folio during writeback + * + * Called for each folio during writeback. If the folio falls outside the + * current iomap, remaps by calling read_iomap_begin. + */ +static ssize_t exfat_writeback_range(struct iomap_writepage_ctx *wpc, + struct folio *folio, u64 offset, unsigned int len, u64 end_pos) +{ + if (offset < wpc->iomap.offset || + offset >= wpc->iomap.offset + wpc->iomap.length) { + int error; + + error = __exfat_iomap_begin(wpc->inode, offset, len, + 0, &wpc->iomap, false); + if (error) + return error; + } + + return iomap_add_to_ioend(wpc, folio, offset, end_pos, len); +} + +const struct iomap_writeback_ops exfat_writeback_ops = { + .writeback_range = exfat_writeback_range, + .writeback_submit = iomap_ioend_writeback_submit, +}; + +/** + * exfat_iomap_read_end_io - iomap read bio completion handler for exFAT + * @bio: bio that has completed reading + * + * exfat_iomap_begin() rounds up MAPPED extents to the block boundary of + * valid_size. This ensures that any subsequent blocks are treated as + * IOMAP_UNWRITTEN, but it also causes the "straddle block" containing + * valid_size to be read from disk. The disk data beyond valid_size in + * this block is stale and must be zeroed to prevent data leakage. + */ +static void exfat_iomap_read_end_io(struct bio *bio) +{ + int error = blk_status_to_errno(bio->bi_status); + struct folio_iter iter; + + bio_for_each_folio_all(iter, bio) { + struct folio *folio = iter.folio; + struct exfat_inode_info *ei = EXFAT_I(folio->mapping->host); + s64 valid_size; + loff_t pos = folio_pos(folio); + + valid_size = ei->valid_size; + if (pos + iter.offset < valid_size && + pos + iter.offset + iter.length > valid_size) + folio_zero_segment(folio, offset_in_folio(folio, valid_size), + iter.offset + iter.length); + + iomap_finish_folio_read(folio, iter.offset, iter.length, error); + } + bio_put(bio); +} + +static void exfat_iomap_bio_submit_read(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx) +{ + struct bio *bio = ctx->read_ctx; + + bio->bi_end_io = exfat_iomap_read_end_io; + submit_bio(bio); +} + +const struct iomap_read_ops exfat_iomap_bio_read_ops = { + .read_folio_range = iomap_bio_read_folio_range, + .submit_read = exfat_iomap_bio_submit_read, +}; diff --git a/fs/exfat/iomap.h b/fs/exfat/iomap.h new file mode 100644 index 000000000000..7f8dcbe20a17 --- /dev/null +++ b/fs/exfat/iomap.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 Namjae Jeon + */ + +#ifndef _LINUX_EXFAT_IOMAP_H +#define _LINUX_EXFAT_IOMAP_H + +extern const struct iomap_ops exfat_iomap_ops; +extern const struct iomap_ops exfat_write_iomap_ops; +extern const struct iomap_writeback_ops exfat_writeback_ops; +extern const struct iomap_read_ops exfat_iomap_bio_read_ops; + +#endif /* _LINUX_EXFAT_IOMAP_H */ From 867b9c96dc837e09ef125a79dc151fa2fc7a5d32 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 23 May 2026 13:56:43 +0900 Subject: [PATCH 3710/5214] exfat: add iomap direct I/O support Add iomap-based direct I/O support to the exfat filesystem. This replaces the previous exfat_direct_IO() implementation that used blockdev_direct_IO() with iomap_dio_rw() interface. Reviewed-by: Christoph Hellwig Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/Kconfig | 1 - fs/exfat/exfat_fs.h | 1 - fs/exfat/file.c | 89 +++++++++++++++--- fs/exfat/inode.c | 221 ++++---------------------------------------- fs/exfat/iomap.c | 26 ++++++ fs/exfat/iomap.h | 1 + 6 files changed, 119 insertions(+), 220 deletions(-) diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig index e0b200902253..1fcb10c8d7bc 100644 --- a/fs/exfat/Kconfig +++ b/fs/exfat/Kconfig @@ -4,7 +4,6 @@ config EXFAT_FS tristate "exFAT filesystem support" select BUFFER_HEAD select NLS - select LEGACY_DIRECT_IO select FS_IOMAP help This allows you to mount devices formatted with the exFAT file system. diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 5f36c6892c8a..2607e51804b2 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -557,7 +557,6 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); /* file.c */ extern const struct file_operations exfat_file_operations; int __exfat_truncate(struct inode *inode); -void exfat_truncate(struct inode *inode); int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); int exfat_getattr(struct mnt_idmap *idmap, const struct path *path, diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 5b667077865d..9cd34149a188 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -293,7 +293,7 @@ int __exfat_truncate(struct inode *inode) return 0; } -void exfat_truncate(struct inode *inode) +static void exfat_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); @@ -672,6 +672,56 @@ static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size) return ret; } +static ssize_t exfat_fallback_buffered_write(struct kiocb *iocb, + struct iov_iter *from) +{ + loff_t offset = iocb->ki_pos, end; + ssize_t written; + int ret; + + iocb->ki_flags &= ~IOCB_DIRECT; + + written = iomap_file_buffered_write(iocb, from, &exfat_write_iomap_ops, + NULL, NULL); + if (written < 0) + return written; + + end = iocb->ki_pos + written - 1; + ret = filemap_write_and_wait_range(iocb->ki_filp->f_mapping, + offset, end); + if (ret) + return -EIO; + + invalidate_mapping_pages(iocb->ki_filp->f_mapping, + offset >> PAGE_SHIFT, + end >> PAGE_SHIFT); + + return written; +} + +static ssize_t exfat_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + ssize_t ret; + + ret = iomap_dio_rw(iocb, from, &exfat_write_iomap_ops, + &exfat_write_dio_ops, 0, NULL, 0); + if (ret == -ENOTBLK) + ret = 0; + else if (ret < 0) + return ret; + + if (iov_iter_count(from)) { + ssize_t written; + + written = exfat_fallback_buffered_write(iocb, from); + if (written < 0) + return written; + ret += written; + } + + return ret; +} + static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) { ssize_t ret; @@ -696,16 +746,6 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (ret <= 0) goto unlock; - if (iocb->ki_flags & IOCB_DIRECT) { - unsigned long align = pos | iov_iter_alignment(iter); - - if (!IS_ALIGNED(align, i_blocksize(inode)) && - !IS_ALIGNED(align, bdev_logical_block_size(inode->i_sb->s_bdev))) { - ret = -EINVAL; - goto unlock; - } - } - err = file_modified(iocb->ki_filp); if (err) { ret = err; @@ -724,7 +764,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) } if (iocb->ki_flags & IOCB_DIRECT) - ret = __generic_file_write_iter(iocb, iter); + ret = exfat_dio_write_iter(iocb, iter); else ret = iomap_file_buffered_write(iocb, iter, &exfat_write_iomap_ops, NULL, NULL); @@ -754,11 +794,24 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) static ssize_t exfat_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) { struct inode *inode = file_inode(iocb->ki_filp); + ssize_t ret; if (unlikely(exfat_forced_shutdown(inode->i_sb))) return -EIO; - return generic_file_read_iter(iocb, iter); + inode_lock_shared(inode); + + if (iocb->ki_flags & IOCB_DIRECT) { + file_accessed(iocb->ki_filp); + ret = iomap_dio_rw(iocb, iter, &exfat_iomap_ops, NULL, 0, + NULL, 0); + } else { + ret = generic_file_read_iter(iocb, iter); + } + + inode_unlock_shared(inode); + + return ret; } static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) @@ -859,10 +912,18 @@ static ssize_t exfat_splice_read(struct file *in, loff_t *ppos, static int exfat_file_open(struct inode *inode, struct file *filp) { + int err; + if (unlikely(exfat_forced_shutdown(inode->i_sb))) return -EIO; - return generic_file_open(inode, filp); + err = generic_file_open(inode, filp); + if (err) + return err; + + filp->f_mode |= FMODE_CAN_ODIRECT; + + return 0; } const struct file_operations exfat_file_operations = { diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 96ea243c67db..8e8d94319c3c 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -72,14 +72,27 @@ int __exfat_write_inode(struct inode *inode, int sync) &ep->dentry.file.access_date, NULL); - /* File size should be zero if there is no cluster allocated */ - on_disk_size = i_size_read(inode); + /* + * During a DIO write, valid_size is updated eagerly in iomap_end (so + * that concurrent buffered reads see IOMAP_MAPPED) while i_size is + * updated asynchronously in end_io. The FAT chain was already + * extended to cover ceil(valid_size/cluster_size) clusters. Use the + * maximum so the on-disk size field always covers the FAT chain, + * preventing fsck from reporting "more clusters are allocated". + */ + on_disk_size = max_t(unsigned long long, i_size_read(inode), + ei->valid_size); if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_size = 0; - /* valid_size must not exceed size in the on-disk stream entry. */ + /* + * valid_size on disk must reflect only confirmed data (up to i_size) + * and must not exceed on_disk_size. + */ on_disk_valid_size = min_t(unsigned long long, ei->valid_size, - on_disk_size); + i_size_read(inode)); + if (ei->start_clu == EXFAT_EOF_CLUSTER) + on_disk_valid_size = 0; ep2->dentry.stream.size = cpu_to_le64(on_disk_size); ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_valid_size); @@ -228,151 +241,6 @@ int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, return 0; } -static int exfat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; - int err = 0; - unsigned long mapped_blocks = 0; - unsigned int cluster, sec_offset, count; - sector_t last_block; - sector_t phys = 0; - sector_t valid_blks; - loff_t i_size; - - mutex_lock(&sbi->s_lock); - i_size = i_size_read(inode); - last_block = exfat_bytes_to_block_round_up(sb, i_size); - if (iblock >= last_block && !create) - goto done; - - /* Is this block already allocated? */ - count = exfat_bytes_to_cluster_round_up(sbi, bh_result->b_size); - err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, - &cluster, &count, create, NULL); - if (err) { - if (err != -ENOSPC) - exfat_fs_error_ratelimit(sb, - "failed to bmap (inode : %p iblock : %llu, err : %d)", - inode, (unsigned long long)iblock, err); - goto unlock_ret; - } - - if (cluster == EXFAT_EOF_CLUSTER) - goto done; - - /* sector offset in cluster */ - sec_offset = iblock & (sbi->sect_per_clus - 1); - - phys = exfat_cluster_to_sector(sbi, cluster) + sec_offset; - mapped_blocks = ((unsigned long)count << sbi->sect_per_clus_bits) - sec_offset; - max_blocks = min(mapped_blocks, max_blocks); - - map_bh(bh_result, sb, phys); - if (buffer_delay(bh_result)) - clear_buffer_delay(bh_result); - - /* - * In most cases, we just need to set bh_result to mapped, unmapped - * or new status as follows: - * 1. i_size == valid_size - * 2. write case (create == 1) - * 3. direct_read (!bh_result->b_folio) - * -> the unwritten part will be zeroed in exfat_direct_IO() - * - * Otherwise, in the case of buffered read, it is necessary to take - * care the last nested block if valid_size is not equal to i_size. - */ - if (i_size == ei->valid_size || create || !bh_result->b_folio) - valid_blks = exfat_bytes_to_block_round_up(sb, ei->valid_size); - else - valid_blks = exfat_bytes_to_block(sb, ei->valid_size); - - /* The range has been fully written, map it */ - if (iblock + max_blocks < valid_blks) - goto done; - - /* The range has been partially written, map the written part */ - if (iblock < valid_blks) { - max_blocks = valid_blks - iblock; - goto done; - } - - /* The area has not been written, map and mark as new for create case */ - if (create) { - set_buffer_new(bh_result); - ei->valid_size = exfat_block_to_bytes(sb, iblock + max_blocks); - mark_inode_dirty(inode); - goto done; - } - - /* - * The area has just one block partially written. - * In that case, we should read and fill the unwritten part of - * a block with zero. - */ - if (bh_result->b_folio && iblock == valid_blks && - (ei->valid_size & (sb->s_blocksize - 1))) { - loff_t size, pos; - void *addr; - - max_blocks = 1; - - /* - * No buffer_head is allocated. - * (1) bmap: It's enough to set blocknr without I/O. - * (2) read: The unwritten part should be filled with zero. - * If a folio does not have any buffers, - * let's returns -EAGAIN to fallback to - * block_read_full_folio() for per-bh IO. - */ - if (!folio_buffers(bh_result->b_folio)) { - err = -EAGAIN; - goto done; - } - - pos = exfat_block_to_bytes(sb, iblock); - size = ei->valid_size - pos; - addr = folio_address(bh_result->b_folio) + - offset_in_folio(bh_result->b_folio, pos); - - /* Check if bh->b_data points to proper addr in folio */ - if (bh_result->b_data != addr) { - exfat_fs_error_ratelimit(sb, - "b_data(%p) != folio_addr(%p)", - bh_result->b_data, addr); - err = -EINVAL; - goto done; - } - - /* Read a block */ - err = bh_read(bh_result, 0); - if (err < 0) - goto done; - - /* Zero unwritten part of a block */ - memset(bh_result->b_data + size, 0, bh_result->b_size - size); - err = 0; - goto done; - } - - /* - * The area has not been written, clear mapped for read/bmap cases. - * If so, it will be filled with zero without reading from disk. - */ - clear_buffer_mapped(bh_result); -done: - bh_result->b_size = exfat_block_to_bytes(sb, max_blocks); - if (err < 0) - clear_buffer_mapped(bh_result); -unlock_ret: - mutex_unlock(&sbi->s_lock); - return err; -} - static int exfat_read_folio(struct file *file, struct folio *folio) { struct iomap_read_folio_ctx ctx = { @@ -419,60 +287,6 @@ static int exfat_writepages(struct address_space *mapping, return iomap_writepages(&wpc); } -static void exfat_write_failed(struct address_space *mapping, loff_t to) -{ - struct inode *inode = mapping->host; - - if (to > i_size_read(inode)) { - truncate_pagecache(inode, i_size_read(inode)); - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - exfat_truncate(inode); - } -} - -static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) -{ - struct address_space *mapping = iocb->ki_filp->f_mapping; - struct inode *inode = mapping->host; - struct exfat_inode_info *ei = EXFAT_I(inode); - loff_t pos = iocb->ki_pos; - loff_t size = pos + iov_iter_count(iter); - int rw = iov_iter_rw(iter); - ssize_t ret; - - /* - * Need to use the DIO_LOCKING for avoiding the race - * condition of exfat_get_block() and ->truncate(). - */ - ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); - if (ret < 0) { - if (rw == WRITE && ret != -EIOCBQUEUED) - exfat_write_failed(mapping, size); - - return ret; - } - - size = pos + ret; - - if (rw == WRITE) { - /* - * If the block had been partially written before this write, - * ->valid_size will not be updated in exfat_get_block(), - * update it here. - */ - if (ei->valid_size < size) { - ei->valid_size = size; - mark_inode_dirty(inode); - } - } else if (pos < ei->valid_size && ei->valid_size < size) { - /* zero the unwritten part in the partially written block */ - iov_iter_revert(iter, size - ei->valid_size); - iov_iter_zero(size - ei->valid_size, iter); - } - - return ret; -} - static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) { sector_t blocknr; @@ -495,7 +309,6 @@ static const struct address_space_operations exfat_aops = { .error_remove_folio = generic_error_remove_folio, .release_folio = iomap_release_folio, .invalidate_folio = iomap_invalidate_folio, - .direct_IO = exfat_direct_IO, }; static inline unsigned long exfat_hash(loff_t i_pos) diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index 188df8cfac9a..7ad94d5806d9 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -12,6 +12,32 @@ #include "exfat_fs.h" #include "iomap.h" +/* + * exfat_file_write_dio_end_io - Direct I/O write completion handler + * + * Updates i_size if the write extended the file. Called from the dio layer + * after I/O completion. + */ +static int exfat_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, + int error, unsigned int flags) +{ + struct inode *inode = file_inode(iocb->ki_filp); + + if (error) + return error; + + if (size && i_size_read(inode) < iocb->ki_pos + size) { + i_size_write(inode, iocb->ki_pos + size); + mark_inode_dirty(inode); + } + + return 0; +} + +const struct iomap_dio_ops exfat_write_dio_ops = { + .end_io = exfat_file_write_dio_end_io, +}; + static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned int flags, struct iomap *iomap, bool may_alloc) { diff --git a/fs/exfat/iomap.h b/fs/exfat/iomap.h index 7f8dcbe20a17..830388f386f4 100644 --- a/fs/exfat/iomap.h +++ b/fs/exfat/iomap.h @@ -6,6 +6,7 @@ #ifndef _LINUX_EXFAT_IOMAP_H #define _LINUX_EXFAT_IOMAP_H +extern const struct iomap_dio_ops exfat_write_dio_ops; extern const struct iomap_ops exfat_iomap_ops; extern const struct iomap_ops exfat_write_iomap_ops; extern const struct iomap_writeback_ops exfat_writeback_ops; From b4b7fe2c7cbf519ab5e7edc6625c71608805fb1d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 23 May 2026 13:59:28 +0900 Subject: [PATCH 3711/5214] exfat: add support for SEEK_HOLE and SEEK_DATA in llseek Adds exfat_file_llseek() that implements these whence values via the iomap layer (iomap_seek_hole() and iomap_seek_data()) using the existing exfat_read_iomap_ops. Unlike many other modern filesystems, exFAT does not support sparse files with unallocated clusters (holes). In exFAT, clusters are always fully allocated once they are written or preallocated. In addition, exFAT maintains a separate "Valid Data Length" (valid_size) that is distinct from the logical file size. This affects how holes are reported during seeking. In exfat_iomap_begin(), ranges where the offset is greater than or equal to ei->valid_size are mapped as IOMAP_UNWRITTEN, while ranges below valid_size are mapped as IOMAP_MAPPED. This mapping behavior is used by the iomap seek functions to correctly report SEEK_HOLE and SEEK_DATA positions. - Ranges with offset >= ei->valid_size are mapped as IOMAP_HOLE. - Ranges with offset < ei->valid_size are mapped as IOMAP_MAPPED. Reviewed-by: Christoph Hellwig Acked-by: Christoph Hellwig Acked-by: "Darrick J. Wong" Signed-off-by: Namjae Jeon --- fs/exfat/file.c | 25 ++++++++++++++++++++++++- fs/exfat/iomap.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 9cd34149a188..c5ff2a97a465 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -926,9 +926,32 @@ static int exfat_file_open(struct inode *inode, struct file *filp) return 0; } +static loff_t exfat_file_llseek(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + + switch (whence) { + case SEEK_HOLE: + inode_lock_shared(inode); + offset = iomap_seek_hole(inode, offset, &exfat_iomap_ops); + inode_unlock_shared(inode); + break; + case SEEK_DATA: + inode_lock_shared(inode); + offset = iomap_seek_data(inode, offset, &exfat_iomap_ops); + inode_unlock_shared(inode); + break; + default: + return generic_file_llseek(file, offset, whence); + } + if (offset < 0) + return offset; + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); +} + const struct file_operations exfat_file_operations = { .open = exfat_file_open, - .llseek = generic_file_llseek, + .llseek = exfat_file_llseek, .read_iter = exfat_file_read_iter, .write_iter = exfat_file_write_iter, .unlocked_ioctl = exfat_ioctl, diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index 7ad94d5806d9..3ac1eebe997f 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -100,12 +100,36 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length iomap->flags |= IOMAP_F_ZERO_TAIL; } } else { + /* + * valid_size is tracked in byte granularity and + * marks the exact boundary between valid data and + * holes (or unwritten space). + * + * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE) + * and SEEK_DATA), we return IOMAP_HOLE. This allows + * iomap_seek_hole_iter() to directly return the + * precise byte position. + * + * For normal I/O paths (without IOMAP_REPORT) we + * return IOMAP_UNWRITTEN so the write path can + * distinguish it from a real hole. + */ if (offset >= ei->valid_size) { - iomap->type = IOMAP_UNWRITTEN; + iomap->type = flags & IOMAP_REPORT ? + IOMAP_HOLE : IOMAP_UNWRITTEN; } else if (offset + iomap->length > ei->valid_size) { - iomap->length = round_up(ei->valid_size, - i_blocksize(inode)) - - iomap->offset; + if (flags & IOMAP_REPORT) { + /* + * For SEEK_HOLE/SEEK_DATA, clip the length + * to the exact byte boundary (valid_size). + * This ensures the caller gets the precise + * hole position in byte units. + */ + iomap->length = ei->valid_size - iomap->offset; + } else + iomap->length = round_up(ei->valid_size, + i_blocksize(inode)) - + iomap->offset; } } From c7034e0bcb087754317d058b1149bb53e0a13213 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 22 May 2026 10:00:00 +0900 Subject: [PATCH 3712/5214] exfat: serialize truncate against in-flight DIO exfat_setattr() did not call inode_dio_wait() before performing a size change, leaving a window where a concurrent in-flight DIO write could be operating on clusters that the truncate is about to free. Add inode_dio_wait() before the truncate_setsize()/exfat_truncate() sequence so that any in-flight DIO completes before cluster freeing begins. Signed-off-by: Namjae Jeon --- fs/exfat/file.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/exfat/file.c b/fs/exfat/file.c index c5ff2a97a465..5fc13378d35f 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -406,6 +406,12 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, exfat_truncate_inode_atime(inode); if (attr->ia_valid & ATTR_SIZE) { + /* + * Wait for any in-flight DIO to finish before truncating to + * prevent a concurrent DIO from writing to clusters that are + * about to be freed. + */ + inode_dio_wait(inode); down_write(&EXFAT_I(inode)->truncate_lock); truncate_setsize(inode, attr->ia_size); From 942296784b2a9439651750c42f540bf2579b330f Mon Sep 17 00:00:00 2001 From: Rochan Avlur Date: Thu, 28 May 2026 07:21:37 -0700 Subject: [PATCH 3713/5214] exfat: preserve benign secondary entries during rename and move Commit 8258ef28001a ("exfat: handle unreconized benign secondary entries") added cluster freeing for benign secondary entries inside exfat_remove_entries(). However, exfat_remove_entries() is also called from the rename and move paths (exfat_rename_file and exfat_move_file), where the old entry set is being relocated rather than deleted. This causes benign secondary entries such as vendor extension entries to be silently destroyed on rename or cross-directory move, violating the exFAT spec requirement (section 8.2) that implementations preserve unrecognized benign secondary entries. Fix this by adding a free_benign parameter to exfat_remove_entries() so callers can suppress cluster freeing during relocation, and extending exfat_init_ext_entry() to copy trailing benign secondary entries from the old entry set into the new one internally. Also clean up the error paths to delete newly allocated entries on failure. Fixes: 8258ef28001a ("exfat: handle unreconized benign secondary entries") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-fsdevel/CAG7tbBV--waov7XVu2FHQEc6paR92dufS=em9DW5Kzsrpu3iQg@mail.gmail.com/ Signed-off-by: Rochan Avlur Reviewed-by: Yuezhang Mo Signed-off-by: Namjae Jeon --- fs/exfat/dir.c | 50 ++++++++++++++++++++++--- fs/exfat/exfat_fs.h | 5 ++- fs/exfat/namei.c | 89 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 8b8f6bc0c233..5a85a8ec0cc0 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -470,32 +470,70 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode, exfat_free_cluster(inode, &dir); } +/* + * exfat_init_ext_entry - initialize extension entries in a directory entry set + * @es: target entry set + * @num_entries: number of entries excluding benign secondary entries + * @p_uniname: filename to store + * @old_es: optional source entry set with benign secondary entries, or NULL + * @num_extra: number of benign secondary entries to copy from @old_es + * + * Set up the file, stream extension, and filename entries in @es, optionally + * preserving @num_extra benign secondary entries from @old_es. @es and @old_es + * may refer to the same entry set; excess entries are marked as deleted. + */ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname) + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra) { - int i; + int i, src_start = 0, old_num; unsigned short *uniname = p_uniname->name; struct exfat_dentry *ep; - es->num_entries = num_entries; + if (WARN_ON(num_extra < 0 || (num_extra && (!old_es || + old_es->num_entries < ES_IDX_FIRST_FILENAME + num_extra)))) + num_extra = 0; + + /* + * Save old entry count and source position before modifying + * es->num_entries, since old_es and es may point to the same + * entry set. + */ + old_num = es->num_entries; + if (old_es && num_extra > 0) + src_start = old_es->num_entries - num_extra; + + es->num_entries = num_entries + num_extra; ep = exfat_get_dentry_cached(es, ES_IDX_FILE); - ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); + ep->dentry.file.num_ext = (unsigned char)(num_entries - 1 + num_extra); ep = exfat_get_dentry_cached(es, ES_IDX_STREAM); ep->dentry.stream.name_len = p_uniname->name_len; ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash); + if (old_es && num_extra > 0) { + for (i = 0; i < num_extra; i++) + *exfat_get_dentry_cached(es, num_entries + i) = + *exfat_get_dentry_cached(old_es, src_start + i); + } + for (i = ES_IDX_FIRST_FILENAME; i < num_entries; i++) { ep = exfat_get_dentry_cached(es, i); exfat_init_name_entry(ep, uniname); uniname += EXFAT_FILE_NAME_LEN; } + /* Mark excess old entries as deleted (in-place shrink) */ + for (i = num_entries + num_extra; i < old_num; i++) { + ep = exfat_get_dentry_cached(es, i); + exfat_set_entry_type(ep, TYPE_DELETED); + } + exfat_update_dir_chksum(es); } void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order) + int order, bool free_benign) { int i; struct exfat_dentry *ep; @@ -503,7 +541,7 @@ void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, for (i = order; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); - if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC) + if (free_benign && (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)) exfat_free_benign_secondary_clusters(inode, ep); exfat_set_entry_type(ep, TYPE_DELETED); diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 2607e51804b2..9be50949ce34 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -592,9 +592,10 @@ void exfat_init_dir_entry(struct exfat_entry_set_cache *es, unsigned int type, unsigned int start_clu, unsigned long long size, struct timespec64 *ts); void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname); + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra); void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order); + int order, bool free_benign); void exfat_update_dir_chksum(struct exfat_entry_set_cache *es); int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 78f861f23246..b7d5e44ad38e 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -504,7 +504,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, * the first cluster is not determined yet. (0) */ exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts); - exfat_init_ext_entry(&es, num_entries, &uniname); + exfat_init_ext_entry(&es, num_entries, &uniname, NULL, 0); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (ret) @@ -786,7 +786,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); /* update the directory entry */ - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (err) @@ -941,7 +941,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); if (err) @@ -968,6 +968,23 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) return err; } +/* + * Count benign secondary entries beyond the filename entries. + * Returns the count, or -EIO if the entry set is inconsistent. + */ +static int exfat_count_extra_entries(struct exfat_entry_set_cache *es) +{ + struct exfat_dentry *stream; + unsigned int name_entries; + int extra; + + stream = exfat_get_dentry_cached(es, ES_IDX_STREAM); + name_entries = EXFAT_FILENAME_ENTRY_NUM(stream->dentry.stream.name_len); + extra = es->num_entries - (ES_IDX_FIRST_FILENAME + name_entries); + + return extra >= 0 ? extra : -EIO; +} + static int exfat_rename_file(struct inode *parent_inode, struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { @@ -976,6 +993,7 @@ static int exfat_rename_file(struct inode *parent_inode, struct super_block *sb = parent_inode->i_sb; struct exfat_entry_set_cache old_es, new_es; int sync = IS_DIRSYNC(parent_inode); + unsigned int num_extra_entries, num_total_entries; if (unlikely(exfat_forced_shutdown(sb))) return -EIO; @@ -985,19 +1003,23 @@ static int exfat_rename_file(struct inode *parent_inode, return num_new_entries; ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei); - if (ret) { - ret = -EIO; - return ret; - } + if (ret) + return -EIO; epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE); - if (old_es.num_entries < num_new_entries) { + ret = exfat_count_extra_entries(&old_es); + if (ret < 0) + goto put_old_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + + if (old_es.num_entries < num_total_entries) { int newentry; struct exfat_chain dir; newentry = exfat_find_empty_entry(parent_inode, &dir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_old_es; @@ -1014,13 +1036,23 @@ static int exfat_rename_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epold; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); ret = exfat_put_dentry_set(&new_es, sync); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, sb, + &dir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_old_es; + } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE); + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE, false); ei->dir = dir; ei->entry = newentry; } else { @@ -1029,8 +1061,8 @@ static int exfat_rename_file(struct inode *parent_inode, ei->attr |= EXFAT_ATTR_ARCHIVE; } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1); - exfat_init_ext_entry(&old_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&old_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); } return exfat_put_dentry_set(&old_es, sync); @@ -1046,6 +1078,7 @@ static int exfat_move_file(struct inode *parent_inode, struct exfat_dentry *epmov, *epnew; struct exfat_entry_set_cache mov_es, new_es; struct exfat_chain newdir; + unsigned int num_extra_entries, num_total_entries; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) @@ -1055,8 +1088,14 @@ static int exfat_move_file(struct inode *parent_inode, if (ret) return -EIO; + ret = exfat_count_extra_entries(&mov_es); + if (ret < 0) + goto put_mov_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + newentry = exfat_find_empty_entry(parent_inode, &newdir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_mov_es; @@ -1074,21 +1113,31 @@ static int exfat_move_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epmov; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); - exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &mov_es, num_extra_entries); + + exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE, false); ei->dir = newdir; ei->entry = newentry; ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode)); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, parent_inode->i_sb, + &newdir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_mov_es; + } return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode)); put_mov_es: exfat_put_dentry_set(&mov_es, false); - return ret; } @@ -1162,7 +1211,7 @@ static int __exfat_rename(struct inode *old_parent_inode, goto del_out; } - exfat_remove_entries(new_inode, &es, ES_IDX_FILE); + exfat_remove_entries(new_inode, &es, ES_IDX_FILE, true); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); if (ret) From 03a43677ca91e3997355a13b1da6e47329b025e1 Mon Sep 17 00:00:00 2001 From: Jan Polensky Date: Tue, 9 Jun 2026 18:21:49 +0900 Subject: [PATCH 3714/5214] exfat: add swap_activate support Commit 07d67f3e9083 ("exfat: add iomap buffered I/O support") converted exfat buffered I/O to iomap, but did not add a .swap_activate handler to the address_space_operations. swapon(2) on an exfat swapfile then fails with EINVAL, which causes LTP swap tests to fail. Add exfat_iomap_swap_activate() and hook it into exfat_aops so exfat uses iomap_swapfile_activate() for swapfile activation. Fixes: 614f71ca1bdf ("exfat: add iomap buffered I/O support") Closes: https://lore.kernel.org/all/20260603110212.3020276-1-japo@linux.ibm.com/ Signed-off-by: Jan Polensky Signed-off-by: Namjae Jeon --- fs/exfat/inode.c | 1 + fs/exfat/iomap.c | 6 ++++++ fs/exfat/iomap.h | 3 +++ 3 files changed, 10 insertions(+) diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 8e8d94319c3c..89826aea5e1e 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -309,6 +309,7 @@ static const struct address_space_operations exfat_aops = { .error_remove_folio = generic_error_remove_folio, .release_folio = iomap_release_folio, .invalidate_folio = iomap_invalidate_folio, + .swap_activate = exfat_iomap_swap_activate, }; static inline unsigned long exfat_hash(loff_t i_pos) diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index 3ac1eebe997f..1aac38e63fe6 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -263,3 +263,9 @@ const struct iomap_read_ops exfat_iomap_bio_read_ops = { .read_folio_range = iomap_bio_read_folio_range, .submit_read = exfat_iomap_bio_submit_read, }; + +int exfat_iomap_swap_activate(struct swap_info_struct *sis, + struct file *file, sector_t *span) +{ + return iomap_swapfile_activate(sis, file, span, &exfat_iomap_ops); +} diff --git a/fs/exfat/iomap.h b/fs/exfat/iomap.h index 830388f386f4..fd8a913f7794 100644 --- a/fs/exfat/iomap.h +++ b/fs/exfat/iomap.h @@ -12,4 +12,7 @@ extern const struct iomap_ops exfat_write_iomap_ops; extern const struct iomap_writeback_ops exfat_writeback_ops; extern const struct iomap_read_ops exfat_iomap_bio_read_ops; +int exfat_iomap_swap_activate(struct swap_info_struct *sis, + struct file *file, sector_t *span); + #endif /* _LINUX_EXFAT_IOMAP_H */ From 3a1230e7b043c62737b05a3e9275ca83a43ad20a Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 14:29:06 -0500 Subject: [PATCH 3715/5214] exfat: bound uniname advance in exfat_find_dir_entry() In exfat_find_dir_entry(), each TYPE_EXTEND (file name) entry advances the output pointer by a fixed amount while the loop guard only tracks the accumulated name length: if (++order == 2) uniname = p_uniname->name; else uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); name_len += len; unichar = *(uniname+len); *(uniname+len) = 0x0; uniname grows by EXFAT_FILE_NAME_LEN (15) per name entry, but name_len grows only by the actual extracted length, which is shorter when a name fragment contains an early NUL. The only guard is `name_len >= MAX_NAME_LENGTH`, so a crafted directory with many short name fragments lets uniname run far past the p_uniname->name[MAX_NAME_LENGTH + 3] buffer while name_len stays small, causing an out-of-bounds read and write at *(uniname+len). The sibling extractor exfat_get_uniname_from_ext_entry() already stops on a short fragment (the lockstep `len != EXFAT_FILE_NAME_LEN` guard added in commit d42334578eba ("exfat: check if filename entries exceeds max filename length")); exfat_find_dir_entry() never got the equivalent. Track the per-entry write offset as a count and reject a fragment once the offset, or the offset plus the extracted length, would exceed MAX_NAME_LENGTH, before forming the output pointer. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Suggested-by: Namjae Jeon Signed-off-by: Bryam Vargas Signed-off-by: Namjae Jeon --- fs/exfat/dir.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 5a85a8ec0cc0..4fd4ec52b8c0 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1069,6 +1069,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; + unsigned int offset; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { @@ -1077,13 +1078,15 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - if (++order == 2) - uniname = p_uniname->name; - else - uniname += EXFAT_FILE_NAME_LEN; - + offset = (++order - 2) * EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); brelse(bh); + if (offset > MAX_NAME_LENGTH || + len > MAX_NAME_LENGTH - offset) { + step = DIRENT_STEP_FILE; + continue; + } + uniname = p_uniname->name + offset; name_len += len; unichar = *(uniname+len); From 203890bbd29266cfff412cab607d2027bd2951c5 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:06 +0200 Subject: [PATCH 3716/5214] mm/slab: introduce slab_alloc_context Similarly to page allocator's struct alloc_context, introduce a helper struct to hold a part of the allocation arguments. This will allow reducing the number of parameters in many functions of the implementation, and extend them easily if needed. For now, make it hold the caller address and the originally requested allocation size. Convert alloc_single_from_new_slab(), __slab_alloc_node() and ___slab_alloc(). No functional change intended. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-4-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 8845e15cb152..8a0c5553876e 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -213,6 +213,12 @@ DEFINE_STATIC_KEY_FALSE(slub_debug_enabled); static DEFINE_STATIC_KEY_FALSE(strict_numa); #endif +/* Structure holding extra parameters for slab allocations */ +struct slab_alloc_context { + unsigned long caller_addr; + size_t orig_size; +}; + /* Structure holding parameters for get_from_partial() call chain */ struct partial_context { gfp_t flags; @@ -3687,7 +3693,8 @@ static inline void init_slab_obj_iter(struct kmem_cache *s, struct slab *slab, * and put the slab to the partial (or full) list. */ static void *alloc_single_from_new_slab(struct kmem_cache *s, struct slab *slab, - int orig_size, bool allow_spin) + const struct slab_alloc_context *ac, + bool allow_spin) { struct kmem_cache_node *n; struct slab_obj_iter iter; @@ -3705,7 +3712,7 @@ static void *alloc_single_from_new_slab(struct kmem_cache *s, struct slab *slab, /* alloc_debug_processing() always expects a valid freepointer */ set_freepointer(s, object, slab->freelist); - if (!alloc_debug_processing(s, slab, object, orig_size)) { + if (!alloc_debug_processing(s, slab, object, ac->orig_size)) { /* * It's not really expected that this would fail on a * freshly allocated slab, but a concurrent memory @@ -4443,7 +4450,7 @@ static unsigned int alloc_from_new_slab(struct kmem_cache *s, struct slab *slab, * slab. */ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, - unsigned long addr, unsigned int orig_size) + const struct slab_alloc_context *ac) { bool allow_spin = gfpflags_allow_spinning(gfpflags); void *object; @@ -4476,7 +4483,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, pc.flags = GFP_NOWAIT | __GFP_THISNODE; } - pc.orig_size = orig_size; + pc.orig_size = ac->orig_size; object = get_from_partial(s, node, &pc); if (object) goto success; @@ -4496,7 +4503,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, stat(s, ALLOC_SLAB); if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { - object = alloc_single_from_new_slab(s, slab, orig_size, allow_spin); + object = alloc_single_from_new_slab(s, slab, ac, allow_spin); if (likely(object)) goto success; @@ -4514,13 +4521,13 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, success: if (kmem_cache_debug_flags(s, SLAB_STORE_USER)) - set_track(s, object, TRACK_ALLOC, addr, gfpflags); + set_track(s, object, TRACK_ALLOC, ac->caller_addr, gfpflags); return object; } static void *__slab_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node, - unsigned long addr, size_t orig_size) + const struct slab_alloc_context *ac) { void *object; @@ -4545,7 +4552,7 @@ static void *__slab_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node, } #endif - object = ___slab_alloc(s, gfpflags, node, addr, orig_size); + object = ___slab_alloc(s, gfpflags, node, ac); return object; } @@ -4926,8 +4933,13 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list object = alloc_from_pcs(s, gfpflags, node); - if (unlikely(!object)) - object = __slab_alloc_node(s, gfpflags, node, addr, orig_size); + if (unlikely(!object)) { + const struct slab_alloc_context ac = { + .caller_addr = addr, + .orig_size = orig_size, + }; + object = __slab_alloc_node(s, gfpflags, node, &ac); + } maybe_wipe_obj_freeptr(s, object); @@ -5353,6 +5365,10 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in struct kmem_cache *s; bool can_retry = true; void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = orig_size, + }; VM_WARN_ON_ONCE(gfp_flags & ~(__GFP_ACCOUNT | __GFP_ZERO | __GFP_NO_OBJ_EXT)); @@ -5398,7 +5414,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in * kfence_alloc. Hence call __slab_alloc_node() (at most twice) * and slab_post_alloc_hook() directly. */ - ret = __slab_alloc_node(s, alloc_gfp, node, _RET_IP_, orig_size); + ret = __slab_alloc_node(s, alloc_gfp, node, &ac); /* * It's possible we failed due to trylock as we preempted someone with @@ -7240,10 +7256,13 @@ static bool __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, int i; if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = s->object_size, + }; for (i = 0; i < size; i++) { - p[i] = ___slab_alloc(s, flags, NUMA_NO_NODE, _RET_IP_, - s->object_size); + p[i] = ___slab_alloc(s, flags, NUMA_NO_NODE, &ac); if (unlikely(!p[i])) goto error; From 74d224ac9288f58a1bc15e86203011d5d806db10 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:07 +0200 Subject: [PATCH 3717/5214] mm/slab: introduce alloc_flags and SLAB_ALLOC_NOLOCK Similarly to the page allocators, introduce slab-allocator specific alloc flags that internally control allocation behavior in addition to gfp_flags, without occupying the limited gfp flags space. Introduce the first flag SLAB_ALLOC_NOLOCK that behaves similarly to page allocator's ALLOC_TRYLOCK and will be used to reimplement kmalloc_nolock()'s "!allow_spin" behavior. That currently relies on gfpflags_allow_spinning() and thus the lack of both __GFP_RECLAIM flags, importantly __GFP_KSWAPD_RECLAIM. This can give false-positive results e.g. in early boot with a restricted gfp_allowed_mask. Also introduce alloc_flags_allow_spinning() to replace the usage of gfpflags_allow_spinning(). Start using alloc_flags and the new check first in alloc_from_pcs() and __pcs_replace_empty_main(). This means some slab allocations that were falsely treated as kmalloc_nolock() due to their gfp flags will now have higher chances of success, and this will further increase with followup changes. Remove a WARN_ON_ONCE() from refill_objects() as it's now legitimate to reach it from a slab allocation that's not _nolock() and yet lacks __GFP_KSWAPD_RECLAIM for other reasons. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-5-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Signed-off-by: Vlastimil Babka (SUSE) --- mm/slab.h | 9 +++++++++ mm/slub.c | 17 ++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/mm/slab.h b/mm/slab.h index 1bf9c3021ae3..f1246f0c9f74 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -16,6 +16,15 @@ * Internal slab definitions */ +/* slab's alloc_flags definitions */ +#define SLAB_ALLOC_DEFAULT 0x00 /* no flags */ +#define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ + +static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) +{ + return !(alloc_flags & SLAB_ALLOC_NOLOCK); +} + #ifdef CONFIG_64BIT # ifdef system_has_cmpxchg128 # define system_has_freelist_aba() system_has_cmpxchg128() diff --git a/mm/slub.c b/mm/slub.c index 8a0c5553876e..c53592baa027 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4641,7 +4641,8 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, * unlocked. */ static struct slub_percpu_sheaves * -__pcs_replace_empty_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, gfp_t gfp) +__pcs_replace_empty_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, + gfp_t gfp, unsigned int alloc_flags) { struct slab_sheaf *empty = NULL; struct slab_sheaf *full; @@ -4667,7 +4668,7 @@ __pcs_replace_empty_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, return NULL; } - allow_spin = gfpflags_allow_spinning(gfp); + allow_spin = alloc_flags_allow_spinning(alloc_flags); full = barn_replace_empty_sheaf(barn, pcs->main, allow_spin); @@ -4753,7 +4754,7 @@ __pcs_replace_empty_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, } static __fastpath_inline -void *alloc_from_pcs(struct kmem_cache *s, gfp_t gfp, int node) +void *alloc_from_pcs(struct kmem_cache *s, gfp_t gfp, unsigned int alloc_flags, int node) { struct slub_percpu_sheaves *pcs; bool node_requested; @@ -4798,7 +4799,7 @@ void *alloc_from_pcs(struct kmem_cache *s, gfp_t gfp, int node) pcs = this_cpu_ptr(s->cpu_sheaves); if (unlikely(pcs->main->size == 0)) { - pcs = __pcs_replace_empty_main(s, pcs, gfp); + pcs = __pcs_replace_empty_main(s, pcs, gfp, alloc_flags); if (unlikely(!pcs)) return NULL; } @@ -4931,7 +4932,7 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list if (unlikely(object)) goto out; - object = alloc_from_pcs(s, gfpflags, node); + object = alloc_from_pcs(s, gfpflags, SLAB_ALLOC_DEFAULT, node); if (unlikely(!object)) { const struct slab_alloc_context ac = { @@ -5362,6 +5363,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in { gfp_t alloc_gfp = __GFP_NOWARN | __GFP_NOMEMALLOC | gfp_flags; size_t orig_size = size; + unsigned int alloc_flags = SLAB_ALLOC_NOLOCK; struct kmem_cache *s; bool can_retry = true; void *ret; @@ -5404,7 +5406,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in */ return NULL; - ret = alloc_from_pcs(s, alloc_gfp, node); + ret = alloc_from_pcs(s, alloc_gfp, alloc_flags, node); if (ret) goto success; @@ -7218,9 +7220,6 @@ refill_objects(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int min, unsigned int refilled; struct slab *slab; - if (WARN_ON_ONCE(!gfpflags_allow_spinning(gfp))) - return 0; - refilled = __refill_objects_node(s, p, gfp, min, max, get_node(s, local_node), /* allow_spin = */ true); From c90a004a9372a907c3ac57dd2499172a0a2a8f3d Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:09 +0200 Subject: [PATCH 3718/5214] mm/slab: replace struct partial_context with slab_alloc_context Refactor get_from_partial_node(), get_from_any_partial(), get_from_partial() and ___slab_alloc(). Remove struct partial_context, which used to be more substantial but shrank as part of the sheaves conversion. Instead pass gfp_flags and pointer to the new slab_alloc_context, which together is a superset of partial_context, and alloc_flags are about to be added to slab_alloc_context as well. No functional change intended. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-7-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 54 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index c53592baa027..6f6c15d796e1 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -219,12 +219,6 @@ struct slab_alloc_context { size_t orig_size; }; -/* Structure holding parameters for get_from_partial() call chain */ -struct partial_context { - gfp_t flags; - unsigned int orig_size; -}; - /* Structure holding parameters for get_partial_node_bulk() */ struct partial_bulk_context { gfp_t flags; @@ -3825,7 +3819,8 @@ static bool get_partial_node_bulk(struct kmem_cache *s, */ static void *get_from_partial_node(struct kmem_cache *s, struct kmem_cache_node *n, - struct partial_context *pc) + gfp_t gfp_flags, + const struct slab_alloc_context *ac) { struct slab *slab, *slab2; unsigned long flags; @@ -3840,7 +3835,7 @@ static void *get_from_partial_node(struct kmem_cache *s, if (!n || !n->nr_partial) return NULL; - if (gfpflags_allow_spinning(pc->flags)) + if (gfpflags_allow_spinning(gfp_flags)) spin_lock_irqsave(&n->list_lock, flags); else if (!spin_trylock_irqsave(&n->list_lock, flags)) return NULL; @@ -3848,12 +3843,12 @@ static void *get_from_partial_node(struct kmem_cache *s, struct freelist_counters old, new; - if (!pfmemalloc_match(slab, pc->flags)) + if (!pfmemalloc_match(slab, gfp_flags)) continue; if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { object = alloc_single_from_partial(s, n, slab, - pc->orig_size); + ac->orig_size); if (object) break; continue; @@ -3887,15 +3882,16 @@ static void *get_from_partial_node(struct kmem_cache *s, /* * Get an object from somewhere. Search in increasing NUMA distances. */ -static void *get_from_any_partial(struct kmem_cache *s, struct partial_context *pc) +static void *get_from_any_partial(struct kmem_cache *s, gfp_t gfp_flags, + const struct slab_alloc_context *ac) { #ifdef CONFIG_NUMA struct zonelist *zonelist; struct zoneref *z; struct zone *zone; - enum zone_type highest_zoneidx = gfp_zone(pc->flags); + enum zone_type highest_zoneidx = gfp_zone(gfp_flags); unsigned int cpuset_mems_cookie; - bool allow_spin = gfpflags_allow_spinning(pc->flags); + bool allow_spin = gfpflags_allow_spinning(gfp_flags); /* * The defrag ratio allows a configuration of the tradeoffs between @@ -3929,16 +3925,17 @@ static void *get_from_any_partial(struct kmem_cache *s, struct partial_context * if (allow_spin) cpuset_mems_cookie = read_mems_allowed_begin(); - zonelist = node_zonelist(mempolicy_slab_node(), pc->flags); + zonelist = node_zonelist(mempolicy_slab_node(), gfp_flags); for_each_zone_zonelist(zone, z, zonelist, highest_zoneidx) { struct kmem_cache_node *n; n = get_node(s, zone_to_nid(zone)); - if (n && cpuset_zone_allowed(zone, pc->flags) && + if (n && cpuset_zone_allowed(zone, gfp_flags) && n->nr_partial > s->min_partial) { - void *object = get_from_partial_node(s, n, pc); + void *object = get_from_partial_node(s, n, + gfp_flags, ac); if (object) { /* @@ -3960,8 +3957,8 @@ static void *get_from_any_partial(struct kmem_cache *s, struct partial_context * /* * Get an object from a partial slab */ -static void *get_from_partial(struct kmem_cache *s, int node, - struct partial_context *pc) +static void *get_from_partial(struct kmem_cache *s, int node, gfp_t flags, + const struct slab_alloc_context *ac) { int searchnode = node; void *object; @@ -3969,11 +3966,11 @@ static void *get_from_partial(struct kmem_cache *s, int node, if (node == NUMA_NO_NODE) searchnode = numa_mem_id(); - object = get_from_partial_node(s, get_node(s, searchnode), pc); - if (object || (node != NUMA_NO_NODE && (pc->flags & __GFP_THISNODE))) + object = get_from_partial_node(s, get_node(s, searchnode), flags, ac); + if (object || (node != NUMA_NO_NODE && (flags & __GFP_THISNODE))) return object; - return get_from_any_partial(s, pc); + return get_from_any_partial(s, flags, ac); } static bool has_pcs_used(int cpu, struct kmem_cache *s) @@ -4453,21 +4450,21 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, const struct slab_alloc_context *ac) { bool allow_spin = gfpflags_allow_spinning(gfpflags); + gfp_t trynode_flags; void *object; struct slab *slab; - struct partial_context pc; bool try_thisnode = true; stat(s, ALLOC_SLOWPATH); new_objects: - pc.flags = gfpflags; + trynode_flags = gfpflags; /* * When a preferred node is indicated but no __GFP_THISNODE * * 1) try to get a partial slab from target node only by having - * __GFP_THISNODE in pc.flags for get_from_partial() + * __GFP_THISNODE in trynode_flags for get_from_partial() * 2) if 1) failed, try to allocate a new slab from target node with * GPF_NOWAIT | __GFP_THISNODE opportunistically * 3) if 2) failed, retry with original gfpflags which will allow @@ -4478,17 +4475,16 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, && try_thisnode)) { if (unlikely(!allow_spin)) /* Do not upgrade gfp to NOWAIT from more restrictive mode */ - pc.flags = gfpflags | __GFP_THISNODE; + trynode_flags = gfpflags | __GFP_THISNODE; else - pc.flags = GFP_NOWAIT | __GFP_THISNODE; + trynode_flags = GFP_NOWAIT | __GFP_THISNODE; } - pc.orig_size = ac->orig_size; - object = get_from_partial(s, node, &pc); + object = get_from_partial(s, node, trynode_flags, ac); if (object) goto success; - slab = new_slab(s, pc.flags, node); + slab = new_slab(s, trynode_flags, node); if (unlikely(!slab)) { if (node != NUMA_NO_NODE && !(gfpflags & __GFP_THISNODE) From 574d3961d37ed25cac4fe3c5d497827d0384a0fd Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:08 +0200 Subject: [PATCH 3719/5214] mm/slab: add alloc_flags to slab_alloc_context Add alloc_flags as a new field to the slab_alloc_context helper struct, so we can pass it to more functions in the slab implementation without adding another function parameter. Start checking them via alloc_flags_allow_spinning() in alloc_single_from_new_slab() (where we can drop the allow_spin parameter), ___slab_alloc(), get_from_partial_node() and get_from_any_partial(). This further reduces false-positive spinning-not-allowed from allocations that are not kmalloc_nolock() but lack __GFP_RECLAIM flags. _kmalloc_nolock_noprof() initializes ac.alloc_flags using its flags that are SLAB_ALLOC_NOLOCK. slab_alloc_node() and __kmem_cache_alloc_bulk() are not reachable from kmalloc_nolock() and all their callers expect spinning to be allowed, so they can use SLAB_ALLOC_DEFAULT. This is temporary as the scope of slab_alloc_context will further move to the callers, making the alloc_flags usage more obvious. Also change how trynode_flags are constructed in ___slab_alloc() to achieve the same "do not upgrade to GFP_NOWAIT" by using masking instead of checking allow_spin. We need to do that because we now determine allow_spin from alloc_flags, and would otherwise start to upgrade e.g. kmalloc() allocations without __GFP_KSWAPD_RECLAIM (that however do allow spinning) to GFP_NOWAIT, thus including __GFP_KSWAPD_RECLAIM. During the masking keep also existing __GFP_NOMEMALLOC (pointed out by Sashiko) and __GFP_ACCOUNT. Previously the hardcoded GFP_NOWAIT would eliminate them, but it's not a big problem that would need a separate fix. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-6-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 6f6c15d796e1..3a34907b881b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -217,6 +217,7 @@ static DEFINE_STATIC_KEY_FALSE(strict_numa); struct slab_alloc_context { unsigned long caller_addr; size_t orig_size; + unsigned int alloc_flags; }; /* Structure holding parameters for get_partial_node_bulk() */ @@ -3687,9 +3688,9 @@ static inline void init_slab_obj_iter(struct kmem_cache *s, struct slab *slab, * and put the slab to the partial (or full) list. */ static void *alloc_single_from_new_slab(struct kmem_cache *s, struct slab *slab, - const struct slab_alloc_context *ac, - bool allow_spin) + const struct slab_alloc_context *ac) { + bool allow_spin = alloc_flags_allow_spinning(ac->alloc_flags); struct kmem_cache_node *n; struct slab_obj_iter iter; bool needs_add_partial; @@ -3835,7 +3836,7 @@ static void *get_from_partial_node(struct kmem_cache *s, if (!n || !n->nr_partial) return NULL; - if (gfpflags_allow_spinning(gfp_flags)) + if (alloc_flags_allow_spinning(ac->alloc_flags)) spin_lock_irqsave(&n->list_lock, flags); else if (!spin_trylock_irqsave(&n->list_lock, flags)) return NULL; @@ -3891,7 +3892,7 @@ static void *get_from_any_partial(struct kmem_cache *s, gfp_t gfp_flags, struct zone *zone; enum zone_type highest_zoneidx = gfp_zone(gfp_flags); unsigned int cpuset_mems_cookie; - bool allow_spin = gfpflags_allow_spinning(gfp_flags); + bool allow_spin = alloc_flags_allow_spinning(ac->alloc_flags); /* * The defrag ratio allows a configuration of the tradeoffs between @@ -4449,7 +4450,7 @@ static unsigned int alloc_from_new_slab(struct kmem_cache *s, struct slab *slab, static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, const struct slab_alloc_context *ac) { - bool allow_spin = gfpflags_allow_spinning(gfpflags); + bool allow_spin = alloc_flags_allow_spinning(ac->alloc_flags); gfp_t trynode_flags; void *object; struct slab *slab; @@ -4466,18 +4467,15 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, * 1) try to get a partial slab from target node only by having * __GFP_THISNODE in trynode_flags for get_from_partial() * 2) if 1) failed, try to allocate a new slab from target node with - * GPF_NOWAIT | __GFP_THISNODE opportunistically + * (at most) GFP_NOWAIT | __GFP_THISNODE opportunistically * 3) if 2) failed, retry with original gfpflags which will allow * get_from_partial() try partial lists of other nodes before * potentially allocating new page from other nodes */ if (unlikely(node != NUMA_NO_NODE && !(gfpflags & __GFP_THISNODE) && try_thisnode)) { - if (unlikely(!allow_spin)) - /* Do not upgrade gfp to NOWAIT from more restrictive mode */ - trynode_flags = gfpflags | __GFP_THISNODE; - else - trynode_flags = GFP_NOWAIT | __GFP_THISNODE; + trynode_flags &= GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_ACCOUNT; + trynode_flags |= __GFP_NOWARN | __GFP_THISNODE; } object = get_from_partial(s, node, trynode_flags, ac); @@ -4499,7 +4497,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, stat(s, ALLOC_SLAB); if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { - object = alloc_single_from_new_slab(s, slab, ac, allow_spin); + object = alloc_single_from_new_slab(s, slab, ac); if (likely(object)) goto success; @@ -4918,6 +4916,7 @@ unsigned int alloc_from_pcs_bulk(struct kmem_cache *s, gfp_t gfp, size_t size, static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) { + const unsigned int alloc_flags = SLAB_ALLOC_DEFAULT; void *object; s = slab_pre_alloc_hook(s, gfpflags); @@ -4928,12 +4927,13 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list if (unlikely(object)) goto out; - object = alloc_from_pcs(s, gfpflags, SLAB_ALLOC_DEFAULT, node); + object = alloc_from_pcs(s, gfpflags, alloc_flags, node); if (unlikely(!object)) { const struct slab_alloc_context ac = { .caller_addr = addr, .orig_size = orig_size, + .alloc_flags = alloc_flags, }; object = __slab_alloc_node(s, gfpflags, node, &ac); } @@ -5366,6 +5366,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in const struct slab_alloc_context ac = { .caller_addr = _RET_IP_, .orig_size = orig_size, + .alloc_flags = alloc_flags, }; VM_WARN_ON_ONCE(gfp_flags & ~(__GFP_ACCOUNT | __GFP_ZERO | @@ -7254,6 +7255,7 @@ static bool __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, const struct slab_alloc_context ac = { .caller_addr = _RET_IP_, .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, }; for (i = 0; i < size; i++) { From f39062e83cd52efd3e2a6773b8f5dfe3a8df9c9f Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:10 +0200 Subject: [PATCH 3720/5214] mm/slab: pass alloc_flags to new slab allocation Add the alloc_flags parameter to allocate_slab() and new_slab() so it can be used to determine if spinning is allowed, independently from gfp flags. refill_objects() passes SLAB_ALLOC_DEFAULT because it can only be reached from contexts that allow spinning. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-8-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 3a34907b881b..a975a2e727c8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3378,9 +3378,10 @@ static __always_inline void unaccount_slab(struct slab *slab, int order, } /* Allocate and initialize a slab without building its freelist. */ -static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, int node) +static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, + unsigned int alloc_flags, int node) { - bool allow_spin = gfpflags_allow_spinning(flags); + bool allow_spin = alloc_flags_allow_spinning(alloc_flags); struct slab *slab; struct kmem_cache_order_objects oo = s->oo; gfp_t alloc_gfp; @@ -3398,10 +3399,6 @@ static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, int node) if ((alloc_gfp & __GFP_DIRECT_RECLAIM) && oo_order(oo) > oo_order(s->min)) alloc_gfp = (alloc_gfp | __GFP_NOMEMALLOC) & ~__GFP_RECLAIM; - /* - * __GFP_RECLAIM could be cleared on the first allocation attempt, - * so pass allow_spin flag directly. - */ slab = alloc_slab_page(alloc_gfp, node, oo, allow_spin); if (unlikely(!slab)) { oo = s->min; @@ -3438,15 +3435,17 @@ static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, int node) return slab; } -static struct slab *new_slab(struct kmem_cache *s, gfp_t flags, int node) +static struct slab *new_slab(struct kmem_cache *s, gfp_t flags, + unsigned int alloc_flags, int node) { if (unlikely(flags & GFP_SLAB_BUG_MASK)) flags = kmalloc_fix_flags(flags); WARN_ON_ONCE(s->ctor && (flags & __GFP_ZERO)); - return allocate_slab(s, - flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node); + flags &= GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK; + + return allocate_slab(s, flags, alloc_flags, node); } static void __free_slab(struct kmem_cache *s, struct slab *slab, bool allow_spin) @@ -4482,7 +4481,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, if (object) goto success; - slab = new_slab(s, trynode_flags, node); + slab = new_slab(s, trynode_flags, ac->alloc_flags, node); if (unlikely(!slab)) { if (node != NUMA_NO_NODE && !(gfpflags & __GFP_THISNODE) @@ -7230,7 +7229,7 @@ refill_objects(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int min, new_slab: - slab = new_slab(s, gfp, local_node); + slab = new_slab(s, gfp, SLAB_ALLOC_DEFAULT, local_node); if (!slab) goto out; @@ -7578,7 +7577,7 @@ static void early_kmem_cache_node_alloc(int node) BUG_ON(kmem_cache_node->size < sizeof(struct kmem_cache_node)); - slab = new_slab(kmem_cache_node, GFP_NOWAIT, node); + slab = new_slab(kmem_cache_node, GFP_NOWAIT, SLAB_ALLOC_DEFAULT, node); BUG_ON(!slab); if (slab_nid(slab) != node) { From ef7f97aa2ce8d59eda0faa9bb40f6263f0d80d5f Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:11 +0200 Subject: [PATCH 3721/5214] mm/slab: pass alloc_flags through slab_post_alloc_hook() chain Convert the whole following call stack to pass either slab_alloc_context (thus including alloc_flags) or just alloc_flags as necessary: slab_post_alloc_hook() alloc_tagging_slab_alloc_hook() __alloc_tagging_slab_alloc_hook() prepare_slab_obj_exts_hook() alloc_slab_obj_exts() memcg_slab_post_alloc_hook() __memcg_slab_post_alloc_hook() alloc_slab_obj_exts() Converting all these at once avoids unnecessary churn and is mostly mechanical. This ultimately allows to decide if spinning is allowed using alloc_flags in alloc_slab_obj_exts(), as well as slab_post_alloc_hook(). Aside from alloc_from_pcs_bulk() (to be handled next) there is nothing else in slab itself relying on gfpflags_allow_spinning() which can be false even if not called from kmalloc_nolock(). A followup change will also use the alloc_flags availability in the call stack above to remove the __GFP_NO_OBJ_EXT flag. For alloc_slab_obj_exts(), also replace the suboptimal "bool new_slab" parameter with a SLAB_ALLOC_NEW_SLAB flag with identical functionality. To further reduce the number of parameters of slab_post_alloc_hook(), also make 'struct list_lru *lru' (which is NULL for most callers) a new field of slab_alloc_context. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-9-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) --- mm/memcontrol.c | 5 +-- mm/slab.h | 6 ++-- mm/slub.c | 94 +++++++++++++++++++++++++++++-------------------- 3 files changed, 62 insertions(+), 43 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c03d4787d466..29390ba13baa 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3424,7 +3424,8 @@ static inline size_t obj_full_size(struct kmem_cache *s) } bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, - gfp_t flags, size_t size, void **p) + gfp_t flags, unsigned int slab_alloc_flags, + size_t size, void **p) { size_t obj_size = obj_full_size(s); struct obj_cgroup *objcg; @@ -3472,7 +3473,7 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, slab = virt_to_slab(p[i]); if (!slab_obj_exts(slab) && - alloc_slab_obj_exts(slab, s, flags, false)) { + alloc_slab_obj_exts(slab, s, flags, slab_alloc_flags)) { continue; } diff --git a/mm/slab.h b/mm/slab.h index f1246f0c9f74..d86203131f58 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -19,6 +19,7 @@ /* slab's alloc_flags definitions */ #define SLAB_ALLOC_DEFAULT 0x00 /* no flags */ #define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ +#define SLAB_ALLOC_NEW_SLAB 0x02 /* a flag for alloc_slab_obj_exts() */ static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) { @@ -612,7 +613,7 @@ static inline struct slabobj_ext *slab_obj_ext(struct slab *slab, } int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - gfp_t gfp, bool new_slab); + gfp_t gfp, unsigned int alloc_flags); #else /* CONFIG_SLAB_OBJ_EXT */ @@ -642,7 +643,8 @@ static inline enum node_stat_item cache_vmstat_idx(struct kmem_cache *s) #ifdef CONFIG_MEMCG bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, - gfp_t flags, size_t size, void **p); + gfp_t flags, unsigned int slab_alloc_flags, + size_t size, void **p); void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p, int objects, unsigned long obj_exts); #endif diff --git a/mm/slub.c b/mm/slub.c index a975a2e727c8..465eb4db5770 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -218,6 +218,7 @@ struct slab_alloc_context { unsigned long caller_addr; size_t orig_size; unsigned int alloc_flags; + struct list_lru *lru; }; /* Structure holding parameters for get_partial_node_bulk() */ @@ -2155,9 +2156,9 @@ static inline size_t obj_exts_alloc_size(struct kmem_cache *s, } int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - gfp_t gfp, bool new_slab) + gfp_t gfp, unsigned int alloc_flags) { - bool allow_spin = gfpflags_allow_spinning(gfp); + const bool allow_spin = alloc_flags_allow_spinning(alloc_flags); unsigned int objects = objs_per_slab(s, slab); unsigned long new_exts; unsigned long old_exts; @@ -2206,7 +2207,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, old_exts = READ_ONCE(slab->obj_exts); handle_failed_objexts_alloc(old_exts, vec, objects); - if (new_slab) { + if (alloc_flags & SLAB_ALLOC_NEW_SLAB) { /* * If the slab is brand new and nobody can yet access its * obj_exts, no synchronization is required and obj_exts can @@ -2331,7 +2332,7 @@ static inline void init_slab_obj_exts(struct slab *slab) } static int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - gfp_t gfp, bool new_slab) + gfp_t gfp, unsigned int alloc_flags) { return 0; } @@ -2351,10 +2352,10 @@ static inline void alloc_slab_obj_exts_early(struct kmem_cache *s, static inline unsigned long prepare_slab_obj_exts_hook(struct kmem_cache *s, struct slab *slab, - gfp_t flags, void *p) + gfp_t flags, unsigned int alloc_flags, void *p) { if (!slab_obj_exts(slab) && - alloc_slab_obj_exts(slab, s, flags, false)) { + alloc_slab_obj_exts(slab, s, flags, alloc_flags)) { pr_warn_once("%s, %s: Failed to create slab extension vector!\n", __func__, s->name); return 0; @@ -2366,7 +2367,8 @@ prepare_slab_obj_exts_hook(struct kmem_cache *s, struct slab *slab, /* Should be called only if mem_alloc_profiling_enabled() */ static noinline void -__alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) +__alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags, + unsigned int alloc_flags) { unsigned long obj_exts; struct slabobj_ext *obj_ext; @@ -2382,7 +2384,7 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) return; slab = virt_to_slab(object); - obj_exts = prepare_slab_obj_exts_hook(s, slab, flags, object); + obj_exts = prepare_slab_obj_exts_hook(s, slab, flags, alloc_flags, object); /* * Currently obj_exts is used only for allocation profiling. * If other users appear then mem_alloc_profiling_enabled() @@ -2401,10 +2403,11 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) } static inline void -alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) +alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags, + unsigned int alloc_flags) { if (mem_alloc_profiling_enabled()) - __alloc_tagging_slab_alloc_hook(s, object, flags); + __alloc_tagging_slab_alloc_hook(s, object, flags, alloc_flags); } /* Should be called only if mem_alloc_profiling_enabled() */ @@ -2443,7 +2446,8 @@ alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p, #else /* CONFIG_MEM_ALLOC_PROFILING */ static inline void -alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) +alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags, + unsigned int alloc_flags) { } @@ -2461,8 +2465,9 @@ alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p, static void memcg_alloc_abort_single(struct kmem_cache *s, void *object); static __fastpath_inline -bool memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, - gfp_t flags, size_t size, void **p) +bool memcg_slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, + size_t size, void **p, + const struct slab_alloc_context *ac) { if (likely(!memcg_kmem_online())) return true; @@ -2470,7 +2475,8 @@ bool memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, if (likely(!(flags & __GFP_ACCOUNT) && !(s->flags & SLAB_ACCOUNT))) return true; - if (likely(__memcg_slab_post_alloc_hook(s, lru, flags, size, p))) + if (likely(__memcg_slab_post_alloc_hook(s, ac->lru, flags, + ac->alloc_flags, size, p))) return true; if (likely(size == 1)) { @@ -2558,14 +2564,15 @@ bool memcg_slab_post_charge(void *p, gfp_t flags) put_slab_obj_exts(obj_exts); } - return __memcg_slab_post_alloc_hook(s, NULL, flags, 1, &p); + return __memcg_slab_post_alloc_hook(s, NULL, flags, SLAB_ALLOC_DEFAULT, + 1, &p); } #else /* CONFIG_MEMCG */ static inline bool memcg_slab_post_alloc_hook(struct kmem_cache *s, - struct list_lru *lru, - gfp_t flags, size_t size, - void **p) + gfp_t flags, + size_t size, void **p, + const struct slab_alloc_context *ac) { return true; } @@ -3352,12 +3359,14 @@ static inline void init_freelist_randomization(void) { } #endif /* CONFIG_SLAB_FREELIST_RANDOM */ static __always_inline void account_slab(struct slab *slab, int order, - struct kmem_cache *s, gfp_t gfp) + struct kmem_cache *s, gfp_t gfp, + unsigned int alloc_flags) { if (memcg_kmem_online() && (s->flags & SLAB_ACCOUNT) && !slab_obj_exts(slab)) - alloc_slab_obj_exts(slab, s, gfp, true); + alloc_slab_obj_exts(slab, s, gfp, + alloc_flags | SLAB_ALLOC_NEW_SLAB); mod_node_page_state(slab_pgdat(slab), cache_vmstat_idx(s), PAGE_SIZE << order); @@ -3430,7 +3439,7 @@ static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, * to prevent the array from being overwritten. */ alloc_slab_obj_exts_early(s, slab); - account_slab(slab, oo_order(oo), s, flags); + account_slab(slab, oo_order(oo), s, flags, alloc_flags); return slab; } @@ -4564,9 +4573,8 @@ struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags) } static __fastpath_inline -bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, - gfp_t flags, size_t size, void **p, - unsigned int orig_size) +bool slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, size_t size, + void **p, const struct slab_alloc_context *ac) { bool init = slab_want_init_on_alloc(flags, s); unsigned int zero_size = s->object_size; @@ -4585,7 +4593,7 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, * orig_size if we track it. */ if (slub_debug_orig_size(s)) - zero_size = orig_size; + zero_size = ac->orig_size; /* * ARM64 can set memory tags and zero the memory using a single @@ -4615,14 +4623,14 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, if (init && p[i] && !is_kfence_address(p[i])) memset(p[i], 0, zero_size); - if (gfpflags_allow_spinning(flags)) + if (alloc_flags_allow_spinning(ac->alloc_flags)) kmemleak_alloc_recursive(p[i], s->object_size, 1, s->flags, init_flags); kmsan_slab_alloc(s, p[i], init_flags); - alloc_tagging_slab_alloc_hook(s, p[i], flags); + alloc_tagging_slab_alloc_hook(s, p[i], flags, ac->alloc_flags); } - return memcg_slab_post_alloc_hook(s, lru, flags, size, p); + return memcg_slab_post_alloc_hook(s, flags, size, p, ac); } /* @@ -4917,6 +4925,12 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list { const unsigned int alloc_flags = SLAB_ALLOC_DEFAULT; void *object; + const struct slab_alloc_context ac = { + .caller_addr = addr, + .orig_size = orig_size, + .alloc_flags = alloc_flags, + .lru = lru, + }; s = slab_pre_alloc_hook(s, gfpflags); if (unlikely(!s)) @@ -4928,14 +4942,8 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list object = alloc_from_pcs(s, gfpflags, alloc_flags, node); - if (unlikely(!object)) { - const struct slab_alloc_context ac = { - .caller_addr = addr, - .orig_size = orig_size, - .alloc_flags = alloc_flags, - }; + if (unlikely(!object)) object = __slab_alloc_node(s, gfpflags, node, &ac); - } maybe_wipe_obj_freeptr(s, object); @@ -4944,7 +4952,7 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list * In case this fails due to memcg_slab_post_alloc_hook(), * object is set to NULL */ - slab_post_alloc_hook(s, lru, gfpflags, 1, &object, orig_size); + slab_post_alloc_hook(s, gfpflags, 1, &object, &ac); return object; } @@ -5239,6 +5247,10 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache *s, gfp_t gfp, struct slab_sheaf *sheaf) { void *ret = NULL; + const struct slab_alloc_context ac = { + .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; if (sheaf->size == 0) goto out; @@ -5249,7 +5261,7 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache *s, gfp_t gfp, ret = sheaf->objects[--sheaf->size]; /* add __GFP_NOFAIL to force successful memcg charging */ - slab_post_alloc_hook(s, NULL, gfp | __GFP_NOFAIL, 1, &ret, s->object_size); + slab_post_alloc_hook(s, gfp | __GFP_NOFAIL, 1, &ret, &ac); out: trace_kmem_cache_alloc(_RET_IP_, ret, s, gfp, NUMA_NO_NODE); @@ -5435,7 +5447,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in success: maybe_wipe_obj_freeptr(s, ret); - slab_post_alloc_hook(s, NULL, alloc_gfp, 1, &ret, orig_size); + slab_post_alloc_hook(s, alloc_gfp, 1, &ret, &ac); ret = kasan_kmalloc(s, ret, orig_size, alloc_gfp); return ret; @@ -7301,6 +7313,10 @@ bool kmem_cache_alloc_bulk_noprof(struct kmem_cache *s, gfp_t flags, { unsigned int i = 0; void *kfence_obj; + const struct slab_alloc_context ac = { + .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; if (!size) return false; @@ -7351,7 +7367,7 @@ bool kmem_cache_alloc_bulk_noprof(struct kmem_cache *s, gfp_t flags, out: /* memcg and kmem_cache debug support and memory initialization */ - return likely(slab_post_alloc_hook(s, NULL, flags, size, p, s->object_size)); + return likely(slab_post_alloc_hook(s, flags, size, p, &ac)); } EXPORT_SYMBOL(kmem_cache_alloc_bulk_noprof); From b5ecc070d1962a130ca5acc3a40b515035e5d21f Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:12 +0200 Subject: [PATCH 3722/5214] mm/slab: replace slab_alloc_node() parameters with slab_alloc_context The function takes all the parameters that exist as fields in slab_alloc_context, except alloc_flags. Replace them with a single pointer. This moves slab_alloc_context initialization to a number of callers, which is more verbose, but arguably also more clear than a long list of parameters, and most do not use the 'lru' field. This will also allow kmalloc_nolock() to call slab_alloc_node() and reduce the special open-coding it currently has. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-10-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 75 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 465eb4db5770..562495b80d74 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4920,30 +4920,23 @@ unsigned int alloc_from_pcs_bulk(struct kmem_cache *s, gfp_t gfp, size_t size, * * Otherwise we can simply pick the next object from the lockless free list. */ -static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru, - gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) +static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, + gfp_t gfpflags, int node, const struct slab_alloc_context *ac) { - const unsigned int alloc_flags = SLAB_ALLOC_DEFAULT; void *object; - const struct slab_alloc_context ac = { - .caller_addr = addr, - .orig_size = orig_size, - .alloc_flags = alloc_flags, - .lru = lru, - }; s = slab_pre_alloc_hook(s, gfpflags); if (unlikely(!s)) return NULL; - object = kfence_alloc(s, orig_size, gfpflags); + object = kfence_alloc(s, ac->orig_size, gfpflags); if (unlikely(object)) goto out; - object = alloc_from_pcs(s, gfpflags, alloc_flags, node); + object = alloc_from_pcs(s, gfpflags, ac->alloc_flags, node); if (unlikely(!object)) - object = __slab_alloc_node(s, gfpflags, node, &ac); + object = __slab_alloc_node(s, gfpflags, node, ac); maybe_wipe_obj_freeptr(s, object); @@ -4952,15 +4945,21 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list * In case this fails due to memcg_slab_post_alloc_hook(), * object is set to NULL */ - slab_post_alloc_hook(s, gfpflags, 1, &object, &ac); + slab_post_alloc_hook(s, gfpflags, 1, &object, ac); return object; } void *kmem_cache_alloc_noprof(struct kmem_cache *s, gfp_t gfpflags) { - void *ret = slab_alloc_node(s, NULL, gfpflags, NUMA_NO_NODE, _RET_IP_, - s->object_size); + void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + ret = slab_alloc_node(s, gfpflags, NUMA_NO_NODE, &ac); trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, NUMA_NO_NODE); @@ -4971,8 +4970,15 @@ EXPORT_SYMBOL(kmem_cache_alloc_noprof); void *kmem_cache_alloc_lru_noprof(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags) { - void *ret = slab_alloc_node(s, lru, gfpflags, NUMA_NO_NODE, _RET_IP_, - s->object_size); + void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + .lru = lru, + }; + + ret = slab_alloc_node(s, gfpflags, NUMA_NO_NODE, &ac); trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, NUMA_NO_NODE); @@ -5004,7 +5010,14 @@ EXPORT_SYMBOL(kmem_cache_charge); */ void *kmem_cache_alloc_node_noprof(struct kmem_cache *s, gfp_t gfpflags, int node) { - void *ret = slab_alloc_node(s, NULL, gfpflags, node, _RET_IP_, s->object_size); + void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = s->object_size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + ret = slab_alloc_node(s, gfpflags, node, &ac); trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, node); @@ -5334,6 +5347,11 @@ void *__do_kmalloc_node(size_t size, kmem_buckets *b, gfp_t flags, int node, { struct kmem_cache *s; void *ret; + const struct slab_alloc_context ac = { + .caller_addr = caller, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) { ret = __kmalloc_large_node_noprof(size, flags, node); @@ -5347,7 +5365,7 @@ void *__do_kmalloc_node(size_t size, kmem_buckets *b, gfp_t flags, int node, s = kmalloc_slab(size, b, flags, token); - ret = slab_alloc_node(s, NULL, flags, node, caller, size); + ret = slab_alloc_node(s, flags, node, &ac); ret = kasan_kmalloc(s, ret, size, flags); trace_kmalloc(caller, ret, size, s->size, flags, node); return ret; @@ -5465,8 +5483,14 @@ EXPORT_SYMBOL(__kmalloc_node_track_caller_noprof); void *__kmalloc_cache_noprof(struct kmem_cache *s, gfp_t gfpflags, size_t size) { - void *ret = slab_alloc_node(s, NULL, gfpflags, NUMA_NO_NODE, - _RET_IP_, size); + void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + ret = slab_alloc_node(s, gfpflags, NUMA_NO_NODE, &ac); trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, NUMA_NO_NODE); @@ -5478,7 +5502,14 @@ EXPORT_SYMBOL(__kmalloc_cache_noprof); void *__kmalloc_cache_node_noprof(struct kmem_cache *s, gfp_t gfpflags, int node, size_t size) { - void *ret = slab_alloc_node(s, NULL, gfpflags, node, _RET_IP_, size); + void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + ret = slab_alloc_node(s, gfpflags, node, &ac); trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, node); From 1a787779fe2a9b5d042b5b54f3580eb3802404c4 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:13 +0200 Subject: [PATCH 3723/5214] mm/slab: allow kmem_cache_alloc_bulk() with any gfp flags The last user of gfpflags_allow_spinning() in slab is alloc_from_pcs_bulk(), which is only called from kmem_cache_alloc_bulk(). It turns out that gfpflags_allow_spinning() is not necessary, because kmem_cache_alloc_bulk() is only expected to be called from context that does allow spinning, so simply replace it with 'true'. This means we can also drop the gfp parameter from alloc_from_pcs_bulk(). With that, we can remove the "@flags must allow spinning" part of the kernel doc, as there is no more connection to the gfp flags in the slab implementation. Also remove a comment in alloc_slab_obj_exts() because there should be no more false positives possible due to gfp_allowed_mask during early boot. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-11-7190909db118@kernel.org Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 562495b80d74..81938774098b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2171,12 +2171,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, sz = obj_exts_alloc_size(s, slab, gfp); - /* - * Note that allow_spin may be false during early boot and its - * restricted GFP_BOOT_MASK. Due to kmalloc_nolock() only supporting - * architectures with cmpxchg16b, early obj_exts will be missing for - * very early allocations on those. - */ if (unlikely(!allow_spin)) vec = kmalloc_nolock(sz, __GFP_ZERO | __GFP_NO_OBJ_EXT, slab_nid(slab)); @@ -4830,8 +4824,7 @@ void *alloc_from_pcs(struct kmem_cache *s, gfp_t gfp, unsigned int alloc_flags, } static __fastpath_inline -unsigned int alloc_from_pcs_bulk(struct kmem_cache *s, gfp_t gfp, size_t size, - void **p) +unsigned int alloc_from_pcs_bulk(struct kmem_cache *s, size_t size, void **p) { struct slub_percpu_sheaves *pcs; struct slab_sheaf *main; @@ -4866,7 +4859,7 @@ unsigned int alloc_from_pcs_bulk(struct kmem_cache *s, gfp_t gfp, size_t size, } full = barn_replace_empty_sheaf(barn, pcs->main, - gfpflags_allow_spinning(gfp)); + /* allow_spin = */ true); if (full) { stat(s, BARN_GET); @@ -7331,8 +7324,7 @@ static bool __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, * Allocate @size objects from @s and places them into @p. @size must be larger * than 0. * - * Interrupts must be enabled when calling this function and @flags must allow - * spinning. + * Interrupts must be enabled when calling this function. * * Unlike alloc_pages_bulk(), this function does not check for already allocated * objects in @p, and thus the caller does not need to zero it. @@ -7370,7 +7362,7 @@ bool kmem_cache_alloc_bulk_noprof(struct kmem_cache *s, gfp_t flags, size--; } - i = alloc_from_pcs_bulk(s, flags, size, p); + i = alloc_from_pcs_bulk(s, size, p); if (i < size) { /* * If we ran out of memory, don't bother with freeing back to From d659903674af9d29b6bbde1768004da9cb99d676 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:14 +0200 Subject: [PATCH 3724/5214] mm/slab: pass slab_alloc_context to __do_kmalloc_node() With alloc_flags usage in slab, we can replace __GFP_NO_OBJ_EXT with an alloc flag that prevents kmalloc recursion. For that we need a version of kmalloc() that takes alloc_flags and use it in places that perform these potentially recursive kmalloc allocations (of sheaves or obj_ext arrays). As a preparatory step, make __do_kmalloc_node() take a pointer to slab_alloc_context. This replaces the 'size' and 'caller' parameters and includes alloc_flags which we'll make use of. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-12-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 54 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 81938774098b..537ea68f417b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5335,20 +5335,16 @@ void *__kmalloc_large_node_noprof(size_t size, gfp_t flags, int node) EXPORT_SYMBOL(__kmalloc_large_node_noprof); static __always_inline -void *__do_kmalloc_node(size_t size, kmem_buckets *b, gfp_t flags, int node, - unsigned long caller, kmalloc_token_t token) +void *__do_kmalloc_node(kmem_buckets *b, gfp_t flags, int node, + kmalloc_token_t token, const struct slab_alloc_context *ac) { + const size_t size = ac->orig_size; struct kmem_cache *s; void *ret; - const struct slab_alloc_context ac = { - .caller_addr = caller, - .orig_size = size, - .alloc_flags = SLAB_ALLOC_DEFAULT, - }; if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) { ret = __kmalloc_large_node_noprof(size, flags, node); - trace_kmalloc(caller, ret, size, + trace_kmalloc(ac->caller_addr, ret, size, PAGE_SIZE << get_order(size), flags, node); return ret; } @@ -5358,22 +5354,34 @@ void *__do_kmalloc_node(size_t size, kmem_buckets *b, gfp_t flags, int node, s = kmalloc_slab(size, b, flags, token); - ret = slab_alloc_node(s, flags, node, &ac); + ret = slab_alloc_node(s, flags, node, ac); ret = kasan_kmalloc(s, ret, size, flags); - trace_kmalloc(caller, ret, size, s->size, flags, node); + trace_kmalloc(ac->caller_addr, ret, size, s->size, flags, node); return ret; } void *__kmalloc_node_noprof(DECL_KMALLOC_PARAMS(size, b, token), gfp_t flags, int node) { - return __do_kmalloc_node(size, PASS_BUCKET_PARAM(b), flags, node, - _RET_IP_, PASS_TOKEN_PARAM(token)); + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + return __do_kmalloc_node(PASS_BUCKET_PARAM(b), flags, node, + PASS_TOKEN_PARAM(token), &ac); } EXPORT_SYMBOL(__kmalloc_node_noprof); void *__kmalloc_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t flags) { - return __do_kmalloc_node(size, NULL, flags, NUMA_NO_NODE, _RET_IP_, - PASS_TOKEN_PARAM(token)); + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + + return __do_kmalloc_node(NULL, flags, NUMA_NO_NODE, + PASS_TOKEN_PARAM(token), &ac); } EXPORT_SYMBOL(__kmalloc_noprof); @@ -5468,9 +5476,14 @@ EXPORT_SYMBOL_GPL(_kmalloc_nolock_noprof); void *__kmalloc_node_track_caller_noprof(DECL_KMALLOC_PARAMS(size, b, token), gfp_t flags, int node, unsigned long caller) { - return __do_kmalloc_node(size, PASS_BUCKET_PARAM(b), flags, node, - caller, PASS_TOKEN_PARAM(token)); + const struct slab_alloc_context ac = { + .caller_addr = caller, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; + return __do_kmalloc_node(PASS_BUCKET_PARAM(b), flags, node, + PASS_TOKEN_PARAM(token), &ac); } EXPORT_SYMBOL(__kmalloc_node_track_caller_noprof); @@ -6871,14 +6884,19 @@ void *__kvmalloc_node_noprof(DECL_KMALLOC_PARAMS(size, b, token), unsigned long { bool allow_block; void *ret; + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_DEFAULT, + }; /* * It doesn't really make sense to fallback to vmalloc for sub page * requests */ - ret = __do_kmalloc_node(size, PASS_BUCKET_PARAM(b), + ret = __do_kmalloc_node(PASS_BUCKET_PARAM(b), kmalloc_gfp_adjust(flags, size), - node, _RET_IP_, PASS_TOKEN_PARAM(token)); + node, PASS_TOKEN_PARAM(token), &ac); if (ret || size <= PAGE_SIZE) return ret; From 6dbded46e447113da97bed4a68b02b8aaaffa995 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:15 +0200 Subject: [PATCH 3725/5214] mm/slab: allow __GFP_NOMEMALLOC and __GFP_NOWARN for kmalloc_nolock() The two flags are added internally so there's no point for warning if they are passed by the caller as well, so allow them. This will allow simplifying obj_ext allocation under kmalloc_nolock(). Also it's not necessary to have the extra alloc_gfp variable for adding the two flags. The original gfp_flags parameter is not used anywhere except for the warning. So remove alloc_gfp and directly modify and use gfp_flags everywhere. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-13-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 3 ++- mm/slub.c | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index ce1c867dc0ba..b955f3cbb732 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1040,7 +1040,8 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in * kmalloc_nolock - Allocate an object of given size from any context. * @size: size to allocate * @gfp_flags: GFP flags. Only __GFP_ACCOUNT, __GFP_ZERO, __GFP_NO_OBJ_EXT - * allowed. + * allowed. Also __GFP_NOWARN and __GFP_NOMEMALLOC are allowed but added + * internally thus not necessary. * @node: node number of the target node. * * Return: pointer to the new object or NULL in case of error. diff --git a/mm/slub.c b/mm/slub.c index 537ea68f417b..8769083bec81 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5387,7 +5387,6 @@ EXPORT_SYMBOL(__kmalloc_noprof); void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, int node) { - gfp_t alloc_gfp = __GFP_NOWARN | __GFP_NOMEMALLOC | gfp_flags; size_t orig_size = size; unsigned int alloc_flags = SLAB_ALLOC_NOLOCK; struct kmem_cache *s; @@ -5400,7 +5399,9 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in }; VM_WARN_ON_ONCE(gfp_flags & ~(__GFP_ACCOUNT | __GFP_ZERO | - __GFP_NO_OBJ_EXT)); + __GFP_NO_OBJ_EXT | __GFP_NOWARN | __GFP_NOMEMALLOC)); + + gfp_flags |= __GFP_NOWARN | __GFP_NOMEMALLOC; if (unlikely(!size)) return ZERO_SIZE_PTR; @@ -5419,7 +5420,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in retry: if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return NULL; - s = kmalloc_slab(size, NULL, alloc_gfp, PASS_TOKEN_PARAM(token)); + s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token)); if (!(s->flags & __CMPXCHG_DOUBLE) && !kmem_cache_debug(s)) /* @@ -5433,7 +5434,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in */ return NULL; - ret = alloc_from_pcs(s, alloc_gfp, alloc_flags, node); + ret = alloc_from_pcs(s, gfp_flags, alloc_flags, node); if (ret) goto success; @@ -5443,7 +5444,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in * kfence_alloc. Hence call __slab_alloc_node() (at most twice) * and slab_post_alloc_hook() directly. */ - ret = __slab_alloc_node(s, alloc_gfp, node, &ac); + ret = __slab_alloc_node(s, gfp_flags, node, &ac); /* * It's possible we failed due to trylock as we preempted someone with @@ -5456,8 +5457,8 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in size = s->object_size + 1; /* * Another alternative is to - * if (memcg) alloc_gfp &= ~__GFP_ACCOUNT; - * else if (!memcg) alloc_gfp |= __GFP_ACCOUNT; + * if (memcg) gfp_flags &= ~__GFP_ACCOUNT; + * else if (!memcg) gfp_flags |= __GFP_ACCOUNT; * to retry from bucket of the same size. */ can_retry = false; @@ -5466,9 +5467,9 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in success: maybe_wipe_obj_freeptr(s, ret); - slab_post_alloc_hook(s, alloc_gfp, 1, &ret, &ac); + slab_post_alloc_hook(s, gfp_flags, 1, &ret, &ac); - ret = kasan_kmalloc(s, ret, orig_size, alloc_gfp); + ret = kasan_kmalloc(s, ret, orig_size, gfp_flags); return ret; } EXPORT_SYMBOL_GPL(_kmalloc_nolock_noprof); From f6d50ab29afd7502f5f74d9814908b93f96f47dd Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:16 +0200 Subject: [PATCH 3726/5214] mm/slab: introduce kmalloc_flags() With alloc_flags usage in slab, we can replace __GFP_NO_OBJ_EXT with an alloc flag that prevents kmalloc recursion. For that we need a version of kmalloc() that takes alloc_flags and use it in places that perform these potentially recursive kmalloc allocations (of sheaves or obj_ext arrays). Add this function, named kmalloc_flags(). Right now it's only useful for these nested allocations, so it doesn't need to optimize build-time constant sizes like kmalloc() or kmalloc_buckets. Since we need it to support both normal and non-spinning kmalloc_nolock() context through the SLAB_ALLOC_NOLOCK flag, split out most of the special _kmalloc_nolock_noprof() implementation to __kmalloc_nolock_noprof() that takes a slab_alloc_context, and make _kmalloc_nolock_noprof() a simple tail calling wrapper with the proper context. kmalloc_flags() can thus determine whether to call __kmalloc_nolock_noprof() or __do_kmalloc_node(), based on the given alloc_flags. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-14-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Suren Baghdasaryan Reviewed-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- mm/slab.h | 13 +++++++++++++ mm/slub.c | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/mm/slab.h b/mm/slab.h index d86203131f58..482b8e0fe797 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -11,6 +11,7 @@ #include #include #include +#include /* * Internal slab definitions @@ -26,6 +27,18 @@ static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) return !(alloc_flags & SLAB_ALLOC_NOLOCK); } +void *__kmalloc_flags_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t flags, + unsigned int alloc_flags, int node) + __assume_kmalloc_alignment __alloc_size(1); + +static __always_inline __alloc_size(1) void *_kmalloc_flags_noprof(size_t size, + gfp_t flags, unsigned int alloc_flags, int node, kmalloc_token_t token) +{ + return __kmalloc_flags_noprof(PASS_TOKEN_PARAMS(size, token), flags, alloc_flags, node); +} +#define kmalloc_flags_noprof(...) _kmalloc_flags_noprof(__VA_ARGS__, __kmalloc_token(__VA_ARGS__)) +#define kmalloc_flags(...) alloc_hooks(kmalloc_flags_noprof(__VA_ARGS__)) + #ifdef CONFIG_64BIT # ifdef system_has_cmpxchg128 # define system_has_freelist_aba() system_has_cmpxchg128() diff --git a/mm/slub.c b/mm/slub.c index 8769083bec81..383d39a22561 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5385,19 +5385,14 @@ void *__kmalloc_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t flags) } EXPORT_SYMBOL(__kmalloc_noprof); -void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, int node) +static void *__kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, + int node, const struct slab_alloc_context *ac) { - size_t orig_size = size; - unsigned int alloc_flags = SLAB_ALLOC_NOLOCK; struct kmem_cache *s; bool can_retry = true; void *ret; - const struct slab_alloc_context ac = { - .caller_addr = _RET_IP_, - .orig_size = orig_size, - .alloc_flags = alloc_flags, - }; + VM_WARN_ON_ONCE(alloc_flags_allow_spinning(ac->alloc_flags)); VM_WARN_ON_ONCE(gfp_flags & ~(__GFP_ACCOUNT | __GFP_ZERO | __GFP_NO_OBJ_EXT | __GFP_NOWARN | __GFP_NOMEMALLOC)); @@ -5434,7 +5429,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in */ return NULL; - ret = alloc_from_pcs(s, gfp_flags, alloc_flags, node); + ret = alloc_from_pcs(s, gfp_flags, ac->alloc_flags, node); if (ret) goto success; @@ -5444,7 +5439,7 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in * kfence_alloc. Hence call __slab_alloc_node() (at most twice) * and slab_post_alloc_hook() directly. */ - ret = __slab_alloc_node(s, gfp_flags, node, &ac); + ret = __slab_alloc_node(s, gfp_flags, node, ac); /* * It's possible we failed due to trylock as we preempted someone with @@ -5467,11 +5462,23 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in success: maybe_wipe_obj_freeptr(s, ret); - slab_post_alloc_hook(s, gfp_flags, 1, &ret, &ac); + slab_post_alloc_hook(s, gfp_flags, 1, &ret, ac); - ret = kasan_kmalloc(s, ret, orig_size, gfp_flags); + ret = kasan_kmalloc(s, ret, ac->orig_size, gfp_flags); return ret; } + +void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, int node) +{ + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = SLAB_ALLOC_NOLOCK, + }; + + return __kmalloc_nolock_noprof(PASS_TOKEN_PARAMS(size, token), + gfp_flags, node, &ac); +} EXPORT_SYMBOL_GPL(_kmalloc_nolock_noprof); void *__kmalloc_node_track_caller_noprof(DECL_KMALLOC_PARAMS(size, b, token), gfp_t flags, @@ -5525,6 +5532,30 @@ void *__kmalloc_cache_node_noprof(struct kmem_cache *s, gfp_t gfpflags, } EXPORT_SYMBOL(__kmalloc_cache_node_noprof); +/* + * The only version of kmalloc_node() that takes alloc_flags and thus can + * determine on its own whether to handle the allocation via kmalloc_nolock() or + * normally + */ +void *__kmalloc_flags_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t flags, + unsigned int alloc_flags, int node) +{ + const struct slab_alloc_context ac = { + .caller_addr = _RET_IP_, + .orig_size = size, + .alloc_flags = alloc_flags, + }; + + if (alloc_flags_allow_spinning(alloc_flags)) { + return __do_kmalloc_node(NULL, flags, node, + PASS_TOKEN_PARAM(token), &ac); + } else { + return __kmalloc_nolock_noprof(PASS_TOKEN_PARAMS(size, token), + flags, node, &ac); + } +} + + static noinline void free_to_partial_list( struct kmem_cache *s, struct slab *slab, void *head, void *tail, int bulk_cnt, From 75a4888b7029a1f98613aef91f517b2ee1f03d43 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:18 -0700 Subject: [PATCH 3727/5214] perf maps: Add maps__mutate_mapping During kernel ELF symbol parsing (dso__process_kernel_symbol), proc kallsyms image loading (dso__load_kernel_sym, dso__load_guest_kernel_sym), and dynamic kernel memory map alignment updates (machine__update_kernel_mmap), the loader directly modifies live virtual address boundary keys fields on map objects. If these boundaries are mutated while the map pointer actively resides inside the parent maps cache array list (kmaps) outside of any lock closure, an unsafe concurrent window is exposed where parallel worker lookup threads (e.g., inside perf top) can mistakenly assume the cache remains sorted based on stale parameters, executing binary search queries (bsearch) across an unsorted range and triggering lookup failures. Fix this by introducing maps__mutate_mapping() that explicitly acquires the parent maps write semaphore lock, executes an incoming mutation callback block to perform the field updates under lock protection, and invalidates the sorted tracking flags prior to releasing the write lock. This guarantees synchronization invariants, closing the concurrent lookup race window. The adjacent module alignment pass inside machine__create_kernel_maps() is safely preserved as a high-performance lockless pass, as its invocation lifecycle bounds remain strictly single-threaded by contract during session initialization construction. To safely support this unconditional down_write write lock mutator without recursive read-to-write self-deadlock upgrades during lazy symbol loading, we introduce a public maps__load_maps() API. It copies map pointers under a brief read lock and force-loads all modules locklessly outside the lock. Callers (such as perf inject) must pre-load all kernel symbol maps up front at startup using maps__load_maps(), completely bypassing dynamic runtime mutations. Fixes: 39b12f781271 ("perf tools: Make it possible to read object code from vmlinux") Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Gabriel Marin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 32 ++++--- tools/perf/util/maps.c | 156 ++++++++++++++++++++++++++++------- tools/perf/util/maps.h | 3 + tools/perf/util/symbol-elf.c | 41 +++++---- tools/perf/util/symbol.c | 17 +++- 5 files changed, 187 insertions(+), 62 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index da1ad58758af..1ea06fde14e0 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1539,22 +1539,30 @@ static void machine__set_kernel_mmap(struct machine *machine, map__set_end(machine->vmlinux_map, ~0ULL); } -static int machine__update_kernel_mmap(struct machine *machine, - u64 start, u64 end) +struct kernel_mmap_mutation_ctx { + u64 start; + u64 end; +}; + +static int kernel_mmap_mutate_cb(struct map *map, void *data) { - struct map *orig, *updated; - int err; + struct kernel_mmap_mutation_ctx *ctx = data; - orig = machine->vmlinux_map; - updated = map__get(orig); + map__set_start(map, ctx->start); + map__set_end(map, ctx->end); + if (ctx->start == 0 && ctx->end == 0) + map__set_end(map, ~0ULL); + return 0; +} - machine->vmlinux_map = updated; - maps__remove(machine__kernel_maps(machine), orig); - machine__set_kernel_mmap(machine, start, end); - err = maps__insert(machine__kernel_maps(machine), updated); - map__put(orig); +static int machine__update_kernel_mmap(struct machine *machine, + u64 start, u64 end) +{ + struct kernel_mmap_mutation_ctx ctx = { .start = start, .end = end }; - return err; + return maps__mutate_mapping(machine__kernel_maps(machine), + machine->vmlinux_map, + kernel_mmap_mutate_cb, &ctx); } int machine__create_kernel_maps(struct machine *machine) diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 923935ee21b6..f808df2fe77b 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -576,6 +576,48 @@ void maps__remove(struct maps *maps, struct map *map) #endif } +/** + * maps__mutate_mapping - Apply write-protected mutations to a map. + * @maps: The maps collection containing the map. + * @map: The map to mutate. + * @mutate_cb: Callback function that performs the actual mutations. + * @data: Private data passed to the callback. + * + * This acquires the write lock on the maps semaphore to safely protect + * concurrent readers from seeing partially mutated or unsorted map boundaries. + * + * WARNING: Acquiring down_write() here can trigger a recursive self-deadlock if + * the caller already holds the read lock (e.g., during maps__for_each_map() or + * maps__find() iteration paths that trigger lazy symbol loading). To completely + * avoid this deadlock, all kernel/module maps must be pre-loaded up-front (via + * maps__load_maps()) under a clean, single-threaded context before entering + * multi-threaded event processing loops. + */ +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data) +{ + int err = 0; + + if (maps) { + down_write(maps__lock(maps)); + + err = mutate_cb(map, data); + + RC_CHK_ACCESS(maps)->maps_by_address_sorted = false; + RC_CHK_ACCESS(maps)->maps_by_name_sorted = false; + + up_write(maps__lock(maps)); + +#ifdef HAVE_LIBDW_SUPPORT + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); +#endif + } else { + err = mutate_cb(map, data); + } + + return err; +} + bool maps__empty(struct maps *maps) { bool res; @@ -626,6 +668,41 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data) return ret; } +int maps__load_maps(struct maps *maps) +{ + struct map **maps_copy; + unsigned int nr_maps; + int err = 0; + + if (!maps) + return 0; + + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + if (nr_maps == 0) { + up_read(maps__lock(maps)); + return 0; + } + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (!maps_copy) { + up_read(maps__lock(maps)); + return -ENOMEM; + } + for (unsigned int i = 0; i < nr_maps; i++) + maps_copy[i] = map__get(maps__maps_by_address(maps)[i]); + up_read(maps__lock(maps)); + + for (unsigned int i = 0; i < nr_maps; i++) { + if (map__load(maps_copy[i]) < 0) { + pr_warning("Failed to load map %s\n", dso__name(map__dso(maps_copy[i]))); + err = -1; + } + map__put(maps_copy[i]); + } + free(maps_copy); + return err; +} + void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data) { struct map **maps_by_address; @@ -668,40 +745,57 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp) return result; } -struct maps__find_symbol_by_name_args { - struct map **mapp; - const char *name; - struct symbol *sym; -}; - -static int maps__find_symbol_by_name_cb(struct map *map, void *data) -{ - struct maps__find_symbol_by_name_args *args = data; - - args->sym = map__find_symbol_by_name(map, args->name); - if (!args->sym) - return 0; - - if (!map__contains_symbol(map, args->sym)) { - args->sym = NULL; - return 0; - } - - if (args->mapp != NULL) - *args->mapp = map__get(map); - return 1; -} - struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) { - struct maps__find_symbol_by_name_args args = { - .mapp = mapp, - .name = name, - .sym = NULL, - }; + struct map **maps_copy; + unsigned int nr_maps; + struct symbol *sym = NULL; - maps__for_each_map(maps, maps__find_symbol_by_name_cb, &args); - return args.sym; + if (!maps) + return NULL; + + /* + * First, ensure all maps are loaded. We pre-load them outside of any + * read-to-write locks to avoid deadlocks. Even if some fail, we proceed. + */ + maps__load_maps(maps); + + /* + * Create a local snapshot of the maps while holding the read lock. + * This prevents deadlocking if iteration triggers further map insertions. + */ + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (maps_copy) { + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps__maps_by_address(maps)[i]; + + maps_copy[i] = map__get(map); + } + } + up_read(maps__lock(maps)); + + if (!maps_copy) + return NULL; + + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps_copy[i]; + + sym = map__find_symbol_by_name(map, name); + if (sym && map__contains_symbol(map, sym)) { + if (mapp) + *mapp = map__get(map); + break; + } + sym = NULL; + } + + for (unsigned int i = 0; i < nr_maps; i++) + map__put(maps_copy[i]); + + free(maps_copy); + return sym; } int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams) diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 5b80b199685e..4ec9b7453a3b 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -59,8 +59,11 @@ void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl); size_t maps__fprintf(struct maps *maps, FILE *fp); +int maps__load_maps(struct maps *maps); int maps__insert(struct maps *maps, struct map *map); void maps__remove(struct maps *maps, struct map *map); +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data); struct map *maps__find(struct maps *maps, u64 addr); struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d84e2e031d43..c301c298ded9 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1373,6 +1373,24 @@ static u64 ref_reloc(struct kmap *kmap) void __weak arch__sym_update(struct symbol *s __maybe_unused, GElf_Sym *sym __maybe_unused) { } +struct remap_kernel_ctx { + u64 sh_addr; + u64 sh_size; + u64 sh_offset; + struct kmap *kmap; +}; + +static int remap_kernel_cb(struct map *map, void *data) +{ + struct remap_kernel_ctx *ctx = data; + + map__set_start(map, ctx->sh_addr + ref_reloc(ctx->kmap)); + map__set_end(map, map__start(map) + ctx->sh_size); + map__set_pgoff(map, ctx->sh_offset); + map__set_mapping_type(map, MAPPING_TYPE__DSO); + return 0; +} + static int dso__process_kernel_symbol(struct dso *dso, struct map *map, GElf_Sym *sym, GElf_Shdr *shdr, struct maps *kmaps, struct kmap *kmap, @@ -1403,22 +1421,15 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map, * map to the kernel dso. */ if (*remap_kernel && dso__kernel(dso) && !kmodule) { - *remap_kernel = false; - map__set_start(map, shdr->sh_addr + ref_reloc(kmap)); - map__set_end(map, map__start(map) + shdr->sh_size); - map__set_pgoff(map, shdr->sh_offset); - map__set_mapping_type(map, MAPPING_TYPE__DSO); - /* Ensure maps are correctly ordered */ - if (kmaps) { - int err; - struct map *tmp = map__get(map); + struct remap_kernel_ctx ctx = { + .sh_addr = shdr->sh_addr, + .sh_size = shdr->sh_size, + .sh_offset = shdr->sh_offset, + .kmap = kmap + }; - maps__remove(kmaps, map); - err = maps__insert(kmaps, map); - map__put(tmp); - if (err) - return err; - } + *remap_kernel = false; + maps__mutate_mapping(kmaps, map, remap_kernel_cb, &ctx); } /* diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 0c46b24ee098..2cc911af8c81 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -48,6 +48,13 @@ #include #include +static int map_fixup_cb(struct map *map, void *data __maybe_unused) +{ + map__fixup_start(map); + map__fixup_end(map); + return 0; +} + static int dso__load_kernel_sym(struct dso *dso, struct map *map); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map); @@ -2240,10 +2247,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map) free(kallsyms_allocated_filename); if (err > 0 && !dso__is_kcore(dso)) { + struct maps *kmaps = map__kmaps(map); + dso__set_binary_type(dso, DSO_BINARY_TYPE__KALLSYMS); dso__set_long_name(dso, DSO__NAME_KALLSYMS, false); - map__fixup_start(map); - map__fixup_end(map); + maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); } return err; @@ -2283,10 +2291,11 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (err > 0) pr_debug("Using %s for symbols\n", kallsyms_filename); if (err > 0 && !dso__is_kcore(dso)) { + struct maps *kmaps = map__kmaps(map); + dso__set_binary_type(dso, DSO_BINARY_TYPE__GUEST_KALLSYMS); dso__set_long_name(dso, machine->mmap_name, false); - map__fixup_start(map); - map__fixup_end(map); + maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); } return err; From febea9ec382f5616954c1e3578e70308f4762e59 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:19 -0700 Subject: [PATCH 3728/5214] perf inject/aslr: Add ASLR tool infrastructure and MMAP tracking If perf.data files are taken from one machine to another they may leak virtual addresses and so weaken ASLR on the machine they are coming from. Add an aslr option for perf inject that remaps all virtual addresses, or drops data/events, so that the virtual address information isn't leaked. This patch introduces the core ASLR remapping tool infrastructure and implements remapping/tracking for metadata events (MMAP, MMAP2, COMM, FORK, EXIT, KSYMBOL, TEXT_POKE). Sample events are delegated without remapping for now. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin Tested-by: James Clark Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 63 ++- tools/perf/util/Build | 1 + tools/perf/util/aslr.c | 814 ++++++++++++++++++++++++++++++++++++ tools/perf/util/aslr.h | 41 ++ 4 files changed, 909 insertions(+), 10 deletions(-) create mode 100644 tools/perf/util/aslr.c create mode 100644 tools/perf/util/aslr.h diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 75ffe31d03fe..8bb37095e2de 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -8,6 +8,7 @@ */ #include "builtin.h" +#include "util/aslr.h" #include "util/color.h" #include "util/dso.h" #include "util/vdso.h" @@ -24,6 +25,7 @@ #include "util/string2.h" #include "util/symbol.h" #include "util/synthetic-events.h" +#include "util/pmus.h" #include "util/thread.h" #include "util/namespaces.h" #include "util/unwind.h" @@ -124,6 +126,7 @@ struct perf_inject { bool in_place_update_dry_run; bool copy_kcore_dir; bool convert_callchain; + bool aslr; const char *input_name; struct perf_data output; u64 bytes_written; @@ -234,20 +237,36 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, u64 *ids; int ret; + union perf_event *aslr_event = NULL; + ret = perf_event__process_attr(tool, event, pevlist); if (ret) return ret; - /* If the output isn't a pipe then the attributes will be written as part of the header. */ - if (!inject->output.is_pipe) - return 0; + if (inject->aslr) { + aslr_event = malloc(event->header.size); + if (!aslr_event) + return -ENOMEM; + memcpy(aslr_event, event, event->header.size); + aslr_tool__strip_attr_event(aslr_event, pevlist); + event = aslr_event; + } - if (!inject->itrace_synth_opts.set) - return perf_event__repipe_synth(tool, event); + /* If the output isn't a pipe then the attributes will be written as part of the header. */ + if (!inject->output.is_pipe) { + ret = 0; + goto out; + } + + if (!inject->itrace_synth_opts.set) { + ret = perf_event__repipe_synth(tool, event); + goto out; + } if (event->header.size < sizeof(struct perf_event_header) + PERF_ATTR_SIZE_VER0) { pr_err("Attribute event size %u is too small\n", event->header.size); - return -EINVAL; + ret = -EINVAL; + goto out; } /* @@ -263,7 +282,8 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, raw_attr_size > event->header.size - sizeof(event->header))) { pr_err("Attribute event size %u is too small for attr.size %u\n", event->header.size, raw_attr_size); - return -EINVAL; + ret = -EINVAL; + goto out; } memset(&attr, 0, sizeof(attr)); @@ -281,8 +301,11 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; } - return perf_event__synthesize_attr(tool, &attr, (u32)n_ids, ids, + ret = perf_event__synthesize_attr(tool, &attr, (u32)n_ids, ids, perf_event__repipe_synth_cb); +out: + free(aslr_event); + return ret; } static int perf_event__repipe_event_update(const struct perf_tool *tool, @@ -2594,7 +2617,6 @@ static int __cmd_inject(struct perf_inject *inject) evsel->core.attr.exclude_callchain_user = 0; } } - session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, @@ -2704,6 +2726,8 @@ int cmd_inject(int argc, const char **argv) unwind__option), OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, "Generate callchains using DWARF and drop register/stack data"), + OPT_BOOLEAN(0, "aslr", &inject.aslr, + "Remap virtual memory addresses similar to ASLR"), OPT_END() }; const char * const inject_usage[] = { @@ -2711,6 +2735,7 @@ int cmd_inject(int argc, const char **argv) NULL }; bool ordered_events; + struct perf_tool *tool = &inject.tool; if (!inject.itrace_synth_opts.set) { /* Disable eager loading of kernel symbols that adds overhead to perf inject. */ @@ -2731,6 +2756,11 @@ int cmd_inject(int argc, const char **argv) if (argc) usage_with_options(inject_usage, options); + if (inject.aslr && inject.convert_callchain) { + pr_err("Error: --aslr and --convert-callchain are mutually exclusive features.\n"); + return -EINVAL; + } + if (inject.strip && !inject.itrace_synth_opts.set) { pr_err("--strip option requires --itrace option\n"); return -1; @@ -2824,12 +2854,21 @@ int cmd_inject(int argc, const char **argv) inject.tool.schedstat_domain = perf_event__repipe_op2_synth; inject.tool.dont_split_sample_group = true; inject.tool.merge_deferred_callchains = false; - inject.session = __perf_session__new(&data, &inject.tool, + if (inject.aslr) { + tool = aslr_tool__new(&inject.tool); + if (!tool) { + ret = -ENOMEM; + goto out_close_output; + } + } + inject.session = __perf_session__new(&data, tool, /*trace_event_repipe=*/inject.output.is_pipe, /*host_env=*/NULL); if (IS_ERR(inject.session)) { ret = PTR_ERR(inject.session); + if (inject.aslr) + aslr_tool__delete(tool); goto out_close_output; } @@ -2922,6 +2961,8 @@ int cmd_inject(int argc, const char **argv) goto out_delete; ret = __cmd_inject(&inject); + if (inject.aslr) + aslr_tool__strip_evlist(tool, inject.session->evlist); guest_session__exit(&inject.guest_session); @@ -2929,6 +2970,8 @@ int cmd_inject(int argc, const char **argv) strlist__delete(inject.known_build_ids); zstd_fini(&(inject.session->zstd_data)); perf_session__delete(inject.session); + if (inject.aslr) + aslr_tool__delete(tool); out_close_output: if (!inject.in_place_update) perf_data__close(&inject.output); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index b22cdc24082a..5e2265018826 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -6,6 +6,7 @@ perf-util-y += arm64-frame-pointer-unwind-support.o perf-util-y += addr2line.o perf-util-y += addr_location.o perf-util-y += annotate.o +perf-util-y += aslr.o perf-util-y += blake2s.o perf-util-y += block-info.o perf-util-y += block-range.o diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c new file mode 100644 index 000000000000..56fc444fbf54 --- /dev/null +++ b/tools/perf/util/aslr.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "aslr.h" + +#include "addr_location.h" +#include "debug.h" +#include "event.h" +#include "evsel.h" +#include "evlist.h" +#include "machine.h" +#include "map.h" +#include "thread.h" +#include "tool.h" +#include "session.h" +#include "data.h" +#include "dso.h" +#include "pmus.h" + +#include /* page_size */ +#include +#include +#include +#include + +/** + * struct remap_addresses_key - Key for mapping original addresses to remapped ones. + * @dso: Pointer to the DSO (Dynamic Shared Object) associated with the mapping. + * @invariant: Unique offset invariant within the VMA (Virtual Memory Area). + * Calculated as `start - pgoff`. This value remains constant when + * perf's internal `maps__fixup_overlap_and_insert` splits a map into + * fragmented VMA pieces due to overlapping events, allowing us to + * resolve split maps consistently back to the original VMA. + * @pid: Process ID associated with the mapping. + */ +struct remap_addresses_key { + struct machine *machine; + struct dso *dso; + u64 invariant; + pid_t pid; +}; + +struct aslr_mapping { + struct list_head node; + u64 orig_start; + u64 len; + u64 remap_start; +}; + +struct process_top_address { + u64 remapped_max; +}; +struct aslr_tool { + /** @tool: The tool implemented here and a pointer to a delegate to process the data. */ + struct delegate_tool tool; + /** @machines: The machines with the input, not remapped, virtual address layout. */ + struct machines machines; + /** @event_copy: Buffer used to create an event to pass to the delegate. */ + char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); + /** @remap_addresses: mapping from remap_addresses_key to remapped address. */ + struct hashmap remap_addresses; + /** @top_addresses: mapping from process to max remapped address. */ + struct hashmap top_addresses; +}; + +static const pid_t kernel_pid = -1; + +/* Start remapping user processes from a small non-zero offset. */ +static const u64 user_space_start = 0x200000; +static const u64 kernel_space_start_64 = 0xffff800010000000ULL; +static const u64 kernel_space_start_32 = 0x80000000ULL; + +static size_t remap_addresses__hash(long _key, void *ctx __maybe_unused) +{ + struct remap_addresses_key *key = (struct remap_addresses_key *)_key; + void *dso_ptr = key->dso ? RC_CHK_ACCESS(key->dso) : NULL; + + return (size_t)key->machine ^ (size_t)dso_ptr ^ key->invariant ^ key->pid; +} + +static bool remap_addresses__equal(long _key1, long _key2, void *ctx __maybe_unused) +{ + struct remap_addresses_key *key1 = (struct remap_addresses_key *)_key1; + struct remap_addresses_key *key2 = (struct remap_addresses_key *)_key2; + + return key1->machine == key2->machine && + RC_CHK_EQUAL(key1->dso, key2->dso) && + key1->invariant == key2->invariant && + key1->pid == key2->pid; +} + +struct top_addresses_key { + struct machine *machine; + pid_t pid; +}; + +static size_t top_addresses__hash(long _key, void *ctx __maybe_unused) +{ + struct top_addresses_key *key = (struct top_addresses_key *)_key; + + return (size_t)key->machine ^ key->pid; +} + +static bool top_addresses__equal(long _key1, long _key2, void *ctx __maybe_unused) +{ + struct top_addresses_key *key1 = (struct top_addresses_key *)_key1; + struct top_addresses_key *key2 = (struct top_addresses_key *)_key2; + + return key1->machine == key2->machine && key1->pid == key2->pid; +} + +static u64 round_up_to_page_size(u64 addr) +{ + return (addr + page_size - 1) & ~((u64)page_size - 1); +} + +struct aslr_machine_priv { + bool kernel_maps_loaded; +}; + +static int aslr_tool__preload_kernel_maps(struct machine *machine) +{ + struct aslr_machine_priv *mpriv = machine->priv; + + if (!mpriv) { + mpriv = zalloc(sizeof(*mpriv)); + if (!mpriv) + return -ENOMEM; + machine->priv = mpriv; + } + + if (!mpriv->kernel_maps_loaded) { + struct maps *kmaps = machine__kernel_maps(machine); + + if (kmaps) { + int err = maps__load_maps(kmaps); + + if (err < 0) { + pr_err("ASLR: Failed to preload kernel maps for machine pid %d\n", + machine->pid); + return err; + } + } + mpriv->kernel_maps_loaded = true; + } + return 0; +} + +static void aslr_tool__free_machine_priv(struct machine *machine) +{ + free(machine->priv); + machine->priv = NULL; +} + +static void aslr_tool__destroy_machines_priv(struct machines *machines) +{ + struct rb_node *nd; + + aslr_tool__free_machine_priv(&machines->host); + for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) { + struct machine *machine = rb_entry(nd, struct machine, rb_node); + + aslr_tool__free_machine_priv(machine); + } +} + +static u64 aslr_tool__findnew_mapping(struct aslr_tool *aslr, + struct machine *session_machine, + struct thread *aslr_thread, + u8 cpumode, u64 start, + u64 len, u64 pgoff) +{ + /* Address location for dso lookup. */ + struct addr_location al; + /* Original ASLR address based key for the remap table. */ + struct remap_addresses_key remap_key; + /* The address in the ASLR sanitized address space less pg_off. */ + u64 *remapped_invariant_ptr; + /* Key for the maximum address in a process. */ + struct top_addresses_key top_addr_key; + /* Value in top address table. */ + struct process_top_address *top = NULL; + /* Address in ASLR sanitized address space. */ + u64 remap_addr; + /* Potentially allocated remap table key. */ + struct remap_addresses_key *new_remap_key = NULL; + /* + * Potentially allocated remap table key. + * TODO: Avoid allocation necessary for perf 32-bit binary support. + */ + u64 *new_remap_val = NULL; + int err; + + if (!aslr_thread) + return 0; + + /* The key to look up an incoming address to the outgoing value. */ + addr_location__init(&al); + remap_key.machine = maps__machine(thread__maps(aslr_thread)); + remap_key.pid = (cpumode == PERF_RECORD_MISC_KERNEL || + cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ? + kernel_pid : thread__pid(aslr_thread); + if (thread__find_map(aslr_thread, cpumode, start, &al)) { + struct dso *dso = map__dso(al.map); + const char *dso_name = dso ? dso__long_name(dso) : NULL; + + remap_key.dso = dso; + if (dso && !is_anon_memory(dso_name) && !is_no_dso_memory(dso_name)) + remap_key.invariant = map__start(al.map) - map__pgoff(al.map); + else + remap_key.invariant = map__start(al.map); + } else { + remap_key.dso = NULL; + remap_key.invariant = start; + } + + /* The key to look up top allocated address. */ + top_addr_key.machine = remap_key.machine; + top_addr_key.pid = remap_key.pid; + + if (hashmap__find(&aslr->remap_addresses, &remap_key, &remapped_invariant_ptr)) { + /* Mmap already exists. */ + u64 calculated_max; + + if (al.map) { + /* + * The cached value is the base of the invariant. We add the + * offset into the VMA (start - map__start), plus the map's + * pgoff, to get the precise virtual address within this chunk. + */ + remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) + + (start - map__start(al.map)); + } else { + /* + * For unmapped memory (e.g. kernel anonymous), the cached value + * was stored offset by pgoff. Adding pgoff yields the true remap_addr. + */ + remap_addr = *remapped_invariant_ptr + pgoff; + } + + calculated_max = remap_addr + len; + + /* See if top mapping was expanded. */ + if (hashmap__find(&aslr->top_addresses, &top_addr_key, &top)) { + if (calculated_max > top->remapped_max) + top->remapped_max = calculated_max; + } + addr_location__exit(&al); + return remap_addr; + } + /* No mmap, create an entry from the top address. */ + if (hashmap__find(&aslr->top_addresses, &top_addr_key, &top)) { + struct addr_location prev_al; + bool is_contiguous = false; + + /* Current max allocated mmap address within the process. */ + remap_addr = top->remapped_max; + + addr_location__init(&prev_al); + if (thread__find_map(aslr_thread, cpumode, start - 1, &prev_al)) { + if (map__end(prev_al.map) == start) + is_contiguous = true; + } + addr_location__exit(&prev_al); + + if (is_contiguous) { + /* Contiguous mapping, do not add 1 page gap! */ + remap_addr = round_up_to_page_size(remap_addr); + } else { + /* Give 1 page gap from current max page. */ + remap_addr = round_up_to_page_size(remap_addr); + remap_addr += page_size; + } + if (remap_addr + len > top->remapped_max) + top->remapped_max = remap_addr + len; + } else { + /* First address of the process, allocate key and first top address. */ + struct top_addresses_key *tk; + struct process_top_address *top_val; + struct perf_env *env = session_machine ? session_machine->env : NULL; + bool is_64 = env ? perf_env__kernel_is_64_bit(env) : (sizeof(void *) == 8); + u64 kernel_start_addr = is_64 ? kernel_space_start_64 : kernel_space_start_32; + + remap_addr = (cpumode == PERF_RECORD_MISC_KERNEL || + cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ? + kernel_start_addr : user_space_start; + remap_addr = round_up_to_page_size(remap_addr); + + tk = malloc(sizeof(*tk)); + top_val = malloc(sizeof(*top_val)); + if (!tk || !top_val) { + err = -ENOMEM; + } else { + *tk = top_addr_key; + top_val->remapped_max = remap_addr + len; + err = hashmap__insert(&aslr->top_addresses, tk, top_val, + HASHMAP_ADD, NULL, NULL); + } + if (err) { + errno = -err; + pr_err("Failure to add ASLR process top address %m\n"); + free(tk); + free(top_val); + addr_location__exit(&al); + return 0; + } + } + /* Create rmeapping entry. */ + new_remap_key = malloc(sizeof(*new_remap_key)); + new_remap_val = malloc(sizeof(u64)); + if (!new_remap_key || !new_remap_val) { + err = -ENOMEM; + } else { + *new_remap_key = remap_key; + new_remap_key->dso = dso__get(remap_key.dso); + if (cpumode == PERF_RECORD_MISC_KERNEL || + cpumode == PERF_RECORD_MISC_GUEST_KERNEL) { + if (al.map) { + *new_remap_val = remap_addr - + (start - map__start(al.map)) - + map__pgoff(al.map); + } else { + /* + * Subtract pgoff from the base virtual address so that + * when the lookup path adds pgoff back, it perfectly + * cancels out and returns remap_addr. + */ + *new_remap_val = remap_addr - pgoff; + } + } else { + *new_remap_val = remap_addr - (al.map ? (start - map__start(al.map)) + + map__pgoff(al.map) : pgoff); + } + err = hashmap__add(&aslr->remap_addresses, new_remap_key, new_remap_val); + if (err) + dso__put(new_remap_key->dso); + } + if (err) { + errno = -err; + pr_err("Failure to add ASLR remapping %m\n"); + free(new_remap_key); + free(new_remap_val); + addr_location__exit(&al); + return 0; + } + addr_location__exit(&al); + return remap_addr; +} + +static int aslr_tool__process_mmap(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + u8 cpumode; + struct thread *thread; + struct machine *aslr_machine; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + new_event = (union perf_event *)aslr->event_copy; + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space. */ + err = perf_event__process_mmap(tool, event, sample, aslr_machine); + if (err) + return err; + + thread = machine__findnew_thread(aslr_machine, event->mmap.pid, event->mmap.tid); + if (!thread) + return -ENOMEM; + memcpy(&new_event->mmap, &event->mmap, event->mmap.header.size); + /* Remaps the mmap.start. */ + new_event->mmap.start = aslr_tool__findnew_mapping(aslr, machine, thread, cpumode, + event->mmap.start, + event->mmap.len, + event->mmap.pgoff); + /* + * For anonymous memory (and kernel maps), the kernel populates the + * event's pgoff field with the original un-obfuscated virtual address + * in bytes (i.e. (addr >> PAGE_SHIFT) << PAGE_SHIFT). + * We must overwrite pgoff with the new remapped byte address to prevent + * leaking the original ASLR layout. + */ + if (is_anon_memory(event->mmap.filename) || is_no_dso_memory(event->mmap.filename) || + ((cpumode == PERF_RECORD_MISC_KERNEL || cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && + !is_kernel_module(event->mmap.filename, cpumode))) + new_event->mmap.pgoff = new_event->mmap.start; + err = delegate->mmap(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_mmap2(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + u8 cpumode; + struct thread *thread; + struct machine *aslr_machine; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + new_event = (union perf_event *)aslr->event_copy; + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space. */ + err = perf_event__process_mmap2(tool, event, sample, aslr_machine); + if (err) + return err; + + thread = machine__findnew_thread(aslr_machine, event->mmap2.pid, event->mmap2.tid); + if (!thread) + return -ENOMEM; + memcpy(&new_event->mmap2, &event->mmap2, event->mmap2.header.size); + /* Remaps the mmap.start. */ + new_event->mmap2.start = aslr_tool__findnew_mapping(aslr, machine, thread, cpumode, + event->mmap2.start, + event->mmap2.len, + event->mmap2.pgoff); + /* + * For anonymous memory (and kernel maps), the kernel populates the + * event's pgoff field with the original un-obfuscated virtual address + * in bytes (i.e. (addr >> PAGE_SHIFT) << PAGE_SHIFT). + * We must overwrite pgoff with the new remapped byte address to prevent + * leaking the original ASLR layout. + */ + if (is_anon_memory(event->mmap2.filename) || is_no_dso_memory(event->mmap2.filename) || + ((cpumode == PERF_RECORD_MISC_KERNEL || cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && + !is_kernel_module(event->mmap2.filename, cpumode))) + new_event->mmap2.pgoff = new_event->mmap2.start; + err = delegate->mmap2(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_comm(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space. */ + err = perf_event__process_comm(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->comm(delegate, event, sample, machine); +} + +static int aslr_tool__process_fork(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space. */ + err = perf_event__process_fork(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->fork(delegate, event, sample, machine); +} + +static int aslr_tool__process_exit(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space. */ + err = perf_event__process_exit(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->exit(delegate, event, sample, machine); +} + +static int aslr_tool__process_text_poke(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + /* Drop in case the instruction encodes an ASLR revealing address. */ + return 0; +} + +static int aslr_tool__process_ksymbol(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + struct thread *thread; + struct machine *aslr_machine; + bool is_unregister; + int err; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + new_event = (union perf_event *)aslr->event_copy; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + thread = machine__findnew_thread(aslr_machine, kernel_pid, 0); + if (!thread) + return -ENOMEM; + + is_unregister = (event->ksymbol.flags & PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER); + + memcpy(&new_event->ksymbol, &event->ksymbol, event->ksymbol.header.size); + + if (is_unregister) { + new_event->ksymbol.addr = aslr_tool__findnew_mapping(aslr, machine, thread, + PERF_RECORD_MISC_KERNEL, + event->ksymbol.addr, + event->ksymbol.len, + /*pgoff=*/0); + err = perf_event__process_ksymbol(tool, event, sample, aslr_machine); + } else { + err = perf_event__process_ksymbol(tool, event, sample, aslr_machine); + new_event->ksymbol.addr = aslr_tool__findnew_mapping(aslr, machine, thread, + PERF_RECORD_MISC_KERNEL, + event->ksymbol.addr, + event->ksymbol.len, + /*pgoff=*/0); + } + if (err) { + thread__put(thread); + return err; + } + + err = delegate->ksymbol(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_sample(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool); + struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool); + struct perf_tool *delegate = aslr->tool.delegate; + + return delegate->sample(delegate, event, sample, machine); +} + +static int skipn(int fd, off_t n) +{ + char buf[4096]; + ssize_t ret; + + while (n > 0) { + ret = read(fd, buf, min_t(off_t, n, (off_t)sizeof(buf))); + if (ret <= 0) + return ret; + n -= ret; + } + + return 0; +} + +static s64 aslr_tool__process_auxtrace(const struct perf_tool *tool __maybe_unused, + struct perf_session *session, + union perf_event *event) +{ + pr_warning_once("ASLR: Dropping auxtrace data as it cannot be obfuscated.\n"); + if (perf_data__is_pipe(session->data)) { + /* Copy behavior of the stub by reading all pipe data. */ + int err = skipn(perf_data__fd(session->data), event->auxtrace.size); + + if (err < 0) + return err; + } + return event->auxtrace.size; +} + +static int aslr_tool__process_auxtrace_info(const struct perf_tool *tool __maybe_unused, + struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + return 0; +} + +static int aslr_tool__process_auxtrace_error(const struct perf_tool *tool __maybe_unused, + struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + return 0; +} + + +void aslr_tool__strip_attr_event(union perf_event *event, struct evlist **pevlist __maybe_unused) +{ + u32 attr_size; + + attr_size = event->attr.attr.size ?: PERF_ATTR_SIZE_VER0; + + if (attr_size >= (offsetof(struct perf_event_attr, sample_type) + sizeof(u64))) { + event->attr.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; + } + + if (attr_size >= (offsetof(struct perf_event_attr, type) + sizeof(u32))) { + u32 type = event->attr.attr.type; + + if (type == PERF_TYPE_BREAKPOINT && + attr_size >= (offsetof(struct perf_event_attr, bp_addr) + sizeof(u64))) { + event->attr.attr.bp_addr = 0; + } else if (type >= PERF_TYPE_MAX) { + struct perf_pmu *pmu; + + pmu = perf_pmus__find_by_type(type); + if (pmu && (!strcmp(pmu->name, "kprobe") || + !strcmp(pmu->name, "uprobe"))) { + if (attr_size >= (offsetof(struct perf_event_attr, config1) + sizeof(u64))) + event->attr.attr.config1 = 0; + if (attr_size >= (offsetof(struct perf_event_attr, config2) + sizeof(u64))) + event->attr.attr.config2 = 0; + } + } + } +} + +void aslr_tool__strip_evlist(struct perf_tool *tool __maybe_unused, + struct evlist *evlist) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + evsel->core.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; + + if (evsel->core.attr.type == PERF_TYPE_BREAKPOINT) + evsel->core.attr.bp_addr = 0; + else if (evsel->core.attr.type >= PERF_TYPE_MAX) { + struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->core.attr.type); + + if (pmu && (!strcmp(pmu->name, "kprobe") || + !strcmp(pmu->name, "uprobe"))) { + evsel->core.attr.config1 = 0; + evsel->core.attr.config2 = 0; + } + } + } +} + +static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) +{ + delegate_tool__init(&aslr->tool, delegate); + aslr->tool.tool.ordered_events = true; + + machines__init(&aslr->machines); + + hashmap__init(&aslr->remap_addresses, + remap_addresses__hash, remap_addresses__equal, + /*ctx=*/NULL); + hashmap__init(&aslr->top_addresses, + top_addresses__hash, top_addresses__equal, + /*ctx=*/NULL); + + aslr->tool.tool.sample = aslr_tool__process_sample; + /* read - reads a counter, okay to delegate. */ + aslr->tool.tool.mmap = aslr_tool__process_mmap; + aslr->tool.tool.mmap2 = aslr_tool__process_mmap2; + aslr->tool.tool.comm = aslr_tool__process_comm; + aslr->tool.tool.fork = aslr_tool__process_fork; + aslr->tool.tool.exit = aslr_tool__process_exit; + /* namesspaces, cgroup, lost, lost_sample, aux, */ + /* itrace_start, aux_output_hw_id, context_switch, throttle, unthrottle */ + /* - no virtual addresses. */ + aslr->tool.tool.ksymbol = aslr_tool__process_ksymbol; + /* bpf - no virtual address. */ + aslr->tool.tool.text_poke = aslr_tool__process_text_poke; + /* + * event_update, tracing_data, finished_round, build_id, id_index, + * auxtrace_info, auxtrace_error, time_conv, thread_map, cpu_map, + * stat_config, stat, feature, finished_init, bpf_metadata, compressed, + * auxtrace - no virtual addresses. + */ + aslr->tool.tool.auxtrace = aslr_tool__process_auxtrace; + aslr->tool.tool.auxtrace_info = aslr_tool__process_auxtrace_info; + aslr->tool.tool.auxtrace_error = aslr_tool__process_auxtrace_error; +} + +struct perf_tool *aslr_tool__new(struct perf_tool *delegate) +{ + struct aslr_tool *aslr = zalloc(sizeof(*aslr)); + + if (!aslr) + return NULL; + + aslr_tool__init(aslr, delegate); + return &aslr->tool.tool; +} + +void aslr_tool__delete(struct perf_tool *tool) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct hashmap_entry *cur; + size_t bkt; + struct rb_node *nd; + + if (!tool) + return; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + + hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) { + struct remap_addresses_key *key = (struct remap_addresses_key *)cur->pkey; + + if (key) + dso__put(key->dso); + zfree(&cur->pkey); + zfree(&cur->pvalue); + } + hashmap__for_each_entry(&aslr->top_addresses, cur, bkt) { + zfree(&cur->pkey); + zfree(&cur->pvalue); + } + + hashmap__clear(&aslr->remap_addresses); + hashmap__clear(&aslr->top_addresses); + aslr_tool__destroy_machines_priv(&aslr->machines); + machines__destroy_kernel_maps(&aslr->machines); + + while ((nd = rb_first_cached(&aslr->machines.guests)) != NULL) { + struct machine *machine = rb_entry(nd, struct machine, rb_node); + + rb_erase_cached(nd, &aslr->machines.guests); + machine__delete(machine); + } + + machines__exit(&aslr->machines); + free(aslr); +} diff --git a/tools/perf/util/aslr.h b/tools/perf/util/aslr.h new file mode 100644 index 000000000000..2b82f711bc67 --- /dev/null +++ b/tools/perf/util/aslr.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PERF_ASLR_H +#define __PERF_ASLR_H + +#include + +#define ASLR_SUPPORTED_SAMPLE_TYPE ( \ + PERF_SAMPLE_IDENTIFIER | \ + PERF_SAMPLE_IP | \ + PERF_SAMPLE_TID | \ + PERF_SAMPLE_TIME | \ + PERF_SAMPLE_ADDR | \ + PERF_SAMPLE_ID | \ + PERF_SAMPLE_STREAM_ID | \ + PERF_SAMPLE_CPU | \ + PERF_SAMPLE_PERIOD | \ + PERF_SAMPLE_READ | \ + PERF_SAMPLE_CALLCHAIN | \ + PERF_SAMPLE_RAW | \ + PERF_SAMPLE_BRANCH_STACK | \ + PERF_SAMPLE_STACK_USER | \ + PERF_SAMPLE_WEIGHT_TYPE | \ + PERF_SAMPLE_DATA_SRC | \ + PERF_SAMPLE_TRANSACTION | \ + PERF_SAMPLE_PHYS_ADDR | \ + PERF_SAMPLE_CGROUP | \ + PERF_SAMPLE_DATA_PAGE_SIZE | \ + PERF_SAMPLE_CODE_PAGE_SIZE | \ + PERF_SAMPLE_AUX) + +struct perf_tool; +struct evsel; +struct evlist; +union perf_event; + +struct perf_tool *aslr_tool__new(struct perf_tool *delegate); +void aslr_tool__delete(struct perf_tool *aslr); +void aslr_tool__strip_attr_event(union perf_event *event, struct evlist **pevlist); +void aslr_tool__strip_evlist(struct perf_tool *tool, struct evlist *evlist); + +#endif /* __PERF_ASLR_H */ From c9a5688380865b968f7aef6534de632857ab5be0 Mon Sep 17 00:00:00 2001 From: Douglas Freimuth Date: Thu, 4 Jun 2026 21:27:53 +0200 Subject: [PATCH 3729/5214] KVM: s390: Add map/unmap ioctl and clean mappings post-guest s390 needs map/unmap ioctls, which map the adapter set indicator pages, so the pages can be accessed when interrupts are disabled. The mappings are cleaned up when the guest is removed. pin_user_pages_remote is used for both the ioctl as well as the pin-on-demand logic in adapter_indicators_set(). Map/Unmap ioctls are fenced in order to avoid the longterm pinning in Secure Execution environments. In Secure Execution environments the path of execution available before this patch is followed. Statistical counters to count map/unmap functions for adapter indicator pages are added. The counters can be used to analyze map/unmap functions in non-Secure Execution environments and similarly can be used to analyze Secure Execution environments where the counters will not be incremented as the adapter indicator pages are not mapped. Reviewed-by: Matthew Rosato Signed-off-by: Douglas Freimuth Acked-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260604192755.203143-2-freimuth@linux.ibm.com> --- arch/s390/include/asm/kvm_host.h | 5 + arch/s390/kvm/interrupt.c | 227 +++++++++++++++++++++++++------ arch/s390/kvm/kvm-s390.c | 3 + arch/s390/kvm/kvm-s390.h | 2 + 4 files changed, 194 insertions(+), 43 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index aa4c4685f95c..4ff1ede90873 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -448,6 +448,8 @@ struct kvm_vcpu_arch { struct kvm_vm_stat { struct kvm_vm_stat_generic generic; u64 inject_io; + u64 io_390_adapter_map; + u64 io_390_adapter_unmap; u64 inject_float_mchk; u64 inject_pfault_done; u64 inject_service_signal; @@ -479,6 +481,9 @@ struct s390_io_adapter { bool masked; bool swap; bool suppressible; + spinlock_t maps_lock; + struct list_head maps; + unsigned int nr_maps; }; #define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 07f59c3b9a7b..d11dea4cb0d3 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2411,34 +2411,46 @@ static int register_io_adapter(struct kvm_device *dev, { struct s390_io_adapter *adapter; struct kvm_s390_io_adapter adapter_info; + int rc = 0; + mutex_lock(&dev->kvm->lock); if (copy_from_user(&adapter_info, - (void __user *)attr->addr, sizeof(adapter_info))) - return -EFAULT; - - if (adapter_info.id >= MAX_S390_IO_ADAPTERS) - return -EINVAL; - + (void __user *)attr->addr, sizeof(adapter_info))) { + rc = -EFAULT; + goto out; + } + if (adapter_info.id >= MAX_S390_IO_ADAPTERS) { + rc = -EINVAL; + goto out; + } adapter_info.id = array_index_nospec(adapter_info.id, MAX_S390_IO_ADAPTERS); - if (dev->kvm->arch.adapters[adapter_info.id] != NULL) - return -EINVAL; - + if (dev->kvm->arch.adapters[adapter_info.id] != NULL) { + rc = -EINVAL; + goto out; + } adapter = kzalloc_obj(*adapter, GFP_KERNEL_ACCOUNT); - if (!adapter) - return -ENOMEM; + if (!adapter) { + rc = -ENOMEM; + goto out; + } + INIT_LIST_HEAD(&adapter->maps); + spin_lock_init(&adapter->maps_lock); + adapter->nr_maps = 0; adapter->id = adapter_info.id; adapter->isc = adapter_info.isc; adapter->maskable = adapter_info.maskable; adapter->masked = false; adapter->swap = adapter_info.swap; - adapter->suppressible = (adapter_info.flags) & + adapter->suppressible = adapter_info.flags & KVM_S390_ADAPTER_SUPPRESSIBLE; dev->kvm->arch.adapters[adapter->id] = adapter; - return 0; +out: + mutex_unlock(&dev->kvm->lock); + return rc; } int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked) @@ -2453,12 +2465,151 @@ int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked) return ret; } +static struct page *pin_map_page(struct kvm *kvm, u64 uaddr, + unsigned int gup_flags) +{ + struct mm_struct *mm = kvm->mm; + struct page *page = NULL; + int locked = 1; + + if (mmget_not_zero(mm)) { + mmap_read_lock(mm); + pin_user_pages_remote(mm, uaddr, 1, FOLL_WRITE | gup_flags, + &page, &locked); + if (locked) + mmap_read_unlock(mm); + mmput(mm); + } + + return page; +} + +static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) +{ + struct s390_io_adapter *adapter = get_io_adapter(kvm, id); + struct s390_map_info *map; + unsigned long flags; + __u64 host_addr; + int ret, idx; + + if (!adapter || !addr) + return -EINVAL; + + map = kzalloc_obj(*map, GFP_KERNEL_ACCOUNT); + if (!map) + return -ENOMEM; + + INIT_LIST_HEAD(&map->list); + idx = srcu_read_lock(&kvm->srcu); + host_addr = gpa_to_hva(kvm, addr); + if (kvm_is_error_hva(host_addr)) { + srcu_read_unlock(&kvm->srcu, idx); + ret = -EFAULT; + goto out; + } + srcu_read_unlock(&kvm->srcu, idx); + map->guest_addr = addr; + map->addr = host_addr; + map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM); + if (!map->page) { + ret = -EINVAL; + goto out; + } + spin_lock_irqsave(&adapter->maps_lock, flags); + if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) { + list_add_tail(&map->list, &adapter->maps); + adapter->nr_maps++; + ret = 0; + } else { + ret = -EINVAL; + } + spin_unlock_irqrestore(&adapter->maps_lock, flags); + if (ret) + unpin_user_page(map->page); +out: + if (ret) + kfree(map); + return ret; +} + +static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) +{ + struct s390_io_adapter *adapter = get_io_adapter(kvm, id); + struct s390_map_info *map, *tmp, *map_to_free; + struct page *map_page_to_put = NULL; + u64 map_addr_to_mark = 0; + unsigned long flags; + int found = 0, idx; + + if (!adapter || !addr) + return -EINVAL; + + spin_lock_irqsave(&adapter->maps_lock, flags); + list_for_each_entry_safe(map, tmp, &adapter->maps, list) { + if (map->guest_addr == addr) { + found = 1; + adapter->nr_maps--; + list_del(&map->list); + map_page_to_put = map->page; + map_addr_to_mark = map->guest_addr; + map_to_free = map; + break; + } + } + spin_unlock_irqrestore(&adapter->maps_lock, flags); + + if (found) { + kfree(map_to_free); + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT); + set_page_dirty_lock(map_page_to_put); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map_page_to_put); + } + + return found ? 0 : -ENOENT; +} + +void kvm_s390_unmap_all_adapters(struct kvm *kvm) +{ + struct s390_map_info *map, *tmp; + unsigned long flags; + int i, idx; + + for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) { + struct s390_io_adapter *adapter = kvm->arch.adapters[i]; + LIST_HEAD(local_list); + + if (!adapter) + continue; + + spin_lock_irqsave(&adapter->maps_lock, flags); + list_splice_init(&adapter->maps, &local_list); + adapter->nr_maps = 0; + spin_unlock_irqrestore(&adapter->maps_lock, flags); + + list_for_each_entry_safe(map, tmp, &local_list, list) { + list_del(&map->list); + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT); + set_page_dirty_lock(map->page); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map->page); + kfree(map); + } + } +} + void kvm_s390_destroy_adapters(struct kvm *kvm) { int i; - for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) + kvm_s390_unmap_all_adapters(kvm); + + for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) { kfree(kvm->arch.adapters[i]); + kvm->arch.adapters[i] = NULL; + } } static int modify_io_adapter(struct kvm_device *dev, @@ -2480,14 +2631,22 @@ static int modify_io_adapter(struct kvm_device *dev, if (ret > 0) ret = 0; break; - /* - * The following operations are no longer needed and therefore no-ops. - * The gpa to hva translation is done when an IRQ route is set up. The - * set_irq code uses get_user_pages_remote() to do the actual write. - */ case KVM_S390_IO_ADAPTER_MAP: case KVM_S390_IO_ADAPTER_UNMAP: - ret = 0; + /* If in Secure Execution mode do not long term pin. */ + mutex_lock(&dev->kvm->lock); + if (kvm_s390_pv_is_protected(dev->kvm)) { + mutex_unlock(&dev->kvm->lock); + return 0; + } + if (req.type == KVM_S390_IO_ADAPTER_MAP) { + dev->kvm->stat.io_390_adapter_map++; + ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr); + } else { + dev->kvm->stat.io_390_adapter_unmap++; + ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr); + } + mutex_unlock(&dev->kvm->lock); break; default: ret = -EINVAL; @@ -2733,24 +2892,6 @@ static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap) return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit; } -static struct page *get_map_page(struct kvm *kvm, u64 uaddr) -{ - struct mm_struct *mm = kvm->mm; - struct page *page = NULL; - int locked = 1; - - if (mmget_not_zero(mm)) { - mmap_read_lock(mm); - get_user_pages_remote(mm, uaddr, 1, FOLL_WRITE, - &page, &locked); - if (locked) - mmap_read_unlock(mm); - mmput(mm); - } - - return page; -} - static int adapter_indicators_set(struct kvm *kvm, struct s390_io_adapter *adapter, struct kvm_s390_adapter_int *adapter_int) @@ -2760,12 +2901,12 @@ static int adapter_indicators_set(struct kvm *kvm, struct page *ind_page, *summary_page; void *map; - ind_page = get_map_page(kvm, adapter_int->ind_addr); + ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0); if (!ind_page) return -1; - summary_page = get_map_page(kvm, adapter_int->summary_addr); + summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0); if (!summary_page) { - put_page(ind_page); + unpin_user_page(ind_page); return -1; } @@ -2784,8 +2925,8 @@ static int adapter_indicators_set(struct kvm *kvm, set_page_dirty_lock(summary_page); srcu_read_unlock(&kvm->srcu, idx); - put_page(ind_page); - put_page(summary_page); + unpin_user_page(ind_page); + unpin_user_page(summary_page); return summary_set ? 0 : 1; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index de28ee1f7882..9de6edeb5ae0 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -68,6 +68,8 @@ const struct kvm_stats_desc kvm_vm_stats_desc[] = { KVM_GENERIC_VM_STATS(), STATS_DESC_COUNTER(VM, inject_io), + STATS_DESC_COUNTER(VM, io_390_adapter_map), + STATS_DESC_COUNTER(VM, io_390_adapter_unmap), STATS_DESC_COUNTER(VM, inject_float_mchk), STATS_DESC_COUNTER(VM, inject_pfault_done), STATS_DESC_COUNTER(VM, inject_service_signal), @@ -2539,6 +2541,7 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd) if (kvm_s390_pv_is_protected(kvm)) break; + kvm_s390_unmap_all_adapters(kvm); mmap_write_lock(kvm->mm); /* * Disable creation of new THPs. Existing THPs can stay, they diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index dc0573b7aa4b..7ba885cb6bd1 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -560,6 +560,8 @@ void kvm_s390_gisa_disable(struct kvm *kvm); void kvm_s390_gisa_enable(struct kvm *kvm); int __init kvm_s390_gib_init(u8 nisc); void kvm_s390_gib_destroy(void); +void kvm_s390_unmap_all_adapters(struct kvm *kvm); + /* implemented in guestdbg.c */ void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); From 1e95e3bc6b0572e60a98c7dde52e27259a802d4d Mon Sep 17 00:00:00 2001 From: Douglas Freimuth Date: Thu, 4 Jun 2026 21:27:54 +0200 Subject: [PATCH 3730/5214] KVM: s390: Enable adapter_indicators_set to use mapped pages The s390 adapter_indicators_set function can now be optimized to use long-term mapped pages when available so that work can be processed on a fast path when interrupts are disabled. If adapter indicator pages are not mapped then local mapping is done on a slow path as it is prior to this patch. For example, Secure Execution environments will take the local mapping path as it does prior to this patch. Reviewed-by: Matthew Rosato Signed-off-by: Douglas Freimuth Acked-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260604192755.203143-3-freimuth@linux.ibm.com> --- arch/s390/kvm/interrupt.c | 87 ++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index d11dea4cb0d3..f276f76f814f 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2892,41 +2892,80 @@ static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap) return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit; } +static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, + u64 addr) +{ + struct s390_map_info *map; + + if (!adapter) + return NULL; + + list_for_each_entry(map, &adapter->maps, list) { + if (map->addr == addr) + return map; + } + return NULL; +} + static int adapter_indicators_set(struct kvm *kvm, struct s390_io_adapter *adapter, struct kvm_s390_adapter_int *adapter_int) { unsigned long bit; int summary_set, idx; - struct page *ind_page, *summary_page; + struct s390_map_info *ind_info, *summary_info; void *map; + struct page *ind_page, *summary_page; + unsigned long flags; - ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0); - if (!ind_page) - return -1; - summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0); - if (!summary_page) { + ind_page = NULL; + + spin_lock_irqsave(&adapter->maps_lock, flags); + ind_info = get_map_info(adapter, adapter_int->ind_addr); + if (!ind_info) { + spin_unlock_irqrestore(&adapter->maps_lock, flags); + ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0); + if (!ind_page) + return -1; + idx = srcu_read_lock(&kvm->srcu); + map = page_address(ind_page); + bit = get_ind_bit(adapter_int->ind_addr, + adapter_int->ind_offset, adapter->swap); + set_bit(bit, map); + mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT); + set_page_dirty_lock(ind_page); + srcu_read_unlock(&kvm->srcu, idx); unpin_user_page(ind_page); - return -1; + } else { + map = page_address(ind_info->page); + bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap); + set_bit(bit, map); + spin_unlock_irqrestore(&adapter->maps_lock, flags); + } + spin_lock_irqsave(&adapter->maps_lock, flags); + summary_info = get_map_info(adapter, adapter_int->summary_addr); + if (!summary_info) { + spin_unlock_irqrestore(&adapter->maps_lock, flags); + summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0); + if (WARN_ON_ONCE(!summary_page)) + return -1; + idx = srcu_read_lock(&kvm->srcu); + map = page_address(summary_page); + bit = get_ind_bit(adapter_int->summary_addr, + adapter_int->summary_offset, adapter->swap); + summary_set = test_and_set_bit(bit, map); + mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT); + set_page_dirty_lock(summary_page); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(summary_page); + } else { + map = page_address(summary_info->page); + bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset, + adapter->swap); + summary_set = test_and_set_bit(bit, map); + spin_unlock_irqrestore(&adapter->maps_lock, flags); } - idx = srcu_read_lock(&kvm->srcu); - map = page_address(ind_page); - bit = get_ind_bit(adapter_int->ind_addr, - adapter_int->ind_offset, adapter->swap); - set_bit(bit, map); - mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT); - set_page_dirty_lock(ind_page); - map = page_address(summary_page); - bit = get_ind_bit(adapter_int->summary_addr, - adapter_int->summary_offset, adapter->swap); - summary_set = test_and_set_bit(bit, map); - mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT); - set_page_dirty_lock(summary_page); - srcu_read_unlock(&kvm->srcu, idx); - - unpin_user_page(ind_page); - unpin_user_page(summary_page); return summary_set ? 0 : 1; } From a868b30492c59f398359b7891293bbde8d126a51 Mon Sep 17 00:00:00 2001 From: Douglas Freimuth Date: Thu, 4 Jun 2026 21:27:55 +0200 Subject: [PATCH 3731/5214] KVM: s390: Introducing kvm_arch_set_irq_inatomic fast inject s390 needs a fast path for irq injection, and along those lines we introduce kvm_arch_set_irq_inatomic. Instead of placing all interrupts on the global work queue as it does today, this patch provides a fast path for irq injection. The inatomic fast path cannot lose control since it is running with interrupts disabled. This meant making the following changes that exist on the slow path today. First, the adapter_indicators page needs to be mapped since it is accessed with interrupts disabled, so we added map/unmap functions. Second, access to shared resources between the fast and slow paths needed to be changed from mutex and semaphores to spin_lock's. Finally, the memory allocation on the slow path utilizes GFP_KERNEL_ACCOUNT but we had to implement the fast path with GFP_ATOMIC allocation. Each of these enhancements were required to prevent blocking on the fast inject path. Fencing of Fast Inject in Secure Execution environments is enabled in the patch series by not mapping adapter indicator pages. In Secure Execution environments the path of execution available before this patch is followed. Statistical counters have been added to enable analysis of irq injection on the fast path and slow path including io_390_inatomic, io_flic_inject_airq, io_set_adapter_int and io_390_inatomic_no_inject. The no inject counter captures adapter masked, coalesced and suppressed interrupts. Reviewed-by: Matthew Rosato Signed-off-by: Douglas Freimuth Acked-by: Claudio Imbrenda Signed-off-by: Claudio Imbrenda Message-ID: <20260604192755.203143-4-freimuth@linux.ibm.com> --- arch/s390/include/asm/kvm_host.h | 6 +- arch/s390/kvm/intercept.c | 5 +- arch/s390/kvm/interrupt.c | 269 ++++++++++++++++++++++++------- arch/s390/kvm/kvm-s390.c | 27 +++- arch/s390/kvm/kvm-s390.h | 3 +- 5 files changed, 243 insertions(+), 67 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 4ff1ede90873..eaa34c5bd3c1 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -359,7 +359,7 @@ struct kvm_s390_float_interrupt { struct kvm_s390_mchk_info mchk; struct kvm_s390_ext_info srv_signal; int last_sleep_cpu; - struct mutex ais_lock; + spinlock_t ais_lock; u8 simm; u8 nimm; }; @@ -450,6 +450,10 @@ struct kvm_vm_stat { u64 inject_io; u64 io_390_adapter_map; u64 io_390_adapter_unmap; + u64 io_390_inatomic; + u64 io_flic_inject_airq; + u64 io_set_adapter_int; + u64 io_390_inatomic_no_inject; u64 inject_float_mchk; u64 inject_pfault_done; u64 inject_service_signal; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 39aff324203e..1980df61ef30 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -517,8 +517,9 @@ static int handle_pv_spx(struct kvm_vcpu *vcpu) static int handle_pv_sclp(struct kvm_vcpu *vcpu) { struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); /* * 2 cases: * a: an sccb answering interrupt was already pending or in flight. @@ -534,7 +535,7 @@ static int handle_pv_sclp(struct kvm_vcpu *vcpu) fi->srv_signal.ext_params |= 0x43000; set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs); clear_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index f276f76f814f..51ea576bb445 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -624,8 +624,9 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu) struct kvm_s390_mchk_info mchk = {}; int deliver = 0; int rc = 0; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); spin_lock(&li->lock); if (test_bit(IRQ_PEND_MCHK_EX, &li->pending_irqs) || test_bit(IRQ_PEND_MCHK_REP, &li->pending_irqs)) { @@ -654,7 +655,7 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu) deliver = 1; } spin_unlock(&li->lock); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); if (deliver) { VCPU_EVENT(vcpu, 3, "deliver: machine check mcic 0x%llx", @@ -941,11 +942,12 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu) { struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_ext_info ext; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); if (test_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs) || !(test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs))) { - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } ext = fi->srv_signal; @@ -954,7 +956,7 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu) clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs); if (kvm_s390_pv_cpu_is_protected(vcpu)) set_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); if (!ext.ext_params) return 0; @@ -972,17 +974,18 @@ static int __must_check __deliver_service_ev(struct kvm_vcpu *vcpu) { struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_ext_info ext; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); if (!(test_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs))) { - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } ext = fi->srv_signal; /* only clear the event bits */ fi->srv_signal.ext_params &= ~SCCB_EVENT_PENDING; clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); VCPU_EVENT(vcpu, 4, "%s", "deliver: sclp parameter event"); vcpu->stat.deliver_service_signal++; @@ -997,8 +1000,9 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu) struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_interrupt_info *inti; int rc = 0; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); inti = list_first_entry_or_null(&fi->lists[FIRQ_LIST_PFAULT], struct kvm_s390_interrupt_info, list); @@ -1008,7 +1012,7 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu) } if (list_empty(&fi->lists[FIRQ_LIST_PFAULT])) clear_bit(IRQ_PEND_PFAULT_DONE, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); if (inti) { trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, @@ -1039,8 +1043,9 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu) struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; struct kvm_s390_interrupt_info *inti; int rc = 0; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); inti = list_first_entry_or_null(&fi->lists[FIRQ_LIST_VIRTIO], struct kvm_s390_interrupt_info, list); @@ -1058,7 +1063,7 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu) } if (list_empty(&fi->lists[FIRQ_LIST_VIRTIO])) clear_bit(IRQ_PEND_VIRTIO, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); if (inti) { rc = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, @@ -1116,10 +1121,11 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, struct kvm_s390_io_info io; u32 isc; int rc = 0; + unsigned long flags; fi = &vcpu->kvm->arch.float_int; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); isc = irq_type_to_isc(irq_type); isc_list = &fi->lists[isc]; inti = list_first_entry_or_null(isc_list, @@ -1146,7 +1152,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, } if (list_empty(isc_list)) clear_bit(irq_type, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); if (inti) { rc = __do_deliver_io(vcpu, &(inti->io)); @@ -1662,8 +1668,9 @@ static struct kvm_s390_interrupt_info *get_io_int(struct kvm *kvm, struct kvm_s390_interrupt_info *iter; u16 id = (schid & 0xffff0000U) >> 16; u16 nr = schid & 0x0000ffffU; + unsigned long flags; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); list_for_each_entry(iter, isc_list, list) { if (schid && (id != iter->io.subchannel_id || nr != iter->io.subchannel_nr)) @@ -1673,10 +1680,10 @@ static struct kvm_s390_interrupt_info *get_io_int(struct kvm *kvm, fi->counters[FIRQ_CNTR_IO] -= 1; if (list_empty(isc_list)) clear_bit(isc_to_irq_type(isc), &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return iter; } - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return NULL; } @@ -1769,9 +1776,10 @@ static int __inject_service(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; + unsigned long flags; kvm->stat.inject_service_signal++; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); fi->srv_signal.ext_params |= inti->ext.ext_params & SCCB_EVENT_PENDING; /* We always allow events, track them separately from the sccb ints */ @@ -1791,7 +1799,7 @@ static int __inject_service(struct kvm *kvm, fi->srv_signal.ext_params |= inti->ext.ext_params & SCCB_MASK; set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs); out: - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); kfree(inti); return 0; } @@ -1800,17 +1808,18 @@ static int __inject_virtio(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; + unsigned long flags; kvm->stat.inject_virtio++; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); if (fi->counters[FIRQ_CNTR_VIRTIO] >= KVM_S390_MAX_VIRTIO_IRQS) { - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return -EBUSY; } fi->counters[FIRQ_CNTR_VIRTIO] += 1; list_add_tail(&inti->list, &fi->lists[FIRQ_LIST_VIRTIO]); set_bit(IRQ_PEND_VIRTIO, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } @@ -1818,18 +1827,19 @@ static int __inject_pfault_done(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; + unsigned long flags; kvm->stat.inject_pfault_done++; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); if (fi->counters[FIRQ_CNTR_PFAULT] >= (ASYNC_PF_PER_VCPU * KVM_MAX_VCPUS)) { - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return -EBUSY; } fi->counters[FIRQ_CNTR_PFAULT] += 1; list_add_tail(&inti->list, &fi->lists[FIRQ_LIST_PFAULT]); set_bit(IRQ_PEND_PFAULT_DONE, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } @@ -1838,13 +1848,14 @@ static int __inject_float_mchk(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; + unsigned long flags; kvm->stat.inject_float_mchk++; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); fi->mchk.cr14 |= inti->mchk.cr14 & (1UL << CR_PENDING_SUBCLASS); fi->mchk.mcic |= inti->mchk.mcic; set_bit(IRQ_PEND_MCHK_REP, &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); kfree(inti); return 0; } @@ -1855,6 +1866,7 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) struct kvm_s390_float_interrupt *fi; struct list_head *list; int isc; + unsigned long flags; kvm->stat.inject_io++; isc = int_word_to_isc(inti->io.io_int_word); @@ -1873,9 +1885,9 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) } fi = &kvm->arch.float_int; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); if (fi->counters[FIRQ_CNTR_IO] >= KVM_S390_MAX_FLOAT_IRQS) { - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return -EBUSY; } fi->counters[FIRQ_CNTR_IO] += 1; @@ -1890,7 +1902,7 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) list = &fi->lists[FIRQ_LIST_IO_ISC_0 + isc]; list_add_tail(&inti->list, list); set_bit(isc_to_irq_type(isc), &fi->pending_irqs); - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); return 0; } @@ -1966,15 +1978,10 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) } int kvm_s390_inject_vm(struct kvm *kvm, - struct kvm_s390_interrupt *s390int) + struct kvm_s390_interrupt *s390int, struct kvm_s390_interrupt_info *inti) { - struct kvm_s390_interrupt_info *inti; int rc; - inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT); - if (!inti) - return -ENOMEM; - inti->type = s390int->type; switch (inti->type) { case KVM_S390_INT_VIRTIO: @@ -2003,15 +2010,13 @@ int kvm_s390_inject_vm(struct kvm *kvm, inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; break; default: - kfree(inti); return -EINVAL; } trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64, 2); rc = __inject_vm(kvm, inti); - if (rc) - kfree(inti); + return rc; } @@ -2176,12 +2181,13 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; int i; + unsigned long flags; mutex_lock(&kvm->lock); if (!kvm_s390_pv_is_protected(kvm)) fi->masked_irqs = 0; mutex_unlock(&kvm->lock); - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); fi->pending_irqs = 0; memset(&fi->srv_signal, 0, sizeof(fi->srv_signal)); memset(&fi->mchk, 0, sizeof(fi->mchk)); @@ -2189,7 +2195,7 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm) clear_irq_list(&fi->lists[i]); for (i = 0; i < FIRQ_MAX_COUNT; i++) fi->counters[i] = 0; - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); kvm_s390_gisa_clear(kvm); }; @@ -2204,6 +2210,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) int ret = 0; int n = 0; int i; + unsigned long flags; if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0) return -EINVAL; @@ -2235,7 +2242,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) } } fi = &kvm->arch.float_int; - spin_lock(&fi->lock); + spin_lock_irqsave(&fi->lock, flags); for (i = 0; i < FIRQ_LIST_COUNT; i++) { list_for_each_entry(inti, &fi->lists[i], list) { if (n == max_irqs) { @@ -2272,7 +2279,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) } out: - spin_unlock(&fi->lock); + spin_unlock_irqrestore(&fi->lock, flags); out_nolock: if (!ret && n > 0) { if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n)) @@ -2287,6 +2294,7 @@ static int flic_ais_mode_get_all(struct kvm *kvm, struct kvm_device_attr *attr) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; struct kvm_s390_ais_all ais; + unsigned long flags; if (attr->attr < sizeof(ais)) return -EINVAL; @@ -2294,10 +2302,10 @@ static int flic_ais_mode_get_all(struct kvm *kvm, struct kvm_device_attr *attr) if (!test_kvm_facility(kvm, 72)) return -EOPNOTSUPP; - mutex_lock(&fi->ais_lock); + spin_lock_irqsave(&fi->ais_lock, flags); ais.simm = fi->simm; ais.nimm = fi->nimm; - mutex_unlock(&fi->ais_lock); + spin_unlock_irqrestore(&fi->ais_lock, flags); if (copy_to_user((void __user *)attr->addr, &ais, sizeof(ais))) return -EFAULT; @@ -2683,6 +2691,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr) struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; struct kvm_s390_ais_req req; int ret = 0; + unsigned long flags; if (!test_kvm_facility(kvm, 72)) return -EOPNOTSUPP; @@ -2699,7 +2708,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr) 2 : KVM_S390_AIS_MODE_SINGLE : KVM_S390_AIS_MODE_ALL, req.mode); - mutex_lock(&fi->ais_lock); + spin_lock_irqsave(&fi->ais_lock, flags); switch (req.mode) { case KVM_S390_AIS_MODE_ALL: fi->simm &= ~AIS_MODE_MASK(req.isc); @@ -2712,7 +2721,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr) default: ret = -EINVAL; } - mutex_unlock(&fi->ais_lock); + spin_unlock_irqrestore(&fi->ais_lock, flags); return ret; } @@ -2726,25 +2735,41 @@ static int kvm_s390_inject_airq(struct kvm *kvm, .parm = 0, .parm64 = isc_to_int_word(adapter->isc), }; + struct kvm_s390_interrupt_info *inti; + unsigned long flags; + int ret = 0; - if (!test_kvm_facility(kvm, 72) || !adapter->suppressible) - return kvm_s390_inject_vm(kvm, &s390int); + inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT); + if (!inti) + return -ENOMEM; - mutex_lock(&fi->ais_lock); - if (fi->nimm & AIS_MODE_MASK(adapter->isc)) { - trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc); - goto out; + if (!test_kvm_facility(kvm, 72) || !adapter->suppressible) { + ret = kvm_s390_inject_vm(kvm, &s390int, inti); + if (ret) + kfree(inti); + return ret; } - ret = kvm_s390_inject_vm(kvm, &s390int); + spin_lock_irqsave(&fi->ais_lock, flags); + if (fi->nimm & AIS_MODE_MASK(adapter->isc)) { + trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc); + spin_unlock_irqrestore(&fi->ais_lock, flags); + kfree(inti); + return ret; + } + + ret = kvm_s390_inject_vm(kvm, &s390int, inti); + if (!ret && (fi->simm & AIS_MODE_MASK(adapter->isc))) { fi->nimm |= AIS_MODE_MASK(adapter->isc); trace_kvm_s390_modify_ais_mode(adapter->isc, KVM_S390_AIS_MODE_SINGLE, 2); } -out: - mutex_unlock(&fi->ais_lock); + + spin_unlock_irqrestore(&fi->ais_lock, flags); + if (ret) + kfree(inti); return ret; } @@ -2753,6 +2778,8 @@ static int flic_inject_airq(struct kvm *kvm, struct kvm_device_attr *attr) unsigned int id = attr->attr; struct s390_io_adapter *adapter = get_io_adapter(kvm, id); + kvm->stat.io_flic_inject_airq++; + if (!adapter) return -EINVAL; @@ -2763,6 +2790,7 @@ static int flic_ais_mode_set_all(struct kvm *kvm, struct kvm_device_attr *attr) { struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; struct kvm_s390_ais_all ais; + unsigned long flags; if (!test_kvm_facility(kvm, 72)) return -EOPNOTSUPP; @@ -2770,10 +2798,10 @@ static int flic_ais_mode_set_all(struct kvm *kvm, struct kvm_device_attr *attr) if (copy_from_user(&ais, (void __user *)attr->addr, sizeof(ais))) return -EFAULT; - mutex_lock(&fi->ais_lock); + spin_lock_irqsave(&fi->ais_lock, flags); fi->simm = ais.simm; fi->nimm = ais.nimm; - mutex_unlock(&fi->ais_lock); + spin_unlock_irqrestore(&fi->ais_lock, flags); return 0; } @@ -2942,6 +2970,7 @@ static int adapter_indicators_set(struct kvm *kvm, set_bit(bit, map); spin_unlock_irqrestore(&adapter->maps_lock, flags); } + spin_lock_irqsave(&adapter->maps_lock, flags); summary_info = get_map_info(adapter, adapter_int->summary_addr); if (!summary_info) { @@ -2969,6 +2998,44 @@ static int adapter_indicators_set(struct kvm *kvm, return summary_set ? 0 : 1; } +static int adapter_indicators_set_fast(struct kvm *kvm, + struct s390_io_adapter *adapter, + struct kvm_s390_adapter_int *adapter_int, + int setbit) +{ + unsigned long bit; + int summary_set; + struct s390_map_info *ind_info, *summary_info; + void *map; + + spin_lock(&adapter->maps_lock); + ind_info = get_map_info(adapter, adapter_int->ind_addr); + if (!ind_info) { + spin_unlock(&adapter->maps_lock); + return -EWOULDBLOCK; + } + map = page_address(ind_info->page); + bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap); + if (setbit) + set_bit(bit, map); + summary_info = get_map_info(adapter, adapter_int->summary_addr); + if (!summary_info) { + spin_unlock(&adapter->maps_lock); + return -EWOULDBLOCK; + } + map = page_address(summary_info->page); + bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset, + adapter->swap); + /* If setbit then set summary bit. Else if falling back to the slow path */ + /* with setbit==0 then clear the summary bit so the slow path re-injects */ + if (setbit) + summary_set = test_and_set_bit(bit, map); + else + summary_set = test_and_clear_bit(bit, map); + spin_unlock(&adapter->maps_lock); + return summary_set ? 0 : 1; +} + /* * < 0 - not injected due to error * = 0 - coalesced, summary indicator already active @@ -2981,6 +3048,8 @@ static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e, int ret; struct s390_io_adapter *adapter; + kvm->stat.io_set_adapter_int++; + /* We're only interested in the 0->1 transition. */ if (!level) return 0; @@ -3049,7 +3118,6 @@ int kvm_set_routing_entry(struct kvm *kvm, int idx; switch (ue->type) { - /* we store the userspace addresses instead of the guest addresses */ case KVM_IRQ_ROUTING_S390_ADAPTER: if (kvm_is_ucontrol(kvm)) return -EINVAL; @@ -3640,3 +3708,86 @@ int __init kvm_s390_gib_init(u8 nisc) out: return rc; } + +/* + * kvm_arch_set_irq_inatomic: fast-path for irqfd injection + */ +int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, + bool line_status) +{ + int ret, setbit; + struct s390_io_adapter *adapter; + struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int; + struct kvm_s390_interrupt_info *inti; + struct kvm_s390_interrupt s390int = { + .type = KVM_S390_INT_IO(1, 0, 0, 0), + .parm = 0, + }; + + kvm->stat.io_390_inatomic++; + + /* We're only interested in the 0->1 transition. */ + if (!level) + return 0; + if (e->type != KVM_IRQ_ROUTING_S390_ADAPTER) + return -EWOULDBLOCK; + + adapter = get_io_adapter(kvm, e->adapter.adapter_id); + if (!adapter) + return -EWOULDBLOCK; + + s390int.parm64 = isc_to_int_word(adapter->isc); + setbit = 1; + ret = adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit); + if (ret < 0) + return -EWOULDBLOCK; + if (!ret || adapter->masked) { + kvm->stat.io_390_inatomic_no_inject++; + return 0; + } + + inti = kzalloc_obj(*inti, GFP_ATOMIC); + if (!inti) { + setbit = 0; + adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit); + return -EWOULDBLOCK; + } + + if (!test_kvm_facility(kvm, 72) || !adapter->suppressible) { + ret = kvm_s390_inject_vm(kvm, &s390int, inti); + if (ret == 0) { + return ret; + } else { + setbit = 0; + adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit); + kfree(inti); + return -EWOULDBLOCK; + } + } + + spin_lock(&fi->ais_lock); + if (fi->nimm & AIS_MODE_MASK(adapter->isc)) { + trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc); + spin_unlock(&fi->ais_lock); + kfree(inti); + kvm->stat.io_390_inatomic_no_inject++; + return 0; + } + + ret = kvm_s390_inject_vm(kvm, &s390int, inti); + if (!ret && (fi->simm & AIS_MODE_MASK(adapter->isc))) { + fi->nimm |= AIS_MODE_MASK(adapter->isc); + trace_kvm_s390_modify_ais_mode(adapter->isc, + KVM_S390_AIS_MODE_SINGLE, 2); + } else if (ret) { + spin_unlock(&fi->ais_lock); + setbit = 0; + adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit); + kfree(inti); + return -EWOULDBLOCK; + } + + spin_unlock(&fi->ais_lock); + return 0; +} diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9de6edeb5ae0..80b4bc497494 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -70,6 +70,10 @@ const struct kvm_stats_desc kvm_vm_stats_desc[] = { STATS_DESC_COUNTER(VM, inject_io), STATS_DESC_COUNTER(VM, io_390_adapter_map), STATS_DESC_COUNTER(VM, io_390_adapter_unmap), + STATS_DESC_COUNTER(VM, io_390_inatomic), + STATS_DESC_COUNTER(VM, io_flic_inject_airq), + STATS_DESC_COUNTER(VM, io_set_adapter_int), + STATS_DESC_COUNTER(VM, io_390_inatomic_no_inject), STATS_DESC_COUNTER(VM, inject_float_mchk), STATS_DESC_COUNTER(VM, inject_pfault_done), STATS_DESC_COUNTER(VM, inject_service_signal), @@ -2877,6 +2881,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) void __user *argp = (void __user *)arg; struct kvm_device_attr attr; int r; + struct kvm_s390_interrupt_info *inti; switch (ioctl) { case KVM_S390_INTERRUPT: { @@ -2885,7 +2890,12 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) r = -EFAULT; if (copy_from_user(&s390int, argp, sizeof(s390int))) break; - r = kvm_s390_inject_vm(kvm, &s390int); + inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT); + if (!inti) + return -ENOMEM; + r = kvm_s390_inject_vm(kvm, &s390int, inti); + if (r) + kfree(inti); break; } case KVM_CREATE_IRQCHIP: { @@ -3286,7 +3296,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) mutex_unlock(&kvm->lock); } - mutex_init(&kvm->arch.float_int.ais_lock); + spin_lock_init(&kvm->arch.float_int.ais_lock); spin_lock_init(&kvm->arch.float_int.lock); for (i = 0; i < FIRQ_LIST_COUNT; i++) INIT_LIST_HEAD(&kvm->arch.float_int.lists[i]); @@ -4408,19 +4418,28 @@ int kvm_s390_try_set_tod_clock(struct kvm *kvm, const struct kvm_s390_vm_tod_clo } static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token, - unsigned long token) + unsigned long token) { struct kvm_s390_interrupt inti; struct kvm_s390_irq irq; + struct kvm_s390_interrupt_info *inti_mem = NULL; + int ret = 0; if (start_token) { irq.u.ext.ext_params2 = token; irq.type = KVM_S390_INT_PFAULT_INIT; WARN_ON_ONCE(kvm_s390_inject_vcpu(vcpu, &irq)); } else { + inti_mem = kzalloc_obj(*inti_mem, GFP_KERNEL_ACCOUNT); + if (WARN_ON_ONCE(!inti_mem)) + return; + inti.type = KVM_S390_INT_PFAULT_DONE; inti.parm64 = token; - WARN_ON_ONCE(kvm_s390_inject_vm(vcpu->kvm, &inti)); + ret = kvm_s390_inject_vm(vcpu->kvm, &inti, inti_mem); + if (ret) + kfree(inti_mem); + WARN_ON_ONCE(ret); } } diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 7ba885cb6bd1..6d2842fb71a3 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -376,7 +376,8 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu); void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu); void kvm_s390_clear_float_irqs(struct kvm *kvm); int __must_check kvm_s390_inject_vm(struct kvm *kvm, - struct kvm_s390_interrupt *s390int); + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti); int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq); static inline int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, From cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 1 Jun 2026 23:11:54 +0300 Subject: [PATCH 3732/5214] KEYS: fix overflow in keyctl_pkey_params_get_2() The length for the internal output buffer is calculated incorrectly, which can result overflow when a too small buffer is provided. Fix the bug by allocating internal output with the size of the maximum length of the cryptographic primitive instead of caller provided size. Link: https://lore.kernel.org/keyrings/20260531024914.3712130-1-jarkko@kernel.org/ Cc: stable@vger.kernel.org # v4.20+ Fixes: 00d60fd3b932 ("KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2]") Reported-by: Alessandro Groppo Tested-by: Alessandro Groppo Signed-off-by: Jarkko Sakkinen --- security/keys/keyctl_pkey.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c index 97bc27bbf079..ba150ee2d4a3 100644 --- a/security/keys/keyctl_pkey.c +++ b/security/keys/keyctl_pkey.c @@ -138,28 +138,35 @@ static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_par if (uparams.in_len > info.max_dec_size || uparams.out_len > info.max_enc_size) return -EINVAL; + + params->out_len = info.max_enc_size; break; case KEYCTL_PKEY_DECRYPT: if (uparams.in_len > info.max_enc_size || uparams.out_len > info.max_dec_size) return -EINVAL; + + params->out_len = info.max_dec_size; break; case KEYCTL_PKEY_SIGN: if (uparams.in_len > info.max_data_size || uparams.out_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; case KEYCTL_PKEY_VERIFY: if (uparams.in_len > info.max_data_size || uparams.in2_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; default: BUG(); } params->in_len = uparams.in_len; - params->out_len = uparams.out_len; /* Note: same as in2_len */ return 0; } From 3a1705d180b203a6764d2a142d602bbf522d339b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Apr 2026 11:41:35 +0300 Subject: [PATCH 3733/5214] KEYS: encrypted: Remove unnecessary selection of CRYPTO_RNG encrypted-keys uses the regular Linux RNG (get_random_bytes()), not the duplicative crypto_rng one. So it does not need to select CRYPTO_RNG. Signed-off-by: Eric Biggers Reviewed-by: Mimi Zohar Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- security/keys/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 84f39e50ca36..f4510d8cb485 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -87,7 +87,6 @@ config ENCRYPTED_KEYS select CRYPTO_AES select CRYPTO_CBC select CRYPTO_LIB_SHA256 - select CRYPTO_RNG help This option provides support for create/encrypting/decrypting keys in the kernel. Encrypted keys are instantiated using kernel From 4d05e948cebe03974ab9927daee55273207fdc22 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 9 Apr 2026 19:07:51 +0300 Subject: [PATCH 3734/5214] KEYS: trusted: Debugging as a feature TPM_DEBUG, and other similar flags, are a non-standard way to specify a feature in Linux kernel. Introduce CONFIG_TRUSTED_KEYS_DEBUG for trusted keys, and use it to replace these ad-hoc feature flags. Given that trusted keys debug dumps can contain sensitive data, harden the feature as follows: 1. In the Kconfig description postulate that pr_debug() statements must be used. 2. Use pr_debug() statements in TPM 1.x driver to print the protocol dump. 3. Require trusted.debug=1 on the kernel command line (default: 0) to activate dumps at runtime, even when CONFIG_TRUSTED_KEYS_DEBUG=y. Traces, when actually needed, can be easily enabled by providing trusted.dyndbg='+p' and trusted.debug=1 in the kernel command-line. Reported-by: Nayna Jain Closes: https://lore.kernel.org/all/7f8b8478-5cd8-4d97-bfd0-341fd5cf10f9@linux.ibm.com/ Reviewed-by: Nayna Jain Tested-by: Srish Srinivasan Signed-off-by: Jarkko Sakkinen --- .../admin-guide/kernel-parameters.txt | 16 +++++++ include/keys/trusted-type.h | 21 +++++---- security/keys/trusted-keys/Kconfig | 23 ++++++++++ security/keys/trusted-keys/trusted_caam.c | 7 ++- security/keys/trusted-keys/trusted_core.c | 6 +++ security/keys/trusted-keys/trusted_tpm1.c | 44 +++++++++++-------- 6 files changed, 87 insertions(+), 30 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 672665ea6265..2f0056b8e692 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7875,6 +7875,22 @@ Kernel parameters first trust source as a backend which is initialized successfully during iteration. + trusted.debug= [KEYS] + Format: + Enable trusted keys debug traces at runtime when + CONFIG_TRUSTED_KEYS_DEBUG=y. + + To make the traces visible after enabling the option, + use trusted.dyndbg='+p' as needed. By convention, + the subsystem uses pr_debug() for these traces. + + SAFETY: The traces can leak sensitive data, so be + cautious before enabling this. They remain inactive + unless this parameter is set this option to a true + value. + + Default: false + trusted.rng= [KEYS] Format: The RNG used to generate key material for trusted keys. diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index 03527162613f..9f9940482da4 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -83,18 +83,21 @@ struct trusted_key_source { extern struct key_type key_type_trusted; -#define TRUSTED_DEBUG 0 +#ifdef CONFIG_TRUSTED_KEYS_DEBUG +extern bool trusted_debug; -#if TRUSTED_DEBUG static inline void dump_payload(struct trusted_key_payload *p) { - pr_info("key_len %d\n", p->key_len); - print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE, - 16, 1, p->key, p->key_len, 0); - pr_info("bloblen %d\n", p->blob_len); - print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE, - 16, 1, p->blob, p->blob_len, 0); - pr_info("migratable %d\n", p->migratable); + if (!trusted_debug) + return; + + pr_debug("key_len %d\n", p->key_len); + print_hex_dump_debug("key ", DUMP_PREFIX_NONE, + 16, 1, p->key, p->key_len, 0); + pr_debug("bloblen %d\n", p->blob_len); + print_hex_dump_debug("blob ", DUMP_PREFIX_NONE, + 16, 1, p->blob, p->blob_len, 0); + pr_debug("migratable %d\n", p->migratable); } #else static inline void dump_payload(struct trusted_key_payload *p) diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig index 9e00482d886a..e5a4a53aeab2 100644 --- a/security/keys/trusted-keys/Kconfig +++ b/security/keys/trusted-keys/Kconfig @@ -1,10 +1,29 @@ config HAVE_TRUSTED_KEYS bool +config HAVE_TRUSTED_KEYS_DEBUG + bool + +config TRUSTED_KEYS_DEBUG + bool "Debug trusted keys" + depends on HAVE_TRUSTED_KEYS_DEBUG + default n + help + Trusted key backends and core code that support debug traces can + opt-in that feature here. Traces must only use debug level output, as + sensitive data may pass by. In the kernel-command line traces can be + enabled via trusted.dyndbg='+p'. + + SAFETY: Debug dumps are inactive at runtime until trusted.debug is set + to a true value on the kernel command-line. Use at your utmost + consideration when enabling this feature on a production build. The + general advice is not to do this. + config TRUSTED_KEYS_TPM bool "TPM-based trusted keys" depends on TCG_TPM >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select CRYPTO_HASH_INFO select CRYPTO_LIB_SHA1 select CRYPTO_LIB_UTILS @@ -23,6 +42,7 @@ config TRUSTED_KEYS_TEE bool "TEE-based trusted keys" depends on TEE >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of the Trusted Execution Environment (TEE) as trusted @@ -33,6 +53,7 @@ config TRUSTED_KEYS_CAAM depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS select CRYPTO_DEV_FSL_CAAM_BLOB_GEN default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of NXP's Cryptographic Accelerator and Assurance Module @@ -42,6 +63,7 @@ config TRUSTED_KEYS_DCP bool "DCP-based trusted keys" depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of NXP's DCP (Data Co-Processor) as trusted key backend. @@ -50,6 +72,7 @@ config TRUSTED_KEYS_PKWM bool "PKWM-based trusted keys" depends on PSERIES_PLPKS >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of IBM PowerVM Key Wrapping Module (PKWM) as a trusted key backend. diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c index 601943ce0d60..6a33dbf2a7f5 100644 --- a/security/keys/trusted-keys/trusted_caam.c +++ b/security/keys/trusted-keys/trusted_caam.c @@ -28,10 +28,13 @@ static const match_table_t key_tokens = { {opt_err, NULL} }; -#ifdef CAAM_DEBUG +#ifdef CONFIG_TRUSTED_KEYS_DEBUG static inline void dump_options(const struct caam_pkey_info *pkey_info) { - pr_info("key encryption algo %d\n", pkey_info->key_enc_algo); + if (!trusted_debug) + return; + + pr_debug("key encryption algo %d\n", pkey_info->key_enc_algo); } #else static inline void dump_options(const struct caam_pkey_info *pkey_info) diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index 0b142d941cd2..6aed17bee09d 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -31,6 +31,12 @@ static char *trusted_rng = "default"; module_param_named(rng, trusted_rng, charp, 0); MODULE_PARM_DESC(rng, "Select trusted key RNG"); +#ifdef CONFIG_TRUSTED_KEYS_DEBUG +bool trusted_debug; +module_param_named(debug, trusted_debug, bool, 0); +MODULE_PARM_DESC(debug, "Enable trusted keys debug traces (default: 0)"); +#endif + static char *trusted_key_source; module_param_named(source, trusted_key_source, charp, 0); MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam, dcp or pkwm)"); diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 6ea728f1eae6..13513819991e 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -46,38 +46,44 @@ enum { SRK_keytype = 4 }; -#define TPM_DEBUG 0 - -#if TPM_DEBUG +#ifdef CONFIG_TRUSTED_KEYS_DEBUG static inline void dump_options(struct trusted_key_options *o) { - pr_info("sealing key type %d\n", o->keytype); - pr_info("sealing key handle %0X\n", o->keyhandle); - pr_info("pcrlock %d\n", o->pcrlock); - pr_info("pcrinfo %d\n", o->pcrinfo_len); - print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE, - 16, 1, o->pcrinfo, o->pcrinfo_len, 0); + if (!trusted_debug) + return; + + pr_debug("sealing key type %d\n", o->keytype); + pr_debug("sealing key handle %0X\n", o->keyhandle); + pr_debug("pcrlock %d\n", o->pcrlock); + pr_debug("pcrinfo %d\n", o->pcrinfo_len); + print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE, + 16, 1, o->pcrinfo, o->pcrinfo_len, 0); } static inline void dump_sess(struct osapsess *s) { - print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE, - 16, 1, &s->handle, 4, 0); - pr_info("secret:\n"); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, - 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); - pr_info("trusted-key: enonce:\n"); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, - 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); + if (!trusted_debug) + return; + + print_hex_dump_debug("trusted-key: handle ", DUMP_PREFIX_NONE, + 16, 1, &s->handle, 4, 0); + pr_debug("secret:\n"); + print_hex_dump_debug("", DUMP_PREFIX_NONE, + 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); + pr_debug("trusted-key: enonce:\n"); + print_hex_dump_debug("", DUMP_PREFIX_NONE, + 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); } static inline void dump_tpm_buf(unsigned char *buf) { int len; - pr_info("\ntpm buffer\n"); + if (!trusted_debug) + return; + pr_debug("\ntpm buffer\n"); len = LOAD32(buf, TPM_SIZE_OFFSET); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); + print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); } #else static inline void dump_options(struct trusted_key_options *o) From 8f2e22cfc45903b28bb9c5829a9ddbccc5001ea3 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 4 May 2026 11:31:00 +0200 Subject: [PATCH 3735/5214] keys: use kmalloc_flex in user_preparse Use kmalloc_flex() when allocating a new struct user_key_payload in user_preparse() to replace the open-coded size arithmetic and to keep the size type-safe. Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20260504093058.49720-3-thorsten.blum@linux.dev Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- security/keys/user_defined.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 686d56e4cc85..6f88b507f927 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -64,7 +64,7 @@ int user_preparse(struct key_preparsed_payload *prep) if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; - upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + upayload = kmalloc_flex(*upayload, data, datalen); if (!upayload) return -ENOMEM; From cc99abbe2aa7aed48fc7d8d21514240e063ea732 Mon Sep 17 00:00:00 2001 From: Len Bao Date: Sat, 16 May 2026 15:22:47 +0000 Subject: [PATCH 3736/5214] keys/trusted_keys: mark 'migratable' as __ro_after_init The 'migratable' variable is initialized only during the init phase in the 'init_trusted' function and never changed. So, mark it as __ro_after_init. Signed-off-by: Len Bao Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260516152249.41851-1-len.bao@gmx.us Signed-off-by: Jarkko Sakkinen --- security/keys/trusted-keys/trusted_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index 6aed17bee09d..0509d9955f2a 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -65,7 +65,7 @@ DEFINE_STATIC_CALL_NULL(trusted_key_unseal, DEFINE_STATIC_CALL_NULL(trusted_key_get_random, *trusted_key_sources[0].ops->get_random); static void (*trusted_key_exit)(void); -static unsigned char migratable; +static unsigned char migratable __ro_after_init; enum { Opt_err, From c1201b37f666f6466ab1fd3a381c2b7a4b7e9fee Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 29 May 2026 11:34:06 +0800 Subject: [PATCH 3737/5214] KEYS: Use acquire when reading state in keyring search The negative-key race fix added release/acquire ordering for key use. Publish payload before state; read state before payload. keyring_search_iterator() still uses READ_ONCE() before match callbacks. An asymmetric match callback calls asymmetric_key_ids(), which reads key->payload.data[asym_key_ids]. Use key_read_state() there to complete that ordering. Fixes: 363b02dab09b ("KEYS: Fix race between updating and finding a negative key") Signed-off-by: Gui-Dong Han Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260529033406.20673-1-hanguidong02@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/keyring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5a9887d6b7be..7a2ee0ded7c9 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) struct keyring_search_context *ctx = iterator_data; const struct key *key = keyring_ptr_to_key(object); unsigned long kflags = READ_ONCE(key->flags); - short state = READ_ONCE(key->state); + short state = key_read_state(key); kenter("{%d}", key->serial); From 44b9597fea4b4d6d79d8b70a297ea425e05543c1 Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:26:03 +0100 Subject: [PATCH 3738/5214] keys: Replace strcpy(derived_buf, "AUTH_KEY") with strscpy(..., HASH_SIZE) derived_buf is guaranteed to be HASH_SIZE - and it is more than enough. The strscpy() degenerates into an memcpy() (as did the strcpy()). Do the same for the associated "ENC_KEY" copy. Removes a possibly unbounded strcpy(). Signed-off-by: David Laight Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260606202633.5018-9-david.laight.linux@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/encrypted-keys/encrypted.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 56b531587a1e..59cb77b237b3 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -343,9 +343,9 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, return -ENOMEM; if (key_type) - strcpy(derived_buf, "AUTH_KEY"); + strscpy(derived_buf, "AUTH_KEY", HASH_SIZE); else - strcpy(derived_buf, "ENC_KEY"); + strscpy(derived_buf, "ENC_KEY", HASH_SIZE); memcpy(derived_buf + strlen(derived_buf) + 1, master_key, master_keylen); From 0934c38b12bd838cc133d5895fc8b42c2c1717ee Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Wed, 10 Jun 2026 07:50:52 +0100 Subject: [PATCH 3739/5214] keys: prevent slab cache merging for key_jar Add SLAB_NO_MERGE to key_jar to prevent the allocator from merging it with other similarly-sized caches. This hardens struct key isolation by ensuring dedicated slab pages. Acked-by: Vlastimil Babka (SUSE) Signed-off-by: Mohammed EL Kadiri Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260610065052.9120-1-med08elkadiri@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/key.c b/security/keys/key.c index 091ee084bc30..b34a64d81d47 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1275,7 +1275,7 @@ void __init key_init(void) { /* allocate a slab in which we can store keys */ key_jar = kmem_cache_create("key_jar", sizeof(struct key), - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_NO_MERGE, NULL); /* add the special key types */ list_add_tail(&key_type_keyring.link, &key_types_list); From fd15b457a86939c38aa12116adabd8ff686c5e51 Mon Sep 17 00:00:00 2001 From: Shaomin Chen Date: Wed, 10 Jun 2026 13:10:05 +0300 Subject: [PATCH 3740/5214] keys: Pin request_key_auth payload in instantiate paths A: request_key() B: KEYCTL_INSTANTIATE_IOV ================ ========================= create auth key store rka in auth key wait for helper get auth key load rka from auth key copy user payload sleep on #PF helper completed detach and free rka destroy auth key wake up use rka->target_key **USE-AFTER-FREE** Give request_key_auth payloads a refcount. Take a payload reference while authkey->sem stabilizes the payload and revocation state. Hold that reference across the instantiate and reject paths. Drop the auth key owning reference from revoke and destroy. [jarkko: Replaced the first two paragraphs of text with an actual concurrency scenario.] Cc: stable@vger.kernel.org # v5.10+ Fixes: b5f545c880a2 ("[PATCH] keys: Permit running process to instantiate keys") Reported-by: Shaomin Chen Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com Signed-off-by: Shaomin Chen Signed-off-by: Jarkko Sakkinen --- include/keys/request_key_auth-type.h | 2 ++ security/keys/internal.h | 2 ++ security/keys/keyctl.c | 24 +++++++++++++++----- security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h index 36b89a933310..01e42ee5f409 100644 --- a/include/keys/request_key_auth-type.h +++ b/include/keys/request_key_auth-type.h @@ -9,12 +9,14 @@ #define _KEYS_REQUEST_KEY_AUTH_TYPE_H #include +#include /* * Authorisation record for request_key(). */ struct request_key_auth { struct rcu_head rcu; + refcount_t usage; struct key *target_key; struct key *dest_keyring; const struct cred *cred; diff --git a/security/keys/internal.h b/security/keys/internal.h index 2cffa6dc8255..b7b622bc36a1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target, const void *callout_info, size_t callout_len, struct key *dest_keyring); +struct request_key_auth *request_key_auth_get(struct key *authkey); +void request_key_auth_put(struct request_key_auth *rka); extern struct key *key_get_instantiation_authkey(key_serial_t target_id); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ef855d69c97a..d14ace88e529 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* pull the payload in if one was supplied */ payload = NULL; @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id, ret = -ENOMEM; payload = kvmalloc(plen, GFP_KERNEL); if (!payload) - goto error; + goto error_put_rka; ret = -EFAULT; if (!copy_from_iter_full(payload, plen, from)) @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id, error2: kvfree_sensitive(payload, plen); +error_put_rka: + request_key_auth_put(rka); error: return ret; } @@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* find the destination keyring if present (which must also be * writable) */ ret = get_instantiation_keyring(ringid, rka, &dest_keyring); if (ret < 0) - goto error; + goto error_put_rka; /* instantiate the key and link it into a keyring */ ret = key_reject_and_link(rka->target_key, timeout, error, @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (ret == 0) keyctl_change_reqkey_auth(NULL); +error_put_rka: + request_key_auth_put(rka); error: return ret; } diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index a7d7538c1f70..282e09d8fa46 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *); static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char *, size_t); +static void request_key_auth_rcu_disposal(struct rcu_head *); /* * The request-key authorisation key type definition. @@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_auth *rka) kfree(rka); } +/* + * Take a reference to the request-key authorisation payload so callers can + * drop authkey->sem before doing operations that may sleep. + */ +struct request_key_auth *request_key_auth_get(struct key *authkey) +{ + struct request_key_auth *rka; + + down_read(&authkey->sem); + rka = dereference_key_locked(authkey); + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags)) + refcount_inc(&rka->usage); + else + rka = NULL; + up_read(&authkey->sem); + + return rka; +} + +void request_key_auth_put(struct request_key_auth *rka) +{ + if (rka && refcount_dec_and_test(&rka->usage)) + call_rcu(&rka->rcu, request_key_auth_rcu_disposal); +} + /* * Dispose of the request_key_auth record under RCU conditions */ @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key) struct request_key_auth *rka = dereference_key_locked(key); kenter("{%d}", key->serial); + if (!rka) + return; rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } /* @@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key) kenter("{%d}", key->serial); if (rka) { rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } } @@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, rka = kzalloc_obj(*rka); if (!rka) goto error; + refcount_set(&rka->usage, 1); rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL); if (!rka->callout_info) goto error_free_rka; From 10366fe27a4ce08a392eb16ed48ea0a440e671c2 Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Sat, 13 Jun 2026 14:04:07 +0100 Subject: [PATCH 3741/5214] keys: request_key: replace BUG with return -EINVAL Replace BUG() in construct_get_dest_keyring() default case with return -EINVAL to handle the unimplemented group keyring destination gracefully. Signed-off-by: Mohammed EL Kadiri Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260613130408.13709-2-med08elkadiri@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/request_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index a7673ad86d18..fa2bb9f2f538 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -332,7 +332,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) case KEY_REQKEY_DEFL_GROUP_KEYRING: default: - BUG(); + return -EINVAL; } /* From 1b9524250996b1f2f49833a1b2ae21c34e486f85 Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Mon, 15 Jun 2026 15:11:50 +0300 Subject: [PATCH 3742/5214] keys: keyctl_pkey: replace BUG with return -EOPNOTSUPP Replace two BUG() calls in keyctl_pkey_params_get_2() and keyctl_pkey_e_d_s() default cases with -EOPNOTSUPP, matching the error style already used in these functions. Signed-off-by: Mohammed EL Kadiri Signed-off-by: Jarkko Sakkinen --- security/keys/keyctl_pkey.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c index ba150ee2d4a3..15b6bf6399b5 100644 --- a/security/keys/keyctl_pkey.c +++ b/security/keys/keyctl_pkey.c @@ -163,7 +163,7 @@ static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_par params->out_len = info.max_sig_size; break; default: - BUG(); + return -EOPNOTSUPP; } params->in_len = uparams.in_len; @@ -245,7 +245,8 @@ long keyctl_pkey_e_d_s(int op, params.op = kernel_pkey_sign; break; default: - BUG(); + ret = -EOPNOTSUPP; + goto error_params; } in = memdup_user(_in, params.in_len); From 8b2c4f88c6ee86efdbc81bed1684e13e2efebd53 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jun 2026 22:02:30 +0200 Subject: [PATCH 3743/5214] pinctrl: Export pinctrl_get_group_selector() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recently added UltraRISC DP1000 is using this symbol, and in a reasonable way as well, so export it. Acked-by: Uwe Kleine-König Reported-by: Nathan Chancellor Closes: Link: https://lore.kernel.org/linux-gpio/20260613164847.GA3152104@ax162/ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606130210.ytVPxHlm-lkp@intel.com/ Fixes: cb7037924836 ("pinctrl: ultrarisc: Add UltraRISC DP1000 pinctrl driver") Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 3fcb7e584a93..1675dd36bd5c 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -772,6 +772,7 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, return -EINVAL; } +EXPORT_SYMBOL_GPL(pinctrl_get_group_selector); bool pinctrl_gpio_can_use_line(struct gpio_chip *gc, unsigned int offset) { From fe47b8b1c0b8c502c090a3de7b1fe6412f0133da Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 13 May 2026 16:01:26 +0200 Subject: [PATCH 3744/5214] s390/timex: Move union tod_clock type to separate header Move union tod_clock type to separate header file. This is preparation for upcoming changes in order to avoid header dependency problems. Signed-off-by: Heiko Carstens Acked-by: Frederic Weisbecker Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/timex.h | 20 +------------------- arch/s390/include/asm/tod_types.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 arch/s390/include/asm/tod_types.h diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 49447b40f038..ac3ab6c29912 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -21,25 +22,6 @@ extern u64 clock_comparator_max; -union tod_clock { - __uint128_t val; - struct { - __uint128_t ei : 8; /* epoch index */ - __uint128_t tod : 64; /* bits 0-63 of tod clock */ - __uint128_t : 40; - __uint128_t pf : 16; /* programmable field */ - }; - struct { - __uint128_t eitod : 72; /* epoch index + bits 0-63 tod clock */ - __uint128_t : 56; - }; - struct { - __uint128_t us : 60; /* micro-seconds */ - __uint128_t sus : 12; /* sub-microseconds */ - __uint128_t : 56; - }; -} __packed; - /* Inline functions for clock register access. */ static inline int set_tod_clock(__u64 time) { diff --git a/arch/s390/include/asm/tod_types.h b/arch/s390/include/asm/tod_types.h new file mode 100644 index 000000000000..976fa0a1e895 --- /dev/null +++ b/arch/s390/include/asm/tod_types.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_S390_TOD_TYPES_H +#define _ASM_S390_TOD_TYPES_H + +#include + +#ifndef __ASSEMBLER__ + +union tod_clock { + __uint128_t val; + struct { + __uint128_t ei : 8; /* epoch index */ + __uint128_t tod : 64; /* bits 0-63 of tod clock */ + __uint128_t : 40; + __uint128_t pf : 16; /* programmable field */ + }; + struct { + __uint128_t eitod : 72; /* epoch index + bits 0-63 tod clock */ + __uint128_t : 56; + }; + struct { + __uint128_t us : 60; /* micro-seconds */ + __uint128_t sus : 12; /* sub-microseconds */ + __uint128_t : 56; + }; +} __packed; + +#endif +#endif From 4fe307f0f5c1fa6afff9175f59930dcd39ee99fd Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 13 May 2026 16:01:27 +0200 Subject: [PATCH 3745/5214] s390/irq/idle: Use stcke instead of stckf for time stamps The upcoming cpu idle time accounting rework involves comparing and subtracting cross cpu time stamps. Time stamps created with the stckf instruction monotonic with respect to the local cpu. For cross cpu monotonic time stamps the slightly slower stcke instruction has to be used [1]. Convert the idle time accounting relevant usages of stckf to stcke. [1] Principles of Operation - Setting and Inspecting the Clock Signed-off-by: Heiko Carstens Acked-by: Frederic Weisbecker Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/idle.h | 3 ++- arch/s390/include/asm/lowcore.h | 4 ++-- arch/s390/include/asm/vtime.h | 4 ++-- arch/s390/kernel/entry.S | 2 +- arch/s390/kernel/idle.c | 4 ++-- arch/s390/kernel/irq.c | 7 ++++--- drivers/s390/cio/qdio_main.c | 2 +- drivers/s390/cio/qdio_thinint.c | 2 +- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h index e4ad09a22400..6963e92b60a1 100644 --- a/arch/s390/include/asm/idle.h +++ b/arch/s390/include/asm/idle.h @@ -11,14 +11,15 @@ #include #include #include +#include struct s390_idle_data { bool idle_dyntick; unsigned long idle_count; unsigned long idle_time; - unsigned long clock_idle_enter; unsigned long timer_idle_enter; unsigned long mt_cycles_enter[8]; + union tod_clock clock_idle_enter; }; DECLARE_PER_CPU(struct s390_idle_data, s390_idle); diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 50ffe75adeb4..b7720484b2f7 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -10,6 +10,7 @@ #define _ASM_S390_LOWCORE_H #include +#include #include #include #include @@ -125,8 +126,7 @@ struct lowcore { __u64 avg_steal_timer; /* 0x0300 */ __u64 last_update_timer; /* 0x0308 */ __u64 last_update_clock; /* 0x0310 */ - __u64 int_clock; /* 0x0318 */ - __u8 pad_0x0320[0x0328-0x0320]; /* 0x0320 */ + union tod_clock int_clock; /* 0x0318 */ __u64 clock_comparator; /* 0x0328 */ __u8 pad_0x0330[0x0340-0x0330]; /* 0x0330 */ diff --git a/arch/s390/include/asm/vtime.h b/arch/s390/include/asm/vtime.h index b1db75d14e9d..da116a93d3b6 100644 --- a/arch/s390/include/asm/vtime.h +++ b/arch/s390/include/asm/vtime.h @@ -48,8 +48,8 @@ static inline void update_timer_idle(void) * The accounted CPU times will be subtracted again from steal_timer * when accumulated steal time is calculated in do_account_vtime(). */ - lc->steal_timer += idle->clock_idle_enter - lc->last_update_clock; - lc->last_update_clock = lc->int_clock; + lc->steal_timer += idle->clock_idle_enter.tod - lc->last_update_clock; + lc->last_update_clock = lc->int_clock.tod; lc->system_timer += lc->last_update_timer - idle->timer_idle_enter; lc->last_update_timer = lc->sys_enter_timer; } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index bb806d1ddae0..7147f3e51ace 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -379,7 +379,7 @@ SYM_CODE_END(pgm_check_handler) SYM_CODE_START(\name) STMG_LC %r8,%r15,__LC_SAVE_AREA GET_LC %r13 - stckf __LC_INT_CLOCK(%r13) + stcke __LC_INT_CLOCK(%r13) stpt __LC_SYS_ENTER_TIMER(%r13) STBEAR __LC_LAST_BREAK(%r13) BPOFF diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 4685d7c5bc51..36020dffb86b 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -26,7 +26,7 @@ void account_idle_time_irq(void) struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); unsigned long idle_time; - idle_time = get_lowcore()->int_clock - idle->clock_idle_enter; + idle_time = get_lowcore()->int_clock.tod - idle->clock_idle_enter.tod; /* Account time spent with enabled wait psw loaded as idle time. */ __atomic64_add(idle_time, &idle->idle_time); @@ -49,7 +49,7 @@ void noinstr arch_cpu_idle(void) set_cpu_flag(CIF_ENABLED_WAIT); if (smp_cpu_mtid) stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter); - idle->clock_idle_enter = get_tod_clock_fast(); + store_tod_clock_ext(&idle->clock_idle_enter); idle->timer_idle_enter = get_cpu_timer(); bpon(); __load_psw_mask(psw_mask); diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index d10a17e6531d..24f44f4a3aac 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -103,9 +103,10 @@ static const struct irq_class irqclass_sub_desc[] = { static void do_IRQ(struct pt_regs *regs, int irq) { - if (tod_after_eq(get_lowcore()->int_clock, - get_lowcore()->clock_comparator)) - /* Serve timer interrupts first. */ + struct lowcore *lc = get_lowcore(); + + /* Serve timer interrupts first */ + if (tod_after_eq(lc->int_clock.tod, lc->clock_comparator)) clock_comparator_work(); generic_handle_irq(irq); } diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 7e594a800525..c1e09fa34e77 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -695,7 +695,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) return; qdio_deliver_irq(irq_ptr); - irq_ptr->last_data_irq_time = get_lowcore()->int_clock; + irq_ptr->last_data_irq_time = get_lowcore()->int_clock.tod; } static void qdio_handle_activate_check(struct qdio_irq *irq_ptr, diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 85ca8650adeb..e167aa75c3df 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -99,7 +99,7 @@ static inline u32 clear_shared_ind(void) static void tiqdio_thinint_handler(struct airq_struct *airq, struct tpi_info *tpi_info) { - u64 irq_time = get_lowcore()->int_clock; + u64 irq_time = get_lowcore()->int_clock.tod; u32 si_used = clear_shared_ind(); struct qdio_irq *irq; From 670e057744e0cc8047bf96d15d18c46e16ae2e93 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 13 May 2026 16:01:28 +0200 Subject: [PATCH 3746/5214] s390/idle: Provide arch specific kcpustat_field_idle()/kcpustat_field_iowait() The former s390 specific arch_cpu_idle_time() implementation was removed, since its implementation was racy and reported idle time could go backwards [1]. However this removal was not necessary, since independently of the s390 architecture specific races there exists the iowait counter update race, which can also lead to reported idle time going backwards [2]. With Frederic Weisbecker's recent cpu idle time accounting refactoring kernel_cpustat got a sequence counter. Use this to implement s390 specific variants of kcpustat_field_idle() and kcpustat_field_iowait(). This is logically a revert of [1] and moves cpu idle time accounting back into s390 architecture code, which is also more precise than the dyntick idle time accounting by nohz/scheduler. For comparing cross cpu time stamps it is necessary to use the stcke instead of the stckf instruction in irq entry path. Furthermore this open-codes a sequence lock in assembler and C code, which is required to update the irq entry time stamp to the per cpu idle_data structure in a race free manner. [1] commit be76ea614460 ("s390/idle: remove arch_cpu_idle_time() and corresponding code") [2] commit ead70b752373 ("timers/nohz: Add a comment about broken iowait counter update race") Signed-off-by: Heiko Carstens Acked-by: Frederic Weisbecker Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/idle.h | 5 +- arch/s390/kernel/asm-offsets.c | 7 +++ arch/s390/kernel/entry.S | 12 +++- arch/s390/kernel/idle.c | 109 ++++++++++++++++++++++++++++++--- arch/s390/kernel/vtime.c | 55 +---------------- include/linux/kernel_stat.h | 27 ++++++++ include/linux/vtime.h | 6 ++ kernel/sched/cputime.c | 4 +- 8 files changed, 158 insertions(+), 67 deletions(-) diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h index 6963e92b60a1..f3502d5621c0 100644 --- a/arch/s390/include/asm/idle.h +++ b/arch/s390/include/asm/idle.h @@ -14,12 +14,15 @@ #include struct s390_idle_data { - bool idle_dyntick; +#ifdef CONFIG_NO_HZ_COMMON + bool in_idle; +#endif unsigned long idle_count; unsigned long idle_time; unsigned long timer_idle_enter; unsigned long mt_cycles_enter[8]; union tod_clock clock_idle_enter; + union tod_clock clock_idle_exit; }; DECLARE_PER_CPU(struct s390_idle_data, s390_idle); diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index fbd26f3e9f96..f6dd2b67dcee 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include int main(void) { @@ -128,6 +130,7 @@ int main(void) OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); OFFSET(__LC_INT_CLOCK, lowcore, int_clock); OFFSET(__LC_CURRENT, lowcore, current_task); + OFFSET(__LC_PERCPU_OFFSET, lowcore, percpu_offset); OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack); OFFSET(__LC_ASYNC_STACK, lowcore, async_stack); OFFSET(__LC_NODAT_STACK, lowcore, nodat_stack); @@ -180,6 +183,10 @@ int main(void) DEFINE(OLDMEM_SIZE, PARMAREA + offsetof(struct parmarea, oldmem_size)); DEFINE(COMMAND_LINE, PARMAREA + offsetof(struct parmarea, command_line)); DEFINE(MAX_COMMAND_LINE_SIZE, PARMAREA + offsetof(struct parmarea, max_command_line_size)); + OFFSET(__IDLE_CLOCK_EXIT, s390_idle_data, clock_idle_exit); +#ifdef CONFIG_NO_HZ_COMMON + OFFSET(__KCPUSTAT_SEQUENCE, kernel_cpustat, idle_sleeptime_seq); +#endif OFFSET(__FTRACE_REGS_PT_REGS, __arch_ftrace_regs, regs); DEFINE(__FTRACE_REGS_SIZE, sizeof(struct __arch_ftrace_regs)); diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 7147f3e51ace..79a45efae23d 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -379,8 +379,19 @@ SYM_CODE_END(pgm_check_handler) SYM_CODE_START(\name) STMG_LC %r8,%r15,__LC_SAVE_AREA GET_LC %r13 +#ifdef CONFIG_NO_HZ_COMMON + larl %r12,kernel_cpustat + ag %r12,__LC_PERCPU_OFFSET(%r13) + asi __KCPUSTAT_SEQUENCE(%r12),1 +#endif stcke __LC_INT_CLOCK(%r13) stpt __LC_SYS_ENTER_TIMER(%r13) + larl %r10,s390_idle + ag %r10,__LC_PERCPU_OFFSET(%r13) + mvc __IDLE_CLOCK_EXIT(16,%r10),__LC_INT_CLOCK(%r13) +#ifdef CONFIG_NO_HZ_COMMON + asi __KCPUSTAT_SEQUENCE(%r12),1 +#endif STBEAR __LC_LAST_BREAK(%r13) BPOFF lmg %r8,%r9,\lc_old_psw(%r13) @@ -407,7 +418,6 @@ SYM_CODE_START(\name) xgr %r5,%r5 xgr %r6,%r6 xgr %r7,%r7 - xgr %r10,%r10 xgr %r12,%r12 xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA(%r13) diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 36020dffb86b..b5fae512fc9c 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -21,22 +22,111 @@ DEFINE_PER_CPU(struct s390_idle_data, s390_idle); -void account_idle_time_irq(void) +static __always_inline void __account_idle_time_irq(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); unsigned long idle_time; - idle_time = get_lowcore()->int_clock.tod - idle->clock_idle_enter.tod; - - /* Account time spent with enabled wait psw loaded as idle time. */ + idle_time = idle->clock_idle_exit.tod - idle->clock_idle_enter.tod; __atomic64_add(idle_time, &idle->idle_time); __atomic64_add_const(1, &idle->idle_count); - - /* Dyntick idle time accounted by nohz/scheduler */ - if (!idle->idle_dyntick) - account_idle_time(cputime_to_nsecs(idle_time)); + account_idle_time(cputime_to_nsecs(idle_time)); } +static __always_inline void __account_idle_time_setup(void) +{ + struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); + + store_tod_clock_ext(&idle->clock_idle_enter); + idle->timer_idle_enter = get_cpu_timer(); + idle->clock_idle_exit = idle->clock_idle_enter; +} + +#ifdef CONFIG_NO_HZ_COMMON + +static u64 arch_cpu_in_idle_time(int cpu) +{ + struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); + union tod_clock now; + u64 idle_time; + + if (!idle->in_idle) + return 0; + store_tod_clock_ext(&now); + if (tod_after(idle->clock_idle_exit.tod, idle->clock_idle_enter.tod)) + idle_time = idle->clock_idle_exit.tod - idle->clock_idle_enter.tod; + else + idle_time = now.tod - idle->clock_idle_enter.tod; + return cputime_to_nsecs(idle_time); +} + +static u64 arch_cpu_idle_time(int cpu, enum cpu_usage_stat idx, bool compute_delta) +{ + struct kernel_cpustat *kc = &kcpustat_cpu(cpu); + u64 *cpustat = kc->cpustat; + unsigned int seq; + u64 idle_time; + + /* + * The open coded seqcount writer in entry.S relies on the + * raw counting mechanism without any writer protection. + */ + typecheck(typeof(kc->idle_sleeptime_seq), seqcount_t); + do { + seq = read_seqcount_begin(&kc->idle_sleeptime_seq); + idle_time = cpustat[idx]; + if (compute_delta) + idle_time += arch_cpu_in_idle_time(cpu); + } while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq)); + return idle_time; +} + +u64 arch_kcpustat_field_idle(int cpu) +{ + return arch_cpu_idle_time(cpu, CPUTIME_IDLE, !nr_iowait_cpu(cpu)); +} + +u64 arch_kcpustat_field_iowait(int cpu) +{ + return arch_cpu_idle_time(cpu, CPUTIME_IOWAIT, nr_iowait_cpu(cpu)); +} + +void account_idle_time_irq(void) +{ + struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); + struct kernel_cpustat *kc = kcpustat_this_cpu; + + write_seqcount_begin(&kc->idle_sleeptime_seq); + idle->in_idle = false; + __account_idle_time_irq(); + write_seqcount_end(&kc->idle_sleeptime_seq); +} + +static __always_inline void account_idle_time_setup(void) +{ + struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); + struct kernel_cpustat *kc = kcpustat_this_cpu; + + raw_write_seqcount_begin(&kc->idle_sleeptime_seq); + idle->in_idle = true; + __account_idle_time_setup(); + raw_write_seqcount_end(&kc->idle_sleeptime_seq); +} + +#else /* CONFIG_NO_HZ_COMMON */ + +void account_idle_time_irq(void) +{ + __account_idle_time_irq(); +} + +static __always_inline void account_idle_time_setup(void) +{ + __account_idle_time_setup(); +} + +#endif /* CONFIG_NO_HZ_COMMON */ + void noinstr arch_cpu_idle(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); @@ -49,8 +139,7 @@ void noinstr arch_cpu_idle(void) set_cpu_flag(CIF_ENABLED_WAIT); if (smp_cpu_mtid) stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter); - store_tod_clock_ext(&idle->clock_idle_enter); - idle->timer_idle_enter = get_cpu_timer(); + account_idle_time_setup(); bpon(); __load_psw_mask(psw_mask); } diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index d1102a6f80bd..d804e1140c2e 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -140,8 +140,6 @@ static int do_account_vtime(struct task_struct *tsk) if (hardirq_count()) lc->hardirq_timer += timer; - else if (in_serving_softirq()) - lc->softirq_timer += timer; else lc->system_timer += timer; @@ -241,63 +239,14 @@ EXPORT_SYMBOL_GPL(vtime_account_kernel); void vtime_account_softirq(struct task_struct *tsk) { - if (!__this_cpu_read(s390_idle.idle_dyntick)) - get_lowcore()->softirq_timer += vtime_delta(); - else - vtime_flush(tsk); + get_lowcore()->softirq_timer += vtime_delta(); } void vtime_account_hardirq(struct task_struct *tsk) { - if (!__this_cpu_read(s390_idle.idle_dyntick)) { - get_lowcore()->hardirq_timer += vtime_delta(); - } else { - /* - * In dynticks mode, the idle cputime is accounted by the nohz - * subsystem. Therefore the s390 timer/clocks are reset on IRQ - * entry and steal time must be accounted now. - */ - vtime_flush(tsk); - } + get_lowcore()->hardirq_timer += vtime_delta(); } -#ifdef CONFIG_NO_HZ_COMMON -/** - * vtime_reset - Fast forward vtime entry clocks - * - * Called from dynticks idle IRQ entry to fast-forward the clocks to current time - * so that the IRQ time is still accounted by vtime while nohz cputime is paused. - */ -void vtime_reset(void) -{ - vtime_reset_last_update(get_lowcore()); -} - -/** - * vtime_dyntick_start - Inform vtime about entry to idle-dynticks - * - * Called when idle enters in dyntick mode. The idle cputime that elapsed so far - * is flushed and the tick subsystem takes over the idle cputime accounting. - */ -void vtime_dyntick_start(void) -{ - __this_cpu_write(s390_idle.idle_dyntick, true); - vtime_flush(current); -} - -/** - * vtime_dyntick_stop - Inform vtime about exit from idle-dynticks - * - * Called when idle exits from dyntick mode. The vtime entry clocks are - * fast-forward to current time and idle accounting resumes. - */ -void vtime_dyntick_stop(void) -{ - vtime_reset_last_update(get_lowcore()); - __this_cpu_write(s390_idle.idle_dyntick, false); -} -#endif /* CONFIG_NO_HZ_COMMON */ - /* * Sorted add to a list. List is linear searched until first bigger * element is found. diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index fce1392e2140..9ca6c2259dfe 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -107,6 +107,30 @@ static inline unsigned long kstat_cpu_irqs_sum(unsigned int cpu) } #ifdef CONFIG_NO_HZ_COMMON + +#ifdef CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE + +static inline void kcpustat_dyntick_start(u64 now) { } +static inline void kcpustat_dyntick_stop(u64 now) { } +static inline void kcpustat_irq_enter(u64 now) { } +static inline void kcpustat_irq_exit(u64 now) { } +static inline bool kcpustat_idle_dyntick(void) { return false; } + +extern u64 arch_kcpustat_field_idle(int cpu); +extern u64 arch_kcpustat_field_iowait(int cpu); + +static inline u64 kcpustat_field_idle(int cpu) +{ + return arch_kcpustat_field_idle(cpu); +} + +static inline u64 kcpustat_field_iowait(int cpu) +{ + return arch_kcpustat_field_iowait(cpu); +} + +#else /* !CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE */ + extern void kcpustat_dyntick_start(u64 now); extern void kcpustat_dyntick_stop(u64 now); extern void kcpustat_irq_enter(u64 now); @@ -118,6 +142,9 @@ static inline bool kcpustat_idle_dyntick(void) { return __this_cpu_read(kernel_cpustat.idle_dyntick); } + +#endif /* !CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE */ + #else static inline u64 kcpustat_field_idle(int cpu) { diff --git a/include/linux/vtime.h b/include/linux/vtime.h index 9dc25b04a119..82825e775499 100644 --- a/include/linux/vtime.h +++ b/include/linux/vtime.h @@ -42,9 +42,15 @@ extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset); extern void vtime_account_softirq(struct task_struct *tsk); extern void vtime_account_hardirq(struct task_struct *tsk); extern void vtime_flush(struct task_struct *tsk); +#ifdef CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE +static inline void vtime_reset(void) { } +static inline void vtime_dyntick_start(void) { } +static inline void vtime_dyntick_stop(void) { } +#else extern void vtime_reset(void); extern void vtime_dyntick_start(void); extern void vtime_dyntick_stop(void); +#endif #else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { } static inline void vtime_account_softirq(struct task_struct *tsk) { } diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 244b57417240..ed49a1e23d17 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -421,7 +421,7 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_ int nr_ticks) { } #endif /* !CONFIG_IRQ_TIME_ACCOUNTING */ -#ifdef CONFIG_NO_HZ_COMMON +#if defined(CONFIG_NO_HZ_COMMON) && !defined(CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE) static void kcpustat_idle_stop(struct kernel_cpustat *kc, u64 now) { u64 *cpustat = kc->cpustat; @@ -560,7 +560,7 @@ static u64 kcpustat_field_dyntick(int cpu, enum cpu_usage_stat idx, { return kcpustat_cpu(cpu).cpustat[idx]; } -#endif /* CONFIG_NO_HZ_COMMON */ +#endif /* CONFIG_NO_HZ_COMMON && !CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE */ static u64 get_cpu_sleep_time_us(int cpu, enum cpu_usage_stat idx, bool compute_delta, u64 *last_update_time) From 968ec3f960d013fc74d3a9bed3ae9ab9a44cb951 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 13 May 2026 16:01:29 +0200 Subject: [PATCH 3747/5214] s390/idle: Remove idle time and count sysfs files Remove the s390 specific idle_time_us and idle_count per cpu sysfs files. They do not provide any additional value. The risk that there are existing applications which rely on these architecture specific files should be very low. However if it turns out such applications exist, this can be easily reverted. Signed-off-by: Heiko Carstens Acked-by: Frederic Weisbecker Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/idle.h | 6 ------ arch/s390/kernel/idle.c | 20 -------------------- arch/s390/kernel/smp.c | 33 +-------------------------------- 3 files changed, 1 insertion(+), 58 deletions(-) diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h index f3502d5621c0..07819f11987c 100644 --- a/arch/s390/include/asm/idle.h +++ b/arch/s390/include/asm/idle.h @@ -10,15 +10,12 @@ #include #include -#include #include struct s390_idle_data { #ifdef CONFIG_NO_HZ_COMMON bool in_idle; #endif - unsigned long idle_count; - unsigned long idle_time; unsigned long timer_idle_enter; unsigned long mt_cycles_enter[8]; union tod_clock clock_idle_enter; @@ -27,7 +24,4 @@ struct s390_idle_data { DECLARE_PER_CPU(struct s390_idle_data, s390_idle); -extern struct device_attribute dev_attr_idle_count; -extern struct device_attribute dev_attr_idle_time_us; - #endif /* _S390_IDLE_H */ diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index b5fae512fc9c..7f7851c001e0 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -28,8 +28,6 @@ static __always_inline void __account_idle_time_irq(void) unsigned long idle_time; idle_time = idle->clock_idle_exit.tod - idle->clock_idle_enter.tod; - __atomic64_add(idle_time, &idle->idle_time); - __atomic64_add_const(1, &idle->idle_count); account_idle_time(cputime_to_nsecs(idle_time)); } @@ -144,24 +142,6 @@ void noinstr arch_cpu_idle(void) __load_psw_mask(psw_mask); } -static ssize_t show_idle_count(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - - return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_count)); -} -DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); - -static ssize_t show_idle_time(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - - return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_time) >> 12); -} -DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); - void arch_cpu_idle_enter(void) { } diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 50bb499cf3e5..0ba7f89b8161 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -1085,31 +1084,6 @@ static struct attribute_group cpu_common_attr_group = { .attrs = cpu_common_attrs, }; -static struct attribute *cpu_online_attrs[] = { - &dev_attr_idle_count.attr, - &dev_attr_idle_time_us.attr, - NULL, -}; - -static struct attribute_group cpu_online_attr_group = { - .attrs = cpu_online_attrs, -}; - -static int smp_cpu_online(unsigned int cpu) -{ - struct cpu *c = per_cpu_ptr(&cpu_devices, cpu); - - return sysfs_create_group(&c->dev.kobj, &cpu_online_attr_group); -} - -static int smp_cpu_pre_down(unsigned int cpu) -{ - struct cpu *c = per_cpu_ptr(&cpu_devices, cpu); - - sysfs_remove_group(&c->dev.kobj, &cpu_online_attr_group); - return 0; -} - bool arch_cpu_is_hotpluggable(int cpu) { return !!cpu; @@ -1175,18 +1149,13 @@ static DEVICE_ATTR_WO(rescan); static int __init s390_smp_init(void) { struct device *dev_root; - int rc; + int rc = 0; dev_root = bus_get_dev_root(&cpu_subsys); if (dev_root) { rc = device_create_file(dev_root, &dev_attr_rescan); put_device(dev_root); - if (rc) - return rc; } - rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "s390/smp:online", - smp_cpu_online, smp_cpu_pre_down); - rc = rc <= 0 ? rc : 0; return rc; } subsys_initcall(s390_smp_init); From 575c6d2bc470ec809d6c803ec0db9e61762cfa32 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:20 -0700 Subject: [PATCH 3748/5214] perf inject/aslr: Implement sample address remapping Add the sample address remapping logic to the ASLR tool. This patch implements aslr_tool__process_sample, which parses sample events, remaps IPs, ADDRs, callchains, and branch stacks using the mappings collected from metadata events, and drops potentially leaking raw, register, stack, physical address, and aux samples. Also adds the aslr_tool__remap_address helper function. Note on cross-endian compatibility: 'perf inject' functions as an endianness converter. Input files are read, and their events are byte-swapped to host endianness in memory. When the tool emits its output, it writes a host-endian PERF_MAGIC in the file header, thereby marking the output file as host-endian natively. Because the output file is always written in host endianness, events and payloads must be constructed entirely using host-endian layouts. For this reason, this patch explicitly un-packs and repacks PERF_SAMPLE_TID (and PERF_SAMPLE_CPU) using unions to ensure that the sequential 32-bit layout is correctly aligned in host endianness. Similarly, branch stack flags (which are modified in-place to host-endian bitfields by the parser) are copied directly to the newly synthesized event. When re-parsing the newly synthesized event, 'needs_swap=false' is explicitly used to prevent double swapping the already host-endian fields. Committer notes: Removed several unused variables, they will be reintroduced in the following patches where they are finally used: struct aslr_evsel_priv *priv = NULL; u64 orig_sample_type; u64 orig_regs_user; u64 orig_regs_intr; Also used PRIx64 for two u64 args (addresses) and %zu for a size_t arg (map__size()) to fix the build on 32-bit architectures. Assisted-by: Antigravity:gemini-3.1-pro Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/aslr.c | 459 +++++++++++++++++++++++++++++++++++++++- tools/perf/util/evsel.c | 6 +- tools/perf/util/evsel.h | 10 +- 3 files changed, 466 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c index 56fc444fbf54..d0760ca8926d 100644 --- a/tools/perf/util/aslr.c +++ b/tools/perf/util/aslr.c @@ -20,6 +20,7 @@ #include #include #include +#include /** * struct remap_addresses_key - Key for mapping original addresses to remapped ones. @@ -112,6 +113,60 @@ static u64 round_up_to_page_size(u64 addr) return (addr + page_size - 1) & ~((u64)page_size - 1); } +static u64 aslr_tool__remap_address(struct aslr_tool *aslr, + struct thread *aslr_thread, + u8 cpumode, + u64 addr) +{ + struct addr_location al; + struct remap_addresses_key key; + u64 *remapped_invariant_ptr = NULL; + u64 remap_addr = 0; + u8 effective_cpumode = cpumode; + + if (!aslr_thread) + return 0; /* No thread. */ + + addr_location__init(&al); + if (!thread__find_map(aslr_thread, cpumode, addr, &al)) { + /* + * If lookup fails with specified cpumode, try fallback to the other space + * to be robust against bad cpumode in samples. + */ + if (cpumode == PERF_RECORD_MISC_KERNEL) + effective_cpumode = PERF_RECORD_MISC_USER; + else if (cpumode == PERF_RECORD_MISC_USER) + effective_cpumode = PERF_RECORD_MISC_KERNEL; + else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL) + effective_cpumode = PERF_RECORD_MISC_GUEST_USER; + else if (cpumode == PERF_RECORD_MISC_GUEST_USER) + effective_cpumode = PERF_RECORD_MISC_GUEST_KERNEL; + + if (!thread__find_map(aslr_thread, effective_cpumode, addr, &al)) { + addr_location__exit(&al); + return 0; /* No mmap. */ + } + } + + key.machine = maps__machine(thread__maps(aslr_thread)); + key.dso = map__dso(al.map); + key.invariant = map__start(al.map) - map__pgoff(al.map); + key.pid = (effective_cpumode == PERF_RECORD_MISC_KERNEL || + effective_cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ? + kernel_pid : thread__pid(aslr_thread); + + if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr)) { + remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) + + (addr - map__start(al.map)); + } else { + pr_debug("Cannot find a remapped entry for address %" PRIx64 " in mapping %" PRIx64 "(%zu) for pid=%d\n", + addr, map__start(al.map), map__size(al.map), key.pid); + } + + addr_location__exit(&al); + return remap_addr; +} + struct aslr_machine_priv { bool kernel_maps_loaded; }; @@ -616,13 +671,409 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool); - struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool); - struct perf_tool *delegate = aslr->tool.delegate; + struct evsel *evsel = sample->evsel; + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + int ret; + u64 sample_type; + struct thread *thread; + struct machine *aslr_machine; + __u64 max_i; + __u64 max_j; + union perf_event *new_event; + struct perf_sample new_sample; + __u64 *in_array, *out_array; + u8 cpumode; + u64 addr; + size_t i; + size_t j; - return delegate->sample(delegate, event, sample, machine); + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + + if (evsel__is_dummy_event(evsel)) + return delegate->sample(delegate, event, sample, machine); + + ret = -EFAULT; + sample_type = evsel->core.attr.sample_type; + max_i = (event->header.size - sizeof(struct perf_event_header)) / sizeof(__u64); + max_j = (PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_event_header)) / sizeof(__u64); + new_event = (union perf_event *)aslr->event_copy; + cpumode = sample->cpumode; + i = 0; + j = 0; + + aslr_machine = machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + return -ENOMEM; + + thread = machine__findnew_thread(aslr_machine, sample->pid, sample->tid); + + if (!thread) + return -ENOMEM; + + if (max_i > PERF_SAMPLE_MAX_SIZE / sizeof(u64)) + goto out_put; + + new_event->sample.header = event->sample.header; + + in_array = &event->sample.array[0]; + out_array = &new_event->sample.array[0]; + +#define CHECK_BOUNDS(required_i, required_j) \ + (i + (required_i) > max_i || j + (required_j) > max_j) + +#define COPY_U64() \ + do { \ + if (CHECK_BOUNDS(1, 1)) { \ + ret = -EFAULT; \ + goto out_put; \ + } \ + out_array[j++] = in_array[i++]; \ + } while (0) + +#define REMAP_U64(addr_field) \ + do { \ + u64 remapped; \ + if (CHECK_BOUNDS(1, 1)) { \ + ret = -EFAULT; \ + goto out_put; \ + } \ + remapped = aslr_tool__remap_address(aslr, thread, cpumode, addr_field); \ + out_array[j++] = remapped; \ + i++; \ + } while (0) + + if (sample_type & PERF_SAMPLE_IDENTIFIER) + COPY_U64(); /* id */ + if (sample_type & PERF_SAMPLE_IP) + REMAP_U64(sample->ip); + if (sample_type & PERF_SAMPLE_TID) { + union { + u64 val64; + u32 val32[2]; + } u; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + u.val32[0] = sample->pid; + u.val32[1] = sample->tid; + out_array[j++] = u.val64; + i++; + } + if (sample_type & PERF_SAMPLE_TIME) + COPY_U64(); /* time */ + if (sample_type & PERF_SAMPLE_ADDR) + REMAP_U64(sample->addr); + if (sample_type & PERF_SAMPLE_ID) + COPY_U64(); /* id */ + if (sample_type & PERF_SAMPLE_STREAM_ID) + COPY_U64(); /* stream_id */ + if (sample_type & PERF_SAMPLE_CPU) + COPY_U64(); /* cpu, res */ + if (sample_type & PERF_SAMPLE_PERIOD) + COPY_U64(); /* period */ + if (sample_type & PERF_SAMPLE_READ) { + if ((evsel->core.attr.read_format & PERF_FORMAT_GROUP) == 0) { + COPY_U64(); /* value */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + COPY_U64(); /* time_enabled */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + COPY_U64(); /* time_running */ + if (evsel->core.attr.read_format & PERF_FORMAT_ID) + COPY_U64(); /* id */ + if (evsel->core.attr.read_format & PERF_FORMAT_LOST) + COPY_U64(); /* lost */ + } else { + u64 nr; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + nr = in_array[i]; + COPY_U64(); + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + COPY_U64(); /* time_enabled */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + COPY_U64(); /* time_running */ + for (u64 cntr = 0; cntr < nr; cntr++) { + COPY_U64(); /* value */ + if (evsel->core.attr.read_format & PERF_FORMAT_ID) + COPY_U64(); /* id */ + if (evsel->core.attr.read_format & PERF_FORMAT_LOST) + COPY_U64(); /* lost */ + } + } + } + if (sample_type & PERF_SAMPLE_CALLCHAIN) { + u64 nr; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + nr = in_array[i]; + COPY_U64(); + + for (u64 cntr = 0; cntr < nr; cntr++) { + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + addr = in_array[i++]; + if (addr >= PERF_CONTEXT_MAX) { + out_array[j++] = addr; + switch (addr) { + case PERF_CONTEXT_HV: + cpumode = PERF_RECORD_MISC_HYPERVISOR; + break; + case PERF_CONTEXT_KERNEL: + cpumode = PERF_RECORD_MISC_KERNEL; + break; + case PERF_CONTEXT_USER: + cpumode = PERF_RECORD_MISC_USER; + break; + case PERF_CONTEXT_GUEST: + cpumode = PERF_RECORD_MISC_GUEST_KERNEL; + break; + case PERF_CONTEXT_GUEST_KERNEL: + cpumode = PERF_RECORD_MISC_GUEST_KERNEL; + break; + case PERF_CONTEXT_GUEST_USER: + cpumode = PERF_RECORD_MISC_GUEST_USER; + break; + case PERF_CONTEXT_USER_DEFERRED: + if (cntr + 1 >= nr) { + pr_debug("Truncated callchain deferred cookie context\n"); + ret = 0; + goto out_put; + } + /* + * Immediately followed by a 64-bit + * stitching cookie. Skip/Copy it! + */ + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j++] = in_array[i++]; + cntr++; + cpumode = PERF_RECORD_MISC_USER; + break; + default: + pr_debug("invalid callchain context: %"PRIx64"\n", addr); + ret = 0; + goto out_put; + } + continue; + } + addr = aslr_tool__remap_address(aslr, thread, cpumode, addr); + out_array[j++] = addr; + } + } + if (sample_type & PERF_SAMPLE_RAW) { + size_t bytes = sizeof(u32) + sample->raw_size; + size_t u64_words = (bytes + 7) / 8; + + if (i + u64_words > max_i || j + u64_words > max_j) { + ret = -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], bytes); + i += u64_words; + j += u64_words; + /* + * TODO: certain raw samples can be remapped, such as + * tracepoints by examining their fields. + */ + pr_debug("Dropping raw samples as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + u64 nr; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + nr = in_array[i]; + COPY_U64(); + + if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) + COPY_U64(); /* hw_idx */ + + if (nr > (ULLONG_MAX / 3)) { + ret = -EFAULT; + goto out_put; + } + if (nr * 3 > max_i - i || nr * 3 > max_j - j) { + ret = -EFAULT; + goto out_put; + } + for (u64 cntr = 0; cntr < nr; cntr++) { + u64 from = in_array[i++]; + u64 to = in_array[i++]; + + from = aslr_tool__remap_address(aslr, thread, sample->cpumode, from); + to = aslr_tool__remap_address(aslr, thread, sample->cpumode, to); + + out_array[j++] = from; + out_array[j++] = to; + out_array[j++] = in_array[i++]; /* flags */ + } + if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) { + if (nr > max_i - i || nr > max_j - j) { + ret = -EFAULT; + goto out_put; + } + for (u64 cntr = 0; cntr < nr; cntr++) + COPY_U64(); + } + } + if (sample_type & PERF_SAMPLE_REGS_USER) { + if (CHECK_BOUNDS(1, 0)) { + ret = -EFAULT; + goto out_put; + } + /* abi */ + COPY_U64(); + /* TODO: can this be less conservative? */ + pr_debug("Dropping regs user sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_STACK_USER) { + u64 size; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + size = in_array[i]; + COPY_U64(); + if (size > 0) { + size_t u64_words = size / 8 + (size % 8 ? 1 : 0); + + if (u64_words > max_i - i || u64_words > max_j - j) { + ret = -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], size); + if (size % 8) { + size_t pad = 8 - (size % 8); + + memset(((char *)&out_array[j]) + size, 0, pad); + } + i += u64_words; + j += u64_words; + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping stack user sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) + COPY_U64(); /* perf_sample_weight */ + if (sample_type & PERF_SAMPLE_DATA_SRC) + COPY_U64(); /* data_src */ + if (sample_type & PERF_SAMPLE_TRANSACTION) + COPY_U64(); /* transaction */ + if (sample_type & PERF_SAMPLE_REGS_INTR) { + if (CHECK_BOUNDS(1, 0)) { + ret = -EFAULT; + goto out_put; + } + /* abi */ + COPY_U64(); + /* TODO: can this be less conservative? */ + pr_debug("Dropping interrupt register sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_PHYS_ADDR) { + COPY_U64(); /* phys_addr */ + /* TODO: can this be less conservative? */ + pr_debug("Dropping physical address sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_CGROUP) + COPY_U64(); /* cgroup */ + if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE) + COPY_U64(); /* data_page_size */ + if (sample_type & PERF_SAMPLE_CODE_PAGE_SIZE) + COPY_U64(); /* code_page_size */ + + if (sample_type & PERF_SAMPLE_AUX) { + u64 size; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + size = out_array[j++]; + i++; + if (size > 0) { + size_t u64_words = size / 8 + (size % 8 ? 1 : 0); + + if (u64_words > max_i - i || u64_words > max_j - j) { + ret = -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], size); + if (size % 8) { + size_t pad = 8 - (size % 8); + + memset(((char *)&out_array[j]) + size, 0, pad); + } + i += u64_words; + j += u64_words; + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping aux sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + + if (evsel__is_offcpu_event(evsel)) { + /* TODO: can this be less conservative? */ + pr_debug("Dropping off-CPU sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + + new_event->sample.header.size = sizeof(struct perf_event_header) + j * sizeof(u64); + + perf_sample__init(&new_sample, /*all=*/ true); + ret = __evsel__parse_sample(evsel, new_event, &new_sample, /*needs_swap=*/false); + + if (ret) { + perf_sample__exit(&new_sample); + goto out_put; + } + + new_sample.evsel = evsel; + ret = delegate->sample(delegate, new_event, &new_sample, machine); + perf_sample__exit(&new_sample); + +out_put: + thread__put(thread); + return ret; } +#undef CHECK_BOUNDS +#undef COPY_U64 +#undef REMAP_U64 + static int skipn(int fd, off_t n) { char buf[4096]; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 34c03f47a913..05fa0010c858 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3337,11 +3337,11 @@ static int __set_offcpu_sample(struct perf_sample *data) return -EFAULT; } -int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - struct perf_sample *data) +int __evsel__parse_sample(struct evsel *evsel, union perf_event *event, + struct perf_sample *data, bool needs_swap) { u64 type = evsel->core.attr.sample_type; - bool swapped = evsel->needs_swap; + bool swapped = needs_swap; const __u64 *array; u16 max_size = event->header.size; const void *endp = (void *)event + max_size; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8178858d168a..8009be22cc3f 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -432,8 +432,14 @@ static inline int evsel__read_on_cpu_scaled(struct evsel *evsel, int cpu_map_idx return __evsel__read_on_cpu(evsel, cpu_map_idx, thread, true); } -int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - struct perf_sample *sample); +int __evsel__parse_sample(struct evsel *evsel, union perf_event *event, + struct perf_sample *data, bool needs_swap); + +static inline int evsel__parse_sample(struct evsel *evsel, union perf_event *event, + struct perf_sample *data) +{ + return __evsel__parse_sample(evsel, event, data, evsel->needs_swap); +} int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, u64 *timestamp); From d6dbf2d4a9009a9cec1b33325308fa5b3a7b6ba9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:21 -0700 Subject: [PATCH 3749/5214] perf aslr: Strip sample registers Extend the ASLR tool stripping helpers to drop register dump payloads by masking out the relevant perf_event_attr fields (sample_regs_user, sample_regs_intr) when the delegated tool is handling the data. struct aslr_evsel_priv maintains the original perf_event_attr values and is looked up via the evsel_orig_attrs hashmap so that sample sizes can be properly parsed even when bits are stripped from the pipeline. This is critical for bounded array copying within aslr_tool__process_sample, which relies on orig_sample_type to determine exactly which fields were captured by the kernel before any stripping occurred. This allows us to keep samples that would otherwise be dropped because they contain registers, while still obfuscating the registers. Committer notes: Moved now used variables from the previous patch: struct aslr_evsel_priv *priv = NULL; u64 orig_sample_type; u64 orig_regs_user; u64 orig_regs_intr; Assisted-by: Antigravity:gemini-3.1-pro Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 28 +++- tools/perf/util/aslr.c | 266 +++++++++++++++++++++++++++--------- tools/perf/util/aslr.h | 9 +- 3 files changed, 233 insertions(+), 70 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 8bb37095e2de..6d6cce4765a7 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -248,7 +248,7 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, if (!aslr_event) return -ENOMEM; memcpy(aslr_event, event, event->header.size); - aslr_tool__strip_attr_event(aslr_event, pevlist); + aslr_tool__strip_attr_event(aslr_event, *pevlist); event = aslr_event; } @@ -297,6 +297,7 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, attr.size = sizeof(struct perf_event_attr); attr.sample_type &= ~PERF_SAMPLE_AUX; + if (inject->itrace_synth_opts.add_last_branch) { attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; @@ -2617,6 +2618,10 @@ static int __cmd_inject(struct perf_inject *inject) evsel->core.attr.exclude_callchain_user = 0; } } + + if (inject->aslr) + aslr_tool__strip_evlist(inject->session->tool, session->evlist); + session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, @@ -2875,6 +2880,18 @@ int cmd_inject(int argc, const char **argv) if (zstd_init(&(inject.session->zstd_data), 0) < 0) pr_warning("Decompression initialization failed.\n"); + if (inject.aslr) { + struct evsel *evsel; + + evlist__for_each_entry(inject.session->evlist, evsel) { + ret = aslr_tool__cache_orig_attrs(tool, evsel); + if (ret) { + pr_err("Failed to cache original attributes: %d\n", ret); + goto out_delete; + } + } + } + /* Save original section info before feature bits change */ ret = save_section_info(&inject); if (ret) @@ -2893,10 +2910,17 @@ int cmd_inject(int argc, const char **argv) * the input. */ if (!data.is_pipe) { + if (inject.aslr) + aslr_tool__strip_evlist(tool, inject.session->evlist); + ret = perf_event__synthesize_for_pipe(&inject.tool, inject.session, &inject.output, perf_event__repipe); + + if (inject.aslr) + aslr_tool__restore_evlist(tool, inject.session->evlist); + if (ret < 0) goto out_delete; } @@ -2961,8 +2985,6 @@ int cmd_inject(int argc, const char **argv) goto out_delete; ret = __cmd_inject(&inject); - if (inject.aslr) - aslr_tool__strip_evlist(tool, inject.session->evlist); guest_session__exit(&inject.guest_session); diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c index d0760ca8926d..a946fff2ac4d 100644 --- a/tools/perf/util/aslr.c +++ b/tools/perf/util/aslr.c @@ -18,6 +18,7 @@ #include /* page_size */ #include #include +#include #include #include #include @@ -46,6 +47,23 @@ struct aslr_mapping { u64 remap_start; }; +struct aslr_evsel_priv { + u64 orig_sample_type; + u64 orig_sample_regs_user; + u64 orig_sample_regs_intr; + int orig_sample_size; +}; + +static size_t evsel_hash(long key, void *ctx __maybe_unused) +{ + return (size_t)key; +} + +static bool evsel_equal(long key1, long key2, void *ctx __maybe_unused) +{ + return key1 == key2; +} + struct process_top_address { u64 remapped_max; }; @@ -60,6 +78,11 @@ struct aslr_tool { struct hashmap remap_addresses; /** @top_addresses: mapping from process to max remapped address. */ struct hashmap top_addresses; + /** + * @evsel_orig_attrs: mapping from evsel pointer to its original + * unstripped sample_type and registers bitmasks. + */ + struct hashmap evsel_orig_attrs; }; static const pid_t kernel_pid = -1; @@ -123,6 +146,8 @@ static u64 aslr_tool__remap_address(struct aslr_tool *aslr, u64 *remapped_invariant_ptr = NULL; u64 remap_addr = 0; u8 effective_cpumode = cpumode; + struct dso *dso; + const char *dso_name; if (!aslr_thread) return 0; /* No thread. */ @@ -148,9 +173,15 @@ static u64 aslr_tool__remap_address(struct aslr_tool *aslr, } } + dso = map__dso(al.map); + dso_name = dso ? dso__long_name(dso) : NULL; + key.machine = maps__machine(thread__maps(aslr_thread)); - key.dso = map__dso(al.map); - key.invariant = map__start(al.map) - map__pgoff(al.map); + key.dso = dso; + if (dso && !is_anon_memory(dso_name) && !is_no_dso_memory(dso_name)) + key.invariant = map__start(al.map) - map__pgoff(al.map); + else + key.invariant = map__start(al.map); key.pid = (effective_cpumode == PERF_RECORD_MISC_KERNEL || effective_cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ? kernel_pid : thread__pid(aslr_thread); @@ -676,6 +707,7 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, struct aslr_tool *aslr; struct perf_tool *delegate; int ret; + int orig_sample_size; u64 sample_type; struct thread *thread; struct machine *aslr_machine; @@ -688,6 +720,10 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, u64 addr; size_t i; size_t j; + struct aslr_evsel_priv *priv = NULL; + u64 orig_sample_type; + u64 orig_regs_user; + u64 orig_regs_intr; del_tool = container_of(tool, struct delegate_tool, tool); aslr = container_of(del_tool, struct aslr_tool, tool); @@ -697,7 +733,24 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, return delegate->sample(delegate, event, sample, machine); ret = -EFAULT; - sample_type = evsel->core.attr.sample_type; + + if (hashmap__find(&aslr->evsel_orig_attrs, evsel, &priv)) { + orig_sample_type = priv->orig_sample_type; + orig_regs_user = priv->orig_sample_regs_user; + orig_regs_intr = priv->orig_sample_regs_intr; + } else { + orig_sample_type = evsel->core.attr.sample_type; + orig_regs_user = evsel->core.attr.sample_regs_user; + orig_regs_intr = evsel->core.attr.sample_regs_intr; + } + + orig_sample_size = evsel->sample_size; + + sample_type = orig_sample_type; + sample_type &= ~PERF_SAMPLE_REGS_USER; + sample_type &= ~PERF_SAMPLE_REGS_INTR; + sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; + max_i = (event->header.size - sizeof(struct perf_event_header)) / sizeof(__u64); max_j = (PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_event_header)) / sizeof(__u64); new_event = (union perf_event *)aslr->event_copy; @@ -748,11 +801,11 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, i++; \ } while (0) - if (sample_type & PERF_SAMPLE_IDENTIFIER) + if (orig_sample_type & PERF_SAMPLE_IDENTIFIER) COPY_U64(); /* id */ - if (sample_type & PERF_SAMPLE_IP) + if (orig_sample_type & PERF_SAMPLE_IP) REMAP_U64(sample->ip); - if (sample_type & PERF_SAMPLE_TID) { + if (orig_sample_type & PERF_SAMPLE_TID) { union { u64 val64; u32 val32[2]; @@ -767,19 +820,19 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, out_array[j++] = u.val64; i++; } - if (sample_type & PERF_SAMPLE_TIME) + if (orig_sample_type & PERF_SAMPLE_TIME) COPY_U64(); /* time */ - if (sample_type & PERF_SAMPLE_ADDR) + if (orig_sample_type & PERF_SAMPLE_ADDR) REMAP_U64(sample->addr); - if (sample_type & PERF_SAMPLE_ID) + if (orig_sample_type & PERF_SAMPLE_ID) COPY_U64(); /* id */ - if (sample_type & PERF_SAMPLE_STREAM_ID) + if (orig_sample_type & PERF_SAMPLE_STREAM_ID) COPY_U64(); /* stream_id */ - if (sample_type & PERF_SAMPLE_CPU) + if (orig_sample_type & PERF_SAMPLE_CPU) COPY_U64(); /* cpu, res */ - if (sample_type & PERF_SAMPLE_PERIOD) + if (orig_sample_type & PERF_SAMPLE_PERIOD) COPY_U64(); /* period */ - if (sample_type & PERF_SAMPLE_READ) { + if (orig_sample_type & PERF_SAMPLE_READ) { if ((evsel->core.attr.read_format & PERF_FORMAT_GROUP) == 0) { COPY_U64(); /* value */ if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) @@ -812,7 +865,7 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, } } } - if (sample_type & PERF_SAMPLE_CALLCHAIN) { + if (orig_sample_type & PERF_SAMPLE_CALLCHAIN) { u64 nr; if (CHECK_BOUNDS(1, 1)) { @@ -878,7 +931,7 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, out_array[j++] = addr; } } - if (sample_type & PERF_SAMPLE_RAW) { + if (orig_sample_type & PERF_SAMPLE_RAW) { size_t bytes = sizeof(u32) + sample->raw_size; size_t u64_words = (bytes + 7) / 8; @@ -897,7 +950,7 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, ret = 0; goto out_put; } - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + if (orig_sample_type & PERF_SAMPLE_BRANCH_STACK) { u64 nr; if (CHECK_BOUNDS(1, 1)) { @@ -938,19 +991,25 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, COPY_U64(); } } - if (sample_type & PERF_SAMPLE_REGS_USER) { + if (orig_sample_type & PERF_SAMPLE_REGS_USER) { + u64 abi; + if (CHECK_BOUNDS(1, 0)) { ret = -EFAULT; goto out_put; } - /* abi */ - COPY_U64(); - /* TODO: can this be less conservative? */ - pr_debug("Dropping regs user sample as possible ASLR leak\n"); - ret = 0; - goto out_put; + abi = in_array[i++]; + if (abi != PERF_SAMPLE_REGS_ABI_NONE) { + u64 nr = hweight64(orig_regs_user); + + if (nr > max_i - i) { + ret = -EFAULT; + goto out_put; + } + i += nr; + } } - if (sample_type & PERF_SAMPLE_STACK_USER) { + if (orig_sample_type & PERF_SAMPLE_STACK_USER) { u64 size; if (CHECK_BOUNDS(1, 1)) { @@ -980,39 +1039,45 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, ret = 0; goto out_put; } - if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) + if (orig_sample_type & PERF_SAMPLE_WEIGHT_TYPE) COPY_U64(); /* perf_sample_weight */ - if (sample_type & PERF_SAMPLE_DATA_SRC) + if (orig_sample_type & PERF_SAMPLE_DATA_SRC) COPY_U64(); /* data_src */ - if (sample_type & PERF_SAMPLE_TRANSACTION) + if (orig_sample_type & PERF_SAMPLE_TRANSACTION) COPY_U64(); /* transaction */ - if (sample_type & PERF_SAMPLE_REGS_INTR) { + if (orig_sample_type & PERF_SAMPLE_REGS_INTR) { + u64 abi; + if (CHECK_BOUNDS(1, 0)) { ret = -EFAULT; goto out_put; } - /* abi */ - COPY_U64(); - /* TODO: can this be less conservative? */ - pr_debug("Dropping interrupt register sample as possible ASLR leak\n"); - ret = 0; - goto out_put; + abi = in_array[i++]; + if (abi != PERF_SAMPLE_REGS_ABI_NONE) { + u64 nr = hweight64(orig_regs_intr); + + if (nr > max_i - i) { + ret = -EFAULT; + goto out_put; + } + i += nr; + } } - if (sample_type & PERF_SAMPLE_PHYS_ADDR) { + if (orig_sample_type & PERF_SAMPLE_PHYS_ADDR) { COPY_U64(); /* phys_addr */ /* TODO: can this be less conservative? */ pr_debug("Dropping physical address sample as possible ASLR leak\n"); ret = 0; goto out_put; } - if (sample_type & PERF_SAMPLE_CGROUP) + if (orig_sample_type & PERF_SAMPLE_CGROUP) COPY_U64(); /* cgroup */ - if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE) + if (orig_sample_type & PERF_SAMPLE_DATA_PAGE_SIZE) COPY_U64(); /* data_page_size */ - if (sample_type & PERF_SAMPLE_CODE_PAGE_SIZE) + if (orig_sample_type & PERF_SAMPLE_CODE_PAGE_SIZE) COPY_U64(); /* code_page_size */ - if (sample_type & PERF_SAMPLE_AUX) { + if (orig_sample_type & PERF_SAMPLE_AUX) { u64 size; if (CHECK_BOUNDS(1, 1)) { @@ -1052,11 +1117,20 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, } new_event->sample.header.size = sizeof(struct perf_event_header) + j * sizeof(u64); - + /* Temporarily override evsel attributes to match the stripped new_event format! */ + evsel->sample_size = __evsel__sample_size(sample_type); + evsel->core.attr.sample_type = sample_type; + evsel->core.attr.sample_regs_user = 0; + evsel->core.attr.sample_regs_intr = 0; perf_sample__init(&new_sample, /*all=*/ true); ret = __evsel__parse_sample(evsel, new_event, &new_sample, /*needs_swap=*/false); if (ret) { + /* Restore original attributes immediately if parsing fails */ + evsel->sample_size = orig_sample_size; + evsel->core.attr.sample_type = orig_sample_type; + evsel->core.attr.sample_regs_user = orig_regs_user; + evsel->core.attr.sample_regs_intr = orig_regs_intr; perf_sample__exit(&new_sample); goto out_put; } @@ -1065,6 +1139,12 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, ret = delegate->sample(delegate, new_event, &new_sample, machine); perf_sample__exit(&new_sample); + /* Restore original attributes so trace ingestion never desynchronizes! */ + evsel->sample_size = orig_sample_size; + evsel->core.attr.sample_type = orig_sample_type; + evsel->core.attr.sample_regs_user = orig_regs_user; + evsel->core.attr.sample_regs_intr = orig_regs_intr; + out_put: thread__put(thread); return ret; @@ -1118,15 +1198,22 @@ static int aslr_tool__process_auxtrace_error(const struct perf_tool *tool __mayb return 0; } - -void aslr_tool__strip_attr_event(union perf_event *event, struct evlist **pevlist __maybe_unused) +void aslr_tool__strip_attr_event(union perf_event *event, struct evlist *evlist) { u32 attr_size; + if (!evlist) + return; + attr_size = event->attr.attr.size ?: PERF_ATTR_SIZE_VER0; if (attr_size >= (offsetof(struct perf_event_attr, sample_type) + sizeof(u64))) { event->attr.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; + + if (attr_size >= (offsetof(struct perf_event_attr, sample_regs_user) + sizeof(u64))) + event->attr.attr.sample_regs_user = 0; + if (attr_size >= (offsetof(struct perf_event_attr, sample_regs_intr) + sizeof(u64))) + event->attr.attr.sample_regs_intr = 0; } if (attr_size >= (offsetof(struct perf_event_attr, type) + sizeof(u32))) { @@ -1150,28 +1237,6 @@ void aslr_tool__strip_attr_event(union perf_event *event, struct evlist **pevlis } } -void aslr_tool__strip_evlist(struct perf_tool *tool __maybe_unused, - struct evlist *evlist) -{ - struct evsel *evsel; - - evlist__for_each_entry(evlist, evsel) { - evsel->core.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; - - if (evsel->core.attr.type == PERF_TYPE_BREAKPOINT) - evsel->core.attr.bp_addr = 0; - else if (evsel->core.attr.type >= PERF_TYPE_MAX) { - struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->core.attr.type); - - if (pmu && (!strcmp(pmu->name, "kprobe") || - !strcmp(pmu->name, "uprobe"))) { - evsel->core.attr.config1 = 0; - evsel->core.attr.config2 = 0; - } - } - } -} - static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) { delegate_tool__init(&aslr->tool, delegate); @@ -1185,6 +1250,9 @@ static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) hashmap__init(&aslr->top_addresses, top_addresses__hash, top_addresses__equal, /*ctx=*/NULL); + hashmap__init(&aslr->evsel_orig_attrs, + evsel_hash, evsel_equal, + /*ctx=*/NULL); aslr->tool.tool.sample = aslr_tool__process_sample; /* read - reads a counter, okay to delegate. */ @@ -1247,9 +1315,13 @@ void aslr_tool__delete(struct perf_tool *tool) zfree(&cur->pkey); zfree(&cur->pvalue); } + hashmap__for_each_entry(&aslr->evsel_orig_attrs, cur, bkt) { + zfree(&cur->pvalue); + } hashmap__clear(&aslr->remap_addresses); hashmap__clear(&aslr->top_addresses); + hashmap__clear(&aslr->evsel_orig_attrs); aslr_tool__destroy_machines_priv(&aslr->machines); machines__destroy_kernel_maps(&aslr->machines); @@ -1263,3 +1335,69 @@ void aslr_tool__delete(struct perf_tool *tool) machines__exit(&aslr->machines); free(aslr); } + +int aslr_tool__cache_orig_attrs(struct perf_tool *tool, struct evsel *evsel) +{ + struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool); + struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool); + struct aslr_evsel_priv *priv = zalloc(sizeof(*priv)); + int err; + + if (!priv) + return -ENOMEM; + + priv->orig_sample_type = evsel->core.attr.sample_type; + priv->orig_sample_regs_user = evsel->core.attr.sample_regs_user; + priv->orig_sample_regs_intr = evsel->core.attr.sample_regs_intr; + priv->orig_sample_size = evsel->sample_size; + + err = hashmap__add(&aslr->evsel_orig_attrs, evsel, priv); + if (err) { + free(priv); + return err; + } + return 0; +} + +void aslr_tool__strip_evlist(const struct perf_tool *tool __maybe_unused, struct evlist *evlist) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + evsel->core.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE; + evsel->core.attr.sample_regs_user = 0; + evsel->core.attr.sample_regs_intr = 0; + evsel->sample_size = __evsel__sample_size(evsel->core.attr.sample_type); + evsel__calc_id_pos(evsel); + + if (evsel->core.attr.type == PERF_TYPE_BREAKPOINT) { + evsel->core.attr.bp_addr = 0; + } else if (evsel->core.attr.type >= PERF_TYPE_MAX) { + struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->core.attr.type); + + if (pmu && (!strcmp(pmu->name, "kprobe") || + !strcmp(pmu->name, "uprobe"))) { + evsel->core.attr.config1 = 0; + evsel->core.attr.config2 = 0; + } + } + } +} + +void aslr_tool__restore_evlist(const struct perf_tool *tool, struct evlist *evlist) +{ + const struct delegate_tool *del_tool = container_of(tool, const struct delegate_tool, tool); + const struct aslr_tool *aslr = container_of(del_tool, const struct aslr_tool, tool); + struct evsel *evsel; + struct aslr_evsel_priv *priv; + + evlist__for_each_entry(evlist, evsel) { + if (hashmap__find(&aslr->evsel_orig_attrs, evsel, &priv)) { + evsel->core.attr.sample_type = priv->orig_sample_type; + evsel->core.attr.sample_regs_user = priv->orig_sample_regs_user; + evsel->core.attr.sample_regs_intr = priv->orig_sample_regs_intr; + evsel->sample_size = priv->orig_sample_size; + evsel__calc_id_pos(evsel); + } + } +} diff --git a/tools/perf/util/aslr.h b/tools/perf/util/aslr.h index 2b82f711bc67..522e31c8e2c0 100644 --- a/tools/perf/util/aslr.h +++ b/tools/perf/util/aslr.h @@ -34,8 +34,11 @@ struct evlist; union perf_event; struct perf_tool *aslr_tool__new(struct perf_tool *delegate); -void aslr_tool__delete(struct perf_tool *aslr); -void aslr_tool__strip_attr_event(union perf_event *event, struct evlist **pevlist); -void aslr_tool__strip_evlist(struct perf_tool *tool, struct evlist *evlist); +void aslr_tool__delete(struct perf_tool *tool); + +void aslr_tool__strip_attr_event(union perf_event *event, struct evlist *evlist); +int aslr_tool__cache_orig_attrs(struct perf_tool *tool, struct evsel *evsel); +void aslr_tool__strip_evlist(const struct perf_tool *tool, struct evlist *evlist); +void aslr_tool__restore_evlist(const struct perf_tool *tool, struct evlist *evlist); #endif /* __PERF_ASLR_H */ From 190c4546384461f3dee70a649b438aa41c24d01e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 11 Jun 2026 09:41:22 -0700 Subject: [PATCH 3750/5214] perf test: Add inject ASLR test Add a new shell test to verify the feature. The test covers: - Basic address remapping for user space samples. - Pipe mode coverage for piped into. - Callchain address remapping. - Consistency of output before and after injection. - Pipe mode report consistency. - Dropping of samples that leak ASLR info (physical addresses). - Kernel address remapping (utilizing a dedicated kernel-intensive VFS dd workload to guarantee continuous timer interrupts sampling flow inside kernel privilege states). - Kernel report consistency with address normalization. The test suite is hardened with global 'set -o pipefail' assertions to catch pipeline failures, stream-consuming awk processors to handle SIGPIPE signals, and a dedicated pipe output scenario validating raw 'perf inject -o -' stdout streams. Note on kernel DSO normalization in the test script: The test script deliberately normalizes all kernel DSOs to a generic [kernel] tag before diffing, as obfuscating physical kernel addresses forces perf report to occasionally shift samples between individual modules and [kernel.kallsyms] due to the lack of valid host module boundary maps. Note on ARM: Kernel-based ASLR test cases (test_kernel_aslr and test_kernel_report_aslr) are skipped on ARM architectures (aarch64 and arm*) to bypass high latency constraints (such as check_invariants() spending excessive execution time in maps__split_kallsyms() on debug builds) and symbolization inconsistencies. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers Tested-by: James Clark Cc: Adrian Hunter Cc: Gabriel Marin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/inject_aslr.sh | 533 ++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100755 tools/perf/tests/shell/inject_aslr.sh diff --git a/tools/perf/tests/shell/inject_aslr.sh b/tools/perf/tests/shell/inject_aslr.sh new file mode 100755 index 000000000000..c00461828ea7 --- /dev/null +++ b/tools/perf/tests/shell/inject_aslr.sh @@ -0,0 +1,533 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# perf inject --aslr test + +set -e +set -o pipefail + +shelldir=$(dirname "$0") +# shellcheck source=lib/perf_has_symbol.sh +. "${shelldir}"/lib/perf_has_symbol.sh + +sym="noploop" + +skip_test_missing_symbol ${sym} + +# Create global temp directory +temp_dir=$(mktemp -d /tmp/perf-test-aslr.XXXXXXXXXX) + +prog="perf test -w noploop" +[ "$(uname -m)" = "s390x" ] && prog="$prog 3" +err=0 +kprog="dd if=/dev/urandom of=/dev/null bs=1M count=50" + +cleanup() { + local exit_code=${1:-$?} + trap - EXIT TERM INT + if [ "${exit_code}" -ne 0 ] || [ "${err}" -ne 0 ]; then + echo "Test failed! Preserving temp directory: ${temp_dir}" + return + fi + # Check if temp_dir is set and looks sane before removing + if [[ "${temp_dir}" =~ ^/tmp/perf-test-aslr\. ]]; then + rm -rf "${temp_dir}" + fi +} + +trap_cleanup() { + local exit_code=$? + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup ${exit_code} + exit ${exit_code} +} +trap trap_cleanup EXIT TERM INT + +get_noploop_addr() { + local file=$1 + perf script -i "$file" | awk ' + BEGIN { found=0 } + { + for (i=1; i<=NF; i++) { + if ($i ~ /noploop\+/) { + if (!found) { + print $(i-1) + found=1 + } + } + } + }' +} + +test_basic_aslr() { + echo "Test basic ASLR remapping" + local data + data=$(mktemp "${temp_dir}/perf.data.basic.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.basic.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + perf inject -v --aslr -i "${data}" -o "${data2}" + + orig_addr=$(get_noploop_addr "${data}") + new_addr=$(get_noploop_addr "${data2}") + + echo "Basic ASLR: orig_addr=$orig_addr, new_addr=$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Basic ASLR test [Failed - no noploop samples in original file]" + err=1 + elif [ -z "$new_addr" ]; then + echo "Basic ASLR test [Failed - could not find remapped address]" + err=1 + elif [ "$orig_addr" = "$new_addr" ]; then + echo "Basic ASLR test [Failed - addresses are not remapped]" + err=1 + else + echo "Basic ASLR test [Success]" + fi +} + +test_pipe_aslr() { + echo "Test pipe mode ASLR remapping" + local data + data=$(mktemp "${temp_dir}/perf.data.pipe.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.pipe.XXXXXX") + + # Use tee to save the original pipe data for comparison + perf record -e task-clock:u -o - ${prog} | tee "${data}" | perf inject --aslr -o "${data2}" + + orig_addr=$(get_noploop_addr "${data}") + new_addr=$(get_noploop_addr "${data2}") + + echo "Pipe ASLR: orig_addr=$orig_addr, new_addr=$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Pipe ASLR test [Failed - no noploop samples in original file]" + err=1 + elif [ -z "$new_addr" ]; then + echo "Pipe ASLR test [Failed - could not find remapped address]" + err=1 + elif [ "$orig_addr" = "$new_addr" ]; then + echo "Pipe ASLR test [Failed - addresses are not remapped]" + err=1 + else + echo "Pipe ASLR test [Success]" + fi +} + +test_callchain_aslr() { + echo "Test Callchain ASLR remapping" + local data + data=$(mktemp "${temp_dir}/perf.data.callchain.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.callchain.XXXXXX") + + perf record -g -e task-clock:u -o "${data}" ${prog} + perf inject --aslr -i "${data}" -o "${data2}" + + orig_addr=$(get_noploop_addr "${data}") + new_addr=$(get_noploop_addr "${data2}") + + echo "Callchain ASLR: orig_addr=$orig_addr, new_addr=$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Callchain ASLR test [Failed - no noploop samples in original file]" + err=1 + elif [ -z "$new_addr" ]; then + echo "Callchain ASLR test [Failed - could not find remapped address]" + err=1 + elif [ "$orig_addr" = "$new_addr" ]; then + echo "Callchain ASLR test [Failed - addresses are not remapped]" + err=1 + else + # Extract callchain addresses (indented lines starting with hex addresses) + orig_callchain=$(perf script -i "${data}" | awk '/^[[:space:]]+[0-9a-f]+/ {print $1}') + new_callchain=$(perf script -i "${data2}" | awk '/^[[:space:]]+[0-9a-f]+/ {print $1}') + + if [ -z "$orig_callchain" ]; then + echo "Callchain ASLR test [Failed - no callchain samples in original file]" + err=1 + elif [ -z "$new_callchain" ]; then + echo "Callchain ASLR test [Failed - callchain data was dropped]" + err=1 + elif [ "$orig_callchain" = "$new_callchain" ]; then + echo "Callchain ASLR test [Failed - callchain addresses were not remapped]" + err=1 + else + echo "Callchain ASLR test [Success]" + fi + fi +} + +test_report_aslr() { + echo "Test perf report consistency" + local data + data=$(mktemp "${temp_dir}/perf.data.report.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.report.XXXXXX") + local data_clean + data_clean=$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + # Use -b to inject build-ids and force ordered events processing in both + perf inject -b -i "${data}" -o "${data_clean}" + perf inject -v -b --aslr -i "${data}" -o "${data2}" + + local report1="${temp_dir}/report1_basic" + local report2="${temp_dir}/report2_basic" + local report1_clean="${temp_dir}/report1_basic.clean" + local report2_clean="${temp_dir}/report2_basic.clean" + local diff_file="${temp_dir}/diff_basic" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${data2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Report ASLR test [Failed - no samples captured]" + err=1 + elif [ -s "${diff_file}" ]; then + echo "Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=1 + else + echo "Report ASLR test [Success]" + fi +} + +test_pipe_report_aslr() { + echo "Test pipe mode perf report consistency" + local data + data=$(mktemp "${temp_dir}/perf.data.pipe_report.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.pipe_report.XXXXXX") + local data_clean + data_clean=$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + # Use tee to save the original pipe data, then process it with inject -b + perf record -e task-clock:u -o - ${prog} | \ + tee "${data}" | \ + perf inject -b --aslr -o "${data2}" + perf inject -b -i "${data}" -o "${data_clean}" + + local report1="${temp_dir}/report1_pipe" + local report2="${temp_dir}/report2_pipe" + local report1_clean="${temp_dir}/report1_pipe.clean" + local report2_clean="${temp_dir}/report2_pipe.clean" + local diff_file="${temp_dir}/diff_pipe" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${data2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Pipe Report ASLR test [Failed - no samples captured]" + err=1 + elif [ -s "${diff_file}" ]; then + echo "Pipe Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=1 + else + echo "Pipe Report ASLR test [Success]" + fi +} + +test_pipe_out_report_aslr() { + echo "Test pipe output mode perf report consistency" + local data + data=$(mktemp "${temp_dir}/perf.data.pipe_out_report.XXXXXX") + local data_clean + data_clean=$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + perf inject -b -i "${data}" -o "${data_clean}" + + local report1="${temp_dir}/report1_pipe_out" + local report2="${temp_dir}/report2_pipe_out" + local report1_clean="${temp_dir}/report1_pipe_out.clean" + local report2_clean="${temp_dir}/report2_pipe_out.clean" + local diff_file="${temp_dir}/diff_pipe_out" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf inject -b --aslr -i "${data}" -o - | perf report -i - --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Pipe Output Report ASLR test [Failed - no samples captured]" + err=1 + elif [ -s "${diff_file}" ]; then + echo "Pipe Output Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=1 + else + echo "Pipe Output Report ASLR test [Success]" + fi +} + +test_dropped_samples() { + echo "Test dropped samples (phys-data)" + local data + data=$(mktemp "${temp_dir}/perf.data.dropped.XXXXXX") + local data2 + data2=$(mktemp "${temp_dir}/perf.data2.dropped.XXXXXX") + + # Check if --phys-data is supported by recording a short run + if ! perf record -e task-clock:u --phys-data -o "${data}" -- sleep 0.1 > /dev/null 2>&1; then + echo "Skipping dropped samples test as --phys-data is not supported" + return + fi + + perf record -e task-clock:u --phys-data -o "${data}" ${prog} + perf inject --aslr -i "${data}" -o "${data2}" + + # Verify that the original file actually contained samples! + orig_samples=$(perf script -i "${data}" | wc -l) + if [ "$orig_samples" -eq 0 ]; then + echo "Dropped samples test [Failed - no samples in original file]" + err=1 + else + # Verify that samples are dropped. + samples_count=$(perf script -i "${data2}" | wc -l) + + if [ "$samples_count" -gt 0 ]; then + echo "Dropped samples test [Failed - samples were not dropped]" + err=1 + else + echo "Dropped samples test [Success]" + fi + fi +} + +test_kernel_aslr() { + echo "Test kernel ASLR remapping" + local kdata + kdata=$(mktemp "${temp_dir}/perf.data.kernel.XXXXXX") + local kdata2 + kdata2=$(mktemp "${temp_dir}/perf.data2.kernel.XXXXXX") + local log_file + log_file=$(mktemp "${temp_dir}/kernel_record.log.XXXXXX") + + # Try to record kernel samples + if ! perf record -e task-clock:k -o "${kdata}" ${kprog} > "${log_file}" 2>&1; then + echo "Skipping kernel ASLR test as recording failed (maybe no permissions)" + return + fi + + # Check for warning about kernel map restriction + if grep -q "Couldn't record kernel reference relocation symbol" "${log_file}"; then + echo "Skipping kernel ASLR test as kernel map could not be recorded (permissions restricted)" + return + fi + + perf inject -v --aslr -i "${kdata}" -o "${kdata2}" + + # Check if kernel addresses are remapped. + # Find the field that ends with :k: (the event name) and take the next field! + orig_addr=$(perf script -i "${kdata}" | awk ' + BEGIN { found=0 } + { + for (i=1; i "${log_file}" 2>&1; then + echo "Skipping kernel report test as recording failed (maybe no permissions)" + return + fi + + # Check for warning about kernel map restriction + if grep -q "Couldn't record kernel reference relocation symbol" "${log_file}"; then + echo "Skipping kernel report test as kernel map could not be recorded (permissions restricted)" + return + fi + + # Use -b to inject build-ids and force ordered events processing in both + perf inject -b -i "${kdata}" -o "${data_clean}" + perf inject -v -b --aslr -i "${kdata}" -o "${kdata2}" + + local report1="${temp_dir}/report_kernel1" + local report2="${temp_dir}/report_kernel2" + local report1_clean="${temp_dir}/report_kernel1.clean" + local report2_clean="${temp_dir}/report_kernel2.clean" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${kdata2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' > "${report2_clean}" || true + + # Normalize kernel DSOs and addresses in clean reports + # This allows kernel modules to be either a module or kernel.kallsyms + local report1_norm="${temp_dir}/report_kernel1.norm" + local report2_norm="${temp_dir}/report_kernel2.norm" + local diff_file="${temp_dir}/diff_kernel" + + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' "${report1_clean}" | \ + awk '{gsub(/\[[a-zA-Z0-9_.-]{2,}\](\.[a-zA-Z0-9_]+)?/, "[kernel]", $0); print}' | \ + sort > "${report1_norm}" || true + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' "${report2_clean}" | \ + awk '{gsub(/\[[a-zA-Z0-9_.-]{2,}\](\.[a-zA-Z0-9_]+)?/, "[kernel]", $0); print}' | \ + sort > "${report2_norm}" || true + + diff -u -w "${report1_norm}" "${report2_norm}" > "${diff_file}" || true + + if [ ! -s "${report1_norm}" ]; then + echo "Kernel Report ASLR test [Failed - no samples captured]" + err=1 + elif [ -s "${diff_file}" ]; then + echo "Kernel Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=1 + else + echo "Kernel Report ASLR test [Success]" + fi +} + +test_regs_stripping() { + echo "Test user register stripping" + local rdata="${temp_dir}/perf.data.regs" + local rdata2="${temp_dir}/perf.data.regs.injected" + local rdata_clean="${temp_dir}/perf.data.regs.clean" + + if ! perf record -e cycles:u --user-regs -o "${rdata}" ${prog} > /dev/null 2>&1; then + echo "Skipping user registers test as recording failed (unsupported flag/platform)" + return + fi + + perf inject -b -i "${rdata}" -o "${rdata_clean}" + perf inject -v -b --aslr -i "${rdata}" -o "${rdata2}" + + local report1="${temp_dir}/report_regs1" + local report2="${temp_dir}/report_regs2" + local report1_clean="${temp_dir}/report_regs1.clean" + local report2_clean="${temp_dir}/report_regs2.clean" + local diff_file="${temp_dir}/diff_regs" + + perf report -i "${rdata_clean}" --stdio > "${report1}" 2>/dev/null || true + perf report -i "${rdata2}" --stdio > "${report2}" 2>/dev/null || true + + grep '%' "${report1}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | \ + sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | \ + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' | \ + sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "User registers stripping test [Failed - profile trace starved/empty]" + err=1 + return + elif [ -s "${diff_file}" ]; then + echo "User registers stripping test [Failed - report parsing differs]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=1 + return + fi + + local script_dump="${temp_dir}/script_regs_dump" + perf script -D -i "${rdata2}" > "${script_dump}" 2>/dev/null || true + if grep -q "user regs:" "${script_dump}"; then + echo "User registers stripping test [Failed - register dumps still present]" + err=1 + else + echo "User registers stripping test [Success]" + fi +} + +test_basic_aslr +test_pipe_aslr +test_callchain_aslr +test_report_aslr +test_pipe_report_aslr +test_pipe_out_report_aslr +test_dropped_samples +case "$(uname -m)" in + aarch64*|arm*) + echo "Skipping kernel ASLR tests on ARM" + ;; + *) + test_kernel_aslr + test_kernel_report_aslr + ;; +esac + +test_regs_stripping + +cleanup ${err} +exit $err From cd355f6dc70503191249c5147e2f2e8f3c8838bd Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 12 Jun 2026 18:56:10 -0400 Subject: [PATCH 3751/5214] perf trace: Fix noise and signed formatting of __probe_ip in bare dynamic probes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a dynamic probe is created without explicitly requested arguments via perf probe --add, the Ftrace subsystem automatically appends "__probe_ip" to the tracepoint format to record the instruction pointer. Currently, perf trace prints this implicit field by default. Furthermore, because the formatting logic defaults to a standard signed integer representation, the kernel space memory address is erroneously displayed as a meaningless negative integer. ❯ sudo ./perf trace --event probe:proc_sys_open --max-events 1 0.000 ps/1316543 probe:proc_sys_open(__probe_ip: -1406056956) This patch addresses the user experience by combining two refinements: 1. "__probe_ip" is now hidden from the standard output, as its presence adds no contextual value for a bare probe. 2. If the user explicitly requests verbose output (--verbose), "__probe_ip" is intercepted and properly formatted as a hexadecimal kernel address, restoring its utility for debugging inline function hits. ❯ sudo ./perf trace --event probe:proc_sys_open --max-events 1 0.000 ps/1314074 probe:proc_sys_open() ❯ sudo ./perf trace --verbose --event probe:proc_sys_open --max-events 1 Using CPUID GenuineIntel-6-8E-C mmap size 528384B 0.000 ps/1314366 probe:proc_sys_open(__probe_ip: 0xffffffffac314604) Reviewed-by: James Clark Signed-off-by: Aaron Tomlin Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Daniel Vacek Cc: Howard Chu Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sean Ashe Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 377f0a18b00e..a8492da23a9c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3261,6 +3261,27 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample * if (val == 0 && !trace->show_zeros && !arg->show_zero && arg->strtoul != STUL_BTF_TYPE) continue; + /* + * __probe_ip is implicitly added to bare dynamic probes. + * Suppress it by default to avoid cluttering the output. + * If verbose mode is enabled, ensure it is formatted as a + * hexadecimal memory address rather than a signed integer. + */ + if (!strcmp(field->name, "__probe_ip")) { + if (!verbose) + continue; + + printed += scnprintf(bf + printed, size - printed, + "%s", printed ? ", " : ""); + if (trace->show_arg_names) + printed += scnprintf(bf + printed, size - printed, + "%s: ", field->name); + + printed += scnprintf(bf + printed, size - printed, "%#016llx", + (unsigned long long)val); + continue; + } + printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); if (trace->show_arg_names) From 84800a8d1d22c475b3c35bdbcf8ed64a55f3b66f Mon Sep 17 00:00:00 2001 From: Tom Haynes Date: Sat, 18 Apr 2026 12:03:01 -0700 Subject: [PATCH 3752/5214] nfs: don't skip revalidate on directory delegation when attrs flagged stale On a local directory mutation (rename/create/unlink) the client marks CHANGE / MTIME / CTIME as invalid in NFS_I(dir)->cache_validity. When a subsequent stat(2) enters __nfs_revalidate_inode() and finds a directory delegation held, the function currently early-exits and returns the cached (now stale) mtime to userspace without sending a GETATTR RPC. Keep the early-exit for the fast path, but take the RPC when CHANGE, MTIME, or CTIME are already marked invalid. The delegation alone is not a guarantee of cached-attr freshness once the code itself has flagged the cache as stale. Assisted-by: Claude:claude-opus-4-7 [bpftrace] [tshark] Signed-off-by: Tom Haynes [Anna: Use NFS_INO_INVALID_ATTR insteado of individual NFS_INO_INVALID_* flags] Signed-off-by: Anna Schumaker --- fs/nfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 0d9451a2ad8e..c6c939a76722 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1357,7 +1357,8 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) status = pnfs_sync_inode(inode, false); if (status) goto out; - } else if (nfs_have_directory_delegation(inode)) { + } else if (nfs_have_directory_delegation(inode) && + !(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)) { status = 0; goto out; } From 41fe0f7b84f0cb822ae10ab08592996a592b2a25 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 12:30:35 -0400 Subject: [PATCH 3753/5214] NFSv4/pNFS: reject zero-length r_addr in nfs4_decode_mp_ds_addr nfs4_decode_mp_ds_addr() decodes the r_netid and r_addr opaques of a netaddr4 from a GETDEVICEINFO multipath-DS body, then immediately calls strrchr(buf, '.') to locate the port separator. Both decodes use xdr_stream_decode_string_dup(), and the current code checks only "nlen < 0" / "rlen < 0" before dereferencing the returned string. When the on-wire opaque has length zero, xdr_stream_decode_opaque_inline() returns 0 and xdr_stream_decode_string_dup() falls through to its "*str = NULL; return ret" tail, leaving buf NULL with a return value of 0. The "< 0" check does not catch this, and the next line is strrchr(NULL, '.'), a kernel NULL pointer dereference reachable from any pNFS-flexfile client mounted against a malicious or compromised metadata server. Reject the zero-length cases explicitly so the decoder fails with -EBADMSG (treated as a malformed GETDEVICEINFO body) instead of panicking the client. Cc: stable@vger.kernel.org Fixes: 6b7f3cf96364 ("nfs41: pull decode_ds_addr from file layout to generic pnfs") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker --- fs/nfs/pnfs_nfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 12632a706da8..0ff43dbcb7cd 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1075,14 +1075,14 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags) /* r_netid */ nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ, gfp_flags); - if (unlikely(nlen < 0)) + if (unlikely(nlen <= 0)) goto out_err; /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ /* port is ".ABC.DEF", 8 chars max */ rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8, gfp_flags); - if (unlikely(rlen < 0)) + if (unlikely(rlen <= 0)) goto out_free_netid; /* replace port '.' with '-' */ From 528ad521a433cf873724893bda339df95d8ac1e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Jun 2026 14:03:45 +0200 Subject: [PATCH 3754/5214] pmdomain: core: fix unused variable warning with !PM_GENERIC_DOMAINS_OF The genpd provider bus is really only used when CONFIG_PM_GENERIC_DOMAINS_OF is enabled, and since the recent deferred initialisation of domain parent devices, the root device pointer is otherwise unused. Fix the unused variable warning by moving the definition of the root device pointer inside the corresponding ifdef. Fixes: 92b69eff8012 ("pmdomain: core: fix early domain registration") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606111746.kAxaAbwg-lkp@intel.com/ Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/pmdomain/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 7796042e3f37..842c4169e290 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -32,9 +32,6 @@ static const struct bus_type genpd_provider_bus_type = { .name = "genpd_provider", }; -/* The parent for genpd_provider devices. */ -static struct device *genpd_provider_bus; - #define GENPD_RETRY_MAX_MS 250 /* Approximate */ #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ @@ -2566,6 +2563,10 @@ struct of_genpd_provider { static LIST_HEAD(of_genpd_providers); /* Mutex to protect the list above. */ static DEFINE_MUTEX(of_genpd_mutex); + +/* The parent for genpd_provider devices. */ +static struct device *genpd_provider_bus; + /* Used to prevent registering devices before the bus. */ static bool genpd_bus_registered; From 1221e50b4aa60b98aade37eb4e536d4a2cb93e75 Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Fri, 12 Jun 2026 14:28:47 -0500 Subject: [PATCH 3755/5214] perf tools: Document recent additions to the perf.data file header Add documentation for recently added HEADER_E_MACHINE and HEADER_CLN_SIZE data to the perf.data file. Also fix a typo at the end of the header section. Reviewed-by: Ian Rogers Signed-off-by: Thomas Falcon Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Dapeng Mi Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/Documentation/perf.data-file-format.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index 0e4d0ecc9e12..b90cba9168f8 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -464,7 +464,21 @@ struct cpu_domain_info { struct domain_info domains[]; }; - other bits are reserved and should ignored for now + HEADER_E_MACHINE = 33, + +ELF machine and flags data. e_machine is expanded from 16 to 32 bits +for alignment. Format: + + u32 e_machine; + u32 e_flags; + + HEADER_CLN_SIZE = 34, + +The size of the cacheline in bytes. Format: + + unsigned int cln_size; + + other bits are reserved and should be ignored for now HEADER_FEAT_BITS = 256, Attributes From 81ccda30b4e83d8f5cc4fd50503c44e3a33abfeb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Jun 2026 22:18:12 +0200 Subject: [PATCH 3756/5214] KVM: x86: Fix shadow paging use-after-free due to unexpected role Commit 0cb2af2ea66ad ("KVM: x86: Fix shadow paging use-after-free due to unexpected GFN") fixed a shadow paging mismatch between stored and computed GFNs; the bug could be triggered by changing a PDE mapping from outside the guest, and then deleting a memslot. The rmap_remove() call would miss entries created after the PDE change because the GFN of the leaf SPTE does not match the GFN of the struct kvm_mmu_page. A similar hole however remains if the modified PDE points to a non-leaf page. In this case the gfn can be made to match, but the role does not match: the original large 2MB page creates a kvm_mmu_page with direct=1, while the new 4KB needs a kvm_mmu_page with direct=0. However, kvm_mmu_get_child_sp() does not compare the role, and therefore reuses the page. The next step is installing a leaf (4KB) SPTE on the new path which records an rmap entry under the gfn resolved by the walk. But when that child is zapped its parent kvm_mmu_page has direct=1 and kvm_mmu_page_get_gfn() computes the gfn for the 4KB page as sp->gfn + index instead of using sp->shadowed_translation[] (or sp->gfns[] in older kernels). It therefore fails to remove the recorded entry. When the memslot is dropped the shadow page is freed but the rmap entry survives, as in the scenario that was already fixed. Code that later walks that gfn (dirty logging, MMU notifier invalidation, and so on) dereferences an sptep that lies in the freed page, causing the use-after-free. Fixes: 2032a93d66fa ("KVM: MMU: Don't allocate gfns page for direct mmu pages") Reported-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 9368a71336fe..c13b80fe3125 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2459,13 +2459,15 @@ static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn, bool direct, unsigned int access) { - union kvm_mmu_page_role role; + union kvm_mmu_page_role role = kvm_mmu_child_role(sptep, direct, access); - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) && - spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn) + if (is_shadow_present_pte(*sptep) && + !is_large_pte(*sptep) && + spte_to_child_sp(*sptep) && + spte_to_child_sp(*sptep)->gfn == gfn && + spte_to_child_sp(*sptep)->role.word == role.word) return ERR_PTR(-EEXIST); - role = kvm_mmu_child_role(sptep, direct, access); return kvm_mmu_get_shadow_page(vcpu, gfn, role); } From ef057cbf825e03b63f6edf5980f96abf3c53089d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 29 Apr 2026 09:34:01 -0700 Subject: [PATCH 3757/5214] KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level When recovering hugepages in the shadow MMU, verify that the base gfn of the shadow page is actually contained within the target memslot, *before* querying the max mapping level given the shadow page's gfn. Failure to pre-check the validity of the gfn can lead to an out-of-bounds access to the slot's lpage_info (which typically manifests as a host #PF because the lpage_info is vmalloc'd) if the guest creates a hugepage mapping (in its PTEs) that extends "below" the bounds of a memslot. When faulting in memory for a guest, and the size of the guest mapping is greater than KVM's (current) max mapping, then KVM will create a "direct" shadow page (direct in that there are no gPTEs to shadow, and so the target gfn is a direct calculation given the base gfn of the shadow page). The hugepage recovery flow looks for such direct shadow pages, as forcing 4KiB mappings when dirty logging generates the guest > host mapping size case. When the 4KiB restriction is lifted, then KVM can replace the shadow page with a hugepage. But if KVM originally used a smaller mapping than the guest because the range of memory covered by the guest hugepage exceeds the bounds of a memslot, then KVM will link a direct shadow page with a gfn that is outside the bounds of the memslot being used to fault in memory. The rmap entry added for the leaf mapping is correct and within bounds, but the gfn of the leaf SPTE's parent shadow page will be out of bounds. BUG: unable to handle page fault for address: ffffc90000806ffc #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 100000067 P4D 100000067 PUD 1002a7067 PMD 10612f067 PTE 0 Oops: Oops: 0000 [#1] SMP CPU: 13 UID: 1000 PID: 757 Comm: mmu_stress_test Not tainted 7.1.0-rc1-48ce1e26eace-x86_pir_to_irr_comments-vm #341 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_mmu_max_mapping_level+0x79/0x2b0 [kvm] Call Trace: kvm_mmu_recover_huge_pages+0x21b/0x320 [kvm] kvm_set_memslot+0x1ee/0x590 [kvm] kvm_set_memory_region.part.0+0x3a1/0x4d0 [kvm] kvm_vm_ioctl+0x9bf/0x15d0 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb7/0xbb0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f21c0f1a9bf Don't bother pre-checking the bounds of the potential hugepage, i.e. don't check that e.g. sp->gfn + KVM_PAGES_PER_HPAGE(sp->role.level + 1) is also within the memslot, as the checks performed by kvm_mmu_max_mapping_level() are a superset of the basic bounds checks. I.e. pre-checking the full range would be a dubious micro-optimization. Fixes: 9eba50f8d7fc ("KVM: x86/mmu: Consult max mapping level when zapping collapsible SPTEs") Cc: stable@vger.kernel.org Cc: David Matlack Cc: James Houghton Cc: Alexander Bulekov Cc: Fred Griffoul Cc: Alexander Graf Cc: David Woodhouse Cc: Filippo Sironi Cc: Ivan Orlov Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 18 ++++++++++++------ include/linux/kvm_host.h | 7 ++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c13b80fe3125..26ed97efda91 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7360,13 +7360,19 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm, sp = sptep_to_sp(sptep); /* - * We cannot do huge page mapping for indirect shadow pages, - * which are found on the last rmap (level = 1) when not using - * tdp; such shadow pages are synced with the page table in - * the guest, and the guest page table is using 4K page size - * mapping if the indirect sp has level = 1. + * Direct shadow page can be replaced by a hugepage if the host + * mapping level allows it and the memslot maps all of the host + * hugepage. Note! If the memslot maps only part of the + * hugepage, sp->gfn may be below slot->base_gfn, and querying + * the max mapping level would cause an out-of-bounds lpage_info + * access. So the gfn bounds check *must* be done first. + * + * Indirect shadow pages are created when the guest page tables + * are using 4K pages. Since the host mapping is always + * constrained by the page size in the guest, indirect shadow + * pages are never collapsible. */ - if (sp->role.direct && + if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) && sp->role.level < kvm_mmu_max_mapping_level(kvm, NULL, slot, sp->gfn)) { kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 27498e990dff..ab8cfaec82d3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1815,6 +1815,11 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm, struct kvm_irq_ack_notifier *kian); bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args); +static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn) +{ + return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages; +} + /* * Returns a pointer to the memslot if it contains gfn. * Otherwise returns NULL. @@ -1825,7 +1830,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn) if (!slot) return NULL; - if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages) + if (is_gfn_in_memslot(slot, gfn)) return slot; else return NULL; From 79c41666b397df9218b6e7555663b298a88e2b56 Mon Sep 17 00:00:00 2001 From: Lakshay Piplani Date: Fri, 12 Jun 2026 16:48:08 +0530 Subject: [PATCH 3758/5214] i3c: master: rename i3c_master_reattach_i3c_dev() to *_locked Rename i3c_master_reattach_i3c_dev() to *_locked() to make the locking requirement explicit and consistent with other I3C core helpers that require the bus lock to be held by the caller. Signed-off-by: Lakshay Piplani Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612111816.3688240-2-lakshay.piplani@nxp.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 109aa50eb1f8..cf56865d875a 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1765,8 +1765,8 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master, return 0; } -static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev, - u8 old_dyn_addr) +static int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev, + u8 old_dyn_addr) { struct i3c_master_controller *master = i3c_dev_get_master(dev); int ret; @@ -1855,7 +1855,7 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, goto err_detach_dev; i3cdev->info.dyn_addr = i3cdev->boardinfo->init_dyn_addr; - ret = i3c_master_reattach_i3c_dev(i3cdev, 0); + ret = i3c_master_reattach_i3c_dev_locked(i3cdev, 0); if (ret) goto err_rstdaa; @@ -2425,7 +2425,7 @@ static void __i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master if (!ret) { old_dyn_addr = newdev->info.dyn_addr; newdev->info.dyn_addr = expected_dyn_addr; - i3c_master_reattach_i3c_dev(newdev, old_dyn_addr); + i3c_master_reattach_i3c_dev_locked(newdev, old_dyn_addr); } else { dev_err(&master->dev, "Failed to assign reserved/old address to device %d%llx", From 8d8afa428318a623aa674c3f90550475ad3e6ccd Mon Sep 17 00:00:00 2001 From: Aman Kumar Pandey Date: Fri, 12 Jun 2026 16:48:09 +0530 Subject: [PATCH 3759/5214] i3c: master: Expose the APIs to support I3C hub Change the below internal static functions to APIs to allow new I3C hub driver to use them 1) i3c_dev_enable_ibi_locked() 2) i3c_dev_disable_ibi_locked() 3) i3c_dev_request_ibi_locked() 4) i3c_dev_free_ibi_locked() 5) i3c_master_reattach_i3c_dev_locked() Signed-off-by: Aman Kumar Pandey Signed-off-by: Lakshay Piplani Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612111816.3688240-3-lakshay.piplani@nxp.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 70 ++++++++++++++++++++++++++++++++++++-- include/linux/i3c/master.h | 2 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index cf56865d875a..372d911ecbad 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1765,8 +1765,23 @@ static int i3c_master_attach_i3c_dev(struct i3c_master_controller *master, return 0; } -static int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev, - u8 old_dyn_addr) +/** + * i3c_master_reattach_i3c_dev_locked() - reattach an I3C device with a new address + * @dev: I3C device descriptor to reattach + * @old_dyn_addr: previous dynamic address of the device + * + * This function reattaches an existing I3C device to the bus when its dynamic + * address has changed. It updates the bus address slot status accordingly: + * - Marks the new dynamic address as occupied by an I3C device. + * - Frees the old dynamic address slot if applicable. + * + * This function must be called with the bus lock held in write mode. + * + * Return: 0 on success, or a negative error code if reattachment fails + * (e.g. -EBUSY if the new address slot is not free). + */ +int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev, + u8 old_dyn_addr) { struct i3c_master_controller *master = i3c_dev_get_master(dev); int ret; @@ -1790,6 +1805,7 @@ static int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev, return 0; } +EXPORT_SYMBOL_GPL(i3c_master_reattach_i3c_dev_locked); static void i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev) { @@ -3387,6 +3403,16 @@ int i3c_dev_do_xfers_locked(struct i3c_dev_desc *dev, struct i3c_xfer *xfers, return master->ops->i3c_xfers(dev, xfers, nxfers, mode); } +/** + * i3c_dev_disable_ibi_locked() - Disable IBIs coming from a specific device + * @dev: device on which IBIs should be disabled + * + * This function disable IBIs coming from a specific device and wait for + * all pending IBIs to be processed. + * + * Context: Must be called with mutex_lock(&dev->desc->ibi_lock) held. + * Return: 0 in case of success, a negative error core otherwise. + */ int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev) { struct i3c_master_controller *master; @@ -3408,7 +3434,22 @@ int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev) return 0; } +EXPORT_SYMBOL_GPL(i3c_dev_disable_ibi_locked); +/** + * i3c_dev_enable_ibi_locked() - Enable IBIs from a specific device (lock held) + * @dev: device on which IBIs should be enabled + * + * This function enable IBIs coming from a specific device and wait for + * all pending IBIs to be processed. This should be called on a device + * where i3c_device_request_ibi() has succeeded. + * + * Note that IBIs from this device might be received before this function + * returns to its caller. + * + * Context: Must be called with mutex_lock(&dev->desc->ibi_lock) held. + * Return: 0 on success, or a negative error code on failure. + */ int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev) { struct i3c_master_controller *master = i3c_dev_get_master(dev); @@ -3423,7 +3464,20 @@ int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev) return ret; } +EXPORT_SYMBOL_GPL(i3c_dev_enable_ibi_locked); +/** + * i3c_dev_request_ibi_locked() - Request an IBI + * @dev: device for which we should enable IBIs + * @req: setup requested for this IBI + * + * This function is responsible for pre-allocating all resources needed to + * process IBIs coming from @dev. When this function returns, the IBI is not + * enabled until i3c_device_enable_ibi() is called. + * + * Context: Must be called with mutex_lock(&dev->desc->ibi_lock) held. + * Return: 0 in case of success, a negative error core otherwise. + */ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev, const struct i3c_ibi_setup *req) { @@ -3462,7 +3516,18 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev, return ret; } +EXPORT_SYMBOL_GPL(i3c_dev_request_ibi_locked); +/** + * i3c_dev_free_ibi_locked() - Free all resources needed for IBI handling + * @dev: device on which you want to release IBI resources + * + * This function is responsible for de-allocating resources previously + * allocated by i3c_device_request_ibi(). It should be called after disabling + * IBIs with i3c_device_disable_ibi(). + * + * Context: Must be called with mutex_lock(&dev->desc->ibi_lock) held. + */ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev) { struct i3c_master_controller *master = i3c_dev_get_master(dev); @@ -3493,6 +3558,7 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev) kfree(dev->ibi); dev->ibi = NULL; } +EXPORT_SYMBOL_GPL(i3c_dev_free_ibi_locked); static int __init i3c_init(void) { diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index f73cede87d36..27eeb598b3c5 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -625,6 +625,8 @@ void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer); DEFINE_FREE(i3c_master_dma_unmap_single, void *, if (_T) i3c_master_dma_unmap_single(_T)) +int i3c_master_reattach_i3c_dev_locked(struct i3c_dev_desc *dev, + u8 old_dyn_addr); int i3c_master_set_info(struct i3c_master_controller *master, const struct i3c_device_info *info); From 081b387c7397498c583b1ba7c2fdaf4c6da6b538 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:28:43 -0300 Subject: [PATCH 3760/5214] perf symbols: Fix bswap copy-paste error for 32-bit ELF p_filesz filename__read_build_id() byte-swaps 32-bit ELF program headers on cross-endian files, but line 178 passes p_offset to bswap_32() instead of p_filesz: hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); This clobbers p_filesz with the already-swapped p_offset value. The 64-bit path on line 182 is correct and swaps p_filesz from p_filesz. The consequence is that the PT_NOTE segment read uses the wrong size, which can cause either a short read (missing the build-id) or an oversized read (reading past the segment into adjacent data). Fix by swapping the correct field. Reported-by: sashiko-bot Fixes: fef8f648bb47726d ("perf symbol: Fix use-after-free in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-minimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 091071d06416..f4b0a711a62c 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -175,7 +175,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid) if (elf32) { hdrs.phdr32[i].p_type = bswap_32(hdrs.phdr32[i].p_type); hdrs.phdr32[i].p_offset = bswap_32(hdrs.phdr32[i].p_offset); - hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); + hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_filesz); } else { hdrs.phdr64[i].p_type = bswap_32(hdrs.phdr64[i].p_type); hdrs.phdr64[i].p_offset = bswap_64(hdrs.phdr64[i].p_offset); From 2a3716544359d4312c81b0fa909a13301186da17 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:29:34 -0300 Subject: [PATCH 3761/5214] perf symbols: Validate p_filesz before use in filename__read_build_id() filename__read_build_id() stores ELF p_filesz in a ssize_t variable. A crafted 32-bit ELF with p_filesz = 0xFFFFFFFF produces ssize_t value -1. The comparison `p_filesz > buf_size` evaluates false because signed -1 is less than any non-negative buf_size, so the realloc is skipped and buf remains NULL. The subsequent read(fd, NULL, -1) returns -1, which equals p_filesz, passing the error check. read_build_id() then dereferences the NULL buffer. Add an explicit check for p_filesz <= 0 before using the value, catching both zero-length and sign-wrapped negative sizes from crafted ELF files. Reported-by: sashiko-bot Fixes: ba0b7081f7a521d7 ("perf symbol-minimal: Fix ehdr reading in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-minimal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index f4b0a711a62c..0a71d1463952 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -186,6 +186,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid) continue; p_filesz = elf32 ? hdrs.phdr32[i].p_filesz : hdrs.phdr64[i].p_filesz; + /* ssize_t can go negative with crafted ELF p_filesz values */ + if (p_filesz <= 0) + continue; if (p_filesz > buf_size) { void *tmp; From 063c647b24f640657d6d9e2e90d620ea3ee19ae6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:32:22 -0300 Subject: [PATCH 3762/5214] perf symbols: Break infinite loop on zero-filled notes in sysfs__read_build_id() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sysfs__read_build_id() iterates ELF note headers from sysfs files in a while(1) loop. If the file contains a zero-filled note header (both n_namesz and n_descsz are 0), the code computes n = namesz + descsz = 0 and calls read(fd, bf, 0). read() with count 0 returns 0, which matches the expected (ssize_t)n value, so the error check passes and the loop repeats — reading the same zero bytes and spinning forever. This can happen with corrupted or zero-padded sysfs pseudo-files. Add a check for n == 0 before the read, since no valid ELF note has both name and description of zero length. Reported-by: sashiko-bot Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c301c298ded9..39562bdec8b9 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -995,6 +995,9 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else { n = namesz + descsz; } + /* no valid note has both namesz and descsz zero */ + if (n == 0) + break; if (read(fd, bf, n) != (ssize_t)n) break; } From f973e52a99776fcc473488984828d1fce56d5382 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:33:48 -0300 Subject: [PATCH 3763/5214] perf dso: Fix heap overflow in dso__get_filename() on decompressed path dso__get_filename() allocates name with malloc(PATH_MAX), but the dso__filename_with_chroot() path replaces name with an asprintf'd exact-size string (e.g. 8 bytes for "/a/b.ko"). When the DSO needs decompression, dso__decompress_kmodule_path() writes the temp path ("/tmp/perf-kmod-XXXXXX", 22 bytes) into newpath, and strcpy(name, newpath) overflows the smaller allocation. Replace the strcpy with strdup(newpath) + free(name) so the buffer is always correctly sized for its content. Reported-by: sashiko-bot Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reviewed-by: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 5d0179758738..511921bd901d 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -603,8 +603,15 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, /* empty pathname means file wasn't actually compressed */ if (newpath[0] != '\0') { + char *tmp = strdup(newpath); + + if (!tmp) { + unlink(newpath); + goto out; + } + free(name); + name = tmp; *decomp = true; - strcpy(name, newpath); } } return name; From d2c6069d68ee9d53b05fe38bc2049cc4286fbb16 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:16:02 -0300 Subject: [PATCH 3764/5214] perf dso: Set error code when open() fails on uncompressed fallback path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit filename__decompress() has an early return for files that are not actually compressed, where it calls open() directly. When open() fails, the function returns -1 but never sets *err. The caller chain (decompress_kmodule → dso__decompress_kmodule_path → dso__get_filename) then reads *dso__load_errno(dso) to set errno, but that field was never populated, so errno gets a stale or zero value. With errno=0, __open_dso() computes fd = -errno = 0, which is non- negative, so callers treat fd 0 (stdin) as a valid DSO file descriptor. Set *err = errno when open() fails on the uncompressed path, matching the error handling on the compressed path at line 354. Reported-by: sashiko-bot Fixes: 8b42b7e5e8b5692b ("perf tools: Add is_compressed callback to compressions array") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 511921bd901d..1a2fc6d18da7 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -344,9 +344,12 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) { + fd = open(name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + *err = errno; if (pathname && len > 0) pathname[0] = '\0'; - return open(name, O_RDONLY | O_CLOEXEC); + return fd; } fd = mkostemp(tmpbuf, O_CLOEXEC); From 7b0df6f4d498b1608afccfd6dffb264e6da91693 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:34:38 -0300 Subject: [PATCH 3765/5214] perf tools: Use snprintf() for root_dir path construction get_kernel_version() in machine.c and dso__load_guest_kernel_sym() in symbol.c use sprintf() to construct paths by prepending root_dir to "/proc/version" and "/proc/kallsyms" respectively. Both write into PATH_MAX stack buffers, but root_dir comes from --guestmount or KVM configuration and is not length-checked. A root_dir at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof(path) to prevent overflow. Reported-by: sashiko-bot Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Cc: Zhang Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 2 +- tools/perf/util/symbol.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 1ea06fde14e0..31715366e29f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1336,7 +1336,7 @@ static char *get_kernel_version(const char *root_dir) char *name, *tmp; const char *prefix = "Linux version "; - sprintf(version, "%s/proc/version", root_dir); + snprintf(version, sizeof(version), "%s/proc/version", root_dir); file = fopen(version, "r"); if (!file) return NULL; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 2cc911af8c81..cd379ced19e5 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2283,7 +2283,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); + snprintf(path, sizeof(path), "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } From cfafef390ca9c753b34c7e97b5abee4cab0ce270 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:40:34 -0300 Subject: [PATCH 3766/5214] perf hwmon: Fix fd check to accept fd 0 in hwmon_pmu__describe_items() hwmon_pmu__describe_items() checks 'if (fd > 0)' after openat(), which incorrectly rejects fd 0. While fd 0 is normally stdin, if stdin has been closed (common in daemon/service contexts), the kernel reuses fd 0 for the next open. With fd > 0, the sysfs file is not read and the fd is leaked. Change to 'if (fd >= 0)' to match the standard openat() error check. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hwmon_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index efd3067a2e59..ed544dca70c3 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -435,7 +435,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si hwmon_item_strs[bit], is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); - if (fd > 0) { + if (fd >= 0) { ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') From 500f5dd0a8b6f7bd174102587c7dff5a7d2fecbf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:00:11 -0300 Subject: [PATCH 3767/5214] perf sched: Replace (void*)1 sentinel with proper runtime allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit map__findnew_thread() marks color-pid threads by storing (void*)1 as the thread private data via thread__set_priv(). This sentinel value causes two problems: 1. thread__get_runtime() returns (void*)1 as a struct thread_runtime pointer. Any field access (e.g. tr->shortname) dereferences address 1, which is an unmapped page — immediate segfault. 2. cmd_sched() registers free() as the thread priv destructor, so thread cleanup calls free((void*)1) — undefined behavior that corrupts the heap on many allocators. Fix by adding a 'color' flag to struct thread_runtime and allocating a real runtime struct for color-pid threads. thread__has_color() now checks the flag instead of relying on priv being non-NULL. Reported-by: sashiko-bot Fixes: 58a606149c60d5da ("perf sched: Avoid union type punning undefined behavior") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5f16caebd964..7fd63a9db457 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -274,6 +274,7 @@ struct thread_runtime { u64 migrations; int prio; + bool color; }; /* per event run time data */ @@ -1589,22 +1590,32 @@ static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unus static bool thread__has_color(struct thread *thread) { - return thread__priv(thread) != NULL; + struct thread_runtime *tr = thread__priv(thread); + + return tr != NULL && tr->color; } static struct thread* map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid) { struct thread *thread = machine__findnew_thread(machine, pid, tid); - bool color = false; - if (!sched->map.color_pids || !thread || thread__priv(thread)) + if (!sched->map.color_pids || !thread) return thread; - if (thread_map__has(sched->map.color_pids, tid)) - color = true; + /* + * Always check the color-pids map, even if thread__priv() is + * already set. COMM events processed before the first sched_switch + * allocate a thread_runtime via thread__get_runtime(), so priv is + * non-NULL before we ever get here. Skipping the check on non-NULL + * priv would prevent those threads from being colored. + */ + if (thread_map__has(sched->map.color_pids, tid)) { + struct thread_runtime *tr = thread__get_runtime(thread); - thread__set_priv(thread, color ? ((void*)1) : NULL); + if (tr) + tr->color = true; + } return thread; } From 10b3c3d63ecc17c6acb855bac5f40367f1115765 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:01:15 -0300 Subject: [PATCH 3768/5214] perf bpf: Validate func_info_rec_size and sub_id in synthesize_bpf_prog_name() synthesize_bpf_prog_name() computes a pointer into the func_info array using sub_id * info->func_info_rec_size without validating either value. Both come from perf.data and are untrusted: - A func_info_rec_size smaller than sizeof(struct bpf_func_info) means the finfo pointer would reference a truncated entry, reading past it into adjacent data. - A sub_id >= nr_func_info computes an offset past the func_info buffer, causing an out-of-bounds read. Add bounds checks for both values before computing the pointer offset. When validation fails, fall through to the non-BTF name path instead of reading garbage. Reported-by: sashiko-bot Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index c4594969d767..fe6fbca508c5 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -143,7 +143,9 @@ static int synthesize_bpf_prog_name(char *buf, int size, name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); - if (btf) { + if (btf && + info->func_info_rec_size >= sizeof(*finfo) && + sub_id < info->nr_func_info) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); if (t) From 2d6ea0875093da9033fcb62c09a9e2f1de49fe91 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:02:25 -0300 Subject: [PATCH 3769/5214] perf bpf: Reject oversized BPF metadata events that truncate header.size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bpf_metadata_alloc() computes event_size from the number of BPF metadata variables and stores it in header.size, which is __u16. With 204 or more .rodata variables prefixed "bpf_metadata_", event_size exceeds 65535 and silently truncates. The truncated header.size causes synthesize_perf_record_bpf_metadata() to allocate a buffer sized by the truncated value, then memcpy the full event data into it — a heap buffer overflow. Add a check that event_size fits in __u16 before proceeding. BPF programs with that many metadata variables are exotic enough that silently dropping the metadata is acceptable. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Reviewed-by: Ian Rogers Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index fe6fbca508c5..57d53ba84835 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -369,6 +369,15 @@ static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, event_size = sizeof(metadata->event->bpf_metadata) + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + /* + * header.size is __u16. synthesize_perf_record_bpf_metadata() + * adds machine->id_hdr_size (up to ~64 bytes) after this, so + * leave headroom to prevent the final size from wrapping. + */ + if (event_size > UINT16_MAX - 256) { + bpf_metadata_free(metadata); + return NULL; + } metadata->event = zalloc(event_size); if (!metadata->event) { bpf_metadata_free(metadata); From 033e85edfbf271f92979d2a39aeaf40f8472a795 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:03:16 -0300 Subject: [PATCH 3770/5214] perf bpf: Bounds-check array offsets in bpil_offs_to_addr() bpil_offs_to_addr() converts offsets stored in perf.data's bpf_prog_info_linear structure into heap pointers by adding the offset to the data allocation base. The offsets come from untrusted file input and are not validated against data_len. If an offset exceeds data_len, the computed address points outside the allocated data buffer. Callers like synthesize_bpf_prog_name() then dereference prog_tags[sub_id] or func_info pointers, reading arbitrary heap memory. Add a bounds check: when an offset exceeds data_len, zero the field and skip the conversion. This prevents out-of-bounds pointer construction from crafted perf.data files. Reported-by: sashiko-bot Fixes: 6ac22d036f86c4e2 ("perf bpf: Pull in bpf_program__get_prog_info_linear()") Cc: Dave Marchevsky Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-utils.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index d6d2c9c190f7..69148197b1ef 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -264,12 +264,28 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; + __u32 count, size; if ((info_linear->arrays & (1UL << i)) == 0) continue; offs = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); + count = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->count_offset); + size = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->size_offset); + /* offset and extent from perf.data are untrusted — keep within data[] */ + if (offs >= info_linear->data_len || + (u64)count * size > info_linear->data_len - offs) { + bpf_prog_info_set_offset_u64(&info_linear->info, + desc->array_offset, 0); + bpf_prog_info_set_offset_u32(&info_linear->info, + desc->count_offset, 0); + /* clear the bit so bpil_addr_to_offs() won't reverse a zeroed address */ + info_linear->arrays &= ~(1UL << i); + continue; + } addr = offs + ptr_to_u64(info_linear->data); bpf_prog_info_set_offset_u64(&info_linear->info, desc->array_offset, addr); From 4a7500d772fe59653053db22ca83c9e2232b22e1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:23:35 -0300 Subject: [PATCH 3771/5214] perf c2c: Free format list entries when releasing c2c hist entries c2c_hists__init() calls hpp_list__parse() which allocates and registers format entries on hists->list. When c2c_he_free() destroys a c2c hist entry, it deletes the histogram entries and frees the hists container but never unregisters the format list entries, leaking them. Call perf_hpp__reset_output_field() before freeing the hists to properly unregister and free all format entries. Fixes: f485e33c4543ac31 ("perf c2c report: Add cacheline hists processing") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e205f58b2f3d..07c7e8fb315e 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -185,6 +185,7 @@ static void c2c_he_free(void *he) c2c_he = container_of(he, struct c2c_hist_entry, he); if (c2c_he->hists) { hists__delete_entries(&c2c_he->hists->hists); + perf_hpp__reset_output_field(&c2c_he->hists->list); zfree(&c2c_he->hists); } From 542e88a4c6f7b6edd1326ce767d4cb3c2ea9d61d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 22:45:08 -0300 Subject: [PATCH 3772/5214] perf cs-etm: Reject CPU IDs that would overflow signed comparison metadata[j][CS_ETM_CPU] is a u64 from perf.data, but the comparison with max_cpu casts it to (int). A crafted value like 0xFFFFFFFF becomes -1 after the cast, which compares less than max_cpu (0), so the queue array is never sized to accommodate it. When the value is later passed to cs_etm__get_queue(), it indexes queue_array with the original large value, causing an out-of-bounds access. Validate that CS_ETM_CPU fits in an int before using it in the signed comparison. Fixes: 57880a7966be510c ("perf: cs-etm: Allocate queues for all CPUs") Reported-by: sashiko-bot Cc: James Clark Cc: Adrian Hunter Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 5e92359f51a7..0927b0b9c06b 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,6 +6,7 @@ * Author: Mathieu Poirier */ +#include #include #include #include @@ -3468,7 +3469,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, goto err_free_metadata; } - if ((int) metadata[j][CS_ETM_CPU] > max_cpu) + /* CPU id comes from perf.data and must fit max_cpu + 1 without overflow */ + if (metadata[j][CS_ETM_CPU] >= INT_MAX) { + err = -EINVAL; + goto err_free_metadata; + } + + if ((int)metadata[j][CS_ETM_CPU] > max_cpu) max_cpu = metadata[j][CS_ETM_CPU]; } From e22a4228546f7220d0425630abf703fd2ef7c600 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 16 Jun 2026 16:37:09 -0300 Subject: [PATCH 3773/5214] perf evsel: Add no-libtraceevent stubs for evsel__field() and evsel__common_field() When building without libtraceevent (NO_LIBTRACEEVENT=1), evsel__field() and evsel__common_field() are declared but never defined, causing link errors in any code path that references them. Add inline stubs that return NULL when HAVE_LIBTRACEEVENT is not defined, matching the pattern used by other evsel accessor functions. Cc: Aaron Tomlin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8009be22cc3f..b959d4797b14 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -397,8 +397,22 @@ struct tep_format_field; u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, bool needs_swap); +#ifdef HAVE_LIBTRACEEVENT struct tep_format_field *evsel__field(struct evsel *evsel, const char *name); struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *name); +#else +static inline struct tep_format_field * +evsel__field(struct evsel *evsel __maybe_unused, const char *name __maybe_unused) +{ + return NULL; +} + +static inline struct tep_format_field * +evsel__common_field(struct evsel *evsel __maybe_unused, const char *name __maybe_unused) +{ + return NULL; +} +#endif bool __evsel__match(const struct evsel *evsel, u32 type, u64 config); From 9212e395c64d80f7b6af314ff6dfc4b526571493 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 16 Jun 2026 16:35:43 -0300 Subject: [PATCH 3774/5214] perf evsel: Add lazy-initialized probe type detection helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several places in perf need to check whether an evsel is a kprobe or uprobe, which requires looking up the PMU by name via evsel__find_pmu(). This lookup walks the PMU list each time, which is wasteful when the same evsel is checked repeatedly. Add evsel__is_kprobe(), evsel__is_uprobe(), and evsel__is_probe() that resolve the probe type on first call via evsel__pmu_name() and cache the result in a 3-bit field (probe_type) in struct evsel. The field fits in existing padding after the bool fields, so struct size does not grow. The enum uses PROBE__UNKNOWN (0) as the uninitialized sentinel — explicitly set in evsel__init() — so the lookup happens on first use. PROBE__NOPE (1) caches "not a probe" to avoid repeated negative lookups. PMU-based probes (kprobe/uprobe PMU) are detected by PMU name. Ftrace-based dynamic probes (created via tracefs, reported as PMU "tracepoint") are detected by the __probe_ip field that the kernel adds to all dynamic probe formats. This covers kprobes, uprobes, and fprobes regardless of their group/system name. Cc: Aaron Tomlin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 53 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/evsel.h | 5 ++++ 2 files changed, 58 insertions(+) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 05fa0010c858..ea9fa04429f0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -254,6 +254,58 @@ const char *evsel__pmu_name(const struct evsel *evsel) return event_type(evsel->core.attr.type); } +enum evsel_probe_type { + PROBE__UNKNOWN = 0, + PROBE__NOPE = 1, + PROBE__KPROBE = 2, + PROBE__UPROBE = 3, + /* + * Ftrace-based dynamic probes (kprobes/uprobes/fprobes created via + * tracefs) report PMU "tracepoint", not "kprobe"/"uprobe". Detect + * them by the __probe_ip field that the kernel adds to all dynamic + * probe formats. + */ + PROBE__FTRACE = 4, +}; + +static void evsel__resolve_probe_type(struct evsel *evsel) +{ + const char *name = evsel__pmu_name(evsel); + + if (!strcmp(name, "kprobe")) + evsel->probe_type = PROBE__KPROBE; + else if (!strcmp(name, "uprobe")) + evsel->probe_type = PROBE__UPROBE; + else if (!strcmp(name, "tracepoint") && evsel__field(evsel, "__probe_ip")) + evsel->probe_type = PROBE__FTRACE; + else + evsel->probe_type = PROBE__NOPE; +} + +bool evsel__is_probe(struct evsel *evsel) +{ + if (evsel->probe_type == PROBE__UNKNOWN) + evsel__resolve_probe_type(evsel); + + return evsel->probe_type > PROBE__NOPE; +} + +bool evsel__is_kprobe(struct evsel *evsel) +{ + if (evsel->probe_type == PROBE__UNKNOWN) + evsel__resolve_probe_type(evsel); + + return evsel->probe_type == PROBE__KPROBE; +} + +bool evsel__is_uprobe(struct evsel *evsel) +{ + if (evsel->probe_type == PROBE__UNKNOWN) + evsel__resolve_probe_type(evsel); + + return evsel->probe_type == PROBE__UPROBE; +} + #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) int __evsel__sample_size(u64 sample_type) @@ -413,6 +465,7 @@ void evsel__init(struct evsel *evsel, evsel->supported = true; evsel->alternate_hw_config = PERF_COUNT_HW_MAX; evsel->script_output_type = -1; // FIXME: OUTPUT_TYPE_UNSET, see builtin-script.c + evsel->probe_type = PROBE__UNKNOWN; } struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b959d4797b14..163fc2b6a7ea 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -126,6 +126,7 @@ struct evsel { bool needs_uniquify; bool fallenback_eacces; bool fallenback_eopnotsupp; + u8 probe_type:3; struct hashmap *per_pkg_mask; int err; int script_output_type; @@ -259,6 +260,10 @@ struct perf_pmu *evsel__find_pmu(const struct evsel *evsel); const char *evsel__pmu_name(const struct evsel *evsel); bool evsel__is_aux_event(const struct evsel *evsel); +bool evsel__is_probe(struct evsel *evsel); +bool evsel__is_kprobe(struct evsel *evsel); +bool evsel__is_uprobe(struct evsel *evsel); + struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx); static inline struct evsel *evsel__new(struct perf_event_attr *attr) From d669529868b355e1f10ff869539dc995cd25db3f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 15 Jun 2026 22:17:41 -0300 Subject: [PATCH 3775/5214] perf trace: Guard __probe_ip suppression with evsel__is_probe() trace__fprintf_tp_fields() compares every field name against "__probe_ip" for all tracepoint events, but this field is only implicitly added by the Ftrace subsystem to bare dynamic probes. Add an evsel__is_probe() check before the strcmp so the string comparison is skipped entirely for non-probe events. Reviewed-by: Aaron Tomlin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index a8492da23a9c..57f3f14c5d43 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3267,7 +3267,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample * * If verbose mode is enabled, ensure it is formatted as a * hexadecimal memory address rather than a signed integer. */ - if (!strcmp(field->name, "__probe_ip")) { + if (evsel__is_probe(evsel) && !strcmp(field->name, "__probe_ip")) { if (!verbose) continue; From 66725039f7090afe14c31bd259e2059a68f04023 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 14:19:43 -0700 Subject: [PATCH 3776/5214] Input: mms114 - reject an oversized device packet size mms114_interrupt() reads a packet of touch data from the device into a fixed-size on-stack buffer struct mms114_touch touch[MMS114_MAX_TOUCH]; which holds MMS114_MAX_TOUCH (10) events of MMS114_EVENT_SIZE (8) bytes, i.e. 80 bytes. The length of the I2C read into it is taken verbatim from the device: packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; ... error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); packet_size is a single device register byte (0x0F) and the only check is the lower bound packet_size <= 0; it is never bounded against the size of touch[]. A malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can report a packet_size of up to 255, so __mms114_read_reg() writes up to 175 bytes past the end of touch[] on the IRQ-thread stack: a stack out-of-bounds write that can overwrite the stack canary, saved registers and the return address. A well-formed device never reports more than the buffer holds, so reject an oversized packet and drop the report, consistent with the handler's other error paths, rather than reading past the buffer. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260612-b4-disp-dc4b8dc4-v1-1-d7cb0a828d92@proton.me Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 9597214d9d3c..4d748a13408d 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -226,6 +226,12 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) if (packet_size <= 0) goto out; + if (packet_size > sizeof(touch)) { + dev_err(&client->dev, "Invalid packet size %d (max %zu)\n", + packet_size, sizeof(touch)); + goto out; + } + /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) touch_size = packet_size / MMS136_EVENT_SIZE; From 4910aa198d25e5d1067236560ba34ab12bccc677 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:52:16 -0500 Subject: [PATCH 3777/5214] gpio: pisosr: Read "ngpios" as u32 The generic "ngpios" property is encoded as a normal uint32 cell. The pisosr driver stores it in the gpio_chip field, but reading it with a u16 helper does not match the DT property encoding. Read "ngpios" as u32 and keep the existing assignment to the chip field. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260612215216.1887485-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pisosr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index 7ec6a46ed600..2732ea8c16b7 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -112,6 +112,7 @@ static int pisosr_gpio_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct pisosr_gpio *gpio; + u32 ngpios; int ret; gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); @@ -120,7 +121,8 @@ static int pisosr_gpio_probe(struct spi_device *spi) gpio->chip = template_chip; gpio->chip.parent = dev; - of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio); + if (!of_property_read_u32(dev->of_node, "ngpios", &ngpios)) + gpio->chip.ngpio = ngpios; gpio->spi = spi; From 0482862a90169f4daaba0ed31a85d8304bf51e04 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 17:19:18 +0800 Subject: [PATCH 3778/5214] gpio: mlxbf3: fail probe if gpiochip registration fails mlxbf3_gpio_probe() logs a devm_gpiochip_add_data() failure but still returns success. That leaves the platform device bound even though the GPIO chip was not registered. Return the registration error so probe failure matches the missing gpiochip state. Fixes: cd33f216d241 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Pengpeng Hou Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260615091918.43333-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 4770578269ba..566326644a2c 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -255,7 +255,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, gc, gs); if (ret) - dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); + return dev_err_probe(dev, ret, + "Failed adding memory mapped gpiochip\n"); return 0; } From 4e1187e12de40b5301977b2476d21b569358dafb Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Mon, 15 Jun 2026 21:56:17 +0530 Subject: [PATCH 3779/5214] powerpc: Restore KUAP registers on syscall restart exit During a syscall restart, block KUAP so that pending interrupts can be replayed. The original KUAP state is not restored before returning to userspace, causing subsequent userspace accesses to fault and eventually trigger bad_access_pkey(), crashing the kernel. The original KUAP register values are already saved in arch_enter_from_user_mode(). Restore them on the syscall restart exit path before returning to userspace. Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") Reported-by: Sayali Patil Closes: https://lore.kernel.org/linuxppc-dev/fcd11556-27ac-4cd7-8c77-50716dec6985@linux.ibm.com/ Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Tested-by: Sayali Patil [Maddy: Added Closes tag] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615162617.2861795-1-mkchauras@gmail.com --- arch/powerpc/kernel/interrupt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index 89a999be1352..f04978080837 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -166,6 +166,7 @@ notrace unsigned long syscall_exit_restart(unsigned long r3, struct pt_regs *reg goto again; } + kuap_user_restore(regs); regs->exit_result |= regs->exit_flags; return regs->exit_result; From 42f252f5a646866a95f025863c8b201042494ba1 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Tue, 16 Jun 2026 11:44:35 +0530 Subject: [PATCH 3780/5214] powerpc/fadump: define MIN_RMA in bytes rather than MB The MIN_RMA size checks in fadump_setup_param_area() use (MIN_RMA * 1024 * 1024), which is evaluated in int and can overflow when MIN_RMA is increased to values such as SZ_2G, triggering compiler warnings such as: warning: integer overflow in expression of type 'int' results in '0' [-Woverflow] Define MIN_RMA directly in bytes using SZ_1M and update the callers accordingly. This avoids repeated unit conversions and prevents integer overflow. Also convert MIN_RMA back to MB when populating the firmware architecture vector, since firmware expects the value in MB. Suggested-by: Christophe Leroy (CS GROUP) Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Sayali Patil Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/310b040acef712fdc79e3e37d0f4c2213938b556.1781589284.git.sayalip@linux.ibm.com --- arch/powerpc/include/asm/prom.h | 4 +++- arch/powerpc/kernel/fadump.c | 4 ++-- arch/powerpc/kernel/prom_init.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index f679a11a7e7f..f4991d10d89e 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -12,12 +12,14 @@ * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. */ #include +#include #include struct device_node; struct property; -#define MIN_RMA 768 /* Minimum RMA (in MB) for CAS negotiation */ +/* Minimum RMA in bytes for CAS negotiation */ +#define MIN_RMA (768ULL * SZ_1M) #define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ #define OF_DT_END_NODE 0x2 /* End node */ diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index a313b1653124..7f79c9aea4a9 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -1759,10 +1759,10 @@ void __init fadump_setup_param_area(void) * 2. The range should be between MIN_RMA and RMA size (ppc64_rma_size) * 3. It must not overlap with the fadump reserved area. */ - if (ppc64_rma_size < MIN_RMA*1024*1024) + if (ppc64_rma_size < MIN_RMA) return; - range_start = MIN_RMA * 1024 * 1024; + range_start = MIN_RMA; range_end = min(ppc64_rma_size, fw_dump.boot_mem_top); } diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index f26e80cbc615..53503937de0e 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1061,7 +1061,7 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .virt_base = cpu_to_be32(0xffffffff), .virt_size = cpu_to_be32(0xffffffff), .load_base = cpu_to_be32(0xffffffff), - .min_rma = cpu_to_be32(MIN_RMA), + .min_rma = cpu_to_be32(MIN_RMA / SZ_1M), .min_load = cpu_to_be32(0xffffffff), /* full client load */ .min_rma_percent = 0, /* min RMA percentage of total RAM */ .max_pft_size = 48, /* max log_2(hash table size) */ From 2564ca2e31bd8ee8348362941af2ee4671e487ca Mon Sep 17 00:00:00 2001 From: Vasileios Almpanis Date: Mon, 15 Jun 2026 16:45:57 +0200 Subject: [PATCH 3781/5214] io_uring/nop: fix file reference leak with IOSQE_FIXED_FILE NOP file-acquisition support choses between a fixed (registered) file and a normal fget()'d file based on its own IORING_NOP_FIXED_FILE flag in sqe->nop_flags. However, a request's REQ_F_FIXED_FILE is set independently from the generic IOSQE_FIXED_FILE sqe flag during request init, before the issue handler runs. If a NOP is submitted with IOSQE_FIXED_FILE set (so REQ_F_FIXED_FILE is set) but without IORING_NOP_FIXED_FILE, io_nop() takes the normal path and grabs a real reference via io_file_get_normal(). On completion, io_put_file() only drops the reference when REQ_F_FIXED_FILE is clear, so the fget()'d file is never released and leaks: BUG: memory leak unreferenced object 0xffff88800f42c240 (size 176): kmem_cache_alloc_noprof+0x358/0x440 alloc_empty_file+0x57/0x180 path_openat+0x44/0x1e50 do_file_open+0x121/0x200 do_sys_openat2+0xa7/0x150 __x64_sys_openat+0x82/0xf0 Decide between fixed and normal file acquisition from REQ_F_FIXED_FILE, the same way io_assign_file() does for every other opcode, and fold IORING_NOP_FIXED_FILE into REQ_F_FIXED_FILE at prep time. Cc: stable@vger.kernel.org Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") Reported-by: syzbot+2cd473471e77bda12b0e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?id=879092631b98f73a28ea405adacfa5bb34a14a25 Signed-off-by: Vasileios Almpanis Link: https://patch.msgid.link/20260615144619.482749-1-vasilisalmpanis@gmail.com Signed-off-by: Jens Axboe --- io_uring/nop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/io_uring/nop.c b/io_uring/nop.c index 91ae0b2e7e55..60ab19604b36 100644 --- a/io_uring/nop.c +++ b/io_uring/nop.c @@ -40,6 +40,8 @@ int io_nop_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) nop->fd = READ_ONCE(sqe->fd); else nop->fd = -1; + if (nop->flags & IORING_NOP_FIXED_FILE) + req->flags |= REQ_F_FIXED_FILE; if (nop->flags & IORING_NOP_FIXED_BUFFER) req->buf_index = READ_ONCE(sqe->buf_index); if (nop->flags & IORING_NOP_CQE32) { @@ -59,12 +61,10 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) int ret = nop->result; if (nop->flags & IORING_NOP_FILE) { - if (nop->flags & IORING_NOP_FIXED_FILE) { + if (req->flags & REQ_F_FIXED_FILE) req->file = io_file_get_fixed(req, nop->fd, issue_flags); - req->flags |= REQ_F_FIXED_FILE; - } else { + else req->file = io_file_get_normal(req, nop->fd); - } if (!req->file) { ret = -EBADF; goto done; From c554246ff4c68abf71b61a89c6e39d3cf94f523e Mon Sep 17 00:00:00 2001 From: Michael Wigham Date: Sat, 13 Jun 2026 23:52:16 +0100 Subject: [PATCH 3782/5214] io_uring/rw: preserve partial result for iopoll A partial read will store the completed byte count in io->bytes_done. The regular completion path applies io_fixup_rw_res() so that, when the following operation reaches EOF, the number of bytes already read is returned. The iopoll completion path does not apply this fixup to the return value and can return zero instead. Use the fixup result when updating the CQE, and the raw result for the reissue check. Cc: stable@vger.kernel.org Fixes: 4d9cb92ca41d ("io_uring/rw: fix short rw error handling") Signed-off-by: Michael Wigham Link: https://patch.msgid.link/20260613225240.34032-1-michael@wigham.net Signed-off-by: Jens Axboe --- io_uring/rw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index 0c4834645279..63b6519e498c 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -601,15 +601,15 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res) { struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb); struct io_kiocb *req = cmd_to_io_kiocb(rw); + int final_res = io_fixup_rw_res(req, res); if (kiocb->ki_flags & IOCB_WRITE) io_req_end_write(req); - if (unlikely(res != req->cqe.res)) { - if (res == -EAGAIN && io_rw_should_reissue(req)) - req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; - else - req->cqe.res = res; - } + + if (res == -EAGAIN && io_rw_should_reissue(req)) + req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; + else if (unlikely(final_res != req->cqe.res)) + req->cqe.res = final_res; /* order with io_iopoll_complete() checking ->iopoll_completed */ smp_store_release(&req->iopoll_completed, 1); From 17c5d247e3e4708cac05ff087c8013c0dda383a2 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 16 May 2026 02:41:19 +0300 Subject: [PATCH 3783/5214] i2c: qcom-cci: Do not check return value of cci_init() The cci_init() function is not supposed to fail, and it never returns a non-zero, so it'd make sense to convert its signature to void. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260515234121.1607425-3-vladimir.zapolskiy@linaro.org --- drivers/i2c/busses/i2c-qcom-cci.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 01e440b6585d..be41a53e30c6 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -243,7 +243,7 @@ static int cci_reset(struct cci *cci) return 0; } -static int cci_init(struct cci *cci) +static void cci_init(struct cci *cci) { u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE | CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT | @@ -284,8 +284,6 @@ static int cci_init(struct cci *cci) val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp; writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i)); } - - return 0; } static int cci_run_queue(struct cci *cci, u8 master, u8 queue) @@ -611,9 +609,7 @@ static int cci_probe(struct platform_device *pdev) if (ret < 0) goto error; - ret = cci_init(cci); - if (ret < 0) - goto error; + cci_init(cci); pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); From 697d58d457e999a03c7c395ff0fbf13e765f0997 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 16 May 2026 02:41:20 +0300 Subject: [PATCH 3784/5214] i2c: qcom-cci: Move cci_init() under cci_reset() function On probe or runtime errors cci_reset() is called and it should be coupled with cci_init(), instead of doing this on caller's side, embed cci_init() directly into the cci_reset() function. This is a non-functional change, cci_reset() and cci_init() function bodies are reordered. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260515234121.1607425-4-vladimir.zapolskiy@linaro.org --- drivers/i2c/busses/i2c-qcom-cci.c | 41 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index be41a53e30c6..2d73903f14d3 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -225,24 +225,6 @@ static int cci_halt(struct cci *cci, u8 master_num) return 0; } -static int cci_reset(struct cci *cci) -{ - /* - * we reset the whole controller, here and for implicity use - * master[0].xxx for waiting on it. - */ - reinit_completion(&cci->master[0].irq_complete); - writel(CCI_RESET_CMD_MASK, cci->base + CCI_RESET_CMD); - - if (!wait_for_completion_timeout(&cci->master[0].irq_complete, - CCI_TIMEOUT)) { - dev_err(cci->dev, "CCI reset timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - static void cci_init(struct cci *cci) { u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE | @@ -286,6 +268,26 @@ static void cci_init(struct cci *cci) } } +static int cci_reset(struct cci *cci) +{ + /* + * we reset the whole controller, here and for implicity use + * master[0].xxx for waiting on it. + */ + reinit_completion(&cci->master[0].irq_complete); + writel(CCI_RESET_CMD_MASK, cci->base + CCI_RESET_CMD); + + if (!wait_for_completion_timeout(&cci->master[0].irq_complete, + CCI_TIMEOUT)) { + dev_err(cci->dev, "CCI reset timeout\n"); + return -ETIMEDOUT; + } + + cci_init(cci); + + return 0; +} + static int cci_run_queue(struct cci *cci, u8 master, u8 queue) { u32 val; @@ -302,7 +304,6 @@ static int cci_run_queue(struct cci *cci, u8 master, u8 queue) dev_err(cci->dev, "master %d queue %d timeout\n", master, queue); cci_reset(cci); - cci_init(cci); return -ETIMEDOUT; } @@ -609,8 +610,6 @@ static int cci_probe(struct platform_device *pdev) if (ret < 0) goto error; - cci_init(cci); - pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); pm_runtime_set_active(dev); From f0285c286bca5a1e018ba25040cef6c7806c31ef Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 16 May 2026 02:41:21 +0300 Subject: [PATCH 3785/5214] i2c: qcom-cci: Remove overcautious disable_irq() calls In cci_probe() the controller's interrupt is requested using a devres managed API, and in cci_probe() error path and cci_remove() it'd be safe to rely on devres mechanism to free and shutdown the interrupt, thus explicit disable_irq() calls can be removed as unnecessary ones. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260515234121.1607425-5-vladimir.zapolskiy@linaro.org --- drivers/i2c/busses/i2c-qcom-cci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 2d73903f14d3..4d64895a9e9e 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -608,7 +608,7 @@ static int cci_probe(struct platform_device *pdev) ret = cci_reset(cci); if (ret < 0) - goto error; + goto disable_clocks; pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); @@ -638,8 +638,6 @@ static int cci_probe(struct platform_device *pdev) of_node_put(cci->master[i].adap.dev.of_node); } } -error: - disable_irq(cci->irq); disable_clocks: cci_disable_clocks(cci); @@ -659,7 +657,6 @@ static void cci_remove(struct platform_device *pdev) } } - disable_irq(cci->irq); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); } From e43f32816a1b1fe5a86279411626fe3a9be56d45 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 11:05:13 +0800 Subject: [PATCH 3786/5214] i2c: davinci: Unregister cpufreq notifier on probe failure davinci_i2c_probe() registers a cpufreq transition notifier before adding the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path releases the device resources without unregistering the notifier. Add a dedicated error path to unregister the cpufreq notifier after i2c_add_numbered_adapter() fails. Fixes: 82c0de11b734 ("i2c: davinci: Add cpufreq support") Signed-off-by: Haoxiang Li Cc: # v2.6.36+ Reviewed-by: Bartosz Golaszewski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260610030513.2651018-1-haoxiang_li2024@163.com --- drivers/i2c/busses/i2c-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 66c23535656b..0617f416cb0b 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -818,12 +818,14 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); if (r) - goto err_unuse_clocks; + goto err_cpufreq; pm_runtime_put_autosuspend(dev->dev); return 0; +err_cpufreq: + i2c_davinci_cpufreq_deregister(dev); err_unuse_clocks: pm_runtime_dont_use_autosuspend(dev->dev); pm_runtime_put_sync(dev->dev); From 5da26ab52ac5502b09b7c20970ee08291130673f Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 8 Jun 2026 12:17:39 -0700 Subject: [PATCH 3787/5214] dt-bindings: i2c: convert i2c-mux-reg to DT schema Convert Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt to the YAML schema so the i2c-mux-reg binding is validated by dt_binding_check. Faithful port of the existing properties; no semantic change. Signed-off-by: Abdurrahman Hussain Acked-by: Conor Dooley Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260608-i2c-mux-reg-base-bus-num-v2-1-776e313f213a@nexthop.ai --- .../devicetree/bindings/i2c/i2c-mux-reg.txt | 74 --------------- .../devicetree/bindings/i2c/i2c-mux-reg.yaml | 92 +++++++++++++++++++ 2 files changed, 92 insertions(+), 74 deletions(-) delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-reg.yaml diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt deleted file mode 100644 index b9d9755e4172..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt +++ /dev/null @@ -1,74 +0,0 @@ -Register-based I2C Bus Mux - -This binding describes an I2C bus multiplexer that uses a single register -to route the I2C signals. - -Required properties: -- compatible: i2c-mux-reg -- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side - port is connected to. -* Standard I2C mux properties. See i2c-mux.yaml in this directory. -* I2C child bus nodes. See i2c-mux.yaml in this directory. - -Optional properties: -- reg: this pair of specifies the register to control the mux. - The depends on its parent node. It can be any memory-mapped - address. The size must be either 1, 2, or 4 bytes. If reg is omitted, the - resource of this device will be used. -- little-endian: The existence indicates the register is in little endian. -- big-endian: The existence indicates the register is in big endian. - If both little-endian and big-endian are omitted, the endianness of the - CPU will be used. -- write-only: The existence indicates the register is write-only. -- idle-state: value to set the muxer to when idle. When no value is - given, it defaults to the last value used. - -Whenever an access is made to a device on a child bus, the value set -in the relevant node's reg property will be output to the register. - -If an idle state is defined, using the idle-state (optional) property, -whenever an access is not being made to a device on a child bus, the -register will be set according to the idle value. - -If an idle state is not defined, the most recently used value will be -left programmed into the register. - -Example of a mux on PCIe card, the host is a powerpc SoC (big endian): - - i2c-mux { - /* the depends on the address translation - * of the parent device. If omitted, device resource - * will be used instead. The size is to determine - * whether iowrite32, iowrite16, or iowrite8 will be used. - */ - reg = <0x6028 0x4>; - little-endian; /* little endian register on PCIe */ - compatible = "i2c-mux-reg"; - #address-cells = <1>; - #size-cells = <0>; - i2c-parent = <&i2c1>; - i2c@0 { - reg = <0>; - #address-cells = <1>; - #size-cells = <0>; - - si5338: clock-generator@70 { - compatible = "silabs,si5338"; - reg = <0x70>; - /* other stuff */ - }; - }; - - i2c@1 { - /* data is written using iowrite32 */ - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - - si5338: clock-generator@70 { - compatible = "silabs,si5338"; - reg = <0x70>; - /* other stuff */ - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-reg.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.yaml new file mode 100644 index 000000000000..01ade0771c60 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-mux-reg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Register-based I2C Bus Mux + +maintainers: + - Peter Rosin + +description: | + This binding describes an I2C bus multiplexer that uses a single + memory-mapped register to route the I2C signals. + + Whenever an access is made to a device on a child bus, the value + set in the relevant node's reg property is output to the register. + + If an idle state is defined via the idle-state property, the + register is set to that value whenever no access is being made. + Otherwise the most recently used value is left programmed. + +allOf: + - $ref: /schemas/i2c/i2c-mux.yaml# + +properties: + compatible: + const: i2c-mux-reg + + reg: + maxItems: 1 + description: | + Offset and size of the register that selects the active child + bus, relative to the parent node's address space. The size + determines the access width and must be 1, 2, or 4 bytes. If + omitted, the platform device's own memory resource is used + instead. + + i2c-parent: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle of the I2C bus that this multiplexer's master-side port + is connected to. + + little-endian: + type: boolean + description: Register is accessed in little-endian byte order. + + big-endian: + type: boolean + description: Register is accessed in big-endian byte order. + + write-only: + type: boolean + description: + Register is write-only; the driver must not read back the + current selection. + + idle-state: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Value to write to the register when no child bus is selected. + +required: + - compatible + - i2c-parent + +unevaluatedProperties: false + +examples: + - | + i2c-mux@6028 { + compatible = "i2c-mux-reg"; + reg = <0x6028 0x4>; + little-endian; + #address-cells = <1>; + #size-cells = <0>; + i2c-parent = <&i2c1>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; +... From e1e5c40a408b09d6e7bd603c51bb5b34868b54e7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 16 Jun 2026 08:07:59 +0200 Subject: [PATCH 3788/5214] dt-bindings: i2c: i2c-mux-pinctrl: change maintainer The YAML conversion added me as maintainer but I can't recall being asked nor do I want to maintain it. Thierry has created the YAML file and works for the company which contributed the driver. Signed-off-by: Wolfram Sang Acked-by: Conor Dooley Acked-by: Thierry Reding Acked-by: Krzysztof Kozlowski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260616060910.1480-2-wsa+renesas@sang-engineering.com --- Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.yaml index 2e3d555eb96c..99812a893476 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Pinctrl-based I2C Bus Mux maintainers: - - Wolfram Sang + - Thierry Reding description: | This binding describes an I2C bus multiplexer that uses pin multiplexing to route the I2C From 9108f7fa493b4c88cbc09503e0c164244456bad5 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 16 Jun 2026 13:44:29 +0200 Subject: [PATCH 3789/5214] regcache: Do not overwrite error code when finalizing cache after error During regcache initialization, if an error occurs in the cache_ops->populate callback, and if cache operations include an exit callback, the error code from populate() is overwritten with the return value from exit(). This hides the error condition from the caller of regcache_init(), and can cause NULL pointer dereferences when the regcache is later accessed. Fixes: 94a3a95f0315 ("regcache: Add ->populate() callback to separate from ->init()") Signed-off-by: Francesco Lavra Link: https://patch.msgid.link/20260616114429.1852456-1-flavra@baylibre.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 27616b05111c..aa8f2efed779 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -245,7 +245,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); map->lock(map->lock_arg); - ret = map->cache_ops->exit(map); + map->cache_ops->exit(map); map->unlock(map->lock_arg); } err_free_reg_defaults: From 28da1cce9e699aecd3554915e9d2bac0e3e15b43 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 15 Apr 2026 17:01:21 +0200 Subject: [PATCH 3790/5214] s390/mm: Complete ptep_get() conversion Finalize commit c33c794828f2 ("mm: ptep_get() conversion") and replace direct page table entry dereferencing with the proper accessors (ptep_get(), pmdp_get(), etc.). Override the default getter implementations even though they are currently identical: pud_clear(), p4d_clear(), and pgd_clear() require corresponding architecture-specific getters, but these are not yet defined. This avoids a dependency loop. Acked-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/boot/vmem.c | 32 +++++++------ arch/s390/include/asm/hugetlb.h | 2 +- arch/s390/include/asm/pgtable.h | 60 ++++++++++++++++++------ arch/s390/mm/hugetlbpage.c | 12 ++--- arch/s390/mm/pageattr.c | 45 ++++++++++-------- arch/s390/mm/vmem.c | 82 ++++++++++++++++++--------------- 6 files changed, 140 insertions(+), 93 deletions(-) diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 7d6cc4c85af0..ff6d58a476ba 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -338,7 +338,7 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e pte = pte_offset_kernel(pmd, addr); for (; addr < end; addr += PAGE_SIZE, pte++) { - if (pte_none(*pte)) { + if (pte_none(ptep_get(pte))) { if (kasan_pte_populate_zero_shadow(pte, mode)) continue; entry = __pte(resolve_pa_may_alloc(addr, PAGE_SIZE, mode)); @@ -355,26 +355,27 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e enum populate_mode mode) { unsigned long pa, next, pages = 0; - pmd_t *pmd, entry; + pmd_t *pmd, entry, large_entry; pte_t *pte; pmd = pmd_offset(pud, addr); for (; addr < end; addr = next, pmd++) { next = pmd_addr_end(addr, end); - if (pmd_none(*pmd)) { + entry = pmdp_get(pmd); + if (pmd_none(entry)) { if (kasan_pmd_populate_zero_shadow(pmd, addr, next, mode)) continue; pa = try_get_large_pmd_pa(pmd, addr, next, mode); if (pa != INVALID_PHYS_ADDR) { - entry = __pmd(pa); - entry = set_pmd_bit(entry, SEGMENT_KERNEL); - set_pmd(pmd, entry); + large_entry = __pmd(pa); + large_entry = set_pmd_bit(large_entry, SEGMENT_KERNEL); + set_pmd(pmd, large_entry); pages++; continue; } pte = boot_pte_alloc(); pmd_populate(&init_mm, pmd, pte); - } else if (pmd_leaf(*pmd)) { + } else if (pmd_leaf(entry)) { continue; } pgtable_pte_populate(pmd, addr, next, mode); @@ -387,26 +388,27 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e enum populate_mode mode) { unsigned long pa, next, pages = 0; - pud_t *pud, entry; + pud_t *pud, entry, large_entry; pmd_t *pmd; pud = pud_offset(p4d, addr); for (; addr < end; addr = next, pud++) { next = pud_addr_end(addr, end); - if (pud_none(*pud)) { + entry = pudp_get(pud); + if (pud_none(entry)) { if (kasan_pud_populate_zero_shadow(pud, addr, next, mode)) continue; pa = try_get_large_pud_pa(pud, addr, next, mode); if (pa != INVALID_PHYS_ADDR) { - entry = __pud(pa); - entry = set_pud_bit(entry, REGION3_KERNEL); - set_pud(pud, entry); + large_entry = __pud(pa); + large_entry = set_pud_bit(large_entry, REGION3_KERNEL); + set_pud(pud, large_entry); pages++; continue; } pmd = boot_crst_alloc(_SEGMENT_ENTRY_EMPTY); pud_populate(&init_mm, pud, pmd); - } else if (pud_leaf(*pud)) { + } else if (pud_leaf(entry)) { continue; } pgtable_pmd_populate(pud, addr, next, mode); @@ -425,7 +427,7 @@ static void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long e p4d = p4d_offset(pgd, addr); for (; addr < end; addr = next, p4d++) { next = p4d_addr_end(addr, end); - if (p4d_none(*p4d)) { + if (p4d_none(p4dp_get(p4d))) { if (kasan_p4d_populate_zero_shadow(p4d, addr, next, mode)) continue; pud = boot_crst_alloc(_REGION3_ENTRY_EMPTY); @@ -451,7 +453,7 @@ static void pgtable_populate(unsigned long addr, unsigned long end, enum populat pgd = pgd_offset(&init_mm, addr); for (; addr < end; addr = next, pgd++) { next = pgd_addr_end(addr, end); - if (pgd_none(*pgd)) { + if (pgd_none(pgdp_get(pgd))) { if (kasan_pgd_populate_zero_shadow(pgd, addr, next, mode)) continue; p4d = boot_crst_alloc(_REGION2_ENTRY_EMPTY); diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index 6983e52eaf81..e33a5b587ee4 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h @@ -41,7 +41,7 @@ static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep, unsigned long sz) { - if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) + if ((pte_val(ptep_get(ptep)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) set_pte(ptep, __pte(_REGION3_ENTRY_EMPTY)); else set_pte(ptep, __pte(_SEGMENT_ENTRY_EMPTY)); diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 3197b8b372a2..f9a8a92fa160 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -983,22 +983,39 @@ static inline void set_pte(pte_t *ptep, pte_t pte) WRITE_ONCE(*ptep, pte); } -static inline void pgd_clear(pgd_t *pgd) +#define ptep_get ptep_get +static inline pte_t ptep_get(pte_t *ptep) { - if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R1) - set_pgd(pgd, __pgd(_REGION1_ENTRY_EMPTY)); + return READ_ONCE(*ptep); } -static inline void p4d_clear(p4d_t *p4d) +#define pmdp_get pmdp_get +static inline pmd_t pmdp_get(pmd_t *pmdp) { - if ((p4d_val(*p4d) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2) - set_p4d(p4d, __p4d(_REGION2_ENTRY_EMPTY)); + return READ_ONCE(*pmdp); } -static inline void pud_clear(pud_t *pud) +#define pudp_get pudp_get +static inline pud_t pudp_get(pud_t *pudp) { - if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) - set_pud(pud, __pud(_REGION3_ENTRY_EMPTY)); + return READ_ONCE(*pudp); +} + +#define p4dp_get p4dp_get +static inline p4d_t p4dp_get(p4d_t *p4dp) +{ + return READ_ONCE(*p4dp); +} + +#define pgdp_get pgdp_get +static inline pgd_t pgdp_get(pgd_t *pgdp) +{ + return READ_ONCE(*pgdp); +} + +static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + set_pte(ptep, __pte(_PAGE_INVALID)); } static inline void pmd_clear(pmd_t *pmdp) @@ -1006,9 +1023,22 @@ static inline void pmd_clear(pmd_t *pmdp) set_pmd(pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); } -static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +static inline void pud_clear(pud_t *pud) { - set_pte(ptep, __pte(_PAGE_INVALID)); + if ((pud_val(pudp_get(pud)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) + set_pud(pud, __pud(_REGION3_ENTRY_EMPTY)); +} + +static inline void p4d_clear(p4d_t *p4d) +{ + if ((p4d_val(p4dp_get(p4d)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2) + set_p4d(p4d, __p4d(_REGION2_ENTRY_EMPTY)); +} + +static inline void pgd_clear(pgd_t *pgd) +{ + if ((pgd_val(pgdp_get(pgd)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R1) + set_pgd(pgd, __pgd(_REGION1_ENTRY_EMPTY)); } /* @@ -1169,7 +1199,7 @@ pte_t ptep_xchg_lazy(struct mm_struct *, unsigned long, pte_t *, pte_t); static inline bool ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { - pte_t pte = *ptep; + pte_t pte = ptep_get(ptep); pte = ptep_xchg_direct(vma->vm_mm, addr, ptep, pte_mkold(pte)); return pte_young(pte); @@ -1230,7 +1260,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, pte_t res; if (full) { - res = *ptep; + res = ptep_get(ptep); set_pte(ptep, __pte(_PAGE_INVALID)); } else { res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); @@ -1259,7 +1289,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - pte_t pte = *ptep; + pte_t pte = ptep_get(ptep); if (pte_write(pte)) ptep_xchg_lazy(mm, addr, ptep, pte_wrprotect(pte)); @@ -1295,7 +1325,7 @@ static inline void flush_tlb_fix_spurious_fault(struct vm_area_struct *vma, * PTE does not have _PAGE_PROTECT set, to avoid unnecessary overhead. * A local RDP can be used to do the flush. */ - if (cpu_has_rdp() && !(pte_val(*ptep) & _PAGE_PROTECT)) + if (cpu_has_rdp() && !(pte_val(ptep_get(ptep)) & _PAGE_PROTECT)) __ptep_rdp(address, ptep, 1); } #define flush_tlb_fix_spurious_fault flush_tlb_fix_spurious_fault diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 302ef5781b65..db35d8fe8609 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -143,7 +143,7 @@ void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, rste = __pte_to_rste(pte); /* Set correct table type for 2G hugepages */ - if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) { + if ((pte_val(ptep_get(ptep)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) { if (likely(pte_present(pte))) rste |= _REGION3_ENTRY_LARGE; rste |= _REGION_ENTRY_TYPE_R3; @@ -161,7 +161,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - return __rste_to_pte(pte_val(*ptep)); + return __rste_to_pte(pte_val(ptep_get(ptep))); } pte_t __huge_ptep_get_and_clear(struct mm_struct *mm, @@ -171,7 +171,7 @@ pte_t __huge_ptep_get_and_clear(struct mm_struct *mm, pmd_t *pmdp = (pmd_t *) ptep; pud_t *pudp = (pud_t *) ptep; - if ((pte_val(*ptep) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) + if ((pte_val(ptep_get(ptep)) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) pudp_xchg_direct(mm, addr, pudp, __pud(_REGION3_ENTRY_EMPTY)); else pmdp_xchg_direct(mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); @@ -209,13 +209,13 @@ pte_t *huge_pte_offset(struct mm_struct *mm, pmd_t *pmdp = NULL; pgdp = pgd_offset(mm, addr); - if (pgd_present(*pgdp)) { + if (pgd_present(pgdp_get(pgdp))) { p4dp = p4d_offset(pgdp, addr); - if (p4d_present(*p4dp)) { + if (p4d_present(p4dp_get(p4dp))) { pudp = pud_offset(p4dp, addr); if (sz == PUD_SIZE) return (pte_t *)pudp; - if (pud_present(*pudp)) + if (pud_present(pudp_get(pudp))) pmdp = pmd_offset(pudp, addr); } } diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index bb29c38ae624..e6f788696dd1 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -85,7 +85,7 @@ static int walk_pte_level(pmd_t *pmdp, unsigned long addr, unsigned long end, return 0; ptep = pte_offset_kernel(pmdp, addr); do { - new = *ptep; + new = ptep_get(ptep); if (pte_none(new)) return -EINVAL; if (flags & SET_MEMORY_RO) @@ -114,15 +114,16 @@ static int split_pmd_page(pmd_t *pmdp, unsigned long addr) { unsigned long pte_addr, prot; pte_t *pt_dir, *ptep; - pmd_t new; + pmd_t new, pmd; int i, ro, nx; pt_dir = vmem_pte_alloc(); if (!pt_dir) return -ENOMEM; - pte_addr = pmd_pfn(*pmdp) << PAGE_SHIFT; - ro = !!(pmd_val(*pmdp) & _SEGMENT_ENTRY_PROTECT); - nx = !!(pmd_val(*pmdp) & _SEGMENT_ENTRY_NOEXEC); + pmd = pmdp_get(pmdp); + pte_addr = pmd_pfn(pmd) << PAGE_SHIFT; + ro = !!(pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT); + nx = !!(pmd_val(pmd) & _SEGMENT_ENTRY_NOEXEC); prot = pgprot_val(ro ? PAGE_KERNEL_RO : PAGE_KERNEL); if (!nx) prot &= ~_PAGE_NOEXEC; @@ -142,7 +143,7 @@ static int split_pmd_page(pmd_t *pmdp, unsigned long addr) static void modify_pmd_page(pmd_t *pmdp, unsigned long addr, unsigned long flags) { - pmd_t new = *pmdp; + pmd_t new = pmdp_get(pmdp); if (flags & SET_MEMORY_RO) new = pmd_wrprotect(new); @@ -165,16 +166,17 @@ static int walk_pmd_level(pud_t *pudp, unsigned long addr, unsigned long end, unsigned long flags) { unsigned long next; + pmd_t *pmdp, pmd; int need_split; - pmd_t *pmdp; int rc = 0; pmdp = pmd_offset(pudp, addr); do { - if (pmd_none(*pmdp)) + pmd = pmdp_get(pmdp); + if (pmd_none(pmd)) return -EINVAL; next = pmd_addr_end(addr, end); - if (pmd_leaf(*pmdp)) { + if (pmd_leaf(pmd)) { need_split = !!(flags & SET_MEMORY_4K); need_split |= !!(addr & ~PMD_MASK); need_split |= !!(addr + PMD_SIZE > next); @@ -201,15 +203,16 @@ int split_pud_page(pud_t *pudp, unsigned long addr) { unsigned long pmd_addr, prot; pmd_t *pm_dir, *pmdp; - pud_t new; + pud_t new, pud; int i, ro, nx; pm_dir = vmem_crst_alloc(_SEGMENT_ENTRY_EMPTY); if (!pm_dir) return -ENOMEM; - pmd_addr = pud_pfn(*pudp) << PAGE_SHIFT; - ro = !!(pud_val(*pudp) & _REGION_ENTRY_PROTECT); - nx = !!(pud_val(*pudp) & _REGION_ENTRY_NOEXEC); + pud = pudp_get(pudp); + pmd_addr = pud_pfn(pud) << PAGE_SHIFT; + ro = !!(pud_val(pud) & _REGION_ENTRY_PROTECT); + nx = !!(pud_val(pud) & _REGION_ENTRY_NOEXEC); prot = pgprot_val(ro ? SEGMENT_KERNEL_RO : SEGMENT_KERNEL); if (!nx) prot &= ~_SEGMENT_ENTRY_NOEXEC; @@ -229,7 +232,7 @@ int split_pud_page(pud_t *pudp, unsigned long addr) static void modify_pud_page(pud_t *pudp, unsigned long addr, unsigned long flags) { - pud_t new = *pudp; + pud_t new = pudp_get(pudp); if (flags & SET_MEMORY_RO) new = pud_wrprotect(new); @@ -252,16 +255,17 @@ static int walk_pud_level(p4d_t *p4d, unsigned long addr, unsigned long end, unsigned long flags) { unsigned long next; + pud_t *pudp, pud; int need_split; - pud_t *pudp; int rc = 0; pudp = pud_offset(p4d, addr); do { - if (pud_none(*pudp)) + pud = pudp_get(pudp); + if (pud_none(pud)) return -EINVAL; next = pud_addr_end(addr, end); - if (pud_leaf(*pudp)) { + if (pud_leaf(pud)) { need_split = !!(flags & SET_MEMORY_4K); need_split |= !!(addr & ~PUD_MASK); need_split |= !!(addr + PUD_SIZE > next); @@ -291,7 +295,7 @@ static int walk_p4d_level(pgd_t *pgd, unsigned long addr, unsigned long end, p4dp = p4d_offset(pgd, addr); do { - if (p4d_none(*p4dp)) + if (p4d_none(p4dp_get(p4dp))) return -EINVAL; next = p4d_addr_end(addr, end); rc = walk_pud_level(p4dp, addr, next, flags); @@ -313,7 +317,7 @@ static int change_page_attr(unsigned long addr, unsigned long end, pgdp = pgd_offset_k(addr); do { - if (pgd_none(*pgdp)) + if (pgd_none(pgdp_get(pgdp))) break; next = pgd_addr_end(addr, end); rc = walk_p4d_level(pgdp, addr, next, flags); @@ -451,7 +455,8 @@ void __kernel_map_pages(struct page *page, int numpages, int enable) nr = min(numpages - i, nr); if (enable) { for (j = 0; j < nr; j++) { - pte = clear_pte_bit(*ptep, __pgprot(_PAGE_INVALID)); + pte = ptep_get(ptep); + pte = clear_pte_bit(pte, __pgprot(_PAGE_INVALID)); set_pte(ptep, pte); address += PAGE_SIZE; ptep++; diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index eeadff45e0e1..803099f3db73 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -171,18 +171,19 @@ static int __ref modify_pte_table(pmd_t *pmd, unsigned long addr, { unsigned long prot, pages = 0; int ret = -ENOMEM; - pte_t *pte; + pte_t *pte, entry; prot = pgprot_val(PAGE_KERNEL); pte = pte_offset_kernel(pmd, addr); for (; addr < end; addr += PAGE_SIZE, pte++) { + entry = ptep_get(pte); if (!add) { - if (pte_none(*pte)) + if (pte_none(entry)) continue; if (!direct) - vmem_free_pages((unsigned long)pfn_to_virt(pte_pfn(*pte)), get_order(PAGE_SIZE), altmap); + vmem_free_pages((unsigned long)pfn_to_virt(pte_pfn(entry)), get_order(PAGE_SIZE), altmap); pte_clear(&init_mm, addr, pte); - } else if (pte_none(*pte)) { + } else if (pte_none(entry)) { if (!direct) { void *new_page = vmemmap_alloc_block_buf(PAGE_SIZE, NUMA_NO_NODE, altmap); @@ -212,10 +213,10 @@ static void try_free_pte_table(pmd_t *pmd, unsigned long start) /* We can safely assume this is fully in 1:1 mapping & vmemmap area */ pte = pte_offset_kernel(pmd, start); for (i = 0; i < PTRS_PER_PTE; i++, pte++) { - if (!pte_none(*pte)) + if (!pte_none(ptep_get(pte))) return; } - vmem_pte_free((unsigned long *) pmd_deref(*pmd)); + vmem_pte_free((unsigned long *)pmd_deref(pmdp_get(pmd))); pmd_clear(pmd); } @@ -226,6 +227,7 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr, { unsigned long next, prot, pages = 0; int ret = -ENOMEM; + pmd_t entry; pmd_t *pmd; pte_t *pte; @@ -233,23 +235,24 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr, pmd = pmd_offset(pud, addr); for (; addr < end; addr = next, pmd++) { next = pmd_addr_end(addr, end); + entry = pmdp_get(pmd); if (!add) { - if (pmd_none(*pmd)) + if (pmd_none(entry)) continue; - if (pmd_leaf(*pmd)) { + if (pmd_leaf(entry)) { if (IS_ALIGNED(addr, PMD_SIZE) && IS_ALIGNED(next, PMD_SIZE)) { if (!direct) - vmem_free_pages(pmd_deref(*pmd), get_order(PMD_SIZE), altmap); + vmem_free_pages(pmd_deref(entry), get_order(PMD_SIZE), altmap); pmd_clear(pmd); pages++; } else if (!direct && vmemmap_unuse_sub_pmd(addr, next)) { - vmem_free_pages(pmd_deref(*pmd), get_order(PMD_SIZE), altmap); + vmem_free_pages(pmd_deref(entry), get_order(PMD_SIZE), altmap); pmd_clear(pmd); } continue; } - } else if (pmd_none(*pmd)) { + } else if (pmd_none(entry)) { if (IS_ALIGNED(addr, PMD_SIZE) && IS_ALIGNED(next, PMD_SIZE) && cpu_has_edat1() && direct && @@ -281,7 +284,7 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr, if (!pte) goto out; pmd_populate(&init_mm, pmd, pte); - } else if (pmd_leaf(*pmd)) { + } else if (pmd_leaf(entry)) { if (!direct) vmemmap_use_sub_pmd(addr, next); continue; @@ -306,9 +309,9 @@ static void try_free_pmd_table(pud_t *pud, unsigned long start) pmd = pmd_offset(pud, start); for (i = 0; i < PTRS_PER_PMD; i++, pmd++) - if (!pmd_none(*pmd)) + if (!pmd_none(pmdp_get(pmd))) return; - vmem_free_pages(pud_deref(*pud), CRST_ALLOC_ORDER, NULL); + vmem_free_pages(pud_deref(pudp_get(pud)), CRST_ALLOC_ORDER, NULL); pud_clear(pud); } @@ -317,21 +320,22 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, { unsigned long next, prot, pages = 0; int ret = -ENOMEM; - pud_t *pud; + pud_t *pud, entry; pmd_t *pmd; prot = pgprot_val(REGION3_KERNEL); pud = pud_offset(p4d, addr); for (; addr < end; addr = next, pud++) { next = pud_addr_end(addr, end); + entry = pudp_get(pud); if (!add) { - if (pud_none(*pud)) + if (pud_none(entry)) continue; - if (pud_leaf(*pud)) { + if (pud_leaf(entry)) { if (IS_ALIGNED(addr, PUD_SIZE) && IS_ALIGNED(next, PUD_SIZE)) { if (!direct) - vmem_free_pages(pud_deref(*pud), get_order(PUD_SIZE), altmap); + vmem_free_pages(pud_deref(entry), get_order(PUD_SIZE), altmap); pud_clear(pud); pages++; continue; @@ -339,7 +343,7 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, split_pud_page(pud, addr & PUD_MASK); } } - } else if (pud_none(*pud)) { + } else if (pud_none(entry)) { if (IS_ALIGNED(addr, PUD_SIZE) && IS_ALIGNED(next, PUD_SIZE) && cpu_has_edat2() && direct && @@ -352,7 +356,7 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, if (!pmd) goto out; pud_populate(&init_mm, pud, pmd); - } else if (pud_leaf(*pud)) { + } else if (pud_leaf(entry)) { continue; } ret = modify_pmd_table(pud, addr, next, add, direct, altmap); @@ -375,10 +379,10 @@ static void try_free_pud_table(p4d_t *p4d, unsigned long start) pud = pud_offset(p4d, start); for (i = 0; i < PTRS_PER_PUD; i++, pud++) { - if (!pud_none(*pud)) + if (!pud_none(pudp_get(pud))) return; } - vmem_free_pages(p4d_deref(*p4d), CRST_ALLOC_ORDER, NULL); + vmem_free_pages(p4d_deref(p4dp_get(p4d)), CRST_ALLOC_ORDER, NULL); p4d_clear(p4d); } @@ -387,16 +391,17 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end, { unsigned long next; int ret = -ENOMEM; - p4d_t *p4d; + p4d_t *p4d, entry; pud_t *pud; p4d = p4d_offset(pgd, addr); for (; addr < end; addr = next, p4d++) { next = p4d_addr_end(addr, end); + entry = p4dp_get(p4d); if (!add) { - if (p4d_none(*p4d)) + if (p4d_none(entry)) continue; - } else if (p4d_none(*p4d)) { + } else if (p4d_none(entry)) { pud = vmem_crst_alloc(_REGION3_ENTRY_EMPTY); if (!pud) goto out; @@ -420,10 +425,10 @@ static void try_free_p4d_table(pgd_t *pgd, unsigned long start) p4d = p4d_offset(pgd, start); for (i = 0; i < PTRS_PER_P4D; i++, p4d++) { - if (!p4d_none(*p4d)) + if (!p4d_none(p4dp_get(p4d))) return; } - vmem_free_pages(pgd_deref(*pgd), CRST_ALLOC_ORDER, NULL); + vmem_free_pages(pgd_deref(pgdp_get(pgd)), CRST_ALLOC_ORDER, NULL); pgd_clear(pgd); } @@ -432,7 +437,7 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add, { unsigned long addr, next; int ret = -ENOMEM; - pgd_t *pgd; + pgd_t *pgd, entry; p4d_t *p4d; if (WARN_ON_ONCE(!PAGE_ALIGNED(start | end))) @@ -449,11 +454,12 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add, for (addr = start; addr < end; addr = next) { next = pgd_addr_end(addr, end); pgd = pgd_offset_k(addr); + entry = pgdp_get(pgd); if (!add) { - if (pgd_none(*pgd)) + if (pgd_none(entry)) continue; - } else if (pgd_none(*pgd)) { + } else if (pgd_none(entry)) { p4d = vmem_crst_alloc(_REGION2_ENTRY_EMPTY); if (!p4d) goto out; @@ -575,6 +581,8 @@ int vmem_add_mapping(unsigned long start, unsigned long size) pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc) { pte_t *ptep = NULL; + pud_t pud_entry; + pmd_t pmd_entry; pgd_t *pgd; p4d_t *p4d; pud_t *pud; @@ -582,7 +590,7 @@ pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc) pte_t *pte; pgd = pgd_offset_k(addr); - if (pgd_none(*pgd)) { + if (pgd_none(pgdp_get(pgd))) { if (!alloc) goto out; p4d = vmem_crst_alloc(_REGION2_ENTRY_EMPTY); @@ -591,7 +599,7 @@ pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc) pgd_populate(&init_mm, pgd, p4d); } p4d = p4d_offset(pgd, addr); - if (p4d_none(*p4d)) { + if (p4d_none(p4dp_get(p4d))) { if (!alloc) goto out; pud = vmem_crst_alloc(_REGION3_ENTRY_EMPTY); @@ -600,25 +608,27 @@ pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc) p4d_populate(&init_mm, p4d, pud); } pud = pud_offset(p4d, addr); - if (pud_none(*pud)) { + pud_entry = pudp_get(pud); + if (pud_none(pud_entry)) { if (!alloc) goto out; pmd = vmem_crst_alloc(_SEGMENT_ENTRY_EMPTY); if (!pmd) goto out; pud_populate(&init_mm, pud, pmd); - } else if (WARN_ON_ONCE(pud_leaf(*pud))) { + } else if (WARN_ON_ONCE(pud_leaf(pud_entry))) { goto out; } pmd = pmd_offset(pud, addr); - if (pmd_none(*pmd)) { + pmd_entry = pmdp_get(pmd); + if (pmd_none(pmd_entry)) { if (!alloc) goto out; pte = vmem_pte_alloc(); if (!pte) goto out; pmd_populate(&init_mm, pmd, pte); - } else if (WARN_ON_ONCE(pmd_leaf(*pmd))) { + } else if (WARN_ON_ONCE(pmd_leaf(pmd_entry))) { goto out; } ptep = pte_offset_kernel(pmd, addr); From ca4aa97194ae353c2882d7cb4ed123a544892bcf Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 15 Jun 2026 13:43:16 -0600 Subject: [PATCH 3791/5214] io_uring: get rid of tw_pending for !DEFER task work The normal task_work path used a tw_pending bit to ensure the callback was only added once: the mpscq drains incrementally, so a single tctx_task_work() run can take the queue through empty -> non-empty several times, and each transition would otherwise re-add the already pending callback_head. This corrupts the task_work list, and is what tw_pending protects again. This can go away, if we stop running the task_work as soon as the queue empties. Suggested-by: Caleb Sander Mateos Reviewed-by: Caleb Sander Mateos Signed-off-by: Jens Axboe --- include/linux/io_uring_types.h | 2 -- io_uring/mpscq.h | 9 +++++++++ io_uring/tw.c | 19 ++++++++----------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index 6415a3353ee0..87151a5b62c1 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -149,8 +149,6 @@ struct io_uring_task { struct { /* task_work */ struct mpscq task_list; - /* BIT(0) guards adding tw only once */ - unsigned long tw_pending; struct callback_head task_work; } ____cacheline_aligned_in_smp; }; diff --git a/io_uring/mpscq.h b/io_uring/mpscq.h index c801384c6a0a..f910526766fd 100644 --- a/io_uring/mpscq.h +++ b/io_uring/mpscq.h @@ -122,4 +122,13 @@ static inline struct llist_node *mpscq_pop(struct mpscq *q, return NULL; } +/* + * Returns true if the most recent mpscq_pop() that returned a node also + * emptied the queue. Consumer must be serialized. + */ +static inline bool mpscq_pop_emptied(struct mpscq *q, struct llist_node *head) +{ + return head == &q->stub; +} + #endif /* IOU_MPSCQ_H */ diff --git a/io_uring/tw.c b/io_uring/tw.c index e74372233f40..f2ce806b01a1 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -34,10 +34,6 @@ void io_tctx_fallback_work(struct work_struct *work) fallback_work); unsigned int count = 0; - /* see tctx_task_work() - a set bit must always have a run coming */ - clear_bit(0, &tctx->tw_pending); - smp_mb__after_atomic(); - /* * Run the entries directly. We're in PF_KTHRED context, hence * io_should_terminate_tw() is true and they will be marked as @@ -101,6 +97,13 @@ void tctx_task_work_run(struct io_uring_task *tctx, unsigned int max_entries, io_poll_task_func, io_req_rw_complete, (struct io_tw_req){req}, ts); (*count)++; + /* + * Break if most recent pop emptied the queue. This helps + * bound task_work run, and also protects the regular + * task_work addition. + */ + if (mpscq_pop_emptied(&tctx->task_list, tctx->task_head)) + break; if (unlikely(need_resched())) { ctx_flush_and_put(ctx, ts); ctx = NULL; @@ -127,8 +130,6 @@ void tctx_task_work(struct callback_head *cb) unsigned int count = 0; tctx = container_of(cb, struct io_uring_task, task_work); - clear_bit(0, &tctx->tw_pending); - smp_mb__after_atomic(); tctx_task_work_run(tctx, UINT_MAX, &count); } @@ -206,7 +207,7 @@ void io_req_normal_work_add(struct io_kiocb *req) struct io_uring_task *tctx = req->tctx; struct io_ring_ctx *ctx = req->ctx; - /* task_work already pending, we're done */ + /* tw run already pending, nothing else to do */ if (!mpscq_push(&tctx->task_list, &req->io_task_work.node)) return; @@ -223,10 +224,6 @@ void io_req_normal_work_add(struct io_kiocb *req) return; } - /* task_work must only be added once */ - if (test_and_set_bit(0, &tctx->tw_pending)) - return; - if (likely(!task_work_add(tctx->task, &tctx->task_work, ctx->notify_method))) return; From bdc2fc388c348ee14b4f984ff75f2ea440cefd44 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Tue, 16 Jun 2026 09:36:32 -0300 Subject: [PATCH 3792/5214] io_uring, audit: don't log IORING_OP_RECV_ZC IORING_OP_RECV_ZC is a read operation. Audit only tracks file/socket creation, not subsequent reads. Set audit_skip to align with audit-userspace uringop_table.h. Fixes: 11ed914bbf94 ("io_uring/zcrx: add io_recvzc request") Suggested-by: Steve Grubb Signed-off-by: Ricardo Robaina Acked-by: Paul Moore Link: https://patch.msgid.link/20260616123632.3209545-1-rrobaina@redhat.com Signed-off-by: Jens Axboe --- io_uring/opdef.c | 1 + 1 file changed, 1 insertion(+) diff --git a/io_uring/opdef.c b/io_uring/opdef.c index 88a45c7d897f..4e58eb1344ea 100644 --- a/io_uring/opdef.c +++ b/io_uring/opdef.c @@ -520,6 +520,7 @@ const struct io_issue_def io_issue_defs[] = { #endif }, [IORING_OP_RECV_ZC] = { + .audit_skip = 1, .needs_file = 1, .unbound_nonreg_file = 1, .pollin = 1, From 4f919141be38ea2b1314e3a531b7b998eb64e8bc Mon Sep 17 00:00:00 2001 From: Yitang Yang Date: Tue, 16 Jun 2026 23:51:29 +0800 Subject: [PATCH 3793/5214] block: fix IORING_URING_CMD_REISSUE flags check in blkdev_uring_cmd blkdev_uring_cmd() checks IORING_URING_CMD_REISSUE to determine whether this is the first issue. However, this flag lives in cmd->flags instead of issue_flags. Coincidentally, IO_URING_F_NONBLOCK shares bit 31 with IORING_URING_CMD_REISSUE. As a result, the SQE read was never performed, bic->len remained zero, and every BLOCK_URING_CMD_DISCARD failed with -EINVAL. Fix it by checking cmd->flags as intended. Cc: stable@vger.kernel.org Fixes: 212ec34e4e72 ("block: only read from sqe on initial invocation of blkdev_uring_cmd") Signed-off-by: Yitang Yang Link: https://patch.msgid.link/20260616155129.406057-1-yi1tang.yang@gmail.com Signed-off-by: Jens Axboe --- block/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/ioctl.c b/block/ioctl.c index ab2c9ed79946..3d4ea1537457 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -951,7 +951,7 @@ int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) u32 cmd_op = cmd->cmd_op; /* Read what we need from the SQE on the first issue */ - if (!(issue_flags & IORING_URING_CMD_REISSUE)) { + if (!(cmd->flags & IORING_URING_CMD_REISSUE)) { const struct io_uring_sqe *sqe = cmd->sqe; if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || From 9cbbac29d752fb5d95e375fa3685a359b89caa0a Mon Sep 17 00:00:00 2001 From: Wen Xiong Date: Tue, 16 Jun 2026 10:31:21 -0400 Subject: [PATCH 3794/5214] block: Remove redundant plug in __submit_bio() The patch removes the automatic plug/unplug operations from __submit_bio() that were added to cache nsecs time when no explicit plug is used. The plug mechanism is most effective when batching multiple I/O operations together. Creating a plug for every bio submission provides minimal benefit while adding function call overhead and stack usage for every I/O operation. Below is performance comparison with the latest upstream kernel. Iotype qd nj rmix mpstat busy mpstat busy without plug Randrw 1 20 100 53% 24% Randrw 1 40 100 70% 24% Randrw 1 20 70 40% 24% Randrw 1 40 70 60% 26% Randrw 1 20 0 14% 6% Randrw 1 40 0 20% 7% Fixes: 060406c61c7c ("block: add plug while submitting IO") Signed-off-by: Wen Xiong Reviewed-by: Ming Lei Link: https://patch.msgid.link/20260616143121.878021-1-wenxiong@linux.ibm.com Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 73a41df98c9a..365641266c9e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -669,11 +669,6 @@ static inline blk_status_t blk_check_zone_append(struct request_queue *q, static void __submit_bio(struct bio *bio) { - /* If plug is not used, add new plug here to cache nsecs time. */ - struct blk_plug plug; - - blk_start_plug(&plug); - if (!bdev_test_flag(bio->bi_bdev, BD_HAS_SUBMIT_BIO)) { blk_mq_submit_bio(bio); } else if (likely(bio_queue_enter(bio) == 0)) { @@ -686,8 +681,6 @@ static void __submit_bio(struct bio *bio) disk->fops->submit_bio(bio); blk_queue_exit(disk->queue); } - - blk_finish_plug(&plug); } /* From 1fe703cc708f19209ae8e6261247483db723c221 Mon Sep 17 00:00:00 2001 From: guzebing Date: Mon, 8 Jun 2026 21:33:16 +0800 Subject: [PATCH 3795/5214] io_uring/register: preserve SQ array entries on resize Ring resizing copies pending SQEs from the old SQE array into the new one so submissions queued before the resize can still be consumed afterwards. That copy currently walks the SQ head/tail range directly. This is only correct when there is no SQ array indirection. With a regular SQ array, each pending SQ entry contains an index into the SQE array. After resize, ctx->sq_array is repointed at the newly allocated array, so pending entries lose their old logical-to-physical mapping and may submit the wrong SQE. Remember the old and new SQ arrays while migrating pending SQ entries. For each pending entry, copy the SQE selected by the old array into the new destination slot and rebuild the new array entry to point at the copied SQE. Keep invalid user-provided entries invalid so the normal submission path still drops them after resize. Fixes: 79cfe9e59c2a1 ("io_uring/register: add IORING_REGISTER_RESIZE_RINGS") Signed-off-by: guzebing Link: https://patch.msgid.link/20260608133316.3656440-1-guzebing1612@gmail.com Signed-off-by: Jens Axboe --- io_uring/register.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/io_uring/register.c b/io_uring/register.c index dce5e2f9cf77..02bc103bcc9d 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -503,6 +503,7 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) unsigned i, tail, old_head; struct io_uring_params *p = &config.p; struct io_rings_layout *rl = &config.layout; + u32 *o_sq_array, *n_sq_array = NULL; int ret; memset(&config, 0, sizeof(config)); @@ -589,6 +590,9 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) ctx->rings = NULL; o.sq_sqes = ctx->sq_sqes; ctx->sq_sqes = NULL; + o_sq_array = ctx->sq_array; + if (!(ctx->flags & IORING_SETUP_NO_SQARRAY)) + n_sq_array = (u32 *)((char *)n.rings + rl->sq_array_offset); /* * Now copy SQ and CQ entries, if any. If either of the destination @@ -599,20 +603,27 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) if (tail - old_head > p->sq_entries) goto overflow; for (i = old_head; i < tail; i++) { - unsigned index, dst_mask, src_mask; + unsigned int dst, src; size_t sq_size; - index = i; - sq_size = sizeof(struct io_uring_sqe); - src_mask = ctx->sq_entries - 1; - dst_mask = p->sq_entries - 1; - if (ctx->flags & IORING_SETUP_SQE128) { - index <<= 1; - sq_size <<= 1; - src_mask = (ctx->sq_entries << 1) - 1; - dst_mask = (p->sq_entries << 1) - 1; + dst = i & (p->sq_entries - 1); + src = i & (ctx->sq_entries - 1); + if (n_sq_array) { + src = READ_ONCE(o_sq_array[src]); + if (unlikely(src >= ctx->sq_entries)) { + WRITE_ONCE(n_sq_array[dst], UINT_MAX); + continue; + } + WRITE_ONCE(n_sq_array[dst], dst); } - memcpy(&n.sq_sqes[index & dst_mask], &o.sq_sqes[index & src_mask], sq_size); + + sq_size = sizeof(struct io_uring_sqe); + if (ctx->flags & IORING_SETUP_SQE128) { + dst <<= 1; + src <<= 1; + sq_size <<= 1; + } + memcpy(&n.sq_sqes[dst], &o.sq_sqes[src], sq_size); } WRITE_ONCE(n.rings->sq.head, old_head); WRITE_ONCE(n.rings->sq.tail, tail); @@ -655,8 +666,8 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) WRITE_ONCE(n.rings->cq_overflow, READ_ONCE(o.rings->cq_overflow)); /* all done, store old pointers and assign new ones */ - if (!(ctx->flags & IORING_SETUP_NO_SQARRAY)) - ctx->sq_array = (u32 *)((char *)n.rings + rl->sq_array_offset); + if (n_sq_array) + ctx->sq_array = n_sq_array; ctx->sq_entries = p->sq_entries; ctx->cq_entries = p->cq_entries; From fd38b75c4b43295b10d69772a46d1c74dbd6fc81 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:17 -0700 Subject: [PATCH 3796/5214] kernel/fork: clear PF_BLOCK_TS in copy_process() PF_BLOCK_TS is only set in blk_time_get_ns() when current->plug is non-NULL, and blk_finish_plug() clears it via __blk_flush_plug() before NULLing the plug pointer. copy_process() breaks the invariant by inheriting PF_BLOCK_TS from the parent while resetting the child's plug to NULL. Clear PF_BLOCK_TS alongside that assignment so callers can rely on "PF_BLOCK_TS set implies current->plug != NULL" and dereference current->plug unguarded. Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-2-usama.arif@linux.dev Signed-off-by: Jens Axboe --- kernel/fork.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/fork.c b/kernel/fork.c index addc555a1077..1fafcb9bb047 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2337,6 +2337,7 @@ __latent_entropy struct task_struct *copy_process( #ifdef CONFIG_BLOCK p->plug = NULL; + p->flags &= ~PF_BLOCK_TS; #endif futex_init_task(p); From fad156c2af227f42ca796cbb20ddc354a6dd9932 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:18 -0700 Subject: [PATCH 3797/5214] block: invalidate cached plug timestamp after task switch blk_time_get_ns() caches ktime_get_ns() in current->plug->cur_ktime and marks the task with PF_BLOCK_TS. That cache is only valid while the task keeps running; if the task is switched out, wall-clock time advances and the cached value must not be reused when the task runs again. The existing invalidation covers explicit plug flushes through __blk_flush_plug(), and the schedule() / rtmutex paths through sched_update_worker(). It does not cover in-kernel preemption paths such as preempt_schedule(), preempt_schedule_notrace(), and preempt_schedule_irq(), which enter __schedule(SM_PREEMPT) directly and return without calling sched_update_worker(). As a result, a task preempted while holding a plug with PF_BLOCK_TS set can reuse a stale plug->cur_ktime after it is scheduled back in. blk-iocost then consumes that stale timestamp through ioc_now(), producing stale vnow values for throttle decisions, and through ioc_rqos_done(), inflating on-queue time and feeding false missed-QoS samples into vrate adjustment. Move the schedule-side invalidation to finish_task_switch(), which runs for the scheduled-in task after every actual context switch regardless of which schedule entry point was used. Keep __blk_flush_plug() as the explicit flush/finish-plug invalidation path, and remove only the PF_BLOCK_TS handling from sched_update_worker(). Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-3-usama.arif@linux.dev Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 16 ++++++---------- kernel/sched/core.c | 12 ++++++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 5070851cf924..9213a5716f95 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1222,16 +1222,12 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) __blk_flush_plug(plug, async); } -/* - * tsk == current here - */ -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static __always_inline void blk_plug_invalidate_ts(void) { - struct blk_plug *plug = tsk->plug; - - if (plug) - plug->cur_ktime = 0; - current->flags &= ~PF_BLOCK_TS; + if (unlikely(current->flags & PF_BLOCK_TS)) { + current->plug->cur_ktime = 0; + current->flags &= ~PF_BLOCK_TS; + } } int blkdev_issue_flush(struct block_device *bdev); @@ -1257,7 +1253,7 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) { } -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static inline void blk_plug_invalidate_ts(void) { } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 8b791e9e9f67..e97e98c33be5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5368,6 +5368,12 @@ static struct rq *finish_task_switch(struct task_struct *prev) */ kmap_local_sched_in(); + /* + * Any cached block-layer timestamp (plug->cur_ktime) is stale now, + * invalidate it. + */ + blk_plug_invalidate_ts(); + fire_sched_in_preempt_notifiers(current); /* * When switching through a kernel thread, the loop in @@ -7290,12 +7296,10 @@ static inline void sched_submit_work(struct task_struct *tsk) static void sched_update_worker(struct task_struct *tsk) { - if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER | PF_BLOCK_TS)) { - if (tsk->flags & PF_BLOCK_TS) - blk_plug_invalidate_ts(tsk); + if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER)) { if (tsk->flags & PF_WQ_WORKER) wq_worker_running(tsk); - else if (tsk->flags & PF_IO_WORKER) + else io_wq_worker_running(tsk); } } From 5ce9ac1531b8e27e754a7d17fa07fa9da0d4a6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 9 Apr 2026 13:08:40 +0300 Subject: [PATCH 3798/5214] drm/i915/mst: Call intel_pfit_compute_config() for sharpness filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sharpness filter property is on the CRTC (as opposed to the connector) so the expectation is that it's usable on all output types. Since the sharpness filter is now fully integrateds into the normal pfit code intel_pfit_compute_config() must be called from the encoder .compute_config() on all relevant output types. Sharpness filter is supported on LNL+ so only HDMI and DP SST/MST outputs are actually relevant. I already took care of HDMI and DP SST, but (as usual) forgot about DP MST. Add the missing intel_pfit_compute_config() call to make the sharpness filter operational on DP MST as well. Cc: Nemesa Garg Fixes: d4686f34bbeb ("drm/i915/pfit: Call intel_pfit_compute_config() unconditionally on (e)DP/HDMI") Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260409100841.1907-1-ville.syrjala@linux.intel.com Reviewed-by: Nemesa Garg (cherry picked from commit ca97f5546f191bf460b3f4b59ade3ea5e7378796) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index bcdc50491347..0aa3e6b4c781 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -722,6 +722,10 @@ static int mst_stream_compute_config(struct intel_encoder *encoder, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; + ret = intel_pfit_compute_config(pipe_config, conn_state); + if (ret) + return ret; + ret = intel_pfit_compute_config(pipe_config, conn_state); if (ret) return ret; From 31f077088e0faae6be8377741f356dea1b94ba46 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:53:10 +0800 Subject: [PATCH 3799/5214] drm/i915: clear CRTC color blob pointers after dropping refs intel_crtc_put_color_blobs() drops the CRTC color blob references, but leaves the corresponding pointers unchanged. This can matter in intel_crtc_prepare_cleared_state(), which frees the old CRTC hw state before calling intel_dp_tunnel_atomic_clear_stream_bw(). The latter can fail while looking up the DP tunnel group state, for example with -EDEADLK. If that happens, the function returns without completing the cleared state preparation. The failed atomic state will then be cleared by the atomic core and intel_crtc_free_hw_state() can be called again for the same state, dropping the same blob references again. Clear the blob pointers after dropping the references so repeated cleanup of the same CRTC hw state is safe. Fixes: 77fcf58df15e ("drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state") Suggested-by: Imre Deak Signed-off-by: Guangshuo Li Reviewed-by: Imre Deak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612035310.3013066-1-lgs201920130244@gmail.com (cherry picked from commit d5005addb5f68e8a0edce249506757bdc9e3d8c8) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 0e4f0678c53c..9d0d47c79dd1 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -288,6 +288,12 @@ static void intel_crtc_put_color_blobs(struct intel_crtc_state *crtc_state) drm_property_blob_put(crtc_state->pre_csc_lut); drm_property_blob_put(crtc_state->post_csc_lut); + + crtc_state->hw.degamma_lut = NULL; + crtc_state->hw.gamma_lut = NULL; + crtc_state->hw.ctm = NULL; + crtc_state->pre_csc_lut = NULL; + crtc_state->post_csc_lut = NULL; } void intel_crtc_free_hw_state(struct intel_crtc_state *crtc_state) From 062499cc4813b5a3cbed5dd4fbe0177265858450 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 12 Jun 2026 20:26:17 +0300 Subject: [PATCH 3800/5214] drm/i915/mtl+: Enable PPS before PLL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling PPS after a display port's PLL is enabled leads to PLL / DDI BUF timeouts during system resuming after a long (> 45 mins) suspended state, at least on some ARL and MTL laptops, either all or some of them also containing an Nvidia GPU. Enabling PPS first and then the PLL fixes the problem for all the reporters. A similar issue is seen when enabling an external DP output on PHY B (vs. PHY A in the above eDP cases), where this change will not have any effect (since no PPS is used in that case). There isn't any direct connection between PPS and PLL, so the fix for eDP works by some side-effect only. However Bspec does seem to require enabling PPS first, so let's do that. Further investigation continues on the actual root cause and a cure for external panels. Fixes: 1a7fad2aea74 ("drm/i915/cx0: Enable dpll framework for MTL+") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16098 Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16064 Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16042 Cc: Mika Kahola Cc: stable@vger.kernel.org # v7.0+ Tested-by: Jouni Högander Tested-by: Marco Nenciarini Reviewed-by: Suraj Kandpal Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612172617.3427027-1-imre.deak@intel.com (cherry picked from commit 28783a274e886dd6da61419be6020bd9d0384e9f) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_ddi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 205978c9feb6..6296635c4e79 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, /* 3. Select Thunderbolt */ mtl_port_buf_ctl_io_selection(encoder); - /* 4. Enable Panel Power if PPS is required */ - intel_pps_on(intel_dp); - /* 5. Enable the port PLL */ intel_ddi_enable_clock(encoder, crtc_state); @@ -3708,6 +3705,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state, else if (display->platform.geminilake || display->platform.broxton) bxt_dpio_phy_set_lane_optim_mask(encoder, crtc_state->lane_lat_optim_mask); + + /* + * There is no direct connection between the PLL and PPS, however + * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts + * during system resume. Do that matching the Bspec order as well. + */ + if (DISPLAY_VER(display) >= 14) + intel_pps_on(&dig_port->dp); } static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) From b4b15e4f12ad1dd83b69d453ef826553917eb8cb Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Fri, 29 May 2026 12:36:02 -0700 Subject: [PATCH 3801/5214] Revert "drm/xe/nvls: Define GuC firmware for NVL-S" This reverts commit 4e88de313ff4d1c67b644b1f39f9fb4089711b71. The early GuC FW definition meant for our CI branch was accidentally merged to the drm-xe-next branch instead. This GuC FW will never be released to linux-firmware, so we do not want the definition to be available in the mainline Linux codebase. Fixes: 4e88de313ff4 ("drm/xe/nvls: Define GuC firmware for NVL-S") Signed-off-by: Daniele Ceraolo Spurio Cc: Julia Filipchuk Cc: Rodrigo Vivi Cc: Matt Roper Cc: stable@vger.kernel.org # v7.0+ Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260529193558.185436-11-daniele.ceraolospurio@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 65b8e0ac86e48cfc9128c04dfc53ea3395d030dd) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_uc_fw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c index df2aa196f6f9..3f08a3b54062 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,7 +115,6 @@ struct fw_blobs_by_type { #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(NOVALAKE_S, GT_TYPE_ANY, mmp_ver(xe, guc, nvl, 70, 55, 4)) \ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 54, 0)) \ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 54, 0)) \ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ From 65b1f95802f90fea0283f226317dc02dba494c81 Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:15 +0530 Subject: [PATCH 3802/5214] Revert "drm/xe: Skip exec queue schedule toggle if queue is idle during suspend" This reverts commit 8533051ce92015e9cc6f75e0d52119b9d91610b6. The idle-skip optimization bypasses GuC suspend, so the GPU may not perform the context switch that flushes TLB entries for invalidated userptr VMAs. In LR/preempt-fence VM mode, this can lead to missed TLB invalidation and page faults during userptr invalidation tests. Restore unconditional schedule toggling on suspend so the context-switch TLB flush is always performed. This optimization will be reintroduced with a fix that does not skip suspend in LR/preempt-fence VM mode. Fixes: 8533051ce920 ("drm/xe: Skip exec queue schedule toggle if queue is idle during suspend") Cc: stable@vger.kernel.org # v7.0+ Suggested-by: Thomas Hellstrom Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-2-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 6a1e7934d9a6cf46aecae00a99c2603d1295e170) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_exec_queue.h | 17 -------- drivers/gpu/drm/xe/xe_guc_submit.c | 55 ++----------------------- drivers/gpu/drm/xe/xe_hw_engine_group.c | 10 +---- 3 files changed, 5 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h index a82d99bd77bc..0225426c57b0 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.h +++ b/drivers/gpu/drm/xe/xe_exec_queue.h @@ -162,21 +162,4 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch); struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q); struct xe_lrc *xe_exec_queue_get_lrc(struct xe_exec_queue *q, u16 idx); -/** - * xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend - * @q: The exec_queue - * - * If an exec queue is not parallel and is idle, the suspend steps can be - * skipped in the submission backend immediatley signaling the suspend fence. - * Parallel queues cannot skip this step due to limitations in the submission - * backend. - * - * Return: True if exec queue is idle and can skip suspend steps, False - * otherwise - */ -static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q); -} - #endif diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index ab501513d806..d1ab66ca1856 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -71,7 +71,6 @@ exec_queue_to_guc(struct xe_exec_queue *q) #define EXEC_QUEUE_STATE_WEDGED (1 << 8) #define EXEC_QUEUE_STATE_BANNED (1 << 9) #define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 10) -#define EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND (1 << 11) static bool exec_queue_registered(struct xe_exec_queue *q) { @@ -218,21 +217,6 @@ static void clear_exec_queue_pending_resume(struct xe_exec_queue *q) atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state); } -static bool exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND; -} - -static void set_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_or(EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - -static void clear_exec_queue_idle_skip_suspend(struct xe_exec_queue *q) -{ - atomic_and(~EXEC_QUEUE_STATE_IDLE_SKIP_SUSPEND, &q->guc->state); -} - static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) { return (atomic_read(&q->guc->state) & @@ -1157,7 +1141,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (!job->restore_replay || job->last_replay) { if (xe_exec_queue_is_parallel(q)) wq_item_append(q); - else if (!exec_queue_idle_skip_suspend(q)) + else xe_lrc_set_ring_tail(lrc, lrc->ring.tail); job->last_replay = false; } @@ -1812,10 +1796,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; struct xe_guc *guc = exec_queue_to_guc(q); - bool idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && guc_exec_queue_allowed_to_change_state(q) && - !exec_queue_suspended(q) && exec_queue_enabled(q)) { + if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) && + exec_queue_enabled(q)) { wait_event(guc->ct.wq, vf_recovery(guc) || ((q->guc->resume_time != RESUME_PENDING || xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q))); @@ -1834,33 +1817,11 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg) disable_scheduling(q, false); } } else if (q->guc->suspend_pending) { - if (idle_skip_suspend) - set_exec_queue_idle_skip_suspend(q); set_exec_queue_suspended(q); suspend_fence_signal(q); } } -static void sched_context(struct xe_exec_queue *q) -{ - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_lrc *lrc = q->lrc[0]; - u32 action[] = { - XE_GUC_ACTION_SCHED_CONTEXT, - q->guc->id, - }; - - xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_parallel(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); - xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q)); - xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q)); - - trace_xe_exec_queue_submit(q); - - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0); -} - static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) { struct xe_exec_queue *q = msg->private_data; @@ -1868,22 +1829,12 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg) if (guc_exec_queue_allowed_to_change_state(q)) { clear_exec_queue_suspended(q); if (!exec_queue_enabled(q)) { - if (exec_queue_idle_skip_suspend(q)) { - struct xe_lrc *lrc = q->lrc[0]; - - clear_exec_queue_idle_skip_suspend(q); - xe_lrc_set_ring_tail(lrc, lrc->ring.tail); - } q->guc->resume_time = RESUME_PENDING; set_exec_queue_pending_resume(q); enable_scheduling(q); - } else if (exec_queue_idle_skip_suspend(q)) { - clear_exec_queue_idle_skip_suspend(q); - sched_context(q); } } else { clear_exec_queue_suspended(q); - clear_exec_queue_idle_skip_suspend(q); } } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_group.c b/drivers/gpu/drm/xe/xe_hw_engine_group.c index 4c2b113364d3..02cf32ae5aa9 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_group.c +++ b/drivers/gpu/drm/xe/xe_hw_engine_group.c @@ -208,21 +208,15 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group lockdep_assert_held_write(&group->mode_sem); list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { - bool idle_skip_suspend; if (!xe_vm_in_fault_mode(q->vm)) continue; - idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q); - if (!idle_skip_suspend && has_deps) + if (has_deps) return -EAGAIN; xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); - if (idle_skip_suspend) - xe_gt_stats_incr(q->gt, - XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1); - - need_resume |= !idle_skip_suspend; + need_resume = true; q->ops->suspend(q); gt = q->gt; } From 1141574cb85db5226042b3d903157f95bfc631b1 Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Wed, 3 Jun 2026 12:22:16 +0530 Subject: [PATCH 3803/5214] drm/xe: Clear pending_disable before signaling suspend fence In the schedule-disable done path for suspend, we signal the suspend fence before clearing pending_disable. That wakeup can let suspend_wait complete and resume be queued immediately. The resume path may then reach enable_scheduling() while pending_disable is still set and hit the !exec_queue_pending_disable(q) assertion. Fix this by clearing pending_disable before signaling the suspend fence, so any resumed transition observes a consistent state. Fixes: 87651f31ae4e ("drm/xe/guc_submit: fix race around suspend_pending") Cc: stable@vger.kernel.org # v7.0+ Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellstrom Signed-off-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260603065217.3131066-3-tilak.tirumalesh.tangudu@intel.com (cherry picked from commit 4b1ae138b0e103d753773956a84eebc2edbf62c4) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_guc_submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index d1ab66ca1856..122a0983df18 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -2791,8 +2791,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q, xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q)); if (q->guc->suspend_pending) { - suspend_fence_signal(q); clear_exec_queue_pending_disable(q); + suspend_fence_signal(q); } else { if (exec_queue_banned(q)) { smp_wmb(); From 65280af331aa15eab7012bd7f07823be13754ba2 Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Wed, 3 Jun 2026 16:39:47 -0700 Subject: [PATCH 3804/5214] drm/xe/multi_queue: skip submit when primary queue is suspended Return early in submit path when the multi-queue primary exec queue is suspended to avoid submitting while suspended. v2: Remove idle_skip_suspend fix as that feature is being reverted here https://patchwork.freedesktop.org/series/167262/ Fixes: bc5775c59258 ("drm/xe/multi_queue: Add GuC interface for multi queue support") Cc: stable@vger.kernel.org # v7.0+ Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Reviewed-by: Daniele Ceraolo Spurio Signed-off-by: Niranjana Vishwanathapura Link: https://patch.msgid.link/20260603233946.863663-2-niranjana.vishwanathapura@intel.com (cherry picked from commit b7fb55cc3364ca128cfff9d50649ffd4327cd01e) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_guc_submit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 122a0983df18..4b247a3019d2 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1151,9 +1151,12 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) /* * All queues in a multi-queue group will use the primary queue - * of the group to interface with GuC. + * of the group to interface with GuC. If primay is suspended, + * just return. Jobs will get scheduled once primary is resumed. */ q = xe_exec_queue_multi_queue_primary(q); + if (exec_queue_suspended(q)) + return; if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; From 45fa032b68570a9ccd80328a857ac5872217be92 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 2 Jun 2026 10:18:42 +0530 Subject: [PATCH 3805/5214] drm/xe/drm_ras: Make counter allocation drm managed cleanup_node_param() is not registered for previous node in case of counter allocation failure, which results in stale memory of previous node that isn't cleaned up on unwind. Fix this using drm managed allocation, which is guaranteed to be cleaned up on unwind. Fixes: b40db12b542f ("drm/xe/xe_drm_ras: Add support for XE DRM RAS") Signed-off-by: Raag Jadav Reviewed-by: Riana Tauro Link: https://patch.msgid.link/20260602044919.702209-3-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit 58d77c77ea0c5cb2b755ebe23e973c8272acd896) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_drm_ras.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c index c21c8b428de6..c1d5ac198a7c 100644 --- a/drivers/gpu/drm/xe/xe_drm_ras.c +++ b/drivers/gpu/drm/xe/xe_drm_ras.c @@ -80,7 +80,7 @@ static struct xe_drm_ras_counter *allocate_and_copy_counters(struct xe_device *x struct xe_drm_ras_counter *counter; int i; - counter = kcalloc(DRM_XE_RAS_ERR_COMP_MAX, sizeof(*counter), GFP_KERNEL); + counter = drmm_kcalloc(&xe->drm, DRM_XE_RAS_ERR_COMP_MAX, sizeof(*counter), GFP_KERNEL); if (!counter) return ERR_PTR(-ENOMEM); @@ -135,7 +135,6 @@ static void cleanup_node_param(struct xe_drm_ras *ras, const enum drm_xe_ras_err { struct drm_ras_node *node = &ras->node[severity]; - kfree(ras->info[severity]); ras->info[severity] = NULL; kfree(node->device_name); From 042f1d5f20d63fb9b0e2fbba97648d16fe1a845d Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 2 Jun 2026 10:18:43 +0530 Subject: [PATCH 3806/5214] drm/xe/drm_ras: Add per node cleanup action cleanup_node_param() is not registered for previous node in case of counter allocation failure, which results in stale memory of previous node that isn't cleaned up on unwind. Add per node cleanup action which guarantees cleanup on unwind and also simplifies the cleanup logic. Fixes: b40db12b542f ("drm/xe/xe_drm_ras: Add support for XE DRM RAS") Signed-off-by: Raag Jadav Reviewed-by: Riana Tauro Link: https://patch.msgid.link/20260602044919.702209-4-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit 67fc5543d8274b2fcbef87734fad0469358f4478) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_drm_ras.c | 58 +++++++++++++-------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c index c1d5ac198a7c..cd236f53699e 100644 --- a/drivers/gpu/drm/xe/xe_drm_ras.c +++ b/drivers/gpu/drm/xe/xe_drm_ras.c @@ -131,53 +131,47 @@ static int assign_node_params(struct xe_device *xe, struct drm_ras_node *node, return 0; } -static void cleanup_node_param(struct xe_drm_ras *ras, const enum drm_xe_ras_error_severity severity) +static void cleanup_node_param(struct drm_ras_node *node) { - struct drm_ras_node *node = &ras->node[severity]; - - ras->info[severity] = NULL; - kfree(node->device_name); node->device_name = NULL; } +static void cleanup_node(struct drm_device *drm, void *node) +{ + drm_ras_node_unregister(node); + cleanup_node_param(node); +} + static int register_nodes(struct xe_device *xe) { struct xe_drm_ras *ras = &xe->ras; - int i; + struct drm_ras_node *node; + int i, ret; for_each_error_severity(i) { - struct drm_ras_node *node = &ras->node[i]; - int ret; + node = &ras->node[i]; ret = assign_node_params(xe, node, i); - if (ret) { - cleanup_node_param(ras, i); - return ret; - } + if (ret) + goto free_param; ret = drm_ras_node_register(node); - if (ret) { - cleanup_node_param(ras, i); - return ret; - } + if (ret) + goto free_param; + + ret = drmm_add_action_or_reset(&xe->drm, cleanup_node, node); + if (ret) + goto null_info; } return 0; -} -static void xe_drm_ras_unregister_nodes(struct drm_device *device, void *arg) -{ - struct xe_device *xe = arg; - struct xe_drm_ras *ras = &xe->ras; - int i; - - for_each_error_severity(i) { - struct drm_ras_node *node = &ras->node[i]; - - drm_ras_node_unregister(node); - cleanup_node_param(ras, i); - } +free_param: + cleanup_node_param(node); +null_info: + ras->info[i] = NULL; + return ret; } /** @@ -206,11 +200,5 @@ int xe_drm_ras_init(struct xe_device *xe) return err; } - err = drmm_add_action_or_reset(&xe->drm, xe_drm_ras_unregister_nodes, xe); - if (err) { - drm_err(&xe->drm, "Failed to add action for Xe DRM RAS (%pe)\n", ERR_PTR(err)); - return err; - } - return 0; } From d46319bc8273c1018fa532a383eb515ee1b1de4b Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 2 Jun 2026 10:18:44 +0530 Subject: [PATCH 3807/5214] drm/xe/hw_error: Use HW_ERR prefix in log Hardware errors should be logged with HW_ERR prefix. Make them consistent with existing logs. Fixes: 01aab7e1c9d4 ("drm/xe/xe_hw_error: Add support for PVC SoC errors") Signed-off-by: Raag Jadav Reviewed-by: Riana Tauro Link: https://patch.msgid.link/20260602044919.702209-5-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit ad60a618c49fef07d1860bfb1091140d29f5eddb) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_hw_error.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_hw_error.c b/drivers/gpu/drm/xe/xe_hw_error.c index 5135e8e4093f..4b72959b2276 100644 --- a/drivers/gpu/drm/xe/xe_hw_error.c +++ b/drivers/gpu/drm/xe/xe_hw_error.c @@ -223,9 +223,9 @@ static void log_hw_error(struct xe_tile *tile, const char *name, struct xe_device *xe = tile_to_xe(tile); if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s %s detected\n", name, severity_str); + drm_warn(&xe->drm, HW_ERR "%s %s detected\n", name, severity_str); else - drm_err_ratelimited(&xe->drm, "%s %s detected\n", name, severity_str); + drm_err_ratelimited(&xe->drm, HW_ERR "%s %s detected\n", name, severity_str); } static void log_gt_err(struct xe_tile *tile, const char *name, int i, u32 err, @@ -235,10 +235,10 @@ static void log_gt_err(struct xe_tile *tile, const char *name, int i, u32 err, struct xe_device *xe = tile_to_xe(tile); if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", + drm_warn(&xe->drm, HW_ERR "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", name, severity_str, i, err); else - drm_err_ratelimited(&xe->drm, "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", + drm_err_ratelimited(&xe->drm, HW_ERR "%s %s detected, ERROR_STAT_GT_VECTOR%d:0x%08x\n", name, severity_str, i, err); } @@ -255,9 +255,9 @@ static void log_soc_error(struct xe_tile *tile, const char * const *reg_info, if (strcmp(name, "Undefined")) { if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) - drm_warn(&xe->drm, "%s SOC %s detected", name, severity_str); + drm_warn(&xe->drm, HW_ERR "%s SOC %s detected", name, severity_str); else - drm_err_ratelimited(&xe->drm, "%s SOC %s detected", name, severity_str); + drm_err_ratelimited(&xe->drm, HW_ERR "%s SOC %s detected", name, severity_str); atomic_inc(&info[index].counter); } } From f2d238408db2b963951f0f2ae70ff7f9e337b540 Mon Sep 17 00:00:00 2001 From: Tangudu Tilak Tirumalesh Date: Mon, 8 Jun 2026 21:57:44 +0530 Subject: [PATCH 3808/5214] drm/xe: include all registered queues in TLB invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context-based TLB invalidation currently selects only scheduling-active exec queues via q->ops->active(). During rebind flows, queues may be suspended (or transitioning through resume) while still owning valid translations, causing them to be skipped from invalidation and leading to missed TLB invalidations on LR rebinds. The underlying issue is a TOCTOU: q->guc->state bits are flipped lock-free from enable_scheduling(), disable_scheduling{,_deregister}(), the suspend/resume sched-msg handlers, handle_sched_done(), and guc_exec_queue_stop(); nothing in send_tlb_inval_ctx_ppgtt() serializes against them, so any state-based predicate can race. Include all the registered queues so that TLB invalidations are not missed. This is race-free because list membership on vm->exec_queues.list is stable under vm->exec_queues.lock held by the caller. The performance impact is expected to be minimal and harmless. If it does turn out to be a concern, we can come back with a race-safe solution to ignore certain queues. Fixes: 6cdaa5346d6f ("drm/xe: Add context-based invalidation to GuC TLB invalidation backend") Assisted-by: Claude:claude-opus-4.6 Suggested-by: Thomas Hellstrom Signed-off-by: Tangudu Tilak Tirumalesh Reviewed-by: Thomas Hellström Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260608162745.338725-2-tilak.tirumalesh.tangudu@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit aa625e1e9f0710e424fe4f0e3f032807df81b5b0) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_guc_tlb_inval.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index ced58f46f846..cf6d106e6036 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -255,9 +255,8 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, #undef EXEC_QUEUE_COUNT_FULL_THRESHOLD /* - * Move exec queues to a temporary list to issue invalidations. The exec - * queue must active and a reference must be taken to prevent concurrent - * deregistrations. + * Move exec queues to a temporary list to issue invalidations. A + * reference must be taken to prevent concurrent deregistrations. * * List modification is safe because we hold 'vm->exec_queues.lock' for * reading, which prevents external modifications. Using a per-GT list @@ -266,7 +265,7 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, */ list_for_each_entry_safe(q, next, &vm->exec_queues.list[id], vm_exec_queue_link) { - if (q->ops->active(q) && xe_exec_queue_get_unless_zero(q)) { + if (xe_exec_queue_get_unless_zero(q)) { last_q = q; list_move_tail(&q->vm_exec_queue_link, &tlb_inval_list); } From 0b837315ca0adc317e5c8a9c7e484a0e29199b9b Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 10 Jun 2026 10:27:05 -0700 Subject: [PATCH 3809/5214] drm/xe: fix refcount leak in xe_range_fence_insert() xe_range_fence_insert() acquires a reference on fence via dma_fence_get() and stores it in rfence->fence. It then calls dma_fence_add_callback() and handles two cases: when the callback is successfully registered (err == 0) the fence is transferred to the tree for later cleanup; when the fence is already signaled (err == -ENOENT) it manually drops the extra reference with dma_fence_put(fence). However, dma_fence_add_callback() can fail with other errors (e.g. -EINVAL) and in that case the code falls through to the free: label without releasing the acquired reference, leaking it. Fix the leak by adding an else branch that calls dma_fence_put() before jumping to free: for any error other than -ENOENT. Fixes: 845f64bdbfc9 ("drm/xe: Introduce a range-fence utility") Signed-off-by: Wentao Liang Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260610172705.3450560-1-matthew.brost@intel.com (cherry picked from commit 98c4a4201290823c2c5c7ba21692bd9a64b61021) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_range_fence.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_range_fence.c b/drivers/gpu/drm/xe/xe_range_fence.c index 372378e89e98..3d8fa194a7b0 100644 --- a/drivers/gpu/drm/xe/xe_range_fence.c +++ b/drivers/gpu/drm/xe/xe_range_fence.c @@ -77,6 +77,8 @@ int xe_range_fence_insert(struct xe_range_fence_tree *tree, } else if (err == 0) { xe_range_fence_tree_insert(rfence, &tree->root); return 0; + } else { + dma_fence_put(fence); } free: From 770031ec2312bfab307d05db5469f24fd297e758 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 10 Jun 2026 11:25:49 -0400 Subject: [PATCH 3810/5214] drm/xe: fix job timeout recovery for unstarted jobs and kernel queues A job that GuC never scheduled (never started) indicates a GuC scheduling failure; previously such jobs were silently errored out instead of triggering a GT reset to recover. Trigger a GT reset and resubmit them, but only when the queue was not already killed or banned: an unstarted job on an already banned queue is the ban working as intended and must neither clear the ban nor kick off a reset, otherwise a banned userspace queue could be resurrected and spam GT resets. Kernel queues are always recovered this way and wedge the device once recovery attempts are exhausted, since kernel work must not silently fail. A started job that times out on a userspace VM bind queue stays banned rather than being reset and retried. The queue is banned early in the timeout handler to signal the G2H scheduling-done handler so it wakes the disable-scheduling waiter; without it the waiter sleeps the full 5s timeout. When a reset is warranted the ban is cleared before rearming so that guc_exec_queue_start() can resubmit jobs after the GT reset - a still-banned queue would block resubmission and cause an infinite TDR loop. The already-banned case is gated out before this point via skip_timeout_check, so it is unaffected. v2: (Himal) Do it for any queue type, not just kernel/migration v3: - (Sashiko and Sanjay): don't clear the ban / GT reset for already killed/banned queues on unstarted-job timeout - Update commit message - (Matt) Add Fixes tag Fixes: fe05cee4d953 ("drm/xe: Don't short circuit TDR on jobs not started") Cc: Matthew Auld Cc: Matthew Brost Cc: Sanjay Yadav Cc: Himal Prasad Ghimiray Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Assisted-by: GitHub-Copilot:claude-opus-4.8 Tested-by: Sanjay Yadav Reviewed-by: Sanjay Yadav Reviewed-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260610152548.404575-3-rodrigo.vivi@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit b1107d085e7e8ed15ba6f80c102528a9c8a6cb0e) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_guc_submit.c | 49 +++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 4b247a3019d2..12a410458df6 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -157,6 +157,11 @@ static void set_exec_queue_banned(struct xe_exec_queue *q) atomic_or(EXEC_QUEUE_STATE_BANNED, &q->guc->state); } +static void clear_exec_queue_banned(struct xe_exec_queue *q) +{ + atomic_andnot(EXEC_QUEUE_STATE_BANNED, &q->guc->state); +} + static bool exec_queue_suspended(struct xe_exec_queue *q) { return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_SUSPENDED; @@ -1363,7 +1368,8 @@ static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), q->guc->id); - return xe_sched_invalidate_job(job, 2); + /* GuC never scheduled this job - let the caller trigger a GT reset. */ + return true; } ctx_timestamp = lower_32_bits(xe_lrc_timestamp(q->lrc[0])); @@ -1460,6 +1466,21 @@ static void disable_scheduling(struct xe_exec_queue *q, bool immediate) G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1); } +/* + * Recover via GT reset for a kernel queue, or for a GuC scheduling failure (job + * never started) on a queue that was not already killed or banned. An already + * banned queue must stay banned, so its unstarted jobs do not clear the ban or + * trigger a reset. + */ +static bool timeout_needs_gt_reset(struct xe_exec_queue *q, struct xe_sched_job *job, + bool skip_timeout_check) +{ + if (q->flags & EXEC_QUEUE_FLAG_KERNEL) + return true; + + return !skip_timeout_check && !xe_sched_job_started(job); +} + static enum drm_gpu_sched_stat guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) { @@ -1608,19 +1629,19 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), q->guc->id, q->flags); - /* - * Kernel jobs should never fail, nor should VM jobs if they do - * somethings has gone wrong and the GT needs a reset - */ - xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL, - "Kernel-submitted job timed out\n"); - xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q), - "VM job timed out on non-killed execqueue\n"); - if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL || - (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) { - if (!xe_sched_invalidate_job(job, 2)) { - xe_gt_reset_async(q->gt); - goto rearm; + if (!wedged) { + if (timeout_needs_gt_reset(q, job, skip_timeout_check)) { + if (!xe_sched_invalidate_job(job, 2)) { + clear_exec_queue_banned(q); + xe_gt_reset_async(q->gt); + goto rearm; + } + if (q->flags & EXEC_QUEUE_FLAG_KERNEL) { + xe_gt_WARN(q->gt, true, "Kernel-submitted job timed out\n"); + xe_device_declare_wedged(gt_to_xe(q->gt)); + } + } else if (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)) { + xe_gt_WARN(q->gt, true, "VM job timed out on non-killed execqueue\n"); } } From 92dc59ab2a09097cdf249e0288ff9b69261761c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Thu, 4 Jun 2026 09:45:00 +0200 Subject: [PATCH 3811/5214] drm/xe: Fix wa_oob codegen recipe for external module builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building with 'make M=drivers/gpu/drm/xe modules', kbuild invokes scripts/Makefile.build with obj=., causing $(obj) to expand to '.'. Make normalizes './xe_gen_wa_oob' to 'xe_gen_wa_oob' when constructing the $^ automatic variable (target name normalization), so the recipe command becomes just 'xe_gen_wa_oob ...' without any path prefix, and the shell cannot find the tool. Fix by replacing $^ with explicit $(obj)/xe_gen_wa_oob and $(src)/ references in both wa_oob recipe commands. In recipe strings, make does not apply target name normalization, so $(obj)/xe_gen_wa_oob correctly expands to './xe_gen_wa_oob' and the shell can execute it. This matches the pattern already used by other DRM drivers (e.g. radeon's mkregtable). Fixes: f037e0b78e6d ("drm/xe: add xe_device_wa infrastructure") Cc: Matt Atwood Cc: Matthew Brost Cc: Rodrigo Vivi Cc: intel-xe@lists.freedesktop.org Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260604074501.172129-1-thomas.hellstrom@linux.intel.com (cherry picked from commit 3a11a63cc16660d514ff584e7551589655337e87) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 09661f079d03..8e7b146880f4 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -16,14 +16,14 @@ subdir-ccflags-y += -I$(obj) -I$(src) hostprogs := xe_gen_wa_oob generated_oob := $(obj)/generated/xe_wa_oob.c $(obj)/generated/xe_wa_oob.h quiet_cmd_wa_oob = GEN $(notdir $(generated_oob)) - cmd_wa_oob = mkdir -p $(@D); $^ $(generated_oob) + cmd_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_wa_oob.rules $(generated_oob) $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_wa_oob.rules $(call cmd,wa_oob) generated_device_oob := $(obj)/generated/xe_device_wa_oob.c $(obj)/generated/xe_device_wa_oob.h quiet_cmd_device_wa_oob = GEN $(notdir $(generated_device_oob)) - cmd_device_wa_oob = mkdir -p $(@D); $^ $(generated_device_oob) + cmd_device_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_device_wa_oob.rules $(generated_device_oob) $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_device_wa_oob.rules $(call cmd,device_wa_oob) From ba7fd163422877ad5a5cf31306a38c07d3932b0c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 11 Jun 2026 16:58:44 -0700 Subject: [PATCH 3812/5214] drm/xe: Set TTM device beneficial_order to 9 (2M) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the TTM device beneficial_order to 9 (2M), which is the sweet spot for Xe when attempting reclaim on system memory BOs, as it matches the large GPU page size. This ensures reclaim is attempted at the most effective order for the driver. This fixes an issue where an order-10 (4M) allocation cannot be found despite an abundance of memory. The 4M allocation triggers reclaim, unnecessarily evicting the working set and hurting performance. Since the TTM infrastructure was introduced recently, we are tagging the TTM patch as the Fixes target, even though this resolves an Xe-side problem. Fixes: 7e9c548d3709 ("drm/ttm: Allow drivers to specify maximum beneficial TTM pool size") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Andi Shyti Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260611235844.3725147-1-matthew.brost@intel.com (cherry picked from commit 0d81db90d364cb3d733410829118759f28957c5a) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index d224861b6f6f..abe25aedeead 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -526,7 +526,8 @@ int xe_device_init_early(struct xe_device *xe) err = ttm_device_init(&xe->ttm, &xe_ttm_funcs, xe->drm.dev, xe->drm.anon_inode->i_mapping, - xe->drm.vma_offset_manager, 0); + xe->drm.vma_offset_manager, + TTM_ALLOCATION_POOL_BENEFICIAL_ORDER(get_order(SZ_2M))); if (err) return err; From 632ecc90e1ca5d3b6822bb4d08f84a175b6c42c0 Mon Sep 17 00:00:00 2001 From: Tejas Upadhyay Date: Fri, 12 Jun 2026 12:34:02 +0530 Subject: [PATCH 3813/5214] drm/xe/guc: Fix buffer overflow in steered register list allocation The size calculation for the steered register extarray uses only the geometry DSS mask (g_dss_mask) to determine the number of entries to allocate: total = bitmap_weight(gt->fuse_topo.g_dss_mask, ...) * steer_reg_num; However, the filling loop uses for_each_dss_steering(), which iterates over for_each_dss(), defined as the union of g_dss_mask and c_dss_mask (geometry + compute DSS). On platforms with compute-only DSS bits, the loop writes past the allocated buffer, corrupting adjacent slab objects. This manifests as list_del corruption and SLUB redzone overwrites during drm_managed_release on device unbind, since the overflow corrupts the drmres list_head of neighboring allocations. Fix by computing the allocation size using the union of both DSS masks, matching the iteration pattern of for_each_dss_steering(). -- v2: - use bitmap_weighted_or() (Zhanjun) Fixes: b170d696c1e2 ("drm/xe/guc: Add XE_LP steered register lists") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/8049 Cc: Zhanjun Dong Cc: stable@vger.kernel.org Assisted-by: GitHub-Copilot:claude-opus-4.6 Reviewed-by: Zhanjun Dong Link: https://patch.msgid.link/20260612070401.543305-2-tejas.upadhyay@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit 0a78a44f4901aa6c9263e66be7fce02282f1109f) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/xe_guc_capture.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c index 21f7caf9ea08..1a019137ddf4 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.c +++ b/drivers/gpu/drm/xe/xe_guc_capture.c @@ -461,8 +461,14 @@ static void guc_capture_alloc_steered_lists(struct xe_guc *guc) if (!list || guc->capture->extlists) return; - total = bitmap_weight(gt->fuse_topo.g_dss_mask, sizeof(gt->fuse_topo.g_dss_mask) * 8) * - guc_capture_get_steer_reg_num(guc_to_xe(guc)); + { + xe_dss_mask_t all_dss; + + total = bitmap_weighted_or(all_dss, gt->fuse_topo.g_dss_mask, + gt->fuse_topo.c_dss_mask, + XE_MAX_DSS_FUSE_BITS) * + guc_capture_get_steer_reg_num(guc_to_xe(guc)); + } if (!total) return; From 0b5ed2756d45b04669502a1f13b1657ec7664571 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Fri, 5 Jun 2026 22:42:58 +0000 Subject: [PATCH 3814/5214] drm/xe: Add compact-PT and addr mask handling for page reclaim Current implementation of generate_reclaim_entry() overlooks some differences between the different page implementations: address masking and compact 64K page handling. Address masking of each leaf varies depending on the leaf entry size. generate_reclaim_entry() is using XE_PTE_ADDR_MASK [51:12] for all leaf entries. For 2MB PTEs, bit 12 (PAT) is part of the flags so the old mask corrupts the physical address extraction. 64K pages can be represented as PS64 and a compact PT, which the latter was not handled. Compact pages aren't walked by the unbind walker, so we separately walk through the compact PT to ensure none of the leaf 64K PTEs are dropped. Previously, compact PT were causing an abort since it was considered covered and not descended into. v2: - Update 64K entry/unbind walker for 64K compact PT handling. (Matthew) - Rework calculations of reclamation and address mask size. - Add new func abstracting the error handling before generating the reclaim entry. v3: - Report finer addr granularity in abort debug print for compact. (Zongyao) - Add comments for ADDR_MASK usage. (Zongyao) - Drop existing phys_addr asserts, the new XE_PAGE_ADDR_MASK clears bits checked, so redundant asserts. (Sashiko) - WARN_ON to verify compact pt and edge pt won't be possible. Fixes: b912138df299 ("drm/xe: Create page reclaim list on unbind") Assisted-by: Sashiko-Review:gemini-3.1-pro-preview Cc: stable@vger.kernel.org Cc: Matthew Auld Suggested-by: Zongyao Bai Signed-off-by: Brian Nguyen Reviewed-by: Matthew Auld Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260605224257.2194194-2-brian3.nguyen@intel.com Signed-off-by: Matt Roper (cherry picked from commit 669252801a4aa4098fbc5dd9dd0bd93f0625abd7) Signed-off-by: Matthew Brost --- drivers/gpu/drm/xe/regs/xe_gtt_defs.h | 6 +- drivers/gpu/drm/xe/xe_pt.c | 131 +++++++++++++++----------- 2 files changed, 82 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h index 4d83461e538b..d6bc19ef277b 100644 --- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h +++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h @@ -9,7 +9,11 @@ #define XELPG_GGTT_PTE_PAT0 BIT_ULL(52) #define XELPG_GGTT_PTE_PAT1 BIT_ULL(53) -#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12) +/* + * Mask for PTE address bits [51:shift]. + * shift is the lower address boundary of page. + */ +#define XE_PAGE_ADDR_MASK(shift) GENMASK_ULL(51, (shift)) #define GGTT_PTE_VFID GENMASK_ULL(11, 2) #define GUC_GGTT_TOP 0xFEE00000 diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 2669ff5ee747..18a98667c0e6 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1602,23 +1602,21 @@ static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level, return false; } -/* page_size = 2^(reclamation_size + XE_PTE_SHIFT) */ -#define COMPUTE_RECLAIM_ADDRESS_MASK(page_size) \ -({ \ - BUILD_BUG_ON(!__builtin_constant_p(page_size)); \ - ilog2(page_size) - XE_PTE_SHIFT; \ -}) - static int generate_reclaim_entry(struct xe_tile *tile, struct xe_page_reclaim_list *prl, u64 pte, struct xe_pt *xe_child) { struct xe_gt *gt = tile->primary_gt; struct xe_guc_page_reclaim_entry *reclaim_entries = prl->entries; - u64 phys_addr = pte & XE_PTE_ADDR_MASK; + bool is_2m = xe_child->level == 1 && (pte & XE_PDE_PS_2M); + bool is_64k = xe_child->level == 0 && ((pte & XE_PTE_PS64) || xe_child->is_compact); + u32 page_shift = is_2m ? ilog2(SZ_2M) : is_64k ? ilog2(SZ_64K) : ilog2(SZ_4K); + /* Physical address bits start at page shift: 2M->[51:21], 64K->[51:16], 4K->[51:12] */ + u64 phys_addr = pte & XE_PAGE_ADDR_MASK(page_shift); + /* Page address is relative to 4K page regardless of entry level */ u64 phys_page = phys_addr >> XE_PTE_SHIFT; int num_entries = prl->num_entries; - u32 reclamation_size; + u32 reclamation_size = page_shift - XE_PTE_SHIFT; xe_tile_assert(tile, xe_child->level <= MAX_HUGEPTE_LEVEL); xe_tile_assert(tile, reclaim_entries); @@ -1633,18 +1631,12 @@ static int generate_reclaim_entry(struct xe_tile *tile, * Page size is computed as 2^(reclamation_size + XE_PTE_SHIFT) bytes. * Only 4K, 64K (level 0), and 2M pages are supported by hardware for page reclaim */ - if (xe_child->level == 0 && !(pte & XE_PTE_PS64)) { - xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_4K); /* reclamation_size = 0 */ - xe_tile_assert(tile, phys_addr % SZ_4K == 0); - } else if (xe_child->level == 0) { - xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_64K); /* reclamation_size = 4 */ - xe_tile_assert(tile, phys_addr % SZ_64K == 0); - } else if (xe_child->level == 1 && pte & XE_PDE_PS_2M) { + if (is_2m) { xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_2M); /* reclamation_size = 9 */ - xe_tile_assert(tile, phys_addr % SZ_2M == 0); + } else if (is_64k) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1); + } else if (xe_child->level == 0) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1); } else { xe_page_reclaim_list_abort(tile->primary_gt, prl, "unsupported PTE level=%u pte=%#llx", @@ -1665,6 +1657,48 @@ static int generate_reclaim_entry(struct xe_tile *tile, return 0; } +static int add_pte_to_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl, + struct xe_pt *xe_child, u64 pte, u64 addr) +{ + /* + * In rare scenarios, pte may not be written yet due to racy conditions. + * In such cases, invalidate the PRL and fallback to full PPC invalidation. + */ + if (!pte) { + xe_page_reclaim_list_abort(tile->primary_gt, prl, + "found zero pte at addr=%#llx", addr); + return -EINVAL; + } + + /* Ensure it is a defined page */ + xe_tile_assert(tile, xe_child->level == 0 || + (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G))); + + /* Account for NULL terminated entry on end (-1) */ + if (prl->num_entries >= XE_PAGE_RECLAIM_MAX_ENTRIES - 1) { + xe_page_reclaim_list_abort(tile->primary_gt, prl, + "overflow while adding pte=%#llx", pte); + return -ENOSPC; + } + + return generate_reclaim_entry(tile, prl, pte, xe_child); +} + +static bool add_compact_pt_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl, + struct xe_device *xe, struct xe_pt *compact_pt, u64 addr) +{ + struct iosys_map *map = &compact_pt->bo->vmap; + + for (pgoff_t i = 0; i < SZ_2M / SZ_64K && xe_page_reclaim_list_valid(prl); i++) { + u64 pte = xe_map_rd(xe, map, i * sizeof(u64), u64); + + if (add_pte_to_prl(tile, prl, compact_pt, pte, addr + i * SZ_64K)) + break; + } + + return xe_page_reclaim_list_valid(prl); +} + static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, unsigned int level, u64 addr, u64 next, struct xe_ptw **child, @@ -1674,21 +1708,22 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); struct xe_pt_stage_unbind_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_device *xe = tile_to_xe(xe_walk->tile); + struct xe_page_reclaim_list *prl = xe_walk->prl; + struct xe_tile *tile = xe_walk->tile; + struct xe_device *xe = tile_to_xe(tile); pgoff_t first = xe_pt_offset(addr, xe_child->level, walk); bool killed; XE_WARN_ON(!*child); XE_WARN_ON(!level); /* Check for leaf node */ - if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + if (prl && xe_page_reclaim_list_valid(prl) && xe_child->level <= MAX_HUGEPTE_LEVEL) { struct iosys_map *leaf_map = &xe_child->bo->vmap; pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk); for (pgoff_t i = 0; i < count; i++) { u64 pte; - int ret; /* * If not a leaf pt, skip unless non-leaf pt is interleaved between @@ -1698,10 +1733,23 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, u64 pt_size = 1ULL << walk->shifts[xe_child->level]; bool edge_pt = (i == 0 && !IS_ALIGNED(addr, pt_size)) || (i == count - 1 && !IS_ALIGNED(next, pt_size)); + struct xe_pt *child_pt = + container_of(xe_child->base.children[first + i], + struct xe_pt, base); - if (!edge_pt) { - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, - xe_walk->prl, + /* Compact PTs always fill a full 2M-aligned slot, never an edge. */ + XE_WARN_ON(child_pt->is_compact && edge_pt); + if (edge_pt) + continue; + + /* Walker never descends into compact PTs, descend now */ + if (child_pt->is_compact) { + if (!add_compact_pt_prl(tile, prl, xe, child_pt, + addr + (u64)i * pt_size)) + break; + } else { + xe_page_reclaim_list_abort(tile->primary_gt, + prl, "PT is skipped by walk at level=%u offset=%lu", xe_child->level, first + i); break; @@ -1711,37 +1759,12 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64); - /* - * In rare scenarios, pte may not be written yet due to racy conditions. - * In such cases, invalidate the PRL and fallback to full PPC invalidation. - */ - if (!pte) { - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, - "found zero pte at addr=%#llx", addr); + if (add_pte_to_prl(tile, prl, xe_child, pte, addr)) break; - } - - /* Ensure it is a defined page */ - xe_tile_assert(xe_walk->tile, xe_child->level == 0 || - (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G))); /* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */ if (pte & XE_PTE_PS64) i += 15; /* Skip other 15 consecutive 4K pages in the 64K page */ - - /* Account for NULL terminated entry on end (-1) */ - if (xe_walk->prl->num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1) { - ret = generate_reclaim_entry(xe_walk->tile, xe_walk->prl, - pte, xe_child); - if (ret) - break; - } else { - /* overflow, mark as invalid */ - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, - "overflow while adding pte=%#llx", - pte); - break; - } } } @@ -1751,7 +1774,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, * Verify if any PTE are potentially dropped at non-leaf levels, either from being * killed or the page walk covers the region. */ - if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + if (prl && xe_page_reclaim_list_valid(prl) && xe_child->level > MAX_HUGEPTE_LEVEL && xe_child->num_live) { bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base); @@ -1760,7 +1783,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, * we need to invalidate the PRL. */ if (killed || covered) - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, + xe_page_reclaim_list_abort(tile->primary_gt, prl, "kill at level=%u addr=%#llx next=%#llx num_live=%u", level, addr, next, xe_child->num_live); } From 9961c88b1cfb444ee03e9c1641cf170e9684c532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 16 Jun 2026 16:29:52 +0200 Subject: [PATCH 3815/5214] ipmi: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct platform_device_id to zero without relying on that value. Drop these unused assignments. Signed-off-by: Uwe Kleine-König (The Capable Hub) Message-ID: <9afdb7b0894f51fba78c64612428f7bb117901d1.1781620139.git.u.kleine-koenig@baylibre.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_si_platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index 704b06c919f0..bdc481ce1302 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -436,9 +436,9 @@ void ipmi_remove_platform_device_by_name(char *name) } static const struct platform_device_id si_plat_ids[] = { - { "dmi-ipmi-si", 0 }, - { "hardcode-ipmi-si", 0 }, - { "hotmod-ipmi-si", 0 }, + { .name = "dmi-ipmi-si" }, + { .name = "hardcode-ipmi-si" }, + { .name = "hotmod-ipmi-si" }, { } }; From 7caf2a2351d4053075670ff3e26a6815da0a9e1e Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Tue, 16 Jun 2026 02:03:00 +0545 Subject: [PATCH 3816/5214] accel/amdxdna: Use caller client for debug BO sync amdxdna_drm_sync_bo_ioctl() looks up args->handle in the ioctl caller's drm_file. For SYNC_DIRECT_FROM_DEVICE, it then calls amdxdna_hwctx_sync_debug_bo(), but passes abo->client. amdxdna_hwctx_sync_debug_bo() uses the passed client both as the handle namespace for debug_bo_hdl and as the owner of the hardware context xarray. Those must match the file that supplied args->handle. The BO's stored client pointer is object state, not the ioctl context. Pass filp->driver_priv instead, matching the original handle lookup. Fixes: 7ea046838021 ("accel/amdxdna: Support firmware debug buffer") Cc: stable@vger.kernel.org # v6.19+ Signed-off-by: Shuvam Pandey Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/178155468039.81818.12173237984867749651@gmail.com --- drivers/accel/amdxdna/amdxdna_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 6e367ddb9e1b..6c16b21994ab 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1027,6 +1027,7 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { + struct amdxdna_client *client = filp->driver_priv; struct amdxdna_dev *xdna = to_xdna_dev(dev); struct amdxdna_drm_sync_bo *args = data; struct amdxdna_gem_obj *abo; @@ -1061,7 +1062,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, args->handle, args->offset, args->size); if (args->direction == SYNC_DIRECT_FROM_DEVICE) - ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle); + ret = amdxdna_hwctx_sync_debug_bo(client, args->handle); put_obj: drm_gem_object_put(gobj); From f1b061b4d4c6cbf861319ba954caa80145cf018f Mon Sep 17 00:00:00 2001 From: guoqi0226 Date: Tue, 16 Jun 2026 18:30:18 +0800 Subject: [PATCH 3817/5214] spi: Add NULL check for spi_get_device_id() in spi_get_device_match_data() Prevent NULL pointer dereference when spi_get_device_id() returns NULL, which can happen when using driver_override without matching SPI ID entry. Signed-off-by: guoqi0226 Link: https://patch.msgid.link/20260616103018.105612-3-guoqi0226@163.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index fe7e3b3b98fc..ab37b50bbf79 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -355,12 +355,16 @@ EXPORT_SYMBOL_GPL(spi_get_device_id); const void *spi_get_device_match_data(const struct spi_device *sdev) { const void *match; + const struct spi_device_id *id; match = device_get_match_data(&sdev->dev); if (match) return match; - return (const void *)spi_get_device_id(sdev)->driver_data; + id = spi_get_device_id(sdev); + if (!id) + return NULL; + return (const void *)id->driver_data; } EXPORT_SYMBOL_GPL(spi_get_device_match_data); From f3ad1c87d8201e54b66bd6072442f0b5d5a308ee Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 16 Jun 2026 10:12:23 +0900 Subject: [PATCH 3818/5214] spi: uniphier: Fix completion initialization order before devm_request_irq() The driver calls devm_request_irq() before initializing the completion used by the interrupt handler. Because the interrupt may occur immediately after devm_request_irq(), the handler may execute before init_completion(). This may result in calling complete() on an uninitialized completion, causing undefined behavior. This has been observed with KASAN. Fix this by initializing the completion before registering the IRQ. Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Fixes: 5ba155a4d4cc ("spi: add SPI controller driver for UniPhier SoC") Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Signed-off-by: Kunihiko Hayashi Reviewed-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260616011223.201357-1-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index cc20fd11f03f..86fce9a571da 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -656,6 +656,8 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->host = host; priv->is_save_param = false; + init_completion(&priv->xfer_done); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -679,8 +681,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) return ret; } - init_completion(&priv->xfer_done); - clk_rate = clk_get_rate(priv->clk); host->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); From 66b6605bcea7af7aca3d1d858b9c5f14903f9f9a Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 12 Jun 2026 08:28:35 +0800 Subject: [PATCH 3819/5214] spi: dw: fix wrong BAUDR setting after resume After resuming from suspend to ram, spi transfer stops working. Further debugging shows that the BAUDR register isn't correctly set, this is due to dws->current_freq doesn't match the HW BAUDR setting, specifically, the dws->current_freq equals to speed_hz, but BAUDR is 0. so the dw_spi_set_clk() in below code won't be called: if (dws->current_freq != speed_hz) { dw_spi_set_clk(dws, clk_div); dws->current_freq = speed_hz; } The mismatch comes from dw_spi_shutdown_chip() when suspending. Fix this mismatch by setting dws->current_freq to 0 as well when clearing BAUDR reg in dw_spi_shutdown_chip(). Fixes: e24c74527207 ("spi: controller driver for Designware SPI core") Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260612002835.5240-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9cc79c566a70..2f2debc64e73 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -282,6 +282,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) { dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); + dws->current_freq = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); From b68d4979c88e31488970373f67ac79b4f6267008 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 16 Jun 2026 17:42:35 +0930 Subject: [PATCH 3820/5214] block: revert the iov_iter after a short copy in bio_iov_iter_bounce_write() For the incoming IOMAP_DIO_BOUNCE flag usage inside btrfs, it's pretty easy to hit short copy inside bio_iov_iter_bounce_write(). This is because btrfs has disabled page fault to avoid certain deadlock during direct writes, and instead btrfs manually fault in the pages then retry. And inside bio_iov_iter_bounce_write(), if we hit a short write, we didn't revert the iov_iter, which can cause problems like unexpected garbage for the next retry. Revert the iov_iter after a short copy. One thing to note is that, the folio is allocated then immediately queued into the bio, so the proper revert size should be (bi_size - this_len + copied). Fixes: 8dd5e7c75d7b ("block: add helpers to bounce buffer an iov_iter into bios") Signed-off-by: Qu Wenruo Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/c400989f227343b134110773d5acaaacf7024574.1781597506.git.wqu@suse.com Signed-off-by: Jens Axboe --- block/bio.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/block/bio.c b/block/bio.c index 811a96796202..96f40d39b62b 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1321,6 +1321,7 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, do { size_t this_len = min(total_len, SZ_1M); + size_t copied; struct folio *folio; if (this_len > minsize * 2) @@ -1334,12 +1335,22 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, break; bio_add_folio_nofail(bio, folio, this_len, 0); - if (copy_from_iter(folio_address(folio), this_len, iter) != - this_len) { + copied = copy_from_iter(folio_address(folio), this_len, iter); + if (copied < this_len) { + /* + * Need to revert the iov iter for all bytes we have + * copied. + * + * However the bio size differs from the real copied + * bytes as @this_len is queued but only advanced + * less than that. + * Need to compensate that for the revert. + */ + iov_iter_revert(iter, bio->bi_iter.bi_size - this_len + + copied); bio_free_folios(bio); return -EFAULT; } - total_len -= this_len; } while (total_len && bio->bi_vcnt < bio->bi_max_vecs); From d5b58fbb2fd7ac25fcd7e1c14730f998a90b0322 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 16 Jun 2026 17:42:36 +0930 Subject: [PATCH 3821/5214] block: respect iov_iter::nofault flag in bio_iov_iter_bounce_write() For the incoming usage of IOMAP_DIO_BOUNCE in btrfs, btrfs has set iov_iter::nofault to prevent deadlock when a page fault is needed to read out the buffer. However bio_iov_iter_bounce_write() doesn't respect iov_iter::nofault flag, and just call a plain copy_from_iter() so it can still trigger page fault and cause deadlock in btrfs. Fix it by utilizing copy_folio_from_iter_atomic() if nofault flag is set, otherwise use copy_folio_from_iter(). Signed-off-by: Qu Wenruo Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/9c165a314022b61566eb247852eb773ca6c70889.1781597506.git.wqu@suse.com Signed-off-by: Jens Axboe --- block/bio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block/bio.c b/block/bio.c index 96f40d39b62b..f2a5f4d0a967 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1335,7 +1335,11 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, break; bio_add_folio_nofail(bio, folio, this_len, 0); - copied = copy_from_iter(folio_address(folio), this_len, iter); + if (iter->nofault) + copied = copy_folio_from_iter_atomic(folio, 0, this_len, + iter); + else + copied = copy_folio_from_iter(folio, 0, this_len, iter); if (copied < this_len) { /* * Need to revert the iov iter for all bytes we have From ab5f9c5cb527c03790a92142ad368881a9100aaf Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:50 +0300 Subject: [PATCH 3822/5214] i3c: master: Update dev_nack_retry_count under maintenance lock Protect master->dev_nack_retry_count against concurrent sysfs updates by updating it while holding the bus maintenance lock. Consequently, combine adjacent return statements into one. For consistency, read dev_nack_retry_count while holding the bus normaluse lock. Fixes: b58f47eb39268 ("i3c: add sysfs entry and attribute for Device NACK Retry count") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 372d911ecbad..05ea9e3c4d46 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -744,7 +744,14 @@ static DEVICE_ATTR_RW(hotjoin); static ssize_t dev_nack_retry_count_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + ssize_t ret; + + i3c_bus_normaluse_lock(i3cbus); + ret = sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + i3c_bus_normaluse_unlock(i3cbus); + + return ret; } static ssize_t dev_nack_retry_count_store(struct device *dev, @@ -762,14 +769,11 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); + if (!ret) + master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); - if (ret) - return ret; - - master->dev_nack_retry_count = val; - - return count; + return ret ?: count; } static DEVICE_ATTR_RW(dev_nack_retry_count); From 79ce29e100ab3de0cad66eb48d32a7de4043e2ae Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:51 +0300 Subject: [PATCH 3823/5214] i3c: master: Add missing runtime PM get in dev_nack_retry_count_store() Ensure the device is runtime resumed while updating the retry configuration to avoid accessing the controller while suspended. Call i3c_master_rpm_get() before accessing the controller in dev_nack_retry_count_store() and release it with i3c_master_rpm_put() afterwards. Fixes: 990c149c61ee4 ("i3c: master: Introduce optional Runtime PM support") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 05ea9e3c4d46..a24944047e30 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -767,12 +767,18 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, if (ret) return ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); if (!ret) master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); + i3c_master_rpm_put(master); + return ret ?: count; } From 225b76e2a711dc061ec337befba49dd3ee75e534 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:52 +0300 Subject: [PATCH 3824/5214] i3c: master: Use unsigned int for dev_nack_retry_count consistently Use unsigned int for dev_nack_retry_count across the core and controller drivers to match the type of master->dev_nack_retry_count. Update the sysfs store path to use kstrtouint() and adjust the ->set_dev_nack_retry() callback prototype and callers accordingly. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni --- drivers/i3c/master.c | 4 ++-- drivers/i3c/master/dw-i3c-master.c | 4 ++-- include/linux/i3c/master.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index a24944047e30..f1be38a640ca 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -760,10 +760,10 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, { struct i3c_bus *i3cbus = dev_to_i3cbus(dev); struct i3c_master_controller *master = dev_to_i3cmaster(dev); - unsigned long val; + unsigned int val; int ret; - ret = kstrtoul(buf, 0, &val); + ret = kstrtouint(buf, 0, &val); if (ret) return ret; diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 971b429b76bc..2f8c0c4683e0 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1485,7 +1485,7 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id) } static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m, - unsigned long dev_nack_retry_cnt) + unsigned int dev_nack_retry_cnt) { struct dw_i3c_master *master = to_dw_i3c_master(m); u32 reg; @@ -1493,7 +1493,7 @@ static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m, if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) { dev_err(&master->base.dev, - "Value %ld exceeds maximum %d\n", + "Value %u exceeds maximum %d\n", dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX); return -ERANGE; } diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 27eeb598b3c5..4d2a68793324 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -494,7 +494,7 @@ struct i3c_master_controller_ops { int (*disable_hotjoin)(struct i3c_master_controller *master); int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed); int (*set_dev_nack_retry)(struct i3c_master_controller *master, - unsigned long dev_nack_retry_cnt); + unsigned int dev_nack_retry_cnt); }; /** From 678e9409dd78d5c080607df15c6f346c7edb03d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 16 Jun 2026 16:34:20 +0200 Subject: [PATCH 3825/5214] i3c: mipi-i3c-hci: Use named initializers for platform_device_id's .driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The assignment in this driver uses a mixed way to initialize the platform_device_id array. .name is assigned by name and .driver_data by position. Unify that to use named assignment for both struct members. This is needed for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Frank Li Reviewed-by: Adrian Hunter Link: https://patch.msgid.link/7c006616f72748fb4deccd197ca2b6427c006f79.1781620397.git.ukleinek@kernel.org Signed-off-by: Alexandre Belloni --- drivers/i3c/master/mipi-i3c-hci/core.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 3a3a9a3d4dec..e80aa1f5722e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -1189,11 +1189,14 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); static const struct platform_device_id i3c_hci_driver_ids[] = { - { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED | - HCI_QUIRK_RPM_IBI_ALLOWED | - HCI_QUIRK_RPM_PARENT_MANAGED | - HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET | - HCI_QUIRK_DMA_REQUIRES_HC_ABORT }, + { + .name = "intel-lpss-i3c", + .driver_data = HCI_QUIRK_RPM_ALLOWED | + HCI_QUIRK_RPM_IBI_ALLOWED | + HCI_QUIRK_RPM_PARENT_MANAGED | + HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET | + HCI_QUIRK_DMA_REQUIRES_HC_ABORT, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); From bb0301f856bfc0ea8192b8d2bd5a79bdc6d3d3f1 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 11 May 2026 12:49:13 +0200 Subject: [PATCH 3826/5214] i2c: algo: bit: use str_plural helper in bit_xfer Replace the manual ternary "s" pluralizations with str_plural() to simplify the code. Signed-off-by: Thorsten Blum Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260511104911.183606-3-thorsten.blum@linux.dev --- drivers/i2c/algos/i2c-algo-bit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 6544d27e4419..d1d9a6c1a1e2 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -562,7 +563,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, ret = readbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", - ret, ret == 1 ? "" : "s"); + ret, str_plural(ret)); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; @@ -573,7 +574,7 @@ static int bit_xfer(struct i2c_adapter *i2c_adap, ret = sendbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", - ret, ret == 1 ? "" : "s"); + ret, str_plural(ret)); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; From ff3ac1a0bd75400375678c0f81c2b613cbc03e14 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 16 Jun 2026 14:39:02 -0700 Subject: [PATCH 3827/5214] io_uring: Use system_dfl_wq instead of system_unbound_wq Commit de7341ffe49e ("io_uring: switch normal task_work to a mpscq") added a use of system_unbound_wq, which is deprecated in favor of system_dfl_wq added by commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq"). An upcoming warning in the workqueue tree flags this with: workqueue: work func io_tctx_fallback_work enqueued on deprecated workqueue. Use system_{percpu|dfl}_wq instead. Switch to system_dfl_wq to clear up the warning. Fixes: de7341ffe49e ("io_uring: switch normal task_work to a mpscq") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260616-io_uring-fix-wq-warning-v1-1-cfc9d934eedb@kernel.org Signed-off-by: Jens Axboe --- io_uring/tw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/tw.c b/io_uring/tw.c index f2ce806b01a1..a4c872870d81 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -51,7 +51,7 @@ static void io_fallback_tw(struct io_uring_task *tctx) * the queued work) stay around until the drain has run. */ get_task_struct(tctx->task); - if (!queue_work(system_unbound_wq, &tctx->fallback_work)) + if (!queue_work(system_dfl_wq, &tctx->fallback_work)) put_task_struct(tctx->task); } From 1e696bc33c30acda698e5e921adcc7f7e61976f7 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 11 Jun 2026 23:55:02 +0200 Subject: [PATCH 3828/5214] i2c: atr: annotate i2c_atr_adap_desc->aliases with __counted_by_ptr Add the __counted_by_ptr() compiler attribute to ->aliases to improve bounds checking via CONFIG_UBSAN_BOUNDS and CONFIG_FORTIFY_SOURCE. Signed-off-by: Thorsten Blum Reviewed-by: Luca Ceresoli Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260611215501.464405-3-thorsten.blum@linux.dev --- include/linux/i2c-atr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h index 2bb54dc87c8e..b52a7b9ec536 100644 --- a/include/linux/i2c-atr.h +++ b/include/linux/i2c-atr.h @@ -71,7 +71,7 @@ struct i2c_atr_adap_desc { struct device *parent; struct fwnode_handle *bus_handle; size_t num_aliases; - u16 *aliases; + u16 *aliases __counted_by_ptr(num_aliases); }; /** From 43a393185e33e573a374c1d4f7ddf6481484ef8d Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 11 Jun 2026 14:48:01 +0100 Subject: [PATCH 3829/5214] rust: prelude: add `zerocopy{,_derive}::IntoBytes` In order to easily use `IntoBytes`, add it to the prelude. This adds both the trait (`zerocopy::IntoBytes`) as well as the derive macro (`zerocopy_derive::IntoBytes`). [ This patch will simplify using the feature in several trees next cycle. Gary writes: This is wanted because I want to convert the upcoming I/O projection series to use `zerocopy` traits rather than keep using transmute module. It is most helpful for derives in doc tests; I do not want to explicitly use `#[derive(zerocopy_derive::IntoBytes)]` in the doc tests. For reference, the upcoming I/O projection series is at: https://lore.kernel.org/rust-for-linux/20260611-io_projection-v4-0-1f7224b02dcb@garyguo.net/ - Miguel ] Signed-off-by: Gary Guo Link: https://patch.msgid.link/20260611134802.2052296-1-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/prelude.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 8a6da92e8da6..ca396f1f78a6 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -61,10 +61,16 @@ }; #[doc(no_inline)] -pub use zerocopy::FromBytes; +pub use zerocopy::{ + FromBytes, + IntoBytes, // +}; #[doc(no_inline)] -pub use zerocopy_derive::FromBytes; +pub use zerocopy_derive::{ + FromBytes, + IntoBytes, // +}; #[doc(no_inline)] pub use super::{ From 6ee2ba9dcefe5bb41d179e183919531c8da01d14 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 6 May 2026 16:40:15 +0100 Subject: [PATCH 3830/5214] i2c: ls2x-v2: return IRQ_HANDLED after servicing an error The event ISR reads SR1 and, when an error flag (ARLO/AF/BERR) is set, calls loongson2_i2c_isr_error() which clears the offending flag, issues STOP for the AF case, records msg->result, masks every CR2 interrupt enable and completes the waiter. The handler then returns IRQ_NONE, declaring to the IRQ core that the device did not interrupt. That report is wrong. The device did interrupt and the handler fully serviced it. Because the IRQ is requested with IRQF_SHARED, the genirq spurious-IRQ tracker counts each error as unhandled. A bus that emits sporadic NACKs, arbitration losses or bus errors will therefore march toward the spurious-IRQ threshold and the line can end up disabled, wedging the controller. Return IRQ_HANDLED on this path. The other IRQ_NONE site, taken when neither an event nor an error bit is set, remains correct. Fixes: 6d1b0785f6d5 ("i2c: ls2x-v2: Add driver for Loongson-2K0300 I2C controller") Signed-off-by: David Carlier Assisted-by: Codex:GPT-5.5 Reviewed-by: Binbin Zhou Tested-by: Binbin Zhou Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260506154015.94815-1-devnexen@gmail.com --- drivers/i2c/busses/i2c-ls2x-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c index 517760d70169..9df73557ecc4 100644 --- a/drivers/i2c/busses/i2c-ls2x-v2.c +++ b/drivers/i2c/busses/i2c-ls2x-v2.c @@ -304,7 +304,7 @@ static irqreturn_t loongson2_i2c_isr_event(int irq, void *data) regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status); if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) { loongson2_i2c_isr_error(status, data); - return IRQ_NONE; + return IRQ_HANDLED; } regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2); From 36904931a1c23db42f1d7078713206874cc10d97 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 Apr 2026 19:35:50 +0200 Subject: [PATCH 3831/5214] i2c: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260423173550.92317-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3123ab75600b..d35456994280 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1058,7 +1058,7 @@ config I2C_QCOM_CCI will be called i2c-qcom-cci. config I2C_QCOM_GENI - tristate "Qualcomm Technologies Inc.'s GENI based I2C controller" + tristate "Qualcomm GENI based I2C controller" depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_GENI_SE help From f09d2312d1d44a8b8c9d3bfd33acfe930a6d8250 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 11 Jun 2026 20:05:54 +0100 Subject: [PATCH 3832/5214] rust: bitfield: mark `Debug` impl as `#[inline]` A `Debug` impl is for debugging and is normally not used, and therefore should ideally not be code-generated unless used. However, Rust has no way of knowing if a dependent crate is going to use the trait impl or not, so unless it is marked as `#[inline]`, it will be code-generated in the defining crate (as it is not generic). Mark the impl generated by bitfield macro `#[inline]`, so they do not stay in the binary unless used. This reduces nova-core.o .text by 17% (from 151922 bytes to 125676 bytes). Signed-off-by: Gary Guo Fixes: b7b8b4ccdad4 ("rust: extract `bitfield!` macro from `register!`") Acked-by: Alexandre Courbot Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260611190555.2298991-1-gary@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/bitfield.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs index 554a5a2ff0ab..35ede53f2b8e 100644 --- a/rust/kernel/bitfield.rs +++ b/rust/kernel/bitfield.rs @@ -535,6 +535,7 @@ const fn [<__with_ $field>]( // `Debug` implementation. (@debug $name:ident { $($field:ident;)* }) => { impl ::kernel::fmt::Debug for $name { + #[inline] fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result { f.debug_struct(stringify!($name)) .field("", &::kernel::prelude::fmt!("{:#x}", self.inner)) From 1589352a0c848682d7b3c15f7ca196a5c1841276 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 6 May 2026 16:00:46 +0100 Subject: [PATCH 3833/5214] dt-bindings: i2c: microchip,corei2c: permit resets Both CoreI2C and the hardened versions of it on mpfs and pic64gx have a reset pin. For the former, usually this is wired to a common fabric reset not managed by software and for the latter two the platform firmware takes them out of reset on first-party boards (or those using modified versions of the vendor firmware), but not all boards may take this approach. Permit providing a reset in devicetree for Linux, or other devicetree-consuming software, to use. Signed-off-by: Conor Dooley Reviewed-by: Krzysztof Kozlowski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260506-bronchial-kitten-e3697fb66ba7@spud --- Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml b/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml index 6ff58b64d496..bd63c70aac6b 100644 --- a/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml +++ b/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml @@ -37,6 +37,9 @@ properties: modes are supported, possible values are 100000 and 400000. enum: [100000, 400000] + resets: + maxItems: 1 + required: - compatible - reg From 1ccc75909ca7c3b52163b408a3e1eb5453db013f Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 8 Jun 2026 01:21:32 +0800 Subject: [PATCH 3834/5214] erofs: clean up erofs_ishare_fill_inode() - Use the shorthand `si` to replace the overly long `sharedinode`; - Introduce erofs_warn() and get rid of barely-used _erofs_printk(); - Get rid of the variable `hash`; - Simplify error paths. Reviewed-by: Hongbo Li Reviewed-by: Chao Yu Signed-off-by: Gao Xiang --- fs/erofs/internal.h | 2 ++ fs/erofs/ishare.c | 45 +++++++++++++++++++-------------------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 4792490161ec..9e2ae7b61977 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -23,6 +23,8 @@ __printf(2, 3) void _erofs_printk(struct super_block *sb, const char *fmt, ...); #define erofs_err(sb, fmt, ...) \ _erofs_printk(sb, KERN_ERR fmt "\n", ##__VA_ARGS__) +#define erofs_warn(sb, fmt, ...) \ + _erofs_printk(sb, KERN_WARNING fmt "\n", ##__VA_ARGS__) #define erofs_info(sb, fmt, ...) \ _erofs_printk(sb, KERN_INFO fmt "\n", ##__VA_ARGS__) diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c index 6ed66b17359b..35cbd0bc04d7 100644 --- a/fs/erofs/ishare.c +++ b/fs/erofs/ishare.c @@ -40,49 +40,42 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data) bool erofs_ishare_fill_inode(struct inode *inode) { struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); - struct erofs_inode *vi = EROFS_I(inode); const struct address_space_operations *aops; + struct erofs_inode *vi = EROFS_I(inode); struct erofs_inode_fingerprint fp; - struct inode *sharedinode; - unsigned long hash; + struct inode *si; aops = erofs_get_aops(inode, true); if (IS_ERR(aops)) return false; if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id)) return false; - hash = xxh32(fp.opaque, fp.size, 0); - sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash, - erofs_ishare_iget5_eq, erofs_ishare_iget5_set, - &fp); - if (!sharedinode) { - kfree(fp.opaque); - return false; - } - if (inode_state_read_once(sharedinode) & I_NEW) { - sharedinode->i_mapping->a_ops = aops; - sharedinode->i_size = vi->vfs_inode.i_size; - unlock_new_inode(sharedinode); + si = iget5_locked(erofs_ishare_mnt->mnt_sb, + xxh32(fp.opaque, fp.size, 0), + erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp); + if (si && (inode_state_read_once(si) & I_NEW)) { + si->i_mapping->a_ops = aops; + si->i_size = inode->i_size; + unlock_new_inode(si); } else { kfree(fp.opaque); - if (aops != sharedinode->i_mapping->a_ops) { - iput(sharedinode); + if (!si || aops != si->i_mapping->a_ops) { + iput(si); return false; } - if (sharedinode->i_size != vi->vfs_inode.i_size) { - _erofs_printk(inode->i_sb, KERN_WARNING - "size(%lld:%lld) not matches for the same fingerprint\n", - vi->vfs_inode.i_size, sharedinode->i_size); - iput(sharedinode); + if (si->i_size != inode->i_size) { + erofs_warn(inode->i_sb, "i_size mismatch (%lld != %lld) for the same fingerprint", + inode->i_size, si->i_size); + iput(si); return false; } } - vi->sharedinode = sharedinode; + vi->sharedinode = si; INIT_LIST_HEAD(&vi->ishare_list); - spin_lock(&EROFS_I(sharedinode)->ishare_lock); - list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list); - spin_unlock(&EROFS_I(sharedinode)->ishare_lock); + spin_lock(&EROFS_I(si)->ishare_lock); + list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list); + spin_unlock(&EROFS_I(si)->ishare_lock); return true; } From b05fb89900e6386b4e56bfe7dddede7becf0db75 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 10 Jun 2026 11:05:32 +0800 Subject: [PATCH 3835/5214] erofs: update the overview of the documentation Update the overview section to better reflect EROFS's design philosophy as an immutable image filesystem, update the feature highlights with recent capabilities, and remove outdated items. The following detailed sections will be revised later since the overview section is the most visible part of our documentation. Outdated or ambiguous information could mislead new users and potential adopters. Reviewed-by: Chao Yu Signed-off-by: Gao Xiang --- Documentation/filesystems/erofs.rst | 121 ++++++++++++++-------------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst index fe06308e546c..e4f84ba91052 100644 --- a/Documentation/filesystems/erofs.rst +++ b/Documentation/filesystems/erofs.rst @@ -7,83 +7,90 @@ EROFS - Enhanced Read-Only File System Overview ======== -EROFS filesystem stands for Enhanced Read-Only File System. It aims to form a -generic read-only filesystem solution for various read-only use cases instead -of just focusing on storage space saving without considering any side effects -of runtime performance. +EROFS (Enhanced Read-Only File System) is a modern, efficient, and secure +read-only kernel filesystem designed for various use cases including immutable +system images, container images, application sandbox images, and dataset +distribution. -It is designed to meet the needs of flexibility, feature extendability and user -payload friendly, etc. Apart from those, it is still kept as a simple -random-access friendly high-performance filesystem to get rid of unneeded I/O -amplification and memory-resident overhead compared to similar approaches. +An immutable image filesystem can be regarded as an enhanced archive format +which allows golden images to be built once and mounted everywhere -- images are +bit-for-bit identical across all deployments and can be verified, audited, or +shared without concerns about runtime modifications (in this model, all user +writes should be redirected into another trusted filesystem, for example, via +overlayfs for copy-on-write-style redirection, by design). -It is implemented to be a better choice for the following scenarios: +EROFS is a dedicated implementation of the image filesystem idea above, with a +flexible, hierarchical on-disk design so that needed features can be enabled on +demand. Filesystem data in the core format is strictly block-aligned in order +to perform optimally on all kinds of storage media, including block devices and +memory-backed devices. The on-disk format is easy to parse and purposely avoids +the unnecessary metadata redundancy found in generic writable filesystems, which +can suffer from extra inconsistency issues -- making it ideal for security +auditing and untrusted remote access. In addition, designs such as inline data, +inline/shared extended attributes, and optimized (de)compression provide better +space efficiency while maintaining high performance. - - read-only storage media or +In short, EROFS aims to be a better fit for the following scenarios: - - part of a fully trusted read-only solution, which means it needs to be + - As part of a secure immutable storage solution, where it needs to be immutable and bit-for-bit identical to the official golden image for - their releases due to security or other considerations and + each individual copy, in order to meet security, data sharing, and/or + other requirements; - - hope to minimize extra storage space with guaranteed end-to-end performance - by using compact layout, transparent file compression and direct access, - especially for those embedded devices with limited memory and high-density - hosts with numerous containers. + - Minimizing storage overhead with guaranteed end-to-end performance + by using compact (meta)data layout, optimized transparent data compression, + deduplication and direct access, especially for those embedded devices with + limited memory and high-density hosts with numerous containers. -Here are the main features of EROFS: +Here is the list of highlights: - - Little endian on-disk design; + - Little endian on-disk design with 48-bit block addressing, supporting up + to 1 EiB filesystem capacity with 4 KiB block size; - - Block-based distribution and file-based distribution over fscache are - supported; + - Two compact inode metadata layouts for space and performance efficiency: - - Support multiple devices to refer to external blobs, which can be used - for container images; + ======================== ======== ====================================== + compact extended + ======================== ======== ====================================== + Inode core metadata size 32 bytes 64 bytes + Max file size 4 GiB 16 EiB (also limited by max. vol size) + Max uids/gids 65536 4294967296 + Nanosecond timestamps no yes + Max hardlinks 65536 4294967296 + ======================== ======== ====================================== - - 32-bit block addresses for each device, therefore 16TiB address space at - most with 4KiB block size for now; + - Support tailpacking inline data for better space efficiency and reduce + unneeded I/O amplification; - - Two inode layouts for different requirements: + - Block-based and file-backed distribution are both supported; - ===================== ============ ====================================== - compact (v1) extended (v2) - ===================== ============ ====================================== - Inode metadata size 32 bytes 64 bytes - Max file size 4 GiB 16 EiB (also limited by max. vol size) - Max uids/gids 65536 4294967296 - Per-inode timestamp no yes (64 + 32-bit timestamp) - Max hardlinks 65536 4294967296 - Metadata reserved 8 bytes 18 bytes - ===================== ============ ====================================== + - Multiple devices to reference external data blobs: inode data can be + optionally placed into external blobs, which enables image layering and data + sharing among different filesystems; - - Support extended attributes as an option; + - Inline and shared extended attributes with an optional bloom filter that + speeds up negative extended attribute lookups; - - Support a bloom filter that speeds up negative extended attribute lookups; + - POSIX.1e ACLs by using extended attributes; - - Support POSIX.1e ACLs by using extended attributes; + - Transparent data compression as an option: Supported algorithms (LZ4, + MicroLZMA, DEFLATE and Zstandard) can be selected on a per-inode basis. + Both the on-disk metadata and decompression runtime have been heavily + optimized to minimize the overhead for better performance. - - Support transparent data compression as an option: - LZ4, MicroLZMA, DEFLATE and Zstandard algorithms can be used on a per-file - basis; In addition, inplace decompression is also supported to avoid bounce - compressed buffers and unnecessary page cache thrashing. + - Merging tail-end data into a special inode as fragments; - - Support chunk-based data deduplication and rolling-hash compressed data - deduplication; + - Chunk-based deduplication and rolling-hash compressed data deduplication; - - Support tailpacking inline compared to byte-addressed unaligned metadata - or smaller block size alternatives; + - Direct I/O and FSDAX support on uncompressed inodes for use cases such as + secure containers, loop devices, and ramdisks that do not need page caching; - - Support merging tail-end data into a special inode as fragments. + - Page cache sharing among inodes with identical content fingerprints on + the same machine. - - Support large folios to make use of THPs (Transparent Hugepages); +For more detailed information, please refer to our documentation site: - - Support direct I/O on uncompressed files to avoid double caching for loop - devices; - - - Support FSDAX on uncompressed images for secure containers and ramdisks in - order to get rid of unnecessary page cache. - - - Support file-based on-demand loading with the Fscache infrastructure. +- https://erofs.docs.kernel.org The following git tree provides the file system user-space tools under development, such as a formatting tool (mkfs.erofs), an on-disk consistency & @@ -91,10 +98,6 @@ compatibility checking tool (fsck.erofs), and a debugging tool (dump.erofs): - git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git -For more information, please also refer to the documentation site: - -- https://erofs.docs.kernel.org - Bugs and patches are welcome, please kindly help us and send to the following linux-erofs mailing list: From 289cf458a69ccda4a4aee2b0274d233d415814ca Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 17 Jun 2026 11:14:59 +0800 Subject: [PATCH 3836/5214] erofs: call erofs_exit_ishare() before rcu_barrier() Ensure all inode free callbacks have completed before destroying the inode slab cache. Fixes: 5ef3208e3be5 ("erofs: introduce the page cache share feature") Reviewed-by: Hongbo Li Signed-off-by: Gao Xiang --- fs/erofs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 802add6652fd..579443e6acfe 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -1048,11 +1048,11 @@ static int __init erofs_module_init(void) static void __exit erofs_module_exit(void) { unregister_filesystem(&erofs_fs_type); + erofs_exit_ishare(); - /* Ensure all RCU free inodes / pclusters are safe to be destroyed. */ + /* ensure all delayed rcu free inodes & pclusters are flushed */ rcu_barrier(); - erofs_exit_ishare(); erofs_exit_sysfs(); z_erofs_exit_subsystem(); erofs_exit_shrinker(); From 99980e9a7eca5ba3f4a2892acb0ccdcc7fe1dd8f Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 15 Jun 2026 15:37:35 +0800 Subject: [PATCH 3837/5214] erofs: introduce erofs_map_chunks() Try to map more chunks in the same metadata on-disk block for more efficient IO performance. Signed-off-by: Gao Xiang --- fs/erofs/data.c | 131 ++++++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 44da21c9d777..cdf2e2ef8ea8 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -98,17 +98,73 @@ void *erofs_read_metabuf(struct erofs_buf *buf, struct super_block *sb, return erofs_bread(buf, offset, true); } -int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map) +static int erofs_map_chunks(struct inode *inode, struct erofs_map_blocks *map) { struct erofs_buf buf = __EROFS_BUF_INITIALIZER; struct super_block *sb = inode->i_sb; - unsigned int unit, blksz = sb->s_blocksize; struct erofs_inode *vi = EROFS_I(inode); struct erofs_inode_chunk_index *idx; - erofs_blk_t startblk, addrmask; - bool tailpacking; + unsigned int unit = vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES ? + sizeof(*idx) : EROFS_BLOCK_MAP_ENTRY_SIZE; + erofs_blk_t addrmask = (vi->chunkformat & EROFS_CHUNK_FORMAT_48BIT) ? + BIT_ULL(48) - 1 : BIT_ULL(32) - 1; + u64 nr = map->m_la >> vi->chunkbits, chunksize = 1ULL << vi->chunkbits; + erofs_off_t pos = ALIGN(erofs_iloc(inode) + vi->inode_isize + + vi->xattr_isize, unit) + unit * nr; + /* m_llen will be clamped to EOF in the end */ + erofs_off_t endpos = round_up(pos + 1, sb->s_blocksize); + u64 last, addr; + + idx = erofs_read_metabuf(&buf, sb, pos, erofs_inode_in_metabox(inode)); + if (IS_ERR(idx)) + return PTR_ERR(idx); + + map->m_la = nr << vi->chunkbits; + map->m_llen = 0; + nr = 0; + do { + if (unit == EROFS_BLOCK_MAP_ENTRY_SIZE) { + addr = le32_to_cpu(((__le32 *)idx)[nr]); + if (addr == (u32)EROFS_NULL_ADDR) + addr = EROFS_NULL_ADDR; + } else { + addr = (((u64)le16_to_cpu(idx[nr].startblk_hi) << 32) | + le32_to_cpu(idx[nr].startblk_lo)) & addrmask; + if (addr ^ (EROFS_NULL_ADDR & addrmask)) + addr |= (u64)(le16_to_cpu(idx[nr].device_id) & + EROFS_SB(sb)->device_id_mask) << 48; + else + addr = EROFS_NULL_ADDR; + } + if (!nr) { + last = addr; + continue; + } + /* expand and account the prior chunk here */ + map->m_llen += chunksize; + if (last != EROFS_NULL_ADDR) + last += erofs_blknr(sb, chunksize); + } while (addr == last && pos + (++nr) * unit < endpos); + + if (last != EROFS_NULL_ADDR) { + map->m_pa = erofs_pos(sb, last & addrmask) - map->m_llen; + map->m_deviceid = last >> 48; + map->m_flags = EROFS_MAP_MAPPED; + } + if (addr == last) + map->m_llen += chunksize; + map->m_llen = min_t(erofs_off_t, map->m_llen, + round_up(inode->i_size - map->m_la, sb->s_blocksize)); + erofs_put_metabuf(&buf); + return 0; +} + +int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map) +{ + struct super_block *sb = inode->i_sb; + struct erofs_inode *vi = EROFS_I(inode); + bool tailinline = (vi->datalayout == EROFS_INODE_FLAT_INLINE); erofs_off_t pos; - u64 chunknr; int err = 0; trace_erofs_map_blocks_enter(inode, map, 0); @@ -116,13 +172,10 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map) map->m_flags = 0; if (map->m_la >= inode->i_size) goto out; - - if (vi->datalayout != EROFS_INODE_CHUNK_BASED) { - tailpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE); - if (!tailpacking && vi->startblk == EROFS_NULL_ADDR) - goto out; - pos = erofs_pos(sb, erofs_iblks(inode) - tailpacking); - + if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { + err = erofs_map_chunks(inode, map); + } else if (tailinline || vi->startblk != EROFS_NULL_ADDR) { + pos = erofs_pos(sb, erofs_iblks(inode) - tailinline); map->m_flags = EROFS_MAP_MAPPED; if (map->m_la < pos) { map->m_pa = erofs_pos(sb, vi->startblk) + map->m_la; @@ -132,57 +185,15 @@ int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map) vi->xattr_isize + erofs_blkoff(sb, map->m_la); map->m_llen = inode->i_size - map->m_la; map->m_flags |= EROFS_MAP_META; - } - goto out; - } - - if (vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES) - unit = sizeof(*idx); /* chunk index */ - else - unit = EROFS_BLOCK_MAP_ENTRY_SIZE; /* block map */ - - chunknr = map->m_la >> vi->chunkbits; - pos = ALIGN(erofs_iloc(inode) + vi->inode_isize + - vi->xattr_isize, unit) + unit * chunknr; - - idx = erofs_read_metabuf(&buf, sb, pos, erofs_inode_in_metabox(inode)); - if (IS_ERR(idx)) { - err = PTR_ERR(idx); - goto out; - } - map->m_la = chunknr << vi->chunkbits; - map->m_llen = min_t(erofs_off_t, 1UL << vi->chunkbits, - round_up(inode->i_size - map->m_la, blksz)); - if (vi->chunkformat & EROFS_CHUNK_FORMAT_INDEXES) { - addrmask = (vi->chunkformat & EROFS_CHUNK_FORMAT_48BIT) ? - BIT_ULL(48) - 1 : BIT_ULL(32) - 1; - startblk = (((u64)le16_to_cpu(idx->startblk_hi) << 32) | - le32_to_cpu(idx->startblk_lo)) & addrmask; - if ((startblk ^ EROFS_NULL_ADDR) & addrmask) { - map->m_deviceid = le16_to_cpu(idx->device_id) & - EROFS_SB(sb)->device_id_mask; - map->m_pa = erofs_pos(sb, startblk); - map->m_flags = EROFS_MAP_MAPPED; - } - } else { - startblk = le32_to_cpu(*(__le32 *)idx); - if (startblk != (u32)EROFS_NULL_ADDR) { - map->m_pa = erofs_pos(sb, startblk); - map->m_flags = EROFS_MAP_MAPPED; + if (erofs_blkoff(sb, map->m_pa) + map->m_llen > + sb->s_blocksize) { + erofs_err(sb, "inline data across blocks @ nid %llu", vi->nid); + return -EFSCORRUPTED; + } } } - erofs_put_metabuf(&buf); out: - if (!err) { - map->m_plen = map->m_llen; - /* inline data should be located in the same meta block */ - if ((map->m_flags & EROFS_MAP_META) && - erofs_blkoff(sb, map->m_pa) + map->m_plen > blksz) { - erofs_err(sb, "inline data across blocks @ nid %llu", vi->nid); - DBG_BUGON(1); - return -EFSCORRUPTED; - } - } + map->m_plen = err ? 0 : map->m_llen; trace_erofs_map_blocks_exit(inode, map, 0, err); return err; } From dece79032f529d2c9fdbf63a9f2fc32244722775 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:42:03 +0200 Subject: [PATCH 3838/5214] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Ensure that GPIO pin resource arrays are safely bounded before accessing indices. Add explicit bounds checking in acpi_request_own_gpiod(), acpi_gpio_irq_is_wake(), and acpi_gpiochip_alloc_event() to prevent out-of-bounds array reads if the ACPI namespace provides malformed or empty pin tables. This change addresses potential safety issues arising from inconsistent or invalid ACPI pin tables. It does not alter functional behavior in well-formed tables. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260610154204.110379-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-core.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 1a762a2988b7..b09f89832890 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -316,10 +316,17 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, unsigned int index, const char *label) { - int polarity = GPIO_ACTIVE_HIGH; - enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); - unsigned int pin = agpio->pin_table[index]; + enum gpiod_flags flags; struct gpio_desc *desc; + unsigned int pin; + int polarity; + + if (index >= agpio->pin_table_length) + return ERR_PTR(-EINVAL); + + pin = agpio->pin_table[index]; + polarity = GPIO_ACTIVE_HIGH; + flags = acpi_gpio_to_gpiod_flags(agpio, polarity); desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); if (IS_ERR(desc)) @@ -333,7 +340,12 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, static bool acpi_gpio_irq_is_wake(struct device *parent, const struct acpi_resource_gpio *agpio) { - unsigned int pin = agpio->pin_table[0]; + unsigned int pin; + + if (agpio->pin_table_length == 0) + return false; + + pin = agpio->pin_table[0]; if (agpio->wake_capable != ACPI_WAKE_CAPABLE) return false; @@ -363,6 +375,9 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, if (!acpi_gpio_get_irq_resource(ares, &agpio)) return AE_OK; + if (agpio->pin_table_length == 0) + return AE_OK; + handle = ACPI_HANDLE(chip->parent); pin = agpio->pin_table[0]; From ae9f812df3149729643d27d2af488c112f62af9a Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:42:04 +0200 Subject: [PATCH 3839/5214] gpiolib: acpi: Prevent out-of-bounds pin access in OperationRegion handler The ACPI GPIO OperationRegion handler receives pin offsets as a 64-bit address. Previously, this value could be assigned to a pin index without validation, potentially causing out-of-bounds access if the ACPI table provides an invalid offset. This patch explicitly checks that the 64-bit address is less than agpio->pin_table_length before using it, returning AE_BAD_PARAMETER if the check fails. Additionally, it makes the length calculation overflow-safe and ensures proper unsigned types for loop counters. This corrects the commit message from v5 to accurately reflect the underlying issue, removing references to truncation or wrap-around, which do not occur in ACPICA. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260610154204.110379-3-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi-core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index b09f89832890..220f0ac4204e 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -1098,10 +1098,10 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_chip *chip = achip->chip; struct acpi_resource_gpio *agpio; struct acpi_resource *ares; - u16 pin_index = address; + unsigned int length; acpi_status status; - int length; - int i; + unsigned int i; + u16 pin_index; status = acpi_buffer_to_resource(achip->conn_info.connection, achip->conn_info.length, &ares); @@ -1121,7 +1121,14 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, return AE_BAD_PARAMETER; } - length = min(agpio->pin_table_length, pin_index + bits); + /* address represents GPIO pin index in connection table */ + if (address >= agpio->pin_table_length) { + ACPI_FREE(ares); + return AE_BAD_PARAMETER; + } + + pin_index = address; + length = min_t(unsigned int, agpio->pin_table_length, pin_index + bits); for (i = pin_index; i < length; ++i) { unsigned int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; From 111bb7f9f4a90b32e495d70a607c67b137f3074a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Rodr=C3=ADguez?= Date: Thu, 11 Jun 2026 12:48:56 +0200 Subject: [PATCH 3840/5214] i2c: stm32f7: truncate clock period instead of rounding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stm32f7_i2c_compute_timing() derives the I2C clock source period (i2cclk) with DIV_ROUND_CLOSEST, which may round it up. When the period is overestimated, all timings computed from it (SCLDEL, SDADEL, SCLL, SCLH) come out shorter on the wire than calculated, and the resulting bus rate can exceed the requested speed, violating the I2C specification minimums for tLOW and tHIGH. For example, with a 104.45 MHz clock source (e.g. PCLK1, the reset-default I2C clock source on STM32MP1), i2cclk is rounded from 9.574 ns up to 10 ns. Requesting a 400 kHz fast mode bus with 72/27 ns rise/fall times and no analog/digital filters then produces an actual bus rate of 415.6 kHz with tLOW = 1254 ns, violating both the 400 kHz maximum rate and the 1300 ns tLOW minimum of the specification. Truncate the period instead, so that it can only be underestimated. The error then falls on the safe side: the programmed timings come out slightly longer than computed and the bus runs marginally below the target rate (375.3 kHz in the example above) while meeting the specification. i2cbus is left rounded-to-closest: it is only used as the target of the clk_error comparison and is never multiplied into the programmed timings, so nearest rounding remains accurate there. Fixes: aeb068c57214 ("i2c: i2c-stm32f7: add driver") Signed-off-by: Guillermo Rodríguez Cc: # v4.14+ Acked-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260611104857.242153-1-guille.rodriguez@gmail.com --- drivers/i2c/busses/i2c-stm32f7.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 16c6e61c7e11..d6d993b436cb 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -464,8 +464,13 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, { struct stm32f7_i2c_spec *specs; u32 p_prev = STM32F7_PRESC_MAX; - u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, - setup->clock_src); + /* + * Truncate instead of rounding to closest: if the clock period is + * overestimated, the computed SCL timings will come out shorter on + * the wire, which can push the bus above the target rate and below + * the spec's tLOW/tHIGH minimums. + */ + u32 i2cclk = NSEC_PER_SEC / setup->clock_src; u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq); u32 clk_error_prev = i2cbus; From 4a60127debb9e370d6c0e22a307326b624a141f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 15 Jun 2026 10:37:26 -0300 Subject: [PATCH 3841/5214] ALSA: compress: Fix task creation error unwind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_compr_task_new() allocates the driver task before validating the returned DMA buffers and reserving file descriptors. When either of those later steps fails, the core frees its task wrapper and DMA-buffer references without calling the driver's task_free() callback. Any driver resources allocated by task_create() are therefore leaked. The dual-fd allocation path also jumps to cleanup without storing the negative get_unused_fd_flags() result in retval. Since retval still contains the successful task_create() return value, TASK_CREATE can incorrectly report success although the task was discarded. Preserve the fd allocation errors and call task_free() when failure occurs after a successful task_create() callback. Fixes: 04177158cf98 ("ALSA: compress_offload: introduce accel operation mode") Fixes: 3d3f43fab4cf ("ALSA: compress_offload: improve file descriptors installation for dma-buf") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260615-alsa-compress-task-unwind-v1-1-39e8ad3ddb27@gmail.com Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index fd63d219bf86..ea699491f0c3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1083,15 +1083,18 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ file descriptors are allocated before fd_install() */ if (!task->input || !task->input->file || !task->output || !task->output->file) { retval = -EINVAL; - goto cleanup; + goto free_driver_task; } fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC); - if (fd_i < 0) - goto cleanup; + if (fd_i < 0) { + retval = fd_i; + goto free_driver_task; + } fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC); if (fd_o < 0) { + retval = fd_o; put_unused_fd(fd_i); - goto cleanup; + goto free_driver_task; } /* keep dmabuf reference until freed with task free ioctl */ get_dma_buf(task->input); @@ -1103,6 +1106,8 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ list_add_tail(&task->list, &stream->runtime->tasks); stream->runtime->total_tasks++; return 0; +free_driver_task: + stream->ops->task_free(stream, task); cleanup: snd_compr_task_free(task); return retval; From f7c4968ae3af3e819428da5416c2dfd361473f5c Mon Sep 17 00:00:00 2001 From: Galen Hassen Date: Tue, 16 Jun 2026 10:32:57 -0700 Subject: [PATCH 3842/5214] ALSA: hda/conexant: Add pin config quirk for Lenovo IdeaPad Slim 5 16AKP10 The Lenovo IdeaPad Slim 5 16AKP10 (PCI SSID 17aa:38b6) uses the Conexant SN6140 codec. The internal microphone is on pin 0x1a but the BIOS configures it with pin default 0x95a60120, which includes a jack detection bit that causes the kernel to treat it as an unplugged external mic rather than a fixed internal mic. Add a pin config quirk that overrides pin 0x1a to 0x95a60130, setting the connectivity bits to indicate a fixed/always-connected device. This allows the internal microphone to be correctly identified and used. Signed-off-by: Galen Hassen Link: https://patch.msgid.link/20260616173257.37373-1-rwekyes@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/conexant.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index e3b6aaabe3a9..3d92262763f6 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -291,6 +291,7 @@ enum { CXT_FIXUP_HEADSET_MIC, CXT_FIXUP_HP_MIC_NO_PRESENCE, CXT_PINCFG_SWS_JS201D, + CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10, CXT_PINCFG_TOP_SPEAKER, CXT_FIXUP_HP_A_U, CXT_FIXUP_ACER_SWIFT_HP, @@ -826,6 +827,12 @@ static const struct hda_pintbl cxt_pincfg_lemote[] = { {} }; +/* Lenovo IdeaPad Slim 5 16AKP10 with SN6140 */ +static const struct hda_pintbl cxt_pincfg_lenovo_ideapad_slim5_16akp10[] = { + { 0x1a, 0x95a60130 }, /* Internal mic, fixed/always-connected */ + {} +}; + /* SuoWoSi/South-holding JS201D with sn6140 */ static const struct hda_pintbl cxt_pincfg_sws_js201d[] = { { 0x16, 0x03211040 }, /* hp out */ @@ -1006,6 +1013,10 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_sws_js201d, }, + [CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_ideapad_slim5_16akp10, + }, [CXT_PINCFG_TOP_SPEAKER] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -1114,6 +1125,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), + SND_PCI_QUIRK(0x17aa, 0x38b6, "Lenovo IdeaPad Slim 5 16AKP10", CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10), SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), From 7d69804a35103a50852eae41bfe6a2e0061c68fd Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 19:59:16 +0800 Subject: [PATCH 3843/5214] ALSA: usb-audio: qcom: Free sideband sg_table objects The Qualcomm USB audio offload driver obtains an endpoint transfer-ring table by calling xhci_sideband_get_endpoint_buffer(). This getter passes the endpoint ring to xhci_ring_to_sgtable(), which allocates the outer struct sg_table with kzalloc_obj(*sgt). The event-ring path is equivalent: xhci_sideband_get_event_buffer() also returns the result of xhci_ring_to_sgtable(). Inside xhci_ring_to_sgtable(), sg_alloc_table_from_pages() separately allocates the scatterlist storage referenced by sgt->sgl. The returned object therefore has two allocation layers: the outer struct sg_table and its internal scatterlist storage. The Qualcomm caller only invokes sg_free_table(sgt). sg_free_table() releases the scatterlist storage owned by the table, but it does not free the separately allocated outer struct sg_table. The local sgt pointer is then discarded, so every successful endpoint or event-ring query leaks the outer object. Call kfree(sgt) after sg_free_table(sgt) in both setup paths, after the required page and DMA addresses have been copied out. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Signed-off-by: Xu Rao Link: https://patch.msgid.link/90B353283AA150C4+20260616115916.1222915-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/usb/qcom/qc_audio_offload.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index a3f90cc7c6ca..436d6821c5c9 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1160,6 +1160,7 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs, tr_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); /* data transfer ring */ iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa, @@ -1229,6 +1230,7 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, er_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa, PAGE_SIZE); From 8956950dab22fbaefe92ca1980728165c5da793d Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Wed, 17 Jun 2026 10:52:34 +0800 Subject: [PATCH 3844/5214] ALSA: usb-audio: Add quirk flags for SC13A The SC13A ( VID 0x1ff7, PID 0x0f81) not support reading the current sample rate and results in an error message printed to kmsg. Set QUIRK_FLAG_GET_SAMPLE_RATE to skip the sample rate check. Quirky device sample: usb 3-5.2.4.1: new high-speed USB device number 11 using xhci_hcd usb 3-5.2.4.1: New USB device found, idVendor=1ff7, idProduct=0f81 usb 3-5.2.4.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 3-5.2.4.1: Product: SC13A usb 3-5.2.4.1: Manufacturer: Linux Foundation usb 3-5.2.4.1: SerialNumber: 000002 usb 3-5.2.4.1: Found UVC 1.50 device SC13A (1ff7:0f81) usb 3-5.2.4.1: 3:1: cannot get freq at ep 0x86 usb 3-5.2.4.1: Warning! Unlikely big volume range (=4096), cval->res is probably wrong. usb 3-5.2.4.1: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1 usbcore: registered new interface driver snd-usb-audio usb 3-5.2.4.1: 3:1: cannot get freq at ep 0x86 usb 3-5.2.4.1: 3:1: cannot get freq at ep 0x86 Signed-off-by: Ai Chao Link: https://patch.msgid.link/20260617025234.3344935-1-aichao@kylinos.cn Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 30f6d572e4d1..1cb588691e16 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2413,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x1ff7, 0x0f81, /* SC13A Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */ QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */ From 9e6febe7891316182bcd80cb46b745a08ad0cacf Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 17 Jun 2026 08:42:18 +0200 Subject: [PATCH 3845/5214] ALSA: sh: Use more common error handling code in snd_aica_probe() Use an additional label so that a bit of exception handling can be better reused at the end of this function implementation. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://patch.msgid.link/47413de1-015b-4543-8e8c-25e41dfa9e39@web.de Signed-off-by: Takashi Iwai --- sound/sh/aica.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 9438c3a68ee9..8196e1bf0416 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -564,10 +564,9 @@ static int snd_aica_probe(struct platform_device *devptr) return -ENOMEM; err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER, THIS_MODULE, 0, &dreamcastcard->card); - if (unlikely(err < 0)) { - kfree(dreamcastcard); - return err; - } + if (unlikely(err < 0)) + goto free_card; + strscpy(dreamcastcard->card->driver, "snd_aica"); strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); strscpy(dreamcastcard->card->longname, @@ -593,6 +592,7 @@ static int snd_aica_probe(struct platform_device *devptr) return 0; freedreamcast: snd_card_free(dreamcastcard->card); +free_card: kfree(dreamcastcard); return err; } From 218cfe364b55b2768221629bd4a69ad190b7fbbc Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:14:50 +0800 Subject: [PATCH 3846/5214] i2c: imx-lpi2c: mark I2C adapter when hardware is powered down On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 1ee867e465c1 ("i2c: imx-lpi2c: add target mode support") Signed-off-by: Carlos Song Cc: # v6.14+ Acked-by: Mukesh Savaliya Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525031450.3183421-1-carlos.song@oss.nxp.com --- drivers/i2c/busses/i2c-imx-lpi2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index cd4da50c4dd9..e6c24a9d934d 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -1646,7 +1646,18 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev) static int __maybe_unused lpi2c_suspend_noirq(struct device *dev) { - return pm_runtime_force_suspend(dev); + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&lpi2c_imx->adapter); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return ret; + } + + return 0; } static int __maybe_unused lpi2c_resume_noirq(struct device *dev) @@ -1666,6 +1677,8 @@ static int __maybe_unused lpi2c_resume_noirq(struct device *dev) if (lpi2c_imx->target) lpi2c_imx_target_init(lpi2c_imx); + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return 0; } From ddd3d0132920319ac426e12456013eadbae67e15 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 12 Jun 2026 12:58:59 +0800 Subject: [PATCH 3847/5214] xfrm: Fix xfrm state cache insertion race The xfrm input state cache insertion code checks the validity of the state before acquiring the global xfrm_state_lock. Thus it's possible for someone else to kill the state after it passed the validity check, and then the insertion will add the dead state to the cache. Fix this by moving the validity check inside the lock. This entire function is called on the input path, where BH must be off (e.g., the caller of this function xfrm_input acquires its spinlocks without disabling BH). So there is no need to disable BH here or take the RCU read lock. Remove both and replace them with an assertion that trips if BH is accidentally enabled on some future calling path. Fixes: 81a331a0e72d ("xfrm: Add an inbound percpu state cache.") Reported-by: Zero Day Initiative Signed-off-by: Herbert Xu Reviewed-by: Simon Horman Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index d8457ceaf28c..9e87f7028201 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1207,9 +1207,11 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, struct hlist_head *state_cache_input; struct xfrm_state *x = NULL; + /* BH is always disabled on the input path. */ + lockdep_assert_in_softirq(); + state_cache_input = raw_cpu_ptr(net->xfrm.state_cache_input); - rcu_read_lock(); hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) { if (x->props.family != family || x->id.spi != spi || @@ -1227,20 +1229,25 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, xfrm_hash_ptrs_get(net, &state_ptrs); x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family); - - if (x && x->km.state == XFRM_STATE_VALID) { - spin_lock_bh(&net->xfrm.xfrm_state_lock); - if (hlist_unhashed(&x->state_cache_input)) { + if (x) { + spin_lock(&net->xfrm.xfrm_state_lock); + if (x->km.state != XFRM_STATE_VALID) { + /* + * The state is about to be destroyed. + * + * Don't add it to the cache but still + * return it to the caller. + */ + } else if (hlist_unhashed(&x->state_cache_input)) { hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } else { hlist_del_rcu(&x->state_cache_input); hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } - spin_unlock_bh(&net->xfrm.xfrm_state_lock); + spin_unlock(&net->xfrm.xfrm_state_lock); } out: - rcu_read_unlock(); return x; } EXPORT_SYMBOL(xfrm_input_state_lookup); From 68de007d5ac9df0e3f4f187a179c5c842bb5a2be Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 05:56:34 +0000 Subject: [PATCH 3848/5214] xfrm: annotate data-races around xfrm_policy_count[] and xfrm_policy_default[] KCSAN reported a data race involving net->xfrm.policy_count access. Add missing READ_ONCE()/WRITE_ONCE() annotations on xfrm_policy_count and xfrm_policy_default. Fixes: 2518c7c2b3d7 ("[XFRM]: Hash policies when non-prefixed.") Reported-by: syzbot+d85ba1c732720b9a4097@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2b9e96.99669fcc.12a77b.0006.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 8 ++++---- net/xfrm/xfrm_policy.c | 24 ++++++++++++------------ net/xfrm/xfrm_user.c | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 874409127e29..35a743129329 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1250,8 +1250,8 @@ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb, int dir) { - if (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) - return net->xfrm.policy_default[dir] == XFRM_USERPOLICY_ACCEPT; + if (!READ_ONCE(net->xfrm.policy_count[dir]) && !secpath_exists(skb)) + return READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_ACCEPT; return false; } @@ -1351,8 +1351,8 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct net *net = dev_net(skb->dev); - if (!net->xfrm.policy_count[XFRM_POLICY_OUT] && - net->xfrm.policy_default[XFRM_POLICY_OUT] == XFRM_USERPOLICY_ACCEPT) + if (!READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]) && + READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]) == XFRM_USERPOLICY_ACCEPT) return true; return (skb_dst(skb)->flags & DST_NOXFRM) || diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 959544425692..1f4afd580105 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -685,7 +685,7 @@ static void xfrm_byidx_resize(struct net *net) static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total) { - unsigned int cnt = net->xfrm.policy_count[dir]; + unsigned int cnt = READ_ONCE(net->xfrm.policy_count[dir]); unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; if (total) @@ -711,12 +711,12 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total) void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) { - si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; - si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; - si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; - si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]; - si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]; - si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; + si->incnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN]); + si->outcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]); + si->fwdcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD]); + si->inscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]); + si->outscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]); + si->fwdscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]); si->spdhcnt = net->xfrm.policy_idx_hmask; si->spdhmcnt = xfrm_policy_hashmax; } @@ -2318,7 +2318,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) } list_add(&pol->walk.all, &net->xfrm.policy_all); - net->xfrm.policy_count[dir]++; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] + 1); xfrm_pol_hold(pol); } @@ -2337,7 +2337,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, } list_del_init(&pol->walk.all); - net->xfrm.policy_count[dir]--; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] - 1); return pol; } @@ -3222,7 +3222,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, /* To accelerate a bit... */ if (!if_id && ((dst_orig->flags & DST_NOXFRM) || - !net->xfrm.policy_count[XFRM_POLICY_OUT])) + !READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]))) goto nopol; xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id); @@ -3296,7 +3296,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, nopol: if ((!dst_orig->dev || !(dst_orig->dev->flags & IFF_LOOPBACK)) && - net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { err = -EPERM; goto error; } @@ -3750,7 +3750,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, const bool is_crypto_offload = sp && (xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO); - if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + if (READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); return 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 3b1cf29bc402..61eb5de33b87 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2485,9 +2485,9 @@ static int xfrm_notify_userpolicy(struct net *net) } up = nlmsg_data(nlh); - up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(skb, nlh); @@ -2511,13 +2511,13 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_userpolicy_default *up = nlmsg_data(nlh); if (xfrm_userpolicy_is_valid(up->in)) - net->xfrm.policy_default[XFRM_POLICY_IN] = up->in; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN], up->in); if (xfrm_userpolicy_is_valid(up->fwd)) - net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD], up->fwd); if (xfrm_userpolicy_is_valid(up->out)) - net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT], up->out); rt_genid_bump_all(net); @@ -2547,9 +2547,9 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, } r_up = nlmsg_data(r_nlh); - r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + r_up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + r_up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + r_up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(r_skb, r_nlh); return nlmsg_unicast(xfrm_net_nlsk(net, skb), r_skb, portid); From 007800408002d871f5699bdb944f985896730b8f Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 12 Jun 2026 16:11:39 +0200 Subject: [PATCH 3849/5214] espintcp: use sk_msg_free_partial to fix partial send sk_msg_free_partial() ensures consistency of the skmsg at every iteration, without having to manually handle uncharges and offsets. This simplifies the code, and fixes some bugs in skmsg accounting when we don't send the full contents. Cc: stable@vger.kernel.org Fixes: e27cca96cd68 ("xfrm: add espintcp (RFC 8229)") Reported-by: Aaron Esau Reported-by: Yiming Qian Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert --- net/xfrm/espintcp.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index d9035546375e..374e1b964438 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -212,43 +212,23 @@ static int espintcp_sendskmsg_locked(struct sock *sk, struct sk_msg *skmsg = &emsg->skmsg; bool more = flags & MSG_MORE; struct scatterlist *sg; - int done = 0; int ret; - sg = &skmsg->sg.data[skmsg->sg.start]; do { struct bio_vec bvec; - size_t size = sg->length - emsg->offset; - int offset = sg->offset + emsg->offset; - struct page *p; - - emsg->offset = 0; + sg = &skmsg->sg.data[skmsg->sg.start]; if (sg_is_last(sg) && !more) msghdr.msg_flags &= ~MSG_MORE; - p = sg_page(sg); -retry: - bvec_set_page(&bvec, p, size, offset); - iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, size); - ret = tcp_sendmsg_locked(sk, &msghdr, size); - if (ret < 0) { - emsg->offset = offset - sg->offset; - skmsg->sg.start += done; + bvec_set_page(&bvec, sg_page(sg), sg->length, sg->offset); + iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, sg->length); + ret = tcp_sendmsg_locked(sk, &msghdr, sg->length); + if (ret < 0) return ret; - } - if (ret != size) { - offset += ret; - size -= ret; - goto retry; - } - - done++; - put_page(p); - sk_mem_uncharge(sk, sg->length); - sg = sg_next(sg); - } while (sg); + sk_msg_free_partial(sk, skmsg, ret); + } while (skmsg->sg.size); memset(emsg, 0, sizeof(*emsg)); From 40f0b1047918539f0b0f795ac65e35336b4c2c78 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 15 Jun 2026 09:02:37 +0000 Subject: [PATCH 3850/5214] xfrm: validate selector family and prefixlen during match syzbot reported a shift-out-of-bounds in xfrm_selector_match() due to AF_UNSPEC selector with large prefixlen (e.g. 128) matched against IPv4 flow (when XFRM_STATE_AF_UNSPEC is set). Fix this by: - Rejecting mismatched families in xfrm_selector_match. - Returning false in addr4_match if prefixlen > 32. - Returning false in addr_match if prefixlen > 128 (prevents overflow). Fixes: 3f0ab59e6537 ("xfrm: validate new SA's prefixlen using SA family when sel.family is unset") Reported-by: syzbot+9383b1ff0df4b29ca5e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2fbe35.be3f099c.2836ae.0018.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 7 +++++++ net/xfrm/xfrm_policy.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 35a743129329..f8c909b0f0c3 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -943,6 +943,9 @@ static inline bool addr_match(const void *token1, const void *token2, unsigned int pdw; unsigned int pbi; + if (prefixlen > 128) + return false; + pdw = prefixlen >> 5; /* num of whole u32 in prefix */ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ @@ -967,6 +970,10 @@ static inline bool addr4_match(__be32 a1, __be32 a2, u8 prefixlen) /* C99 6.5.7 (3): u32 << 32 is undefined behaviour */ if (sizeof(long) == 4 && prefixlen == 0) return true; + + if (prefixlen > 32) + return false; + return !((a1 ^ a2) & htonl(~0UL << (32 - prefixlen))); } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 1f4afd580105..639934f30016 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -242,6 +242,9 @@ __xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family) { + if (family != sel->family && sel->family != AF_UNSPEC) + return false; + switch (family) { case AF_INET: return __xfrm4_selector_match(sel, fl); From 2cd517ed4eaf728631eafce645ebf63d0749c679 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:35 +0530 Subject: [PATCH 3851/5214] dt-bindings: mfd: Add documentation for S2MU005 PMIC Samsung's S2MU005 PMIC includes subdevices for a charger, an MUIC (Micro USB Interface Controller), and flash and RGB LED controllers. Add the compatible and documentation for the S2MU005 PMIC. Also, add an example for nodes for supported sub-devices, i.e. MUIC, flash LEDs, and RGB LEDs. Charger sub-device uses the node of the parent. Signed-off-by: Kaustabh Chakraborty Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-3-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- .../bindings/mfd/samsung,s2mu005-pmic.yaml | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/samsung,s2mu005-pmic.yaml diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mu005-pmic.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mu005-pmic.yaml new file mode 100644 index 000000000000..aff68c035b38 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/samsung,s2mu005-pmic.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/samsung,s2mu005-pmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung S2MU005 Power Management IC + +maintainers: + - Kaustabh Chakraborty + +description: | + The S2MU005 is a companion power management IC which includes subdevices for + a charger controller, an MUIC (Micro USB Interface Controller), and flash and + RGB LED controllers. + +allOf: + - $ref: /schemas/power/supply/power-supply.yaml# + +properties: + compatible: + const: samsung,s2mu005-pmic + + flash: + $ref: /schemas/leds/samsung,s2mu005-flash.yaml# + description: + Child node describing flash LEDs. + + interrupts: + maxItems: 1 + + muic: + $ref: /schemas/extcon/samsung,s2mu005-muic.yaml# + description: + Child node describing MUIC device. + + multi-led: + type: object + + allOf: + - $ref: /schemas/leds/leds-class-multicolor.yaml# + + properties: + compatible: + const: samsung,s2mu005-rgb + + required: + - compatible + + unevaluatedProperties: false + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@3d { + compatible = "samsung,s2mu005-pmic"; + reg = <0x3d>; + interrupt-parent = <&gpa2>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + + monitored-battery = <&battery>; + + flash { + compatible = "samsung,s2mu005-flash"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_FLASH; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_FLASH; + function-enumerator = <1>; + }; + }; + + muic { + compatible = "samsung,s2mu005-muic"; + + connector { + compatible = "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + }; + + port { + muic_to_usb: endpoint { + remote-endpoint = <&usb_to_muic>; + }; + }; + }; + + multi-led { + compatible = "samsung,s2mu005-rgb"; + color = ; + function = LED_FUNCTION_INDICATOR; + linux,default-trigger = "pattern"; + }; + }; + }; From e491ebea157ff3ba18483ea0f82a9536dc393dec Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:36 +0530 Subject: [PATCH 3852/5214] mfd: sec: Add support for S2MU005 PMIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Samsung's S2MU005 PMIC includes subdevices for a charger, an MUIC (Micro USB Interface Controller), and flash and RGB LED controllers. S2MU005's interrupt registers divided into three domains, each for the charger, flash LEDs, and the MUIC, packed into a single regmap IRQ chip construct. In devices other than S2MPG1X, the revision can be retrieved from the first register of the PMIC regmap. In S2MU005 however, the location is in offset 0x73. Introduce a switch-case block to allow selecting the REG_ID register. S2MU005 also has a field mask for the revision. Apply it using FIELD_GET() and get the extracted value. Add initial support for S2MU005 in the PMIC driver, along with its three interrupt chips, and support for allowing to fetch revision based on the device variant. Co-developed-by: Łukasz Lebiedziński Signed-off-by: Łukasz Lebiedziński Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-4-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- drivers/mfd/sec-common.c | 34 ++- drivers/mfd/sec-i2c.c | 29 +++ drivers/mfd/sec-irq.c | 73 ++++++ include/linux/mfd/samsung/core.h | 1 + include/linux/mfd/samsung/irq.h | 66 ++++++ include/linux/mfd/samsung/s2mu005.h | 332 ++++++++++++++++++++++++++++ 6 files changed, 529 insertions(+), 6 deletions(-) create mode 100644 include/linux/mfd/samsung/s2mu005.h diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c index bd8b5f968689..22f6c74eb6c0 100644 --- a/drivers/mfd/sec-common.c +++ b/drivers/mfd/sec-common.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -105,22 +106,39 @@ static const struct mfd_cell s2mpu05_devs[] = { MFD_CELL_RES("s2mps15-rtc", s2mpu05_rtc_resources), }; +static const struct resource s2mu005_muic_resources[] = { + DEFINE_RES_IRQ_NAMED(S2MU005_IRQ_MUIC_ATTACH, "attach"), + DEFINE_RES_IRQ_NAMED(S2MU005_IRQ_MUIC_DETACH, "detach"), +}; + +static const struct mfd_cell s2mu005_devs[] = { + MFD_CELL_NAME("s2mu005-charger"), + MFD_CELL_OF("s2mu005-flash", NULL, NULL, 0, 0, "samsung,s2mu005-flash"), + MFD_CELL_OF("s2mu005-muic", s2mu005_muic_resources, NULL, 0, 0, "samsung,s2mu005-muic"), + MFD_CELL_OF("s2mu005-rgb", NULL, NULL, 0, 0, "samsung,s2mu005-rgb"), +}; + static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic) { - unsigned int val; + unsigned int reg, mask, val; - /* For s2mpg1x, the revision is in a different regmap */ switch (sec_pmic->device_type) { case S2MPG10: case S2MPG11: + /* For s2mpg1x, the revision is in a different regmap */ return; - default: + case S2MU005: + reg = S2MU005_REG_ID; + mask = S2MU005_ID_MASK; break; + default: + /* For other device types, REG_ID is always the first register. */ + reg = S2MPS11_REG_ID; + mask = ~0; } - /* For each device type, the REG_ID is always the first register */ - if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val)) - dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val); + if (!regmap_read(sec_pmic->regmap_pmic, reg, &val)) + dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", field_get(mask, val)); } static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic) @@ -250,6 +268,10 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq, sec_devs = s2mpu05_devs; num_sec_devs = ARRAY_SIZE(s2mpu05_devs); break; + case S2MU005: + sec_devs = s2mu005_devs; + num_sec_devs = ARRAY_SIZE(s2mu005_devs); + break; default: return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n", diff --git a/drivers/mfd/sec-i2c.c b/drivers/mfd/sec-i2c.c index 3132b849b4bc..d8609886fcc8 100644 --- a/drivers/mfd/sec-i2c.c +++ b/drivers/mfd/sec-i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,19 @@ static bool s2mpu02_volatile(struct device *dev, unsigned int reg) } } +static bool s2mu005_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case S2MU005_REG_CHGR_INT1M: + case S2MU005_REG_FLED_INT1M: + case S2MU005_REG_MUIC_INT1M: + case S2MU005_REG_MUIC_INT2M: + return false; + default: + return true; + } +} + static const struct regmap_config s2dos05_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -130,6 +144,15 @@ static const struct regmap_config s2mpu05_regmap_config = { .val_bits = 8, }; +static const struct regmap_config s2mu005_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MU005_REG_MUIC_LDOADC_H, + .volatile_reg = s2mu005_volatile, + .cache_type = REGCACHE_FLAT_S, +}; + static const struct regmap_config s5m8767_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -203,6 +226,11 @@ static const struct sec_pmic_i2c_platform_data s2mpu05_data = { .device_type = S2MPU05, }; +static const struct sec_pmic_i2c_platform_data s2mu005_data = { + .regmap_cfg = &s2mu005_regmap_config, + .device_type = S2MU005, +}; + static const struct sec_pmic_i2c_platform_data s5m8767_data = { .regmap_cfg = &s5m8767_regmap_config, .device_type = S5M8767X, @@ -217,6 +245,7 @@ static const struct of_device_id sec_pmic_i2c_of_match[] = { { .compatible = "samsung,s2mps15-pmic", .data = &s2mps15_data, }, { .compatible = "samsung,s2mpu02-pmic", .data = &s2mpu02_data, }, { .compatible = "samsung,s2mpu05-pmic", .data = &s2mpu05_data, }, + { .compatible = "samsung,s2mu005-pmic", .data = &s2mu005_data, }, { .compatible = "samsung,s5m8767-pmic", .data = &s5m8767_data, }, { }, }; diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index 133188391f7c..42862807be1a 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "sec-core.h" @@ -223,6 +224,65 @@ static const struct regmap_irq s2mpu05_irqs[] = { REGMAP_IRQ_REG(S2MPU05_IRQ_TSD, 2, S2MPU05_IRQ_TSD_MASK), }; +static const struct regmap_irq s2mu005_irqs[] = { + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_DETBAT, 0, S2MU005_IRQ_CHGR_DETBAT_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_BAT, 0, S2MU005_IRQ_CHGR_BAT_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_IVR, 0, S2MU005_IRQ_CHGR_IVR_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_EVENT, 0, S2MU005_IRQ_CHGR_EVENT_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_CHG, 0, S2MU005_IRQ_CHGR_CHG_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_VMID, 0, S2MU005_IRQ_CHGR_VMID_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_WCIN, 0, S2MU005_IRQ_CHGR_WCIN_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_CHGR_VBUS, 0, S2MU005_IRQ_CHGR_VBUS_MASK), + + REGMAP_IRQ_REG(S2MU005_IRQ_FLED_LBPROT, 1, S2MU005_IRQ_FLED_LBPROT_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_FLED_OPENCH2, 1, S2MU005_IRQ_FLED_OPENCH2_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_FLED_OPENCH1, 1, S2MU005_IRQ_FLED_OPENCH1_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_FLED_SHORTCH2, 1, S2MU005_IRQ_FLED_SHORTCH2_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_FLED_SHORTCH1, 1, S2MU005_IRQ_FLED_SHORTCH1_MASK), + + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_ATTACH, 2, S2MU005_IRQ_MUIC_ATTACH_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_DETACH, 2, S2MU005_IRQ_MUIC_DETACH_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_KP, 2, S2MU005_IRQ_MUIC_KP_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_LKP, 2, S2MU005_IRQ_MUIC_LKP_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_LKR, 2, S2MU005_IRQ_MUIC_LKR_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_RIDCHG, 2, S2MU005_IRQ_MUIC_RIDCHG_MASK), + + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_VBUSON, 3, S2MU005_IRQ_MUIC_VBUSON_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_RSVD, 3, S2MU005_IRQ_MUIC_RSVD_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_ADC, 3, S2MU005_IRQ_MUIC_ADC_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_STUCK, 3, S2MU005_IRQ_MUIC_STUCK_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_STUCKRCV, 3, S2MU005_IRQ_MUIC_STUCKRCV_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_MHDL, 3, S2MU005_IRQ_MUIC_MHDL_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_AVCHG, 3, S2MU005_IRQ_MUIC_AVCHG_MASK), + REGMAP_IRQ_REG(S2MU005_IRQ_MUIC_VBUSOFF, 3, S2MU005_IRQ_MUIC_VBUSOFF_MASK), +}; + +static unsigned int s2mu005_irq_get_reg(struct regmap_irq_chip_data *data, + unsigned int base, int index) +{ + const unsigned int irqf_regs[] = { + S2MU005_REG_CHGR_INT1, + S2MU005_REG_FLED_INT1, + S2MU005_REG_MUIC_INT1, + S2MU005_REG_MUIC_INT2, + }; + const unsigned int mask_regs[] = { + S2MU005_REG_CHGR_INT1M, + S2MU005_REG_FLED_INT1M, + S2MU005_REG_MUIC_INT1M, + S2MU005_REG_MUIC_INT2M, + }; + + switch (base) { + case S2MU005_REG_CHGR_INT1: + return irqf_regs[index]; + case S2MU005_REG_CHGR_INT1M: + return mask_regs[index]; + } + + return base; +} + static const struct regmap_irq s5m8767_irqs[] = { REGMAP_IRQ_REG(S5M8767_IRQ_PWRR, 0, S5M8767_IRQ_PWRR_MASK), REGMAP_IRQ_REG(S5M8767_IRQ_PWRF, 0, S5M8767_IRQ_PWRF_MASK), @@ -337,6 +397,16 @@ static const struct regmap_irq_chip s2mpu05_irq_chip = { .ack_base = S2MPU05_REG_INT1, }; +static const struct regmap_irq_chip s2mu005_irq_chip = { + .name = "s2mu005", + .irqs = s2mu005_irqs, + .num_irqs = ARRAY_SIZE(s2mu005_irqs), + .num_regs = 4, + .status_base = S2MU005_REG_CHGR_INT1, + .mask_base = S2MU005_REG_CHGR_INT1M, + .get_irq_reg = s2mu005_irq_get_reg, +}; + static const struct regmap_irq_chip s5m8767_irq_chip = { .name = "s5m8767", .irqs = s5m8767_irqs, @@ -442,6 +512,9 @@ struct regmap_irq_chip_data *sec_irq_init(struct sec_pmic_dev *sec_pmic) case S2MPU05: sec_irq_chip = &s2mpu05_irq_chip; break; + case S2MU005: + sec_irq_chip = &s2mu005_irq_chip; + break; default: return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n", sec_pmic->device_type); diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 4480c631110a..6191f409de94 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -47,6 +47,7 @@ enum sec_device_type { S2MPS15X, S2MPU02, S2MPU05, + S2MU005, }; /** diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h index 6eab95de6fa8..19d0f0e12944 100644 --- a/include/linux/mfd/samsung/irq.h +++ b/include/linux/mfd/samsung/irq.h @@ -408,6 +408,72 @@ enum s2mpu05_irq { #define S2MPU05_IRQ_INT140C_MASK BIT(1) #define S2MPU05_IRQ_TSD_MASK BIT(2) +enum s2mu005_irq { + S2MU005_IRQ_CHGR_DETBAT, + S2MU005_IRQ_CHGR_BAT, + S2MU005_IRQ_CHGR_IVR, + S2MU005_IRQ_CHGR_EVENT, + S2MU005_IRQ_CHGR_CHG, + S2MU005_IRQ_CHGR_VMID, + S2MU005_IRQ_CHGR_WCIN, + S2MU005_IRQ_CHGR_VBUS, + + S2MU005_IRQ_FLED_LBPROT, + S2MU005_IRQ_FLED_OPENCH2, + S2MU005_IRQ_FLED_OPENCH1, + S2MU005_IRQ_FLED_SHORTCH2, + S2MU005_IRQ_FLED_SHORTCH1, + + S2MU005_IRQ_MUIC_ATTACH, + S2MU005_IRQ_MUIC_DETACH, + S2MU005_IRQ_MUIC_KP, + S2MU005_IRQ_MUIC_LKP, + S2MU005_IRQ_MUIC_LKR, + S2MU005_IRQ_MUIC_RIDCHG, + + S2MU005_IRQ_MUIC_VBUSON, + S2MU005_IRQ_MUIC_RSVD, + S2MU005_IRQ_MUIC_ADC, + S2MU005_IRQ_MUIC_STUCK, + S2MU005_IRQ_MUIC_STUCKRCV, + S2MU005_IRQ_MUIC_MHDL, + S2MU005_IRQ_MUIC_AVCHG, + S2MU005_IRQ_MUIC_VBUSOFF, + + S2MU005_IRQ_NR, +}; + +#define S2MU005_IRQ_CHGR_DETBAT_MASK BIT(0) +#define S2MU005_IRQ_CHGR_BAT_MASK BIT(1) +#define S2MU005_IRQ_CHGR_IVR_MASK BIT(2) +#define S2MU005_IRQ_CHGR_EVENT_MASK BIT(3) +#define S2MU005_IRQ_CHGR_CHG_MASK BIT(4) +#define S2MU005_IRQ_CHGR_VMID_MASK BIT(5) +#define S2MU005_IRQ_CHGR_WCIN_MASK BIT(6) +#define S2MU005_IRQ_CHGR_VBUS_MASK BIT(7) + +#define S2MU005_IRQ_FLED_LBPROT_MASK BIT(2) +#define S2MU005_IRQ_FLED_OPENCH2_MASK BIT(4) +#define S2MU005_IRQ_FLED_OPENCH1_MASK BIT(5) +#define S2MU005_IRQ_FLED_SHORTCH2_MASK BIT(6) +#define S2MU005_IRQ_FLED_SHORTCH1_MASK BIT(7) + +#define S2MU005_IRQ_MUIC_ATTACH_MASK BIT(0) +#define S2MU005_IRQ_MUIC_DETACH_MASK BIT(1) +#define S2MU005_IRQ_MUIC_KP_MASK BIT(2) +#define S2MU005_IRQ_MUIC_LKP_MASK BIT(3) +#define S2MU005_IRQ_MUIC_LKR_MASK BIT(4) +#define S2MU005_IRQ_MUIC_RIDCHG_MASK BIT(5) + +#define S2MU005_IRQ_MUIC_VBUSON_MASK BIT(0) +#define S2MU005_IRQ_MUIC_RSVD_MASK BIT(1) +#define S2MU005_IRQ_MUIC_ADC_MASK BIT(2) +#define S2MU005_IRQ_MUIC_STUCK_MASK BIT(3) +#define S2MU005_IRQ_MUIC_STUCKRCV_MASK BIT(4) +#define S2MU005_IRQ_MUIC_MHDL_MASK BIT(5) +#define S2MU005_IRQ_MUIC_AVCHG_MASK BIT(6) +#define S2MU005_IRQ_MUIC_VBUSOFF_MASK BIT(7) + enum s5m8767_irq { S5M8767_IRQ_PWRR, S5M8767_IRQ_PWRF, diff --git a/include/linux/mfd/samsung/s2mu005.h b/include/linux/mfd/samsung/s2mu005.h new file mode 100644 index 000000000000..46e7759545af --- /dev/null +++ b/include/linux/mfd/samsung/s2mu005.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * Copyright (c) 2026 Kaustabh Chakraborty + * Copyright (c) 2026 Łukasz Lebiedziński + */ + +#ifndef __LINUX_MFD_S2MU005_H +#define __LINUX_MFD_S2MU005_H + +#include +#include + +/* S2MU005 registers */ +enum s2mu005_reg { + S2MU005_REG_CHGR_INT1, + S2MU005_REG_CHGR_INT1M, + + S2MU005_REG_FLED_INT1, + S2MU005_REG_FLED_INT1M, + + S2MU005_REG_MUIC_INT1, + S2MU005_REG_MUIC_INT2, + S2MU005_REG_MUIC_INT1M, + S2MU005_REG_MUIC_INT2M, + + S2MU005_REG_CHGR_STATUS0, + S2MU005_REG_CHGR_STATUS1, + S2MU005_REG_CHGR_STATUS2, + S2MU005_REG_CHGR_STATUS3, + S2MU005_REG_CHGR_STATUS4, + S2MU005_REG_CHGR_STATUS5, + S2MU005_REG_CHGR_CTRL0, + S2MU005_REG_CHGR_CTRL1, + S2MU005_REG_CHGR_CTRL2, + S2MU005_REG_CHGR_CTRL3, + S2MU005_REG_CHGR_CTRL4, + S2MU005_REG_CHGR_CTRL5, + S2MU005_REG_CHGR_CTRL6, + S2MU005_REG_CHGR_CTRL7, + S2MU005_REG_CHGR_CTRL8, + S2MU005_REG_CHGR_CTRL9, + S2MU005_REG_CHGR_CTRL10, + S2MU005_REG_CHGR_CTRL11, + S2MU005_REG_CHGR_CTRL12, + S2MU005_REG_CHGR_CTRL13, + S2MU005_REG_CHGR_CTRL14, + S2MU005_REG_CHGR_CTRL15, + S2MU005_REG_CHGR_CTRL16, + S2MU005_REG_CHGR_CTRL17, + S2MU005_REG_CHGR_CTRL18, + S2MU005_REG_CHGR_CTRL19, + S2MU005_REG_CHGR_TEST0, + S2MU005_REG_CHGR_TEST1, + S2MU005_REG_CHGR_TEST2, + S2MU005_REG_CHGR_TEST3, + S2MU005_REG_CHGR_TEST4, + S2MU005_REG_CHGR_TEST5, + S2MU005_REG_CHGR_TEST6, + S2MU005_REG_CHGR_TEST7, + S2MU005_REG_CHGR_TEST8, + S2MU005_REG_CHGR_TEST9, + S2MU005_REG_CHGR_TEST10, + + S2MU005_REG_FLED_STATUS, + S2MU005_REG_FLED_CH0_CTRL0, + S2MU005_REG_FLED_CH0_CTRL1, + S2MU005_REG_FLED_CH0_CTRL2, + S2MU005_REG_FLED_CH0_CTRL3, + S2MU005_REG_FLED_CH1_CTRL0, + S2MU005_REG_FLED_CH1_CTRL1, + S2MU005_REG_FLED_CH1_CTRL2, + S2MU005_REG_FLED_CH1_CTRL3, + S2MU005_REG_FLED_CTRL0, + S2MU005_REG_FLED_CTRL1, + S2MU005_REG_FLED_CTRL2, + S2MU005_REG_FLED_CTRL3, + S2MU005_REG_FLED_CTRL4, + S2MU005_REG_FLED_CTRL5, + S2MU005_REG_FLED_CTRL6, + + S2MU005_REG_RGB_EN, + S2MU005_REG_RGB_CH0_CTRL, + S2MU005_REG_RGB_CH1_CTRL, + S2MU005_REG_RGB_CH2_CTRL, + S2MU005_REG_RGB_CH0_RAMP, + S2MU005_REG_RGB_CH0_STAY, + S2MU005_REG_RGB_CH1_RAMP, + S2MU005_REG_RGB_CH1_STAY, + S2MU005_REG_RGB_CH2_RAMP, + S2MU005_REG_RGB_CH2_STAY, + S2MU005_REG_RGB_TEST0, + S2MU005_REG_RGB_CTRL0, + + S2MU005_REG_MUIC_ADC, + S2MU005_REG_MUIC_DEV1, + S2MU005_REG_MUIC_DEV2, + S2MU005_REG_MUIC_DEV3, + S2MU005_REG_MUIC_BUTTON1, + S2MU005_REG_MUIC_BUTTON2, + S2MU005_REG_MUIC_RESET, + S2MU005_REG_MUIC_CHGTYPE, + S2MU005_REG_MUIC_DEVAPPLE, + S2MU005_REG_MUIC_BCDRESCAN, + S2MU005_REG_MUIC_TEST1, + S2MU005_REG_MUIC_TEST2, + S2MU005_REG_MUIC_TEST3, + + S2MU005_REG_ID = 0x73, + + S2MU005_REG_MUIC_CTRL1 = 0xb2, + S2MU005_REG_MUIC_TIMERSET1, + S2MU005_REG_MUIC_TIMERSET2, + S2MU005_REG_MUIC_SWCTRL, + S2MU005_REG_MUIC_TIMERSET3, + S2MU005_REG_MUIC_CTRL2, + S2MU005_REG_MUIC_CTRL3, + + S2MU005_REG_MUIC_LDOADC_L = 0xbf, + S2MU005_REG_MUIC_LDOADC_H, +}; + +#define S2MU005_REG_FLED_CH_CTRL0(x) (S2MU005_REG_FLED_CH0_CTRL0 + 4 * (x)) +#define S2MU005_REG_FLED_CH_CTRL1(x) (S2MU005_REG_FLED_CH0_CTRL1 + 4 * (x)) +#define S2MU005_REG_FLED_CH_CTRL2(x) (S2MU005_REG_FLED_CH0_CTRL2 + 4 * (x)) +#define S2MU005_REG_FLED_CH_CTRL3(x) (S2MU005_REG_FLED_CH0_CTRL3 + 4 * (x)) + +#define S2MU005_REG_RGB_CH_CTRL(x) (S2MU005_REG_RGB_CH0_CTRL + 1 * (x)) +#define S2MU005_REG_RGB_CH_RAMP(x) (S2MU005_REG_RGB_CH0_RAMP + 2 * (x)) +#define S2MU005_REG_RGB_CH_STAY(x) (S2MU005_REG_RGB_CH0_STAY + 2 * (x)) + +/* S2MU005_REG_CHGR_STATUS0 */ +#define S2MU005_CHGR_VBUS BIT(7) +#define S2MU005_CHGR_WCIN BIT(6) +#define S2MU005_CHGR_VMID BIT(5) +#define S2MU005_CHGR_CHG BIT(4) +#define S2MU005_CHGR_STAT GENMASK(3, 0) + +#define S2MU005_CHGR_STAT_DONE 8 +#define S2MU005_CHGR_STAT_TOPOFF 7 +#define S2MU005_CHGR_STAT_DONE_FLAG 6 +#define S2MU005_CHGR_STAT_CV 5 +#define S2MU005_CHGR_STAT_CC 4 +#define S2MU005_CHGR_STAT_COOL_CHG 3 +#define S2MU005_CHGR_STAT_PRE_CHG 2 + +/* S2MU005_REG_CHGR_STATUS1 */ +#define S2MU005_CHGR_DETBAT BIT(7) +#define S2MU005_CHGR_VBUS_OVP GENMASK(6, 4) + +#define S2MU005_CHGR_VBUS_OVP_OVERVOLT 2 + +/* S2MU005_REG_CHGR_STATUS2 */ +#define S2MU005_CHGR_BAT GENMASK(6, 4) + +#define S2MU005_CHGR_BAT_VOLT_DET 7 +#define S2MU005_CHGR_BAT_FAST_CHG_DET 6 +#define S2MU005_CHGR_BAT_COOL_CHG_DET 5 +#define S2MU005_CHGR_BAT_LOW_CHG 2 +#define S2MU005_CHGR_BAT_SELF_DISCHG 1 +#define S2MU005_CHGR_BAT_OVP_DET 0 + +/* S2MU005_REG_CHGR_STATUS3 */ +#define S2MU005_CHGR_EVT GENMASK(3, 0) + +#define S2MU005_CHGR_EVT_WDT_RST 6 +#define S2MU005_CHGR_EVT_WDT_SUSP 5 +#define S2MU005_CHGR_EVT_VSYS_VUVLO 4 +#define S2MU005_CHGR_EVT_VSYS_VOVP 3 +#define S2MU005_CHGR_EVT_THERM_FOLDBACK 2 +#define S2MU005_CHGR_EVT_THERM_SHUTDOWN 1 + +/* S2MU005_REG_CHGR_CTRL0 */ +#define S2MU005_CHGR_CHG_EN BIT(4) +#define S2MU005_CHGR_OP_MODE GENMASK(2, 0) + +#define S2MU005_CHGR_OP_MODE_OTG BIT(2) +#define S2MU005_CHGR_OP_MODE_CHG BIT(1) + +/* S2MU005_REG_CHGR_CTRL1 */ +#define S2MU005_CHGR_VIN_DROP GENMASK(6, 4) + +/* S2MU005_REG_CHGR_CTRL2 */ +#define S2MU005_CHGR_IN_CURR_LIM GENMASK(5, 0) + +/* S2MU005_REG_CHGR_CTRL4 */ +#define S2MU005_CHGR_OTG_OCP_ON BIT(5) +#define S2MU005_CHGR_OTG_OCP_OFF BIT(4) +#define S2MU005_CHGR_OTG_OCP GENMASK(3, 2) +#define S2MU005_CHGR_OTG_OCP_1P5A 0x3 + +/* S2MU005_REG_CHGR_CTRL5 */ +#define S2MU005_CHGR_VMID_BOOST GENMASK(4, 0) +#define S2MU005_CHGR_VMID_BOOST_5P1V 0x16 + +/* S2MU005_REG_CHGR_CTRL6 */ +#define S2MU005_CHGR_COOL_CHG_CURR GENMASK(5, 0) + +/* S2MU005_REG_CHGR_CTRL7 */ +#define S2MU005_CHGR_FAST_CHG_CURR GENMASK(5, 0) + +/* S2MU005_REG_CHGR_CTRL8 */ +#define S2MU005_CHGR_VF_VBAT GENMASK(6, 1) + +/* S2MU005_REG_CHGR_CTRL10 */ +#define S2MU005_CHGR_TOPOFF_CURR(x) (GENMASK(3, 0) << 4 * (x)) + +/* S2MU005_REG_CHGR_CTRL11 */ +#define S2MU005_CHGR_OSC_BOOST GENMASK(6, 5) +#define S2MU005_CHGR_OSC_BUCK GENMASK(4, 3) +#define S2MU005_CHGR_OSC_BOOST_2MHZ 0x3 + +/* S2MU005_REG_CHGR_CTRL12 */ +#define S2MU005_CHGR_WDT GENMASK(2, 0) + +#define S2MU005_CHGR_WDT_ON BIT(2) +#define S2MU005_CHGR_WDT_OFF BIT(1) + +/* S2MU005_REG_CHGR_CTRL15 */ +#define S2MU005_CHGR_OTG_EN GENMASK(3, 2) +#define S2MU005_CHGR_OTG_EN_ON 0x3 + +/* S2MU005_REG_FLED_STATUS */ +#define S2MU005_FLED_FLASH_STATUS(x) (BIT(7) >> 2 * (x)) +#define S2MU005_FLED_TORCH_STATUS(x) (BIT(6) >> 2 * (x)) + +/* S2MU005_REG_FLED_CHx_CTRL0 */ +#define S2MU005_FLED_FLASH_IOUT GENMASK(3, 0) + +/* S2MU005_REG_FLED_CHx_CTRL1 */ +#define S2MU005_FLED_TORCH_IOUT GENMASK(3, 0) + +/* S2MU005_REG_FLED_CHx_CTRL2 */ +#define S2MU005_FLED_TORCH_TIMEOUT GENMASK(3, 0) + +/* S2MU005_REG_FLED_CHx_CTRL3 */ +#define S2MU005_FLED_FLASH_TIMEOUT GENMASK(3, 0) + +/* S2MU005_REG_FLED_CTRL1 */ +#define S2MU005_FLED_CH_EN BIT(7) + +/* + * S2MU005_REG_FLED_CTRL4 - Rev. EVT0 + * S2MU005_REG_FLED_CTRL6 - Rev. EVT1 and later + */ +#define S2MU005_FLED_FLASH_EN(x) (GENMASK(7, 6) >> 4 * (x)) +#define S2MU005_FLED_TORCH_EN(x) (GENMASK(5, 4) >> 4 * (x)) + +/* S2MU005_REG_RGB_EN */ +#define S2MU005_RGB_RESET BIT(6) +#define S2MU005_RGB_SLOPE GENMASK(5, 0) + +#define S2MU005_RGB_SLOPE_CONST (BIT(4) | BIT(2) | BIT(0)) +#define S2MU005_RGB_SLOPE_SMOOTH (BIT(5) | BIT(3) | BIT(1)) + +/* S2MU005_REG_RGB_CHx_RAMP */ +#define S2MU005_RGB_CH_RAMP_UP GENMASK(7, 4) +#define S2MU005_RGB_CH_RAMP_DN GENMASK(3, 0) + +/* S2MU005_REG_RGB_CHx_STAY */ +#define S2MU005_RGB_CH_STAY_HI GENMASK(7, 4) +#define S2MU005_RGB_CH_STAY_LO GENMASK(3, 0) + +/* S2MU005_REG_MUIC_DEV1 */ +#define S2MU005_MUIC_OTG BIT(7) +#define S2MU005_MUIC_DCP BIT(6) +#define S2MU005_MUIC_CDP BIT(5) +#define S2MU005_MUIC_T1_T2_CHG BIT(4) +#define S2MU005_MUIC_UART BIT(3) +#define S2MU005_MUIC_SDP BIT(2) +#define S2MU005_MUIC_LANHUB BIT(1) +#define S2MU005_MUIC_AUDIO BIT(0) + +/* S2MU005_REG_MUIC_DEV2 */ +#define S2MU005_MUIC_SDP_1P8S BIT(7) +#define S2MU005_MUIC_AV BIT(6) +#define S2MU005_MUIC_TTY BIT(5) +#define S2MU005_MUIC_PPD BIT(4) +#define S2MU005_MUIC_JIG_UART_OFF BIT(3) +#define S2MU005_MUIC_JIG_UART_ON BIT(2) +#define S2MU005_MUIC_JIG_USB_OFF BIT(1) +#define S2MU005_MUIC_JIG_USB_ON BIT(0) + +/* S2MU005_REG_MUIC_DEV3 */ +#define S2MU005_MUIC_U200_CHG BIT(7) +#define S2MU005_MUIC_VBUS_AV BIT(4) +#define S2MU005_MUIC_VBUS_R255 BIT(1) +#define S2MU005_MUIC_MHL BIT(0) + +/* S2MU005_REG_MUIC_DEVAPPLE */ +#define S2MU005_MUIC_APPLE_CHG_0P5A BIT(7) +#define S2MU005_MUIC_APPLE_CHG_1P0A BIT(6) +#define S2MU005_MUIC_APPLE_CHG_2P0A BIT(5) +#define S2MU005_MUIC_APPLE_CHG_2P4A BIT(4) +#define S2MU005_MUIC_SDP_DCD_OUT BIT(3) +#define S2MU005_MUIC_RID_WAKEUP BIT(2) +#define S2MU005_MUIC_VBUS_WAKEUP BIT(1) +#define S2MU005_MUIC_BCV1P2_OR_OPEN BIT(0) + +/* S2MU005_REG_ID */ +#define S2MU005_ID_MASK GENMASK(3, 0) + +/* S2MU005_REG_MUIC_SWCTRL */ +#define S2MU005_MUIC_DM_DP GENMASK(7, 2) +#define S2MU005_MUIC_JIG BIT(0) + +#define S2MU005_MUIC_DM_DP_UART 0x12 +#define S2MU005_MUIC_DM_DP_USB 0x09 + +/* S2MU005_REG_MUIC_CTRL1 */ +#define S2MU005_MUIC_OPEN BIT(4) +#define S2MU005_MUIC_RAW_DATA BIT(3) +#define S2MU005_MUIC_MAN_SW BIT(2) +#define S2MU005_MUIC_WAIT BIT(1) +#define S2MU005_MUIC_IRQ BIT(0) + +/* S2MU005_REG_MUIC_CTRL3 */ +#define S2MU005_MUIC_ONESHOT_ADC BIT(2) + +/* S2MU005_REG_MUIC_LDOADC_L and S2MU005_REG_MUIC_LDOADC_H */ +#define S2MU005_MUIC_VSET GENMASK(4, 0) + +#define S2MU005_MUIC_VSET_3P0V 0x1f +#define S2MU005_MUIC_VSET_2P6V 0x0e +#define S2MU005_MUIC_VSET_2P4V 0x0c +#define S2MU005_MUIC_VSET_2P2V 0x0a +#define S2MU005_MUIC_VSET_2P0V 0x08 +#define S2MU005_MUIC_VSET_1P5V 0x03 +#define S2MU005_MUIC_VSET_1P4V 0x02 +#define S2MU005_MUIC_VSET_1P2V 0x00 + +#endif /* __LINUX_MFD_S2MU005_H */ From 9e667b8ebd5d8229cf523372bf62660f1090c8a0 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:37 +0530 Subject: [PATCH 3853/5214] mfd: sec: Set DMA coherent mask Kernel logs are filled with "DMA mask not set" messages for every sub-device. The device does not use DMA for communication, so these messages are useless. Disable the coherent DMA mask for the PMIC device, which is also propagated to sub-devices. Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-5-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- drivers/mfd/sec-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c index 22f6c74eb6c0..fe92bc4a3dd2 100644 --- a/drivers/mfd/sec-common.c +++ b/drivers/mfd/sec-common.c @@ -221,6 +221,9 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq, if (IS_ERR(irq_data)) return PTR_ERR(irq_data); + dev->coherent_dma_mask = 0; + dev->dma_mask = &dev->coherent_dma_mask; + pm_runtime_set_active(sec_pmic->dev); switch (sec_pmic->device_type) { From 5ed87cc71c1bc1c3a3449ebd2de83e2b9c32670b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 27 Mar 2026 13:58:41 +0100 Subject: [PATCH 3854/5214] leds: st1202: Drop unused include The driver includes the legacy GPIO header but does not use any symbols from it so drop the include. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260327-leds-st1202-v1-1-15c107cc9fb9@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-st1202.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c index 4e5dd76d714d..2b68cd3c45f8 100644 --- a/drivers/leds/leds-st1202.c +++ b/drivers/leds/leds-st1202.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include From ce1464f193a8299166acc26d4f8e13cdb56c060d Mon Sep 17 00:00:00 2001 From: Biswapriyo Nath Date: Wed, 25 Mar 2026 18:07:27 +0000 Subject: [PATCH 3855/5214] dt-bindings: leds: irled: ir-spi-led: Add new duty-cycle value 30 duty cycle for IR transmitter is used in Xiaomi Redmi Note 8 (ginkgo). Signed-off-by: Biswapriyo Nath Reviewed-by: Sean Young Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260325-ginkgo-add-usb-ir-vib-v1-4-446c6e865ad6@gmail.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml b/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml index 72cadebf6e3e..0297bfbb2750 100644 --- a/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml +++ b/Documentation/devicetree/bindings/leds/irled/ir-spi-led.yaml @@ -25,7 +25,7 @@ properties: duty-cycle: $ref: /schemas/types.yaml#/definitions/uint8 - enum: [50, 60, 70, 75, 80, 90] + enum: [30, 50, 60, 70, 75, 80, 90] description: Percentage of one period in which the signal is active. From 0261683a4d31783d680e74b3ae5f22f6a62128cc Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Tue, 31 Mar 2026 22:28:48 +0200 Subject: [PATCH 3856/5214] leds: pca9532: Don't stop blinking for non-zero brightness pca9532 unexpectedly stopped blinking when changing brightness to a non-zero value. To reproduce: echo timer > /sys/class/leds/led-1/trigger # blinks echo 255 > /sys/class/leds/led-1/brightness # blinking stops, light on cat /sys/class/leds/led-1/trigger # still claims [timer] According to Documentation/leds/leds-class.rst, only brightness = 0 shall be a stop condition: > You can change the brightness value of a LED independently of the > timer trigger. However, if you set the brightness value to LED_OFF it > will also disable the timer trigger. Therefore add a guard to continue blinking when brightness != LED_OFF, similar to how pca955x does it since 575f10dc64a2 ("leds: pca955x: Add HW blink support"). Signed-off-by: Tobias Deiminger Link: https://patch.msgid.link/20260331202848.658676-1-tobias.deiminger@linutronix.de Signed-off-by: Lee Jones --- drivers/leds/leds-pca9532.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 0344189bb991..dae7c6760508 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -182,11 +182,13 @@ static int pca9532_set_brightness(struct led_classdev *led_cdev, int err = 0; struct pca9532_led *led = ldev_to_led(led_cdev); - if (value == LED_OFF) + if (value == LED_OFF) { led->state = PCA9532_OFF; - else if (value == LED_FULL) + } else if (led->state == PCA9532_PWM1) { + return 0; /* Non-zero brightness shall not stop HW blinking */ + } else if (value == LED_FULL) { led->state = PCA9532_ON; - else { + } else { led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ err = pca9532_calcpwm(led->client, PCA9532_PWM_ID_0, 0, value); if (err) From 7c150c17c01d4d64942b9872d66dd7670e8ba80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corvin=20K=C3=B6hne?= Date: Wed, 8 Apr 2026 08:29:42 +0200 Subject: [PATCH 3857/5214] dt-binding: leds: Publish common bindings under dual license MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes leds/common.h DT binding header file to be published under GPLv2 or BSD-2-Clause license terms. This change allows this common LED bindings header file to be used in software components as bootloaders and OSes that are not published under GPLv2 terms. All contributors to leds/common.h file in copy. Signed-off-by: Corvin Köhne Acked-by: Krzysztof Kozlowski Acked-by: Gergo Koteles Acked-by: Jacek Anaszewski Acked-by: Rafał Miłecki Link: https://patch.msgid.link/20260408062942.7128-1-corvin.koehne@gmail.com Signed-off-by: Lee Jones --- include/dt-bindings/leds/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h index 4f017bea0123..b7bafbaf7df3 100644 --- a/include/dt-bindings/leds/common.h +++ b/include/dt-bindings/leds/common.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ /* * This header provides macros for the common LEDs device tree bindings. * From 26e15f2558f66b6747ef981df8054d0d92d01b60 Mon Sep 17 00:00:00 2001 From: Piotr Kubik Date: Wed, 8 Apr 2026 13:51:06 +0200 Subject: [PATCH 3858/5214] leds: trigger: gpio: Use GPIOD_FLAGS_BIT_NONEXCLUSIVE When a GPIO is shared between the LED trigger driver and another driver, the LED trigger driver needs to request the GPIO with GPIOD_FLAGS_BIT_NONEXCLUSIVE to allow both drivers to monitor the same GPIO pin. Without this flag, if another driver has already claimed the GPIO, the LED trigger driver's gpiod_get_optional() call fails silently, and the LED trigger doesn't work. This is needed for scenarios like: - SFP module presence/status LED triggered by SFP Mod_ABS/Rx_LOS Both GPIOs are also monitored by the SFP driver for module state management, so they need to be shared. Signed-off-by: Piotr Kubik Link: https://patch.msgid.link/20260408115106.379834-1-piotr@kubik.pl Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index fc911dfec0ef..3b3e6869e856 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -86,7 +86,8 @@ static int gpio_trig_activate(struct led_classdev *led) * The generic property "trigger-sources" is followed, * and we hope that this is a GPIO. */ - gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", GPIOD_IN); + gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", + GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(gpio_data->gpiod)) { ret = PTR_ERR(gpio_data->gpiod); kfree(gpio_data); From e7fc971825608d6b3861e0bfef9965c3ae7c0d86 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 9 Apr 2026 10:15:55 -0700 Subject: [PATCH 3859/5214] leds: qcom-lpg: Allocate channels with main struct Use a flexible array member to combine kzalloc and kcalloc. This required moving the struct lpg_channel definition up as flexible array members require a full definition. Add __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260409171555.14580-1-rosenp@gmail.com Signed-off-by: Lee Jones --- drivers/leds/rgb/leds-qcom-lpg.c | 120 +++++++++++++++---------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index f6061c47f863..d7d6518de30f 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -80,58 +80,8 @@ #define SDAM_PAUSE_HI_MULTIPLIER_OFFSET 0x8 #define SDAM_PAUSE_LO_MULTIPLIER_OFFSET 0x9 -struct lpg_channel; struct lpg_data; -/** - * struct lpg - LPG device context - * @dev: pointer to LPG device - * @map: regmap for register access - * @lock: used to synchronize LED and pwm callback requests - * @pwm: PWM-chip object, if operating in PWM mode - * @data: reference to version specific data - * @lut_base: base address of the LUT block (optional) - * @lut_size: number of entries in the LUT block - * @lut_bitmap: allocation bitmap for LUT entries - * @pbs_dev: PBS device - * @lpg_chan_sdam: LPG SDAM peripheral device - * @lut_sdam: LUT SDAM peripheral device - * @pbs_en_bitmap: bitmap for tracking PBS triggers - * @triled_base: base address of the TRILED block (optional) - * @triled_src: power-source for the TRILED - * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register - * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register - * @channels: list of PWM channels - * @num_channels: number of @channels - */ -struct lpg { - struct device *dev; - struct regmap *map; - - struct mutex lock; - - struct pwm_chip *pwm; - - const struct lpg_data *data; - - u32 lut_base; - u32 lut_size; - unsigned long *lut_bitmap; - - struct pbs_dev *pbs_dev; - struct nvmem_device *lpg_chan_sdam; - struct nvmem_device *lut_sdam; - unsigned long pbs_en_bitmap; - - u32 triled_base; - u32 triled_src; - bool triled_has_atc_ctl; - bool triled_has_src_sel; - - struct lpg_channel *channels; - unsigned int num_channels; -}; - /** * struct lpg_channel - per channel data * @lpg: reference to parent lpg @@ -203,8 +153,8 @@ struct lpg_channel { * @lpg: lpg context reference * @cdev: LED class device * @mcdev: Multicolor LED class device - * @num_channels: number of @channels * @channels: list of channels associated with the LED + * @num_channels: number of @channels */ struct lpg_led { struct lpg *lpg; @@ -216,6 +166,55 @@ struct lpg_led { struct lpg_channel *channels[] __counted_by(num_channels); }; +/** + * struct lpg - LPG device context + * @dev: pointer to LPG device + * @map: regmap for register access + * @lock: used to synchronize LED and pwm callback requests + * @pwm: PWM-chip object, if operating in PWM mode + * @data: reference to version specific data + * @lut_base: base address of the LUT block (optional) + * @lut_size: number of entries in the LUT block + * @lut_bitmap: allocation bitmap for LUT entries + * @pbs_dev: PBS device + * @lpg_chan_sdam: LPG SDAM peripheral device + * @lut_sdam: LUT SDAM peripheral device + * @pbs_en_bitmap: bitmap for tracking PBS triggers + * @triled_base: base address of the TRILED block (optional) + * @triled_src: power-source for the TRILED + * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register + * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register + * @num_channels: number of @channels + * @channels: list of PWM channels + */ +struct lpg { + struct device *dev; + struct regmap *map; + + struct mutex lock; + + struct pwm_chip *pwm; + + const struct lpg_data *data; + + u32 lut_base; + u32 lut_size; + unsigned long *lut_bitmap; + + struct pbs_dev *pbs_dev; + struct nvmem_device *lpg_chan_sdam; + struct nvmem_device *lut_sdam; + unsigned long pbs_en_bitmap; + + u32 triled_base; + u32 triled_src; + bool triled_has_atc_ctl; + bool triled_has_src_sel; + + unsigned int num_channels; + struct lpg_channel channels[] __counted_by(num_channels); +}; + /** * struct lpg_channel_data - per channel initialization data * @sdam_offset: Channel offset in LPG SDAM @@ -1475,12 +1474,6 @@ static int lpg_init_channels(struct lpg *lpg) struct lpg_channel *chan; int i; - lpg->num_channels = data->num_channels; - lpg->channels = devm_kcalloc(lpg->dev, data->num_channels, - sizeof(struct lpg_channel), GFP_KERNEL); - if (!lpg->channels) - return -ENOMEM; - for (i = 0; i < data->num_channels; i++) { chan = &lpg->channels[i]; @@ -1603,18 +1596,21 @@ static int lpg_init_sdam(struct lpg *lpg) static int lpg_probe(struct platform_device *pdev) { + const struct lpg_data *data; struct lpg *lpg; int ret; int i; - lpg = devm_kzalloc(&pdev->dev, sizeof(*lpg), GFP_KERNEL); + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -EINVAL; + + lpg = devm_kzalloc(&pdev->dev, struct_size(lpg, channels, data->num_channels), GFP_KERNEL); if (!lpg) return -ENOMEM; - lpg->data = of_device_get_match_data(&pdev->dev); - if (!lpg->data) - return -EINVAL; - + lpg->num_channels = data->num_channels; + lpg->data = data; lpg->dev = &pdev->dev; mutex_init(&lpg->lock); From 7bf097d90e0439c3b601ed72758706a6978c7a41 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Thu, 23 Apr 2026 18:39:10 +0800 Subject: [PATCH 3860/5214] docs: leds: Fix sysfs ABI reference in lp5812.rst Documentation/ABI/testing/sysfs-class-led-multicolor is a plain ABI description without a .rst suffix. The lp5812 documentation incorrectly referred to sysfs-class-led-multicolor.rst, which does not exist. This was reported by documentation-file-ref-check (make refcheckdocs). Signed-off-by: Xinhong Hu Link: https://patch.msgid.link/tencent_26F6155ED10CA20CC65F62FD659218853809@qq.com Signed-off-by: Lee Jones --- Documentation/leds/leds-lp5812.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/leds/leds-lp5812.rst b/Documentation/leds/leds-lp5812.rst index c2a6368d5149..12e757d45c3a 100644 --- a/Documentation/leds/leds-lp5812.rst +++ b/Documentation/leds/leds-lp5812.rst @@ -20,7 +20,7 @@ Sysfs Interface =============== This driver uses the standard multicolor LED class interfaces defined -in Documentation/ABI/testing/sysfs-class-led-multicolor.rst. +in Documentation/ABI/testing/sysfs-class-led-multicolor. Each LP5812 LED output appears under ``/sys/class/leds/`` with its assigned label (for example ``LED_A``). From d92e7ca6931910e72ebbed7ff96f4f74c24cf0d8 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:21 -0400 Subject: [PATCH 3861/5214] leds: as3668: Fix Kconfig symbol name mismatch in Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kconfiglint reports: X001: CONFIG_LEDS_AS3668 referenced in Makefile but not defined in any Kconfig The AS3668 LED driver was introduced in commit c7dd343a3756 ("leds: as3668: Driver for the ams Osram 4-channel i2c LED driver"). That commit defined the Kconfig symbol as LEDS_OSRAM_AMS_AS3668 in drivers/leds/Kconfig but used the shorter name LEDS_AS3668 in the Makefile's obj-* line. Because the Makefile references CONFIG_LEDS_AS3668 which does not exist, the driver can never be built — the obj-* line always evaluates to obj- += leds-as3668.o (empty config), so the object file is never compiled regardless of what the user selects in menuconfig. Fix the Makefile to reference the correct Kconfig symbol CONFIG_LEDS_OSRAM_AMS_AS3668, matching what is defined in drivers/leds/Kconfig. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Acked-by: Lukas Timmermann Link: https://patch.msgid.link/20260426000322.55999-1-sashal@kernel.org Signed-off-by: Lee Jones --- drivers/leds/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8fdb45d5b439..7db3768912ca 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o obj-$(CONFIG_LEDS_APU) += leds-apu.o obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o -obj-$(CONFIG_LEDS_AS3668) += leds-as3668.o +obj-$(CONFIG_LEDS_OSRAM_AMS_AS3668) += leds-as3668.o obj-$(CONFIG_LEDS_AW200XX) += leds-aw200xx.o obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o From 647a6b59d138ff11761fa97dbf36524d726437c7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:01:18 +0200 Subject: [PATCH 3862/5214] leds: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260427070117.18363-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/leds/flash/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 5e08102a6784..31c996651f2a 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -76,7 +76,7 @@ config LEDS_MT6370_FLASH will be called "leds-mt6370-flash". config LEDS_QCOM_FLASH - tristate "LED support for flash module inside Qualcomm Technologies, Inc. PMIC" + tristate "LED support for flash module inside Qualcomm PMIC" depends on MFD_SPMI_PMIC || COMPILE_TEST depends on LEDS_CLASS && OF depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS From a03484e1e42893e2b219d9d780ce6933d319ef46 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 4 May 2026 17:37:30 +0500 Subject: [PATCH 3863/5214] leds: blinkm: Fix spelling and comment style above lock acquire Fix two issues in the comment above mutex_lock_interruptible() in blinkm_transfer_hw(): - Spelling mistake (Aquire -> Acquire). - Trailing "*/" was on the same line as text; move it to its own line to match kernel coding style. No functional change. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260504123730.1094-1-sozdayvek@gmail.com Signed-off-by: Lee Jones --- drivers/leds/leds-blinkm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 577497b9d426..1978bbda47db 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -356,7 +356,8 @@ static int blinkm_transfer_hw(struct i2c_client *client, int cmd) struct blinkm_data *data = i2c_get_clientdata(client); /* We start hardware transfers which are not to be - * mixed with other commands. Aquire a lock now. */ + * mixed with other commands. Acquire a lock now. + */ if (mutex_lock_interruptible(&data->update_lock) < 0) return -EAGAIN; From 79ac28f175e02c8ca6216d5df1c83885f30b4c3b Mon Sep 17 00:00:00 2001 From: Carlos Ferreira Date: Mon, 4 May 2026 16:54:34 +0200 Subject: [PATCH 3864/5214] Documentation: leds: leds-class: Document keyboard backlight LED class naming Document the existing practice of always using 'kbd_backlight' for the function part of LED class device names for LED class devices which control single-zone keyboard backlights. Also extend this existing practice with a new naming scheme for keyboards with zoned backlight control. There are several drivers in the works (see the Link:tags below) which offer backlight control for keyboards where the keyboard backlight is divided in a limited number of zones, e.g. "main", "cursor" and "numpad" zones. It is important to agree on a consistent naming scheme for these now, so that userspace can support multiple different models / vendors through a single unified naming scheme. Link: https://lore.kernel.org/platform-driver-x86/20230131235027.36304-1-rishitbansal0@gmail.com/ Link: https://lore.kernel.org/platform-driver-x86/20240719100011.16656-1-carlosmiguelferreira.2003@gmail.com/ Link: https://lore.kernel.org/platform-driver-x86/20260304105831.119349-3-edip@medip.dev/ Link: https://lore.kernel.org/platform-driver-x86/20240806205001.191551-2-mustafa.eskieksi@gmail.com/ Link: https://lore.kernel.org/linux-input/20260402075239.3829699-1-xav@bes.tel/ Signed-off-by: Carlos Ferreira Co-developed-by: Hans de Goede Signed-off-by: Hans de Goede Acked-by: Kate Hsuan Link: https://patch.msgid.link/20260504145434.12746-1-johannes.goede@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/leds/leds-class.rst | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Documentation/leds/leds-class.rst b/Documentation/leds/leds-class.rst index 5db620ed27aa..3913966cfdac 100644 --- a/Documentation/leds/leds-class.rst +++ b/Documentation/leds/leds-class.rst @@ -116,6 +116,69 @@ above leaves scope for further attributes should they be needed. If sections of the name don't apply, just leave that section blank. +Keyboard backlight control LED Device Naming +============================================ + +For backlit keyboards with a single brightness / color settings a single +(multicolor) LED class device should be used to allow userspace to change +the backlight brightness (and if possible the color). This LED class device +must use "kbd_backlight" for the function part of the LED class device name. +IOW the name must end with ":kbd_backlight". + +For backlit keyboards with multiple control zones, one (multicolor) LED class +device should be used per zone. These LED class devices' name must follow: + + "::kbd_zoned_backlight-" + +and must be the same for all zones of the same keyboard. + + should be descriptive of which part of the keyboard backlight +the zone covers and should be suitable for userspace to show to an end user +in an UI for controlling the zones. + +Where possible should be a value already used by other +zoned keyboards with a similar or identical zone layout, e.g.: + +::kbd_zoned_backlight-right +::kbd_zoned_backlight-middle +::kbd_zoned_backlight-left +::kbd_zoned_backlight-corners +::kbd_zoned_backlight-wasd + +or: + +::kbd_zoned_backlight-main +::kbd_zoned_backlight-cursor +::kbd_zoned_backlight-numpad +::kbd_zoned_backlight-corners +::kbd_zoned_backlight-wasd + +Note that this is intended for keyboards with a limited number of zones, +keyboards with per key addressable backlighting must not use LED class devices +since the sysfs API is not suitable for rapidly change multiple LEDs in one +"commit" as is necessary to do animations / special effects on such keyboards. + +An exception to the rule that all zones must follow: + + "::kbd_zoned_backlight-" + +is made for the special case where there is a single big zone which controls +the backlighting of almost all of the keyboard and there are some small areas +with separate control, like just the 4 cursor keys, or the WASD keys. In this +case the main zone should use 'kbd_backlight' for the function part of the name +for compatibility with (older) userspace code which is not aware of +the "kbd_zoned_backlight-" function naming scheme. + +While the smaller zones should use the new zoned naming scheme. Such a setup +would result in e.g.: + +::kbd_backlight +::kbd_zoned_backlight-wasd + +"kbd_zoned_backlight-" aware userspace should be aware of this +exception and check for a main zone with a "kbd_backlight" function-name. + + Brightness setting API ====================== From f0a66563aa2d0bb5673329ae6c4acb5d583cfdef Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Tue, 5 May 2026 08:16:10 +0200 Subject: [PATCH 3865/5214] leds: Add support for TI LP5860 LED driver chip Add support for the Texas Instruments LP5860 LED driver chip via SPI interfaces. The LP5860 is an LED matrix driver for up to 196 LEDs, which supports short and open detection of the individual channel select lines. It can be connected to SPI or I2C bus. For now add support for SPI only. Signed-off-by: Steffen Trumtrar Link: https://patch.msgid.link/20260505-v6-14-topic-ti-lp5860-v10-1-ee29341a75e4@pengutronix.de Signed-off-by: Lee Jones --- drivers/leds/rgb/Kconfig | 25 +++ drivers/leds/rgb/Makefile | 2 + drivers/leds/rgb/leds-lp5860-core.c | 234 ++++++++++++++++++++++++ drivers/leds/rgb/leds-lp5860-spi.c | 98 ++++++++++ drivers/leds/rgb/leds-lp5860.h | 268 ++++++++++++++++++++++++++++ 5 files changed, 627 insertions(+) create mode 100644 drivers/leds/rgb/leds-lp5860-core.c create mode 100644 drivers/leds/rgb/leds-lp5860-spi.c create mode 100644 drivers/leds/rgb/leds-lp5860.h diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 28ef4c487367..9a4ba6531cf8 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -39,6 +39,31 @@ config LEDS_LP5812 If unsure, say N. +config LEDS_LP5860_CORE + tristate "Core Driver for TI LP5860" + depends on LEDS_CLASS + depends on OF + select REGMAP + help + This option supports common operations for LP5860 devices. + The LP5860 is a LED matrix driver with 18 constant current + sinks and 11 scan switches for 198 LED dots. Each dot can be + controlled individually and supports 8/16-bit PWM dimming. + The chip supports individual LED open and short detection. + + The device can be used with SPI or I2C bus. + +config LEDS_LP5860_SPI + tristate "LED Support for TI LP5860 SPI LED driver chip" + depends on SPI + select LEDS_LP5860_CORE + help + If you say yes here you get support for the Texas Instruments + LP5860 LED driver for SPI bus connections. + + To compile this driver as a module, choose M here: the + module will be called leds-lp5860-spi. + config LEDS_NCP5623 tristate "LED support for NCP5623" depends on I2C diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index be45991f63f5..f3b365ea082d 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o obj-$(CONFIG_LEDS_LP5812) += leds-lp5812.o +obj-$(CONFIG_LEDS_LP5860_CORE) += leds-lp5860-core.o +obj-$(CONFIG_LEDS_LP5860_SPI) += leds-lp5860-spi.o obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o diff --git a/drivers/leds/rgb/leds-lp5860-core.c b/drivers/leds/rgb/leds-lp5860-core.c new file mode 100644 index 000000000000..fd0e2f6e6e0f --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860-core.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#include +#include +#include +#include +#include +#include + +#include "leds-lp5860.h" + +static struct lp5860_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev) +{ + return container_of(mc_cdev, struct lp5860_led, mc_cdev); +} + +static int lp5860_set_dot_onoff(struct lp5860_led *led, unsigned int dot, bool enable) +{ + unsigned int offset = dot / LP5860_MAX_DOT_ONOFF_GROUP_NUM; + unsigned int mask = BIT(dot % LP5860_MAX_DOT_ONOFF_GROUP_NUM); + + if (dot > LP5860_MAX_LED) + return -EINVAL; + + return regmap_update_bits(led->chip->regmap, + LP5860_REG_DOT_ONOFF_START + offset, mask, + enable ? LP5860_DOT_ALL_ON : LP5860_DOT_ALL_OFF); +} + +static int lp5860_set_mc_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct lp5860_led *led = mcled_cdev_to_led(mc_cdev); + + led_mc_calc_color_components(mc_cdev, brightness); + + guard(mutex)(&led->chip->lock); + for (int i = 0; i < led->mc_cdev.num_colors; i++) { + unsigned int channel = mc_cdev->subled_info[i].channel; + unsigned int led_brightness = mc_cdev->subled_info[i].brightness; + int ret; + + ret = lp5860_set_dot_onoff(led, channel, !!led_brightness); + if (ret) + return ret; + + ret = regmap_write(led->chip->regmap, + LP5860_REG_PWM_BRI_START + channel, led_brightness); + if (ret) + return ret; + } + + return 0; +} + +static int lp5860_chip_enable(struct lp5860 *lp, bool enable) +{ + guard(mutex)(&lp->lock); + return regmap_write(lp->regmap, LP5860_REG_CHIP_EN, enable); +} + +static int lp5860_led_init(struct lp5860_led *led, struct fwnode_handle *fwnode, + unsigned int channel) +{ + enum led_default_state default_state; + unsigned int brightness; + int ret; + + guard(mutex)(&led->chip->lock); + ret = regmap_read(led->chip->regmap, LP5860_REG_PWM_BRI_START + channel, &brightness); + if (ret) + return ret; + + default_state = led_init_default_state_get(fwnode); + + switch (default_state) { + case LEDS_DEFSTATE_ON: + led->brightness = LP5860_MAX_BRIGHTNESS; + break; + case LEDS_DEFSTATE_KEEP: + led->brightness = min(brightness, LP5860_MAX_BRIGHTNESS); + break; + default: + led->brightness = 0; + break; + } + + return 0; +} + +static int lp5860_iterate_subleds(struct lp5860_led *led, struct led_init_data *init_data) +{ + struct fwnode_handle *led_node = NULL; + struct fwnode_handle *multi_led = init_data->fwnode; + int subled = 0; + + fwnode_for_each_child_node(multi_led, led_node) { + u32 channel; + u32 color_index; + int ret; + + ret = fwnode_property_read_u32(led_node, "color", &color_index); + if (ret) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: Cannot read 'color' property. Skipping.\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + ret = fwnode_property_read_u32(led_node, "reg", &channel); + if (ret < 0 || channel > LP5860_MAX_LED) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: 'reg' property is missing. Skipping.\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + led->mc_cdev.subled_info[subled].color_index = color_index; + led->mc_cdev.subled_info[subled].channel = channel; + ret = lp5860_led_init(led, init_data->fwnode, channel); + if (ret) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: Failed to init LED\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + subled++; + } + + return 0; +} + +static int lp5860_init_dt(struct lp5860 *lp) +{ + struct led_init_data init_data = {}; + struct led_classdev *led_cdev; + struct mc_subled *mc_led_info; + struct lp5860_led *led; + int led_index = 0; + int chan; + int ret; + + device_for_each_child_node_scoped(lp->dev, multi_led) { + led = &lp->leds[led_index]; + + init_data.fwnode = multi_led; + + /* Count the number of channels in this multi_led */ + chan = fwnode_get_child_node_count(multi_led); + if (!chan || chan > LP5860_MAX_LED_CHANNELS) + return -EINVAL; + + led->mc_cdev.num_colors = chan; + + mc_led_info = devm_kcalloc(lp->dev, chan, sizeof(*mc_led_info), GFP_KERNEL); + if (!mc_led_info) + return -ENOMEM; + + led->chip = lp; + led->mc_cdev.subled_info = mc_led_info; + led_cdev = &led->mc_cdev.led_cdev; + led_cdev->max_brightness = LP5860_MAX_BRIGHTNESS; + led_cdev->brightness_set_blocking = lp5860_set_mc_brightness; + + ret = lp5860_iterate_subleds(led, &init_data); + if (ret) + continue; + + ret = lp5860_set_mc_brightness(&led->mc_cdev.led_cdev, led->brightness); + if (ret) + return dev_err_probe(lp->dev, ret, "%pfwP: Failed to set Multi-Color brightness\n", + multi_led); + + ret = devm_led_classdev_multicolor_register_ext(lp->dev, &led->mc_cdev, &init_data); + if (ret) + return dev_err_probe(lp->dev, ret, "%pfwP: Failed to register Multi-Color LEDs\n", + multi_led); + led_index++; + } + + return 0; +} + +int lp5860_device_init(struct device *dev) +{ + struct lp5860 *lp = dev_get_drvdata(dev); + int ret; + + ret = lp5860_chip_enable(lp, LP5860_CHIP_ENABLE); + if (ret) + return ret; + + /* + * Set to 8-bit PWM data without VSYNC. + * Data is sent out for display instantly after received. + */ + mutex_lock(&lp->lock); + ret = regmap_update_bits(lp->regmap, LP5860_REG_DEV_INITIAL, LP5860_MODE_MASK, + LP5860_MODE_1 << LP5860_MODE_SHIFT); + if (ret) + goto err_disable; + mutex_unlock(&lp->lock); + + ret = lp5860_init_dt(lp); + if (ret) + goto err_disable; + + return 0; + +err_disable: + mutex_unlock(&lp->lock); + lp5860_chip_enable(lp, LP5860_CHIP_DISABLE); + return ret; +} +EXPORT_SYMBOL_GPL(lp5860_device_init); + +void lp5860_device_remove(struct device *dev) +{ + struct lp5860 *lp = dev_get_drvdata(dev); + + lp5860_chip_enable(lp, LP5860_CHIP_DISABLE); +} +EXPORT_SYMBOL_GPL(lp5860_device_remove); + +MODULE_AUTHOR("Steffen Trumtrar "); +MODULE_DESCRIPTION("TI LP5860 RGB LED core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/rgb/leds-lp5860-spi.c b/drivers/leds/rgb/leds-lp5860-spi.c new file mode 100644 index 000000000000..5e0c44854a68 --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860-spi.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "leds-lp5860.h" + +#define LP5860_SPI_WRITE_FLAG BIT(13) + +/* + * The lp5860 uses a rather uncommon SPI data format: The R/W flag is on BIT(5) in the two address + * bytes; BIT(4) to BIT(0) are don't care. Therefore it has 10 bits for the address and 6 bits for + * padding the address. The address bytes are sent MSB first. Matching the cores registers to regmap + * results in write_flag_mask being BIT(13). + */ +static const struct regmap_config lp5860_regmap_config = { + .name = "lp5860", + .reg_bits = 10, + .pad_bits = 6, + .val_bits = 8, + .write_flag_mask = LP5860_SPI_WRITE_FLAG, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = LP5860_MAX_REG, +}; + +static int lp5860_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct lp5860 *lp5860; + unsigned int multi_leds; + + multi_leds = device_get_child_node_count(dev); + if (!multi_leds) { + dev_err(dev, "LEDs are not defined in Device Tree!"); + return -ENODEV; + } + + if (multi_leds > LP5860_MAX_LED) { + dev_err(dev, "Too many LEDs specified.\n"); + return -EINVAL; + } + + lp5860 = devm_kzalloc(dev, struct_size(lp5860, leds, multi_leds), + GFP_KERNEL); + if (!lp5860) + return -ENOMEM; + + lp5860->regmap = devm_regmap_init_spi(spi, &lp5860_regmap_config); + if (IS_ERR(lp5860->regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(lp5860->regmap), + "Failed to initialise Regmap.\n"); + + lp5860->dev = dev; + mutex_init(&lp5860->lock); + + spi_set_drvdata(spi, lp5860); + + return lp5860_device_init(dev); +} + +static void lp5860_remove(struct spi_device *spi) +{ + struct lp5860 *lp5860 = spi_get_drvdata(spi); + + mutex_destroy(&lp5860->lock); + + lp5860_device_remove(&spi->dev); +} + +static const struct of_device_id lp5860_of_match[] = { + { .compatible = "ti,lp5860" }, + {} +}; +MODULE_DEVICE_TABLE(of, lp5860_of_match); + +static struct spi_driver lp5860_driver = { + .driver = { + .name = "lp5860-spi", + .of_match_table = lp5860_of_match, + }, + .probe = lp5860_probe, + .remove = lp5860_remove, +}; +module_spi_driver(lp5860_driver); + +MODULE_AUTHOR("Steffen Trumtrar "); +MODULE_DESCRIPTION("TI LP5860 RGB LED SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/rgb/leds-lp5860.h b/drivers/leds/rgb/leds-lp5860.h new file mode 100644 index 000000000000..940be0c6e8da --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#ifndef _DRIVERS_LEDS_RGB_LP5860_H +#define _DRIVERS_LEDS_RGB_LP5860_H + +#include +#include + +#define LP5860_REG_CHIP_EN 0x00 +#define LP5860_REG_DEV_INITIAL 0x01 +#define LP5860_REG_DEV_CONFIG1 0x02 +#define LP5860_REG_DEV_CONFIG2 0x03 +#define LP5860_REG_DEV_CONFIG3 0x04 +#define LP5860_REG_GLOBAL_BRI 0x05 +#define LP5860_REG_GROUP0_BRI 0x06 +#define LP5860_REG_GROUP1_BRI 0x07 +#define LP5860_REG_GROUP2_BRI 0x08 +#define LP5860_REG_R_CURRENT_SET 0x09 +#define LP5860_REG_G_CURRENT_SET 0x0A +#define LP5860_REG_B_CURRENT_SET 0x0B +#define LP5860_REG_GRP_SEL_START 0x0C +#define LP5860_REG_DOT_ONOFF_START 0x43 +#define LP5860_REG_DOT_ONOFF_MAX 0x63 +#define LP5860_REG_FAULT_STATE 0x64 +#define LP5860_REG_DOT_LOD_START 0x65 +#define LP5860_REG_DOT_LSD_START 0x86 +#define LP5860_REG_LOD_CLEAR 0xA7 +#define LP5860_REG_LSD_CLEAR 0xA8 +#define LP5860_REG_RESET 0xA9 +#define LP5860_REG_DC_START 0x0100 +#define LP5860_REG_PWM_BRI_START 0x0200 +#define LP5860_MAX_REG 0x038B + +/* Register chip_enable value */ +#define LP5860_CHIP_SHIFT 0 +#define LP5860_CHIP_MASK BIT(0) +#define LP5860_CHIP_DISABLE false +#define LP5860_CHIP_ENABLE true + +/* Register dev_initial value */ +#define LP5860_MAX_LINE_SHIFT 3 +#define LP5860_MAX_LINE_MASK GENMASK(6, 3) +#define LP5860_MAX_LINE_11 0x0B +#define LP5860_MAX_LINE_10 0x0A +#define LP5860_MAX_LINE_9 0x09 +#define LP5860_MAX_LINE_8 0x08 +#define LP5860_MAX_LINE_7 0x07 +#define LP5860_MAX_LINE_6 0x06 +#define LP5860_MAX_LINE_5 0x05 +#define LP5860_MAX_LINE_4 0x04 +#define LP5860_MAX_LINE_3 0x03 +#define LP5860_MAX_LINE_2 0x02 +#define LP5860_MAX_LINE_1 0x01 + +#define LP5860_MODE_SHIFT 1 +#define LP5860_MODE_MASK GENMASK(2, 1) +#define LP5860_MODE_3_1 0x03 +#define LP5860_MODE_3 0x02 +#define LP5860_MODE_2 0x01 +#define LP5860_MODE_1 0x00 + +#define LP5860_PWM_FREQUENCY_SHIFT 0 +#define LP5860_PWM_FREQUENCY_MASK BIT(0) +#define LP5860_PWM_FREQUENCY_62_5K 0x01 +#define LP5860_PWM_FREQUENCY_125K 0x00 + +/* Register dev_config1 value */ +#define LP5860_SW_BLK_SHIFT 3 +#define LP5860_SW_BLK_MASK BIT(3) +#define LP5860_SW_BLK_05US 0x01 +#define LP5860_SW_BLK_1US 0x00 + +#define LP5860_PWM_SCALE_MODE_SHIFT 2 +#define LP5860_PWM_SCALE_MODE_MASK BIT(2) +#define LP5860_PWM_SCALE_EXPONENTIAL 0x01 +#define LP5860_PWM_SCALE_LINEAR 0x00 + +#define LP5860_PWM_PHASESHIFT_SHIFT 1 +#define LP5860_PWM_PHASESHIFT_MASK BIT(1) +#define LP5860_PWM_PHASESHIFT_ON 0x01 +#define LP5860_PWM_PHASESHIFT_OFF 0x00 + +#define LP5860_CS_ON_SHIFT_SHIFT 0 +#define LP5860_CS_ON_SHIFT_MASK BIT(0) +#define LP5860_CS_DELAY_ON 0x01 +#define LP5860_CS_DELAY_OFF 0x00 + +/* Register dev_config2 value */ +#define LP5860_COMP_GROUP3_SHIFT 6 +#define LP5860_COMP_GROUP3_MASK GENMASK(7, 6) +#define LP5860_COMP_GROUP3_3CLOCK 0x03 +#define LP5860_COMP_GROUP3_2CLOCK 0x02 +#define LP5860_COMP_GROUP3_1CLOCK 0x01 +#define LP5860_COMP_GROUP3_OFF 0x00 + +#define LP5860_COMP_GROUP2_SHIFT 4 +#define LP5860_COMP_GROUP2_MASK GENMASK(5, 4) +#define LP5860_COMP_GROUP2_3CLOCK 0x03 +#define LP5860_COMP_GROUP2_2CLOCK 0x02 +#define LP5860_COMP_GROUP2_1CLOCK 0x01 +#define LP5860_COMP_GROUP2_OFF 0x00 + +#define LP5860_COMP_GROUP1_SHIFT 2 +#define LP5860_COMP_GROUP1_MASK GENMASK(3, 2) +#define LP5860_COMP_GROUP1_3CLOCK 0x03 +#define LP5860_COMP_GROUP1_2CLOCK 0x02 +#define LP5860_COMP_GROUP1_1CLOCK 0x01 +#define LP5860_COMP_GROUP1_OFF 0x00 + +#define LP5860_LOD_REMOVAL_SHIFT 1 +#define LP5860_LOD_REMOVAL_MASK BIT(1) +#define LP5860_LOD_REMOVAL_EN 0x01 +#define LP5860_LOD_REMOVAL_OFF 0x00 + +#define LP5860_LSD_REMOVAL_SHIFT 0 +#define LP5860_LSD_REMOVAL_MASK BIT(0) +#define LP5860_LSD_REMOVAL_EN 0x01 +#define LP5860_LSD_REMOVAL_OFF 0x00 + +/* Register dev_config3 value */ +#define LP5860_DOWN_DEGHOST_SHIFT 6 +#define LP5860_DOWN_DEGHOST_MASK GENMASK(7, 6) +#define LP5860_DOWN_DEGHOST_STRONG 0x03 +#define LP5860_DOWN_DEGHOST_MEDIUM 0x02 +#define LP5860_DOWN_DEGHOST_WEAK 0x01 +#define LP5860_DOWN_DEGHOST_OFF 0x00 + +#define LP5860_UP_DEGHOST_SHIFT 4 +#define LP5860_UP_DEGHOST_MASK GENMASK(5, 4) +#define LP5860_UP_DEGHOST_GND 0x03 +#define LP5860_UP_DEGHOST_3 0x02 +#define LP5860_UP_DEGHOST_2_5 0x01 +#define LP5860_UP_DEGHOST_2 0x00 + +#define LP5860_MAXIMUM_CURRENT_SHIFT 1 +#define LP5860_MAXIMUM_CURRENT_MASK GENMASK(3, 1) +#define LP5860_MAXIMUM_CURRENT_50 0x07 +#define LP5860_MAXIMUM_CURRENT_40 0x06 +#define LP5860_MAXIMUM_CURRENT_30 0x05 +#define LP5860_MAXIMUM_CURRENT_20 0x04 +#define LP5860_MAXIMUM_CURRENT_15 0x03 +#define LP5860_MAXIMUM_CURRENT_10 0x02 +#define LP5860_MAXIMUM_CURRENT_5 0x01 +#define LP5860_MAXIMUM_CURRENT_3 0x00 + +#define LP5860_UP_DEGHOST_ENABLE_SHIFT 0 +#define LP5860_UP_DEGHOST_ENABLE_MASK BIT(0) +#define LP5860_UP_DEGHOST_ENABLE_EN 0x01 +#define LP5860_UP_DEGHOST_ENABLE_OFF 0x00 + +/* Register PWM */ +#define LP5860_PWM_GLOBAL_MAX 0xff +#define LP5860_PWM_GROUP_MAX 0xff + +/* Register CC group select */ +#define LP5860_CC_GROUP_MASK GENMASK(7, 0) +#define LP5860_CC_GROUP_MAX 0x7F + +/* Register dot group select */ +#define LP5860_DOT_0_SHIFT 0 +#define LP5860_DOT_1_SHIFT 2 +#define LP5860_DOT_2_SHIFT 4 +#define LP5860_DOT_3_SHIFT 6 + +#define LP5860_DOT_GROUP3 0x03 +#define LP5860_DOT_GROUP2 0x02 +#define LP5860_DOT_GROUP1 0x01 +#define LP5860_DOT_GROUP_NONE 0x00 + +#define LP5860_DOT_ALL_ON 0xff +#define LP5860_DOT_ALL_OFF 0x0 +#define LP5860_PWM_DOT_MAX 0xff +/* Dot onoff value */ +#define LP5860_DOT_CS0_SHIFT 0 +#define LP5860_DOT_CS1_SHIFT 1 +#define LP5860_DOT_CS2_SHIFT 2 +#define LP5860_DOT_CS3_SHIFT 3 +#define LP5860_DOT_CS4_SHIFT 4 +#define LP5860_DOT_CS5_SHIFT 5 +#define LP5860_DOT_CS6_SHIFT 6 +#define LP5860_DOT_CS7_SHIFT 7 + +#define LP5860_DOT_CS_ON 0x01 +#define LP5860_DOT_CS_OFF 0x00 + +/* Dot lod value */ +#define LP5860_DOT_LOD0_SHIFT 0 +#define LP5860_DOT_LOD1_SHIFT 1 +#define LP5860_DOT_LOD2_SHIFT 2 +#define LP5860_DOT_LOD3_SHIFT 3 +#define LP5860_DOT_LOD4_SHIFT 4 +#define LP5860_DOT_LOD5_SHIFT 5 +#define LP5860_DOT_LOD6_SHIFT 6 +#define LP5860_DOT_LOD7_SHIFT 7 + +#define LP5860_DOT_LOD_ON 0x01 +#define LP5860_DOT_LOD_OFF 0x00 + +/* dot lsd value */ +#define LP5860_DOT_LSD0_SHIFT 0 +#define LP5860_DOT_LSD1_SHIFT 1 +#define LP5860_DOT_LSD2_SHIFT 2 +#define LP5860_DOT_LSD3_SHIFT 3 +#define LP5860_DOT_LSD4_SHIFT 4 +#define LP5860_DOT_LSD5_SHIFT 5 +#define LP5860_DOT_LSD6_SHIFT 6 +#define LP5860_DOT_LSD7_SHIFT 7 + +#define LP5860_DOT_LSD_ON 0x01 +#define LP5860_DOT_LSD_OFF 0x00 + +/* Register lod state */ +#define LP5860_GLOBAL_LOD_SHIFT 1 +#define LP5860_GLOBAL_LOD_STATE BIT(1) +#define LP5860_GLOBAL_LSD_SHIFT 0 +#define LP5860_GLOBAL_LSD_STATE BIT(0) + +#define LP5860_FAULT_STATE_ON 0x01 +#define LP5860_FAULT_STATE_OFF 0x00 + +#define LP5860_GLOBAL_LOD_CLEAR 0x00 +#define LP5860_GLOBAL_LSD_CLEAR 0x00 + + +#define LP5860_LOD_CLEAR_EN 0xff +#define LP5860_LSD_CLEAR_EN 0xff +#define LP5860_RESET_EN 0xff + +#define LP5860_MAX_BRIGHTNESS 255 +#define LP5860_REG_R_PWM 0x0 +#define LP5860_REG_G_PWM 0x1 +#define LP5860_REG_B_PWM 0x2 + +#define LP5860_MAX_LED_CONSTANT 18 +#define LP5860_MAX_LED_SCAN 11 +#define LP5860_MAX_LED (LP5860_MAX_LED_CONSTANT * LP5860_MAX_LED_SCAN) + +#define LP5860_MAX_DOT_ONOFF_GROUP_NUM 8 + +/* + * Theoretically, there is no max channel per LED, + * limit this to a reasonable value for RGBW LEDs + */ +#define LP5860_MAX_LED_CHANNELS 4 + +struct lp5860_led { + struct lp5860 *chip; + struct led_classdev_mc mc_cdev; + u8 brightness; +}; + +struct lp5860 { + struct device *dev; + struct regmap *regmap; + struct mutex lock; + + DECLARE_FLEX_ARRAY(struct lp5860_led, leds); +}; + +int lp5860_device_init(struct device *dev); +void lp5860_device_remove(struct device *dev); + +#endif /* _DRIVERS_LEDS_RGB_LP5860_H */ From a6ec318b16f8e6b91867d0cfce22cb8b8b665eeb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:28:45 +0200 Subject: [PATCH 3866/5214] leds: bcm63138/cros_ec: Move MODULE_DEVICE_TABLE next to the table itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the privers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Acked-by: Thomas Weißschuh # leds-cros_ec.c Reviewed-by: Tzung-Bi Shih Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260505102846.186219-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/leds/blink/leds-bcm63138.c | 2 +- drivers/leds/leds-cros_ec.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/leds/blink/leds-bcm63138.c b/drivers/leds/blink/leds-bcm63138.c index ef2e511438cc..45c0662df933 100644 --- a/drivers/leds/blink/leds-bcm63138.c +++ b/drivers/leds/blink/leds-bcm63138.c @@ -296,6 +296,7 @@ static const struct of_device_id bcm63138_leds_of_match_table[] = { { .compatible = "brcm,bcm63138-leds", }, { }, }; +MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table); static struct platform_driver bcm63138_leds_driver = { .probe = bcm63138_leds_probe, @@ -310,4 +311,3 @@ module_platform_driver(bcm63138_leds_driver); MODULE_AUTHOR("Rafał Miłecki"); MODULE_DESCRIPTION("Broadcom BCM63138 SoC LED driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table); diff --git a/drivers/leds/leds-cros_ec.c b/drivers/leds/leds-cros_ec.c index bea3cc3fbfd2..6592ceee866a 100644 --- a/drivers/leds/leds-cros_ec.c +++ b/drivers/leds/leds-cros_ec.c @@ -249,6 +249,7 @@ static const struct platform_device_id cros_ec_led_id[] = { { "cros-ec-led", 0 }, {} }; +MODULE_DEVICE_TABLE(platform, cros_ec_led_id); static struct platform_driver cros_ec_led_driver = { .driver.name = "cros-ec-led", @@ -257,7 +258,6 @@ static struct platform_driver cros_ec_led_driver = { }; module_platform_driver(cros_ec_led_driver); -MODULE_DEVICE_TABLE(platform, cros_ec_led_id); MODULE_DESCRIPTION("ChromeOS EC LED Driver"); MODULE_AUTHOR("Thomas Weißschuh Date: Wed, 6 May 2026 09:48:42 +0300 Subject: [PATCH 3867/5214] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Document TI LM3559 and LM3560 Synchronous Boost Flash Driver used for camera flash LEDs. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Conor Dooley Link: https://patch.msgid.link/20260506064847.37795-2-clamor95@gmail.com Signed-off-by: Lee Jones --- .../devicetree/bindings/leds/ti,lm3560.yaml | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3560.yaml diff --git a/Documentation/devicetree/bindings/leds/ti,lm3560.yaml b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml new file mode 100644 index 000000000000..6cf8cf91ab2e --- /dev/null +++ b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/ti,lm3560.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI LM3560 Synchronous Boost Flash Driver + +maintainers: + - Svyatoslav Ryhel + +description: + The LM3560 is a 2-MHz fixed frequency synchronous boost converter with two + 1000-mA constant current drivers for high-current white LEDs. The dual high- + side current sources allow for grounded cathode LED operation and can be + tied together for providing flash currents at up to 2 A through a single LED. + An adaptive regulation method ensures the current for each LED remains in + regulation and maximizes efficiency. + +properties: + compatible: + enum: + - ti,lm3559 + - ti,lm3560 + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + enable-gpios: + description: GPIO connected to the HWEN pin. + maxItems: 1 + + vin-supply: + description: Supply connected to the IN line. + + flash-max-timeout-us: + minimum: 32000 + maximum: 1024000 + default: 32000 + + ti,peak-current-microamp: + description: + The LM3560 features 4 selectable current limits 1.6A, 2.3A, 3A, and 3.6A + (in case of LM3559 - 1.4A, 2.1A, 2.7A, and 3.2A). When the current limit + is reached, the LM3559/LM3560 stops switching for the remainder of the + switching cycle. + +patternProperties: + '^led@[01]$': + type: object + $ref: /schemas/leds/common.yaml# + description: LED control bank nodes. + unevaluatedProperties: false + + properties: + reg: + description: Control bank selection (0 = bank A, 1 = bank B). + maximum: 1 + + required: + - reg + - flash-max-microamp + - led-max-microamp + +allOf: + - $ref: /schemas/leds/common.yaml# + - if: + properties: + compatible: + contains: + const: ti,lm3559 + then: + properties: + ti,peak-current-microamp: + enum: [1400000, 2100000, 2700000, 3200000] + default: 1400000 + patternProperties: + '^led@[01]$': + properties: + flash-max-microamp: + minimum: 56250 + maximum: 900000 + led-max-microamp: + minimum: 28125 + maximum: 225000 + + - if: + properties: + compatible: + contains: + const: ti,lm3560 + then: + properties: + ti,peak-current-microamp: + enum: [1600000, 2300000, 3000000, 3600000] + default: 1600000 + patternProperties: + '^led@[01]$': + properties: + flash-max-microamp: + minimum: 62500 + maximum: 1000000 + led-max-microamp: + minimum: 31250 + maximum: 250000 + +required: + - compatible + - reg + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@53 { + compatible = "ti,lm3560"; + reg = <0x53>; + + enable-gpios = <&gpio 28 GPIO_ACTIVE_HIGH>; + vin-supply = <&vdd_3v3_sys>; + + flash-max-timeout-us = <1024000>; + ti,peak-current-microamp = <1600000>; + + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + + function = LED_FUNCTION_FLASH; + color = ; + + flash-max-microamp = <562500>; + led-max-microamp = <156250>; + }; + + led@1 { + reg = <1>; + + function = LED_FUNCTION_FLASH; + color = ; + + flash-max-microamp = <562500>; + led-max-microamp = <156250>; + }; + }; + }; From 2682d6308654850ee4d4f9b3bbe3a31417890594 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 May 2026 23:46:03 +0200 Subject: [PATCH 3868/5214] leds: Introduce the multi_max_intensity sysfs attribute Some multicolor LEDs support global brightness control in hardware, meaning that the maximum intensity of the color components is not connected to the maximum global brightness. Such LEDs cannot be described properly by the current multicolor LED class interface, because it assumes that the maximum intensity of each color component is described by the maximum global brightness of the LED. Fix this by introducing a new sysfs attribute called "multi_max_intensity" holding the maximum intensity values for the color components of a multicolor LED class device. Drivers can use the new max_intensity field inside struct mc_subled to tell the multicolor LED class code about those values. Intensity values written by userspace applications will be limited to this maximum value. Drivers for multicolor LEDs that do not support global brightness control in hardware might still want to use the maximum global LED brightness supplied via devicetree as the maximum intensity of each individual color component. Such drivers should set max_intensity to 0 so that the multicolor LED core can act accordingly. The lp50xx and ncp5623 LED drivers already use hardware-based control for the global LED brightness. Modify those drivers to correctly initalize .max_intensity to avoid being limited to the maximum global brightness supplied via devicetree. Reviewed-by: Werner Sembach Reviewed-by: Jacek Anaszewski Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260509214603.262368-2-W_Armin@gmx.de Signed-off-by: Lee Jones --- .../ABI/testing/sysfs-class-led-multicolor | 19 ++++++-- Documentation/leds/leds-class-multicolor.rst | 21 ++++++++- drivers/leds/led-class-multicolor.c | 47 ++++++++++++++++++- drivers/leds/leds-lp50xx.c | 1 + drivers/leds/rgb/leds-ncp5623.c | 4 +- include/linux/led-class-multicolor.h | 30 +++++++++++- 6 files changed, 113 insertions(+), 9 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-led-multicolor b/Documentation/ABI/testing/sysfs-class-led-multicolor index 16fc827b10cb..197da3e775b4 100644 --- a/Documentation/ABI/testing/sysfs-class-led-multicolor +++ b/Documentation/ABI/testing/sysfs-class-led-multicolor @@ -16,9 +16,22 @@ Date: March 2020 KernelVersion: 5.9 Contact: Dan Murphy Description: read/write - This file contains array of integers. Order of components is - described by the multi_index array. The maximum intensity should - not exceed /sys/class/leds//max_brightness. + This file contains an array of integers. The order of components + is described by the multi_index array. The maximum intensity value + supported by each color component is described by the multi_max_intensity + file. Writing intensity values larger than the maximum value of a + given color component will result in those values being clamped. + + For additional details please refer to + Documentation/leds/leds-class-multicolor.rst. + +What: /sys/class/leds//multi_max_intensity +Date: March 2026 +KernelVersion: 7.1 +Contact: Armin Wolf +Description: read + This file contains an array of integers describing the maximum + intensity value for each intensity component. For additional details please refer to Documentation/leds/leds-class-multicolor.rst. diff --git a/Documentation/leds/leds-class-multicolor.rst b/Documentation/leds/leds-class-multicolor.rst index c6b47b4093c4..68340644f80b 100644 --- a/Documentation/leds/leds-class-multicolor.rst +++ b/Documentation/leds/leds-class-multicolor.rst @@ -25,10 +25,14 @@ color name to indexed value. The ``multi_index`` file is an array that contains the string list of the colors as they are defined in each ``multi_*`` array file. -The ``multi_intensity`` is an array that can be read or written to for the +The ``multi_intensity`` file is an array that can be read or written to for the individual color intensities. All elements within this array must be written in order for the color LED intensities to be updated. +The ``multi_max_intensity`` file is an array that contains the maximum intensity +value supported by each color intensity. Intensity values above this will be +automatically clamped into the supported range. + Directory Layout Example ======================== .. code-block:: console @@ -38,6 +42,7 @@ Directory Layout Example -r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness -r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index -rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity + -r--r--r-- 1 root root 4096 Oct 19 16:16 multi_max_intensity .. @@ -104,3 +109,17 @@ the color LED group. 128 .. + +Writing intensity values larger than the maximum specified in ``multi_max_intensity`` +will result in those values being clamped into the supported range. + +.. code-block:: console + + # cat /sys/class/leds/multicolor:status/multi_max_intensity + 255 255 255 + + # echo 512 512 512 > /sys/class/leds/multicolor:status/multi_intensity + # cat /sys/class/leds/multicolor:status/multi_intensity + 255 255 255 + +.. diff --git a/drivers/leds/led-class-multicolor.c b/drivers/leds/led-class-multicolor.c index 6b671f3f9c61..59d46f8a90d5 100644 --- a/drivers/leds/led-class-multicolor.c +++ b/drivers/leds/led-class-multicolor.c @@ -7,10 +7,29 @@ #include #include #include +#include #include #include #include +static unsigned int led_mc_get_max_intensity(struct led_classdev_mc *mcled_cdev, size_t index) +{ + unsigned int max_intensity; + + /* + * The maximum global brightness value might still be changed by + * led_classdev_register_ext() using devicetree properties. This + * prevents us from changing subled_info[X].max_intensity when + * registering a multicolor LED class device, so we have to do + * this during runtime. + */ + max_intensity = mcled_cdev->subled_info[index].max_intensity; + if (max_intensity) + return max_intensity; + + return mcled_cdev->led_cdev.max_brightness; +} + int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, enum led_brightness brightness) { @@ -27,6 +46,26 @@ int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, } EXPORT_SYMBOL_GPL(led_mc_calc_color_components); +static ssize_t multi_max_intensity_show(struct device *dev, + struct device_attribute *intensity_attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + unsigned int max_intensity; + int len = 0; + + for (int i = 0; i < mcled_cdev->num_colors; i++) { + max_intensity = led_mc_get_max_intensity(mcled_cdev, i); + len += sysfs_emit_at(buf, len, "%u", max_intensity); + if (i < mcled_cdev->num_colors - 1) + len += sprintf(buf + len, " "); + } + + buf[len++] = '\n'; + return len; +} +static DEVICE_ATTR_RO(multi_max_intensity); + static ssize_t multi_intensity_store(struct device *dev, struct device_attribute *intensity_attr, const char *buf, size_t size) @@ -35,6 +74,7 @@ static ssize_t multi_intensity_store(struct device *dev, struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); int nrchars, offset = 0; unsigned int intensity_value[LED_COLOR_ID_MAX]; + unsigned int max_intensity; int i; ssize_t ret; @@ -56,8 +96,10 @@ static ssize_t multi_intensity_store(struct device *dev, goto err_out; } - for (i = 0; i < mcled_cdev->num_colors; i++) - mcled_cdev->subled_info[i].intensity = intensity_value[i]; + for (int i = 0; i < mcled_cdev->num_colors; i++) { + max_intensity = led_mc_get_max_intensity(mcled_cdev, i); + mcled_cdev->subled_info[i].intensity = min(intensity_value[i], max_intensity); + } if (!test_bit(LED_BLINK_SW, &led_cdev->work_flags)) led_set_brightness(led_cdev, led_cdev->brightness); @@ -111,6 +153,7 @@ static ssize_t multi_index_show(struct device *dev, static DEVICE_ATTR_RO(multi_index); static struct attribute *led_multicolor_attrs[] = { + &dev_attr_multi_max_intensity.attr, &dev_attr_multi_intensity.attr, &dev_attr_multi_index.attr, NULL, diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c index e2a9c8592953..69c3550f1a31 100644 --- a/drivers/leds/leds-lp50xx.c +++ b/drivers/leds/leds-lp50xx.c @@ -525,6 +525,7 @@ static int lp50xx_probe_dt(struct lp50xx *priv) } mc_led_info[multi_index].color_index = color_id; + mc_led_info[multi_index].max_intensity = 255; num_colors++; } diff --git a/drivers/leds/rgb/leds-ncp5623.c b/drivers/leds/rgb/leds-ncp5623.c index 85d6be6fff2b..f2528f06507d 100644 --- a/drivers/leds/rgb/leds-ncp5623.c +++ b/drivers/leds/rgb/leds-ncp5623.c @@ -56,8 +56,7 @@ static int ncp5623_brightness_set(struct led_classdev *cdev, for (int i = 0; i < mc_cdev->num_colors; i++) { ret = ncp5623_write(ncp->client, NCP5623_PWM_REG(mc_cdev->subled_info[i].channel), - min(mc_cdev->subled_info[i].intensity, - NCP5623_MAX_BRIGHTNESS)); + mc_cdev->subled_info[i].intensity); if (ret) return ret; } @@ -190,6 +189,7 @@ static int ncp5623_probe(struct i2c_client *client) goto release_led_node; subled_info[ncp->mc_dev.num_colors].channel = reg; + subled_info[ncp->mc_dev.num_colors].max_intensity = NCP5623_MAX_BRIGHTNESS; subled_info[ncp->mc_dev.num_colors++].color_index = color_index; } diff --git a/include/linux/led-class-multicolor.h b/include/linux/led-class-multicolor.h index db9f34c6736e..8a05836cae25 100644 --- a/include/linux/led-class-multicolor.h +++ b/include/linux/led-class-multicolor.h @@ -9,10 +9,31 @@ #include #include +/** + * struct mc_subled - Color component description. + * @color_index: Color ID. + * @brightness: Scaled intensity. + * @intensity: Current intensity. + * @max_intensity: Maximum supported intensity value. + * @channel: Channel index. + * + * Describes a color component of a multicolor LED. Many multicolor LEDs + * do not support global brightness control in hardware, so they use + * the brightness field in connection with led_mc_calc_color_components() + * to perform the intensity scaling in software. + * Such drivers should set max_intensity to 0 to signal the multicolor LED core + * that the maximum global brightness of the LED class device should be used for + * limiting incoming intensity values. + * + * Multicolor LEDs that do support global brightness control in hardware + * should instead set max_intensity to the maximum intensity value supported + * by the hardware for a given color component. + */ struct mc_subled { unsigned int color_index; unsigned int brightness; unsigned int intensity; + unsigned int max_intensity; unsigned int channel; }; @@ -53,7 +74,14 @@ int led_classdev_multicolor_register_ext(struct device *parent, */ void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev); -/* Calculate brightness for the monochrome LED cluster */ +/** + * led_mc_calc_color_components() - Calculates component brightness values of a LED cluster. + * @mcled_cdev - Multicolor LED class device of the LED cluster. + * @brightness - Global brightness of the LED cluster. + * + * Calculates the brightness values for each color component of a monochrome LED cluster, + * see Documentation/leds/leds-class-multicolor.rst for details. + */ int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, enum led_brightness brightness); From d0316c89636d72a8a6766e82453570f7bd003b21 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 10 May 2026 05:36:32 +0500 Subject: [PATCH 3869/5214] leds: dac124s085: Declare SPI command word as __le16 dac124s085_set_brightness() builds a 16-bit SPI command word: u16 word; ... word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE | (brightness & 0xfff)); ret = spi_write(led->spi, (const u8 *)&word, sizeof(word)); cpu_to_le16() returns __le16, but the local 'word' is declared as plain u16, which sparse flags: drivers/leds/leds-dac124s085.c:42:14: warning: incorrect type in assignment (different base types) The bytes that hit the wire are correct because cpu_to_le16() does the right thing on either endianness, but mixing the annotated and unannotated types defeats sparse's __bitwise checking and would let a future reader treat the buffer as a host-endian u16 by mistake. Declare 'word' as __le16 to match how it is built and consumed. No functional change. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260510003632.35942-1-sozdayvek@gmail.com Signed-off-by: Lee Jones --- drivers/leds/leds-dac124s085.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c index cf5fb1195f87..192b43333aee 100644 --- a/drivers/leds/leds-dac124s085.c +++ b/drivers/leds/leds-dac124s085.c @@ -35,7 +35,7 @@ static int dac124s085_set_brightness(struct led_classdev *ldev, { struct dac124s085_led *led = container_of(ldev, struct dac124s085_led, ldev); - u16 word; + __le16 word; int ret; mutex_lock(&led->mutex); From 6ebff754c46e2277c831670fa1f0b5043250eb77 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:05:24 +0200 Subject: [PATCH 3870/5214] leds: Adjust documentation of brightness sysfs node Adjust documentation of brightness sysfs node about accepted value range. The code accepts only decimal values. We may not relax that due to different readings for, e.g., octal 0100, which becomes 64 instead of currently parsed 100. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220620.369825-1-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- Documentation/ABI/testing/sysfs-class-led | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led index 0313b82644f2..d4c918cc11a1 100644 --- a/Documentation/ABI/testing/sysfs-class-led +++ b/Documentation/ABI/testing/sysfs-class-led @@ -22,8 +22,8 @@ Description: For additional details please refer to Documentation/leds/leds-class-multicolor.rst. - The value is between 0 and - /sys/class/leds//max_brightness. + The value is between 0 and /sys/class/leds//max_brightness + and is represented by as a decimal. Writing 0 to this file clears active trigger. From f92135f100669b508dd62b424ab20bcb33494c79 Mon Sep 17 00:00:00 2001 From: Craig McQueen Date: Thu, 23 Apr 2026 21:36:38 +1000 Subject: [PATCH 3871/5214] leds: core: Fix race condition for software blink led_set_brightness() function: Change handling of software blink to avoid race conditions when stopping blink and setting brightness. Triggers may call led_set_brightness(LED_OFF), led_set_brightness(LED_FULL) in quick succession to disable blinking and turn the LED on. If the delayed work task has not yet disabled blinking by the time the second call occurs, then the brightness also needs to be changed in the delayed work task. Signed-off-by: Craig McQueen Link: https://patch.msgid.link/20260423113638.2079302-1-craig@mcqueen.au Signed-off-by: Lee Jones --- drivers/leds/led-core.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 385e78af1dac..073c547068cc 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -303,24 +303,31 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink); void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness) { - /* - * If software blink is active, delay brightness setting - * until the next timer tick. - */ - if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) { + if (brightness) { + /* + * If software blink disable is pending, also queue brightness setting. + * If software blink is active, delay brightness setting + * until the next timer tick. + */ + if (test_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags) || + test_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) { + led_cdev->delayed_set_value = brightness; + set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags); + queue_work(led_cdev->wq, &led_cdev->set_brightness_work); + return; + } else if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) { + led_cdev->new_blink_brightness = brightness; + set_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags); + return; + } + } else if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) { /* * If we need to disable soft blinking delegate this to the * work queue task to avoid problems in case we are called * from hard irq context. */ - if (!brightness) { - set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags); - queue_work(led_cdev->wq, &led_cdev->set_brightness_work); - } else { - set_bit(LED_BLINK_BRIGHTNESS_CHANGE, - &led_cdev->work_flags); - led_cdev->new_blink_brightness = brightness; - } + set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags); + queue_work(led_cdev->wq, &led_cdev->set_brightness_work); return; } From 38569c751ccfa8a37ce4724b12548ae5fa9e7f57 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:33 +0530 Subject: [PATCH 3872/5214] dt-bindings: leds: Document Samsung S2M series PMIC flash LED device Certain Samsung S2M series PMICs have a flash LED controller with two LED channels, and with torch and flash control modes. Document the devicetree schema for the device. Signed-off-by: Kaustabh Chakraborty Acked-by: Conor Dooley Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-1-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- .../bindings/leds/samsung,s2mu005-flash.yaml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml diff --git a/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml b/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml new file mode 100644 index 000000000000..36051ab20509 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/samsung,s2mu005-flash.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/samsung,s2mu005-flash.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Flash and Torch LED Controller for Samsung S2M series PMICs + +maintainers: + - Kaustabh Chakraborty + +description: | + The Samsung S2M series PMIC flash LED has two led channels (typically + as back and front camera flashes), with support for both torch and + flash modes. + + This is a part of device tree bindings for S2M and S5M family of Power + Management IC (PMIC). + + See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for + additional information and example. + +properties: + compatible: + enum: + - samsung,s2mu005-flash + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[0-1]$": + type: object + $ref: common.yaml# + unevaluatedProperties: false + + properties: + reg: + enum: [0, 1] + + required: + - reg + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: false From 02149db273a936703764c4b5173e533817180d25 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:38 +0530 Subject: [PATCH 3873/5214] leds: flash: Add support for Samsung S2M series PMIC flash LED device Add support for flash LEDs found in certain Samsung S2M series PMICs. The device has two channels for LEDs, typically for the back and front cameras in mobile devices. Both channels can be independently controlled, and can be operated in torch or flash modes. The driver includes initial support for the S2MU005 PMIC flash LEDs. Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-6-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- drivers/leds/flash/Kconfig | 11 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-s2m-flash.c | 350 ++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 drivers/leds/flash/leds-s2m-flash.c diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 31c996651f2a..9b6dd3f1ffe8 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -114,6 +114,17 @@ config LEDS_RT8515 To compile this driver as a module, choose M here: the module will be called leds-rt8515. +config LEDS_S2M_FLASH + tristate "Samsung S2M series PMICs flash/torch LED support" + depends on LEDS_CLASS + depends on MFD_SEC_CORE + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + help + This option enables support for the flash/torch LEDs found in certain + Samsung S2M series PMICs, such as the S2MU005. It has a LED channel + dedicated for every physical LED. The LEDs can be controlled in flash + and torch modes. + config LEDS_SGM3140 tristate "LED support for the SGM3140" depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 712fb737a428..44e6c1b4beb3 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_QCOM_FLASH) += leds-qcom-flash.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o +obj-$(CONFIG_LEDS_S2M_FLASH) += leds-s2m-flash.o obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o obj-$(CONFIG_LEDS_SY7802) += leds-sy7802.o obj-$(CONFIG_LEDS_TPS6131X) += leds-tps6131x.o diff --git a/drivers/leds/flash/leds-s2m-flash.c b/drivers/leds/flash/leds-s2m-flash.c new file mode 100644 index 000000000000..6ee8db094611 --- /dev/null +++ b/drivers/leds/flash/leds-s2m-flash.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Flash and Torch LED Driver for Samsung S2M series PMICs. + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * Copyright (c) 2026 Kaustabh Chakraborty + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHANNELS 2 + +struct s2m_led { + struct regmap *regmap; + struct led_classdev_flash fled; + struct v4l2_flash *v4l2_flash; + /* + * The mutex object prevents the concurrent access of flash control + * registers by the LED and V4L2 subsystems. + */ + struct mutex lock; + unsigned int reg_enable; + u8 channel; + u8 flash_brightness; + u8 flash_timeout; +}; + +static struct s2m_led *to_s2m_led(struct led_classdev_flash *fled) +{ + return container_of(fled, struct s2m_led, fled); +} + +static struct led_classdev_flash *to_s2m_fled(struct led_classdev *cdev) +{ + return container_of(cdev, struct led_classdev_flash, led_cdev); +} + +static int s2m_fled_flash_brightness_set(struct led_classdev_flash *fled, u32 brightness) +{ + struct s2m_led *led = to_s2m_led(fled); + struct led_flash_setting *setting = &fled->brightness; + + mutex_lock(&led->lock); + led->flash_brightness = (brightness - setting->min) / setting->step; + mutex_unlock(&led->lock); + + return 0; +} + +static int s2m_fled_flash_timeout_set(struct led_classdev_flash *fled, u32 timeout) +{ + struct s2m_led *led = to_s2m_led(fled); + struct led_flash_setting *setting = &fled->timeout; + + mutex_lock(&led->lock); + led->flash_timeout = (timeout - setting->min) / setting->step; + mutex_unlock(&led->lock); + + return 0; +} + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +static int s2m_fled_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, bool enable) +{ + struct s2m_led *led = to_s2m_led(v4l2_flash->fled_cdev); + + return led->fled.ops->strobe_set(&led->fled, enable); +} + +static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops = { + .external_strobe_set = s2m_fled_flash_external_strobe_set, +}; +#else +static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops; +#endif + +static void s2m_fled_v4l2_flash_release(void *v4l2_flash) +{ + v4l2_flash_release(v4l2_flash); +} + +static int s2mu005_fled_torch_brightness_set(struct led_classdev *cdev, enum led_brightness value) +{ + struct s2m_led *led = to_s2m_led(to_s2m_fled(cdev)); + int ret; + + mutex_lock(&led->lock); + + if (!value) { + ret = regmap_clear_bits(led->regmap, led->reg_enable, + S2MU005_FLED_TORCH_EN(led->channel)); + if (ret) + dev_err(cdev->dev, "failed to disable torch LED\n"); + goto unlock; + } + + ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL1(led->channel), + S2MU005_FLED_TORCH_IOUT, + FIELD_PREP(S2MU005_FLED_TORCH_IOUT, value - 1)); + if (ret) { + dev_err(cdev->dev, "failed to set torch current\n"); + goto unlock; + } + + ret = regmap_set_bits(led->regmap, led->reg_enable, S2MU005_FLED_TORCH_EN(led->channel)); + if (ret) { + dev_err(cdev->dev, "failed to enable torch LED\n"); + goto unlock; + } + +unlock: + mutex_unlock(&led->lock); + + return ret; +} + +static int s2mu005_fled_flash_strobe_set(struct led_classdev_flash *fled, bool state) +{ + struct s2m_led *led = to_s2m_led(fled); + int ret; + + mutex_lock(&led->lock); + + ret = regmap_clear_bits(led->regmap, led->reg_enable, S2MU005_FLED_FLASH_EN(led->channel)); + if (ret) { + dev_err(fled->led_cdev.dev, "failed to disable flash LED\n"); + goto unlock; + } + + if (!state) + goto unlock; + + ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL0(led->channel), + S2MU005_FLED_FLASH_IOUT, + FIELD_PREP(S2MU005_FLED_FLASH_IOUT, led->flash_brightness)); + if (ret) { + dev_err(fled->led_cdev.dev, "failed to set flash brightness\n"); + goto unlock; + } + + ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL3(led->channel), + S2MU005_FLED_FLASH_TIMEOUT, + FIELD_PREP(S2MU005_FLED_FLASH_TIMEOUT, led->flash_timeout)); + if (ret) { + dev_err(fled->led_cdev.dev, "failed to set flash timeout\n"); + goto unlock; + } + + ret = regmap_set_bits(led->regmap, led->reg_enable, S2MU005_FLED_FLASH_EN(led->channel)); + if (ret) { + dev_err(fled->led_cdev.dev, "failed to enable flash LED\n"); + goto unlock; + } + +unlock: + mutex_unlock(&led->lock); + + return ret; +} + +static int s2mu005_fled_flash_strobe_get(struct led_classdev_flash *fled, bool *state) +{ + struct s2m_led *led = to_s2m_led(fled); + u32 val; + int ret; + + mutex_lock(&led->lock); + + ret = regmap_read(led->regmap, S2MU005_REG_FLED_STATUS, &val); + if (ret) { + dev_err(fled->led_cdev.dev, "failed to fetch LED status\n"); + goto unlock; + } + + *state = !!(val & S2MU005_FLED_FLASH_STATUS(led->channel)); + +unlock: + mutex_unlock(&led->lock); + + return ret; +} + +static const struct led_flash_ops s2mu005_fled_flash_ops = { + .flash_brightness_set = s2m_fled_flash_brightness_set, + .timeout_set = s2m_fled_flash_timeout_set, + .strobe_set = s2mu005_fled_flash_strobe_set, + .strobe_get = s2mu005_fled_flash_strobe_get, +}; + +static int s2mu005_fled_init(struct s2m_led *led, struct device *dev, struct regmap *regmap, + unsigned int nr_channels) +{ + unsigned int val; + int ret; + + ret = regmap_read(regmap, S2MU005_REG_ID, &val); + if (ret) + return dev_err_probe(dev, ret, "failed to read revision\n"); + + for (int i = 0; i < nr_channels; i++) { + /* + * Read the revision register. Revision EVT0 has the register + * at CTRL4, while EVT1 and higher have it at CTRL6. + */ + if (FIELD_GET(S2MU005_ID_MASK, val) == 0) + led[i].reg_enable = S2MU005_REG_FLED_CTRL4; + else + led[i].reg_enable = S2MU005_REG_FLED_CTRL6; + } + + /* Enable the LED channels. */ + ret = regmap_set_bits(regmap, S2MU005_REG_FLED_CTRL1, S2MU005_FLED_CH_EN); + if (ret) + return dev_err_probe(dev, ret, "failed to enable LED channels\n"); + + return 0; +} + +static int s2mu005_fled_init_channel(struct s2m_led *led, struct device *dev, + struct fwnode_handle *fwnp) +{ + struct led_classdev *cdev = &led->fled.led_cdev; + struct led_init_data init_data = {}; + struct v4l2_flash_config v4l2_cfg = {}; + int ret; + + cdev->max_brightness = 16; + cdev->brightness_set_blocking = s2mu005_fled_torch_brightness_set; + cdev->flags |= LED_DEV_CAP_FLASH; + + led->fled.timeout.min = 62000; + led->fled.timeout.step = 62000; + led->fled.timeout.max = 992000; + led->fled.timeout.val = 992000; + + led->fled.brightness.min = 25000; + led->fled.brightness.step = 25000; + led->fled.brightness.max = 375000; /* 400000 causes flickering */ + led->fled.brightness.val = 375000; + + s2m_fled_flash_timeout_set(&led->fled, led->fled.timeout.val); + s2m_fled_flash_brightness_set(&led->fled, led->fled.brightness.val); + + led->fled.ops = &s2mu005_fled_flash_ops; + + init_data.fwnode = fwnp; + ret = devm_led_classdev_flash_register_ext(dev, &led->fled, &init_data); + if (ret) + return dev_err_probe(dev, ret, "failed to create LED flash device\n"); + + v4l2_cfg.intensity.min = led->fled.brightness.min; + v4l2_cfg.intensity.step = led->fled.brightness.step; + v4l2_cfg.intensity.max = led->fled.brightness.max; + v4l2_cfg.intensity.val = led->fled.brightness.val; + + v4l2_cfg.has_external_strobe = true; + + led->v4l2_flash = v4l2_flash_init(dev, fwnp, &led->fled, &s2m_fled_v4l2_flash_ops, + &v4l2_cfg); + if (IS_ERR(led->v4l2_flash)) + return dev_err_probe(dev, PTR_ERR(led->v4l2_flash), + "failed to create V4L2 flash device\n"); + + ret = devm_add_action_or_reset(dev, s2m_fled_v4l2_flash_release, led->v4l2_flash); + if (ret) + return dev_err_probe(dev, ret, "failed to add cleanup action\n"); + + return 0; +} + +static int s2m_fled_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sec_pmic_dev *ddata = dev_get_drvdata(dev->parent); + struct s2m_led *led; + int ret; + + led = devm_kzalloc(dev, sizeof(*led) * MAX_CHANNELS, GFP_KERNEL); + if (!led) + return -ENOMEM; + + /* Initialize LED controller with variant-specific implementation. */ + ret = s2mu005_fled_init(led, dev, ddata->regmap_pmic, MAX_CHANNELS); + if (ret) + return ret; + + device_for_each_child_node_scoped(dev, child) { + u32 reg; + + if (fwnode_property_read_u32(child, "reg", ®)) + continue; + + if (reg >= MAX_CHANNELS) { + dev_warn(dev, "channel %d is non-existent\n", reg); + continue; + } + + if (led[reg].regmap) { + dev_warn(dev, "duplicate node for channel %d\n", reg); + continue; + } + + led[reg].regmap = ddata->regmap_pmic; + led[reg].channel = (u8)reg; + + ret = devm_mutex_init(dev, &led[reg].lock); + if (ret) + return dev_err_probe(dev, ret, "failed to create mutex\n"); + + /* Initialize LED channel with variant-specific implementation. */ + ret = s2mu005_fled_init_channel(led + reg, dev, child); + if (ret) + return ret; + } + + return 0; +} + +static const struct platform_device_id s2m_fled_id_table[] = { + { "s2mu005-flash", S2MU005 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, s2m_fled_id_table); + +static const struct of_device_id s2m_fled_of_match_table[] = { + { .compatible = "samsung,s2mu005-flash", .data = (void *)S2MU005 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s2m_fled_of_match_table); + +static struct platform_driver s2m_fled_driver = { + .driver = { + .name = "s2m-flash", + }, + .probe = s2m_fled_probe, + .id_table = s2m_fled_id_table, +}; +module_platform_driver(s2m_fled_driver); + +MODULE_DESCRIPTION("Flash/Torch LED Driver for Samsung S2M Series PMICs"); +MODULE_AUTHOR("Kaustabh Chakraborty "); +MODULE_LICENSE("GPL"); From 63ccd117f4257c66d66bc4a49f2d7199adaefa66 Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:39 +0530 Subject: [PATCH 3874/5214] leds: rgb: Add support for Samsung S2M series PMIC RGB LED device Add support for the RGB LEDs found in certain Samsung S2M series PMICs. The device has three LED channels, controlled as a single device. These LEDs are typically used as status indicators in mobile phones. The driver includes initial support for the S2MU005 PMIC RGB LEDs. Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-7-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- drivers/leds/rgb/Kconfig | 10 + drivers/leds/rgb/Makefile | 1 + drivers/leds/rgb/leds-s2m-rgb.c | 426 ++++++++++++++++++++++++++++++++ 3 files changed, 437 insertions(+) create mode 100644 drivers/leds/rgb/leds-s2m-rgb.c diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 9a4ba6531cf8..5599b71be54d 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -100,6 +100,16 @@ config LEDS_QCOM_LPG If compiled as a module, the module will be named leds-qcom-lpg. +config LEDS_S2M_RGB + tristate "Samsung S2M series PMICs RGB LED support" + depends on LEDS_CLASS + depends on MFD_SEC_CORE + help + This option enables support for the S2MU005 RGB LEDs. These devices + have three LED channels, with 8-bit brightness control for each + channel. The S2MU005 is usually found in mobile phones as status + indicators. + config LEDS_MT6370_RGB tristate "LED Support for MediaTek MT6370 PMIC" depends on MFD_MT6370 diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index f3b365ea082d..cc0f2df66286 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_LEDS_LP5860_SPI) += leds-lp5860-spi.o obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o +obj-$(CONFIG_LEDS_S2M_RGB) += leds-s2m-rgb.o obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o diff --git a/drivers/leds/rgb/leds-s2m-rgb.c b/drivers/leds/rgb/leds-s2m-rgb.c new file mode 100644 index 000000000000..d239f54eee90 --- /dev/null +++ b/drivers/leds/rgb/leds-s2m-rgb.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RGB LED Driver for Samsung S2M series PMICs. + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd + * Copyright (c) 2026 Kaustabh Chakraborty + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct s2m_rgb { + struct device *dev; + struct regmap *regmap; + struct led_classdev_mc mc; + /* + * The mutex object prevents race conditions when evaluation and + * application of LED pattern state. + */ + struct mutex lock; + /* + * State variables representing the current LED pattern, these only to + * be accessed when lock is held. + */ + u8 ramp_up; + u8 ramp_dn; + u8 stay_hi; + u8 stay_lo; +}; + +static struct led_classdev_mc *to_s2m_mc(struct led_classdev *cdev) +{ + return container_of(cdev, struct led_classdev_mc, led_cdev); +} + +static struct s2m_rgb *to_s2m_rgb(struct led_classdev_mc *mc) +{ + return container_of(mc, struct s2m_rgb, mc); +} + +static const u32 s2mu005_rgb_lut_ramp[] = { + 0, 100, 200, 300, 400, 500, 600, 700, + 800, 1000, 1200, 1400, 1600, 1800, 2000, 2200, +}; + +static const u32 s2mu005_rgb_lut_stay_hi[] = { + 100, 200, 300, 400, 500, 750, 1000, 1250, + 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, +}; + +static const u32 s2mu005_rgb_lut_stay_lo[] = { + 0, 500, 1000, 1500, 2000, 2500, 3000, 3500, + 4000, 4500, 5000, 6000, 7000, 8000, 10000, 12000, +}; + +static int s2mu005_rgb_apply_params(struct s2m_rgb *rgb) +{ + struct regmap *regmap = rgb->regmap; + unsigned int ramp_val = 0; + unsigned int stay_val = 0; + int ret; + + ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_UP, rgb->ramp_up); + ramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_DN, rgb->ramp_dn); + + stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_HI, rgb->stay_hi); + stay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_LO, rgb->stay_lo); + + ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET); + if (ret) { + dev_err(rgb->dev, "failed to reset RGB LEDs\n"); + return ret; + } + + for (int i = 0; i < rgb->mc.num_colors; i++) { + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_CTRL(i), + rgb->mc.subled_info[i].brightness); + if (ret) { + dev_err(rgb->dev, "failed to set LED brightness\n"); + return ret; + } + + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_RAMP(i), ramp_val); + if (ret) { + dev_err(rgb->dev, "failed to set ramp timings\n"); + return ret; + } + + ret = regmap_write(regmap, S2MU005_REG_RGB_CH_STAY(i), stay_val); + if (ret) { + dev_err(rgb->dev, "failed to set stay timings\n"); + return ret; + } + } + + ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_SLOPE_SMOOTH); + if (ret) { + dev_err(rgb->dev, "failed to set ramp slope\n"); + return ret; + } + + return 0; +} + +static int s2mu005_rgb_reset_params(struct s2m_rgb *rgb) +{ + struct regmap *regmap = rgb->regmap; + int ret; + + ret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET); + if (ret) { + dev_err(rgb->dev, "failed to reset RGB LEDs\n"); + return ret; + } + + rgb->ramp_up = 0; + rgb->ramp_dn = 0; + rgb->stay_hi = 0; + rgb->stay_lo = 0; + + return 0; +} + +/* + * s2m_rgb_lut_get_closest_duration - find closest duration in look-up table + * @lut: the look-up table to search for the closest timing + * @len: number of elements in the look-up table array + * @duration: the timing duration requested + * + * This function does a binary search on the given array, and finds the closest + * value to the requested timing. It is expected that the look-up table to be + * provided, is already sorted. + * + * This function returns a negative error code, or a non-negative index of the + * value in the look-up table closest to the one requested. + */ +static int s2m_rgb_lut_get_closest_duration(const u32 *lut, const size_t len, const u32 duration) +{ + u32 closest_distance = abs(duration - lut[0]); + int closest_index = 0; + int lo = 0; + int hi = len - 1; + + /* + * Allow a small amount of extrapolation beyond the highest timing value. + * + * Consider x and y to be the two last values in the table, and x < y. + * Since (y - x) / 2 integers, in the range [x + (y - x) / 2, y) + * returns y as the closest, allow extrapolation for the succeeding + * (y - x) / 2 integers as well, viz, up to (y, y + (y - x) / 2]. + * Anything beyond that is invalid. + */ + if (len >= 2 && duration > lut[len - 1] + (lut[len - 1] - lut[len - 2]) / 2) + return -EINVAL; + + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + + /* Narrow down search window as per binary-search algorithm. */ + if (duration < lut[mid]) + hi = mid - 1; + else + lo = mid + 1; + + if (abs(duration - lut[mid]) < closest_distance) { + closest_distance = abs(duration - lut[mid]); + closest_index = mid; + } + } + + return closest_index; +} + +static int s2m_rgb_pattern_set(struct led_classdev *cdev, struct led_pattern *pattern, u32 len, + int repeat) +{ + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev)); + const u32 *lut_ramp_up, *lut_ramp_dn, *lut_stay_hi, *lut_stay_lo; + size_t lut_ramp_up_len, lut_ramp_dn_len, lut_stay_hi_len, lut_stay_lo_len; + int brightness_peak = 0; + u32 time_hi = 0, time_lo = 0; + bool ramp_up_en = false, ramp_dn_en = false; + int ret; + + /* + * The typical pattern supported by this device can be represented with + * the following graph: + * + * 255 T ''''''-. .-'''''''-. + * | '. .' '. + * | \ / \ + * | '. .' '. + * | '-...........-' '- + * 0 +----------------------------------------------------> time (s) + * + * <---- HIGH ----><-- LOW --><-------- HIGH ---------> + * <-----><-------><---------><-------><-----><-------> + * stay_hi ramp_dn stay_lo ramp_up stay_hi ramp_dn + * + * There are two states, named HIGH and LOW. HIGH has a non-zero + * brightness level, while LOW is of zero brightness. The pattern + * provided should mention only one zero and non-zero brightness level. + * The hardware always starts the pattern from the HIGH state, as shown + * in the graph. + * + * The HIGH state can be divided in three somewhat equal timings: + * ramp_up, stay_hi, and ramp_dn. The LOW state has only one timing: + * stay_lo. + */ + + /* Only indefinitely looping patterns are supported. */ + if (repeat != -1) + return -EINVAL; + + /* Pattern should consist of at least two tuples. */ + if (len < 2) + return -EINVAL; + + for (int i = 0; i < len; i++) { + int brightness = pattern[i].brightness; + u32 delta_t = pattern[i].delta_t; + + if (brightness) { + /* + * The pattern should define only one non-zero + * brightness in the HIGH state. The device doesn't + * have any provisions to handle multiple peak + * brightness levels. + */ + if (brightness_peak && brightness_peak != brightness) + return -EINVAL; + + brightness_peak = brightness; + time_hi += delta_t; + ramp_dn_en = !!delta_t; + } else { + time_lo += delta_t; + ramp_up_en = !!delta_t; + } + } + + /* LUTs are specific to device variant. */ + lut_ramp_up = s2mu005_rgb_lut_ramp; + lut_ramp_up_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp); + lut_ramp_dn = s2mu005_rgb_lut_ramp; + lut_ramp_dn_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp); + lut_stay_hi = s2mu005_rgb_lut_stay_hi; + lut_stay_hi_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_hi); + lut_stay_lo = s2mu005_rgb_lut_stay_lo; + lut_stay_lo_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_lo); + + mutex_lock(&rgb->lock); + + /* + * The timings ramp_up, stay_hi, and ramp_dn of the HIGH state are + * roughly equal. Firstly, calculate and set timings for ramp_up and + * ramp_dn (making sure they're exactly equal). + */ + rgb->ramp_up = 0; + rgb->ramp_dn = 0; + + if (ramp_up_en) { + ret = s2m_rgb_lut_get_closest_duration(lut_ramp_up, lut_ramp_up_len, time_hi / 3); + if (ret < 0) + goto param_fail; + rgb->ramp_up = (u8)ret; + } + + if (ramp_dn_en) { + ret = s2m_rgb_lut_get_closest_duration(lut_ramp_dn, lut_ramp_dn_len, time_hi / 3); + if (ret < 0) + goto param_fail; + rgb->ramp_dn = (u8)ret; + } + + /* + * Subtract the allocated ramp timings from time_hi (and also making + * sure it doesn't underflow!). The remaining time is allocated to + * stay_hi. + */ + time_hi -= min(time_hi, lut_ramp_up[rgb->ramp_up]); + time_hi -= min(time_hi, lut_ramp_dn[rgb->ramp_dn]); + + ret = s2m_rgb_lut_get_closest_duration(lut_stay_hi, lut_stay_hi_len, time_hi); + if (ret < 0) + goto param_fail; + rgb->stay_hi = (u8)ret; + + ret = s2m_rgb_lut_get_closest_duration(lut_stay_lo, lut_stay_lo_len, time_lo); + if (ret < 0) + goto param_fail; + rgb->stay_lo = (u8)ret; + + led_mc_calc_color_components(&rgb->mc, brightness_peak); + /* Apply params with variant-specific implementation. */ + ret = s2mu005_rgb_apply_params(rgb); + if (ret) + goto param_fail; + + mutex_unlock(&rgb->lock); + + return 0; + +param_fail: + rgb->ramp_up = 0; + rgb->ramp_dn = 0; + rgb->stay_hi = 0; + rgb->stay_lo = 0; + + mutex_unlock(&rgb->lock); + + return ret; +} + +static int s2m_rgb_pattern_clear(struct led_classdev *cdev) +{ + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev)); + int ret = 0; + + mutex_lock(&rgb->lock); + + /* Reset params with variant-specific implementation. */ + ret = s2mu005_rgb_reset_params(rgb); + + mutex_unlock(&rgb->lock); + + return ret; +} + +static int s2m_rgb_brightness_set(struct led_classdev *cdev, enum led_brightness value) +{ + struct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev)); + int ret = 0; + + if (!value) + return s2m_rgb_pattern_clear(cdev); + + mutex_lock(&rgb->lock); + + led_mc_calc_color_components(&rgb->mc, value); + /* Apply params with variant-specific implementation. */ + ret = s2mu005_rgb_apply_params(rgb); + + mutex_unlock(&rgb->lock); + + return ret; +} + +static const struct mc_subled s2mu005_rgb_subled_info[] = { + { .channel = 0, .color_index = LED_COLOR_ID_BLUE }, + { .channel = 1, .color_index = LED_COLOR_ID_GREEN }, + { .channel = 2, .color_index = LED_COLOR_ID_RED }, +}; + +static int s2m_rgb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent); + struct s2m_rgb *rgb; + struct led_init_data init_data = {}; + int ret; + + rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL); + if (!rgb) + return -ENOMEM; + + platform_set_drvdata(pdev, rgb); + rgb->dev = dev; + rgb->regmap = pmic_drvdata->regmap_pmic; + + /* Configure variant-specific details. */ + rgb->mc.num_colors = ARRAY_SIZE(s2mu005_rgb_subled_info); + rgb->mc.subled_info = devm_kmemdup(dev, s2mu005_rgb_subled_info, + sizeof(s2mu005_rgb_subled_info), GFP_KERNEL); + if (!rgb->mc.subled_info) + return -ENOMEM; + + rgb->mc.led_cdev.max_brightness = 255; + rgb->mc.led_cdev.brightness_set_blocking = s2m_rgb_brightness_set; + rgb->mc.led_cdev.pattern_set = s2m_rgb_pattern_set; + rgb->mc.led_cdev.pattern_clear = s2m_rgb_pattern_clear; + + ret = devm_mutex_init(dev, &rgb->lock); + if (ret) + return dev_err_probe(dev, ret, "failed to create mutex lock\n"); + + init_data.fwnode = of_fwnode_handle(dev->of_node); + ret = devm_led_classdev_multicolor_register_ext(dev, &rgb->mc, &init_data); + if (ret) + return dev_err_probe(dev, ret, "failed to create LED device\n"); + + return 0; +} + +static const struct platform_device_id s2m_rgb_id_table[] = { + { "s2mu005-rgb", S2MU005 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, s2m_rgb_id_table); + +static const struct of_device_id s2m_rgb_of_match_table[] = { + { .compatible = "samsung,s2mu005-rgb", .data = (void *)S2MU005 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s2m_rgb_of_match_table); + +static struct platform_driver s2m_rgb_driver = { + .driver = { + .name = "s2m-rgb", + }, + .probe = s2m_rgb_probe, + .id_table = s2m_rgb_id_table, +}; +module_platform_driver(s2m_rgb_driver); + +MODULE_DESCRIPTION("RGB LED Driver for Samsung S2M Series PMICs"); +MODULE_AUTHOR("Kaustabh Chakraborty "); +MODULE_LICENSE("GPL"); From 373777fb56ab4db8a2a45cb14985550e888043eb Mon Sep 17 00:00:00 2001 From: Kaustabh Chakraborty Date: Sat, 16 May 2026 03:08:40 +0530 Subject: [PATCH 3875/5214] Documentation: leds: Document pattern behavior of Samsung S2M series PMIC RGB LEDs Add documentation to describe how hardware patterns (as defined by the documentation of leds-trigger-pattern) are parsed and implemented by the Samsung S2M series PMIC RGB LED driver. Signed-off-by: Kaustabh Chakraborty Link: https://patch.msgid.link/20260516-s2mu005-pmic-v7-8-73f9702fb461@disroot.org Signed-off-by: Lee Jones --- Documentation/leds/index.rst | 1 + Documentation/leds/leds-s2m-rgb.rst | 60 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Documentation/leds/leds-s2m-rgb.rst diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst index bebf44004278..23fa9ff7aaf4 100644 --- a/Documentation/leds/index.rst +++ b/Documentation/leds/index.rst @@ -28,6 +28,7 @@ LEDs leds-lp5812 leds-mlxcpld leds-mt6370-rgb + leds-s2m-rgb leds-sc27xx leds-st1202 leds-qcom-lpg diff --git a/Documentation/leds/leds-s2m-rgb.rst b/Documentation/leds/leds-s2m-rgb.rst new file mode 100644 index 000000000000..4f89a8c89ea8 --- /dev/null +++ b/Documentation/leds/leds-s2m-rgb.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +Samsung S2M Series PMIC RGB LED Driver +====================================== + +Description +----------- + +The RGB LED on the S2M series PMIC hardware features a three-channel LED that +is grouped together as a single device. Furthermore, it supports 8-bit +brightness control for each channel. This LED is typically used as a status +indicator in mobile devices. It also supports various parameters for hardware +patterns. + +The hardware pattern can be programmed using the "pattern" trigger, using the +hw_pattern attribute. + +/sys/class/leds//repeat +---------------------------- + +The hardware supports only indefinitely repeating patterns. The repeat +attribute must be set to -1 for hardware patterns to function. + +/sys/class/leds//hw_pattern +-------------------------------- + +Specify a hardware pattern for the RGB LEDs. + +The pattern is a series of brightness levels and durations in milliseconds. +There should be only one non-zero brightness level. Unlike the results +described in leds-trigger-pattern, the transitions between on and off states +are smoothed out by the hardware. + +Simple pattern:: + + "255 3000 0 1000" + + 255 -+ ''''''-. .-'''''''-. + | '. .' '. + | \ / \ + | '. .' '. + | '-.......-' '- + 0 -+-------+-------+-------+-------+-------+-------+--> time (s) + 0 1 2 3 4 5 6 + +As described in leds-trigger-pattern, it is also possible to use zero-length +entries to disable the ramping mechanism. + +On-Off pattern:: + + "255 1000 255 0 0 1000 0 0" + + 255 -+ ------+ +-------+ +-------+ + | | | | | | + | | | | | | + | | | | | | + | +-------+ +-------+ +------- + 0 -+-------+-------+-------+-------+-------+-------+--> time (s) + 0 1 2 3 4 5 6 From 8f040b5c5e3a4fa6e14ec44219d4717f66923e16 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Wed, 13 May 2026 13:58:53 +0200 Subject: [PATCH 3876/5214] leds: class: Use firmware nodes for device lookup Replace the OF based lookup with the fwnode equivalent to get support for ACPI and software nodes. Signed-off-by: Alban Bedel Link: https://patch.msgid.link/20260513115853.1584230-1-alban.bedel@lht.dlh.de Signed-off-by: Lee Jones --- drivers/leds/led-class.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 9e14ae588f78..a17db3d6644f 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -249,32 +249,34 @@ static const struct class leds_class = { }; /** - * of_led_get() - request a LED device via the LED framework - * @np: device node to get the LED device from + * fwnode_led_get() - request a LED device via the LED framework + * @fwnode: firmware node to get the LED device from * @index: the index of the LED * @name: the name of the LED used to map it to its function, if present * * Returns the LED device parsed from the phandle specified in the "leds" * property of a device tree node or a negative error-code on failure. */ -static struct led_classdev *of_led_get(struct device_node *np, int index, - const char *name) +static struct led_classdev *fwnode_led_get(struct fwnode_handle *fwnode, + int index, const char *name) { + struct fwnode_handle *led_node; struct device *led_dev; - struct device_node *led_node; /* * For named LEDs, first look up the name in the "led-names" property. - * If it cannot be found, then of_parse_phandle() will propagate the error. + * If it cannot be found, then fwnode_find_reference() will propagate + * the error. */ if (name) - index = of_property_match_string(np, "led-names", name); - led_node = of_parse_phandle(np, "leds", index); - if (!led_node) - return ERR_PTR(-ENOENT); + index = fwnode_property_match_string(fwnode, "led-names", + name); + led_node = fwnode_find_reference(fwnode, "leds", index); + if (IS_ERR(led_node)) + return ERR_CAST(led_node); - led_dev = class_find_device_by_fwnode(&leds_class, of_fwnode_handle(led_node)); - of_node_put(led_node); + led_dev = class_find_device_by_fwnode(&leds_class, led_node); + fwnode_handle_put(led_node); return led_module_get(led_dev); } @@ -332,7 +334,7 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev, if (!dev) return ERR_PTR(-EINVAL); - led = of_led_get(dev->of_node, index, NULL); + led = fwnode_led_get(dev_fwnode(dev), index, NULL); if (IS_ERR(led)) return led; @@ -354,7 +356,7 @@ struct led_classdev *led_get(struct device *dev, char *con_id) const char *provider = NULL; struct device *led_dev; - led_cdev = of_led_get(dev->of_node, -1, con_id); + led_cdev = fwnode_led_get(dev_fwnode(dev), -1, con_id); if (!IS_ERR(led_cdev) || PTR_ERR(led_cdev) != -ENOENT) return led_cdev; From b819dc7d8fb2896b07e51eba0381d61daf35edd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 21 May 2026 18:42:41 +0200 Subject: [PATCH 3877/5214] leds: core: Report ENODATA for brightness of hardware controlled LED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the LED is controlled fully by the hardware, the value cached by the LED driver core is incorrect. Return ENODATA to userspace in this case. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260521-cros_ec-leds-hw-trigger-brightness-v1-1-6cd9d7c9671e@weissschuh.net Signed-off-by: Lee Jones --- drivers/leds/led-class.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index a17db3d6644f..a51b0ed53886 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -27,12 +27,25 @@ static LIST_HEAD(leds_lookup_list); static struct workqueue_struct *leds_wq; +static bool led_trigger_is_hw_controlled(struct led_classdev *led_cdev) +{ +#ifdef CONFIG_LEDS_TRIGGERS + guard(rwsem_read)(&led_cdev->trigger_lock); + return led_cdev->trigger && led_cdev->trigger->trigger_type; +#else + return false; +#endif +} + static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); unsigned int brightness; + if (led_trigger_is_hw_controlled(led_cdev)) + return -ENODATA; + mutex_lock(&led_cdev->led_access); led_update_brightness(led_cdev); brightness = led_cdev->brightness; From 61ed78f55a46e12afd4b464c4ba736f55ff33c5e Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Thu, 21 May 2026 20:12:05 +0200 Subject: [PATCH 3878/5214] leds: uleds: Return -EFAULT on copy_to_user() failure uleds_read() copies the current brightness value to userspace but ignores copy_to_user() failures. It then clears the pending update and reports a successful full read even when no data was copied. Return -EFAULT when the copy fails and leave the update pending so a later read can retry. Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260521181205.15130-1-alhouseenyousef@gmail.com Signed-off-by: Lee Jones --- drivers/leds/uleds.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c index ace71ffc0591..470015e3f802 100644 --- a/drivers/leds/uleds.c +++ b/drivers/leds/uleds.c @@ -147,10 +147,13 @@ static ssize_t uleds_read(struct file *file, char __user *buffer, size_t count, } else if (!udev->new_data && (file->f_flags & O_NONBLOCK)) { retval = -EAGAIN; } else if (udev->new_data) { - retval = copy_to_user(buffer, &udev->brightness, - sizeof(udev->brightness)); - udev->new_data = false; - retval = sizeof(udev->brightness); + if (copy_to_user(buffer, &udev->brightness, + sizeof(udev->brightness))) { + retval = -EFAULT; + } else { + udev->new_data = false; + retval = sizeof(udev->brightness); + } } mutex_unlock(&udev->mutex); From a031b5fce5265938912d66047ec12b2208dd868f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 22 May 2026 12:42:22 +0200 Subject: [PATCH 3879/5214] leds: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Alexander Sverdlin Link: https://patch.msgid.link/20260522104222.4081017-2-u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- drivers/leds/flash/leds-as3645a.c | 2 +- drivers/leds/flash/leds-lm3601x.c | 4 ++-- drivers/leds/leds-an30259a.c | 2 +- drivers/leds/leds-as3668.c | 2 +- drivers/leds/leds-aw200xx.c | 10 +++++----- drivers/leds/leds-bd2802.c | 2 +- drivers/leds/leds-blinkm.c | 4 ++-- drivers/leds/leds-is31fl319x.c | 22 +++++++++++----------- drivers/leds/leds-is31fl32xx.c | 18 +++++++++--------- drivers/leds/leds-lm3530.c | 4 ++-- drivers/leds/leds-lm3532.c | 4 ++-- drivers/leds/leds-lm355x.c | 6 +++--- drivers/leds/leds-lm3642.c | 4 ++-- drivers/leds/leds-lm3692x.c | 4 ++-- drivers/leds/leds-lm3697.c | 2 +- drivers/leds/leds-lp3944.c | 4 ++-- drivers/leds/leds-lp3952.c | 4 ++-- drivers/leds/leds-lp50xx.c | 12 ++++++------ drivers/leds/leds-lp5521.c | 2 +- drivers/leds/leds-lp5523.c | 4 ++-- drivers/leds/leds-lp5562.c | 2 +- drivers/leds/leds-lp5569.c | 2 +- drivers/leds/leds-lp8501.c | 2 +- drivers/leds/leds-lp8860.c | 2 +- drivers/leds/leds-lp8864.c | 4 ++-- drivers/leds/leds-pca9532.c | 8 ++++---- drivers/leds/leds-pca955x.c | 12 ++++++------ drivers/leds/leds-pca963x.c | 8 ++++---- drivers/leds/leds-pca995x.c | 8 ++++---- drivers/leds/leds-st1202.c | 2 +- drivers/leds/leds-tca6507.c | 2 +- drivers/leds/leds-tlc591xx.c | 6 +++--- drivers/leds/leds-turris-omnia.c | 2 +- drivers/leds/rgb/leds-ktd202x.c | 6 +++--- 34 files changed, 91 insertions(+), 91 deletions(-) diff --git a/drivers/leds/flash/leds-as3645a.c b/drivers/leds/flash/leds-as3645a.c index 2f2d783c62c3..5fefcaef3714 100644 --- a/drivers/leds/flash/leds-as3645a.c +++ b/drivers/leds/flash/leds-as3645a.c @@ -741,7 +741,7 @@ static void as3645a_remove(struct i2c_client *client) } static const struct i2c_device_id as3645a_id_table[] = { - { AS_NAME }, + { .name = AS_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, as3645a_id_table); diff --git a/drivers/leds/flash/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c index abf6b96ade3d..8d00510c8967 100644 --- a/drivers/leds/flash/leds-lm3601x.c +++ b/drivers/leds/flash/leds-lm3601x.c @@ -465,8 +465,8 @@ static void lm3601x_remove(struct i2c_client *client) } static const struct i2c_device_id lm3601x_id[] = { - { "LM36010", CHIP_LM36010 }, - { "LM36011", CHIP_LM36011 }, + { .name = "LM36010", .driver_data = CHIP_LM36010 }, + { .name = "LM36011", .driver_data = CHIP_LM36011 }, { } }; MODULE_DEVICE_TABLE(i2c, lm3601x_id); diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index a42cc4bc6917..4654a732d1b1 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -331,7 +331,7 @@ static const struct of_device_id an30259a_match_table[] = { MODULE_DEVICE_TABLE(of, an30259a_match_table); static const struct i2c_device_id an30259a_id[] = { - { "an30259a" }, + { .name = "an30259a" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, an30259a_id); diff --git a/drivers/leds/leds-as3668.c b/drivers/leds/leds-as3668.c index b2794492370e..46427fe752a8 100644 --- a/drivers/leds/leds-as3668.c +++ b/drivers/leds/leds-as3668.c @@ -175,7 +175,7 @@ static void as3668_remove(struct i2c_client *client) } static const struct i2c_device_id as3668_idtable[] = { - { "as3668" }, + { .name = "as3668" }, { } }; MODULE_DEVICE_TABLE(i2c, as3668_idtable); diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index fe223d363a5d..0d90eeb6448f 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -637,11 +637,11 @@ static const struct aw200xx_chipdef aw20108_cdef = { }; static const struct i2c_device_id aw200xx_id[] = { - { "aw20036" }, - { "aw20054" }, - { "aw20072" }, - { "aw20108" }, - {} + { .name = "aw20036" }, + { .name = "aw20054" }, + { .name = "aw20072" }, + { .name = "aw20108" }, + { } }; MODULE_DEVICE_TABLE(i2c, aw200xx_id); diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 2a08c5f27608..9732f41a1143 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -776,7 +776,7 @@ static int bd2802_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume); static const struct i2c_device_id bd2802_id[] = { - { "BD2802" }, + { .name = "BD2802" }, { } }; MODULE_DEVICE_TABLE(i2c, bd2802_id); diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 1978bbda47db..ee1589015826 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -806,8 +806,8 @@ static void blinkm_remove(struct i2c_client *client) } static const struct i2c_device_id blinkm_id[] = { - { "blinkm" }, - {} + { .name = "blinkm" }, + { } }; MODULE_DEVICE_TABLE(i2c, blinkm_id); diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c index e411cee06dab..80f38dba0fba 100644 --- a/drivers/leds/leds-is31fl319x.c +++ b/drivers/leds/leds-is31fl319x.c @@ -565,17 +565,17 @@ static int is31fl319x_probe(struct i2c_client *client) * even though it is not used for DeviceTree based instantiation. */ static const struct i2c_device_id is31fl319x_id[] = { - { "is31fl3190" }, - { "is31fl3191" }, - { "is31fl3193" }, - { "is31fl3196" }, - { "is31fl3199" }, - { "sn3190" }, - { "sn3191" }, - { "sn3193" }, - { "sn3196" }, - { "sn3199" }, - {}, + { .name = "is31fl3190" }, + { .name = "is31fl3191" }, + { .name = "is31fl3193" }, + { .name = "is31fl3196" }, + { .name = "is31fl3199" }, + { .name = "sn3190" }, + { .name = "sn3191" }, + { .name = "sn3193" }, + { .name = "sn3196" }, + { .name = "sn3199" }, + { } }; MODULE_DEVICE_TABLE(i2c, is31fl319x_id); diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index fe07acbb103a..6c8d6b833260 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -616,15 +616,15 @@ static void is31fl32xx_remove(struct i2c_client *client) * even though it is not used for DeviceTree based instantiation. */ static const struct i2c_device_id is31fl32xx_id[] = { - { "is31fl3293" }, - { "is31fl3236" }, - { "is31fl3236a" }, - { "is31fl3235" }, - { "is31fl3218" }, - { "sn3218" }, - { "is31fl3216" }, - { "sn3216" }, - {}, + { .name = "is31fl3293" }, + { .name = "is31fl3236" }, + { .name = "is31fl3236a" }, + { .name = "is31fl3235" }, + { .name = "is31fl3218" }, + { .name = "sn3218" }, + { .name = "is31fl3216" }, + { .name = "sn3216" }, + { } }; MODULE_DEVICE_TABLE(i2c, is31fl32xx_id); diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index e44a3db106c3..481e9c0a41e7 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -478,8 +478,8 @@ static void lm3530_remove(struct i2c_client *client) } static const struct i2c_device_id lm3530_id[] = { - { LM3530_NAME }, - {} + { .name = LM3530_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3530_id); diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 24dc8ad27bb3..b51496910b08 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -722,8 +722,8 @@ static const struct of_device_id of_lm3532_leds_match[] = { MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); static const struct i2c_device_id lm3532_id[] = { - { LM3532_NAME }, - {} + { .name = LM3532_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3532_id); diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index f68771b9eac6..2b7cf42141e4 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -504,9 +504,9 @@ static void lm355x_remove(struct i2c_client *client) } static const struct i2c_device_id lm355x_id[] = { - {LM3554_NAME, CHIP_LM3554}, - {LM3556_NAME, CHIP_LM3556}, - {} + { .name = LM3554_NAME, .driver_data = CHIP_LM3554 }, + { .name = LM3556_NAME, .driver_data = CHIP_LM3556 }, + { } }; MODULE_DEVICE_TABLE(i2c, lm355x_id); diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 36246267b096..2a893399e05f 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -388,8 +388,8 @@ static void lm3642_remove(struct i2c_client *client) } static const struct i2c_device_id lm3642_id[] = { - { LM3642_NAME }, - {} + { .name = LM3642_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3642_id); diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 1d64ceb5ac85..95b850a3b31c 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -503,8 +503,8 @@ static void lm3692x_remove(struct i2c_client *client) } static const struct i2c_device_id lm3692x_id[] = { - { "lm36922", LM36922_MODEL }, - { "lm36923", LM36923_MODEL }, + { .name = "lm36922", .driver_data = LM36922_MODEL }, + { .name = "lm36923", .driver_data = LM36923_MODEL }, { } }; MODULE_DEVICE_TABLE(i2c, lm3692x_id); diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 7ad232780a31..933191fb2be0 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -354,7 +354,7 @@ static void lm3697_remove(struct i2c_client *client) } static const struct i2c_device_id lm3697_id[] = { - { "lm3697" }, + { .name = "lm3697" }, { } }; MODULE_DEVICE_TABLE(i2c, lm3697_id); diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index ccfeee49ea78..8e95dcedce40 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -417,8 +417,8 @@ static void lp3944_remove(struct i2c_client *client) /* lp3944 i2c driver struct */ static const struct i2c_device_id lp3944_id[] = { - { "lp3944" }, - {} + { .name = "lp3944" }, + { } }; MODULE_DEVICE_TABLE(i2c, lp3944_id); diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 17219a582704..0a1af284f52b 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -266,8 +266,8 @@ static int lp3952_probe(struct i2c_client *client) } static const struct i2c_device_id lp3952_id[] = { - { LP3952_NAME }, - {} + { .name = LP3952_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lp3952_id); diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c index 69c3550f1a31..259169214aaf 100644 --- a/drivers/leds/leds-lp50xx.c +++ b/drivers/leds/leds-lp50xx.c @@ -601,12 +601,12 @@ static void lp50xx_remove(struct i2c_client *client) } static const struct i2c_device_id lp50xx_id[] = { - { "lp5009", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5009] }, - { "lp5012", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5012] }, - { "lp5018", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5018] }, - { "lp5024", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5024] }, - { "lp5030", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5030] }, - { "lp5036", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5036] }, + { .name = "lp5009", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5009] }, + { .name = "lp5012", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5012] }, + { .name = "lp5018", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5018] }, + { .name = "lp5024", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5024] }, + { .name = "lp5030", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5030] }, + { .name = "lp5036", .driver_data = (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5036] }, { } }; MODULE_DEVICE_TABLE(i2c, lp50xx_id); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 7564b9953408..4937fc968011 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -253,7 +253,7 @@ static struct lp55xx_device_config lp5521_cfg = { }; static const struct i2c_device_id lp5521_id[] = { - { "lp5521", .driver_data = (kernel_ulong_t)&lp5521_cfg, }, /* Three channel chip */ + { .name = "lp5521", .driver_data = (kernel_ulong_t)&lp5521_cfg }, /* Three channel chip */ { } }; MODULE_DEVICE_TABLE(i2c, lp5521_id); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4ed3e735260c..39cb26d343c1 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -346,8 +346,8 @@ static struct lp55xx_device_config lp5523_cfg = { }; static const struct i2c_device_id lp5523_id[] = { - { "lp5523", .driver_data = (kernel_ulong_t)&lp5523_cfg, }, - { "lp55231", .driver_data = (kernel_ulong_t)&lp5523_cfg, }, + { .name = "lp5523", .driver_data = (kernel_ulong_t)&lp5523_cfg }, + { .name = "lp55231", .driver_data = (kernel_ulong_t)&lp5523_cfg }, { } }; diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 14a4af361b26..9e6056821eb5 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -395,7 +395,7 @@ static struct lp55xx_device_config lp5562_cfg = { }; static const struct i2c_device_id lp5562_id[] = { - { "lp5562", .driver_data = (kernel_ulong_t)&lp5562_cfg, }, + { .name = "lp5562", .driver_data = (kernel_ulong_t)&lp5562_cfg }, { } }; MODULE_DEVICE_TABLE(i2c, lp5562_id); diff --git a/drivers/leds/leds-lp5569.c b/drivers/leds/leds-lp5569.c index a252ba6c455d..199db3efca65 100644 --- a/drivers/leds/leds-lp5569.c +++ b/drivers/leds/leds-lp5569.c @@ -514,7 +514,7 @@ static struct lp55xx_device_config lp5569_cfg = { }; static const struct i2c_device_id lp5569_id[] = { - { "lp5569", .driver_data = (kernel_ulong_t)&lp5569_cfg, }, + { .name = "lp5569", .driver_data = (kernel_ulong_t)&lp5569_cfg }, { } }; diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index ee4ff4586bc0..8009689db7b4 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -130,7 +130,7 @@ static struct lp55xx_device_config lp8501_cfg = { }; static const struct i2c_device_id lp8501_id[] = { - { "lp8501", .driver_data = (kernel_ulong_t)&lp8501_cfg, }, + { .name = "lp8501", .driver_data = (kernel_ulong_t)&lp8501_cfg }, { } }; MODULE_DEVICE_TABLE(i2c, lp8501_id); diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c index 7a436861c4b7..69f064781f69 100644 --- a/drivers/leds/leds-lp8860.c +++ b/drivers/leds/leds-lp8860.c @@ -333,7 +333,7 @@ static int lp8860_probe(struct i2c_client *client) } static const struct i2c_device_id lp8860_id[] = { - { "lp8860" }, + { .name = "lp8860" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8860_id); diff --git a/drivers/leds/leds-lp8864.c b/drivers/leds/leds-lp8864.c index 3afd729d2f8a..204727f2f350 100644 --- a/drivers/leds/leds-lp8864.c +++ b/drivers/leds/leds-lp8864.c @@ -270,8 +270,8 @@ static int lp8864_probe(struct i2c_client *client) } static const struct i2c_device_id lp8864_id[] = { - { "lp8864" }, - {} + { .name = "lp8864" }, + { } }; MODULE_DEVICE_TABLE(i2c, lp8864_id); diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index dae7c6760508..f3bf59495b68 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -67,10 +67,10 @@ enum { }; static const struct i2c_device_id pca9532_id[] = { - { "pca9530", pca9530 }, - { "pca9531", pca9531 }, - { "pca9532", pca9532 }, - { "pca9533", pca9533 }, + { .name = "pca9530", .driver_data = pca9530 }, + { .name = "pca9531", .driver_data = pca9531 }, + { .name = "pca9532", .driver_data = pca9532 }, + { .name = "pca9533", .driver_data = pca9533 }, { } }; diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 2007fe6217ec..273383351ba0 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -764,12 +764,12 @@ static int pca955x_probe(struct i2c_client *client) } static const struct i2c_device_id pca955x_id[] = { - { "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, - { "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, - { "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, - { "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, - { "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, - {} + { .name = "pca9550", .driver_data = (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, + { .name = "pca9551", .driver_data = (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, + { .name = "pca9552", .driver_data = (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, + { .name = "ibm-pca9552", .driver_data = (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, + { .name = "pca9553", .driver_data = (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, + { } }; MODULE_DEVICE_TABLE(i2c, pca955x_id); diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 050e93b04884..e3a81c60ee27 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -88,10 +88,10 @@ static struct pca963x_chipdef pca963x_chipdefs[] = { #define PCA963X_BLINK_PERIOD_MAX 10667 static const struct i2c_device_id pca963x_id[] = { - { "pca9632", pca9633 }, - { "pca9633", pca9633 }, - { "pca9634", pca9634 }, - { "pca9635", pca9635 }, + { .name = "pca9632", .driver_data = pca9633 }, + { .name = "pca9633", .driver_data = pca9633 }, + { .name = "pca9634", .driver_data = pca9634 }, + { .name = "pca9635", .driver_data = pca9635 }, { } }; MODULE_DEVICE_TABLE(i2c, pca963x_id); diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c index 6ad06ce2bf64..59951207fd04 100644 --- a/drivers/leds/leds-pca995x.c +++ b/drivers/leds/leds-pca995x.c @@ -188,10 +188,10 @@ static int pca995x_probe(struct i2c_client *client) } static const struct i2c_device_id pca995x_id[] = { - { "pca9952", .driver_data = (kernel_ulong_t)&pca9952_chipdef }, - { "pca9955b", .driver_data = (kernel_ulong_t)&pca9955b_chipdef }, - { "pca9956b", .driver_data = (kernel_ulong_t)&pca9956b_chipdef }, - {} + { .name = "pca9952", .driver_data = (kernel_ulong_t)&pca9952_chipdef }, + { .name = "pca9955b", .driver_data = (kernel_ulong_t)&pca9955b_chipdef }, + { .name = "pca9956b", .driver_data = (kernel_ulong_t)&pca9956b_chipdef }, + { } }; MODULE_DEVICE_TABLE(i2c, pca995x_id); diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c index 2b68cd3c45f8..7f68d956f694 100644 --- a/drivers/leds/leds-st1202.c +++ b/drivers/leds/leds-st1202.c @@ -389,7 +389,7 @@ static int st1202_probe(struct i2c_client *client) } static const struct i2c_device_id st1202_id[] = { - { "st1202-i2c" }, + { .name = "st1202-i2c" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, st1202_id); diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index fd0e8bab9a4b..9afe2722986c 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -182,7 +182,7 @@ struct tca6507_chip { }; static const struct i2c_device_id tca6507_id[] = { - { "tca6507" }, + { .name = "tca6507" }, { } }; MODULE_DEVICE_TABLE(i2c, tca6507_id); diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 6605e08a042a..2f272cfd2e91 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -214,9 +214,9 @@ tlc591xx_probe(struct i2c_client *client) } static const struct i2c_device_id tlc591xx_id[] = { - { "tlc59116" }, - { "tlc59108" }, - {}, + { .name = "tlc59116" }, + { .name = "tlc59108" }, + { } }; MODULE_DEVICE_TABLE(i2c, tlc591xx_id); diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index 25ee5c1eb820..ed6a47bbb44f 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -532,7 +532,7 @@ static const struct of_device_id of_omnia_leds_match[] = { MODULE_DEVICE_TABLE(of, of_omnia_leds_match); static const struct i2c_device_id omnia_id[] = { - { "omnia" }, + { .name = "omnia" }, { } }; MODULE_DEVICE_TABLE(i2c, omnia_id); diff --git a/drivers/leds/rgb/leds-ktd202x.c b/drivers/leds/rgb/leds-ktd202x.c index e4f0f25a5e45..143020945e23 100644 --- a/drivers/leds/rgb/leds-ktd202x.c +++ b/drivers/leds/rgb/leds-ktd202x.c @@ -605,9 +605,9 @@ static void ktd202x_shutdown(struct i2c_client *client) } static const struct i2c_device_id ktd202x_id[] = { - {"ktd2026", KTD2026_NUM_LEDS}, - {"ktd2027", KTD2027_NUM_LEDS}, - {} + { .name = "ktd2026", .driver_data = KTD2026_NUM_LEDS }, + { .name = "ktd2027", .driver_data = KTD2027_NUM_LEDS }, + { } }; MODULE_DEVICE_TABLE(i2c, ktd202x_id); From c19fe864f667afc49d1391d764e20b66555bcf7a Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 25 May 2026 01:55:53 +0200 Subject: [PATCH 3880/5214] leds: uleds: Fix potential buffer overread The name string supplied by userspace is not guaranteed to be null-terminated, so using strchr() on it might result in a buffer overread. The same thing will happen when said string is used by the LED class device. Fix this by using strnchr() instead and explicitly check that the name string is properly null-terminated. Cc: stable@vger.kernel.org Fixes: e381322b0190 ("leds: Introduce userspace LED class driver") Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260524235553.189134-1-W_Armin@gmx.de Signed-off-by: Lee Jones --- drivers/leds/uleds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c index 470015e3f802..6affa581b61d 100644 --- a/drivers/leds/uleds.c +++ b/drivers/leds/uleds.c @@ -102,7 +102,8 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer, name = udev->user_dev.name; if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") || - strchr(name, '/')) { + strnchr(name, sizeof(udev->user_dev.name), '/') || + !strnchr(name, sizeof(udev->user_dev.name), '\0')) { ret = -EINVAL; goto out; } From 9ba15b2eb87ec33a9037343785e44594df4a59fc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:37:32 +0200 Subject: [PATCH 3881/5214] leds: Fix CONFIG_OF dependency for LEDS_LP5860_CORE Building LEDS_LP5860_SPI without CONFIG_OF leads to a build time warning: WARNING: unmet direct dependencies detected for LEDS_LP5860_CORE Depends on [n]: NEW_LEDS [=y] && LEDS_CLASS_MULTICOLOR [=y] && LEDS_CLASS [=y] && OF [=n] Selected by [y]: - LEDS_LP5860_SPI [=y] && NEW_LEDS [=y] && LEDS_CLASS_MULTICOLOR [=y] && SPI [=y] Address this by adding the same dependency here as well. Fixes: 3daf2c4ef82b ("leds: Add support for TI LP5860 LED driver chip") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260526103738.3389272-1-arnd@kernel.org Signed-off-by: Lee Jones --- drivers/leds/rgb/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 5599b71be54d..6e9ab5f60714 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -56,6 +56,7 @@ config LEDS_LP5860_CORE config LEDS_LP5860_SPI tristate "LED Support for TI LP5860 SPI LED driver chip" depends on SPI + depends on OF select LEDS_LP5860_CORE help If you say yes here you get support for the Texas Instruments From 938fda9671dd07196f4e591d30977b2e69d8f320 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 27 May 2026 02:14:22 +0200 Subject: [PATCH 3882/5214] leds: Fix sysfs ABI date The "multi_max_intensity" sysfs attribute was not included in kernel 7.1, so update the KernelVersion and Date tags accordingly. Fixes: b1a9b7a904af ("leds: Introduce the multi_max_intensity sysfs attribute") Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260527001422.51111-1-W_Armin@gmx.de Signed-off-by: Lee Jones --- Documentation/ABI/testing/sysfs-class-led-multicolor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-led-multicolor b/Documentation/ABI/testing/sysfs-class-led-multicolor index 197da3e775b4..62ce58f393d6 100644 --- a/Documentation/ABI/testing/sysfs-class-led-multicolor +++ b/Documentation/ABI/testing/sysfs-class-led-multicolor @@ -26,8 +26,8 @@ Description: read/write Documentation/leds/leds-class-multicolor.rst. What: /sys/class/leds//multi_max_intensity -Date: March 2026 -KernelVersion: 7.1 +Date: May 2026 +KernelVersion: 7.2 Contact: Armin Wolf Description: read This file contains an array of integers describing the maximum From f5f40b828179361862cac6c9d3c0e975e573dc3e Mon Sep 17 00:00:00 2001 From: Wenyuan Li <2063309626@qq.com> Date: Wed, 1 Apr 2026 13:09:31 +0800 Subject: [PATCH 3883/5214] mfd: tps65910: Add error handling for dummy I2C transfer in probe In tps65910_i2c_probe(), a dummy I2C transfer is performed to work around silicon erratum SWCZ010. However, the return value of i2c_master_send() is not checked. If this dummy transfer fails, the driver continues execution without detecting the error. This may lead to subsequent I2C operations also failing, but the driver would incorrectly report success. Add proper return value checking for the dummy I2C transfer. If the transfer fails, log the error and return an appropriate error code to the caller. Signed-off-by: Wenyuan Li <2063309626@qq.com> Link: https://patch.msgid.link/tencent_01102156392EC89EDF2CA22A7C8B4ABB2509@qq.com Signed-off-by: Lee Jones --- drivers/mfd/tps65910.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 6a7b7a697fb7..6243b66b1b6f 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -21,6 +21,9 @@ #include #include +/* Dummy I2C transfer length for SWCZ010 workaround */ +#define TPS65910_DUMMY_XFER_LEN 1 + static const struct resource rtc_resources[] = { { .start = TPS65910_IRQ_RTC_ALARM, @@ -472,7 +475,18 @@ static int tps65910_i2c_probe(struct i2c_client *i2c) * first I2C transfer. So issue a dummy transfer before the first * real transfer. */ - i2c_master_send(i2c, "", 1); + ret = i2c_master_send(i2c, "", TPS65910_DUMMY_XFER_LEN); + if (ret != TPS65910_DUMMY_XFER_LEN) { + int err; + + if (ret < 0) + err = ret; + else + err = -EIO; + + return dev_err_probe(&i2c->dev, err, "dummy transfer failed\n"); + } + tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); From d43f1d792902ba0a53fd311bff2cf96095c7606d Mon Sep 17 00:00:00 2001 From: Matthias Fend Date: Wed, 27 May 2026 12:26:51 +0200 Subject: [PATCH 3884/5214] leds: tps6131x: Increase overvoltage protection threshold to 6V Currently, there may be cases where the overvoltage detection is triggered even with a valid and generally functioning hardware setup. This occurs, for example, when the input voltage exceeds the currently used overvoltage threshold of 4.65V (typical). Since input voltages up to 5V are supported, the threshold should be adjusted accordingly. While the target output voltage setting has no effect on the LED operation used here, it indirectly selects the threshold for overvoltage detection. Set this to a value of 4.95V to select a threshold of 6V (typical). Signed-off-by: Matthias Fend Link: https://patch.msgid.link/20260527-leds-tps6131x-ovp-v1-1-1ac70d03c9eb@emfend.at Signed-off-by: Lee Jones --- drivers/leds/flash/leds-tps6131x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/flash/leds-tps6131x.c b/drivers/leds/flash/leds-tps6131x.c index f0f1f2b77d5a..5c9a5af5af05 100644 --- a/drivers/leds/flash/leds-tps6131x.c +++ b/drivers/leds/flash/leds-tps6131x.c @@ -277,7 +277,7 @@ static int tps6131x_init_chip(struct tps6131x *tps6131x) if (ret) return ret; - val = TPS6131X_REG_6_ENTS; + val = TPS6131X_REG_6_ENTS | (TPS6131X_OV_4950MV << TPS6131X_REG_6_OV_SHIFT); ret = regmap_write(tps6131x->regmap, TPS6131X_REG_6, val); if (ret) From 104cd764a031bfe2ffe253adce9581384a78c16e Mon Sep 17 00:00:00 2001 From: Akashdeep Kaur Date: Wed, 1 Apr 2026 16:52:57 +0530 Subject: [PATCH 3885/5214] mfd: tps65219: Make poweroff handler conditional on system-power-controller Currently, the TPS65219 driver unconditionally registers a poweroff handler. This causes issues on systems where a different component (such as TF-A firmware) should handle system poweroff instead. Make the poweroff handler registration conditional based on the "system-power-controller" device tree property. This follows the standard kernel pattern where only the designated power controller registers for system poweroff operations. On systems where the property is absent, the PMIC will not register a poweroff handler, allowing other poweroff mechanisms to function. Signed-off-by: Akashdeep Kaur Link: https://patch.msgid.link/20260401112257.1248437-3-a-kaur@ti.com Signed-off-by: Lee Jones --- drivers/mfd/tps65219.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c index 7275dcdb7c44..e52fbf1481fe 100644 --- a/drivers/mfd/tps65219.c +++ b/drivers/mfd/tps65219.c @@ -541,13 +541,15 @@ static int tps65219_probe(struct i2c_client *client) return ret; } - ret = devm_register_power_off_handler(tps->dev, - tps65219_power_off_handler, - tps); - if (ret) { - dev_err(tps->dev, "failed to register power-off handler: %d\n", ret); - return ret; + if (of_device_is_system_power_controller(tps->dev->of_node)) { + ret = devm_register_power_off_handler(tps->dev, + tps65219_power_off_handler, + tps); + if (ret) + return dev_err_probe(tps->dev, ret, + "Failed to register power-off handler\n"); } + return 0; } From 3c8658d6b55564167b11c6dd5ecffe368ef9c2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Pfl=C3=BCger?= Date: Sun, 29 Mar 2026 09:27:47 +0200 Subject: [PATCH 3886/5214] mfd: sprd-sc27xx: Switch to devm_mfd_add_devices() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow instantiating subdevices such as the regulator and poweroff devices that do not have corresponding device tree nodes with a "compatible" property, use devm_mfd_add_devices() with MFD cells instead of devm_of_platform_populate(). Since different PMICs in the SC27xx series contain different components, use separate MFD cell tables for each PMIC model. Define cells for all components that have upstream drivers at this point. Signed-off-by: Otto Pflüger Link: https://patch.msgid.link/20260329-sc27xx-mfd-cells-v3-3-9158dee41f74@abscue.de Signed-off-by: Lee Jones --- drivers/mfd/sprd-sc27xx-spi.c | 62 ++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c index d6b4350779e6..aa052f646623 100644 --- a/drivers/mfd/sprd-sc27xx-spi.c +++ b/drivers/mfd/sprd-sc27xx-spi.c @@ -14,6 +14,11 @@ #include #include +enum sprd_pmic_type { + PMIC_TYPE_SC2730 = 1, + PMIC_TYPE_SC2731, +}; + #define SPRD_PMIC_INT_MASK_STATUS 0x0 #define SPRD_PMIC_INT_RAW_STATUS 0x4 #define SPRD_PMIC_INT_EN 0x8 @@ -50,6 +55,29 @@ struct sprd_pmic_data { u32 charger_det; }; +static const struct mfd_cell sc2730_devices[] = { + MFD_CELL_OF("sc2730-adc", NULL, NULL, 0, 0, "sprd,sc2730-adc"), + MFD_CELL_OF("sc2730-bltc", NULL, NULL, 0, 0, "sprd,sc2730-bltc"), + MFD_CELL_OF("sc2730-efuse", NULL, NULL, 0, 0, "sprd,sc2730-efuse"), + MFD_CELL_OF("sc2730-eic", NULL, NULL, 0, 0, "sprd,sc2730-eic"), + MFD_CELL_OF("sc2730-fgu", NULL, NULL, 0, 0, "sprd,sc2730-fgu"), + MFD_CELL_OF("sc2730-rtc", NULL, NULL, 0, 0, "sprd,sc2730-rtc"), + MFD_CELL_OF("sc2730-vibrator", NULL, NULL, 0, 0, "sprd,sc2730-vibrator"), +}; + +static const struct mfd_cell sc2731_devices[] = { + MFD_CELL_OF("sc2731-adc", NULL, NULL, 0, 0, "sprd,sc2731-adc"), + MFD_CELL_OF("sc2731-bltc", NULL, NULL, 0, 0, "sprd,sc2731-bltc"), + MFD_CELL_OF("sc2731-charger", NULL, NULL, 0, 0, "sprd,sc2731-charger"), + MFD_CELL_OF("sc2731-efuse", NULL, NULL, 0, 0, "sprd,sc2731-efuse"), + MFD_CELL_OF("sc2731-eic", NULL, NULL, 0, 0, "sprd,sc2731-eic"), + MFD_CELL_OF("sc2731-fgu", NULL, NULL, 0, 0, "sprd,sc2731-fgu"), + MFD_CELL_NAME("sc2731-poweroff"), + MFD_CELL_NAME("sc2731-regulator"), + MFD_CELL_OF("sc2731-rtc", NULL, NULL, 0, 0, "sprd,sc2731-rtc"), + MFD_CELL_OF("sc2731-vibrator", NULL, NULL, 0, 0, "sprd,sc2731-vibrator"), +}; + /* * Since different PMICs of SC27xx series can have different interrupt * base address and irq number, we should save irq number and irq base @@ -152,12 +180,26 @@ static const struct regmap_config sprd_pmic_config = { static int sprd_pmic_probe(struct spi_device *spi) { struct sprd_pmic *ddata; + enum sprd_pmic_type pmic_type; const struct sprd_pmic_data *pdata; - int ret, i; + const struct mfd_cell *cells; + int ret, i, num_cells; - pdata = of_device_get_match_data(&spi->dev); - if (!pdata) { - dev_err(&spi->dev, "No matching driver data found\n"); + pmic_type = (uintptr_t)of_device_get_match_data(&spi->dev); + + switch (pmic_type) { + case PMIC_TYPE_SC2730: + pdata = &sc2730_data; + cells = sc2730_devices; + num_cells = ARRAY_SIZE(sc2730_devices); + break; + case PMIC_TYPE_SC2731: + pdata = &sc2731_data; + cells = sc2731_devices; + num_cells = ARRAY_SIZE(sc2731_devices); + break; + default: + dev_err(&spi->dev, "Invalid device ID\n"); return -EINVAL; } @@ -204,7 +246,9 @@ static int sprd_pmic_probe(struct spi_device *spi) return ret; } - ret = devm_of_platform_populate(&spi->dev); + ret = devm_mfd_add_devices(&spi->dev, PLATFORM_DEVID_AUTO, + cells, num_cells, NULL, 0, + regmap_irq_get_domain(ddata->irq_data)); if (ret) { dev_err(&spi->dev, "Failed to populate sub-devices %d\n", ret); return ret; @@ -241,15 +285,15 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sprd_pmic_pm_ops, sprd_pmic_suspend, sprd_pmic_resume); static const struct of_device_id sprd_pmic_match[] = { - { .compatible = "sprd,sc2730", .data = &sc2730_data }, - { .compatible = "sprd,sc2731", .data = &sc2731_data }, + { .compatible = "sprd,sc2730", .data = (void *)PMIC_TYPE_SC2730 }, + { .compatible = "sprd,sc2731", .data = (void *)PMIC_TYPE_SC2731 }, {}, }; MODULE_DEVICE_TABLE(of, sprd_pmic_match); static const struct spi_device_id sprd_pmic_spi_ids[] = { - { .name = "sc2730", .driver_data = (unsigned long)&sc2730_data }, - { .name = "sc2731", .driver_data = (unsigned long)&sc2731_data }, + { .name = "sc2730", .driver_data = PMIC_TYPE_SC2730 }, + { .name = "sc2731", .driver_data = PMIC_TYPE_SC2731 }, {}, }; MODULE_DEVICE_TABLE(spi, sprd_pmic_spi_ids); From 6a2cb13761d90ef3fa960db189a2cc1bdf965ae1 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 7 Apr 2026 16:26:48 +0300 Subject: [PATCH 3887/5214] mfd: bd72720: Drop BUCK11 ID The BD72720 header reserves an ID for BUCK11. While this does not (at the moment) cause problems I can see, it is misleading as the BD72720 contains only 10 BUCKs. Make the code clearer and drop the BUCK11 ID. Fixes: af25277b1ddc ("mfd: rohm-bd71828: Support ROHM BD72720") Signed-off-by: Matti Vaittinen Link: https://patch.msgid.link/812c3749a18d609d6f4698506bc516ec7183dfdd.1775565298.git.mazziesaccount@gmail.com Signed-off-by: Lee Jones --- include/linux/mfd/rohm-bd72720.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mfd/rohm-bd72720.h b/include/linux/mfd/rohm-bd72720.h index ae7343bcab06..d8ddbf232bb3 100644 --- a/include/linux/mfd/rohm-bd72720.h +++ b/include/linux/mfd/rohm-bd72720.h @@ -21,7 +21,6 @@ enum { BD72720_BUCK8, BD72720_BUCK9, BD72720_BUCK10, - BD72720_BUCK11, BD72720_LDO1, BD72720_LDO2, BD72720_LDO3, From 25c80f08e54ef5298eb68866f755f35b59a26d2b Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 3 Nov 2025 00:02:00 +0100 Subject: [PATCH 3888/5214] mfd: simple-mfd-i2c: Add a reboot cell for the SpacemiT P1 chip Add a "spacemit-p1-reboot" cell for the SpacemiT P1 chip. Signed-off-by: Aurelien Jarno Reviewed-by: Troy Mitchell Tested-by: Vincent Legoll # OrangePi-RV2 Link: https://patch.msgid.link/20251102230352.914421-3-aurelien@aurel32.net Signed-off-by: Lee Jones --- drivers/mfd/simple-mfd-i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c index 7315fad618e4..52c81b18750e 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -105,6 +105,7 @@ static const struct regmap_config spacemit_p1_regmap_config = { }; static const struct mfd_cell spacemit_p1_cells[] = { + { .name = "spacemit-p1-reboot", }, { .name = "spacemit-p1-regulator", }, { .name = "spacemit-p1-rtc", }, }; From 583003fe5e6e9ffc4a4edc6a1527890965d2c95c Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 31 Mar 2026 19:43:38 +0400 Subject: [PATCH 3889/5214] dt-bindings: mfd: ti,bq25703a: Expand to include BQ25792 TI BQ25792 is similar in operation to BQ25703A, but has a different register layout and different current/voltage capabilities. Expand the existing BQ25703A binding to include BQ25792, and move the voltage and current limits into per-variant conditional statements. Signed-off-by: Alexey Charkov Reviewed-by: Krzysztof Kozlowski Tested-by: Chris Morgan Link: https://patch.msgid.link/20260331-bq25792-v6-1-0278fba33eb9@flipper.net Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/ti,bq25703a.yaml | 73 ++++++++++++++++--- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml b/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml index ba14663c9266..cdce83f05804 100644 --- a/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml @@ -4,17 +4,16 @@ $id: http://devicetree.org/schemas/mfd/ti,bq25703a.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: BQ25703A Charger Manager/Buck/Boost Converter +title: BQ257xx Charger Manager/Buck/Boost Converter maintainers: - Chris Morgan -allOf: - - $ref: /schemas/power/supply/power-supply.yaml# - properties: compatible: - const: ti,bq25703a + enum: + - ti,bq25703a + - ti,bq25792 reg: const: 0x6b @@ -25,7 +24,6 @@ properties: powering the device. minimum: 50000 maximum: 6400000 - default: 3250000 interrupts: maxItems: 1 @@ -57,11 +55,11 @@ properties: minimum: 0 maximum: 6350000 regulator-min-microvolt: - minimum: 4480000 - maximum: 20800000 + minimum: 2800000 + maximum: 22000000 regulator-max-microvolt: - minimum: 4480000 - maximum: 20800000 + minimum: 2800000 + maximum: 22000000 enable-gpios: description: The BQ25703 may require both a register write and a GPIO @@ -74,6 +72,61 @@ properties: - regulator-min-microvolt - regulator-max-microvolt +allOf: + - $ref: /schemas/power/supply/power-supply.yaml# + - if: + properties: + compatible: + const: ti,bq25703a + then: + properties: + input-current-limit-microamp: + minimum: 50000 + maximum: 6400000 + default: 3250000 + regulators: + properties: + vbus: + properties: + regulator-min-microamp: + minimum: 0 + maximum: 6350000 + regulator-max-microamp: + minimum: 0 + maximum: 6350000 + regulator-min-microvolt: + minimum: 4480000 + maximum: 20800000 + regulator-max-microvolt: + minimum: 4480000 + maximum: 20800000 + - if: + properties: + compatible: + const: ti,bq25792 + then: + properties: + input-current-limit-microamp: + minimum: 100000 + maximum: 3300000 + default: 3000000 + regulators: + properties: + vbus: + properties: + regulator-min-microamp: + minimum: 0 + maximum: 3320000 + regulator-max-microamp: + minimum: 0 + maximum: 3320000 + regulator-min-microvolt: + minimum: 2800000 + maximum: 22000000 + regulator-max-microvolt: + minimum: 2800000 + maximum: 22000000 + unevaluatedProperties: false required: From 9adbe53e32cdf3785cf71e27962282c628e97f5c Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 31 Mar 2026 19:43:46 +0400 Subject: [PATCH 3890/5214] mfd: bq257xx: Add BQ25792 support Add register definitions and a new 'type' enum to be passed via MFD private data to support the BQ25792, which is a newer variant of the BQ257xx family. BQ25792 shares similar logic of operation with the already supported BQ25703A but has a completely different register map and different electrical constraints. Signed-off-by: Alexey Charkov Tested-by: Chris Morgan Link: https://patch.msgid.link/20260331-bq25792-v6-9-0278fba33eb9@flipper.net Signed-off-by: Lee Jones --- drivers/mfd/bq257xx.c | 54 ++++- include/linux/mfd/bq257xx.h | 412 ++++++++++++++++++++++++++++++++++++ 2 files changed, 463 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c index e9d49dac0a16..054342c60b73 100644 --- a/drivers/mfd/bq257xx.c +++ b/drivers/mfd/bq257xx.c @@ -39,6 +39,39 @@ static const struct regmap_config bq25703_regmap_config = { .val_format_endian = REGMAP_ENDIAN_LITTLE, }; +static const struct regmap_range bq25792_writeable_reg_ranges[] = { + regmap_reg_range(BQ25792_REG00_MIN_SYS_VOLTAGE, + BQ25792_REG18_NTC_CONTROL_1), + regmap_reg_range(BQ25792_REG28_CHARGER_MASK_0, + BQ25792_REG30_ADC_FUNCTION_DISABLE_1), +}; + +static const struct regmap_access_table bq25792_writeable_regs = { + .yes_ranges = bq25792_writeable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(bq25792_writeable_reg_ranges), +}; + +static const struct regmap_range bq25792_volatile_reg_ranges[] = { + regmap_reg_range(BQ25792_REG19_ICO_CURRENT_LIMIT, + BQ25792_REG27_FAULT_FLAG_1), + regmap_reg_range(BQ25792_REG31_IBUS_ADC, + BQ25792_REG47_DPDM_DRIVER), +}; + +static const struct regmap_access_table bq25792_volatile_regs = { + .yes_ranges = bq25792_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(bq25792_volatile_reg_ranges), +}; + +static const struct regmap_config bq25792_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BQ25792_REG48_PART_INFORMATION, + .cache_type = REGCACHE_MAPLE, + .wr_table = &bq25792_writeable_regs, + .volatile_table = &bq25792_volatile_regs, +}; + static const struct mfd_cell cells[] = { MFD_CELL_NAME("bq257xx-regulator"), MFD_CELL_NAME("bq257xx-charger"), @@ -46,6 +79,7 @@ static const struct mfd_cell cells[] = { static int bq257xx_probe(struct i2c_client *client) { + const struct regmap_config *rcfg; struct bq257xx_device *ddata; int ret; @@ -53,9 +87,21 @@ static int bq257xx_probe(struct i2c_client *client) if (!ddata) return -ENOMEM; + ddata->type = (uintptr_t)i2c_get_match_data(client); ddata->client = client; - ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config); + switch (ddata->type) { + case BQ25703A: + rcfg = &bq25703_regmap_config; + break; + case BQ25792: + rcfg = &bq25792_regmap_config; + break; + default: + return dev_err_probe(&client->dev, -ENODEV, "Unsupported device type\n"); + } + + ddata->regmap = devm_regmap_init_i2c(client, rcfg); if (IS_ERR(ddata->regmap)) { return dev_err_probe(&client->dev, PTR_ERR(ddata->regmap), "Failed to allocate register map\n"); @@ -73,13 +119,15 @@ static int bq257xx_probe(struct i2c_client *client) } static const struct i2c_device_id bq257xx_i2c_ids[] = { - { "bq25703a" }, + { "bq25703a", BQ25703A }, + { "bq25792", BQ25792 }, {} }; MODULE_DEVICE_TABLE(i2c, bq257xx_i2c_ids); static const struct of_device_id bq257xx_of_match[] = { - { .compatible = "ti,bq25703a" }, + { .compatible = "ti,bq25703a", .data = (void *)BQ25703A }, + { .compatible = "ti,bq25792", .data = (void *)BQ25792 }, {} }; MODULE_DEVICE_TABLE(of, bq257xx_of_match); diff --git a/include/linux/mfd/bq257xx.h b/include/linux/mfd/bq257xx.h index 1d6ddc7fb09f..4ec72eb920f2 100644 --- a/include/linux/mfd/bq257xx.h +++ b/include/linux/mfd/bq257xx.h @@ -98,7 +98,419 @@ #define BQ25703_EN_OTG_MASK BIT(12) +#define BQ25792_REG00_MIN_SYS_VOLTAGE 0x00 +#define BQ25792_REG01_CHARGE_VOLTAGE_LIMIT 0x01 +#define BQ25792_REG03_CHARGE_CURRENT_LIMIT 0x03 +#define BQ25792_REG05_INPUT_VOLTAGE_LIMIT 0x05 +#define BQ25792_REG06_INPUT_CURRENT_LIMIT 0x06 +#define BQ25792_REG08_PRECHARGE_CONTROL 0x08 +#define BQ25792_REG09_TERMINATION_CONTROL 0x09 +#define BQ25792_REG0A_RECHARGE_CONTROL 0x0a +#define BQ25792_REG0B_VOTG_REGULATION 0x0b +#define BQ25792_REG0D_IOTG_REGULATION 0x0d +#define BQ25792_REG0E_TIMER_CONTROL 0x0e +#define BQ25792_REG0F_CHARGER_CONTROL_0 0x0f +#define BQ25792_REG10_CHARGER_CONTROL_1 0x10 +#define BQ25792_REG11_CHARGER_CONTROL_2 0x11 +#define BQ25792_REG12_CHARGER_CONTROL_3 0x12 +#define BQ25792_REG13_CHARGER_CONTROL_4 0x13 +#define BQ25792_REG14_CHARGER_CONTROL_5 0x14 +/* REG15 reserved */ +#define BQ25792_REG16_TEMPERATURE_CONTROL 0x16 +#define BQ25792_REG17_NTC_CONTROL_0 0x17 +#define BQ25792_REG18_NTC_CONTROL_1 0x18 +#define BQ25792_REG19_ICO_CURRENT_LIMIT 0x19 +#define BQ25792_REG1B_CHARGER_STATUS_0 0x1b +#define BQ25792_REG1C_CHARGER_STATUS_1 0x1c +#define BQ25792_REG1D_CHARGER_STATUS_2 0x1d +#define BQ25792_REG1E_CHARGER_STATUS_3 0x1e +#define BQ25792_REG1F_CHARGER_STATUS_4 0x1f +#define BQ25792_REG20_FAULT_STATUS_0 0x20 +#define BQ25792_REG21_FAULT_STATUS_1 0x21 +#define BQ25792_REG22_CHARGER_FLAG_0 0x22 +#define BQ25792_REG23_CHARGER_FLAG_1 0x23 +#define BQ25792_REG24_CHARGER_FLAG_2 0x24 +#define BQ25792_REG25_CHARGER_FLAG_3 0x25 +#define BQ25792_REG26_FAULT_FLAG_0 0x26 +#define BQ25792_REG27_FAULT_FLAG_1 0x27 +#define BQ25792_REG28_CHARGER_MASK_0 0x28 +#define BQ25792_REG29_CHARGER_MASK_1 0x29 +#define BQ25792_REG2A_CHARGER_MASK_2 0x2a +#define BQ25792_REG2B_CHARGER_MASK_3 0x2b +#define BQ25792_REG2C_FAULT_MASK_0 0x2c +#define BQ25792_REG2D_FAULT_MASK_1 0x2d +#define BQ25792_REG2E_ADC_CONTROL 0x2e +#define BQ25792_REG2F_ADC_FUNCTION_DISABLE_0 0x2f +#define BQ25792_REG30_ADC_FUNCTION_DISABLE_1 0x30 +#define BQ25792_REG31_IBUS_ADC 0x31 +#define BQ25792_REG33_IBAT_ADC 0x33 +#define BQ25792_REG35_VBUS_ADC 0x35 +#define BQ25792_REG37_VAC1_ADC 0x37 +#define BQ25792_REG39_VAC2_ADC 0x39 +#define BQ25792_REG3B_VBAT_ADC 0x3b +#define BQ25792_REG3D_VSYS_ADC 0x3d +#define BQ25792_REG3F_TS_ADC 0x3f +#define BQ25792_REG41_TDIE_ADC 0x41 +#define BQ25792_REG43_DP_ADC 0x43 +#define BQ25792_REG45_DM_ADC 0x45 +#define BQ25792_REG47_DPDM_DRIVER 0x47 +#define BQ25792_REG48_PART_INFORMATION 0x48 + +/* Minimal System Voltage */ +#define BQ25792_REG00_VSYSMIN_MASK GENMASK(5, 0) + +#define BQ25792_MINVSYS_MIN_UV 2500000 +#define BQ25792_MINVSYS_STEP_UV 250000 +#define BQ25792_MINVSYS_MAX_UV 16000000 + +/* Charge Voltage Limit */ +#define BQ25792_REG01_VREG_MASK GENMASK(10, 0) + +#define BQ25792_VBATREG_MIN_UV 3000000 +#define BQ25792_VBATREG_STEP_UV 10000 +#define BQ25792_VBATREG_MAX_UV 18800000 + +/* Charge Current Limit */ +#define BQ25792_REG03_ICHG_MASK GENMASK(8, 0) + +#define BQ25792_ICHG_MIN_UA 50000 +#define BQ25792_ICHG_STEP_UA 10000 +#define BQ25792_ICHG_MAX_UA 5000000 + +/* Input Voltage Limit */ +#define BQ25792_REG05_VINDPM_MASK GENMASK(7, 0) + +/* Input Current Limit */ +#define BQ25792_REG06_IINDPM_MASK GENMASK(8, 0) +#define BQ25792_IINDPM_DEFAULT_UA 3000000 +#define BQ25792_IINDPM_STEP_UA 10000 +#define BQ25792_IINDPM_MIN_UA 100000 +#define BQ25792_IINDPM_MAX_UA 3300000 + +/* Precharge Control */ +#define BQ25792_REG08_VBAT_LOWV_MASK GENMASK(7, 6) +#define BQ25792_REG08_IPRECHG_MASK GENMASK(5, 0) + +/* Termination Control */ +#define BQ25792_REG09_REG_RST BIT(6) +#define BQ25792_REG09_ITERM_MASK GENMASK(4, 0) + +/* Re-charge Control */ +#define BQ25792_REG0A_CELL_MASK GENMASK(7, 6) +#define BQ25792_REG0A_TRECHG_MASK GENMASK(5, 4) +#define BQ25792_REG0A_VRECHG_MASK GENMASK(3, 0) + +/* VOTG regulation */ +#define BQ25792_REG0B_VOTG_MASK GENMASK(10, 0) + +#define BQ25792_OTG_VOLT_MIN_UV 2800000 +#define BQ25792_OTG_VOLT_STEP_UV 10000 +#define BQ25792_OTG_VOLT_MAX_UV 22000000 +#define BQ25792_OTG_VOLT_NUM_VOLT ((BQ25792_OTG_VOLT_MAX_UV \ + - BQ25792_OTG_VOLT_MIN_UV) \ + / BQ25792_OTG_VOLT_STEP_UV + 1) + +/* IOTG regulation */ +#define BQ25792_REG0D_PRECHG_TMR BIT(7) +#define BQ25792_REG0D_IOTG_MASK GENMASK(6, 0) + +#define BQ25792_OTG_CUR_MIN_UA 120000 +#define BQ25792_OTG_CUR_STEP_UA 40000 +#define BQ25792_OTG_CUR_MAX_UA 3320000 + +/* Timer Control */ +#define BQ25792_REG0E_TOPOFF_TMR_MASK GENMASK(7, 6) +#define BQ25792_REG0E_EN_TRICHG_TMR BIT(5) +#define BQ25792_REG0E_EN_PRECHG_TMR BIT(4) +#define BQ25792_REG0E_EN_CHG_TMR BIT(3) +#define BQ25792_REG0E_CHG_TMR_MASK GENMASK(2, 1) +#define BQ25792_REG0E_TMR2X_EN BIT(0) + +/* Charger Control 0 */ +#define BQ25792_REG0F_EN_AUTO_IBATDIS BIT(7) +#define BQ25792_REG0F_FORCE_IBATDIS BIT(6) +#define BQ25792_REG0F_EN_CHG BIT(5) +#define BQ25792_REG0F_EN_ICO BIT(4) +#define BQ25792_REG0F_FORCE_ICO BIT(3) +#define BQ25792_REG0F_EN_HIZ BIT(2) +#define BQ25792_REG0F_EN_TERM BIT(1) +/* bit0 reserved */ + +/* Charger Control 1 */ +#define BQ25792_REG10_VAC_OVP_MASK GENMASK(5, 4) +#define BQ25792_REG10_WD_RST BIT(3) +#define BQ25792_REG10_WATCHDOG_MASK GENMASK(2, 0) + +/* Charger Control 2 */ +#define BQ25792_REG11_FORCE_INDET BIT(7) +#define BQ25792_REG11_AUTO_INDET_EN BIT(6) +#define BQ25792_REG11_EN_12V BIT(5) +#define BQ25792_REG11_EN_9V BIT(4) +#define BQ25792_REG11_HVDCP_EN BIT(3) +#define BQ25792_REG11_SDRV_CTRL_MASK GENMASK(2, 1) +#define BQ25792_REG11_SDRV_DLY BIT(0) + +/* Charger Control 3 */ +#define BQ25792_REG12_DIS_ACDRV BIT(7) +#define BQ25792_REG12_EN_OTG BIT(6) +#define BQ25792_REG12_PFM_OTG_DIS BIT(5) +#define BQ25792_REG12_PFM_FWD_DIS BIT(4) +#define BQ25792_REG12_WKUP_DLY BIT(3) +#define BQ25792_REG12_DIS_LDO BIT(2) +#define BQ25792_REG12_DIS_OTG_OOA BIT(1) +#define BQ25792_REG12_DIS_FWD_OOA BIT(0) + +/* Charger Control 4 */ +#define BQ25792_REG13_EN_ACDRV2 BIT(7) +#define BQ25792_REG13_EN_ACDRV1 BIT(6) +#define BQ25792_REG13_PWM_FREQ BIT(5) +#define BQ25792_REG13_DIS_STAT BIT(4) +#define BQ25792_REG13_DIS_VSYS_SHORT BIT(3) +#define BQ25792_REG13_DIS_VOTG_UVP BIT(2) +#define BQ25792_REG13_FORCE_VINDPM_DET BIT(1) +#define BQ25792_REG13_EN_IBUS_OCP BIT(0) + +/* Charger Control 5 */ +#define BQ25792_REG14_SFET_PRESENT BIT(7) +/* bit6 reserved */ +#define BQ25792_REG14_EN_IBAT BIT(5) +#define BQ25792_REG14_IBAT_REG_MASK GENMASK(4, 3) +#define BQ25792_REG14_EN_IINDPM BIT(2) +#define BQ25792_REG14_EN_EXTILIM BIT(1) +#define BQ25792_REG14_EN_BATOC BIT(0) + +#define BQ25792_IBAT_3A FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 0) +#define BQ25792_IBAT_4A FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 1) +#define BQ25792_IBAT_5A FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 2) +#define BQ25792_IBAT_UNLIM FIELD_PREP(BQ25792_REG14_IBAT_REG_MASK, 3) + +/* Temperature Control */ +#define BQ25792_REG16_TREG_MASK GENMASK(7, 6) +#define BQ25792_REG16_TSHUT_MASK GENMASK(5, 4) +#define BQ25792_REG16_VBUS_PD_EN BIT(3) +#define BQ25792_REG16_VAC1_PD_EN BIT(2) +#define BQ25792_REG16_VAC2_PD_EN BIT(1) + +/* NTC Control 0 */ +#define BQ25792_REG17_JEITA_VSET_MASK GENMASK(7, 5) +#define BQ25792_REG17_JEITA_ISETH_MASK GENMASK(4, 3) +#define BQ25792_REG17_JEITA_ISETC_MASK GENMASK(2, 1) + +/* NTC Control 1 */ +#define BQ25792_REG18_TS_COOL_MASK GENMASK(7, 6) +#define BQ25792_REG18_TS_WARM_MASK GENMASK(5, 4) +#define BQ25792_REG18_BHOT_MASK GENMASK(3, 2) +#define BQ25792_REG18_BCOLD BIT(1) +#define BQ25792_REG18_TS_IGNORE BIT(0) + +/* ICO Current Limit */ +#define BQ25792_REG19_ICO_ILIM_MASK GENMASK(8, 0) + +/* Charger Status 0 */ +#define BQ25792_REG1B_IINDPM_STAT BIT(7) +#define BQ25792_REG1B_VINDPM_STAT BIT(6) +#define BQ25792_REG1B_WD_STAT BIT(5) +#define BQ25792_REG1B_POORSRC_STAT BIT(4) +#define BQ25792_REG1B_PG_STAT BIT(3) +#define BQ25792_REG1B_AC2_PRESENT_STAT BIT(2) +#define BQ25792_REG1B_AC1_PRESENT_STAT BIT(1) +#define BQ25792_REG1B_VBUS_PRESENT_STAT BIT(0) + +/* Charger Status 1 */ +#define BQ25792_REG1C_CHG_STAT_MASK GENMASK(7, 5) +#define BQ25792_REG1C_VBUS_STAT_MASK GENMASK(4, 1) +#define BQ25792_REG1C_BC12_DONE_STAT BIT(0) + +/* Charger Status 2 */ +#define BQ25792_REG1D_ICO_STAT_MASK GENMASK(7, 6) +#define BQ25792_REG1D_TREG_STAT BIT(2) +#define BQ25792_REG1D_DPDM_STAT BIT(1) +#define BQ25792_REG1D_VBAT_PRESENT_STAT BIT(0) + +/* Charger Status 3 */ +#define BQ25792_REG1E_ACRB2_STAT BIT(7) +#define BQ25792_REG1E_ACRB1_STAT BIT(6) +#define BQ25792_REG1E_ADC_DONE_STAT BIT(5) +#define BQ25792_REG1E_VSYS_STAT BIT(4) +#define BQ25792_REG1E_CHG_TMR_STAT BIT(3) +#define BQ25792_REG1E_TRICHG_TMR_STAT BIT(2) +#define BQ25792_REG1E_PRECHG_TMR_STAT BIT(1) + +/* Charger Status 4 */ +#define BQ25792_REG1F_VBATOTG_LOW_STAT BIT(4) +#define BQ25792_REG1F_TS_COLD_STAT BIT(3) +#define BQ25792_REG1F_TS_COOL_STAT BIT(2) +#define BQ25792_REG1F_TS_WARM_STAT BIT(1) +#define BQ25792_REG1F_TS_HOT_STAT BIT(0) + +/* FAULT Status 0 */ +#define BQ25792_REG20_IBAT_REG_STAT BIT(7) +#define BQ25792_REG20_VBUS_OVP_STAT BIT(6) +#define BQ25792_REG20_VBAT_OVP_STAT BIT(5) +#define BQ25792_REG20_IBUS_OCP_STAT BIT(4) +#define BQ25792_REG20_IBAT_OCP_STAT BIT(3) +#define BQ25792_REG20_CONV_OCP_STAT BIT(2) +#define BQ25792_REG20_VAC2_OVP_STAT BIT(1) +#define BQ25792_REG20_VAC1_OVP_STAT BIT(0) + +#define BQ25792_REG20_OVERVOLTAGE_MASK (BQ25792_REG20_VBAT_OVP_STAT | \ + BQ25792_REG20_VAC2_OVP_STAT | \ + BQ25792_REG20_VAC1_OVP_STAT) +#define BQ25792_REG20_OVERCURRENT_MASK (BQ25792_REG20_IBAT_OCP_STAT | \ + BQ25792_REG20_CONV_OCP_STAT) + +/* FAULT Status 1 */ +#define BQ25792_REG21_VSYS_SHORT_STAT BIT(7) +#define BQ25792_REG21_VSYS_OVP_STAT BIT(6) +#define BQ25792_REG21_OTG_OVP_STAT BIT(5) +#define BQ25792_REG21_OTG_UVP_STAT BIT(4) +#define BQ25792_REG21_TSHUT_STAT BIT(2) + + +/* Charger Flag 0 */ +#define BQ25792_REG22_IINDPM_FLAG BIT(7) +#define BQ25792_REG22_VINDPM_FLAG BIT(6) +#define BQ25792_REG22_WD_FLAG BIT(5) +#define BQ25792_REG22_POORSRC_FLAG BIT(4) +#define BQ25792_REG22_PG_FLAG BIT(3) +#define BQ25792_REG22_AC2_PRESENT_FLAG BIT(2) +#define BQ25792_REG22_AC1_PRESENT_FLAG BIT(1) +#define BQ25792_REG22_VBUS_PRESENT_FLAG BIT(0) + +/* Charger Flag 1 */ +#define BQ25792_REG23_CHG_FLAG BIT(7) +#define BQ25792_REG23_ICO_FLAG BIT(6) +#define BQ25792_REG23_VBUS_FLAG BIT(4) +#define BQ25792_REG23_TREG_FLAG BIT(2) +#define BQ25792_REG23_VBAT_PRESENT_FLAG BIT(1) +#define BQ25792_REG23_BC12_DONE_FLAG BIT(0) + +/* Charger Flag 2 */ +#define BQ25792_REG24_DPDM_DONE_FLAG BIT(6) +#define BQ25792_REG24_ADC_DONE_FLAG BIT(5) +#define BQ25792_REG24_VSYS_FLAG BIT(4) +#define BQ25792_REG24_CHG_TMR_FLAG BIT(3) +#define BQ25792_REG24_TRICHG_TMR_FLAG BIT(2) +#define BQ25792_REG24_PRECHG_TMR_FLAG BIT(1) +#define BQ25792_REG24_TOPOFF_TMR_FLAG BIT(0) + +/* Charger Flag 3 */ +#define BQ25792_REG25_VBATOTG_LOW_FLAG BIT(4) +#define BQ25792_REG25_TS_COLD_FLAG BIT(3) +#define BQ25792_REG25_TS_COOL_FLAG BIT(2) +#define BQ25792_REG25_TS_WARM_FLAG BIT(1) +#define BQ25792_REG25_TS_HOT_FLAG BIT(0) + +/* FAULT Flag 0 */ +#define BQ25792_REG26_IBAT_REG_FLAG BIT(7) +#define BQ25792_REG26_VBUS_OVP_FLAG BIT(6) +#define BQ25792_REG26_VBAT_OVP_FLAG BIT(5) +#define BQ25792_REG26_IBUS_OCP_FLAG BIT(4) +#define BQ25792_REG26_IBAT_OCP_FLAG BIT(3) +#define BQ25792_REG26_CONV_OCP_FLAG BIT(2) +#define BQ25792_REG26_VAC2_OVP_FLAG BIT(1) +#define BQ25792_REG26_VAC1_OVP_FLAG BIT(0) + +/* FAULT Flag 1 */ +#define BQ25792_REG27_VSYS_SHORT_FLAG BIT(7) +#define BQ25792_REG27_VSYS_OVP_FLAG BIT(6) +#define BQ25792_REG27_OTG_OVP_FLAG BIT(5) +#define BQ25792_REG27_OTG_UVP_FLAG BIT(4) +#define BQ25792_REG27_TSHUT_FLAG BIT(2) + +/* Charger Mask 0 */ +#define BQ25792_REG28_IINDPM_MASK BIT(7) +#define BQ25792_REG28_VINDPM_MASK BIT(6) +#define BQ25792_REG28_WD_MASK BIT(5) +#define BQ25792_REG28_POORSRC_MASK BIT(4) +#define BQ25792_REG28_PG_MASK BIT(3) +#define BQ25792_REG28_AC2_PRESENT_MASK BIT(2) +#define BQ25792_REG28_AC1_PRESENT_MASK BIT(1) +#define BQ25792_REG28_VBUS_PRESENT_MASK BIT(0) + +/* Charger Mask 1 */ +#define BQ25792_REG29_CHG_MASK BIT(7) +#define BQ25792_REG29_ICO_MASK BIT(6) +#define BQ25792_REG29_VBUS_MASK BIT(4) +#define BQ25792_REG29_TREG_MASK BIT(2) +#define BQ25792_REG29_VBAT_PRESENT_MASK BIT(1) +#define BQ25792_REG29_BC12_DONE_MASK BIT(0) + +/* Charger Mask 2 */ +#define BQ25792_REG2A_DPDM_DONE_MASK BIT(6) +#define BQ25792_REG2A_ADC_DONE_MASK BIT(5) +#define BQ25792_REG2A_VSYS_MASK BIT(4) +#define BQ25792_REG2A_CHG_TMR_MASK BIT(3) +#define BQ25792_REG2A_TRICHG_TMR_MASK BIT(2) +#define BQ25792_REG2A_PRECHG_TMR_MASK BIT(1) +#define BQ25792_REG2A_TOPOFF_TMR_MASK BIT(0) + +/* Charger Mask 3 */ +#define BQ25792_REG2B_VBATOTG_LOW_MASK BIT(4) +#define BQ25792_REG2B_TS_COLD_MASK BIT(3) +#define BQ25792_REG2B_TS_COOL_MASK BIT(2) +#define BQ25792_REG2B_TS_WARM_MASK BIT(1) +#define BQ25792_REG2B_TS_HOT_MASK BIT(0) + +/* FAULT Mask 0 */ +#define BQ25792_REG2C_IBAT_REG_MASK BIT(7) +#define BQ25792_REG2C_VBUS_OVP_MASK BIT(6) +#define BQ25792_REG2C_VBAT_OVP_MASK BIT(5) +#define BQ25792_REG2C_IBUS_OCP_MASK BIT(4) +#define BQ25792_REG2C_IBAT_OCP_MASK BIT(3) +#define BQ25792_REG2C_CONV_OCP_MASK BIT(2) +#define BQ25792_REG2C_VAC2_OVP_MASK BIT(1) +#define BQ25792_REG2C_VAC1_OVP_MASK BIT(0) + +/* FAULT Mask 1 */ +#define BQ25792_REG2D_VSYS_SHORT_MASK BIT(7) +#define BQ25792_REG2D_VSYS_OVP_MASK BIT(6) +#define BQ25792_REG2D_OTG_OVP_MASK BIT(5) +#define BQ25792_REG2D_OTG_UVP_MASK BIT(4) +#define BQ25792_REG2D_TSHUT_MASK BIT(2) + +/* ADC Control */ +#define BQ25792_REG2E_ADC_EN BIT(7) +#define BQ25792_REG2E_ADC_RATE BIT(6) +#define BQ25792_REG2E_ADC_SAMPLE_MASK GENMASK(5, 4) +#define BQ25792_REG2E_ADC_AVG BIT(3) +#define BQ25792_REG2E_ADC_AVG_INIT BIT(2) + +/* ADC Function Disable 0 */ +#define BQ25792_REG2F_IBUS_ADC_DIS BIT(7) +#define BQ25792_REG2F_IBAT_ADC_DIS BIT(6) +#define BQ25792_REG2F_VBUS_ADC_DIS BIT(5) +#define BQ25792_REG2F_VBAT_ADC_DIS BIT(4) +#define BQ25792_REG2F_VSYS_ADC_DIS BIT(3) +#define BQ25792_REG2F_TS_ADC_DIS BIT(2) +#define BQ25792_REG2F_TDIE_ADC_DIS BIT(1) + +/* ADC Function Disable 1 */ +#define BQ25792_REG30_DP_ADC_DIS BIT(7) +#define BQ25792_REG30_DM_ADC_DIS BIT(6) +#define BQ25792_REG30_VAC2_ADC_DIS BIT(5) +#define BQ25792_REG30_VAC1_ADC_DIS BIT(4) + +/* 0x31-0x45: ADC result registers (16-bit, RO): single full-width field */ + +#define BQ25792_ADCVSYSVBAT_STEP_UV 1000 +#define BQ25792_ADCIBAT_STEP_UA 1000 + +/* DPDM Driver */ +#define BQ25792_REG47_DPLUS_DAC_MASK GENMASK(7, 5) +#define BQ25792_REG47_DMINUS_DAC_MASK GENMASK(4, 2) + +/* Part Information */ +#define BQ25792_REG48_PN_MASK GENMASK(5, 3) +#define BQ25792_REG48_DEV_REV_MASK GENMASK(2, 0) + +enum bq257xx_type { + BQ25703A = 1, + BQ25792, +}; + struct bq257xx_device { struct i2c_client *client; struct regmap *regmap; + enum bq257xx_type type; }; From 8c2f0b42fc252e1bf1c7746447091a468e784ca1 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:26:27 +0800 Subject: [PATCH 3891/5214] mfd: sm501: Fix reference leak on failed device registration When platform_device_register() fails in sm501_register_device(), the embedded struct device in pdev has already been initialized by device_initialize(), but the failure path only reports the error and returns without dropping the device reference for the current platform device: sm501_register_device() -> platform_device_register(pdev) -> device_initialize(&pdev->dev) -> setup_pdev_dma_masks(pdev) -> platform_device_add(pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: b6d6454fdb66f ("[PATCH] mfd: SM501 core driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260415162627.3558789-1-lgs201920130244@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/sm501.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 0ee6d8940e69..8276456b142f 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -704,9 +704,11 @@ static int sm501_register_device(struct sm501_devdata *sm, if (ret >= 0) { dev_dbg(sm->dev, "registered %s\n", pdev->name); list_add_tail(&smdev->list, &sm->devices); - } else + } else { dev_err(sm->dev, "error registering %s (%d)\n", pdev->name, ret); + platform_device_put(pdev); + } return ret; } From 17f95281839db78be227a6924750dda699f74706 Mon Sep 17 00:00:00 2001 From: Jameson Thies Date: Fri, 3 Apr 2026 22:22:53 +0000 Subject: [PATCH 3892/5214] mfd: cros_ec: Don't add cros_ec_ucsi if it is defined in OF or ACPI On devices with a UCSI PPM in the EC, check for cros_ec_ucsi to be defined in the OF device tree or an ACPI node. If it is defined by either OF or ACPI, it does not need to be added as a subdevice of cros_ec_dev mfd. cros_ec_ucsi will load from the OF or ACPI node. Signed-off-by: Jameson Thies Reviewed-by: Benson Leung Reviewed-by: Abhishek Pandit-Subedi Link: https://patch.msgid.link/20260403222253.1888991-2-jthies@google.com Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 39430dd44e30..70d64b7c6243 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -5,6 +5,7 @@ * Copyright (C) 2014 Google, Inc. */ +#include #include #include #include @@ -131,11 +132,6 @@ static const struct cros_feature_to_cells cros_subdevices[] = { .mfd_cells = cros_ec_rtc_cells, .num_cells = ARRAY_SIZE(cros_ec_rtc_cells), }, - { - .id = EC_FEATURE_UCSI_PPM, - .mfd_cells = cros_ec_ucsi_cells, - .num_cells = ARRAY_SIZE(cros_ec_ucsi_cells), - }, { .id = EC_FEATURE_HANG_DETECT, .mfd_cells = cros_ec_wdt_cells, @@ -264,6 +260,23 @@ static int ec_device_probe(struct platform_device *pdev) } } + /* + * FW nodes can load cros_ec_ucsi, but early PDC devices did not define + * the required nodes. On PDC systems without FW nodes for cros_ec_ucsi, + * the driver should be added as an mfd subdevice. + */ + if (cros_ec_check_features(ec, EC_FEATURE_USB_PD) && + cros_ec_check_features(ec, EC_FEATURE_UCSI_PPM) && + !acpi_dev_found("GOOG0021") && + !of_find_compatible_node(NULL, NULL, "google,cros-ec-ucsi")) { + retval = mfd_add_hotplug_devices(ec->dev, + cros_ec_ucsi_cells, + ARRAY_SIZE(cros_ec_ucsi_cells)); + + if (retval) + dev_warn(ec->dev, "failed to add cros_ec_ucsi: %d\n", retval); + } + /* * UCSI provides power supply information so we don't need to separately * load the cros_usbpd_charger driver. From 68d574451d20927b38db08369af6bacecf55205a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 21 Apr 2026 18:29:03 +0100 Subject: [PATCH 3893/5214] dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L The RZ/G3{E,L} SoCs have an LVDS Common (LVDS_CMN) region which is common to all LVDS channels. The RZ/G3L has single-link, but the RZ/G3E has both single and dual-link. Use the syscon interface to access these registers for scalability. Signed-off-by: Tommaso Merciai Signed-off-by: Biju Das Acked-by: Conor Dooley Link: https://patch.msgid.link/20260421172910.218497-2-biju.das.jz@bp.renesas.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/syscon.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index e22867088063..9c81010d5a74 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -221,6 +221,7 @@ properties: - nxp,s32g3-gpr - qcom,apq8064-mmss-sfpb - qcom,apq8064-sps-sic + - renesas,r9a08g046-lvds-cmn - rockchip,px30-qos - rockchip,rk3036-qos - rockchip,rk3066-qos From 39dd85d9246ecf880aeea165f594c81c50afaabf Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Tue, 21 Apr 2026 13:49:18 +0200 Subject: [PATCH 3894/5214] dt-bindings: mfd: khadas: Add new compatible for Khadas VIM4 MCU The Khadas VIM4 MCU register is slightly different from previous boards' MCU. This board also features a switchable power source for its fan. Signed-off-by: Ronald Claveau Acked-by: Conor Dooley Link: https://patch.msgid.link/20260421-add-mcu-fan-khadas-vim4-v4-1-447114a28f2d@aliel.fr Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/khadas,mcu.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml index 084960fd5a1f..1f135618e3b6 100644 --- a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml +++ b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml @@ -18,6 +18,7 @@ properties: compatible: enum: - khadas,mcu # MCU revision is discoverable + - khadas,vim4-mcu # Different MCU variant, not discoverable "#cooling-cells": # Only needed for boards having FAN control feature const: 2 @@ -25,10 +26,27 @@ properties: reg: maxItems: 1 + fan-supply: + description: Phandle to the regulator that powers the fan. + $ref: /schemas/types.yaml#/definitions/phandle + required: - compatible - reg +allOf: + - if: + properties: + compatible: + contains: + const: khadas,vim4-mcu + then: + required: + - fan-supply + else: + properties: + fan-supply: false + additionalProperties: false examples: From 82f09182307f9a721b9bb87869cb6cf03ba36fb4 Mon Sep 17 00:00:00 2001 From: Shaunak Datar Date: Thu, 23 Apr 2026 17:02:37 +0530 Subject: [PATCH 3895/5214] dt-bindings: mfd: hisilicon,hi655x-pmic: Convert to DT schema Convert the Hisilicon Hi655x PMIC binding from the text format to DT schema to enable dtbs_check validation. The 'regulators' child node is added based on existing usage in arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts, which defines child regulator nodes not documented in the original .txt binding. The uppercase LDO names are retained to match existing DTS usage. Signed-off-by: Shaunak Datar Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260423113237.260652-1-shaunakkdatar@gmail.com Signed-off-by: Lee Jones --- .../bindings/mfd/hisilicon,hi655x-pmic.yaml | 80 +++++++++++++++++++ .../bindings/mfd/hisilicon,hi655x.txt | 33 -------- 2 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/hisilicon,hi655x-pmic.yaml delete mode 100644 Documentation/devicetree/bindings/mfd/hisilicon,hi655x.txt diff --git a/Documentation/devicetree/bindings/mfd/hisilicon,hi655x-pmic.yaml b/Documentation/devicetree/bindings/mfd/hisilicon,hi655x-pmic.yaml new file mode 100644 index 000000000000..6f28f472e0f5 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/hisilicon,hi655x-pmic.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/hisilicon,hi655x-pmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon Hi655x Power Management Integrated Circuit + +maintainers: + - Chen Feng + - Daniel Lezcano + +description: + The hardware layout for access PMIC Hi655x from AP SoC Hi6220. + Between PMIC Hi655x and Hi6220, the physical signal channel is SSI. + We can use memory-mapped I/O to communicate. + +properties: + compatible: + const: hisilicon,hi655x-pmic + + reg: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + pmic-gpios: + maxItems: 1 + description: The GPIO used by PMIC IRQ + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + regulators: + type: object + additionalProperties: false + + patternProperties: + '^LDO(2|7|10|13|14|15|17|19|21|22)$': + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupt-controller + - '#interrupt-cells' + - pmic-gpios + - '#clock-cells' + +additionalProperties: false + +examples: + - | + #include + + pmic: pmic@f8000000 { + compatible = "hisilicon,hi655x-pmic"; + reg = <0xf8000000 0x1000>; + #clock-cells = <0>; + interrupt-controller; + #interrupt-cells = <2>; + pmic-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + + regulators { + ldo2: LDO2 { + regulator-name = "LDO2_2V8"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3200000>; + regulator-enable-ramp-delay = <120>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/hisilicon,hi655x.txt b/Documentation/devicetree/bindings/mfd/hisilicon,hi655x.txt deleted file mode 100644 index 9630ac0e4b56..000000000000 --- a/Documentation/devicetree/bindings/mfd/hisilicon,hi655x.txt +++ /dev/null @@ -1,33 +0,0 @@ -Hisilicon Hi655x Power Management Integrated Circuit (PMIC) - -The hardware layout for access PMIC Hi655x from AP SoC Hi6220. -Between PMIC Hi655x and Hi6220, the physical signal channel is SSI. -We can use memory-mapped I/O to communicate. - -+----------------+ +-------------+ -| | | | -| Hi6220 | SSI bus | Hi655x | -| |-------------| | -| |(REGMAP_MMIO)| | -+----------------+ +-------------+ - -Required properties: -- compatible: Should be "hisilicon,hi655x-pmic". -- reg: Base address of PMIC on Hi6220 SoC. -- interrupt-controller: Hi655x has internal IRQs (has own IRQ domain). -- pmic-gpios: The GPIO used by PMIC IRQ. -- #clock-cells: From common clock binding; shall be set to 0 - -Optional properties: -- clock-output-names: From common clock binding to override the - default output clock name - -Example: - pmic: pmic@f8000000 { - compatible = "hisilicon,hi655x-pmic"; - reg = <0x0 0xf8000000 0x0 0x1000>; - interrupt-controller; - #interrupt-cells = <2>; - pmic-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; - #clock-cells = <0>; - } From 4dd72d0044aaf5febe731e811bcaa1bc73400b48 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 15:52:16 +0200 Subject: [PATCH 3896/5214] mfd: MAINTAINERS: Remove Krzysztof from Samsung PMIC drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to lack of time I did not perform reviews of patches for Samsung PMIC drivers last year, at least not in timely manner. I still can perform limited testing of the code on hardware, but that does not warrant having "M" here. Maintainer should be responsive, so drop my name and shift these drivers maintenance to André Draszik (from previous reviewer role). Signed-off-by: Krzysztof Kozlowski Acked-by: André Draszik Link: https://patch.msgid.link/20260428135216.100135-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Lee Jones --- CREDITS | 1 + MAINTAINERS | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS b/CREDITS index 17962bdd6dbd..1537f2f70582 100644 --- a/CREDITS +++ b/CREDITS @@ -2241,6 +2241,7 @@ S: Canada N: Krzysztof Kozlowski E: krzk@kernel.org D: NFC network subsystem and drivers maintainer +D: Samsung S2M/S5M Multifunction PMIC device drivers for Exynos platforms N: Christian Krafft D: PowerPC Cell support diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..3c40021ad2db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23712,8 +23712,7 @@ S: Maintained F: drivers/platform/x86/samsung-laptop.c SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS -M: Krzysztof Kozlowski -R: André Draszik +M: André Draszik L: linux-kernel@vger.kernel.org L: linux-samsung-soc@vger.kernel.org S: Maintained From 6bc38f26ed07197b11a2b588edf1f43bfbc81d76 Mon Sep 17 00:00:00 2001 From: Matthew Bystrin Date: Wed, 29 Apr 2026 10:20:46 +0300 Subject: [PATCH 3897/5214] mfd: rsmu: Fix page register setup Fix writes to page register in 8A3400x family (Clock Matrix). All calls to rsmu_write_page_register() (both in i2c and spi) have resulted in early return, because all addresses in include/linux/mfd/idt8a340_reg.h are less than RSMU_CM_SCSR_BASE. There were 2 separate patch series which have to be merged in one time: mfd and ptp. The latter have been merged, the former[1] have not. Link: https://lore.kernel.org/netdev/LV3P220MB1202F8E2FCCFBA2519B4966EA0192@LV3P220MB1202.NAMP220.PROD.OUTLOOK.COM/ Fixes: 67d6c76fc815 ("mfd: rsmu: Support 32-bit address space") Signed-off-by: Matthew Bystrin Link: https://patch.msgid.link/20260429072047.1111427-2-dev.mbstr@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/rsmu_i2c.c | 6 +----- drivers/mfd/rsmu_spi.c | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c index cba64f107a2f..9e5fc8259eec 100644 --- a/drivers/mfd/rsmu_i2c.c +++ b/drivers/mfd/rsmu_i2c.c @@ -134,14 +134,10 @@ static int rsmu_i2c_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 by static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg, rsmu_rw_device rsmu_write_device) { - u32 page = reg & RSMU_CM_PAGE_MASK; + u32 page = (reg | RSMU_CM_SCSR_BASE) & RSMU_CM_PAGE_MASK; u8 buf[4]; int err; - /* Do not modify offset register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; - /* Simply return if we are on the same page */ if (rsmu->page == page) return 0; diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index 39d9be1e141f..c931d8cea0a1 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -101,11 +101,8 @@ static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg) switch (rsmu->type) { case RSMU_CM: - /* Do not modify page register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; page_reg = RSMU_CM_PAGE_ADDR; - page = reg & RSMU_PAGE_MASK; + page = (reg | RSMU_CM_SCSR_BASE) & RSMU_PAGE_MASK; buf[0] = (u8)(page & 0xFF); buf[1] = (u8)((page >> 8) & 0xFF); buf[2] = (u8)((page >> 16) & 0xFF); From d18fd55c780c2bae3d353024cab7f8746d3d9e91 Mon Sep 17 00:00:00 2001 From: Matthew Bystrin Date: Wed, 29 Apr 2026 10:20:47 +0300 Subject: [PATCH 3898/5214] mfd: rsmu: Add 8a34002 support Add compatible string, i2c_devcie_id and spi_devcie_id to support 8a34002. Signed-off-by: Matthew Bystrin Link: https://patch.msgid.link/20260429072047.1111427-3-dev.mbstr@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/rsmu_i2c.c | 2 ++ drivers/mfd/rsmu_spi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c index 9e5fc8259eec..c57795ed20f4 100644 --- a/drivers/mfd/rsmu_i2c.c +++ b/drivers/mfd/rsmu_i2c.c @@ -330,6 +330,7 @@ static void rsmu_i2c_remove(struct i2c_client *client) static const struct i2c_device_id rsmu_i2c_id[] = { { "8a34000", RSMU_CM }, { "8a34001", RSMU_CM }, + { "8a34002", RSMU_CM }, { "82p33810", RSMU_SABRE }, { "82p33811", RSMU_SABRE }, { "8v19n850", RSMU_SL }, @@ -341,6 +342,7 @@ MODULE_DEVICE_TABLE(i2c, rsmu_i2c_id); static const struct of_device_id rsmu_i2c_of_match[] = { { .compatible = "idt,8a34000", .data = (void *)RSMU_CM }, { .compatible = "idt,8a34001", .data = (void *)RSMU_CM }, + { .compatible = "idt,8a34002", .data = (void *)RSMU_CM }, { .compatible = "idt,82p33810", .data = (void *)RSMU_SABRE }, { .compatible = "idt,82p33811", .data = (void *)RSMU_SABRE }, { .compatible = "idt,8v19n850", .data = (void *)RSMU_SL }, diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index c931d8cea0a1..e07f21482439 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -241,6 +241,7 @@ static void rsmu_spi_remove(struct spi_device *client) static const struct spi_device_id rsmu_spi_id[] = { { "8a34000", RSMU_CM }, { "8a34001", RSMU_CM }, + { "8a34002", RSMU_CM }, { "82p33810", RSMU_SABRE }, { "82p33811", RSMU_SABRE }, {} @@ -250,6 +251,7 @@ MODULE_DEVICE_TABLE(spi, rsmu_spi_id); static const struct of_device_id rsmu_spi_of_match[] = { { .compatible = "idt,8a34000", .data = (void *)RSMU_CM }, { .compatible = "idt,8a34001", .data = (void *)RSMU_CM }, + { .compatible = "idt,8a34002", .data = (void *)RSMU_CM }, { .compatible = "idt,82p33810", .data = (void *)RSMU_SABRE }, { .compatible = "idt,82p33811", .data = (void *)RSMU_SABRE }, {} From ade13e2b7bbb1dcdfb98091236b5abf63f259d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 29 Apr 2026 19:06:51 +0200 Subject: [PATCH 3899/5214] mfd: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The various struct pci_device_id arrays were initialized mostly by one of the PCI_DEVICE macros and then list expressions. The latter isn't easily readable if you're not into PCI. Using named initializers is more explicit and thus easier to parse. The secret plan is to make struct pci_device_id::driver_data an anonymous union (similar to https://lore.kernel.org/all/cover.1776579304.git.u.kleine-koenig@baylibre.com/) and that requires named initializers. But it's also a nice cleanup on its own. This change doesn't introduce changes to the compiled pci_device_id arrays. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260429170652.4178050-2-u.kleine-koenig@baylibre.com Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-pci.c | 786 +++++++++++++++++------------------ drivers/mfd/lpc_ich.c | 476 ++++++++++----------- drivers/mfd/lpc_sch.c | 10 +- drivers/mfd/sm501.c | 4 +- 4 files changed, 638 insertions(+), 638 deletions(-) diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index a9452ac92fb2..f7c592dd7e87 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -247,431 +247,431 @@ static const struct intel_lpss_platform_info tgl_spi_info = { static const struct pci_device_id intel_lpss_pci_ids[] = { /* CML-LP */ - { PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x02c5), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02c6), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02c7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x02e8), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02e9), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x02a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x02a9), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x02aa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x02ab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x02c5), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02c6), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02c7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x02e8), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02e9), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02ea), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02eb), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x02fb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* CML-H */ - { PCI_VDEVICE(INTEL, 0x06a8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x06a9), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x06c7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x06e8), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x06e9), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x06ea), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x06eb), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x06a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06a9), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06aa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x06ab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x06c7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06e8), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06e9), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06ea), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06eb), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06fb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* BXT A-Step */ - { PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0ab0), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0ab2), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0ab4), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0ab6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0ab8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0aba), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x0abc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x0abe), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x0ac0), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x0aee), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x0aac), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0aae), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0ab0), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0ab2), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0ab4), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0ab6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0ab8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0aba), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x0abc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x0abe), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x0ac0), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x0ac2), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x0ac4), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x0ac6), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x0aee), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* BXT B-Step */ - { PCI_VDEVICE(INTEL, 0x1aac), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1aae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1ab0), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1ab2), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1ab4), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1ab6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1ab8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1aba), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x1abc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x1abe), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x1ac0), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1aac), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1aae), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab0), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab2), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab4), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1aba), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1abc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1abe), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1ac0), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1ac2), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x1ac4), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x1ac6), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x1aee), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* EBG */ - { PCI_VDEVICE(INTEL, 0x1bad), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x1bae), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1bad), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1bae), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* GLK */ - { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&glk_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31ac), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ae), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b0), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b2), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b4), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b6), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b8), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ba), .driver_data = (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31bc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31be), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31c0), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31c2), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x31c4), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x31c6), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x31ee), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* ICL-LP */ - { PCI_VDEVICE(INTEL, 0x34a8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x34a9), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x34c5), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34c6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34c7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x34e8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34e9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34ea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34eb), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x34a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x34a9), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x34aa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x34ab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x34c5), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34c6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34c7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x34e8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34e9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34ea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34eb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x34fb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* ICL-N */ - { PCI_VDEVICE(INTEL, 0x38a8), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x38a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, /* TGL-H */ - { PCI_VDEVICE(INTEL, 0x43a7), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x43a8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x43a9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x43ad), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43ae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43d8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43da), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x43e8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43e9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43ea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43eb), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x43a7), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x43a8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x43a9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x43aa), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x43ab), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x43ad), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43ae), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43d8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43da), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x43e8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43e9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43ea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43eb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x43fb), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x43fd), .driver_data = (kernel_ulong_t)&tgl_spi_info }, /* EHL */ - { PCI_VDEVICE(INTEL, 0x4b28), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4b29), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x4b44), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b45), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b4b), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b4c), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b4d), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4b78), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b79), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b7a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4b7b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b28), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4b29), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4b2a), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x4b2b), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x4b37), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x4b44), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b45), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b4b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b4c), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b4d), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4b78), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b79), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b7a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4b7b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* WCL */ - { PCI_VDEVICE(INTEL, 0x4d25), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4d26), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4d27), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x4d30), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x4d46), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x4d50), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4d51), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4d52), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4d78), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4d79), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4d7a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4d7b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d25), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4d26), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4d27), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4d30), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4d46), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4d50), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d51), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d52), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4d78), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d79), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d7a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4d7b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* JSL */ - { PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x4dc5), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4dc6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4dc7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x4de8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4de9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4da8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4da9), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4daa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4dab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x4dc5), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4dc6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4dc7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x4de8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4de9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4dea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4deb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x4dfb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* ADL-P */ - { PCI_VDEVICE(INTEL, 0x51a8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x51a9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x51c5), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51c6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51c7), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x51d8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51d9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51e8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51e9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51ea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51eb), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x51a8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51a9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51aa), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x51ab), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x51c5), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51c6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51c7), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x51d8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51d9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51e8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51e9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51ea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51eb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51fb), .driver_data = (kernel_ulong_t)&tgl_spi_info }, /* ADL-M */ - { PCI_VDEVICE(INTEL, 0x54a8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x54a9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x54c5), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54c6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54c7), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x54e8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54e9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54ea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54eb), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x54a8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x54a9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x54aa), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x54ab), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x54c5), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54c6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54c7), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x54e8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54e9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54ea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54eb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x54fb), .driver_data = (kernel_ulong_t)&tgl_spi_info }, /* APL */ - { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab0), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab2), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab4), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab6), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab8), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5aba), (kernel_ulong_t)&apl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5abc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x5abe), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x5ac0), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x5aac), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5aae), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab0), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab2), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab4), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab6), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab8), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5aba), .driver_data = (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5abc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x5abe), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x5ac0), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x5ac2), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x5ac4), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x5ac6), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x5aee), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* NVL-S */ - { PCI_VDEVICE(INTEL, 0x6e28), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x6e29), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x6e2a), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x6e2b), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x6e4c), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x6e4d), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x6e4e), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x6e4f), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x6e5c), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x6e5e), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x6e7a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x6e7b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e28), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x6e29), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x6e2a), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x6e2b), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x6e4c), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e4d), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e4e), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e4f), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e5c), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x6e5e), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x6e7a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x6e7b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* ARL-H */ - { PCI_VDEVICE(INTEL, 0x7725), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7726), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7727), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7730), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7746), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7750), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7751), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7752), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7778), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7779), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x777a), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x777b), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7725), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7726), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7727), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7730), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7746), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7750), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7751), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7752), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7778), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7779), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x777a), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x777b), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, /* RPL-S */ - { PCI_VDEVICE(INTEL, 0x7a28), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7a29), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7a4c), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a4d), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a4e), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a4f), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a5c), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7a7c), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a7d), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7a7e), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a28), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a29), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a2a), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7a2b), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7a4c), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4d), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4e), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a4f), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a5c), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7a79), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7a7b), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7a7c), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a7d), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7a7e), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* ADL-S */ - { PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7acc), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7acd), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7ace), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7acf), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7adc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7afc), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7afd), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7afe), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7aa8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7aa9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7aaa), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7aab), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7acc), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7acd), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7ace), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7acf), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7adc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7af9), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7afb), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7afc), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7afd), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7afe), .driver_data = (kernel_ulong_t)&bxt_uart_info }, /* MTL-P */ - { PCI_VDEVICE(INTEL, 0x7e25), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7e26), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7e50), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7e51), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7e52), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7e78), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7e79), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7e7a), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7e7b), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e25), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7e26), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7e27), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7e30), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7e46), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7e50), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e51), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e52), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7e78), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e79), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e7a), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e7b), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, /* MTP-S */ - { PCI_VDEVICE(INTEL, 0x7f28), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7f29), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7f4c), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7f4d), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7f4e), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7f4f), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7f5c), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7f5d), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0x7f7a), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x7f7b), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f28), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f29), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f2a), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7f2b), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7f4c), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4d), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4e), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f4f), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f5c), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f5d), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x7f5e), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7f5f), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0x7f7a), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7f7b), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, /* LKF */ - { PCI_VDEVICE(INTEL, 0x98a8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x98a9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x98aa), (kernel_ulong_t)&bxt_spi_info }, - { PCI_VDEVICE(INTEL, 0x98c5), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x98c6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x98c7), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0x98e8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x98e9), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x98ea), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x98eb), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98a8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x98a9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x98aa), .driver_data = (kernel_ulong_t)&bxt_spi_info }, + { PCI_VDEVICE(INTEL, 0x98c5), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98c6), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98c7), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x98e8), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98e9), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98ea), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98eb), .driver_data = (kernel_ulong_t)&bxt_i2c_info }, /* SPT-LP */ - { PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d63), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d64), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d65), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9d66), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9d27), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9d28), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9d29), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0x9d2a), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0x9d60), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d61), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d62), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d63), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d64), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d65), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d66), .driver_data = (kernel_ulong_t)&spt_uart_info }, /* CNL-LP */ - { PCI_VDEVICE(INTEL, 0x9da8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x9da9), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0x9dc5), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9dc6), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9dc7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0x9de8), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9de9), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9dea), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9deb), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x9da8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9da9), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9daa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x9dab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0x9dc5), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9dc6), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9dc7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x9de8), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9de9), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9dea), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9deb), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9dfb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* TGL-LP */ - { PCI_VDEVICE(INTEL, 0xa0a8), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0a9), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0c5), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0c6), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0c7), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0d8), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0d9), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0da), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0db), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0dc), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0dd), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0e8), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0e9), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0ea), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0eb), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0a8), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0a9), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0aa), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0ab), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0c5), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0c6), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0c7), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0d8), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0d9), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0da), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0db), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0dc), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0dd), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa0de), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0df), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0e8), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0e9), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0ea), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0eb), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa0fb), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0fd), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa0fe), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* SPT-H */ - { PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa162), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa166), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa127), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa128), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa129), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa12a), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa160), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa161), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa162), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa166), .driver_data = (kernel_ulong_t)&spt_uart_info }, /* KBL-H */ - { PCI_VDEVICE(INTEL, 0xa2a7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa2a8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa2e0), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa2e1), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa2e2), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa2e3), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa2e6), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa2a7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa2a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa2a9), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa2aa), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa2e0), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e1), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e2), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e3), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e6), .driver_data = (kernel_ulong_t)&spt_uart_info }, /* CNL-H */ - { PCI_VDEVICE(INTEL, 0xa328), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa329), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa347), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa368), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa369), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa36a), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa36b), (kernel_ulong_t)&cnl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa328), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa329), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa32a), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa32b), .driver_data = (kernel_ulong_t)&cnl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa347), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa368), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa369), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa36a), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa36b), .driver_data = (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa37b), .driver_data = (kernel_ulong_t)&cnl_spi_info }, /* CML-V */ - { PCI_VDEVICE(INTEL, 0xa3a7), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa3a8), (kernel_ulong_t)&spt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_spi_info }, - { PCI_VDEVICE(INTEL, 0xa3e0), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa3e1), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa3e2), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa3e3), (kernel_ulong_t)&spt_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa3e6), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa3a7), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa3a8), .driver_data = (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa3a9), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa3aa), .driver_data = (kernel_ulong_t)&spt_spi_info }, + { PCI_VDEVICE(INTEL, 0xa3e0), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa3e1), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa3e2), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa3e3), .driver_data = (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa3e6), .driver_data = (kernel_ulong_t)&spt_uart_info }, /* LNL-M */ - { PCI_VDEVICE(INTEL, 0xa825), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa826), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa827), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa830), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa846), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xa850), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa851), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa852), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xa878), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa879), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa87a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xa87b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa825), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa826), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa827), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa830), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa846), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xa850), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa851), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa852), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xa878), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa879), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa87a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa87b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* NVL-H */ - { PCI_VDEVICE(INTEL, 0xd325), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xd326), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xd327), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xd330), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xd347), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xd350), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xd351), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xd352), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xd378), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xd379), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xd37a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xd37b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd325), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd326), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd327), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd330), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd347), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd350), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd351), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd352), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd378), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd379), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd37a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd37b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* PTL-H */ - { PCI_VDEVICE(INTEL, 0xe325), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe326), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe327), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe330), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe346), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe350), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe351), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe352), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe378), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe379), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe37a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe37b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe325), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe326), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe327), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe330), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe346), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe350), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe351), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe352), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe378), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe379), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe37a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe37b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, /* PTL-P */ - { PCI_VDEVICE(INTEL, 0xe425), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe426), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe427), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe430), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe446), (kernel_ulong_t)&tgl_spi_info }, - { PCI_VDEVICE(INTEL, 0xe450), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe451), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe452), (kernel_ulong_t)&bxt_uart_info }, - { PCI_VDEVICE(INTEL, 0xe478), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe479), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe47a), (kernel_ulong_t)&ehl_i2c_info }, - { PCI_VDEVICE(INTEL, 0xe47b), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe425), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe426), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe427), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe430), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe446), .driver_data = (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xe450), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe451), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe452), .driver_data = (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xe478), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe479), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe47a), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xe47b), .driver_data = (kernel_ulong_t)&ehl_i2c_info }, { } }; MODULE_DEVICE_TABLE(pci, intel_lpss_pci_ids); diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 5a3d79f339dd..d2efcc92bb59 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -721,244 +721,244 @@ static struct lpc_ich_info lpc_chipset_info[] = { * functions that probably will be registered by other drivers. */ static const struct pci_device_id lpc_ich_ids[] = { - { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL}, - { PCI_VDEVICE(INTEL, 0x19dc), LPC_DNV}, - { PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD}, - { PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM}, - { PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT}, - { PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG}, - { PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG}, - { PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT}, - { PCI_VDEVICE(INTEL, 0x1f38), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x229c), LPC_BRASWELL}, - { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC}, - { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO}, - { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH}, - { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0}, - { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, - { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M}, - { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH}, - { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3}, - { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M}, - { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4}, - { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M}, - { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5}, - { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB}, - { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6}, - { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M}, - { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W}, - { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH}, - { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7}, - { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M}, - { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10}, - { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH}, - { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8}, - { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME}, - { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH}, - { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO}, - { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M}, - { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH}, - { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO}, - { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R}, - { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, - { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, - { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, - { PCI_VDEVICE(INTEL, 0x2b9c), LPC_COUGARMOUNTAIN}, - { PCI_VDEVICE(INTEL, 0x3197), LPC_GLK}, - { PCI_VDEVICE(INTEL, 0x31e8), LPC_GLK}, - { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, - { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, - { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, - { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D}, - { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH}, - { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM}, - { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55}, - { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55}, - { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55}, - { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57}, - { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57}, - { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55}, - { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57}, - { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57}, - { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF}, - { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57}, - { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400}, - { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, - { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, - { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, - { PCI_VDEVICE(INTEL, 0x5ae8), LPC_APL}, - { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x8cc1), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc2), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc3), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc4), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc6), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x9c40), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c41), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c42), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c43), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c44), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc3), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc5), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0xa1c1), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c2), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c3), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c4), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c5), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c6), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa1c7), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa242), LPC_LEWISBURG}, - { PCI_VDEVICE(INTEL, 0xa243), LPC_LEWISBURG}, - { 0, }, /* End of list */ + { PCI_VDEVICE(INTEL, 0x0f1c), .driver_data = LPC_BAYTRAIL }, + { PCI_VDEVICE(INTEL, 0x19dc), .driver_data = LPC_DNV }, + { PCI_VDEVICE(INTEL, 0x1c41), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c42), .driver_data = LPC_CPTD }, + { PCI_VDEVICE(INTEL, 0x1c43), .driver_data = LPC_CPTM }, + { PCI_VDEVICE(INTEL, 0x1c44), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c45), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c46), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c47), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c48), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c49), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4a), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4b), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4c), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4d), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4e), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c4f), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c50), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c51), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c52), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c53), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c54), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c55), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c56), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c57), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c58), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c59), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5a), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5b), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5c), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5d), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5e), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1c5f), .driver_data = LPC_CPT }, + { PCI_VDEVICE(INTEL, 0x1d40), .driver_data = LPC_PBG }, + { PCI_VDEVICE(INTEL, 0x1d41), .driver_data = LPC_PBG }, + { PCI_VDEVICE(INTEL, 0x1e40), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e41), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e42), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e43), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e44), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e45), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e46), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e47), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e48), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e49), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4a), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4b), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4c), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4d), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4e), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e4f), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e50), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e51), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e52), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e53), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e54), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e55), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e56), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e57), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e58), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e59), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5a), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5b), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5c), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5d), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5e), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1e5f), .driver_data = LPC_PPT }, + { PCI_VDEVICE(INTEL, 0x1f38), .driver_data = LPC_AVN }, + { PCI_VDEVICE(INTEL, 0x1f39), .driver_data = LPC_AVN }, + { PCI_VDEVICE(INTEL, 0x1f3a), .driver_data = LPC_AVN }, + { PCI_VDEVICE(INTEL, 0x1f3b), .driver_data = LPC_AVN }, + { PCI_VDEVICE(INTEL, 0x229c), .driver_data = LPC_BRASWELL }, + { PCI_VDEVICE(INTEL, 0x2310), .driver_data = LPC_DH89XXCC }, + { PCI_VDEVICE(INTEL, 0x2390), .driver_data = LPC_COLETO }, + { PCI_VDEVICE(INTEL, 0x2410), .driver_data = LPC_ICH }, + { PCI_VDEVICE(INTEL, 0x2420), .driver_data = LPC_ICH0 }, + { PCI_VDEVICE(INTEL, 0x2440), .driver_data = LPC_ICH2 }, + { PCI_VDEVICE(INTEL, 0x244c), .driver_data = LPC_ICH2M }, + { PCI_VDEVICE(INTEL, 0x2450), .driver_data = LPC_CICH }, + { PCI_VDEVICE(INTEL, 0x2480), .driver_data = LPC_ICH3 }, + { PCI_VDEVICE(INTEL, 0x248c), .driver_data = LPC_ICH3M }, + { PCI_VDEVICE(INTEL, 0x24c0), .driver_data = LPC_ICH4 }, + { PCI_VDEVICE(INTEL, 0x24cc), .driver_data = LPC_ICH4M }, + { PCI_VDEVICE(INTEL, 0x24d0), .driver_data = LPC_ICH5 }, + { PCI_VDEVICE(INTEL, 0x25a1), .driver_data = LPC_6300ESB }, + { PCI_VDEVICE(INTEL, 0x2640), .driver_data = LPC_ICH6 }, + { PCI_VDEVICE(INTEL, 0x2641), .driver_data = LPC_ICH6M }, + { PCI_VDEVICE(INTEL, 0x2642), .driver_data = LPC_ICH6W }, + { PCI_VDEVICE(INTEL, 0x2670), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2671), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2672), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2673), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2674), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2675), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2676), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2677), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2678), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x2679), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267a), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267b), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267c), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267d), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267e), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x267f), .driver_data = LPC_631XESB }, + { PCI_VDEVICE(INTEL, 0x27b0), .driver_data = LPC_ICH7DH }, + { PCI_VDEVICE(INTEL, 0x27b8), .driver_data = LPC_ICH7 }, + { PCI_VDEVICE(INTEL, 0x27b9), .driver_data = LPC_ICH7M }, + { PCI_VDEVICE(INTEL, 0x27bc), .driver_data = LPC_NM10 }, + { PCI_VDEVICE(INTEL, 0x27bd), .driver_data = LPC_ICH7MDH }, + { PCI_VDEVICE(INTEL, 0x2810), .driver_data = LPC_ICH8 }, + { PCI_VDEVICE(INTEL, 0x2811), .driver_data = LPC_ICH8ME }, + { PCI_VDEVICE(INTEL, 0x2812), .driver_data = LPC_ICH8DH }, + { PCI_VDEVICE(INTEL, 0x2814), .driver_data = LPC_ICH8DO }, + { PCI_VDEVICE(INTEL, 0x2815), .driver_data = LPC_ICH8M }, + { PCI_VDEVICE(INTEL, 0x2912), .driver_data = LPC_ICH9DH }, + { PCI_VDEVICE(INTEL, 0x2914), .driver_data = LPC_ICH9DO }, + { PCI_VDEVICE(INTEL, 0x2916), .driver_data = LPC_ICH9R }, + { PCI_VDEVICE(INTEL, 0x2917), .driver_data = LPC_ICH9ME }, + { PCI_VDEVICE(INTEL, 0x2918), .driver_data = LPC_ICH9 }, + { PCI_VDEVICE(INTEL, 0x2919), .driver_data = LPC_ICH9M }, + { PCI_VDEVICE(INTEL, 0x2b9c), .driver_data = LPC_COUGARMOUNTAIN }, + { PCI_VDEVICE(INTEL, 0x3197), .driver_data = LPC_GLK }, + { PCI_VDEVICE(INTEL, 0x31e8), .driver_data = LPC_GLK }, + { PCI_VDEVICE(INTEL, 0x3a14), .driver_data = LPC_ICH10DO }, + { PCI_VDEVICE(INTEL, 0x3a16), .driver_data = LPC_ICH10R }, + { PCI_VDEVICE(INTEL, 0x3a18), .driver_data = LPC_ICH10 }, + { PCI_VDEVICE(INTEL, 0x3a1a), .driver_data = LPC_ICH10D }, + { PCI_VDEVICE(INTEL, 0x3b00), .driver_data = LPC_PCH }, + { PCI_VDEVICE(INTEL, 0x3b01), .driver_data = LPC_PCHM }, + { PCI_VDEVICE(INTEL, 0x3b02), .driver_data = LPC_P55 }, + { PCI_VDEVICE(INTEL, 0x3b03), .driver_data = LPC_PM55 }, + { PCI_VDEVICE(INTEL, 0x3b06), .driver_data = LPC_H55 }, + { PCI_VDEVICE(INTEL, 0x3b07), .driver_data = LPC_QM57 }, + { PCI_VDEVICE(INTEL, 0x3b08), .driver_data = LPC_H57 }, + { PCI_VDEVICE(INTEL, 0x3b09), .driver_data = LPC_HM55 }, + { PCI_VDEVICE(INTEL, 0x3b0a), .driver_data = LPC_Q57 }, + { PCI_VDEVICE(INTEL, 0x3b0b), .driver_data = LPC_HM57 }, + { PCI_VDEVICE(INTEL, 0x3b0d), .driver_data = LPC_PCHMSFF }, + { PCI_VDEVICE(INTEL, 0x3b0f), .driver_data = LPC_QS57 }, + { PCI_VDEVICE(INTEL, 0x3b12), .driver_data = LPC_3400 }, + { PCI_VDEVICE(INTEL, 0x3b14), .driver_data = LPC_3420 }, + { PCI_VDEVICE(INTEL, 0x3b16), .driver_data = LPC_3450 }, + { PCI_VDEVICE(INTEL, 0x5031), .driver_data = LPC_EP80579 }, + { PCI_VDEVICE(INTEL, 0x5ae8), .driver_data = LPC_APL }, + { PCI_VDEVICE(INTEL, 0x8c40), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c41), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c42), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c43), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c44), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c45), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c46), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c47), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c48), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c49), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4a), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4b), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4c), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4d), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4e), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c4f), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c50), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c51), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c52), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c53), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c54), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c55), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c56), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c57), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c58), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c59), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5a), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5b), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5c), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5d), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5e), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8c5f), .driver_data = LPC_LPT }, + { PCI_VDEVICE(INTEL, 0x8cc1), .driver_data = LPC_9S }, + { PCI_VDEVICE(INTEL, 0x8cc2), .driver_data = LPC_9S }, + { PCI_VDEVICE(INTEL, 0x8cc3), .driver_data = LPC_9S }, + { PCI_VDEVICE(INTEL, 0x8cc4), .driver_data = LPC_9S }, + { PCI_VDEVICE(INTEL, 0x8cc6), .driver_data = LPC_9S }, + { PCI_VDEVICE(INTEL, 0x8d40), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d41), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d42), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d43), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d44), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d45), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d46), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d47), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d48), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d49), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4a), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4b), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4c), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4d), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4e), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d4f), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d50), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d51), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d52), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d53), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d54), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d55), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d56), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d57), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d58), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d59), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5a), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5b), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5c), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5d), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5e), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x8d5f), .driver_data = LPC_WBG }, + { PCI_VDEVICE(INTEL, 0x9c40), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c41), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c42), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c43), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c44), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c45), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c46), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9c47), .driver_data = LPC_LPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc1), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc2), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc3), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc5), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc6), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc7), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0x9cc9), .driver_data = LPC_WPT_LP }, + { PCI_VDEVICE(INTEL, 0xa1c1), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c2), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c3), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c4), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c5), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c6), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa1c7), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa242), .driver_data = LPC_LEWISBURG }, + { PCI_VDEVICE(INTEL, 0xa243), .driver_data = LPC_LEWISBURG }, + { }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, lpc_ich_ids); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 9ab9adce06fd..d069165f9b86 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -64,11 +64,11 @@ static struct lpc_sch_info sch_chipset_info[] = { }; static const struct pci_device_id lpc_sch_ids[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 }, - { 0, } + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), .driver_data = LPC_SCH }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), .driver_data = LPC_ITC }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), .driver_data = LPC_CENTERTON }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), .driver_data = LPC_QUARK_X1000 }, + { } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 8276456b142f..b5bda477ebfc 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1640,8 +1640,8 @@ static void sm501_plat_remove(struct platform_device *dev) } static const struct pci_device_id sm501_pci_tbl[] = { - { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, }, + { PCI_DEVICE(0x126f, 0x0501) }, + { }, }; MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); From 4b51f0da731adb1d3468518dc2deb5dae83978b7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Apr 2026 14:23:30 +0200 Subject: [PATCH 3900/5214] mfd: cs5535: Add, assign and expose the software node for the GPIO cell There are board files in-tree that want to request GPIOs from this chip. They currently rely on the GPIO core's mechanism of matching software nodes' labels against GPIO controller names. We want to remove this behavior from the kernel and to this end, we need to associate the referenced GPIO controller with its target software node. Create a dedicated GPIO software node for cs5535, assign it to the GPIO cell and expose its address in a new header. We only expose a single software node instance but that's alright: all existing hardware only contains a single cs5535 companion and the geode board file for which we expose this is legacy anyway. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260429-cs5535-swnode-v1-1-2bc5e17ddcf9@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/cs5535-mfd.c | 9 +++++++++ include/linux/mfd/cs5535.h | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 include/linux/mfd/cs5535.h diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index d0fb2e52ee76..f3becbef19f5 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -12,8 +12,11 @@ #include #include +#include #include #include +#include + #include #define DRV_NAME "cs5535-mfd" @@ -29,6 +32,11 @@ enum cs5535_mfd_bars { static struct resource cs5535_mfd_resources[NR_BARS]; +const struct software_node cs5535_gpio_swnode = { + .name = "cs5535-gpio", +}; +EXPORT_SYMBOL_NS(cs5535_gpio_swnode, "CS5535"); + static struct mfd_cell cs5535_mfd_cells[] = { { .name = "cs5535-smb", @@ -39,6 +47,7 @@ static struct mfd_cell cs5535_mfd_cells[] = { .name = "cs5535-gpio", .num_resources = 1, .resources = &cs5535_mfd_resources[GPIO_BAR], + .swnode = &cs5535_gpio_swnode, }, { .name = "cs5535-mfgpt", diff --git a/include/linux/mfd/cs5535.h b/include/linux/mfd/cs5535.h new file mode 100644 index 000000000000..2e4ebf5d06af --- /dev/null +++ b/include/linux/mfd/cs5535.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __MFD_CS5535_H__ +#define __MFD_CS5535_H__ + +extern const struct software_node cs5535_gpio_swnode; + +#endif /* __MFD_CS5535_H__ */ From 5618127fed7e8f974b3615d429ffdea670b203be Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Apr 2026 16:34:27 +0200 Subject: [PATCH 3901/5214] mfd: wm8994: Remove dead legacy-gpio code The old-style gpio handling in wm8994 came from a commit 7c8844481a1c ("mfd: wm8994: Emulate level triggered interrupts if required") in linux-3.11, but nothing in the kernel ever set the 'irq_gpio' member in the wm8994_pdata structure, so this was always dead code. Remove it now to reduce the dependency on the legacy gpio interfaces. Signed-off-by: Arnd Bergmann Reviewed-by: Bartosz Golaszewski Reviewed-by: Charles Keepax Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260427143437.3059210-1-arnd@kernel.org Signed-off-by: Lee Jones --- drivers/mfd/wm8994-irq.c | 94 ++------------------------------ include/linux/mfd/wm8994/pdata.h | 5 -- 2 files changed, 4 insertions(+), 95 deletions(-) diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 1475b1ac6983..a46cea948763 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -135,53 +135,9 @@ static const struct regmap_irq_chip wm8994_irq_chip = { .runtime_pm = true, }; -static void wm8994_edge_irq_enable(struct irq_data *data) -{ -} - -static void wm8994_edge_irq_disable(struct irq_data *data) -{ -} - -static struct irq_chip wm8994_edge_irq_chip = { - .name = "wm8994_edge", - .irq_disable = wm8994_edge_irq_disable, - .irq_enable = wm8994_edge_irq_enable, -}; - -static irqreturn_t wm8994_edge_irq(int irq, void *data) -{ - struct wm8994 *wm8994 = data; - - while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio)) - handle_nested_irq(irq_find_mapping(wm8994->edge_irq, 0)); - - return IRQ_HANDLED; -} - -static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct wm8994 *wm8994 = h->host_data; - - irq_set_chip_data(virq, wm8994); - irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq); - irq_set_nested_thread(virq, 1); - irq_set_noprobe(virq); - - return 0; -} - -static const struct irq_domain_ops wm8994_edge_irq_ops = { - .map = wm8994_edge_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - int wm8994_irq_init(struct wm8994 *wm8994) { int ret; - unsigned long irqflags; - struct wm8994_pdata *pdata = &wm8994->pdata; if (!wm8994->irq) { dev_warn(wm8994->dev, @@ -190,53 +146,11 @@ int wm8994_irq_init(struct wm8994 *wm8994) return 0; } - /* select user or default irq flags */ - irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; - if (pdata->irq_flags) - irqflags = pdata->irq_flags; - /* use a GPIO for edge triggered controllers */ - if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) { - dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n", - wm8994->irq, pdata->irq_gpio, - gpio_to_irq(pdata->irq_gpio)); - wm8994->irq = gpio_to_irq(pdata->irq_gpio); - } - - ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio, - GPIOF_IN, "WM8994 IRQ"); - - if (ret != 0) { - dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n", - ret); - return ret; - } - - wm8994->edge_irq = irq_domain_create_linear(NULL, 1, &wm8994_edge_irq_ops, wm8994); - - ret = regmap_add_irq_chip(wm8994->regmap, - irq_create_mapping(wm8994->edge_irq, - 0), - IRQF_ONESHOT, - wm8994->irq_base, &wm8994_irq_chip, - &wm8994->irq_data); - if (ret != 0) { - dev_err(wm8994->dev, "Failed to get IRQ: %d\n", - ret); - return ret; - } - - ret = request_threaded_irq(wm8994->irq, - NULL, wm8994_edge_irq, - irqflags, - "WM8994 edge", wm8994); - } else { - ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, - irqflags, - wm8994->irq_base, &wm8994_irq_chip, - &wm8994->irq_data); - } + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + wm8994->irq_base, &wm8994_irq_chip, + &wm8994->irq_data); if (ret != 0) { dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 6e2962ef5b81..b95a56a338c3 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -226,11 +226,6 @@ struct wm8994_pdata { * lines is mastered. */ int max_channels_clocked[WM8994_NUM_AIF]; - - /** - * GPIO for the IRQ pin if host only supports edge triggering - */ - int irq_gpio; }; #endif From 7b7b7fa42d08087a9d5f3d49453dbe721cbbd2d4 Mon Sep 17 00:00:00 2001 From: Amit Sunil Dhamne Date: Mon, 4 May 2026 22:49:54 +0000 Subject: [PATCH 3902/5214] mfd: max77759: Improve static struct formatting and commentary Improve code style. This includes the following: - Formatting the max77759_chgr_irqs entries to fit in a single line instead of breaking them into multiple lines to improve readability. - Refactoring comments such that they're full sentences and have punctuation marks for a couple of macro definitions to adhere to the documentation style. - Explicitly initializing `MAX77759_CHGR_MODE_OFF`. Signed-off-by: Amit Sunil Dhamne Link: https://patch.msgid.link/20260504-fix-mfd-max77759-driver-v1-1-4d4a31a1d214@google.com Signed-off-by: Lee Jones --- drivers/mfd/max77759.c | 48 ++++++++++++------------------------ include/linux/mfd/max77759.h | 6 ++--- 2 files changed, 19 insertions(+), 35 deletions(-) diff --git a/drivers/mfd/max77759.c b/drivers/mfd/max77759.c index 9fa6027a92c4..b50433e7b3d3 100644 --- a/drivers/mfd/max77759.c +++ b/drivers/mfd/max77759.c @@ -286,38 +286,22 @@ static const struct regmap_irq max77759_topsys_irqs[] = { }; static const struct regmap_irq max77759_chgr_irqs[] = { - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0, - MAX77759_CHGR_REG_CHG_INT_AICL), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0, - MAX77759_CHGR_REG_CHG_INT_CHGIN), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0, - MAX77759_CHGR_REG_CHG_INT_WCIN), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0, - MAX77759_CHGR_REG_CHG_INT_CHG), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0, - MAX77759_CHGR_REG_CHG_INT_BAT), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0, - MAX77759_CHGR_REG_CHG_INT_INLIM), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0, - MAX77759_CHGR_REG_CHG_INT_THM2), - REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0, - MAX77759_CHGR_REG_CHG_INT_BYP), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1, - MAX77759_CHGR_REG_CHG_INT2_INSEL), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1, - MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1, - MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1, - MAX77759_CHGR_REG_CHG_INT2_BAT_OILO), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1, - MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1, - MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1, - MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO), - REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1, - MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0, MAX77759_CHGR_REG_CHG_INT_AICL), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0, MAX77759_CHGR_REG_CHG_INT_CHGIN), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0, MAX77759_CHGR_REG_CHG_INT_WCIN), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0, MAX77759_CHGR_REG_CHG_INT_CHG), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0, MAX77759_CHGR_REG_CHG_INT_BAT), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0, MAX77759_CHGR_REG_CHG_INT_INLIM), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0, MAX77759_CHGR_REG_CHG_INT_THM2), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0, MAX77759_CHGR_REG_CHG_INT_BYP), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1, MAX77759_CHGR_REG_CHG_INT2_INSEL), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1, MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1, MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1, MAX77759_CHGR_REG_CHG_INT2_BAT_OILO), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1, MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE), }; static const struct regmap_irq_chip max77759_pmic_irq_chip = { diff --git a/include/linux/mfd/max77759.h b/include/linux/mfd/max77759.h index ec19be952877..7c0b13219d51 100644 --- a/include/linux/mfd/max77759.h +++ b/include/linux/mfd/max77759.h @@ -106,9 +106,9 @@ #define MAX77759_CHGR_REG_CHG_CNFG_10 0xc3 #define MAX77759_CHGR_REG_CHG_CNFG_11 0xc4 #define MAX77759_CHGR_REG_CHG_CNFG_12 0xc5 -/* Wireless Charging input channel select */ +/* Setting this enables the Wireless Charging input channel. */ #define MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL BIT(6) -/* CHGIN/USB input channel select */ +/* Setting this enables the CHGIN/USB input channel. */ #define MAX77759_CHGR_REG_CHG_CNFG_12_CHGINSEL BIT(5) #define MAX77759_CHGR_REG_CHG_CNFG_13 0xc6 #define MAX77759_CHGR_REG_CHG_CNFG_14 0xc7 @@ -204,7 +204,7 @@ enum max77759_chgr_chg_dtls_states { }; enum max77759_chgr_mode { - MAX77759_CHGR_MODE_OFF, + MAX77759_CHGR_MODE_OFF = 0x0, MAX77759_CHGR_MODE_CHG_BUCK_ON = 0x5, MAX77759_CHGR_MODE_OTG_BOOST_ON = 0xA, }; From 484c67469d15d80bc466fdec7fc85caaadd4b08e Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 4 May 2026 16:11:22 +0800 Subject: [PATCH 3903/5214] dt-bindings: mfd: qcom,tcsr: Add compatible for Nord Document Top Control and Status Register (TCSR) controller on Qualcomm Nord SoC. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260504081122.825635-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml index 14ae3f00ef7e..23317d1b381c 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml @@ -19,6 +19,7 @@ properties: - enum: - qcom,msm8976-tcsr - qcom,msm8998-tcsr + - qcom,nord-tcsr - qcom,qcm2290-tcsr - qcom,qcs404-tcsr - qcom,qcs615-tcsr From d11880e32e7b3c702ac5cd7c415543a743c3db85 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 28 Apr 2026 18:58:01 +0200 Subject: [PATCH 3904/5214] mfd: menf21bmc: Inline i2c_check_functionality check Inline the i2c_check_functionality() check, since the function returns a boolean status rather than an error code. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260428165800.590496-3-thorsten.blum@linux.dev Signed-off-by: Lee Jones --- drivers/mfd/menf21bmc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c index 1d36095155e0..0f24de516d72 100644 --- a/drivers/mfd/menf21bmc.c +++ b/drivers/mfd/menf21bmc.c @@ -54,11 +54,9 @@ menf21bmc_probe(struct i2c_client *client) int rev_major, rev_minor, rev_main; int ret; - ret = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BYTE); - if (!ret) + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BYTE)) return -ENODEV; rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR); From 7ee4fa2674e51a0d100b9240cd8a50355141362f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 5 May 2026 13:45:43 +0200 Subject: [PATCH 3905/5214] mfd: twl-core: Use i2c_check_functionality as boolean value i2c_check_functionality() returns a boolean status rather than an error code - directly use it as a boolean value. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260505114543.159381-2-thorsten.blum@linux.dev Signed-off-by: Lee Jones --- drivers/mfd/twl-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index f89eda4a17fe..c024a28b057e 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -754,7 +754,7 @@ twl_probe(struct i2c_client *client) return status; } - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_dbg(&client->dev, "can't talk I2C?\n"); status = -EIO; goto free; From e6f0018fbf1f4eabd3fb8ac71f06cb31efce3c86 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 30 Apr 2026 18:28:05 +0200 Subject: [PATCH 3906/5214] mfd: ezx-pcap: Remove unused driver Support for the Motorola EZX phones based on Intel PXA processors was removed in 2022, but this driver remained present in the tree. As far as I can tell, the support was never quite functional upstream because the board files did not actually instantiate the SPI device for the PCAP. There are still also drivers for the various mfd cells: keys, touchscreen, regulator and rtc, all of which are obviously orphaned as well but can be removed separately as the Kconfig dependency now prevents them from being enabled. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604301209.f1YXTsIr-lkp@intel.com/ Signed-off-by: Arnd Bergmann Reviewed-by: Linus Walleij Acked-by: Harald Welte Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260430162855.2029285-1-arnd@kernel.org Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 7 - drivers/mfd/Makefile | 1 - drivers/mfd/ezx-pcap.c | 491 ----------------------------------- include/linux/mfd/ezx-pcap.h | 253 ------------------ 4 files changed, 752 deletions(-) delete mode 100644 drivers/mfd/ezx-pcap.c delete mode 100644 include/linux/mfd/ezx-pcap.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268..d08d70d41bce 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1199,13 +1199,6 @@ config MFD_OCELOT If unsure, say N. -config EZX_PCAP - bool "Motorola EZXPCAP Support" - depends on SPI_MASTER - help - This enables the PCAP ASIC present on EZX Phones. This is - needed for MMC, TouchScreen, Sound, USB, etc.. - config MFD_CPCAP tristate "Support for Motorola CPCAP" depends on SPI diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28a..dd4bb7e77c33 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -131,7 +131,6 @@ obj-$(CONFIG_MFD_CORE) += mfd-core.o ocelot-soc-objs := ocelot-core.o ocelot-spi.o obj-$(CONFIG_MFD_OCELOT) += ocelot-soc.o -obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o obj-$(CONFIG_MCP) += mcp-core.o diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c deleted file mode 100644 index 9a685ff8cd15..000000000000 --- a/drivers/mfd/ezx-pcap.c +++ /dev/null @@ -1,491 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for Motorola PCAP2 as present in EZX phones - * - * Copyright (C) 2006 Harald Welte - * Copyright (C) 2009 Daniel Ribeiro - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PCAP_ADC_MAXQ 8 -struct pcap_adc_request { - u8 bank; - u8 ch[2]; - u32 flags; - void (*callback)(void *, u16[]); - void *data; -}; - -struct pcap_chip { - struct spi_device *spi; - - /* IO */ - u32 buf; - spinlock_t io_lock; - - /* IRQ */ - unsigned int irq_base; - u32 msr; - struct work_struct isr_work; - struct work_struct msr_work; - struct workqueue_struct *workqueue; - - /* ADC */ - struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ]; - u8 adc_head; - u8 adc_tail; - spinlock_t adc_lock; -}; - -/* IO */ -static int ezx_pcap_putget(struct pcap_chip *pcap, u32 *data) -{ - struct spi_transfer t; - struct spi_message m; - int status; - - memset(&t, 0, sizeof(t)); - spi_message_init(&m); - t.len = sizeof(u32); - spi_message_add_tail(&t, &m); - - pcap->buf = *data; - t.tx_buf = (u8 *) &pcap->buf; - t.rx_buf = (u8 *) &pcap->buf; - status = spi_sync(pcap->spi, &m); - - if (status == 0) - *data = pcap->buf; - - return status; -} - -int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&pcap->io_lock, flags); - value &= PCAP_REGISTER_VALUE_MASK; - value |= PCAP_REGISTER_WRITE_OP_BIT - | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); - ret = ezx_pcap_putget(pcap, &value); - spin_unlock_irqrestore(&pcap->io_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(ezx_pcap_write); - -int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&pcap->io_lock, flags); - *value = PCAP_REGISTER_READ_OP_BIT - | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); - - ret = ezx_pcap_putget(pcap, value); - spin_unlock_irqrestore(&pcap->io_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(ezx_pcap_read); - -int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val) -{ - unsigned long flags; - int ret; - u32 tmp = PCAP_REGISTER_READ_OP_BIT | - (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); - - spin_lock_irqsave(&pcap->io_lock, flags); - ret = ezx_pcap_putget(pcap, &tmp); - if (ret) - goto out_unlock; - - tmp &= (PCAP_REGISTER_VALUE_MASK & ~mask); - tmp |= (val & mask) | PCAP_REGISTER_WRITE_OP_BIT | - (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); - - ret = ezx_pcap_putget(pcap, &tmp); -out_unlock: - spin_unlock_irqrestore(&pcap->io_lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(ezx_pcap_set_bits); - -/* IRQ */ -int irq_to_pcap(struct pcap_chip *pcap, int irq) -{ - return irq - pcap->irq_base; -} -EXPORT_SYMBOL_GPL(irq_to_pcap); - -int pcap_to_irq(struct pcap_chip *pcap, int irq) -{ - return pcap->irq_base + irq; -} -EXPORT_SYMBOL_GPL(pcap_to_irq); - -static void pcap_mask_irq(struct irq_data *d) -{ - struct pcap_chip *pcap = irq_data_get_irq_chip_data(d); - - pcap->msr |= 1 << irq_to_pcap(pcap, d->irq); - queue_work(pcap->workqueue, &pcap->msr_work); -} - -static void pcap_unmask_irq(struct irq_data *d) -{ - struct pcap_chip *pcap = irq_data_get_irq_chip_data(d); - - pcap->msr &= ~(1 << irq_to_pcap(pcap, d->irq)); - queue_work(pcap->workqueue, &pcap->msr_work); -} - -static struct irq_chip pcap_irq_chip = { - .name = "pcap", - .irq_disable = pcap_mask_irq, - .irq_mask = pcap_mask_irq, - .irq_unmask = pcap_unmask_irq, -}; - -static void pcap_msr_work(struct work_struct *work) -{ - struct pcap_chip *pcap = container_of(work, struct pcap_chip, msr_work); - - ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); -} - -static void pcap_isr_work(struct work_struct *work) -{ - struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work); - struct pcap_platform_data *pdata = dev_get_platdata(&pcap->spi->dev); - u32 msr, isr, int_sel, service; - int irq; - - do { - ezx_pcap_read(pcap, PCAP_REG_MSR, &msr); - ezx_pcap_read(pcap, PCAP_REG_ISR, &isr); - - /* We can't service/ack irqs that are assigned to port 2 */ - if (!(pdata->config & PCAP_SECOND_PORT)) { - ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel); - isr &= ~int_sel; - } - - ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr); - ezx_pcap_write(pcap, PCAP_REG_ISR, isr); - - service = isr & ~msr; - for (irq = pcap->irq_base; service; service >>= 1, irq++) { - if (service & 1) - generic_handle_irq_safe(irq); - } - ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); - } while (gpio_get_value(pdata->gpio)); -} - -static void pcap_irq_handler(struct irq_desc *desc) -{ - struct pcap_chip *pcap = irq_desc_get_handler_data(desc); - - desc->irq_data.chip->irq_ack(&desc->irq_data); - queue_work(pcap->workqueue, &pcap->isr_work); -} - -/* ADC */ -void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits) -{ - unsigned long flags; - u32 tmp; - - spin_lock_irqsave(&pcap->adc_lock, flags); - ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); - tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR); - tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR); - ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); - spin_unlock_irqrestore(&pcap->adc_lock, flags); -} -EXPORT_SYMBOL_GPL(pcap_set_ts_bits); - -static void pcap_disable_adc(struct pcap_chip *pcap) -{ - u32 tmp; - - ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); - tmp &= ~(PCAP_ADC_ADEN|PCAP_ADC_BATT_I_ADC|PCAP_ADC_BATT_I_POLARITY); - ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); -} - -static void pcap_adc_trigger(struct pcap_chip *pcap) -{ - unsigned long flags; - u32 tmp; - u8 head; - - spin_lock_irqsave(&pcap->adc_lock, flags); - head = pcap->adc_head; - if (!pcap->adc_queue[head]) { - /* queue is empty, save power */ - pcap_disable_adc(pcap); - spin_unlock_irqrestore(&pcap->adc_lock, flags); - return; - } - /* start conversion on requested bank, save TS_M bits */ - ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); - tmp &= (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR); - tmp |= pcap->adc_queue[head]->flags | PCAP_ADC_ADEN; - - if (pcap->adc_queue[head]->bank == PCAP_ADC_BANK_1) - tmp |= PCAP_ADC_AD_SEL1; - - ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); - spin_unlock_irqrestore(&pcap->adc_lock, flags); - ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC); -} - -static irqreturn_t pcap_adc_irq(int irq, void *_pcap) -{ - struct pcap_chip *pcap = _pcap; - struct pcap_adc_request *req; - u16 res[2]; - u32 tmp; - - spin_lock(&pcap->adc_lock); - req = pcap->adc_queue[pcap->adc_head]; - - if (WARN(!req, "adc irq without pending request\n")) { - spin_unlock(&pcap->adc_lock); - return IRQ_HANDLED; - } - - /* read requested channels results */ - ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); - tmp &= ~(PCAP_ADC_ADA1_MASK | PCAP_ADC_ADA2_MASK); - tmp |= (req->ch[0] << PCAP_ADC_ADA1_SHIFT); - tmp |= (req->ch[1] << PCAP_ADC_ADA2_SHIFT); - ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); - ezx_pcap_read(pcap, PCAP_REG_ADR, &tmp); - res[0] = (tmp & PCAP_ADR_ADD1_MASK) >> PCAP_ADR_ADD1_SHIFT; - res[1] = (tmp & PCAP_ADR_ADD2_MASK) >> PCAP_ADR_ADD2_SHIFT; - - pcap->adc_queue[pcap->adc_head] = NULL; - pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1); - spin_unlock(&pcap->adc_lock); - - /* pass the results and release memory */ - req->callback(req->data, res); - kfree(req); - - /* trigger next conversion (if any) on queue */ - pcap_adc_trigger(pcap); - - return IRQ_HANDLED; -} - -int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[], - void *callback, void *data) -{ - struct pcap_adc_request *req; - unsigned long irq_flags; - - /* This will be freed after we have a result */ - req = kmalloc_obj(struct pcap_adc_request); - if (!req) - return -ENOMEM; - - req->bank = bank; - req->flags = flags; - req->ch[0] = ch[0]; - req->ch[1] = ch[1]; - req->callback = callback; - req->data = data; - - spin_lock_irqsave(&pcap->adc_lock, irq_flags); - if (pcap->adc_queue[pcap->adc_tail]) { - spin_unlock_irqrestore(&pcap->adc_lock, irq_flags); - kfree(req); - return -EBUSY; - } - pcap->adc_queue[pcap->adc_tail] = req; - pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1); - spin_unlock_irqrestore(&pcap->adc_lock, irq_flags); - - /* start conversion */ - pcap_adc_trigger(pcap); - - return 0; -} -EXPORT_SYMBOL_GPL(pcap_adc_async); - -/* subdevs */ -static int pcap_remove_subdev(struct device *dev, void *unused) -{ - platform_device_unregister(to_platform_device(dev)); - return 0; -} - -static int pcap_add_subdev(struct pcap_chip *pcap, - struct pcap_subdev *subdev) -{ - struct platform_device *pdev; - int ret; - - pdev = platform_device_alloc(subdev->name, subdev->id); - if (!pdev) - return -ENOMEM; - - pdev->dev.parent = &pcap->spi->dev; - pdev->dev.platform_data = subdev->platform_data; - - ret = platform_device_add(pdev); - if (ret) - platform_device_put(pdev); - - return ret; -} - -static void ezx_pcap_remove(struct spi_device *spi) -{ - struct pcap_chip *pcap = spi_get_drvdata(spi); - unsigned long flags; - int i; - - /* remove all registered subdevs */ - device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); - - /* cleanup ADC */ - spin_lock_irqsave(&pcap->adc_lock, flags); - for (i = 0; i < PCAP_ADC_MAXQ; i++) - kfree(pcap->adc_queue[i]); - spin_unlock_irqrestore(&pcap->adc_lock, flags); - - /* cleanup irqchip */ - for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) - irq_set_chip_and_handler(i, NULL, NULL); -} - -static int ezx_pcap_probe(struct spi_device *spi) -{ - struct pcap_platform_data *pdata = dev_get_platdata(&spi->dev); - struct pcap_chip *pcap; - int i, adc_irq; - int ret; - - /* platform data is required */ - if (!pdata) - return -ENODEV; - - pcap = devm_kzalloc(&spi->dev, sizeof(*pcap), GFP_KERNEL); - if (!pcap) - return -ENOMEM; - - spin_lock_init(&pcap->io_lock); - spin_lock_init(&pcap->adc_lock); - INIT_WORK(&pcap->isr_work, pcap_isr_work); - INIT_WORK(&pcap->msr_work, pcap_msr_work); - spi_set_drvdata(spi, pcap); - - /* setup spi */ - spi->bits_per_word = 32; - spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0); - ret = spi_setup(spi); - if (ret) - return ret; - - pcap->spi = spi; - - /* setup irq */ - pcap->irq_base = pdata->irq_base; - pcap->workqueue = devm_alloc_ordered_workqueue(&spi->dev, "pcapd", 0); - if (!pcap->workqueue) - return -ENOMEM; - - /* redirect interrupts to AP, except adcdone2 */ - if (!(pdata->config & PCAP_SECOND_PORT)) - ezx_pcap_write(pcap, PCAP_REG_INT_SEL, - (1 << PCAP_IRQ_ADCDONE2)); - - /* setup irq chip */ - for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) { - irq_set_chip_and_handler(i, &pcap_irq_chip, handle_simple_irq); - irq_set_chip_data(i, pcap); - irq_clear_status_flags(i, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - /* mask/ack all PCAP interrupts */ - ezx_pcap_write(pcap, PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT); - ezx_pcap_write(pcap, PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER); - pcap->msr = PCAP_MASK_ALL_INTERRUPT; - - irq_set_irq_type(spi->irq, IRQ_TYPE_EDGE_RISING); - irq_set_chained_handler_and_data(spi->irq, pcap_irq_handler, pcap); - irq_set_irq_wake(spi->irq, 1); - - /* ADC */ - adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? - PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); - - ret = devm_request_irq(&spi->dev, adc_irq, pcap_adc_irq, 0, "ADC", - pcap); - if (ret) - goto free_irqchip; - - /* setup subdevs */ - for (i = 0; i < pdata->num_subdevs; i++) { - ret = pcap_add_subdev(pcap, &pdata->subdevs[i]); - if (ret) - goto remove_subdevs; - } - - /* board specific quirks */ - if (pdata->init) - pdata->init(pcap); - - return 0; - -remove_subdevs: - device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); -free_irqchip: - for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) - irq_set_chip_and_handler(i, NULL, NULL); - - return ret; -} - -static struct spi_driver ezxpcap_driver = { - .probe = ezx_pcap_probe, - .remove = ezx_pcap_remove, - .driver = { - .name = "ezx-pcap", - }, -}; - -static int __init ezx_pcap_init(void) -{ - return spi_register_driver(&ezxpcap_driver); -} - -static void __exit ezx_pcap_exit(void) -{ - spi_unregister_driver(&ezxpcap_driver); -} - -subsys_initcall(ezx_pcap_init); -module_exit(ezx_pcap_exit); - -MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); -MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver"); -MODULE_ALIAS("spi:ezx-pcap"); diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h deleted file mode 100644 index ea51b1cdca5a..000000000000 --- a/include/linux/mfd/ezx-pcap.h +++ /dev/null @@ -1,253 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright 2009 Daniel Ribeiro - * - * For further information, please see http://wiki.openezx.org/PCAP2 - */ - -#ifndef EZX_PCAP_H -#define EZX_PCAP_H - -struct pcap_subdev { - int id; - const char *name; - void *platform_data; -}; - -struct pcap_platform_data { - unsigned int irq_base; - unsigned int config; - int gpio; - void (*init) (void *); /* board specific init */ - int num_subdevs; - struct pcap_subdev *subdevs; -}; - -struct pcap_chip; - -int ezx_pcap_write(struct pcap_chip *, u8, u32); -int ezx_pcap_read(struct pcap_chip *, u8, u32 *); -int ezx_pcap_set_bits(struct pcap_chip *, u8, u32, u32); -int pcap_to_irq(struct pcap_chip *, int); -int irq_to_pcap(struct pcap_chip *, int); -int pcap_adc_async(struct pcap_chip *, u8, u32, u8[], void *, void *); -void pcap_set_ts_bits(struct pcap_chip *, u32); - -#define PCAP_SECOND_PORT 1 -#define PCAP_CS_AH 2 - -#define PCAP_REGISTER_WRITE_OP_BIT 0x80000000 -#define PCAP_REGISTER_READ_OP_BIT 0x00000000 - -#define PCAP_REGISTER_VALUE_MASK 0x01ffffff -#define PCAP_REGISTER_ADDRESS_MASK 0x7c000000 -#define PCAP_REGISTER_ADDRESS_SHIFT 26 -#define PCAP_REGISTER_NUMBER 32 -#define PCAP_CLEAR_INTERRUPT_REGISTER 0x01ffffff -#define PCAP_MASK_ALL_INTERRUPT 0x01ffffff - -/* registers accessible by both pcap ports */ -#define PCAP_REG_ISR 0x0 /* Interrupt Status */ -#define PCAP_REG_MSR 0x1 /* Interrupt Mask */ -#define PCAP_REG_PSTAT 0x2 /* Processor Status */ -#define PCAP_REG_VREG2 0x6 /* Regulator Bank 2 Control */ -#define PCAP_REG_AUXVREG 0x7 /* Auxiliary Regulator Control */ -#define PCAP_REG_BATT 0x8 /* Battery Control */ -#define PCAP_REG_ADC 0x9 /* AD Control */ -#define PCAP_REG_ADR 0xa /* AD Result */ -#define PCAP_REG_CODEC 0xb /* Audio Codec Control */ -#define PCAP_REG_RX_AMPS 0xc /* RX Audio Amplifiers Control */ -#define PCAP_REG_ST_DAC 0xd /* Stereo DAC Control */ -#define PCAP_REG_BUSCTRL 0x14 /* Connectivity Control */ -#define PCAP_REG_PERIPH 0x15 /* Peripheral Control */ -#define PCAP_REG_LOWPWR 0x18 /* Regulator Low Power Control */ -#define PCAP_REG_TX_AMPS 0x1a /* TX Audio Amplifiers Control */ -#define PCAP_REG_GP 0x1b /* General Purpose */ -#define PCAP_REG_TEST1 0x1c -#define PCAP_REG_TEST2 0x1d -#define PCAP_REG_VENDOR_TEST1 0x1e -#define PCAP_REG_VENDOR_TEST2 0x1f - -/* registers accessible by pcap port 1 only (a1200, e2 & e6) */ -#define PCAP_REG_INT_SEL 0x3 /* Interrupt Select */ -#define PCAP_REG_SWCTRL 0x4 /* Switching Regulator Control */ -#define PCAP_REG_VREG1 0x5 /* Regulator Bank 1 Control */ -#define PCAP_REG_RTC_TOD 0xe /* RTC Time of Day */ -#define PCAP_REG_RTC_TODA 0xf /* RTC Time of Day Alarm */ -#define PCAP_REG_RTC_DAY 0x10 /* RTC Day */ -#define PCAP_REG_RTC_DAYA 0x11 /* RTC Day Alarm */ -#define PCAP_REG_MTRTMR 0x12 /* AD Monitor Timer */ -#define PCAP_REG_PWR 0x13 /* Power Control */ -#define PCAP_REG_AUXVREG_MASK 0x16 /* Auxiliary Regulator Mask */ -#define PCAP_REG_VENDOR_REV 0x17 -#define PCAP_REG_PERIPH_MASK 0x19 /* Peripheral Mask */ - -/* PCAP2 Interrupts */ -#define PCAP_NIRQS 23 -#define PCAP_IRQ_ADCDONE 0 /* ADC done port 1 */ -#define PCAP_IRQ_TS 1 /* Touch Screen */ -#define PCAP_IRQ_1HZ 2 /* 1HZ timer */ -#define PCAP_IRQ_WH 3 /* ADC above high limit */ -#define PCAP_IRQ_WL 4 /* ADC below low limit */ -#define PCAP_IRQ_TODA 5 /* Time of day alarm */ -#define PCAP_IRQ_USB4V 6 /* USB above 4V */ -#define PCAP_IRQ_ONOFF 7 /* On/Off button */ -#define PCAP_IRQ_ONOFF2 8 /* On/Off button 2 */ -#define PCAP_IRQ_USB1V 9 /* USB above 1V */ -#define PCAP_IRQ_MOBPORT 10 -#define PCAP_IRQ_MIC 11 /* Mic attach/HS button */ -#define PCAP_IRQ_HS 12 /* Headset attach */ -#define PCAP_IRQ_ST 13 -#define PCAP_IRQ_PC 14 /* Power Cut */ -#define PCAP_IRQ_WARM 15 -#define PCAP_IRQ_EOL 16 /* Battery End Of Life */ -#define PCAP_IRQ_CLK 17 -#define PCAP_IRQ_SYSRST 18 /* System Reset */ -#define PCAP_IRQ_DUMMY 19 -#define PCAP_IRQ_ADCDONE2 20 /* ADC done port 2 */ -#define PCAP_IRQ_SOFTRESET 21 -#define PCAP_IRQ_MNEXB 22 - -/* voltage regulators */ -#define V1 0 -#define V2 1 -#define V3 2 -#define V4 3 -#define V5 4 -#define V6 5 -#define V7 6 -#define V8 7 -#define V9 8 -#define V10 9 -#define VAUX1 10 -#define VAUX2 11 -#define VAUX3 12 -#define VAUX4 13 -#define VSIM 14 -#define VSIM2 15 -#define VVIB 16 -#define SW1 17 -#define SW2 18 -#define SW3 19 -#define SW1S 20 -#define SW2S 21 - -#define PCAP_BATT_DAC_MASK 0x000000ff -#define PCAP_BATT_DAC_SHIFT 0 -#define PCAP_BATT_B_FDBK (1 << 8) -#define PCAP_BATT_EXT_ISENSE (1 << 9) -#define PCAP_BATT_V_COIN_MASK 0x00003c00 -#define PCAP_BATT_V_COIN_SHIFT 10 -#define PCAP_BATT_I_COIN (1 << 14) -#define PCAP_BATT_COIN_CH_EN (1 << 15) -#define PCAP_BATT_EOL_SEL_MASK 0x000e0000 -#define PCAP_BATT_EOL_SEL_SHIFT 17 -#define PCAP_BATT_EOL_CMP_EN (1 << 20) -#define PCAP_BATT_BATT_DET_EN (1 << 21) -#define PCAP_BATT_THERMBIAS_CTRL (1 << 22) - -#define PCAP_ADC_ADEN (1 << 0) -#define PCAP_ADC_RAND (1 << 1) -#define PCAP_ADC_AD_SEL1 (1 << 2) -#define PCAP_ADC_AD_SEL2 (1 << 3) -#define PCAP_ADC_ADA1_MASK 0x00000070 -#define PCAP_ADC_ADA1_SHIFT 4 -#define PCAP_ADC_ADA2_MASK 0x00000380 -#define PCAP_ADC_ADA2_SHIFT 7 -#define PCAP_ADC_ATO_MASK 0x00003c00 -#define PCAP_ADC_ATO_SHIFT 10 -#define PCAP_ADC_ATOX (1 << 14) -#define PCAP_ADC_MTR1 (1 << 15) -#define PCAP_ADC_MTR2 (1 << 16) -#define PCAP_ADC_TS_M_MASK 0x000e0000 -#define PCAP_ADC_TS_M_SHIFT 17 -#define PCAP_ADC_TS_REF_LOWPWR (1 << 20) -#define PCAP_ADC_TS_REFENB (1 << 21) -#define PCAP_ADC_BATT_I_POLARITY (1 << 22) -#define PCAP_ADC_BATT_I_ADC (1 << 23) - -#define PCAP_ADC_BANK_0 0 -#define PCAP_ADC_BANK_1 1 -/* ADC bank 0 */ -#define PCAP_ADC_CH_COIN 0 -#define PCAP_ADC_CH_BATT 1 -#define PCAP_ADC_CH_BPLUS 2 -#define PCAP_ADC_CH_MOBPORTB 3 -#define PCAP_ADC_CH_TEMPERATURE 4 -#define PCAP_ADC_CH_CHARGER_ID 5 -#define PCAP_ADC_CH_AD6 6 -/* ADC bank 1 */ -#define PCAP_ADC_CH_AD7 0 -#define PCAP_ADC_CH_AD8 1 -#define PCAP_ADC_CH_AD9 2 -#define PCAP_ADC_CH_TS_X1 3 -#define PCAP_ADC_CH_TS_X2 4 -#define PCAP_ADC_CH_TS_Y1 5 -#define PCAP_ADC_CH_TS_Y2 6 - -#define PCAP_ADC_T_NOW 0 -#define PCAP_ADC_T_IN_BURST 1 -#define PCAP_ADC_T_OUT_BURST 2 - -#define PCAP_ADC_ATO_IN_BURST 6 -#define PCAP_ADC_ATO_OUT_BURST 0 - -#define PCAP_ADC_TS_M_XY 1 -#define PCAP_ADC_TS_M_PRESSURE 2 -#define PCAP_ADC_TS_M_PLATE_X 3 -#define PCAP_ADC_TS_M_PLATE_Y 4 -#define PCAP_ADC_TS_M_STANDBY 5 -#define PCAP_ADC_TS_M_NONTS 6 - -#define PCAP_ADR_ADD1_MASK 0x000003ff -#define PCAP_ADR_ADD1_SHIFT 0 -#define PCAP_ADR_ADD2_MASK 0x000ffc00 -#define PCAP_ADR_ADD2_SHIFT 10 -#define PCAP_ADR_ADINC1 (1 << 20) -#define PCAP_ADR_ADINC2 (1 << 21) -#define PCAP_ADR_ASC (1 << 22) -#define PCAP_ADR_ONESHOT (1 << 23) - -#define PCAP_BUSCTRL_FSENB (1 << 0) -#define PCAP_BUSCTRL_USB_SUSPEND (1 << 1) -#define PCAP_BUSCTRL_USB_PU (1 << 2) -#define PCAP_BUSCTRL_USB_PD (1 << 3) -#define PCAP_BUSCTRL_VUSB_EN (1 << 4) -#define PCAP_BUSCTRL_USB_PS (1 << 5) -#define PCAP_BUSCTRL_VUSB_MSTR_EN (1 << 6) -#define PCAP_BUSCTRL_VBUS_PD_ENB (1 << 7) -#define PCAP_BUSCTRL_CURRLIM (1 << 8) -#define PCAP_BUSCTRL_RS232ENB (1 << 9) -#define PCAP_BUSCTRL_RS232_DIR (1 << 10) -#define PCAP_BUSCTRL_SE0_CONN (1 << 11) -#define PCAP_BUSCTRL_USB_PDM (1 << 12) -#define PCAP_BUSCTRL_BUS_PRI_ADJ (1 << 24) - -/* leds */ -#define PCAP_LED0 0 -#define PCAP_LED1 1 -#define PCAP_BL0 2 -#define PCAP_BL1 3 -#define PCAP_LED_3MA 0 -#define PCAP_LED_4MA 1 -#define PCAP_LED_5MA 2 -#define PCAP_LED_9MA 3 -#define PCAP_LED_T_MASK 0xf -#define PCAP_LED_C_MASK 0x3 -#define PCAP_BL_MASK 0x1f -#define PCAP_BL0_SHIFT 0 -#define PCAP_LED0_EN (1 << 5) -#define PCAP_LED1_EN (1 << 6) -#define PCAP_LED0_T_SHIFT 7 -#define PCAP_LED1_T_SHIFT 11 -#define PCAP_LED0_C_SHIFT 15 -#define PCAP_LED1_C_SHIFT 17 -#define PCAP_BL1_SHIFT 20 - -/* RTC */ -#define PCAP_RTC_DAY_MASK 0x3fff -#define PCAP_RTC_TOD_MASK 0xffff -#define PCAP_RTC_PC_MASK 0x7 -#define SEC_PER_DAY 86400 - -#endif From 2611c4d623ba867e196a4146f1927c3c468ac2bc Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Tue, 28 Apr 2026 17:49:46 +0800 Subject: [PATCH 3907/5214] dt-bindings: mfd: aspeed,ast2x00-scu: Describe AST2700 SCU0 AST2700 consists of two interconnected SoC instances, each with its own System Control Unit (SCU). The SCU0 provides pin control, interrupt controllers, clocks, resets, and address-space mappings for the Secondary and Tertiary Service Processors (SSP and TSP). Describe the SSP/TSP address mappings using the standard memory-region and memory-region-names properties. Disallow legacy child nodes that are not present on AST2700, including p2a-control and smp-memram. The latter is unnecessary as software can access the scratch registers via the SCU syscon. Also allow the AST2700 SoC0 pin controller to be described as a child node of the SCU0, and add an example illustrating the SCU0 layout, including reserved-memory, interrupt controllers, and pinctrl. Signed-off-by: Billy Tsai Reviewed-by: Rob Herring (Arm) Acked-by: Linus Walleij Link: https://patch.msgid.link/20260428-upstream_pinctrl-v8-2-eb8ef9ab0498@aspeedtech.com Signed-off-by: Lee Jones --- .../bindings/mfd/aspeed,ast2x00-scu.yaml | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml index a87f31fce019..0d5e168b0309 100644 --- a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml +++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml @@ -46,6 +46,18 @@ properties: '#reset-cells': const: 1 + memory-region: + items: + - description: Region mapped through the first SSP address window. + - description: Region mapped through the second SSP address window. + - description: Region mapped through the TSP address window. + + memory-region-names: + items: + - const: ssp-0 + - const: ssp-1 + - const: tsp + patternProperties: '^p2a-control@[0-9a-f]+$': description: > @@ -87,6 +99,7 @@ patternProperties: - aspeed,ast2400-pinctrl - aspeed,ast2500-pinctrl - aspeed,ast2600-pinctrl + - aspeed,ast2700-soc0-pinctrl required: - compatible @@ -156,6 +169,30 @@ required: - '#clock-cells' - '#reset-cells' +allOf: + - if: + properties: + compatible: + contains: + enum: + - aspeed,ast2700-scu0 + - aspeed,ast2700-scu1 + then: + patternProperties: + '^p2a-control@[0-9a-f]+$': false + '^smp-memram@[0-9a-f]+$': false + + - if: + not: + properties: + compatible: + contains: + const: aspeed,ast2700-scu0 + then: + properties: + memory-region: false + memory-region-names: false + additionalProperties: false examples: @@ -180,4 +217,81 @@ examples: reg = <0x7c 0x4>, <0x150 0x8>; }; }; + + - | + / { + #address-cells = <2>; + #size-cells = <2>; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ssp_region_0: memory@400000000 { + reg = <0x4 0x00000000 0x0 0x01000000>; + no-map; + }; + + ssp_region_1: memory@401000000 { + reg = <0x4 0x01000000 0x0 0x01000000>; + no-map; + }; + + tsp_region: memory@402000000 { + reg = <0x4 0x02000000 0x0 0x01000000>; + no-map; + }; + }; + + bus { + #address-cells = <2>; + #size-cells = <2>; + + syscon@12c02000 { + compatible = "aspeed,ast2700-scu0", "syscon", "simple-mfd"; + reg = <0 0x12c02000 0 0x1000>; + ranges = <0x0 0x0 0x12c02000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + #clock-cells = <1>; + #reset-cells = <1>; + + memory-region = <&ssp_region_0>, <&ssp_region_1>, + <&tsp_region>; + memory-region-names = "ssp-0", "ssp-1", "tsp"; + + silicon-id@0 { + compatible = "aspeed,ast2700-silicon-id", "aspeed,silicon-id"; + reg = <0x0 0x4>; + }; + + interrupt-controller@1b0 { + compatible = "aspeed,ast2700-scu-ic0"; + reg = <0x1b0 0x4>; + #interrupt-cells = <1>; + interrupts-extended = <&intc0 97>; + interrupt-controller; + }; + + interrupt-controller@1e0 { + compatible = "aspeed,ast2700-scu-ic1"; + reg = <0x1e0 0x4>; + #interrupt-cells = <1>; + interrupts-extended = <&intc0 98>; + interrupt-controller; + }; + + pinctrl@400 { + compatible = "aspeed,ast2700-soc0-pinctrl"; + reg = <0x400 0x318>; + emmc-state { + function = "EMMC"; + groups = "EMMCG1"; + }; + }; + }; + }; + }; + ... From 8b2c1d41bc36c100b38ce5ee6def246c527eaf8a Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 27 Apr 2026 13:17:21 +0000 Subject: [PATCH 3908/5214] mfd: cros_ec: Delay dev_set_drvdata() until probe success If ec_device_probe() fails, cros_ec_class_release releases memory for the cros_ec_dev structure. However, because the drvdata was already set, sub-drivers like cros_ec_typec can still retrieve the stale pointer via the platform device. This leads to a use-after-free when cros_ec_typec attempts to access &typec->ec->ec->dev on a device that has already been released. Move dev_set_drvdata() to ensure that the pointer is only made available once all initialization steps have succeeded. sysfs: cannot create duplicate filename '/class/chromeos/cros_ec' Call trace: sysfs_do_create_link_sd+0x94/0xdc sysfs_create_link+0x30/0x44 device_add_class_symlinks+0x90/0x13c device_add+0xf0/0x50c ec_device_probe+0x150/0x4f0 platform_probe+0xa0/0xe0 ... BUG: KASAN: invalid-access in __memcpy+0x44/0x230 Write at addr f5ffff809e2d33ac by task kworker/u32:5/125 Pointer tag: [f5], memory tag: [fe] Tainted : [W]=WARN, [O]=OOT_MODULE Hardware name: Google Navi unprovisioned 0x7FFFFFFF/sku0 board/sku3 Workqueue: events_unbound deferred_probe_work_func Call trace: __memcpy+0x44/0x230 cros_ec_check_features+0x60/0xcc [cros_ec_proto] cros_typec_probe+0xe8/0x6e0 [cros_ec_typec] platform_probe+0xa0/0xe0 Cc: stable@vger.kernel.org Fixes: 1c1d152cc5ac ("platform/chrome: cros_ec_dev - utilize new cdev_device_add helper function") Co-developed-by: Sergey Senozhatsky Signed-off-by: Sergey Senozhatsky Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260427131721.1165078-1-akuchynski@chromium.org Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 70d64b7c6243..11ee1146cf71 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -191,7 +191,6 @@ static int ec_device_probe(struct platform_device *pdev) if (!ec) return retval; - dev_set_drvdata(dev, ec); ec->ec_dev = dev_get_drvdata(dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset; @@ -233,6 +232,8 @@ static int ec_device_probe(struct platform_device *pdev) if (retval) goto failed; + dev_set_drvdata(dev, ec); + /* check whether this EC is a sensor hub. */ if (cros_ec_get_sensor_count(ec) > 0) { retval = mfd_add_hotplug_devices(ec->dev, From 1cd125db9343dde0c521562a02190088dde2bd29 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:01:10 +0200 Subject: [PATCH 3909/5214] mfd: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Acked-by: Guru Das Srinagesh Link: https://patch.msgid.link/20260427070109.18271-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d08d70d41bce..763ce6a34782 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2381,7 +2381,7 @@ config MFD_ACER_A500_EC customized for the specific needs of the Acer A500 hardware. config MFD_QCOM_PM8008 - tristate "QCOM PM8008 Power Management IC" + tristate "Qualcomm PM8008 Power Management IC" depends on I2C && OF select MFD_CORE select REGMAP_I2C From b6ef1a74b3ec254f87a6a3c554fe8f8083ebd37c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 14:48:04 +0100 Subject: [PATCH 3910/5214] mfd: cs42l43: Sanity check firmware size Currently the code checks if a firmware was received, however it does not verify that the firmware size is larger than the firmware header. As the firmware pointer is dereferenced as a pointer to the header structure this could lead to an out of bounds memory access. Add the missing check. Fixes: ace6d1448138 ("mfd: cs42l43: Add support for cs42l43 core driver") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260508134804.1787461-1-ckeepax@opensource.cirrus.com Signed-off-by: Lee Jones --- drivers/mfd/cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 166881751e69..ed6d93893de0 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -722,7 +722,7 @@ static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *con unsigned int loadaddr, val; int ret; - if (!firmware) { + if (!firmware || firmware->size < sizeof(*hdr)) { dev_err(cs42l43->dev, "Failed to load firmware\n"); cs42l43->firmware_error = -ENODEV; goto err; From 616207451dedfc064c2735966d42811259cf0a82 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 29 Apr 2026 11:44:14 +0200 Subject: [PATCH 3911/5214] dt-bindings: mfd: mediatek: mt6397: Add rtc for MT6359 The rtc block of MT6359 PMIC is compatible with the one found in MT6358 but this compatibility was never expressed in the dt-bindings, so add the missing compatible string for the rtc subnode. Signed-off-by: Louis-Alexis Eyraud Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260429-mediatek-genio-mt6365-cleanup-v1-1-6f43838be92f@collabora.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index 05c121b0cb3d..dc2b38cf285d 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -70,6 +70,7 @@ properties: - mediatek,mt6397-rtc - items: - enum: + - mediatek,mt6359-rtc - mediatek,mt6366-rtc - const: mediatek,mt6358-rtc From 078ca283be013d9179981221b2242d049e4b2aa5 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 29 Apr 2026 11:44:15 +0200 Subject: [PATCH 3912/5214] dt-bindings: mfd: mediatek: mt6397: Add MT6365 PMIC support MT6365 PMIC is compatible with MT6359, so add the compatible strings for the main and sub devices (regulator, rtc, audio codec). Signed-off-by: Louis-Alexis Eyraud Reviewed-by: AngeloGioacchino Del Regno Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260429-mediatek-genio-mt6365-cleanup-v1-2-6f43838be92f@collabora.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/mediatek,mt6397.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index dc2b38cf285d..122aba7a54f8 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -44,6 +44,10 @@ properties: - enum: - mediatek,mt6366 - const: mediatek,mt6358 + - items: + - enum: + - mediatek,mt6365 + - const: mediatek,mt6359 interrupts: maxItems: 1 @@ -71,6 +75,7 @@ properties: - items: - enum: - mediatek,mt6359-rtc + - mediatek,mt6365-rtc - mediatek,mt6366-rtc - const: mediatek,mt6358-rtc @@ -99,6 +104,10 @@ properties: - enum: - mediatek,mt6366-regulator - const: mediatek,mt6358-regulator + - items: + - enum: + - mediatek,mt6365-regulator + - const: mediatek,mt6359-regulator required: - compatible @@ -125,6 +134,10 @@ properties: - enum: - mediatek,mt6366-sound - const: mediatek,mt6358-sound + - items: + - enum: + - mediatek,mt6365-codec + - const: mediatek,mt6359-codec required: - compatible From 7502c035b884b73f51669cada3f4022cd11e8f5c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 14 May 2026 17:15:14 +0800 Subject: [PATCH 3913/5214] mfd: dt-bindings: mt6397: Add regulator supplies On the MT6397 family each buck regulator has a separate supply. LDOs are split into various groups with independent supplies. There is also a supply for the regulator control logic. Add descriptions for all of the supplies for the MT6359. Signed-off-by: Chen-Yu Tsai Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260514091520.2718987-2-wenst@chromium.org Signed-off-by: Lee Jones --- .../bindings/mfd/mediatek,mt6397.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index 122aba7a54f8..3cbc0dc12c31 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -239,12 +239,62 @@ properties: description: Pin controller + vsys-smps-supply: + description: Supply for regulator control logic + +patternProperties: + "^vsys-v[a-z]+[0-9]*-supply$": + description: Supplies for PMIC buck regulators + "^vs(ys|[12])-ldo[1-9]-supply$": + description: Supplies for PMIC LDO regulators + required: - compatible - regulators additionalProperties: false +allOf: + - if: + properties: + "compatible": + contains: + const: mediatek,mt6359 + then: + properties: + vsys-ldo1-supply: + description: Supply for LDOs vcn33_[12], vio28, vfe28, vibr + vsys-ldo2-supply: + description: Supply for LDOs vaux18, vbif28, vxo22, vrfck, vrfck_1, + vemc, vsim1, vsim2, vusb + vsys-vcore-supply: + description: Supply for buck regulator vcore + vsys-vgpu11-supply: + description: Supply for buck regulator vgpu11 + vsys-vmodem-supply: + description: Supply for buck regulator vmodem + vsys-vpa-supply: + description: Supply for buck regulator vpa + vsys-vproc1-supply: + description: Supply for buck regulator vproc1 + vsys-vproc2-supply: + description: Supply for buck regulator vproc2 + vsys-vpu-supply: + description: Supply for buck regulator vpu + vsys-vs1-supply: + description: Supply for buck regulator vs1 + vsys-vs2-supply: + description: Supply for buck regulator vs2 + vs1-ldo1-supply: + description: Supply for LDOs vaud18, vcamio, vm18, vufs + vs1-ldo2-supply: + description: Supply for LDOs vcn18, vefuse, vio18, vrf18 + vs2-ldo1-supply: + description: + Supply for LDOs vsram_proc1, vsram_proc2, vsram_others, vsram_md + vs2-ldo2-supply: + description: Supply for LDOs va09, va12, vcn13, vrf12 + examples: - | #include From f01e402bebf8a2f3843990b209b3f50cf536dbf8 Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Thu, 14 May 2026 16:47:20 +0200 Subject: [PATCH 3914/5214] mfd: max77620: Convert poweroff support to sys-off API Convert max77620_pm_power_off() to the sys-off callback prototype and register it with the sys-off API when the device tree marks the PMIC as a system power controller. This also removes the global max77620_scratch pointer by passing the chip instance through the callback data. This modernizes the driver's poweroff handling and aligns it with the kernel sys-off infrastructure. Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20260514-smaug-poweroff-v1-2-30f9a4688966@tecnico.ulisboa.pt Signed-off-by: Lee Jones --- drivers/mfd/max77620.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index 3af2974b3023..c4f89a9681f3 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -31,11 +31,10 @@ #include #include #include +#include #include #include -static struct max77620_chip *max77620_scratch; - static const struct resource gpio_resources[] = { DEFINE_RES_IRQ(MAX77620_IRQ_TOP_GPIO), }; @@ -484,13 +483,15 @@ static int max77620_read_es_version(struct max77620_chip *chip) return ret; } -static void max77620_pm_power_off(void) +static int max77620_pm_power_off(struct sys_off_data *data) { - struct max77620_chip *chip = max77620_scratch; + struct max77620_chip *chip = data->cb_data; regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_SFT_RST, MAX77620_ONOFFCNFG1_SFT_RST); + + return NOTIFY_DONE; } static int max77620_probe(struct i2c_client *client) @@ -501,7 +502,6 @@ static int max77620_probe(struct i2c_client *client) struct regmap_irq_chip *chip_desc; const struct mfd_cell *mfd_cells; int n_mfd_cells; - bool pm_off; int ret; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); @@ -573,10 +573,14 @@ static int max77620_probe(struct i2c_client *client) return ret; } - pm_off = of_device_is_system_power_controller(client->dev.of_node); - if (pm_off && !pm_power_off) { - max77620_scratch = chip; - pm_power_off = max77620_pm_power_off; + if (of_device_is_system_power_controller(client->dev.of_node)) { + ret = devm_register_sys_off_handler(&client->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + max77620_pm_power_off, chip); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to register power-off handler\n"); } return 0; From a44ec8fd3839434c0c85ae68106a94081a31341f Mon Sep 17 00:00:00 2001 From: Md Shofiqul Islam Date: Thu, 14 May 2026 21:19:54 +0300 Subject: [PATCH 3915/5214] mfd: si476x-i2c: Fix spelling mistakes in comments Fix spelling mistake in comments: - succes -> success (4 times) Signed-off-by: Md Shofiqul Islam Link: https://patch.msgid.link/20260514181954.1442-1-shofiqtest@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/si476x-i2c.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index 899c0b5ea3aa..7ddc97dfc940 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -30,7 +30,7 @@ * * Configure the functions of the pins of the radio chip. * - * The function returns zero in case of succes or negative error code + * The function returns zero in case of success or negative error code * otherwise. */ static int si476x_core_config_pinmux(struct si476x_core *core) @@ -121,7 +121,7 @@ static inline void si476x_core_schedule_polling_work(struct si476x_core *core) * 4. Configures, pin multiplexor, disables digital audio and * configures interrupt sources. * - * The function returns zero in case of succes or negative error code + * The function returns zero in case of success or negative error code * otherwise. */ int si476x_core_start(struct si476x_core *core, bool soft) @@ -215,7 +215,7 @@ EXPORT_SYMBOL_GPL(si476x_core_start); * 2. Send the POWER_DOWN command if the power down is soft or bring * reset line low if not. * - * The function returns zero in case of succes or negative error code + * The function returns zero in case of success or negative error code * otherwise. */ int si476x_core_stop(struct si476x_core *core, bool soft) @@ -428,7 +428,7 @@ static void si476x_core_pronounce_dead(struct si476x_core *core) * of I/O errors. If the error counter rises above the threshold * pronounce device dead. * - * The function returns zero on succes or negative error code on + * The function returns zero on success or negative error code on * failure. */ int si476x_core_i2c_xfer(struct si476x_core *core, From 6b6e7e965a11f761a3b614f5ff3fa8d1d9c86b1e Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Soemardi Date: Thu, 14 May 2026 21:08:32 +0700 Subject: [PATCH 3916/5214] mfd: qcom_rpm: Add msm8960 QDSS clock resource The msm8960 RPM resource table is missing the QDSS clock entry (resource ID 209) that is present in the android-msm-mako-3.4 downstream kernel. Add it so that RPM clock initialization succeeds. Tested-by: Rudraksha Gupta Signed-off-by: Antony Kurniawan Soemardi Link: https://patch.msgid.link/20260514-msm8960-wifi-v2-3-7cbae45dab5e@smankusors.com Signed-off-by: Lee Jones --- drivers/mfd/qcom_rpm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c index 27446f43e3f3..0defb3279af1 100644 --- a/drivers/mfd/qcom_rpm.c +++ b/drivers/mfd/qcom_rpm.c @@ -324,6 +324,7 @@ static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = { [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 }, [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 }, [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 }, + [QCOM_RPM_QDSS_CLK] = { 209, ~0, 7, 1 }, }; static const struct qcom_rpm_data msm8960_template = { From 45a1d5f19684ab928b8375c30895b4cc8de937d3 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Mon, 11 May 2026 16:28:21 +0530 Subject: [PATCH 3917/5214] dt-bindings: mfd: qcom,tcsr: Document the IPQ5210 TCSR block Document the TCSR block found on the Qualcomm's IPQ5210 SoC. Signed-off-by: Kathiravan Thirumoorthy Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260511-ipq5210_tcsr_binding-v1-1-c8d20fed014f@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml index 23317d1b381c..7dd2fe035e6d 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml @@ -43,6 +43,7 @@ properties: - qcom,tcsr-apq8064 - qcom,tcsr-apq8084 - qcom,tcsr-ipq5018 + - qcom,tcsr-ipq5210 - qcom,tcsr-ipq5332 - qcom,tcsr-ipq5424 - qcom,tcsr-ipq6018 From 7aa3d8aba56ef337a2450385a29e074848ab0fa0 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sat, 16 May 2026 19:37:21 -0700 Subject: [PATCH 3918/5214] mfd: twl4030-power: Update checks for specific boards to use the DT The twl4030-power driver contains two checks for ARM machine IDs via machine_is_*() macros. The two boards concerned now support only FDT booting, which does not use machine IDs, and therefore the code should be updated to check the DT compatible property instead. The legacy board files for these machines were removed in commit 1b383f44aabc ("ARM: OMAP2+: Drop board file for 3430sdp") and commit e92fc4f04a34 ("ARM: OMAP2+: Drop legacy board file for LDP"). The presence of these machine ID checks prevents the removal of machine IDs no longer used by the kernel from arch/arm/tools/mach-types, because the machine_is_*() macros are generated from mach-types. To resolve this issue, use of_machine_is_compatible() instead. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260517023723.92731-2-enelsonmoore@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/twl4030-power.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 0bca948ab6ba..fc1cf316c236 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -30,8 +30,6 @@ #include #include -#include - static u8 twl4030_start_script_address = 0x2b; /* Register bits for P1, P2 and P3_SW_EVENTS */ @@ -294,8 +292,8 @@ twl4030_config_wakeup12_sequence(const struct twl4030_power_data *pdata, if (err) goto out; - if (pdata->ac_charger_quirk || machine_is_omap_3430sdp() || - machine_is_omap_ldp()) { + if (pdata->ac_charger_quirk || of_machine_is_compatible("ti,omap3430-sdp") || + of_machine_is_compatible("ti,omap3-ldp")) { /* Disabling AC charger effect on sleep-active transitions */ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_CFG_P1_TRANSITION); From 28ad23ef7f58abc8715e6f6c4255921da98e6acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Pfl=C3=BCger?= Date: Tue, 19 May 2026 14:06:12 +0200 Subject: [PATCH 3919/5214] dt-bindings: mfd: sprd,sc2731: Include SC2730 regulator bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SC2730 PMIC provides a different set of regulators than SC2731 and thus requires separate regulator bindings. Allow using them for the "regulators" node. Signed-off-by: Otto Pflüger Acked-by: Conor Dooley Link: https://patch.msgid.link/20260519-sc2730-regulators-v3-2-5bf0e02507e3@abscue.de Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/sprd,sc2731.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/sprd,sc2731.yaml b/Documentation/devicetree/bindings/mfd/sprd,sc2731.yaml index b023e1ef8d3c..e74ec4970994 100644 --- a/Documentation/devicetree/bindings/mfd/sprd,sc2731.yaml +++ b/Documentation/devicetree/bindings/mfd/sprd,sc2731.yaml @@ -54,7 +54,9 @@ properties: regulators: type: object - $ref: /schemas/regulator/sprd,sc2731-regulator.yaml# + oneOf: + - $ref: /schemas/regulator/sprd,sc2730-regulator.yaml# + - $ref: /schemas/regulator/sprd,sc2731-regulator.yaml# patternProperties: "^adc@[0-9a-f]+$": From 3001ed2b4e06da2276c42ace6551617065a5b1f9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:36:24 +0200 Subject: [PATCH 3920/5214] mfd: tps6586x: Fix OF node refcount Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 62f6b0879304 ("tps6586x: Add device tree support") Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260521-pdev-fwnode-ref-v1-1-88c324a1b8d2@oss.qualcomm.com Signed-off-by: Lee Jones --- drivers/mfd/tps6586x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 8d5fe2b60bfa..f5f805446603 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x, pdev->dev.parent = tps6586x->dev; pdev->dev.platform_data = subdev->platform_data; - pdev->dev.of_node = subdev->of_node; + pdev->dev.of_node = of_node_get(subdev->of_node); ret = platform_device_add(pdev); if (ret) { From 960b38750b17ed24d8f8de06e3c066c20d2167f9 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 21 May 2026 17:17:44 +0800 Subject: [PATCH 3921/5214] dt-bindings: mfd: aspeed,ast2x00-scu: Support AST2700 SoC1 pinctrl The AST2700 SoC integrates two interconnected SoC instances, each managed by its own System Control Unit (SCU). Allow the AST2700 SoC1 pin controller to be described as a child node of the SCU by extending the compatible strings accepted by the SCU binding. There is no functional change to the SCU binding beyond permitting the aspeed,ast2700-soc1-pinctrl compatible string. Signed-off-by: Billy Tsai Acked-by: Conor Dooley Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260521-pinctrl-single-bit-v5-1-308be2c160fc@aspeedtech.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml index 0d5e168b0309..948ba92ef49b 100644 --- a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml +++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml @@ -100,6 +100,7 @@ patternProperties: - aspeed,ast2500-pinctrl - aspeed,ast2600-pinctrl - aspeed,ast2700-soc0-pinctrl + - aspeed,ast2700-soc1-pinctrl required: - compatible From ed1a370da26369a26b25901685e620c12040a471 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 27 May 2026 17:56:03 +0300 Subject: [PATCH 3922/5214] mfd: rz-mtu3: Use device-managed APIs Replace devm_reset_control_get_exclusive() and the manual reset_control_deassert()/reset_control_assert() with handling by devm_reset_control_get_exclusive_deasserted(). Replace mfd_add_devices()/mfd_remove_devices() with devm_mfd_add_devices(). Remove the custom cleanup action. Signed-off-by: Cosmin Tanislav Link: https://patch.msgid.link/20260527145606.136536-2-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Lee Jones --- drivers/mfd/rz-mtu3.c | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index 9cdfef610398..6f6ed32e7592 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -301,15 +301,6 @@ void rz_mtu3_disable(struct rz_mtu3_channel *ch) } EXPORT_SYMBOL_GPL(rz_mtu3_disable); -static void rz_mtu3_reset_assert(void *data) -{ - struct rz_mtu3 *mtu = dev_get_drvdata(data); - struct rz_mtu3_priv *priv = mtu->priv_data; - - mfd_remove_devices(data); - reset_control_assert(priv->rstc); -} - static const struct mfd_cell rz_mtu3_devs[] = { { .name = "rz-mtu3-counter", @@ -324,7 +315,6 @@ static int rz_mtu3_probe(struct platform_device *pdev) struct rz_mtu3_priv *priv; struct rz_mtu3 *ddata; unsigned int i; - int ret; ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) @@ -340,7 +330,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (IS_ERR(priv->mmio)) return PTR_ERR(priv->mmio); - priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + priv->rstc = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); if (IS_ERR(priv->rstc)) return PTR_ERR(priv->rstc); @@ -348,7 +338,6 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (IS_ERR(ddata->clk)) return PTR_ERR(ddata->clk); - reset_control_deassert(priv->rstc); spin_lock_init(&priv->lock); platform_set_drvdata(pdev, ddata); @@ -358,17 +347,8 @@ static int rz_mtu3_probe(struct platform_device *pdev) mutex_init(&ddata->channels[i].lock); } - ret = mfd_add_devices(&pdev->dev, 0, rz_mtu3_devs, - ARRAY_SIZE(rz_mtu3_devs), NULL, 0, NULL); - if (ret < 0) - goto err_assert; - - return devm_add_action_or_reset(&pdev->dev, rz_mtu3_reset_assert, - &pdev->dev); - -err_assert: - reset_control_assert(priv->rstc); - return ret; + return devm_mfd_add_devices(&pdev->dev, 0, rz_mtu3_devs, + ARRAY_SIZE(rz_mtu3_devs), NULL, 0, NULL); } static const struct of_device_id rz_mtu3_of_match[] = { From 4bf15cafe9588d0c303143b4c64c7bfc7b1a3a5d Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 27 May 2026 17:56:04 +0300 Subject: [PATCH 3923/5214] mfd: rz-mtu3: Use local variable for reset Remove struct rz_mtu3_priv::rstc and use a local variable for it as it is not needed outside of rz_mtu3_probe() anymore. Signed-off-by: Cosmin Tanislav Link: https://patch.msgid.link/20260527145606.136536-3-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Lee Jones --- drivers/mfd/rz-mtu3.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index 6f6ed32e7592..8122355e0e29 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -21,7 +21,6 @@ struct rz_mtu3_priv { void __iomem *mmio; - struct reset_control *rstc; spinlock_t lock; }; @@ -314,6 +313,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) { struct rz_mtu3_priv *priv; struct rz_mtu3 *ddata; + struct reset_control *rstc; unsigned int i; ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); @@ -330,9 +330,9 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (IS_ERR(priv->mmio)) return PTR_ERR(priv->mmio); - priv->rstc = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); - if (IS_ERR(priv->rstc)) - return PTR_ERR(priv->rstc); + rstc = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(rstc)) + return PTR_ERR(rstc); ddata->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ddata->clk)) From a3bd9f3dd5c88c621e4bc4ecd5f2ae9c277f558a Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 27 May 2026 17:56:05 +0300 Subject: [PATCH 3924/5214] mfd: rz-mtu3: Store &pdev->dev in local variable &pdev->dev is accessed multiple times during probe. Store it in a local variable and use that to simplify the code. Signed-off-by: Cosmin Tanislav Link: https://patch.msgid.link/20260527145606.136536-4-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Lee Jones --- drivers/mfd/rz-mtu3.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index 8122355e0e29..f64c748d6ce4 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -311,16 +311,17 @@ static const struct mfd_cell rz_mtu3_devs[] = { static int rz_mtu3_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rz_mtu3_priv *priv; struct rz_mtu3 *ddata; struct reset_control *rstc; unsigned int i; - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; - ddata->priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + ddata->priv_data = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!ddata->priv_data) return -ENOMEM; @@ -330,11 +331,11 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (IS_ERR(priv->mmio)) return PTR_ERR(priv->mmio); - rstc = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return PTR_ERR(rstc); - ddata->clk = devm_clk_get(&pdev->dev, NULL); + ddata->clk = devm_clk_get(dev, NULL); if (IS_ERR(ddata->clk)) return PTR_ERR(ddata->clk); @@ -347,7 +348,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) mutex_init(&ddata->channels[i].lock); } - return devm_mfd_add_devices(&pdev->dev, 0, rz_mtu3_devs, + return devm_mfd_add_devices(dev, 0, rz_mtu3_devs, ARRAY_SIZE(rz_mtu3_devs), NULL, 0, NULL); } From f6b692d1f466ba3837ae9478e0d5ce6ef77697b4 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Wed, 27 May 2026 17:56:06 +0300 Subject: [PATCH 3925/5214] mfd: rz-mtu3: Make reset optional The Renesas RZ/T2H (R9A09G077) and RZ/N2H (R9A09G087) SoCs do not have a reset line for the MTU3 block. Prepare for them by making it optional. Signed-off-by: Cosmin Tanislav Link: https://patch.msgid.link/20260527145606.136536-5-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Lee Jones --- drivers/mfd/rz-mtu3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index f64c748d6ce4..3fa7dfe71386 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -331,7 +331,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) if (IS_ERR(priv->mmio)) return PTR_ERR(priv->mmio); - rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return PTR_ERR(rstc); From f167dc91aeed1928292e65a03d6472d3d01711fb Mon Sep 17 00:00:00 2001 From: Manish Baing Date: Sat, 23 May 2026 17:32:50 +0000 Subject: [PATCH 3926/5214] dt-bindings: mfd: st,stmpe: Add missing properties for PWM subnode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The st,stmpe-pwm binding is already covered by the MFD schema in Documentation/devicetree/bindings/mfd/st,stmpe.yaml. However, the PWM subnode was missing a 'required' properties block. This allowed Device Tree nodes to pass validation even if the 'compatible' string was omitted. This omission could lead to probe failures at runtime. Fix the schema by adding the missing 'required' block. Signed-off-by: Manish Baing Acked-by: Conor Dooley Acked-by: Uwe Kleine-König Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260523173251.72540-2-manishbaing2789@gmail.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/st,stmpe.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/st,stmpe.yaml b/Documentation/devicetree/bindings/mfd/st,stmpe.yaml index df43878fbe18..4bb05d544901 100644 --- a/Documentation/devicetree/bindings/mfd/st,stmpe.yaml +++ b/Documentation/devicetree/bindings/mfd/st,stmpe.yaml @@ -127,6 +127,10 @@ properties: "#pwm-cells": const: 2 + required: + - compatible + - "#pwm-cells" + touchscreen: type: object $ref: /schemas/input/touchscreen/touchscreen.yaml# From c454531af72e0df811600601413bb8d3d039ed08 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 8 Jun 2026 13:55:08 +0200 Subject: [PATCH 3927/5214] dt-bindings: mfd: syscon: Revert renesas,r9a08g046-lvds-cmn Revert commit 51284d8b1dbc ("dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L") because it is completely not matching reality and clearly incorrect in respect of renesas,r9a08g046-lvds-cmn. It wasn't ever build-tested by author on their DTS, either. The documented renesas,r9a08g046-lvds-cmn compatible clearly disallows any children and simple-mfd fallback, however its only use in original patchset is with simple-mfd and children, so this could have never worked. Fixes: 51284d8b1dbc ("dt-bindings: mfd: syscon: Document the LVDS_CMN syscon for the RZ/G3L") Signed-off-by: Krzysztof Kozlowski Acked-by: Biju Das Link: https://patch.msgid.link/20260608115507.134969-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/syscon.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index 9c81010d5a74..e22867088063 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -221,7 +221,6 @@ properties: - nxp,s32g3-gpr - qcom,apq8064-mmss-sfpb - qcom,apq8064-sps-sic - - renesas,r9a08g046-lvds-cmn - rockchip,px30-qos - rockchip,rk3036-qos - rockchip,rk3066-qos From 8a4506595857356fcef9f7aad3506593e9fabbbc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 13:46:29 -0300 Subject: [PATCH 3928/5214] perf machine: Propagate machine__init() error to callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit machine__init() always returns 0 even when memory allocation fails, because commit 81f981d7ec43ed93 ("perf machine: Free root_dir in machine__init() error path") introduced 'int err = -ENOMEM' and an error cleanup path but left the final 'return 0' instead of 'return err'. Fix by returning err, check the return value in __machine__new_host() which was ignoring it, and change machines__init() from void to int so it too can propagate the error to perf_session__new(), aslr_tool__init() and test callers. The error cleanup also used zfree(&machine->kmaps), but kmaps is a refcounted maps structure — use maps__zput() to properly drop the reference, matching machine__exit(). Move dsos__init() and threads__init() before the first fallible allocation (maps__new) so that machine__exit() is safe to call on any machine struct that machine__init() touched, even on early failure. Fixes: 81f981d7ec43ed93 ("perf machine: Free root_dir in machine__init() error path") Reported-by: sashiko-bot Cc: Ian Rogers Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hists_cumulate.c | 5 +++-- tools/perf/tests/hists_filter.c | 5 +++-- tools/perf/tests/hists_link.c | 5 +++-- tools/perf/tests/hists_output.c | 5 +++-- tools/perf/tests/kallsyms-split.c | 7 +++++-- tools/perf/tests/thread-maps-share.c | 2 +- tools/perf/tests/vmlinux-kallsyms.c | 8 +++++--- tools/perf/util/aslr.c | 12 +++++++++--- tools/perf/util/machine.c | 24 ++++++++++++++---------- tools/perf/util/machine.h | 2 +- tools/perf/util/session.c | 7 ++++--- 11 files changed, 51 insertions(+), 31 deletions(-) diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 267cbc24691a..09ee08085b06 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -704,7 +704,7 @@ static int test4(struct evsel *evsel, struct machine *machine) static int test__hists_cumulate(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int err = TEST_FAIL; - struct machines machines; + struct machines machines = { 0 }; struct machine *machine; struct evsel *evsel; struct evlist *evlist = evlist__new(); @@ -723,7 +723,8 @@ static int test__hists_cumulate(struct test_suite *test __maybe_unused, int subt goto out; err = TEST_FAIL; - machines__init(&machines); + if (machines__init(&machines)) + goto out; /* setup threads/dso/map/symbols also */ machine = setup_fake_machine(&machines); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 002e3a4c1ca5..ac5affb7afff 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -116,7 +116,7 @@ static void put_fake_samples(void) static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int err = TEST_FAIL; - struct machines machines; + struct machines machines = { 0 }; struct machine *machine; struct evsel *evsel; struct evlist *evlist = evlist__new(); @@ -131,7 +131,8 @@ static int test__hists_filter(struct test_suite *test __maybe_unused, int subtes goto out; err = TEST_FAIL; - machines__init(&machines); + if (machines__init(&machines)) + goto out; /* setup threads/dso/map/symbols also */ machine = setup_fake_machine(&machines); diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 996f5f0b3bd1..e55990163865 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -287,7 +287,7 @@ static int test__hists_link(struct test_suite *test __maybe_unused, int subtest { int err = -1; struct hists *hists, *first_hists; - struct machines machines; + struct machines machines = { 0 }; struct machine *machine = NULL; struct evsel *evsel, *first; struct evlist *evlist = evlist__new(); @@ -303,7 +303,8 @@ static int test__hists_link(struct test_suite *test __maybe_unused, int subtest goto out; err = TEST_FAIL; - machines__init(&machines); + if (machines__init(&machines)) + goto out; /* setup threads/dso/map/symbols also */ machine = setup_fake_machine(&machines); diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index fa683fd7b1e5..5e59dba92e81 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -590,7 +590,7 @@ static int test5(struct evsel *evsel, struct machine *machine) static int test__hists_output(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int err = TEST_FAIL; - struct machines machines; + struct machines machines = { 0 }; struct machine *machine; struct evsel *evsel; struct evlist *evlist = evlist__new(); @@ -610,7 +610,8 @@ static int test__hists_output(struct test_suite *test __maybe_unused, int subtes goto out; err = TEST_FAIL; - machines__init(&machines); + if (machines__init(&machines)) + goto out; /* setup threads/dso/map/symbols also */ machine = setup_fake_machine(&machines); diff --git a/tools/perf/tests/kallsyms-split.c b/tools/perf/tests/kallsyms-split.c index 117ed3b70f63..244daa01bd5d 100644 --- a/tools/perf/tests/kallsyms-split.c +++ b/tools/perf/tests/kallsyms-split.c @@ -97,7 +97,7 @@ static int create_proc_dir(void) static int test__kallsyms_split(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - struct machine m; + struct machine m = { 0 }; struct map *map = NULL; int ret = TEST_FAIL; @@ -113,7 +113,10 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused, signal(SIGTERM, remove_proc_dir); pr_debug("create kernel maps from the fake root directory\n"); - machine__init(&m, root_dir, HOST_KERNEL_ID); + if (machine__init(&m, root_dir, HOST_KERNEL_ID)) { + pr_debug("FAIL: failed to init machine\n"); + goto out; + } if (machine__create_kernel_maps(&m) < 0) { pr_debug("FAIL: failed to create kernel maps\n"); goto out; diff --git a/tools/perf/tests/thread-maps-share.c b/tools/perf/tests/thread-maps-share.c index e9ecd30a5c05..0431bff31b3a 100644 --- a/tools/perf/tests/thread-maps-share.c +++ b/tools/perf/tests/thread-maps-share.c @@ -27,7 +27,7 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s * other group (pid: 4, tids: 4, 5) */ - machines__init(&machines); + TEST_ASSERT_VAL("failed to init machines", machines__init(&machines) == 0); machine = &machines.host; /* create process with 4 threads */ diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 7409abe4aa36..9396c8a77c86 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -192,7 +192,7 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused struct rb_node *nd; struct symbol *sym; struct map *kallsyms_map; - struct machine vmlinux; + struct machine vmlinux = { 0 }; struct maps *maps; u64 mem_start, mem_end; struct test__vmlinux_matches_kallsyms_cb_args args; @@ -203,8 +203,10 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused * Init the machines that will hold kernel, modules obtained from * both vmlinux + .ko files and from /proc/kallsyms split by modules. */ - machine__init(&args.kallsyms, "", HOST_KERNEL_ID); - machine__init(&vmlinux, "", HOST_KERNEL_ID); + if (machine__init(&args.kallsyms, "", HOST_KERNEL_ID)) + goto out; + if (machine__init(&vmlinux, "", HOST_KERNEL_ID)) + goto out; maps = machine__kernel_maps(&vmlinux); diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c index a946fff2ac4d..6a7542e7db82 100644 --- a/tools/perf/util/aslr.c +++ b/tools/perf/util/aslr.c @@ -1237,12 +1237,13 @@ void aslr_tool__strip_attr_event(union perf_event *event, struct evlist *evlist) } } -static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) +static int aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) { delegate_tool__init(&aslr->tool, delegate); aslr->tool.tool.ordered_events = true; - machines__init(&aslr->machines); + if (machines__init(&aslr->machines)) + return -ENOMEM; hashmap__init(&aslr->remap_addresses, remap_addresses__hash, remap_addresses__equal, @@ -1276,6 +1277,8 @@ static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate) aslr->tool.tool.auxtrace = aslr_tool__process_auxtrace; aslr->tool.tool.auxtrace_info = aslr_tool__process_auxtrace_info; aslr->tool.tool.auxtrace_error = aslr_tool__process_auxtrace_error; + + return 0; } struct perf_tool *aslr_tool__new(struct perf_tool *delegate) @@ -1285,7 +1288,10 @@ struct perf_tool *aslr_tool__new(struct perf_tool *delegate) if (!aslr) return NULL; - aslr_tool__init(aslr, delegate); + if (aslr_tool__init(aslr, delegate)) { + free(aslr); + return NULL; + } return &aslr->tool.tool; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 31715366e29f..9329d319bd03 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -79,15 +79,14 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) int err = -ENOMEM; memset(machine, 0, sizeof(*machine)); - machine->kmaps = maps__new(machine); - if (machine->kmaps == NULL) - return -ENOMEM; - RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); - threads__init(&machine->threads); + machine->kmaps = maps__new(machine); + if (machine->kmaps == NULL) + goto out; + machine->vdso_info = NULL; machine->env = NULL; @@ -124,11 +123,11 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) out: if (err) { - zfree(&machine->kmaps); + maps__zput(machine->kmaps); zfree(&machine->root_dir); zfree(&machine->mmap_name); } - return 0; + return err; } static struct machine *__machine__new_host(struct perf_env *host_env, bool kernel_maps) @@ -138,7 +137,10 @@ static struct machine *__machine__new_host(struct perf_env *host_env, bool kerne if (!machine) return NULL; - machine__init(machine, "", HOST_KERNEL_ID); + if (machine__init(machine, "", HOST_KERNEL_ID) != 0) { + free(machine); + return NULL; + } if (kernel_maps && machine__create_kernel_maps(machine) < 0) { free(machine); @@ -231,10 +233,12 @@ void machine__delete(struct machine *machine) } } -void machines__init(struct machines *machines) +int machines__init(struct machines *machines) { - machine__init(&machines->host, "", HOST_KERNEL_ID); + int err = machine__init(&machines->host, "", HOST_KERNEL_ID); + machines->guests = RB_ROOT_CACHED; + return err; } void machines__exit(struct machines *machines) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index aaddfb70ea66..26f9827062f5 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -152,7 +152,7 @@ struct machines { struct rb_root_cached guests; }; -void machines__init(struct machines *machines); +int machines__init(struct machines *machines); void machines__exit(struct machines *machines); void machines__process_guests(struct machines *machines, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1a9a008ddda3..f391a822480d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -160,11 +160,12 @@ struct perf_session *__perf_session__new(struct perf_data *data, session->decomp_data.zstd_decomp = &session->zstd_data; session->active_decomp = &session->decomp_data; INIT_LIST_HEAD(&session->auxtrace_index); - machines__init(&session->machines); + perf_env__init(&session->header.env); + if (machines__init(&session->machines)) + goto out_delete; + ordered_events__init(&session->ordered_events, ordered_events__deliver_event, NULL); - - perf_env__init(&session->header.env); if (data) { ret = perf_data__open(data); if (ret < 0) From fe63d3bca288c5bb983304efd5fc3a5ff3183403 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 13:59:39 -0300 Subject: [PATCH 3929/5214] perf machine: Use snprintf() for guestmount path construction machines__findnew() and machines__create_guest_kernel_maps() use sprintf() to build paths by prepending symbol_conf.guestmount. Both write into PATH_MAX stack buffers, but guestmount comes from user configuration and is not length-checked. A guestmount path at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof() to prevent overflow. The subsequent access()/fopen() calls will fail on a truncated path. Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Reported-by: sashiko-bot Cc: Zhang, Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9329d319bd03..0d2ebf6a84bc 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -333,7 +333,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid) if ((pid != HOST_KERNEL_ID) && (pid != DEFAULT_GUEST_KERNEL_ID) && (symbol_conf.guestmount)) { - sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid); if (access(path, R_OK)) { static struct strlist *seen; @@ -1260,9 +1260,9 @@ int machines__create_guest_kernel_maps(struct machines *machines) namelist[i]->d_name); continue; } - sprintf(path, "%s/%s/proc/kallsyms", - symbol_conf.guestmount, - namelist[i]->d_name); + snprintf(path, sizeof(path), "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); ret = access(path, R_OK); if (ret) { pr_debug("Can't access file %s\n", path); From 312d91329b8fc6989a916a3f9a12d0674167b7e4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:16:45 -0300 Subject: [PATCH 3930/5214] perf cs-etm: Validate num_cpu before metadata allocation cs_etm__process_auxtrace_info_full() reads num_cpu from untrusted perf.data and uses it to allocate the metadata pointer array: metadata = zalloc(sizeof(*metadata) * num_cpu); On 32-bit, sizeof(*metadata) is 4, so num_cpu = 0x40000000 overflows the multiplication to 0, causing zalloc(0) to return a valid zero-sized allocation followed by out-of-bounds writes in the population loop. Fix by computing priv_size early and using it to bound num_cpu: each CPU needs at least one u64 metadata entry, so num_cpu cannot exceed the total number of u64 entries in the event's private data area. Fixes: cd8bfd8c973eaff8 ("perf tools: Add processing of coresight metadata") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Cc: Tor Jeremiassen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 0927b0b9c06b..d121c8f22028 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -3431,6 +3431,18 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, /* First the global part */ ptr = (u64 *) auxtrace_info->priv; num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; + + /* + * Bound num_cpu by the event size: the global header consumes + * CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64 + * metadata entry after that. + */ + priv_size = total_size - event_header_size - INFO_HEADER_SIZE - + CS_ETM_HEADER_SIZE; + if (num_cpu <= 0 || priv_size <= 0 || + num_cpu > priv_size / (int)sizeof(u64)) + return -EINVAL; + metadata = zalloc(sizeof(*metadata) * num_cpu); if (!metadata) return -ENOMEM; From 78d8ba680126f3545e8d0fba667e12d79fd4353b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:40:36 -0300 Subject: [PATCH 3931/5214] perf cs-etm: Require full global header in auxtrace_info size check cs_etm__process_auxtrace_info() checks that header.size covers event_header_size + INFO_HEADER_SIZE (16 bytes total), but then accesses ptr[CS_PMU_TYPE_CPUS] at offset 24 from the start of the event. A crafted 16-byte auxtrace_info event passes the size check but reads out-of-bounds. Include CS_ETM_HEADER_SIZE in the minimum size check so that the global header entries (version, pmu_type_cpus, snapshot) are guaranteed to fit within the event. Fixes: 55c1de9973d66516 ("perf cs-etm: Print auxtrace info even if OpenCSD isn't linked") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c index 4abe416e3feb..aebef71d3a0a 100644 --- a/tools/perf/util/cs-etm-base.c +++ b/tools/perf/util/cs-etm-base.c @@ -170,7 +170,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event, u64 *ptr = NULL; u64 hdr_version; - if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE)) + /* Ensure priv[] is large enough for the global header entries */ + if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE + + CS_ETM_HEADER_SIZE)) return -EINVAL; /* First the global part */ From 9a989e60cc6e29d98aed2087425cba53bf4b392d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:55:32 -0300 Subject: [PATCH 3932/5214] perf cs-etm: Bounds-check CPU in cs_etm__get_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cs_etm__get_queue() indexes etm->queues.queue_array[cpu] without validating that cpu is within nr_queues. When processing AUX_OUTPUT_HW_ID events, the cpu value comes from untrusted perf.data trace payload and flows through cs_etm__process_trace_id_v0_1() and cs_etm__queue_aux_fragment() without bounds checking, allowing an out-of-bounds read with a crafted file. Add a bounds check in cs_etm__get_queue() and NULL checks in all callers. Also add NULL checks for queue_array[i].priv in the queue iteration loops in cs_etm__map_trace_id_v0() and cs_etm__process_trace_id_v0_1() — after auxtrace_queues__grow() new entries are zero-initialized so .priv can be NULL. Add a get_cpu_data() NULL check in cs_etm__process_trace_id_v0_1(), matching the existing check in cs_etm__process_trace_id_v0(). Fixes: 77c123f53e97ad4b ("perf: cs-etm: Move traceid_list to each queue") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index d121c8f22028..5d0664ff73b7 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -292,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int c { if (etm->per_thread_decoding) return etm->queues.queue_array[0].priv; - else - return etm->queues.queue_array[cpu].priv; + + if (cpu < 0 || cpu >= (int)etm->queues.nr_queues) + return NULL; + + return etm->queues.queue_array[cpu].priv; } static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, @@ -306,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id * queue associated with that CPU so only one decoder is made. */ etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (!etmq) + return -EINVAL; + if (etmq->format == UNFORMATTED) return cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); @@ -318,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id int ret; etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); if (ret) @@ -358,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + if (!etmq) + return -EINVAL; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -375,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + if (!other_etmq) + continue; + /* Different sinks, skip */ if (other_etmq->sink_id != etmq->sink_id) continue; @@ -396,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, } cpu_data = get_cpu_data(etm, cpu); + if (!cpu_data) + return -EINVAL; + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); if (ret) return ret; @@ -3144,6 +3162,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu); + if (!etmq) + return -EINVAL; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. From 61a21d11afd8dd83b2260ae00541bed62a843219 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:13:54 -0300 Subject: [PATCH 3933/5214] perf c2c: Free format list entries when c2c_hists__init() fails When c2c_hists__init() fails partway through hpp_list__parse(), dynamically allocated format structures that were already added to hists->list are leaked because he__get_c2c_hists() frees the hists container without first unregistering the format entries. Call perf_hpp__reset_output_field() before freeing the hists container on the error path, matching what c2c_he_free() already does on the normal destruction path. Fixes: 17a7c5946d79a12c ("perf c2c report: Decode c2c_stats for hist entries") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 07c7e8fb315e..eabb922ef295 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -226,6 +226,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + perf_hpp__reset_output_field(&hists->list); c2c_he->hists = NULL; free(hists); return NULL; From fe68cf349fb343c0a7cb6c4fe6c3de4f4afe8d1c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:16:42 -0300 Subject: [PATCH 3934/5214] perf c2c: Fix hist entry and format list leaks in c2c_he_free() c2c_he_free() calls hists__delete_entries() which only walks the output-sorted entries tree. During c2c resort, when cacheline entries are merged and the redundant entry is freed, the inner hists have not been output-resorted yet, so hists->entries is empty. The actual inner hist_entry objects live in entries_in_array[] and entries_collapsed, which are never walked, leaking all inner hist_entry objects for every merged cacheline. Additionally, the dynamically allocated format entries on hists->list are never unregistered or freed. Fix both issues by switching to hists__delete_all_entries() which walks all rb_root trees, and calling perf_hpp__reset_output_field() to clean up format entries. Fixes: bf0e0d407ea09ce5 ("perf c2c report: Add sample processing") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 2 +- tools/perf/util/hist.c | 2 +- tools/perf/util/hist.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index eabb922ef295..c9584dbedf77 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -184,7 +184,7 @@ static void c2c_he_free(void *he) c2c_he = container_of(he, struct c2c_hist_entry, he); if (c2c_he->hists) { - hists__delete_entries(&c2c_he->hists->hists); + hists__delete_all_entries(&c2c_he->hists->hists); perf_hpp__reset_output_field(&c2c_he->hists->list); zfree(&c2c_he->hists); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index df978c996b6c..c93915625ee7 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -3041,7 +3041,7 @@ static void hists__delete_remaining_entries(struct rb_root_cached *root) } } -static void hists__delete_all_entries(struct hists *hists) +void hists__delete_all_entries(struct hists *hists) { hists__delete_entries(hists); hists__delete_remaining_entries(&hists->entries_in_array[0]); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 8fb89d81ef06..b830cbe7f95b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -391,6 +391,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__delete_entries(struct hists *hists); +void hists__delete_all_entries(struct hists *hists); void hists__output_recalc_col_len(struct hists *hists, int max_rows); struct hist_entry *hists__get_entry(struct hists *hists, int idx); From 5ebf4137d23a4fd6c0cc6a6fb766ee60d2b09193 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:25:11 -0300 Subject: [PATCH 3935/5214] perf bpf: Validate array presence before casting BPF prog info pointers Several functions cast bpf_prog_info fields (jited_ksyms, jited_func_lens, jited_prog_insns) from u64 to pointers and dereference them. These fields are only valid pointers if bpil_offs_to_addr() converted their file offsets to addresses, which only happens when the corresponding PERF_BPIL_* bits are set in info_linear->arrays. A crafted perf.data can leave these bits unset while setting non-zero counts and offset values, causing the functions to dereference raw file offsets as pointers. Add array bitmask validation to all perf.data processing paths: - __bpf_event__print_bpf_prog_info(): check JITED_KSYMS and JITED_FUNC_LENS (changed to take struct perf_bpil *) - machine__process_bpf_event_load(): check JITED_KSYMS - bpf_read(): check JITED_INSNS before memcpy from jited_prog_insns - dso__disassemble_filename(): check JITED_INSNS before returning jited_prog_insns pointer Fixes: f8dfeae009effc0b ("perf bpf: Show more BPF program info in print_bpf_prog_info()") Reported-by: sashiko-bot Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-event.c | 20 +++++++++++++++++--- tools/perf/util/bpf-event.h | 4 ++-- tools/perf/util/dso.c | 10 ++++++++++ tools/perf/util/header.c | 3 +-- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 57d53ba84835..fa3ebc8ea7f0 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -59,6 +59,10 @@ static int machine__process_bpf_event_load(struct machine *machine, return 0; info_linear = info_node->info_linear; + /* jited_ksyms is only valid if bpil_offs_to_addr() converted it */ + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_KSYMS))) + return 0; + for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) { u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); u64 addr = addrs[i]; @@ -959,12 +963,15 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env) return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env); } -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp) { - __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); - __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + struct bpf_prog_info *info = &info_linear->info; + __u64 required_arrays = (1UL << PERF_BPIL_JITED_KSYMS) | + (1UL << PERF_BPIL_JITED_FUNC_LENS); + __u32 *prog_lens; + __u64 *prog_addrs; char name[KSYM_NAME_LEN]; struct btf *btf = NULL; u32 sub_prog_cnt, i; @@ -974,6 +981,13 @@ void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, sub_prog_cnt != info->nr_jited_func_lens) return; + /* Ensure the arrays were present and converted by bpil_offs_to_addr() */ + if ((info_linear->arrays & required_arrays) != required_arrays) + return; + + prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); + prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + if (info->btf_id) { struct btf_node *node; diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index 60d2c6637af5..da4eeb4a1a73 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -40,7 +40,7 @@ struct btf_node { int machine__process_bpf(struct machine *machine, union perf_event *event, struct perf_sample *sample); int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env); -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp); void bpf_metadata_free(struct bpf_metadata *metadata); @@ -58,7 +58,7 @@ static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused, return 0; } -static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused, +static inline void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear __maybe_unused, struct perf_env *env __maybe_unused, FILE *fp __maybe_unused) { diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 1a2fc6d18da7..79f1a30f3683 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -880,6 +880,12 @@ static ssize_t bpf_read(struct dso *dso, u64 offset, char *data) return -1; } + /* jited_prog_insns is only valid if bpil_offs_to_addr() converted it */ + if (!(node->info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + dso__data(dso)->status = DSO_DATA_STATUS_ERROR; + return -1; + } + len = node->info_linear->info.jited_prog_len; buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns; @@ -1995,6 +2001,10 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename, return NULL; } info_linear = info_node->info_linear; + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; + return NULL; + } assert(len <= info_linear->info.jited_prog_len); *out_buf_len = len; return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index d7f41db7322c..091d8f7f6bd2 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2107,8 +2107,7 @@ static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp) node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node); - __bpf_event__print_bpf_prog_info(&node->info_linear->info, - env, fp); + __bpf_event__print_bpf_prog_info(node->info_linear, env, fp); } up_read(&env->bpf_progs.lock); From 1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:55:33 -0300 Subject: [PATCH 3936/5214] perf dso: Set standard errno on decompression failure dso__get_filename() sets errno to a negative custom DSO_LOAD_ERRNO value when kernel module decompression fails: errno = *dso__load_errno(dso); /* e.g. -9996 */ The caller __open_dso() then computes fd = -errno, producing a large positive value (9996) that looks like a valid file descriptor. This can cause close_data_fd() to close an unrelated fd used by another subsystem. Set errno to EIO instead. The detailed error code is already stored in dso__load_errno(dso) for diagnostic messages. Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 79f1a30f3683..2309196d8df3 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -600,7 +600,13 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, size_t len = sizeof(newpath); if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) { - errno = *dso__load_errno(dso); + /* + * Use a standard errno value, not the negative custom + * DSO_LOAD_ERRNO stored in dso__load_errno(dso): + * __open_dso() computes fd = -errno, so a negative + * errno produces a positive fd that looks valid. + */ + errno = EIO; goto out; } From 2e9261761b35f0b67b7487688cd1365f535be0b3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 16 Jun 2026 18:02:57 +0100 Subject: [PATCH 3937/5214] ASoC: qcom: q6apm: fix NULL pointer dereference in graph_callback When q6apm_free_fragments() is called it frees rx_data.buf/tx_data.buf and sets them to NULL under graph->lock. A late DSP buffer-done response can race with this: graph_callback() passes the !graph->ar_graph guard (not yet NULL), acquires the lock, but then dereferences a now-NULL buf pointer to read buf[token].phys, crashing at virtual address 0x10. Add a NULL check for buf inside the mutex-protected section in both the write-done (DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2) and read-done (DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2) handlers and bail out cleanly if buffers have already been freed. This problem is only shown up recently while apr bus was updated to process the commands per service rather from single global queue. Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Cc: Stable@vger.kernel.org Assisted-by: Claude:claude-4-6-sonnet Reported-by: Val Packett Closes: https://lore.kernel.org/all/133ced18-1aa9-475d-80d8-6120678bdde4@packett.cool/ Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260616170257.9381-1-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2e5b25b8d00f..641d6d243229 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -587,6 +587,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) token = hdr->token & APM_WRITE_TOKEN_MASK; done = data->payload; + if (!graph->rx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->rx_data.buf[token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ @@ -609,6 +613,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) client_event = APM_CLIENT_EVENT_DATA_READ_DONE; mutex_lock(&graph->lock); rd_done = data->payload; + if (!graph->tx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->tx_data.buf[hdr->token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ From 1a3c8e28959790ae3e06029681418278b7821a3c Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Wed, 17 Jun 2026 13:46:04 +0200 Subject: [PATCH 3938/5214] ASoC: rockchip: Drop problematic guard() changes This reverts commit f7fe9f707360 ("ASoC: rockchip: rockchip_sai: Use guard() for spin locks"). This is very noisy pointless churn that was not tested by the submitter, nor was it addressed to the driver's maintainer. It mixes unrelated whitespace changes (eliminating the blank line between the includes - why?) with hard to review diffs that add a whole indentation level to the function for no benefit, while also not following kernel code style by doing stuff like "ret == 0". The driver is better off without these changes, and they're not worth the time to validate whether they really do make no functional changes. Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260617-sai-revert-v1-1-e46adda2213b@collabora.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 274 +++++++++++++++--------------- 1 file changed, 139 insertions(+), 135 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index a195e96fed0a..ed393e5034a4 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -18,6 +18,7 @@ #include #include #include + #include "rockchip_sai.h" #define DRV_NAME "rockchip-sai" @@ -215,12 +216,14 @@ static void rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsigned static int rockchip_sai_runtime_suspend(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); + unsigned long flags; rockchip_sai_fsync_lost_detect(sai, 0); rockchip_sai_fsync_err_detect(sai, 0); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) - rockchip_sai_xfer_clk_stop_and_wait(sai, NULL); + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, NULL); + spin_unlock_irqrestore(&sai->xfer_lock, flags); regcache_cache_only(sai->regmap, true); /* @@ -480,6 +483,7 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); unsigned int mask = 0, val = 0; unsigned int clk_gates; + unsigned long flags; int ret = 0; pm_runtime_get_sync(dai->dev); @@ -495,56 +499,56 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) sai->is_master_mode = false; break; default: - pm_runtime_put(dai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { - rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); - if (sai->initialized) { - if (sai->has_capture && sai->has_playback) - rockchip_sai_xfer_stop(sai, -1); - else if (sai->has_capture) - rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE); - else - rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK); - } else { - rockchip_sai_clear(sai, 0); - sai->initialized = true; - } - - regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - - mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK; - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL; - break; - case SND_SOC_DAIFMT_NB_IF: - val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED; - break; - case SND_SOC_DAIFMT_IB_NF: - val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL; - break; - case SND_SOC_DAIFMT_IB_IF: - val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED; - break; - default: - ret = -EINVAL; - break; - } - - if (ret == 0) { - regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - rockchip_sai_fmt_create(sai, fmt); - } - - if (clk_gates) - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, - clk_gates); + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + if (sai->initialized) { + if (sai->has_capture && sai->has_playback) + rockchip_sai_xfer_stop(sai, -1); + else if (sai->has_capture) + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE); + else + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK); + } else { + rockchip_sai_clear(sai, 0); + sai->initialized = true; } + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_NB_IF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED; + break; + default: + ret = -EINVAL; + goto err_xfer_unlock; + } + + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + rockchip_sai_fmt_create(sai, fmt); + +err_xfer_unlock: + if (clk_gates) + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: pm_runtime_put(dai->dev); return ret; @@ -560,6 +564,7 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, unsigned int ch_per_lane, slot_width; unsigned int val, fscr, reg; unsigned int lanes, req_lanes; + unsigned long flags; int ret = 0; if (!rockchip_sai_stream_valid(substream, dai)) @@ -586,8 +591,8 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n", lanes, reg == SAI_TXCR ? "playback" : "capture", params_channels(params)); - pm_runtime_put(sai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } else { lanes = req_lanes; } @@ -613,88 +618,84 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, val = SAI_XCR_VDW(32); break; default: - pm_runtime_put(sai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } val |= SAI_XCR_CSR(lanes); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { + spin_lock_irqsave(&sai->xfer_lock, flags); - regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); + regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); - if (!sai->is_tdm) - regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(params_physical_width(params))); + if (!sai->is_tdm) + regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(params_physical_width(params))); - regmap_read(sai->regmap, reg, &val); + regmap_read(sai->regmap, reg, &val); - slot_width = SAI_XCR_SBW_V(val); - ch_per_lane = params_channels(params) / lanes; + slot_width = SAI_XCR_SBW_V(val); + ch_per_lane = params_channels(params) / lanes; - regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, - SAI_XCR_SNB(ch_per_lane)); + regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, + SAI_XCR_SNB(ch_per_lane)); - fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane); + fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane); - switch (sai->fpw) { - case FPW_ONE_BCLK_WIDTH: - fscr |= SAI_FSCR_FPW(1); - break; - case FPW_ONE_SLOT_WIDTH: - fscr |= SAI_FSCR_FPW(slot_width); - break; - case FPW_HALF_FRAME_WIDTH: - fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2); - break; - default: - dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw); - ret = -EINVAL; - break; - } - - if (ret == 0) { - regmap_update_bits(sai->regmap, SAI_FSCR, - SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr); - - if (sai->is_master_mode) { - bclk_rate = sai->fw_ratio * slot_width * - ch_per_lane * params_rate(params); - ret = clk_set_rate(sai->mclk, sai->mclk_rate); - if (ret) - dev_err(sai->dev, "Failed to set mclk to %u: %pe\n", - sai->mclk_rate, ERR_PTR(ret)); - else { - mclk_rate = clk_get_rate(sai->mclk); - if (mclk_rate < bclk_rate) { - dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n", - mclk_rate, bclk_rate); - ret = -EINVAL; - } else { - - div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); - mclk_req_rate = bclk_rate * div_bclk; - - if (mclk_rate < - mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX || - mclk_rate > - mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) { - dev_err(sai->dev, - "Mismatch mclk: %u, expected %u (+/- %dHz)\n", - mclk_rate, mclk_req_rate, - CLK_SHIFT_RATE_HZ_MAX); - ret = -EINVAL; - } else - regmap_update_bits(sai->regmap, - SAI_CKR, - SAI_CKR_MDIV_MASK, - SAI_CKR_MDIV(div_bclk)); - } - } - } - } + switch (sai->fpw) { + case FPW_ONE_BCLK_WIDTH: + fscr |= SAI_FSCR_FPW(1); + break; + case FPW_ONE_SLOT_WIDTH: + fscr |= SAI_FSCR_FPW(slot_width); + break; + case FPW_HALF_FRAME_WIDTH: + fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2); + break; + default: + dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw); + ret = -EINVAL; + goto err_xfer_unlock; } + regmap_update_bits(sai->regmap, SAI_FSCR, + SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr); + + if (sai->is_master_mode) { + bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params); + ret = clk_set_rate(sai->mclk, sai->mclk_rate); + if (ret) { + dev_err(sai->dev, "Failed to set mclk to %u: %pe\n", + sai->mclk_rate, ERR_PTR(ret)); + goto err_xfer_unlock; + } + + mclk_rate = clk_get_rate(sai->mclk); + if (mclk_rate < bclk_rate) { + dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n", + mclk_rate, bclk_rate); + ret = -EINVAL; + goto err_xfer_unlock; + } + + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + mclk_req_rate = bclk_rate * div_bclk; + + if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX || + mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) { + dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n", + mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX); + ret = -EINVAL; + goto err_xfer_unlock; + } + + regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, + SAI_CKR_MDIV(div_bclk)); + } + +err_xfer_unlock: + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: pm_runtime_put(sai->dev); return ret; @@ -704,6 +705,7 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; if (!rockchip_sai_stream_valid(substream, dai)) return 0; @@ -724,12 +726,13 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream, * udelay falls short. */ udelay(20); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | - SAI_XFER_FSS_MASK, - SAI_XFER_CLK_EN | - SAI_XFER_FSS_EN); + spin_lock_irqsave(&sai->xfer_lock, flags); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); + spin_unlock_irqrestore(&sai->xfer_lock, flags); } rockchip_sai_fsync_lost_detect(sai, 1); @@ -912,6 +915,7 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; unsigned int clk_gates; int sw = slot_width; @@ -927,16 +931,16 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; pm_runtime_get_sync(dai->dev); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { - rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); - regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(sw)); - regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(sw)); - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, - clk_gates); - } + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(sw)); + regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(sw)); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); pm_runtime_put(dai->dev); return 0; From fdf043f5f3bae150b678feae3d7bb1beed87ec14 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 16 Jun 2026 18:33:22 -0500 Subject: [PATCH 3939/5214] ASoC: tlv320aic3x: restrict CLKDIV bypass Q values in dual-rate mode The datasheet documents that when the PLL is disabled and dual-rate mode is enabled, only Q values {4, 8, 9, 12, 16} are valid for the CLKDIV bypass path; all other Q values produce invalid bitclock output. The existing loop iterates Q from 2 to 17 without this restriction, causing silent audio failure when an out-of-spec Q is picked. Restrict the Q search to the allowed set in dual-rate mode. Fixes: 4f9c16ccfa26 ("[ALSA] soc - tlv320aic3x - revisit clock setup") Suggested-by: Mir Jeffres Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260616233322.873081-1-sen@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index b12c1952823b..b38393a8130f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + static const u8 dual_rate_q[] = {4, 8, 9, 12, 16}; struct snd_soc_component *component = dai->component; struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; + bool dual_rate; int clk; int width = aic3x->slot_width; @@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, /* Fsref can be 44100 or 48000 */ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + dual_rate = params_rate(params) >= 64000; /* Try to find a value for Q which allows us to bypass the PLL and * generate CODEC_CLK directly. */ - for (pll_q = 2; pll_q < 18; pll_q++) - if (aic3x->sysclk / (128 * pll_q) == fsref) { - bypass_pll = 1; - break; + if (dual_rate) { + for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) { + pll_q = dual_rate_q[i]; + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } } + } else { + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + } if (bypass_pll) { pll_q &= 0xf; @@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, * right DAC to right channel input */ data = (LDAC2LCH | RDAC2RCH); data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; - if (params_rate(params) >= 64000) + if (dual_rate) data |= DUAL_RATE_MODE; snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); /* codec sample rate select */ data = (fsref * 20) / params_rate(params); - if (params_rate(params) < 64000) + if (!dual_rate) data /= 2; data /= 5; data -= 2; From 5096a1634def431cd7f89e2b0af4456de91dd26d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 15 Jun 2026 17:10:12 +0800 Subject: [PATCH 3940/5214] ASoC: rt5650: enhance spk protection function This patch adjusts several default settings to ensure the speaker protection function can be enabled safely. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260615091012.718168-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 6 ++---- sound/soc/codecs/rt5645.h | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 8a9af260e5f7..e9819653b30d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -83,6 +83,8 @@ static const struct reg_sequence rt5650_init_list[] = { {RT5645_PWR_ANLG1, 0x02}, {RT5645_IL_CMD3, 0x6728}, {RT5645_PR_BASE + 0x3a, 0x0000}, + {RT5645_CLSD_OUT_CTRL1, 0x4059}, + {RT5645_GEN_CTRL3, 0x0200}, }; static const struct reg_default rt5645_reg[] = { @@ -1855,13 +1857,9 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, RT5645_PWR_CLS_D_L, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L); - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1); break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS); snd_soc_component_write(component, RT5645_EQ_CTRL2, 0); snd_soc_component_update_bits(component, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index bef74b29fd54..a5bfe9861b8b 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -118,6 +118,7 @@ #define RT5645_A_JD_CTRL1 0x94 #define RT5645_VAD_CTRL4 0x9d #define RT5645_CLSD_OUT_CTRL 0xa0 +#define RT5645_CLSD_OUT_CTRL1 0xa1 /* Function - Digital */ #define RT5645_ADC_EQ_CTRL1 0xae #define RT5645_ADC_EQ_CTRL2 0xaf From 12aad822fb9a761f3a9d278083a5bdcb1524e5ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Jun 2026 11:24:06 +0200 Subject: [PATCH 3941/5214] spi: acpi: Free resource list at appropriate time We do unneeded "double free" (emptying an empty list) in one case. This is not a critical issue at all, the fix just makes code robust against any possible future changes in the flow. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260617092406.2649384-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ab37b50bbf79..d7e584afa301 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2979,12 +2979,12 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_spi_add_resource, &lookup); - acpi_dev_free_resource_list(&resource_list); - if (ret < 0) /* Found SPI in _CRS but it points to another controller */ return ERR_PTR(ret); + acpi_dev_free_resource_list(&resource_list); + if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && device_match_acpi_handle(lookup.ctlr->dev.parent, parent_handle)) { From 6cf83f43bfc36dccc35a7f3f12094b14c4c0dda4 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 1 Jun 2026 11:18:35 +0200 Subject: [PATCH 3942/5214] rtla: Fix and clean up .gitignore .gitignore includes several entries prone to unwanted matches in subdirectories. One of them, the recently added "lib/", matches the recently added directory "tests/scripts/lib/" in addition to the intended top-level "lib/", which contains object files built from sources in tools/lib. Add "/" to all .gitignore entries that are intended to only match top-level files or directories: rtla, rtla_static, unit_tests, libsubcmd/. Remove .gitignore entries that are not needed at all: - lib/ (contains only object files, ignored by top-level .gitignore already). - .txt rtla output files added to .gitignore in commit 02689ae385c5 ("rtla: Add generated output files to gitignore"). Since commit ad5b50a0959f ("rtla/tests: Run runtime tests in temporary directory"), those are created in a temporary directory, not in tools/tracing/rtla. Keeping libsubcmd/ as that contains other generated files (headers, archives, etc.). Fixes: 48209d763c22 ("rtla: Add libsubcmd dependency") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605291919.eszupseg-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202605300436.PqQ0Bc8q-lkp@intel.com/ Link: https://lore.kernel.org/r/20260601091835.3118094-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/.gitignore | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore index c7b4bf1c8ba9..2a1c99ddd1bf 100644 --- a/tools/tracing/rtla/.gitignore +++ b/tools/tracing/rtla/.gitignore @@ -1,14 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -rtla -rtla-static -unit_tests +/rtla +/rtla-static +/unit_tests fixdep feature FEATURE-DUMP *.skel.h -custom_filename.txt -osnoise_irq_noise_hist.txt -osnoise_trace.txt -timerlat_trace.txt -libsubcmd/ -lib/ +/libsubcmd/ From c35eb77a67515d4201bc91294f40761591f43bbd Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Thu, 4 Jun 2026 16:05:47 +0200 Subject: [PATCH 3943/5214] rtla/tests: Fix pgrep filter in get_workload_pids.sh Multiple runtime tests in RTLA rely on the get_workload_pids() shell helper function to get the PIDs of both kernel and user workloads. On some systems (e.g. Fedora 43), pgrep matches kernel thread names including square brackets: "[osnoise/0]"; on other systems (e.g. RHEL 9.8), brackets are not included: "osnoise/0". Accept both as valid workload PIDs rather that just the non-bracket form to make the tests work on all systems. Fixes: a98dad63cda3 ("rtla/tests: Add runtime test for -k and -u options") Reported-by: Crystal Wood Link: https://lore.kernel.org/r/20260604140547.3616495-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh index 8aff98cd2c1f..d10a4e3b321d 100644 --- a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh +++ b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh @@ -5,7 +5,7 @@ get_workload_pids() { local rtla_pid=$(ps -o ppid= $shell_pid) # kernel threads - pgrep -P $(pgrep ^kthreadd$) -f '^(osnoise|timerlat)/[0-9]+$' + pgrep -P $(pgrep ^kthreadd$) -f '^\[?(osnoise|timerlat)/[0-9]+\]?$' # user threads pgrep -P $rtla_pid | grep -v "^$shell_pid$" } From 8fa30821180a9a19e78e9f4df1c0ba710252801e Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 16 Jun 2026 12:09:14 +0500 Subject: [PATCH 3944/5214] timekeeping: Register default clocksource before taking tk_core.lock Commit f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") moved the jiffies clocksource registration into clocksource_default_clock(), so that it is registered lazily on the first call. __clocksource_register() acquires clocksource_mutex, but the first caller is timekeeping_init(), which invokes clocksource_default_clock() while holding tk_core.lock, a raw spinlock. Acquiring a sleeping mutex while holding a raw spinlock is invalid. The default clocksource only has to be registered before tk_setup_internals() consumes its mult/shift/maxadj. Neither clocksource_default_clock(), the ->enable() callback, nor the registration itself need tk_core.lock, so fetch and enable the clock before acquiring the lock. This preserves the "register before usage" ordering while keeping clocksource_mutex out of the raw spinlock section. clocksource_default_clock() has a second caller, clocksource_done_booting(), which invokes it with clocksource_mutex already held. That path avoids a recursive lock because timekeeping_init() has already run and set cs_jiffies_registered, so the registration is skipped there. This change does not alter that; it only fixes the invalid wait context in timekeeping_init(). Fixes: f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") Signed-off-by: Mikhail Gavrilov Signed-off-by: Thomas Gleixner Reported-by: Breno Leitao Reported-by: Oleg Nesterov Reviewed-by: Breno Leitao Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616070914.65818-1-mikhail.v.gavrilov@gmail.com --- kernel/time/timekeeping.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 0d5b67f609bb..b1b5ec43c0f2 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -2061,13 +2061,14 @@ void __init timekeeping_init(void) */ wall_to_mono = timespec64_sub(boot_offset, wall_time); + clock = clocksource_default_clock(); + if (clock->enable) + clock->enable(clock); + guard(raw_spinlock_irqsave)(&tk_core.lock); ntp_init(); - clock = clocksource_default_clock(); - if (clock->enable) - clock->enable(clock); tk_setup_internals(tks, clock); tk_set_xtime(tks, &wall_time); From 26aff38fefb1d6cd87e22525f41cc8f1aa61b24f Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 16 Jun 2026 19:20:17 +0800 Subject: [PATCH 3945/5214] posix-cpu-timers: Use u64 multiplication in update_rlimit_cpu() update_rlimit_cpu() converts the RLIMIT_CPU value to nanoseconds with u64 nsecs = rlim_new * NSEC_PER_SEC; On 32-bit kernels both rlim_new (unsigned long) and NSEC_PER_SEC (1000000000L) are 32-bit, so the multiplication is performed in unsigned long and truncated for rlim_new > 4 seconds before being widened to u64. The same file already casts to u64 for the matching computation in check_process_timers(): u64 softns = (u64)soft * NSEC_PER_SEC; As a result, the truncated value is installed into the CPUCLOCK_PROF expiry cache (nextevt), causing the process CPU timer to be programmed to fire prematurely for any RLIMIT_CPU soft limit >= 5 seconds. The actual SIGXCPU/SIGKILL decision in check_process_timers() already casts to u64 and is therefore correct, so limit enforcement is not broken; only the expiry-cache programming is wrong. Apply the same cast here so both paths convert rlim_cur identically. 64-bit kernels are unaffected. Fixes: 858cf3a8c599 ("timers/itimer: Convert internal cputime_t units to nsec") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616112017.1681372-1-zhanxusheng@xiaomi.com --- kernel/time/posix-cpu-timers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 74775b94d11b..5e633d8750d1 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -41,7 +41,7 @@ void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) */ int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new) { - u64 nsecs = rlim_new * NSEC_PER_SEC; + u64 nsecs = (u64)rlim_new * NSEC_PER_SEC; unsigned long irq_fl; if (!lock_task_sighand(task, &irq_fl)) From f8aceb1adb05896d66a3abbc1b0f41b90c9179ae Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 8 Jun 2026 20:43:13 -0700 Subject: [PATCH 3946/5214] hrtimer: Correct CONFIG_NO_HZ_COMMON macro name in comment A comment in kernel/time/hrtimer.c incorrectly refers to CONFIG_NOHZ_COMMON instead of CONFIG_NO_HZ_COMMON. Correct it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260609034314.25029-1-enelsonmoore@gmail.com --- kernel/time/hrtimer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 638ce623c342..313dcea127fe 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -799,7 +799,7 @@ static inline void hrtimer_switch_to_hres(void) { } * * This is only invoked when: * - CONFIG_HIGH_RES_TIMERS is enabled. - * - CONFIG_NOHZ_COMMON is enabled + * - CONFIG_NO_HZ_COMMON is enabled * * For the other cases this function is empty and because the call sites * are optimized out it vanishes as well, i.e. no need for lots of From d189f224308c8ac3feeea8e442c99922bd18f1b2 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 14 Jun 2026 09:56:35 +0200 Subject: [PATCH 3947/5214] NFS: Prevent resource leak in nfs_alloc_server() It was overlooked to call ida_free() after a failed nfs_alloc_iostats() call. Thus add the missed function call in an if branch. Fixes: 1c7251187dc067a6d460cf33ca67da9c1dd87807 ("NFS: add superblock sysfs entries") Cc: stable@vger.kernel.org Reported-by: Christophe Jaillet Closes: https://lore.kernel.org/linux-nfs/1c8e10c9-def7-4f0d-8aa1-23c8035a38c8@wanadoo.fr/ Signed-off-by: Markus Elfring Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index be02bb227741..0781b15e7e05 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1074,6 +1074,7 @@ struct nfs_server *nfs_alloc_server(void) server->io_stats = nfs_alloc_iostats(); if (!server->io_stats) { + ida_free(&s_sysfs_ids, server->s_sysfs_id); kfree(server); return NULL; } From 284ea3fb4f6715201e1d9ef3474c25e817ad70e9 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 14 Jun 2026 10:06:11 +0200 Subject: [PATCH 3948/5214] NFS: Use common error handling code in nfs_alloc_server() Use an additional label so that a bit of exception handling can be better reused at the end of this function implementation. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0781b15e7e05..48ce013b30b0 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1049,10 +1049,8 @@ struct nfs_server *nfs_alloc_server(void) return NULL; server->s_sysfs_id = ida_alloc(&s_sysfs_ids, GFP_KERNEL); - if (server->s_sysfs_id < 0) { - kfree(server); - return NULL; - } + if (server->s_sysfs_id < 0) + goto free_server; server->client = server->client_acl = ERR_PTR(-EINVAL); @@ -1075,8 +1073,7 @@ struct nfs_server *nfs_alloc_server(void) server->io_stats = nfs_alloc_iostats(); if (!server->io_stats) { ida_free(&s_sysfs_ids, server->s_sysfs_id); - kfree(server); - return NULL; + goto free_server; } server->change_attr_type = NFS4_CHANGE_TYPE_IS_UNDEFINED; @@ -1090,6 +1087,10 @@ struct nfs_server *nfs_alloc_server(void) rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC"); return server; + +free_server: + kfree(server); + return NULL; } EXPORT_SYMBOL_GPL(nfs_alloc_server); From 9ef450ca74e43dacf9a2a15db7a851052c78dcf0 Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Tue, 16 Jun 2026 23:47:33 +0800 Subject: [PATCH 3949/5214] cpufreq: schedutil: Fix uncleared need_freq_update on the .adjust_perf() path The need_freq_update flag makes sugov_should_update_freq() return true regardless of the rate_limit_us throttling, and is cleared in sugov_update_next_freq(). sugov_update_single_freq() and sugov_update_shared() go through that helper, so the flag does not persist there. However, sugov_update_single_perf(), used by drivers implementing the .adjust_perf() callback (e.g. intel_pstate or amd-pstate in passive mode) calls cpufreq_driver_adjust_perf() directly and never goes through sugov_update_next_freq(), so the need_freq_update flag is not cleared in that path. Before commit 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()"), this was effectively harmless because sugov_should_update_freq() still honored the rate limit even when need_freq_update was set. After that change, the flag forces sugov_should_update_freq() to always return true, so once set, it stays effective indefinitely on the .adjust_perf() path. As a result, cpufreq_driver_adjust_perf() gets called on every scheduler utilization update (with the runqueue lock held) rather than being throttled by rate_limit_us, even if the driver itself may skip redundant hardware updates. Clear need_freq_update at the end of the adjust_perf path as well. Fixes: 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()") Signed-off-by: Zhongqiu Han Reviewed-by: Hongyan Xia Reviewed-by: Christian Loehle Cc: All applicable [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260616154733.2405236-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki --- kernel/sched/cpufreq_schedutil.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index ae9fd211cec1..a4e689eefdfb 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -486,6 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, sg_cpu->util, max_cap); + sg_policy->need_freq_update = false; sg_policy->last_freq_update_time = time; } From 3f0cc1735273a57c5116710cf0202e12152f59cc Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Thu, 4 Jun 2026 12:59:11 -0400 Subject: [PATCH 3950/5214] drm/amdkfd: Avoid double-unpin of DOORBELL/MMIO BOs on free amdgpu_amdkfd_gpuvm_free_memory_of_gpu() unpinned DOORBELL and MMIO remap BOs (which are pinned at allocation time) before checking whether the BO is still mapped to the GPU. When the BO is still mapped, the function returns -EBUSY and leaves the BO alive, but it has already been unpinned. The BO is then unpinned again when it is finally freed during process teardown, triggering a ttm_bo_unpin() underflow warning: WARNING: CPU: 18 PID: 15066 at ttm/ttm_bo.c:650 amdttm_bo_unpin+0x6d/0x80 [amdttm] Workqueue: kfd_process_wq kfd_process_wq_release [amdgpu] RIP: 0010:amdttm_bo_unpin+0x6d/0x80 [amdttm] Call Trace: amdgpu_bo_unpin+0x1a/0x90 [amdgpu] amdgpu_amdkfd_gpuvm_unpin_bo+0x31/0xb0 [amdgpu] amdgpu_amdkfd_gpuvm_free_memory_of_gpu+0x3bf/0x460 [amdgpu] kfd_process_free_outstanding_kfd_bos+0xd4/0x170 [amdgpu] kfd_process_wq_release+0x109/0x1b0 [amdgpu] process_one_work+0x1e2/0x3b0 worker_thread+0x50/0x3a0 kthread+0xdd/0x100 ret_from_fork+0x29/0x50 Move the unpin after the mapped_to_gpu_memory check so it only happens once we are committed to freeing the BO. Fixes: d25e35bc26c3 ("drm/amdgpu: Pin MMIO/DOORBELL BO's in GTT domain") Signed-off-by: Yunxiang Li Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 927c5b2defb9b09856444d94bebfd056a002bd75) --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index d54794e5b18b..35fe2c974699 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1914,13 +1914,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( mutex_lock(&mem->lock); - /* Unpin MMIO/DOORBELL BO's that were pinned during allocation */ - if (mem->alloc_flags & - (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | - KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { - amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); - } - mapped_to_gpu_memory = mem->mapped_to_gpu_memory; is_imported = mem->is_imported; mutex_unlock(&mem->lock); @@ -1934,6 +1927,15 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( return -EBUSY; } + /* At this point the BO is guaranteed to be freed, so unpin the + * MMIO/DOORBELL BOs that were pinned during allocation. + */ + if (mem->alloc_flags & + (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | + KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { + amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); + } + /* Make sure restore workers don't access the BO any more */ mutex_lock(&process_info->lock); if (!list_empty(&mem->validate_list)) From 1c2d7a656655a50bd1b7227fb26d173959a1955d Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Tue, 26 May 2026 14:45:48 +0800 Subject: [PATCH 3951/5214] drm/amdgpu: initialize iter.start in amdgpu_devcoredump_format This fixes read /sys/class/drm/cardN/device/devcoredump/data return empty content sometimes. amdgpu_devcoredump_format() leaves struct drm_print_iterator's .start field uninitialized on the stack before passing it to drm_coredump_printer(). __drm_puts_coredump() compares the running .offset against .start to decide whether to skip or copy each chunk: if (iterator->offset < iterator->start) { if (iterator->offset + len <= iterator->start) { iterator->offset += len; return; } ... } Fixes: 4bbba79a7f1d ("drm/amdgpu: move devcoredump generation to a worker") Acked-by: Alex Deucher Signed-off-by: Qiang Yu Signed-off-by: Alex Deucher (cherry picked from commit cd6397b7af8262a380e188dc32e9de11ff897ed2) --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 27830518a230..bed68f0c3080 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -229,6 +229,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf sizing_pass = buffer == NULL; iter.data = buffer; + iter.start = 0; iter.offset = 0; iter.remain = count; From 76589bcc73f477ef2b3b90e4fae6a7a4dfd925af Mon Sep 17 00:00:00 2001 From: Shubhankar Milind Sardeshpande Date: Thu, 21 May 2026 10:55:18 +0530 Subject: [PATCH 3952/5214] drm/amd/pm: re-enable MC access after PrepareMp1ForUnload on SMU V15 APUs During smu_v15_0_0_system_features_control(), the driver sends a PrepareMp1ForUnload message to PMFW. PMFW then performs nBIF and SYSHUB function-level resets (FLR), disabling PCIe CFG space reset, which clears the framebuffer enable bit to zero and disables MC (memory controller) access from the host. Re-enable MC access via the nbio mc_access_enable callback right after PrepareMp1ForUnload completes in smu_v15_0_0_system_features_control(). Signed-off-by: Shubhankar Milind Sardeshpande Signed-off-by: Suresh Guttula Signed-off-by: Alex Deucher (cherry picked from commit 840a3c5aeae779a3bc75d7f747c3ed18b1af6507) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index fb1145691410..a214ddbd4c86 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -227,9 +227,14 @@ static int smu_v15_0_0_system_features_control(struct smu_context *smu, bool en) struct amdgpu_device *adev = smu->adev; int ret = 0; - if (!en && !adev->in_s0ix) + if (!en && !adev->in_s0ix) { ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL); + /* SMU resets BIF_FB_EN to zero, re-enable MC access on APUs with SMU V15 */ + if (!ret && adev->nbio.funcs && adev->nbio.funcs->mc_access_enable) + adev->nbio.funcs->mc_access_enable(adev, true); + } + return ret; } From 20a5e7ffdfecddc34c60a6b4483f42acf3d8731d Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 4 Jun 2026 15:04:03 -0400 Subject: [PATCH 3953/5214] drm/amdkfd: Properly acquire queue buffers in CRIU restore When kfd_queue_acquire_buffers() was split off from set_queue_properties_from_user(), set_queue_properties_from_criu() was missed. Thus, set_queue_properties_from_criu() is not filling out the buffer fields of queue_properties, which can come up when subsequent code expects them to be non-null. Add the proper call to kfd_queue_acquire_buffers(), and also use the right cast types in set_queue_properties_from_criu() (which were missed at the same time) Signed-off-by: David Francis Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 88ed96abbbe27b70193544fbc1ee06448c274714) --- .../gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 44e39ce222b7..0ac35789b239 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -962,8 +962,8 @@ static void set_queue_properties_from_criu(struct queue_properties *qp, qp->priority = q_data->priority; qp->queue_address = q_data->q_address; qp->queue_size = q_data->q_size; - qp->read_ptr = (uint32_t *) q_data->read_ptr_addr; - qp->write_ptr = (uint32_t *) q_data->write_ptr_addr; + qp->read_ptr = (void __user *)q_data->read_ptr_addr; + qp->write_ptr = (void __user *)q_data->write_ptr_addr; qp->eop_ring_buffer_address = q_data->eop_ring_buffer_address; qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size; qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address; @@ -1042,10 +1042,18 @@ int kfd_criu_restore_queue(struct kfd_process *p, memset(&qp, 0, sizeof(qp)); set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask)); + ret = kfd_queue_acquire_buffers(pdd, &qp); + if (ret) { + pr_debug("failed to acquire user queue buffers for CRIU\n"); + goto exit; + } + print_queue_properties(&qp); ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL); if (ret) { + kfd_queue_unref_bo_vas(pdd, &qp); + kfd_queue_release_buffers(pdd, &qp); pr_err("Failed to create new queue err:%d\n", ret); goto exit; } From b29aaf0a72222bfafe4d73899dadb0486b5bcb09 Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Fri, 5 Jun 2026 08:59:34 -0400 Subject: [PATCH 3954/5214] drm/amdgpu: skip already suspended IP blocks in ip_suspend_phase2 The GPU reload test (S3 / mode1 reset / module reload) triggers a WARN_ON in amdgpu_irq_put() on gfx10 when unloading amdgpu: WARNING: CPU: 0 PID: 2314 at amd/amdgpu/amdgpu_irq.c:676 amdgpu_irq_put+0xc3/0xe0 [amdgpu] Call Trace: gfx_v10_0_hw_fini+0x41/0x150 [amdgpu] amdgpu_ip_block_hw_fini+0x29/0xc0 [amdgpu] amdgpu_device_fini_hw+0x315/0x610 [amdgpu] amdgpu_driver_unload_kms+0x7c/0x90 [amdgpu] amdgpu_pci_remove+0x51/0x90 [amdgpu] amdgpu_device_ip_resume_phase2() skips IP blocks whose status.hw is already set, but amdgpu_device_ip_suspend_phase2() never had the matching guard, so a block can be suspended twice (e.g. a reset or recovery issued while the device is already suspended). The second suspend runs hw_fini again, which now releases the gfx fault IRQs unconditionally, dropping a refcount that is already zero and tripping the WARN_ON in amdgpu_irq_put(). The fault/EOP IRQ get/put were balanced through late_init/hw_fini before, which masked the double-suspend; moving the get into hw_init made the suspend/resume asymmetry visible as an IRQ refcount underflow. Honor status.hw in ip_suspend_phase2() so suspend mirrors resume and a block is only torn down once. Fixes: 9117d8be850b ("drm/amdgpu/gfx: move fault and EOP IRQ get/put to hw_init/hw_fini") Fixes: 482f0e538580 ("drm/amdgpu: fix double ucode load by PSP(v3)") Signed-off-by: Yunxiang Li Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit f44f2af13c418969be358b15743f939d705de998) --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 942f0251c748..0fa2ce36c2ea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3043,7 +3043,7 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev) amdgpu_dpm_gfx_state_change(adev, sGpuChangeState_D3Entry); for (i = adev->num_ip_blocks - 1; i >= 0; i--) { - if (!adev->ip_blocks[i].status.valid) + if (!adev->ip_blocks[i].status.valid || !adev->ip_blocks[i].status.hw) continue; /* displays are handled in phase1 */ if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_DCE) From fe7945d092a1d3c340febc2ab176cee50d0f6c80 Mon Sep 17 00:00:00 2001 From: Roman Li Date: Wed, 20 May 2026 16:50:34 -0400 Subject: [PATCH 3955/5214] drm/amd/display: Skip PHY SSC reduction on some 8K panels [Why] Some 8K displays cannot tolerate the reduced phy ssc value at high link utilization and show corruption or black screen. [How] Add an EDID panel-id quirk to utilize existing skip_phy_ssc_reduction flag. To pass the link into the quirk handler, change the signature of apply_edid_quirks() to take link as an argument. The dev local in dm_helpers_parse_edid_caps() becomes unused and is removed. Fixes: 5fa62c87cffd ("drm/amd/display: Add option to disable PHY SSC reduction on transmitter enable") Reviewed-by: Alex Hung Signed-off-by: Roman Li Signed-off-by: Aurabindo Pillai Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 144169e7be0831e09958a906d08d1856751aa6c6) --- .../drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index f257ea91a34d..c6f94eb71ffa 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -95,8 +95,11 @@ static u32 edid_extract_panel_id(struct edid *edid) (u32)EDID_PRODUCT_ID(edid); } -static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct dc_edid_caps *edid_caps) +static void apply_edid_quirks(struct dc_link *link, struct edid *edid, + struct dc_edid_caps *edid_caps) { + struct amdgpu_dm_connector *aconnector = link->priv; + struct drm_device *dev = aconnector->base.dev; uint32_t panel_id = edid_extract_panel_id(edid); switch (panel_id) { @@ -126,6 +129,11 @@ static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct drm_dbg_driver(dev, "Disabling VSC on monitor with panel id %X\n", panel_id); edid_caps->panel_patch.disable_colorimetry = true; break; + /* Workaround for monitors that get corrupted by the PHY SSC reduction */ + case drm_edid_encode_panel_id('D', 'E', 'L', 0x4147): + drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id); + link->wa_flags.skip_phy_ssc_reduction = true; + break; default: return; } @@ -147,7 +155,6 @@ enum dc_edid_status dm_helpers_parse_edid_caps( { struct amdgpu_dm_connector *aconnector = link->priv; struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; struct edid *edid_buf = edid ? (struct edid *) edid->raw_edid : NULL; struct cea_sad *sads; int sad_count = -1; @@ -188,7 +195,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps( edid_caps->frl_dsc_max_frl_rate, edid_caps->frl_dsc_total_chunk_kbytes); } - apply_edid_quirks(dev, edid_buf, edid_caps); + apply_edid_quirks(link, edid_buf, edid_caps); sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads); if (sad_count <= 0) From 5cc0f35d83e2c72f70edaf7478db350af3082a17 Mon Sep 17 00:00:00 2001 From: Ivan Lipski Date: Thu, 28 May 2026 12:28:51 -0400 Subject: [PATCH 3956/5214] drm/amd/display: Restore periodic detection for DCN35 [Why&How] Periodic detection callbacks from DCN35 was removed for higher IPS residency causing some displays to fail to recover after DPMS sleep. The monitors bounces HPD ~1.2s after link training, and without periodic detection the system enters IPS with no mechanism to wake and rediscover the display. Restore the periodic detection calls in dcn35_clk_mgr for now. It should be replaced with a proper IPS-aware solution long term using DMUB. Also remove it from dcn31 and dcn314_clk_mgr.c since they do not have IPS, thus should not affect them. Fixes: 3f6c060846be ("drm/amd/display: Remove periodic detection callbacks from dcn35+") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5318 Reviewed-by: Nicholas Kazlauskas Signed-off-by: Ivan Lipski Signed-off-by: Aurabindo Pillai Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 0c300e6a76916e944b6b18a64c73f7895a0fee87) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c | 2 -- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c | 2 -- drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c | 2 ++ 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c index 00c4be7c3aa4..ff47af3854b6 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c @@ -158,7 +158,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn31_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -184,7 +183,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn31_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index dd6f11ecb9c9..24f6304011ae 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -230,7 +230,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn314_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -255,7 +254,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn314_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 103013e2a0de..a69824e1eb26 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -419,6 +419,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn35_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); + dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -438,6 +439,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn35_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); + dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } From 1142738572ef3fcf8b169f1c48d94b4a71cc2d97 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Thu, 28 May 2026 10:32:52 -0400 Subject: [PATCH 3957/5214] drm/amdkfd: Fix SMI event PID reporting for containers SMI events were reporting incorrect PIDs in containerized environments, causing test failures where container processes expected to see their namespace-local PIDs but instead received global host PIDs. The issue had two root causes: 1. Event functions were called from kernel context (page fault handlers, migration workers) where 'current' refers to the kernel worker thread, not the userspace GPU process that triggered the event. 2. PID conversion used task_tgid_vnr() which returns the PID in the caller's namespace (init namespace for kernel threads), not the task's own namespace. This patch updates the SMI event interface: - Change 8 event function signatures to accept task_struct pointer instead of pid_t, allowing proper namespace-aware PID conversion - Convert PIDs using task_tgid_nr_ns(task, task_active_pid_ns(task)) which returns the PID as the process sees it via getpid() - Update 10 call sites to pass p->lead_thread (the GPU process) instead of p->lead_thread->pid or current (kernel worker) This ensures SMI events report container-local PIDs, which is critical for containerized GPU workloads to correctly correlate events with their processes. Tested-by: Andrew Martin Assisted-by: Claude:Sonnet 4-5 Signed-off-by: Andrew Martin Reviewed-by: Harish Kasiviswanathan Signed-off-by: Alex Deucher (cherry picked from commit 60271ec06e04ba5d69d68714f3abdf637d86c257) --- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 8 +- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 6 +- drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c | 99 +++++++++++++-------- drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h | 14 +-- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 6 +- 5 files changed, 77 insertions(+), 56 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 28dc6886c1ff..226e76ae0be7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -424,7 +424,7 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, migrate.dst = migrate.src + npages; scratch = (dma_addr_t *)(migrate.dst + npages); - kfd_smi_event_migration_start(node, p->lead_thread->pid, + kfd_smi_event_migration_start(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, 0, node->id, prange->prefetch_loc, prange->preferred_loc, trigger); @@ -462,7 +462,7 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, out_free: kvfree(buf); - kfd_smi_event_migration_end(node, p->lead_thread->pid, + kfd_smi_event_migration_end(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, 0, node->id, trigger, r); out: @@ -727,7 +727,7 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, migrate.fault_page = fault_page; scratch = (dma_addr_t *)(migrate.dst + npages); - kfd_smi_event_migration_start(node, p->lead_thread->pid, + kfd_smi_event_migration_start(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, node->id, 0, prange->prefetch_loc, prange->preferred_loc, trigger); @@ -766,7 +766,7 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, out_free: kvfree(buf); - kfd_smi_event_migration_end(node, p->lead_thread->pid, + kfd_smi_event_migration_end(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, node->id, 0, trigger, r); out: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 368283d53077..63815be995fc 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -1969,7 +1969,7 @@ int kfd_process_evict_queues(struct kfd_process *p, uint32_t trigger) struct kfd_process_device *pdd = p->pdds[i]; struct device *dev = pdd->dev->adev->dev; - kfd_smi_event_queue_eviction(pdd->dev, p->lead_thread->pid, + kfd_smi_event_queue_eviction(pdd->dev, p->lead_thread, trigger); r = pdd->dev->dqm->ops.evict_process_queues(pdd->dev->dqm, @@ -1999,7 +1999,7 @@ int kfd_process_evict_queues(struct kfd_process *p, uint32_t trigger) if (n_evicted == 0) break; - kfd_smi_event_queue_restore(pdd->dev, p->lead_thread->pid); + kfd_smi_event_queue_restore(pdd->dev, p->lead_thread); if (pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm, &pdd->qpd)) @@ -2022,7 +2022,7 @@ int kfd_process_restore_queues(struct kfd_process *p) struct kfd_process_device *pdd = p->pdds[i]; struct device *dev = pdd->dev->adev->dev; - kfd_smi_event_queue_restore(pdd->dev, p->lead_thread->pid); + kfd_smi_event_queue_restore(pdd->dev, p->lead_thread); r = pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm, &pdd->qpd); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c index dfbde5a571f6..e659cd50eb0b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c @@ -195,17 +195,35 @@ static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev, rcu_read_unlock(); } +/** + * kfd_smi_task_to_pid - Convert task to namespace-aware PID + * @task: task_struct pointer (typically p->lead_thread) + * + * Returns the PID as it appears in the task's own PID namespace. + * For containerized processes, this returns the container-local PID + * (what getpid() returns), not the global host PID. + * + * Returns 0 if task is NULL. + */ +static inline pid_t kfd_smi_task_to_pid(struct task_struct *task) +{ + return task ? task_tgid_nr_ns(task, task_active_pid_ns(task)) : 0; +} + __printf(4, 5) -static void kfd_smi_event_add(pid_t pid, struct kfd_node *dev, +static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev, unsigned int event, char *fmt, ...) { char fifo_in[KFD_SMI_EVENT_MSG_SIZE]; int len; va_list args; + pid_t pid; if (list_empty(&dev->smi_clients)) return; + pid = kfd_smi_task_to_pid(task); + len = snprintf(fifo_in, sizeof(fifo_in), "%x ", event); va_start(args, fmt); @@ -234,14 +252,15 @@ void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset, amdgpu_reset_get_desc(reset_context, reset_cause, sizeof(reset_cause)); - kfd_smi_event_add(0, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET( + kfd_smi_event_add(NULL, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET( dev->reset_seq_num, reset_cause)); } void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev, uint64_t throttle_bitmask) { - kfd_smi_event_add(0, dev, KFD_SMI_EVENT_THERMAL_THROTTLE, KFD_EVENT_FMT_THERMAL_THROTTLING( + kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_THERMAL_THROTTLE, + KFD_EVENT_FMT_THERMAL_THROTTLING( throttle_bitmask, amdgpu_dpm_get_thermal_throttling_counter(dev->adev))); } @@ -254,67 +273,67 @@ void kfd_smi_event_update_vmfault(struct kfd_node *dev, uint16_t pasid) if (task_info) { /* Report VM faults from user applications, not retry from kernel */ if (task_info->task.pid) - kfd_smi_event_add(task_info->tgid, dev, - KFD_SMI_EVENT_VMFAULT, - KFD_EVENT_FMT_VMFAULT(task_info->task.pid, - task_info->task.comm)); + kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_VMFAULT, KFD_EVENT_FMT_VMFAULT( + task_info->task.pid, task_info->task.comm)); amdgpu_vm_put_task_info(task_info); } } -void kfd_smi_event_page_fault_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *task, unsigned long address, bool write_fault, ktime_t ts) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_PAGE_FAULT_START, - KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts), pid, - address, node->id, write_fault ? 'W' : 'R')); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_START, + KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts), + kfd_smi_task_to_pid(task), address, node->id, + write_fault ? 'W' : 'R')); } -void kfd_smi_event_page_fault_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task, unsigned long address, bool migration) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_PAGE_FAULT_END, + kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_END, KFD_EVENT_FMT_PAGEFAULT_END(ktime_get_boottime_ns(), - pid, address, node->id, migration ? 'M' : 'U')); + kfd_smi_task_to_pid(task), address, node->id, + migration ? 'M' : 'U')); } -void kfd_smi_event_migration_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t prefetch_loc, uint32_t preferred_loc, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_MIGRATE_START, - KFD_EVENT_FMT_MIGRATE_START( - ktime_get_boottime_ns(), pid, start, end - start, - from, to, prefetch_loc, preferred_loc, trigger)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_START, + KFD_EVENT_FMT_MIGRATE_START(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), start, end - start, from, + to, prefetch_loc, preferred_loc, trigger)); } -void kfd_smi_event_migration_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t trigger, int error_code) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_MIGRATE_END, - KFD_EVENT_FMT_MIGRATE_END( - ktime_get_boottime_ns(), pid, start, end - start, - from, to, trigger, error_code)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_END, + KFD_EVENT_FMT_MIGRATE_END(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), start, end - start, from, + to, trigger, error_code)); } -void kfd_smi_event_queue_eviction(struct kfd_node *node, pid_t pid, +void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_QUEUE_EVICTION, - KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(), pid, - node->id, trigger)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_EVICTION, + KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), node->id, trigger)); } -void kfd_smi_event_queue_restore(struct kfd_node *node, pid_t pid) +void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_QUEUE_RESTORE, - KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), pid, - node->id, '0')); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_RESTORE, + KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), node->id, '0')); } void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm) @@ -329,21 +348,23 @@ void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm) for (i = 0; i < p->n_pdds; i++) { struct kfd_process_device *pdd = p->pdds[i]; - kfd_smi_event_add(p->lead_thread->pid, pdd->dev, + kfd_smi_event_add(p->lead_thread, pdd->dev, KFD_SMI_EVENT_QUEUE_RESTORE, KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), - p->lead_thread->pid, pdd->dev->id, 'R')); + kfd_smi_task_to_pid(p->lead_thread), + pdd->dev->id, 'R')); } kfd_unref_process(p); } -void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, pid_t pid, +void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *task, unsigned long address, unsigned long last, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_UNMAP_FROM_GPU, + kfd_smi_event_add(task, node, KFD_SMI_EVENT_UNMAP_FROM_GPU, KFD_EVENT_FMT_UNMAP_FROM_GPU(ktime_get_boottime_ns(), - pid, address, last - address + 1, node->id, trigger)); + kfd_smi_task_to_pid(task), address, + last - address + 1, node->id, trigger)); } void kfd_smi_event_process(struct kfd_process_device *pdd, bool start) @@ -358,7 +379,7 @@ void kfd_smi_event_process(struct kfd_process_device *pdd, bool start) task_info = amdgpu_vm_get_task_info_vm(avm); if (task_info) { - kfd_smi_event_add(task_info->tgid, pdd->dev, + kfd_smi_event_add(NULL, pdd->dev, start ? KFD_SMI_EVENT_PROCESS_START : KFD_SMI_EVENT_PROCESS_END, KFD_EVENT_FMT_PROCESS(task_info->task.pid, @@ -387,7 +408,7 @@ int kfd_smi_event_open(struct kfd_node *dev, uint32_t *fd) spin_lock_init(&client->lock); client->events = 0; client->dev = dev; - client->pid = current->tgid; + client->pid = kfd_smi_task_to_pid(current); client->suser = capable(CAP_SYS_ADMIN); spin_lock(&dev->smi_lock); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h index bb4d72b57387..afa93d7cfa7f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h @@ -32,25 +32,25 @@ void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev, uint64_t throttle_bitmask); void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset, struct amdgpu_reset_context *reset_context); -void kfd_smi_event_page_fault_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *task, unsigned long address, bool write_fault, ktime_t ts); -void kfd_smi_event_page_fault_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task, unsigned long address, bool migration); -void kfd_smi_event_migration_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t prefetch_loc, uint32_t preferred_loc, uint32_t trigger); -void kfd_smi_event_migration_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t trigger, int error_code); -void kfd_smi_event_queue_eviction(struct kfd_node *node, pid_t pid, +void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task, uint32_t trigger); -void kfd_smi_event_queue_restore(struct kfd_node *node, pid_t pid); +void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task); void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm); -void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, pid_t pid, +void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *task, unsigned long address, unsigned long last, uint32_t trigger); void kfd_smi_event_process(struct kfd_process_device *pdd, bool start); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 3841943da5ec..d64d104783d4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1408,7 +1408,7 @@ svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start, return -EINVAL; } - kfd_smi_event_unmap_from_gpu(pdd->dev, p->lead_thread->pid, + kfd_smi_event_unmap_from_gpu(pdd->dev, p->lead_thread, start, last, trigger); r = svm_range_unmap_from_gpu(pdd->dev->adev, @@ -3205,7 +3205,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid, svms, prange->start, prange->last, best_loc, prange->actual_loc); - kfd_smi_event_page_fault_start(node, p->lead_thread->pid, addr, + kfd_smi_event_page_fault_start(node, p->lead_thread, addr, write_fault, timestamp); /* Align migration range start and size to granularity size */ @@ -3248,7 +3248,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid, r, svms, start, last); out_migrate_fail: - kfd_smi_event_page_fault_end(node, p->lead_thread->pid, addr, + kfd_smi_event_page_fault_end(node, p->lead_thread, addr, migration); out_unlock_range: From 3d3372add033528ca90d1949abb52142504f5d23 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Fri, 5 Jun 2026 15:28:40 +0800 Subject: [PATCH 3958/5214] drm/amdgpu: allocate lockdep mutex on the heap to fix stack overflow Replace the stack-allocated amdgpu_lockdep mutex with a heap allocation via kmalloc to fix a stack overflow caused by the large struct size. Signed-off-by: Prike Liang Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit dbae980eefb2f46f31cee12f1f8540d0d79f61ae) --- drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c | 103 ++++++++++---------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c index d5d71fd7c70d..61450af539a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c @@ -16,6 +16,17 @@ #ifdef CONFIG_LOCKDEP +struct amdgpu_lockdep_dummy_locks { + struct mutex reset_lock; + struct mutex userq_sch_mutex; + struct mutex userq_mutex; + struct mutex notifier_lock; + struct mutex vram_lock; + struct mutex srbm_mutex; + struct mutex grbm_idx_mutex; + spinlock_t mmio_idx_lock; +}; + /* Lock class keys for associating with real driver locks */ static struct lock_class_key amdgpu_userq_sch_mutex_key; static struct lock_class_key amdgpu_userq_mutex_key; @@ -84,72 +95,65 @@ void amdgpu_lockdep_set_class(struct amdgpu_device *adev) int amdgpu_lockdep_init(void) { struct amdgpu_reset_domain *reset_domain = NULL; - struct amdgpu_reset_control reset_ctl; - struct mutex userq_sch_mutex; - struct mutex userq_mutex; - struct mutex notifier_lock; - struct mutex vram_lock; - struct mutex srbm_mutex; - struct mutex grbm_idx_mutex; - spinlock_t mmio_idx_lock; + struct amdgpu_lockdep_dummy_locks *locks; unsigned long flags; + locks = kzalloc(sizeof(*locks), GFP_KERNEL); + if (!locks) + return -ENOMEM; + /* * Initialize dummy reset domain */ reset_domain = amdgpu_reset_create_reset_domain(SINGLE_DEVICE, "lockdep_test"); - if (!reset_domain) + if (!reset_domain) { + kfree(locks); return -ENOMEM; - + } /* Initialize dummy locks */ - mutex_init(&userq_sch_mutex); - mutex_init(&userq_mutex); - mutex_init(¬ifier_lock); - mutex_init(&vram_lock); - mutex_init(&reset_ctl.reset_lock); - mutex_init(&srbm_mutex); - mutex_init(&grbm_idx_mutex); - spin_lock_init(&mmio_idx_lock); + mutex_init(&locks->userq_sch_mutex); + mutex_init(&locks->userq_mutex); + mutex_init(&locks->notifier_lock); + mutex_init(&locks->vram_lock); + mutex_init(&locks->reset_lock); + mutex_init(&locks->srbm_mutex); + mutex_init(&locks->grbm_idx_mutex); + spin_lock_init(&locks->mmio_idx_lock); /* * Associate dummy locks with the same class keys used for real * driver locks. This ensures lockdep connects the ordering learned * here with the actual locks used at runtime. */ - lockdep_set_class(&userq_sch_mutex, &amdgpu_userq_sch_mutex_key); - lockdep_set_class(&userq_mutex, &amdgpu_userq_mutex_key); - lockdep_set_class(¬ifier_lock, &amdgpu_notifier_lock_key); - lockdep_set_class(&vram_lock, &amdgpu_vram_lock_key); + lockdep_set_class(&locks->userq_sch_mutex, &amdgpu_userq_sch_mutex_key); + lockdep_set_class(&locks->userq_mutex, &amdgpu_userq_mutex_key); + lockdep_set_class(&locks->notifier_lock, &amdgpu_notifier_lock_key); + lockdep_set_class(&locks->vram_lock, &amdgpu_vram_lock_key); lockdep_set_class(&reset_domain->sem, &amdgpu_reset_sem_key); - lockdep_set_class(&reset_ctl.reset_lock, &amdgpu_reset_lock_key); - lockdep_set_class(&srbm_mutex, &amdgpu_srbm_lock_key); - lockdep_set_class(&grbm_idx_mutex, &amdgpu_grbm_lock_key); - lockdep_set_class(&mmio_idx_lock, &amdgpu_mmio_lock_key); - + lockdep_set_class(&locks->reset_lock, &amdgpu_reset_lock_key); + lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key); + lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key); + lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key); /* * Take locks in the correct order to train lockdep. * This establishes the dependency chain. */ /* Level 1: Global userq scheduler mutex (outermost) */ - mutex_lock(&userq_sch_mutex); + mutex_lock(&locks->userq_sch_mutex); /* Level 2: Per-context userq mutex */ - mutex_lock(&userq_mutex); - + mutex_lock(&locks->userq_mutex); /* Level 3: MMU notifier lock */ - mutex_lock(¬ifier_lock); - + mutex_lock(&locks->notifier_lock); /* Level 4: VRAM allocator lock */ - mutex_lock(&vram_lock); - + mutex_lock(&locks->vram_lock); /* Level 5: Reset domain semaphore */ down_read(&reset_domain->sem); /* Level 6: Reset control lock */ - mutex_lock(&reset_ctl.reset_lock); - + mutex_lock(&locks->reset_lock); /* * Mark potential memory reclaim boundary. * GPU operations might trigger memory allocation/reclaim. @@ -157,36 +161,35 @@ int amdgpu_lockdep_init(void) fs_reclaim_acquire(GFP_KERNEL); /* Level 7: SRBM register access */ - mutex_lock(&srbm_mutex); - + mutex_lock(&locks->srbm_mutex); /* Level 8: GRBM index access */ - mutex_lock(&grbm_idx_mutex); + mutex_lock(&locks->grbm_idx_mutex); /* Level 9: MMIO index access (innermost lock, spinlock) */ - spin_lock_irqsave(&mmio_idx_lock, flags); - + spin_lock_irqsave(&locks->mmio_idx_lock, flags); /* * All locks acquired in order. * Lockdep has now learned the valid dependency chain. */ /* Release in reverse order */ - spin_unlock_irqrestore(&mmio_idx_lock, flags); - mutex_unlock(&grbm_idx_mutex); - mutex_unlock(&srbm_mutex); - + spin_unlock_irqrestore(&locks->mmio_idx_lock, flags); + mutex_unlock(&locks->grbm_idx_mutex); + mutex_unlock(&locks->srbm_mutex); fs_reclaim_release(GFP_KERNEL); - mutex_unlock(&reset_ctl.reset_lock); + mutex_unlock(&locks->reset_lock); up_read(&reset_domain->sem); - mutex_unlock(&vram_lock); - mutex_unlock(¬ifier_lock); - mutex_unlock(&userq_mutex); - mutex_unlock(&userq_sch_mutex); + + mutex_unlock(&locks->vram_lock); + mutex_unlock(&locks->notifier_lock); + mutex_unlock(&locks->userq_mutex); + mutex_unlock(&locks->userq_sch_mutex); /* Cleanup */ amdgpu_reset_put_reset_domain(reset_domain); + kfree(locks); pr_info("AMDGPU: Lockdep annotations initialized (9 lock levels)\n"); return 0; From 3e864bf2a32a1cbdf1e0f9c5a5a4176e8575f4a3 Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Fri, 5 Jun 2026 23:44:08 +0800 Subject: [PATCH 3959/5214] drm/amdgpu/gfx: fix cleaner shader IB buffer overflow The cleaner shader sysfs path allocates a 16-dword (64 byte) IB but incorrectly fills (align_mask + 1) dwords. On GFX rings align_mask is 0xff, so the loop wrote 256 dwords into a 64-byte buffer, causing a kernel page fault. The IB only needs to be a minimal NOP shell to schedule the job; the cleaner shader itself is emitted on the ring via emit_cleaner_shader(). Fill 16 dwords to match the allocation. v2: Use ib_size_dw variable (Lijo) Fixes: d361ad5d2fc0 ("drm/amdgpu: Add sysfs interface for running cleaner shader") Suggested-by: Lijo Lazar Signed-off-by: Asad Kamal Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit bf21af331ebf72d0935fd70c73192414a422c03a) CC: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 1e190fb54a97..85372af1216d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1664,12 +1664,13 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; struct drm_gpu_scheduler *sched = &ring->sched; struct drm_sched_entity entity; + unsigned int ib_size_dw = 16; static atomic_t counter; struct dma_fence *f; struct amdgpu_job *job; struct amdgpu_ib *ib; void *owner; - int i, r; + int r; /* Initialize the scheduler entity */ r = drm_sched_entity_init(&entity, DRM_SCHED_PRIORITY_NORMAL, @@ -1687,7 +1688,7 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) owner = (void *)(unsigned long)atomic_inc_return(&counter); r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner, - 64, 0, &job, + ib_size_dw * sizeof(uint32_t), 0, &job, AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER); if (r) goto err; @@ -1697,9 +1698,8 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) job->run_cleaner_shader = true; ib = &job->ibs[0]; - for (i = 0; i <= ring->funcs->align_mask; ++i) - ib->ptr[i] = ring->funcs->nop; - ib->length_dw = ring->funcs->align_mask + 1; + memset32(ib->ptr, ring->funcs->nop, ib_size_dw); + ib->length_dw = ib_size_dw; f = amdgpu_job_submit(job); From 40f9e2f514924312ea737c1c16ba945a5a7e374d Mon Sep 17 00:00:00 2001 From: James Lin Date: Fri, 12 Jun 2026 10:05:29 -0400 Subject: [PATCH 3960/5214] drm/amd/display: Add IN_FORMATS_ASYNC support for planes [Why] The DRM core exposes an IN_FORMATS_ASYNC plane property describing the set of format/modifier pairs that are valid for asynchronous (immediate) page flips. amdgpu already advertises async page flip support via mode_config.async_page_flip = true, but never implemented the .format_mod_supported_async plane callback, so the IN_FORMATS_ASYNC property was not created. This inconsistency (advertising async flips while exposing IN_FORMATS but no IN_FORMATS_ASYNC) causes userspace, such as igt-gpu-tools, to emit a repeated warning during plane initialization, which in turn demotes many otherwise passing KMS subtests to a WARN result. [How] Wire up .format_mod_supported_async to the existing amdgpu_dm_plane_format_mod_supported callback so the async format list is populated. amdgpu does not restrict async flips at the format/modifier level: the async flip constraints are enforced at atomic check and commit time and only require a fast update (no change to FB pitch, DCC state, rotation or memory type) between the old and new buffers. Therefore the set of formats/modifiers valid for async flips is identical to the regular IN_FORMATS set, and the same callback can be reused. Reviewed-by: Aurabindo Pillai Signed-off-by: James Lin Signed-off-by: Ivan Lipski Signed-off-by: Alex Deucher (cherry picked from commit 8e2d7bbd6b184c0c1b0fe7cb404c9b5214d89931) --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index e957657b06c7..c7f8e08feaf4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1859,6 +1859,7 @@ static const struct drm_plane_funcs dm_plane_funcs = { .atomic_duplicate_state = amdgpu_dm_plane_drm_plane_duplicate_state, .atomic_destroy_state = amdgpu_dm_plane_drm_plane_destroy_state, .format_mod_supported = amdgpu_dm_plane_format_mod_supported, + .format_mod_supported_async = amdgpu_dm_plane_format_mod_supported, #ifdef AMD_PRIVATE_COLOR .atomic_set_property = dm_atomic_plane_set_property, .atomic_get_property = dm_atomic_plane_get_property, From 8e792f018e10e68f488f279fbd4f38009a2e066d Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Thu, 11 Jun 2026 08:44:38 -0700 Subject: [PATCH 3961/5214] drm/amd/display: Fix mem_type change detection for async flips [Why] amdgpu_dm_crtc_mem_type_changed() fetches the "old" and "new" plane state with two drm_atomic_get_plane_state() calls, which both return the new state. It compares a state against itself, so it never detects a mem_type change and never rejects the async flip. On DCN 3.0.1, this shows up as intermittent corruption when a single DCC plane is scanned out with immediate flips under gamescope and its buffer moves between the VRAM carveout and GTT. [How] Use drm_atomic_get_old_plane_state() and drm_atomic_get_new_plane_state() to compare the actual old and new states. These return NULL rather than an error pointer for a plane that is not part of the commit, so the IS_ERR() check becomes a NULL check that skips those planes, such as an unmodified cursor still in the CRTC's plane_mask. Fixes: 4caacd1671b7 ("drm/amd/display: Do not elevate mem_type change to full update") Reviewed-by: Harry Wentland Reviewed-by: Melissa Wen Signed-off-by: Matthew Schwartz Signed-off-by: Alex Deucher (cherry picked from commit 13158e5dbd896281f3e9982b5437cffa5fd621b2) --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1ed697a3a453..eb5696b5daeb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12938,13 +12938,11 @@ static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev, struct drm_plane_state *new_plane_state, *old_plane_state; drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) { - new_plane_state = drm_atomic_get_plane_state(state, plane); - old_plane_state = drm_atomic_get_plane_state(state, plane); + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + old_plane_state = drm_atomic_get_old_plane_state(state, plane); - if (IS_ERR(new_plane_state) || IS_ERR(old_plane_state)) { - drm_err(dev, "Failed to get plane state for plane %s\n", plane->name); - return false; - } + if (!old_plane_state || !new_plane_state) + continue; if (old_plane_state->fb && new_plane_state->fb && get_mem_type(old_plane_state->fb) != get_mem_type(new_plane_state->fb)) From f896e86273dbbebb5eac966b4a201b5c62a02e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Ondra=C4=8Dka?= Date: Wed, 10 Jun 2026 10:32:45 +0200 Subject: [PATCH 3962/5214] drm/radeon: fix r100_copy_blit for large BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit r100_copy_blit() copies BOs as 1024-pixel-wide ARGB8888 blits, so one GPU page becomes one blit row. Large copies are split into chunks of at most 8191 rows. The kernel register header names the packet coordinate dwords SRC_Y_X and DST_Y_X. In the BITBLT_MULTI description in R5xx_Acceleration_v1.5.pdf docs, these correspond to [SRC_X1 | SRC_Y1] and [DST_X1 | DST_Y1], which are signed 13-bit coordinates in the -8192..8191 range. The old code kept SRC/DST_PITCH_OFFSET at the BO base and used SRC_Y_X/DST_Y_X as the chunk address, so large BO moves could exceed that coordinate range. Compute per-chunk SRC/DST_PITCH_OFFSET bases and emit zero source and destination coordinates. r100_copy_blit() already packs SRC/DST_PITCH_OFFSET as pitch plus base offset, so large chunk addresses belong there rather than in the coordinate fields. This fixes Prison Architect corruption with 4096x4096 mipped textures after they are evicted to GTT under memory pressure on RV530. Closes: https://gitlab.freedesktop.org/mesa/mesa/-/work_items/6716 Acked-by: Christian König Signed-off-by: Pavel Ondračka Signed-off-by: Alex Deucher (cherry picked from commit 87be26aee76239c6da03e599f238a426897f78ad) Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/r100.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 3ac1a79b6f13..533215d6e9cb 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -906,6 +906,7 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; struct radeon_fence *fence; + uint64_t cur_src_offset, cur_dst_offset; uint32_t cur_pages; uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE; uint32_t pitch; @@ -934,6 +935,10 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, cur_pages = 8191; } num_gpu_pages -= cur_pages; + cur_src_offset = src_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; + cur_dst_offset = dst_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; /* pages are in Y direction - height page width in X direction - width */ @@ -950,13 +955,13 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, RADEON_DP_SRC_SOURCE_MEMORY | RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); - radeon_ring_write(ring, (pitch << 22) | (src_offset >> 10)); - radeon_ring_write(ring, (pitch << 22) | (dst_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_src_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_dst_offset >> 10)); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); radeon_ring_write(ring, 0); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); - radeon_ring_write(ring, num_gpu_pages); - radeon_ring_write(ring, num_gpu_pages); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); radeon_ring_write(ring, cur_pages | (stride_pixels << 16)); } radeon_ring_write(ring, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0)); From 8fa5655da368d0306c03e9dc9cda8ae2a7840926 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:22:04 -0500 Subject: [PATCH 3963/5214] drm/amdkfd: fix list_del corruption in kfd_criu_resume_svm The cleanup tail of kfd_criu_resume_svm() walks svms->criu_svm_metadata_list and kfree()s each struct criu_svm_metadata without removing it from the list. The list head is left pointing at freed kmalloc-96 objects. A second AMDKFD_IOC_CRIU_OP from the same process re-enters: list_empty() reads the dangling ->next (use-after-free), the loop walks freed entries, and each is kfree()'d again (double-free). This is reachable by an unprivileged render-group user via /dev/kfd with no capabilities required. Add list_del() before the kfree() so the list is properly emptied. The list_for_each_entry_safe() iterator already caches the next pointer, so unlinking during the walk is safe. Fixes: 2a909ae71871 ("drm/amdkfd: CRIU resume shared virtual memory ranges") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 6322d278a298e2c1430b9d2697743d3a04b788b1) --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index d64d104783d4..5a56d86b3ecf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -4115,6 +4115,7 @@ int kfd_criu_resume_svm(struct kfd_process *p) list_for_each_entry_safe(criu_svm_md, next, &svms->criu_svm_metadata_list, list) { pr_debug("freeing criu_svm_md[]\n\tstart: 0x%llx\n", criu_svm_md->data.start_addr); + list_del(&criu_svm_md->list); kfree(criu_svm_md); } From a2b270c0ecf6d95bcd14ef4c20d0301a88143ff5 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 8 Jun 2026 16:22:35 -0300 Subject: [PATCH 3964/5214] drm/amdgpu: initialize irq.lock spinlock earlier If there is an early failure during amdgpu probe, like missing firmware, it will end up calling amdgpu_irq_disable_all, which takes irq.lock spinlock without it being initialized. Initializing irq.lock earlier at amdgpu_device_init fixes the issue. [ 79.334079] INFO: trying to register non-static key. [ 79.334081] The code is fine but needs lockdep annotation, or maybe [ 79.334083] you didn't initialize this object before use? [ 79.334084] turning off the locking correctness validator. [ 79.334088] CPU: 2 UID: 0 PID: 1819 Comm: bash Not tainted 7.1.0-rc5-gfd06300b2348 #96 PREEMPT 8e8f461221633dae3c832d6689eaf0546c0ed4cd [ 79.334092] Hardware name: Valve Jupiter/Jupiter, BIOS F7A0133 08/05/2024 [ 79.334094] Call Trace: [ 79.334095] [ 79.334097] dump_stack_lvl+0x5d/0x80 [ 79.334103] register_lock_class+0x7af/0x7c0 [ 79.334109] __lock_acquire+0x416/0x2610 [ 79.334114] lock_acquire+0xcf/0x310 [ 79.334117] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334503] ? _raw_spin_lock_irqsave+0x53/0x60 [ 79.334508] _raw_spin_lock_irqsave+0x3f/0x60 [ 79.334510] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334881] amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335240] amdgpu_device_fini_hw+0x90/0x32c [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335704] amdgpu_driver_load_kms.cold+0x22/0x44 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336159] amdgpu_pci_probe+0x204/0x440 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336494] local_pci_probe+0x3c/0x80 [ 79.336500] pci_call_probe+0x55/0x2e0 [ 79.336505] ? _raw_spin_unlock+0x2d/0x50 [ 79.336508] ? pci_match_device+0x157/0x180 [ 79.336512] pci_device_probe+0x9b/0x170 [ 79.336516] really_probe+0xd5/0x370 [ 79.336521] __driver_probe_device+0x84/0x150 [ 79.336525] device_driver_attach+0x47/0xb0 [ 79.336528] bind_store+0x73/0xc0 [ 79.336531] kernfs_fop_write_iter+0x176/0x250 [ 79.336536] vfs_write+0x24d/0x560 [ 79.336542] ksys_write+0x71/0xe0 [ 79.336546] do_syscall_64+0x122/0x710 [ 79.336550] ? do_syscall_64+0xd1/0x710 [ 79.336553] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 79.336557] RIP: 0033:0x7f92fd675006 [ 79.336561] Code: 5d e8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 75 19 83 e2 39 83 fa 08 75 11 e8 26 ff ff ff 66 0f 1f 44 00 00 48 8b 45 10 0f 05 <48> 8b 5d f8 c9 c3 0f 1f 40 00 f3 0f 1e fa 55 48 89 e5 48 83 ec 08 [ 79.336562] RSP: 002b:00007ffe4fa867a0 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 [ 79.336565] RAX: ffffffffffffffda RBX: 000000000000000d RCX: 00007f92fd675006 [ 79.336567] RDX: 000000000000000d RSI: 000055b2dfce59b0 RDI: 0000000000000001 [ 79.336568] RBP: 00007ffe4fa867c0 R08: 0000000000000000 R09: 0000000000000000 [ 79.336569] R10: 0000000000000000 R11: 0000000000000202 R12: 000000000000000d [ 79.336570] R13: 000055b2dfce59b0 R14: 00007f92fd7ca5c0 R15: 000055b2dfdbaf70 [ 79.336574] Fixes: 9950cda2a018 ("drm/amdgpu: drop the drm irq pre/post/un install callbacks") Reviewed-by: Tvrtko Ursulin Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Alex Deucher (cherry picked from commit 7dba3e10ecdeec85208e255853fcd3890880b10e) --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 0fa2ce36c2ea..211d30f03d25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3771,6 +3771,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, mutex_init(&adev->gfx.workload_profile_mutex); mutex_init(&adev->vcn.workload_profile_mutex); + spin_lock_init(&adev->irq.lock); + amdgpu_device_init_apu_flags(adev); r = amdgpu_device_check_arguments(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 254a4e983f40..40b8506ac66f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -309,8 +309,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev) unsigned int irq, flags; int r; - spin_lock_init(&adev->irq.lock); - /* Enable MSI if not disabled by module parameter */ adev->irq.msi_enabled = false; From 93475c34111916df71c63e510fc52db01351f809 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:11:53 -0500 Subject: [PATCH 3965/5214] drm/amdgpu: check amdgpu_vm_bo_find() result in GET_MAPPING_INFO The AMDGPU_GEM_OP_GET_MAPPING_INFO path of amdgpu_gem_op_ioctl() looks up the bo_va for the buffer object in the caller's VM via amdgpu_vm_bo_find(), but uses the returned pointer without checking it. amdgpu_vm_bo_find() returns NULL when the BO has no bo_va in that VM, which is the normal case for a BO that has never been mapped. The result is fed straight into amdgpu_vm_bo_va_for_each_valid_mapping(), which expands to list_for_each_entry(mapping, &(bo_va)->valids, list) and dereferences bo_va, causing a NULL pointer dereference. This is reachable by any process able to issue the ioctl (render group) simply by requesting mapping info for an unmapped BO. Return -ENOENT when no bo_va is found, jumping to out_exec so the drm_exec context and GEM object reference are released. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 528b19377affc1cc7362a70a254c1dda793595f9) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 212c14d99f6b..76da3f932f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1094,6 +1094,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (!bo_va) { + r = -ENOENT; + goto out_exec; + } + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { r = -EINVAL; goto out_exec; From 84c4c36acd5c4b2558b5069f869a165b2c655c84 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:07:24 -0500 Subject: [PATCH 3966/5214] drm/amdgpu: validate CP_GFX_SHADOW chunk size in CS pass1 Add a minimum-length check for the AMDGPU_CHUNK_ID_CP_GFX_SHADOW chunk in amdgpu_cs_pass1(), matching the gate already present for the IB, FENCE and BO_HANDLES chunk types. The CP_GFX_SHADOW case previously shared a bare break with the dependency and syncobj chunk types, which do not dereference a fixed-size struct. When userspace submits this chunk with length_dw == 0, vmemdup_array_user() is called with size 0 and returns ZERO_SIZE_PTR, which passes the IS_ERR() check. amdgpu_cs_p2_shadow() then dereferences chunk->kdata as a struct drm_amdgpu_cs_chunk_cp_gfx_shadow (reading shadow->flags), faulting on the ZERO_SIZE_PTR and causing a NULL-pointer dereference. This is reachable by an unprivileged process in the render group. Reject undersized chunks with -EINVAL during pass1 so the bad submission is rejected before pass2 ever dereferences the data. Fixes: ac9287055ff1 ("drm/amdgpu: add gfx shadow CS IOCTL support") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 7f61b2eef7415eccdb40850aca0de94211948657) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 115b134b4cd1..c2e6495a28bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -247,13 +247,17 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, goto free_partial_kdata; break; + case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: + if (size < sizeof(struct drm_amdgpu_cs_chunk_cp_gfx_shadow)) + goto free_partial_kdata; + break; + case AMDGPU_CHUNK_ID_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_IN: case AMDGPU_CHUNK_ID_SYNCOBJ_OUT: case AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL: - case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: break; default: From d072a3f603c639ee12a05126aa0bab0ff1732323 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 1 Jun 2026 23:55:53 +1000 Subject: [PATCH 3967/5214] drm/amdkfd: Fix NULL deref during sysfs teardown Move kfd_process_remove_sysfs() earlier in kfd_process_wq_release() so that all sysfs/procfs entries are removed before tearing down PDDs and dropping lead_thread. The per-process sysfs attributes are backed by struct kfd_process_device, and their show/store callbacks dereference PDD fields. Since sysfs removal waits for active callbacks to complete, removing these entries first closes a race where userspace reads sdma_* and stats_* files after PDD teardown. Previously this cleanup ran after kfd_process_destroy_pdds(), which resets p->n_pdds to 0. This meant kfd_process_remove_sysfs() could no longer walk the PDD array, so the per-PDD sysfs cleanup did not run as intended. This race caused NULL pointer dereferences observed in kfd_sdma_activity_worker and kfd_procfs_stats_show. Also harden kfd_process_remove_sysfs() against partially initialized or already-freed objects: - Check kobj_queues before removing PASID and deleting it - Guard kobj_stats and kobj_counters before use These checks prevent invalid dereferences during cleanup. Cc: Felix Kuehling Cc: Alex Deucher Signed-off-by: Geoffrey McRae Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 674c692702341fed321720b4b92036c5934fb485) --- drivers/gpu/drm/amd/amdkfd/kfd_process.c | 40 ++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 63815be995fc..ca71fa726e32 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -1175,10 +1175,12 @@ static void kfd_process_remove_sysfs(struct kfd_process *p) if (!p->kobj) return; - sysfs_remove_file(p->kobj, &p->attr_pasid); - kobject_del(p->kobj_queues); - kobject_put(p->kobj_queues); - p->kobj_queues = NULL; + if (p->kobj_queues) { + sysfs_remove_file(p->kobj, &p->attr_pasid); + kobject_del(p->kobj_queues); + kobject_put(p->kobj_queues); + p->kobj_queues = NULL; + } for (i = 0; i < p->n_pdds; i++) { pdd = p->pdds[i]; @@ -1186,17 +1188,21 @@ static void kfd_process_remove_sysfs(struct kfd_process *p) sysfs_remove_file(p->kobj, &pdd->attr_vram); sysfs_remove_file(p->kobj, &pdd->attr_sdma); - sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict); - if (pdd->dev->kfd2kgd->get_cu_occupancy) - sysfs_remove_file(pdd->kobj_stats, - &pdd->attr_cu_occupancy); - kobject_del(pdd->kobj_stats); - kobject_put(pdd->kobj_stats); - pdd->kobj_stats = NULL; + if (pdd->kobj_stats) { + sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict); + if (pdd->dev->kfd2kgd->get_cu_occupancy) + sysfs_remove_file(pdd->kobj_stats, + &pdd->attr_cu_occupancy); + kobject_del(pdd->kobj_stats); + kobject_put(pdd->kobj_stats); + pdd->kobj_stats = NULL; + } } for_each_set_bit(i, p->svms.bitmap_supported, p->n_pdds) { pdd = p->pdds[i]; + if (!pdd->kobj_counters) + continue; sysfs_remove_file(pdd->kobj_counters, &pdd->attr_faults); sysfs_remove_file(pdd->kobj_counters, &pdd->attr_page_in); @@ -1254,6 +1260,13 @@ static void kfd_process_wq_release(struct work_struct *work) kfd_debugfs_remove_process(p); + /* + * Remove the proc/sysfs entries before destroying PDDs. The removal path + * walks the PDD array and sysfs callbacks dereference PDD fields, so the + * backing data must remain valid until sysfs removal has completed. + */ + kfd_process_remove_sysfs(p); + kfd_process_kunmap_signal_bo(p); kfd_process_free_outstanding_kfd_bos(p); svm_range_list_fini(p); @@ -1267,11 +1280,6 @@ static void kfd_process_wq_release(struct work_struct *work) put_task_struct(p->lead_thread); - /* the last step is removing process entries under /sys - * to indicate the process has been terminated. - */ - kfd_process_remove_sysfs(p); - kfree(p); } From 75b3e4d0494f5f831939bec835deceebff0bded7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 28 Apr 2026 16:47:03 +0200 Subject: [PATCH 3968/5214] drm/amdgpu: Don't use UTS_RELEASE directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UTS_RELEASE evaluates to a static string and changes quite easily (e.g. uncommitted changes in the source tree or new commits). So when checking if a patch introduces changes to the resulting binary each usage of UTS_RELEASE is source of annoyance. Instead of using UTS_RELEASE directly use init_utsname()->release which evaluates to the same string but with that a change of UTS_RELEASE doesn't affect amdgpu_dev_coredump.o. Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260428144704.1114562-2-u.kleine-koenig@baylibre.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit d785df5598fd1d1cc2f2f45c05448271b6d490b7) --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index bed68f0c3080..322c55aaf15f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -22,8 +22,8 @@ * */ -#include #include +#include #include "amdgpu_dev_coredump.h" #include "atom.h" @@ -237,7 +237,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf drm_printf(&p, "**** AMDGPU Device Coredump ****\n"); drm_printf(&p, "version: " AMDGPU_COREDUMP_VERSION "\n"); - drm_printf(&p, "kernel: " UTS_RELEASE "\n"); + drm_printf(&p, "kernel: %s\n", init_utsname()->release); drm_printf(&p, "module: " KBUILD_MODNAME "\n"); drm_printf(&p, "time: %ptSp\n", &coredump->reset_time); From 9920249a5288e7cbec222cd52996bbd9aac7ec9e Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Fri, 29 May 2026 11:47:38 +0500 Subject: [PATCH 3969/5214] drm/amdgpu: convert amdgpu_vm_lock_by_pasid() to drm_exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_vm_lock_by_pasid() looks up a VM by PASID and reserves its root PD with a bare amdgpu_bo_reserve(), returning the still-reserved root to the caller. A caller that then needs to reserve further BOs (for example the devcoredump IB dump) ends up nesting reservation_ww_class_mutex acquires without a ww_acquire_ctx, which lockdep flags as recursive locking. Convert the helper to take a drm_exec context and lock the root PD with drm_exec_lock_obj(). Callers now run it inside a drm_exec_until_all_locked() loop and can lock additional BOs in the same ww ticket, so there is no nested ww_mutex acquire. The drm_exec context holds its own reference on the locked root BO, so the helper no longer hands a root reference back to the caller: the root output parameter is dropped, and the transient reference taken across the PASID lookup is released before returning. The only existing caller, amdgpu_vm_handle_fault(), is updated accordingly. Its is_compute_context path, which previously dropped the root reservation around svm_range_restore_pages() and re-took it, now finalises the drm_exec context and re-initialises a fresh one; behaviour is otherwise unchanged. No functional change intended for the page-fault path. Reviewed-by: Christian König Signed-off-by: Mikhail Gavrilov Signed-off-by: Alex Deucher (cherry picked from commit 14682de8ad377bf13ea66e47c26dcfea0b19a21d) --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 91 ++++++++++++++++---------- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 2 +- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 7d51880b4860..fee4c94c2585 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2920,47 +2920,56 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } /** - * amdgpu_vm_lock_by_pasid - return an amdgpu_vm and its root bo from a pasid, if possible. + * amdgpu_vm_lock_by_pasid - look up a VM by PASID and lock its root PD * @adev: amdgpu device pointer - * @root: root BO of the VM * @pasid: PASID of the VM - * The caller needs to unreserve and unref the root bo on success. + * @exec: drm_exec context to lock the root PD in + * + * Must be called from within a drm_exec_until_all_locked() loop; the caller + * runs drm_exec_retry_on_contention() afterwards. The drm_exec context holds + * a reference on the root BO until it is finalised. + * + * Return: the VM on success, or NULL if the PASID has no VM, the VM is being + * torn down, or locking the root PD failed. */ struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, - struct amdgpu_bo **root, u32 pasid) + u32 pasid, struct drm_exec *exec) { unsigned long irqflags; + struct amdgpu_bo *root; struct amdgpu_vm *vm; int r; xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); vm = xa_load(&adev->vm_manager.pasids, pasid); - *root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; + root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - if (!*root) + if (!root) return NULL; - r = amdgpu_bo_reserve(*root, true); - if (r) - goto error_unref; + r = drm_exec_lock_obj(exec, &root->tbo.base); + if (r) { + amdgpu_bo_unref(&root); + return NULL; + } /* Double check that the VM still exists */ xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); vm = xa_load(&adev->vm_manager.pasids, pasid); - if (vm && vm->root.bo != *root) + if (vm && vm->root.bo != root) vm = NULL; xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - if (!vm) - goto error_unlock; + if (!vm) { + drm_exec_unlock_obj(exec, &root->tbo.base); + amdgpu_bo_unref(&root); + return NULL; + } + + /* The drm_exec context holds its own reference on the root BO. */ + amdgpu_bo_unref(&root); return vm; -error_unlock: - amdgpu_bo_unreserve(*root); - -error_unref: - amdgpu_bo_unref(root); - return NULL; } /** @@ -2982,33 +2991,49 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, uint64_t ts, bool write_fault) { bool is_compute_context = false; - struct amdgpu_bo *root; + struct drm_exec exec; uint64_t value, flags; struct amdgpu_vm *vm; int r; - vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); - if (!vm) + drm_exec_init(&exec, 0, 1); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, pasid, &exec); + drm_exec_retry_on_contention(&exec); + if (!vm) + break; + } + if (!vm) { + drm_exec_fini(&exec); return false; + } is_compute_context = vm->is_compute_context; if (is_compute_context) { - /* Unreserve root since svm_range_restore_pages might try to reserve it. */ - /* TODO: rework svm_range_restore_pages so that this isn't necessary. */ - amdgpu_bo_unreserve(root); + /* Release the root PD lock since svm_range_restore_pages + * might try to take it. + * TODO: rework svm_range_restore_pages so that this isn't + * necessary. + */ + drm_exec_fini(&exec); if (!svm_range_restore_pages(adev, pasid, vmid, - node_id, addr >> PAGE_SHIFT, ts, write_fault)) { - amdgpu_bo_unref(&root); + node_id, addr >> PAGE_SHIFT, ts, write_fault)) return true; - } - amdgpu_bo_unref(&root); /* Re-acquire the VM lock, could be that the VM was freed in between. */ - vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); - if (!vm) + drm_exec_init(&exec, 0, 1); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, pasid, &exec); + drm_exec_retry_on_contention(&exec); + if (!vm) + break; + } + if (!vm) { + drm_exec_fini(&exec); return false; + } } addr /= AMDGPU_GPU_PAGE_SIZE; @@ -3032,7 +3057,7 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, value = 0; } - r = dma_resv_reserve_fences(root->tbo.base.resv, 1); + r = dma_resv_reserve_fences(vm->root.bo->tbo.base.resv, 1); if (r) { pr_debug("failed %d to reserve fence slot\n", r); goto error_unlock; @@ -3046,12 +3071,10 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, r = amdgpu_vm_update_pdes(adev, vm, true); error_unlock: - amdgpu_bo_unreserve(root); + drm_exec_fini(&exec); if (r < 0) dev_err(adev->dev, "Can't handle page fault (%d)\n", r); - amdgpu_bo_unref(&root); - return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 3695299f1a03..b32f51a78cd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -592,7 +592,7 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, bool write_fault); struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, - struct amdgpu_bo **root, u32 pasid); + u32 pasid, struct drm_exec *exec); void amdgpu_vm_set_task_info(struct amdgpu_vm *vm); From 7152b248dc3c8d5fa8629e99ed5655dd41b51562 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Fri, 29 May 2026 11:47:39 +0500 Subject: [PATCH 3970/5214] drm/amdgpu: fix recursive ww_mutex acquire in amdgpu_devcoredump_format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dumping IB contents from a hung job, amdgpu_devcoredump_format() acquired the VM root PD's reservation via amdgpu_vm_lock_by_pasid() and then, for each IB, called amdgpu_bo_reserve() on the BO backing the IB. Both reservations are reservation_ww_class_mutex objects and neither used a ww_acquire_ctx, which trips lockdep: WARNING: possible recursive locking detected -------------------------------------------- kworker/u128:0 is trying to acquire lock: ffff88838b16e1f0 (reservation_ww_class_mutex){+.+.}-{4:4}, at: amdgpu_devcoredump_format+0x1594/0x23f0 [amdgpu] but task is already holding lock: ffff8882f82681f0 (reservation_ww_class_mutex){+.+.}-{4:4}, at: amdgpu_devcoredump_format+0x1594/0x23f0 [amdgpu] Possible unsafe locking scenario: CPU0 ---- lock(reservation_ww_class_mutex); lock(reservation_ww_class_mutex); *** DEADLOCK *** May be due to missing lock nesting notation Workqueue: events_unbound amdgpu_devcoredump_deferred_work [amdgpu] Call Trace: __ww_mutex_lock.constprop.0 ww_mutex_lock amdgpu_bo_reserve amdgpu_devcoredump_format+0x1594 [amdgpu] amdgpu_devcoredump_deferred_work+0xea [amdgpu] The two reservations are on different BOs in the captured trace, so the splat is a lockdep-correctness warning, not an observed deadlock. It becomes a real self-deadlock whenever the IB BO shares its dma_resv with the root PD (the always-valid case, see amdgpu_vm_is_bo_always_valid()): amdgpu_bo_reserve(abo) re-acquires the same ww_mutex without a ticket and blocks forever. With amdgpu.gpu_recovery=0 the timeout handler refires every ~2 s and each invocation produces this splat, drowning the kernel ring buffer. Now that amdgpu_vm_lock_by_pasid() takes a drm_exec context, move the IB dumping into a separate helper that locks the root PD and every IB BO together in a single drm_exec ticket. DRM_EXEC_IGNORE_DUPLICATES handles IB BOs that share a dma_resv (e.g. always-valid BOs, or two IBs backed by the same BO). Every lock is now a top-level acquire under one ww_acquire_ctx, so the recursive ww_mutex condition is gone, and the per-IB amdgpu_bo_reserve()/amdgpu_bo_unref() dance -- including a BO refcount leak on the amdgpu_bo_reserve() failure path -- is removed. Fixes: 7b15fc2d1f1a ("drm/amdgpu: dump job ibs in the devcoredump") Suggested-by: Christian König Signed-off-by: Mikhail Gavrilov Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d6bf4242731219ee08ce54c365631e395486651e) --- .../gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 215 ++++++++++-------- 1 file changed, 126 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 322c55aaf15f..e77db76b48b8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -24,6 +24,7 @@ #include #include +#include #include "amdgpu_dev_coredump.h" #include "atom.h" @@ -207,23 +208,137 @@ static void amdgpu_devcoredump_fw_info(struct amdgpu_device *adev, } } +static void +amdgpu_devcoredump_print_ibs(struct drm_printer *p, + struct amdgpu_coredump_info *coredump, + bool sizing_pass) +{ + struct amdgpu_device *adev = coredump->adev; + struct amdgpu_bo_va_mapping *mapping; + struct amdgpu_bo *abo; + struct drm_exec exec; + struct amdgpu_vm *vm; + u32 *ib_content; + u64 va_start, offset; + u8 *kptr; + u32 off; + int r; + + /* + * On the sizing pass there is no VM to look up and no BO to lock; the + * size estimate doesn't depend on whether the IB BOs are reachable. + * Just emit the per-IB headers (the content is not written anywhere). + */ + if (sizing_pass) { + for (int i = 0; i < coredump->num_ibs; i++) { + drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i, + coredump->ibs[i].gpu_addr, + coredump->ibs[i].ib_size_dw); + } + return; + } + + /* + * Lock the VM root PD and every IB BO together in a single drm_exec + * ticket. Reserving the IB BOs one by one while the root PD is held + * would be a recursive reservation_ww_class_mutex acquire without a + * ww_acquire_ctx, which trips lockdep and self-deadlocks for IB BOs + * that share their dma_resv with the root PD (always-valid BOs). + */ + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 1 + coredump->num_ibs); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, coredump->pasid, &exec); + if (!vm) + goto unlock; + + for (int i = 0; i < coredump->num_ibs; i++) { + u64 pfn = (coredump->ibs[i].gpu_addr & + AMDGPU_GMC_HOLE_MASK) / AMDGPU_GPU_PAGE_SIZE; + + mapping = amdgpu_vm_bo_lookup_mapping(vm, pfn); + if (!mapping) + continue; + + abo = mapping->bo_va->base.bo; + r = drm_exec_lock_obj(&exec, &abo->tbo.base); + drm_exec_retry_on_contention(&exec); + if (r) + goto unlock; + } + } + + for (int i = 0; i < coredump->num_ibs; i++) { + bool emit_content = false; + + ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4, + GFP_KERNEL); + if (!ib_content) + continue; + + va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK; + mapping = amdgpu_vm_bo_lookup_mapping(vm, + va_start / AMDGPU_GPU_PAGE_SIZE); + if (!mapping) + goto output_ib_content; + + abo = mapping->bo_va->base.bo; + offset = va_start - mapping->start * AMDGPU_GPU_PAGE_SIZE; + + if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) { + struct amdgpu_res_cursor cursor; + + off = 0; + + if (abo->tbo.resource->mem_type != TTM_PL_VRAM) + goto output_ib_content; + + amdgpu_res_first(abo->tbo.resource, offset, + coredump->ibs[i].ib_size_dw * 4, &cursor); + while (cursor.remaining) { + amdgpu_device_mm_access(adev, cursor.start / 4, + &ib_content[off], cursor.size / 4, + false); + off += cursor.size; + amdgpu_res_next(&cursor, cursor.size); + } + emit_content = true; + } else { + r = ttm_bo_kmap(&abo->tbo, 0, PFN_UP(abo->tbo.base.size), + &abo->kmap); + if (r) + goto output_ib_content; + + kptr = amdgpu_bo_kptr(abo); + kptr += offset; + memcpy(ib_content, kptr, coredump->ibs[i].ib_size_dw * 4); + + amdgpu_bo_kunmap(abo); + emit_content = true; + } + +output_ib_content: + drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i, + coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); + if (emit_content) { + for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) + drm_printf(p, "0x%08x\n", ib_content[j]); + } + kvfree(ib_content); + } + +unlock: + drm_exec_fini(&exec); +} + static ssize_t amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_info *coredump) { - struct amdgpu_device *adev = coredump->adev; struct drm_printer p; struct drm_print_iterator iter; struct amdgpu_vm_fault_info *fault_info; - struct amdgpu_bo_va_mapping *mapping; struct amdgpu_ip_block *ip_block; - struct amdgpu_res_cursor cursor; - struct amdgpu_bo *abo, *root; - uint64_t va_start, offset; struct amdgpu_ring *ring; - struct amdgpu_vm *vm; - u32 *ib_content; - uint8_t *kptr; - int ver, i, j, r; + int ver, i, j; u32 ring_idx, off; bool sizing_pass; @@ -343,86 +458,8 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf else if (coredump->reset_vram_lost) drm_printf(&p, "VRAM is lost due to GPU reset!\n"); - if (coredump->num_ibs) { - /* Don't try to lookup the VM or map the BOs when calculating the - * size required to store the devcoredump. - */ - if (sizing_pass) - vm = NULL; - else - vm = amdgpu_vm_lock_by_pasid(adev, &root, coredump->pasid); - - for (int i = 0; i < coredump->num_ibs && (sizing_pass || vm); i++) { - ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4, - GFP_KERNEL); - if (!ib_content) - continue; - - /* vm=NULL can only happen when 'sizing_pass' is true. Skip to the - * drm_printf() calls (ib_content doesn't need to be initialized - * as its content won't be written anywhere). - */ - if (!vm) - goto output_ib_content; - - va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK; - mapping = amdgpu_vm_bo_lookup_mapping(vm, va_start / AMDGPU_GPU_PAGE_SIZE); - if (!mapping) - goto free_ib_content; - - offset = va_start - (mapping->start * AMDGPU_GPU_PAGE_SIZE); - abo = amdgpu_bo_ref(mapping->bo_va->base.bo); - r = amdgpu_bo_reserve(abo, false); - if (r) - goto free_ib_content; - - if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) { - off = 0; - - if (abo->tbo.resource->mem_type != TTM_PL_VRAM) - goto unreserve_abo; - - amdgpu_res_first(abo->tbo.resource, offset, - coredump->ibs[i].ib_size_dw * 4, - &cursor); - while (cursor.remaining) { - amdgpu_device_mm_access(adev, cursor.start / 4, - &ib_content[off], cursor.size / 4, - false); - off += cursor.size; - amdgpu_res_next(&cursor, cursor.size); - } - } else { - r = ttm_bo_kmap(&abo->tbo, 0, - PFN_UP(abo->tbo.base.size), - &abo->kmap); - if (r) - goto unreserve_abo; - - kptr = amdgpu_bo_kptr(abo); - kptr += offset; - memcpy(ib_content, kptr, - coredump->ibs[i].ib_size_dw * 4); - - amdgpu_bo_kunmap(abo); - } - -output_ib_content: - drm_printf(&p, "\nIB #%d 0x%llx %d dw\n", - i, coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); - for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) - drm_printf(&p, "0x%08x\n", ib_content[j]); -unreserve_abo: - if (vm) - amdgpu_bo_unreserve(abo); -free_ib_content: - kvfree(ib_content); - } - if (vm) { - amdgpu_bo_unreserve(root); - amdgpu_bo_unref(&root); - } - } + if (coredump->num_ibs) + amdgpu_devcoredump_print_ibs(&p, coredump, sizing_pass); return count - iter.remain; } From 8242a8d9d7194d5ef8a8b38a5621ca0966a3ec15 Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Tue, 26 May 2026 22:50:02 -0500 Subject: [PATCH 3971/5214] drm/amdkfd: Let driver decide buffer size at AMDKFD_IOC_GET_DMABUF_INFO ioctl amdkfd driver needs allocate buffer to return bo metadata to user space. The buffer size is controlled by user currently. It is a potential security issue that hostile value (e.g. 2 GiB) lets any render-group user trigger order-MAX allocation/OOM in kernel context. This patch first finds bo metadata size. If the size is smaller than user provided value drive can safely allocate buffer in kernel space and copy to user space buffer. If not, driver will let user know, not allocate and copy. User will redo with new buffer in user space. This patch lets driver decide buffer allocation size to avoid potential hostile size from user space. Signed-off-by: Xiaogang Chen Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit f54ce9e8cbd3abe0eda3a285f54dc4f572fe589a) --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 23 ++++++++++++++++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 ++-------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 9783a3cefb04..da325863ad76 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -558,7 +558,7 @@ uint32_t amdgpu_amdkfd_get_max_engine_clock_in_mhz(struct amdgpu_device *adev) int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, struct amdgpu_device **dmabuf_adev, - uint64_t *bo_size, void *metadata_buffer, + uint64_t *bo_size, void **metadata_buffer, size_t buffer_size, uint32_t *metadata_size, uint32_t *flags, int8_t *xcp_id) { @@ -593,9 +593,24 @@ int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, *dmabuf_adev = adev; if (bo_size) *bo_size = amdgpu_bo_size(bo); - if (metadata_buffer) - r = amdgpu_bo_get_metadata(bo, metadata_buffer, buffer_size, - metadata_size, &metadata_flags); + if (metadata_buffer) { + /* first get metadata_size by buffer = NULL */ + r = amdgpu_bo_get_metadata(bo, NULL, 0, + metadata_size, NULL); + + /* user buf_size is bigger than bo metadata_size + * allocate a buf at kernel space and copy */ + if (*metadata_size <= buffer_size) { + *metadata_buffer = kzalloc(*metadata_size, GFP_KERNEL); + + if (!*metadata_buffer) + return -ENOMEM; + + r = amdgpu_bo_get_metadata(bo, *metadata_buffer, *metadata_size, + NULL, &metadata_flags); + } else + r = -EINVAL; + } if (flags) { *flags = (bo->preferred_domains & AMDGPU_GEM_DOMAIN_VRAM) ? KFD_IOC_ALLOC_MEM_FLAGS_VRAM diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 5333e052d56d..e443a7277299 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -262,7 +262,7 @@ uint64_t amdgpu_amdkfd_get_gpu_clock_counter(struct amdgpu_device *adev); uint32_t amdgpu_amdkfd_get_max_engine_clock_in_mhz(struct amdgpu_device *adev); int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, struct amdgpu_device **dmabuf_adev, - uint64_t *bo_size, void *metadata_buffer, + uint64_t *bo_size, void **metadata_buffer, size_t buffer_size, uint32_t *metadata_size, uint32_t *flags, int8_t *xcp_id); int amdgpu_amdkfd_get_pcie_bandwidth_mbytes(struct amdgpu_device *adev, bool is_min); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index a2b100d14425..6a991ffa53fc 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1562,16 +1562,10 @@ static int kfd_ioctl_get_dmabuf_info(struct file *filep, if (!dev) return -EINVAL; - if (args->metadata_ptr) { - metadata_buffer = kzalloc(args->metadata_size, GFP_KERNEL); - if (!metadata_buffer) - return -ENOMEM; - } - /* Get dmabuf info from KGD */ r = amdgpu_amdkfd_get_dmabuf_info(dev->adev, args->dmabuf_fd, &dmabuf_adev, &args->size, - metadata_buffer, args->metadata_size, + &metadata_buffer, args->metadata_size, &args->metadata_size, &flags, &xcp_id); if (r) goto exit; @@ -1583,7 +1577,7 @@ static int kfd_ioctl_get_dmabuf_info(struct file *filep, args->flags = flags; /* Copy metadata buffer to user mode */ - if (metadata_buffer) { + if (metadata_buffer && args->metadata_ptr) { r = copy_to_user((void __user *)args->metadata_ptr, metadata_buffer, args->metadata_size); if (r != 0) From 516bf737a5602875f6c28d1028967837c8edf2c0 Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Tue, 16 Jun 2026 12:54:49 -0500 Subject: [PATCH 3972/5214] drm/amdkfd: check find_first_zero_bit before __set_bit on kfd->doorbell_bitmap If inx from find_first_zero_bit is beyond range not need set doorbell_bitmap. Signed-off-by: Xiaogang Chen Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2664ce9143d174651a793d96a6a2326050c4f45a) --- drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index 05c74887fd6f..fdcf7f2d1b5b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -153,14 +153,16 @@ void __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, u32 inx; mutex_lock(&kfd->doorbell_mutex); + inx = find_first_zero_bit(kfd->doorbell_bitmap, PAGE_SIZE / sizeof(u32)); + if (inx >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) { + mutex_unlock(&kfd->doorbell_mutex); + return NULL; + } __set_bit(inx, kfd->doorbell_bitmap); mutex_unlock(&kfd->doorbell_mutex); - if (inx >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) - return NULL; - *doorbell_off = amdgpu_doorbell_index_on_bar(kfd->adev, kfd->doorbells, inx, From 2321831d7e95d4e1abaff3ffd682be9dd45db62e Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Tue, 16 Jun 2026 13:25:56 -0500 Subject: [PATCH 3973/5214] drm/amdkfd: Use memdup_array_user to copy data from/to user space at kfd ioctls Several kfd ioctls need transfer array data from/to user space. Kfd driver uses kmalloc_array with user provided size. That can oversize alloc or 32-bit wrap with hostile value. Replace it by memdup_array_user that does overflow checking and allocates through dedicated slab caches, also physical continuous as kmalloc. Signed-off-by: Xiaogang Chen Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 4eca4742eb215951f9739ffe0122d179d545a7a4) --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 46 +++++++----------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 6a991ffa53fc..531e20748198 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1299,18 +1299,11 @@ static int kfd_ioctl_map_memory_to_gpu(struct file *filep, return -EINVAL; } - devices_arr = kmalloc_array(args->n_devices, sizeof(*devices_arr), - GFP_KERNEL); - if (!devices_arr) - return -ENOMEM; + devices_arr = memdup_array_user((void *)args->device_ids_array_ptr, + args->n_devices, sizeof(*devices_arr)); - err = copy_from_user(devices_arr, - (void __user *)args->device_ids_array_ptr, - args->n_devices * sizeof(*devices_arr)); - if (err != 0) { - err = -EFAULT; - goto copy_from_user_failed; - } + if (IS_ERR(devices_arr)) + return PTR_ERR(devices_arr); mutex_lock(&p->mutex); pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(args->handle)); @@ -1391,7 +1384,6 @@ static int kfd_ioctl_map_memory_to_gpu(struct file *filep, map_memory_to_gpu_failed: sync_memory_failed: mutex_unlock(&p->mutex); -copy_from_user_failed: kfree(devices_arr); return err; @@ -1416,18 +1408,11 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, return -EINVAL; } - devices_arr = kmalloc_array(args->n_devices, sizeof(*devices_arr), - GFP_KERNEL); - if (!devices_arr) - return -ENOMEM; + devices_arr = memdup_array_user((void *)args->device_ids_array_ptr, + args->n_devices, sizeof(*devices_arr)); - err = copy_from_user(devices_arr, - (void __user *)args->device_ids_array_ptr, - args->n_devices * sizeof(*devices_arr)); - if (err != 0) { - err = -EFAULT; - goto copy_from_user_failed; - } + if (IS_ERR(devices_arr)) + return PTR_ERR(devices_arr); mutex_lock(&p->mutex); pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(args->handle)); @@ -1493,7 +1478,6 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, unmap_memory_from_gpu_failed: sync_memory_failed: mutex_unlock(&p->mutex); -copy_from_user_failed: kfree(devices_arr); return err; } @@ -2353,17 +2337,11 @@ static int criu_restore_devices(struct kfd_process *p, if (*priv_offset + (args->num_devices * sizeof(*device_privs)) > max_priv_data_size) return -EINVAL; - device_buckets = kmalloc_objs(*device_buckets, args->num_devices); - if (!device_buckets) - return -ENOMEM; + device_buckets = memdup_array_user((void *)args->devices, + args->num_devices, sizeof(*device_buckets)); - ret = copy_from_user(device_buckets, (void __user *)args->devices, - args->num_devices * sizeof(*device_buckets)); - if (ret) { - pr_err("Failed to copy devices buckets from user\n"); - ret = -EFAULT; - goto exit; - } + if (IS_ERR(device_buckets)) + return PTR_ERR(device_buckets); for (i = 0; i < args->num_devices; i++) { struct kfd_node *dev; From c1dc4ccb82c9e56325d8e7514ca4c90bd1efb351 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 1 Jun 2026 15:08:22 +0100 Subject: [PATCH 3974/5214] drm/amdgpu: Fix context pstate override handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are several problems in the context pstate handling code. The most serious ones are potential use-after-free and NULL pointer dereferences at context initialization time. Both are due amdgpu_ctx_init() not holding the adev->pm.stable_pstate_ctx_lock, which is otherwise used from both sysfs and the context code itself for modifying and clearing the stored context pointer. Second issue is that context fini can trample over the pstate configuration set via sysfs. This is due the restore state (ctx->stable_pstate) being saved at context init time, and not if, or when the context actually changes the pstate. As the context exits it will therefore incorrectly restore to what was set before the sysfs override was requested. The simplest fix is to drastically simplify how the state is tracked, by clearly defining the points at which pstate ownership is taken and released, and to handle all transitions under the correct lock. Instead of at context init time, the previous state is saved only at the point the context overrides the current state, and is restored on context exit only if the context is still the owner of the current override state. Signed-off-by: Tvrtko Ursulin Fixes: 79610d304133 ("drm/amdgpu: fix pstate setting issue") Cc: Chengming Gui Cc: Alex Deucher Cc: "Christian König" Signed-off-by: Alex Deucher (cherry picked from commit 1b5e413713c0a93bc1818394d0ce49aaad21bd27) Cc: # v6.1+ --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 73 +++++++++++++++---------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 0d7f6cd74f79..ce35b415093d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -326,7 +326,6 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, struct drm_file *filp, struct amdgpu_ctx *ctx) { struct amdgpu_fpriv *fpriv = filp->driver_priv; - u32 current_stable_pstate; int r; r = amdgpu_ctx_priority_permit(filp, priority); @@ -344,36 +343,21 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm); ctx->init_priority = priority; ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET; - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r) - return r; - - if (mgr->adev->pm.stable_pstate_ctx) - ctx->stable_pstate = mgr->adev->pm.stable_pstate_ctx->stable_pstate; - else - ctx->stable_pstate = current_stable_pstate; + ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; return 0; } -static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, - u32 stable_pstate) +static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) { struct amdgpu_device *adev = ctx->mgr->adev; enum amd_dpm_forced_level level; + struct amdgpu_ctx *current_ctx; u32 current_stable_pstate; - int r; + int r = 0; - mutex_lock(&adev->pm.stable_pstate_ctx_lock); - if (adev->pm.stable_pstate_ctx && adev->pm.stable_pstate_ctx != ctx) { - r = -EBUSY; - goto done; - } - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r || (stable_pstate == current_stable_pstate)) - goto done; + lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock); switch (stable_pstate) { case AMDGPU_CTX_STABLE_PSTATE_NONE: @@ -392,17 +376,41 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; break; default: - r = -EINVAL; - goto done; + return -EINVAL; } - r = amdgpu_dpm_force_performance_level(adev, level); + current_ctx = adev->pm.stable_pstate_ctx; + if (current_ctx && current_ctx != ctx) + return -EBUSY; - if (level == AMD_DPM_FORCED_LEVEL_AUTO) - adev->pm.stable_pstate_ctx = NULL; - else + r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); + if (r || current_stable_pstate == stable_pstate) + return r; + + r = amdgpu_dpm_force_performance_level(adev, level); + if (r) + return r; + + if (!current_ctx) { adev->pm.stable_pstate_ctx = ctx; -done: + /* + * Serialized by context taking ownership for the first time + * while holding adev->pm.stable_pstate_ctx_lock). + */ + WRITE_ONCE(ctx->stable_pstate, current_stable_pstate); + } + + return 0; +} + +static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) +{ + struct amdgpu_device *adev = ctx->mgr->adev; + int r; + + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return r; @@ -428,7 +436,12 @@ static void amdgpu_ctx_fini(struct kref *ref) } if (drm_dev_enter(adev_to_drm(adev), &idx)) { - amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + if (adev->pm.stable_pstate_ctx == ctx) { + __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + adev->pm.stable_pstate_ctx = NULL; + } + mutex_unlock(&adev->pm.stable_pstate_ctx_lock); drm_dev_exit(idx); } From 808c447df2fe234eb7d9e08ecf53159d291c104c Mon Sep 17 00:00:00 2001 From: Cao Ruichuang Date: Tue, 7 Apr 2026 18:26:13 +0800 Subject: [PATCH 3975/5214] selftests/ftrace: Drop invalid top-level local in test_ownership test_ownership.tc is sourced by ftracetest under /bin/sh. The script currently declares mount_point with local at file scope, which makes /bin/sh abort with "local: not in a function" before the test can reach the eventfs ownership checks. Replace the top-level local declaration with a normal shell variable so kernels that support the gid= tracefs mount option can run the test at all. Link: https://lore.kernel.org/r/20260407102613.81419-1-create0818@163.com Fixes: 8b55572e51805 ("tracing/selftests: Add tracefs mount options test") Signed-off-by: Cao Ruichuang Reviewed-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc index e71cc3ad0bdf..6d00d3c0f493 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc @@ -6,7 +6,7 @@ original_group=`stat -c "%g" .` original_owner=`stat -c "%u" .` -local mount_point=$(get_mount_point) +mount_point=$(get_mount_point) mount_options=$(get_mnt_options "$mount_point") From 8882f8897e554053af9e72f4c2da8b1e2cce56c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:17 +0200 Subject: [PATCH 3976/5214] drm/amdgpu: Respect placement requirements in amdgpu_gtt_mgr functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When testing intersection and compatibility, respect the actual placement requirements. This is a pre-requisite for ensuring that UVD CS BOs do not cross 256M segments. Fixes: ded910f368a5 ("drm/amdgpu: Implement intersect/compatible functions") Suggested-by: Christian König Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit bc06579ca29dee9c245a41b12e39c7bb6938af5d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 30 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index d23a91d029aa..0ea32561c4bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -272,7 +272,20 @@ static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (place->fpfn >= (node->start + num_pages) || + (place->lpfn && place->lpfn <= node->start)) + return false; + + return true; } /** @@ -290,7 +303,20 @@ static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (node->start < place->fpfn || + (place->lpfn && (node->start + num_pages) > place->lpfn)) + return false; + + return true; } /** From ee94a65f192c05c543b4d3ad7137cd696b5c18fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:18 +0200 Subject: [PATCH 3977/5214] drm/amdgpu: Fix amdgpu_bo_move() when old_mem and new_mem are both GTT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UVD code relies on GTT to GTT moves in order to ensure that its BOs don't cross 256M segments. Fixes: bfe5e585b44f ("drm/ttm: move last binding into the drivers.") Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 21fd45e5e2628d00b478590bcc3d14d3de5d45b6) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 2740de94e93c..16c060badaee 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -515,6 +515,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, if (new_mem->mem_type == TTM_PL_TT || new_mem->mem_type == AMDGPU_PL_PREEMPT) { + if (old_mem && (old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT)) { + r = ttm_bo_wait_ctx(bo, ctx); + if (r) + return r; + + amdgpu_ttm_backend_unbind(bo->bdev, bo->ttm); + } + r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, new_mem); if (r) return r; @@ -549,6 +558,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, ttm_bo_assign_mem(bo, new_mem); return 0; } + if ((old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT) && + (new_mem->mem_type == TTM_PL_TT || + new_mem->mem_type == AMDGPU_PL_PREEMPT)) { + amdgpu_bo_move_notify(bo, evict, new_mem); + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); + return 0; + } if (old_mem->mem_type == AMDGPU_PL_GDS || old_mem->mem_type == AMDGPU_PL_GWS || From 8002b744ad70055ef11ff7d0a7d685bfe8ffe6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:19 +0200 Subject: [PATCH 3978/5214] drm/amdgpu/uvd: Place VCPU BO only in VRAM for UVD 4.x and older MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These UVD versions don't fully support GPUVM and are only validated to work when their VCPU BO is placed in VRAM. Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 01b8dfc0660db5d6cdd62c22dc20f774a26ce853) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 3a3bc0d370fa..1e59ca924abe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -188,6 +188,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) const struct common_firmware_header *hdr; unsigned int family_id; int i, j, r; + u32 vcpu_bo_domain; INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); @@ -319,12 +320,20 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8); + /* UVD 5.0 and newer HW can use 64 bit addressing. */ + adev->uvd.address_64_bit = + !amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0); + + vcpu_bo_domain = AMDGPU_GEM_DOMAIN_VRAM; + if (adev->uvd.address_64_bit) + vcpu_bo_domain |= AMDGPU_GEM_DOMAIN_GTT; + for (j = 0; j < adev->uvd.num_uvd_inst; j++) { if (adev->uvd.harvest_config & (1 << j)) continue; + r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM | - AMDGPU_GEM_DOMAIN_GTT, + vcpu_bo_domain, &adev->uvd.inst[j].vcpu_bo, &adev->uvd.inst[j].gpu_addr, &adev->uvd.inst[j].cpu_addr); @@ -339,10 +348,6 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) adev->uvd.filp[i] = NULL; } - /* from uvd v5.0 HW addressing capacity increased to 64 bits */ - if (!amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0)) - adev->uvd.address_64_bit = true; - r = amdgpu_uvd_create_msg_bo_helper(adev, 128 << 10, &adev->uvd.ib_bo); if (r) return r; From 32bd35f068a3507a1b3922cd12ea2985fc58c85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:20 +0200 Subject: [PATCH 3979/5214] drm/amdgpu/uvd: Fix forcing MSG, FB BOs into VCPU segment when it isn't at 0 (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UVD 4.x and older can only access MSG, FEEDBACK buffers from a specific 256M VRAM segment that the VCPU BO is also located in. We already modify all placements of the given BO to ensure the BO is placed within this segment. Previously, it always assumed that the VCPU segment is the first 256M of VRAM, even though under some conditions the VCPU BO could be allocated outside this segment, which made UVD non-functional as the BOs were not inside the same segment as the UVD VCPU BO. Solve that by using the segment where the VCPU BO actually is. This fixes an issue with UVD failing to initialize on SI/CIK when resizable BAR is enabled and the VCPU BO is allocated in a different segment. v2: - For other BOs, keep using the same UVD segment as before. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/3851 Reviewed-by: Christian König Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit cbfd4d3fc2061a1ec8e9d36e65973ac3e813358a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 1e59ca924abe..480bf88def46 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -135,7 +135,7 @@ MODULE_FIRMWARE(FIRMWARE_VEGA12); MODULE_FIRMWARE(FIRMWARE_VEGA20); static void amdgpu_uvd_idle_work_handler(struct work_struct *work); -static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo); +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *abo); static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, uint32_t size, @@ -158,7 +158,7 @@ static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, amdgpu_bo_kunmap(bo); amdgpu_bo_unpin(bo); amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM); - amdgpu_uvd_force_into_uvd_segment(bo); + amdgpu_uvd_force_into_vcpu_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); if (r) goto err; @@ -550,6 +550,24 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp) } } +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *bo) +{ + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vcpu_bo = adev->uvd.inst[0].vcpu_bo; + struct amdgpu_res_cursor vcpu_cur; + + amdgpu_res_first(vcpu_bo->tbo.resource, 0, + amdgpu_bo_size(vcpu_bo), &vcpu_cur); + + bo->placement.num_placement = 1; + bo->placement.placement = &bo->placements[0]; + bo->placements[0].fpfn = ALIGN_DOWN(vcpu_cur.start, SZ_256M) >> PAGE_SHIFT; + bo->placements[0].lpfn = bo->placements[0].fpfn + (SZ_256M >> PAGE_SHIFT); + bo->placements[0].mem_type = vcpu_bo->tbo.resource->mem_type; + if (bo->placements[0].mem_type == TTM_PL_VRAM) + bo->placements[0].flags |= TTM_PL_FLAG_CONTIGUOUS; +} + static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo) { int i; @@ -600,13 +618,10 @@ static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx) if (!ctx->parser->adev->uvd.address_64_bit) { /* check if it's a message or feedback command */ cmd = amdgpu_ib_get_value(ctx->ib, ctx->idx) >> 1; - if (cmd == 0x0 || cmd == 0x3) { - /* yes, force it into VRAM */ - uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; - - amdgpu_bo_placement_from_domain(bo, domain); - } - amdgpu_uvd_force_into_uvd_segment(bo); + if (cmd == 0x0 || cmd == 0x3) + amdgpu_uvd_force_into_vcpu_segment(bo); + else + amdgpu_uvd_force_into_uvd_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &tctx); } From 85ed06d990ff73212b5a91a406671cabd962e521 Mon Sep 17 00:00:00 2001 From: Jiqian Chen Date: Thu, 4 Jun 2026 18:30:23 +0800 Subject: [PATCH 3980/5214] drm/amdgpu/gfx9: Fix Ring and IB test fail after mode2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For Renior APU with gfx9, in some test scenarios with disabling ring_reset, like accessing an unmapped invalid address, it can trigger a gpu job timeout event, then driver uses Mode2 reset to reset GPU, but after Mode2 compute Ring test and IB test fail randomly. It because the HQDs of MECs are always active before or after Mode2, that causes MECs use stale HQDs when MECs are unhalted before driver restore MQDs, and causes CPC and CPF are still stuck after Mode2, then causes compute Ring and IB tests fail. So, add sequences to deactivate HQDs of MECs in suspend IP function of the resetting process. v2: Move all sequences into a new function gfx_v9_0_cp_mode2_clear_state (Ray Huang) To check reset Mode2 method in the if condition (Ray Huang) v3: Move all sequences before Mode2 instead of after Mode2 (Timur Kristóf) v4: Call amdgpu_gfx_rlc_enter/exit_safe_mode int the begin and end of gfx_v9_0_deactivate_kcq_hqd (Alex Deucher) Signed-off-by: Jiqian Chen Reviewed-by: Huang Rui Reviewed-by: Timur Kristóf Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit c3988a7ad4799514447294f04f063b422e0551df) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 47721d0c3781..81a759a98725 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -4071,6 +4071,41 @@ static int gfx_v9_0_hw_init(struct amdgpu_ip_block *ip_block) return r; } +static void gfx_v9_0_deactivate_kcq_hqd(struct amdgpu_device *adev) +{ + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + for (int i = 0; i < adev->gfx.num_compute_rings; i++) { + u32 tmp; + struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; + + mutex_lock(&adev->srbm_mutex); + soc15_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, 0); + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + /* disable the queue if it's active */ + if (tmp & CP_HQD_ACTIVE__ACTIVE_MASK) { + int j; + + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 1); + for (j = 0; j < adev->usec_timeout; j++) { + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + if (!(tmp & CP_HQD_ACTIVE__ACTIVE_MASK)) + break; + udelay(1); + } + if (j == AMDGPU_MAX_USEC_TIMEOUT) { + DRM_DEBUG("comp_%u_%u_%u dequeue request failed.\n", + ring->me, ring->pipe, ring->queue); + /* Manual disable if dequeue request times out */ + WREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE, 0); + } + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 0); + } + soc15_grbm_select(adev, 0, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + } + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); +} + static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -4095,6 +4130,10 @@ static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) return 0; } + if ((adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev) && + amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_MODE2) + gfx_v9_0_deactivate_kcq_hqd(adev); + /* Use deinitialize sequence from CAIL when unbinding device from driver, * otherwise KIQ is hanging when binding back */ From b89d58b6595d79dc3fe75e213e1f4c5efd0251d4 Mon Sep 17 00:00:00 2001 From: Gerhard Schwanzer Date: Tue, 16 Jun 2026 10:56:06 +0000 Subject: [PATCH 3981/5214] drm/amdkfd: Use exclusive bounds for SVM split alignment checks SVM ranges use inclusive page indices: prange->last is the last page in the range. The split-remap logic introduced by commit 448ee45353ef ("drm/amdkfd: Use huge page size to check split svm range alignment") uses ALIGN_DOWN(prange->last, 512) to determine whether the original range can contain a 2MB huge-page mapping. That aligns the last page itself down. Thus a range ending one page before the next 2MB boundary is classified as if the final 2MB block did not exist. When such a range is split inside that final block, the split head or tail can be left off the remap list even though it was derived from an original range that may have PMD mappings. Use prange->last + 1 as the exclusive upper bound when computing the original range's last 2MB-aligned boundary. Then use the actual split boundary for the head and tail alignment checks: tail->start for a tail split, and new_start for a head split. new_start is equivalent to head->last + 1 and directly names the exclusive end of the split head. Using head->last for the head-side check can both remap a head that ends exactly one page before a 2MB boundary and miss a head whose split boundary is one page after such a boundary. Philip Yang pointed out in the review of the original change that this condition should use head->last + 1 or new_start. Xiaogang Chen identified the inclusive-last cause and posted the candidate fix in the regression thread. With the culprit change active and the local revert not applied, the unchanged C/HSA reproducer completes 10/10 runs with this change on an RX 7600 XT. Fixes: 448ee45353ef ("drm/amdkfd: Use huge page size to check split svm range alignment") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/4914 Link: https://lore.kernel.org/stable/IA1PR12MB85172F7FE9157C092EDA46A0E3112@IA1PR12MB8517.namprd12.prod.outlook.com/ Link: https://lore.kernel.org/all/32ce2b72-aa16-4202-9f99-92e3cd4408bc@amd.com/ Suggested-by: Xiaogang Chen Acked-by: Alex Deucher Signed-off-by: Gerhard Schwanzer Signed-off-by: Alex Deucher (cherry picked from commit a60ea15807126b148a328051636977a33ad0e9bb) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 5a56d86b3ecf..0900bb23349e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1144,7 +1144,7 @@ static int svm_range_split_tail(struct svm_range *prange, uint64_t new_last, struct list_head *insert_list, struct list_head *remap_list) { - unsigned long last_align_down = ALIGN_DOWN(prange->last, 512); + unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512); unsigned long start_align = ALIGN(prange->start, 512); bool huge_page_mapping = last_align_down > start_align; struct svm_range *tail = NULL; @@ -1168,7 +1168,7 @@ static int svm_range_split_head(struct svm_range *prange, uint64_t new_start, struct list_head *insert_list, struct list_head *remap_list) { - unsigned long last_align_down = ALIGN_DOWN(prange->last, 512); + unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512); unsigned long start_align = ALIGN(prange->start, 512); bool huge_page_mapping = last_align_down > start_align; struct svm_range *head = NULL; @@ -1181,8 +1181,8 @@ svm_range_split_head(struct svm_range *prange, uint64_t new_start, list_add(&head->list, insert_list); - if (huge_page_mapping && head->last + 1 > start_align && - head->last + 1 < last_align_down && (!IS_ALIGNED(head->last, 512))) + if (huge_page_mapping && new_start > start_align && + new_start < last_align_down && !IS_ALIGNED(new_start, 512)) list_add(&head->update_list, remap_list); return 0; From 29b5def20a2cc3d7df375bf3803980c86f7b10ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Wed, 13 May 2026 19:08:47 +0200 Subject: [PATCH 3982/5214] amdgpu/ih6.1: Fix minor version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report the correct version of IH v6.1 (previously it showed v6.0). Reviewed-by: Tvrtko Ursulin Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 940d33ebbcdebaf095fade86e9c981ad8789aee2) --- drivers/gpu/drm/amd/amdgpu/ih_v6_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c index 95b3f4e55ec3..699c274d357e 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c @@ -790,7 +790,7 @@ static void ih_v6_1_set_interrupt_funcs(struct amdgpu_device *adev) const struct amdgpu_ip_block_version ih_v6_1_ip_block = { .type = AMD_IP_BLOCK_TYPE_IH, .major = 6, - .minor = 0, + .minor = 1, .rev = 0, .funcs = &ih_v6_1_ip_funcs, }; From ba2977dcce72127986fbad76c4c67f134e2f69ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Wed, 13 May 2026 19:08:49 +0200 Subject: [PATCH 3983/5214] drm/amdgpu: Use system unbound workqueue for soft IH ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the kernel to dispatch the soft IH work on other CPUs. Otherwise it can happen that the soft IH ring fills up before it actually starts processing anything, which can easily happen with retry page faults, in which case the CP repeatedly spams the CPU with a lot of interrupts. This significantly improves retry page fault handling on GPUs that don't have the filter CAM and must rely on software based filtering. Reviewed-by: Tvrtko Ursulin Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 3cdff3c8b93c2834977224d9c2b201fc334dd184) --- drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 40b8506ac66f..53be764968e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -545,7 +545,7 @@ void amdgpu_irq_delegate(struct amdgpu_device *adev, unsigned int num_dw) { amdgpu_ih_ring_write(adev, &adev->irq.ih_soft, entry->iv_entry, num_dw); - schedule_work(&adev->irq.ih_soft_work); + queue_work(system_unbound_wq, &adev->irq.ih_soft_work); } /** From c7fdbc2c2f26b9c397eb3aad2fdc54dbd85f68e1 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Mon, 8 Jun 2026 13:39:34 +0800 Subject: [PATCH 3984/5214] selftests/uevent: increase __UEVENT_BUFFER_SIZE to avoid ENOBUFS on busy systems The kselftests case uevent.uevent_filtering fails reproducibly on busy systems (e.g. Intel EMR / AMD servers) with: No buffer space available - Failed to receive uevent The listener binds the NETLINK_KOBJECT_UEVENT socket to all 32 multicast groups (nl_groups = -1) but only sets SO_RCVBUF to 4 KiB (__UEVENT_BUFFER_SIZE = 2048 * 2). On hosts with many devices, the kernel and userspace daemons (udev/systemd) constantly emit uevents on multiple groups, plus the test itself triggers 10 add events in a row. The 4 KiB receive buffer overflows before the listener can drain it, recvmsg() returns -ENOBUFS, and the test bails out as failure. Increase __UEVENT_BUFFER_SIZE to 1 MiB so the receive buffer is large enough to absorb the burst of uevents on busy systems. After this change the test passes consistently across dozens of runs on Intel EMR and AMD platforms. Link: https://lore.kernel.org/20260608053934.4059533-1-kanie@linux.alibaba.com Signed-off-by: Guixin Liu Cc: Christian Brauner Cc: Shuah Khan Cc: Wei Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/uevent/uevent_filtering.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/uevent/uevent_filtering.c b/tools/testing/selftests/uevent/uevent_filtering.c index 974b076f9235..33a09f66d7e2 100644 --- a/tools/testing/selftests/uevent/uevent_filtering.c +++ b/tools/testing/selftests/uevent/uevent_filtering.c @@ -22,7 +22,7 @@ #include "kselftest_harness.h" #define __DEV_FULL "/sys/devices/virtual/mem/full/uevent" -#define __UEVENT_BUFFER_SIZE (2048 * 2) +#define __UEVENT_BUFFER_SIZE (1024 * 1024) #define __UEVENT_HEADER "add@/devices/virtual/mem/full" #define __UEVENT_HEADER_LEN sizeof("add@/devices/virtual/mem/full") #define __UEVENT_LISTEN_ALL -1 From 5108f4765637bd0ac5ea2897dc7d537486a09885 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 15:52:15 +0000 Subject: [PATCH 3985/5214] fat: reject BPB volumes whose data area starts beyond total sectors fat_fill_super() subtracts sbi->data_start from the BPB total sector count before computing the number of clusters. A malformed image can declare a total sector count smaller than data_start, causing the subtraction to underflow and the mount code to derive a plausible cluster count from the FAT length instead. Reject such images before the subtraction. In QEMU, a crafted FAT image with total_sectors=2 and data_start=3 mounted successfully before the fix and reading a file returned bytes stored past the BPB-declared end of the volume. With this change, the same image is rejected during mount. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260605155216.2126545-1-sam.moelius@trailofbits.com Signed-off-by: Samuel Moelius Acked-by: OGAWA Hirofumi Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/fat/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b032bbc6855c..3aa52481ad5c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1738,6 +1738,14 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, if (total_sectors == 0) total_sectors = bpb.fat_total_sect; + if (total_sectors < sbi->data_start) { + if (!silent) + fat_msg(sb, KERN_ERR, + "data area starts beyond volume (%lu > %u)", + sbi->data_start, total_sectors); + goto out_invalid; + } + total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus; if (!is_fat32(sbi)) From 452a8467be8143747292218212671deeb186d2ae Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 10 Jun 2026 19:23:11 -0500 Subject: [PATCH 3986/5214] ocfs2: fix UBSAN array-index-out-of-bounds in ocfs2_sum_rightmost_rec [BUG] On-disk corruption setting l_next_free_rec to 0 in an inode's embedded extent list triggers a UBSAN panic on the next write to that file. [CAUSE] ocfs2_sum_rightmost_rec() computes i = le16_to_cpu(el->l_next_free_rec) - 1 and accesses el->l_recs[i] without validating i. When l_next_free_rec is 0, i becomes -1; when l_next_free_rec exceeds l_count, i falls past the end of the array. Either case violates the __counted_by_le(l_count) annotation on l_recs[] and triggers UBSAN. [FIX] Validate the inode's embedded extent list when the inode is read, in ocfs2_validate_inode_block(): l_count must be non-zero and no larger than the inode block can hold, and l_next_free_rec must not exceed l_count. A corrupt list is rejected at read time, before the b-tree code can index l_recs[] out of bounds. Link: https://lore.kernel.org/ain_780qc0P4ypNd@dev Signed-off-by: Ian Bridges Reported-by: syzbot+be16e33db01e6644db7a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=be16e33db01e6644db7a Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 6fc29920ecb2..662dbc845b8b 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1696,6 +1696,38 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + if (ocfs2_dinode_has_extents(di)) { + struct ocfs2_extent_list *el = &di->id2.i_list; + u16 count = le16_to_cpu(el->l_count); + u16 next_free = le16_to_cpu(el->l_next_free_rec); + + if (count == 0) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count is zero\n", + (unsigned long long)bh->b_blocknr); + goto bail; + } + /* + * The exact capacity depends on i_xattr_inline_size, another + * unvalidated on-disk field. Inline xattrs only shrink the + * list, so the no-xattr maximum is a safe upper bound that a + * valid l_count never exceeds. + */ + if (count > ocfs2_extent_recs_per_inode(sb)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count %u exceeds max %u\n", + (unsigned long long)bh->b_blocknr, count, + ocfs2_extent_recs_per_inode(sb)); + goto bail; + } + if (next_free > count) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_next_free_rec %u exceeds l_count %u\n", + (unsigned long long)bh->b_blocknr, next_free, count); + goto bail; + } + } + rc = 0; bail: From 07669b0abe4ce76c716e8437e198e1337cf43d1f Mon Sep 17 00:00:00 2001 From: Shardul Deshpande Date: Fri, 12 Jun 2026 23:46:32 +0530 Subject: [PATCH 3987/5214] treewide: fix transposed "sign" typos and update spelling.txt Several comments transpose the letters in "assigned" and "unsigned", spelling them with "sing" instead of "sign". Correct all of them. Of these, the misspelling of "assigned" is not yet flagged by checkpatch, so also add it to scripts/spelling.txt. The remaining matches of `grep -ri singed` are RISINGEDGE register and enum names, not typos. Link: https://lore.kernel.org/20260612181633.734458-1-iamsharduld@gmail.com Signed-off-by: Shardul Deshpande Suggested-by: Andrew Morton Reviewed-by: SeongJae Park Cc: Joe Perches Signed-off-by: Andrew Morton --- arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi | 2 +- drivers/gpu/drm/drm_color_mgmt.c | 2 +- drivers/scsi/elx/efct/efct_hw.h | 2 +- drivers/scsi/isci/host.c | 2 +- drivers/scsi/isci/port.c | 2 +- drivers/scsi/isci/port_config.c | 2 +- kernel/bpf/helpers.c | 2 +- scripts/spelling.txt | 1 + 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi index 469a5d103e3d..9ae9af40f4d2 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi @@ -562,7 +562,7 @@ &sdc1_rclk { * * This has entries that are defined by Qcard even if they go to the main * board. In cases where the pulls may be board dependent we defer those - * settings to the board device tree. Drive strengths tend to be assinged here + * settings to the board device tree. Drive strengths tend to be assigned here * but could conceivably be overwridden by board device trees. */ diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index e7db4e4ea700..cb7f51ff83bf 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -54,7 +54,7 @@ * &drm_crtc_state.degamma_lut. * * “DEGAMMA_LUT_SIZE”: - * Unsinged range property to give the size of the lookup table to be set + * Unsigned range property to give the size of the lookup table to be set * on the DEGAMMA_LUT property (the size depends on the underlying * hardware). If drivers support multiple LUT sizes then they should * publish the largest size, and sub-sample smaller sized LUTs (e.g. for diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index f3f4aa78dce9..20aae04021aa 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -59,7 +59,7 @@ #define EFCT_HW_EQ_DEPTH 1024 /* - * A CQ will be assinged to each WQ + * A CQ will be assigned to each WQ * (CQ must have 2X entries of the WQ for abort * processing), plus a separate one for each RQ PAIR and one for MQ */ diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index ff199bab5d1a..1592fc552e6c 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -2488,7 +2488,7 @@ struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag) * free remote node ids * @idev: This is the device object which is requesting the a remote node * id - * @node_id: This is the remote node id that is assinged to the device if one + * @node_id: This is the remote node id that is assigned to the device if one * is available * * enum sci_status SCI_FAILURE_OUT_OF_RESOURCES if there are no available remote diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 10bd2aac2cb4..0dea0abb9927 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -464,7 +464,7 @@ static enum sci_status sci_port_set_phy(struct isci_port *iport, struct isci_phy { /* Check to see if we can add this phy to a port * that means that the phy is not part of a port and that the port does - * not already have a phy assinged to the phy index. + * not already have a phy assigned to the phy index. */ if (!iport->phy_table[iphy->phy_index] && !phy_get_non_dummy_port(iphy) && diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 3b4820defe63..d709c9df823c 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -259,7 +259,7 @@ sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost, if (!phy_mask) continue; /* - * Make sure that one or more of the phys were not already assinged to + * Make sure that one or more of the phys were not already assigned to * a different port. */ if ((phy_mask & ~assigned_phy_mask) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index b5314c9fed3c..6f56ba88fb61 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -3601,7 +3601,7 @@ __bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz, return ret + 1; } -/* Keep unsinged long in prototype so that kfunc is usable when emitted to +/* Keep unsigned long in prototype so that kfunc is usable when emitted to * vmlinux.h in BPF programs directly, but note that while in BPF prog, the * unsigned long always points to 8-byte region on stack, the kernel may only * read and write the 4-bytes on 32-bit. diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 2f2e81dbda03..3372873cd7bb 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -176,6 +176,7 @@ assgined||assigned assiged||assigned assigment||assignment assigments||assignments +assinged||assigned assistent||assistant assocaited||associated assocated||associated From 22920541c35a9f23f219038ba5874c843a7c4419 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 11 Jun 2026 14:35:10 -0700 Subject: [PATCH 3988/5214] ocfs2: avoid moving extents to occupied clusters For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical me_goal. ocfs2_move_extent() initializes new_phys_cpos from that goal and expects ocfs2_probe_alloc_group() to replace it with a free run in the target block group. The probe currently leaves *phys_cpos unchanged if the scan reaches the end of the group without finding a free run. An occupied goal at the last bit can therefore survive the probe and be passed to __ocfs2_move_extent(), which copies file data into a cluster still owned by another inode before the bitmap is updated. When the probe does find a free run, it also subtracts move_len from the ending bit. The start of an N-bit run ending at i is i - N + 1, so the current calculation can report the bit immediately before the free run. Clear *phys_cpos before scanning and use the correct free-run start. Callers already treat a zero result as -ENOSPC, so failed probes no longer continue with an occupied caller-controlled goal. Link: https://lore.kernel.org/20260611213510.16956-1-kylebot@openai.com Fixes: e6b5859cccfa ("Ocfs2/move_extents: helper to probe a proper region to move in an alloc group.") Fixes: 236b9254f8d1 ("ocfs2: fix non-auto defrag path not working issue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/move_extents.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index c53de4439d93..ad1678ee7cc4 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb, le64_to_cpu(gd->bg_blkno)); + *phys_cpos = 0; + for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) { used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap); @@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, last_free_bits++; if (last_free_bits == move_len) { - i -= move_len; + i = i - move_len + 1; *goal_bit = i; *phys_cpos = base_cpos + i; break; From c1fff9794a165b6b64ae4ad9b54c00bc94e7daed Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 00:54:47 +0000 Subject: [PATCH 3989/5214] lib: interval_tree_test: validate benchmark parameters The interval tree runtime test accepts module parameters that are later used as divisors while generating randomized intervals and while reporting average timings. For example, max_endpoint=1 makes the generated interval end value zero and the next modulo operation divides by that zero value. Reject non-positive counts and require max_endpoint to provide at least one non-zero generated endpoint before the test allocates state or starts the benchmark. [akpm@linux-foundation.org: include printk.h for pr_warn()] Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://lore.kernel.org/20260609005446.1241288.1525a5964698.interval-tree-test-small-max-endpoint-div0@trailofbits.com Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/interval_tree_test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/interval_tree_test.c b/lib/interval_tree_test.c index 16200feacbf3..eba2d3e28980 100644 --- a/lib/interval_tree_test.c +++ b/lib/interval_tree_test.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,27 @@ static inline int span_iteration_check(void) {return 0; } static int interval_tree_test_init(void) { + if (nnodes <= 0) { + pr_warn("nnodes must be positive\n"); + return -EINVAL; + } + if (nsearches <= 0) { + pr_warn("nsearches must be positive\n"); + return -EINVAL; + } + if (perf_loops <= 0) { + pr_warn("perf_loops must be positive\n"); + return -EINVAL; + } + if (search_loops <= 0) { + pr_warn("search_loops must be positive\n"); + return -EINVAL; + } + if (max_endpoint < 2) { + pr_warn("max_endpoint must be at least 2\n"); + return -EINVAL; + } + nodes = kmalloc_objs(struct interval_tree_node, nnodes); if (!nodes) return -ENOMEM; From f9ab30c96b0f00c20c6dac93681bdae3a033d229 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 11 Jun 2026 09:46:38 -0500 Subject: [PATCH 3990/5214] ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits [BUG] A direct write over unwritten extents can panic the kernel in ocfs2_assure_trans_credits() when the journal aborts during DIO completion. The crash is a general protection fault from a NULL pointer dereference. [CAUSE] ocfs2_dio_end_io_write() loops over a direct write's unwritten extents, marking each written under a single journal handle. If the journal aborts (for example after an I/O error) while the extent tree is being updated, the handle is left aborted with its transaction pointer cleared. The extent merge treats that failure as not critical and reports success, so the loop keeps using the handle. ocfs2_assure_trans_credits() reads the handle's remaining credits without first checking whether the handle is aborted, and that read dereferences the cleared transaction pointer. [FIX] A journal abort is recorded in the handle itself, so callers are expected to test the handle rather than rely on a returned error. Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal helpers already do, and return -EROFS when the handle is aborted. Link: https://lore.kernel.org/airKTsM1fRVN-Wj7@dev Fixes: be346c1a6eeb ("ocfs2: fix DIO failure due to insufficient transaction credits") Signed-off-by: Ian Bridges Reported-by: syzbot+e9c15ff790cea6a0cfae@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c15ff790cea6a0cfae Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/journal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index fc54cc798ce3..d8afbc1a76bb 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -473,8 +473,12 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) */ int ocfs2_assure_trans_credits(handle_t *handle, int nblocks) { - int old_nblks = jbd2_handle_buffer_credits(handle); + int old_nblks; + if (is_handle_aborted(handle)) + return -EROFS; + + old_nblks = jbd2_handle_buffer_credits(handle); trace_ocfs2_assure_trans_credits(old_nblks); if (old_nblks >= nblocks) return 0; From ff6f26c58421614b02694ac9d219ac61d924bc68 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 12 Jun 2026 11:50:20 +0000 Subject: [PATCH 3991/5214] ocfs2: fix circular locking dependency in ocfs2_dio_end_io_write A circular locking dependency involves INODE_ALLOC_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, and ORPHAN_DIR_SYSTEM_INODE. 1. ocfs2_mknod() acquires INODE_ALLOC then EXTENT_ALLOC. 2. ocfs2_dio_end_io_write() acquires EXTENT_ALLOC for unwritten extents, then ORPHAN_DIR via ocfs2_del_inode_from_orphan() while still holding EXTENT_ALLOC. 3. ocfs2_wipe_inode() acquires ORPHAN_DIR then INODE_ALLOC via ocfs2_remove_inode. Break the cycle in ocfs2_dio_end_io_write() by freeing the allocation contexts (releasing EXTENT_ALLOC) before acquiring ORPHAN_DIR. WARNING: possible circular locking dependency detected ------------------------------------------------------ is trying to acquire lock: ffff8881e78b33a0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 but task is already holding lock: ffff8881e78b4fa0 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0xe97/0x43b0 fs/ocfs2/inode.c:1299 the existing dependency chain (in reverse order) is: -> #2 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_del_inode_from_orphan+0x12e/0x7a0 fs/ocfs2/namei.c:2728 ocfs2_dio_end_io+0xf9c/0x1370 fs/ocfs2/aops.c:2418 dio_complete+0x25b/0x790 fs/direct-io.c:281 -> #1 (&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_reserve_suballoc_bits+0x16d/0x4840 fs/ocfs2/suballoc.c:882 ocfs2_reserve_new_metadata_blocks+0x415/0x9a0 fs/ocfs2/suballoc.c:1078 ocfs2_mknod+0x10f3/0x2260 fs/ocfs2/namei.c:351 -> #0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 down_write+0x96/0x200 kernel/locking/rwsem.c:1625 inode_lock include/linux/fs.h:1029 [inline] ocfs2_remove_inode fs/ocfs2/inode.c:733 [inline] ocfs2_wipe_inode fs/ocfs2/inode.c:896 [inline] ocfs2_delete_inode fs/ocfs2/inode.c:1157 [inline] ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 Chain exists of: &ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]); *** DEADLOCK *** Link: https://lore.kernel.org/97c902a6-3bcf-43ea-9b70-f1f136a6c3f2@mail.kernel.org Fixes: d647c5b2fbf8 ("ocfs2: split transactions in dio completion to avoid credit exhaustion") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+b225d4dfce6219600c42@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b225d4dfce6219600c42 Link: https://syzkaller.appspot.com/ai_job?id=0b53ce1e-2972-4192-aa85-8097a702762c Signed-off-by: Aleksandr Nogikh Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Joseph Qi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton --- fs/ocfs2/aops.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 6ec198bdab12..4acdbb70882c 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2372,6 +2372,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode, unlock: up_write(&oi->ip_alloc_sem); + if (data_ac) { + ocfs2_free_alloc_context(data_ac); + data_ac = NULL; + } + if (meta_ac) { + ocfs2_free_alloc_context(meta_ac); + meta_ac = NULL; + } + /* everything looks good, let's start the cleanup */ if (!ret && dwc->dw_orphaned) { BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); @@ -2383,10 +2392,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode, ocfs2_inode_unlock(inode, 1); brelse(di_bh); out: - if (data_ac) - ocfs2_free_alloc_context(data_ac); - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); ocfs2_run_deallocs(osb, &dealloc); ocfs2_dio_free_write_ctx(inode, dwc); From e586644d0a89b6c63b77ae717f19d70181faee76 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 11 Jun 2026 12:24:49 +0200 Subject: [PATCH 3992/5214] net: pse-pd: set user byte command SUB2 field The Set User Byte to Save command has three subject bytes. The PD692x0 protocol guides defines SUB2 with value 0x4e, while SUB1 carries the NVM user byte. Template only initialized SUB and SUB1. Fill SUB2 explicitly so the command matches the documented layout. Signed-off-by: Robert Marko Acked-by: Kory Maincent Link: https://patch.msgid.link/20260611102517.445549-1-robert.marko@sartura.hr Signed-off-by: Jakub Kicinski --- drivers/net/pse-pd/pd692x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c index cb377d5ba7af..209de9cec849 100644 --- a/drivers/net/pse-pd/pd692x0.c +++ b/drivers/net/pse-pd/pd692x0.c @@ -200,7 +200,7 @@ static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = { }, [PD692X0_MSG_SET_USER_BYTE] = { .key = PD692X0_KEY_PRG, - .sub = {0x41, PD692X0_USER_BYTE}, + .sub = {0x41, PD692X0_USER_BYTE, 0x4e}, .data = {0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e}, }, From 8165f7ff57d9667d2bb477ef6af83ede7fed4ad7 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:35 +0800 Subject: [PATCH 3993/5214] net: ip_gre: require CAP_NET_ADMIN in the device netns for changelink A tunnel changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Add rtnl_dev_link_net_capable() next to rtnl_get_net_ns_capable() in net/core/rtnetlink.c. It requires CAP_NET_ADMIN in the link netns and is skipped when the link netns is dev_net(dev), where the rtnl path already checked it. The other patches in this series use the same helper. Gate ipgre_changelink() and erspan_changelink() with it, at the top of the op before any attribute is parsed, because the parsers update live tunnel fields first. ipgre_netlink_parms() sets t->collect_md before ip_tunnel_changelink() runs. Commit 8b484efd5cb4 ("ip6: vti: Use ip6_tnl.net in vti6_siocdevprivate().") added the same check on the ioctl path. This adds it on RTM_NEWLINK. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: b57708add314 ("gre: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-2-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- include/net/rtnetlink.h | 2 ++ net/core/rtnetlink.c | 8 ++++++++ net/ipv4/ip_gre.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index ec65a8cebb99..2bff41aacc98 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -256,6 +256,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer, struct netlink_ext_ack *exterr); struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net); #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 61d095ce1b3b..12aa3aa1688b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2438,6 +2438,14 @@ struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid) } EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net) +{ + return net_eq(link_net, dev_net(dev)) || + ns_capable(link_net->user_ns, CAP_NET_ADMIN); +} +EXPORT_SYMBOL_GPL(rtnl_dev_link_net_capable); + static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh, bool strict_check, struct nlattr **tb, struct netlink_ext_ack *extack) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 208dd48012d9..3efdfb4ffa21 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1457,6 +1457,9 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; @@ -1486,6 +1489,9 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; From 8211a26324667980a463c069469a818e71207e02 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:36 +0800 Subject: [PATCH 3994/5214] net: ipip: require CAP_NET_ADMIN in the device netns for changelink ipip_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 6c742e714d8c ("ipip: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-3-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv4/ipip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 4f89a03e0b49..b643194f57d2 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -494,6 +494,9 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], bool collect_md; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { int err = ip_tunnel_encap_setup(t, &ipencap); From 95cceadbfd52d7239bd730afdda0655287d77425 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:37 +0800 Subject: [PATCH 3995/5214] net: ip_vti: require CAP_NET_ADMIN in the device netns for changelink vti_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 895de9a3488a ("vti4: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-4-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv4/ip_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 95b6bb78fcd2..3b80929994a0 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -596,6 +596,9 @@ static int vti_changelink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm_kern p; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + vti_netlink_parms(data, &p, &fwmark); return ip_tunnel_changelink(dev, tb, &p, fwmark); } From 2496fa0b7d180b3ad356b514e7ff93bb14e6140a Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:38 +0800 Subject: [PATCH 3996/5214] net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 0bd8762824e7 ("ip6tnl: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-5-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 73fc5a0b8203..d7c90a8533ec 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -2103,6 +2103,9 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip_tunnel_encap ipencap; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == ip6n->fb_tnl_dev) { if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { /* iproute2 always sets TUNNEL_ENCAP_FLAG_CSUM6, so From f00a50876d2818bd6dc86fa98b3ef360884c53c8 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:39 +0800 Subject: [PATCH 3997/5214] net: ip6_gre: require CAP_NET_ADMIN in the device netns for changelink ip6gre_changelink() and ip6erspan_changelink() operate on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate both ops on rtnl_dev_link_net_capable() at their top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 690afc165bb3 ("net: ip6_gre: fix moving ip6gre between namespaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-6-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_gre.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 795be59946f7..7c09a269b352 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2047,6 +2047,9 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); struct __ip6_tnl_parm p; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) return PTR_ERR(t); @@ -2266,6 +2269,9 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct ip6gre_net *ign; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + ign = net_generic(t->net, ip6gre_net_id); t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) From e2ac3b242c37dff323a964962e43854f4b1a2b79 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:40 +0800 Subject: [PATCH 3998/5214] net: ip6_vti: require CAP_NET_ADMIN in the device netns for changelink vti6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 61220ab34948 ("vti6: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-7-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index d871cab6938d..ab94b3a4ba9c 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1046,6 +1046,9 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct vti6_net *ip6n; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + ip6n = net_generic(net, vti6_net_id); if (dev == ip6n->fb_tnl_dev) return -EINVAL; From 095515d89b19b6cc19dfcdc846f97403ed1ebce3 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:41 +0800 Subject: [PATCH 3999/5214] xfrm: xfrm_interface: require CAP_NET_ADMIN in the device netns for changelink xfrmi_changelink() operates on at most two netns, dev_net(dev) and the interface link netns xi->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in xi->net can rewrite an interface that lives in xi->net. Gate xfrmi_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: f203b76d7809 ("xfrm: Add virtual xfrm interfaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-8-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/xfrm/xfrm_interface_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c index 330a05286a56..688306bf62c5 100644 --- a/net/xfrm/xfrm_interface_core.c +++ b/net/xfrm/xfrm_interface_core.c @@ -869,6 +869,9 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], struct net *net = xi->net; struct xfrm_if_parms p = {}; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + xfrmi_netlink_parms(data, &p); if (!p.if_id) { NL_SET_ERR_MSG(extack, "if_id must be non zero"); From e4b4d8410c7ccea25f4b332a077c6b9f5d263228 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Mon, 15 Jun 2026 17:11:00 +0200 Subject: [PATCH 4000/5214] net: ethernet: mtk_eth_soc: fix supported_interface set after phylink_create Everything configured in phylink_config it's assumed to be set before calling phylink_create() to permit correct parsing of all the different modes and capabilities. Commit 51cf06ddafc9 ("net: ethernet: mtk_eth_soc: add support for MT7988 internal 2.5G PHY") while introducing support for 2.5G phy for MT7988, probably due to an auto-rebase, placed the configuration of the INTERNAL interface mode for the supported_interfaces for phylink_config right after phylink_create() introducing a possible problem with supported interfaces parsing. While this doesn't currently create any problem/bug, move setting this bit before phylink_create() to prevent any possible regression in future code change in phylink core. Fixes: 51cf06ddafc9 ("net: ethernet: mtk_eth_soc: add support for MT7988 internal 2.5G PHY") Signed-off-by: Christian Marangi Reviewed-by: Maxime Chevallier Reviewed-by: Daniel Golle Link: https://patch.msgid.link/20260615151106.15438-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 7d771168b990..5d291e50a47b 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4960,6 +4960,11 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) mac_ops = &rt5350_phylink_ops; + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && + id == MTK_GMAC2_ID) + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + mac->phylink_config.supported_interfaces); + phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, mac_ops); @@ -4970,11 +4975,6 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) mac->phylink = phylink; - if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && - id == MTK_GMAC2_ID) - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - mac->phylink_config.supported_interfaces); - SET_NETDEV_DEV(eth->netdev[id], eth->dev); eth->netdev[id]->watchdog_timeo = 5 * HZ; eth->netdev[id]->netdev_ops = &mtk_netdev_ops; From 1f24c0d01db214c9e661915e9972404c96ca73c0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Tue, 16 Jun 2026 01:17:36 +0800 Subject: [PATCH 4001/5214] netdev-genl: report NAPI thread PID in the caller's pid namespace netdev_nl_napi_fill_one() reports the NAPI kthread PID in NETDEV_A_NAPI_PID using task_pid_nr(), which returns the PID in the initial pid namespace. NETDEV_CMD_NAPI_GET does not have GENL_ADMIN_PERM and the netdev genl family is netnsok, so a caller in a child pid namespace can issue it. That caller then sees the kthread's global PID, even though the kthread is not visible in its pid namespace, where the value should be 0. Translate the PID through the caller's pid namespace, the same way commit 3799c2570982 ("io_uring/fdinfo: translate SqThread PID through caller's pid_ns") did for the io_uring SQPOLL thread. The doit and dumpit paths both run synchronously in the caller's context, so task_active_pid_ns(current) is the caller's pid namespace. Fixes: db4704f4e4df ("netdev-genl: Add PID for the NAPI thread") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Joe Damato Reviewed-by: Samiullah Khawaja Link: https://patch.msgid.link/20260615171736.1709318-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/core/netdev-genl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 11b0b91683d7..c15d8d4ca1f8 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -189,7 +190,8 @@ netdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi, goto nla_put_failure; if (napi->thread) { - pid = task_pid_nr(napi->thread); + pid = task_pid_nr_ns(napi->thread, + task_active_pid_ns(current)); if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid)) goto nla_put_failure; } From 65f26d15f7db80b0a3f995c518cdddb50e6dea99 Mon Sep 17 00:00:00 2001 From: Tianchen Ding Date: Mon, 1 Jun 2026 10:32:51 +0800 Subject: [PATCH 4002/5214] selftests/ftrace: Fix trace_marker_raw test on 64K page kernels On ARM64 kernels with 64K pages, the trace_marker_raw test fails because bash's printf builtin uses stdio buffering which splits output into multiple small write() calls to the tracefs file. Since each individual write is within TRACE_MARKER_MAX_SIZE (4096), they all succeed, causing the "too big" write test to incorrectly pass. Fix by writing through dd with iflag=fullblock to guarantee a single atomic write() syscall to trace_marker_raw. Link: https://lore.kernel.org/r/20260601023251.1916483-1-dtcccc@linux.alibaba.com Fixes: 37f46601383a ("selftests/tracing: Add basic test for trace_marker_raw file") Signed-off-by: Tianchen Ding Reviewed-by: Steven Rostedt Signed-off-by: Shuah Khan --- .../ftrace/test.d/00basic/trace_marker_raw.tc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc index 8e905d4fe6dd..f985ff391463 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc @@ -36,15 +36,23 @@ make_str() { data=`printf -- 'X%.0s' $(seq $cnt)` - printf "${val}${data}" + # Return escape-sequence text (e.g. "\003\000..."); the caller + # converts to binary. Shell command substitution strips NUL bytes, + # so the binary form cannot survive being captured into a variable. + printf '%s' "${val}${data}" } write_buffer() { id=$1 size=$2 - # write the string into the raw marker - make_str $id $size > trace_marker_raw + str=`make_str $id $size` + len=`printf "$str" | wc -c` + # Pipe through dd to ensure a single atomic write() syscall + # on architectures with 64K pages, where shell's printf builtin + # uses stdio buffering which may split the output into multiple + # writes. + printf "$str" | dd of=trace_marker_raw bs=$len iflag=fullblock } From a056db30de92945ff8ee6033096678bfbae878e3 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 15 Jun 2026 08:37:04 +0530 Subject: [PATCH 4003/5214] octeontx2-pf: Fix leak of SQ timestamp buffer on teardown The send-queue timestamp ring is allocated with qmem_alloc() when timestamping is used, but otx2_free_sq_res() never freed sq->timestamps, leaking that memory across ifdown and device removal. Add the missing qmem_free() alongside the other SQ companion buffers. Fixes: c9c12d339d93 ("octeontx2-pf: Add support for PTP clock") Cc: Aleksey Makarov Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615030704.504536-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 41a0ebdf201e..b63df5737ff2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1575,6 +1575,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf) qmem_free(pf->dev, sq->sqe_ring); qmem_free(pf->dev, sq->cpt_resp); qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); kfree(sq->sg); kfree(sq->sqb_ptrs); } From 4f6ac65e81625165257131ec2574bb6bb09bd7d8 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 15 Jun 2026 09:01:57 +0530 Subject: [PATCH 4004/5214] octeontx2-af: npc: Log successful MCAM drop-on-non-hit install at debug level npc_install_mcam_drop_rule() used dev_err() after a successful rvu_mbox_handler_npc_mcam_write_entry() call, so normal installs appeared as errors in dmesg. Use dev_dbg() for the success path and keep dev_err() for real failures. Fixes: 3571fe07a090 ("octeontx2-af: Drop rules for NPC MCAM") Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615033157.535237-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index a22decbe3449..91b5947dae06 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -2225,7 +2225,7 @@ int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, return err; } - dev_err(rvu->dev, + dev_dbg(rvu->dev, "%s: Installed single drop on non hit rule at %d, cntr=%d\n", __func__, mcam_idx, req.cntr); From 1c4b39746c4ba32370e9a60801e96181bc1260a3 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 15:00:31 +0800 Subject: [PATCH 4005/5214] net: ehea: unwind probe_port sysfs file on failure ehea_create_device_sysfs() creates probe_port and then remove_port. If the second device_create_file() fails, the helper returns the error but leaves probe_port installed even though probe treats the sysfs setup as failed. Remove probe_port on the remove_port creation failure path so the helper leaves no partial sysfs state behind. Signed-off-by: Pengpeng Hou Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260615070033.43461-1-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/ehea/ehea_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index ff67c4fd66a3..bfc8699a05b9 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3216,6 +3216,8 @@ static int ehea_create_device_sysfs(struct platform_device *dev) goto out; ret = device_create_file(&dev->dev, &dev_attr_remove_port); + if (ret) + device_remove_file(&dev->dev, &dev_attr_probe_port); out: return ret; } From efb8763d7bbb40cff4cc55a6b62c3095a038149c Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Mon, 15 Jun 2026 18:31:18 +0800 Subject: [PATCH 4006/5214] net: ipv4: bound TCP reordering sysctl writes and MTU probe sizes Reject invalid `net.ipv4.tcp_reordering` values before they reach TCP socket state. The sysctl is stored as an `int` but copied into the `u32` `tp->reordering` field for new sockets, so negative writes wrap to large values. With `tcp_mtu_probing=2`, the wrapped value can overflow the `tcp_mtu_probe()` size calculation and drive the MTU probing path into an out-of-bounds read. Route `tcp_reordering` writes through `proc_dointvec_minmax()` and require it to be at least 1. Also require `tcp_max_reordering` to be at least 1 so the configured maximum cannot become negative either. When registering the table for a non-init network namespace, relocate `extra2` pointers that refer into `init_net.ipv4` so the `tcp_reordering` upper bound follows that namespace's `tcp_max_reordering`. Harden `tcp_mtu_probe()` itself by computing `size_needed` as `u64`. This keeps the send queue and window checks from being bypassed through signed integer overflow. Fixes: 91cc17c0e5e5 ("[TCP]: MTUprobe: receiver window & data available checks fixed") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Suggested-by: Eric Dumazet Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/1a5b7e1ef4d70fbad8c8ee0b82d8405f3c964a3d.1781395200.git.bronzed_45_vested@icloud.com Signed-off-by: Jakub Kicinski --- net/ipv4/sysctl_net_ipv4.c | 10 ++++++++-- net/ipv4/tcp_output.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index c0e85cc171ae..ca1180dba1de 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1058,7 +1058,9 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &init_net.ipv4.sysctl_tcp_max_reordering, }, { .procname = "tcp_retries1", @@ -1293,7 +1295,8 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_max_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, }, { .procname = "tcp_dsack", @@ -1676,6 +1679,9 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) */ table[i].mode &= ~0222; } + if (table[i].extra2 >= (void *)&init_net.ipv4 && + table[i].extra2 < (void *)(&init_net.ipv4 + 1)) + table[i].extra2 += (void *)net - (void *)&init_net; } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 26dd751ec72a..00ec4b5900f2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2688,7 +2688,7 @@ static int tcp_mtu_probe(struct sock *sk) struct sk_buff *skb, *nskb, *next; struct net *net = sock_net(sk); int probe_size; - int size_needed; + u64 size_needed; int copy, len; int mss_now; int interval; @@ -2712,7 +2712,7 @@ static int tcp_mtu_probe(struct sock *sk) mss_now = tcp_current_mss(sk); probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high + icsk->icsk_mtup.search_low) >> 1); - size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; + size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; /* When misfortune happens, we are reprobing actively, * and then reprobe timer has expired. We stick with current From b50fa1e07cf875609b9d34c5c8b32dcf11b8b603 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 15 Jun 2026 22:04:06 +0800 Subject: [PATCH 4007/5214] net/mlx5: Remove broken and unused mlx5_query_mtppse() mlx5_query_mtppse() reads the Event Trigger Pin (MTPPSE) register but reads the returned arm and mode values from the input buffer 'in' instead of the output buffer 'out', so it always returns the values that were written rather than the actual hardware state, making the query useless. The function has no in-tree callers. Remove it rather than fix it. Signed-off-by: Li RongQing Reviewed-by: Gal Pressman Link: https://patch.msgid.link/20260615140406.1828-1-lirongqing@baidu.com Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/mlx5_core.h | 1 - .../net/ethernet/mellanox/mlx5/core/port.c | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 51637e58a48b..09e669f83dba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -297,7 +297,6 @@ void mlx5_core_reps_aux_devs_remove(struct mlx5_core_dev *dev); void mlx5_fw_reporters_create(struct mlx5_core_dev *dev); int mlx5_query_mtpps(struct mlx5_core_dev *dev, u32 *mtpps, u32 mtpps_size); int mlx5_set_mtpps(struct mlx5_core_dev *mdev, u32 *mtpps, u32 mtpps_size); -int mlx5_query_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 *arm, u8 *mode); int mlx5_set_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 arm, u8 mode); struct mlx5_dm *mlx5_dm_create(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index ee8b9765c5ba..ddbe9ca8971d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -908,25 +908,6 @@ int mlx5_set_mtpps(struct mlx5_core_dev *mdev, u32 *mtpps, u32 mtpps_size) sizeof(out), MLX5_REG_MTPPS, 0, 1); } -int mlx5_query_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 *arm, u8 *mode) -{ - u32 out[MLX5_ST_SZ_DW(mtppse_reg)] = {0}; - u32 in[MLX5_ST_SZ_DW(mtppse_reg)] = {0}; - int err = 0; - - MLX5_SET(mtppse_reg, in, pin, pin); - - err = mlx5_core_access_reg(mdev, in, sizeof(in), out, - sizeof(out), MLX5_REG_MTPPSE, 0, 0); - if (err) - return err; - - *arm = MLX5_GET(mtppse_reg, in, event_arm); - *mode = MLX5_GET(mtppse_reg, in, event_generation_mode); - - return err; -} - int mlx5_set_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 arm, u8 mode) { u32 out[MLX5_ST_SZ_DW(mtppse_reg)] = {0}; From aedd02af1f8b0bceb7f42f5a21c41634ca9ed390 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 15 Jun 2026 17:30:46 -0700 Subject: [PATCH 4008/5214] net: psample: fix info leak in PSAMPLE_ATTR_DATA psample open codes nla_put() presumably to avoid wiping the data with 0s just to override it with packet data. This open coding is missing clearing the pad, however, each netlink attr is padded to 4B and data_len may not be divisible by 4B. Fixes: 6ae0a6286171 ("net: Introduce psample, a new genetlink channel for packet sampling") Reported-by: Weiming Shi Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260616003046.1099490-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/psample/psample.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/psample/psample.c b/net/psample/psample.c index 7763662036fb..c112e1f0ccac 100644 --- a/net/psample/psample.c +++ b/net/psample/psample.c @@ -476,15 +476,17 @@ void psample_sample_packet(struct psample_group *group, goto error; if (data_len) { - int nla_len = nla_total_size(data_len); + int nla_len = nla_attr_size(data_len); struct nlattr *nla; nla = skb_put(nl_skb, nla_len); nla->nla_type = PSAMPLE_ATTR_DATA; - nla->nla_len = nla_attr_size(data_len); + nla->nla_len = nla_len; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) goto error; + + skb_put_zero(nl_skb, nla_padlen(data_len)); } #ifdef CONFIG_INET From 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 15 Jun 2026 15:36:30 -0400 Subject: [PATCH 4009/5214] sctp: hold socket lock when dumping endpoints in sctp_diag SCTP_DIAG endpoint dumping was traversing endpoint address lists without holding lock_sock(), while those lists could change concurrently via socket operations (e.g., bindx changes). This creates a race where nla_reserve() counts addresses under RCU protection, but the subsequent copy may see fewer entries, potentially leaking uninitialized memory to userspace. Fix this by: - Taking a reference on each endpoint during hash traversal - Moving socket operations (lock_sock()) outside read_lock_bh() - Serializing address list access during dump - Reworking sctp_for_each_endpoint() to support restart-based traversal with (net, pos) tracking Also: - Add WARN_ON_ONCE() for inconsistent address counts - Fix idiag_states filtering for LISTEN vs association cases - Skip dumping endpoints being freed (ep->base.dead) - Move dump position tracking into iterator, removing cb->args[4] and its comment for sctp_ep_dump()., - Update the comment for cb->args[4] and remove the comment for unused cb->args[5] for sctp_sock_dump(). Note: traversal is restart-based and may re-scan buckets multiple times, but this is acceptable due to small bucket sizes and required to support sleeping-safe callbacks. This issue was reported by Nico Yip (@_cyeaa_) working with TrendAI Zero Day Initiative. Reported-by: Zero Day Initiative Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Signed-off-by: Xin Long Link: https://patch.msgid.link/4c1b49ab87e0f7d552ebd8172b364b1994e913c9.1781552190.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- include/net/sctp/sctp.h | 3 +- net/sctp/diag.c | 67 ++++++++++++++++++++--------------------- net/sctp/socket.c | 29 +++++++++++++----- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 60b073fd3ed8..d50c27812504 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -111,7 +111,8 @@ int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *paddr, void *p, int dif); int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, struct net *net, int *pos, void *p); -int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p); +int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), + struct net *net, int *pos, void *p); int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc, struct sctp_info *info); diff --git a/net/sctp/diag.c b/net/sctp/diag.c index d758f5c3e06e..c2a0de2adf6f 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -92,6 +92,7 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, if (!--addrcnt) break; } + WARN_ON_ONCE(addrcnt); rcu_read_unlock(); return 0; @@ -373,42 +374,39 @@ static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) struct sk_buff *skb = commp->skb; struct netlink_callback *cb = commp->cb; const struct inet_diag_req_v2 *r = commp->r; - struct net *net = sock_net(skb->sk); struct inet_sock *inet = inet_sk(sk); int err = 0; - if (!net_eq(sock_net(sk), net)) + lock_sock(sk); + if (ep->base.dead) goto out; - if (cb->args[4] < cb->args[1]) - goto next; - - if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs)) - goto next; + /* Skip eps with assocs if non-LISTEN states were requested, since + * they'll be dumped by sctp_sock_dump() during assoc traversal. + */ + if ((r->idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)) && + !list_empty(&ep->asocs)) + goto out; if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) - goto next; + goto out; if (r->id.idiag_sport != inet->inet_sport && r->id.idiag_sport) - goto next; + goto out; if (r->id.idiag_dport != inet->inet_dport && r->id.idiag_dport) - goto next; - - if (inet_sctp_diag_fill(sk, NULL, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, commp->net_admin) < 0) { - err = 2; goto out; - } -next: - cb->args[4]++; + + err = inet_sctp_diag_fill(sk, NULL, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).sk), + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + cb->nlh, commp->net_admin); out: + release_sock(sk); return err; } @@ -479,41 +477,40 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, .r = r, .net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN), }; - int pos = cb->args[2]; + int pos; /* eps hashtable dumps * args: * 0 : if it will traversal listen sock * 1 : to record the sock pos of this time's traversal - * 4 : to work as a temporary variable to traversal list */ if (cb->args[0] == 0) { - if (!(idiag_states & TCPF_LISTEN)) - goto skip; - if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) - goto done; -skip: + if (idiag_states & TCPF_LISTEN) { + pos = cb->args[1]; + if (sctp_for_each_endpoint(sctp_ep_dump, net, &pos, + &commp)) { + cb->args[1] = pos; + return; + } + } cb->args[0] = 1; cb->args[1] = 0; - cb->args[4] = 0; } + if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) + return; + /* asocs by transport hashtable dump * args: * 1 : to record the assoc pos of this time's traversal * 2 : to record the transport pos of this time's traversal * 3 : to mark if we have dumped the ep info of the current asoc - * 4 : to work as a temporary variable to traversal list - * 5 : to save the sk we get from travelsing the tsp list. + * 4 : to track position within ep->asocs list in sctp_sock_dump() */ - if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) - goto done; - + pos = cb->args[2]; sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump, net, &pos, &commp); cb->args[2] = pos; - -done: cb->args[1] = cb->args[4]; cb->args[4] = 0; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 66e12fb0c646..c8481461f7d8 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5369,24 +5369,39 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net, } int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), - void *p) { - int err = 0; - int hash = 0; - struct sctp_endpoint *ep; + struct net *net, int *pos, void *p) { + int err, hash = 0, idx = 0, start; struct sctp_hashbucket *head; + struct sctp_endpoint *ep; for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize; hash++, head++) { + start = idx; +again: read_lock_bh(&head->lock); sctp_for_each_hentry(ep, &head->chain) { - err = cb(ep, p); - if (err) + if (sock_net(ep->base.sk) != net) + continue; + if (idx++ >= *pos) { + sctp_endpoint_hold(ep); break; + } } read_unlock_bh(&head->lock); + + if (ep) { + err = cb(ep, p); + sctp_endpoint_put(ep); + if (err) + return err; + (*pos)++; + + idx = start; + goto again; + } } - return err; + return 0; } EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); From 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:34 +0800 Subject: [PATCH 4010/5214] gpio: sch: use raw_spinlock_t in the irq startup path sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. Fixes: 7a81638485c1 ("gpio: sch: Add edge event support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260617154035.1199948-2-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 966d16a6d515..5e361742a11a 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -39,7 +39,7 @@ struct sch_gpio { struct gpio_chip chip; void __iomem *regs; - spinlock_t lock; + raw_spinlock_t lock; unsigned short resume_base; /* GPE handling */ @@ -104,9 +104,9 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -122,9 +122,9 @@ static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -135,9 +135,9 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 0); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); /* * according to the datasheet, writing to the level register has no @@ -196,14 +196,14 @@ static int sch_irq_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTPE, rising); sch_gpio_reg_set(sch, gpio_num, GTNE, falling); irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -215,9 +215,9 @@ static void sch_irq_ack(struct irq_data *d) irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTS, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) @@ -225,9 +225,9 @@ static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GGPE, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask(struct irq_data *d) @@ -268,12 +268,12 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) int offset; u32 ret; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status; for_each_set_bit(offset, &pending, sch->chip.ngpio) @@ -343,7 +343,7 @@ static int sch_gpio_probe(struct platform_device *pdev) sch->regs = regs; - spin_lock_init(&sch->lock); + raw_spin_lock_init(&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label = dev_name(dev); sch->chip.parent = dev; From 90f0109019e6817eb40a486671b7722d1544ae29 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:35 +0800 Subject: [PATCH 4011/5214] gpio: eic-sprd: use raw_spinlock_t in the irq startup path sprd_eic_irq_unmask() enables the GPIO IRQ and then updates controller state through sprd_eic_update(), which takes sprd_eic->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sprd_eic_irq_unmask() -> sprd_eic_update() carrier and used the original spin_lock_irqsave(&sprd_eic->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sprd_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sprd_eic_update.constprop.0+0x48/0x90 [vuln_msv] sprd_eic_irq_unmask.constprop.0+0x35/0x50 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the Spreadtrum EIC controller lock to raw_spinlock_t. The locked section only serializes MMIO register updates and does not contain sleepable operations, so keeping it non-sleeping is appropriate for the irqchip callbacks. Fixes: 25518e024e3a ("gpio: Add Spreadtrum EIC driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260617154035.1199948-3-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-eic-sprd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 50fafeda8d7e..3b7ebcf12fe7 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -95,7 +95,7 @@ struct sprd_eic { struct notifier_block irq_nb; void __iomem *base[SPRD_EIC_MAX_BANK]; enum sprd_eic_type type; - spinlock_t lock; + raw_spinlock_t lock; int irq; }; @@ -149,7 +149,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, unsigned long flags; u32 tmp; - spin_lock_irqsave(&sprd_eic->lock, flags); + raw_spin_lock_irqsave(&sprd_eic->lock, flags); tmp = readl_relaxed(base + reg); if (val) @@ -158,7 +158,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, tmp &= ~BIT(SPRD_EIC_BIT(offset)); writel_relaxed(tmp, base + reg); - spin_unlock_irqrestore(&sprd_eic->lock, flags); + raw_spin_unlock_irqrestore(&sprd_eic->lock, flags); } static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg) @@ -628,7 +628,7 @@ static int sprd_eic_probe(struct platform_device *pdev) if (!sprd_eic) return -ENOMEM; - spin_lock_init(&sprd_eic->lock); + raw_spin_lock_init(&sprd_eic->lock); sprd_eic->type = pdata->type; sprd_eic->irq = platform_get_irq(pdev, 0); From 28c5d230980bdd8cb18c073225296c7747995935 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 17 Jun 2026 15:43:34 +0800 Subject: [PATCH 4012/5214] erofs: add folio order to trace_erofs_read_folio erofs supports large folios for reads, but the actual folio order instantiated in the page cache may be lower due to allocation constraints such as memory fragmentation. trace_erofs_read_folio already receives the folio being read but currently records only its index. Add folio_order() to the tracepoint so that users can observe the realized folio order and verify the effectiveness of large folio reads. Also drop the uptodate field: read_folio() is only called for a non-uptodate folio, so it is always 0. Suggested-by: Gao Xiang Signed-off-by: Zhan Xusheng Reviewed-by: Gao Xiang Signed-off-by: Gao Xiang --- include/trace/events/erofs.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/trace/events/erofs.h b/include/trace/events/erofs.h index cd0e3fd8c23f..0a178cb10fb1 100644 --- a/include/trace/events/erofs.h +++ b/include/trace/events/erofs.h @@ -90,7 +90,7 @@ TRACE_EVENT(erofs_read_folio, __field(erofs_nid_t, nid ) __field(int, dir ) __field(pgoff_t, index ) - __field(int, uptodate) + __field(unsigned int, order ) __field(bool, raw ) ), @@ -99,16 +99,15 @@ TRACE_EVENT(erofs_read_folio, __entry->nid = EROFS_I(inode)->nid; __entry->dir = S_ISDIR(inode->i_mode); __entry->index = folio->index; - __entry->uptodate = folio_test_uptodate(folio); + __entry->order = folio_order(folio); __entry->raw = raw; ), - TP_printk("dev = (%d,%d), nid = %llu, %s, index = %lu, uptodate = %d " - "raw = %d", + TP_printk("dev = (%d,%d), nid = %llu, %s, index = %lu, order = %u, raw = %d", show_dev_nid(__entry), show_file_type(__entry->dir), (unsigned long)__entry->index, - __entry->uptodate, + __entry->order, __entry->raw) ); From 30222639602c89ddc52208ac6c9d7baed376c84a Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:17 +0200 Subject: [PATCH 4013/5214] mm/slab: remove __GFP_NO_OBJ_EXT usage from alloc_slab_obj_exts() __GFP_NO_OBJ_EXT has limited scope within the slab allocator itself and gfp flags are a scarce resource, unlike slab's alloc_flags. Introduce SLAB_ALLOC_NO_RECURSE alloc flag that has the same intent as __GFP_NO_OBJ_EXT but a more generic name, meaning that a kmalloc() family function should not recurse into another kmalloc*() for the purposes of allocating auxiliary structures (obj_ext arrays or sheaves). First, replace the __GFP_NO_OBJ_EXT for allocating obj_ext arrays in alloc_slab_obj_exts(). Make use of the newly added kmalloc_flags() function, where we can pass alloc_flags with SLAB_ALLOC_NO_RECURSE added. This will also pass through SLAB_ALLOC_NOLOCK so we don't need to special case kmalloc_nolock() anymore. Note that until now the kmalloc_nolock() ignored the incoming gfp flags and hardcoded __GFP_ZERO | __GFP_NO_OBJ_EXT. But it's correct to pass on the incoming gfp flags (only augmented with __GFP_ZERO), because if alloc_flags contain SLAB_ALLOC_NOLOCK, the incoming gfp flags have to be also compatible with it. However, we might have added __GFP_THISNODE for opportunistic slab allocation, as pointed out by Hao Li, and __GFP_COMP by allocate_slab() as pointed out by Shengming Hu. Solve this by adding both flags to OBJCGS_CLEAR_MASK as it makes sense to strip them anyway for non-kmalloc_nolock() allocations of sheaves or obj_ext arrays as well. To avoid recursion of sheaf -> obj_ext -> sheaf -> ... allocations at this patch, until the next patch converts sheaves to SLAB_ALLOC_NO_RECURSE, use both gfp and alloc_flags for obj_ext. The next patch will remove the gfp part. Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-15-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) --- mm/slab.h | 1 + mm/slub.c | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/mm/slab.h b/mm/slab.h index 482b8e0fe797..281a65233795 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -21,6 +21,7 @@ #define SLAB_ALLOC_DEFAULT 0x00 /* no flags */ #define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ #define SLAB_ALLOC_NEW_SLAB 0x02 /* a flag for alloc_slab_obj_exts() */ +#define SLAB_ALLOC_NO_RECURSE 0x04 /* prevent kmalloc() recursion */ static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) { diff --git a/mm/slub.c b/mm/slub.c index 383d39a22561..dc4b4ae874ce 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2047,12 +2047,16 @@ static inline void dec_slabs_node(struct kmem_cache *s, int node, #endif /* CONFIG_SLUB_DEBUG */ /* - * The allocated objcg pointers array is not accounted directly. + * The allocated objcg pointers array or sheaf is not accounted directly. * Moreover, it should not come from DMA buffer and is not readily - * reclaimable. So those GFP bits should be masked off. + * reclaimable. Node restriction for the parent allocation also should + * not apply to the slab's internal objects, as well as __GFP_COMP used + * for new slab allocations. + * So those GFP bits should be masked off. */ #define OBJCGS_CLEAR_MASK (__GFP_DMA | __GFP_RECLAIMABLE | \ - __GFP_ACCOUNT | __GFP_NOFAIL) + __GFP_ACCOUNT | __GFP_NOFAIL | \ + __GFP_THISNODE | __GFP_COMP) #ifdef CONFIG_SLAB_OBJ_EXT @@ -2160,6 +2164,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, { const bool allow_spin = alloc_flags_allow_spinning(alloc_flags); unsigned int objects = objs_per_slab(s, slab); + bool new_slab = alloc_flags & SLAB_ALLOC_NEW_SLAB; unsigned long new_exts; unsigned long old_exts; struct slabobj_ext *vec; @@ -2168,14 +2173,13 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, gfp &= ~OBJCGS_CLEAR_MASK; /* Prevent recursive extension vector allocation */ gfp |= __GFP_NO_OBJ_EXT; + alloc_flags |= SLAB_ALLOC_NO_RECURSE; + alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; sz = obj_exts_alloc_size(s, slab, gfp); - if (unlikely(!allow_spin)) - vec = kmalloc_nolock(sz, __GFP_ZERO | __GFP_NO_OBJ_EXT, - slab_nid(slab)); - else - vec = kmalloc_node(sz, gfp | __GFP_ZERO, slab_nid(slab)); + /* This will use kmalloc_nolock() if alloc_flags say so */ + vec = kmalloc_flags(sz, gfp | __GFP_ZERO, alloc_flags, slab_nid(slab)); if (!vec) { /* @@ -2201,7 +2205,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, old_exts = READ_ONCE(slab->obj_exts); handle_failed_objexts_alloc(old_exts, vec, objects); - if (alloc_flags & SLAB_ALLOC_NEW_SLAB) { + if (new_slab) { /* * If the slab is brand new and nobody can yet access its * obj_exts, no synchronization is required and obj_exts can @@ -2251,7 +2255,7 @@ static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) } /* - * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its + * obj_exts was created with SLAB_ALLOC_NO_RECURSE flag, therefore its * corresponding extension will be NULL. alloc_tag_sub() will throw a * warning if slab has extensions but the extension of an object is * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that @@ -2374,7 +2378,7 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags, if (s->flags & (SLAB_NO_OBJ_EXT | SLAB_NOLEAKTRACE)) return; - if (flags & __GFP_NO_OBJ_EXT) + if (alloc_flags & SLAB_ALLOC_NO_RECURSE || flags & __GFP_NO_OBJ_EXT) return; slab = virt_to_slab(object); From 71553a60675994a79575567f6d56e214f9030dc4 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Thu, 11 Jun 2026 18:24:06 +0200 Subject: [PATCH 4014/5214] mm/slab: replace __GFP_NO_OBJ_EXT with SLAB_ALLOC_NO_RECURSE for sheaves Finish the switch away from __GFP_NO_OBJ_EXT by replacing it with SLAB_ALLOC_NO_RECURSE when allocating empty sheaves. Pass alloc_flags to [__]alloc_empty_sheaf(). Callers that can't be part of a recursive kmalloc() chain simply pass SLAB_ALLOC_DEFAULT. Use kmalloc_flags() instead of kzalloc() for allocating the sheaf. With that we can finalize the removal the __GFP_NO_OBJ_EXT handling from obj_ext allocations as well, leaving only SLAB_ALLOC_NO_RECURSE in place. This leaves __GFP_NO_OBJ_EXT with no users in slab, so stop allowing the flag in kmalloc_nolock(). Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-16-7190909db118@kernel.org Reviewed-by: Hao Li Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 6 +++--- mm/slub.c | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index b955f3cbb732..43c3d9b51107 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1039,9 +1039,9 @@ void *_kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_flags, in /** * kmalloc_nolock - Allocate an object of given size from any context. * @size: size to allocate - * @gfp_flags: GFP flags. Only __GFP_ACCOUNT, __GFP_ZERO, __GFP_NO_OBJ_EXT - * allowed. Also __GFP_NOWARN and __GFP_NOMEMALLOC are allowed but added - * internally thus not necessary. + * @gfp_flags: GFP flags. Only __GFP_ACCOUNT and __GFP_ZERO allowed. Also + * __GFP_NOWARN and __GFP_NOMEMALLOC are allowed but added internally thus not + * necessary. * @node: node number of the target node. * * Return: pointer to the new object or NULL in case of error. diff --git a/mm/slub.c b/mm/slub.c index dc4b4ae874ce..917635203f73 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2172,7 +2172,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, gfp &= ~OBJCGS_CLEAR_MASK; /* Prevent recursive extension vector allocation */ - gfp |= __GFP_NO_OBJ_EXT; alloc_flags |= SLAB_ALLOC_NO_RECURSE; alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; @@ -2378,7 +2377,7 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags, if (s->flags & (SLAB_NO_OBJ_EXT | SLAB_NOLEAKTRACE)) return; - if (alloc_flags & SLAB_ALLOC_NO_RECURSE || flags & __GFP_NO_OBJ_EXT) + if (alloc_flags & SLAB_ALLOC_NO_RECURSE) return; slab = virt_to_slab(object); @@ -2763,7 +2762,7 @@ static inline void *setup_object(struct kmem_cache *s, void *object) } static struct slab_sheaf *__alloc_empty_sheaf(struct kmem_cache *s, gfp_t gfp, - unsigned int capacity) + unsigned int alloc_flags, unsigned int capacity) { struct slab_sheaf *sheaf; size_t sheaf_size; @@ -2774,10 +2773,10 @@ static struct slab_sheaf *__alloc_empty_sheaf(struct kmem_cache *s, gfp_t gfp, * bucket) */ if (s->flags & SLAB_KMALLOC) - gfp |= __GFP_NO_OBJ_EXT; + alloc_flags |= SLAB_ALLOC_NO_RECURSE; sheaf_size = struct_size(sheaf, objects, capacity); - sheaf = kzalloc(sheaf_size, gfp); + sheaf = kmalloc_flags(sheaf_size, gfp | __GFP_ZERO, alloc_flags, NUMA_NO_NODE); if (unlikely(!sheaf)) return NULL; @@ -2790,20 +2789,20 @@ static struct slab_sheaf *__alloc_empty_sheaf(struct kmem_cache *s, gfp_t gfp, } static inline struct slab_sheaf *alloc_empty_sheaf(struct kmem_cache *s, - gfp_t gfp) + gfp_t gfp, unsigned int alloc_flags) { - if (gfp & __GFP_NO_OBJ_EXT) + if (alloc_flags & SLAB_ALLOC_NO_RECURSE) return NULL; gfp &= ~OBJCGS_CLEAR_MASK; - return __alloc_empty_sheaf(s, gfp, s->sheaf_capacity); + return __alloc_empty_sheaf(s, gfp, alloc_flags, s->sheaf_capacity); } static void free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf) { /* - * If the sheaf was created with __GFP_NO_OBJ_EXT flag then its + * If the sheaf was created with SLAB_ALLOC_NO_RECURSE flag then its * corresponding extension is NULL and alloc_tag_sub() will throw a * warning, therefore replace NULL with CODETAG_EMPTY to indicate * that the extension for this sheaf is expected to be NULL. @@ -4695,7 +4694,7 @@ __pcs_replace_empty_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, return NULL; if (!empty) { - empty = alloc_empty_sheaf(s, gfp); + empty = alloc_empty_sheaf(s, gfp, alloc_flags); if (!empty) return NULL; } @@ -5068,7 +5067,7 @@ kmem_cache_prefill_sheaf(struct kmem_cache *s, gfp_t gfp, unsigned int size) if (unlikely(size > s->sheaf_capacity)) { - sheaf = __alloc_empty_sheaf(s, gfp, size); + sheaf = __alloc_empty_sheaf(s, gfp, SLAB_ALLOC_DEFAULT, size); if (!sheaf) return NULL; @@ -5113,7 +5112,7 @@ kmem_cache_prefill_sheaf(struct kmem_cache *s, gfp_t gfp, unsigned int size) if (!sheaf) - sheaf = alloc_empty_sheaf(s, gfp); + sheaf = alloc_empty_sheaf(s, gfp, SLAB_ALLOC_DEFAULT); if (sheaf) { sheaf->capacity = s->sheaf_capacity; @@ -5398,7 +5397,7 @@ static void *__kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_f VM_WARN_ON_ONCE(alloc_flags_allow_spinning(ac->alloc_flags)); VM_WARN_ON_ONCE(gfp_flags & ~(__GFP_ACCOUNT | __GFP_ZERO | - __GFP_NO_OBJ_EXT | __GFP_NOWARN | __GFP_NOMEMALLOC)); + __GFP_NOWARN | __GFP_NOMEMALLOC)); gfp_flags |= __GFP_NOWARN | __GFP_NOMEMALLOC; @@ -5913,7 +5912,7 @@ __pcs_replace_full_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, if (!allow_spin) return NULL; - empty = alloc_empty_sheaf(s, GFP_NOWAIT); + empty = alloc_empty_sheaf(s, GFP_NOWAIT, SLAB_ALLOC_DEFAULT); if (empty) goto got_empty; @@ -6097,7 +6096,7 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) local_unlock(&s->cpu_sheaves->lock); - empty = alloc_empty_sheaf(s, GFP_NOWAIT); + empty = alloc_empty_sheaf(s, GFP_NOWAIT, SLAB_ALLOC_DEFAULT); if (!empty) goto fail; @@ -7642,7 +7641,7 @@ static int init_percpu_sheaves(struct kmem_cache *s) if (!s->sheaf_capacity) pcs->main = &bootstrap_sheaf; else - pcs->main = alloc_empty_sheaf(s, GFP_KERNEL); + pcs->main = alloc_empty_sheaf(s, GFP_KERNEL, SLAB_ALLOC_DEFAULT); if (!pcs->main) return -ENOMEM; @@ -8508,7 +8507,8 @@ static void __init bootstrap_cache_sheaves(struct kmem_cache *s) pcs = per_cpu_ptr(s->cpu_sheaves, cpu); - pcs->main = __alloc_empty_sheaf(s, GFP_KERNEL, capacity); + pcs->main = __alloc_empty_sheaf(s, GFP_KERNEL, + SLAB_ALLOC_DEFAULT, capacity); if (!pcs->main) { failed = true; From 6808645b71f02752852731764961a16f728772a6 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 11 Jun 2026 13:46:41 +0100 Subject: [PATCH 4015/5214] mm/slab: add a node-track-caller variant for kmem buckets allocation This is required by users that want to use kmem buckets, but still desire specifying the NUMA node. Reviewed-by: Kees Cook Acked-by: Vlastimil Babka (SUSE) Acked-by: Harry Yoo (Oracle) Signed-off-by: Pedro Falcato Link: https://patch.msgid.link/20260611124642.345400-2-pfalcato@suse.de Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 43c3d9b51107..333c85492314 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1156,8 +1156,11 @@ void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node); #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_KMALLOC_PARAMS(_size, _b, __kmalloc_token(_size)), _flags, NUMA_NO_NODE)) -#define kmem_buckets_alloc_track_caller(_b, _size, _flags) \ - alloc_hooks(__kmalloc_node_track_caller_noprof(PASS_KMALLOC_PARAMS(_size, _b, __kmalloc_token(_size)), _flags, NUMA_NO_NODE, _RET_IP_)) +#define kmem_buckets_alloc_node_track_caller(_b, _size, _flags, _node) \ + alloc_hooks(__kmalloc_node_track_caller_noprof(PASS_KMALLOC_PARAMS(_size, _b, __kmalloc_token(_size)), _flags, _node, _RET_IP_)) + +#define kmem_buckets_alloc_track_caller(_b, _size, _flags) \ + kmem_buckets_alloc_node_track_caller(_b, _size, _flags, NUMA_NO_NODE) static __always_inline __alloc_size(1) void *_kmalloc_node_noprof(size_t size, gfp_t flags, int node, kmalloc_token_t token) { From 7b5f5865fb11e60edd03c5e063e2d228b7062317 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 17 Jun 2026 09:31:25 -0700 Subject: [PATCH 4016/5214] slab: recognize @GFP parameter as optional in kernel-doc Since the @GFP parameter in kmalloc_obj() etc. is now optional, change the kernel-doc to indicate that it is optional. This avoids kernel-doc warnings: WARNING: include/linux/slab.h:1101 Excess function parameter 'GFP' description in 'kmalloc_obj' WARNING: include/linux/slab.h:1113 Excess function parameter 'GFP' description in 'kmalloc_objs' WARNING: include/linux/slab.h:1128 Excess function parameter 'GFP' description in 'kmalloc_flex' Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new *alloc_obj() helpers") Signed-off-by: Randy Dunlap Acked-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260617163125.2716279-1-rdunlap@infradead.org Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 333c85492314..516fdcf1b6c5 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1094,7 +1094,7 @@ void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node); /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. @@ -1106,7 +1106,7 @@ void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node); * kmalloc_objs - Allocate an array of the given type * @VAR_OR_TYPE: Variable or type to allocate an array of. * @COUNT: How many elements in the array. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. @@ -1119,7 +1119,7 @@ void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node); * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). * @FAM: The name of the flexible array member of the structure. * @COUNT: How many flexible array member elements are desired. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on * failure. If @FAM has been annotated with __counted_by(), the allocation From 892a7864730775c3dbee2a39e9ead4fa8d4256e7 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Fri, 12 Jun 2026 15:13:59 +0800 Subject: [PATCH 4017/5214] tools/mm/slabinfo: fix total_objects attribute name SLUB exports the total_objects sysfs attribute, but slabinfo tries to read objects_total. As a result, the lookup fails and the field remains zero. Use the correct attribute name and rename the corresponding structure member to match. Fixes: 205ab99dd103 ("slub: Update statistics handling for variable order slabs") Signed-off-by: Yichong Chen Cc: Reviewed-by: SeongJae Park Link: https://patch.msgid.link/96556748872BB47E+20260612071359.649946-1-chenyichong@uniontech.com Signed-off-by: Vlastimil Babka (SUSE) --- tools/mm/slabinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index 87570c22b151..48d1ee8b0e81 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -33,7 +33,7 @@ struct slabinfo { unsigned int hwcache_align, object_size, objs_per_slab; unsigned int sanity_checks, slab_size, store_user, trace; int order, poison, reclaim_account, red_zone; - unsigned long partial, objects, slabs, objects_partial, objects_total; + unsigned long partial, objects, slabs, objects_partial, total_objects; unsigned long alloc_fastpath, alloc_slowpath; unsigned long free_fastpath, free_slowpath; unsigned long free_frozen, free_add_partial, free_remove_partial; @@ -1262,7 +1262,7 @@ static void read_slab_dir(void) slab->object_size = get_obj("object_size"); slab->objects = get_obj("objects"); slab->objects_partial = get_obj("objects_partial"); - slab->objects_total = get_obj("objects_total"); + slab->total_objects = get_obj("total_objects"); slab->objs_per_slab = get_obj("objs_per_slab"); slab->order = get_obj("order"); slab->partial = get_obj_and_str("partial", &t); From ac930b80c1e0eba283d7843180964e6d2a87369d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 17 Jun 2026 11:37:36 +0200 Subject: [PATCH 4018/5214] i2c: pxa: Use named initializers for the platform_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/827657abb02edb39bc90f7336194f614d383770e.1781688767.git.u.kleine-koenig@baylibre.com --- drivers/i2c/busses/i2c-pxa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 9a8b154ab69e..c9927a389aaf 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -214,11 +214,11 @@ static const struct of_device_id i2c_pxa_dt_ids[] = { MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); static const struct platform_device_id i2c_pxa_id_table[] = { - { "pxa2xx-i2c", REGS_PXA2XX }, - { "pxa3xx-pwri2c", REGS_PXA3XX }, - { "ce4100-i2c", REGS_CE4100 }, - { "pxa910-i2c", REGS_PXA910 }, - { "armada-3700-i2c", REGS_A3700 }, + { .name = "pxa2xx-i2c", .driver_data = REGS_PXA2XX }, + { .name = "pxa3xx-pwri2c", .driver_data = REGS_PXA3XX }, + { .name = "ce4100-i2c", .driver_data = REGS_CE4100 }, + { .name = "pxa910-i2c", .driver_data = REGS_PXA910 }, + { .name = "armada-3700-i2c", .driver_data = REGS_A3700 }, { } }; MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); From 53d1ae7c20d97b08741d667ba54bb09d330eba3b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:29:43 +0100 Subject: [PATCH 4019/5214] ALSA: pcm: fix __le32 cast warning in snd_pcm_set_sync_per_card In snd_pcm_set_sync_per_card() the le32 value is written to an u32 instead of an __le32 pointer. Fix the following warning by fixing the type: sound/soc/soc-pcm.c:2166:9: warning: incorrect type in argument 7 (different base types) sound/soc/soc-pcm.c:2166:9: expected int sound/soc/soc-pcm.c:2166:9: got restricted snd_pcm_format_t Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260617102943.893950-1-ben.dooks@codethink.co.uk Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fe597f7d522d..4d665b4148d7 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -545,7 +545,7 @@ void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, const unsigned char *id, unsigned int len) { - *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number); + *(__le32 *)params->sync = cpu_to_le32(substream->pcm->card->number); len = min(12, len); memcpy(params->sync + 4, id, len); memset(params->sync + 4 + len, 0, 12 - len); From f7f3f9fd81e7adbaa12c2e62ee07f0e094a543fd Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 14:03:15 +0800 Subject: [PATCH 4020/5214] ALSA: caiaq: fix out-of-bounds read in the Traktor Kontrol S4 input parser snd_usb_caiaq_tks4_dispatch() decodes the Traktor Kontrol S4 input stream in fixed 16-byte (TKS4_MSGBLOCK_SIZE) message blocks. On every iteration it advances buf and subtracts the block size while looping on "while (len)". len is urb->actual_length. That value is supplied by the device and is not guaranteed to be a multiple of 16. When a final short block leaves len between 1 and 15, the loop runs once more, reads up to buf[15], and then does "len -= TKS4_MSGBLOCK_SIZE". As len is unsigned this underflows to a huge value. The loop then keeps iterating and walking buf far past the end of the 512-byte ep4_in_buf, reading out of bounds until a bogus block id happens to be hit. Iterate only while a full message block is available. This stops the unsigned underflow and silently drops any trailing partial block, which carries no complete control value anyway. The sibling endpoint-4 parsers are not affected. The Traktor Kontrol X1 and Maschine arms in snd_usb_caiaq_ep4_reply_dispatch() floor urb->actual_length before dispatching. Fixes: 15c5ab607045 ("ALSA: snd-usb-caiaq: Add support for Traktor Kontrol S4") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178176259547.3343534.2724779296835237429@maoyixie.com Signed-off-by: Takashi Iwai --- sound/usb/caiaq/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 5c70fdf61cc1..2db4d1332df1 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -330,7 +330,7 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev, { struct device *dev = caiaqdev_to_dev(cdev); - while (len) { + while (len >= TKS4_MSGBLOCK_SIZE) { unsigned int i, block_id = (buf[0] << 8) | buf[1]; switch (block_id) { From 58fc1275b3f288500ee79a02dbe89ed4197fdc3e Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 14:03:15 +0800 Subject: [PATCH 4021/5214] ALSA: caiaq: bound the length in the EP1 input parsers snd_caiaq_input_read_erp() and snd_caiaq_input_read_io() can be reached from snd_usb_caiaq_input_dispatch(). They read fixed byte offsets from the reply buffer without checking the reported length. On a short reply they decode stale bytes left from a previous, longer report and feed them to the input layer. This is not an out-of-bounds access. Every offset is a compile-time driver constant. The largest is buf[21] in the Maschine ERP case. The EP1 transfer buffer ep1_in_buf is EP1_BUFSIZE (64) bytes, and the USB core caps actual_length at 64, so a short reply only reads in-bounds stale data. Acting on data the device did not send is still wrong, so bail out per usb_id case when the reply is shorter than the bytes that case consumes. read_erp: AK1 needs 2 bytes, Kore needs 16, Maschine needs 22. read_io: the Kore case needs 5 bytes (buf[4]) and the Traktor Kontrol X1 case needs 7 (buf[5]/buf[6]). The preceding key bit loop is already bounded by "i < len * 8" and is left untouched. snd_caiaq_input_read_analog() and snd_usb_caiaq_maschine_dispatch() are not changed. Their callers already floor the reply length. Suggested-by: Takashi Iwai Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178176259547.3343534.6659489917322808916@maoyixie.com Signed-off-by: Takashi Iwai --- sound/usb/caiaq/input.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 2db4d1332df1..eabbf41fdfb2 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -237,12 +237,16 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev, switch (cdev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + if (len < 2) + return; i = decode_erp(buf[0], buf[1]); input_report_abs(input_dev, ABS_X, i); input_sync(input_dev); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + if (len < 16) + return; i = decode_erp(buf[7], buf[5]); input_report_abs(input_dev, ABS_HAT0X, i); i = decode_erp(buf[12], buf[14]); @@ -263,6 +267,8 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev, break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + if (len < 22) + return; /* 4 under the left screen */ input_report_abs(input_dev, ABS_HAT0X, decode_erp(buf[21], buf[20])); input_report_abs(input_dev, ABS_HAT0Y, decode_erp(buf[15], buf[14])); @@ -308,9 +314,13 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *cdev, switch (cdev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + if (len < 5) + return; input_report_abs(cdev->input_dev, ABS_MISC, 255 - buf[4]); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (len < 7) + return; /* rotary encoders */ input_report_abs(cdev->input_dev, ABS_X, buf[5] & 0xf); input_report_abs(cdev->input_dev, ABS_Y, buf[5] >> 4); From 4520fbbaedbeda8861f0f0bfbe50b3bca4deae08 Mon Sep 17 00:00:00 2001 From: Haowen Tu Date: Thu, 18 Jun 2026 15:54:31 +0800 Subject: [PATCH 4022/5214] ALSA: hda/realtek: Add headset mic quirk for Acer S40-54 Acer S40-54 with ALC256 does not restore headset mic detection properly after S4 resume. After resume, headset plug events may no longer update the headset mic state, leaving the headset microphone unavailable. The system uses subsystem ID 1025:161f. Applying the existing ALC256_FIXUP_ACER_MIC_NO_PRESENCE fixup restores headset mic detection on this machine. Add a machine-specific quirk for this system. Signed-off-by: Haowen Tu Link: https://patch.msgid.link/20260618075431.1116988-1-tuhaowen@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5d09baa6d32e..d209efdb8272 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6807,6 +6807,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), From bdb640be82e645e2828731648f485224d0c2587b Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:25 -0400 Subject: [PATCH 4023/5214] ALSA: usb-audio: qcom: reject stream disable with no active interface handle_uaudio_stream_req() resolves an interface index with info_idx_from_ifnum(), which returns -EINVAL when no interface matches. The enable branch and the response: cleanup label both guard against a negative index, but the disable branch does not: it forms info = &uadev[pcm_card_num].info[info_idx] and dereferences it. uadev[].info is a pointer allocated only when a stream is first enabled, so a negative info_idx on the disable path is unsafe in two ways: - If the card was never enabled, .info is NULL and &info[-EINVAL] is a wild pointer; reading info->data_ep_pipe faults (kernel oops). - If the card was enabled at least once (.info allocated) and the disable names an interface that does not match, &info[-EINVAL] points before the allocation; info->data_ep_pipe / info->sync_ep_pipe are an out-of-bounds slab read and, when non-zero, an out-of-bounds 4-byte write (both pipe fields are cleared to 0). That is memory corruption, not just a NULL dereference. The request is reachable from unprivileged local userspace over AF_QIPCRTR. Reject a disable request with no resolved interface, matching the guard the enable path already has. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-2-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/qcom/qc_audio_offload.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 436d6821c5c9..3b05dadbbeae 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1642,6 +1642,11 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->opened = 0; } } else { + if (info_idx < 0) { + ret = -EINVAL; + goto response; + } + info = &uadev[pcm_card_num].info[info_idx]; if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, From 3c7af07943b2718087ae791cad450af5cf646d90 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:26 -0400 Subject: [PATCH 4024/5214] ALSA: usb-audio: qcom: clear opened when stream enable fails On enable, subs->opened is set before the service_interval is validated; an invalid interval jumps to the response label without clearing it, so the substream is wedged at -EBUSY until a disable or disconnect. Clear subs->opened on the enable error path. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-3-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/qcom/qc_audio_offload.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 3b05dadbbeae..3a586fd16e72 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1620,8 +1620,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); - if (ret == -EINVAL) + if (ret == -EINVAL) { + if (req_msg->enable) { + guard(mutex)(&chip->mutex); + subs->opened = 0; + } goto response; + } datainterval = ret; } From 69388468721151e15d34657a2e4a654741f32f1b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 17 Jun 2026 16:53:13 +0200 Subject: [PATCH 4025/5214] s390/idle: Add missing EXPORT_SYMBOL_GPL() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uwe Kleine-König reported this build breakage caused by a recent commit which provides arch specific kcpustat_field_idle()/kcpustat_field_iowait() functions: ERROR: modpost: "arch_kcpustat_field_idle" [drivers/leds/trigger/ledtrig-activity.ko] undefined! ERROR: modpost: "arch_kcpustat_field_iowait" [drivers/leds/trigger/ledtrig-activity.ko] undefined! Fix this by adding the missing EXPORT_SYMBOL_GPL(). Fixes: 670e057744e0 ("s390/idle: Provide arch specific kcpustat_field_idle()/kcpustat_field_iowait()") Reported-by: Uwe Kleine-König Closes: https://lore.kernel.org/r/ajKsG0JP6qTssQBX@monoceros Acked-by: Alexander Gordeev Tested-by: Uwe Kleine-König Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/idle.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 7f7851c001e0..08f3520c6785 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -83,11 +84,13 @@ u64 arch_kcpustat_field_idle(int cpu) { return arch_cpu_idle_time(cpu, CPUTIME_IDLE, !nr_iowait_cpu(cpu)); } +EXPORT_SYMBOL_GPL(arch_kcpustat_field_idle); u64 arch_kcpustat_field_iowait(int cpu) { return arch_cpu_idle_time(cpu, CPUTIME_IOWAIT, nr_iowait_cpu(cpu)); } +EXPORT_SYMBOL_GPL(arch_kcpustat_field_iowait); void account_idle_time_irq(void) { From e6fa716c9d7248e03cb93f566874bd5709901bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvin=20=C5=A0ipraga?= Date: Wed, 17 Jun 2026 16:55:08 +0200 Subject: [PATCH 4026/5214] ASoC: audio-graph-card2: Drop warning for manually selected DAI formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes the DAI format must be specified in the audio-graph-card2 device tree, so emitting a warning can be misleading. Revert back to emitting no warning. A few examples where automatic format selection might not be applicable: - For DPCM, where the other side of the DAI link is not apparent, no proper selection can actually be made. This can lead to disagreeing formats. - Due to hardware peculiarities, some ostensibly supported formats might not work in practice. In either case, the only correct solution is for the sound card to set the format Link: https://lore.kernel.org/all/87ik7s36k2.wl-kuninori.morimoto.gx@renesas.com/ Signed-off-by: Alvin Šipraga Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260617145508.327213-1-alvin@pqrs.dk Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 6894bb936cfd..0202ed0ee78e 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -778,18 +778,6 @@ static void graph_link_init(struct simple_util_priv *priv, graph_parse_daifmt(ports_cpu, &daifmt); graph_parse_daifmt(ports_codec, &daifmt); graph_parse_daifmt(lnk, &daifmt); - if (daifmt) { - struct device *dev = simple_priv_to_dev(priv); - - /* - * Recommend to use Auto Select by using .auto_selectable_formats. - * linux/sound/soc/renesas/rcar/core.c can be good sample for it. - * - * One note is that Audio Graph Card2 still keeps compatible to set - * DAI format via DT. - */ - dev_warn_once(dev, "use .auto_selectable_formats on each corresponding CPU/Codec driver"); - } graph_util_parse_link_direction(lnk, &playback_only, &capture_only); graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only); From 7cb033507068936f9da3c59e5c31a54e4e8dafa6 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 25 May 2026 21:40:16 -0700 Subject: [PATCH 4027/5214] PCI: mvebu: Use fixed-width interrupt masks to avoid truncation in 64-bit builds Use u32-typed BIT and GENMASK helpers for PCIe interrupt register masks. This keeps inverted masks in the same width as the registers and avoids truncation warnings on 64-bit compile-test builds. Fixes below and similar warnings: drivers/pci/controller/pci-mvebu.c:316:21: error: implicit conversion from 'unsigned long' to 'u32' (aka 'unsigned int') changes value from 18446744069414584320 to 0 [-Werror,-Wconstant-conversion] mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260526044016.1025613-1-rosenp@gmail.com --- drivers/pci/controller/pci-mvebu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a72aa57591c0..07ed45bab5d0 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -57,9 +57,9 @@ #define PCIE_CONF_DATA_OFF 0x18fc #define PCIE_INT_CAUSE_OFF 0x1900 #define PCIE_INT_UNMASK_OFF 0x1910 -#define PCIE_INT_INTX(i) BIT(24+i) -#define PCIE_INT_PM_PME BIT(28) -#define PCIE_INT_ALL_MASK GENMASK(31, 0) +#define PCIE_INT_INTX(i) BIT_U32(24 + (i)) +#define PCIE_INT_PM_PME BIT_U32(28) +#define PCIE_INT_ALL_MASK GENMASK_U32(31, 0) #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_CTRL_RC_MODE BIT(1) From 3c2e6cc6affa8acdb99a580be1f8f297edf54204 Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Thu, 30 Apr 2026 14:16:28 +1200 Subject: [PATCH 4028/5214] PCI: iproc: Restore .map_irq() for the platform bus driver Commit b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") moved the assignment of default .map_irq() callback to devm_of_pci_bridge_init() and removed the initialization of 'iproc_pcie::map_irq' in platform bus driver. This led to the callback getting assigned the NULL pointer for platform bus driver, thereby breaking the INTx functionality, since 'iproc_pcie::map_irq' overrides the 'pci_host_bridge::map_irq' callback in iproc_pcie_setup(). This issue only affected the iproc platform bus driver as this driver relies on the default callback for non-PAXC controllers. iproc-brcm driver was already providing the custom mapping function, so it was unaffected. Restore the original (and intended) behaviour to use the default map_irq function by removing the local 'iproc_pcie::map_irq' pointer and directly assigning the 'pci_host_bridge::map_irq' callback in iproc-bcma driver. This ensures that the default 'map_irq' callback is used for platform bus driver and only iproc-brcm driver overrides it with a custom one. Fixes: b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") Signed-off-by: Mark Tomlinson [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Ray Jui Link: https://patch.msgid.link/20260430021628.1343154-1-mark.tomlinson@alliedtelesis.co.nz --- drivers/pci/controller/pcie-iproc-bcma.c | 2 +- drivers/pci/controller/pcie-iproc-platform.c | 2 +- drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-iproc.h | 2 -- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c index 99a99900444d..593418c2bc3a 100644 --- a/drivers/pci/controller/pcie-iproc-bcma.c +++ b/drivers/pci/controller/pcie-iproc-bcma.c @@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev) if (ret) return ret; - pcie->map_irq = iproc_bcma_pcie_map_irq; + bridge->map_irq = iproc_bcma_pcie_map_irq; bcma_set_drvdata(bdev, pcie); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 0cb78c583c7e..4c9a0c4bb923 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) switch (pcie->type) { case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC_V2: - pcie->map_irq = NULL; + bridge->map_irq = NULL; break; default: break; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index ccf71993ea35..c22d0aecaaac 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1502,7 +1502,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->ops = &iproc_pcie_ops; host->sysdata = pcie; - host->map_irq = pcie->map_irq; ret = pci_host_probe(host); if (ret < 0) { diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index 969ded03b8c2..c4443f236ca3 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -61,7 +61,6 @@ struct iproc_msi; * @base_addr: PCIe host controller register base physical address * @mem: host bridge memory window resource * @phy: optional PHY device that controls the Serdes - * @map_irq: function callback to map interrupts * @ep_is_internal: indicates an internal emulated endpoint device is connected * @iproc_cfg_read: indicates the iProc config read function should be used * @rej_unconfig_pf: indicates the root complex needs to detect and reject @@ -91,7 +90,6 @@ struct iproc_pcie { phys_addr_t base_addr; struct resource mem; struct phy *phy; - int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; bool iproc_cfg_read; bool rej_unconfig_pf; From 348f69320e4db6ebec6940c81154bec4b9eb275a Mon Sep 17 00:00:00 2001 From: Jean-Louis Colaco Date: Thu, 18 Jun 2026 13:32:02 +0200 Subject: [PATCH 4029/5214] ALSA: usb-audio: Add quirk for YAMAHA CDS3000 This quirk is identical to the one for the Yamaha Steinberg UR22, here applied to a CD player that also uses the Steinberg USB interface. This quirk is necessary to avoid sporadic "clic" noise when using the DAC of the player. Signed-off-by: Jean-Louis Colaco Link: https://patch.msgid.link/20260618113202.8363-1-jean-louis.colaco@orange.fr Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 97f28c53d201..71444c2898b4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -390,6 +390,20 @@ YAMAHA_DEVICE(0x105d, NULL), } } }, +{ + USB_DEVICE(0x0499, 0x150d), + QUIRK_DRIVER_INFO { + /* .vendor_name = "Yamaha", */ + /* .product_name = "CDS3000", */ + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_STANDARD_AUDIO(1) }, + { QUIRK_DATA_STANDARD_AUDIO(2) }, + { QUIRK_DATA_MIDI_YAMAHA(3) }, + { QUIRK_DATA_IGNORE(4) }, + QUIRK_COMPOSITE_END + } + } +}, { USB_DEVICE(0x0499, 0x1718), QUIRK_DRIVER_INFO { From b59aff62767bf59ca0c787015c0ddc14f60ab10d Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 18 Jun 2026 12:24:36 +0200 Subject: [PATCH 4030/5214] ALSA: emu10k1: Use common error handling code in snd_emu10k1_playback_open() Use an additional label so that a bit of exception handling can be better reused at the end of this function implementation. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://patch.msgid.link/d709474d-62b0-4f7e-9011-a0f716b35383@web.de Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emupcm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 9023f3444d20..b8749e0131ad 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1181,19 +1181,17 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_playback; err = snd_emu10k1_playback_set_constraints(runtime); - if (err < 0) { - kfree(epcm); - return err; - } + if (err < 0) + goto free_epcm; + if (emu->card_capabilities->emu_model) sample_rate = emu->emu1010.word_clock; else sample_rate = 48000; err = snd_pcm_hw_rule_noresample(runtime, sample_rate); - if (err < 0) { - kfree(epcm); - return err; - } + if (err < 0) + goto free_epcm; + mix = &emu->pcm_mixer[substream->number]; for (i = 0; i < 8; i++) mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; @@ -1204,6 +1202,10 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) mix->epcm = epcm; snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); return 0; + +free_epcm: + kfree(epcm); + return err; } static int snd_emu10k1_playback_close(struct snd_pcm_substream *substream) From a8759c8ac48c0419f5899e95a6ffc611b07c965b Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:16 +0800 Subject: [PATCH 4031/5214] PCI: altera: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-4-18255117159@163.com --- drivers/pci/controller/pcie-altera.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3dbb7adc421c..7e1db267ae34 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -1045,8 +1045,10 @@ static void altera_pcie_remove(struct platform_device *pdev) struct altera_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); altera_pcie_irq_teardown(pcie); } From 20b7aba83c0eac13bf9d46b0fa7575df5765f205 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:17 +0800 Subject: [PATCH 4032/5214] PCI: brcmstb: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-5-18255117159@163.com --- drivers/pci/controller/pcie-brcmstb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 714bcab97b60..7ca27f0756c9 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1894,8 +1894,10 @@ static void brcm_pcie_remove(struct platform_device *pdev) struct brcm_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); if (pcie->cfg->has_err_report) brcm_unregister_die_notifiers(pcie); From 713331969ce89489c84af917058df6d9910cff97 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:14 +0800 Subject: [PATCH 4033/5214] PCI: cadence: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-2-18255117159@163.com --- drivers/pci/controller/cadence/pcie-cadence-host.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 0bc9e6e90e0e..87fd8afa301b 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -365,8 +365,10 @@ void cdns_pcie_host_disable(struct cdns_pcie_rc *rc) struct pci_host_bridge *bridge; bridge = pci_host_bridge_from_priv(rc); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); cdns_pcie_host_deinit(rc); cdns_pcie_host_link_disable(rc); From 26335696498ab502e907a556e97c7039bc80a87e Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:15 +0800 Subject: [PATCH 4034/5214] PCI: dwc: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-3-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c9517a348836..f856ac2fb15d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -703,8 +703,10 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) dwc_pcie_debugfs_deinit(pci); + pci_lock_rescan_remove(); pci_stop_root_bus(pp->bridge->bus); pci_remove_root_bus(pp->bridge->bus); + pci_unlock_rescan_remove(); dw_pcie_stop_link(pci); From a6a64e150f12ad5391e0a0d60f6a3d119b06ce50 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:18 +0800 Subject: [PATCH 4035/5214] PCI: iproc: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-6-18255117159@163.com --- drivers/pci/controller/pcie-iproc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index ccf71993ea35..c8f0a87cf28a 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1529,8 +1529,10 @@ void iproc_pcie_remove(struct iproc_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); + pci_unlock_rescan_remove(); iproc_pcie_msi_disable(pcie); From a29812a55da8d0dbeb071b26ac428c338e3fc389 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:19 +0800 Subject: [PATCH 4036/5214] PCI: mediatek: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-7-18255117159@163.com --- drivers/pci/controller/pcie-mediatek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 75722524fe74..2fedb6d2939d 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1172,8 +1172,10 @@ static void mtk_pcie_remove(struct platform_device *pdev) struct mtk_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); + pci_unlock_rescan_remove(); mtk_pcie_free_resources(pcie); mtk_pcie_irq_teardown(pcie); From 4e4f9745f016c1631d00a4035b06f6e75d449e01 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:22 +0800 Subject: [PATCH 4037/5214] PCI: plda: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-10-18255117159@163.com --- drivers/pci/controller/plda/pcie-plda-host.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index 3c2f68383010..f9a34f323ad8 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -640,8 +640,10 @@ EXPORT_SYMBOL_GPL(plda_pcie_host_init); void plda_pcie_host_deinit(struct plda_pcie_rp *port) { + pci_lock_rescan_remove(); pci_stop_root_bus(port->bridge->bus); pci_remove_root_bus(port->bridge->bus); + pci_unlock_rescan_remove(); plda_pcie_irq_domain_deinit(port); From 0bd9611587bb494c33566d825fe34b2705e4b167 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 22 May 2026 00:18:20 +0800 Subject: [PATCH 4038/5214] PCI: rockchip: Protect root bus removal with rescan lock Hold the pci_rescan_remove_lock lock while stopping and removing a root bus to avoid racing with concurrent rescan or hotplug operations triggered via sysfs. Such races may lead to use-after-free issues or system crashes. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260521161822.132996-8-18255117159@163.com --- drivers/pci/controller/pcie-rockchip-host.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index ee1822ca01db..d203c4876d30 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1012,8 +1012,10 @@ static void rockchip_pcie_remove(struct platform_device *pdev) struct rockchip_pcie *rockchip = dev_get_drvdata(dev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); irq_domain_remove(rockchip->irq_domain); rockchip_pcie_deinit_phys(rockchip); From fda8749ba73638f5bbca3ffb39bc6861eb3b23fa Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Tue, 14 Apr 2026 13:47:30 +0530 Subject: [PATCH 4039/5214] PCI: host-common: Request bus reassignment when not probe-only pci_host_common_init() is used by several generic ECAM host drivers. After PCI core changes around pci_flags and preserve_config, these hosts no longer opted into full bus number reassignment the way they did before, which broke enumeration of devices on a Marvell CN106XX board. When PCI_PROBE_ONLY is not set, add PCI_REASSIGN_ALL_BUS so pci_scan_bridge_extend() takes the reassignment path: bus numbers can be assigned from firmware EA data (e.g. pci_ea_fixed_busnrs()). Skip the flag in probe-only mode so existing assignments are not overridden. Fixes: 7246a4520b4b ("PCI: Use preserve_config in place of pci_flags") Closes: https://lore.kernel.org/all/abkqm_LCd9zAM8cW@rkannoth-OptiPlex-7090/ Signed-off-by: Ratheesh Kannoth [mani: added stable tag] Signed-off-by: Manivannan Sadhasivam [bhelgaas: add problem report link] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Cc: Vidya Sagar Link: https://patch.msgid.link/20260414081730.3864372-1-rkannoth@marvell.com --- drivers/pci/controller/pci-host-common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5..99952fb7189b 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -68,6 +68,10 @@ int pci_host_common_init(struct platform_device *pdev, if (IS_ERR(cfg)) return PTR_ERR(cfg); + /* Do not reassign bus numbers if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_BUS); + bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->enable_device = ops->enable_device; From 7b25dbafa2fce50b1a48c1d057adb35da3563f9b Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 18 Jun 2026 09:19:30 +0100 Subject: [PATCH 4040/5214] spi: rpc-if: Use correct device for hardware reinitialization on resume rpcif_spi_resume() currently passes the SPI controller device to rpcif_hw_init(), but the function should be called with the RPC interface device. Retrieve the rpcif private data from the SPI controller and pass rpc->dev instead. Also propagate the return value of rpcif_hw_init() so that a failure during resume is properly reported rather than silently ignored. Fixes: ad4728740bd6 ("spi: rpc-if: Add resume support for RZ/G3E") Signed-off-by: Quang Nguyen Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260618081932.172168-1-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- drivers/spi/spi-rpc-if.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 1ef7bd91b3b3..b63c7856e758 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -206,8 +206,12 @@ static int rpcif_spi_suspend(struct device *dev) static int rpcif_spi_resume(struct device *dev) { struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rpcif *rpc = spi_controller_get_devdata(ctlr); + int ret; - rpcif_hw_init(dev, false); + ret = rpcif_hw_init(rpc->dev, false); + if (ret) + return ret; return spi_controller_resume(ctlr); } From 9a289cc425bf469642533e4afa01c90f08971d01 Mon Sep 17 00:00:00 2001 From: Rong Xu Date: Wed, 17 Jun 2026 15:46:23 -0700 Subject: [PATCH 4041/5214] modpost: Ignore Clang LTO suffixes in symbol matching When building the kernel with Clang ThinLTO enabled, the compiler can mangle static variable names by appending suffixes such as ".llvm." to prevent naming collisions across translation units. This name mangling breaks the section mismatch whitelisting in modpost. modpost relies on glob patterns (e.g., "*_ops" or "*_probe") to identify safe references between permanent data and initialization code. Because the LTO suffix modifies the end of the symbol name, legitimately whitelisted structures fail the match, resulting in false positive warnings. For example, a static pernet_operations struct triggers the following: WARNING: modpost: vmlinux: section mismatch in reference: \ ping_v4_net_ops.llvm.5641696707737373282 (section: .data) -> \ ping_v4_proc_init_net (section: .init.text) Fix this by ignoring "*_ops.llvm.*" in "from" symbol names (the same as "*_ops"). Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606111233.kM8oo8Df-lkp@intel.com/ Signed-off-by: Rong Xu Link: https://patch.msgid.link/20260617224623.1346309-1-xur@google.com Signed-off-by: Nathan Chancellor --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d592548cbd60..a7b72a81d248 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -969,7 +969,7 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, /* symbols in data sections that may refer to any init/exit sections */ if (match(fromsec, PATTERNS(DATA_SECTIONS)) && match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && - match(fromsym, PATTERNS("*_ops", "*_console"))) + match(fromsym, PATTERNS("*_ops", "*_ops.llvm.*", "*_console"))) return 0; /* Check for pattern 3 */ From 645323a7f4e55bb3abb0cb003b6b9dc715c8dc21 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 11 Jun 2026 14:00:00 +0800 Subject: [PATCH 4042/5214] kconfig: add optional warnings for changed input values When reading .config input, Kconfig stores user-provided values first and then resolves the final value after applying dependencies, ranges, and other constraints. If the final value differs from the user input, Kconfig already tracks that state internally, but it does not provide a focused diagnostic to show which explicit inputs were adjusted. This is particularly confusing for requested values that get forced down by unmet dependencies or clamped by ranges. Add an opt-in diagnostic controlled by KCONFIG_WARN_CHANGED_INPUT. Emit the warnings from conf_write() and conf_write_defconfig() after value resolution. Print the diagnostic to stderr directly, not through the normal message callback, so it remains visible when conf is run with -s, such as from make -s. Keep the diagnostic out of the conf_message() formatting buffer so long warning lists are not truncated, and mark processed symbols as written before the SYMBOL_WRITE check so duplicate menu nodes cannot emit duplicate warnings. Document the new environment variable and add tests for olddefconfig, savedefconfig, and the silent-conf path. Signed-off-by: Pengpeng Hou Tested-by: Julian Braha Link: https://patch.msgid.link/20260611060000.23858-1-pengpeng@iscas.ac.cn Signed-off-by: Nathan Chancellor --- Documentation/kbuild/kconfig.rst | 5 + scripts/kconfig/confdata.c | 106 +++++++++++++++++- scripts/kconfig/tests/conftest.py | 8 +- .../kconfig/tests/warn_changed_input/Kconfig | 40 +++++++ .../tests/warn_changed_input/__init__.py | 33 ++++++ .../kconfig/tests/warn_changed_input/config | 3 + .../tests/warn_changed_input/expected_config | 6 + .../warn_changed_input/expected_defconfig | 1 + .../tests/warn_changed_input/expected_stderr | 4 + 9 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 scripts/kconfig/tests/warn_changed_input/Kconfig create mode 100644 scripts/kconfig/tests/warn_changed_input/__init__.py create mode 100644 scripts/kconfig/tests/warn_changed_input/config create mode 100644 scripts/kconfig/tests/warn_changed_input/expected_config create mode 100644 scripts/kconfig/tests/warn_changed_input/expected_defconfig create mode 100644 scripts/kconfig/tests/warn_changed_input/expected_stderr diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index d213c4f599a4..e35dd1d5f9d3 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -59,6 +59,11 @@ Environment variables for ``*config``: This environment variable makes Kconfig warn about all unrecognized symbols in the config input. +``KCONFIG_WARN_CHANGED_INPUT`` + If set to a non-blank value, Kconfig prints optional warnings for + user-provided values that change after Kconfig resolves dependencies + or applies other constraints such as ranges. + ``KCONFIG_WERROR`` If set, Kconfig treats warnings as errors. diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 9599a0408862..4234a51d16fd 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -206,6 +206,78 @@ static void conf_message(const char *fmt, ...) va_end(ap); } +static void conf_changed_input_warning(const char *s) +{ + fputs(s, stderr); +} + +static bool conf_warn_changed_input_enabled(void) +{ + const char *env = getenv("KCONFIG_WARN_CHANGED_INPUT"); + + return env && *env; +} + +static const char *sym_get_user_value_string(struct symbol *sym) +{ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym->def[S_DEF_USER].tri) { + case yes: + return "y"; + case mod: + return "m"; + default: + return "n"; + } + default: + return sym->def[S_DEF_USER].val ?: ""; + } +} + +static bool sym_user_value_changed(struct symbol *sym) +{ + if (!sym_has_value(sym) || sym->type == S_UNKNOWN) + return false; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + return sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym); + default: + return strcmp(sym_get_user_value_string(sym), + sym_get_string_value(sym)); + } +} + +static void conf_clear_written_flags(void) +{ + struct symbol *sym; + + for_all_symbols(sym) + sym->flags &= ~SYMBOL_WRITTEN; +} + +static void conf_append_changed_input_warning(struct gstr *gs, + struct symbol *sym, + bool *changed_input_found) +{ + if (!sym_user_value_changed(sym)) + return; + + if (!*changed_input_found) { + str_printf(gs, + "warning: user-provided values changed by Kconfig:\n"); + *changed_input_found = true; + } + + str_printf(gs, " %s%s: %s -> %s\n", + CONFIG_, sym->name, + sym_get_user_value_string(sym), + sym_get_string_value(sym)); +} + const char *conf_get_configname(void) { char *name = getenv("KCONFIG_CONFIG"); @@ -759,11 +831,15 @@ int conf_write_defconfig(const char *filename) { struct symbol *sym; struct menu *menu; + struct gstr gs; FILE *out; + bool warn_changed_input = conf_warn_changed_input_enabled(); + bool changed_input_found = false; out = fopen(filename, "w"); if (!out) return 1; + gs = str_new(); sym_clear_all_valid(); @@ -772,10 +848,14 @@ int conf_write_defconfig(const char *filename) sym = menu->sym; - if (!sym || sym_is_choice(sym)) + if (!sym || sym_is_choice(sym) || sym->flags & SYMBOL_WRITTEN) continue; sym_calc_value(sym); + if (warn_changed_input) + conf_append_changed_input_warning(&gs, sym, + &changed_input_found); + sym->flags |= SYMBOL_WRITTEN; if (!(sym->flags & SYMBOL_WRITE)) continue; sym->flags &= ~SYMBOL_WRITE; @@ -798,6 +878,13 @@ int conf_write_defconfig(const char *filename) print_symbol_for_dotconfig(out, sym); } fclose(out); + + conf_clear_written_flags(); + + if (changed_input_found) + conf_changed_input_warning(str_get(&gs)); + + str_free(&gs); return 0; } @@ -809,7 +896,10 @@ int conf_write(const char *name) const char *str; char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1]; char *env; + struct gstr gs; bool need_newline = false; + bool warn_changed_input = conf_warn_changed_input_enabled(); + bool changed_input_found = false; if (!name) name = conf_get_configname(); @@ -838,6 +928,7 @@ int conf_write(const char *name) } if (!out) return 1; + gs = str_new(); conf_write_heading(out, &comment_style_pound); @@ -859,13 +950,16 @@ int conf_write(const char *name) } else if (!sym_is_choice(sym) && !(sym->flags & SYMBOL_WRITTEN)) { sym_calc_value(sym); + if (warn_changed_input) + conf_append_changed_input_warning(&gs, sym, + &changed_input_found); + sym->flags |= SYMBOL_WRITTEN; if (!(sym->flags & SYMBOL_WRITE)) goto next; if (need_newline) { fprintf(out, "\n"); need_newline = false; } - sym->flags |= SYMBOL_WRITTEN; print_symbol_for_dotconfig(out, sym); } @@ -892,8 +986,12 @@ int conf_write(const char *name) } fclose(out); - for_all_symbols(sym) - sym->flags &= ~SYMBOL_WRITTEN; + conf_clear_written_flags(); + + if (changed_input_found) + conf_changed_input_warning(str_get(&gs)); + + str_free(&gs); if (*tmpname) { if (is_same(name, tmpname)) { diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py index d94b79e012c0..66f95e4ed58c 100644 --- a/scripts/kconfig/tests/conftest.py +++ b/scripts/kconfig/tests/conftest.py @@ -37,7 +37,8 @@ class Conf: # runners def _run_conf(self, mode, dot_config=None, out_file='.config', - interactive=False, in_keys=None, extra_env={}): + interactive=False, in_keys=None, extra_env={}, + silent=False): """Run text-based Kconfig executable and save the result. mode: input mode option (--oldaskconfig, --defconfig= etc.) @@ -48,7 +49,10 @@ class Conf: extra_env: additional environments returncode: exit status of the Kconfig executable """ - command = [CONF_PATH, mode, 'Kconfig'] + command = [CONF_PATH] + if silent: + command.append('-s') + command += [mode, 'Kconfig'] # Override 'srctree' environment to make the test as the top directory extra_env['srctree'] = self._test_dir diff --git a/scripts/kconfig/tests/warn_changed_input/Kconfig b/scripts/kconfig/tests/warn_changed_input/Kconfig new file mode 100644 index 000000000000..69845e2f3fb3 --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/Kconfig @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DEP + bool "DEP" + help + Test dependency symbol for Kconfig warning coverage. + This is used by the warn_changed_input selftest. + It intentionally stays unset in the input fragment. + The test checks how dependent user input is adjusted. + +config A + bool "A" + depends on DEP + help + Test bool symbol for changed-input diagnostics. + The input fragment requests this symbol as built-in. + The unmet dependency on DEP forces the final value to n. + The warning should report that downgrade. + +config NUM + int "NUM" + range 10 20 + help + Test integer symbol for changed-input diagnostics. + The input fragment requests a value outside the allowed range. + Kconfig resolves it to the constrained in-range value. + The warning should report that adjustment. + +config DUP + bool "DUP" + depends on DEP + help + Test duplicate-definition handling for changed-input diagnostics. + The input fragment requests this symbol as built-in. + The duplicate definition below must not produce a duplicate warning. + This keeps the warning output stable for repeated menu entries. + +config DUP + bool + depends on DEP diff --git a/scripts/kconfig/tests/warn_changed_input/__init__.py b/scripts/kconfig/tests/warn_changed_input/__init__.py new file mode 100644 index 000000000000..4c3bca6af846 --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/__init__.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0 +""" +Test optional warnings for user-provided values changed by Kconfig. + +Warnings should stay disabled by default, and should only appear when +KCONFIG_WARN_CHANGED_INPUT is enabled. +""" + + +def test(conf): + assert conf.olddefconfig('config') == 0 + assert 'user-provided values changed by Kconfig' not in conf.stderr + + assert conf._run_conf('--olddefconfig', dot_config='config', + extra_env={ + 'KCONFIG_WARN_CHANGED_INPUT': '1', + }) == 0 + assert conf.stderr_contains('expected_stderr') + assert conf.config_matches('expected_config') + + assert conf._run_conf('--olddefconfig', dot_config='config', + extra_env={ + 'KCONFIG_WARN_CHANGED_INPUT': '1', + }, silent=True) == 0 + assert conf.stderr_contains('expected_stderr') + + assert conf._run_conf('--savedefconfig=defconfig', dot_config='config', + out_file='defconfig', + extra_env={ + 'KCONFIG_WARN_CHANGED_INPUT': '1', + }) == 0 + assert conf.stderr_contains('expected_stderr') + assert conf.config_matches('expected_defconfig') diff --git a/scripts/kconfig/tests/warn_changed_input/config b/scripts/kconfig/tests/warn_changed_input/config new file mode 100644 index 000000000000..dbe93ff26408 --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/config @@ -0,0 +1,3 @@ +CONFIG_A=y +CONFIG_NUM=30 +CONFIG_DUP=y diff --git a/scripts/kconfig/tests/warn_changed_input/expected_config b/scripts/kconfig/tests/warn_changed_input/expected_config new file mode 100644 index 000000000000..fe8bbec66c53 --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/expected_config @@ -0,0 +1,6 @@ +# +# Automatically generated file; DO NOT EDIT. +# Main menu +# +# CONFIG_DEP is not set +CONFIG_NUM=20 diff --git a/scripts/kconfig/tests/warn_changed_input/expected_defconfig b/scripts/kconfig/tests/warn_changed_input/expected_defconfig new file mode 100644 index 000000000000..af9e34851d2a --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/expected_defconfig @@ -0,0 +1 @@ +CONFIG_NUM=20 diff --git a/scripts/kconfig/tests/warn_changed_input/expected_stderr b/scripts/kconfig/tests/warn_changed_input/expected_stderr new file mode 100644 index 000000000000..9ec8446b4ac2 --- /dev/null +++ b/scripts/kconfig/tests/warn_changed_input/expected_stderr @@ -0,0 +1,4 @@ +warning: user-provided values changed by Kconfig: + CONFIG_A: y -> n + CONFIG_NUM: 30 -> 20 + CONFIG_DUP: y -> n From fbb7ad31ab376c5101b2ac7205fad0344fd2de60 Mon Sep 17 00:00:00 2001 From: Brett A C Sheffield Date: Thu, 15 Jan 2026 17:24:44 +0000 Subject: [PATCH 4043/5214] docs: kselftest: remove link to obsolete wiki Remove the reference to the obsolete kselftest wiki. The kselftest wiki is marked obsolete and is no longer updated. The last edit was in 2019, and the information is outdated, referring readers for support to IRC networks that have not been used for years, and to kernel versions that are no longer supported. If there is any relevant information left in the wiki it needs to be cleaned up and moved into the canonical kselftest documentation here. Link: https://lore.kernel.org/r/20260115172817.7120-1-bacs@librecast.net Signed-off-by: Brett A C Sheffield Acked-by: Bagas Sanjaya Acked-by: SeongJae Park Signed-off-by: Shuah Khan --- Documentation/dev-tools/kselftest.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst index d7bfe320338c..64c0ec7428a2 100644 --- a/Documentation/dev-tools/kselftest.rst +++ b/Documentation/dev-tools/kselftest.rst @@ -15,11 +15,6 @@ able to run that test on an older kernel. Hence, it is important to keep code that can still test an older kernel and make sure it skips the test gracefully on newer releases. -You can find additional information on Kselftest framework, how to -write new tests using the framework on Kselftest wiki: - -https://kselftest.wiki.kernel.org/ - On some systems, hot-plug tests could hang forever waiting for cpu and memory to be ready to be offlined. A special hot-plug target is created to run the full range of hot-plug tests. In default mode, hot-plug tests run From 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Thu, 18 Jun 2026 23:28:05 +0800 Subject: [PATCH 4044/5214] smb: client: fix double-free in SMB2_flush() replay SMB2_flush() keeps its response buffer bookkeeping across replay attempts. If a replayable flush response is received and the retry then fails before cifs_send_recv() stores a replacement response, flush_exit will free the stale response pointer a second time. Reinitialize resp_buftype and rsp_iov at the top of the replay loop so cleanup only acts on response state produced by the current attempt. This fixes a double-free without changing replay handling for successful requests. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Acked-by: Henrique Carvalho Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3c7691b39377..318559cd00db 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4450,6 +4450,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From b55e182f2324bc6a604c21a47aa6c448f719a532 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:33 -0300 Subject: [PATCH 4045/5214] smb: client: fix double-free in SMB2_open() replay A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_open_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 318559cd00db..4d6a989748f9 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3305,6 +3305,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); oparms->replay = !!(retries); From f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:34 -0300 Subject: [PATCH 4046/5214] smb: client: fix double-free in SMB2_ioctl() replay A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_ioctl_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 4d6a989748f9..121ae914c3cf 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3532,6 +3532,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From f96e1cdcb63ed3321142ff2fcdf784e32cda8fee Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:35 -0300 Subject: [PATCH 4047/5214] smb: client: fix double-free in SMB2_close() replay A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_close_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 121ae914c3cf..a7b1fbe28a2d 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3728,6 +3728,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; query_attrs = false; server = cifs_pick_channel(ses); From 2a88561d66eb855813cf004a0abe648bbb17de5e Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:36 -0300 Subject: [PATCH 4048/5214] smb: client: fix query_info() replay double-free A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_info_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index a7b1fbe28a2d..6e6aed87ab0a 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3942,6 +3942,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; allocated = false; server = cifs_pick_channel(ses); From 145f820dcbb2cced374f2532f8a61a44dce4a615 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:37 -0300 Subject: [PATCH 4049/5214] smb: client: fix change notify replay double-free A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_notify_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 6e6aed87ab0a..7d4b37b776c5 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4116,6 +4116,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 9647492b5e41954be59d5157eddbcd4cdc1656f7 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:38 -0300 Subject: [PATCH 4050/5214] smb: client: fix query directory replay double-free A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_directory_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 7d4b37b776c5..85642ea992d5 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5720,6 +5720,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 4cb5e246e65cd497155370bbca54c8f5e3a4105f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:15:40 +0000 Subject: [PATCH 4051/5214] alpha: Remove arch-specific strncpy() implementation strncpy() has no remaining callers in the kernel[1]. Remove the alpha-specific assembly implementation and __HAVE_ARCH_STRNCPY define, falling back to the generic version in lib/string.c. The __stxncpy helper (stxncpy.S/ev6-stxncpy.S) is retained as it is still used by strncat. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- arch/alpha/include/asm/string.h | 2 - arch/alpha/lib/strncpy.S | 83 --------------------------------- arch/alpha/lib/styncpy.S | 1 - 3 files changed, 86 deletions(-) delete mode 100644 arch/alpha/lib/strncpy.S diff --git a/arch/alpha/include/asm/string.h b/arch/alpha/include/asm/string.h index f043f91ff988..7c7545a2319a 100644 --- a/arch/alpha/include/asm/string.h +++ b/arch/alpha/include/asm/string.h @@ -47,8 +47,6 @@ extern inline void *__memset(void *s, int c, size_t n) #define __HAVE_ARCH_STRCPY extern char * strcpy(char *,const char *); -#define __HAVE_ARCH_STRNCPY -extern char * strncpy(char *, const char *, size_t); #define __HAVE_ARCH_STRCAT extern char * strcat(char *, const char *); #define __HAVE_ARCH_STRNCAT diff --git a/arch/alpha/lib/strncpy.S b/arch/alpha/lib/strncpy.S deleted file mode 100644 index cb90cf022df3..000000000000 --- a/arch/alpha/lib/strncpy.S +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * arch/alpha/lib/strncpy.S - * Contributed by Richard Henderson (rth@tamu.edu) - * - * Copy no more than COUNT bytes of the null-terminated string from - * SRC to DST. If SRC does not cover all of COUNT, the balance is - * zeroed. - * - * Or, rather, if the kernel cared about that weird ANSI quirk. This - * version has cropped that bit o' nastiness as well as assuming that - * __stxncpy is in range of a branch. - */ -#include - .set noat - .set noreorder - - .text - - .align 4 - .globl strncpy - .ent strncpy -strncpy: - .frame $30, 0, $26 - .prologue 0 - - mov $16, $0 # set return value now - beq $18, $zerolen - unop - bsr $23, __stxncpy # do the work of the copy - - unop - bne $18, $multiword # do we have full words left? - subq $24, 1, $3 # nope - subq $27, 1, $4 - - or $3, $24, $3 # clear the bits between the last - or $4, $27, $4 # written byte and the last byte in COUNT - andnot $3, $4, $4 - zap $1, $4, $1 - - stq_u $1, 0($16) - ret - - .align 4 -$multiword: - subq $27, 1, $2 # clear the final bits in the prev word - or $2, $27, $2 - zapnot $1, $2, $1 - subq $18, 1, $18 - - stq_u $1, 0($16) - addq $16, 8, $16 - unop - beq $18, 1f - - nop - unop - nop - blbc $18, 0f - - stq_u $31, 0($16) # zero one word - subq $18, 1, $18 - addq $16, 8, $16 - beq $18, 1f - -0: stq_u $31, 0($16) # zero two words - subq $18, 2, $18 - stq_u $31, 8($16) - addq $16, 16, $16 - bne $18, 0b - -1: ldq_u $1, 0($16) # clear the leading bits in the final word - subq $24, 1, $2 - or $2, $24, $2 - - zap $1, $2, $1 - stq_u $1, 0($16) -$zerolen: - ret - - .end strncpy - EXPORT_SYMBOL(strncpy) diff --git a/arch/alpha/lib/styncpy.S b/arch/alpha/lib/styncpy.S index 72fc2754eb57..f1078ff12530 100644 --- a/arch/alpha/lib/styncpy.S +++ b/arch/alpha/lib/styncpy.S @@ -1,4 +1,3 @@ -#include "strncpy.S" #ifdef CONFIG_ALPHA_EV67 #include "ev67-strncat.S" #else From ad3242a9acaa01cc9ecc6e483341697ca4f77461 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:16:06 +0000 Subject: [PATCH 4052/5214] m68k: Remove arch-specific strncpy() implementation strncpy() has no remaining callers in the kernel[1]. Remove the m68k-specific inline assembly implementation and __HAVE_ARCH_STRNCPY define, falling back to the generic version in lib/string.c. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- arch/m68k/include/asm/string.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/arch/m68k/include/asm/string.h b/arch/m68k/include/asm/string.h index 760cc13acdf4..ae6a6b51ad07 100644 --- a/arch/m68k/include/asm/string.h +++ b/arch/m68k/include/asm/string.h @@ -21,23 +21,6 @@ static inline size_t strnlen(const char *s, size_t count) return sc - s; } -#define __HAVE_ARCH_STRNCPY -static inline char *strncpy(char *dest, const char *src, size_t n) -{ - char *xdest = dest; - - asm volatile ("\n" - " jra 2f\n" - "1: move.b (%1),(%0)+\n" - " jeq 2f\n" - " addq.l #1,%1\n" - "2: subq.l #1,%2\n" - " jcc 1b\n" - : "+a" (dest), "+a" (src), "+d" (n) - : : "memory"); - return xdest; -} - #define __HAVE_ARCH_MEMMOVE extern void *memmove(void *, const void *, __kernel_size_t); From 7eda356658cbfba9aea48bddd12b97e562c21188 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:17:19 +0000 Subject: [PATCH 4053/5214] powerpc: Remove arch-specific strncpy() implementation strncpy() has no remaining callers in the kernel[1]. Remove the powerpc-specific assembly implementation from both the kernel (arch/powerpc/lib/string.S) and the boot wrapper (arch/powerpc/boot/string.S), along with the __HAVE_ARCH_STRNCPY define and declaration, falling back to the generic version in lib/string.c. The boot wrapper's strncpy had no callers in arch/powerpc/boot/. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- arch/powerpc/boot/string.S | 13 ------------- arch/powerpc/boot/string.h | 1 - arch/powerpc/include/asm/string.h | 2 -- arch/powerpc/lib/string.S | 22 ---------------------- 4 files changed, 38 deletions(-) diff --git a/arch/powerpc/boot/string.S b/arch/powerpc/boot/string.S index d2a2dbf1eefc..76801eb6d937 100644 --- a/arch/powerpc/boot/string.S +++ b/arch/powerpc/boot/string.S @@ -18,19 +18,6 @@ strcpy: bne 1b blr - .globl strncpy -strncpy: - cmpwi 0,r5,0 - beqlr - mtctr r5 - addi r6,r3,-1 - addi r4,r4,-1 -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - stbu r0,1(r6) - bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ - blr - .globl strcat strcat: addi r5,r3,-1 diff --git a/arch/powerpc/boot/string.h b/arch/powerpc/boot/string.h index 8c2ec0c05e4e..2b28d185343c 100644 --- a/arch/powerpc/boot/string.h +++ b/arch/powerpc/boot/string.h @@ -4,7 +4,6 @@ #include extern char *strcpy(char *dest, const char *src); -extern char *strncpy(char *dest, const char *src, size_t n); extern char *strcat(char *dest, const char *src); extern char *strchr(const char *s, int c); extern char *strrchr(const char *s, int c); diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h index 60ba22770f51..1981bd4036b5 100644 --- a/arch/powerpc/include/asm/string.h +++ b/arch/powerpc/include/asm/string.h @@ -5,7 +5,6 @@ #ifdef __KERNEL__ #ifndef CONFIG_KASAN -#define __HAVE_ARCH_STRNCPY #define __HAVE_ARCH_STRNCMP #define __HAVE_ARCH_MEMCHR #define __HAVE_ARCH_MEMCMP @@ -18,7 +17,6 @@ #define __HAVE_ARCH_MEMCPY_FLUSHCACHE extern char * strcpy(char *,const char *); -extern char * strncpy(char *,const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); extern int strcmp(const char *,const char *); extern int strncmp(const char *, const char *, __kernel_size_t); diff --git a/arch/powerpc/lib/string.S b/arch/powerpc/lib/string.S index daa72061dc0c..c3f5e7d31ee7 100644 --- a/arch/powerpc/lib/string.S +++ b/arch/powerpc/lib/string.S @@ -10,28 +10,6 @@ .text -/* This clears out any unused part of the destination buffer, - just as the libc version does. -- paulus */ -_GLOBAL(strncpy) - PPC_LCMPI 0,r5,0 - beqlr - mtctr r5 - addi r6,r3,-1 - addi r4,r4,-1 - .balign IFETCH_ALIGN_BYTES -1: lbzu r0,1(r4) - cmpwi 0,r0,0 - stbu r0,1(r6) - bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ - bnelr /* if we didn't hit a null char, we're done */ - mfctr r5 - PPC_LCMPI 0,r5,0 /* any space left in destination buffer? */ - beqlr /* we know r0 == 0 here */ -2: stbu r0,1(r6) /* clear it out if so */ - bdnz 2b - blr -EXPORT_SYMBOL(strncpy) - _GLOBAL(strncmp) PPC_LCMPI 0,r5,0 beq- 2f From dfe05fcca83d794cd76da1b6deb2dcd082aa1174 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:17:59 +0000 Subject: [PATCH 4054/5214] x86: Remove arch-specific strncpy() implementation strncpy() has no remaining callers in the kernel[1]. Remove the x86-32-specific inline assembly implementation and __HAVE_ARCH_STRNCPY define, falling back to the generic version in lib/string.c. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- arch/x86/include/asm/string_32.h | 3 --- arch/x86/lib/string_32.c | 19 ------------------- 2 files changed, 22 deletions(-) diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h index e9cce169bb4c..43ab6806ab53 100644 --- a/arch/x86/include/asm/string_32.h +++ b/arch/x86/include/asm/string_32.h @@ -9,9 +9,6 @@ #define __HAVE_ARCH_STRCPY extern char *strcpy(char *dest, const char *src); -#define __HAVE_ARCH_STRNCPY -extern char *strncpy(char *dest, const char *src, size_t count); - #define __HAVE_ARCH_STRCAT extern char *strcat(char *dest, const char *src); diff --git a/arch/x86/lib/string_32.c b/arch/x86/lib/string_32.c index f87ec24fa579..a9480d73c4f0 100644 --- a/arch/x86/lib/string_32.c +++ b/arch/x86/lib/string_32.c @@ -30,25 +30,6 @@ char *strcpy(char *dest, const char *src) EXPORT_SYMBOL(strcpy); #endif -#ifdef __HAVE_ARCH_STRNCPY -char *strncpy(char *dest, const char *src, size_t count) -{ - int d0, d1, d2, d3; - asm volatile("1:\tdecl %2\n\t" - "js 2f\n\t" - "lodsb\n\t" - "stosb\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n\t" - "rep stosb\n" - "2:" - : "=&S" (d0), "=&D" (d1), "=&c" (d2), "=&a" (d3) - : "0" (src), "1" (dest), "2" (count) : "memory"); - return dest; -} -EXPORT_SYMBOL(strncpy); -#endif - #ifdef __HAVE_ARCH_STRCAT char *strcat(char *dest, const char *src) { From 58c4ce8cd6cd1fbf1bca2e1d1f42f9e2899fa934 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:18:34 +0000 Subject: [PATCH 4055/5214] xtensa: Remove arch-specific strncpy() implementation strncpy() has no remaining callers in the kernel[1]. Remove the xtensa-specific inline assembly implementation and __HAVE_ARCH_STRNCPY define, falling back to the generic version in lib/string.c. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- arch/xtensa/include/asm/string.h | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/arch/xtensa/include/asm/string.h b/arch/xtensa/include/asm/string.h index ffce43513fa2..e16cda026df7 100644 --- a/arch/xtensa/include/asm/string.h +++ b/arch/xtensa/include/asm/string.h @@ -34,31 +34,6 @@ static inline char *strcpy(char *__dest, const char *__src) return __xdest; } -#define __HAVE_ARCH_STRNCPY -static inline char *strncpy(char *__dest, const char *__src, size_t __n) -{ - register char *__xdest = __dest; - unsigned long __dummy; - - if (__n == 0) - return __xdest; - - __asm__ __volatile__( - "1:\n\t" - "l8ui %2, %1, 0\n\t" - "s8i %2, %0, 0\n\t" - "addi %1, %1, 1\n\t" - "addi %0, %0, 1\n\t" - "beqz %2, 2f\n\t" - "bne %1, %5, 1b\n" - "2:" - : "=r" (__dest), "=r" (__src), "=&r" (__dummy) - : "0" (__dest), "1" (__src), "r" ((uintptr_t)__src+__n) - : "memory"); - - return __xdest; -} - #define __HAVE_ARCH_STRCMP static inline int strcmp(const char *__cs, const char *__ct) { From 079a028d6327e68cfa5d38b36123637b321c19a7 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 23 Mar 2026 01:27:25 +0000 Subject: [PATCH 4056/5214] string: Remove strncpy() from the kernel strncpy() has been a persistent source of bugs due to its ambiguous intended usage and frequently counter-intuitive semantics: it may not NUL-terminate the destination, and it unconditionally zero-pads to the full length, which isn't always needed. All former callers have been migrated[1] to: - strscpy() for NUL-terminated destinations - strscpy_pad() for NUL-terminated destinations needing zero-padding - strtomem_pad() for non-NUL-terminated fixed-width fields - memcpy_and_pad() for bounded copies with explicit padding - memcpy() for known-length copies Remove the generic implementation, its declaration, the FORTIFY_SOURCE wrapper, and associated tests. Link: https://github.com/KSPP/linux/issues/90 [1] Signed-off-by: Kees Cook --- Documentation/process/deprecated.rst | 43 +++++++------ drivers/misc/lkdtm/fortify.c | 2 +- include/linux/fortify-string.h | 49 --------------- include/linux/string.h | 3 - lib/string.c | 16 ----- lib/test_fortify/write_overflow-strncpy-src.c | 5 -- lib/test_fortify/write_overflow-strncpy.c | 5 -- lib/tests/fortify_kunit.c | 61 +------------------ 8 files changed, 26 insertions(+), 158 deletions(-) delete mode 100644 lib/test_fortify/write_overflow-strncpy-src.c delete mode 100644 lib/test_fortify/write_overflow-strncpy.c diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst index 03de71f654c7..22a5e62c92ea 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -131,27 +131,32 @@ value of strcpy() was used, since strscpy() does not return a pointer to the destination, but rather a count of non-NUL bytes copied (or negative errno when it truncates). -strncpy() on NUL-terminated strings ------------------------------------ -Use of strncpy() does not guarantee that the destination buffer will -be NUL terminated. This can lead to various linear read overflows and -other misbehavior due to the missing termination. It also NUL-pads -the destination buffer if the source contents are shorter than the -destination buffer size, which may be a needless performance penalty -for callers using only NUL-terminated strings. +strncpy() +--------- +strncpy() has been removed from the kernel. All former callers have +been migrated to safer alternatives. -When the destination is required to be NUL-terminated, the replacement is -strscpy(), though care must be given to any cases where the return value -of strncpy() was used, since strscpy() does not return a pointer to the -destination, but rather a count of non-NUL bytes copied (or negative -errno when it truncates). Any cases still needing NUL-padding should -instead use strscpy_pad(). +strncpy() did not guarantee NUL-termination of the destination buffer, +leading to linear read overflows and other misbehavior. It also +unconditionally NUL-padded the destination, which was a needless +performance penalty for callers using only NUL-terminated strings. Due +to its various behaviors, it was an ambiguous API for determining what +an author's true intent was for the copy. -If a caller is using non-NUL-terminated strings, strtomem() should be -used, and the destinations should be marked with the `__nonstring -`_ -attribute to avoid future compiler warnings. For cases still needing -NUL-padding, strtomem_pad() can be used. +The replacements for strncpy() are: + +- strscpy() when the destination must be NUL-terminated. +- strscpy_pad() when the destination must be NUL-terminated and + zero-padded (e.g., structs crossing privilege boundaries). +- memtostr() for NUL-terminated destinations from non-NUL-terminated + fixed-width sources (with the `__nonstring` attribute on the source). +- memtostr_pad() for the same, but with zero-padding. +- strtomem() for non-NUL-terminated fixed-width destinations, with + the `__nonstring` attribute on the destination. +- strtomem_pad() for non-NUL-terminated destinations that also need + zero-padding. +- memcpy_and_pad() for bounded copies from potentially unterminated + sources where the destination size is a runtime value. strlcpy() --------- diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c index 7615a02dfc47..5e66707e1180 100644 --- a/drivers/misc/lkdtm/fortify.c +++ b/drivers/misc/lkdtm/fortify.c @@ -98,7 +98,7 @@ static void lkdtm_FORTIFY_MEM_MEMBER(void) pr_info("trying to memcpy() past the end of a struct member...\n"); /* - * strncpy(target.a, src, 20); will hit a compile error because the + * memcpy(target.a, src, 20); will hit a compile error because the * compiler knows at build time that target.a < 20 bytes. Use a * volatile to force a runtime error. */ diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 171982e53c9a..cf841dc71fef 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -26,7 +26,6 @@ #define FORTIFY_WRITE 1 #define EACH_FORTIFY_FUNC(macro) \ - macro(strncpy), \ macro(strnlen), \ macro(strlen), \ macro(strscpy), \ @@ -95,8 +94,6 @@ extern char *__underlying_strcat(char *p, const char *q) __RENAME(strcat); extern char *__underlying_strcpy(char *p, const char *q) __RENAME(strcpy); extern __kernel_size_t __underlying_strlen(const char *p) __RENAME(strlen); extern char *__underlying_strncat(char *p, const char *q, __kernel_size_t count) __RENAME(strncat); -extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) __RENAME(strncpy); - #else #if defined(__SANITIZE_MEMORY__) @@ -120,7 +117,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) #define __underlying_strcpy __builtin_strcpy #define __underlying_strlen __builtin_strlen #define __underlying_strncat __builtin_strncat -#define __underlying_strncpy __builtin_strncpy #endif @@ -159,50 +155,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) (bounds) < (length) \ ) -/** - * strncpy - Copy a string to memory with non-guaranteed NUL padding - * - * @p: pointer to destination of copy - * @q: pointer to NUL-terminated source string to copy - * @size: bytes to write at @p - * - * If strlen(@q) >= @size, the copy of @q will stop after @size bytes, - * and @p will NOT be NUL-terminated - * - * If strlen(@q) < @size, following the copy of @q, trailing NUL bytes - * will be written to @p until @size total bytes have been written. - * - * Do not use this function. While FORTIFY_SOURCE tries to avoid - * over-reads of @q, it cannot defend against writing unterminated - * results to @p. Using strncpy() remains ambiguous and fragile. - * Instead, please choose an alternative, so that the expectation - * of @p's contents is unambiguous: - * - * +--------------------+--------------------+------------+ - * | **p** needs to be: | padded to **size** | not padded | - * +====================+====================+============+ - * | NUL-terminated | strscpy_pad() | strscpy() | - * +--------------------+--------------------+------------+ - * | not NUL-terminated | strtomem_pad() | strtomem() | - * +--------------------+--------------------+------------+ - * - * Note strscpy*()'s differing return values for detecting truncation, - * and strtomem*()'s expectation that the destination is marked with - * __nonstring when it is a character array. - * - */ -__FORTIFY_INLINE __diagnose_as(__builtin_strncpy, 1, 2, 3) -char *strncpy(char * const POS p, const char *q, __kernel_size_t size) -{ - const size_t p_size = __member_size(p); - - if (__compiletime_lessthan(p_size, size)) - __write_overflow(); - if (p_size < size) - fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p); - return __underlying_strncpy(p, q, size); -} - extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen); /** * strnlen - Return bounded count of characters in a NUL-terminated string @@ -809,7 +761,6 @@ char *strcpy(char * const POS p, const char * const POS q) #undef __underlying_strcpy #undef __underlying_strlen #undef __underlying_strncat -#undef __underlying_strncpy #undef POS #undef POS0 diff --git a/include/linux/string.h b/include/linux/string.h index b850bd91b3d8..5702daca4326 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -67,9 +67,6 @@ void *vmemdup_array_user(const void __user *src, size_t n, size_t size) #ifndef __HAVE_ARCH_STRCPY extern char * strcpy(char *,const char *); #endif -#ifndef __HAVE_ARCH_STRNCPY -extern char * strncpy(char *,const char *, __kernel_size_t); -#endif ssize_t sized_strscpy(char *, const char *, size_t); /* diff --git a/lib/string.c b/lib/string.c index b632c71df1a5..45ca1cfe0184 100644 --- a/lib/string.c +++ b/lib/string.c @@ -88,22 +88,6 @@ char *strcpy(char *dest, const char *src) EXPORT_SYMBOL(strcpy); #endif -#ifndef __HAVE_ARCH_STRNCPY -char *strncpy(char *dest, const char *src, size_t count) -{ - char *tmp = dest; - - while (count) { - if ((*tmp = *src) != 0) - src++; - tmp++; - count--; - } - return dest; -} -EXPORT_SYMBOL(strncpy); -#endif - #ifdef __BIG_ENDIAN # define ALLBUTLAST_BYTE_MASK (~255ul) #else diff --git a/lib/test_fortify/write_overflow-strncpy-src.c b/lib/test_fortify/write_overflow-strncpy-src.c deleted file mode 100644 index 8dcfb8c788dd..000000000000 --- a/lib/test_fortify/write_overflow-strncpy-src.c +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define TEST \ - strncpy(small, large_src, sizeof(small) + 1) - -#include "test_fortify.h" diff --git a/lib/test_fortify/write_overflow-strncpy.c b/lib/test_fortify/write_overflow-strncpy.c deleted file mode 100644 index b85f079c815d..000000000000 --- a/lib/test_fortify/write_overflow-strncpy.c +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define TEST \ - strncpy(instance.buf, large_src, sizeof(instance.buf) + 1) - -#include "test_fortify.h" diff --git a/lib/tests/fortify_kunit.c b/lib/tests/fortify_kunit.c index fc9c76f026d6..413cdbf3dc0d 100644 --- a/lib/tests/fortify_kunit.c +++ b/lib/tests/fortify_kunit.c @@ -458,7 +458,7 @@ static void fortify_test_strnlen(struct kunit *test) /* Make string unterminated, and recount. */ pad.buf[end] = 'A'; end = sizeof(pad.buf); - /* Reading beyond with strncpy() will fail. */ + /* Reading beyond will fail. */ KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end); KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end); @@ -531,64 +531,6 @@ static void fortify_test_strcpy(struct kunit *test) KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); } -static void fortify_test_strncpy(struct kunit *test) -{ - struct fortify_padding pad = { }; - char src[] = "Copy me fully into a small buffer and I will overflow!"; - size_t sizeof_buf = sizeof(pad.buf); - - OPTIMIZER_HIDE_VAR(sizeof_buf); - - /* Destination is %NUL-filled to start with. */ - KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); - KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0'); - KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0'); - KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 3], '\0'); - KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); - - /* Legitimate strncpy() 1 less than of max size. */ - KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf - 1) - == pad.buf); - KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); - /* Only last byte should be %NUL */ - KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 3], '\0'); - - /* Legitimate (though unterminated) max-size strncpy. */ - KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf) - == pad.buf); - KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); - /* No trailing %NUL -- thanks strncpy API. */ - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - /* But we will not have gone beyond. */ - KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); - - /* Now verify that FORTIFY is working... */ - KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 1) - == pad.buf); - /* Should catch the overflow. */ - KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - /* And we will not have gone beyond. */ - KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); - - /* And further... */ - KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 2) - == pad.buf); - /* Should catch the overflow. */ - KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0'); - /* And we will not have gone beyond. */ - KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); -} - static void fortify_test_strscpy(struct kunit *test) { struct fortify_padding pad = { }; @@ -1100,7 +1042,6 @@ static struct kunit_case fortify_test_cases[] = { KUNIT_CASE(fortify_test_strlen), KUNIT_CASE(fortify_test_strnlen), KUNIT_CASE(fortify_test_strcpy), - KUNIT_CASE(fortify_test_strncpy), KUNIT_CASE(fortify_test_strscpy), KUNIT_CASE(fortify_test_strcat), KUNIT_CASE(fortify_test_strncat), From 9092e15defbe6c7bc241c306093ca9d358a578e7 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 14 Jun 2026 01:42:39 +0800 Subject: [PATCH 4057/5214] net/sched: act_ct: preserve tc_skb_cb across defragmentation tcf_ct_handle_fragments() calls nf_ct_handle_fragments() without saving and restoring skb->cb. The defrag helper clears IPCB/IP6CB, which aliases the tc_skb_cb/qdisc_skb_cb control buffer. Fragmented traffic through act_ct therefore loses qdisc metadata such as pkt_segs and can trigger WARN_ON_ONCE() in qdisc_pkt_segs() when panic_on_warn is enabled. Save and restore the full tc_skb_cb around nf_ct_handle_fragments(), matching the pattern used by ovs_ct_handle_fragments(). Fixes: ec624fe740b4 ("net/sched: Extend qdisc control block with tc control block") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Link: https://patch.msgid.link/510c51217fd7aaf29c6dc298bab8d643fe229b1c.1781358692.git.xizh2024@lzu.edu.cn Signed-off-by: Jakub Kicinski --- net/sched/act_ct.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 6158e13c98d3..d47a82f9ac6c 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -844,11 +844,11 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, u8 family, u16 zone, bool *defrag) { enum ip_conntrack_info ctinfo; + struct tc_skb_cb cb; struct nf_conn *ct; int err = 0; bool frag; u8 proto; - u16 mru; /* Previously seen (loopback)? Ignore. */ ct = nf_ct_get(skb, &ctinfo); @@ -862,12 +862,13 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, if (err || !frag) return err; - err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &mru); + cb = *tc_skb_cb(skb); + err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru); if (err) return err; *defrag = true; - tc_skb_cb(skb)->mru = mru; + *tc_skb_cb(skb) = cb; return 0; } From 1fd8f80a199d321dd429c9881adccd1413417ad9 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 14 Jun 2026 01:42:40 +0800 Subject: [PATCH 4058/5214] selftests/tc-testing: act_ct: add TDC test for skb cb preservation across defrag Add a tc-testing case that sends IPv4 fragments through act_ct on clsact egress while a root prio qdisc is present on the transmit path. The test verifies that packet processing and qdisc accounting continue to work after conntrack defragmentation, covering tc_skb_cb preservation across defragmentation. Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Link: https://patch.msgid.link/53493549fdbcb2de25788b7894c56baadbc5fede.1781358692.git.xizh2024@lzu.edu.cn Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/actions/ct.json | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json index 33bb8f3ff8ed..da65f838bd52 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json @@ -664,5 +664,43 @@ "teardown": [ "$TC qdisc del dev $DEV1 ingress_block 21 clsact" ] + }, + { + "id": "9c2a", + "name": "Act_ct preserves skb cb across defrag before prio dequeue", + "category": [ + "actions", + "ct", + "scapy" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: prio", + "$TC qdisc add dev $DUMMY clsact", + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 matchall action mirred egress redirect dev $DUMMY" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 1 matchall action ct zone 1 pipe", + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "[Ether()/frag for frag in fragment(IP(src='10.0.0.10', dst='10.0.0.1', id=1)/UDP(sport=12345, dport=9)/Raw(b'A' * 4000), fragsize=1400)]" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 1 '^qdisc prio 1:'", + "matchPattern": "Sent [1-9][0-9]* bytes [1-9][0-9]* pkt", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact", + "$TC qdisc del dev $DUMMY clsact", + "$TC qdisc del dev $DUMMY root handle 1:" + ] } ] From 9e5ad06ea826322ce8c58b4a68442a96f600c3c4 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 21:28:37 -0700 Subject: [PATCH 4059/5214] virtio-net: fix len check in receive_big() receive_big() bounds the device-announced length by (big_packets_num_skbfrags + 1) * PAGE_SIZE. That is still too loose: add_recvbuf_big() sets sg[1] to start at offset sizeof(struct padded_vnet_hdr) into the first page, so the chain actually carries hdr_len + (PAGE_SIZE - sizeof(padded_vnet_hdr)) + big_packets_num_skbfrags * PAGE_SIZE bytes -- 20 bytes less than the check allows for the common hdr_len == 12 case. A malicious virtio backend can announce a len in that gap. page_to_skb() then walks one frag past the page chain, storing a NULL page->private into skb_shinfo()->frags[MAX_SKB_FRAGS], which is both an out-of-bounds write past the static frag array and a NULL frag handed up the rx path. Bound len by the size add_recvbuf_big() actually advertised. Fixes: 0c716703965f ("virtio-net: fix received length check in big packets") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Xuan Zhuo Acked-by: Michael S. Tsirkin Reviewed-by: Bui Quang Minh Link: https://patch.msgid.link/20260616042837.2249468-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7d2eeb9b1226..26afa6341d16 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1999,15 +1999,18 @@ static struct sk_buff *receive_big(struct net_device *dev, struct virtnet_rq_stats *stats) { struct page *page = buf; + unsigned long max_len; struct sk_buff *skb; + max_len = (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE - + sizeof(struct padded_vnet_hdr) + vi->hdr_len; + /* Make sure that len does not exceed the size allocated in * add_recvbuf_big. */ - if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) { + if (unlikely(len > max_len)) { pr_debug("%s: rx error: len %u exceeds allocated size %lu\n", - dev->name, len, - (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE); + dev->name, len, max_len); goto err; } From ed2294f94e34e97342850c40b320833d881c3819 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 16 Jun 2026 13:54:30 +0300 Subject: [PATCH 4060/5214] dpaa2-switch: fix VLAN upper check not rejecting bridge join The blamed commit refactored the prechangeupper event handling but failed to actually return an error in case dpaa2_switch_prevent_bridging_with_8021q_upper() detected a 802.1q upper on a port which tries to join a bridge. Fix this by returning err instead of 0. Fixes: 45035febc495 ("net: dpaa2-switch: refactor prechangeupper sanity checks") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260616105430.3725910-1-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 45f276c2c3ec..9f75cd66ae38 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2212,7 +2212,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot join a bridge while VLAN uppers are present"); - return 0; + return err; } netdev_for_each_lower_dev(upper_dev, other_dev, iter) { From d31deeab707b7945a7805d6406d0cd3118e640ef Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Tue, 16 Jun 2026 14:40:53 +0800 Subject: [PATCH 4061/5214] net: llc: make empty have static storage duration Make @empty have static storage duration (like net/sysctl_net.c does) to avoid storing a bad pointer, and keep consistent with __register_sysctl_table @table 'should not be free'd after registration'. Note that this is _not_ a bug, since size is 0 the pointer will never get deferenced. Signed-off-by: Wentao Guan Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616064053.690154-1-guanwentao@uniontech.com Signed-off-by: Jakub Kicinski --- net/llc/sysctl_net_llc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index c8d88e2508fc..15f1e5d88f20 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c @@ -47,7 +47,7 @@ static struct ctl_table_header *llc_station_header; int __init llc_sysctl_init(void) { - struct ctl_table empty[1] = {}; + static struct ctl_table empty[1] = {}; llc2_timeout_header = register_net_sysctl(&init_net, "net/llc/llc2/timeout", llc2_timeout_table); llc_station_header = register_net_sysctl_sz(&init_net, "net/llc/station", empty, 0); From a0aa6bf985aa5f22f7d398ddfff3aa0754892aea Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Mon, 15 Jun 2026 20:50:42 +0800 Subject: [PATCH 4062/5214] net: pch_gbe: handle TX skb allocation failure pch_gbe_alloc_tx_buffers() allocates an skb for each TX descriptor and then passes the returned pointer to skb_reserve(). If netdev_alloc_skb() fails, skb_reserve() dereferences NULL. Make pch_gbe_alloc_tx_buffers() return an error when an skb allocation fails. On failure, let pch_gbe_alloc_tx_buffers() clean the partially allocated TX ring before returning the error. While bringing the device up, release the RX buffer pool through a shared cleanup helper before unwinding the IRQ setup. Cc: stable+noautosel@kernel.org # untested fix to unlikely error path Fixes: 77555ee72282 ("net: Add Gigabit Ethernet driver of Topcliff PCH") Signed-off-by: Ruoyu Wang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615125043.3537046-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski --- .../ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 62f05f4569b1..48b94ce77490 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -1420,13 +1420,25 @@ pch_gbe_alloc_rx_buffers_pool(struct pch_gbe_adapter *adapter, return 0; } +static void pch_gbe_free_rx_buffers_pool(struct pch_gbe_adapter *adapter, + struct pch_gbe_rx_ring *rx_ring) +{ + dma_free_coherent(&adapter->pdev->dev, rx_ring->rx_buff_pool_size, + rx_ring->rx_buff_pool, rx_ring->rx_buff_pool_logic); + rx_ring->rx_buff_pool_logic = 0; + rx_ring->rx_buff_pool_size = 0; + rx_ring->rx_buff_pool = NULL; +} + /** * pch_gbe_alloc_tx_buffers - Allocate transmit buffers * @adapter: Board private structure * @tx_ring: Tx descriptor ring + * + * Return: 0 on success, -ENOMEM if a TX skb allocation fails. */ -static void pch_gbe_alloc_tx_buffers(struct pch_gbe_adapter *adapter, - struct pch_gbe_tx_ring *tx_ring) +static int pch_gbe_alloc_tx_buffers(struct pch_gbe_adapter *adapter, + struct pch_gbe_tx_ring *tx_ring) { struct pch_gbe_buffer *buffer_info; struct sk_buff *skb; @@ -1440,12 +1452,17 @@ static void pch_gbe_alloc_tx_buffers(struct pch_gbe_adapter *adapter, for (i = 0; i < tx_ring->count; i++) { buffer_info = &tx_ring->buffer_info[i]; skb = netdev_alloc_skb(adapter->netdev, bufsz); + if (!skb) { + pch_gbe_clean_tx_ring(adapter, tx_ring); + return -ENOMEM; + } skb_reserve(skb, PCH_GBE_DMA_ALIGN); buffer_info->skb = skb; tx_desc = PCH_GBE_TX_DESC(*tx_ring, i); tx_desc->gbec_status = (DSC_INIT16); } - return; + + return 0; } /** @@ -1887,7 +1904,12 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) "Error: can't bring device up - alloc rx buffers pool failed\n"); goto freeirq; } - pch_gbe_alloc_tx_buffers(adapter, tx_ring); + err = pch_gbe_alloc_tx_buffers(adapter, tx_ring); + if (err) { + netdev_err(netdev, + "Error: can't bring device up - alloc tx buffers failed\n"); + goto freebuf; + } pch_gbe_alloc_rx_buffers(adapter, rx_ring, rx_ring->count); adapter->tx_queue_len = netdev->tx_queue_len; pch_gbe_enable_dma_rx(&adapter->hw); @@ -1901,6 +1923,8 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) return 0; +freebuf: + pch_gbe_free_rx_buffers_pool(adapter, rx_ring); freeirq: pch_gbe_free_irq(adapter); out: @@ -1936,11 +1960,7 @@ void pch_gbe_down(struct pch_gbe_adapter *adapter) pch_gbe_clean_tx_ring(adapter, adapter->tx_ring); pch_gbe_clean_rx_ring(adapter, adapter->rx_ring); - dma_free_coherent(&adapter->pdev->dev, rx_ring->rx_buff_pool_size, - rx_ring->rx_buff_pool, rx_ring->rx_buff_pool_logic); - rx_ring->rx_buff_pool_logic = 0; - rx_ring->rx_buff_pool_size = 0; - rx_ring->rx_buff_pool = NULL; + pch_gbe_free_rx_buffers_pool(adapter, rx_ring); } /** From 8cdcf3d2caacdee7ddd363705fb4d93b0c1a0915 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 15 Jun 2026 23:04:27 +0800 Subject: [PATCH 4063/5214] octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF rvu_mbox_handler_lmtst_tbl_setup() uses req->base_pcifunc as a direct index into the LMT map table to read another function's LMTLINE physical base address and copy it into the caller's own LMT map table entry. The mailbox dispatcher authenticates req->hdr.pcifunc from the IRQ source, but req->base_pcifunc is a separate payload field and is not sanitized. Reject the request with -EPERM when a VF caller's base_pcifunc is not a valid function under its own PF. is_pf_func_valid() bounds the FUNC field to the PF's configured VF count, keeping the computed index inside the caller's own slot block. Fixes: 893ae97214c3 ("octeontx2-af: cn10k: Support configurable LMTST regions") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB78811656934E713B77DA6CEDAFE62@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c index d2163da28d18..fa4ea1258d29 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c @@ -178,6 +178,15 @@ int rvu_mbox_handler_lmtst_tbl_setup(struct rvu *rvu, * pcifunc (will be the one who is calling this mailbox). */ if (req->base_pcifunc) { + /* A VF is untrusted and must not redirect its LMTLINE to + * another PF's region, so confine VF callers to their own PF. + */ + if (is_vf(req->hdr.pcifunc) && + (!is_pf_func_valid(rvu, req->base_pcifunc) || + rvu_get_pf(rvu->pdev, req->hdr.pcifunc) != + rvu_get_pf(rvu->pdev, req->base_pcifunc))) + return -EPERM; + /* Calculating the LMT table index equivalent to primary * pcifunc. */ From ba45106342bbdd905651cb9fcefb8c11871d4c25 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:32 +0300 Subject: [PATCH 4064/5214] devlink: Fix parent ref leak in devl_rate_node_create() In the original commit the function bails out on kstrdup failure, forgetting to decrement the refcnt of the parent. Fix that by moving the parent refcnt setting after kstrdup. Fixes: caba177d7f4d ("devlink: Enable creation of the devlink-rate nodes from the driver") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-2-cratiu@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/rate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 41be2d6c2954..210e26c6cfa0 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -725,11 +725,6 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, if (!rate_node) return ERR_PTR(-ENOMEM); - if (parent) { - rate_node->parent = parent; - refcount_inc(&rate_node->parent->refcnt); - } - rate_node->type = DEVLINK_RATE_TYPE_NODE; rate_node->devlink = devlink; rate_node->priv = priv; @@ -740,6 +735,11 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, return ERR_PTR(-ENOMEM); } + if (parent) { + rate_node->parent = parent; + refcount_inc(&rate_node->parent->refcnt); + } + refcount_set(&rate_node->refcnt, 1); list_add(&rate_node->list, &devlink->rate_list); devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); From ba81a8b80f042038b9f73a4e5bb135de890b59bb Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:33 +0300 Subject: [PATCH 4065/5214] devlink: Fix parent ref leak on tc-bw failure When a node is created via rate-new with tc-bw and a parent node, devlink_nl_rate_set() executes the sequence of ops. It bails out on the first failure and doesn't rollback anything. For most things that is fine (setting some numbers), but the parent set can leak if there's another failure after that. That is precisely what happens when parent setting isn't the last block in the function. After the referenced "Fixes" commit, when tc-bw fails to be set the function bails out after having set the parent and incremented its refcount. There are two callers: - devlink_nl_rate_set_doit() is fine, it just reports the error. - but devlink_nl_rate_new_doit() frees the newly created node and leaks the parent refcnt. Fix that by reordering the blocks so parent setting is last and adding a comment explaining this so future modification preserve the ordering (hopefully). Fixes: 566e8f108fc7 ("devlink: Extend devlink rate API with traffic classes bandwidth management") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-3-cratiu@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/rate.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 210e26c6cfa0..533d21b028a7 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -486,16 +486,19 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, devlink_rate->tx_weight = weight; } - nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; - if (nla_parent) { - err = devlink_nl_rate_parent_node_set(devlink_rate, info, - nla_parent); + if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { + err = devlink_nl_rate_tc_bw_set(devlink_rate, info); if (err) return err; } - if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { - err = devlink_nl_rate_tc_bw_set(devlink_rate, info); + /* Keep parent setting last because it takes a reference. This function + * has no rollback, so failing after taking the ref would leak it. + */ + nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; + if (nla_parent) { + err = devlink_nl_rate_parent_node_set(devlink_rate, info, + nla_parent); if (err) return err; } From 5c121ee635680c93d7074becf14cfbaac140f80d Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Tue, 16 Jun 2026 19:52:36 +0800 Subject: [PATCH 4066/5214] net: airoha: fix foe_check_time allocation size foe_check_time is declared as u16 pointer but was allocated with only ppe_num_entries bytes instead of ppe_num_entries * sizeof(u16). When airoha_ppe_foe_verify_entry() is called with hash >= ppe_num_entries/2, it writes beyond the allocated buffer, causing heap buffer overflow and potential kernel crash. Fixes: 6d5b601d52a2 ("net: airoha: ppe: Dynamically allocate foe_check_time array in airoha_ppe struct") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178161119471.2163752.14373384830691569758@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 329e7c2aae89..42f4b0f21d17 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1601,7 +1601,8 @@ int airoha_ppe_init(struct airoha_eth *eth) return -ENOMEM; } - ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries, + ppe->foe_check_time = devm_kzalloc(eth->dev, + ppe_num_entries * sizeof(*ppe->foe_check_time), GFP_KERNEL); if (!ppe->foe_check_time) return -ENOMEM; From e438ec3e9e95cd3f49a8120e5f63ae3f9606e6fa Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 16 Jun 2026 15:23:03 +0200 Subject: [PATCH 4067/5214] net: macb: add TX stall timeout callback to recover from lost TSTART write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MACB found in the Raspberry Pi RP1 suffers from sporadic stalls on the TX queue. While the exact root cause is not yet fully understood, it is likely related to a hardware issue where a TSTART write to the NCR register is missed, preventing the transmission from being kicked off. Implement a timeout callback to handle TX queue stalls, triggering the existing restart mechanism to recover. Link: https://lore.kernel.org/all/20260514215459.36109-1-lukasz@raczylo.com/ Fixes: dc110d1b23564 ("net: cadence: macb: Add support for Raspberry Pi RP1 ethernet controller") Signed-off-by: Lukasz Raczylo Co-developed-by: Steffen Jaeckel Signed-off-by: Steffen Jaeckel Co-developed-by: Andrea della Porta Signed-off-by: Andrea della Porta Reviewed-by: Nicolai Buchwitz Reviewed-by: Théo Lebrun Link: https://patch.msgid.link/468f480454a314303bac6a54780b153f689f2267.1781598350.git.andrea.porta@suse.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index a12aa21244e8..fd282a1700fb 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4522,6 +4522,13 @@ static int macb_setup_tc(struct net_device *dev, enum tc_setup_type type, } } +static void macb_tx_timeout(struct net_device *dev, unsigned int q) +{ + struct macb *bp = netdev_priv(dev); + + macb_tx_restart(&bp->queues[q]); +} + static const struct net_device_ops macb_netdev_ops = { .ndo_open = macb_open, .ndo_stop = macb_close, @@ -4540,6 +4547,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_hwtstamp_set = macb_hwtstamp_set, .ndo_hwtstamp_get = macb_hwtstamp_get, .ndo_setup_tc = macb_setup_tc, + .ndo_tx_timeout = macb_tx_timeout, }; /* Configure peripheral capabilities according to device tree From bf6e8af2c8be77489bedeae9f8a9654cb710e500 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Tue, 16 Jun 2026 20:30:57 +0800 Subject: [PATCH 4068/5214] flow_dissector: check device type before reading ETH_ADDRS __skb_flow_dissect() unconditionally reads 12 bytes from eth_hdr(skb) when FLOW_DISSECTOR_KEY_ETH_ADDRS is requested. This assumes the skb has a valid Ethernet header at mac_header, which is not always the case. The problem can be triggered by: 1. Creating a TUN device in L3 mode (IFF_TUN, hard_header_len=0) 2. Attaching a multiq qdisc with a flower filter matching on eth_src 3. Sending a packet through AF_PACKET Since TUN in L3 mode has no link-layer header, mac_header points to the L3 data area. The flow dissector reads 12 bytes of uninitialized skb memory, which then propagates through fl_set_masked_key() and is used as a rhashtable lookup key in __fl_lookup(), as reported by KMSAN. Rejecting the filter in the control path (at tc filter add time) is not feasible because TC filter blocks can be shared between arbitrary devices -- a filter installed on an Ethernet device may later classify packets on a headerless device through a shared block. The device association is not fixed at filter creation time. Fix this by gating the memcpy on dev->type == ARPHRD_ETHER, which ensures only true Ethernet-framed packets have their addresses read. This is more precise than the previous hard_header_len >= 12 check, which would incorrectly pass for non-Ethernet link types like IPoIB (ARPHRD_INFINIBAND, hard_header_len=24) and FDDI (hard_header_len=21) whose L2 headers are not in Ethernet format. Additionally check skb_mac_header_was_set() to guard against the pathological case where mac_header is the unset sentinel (~0U), which would cause eth_hdr() to return a wild pointer. For the act_mirred redirect case (Ethernet packet redirected to a non-Ethernet device sharing a TC block), zeroing the key is the correct behavior: the packet is now being classified on the target device, where Ethernet address matching is not semantically meaningful. Note: on non-Ethernet devices, the zeroed key will match a filter configured with all-zero MAC addresses. This is an improvement over the previous behavior where uninitialized memory could randomly match any filter. Reported-by: syzbot+fa2f5b1fb06147be5e16@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fa2f5b1fb06147be5e16 Fixes: 67a900cc0436 ("flow_dissector: introduce support for Ethernet addresses") Signed-off-by: Yun Zhou Link: https://patch.msgid.link/20260616123057.482154-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski --- net/core/flow_dissector.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 2a98f5fa74eb..8aa4f9b4df81 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1173,13 +1173,21 @@ bool __skb_flow_dissect(const struct net *net, if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct ethhdr *eth = eth_hdr(skb); struct flow_dissector_key_eth_addrs *key_eth_addrs; key_eth_addrs = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS, target_container); - memcpy(key_eth_addrs, eth, sizeof(*key_eth_addrs)); + /* TC filter blocks can be shared across devices with + * different link types, so we cannot validate this + * when the filter is installed -- check at dissect time. + */ + if (skb && skb->dev && + skb->dev->type == ARPHRD_ETHER && + skb_mac_header_was_set(skb)) + memcpy(key_eth_addrs, eth_hdr(skb), sizeof(*key_eth_addrs)); + else + memset(key_eth_addrs, 0, sizeof(*key_eth_addrs)); } if (dissector_uses_key(flow_dissector, From c0ebe492329a4d29592e2240df17e56724849f1f Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 16 Jun 2026 09:09:52 -0700 Subject: [PATCH 4069/5214] netconsole: don't drop the last byte of a full-sized message nt->buf is exactly MAX_PRINT_CHUNK bytes, but scnprintf() reserves one byte for its NUL terminator, so a non-fragmented payload of exactly MAX_PRINT_CHUNK loses its last byte (emitted as a stray NUL in the release path). Grow nt->buf to MAX_PRINT_CHUNK + 1 and bound the scnprintf() calls with sizeof(nt->buf); the transmitted length stays capped at MAX_PRINT_CHUNK. Alternatively, nt->buf could be left at MAX_PRINT_CHUNK and the NUL byte reserved by routing exactly-MAX_PRINT_CHUNK payloads to fragmentation ('len < MAX_PRINT_CHUNK'), at the cost of fragmenting those messages. But it would look less sane, thus the current approach. Fixes: c62c0a17f9b7 ("netconsole: Append kernel version to message") Signed-off-by: Breno Leitao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616-max_print_chunk-v1-1-8dc125d67083@debian.org Signed-off-by: Jakub Kicinski --- drivers/net/netconsole.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index a159cb293981..862001d09aa8 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -190,8 +190,10 @@ struct netconsole_target { bool extended; bool release; struct netpoll np; - /* protected by target_list_lock */ - char buf[MAX_PRINT_CHUNK]; + /* protected by target_list_lock; +1 gives scnprintf() room for its + * NUL terminator so a full MAX_PRINT_CHUNK payload is not truncated + */ + char buf[MAX_PRINT_CHUNK + 1]; struct work_struct resume_wq; }; @@ -1938,7 +1940,7 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt, if (release_len) { release = init_utsname()->release; - scnprintf(nt->buf, MAX_PRINT_CHUNK, "%s,%.*s", release, + scnprintf(nt->buf, sizeof(nt->buf), "%s,%.*s", release, msg_len, msg); msg_len += release_len; } else { @@ -1947,12 +1949,12 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt, if (userdata) msg_len += scnprintf(&nt->buf[msg_len], - MAX_PRINT_CHUNK - msg_len, "%s", + sizeof(nt->buf) - msg_len, "%s", userdata); if (sysdata) msg_len += scnprintf(&nt->buf[msg_len], - MAX_PRINT_CHUNK - msg_len, "%s", + sizeof(nt->buf) - msg_len, "%s", sysdata); send_udp(nt, nt->buf, msg_len); From 55d9895f89970501fe126d1026b586b04a224c27 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 01:38:41 +0800 Subject: [PATCH 4070/5214] net: thunderbolt: Fix frags[] overflow by bounding frame_count tbnet_poll() assembles a multi-frame ThunderboltIP packet into one skb. The first frame goes into the skb linear area and every further frame is added as a page fragment. skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, hdr_size, frame_size, TBNET_RX_PAGE_SIZE - hdr_size); A packet of frame_count frames therefore ends up with frame_count - 1 fragments. tbnet_check_frame() only bounds the peer supplied frame_count to TBNET_RING_SIZE / 4 (64), which is far above MAX_SKB_FRAGS (17 by default). A peer that sends a packet of 19 or more small frames pushes nr_frags past MAX_SKB_FRAGS, so skb_add_rx_frag() writes past skb_shinfo()->frags[] and corrupts memory after the shared info. Tighten the start of packet bound to MAX_SKB_FRAGS + 1 so a packet can never produce more fragments than frags[] can hold. This matches the recent skb frags overflow fixes in other receive paths, for example f0813bcd2d9d ("net: wwan: t7xx: fix potential skb->frags overflow in RX path") and 600dc40554dc ("net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()"). Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Acked-by: Mika Westerberg Link: https://patch.msgid.link/178163152194.2486768.14724194232649760778@maoyixie.com Signed-off-by: Jakub Kicinski --- drivers/net/thunderbolt/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 7aae5d915a1e..ac016890646c 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -787,8 +787,12 @@ static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf, return true; } - /* Start of packet, validate the frame header */ - if (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) { + /* Start of packet, validate the frame header. tbnet_poll() puts the + * first frame in the skb linear area and every further frame in a page + * fragment, so a packet may not span more than MAX_SKB_FRAGS + 1 frames + * without overflowing skb_shinfo()->frags[]. + */ + if (frame_count == 0 || frame_count > MAX_SKB_FRAGS + 1) { net->stats.rx_length_errors++; return false; } From d676c9a73bdcd8237425dbb826f2bd1a25c36e40 Mon Sep 17 00:00:00 2001 From: Ankit Garg Date: Tue, 16 Jun 2026 18:32:08 -0700 Subject: [PATCH 4071/5214] gve: fix header buffer corruption with header-split and HW-GRO The DQO RX datapath programs a per-buffer-queue-descriptor header_buf_addr at post time and reads the split header back at completion time. Both the post and the read currently index the header buffer by queue position rather than by the buffer's identity: - post (gve_rx_post_buffers_dqo): header_buf_addr is computed from bufq->tail - read (gve_rx_dqo): the header is read from desc_idx (the completion queue head index) This relies on the buffer-queue index and the completion-queue index being equal for the start of every packet, i.e. on the device consuming posted buffers and returning completions in the exact same order. That assumption does not hold once HW-GRO is enabled with multiple flows: coalesced segments are accepted and completed in an order that may differ from the order buffers were posted, and segments from different flows may interleave. That results in two problems: 1. Wrong header slot on read. Because the read offset is derived from the completion index (desc_idx) while the device wrote the header to the address programmed for the buffer's buf_id, the driver can copy a header belonging to a different packet. This shows up as throughput drop (about 30% drop and large numbers of TCP retransmissions) with header-split and HW-GRO both enabled and many streams. 2. Header buffer reused while still owned by the device. The driver advances bufq->head by one per completion and re-posts buffers based on that. Arrival of N RX completions only guarantees that at least N RX buffer descriptors have been read by the device. It does not guarantee that the device has relinquished the ownership of all the buffers corresponding to those N descriptors. With out-of-order completions (e.g. the completion for a packet copied into buffer N arrives before the completion for a packet copied into buffer N-1), the driver can re-post and overwrite a header buffer that the device is still going to write into, corrupting the header of a packet whose completion has not yet been processed. Fix both issues by indexing the header buffer by buf_id on both the post and read paths. Reading from buf_id's slot is therefore always correct regardless of completion ordering (fixes problem 1). Indexing by buf_id also ties each header slot to the lifetime of its buffer state. A buffer state is only returned to the free/recycle lists when its own completion (buf_id) is processed, so its header slot can only be re-posted after the device is done with it. This makes header slot reuse safe under out-of-order completions (fixes problem 2). Allocate (gve_rx_alloc_hdr_bufs) and free (gve_rx_free_hdr_bufs) the header buffers based on num_buf_states to match the buf_id indexing. Cc: stable@vger.kernel.org Fixes: 5e37d8254e7f ("gve: Add header split data path") Signed-off-by: Ankit Garg Reviewed-by: Praveen Kaligineedi Reviewed-by: Jordan Rhee Reviewed-by: Harshitha Ramamurthy Signed-off-by: Joshua Washington Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260617013208.3781453-1-joshwash@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 7924dce719e2..02cba280d81a 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -21,11 +21,13 @@ static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; - int buf_count = rx->dqo.bufq.mask + 1; if (rx->dqo.hdr_bufs.data) { - dma_free_coherent(hdev, priv->header_buf_size * buf_count, - rx->dqo.hdr_bufs.data, rx->dqo.hdr_bufs.addr); + size_t size = + (size_t)priv->header_buf_size * rx->dqo.num_buf_states; + + dma_free_coherent(hdev, size, rx->dqo.hdr_bufs.data, + rx->dqo.hdr_bufs.addr); rx->dqo.hdr_bufs.data = NULL; } } @@ -254,7 +256,7 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv, /* Allocate header buffers for header-split */ if (cfg->enable_header_split) - if (gve_rx_alloc_hdr_bufs(priv, rx, buffer_queue_slots)) + if (gve_rx_alloc_hdr_bufs(priv, rx, rx->dqo.num_buf_states)) goto err; /* Allocate RX completion queue */ @@ -381,10 +383,13 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) break; } - if (rx->dqo.hdr_bufs.data) + if (rx->dqo.hdr_bufs.data) { + u16 buf_id = le16_to_cpu(desc->buf_id); + desc->header_buf_addr = cpu_to_le64(rx->dqo.hdr_bufs.addr + - priv->header_buf_size * bufq->tail); + (size_t)priv->header_buf_size * buf_id); + } bufq->tail = (bufq->tail + 1) & bufq->mask; complq->num_free_slots--; @@ -826,10 +831,13 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, int unsplit = 0; if (hdr_len && !hbo) { - rx->ctx.skb_head = gve_rx_copy_data(priv->dev, napi, - rx->dqo.hdr_bufs.data + - desc_idx * priv->header_buf_size, - hdr_len); + size_t offset = + (size_t)buffer_id * priv->header_buf_size; + + rx->ctx.skb_head = + gve_rx_copy_data(priv->dev, napi, + rx->dqo.hdr_bufs.data + offset, + hdr_len); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; From 1bd6676254b4ab6acd44b662b5e92822c036463a Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 16 Jun 2026 22:24:24 +0800 Subject: [PATCH 4072/5214] net: ena: clean up XDP TX queues when regular TX setup fails create_queues_with_size_backoff() creates XDP TX queues before setting up the regular TX path. If the subsequent allocation or creation of regular TX queues fails, the error handling paths omit the teardown of the XDP TX queues, leading to a resource leak. Fix this by explicitly destroying the XDP TX queue subset at the two missing failure points. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have an ENA device to test with, no runtime testing was able to be performed. Fixes: 548c4940b9f1 ("net: ena: Implement XDP_TX action") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Arthur Kiyanovski Tested-by: Arthur Kiyanovski Link: https://patch.msgid.link/20260616142424.4005130-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 92d149d4f091..5d05020a6d05 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -752,6 +752,18 @@ static void ena_destroy_all_tx_queues(struct ena_adapter *adapter) } } +static void ena_destroy_xdp_tx_queues(struct ena_adapter *adapter) +{ + u16 ena_qid; + int i; + + for (i = adapter->xdp_first_ring; + i < adapter->xdp_first_ring + adapter->xdp_num_queues; i++) { + ena_qid = ENA_IO_TXQ_IDX(i); + ena_com_destroy_io_queue(adapter->ena_dev, ena_qid); + } +} + static void ena_destroy_all_rx_queues(struct ena_adapter *adapter) { u16 ena_qid; @@ -2078,14 +2090,21 @@ static int create_queues_with_size_backoff(struct ena_adapter *adapter) rc = ena_setup_tx_resources_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); + ena_free_all_io_tx_resources_in_range(adapter, + adapter->xdp_first_ring, + adapter->xdp_num_queues); goto err_setup_tx; + } rc = ena_create_io_tx_queues_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); goto err_create_tx_queues; + } rc = ena_setup_all_rx_resources(adapter); if (rc) From 4045f1c3d68ef4b589ae2587e6ff66ce8017daf2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 17 Jun 2026 13:43:23 +0300 Subject: [PATCH 4073/5214] selftests: vlan_bridge_binding: Fix flaky operational state check check_operstate() busy waits for up to one second for the operational state to change to the expected state. This is not enough since carrier loss events can be delayed by the kernel for up to one second (see __linkwatch_run_queue()), leading to sporadic failures. Fix by increasing the busy wait period to two seconds. Fixes: dca12e9ab760 ("selftests: net: Add a VLAN bridge binding selftest") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20260616092733.3a31be4d@kernel.org/ Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260617104323.1069457-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/vlan_bridge_binding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/vlan_bridge_binding.sh b/tools/testing/selftests/net/vlan_bridge_binding.sh index e8c02c64e03a..d04caa14202d 100755 --- a/tools/testing/selftests/net/vlan_bridge_binding.sh +++ b/tools/testing/selftests/net/vlan_bridge_binding.sh @@ -64,7 +64,7 @@ check_operstate() local expect=$1; shift local operstate - operstate=$(busywait 1000 \ + operstate=$(busywait 2000 \ operstate_is "$dev" "$expect") check_err $? "Got operstate of $operstate, expected $expect" } From 0ec4b785987049031db437ef9d5bb962706b69bb Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Wed, 17 Jun 2026 17:28:54 +0800 Subject: [PATCH 4074/5214] net: wangxun: don't advertise IFF_SUPP_NOFCS Like commit a24162f18825("i40e: don't advertise IFF_SUPP_NOFCS"), ngbe and txgbe also advertises IFF_SUPP_NOFCS and allowing users to use the SO_NOFCS socket option. But the driver does not check skb->no_fcs, so this option is silently ignored. With this change, send() fails with -EPROTONOSUPPORT when AF_PACKET socket is set SO_NOFCS option. Signed-off-by: Rongguang Wei Link: https://patch.msgid.link/20260617092854.133992-1-clementwei90@163.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 1 - drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 8678c49b892a..a16221995909 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -715,7 +715,6 @@ static int ngbe_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_GRO; netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; netdev->min_mtu = ETH_MIN_MTU; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ce82e13aa8ae..20c5a295c6c2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -796,7 +796,6 @@ static int txgbe_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_RX_UDP_TUNNEL_PORT; netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; netdev->min_mtu = ETH_MIN_MTU; From b8613e9792002add3bf77122868cc06ce142e953 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Wed, 17 Jun 2026 18:10:39 +1200 Subject: [PATCH 4075/5214] selftests: net: fix file owner for broadcast_ether_dst test Ensure the output file is always owned by root (even if tcpdump was compiled with `--with-user`), by passing the `-Z root` argument when invoking it. Cc: stable@vger.kernel.org Reported-by: Edoardo Canepa Closes: https://bugs.launchpad.net/ubuntu-kernel-tests/+bug/2129815 Fixes: bf59028ea8d4 ("selftests: net: add test for destination in broadcast packets") Suggested-by: Edoardo Canepa Tested-by: Ross Porter Signed-off-by: Ross Porter Link: https://patch.msgid.link/20260617061039.79717-2-ross.porter@canonical.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/broadcast_ether_dst.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/broadcast_ether_dst.sh b/tools/testing/selftests/net/broadcast_ether_dst.sh index 334a7eca8a80..cc571f607429 100755 --- a/tools/testing/selftests/net/broadcast_ether_dst.sh +++ b/tools/testing/selftests/net/broadcast_ether_dst.sh @@ -44,7 +44,7 @@ test_broadcast_ether_dst() { # tcpdump will exit after receiving a single packet # timeout will kill tcpdump if it is still running after 2s timeout 2s ip netns exec "${CLIENT_NS}" \ - tcpdump -i link0 -c 1 -w "${CAPFILE}" icmp &> "${OUTPUT}" & + tcpdump -i link0 -c 1 -w "${CAPFILE}" -Z root icmp &> "${OUTPUT}" & pid=$! slowwait 1 grep -qs "listening" "${OUTPUT}" From e5c00023270e87953d32979ff7dc6e4c63d693df Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 16 Jun 2026 22:31:46 -0400 Subject: [PATCH 4076/5214] net: rds: check cmsg_len before reading rds_rdma_args in size pass rds_rm_size() handles RDS_CMSG_RDMA_ARGS after only CMSG_OK() and then calls rds_rdma_extra_size(), which reads args->local_vec_addr and args->nr_local without first checking that cmsg_len covers struct rds_rdma_args. The other two RDS_CMSG_RDMA_ARGS consumers already guard this: rds_rdma_bytes() in rds_sendmsg() and rds_cmsg_rdma_args() in rds_cmsg_send() both reject cmsg_len < CMSG_LEN(sizeof(struct rds_rdma_args)). Add the same check to rds_rm_size() so all three RDMA args passes are consistent. This is a consistency and hardening change with no behavioral effect for well-formed senders and no reachable bug today: rds_rdma_bytes() runs before rds_rm_size() in rds_sendmsg() and already rejects a short RDS_CMSG_RDMA_ARGS, so the size pass is not reached with an undersized cmsg. But rds_rm_size() reads the args independently of that earlier pass, and nothing in rds_rm_size() itself records or enforces the precondition, so a reader or a future refactor of the size pass cannot tell the cmsg has already been length-checked. Applying the same cmsg_len guard in all three RDS_CMSG_RDMA_ARGS consumers keeps that invariant local to each and robust to reordering. Signed-off-by: Michael Bommarito Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260617023146.2780077-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/send.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/rds/send.c b/net/rds/send.c index e5d58c29aabe..68be1bf0e0ad 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -967,6 +967,8 @@ static int rds_rm_size(struct msghdr *msg, int num_sgs, switch (cmsg->cmsg_type) { case RDS_CMSG_RDMA_ARGS: + if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct rds_rdma_args))) + return -EINVAL; if (vct->indx >= vct->len) { vct->len += vct->incr; tmp_iov = From bda3348872a2ef0d19f2df6aa8cb5025adce2f20 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Wed, 17 Jun 2026 09:58:18 +0200 Subject: [PATCH 4077/5214] tipc: fix slab-use-after-free Read in tipc_aead_decrypt_done tipc_aead_decrypt() goes straight from tipc_bearer_hold(b) to crypto_aead_decrypt(req) without taking a reference on the netns, unlike the encrypt path. When crypto_aead_decrypt() is offloaded asynchronously (e.g. the SIMD aead wrapper queuing to cryptd), the cryptd worker runs tipc_aead_decrypt_done() later. If the bearer's netns is torn down in the meantime, cleanup_net() -> tipc_exit_net() -> tipc_crypto_stop() frees the per-netns tipc_crypto, and the completion then reads it: tipc_aead_decrypt_done() dereferences aead->crypto->stats and aead->crypto->net, and tipc_crypto_rcv_complete() dereferences aead->crypto->aead[] and the node table -- reading freed memory. Decoded KASAN splat (v7.1-rc7, CONFIG_KASAN_INLINE + TIPC + TIPC_CRYPTO): BUG: KASAN: slab-use-after-free in tipc_aead_decrypt_done (net/tipc/crypto.c:999) Read of size 8 at addr ffff8881056258a8 by task kworker/u16:2/51 Workqueue: events_unbound Call Trace: tipc_aead_decrypt_done (net/tipc/crypto.c:999) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Allocated by task 169: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) tipc_crypto_start (net/tipc/crypto.c:1502) tipc_init_net (net/tipc/core.c:72) ops_init (net/core/net_namespace.c:137) setup_net (net/core/net_namespace.c:446) copy_net_ns (net/core/net_namespace.c:579) create_new_namespaces (kernel/nsproxy.c:132) __x64_sys_unshare (kernel/fork.c:3316) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Freed by task 8: kfree (mm/slub.c:6566) tipc_exit_net (net/tipc/core.c:119) cleanup_net (net/core/net_namespace.c:704) process_one_work (kernel/workqueue.c:3314) kthread (kernel/kthread.c:436) This is the same class of bug that commit e279024617134 ("net/tipc: fix slab-use-after-free Read in tipc_aead_encrypt_done") fixed for the encrypt side. The encrypt path takes maybe_get_net(aead->crypto->net) before crypto_aead_encrypt() and drops it with put_net() on the synchronous return paths and in tipc_aead_encrypt_done(); the -EINPROGRESS/-EBUSY return keeps the reference for the async callback to release. The decrypt path was left without the equivalent guard. Mirror the encrypt-side fix on the decrypt path: take a net reference before crypto_aead_decrypt() (failing with -ENODEV and the matching bearer put if it cannot be acquired), keep it across the -EINPROGRESS/-EBUSY async return, and drop it with put_net() on the synchronous success/error return and at the end of tipc_aead_decrypt_done(). Reproduced under KASAN on v7.1-rc7: a UDP bearer with a cluster key is flooded with crafted encrypted frames from an unknown peer (driving the cluster-key decrypt path) while the bearer's netns is repeatedly torn down. The completion must run asynchronously to outlive tipc_crypto_stop(); on x86 the stock aesni gcm(aes) now decrypts synchronously, so the async path was exercised via cryptd offload. The unguarded aead->crypto dereference in tipc_aead_decrypt_done() is the unpatched upstream path; tipc_aead_decrypt() still lacks maybe_get_net(aead->crypto->net), so the completion can outlive the free on any config where crypto_aead_decrypt() goes async. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: fc1b6d6de220 ("tipc: introduce TIPC encryption & authentication") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Alexander Lobakin Reviewed-by: Tung Nguyen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617075818.37431-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- net/tipc/crypto.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 6d3b6b89b1d1..16f1ed1f6b1b 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -941,12 +941,20 @@ static int tipc_aead_decrypt(struct net *net, struct tipc_aead *aead, goto exit; } + /* Get net to avoid freed tipc_crypto when delete namespace */ + if (!maybe_get_net(net)) { + tipc_bearer_put(b); + rc = -ENODEV; + goto exit; + } + /* Now, do decrypt */ rc = crypto_aead_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) return rc; tipc_bearer_put(b); + put_net(net); exit: kfree(ctx); @@ -984,6 +992,7 @@ static void tipc_aead_decrypt_done(void *data, int err) } tipc_bearer_put(b); + put_net(net); } static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr) From 96e7f9122aae0ed000ee321f324b812a447906d9 Mon Sep 17 00:00:00 2001 From: Daniel Zahka Date: Wed, 17 Jun 2026 03:39:49 -0700 Subject: [PATCH 4078/5214] eth: fbnic: take netif_addr_lock_bh() around rx mode address programming When __fbnic_set_rx_mode() is called from contexts other than .ndo_set_rx_mode_async(), the uc and mc addr lists are accessed without the addr lock that __hw_addr_sync_dev() and __hw_addr_unsync_dev() require. Wrap these unprotected accesses with netif_addr_lock_bh(). fbnic_clear_rx_mode() has similar issues. Fixes: eb690ef8d1c2 ("eth: fbnic: Add L2 address programming") Signed-off-by: Daniel Zahka Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617-linux-fbnic-hwaddr-v1-1-3f9f5dee7f99@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/meta/fbnic/fbnic_netdev.c | 7 ++++++- drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 4 ++++ drivers/net/ethernet/meta/fbnic/fbnic_rpc.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index dd77ab6052c8..10bf99be3f24 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -264,8 +264,11 @@ static int fbnic_set_mac(struct net_device *netdev, void *p) eth_hw_addr_set(netdev, addr->sa_data); - if (netif_running(netdev)) + if (netif_running(netdev)) { + netif_addr_lock_bh(netdev); __fbnic_set_rx_mode(fbn->fbd, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); + } return 0; } @@ -310,8 +313,10 @@ void fbnic_clear_rx_mode(struct fbnic_dev *fbd) /* Write updates to hardware */ fbnic_write_macda(fbd); + netif_addr_lock_bh(netdev); __dev_uc_unsync(netdev, NULL); __dev_mc_unsync(netdev, NULL); + netif_addr_unlock_bh(netdev); } static int fbnic_hwtstamp_get(struct net_device *netdev, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index 7e85b480203c..8b9bc9e8ea56 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -135,7 +135,9 @@ void fbnic_up(struct fbnic_net *fbn) fbnic_rss_reinit_hw(fbn->fbd, fbn); + netif_addr_lock_bh(fbn->netdev); __fbnic_set_rx_mode(fbn->fbd, &fbn->netdev->uc, &fbn->netdev->mc); + netif_addr_unlock_bh(fbn->netdev); /* Enable Tx/Rx processing */ fbnic_napi_enable(fbn); @@ -180,7 +182,9 @@ static int fbnic_fw_config_after_crash(struct fbnic_dev *fbd) } fbnic_rpc_reset_valid_entries(fbd); + netif_addr_lock_bh(fbd->netdev); __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c index fe95b6f69646..bc0f38b6a2b2 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c @@ -244,7 +244,9 @@ void fbnic_bmc_rpc_check(struct fbnic_dev *fbd) if (fbd->fw_cap.need_bmc_tcam_reinit) { fbnic_bmc_rpc_init(fbd); + netif_addr_lock_bh(fbd->netdev); __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); fbd->fw_cap.need_bmc_tcam_reinit = false; } From ac4d1caa82d487e7ed46d0597da1adc9c1a51c70 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 16 Jun 2026 14:25:56 +0100 Subject: [PATCH 4079/5214] rust: doctest: fix incorrect pattern in replacement The `-> Result<(), impl core::fmt::Debug>` string is generated by rustdoc and by adding "::" into the string it no longer finds anything, making the line useless. Remove the "::" in the pattern. Omit it in the replacement too, for consistency with upstream rustdoc. Fixes: de7cd3e4d638 ("rust: use absolute paths in macros referencing core and kernel") Signed-off-by: Gary Guo Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616132559.2245814-1-gary@kernel.org [ Added link in code comment to `rustdoc`'s 1.87 PR that fully qualified it for context. Improved comments for consistency. Reworded to drop changelog and to fix typo. - Miguel ] Signed-off-by: Miguel Ojeda --- scripts/rustdoc_test_builder.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs index f7540bcf595a..df864437cef7 100644 --- a/scripts/rustdoc_test_builder.rs +++ b/scripts/rustdoc_test_builder.rs @@ -28,7 +28,7 @@ fn main() { // // ``` // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_28_0() { - // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl ::core::fmt::Debug> { + // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl core::fmt::Debug> { // ``` // // It should be unlikely that doctest code matches such lines (when code is formatted properly). @@ -47,12 +47,16 @@ fn main() { }) .expect("No test function found in `rustdoc`'s output."); - // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude. + // Replicate `rustdoc` 1.87+ behaviour [1] by fully qualifying `Result` to avoid the collision + // with our own `Result` coming from the prelude. + // + // [1]: https://github.com/rust-lang/rust/pull/137807 + // + // TODO: Remove this when MSRV is bumped above 1.87. let body = body.replace( - &format!("{rustdoc_function_name}() -> Result<(), impl ::core::fmt::Debug> {{"), - &format!( - "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{" - ), + &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"), + // This intentionally does not use absolute paths to match `rustdoc` 1.87 behaviour. + &format!("{rustdoc_function_name}() -> core::result::Result<(), impl core::fmt::Debug> {{"), ); // For tests that get generated with `Result`, like above, `rustdoc` generates an `unwrap()` on From 191f49f1e38b1c10eb44b0f967c6175c884ef7db Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 16 Jun 2026 12:30:38 +0000 Subject: [PATCH 4080/5214] rust: Kbuild: set frame-pointer llvm module flag for CONFIG_FRAME_POINTER Due to a rustc bug, the -Cforce-frame-pointers=y flag only emits the frame-pointer annotation for functions, but not for the module. This means that functions generated by the LLVM backend such as 'asan.module_ctor' do not receive the frame-pointer annotation. This is likely to lead to broken backtraces and may also cause issues with ftrace if these features are used with functions generated by the LLVM backend. Thus, use -Zllvm_module_flag to work around this rustc bug if using a rustc without the fix. [ The fix [1] has landed for Rust 1.98.0 (expected release on 2026-08-20). - Miguel ] Cc: stable@vger.kernel.org # 6.12.y and later (flag not available in pinned Rust in older LTSs). Fixes: 2f7ab1267dc9 ("Kbuild: add Rust support") Link: https://github.com/rust-lang/rust/pull/156980 [1] Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260616-frame-ptr-fix-v1-1-dc6b29a631d9@google.com [ - Adjusted Cc: stable@ as discussed. - Added comment with link to the PR, similar to what we did in commit ac35b5580ace ("rust: arm64: set uwtable llvm module flag for CONFIG_UNWIND_TABLES"). - Miguel ] Signed-off-by: Miguel Ojeda --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index d33a7cadd237..82b2d8cd6804 100644 --- a/Makefile +++ b/Makefile @@ -966,6 +966,9 @@ KBUILD_CFLAGS += $(stackp-flags-y) ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y +# Work around rustc bug on compilers without +# https://github.com/rust-lang/rust/pull/156980. +KBUILD_RUSTFLAGS += $(if $(call rustc-min-version,109800),,-Zllvm_module_flag=frame-pointer:u32:2:max) else # Some targets (ARM with Thumb2, for example), can't be built with frame # pointers. For those, we don't have FUNCTION_TRACER automatically From f199c8a8bdd54296d3458777e70fe82a78bd9817 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Fri, 19 Jun 2026 01:00:10 +0800 Subject: [PATCH 4081/5214] ALSA: usb-audio: Kill MIDI 2.0 URBs before freeing endpoints MIDI 2.0 input URBs are started during snd_usb_midi_v2_create(). A later setup failure can still jump to snd_usb_midi_v2_free(), which currently frees each endpoint and its coherent URB buffers without first stopping the submitted URBs. A completion can then dereference the embedded URB context and endpoint state after they have been freed, or try to resubmit from the stale endpoint. This was observed as a KASAN slab-use-after-free in input_urb_complete(). The buggy scenario involves two paths, with each column showing the order within that path: probe error path: USB completion path: 1. start_input_streams() submits 1. The HCD still owns a input URBs. submitted input URB. 2. A later setup helper returns 2. input_urb_complete() runs an error. with urb->context in ep. 3. snd_usb_midi_v2_free() frees 3. The completion reads ep endpoint storage and URB buffers. state and can requeue URBs. Make the endpoint destructor follow the same teardown ordering used for disconnect when the endpoint has not already been disconnected: publish ep->disconnected, kill the URBs synchronously, and drain the endpoint before freeing URB buffers and endpoint storage. The guard avoids repeating the stop sequence after the normal snd_usb_midi_v2_disconnect_all() path, while still synchronizing the direct MIDI 2.0 create-error free path. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in input_urb_complete+0x37/0x1b0 Workqueue: usb_hub_wq hub_event RIP: 0010:_raw_spin_unlock_irq+0x2e/0x50 Read of size 8 Call trace: dump_stack_lvl+0x77/0xb0 print_report+0xce/0x5f0 input_urb_complete+0x37/0x1b0 (sound/usb/midi2.c:186) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __usb_hcd_giveback_urb+0x112/0x1d0 dummy_timer+0xaaa/0x19a0 lock_is_held_type+0x9a/0x110 __lock_acquire+0x467/0x28b0 mark_held_locks+0x40/0x70 _raw_spin_unlock_irqrestore+0x44/0x60 lockdep_hardirqs_on_prepare+0xbb/0x1a0 __hrtimer_run_queues+0x101/0x520 hrtimer_run_softirq+0xd0/0x130 handle_softirqs+0x15b/0x670 __irq_exit_rcu+0xd0/0x170 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 Fixes: d9c99876868c ("ALSA: usb-audio: Create UMP blocks from USB MIDI GTBs") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260618170010.191433-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/midi2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 04aeb9052f13..3ec633291772 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -470,6 +470,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi, static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) { list_del(&ep->list); + if (!ep->disconnected) { + ep->disconnected = 1; + kill_midi_urbs(ep, false); + drain_urb_queue(ep); + } free_midi_urbs(ep); kfree(ep); } From f56521ab6f76faeaa5a524320898835454e3bc31 Mon Sep 17 00:00:00 2001 From: Ramcharan Rajpurohit Date: Fri, 19 Jun 2026 11:54:35 +0530 Subject: [PATCH 4082/5214] ALSA: hda/realtek: Enable mute LED on HP EliteBook 840 G6 The HP EliteBook 840 G6 (PCI SSID 103c:854d) has an ALC215 codec whose mic-mute and audio-mute LEDs are wired to the same GPIOs as the already supported EliteBook 830 G6 and 840 G7. Without a matching quirk the LEDs are never registered, so the front-panel mic-mute LED stays permanently lit and does not track the mute state. Apply ALC285_FIXUP_HP_GPIO_LED, mirroring the sibling EliteBook entries. With this fixup the codec registers an "hda::micmute" LED class device and the LED correctly follows the capture-mute state. This was verified on the affected machine by forcing the same fixup at runtime via snd_sof_intel_hda_generic.hda_model=103c:8548, which made the LED work as expected. Signed-off-by: Ramcharan Rajpurohit Link: https://patch.msgid.link/20260619062435.26256-1-b23ci1032@iitj.ac.in Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d209efdb8272..2df491335a76 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6987,6 +6987,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x854d, "HP EliteBook 840 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), From 442d60df742a597dca7cca89a28a4843ce935f09 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 11 Jun 2026 09:59:27 +0200 Subject: [PATCH 4083/5214] x86/platform/geode: reference the real node of the cs5535 GPIO controller GPIO software node lookup should rely exclusively on matching the addresses of the referenced firmware nodes. Commit e5d527be7e69 ("gpio: swnode: don't use the swnode's name as the key for GPIO lookup") tried to enforce this but had to be reverted: it broke existing users who abuse the software node mechanism by creating "dummy" software nodes named after the device they want to get GPIOs from, without ever attaching them to the actual GPIO devices. Those users rely on GPIOLIB matching the label of the GPIO controller against the name of the software node rather than on a real firmware node link. Un-reverting e5d527be7e69 therefore requires converting all such users to real firmware node lookup. The geode board setup is one of them: it references the cs5535 GPIO controller through a locally-defined dummy node named "cs5535-gpio". The cs5535 MFD driver now exports the software node associated with its GPIO controller cell as cs5535_gpio_swnode. Use it as the target of the GPIO software node references in geode-common.c instead of the dummy node, so the lookup resolves by firmware node address. As the referenced node must exist at lookup time, make the cs5535 driver built-in for all boards selecting GEODE_COMMON (depend on GPIO_CS5535=y). The node is exported in the "CS5535" namespace, so import it in this module. Acked-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260611-cs5535-swnode-v3-1-2b0c517c0c03@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- arch/x86/Kconfig | 10 +++++----- arch/x86/platform/geode/geode-common.c | 12 +++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fa4f0079614c..bdad90f210e4 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -3026,7 +3026,7 @@ config GEODE_COMMON config ALIX bool "PCEngines ALIX System Support (LED setup)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON help This option enables system support for the PCEngines ALIX. @@ -3034,21 +3034,21 @@ config ALIX ALIX2/3/6 boards. However, other system specific setup should get added here. - Note: You must still enable the drivers for GPIO and LED support - (GPIO_CS5535 & LEDS_GPIO) to actually use the LEDs + Note: You must still enable the drivers for LED support (LEDS_GPIO) + to actually use the LEDs Note: You have to set alix.force=1 for boards with Award BIOS. config NET5501 bool "Soekris Engineering net5501 System Support (LEDS, GPIO, etc)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON help This option enables system support for the Soekris Engineering net5501. config GEOS bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)" - select GPIOLIB + depends on GPIO_CS5535=y select GEODE_COMMON depends on DMI help diff --git a/arch/x86/platform/geode/geode-common.c b/arch/x86/platform/geode/geode-common.c index 1843ae385e2d..679b4b07b790 100644 --- a/arch/x86/platform/geode/geode-common.c +++ b/arch/x86/platform/geode/geode-common.c @@ -9,15 +9,12 @@ #include #include #include +#include #include #include #include "geode-common.h" -static const struct software_node geode_gpiochip_node = { - .name = "cs5535-gpio", -}; - static const struct property_entry geode_gpio_keys_props[] = { PROPERTY_ENTRY_U32("poll-interval", 20), { } @@ -44,7 +41,6 @@ static const struct software_node geode_restart_key_node = { }; static const struct software_node *geode_gpio_keys_swnodes[] __initconst = { - &geode_gpiochip_node, &geode_gpio_keys_node, &geode_restart_key_node, NULL @@ -66,7 +62,7 @@ int __init geode_create_restart_key(unsigned int pin) struct platform_device *pd; int err; - geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + geode_restart_gpio_ref = SOFTWARE_NODE_REFERENCE(&cs5535_gpio_swnode, pin, GPIO_ACTIVE_LOW); err = software_node_register_node_group(geode_gpio_keys_swnodes); @@ -143,7 +139,7 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, goto err_free_names; } - gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&geode_gpiochip_node, + gpio_refs[i] = SOFTWARE_NODE_REFERENCE(&cs5535_gpio_swnode, leds[i].pin, GPIO_ACTIVE_LOW); props[i * 3 + 0] = @@ -188,3 +184,5 @@ int __init geode_create_leds(const char *label, const struct geode_led *leds, kfree(swnodes); return err; } + +MODULE_IMPORT_NS("CS5535"); From 53b3e60edb674b442b2b3bbdba484667b0f47a5d Mon Sep 17 00:00:00 2001 From: Adrian Bente Date: Thu, 28 May 2026 10:08:51 +0300 Subject: [PATCH 4084/5214] netfilter: flowtable: fix offloaded ct timeout never being extended OpenWrt has recently migrated many platforms to kernel 6.18. On the MediaTek platform, which supports hardware network offloading, WiFi connections accelerated via the WED path were observed to drop after roughly 300 seconds. After several debugging sessions, assisted by the Claude LLM, the problem was narrowed down as follows: nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded flows using: cmpxchg(&ct->timeout, expires, new_timeout); 'expires' comes from nf_ct_expires(ct) and is a relative value, while ct->timeout holds an absolute timestamp. The two are never equal, so the cmpxchg always fails and the timeout is never extended. This goes unnoticed for most flows, but a long-lived hardware (WED) offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to zero, the conntrack entry is reaped and the connection breaks. Open-code the relative value from a single READ_ONCE(ct->timeout) snapshot and compare against that same absolute snapshot in the cmpxchg, so the timeout extension actually takes effect while the datapath remains authoritative if it updates ct->timeout concurrently. Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic") Cc: stable@vger.kernel.org Suggested-by: Florian Westphal Signed-off-by: Adrian Bente Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 785d8c244a77..99c5b9d671a0 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -505,8 +505,13 @@ static u32 nf_flow_table_tcp_timeout(const struct nf_conn *ct) */ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) { - static const u32 min_timeout = 5 * 60 * HZ; - u32 expires = nf_ct_expires(ct); + static const s32 min_timeout = 5 * 60 * HZ; + u32 ct_timeout = READ_ONCE(ct->timeout); + s32 expires; + + expires = ct_timeout - nfct_time_stamp; + if (expires <= 0) /* already expired */ + return; /* normal case: large enough timeout, nothing to do. */ if (likely(expires >= min_timeout)) @@ -524,7 +529,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) if (nf_ct_is_confirmed(ct) && test_bit(IPS_OFFLOAD_BIT, &ct->status)) { u8 l4proto = nf_ct_protonum(ct); - u32 new_timeout = true; + u32 new_timeout = 1; switch (l4proto) { case IPPROTO_UDP: @@ -549,7 +554,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) */ if (new_timeout) { new_timeout += nfct_time_stamp; - cmpxchg(&ct->timeout, expires, new_timeout); + cmpxchg(&ct->timeout, ct_timeout, new_timeout); } } From e62d4192e593630f355094adc467058a05bdc935 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 18 Jun 2026 14:18:27 +0200 Subject: [PATCH 4085/5214] perf: Fix addr_filter_ranges lifetime Lee Jia Jie reported that since event::addr_filter_ranges is used under RCU, it should be RCU freed. Reported-by: Lee Jia Jie Signed-off-by: Peter Zijlstra (Intel) --- kernel/events/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 7935d5663944..c3a84c7bcaeb 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5303,6 +5303,7 @@ static void free_event_rcu(struct rcu_head *head) if (event->ns) put_pid_ns(event->ns); perf_event_free_filter(event); + kfree(event->addr_filter_ranges); kmem_cache_free(perf_event_cache, event); } @@ -5750,8 +5751,6 @@ static void __free_event(struct perf_event *event) if (event->attach_state & PERF_ATTACH_CALLCHAIN) put_callchain_buffers(); - kfree(event->addr_filter_ranges); - if (event->attach_state & PERF_ATTACH_EXCLUSIVE) exclusive_event_destroy(event); From c9c9b37f8c5505224e8d206184df3bb668ee00cf Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Mon, 8 Jun 2026 13:43:44 +0800 Subject: [PATCH 4086/5214] netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force(). At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins. Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed. Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection. This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic. Fixes: 34666d467cbf ("netfilter: bridge: move br_netfilter out of the core") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Haoze Xie Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nf_queue.c | 14 ++++++++++++++ net/netfilter/nfnetlink_queue.c | 3 +++ 3 files changed, 18 insertions(+) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 3978c3174cdb..fc3e81c07364 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -18,6 +18,7 @@ struct nf_queue_entry { unsigned int id; unsigned int hook_index; /* index in hook_entries->hook[] */ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + struct net_device *bridge_dev; struct net_device *physin; struct net_device *physout; #endif diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 57b450024a99..73363ceedebe 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -68,6 +68,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) nf_queue_sock_put(state->sk); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_put(entry->bridge_dev); dev_put(entry->physin); dev_put(entry->physout); #endif @@ -84,6 +85,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) { #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) const struct sk_buff *skb = entry->skb; + struct dst_entry *dst = skb_dst(skb); + struct net_device *dev = NULL; if (nf_bridge_info_exists(skb)) { entry->physin = nf_bridge_get_physindev(skb, entry->state.net); @@ -92,6 +95,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) entry->physin = NULL; entry->physout = NULL; } + + if (entry->state.pf == NFPROTO_BRIDGE && + dst && (dst->flags & DST_FAKE_RTABLE)) + dev = dst_dev_rcu(dst); + + /* Must hold a reference on the bridge device: dst_hold() protects + * the dst itself, but the fake rtable is embedded in bridge-private + * storage that netdevice teardown can free independently. + */ + entry->bridge_dev = dev; #endif } @@ -108,6 +121,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) dev_hold(state->out); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_hold(entry->bridge_dev); dev_hold(entry->physin); dev_hold(entry->physout); #endif diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index c5e29fec419b..80ca077b81bd 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1262,6 +1262,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) if (physinif == ifindex || physoutif == ifindex) return 1; + + if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) + return 1; #endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) return 1; From 5feba91006ec92da57acc1cc2e34df623b98541e Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Thu, 11 Jun 2026 15:21:42 +0800 Subject: [PATCH 4087/5214] netfilter: xt_cluster: reject template conntracks in hash match xt_cluster_mt() treats any non-NULL nf_ct_get() result as a fully initialized conntrack and passes it to xt_cluster_hash(). This causes a state confusion bug when the raw table CT target attaches a template conntrack to skb->_nfct before normal conntrack processing. Templates carry IPS_TEMPLATE status but do not have a valid tuple for hashing yet, so xt_cluster_hash() can hit its WARN_ON() path on the zeroed l3num field. Reject template conntracks before hashing them. This matches existing netfilter handling for template objects and avoids hashing incomplete conntrack state. Fixes: 0269ea493734 ("netfilter: xtables: add cluster match") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 908fd5f2c3c8..eaf2511d63f0 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -107,7 +107,7 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) } ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) + if (!ct || nf_ct_is_template(ct)) return false; if (ct->master) From f4c2d8668d85ed125985da663c824a9c25498257 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 15 Jun 2026 11:18:25 +0200 Subject: [PATCH 4088/5214] netfilter: flowtable: fix and simplify IP6IP6 tunnel handling Fix nf_flow_ip6_tunnel_proto() to use pskb_may_pull() instead of skb_header_pointer() to ensure the outer IPv6 header is in the skb headroom, which is required for subsequent packet processing. Move ctx->offset update inside the IPPROTO_IPV6 conditional block since it should only be adjusted when an IP6IP6 tunnel is actually detected. Simplify the rx path by removing ipv6_skip_exthdr() and checking ip6h->nexthdr directly, as the flowtable fast path only handles simple IP6IP6 encapsulation without extension headers. Drop the tunnel encapsulation limit destination option support from the tx path to match, since the rx path no longer handles extension headers. Remove the encap_limit parameter from nf_flow_offload_ipv6_forward(), nf_flow_tunnel_ip6ip6_push() and nf_flow_tunnel_v6_push(), along with the ipv6_tel_txoption struct and related headroom/MTU adjustments. Fixes: d98103575dcdd ("netfilter: flowtable: Add IP6IP6 rx sw acceleration") Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso --- net/ipv6/ip6_tunnel.c | 7 ++ net/netfilter/nf_flow_table_ip.c | 80 +++++-------------- .../selftests/net/netfilter/nft_flowtable.sh | 8 +- 3 files changed, 30 insertions(+), 65 deletions(-) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index d7c90a8533ec..bf8e40af60b0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1851,6 +1851,13 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx, struct dst_entry *dst; int err; + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { + /* encaplimit option is currently not supported is + * sw-acceleration path. + */ + return -EOPNOTSUPP; + } + dst = ip6_route_output(dev_net(ctx->dev), NULL, &fl6); if (!dst->error) { path->type = DEV_PATH_TUN; diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 9c05a50d6013..e7a3fb2b2d94 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -347,29 +347,23 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx, struct sk_buff *skb) { #if IS_ENABLED(CONFIG_IPV6) - struct ipv6hdr *ip6h, _ip6h; - __be16 frag_off; - u8 nexthdr; - int hdrlen; + struct ipv6hdr *ip6h; - ip6h = skb_header_pointer(skb, ctx->offset, sizeof(*ip6h), &_ip6h); - if (!ip6h) + if (!pskb_may_pull(skb, sizeof(*ip6h) + ctx->offset)) return false; + ip6h = (struct ipv6hdr *)(skb_network_header(skb) + ctx->offset); if (ip6h->hop_limit <= 1) return false; - nexthdr = ip6h->nexthdr; - hdrlen = ipv6_skip_exthdr(skb, sizeof(*ip6h) + ctx->offset, &nexthdr, - &frag_off); - if (hdrlen < 0) + if (ipv6_ext_hdr(ip6h->nexthdr)) return false; - if (nexthdr == IPPROTO_IPV6) { - ctx->tun.hdr_size = hdrlen; - ctx->tun.proto = IPPROTO_IPV6; + if (ip6h->nexthdr == IPPROTO_IPV6) { + ctx->tun.proto = ip6h->nexthdr; + ctx->tun.hdr_size = sizeof(*ip6h); + ctx->offset += ctx->tun.hdr_size; } - ctx->offset += ctx->tun.hdr_size; return true; #else @@ -648,25 +642,19 @@ static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, return 0; } -struct ipv6_tel_txoption { - struct ipv6_txoptions ops; - __u8 dst_opt[8]; -}; - static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - struct in6_addr **ip6_daddr, - int encap_limit) + struct in6_addr **ip6_daddr) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); - u8 hop_limit = ip6h->hop_limit, proto = IPPROTO_IPV6; struct rtable *rt = dst_rtable(tuple->dst_cache); __u8 dsfield = ipv6_get_dsfield(ip6h); struct flowi6 fl6 = { .daddr = tuple->tun.src_v6, .saddr = tuple->tun.dst_v6, - .flowi6_proto = proto, + .flowi6_proto = IPPROTO_IPV6, }; + u8 hop_limit = ip6h->hop_limit; int err, mtu; u32 headroom; @@ -674,41 +662,18 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, if (err) return err; - skb_set_inner_ipproto(skb, proto); + skb_set_inner_ipproto(skb, IPPROTO_IPV6); headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len; - if (encap_limit) - headroom += 8; err = skb_cow_head(skb, headroom); if (err) return err; skb_scrub_packet(skb, true); mtu = dst_mtu(&rt->dst) - sizeof(*ip6h); - if (encap_limit) - mtu -= 8; mtu = max(mtu, IPV6_MIN_MTU); skb_dst_update_pmtu_no_confirm(skb, mtu); - if (encap_limit > 0) { - struct ipv6_tel_txoption opt = { - .dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT, - .dst_opt[3] = 1, - .dst_opt[4] = encap_limit, - .dst_opt[5] = IPV6_TLV_PADN, - .dst_opt[6] = 1, - }; - struct ipv6_opt_hdr *hopt; - - opt.ops.dst1opt = (struct ipv6_opt_hdr *)opt.dst_opt; - opt.ops.opt_nflen = 8; - - hopt = skb_push(skb, ipv6_optlen(opt.ops.dst1opt)); - memcpy(hopt, opt.ops.dst1opt, ipv6_optlen(opt.ops.dst1opt)); - hopt->nexthdr = IPPROTO_IPV6; - proto = NEXTHDR_DEST; - } - skb_push(skb, sizeof(*ip6h)); skb_reset_network_header(skb); @@ -716,7 +681,7 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, ip6_flow_hdr(ip6h, dsfield, ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6)); ip6h->hop_limit = hop_limit; - ip6h->nexthdr = proto; + ip6h->nexthdr = IPPROTO_IPV6; ip6h->daddr = tuple->tun.src_v6; ip6h->saddr = tuple->tun.dst_v6; ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h)); @@ -729,12 +694,10 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - struct in6_addr **ip6_daddr, - int encap_limit) + struct in6_addr **ip6_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr, - encap_limit); + return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr); return 0; } @@ -1089,7 +1052,7 @@ static int nf_flow_tuple_ipv6(struct nf_flowtable_ctx *ctx, struct sk_buff *skb, static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, struct nf_flowtable *flow_table, struct flow_offload_tuple_rhash *tuplehash, - struct sk_buff *skb, int encap_limit) + struct sk_buff *skb) { enum flow_offload_tuple_dir dir; struct flow_offload *flow; @@ -1100,11 +1063,8 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset; - if (flow->tuplehash[!dir].tuple.tun_num) { + if (flow->tuplehash[!dir].tuple.tun_num) mtu -= sizeof(*ip6h); - if (encap_limit > 0) - mtu -= 8; /* encap limit option */ - } if (unlikely(nf_flow_exceeds_mtu(skb, mtu))) return 0; @@ -1158,7 +1118,6 @@ unsigned int nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - int encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; struct flow_offload_tuple_rhash *tuplehash; struct nf_flowtable *flow_table = priv; struct flow_offload_tuple *other_tuple; @@ -1177,8 +1136,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, if (tuplehash == NULL) return NF_ACCEPT; - ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb, - encap_limit); + ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb); if (ret < 0) return NF_DROP; else if (ret == 0) @@ -1198,7 +1156,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ip6_daddr = &other_tuple->src_v6; if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, - &ip6_daddr, encap_limit) < 0) + &ip6_daddr) < 0) return NF_DROP; switch (tuplehash->tuple.xmit_type) { diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index 7a34ef468975..08ad07500e8a 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -592,7 +592,7 @@ ip -net "$nsr1" link set tun0 up ip -net "$nsr1" addr add 192.168.100.1/24 dev tun0 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null -ip -net "$nsr1" link add name tun6 type ip6tnl local fee1:2::1 remote fee1:2::2 +ip -net "$nsr1" link add name tun6 type ip6tnl local fee1:2::1 remote fee1:2::2 encaplimit none ip -net "$nsr1" link set tun6 up ip -net "$nsr1" addr add fee1:3::1/64 dev tun6 nodad @@ -601,7 +601,7 @@ ip -net "$nsr2" link set tun0 up ip -net "$nsr2" addr add 192.168.100.2/24 dev tun0 ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null -ip -net "$nsr2" link add name tun6 type ip6tnl local fee1:2::2 remote fee1:2::1 || ret=1 +ip -net "$nsr2" link add name tun6 type ip6tnl local fee1:2::2 remote fee1:2::1 encaplimit none || ret=1 ip -net "$nsr2" link set tun6 up ip -net "$nsr2" addr add fee1:3::2/64 dev tun6 nodad @@ -651,7 +651,7 @@ ip -net "$nsr1" route change default via 192.168.200.2 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept' -ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 +ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none ip -net "$nsr1" link set tun6.10 up ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad ip -6 -net "$nsr1" route delete default @@ -670,7 +670,7 @@ ip -net "$nsr2" addr add 192.168.200.2/24 dev tun0.10 ip -net "$nsr2" route change default via 192.168.200.1 ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null -ip -net "$nsr2" link add name tun6.10 type ip6tnl local fee1:4::2 remote fee1:4::1 || ret=1 +ip -net "$nsr2" link add name tun6.10 type ip6tnl local fee1:4::2 remote fee1:4::1 encaplimit none || ret=1 ip -net "$nsr2" link set tun6.10 up ip -net "$nsr2" addr add fee1:5::2/64 dev tun6.10 nodad ip -6 -net "$nsr2" route delete default From b3d4ab2d7df9426f7f1d3671d7e2108f2ca6e970 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 15 Jun 2026 17:39:12 +0200 Subject: [PATCH 4089/5214] s390/pkey: Check length in PKEY_VERIFYPROTK ioctl Explicitly check the buffer length request structure provided by user-space and fail, if it exceeds the buffer size. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_api.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index d6b595eb3370..28e1007005f2 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -334,6 +334,13 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) if (copy_from_user(&kvp, uvp, sizeof(kvp))) return -EFAULT; + if (kvp.protkey.len > sizeof(kvp.protkey.protkey)) { + PKEY_DBF_ERR("%s protkey length %u exceeds protkey buffer size\n", + __func__, kvp.protkey.len); + memzero_explicit(&kvp, sizeof(kvp)); + return -EINVAL; + } + keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); if (!keytype) { PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", From 1ac287e2af9a9112fe271427ef45eceb26bce8b4 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 17 Jun 2026 19:06:39 +0200 Subject: [PATCH 4090/5214] s390/pkey: Check length in pkey_pckmo handler implementation Explicitly check the length of the target buffer in the pkey_pckmo implementation of the key_to_protkey() handler function. The handler function fails, if the generated output data exceeds the length of the provided target buffer. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_pckmo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/s390/crypto/pkey_pckmo.c b/drivers/s390/crypto/pkey_pckmo.c index ea774ab89180..0cc015cc9f6b 100644 --- a/drivers/s390/crypto/pkey_pckmo.c +++ b/drivers/s390/crypto/pkey_pckmo.c @@ -257,6 +257,10 @@ static int pckmo_key2protkey(const u8 *key, u32 keylen, goto out; break; } + if (t->len > *protkeylen) { + rc = -EINVAL; + goto out; + } memcpy(protkey, t->protkey, t->len); *protkeylen = t->len; *protkeytype = t->keytype; From 998d4d789d2d625e1d8c77c1b6c3951e30e81bb0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:21:44 +0200 Subject: [PATCH 4091/5214] arm64: static_call: include asm/insns.h I came a cross a missing declaration in a randconfig build: arch/arm64/kernel/static_call.c:16:5: error: call to undeclared function 'aarch64_insn_adrp_get_offset'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 16 | aarch64_insn_adrp_get_offset(le32_to_cpup(tramp + 4)) + | ^ Include the header that contains this definition explicitly, rather than relying on it to come indirectly through another header. Fixes: 54ac9ff8f119 ("arm64: Use static call trampolines when kCFI is enabled") Signed-off-by: Arnd Bergmann Signed-off-by: Will Deacon --- arch/arm64/kernel/static_call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/static_call.c b/arch/arm64/kernel/static_call.c index 8b3a19e10871..c126edced022 100644 --- a/arch/arm64/kernel/static_call.c +++ b/arch/arm64/kernel/static_call.c @@ -2,6 +2,7 @@ #include #include #include +#include void arch_static_call_transform(void *site, void *tramp, void *func, bool tail) { From 4cc70f75853bebac022334b6a86b953348072f74 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 9 Jun 2026 06:15:53 -0700 Subject: [PATCH 4092/5214] arm64/hw_breakpoint: reject unaligned watchpoints that would truncate BAS hw_breakpoint_arch_parse() positions the BAS bit pattern in hw->ctrl.len with offset = hw->address & alignment_mask; /* 0..7 */ hw->ctrl.len <<= offset; ctrl.len is an 8-bit bitfield (struct arch_hw_breakpoint_ctrl::len is u32 :8), so the shift silently drops any bits past bit 7. For non-compat AArch64 watchpoints the offset is unbounded relative to ctrl.len: a perf_event_open(PERF_TYPE_BREAKPOINT) caller asking for HW_BREAKPOINT_W with bp_addr=page+1 and bp_len=HW_BREAKPOINT_LEN_8 ends up with 0xff << 1 = 0x1fe, stored as 0xfe. The kernel programs WCR.BAS=0xfe and the hardware watches bytes [1..7] instead of the requested [1..8] -- the eighth byte is silently dropped. The syscall still returns success, leaving userspace to discover the gap by empirical probing. The same class affects HW_BREAKPOINT_LEN_{2,4} when offset pushes the high BAS bit past bit 7 (e.g. LEN_4 with offset=5 yields 0xe0 instead of 0x1e0). No memory-safety impact -- the value is masked into 8 bits before encoding -- but debuggers and perf users observe missed events on bytes they thought they were watching. The AArch32 branch immediately above already rejects unrepresentable (offset, len) combinations via an explicit switch. Mirror that for the non-compat branch by checking that the shifted pattern fits in the BAS field, returning -EINVAL when it does not. GDB and similar debuggers are unaffected by the stricter check. aarch64_linux_set_debug_regs() already treats EINVAL on NT_ARM_HW_WATCH as a downgrade signal: it clears kernel_supports_any_contiguous_range, calls aarch64_downgrade_regs() to round the BAS up to a legacy 0x01/03/0f/ff mask with an aligned base, and retries -- the same fallback path that PR-20207 introduced. The new -EINVAL is therefore reachable only from a raw perf_event_open() that pairs an unaligned base with an oversized bp_len, which is precisely the bug. Reproducer: struct perf_event_attr a = { .type = PERF_TYPE_BREAKPOINT, .size = sizeof(a), .bp_type = HW_BREAKPOINT_W, .bp_addr = (uintptr_t)(buf + 1), .bp_len = HW_BREAKPOINT_LEN_8, .exclude_kernel = 1, .exclude_hv = 1, }; int fd = perf_event_open(&a, 0, -1, -1, 0); /* before this fix: succeeds, watches 7 bytes (buf+1..buf+7) */ /* after this fix: fails with EINVAL */ Fixes: b08fb180bb88 ("arm64: Allow hw watchpoint at varied offset from base address") Signed-off-by: Breno Leitao Signed-off-by: Will Deacon --- arch/arm64/kernel/hw_breakpoint.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ab76b36dce82..73cce8ac8368 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -559,6 +559,15 @@ int hw_breakpoint_arch_parse(struct perf_event *bp, else alignment_mask = 0x7; offset = hw->address & alignment_mask; + + /* + * BAS is an 8-bit field in WCR/BCR; the shift below would + * silently drop the high bits of ctrl.len when offset + len + * exceeds 8, programming hardware to watch fewer bytes than + * the user requested. + */ + if (((u32)hw->ctrl.len << offset) > ARM_BREAKPOINT_LEN_8) + return -EINVAL; } hw->address &= ~alignment_mask; From 07f251e0ed0b78591114101b3ce16db2e1365171 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Fri, 19 Jun 2026 07:34:42 -0700 Subject: [PATCH 4093/5214] spi: dt-bindings: snps,dw-apb-ssi: Add starfive,jhb100-spi Add a new compatible string "starfive,jhb100-spi" for the StarFive JHB100 SPI, it based on the Synopsys DesignWare SSI version 2.00a, uses snps,dwc-ssi-2.00a as the primary fallback and snps,dwc-ssi-1.01a as the secondary fallback. Signed-off-by: Changhuang Liang Acked-by: Conor Dooley Link: https://patch.msgid.link/20260619143443.22267-2-changhuang.liang@starfivetech.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 8ebebcebca16..4458316326fc 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -54,6 +54,12 @@ properties: - sophgo,sg2042-spi - thead,th1520-spi - const: snps,dw-apb-ssi + - description: Vendor controllers which use snps,dwc-ssi-2.00a as fallback + items: + - enum: + - starfive,jhb100-spi + - const: snps,dwc-ssi-2.00a + - const: snps,dwc-ssi-1.01a - description: Intel Keem Bay SPI Controller const: intel,keembay-ssi - description: Intel Mount Evans Integrated Management Complex SPI Controller From 914e708e3049c9e0be46533406abd832a46c6e8d Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Fri, 19 Jun 2026 07:34:43 -0700 Subject: [PATCH 4094/5214] spi: dw: Add support for snps,dwc-ssi-2.00a Add a new compatible entry "snps,dwc-ssi-2.00a" for the Synopsys DesignWare SSI controller version 2.00a. This variant uses the same initialization routine as snps,dwc-ssi-1.01a (dw_spi_hssi_init). Signed-off-by: Changhuang Liang Link: https://patch.msgid.link/20260619143443.22267-3-changhuang.liang@starfivetech.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 4fc864d38cff..603e81a92c57 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -438,6 +438,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init}, + { .compatible = "snps,dwc-ssi-2.00a", .data = dw_spi_hssi_init}, { .compatible = "intel,keembay-ssi", .data = dw_spi_hssi_no_dma_init}, { .compatible = "intel,mountevans-imc-ssi", From fbef4191b4961c125585c715407e693f7d0024a9 Mon Sep 17 00:00:00 2001 From: Joy Zou Date: Thu, 18 Jun 2026 10:03:05 +0800 Subject: [PATCH 4095/5214] regulator: pca9450: Correct default t_off_deb for PCA9451A/PCA9452 The PMIC PCA9451A and PCA9452 have a default power-off debounce time of 2ms according to their datasheet, while PCA9450A and PCA9450BC use 120us. Add default_t_off_deb field to struct pca9450 to support per-variant default configuration when the device tree property is not specified. Datasheet reference links: - PCA9451A Rev.2.1: https://www.nxp.com/docs/en/data-sheet/PCA9451A.pdf - PCA9452 Rev.1.0: https://www.nxp.com/docs/en/data-sheet/PCA9452.pdf Signed-off-by: Joy Zou Reviewed-by: Frank Li Link: https://patch.msgid.link/20260618-b4-regulator-opt-v1-1-c43b1f62aaf6@oss.nxp.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 45d7dc44c2cd..c41db70fa052 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -44,6 +44,7 @@ struct pca9450 { unsigned int rcnt; int irq; bool sd_vsel_fixed_low; + int default_t_off_deb; }; static const struct regmap_range pca9450_status_range = { @@ -1209,7 +1210,7 @@ static int pca9450_of_init(struct pca9450 *pca9450) ret = of_property_read_u32(i2c->dev.of_node, "nxp,pmic-on-req-off-debounce-us", &val); if (ret == -EINVAL) - t_off_deb = T_OFF_DEB_120US; + t_off_deb = pca9450->default_t_off_deb; else if (ret) return ret; else { @@ -1304,21 +1305,25 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) case PCA9450_TYPE_PCA9450A: regulator_desc = pca9450a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9450a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_120US; type_name = "pca9450a"; break; case PCA9450_TYPE_PCA9450BC: regulator_desc = pca9450bc_regulators; pca9450->rcnt = ARRAY_SIZE(pca9450bc_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_120US; type_name = "pca9450bc"; break; case PCA9450_TYPE_PCA9451A: regulator_desc = pca9451a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9451a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_2MS; type_name = "pca9451a"; break; case PCA9450_TYPE_PCA9452: regulator_desc = pca9451a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9451a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_2MS; type_name = "pca9452"; break; default: From b91d287fa7a1ba0727eed5823c6ee4924ee5fa31 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Sat, 13 Jun 2026 15:17:47 -0700 Subject: [PATCH 4096/5214] thermal: intel: Fix dangling resources on thermal_throttle_online() failure The function thermal_throttle_add_dev() may fail and abort a CPU hotplug online operation. Since the failure occurs within the online callback, thermal_throttle_online(), the CPU hotplug framework does not invoke the corresponding offline callback. As a result, the hardware and software resources set up during the failed operation are not torn down. Since only thermal_throttle_add_dev() can fail, call it before setting up the rest of the resources. Fixes: f6656208f04e ("x86/mce/therm_throt: Optimize notifications of thermal throttle") Signed-off-by: Ricardo Neri Link: https://patch.msgid.link/20260613-rneri-directed-therm-intr-v3-1-3a26d1e47fc8@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/therm_throt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 44fa4dd15dd1..45a8ef4a608b 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -529,8 +529,13 @@ static int thermal_throttle_online(unsigned int cpu) { struct thermal_state *state = &per_cpu(thermal_state, cpu); struct device *dev = get_cpu_device(cpu); + int err; u32 l; + err = thermal_throttle_add_dev(dev, cpu); + if (err) + return err; + state->package_throttle.level = PACKAGE_LEVEL; state->core_throttle.level = CORE_LEVEL; @@ -548,7 +553,7 @@ static int thermal_throttle_online(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); - return thermal_throttle_add_dev(dev, cpu); + return err; } static int thermal_throttle_offline(unsigned int cpu) From b2b42ad22828da9cdb876eedb8914134e0759355 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Thu, 11 Jun 2026 22:25:18 +0800 Subject: [PATCH 4097/5214] ACPI: sysfs: Fix path of module parameters in comments The correct path of module parameters should be /sys/module/acpi/parameters/xxx. Fix them. Signed-off-by: Zenghui Yu Link: https://patch.msgid.link/20260611142518.77343-1-zenghui.yu@linux.dev Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sysfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index a625de3c3c8b..908cc5c7e643 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -17,12 +17,12 @@ #ifdef CONFIG_ACPI_DEBUG /* * ACPI debug sysfs I/F, including: - * /sys/modules/acpi/parameters/debug_layer - * /sys/modules/acpi/parameters/debug_level - * /sys/modules/acpi/parameters/trace_method_name - * /sys/modules/acpi/parameters/trace_state - * /sys/modules/acpi/parameters/trace_debug_layer - * /sys/modules/acpi/parameters/trace_debug_level + * /sys/module/acpi/parameters/debug_layer + * /sys/module/acpi/parameters/debug_level + * /sys/module/acpi/parameters/trace_method_name + * /sys/module/acpi/parameters/trace_state + * /sys/module/acpi/parameters/trace_debug_layer + * /sys/module/acpi/parameters/trace_debug_level */ struct acpi_dlayer { @@ -269,7 +269,7 @@ module_param_call(trace_state, param_set_trace_state, param_get_trace_state, #endif /* CONFIG_ACPI_DEBUG */ -/* /sys/modules/acpi/parameters/aml_debug_output */ +/* /sys/module/acpi/parameters/aml_debug_output */ module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object, byte, 0644); From 78ad5c7722b7bed9d35ffc5b45eb0f12e2c22fee Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Jun 2026 11:05:55 +0200 Subject: [PATCH 4098/5214] ACPI: resource: Amend kernel-doc style The functions are referred as func() in the kernel-doc. The % (percent) character makes the rendering for constants as described in the respective documentation. Amend all these. Fixes: 8e345c991c8c ("ACPI: Centralized processing of ACPI device resources") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260617090555.2648709-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index bc8050d8a6f5..56df4599d360 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -871,7 +871,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); /** - * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * acpi_dev_free_resource_list - Free resource from acpi_dev_get_resources(). * @list: The head of the resource list to free. */ void acpi_dev_free_resource_list(struct list_head *list) @@ -991,7 +991,7 @@ static int __acpi_dev_get_resources(struct acpi_device *adev, * * The resultant struct resource objects are put on the list pointed to by * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * objects. Callers of this routine should use acpi_dev_free_resource_list() to * free that list. * * The number of resources in the output list is returned on success, an error @@ -1032,7 +1032,7 @@ static int is_memory(struct acpi_resource *ares, void *not_used) * The resultant struct resource objects are put on the list pointed to * by @list, that must be empty initially, as members of struct * resource_entry objects. Callers of this routine should use - * %acpi_dev_free_resource_list() to free that list. + * acpi_dev_free_resource_list() to free that list. * * The number of resources in the output list is returned on success, * an error code reflecting the error condition is returned otherwise. From 71b57aca295d61276a60e131d8f62b0cc7cf1a35 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 17:36:21 +0800 Subject: [PATCH 4099/5214] ACPI: IPMI: Fix inverted interface check in ipmi_bmc_gone() Before commit a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user"), ipmi_bmc_gone() skipped entries whose interface number did not match the SMI being removed, then killed the matching entry: if (ipmi_device->ipmi_ifnum != iface) continue; __ipmi_dev_kill(ipmi_device); That commit folded the removal block into the existing non-match test while converting the object lifetime handling, but left the comparison unchanged. The old != meant "continue past this entry"; after the refactor it meant "kill this entry". As a result, a single ACPI IPMI interface is never removed when its SMI disappears. If multiple interfaces are tracked, the first interface whose number differs from iface is removed instead, while the interface that actually disappeared remains on driver_data.ipmi_devices. The stale entry is not marked dead and can continue to be selected for ACPI IPMI transactions. It can also prevent the same ACPI handle from being registered again. Change the comparison to == so ipmi_bmc_gone() removes exactly the interface reported as gone by the SMI watcher. This restores the pre-a1a69b297e47 behavior and is the correct interface matching logic. Fixes: a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user") Signed-off-by: Xu Rao Link: https://patch.msgid.link/B486593E06E6F6E0+20260616093621.1039943-1-raoxu@uniontech.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_ipmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 79ce6e72bf29..2dbed92d54b3 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -490,7 +490,7 @@ static void ipmi_bmc_gone(int iface) mutex_lock(&driver_data.ipmi_lock); list_for_each_entry_safe(iter, temp, &driver_data.ipmi_devices, head) { - if (iter->ipmi_ifnum != iface) { + if (iter->ipmi_ifnum == iface) { ipmi_device = iter; __ipmi_dev_kill(iter); break; From a2e06b4bef20b59446d5088e938c2be53cc4e6c6 Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:32 +0300 Subject: [PATCH 4100/5214] ieee802154: Restore initial state on failed device_rename() in cfg802154_switch_netns() Currently, the return value of device_rename() is not acted upon. To avoid an inconsistent state in case of failure, roll back the changes made before the device_rename() call. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-2-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt --- net/ieee802154/core.c | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 89b671b12600..84d514430e45 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -233,31 +233,36 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = true; } - if (err) { - /* failed -- clean up to old netns */ - net = wpan_phy_net(&rdev->wpan_phy); - - list_for_each_entry_continue_reverse(wpan_dev, - &rdev->wpan_dev_list, - list) { - if (!wpan_dev->netdev) - continue; - wpan_dev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wpan_dev->netdev, net, - "wpan%d"); - WARN_ON(err); - wpan_dev->netdev->netns_immutable = true; - } - - return err; - } - - wpan_phy_net_set(&rdev->wpan_phy, net); + if (err) + goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); WARN_ON(err); + if (err) + goto errout; + + wpan_phy_net_set(&rdev->wpan_phy, net); + return 0; + +errout: + /* failed -- clean up to old netns */ + net = wpan_phy_net(&rdev->wpan_phy); + + list_for_each_entry_continue_reverse(wpan_dev, + &rdev->wpan_dev_list, + list) { + if (!wpan_dev->netdev) + continue; + wpan_dev->netdev->netns_immutable = false; + err = dev_change_net_namespace(wpan_dev->netdev, net, + "wpan%d"); + WARN_ON(err); + wpan_dev->netdev->netns_immutable = true; + } + + return err; } void cfg802154_dev_free(struct cfg802154_registered_device *rdev) From 0569f67ed6a7af838e2141da93c68e6b6013f483 Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:33 +0300 Subject: [PATCH 4101/5214] ieee802154: Avoid calling WARN_ON() on -ENOMEM in cfg802154_switch_netns() It's pointless to call WARN_ON() in case of an allocation failure in dev_change_net_namespace() and device_rename(), since it only leads to useless splats caused by deliberate fault injections, so avoid it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reported-by: syzbot+e0bd4e4815a910c0daa8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/000000000000f4a1b7061f9421de@google.com/#t Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-3-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt --- net/ieee802154/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 84d514430e45..987c633e2c54 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -228,8 +228,10 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, continue; wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - if (err) + if (err) { + WARN_ON(err && err != -ENOMEM); break; + } wpan_dev->netdev->netns_immutable = true; } @@ -237,7 +239,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); if (err) goto errout; @@ -258,7 +260,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); wpan_dev->netdev->netns_immutable = true; } From e69ed6fc9fb3b386b5fcdb9f51623f122cee2ebd Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:34 +0300 Subject: [PATCH 4102/5214] ieee802154: Remove WARN_ON() in cfg802154_pernet_exit() There's no need to call WARN_ON() in cfg802154_pernet_exit(), since every point of failure in cfg802154_switch_netns() is covered with WARN_ON(), so remove it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-4-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt --- net/ieee802154/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 987c633e2c54..c0b8712018a1 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -358,7 +358,7 @@ static void __net_exit cfg802154_pernet_exit(struct net *net) rtnl_lock(); list_for_each_entry(rdev, &cfg802154_rdev_list, list) { if (net_eq(wpan_phy_net(&rdev->wpan_phy), net)) - WARN_ON(cfg802154_switch_netns(rdev, &init_net)); + cfg802154_switch_netns(rdev, &init_net); } rtnl_unlock(); } From de3ab9bd3133899efb92e4cd05ba4203e58fc0a3 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 16 Jun 2026 16:38:17 -0400 Subject: [PATCH 4103/5214] sched/mmcid: Fix OOB clear_bit when CID is MM_CID_UNSET in fixup path In mm_cid_fixup_cpus_to_tasks(), when rq->curr has the target mm and mm_cid.active is set, the CID is checked with cid_in_transit() before setting the transition bit. In per-CPU mode a newly forked or exec'd task can be running with mm_cid.cid == MM_CID_UNSET because CIDs are assigned lazily on schedule-in. With cid_in_transit() the guard passes for MM_CID_UNSET (no transit bit), converts it to MM_CID_UNSET | MM_CID_TRANSIT and stores it back; later mm_cid_schedout() feeds this to clear_bit() with MM_CID_UNSET as the bit number, triggering an out-of-bounds write. Symptoms: this is genuine memory corruption, but a bounded out-of-bounds write, not an arbitrary one. MM_CID_UNSET is the fixed sentinel BIT(31), so once the bad value reaches mm_cid_schedout() the cid_from_transit_cid() strip leaves MM_CID_UNSET, which fails the "cid < max_cids" convergence test and falls into mm_drop_cid() -> clear_bit(MM_CID_UNSET, mm_cidmask(mm)). The cid bitmap is embedded in the mm_struct slab object (after cpu_bitmap and mm_cpus_allowed) and is only num_possible_cpus() bits wide, so clearing bit 31 is a deterministic OOB bit-clear at a fixed offset of 2^31 / 8 == 256 MiB past the bitmap base. The address is not attacker-influenced (fixed sentinel -> fixed offset) and the op only clears a single bit; what sits 256 MiB further along the direct map is whatever kernel object happens to live there, so this corrupts one bit of unpredictable kernel memory -- it is not an arbitrary-address or arbitrary-value write. It triggers only in per-CPU CID mode, when a CPU is running an active task of the target mm whose cid is still MM_CID_UNSET -- the fork()/execve() window before that task's next schedule-in assigns it a real CID -- and a per-CPU -> per-task fixup walks over it (the mode fallback driven by a thread exit, sched_mm_cid_exit(), or by the deferred max_cids recompute in mm_cid_work_fn()). In practice syzkaller surfaced it as a KASAN use-after-free reported in __schedule -> mm_cid_switch_to, where the offending clear_bit() is inlined via mm_cid_schedout() -> mm_drop_cid(). Guard the transition-bit assignment against MM_CID_UNSET, in addition to the existing cid_in_transit() check, so the bit is only set on a genuine task-owned CID. A CPU-owned (MM_CID_ONCPU) CID of a running active task is handled by the cid_on_cpu(pcp->cid) branch above and never reaches this path, so excluding MM_CID_UNSET (and the already-transitioning case) is sufficient. Fixes: fbd0e71dc370 ("sched/mmcid: Provide CID ownership mode fixup functions") Signed-off-by: Rik van Riel Signed-off-by: Thomas Gleixner Assisted-by: Claude:claude-opus-4-8 syzkaller Reviewed-by: Mathieu Desnoyers Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616203818.1516263-1-riel@surriel.com --- kernel/sched/core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 8b791e9e9f67..3cc6fb1d2054 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -10909,8 +10909,19 @@ static void mm_cid_fixup_cpus_to_tasks(struct mm_struct *mm) } else if (rq->curr->mm == mm && rq->curr->mm_cid.active) { unsigned int cid = rq->curr->mm_cid.cid; - /* Ensure it has the transition bit set */ - if (!cid_in_transit(cid)) { + /* + * Set the transition bit only on a genuine task-owned + * CID. A running active task can legitimately have + * MM_CID_UNSET here: in per-CPU mode CIDs are assigned + * lazily on schedule-in, so the fork()/execve() window + * leaves the task active with no owned CID. Setting the + * transition bit on MM_CID_UNSET would later feed + * clear_bit() an out-of-bounds bit number via + * mm_cid_schedout(), so exclude it. A CPU-owned + * (MM_CID_ONCPU) CID is handled by the cid_on_cpu() + * branch above and never reaches here. + */ + if (cid != MM_CID_UNSET && !cid_in_transit(cid)) { cid = cid_to_transit_cid(cid); rq->curr->mm_cid.cid = cid; pcp->cid = cid; From e09390e439bd7cca30dd10893b1f64802961667a Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Tue, 21 Apr 2026 13:02:59 +0530 Subject: [PATCH 4104/5214] ieee802154: ca8210: fix cas_ctl leak on spi_async failure ca8210_spi_transfer() allocates cas_ctl with kzalloc_obj(GFP_ATOMIC) and relies entirely on the SPI completion callback ca8210_spi_transfer_complete() to free it. The spi_async() API only invokes the completion callback on successful submission. On failure it returns a negative error code without ever queuing the callback, which leaves cas_ctl and its embedded spi_message and spi_transfer orphaned. Every kfree(cas_ctl) in the driver is inside the completion callback, so there is no other reclamation path. ca8210_spi_transfer() is called from ca8210_spi_exchange(), the interrupt handler ca8210_interrupt_handler(), and from the retry path inside the completion callback itself. The exchange and interrupt handler paths loop on -EBUSY, so under sustained SPI bus contention every retry iteration leaks a fresh cas_ctl (~600 bytes per occurrence). Fix it by freeing cas_ctl on the spi_async() error path. While here, correct the misleading error string: the function calls spi_async(), not spi_sync(). Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260421073259.2259783-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/ca8210.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index ed4178155a5d..bf837adfebb2 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -919,9 +919,10 @@ static int ca8210_spi_transfer( if (status < 0) { dev_crit( &spi->dev, - "status %d from spi_sync in write\n", + "status %d from spi_async in write\n", status ); + kfree(cas_ctl); } return status; From 6d7f7bcf225b2d566176bf6229dbd1252940cb3c Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Wed, 20 May 2026 16:27:50 +0530 Subject: [PATCH 4105/5214] ieee802154: ca8210: fix pointer truncation in kfifo on 64-bit ca8210_test_int_driver_write() and ca8210_test_int_user_read() exchange a kmalloc'd buffer pointer through a struct kfifo, but pass a literal '4' as the byte count to kfifo_in()/kfifo_out(). This is correct on 32-bit (pointer = 4 bytes), but on 64-bit only the low 4 bytes of the 8-byte pointer are written into the FIFO. The reader then reads back 4 bytes into an 8-byte local pointer variable, leaving the upper 4 bytes uninitialized stack data. The first dereference of the reconstructed pointer (fifo_buffer[1]) accesses an arbitrary kernel address and generally results in an oops. Use sizeof(fifo_buffer) so the byte count matches pointer width on every architecture. The driver has no architecture restriction in Kconfig, so any 64-bit build with CONFIG_IEEE802154_CA8210_DEBUGFS=y is exposed. Issue has been latent since the driver was added in 2017 because it is most commonly deployed on 32-bit MCUs. Found via a custom Coccinelle semantic patch hunting for short-byte kfifo I/O on byte-mode kfifos used to shuttle pointers. Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Simon Horman Link: https://lore.kernel.org/20260520105750.30144-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/ca8210.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index bf837adfebb2..01af4f9cf7f2 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -595,7 +595,7 @@ static int ca8210_test_int_driver_write( fifo_buffer = kmemdup(buf, len, GFP_KERNEL); if (!fifo_buffer) return -ENOMEM; - kfifo_in(&test->up_fifo, &fifo_buffer, 4); + kfifo_in(&test->up_fifo, &fifo_buffer, sizeof(fifo_buffer)); wake_up_interruptible(&priv->test.readq); return 0; @@ -2526,6 +2526,7 @@ static ssize_t ca8210_test_int_user_read( struct ca8210_priv *priv = filp->private_data; unsigned char *fifo_buffer; unsigned long bytes_not_copied; + unsigned int copied; if (filp->f_flags & O_NONBLOCK) { /* Non-blocking mode */ @@ -2539,7 +2540,8 @@ static ssize_t ca8210_test_int_user_read( ); } - if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) { + copied = kfifo_out(&priv->test.up_fifo, &fifo_buffer, sizeof(fifo_buffer)); + if (copied != sizeof(fifo_buffer)) { dev_err( &priv->spi->dev, "test_interface: Wrong number of elements popped from upstream fifo\n" From 84a04eb5b210643bd67aab81ff805d32f62aa865 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Tue, 26 May 2026 20:37:26 +0200 Subject: [PATCH 4106/5214] mac802154: llsec: add skb_cow_data() before in-place crypto llsec_do_encrypt_unauth(), llsec_do_encrypt_auth(), llsec_do_decrypt_unauth(), and llsec_do_decrypt_auth() all perform in-place cryptographic transformations on skb data. They build a scatterlist with sg_init_one() pointing into the skb's linear data area and then pass the same scatterlist as both src and dst to the crypto API (e.g. crypto_skcipher_encrypt/decrypt, crypto_aead_encrypt/decrypt). On the RX path, __ieee802154_rx_handle_packet() clones the received skb before handing it to each subscriber via ieee802154_subif_frame(). The cloned skb shares the same underlying data buffer via reference counting. When llsec_do_decrypt() subsequently modifies this shared buffer in place, it corrupts data that other clones -- potentially belonging to other sockets or subsystems -- still reference. On the TX path, similar data sharing can occur when an skb's head has been cloned (skb_cloned() returns true). The fix is to call skb_cow_data() before performing any in-place crypto operation. skb_cow_data() ensures that the skb's data area is not shared: if the skb head is cloned or the data spans multiple fragments, it copies the data into a private buffer that can be safely modified in place. This is the same pattern used by: - ESP (net/ipv4/esp4.c, net/ipv6/esp6.c) - MACsec (drivers/net/macsec.c) - WireGuard (drivers/net/wireguard/receive.c) - TIPC (net/tipc/crypto.c) Without this guard, in-place crypto on shared skb data leads to: - Silent data corruption of other skb clones - Use-after-free when the crypto API scatterwalk writes through a page that has already been freed by another clone's kfree_skb() - Kernel crashes under concurrent 802.15.4 traffic with security enabled (KASAN/KMSAN reports slab-use-after-free) Found by 0sec (https://0sec.ai) using automated source analysis. Fixes: 4c14a2fb5d14 ("mac802154: add llsec decryption method") Fixes: 03556e4d0dbb ("mac802154: add llsec encryption method") Cc: stable@vger.kernel.org Reported-by: Doruk Tan Ozturk Closes: https://lore.kernel.org/linux-wpan/20260525161806.96158-1-doruk@0sec.ai/ Reviewed-by: Alexander Lobakin Signed-off-by: Doruk Tan Ozturk Closes: Link: https://lore.kernel.org/20260526183726.56100-1-doruk@0sec.ai Signed-off-by: Stefan Schmidt --- net/mac802154/llsec.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index e8512578398e..5e7cc11fab3a 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -710,6 +710,7 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) { struct ieee802154_hdr hdr; int rc, authlen, hlen; + struct sk_buff *trailer; struct mac802154_llsec_key *key; u32 frame_ctr; @@ -769,6 +770,12 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) skb->mac_len = ieee802154_hdr_push(skb, &hdr); skb_reset_mac_header(skb); + rc = skb_cow_data(skb, 0, &trailer); + if (rc < 0) { + llsec_key_put(key); + return rc; + } + rc = llsec_do_encrypt(skb, sec, &hdr, key); llsec_key_put(key); @@ -908,6 +915,13 @@ llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, const struct ieee802154_hdr *hdr, struct mac802154_llsec_key *key, __le64 dev_addr) { + struct sk_buff *trailer; + int err; + + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) + return err; + if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); else From 4db86f8ab11b5a41bfc36680be837e6ac1375ec6 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 27 May 2026 20:18:18 +0000 Subject: [PATCH 4107/5214] ieee802154: fix kernel-infoleak in dgram_recvmsg() KMSAN reported a kernel-infoleak in move_addr_to_user(): BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:131 [inline] BUG: KMSAN: kernel-infoleak in _inline_copy_to_user include/linux/uaccess.h:205 [inline] BUG: KMSAN: kernel-infoleak in _copy_to_user+0xcc/0x120 lib/usercopy.c:26 instrument_copy_to_user include/linux/instrumented.h:131 [inline] _inline_copy_to_user include/linux/uaccess.h:205 [inline] _copy_to_user+0xcc/0x120 lib/usercopy.c:26 copy_to_user include/linux/uaccess.h:236 [inline] move_addr_to_user+0x2e7/0x440 net/socket.c:302 ____sys_recvmsg+0x232/0x610 net/socket.c:2925 ... Uninit was stored to memory at: ieee802154_addr_to_sa include/net/ieee802154_netdev.h:369 [inline] dgram_recvmsg+0xa09/0xbe0 net/ieee802154/socket.c:739 The issue occurs because the `pan_id` field of `struct ieee802154_addr` is left uninitialized when the address mode is `IEEE802154_ADDR_NONE`. The execution flow is as follows: 1. `__ieee802154_rx_handle_packet()` declares a local `struct ieee802154_hdr hdr` on the stack. 2. `ieee802154_hdr_pull()` calls `ieee802154_hdr_get_addr()` to parse the source and destination addresses into this structure. 3. If the address mode is `IEEE802154_ADDR_NONE`, `ieee802154_hdr_get_addr()` previously only set the `mode` field, leaving the `pan_id` field containing uninitialized stack memory. 4. This uninitialized `pan_id` is later copied into a `struct sockaddr_ieee802154` in `dgram_recvmsg()` via `ieee802154_addr_to_sa()`. 5. Finally, `move_addr_to_user()` copies the socket address structure to user space, leaking the uninitialized bytes. Fix this by using `memset` to zero out the address structure in `ieee802154_hdr_get_addr()` when the mode is `IEEE802154_ADDR_NONE`. Fixes: 94b4f6c21cf5 ("ieee802154: add header structs with endiannes and operations") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090 Link: https://syzkaller.appspot.com/ai_job?id=a507a109-d683-4a2c-bc03-93394f491b17 Signed-off-by: Aleksandr Nogikh Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/62795fd9-fc0c-48eb-bb82-05ffc5a57104@mail.kernel.org Signed-off-by: Stefan Schmidt --- net/ieee802154/header_ops.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index 41a556be1017..a9f0c8df5ae4 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -173,10 +173,13 @@ ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, { int pos = 0; - addr->mode = mode; - - if (mode == IEEE802154_ADDR_NONE) + if (mode == IEEE802154_ADDR_NONE) { + memset(addr, 0, sizeof(*addr)); + addr->mode = IEEE802154_ADDR_NONE; return 0; + } + + addr->mode = mode; if (!omit_pan) { memcpy(&addr->pan_id, buf + pos, 2); From 649147cb3f8b3c0c9aeba5d89d69a6ef221c12c2 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 2 Jun 2026 12:41:33 +0700 Subject: [PATCH 4108/5214] mac802154: Prevent overwrite return code in mac802154_perform_association() When assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL, the return value assigned to either "-ERANGE" or "-EPERM" but this return value will be overwritten to 0 after exiting the conditional scope. So, jump to clear_assoc label to preserve the return value when assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL. This is reported by Coverity Scan as "Unused value". Fixes: fefd19807fe9 ("mac802154: Handle associating") Signed-off-by: Robertus Diawan Chris Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260602054133.470293-1-robertusdchris@gmail.com Signed-off-by: Stefan Schmidt --- net/mac802154/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 0a31ac8d8415..300d4584533e 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -594,6 +594,7 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, "Negative ASSOC RESP received from %8phC: %s\n", &ceaddr, local->assoc_status == IEEE802154_PAN_AT_CAPACITY ? "PAN at capacity" : "access denied"); + goto clear_assoc; } ret = 0; From 9c1e0b6d49471a712511d23fc9d06901561135e8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:39 -0400 Subject: [PATCH 4109/5214] ieee802154: admin-gate legacy LLSEC dump operations In net/ieee802154/netlink.c, the legacy IEEE802154_NL family ops table builds the LLSEC dump entries (LLSEC_LIST_KEY, LLSEC_LIST_DEV, LLSEC_LIST_DEVKEY, LLSEC_LIST_SECLEVEL) with IEEE802154_DUMP() which sets no .flags, so generic netlink runs them ungated. The modern nl802154 family admin-gates the equivalent reads via NL802154_CMD_GET_SEC_KEY and friends with .flags = GENL_ADMIN_PERM. Any local uid that can open AF_NETLINK / NETLINK_GENERIC can resolve the "802.15.4 MAC" family and dump LLSEC_LIST_KEY on any wpan netdev that has an LLSEC key installed; the dump handler writes the raw 16-byte AES-128 key bytes (IEEE802154_ATTR_LLSEC_KEY_BYTES, copied verbatim from struct ieee802154_llsec_key.key) into the reply. Recovering the AES key compromises 802.15.4 LLSEC link confidentiality and authenticity, since LLSEC uses CCM* and the same key authenticates and encrypts frames. Impact: any local uid with no capabilities can read the raw 16-byte AES-128 LLSEC key from the kernel keytable on any wpan netdev that has an administrator-installed LLSEC key, by issuing an LLSEC_LIST_KEY dump on the legacy IEEE802154_NL generic-netlink family. Introduce IEEE802154_DUMP_PRIV() mirroring IEEE802154_DUMP() but setting .flags = GENL_ADMIN_PERM, and use it for the four LLSEC dump entries. LIST_PHY and LIST_IFACE retain IEEE802154_DUMP() because the modern nl802154 family exposes their equivalents to unprivileged readers by design (NL802154_CMD_GET_WPAN_PHY and NL802154_CMD_GET_INTERFACE carry "can be retrieved by unprivileged users" annotations). Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-2-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt --- net/ieee802154/ieee802154.h | 8 ++++++++ net/ieee802154/netlink.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index c5d91f78301a..fd9778f70550 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -23,6 +23,14 @@ void ieee802154_nl_exit(void); .dumpit = _dump, \ } +#define IEEE802154_DUMP_PRIV(_cmd, _func, _dump) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = _dump, \ + .flags = GENL_ADMIN_PERM, \ + } + struct genl_info; struct sk_buff *ieee802154_nl_create(int flags, u8 req); diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 7d2de4ee6992..9c9fd14d0ca8 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -98,20 +98,20 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, - ieee802154_llsec_dump_keys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, + ieee802154_llsec_dump_keys), IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, - ieee802154_llsec_dump_devs), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, + ieee802154_llsec_dump_devs), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, - ieee802154_llsec_dump_devkeys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, + ieee802154_llsec_dump_devkeys), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, - ieee802154_llsec_dump_seclevels), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, + ieee802154_llsec_dump_seclevels), IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, ieee802154_llsec_add_seclevel), IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, From a6bfdfcc6711d1d5a92e98644359dedc67c0c858 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:40 -0400 Subject: [PATCH 4110/5214] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation The LLSEC ADD/DEL doit handlers under the legacy IEEE802154_NL family consume IEEE802154_ATTR_LLSEC_KEY_BYTES and IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, both declared in net/ieee802154/nl_policy.c as bare length entries with no .type (defaulting to NLA_UNSPEC). Generic netlink strict validation rejects all NLA_UNSPEC attributes via validate_nla(), so every LLSEC_ADD_KEY, LLSEC_DEL_KEY, LLSEC_ADD_DEV, LLSEC_DEL_DEV, LLSEC_ADD_DEVKEY, LLSEC_DEL_DEVKEY, LLSEC_ADD_SECLEVEL, and LLSEC_DEL_SECLEVEL request fails at the dispatcher with "Unsupported attribute" before reaching the handler. The doit path has been silently dead since strict validation became the default for genl families that do not opt out. The dump path is unaffected because dump requests carry no LLSEC attributes to validate, which is why the LLSEC_LIST_KEY read remained reachable (patch 1/2). Introduce IEEE802154_OP_RELAXED() mirroring IEEE802154_OP() but with .validate = GENL_DONT_VALIDATE_STRICT, and use it for the eight legacy LLSEC mutate ops so admin-driven LLSEC configuration via the legacy interface works again. Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-3-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt --- net/ieee802154/ieee802154.h | 9 +++++++++ net/ieee802154/netlink.c | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index fd9778f70550..e765adc4b88f 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -16,6 +16,15 @@ void ieee802154_nl_exit(void); .flags = GENL_ADMIN_PERM, \ } +#define IEEE802154_OP_RELAXED(_cmd, _func) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = NULL, \ + .flags = GENL_ADMIN_PERM, \ + .validate = GENL_DONT_VALIDATE_STRICT,\ + } + #define IEEE802154_DUMP(_cmd, _func, _dump) \ { \ .cmd = _cmd, \ diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 9c9fd14d0ca8..cacad21347ec 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -100,22 +100,22 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, ieee802154_llsec_dump_keys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), - IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, ieee802154_llsec_dump_devs), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, ieee802154_llsec_dump_devkeys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, ieee802154_llsec_dump_seclevels), - IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, - ieee802154_llsec_add_seclevel), - IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, - ieee802154_llsec_del_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL, + ieee802154_llsec_add_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL, + ieee802154_llsec_del_seclevel), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { From a540a64c4801fe02e452ee37e961018106418260 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Jun 2026 09:06:58 +0200 Subject: [PATCH 4111/5214] smb: client: refactor ACL setting control flow in id_mode_to_cifs_acl() Refactor the control flow in id_mode_to_cifs_acl() to reduce nesting and prevent error code overwriting. Instead of wrapping the call to ops->set_acl() in a conditional block, introduce early exits (goto id_mode_to_cifs_acl_exit) when build_sec_desc() fails or ops->set_acl is NULL. This ensures that any actual error returned by build_sec_desc() is not overwritten with -EOPNOTSUPP. Signed-off-by: Ralph Boehme Signed-off-by: Steve French --- fs/smb/client/cifsacl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 42a3115359da..5bbf73736358 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -1834,14 +1834,18 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); - if (ops->set_acl == NULL) - rc = -EOPNOTSUPP; + if (rc != 0) + goto id_mode_to_cifs_acl_exit; - if (!rc) { - /* Set the security descriptor */ - rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); - cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + if (ops->set_acl == NULL) { + rc = -EOPNOTSUPP; + goto id_mode_to_cifs_acl_exit; } + + /* Set the security descriptor */ + rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); + cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + id_mode_to_cifs_acl_exit: cifs_put_tlink(tlink); From de9aa5ea2d9ea55234e78af1e6182979aa4f646a Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sat, 20 Jun 2026 20:27:47 +0800 Subject: [PATCH 4112/5214] docs: ipmi: Fix path of the "hotmod" module parameter The correct path of the "hotmod" module parameter should be /sys/module/ipmi_si/parameters/hotmod. Fix it. Signed-off-by: Zenghui Yu Message-ID: <20260620122747.7902-1-zenghui.yu@linux.dev> Signed-off-by: Corey Minyard --- Documentation/driver-api/ipmi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/ipmi.rst b/Documentation/driver-api/ipmi.rst index f52ab2df2569..d08cee98e34a 100644 --- a/Documentation/driver-api/ipmi.rst +++ b/Documentation/driver-api/ipmi.rst @@ -495,7 +495,7 @@ tuned to the user's desired performance. The driver supports a hot add and remove of interfaces. This way, interfaces can be added or removed after the kernel is up and running. -This is done using /sys/modules/ipmi_si/parameters/hotmod, which is a +This is done using /sys/module/ipmi_si/parameters/hotmod, which is a write-only parameter. You write a string to this interface. The string has the format:: From 8e065a1602511282fc0da2dc89445e0eb71a681c Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:07 +0000 Subject: [PATCH 4113/5214] md/raid1: fix writes_pending and barrier reference leaks on write failures raid1_make_request() acquires a writes_pending reference with md_write_start() before calling raid1_write_request(). Several failure paths in raid1_write_request() complete the bio and return without reaching the normal write completion path, causing the corresponding md_write_end() to be skipped. Make raid1_write_request() return a status indicating whether the write request was successfully queued. This allows raid1_make_request() to call md_write_end() when raid1_write_request() fails. Additionally, if wait_blocked_rdev() fails after wait_barrier() succeeds, the associated barrier reference is not released. Call allow_barrier() before returning from that path to keep the barrier accounting balanced. Fixes: b1a7ad8b5c4f ("md/raid1: Handle bio_split() errors") Fixes: f2a38abf5f1c ("md/raid1: Atomic write support") Fixes: 5aa705039c4f ("md: raid1 add nowait support") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260611083514.754922-1-abd.masalkhi@gmail.com?part=1 Closes: https://sashiko.dev/#/patchset/20260611132500.763528-1-abd.masalkhi@gmail.com?part=1 Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-2-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/raid1.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 5b9368bd9e70..632d72607e11 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1501,7 +1501,7 @@ static void raid1_start_write_behind(struct mddev *mddev, struct r1bio *r1_bio, } -static void raid1_write_request(struct mddev *mddev, struct bio *bio, +static bool raid1_write_request(struct mddev *mddev, struct bio *bio, int max_write_sectors) { struct r1conf *conf = mddev->private; @@ -1512,6 +1512,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, int max_sectors; bool write_behind = false; bool is_discard = (bio_op(bio) == REQ_OP_DISCARD); + sector_t sector = bio->bi_iter.bi_sector; if (mddev_is_clustered(mddev) && mddev->cluster_ops->area_resyncing(mddev, WRITE, @@ -1519,7 +1520,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); - return; + return false; } wait_event_idle(conf->wait_barrier, !mddev->cluster_ops->area_resyncing(mddev, WRITE, @@ -1535,12 +1536,13 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (!wait_barrier(conf, bio->bi_iter.bi_sector, bio->bi_opf & REQ_NOWAIT)) { bio_wouldblock_error(bio); - return; + return false; } if (!wait_blocked_rdev(mddev, bio)) { bio_wouldblock_error(bio); - return; + allow_barrier(conf, sector); + return false; } r1_bio = alloc_r1bio(mddev, bio); @@ -1699,7 +1701,8 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, /* In case raid1d snuck in to freeze_array */ wake_up_barrier(conf); - return; + return true; + err_handle: for (k = 0; k < i; k++) { if (r1_bio->bios[k]) { @@ -1709,6 +1712,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, } raid_end_bio_io(r1_bio); + return false; } static bool raid1_make_request(struct mddev *mddev, struct bio *bio) @@ -1732,8 +1736,9 @@ static bool raid1_make_request(struct mddev *mddev, struct bio *bio) if (bio_data_dir(bio) == READ) raid1_read_request(mddev, bio, sectors, NULL); else { - md_write_start(mddev,bio); - raid1_write_request(mddev, bio, sectors); + md_write_start(mddev, bio); + if (!raid1_write_request(mddev, bio, sectors)) + md_write_end(mddev); } return true; } From e045d6ed33f8faa3e3dd6dc33c62ec01e3ad275d Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:08 +0000 Subject: [PATCH 4114/5214] md/raid10: fix writes_pending leak on write request failures raid10_make_request() acquires a writes_pending reference with md_write_start() before dispatching write requests. Several failure paths in raid10_write_request() complete the bio and return without reaching the normal write completion path, causing the corresponding md_write_end() to be skipped. Make raid10_write_request() return a status indicating whether the write request was successfully queued. This allows raid10_make_request() to release the writes_pending reference with md_write_end() when a write request fails. Fixes: 4cf58d952909 ("md/raid10: Handle bio_split() errors") Fixes: c9aa889b035f ("md: raid10 add nowait support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-3-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/raid10.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index cee5a253a281..c123a8c76ddc 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1349,7 +1349,7 @@ static void wait_blocked_dev(struct mddev *mddev, struct r10bio *r10_bio) } } -static void raid10_write_request(struct mddev *mddev, struct bio *bio, +static bool raid10_write_request(struct mddev *mddev, struct bio *bio, struct r10bio *r10_bio) { struct r10conf *conf = mddev->private; @@ -1365,7 +1365,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, /* Bail out if REQ_NOWAIT is set for the bio */ if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); - return; + return false; } for (;;) { prepare_to_wait(&conf->wait_barrier, @@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, sectors = r10_bio->sectors; if (!regular_request_wait(mddev, conf, bio, sectors)) { free_r10bio(r10_bio); - return; + return false; } if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && @@ -1398,7 +1398,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, if (bio->bi_opf & REQ_NOWAIT) { allow_barrier(conf); bio_wouldblock_error(bio); - return; + return false; } mddev_add_trace_msg(conf->mddev, "raid10 wait reshape metadata"); @@ -1514,7 +1514,8 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, raid10_write_one_disk(mddev, r10_bio, bio, true, i); } one_write_done(r10_bio); - return; + return true; + err_handle: for (k = 0; k < i; k++) { int d = r10_bio->devs[k].devnum; @@ -1532,10 +1533,12 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, } raid_end_bio_io(r10_bio); + return false; } -static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) +static bool __make_request(struct mddev *mddev, struct bio *bio, int sectors) { + bool ret; struct r10conf *conf = mddev->private; struct r10bio *r10_bio; @@ -1551,10 +1554,13 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors) memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * conf->geo.raid_disks); + ret = true; if (bio_data_dir(bio) == READ) raid10_read_request(mddev, bio, r10_bio); else - raid10_write_request(mddev, bio, r10_bio); + ret = raid10_write_request(mddev, bio, r10_bio); + + return ret; } static void raid_end_discard_bio(struct r10bio *r10bio) @@ -1900,7 +1906,8 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio) sectors = chunk_sects - (bio->bi_iter.bi_sector & (chunk_sects - 1)); - __make_request(mddev, bio, sectors); + if (!__make_request(mddev, bio, sectors)) + md_write_end(mddev); /* In case raid10d snuck in to freeze_array */ wake_up_barrier(conf); From 393d687131d8aa8c7e4de2cb494438e145d20fc2 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:09 +0000 Subject: [PATCH 4115/5214] md/raid10: fix writes_pending and barrier reference leaks on discard failures raid10_make_request() acquires a writes_pending reference with md_write_start() before calling raid10_handle_discard(). Several failure paths in raid10_handle_discard() complete the bio and return without releasing the corresponding reference, causing md_write_end() to be skipped. Call md_write_end() before returning from these failure paths to keep writes_pending accounting balanced. Additionally, discard split allocation failures can occur after wait_barrier() succeeds. Those paths return without calling allow_barrier(), leaking the associated barrier reference. Release the barrier before returning from those paths. Fixes: c9aa889b035f ("md: raid10 add nowait support") Fixes: 4cf58d952909 ("md/raid10: Handle bio_split() errors") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-4-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/raid10.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index c123a8c76ddc..0a3cfdd3f5df 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1639,6 +1639,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (!wait_barrier(conf, bio->bi_opf & REQ_NOWAIT)) { bio_wouldblock_error(bio); + md_write_end(mddev); return 0; } @@ -1681,6 +1682,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (IS_ERR(split)) { bio->bi_status = errno_to_blk_status(PTR_ERR(split)); bio_endio(bio); + md_write_end(mddev); + allow_barrier(conf); return 0; } @@ -1698,6 +1701,8 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) if (IS_ERR(split)) { bio->bi_status = errno_to_blk_status(PTR_ERR(split)); bio_endio(bio); + md_write_end(mddev); + allow_barrier(conf); return 0; } From a4c55c902670f2784d3001652183aafa17712cfe Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 13 Jun 2026 18:28:10 +0000 Subject: [PATCH 4116/5214] md/raid1: simplify raid1_write_request() error handling raid1_write_request() increments rdev->nr_pending before checking the badblocks and then immediately decrements it again when a device is skipped. Move the increment until after the checks succeed so the reference accounting is easier to follow. Consolidate the failure paths so that each error label releases exactly the resources acquired up to that point. err_dec_pending drops pending references and frees the r1bio, while err_allow_barrier handles the barrier release before returning. When a REQ_ATOMIC write cannot be satisfied due to a badblock range, complete the bio with BLK_STS_NOTSUPP rather than reporting an I/O error, since the operation is unsupported rather than having failed during I/O. Rename max_write_sectors to max_sectors and remove the redundant local copy. Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260613182810.1317258-5-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/raid1.c | 59 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 632d72607e11..86d4f224ffb1 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1502,29 +1502,29 @@ static void raid1_start_write_behind(struct mddev *mddev, struct r1bio *r1_bio, } static bool raid1_write_request(struct mddev *mddev, struct bio *bio, - int max_write_sectors) + int max_sectors) { struct r1conf *conf = mddev->private; struct r1bio *r1_bio; int i, disks, k; unsigned long flags; int first_clone; - int max_sectors; bool write_behind = false; - bool is_discard = (bio_op(bio) == REQ_OP_DISCARD); + bool nowait = bio->bi_opf & REQ_NOWAIT; + bool is_discard = op_is_discard(bio->bi_opf); sector_t sector = bio->bi_iter.bi_sector; if (mddev_is_clustered(mddev) && - mddev->cluster_ops->area_resyncing(mddev, WRITE, - bio->bi_iter.bi_sector, bio_end_sector(bio))) { + mddev->cluster_ops->area_resyncing(mddev, WRITE, sector, + bio_end_sector(bio))) { - if (bio->bi_opf & REQ_NOWAIT) { + if (nowait) { bio_wouldblock_error(bio); return false; } wait_event_idle(conf->wait_barrier, !mddev->cluster_ops->area_resyncing(mddev, WRITE, - bio->bi_iter.bi_sector, + sector, bio_end_sector(bio))); } @@ -1533,20 +1533,18 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, * thread has put up a bar for new requests. * Continue immediately if no resync is active currently. */ - if (!wait_barrier(conf, bio->bi_iter.bi_sector, - bio->bi_opf & REQ_NOWAIT)) { + if (!wait_barrier(conf, sector, nowait)) { bio_wouldblock_error(bio); return false; } if (!wait_blocked_rdev(mddev, bio)) { bio_wouldblock_error(bio); - allow_barrier(conf, sector); - return false; + goto err_allow_barrier; } r1_bio = alloc_r1bio(mddev, bio); - r1_bio->sectors = max_write_sectors; + r1_bio->sectors = max_sectors; /* first select target devices under rcu_lock and * inc refcount on their rdev. Record them by setting @@ -1560,7 +1558,6 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, */ disks = conf->raid_disks * 2; - max_sectors = r1_bio->sectors; for (i = 0; i < disks; i++) { struct md_rdev *rdev = conf->mirrors[i].rdev; @@ -1576,23 +1573,21 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, if (!rdev || test_bit(Faulty, &rdev->flags)) continue; - atomic_inc(&rdev->nr_pending); if (test_bit(WriteErrorSeen, &rdev->flags)) { sector_t first_bad; sector_t bad_sectors; int is_bad; - is_bad = is_badblock(rdev, r1_bio->sector, max_sectors, + is_bad = is_badblock(rdev, sector, max_sectors, &first_bad, &bad_sectors); - if (is_bad && first_bad <= r1_bio->sector) { + if (is_bad && first_bad <= sector) { /* Cannot write here at all */ - bad_sectors -= (r1_bio->sector - first_bad); + bad_sectors -= (sector - first_bad); if (bad_sectors < max_sectors) /* mustn't write more than bad_sectors * to other devices yet */ max_sectors = bad_sectors; - rdev_dec_pending(rdev, mddev); continue; } if (is_bad) { @@ -1606,15 +1601,18 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, * the benefit. */ if (bio->bi_opf & REQ_ATOMIC) { - rdev_dec_pending(rdev, mddev); - goto err_handle; + bio->bi_status = BLK_STS_NOTSUPP; + bio_endio(bio); + goto err_dec_pending; } - good_sectors = first_bad - r1_bio->sector; + good_sectors = first_bad - sector; if (good_sectors < max_sectors) max_sectors = good_sectors; } } + + atomic_inc(&rdev->nr_pending); r1_bio->bios[i] = bio; } @@ -1630,10 +1628,8 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, if (max_sectors < bio_sectors(bio)) { bio = bio_submit_split_bioset(bio, max_sectors, &conf->bio_split); - if (!bio) { - set_bit(R1BIO_Returned, &r1_bio->state); - goto err_handle; - } + if (!bio) + goto err_dec_pending; r1_bio->master_bio = bio; r1_bio->sectors = max_sectors; @@ -1677,7 +1673,7 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, mbio->bi_opf &= ~REQ_NOWAIT; r1_bio->bios[i] = mbio; - mbio->bi_iter.bi_sector = (r1_bio->sector + rdev->data_offset); + mbio->bi_iter.bi_sector = sector + rdev->data_offset; mbio->bi_end_io = raid1_end_write_request; if (test_bit(FailFast, &rdev->flags) && !test_bit(WriteMostly, &rdev->flags) && @@ -1686,7 +1682,7 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, mbio->bi_private = r1_bio; atomic_inc(&r1_bio->remaining); - mddev_trace_remap(mddev, mbio, r1_bio->sector); + mddev_trace_remap(mddev, mbio, sector); /* flush_pending_writes() needs access to the rdev so...*/ mbio->bi_bdev = (void *)rdev; if (!raid1_add_bio_to_plug(mddev, mbio, raid1_unplug, disks)) { @@ -1701,9 +1697,10 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, /* In case raid1d snuck in to freeze_array */ wake_up_barrier(conf); + return true; -err_handle: +err_dec_pending: for (k = 0; k < i; k++) { if (r1_bio->bios[k]) { rdev_dec_pending(conf->mirrors[k].rdev, mddev); @@ -1711,7 +1708,11 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio, } } - raid_end_bio_io(r1_bio); + free_r1bio(r1_bio); + +err_allow_barrier: + allow_barrier(conf, sector); + return false; } From 74ddbf98e2db646ec58f7e7731c936b7a4a470fe Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 5 Jun 2026 15:26:37 +0800 Subject: [PATCH 4117/5214] md/raid5: account discard IO Raid5 handles discard bios internally through make_discard_request() and never passes them through md_account_bio(). As a result, discard IO is missing the md-device iostat accounting that normal raid5 IO and discard IO in other raid levels get from md_account_bio(). Before accounting the bio, trim the request to the full data stripes that raid5 will actually discard. The first full stripe is the ceiling of the bio start divided by data-stripe sectors, and the last full stripe is the floor of the bio end divided by data-stripe sectors. Account that exact MD logical full-stripe range, then restore the original iterator so bio completion and iostat still cover the original request. Link: https://patch.msgid.link/20260605072639.2434847-2-yukuai@kernel.org Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 65ae7d8930fc..debf35342ae0 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5690,7 +5690,10 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi) { struct r5conf *conf = mddev->private; sector_t logical_sector, last_sector; + sector_t first_stripe, last_stripe; struct stripe_head *sh; + struct bvec_iter bi_iter; + struct bio *orig_bi = bi; int stripe_sectors; /* We need to handle this when io_uring supports discard/trim */ @@ -5701,19 +5704,29 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi) /* Skip discard while reshape is happening */ return; - logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1); - last_sector = bio_end_sector(bi); - - bi->bi_next = NULL; - stripe_sectors = conf->chunk_sectors * (conf->raid_disks - conf->max_degraded); - logical_sector = DIV_ROUND_UP_SECTOR_T(logical_sector, - stripe_sectors); - sector_div(last_sector, stripe_sectors); + first_stripe = DIV_ROUND_UP_SECTOR_T(bi->bi_iter.bi_sector, + stripe_sectors); + last_stripe = bio_end_sector(bi); + sector_div(last_stripe, stripe_sectors); - logical_sector *= conf->chunk_sectors; - last_sector *= conf->chunk_sectors; + if (first_stripe >= last_stripe) { + bio_endio(bi); + return; + } + + bi_iter = bi->bi_iter; + bi->bi_iter.bi_sector = first_stripe * stripe_sectors; + bi->bi_iter.bi_size = ((last_stripe - first_stripe) * + stripe_sectors) << 9; + md_account_bio(mddev, &bi); + orig_bi->bi_iter = bi_iter; + bi->bi_iter = bi_iter; + bi->bi_next = NULL; + + logical_sector = first_stripe * conf->chunk_sectors; + last_sector = last_stripe * conf->chunk_sectors; for (; logical_sector < last_sector; logical_sector += RAID5_STRIPE_SECTORS(conf)) { From 90573092673cdbb2f28f0932fd40de65c3f762cf Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 5 Jun 2026 15:26:38 +0800 Subject: [PATCH 4118/5214] md/raid5: validate discard support at request time Raid5 used to disable discard limits when devices_handle_discard_safely was not set or when stacked member limits could not support a full-stripe discard. That hides discard from userspace before raid5 can decide whether a request can be handled safely. Follow other virtual drivers and advertise a UINT_MAX discard limit for the md device. Cache lower discard support in r5conf when setting queue limits, and reject unsupported discard bios before queuing stripe work. Link: https://patch.msgid.link/20260605072639.2434847-3-yukuai@kernel.org Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 34 +++++++++++++++++++--------------- drivers/md/raid5.h | 1 + 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index debf35342ae0..76e736ee48d3 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1134,6 +1134,18 @@ static void defer_issue_bios(struct r5conf *conf, sector_t sector, dispatch_bio_list(&tmp); } +static bool raid5_discard_limits(struct mddev *mddev, struct bio *bi) +{ + struct r5conf *conf = mddev->private; + + if (!conf->raid5_discard_unsupported) + return true; + + bi->bi_status = BLK_STS_NOTSUPP; + bio_endio(bi); + return false; +} + static void raid5_end_read_request(struct bio *bi); static void @@ -5704,6 +5716,9 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi) /* Skip discard while reshape is happening */ return; + if (!raid5_discard_limits(mddev, bi)) + return; + stripe_sectors = conf->chunk_sectors * (conf->raid_disks - conf->max_degraded); first_stripe = DIV_ROUND_UP_SECTOR_T(bi->bi_iter.bi_sector, @@ -7817,24 +7832,12 @@ static int raid5_set_limits(struct mddev *mddev) queue_limits_stack_bdev(&lim, rdev->bdev, rdev->new_data_offset, mddev->gendisk->disk_name); - /* - * Zeroing is required for discard, otherwise data could be lost. - * - * Consider a scenario: discard a stripe (the stripe could be - * inconsistent if discard_zeroes_data is 0); write one disk of the - * stripe (the stripe could be inconsistent again depending on which - * disks are used to calculate parity); the disk is broken; The stripe - * data of this disk is lost. - * - * We only allow DISCARD if the sysadmin has confirmed that only safe - * devices are in use by setting a module parameter. A better idea - * might be to turn DISCARD into WRITE_ZEROES requests, as that is - * required to be safe. - */ if (!devices_handle_discard_safely || lim.max_discard_sectors < (stripe >> 9) || lim.discard_granularity < stripe) - lim.max_hw_discard_sectors = 0; + conf->raid5_discard_unsupported = true; + else + conf->raid5_discard_unsupported = false; /* * Requests require having a bitmap for each stripe. @@ -7843,6 +7846,7 @@ static int raid5_set_limits(struct mddev *mddev) lim.max_hw_sectors = RAID5_MAX_REQ_STRIPES << RAID5_STRIPE_SHIFT(conf); if ((lim.max_hw_sectors << 9) < lim.io_opt) lim.max_hw_sectors = lim.io_opt >> 9; + lim.max_hw_discard_sectors = UINT_MAX; /* No restrictions on the number of segments in the request */ lim.max_segments = USHRT_MAX; diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 1dfa60a41d91..cb5feae04db2 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -689,6 +689,7 @@ struct r5conf { struct list_head pending_list; int pending_data_cnt; struct r5pending_data *next_pending_data; + bool raid5_discard_unsupported; mempool_t *ctx_pool; int ctx_size; From 53528031e7a6e16f0f05347f3826ffb4f957b7e4 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 5 Jun 2026 15:26:39 +0800 Subject: [PATCH 4119/5214] md/raid5: always convert llbitmap bits for discard llbitmap discard is useful even when no underlying member device supports it. The discard still converts the llbitmap range to unwritten, so later reads and recovery do not rely on stale parity for that range. Let llbitmap discard bypass the raid5 lower discard support check. If lower discard is not safe or not supported, complete the accounted clone after md_account_bio() so the llbitmap conversion callbacks run without member discard bios. Link: https://patch.msgid.link/20260605072639.2434847-4-yukuai@kernel.org Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 76e736ee48d3..180ff0660b6a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1138,6 +1138,9 @@ static bool raid5_discard_limits(struct mddev *mddev, struct bio *bi) { struct r5conf *conf = mddev->private; + if (mddev->bitmap_id == ID_LLBITMAP) + return true; + if (!conf->raid5_discard_unsupported) return true; @@ -5740,6 +5743,12 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi) bi->bi_iter = bi_iter; bi->bi_next = NULL; + if (mddev->bitmap_id == ID_LLBITMAP && + conf->raid5_discard_unsupported) { + bio_endio(bi); + return; + } + logical_sector = first_stripe * conf->chunk_sectors; last_sector = last_stripe * conf->chunk_sectors; From a286cb88ddb26c5f4377859d8e77233d9181eb82 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 08:35:14 +0000 Subject: [PATCH 4120/5214] md/raid1: honor REQ_NOWAIT when waiting for behind writes raid1 supports REQ_NOWAIT reads by avoiding waits in the barrier path through wait_read_barrier(). However, a read can still block on a WriteMostly device when the array uses a bitmap and there are outstanding behind writes. In that case raid1 unconditionally calls wait_behind_writes(), which may sleep until all behind writes complete. As a result, a REQ_NOWAIT read can block despite the caller explicitly requesting non-blocking behavior. This ensures that raid1 consistently honors REQ_NOWAIT reads across all paths that may otherwise wait for behind writes. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611083514.754922-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/md-bitmap.c | 9 +++++++-- drivers/md/md-bitmap.h | 2 +- drivers/md/md-llbitmap.c | 13 ++++++++----- drivers/md/md.c | 2 +- drivers/md/raid1.c | 10 +++++++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 7d778fe1c47c..0f02e2956398 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -2064,18 +2064,23 @@ static void bitmap_end_behind_write(struct mddev *mddev) bitmap->mddev->bitmap_info.max_write_behind); } -static void bitmap_wait_behind_writes(struct mddev *mddev) +static bool bitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct bitmap *bitmap = mddev->bitmap; /* wait for behind writes to complete */ if (bitmap && atomic_read(&bitmap->behind_writes) > 0) { + if (nowait) + return false; + pr_debug("md:%s: behind writes in progress - waiting to stop.\n", mdname(mddev)); /* need to kick something here to make sure I/O goes? */ wait_event(bitmap->behind_wait, atomic_read(&bitmap->behind_writes) == 0); } + + return true; } static void bitmap_destroy(struct mddev *mddev) @@ -2085,7 +2090,7 @@ static void bitmap_destroy(struct mddev *mddev) if (!bitmap) /* there was no bitmap */ return; - bitmap_wait_behind_writes(mddev); + bitmap_wait_behind_writes(mddev, false); if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) mddev_destroy_serial_pool(mddev, NULL); diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h index 214f623c7e79..f46674bdfeb9 100644 --- a/drivers/md/md-bitmap.h +++ b/drivers/md/md-bitmap.h @@ -98,7 +98,7 @@ struct bitmap_operations { void (*start_behind_write)(struct mddev *mddev); void (*end_behind_write)(struct mddev *mddev); - void (*wait_behind_writes)(struct mddev *mddev); + bool (*wait_behind_writes)(struct mddev *mddev, bool nowait); md_bitmap_fn *start_write; md_bitmap_fn *end_write; diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1adc5b117821..5a4e2abaa757 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -1574,16 +1574,19 @@ static void llbitmap_end_behind_write(struct mddev *mddev) wake_up(&llbitmap->behind_wait); } -static void llbitmap_wait_behind_writes(struct mddev *mddev) +static bool llbitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct llbitmap *llbitmap = mddev->bitmap; - if (!llbitmap) - return; + if (llbitmap && atomic_read(&llbitmap->behind_writes) > 0) { + if (nowait) + return false; - wait_event(llbitmap->behind_wait, - atomic_read(&llbitmap->behind_writes) == 0); + wait_event(llbitmap->behind_wait, + atomic_read(&llbitmap->behind_writes) == 0); + } + return true; } static ssize_t bits_show(struct mddev *mddev, char *page) diff --git a/drivers/md/md.c b/drivers/md/md.c index 096bb64e87bd..d1465bcd86c8 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7050,7 +7050,7 @@ EXPORT_SYMBOL_GPL(md_stop_writes); static void mddev_detach(struct mddev *mddev) { if (md_bitmap_enabled(mddev, false)) - mddev->bitmap_ops->wait_behind_writes(mddev); + mddev->bitmap_ops->wait_behind_writes(mddev, false); if (mddev->pers && mddev->pers->quiesce && !is_md_suspended(mddev)) { mddev->pers->quiesce(mddev, 1); mddev->pers->quiesce(mddev, 0); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 86d4f224ffb1..e2a50816e5a0 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1341,6 +1341,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, int max_sectors; int rdisk; bool r1bio_existed = !!r1_bio; + bool nowait = bio->bi_opf & REQ_NOWAIT; /* * An md cloned bio indicates we are in the error path. @@ -1360,8 +1361,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * Still need barrier for READ in case that whole * array is frozen. */ - if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, - bio->bi_opf & REQ_NOWAIT)) { + if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); return; } @@ -1402,7 +1402,11 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * over-take any writes that are 'behind' */ mddev_add_trace_msg(mddev, "raid1 wait behind writes"); - mddev->bitmap_ops->wait_behind_writes(mddev); + if (!mddev->bitmap_ops->wait_behind_writes(mddev, nowait)) { + bio_wouldblock_error(bio); + set_bit(R1BIO_Returned, &r1_bio->state); + goto err_handle; + } } if (max_sectors < bio_sectors(bio)) { From 69ad6ce47f9bf2b9fe0ed69b042db993d33bbf12 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 10:13:50 +0000 Subject: [PATCH 4121/5214] md/raid1: free r1_bio when REQ_NOWAIT is set and read would block on retry When a read is retried, raid1_read_request() may be called with a pre-allocated r1_bio. If wait_read_barrier() fails for a REQ_NOWAIT read, the bio is completed and the function returns immediately. In this case the existing r1_bio is leaked. This fixes a leak of pre-allocated r1_bio structures for retried reads. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260611083514.754922-1-abd.masalkhi@gmail.com?part=1 Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611101350.759154-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai --- drivers/md/raid1.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index e2a50816e5a0..41d9094fa50a 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1363,6 +1363,12 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, */ if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); + + if (r1bio_existed) { + set_bit(R1BIO_Returned, &r1_bio->state); + raid_end_bio_io(r1_bio); + } + return; } From 601d3c21b2e26b676cc67ae8804e991bbbcd4507 Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 19 Jun 2026 12:41:14 +0800 Subject: [PATCH 4122/5214] md/raid1: protect head_position for read balance KCSAN reports a data race between raid1_end_read_request() and raid1_read_request(). The completion path updates conf->mirrors[disk].head_position in update_head_pos() without a lock, while the read-balance heuristic reads the same field locklessly in is_sequential() and choose_best_rdev(). KCSAN report: ========================= BUG: KCSAN: data-race in raid1_end_read_request / raid1_read_request write to 0xffff8f0306ba7868 of 8 bytes by interrupt on cpu 9: raid1_end_read_request+0xb5/0x440 bio_endio+0x3c9/0x3e0 blk_update_request+0x257/0x770 scsi_end_request+0x4d/0x520 scsi_io_completion+0x6f/0x990 scsi_finish_command+0x188/0x280 scsi_complete+0xac/0x160 blk_complete_reqs+0x8e/0xb0 blk_done_softirq+0x1d/0x30 [...] read to 0xffff8f0306ba7868 of 8 bytes by task 667002 on cpu 11: raid1_read_request+0x497/0x1a10 raid1_make_request+0xdf/0x1950 md_handle_request+0x2c5/0x700 md_submit_bio+0x126/0x320 __submit_bio+0x2ec/0x3a0 submit_bio_noacct_nocheck+0x572/0x890 [...] value changed: 0x0000000000000078 -> 0x00000000005fe448 Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260619044114.1208456-1-chencheng@fnnas.com Signed-off-by: Yu Kuai --- drivers/md/raid1.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 41d9094fa50a..afe2ca96ad8c 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -359,8 +359,8 @@ static inline void update_head_pos(int disk, struct r1bio *r1_bio) { struct r1conf *conf = r1_bio->mddev->private; - conf->mirrors[disk].head_position = - r1_bio->sector + (r1_bio->sectors); + WRITE_ONCE(conf->mirrors[disk].head_position, + r1_bio->sector + r1_bio->sectors); } /* @@ -737,7 +737,7 @@ static bool is_sequential(struct r1conf *conf, int disk, struct r1bio *r1_bio) { /* TODO: address issues with this check and concurrency. */ return conf->mirrors[disk].next_seq_sect == r1_bio->sector || - conf->mirrors[disk].head_position == r1_bio->sector; + READ_ONCE(conf->mirrors[disk].head_position) == r1_bio->sector; } /* @@ -814,7 +814,8 @@ static int choose_best_rdev(struct r1conf *conf, struct r1bio *r1_bio) set_bit(R1BIO_FailFast, &r1_bio->state); pending = atomic_read(&rdev->nr_pending); - dist = abs(r1_bio->sector - conf->mirrors[disk].head_position); + dist = abs(r1_bio->sector - + READ_ONCE(conf->mirrors[disk].head_position)); /* Don't change to another disk for sequential reads */ if (is_sequential(conf, disk, r1_bio)) { From 00e93faf4cea9e8802ac5dfee0952d84fc95c40f Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Thu, 18 Jun 2026 10:57:35 +0800 Subject: [PATCH 4123/5214] md/raid5: let stripe batch bm_seq comparison wrap-safe Once the 32-bit seq wraps, a newer bm_seq can look smaller than old, so .. covert to wrap-safe calculate way. Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260618025735.915113-1-chencheng@fnnas.com Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 180ff0660b6a..f35c2a7b2be1 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -996,7 +996,7 @@ static void stripe_add_to_batch_list(struct r5conf *conf, if (test_and_clear_bit(STRIPE_BIT_DELAY, &sh->state)) { int seq = sh->bm_seq; if (test_bit(STRIPE_BIT_DELAY, &sh->batch_head->state) && - sh->batch_head->bm_seq > seq) + sh->batch_head->bm_seq - seq > 0) seq = sh->batch_head->bm_seq; set_bit(STRIPE_BIT_DELAY, &sh->batch_head->state); sh->batch_head->bm_seq = seq; From e4b4984e28c16406ecb318444dea4a8bf47def3e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:22 +0200 Subject: [PATCH 4124/5214] netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types Sashiko pointed out that there are a few lockless RCU readers using test_bit() which is a relaxed atomic operation and provides no memory barrier guarantees. Use test_bit_acquire() instead where the operation may run parallel with add/del/gc, i.e. is not one from the next cases - protected by region lock - in a set destroy phase - in a new/temporary set creation phase Fixes: 18f84d41d34f ("netfilter: ipset: Introduce RCU locking in hash:* types") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 04e4627ddfc1..00c27b95207f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -689,7 +689,7 @@ mtype_resize(struct ip_set *set, bool retried) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, dsize); if (SET_ELEM_EXPIRED(set, data)) @@ -826,7 +826,7 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, set->dsize); if (!SET_ELEM_EXPIRED(set, data)) @@ -1201,7 +1201,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1259,7 +1259,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, } pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1396,7 +1396,7 @@ mtype_list(const struct ip_set *set, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; e = ahash_data(n, i, set->dsize); if (SET_ELEM_EXPIRED(set, e)) From 1171192ac9af46ddf65caf4162f1b64c58ae37f4 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:23 +0200 Subject: [PATCH 4125/5214] netfilter: ipset: Don't use test_bit() in lockless RCU readers in bitmap types The pair of the patch "netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types" for the bitmap types. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Fixes: b0da3905bb1e ("netfilter: ipset: Bitmap types using the unified code base") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_bitmap_gen.h | 4 +++- net/netfilter/ipset/ip_set_bitmap_ip.c | 2 +- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 2 +- net/netfilter/ipset/ip_set_bitmap_port.c | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 798c7993635e..bb9b5bed10e1 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -165,6 +165,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ip_set_init_skbinfo(ext_skbinfo(x, set), ext); /* Activate element */ + smp_mb__before_atomic(); set_bit(e->id, map->members); set->elements++; @@ -219,7 +220,7 @@ mtype_list(const struct ip_set *set, cond_resched_rcu(); id = cb->args[IPSET_CB_ARG0]; x = get_ext(set, map, id); - if (!test_bit(id, map->members) || + if (!test_bit_acquire(id, map->members) || (SET_WITH_TIMEOUT(set) && #ifdef IP_SET_BITMAP_STORED_TIMEOUT mtype_is_filled(x) && @@ -278,6 +279,7 @@ mtype_gc(struct timer_list *t) x = get_ext(set, map, id); if (ip_set_timeout_expired(ext_timeout(x, set))) { clear_bit(id, map->members); + smp_mb__after_atomic(); ip_set_ext_destroy(set, x); set->elements--; } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 5988b9bb9029..ac7febce074f 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -67,7 +67,7 @@ static int bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, size_t dsize) { - return !!test_bit(e->id, map->members); + return !!test_bit_acquire(e->id, map->members); } static int diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 752f59ef8744..5921fd9d2dca 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -86,7 +86,7 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, { const struct bitmap_ipmac_elem *elem; - if (!test_bit(e->id, map->members)) + if (!test_bit_acquire(e->id, map->members)) return 0; elem = get_const_elem(map->extensions, e->id, dsize); if (e->add_mac && elem->filled == MAC_FILLED) diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 7138e080def4..ca875c982424 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -58,7 +58,7 @@ static int bitmap_port_do_test(const struct bitmap_port_adt_elem *e, const struct bitmap_port *map, size_t dsize) { - return !!test_bit(e->id, map->members); + return !!test_bit_acquire(e->id, map->members); } static int From 3ca9982a8882470aa0ac4e8bb9a552b181d1efcd Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:24 +0200 Subject: [PATCH 4126/5214] netfilter: ipset: fix order of kfree_rcu() and rcu_assign_pointer() Sashiko pointed out that kfree_rcu() was called before rcu_assign_pointer() in handling the comment extension. Fix the order so that rcu_assign_pointer() called first. Fixes: b57b2d1fa53f ("netfilter: ipset: Prepare the ipset core to use RCU at set level") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 3706b4a85a0f..a531b654b8d9 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -351,8 +351,8 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, if (unlikely(c)) { set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } if (!len) return; @@ -393,8 +393,8 @@ ip_set_comment_free(struct ip_set *set, void *ptr) if (unlikely(!c)) return; set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } typedef void (*destroyer)(struct ip_set *, void *); From 4a597a87e2e2f608edb6be2c510dc826b4fdfb53 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:26 +0200 Subject: [PATCH 4127/5214] netfilter: ipset: make sure gc is properly stopped Sashiko noticed that when destroying a set, cancel_delayed_work_sync() was called while gc calls queue_delayed_work() unconditionally which can lead not to properly shutting down the gc. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 00c27b95207f..dedf59b661dd 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -606,7 +606,7 @@ mtype_cancel_gc(struct ip_set *set) struct htype *h = set->data; if (SET_WITH_TIMEOUT(set)) - cancel_delayed_work_sync(&h->gc.dwork); + disable_delayed_work_sync(&h->gc.dwork); } static int From 213be32f46a29ca15a314df06c3424ecffd6c90a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 06:58:24 +0200 Subject: [PATCH 4128/5214] netfilter: nft_payload: reject offsets exceeding 65535 bytes Large offsets were rejected based on netlink policy, but blamed commit removed the policy without updating nft_payload_inner_init() to use the truncation-check helper. Silent truncation is not a problem, but not wanted either, so add a check. Fixes: 077dc4a27579 ("netfilter: nft_payload: extend offset to 65535 bytes") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_payload.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index ef2a80dfc68f..345eff140d56 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -224,11 +224,17 @@ static int nft_payload_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); + u32 offset; + int err; priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; + return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, priv->len); @@ -621,7 +627,8 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); - u32 base; + u32 base, offset; + int err; if (!tb[NFTA_PAYLOAD_BASE] || !tb[NFTA_PAYLOAD_OFFSET] || !tb[NFTA_PAYLOAD_LEN] || !tb[NFTA_PAYLOAD_DREG]) @@ -639,8 +646,11 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, } priv->base = base; - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, From bff1c8b49a9cb5c04af20f4e7d43bf4af5863bc6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:16:18 +0200 Subject: [PATCH 4129/5214] netfilter: nft_meta_bridge: add validate callback for get operations Blamed commit added NFT_META_BRI_IIFHWADDR to the set validate callback, yet this is a get operation. Add a get validate callback and move the NFT_META_BRI_IIFHWADDR key there. AFAICS this is harmless, NFT_META_BRI_IIFHWADDR can deal with a NULL input device and the set handler ignores a NFT_META_BRI_IIFHWADDR operation, but it allows to read 4 bytes off bridge skb->cb[]. Fixes: cbd2257dc96e ("netfilter: nft_meta_bridge: introduce NFT_META_BRI_IIFHWADDR support") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nft_meta.h | 2 ++ net/bridge/netfilter/nft_meta_bridge.c | 19 ++++++++++++++++++- net/netfilter/nft_meta.c | 5 +++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index f74e63290603..6cf1d910bbf8 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -40,6 +40,8 @@ void nft_meta_set_eval(const struct nft_expr *expr, void nft_meta_set_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr); +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr); diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 219c40680260..3d95f68e0906 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -107,12 +107,30 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, NULL, NFT_DATA_VALUE, len); } +static int nft_meta_bridge_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_meta *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->key) { + case NFT_META_BRI_IIFHWADDR: + hooks = 1 << NF_BR_PRE_ROUTING; + break; + default: + return nft_meta_get_validate(ctx, expr); + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_meta_bridge_type; static const struct nft_expr_ops nft_meta_bridge_get_ops = { .type = &nft_meta_bridge_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_bridge_get_eval, .init = nft_meta_bridge_get_init, + .validate = nft_meta_bridge_get_validate, .dump = nft_meta_get_dump, }; @@ -168,7 +186,6 @@ static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx, switch (priv->key) { case NFT_META_BRI_BROUTE: - case NFT_META_BRI_IIFHWADDR: hooks = 1 << NF_BR_PRE_ROUTING; break; default: diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 9b5821c64442..0a43e0787a68 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -635,8 +635,8 @@ static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) #endif } -static int nft_meta_get_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr) +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -652,6 +652,7 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_get_validate); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) From e409c23c2d0630f3b95efd12428b2e58800b7645 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:25:47 +0200 Subject: [PATCH 4130/5214] netfilter: nft_flow_offload: zero device address for non-ether case LLM points out that the skip causes unitialised stack array to propagate down into dev_fill_forward_path(). Its not clear to me that there is a guarantee that a later ctx.dev->netdev_ops->ndo_fill_forward_path() would always fix this up. Cc: Felix Fietkau Fixes: 45ca3e61999e ("netfilter: nft_flow_offload: skip dst neigh lookup for ppp devices") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_path.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index 1e7e216b9f89..98c03b487f52 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -53,8 +53,10 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route, struct neighbour *n; u8 nud_state; - if (!nft_is_valid_ether_device(dev)) + if (!nft_is_valid_ether_device(dev)) { + eth_zero_addr(ha); goto out; + } n = dst_neigh_lookup(dst_cache, daddr); if (!n) From af8d6ae09c0a5f8b8a0d5680203c74b3c1daa85b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 10:49:24 +0200 Subject: [PATCH 4131/5214] netfilter: nf_reject: skip iphdr options when looking for icmp header Not a big deal but this hould have used the real ip header length and not the base header size. As-is, if there are options then nf_skb_is_icmp_unreach() result will be random. Fixes: db99b2f2b3e2 ("netfilter: nf_reject: don't reply to icmp error messages") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index fecf6621f679..4626dc46808f 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -89,7 +89,7 @@ static bool nf_skb_is_icmp_unreach(const struct sk_buff *skb) if (iph->protocol != IPPROTO_ICMP) return false; - thoff = skb_network_offset(skb) + sizeof(*iph); + thoff = skb_network_offset(skb) + ip_hdrlen(skb); tp = skb_header_pointer(skb, thoff + offsetof(struct icmphdr, type), From b8b09dc2bf35a00d4e0556b5d6308c7b917ebda2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 18 Jun 2026 13:56:38 +0200 Subject: [PATCH 4132/5214] netfilter: nf_conntrack_expect: use conntrack GC to reap expectations This patch replaces the timer API by GC worker approach for expectations, as it already happened in many other subsystems. Use the existing conntrack GC worker to iterate over the local list of expectations in the master conntrack to reap expired expectations. Check IPS_HELPER_BIT to run GC for expectations, set it on for nft_ct expectation which nevers sets it. Hold the expectation spinlock while iterating over the master conntrack expectation list to synchronize with nf_ct_remove_expectations(). This also performs runtime packet path garbage collection through the expectation insertion and lookup functions while walking over one of the chains of the global expectation hashtables. Unconfirmed conntrack entries are skipped since ct->ext can be reallocated and dying are skipped since those will be gone soon. Set on IPS_HELPER_BIT if the helper ct extension is added, then the new GC worker does not need to bump the ct refcount to check if the ct->ext helper is available. This removes the extra bump on the refcount for expectation timers, this allows to remove several nf_ct_expect_put() calls after the unlink, after this update only refcount remains at 1 while on the expectation hashes. This patch implicitly addresses a race with the existing timer API allowing an expectation to access a stale exp->master pointer which has been already released when expectation removal loses races with an expiring timer, ie. timer_del() reporting false. Add a new NF_CT_EXPECT_DEAD flag to reap this expectation via GC. This is needed by nf_conntrack_unexpect_related() which is called in error paths to invalidate newly created expectations that has been added into the hashes. These expectactions cannot be inmediately released as GC or nf_ct_remove_expectations() could race to make it. On expectation insert, the runtime GC reaps stale expectations before checking the expectation limit set by policy. Set current timestamp in nf_ct_expect_alloc(), then add the expectation policy timeout (or custom timeout specified added on top of this) to specify the expectation lifetime. Fixes: bffcaad9afdf ("netfilter: ctnetlink: ensure safe access to master conntrack") Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 16 +- .../linux/netfilter/nf_conntrack_common.h | 1 + net/netfilter/nf_conntrack_core.c | 33 +++- net/netfilter/nf_conntrack_expect.c | 145 +++++++++--------- net/netfilter/nf_conntrack_h323_main.c | 4 +- net/netfilter/nf_conntrack_helper.c | 10 +- net/netfilter/nf_conntrack_netlink.c | 22 ++- net/netfilter/nf_conntrack_sip.c | 13 +- net/netfilter/nft_ct.c | 3 +- 9 files changed, 139 insertions(+), 108 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 80f50fd0f7ad..be4a120d549e 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -54,8 +54,8 @@ struct nf_conntrack_expect { /* The conntrack of the master connection */ struct nf_conn *master; - /* Timer function; deletes the expectation. */ - struct timer_list timeout; + /* jiffies32 when this expectation expires */ + u32 timeout; #if IS_ENABLED(CONFIG_NF_NAT) union nf_inet_addr saved_addr; @@ -69,6 +69,14 @@ struct nf_conntrack_expect { struct rcu_head rcu; }; +static inline bool nf_ct_exp_is_expired(const struct nf_conntrack_expect *exp) +{ + if (READ_ONCE(exp->flags) & NF_CT_EXPECT_DEAD) + return true; + + return (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) <= 0; +} + static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp) { return read_pnet(&exp->net); @@ -130,7 +138,6 @@ static inline void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) void nf_ct_remove_expectations(struct nf_conn *ct); void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); -bool nf_ct_remove_expect(struct nf_conntrack_expect *exp); void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data); void nf_ct_expect_iterate_net(struct net *net, @@ -153,5 +160,8 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect, return nf_ct_expect_related_report(expect, 0, 0, flags); } +struct nf_conn_help; +void nf_ct_expectation_gc(struct nf_conn_help *master_help); + #endif /*_NF_CONNTRACK_EXPECT_H*/ diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 56b6b60a814f..ee51045ae1d6 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -160,6 +160,7 @@ enum ip_conntrack_expect_events { #define NF_CT_EXPECT_USERSPACE 0x4 #ifdef __KERNEL__ +#define NF_CT_EXPECT_DEAD 0x8 #define NF_CT_EXPECT_MASK (NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE | \ NF_CT_EXPECT_USERSPACE) #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 4fb3a2d18631..784bd1d7a9bf 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1471,6 +1471,31 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct) return false; } +static void nf_ct_help_gc(struct nf_conn *ct) +{ + struct nf_conn_help *help; + + if (!refcount_inc_not_zero(&ct->ct_general.use)) + return; + + /* load ->status after refcount increase */ + smp_acquire__after_ctrl_dep(); + + if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct)) { + nf_ct_put(ct); + return; + } + + /* re-check helper due to SLAB_TYPESAFE_BY_RCU */ + if (test_bit(IPS_HELPER_BIT, &ct->status)) { + help = nfct_help(ct); + if (help) + nf_ct_expectation_gc(help); + } + + nf_ct_put(ct); +} + static void gc_worker(struct work_struct *work) { unsigned int i, hashsz, nf_conntrack_max95 = 0; @@ -1543,7 +1568,13 @@ static void gc_worker(struct work_struct *work) expires = (expires - (long)next_run) / ++count; next_run += expires; - if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp)) + if (gc_worker_skip_ct(tmp)) + continue; + + if (test_bit(IPS_HELPER_BIT, &tmp->status)) + nf_ct_help_gc(tmp); + + if (nf_conntrack_max95 == 0) continue; net = nf_ct_net(tmp); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 5c9b17835c28..49e18eda037e 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -43,6 +43,24 @@ unsigned int nf_ct_expect_max __read_mostly; static struct kmem_cache *nf_ct_expect_cachep __read_mostly; static siphash_aligned_key_t nf_ct_expect_hashrnd; +void nf_ct_expectation_gc(struct nf_conn_help *master_help) +{ + struct nf_conntrack_expect *exp; + struct hlist_node *next; + + if (hlist_empty(&master_help->expectations)) + return; + + spin_lock_bh(&nf_conntrack_expect_lock); + hlist_for_each_entry_safe(exp, next, &master_help->expectations, lnode) { + if (!nf_ct_exp_is_expired(exp)) + continue; + + nf_ct_unlink_expect(exp); + } + spin_unlock_bh(&nf_conntrack_expect_lock); +} + /* nf_conntrack_expect helper functions */ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, u32 portid, int report) @@ -52,7 +70,6 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, struct nf_conntrack_net *cnet; lockdep_nfct_expect_lock_held(); - WARN_ON_ONCE(timer_pending(&exp->timeout)); hlist_del_rcu(&exp->hnode); @@ -70,16 +87,6 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, } EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report); -static void nf_ct_expectation_timed_out(struct timer_list *t) -{ - struct nf_conntrack_expect *exp = timer_container_of(exp, t, timeout); - - spin_lock_bh(&nf_conntrack_expect_lock); - nf_ct_unlink_expect(exp); - spin_unlock_bh(&nf_conntrack_expect_lock); - nf_ct_expect_put(exp); -} - static unsigned int nf_ct_expect_dst_hash(const struct net *n, const struct nf_conntrack_tuple *tuple) { struct { @@ -117,19 +124,6 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple, nf_ct_exp_zone_equal_any(i, zone); } -bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) -{ - lockdep_nfct_expect_lock_held(); - - if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - return true; - } - return false; -} -EXPORT_SYMBOL_GPL(nf_ct_remove_expect); - struct nf_conntrack_expect * __nf_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, @@ -144,6 +138,8 @@ __nf_ct_expect_find(struct net *net, h = nf_ct_expect_dst_hash(net, tuple); hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) + continue; if (nf_ct_exp_equal(tuple, i, zone, net)) return i; } @@ -178,6 +174,7 @@ nf_ct_find_expectation(struct net *net, { struct nf_conntrack_net *cnet = nf_ct_pernet(net); struct nf_conntrack_expect *i, *exp = NULL; + struct hlist_node *next; unsigned int h; lockdep_nfct_expect_lock_held(); @@ -186,7 +183,11 @@ nf_ct_find_expectation(struct net *net, return NULL; h = nf_ct_expect_dst_hash(net, tuple); - hlist_for_each_entry(i, &nf_ct_expect_hash[h], hnode) { + hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) { + nf_ct_unlink_expect(i); + continue; + } if (!(i->flags & NF_CT_EXPECT_INACTIVE) && nf_ct_exp_equal(tuple, i, zone, net)) { exp = i; @@ -196,13 +197,16 @@ nf_ct_find_expectation(struct net *net, if (!exp) return NULL; + if (!refcount_inc_not_zero(&exp->use)) + return NULL; + /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ if (!nf_ct_is_confirmed(exp->master)) - return NULL; + goto err_release_exp; /* Avoid race with other CPUs, that for exp->master ct, is * about to invoke ->destroy(), or nf_ct_delete() via timeout @@ -214,18 +218,17 @@ nf_ct_find_expectation(struct net *net, */ if (unlikely(nf_ct_is_dying(exp->master) || !refcount_inc_not_zero(&exp->master->ct_general.use))) - return NULL; + goto err_release_exp; - if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) { - refcount_inc(&exp->use); + if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) return exp; - } else if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect(exp); - return exp; - } - /* Undo exp->master refcnt increase, if timer_delete() failed */ - nf_ct_put(exp->master); + nf_ct_unlink_expect(exp); + + return exp; + +err_release_exp: + nf_ct_expect_put(exp); return NULL; } @@ -241,9 +244,8 @@ void nf_ct_remove_expectations(struct nf_conn *ct) return; spin_lock_bh(&nf_conntrack_expect_lock); - hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { - nf_ct_remove_expect(exp); - } + hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) + nf_ct_unlink_expect(exp); spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_remove_expectations); @@ -292,7 +294,7 @@ static bool master_matches(const struct nf_conntrack_expect *a, void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) { spin_lock_bh(&nf_conntrack_expect_lock); - nf_ct_remove_expect(exp); + WRITE_ONCE(exp->flags, exp->flags | NF_CT_EXPECT_DEAD); spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); @@ -308,6 +310,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) if (!new) return NULL; + new->timeout = nfct_time_stamp; new->master = me; refcount_set(&new->use, 1); return new; @@ -413,17 +416,12 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, struct net *net = nf_ct_exp_net(exp); unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); - /* two references : one for hash insert, one for the timer */ - refcount_add(2, &exp->use); + refcount_inc(&exp->use); - timer_setup(&exp->timeout, nf_ct_expectation_timed_out, 0); helper = rcu_dereference_protected(master_help->helper, lockdep_is_held(&nf_conntrack_expect_lock)); - if (helper) { - exp->timeout.expires = jiffies + - helper->expect_policy[exp->class].timeout * HZ; - } - add_timer(&exp->timeout); + if (helper) + exp->timeout += helper->expect_policy[exp->class].timeout * HZ; hlist_add_head_rcu(&exp->lnode, &master_help->expectations); master_help->expecting[exp->class]++; @@ -435,19 +433,26 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, NF_CT_STAT_INC(net, expect_create); } -/* Race with expectations being used means we could have none to find; OK. */ static void evict_oldest_expect(struct nf_conn_help *master_help, - struct nf_conntrack_expect *new) + struct nf_conntrack_expect *new, + const struct nf_conntrack_expect_policy *p) { struct nf_conntrack_expect *exp, *last = NULL; + struct hlist_node *next; - hlist_for_each_entry(exp, &master_help->expectations, lnode) { + hlist_for_each_entry_safe(exp, next, &master_help->expectations, lnode) { + if (nf_ct_exp_is_expired(exp)) { + nf_ct_unlink_expect(exp); + continue; + } if (exp->class == new->class) last = exp; } - if (last) - nf_ct_remove_expect(last); + /* Still worth to evict oldest expectation after garbage collection? */ + if (last && + master_help->expecting[last->class] >= p->max_expected) + nf_ct_unlink_expect(last); } static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, @@ -467,14 +472,18 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, h = nf_ct_expect_dst_hash(net, &expect->tuple); hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { + if (nf_ct_exp_is_expired(i)) { + nf_ct_unlink_expect(i); + continue; + } if (master_matches(i, expect, flags) && expect_matches(i, expect)) { if (i->class != expect->class || i->master != expect->master) return -EALREADY; - if (nf_ct_remove_expect(i)) - break; + nf_ct_unlink_expect(i); + break; } else if (expect_clash(i, expect)) { ret = -EBUSY; goto out; @@ -486,14 +495,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, if (helper) { p = &helper->expect_policy[expect->class]; if (p->max_expected && - master_help->expecting[expect->class] >= p->max_expected) { - evict_oldest_expect(master_help, expect); - if (master_help->expecting[expect->class] - >= p->max_expected) { - ret = -EMFILE; - goto out; - } - } + master_help->expecting[expect->class] >= p->max_expected) + evict_oldest_expect(master_help, expect, p); } cnet = nf_ct_pernet(net); @@ -547,10 +550,8 @@ void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, vo hlist_for_each_entry_safe(exp, next, &nf_ct_expect_hash[i], hnode) { - if (iter(exp, data) && timer_delete(&exp->timeout)) { + if (iter(exp, data)) nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - } } } @@ -577,10 +578,8 @@ void nf_ct_expect_iterate_net(struct net *net, if (!net_eq(nf_ct_exp_net(exp), net)) continue; - if (iter(exp, data) && timer_delete(&exp->timeout)) { + if (iter(exp, data)) nf_ct_unlink_expect_report(exp, portid, report); - nf_ct_expect_put(exp); - } } } @@ -657,17 +656,17 @@ static int exp_seq_show(struct seq_file *s, void *v) struct net *net = seq_file_net(s); struct hlist_node *n = v; char *delim = ""; + __s32 timeout; expect = hlist_entry(n, struct nf_conntrack_expect, hnode); if (!net_eq(nf_ct_exp_net(expect), net)) return 0; + if (nf_ct_exp_is_expired(expect)) + return 0; - if (expect->timeout.function) - seq_printf(s, "%ld ", timer_pending(&expect->timeout) - ? (long)(expect->timeout.expires - jiffies)/HZ : 0); - else - seq_puts(s, "- "); + timeout = (__s32)(READ_ONCE(expect->timeout) - nfct_time_stamp) / HZ; + seq_printf(s, "%d ", timeout > 0 ? timeout : 0); seq_printf(s, "l3proto = %u proto=%u ", expect->tuple.src.l3num, expect->tuple.dst.protonum); diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 7f189dceb3c4..24931e379985 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1388,8 +1388,8 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, "timeout to %u seconds for", info->timeout); nf_ct_dump_tuple(&exp->tuple); - mod_timer_pending(&exp->timeout, - jiffies + info->timeout * HZ); + WRITE_ONCE(exp->timeout, + nfct_time_stamp + (info->timeout * HZ)); } spin_unlock_bh(&nf_conntrack_expect_lock); } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 2f35bdd0d7d7..8b94001c2430 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -181,10 +181,10 @@ nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) struct nf_conn_help *help; help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); - if (help) + if (help) { + __set_bit(IPS_HELPER_BIT, &ct->status); INIT_HLIST_HEAD(&help->expectations); - else - pr_debug("failed to add helper extension area"); + } return help; } EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); @@ -203,10 +203,8 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, return 0; help = nfct_help(tmpl); - if (help != NULL) { + if (help) helper = rcu_dereference(help->helper); - set_bit(IPS_HELPER_BIT, &ct->status); - } help = nfct_help(ct); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index b429e648f06c..4e78d2482989 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3014,8 +3014,8 @@ static int ctnetlink_exp_dump_expect(struct sk_buff *skb, const struct nf_conntrack_expect *exp) { + __s32 timeout = (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) / HZ; struct nf_conn *master = exp->master; - long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ; struct nf_conntrack_helper *helper; #if IS_ENABLED(CONFIG_NF_NAT) struct nlattr *nest_parms; @@ -3178,6 +3178,9 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) restart: hlist_for_each_entry_rcu(exp, &nf_ct_expect_hash[cb->args[0]], hnode) { + if (nf_ct_exp_is_expired(exp)) + continue; + if (l3proto && exp->tuple.src.l3num != l3proto) continue; @@ -3456,11 +3459,8 @@ static int ctnetlink_del_expect(struct sk_buff *skb, } /* after list removal, usage count == 1 */ - if (timer_delete(&exp->timeout)) { - nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, - nlmsg_report(info->nlh)); - nf_ct_expect_put(exp); - } + nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, + nlmsg_report(info->nlh)); spin_unlock_bh(&nf_conntrack_expect_lock); /* have to put what we 'get' above. * after this line usage count == 0 */ @@ -3484,14 +3484,10 @@ static int ctnetlink_change_expect(struct nf_conntrack_expect *x, const struct nlattr * const cda[]) { - if (cda[CTA_EXPECT_TIMEOUT]) { - if (!timer_delete(&x->timeout)) - return -ETIME; + if (cda[CTA_EXPECT_TIMEOUT]) + WRITE_ONCE(x->timeout, nfct_time_stamp + + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ); - x->timeout.expires = jiffies + - ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ; - add_timer(&x->timeout); - } return 0; } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index c606d1f60b58..5ec3a4a4bbd7 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -897,11 +897,10 @@ static int refresh_signalling_expectation(struct nf_conn *ct, exp->tuple.dst.protonum != proto || exp->tuple.dst.u.udp.port != port) continue; - if (mod_timer_pending(&exp->timeout, jiffies + expires * HZ)) { - exp->flags &= ~NF_CT_EXPECT_INACTIVE; - found = 1; - break; - } + WRITE_ONCE(exp->timeout, nfct_time_stamp + (expires * HZ)); + WRITE_ONCE(exp->flags, exp->flags & ~NF_CT_EXPECT_INACTIVE); + found = 1; + break; } spin_unlock_bh(&nf_conntrack_expect_lock); return found; @@ -920,8 +919,7 @@ static void flush_expectations(struct nf_conn *ct, bool media) hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) continue; - if (!nf_ct_remove_expect(exp)) - continue; + nf_ct_unlink_expect(exp); if (!media) break; } @@ -1413,7 +1411,6 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), saddr, &daddr, proto, NULL, &port); - exp->timeout.expires = sip_timeout * HZ; rcu_assign_pointer(exp->assign_helper, helper); exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 25934c6f01fb..958054dd2e2e 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1145,7 +1145,6 @@ static void nft_ct_helper_obj_eval(struct nft_object *obj, help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); if (help && refcount_inc_not_zero(&to_assign->ct_refcnt)) { rcu_assign_pointer(help->helper, to_assign); - set_bit(IPS_HELPER_BIT, &ct->status); if ((ct->status & IPS_NAT_MASK) && !nfct_seqadj(ct)) if (!nfct_seqadj_ext_add(ct)) @@ -1326,7 +1325,7 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, priv->l4proto, NULL, &priv->dport); - exp->timeout.expires = jiffies + priv->timeout * HZ; + exp->timeout += priv->timeout * HZ; if (nf_ct_expect_related(exp, 0) != 0) regs->verdict.code = NF_DROP; From 27dd2997746d54ebc079bb13161cc1bdd401d4a6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 19 Jun 2026 00:34:49 +0200 Subject: [PATCH 4133/5214] netfilter: nft_meta_bridge: fix NFT_META_BRI_IIFPVID stack leak This needs to test for nonzero retval. Fixes: c54c7c685494 ("netfilter: nft_meta_bridge: add NFT_META_BRI_IIFPVID support") Closes: https://sashiko.dev/#/patchset/20260618061631.21919-1-fw%40strlen.de Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nft_meta_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 3d95f68e0906..e4c9aa1f64e2 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -44,7 +44,9 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr, if (!br_dev || !br_vlan_enabled(br_dev)) goto err; - br_vlan_get_pvid_rcu(in, &p_pvid); + if (br_vlan_get_pvid_rcu(in, &p_pvid)) + goto err; + nft_reg_store16(dest, p_pvid); return; } From ebc242f78c52ef9de0e650bf48f64eb110351a4f Mon Sep 17 00:00:00 2001 From: David Windsor Date: Tue, 5 May 2026 16:27:38 -0400 Subject: [PATCH 4134/5214] tpm: svsm: constify tpm_chip_ops Constify the SVSM vTPM ops. It is statically initialized and never written to, so let's store it in .rodata. Every other tpm_class_ops instance in drivers/char/tpm/ is already const. Signed-off-by: David Windsor Reviewed-by: Stefano Garzarella Link: https://lore.kernel.org/r/20260505202738.145800-1-dwindsor@gmail.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_svsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c index f5ba0f64850b..b74d60f687d5 100644 --- a/drivers/char/tpm/tpm_svsm.c +++ b/drivers/char/tpm/tpm_svsm.c @@ -49,7 +49,7 @@ static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz); } -static struct tpm_class_ops tpm_chip_ops = { +static const struct tpm_class_ops tpm_chip_ops = { .flags = TPM_OPS_AUTO_STARTUP, .send = tpm_svsm_send, }; From de59d78e64039baa5fed455ddb905ba8263e7ede Mon Sep 17 00:00:00 2001 From: Baoli Zhang Date: Tue, 21 Apr 2026 08:50:20 +0800 Subject: [PATCH 4135/5214] tpm: restore timeout for key creation commands Commit 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") inadvertently reduced the timeout for TPM2 key creation commands (`CREATE_PRIMARY`, `CREATE`, `CREATE_LOADED`) from 300 seconds to 30 seconds. This causes intermittent timeout failures, with several failures observed across hundreds of test runs on some Intel platforms using Infineon SLB9670 and SLB9672 TPM modules. Restore the timeout to 300 seconds to avoid spurious failures. Cc: stable@vger.kernel.org # v6.18+ Fixes: 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") Co-developed-by: Lili Li Signed-off-by: Lili Li Signed-off-by: Baoli Zhang Link: https://lore.kernel.org/r/20260421005021.13765-1-baoli.zhang@linux.intel.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm2-cmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index b11e6fa8b740..52ee350da867 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -71,9 +71,9 @@ static const struct { {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000}, {TPM2_CC_GET_CAPABILITY, 750}, {TPM2_CC_NV_READ, 2000}, - {TPM2_CC_CREATE_PRIMARY, 30000}, - {TPM2_CC_CREATE, 30000}, - {TPM2_CC_CREATE_LOADED, 30000}, + {TPM2_CC_CREATE_PRIMARY, 300000}, + {TPM2_CC_CREATE, 300000}, + {TPM2_CC_CREATE_LOADED, 300000}, }; /** From 595ca21f797e43da24cb80529fb8b29381ed8716 Mon Sep 17 00:00:00 2001 From: Gunnar Kudrjavets Date: Sun, 10 May 2026 17:11:27 +0000 Subject: [PATCH 4136/5214] tpm: Initialize name_size_alg for non-NULL name in tpm_buf_append_name() tpm_buf_append_name() supports callers passing a pre-computed name for handles. When name is non-NULL, the code skips the tpm2_read_public() path but leaves name_size_alg uninitialized before it is used as the memcpy size argument. No current in-tree caller passes a non-NULL name, but future use cases such as name caching would exercise this path. Initialize name_size_alg by calling name_size() on the caller-provided name, sharing the error check and assignment with the existing tpm2_read_public() path. This prevents unmasking a latent bug when the non-NULL name path is eventually used. Assisted-by: Kiro:claude-opus-4.6 Reviewed-by: Justinien Bouron Reviewed-by: Muhammad Hammad Ijaz Signed-off-by: Gunnar Kudrjavets Link: https://lore.kernel.org/r/20260510171152.4607-1-gunnarku@amazon.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm2-sessions.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index c4da6fde748f..795cd99dc6fe 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -285,11 +285,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf, mso == TPM2_MSO_NVRAM) { if (!name) { ret = tpm2_read_public(chip, handle, auth->name[slot]); - if (ret < 0) - goto err; - - name_size_alg = ret; + } else { + ret = name_size(name); } + + if (ret < 0) + goto err; + + name_size_alg = ret; } else { if (name) { dev_err(&chip->dev, "handle 0x%08x does not use a name\n", From c0c9cfb3b75def8bf200a2d4db09015806acfeaf Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 9 May 2026 21:51:07 +0300 Subject: [PATCH 4137/5214] tpm: tpm_tis_spi: Use wait_woken() in wait_for_tmp_stat() wait_event_interruptible_timeout() evaluates its condition after setting the current task state to TASK_INTERRUPTIBLE. With CONFIG_DEBUG_ATOMIC_SLEEP this triggers a warning when the IRQ wait path is used: tpm_tis_status() tpm_tis_spi_read_bytes() tpm_tis_spi_transfer_full() spi_bus_lock() mutex_lock() Address this with the following measures: 1. Call wait_tpm_stat_cond() only while tasking is running. 2. Use wait_woken() to wait for changes. Cc: stable@vger.kernel.org # v4.19+ Cc: Linus Walleij Reported-by: Stefan Wahren Closes: https://lore.kernel.org/linux-integrity/6964bec7-3dbb-453b-89ef-9b990217a8b9@gmx.net/ Fixes: 1a339b658d9d ("tpm_tis_spi: Pass the SPI IRQ down to the driver") Reviewed-by: Linus Walleij Tested-by: Stefan Wahren Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 35 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 21d79ad3b164..153a57c79240 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, bool check_cancel) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + DEFINE_WAIT_FUNC(wait, woken_wake_function); unsigned long stop; - long rc; u8 status; bool canceled = false; u8 sts_mask; @@ -87,23 +87,30 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, /* process status changes with irq support */ if (sts_mask) { ret = -ETIME; + add_wait_queue(queue, &wait); again: + if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, + &canceled)) { + ret = canceled ? -ECANCELED : 0; + goto out; + } + timeout = stop - jiffies; if ((long)timeout <= 0) - return -ETIME; - rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, - &canceled), - timeout); - if (rc > 0) { - if (canceled) - return -ECANCELED; - ret = 0; - } - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; + goto out; + + if (signal_pending(current)) { + if (freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } + goto out; } + + wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); + goto again; +out: + remove_wait_queue(queue, &wait); } if (ret) From ddd33806b8911fa2ef849e8bbbab1e3fcb26adc0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:16:23 +0200 Subject: [PATCH 4138/5214] tpm_crb: Check ACPI_COMPANION() against NULL during probe Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the tpm_crb driver. Fixes: 48fe2cddc85c ("tpm_crb: Convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/2848144.mvXUDI8C0e@rafael.j.wysocki Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_crb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 7d1377e8e616..ceb4100ba400 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -786,8 +786,8 @@ static int crb_map_pluton(struct device *dev, struct crb_priv *priv, static int crb_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acpi_device *device = ACPI_COMPANION(dev); struct acpi_table_tpm2 *buf; + struct acpi_device *device; struct crb_priv *priv; struct tpm_chip *chip; struct tpm2_crb_smc *crb_smc; @@ -797,6 +797,10 @@ static int crb_acpi_probe(struct platform_device *pdev) u32 sm; int rc; + device = ACPI_COMPANION(dev); + if (!device) + return -ENODEV; + status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status) || buf->header.length < sizeof(*buf)) { From c4d52950536bb421eaf11d83c0ba8612c443bb20 Mon Sep 17 00:00:00 2001 From: Jim Broadus Date: Tue, 26 May 2026 16:22:43 -0700 Subject: [PATCH 4139/5214] tpm: tpm_tis: store entire did_vid The entire 32 bit did_vid is read from the device, but only the 16 bit vendor id portion was stored in the tpm_tis_data structure. Storing the entire value allows the device id to be used to handle quirks. Printing the vid and did in the error case also helps identify problem devices. Signed-off-by: Jim Broadus Link: https://lore.kernel.org/r/20260526232245.5409-2-jbroadus@gmail.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 22 ++++++++++++++-------- drivers/char/tpm/tpm_tis_core.h | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 153a57c79240..3408b531d3b8 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -799,9 +799,10 @@ static int probe_itpm(struct tpm_chip *chip) static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u16 vendor_id = priv->did_vid; if (!test_bit(TPM_TIS_DEFAULT_CANCELLATION, &priv->flags)) { - switch (priv->manufacturer_id) { + switch (vendor_id) { case TPM_VID_WINBOND: return ((status == TPM_STS_VALID) || (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY))); @@ -1122,7 +1123,8 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, const struct tpm_tis_phy_ops *phy_ops, acpi_handle acpi_dev_handle) { - u32 vendor; + u16 vendor_id; + u16 device_id; u32 intfcaps; u32 intmask; u32 clkrun_val; @@ -1155,19 +1157,20 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, dev_set_drvdata(&chip->dev, priv); - rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &priv->did_vid); if (rc < 0) return rc; - priv->manufacturer_id = vendor; + vendor_id = priv->did_vid; + device_id = priv->did_vid >> 16; - if (priv->manufacturer_id == TPM_VID_ATML && + if (vendor_id == TPM_VID_ATML && !(chip->flags & TPM_CHIP_FLAG_TPM2)) { priv->timeout_min = TIS_TIMEOUT_MIN_ATML; priv->timeout_max = TIS_TIMEOUT_MAX_ATML; } - if (priv->manufacturer_id == TPM_VID_IFX) + if (vendor_id == TPM_VID_IFX) set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags); if (is_bsw()) { @@ -1254,9 +1257,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (rc < 0) goto out_err; - dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", + dev_info(dev, "%s TPM (vendor-id 0x%X, device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, rid); + vendor_id, device_id, rid); probe = probe_itpm(chip); if (probe < 0) { @@ -1322,6 +1325,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, return 0; out_err: + dev_err(dev, "TPM vid 0x%X, did 0x%X init failed with error %d\n", + vendor_id, device_id, rc); + if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 6c3aa480396b..f2c77844062a 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -94,7 +94,7 @@ enum tpm_tis_flags { struct tpm_tis_data { struct tpm_chip *chip; - u16 manufacturer_id; + u32 did_vid; struct mutex locality_count_mutex; unsigned int locality_count; int locality; From 661f4d304960e3b093fae5211504e0e8c9fd4f23 Mon Sep 17 00:00:00 2001 From: Jim Broadus Date: Tue, 26 May 2026 16:22:44 -0700 Subject: [PATCH 4140/5214] tpm: tpm_tis: Add settle time for some TPMs Some TPMs fail to grant locality when requested immediately after being relinquished. In this case, the TPM_ACCESS_REQUEST_USE bit of the TPM_ACCESS register is cleared immediately without setting TPM_ACCESS_ACTIVE_LOCALITY. This issue can be seen at boot since tpm_chip_start, called right after locality is relinquished, will fail. This causes the probe to fail: tpm_tis MSFT0101:00: probe with driver tpm_tis failed with error -1 This occurs on some older Dell Latitudes. For the Nuvoton TPM used in these machines, add a delay after locality is relinquished. Signed-off-by: Jim Broadus Link: https://lore.kernel.org/r/20260526232245.5409-3-jbroadus@gmail.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 6 ++++++ drivers/char/tpm/tpm_tis_core.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 3408b531d3b8..143ef160820e 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -178,6 +178,9 @@ static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l) { tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + if (test_bit(TPM_TIS_SETTLE_AFTER_RELINQUISH, &priv->flags)) + tpm_msleep(TPM_TIMEOUT); + return 0; } @@ -1173,6 +1176,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (vendor_id == TPM_VID_IFX) set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags); + if (vendor_id == TPM_VID_WINBOND && device_id == 0x00FE) + set_bit(TPM_TIS_SETTLE_AFTER_RELINQUISH, &priv->flags); + if (is_bsw()) { priv->ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR, ILB_REMAP_SIZE); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index f2c77844062a..aa6d78898ef3 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -90,6 +90,7 @@ enum tpm_tis_flags { TPM_TIS_DEFAULT_CANCELLATION = 2, TPM_TIS_IRQ_TESTED = 3, TPM_TIS_STATUS_VALID_RETRY = 4, + TPM_TIS_SETTLE_AFTER_RELINQUISH = 5, }; struct tpm_tis_data { From 73851a7c43dfa52d2ed9415889b33daf85da0ed9 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 31 May 2026 08:44:28 -0400 Subject: [PATCH 4141/5214] tpm: tpm2-sessions: wait for async KPP completion in tpm_buf_append_salt tpm_buf_append_salt() in drivers/char/tpm/tpm2-sessions.c calls crypto_kpp_generate_public_key() and crypto_kpp_compute_shared_secret() without installing a completion callback, discards both return values, and immediately frees the kpp_request via kpp_request_free(). When the resolved ecdh-nist-p256 KPP backend is asynchronous (atmel-ecc, HPRE, keembay-ocs), either operation returns -EINPROGRESS and the deferred completion worker dereferences the freed request. The path fires automatically from the hwrng_fillfn kernel thread via tpm_get_random -> tpm2_get_random -> tpm2_start_auth_session -> tpm_buf_append_salt on every entropy poll, without any userland action. Install crypto_req_done as the completion callback, wrap both KPP operations in crypto_wait_req(), and propagate errors to the caller. The wait is a no-op for synchronous backends. Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API") Cc: stable@vger.kernel.org # v6.10+ Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm2-sessions.c | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 795cd99dc6fe..a1ae5e1829cb 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -492,15 +492,17 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, sha256_final(&sctx, out); } -static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, - struct tpm2_auth *auth) +static int tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, + struct tpm2_auth *auth) { struct crypto_kpp *kpp; struct kpp_request *req; + DECLARE_CRYPTO_WAIT(wait); struct scatterlist s[2], d[1]; struct ecdh p = {0}; u8 encoded_key[EC_PT_SZ], *x, *y; unsigned int buf_len; + int rc; /* secret is two sized points */ tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2); @@ -523,14 +525,15 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(kpp)) { dev_err(&chip->dev, "crypto ecdh allocation failed\n"); - return; + return PTR_ERR(kpp); } buf_len = crypto_ecdh_key_len(&p); if (sizeof(encoded_key) < buf_len) { dev_err(&chip->dev, "salt buffer too small needs %d\n", buf_len); - goto out; + rc = -EINVAL; + goto err_free_kpp; } crypto_ecdh_encode_key(encoded_key, buf_len, &p); /* this generates a random private key */ @@ -538,11 +541,17 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, /* salt is now the public point of this private key */ req = kpp_request_alloc(kpp, GFP_KERNEL); - if (!req) - goto out; + if (!req) { + rc = -ENOMEM; + goto err_free_kpp; + } + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, s, EC_PT_SZ*2); - crypto_kpp_generate_public_key(req); + rc = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); + if (rc) + goto err_free_req; /* * we're not done: now we have to compute the shared secret * which is our private key multiplied by the tpm_key public @@ -554,8 +563,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp_request_set_input(req, s, EC_PT_SZ*2); sg_init_one(d, auth->salt, EC_PT_SZ); kpp_request_set_output(req, d, EC_PT_SZ); - crypto_kpp_compute_shared_secret(req); - kpp_request_free(req); + rc = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); + if (rc) + goto err_free_req; /* * pass the shared secret through KDFe for salt. Note salt @@ -565,8 +575,16 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, */ tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt); - out: + kpp_request_free(req); crypto_free_kpp(kpp); + return 0; + +err_free_req: + kpp_request_free(req); + +err_free_kpp: + crypto_free_kpp(kpp); + return rc; } /** @@ -1021,7 +1039,12 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); /* append encrypted salt and squirrel away unencrypted in auth */ - tpm_buf_append_salt(&buf, chip, auth); + rc = tpm_buf_append_salt(&buf, chip, auth); + if (rc) { + tpm2_flush_context(chip, null_key); + tpm_buf_destroy(&buf); + goto out; + } /* session type (HMAC, audit or policy) */ tpm_buf_append_u8(&buf, TPM2_SE_HMAC); From 677042afb97ac5057e1d2900139f123bb15ba6e6 Mon Sep 17 00:00:00 2001 From: Yeoreum Yun Date: Fri, 5 Jun 2026 15:43:25 +0100 Subject: [PATCH 4142/5214] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's built-in") probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA. However, IMA now provides the IMA_INIT_LATE_SYNC build option, which initialises IMA at the late_initcall_sync level, so this change is no longer required. Signed-off-by: Yeoreum Yun Link: https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/commit/?h=for-next/ffa/updates&id=cc7e8f21b9f0c229d68cf19a837cba82b5ac2d87 [0] Link: https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/commit/?h=for-next/ffa/updates&id=e659fc8e537c7a21d5d693d6f30d8852f2fa8d91 [1] Link: https://lore.kernel.org/r/20260605144325.434436-5-yeoreum.yun@arm.com Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_crb_ffa.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c index 99f1c1e5644b..025c4d4b17ca 100644 --- a/drivers/char/tpm/tpm_crb_ffa.c +++ b/drivers/char/tpm/tpm_crb_ffa.c @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno) */ int tpm_crb_ffa_init(void) { - int ret = 0; - - if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) { - ret = ffa_register(&tpm_crb_ffa_driver); - if (ret) { - tpm_crb_ffa = ERR_PTR(-ENODEV); - return ret; - } - } - if (!tpm_crb_ffa) - ret = -ENOENT; + return -ENOENT; if (IS_ERR_VALUE(tpm_crb_ffa)) - ret = -ENODEV; + return -ENODEV; - return ret; + return 0; } EXPORT_SYMBOL_GPL(tpm_crb_ffa_init); @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = { .id_table = tpm_crb_ffa_device_id, }; -#ifdef MODULE module_ffa_driver(tpm_crb_ffa_driver); -#endif MODULE_AUTHOR("Arm"); MODULE_DESCRIPTION("TPM CRB FFA driver"); From 1a58f6115bfb34eabcc7de8a3a9745b219179781 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 15 Jun 2026 15:02:05 +0300 Subject: [PATCH 4143/5214] tpm: fix event_size output in tpm1_binary_bios_measurements_show Commit 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") split the output to write the endian-converted event header first and then the variable-length event data. However, the split was at sizeof(struct tcpa_event) - 1, even though event_data was a zero-length array, and later a flexible array member, both of which already excluded the event data. Therefore, the current code writes the first three bytes of event_size from the endian-converted header and then the last byte from the raw header, which can emit a corrupted event_size on PPC64, where do_endian_conversion() maps to be32_to_cpu(). Split one byte later to write the full endian-converted header first, followed by the variable-length event->event_data. Fixes: 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") Cc: stable@vger.kernel.org # v5.10+ Signed-off-by: Thorsten Blum Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/tpm1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index e7913b2853d5..0397e3361020 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v) temp_ptr = (char *) &temp_event; - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++) + for (i = 0; i < sizeof(struct tcpa_event); i++) seq_putc(m, temp_ptr[i]); temp_ptr = (char *) v; - for (i = (sizeof(struct tcpa_event) - 1); + for (i = sizeof(struct tcpa_event); i < (sizeof(struct tcpa_event) + temp_event.event_size); i++) seq_putc(m, temp_ptr[i]); From 82ef9a635d7130ca27ec9dd88c16afc39c83a4e8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:26 +0200 Subject: [PATCH 4144/5214] mailbox: imx: Forward the timeout/ error in imx_mu_generic_tx() imx_mu_generic_tx() for the IMX_MU_TYPE_TXDB_V2 type polls on a register which may timeout and is recognized as an error. This error is siltently dropped and not dropped to the caller. Forward the error to the caller. Fixes: b5ef17917f3a7 ("mailbox: imx: fix TXDB_V2 channel race condition") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 246a9a9e3952..0028073be4a7 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -227,6 +227,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, u32 val; int ret, count; + ret = 0; switch (cp->type) { case IMX_MU_TYPE_TX: imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); @@ -259,7 +260,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, return -EINVAL; } - return 0; + return ret; } static int imx_mu_generic_rx(struct imx_mu_priv *priv, From 5ccea7eacb7786c358833634f45700365f6c1d99 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:27 +0200 Subject: [PATCH 4145/5214] mailbox: imx: Add a channel shutdown field sashiko complained about possible teardown problem. The scenario CPU 0 CPU 1 imx_mu_isr() imx_mu_shutdown() imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); imx_mu_specific_rx() imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); free_irq() The RX event remains enabled because in this short window the RX event was disabled in ->shutdown() while the interrupt was active and then enabled again by the ISR while ->shutdown waited in free_irq(). This race requires timing and if happens can be problematic on shared handlers if the "removed" channel triggers an interrupt. In this case the irq-core will shutdown the interrupt with the "nobody cared" message. Introduce imx_mu_con_priv::shutdown to signal that the channel is shutting down. This flag is set with the lock held (by imx_mu_xcr_clr_shut()). The unmask side uses imx_mu_xcr_set_act() which only enables the event if the channel has not been shutdown and serialises on the same lock. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 40 +++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 0028073be4a7..e261b382d5c9 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -80,6 +80,7 @@ struct imx_mu_con_priv { enum imx_mu_chan_type type; struct mbox_chan *chan; struct work_struct txdb_work; + bool shutdown; }; struct imx_mu_priv { @@ -219,6 +220,36 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 se return val; } +static void imx_mu_xcr_clr_shut(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, + enum imx_mu_xcr type, u32 clr) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->xcr_lock, flags); + cp->shutdown = true; + + val = imx_mu_read(priv, priv->dcfg->xCR[type]); + val &= ~clr; + imx_mu_write(priv, val, priv->dcfg->xCR[type]); + spin_unlock_irqrestore(&priv->xcr_lock, flags); +} + +static void imx_mu_xcr_set_act(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, + enum imx_mu_xcr type, u32 set) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->xcr_lock, flags); + if (!cp->shutdown) { + val = imx_mu_read(priv, priv->dcfg->xCR[type]); + val |= set; + imx_mu_write(priv, val, priv->dcfg->xCR[type]); + } + spin_unlock_irqrestore(&priv->xcr_lock, flags); +} + static int imx_mu_generic_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data) @@ -377,7 +408,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; @@ -605,6 +636,7 @@ static int imx_mu_startup(struct mbox_chan *chan) return ret; } + cp->shutdown = false; switch (cp->type) { case IMX_MU_TYPE_RX: imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0); @@ -639,13 +671,13 @@ static void imx_mu_shutdown(struct mbox_chan *chan) switch (cp->type) { case IMX_MU_TYPE_TX: - imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RX: - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RXDB: - imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RST: imx_mu_xcr_rmw(priv, IMX_MU_CR, IMX_MU_xCR_RST(priv->dcfg->type), 0); From 1f602619e408b6e9655ee76656a2a5ab6e89c5e4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:28 +0200 Subject: [PATCH 4146/5214] mailbox: imx: Use devm_pm_runtime_enable() sashiko complained about early usage of the device while probe isn't completed. This can be mitigated by delaying the pm_runtime_enable() into the removal path instead doing it early. This ensures that in an error case the device is removed (and imx_mu_shutdown()) before pm_runtime_disable() so we don't have to do this manually. For the order to work, lets move devm_mbox_controller_register() until after the pm-runtime part. So the reverse order will be mbox-controller removal followed by disabling pm runtime. Use devm_pm_runtime_enable(), remove manual pm_runtime_disable() invocations and move the pm_runtime handling in probe before devm_mbox_controller_register(). Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index e261b382d5c9..516a05b64daa 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -966,38 +966,36 @@ static int imx_mu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = devm_mbox_controller_register(dev, &priv->mbox); - if (ret) + ret = devm_pm_runtime_enable(dev); + if (ret < 0) goto disable_clk; - of_platform_populate(dev->of_node, NULL, NULL, dev); - - pm_runtime_enable(dev); - ret = pm_runtime_resume_and_get(dev); if (ret < 0) - goto disable_runtime_pm; + goto disable_clk; ret = pm_runtime_put_sync(dev); if (ret < 0) - goto disable_runtime_pm; + goto disable_clk; clk_disable_unprepare(priv->clk); + ret = devm_mbox_controller_register(dev, &priv->mbox); + if (ret) + goto err_out; + + of_platform_populate(dev->of_node, NULL, NULL, dev); + return 0; -disable_runtime_pm: - pm_runtime_disable(dev); disable_clk: clk_disable_unprepare(priv->clk); +err_out: return ret; } static void imx_mu_remove(struct platform_device *pdev) { - struct imx_mu_priv *priv = platform_get_drvdata(pdev); - - pm_runtime_disable(priv->dev); } static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { From dd1b321e8024fb01404fe163076c9010c5df8608 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:29 +0200 Subject: [PATCH 4147/5214] mailbox: imx: use devm_of_platform_populate() The driver uses of_platform_populate() but does not remove the added devices on removal. This can lead to "double devices" on module removal followed by adding the module again. Use devm_of_platform_populate() to remove the populated devices once the parent device is removed. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 516a05b64daa..a8d4e970fb78 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -984,7 +984,7 @@ static int imx_mu_probe(struct platform_device *pdev) if (ret) goto err_out; - of_platform_populate(dev->of_node, NULL, NULL, dev); + devm_of_platform_populate(dev); return 0; From fbc0f319cee18f40ae3f8658086217c655ad2489 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:30 +0200 Subject: [PATCH 4148/5214] mailbox: imx: Use channel index instead of zero in imx_mu_specific_rx() imx_mu_specific_rx() masks channel 0 and unmasks it again at the end of the function. Given that at startup the channel index got unmasked it should do the right job. This here either unmasks the actual channel or another one but should have no impact given that it reverses its doing at the end. Peng Fan commented here: | For specific rx channel, whether it is i.MX8 SCU or i.MX ELE, actually there is | only 1 channel as of now, but it seems better to use cp->idx in case more | channels in future. Use the channel index instead of zero. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index a8d4e970fb78..408cd083c64e 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -381,7 +381,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * data = (u32 *)priv->msg; - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); + imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); *data++ = imx_mu_read(priv, priv->dcfg->xRR); if (priv->dcfg->type & IMX_MU_V2_S4) { @@ -408,7 +408,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; From 692e1bc96a5dc7d5c43d937c2a50b56303544dea Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:31 +0200 Subject: [PATCH 4149/5214] mailbox: imx: Start splitting the IRQ handler in primary and threaded handler Split the mailbox irq handling into a primary handler (imx_mu_isr()) and a threaded handler (imx_mu_isr_th()). The primary handler masks the interrupt event so the threaded handler can run without raising the interrupt again. The goal here is to invoke the mailbox core functions (such as mbox_chan_received_data(), mbox_chan_txdone()) in preemptible context which is made possible by using an threaded interrupt handler. This in turn means that mailbox's client callbacks are invoked in preemptible context, too. This then allows the mailbox client callback to skip an indirection via a workqueue if it requries preemptible callback. As a first step, prepare the logic and move TX handling part. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 408cd083c64e..87acc43cb99c 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -540,11 +540,31 @@ static void imx_mu_txdb_work(struct work_struct *t) mbox_chan_txdone(cp->chan, 0); } +static irqreturn_t imx_mu_isr_th(int irq, void *p) +{ + struct mbox_chan *chan = p; + struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); + struct imx_mu_con_priv *cp = chan->con_priv; + + switch (cp->type) { + case IMX_MU_TYPE_TX: + mbox_chan_txdone(chan, 0); + break; + + default: + dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", + cp->type); + return IRQ_NONE; + } + return IRQ_HANDLED; +} + static irqreturn_t imx_mu_isr(int irq, void *p) { struct mbox_chan *chan = p; struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); struct imx_mu_con_priv *cp = chan->con_priv; + irqreturn_t ret = IRQ_HANDLED; u32 val, ctrl; switch (cp->type) { @@ -580,7 +600,7 @@ static irqreturn_t imx_mu_isr(int irq, void *p) if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_TX)) { imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); - mbox_chan_txdone(chan, 0); + ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RX)) { priv->dcfg->rx(priv, cp); @@ -595,7 +615,7 @@ static irqreturn_t imx_mu_isr(int irq, void *p) if (priv->suspend) pm_system_wakeup(); - return IRQ_HANDLED; + return ret; } static int imx_mu_send_data(struct mbox_chan *chan, void *data) @@ -630,7 +650,8 @@ static int imx_mu_startup(struct mbox_chan *chan) if (!(priv->dcfg->type & IMX_MU_V2_IRQ)) irq_flag |= IRQF_SHARED; - ret = request_irq(priv->irq[cp->type], imx_mu_isr, irq_flag, cp->irq_desc, chan); + ret = request_threaded_irq(priv->irq[cp->type], imx_mu_isr, imx_mu_isr_th, + irq_flag, cp->irq_desc, chan); if (ret) { dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq[cp->type]); return ret; From e55bea377d492f0fd4a7f657fc2a9247b5b96afb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:32 +0200 Subject: [PATCH 4150/5214] mailbox: imx: Move the RX part of the mailbox into the threaded handler Move RX callback handling into the threaded handler. This is similar to the TX side except that we explicitly mask the source interrupt in the primary handler and unmask it in the threaded handler again after success. This was done automatically in the TX part. The masking/ unmasking can be removed from imx_mu_specific_rx() since it already happens in the primary/ threaded handler before invoking the channel specific callback. Move RX channel handling into threaded handler. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 87acc43cb99c..1219c35b116e 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -381,7 +381,6 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * data = (u32 *)priv->msg; - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); *data++ = imx_mu_read(priv, priv->dcfg->xRR); if (priv->dcfg->type & IMX_MU_V2_S4) { @@ -408,7 +407,6 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; @@ -551,6 +549,11 @@ static irqreturn_t imx_mu_isr_th(int irq, void *p) mbox_chan_txdone(chan, 0); break; + case IMX_MU_TYPE_RX: + if (!priv->dcfg->rx(priv, cp)) + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + break; + default: dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", cp->type); @@ -603,7 +606,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p) ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RX)) { - priv->dcfg->rx(priv, cp); + imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RXDB)) { priv->dcfg->rxdb(priv, cp); From 3225a745f51747787cb05de85ab44e962a3c664b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:33 +0200 Subject: [PATCH 4151/5214] mailbox: imx: Move the RXDB part of the mailbox into the threaded handler Move RXDB callback handling into the threaded handler. This similar to the RX side and since the imx_mu_dcfg::rxdb callback can return an error, the interrupt is only enabled on success. Move RXDB callback handling into the threaded handler. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 1219c35b116e..1bd434bdff87 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -554,6 +554,11 @@ static irqreturn_t imx_mu_isr_th(int irq, void *p) imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); break; + case IMX_MU_TYPE_RXDB: + if (!priv->dcfg->rxdb(priv, cp)) + imx_mu_xcr_set_act(priv, cp, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + break; + default: dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", cp->type); @@ -610,7 +615,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p) ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RXDB)) { - priv->dcfg->rxdb(priv, cp); + imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + ret = IRQ_WAKE_THREAD; } else { dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); return IRQ_NONE; From 36cac4b5101f8ecbc851356df175b99543c84ec6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:34 +0200 Subject: [PATCH 4152/5214] mailbox: imx: Don't force-thread the primary handler The primary interrupt handler (imx_mu_isr()) no longer invokes any callbacks it only masks the interrupt source and returns. In a forced-threaded environment the IRQ-core will force-thread the primary handler which can be avoided. The primary handler uses a spinlock_t to protect the RMW operation in imx_mu_xcr_rmw() - nothing that may introduce long latencies. The lock can be turned into a raw_spinlock_t and then the primary handler can run in hardirq context even on PREEMPT_RT skipping one thread. Make struct imx_mu_priv::xcr_lock a raw_spinlock_t and skip force-threading the primrary handler by marking it IRQF_NO_THREAD. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 1bd434bdff87..40bad2b3e4d1 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -87,7 +87,7 @@ struct imx_mu_priv { struct device *dev; void __iomem *base; void *msg; - spinlock_t xcr_lock; /* control register lock */ + raw_spinlock_t xcr_lock; /* control register lock */ struct mbox_controller mbox; struct mbox_chan mbox_chans[IMX_MU_CHANS]; @@ -207,15 +207,14 @@ static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx) static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); + val = imx_mu_read(priv, priv->dcfg->xCR[type]); val &= ~clr; val |= set; imx_mu_write(priv, val, priv->dcfg->xCR[type]); - spin_unlock_irqrestore(&priv->xcr_lock, flags); return val; } @@ -223,31 +222,27 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 se static void imx_mu_xcr_clr_shut(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, enum imx_mu_xcr type, u32 clr) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); cp->shutdown = true; val = imx_mu_read(priv, priv->dcfg->xCR[type]); val &= ~clr; imx_mu_write(priv, val, priv->dcfg->xCR[type]); - spin_unlock_irqrestore(&priv->xcr_lock, flags); } static void imx_mu_xcr_set_act(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, enum imx_mu_xcr type, u32 set) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); if (!cp->shutdown) { val = imx_mu_read(priv, priv->dcfg->xCR[type]); val |= set; imx_mu_write(priv, val, priv->dcfg->xCR[type]); } - spin_unlock_irqrestore(&priv->xcr_lock, flags); } static int imx_mu_generic_tx(struct imx_mu_priv *priv, @@ -640,7 +635,7 @@ static int imx_mu_startup(struct mbox_chan *chan) { struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); struct imx_mu_con_priv *cp = chan->con_priv; - unsigned long irq_flag = 0; + unsigned long irq_flag = IRQF_NO_THREAD; int ret; pm_runtime_get_sync(priv->dev); @@ -988,7 +983,7 @@ static int imx_mu_probe(struct platform_device *pdev) goto disable_clk; } - spin_lock_init(&priv->xcr_lock); + raw_spin_lock_init(&priv->xcr_lock); priv->mbox.dev = dev; priv->mbox.ops = &imx_mu_ops; From 1a3860d46e3eb47dbd60339783cdad7904486b9f Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 28 May 2026 13:39:16 +0800 Subject: [PATCH 4153/5214] 9p: avoid putting oldfid in p9_client_walk() error path When p9_client_walk() is called with clone set to false, fid aliases oldfid. If the walk subsequently fails after the request has been sent, the error path jumps to clunk_fid, which currently calls p9_fid_put(fid) unconditionally. This drops a reference to oldfid even though ownership of oldfid remains with the caller. If this is the last reference, oldfid can be clunked and destroyed while the caller still expects it to be valid. A later use or put of oldfid can then trigger a use-after-free or refcount underflow. Fix this by only putting fid in the clunk_fid error path when it does not alias oldfid, matching the existing guard in the error path below. This can be triggered when a multi-component walk is split into multiple p9_client_walk() calls and a later non-cloning walk fails. A reproducer and refcount warning logs are available on request. Fixes: b48dbb998d70 ("9p fid refcount: add p9_fid_get/put wrappers") Cc: stable@vger.kernel.org Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM 5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260528053918.53550-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet --- net/9p/client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/9p/client.c b/net/9p/client.c index 69d3efd340c0..ef64546c6d52 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1094,7 +1094,8 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, clunk_fid: kfree(wqids); - p9_fid_put(fid); + if (fid != oldfid) + p9_fid_put(fid); fid = NULL; error: From 314b58c01a9047567fd19446ca5fd46c473b89ff Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 20 May 2026 10:26:50 +0800 Subject: [PATCH 4154/5214] 9p: avoid returning ERR_PTR(0) from mkdir operations When mkdir succeeds, v9fs_vfs_mkdir_dotl() and v9fs_vfs_mkdir() return ERR_PTR(0) which is incorrect. They should return NULL instead for success and ERR_PTR() only with negative error codes for failure. Return NULL instead of passing to ERR_PTR while err is zero Fixes smatch warnings: fs/9p/vfs_inode_dotl.c:420 v9fs_vfs_mkdir_dotl() warn: passing zero to 'ERR_PTR' fs/9p/vfs_inode.c:695 v9fs_vfs_mkdir() warn: passing zero to 'ERR_PTR' The v9fs_vfs_mkdir() code was further simplified because v9fs_create() can never return NULL, so we do not need to check for fid being set separately, and the error path can be a simple return immediately after v9fs_create() failure. There is no intended functional change. Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Suggested-by: David Laight Acked-by: Christian Schoenebeck Signed-off-by: Hongling Zeng Message-ID: <20260520022650.14217-1-zenghongling@kylinos.cn> Signed-off-by: Dominique Martinet --- fs/9p/vfs_inode.c | 19 ++++++------------- fs/9p/vfs_inode_dotl.c | 4 ++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 97abe65bf7c1..e4b15ebba3aa 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -672,27 +672,20 @@ v9fs_vfs_create(struct mnt_idmap *idmap, struct inode *dir, static struct dentry *v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - int err; u32 perm; struct p9_fid *fid; struct v9fs_session_info *v9ses; p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); - err = 0; v9ses = v9fs_inode2v9ses(dir); perm = unixmode2p9mode(v9ses, mode | S_IFDIR); fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); - if (IS_ERR(fid)) { - err = PTR_ERR(fid); - fid = NULL; - } else { - inc_nlink(dir); - v9fs_invalidate_inode_attr(dir); - } - - if (fid) - p9_fid_put(fid); - return ERR_PTR(err); + if (IS_ERR(fid)) + return ERR_CAST(fid); + inc_nlink(dir); + v9fs_invalidate_inode_attr(dir); + p9_fid_put(fid); + return NULL; } /** diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 643e759eacb2..fae324681ff3 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -349,7 +349,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t omode) { - int err; + int err = 0; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; kgid_t gid; @@ -412,7 +412,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, p9_fid_put(fid); v9fs_put_acl(dacl, pacl); p9_fid_put(dfid); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } static int From f00e6c1d282578b077b87b88f42e701bc40f2fff Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Thu, 21 May 2026 11:40:29 +0200 Subject: [PATCH 4155/5214] 9p: Cache negative dentries for lookup performance Not caching negative dentries can result in poor performance for workloads that repeatedly look up non-existent paths. Each such lookup triggers a full 9P transaction with the server, adding unnecessary overhead. A typical example is source compilation, where multiple cc1 processes are spawned and repeatedly search for the same missing header files over and over again. This change enables caching of negative dentries, so that lookups for known non-existent paths do not require a full 9P transaction. The cached negative dentries are retained for a configurable duration (expressed in milliseconds), as specified by the ndentry_timeout field in struct v9fs_session_info. If set to -1, negative dentries are cached indefinitely. This optimization reduces lookup overhead and improves performance for workloads involving frequent access to non-existent paths. Signed-off-by: Remi Pommarel Message-ID: Signed-off-by: Dominique Martinet --- fs/9p/fid.c | 11 +++-- fs/9p/v9fs.c | 1 + fs/9p/v9fs.h | 5 ++ fs/9p/v9fs_vfs.h | 15 ++++++ fs/9p/vfs_dentry.c | 105 ++++++++++++++++++++++++++++++++++------ fs/9p/vfs_inode.c | 12 +++-- fs/9p/vfs_super.c | 1 + include/net/9p/client.h | 2 + 8 files changed, 128 insertions(+), 24 deletions(-) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index f84412290a30..76242d450aa7 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -20,7 +20,9 @@ static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid) { - hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata); + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); + + hlist_add_head(&fid->dlist, &v9fs_dentry->head); } @@ -112,6 +114,7 @@ void v9fs_open_fid_add(struct inode *inode, struct p9_fid **pfid) static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any) { + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); struct p9_fid *fid, *ret; p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n", @@ -119,11 +122,9 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any) any); ret = NULL; /* we'll recheck under lock if there's anything to look in */ - if (dentry->d_fsdata) { - struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata; - + if (!hlist_empty(&v9fs_dentry->head)) { spin_lock(&dentry->d_lock); - hlist_for_each_entry(fid, h, dlist) { + hlist_for_each_entry(fid, &v9fs_dentry->head, dlist) { if (any || uid_eq(fid->uid, uid)) { ret = fid; p9_fid_get(ret); diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index acda42499ca9..be83744b75b2 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -426,6 +426,7 @@ static void v9fs_apply_options(struct v9fs_session_info *v9ses, v9ses->cache = ctx->session_opts.cache; v9ses->uid = ctx->session_opts.uid; v9ses->session_lock_timeout = ctx->session_opts.session_lock_timeout; + v9ses->ndentry_timeout_ms = ctx->session_opts.ndentry_timeout_ms; } /** diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 6a12445d3858..e630c5111d74 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -91,6 +91,7 @@ enum p9_cache_bits { * @debug: debug level * @afid: authentication handle * @cache: cache mode of type &p9_cache_bits + * @ndentry_timeout: Negative dentry lookup cache retention time in ms * @cachetag: the tag of the cache associated with this session * @fscache: session cookie associated with FS-Cache * @uname: string user name to mount hierarchy as @@ -101,6 +102,7 @@ enum p9_cache_bits { * @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy * @clnt: reference to 9P network client instantiated for this session * @slist: reference to list of registered 9p sessions + * @ndentry_timeout_ms: Negative dentry caching retention time * * This structure holds state for each session instance established during * a sys_mount() . @@ -116,6 +118,7 @@ struct v9fs_session_info { unsigned short debug; unsigned int afid; unsigned int cache; + unsigned int ndentry_timeout_ms; #ifdef CONFIG_9P_FSCACHE char *cachetag; struct fscache_volume *fscache; @@ -133,6 +136,8 @@ struct v9fs_session_info { long session_lock_timeout; /* retry interval for blocking locks */ }; +#define NDENTRY_TIMEOUT_NEVER (-1U) + /* cache_validity flags */ #define V9FS_INO_INVALID_ATTR 0x01 diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index d3aefbec4de6..83c2335f438d 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -28,6 +28,19 @@ /* flags for v9fs_stat2inode() & v9fs_stat2inode_dotl() */ #define V9FS_STAT2INODE_KEEP_ISIZE 1 +/** + * struct v9fs_dentry - v9fs specific dentry data + * @head: List of fid associated with this dentry + * @expire_time: Lookup cache expiration time for negative dentries + * @rcu: used by kfree_rcu to schedule clean up job + */ +struct v9fs_dentry { + struct hlist_head head; + u64 expire_time; + struct rcu_head rcu; +}; +#define to_v9fs_dentry(d) ((struct v9fs_dentry *)((d)->d_fsdata)) + extern struct file_system_type v9fs_fs_type; extern const struct address_space_operations v9fs_addr_operations; extern const struct file_operations v9fs_file_operations; @@ -35,6 +48,8 @@ extern const struct file_operations v9fs_file_operations_dotl; extern const struct file_operations v9fs_dir_operations; extern const struct file_operations v9fs_dir_operations_dotl; extern const struct dentry_operations v9fs_dentry_operations; +extern void v9fs_ndentry_refresh_timeout(struct dentry *dentry); +extern void v9fs_dentry_fid_remove(struct dentry *dentry); extern const struct dentry_operations v9fs_cached_dentry_operations; extern struct kmem_cache *v9fs_inode_cache; diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index c5bf74d547e8..e549e222602e 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -23,6 +23,46 @@ #include "v9fs_vfs.h" #include "fid.h" +/** + * v9fs_ndentry_is_expired - Check if negative dentry lookup has expired + * + * This should be called to know if a negative dentry should be removed from + * cache. + * + * @dentry: dentry in question + * + */ +static bool v9fs_ndentry_is_expired(struct dentry const *dentry) +{ + struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); + + if (v9ses->ndentry_timeout_ms == NDENTRY_TIMEOUT_NEVER) + return false; + + return time_before_eq64(v9fs_dentry->expire_time, get_jiffies_64()); +} + +/** + * v9fs_ndentry_refresh_timeout - Refresh negative dentry lookup cache timeout + * + * This should be called when a look up yields a negative entry. + * + * @dentry: dentry in question + * + */ +void v9fs_ndentry_refresh_timeout(struct dentry *dentry) +{ + struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); + + if (v9ses->ndentry_timeout_ms == NDENTRY_TIMEOUT_NEVER) + return; + + v9fs_dentry->expire_time = get_jiffies_64() + + msecs_to_jiffies(v9ses->ndentry_timeout_ms); +} + /** * v9fs_cached_dentry_delete - called when dentry refcount equals 0 * @dentry: dentry in question @@ -33,20 +73,15 @@ static int v9fs_cached_dentry_delete(const struct dentry *dentry) p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n", dentry, dentry); - /* Don't cache negative dentries */ - if (d_really_is_negative(dentry)) - return 1; - return 0; + if (!d_really_is_negative(dentry)) + return 0; + + return v9fs_ndentry_is_expired(dentry); } -/** - * v9fs_dentry_release - called when dentry is going to be freed - * @dentry: dentry that is being release - * - */ - -static void v9fs_dentry_release(struct dentry *dentry) +static void __v9fs_dentry_fid_remove(struct dentry *dentry) { + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); struct hlist_node *p, *n; struct hlist_head head; @@ -54,13 +89,54 @@ static void v9fs_dentry_release(struct dentry *dentry) dentry, dentry); spin_lock(&dentry->d_lock); - hlist_move_list((struct hlist_head *)&dentry->d_fsdata, &head); + hlist_move_list(&v9fs_dentry->head, &head); spin_unlock(&dentry->d_lock); hlist_for_each_safe(p, n, &head) p9_fid_put(hlist_entry(p, struct p9_fid, dlist)); } +/** + * v9fs_dentry_fid_remove - Release all dentry's fids + * @dentry: dentry in question + * + */ +void v9fs_dentry_fid_remove(struct dentry *dentry) +{ + __v9fs_dentry_fid_remove(dentry); +} + +/** + * v9fs_dentry_init - Initialize v9fs dentry data + * @dentry: dentry in question + * + */ +static int v9fs_dentry_init(struct dentry *dentry) +{ + struct v9fs_dentry *v9fs_dentry = kzalloc(sizeof(*v9fs_dentry), + GFP_KERNEL); + + if (!v9fs_dentry) + return -ENOMEM; + + INIT_HLIST_HEAD(&v9fs_dentry->head); + dentry->d_fsdata = (void *)v9fs_dentry; + return 0; +} + +/** + * v9fs_dentry_release - called when dentry is going to be freed + * @dentry: dentry that is being released + * + */ +static void v9fs_dentry_release(struct dentry *dentry) +{ + struct v9fs_dentry *v9fs_dentry = to_v9fs_dentry(dentry); + + __v9fs_dentry_fid_remove(dentry); + kfree_rcu(v9fs_dentry, rcu); +} + static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) { struct p9_fid *fid; @@ -72,7 +148,7 @@ static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) inode = d_inode(dentry); if (!inode) - goto out_valid; + return !v9fs_ndentry_is_expired(dentry); v9inode = V9FS_I(inode); if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { @@ -112,7 +188,6 @@ static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags) return retval; } } -out_valid: p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) is valid\n", dentry, dentry); return 1; } @@ -139,12 +214,14 @@ const struct dentry_operations v9fs_cached_dentry_operations = { .d_revalidate = v9fs_lookup_revalidate, .d_weak_revalidate = __v9fs_lookup_revalidate, .d_delete = v9fs_cached_dentry_delete, + .d_init = v9fs_dentry_init, .d_release = v9fs_dentry_release, .d_unalias_trylock = v9fs_dentry_unalias_trylock, .d_unalias_unlock = v9fs_dentry_unalias_unlock, }; const struct dentry_operations v9fs_dentry_operations = { + .d_init = v9fs_dentry_init, .d_release = v9fs_dentry_release, .d_unalias_trylock = v9fs_dentry_unalias_trylock, .d_unalias_unlock = v9fs_dentry_unalias_unlock, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index e4b15ebba3aa..a178e8cb2c82 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -549,7 +549,7 @@ static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags) /* invalidate all fids associated with dentry */ /* NOTE: This will not include open fids */ - dentry->d_op->d_release(dentry); + v9fs_dentry_fid_remove(dentry); } return retval; } @@ -725,14 +725,16 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, name = dentry->d_name.name; fid = p9_client_walk(dfid, 1, &name, 1); p9_fid_put(dfid); - if (fid == ERR_PTR(-ENOENT)) + if (fid == ERR_PTR(-ENOENT)) { inode = NULL; - else if (IS_ERR(fid)) + v9fs_ndentry_refresh_timeout(dentry); + } else if (IS_ERR(fid)) { inode = ERR_CAST(fid); - else if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) + } else if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb); - else + } else { inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); + } /* * If we had a rename on the server and a parallel lookup * for the new name, then make sure we instantiate with diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 431f24938a1d..94d6b02c221b 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -330,6 +330,7 @@ static int v9fs_init_fs_context(struct fs_context *fc) ctx->session_opts.uid = INVALID_UID; ctx->session_opts.dfltuid = V9FS_DEFUID; ctx->session_opts.dfltgid = V9FS_DEFGID; + ctx->session_opts.ndentry_timeout_ms = 0; /* initialize client options */ ctx->client_opts.proto_version = p9_proto_2000L; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 838a94218b59..55c6cb54bd25 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -192,6 +192,7 @@ struct p9_rdma_opts { * @dfltgid: default numeric groupid to mount hierarchy as * @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy * @session_lock_timeout: retry interval for blocking locks + * @ndentry_timeout_ms: Negative dentry lookup cache retention time in ms * * This strucure holds options which are parsed and will be transferred * to the v9fs_session_info structure when mounted, and therefore largely @@ -203,6 +204,7 @@ struct p9_session_opts { unsigned short debug; unsigned int afid; unsigned int cache; + unsigned int ndentry_timeout_ms; #ifdef CONFIG_9P_FSCACHE char *cachetag; #endif From 5670a84b5cda6b82016282accfd61ef36aceafbf Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Thu, 21 May 2026 11:40:30 +0200 Subject: [PATCH 4156/5214] 9p: Add mount option for negative dentry cache retention Introduce a new mount option, negtimeout, for v9fs that allows users to specify how long negative dentries are retained in the cache. The retention time can be set in milliseconds (e.g. negtimeout=10000 for a 10secs retention time) or a negative value (e.g. negtimeout=-1) to keep negative entries until the buffer cache management removes them. For consistency reasons, this option should only be used in exclusive or read-only mount scenarios, aligning with the cache=loose usage. Signed-off-by: Remi Pommarel Message-ID: Signed-off-by: Dominique Martinet --- Documentation/filesystems/9p.rst | 5 +++++ fs/9p/v9fs.c | 16 +++++++++++++++- fs/9p/v9fs.h | 23 +++++++++++++---------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Documentation/filesystems/9p.rst b/Documentation/filesystems/9p.rst index 65809a1dad21..3f65db648db0 100644 --- a/Documentation/filesystems/9p.rst +++ b/Documentation/filesystems/9p.rst @@ -235,6 +235,11 @@ Options cachetag cache tag to use the specified persistent cache. cache tags for existing cache sessions can be listed at /sys/fs/9p/caches. (applies only to cache=fscache) + + negtimeout the duration (in milliseconds) that negative dentries (paths + that do not actually exist) are retained in the cache. If + set to a negative value, those entries are kept indefinitely + until evicted by the buffer cache management system ============= =============================================================== Behavior diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index be83744b75b2..3e758b66fefa 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -39,7 +39,7 @@ enum { * source if we rejected it as EINVAL */ Opt_source, /* Options that take integer arguments */ - Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, + Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, Opt_negtimeout, /* String options */ Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag, /* Options that take no arguments */ @@ -93,6 +93,7 @@ const struct fs_parameter_spec v9fs_param_spec[] = { fsparam_string ("access", Opt_access), fsparam_flag ("posixacl", Opt_posixacl), fsparam_u32 ("locktimeout", Opt_locktimeout), + fsparam_s32 ("negtimeout", Opt_negtimeout), /* client options */ fsparam_u32 ("msize", Opt_msize), @@ -159,6 +160,9 @@ int v9fs_show_options(struct seq_file *m, struct dentry *root) from_kgid_munged(&init_user_ns, v9ses->dfltgid)); if (v9ses->afid != ~0) seq_printf(m, ",afid=%u", v9ses->afid); + if (v9ses->flags & V9FS_NDENTRY_TIMEOUT_SET) + seq_printf(m, ",negtimeout=%d", + (int)v9ses->ndentry_timeout_ms); if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0) seq_printf(m, ",uname=%s", v9ses->uname); if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0) @@ -337,6 +341,16 @@ int v9fs_parse_param(struct fs_context *fc, struct fs_parameter *param) session_opts->session_lock_timeout = (long)result.uint_32 * HZ; break; + case Opt_negtimeout: + session_opts->flags |= V9FS_NDENTRY_TIMEOUT_SET; + if (result.int_32 < 0) { + session_opts->ndentry_timeout_ms = + NDENTRY_TIMEOUT_NEVER; + } else { + session_opts->ndentry_timeout_ms = result.int_32; + } + break; + /* Options for client */ case Opt_msize: if (result.uint_32 < 4096) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index e630c5111d74..a462bcbfc7da 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -24,6 +24,8 @@ * @V9FS_ACCESS_ANY: use a single attach for all users * @V9FS_ACCESS_MASK: bit mask of different ACCESS options * @V9FS_POSIX_ACL: POSIX ACLs are enforced + * @V9FS_NDENTRY_TIMEOUT_SET: Has negative dentry timeout retention time been + * overridden by negtimeout mount option * * Session flags reflect options selected by users at mount time */ @@ -34,16 +36,17 @@ #define V9FS_ACL_MASK V9FS_POSIX_ACL enum p9_session_flags { - V9FS_PROTO_2000U = 0x01, - V9FS_PROTO_2000L = 0x02, - V9FS_ACCESS_SINGLE = 0x04, - V9FS_ACCESS_USER = 0x08, - V9FS_ACCESS_CLIENT = 0x10, - V9FS_POSIX_ACL = 0x20, - V9FS_NO_XATTR = 0x40, - V9FS_IGNORE_QV = 0x80, /* ignore qid.version for cache hints */ - V9FS_DIRECT_IO = 0x100, - V9FS_SYNC = 0x200 + V9FS_PROTO_2000U = 0x01, + V9FS_PROTO_2000L = 0x02, + V9FS_ACCESS_SINGLE = 0x04, + V9FS_ACCESS_USER = 0x08, + V9FS_ACCESS_CLIENT = 0x10, + V9FS_POSIX_ACL = 0x20, + V9FS_NO_XATTR = 0x40, + V9FS_IGNORE_QV = 0x80, /* ignore qid.version for cache hints */ + V9FS_DIRECT_IO = 0x100, + V9FS_SYNC = 0x200, + V9FS_NDENTRY_TIMEOUT_SET = 0x400, }; /** From 15bbf82857fa61de9bad9faa7a3cf3a95b1b292a Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Thu, 21 May 2026 11:40:31 +0200 Subject: [PATCH 4157/5214] 9p: Set default negative dentry retention time for cache=loose For cache=loose mounts, set the default negative dentry cache retention time to 24 hours. Signed-off-by: Remi Pommarel Message-ID: Signed-off-by: Dominique Martinet --- fs/9p/v9fs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 3e758b66fefa..274c5157135d 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -24,6 +24,9 @@ #include "v9fs_vfs.h" #include "cache.h" +/* cache=loose default negative dentry retention time is 24hours */ +#define CACHE_LOOSE_NDENTRY_TIMEOUT_DEFAULT (24 * 60 * 60 * 1000) + static DEFINE_SPINLOCK(v9fs_sessionlist_lock); static LIST_HEAD(v9fs_sessionlist); struct kmem_cache *v9fs_inode_cache; @@ -441,6 +444,13 @@ static void v9fs_apply_options(struct v9fs_session_info *v9ses, v9ses->uid = ctx->session_opts.uid; v9ses->session_lock_timeout = ctx->session_opts.session_lock_timeout; v9ses->ndentry_timeout_ms = ctx->session_opts.ndentry_timeout_ms; + + /* If negative dentry timeout has not been overridden set default for + * cache=loose + */ + if (!(v9ses->flags & V9FS_NDENTRY_TIMEOUT_SET) && + (v9ses->cache & CACHE_LOOSE)) + v9ses->ndentry_timeout_ms = CACHE_LOOSE_NDENTRY_TIMEOUT_DEFAULT; } /** From 712da38d134b6681946368dd5c9400b43eb772b0 Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Thu, 21 May 2026 11:40:32 +0200 Subject: [PATCH 4158/5214] 9p: Enable symlink caching in page cache Currently, when cache=loose is enabled, file reads are cached in the page cache, but symlink reads are not. This patch allows the results of p9_client_readlink() to be stored in the page cache, eliminating the need for repeated 9P transactions on subsequent symlink accesses. This change improves performance for workloads that involve frequent symlink resolution. Signed-off-by: Remi Pommarel Message-ID: <982462d17c0c0d2856763266a25eb04d080c1dbb.1779355927.git.repk@triplefau.lt> Signed-off-by: Dominique Martinet --- fs/9p/vfs_addr.c | 36 ++++++++++++++++++++++++++-- fs/9p/vfs_inode.c | 6 +++-- fs/9p/vfs_inode_dotl.c | 54 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 862164181bac..2b6ca573f955 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -70,11 +70,34 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; struct p9_fid *fid = rreq->netfs_priv; + char *target; unsigned long long pos = subreq->start + subreq->transferred; - int total, err; + int total = 0, err, len, n; - total = p9_client_read(fid, pos, &subreq->io_iter, &err); + if (S_ISLNK(rreq->inode->i_mode)) { + /* p9_client_readlink() must not be called for legacy protocols + * 9p2000 or 9p2000.u. + */ + BUG_ON(!p9_is_proto_dotl(fid->clnt)); + if (WARN_ON_ONCE(pos)) { + /* reading a link at a non null offset should + * not happen + */ + err = -EIO; + goto fill_subreq; + } + err = p9_client_readlink(fid, &target); + if (err != 0) + goto fill_subreq; + len = strlen(target); + n = copy_to_iter(target, len, &subreq->io_iter); + kfree(target); + total = n; + } else { + total = p9_client_read(fid, pos, &subreq->io_iter, &err); + } +fill_subreq: /* if we just extended the file size, any portion not in * cache won't be on server and is zeroes */ if (subreq->rreq->origin != NETFS_UNBUFFERED_READ && @@ -99,6 +122,7 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq) static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file) { struct p9_fid *fid; + struct dentry *dentry; bool writing = (rreq->origin == NETFS_READ_FOR_WRITE || rreq->origin == NETFS_WRITETHROUGH || rreq->origin == NETFS_UNBUFFERED_WRITE || @@ -115,6 +139,14 @@ static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file) if (!fid) goto no_fid; p9_fid_get(fid); + } else if (S_ISLNK(rreq->inode->i_mode)) { + dentry = d_find_any_alias(rreq->inode); + if (!dentry) + goto no_fid; + fid = v9fs_fid_lookup(dentry); + dput(dentry); + if (IS_ERR(fid)) + goto no_fid; } else { fid = v9fs_fid_find_inode(rreq->inode, writing, INVALID_UID, true); if (!fid) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index a178e8cb2c82..cdaa5034cbef 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -302,10 +302,12 @@ int v9fs_init_inode(struct v9fs_session_info *v9ses, goto error; } - if (v9fs_proto_dotl(v9ses)) + if (v9fs_proto_dotl(v9ses)) { inode->i_op = &v9fs_symlink_inode_operations_dotl; - else + inode_nohighmem(inode); + } else { inode->i_op = &v9fs_symlink_inode_operations; + } break; case S_IFDIR: diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index fae324681ff3..8b056c334c2b 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -686,9 +686,11 @@ v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct inode *dir, int err; kgid_t gid; const unsigned char *name; + struct v9fs_session_info *v9ses; struct p9_qid qid; struct p9_fid *dfid; struct p9_fid *fid = NULL; + struct inode *inode; name = dentry->d_name.name; p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname); @@ -712,6 +714,26 @@ v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct inode *dir, v9fs_invalidate_inode_attr(dir); + /* instantiate inode and assign the unopened fid to the dentry */ + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", + err); + goto error; + } + + v9ses = v9fs_inode2v9ses(dir); + inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", + err); + goto error; + } + v9fs_fid_add(dentry, &fid); + d_instantiate(dentry, inode); + err = 0; error: p9_fid_put(fid); p9_fid_put(dfid); @@ -853,16 +875,18 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir, } /** - * v9fs_vfs_get_link_dotl - follow a symlink path + * v9fs_vfs_get_link_nocache_dotl - Resolve a symlink directly. + * + * To be used when symlink caching is not enabled. + * * @dentry: dentry for symlink * @inode: inode for symlink * @done: destructor for return value */ - static const char * -v9fs_vfs_get_link_dotl(struct dentry *dentry, - struct inode *inode, - struct delayed_call *done) +v9fs_vfs_get_link_nocache_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct p9_fid *fid; char *target; @@ -884,6 +908,26 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry, return target; } +/** + * v9fs_vfs_get_link_dotl - follow a symlink path + * @dentry: dentry for symlink + * @inode: inode for symlink + * @done: destructor for return value + */ +static const char * +v9fs_vfs_get_link_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) +{ + struct v9fs_session_info *v9ses; + + v9ses = v9fs_inode2v9ses(inode); + if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) + return page_get_link(dentry, inode, done); + + return v9fs_vfs_get_link_nocache_dotl(dentry, inode, done); +} + int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) { struct p9_stat_dotl *st; From 96a6db3fd7763f676fb6a25658b11aaf0e4fa4f9 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Fri, 29 May 2026 12:09:20 +0900 Subject: [PATCH 4159/5214] 9p: v9fs_file_do_lock: replace WARN_ONCE with p9_debug This warning depends on server-provided data, we should not use WARN here Reported-by: Yifei Chu Closes: https://lore.kernel.org/r/CAPJnbgJ7ZK7DCjCfG56hd_iKGePmAzudb4hOWd4=9r32nM+KcA@mail.gmail.com Signed-off-by: Dominique Martinet Message-ID: <20260529-lock-warn-v1-1-20c29580d61d@codewreck.org> --- fs/9p/vfs_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index c5e73c37baea..cddfb4b7ce4e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -198,7 +198,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl) res = -EAGAIN; break; default: - WARN_ONCE(1, "unknown lock status code: %d\n", status); + p9_debug(P9_DEBUG_ERROR, "unknown lock status code: %d\n", status); fallthrough; case P9_LOCK_ERROR: case P9_LOCK_GRACE: From 7d54894a1ee265a72d70f7cae1da6cc774cccc71 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 29 May 2026 15:39:31 +0800 Subject: [PATCH 4160/5214] net/9p: fix race condition on rdma->state in trans_rdma.c The rdma->state field is modified without holding req_lock in both recv_done() and p9_cm_event_handler(), while rdma_request() accesses the same field under the req_lock spinlock. This inconsistent locking creates a race condition: - recv_done() running in softirq completion context sets rdma->state = P9_RDMA_FLUSHING without acquiring req_lock - p9_cm_event_handler() modifies rdma->state at multiple points (ADDR_RESOLVED, ROUTE_RESOLVED, ESTABLISHED, CLOSED) without req_lock - rdma_request() uses spin_lock_irqsave(&rdma->req_lock, flags) to protect the read-modify-write of rdma->state The race can cause lost state transitions: recv_done() or the CM event handler could set state to FLUSHING/CLOSED while rdma_request() is concurrently checking or modifying state under the lock, leading to the FLUSHING transition being silently overwritten by CLOSING. This corrupts the connection state machine and can cause use-after-free on RDMA request objects during teardown. Fix by adding req_lock protection to all rdma->state modifications in recv_done() and p9_cm_event_handler(), matching the pattern already used in rdma_request(). Use spin_lock_irqsave/spin_unlock_irqrestore in the CM event handler since it can race with recv_done() which runs in softirq context. Tested with a kernel module that races two threads (simulating rdma_request and recv_done/CM handler) on rdma->state with proper locking: 5.5M+ FLUSHING writes over 27M iterations with 0 lost transitions. Fixes: 473c7dd1d7b5 ("9p/rdma: remove useless check in cm_event_handler") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM:GLM-5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260529073933.77315-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet --- net/9p/trans_rdma.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index aa5bd74d333f..b4274f10fa44 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -128,25 +128,36 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) { struct p9_client *c = id->context; struct p9_trans_rdma *rdma = c->trans; + unsigned long flags; + switch (event->event) { case RDMA_CM_EVENT_ADDR_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_INIT); rdma->state = P9_RDMA_ADDR_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ROUTE_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED); rdma->state = P9_RDMA_ROUTE_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ESTABLISHED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED); rdma->state = P9_RDMA_CONNECTED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_DISCONNECTED: - if (rdma) + if (rdma) { + spin_lock_irqsave(&rdma->req_lock, flags); rdma->state = P9_RDMA_CLOSED; + spin_unlock_irqrestore(&rdma->req_lock, flags); + } c->status = Disconnected; break; @@ -184,6 +195,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) struct p9_req_t *req; int err = 0; int16_t tag; + unsigned long flags; req = NULL; ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize, @@ -220,7 +232,10 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) err_out: p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, wc->status); - rdma->state = P9_RDMA_FLUSHING; + spin_lock_irqsave(&rdma->req_lock, flags); + if (rdma->state < P9_RDMA_FLUSHING) + rdma->state = P9_RDMA_FLUSHING; + spin_unlock_irqrestore(&rdma->req_lock, flags); client->status = Disconnected; goto out; } From 574aa0b4799470ac814479f1138d19efe6262255 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 21 Apr 2026 02:41:09 -0700 Subject: [PATCH 4161/5214] 9p: skip nlink update in cacheless mode to fix WARN_ON v9fs_dec_count() unconditionally calls drop_nlink() on regular files, even when the inode's nlink is already zero. In cacheless mode the client refetches inode metadata from the server (the source of truth) on every operation, so by the time v9fs_remove() returns, the locally cached nlink may already reflect the post-unlink value: 1. Client initiates unlink, server processes it and sets nlink to 0 2. Client refetches inode metadata (nlink=0) before unlink returns 3. Client's v9fs_remove() completes successfully 4. Client calls v9fs_dec_count() which calls drop_nlink() on nlink=0 This race is easily triggered under heavy unlink workloads, such as stress-ng's unlink stressor, producing the following warning: WARNING: fs/inode.c:417 at drop_nlink+0x4c/0xc8 Call trace: drop_nlink+0x4c/0xc8 v9fs_remove+0x1e0/0x250 [9p] v9fs_vfs_unlink+0x20/0x38 [9p] vfs_unlink+0x13c/0x258 ... In cacheless mode the server is authoritative and the inode is on its way out, so locally adjusting nlink buys nothing. Skip v9fs_dec_count() entirely when neither CACHE_META nor CACHE_LOOSE is set, which both avoids the warning and removes a class of nlink races (two concurrent unlinkers observing nlink > 0 and both calling drop_nlink()) that an nlink == 0 guard alone would only narrow rather than close. Fixes: ac89b2ef9b55 ("9p: don't maintain dir i_nlink if the exported fs doesn't either") Cc: stable@vger.kernel.org Suggested-by: Dominique Martinet Signed-off-by: Breno Leitao Message-ID: <20260421-9p-v2-1-48762d294fad@debian.org> Signed-off-by: Dominique Martinet --- fs/9p/vfs_inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index cdaa5034cbef..3a811db2dc19 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -490,10 +490,19 @@ static int v9fs_at_to_dotl_flags(int flags) * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more * than EXT4_LINK_MAX (65000) links. * + * In cacheless mode the server is the source of truth for nlink and the + * inode is going away immediately, so locally adjusting i_nlink buys + * nothing and races with concurrent metadata fetches that may already + * have observed the post-unlink value (nlink == 0). + * * @inode: inode whose nlink is being dropped */ static void v9fs_dec_count(struct inode *inode) { + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); + + if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE))) + return; if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) drop_nlink(inode); } From cc8b15a2c435bd1caf19741ba85286846a115764 Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:27:42 +0100 Subject: [PATCH 4162/5214] net/9p: Replace strlen() strcpy() pair with strscpy() Use the result of strscpy() for the overflow check. Signed-off-by: David Laight Message-ID: <20260606202744.5113-3-david.laight.linux@gmail.com> Signed-off-by: Dominique Martinet --- net/9p/trans_fd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index dbad3213ba84..eb685b52aeb2 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -940,14 +940,12 @@ p9_fd_create_unix(struct p9_client *client, struct fs_context *fc) if (!addr || !strlen(addr)) return -EINVAL; - if (strlen(addr) >= UNIX_PATH_MAX) { + sun_server.sun_family = PF_UNIX; + if (strscpy(sun_server.sun_path, addr) < 0) { pr_err("%s (%d): address too long: %s\n", __func__, task_pid_nr(current), addr); return -ENAMETOOLONG; } - - sun_server.sun_family = PF_UNIX; - strcpy(sun_server.sun_path, addr); err = __sock_create(current->nsproxy->net_ns, PF_UNIX, SOCK_STREAM, 0, &csocket, 1); if (err < 0) { From aa88278693cbfaf7a2acf961379973fbb63b165c Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 29 May 2026 15:54:41 +0800 Subject: [PATCH 4163/5214] 9p: Add missing read barrier in virtio zero-copy path Commit 2b6e72ed747f ("9P: Add memory barriers to protect request fields over cb/rpc threads handoff") added a read barrier after p9_client_rpc() waits for req->status, pairing with the write barrier in p9_client_cb(). The virtio zero-copy wait path was missed. Add the same read barrier after the zero-copy wait before reading the completed request. Fixes: 2b6e72ed747f ("9P: Add memory barriers to protect request fields over cb/rpc threads handoff") Signed-off-by: Gui-Dong Han Message-ID: <20260529075441.233369-1-hanguidong02@gmail.com> Signed-off-by: Dominique Martinet --- net/9p/trans_virtio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 4cdab7094b27..b0d0094ec8e2 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -532,6 +532,11 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); err = io_wait_event_killable(req->wq, READ_ONCE(req->status) >= REQ_STATUS_RCVD); + /* + * Make sure our req is coherent with regard to updates in other + * threads - echoes to wmb() in the callback + */ + smp_rmb(); // RERROR needs reply (== error string) in static data if (READ_ONCE(req->status) == REQ_STATUS_RCVD && unlikely(req->rc.sdata[4] == P9_RERROR)) From 89038cc87d80c77e7aa6f42a64b2573b74af339f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 19 Jun 2026 14:52:08 +0200 Subject: [PATCH 4164/5214] locking/rt: Fix the incorrect RCU protection in rt_spin_unlock() rt_spin_unlock() releases the RCU protection before unlocking the lock. That opens the door for the following UAF scenario: T1 T2 spin_lock(&p->lock); rcu_read_lock(); invalidate(p); p = rcu_dereference(ptr); rcu_assign_pointer(ptr, NULL); if (!p) return; spin_unlock(&p->lock); spin_lock(&p->lock) lock(&lock->lock); rcu_read_lock(); kfree_rcu(p); rcu_read_unlock(); .... spin_unlock(&p->lock) rcu_read_unlock(); // Ends grace period rcu_do_batch() kfree(p); UAF -> rt_mutex_cmpxchg_release(&lock->lock...) Regular spinlocks keep preemption disabled accross the unlock operation, which provides full RCU protection, but the RT substitution fails to resemble that. Same applies for the rwlock substitution. Move the rcu_read_unlock() invocation past the unlock operations to match the non-RT semantics. This makes it asymmetric vs. rt_xxx_lock(), but that's harmless as the caller needs to hold RCU read lock across the lock operation. The migrate_enable() call stays before the unlock operation because there is no per CPU operation in the unlock path which would require migration to be kept disabled. Fixes: 0f383b6dc96e ("locking/spinlock: Provide RT variant") Reported-by: syzbot+000c800a02097aaa10ed@syzkaller.appspotmail.com Decoded-by: Jann Horn Signed-off-by: Thomas Gleixner Reviewed-by: Sebastian Andrzej Siewior Acked-by: Al Viro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/87jyrud75z.ffs@fw13 --- kernel/locking/spinlock_rt.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c index db1e11b45de6..1d5e1b3c60bf 100644 --- a/kernel/locking/spinlock_rt.c +++ b/kernel/locking/spinlock_rt.c @@ -79,10 +79,27 @@ void __sched rt_spin_unlock(spinlock_t *lock) __releases(RCU) { spin_release(&lock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); if (unlikely(!rt_mutex_cmpxchg_release(&lock->lock, current, NULL))) rt_mutex_slowunlock(&lock->lock); + + /* + * This must be last to prevent the following UAF: + * + * T1 T2 + * spin_lock(&p->lock); rcu_read_lock(); + * invalidate(p); p = rcu_dereference(ptr); + * rcu_assign_pointer(ptr, NULL); if (!p) return; + * spin_unlock(&p->lock); spin_lock(&p->lock); + * kfree_rcu(p); rcu_read_unlock(); + * .... + * spin_unlock(&p->lock) + * rcu_read_unlock(); // Ends grace period + * rcu_do_batch() + * kfree(p); + * UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...) + */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_spin_unlock); @@ -262,17 +279,21 @@ void __sched rt_read_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); rwbase_read_unlock(&rwlock->rwbase, TASK_RTLOCK_WAIT); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_read_unlock); void __sched rt_write_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); - rcu_read_unlock(); migrate_enable(); rwbase_write_unlock(&rwlock->rwbase); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_write_unlock); From 043db005a8d6932dc7d217c86307e9af0bc10ddc Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:16 +0530 Subject: [PATCH 4165/5214] irqchip/crossbar: Use correct index in crossbar_domain_free() crossbar_domain_free() resets the domain data and then uses the nulled out irq_data->hwirq member as index to reset the irq_map[] entry and to write the relevant crossbar register with a safe entry. That means it never frees the correct index and keeps the crossbar register connection to the source interrupt active. If it would not reset the domain data, then this would be even worse as irq_data->hwirq holds the source interrupt number, but both the map and register index need the corresponding GIC SPI number and not the source interrupt number. This might even result in an out of bounds access as the source interrupt number can be higher than the maximal index space. Fix this by using the GIC SPI index from the parent domain's irq_data. Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-1-b8e8499f468a@gmail.com --- drivers/irqchip/irq-crossbar.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index cd1134101ace..4e19e9d8a41d 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -158,9 +158,14 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, for (i = 0; i < nr_irqs; i++) { struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + /* + * irq_map[] is indexed by GIC SPI number. The parent domain's + * hwirq contains the GIC interrupt number (GIC SPI + + * GIC_IRQ_START). + */ + cb->irq_map[d->parent_data->hwirq - GIC_IRQ_START] = IRQ_FREE; + cb->write(d->parent_data->hwirq - GIC_IRQ_START, cb->safe_map); irq_domain_reset_irq_data(d); - cb->irq_map[d->hwirq] = IRQ_FREE; - cb->write(d->hwirq, cb->safe_map); } raw_spin_unlock(&cb->lock); } From a1074dd62faa6572921d387e8a21589ccea00efc Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:17 +0530 Subject: [PATCH 4166/5214] irqchip/crossbar: Fix parent domain resource leak irq_domain_alloc_irqs_parent() is called in allocate_gic_irq() but irq_domain_free_irqs_parent() is never called which causes a resource leak. Fix this by calling irq_domain_free_irqs_parent() in crossbar_domain_free(). Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-2-b8e8499f468a@gmail.com --- drivers/irqchip/irq-crossbar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 4e19e9d8a41d..033b08782119 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -168,6 +168,7 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, irq_domain_reset_irq_data(d); } raw_spin_unlock(&cb->lock); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); } static int crossbar_domain_translate(struct irq_domain *d, From c3027973f692077a1b66a9fb26d6a7c46c0dc72c Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Sat, 6 Jun 2026 15:56:06 +0000 Subject: [PATCH 4167/5214] drm/nouveau/acr: fix missing nvkm_done() in error path of nvkm_acr_oneinit() In nvkm_acr_oneinit(), nvkm_kmap(acr->wpr) is invoked unconditionally at line 309 to obtain a mapping reference. Additionally, when both acr->wpr_fw and acr->wpr_comp are present, a second nvkm_kmap() is called inside the conditional block. Both mappings are expected to be released by nvkm_done(acr->wpr) at line 320 before the function returns successfully. However, when a mismatch is detected during the loop within the conditional block, the function returns -EINVAL at line 318 without calling nvkm_done(). This results in a leak of the kmap reference(s) acquired earlier. Fix the issue by invoking nvkm_done(acr->wpr) prior to the early return to ensure proper release of the mapping references. Fixes: 22dcda45a3d1 ("drm/nouveau/acr: implement new subdev to replace "secure boot"") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260606155606.77593-1-vulab@iscas.ac.cn Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c index 4c7745cd6ae5..7fd967a2554f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c @@ -315,6 +315,7 @@ nvkm_acr_oneinit(struct nvkm_subdev *subdev) i, us, fw); } } + nvkm_done(acr->wpr); return -EINVAL; } nvkm_done(acr->wpr); From ab99ead646b1b833ecd57fe577a2816f2e848167 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 10 Jun 2026 18:01:28 +0800 Subject: [PATCH 4168/5214] drm/nouveau: fix reversed error cleanup order in ucopy functions nouveau_uvmm_vm_bind_ucopy() and nouveau_exec_ucopy() place their error cleanup labels in allocation order rather than reverse allocation order. On a u_memcpya() failure for in_sync.s, the goto to err_free_ops (or err_free_pushs) frees the first allocation and then falls through to err_free_ins, which calls u_free() on args->in_sync.s. Since args->in_sync.s still holds the ERR_PTR returned by the failed u_memcpya(), and ERR_PTR values are not caught by ZERO_OR_NULL_PTR(), kvfree() proceeds to dereference it, which can result in a kernel oops. A failure for out_sync.s instead jumps to err_free_ins and skips freeing the first allocation, leading to a memory leak. Fix by swapping the cleanup label order so resources are freed in the correct reverse allocation sequence. Fixes: b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB7881484D91A6F80271415F71AF1A2@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_exec.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_uvmm.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index c01a01aee32b..a08ab1cfea9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -331,10 +331,10 @@ nouveau_exec_ucopy(struct nouveau_exec_job_args *args, return 0; -err_free_pushs: - u_free(args->push.s); err_free_ins: u_free(args->in_sync.s); +err_free_pushs: + u_free(args->push.s); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index 36445915aa58..f5e4756b4de4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -1779,10 +1779,10 @@ nouveau_uvmm_vm_bind_ucopy(struct nouveau_uvmm_bind_job_args *args, return 0; -err_free_ops: - u_free(args->op.s); err_free_ins: u_free(args->in_sync.s); +err_free_ops: + u_free(args->op.s); return ret; } From 0c3a350d13ce8bb7c3427597819b0dfbc19ba242 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 4 Jun 2026 10:40:08 +0800 Subject: [PATCH 4169/5214] mm/alloc_tag: replace fixed-size early PFN array with dynamic linked list Pages allocated before page_ext is available have their codetag left uninitialized. Track these early PFNs and clear their codetag in clear_early_alloc_pfn_tag_refs() to avoid "alloc_tag was not set" warnings when they are freed later. Currently a fixed-size array of 8192 entries is used, with a warning if the limit is exceeded. However, the number of early allocations depends on the number of CPUs and can be larger than 8192. Replace the fixed-size array with a dynamically allocated linked list of pfn_pool structs. Each node is allocated via alloc_page() and mapped to a pfn_pool containing a next pointer, an atomic slot counter, and a PFN array that fills the remainder of the page. The tracking pages themselves are allocated via alloc_page(), which would trigger __pgalloc_tag_add() -> alloc_tag_add_early_pfn() and recurse indefinitely. Introduce __GFP_NO_CODETAG (reuses the %__GFP_NO_OBJ_EXT bit) and pass gfp_flags through pgalloc_tag_add() so that the early path can skip recording allocations that carry this flag. Link: https://lore.kernel.org/20260604024008.46592-1-hao.ge@linux.dev Signed-off-by: Hao Ge Suggested-by: Suren Baghdasaryan Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Signed-off-by: Andrew Morton --- include/linux/alloc_tag.h | 4 +- lib/alloc_tag.c | 140 +++++++++++++++++++++++++------------- mm/page_alloc.c | 12 ++-- 3 files changed, 99 insertions(+), 57 deletions(-) diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index 02de2ede560f..068ba2e77c5d 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -163,11 +163,11 @@ static inline void alloc_tag_sub_check(union codetag_ref *ref) { WARN_ONCE(ref && !ref->ct, "alloc_tag was not set\n"); } -void alloc_tag_add_early_pfn(unsigned long pfn); +void alloc_tag_add_early_pfn(unsigned long pfn, gfp_t gfp_flags); #else static inline void alloc_tag_add_check(union codetag_ref *ref, struct alloc_tag *tag) {} static inline void alloc_tag_sub_check(union codetag_ref *ref) {} -static inline void alloc_tag_add_early_pfn(unsigned long pfn) {} +static inline void alloc_tag_add_early_pfn(unsigned long pfn, gfp_t gfp_flags) {} #endif /* Caller should verify both ref and tag to be valid */ diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index ed1bdcf1f8ab..f2f574bcf383 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -767,50 +767,82 @@ static __init bool need_page_alloc_tagging(void) * their codetag uninitialized. Track these early PFNs so we can clear * their codetag refs later to avoid warnings when they are freed. * - * Early allocations include: - * - Base allocations independent of CPU count - * - Per-CPU allocations (e.g., CPU hotplug callbacks during smp_init, - * such as trace ring buffers, scheduler per-cpu data) - * - * For simplicity, we fix the size to 8192. - * If insufficient, a warning will be triggered to alert the user. - * - * TODO: Replace fixed-size array with dynamic allocation using - * a GFP flag similar to ___GFP_NO_OBJ_EXT to avoid recursion. + * Each page is cast to a pfn_pool: the first few bytes hold metadata + * (next pointer and slot count), the remainder stores PFNs. */ -#define EARLY_ALLOC_PFN_MAX 8192 +struct pfn_pool { + struct pfn_pool *next; + atomic_t count; + unsigned long pfns[]; +}; -static unsigned long early_pfns[EARLY_ALLOC_PFN_MAX] __initdata; -static atomic_t early_pfn_count __initdata = ATOMIC_INIT(0); +#define PFN_POOL_SIZE ((PAGE_SIZE - offsetof(struct pfn_pool, pfns)) / \ + sizeof(unsigned long)) + +/* + * Skip early PFN recording for a page allocation. Reuses the + * %__GFP_NO_OBJ_EXT bit. Used by __alloc_tag_add_early_pfn() to avoid + * recursion when allocating pages for the early PFN tracking list + * itself. + * + * Codetags of the pages allocated with __GFP_NO_CODETAG should be + * cleared (via clear_page_tag_ref()) before freeing the pages to prevent + * alloc_tag_sub_check() from triggering a warning. + */ +#define __GFP_NO_CODETAG __GFP_NO_OBJ_EXT + +static struct pfn_pool *current_pfn_pool __initdata; static void __init __alloc_tag_add_early_pfn(unsigned long pfn) { - int old_idx, new_idx; + struct pfn_pool *pool; + int idx; do { - old_idx = atomic_read(&early_pfn_count); - if (old_idx >= EARLY_ALLOC_PFN_MAX) { - pr_warn_once("Early page allocations before page_ext init exceeded EARLY_ALLOC_PFN_MAX (%d)\n", - EARLY_ALLOC_PFN_MAX); - return; - } - new_idx = old_idx + 1; - } while (!atomic_try_cmpxchg(&early_pfn_count, &old_idx, new_idx)); + pool = READ_ONCE(current_pfn_pool); + if (!pool || atomic_read(&pool->count) >= PFN_POOL_SIZE) { + struct page *new_page = alloc_page(__GFP_HIGH | __GFP_NO_CODETAG); + struct pfn_pool *new; - early_pfns[old_idx] = pfn; + if (!new_page) { + pr_warn_once("early PFN tracking page allocation failed\n"); + return; + } + new = page_address(new_page); + new->next = pool; + atomic_set(&new->count, 0); + if (cmpxchg(¤t_pfn_pool, pool, new) != pool) { + clear_page_tag_ref(new_page); + __free_page(new_page); + continue; + } + pool = new; + } + idx = atomic_read(&pool->count); + if (idx >= PFN_POOL_SIZE) + continue; + if (atomic_cmpxchg(&pool->count, idx, idx + 1) == idx) + break; + } while (1); + + pool->pfns[idx] = pfn; } typedef void alloc_tag_add_func(unsigned long pfn); static alloc_tag_add_func __rcu *alloc_tag_add_early_pfn_ptr __refdata = RCU_INITIALIZER(__alloc_tag_add_early_pfn); -void alloc_tag_add_early_pfn(unsigned long pfn) +void alloc_tag_add_early_pfn(unsigned long pfn, gfp_t gfp_flags) { alloc_tag_add_func *alloc_tag_add; if (static_key_enabled(&mem_profiling_compressed)) return; + /* Skip allocations for the tracking list itself to avoid recursion. */ + if (gfp_flags & __GFP_NO_CODETAG) + return; + rcu_read_lock(); alloc_tag_add = rcu_dereference(alloc_tag_add_early_pfn_ptr); if (alloc_tag_add) @@ -820,7 +852,9 @@ void alloc_tag_add_early_pfn(unsigned long pfn) static void __init clear_early_alloc_pfn_tag_refs(void) { - unsigned int i; + struct pfn_pool *pool, *next; + struct page *page; + int i; if (static_key_enabled(&mem_profiling_compressed)) return; @@ -829,37 +863,45 @@ static void __init clear_early_alloc_pfn_tag_refs(void) /* Make sure we are not racing with __alloc_tag_add_early_pfn() */ synchronize_rcu(); - for (i = 0; i < atomic_read(&early_pfn_count); i++) { - unsigned long pfn = early_pfns[i]; + for (pool = current_pfn_pool; pool; pool = next) { + int nr_pfns = atomic_read(&pool->count); - if (pfn_valid(pfn)) { - struct page *page = pfn_to_page(pfn); - union pgtag_ref_handle handle; - union codetag_ref ref; + for (i = 0; i < nr_pfns; i++) { + unsigned long pfn = pool->pfns[i]; - if (get_page_tag_ref(page, &ref, &handle)) { - /* - * An early-allocated page could be freed and reallocated - * after its page_ext is initialized but before we clear it. - * In that case, it already has a valid tag set. - * We should not overwrite that valid tag with CODETAG_EMPTY. - * - * Note: there is still a small race window between checking - * ref.ct and calling set_codetag_empty(). We accept this - * race as it's unlikely and the extra complexity of atomic - * cmpxchg is not worth it for this debug-only code path. - */ - if (ref.ct) { + if (pfn_valid(pfn)) { + union pgtag_ref_handle handle; + union codetag_ref ref; + + if (get_page_tag_ref(pfn_to_page(pfn), &ref, &handle)) { + /* + * An early-allocated page could be freed and reallocated + * after its page_ext is initialized but before we clear it. + * In that case, it already has a valid tag set. + * We should not overwrite that valid tag + * with CODETAG_EMPTY. + * + * Note: there is still a small race window between checking + * ref.ct and calling set_codetag_empty(). We accept this + * race as it's unlikely and the extra complexity of atomic + * cmpxchg is not worth it for this debug-only code path. + */ + if (ref.ct) { + put_page_tag_ref(handle); + continue; + } + + set_codetag_empty(&ref); + update_page_tag_ref(handle, &ref); put_page_tag_ref(handle); - continue; } - - set_codetag_empty(&ref); - update_page_tag_ref(handle, &ref); - put_page_tag_ref(handle); } } + next = pool->next; + page = virt_to_page(pool); + clear_page_tag_ref(page); + __free_page(page); } } #else /* !CONFIG_MEM_ALLOC_PROFILING_DEBUG */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f7db8f049bd2..81a9d4d1e6c0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1246,7 +1246,7 @@ void __clear_page_tag_ref(struct page *page) /* Should be called only if mem_alloc_profiling_enabled() */ static noinline void __pgalloc_tag_add(struct page *page, struct task_struct *task, - unsigned int nr) + unsigned int nr, gfp_t gfp_flags) { union pgtag_ref_handle handle; union codetag_ref ref; @@ -1260,17 +1260,17 @@ void __pgalloc_tag_add(struct page *page, struct task_struct *task, * page_ext is not available yet, record the pfn so we can * clear the tag ref later when page_ext is initialized. */ - alloc_tag_add_early_pfn(page_to_pfn(page)); + alloc_tag_add_early_pfn(page_to_pfn(page), gfp_flags); if (task->alloc_tag) alloc_tag_set_inaccurate(task->alloc_tag); } } static inline void pgalloc_tag_add(struct page *page, struct task_struct *task, - unsigned int nr) + unsigned int nr, gfp_t gfp_flags) { if (mem_alloc_profiling_enabled()) - __pgalloc_tag_add(page, task, nr); + __pgalloc_tag_add(page, task, nr, gfp_flags); } /* Should be called only if mem_alloc_profiling_enabled() */ @@ -1303,7 +1303,7 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) #else /* CONFIG_MEM_ALLOC_PROFILING */ static inline void pgalloc_tag_add(struct page *page, struct task_struct *task, - unsigned int nr) {} + unsigned int nr, gfp_t gfp_flags) {} static inline void pgalloc_tag_sub(struct page *page, unsigned int nr) {} static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) {} @@ -1858,7 +1858,7 @@ inline void post_alloc_hook(struct page *page, unsigned int order, set_page_owner(page, order, gfp_flags); page_table_check_alloc(page, order); - pgalloc_tag_add(page, current, 1 << order); + pgalloc_tag_add(page, current, 1 << order, gfp_flags); } static void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags, From 2956268efc457cb05d29c1bf94de1e8e684d7bbc Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 4 Jun 2026 14:59:38 +0800 Subject: [PATCH 4170/5214] alloc_tag: fix use-after-free in /proc/allocinfo after module unload allocinfo_start() only reinitializes the codetag iterator at position 0. For subsequent reads (position > 0), it reuses cached iterator state from the previous batch. allocinfo_stop() drops mod_lock between read batches, which allows module unload to complete and free the module memory that the cached iterator still references: CPU0 (read) CPU1 (rmmod) ---- ---- allocinfo_start(pos=0) down_read(mod_lock) allocinfo_show() ... allocinfo_stop() up_read(mod_lock) codetag_unload_module() kfree(cmod) release_module_tags() ... free_mod_mem() allocinfo_start(pos=N) down_read(mod_lock) // reuses cached iter, skips re-init allocinfo_show() ct->filename <-- UAF After free_mod_mem() frees the module's .rodata, allocinfo_show() dereferences ct->filename, ct->function which point there. Save the iterator state in allocinfo_next() and resume from it in allocinfo_start() with codetag_next_ct(), which detects module removal via idr_find() returning NULL and skips to the next module. Link: https://lore.kernel.org/20260604065938.105991-1-hao.ge@linux.dev Fixes: 9f44df50fee4 ("alloc_tag: keep codetag iterator active between read()") Signed-off-by: Hao Ge Suggested-by: Suren Baghdasaryan Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Signed-off-by: Andrew Morton --- lib/alloc_tag.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index f2f574bcf383..551cc14bb1fd 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -45,6 +45,7 @@ int alloc_tag_ref_offs; struct allocinfo_private { struct codetag_iterator iter; + struct codetag_iterator reported_iter; bool print_header; }; @@ -58,16 +59,20 @@ static void *allocinfo_start(struct seq_file *m, loff_t *pos) if (node == 0) { priv->print_header = true; priv->iter = codetag_get_ct_iter(alloc_tag_cttype); - codetag_next_ct(&priv->iter); + } else { + priv->iter = priv->reported_iter; } + codetag_next_ct(&priv->iter); return priv->iter.ct ? priv : NULL; } static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos) { struct allocinfo_private *priv = (struct allocinfo_private *)arg; - struct codetag *ct = codetag_next_ct(&priv->iter); + struct codetag *ct; + priv->reported_iter = priv->iter; + ct = codetag_next_ct(&priv->iter); (*pos)++; if (!ct) return NULL; From 67c2696cf76aa276376fdd845c7d1b3e16b97af2 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 24 Mar 2026 14:42:25 -0700 Subject: [PATCH 4171/5214] lib: split codetag_lock_module_list() Letting a function argument indicate whether a lock or unlock operation should be performed is incompatible with compile-time analysis of locking operations by sparse and Clang. Hence, split codetag_lock_module_list() into two functions: a function that locks cttype->mod_lock and another function that unlocks cttype->mod_lock. No functionality has been changed. See also commit 916cc5167cc6 ("lib: code tagging framework"). Link: https://lore.kernel.org/20260324214226.3684605-1-bvanassche@acm.org Signed-off-by: Bart Van Assche Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Cc: Nathan Chancellor Signed-off-by: Andrew Morton --- include/linux/codetag.h | 3 ++- lib/alloc_tag.c | 8 ++++---- lib/codetag.c | 12 +++++++----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/include/linux/codetag.h b/include/linux/codetag.h index 8ea2a5f7c98a..ddae7484ca45 100644 --- a/include/linux/codetag.h +++ b/include/linux/codetag.h @@ -74,8 +74,9 @@ struct codetag_iterator { .flags = 0, \ } -void codetag_lock_module_list(struct codetag_type *cttype, bool lock); +void codetag_lock_module_list(struct codetag_type *cttype); bool codetag_trylock_module_list(struct codetag_type *cttype); +void codetag_unlock_module_list(struct codetag_type *cttype); struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype); struct codetag *codetag_next_ct(struct codetag_iterator *iter); diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index 551cc14bb1fd..d9be1cf5187d 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -55,7 +55,7 @@ static void *allocinfo_start(struct seq_file *m, loff_t *pos) loff_t node = *pos; priv = (struct allocinfo_private *)m->private; - codetag_lock_module_list(alloc_tag_cttype, true); + codetag_lock_module_list(alloc_tag_cttype); if (node == 0) { priv->print_header = true; priv->iter = codetag_get_ct_iter(alloc_tag_cttype); @@ -82,7 +82,7 @@ static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos) static void allocinfo_stop(struct seq_file *m, void *arg) { - codetag_lock_module_list(alloc_tag_cttype, false); + codetag_unlock_module_list(alloc_tag_cttype); } static void print_allocinfo_header(struct seq_buf *buf) @@ -141,7 +141,7 @@ size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sl return 0; if (can_sleep) - codetag_lock_module_list(alloc_tag_cttype, true); + codetag_lock_module_list(alloc_tag_cttype); else if (!codetag_trylock_module_list(alloc_tag_cttype)) return 0; @@ -166,7 +166,7 @@ size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sl } } - codetag_lock_module_list(alloc_tag_cttype, false); + codetag_unlock_module_list(alloc_tag_cttype); return nr; } diff --git a/lib/codetag.c b/lib/codetag.c index 304667897ad4..4001a7ea6675 100644 --- a/lib/codetag.c +++ b/lib/codetag.c @@ -35,12 +35,9 @@ struct codetag_module { static DEFINE_MUTEX(codetag_lock); static LIST_HEAD(codetag_types); -void codetag_lock_module_list(struct codetag_type *cttype, bool lock) +void codetag_lock_module_list(struct codetag_type *cttype) { - if (lock) - down_read(&cttype->mod_lock); - else - up_read(&cttype->mod_lock); + down_read(&cttype->mod_lock); } bool codetag_trylock_module_list(struct codetag_type *cttype) @@ -48,6 +45,11 @@ bool codetag_trylock_module_list(struct codetag_type *cttype) return down_read_trylock(&cttype->mod_lock) != 0; } +void codetag_unlock_module_list(struct codetag_type *cttype) +{ + up_read(&cttype->mod_lock); +} + struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype) { struct codetag_iterator iter = { From b35a8205a3cc87c1fcfb5d2ba25f49ea9342cbc7 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 4 Jun 2026 10:20:03 +0200 Subject: [PATCH 4172/5214] zsmalloc: simplify data output in zs_stats_size_show() Move the specification for a line break from a seq_puts() call to a seq_printf() call. The source code was transformed by using the Coccinelle software. Link: https://lore.kernel.org/126a924b-6f68-43bf-ae5a-449fb93e527b@web.de Signed-off-by: Markus Elfring Reviewed-by: Sergey Senozhatsky Cc: Minchan Kim Signed-off-by: Andrew Morton --- mm/zsmalloc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 63128ddb7959..83f5820c45f9 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -565,8 +565,7 @@ static int zs_stats_size_show(struct seq_file *s, void *v) total_freeable += freeable; } - seq_puts(s, "\n"); - seq_printf(s, " %5s %5s ", "Total", ""); + seq_printf(s, "\n %5s %5s ", "Total", ""); for (fg = ZS_INUSE_RATIO_10; fg < NR_FULLNESS_GROUPS; fg++) seq_printf(s, "%9lu ", inuse_totals[fg]); From 874611d193f29666184bcbcb8638eff87d63d019 Mon Sep 17 00:00:00 2001 From: Jianlin Shi Date: Thu, 4 Jun 2026 12:01:38 +0800 Subject: [PATCH 4173/5214] mm/page_alloc: only update NUMA min ratios on sysctl write The sysctl handlers for min_unmapped_ratio and min_slab_ratio invoke setup_min_unmapped_ratio() and setup_min_slab_ratio() unconditionally after proc_dointvec_minmax(), even for read operations. These setup functions first zero all per-NUMA node thresholds (min_unmapped_pages and min_slab_pages) before recalculating them. Reading /proc sysctl entries therefore temporarily resets node reclaim thresholds to zero, which may disturb the behavior of __node_reclaim() and node_reclaim() during the recomputation. Fix this by only calling the setup functions when the sysctl is actually written (write == 1), matching the behavior of existing sysctl handlers like min_free_kbytes and watermark_scale_factor. This only affects systems with CONFIG_NUMA. Link: https://lore.kernel.org/tencent_5891052AF9A4C2D490A62F478D446F74AB09@qq.com Signed-off-by: Jianlin Shi Cc: Brendan Jackman Cc: Johannes Weiner Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/page_alloc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 81a9d4d1e6c0..ee902a468c2f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6653,7 +6653,8 @@ static int sysctl_min_unmapped_ratio_sysctl_handler(const struct ctl_table *tabl if (rc) return rc; - setup_min_unmapped_ratio(); + if (write) + setup_min_unmapped_ratio(); return 0; } @@ -6680,7 +6681,8 @@ static int sysctl_min_slab_ratio_sysctl_handler(const struct ctl_table *table, i if (rc) return rc; - setup_min_slab_ratio(); + if (write) + setup_min_slab_ratio(); return 0; } From 472ebd691d979c9f3545c469ac79d6575709bbdf Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:08 -0600 Subject: [PATCH 4174/5214] mm/khugepaged: generalize hugepage_vma_revalidate for mTHP support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "khugepaged: add mTHP collapse support", v19. The following series provides khugepaged with the capability to collapse anonymous memory regions to mTHPs. To achieve this we generalize the khugepaged functions to no longer depend on PMD_ORDER. Then during the PMD scan, we use a bitmap to track individual pages that are occupied (!none/zero). After the PMD scan is done, we use the bitmap to find the optimal mTHP sizes for the PMD range. The restriction on max_ptes_none is removed during the scan, to make sure we account for the whole PMD range in the bitmap. When no mTHP size is enabled, the legacy behavior of khugepaged is maintained. We currently only support max_ptes_none values of 0 or HPAGE_PMD_NR - 1 (ie 511). If any other value is specified, the kernel will emit a warning and mTHP collapse will default to max_ptes_none=0. If a mTHP collapse is attempted, but contains swapped out, or shared pages, we don't perform the collapse. It is now also possible to collapse to mTHPs without requiring the PMD THP size to be enabled. These limitations are to prevent collapse "creep" behavior. This prevents constantly promoting mTHPs to the next available size, which would occur because a collapse introduces more non-zero pages that would satisfy the promotion condition on subsequent scans. Patch 1-2: Generalize hugepage_vma_revalidate and alloc_charge_folio for arbitrary orders. Patch 3: Rework max_ptes_* handling into helper functions Patch 4: Generalize __collapse_huge_page_* for mTHP support Patch 5: Require collapse_huge_page to enter/exit with the lock dropped Patch 6: Generalize collapse_huge_page for mTHP collapse Patch 7: Skip collapsing mTHP to smaller orders Patch 8-9: Add per-order mTHP statistics and tracepoints Patch 10: Introduce collapse_possible_orders helper functions Patch 11-13: Introduce bitmap and mTHP collapse support, fully enabled Patch 14: Documentation Testing: - Built for x86_64, aarch64, ppc64le, and s390x - ran all arches on test suites provided by the kernel-tests project - internal testing suites: functional testing and performance testing - selftests mm - I created a test script that I used to push khugepaged to its limits while monitoring a number of stats and tracepoints. The code is available here[1] (Run in legacy mode for these changes and set mthp sizes to inherit) The summary from my testings was that there was no significant regression noticed through this test. In some cases my changes had better collapse latencies, and was able to scan more pages in the same amount of time/work, but for the most part the results were consistent. - redis testing. I did some testing with these changes along with my defer changes (see followup [2] post for more details). We've decided to get the mTHP changes merged first before attempting the defer series. - some basic testing on 64k page size. - lots of general use. This patch (of 14): For khugepaged to support different mTHP orders, we must generalize this to check if the PMD is not shared by another VMA and that the order is enabled. We cannot collapse VMA regions that do not span the full PMD. This is due to the potential of the PMD being shared by another VMA which leaves us vulnerable to race conditions if neighboring VMAs are resized. Always check the PMD order here to ensure its not shared by another VMA. We'd need to lock all VMAs in the PMD range to support this which may lead to increased lock contention and code complexity. No functional change in this patch. Also correct a comment about the functionality of the revalidation and fix a double space issues. Link: https://lore.kernel.org/20260605161422.213817-1-npache@redhat.com Link: https://lore.kernel.org/20260605161422.213817-2-npache@redhat.com Link: https://gitlab.com/npache/khugepaged_mthp_test [1] Link: https://lore.kernel.org/lkml/20250515033857.132535-1-npache@redhat.com/ [2] Co-developed-by: Dev Jain Signed-off-by: Dev Jain Signed-off-by: Nico Pache Reviewed-by: Wei Yang Reviewed-by: Lance Yang Reviewed-by: Baolin Wang Reviewed-by: Lorenzo Stoakes Reviewed-by: Zi Yan Acked-by: Usama Arif Acked-by: David Hildenbrand (Arm) Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Signed-off-by: Andrew Morton --- mm/khugepaged.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 73e262cb30dd..cc1328cb6237 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -905,12 +905,13 @@ static int collapse_find_target_node(struct collapse_control *cc) /* * If mmap_lock temporarily dropped, revalidate vma - * before taking mmap_lock. + * after taking the mmap_lock again. * Returns enum scan_result value. */ static enum scan_result hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address, - bool expect_anon, struct vm_area_struct **vmap, struct collapse_control *cc) + bool expect_anon, struct vm_area_struct **vmap, + struct collapse_control *cc, unsigned int order) { struct vm_area_struct *vma; enum tva_type type = cc->is_khugepaged ? TVA_KHUGEPAGED : @@ -923,15 +924,22 @@ static enum scan_result hugepage_vma_revalidate(struct mm_struct *mm, unsigned l if (!vma) return SCAN_VMA_NULL; + /* + * We cannot collapse VMA regions that do not span the full PMD. This is + * due to the potential of the PMD being shared by another VMA leaving + * us vulnerable to a race condition. Always check the PMD order here to + * ensure its not shared by another VMA. We'd need to lock all VMAs in + * the PMD range to support this. + */ if (!thp_vma_suitable_order(vma, address, PMD_ORDER)) return SCAN_ADDRESS_RANGE; - if (!thp_vma_allowable_order(vma, vma->vm_flags, type, PMD_ORDER)) + if (!thp_vma_allowable_orders(vma, vma->vm_flags, type, BIT(order))) return SCAN_VMA_CHECK; /* * Anon VMA expected, the address may be unmapped then * remapped to file after khugepaged reaquired the mmap_lock. * - * thp_vma_allowable_order may return true for qualified file + * thp_vma_allowable_orders may return true for qualified file * vmas. */ if (expect_anon && (!(*vmap)->anon_vma || !vma_is_anonymous(*vmap))) @@ -1129,7 +1137,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a } mmap_read_lock(mm); - result = hugepage_vma_revalidate(mm, address, true, &vma, cc); + result = hugepage_vma_revalidate(mm, address, true, &vma, cc, + HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) { mmap_read_unlock(mm); goto out_nolock; @@ -1163,7 +1172,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * mmap_lock. */ mmap_write_lock(mm); - result = hugepage_vma_revalidate(mm, address, true, &vma, cc); + result = hugepage_vma_revalidate(mm, address, true, &vma, cc, + HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) goto out_up_write; /* check if the pmd is still valid */ @@ -2866,8 +2876,8 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, mmap_unlocked = false; *lock_dropped = true; result = hugepage_vma_revalidate(mm, addr, false, &vma, - cc); - if (result != SCAN_SUCCEED) { + cc, HPAGE_PMD_ORDER); + if (result != SCAN_SUCCEED) { last_fail = result; goto out_nolock; } From d04fa025671ea49e6950b3261af115eff1e6a608 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Fri, 5 Jun 2026 10:14:09 -0600 Subject: [PATCH 4175/5214] mm/khugepaged: generalize alloc_charge_folio() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass order to alloc_charge_folio() and update mTHP statistics. Link: https://lore.kernel.org/20260605161422.213817-3-npache@redhat.com Signed-off-by: Dev Jain Co-developed-by: Nico Pache Signed-off-by: Nico Pache Reviewed-by: Wei Yang Reviewed-by: Lance Yang Reviewed-by: Baolin Wang Reviewed-by: Lorenzo Stoakes Reviewed-by: Zi Yan Acked-by: Usama Arif Acked-by: David Hildenbrand (Arm) Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/transhuge.rst | 8 ++++++++ include/linux/huge_mm.h | 2 ++ mm/huge_memory.c | 4 ++++ mm/khugepaged.c | 20 +++++++++++++------- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index 76f4eb14e262..a74844e01f1e 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -639,6 +639,14 @@ anon_fault_fallback_charge instead falls back to using huge pages with lower orders or small pages even though the allocation was successful. +collapse_alloc + is incremented every time a huge page is successfully allocated for a + khugepaged collapse. + +collapse_alloc_failed + is incremented every time a huge page allocation fails during a + khugepaged collapse. + zswpout is incremented every time a huge page is swapped out to zswap in one piece without splitting. diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index c0d223d0c556..e225d1d4a3fe 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -128,6 +128,8 @@ enum mthp_stat_item { MTHP_STAT_ANON_FAULT_ALLOC, MTHP_STAT_ANON_FAULT_FALLBACK, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE, + MTHP_STAT_COLLAPSE_ALLOC, + MTHP_STAT_COLLAPSE_ALLOC_FAILED, MTHP_STAT_ZSWPOUT, MTHP_STAT_SWPIN, MTHP_STAT_SWPIN_FALLBACK, diff --git a/mm/huge_memory.c b/mm/huge_memory.c index a5176653ba1f..c8b851dd518a 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -702,6 +702,8 @@ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) DEFINE_MTHP_STAT_ATTR(anon_fault_alloc, MTHP_STAT_ANON_FAULT_ALLOC); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback, MTHP_STAT_ANON_FAULT_FALLBACK); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback_charge, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); +DEFINE_MTHP_STAT_ATTR(collapse_alloc, MTHP_STAT_COLLAPSE_ALLOC); +DEFINE_MTHP_STAT_ATTR(collapse_alloc_failed, MTHP_STAT_COLLAPSE_ALLOC_FAILED); DEFINE_MTHP_STAT_ATTR(zswpout, MTHP_STAT_ZSWPOUT); DEFINE_MTHP_STAT_ATTR(swpin, MTHP_STAT_SWPIN); DEFINE_MTHP_STAT_ATTR(swpin_fallback, MTHP_STAT_SWPIN_FALLBACK); @@ -767,6 +769,8 @@ static struct attribute *any_stats_attrs[] = { #endif &split_attr.attr, &split_failed_attr.attr, + &collapse_alloc_attr.attr, + &collapse_alloc_failed_attr.attr, NULL, }; diff --git a/mm/khugepaged.c b/mm/khugepaged.c index cc1328cb6237..454ca90e3696 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1077,28 +1077,34 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm, } static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_struct *mm, - struct collapse_control *cc) + struct collapse_control *cc, unsigned int order) { gfp_t gfp = (cc->is_khugepaged ? alloc_hugepage_khugepaged_gfpmask() : GFP_TRANSHUGE); int node = collapse_find_target_node(cc); struct folio *folio; - folio = __folio_alloc(gfp, HPAGE_PMD_ORDER, node, &cc->alloc_nmask); + folio = __folio_alloc(gfp, order, node, &cc->alloc_nmask); if (!folio) { *foliop = NULL; - count_vm_event(THP_COLLAPSE_ALLOC_FAILED); + if (is_pmd_order(order)) + count_vm_event(THP_COLLAPSE_ALLOC_FAILED); + count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC_FAILED); return SCAN_ALLOC_HUGE_PAGE_FAIL; } - count_vm_event(THP_COLLAPSE_ALLOC); + if (is_pmd_order(order)) + count_vm_event(THP_COLLAPSE_ALLOC); + count_mthp_stat(order, MTHP_STAT_COLLAPSE_ALLOC); + if (unlikely(mem_cgroup_charge(folio, mm, gfp))) { folio_put(folio); *foliop = NULL; return SCAN_CGROUP_CHARGE_FAIL; } - count_memcg_folio_events(folio, THP_COLLAPSE_ALLOC, 1); + if (is_pmd_order(order)) + count_memcg_folio_events(folio, THP_COLLAPSE_ALLOC, 1); *foliop = folio; return SCAN_SUCCEED; @@ -1127,7 +1133,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a */ mmap_read_unlock(mm); - result = alloc_charge_folio(&folio, mm, cc); + result = alloc_charge_folio(&folio, mm, cc, HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) goto out_nolock; @@ -1913,7 +1919,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, VM_BUG_ON(!IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && !is_shmem); VM_BUG_ON(start & (HPAGE_PMD_NR - 1)); - result = alloc_charge_folio(&new_folio, mm, cc); + result = alloc_charge_folio(&new_folio, mm, cc, HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) goto out; From 45b310faeefaea811291bef934ccc11e81761326 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:10 -0600 Subject: [PATCH 4176/5214] mm/khugepaged: rework max_ptes_* handling with helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following cleanup reworks all the max_ptes_* handling into helper functions. This increases the code readability and will later be used to implement the mTHP handling of these variables. With these changes we abstract all the madvise_collapse() special casing (do not respect the sysctls) away from the functions that utilize them. And will be used later in this series to cleanly restrict the mTHP collapse behavior. No functional change is intended; however, we are now only reading the sysfs variables once per scan, whereas before these variables were being read on each loop iteration. Link: https://lore.kernel.org/20260605161422.213817-4-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Zi Yan Reviewed-by: Lorenzo Stoakes Reviewed-by: Lance Yang Suggested-by: David Hildenbrand Acked-by: David Hildenbrand (Arm) Acked-by: Usama Arif Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Signed-off-by: Andrew Morton --- mm/khugepaged.c | 120 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 36 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 454ca90e3696..69d34305b217 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -348,6 +348,64 @@ static bool pte_none_or_zero(pte_t pte) return pte_present(pte) && is_zero_pfn(pte_pfn(pte)); } +/** + * collapse_max_ptes_none - Calculate maximum allowed empty PTEs or PTEs mapping + * the shared zeropage for the given collapse operation. + * @cc: The collapse control struct + * @vma: The vma to check for userfaultfd + * + * Return: Maximum number of empty/shared zeropage PTEs for the collapse operation + */ +static unsigned int collapse_max_ptes_none(struct collapse_control *cc, + struct vm_area_struct *vma) +{ + if (vma && userfaultfd_armed(vma)) + return 0; + /* for MADV_COLLAPSE, allow any empty/shared zeropage PTEs */ + if (!cc->is_khugepaged) + return HPAGE_PMD_NR; + /* For all other cases respect the user defined maximum */ + return khugepaged_max_ptes_none; +} + +/** + * collapse_max_ptes_shared - Calculate maximum allowed PTEs that map shared + * anonymous pages for the given collapse operation. + * @cc: The collapse control struct + * + * Return: Maximum number of PTEs that map shared anonymous pages for the + * collapse operation + */ +static unsigned int collapse_max_ptes_shared(struct collapse_control *cc) +{ + /* + * For MADV_COLLAPSE, do not restrict the number of PTEs that map shared + * anonymous pages. + */ + if (!cc->is_khugepaged) + return HPAGE_PMD_NR; + return khugepaged_max_ptes_shared; +} + +/** + * collapse_max_ptes_swap - Calculate the maximum allowed non-present PTEs or the + * maximum allowed non-present pagecache entries for the given collapse operation. + * @cc: The collapse control struct + * + * Return: Maximum number of non-present PTEs or the maximum allowed non-present + * pagecache entries for the collapse operation. + */ +static unsigned int collapse_max_ptes_swap(struct collapse_control *cc) +{ + /* + * For MADV_COLLAPSE, do not restrict the number PTEs entries or + * pagecache entries that are non-present. + */ + if (!cc->is_khugepaged) + return HPAGE_PMD_NR; + return khugepaged_max_ptes_swap; +} + int hugepage_madvise(struct vm_area_struct *vma, vm_flags_t *vm_flags, int advice) { @@ -543,6 +601,8 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, unsigned long start_addr, pte_t *pte, struct collapse_control *cc, struct list_head *compound_pagelist) { + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma); + const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc); struct page *page = NULL; struct folio *folio = NULL; unsigned long addr = start_addr; @@ -554,16 +614,12 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, _pte++, addr += PAGE_SIZE) { pte_t pteval = ptep_get(_pte); if (pte_none_or_zero(pteval)) { - ++none_or_zero; - if (!userfaultfd_armed(vma) && - (!cc->is_khugepaged || - none_or_zero <= khugepaged_max_ptes_none)) { - continue; - } else { + if (++none_or_zero > max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; count_vm_event(THP_SCAN_EXCEED_NONE_PTE); goto out; } + continue; } if (!pte_present(pteval)) { result = SCAN_PTE_NON_PRESENT; @@ -594,9 +650,7 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, /* See collapse_scan_pmd(). */ if (folio_maybe_mapped_shared(folio)) { - ++shared; - if (cc->is_khugepaged && - shared > khugepaged_max_ptes_shared) { + if (++shared > max_ptes_shared) { result = SCAN_EXCEED_SHARED_PTE; count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); goto out; @@ -1276,6 +1330,9 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long start_addr, bool *lock_dropped, struct collapse_control *cc) { + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma); + const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc); + const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc); pmd_t *pmd; pte_t *pte, *_pte; int none_or_zero = 0, shared = 0, referenced = 0; @@ -1309,36 +1366,29 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, pte_t pteval = ptep_get(_pte); if (pte_none_or_zero(pteval)) { - ++none_or_zero; - if (!userfaultfd_armed(vma) && - (!cc->is_khugepaged || - none_or_zero <= khugepaged_max_ptes_none)) { - continue; - } else { + if (++none_or_zero > max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; count_vm_event(THP_SCAN_EXCEED_NONE_PTE); goto out_unmap; } + continue; } if (!pte_present(pteval)) { - ++unmapped; - if (!cc->is_khugepaged || - unmapped <= khugepaged_max_ptes_swap) { - /* - * Always be strict with uffd-wp - * enabled swap entries. Please see - * comment below for pte_uffd_wp(). - */ - if (pte_swp_uffd_wp_any(pteval)) { - result = SCAN_PTE_UFFD_WP; - goto out_unmap; - } - continue; - } else { + if (++unmapped > max_ptes_swap) { result = SCAN_EXCEED_SWAP_PTE; count_vm_event(THP_SCAN_EXCEED_SWAP_PTE); goto out_unmap; } + /* + * Always be strict with uffd-wp + * enabled swap entries. Please see + * comment below for pte_uffd_wp(). + */ + if (pte_swp_uffd_wp_any(pteval)) { + result = SCAN_PTE_UFFD_WP; + goto out_unmap; + } + continue; } if (pte_uffd_wp(pteval)) { /* @@ -1381,9 +1431,7 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, * is shared. */ if (folio_maybe_mapped_shared(folio)) { - ++shared; - if (cc->is_khugepaged && - shared > khugepaged_max_ptes_shared) { + if (++shared > max_ptes_shared) { result = SCAN_EXCEED_SHARED_PTE; count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); goto out_unmap; @@ -2338,6 +2386,8 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, unsigned long addr, struct file *file, pgoff_t start, struct collapse_control *cc) { + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, NULL); + const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc); struct folio *folio = NULL; struct address_space *mapping = file->f_mapping; XA_STATE(xas, &mapping->i_pages, start); @@ -2356,8 +2406,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, if (xa_is_value(folio)) { swap += 1 << xas_get_order(&xas); - if (cc->is_khugepaged && - swap > khugepaged_max_ptes_swap) { + if (swap > max_ptes_swap) { result = SCAN_EXCEED_SWAP_PTE; count_vm_event(THP_SCAN_EXCEED_SWAP_PTE); break; @@ -2428,8 +2477,7 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, cc->progress += HPAGE_PMD_NR; if (result == SCAN_SUCCEED) { - if (cc->is_khugepaged && - present < HPAGE_PMD_NR - khugepaged_max_ptes_none) { + if (present < HPAGE_PMD_NR - max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; count_vm_event(THP_SCAN_EXCEED_NONE_PTE); } else { From 5a9c05d86683db5449b7fefd278c461cffb55234 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:11 -0600 Subject: [PATCH 4177/5214] mm/khugepaged: generalize __collapse_huge_page_* for mTHP support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generalize the order of the __collapse_huge_page_* and collapse_max_* functions to support future mTHP collapse. The current mechanism for determining collapse with the khugepaged_max_ptes_none value is not designed with mTHP in mind. This raises a key design issue: if we support user defined max_pte_none values (even those scaled by order), a collapse of a lower order can introduces an feedback loop, or "creep", when max_ptes_none is set to a value greater than HPAGE_PMD_NR / 2. [1] With this configuration, a successful collapse to order N will populate enough pages to satisfy the collapse condition on order N+1 on the next scan. This leads to unnecessary work and memory churn. To fix this issue introduce a helper function that will limit mTHP collapse support to two max_ptes_none values, 0 and HPAGE_PMD_NR - 1. This effectively supports two modes: [2] - max_ptes_none=0: never collapses if it encounters an empty PTE or a PTE that maps the shared zeropage. Consequently, no memory bloat. - max_ptes_none=511 (on 4k pagesz): Always collapse to the highest available mTHP order. This removes the possibility of "creep", and a warning will be emitted if any non-supported max_ptes_none value is configured with mTHP enabled. Any intermediate value will default mTHP collapse to max_ptes_none=0. mTHP collapse will not honor the khugepaged_max_ptes_shared or khugepaged_max_ptes_swap parameters, and will fail if it encounters a shared or swapped entry. No functional changes in this patch; however it defines future behavior for mTHP collapse. Link: https://lore.kernel.org/20260605161422.213817-5-npache@redhat.com Link: https://lore.kernel.org/all/e46ab3ab-a3d7-4fb7-9970-d0704bd5d05a@arm.com [1] Link: https://lore.kernel.org/all/37375ace-5601-4d6c-9dac-d1c8268698e9@redhat.com [2] Co-developed-by: Dev Jain Signed-off-by: Dev Jain Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (arm) Reviewed-by: Lance Yang Reviewed-by: Zi Yan Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Signed-off-by: Andrew Morton --- mm/khugepaged.c | 126 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 93 insertions(+), 33 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 69d34305b217..1075dadb9de3 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -353,30 +353,51 @@ static bool pte_none_or_zero(pte_t pte) * the shared zeropage for the given collapse operation. * @cc: The collapse control struct * @vma: The vma to check for userfaultfd + * @order: The folio order being collapsed to * * Return: Maximum number of empty/shared zeropage PTEs for the collapse operation */ static unsigned int collapse_max_ptes_none(struct collapse_control *cc, - struct vm_area_struct *vma) + struct vm_area_struct *vma, unsigned int order) { + const unsigned int max_ptes_none = khugepaged_max_ptes_none; + if (vma && userfaultfd_armed(vma)) return 0; /* for MADV_COLLAPSE, allow any empty/shared zeropage PTEs */ if (!cc->is_khugepaged) return HPAGE_PMD_NR; - /* For all other cases respect the user defined maximum */ - return khugepaged_max_ptes_none; + /* for PMD collapse, respect the user defined maximum */ + if (is_pmd_order(order)) + return max_ptes_none; + /* + * for mTHP collapse with the sysctl value set to KHUGEPAGED_MAX_PTES_LIMIT, + * scale the maximum number of PTEs to the order of the collapse. + */ + if (max_ptes_none == KHUGEPAGED_MAX_PTES_LIMIT) + return (1 << order) - 1; + /* + * For mTHP collapse of values other than 0 or KHUGEPAGED_MAX_PTES_LIMIT, + * emit a warning and return 0. + */ + if (max_ptes_none) + pr_warn_once("mTHP collapse does not support max_ptes_none" + " values other than 0 or %u, defaulting to 0.\n", + KHUGEPAGED_MAX_PTES_LIMIT); + return 0; } /** * collapse_max_ptes_shared - Calculate maximum allowed PTEs that map shared * anonymous pages for the given collapse operation. * @cc: The collapse control struct + * @order: The folio order being collapsed to * * Return: Maximum number of PTEs that map shared anonymous pages for the * collapse operation */ -static unsigned int collapse_max_ptes_shared(struct collapse_control *cc) +static unsigned int collapse_max_ptes_shared(struct collapse_control *cc, + unsigned int order) { /* * For MADV_COLLAPSE, do not restrict the number of PTEs that map shared @@ -384,6 +405,13 @@ static unsigned int collapse_max_ptes_shared(struct collapse_control *cc) */ if (!cc->is_khugepaged) return HPAGE_PMD_NR; + /* + * for mTHP collapse do not allow collapsing anonymous memory pages that + * are shared between processes. + */ + if (!is_pmd_order(order)) + return 0; + /* for PMD collapse, respect the user defined maximum */ return khugepaged_max_ptes_shared; } @@ -391,11 +419,13 @@ static unsigned int collapse_max_ptes_shared(struct collapse_control *cc) * collapse_max_ptes_swap - Calculate the maximum allowed non-present PTEs or the * maximum allowed non-present pagecache entries for the given collapse operation. * @cc: The collapse control struct + * @order: The folio order being collapsed to * * Return: Maximum number of non-present PTEs or the maximum allowed non-present * pagecache entries for the collapse operation. */ -static unsigned int collapse_max_ptes_swap(struct collapse_control *cc) +static unsigned int collapse_max_ptes_swap(struct collapse_control *cc, + unsigned int order) { /* * For MADV_COLLAPSE, do not restrict the number PTEs entries or @@ -403,6 +433,10 @@ static unsigned int collapse_max_ptes_swap(struct collapse_control *cc) */ if (!cc->is_khugepaged) return HPAGE_PMD_NR; + /* for mTHP collapse do not allow any non-present PTEs or pagecache entries */ + if (!is_pmd_order(order)) + return 0; + /* for PMD collapse, respect the user defined maximum */ return khugepaged_max_ptes_swap; } @@ -599,10 +633,11 @@ static void release_pte_pages(pte_t *pte, pte_t *_pte, static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, unsigned long start_addr, pte_t *pte, struct collapse_control *cc, - struct list_head *compound_pagelist) + unsigned int order, struct list_head *compound_pagelist) { - const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma); - const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc); + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma, order); + const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc, order); + const unsigned long nr_pages = 1UL << order; struct page *page = NULL; struct folio *folio = NULL; unsigned long addr = start_addr; @@ -610,7 +645,7 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, int none_or_zero = 0, shared = 0, referenced = 0; enum scan_result result = SCAN_FAIL; - for (_pte = pte; _pte < pte + HPAGE_PMD_NR; + for (_pte = pte; _pte < pte + nr_pages; _pte++, addr += PAGE_SIZE) { pte_t pteval = ptep_get(_pte); if (pte_none_or_zero(pteval)) { @@ -650,6 +685,12 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, /* See collapse_scan_pmd(). */ if (folio_maybe_mapped_shared(folio)) { + /* + * TODO: Support shared pages without leading to further + * mTHP collapses. Currently bringing in new pages via + * shared may cause a future higher order collapse on a + * rescan of the same range. + */ if (++shared > max_ptes_shared) { result = SCAN_EXCEED_SHARED_PTE; count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); @@ -743,18 +784,18 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, } static void __collapse_huge_page_copy_succeeded(pte_t *pte, - struct vm_area_struct *vma, - unsigned long address, - spinlock_t *ptl, - struct list_head *compound_pagelist) + struct vm_area_struct *vma, unsigned long address, + spinlock_t *ptl, unsigned int order, + struct list_head *compound_pagelist) { - unsigned long end = address + HPAGE_PMD_SIZE; + const unsigned long nr_pages = 1UL << order; + unsigned long end = address + (PAGE_SIZE * nr_pages); struct folio *src, *tmp; pte_t pteval; pte_t *_pte; unsigned int nr_ptes; - for (_pte = pte; _pte < pte + HPAGE_PMD_NR; _pte += nr_ptes, + for (_pte = pte; _pte < pte + nr_pages; _pte += nr_ptes, address += nr_ptes * PAGE_SIZE) { nr_ptes = 1; pteval = ptep_get(_pte); @@ -807,11 +848,10 @@ static void __collapse_huge_page_copy_succeeded(pte_t *pte, } static void __collapse_huge_page_copy_failed(pte_t *pte, - pmd_t *pmd, - pmd_t orig_pmd, - struct vm_area_struct *vma, - struct list_head *compound_pagelist) + pmd_t *pmd, pmd_t orig_pmd, struct vm_area_struct *vma, + unsigned int order, struct list_head *compound_pagelist) { + const unsigned long nr_pages = 1UL << order; spinlock_t *pmd_ptl; /* @@ -827,7 +867,7 @@ static void __collapse_huge_page_copy_failed(pte_t *pte, * Release both raw and compound pages isolated * in __collapse_huge_page_isolate. */ - release_pte_pages(pte, pte + HPAGE_PMD_NR, compound_pagelist); + release_pte_pages(pte, pte + nr_pages, compound_pagelist); } /* @@ -847,16 +887,17 @@ static void __collapse_huge_page_copy_failed(pte_t *pte, */ static enum scan_result __collapse_huge_page_copy(pte_t *pte, struct folio *folio, pmd_t *pmd, pmd_t orig_pmd, struct vm_area_struct *vma, - unsigned long address, spinlock_t *ptl, + unsigned long address, spinlock_t *ptl, unsigned int order, struct list_head *compound_pagelist) { + const unsigned long nr_pages = 1UL << order; unsigned int i; enum scan_result result = SCAN_SUCCEED; /* * Copying pages' contents is subject to memory poison at any iteration. */ - for (i = 0; i < HPAGE_PMD_NR; i++) { + for (i = 0; i < nr_pages; i++) { pte_t pteval = ptep_get(pte + i); struct page *page = folio_page(folio, i); unsigned long src_addr = address + i * PAGE_SIZE; @@ -875,10 +916,10 @@ static enum scan_result __collapse_huge_page_copy(pte_t *pte, struct folio *foli if (likely(result == SCAN_SUCCEED)) __collapse_huge_page_copy_succeeded(pte, vma, address, ptl, - compound_pagelist); + order, compound_pagelist); else __collapse_huge_page_copy_failed(pte, pmd, orig_pmd, vma, - compound_pagelist); + order, compound_pagelist); return result; } @@ -1051,16 +1092,20 @@ static enum scan_result check_pmd_still_valid(struct mm_struct *mm, * Bring missing pages in from swap, to complete THP collapse. * Only done if khugepaged_scan_pmd believes it is worthwhile. * + * For mTHP orders the function bails on the first swap entry, because + * faulting pages back in during collapse could re-populate PTEs that + * push a later scan over the threshold for a higher-order collapse. + * * Called and returns without pte mapped or spinlocks held. * Returns result: if not SCAN_SUCCEED, mmap_lock has been released. */ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm, - struct vm_area_struct *vma, unsigned long start_addr, pmd_t *pmd, - int referenced) + struct vm_area_struct *vma, unsigned long start_addr, + pmd_t *pmd, int referenced, unsigned int order) { int swapped_in = 0; vm_fault_t ret = 0; - unsigned long addr, end = start_addr + (HPAGE_PMD_NR * PAGE_SIZE); + unsigned long addr, end = start_addr + (PAGE_SIZE << order); enum scan_result result; pte_t *pte = NULL; spinlock_t *ptl; @@ -1092,6 +1137,19 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm, pte_present(vmf.orig_pte)) continue; + /* + * TODO: Support swapin without leading to further mTHP + * collapses. Currently bringing in new pages via swapin may + * cause a future higher order collapse on a rescan of the same + * range. + */ + if (!is_pmd_order(order)) { + pte_unmap(pte); + mmap_read_unlock(mm); + result = SCAN_EXCEED_SWAP_PTE; + goto out; + } + vmf.pte = pte; vmf.ptl = ptl; ret = do_swap_page(&vmf); @@ -1217,7 +1275,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * that case. Continuing to collapse causes inconsistency. */ result = __collapse_huge_page_swapin(mm, vma, address, pmd, - referenced); + referenced, HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) goto out_nolock; } @@ -1265,6 +1323,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a pte = pte_offset_map_lock(mm, &_pmd, address, &pte_ptl); if (pte) { result = __collapse_huge_page_isolate(vma, address, pte, cc, + HPAGE_PMD_ORDER, &compound_pagelist); spin_unlock(pte_ptl); } else { @@ -1295,6 +1354,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a result = __collapse_huge_page_copy(pte, folio, pmd, _pmd, vma, address, pte_ptl, + HPAGE_PMD_ORDER, &compound_pagelist); pte_unmap(pte); if (unlikely(result != SCAN_SUCCEED)) @@ -1330,9 +1390,9 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long start_addr, bool *lock_dropped, struct collapse_control *cc) { - const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma); - const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc); - const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc); + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma, HPAGE_PMD_ORDER); + const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc, HPAGE_PMD_ORDER); + const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc, HPAGE_PMD_ORDER); pmd_t *pmd; pte_t *pte, *_pte; int none_or_zero = 0, shared = 0, referenced = 0; @@ -2386,8 +2446,8 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm, unsigned long addr, struct file *file, pgoff_t start, struct collapse_control *cc) { - const unsigned int max_ptes_none = collapse_max_ptes_none(cc, NULL); - const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc); + const unsigned int max_ptes_none = collapse_max_ptes_none(cc, NULL, HPAGE_PMD_ORDER); + const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc, HPAGE_PMD_ORDER); struct folio *folio = NULL; struct address_space *mapping = file->f_mapping; XA_STATE(xas, &mapping->i_pages, start); From da98790891a4fefba89f831ae77dc2d34275da3c Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:12 -0600 Subject: [PATCH 4178/5214] mm/khugepaged: require collapse_huge_page to enter/exit with the lock dropped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the collapse_huge_page function requires the mmap_read_lock to enter with it held, and exit with it dropped. This function moves the unlock into its parent caller, and changes this semantic to requiring it to enter/exit with it always unlocked. In future patches, we need this expectation, as for in mTHP collapse, we may have already dropped the lock, and do not want to conditionally check for this by passing through the lock_dropped variable. No functional change is expected as one of the first things the collapse_huge_page function does is drop this lock before allocating the hugepage. Link: https://lore.kernel.org/20260605161422.213817-6-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Signed-off-by: Andrew Morton --- mm/khugepaged.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 1075dadb9de3..72b5d3671abc 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1222,6 +1222,12 @@ static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_stru return SCAN_SUCCEED; } +/* + * collapse_huge_page expects the mmap_lock to be unlocked before entering and + * will always return with the lock unlocked, to avoid holding the mmap_lock + * while allocating a THP, as that could trigger direct reclaim/compaction. + * Note that the VMA must be rechecked after grabbing the mmap_lock again. + */ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long address, int referenced, int unmapped, struct collapse_control *cc) { @@ -1237,14 +1243,6 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a VM_BUG_ON(address & ~HPAGE_PMD_MASK); - /* - * Before allocating the hugepage, release the mmap_lock read lock. - * The allocation can take potentially a long time if it involves - * sync compaction, and we do not need to hold the mmap_lock during - * that. We will recheck the vma after taking it again in write mode. - */ - mmap_read_unlock(mm); - result = alloc_charge_folio(&folio, mm, cc, HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) goto out_nolock; @@ -1554,6 +1552,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, out_unmap: pte_unmap_unlock(pte, ptl); if (result == SCAN_SUCCEED) { + /* collapse_huge_page expects the lock to be dropped before calling */ + mmap_read_unlock(mm); result = collapse_huge_page(mm, start_addr, referenced, unmapped, cc); /* collapse_huge_page will return with the mmap_lock released */ From e22f2fe72c74e99f419ba2a8adf33461d0b8330d Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:13 -0600 Subject: [PATCH 4179/5214] mm/khugepaged: generalize collapse_huge_page for mTHP collapse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass an order to collapse_huge_page to support collapsing anon memory to arbitrary orders within a PMD. order indicates what mTHP size we are attempting to collapse to. For non-PMD collapse we must leave the anon VMA write locked until after we collapse the mTHP-- in the PMD case all the pages are isolated, but in the mTHP case this is not true, and we must keep the lock to prevent access/changes to the page tables. This can happen if the rmap walkers hit a pmd_none while the PMD entry is currently unavailable due to being temporarily removed during the collapse phase. To properly establish the page table hierarchy without violating any expectations from certain architectures (e.g. MIPS), we must make sure to have the PMD reinstalled before the PTEs, and hold both PTE/PMD locks before calling update_mmu_cache_range() (if they are distinct locks). Link: https://lore.kernel.org/20260605161422.213817-7-npache@redhat.com Signed-off-by: Nico Pache Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 105 ++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 38 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 72b5d3671abc..acbb925b0123 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1228,22 +1228,24 @@ static enum scan_result alloc_charge_folio(struct folio **foliop, struct mm_stru * while allocating a THP, as that could trigger direct reclaim/compaction. * Note that the VMA must be rechecked after grabbing the mmap_lock again. */ -static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long address, - int referenced, int unmapped, struct collapse_control *cc) +static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long start_addr, + int referenced, int unmapped, struct collapse_control *cc, + unsigned int order) { + const unsigned long pmd_addr = start_addr & HPAGE_PMD_MASK; + const unsigned long end_addr = start_addr + (PAGE_SIZE << order); LIST_HEAD(compound_pagelist); pmd_t *pmd, _pmd; - pte_t *pte; + pte_t *pte = NULL; pgtable_t pgtable; struct folio *folio; spinlock_t *pmd_ptl, *pte_ptl; enum scan_result result = SCAN_FAIL; struct vm_area_struct *vma; struct mmu_notifier_range range; + bool anon_vma_locked = false; - VM_BUG_ON(address & ~HPAGE_PMD_MASK); - - result = alloc_charge_folio(&folio, mm, cc, HPAGE_PMD_ORDER); + result = alloc_charge_folio(&folio, mm, cc, order); if (result != SCAN_SUCCEED) goto out_nolock; @@ -1253,14 +1255,14 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a } mmap_read_lock(mm); - result = hugepage_vma_revalidate(mm, address, true, &vma, cc, - HPAGE_PMD_ORDER); + result = hugepage_vma_revalidate(mm, pmd_addr, /*expect_anon=*/ true, + &vma, cc, order); if (result != SCAN_SUCCEED) { mmap_read_unlock(mm); goto out_nolock; } - result = find_pmd_or_thp_or_none(mm, address, &pmd); + result = find_pmd_or_thp_or_none(mm, pmd_addr, &pmd); if (result != SCAN_SUCCEED) { mmap_read_unlock(mm); goto out_nolock; @@ -1272,8 +1274,8 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * released when it fails. So we jump out_nolock directly in * that case. Continuing to collapse causes inconsistency. */ - result = __collapse_huge_page_swapin(mm, vma, address, pmd, - referenced, HPAGE_PMD_ORDER); + result = __collapse_huge_page_swapin(mm, vma, start_addr, pmd, + referenced, order); if (result != SCAN_SUCCEED) goto out_nolock; } @@ -1288,20 +1290,28 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * mmap_lock. */ mmap_write_lock(mm); - result = hugepage_vma_revalidate(mm, address, true, &vma, cc, - HPAGE_PMD_ORDER); + result = hugepage_vma_revalidate(mm, pmd_addr, /*expect_anon=*/ true, + &vma, cc, order); if (result != SCAN_SUCCEED) goto out_up_write; /* check if the pmd is still valid */ vma_start_write(vma); - result = check_pmd_still_valid(mm, address, pmd); + result = check_pmd_still_valid(mm, pmd_addr, pmd); if (result != SCAN_SUCCEED) goto out_up_write; anon_vma_lock_write(vma->anon_vma); + anon_vma_locked = true; - mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, mm, address, - address + HPAGE_PMD_SIZE); + /* + * Only notify about the PTE range we will actually modify. While we + * temporary unmap the whole PTE table for mTHP collapse, we'll remap + * it later, leaving other PTEs effectively unmodified. The locks we + * hold prevent anybody from stumbling over such temporarily unmapped + * PTE tables. + */ + mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, mm, start_addr, + end_addr); mmu_notifier_invalidate_range_start(&range); pmd_ptl = pmd_lock(mm, pmd); /* probably unnecessary */ @@ -1313,26 +1323,23 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * Parallel GUP-fast is fine since GUP-fast will back off when * it detects PMD is changed. */ - _pmd = pmdp_collapse_flush(vma, address, pmd); + _pmd = pmdp_collapse_flush(vma, pmd_addr, pmd); spin_unlock(pmd_ptl); mmu_notifier_invalidate_range_end(&range); tlb_remove_table_sync_one(); - pte = pte_offset_map_lock(mm, &_pmd, address, &pte_ptl); + pte = pte_offset_map_lock(mm, &_pmd, start_addr, &pte_ptl); if (pte) { - result = __collapse_huge_page_isolate(vma, address, pte, cc, - HPAGE_PMD_ORDER, - &compound_pagelist); + result = __collapse_huge_page_isolate(vma, start_addr, pte, cc, + order, &compound_pagelist); spin_unlock(pte_ptl); } else { result = SCAN_NO_PTE_TABLE; } if (unlikely(result != SCAN_SUCCEED)) { - if (pte) - pte_unmap(pte); spin_lock(pmd_ptl); - BUG_ON(!pmd_none(*pmd)); + VM_WARN_ON_ONCE(!pmd_none(*pmd)); /* * We can only use set_pmd_at when establishing * hugepmds and never for establishing regular pmds that @@ -1340,21 +1347,24 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a */ pmd_populate(mm, pmd, pmd_pgtable(_pmd)); spin_unlock(pmd_ptl); - anon_vma_unlock_write(vma->anon_vma); goto out_up_write; } /* - * All pages are isolated and locked so anon_vma rmap - * can't run anymore. + * For PMD collapse all pages are isolated and locked so anon_vma + * rmap can't run anymore. For mTHP collapse the PMD entry has been + * removed and not all pages are isolated and locked, so we must hold + * the lock to prevent neighboring folios from attempting to access + * this PMD until its reinstalled. */ - anon_vma_unlock_write(vma->anon_vma); + if (is_pmd_order(order)) { + anon_vma_unlock_write(vma->anon_vma); + anon_vma_locked = false; + } result = __collapse_huge_page_copy(pte, folio, pmd, _pmd, - vma, address, pte_ptl, - HPAGE_PMD_ORDER, - &compound_pagelist); - pte_unmap(pte); + vma, start_addr, pte_ptl, + order, &compound_pagelist); if (unlikely(result != SCAN_SUCCEED)) goto out_up_write; @@ -1364,18 +1374,37 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long a * write. */ __folio_mark_uptodate(folio); - pgtable = pmd_pgtable(_pmd); - spin_lock(pmd_ptl); - BUG_ON(!pmd_none(*pmd)); - pgtable_trans_huge_deposit(mm, pmd, pgtable); - map_anon_folio_pmd_nopf(folio, pmd, vma, address); + VM_WARN_ON_ONCE(!pmd_none(*pmd)); + if (is_pmd_order(order)) { + pgtable = pmd_pgtable(_pmd); + pgtable_trans_huge_deposit(mm, pmd, pgtable); + map_anon_folio_pmd_nopf(folio, pmd, vma, pmd_addr); + } else { + /* + * Some architectures (e.g. MIPS) walk the live page table in + * their implementation. update_mmu_cache_range() must be called + * with a valid page table hierarchy and the PTE lock held. + * Acquire it nested inside pmd_ptl when they are distinct locks. + */ + if (pte_ptl != pmd_ptl) + spin_lock_nested(pte_ptl, SINGLE_DEPTH_NESTING); + pmd_populate(mm, pmd, pmd_pgtable(_pmd)); + map_anon_folio_pte_nopf(folio, pte, vma, start_addr, + /*uffd_wp=*/ false); + if (pte_ptl != pmd_ptl) + spin_unlock(pte_ptl); + } spin_unlock(pmd_ptl); folio = NULL; result = SCAN_SUCCEED; out_up_write: + if (anon_vma_locked) + anon_vma_unlock_write(vma->anon_vma); + if (pte) + pte_unmap(pte); mmap_write_unlock(mm); out_nolock: if (folio) @@ -1555,7 +1584,7 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, /* collapse_huge_page expects the lock to be dropped before calling */ mmap_read_unlock(mm); result = collapse_huge_page(mm, start_addr, referenced, - unmapped, cc); + unmapped, cc, HPAGE_PMD_ORDER); /* collapse_huge_page will return with the mmap_lock released */ *lock_dropped = true; } From 3a460f245f000c6fbd07fd94e572268e4a7a420d Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:14 -0600 Subject: [PATCH 4180/5214] mm/khugepaged: skip collapsing mTHP to smaller orders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit khugepaged may try to collapse a mTHP to a folio of equal or smaller size, possibly resulting in a partially mapped source folio, which is undesired. Skip these cases until we have a way to check if its ok to collapse to a smaller mTHP size (like in the case of a partially mapped folio). This check is not done during the scan phase as the current collapse order is unknown at that time. This patch is inspired by Dev Jain's work on khugepaged mTHP support [1]. Link: https://lore.kernel.org/20260605161422.213817-8-npache@redhat.com Link: https://lore.kernel.org/lkml/20241216165105.56185-11-dev.jain@arm.com/ [1] Co-developed-by: Dev Jain Signed-off-by: Dev Jain Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (arm) Acked-by: Usama Arif Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index acbb925b0123..c7819a854f14 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -697,6 +697,14 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, goto out; } } + /* + * TODO: In some cases of partially-mapped folios, we'd actually + * want to collapse. + */ + if (!is_pmd_order(order) && folio_order(folio) >= order) { + result = SCAN_PTE_MAPPED_HUGEPAGE; + goto out; + } if (folio_test_large(folio)) { struct folio *f; From b5b8b329c4b456806e3bcc6bf3490fbc508e9080 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:15 -0600 Subject: [PATCH 4181/5214] mm/khugepaged: add per-order mTHP collapse failure statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add three new mTHP statistics to track collapse failures for different orders when encountering swap PTEs, excessive none PTEs, and shared PTEs: - collapse_exceed_swap_pte: Increment when mTHP collapse fails due to encountering a swap PTE. - collapse_exceed_none_pte: Counts when mTHP collapse fails due to exceeding the none PTE threshold for the given order - collapse_exceed_shared_pte: Counts when mTHP collapse fails due to encountering a shared PTE. These statistics complement the existing THP_SCAN_EXCEED_* events by providing per-order granularity for mTHP collapse attempts. The stats are exposed via sysfs under `/sys/kernel/mm/transparent_hugepage/hugepages-*/stats/` for each supported hugepage size. As we currently do not support collapsing mTHPs that contain a swap or shared entry, those statistics keep track of how often we are encountering failed mTHP collapses due to these restrictions. We will add support for mTHP collapse for anonymous pages next; lets also track when this happens at the PMD level within the per-mTHP stats. Link: https://lore.kernel.org/20260605161422.213817-9-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/transhuge.rst | 14 ++++++++++++++ include/linux/huge_mm.h | 3 +++ mm/huge_memory.c | 7 +++++++ mm/khugepaged.c | 15 +++++++++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index a74844e01f1e..b98e18c80185 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -714,6 +714,20 @@ nr_anon_partially_mapped an anonymous THP as "partially mapped" and count it here, even though it is not actually partially mapped anymore. +collapse_exceed_none_pte + The number of collapse attempts that failed due to exceeding the + max_ptes_none threshold. + +collapse_exceed_swap_pte + The number of collapse attempts that failed due to exceeding the + max_ptes_swap threshold. For non-PMD orders this occurs if a mTHP range + contains at least one swap PTE. + +collapse_exceed_shared_pte + The number of collapse attempts that failed due to exceeding the + max_ptes_shared threshold. For non-PMD orders this occurs if a mTHP range + contains at least one shared PTE. + As the system ages, allocating huge pages may be expensive as the system uses memory compaction to copy data around memory to free a huge page for use. There are some counters in ``/proc/vmstat`` to help diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index e225d1d4a3fe..89f4c22d7d62 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -144,6 +144,9 @@ enum mthp_stat_item { MTHP_STAT_SPLIT_DEFERRED, MTHP_STAT_NR_ANON, MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, + MTHP_STAT_COLLAPSE_EXCEED_SWAP, + MTHP_STAT_COLLAPSE_EXCEED_NONE, + MTHP_STAT_COLLAPSE_EXCEED_SHARED, __MTHP_STAT_COUNT }; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index c8b851dd518a..b4062396ce87 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -720,6 +720,10 @@ DEFINE_MTHP_STAT_ATTR(split_failed, MTHP_STAT_SPLIT_FAILED); DEFINE_MTHP_STAT_ATTR(split_deferred, MTHP_STAT_SPLIT_DEFERRED); DEFINE_MTHP_STAT_ATTR(nr_anon, MTHP_STAT_NR_ANON); DEFINE_MTHP_STAT_ATTR(nr_anon_partially_mapped, MTHP_STAT_NR_ANON_PARTIALLY_MAPPED); +DEFINE_MTHP_STAT_ATTR(collapse_exceed_swap_pte, MTHP_STAT_COLLAPSE_EXCEED_SWAP); +DEFINE_MTHP_STAT_ATTR(collapse_exceed_none_pte, MTHP_STAT_COLLAPSE_EXCEED_NONE); +DEFINE_MTHP_STAT_ATTR(collapse_exceed_shared_pte, MTHP_STAT_COLLAPSE_EXCEED_SHARED); + static struct attribute *anon_stats_attrs[] = { &anon_fault_alloc_attr.attr, @@ -736,6 +740,9 @@ static struct attribute *anon_stats_attrs[] = { &split_deferred_attr.attr, &nr_anon_attr.attr, &nr_anon_partially_mapped_attr.attr, + &collapse_exceed_swap_pte_attr.attr, + &collapse_exceed_none_pte_attr.attr, + &collapse_exceed_shared_pte_attr.attr, NULL, }; diff --git a/mm/khugepaged.c b/mm/khugepaged.c index c7819a854f14..a496484157a1 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -651,7 +651,9 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, if (pte_none_or_zero(pteval)) { if (++none_or_zero > max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; - count_vm_event(THP_SCAN_EXCEED_NONE_PTE); + if (is_pmd_order(order)) + count_vm_event(THP_SCAN_EXCEED_NONE_PTE); + count_mthp_stat(order, MTHP_STAT_COLLAPSE_EXCEED_NONE); goto out; } continue; @@ -693,7 +695,9 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, */ if (++shared > max_ptes_shared) { result = SCAN_EXCEED_SHARED_PTE; - count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); + if (is_pmd_order(order)) + count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); + count_mthp_stat(order, MTHP_STAT_COLLAPSE_EXCEED_SHARED); goto out; } } @@ -1152,6 +1156,7 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm, * range. */ if (!is_pmd_order(order)) { + count_mthp_stat(order, MTHP_STAT_COLLAPSE_EXCEED_SWAP); pte_unmap(pte); mmap_read_unlock(mm); result = SCAN_EXCEED_SWAP_PTE; @@ -1464,6 +1469,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, if (++none_or_zero > max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; count_vm_event(THP_SCAN_EXCEED_NONE_PTE); + count_mthp_stat(HPAGE_PMD_ORDER, + MTHP_STAT_COLLAPSE_EXCEED_NONE); goto out_unmap; } continue; @@ -1472,6 +1479,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, if (++unmapped > max_ptes_swap) { result = SCAN_EXCEED_SWAP_PTE; count_vm_event(THP_SCAN_EXCEED_SWAP_PTE); + count_mthp_stat(HPAGE_PMD_ORDER, + MTHP_STAT_COLLAPSE_EXCEED_SWAP); goto out_unmap; } /* @@ -1529,6 +1538,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, if (++shared > max_ptes_shared) { result = SCAN_EXCEED_SHARED_PTE; count_vm_event(THP_SCAN_EXCEED_SHARED_PTE); + count_mthp_stat(HPAGE_PMD_ORDER, + MTHP_STAT_COLLAPSE_EXCEED_SHARED); goto out_unmap; } } From ceaa0b641311e6279722b03acfbcd173d166518f Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:16 -0600 Subject: [PATCH 4182/5214] mm/khugepaged: improve tracepoints for mTHP orders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the order to the mm_collapse_huge_page<_swapin,_isolate> tracepoints to give better insight into what order is being operated at for. Link: https://lore.kernel.org/20260605161422.213817-10-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- include/trace/events/huge_memory.h | 34 +++++++++++++++++++----------- mm/khugepaged.c | 9 ++++---- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h index bcdc57eea270..291fae364c62 100644 --- a/include/trace/events/huge_memory.h +++ b/include/trace/events/huge_memory.h @@ -89,40 +89,44 @@ TRACE_EVENT(mm_khugepaged_scan_pmd, TRACE_EVENT(mm_collapse_huge_page, - TP_PROTO(struct mm_struct *mm, int isolated, int status), + TP_PROTO(struct mm_struct *mm, int isolated, int status, unsigned int order), - TP_ARGS(mm, isolated, status), + TP_ARGS(mm, isolated, status, order), TP_STRUCT__entry( __field(struct mm_struct *, mm) __field(int, isolated) __field(int, status) + __field(unsigned int, order) ), TP_fast_assign( __entry->mm = mm; __entry->isolated = isolated; __entry->status = status; + __entry->order = order; ), - TP_printk("mm=%p, isolated=%d, status=%s", + TP_printk("mm=%p, isolated=%d, status=%s, order=%u", __entry->mm, __entry->isolated, - __print_symbolic(__entry->status, SCAN_STATUS)) + __print_symbolic(__entry->status, SCAN_STATUS), + __entry->order) ); TRACE_EVENT(mm_collapse_huge_page_isolate, TP_PROTO(struct folio *folio, int none_or_zero, - int referenced, int status), + int referenced, int status, unsigned int order), - TP_ARGS(folio, none_or_zero, referenced, status), + TP_ARGS(folio, none_or_zero, referenced, status, order), TP_STRUCT__entry( __field(unsigned long, pfn) __field(int, none_or_zero) __field(int, referenced) __field(int, status) + __field(unsigned int, order) ), TP_fast_assign( @@ -130,26 +134,30 @@ TRACE_EVENT(mm_collapse_huge_page_isolate, __entry->none_or_zero = none_or_zero; __entry->referenced = referenced; __entry->status = status; + __entry->order = order; ), - TP_printk("scan_pfn=0x%lx, none_or_zero=%d, referenced=%d, status=%s", + TP_printk("scan_pfn=0x%lx, none_or_zero=%d, referenced=%d, status=%s, order=%u", __entry->pfn, __entry->none_or_zero, __entry->referenced, - __print_symbolic(__entry->status, SCAN_STATUS)) + __print_symbolic(__entry->status, SCAN_STATUS), + __entry->order) ); TRACE_EVENT(mm_collapse_huge_page_swapin, - TP_PROTO(struct mm_struct *mm, int swapped_in, int referenced, int ret), + TP_PROTO(struct mm_struct *mm, int swapped_in, int referenced, int ret, + unsigned int order), - TP_ARGS(mm, swapped_in, referenced, ret), + TP_ARGS(mm, swapped_in, referenced, ret, order), TP_STRUCT__entry( __field(struct mm_struct *, mm) __field(int, swapped_in) __field(int, referenced) __field(int, ret) + __field(unsigned int, order) ), TP_fast_assign( @@ -157,13 +165,15 @@ TRACE_EVENT(mm_collapse_huge_page_swapin, __entry->swapped_in = swapped_in; __entry->referenced = referenced; __entry->ret = ret; + __entry->order = order; ), - TP_printk("mm=%p, swapped_in=%d, referenced=%d, ret=%d", + TP_printk("mm=%p, swapped_in=%d, referenced=%d, ret=%d, order=%u", __entry->mm, __entry->swapped_in, __entry->referenced, - __entry->ret) + __entry->ret, + __entry->order) ); TRACE_EVENT(mm_khugepaged_scan_file, diff --git a/mm/khugepaged.c b/mm/khugepaged.c index a496484157a1..e4872fdd3e8d 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -785,13 +785,13 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma, } else { result = SCAN_SUCCEED; trace_mm_collapse_huge_page_isolate(folio, none_or_zero, - referenced, result); + referenced, result, order); return result; } out: release_pte_pages(pte, _pte, compound_pagelist); trace_mm_collapse_huge_page_isolate(folio, none_or_zero, - referenced, result); + referenced, result, order); return result; } @@ -1197,7 +1197,8 @@ static enum scan_result __collapse_huge_page_swapin(struct mm_struct *mm, result = SCAN_SUCCEED; out: - trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, result); + trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, result, + order); return result; } @@ -1422,7 +1423,7 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s out_nolock: if (folio) folio_put(folio); - trace_mm_collapse_huge_page(mm, result == SCAN_SUCCEED, result); + trace_mm_collapse_huge_page(mm, result == SCAN_SUCCEED, result, order); return result; } From 2c7d0fa84dcfd9080181bd92e18aacbc4abbe4f5 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:17 -0600 Subject: [PATCH 4183/5214] mm/khugepaged: introduce collapse_possible_orders helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add collapse_possible_orders() to generalize THP order eligibility. The function determines which THP orders are permitted based on collapse context (khugepaged vs madv_collapse). We also add collapse_possible() as a thin wrapper around collapse_possible_orders() that returns a bool rather than the whole bitmap. This consolidates collapse configuration logic and provides a clean interface for future mTHP collapse support where the orders may be different. Link: https://lore.kernel.org/20260605161422.213817-11-npache@redhat.com Signed-off-by: Nico Pache Acked-by: David Hildenbrand (Arm) Reviewed-by: Baolin Wang Reviewed-by: Lorenzo Stoakes Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Cc: Bagas Sanjaya Cc: Usama Arif Signed-off-by: Andrew Morton --- mm/khugepaged.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index e4872fdd3e8d..a763d46914bb 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -554,12 +554,30 @@ void __khugepaged_enter(struct mm_struct *mm) wake_up_interruptible(&khugepaged_wait); } +/* + * Check what orders are possible based on the vma and collapse type. + * This is used to determine if mTHP collapse is a viable option. + */ +static unsigned long collapse_possible_orders(struct vm_area_struct *vma, + vm_flags_t vm_flags, enum tva_type tva_flags) +{ + const unsigned long orders = BIT(HPAGE_PMD_ORDER); + + return thp_vma_allowable_orders(vma, vm_flags, tva_flags, orders); +} + +static bool collapse_possible(struct vm_area_struct *vma, + vm_flags_t vm_flags, enum tva_type tva_flags) +{ + return collapse_possible_orders(vma, vm_flags, tva_flags); +} + void khugepaged_enter_vma(struct vm_area_struct *vma, vm_flags_t vm_flags) { if (!mm_flags_test(MMF_VM_HUGEPAGE, vma->vm_mm) && hugepage_pmd_enabled()) { - if (thp_vma_allowable_order(vma, vm_flags, TVA_KHUGEPAGED, PMD_ORDER)) + if (collapse_possible(vma, vm_flags, TVA_KHUGEPAGED)) __khugepaged_enter(vma->vm_mm); } } @@ -2705,7 +2723,7 @@ static void collapse_scan_mm_slot(unsigned int progress_max, cc->progress++; break; } - if (!thp_vma_allowable_order(vma, vma->vm_flags, TVA_KHUGEPAGED, PMD_ORDER)) { + if (!collapse_possible(vma, vma->vm_flags, TVA_KHUGEPAGED)) { cc->progress++; continue; } @@ -3015,7 +3033,7 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, BUG_ON(vma->vm_start > start); BUG_ON(vma->vm_end < end); - if (!thp_vma_allowable_order(vma, vma->vm_flags, TVA_FORCED_COLLAPSE, PMD_ORDER)) + if (!collapse_possible(vma, vma->vm_flags, TVA_FORCED_COLLAPSE)) return -EINVAL; cc = kmalloc_obj(*cc); From 90ed32d00054496ff763e8f97c49f422a4682fd3 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:18 -0600 Subject: [PATCH 4184/5214] mm/khugepaged: introduce mTHP collapse support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable khugepaged to collapse to mTHP orders. This patch implements the main scanning logic using a bitmap to track occupied pages and the algorithm to find optimal collapse sizes. Previous to this patch, PMD collapse had 3 main phases, a light weight scanning phase (mmap_read_lock) that determines a potential PMD collapse, an alloc phase (mmap unlocked), then finally heavier collapse phase (mmap_write_lock). To enabled mTHP collapse we make the following changes: During PMD scan phase, track occupied pages in a bitmap. When mTHP orders are enabled, we remove the restriction of max_ptes_none during the scan phase to avoid missing potential mTHP collapse candidates. Once we have scanned the full PMD range and updated the bitmap to track occupied pages, we use the bitmap to find the optimal mTHP size. Implement mthp_collapse() to walk forward through the bitmap and determine the best eligible order for each naturally-aligned region. The algorithm starts at the beginning of the PMD range and, for each offset, tries the highest order that fits the alignment. If the number of occupied PTEs in that region satisfies the max_ptes_none threshold for that order, a collapse is attempted. On failure, the order is decremented and the same offset is retried at the next smaller size. Once the smallest enabled order is exhausted (or a collapse succeeds), the offset advances past the region just processed, and the next attempt starts at the highest order permitted by the new offset's natural alignment. The algorithm works as follows: 1) set offset=0 and order=HPAGE_PMD_ORDER 2) if the order is not enabled, go to step (5) 3) count occupied PTEs in the (offset, order) range using bitmap_weight_from() 4) if the count satisfies the max_ptes_none threshold, attempt collapse; on success, advance to step (6) 5) if a smaller enabled order exists, decrement order and retry from step (2) at the same offset 6) advance offset past the current region and compute the next order from the new offset's natural alignment via __ffs(offset), capped at HPAGE_PMD_ORDER 7) repeat from step (2) until the full PMD range is covered mTHP collapses reject regions containing swapped out or shared pages. This is because adding new entries can lead to new none pages, and these may lead to constant promotion into a higher order mTHP. A similar issue can occur with "max_ptes_none > HPAGE_PMD_NR/2" due to a collapse introducing at least 2x the number of pages, and on a future scan will satisfy the promotion condition once again. This issue is prevented via the collapse_max_ptes_none() function which imposes the max_ptes_none restrictions above. We currently only support mTHP collapse for max_ptes_none values of 0 and HPAGE_PMD_NR - 1. resulting in the following behavior: - max_ptes_none=0: Never introduce new empty pages during collapse - max_ptes_none=HPAGE_PMD_NR-1: Always try collapse to the highest available mTHP order Any other max_ptes_none value will emit a warning and default mTHP collapse to max_ptes_none=0. There should be no behavior change for PMD collapse. Once we determine what mTHP sizes fits best in that PMD range a collapse is attempted. A minimum collapse order of 2 is used as this is the lowest order supported by anon memory as defined by THP_ORDERS_ALL_ANON. Currently madv_collapse is not supported and will only attempt PMD collapse. We can also remove the check for is_khugepaged inside the PMD scan as the collapse_max_ptes_none() function handles this logic now. Link: https://lore.kernel.org/20260605161422.213817-12-npache@redhat.com Signed-off-by: Nico Pache Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Lance Yang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 146 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 8 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index a763d46914bb..e57289f4d3c6 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -99,6 +99,8 @@ static DEFINE_READ_MOSTLY_HASHTABLE(mm_slots_hash, MM_SLOTS_HASH_BITS); static struct kmem_cache *mm_slot_cache __ro_after_init; +#define KHUGEPAGED_MIN_MTHP_ORDER 2 + struct collapse_control { bool is_khugepaged; @@ -110,6 +112,9 @@ struct collapse_control { /* nodemask for allocation fallback */ nodemask_t alloc_nmask; + + /* Each bit represents a single occupied (!none/zero) page. */ + DECLARE_BITMAP(mthp_present_ptes, MAX_PTRS_PER_PTE); }; /** @@ -1445,20 +1450,130 @@ static enum scan_result collapse_huge_page(struct mm_struct *mm, unsigned long s return result; } +/* Return the highest naturally aligned order that fits at @offset within a PMD. */ +static unsigned int max_order_from_offset(unsigned int offset) +{ + if (offset == 0) + return HPAGE_PMD_ORDER; + + return min_t(unsigned int, __ffs(offset), HPAGE_PMD_ORDER); +} + +/* + * mthp_collapse() consumes the bitmap that is generated during + * collapse_scan_pmd() to determine what regions and mTHP orders fit best. + * + * Each bit in cc->mthp_present_ptes represents a single occupied (!none/zero) + * page. We start at the PMD order and check if it is eligible for collapse; + * if not, we check the left and right halves of the PTE page table we are + * examining at a lower order. + * + * For each of these, we determine how many PTE entries are occupied in the + * range of PTE entries we propose to collapse, then we compare this to a + * threshold number of PTE entries which would need to be occupied for a + * collapse to be permitted at that order (accounting for max_ptes_none). + * + * If a collapse is permitted, we attempt to collapse the PTE range into a + * mTHP. + */ +static enum scan_result mthp_collapse(struct mm_struct *mm, + unsigned long address, int referenced, int unmapped, + struct collapse_control *cc, unsigned long enabled_orders) +{ + unsigned int nr_occupied_ptes, nr_ptes, max_ptes_none; + enum scan_result last_result = SCAN_FAIL; + int collapsed = 0; + bool alloc_failed = false; + unsigned long collapse_address; + unsigned int offset = 0; + unsigned int order = HPAGE_PMD_ORDER; + + while (offset < HPAGE_PMD_NR) { + nr_ptes = 1UL << order; + + if (!test_bit(order, &enabled_orders)) + goto next_order; + + max_ptes_none = collapse_max_ptes_none(cc, NULL, order); + nr_occupied_ptes = bitmap_weight_from(cc->mthp_present_ptes, offset, + offset + nr_ptes); + + if (nr_occupied_ptes >= nr_ptes - max_ptes_none) { + enum scan_result ret; + + collapse_address = address + offset * PAGE_SIZE; + ret = collapse_huge_page(mm, collapse_address, referenced, + unmapped, cc, order); + switch (ret) { + /* Cases where we continue to next collapse candidate */ + case SCAN_SUCCEED: + collapsed += nr_ptes; + fallthrough; + case SCAN_PTE_MAPPED_HUGEPAGE: + goto next_offset; + /* Cases where lower orders might still succeed */ + case SCAN_ALLOC_HUGE_PAGE_FAIL: + alloc_failed = true; + last_result = ret; + goto next_order; + /* Cases where no further collapse is possible */ + case SCAN_PMD_MAPPED: + fallthrough; + default: + last_result = ret; + goto done; + } + } + +next_order: + /* + * Continue with the next smaller order if there is still + * any smaller order enabled. When at the smallest order + * we must always move to the next offset. + */ + if (order > KHUGEPAGED_MIN_MTHP_ORDER && + (enabled_orders & GENMASK(order - 1, 0))) { + order--; + continue; + } +next_offset: + /* + * Advance past the region we just processed and determine the + * highest order we can attempt next. Since huge pages must be + * naturally aligned, the max order we can attempt next is + * limited by the alignment of the new offset. + * E.g. if we collapsed a order-2 mTHP at offset 0, offset + * becomes 4 and __ffs(4) == 2, so the next attempt starts at + * order 2. + */ + offset += nr_ptes; + order = max_order_from_offset(offset); + } +done: + if (collapsed) + return SCAN_SUCCEED; + if (alloc_failed) + return SCAN_ALLOC_HUGE_PAGE_FAIL; + return last_result; +} + static enum scan_result collapse_scan_pmd(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long start_addr, bool *lock_dropped, struct collapse_control *cc) { - const unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma, HPAGE_PMD_ORDER); const unsigned int max_ptes_shared = collapse_max_ptes_shared(cc, HPAGE_PMD_ORDER); const unsigned int max_ptes_swap = collapse_max_ptes_swap(cc, HPAGE_PMD_ORDER); + unsigned int max_ptes_none = collapse_max_ptes_none(cc, vma, HPAGE_PMD_ORDER); + enum tva_type tva_flags = cc->is_khugepaged ? TVA_KHUGEPAGED : TVA_FORCED_COLLAPSE; pmd_t *pmd; - pte_t *pte, *_pte; + pte_t *pte, *_pte, pteval; + int i; int none_or_zero = 0, shared = 0, referenced = 0; enum scan_result result = SCAN_FAIL; struct page *page = NULL; struct folio *folio = NULL; unsigned long addr; + unsigned long enabled_orders; spinlock_t *ptl; int node = NUMA_NO_NODE, unmapped = 0; @@ -1470,8 +1585,19 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, goto out; } + bitmap_zero(cc->mthp_present_ptes, MAX_PTRS_PER_PTE); memset(cc->node_load, 0, sizeof(cc->node_load)); nodes_clear(cc->alloc_nmask); + + enabled_orders = collapse_possible_orders(vma, vma->vm_flags, tva_flags); + + /* + * If PMD is the only enabled order, enforce max_ptes_none, otherwise + * scan all pages to populate the bitmap for mTHP collapse. + */ + if (enabled_orders != BIT(HPAGE_PMD_ORDER)) + max_ptes_none = KHUGEPAGED_MAX_PTES_LIMIT; + pte = pte_offset_map_lock(mm, pmd, start_addr, &ptl); if (!pte) { cc->progress++; @@ -1479,11 +1605,13 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, goto out; } - for (addr = start_addr, _pte = pte; _pte < pte + HPAGE_PMD_NR; - _pte++, addr += PAGE_SIZE) { + for (i = 0; i < HPAGE_PMD_NR; i++) { + _pte = pte + i; + addr = start_addr + i * PAGE_SIZE; + pteval = ptep_get(_pte); + cc->progress++; - pte_t pteval = ptep_get(_pte); if (pte_none_or_zero(pteval)) { if (++none_or_zero > max_ptes_none) { result = SCAN_EXCEED_NONE_PTE; @@ -1563,6 +1691,8 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, } } + /* Set bit for occupied pages */ + __set_bit(i, cc->mthp_present_ptes); /* * Record which node the original page is from and save this * information to cc->node_load[]. @@ -1621,9 +1751,9 @@ static enum scan_result collapse_scan_pmd(struct mm_struct *mm, if (result == SCAN_SUCCEED) { /* collapse_huge_page expects the lock to be dropped before calling */ mmap_read_unlock(mm); - result = collapse_huge_page(mm, start_addr, referenced, - unmapped, cc, HPAGE_PMD_ORDER); - /* collapse_huge_page will return with the mmap_lock released */ + result = mthp_collapse(mm, start_addr, referenced, + unmapped, cc, enabled_orders); + /* mmap_lock was released above, set lock_dropped */ *lock_dropped = true; } out: From 5fea7eb1a33154a94ae7be3cd062fec8adfe2fad Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:19 -0600 Subject: [PATCH 4185/5214] mm/khugepaged: avoid unnecessary mTHP collapse attempts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are cases where, if an attempted collapse fails, all subsequent orders are guaranteed to also fail. Avoid these collapse attempts by bailing out early. Link: https://lore.kernel.org/20260605161422.213817-13-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Acked-by: Usama Arif Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index e57289f4d3c6..5a2adbdbd055 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1504,6 +1504,7 @@ static enum scan_result mthp_collapse(struct mm_struct *mm, collapse_address = address + offset * PAGE_SIZE; ret = collapse_huge_page(mm, collapse_address, referenced, unmapped, cc, order); + switch (ret) { /* Cases where we continue to next collapse candidate */ case SCAN_SUCCEED: @@ -1514,6 +1515,18 @@ static enum scan_result mthp_collapse(struct mm_struct *mm, /* Cases where lower orders might still succeed */ case SCAN_ALLOC_HUGE_PAGE_FAIL: alloc_failed = true; + fallthrough; + case SCAN_LACK_REFERENCED_PAGE: + case SCAN_EXCEED_NONE_PTE: + case SCAN_EXCEED_SWAP_PTE: + case SCAN_EXCEED_SHARED_PTE: + case SCAN_PAGE_LOCK: + case SCAN_PAGE_COUNT: + case SCAN_PAGE_NULL: + case SCAN_DEL_PAGE_LRU: + case SCAN_PTE_NON_PRESENT: + case SCAN_PTE_UFFD_WP: + case SCAN_PAGE_LAZYFREE: last_result = ret; goto next_order; /* Cases where no further collapse is possible */ From b7f16963efe73502dc3e27c4ca15c8e687fd37bc Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Fri, 5 Jun 2026 10:14:20 -0600 Subject: [PATCH 4186/5214] mm/khugepaged: run khugepaged for all orders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If any order (m)THP is enabled we should allow running khugepaged to attempt scanning and collapsing mTHPs. In order for khugepaged to operate when only mTHP sizes are specified in sysfs, we must modify the predicate function that determines whether it ought to run to do so. This function is currently called hugepage_pmd_enabled(), this patch renames it to hugepage_enabled() and updates the logic to check to determine whether any valid orders may exist which would justify khugepaged running. We must also update collapse_possible_orders() to check all orders if the vma is anonymous and the collapse is khugepaged. After this patch khugepaged mTHP collapse is fully enabled. Link: https://lore.kernel.org/20260605161422.213817-14-npache@redhat.com Signed-off-by: Baolin Wang Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Reviewed-by: Lance Yang Acked-by: Usama Arif Acked-by: David Hildenbrand (Arm) Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Bagas Sanjaya Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 5a2adbdbd055..6de1a148222a 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -503,23 +503,23 @@ static inline int collapse_test_exit_or_disable(struct mm_struct *mm) mm_flags_test(MMF_DISABLE_THP_COMPLETELY, mm); } -static bool hugepage_pmd_enabled(void) +static bool hugepage_enabled(void) { /* * We cover the anon, shmem and the file-backed case here; file-backed * hugepages, when configured in, are determined by the global control. - * Anon pmd-sized hugepages are determined by the pmd-size control. + * Anon hugepages are determined by its per-size mTHP control. * Shmem pmd-sized hugepages are also determined by its pmd-size control, * except when the global shmem_huge is set to SHMEM_HUGE_DENY. */ if (IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && hugepage_global_enabled()) return true; - if (test_bit(PMD_ORDER, &huge_anon_orders_always)) + if (READ_ONCE(huge_anon_orders_always)) return true; - if (test_bit(PMD_ORDER, &huge_anon_orders_madvise)) + if (READ_ONCE(huge_anon_orders_madvise)) return true; - if (test_bit(PMD_ORDER, &huge_anon_orders_inherit) && + if (READ_ONCE(huge_anon_orders_inherit) && hugepage_global_enabled()) return true; if (IS_ENABLED(CONFIG_SHMEM) && shmem_hpage_pmd_enabled()) @@ -566,7 +566,13 @@ void __khugepaged_enter(struct mm_struct *mm) static unsigned long collapse_possible_orders(struct vm_area_struct *vma, vm_flags_t vm_flags, enum tva_type tva_flags) { - const unsigned long orders = BIT(HPAGE_PMD_ORDER); + unsigned long orders; + + /* If khugepaged is scanning an anonymous vma, allow mTHP collapse */ + if ((tva_flags == TVA_KHUGEPAGED) && vma_is_anonymous(vma)) + orders = THP_ORDERS_ALL_ANON; + else + orders = BIT(HPAGE_PMD_ORDER); return thp_vma_allowable_orders(vma, vm_flags, tva_flags, orders); } @@ -580,11 +586,9 @@ static bool collapse_possible(struct vm_area_struct *vma, void khugepaged_enter_vma(struct vm_area_struct *vma, vm_flags_t vm_flags) { - if (!mm_flags_test(MMF_VM_HUGEPAGE, vma->vm_mm) && - hugepage_pmd_enabled()) { - if (collapse_possible(vma, vm_flags, TVA_KHUGEPAGED)) - __khugepaged_enter(vma->vm_mm); - } + if (!mm_flags_test(MMF_VM_HUGEPAGE, vma->vm_mm) && hugepage_enabled() + && collapse_possible(vma, vm_flags, TVA_KHUGEPAGED)) + __khugepaged_enter(vma->vm_mm); } void __khugepaged_exit(struct mm_struct *mm) @@ -2941,7 +2945,7 @@ static void collapse_scan_mm_slot(unsigned int progress_max, static int khugepaged_has_work(void) { - return !list_empty(&khugepaged_scan.mm_head) && hugepage_pmd_enabled(); + return !list_empty(&khugepaged_scan.mm_head) && hugepage_enabled(); } static int khugepaged_wait_event(void) @@ -3014,7 +3018,7 @@ static void khugepaged_wait_work(void) return; } - if (hugepage_pmd_enabled()) + if (hugepage_enabled()) wait_event_freezable(khugepaged_wait, khugepaged_wait_event()); } @@ -3045,7 +3049,7 @@ void set_recommended_min_free_kbytes(void) int nr_zones = 0; unsigned long recommended_min; - if (!hugepage_pmd_enabled()) { + if (!hugepage_enabled()) { calculate_min_free_kbytes(); goto update_wmarks; } @@ -3095,7 +3099,7 @@ int start_stop_khugepaged(void) int err = 0; mutex_lock(&khugepaged_mutex); - if (hugepage_pmd_enabled()) { + if (hugepage_enabled()) { if (!khugepaged_thread) khugepaged_thread = kthread_run(khugepaged, NULL, "khugepaged"); @@ -3121,7 +3125,7 @@ int start_stop_khugepaged(void) void khugepaged_min_free_kbytes_update(void) { mutex_lock(&khugepaged_mutex); - if (hugepage_pmd_enabled() && khugepaged_thread) + if (hugepage_enabled() && khugepaged_thread) set_recommended_min_free_kbytes(); mutex_unlock(&khugepaged_mutex); } From cae43f22de82130a8fc1afa7b9b26fa8c08665f5 Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 5 Jun 2026 10:14:21 -0600 Subject: [PATCH 4187/5214] Documentation: mm: update the admin guide for mTHP collapse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can collapse to mTHPs lets update the admin guide to reflect these changes and provide proper guidance on how to utilize it. Link: https://lore.kernel.org/20260605161422.213817-15-npache@redhat.com Signed-off-by: Nico Pache Reviewed-by: Lorenzo Stoakes Reviewed-by: Bagas Sanjaya Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Cc: Alistair Popple Cc: Andrea Arcangeli Cc: Anshuman Khandual Cc: Baolin Wang Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Catalin Marinas Cc: David Rientjes Cc: Dev Jain Cc: Gregory Price Cc: "Huang, Ying" Cc: Hugh Dickins Cc: Jan Kara Cc: Jann Horn Cc: Johannes Weiner Cc: Jonathan Corbet Cc: Joshua Hahn Cc: Kefeng Wang Cc: Liam R. Howlett Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nanyong Sun Cc: Pedro Falcato Cc: Peter Xu Cc: Rafael Aquini Cc: Rakie Kim Cc: Randy Dunlap Cc: Ryan Roberts Cc: Shivank Garg Cc: Steven Rostedt Cc: Suren Baghdasaryan Cc: Takashi Iwai (SUSE) Cc: Thomas Hellström Cc: Usama Arif Cc: Usama Arif Cc: Vishal Moola (Oracle) Cc: Vlastimil Babka Cc: Wei Yang Cc: Will Deacon Cc: Yang Shi Cc: Zach O'Keefe Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/transhuge.rst | 49 ++++++++++++++-------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index b98e18c80185..23f8d13c2629 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -63,7 +63,8 @@ often. THP can be enabled system wide or restricted to certain tasks or even memory ranges inside task's address space. Unless THP is completely disabled, there is ``khugepaged`` daemon that scans memory and -collapses sequences of basic pages into PMD-sized huge pages. +collapses sequences of basic pages into huge pages of either PMD size +or mTHP sizes, if the system is configured to do so. The THP behaviour is controlled via :ref:`sysfs ` interface and using madvise(2) and prctl(2) system calls. @@ -219,10 +220,10 @@ this behaviour by writing 0 to shrink_underused, and enable it by writing echo 0 > /sys/kernel/mm/transparent_hugepage/shrink_underused echo 1 > /sys/kernel/mm/transparent_hugepage/shrink_underused -khugepaged will be automatically started when PMD-sized THP is enabled +khugepaged will be automatically started when any THP size is enabled (either of the per-size anon control or the top-level control are set to "always" or "madvise"), and it'll be automatically shutdown when -PMD-sized THP is disabled (when both the per-size anon control and the +all THP sizes are disabled (when both the per-size anon control and the top-level control are "never") process THP controls @@ -265,8 +266,8 @@ Khugepaged controls ------------------- .. note:: - khugepaged currently only searches for opportunities to collapse to - PMD-sized THP and no attempt is made to collapse to other THP + khugepaged currently only searches for opportunities to collapse file/shmem + to PMD-sized THP. Only anonymous memory will attempt to collapse to other THP sizes. khugepaged runs usually at low frequency so while one may not want to @@ -296,11 +297,11 @@ allocation failure to throttle the next allocation attempt:: The khugepaged progress can be seen in the number of pages collapsed (note that this counter may not be an exact count of the number of pages collapsed, since "collapsed" could mean multiple things: (1) A PTE mapping -being replaced by a PMD mapping, or (2) All 4K physical pages replaced by -one 2M hugepage. Each may happen independently, or together, depending on -the type of memory and the failures that occur. As such, this value should -be interpreted roughly as a sign of progress, and counters in /proc/vmstat -consulted for more accurate accounting):: +being replaced by a PMD mapping, or (2) physical pages replaced by one +hugepage of various sizes (PMD-sized or mTHP). Each may happen independently, +or together, depending on the type of memory and the failures that occur. +As such, this value should be interpreted roughly as a sign of progress, +and counters in /proc/vmstat consulted for more accurate accounting):: /sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed @@ -308,16 +309,21 @@ for each pass:: /sys/kernel/mm/transparent_hugepage/khugepaged/full_scans -``max_ptes_none`` specifies how many extra small pages (that are -not already mapped) can be allocated when collapsing a group -of small pages into one large page:: +``max_ptes_none`` specifies how many empty (none/zero) pages are allowed +when collapsing a group of small pages into one large page:: /sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none -A higher value leads to use additional memory for programs. -A lower value leads to gain less thp performance. Value of -max_ptes_none can waste cpu time very little, you can -ignore it. +For PMD-sized THP collapse, this directly limits the number of empty pages +allowed in the 2MB region. + +For mTHP collapse, only 0 or (HPAGE_PMD_NR - 1) are supported. At +HPAGE_PMD_NR - 1, we collapse to the highest possible order. Any intermediate +value will emit a warning and mTHP collapse will default to max_ptes_none=0. + +A higher value allows more empty pages, potentially leading to more memory +usage but better THP performance. A lower value is more conservative and +may result in fewer THP collapses. ``max_ptes_swap`` specifies how many pages can be brought in from swap when collapsing a group of pages into a transparent huge page:: @@ -337,6 +343,15 @@ that THP is shared. Exceeding the number would block the collapse:: A higher value may increase memory footprint for some workloads. +.. note:: + For mTHP collapse, khugepaged does not support collapsing regions that + contain shared or swapped out pages, as this could lead to continuous + promotion to higher orders. The collapse will fail if any shared or + swapped PTEs are encountered during the scan. + + Currently, madvise_collapse only supports collapsing to PMD-sized THPs + and does not attempt mTHP collapses. + Boot parameters =============== From c85418be96666be9d6127448baad95ca404ee34e Mon Sep 17 00:00:00 2001 From: Lance Yang Date: Tue, 9 Jun 2026 20:04:43 +0800 Subject: [PATCH 4188/5214] mm/khugepaged: fix PMD collapse swap PTE accounting mthp_collapse() uses mthp_present_ptes to decide whether a range has enough occupied PTEs to try collapse. Swap PTEs accepted by collapse_scan_pmd() are counted in unmapped, but are not represented in mthp_present_ptes. When lower orders are enabled, collapse_scan_pmd() relaxes max_ptes_none so the scan can cover the whole PMD and build the bitmap. mthp_collapse() then checks the PMD-order candidate using the bitmap. With max_ptes_none set to 0, a range with 511 present PTEs and one swap PTE no longer reaches collapse_huge_page(), even though PMD collapse can handle swap PTEs up to max_ptes_swap. Account unmapped PTEs only for PMD order. PMD collapse supports swap PTEs through max_ptes_swap, while lower-order mTHP collapse does not currently support non-present PTEs. Keep non-present PTEs out of the lower-order eligibility check. Link: https://lore.kernel.org/20260609120443.71864-1-lance.yang@linux.dev Fixes: 90ed32d00054 ("mm/khugepaged: introduce mTHP collapse support") Signed-off-by: Lance Yang Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Acked-by: Nico Pache Reviewed-by: Baolin Wang Reviewed-by: Wei Yang Cc: Barry Song Cc: Dev Jain Cc: Liam R. Howlett Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 6de1a148222a..33ab5d12e1bc 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1502,6 +1502,14 @@ static enum scan_result mthp_collapse(struct mm_struct *mm, nr_occupied_ptes = bitmap_weight_from(cc->mthp_present_ptes, offset, offset + nr_ptes); + /* + * Swap PTEs accepted during the scan are counted in @unmapped, + * not in the present-PTE bitmap. Account them for the PMD-order + * candidate. + */ + if (is_pmd_order(order)) + nr_occupied_ptes += unmapped; + if (nr_occupied_ptes >= nr_ptes - max_ptes_none) { enum scan_result ret; From cd2d3d1f26c27eace8c70bd481889afd3c34a42c Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:03 -0400 Subject: [PATCH 4189/5214] mm/khugepaged: remove READ_ONLY_THP_FOR_FS check Patch series "Remove CONFIG_READ_ONLY_THP_FOR_FS and enable file THP for writable files", v6. This patch (of 14): collapse_file() requires FSes supporting large folio with at least PMD_ORDER, so replace the READ_ONLY_THP_FOR_FS check with that. MADV_COLLAPSE ignores shmem huge config, so exclude the check for shmem. While at it, replace VM_BUG_ON with VM_WARN_ON_ONCE. Add a helper function mapping_pmd_folio_support() for FSes supporting large folio with at least PMD_ORDER. Link: https://lore.kernel.org/20260517135416.1434539-1-ziy@nvidia.com Link: https://lore.kernel.org/20260517135416.1434539-2-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lance Yang Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (Arm) Reviewed-by: Nico Pache Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/pagemap.h | 27 +++++++++++++++++++++++++++ mm/khugepaged.c | 10 ++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 1f50991b43e3..308d846531d0 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -513,6 +513,33 @@ static inline bool mapping_large_folio_support(const struct address_space *mappi return mapping_max_folio_order(mapping) > 0; } +/** + * mapping_pmd_folio_support() - Check if a mapping supports PMD-sized folio + * @mapping: The address_space + * + * While some mappings support large folios, they might not support PMD-sized + * folios. This function checks whether a mapping supports PMD-sized folios. + * For example, khugepaged needs this information before attempting to + * collapsing THPs. + * + * Return: True if PMD-sized folios are supported, otherwise false. + */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static inline bool mapping_pmd_folio_support(const struct address_space *mapping) +{ + /* AS_FOLIO_ORDER is only reasonable for pagecache folios */ + VM_WARN_ON_ONCE((unsigned long)mapping & FOLIO_MAPPING_ANON); + + return mapping_min_folio_order(mapping) <= PMD_ORDER && + mapping_max_folio_order(mapping) >= PMD_ORDER; +} +#else +static inline bool mapping_pmd_folio_support(const struct address_space *mapping) +{ + return false; +} +#endif + /* Return the maximum folio size for this pagecache mapping, in bytes. */ static inline size_t mapping_max_folio_size(const struct address_space *mapping) { diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 33ab5d12e1bc..06be690fe8d2 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2246,8 +2246,14 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, int nr_none = 0; bool is_shmem = shmem_file(file); - VM_BUG_ON(!IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && !is_shmem); - VM_BUG_ON(start & (HPAGE_PMD_NR - 1)); + /* + * MADV_COLLAPSE ignores shmem huge config, so do not check shmem + * + * TODO: once shmem always calls mapping_set_large_folios() on its + * mapping, the shmem check can be removed. + */ + VM_WARN_ON_ONCE(!is_shmem && !mapping_pmd_folio_support(mapping)); + VM_WARN_ON_ONCE(start & (HPAGE_PMD_NR - 1)); result = alloc_charge_folio(&new_folio, mm, cc, HPAGE_PMD_ORDER); if (result != SCAN_SUCCEED) From 4e3d769bf0fc13f2b44c9e693e587176b15200b8 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:04 -0400 Subject: [PATCH 4190/5214] mm/khugepaged: add folio dirty check after try_to_unmap() This check ensures the correctness of read-only PMD folio collapse after it is enabled for all FSes supporting PMD pagecache folios and replaces READ_ONLY_THP_FOR_FS. READ_ONLY_THP_FOR_FS only supports read-only fd and uses mapping->nr_thps and inode->i_writecount to prevent any write to read-only to-be-collapsed folios. In upcoming commits, READ_ONLY_THP_FOR_FS will be removed and the aforementioned mechanism will go away too. To ensure khugepaged functions as expected after the changes, skip if any folio is dirty after try_to_unmap(), since a dirty folio at that point means this read-only folio can get writes between try_to_unmap() and try_to_unmap_flush() via cached TLB entries and khugepaged does not support writable pagecache folio collapse yet. Link: https://lore.kernel.org/20260517135416.1434539-3-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Reviewed-by: Nico Pache Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/khugepaged.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 06be690fe8d2..eca22f1185d9 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2338,8 +2338,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, } } else if (folio_test_dirty(folio)) { /* - * khugepaged only works on read-only fd, - * so this page is dirty because it hasn't + * This page is dirty because it hasn't * been flushed since first write. There * won't be new dirty pages. * @@ -2397,8 +2396,8 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, if (!is_shmem && (folio_test_dirty(folio) || folio_test_writeback(folio))) { /* - * khugepaged only works on read-only fd, so this - * folio is dirty because it hasn't been flushed + * khugepaged only works on clean file-backed folios, + * so this folio is dirty because it hasn't been flushed * since first write. */ result = SCAN_PAGE_DIRTY_OR_WRITEBACK; @@ -2442,6 +2441,27 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, goto out_unlock; } + /* + * At this point, the folio is locked and unmapped. If the PTE + * was dirty, try_to_unmap() has transferred the dirty bit to + * the folio and we must not collapse it into a clean + * file-backed folio. + * + * If the folio is clean here, no one can write it until we + * drop the folio lock. A write through a stale TLB entry came + * from a clean PTE and must fault because the PTE has been + * cleared; the fault path has to take the folio lock before + * installing a writable mapping. Buffered write paths also + * have to take the folio lock before modifying file contents + * without a mapping, typically via write_begin_get_folio(). + */ + if (!is_shmem && folio_test_dirty(folio)) { + result = SCAN_PAGE_DIRTY_OR_WRITEBACK; + xas_unlock_irq(&xas); + folio_putback_lru(folio); + goto out_unlock; + } + /* * Accumulate the folios that are being collapsed. */ From 8a088263a097d567c212159d3a7bc747348e31fd Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:05 -0400 Subject: [PATCH 4191/5214] mm/huge_memory: remove READ_ONLY_THP_FOR_FS from file_thp_enabled() Replace it with a check on the max folio order of the file's address space mapping, making sure PMD folio is supported. Keep the inode open-for-write check, since even if collapse_file() now makes sure all to-be-collapsed folios are clean and the created PMD file THP can be handled by FSes properly, the filemap_flush() could perform undesirable write back. Link: https://lore.kernel.org/20260517135416.1434539-4-ziy@nvidia.com Signed-off-by: Zi Yan Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Reviewed-by: Nico Pache Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/huge_memory.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b4062396ce87..8808eb56ef0e 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -89,9 +89,6 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma) { struct inode *inode; - if (!IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS)) - return false; - if (!vma->vm_file) return false; @@ -100,6 +97,9 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma) if (IS_ANON_FILE(inode)) return false; + if (!mapping_pmd_folio_support(vma->vm_file->f_mapping)) + return false; + return !inode_is_open_for_write(inode) && S_ISREG(inode->i_mode); } From efc36a715383439d7987b2d592b7549144fb5aad Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:06 -0400 Subject: [PATCH 4192/5214] mm/khugepaged: remove READ_ONLY_THP_FOR_FS check in hugepage_enabled() Remove the READ_ONLY_THP_FOR_FS gate and khugepaged for file-backed pmd-sized hugepages are enabled by the global transparent hugepage control. khugepaged can still be enabled by per-size control for anon and shmem when the global control is off. Add shmem_hpage_pmd_enabled() stub for !CONFIG_SHMEM to remove IS_ENABLED(SHMEM) in hugepage_enabled(). Clean up hugepage_enabled() by moving anon code to anon_hpage_enabled(). Link: https://lore.kernel.org/20260517135416.1434539-5-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (Arm) Reviewed-by: Nico Pache Reviewed-by: Lance Yang Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/shmem_fs.h | 2 +- mm/khugepaged.c | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 93a0ba872ebe..acb8dd961b45 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -127,7 +127,7 @@ int shmem_writeout(struct folio *folio, struct swap_iocb **plug, void shmem_truncate_range(struct inode *inode, loff_t start, uoff_t end); int shmem_unuse(unsigned int type); -#ifdef CONFIG_TRANSPARENT_HUGEPAGE +#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && defined(CONFIG_SHMEM) unsigned long shmem_allowable_huge_orders(struct inode *inode, struct vm_area_struct *vma, pgoff_t index, loff_t write_end, bool shmem_huge_force); diff --git a/mm/khugepaged.c b/mm/khugepaged.c index eca22f1185d9..d8fdcc7882ad 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -503,18 +503,8 @@ static inline int collapse_test_exit_or_disable(struct mm_struct *mm) mm_flags_test(MMF_DISABLE_THP_COMPLETELY, mm); } -static bool hugepage_enabled(void) +static inline bool anon_hpage_enabled(void) { - /* - * We cover the anon, shmem and the file-backed case here; file-backed - * hugepages, when configured in, are determined by the global control. - * Anon hugepages are determined by its per-size mTHP control. - * Shmem pmd-sized hugepages are also determined by its pmd-size control, - * except when the global shmem_huge is set to SHMEM_HUGE_DENY. - */ - if (IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && - hugepage_global_enabled()) - return true; if (READ_ONCE(huge_anon_orders_always)) return true; if (READ_ONCE(huge_anon_orders_madvise)) @@ -522,7 +512,23 @@ static bool hugepage_enabled(void) if (READ_ONCE(huge_anon_orders_inherit) && hugepage_global_enabled()) return true; - if (IS_ENABLED(CONFIG_SHMEM) && shmem_hpage_pmd_enabled()) + return false; +} + +static bool hugepage_enabled(void) +{ + /* + * We cover the anon, shmem and the file-backed case here; file-backed + * hugepages are determined by the global control. + * Anon hugepages are determined by its per-size mTHP control. + * Shmem pmd-sized hugepages are also determined by its pmd-size control, + * except when the global shmem_huge is set to SHMEM_HUGE_DENY. + */ + if (hugepage_global_enabled()) + return true; + if (anon_hpage_enabled()) + return true; + if (shmem_hpage_pmd_enabled()) return true; return false; } From e4df0f37bd95bff5eb64e2a28eba378d64f4c01e Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:07 -0400 Subject: [PATCH 4193/5214] mm: remove READ_ONLY_THP_FOR_FS Kconfig option After removing READ_ONLY_THP_FOR_FS check in file_thp_enabled(), khugepaged and MADV_COLLAPSE can run on FSes with PMD THP pagecache support even without READ_ONLY_THP_FOR_FS enabled. Remove the Kconfig first so that no one can use READ_ONLY_THP_FOR_FS as upcoming commits remove mapping->nr_thps, which its safe guard mechanism relies on. Link: https://lore.kernel.org/20260517135416.1434539-6-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lorenzo Stoakes (Oracle) Acked-by: David Hildenbrand (Arm) Reviewed-by: Baolin Wang Reviewed-by: Nico Pache Reviewed-by: Lance Yang Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/Kconfig | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/mm/Kconfig b/mm/Kconfig index 97b079372325..776b67c66e82 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -936,17 +936,6 @@ config THP_SWAP For selection by architectures with reasonable THP sizes. -config READ_ONLY_THP_FOR_FS - bool "Read-only THP for filesystems (EXPERIMENTAL)" - depends on TRANSPARENT_HUGEPAGE - - help - Allow khugepaged to put read-only file-backed pages in THP. - - This is marked experimental because it is a new feature. Write - support of file THPs will be developed in the next few release - cycles. - config NO_PAGE_MAPCOUNT bool "No per-page mapcount (EXPERIMENTAL)" help From 044925f9b56501f893bbfb7f4acfdbe0f5fba0d1 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:08 -0400 Subject: [PATCH 4194/5214] mm: fs: remove filemap_nr_thps*() functions and their users They are used by READ_ONLY_THP_FOR_FS to handle writes to FSes without large folio support, so that read-only THPs created in these FSes are not seen by the FSes when the underlying fd becomes writable. Now read-only PMD THPs only appear in a FS with large folio support and the supported orders include PMD_ORDER. READ_ONLY_THP_FOR_FS was using mapping->nr_thps, inode->i_writecount, and smp_mb() to prevent writes to a read-only THP and collapsing writable folios into a THP. In collapse_file(), mapping->nr_thps is increased, then smp_mb(), and if inode->i_writecount > 0, collapse is stopped, while do_dentry_open() first increases inode->i_writecount, then a full memory fence, and if mapping->nr_thps > 0, all read-only THPs are truncated. Now this mechanism can be removed along with READ_ONLY_THP_FOR_FS code, since a dirty folio check has been added after try_to_unmap() in collapse_file() to prevent dirty folios from being collapsed as clean. Link: https://lore.kernel.org/20260517135416.1434539-7-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Matthew Wilcox (Oracle) Acked-by: David Hildenbrand (Arm) Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- fs/open.c | 27 --------------------------- include/linux/pagemap.h | 29 ----------------------------- mm/filemap.c | 1 - mm/huge_memory.c | 1 - mm/khugepaged.c | 28 ---------------------------- 5 files changed, 86 deletions(-) diff --git a/fs/open.c b/fs/open.c index 681d405bc61e..c321b80027f1 100644 --- a/fs/open.c +++ b/fs/open.c @@ -968,33 +968,6 @@ static int do_dentry_open(struct file *f, if ((f->f_flags & O_DIRECT) && !(f->f_mode & FMODE_CAN_ODIRECT)) return -EINVAL; - /* - * XXX: Huge page cache doesn't support writing yet. Drop all page - * cache for this file before processing writes. - */ - if (f->f_mode & FMODE_WRITE) { - /* - * Depends on full fence from get_write_access() to synchronize - * against collapse_file() regarding i_writecount and nr_thps - * updates. Ensures subsequent insertion of THPs into the page - * cache will fail. - */ - if (filemap_nr_thps(inode->i_mapping)) { - struct address_space *mapping = inode->i_mapping; - - filemap_invalidate_lock(inode->i_mapping); - /* - * unmap_mapping_range just need to be called once - * here, because the private pages is not need to be - * unmapped mapping (e.g. data segment of dynamic - * shared libraries here). - */ - unmap_mapping_range(mapping, 0, 0, 0); - truncate_inode_pages(mapping, 0); - filemap_invalidate_unlock(inode->i_mapping); - } - } - return 0; cleanup_all: diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 308d846531d0..627771e82eb1 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -546,35 +546,6 @@ static inline size_t mapping_max_folio_size(const struct address_space *mapping) return PAGE_SIZE << mapping_max_folio_order(mapping); } -static inline int filemap_nr_thps(const struct address_space *mapping) -{ -#ifdef CONFIG_READ_ONLY_THP_FOR_FS - return atomic_read(&mapping->nr_thps); -#else - return 0; -#endif -} - -static inline void filemap_nr_thps_inc(struct address_space *mapping) -{ -#ifdef CONFIG_READ_ONLY_THP_FOR_FS - if (!mapping_large_folio_support(mapping)) - atomic_inc(&mapping->nr_thps); -#else - WARN_ON_ONCE(mapping_large_folio_support(mapping) == 0); -#endif -} - -static inline void filemap_nr_thps_dec(struct address_space *mapping) -{ -#ifdef CONFIG_READ_ONLY_THP_FOR_FS - if (!mapping_large_folio_support(mapping)) - atomic_dec(&mapping->nr_thps); -#else - WARN_ON_ONCE(mapping_large_folio_support(mapping) == 0); -#endif -} - struct address_space *folio_mapping(const struct folio *folio); /** diff --git a/mm/filemap.c b/mm/filemap.c index 5d9f9b36e9d8..dc3a0e960b9f 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -189,7 +189,6 @@ static void filemap_unaccount_folio(struct address_space *mapping, lruvec_stat_mod_folio(folio, NR_SHMEM_THPS, -nr); } else if (folio_test_pmd_mappable(folio)) { lruvec_stat_mod_folio(folio, NR_FILE_THPS, -nr); - filemap_nr_thps_dec(mapping); } if (test_bit(AS_KERNEL_FILE, &folio->mapping->flags)) mod_node_page_state(folio_pgdat(folio), diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 8808eb56ef0e..2fc3356d0a3c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3877,7 +3877,6 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n } else { lruvec_stat_mod_folio(folio, NR_FILE_THPS, -nr); - filemap_nr_thps_dec(mapping); } } } diff --git a/mm/khugepaged.c b/mm/khugepaged.c index d8fdcc7882ad..d9de48423a2e 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2480,21 +2480,6 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, goto xa_unlocked; } - if (!is_shmem) { - filemap_nr_thps_inc(mapping); - /* - * Paired with the fence in do_dentry_open() -> get_write_access() - * to ensure i_writecount is up to date and the update to nr_thps - * is visible. Ensures the page cache will be truncated if the - * file is opened writable. - */ - smp_mb(); - if (inode_is_open_for_write(mapping->host)) { - result = SCAN_FAIL; - filemap_nr_thps_dec(mapping); - } - } - xa_locked: xas_unlock_irq(&xas); xa_unlocked: @@ -2672,19 +2657,6 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, folio_putback_lru(folio); folio_put(folio); } - /* - * Undo the updates of filemap_nr_thps_inc for non-SHMEM - * file only. This undo is not needed unless failure is - * due to SCAN_COPY_MC. - */ - if (!is_shmem && result == SCAN_COPY_MC) { - filemap_nr_thps_dec(mapping); - /* - * Paired with the fence in do_dentry_open() -> get_write_access() - * to ensure the update to nr_thps is visible. - */ - smp_mb(); - } new_folio->mapping = NULL; From 27e7918905ee918d8ca268afc98234e8c8450641 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:09 -0400 Subject: [PATCH 4195/5214] fs: remove nr_thps from struct address_space filemap_nr_thps*() are removed, the related field, address_space->nr_thps, is no longer needed. Remove it. This shrinks struct address_space by 8 bytes on 64-bit systems which may increase the number of inodes we can cache. Link: https://lore.kernel.org/20260517135416.1434539-8-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lorenzo Stoakes (Oracle) Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: Baolin Wang Reviewed-by: Nico Pache Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- fs/inode.c | 3 --- include/linux/fs.h | 5 ----- 2 files changed, 8 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 62c579a0cf7d..11bfb93ef0e6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -279,9 +279,6 @@ int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp mapping->flags = 0; mapping->wb_err = 0; atomic_set(&mapping->i_mmap_writable, 0); -#ifdef CONFIG_READ_ONLY_THP_FOR_FS - atomic_set(&mapping->nr_thps, 0); -#endif mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->writeback_index = 0; init_rwsem(&mapping->invalidate_lock); diff --git a/include/linux/fs.h b/include/linux/fs.h index 11559c513dfb..bb9cc4f7207c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -460,7 +460,6 @@ struct mapping_metadata_bhs { * memory mappings. * @gfp_mask: Memory allocation flags to use for allocating pages. * @i_mmap_writable: Number of VM_SHARED, VM_MAYWRITE mappings. - * @nr_thps: Number of THPs in the pagecache (non-shmem only). * @i_mmap: Tree of private and shared mappings. * @i_mmap_rwsem: Protects @i_mmap and @i_mmap_writable. * @nrpages: Number of page entries, protected by the i_pages lock. @@ -476,10 +475,6 @@ struct address_space { struct rw_semaphore invalidate_lock; gfp_t gfp_mask; atomic_t i_mmap_writable; -#ifdef CONFIG_READ_ONLY_THP_FOR_FS - /* number of thp, only for non-shmem files */ - atomic_t nr_thps; -#endif struct rb_root_cached i_mmap; unsigned long nrpages; pgoff_t writeback_index; From 7c16f524f20c0fe7d694ef889e51a89872846931 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:10 -0400 Subject: [PATCH 4196/5214] mm/huge_memory: remove folio split check for READ_ONLY_THP_FOR_FS Without READ_ONLY_THP_FOR_FS, large file-backed folios cannot be created by a FS without large folio support. The check is no longer needed. Link: https://lore.kernel.org/20260517135416.1434539-9-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lance Yang Reviewed-by: Lorenzo Stoakes (Oracle) Reviewed-by: Baolin Wang Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/huge_memory.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 2fc3356d0a3c..74bdf6ecd0cd 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3763,33 +3763,9 @@ int folio_check_splittable(struct folio *folio, unsigned int new_order, if (!folio->mapping && !folio_test_anon(folio)) return -EBUSY; - if (folio_test_anon(folio)) { - /* order-1 is not supported for anonymous THP. */ - if (new_order == 1) - return -EINVAL; - } else if (split_type == SPLIT_TYPE_NON_UNIFORM || new_order) { - if (IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && - !mapping_large_folio_support(folio->mapping)) { - /* - * We can always split a folio down to a single page - * (new_order == 0) uniformly. - * - * For any other scenario - * a) uniform split targeting a large folio - * (new_order > 0) - * b) any non-uniform split - * we must confirm that the file system supports large - * folios. - * - * Note that we might still have THPs in such - * mappings, which is created from khugepaged when - * CONFIG_READ_ONLY_THP_FOR_FS is enabled. But in that - * case, the mapping does not actually support large - * folios properly. - */ - return -EINVAL; - } - } + /* order-1 is not supported for anonymous THP. */ + if (folio_test_anon(folio) && new_order == 1) + return -EINVAL; /* * swapcache folio could only be split to order 0 From 15e2258e255525ddae5c78c1ffdc88fe8a1116e6 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:11 -0400 Subject: [PATCH 4197/5214] mm/truncate: use folio_split() in truncate_inode_partial_folio() After READ_ONLY_THP_FOR_FS is removed, FS either supports large folio or not. folio_split() can be used on a FS with large folio support without worrying about getting a THP on a FS without large folio support. When READ_ONLY_THP_FOR_FS was present, a PMD large pagecache folio can appear in a FS without large folio support after khugepaged or madvise(MADV_COLLAPSE) creates it. During truncate_inode_partial_folio(), such a PMD large pagecache folio is split and if the FS does not support large folio, it needs to be split to order-0 ones and could not be split non uniformly to ones with various orders. try_folio_split_to_order() was added to handle this situation by checking folio_check_splittable(..., SPLIT_TYPE_NON_UNIFORM) to detect if the large folio is created due to READ_ONLY_THP_FOR_FS and the FS does not support large folio. Now READ_ONLY_THP_FOR_FS is removed, all large pagecache folios are created with FSes supporting large folio, this function is no longer needed and all large pagecache folios can be split non uniformly. Link: https://lore.kernel.org/20260517135416.1434539-10-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lance Yang Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/huge_mm.h | 25 ++----------------------- mm/truncate.c | 8 ++++---- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 89f4c22d7d62..ad20f7f8c179 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -419,27 +419,6 @@ static inline int split_huge_page_to_order(struct page *page, unsigned int new_o return split_huge_page_to_list_to_order(page, NULL, new_order); } -/** - * try_folio_split_to_order() - try to split a @folio at @page to @new_order - * using non uniform split. - * @folio: folio to be split - * @page: split to @new_order at the given page - * @new_order: the target split order - * - * Try to split a @folio at @page using non uniform split to @new_order, if - * non uniform split is not supported, fall back to uniform split. After-split - * folios are put back to LRU list. Use min_order_for_split() to get the lower - * bound of @new_order. - * - * Return: 0 - split is successful, otherwise split failed. - */ -static inline int try_folio_split_to_order(struct folio *folio, - struct page *page, unsigned int new_order) -{ - if (folio_check_splittable(folio, new_order, SPLIT_TYPE_NON_UNIFORM)) - return split_huge_page_to_order(&folio->page, new_order); - return folio_split(folio, new_order, page, NULL); -} static inline int split_huge_page(struct page *page) { return split_huge_page_to_list_to_order(page, NULL, 0); @@ -677,8 +656,8 @@ static inline int split_folio_to_list(struct folio *folio, struct list_head *lis return -EINVAL; } -static inline int try_folio_split_to_order(struct folio *folio, - struct page *page, unsigned int new_order) +static inline int folio_split(struct folio *folio, unsigned int new_order, + struct page *page, struct list_head *list) { VM_WARN_ON_ONCE_FOLIO(1, folio); return -EINVAL; diff --git a/mm/truncate.c b/mm/truncate.c index 12cc89f89afc..b58ba940be47 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -177,7 +177,7 @@ int truncate_inode_folio(struct address_space *mapping, struct folio *folio) return 0; } -static int try_folio_split_or_unmap(struct folio *folio, struct page *split_at, +static int folio_split_or_unmap(struct folio *folio, struct page *split_at, unsigned long min_order) { enum ttu_flags ttu_flags = @@ -186,7 +186,7 @@ static int try_folio_split_or_unmap(struct folio *folio, struct page *split_at, TTU_IGNORE_MLOCK; int ret; - ret = try_folio_split_to_order(folio, split_at, min_order); + ret = folio_split(folio, min_order, split_at, NULL); /* * If the split fails, unmap the folio, so it will be refaulted @@ -252,7 +252,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end) min_order = mapping_min_folio_order(folio->mapping); split_at = folio_page(folio, PAGE_ALIGN_DOWN(offset) / PAGE_SIZE); - if (!try_folio_split_or_unmap(folio, split_at, min_order)) { + if (!folio_split_or_unmap(folio, split_at, min_order)) { /* * try to split at offset + length to make sure folios within * the range can be dropped, especially to avoid memory waste @@ -279,7 +279,7 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end) /* make sure folio2 is large and does not change its mapping */ if (folio_test_large(folio2) && folio2->mapping == folio->mapping) - try_folio_split_or_unmap(folio2, split_at2, min_order); + folio_split_or_unmap(folio2, split_at2, min_order); folio_unlock(folio2); out: From 14a357d028990fc4b2b163963e9232c1eb1da5f8 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:12 -0400 Subject: [PATCH 4198/5214] fs/btrfs: remove a comment referring to READ_ONLY_THP_FOR_FS READ_ONLY_THP_FOR_FS is no longer present, remove related comment. Link: https://lore.kernel.org/20260517135416.1434539-11-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: David Hildenbrand (Arm) Acked-by: David Sterba Reviewed-by: Lorenzo Stoakes (Oracle) Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: Dev Jain Cc: Jan Kara Cc: Lance Yang Cc: Liam Howlett Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- fs/btrfs/defrag.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index 7e2db5d3a4d4..a8d49d9ca981 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -860,9 +860,6 @@ static struct folio *defrag_prepare_one_folio(struct btrfs_inode *inode, pgoff_t return folio; /* - * Since we can defragment files opened read-only, we can encounter - * transparent huge pages here (see CONFIG_READ_ONLY_THP_FOR_FS). - * * The IO for such large folios is not fully tested, thus return * an error to reject such folios unless it's an experimental build. * From a2f8f73636cb9b35231c0b721b6327f6efe811d1 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:13 -0400 Subject: [PATCH 4199/5214] selftests/mm: remove READ_ONLY_THP_FOR_FS in khugepaged Change the requirement to a file system with large folio support and the supported order needs to include PMD_ORDER. Also add tests of opening a file with read write permission and populating folios with writes. Reuse the XFS image from split_huge_page_test. Link: https://lore.kernel.org/20260517135416.1434539-12-ziy@nvidia.com Signed-off-by: Zi Yan Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Hildenbrand (Arm) Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Lance Yang Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/khugepaged.c | 125 +++++++++++++++------- tools/testing/selftests/mm/run_vmtests.sh | 12 ++- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index c8393ca52cab..7ee6dfe93b3d 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -49,7 +49,8 @@ struct mem_ops { const char *name; }; -static struct mem_ops *file_ops; +static struct mem_ops *read_only_file_ops; +static struct mem_ops *read_write_file_ops; static struct mem_ops *anon_ops; static struct mem_ops *shmem_ops; @@ -112,7 +113,8 @@ static void restore_settings(int sig) static void save_settings(void) { printf("Save THP and khugepaged settings..."); - if (file_ops && finfo.type == VMA_FILE) + if ((read_only_file_ops || read_write_file_ops) && + finfo.type == VMA_FILE) thp_set_read_ahead_path(finfo.dev_queue_read_ahead_path); thp_save_settings(); @@ -364,8 +366,10 @@ static bool anon_check_huge(void *addr, int nr_hpages) return check_huge_anon(addr, nr_hpages, hpage_pmd_size); } -static void *file_setup_area(int nr_hpages) +static void *file_setup_area_common(int nr_hpages, bool read_only) { + const int open_opt = read_only ? O_RDONLY : O_RDWR; + const int mmap_prot = read_only ? PROT_READ : (PROT_READ | PROT_WRITE); int fd; void *p; unsigned long size; @@ -400,14 +404,14 @@ static void *file_setup_area(int nr_hpages) munmap(p, size); success("OK"); - printf("Opening %s read only for collapse...", finfo.path); - finfo.fd = open(finfo.path, O_RDONLY, 777); + printf("Opening %s %s for collapse...", finfo.path, + read_only ? "read-only" : "read-write"); + finfo.fd = open(finfo.path, open_opt, 777); if (finfo.fd < 0) { perror("open()"); exit(EXIT_FAILURE); } - p = mmap(BASE_ADDR, size, PROT_READ, - MAP_PRIVATE, finfo.fd, 0); + p = mmap(BASE_ADDR, size, mmap_prot, MAP_SHARED, finfo.fd, 0); if (p == MAP_FAILED || p != BASE_ADDR) { perror("mmap()"); exit(EXIT_FAILURE); @@ -419,6 +423,16 @@ static void *file_setup_area(int nr_hpages) return p; } +static void *file_setup_read_only_area(int nr_hpages) +{ + return file_setup_area_common(nr_hpages, /* read_only= */ true); +} + +static void *file_setup_read_write_area(int nr_hpages) +{ + return file_setup_area_common(nr_hpages, /* read_only= */ false); +} + static void file_cleanup_area(void *p, unsigned long size) { munmap(p, size); @@ -426,10 +440,18 @@ static void file_cleanup_area(void *p, unsigned long size) unlink(finfo.path); } -static void file_fault(void *p, unsigned long start, unsigned long end) +static void file_fault_read(void *p, unsigned long start, unsigned long end) { if (madvise(((char *)p) + start, end - start, MADV_POPULATE_READ)) { - perror("madvise(MADV_POPULATE_READ"); + perror("madvise(MADV_POPULATE_READ)"); + exit(EXIT_FAILURE); + } +} + +static void file_fault_write(void *p, unsigned long start, unsigned long end) +{ + if (madvise(((char *)p) + start, end - start, MADV_POPULATE_WRITE)) { + perror("madvise(MADV_POPULATE_WRITE)"); exit(EXIT_FAILURE); } } @@ -489,10 +511,18 @@ static struct mem_ops __anon_ops = { .name = "anon", }; -static struct mem_ops __file_ops = { - .setup_area = &file_setup_area, +static struct mem_ops __read_only_file_ops = { + .setup_area = &file_setup_read_only_area, .cleanup_area = &file_cleanup_area, - .fault = &file_fault, + .fault = &file_fault_read, + .check_huge = &file_check_huge, + .name = "file", +}; + +static struct mem_ops __read_write_file_ops = { + .setup_area = &file_setup_read_write_area, + .cleanup_area = &file_cleanup_area, + .fault = &file_fault_write, .check_huge = &file_check_huge, .name = "file", }; @@ -505,6 +535,17 @@ static struct mem_ops __shmem_ops = { .name = "shmem", }; +static bool is_tmpfs(struct mem_ops *ops) +{ + return (ops == &__read_only_file_ops || ops == &__read_write_file_ops) && + finfo.type == VMA_SHMEM; +} + +static bool is_anon(struct mem_ops *ops) +{ + return ops == &__anon_ops; +} + static void __madvise_collapse(const char *msg, char *p, int nr_hpages, struct mem_ops *ops, bool expect) { @@ -513,6 +554,10 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages, printf("%s...", msg); + /* read&write file collapse always fail */ + if (!is_tmpfs(ops) && ops == &__read_write_file_ops) + expect = false; + /* * Prevent khugepaged interference and tests that MADV_COLLAPSE * ignores /sys/kernel/mm/transparent_hugepage/enabled @@ -579,6 +624,10 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages, static void khugepaged_collapse(const char *msg, char *p, int nr_hpages, struct mem_ops *ops, bool expect) { + /* read&write file collapse always fail */ + if (!is_tmpfs(ops) && ops == &__read_write_file_ops) + expect = false; + if (wait_for_scan(msg, p, nr_hpages, ops)) { if (expect) fail("Timeout"); @@ -613,16 +662,6 @@ static struct collapse_context __madvise_context = { .name = "madvise", }; -static bool is_tmpfs(struct mem_ops *ops) -{ - return ops == &__file_ops && finfo.type == VMA_SHMEM; -} - -static bool is_anon(struct mem_ops *ops) -{ - return ops == &__anon_ops; -} - static void alloc_at_fault(void) { struct thp_settings settings = *thp_current_settings(); @@ -1098,8 +1137,8 @@ static void usage(void) fprintf(stderr, "\t\t: [all|khugepaged|madvise]\n"); fprintf(stderr, "\t\t: [all|anon|file|shmem]\n"); fprintf(stderr, "\n\t\"file,all\" mem_type requires [dir] argument\n"); - fprintf(stderr, "\n\t\"file,all\" mem_type requires kernel built with\n"); - fprintf(stderr, "\tCONFIG_READ_ONLY_THP_FOR_FS=y\n"); + fprintf(stderr, "\n\t\"file,all\" mem_type requires a file system\n"); + fprintf(stderr, "\twith PMD-sized large folio support\n"); fprintf(stderr, "\n\tif [dir] is a (sub)directory of a tmpfs mount, tmpfs must be\n"); fprintf(stderr, "\tmounted with huge=advise option for khugepaged tests to work\n"); fprintf(stderr, "\n\tSupported Options:\n"); @@ -1155,20 +1194,22 @@ static void parse_test_type(int argc, char **argv) usage(); if (!strcmp(buf, "all")) { - file_ops = &__file_ops; + read_only_file_ops = &__read_only_file_ops; + read_write_file_ops = &__read_write_file_ops; anon_ops = &__anon_ops; shmem_ops = &__shmem_ops; } else if (!strcmp(buf, "anon")) { anon_ops = &__anon_ops; } else if (!strcmp(buf, "file")) { - file_ops = &__file_ops; + read_only_file_ops = &__read_only_file_ops; + read_write_file_ops = &__read_write_file_ops; } else if (!strcmp(buf, "shmem")) { shmem_ops = &__shmem_ops; } else { usage(); } - if (!file_ops) + if (!read_only_file_ops && !read_write_file_ops) return; if (argc != 2) @@ -1240,37 +1281,43 @@ int main(int argc, char **argv) } while (0) TEST(collapse_full, khugepaged_context, anon_ops); - TEST(collapse_full, khugepaged_context, file_ops); + TEST(collapse_full, khugepaged_context, read_only_file_ops); + TEST(collapse_full, khugepaged_context, read_write_file_ops); TEST(collapse_full, khugepaged_context, shmem_ops); TEST(collapse_full, madvise_context, anon_ops); - TEST(collapse_full, madvise_context, file_ops); + TEST(collapse_full, madvise_context, read_only_file_ops); + TEST(collapse_full, madvise_context, read_write_file_ops); TEST(collapse_full, madvise_context, shmem_ops); TEST(collapse_empty, khugepaged_context, anon_ops); TEST(collapse_empty, madvise_context, anon_ops); TEST(collapse_single_pte_entry, khugepaged_context, anon_ops); - TEST(collapse_single_pte_entry, khugepaged_context, file_ops); + TEST(collapse_single_pte_entry, khugepaged_context, read_only_file_ops); + TEST(collapse_single_pte_entry, khugepaged_context, read_write_file_ops); TEST(collapse_single_pte_entry, khugepaged_context, shmem_ops); TEST(collapse_single_pte_entry, madvise_context, anon_ops); - TEST(collapse_single_pte_entry, madvise_context, file_ops); + TEST(collapse_single_pte_entry, madvise_context, read_only_file_ops); + TEST(collapse_single_pte_entry, madvise_context, read_write_file_ops); TEST(collapse_single_pte_entry, madvise_context, shmem_ops); TEST(collapse_max_ptes_none, khugepaged_context, anon_ops); - TEST(collapse_max_ptes_none, khugepaged_context, file_ops); + TEST(collapse_max_ptes_none, khugepaged_context, read_only_file_ops); + TEST(collapse_max_ptes_none, khugepaged_context, read_write_file_ops); TEST(collapse_max_ptes_none, madvise_context, anon_ops); - TEST(collapse_max_ptes_none, madvise_context, file_ops); + TEST(collapse_max_ptes_none, madvise_context, read_only_file_ops); + TEST(collapse_max_ptes_none, madvise_context, read_write_file_ops); TEST(collapse_single_pte_entry_compound, khugepaged_context, anon_ops); - TEST(collapse_single_pte_entry_compound, khugepaged_context, file_ops); + TEST(collapse_single_pte_entry_compound, khugepaged_context, read_only_file_ops); TEST(collapse_single_pte_entry_compound, madvise_context, anon_ops); - TEST(collapse_single_pte_entry_compound, madvise_context, file_ops); + TEST(collapse_single_pte_entry_compound, madvise_context, read_only_file_ops); TEST(collapse_full_of_compound, khugepaged_context, anon_ops); - TEST(collapse_full_of_compound, khugepaged_context, file_ops); + TEST(collapse_full_of_compound, khugepaged_context, read_only_file_ops); TEST(collapse_full_of_compound, khugepaged_context, shmem_ops); TEST(collapse_full_of_compound, madvise_context, anon_ops); - TEST(collapse_full_of_compound, madvise_context, file_ops); + TEST(collapse_full_of_compound, madvise_context, read_only_file_ops); TEST(collapse_full_of_compound, madvise_context, shmem_ops); TEST(collapse_compound_extreme, khugepaged_context, anon_ops); @@ -1292,10 +1339,10 @@ int main(int argc, char **argv) TEST(collapse_max_ptes_shared, madvise_context, anon_ops); TEST(madvise_collapse_existing_thps, madvise_context, anon_ops); - TEST(madvise_collapse_existing_thps, madvise_context, file_ops); + TEST(madvise_collapse_existing_thps, madvise_context, read_only_file_ops); TEST(madvise_collapse_existing_thps, madvise_context, shmem_ops); - TEST(madvise_retracted_page_tables, madvise_context, file_ops); + TEST(madvise_retracted_page_tables, madvise_context, read_only_file_ops); TEST(madvise_retracted_page_tables, madvise_context, shmem_ops); restore_settings(0); diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 3b61677fe984..b73921b2cac0 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -490,8 +490,6 @@ CATEGORY="thp" run_test ./khugepaged all:shmem CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem -CATEGORY="thp" run_test ./transhuge-stress -d 20 - # Try to create XFS if not provided if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then if [ "${HAVE_HUGEPAGES}" = "1" ]; then @@ -508,6 +506,14 @@ if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then fi fi +if [ -n "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then +CATEGORY="thp" run_test ./khugepaged all:file ${SPLIT_HUGE_PAGE_TEST_XFS_PATH} +elif test_selected thp; then + count_total=$(( count_total + 1 )) + count_skip=$(( count_skip + 1 )) + echo "[SKIP] ./khugepaged all:file" | tap_prefix +fi + CATEGORY="thp" run_test ./split_huge_page_test ${SPLIT_HUGE_PAGE_TEST_XFS_PATH} if [ -n "${MOUNTED_XFS}" ]; then @@ -516,6 +522,8 @@ if [ -n "${MOUNTED_XFS}" ]; then rm -f ${XFS_IMG} fi +CATEGORY="thp" run_test ./transhuge-stress -d 20 + CATEGORY="thp" run_test ./folio_split_race_test CATEGORY="migration" run_test ./migration From a8b43cadbe32632ce3a1e73a7301dd0dbf92a114 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:14 -0400 Subject: [PATCH 4200/5214] selftests/mm: remove READ_ONLY_THP_FOR_FS code from guard-regions Any file system with large folio support and the supported orders include PMD_ORDER can be used. There is no need to open a file with read-only. Link: https://lore.kernel.org/20260517135416.1434539-13-ziy@nvidia.com Signed-off-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Lance Yang Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/guard-regions.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c index 48e8b1539be3..117639891953 100644 --- a/tools/testing/selftests/mm/guard-regions.c +++ b/tools/testing/selftests/mm/guard-regions.c @@ -2203,17 +2203,6 @@ TEST_F(guard_regions, collapse) if (variant->backing != ANON_BACKED) ASSERT_EQ(ftruncate(self->fd, size), 0); - /* - * We must close and re-open local-file backed as read-only for - * CONFIG_READ_ONLY_THP_FOR_FS to work. - */ - if (variant->backing == LOCAL_FILE_BACKED) { - ASSERT_EQ(close(self->fd), 0); - - self->fd = open(self->path, O_RDONLY); - ASSERT_GE(self->fd, 0); - } - ptr = mmap_(self, variant, NULL, size, PROT_READ, 0, 0); ASSERT_NE(ptr, MAP_FAILED); @@ -2237,9 +2226,10 @@ TEST_F(guard_regions, collapse) /* * Now collapse the entire region. This should fail in all cases. * - * The madvise() call will also fail if CONFIG_READ_ONLY_THP_FOR_FS is - * not set for the local file case, but we can't differentiate whether - * this occurred or if the collapse was rightly rejected. + * The madvise() call will also fail if the file system does not support + * large folio or the supported orders do not include PMD_ORDER for the + * local file case, but we can't differentiate whether this occurred or + * if the collapse was rightly rejected. */ EXPECT_NE(madvise(ptr, size, MADV_COLLAPSE), 0); From 1b6444331bebb4e586dab30b1fa0612ef482bab4 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:15 -0400 Subject: [PATCH 4201/5214] mm/khugepaged: enable clean pagecache folio collapse for writable files collapse_file() is capable of collapsing pagecache folios from writable files to PMD folios. Now enable clean pagecache folio collapse in addition to read-only pagecache folio collapse by removing the inode_is_open_for_write() from file_thp_enabled() and only performing filemap_flush() if the file is read-only. This means userspace needs to explicitly flush the content of pagecache folios before khugepaged can collapse the folios, or use madvise(MADV_COLLAPSE), which does the flush in the retry. The reason is that blindly enabling dirty pagecache folio from writable files collapse makes khugepaged flush these folios all the time. It is undesirable to cause system level pagecache flushes. To properly support dirty pagecache folio collapse, filemap_flush() needs to be avoided. Potentially, merging associated buffer instead of dropping it with filemap_release_folio() might be needed. NOTE: this breaks khugepaged selftests for writable file pagecache collapse, which is set to fail all the time. The next commit fixes it. Link: https://lore.kernel.org/20260517135416.1434539-14-ziy@nvidia.com Signed-off-by: Zi Yan Reviewed-by: Lance Yang Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Hildenbrand (Arm) Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/huge_memory.c | 2 +- mm/khugepaged.c | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 74bdf6ecd0cd..cbc0f0d3bf02 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -100,7 +100,7 @@ static inline bool file_thp_enabled(struct vm_area_struct *vma) if (!mapping_pmd_folio_support(vma->vm_file->f_mapping)) return false; - return !inode_is_open_for_write(inode) && S_ISREG(inode->i_mode); + return S_ISREG(inode->i_mode); } /* If returns true, we are unable to access the VMA's folios. */ diff --git a/mm/khugepaged.c b/mm/khugepaged.c index d9de48423a2e..e69a0fb4c7cc 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2345,18 +2345,21 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, } else if (folio_test_dirty(folio)) { /* * This page is dirty because it hasn't - * been flushed since first write. There - * won't be new dirty pages. + * been flushed since first write. * - * Trigger async flush here and hope the - * writeback is done when khugepaged - * revisits this page. + * Trigger async flush for read-only files and + * hope the writeback is done when khugepaged + * revisits this page. Writable files can have + * their folios dirty at any time; blindly + * flushing them would cause undesirable + * system-wide writeback. * * This is a one-off situation. We are not * forcing writeback in loop. */ xas_unlock_irq(&xas); - filemap_flush(mapping); + if (!inode_is_open_for_write(mapping->host)) + filemap_flush(mapping); result = SCAN_PAGE_DIRTY_OR_WRITEBACK; goto xa_unlocked; } else if (folio_test_writeback(folio)) { From 366aaad86239cac580c922be0e3336d9c38a6f41 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Sun, 17 May 2026 09:54:16 -0400 Subject: [PATCH 4202/5214] selftests/mm: add writable-file collapse tests for khugepaged collapse_file() now supports collapsing clean pagecache folios from writable files, so add corresponding tests. Note that madvise(MADV_COLLAPSE) works for dirty pagecache folios from writable files, because collapse_single_pmd() triggers a synchronous writeback when first attempt of collapse_file() fails. That writeback makes dirty folios clean and the retry of collapse_file() succeeds. Link: https://lore.kernel.org/20260517135416.1434539-15-ziy@nvidia.com Signed-off-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Baolin Wang Cc: Barry Song Cc: Chris Mason Cc: Christian Brauner Cc: David Sterba Cc: Dev Jain Cc: Jan Kara Cc: Lance Yang Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nico Pache Cc: Ryan Roberts Cc: Shuah Khan Cc: Song Liu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/khugepaged.c | 111 ++++++++++++++++++------ 1 file changed, 85 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 7ee6dfe93b3d..9ce0a11b461d 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -41,6 +41,12 @@ enum vma_type { VMA_SHMEM, }; +enum file_setup_ops { + FILE_SETUP_READ_ONLY_FS, + FILE_SETUP_READ_WRITE_FS_READ_DATA, + FILE_SETUP_READ_WRITE_FS_WRITE_DATA, +}; + struct mem_ops { void *(*setup_area)(int nr_hpages); void (*cleanup_area)(void *p, unsigned long size); @@ -50,7 +56,8 @@ struct mem_ops { }; static struct mem_ops *read_only_file_ops; -static struct mem_ops *read_write_file_ops; +static struct mem_ops *read_write_file_read_ops; +static struct mem_ops *read_write_file_write_ops; static struct mem_ops *anon_ops; static struct mem_ops *shmem_ops; @@ -113,7 +120,8 @@ static void restore_settings(int sig) static void save_settings(void) { printf("Save THP and khugepaged settings..."); - if ((read_only_file_ops || read_write_file_ops) && + if ((read_only_file_ops || read_write_file_read_ops || + read_write_file_write_ops) && finfo.type == VMA_FILE) thp_set_read_ahead_path(finfo.dev_queue_read_ahead_path); thp_save_settings(); @@ -366,10 +374,10 @@ static bool anon_check_huge(void *addr, int nr_hpages) return check_huge_anon(addr, nr_hpages, hpage_pmd_size); } -static void *file_setup_area_common(int nr_hpages, bool read_only) +static void *file_setup_area_common(int nr_hpages, enum file_setup_ops setup) { - const int open_opt = read_only ? O_RDONLY : O_RDWR; - const int mmap_prot = read_only ? PROT_READ : (PROT_READ | PROT_WRITE); + const int open_opt = setup == FILE_SETUP_READ_ONLY_FS ? O_RDONLY : O_RDWR; + const int mmap_prot = setup == FILE_SETUP_READ_ONLY_FS ? PROT_READ : (PROT_READ | PROT_WRITE); int fd; void *p; unsigned long size; @@ -405,7 +413,10 @@ static void *file_setup_area_common(int nr_hpages, bool read_only) success("OK"); printf("Opening %s %s for collapse...", finfo.path, - read_only ? "read-only" : "read-write"); + setup == FILE_SETUP_READ_ONLY_FS ? "read-only" : + setup == FILE_SETUP_READ_WRITE_FS_READ_DATA ? + "read-write (read)" : + "read-write (write)"); finfo.fd = open(finfo.path, open_opt, 777); if (finfo.fd < 0) { perror("open()"); @@ -425,12 +436,17 @@ static void *file_setup_area_common(int nr_hpages, bool read_only) static void *file_setup_read_only_area(int nr_hpages) { - return file_setup_area_common(nr_hpages, /* read_only= */ true); + return file_setup_area_common(nr_hpages, FILE_SETUP_READ_ONLY_FS); } -static void *file_setup_read_write_area(int nr_hpages) +static void *file_setup_read_write_fs_read_area(int nr_hpages) { - return file_setup_area_common(nr_hpages, /* read_only= */ false); + return file_setup_area_common(nr_hpages, FILE_SETUP_READ_WRITE_FS_READ_DATA); +} + +static void *file_setup_read_write_fs_write_area(int nr_hpages) +{ + return file_setup_area_common(nr_hpages, FILE_SETUP_READ_WRITE_FS_WRITE_DATA); } static void file_cleanup_area(void *p, unsigned long size) @@ -448,6 +464,16 @@ static void file_fault_read(void *p, unsigned long start, unsigned long end) } } +static void file_fault_read_and_flush(void *p, unsigned long start, unsigned long end) +{ + file_fault_read(p, start, end); + /* + * make folio clean, since dirty folios from read&write file are + * rejected and not flushed + */ + msync((char *)p + start, end - start, MS_SYNC); +} + static void file_fault_write(void *p, unsigned long start, unsigned long end) { if (madvise(((char *)p) + start, end - start, MADV_POPULATE_WRITE)) { @@ -519,8 +545,16 @@ static struct mem_ops __read_only_file_ops = { .name = "file", }; -static struct mem_ops __read_write_file_ops = { - .setup_area = &file_setup_read_write_area, +static struct mem_ops __read_write_file_read_ops = { + .setup_area = &file_setup_read_write_fs_read_area, + .cleanup_area = &file_cleanup_area, + .fault = &file_fault_read_and_flush, + .check_huge = &file_check_huge, + .name = "file", +}; + +static struct mem_ops __read_write_file_write_ops = { + .setup_area = &file_setup_read_write_fs_write_area, .cleanup_area = &file_cleanup_area, .fault = &file_fault_write, .check_huge = &file_check_huge, @@ -537,7 +571,9 @@ static struct mem_ops __shmem_ops = { static bool is_tmpfs(struct mem_ops *ops) { - return (ops == &__read_only_file_ops || ops == &__read_write_file_ops) && + return (ops == &__read_only_file_ops || + ops == &__read_write_file_read_ops || + ops == &__read_write_file_write_ops) && finfo.type == VMA_SHMEM; } @@ -554,9 +590,11 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages, printf("%s...", msg); - /* read&write file collapse always fail */ - if (!is_tmpfs(ops) && ops == &__read_write_file_ops) - expect = false; + /* + * read&write file collapse succeeds for MADV_COLLAPSE because dirty + * folios are written back after collapse fails for dirty folios and + * another collapse is attempted. + */ /* * Prevent khugepaged interference and tests that MADV_COLLAPSE @@ -624,8 +662,11 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages, static void khugepaged_collapse(const char *msg, char *p, int nr_hpages, struct mem_ops *ops, bool expect) { - /* read&write file collapse always fail */ - if (!is_tmpfs(ops) && ops == &__read_write_file_ops) + /* + * read&write file collapse fails since khugepaged does not flush + * the target dirty folios + */ + if (!is_tmpfs(ops) && ops == &__read_write_file_write_ops) expect = false; if (wait_for_scan(msg, p, nr_hpages, ops)) { @@ -748,6 +789,9 @@ static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *o validate_memory(p, 0, (hpage_pmd_nr - max_ptes_none - fault_nr_pages) * page_size); if (c->enforce_pte_scan_limits) { + ops->cleanup_area(p, hpage_pmd_size); + p = ops->setup_area(1); + ops->fault(p, 0, (hpage_pmd_nr - max_ptes_none) * page_size); c->collapse("Collapse with max_ptes_none PTEs empty", p, 1, ops, true); @@ -1195,21 +1239,24 @@ static void parse_test_type(int argc, char **argv) if (!strcmp(buf, "all")) { read_only_file_ops = &__read_only_file_ops; - read_write_file_ops = &__read_write_file_ops; + read_write_file_read_ops = &__read_write_file_read_ops; + read_write_file_write_ops = &__read_write_file_write_ops; anon_ops = &__anon_ops; shmem_ops = &__shmem_ops; } else if (!strcmp(buf, "anon")) { anon_ops = &__anon_ops; } else if (!strcmp(buf, "file")) { read_only_file_ops = &__read_only_file_ops; - read_write_file_ops = &__read_write_file_ops; + read_write_file_read_ops = &__read_write_file_read_ops; + read_write_file_write_ops = &__read_write_file_write_ops; } else if (!strcmp(buf, "shmem")) { shmem_ops = &__shmem_ops; } else { usage(); } - if (!read_only_file_ops && !read_write_file_ops) + if (!read_only_file_ops && !read_write_file_read_ops && + !read_write_file_write_ops) return; if (argc != 2) @@ -1282,11 +1329,13 @@ int main(int argc, char **argv) TEST(collapse_full, khugepaged_context, anon_ops); TEST(collapse_full, khugepaged_context, read_only_file_ops); - TEST(collapse_full, khugepaged_context, read_write_file_ops); + TEST(collapse_full, khugepaged_context, read_write_file_read_ops); + TEST(collapse_full, khugepaged_context, read_write_file_write_ops); TEST(collapse_full, khugepaged_context, shmem_ops); TEST(collapse_full, madvise_context, anon_ops); TEST(collapse_full, madvise_context, read_only_file_ops); - TEST(collapse_full, madvise_context, read_write_file_ops); + TEST(collapse_full, madvise_context, read_write_file_read_ops); + TEST(collapse_full, madvise_context, read_write_file_write_ops); TEST(collapse_full, madvise_context, shmem_ops); TEST(collapse_empty, khugepaged_context, anon_ops); @@ -1294,30 +1343,38 @@ int main(int argc, char **argv) TEST(collapse_single_pte_entry, khugepaged_context, anon_ops); TEST(collapse_single_pte_entry, khugepaged_context, read_only_file_ops); - TEST(collapse_single_pte_entry, khugepaged_context, read_write_file_ops); + TEST(collapse_single_pte_entry, khugepaged_context, read_write_file_read_ops); + TEST(collapse_single_pte_entry, khugepaged_context, read_write_file_write_ops); TEST(collapse_single_pte_entry, khugepaged_context, shmem_ops); TEST(collapse_single_pte_entry, madvise_context, anon_ops); TEST(collapse_single_pte_entry, madvise_context, read_only_file_ops); - TEST(collapse_single_pte_entry, madvise_context, read_write_file_ops); + TEST(collapse_single_pte_entry, madvise_context, read_write_file_read_ops); + TEST(collapse_single_pte_entry, madvise_context, read_write_file_write_ops); TEST(collapse_single_pte_entry, madvise_context, shmem_ops); TEST(collapse_max_ptes_none, khugepaged_context, anon_ops); TEST(collapse_max_ptes_none, khugepaged_context, read_only_file_ops); - TEST(collapse_max_ptes_none, khugepaged_context, read_write_file_ops); + TEST(collapse_max_ptes_none, khugepaged_context, read_write_file_read_ops); + TEST(collapse_max_ptes_none, khugepaged_context, read_write_file_write_ops); TEST(collapse_max_ptes_none, madvise_context, anon_ops); TEST(collapse_max_ptes_none, madvise_context, read_only_file_ops); - TEST(collapse_max_ptes_none, madvise_context, read_write_file_ops); + TEST(collapse_max_ptes_none, madvise_context, read_write_file_read_ops); + TEST(collapse_max_ptes_none, madvise_context, read_write_file_write_ops); TEST(collapse_single_pte_entry_compound, khugepaged_context, anon_ops); TEST(collapse_single_pte_entry_compound, khugepaged_context, read_only_file_ops); + TEST(collapse_single_pte_entry_compound, khugepaged_context, read_write_file_read_ops); TEST(collapse_single_pte_entry_compound, madvise_context, anon_ops); TEST(collapse_single_pte_entry_compound, madvise_context, read_only_file_ops); + TEST(collapse_single_pte_entry_compound, madvise_context, read_write_file_read_ops); TEST(collapse_full_of_compound, khugepaged_context, anon_ops); TEST(collapse_full_of_compound, khugepaged_context, read_only_file_ops); + TEST(collapse_full_of_compound, khugepaged_context, read_write_file_read_ops); TEST(collapse_full_of_compound, khugepaged_context, shmem_ops); TEST(collapse_full_of_compound, madvise_context, anon_ops); TEST(collapse_full_of_compound, madvise_context, read_only_file_ops); + TEST(collapse_full_of_compound, madvise_context, read_write_file_read_ops); TEST(collapse_full_of_compound, madvise_context, shmem_ops); TEST(collapse_compound_extreme, khugepaged_context, anon_ops); @@ -1340,9 +1397,11 @@ int main(int argc, char **argv) TEST(madvise_collapse_existing_thps, madvise_context, anon_ops); TEST(madvise_collapse_existing_thps, madvise_context, read_only_file_ops); + TEST(madvise_collapse_existing_thps, madvise_context, read_write_file_read_ops); TEST(madvise_collapse_existing_thps, madvise_context, shmem_ops); TEST(madvise_retracted_page_tables, madvise_context, read_only_file_ops); + TEST(madvise_retracted_page_tables, madvise_context, read_write_file_read_ops); TEST(madvise_retracted_page_tables, madvise_context, shmem_ops); restore_settings(0); From 79f33d19fce70bc8a3d5f16dfe24d28e789ce931 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:45 +0300 Subject: [PATCH 4203/5214] selftests/mm: hugetlb-read-hwpoison: add SIGBUS handler Patch series "make MM selftests more CI friendly", v4. There's a lot of dancing around HugeTLB settings in run_vmtests.sh. Some test need just a few default huge pages, some require at least 256 MB, and some just skip lots of tests if huge pages of all supported sizes are not available. The goal of this set is to make tests deal with HugeTLB setup and teardown. There are already convenient helpers that allow easy reading and writing of /proc and /sysfs, so adding a few APIs that will detect and update HugeTLB settings shouldn't be a big deal. But these nice helpers use kselftest framework, and many of HugeTLB (and even THP) test don't, so as a result this patchset also includes a lot of churn for conversion of those tests to kselftest framework (patches 7-19). The series break out: patches 1-5: small fixes patch 6: merge of hugetlb mmap tests patch 7: renaming of hugepage-* to hugetlb-* patches 8-21: mechanical conversion to kselftest framework patches 22-28: extension of thp_settings to hugepage_settings to also include HugeTLB helpers patches 29-30: add helpers for setting up SHM limits in hugetlb-shm and thuge-gen tests patches 31-53: integrate the new APIs in all the tests that use HugeTLB patches 54-55: drop HugeTLB setup from run_vmtests.sh This patch (of 55): Injection of a memory error with madvise() causes SIGBUS, which terminates the hugetlb-read-hwpoison test prematurely. Add a dummy SIGBUS handler to allow the test to continue regardless of SIGBUS. Link: https://lore.kernel.org/20260511162840.375890-1-rppt@kernel.org Link: https://lore.kernel.org/20260511162840.375890-2-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Sarthak Sharma Tested-by: Li Wang Reviewed-by: Li Wang Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-read-hwpoison.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/mm/hugetlb-read-hwpoison.c b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c index 46230462ad48..ad3198b444ce 100644 --- a/tools/testing/selftests/mm/hugetlb-read-hwpoison.c +++ b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "kselftest.h" @@ -261,6 +262,10 @@ static int create_hugetlbfs_file(struct statfs *file_stat) return -1; } +static void sigbus_handler(int sig) +{ +} + int main(void) { int fd; @@ -273,6 +278,7 @@ int main(void) }; size_t i; + signal(SIGBUS, sigbus_handler); for (i = 0; i < ARRAY_SIZE(wr_chunk_sizes); ++i) { printf("Write/read chunk size=0x%lx\n", wr_chunk_sizes[i]); From 4d7f4bf5109822f231b5e40b54be4b3c690647cb Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:46 +0300 Subject: [PATCH 4204/5214] selftests/mm: migration: don't assume huge page is TWOMEG migration tests presume that both THP and HugeTLB huge pages are 2MB. Add dynamic detection of huge page size with read_pmd_pagesize() for THP and with default_huge_page_size() for HugeTLB. Link: https://lore.kernel.org/20260511162840.375890-3-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/migration.c | 44 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 60e78bbfc0e3..0f5a4ddac529 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -184,22 +184,27 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) */ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) { + uint64_t pmdsize; uint64_t *ptr; int i; if (!thp_is_enabled()) SKIP(return, "Transparent Hugepages not available"); + pmdsize = read_pmd_pagesize(); + if (!pmdsize) + SKIP(return, "Reading PMD pagesize failed"); + if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE, + ptr = mmap(NULL, 2 * pmdsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); - ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG); - ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0); - memset(ptr, 0xde, TWOMEG); + ptr = (uint64_t *) ALIGN((uintptr_t) ptr, pmdsize); + ASSERT_EQ(madvise(ptr, pmdsize, MADV_HUGEPAGE), 0); + memset(ptr, 0xde, pmdsize); for (i = 0; i < self->nthreads - 1; i++) if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) perror("Couldn't create thread"); @@ -215,6 +220,7 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) { + uint64_t pmdsize; pid_t pid; uint64_t *ptr; int i; @@ -222,17 +228,21 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) if (!thp_is_enabled()) SKIP(return, "Transparent Hugepages not available"); + pmdsize = read_pmd_pagesize(); + if (!pmdsize) + SKIP(return, "Reading PMD pagesize failed"); + if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, 2 * TWOMEG, PROT_READ | PROT_WRITE, + ptr = mmap(NULL, 2 * pmdsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); - ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG); - ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0); + ptr = (uint64_t *) ALIGN((uintptr_t) ptr, pmdsize); + ASSERT_EQ(madvise(ptr, pmdsize, MADV_HUGEPAGE), 0); - memset(ptr, 0xde, TWOMEG); + memset(ptr, 0xde, pmdsize); for (i = 0; i < self->nthreads - 1; i++) { pid = fork(); if (!pid) { @@ -256,17 +266,22 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) */ TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME) { + unsigned long hugepage_size; uint64_t *ptr; int i; if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, + hugepage_size = default_huge_page_size(); + if (!hugepage_size) + SKIP(return, "Reading HugeTLB pagesize failed"); + + ptr = mmap(NULL, hugepage_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); ASSERT_NE(ptr, MAP_FAILED); - memset(ptr, 0xde, TWOMEG); + memset(ptr, 0xde, hugepage_size); for (i = 0; i < self->nthreads - 1; i++) if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) perror("Couldn't create thread"); @@ -281,6 +296,7 @@ TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME) */ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) { + unsigned long hugepage_size; pid_t pid; uint64_t *ptr; int i; @@ -288,11 +304,15 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, + hugepage_size = default_huge_page_size(); + if (!hugepage_size) + SKIP(return, "Reading HugeTLB pagesize failed"); + + ptr = mmap(NULL, hugepage_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); ASSERT_NE(ptr, MAP_FAILED); - memset(ptr, 0xde, TWOMEG); + memset(ptr, 0xde, hugepage_size); for (i = 0; i < self->nthreads - 1; i++) { pid = fork(); if (!pid) { From 3e600b2564ad9c106e4698e6dc8ae50cd942400a Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:47 +0300 Subject: [PATCH 4205/5214] selftests/mm: migration: make nthreads represent number of working threads Fixture setup sets self->nthreads to number of available CPUs minus 1 and then each test creates 'self->nthreads - 1' threads or processes, so essentially nthreads counts the worker tasks and the main task. Make nthreads represent the number of spawned tasks to simplify thread/process creation and teardown. While on it, make the fixture setup skip the tests if there are not enough CPUs or NUMA nodes instead of checking this in each test. Link: https://lore.kernel.org/20260511162840.375890-4-rppt@kernel.org Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Signed-off-by: Mike Rapoport (Microsoft) Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/migration.c | 47 +++++++++----------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 0f5a4ddac529..16ffd3c55ee0 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -38,7 +38,7 @@ FIXTURE_SETUP(migration) if (numa_available() < 0) SKIP(return, "NUMA not available"); - self->nthreads = numa_num_task_cpus() - 1; + self->nthreads = numa_num_task_cpus() - 2; self->n1 = -1; self->n2 = -1; @@ -52,6 +52,9 @@ FIXTURE_SETUP(migration) } } + if (self->nthreads < 1 || self->n1 < 0 || self->n2 < 0) + SKIP(return, "Not enough threads or NUMA nodes available"); + self->threads = malloc(self->nthreads * sizeof(*self->threads)); ASSERT_NE(self->threads, NULL); self->pids = malloc(self->nthreads * sizeof(*self->pids)); @@ -127,20 +130,17 @@ TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME) uint64_t *ptr; int i; - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); memset(ptr, 0xde, TWOMEG); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) perror("Couldn't create thread"); ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(pthread_cancel(self->threads[i]), 0); } @@ -153,15 +153,12 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) uint64_t *ptr; int i; - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); memset(ptr, 0xde, TWOMEG); - for (i = 0; i < self->nthreads - 1; i++) { + for (i = 0; i < self->nthreads; i++) { pid = fork(); if (!pid) { prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -175,7 +172,7 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) } ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); } @@ -195,9 +192,6 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) if (!pmdsize) SKIP(return, "Reading PMD pagesize failed"); - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, 2 * pmdsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); @@ -205,12 +199,12 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME) ptr = (uint64_t *) ALIGN((uintptr_t) ptr, pmdsize); ASSERT_EQ(madvise(ptr, pmdsize, MADV_HUGEPAGE), 0); memset(ptr, 0xde, pmdsize); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) perror("Couldn't create thread"); ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(pthread_cancel(self->threads[i]), 0); } @@ -232,9 +226,6 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) if (!pmdsize) SKIP(return, "Reading PMD pagesize failed"); - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - ptr = mmap(NULL, 2 * pmdsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ASSERT_NE(ptr, MAP_FAILED); @@ -243,7 +234,7 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) ASSERT_EQ(madvise(ptr, pmdsize, MADV_HUGEPAGE), 0); memset(ptr, 0xde, pmdsize); - for (i = 0; i < self->nthreads - 1; i++) { + for (i = 0; i < self->nthreads; i++) { pid = fork(); if (!pid) { prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -257,7 +248,7 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) } ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); } @@ -270,9 +261,6 @@ TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME) uint64_t *ptr; int i; - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - hugepage_size = default_huge_page_size(); if (!hugepage_size) SKIP(return, "Reading HugeTLB pagesize failed"); @@ -282,12 +270,12 @@ TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME) ASSERT_NE(ptr, MAP_FAILED); memset(ptr, 0xde, hugepage_size); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) if (pthread_create(&self->threads[i], NULL, access_mem, ptr)) perror("Couldn't create thread"); ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(pthread_cancel(self->threads[i]), 0); } @@ -301,9 +289,6 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) uint64_t *ptr; int i; - if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0) - SKIP(return, "Not enough threads or NUMA nodes available"); - hugepage_size = default_huge_page_size(); if (!hugepage_size) SKIP(return, "Reading HugeTLB pagesize failed"); @@ -313,7 +298,7 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) ASSERT_NE(ptr, MAP_FAILED); memset(ptr, 0xde, hugepage_size); - for (i = 0; i < self->nthreads - 1; i++) { + for (i = 0; i < self->nthreads; i++) { pid = fork(); if (!pid) { prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -327,7 +312,7 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) } ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads - 1; i++) + for (i = 0; i < self->nthreads; i++) ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); } From 5ea01affb6546294d4d5af954c397b808267357c Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:48 +0300 Subject: [PATCH 4206/5214] selftests/mm: migration: properly cleanup fork()ed processes Several migration test use fork() to create worker processes. These processes are later killed, but nothing collects their exit status and they remain as zombies in the system. Add a helper function that kills the worker processes, waitpid()s for them and verifies the exit status. Replace the loops that call kill() for each process with a call to that helper. Link: https://lore.kernel.org/20260511162840.375890-5-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reported-by: Luiz Capitulino Tested-by: Luiz Capitulino Reviewed-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/migration.c | 47 +++++++++++++++++++------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 16ffd3c55ee0..e504829df6b6 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -67,6 +67,29 @@ FIXTURE_TEARDOWN(migration) free(self->pids); } +static bool kill_children(FIXTURE_DATA(migration) * self) +{ + bool err = false; + pid_t pid; + int i; + + for (i = 0; i < self->nthreads; i++) { + int status = 0; + + pid = self->pids[i]; + if (pid < 0) + continue; + if (kill(pid, SIGTERM)) + err = true; + if (pid != waitpid(pid, &status, 0)) + err = true; + if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGTERM) + err = true; + } + + return !err; +} + int migrate(uint64_t *ptr, int n1, int n2) { int ret, tmp; @@ -151,7 +174,7 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) { pid_t pid; uint64_t *ptr; - int i; + int i, err; ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); @@ -171,9 +194,9 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) } } - ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads; i++) - ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); + err = migrate(ptr, self->n1, self->n2); + ASSERT_EQ(kill_children(self), true); + ASSERT_EQ(err, 0); } /* @@ -217,7 +240,7 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) uint64_t pmdsize; pid_t pid; uint64_t *ptr; - int i; + int i, err; if (!thp_is_enabled()) SKIP(return, "Transparent Hugepages not available"); @@ -247,9 +270,9 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME) } } - ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads; i++) - ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); + err = migrate(ptr, self->n1, self->n2); + ASSERT_EQ(kill_children(self), true); + ASSERT_EQ(err, 0); } /* @@ -287,7 +310,7 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) unsigned long hugepage_size; pid_t pid; uint64_t *ptr; - int i; + int i, err; hugepage_size = default_huge_page_size(); if (!hugepage_size) @@ -311,9 +334,9 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) } } - ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); - for (i = 0; i < self->nthreads; i++) - ASSERT_EQ(kill(self->pids[i], SIGTERM), 0); + err = migrate(ptr, self->n1, self->n2); + ASSERT_EQ(kill_children(self), true); + ASSERT_EQ(err, 0); } TEST_HARNESS_MAIN From 21de457215049aac93a8b0da1bba71c1a59d8be5 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:49 +0300 Subject: [PATCH 4207/5214] selftests/mm: run_vmtests.sh: don't gate THP and KSM tests on HAVE_HUGEPAGES HAVE_HUGEPAGES indicates availability of free HugeTLB pages. It should not be used to gate KSM test that merges transparent huge pages or split_huge_page_test. Remove check for HAVE_HUGEPAGES when running these tests. Link: https://lore.kernel.org/20260511162840.375890-6-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Li Wang Reviewed-by: Li Wang Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/run_vmtests.sh | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index b73921b2cac0..61a01d2bc220 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -437,9 +437,7 @@ CATEGORY="memfd_secret" run_test ./memfd_secret fi # KSM KSM_MERGE_TIME_HUGE_PAGES test with size of 100 -if [ "${HAVE_HUGEPAGES}" = "1" ]; then - CATEGORY="ksm" run_test ./ksm_tests -H -s 100 -fi +CATEGORY="ksm" run_test ./ksm_tests -H -s 100 # KSM KSM_MERGE_TIME test with size of 100 CATEGORY="ksm" run_test ./ksm_tests -P -s 100 # KSM MADV_MERGEABLE test with 10 identical pages @@ -492,16 +490,14 @@ CATEGORY="thp" run_test ./khugepaged -s 4 all:shmem # Try to create XFS if not provided if [ -z "${SPLIT_HUGE_PAGE_TEST_XFS_PATH}" ]; then - if [ "${HAVE_HUGEPAGES}" = "1" ]; then - if test_selected "thp"; then - if grep xfs /proc/filesystems &>/dev/null; then - XFS_IMG=$(mktemp /tmp/xfs_img_XXXXXX) - SPLIT_HUGE_PAGE_TEST_XFS_PATH=$(mktemp -d /tmp/xfs_dir_XXXXXX) - truncate -s 314572800 ${XFS_IMG} - mkfs.xfs -q ${XFS_IMG} - mount -o loop ${XFS_IMG} ${SPLIT_HUGE_PAGE_TEST_XFS_PATH} - MOUNTED_XFS=1 - fi + if test_selected "thp"; then + if grep xfs /proc/filesystems &>/dev/null; then + XFS_IMG=$(mktemp /tmp/xfs_img_XXXXXX) + SPLIT_HUGE_PAGE_TEST_XFS_PATH=$(mktemp -d /tmp/xfs_dir_XXXXXX) + truncate -s 314572800 ${XFS_IMG} + mkfs.xfs -q ${XFS_IMG} + mount -o loop ${XFS_IMG} ${SPLIT_HUGE_PAGE_TEST_XFS_PATH} + MOUNTED_XFS=1 fi fi fi From 9c5a65f374f86ed7105f064c89f74de8eb0c5f2f Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:50 +0300 Subject: [PATCH 4208/5214] selftests/mm: merge map_hugetlb into hugepage-mmap Both tests create a hugettlb mapping, fill it with data and verify the data, the only difference is that one uses file-backed memory and another one uses anonymous memory. Merge both tests into a single file. Link: https://lore.kernel.org/20260511162840.375890-7-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Reviewed-by: Donet Tom Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/hugetlbpage.rst | 7 +- tools/testing/selftests/mm/Makefile | 1 - tools/testing/selftests/mm/hugepage-mmap.c | 113 ++++++++++++++----- tools/testing/selftests/mm/map_hugetlb.c | 88 --------------- tools/testing/selftests/mm/run_vmtests.sh | 1 - 5 files changed, 87 insertions(+), 123 deletions(-) delete mode 100644 tools/testing/selftests/mm/map_hugetlb.c diff --git a/Documentation/admin-guide/mm/hugetlbpage.rst b/Documentation/admin-guide/mm/hugetlbpage.rst index 67a941903fd2..2dea8c636641 100644 --- a/Documentation/admin-guide/mm/hugetlbpage.rst +++ b/Documentation/admin-guide/mm/hugetlbpage.rst @@ -455,7 +455,7 @@ used to change the file attributes on hugetlbfs. Also, it is important to note that no such mount command is required if applications are going to use only shmat/shmget system calls or mmap with MAP_HUGETLB. For an example of how to use mmap with MAP_HUGETLB see -:ref:`map_hugetlb ` below. +:ref:`examples ` below. Users who wish to use hugetlb memory via shared memory segment should be members of a supplementary group and system admin needs to configure that gid @@ -473,10 +473,7 @@ a hugetlb page and the length is smaller than the hugepage size. Examples ======== -.. _map_hugetlb: - -``map_hugetlb`` - see tools/testing/selftests/mm/map_hugetlb.c +.. _examples: ``hugepage-shm`` see tools/testing/selftests/mm/hugepage-shm.c diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 41053fdaad88..7774f629d0b9 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -70,7 +70,6 @@ TEST_GEN_FILES += hugepage-vmemmap TEST_GEN_FILES += khugepaged TEST_GEN_FILES += madv_populate TEST_GEN_FILES += map_fixed_noreplace -TEST_GEN_FILES += map_hugetlb TEST_GEN_FILES += map_populate ifneq (,$(filter $(ARCH),arm64 riscv riscv64 x86 x86_64 loongarch32 loongarch64)) TEST_GEN_FILES += memfd_secret diff --git a/tools/testing/selftests/mm/hugepage-mmap.c b/tools/testing/selftests/mm/hugepage-mmap.c index d543419de040..66cf74b73dea 100644 --- a/tools/testing/selftests/mm/hugepage-mmap.c +++ b/tools/testing/selftests/mm/hugepage-mmap.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include "vm_util.h" #include "kselftest.h" #define LENGTH (256UL*1024*1024) @@ -25,54 +27,109 @@ static void check_bytes(char *addr) ksft_print_msg("First hex is %x\n", *((unsigned int *)addr)); } -static void write_bytes(char *addr) +static void write_bytes(char *addr, size_t length) { unsigned long i; - for (i = 0; i < LENGTH; i++) + for (i = 0; i < length; i++) *(addr + i) = (char)i; } -static int read_bytes(char *addr) +static bool verify_bytes(char *addr, size_t length) { unsigned long i; check_bytes(addr); - for (i = 0; i < LENGTH; i++) + for (i = 0; i < length; i++) if (*(addr + i) != (char)i) { - ksft_print_msg("Error: Mismatch at %lu\n", i); - return 1; + ksft_print_msg("Error: Mismatch at %lu(%p)\n", i, addr); + return false; } - return 0; + + return true; } -int main(void) +static void test_mmap(size_t length, int mmap_flags, int fd, + const char *test_name) { + bool passed = true; void *addr; - int fd, ret; - ksft_print_header(); - ksft_set_plan(1); - - fd = memfd_create("hugepage-mmap", MFD_HUGETLB); - if (fd < 0) - ksft_exit_fail_msg("memfd_create() failed: %s\n", strerror(errno)); - - addr = mmap(NULL, LENGTH, PROTECTION, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - close(fd); - ksft_exit_fail_msg("mmap(): %s\n", strerror(errno)); - } + addr = mmap(NULL, length, PROTECTION, mmap_flags, fd, 0); + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); ksft_print_msg("Returned address is %p\n", addr); check_bytes(addr); - write_bytes(addr); - ret = read_bytes(addr); + write_bytes(addr, length); + if (!verify_bytes(addr, length)) + passed = false; - munmap(addr, LENGTH); - close(fd); + /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ + if (munmap(addr, length)) + ksft_exit_fail_perror("munmap"); - ksft_test_result(!ret, "Read same data\n"); - - ksft_exit(!ret); + ksft_test_result(passed, "%s\n", test_name); +} + +static void test_anon_mmap(size_t length, int shift) +{ + const char *test_name = "hugetlb anonymous mmap"; + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; + + if (shift) + mmap_flags |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + + test_mmap(length, mmap_flags, -1, test_name); +} + +static void test_file_mmap(size_t length, int shift) +{ + const char *test_name = "hugetlb file mmap"; + int mfd_flags = MFD_HUGETLB; + int fd; + + if (shift) + mfd_flags |= (shift & MFD_HUGE_MASK) << MFD_HUGE_SHIFT; + + fd = memfd_create("hugetlb-mmap", mfd_flags); + if (fd < 0) + ksft_exit_fail_perror("memfd_create"); + + test_mmap(length, MAP_SHARED, fd, test_name); + close(fd); +} + +int main(int argc, char **argv) +{ + size_t hugepage_size; + size_t length = LENGTH; + int shift = 0; + + ksft_print_header(); + ksft_set_plan(2); + + if (argc > 1) + length = atol(argv[1]) << 20; + if (argc > 2) + shift = atoi(argv[2]); + + if (shift) { + hugepage_size = (1UL << shift); + ksft_print_msg("%lu kB hugepages\n", 1UL << (shift - 10)); + } else { + hugepage_size = default_huge_page_size(); + ksft_print_msg("Default size hugepages (%lu kB)\n", hugepage_size >> 10); + } + + /* munmap will fail if the length is not page aligned */ + if (hugepage_size > length) + length = hugepage_size; + + ksft_print_msg("Mapping %lu Mbytes\n", (unsigned long)length >> 20); + + test_anon_mmap(length, shift); + test_file_mmap(length, shift); + + ksft_finished(); } diff --git a/tools/testing/selftests/mm/map_hugetlb.c b/tools/testing/selftests/mm/map_hugetlb.c deleted file mode 100644 index aa409107611b..000000000000 --- a/tools/testing/selftests/mm/map_hugetlb.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Example of using hugepage memory in a user application using the mmap - * system call with MAP_HUGETLB flag. Before running this program make - * sure the administrator has allocated enough default sized huge pages - * to cover the 256 MB allocation. - */ -#include -#include -#include -#include -#include -#include "vm_util.h" -#include "kselftest.h" - -#define LENGTH (256UL*1024*1024) -#define PROTECTION (PROT_READ | PROT_WRITE) - -static void check_bytes(char *addr) -{ - ksft_print_msg("First hex is %x\n", *((unsigned int *)addr)); -} - -static void write_bytes(char *addr, size_t length) -{ - unsigned long i; - - for (i = 0; i < length; i++) - *(addr + i) = (char)i; -} - -static void read_bytes(char *addr, size_t length) -{ - unsigned long i; - - check_bytes(addr); - for (i = 0; i < length; i++) - if (*(addr + i) != (char)i) - ksft_exit_fail_msg("Mismatch at %lu\n", i); - - ksft_test_result_pass("Read correct data\n"); -} - -int main(int argc, char **argv) -{ - void *addr; - size_t hugepage_size; - size_t length = LENGTH; - int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; - int shift = 0; - - hugepage_size = default_huge_page_size(); - /* munmap with fail if the length is not page aligned */ - if (hugepage_size > length) - length = hugepage_size; - - ksft_print_header(); - ksft_set_plan(1); - - if (argc > 1) - length = atol(argv[1]) << 20; - if (argc > 2) { - shift = atoi(argv[2]); - if (shift) - flags |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; - } - - if (shift) - ksft_print_msg("%u kB hugepages\n", 1 << (shift - 10)); - else - ksft_print_msg("Default size hugepages\n"); - ksft_print_msg("Mapping %lu Mbytes\n", (unsigned long)length >> 20); - - addr = mmap(NULL, length, PROTECTION, flags, -1, 0); - if (addr == MAP_FAILED) - ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); - - ksft_print_msg("Returned address is %p\n", addr); - check_bytes(addr); - write_bytes(addr, length); - read_bytes(addr, length); - - /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ - if (munmap(addr, length)) - ksft_exit_fail_msg("munmap: %s\n", strerror(errno)); - - ksft_finished(); -} diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 61a01d2bc220..886a3b640bae 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -292,7 +292,6 @@ CATEGORY="hugetlb" run_test ./hugepage-shm echo "$shmmax" > /proc/sys/kernel/shmmax echo "$shmall" > /proc/sys/kernel/shmall -CATEGORY="hugetlb" run_test ./map_hugetlb CATEGORY="hugetlb" run_test ./hugepage-mremap CATEGORY="hugetlb" run_test ./hugepage-vmemmap CATEGORY="hugetlb" run_test ./hugetlb-madvise From a93cbee5275104d51d30a7d392d10fae2b58bfc0 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:51 +0300 Subject: [PATCH 4209/5214] selftests/mm: rename hugepage-* tests to hugetlb-* hugepage could mean both THP and HugeTLB these days. Rename hugepage-* tests for HugeTLB to hugetlb-* to avoid confusion. Make sure that Makefile update keeps alphabetical ordering of the TEST_GEN_FILES entries. Keep old binary names in .gitignore because Linus prefers it this way. Link: https://lore.kernel.org/20260511162840.375890-8-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Li Wang Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/hugetlbpage.rst | 8 ++++---- tools/testing/selftests/mm/.gitignore | 4 ++++ tools/testing/selftests/mm/Makefile | 8 ++++---- tools/testing/selftests/mm/hugetlb-madvise.c | 2 +- .../selftests/mm/{hugepage-mmap.c => hugetlb-mmap.c} | 2 +- .../selftests/mm/{hugepage-mremap.c => hugetlb-mremap.c} | 2 +- .../selftests/mm/{hugepage-shm.c => hugetlb-shm.c} | 2 +- .../mm/{hugepage-vmemmap.c => hugetlb-vmemmap.c} | 0 tools/testing/selftests/mm/run_vmtests.sh | 8 ++++---- 9 files changed, 20 insertions(+), 16 deletions(-) rename tools/testing/selftests/mm/{hugepage-mmap.c => hugetlb-mmap.c} (99%) rename tools/testing/selftests/mm/{hugepage-mremap.c => hugetlb-mremap.c} (99%) rename tools/testing/selftests/mm/{hugepage-shm.c => hugetlb-shm.c} (99%) rename tools/testing/selftests/mm/{hugepage-vmemmap.c => hugetlb-vmemmap.c} (100%) diff --git a/Documentation/admin-guide/mm/hugetlbpage.rst b/Documentation/admin-guide/mm/hugetlbpage.rst index 2dea8c636641..3cc15d800be1 100644 --- a/Documentation/admin-guide/mm/hugetlbpage.rst +++ b/Documentation/admin-guide/mm/hugetlbpage.rst @@ -475,11 +475,11 @@ Examples .. _examples: -``hugepage-shm`` - see tools/testing/selftests/mm/hugepage-shm.c +``hugetlb-shm`` + see tools/testing/selftests/mm/hugetlb-shm.c -``hugepage-mmap`` - see tools/testing/selftests/mm/hugepage-mmap.c +``hugetlb-mmap`` + see tools/testing/selftests/mm/hugetlb-mmap.c The `libhugetlbfs`_ library provides a wide range of userspace tools to help with huge page usability, environment setup, and control. diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index b0c30c5ee9e3..9ccd9e1447e6 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -4,6 +4,10 @@ hugepage-mmap hugepage-mremap hugepage-shm hugepage-vmemmap +hugetlb-mmap +hugetlb-mremap +hugetlb-shm +hugetlb-vmemmap hugetlb-madvise hugetlb-read-hwpoison hugetlb-soft-offline diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 7774f629d0b9..b5d582febae0 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -61,12 +61,12 @@ TEST_GEN_FILES += gup_longterm TEST_GEN_FILES += gup_test TEST_GEN_FILES += hmm-tests TEST_GEN_FILES += hugetlb-madvise +TEST_GEN_FILES += hugetlb-mmap +TEST_GEN_FILES += hugetlb-mremap TEST_GEN_FILES += hugetlb-read-hwpoison +TEST_GEN_FILES += hugetlb-shm TEST_GEN_FILES += hugetlb-soft-offline -TEST_GEN_FILES += hugepage-mmap -TEST_GEN_FILES += hugepage-mremap -TEST_GEN_FILES += hugepage-shm -TEST_GEN_FILES += hugepage-vmemmap +TEST_GEN_FILES += hugetlb-vmemmap TEST_GEN_FILES += khugepaged TEST_GEN_FILES += madv_populate TEST_GEN_FILES += map_fixed_noreplace diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index 5b12041fa310..898cc90b314f 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * hugepage-madvise: + * hugetlb-madvise: * * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE * on hugetlb mappings. diff --git a/tools/testing/selftests/mm/hugepage-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c similarity index 99% rename from tools/testing/selftests/mm/hugepage-mmap.c rename to tools/testing/selftests/mm/hugetlb-mmap.c index 66cf74b73dea..a327d90d7a79 100644 --- a/tools/testing/selftests/mm/hugepage-mmap.c +++ b/tools/testing/selftests/mm/hugetlb-mmap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * hugepage-mmap: + * hugetlb-mmap: * * Example of using huge page memory in a user application using the mmap * system call. Before running this application, make sure that the diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c similarity index 99% rename from tools/testing/selftests/mm/hugepage-mremap.c rename to tools/testing/selftests/mm/hugetlb-mremap.c index b8f7d92e5a35..1c87c39780c5 100644 --- a/tools/testing/selftests/mm/hugepage-mremap.c +++ b/tools/testing/selftests/mm/hugetlb-mremap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * hugepage-mremap: + * hugetlb-mremap: * * Example of remapping huge page memory in a user application using the * mremap system call. The path to a file in a hugetlbfs filesystem must diff --git a/tools/testing/selftests/mm/hugepage-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c similarity index 99% rename from tools/testing/selftests/mm/hugepage-shm.c rename to tools/testing/selftests/mm/hugetlb-shm.c index ef06260802b5..de8f5d523084 100644 --- a/tools/testing/selftests/mm/hugepage-shm.c +++ b/tools/testing/selftests/mm/hugetlb-shm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * hugepage-shm: + * hugetlb-shm: * * Example of using huge page memory in a user application using Sys V shared * memory system calls. In this example the app is requesting 256MB of diff --git a/tools/testing/selftests/mm/hugepage-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c similarity index 100% rename from tools/testing/selftests/mm/hugepage-vmemmap.c rename to tools/testing/selftests/mm/hugetlb-vmemmap.c diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 886a3b640bae..8453be45ab18 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -282,18 +282,18 @@ run_test() { echo "TAP version 13" | tap_output -CATEGORY="hugetlb" run_test ./hugepage-mmap +CATEGORY="hugetlb" run_test ./hugetlb-mmap shmmax=$(cat /proc/sys/kernel/shmmax) shmall=$(cat /proc/sys/kernel/shmall) echo 268435456 > /proc/sys/kernel/shmmax echo 4194304 > /proc/sys/kernel/shmall -CATEGORY="hugetlb" run_test ./hugepage-shm +CATEGORY="hugetlb" run_test ./hugetlb-shm echo "$shmmax" > /proc/sys/kernel/shmmax echo "$shmall" > /proc/sys/kernel/shmall -CATEGORY="hugetlb" run_test ./hugepage-mremap -CATEGORY="hugetlb" run_test ./hugepage-vmemmap +CATEGORY="hugetlb" run_test ./hugetlb-mremap +CATEGORY="hugetlb" run_test ./hugetlb-vmemmap CATEGORY="hugetlb" run_test ./hugetlb-madvise CATEGORY="hugetlb" run_test ./hugetlb_dio From f62c93fa8bc1f22fc823e21790a466b2cbcba8f2 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:52 +0300 Subject: [PATCH 4210/5214] selftests/mm: hugetlb-shm: use kselftest framework Convert hugetlb-shm test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-9-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-shm.c | 51 +++++++++++------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c index de8f5d523084..c2c7aee85b96 100644 --- a/tools/testing/selftests/mm/hugetlb-shm.c +++ b/tools/testing/selftests/mm/hugetlb-shm.c @@ -28,9 +28,9 @@ #include #include -#define LENGTH (256UL*1024*1024) +#include "vm_util.h" -#define dprintf(x) printf(x) +#define LENGTH (256UL*1024*1024) int main(void) { @@ -38,44 +38,41 @@ int main(void) unsigned long i; char *shmaddr; + ksft_print_header(); + ksft_set_plan(1); + shmid = shmget(2, LENGTH, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); - if (shmid < 0) { - perror("shmget"); - exit(1); - } - printf("shmid: 0x%x\n", shmid); + if (shmid < 0) + ksft_exit_fail_perror("shmget"); + + ksft_print_msg("shmid: 0x%x\n", shmid); shmaddr = shmat(shmid, NULL, 0); if (shmaddr == (char *)-1) { - perror("Shared memory attach failure"); + ksft_perror("Shared memory attach failure"); shmctl(shmid, IPC_RMID, NULL); - exit(2); + ksft_exit_fail(); } - printf("shmaddr: %p\n", shmaddr); + ksft_print_msg("shmaddr: %p\n", shmaddr); - dprintf("Starting the writes:\n"); - for (i = 0; i < LENGTH; i++) { - shmaddr[i] = (char)(i); - if (!(i % (1024 * 1024))) - dprintf("."); - } - dprintf("\n"); - - dprintf("Starting the Check..."); + ksft_print_msg("Starting the writes:\n"); for (i = 0; i < LENGTH; i++) - if (shmaddr[i] != (char)i) { - printf("\nIndex %lu mismatched\n", i); - exit(3); - } - dprintf("Done.\n"); + shmaddr[i] = (char)(i); + + ksft_print_msg("Starting the Check..."); + for (i = 0; i < LENGTH; i++) + if (shmaddr[i] != (char)i) + ksft_exit_fail_msg("Data mismatch at index %lu\n", i); + ksft_print_msg("Done.\n"); if (shmdt((const void *)shmaddr) != 0) { - perror("Detach failure"); + ksft_perror("Detach failure"); shmctl(shmid, IPC_RMID, NULL); - exit(4); + ksft_exit_fail(); } shmctl(shmid, IPC_RMID, NULL); - return 0; + ksft_test_result_pass("hugepage using SysV shmget/shmat\n"); + ksft_finished(); } From 40e9e3d76069f4108877ad05a5cd171192a0b355 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:53 +0300 Subject: [PATCH 4211/5214] selftests/mm: hugetlb-vmemmap: use kselftest framework Convert hugetlb-vmemmap test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-10-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-vmemmap.c | 42 +++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c index df366a4d1b92..485a6978b40f 100644 --- a/tools/testing/selftests/mm/hugetlb-vmemmap.c +++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c @@ -63,7 +63,7 @@ static int check_page_flags(unsigned long pfn) read(fd, &pageflags, sizeof(pageflags)); if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) { close(fd); - printf("Head page flags (%lx) is invalid\n", pageflags); + ksft_print_msg("Head page flags (%lx) is invalid\n", pageflags); return -1; } @@ -77,7 +77,7 @@ static int check_page_flags(unsigned long pfn) if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS || (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) { close(fd); - printf("Tail page flags (%lx) is invalid\n", pageflags); + ksft_print_msg("Tail page flags (%lx) is invalid\n", pageflags); return -1; } } @@ -91,44 +91,38 @@ int main(int argc, char **argv) { void *addr; unsigned long pfn; + int ret; + + ksft_print_header(); + ksft_set_plan(1); pagesize = psize(); maplength = default_huge_page_size(); - if (!maplength) { - printf("Unable to determine huge page size\n"); - exit(1); - } + if (!maplength) + ksft_exit_skip("Unable to determine huge page size\n"); addr = mmap(NULL, maplength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); /* Trigger allocation of HugeTLB page. */ write_bytes(addr, maplength); pfn = virt_to_pfn(addr); if (pfn == -1UL) { + ksft_perror("virt_to_pfn"); munmap(addr, maplength); - perror("virt_to_pfn"); - exit(1); + ksft_exit_fail(); } - printf("Returned address is %p whose pfn is %lx\n", addr, pfn); + ksft_print_msg("Returned address is %p whose pfn is %lx\n", addr, pfn); - if (check_page_flags(pfn) < 0) { - munmap(addr, maplength); - perror("check_page_flags"); - exit(1); - } + ret = check_page_flags(pfn); - /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ - if (munmap(addr, maplength)) { - perror("munmap"); - exit(1); - } + if (munmap(addr, maplength)) + ksft_exit_fail_perror("munmap"); - return 0; + ksft_test_result(!ret, "HugeTLB vmemmap page flags\n"); + ksft_finished(); } From 83ccc26e5e417792140c5b2026876ead8ce731d9 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:54 +0300 Subject: [PATCH 4212/5214] selftests/mm: hugetlb-madvise: use kselftest framework Convert hugetlb-madvise test to use kselftest framework for reporting and tracking successful and failing runs. While on it fix the check for base page size detection to actually use base_page_size. Link: https://lore.kernel.org/20260511162840.375890-11-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-madvise.c | 204 ++++++++----------- 1 file changed, 82 insertions(+), 122 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index 898cc90b314f..e7dd8d06d986 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -26,12 +26,11 @@ #define validate_free_pages(exp_free) \ do { \ - int fhp = get_free_hugepages(); \ - if (fhp != (exp_free)) { \ - printf("Unexpected number of free huge " \ - "pages line %d\n", __LINE__); \ - exit(1); \ - } \ + unsigned long fhp = get_free_hugepages(); \ + if (fhp != (exp_free)) \ + ksft_exit_fail_msg("Unexpected number of free " \ + "huge pages %lu, expected %lu line %d\n", \ + fhp, (exp_free), __LINE__); \ } while (0) unsigned long huge_page_size; @@ -57,28 +56,24 @@ int main(int argc, char **argv) int fd; int ret; + ksft_print_header(); + ksft_set_plan(1); + huge_page_size = default_huge_page_size(); - if (!huge_page_size) { - printf("Unable to determine huge page size, exiting!\n"); - exit(1); - } + if (!huge_page_size) + ksft_exit_skip("Unable to determine huge page size\n"); + base_page_size = sysconf(_SC_PAGE_SIZE); - if (!huge_page_size) { - printf("Unable to determine base page size, exiting!\n"); - exit(1); - } + if (!base_page_size) + ksft_exit_fail_msg("Unable to determine base page size\n"); free_hugepages = get_free_hugepages(); - if (free_hugepages < MIN_FREE_PAGES) { - printf("Not enough free huge pages to test, exiting!\n"); - exit(KSFT_SKIP); - } + if (free_hugepages < MIN_FREE_PAGES) + ksft_exit_skip("Not enough free huge pages (have %lu, need %d)\n", free_hugepages, MIN_FREE_PAGES); fd = memfd_create(argv[0], MFD_HUGETLB); - if (fd < 0) { - perror("memfd_create() failed"); - exit(1); - } + if (fd < 0) + ksft_exit_fail_perror("memfd_create"); /* * Test validity of MADV_DONTNEED addr and length arguments. mmap @@ -90,16 +85,13 @@ int main(int argc, char **argv) PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); + if (munmap(addr, huge_page_size) || munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size, - huge_page_size)) { - perror("munmap"); - exit(1); - } + huge_page_size)) + ksft_exit_fail_perror("munmap"); addr = addr + huge_page_size; write_fault_pages(addr, NR_HUGE_PAGES); @@ -108,20 +100,14 @@ int main(int argc, char **argv) /* addr before mapping should fail */ ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED); - if (!ret) { - printf("Unexpected success of madvise call with invalid addr line %d\n", - __LINE__); - exit(1); - } + if (!ret) + ksft_exit_fail_msg("madvise with invalid addr unexpectedly succeeded line %d\n", __LINE__); /* addr + length after mapping should fail */ ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size, MADV_DONTNEED); - if (!ret) { - printf("Unexpected success of madvise call with invalid length line %d\n", - __LINE__); - exit(1); - } + if (!ret) + ksft_exit_fail_msg("madvise with invalid length unexpectedly succeeded line %d\n", __LINE__); (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); @@ -132,10 +118,9 @@ int main(int argc, char **argv) PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); + write_fault_pages(addr, NR_HUGE_PAGES); validate_free_pages(free_hugepages - NR_HUGE_PAGES); @@ -143,19 +128,14 @@ int main(int argc, char **argv) ret = madvise(addr + base_page_size, NR_HUGE_PAGES * huge_page_size - base_page_size, MADV_DONTNEED); - if (!ret) { - printf("Unexpected success of madvise call with unaligned start address %d\n", - __LINE__); - exit(1); - } + if (!ret) + ksft_exit_fail_msg("madvise with unaligned start unexpectedly succeeded line %d\n", __LINE__); /* addr + length should be aligned down to huge page size */ if (madvise(addr, ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size, - MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); /* should free all but last page in mapping */ validate_free_pages(free_hugepages - 1); @@ -170,17 +150,14 @@ int main(int argc, char **argv) PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); + write_fault_pages(addr, NR_HUGE_PAGES); validate_free_pages(free_hugepages - NR_HUGE_PAGES); - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); /* should free all pages in mapping */ validate_free_pages(free_hugepages); @@ -190,29 +167,25 @@ int main(int argc, char **argv) /* * Test MADV_DONTNEED on private mapping of hugetlb file */ - if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { - perror("fallocate"); - exit(1); - } + if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) + ksft_exit_fail_perror("fallocate"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); /* read should not consume any pages */ read_fault_pages(addr, NR_HUGE_PAGES); validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* madvise should not free any pages */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* writes should allocate private pages */ @@ -220,10 +193,9 @@ int main(int argc, char **argv) validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); /* madvise should free private pages */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* writes should allocate private pages */ @@ -238,10 +210,9 @@ int main(int argc, char **argv) * implementation. */ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - 0, NR_HUGE_PAGES * huge_page_size)) { - perror("fallocate"); - exit(1); - } + 0, NR_HUGE_PAGES * huge_page_size)) + ksft_exit_fail_perror("fallocate"); + validate_free_pages(free_hugepages); (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); @@ -249,29 +220,25 @@ int main(int argc, char **argv) /* * Test MADV_DONTNEED on shared mapping of hugetlb file */ - if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { - perror("fallocate"); - exit(1); - } + if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) + ksft_exit_fail_perror("fallocate"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); /* write should not consume any pages */ write_fault_pages(addr, NR_HUGE_PAGES); validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* madvise should not free any pages */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* @@ -279,29 +246,25 @@ int main(int argc, char **argv) * * madvise is same as hole punch and should free all pages. */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages); (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); /* * Test MADV_REMOVE on shared and private mapping of hugetlb file */ - if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { - perror("fallocate"); - exit(1); - } + if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) + ksft_exit_fail_perror("fallocate"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (addr == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr == MAP_FAILED) + ksft_exit_fail_perror("mmap"); /* shared write should not consume any additional pages */ write_fault_pages(addr, NR_HUGE_PAGES); @@ -310,10 +273,8 @@ int main(int argc, char **argv) addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (addr2 == MAP_FAILED) { - perror("mmap"); - exit(1); - } + if (addr2 == MAP_FAILED) + ksft_exit_fail_perror("mmap"); /* private read should not consume any pages */ read_fault_pages(addr2, NR_HUGE_PAGES); @@ -324,17 +285,15 @@ int main(int argc, char **argv) validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); /* madvise of shared mapping should not free any pages */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); /* madvise of private mapping should free private pages */ - if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { - perror("madvise"); - exit(1); - } + if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages - NR_HUGE_PAGES); /* private write should consume additional pages again */ @@ -346,15 +305,16 @@ int main(int argc, char **argv) * not correct. private pages should not be freed, but this is * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call. */ - if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { - perror("madvise"); - exit(1); - } + if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) + ksft_exit_fail_perror("madvise"); + validate_free_pages(free_hugepages); (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size); close(fd); - return 0; + + ksft_test_result_pass("MADV_DONTNEED and MADV_REMOVE on hugetlb\n"); + ksft_finished(); } From e9c43934be262ad107b17d606b430d40988f7075 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:55 +0300 Subject: [PATCH 4213/5214] selftests/mm: hugetlb_madv_vs_map: use kselftest framework Convert hugetlb_madv_vs_map test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-12-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Mark Brown Reviewed-by: Donet Tom Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../testing/selftests/mm/hugetlb_madv_vs_map.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c index efd774b41389..c7105c6d319b 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -25,7 +25,6 @@ #include #include "vm_util.h" -#include "kselftest.h" #define INLOOP_ITER 100 @@ -86,12 +85,14 @@ int main(void) */ int max = 10; + ksft_print_header(); + ksft_set_plan(1); + free_hugepages = get_free_hugepages(); - if (free_hugepages != 1) { + if (free_hugepages != 1) ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", free_hugepages); - } mmap_size = default_huge_page_size(); @@ -100,10 +101,8 @@ int main(void) MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); - if ((unsigned long)huge_ptr == -1) { - ksft_test_result_fail("Failed to allocate huge page\n"); - return KSFT_FAIL; - } + if ((unsigned long)huge_ptr == -1) + ksft_exit_fail_msg("Failed to allocate huge page\n"); pthread_create(&thread1, NULL, madv, NULL); pthread_create(&thread2, NULL, touch, NULL); @@ -115,12 +114,13 @@ int main(void) if (ret) { ksft_test_result_fail("Unexpected huge page allocation\n"); - return KSFT_FAIL; + ksft_finished(); } /* Unmap and restart */ munmap(huge_ptr, mmap_size); } - return KSFT_PASS; + ksft_test_result_pass("No unexpected huge page allocations\n"); + ksft_finished(); } From 5be449d0eac1a568401786c9e20106afe55c7394 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:56 +0300 Subject: [PATCH 4214/5214] selftests/mm: hugetlb-read-hwpoison: use kselftest framework Convert hugetlb-read-hwpoison test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-13-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Li Wang Reviewed-by: Li Wang Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../selftests/mm/hugetlb-read-hwpoison.c | 115 +++++++++--------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-read-hwpoison.c b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c index ad3198b444ce..70b24e3660c4 100644 --- a/tools/testing/selftests/mm/hugetlb-read-hwpoison.c +++ b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c @@ -14,9 +14,6 @@ #include "kselftest.h" -#define PREFIX " ... " -#define ERROR_PREFIX " !!! " - #define MAX_WRITE_READ_CHUNK_SIZE (getpagesize() * 16) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) @@ -26,17 +23,22 @@ enum test_status { TEST_SKIPPED = 2, }; -static char *status_to_str(enum test_status status) +static void report_status(enum test_status status, const char *test_name, + size_t chunk_size) { switch (status) { case TEST_PASSED: - return "TEST_PASSED"; + ksft_test_result_pass("%s chunk_size=0x%lx\n", + test_name, chunk_size); + break; case TEST_FAILED: - return "TEST_FAILED"; + ksft_test_result_fail("%s chunk_size=0x%lx\n", + test_name, chunk_size); + break; case TEST_SKIPPED: - return "TEST_SKIPPED"; - default: - return "TEST_???"; + ksft_test_result_skip("%s chunk_size=0x%lx\n", + test_name, chunk_size); + break; } } @@ -59,8 +61,8 @@ static bool verify_chunk(char *buf, size_t len, char val) for (i = 0; i < len; ++i) { if (buf[i] != val) { - printf(PREFIX ERROR_PREFIX "check fail: buf[%lu] = %u != %u\n", - i, buf[i], val); + ksft_print_msg("check fail: buf[%lu] = %u != %u\n", + i, buf[i], val); return false; } } @@ -76,21 +78,21 @@ static bool seek_read_hugepage_filemap(int fd, size_t len, size_t wr_chunk_size, ssize_t total_ret_count = 0; char val = offset / wr_chunk_size + offset % wr_chunk_size; - printf(PREFIX PREFIX "init val=%u with offset=0x%lx\n", val, offset); - printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n", - expected); + ksft_print_msg("init val=%u with offset=0x%lx\n", val, offset); + ksft_print_msg("expect to read 0x%lx bytes of data in total\n", + expected); if (lseek(fd, offset, SEEK_SET) < 0) { - perror(PREFIX ERROR_PREFIX "seek failed"); + ksft_perror("seek failed"); return false; } while (offset + total_ret_count < len) { ret_count = read(fd, buf, wr_chunk_size); if (ret_count == 0) { - printf(PREFIX PREFIX "read reach end of the file\n"); + ksft_print_msg("read reach end of the file\n"); break; } else if (ret_count < 0) { - perror(PREFIX ERROR_PREFIX "read failed"); + ksft_perror("read failed"); break; } ++val; @@ -99,8 +101,8 @@ static bool seek_read_hugepage_filemap(int fd, size_t len, size_t wr_chunk_size, total_ret_count += ret_count; } - printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n", - total_ret_count); + ksft_print_msg("actually read 0x%lx bytes of data in total\n", + total_ret_count); return total_ret_count == expected; } @@ -113,15 +115,15 @@ static bool read_hugepage_filemap(int fd, size_t len, ssize_t total_ret_count = 0; char val = 0; - printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n", - expected); + ksft_print_msg("expect to read 0x%lx bytes of data in total\n", + expected); while (total_ret_count < len) { ret_count = read(fd, buf, wr_chunk_size); if (ret_count == 0) { - printf(PREFIX PREFIX "read reach end of the file\n"); + ksft_print_msg("read reach end of the file\n"); break; } else if (ret_count < 0) { - perror(PREFIX ERROR_PREFIX "read failed"); + ksft_perror("read failed"); break; } ++val; @@ -130,8 +132,8 @@ static bool read_hugepage_filemap(int fd, size_t len, total_ret_count += ret_count; } - printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n", - total_ret_count); + ksft_print_msg("actually read 0x%lx bytes of data in total\n", + total_ret_count); return total_ret_count == expected; } @@ -143,14 +145,14 @@ test_hugetlb_read(int fd, size_t len, size_t wr_chunk_size) char *filemap = NULL; if (ftruncate(fd, len) < 0) { - perror(PREFIX ERROR_PREFIX "ftruncate failed"); + ksft_perror("ftruncate failed"); return status; } filemap = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, 0); if (filemap == MAP_FAILED) { - perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed"); + ksft_perror("mmap for primary mapping failed"); goto done; } @@ -163,7 +165,7 @@ test_hugetlb_read(int fd, size_t len, size_t wr_chunk_size) munmap(filemap, len); done: if (ftruncate(fd, 0) < 0) { - perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed"); + ksft_perror("ftruncate back to 0 failed"); status = TEST_FAILED; } @@ -180,14 +182,14 @@ test_hugetlb_read_hwpoison(int fd, size_t len, size_t wr_chunk_size, const unsigned long pagesize = getpagesize(); if (ftruncate(fd, len) < 0) { - perror(PREFIX ERROR_PREFIX "ftruncate failed"); + ksft_perror("ftruncate failed"); return status; } filemap = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, 0); if (filemap == MAP_FAILED) { - perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed"); + ksft_perror("mmap for primary mapping failed"); goto done; } @@ -202,7 +204,7 @@ test_hugetlb_read_hwpoison(int fd, size_t len, size_t wr_chunk_size, */ hwp_addr = filemap + len / 2 + pagesize; if (madvise(hwp_addr, pagesize, MADV_HWPOISON) < 0) { - perror(PREFIX ERROR_PREFIX "MADV_HWPOISON failed"); + ksft_perror("MADV_HWPOISON failed"); goto unmap; } @@ -229,7 +231,7 @@ test_hugetlb_read_hwpoison(int fd, size_t len, size_t wr_chunk_size, munmap(filemap, len); done: if (ftruncate(fd, 0) < 0) { - perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed"); + ksft_perror("ftruncate back to 0 failed"); status = TEST_FAILED; } @@ -242,17 +244,17 @@ static int create_hugetlbfs_file(struct statfs *file_stat) fd = memfd_create("hugetlb_tmp", MFD_HUGETLB); if (fd < 0) { - perror(PREFIX ERROR_PREFIX "could not open hugetlbfs file"); + ksft_perror("could not open hugetlbfs file"); return -1; } memset(file_stat, 0, sizeof(*file_stat)); if (fstatfs(fd, file_stat)) { - perror(PREFIX ERROR_PREFIX "fstatfs failed"); + ksft_perror("fstatfs failed"); goto close; } if (file_stat->f_type != HUGETLBFS_MAGIC) { - printf(PREFIX ERROR_PREFIX "not hugetlbfs file\n"); + ksft_print_msg("not hugetlbfs file\n"); goto close; } @@ -278,51 +280,44 @@ int main(void) }; size_t i; + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(wr_chunk_sizes) * 3); + signal(SIGBUS, sigbus_handler); for (i = 0; i < ARRAY_SIZE(wr_chunk_sizes); ++i) { - printf("Write/read chunk size=0x%lx\n", - wr_chunk_sizes[i]); + ksft_print_msg("Write/read chunk size=0x%lx\n", + wr_chunk_sizes[i]); fd = create_hugetlbfs_file(&file_stat); if (fd < 0) - goto create_failure; - printf(PREFIX "HugeTLB read regression test...\n"); + ksft_exit_fail_msg("Failed to create hugetlbfs file\n"); + status = test_hugetlb_read(fd, file_stat.f_bsize, wr_chunk_sizes[i]); - printf(PREFIX "HugeTLB read regression test...%s\n", - status_to_str(status)); close(fd); - if (status == TEST_FAILED) - return -1; + report_status(status, "HugeTLB read regression", + wr_chunk_sizes[i]); fd = create_hugetlbfs_file(&file_stat); if (fd < 0) - goto create_failure; - printf(PREFIX "HugeTLB read HWPOISON test...\n"); + ksft_exit_fail_msg("Failed to create hugetlbfs file\n"); + status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize, wr_chunk_sizes[i], false); - printf(PREFIX "HugeTLB read HWPOISON test...%s\n", - status_to_str(status)); close(fd); - if (status == TEST_FAILED) - return -1; + report_status(status, "HugeTLB read HWPOISON", + wr_chunk_sizes[i]); fd = create_hugetlbfs_file(&file_stat); if (fd < 0) - goto create_failure; - printf(PREFIX "HugeTLB seek then read HWPOISON test...\n"); + ksft_exit_fail_msg("Failed to create hugetlbfs file\n"); + status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize, wr_chunk_sizes[i], true); - printf(PREFIX "HugeTLB seek then read HWPOISON test...%s\n", - status_to_str(status)); close(fd); - if (status == TEST_FAILED) - return -1; + report_status(status, "HugeTLB seek then read HWPOISON", + wr_chunk_sizes[i]); } - return 0; - -create_failure: - printf(ERROR_PREFIX "Abort test: failed to create hugetlbfs file\n"); - return -1; + ksft_finished(); } From 5424015544869c731e01773d7fdfd44082fb00e8 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:57 +0300 Subject: [PATCH 4215/5214] selftests/mm: khugepaged: group tests in an array Currently khugepaged decides if a test can run using TEST() macro that checks what mem_ops and collapse_context are set by the command line arguments. For better compatibility with ksefltest framework, add an array of 'struct test_case's and redefine TEST() macro to conditionally add enabled tests to that array. Then execute the enabled test by looping the test_case's array. Link: https://lore.kernel.org/20260511162840.375890-14-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/khugepaged.c | 43 +++++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 9ce0a11b461d..7f61bfa455e9 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -1265,6 +1265,34 @@ static void parse_test_type(int argc, char **argv) get_finfo(argv[1]); } +typedef void (*test_fn)(struct collapse_context *c, struct mem_ops *ops); + +struct test_case { + struct collapse_context *ctx; + struct mem_ops *ops; + const char *desc; + test_fn fn; +}; + +#define MAX_TEST_CASES 64 +static struct test_case test_cases[MAX_TEST_CASES]; +static int nr_test_cases; + +#define TEST(t, c, o) do { \ + if (c && o) { \ + if (nr_test_cases >= MAX_TEST_CASES) { \ + printf("MAX_TEST_CASES is too small\n"); \ + exit(EXIT_FAILURE); \ + } \ + test_cases[nr_test_cases++] = (struct test_case){ \ + .ctx = c, \ + .ops = o, \ + .desc = #t, \ + .fn = t, \ + }; \ + } \ + } while (0) + int main(int argc, char **argv) { int hpage_pmd_order; @@ -1320,13 +1348,6 @@ int main(int argc, char **argv) alloc_at_fault(); -#define TEST(t, c, o) do { \ - if (c && o) { \ - printf("\nRun test: " #t " (%s:%s)\n", c->name, o->name); \ - t(c, o); \ - } \ - } while (0) - TEST(collapse_full, khugepaged_context, anon_ops); TEST(collapse_full, khugepaged_context, read_only_file_ops); TEST(collapse_full, khugepaged_context, read_write_file_read_ops); @@ -1404,5 +1425,13 @@ int main(int argc, char **argv) TEST(madvise_retracted_page_tables, madvise_context, read_write_file_read_ops); TEST(madvise_retracted_page_tables, madvise_context, shmem_ops); + exit_status = KSFT_PASS; + for (int i = 0; i < nr_test_cases; i++) { + struct test_case *t = &test_cases[i]; + + printf("\nRun test: %s (%s:%s)\n", t->desc, t->ctx->name, t->ops->name); + t->fn(t->ctx, t->ops); + } + restore_settings(0); } From 3ab1f01b091df0ea00cb306e0a8501a8379444f9 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:58 +0300 Subject: [PATCH 4216/5214] selftests/mm: khugepaged: use kselftest framework Convert khugepaged tests to use kselftest framework for reporting and tracking successful and failing runs. The conversion is mostly about replacing printf()/perror() + exit() pairs with their ksft_ counterparts. The nice colored success and failure indications are left intact. Replace the progress report in collapse_compound_extreme() with a single ksft_print_msg() to avoid headache with formatting and make the test output more concise. [rppt@kernel.org: make the output TAP-compatible] [ziy@nvidia.com: update for "Remove CONFIG_READ_ONLY_THP_FOR_FS and enable file THP for writable files", v6] https://lore.kernel.org/20260517135416.1434539-1-ziy@nvidia.com Link: https://lore.kernel.org/20260511162840.375890-15-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Li Wang Cc: Sarthak Sharma Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/khugepaged.c | 321 ++++++++++-------------- 1 file changed, 132 insertions(+), 189 deletions(-) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 7f61bfa455e9..61c3b2c42544 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -86,17 +86,19 @@ static int exit_status; static void success(const char *msg) { printf(" \e[32m%s\e[0m\n", msg); + exit_status = KSFT_PASS; } static void fail(const char *msg) { printf(" \e[31m%s\e[0m\n", msg); - exit_status++; + exit_status = KSFT_FAIL; } static void skip(const char *msg) { printf(" \e[33m%s\e[0m\n", msg); + exit_status = KSFT_SKIP; } static void restore_settings_atexit(void) @@ -104,22 +106,24 @@ static void restore_settings_atexit(void) if (skip_settings_restore) return; - printf("Restore THP and khugepaged settings..."); + ksft_print_msg("Restore THP and khugepaged settings..."); thp_restore_settings(); success("OK"); skip_settings_restore = true; + ksft_print_cnts(); + exit(exit_status); } static void restore_settings(int sig) { /* exit() will invoke the restore_settings_atexit handler. */ - exit(sig ? EXIT_FAILURE : exit_status); + exit(sig ? KSFT_FAIL : exit_status); } static void save_settings(void) { - printf("Save THP and khugepaged settings..."); + ksft_print_msg("Save THP and khugepaged settings..."); if ((read_only_file_ops || read_write_file_read_ops || read_write_file_write_ops) && finfo.type == VMA_FILE) @@ -145,19 +149,13 @@ static void get_finfo(const char *dir) finfo.dir = dir; stat(finfo.dir, &path_stat); - if (!S_ISDIR(path_stat.st_mode)) { - printf("%s: Not a directory (%s)\n", __func__, finfo.dir); - exit(EXIT_FAILURE); - } + if (!S_ISDIR(path_stat.st_mode)) + ksft_exit_fail_msg("%s: Not a directory (%s)\n", __func__, finfo.dir); if (snprintf(finfo.path, sizeof(finfo.path), "%s/" TEST_FILE, - finfo.dir) >= sizeof(finfo.path)) { - printf("%s: Pathname is too long\n", __func__); - exit(EXIT_FAILURE); - } - if (statfs(finfo.dir, &fs)) { - perror("statfs()"); - exit(EXIT_FAILURE); - } + finfo.dir) >= sizeof(finfo.path)) + ksft_exit_fail_msg("%s: Pathname is too long\n", __func__); + if (statfs(finfo.dir, &fs)) + ksft_exit_fail_perror("statfs()"); finfo.type = fs.f_type == TMPFS_MAGIC ? VMA_SHMEM : VMA_FILE; if (finfo.type == VMA_SHMEM) return; @@ -165,40 +163,30 @@ static void get_finfo(const char *dir) /* Find owning device's queue/read_ahead_kb control */ if (snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/uevent", major(path_stat.st_dev), minor(path_stat.st_dev)) - >= sizeof(path)) { - printf("%s: Pathname is too long\n", __func__); - exit(EXIT_FAILURE); - } - if (read_file(path, buf, sizeof(buf)) < 0) { - perror("read_file(read_num)"); - exit(EXIT_FAILURE); - } + >= sizeof(path)) + ksft_exit_fail_msg("%s: Pathname is too long\n", __func__); + if (read_file(path, buf, sizeof(buf)) < 0) + ksft_exit_fail_perror("read_file(read_num)"); if (strstr(buf, "DEVTYPE=disk")) { /* Found it */ if (snprintf(finfo.dev_queue_read_ahead_path, sizeof(finfo.dev_queue_read_ahead_path), "/sys/dev/block/%d:%d/queue/read_ahead_kb", major(path_stat.st_dev), minor(path_stat.st_dev)) - >= sizeof(finfo.dev_queue_read_ahead_path)) { - printf("%s: Pathname is too long\n", __func__); - exit(EXIT_FAILURE); - } + >= sizeof(finfo.dev_queue_read_ahead_path)) + ksft_exit_fail_msg("%s: Pathname is too long\n", __func__); return; } - if (!strstr(buf, "DEVTYPE=partition")) { - printf("%s: Unknown device type: %s\n", __func__, path); - exit(EXIT_FAILURE); - } + if (!strstr(buf, "DEVTYPE=partition")) + ksft_exit_fail_msg("%s: Unknown device type: %s\n", __func__, path); /* * Partition of block device - need to find actual device. * Using naming convention that devnameN is partition of * device devname. */ str = strstr(buf, "DEVNAME="); - if (!str) { - printf("%s: Could not read: %s", __func__, path); - exit(EXIT_FAILURE); - } + if (!str) + ksft_exit_fail_msg("%s: Could not read: %s", __func__, path); str += 8; end = str; while (*end) { @@ -207,16 +195,13 @@ static void get_finfo(const char *dir) if (snprintf(finfo.dev_queue_read_ahead_path, sizeof(finfo.dev_queue_read_ahead_path), "/sys/block/%s/queue/read_ahead_kb", - str) >= sizeof(finfo.dev_queue_read_ahead_path)) { - printf("%s: Pathname is too long\n", __func__); - exit(EXIT_FAILURE); - } + str) >= sizeof(finfo.dev_queue_read_ahead_path)) + ksft_exit_fail_msg("%s: Pathname is too long\n", __func__); return; } ++end; } - printf("%s: Could not read: %s\n", __func__, path); - exit(EXIT_FAILURE); + ksft_exit_fail_msg("%s: Could not read: %s\n", __func__, path); } static bool check_swap(void *addr, unsigned long size) @@ -229,26 +214,19 @@ static bool check_swap(void *addr, unsigned long size) ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-", (unsigned long) addr); - if (ret >= MAX_LINE_LENGTH) { - printf("%s: Pattern is too long\n", __func__); - exit(EXIT_FAILURE); - } - + if (ret >= MAX_LINE_LENGTH) + ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); fp = fopen(PID_SMAPS, "r"); - if (!fp) { - printf("%s: Failed to open file %s\n", __func__, PID_SMAPS); - exit(EXIT_FAILURE); - } + if (!fp) + ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, PID_SMAPS); if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer))) goto err_out; ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "Swap:%19ld kB", size >> 10); - if (ret >= MAX_LINE_LENGTH) { - printf("%s: Pattern is too long\n", __func__); - exit(EXIT_FAILURE); - } + if (ret >= MAX_LINE_LENGTH) + ksft_exit_fail_msg("%s: Pattern is too long\n", __func__); /* * Fetch the Swap: in the same block and check whether it got * the expected number of hugeepages next. @@ -271,10 +249,8 @@ static void *alloc_mapping(int nr) p = mmap(BASE_ADDR, nr * hpage_pmd_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (p != BASE_ADDR) { - printf("Failed to allocate VMA at %p\n", BASE_ADDR); - exit(EXIT_FAILURE); - } + if (p != BASE_ADDR) + ksft_exit_fail_msg("Failed to allocate VMA at %p\n", BASE_ADDR); return p; } @@ -324,19 +300,13 @@ static void *alloc_hpage(struct mem_ops *ops) * khugepaged on low-load system (like a test machine), which * would cause MADV_COLLAPSE to fail with EAGAIN. */ - printf("Allocate huge page..."); - if (madvise_collapse_retry(p, hpage_pmd_size)) { - perror("madvise(MADV_COLLAPSE)"); - exit(EXIT_FAILURE); - } - if (!ops->check_huge(p, 1)) { - perror("madvise(MADV_COLLAPSE)"); - exit(EXIT_FAILURE); - } - if (madvise(p, hpage_pmd_size, MADV_HUGEPAGE)) { - perror("madvise(MADV_HUGEPAGE)"); - exit(EXIT_FAILURE); - } + ksft_print_msg("Allocate huge page..."); + if (madvise_collapse_retry(p, hpage_pmd_size)) + ksft_exit_fail_perror("madvise(MADV_COLLAPSE)"); + if (!ops->check_huge(p, 1)) + ksft_exit_fail_perror("madvise(MADV_COLLAPSE)"); + if (madvise(p, hpage_pmd_size, MADV_HUGEPAGE)) + ksft_exit_fail_perror("madvise(MADV_HUGEPAGE)"); success("OK"); return p; } @@ -346,11 +316,9 @@ static void validate_memory(int *p, unsigned long start, unsigned long end) int i; for (i = start / page_size; i < end / page_size; i++) { - if (p[i * page_size / sizeof(*p)] != i + 0xdead0000) { - printf("Page %d is corrupted: %#x\n", - i, p[i * page_size / sizeof(*p)]); - exit(EXIT_FAILURE); - } + if (p[i * page_size / sizeof(*p)] != i + 0xdead0000) + ksft_exit_fail_msg("Page %d is corrupted: %#x\n", + i, p[i * page_size / sizeof(*p)]); } } @@ -383,14 +351,12 @@ static void *file_setup_area_common(int nr_hpages, enum file_setup_ops setup) unsigned long size; unlink(finfo.path); /* Cleanup from previous failed tests */ - printf("Creating %s for collapse%s...", finfo.path, - finfo.type == VMA_SHMEM ? " (tmpfs)" : ""); + ksft_print_msg("Creating %s for collapse%s...", finfo.path, + finfo.type == VMA_SHMEM ? " (tmpfs)" : ""); fd = open(finfo.path, O_CREAT | O_RDWR | O_TRUNC | O_EXCL, 777); - if (fd < 0) { - perror("open()"); - exit(EXIT_FAILURE); - } + if (fd < 0) + ksft_exit_fail_perror("open()"); size = nr_hpages * hpage_pmd_size; if (ftruncate(fd, size)) { @@ -411,22 +377,17 @@ static void *file_setup_area_common(int nr_hpages, enum file_setup_ops setup) close(fd); munmap(p, size); success("OK"); - - printf("Opening %s %s for collapse...", finfo.path, + ksft_print_msg("Opening %s %s for collapse...", finfo.path, setup == FILE_SETUP_READ_ONLY_FS ? "read-only" : setup == FILE_SETUP_READ_WRITE_FS_READ_DATA ? "read-write (read)" : "read-write (write)"); finfo.fd = open(finfo.path, open_opt, 777); - if (finfo.fd < 0) { - perror("open()"); - exit(EXIT_FAILURE); - } + if (finfo.fd < 0) + ksft_exit_fail_perror("open()"); p = mmap(BASE_ADDR, size, mmap_prot, MAP_SHARED, finfo.fd, 0); - if (p == MAP_FAILED || p != BASE_ADDR) { - perror("mmap()"); - exit(EXIT_FAILURE); - } + if (p == MAP_FAILED || p != BASE_ADDR) + ksft_exit_fail_perror("mmap()"); /* Drop page cache */ write_file("/proc/sys/vm/drop_caches", "3", 2); @@ -458,10 +419,8 @@ static void file_cleanup_area(void *p, unsigned long size) static void file_fault_read(void *p, unsigned long start, unsigned long end) { - if (madvise(((char *)p) + start, end - start, MADV_POPULATE_READ)) { - perror("madvise(MADV_POPULATE_READ)"); - exit(EXIT_FAILURE); - } + if (madvise(((char *)p) + start, end - start, MADV_POPULATE_READ)) + ksft_exit_fail_perror("madvise(MADV_POPULATE_READ)"); } static void file_fault_read_and_flush(void *p, unsigned long start, unsigned long end) @@ -476,10 +435,8 @@ static void file_fault_read_and_flush(void *p, unsigned long start, unsigned lon static void file_fault_write(void *p, unsigned long start, unsigned long end) { - if (madvise(((char *)p) + start, end - start, MADV_POPULATE_WRITE)) { - perror("madvise(MADV_POPULATE_WRITE)"); - exit(EXIT_FAILURE); - } + if (madvise(((char *)p) + start, end - start, MADV_POPULATE_WRITE)) + ksft_exit_fail_perror("madvise(MADV_POPULATE_WRITE)"); } static bool file_check_huge(void *addr, int nr_hpages) @@ -501,20 +458,14 @@ static void *shmem_setup_area(int nr_hpages) unsigned long size = nr_hpages * hpage_pmd_size; finfo.fd = memfd_create("khugepaged-selftest-collapse-shmem", 0); - if (finfo.fd < 0) { - perror("memfd_create()"); - exit(EXIT_FAILURE); - } - if (ftruncate(finfo.fd, size)) { - perror("ftruncate()"); - exit(EXIT_FAILURE); - } + if (finfo.fd < 0) + ksft_exit_fail_perror("memfd_create()"); + if (ftruncate(finfo.fd, size)) + ksft_exit_fail_perror("ftruncate()"); p = mmap(BASE_ADDR, size, PROT_READ | PROT_WRITE, MAP_SHARED, finfo.fd, 0); - if (p != BASE_ADDR) { - perror("mmap()"); - exit(EXIT_FAILURE); - } + if (p != BASE_ADDR) + ksft_exit_fail_perror("mmap()"); return p; } @@ -588,7 +539,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages, int ret; struct thp_settings settings = *thp_current_settings(); - printf("%s...", msg); + ksft_print_msg("%s...", msg); /* * read&write file collapse succeeds for MADV_COLLAPSE because dirty @@ -621,10 +572,8 @@ static void madvise_collapse(const char *msg, char *p, int nr_hpages, struct mem_ops *ops, bool expect) { /* Sanity check */ - if (!ops->check_huge(p, 0)) { - printf("Unexpected huge page\n"); - exit(EXIT_FAILURE); - } + if (!ops->check_huge(p, 0)) + ksft_exit_fail_msg("Unexpected huge page\n"); __madvise_collapse(msg, p, nr_hpages, ops, expect); } @@ -636,17 +585,15 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages, int timeout = 6; /* 3 seconds */ /* Sanity check */ - if (!ops->check_huge(p, 0)) { - printf("Unexpected huge page\n"); - exit(EXIT_FAILURE); - } + if (!ops->check_huge(p, 0)) + ksft_exit_fail_msg("Unexpected huge page\n"); madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE); /* Wait until the second full_scan completed */ full_scans = thp_read_num("khugepaged/full_scans") + 2; - printf("%s...", msg); + ksft_print_msg("%s...", msg); while (timeout--) { if (ops->check_huge(p, nr_hpages)) break; @@ -713,7 +660,7 @@ static void alloc_at_fault(void) p = alloc_mapping(1); *p = 1; - printf("Allocate huge page on fault..."); + ksft_print_msg("Allocate huge page on fault..."); if (check_huge_anon(p, 1, hpage_pmd_size)) success("OK"); else @@ -722,12 +669,14 @@ static void alloc_at_fault(void) thp_pop_settings(); madvise(p, page_size, MADV_DONTNEED); - printf("Split huge PMD on MADV_DONTNEED..."); + ksft_print_msg("Split huge PMD on MADV_DONTNEED..."); if (check_huge_anon(p, 0, hpage_pmd_size)) success("OK"); else fail("Fail"); munmap(p, hpage_pmd_size); + + ksft_test_result_report(exit_status, "allocate on fault and split\n"); } static void collapse_full(struct collapse_context *c, struct mem_ops *ops) @@ -742,6 +691,8 @@ static void collapse_full(struct collapse_context *c, struct mem_ops *ops) ops, true); validate_memory(p, 0, size); ops->cleanup_area(p, size); + + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_empty(struct collapse_context *c, struct mem_ops *ops) @@ -751,6 +702,7 @@ static void collapse_empty(struct collapse_context *c, struct mem_ops *ops) p = ops->setup_area(1); c->collapse("Do not collapse empty PTE table", p, 1, ops, false); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_single_pte_entry(struct collapse_context *c, struct mem_ops *ops) @@ -762,6 +714,7 @@ static void collapse_single_pte_entry(struct collapse_context *c, struct mem_ops c->collapse("Collapse PTE table with single PTE entry present", p, 1, ops, true); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *ops) @@ -801,6 +754,7 @@ static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *o skip: ops->cleanup_area(p, hpage_pmd_size); thp_pop_settings(); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_ops *ops) @@ -810,11 +764,9 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op p = ops->setup_area(1); ops->fault(p, 0, hpage_pmd_size); - printf("Swapout one page..."); - if (madvise(p, page_size, MADV_PAGEOUT)) { - perror("madvise(MADV_PAGEOUT)"); - exit(EXIT_FAILURE); - } + ksft_print_msg("Swapout one page..."); + if (madvise(p, page_size, MADV_PAGEOUT)) + ksft_exit_fail_perror("madvise(MADV_PAGEOUT)"); if (check_swap(p, page_size)) { success("OK"); } else { @@ -827,6 +779,7 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op validate_memory(p, 0, hpage_pmd_size); out: ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *ops) @@ -837,11 +790,9 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o p = ops->setup_area(1); ops->fault(p, 0, hpage_pmd_size); - printf("Swapout %d of %d pages...", max_ptes_swap + 1, hpage_pmd_nr); - if (madvise(p, (max_ptes_swap + 1) * page_size, MADV_PAGEOUT)) { - perror("madvise(MADV_PAGEOUT)"); - exit(EXIT_FAILURE); - } + ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap + 1, hpage_pmd_nr); + if (madvise(p, (max_ptes_swap + 1) * page_size, MADV_PAGEOUT)) + ksft_exit_fail_perror("madvise(MADV_PAGEOUT)"); if (check_swap(p, (max_ptes_swap + 1) * page_size)) { success("OK"); } else { @@ -855,12 +806,10 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o if (c->enforce_pte_scan_limits) { ops->fault(p, 0, hpage_pmd_size); - printf("Swapout %d of %d pages...", max_ptes_swap, + ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap, hpage_pmd_nr); - if (madvise(p, max_ptes_swap * page_size, MADV_PAGEOUT)) { - perror("madvise(MADV_PAGEOUT)"); - exit(EXIT_FAILURE); - } + if (madvise(p, max_ptes_swap * page_size, MADV_PAGEOUT)) + ksft_exit_fail_perror("madvise(MADV_PAGEOUT)"); if (check_swap(p, max_ptes_swap * page_size)) { success("OK"); } else { @@ -874,6 +823,7 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o } out: ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_single_pte_entry_compound(struct collapse_context *c, struct mem_ops *ops) @@ -890,7 +840,7 @@ static void collapse_single_pte_entry_compound(struct collapse_context *c, struc } madvise(p, hpage_pmd_size, MADV_NOHUGEPAGE); - printf("Split huge page leaving single PTE mapping compound page..."); + ksft_print_msg("Split huge page leaving single PTE mapping compound page..."); madvise(p + page_size, hpage_pmd_size - page_size, MADV_DONTNEED); if (ops->check_huge(p, 0)) success("OK"); @@ -902,6 +852,7 @@ static void collapse_single_pte_entry_compound(struct collapse_context *c, struc validate_memory(p, 0, page_size); skip: ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_full_of_compound(struct collapse_context *c, struct mem_ops *ops) @@ -909,7 +860,7 @@ static void collapse_full_of_compound(struct collapse_context *c, struct mem_ops void *p; p = alloc_hpage(ops); - printf("Split huge page leaving single PTE page table full of compound pages..."); + ksft_print_msg("Split huge page leaving single PTE page table full of compound pages..."); madvise(p, page_size, MADV_NOHUGEPAGE); madvise(p, hpage_pmd_size, MADV_NOHUGEPAGE); if (ops->check_huge(p, 0)) @@ -921,6 +872,7 @@ static void collapse_full_of_compound(struct collapse_context *c, struct mem_ops true); validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_compound_extreme(struct collapse_context *c, struct mem_ops *ops) @@ -929,16 +881,12 @@ static void collapse_compound_extreme(struct collapse_context *c, struct mem_ops int i; p = ops->setup_area(1); + ksft_print_msg("Construct PTE page table full of different PTE-mapped compound pages\n"); for (i = 0; i < hpage_pmd_nr; i++) { - printf("\rConstruct PTE page table full of different PTE-mapped compound pages %3d/%d...", - i + 1, hpage_pmd_nr); - madvise(BASE_ADDR, hpage_pmd_size, MADV_HUGEPAGE); ops->fault(BASE_ADDR, 0, hpage_pmd_size); - if (!ops->check_huge(BASE_ADDR, 1)) { - printf("Failed to allocate huge page\n"); - exit(EXIT_FAILURE); - } + if (!ops->check_huge(BASE_ADDR, 1)) + ksft_exit_fail_msg("Failed to allocate huge page\n"); madvise(BASE_ADDR, hpage_pmd_size, MADV_NOHUGEPAGE); p = mremap(BASE_ADDR - i * page_size, @@ -946,20 +894,16 @@ static void collapse_compound_extreme(struct collapse_context *c, struct mem_ops (i + 1) * page_size, MREMAP_MAYMOVE | MREMAP_FIXED, BASE_ADDR + 2 * hpage_pmd_size); - if (p == MAP_FAILED) { - perror("mremap+unmap"); - exit(EXIT_FAILURE); - } + if (p == MAP_FAILED) + ksft_exit_fail_perror("mremap+unmap"); p = mremap(BASE_ADDR + 2 * hpage_pmd_size, (i + 1) * page_size, (i + 1) * page_size + hpage_pmd_size, MREMAP_MAYMOVE | MREMAP_FIXED, BASE_ADDR - (i + 1) * page_size); - if (p == MAP_FAILED) { - perror("mremap+alloc"); - exit(EXIT_FAILURE); - } + if (p == MAP_FAILED) + ksft_exit_fail_perror("mremap+alloc"); } ops->cleanup_area(BASE_ADDR, hpage_pmd_size); @@ -974,6 +918,7 @@ static void collapse_compound_extreme(struct collapse_context *c, struct mem_ops validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) @@ -983,18 +928,17 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) p = ops->setup_area(1); - printf("Allocate small page..."); + ksft_print_msg("Allocate small page..."); ops->fault(p, 0, page_size); if (ops->check_huge(p, 0)) success("OK"); else fail("Fail"); - printf("Share small page over fork()..."); + ksft_print_msg("Share small page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ skip_settings_restore = true; - exit_status = 0; if (ops->check_huge(p, 0)) success("OK"); @@ -1011,15 +955,16 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) } wait(&wstatus); - exit_status += WEXITSTATUS(wstatus); + exit_status = WEXITSTATUS(wstatus); - printf("Check if parent still has small page..."); + ksft_print_msg("Check if parent still has small page..."); if (ops->check_huge(p, 0)) success("OK"); else fail("Fail"); validate_memory(p, 0, page_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *ops) @@ -1028,18 +973,17 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o void *p; p = alloc_hpage(ops); - printf("Share huge page over fork()..."); + ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ skip_settings_restore = true; - exit_status = 0; if (ops->check_huge(p, 1)) success("OK"); else fail("Fail"); - printf("Split huge page PMD in child process..."); + ksft_print_msg("Split huge page PMD in child process..."); madvise(p, page_size, MADV_NOHUGEPAGE); madvise(p, hpage_pmd_size, MADV_NOHUGEPAGE); if (ops->check_huge(p, 0)) @@ -1060,15 +1004,16 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o } wait(&wstatus); - exit_status += WEXITSTATUS(wstatus); + exit_status = WEXITSTATUS(wstatus); - printf("Check if parent still has huge page..."); + ksft_print_msg("Check if parent still has huge page..."); if (ops->check_huge(p, 1)) success("OK"); else fail("Fail"); validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops *ops) @@ -1078,18 +1023,17 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops void *p; p = alloc_hpage(ops); - printf("Share huge page over fork()..."); + ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ skip_settings_restore = true; - exit_status = 0; if (ops->check_huge(p, 1)) success("OK"); else fail("Fail"); - printf("Trigger CoW on page %d of %d...", + ksft_print_msg("Trigger CoW on page %d of %d...", hpage_pmd_nr - max_ptes_shared - 1, hpage_pmd_nr); ops->fault(p, 0, (hpage_pmd_nr - max_ptes_shared - 1) * page_size); if (ops->check_huge(p, 0)) @@ -1101,7 +1045,7 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops 1, ops, !c->enforce_pte_scan_limits); if (c->enforce_pte_scan_limits) { - printf("Trigger CoW on page %d of %d...", + ksft_print_msg("Trigger CoW on page %d of %d...", hpage_pmd_nr - max_ptes_shared, hpage_pmd_nr); ops->fault(p, 0, (hpage_pmd_nr - max_ptes_shared) * page_size); @@ -1120,15 +1064,16 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops } wait(&wstatus); - exit_status += WEXITSTATUS(wstatus); + exit_status = WEXITSTATUS(wstatus); - printf("Check if parent still has huge page..."); + ksft_print_msg("Check if parent still has huge page..."); if (ops->check_huge(p, 1)) success("OK"); else fail("Fail"); validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void madvise_collapse_existing_thps(struct collapse_context *c, @@ -1145,6 +1090,7 @@ static void madvise_collapse_existing_thps(struct collapse_context *c, __madvise_collapse("Re-collapse PMD-mapped hugepage", p, 1, ops, true); validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); + ksft_test_result_report(exit_status, "%s\n", __func__); } /* @@ -1172,6 +1118,7 @@ static void madvise_retracted_page_tables(struct collapse_context *c, true); validate_memory(p, 0, size); ops->cleanup_area(p, size); + ksft_test_result_report(exit_status, "%s\n", __func__); } static void usage(void) @@ -1280,10 +1227,8 @@ static int nr_test_cases; #define TEST(t, c, o) do { \ if (c && o) { \ - if (nr_test_cases >= MAX_TEST_CASES) { \ - printf("MAX_TEST_CASES is too small\n"); \ - exit(EXIT_FAILURE); \ - } \ + if (nr_test_cases >= MAX_TEST_CASES) \ + ksft_exit_fail_msg("MAX_TEST_CASES is too small\n"); \ test_cases[nr_test_cases++] = (struct test_case){ \ .ctx = c, \ .ops = o, \ @@ -1316,10 +1261,10 @@ int main(int argc, char **argv) .read_ahead_kb = 0, }; - if (!thp_is_enabled()) { - printf("Transparent Hugepages not available\n"); - return KSFT_SKIP; - } + ksft_print_header(); + + if (!thp_is_enabled()) + ksft_exit_skip("Transparent Hugepages not available\n"); parse_test_type(argc, argv); @@ -1327,10 +1272,8 @@ int main(int argc, char **argv) page_size = getpagesize(); hpage_pmd_size = read_pmd_pagesize(); - if (!hpage_pmd_size) { - printf("Reading PMD pagesize failed"); - exit(EXIT_FAILURE); - } + if (!hpage_pmd_size) + ksft_exit_fail_msg("Reading PMD pagesize failed\n"); hpage_pmd_nr = hpage_pmd_size / page_size; hpage_pmd_order = __builtin_ctz(hpage_pmd_nr); @@ -1346,8 +1289,6 @@ int main(int argc, char **argv) save_settings(); thp_push_settings(&default_settings); - alloc_at_fault(); - TEST(collapse_full, khugepaged_context, anon_ops); TEST(collapse_full, khugepaged_context, read_only_file_ops); TEST(collapse_full, khugepaged_context, read_write_file_read_ops); @@ -1425,11 +1366,13 @@ int main(int argc, char **argv) TEST(madvise_retracted_page_tables, madvise_context, read_write_file_read_ops); TEST(madvise_retracted_page_tables, madvise_context, shmem_ops); - exit_status = KSFT_PASS; + ksft_set_plan(nr_test_cases + 1); + + alloc_at_fault(); for (int i = 0; i < nr_test_cases; i++) { struct test_case *t = &test_cases[i]; - printf("\nRun test: %s (%s:%s)\n", t->desc, t->ctx->name, t->ops->name); + ksft_print_msg("\n# Run test: %s (%s:%s)\n", t->desc, t->ctx->name, t->ops->name); t->fn(t->ctx, t->ops); } From 4cc68d5f52de9614e2db70f43c52f012d1ebb5ad Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:27:59 +0300 Subject: [PATCH 4217/5214] selftests/mm: ksm_tests: use kselftest framework Convert ksm_tests to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-16-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/ksm_tests.c | 180 +++++++++++-------------- 1 file changed, 81 insertions(+), 99 deletions(-) diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c index a0b48b839d54..0a5b38d3281c 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -175,12 +175,12 @@ static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_ void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0); if (!map_ptr) { - perror("mmap"); + ksft_perror("mmap"); return NULL; } memset(map_ptr, data, map_size); if (mprotect(map_ptr, map_size, prot)) { - perror("mprotect"); + ksft_perror("mprotect"); munmap(map_ptr, map_size); return NULL; } @@ -201,11 +201,11 @@ static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout) if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan)) return 1; if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return 1; } if ((cur_time.tv_sec - start_time.tv_sec) > timeout) { - printf("Scan time limit exceeded\n"); + ksft_print_msg("Scan time limit exceeded\n"); return 1; } } @@ -218,12 +218,12 @@ static int ksm_merge_pages(int merge_type, void *addr, size_t size, { if (merge_type == KSM_MERGE_MADVISE) { if (madvise(addr, size, MADV_MERGEABLE)) { - perror("madvise"); + ksft_perror("madvise"); return 1; } } else if (merge_type == KSM_MERGE_PRCTL) { if (prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0)) { - perror("prctl"); + ksft_perror("prctl"); return 1; } } @@ -242,7 +242,7 @@ static int ksm_unmerge_pages(void *addr, size_t size, struct timespec start_time, int timeout) { if (madvise(addr, size, MADV_UNMERGEABLE)) { - perror("madvise"); + ksft_perror("madvise"); return 1; } return 0; @@ -324,7 +324,7 @@ static int check_ksm_merge(int merge_type, int mapping, int prot, struct timespec start_time; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } @@ -338,7 +338,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot, /* verify that the right number of pages are merged */ if (assert_ksm_pages_count(page_count)) { - printf("OK\n"); munmap(map_ptr, page_size * page_count); if (merge_type == KSM_MERGE_PRCTL) prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); @@ -346,7 +345,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot, } err_out: - printf("Not OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_FAIL; } @@ -358,7 +356,7 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout, int page_count = 2; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } @@ -380,13 +378,11 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout, /* check that unmerging was successful and 0 pages are currently merged */ if (assert_ksm_pages_count(0)) { - printf("OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_PASS; } err_out: - printf("Not OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_FAIL; } @@ -398,7 +394,7 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long struct timespec start_time; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } @@ -425,12 +421,10 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long else if (!use_zero_pages && !assert_ksm_pages_count(page_count)) goto err_out; - printf("OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_PASS; err_out: - printf("Not OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_FAIL; } @@ -465,16 +459,16 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo int first_node; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } if (numa_available() < 0) { - perror("NUMA support not enabled"); + ksft_print_msg("NUMA support not enabled\n"); return KSFT_SKIP; } if (numa_num_configured_nodes() <= 1) { - printf("At least 2 NUMA nodes must be available\n"); + ksft_print_msg("At least 2 NUMA nodes must be available\n"); return KSFT_SKIP; } if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes)) @@ -485,7 +479,7 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo numa1_map_ptr = numa_alloc_onnode(page_size, first_node); numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node)); if (!numa1_map_ptr || !numa2_map_ptr) { - perror("numa_alloc_onnode"); + ksft_perror("numa_alloc_onnode"); return KSFT_FAIL; } @@ -510,13 +504,11 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo numa_free(numa1_map_ptr, page_size); numa_free(numa2_map_ptr, page_size); - printf("OK\n"); return KSFT_PASS; err_out: numa_free(numa1_map_ptr, page_size); numa_free(numa2_map_ptr, page_size); - printf("Not OK\n"); return KSFT_FAIL; } @@ -529,7 +521,7 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot, int pagemap_fd, n_normal_pages, n_huge_pages; if (!thp_is_enabled()) { - printf("Transparent Hugepages not available\n"); + ksft_print_msg("Transparent Hugepages not available\n"); return KSFT_SKIP; } @@ -559,36 +551,35 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot, else n_huge_pages++; } - printf("Number of normal pages: %d\n", n_normal_pages); - printf("Number of huge pages: %d\n", n_huge_pages); + ksft_print_msg("Number of normal pages: %d\n", n_normal_pages); + ksft_print_msg("Number of huge pages: %d\n", n_huge_pages); memset(map_ptr, '*', len); if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) goto err_out; if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + (end_time.tv_nsec - start_time.tv_nsec); - printf("Total size: %lu MiB\n", map_size / MB); - printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, + ksft_print_msg("Total size: %lu MiB\n", map_size / MB); + ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, scan_time_ns % NSEC_PER_SEC); - printf("Average speed: %.3f MiB/s\n", (map_size / MB) / + ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) / ((double)scan_time_ns / NSEC_PER_SEC)); munmap(map_ptr_orig, len + HPAGE_SIZE); return KSFT_PASS; err_out: - printf("Not OK\n"); munmap(map_ptr_orig, len + HPAGE_SIZE); return KSFT_FAIL; } @@ -606,30 +597,29 @@ static int ksm_merge_time(int merge_type, int mapping, int prot, int timeout, si return KSFT_FAIL; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) goto err_out; if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + (end_time.tv_nsec - start_time.tv_nsec); - printf("Total size: %lu MiB\n", map_size / MB); - printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, + ksft_print_msg("Total size: %lu MiB\n", map_size / MB); + ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, scan_time_ns % NSEC_PER_SEC); - printf("Average speed: %.3f MiB/s\n", (map_size / MB) / + ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) / ((double)scan_time_ns / NSEC_PER_SEC)); munmap(map_ptr, map_size); return KSFT_PASS; err_out: - printf("Not OK\n"); munmap(map_ptr, map_size); return KSFT_FAIL; } @@ -646,37 +636,36 @@ static int ksm_unmerge_time(int merge_type, int mapping, int prot, int timeout, if (!map_ptr) return KSFT_FAIL; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) goto err_out; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout)) goto err_out; if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + (end_time.tv_nsec - start_time.tv_nsec); - printf("Total size: %lu MiB\n", map_size / MB); - printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, + ksft_print_msg("Total size: %lu MiB\n", map_size / MB); + ksft_print_msg("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, scan_time_ns % NSEC_PER_SEC); - printf("Average speed: %.3f MiB/s\n", (map_size / MB) / + ksft_print_msg("Average speed: %.3f MiB/s\n", (map_size / MB) / ((double)scan_time_ns / NSEC_PER_SEC)); munmap(map_ptr, map_size); return KSFT_PASS; err_out: - printf("Not OK\n"); munmap(map_ptr, map_size); return KSFT_FAIL; } @@ -695,24 +684,24 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size return KSFT_FAIL; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } for (size_t i = 0; i < page_count - 1; i = i + 2) memset(map_ptr + page_size * i, '-', 1); if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); return KSFT_FAIL; } cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + (end_time.tv_nsec - start_time.tv_nsec); - printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB); - printf("Not merged pages:\n"); - printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, + ksft_print_msg("Total size: %lu MiB\n\n", (page_size * page_count) / MB); + ksft_print_msg("Not merged pages:\n"); + ksft_print_msg("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, cow_time_ns % NSEC_PER_SEC); - printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) / + ksft_print_msg("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) / ((double)cow_time_ns / NSEC_PER_SEC)); /* Create 2000 pairs of duplicate pages */ @@ -724,30 +713,29 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size goto err_out; if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } for (size_t i = 0; i < page_count - 1; i = i + 2) memset(map_ptr + page_size * i, '-', 1); if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { - perror("clock_gettime"); + ksft_perror("clock_gettime"); goto err_out; } cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + (end_time.tv_nsec - start_time.tv_nsec); - printf("Merged pages:\n"); - printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, + ksft_print_msg("Merged pages:\n"); + ksft_print_msg("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, cow_time_ns % NSEC_PER_SEC); - printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) / + ksft_print_msg("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) / ((double)cow_time_ns / NSEC_PER_SEC)); munmap(map_ptr, page_size * page_count); return KSFT_PASS; err_out: - printf("Not OK\n"); munmap(map_ptr, page_size * page_count); return KSFT_FAIL; } @@ -765,6 +753,10 @@ int main(int argc, char *argv[]) bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT; bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; long size_MB = 0; + const char *test_descr = "KSM merging"; + + ksft_print_header(); + ksft_set_plan(1); while ((opt = getopt(argc, argv, "dha:p:l:z:m:s:t:MUZNPCHD")) != -1) { switch (opt) { @@ -773,17 +765,13 @@ int main(int argc, char *argv[]) break; case 'p': page_count = atol(optarg); - if (page_count <= 0) { - printf("The number of pages must be greater than 0\n"); - return KSFT_FAIL; - } + if (page_count <= 0) + ksft_exit_fail_msg("The number of pages must be greater than 0\n"); break; case 'l': ksm_scan_limit_sec = atoi(optarg); - if (ksm_scan_limit_sec <= 0) { - printf("Timeout value must be greater than 0\n"); - return KSFT_FAIL; - } + if (ksm_scan_limit_sec <= 0) + ksft_exit_fail_msg("Timeout value must be greater than 0\n"); break; case 'h': print_help(); @@ -805,19 +793,15 @@ int main(int argc, char *argv[]) break; case 's': size_MB = atoi(optarg); - if (size_MB <= 0) { - printf("Size must be greater than 0\n"); - return KSFT_FAIL; - } + if (size_MB <= 0) + ksft_exit_fail_msg("Size must be greater than 0\n"); break; case 't': { int tmp = atoi(optarg); - if (tmp < 0 || tmp > KSM_MERGE_LAST) { - printf("Invalid merge type\n"); - return KSFT_FAIL; - } + if (tmp < 0 || tmp > KSM_MERGE_LAST) + ksft_exit_fail_msg("Invalid merge type\n"); merge_type = tmp; } break; @@ -845,82 +829,80 @@ int main(int argc, char *argv[]) test_name = KSM_COW_TIME; break; default: - return KSFT_FAIL; + ksft_exit_fail_msg("Unknown option\n"); } } if (prot == 0) prot = str_to_prot(KSM_PROT_STR_DEFAULT); - if (access(KSM_SYSFS_PATH, F_OK)) { - printf("Config KSM not enabled\n"); - return KSFT_SKIP; - } + if (access(KSM_SYSFS_PATH, F_OK)) + ksft_exit_skip("Config KSM not enabled\n"); - if (ksm_save_def(&ksm_sysfs_old)) { - printf("Cannot save default tunables\n"); - return KSFT_FAIL; - } + if (ksm_save_def(&ksm_sysfs_old)) + ksft_exit_fail_msg("Cannot save default tunables\n"); if (ksm_write_sysfs(KSM_FP("run"), 2) || ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) || numa_available() ? 0 : ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) || ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count)) - return KSFT_FAIL; + ksft_exit_fail_msg("Cannot set up KSM tunables\n"); switch (test_name) { case CHECK_KSM_MERGE: + test_descr = "KSM merging"; ret = check_ksm_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, ksm_scan_limit_sec, page_size); break; case CHECK_KSM_UNMERGE: + test_descr = "KSM unmerging"; ret = check_ksm_unmerge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, page_size); break; case CHECK_KSM_ZERO_PAGE_MERGE: + test_descr = "KSM zero page merging"; ret = check_ksm_zero_page_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, ksm_scan_limit_sec, use_zero_pages, page_size); break; case CHECK_KSM_NUMA_MERGE: + test_descr = "KSM NUMA merging"; ret = check_ksm_numa_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, merge_across_nodes, page_size); break; case KSM_MERGE_TIME: - if (size_MB == 0) { - printf("Option '-s' is required.\n"); - return KSFT_FAIL; - } + if (size_MB == 0) + ksft_exit_fail_msg("Option '-s' is required\n"); + test_descr = "KSM merge time"; ret = ksm_merge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, size_MB); break; case KSM_MERGE_TIME_HUGE_PAGES: - if (size_MB == 0) { - printf("Option '-s' is required.\n"); - return KSFT_FAIL; - } + if (size_MB == 0) + ksft_exit_fail_msg("Option '-s' is required\n"); + test_descr = "KSM merge time with huge pages"; ret = ksm_merge_hugepages_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, size_MB); break; case KSM_UNMERGE_TIME: - if (size_MB == 0) { - printf("Option '-s' is required.\n"); - return KSFT_FAIL; - } + if (size_MB == 0) + ksft_exit_fail_msg("Option '-s' is required\n"); + test_descr = "KSM unmerge time"; ret = ksm_unmerge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, size_MB); break; case KSM_COW_TIME: + test_descr = "KSM COW time"; ret = ksm_cow_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, page_size); break; } - if (ksm_restore(&ksm_sysfs_old)) { - printf("Cannot restore default tunables\n"); - return KSFT_FAIL; - } + if (ksm_restore(&ksm_sysfs_old)) + ksft_print_msg("Cannot restore default tunables\n"); - return ret; + ksft_test_result_report(ret, "%s\n", test_descr); + + ksft_finished(); } From a932f05e2eae1473579c7e17dcf6c6e4b73c0a35 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:00 +0300 Subject: [PATCH 4218/5214] selftests/mm: protection_keys: use descriptive test names in the output Replace the numeric test index in TAP output with the actual test function name. Use a structure containing function pointer and its name rather than only the function pointer in the pkey_tests array. Link: https://lore.kernel.org/20260511162840.375890-17-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/protection_keys.c | 55 +++++++++++--------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c index 2085982dba69..80c6124e8378 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -1692,29 +1692,36 @@ static void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey) pkey_assert(sret < 0); } -static void (*pkey_tests[])(int *ptr, u16 pkey) = { - test_read_of_write_disabled_region, - test_read_of_access_disabled_region, - test_read_of_access_disabled_region_with_page_already_mapped, - test_write_of_write_disabled_region, - test_write_of_write_disabled_region_with_page_already_mapped, - test_write_of_access_disabled_region, - test_write_of_access_disabled_region_with_page_already_mapped, - test_kernel_write_of_access_disabled_region, - test_kernel_write_of_write_disabled_region, - test_kernel_gup_of_access_disabled_region, - test_kernel_gup_write_to_write_disabled_region, - test_executing_on_unreadable_memory, - test_implicit_mprotect_exec_only_memory, - test_mprotect_with_pkey_0, - test_ptrace_of_child, - test_pkey_init_state, - test_pkey_syscalls_on_non_allocated_pkey, - test_pkey_syscalls_bad_args, - test_pkey_alloc_exhaust, - test_pkey_alloc_free_attach_pkey0, +struct pkey_test { + void (*func)(int *ptr, u16 pkey); + const char *name; +}; + +#define PKEY_TEST(fn) { fn, #fn } + +static struct pkey_test pkey_tests[] = { + PKEY_TEST(test_read_of_write_disabled_region), + PKEY_TEST(test_read_of_access_disabled_region), + PKEY_TEST(test_read_of_access_disabled_region_with_page_already_mapped), + PKEY_TEST(test_write_of_write_disabled_region), + PKEY_TEST(test_write_of_write_disabled_region_with_page_already_mapped), + PKEY_TEST(test_write_of_access_disabled_region), + PKEY_TEST(test_write_of_access_disabled_region_with_page_already_mapped), + PKEY_TEST(test_kernel_write_of_access_disabled_region), + PKEY_TEST(test_kernel_write_of_write_disabled_region), + PKEY_TEST(test_kernel_gup_of_access_disabled_region), + PKEY_TEST(test_kernel_gup_write_to_write_disabled_region), + PKEY_TEST(test_executing_on_unreadable_memory), + PKEY_TEST(test_implicit_mprotect_exec_only_memory), + PKEY_TEST(test_mprotect_with_pkey_0), + PKEY_TEST(test_ptrace_of_child), + PKEY_TEST(test_pkey_init_state), + PKEY_TEST(test_pkey_syscalls_on_non_allocated_pkey), + PKEY_TEST(test_pkey_syscalls_bad_args), + PKEY_TEST(test_pkey_alloc_exhaust), + PKEY_TEST(test_pkey_alloc_free_attach_pkey0), #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) - test_ptrace_modifies_pkru, + PKEY_TEST(test_ptrace_modifies_pkru), #endif }; @@ -1735,7 +1742,7 @@ static void run_tests_once(void) dprintf1("test %d starting with pkey: %d\n", test_nr, pkey); ptr = malloc_pkey(PAGE_SIZE, prot, pkey); dprintf1("test %d starting...\n", test_nr); - pkey_tests[test_nr](ptr, pkey); + pkey_tests[test_nr].func(ptr, pkey); dprintf1("freeing test memory: %p\n", ptr); free_pkey_malloc(ptr); sys_pkey_free(pkey); @@ -1746,7 +1753,7 @@ static void run_tests_once(void) tracing_off(); close_test_fds(); - printf("test %2d PASSED (iteration %d)\n", test_nr, iteration_nr); + printf("test %s PASSED (iteration %d)\n", pkey_tests[test_nr].name, iteration_nr); dprintf1("======================\n\n"); } iteration_nr++; From 04d0c0092b427dd28f57b57f001be9110cb5acfe Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:01 +0300 Subject: [PATCH 4219/5214] selftests/mm: protection_keys: use kselftest framework Convert protection_keys test to use kselftest framework for reporting and tracking successful and failing runs. Adjust dprintf0() printouts to use "#" in the beginning of the line for TAP compatibility. Link: https://lore.kernel.org/20260511162840.375890-18-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/pkey-helpers.h | 15 +++++------ tools/testing/selftests/mm/protection_keys.c | 26 +++++++++++--------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/mm/pkey-helpers.h b/tools/testing/selftests/mm/pkey-helpers.h index 7c29f075e40b..2c377f4e9df1 100644 --- a/tools/testing/selftests/mm/pkey-helpers.h +++ b/tools/testing/selftests/mm/pkey-helpers.h @@ -71,13 +71,14 @@ static inline void sigsafe_printf(const char *format, ...) extern void abort_hooks(void); #define pkey_assert(condition) do { \ if (!(condition)) { \ - dprintf0("assert() at %s::%d test_nr: %d iteration: %d\n", \ - __FILE__, __LINE__, \ - test_nr, iteration_nr); \ - dprintf0("errno at assert: %d", errno); \ - abort_hooks(); \ - exit(__LINE__); \ - } \ + dprintf0("# assert() at %s::%d test_nr: %d iteration: %d\n", \ + __FILE__, __LINE__, \ + test_nr, iteration_nr); \ + dprintf0("# errno at assert: %d\n", errno); \ + abort_hooks(); \ + ksft_exit_fail_msg("test %d (iteration %d)\n", \ + test_nr, iteration_nr); \ + } \ } while (0) #define barrier() __asm__ __volatile__("": : :"memory") diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c index 80c6124e8378..d617b41dda6b 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -136,6 +136,7 @@ static void tracing_off(void) void abort_hooks(void) { + fflush(stdout); fprintf(stderr, "running %s()...\n", __func__); tracing_off(); #ifdef SLEEP_ON_ABORT @@ -370,8 +371,8 @@ static void signal_handler(int signum, siginfo_t *si, void *vucontext) if ((si->si_code == SEGV_MAPERR) || (si->si_code == SEGV_ACCERR) || (si->si_code == SEGV_BNDERR)) { - printf("non-PK si_code, exiting...\n"); - exit(4); + dprintf0("# non-PK si_code: %d, exiting...\n", si->si_code); + exit(1); } si_pkey_ptr = siginfo_get_pkey_ptr(si); @@ -719,7 +720,7 @@ static void setup_hugetlbfs(void) long hpagesz_mb; if (geteuid() != 0) { - fprintf(stderr, "WARNING: not run as root, can not do hugetlb test\n"); + ksft_print_msg("WARNING: not run as root, can not do hugetlb test\n"); return; } @@ -855,7 +856,7 @@ void expected_pkey_fault(int pkey) #define do_not_expect_pkey_fault(msg) do { \ if (last_pkey_faults != pkey_faults) \ - dprintf0("unexpected PKey fault: %s\n", msg); \ + dprintf0("# unexpected PKey fault: %s\n", msg); \ pkey_assert(last_pkey_faults == pkey_faults); \ } while (0) @@ -1753,7 +1754,7 @@ static void run_tests_once(void) tracing_off(); close_test_fds(); - printf("test %s PASSED (iteration %d)\n", pkey_tests[test_nr].name, iteration_nr); + ksft_test_result_pass("test %s (iteration %d)\n", pkey_tests[test_nr].name, iteration_nr); dprintf1("======================\n\n"); } iteration_nr++; @@ -1773,27 +1774,30 @@ int main(void) setup_handlers(); - printf("has pkeys: %d\n", pkeys_supported); + ksft_print_header(); if (!pkeys_supported) { int size = PAGE_SIZE; int *ptr; - printf("running PKEY tests for unsupported CPU/OS\n"); + ksft_set_plan(1); + ksft_print_msg("running PKEY tests for unsupported CPU/OS\n"); ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); assert(ptr != (void *)-1); test_mprotect_pkey_on_unsupported_cpu(ptr, 1); - exit(0); + ksft_test_result_pass("pkey on unsupported CPU/OS\n"); + ksft_finished(); } + ksft_set_plan(ARRAY_SIZE(pkey_tests) * nr_iterations); + pkey_setup_shadow(); - printf("startup pkey_reg: %016llx\n", read_pkey_reg()); + ksft_print_msg("startup pkey_reg: %016llx\n", read_pkey_reg()); setup_hugetlbfs(); while (nr_iterations-- > 0) run_tests_once(); - printf("done (all tests OK)\n"); - return 0; + ksft_finished(); } From 4cf3a2657b2a7917cff2ff5383996245675499af Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:02 +0300 Subject: [PATCH 4220/5214] selftests/mm: uffd-common: use kselftest framework Update err() and errexit() to use ksft_print_msg() and ksft_exit_fail(). This is preparatory change required to update userfaulfd tests to use kselftest framework. Link: https://lore.kernel.org/20260511162840.375890-19-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Luiz Capitulino Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-common.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h index 844a85ab31eb..0723843a7626 100644 --- a/tools/testing/selftests/mm/uffd-common.h +++ b/tools/testing/selftests/mm/uffd-common.h @@ -40,21 +40,20 @@ #define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY) -#define _err(fmt, ...) \ - do { \ - int ret = errno; \ - fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__); \ - fprintf(stderr, " (errno=%d, @%s:%d)\n", \ - ret, __FILE__, __LINE__); \ +#define _err(fmt, ...) \ + do { \ + int ret = errno; \ + ksft_print_msg("ERROR: " fmt " (errno=%d, @%s:%d)\n", \ + ##__VA_ARGS__, ret, __FILE__, __LINE__); \ } while (0) -#define errexit(exitcode, fmt, ...) \ +#define errexit(fmt, ...) \ do { \ _err(fmt, ##__VA_ARGS__); \ - exit(exitcode); \ + ksft_exit_fail(); \ } while (0) -#define err(fmt, ...) errexit(1, fmt, ##__VA_ARGS__) +#define err(fmt, ...) errexit(fmt, ##__VA_ARGS__) struct uffd_global_test_opts { unsigned long nr_parallel, nr_pages, nr_pages_per_cpu, page_size; From a7d3925151723ea916f03ac0da244b3a92541a71 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:03 +0300 Subject: [PATCH 4221/5214] selftests/mm: uffd-stress: use kselftest framework Convert uffd-stress test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-20-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Mark Brown Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-stress.c | 40 +++++++++++------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 700fbaa18d44..0ce493dac354 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -286,18 +286,12 @@ static int userfaultfd_stress(uffd_global_test_opts_t *gopts) pthread_attr_setstacksize(&attr, 16*1024*1024); while (bounces--) { - printf("bounces: %d, mode:", bounces); - if (bounces & BOUNCE_RANDOM) - printf(" rnd"); - if (bounces & BOUNCE_RACINGFAULTS) - printf(" racing"); - if (bounces & BOUNCE_VERIFY) - printf(" ver"); - if (bounces & BOUNCE_POLL) - printf(" poll"); - else - printf(" read"); - printf(", "); + ksft_print_msg("bounces: %d, mode:%s%s%s%s, ", + bounces, + bounces & BOUNCE_RANDOM ? " rnd" : "", + bounces & BOUNCE_RACINGFAULTS ? " racing" : "", + bounces & BOUNCE_VERIFY ? " ver" : "", + bounces & BOUNCE_POLL ? " poll" : " read"); fflush(stdout); if (bounces & BOUNCE_POLL) @@ -461,6 +455,9 @@ int main(int argc, char **argv) if (argc < 4) usage(); + ksft_print_header(); + ksft_set_plan(1); + if (signal(SIGALRM, sigalrm) == SIG_ERR) err("failed to arm SIGALRM"); alarm(ALARM_INTERVAL_SECS); @@ -484,10 +481,8 @@ int main(int argc, char **argv) * for racy extra reservation of hugepages. */ if (gopts->test_type == TEST_HUGETLB && - get_free_hugepages() < 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1) { - printf("skip: Skipping userfaultfd... not enough hugepages\n"); - return KSFT_SKIP; - } + get_free_hugepages() < 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1) + ksft_exit_skip("Skipping userfaultfd... not enough hugepages\n"); gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; if (!gopts->nr_pages_per_cpu) { @@ -503,9 +498,12 @@ int main(int argc, char **argv) } gopts->nr_pages = gopts->nr_pages_per_cpu * gopts->nr_parallel; - printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", - gopts->nr_pages, gopts->nr_pages_per_cpu); - return userfaultfd_stress(gopts); + ksft_print_msg("nr_pages: %lu, nr_pages_per_cpu: %lu\n", + gopts->nr_pages, gopts->nr_pages_per_cpu); + + ksft_test_result(!userfaultfd_stress(gopts), + "uffd-stress %s\n", argv[1]); + ksft_finished(); } #else /* __NR_userfaultfd */ @@ -514,8 +512,8 @@ int main(int argc, char **argv) int main(void) { - printf("skip: Skipping userfaultfd test (missing __NR_userfaultfd)\n"); - return KSFT_SKIP; + ksft_print_header(); + ksft_exit_skip("missing __NR_userfaultfd definition\n"); } #endif /* __NR_userfaultfd */ From 462de7f58c2841489df9e8af23cb1d3e2fe05003 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:04 +0300 Subject: [PATCH 4222/5214] selftests/mm: uffd-unit-tests: use kselftest framework Convert uffd-unit-tests to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-21-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Donet Tom Reviewed-by: Mark Brown Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-unit-tests.c | 105 +++++++++---------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index 6f5e404a446c..db7c26835487 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -86,47 +86,28 @@ typedef struct { uffd_test_case_ops_t *test_case_ops; } uffd_test_case_t; -static void uffd_test_report(void) -{ - printf("Userfaults unit tests: pass=%u, skip=%u, fail=%u (total=%u)\n", - ksft_get_pass_cnt(), - ksft_get_xskip_cnt(), - ksft_get_fail_cnt(), - ksft_test_num()); -} +static char current_test[256]; static void uffd_test_pass(void) { - printf("done\n"); - ksft_inc_pass_cnt(); + ksft_test_result_pass("%s\n", current_test); } #define uffd_test_start(...) do { \ - printf("Testing "); \ - printf(__VA_ARGS__); \ - printf("... "); \ - fflush(stdout); \ + snprintf(current_test, sizeof(current_test), __VA_ARGS__); \ } while (0) -#define uffd_test_fail(...) do { \ - printf("failed [reason: "); \ - printf(__VA_ARGS__); \ - printf("]\n"); \ - ksft_inc_fail_cnt(); \ +#define uffd_test_fail(fmt, ...) do { \ + ksft_print_msg("failed reason: [" fmt "]\n", ##__VA_ARGS__); \ + ksft_test_result_fail("%s\n", current_test); \ } while (0) static void uffd_test_skip(const char *message) { - printf("skipped [reason: %s]\n", message); - ksft_inc_xskip_cnt(); + ksft_test_result_skip("%s (%s)\n", current_test, message); } -/* - * Returns 1 if specific userfaultfd supported, 0 otherwise. Note, we'll - * return 1 even if some test failed as long as uffd supported, because in - * that case we still want to proceed with the rest uffd unit tests. - */ -static int test_uffd_api(bool use_dev) +static void test_uffd_api(bool use_dev) { struct uffdio_api uffdio_api; int uffd; @@ -140,7 +121,7 @@ static int test_uffd_api(bool use_dev) uffd = uffd_open_sys(UFFD_FLAGS); if (uffd < 0) { uffd_test_skip("cannot open userfaultfd handle"); - return 0; + return; } /* Test wrong UFFD_API */ @@ -177,8 +158,6 @@ static int test_uffd_api(bool use_dev) uffd_test_pass(); out: close(uffd); - /* We have a valid uffd handle */ - return 1; } @@ -1701,6 +1680,26 @@ static void usage(const char *prog) exit(KSFT_FAIL); } +static int uffd_count_tests(int n_tests, int n_mems, const char *test_filter) +{ + uffd_test_case_t *test; + int i, j, count = 0; + + if (!test_filter) + count += 2; /* test_uffd_api(false) + test_uffd_api(true) */ + + for (i = 0; i < n_tests; i++) { + test = &uffd_tests[i]; + if (test_filter && !strstr(test->name, test_filter)) + continue; + for (j = 0; j < n_mems; j++) + if (test->mem_targets & mem_types[j].mem_flag) + count++; + } + + return count; +} + int main(int argc, char *argv[]) { int n_tests = sizeof(uffd_tests) / sizeof(uffd_test_case_t); @@ -1711,8 +1710,7 @@ int main(int argc, char *argv[]) mem_type_t *mem_type; uffd_test_args_t args; const char *errmsg; - int has_uffd, opt; - int i, j; + int i, j, opt; while ((opt = getopt(argc, argv, "f:hl")) != -1) { switch (opt) { @@ -1730,24 +1728,28 @@ int main(int argc, char *argv[]) } } - if (!test_filter && !list_only) { - has_uffd = test_uffd_api(false); - has_uffd |= test_uffd_api(true); - - if (!has_uffd) { - printf("Userfaultfd not supported or unprivileged, skip all tests\n"); - exit(KSFT_SKIP); + if (list_only) { + for (i = 0; i < n_tests; i++) { + test = &uffd_tests[i]; + if (test_filter && !strstr(test->name, test_filter)) + continue; + printf("%s\n", test->name); } + return KSFT_PASS; + } + + ksft_print_header(); + ksft_set_plan(uffd_count_tests(n_tests, n_mems, test_filter)); + + if (!test_filter) { + test_uffd_api(false); + test_uffd_api(true); } for (i = 0; i < n_tests; i++) { test = &uffd_tests[i]; if (test_filter && !strstr(test->name, test_filter)) continue; - if (list_only) { - printf("%s\n", test->name); - continue; - } for (j = 0; j < n_mems; j++) { mem_type = &mem_types[j]; @@ -1758,6 +1760,10 @@ int main(int argc, char *argv[]) uffd_test_ops = mem_type->mem_ops; uffd_test_case_ops = test->test_case_ops; + if (!(test->mem_targets & mem_type->mem_flag)) + continue; + + uffd_test_start("%s on %s", test->name, mem_type->name); if (mem_type->mem_flag & (MEM_HUGETLB_PRIVATE | MEM_HUGETLB)) { gopts.page_size = default_huge_page_size(); if (gopts.page_size == 0) { @@ -1777,10 +1783,6 @@ int main(int argc, char *argv[]) /* Initialize test arguments */ args.mem_type = mem_type; - if (!(test->mem_targets & mem_type->mem_flag)) - continue; - - uffd_test_start("%s on %s", test->name, mem_type->name); if (!uffd_feature_supported(test)) { uffd_test_skip("feature missing"); continue; @@ -1794,10 +1796,7 @@ int main(int argc, char *argv[]) } } - if (!list_only) - uffd_test_report(); - - return ksft_get_fail_cnt() ? KSFT_FAIL : KSFT_PASS; + ksft_finished(); } #else /* __NR_userfaultfd */ @@ -1806,8 +1805,8 @@ int main(int argc, char *argv[]) int main(void) { - printf("Skipping %s (missing __NR_userfaultfd)\n", __file__); - return KSFT_SKIP; + ksft_print_header(); + ksft_exit_skip("missing __NR_userfaultfd definition\n"); } #endif /* __NR_userfaultfd */ From 7b35a64019262667d00885ca789e90d0453b7cd6 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:05 +0300 Subject: [PATCH 4223/5214] selftests/mm: va_high_addr_switch: use kselftest framework Convert va_high_addr_switch test to use kselftest framework for reporting and tracking successful and failing runs. Link: https://lore.kernel.org/20260511162840.375890-22-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../selftests/mm/va_high_addr_switch.c | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c index 51401e081b20..5d38735ea60e 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -257,40 +257,35 @@ void testcases_init(void) switch_hint = addr_switch_hint; } -static int run_test(struct testcase *test, int count) +static void run_test(struct testcase *test, int count) { void *p; - int i, ret = KSFT_PASS; + int i; for (i = 0; i < count; i++) { struct testcase *t = test + i; p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0); - - printf("%s: %p - ", t->msg, p); - if (p == MAP_FAILED) { - printf("FAILED\n"); - ret = KSFT_FAIL; + ksft_perror("MAP_FAILED"); + ksft_test_result_fail("%s\n", t->msg); continue; } if (t->low_addr_required && p >= (void *)(switch_hint)) { - printf("FAILED\n"); - ret = KSFT_FAIL; + ksft_print_msg("%p not below switch hint\n", p); + ksft_test_result_fail("%s\n", t->msg); } else { /* * Do a dereference of the address returned so that we catch * bugs in page fault handling */ memset(p, 0, t->size); - printf("OK\n"); + ksft_test_result_pass("%s\n", t->msg); } if (!t->keep_mapped) munmap(p, t->size); } - - return ret; } #ifdef __aarch64__ @@ -322,19 +317,23 @@ static int supported_arch(void) int main(int argc, char **argv) { - int ret, hugetlb_ret = KSFT_PASS; + bool run_hugetlb = false; + + ksft_print_header(); if (!supported_arch()) - return KSFT_SKIP; + ksft_exit_skip("Architecture not supported\n"); + + if (argc == 2 && !strcmp(argv[1], "--run-hugetlb")) + run_hugetlb = true; testcases_init(); - ret = run_test(testcases, sz_testcases); - if (argc == 2 && !strcmp(argv[1], "--run-hugetlb")) - hugetlb_ret = run_test(hugetlb_testcases, sz_hugetlb_testcases); + ksft_set_plan(sz_testcases + (run_hugetlb ? sz_hugetlb_testcases : 0)); - if (ret == KSFT_PASS && hugetlb_ret == KSFT_PASS) - return KSFT_PASS; - else - return KSFT_FAIL; + run_test(testcases, sz_testcases); + if (run_hugetlb) + run_test(hugetlb_testcases, sz_hugetlb_testcases); + + ksft_finished(); } From d4cdf641b0dc749b5e88474a11543a9479e1f766 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:06 +0300 Subject: [PATCH 4224/5214] selftests/mm: add atexit() and signal handlers to thp_settings khugepaged registers atexit() and signal handlers that ensure that THP settings are restored regardless of how the test exited. Make these handlers available for all users of thp_settings. The call to thp_save_settings() installs thp_restore_settings as the atexit() callback and makes sure that signals that kill a process would still call exit() and atexit() callback. Update child process in khugepaged tests using thp_settings to use _exit() instead of exit() to avoid altering THP settings in the middle of a test. Remove redundant THP cleanup from folio_split_race_test.c. Link: https://lore.kernel.org/20260511162840.375890-23-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Sarthak Sharma Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/cow.c | 21 ++++------ .../selftests/mm/folio_split_race_test.c | 23 ----------- tools/testing/selftests/mm/khugepaged.c | 41 ++----------------- tools/testing/selftests/mm/thp_settings.c | 27 +++++++++++- tools/testing/selftests/mm/uffd-wp-mremap.c | 4 -- 5 files changed, 38 insertions(+), 78 deletions(-) diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index d9c69c04b67d..6abdcb30aba8 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -202,7 +202,7 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect, log_test_result(KSFT_FAIL); goto close_comm_pipes; } else if (!ret) { - exit(fn(mem, size, &comm_pipes)); + _exit(fn(mem, size, &comm_pipes)); } while (read(comm_pipes.child_ready[0], &buf, 1) != 1) @@ -333,7 +333,7 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size, ; /* Modify page content in the child. */ memset(mem, 0xff, size); - exit(0); + _exit(0); } if (!before_fork) { @@ -480,7 +480,7 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork) write(comm_pipes.child_ready[1], "0", 1); while (read(comm_pipes.parent_ready[0], &buf, 1) != 1) ; - exit(0); + _exit(0); } while (read(comm_pipes.child_ready[0], &buf, 1) != 1) @@ -645,7 +645,7 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test, write(comm_pipes.child_ready[1], "0", 1); while (read(comm_pipes.parent_ready[0], &buf, 1) != 1) ; - exit(0); + _exit(0); } /* Wait until our child is ready. */ @@ -956,7 +956,7 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run, size_t thpsize) log_test_result(KSFT_FAIL); goto munmap; } else if (!ret) { - exit(0); + _exit(0); } wait(&ret); /* Allow for sharing all pages again. */ @@ -1347,13 +1347,13 @@ static void do_test_anon_thp_collapse(char *mem, size_t size, switch (test) { case ANON_THP_COLLAPSE_UNSHARED: case ANON_THP_COLLAPSE_FULLY_SHARED: - exit(child_memcmp_fn(mem, size, &comm_pipes)); + _exit(child_memcmp_fn(mem, size, &comm_pipes)); break; case ANON_THP_COLLAPSE_LOWER_SHARED: - exit(child_memcmp_fn(mem, size / 2, &comm_pipes)); + _exit(child_memcmp_fn(mem, size / 2, &comm_pipes)); break; case ANON_THP_COLLAPSE_UPPER_SHARED: - exit(child_memcmp_fn(mem + size / 2, size / 2, + _exit(child_memcmp_fn(mem + size / 2, size / 2, &comm_pipes)); break; default: @@ -1911,10 +1911,5 @@ int main(int argc, char **argv) run_anon_thp_test_cases(); run_non_anon_test_cases(); - if (pmdsize) { - /* Only if THP is supported. */ - thp_restore_settings(); - } - ksft_finished(); } diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/testing/selftests/mm/folio_split_race_test.c index ff026f183ac7..390fd97c1bd9 100644 --- a/tools/testing/selftests/mm/folio_split_race_test.c +++ b/tools/testing/selftests/mm/folio_split_race_test.c @@ -226,23 +226,6 @@ static uint64_t run_iteration(void) return reader_failures; } -static void thp_cleanup_handler(int signum) -{ - thp_restore_settings(); - /* - * Restore default handler and re-raise the signal to exit. - * This is to ensure the test process exits with the correct - * status code corresponding to the signal. - */ - signal(signum, SIG_DFL); - raise(signum); -} - -static void thp_settings_cleanup(void) -{ - thp_restore_settings(); -} - int main(void) { struct thp_settings current_settings; @@ -261,12 +244,6 @@ int main(void) ksft_exit_skip("Please run the test as root\n"); thp_save_settings(); - /* make sure thp settings are restored */ - if (atexit(thp_settings_cleanup) != 0) - ksft_exit_fail_msg("atexit failed\n"); - - signal(SIGINT, thp_cleanup_handler); - signal(SIGTERM, thp_cleanup_handler); thp_read_settings(¤t_settings); current_settings.shmem_enabled = SHMEM_ADVISE; diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index 61c3b2c42544..ac2433f19b1e 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -80,7 +80,6 @@ struct file_info { }; static struct file_info finfo; -static bool skip_settings_restore; static int exit_status; static void success(const char *msg) @@ -101,26 +100,6 @@ static void skip(const char *msg) exit_status = KSFT_SKIP; } -static void restore_settings_atexit(void) -{ - if (skip_settings_restore) - return; - - ksft_print_msg("Restore THP and khugepaged settings..."); - thp_restore_settings(); - success("OK"); - - skip_settings_restore = true; - ksft_print_cnts(); - exit(exit_status); -} - -static void restore_settings(int sig) -{ - /* exit() will invoke the restore_settings_atexit handler. */ - exit(sig ? KSFT_FAIL : exit_status); -} - static void save_settings(void) { ksft_print_msg("Save THP and khugepaged settings..."); @@ -131,12 +110,6 @@ static void save_settings(void) thp_save_settings(); success("OK"); - - atexit(restore_settings_atexit); - signal(SIGTERM, restore_settings); - signal(SIGINT, restore_settings); - signal(SIGHUP, restore_settings); - signal(SIGQUIT, restore_settings); } static void get_finfo(const char *dir) @@ -938,8 +911,6 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) ksft_print_msg("Share small page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 0)) success("OK"); else @@ -951,7 +922,7 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) validate_memory(p, 0, page_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -976,8 +947,6 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 1)) success("OK"); else @@ -1000,7 +969,7 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -1026,8 +995,6 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 1)) success("OK"); else @@ -1060,7 +1027,7 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -1376,5 +1343,5 @@ int main(int argc, char **argv) t->fn(t->ctx, t->ops); } - restore_settings(0); + ksft_finished(); } diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/thp_settings.c index e748ebfb3d4e..9bfa5dc42624 100644 --- a/tools/testing/selftests/mm/thp_settings.c +++ b/tools/testing/selftests/mm/thp_settings.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -15,6 +16,7 @@ static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH]; static int settings_index; static struct thp_settings saved_settings; static char dev_queue_read_ahead_path[PATH_MAX]; +static bool thp_settings_saved; static const char * const thp_enabled_strings[] = { "never", @@ -298,12 +300,35 @@ void thp_pop_settings(void) void thp_restore_settings(void) { - thp_write_settings(&saved_settings); + if (thp_settings_saved) + thp_write_settings(&saved_settings); +} + +static void thp_restore_settings_atexit(void) +{ + thp_restore_settings(); +} + +static void thp_restore_settings_sighandler(int sig) +{ + /* exit() will invoke the thp_restore_settings_atexit handler. */ + exit(KSFT_FAIL); } void thp_save_settings(void) { thp_read_settings(&saved_settings); + thp_settings_saved = true; + + /* + * setup exit hooks to make sure THP settings are restored on graceful + * and error exits and signals + */ + atexit(thp_restore_settings_atexit); + signal(SIGTERM, thp_restore_settings_sighandler); + signal(SIGINT, thp_restore_settings_sighandler); + signal(SIGHUP, thp_restore_settings_sighandler); + signal(SIGQUIT, thp_restore_settings_sighandler); } void thp_set_read_ahead_path(char *path) diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index 17186d4a4147..516c35d236e1 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -368,10 +368,6 @@ int main(int argc, char **argv) tc->swapout, tc->hugetlb); } - /* If THP is supported, restore original THP settings. */ - if (nr_thpsizes) - thp_restore_settings(); - i = ksft_get_fail_cnt(); if (i) ksft_exit_fail_msg("%d out of %d tests failed\n", From 81afb1ee7b2fb8b533aaa5cbd907fd000e60f193 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:07 +0300 Subject: [PATCH 4225/5214] selftests/mm: rename thp_settings.[ch] to hugepage_settings.[ch] ... for upcoming addition of HugeTLB helpers. Link: https://lore.kernel.org/20260511162840.375890-24-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 4 ++-- tools/testing/selftests/mm/cow.c | 2 +- tools/testing/selftests/mm/folio_split_race_test.c | 2 +- tools/testing/selftests/mm/guard-regions.c | 2 +- .../selftests/mm/{thp_settings.c => hugepage_settings.c} | 2 +- .../selftests/mm/{thp_settings.h => hugepage_settings.h} | 6 +++--- tools/testing/selftests/mm/khugepaged.c | 2 +- tools/testing/selftests/mm/ksm_tests.c | 2 +- tools/testing/selftests/mm/migration.c | 2 +- tools/testing/selftests/mm/prctl_thp_disable.c | 2 +- tools/testing/selftests/mm/soft-dirty.c | 2 +- tools/testing/selftests/mm/split_huge_page_test.c | 2 +- tools/testing/selftests/mm/transhuge-stress.c | 2 +- tools/testing/selftests/mm/uffd-wp-mremap.c | 2 +- 14 files changed, 17 insertions(+), 17 deletions(-) rename tools/testing/selftests/mm/{thp_settings.c => hugepage_settings.c} (99%) rename tools/testing/selftests/mm/{thp_settings.h => hugepage_settings.h} (95%) diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index b5d582febae0..e6df968f0971 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -187,8 +187,8 @@ TEST_FILES += write_hugetlb_memory.sh include ../lib.mk -$(TEST_GEN_PROGS): vm_util.c thp_settings.c -$(TEST_GEN_FILES): vm_util.c thp_settings.c +$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c +$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(OUTPUT)/uffd-stress: uffd-common.c $(OUTPUT)/uffd-unit-tests: uffd-common.c diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index 6abdcb30aba8..4321f4208fe3 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -29,7 +29,7 @@ #include "../../../../mm/gup_test.h" #include "kselftest.h" #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" static size_t pagesize; static int pagemap_fd; diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/testing/selftests/mm/folio_split_race_test.c index 390fd97c1bd9..6329e37fff4c 100644 --- a/tools/testing/selftests/mm/folio_split_race_test.c +++ b/tools/testing/selftests/mm/folio_split_race_test.c @@ -25,7 +25,7 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "thp_settings.h" +#include "hugepage_settings.h" uint64_t page_size; uint64_t pmd_pagesize; diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c index 117639891953..b21df3040b1c 100644 --- a/tools/testing/selftests/mm/guard-regions.c +++ b/tools/testing/selftests/mm/guard-regions.c @@ -21,7 +21,7 @@ #include #include #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #include "../pidfd/pidfd.h" diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/hugepage_settings.c similarity index 99% rename from tools/testing/selftests/mm/thp_settings.c rename to tools/testing/selftests/mm/hugepage_settings.c index 9bfa5dc42624..9000864f8aa4 100644 --- a/tools/testing/selftests/mm/thp_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -8,7 +8,7 @@ #include #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/" #define MAX_SETTINGS_DEPTH 4 diff --git a/tools/testing/selftests/mm/thp_settings.h b/tools/testing/selftests/mm/hugepage_settings.h similarity index 95% rename from tools/testing/selftests/mm/thp_settings.h rename to tools/testing/selftests/mm/hugepage_settings.h index 7748a9009191..d81e33e425c2 100644 --- a/tools/testing/selftests/mm/thp_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __THP_SETTINGS_H__ -#define __THP_SETTINGS_H__ +#ifndef __HUGEPAGE_SETTINGS_H__ +#define __HUGEPAGE_SETTINGS_H__ #include #include @@ -86,4 +86,4 @@ unsigned long thp_shmem_supported_orders(void); bool thp_available(void); bool thp_is_enabled(void); -#endif /* __THP_SETTINGS_H__ */ +#endif /* __HUGEPAGE_SETTINGS_H__ */ diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index ac2433f19b1e..10e8dedcb087 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -22,7 +22,7 @@ #include "linux/magic.h" #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #define BASE_ADDR ((void *)(1UL << 30)) static unsigned long hpage_pmd_size; diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c index 0a5b38d3281c..64d1c63ae8c0 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -15,7 +15,7 @@ #include "kselftest.h" #include #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" #define KSM_FP(s) (KSM_SYSFS_PATH s) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index e504829df6b6..7781a59bb9f8 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -5,7 +5,7 @@ */ #include "kselftest_harness.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #include #include diff --git a/tools/testing/selftests/mm/prctl_thp_disable.c b/tools/testing/selftests/mm/prctl_thp_disable.c index ca27200596a4..d8d9d1de57b8 100644 --- a/tools/testing/selftests/mm/prctl_thp_disable.c +++ b/tools/testing/selftests/mm/prctl_thp_disable.c @@ -14,7 +14,7 @@ #include #include "kselftest_harness.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #include "vm_util.h" #ifndef PR_THP_DISABLE_EXCEPT_ADVISED diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c index bcfcac99b436..c426c4636ef5 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -9,7 +9,7 @@ #include "kselftest.h" #include "vm_util.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #define PAGEMAP_FILE_PATH "/proc/self/pagemap" #define TEST_ITERATIONS 10000 diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 40a5093917e7..a1ccfabc5367 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -21,7 +21,7 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "thp_settings.h" +#include "hugepage_settings.h" uint64_t pagesize; unsigned int pageshift; diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c index 7a9f1035099b..8eb0c5630e7e 100644 --- a/tools/testing/selftests/mm/transhuge-stress.c +++ b/tools/testing/selftests/mm/transhuge-stress.c @@ -17,7 +17,7 @@ #include #include "vm_util.h" #include "kselftest.h" -#include "thp_settings.h" +#include "hugepage_settings.h" int backing_fd = -1; int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE; diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index 516c35d236e1..9d67b11c2f28 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -8,7 +8,7 @@ #include #include #include "kselftest.h" -#include "thp_settings.h" +#include "hugepage_settings.h" #include "uffd-common.h" static int pagemap_fd; From 1bdc1698e6058fc8520041fb3259dc7656deae5e Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:08 +0300 Subject: [PATCH 4226/5214] selftests/mm: move HugeTLB helpers to hugepage_settings Move library functions that abstract HugeTLB /proc and /sysfs access from vm_util to hugepage_settings. This will help creating common helpers that save and restore HugeTLB and THP settings. Link: https://lore.kernel.org/20260511162840.375890-25-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/gup_longterm.c | 1 + tools/testing/selftests/mm/hmm-tests.c | 2 +- .../testing/selftests/mm/hugepage_settings.c | 68 +++++++++++++++++++ .../testing/selftests/mm/hugepage_settings.h | 8 +++ tools/testing/selftests/mm/hugetlb-madvise.c | 1 + tools/testing/selftests/mm/hugetlb-mmap.c | 1 + tools/testing/selftests/mm/hugetlb-vmemmap.c | 1 + tools/testing/selftests/mm/hugetlb_dio.c | 1 + .../selftests/mm/hugetlb_fault_after_madv.c | 1 + .../selftests/mm/hugetlb_madv_vs_map.c | 1 + tools/testing/selftests/mm/protection_keys.c | 1 + tools/testing/selftests/mm/thuge-gen.c | 1 + tools/testing/selftests/mm/uffd-common.h | 1 + .../selftests/mm/va_high_addr_switch.c | 1 + tools/testing/selftests/mm/vm_util.c | 67 ------------------ tools/testing/selftests/mm/vm_util.h | 3 - 16 files changed, 88 insertions(+), 71 deletions(-) diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c index f61150d28eb2..ab4eaf4feb7c 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -29,6 +29,7 @@ #include "../../../../mm/gup_test.h" #include "kselftest.h" #include "vm_util.h" +#include "hugepage_settings.h" static size_t pagesize; static int nr_hugetlbsizes; diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 6a23c09ac2da..43341154f07c 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -11,6 +11,7 @@ */ #include "kselftest_harness.h" +#include "hugepage_settings.h" #include #include @@ -27,7 +28,6 @@ #include #include - /* * This is a private UAPI to the kernel test module so it isn't exported * in the usual include/uapi/... directory. diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 9000864f8aa4..3d6e4376de06 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include +#include #include #include #include @@ -395,3 +397,69 @@ bool thp_is_enabled(void) /* THP is considered enabled if it's either "always" or "madvise" */ return mode == 1 || mode == 3; } + +int detect_hugetlb_page_sizes(size_t sizes[], int max) +{ + DIR *dir = opendir("/sys/kernel/mm/hugepages/"); + int count = 0; + + if (!dir) + return 0; + + while (count < max) { + struct dirent *entry = readdir(dir); + size_t kb; + + if (!entry) + break; + if (entry->d_type != DT_DIR) + continue; + if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1) + continue; + sizes[count++] = kb * 1024; + ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n", + kb); + } + closedir(dir); + return count; +} + +unsigned long default_huge_page_size(void) +{ + unsigned long hps = 0; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen("/proc/meminfo", "r"); + + if (!f) + return 0; + while (getline(&line, &linelen, f) > 0) { + if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { + hps <<= 10; + break; + } + } + + free(line); + fclose(f); + return hps; +} + +unsigned long get_free_hugepages(void) +{ + unsigned long fhp = 0; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen("/proc/meminfo", "r"); + + if (!f) + return fhp; + while (getline(&line, &linelen, f) > 0) { + if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1) + break; + } + + free(line); + fclose(f); + return fhp; +} diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index d81e33e425c2..4c51e9219f6a 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -6,6 +6,8 @@ #include #include +/* Transparent Huge Pages (THP) */ + enum thp_enabled { THP_NEVER, THP_ALWAYS, @@ -86,4 +88,10 @@ unsigned long thp_shmem_supported_orders(void); bool thp_available(void); bool thp_is_enabled(void); +/* HugeTLB */ + +int detect_hugetlb_page_sizes(size_t sizes[], int max); +unsigned long default_huge_page_size(void); +unsigned long get_free_hugepages(void); + #endif /* __HUGEPAGE_SETTINGS_H__ */ diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index e7dd8d06d986..5dfa2e0097ef 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -20,6 +20,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" #define MIN_FREE_PAGES 20 #define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */ diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c index a327d90d7a79..395be5d0dc1a 100644 --- a/tools/testing/selftests/mm/hugetlb-mmap.c +++ b/tools/testing/selftests/mm/hugetlb-mmap.c @@ -18,6 +18,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" #define LENGTH (256UL*1024*1024) #define PROTECTION (PROT_READ | PROT_WRITE) diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c index 485a6978b40f..af5786bebfd1 100644 --- a/tools/testing/selftests/mm/hugetlb-vmemmap.c +++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c @@ -11,6 +11,7 @@ #include #include #include "vm_util.h" +#include "hugepage_settings.h" #define PAGE_COMPOUND_HEAD (1UL << 15) #define PAGE_COMPOUND_TAIL (1UL << 16) diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c index 31a054fa8134..81e3f7bc8e76 100644 --- a/tools/testing/selftests/mm/hugetlb_dio.c +++ b/tools/testing/selftests/mm/hugetlb_dio.c @@ -20,6 +20,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" #ifndef STATX_DIOALIGN #define STATX_DIOALIGN 0x00002000U diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c index b4b257775b74..abc3904c5268 100644 --- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c +++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c @@ -10,6 +10,7 @@ #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" #define INLOOP_ITER 100 diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c index c7105c6d319b..ac60b4f18784 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -25,6 +25,7 @@ #include #include "vm_util.h" +#include "hugepage_settings.h" #define INLOOP_ITER 100 diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c index d617b41dda6b..4e4aa3b326c1 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -46,6 +46,7 @@ #include #include +#include "hugepage_settings.h" #include "pkey-helpers.h" int iteration_nr = 1; diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c index 77813d34dcc2..1007bc8aa57c 100644 --- a/tools/testing/selftests/mm/thuge-gen.c +++ b/tools/testing/selftests/mm/thuge-gen.c @@ -28,6 +28,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" #if !defined(MAP_HUGETLB) #define MAP_HUGETLB 0x40000 diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h index 0723843a7626..92a21b97f745 100644 --- a/tools/testing/selftests/mm/uffd-common.h +++ b/tools/testing/selftests/mm/uffd-common.h @@ -37,6 +37,7 @@ #include "kselftest.h" #include "vm_util.h" +#include "hugepage_settings.h" #define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c index 5d38735ea60e..0b69bd4b901d 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -11,6 +11,7 @@ #include "vm_util.h" #include "kselftest.h" +#include "hugepage_settings.h" /* * The hint addr value is used to allocate addresses diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index db94564f4431..412e980cce9a 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -291,53 +290,6 @@ int64_t allocate_transhuge(void *ptr, int pagemap_fd) return -1; } -unsigned long default_huge_page_size(void) -{ - unsigned long hps = 0; - char *line = NULL; - size_t linelen = 0; - FILE *f = fopen("/proc/meminfo", "r"); - - if (!f) - return 0; - while (getline(&line, &linelen, f) > 0) { - if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { - hps <<= 10; - break; - } - } - - free(line); - fclose(f); - return hps; -} - -int detect_hugetlb_page_sizes(size_t sizes[], int max) -{ - DIR *dir = opendir("/sys/kernel/mm/hugepages/"); - int count = 0; - - if (!dir) - return 0; - - while (count < max) { - struct dirent *entry = readdir(dir); - size_t kb; - - if (!entry) - break; - if (entry->d_type != DT_DIR) - continue; - if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1) - continue; - sizes[count++] = kb * 1024; - ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n", - kb); - } - closedir(dir); - return count; -} - int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags) { size_t count; @@ -396,25 +348,6 @@ int uffd_unregister(int uffd, void *addr, uint64_t len) return ret; } -unsigned long get_free_hugepages(void) -{ - unsigned long fhp = 0; - char *line = NULL; - size_t linelen = 0; - FILE *f = fopen("/proc/meminfo", "r"); - - if (!f) - return fhp; - while (getline(&line, &linelen, f) > 0) { - if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1) - break; - } - - free(line); - fclose(f); - return fhp; -} - static bool check_vmflag(void *addr, const char *flag) { char buffer[MAX_LINE_LENGTH]; diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 1a07305ceff4..195bf2e26792 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -94,8 +94,6 @@ bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size); bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size); bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size); int64_t allocate_transhuge(void *ptr, int pagemap_fd); -unsigned long default_huge_page_size(void); -int detect_hugetlb_page_sizes(size_t sizes[], int max); int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags); int uffd_register(int uffd, void *addr, uint64_t len, @@ -103,7 +101,6 @@ int uffd_register(int uffd, void *addr, uint64_t len, int uffd_unregister(int uffd, void *addr, uint64_t len); int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor, uint64_t *ioctls); -unsigned long get_free_hugepages(void); bool check_vmflag_io(void *addr); bool check_vmflag_pfnmap(void *addr); bool check_vmflag_guard(void *addr); From 0020a1f5ca36f2781e5471e9efcab6b03c4d4072 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:09 +0300 Subject: [PATCH 4227/5214] selftests/mm: hugepage_settings: use unsigned long in detect_hugetlb_page_size ... instead of size_t to avoid type mismatch in 32 and 64 bit builds. Link: https://lore.kernel.org/20260511162840.375890-26-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/cow.c | 2 +- tools/testing/selftests/mm/gup_longterm.c | 2 +- tools/testing/selftests/mm/hugepage_settings.c | 2 +- tools/testing/selftests/mm/hugepage_settings.h | 2 +- tools/testing/selftests/mm/uffd-wp-mremap.c | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index 4321f4208fe3..5e571216b8d2 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -37,7 +37,7 @@ static size_t pmdsize; static int nr_thpsizes; static size_t thpsizes[20]; static int nr_hugetlbsizes; -static size_t hugetlbsizes[10]; +static unsigned long hugetlbsizes[10]; static int gup_fd; static bool has_huge_zeropage; diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c index ab4eaf4feb7c..96dae0acd11a 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -33,7 +33,7 @@ static size_t pagesize; static int nr_hugetlbsizes; -static size_t hugetlbsizes[10]; +static unsigned long hugetlbsizes[10]; static int gup_fd; static __fsword_t get_fs_type(int fd) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 3d6e4376de06..fa635667aabb 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -398,7 +398,7 @@ bool thp_is_enabled(void) return mode == 1 || mode == 3; } -int detect_hugetlb_page_sizes(size_t sizes[], int max) +int detect_hugetlb_page_sizes(unsigned long sizes[], int max) { DIR *dir = opendir("/sys/kernel/mm/hugepages/"); int count = 0; diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index 4c51e9219f6a..f49bd7fba512 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -90,7 +90,7 @@ bool thp_is_enabled(void); /* HugeTLB */ -int detect_hugetlb_page_sizes(size_t sizes[], int max); +int detect_hugetlb_page_sizes(unsigned long sizes[], int max); unsigned long default_huge_page_size(void); unsigned long get_free_hugepages(void); diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index 9d67b11c2f28..b44e02840a5e 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -12,12 +12,12 @@ #include "uffd-common.h" static int pagemap_fd; -static size_t pagesize; static int nr_pagesizes = 1; +static unsigned long pagesize; static int nr_thpsizes; static size_t thpsizes[20]; static int nr_hugetlbsizes; -static size_t hugetlbsizes[10]; +static unsigned long hugetlbsizes[10]; static int detect_thp_sizes(size_t sizes[], int max) { @@ -245,7 +245,7 @@ static void test_one_folio(uffd_global_test_opts_t *gopts, size_t size, bool pri } struct testcase { - size_t *sizes; + unsigned long *sizes; int *nr_sizes; bool private; bool swapout; From 27477b28b74fd7bb69325a423be52d80b38b25cf Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:10 +0300 Subject: [PATCH 4228/5214] selftests/mm: hugepage_settings: add APIs to get and set nr_hugepages Add APIs that allow reading and writing of /sys/kernel/mm/hugepages/hugepages-NkB/nr_hugepages to detect and change the amount of HugeTLB pages of different sizes. Link: https://lore.kernel.org/20260511162840.375890-27-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../testing/selftests/mm/hugepage_settings.c | 25 +++++++++++++++++++ .../testing/selftests/mm/hugepage_settings.h | 23 +++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index fa635667aabb..99402cfccb6d 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -463,3 +463,28 @@ unsigned long get_free_hugepages(void) fclose(f); return fhp; } + +static void hugetlb_sysfs_path(char *buf, size_t buflen, + unsigned long size, const char *attr) +{ + snprintf(buf, buflen, "/sys/kernel/mm/hugepages/hugepages-%lukB/%s", + size / 1024, attr); +} + +unsigned long hugetlb_nr_pages(unsigned long size) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); + + return read_num(path); +} + +void hugetlb_set_nr_pages(unsigned long size, unsigned long nr) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); + + write_num(path, nr); +} diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index f49bd7fba512..e66302926377 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -94,4 +94,27 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max); unsigned long default_huge_page_size(void); unsigned long get_free_hugepages(void); +unsigned long hugetlb_nr_pages(unsigned long size); +void hugetlb_set_nr_pages(unsigned long size, unsigned long nr); + +static inline unsigned long hugetlb_nr_default_pages(void) +{ + unsigned long size = default_huge_page_size(); + + if (!size) + return 0; + + return hugetlb_nr_pages(size); +} + +static inline void hugetlb_set_nr_default_pages(unsigned long nr) +{ + unsigned long size = default_huge_page_size(); + + if (!size) + return; + + hugetlb_set_nr_pages(size, nr); +} + #endif /* __HUGEPAGE_SETTINGS_H__ */ From 024ec9a7e03bdbcdfc3f995547adef6473d87bf5 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:11 +0300 Subject: [PATCH 4229/5214] selftests/mm: hugepage_settings: rename and rework get_free_hugepages() ... to hugetlb_free_default_pages() for consistency with hugetlb_nr_default_pages(). Make hugetlb_free_default_pages() use hugetlb_sysfs_path() helper instead of parsing /proc/meminfo. Link: https://lore.kernel.org/20260511162840.375890-28-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../testing/selftests/mm/hugepage_settings.c | 28 ++++++------------- .../testing/selftests/mm/hugepage_settings.h | 12 +++++++- tools/testing/selftests/mm/hugetlb-madvise.c | 4 +-- tools/testing/selftests/mm/hugetlb_dio.c | 6 ++-- .../selftests/mm/hugetlb_fault_after_madv.c | 2 +- .../selftests/mm/hugetlb_madv_vs_map.c | 2 +- tools/testing/selftests/mm/uffd-stress.c | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 99402cfccb6d..3c944d28d14a 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -445,25 +445,6 @@ unsigned long default_huge_page_size(void) return hps; } -unsigned long get_free_hugepages(void) -{ - unsigned long fhp = 0; - char *line = NULL; - size_t linelen = 0; - FILE *f = fopen("/proc/meminfo", "r"); - - if (!f) - return fhp; - while (getline(&line, &linelen, f) > 0) { - if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1) - break; - } - - free(line); - fclose(f); - return fhp; -} - static void hugetlb_sysfs_path(char *buf, size_t buflen, unsigned long size, const char *attr) { @@ -488,3 +469,12 @@ void hugetlb_set_nr_pages(unsigned long size, unsigned long nr) write_num(path, nr); } + +unsigned long hugetlb_free_pages(unsigned long size) +{ + char path[PATH_MAX]; + + hugetlb_sysfs_path(path, sizeof(path), size, "free_hugepages"); + + return read_num(path); +} diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index e66302926377..436f4ce02984 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -92,10 +92,10 @@ bool thp_is_enabled(void); int detect_hugetlb_page_sizes(unsigned long sizes[], int max); unsigned long default_huge_page_size(void); -unsigned long get_free_hugepages(void); unsigned long hugetlb_nr_pages(unsigned long size); void hugetlb_set_nr_pages(unsigned long size, unsigned long nr); +unsigned long hugetlb_free_pages(unsigned long size); static inline unsigned long hugetlb_nr_default_pages(void) { @@ -117,4 +117,14 @@ static inline void hugetlb_set_nr_default_pages(unsigned long nr) hugetlb_set_nr_pages(size, nr); } +static inline unsigned long hugetlb_free_default_pages(void) +{ + unsigned long size = default_huge_page_size(); + + if (!size) + return 0; + + return hugetlb_free_pages(size); +} + #endif /* __HUGEPAGE_SETTINGS_H__ */ diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index 5dfa2e0097ef..8adcd91d078d 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -27,7 +27,7 @@ #define validate_free_pages(exp_free) \ do { \ - unsigned long fhp = get_free_hugepages(); \ + unsigned long fhp = hugetlb_free_default_pages(); \ if (fhp != (exp_free)) \ ksft_exit_fail_msg("Unexpected number of free " \ "huge pages %lu, expected %lu line %d\n", \ @@ -68,7 +68,7 @@ int main(int argc, char **argv) if (!base_page_size) ksft_exit_fail_msg("Unable to determine base page size\n"); - free_hugepages = get_free_hugepages(); + free_hugepages = hugetlb_free_default_pages(); if (free_hugepages < MIN_FREE_PAGES) ksft_exit_skip("Not enough free huge pages (have %lu, need %d)\n", free_hugepages, MIN_FREE_PAGES); diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c index 81e3f7bc8e76..47019433ddaf 100644 --- a/tools/testing/selftests/mm/hugetlb_dio.c +++ b/tools/testing/selftests/mm/hugetlb_dio.c @@ -93,7 +93,7 @@ static void run_dio_using_hugetlb(int fd, unsigned int start_off, ksft_exit_fail_perror("lseek failed\n"); /* Get the free huge pages before allocation */ - free_hpage_b = get_free_hugepages(); + free_hpage_b = hugetlb_free_default_pages(); if (free_hpage_b == 0) { close(fd); ksft_exit_skip("No free hugepage, exiting!\n"); @@ -121,7 +121,7 @@ static void run_dio_using_hugetlb(int fd, unsigned int start_off, munmap(orig_buffer, h_pagesize); /* Get the free huge pages after unmap*/ - free_hpage_a = get_free_hugepages(); + free_hpage_a = hugetlb_free_default_pages(); ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); @@ -142,7 +142,7 @@ int main(void) ksft_print_header(); /* Check if huge pages are free */ - if (!get_free_hugepages()) + if (!hugetlb_free_default_pages()) ksft_exit_skip("No free hugepage, exiting\n"); fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c index abc3904c5268..c718dd065d53 100644 --- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c +++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c @@ -78,7 +78,7 @@ int main(void) ksft_print_msg("[INFO] detected default hugetlb page size: %zu KiB\n", huge_page_size / 1024); - free_hugepages = get_free_hugepages(); + free_hugepages = hugetlb_free_default_pages(); if (free_hugepages != 1) { ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", free_hugepages); diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c index ac60b4f18784..dfbd71a7f709 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -89,7 +89,7 @@ int main(void) ksft_print_header(); ksft_set_plan(1); - free_hugepages = get_free_hugepages(); + free_hugepages = hugetlb_free_default_pages(); if (free_hugepages != 1) ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 0ce493dac354..7c719d789249 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -481,7 +481,7 @@ int main(int argc, char **argv) * for racy extra reservation of hugepages. */ if (gopts->test_type == TEST_HUGETLB && - get_free_hugepages() < 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1) + hugetlb_free_default_pages() < 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1) ksft_exit_skip("Skipping userfaultfd... not enough hugepages\n"); gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; From 08e51ae56a442c7b53e90f98bd30d6e0e1de5c1d Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:12 +0300 Subject: [PATCH 4230/5214] selftests/mm: hugepage_settings: add APIs for HugeTLB setup and teardown A lot of tests require free HugeTLB pages. Some need just a few default huge pages, some need a certain amount of memory available as HugeTLB, and some just skip lots of tests if huge pages of all supported sizes are not available. This all resulted in a huge mess in run_vmtests.sh that sets up some huge pages, adjusts them later and restores some of the settings if the stars align. Add APIs that allow saving the state of HugeTLB and setting up the desired amount of HugeTLB pages. Saving the state also registers atexit() callback and signal handler that will ensure restoration of HugeTLB state. Since many tests use both HugeTLB and THP, the atexit() callbacks and signal handler are restoring both. For kselftest_harness tests that run fixture setups and test in child processes add a constructor that will save and restore settings in the main process. Link: https://lore.kernel.org/20260511162840.375890-29-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Reviewed-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../testing/selftests/mm/hugepage_settings.c | 205 ++++++++++++++++-- .../testing/selftests/mm/hugepage_settings.h | 31 ++- 2 files changed, 213 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 3c944d28d14a..01a557f372d1 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -306,31 +306,16 @@ void thp_restore_settings(void) thp_write_settings(&saved_settings); } -static void thp_restore_settings_atexit(void) +static void __thp_save_settings(void) { - thp_restore_settings(); -} + if (!thp_available()) + return; -static void thp_restore_settings_sighandler(int sig) -{ - /* exit() will invoke the thp_restore_settings_atexit handler. */ - exit(KSFT_FAIL); -} + if (thp_settings_saved) + return; -void thp_save_settings(void) -{ thp_read_settings(&saved_settings); thp_settings_saved = true; - - /* - * setup exit hooks to make sure THP settings are restored on graceful - * and error exits and signals - */ - atexit(thp_restore_settings_atexit); - signal(SIGTERM, thp_restore_settings_sighandler); - signal(SIGINT, thp_restore_settings_sighandler); - signal(SIGHUP, thp_restore_settings_sighandler); - signal(SIGQUIT, thp_restore_settings_sighandler); } void thp_set_read_ahead_path(char *path) @@ -398,11 +383,32 @@ bool thp_is_enabled(void) return mode == 1 || mode == 3; } +#define HUGETLB_MAX_NR_PAGESIZES 10 +struct hugetlb_settings { + unsigned long nr_hugepages[HUGETLB_MAX_NR_PAGESIZES]; + unsigned long sizes[HUGETLB_MAX_NR_PAGESIZES]; + unsigned long default_size; + int nr_sizes; +}; + +static struct hugetlb_settings hugetlb_saved_settings; +static bool hugetlb_settings_saved; + int detect_hugetlb_page_sizes(unsigned long sizes[], int max) { - DIR *dir = opendir("/sys/kernel/mm/hugepages/"); + static struct hugetlb_settings *settings = &hugetlb_saved_settings; + DIR *dir; int count = 0; + if (settings->nr_sizes) { + if (settings->nr_sizes < max) + max = settings->nr_sizes; + for (count = 0; count < max; count++) + sizes[count] = settings->sizes[count]; + return count; + } + + dir = opendir("/sys/kernel/mm/hugepages/"); if (!dir) return 0; @@ -426,11 +432,16 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max) unsigned long default_huge_page_size(void) { + static struct hugetlb_settings *settings = &hugetlb_saved_settings; unsigned long hps = 0; char *line = NULL; size_t linelen = 0; - FILE *f = fopen("/proc/meminfo", "r"); + FILE *f; + if (settings->default_size) + return settings->default_size; + + f = fopen("/proc/meminfo", "r"); if (!f) return 0; while (getline(&line, &linelen, f) > 0) { @@ -478,3 +489,153 @@ unsigned long hugetlb_free_pages(unsigned long size) return read_num(path); } + +static bool __hugetlb_setup(unsigned long size, unsigned long nr) +{ + unsigned long free = hugetlb_free_pages(size); + unsigned long total = hugetlb_nr_pages(size); + + if (free >= nr) + return true; + + hugetlb_set_nr_pages(size, total + (nr - free)); + + return hugetlb_free_pages(size) >= nr; +} + +bool hugetlb_setup_default(unsigned long nr) +{ + unsigned long size; + + hugetlb_save_settings(); + size = default_huge_page_size(); + if (!size) + return false; + + return __hugetlb_setup(size, nr); +} + +bool hugetlb_setup_default_exact(unsigned long nr) +{ + unsigned long size; + + hugetlb_save_settings(); + size = default_huge_page_size(); + if (!size) + return false; + + hugetlb_set_nr_pages(size, nr); + + return hugetlb_free_pages(size) == nr; +} + +unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[], + int max) +{ + unsigned long enabled[10]; + int nr_sizes = 0; + int nr_enabled; + + hugetlb_save_settings(); + + nr_enabled = detect_hugetlb_page_sizes(enabled, ARRAY_SIZE(enabled)); + if (!nr_enabled) + return 0; + + if (nr_enabled > max) { + ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max); + nr_enabled = max; + } + + /* request nr HugeTLB pages of every size. */ + for (int i = 0; i < nr_enabled; i++) { + if (!__hugetlb_setup(enabled[i], nr)) + continue; + sizes[nr_sizes++] = enabled[i]; + } + + return nr_sizes; +} + +static void __hugetlb_save_settings(void) +{ + struct hugetlb_settings *settings = &hugetlb_saved_settings; + int nr_sizes; + + if (hugetlb_settings_saved) + return; + + settings->default_size = default_huge_page_size(); + if (!settings->default_size) + return; + + nr_sizes = detect_hugetlb_page_sizes(settings->sizes, + HUGETLB_MAX_NR_PAGESIZES); + if (!nr_sizes) { + settings->default_size = 0; + return; + } + + for (int i = 0; i < nr_sizes; i++) { + unsigned long sz = settings->sizes[i]; + + if (!sz) + continue; + settings->nr_hugepages[i] = hugetlb_nr_pages(sz); + } + + settings->nr_sizes = nr_sizes; + hugetlb_settings_saved = true; +} + +void hugetlb_restore_settings(void) +{ + struct hugetlb_settings *settings = &hugetlb_saved_settings; + + if (!hugetlb_settings_saved || !settings->default_size) + return; + + for (int i = 0; i < HUGETLB_MAX_NR_PAGESIZES; i++) { + unsigned long sz = settings->sizes[i]; + + if (!sz) + continue; + + hugetlb_set_nr_pages(sz, settings->nr_hugepages[i]); + } +} + +static void hugepage_restore_settings_atexit(void) +{ + if (thp_settings_saved) + thp_restore_settings(); + if (hugetlb_settings_saved) + hugetlb_restore_settings(); +} + +static void hugepage_restore_settings_sighandler(int sig) +{ + /* exit() will invoke the hugepage_restore_settings_atexit handler. */ + exit(KSFT_FAIL); +} + +void hugepage_save_settings(bool thp, bool hugetlb) +{ + if (!thp && !hugetlb) + return; + + if (thp) + __thp_save_settings(); + if (hugetlb) + __hugetlb_save_settings(); + + /* + * setup exit hooks to make sure THP and HugeTLB settings are + * restored on graceful and error exits and signals + */ + atexit(hugepage_restore_settings_atexit); + signal(SIGTERM, hugepage_restore_settings_sighandler); + signal(SIGINT, hugepage_restore_settings_sighandler); + signal(SIGHUP, hugepage_restore_settings_sighandler); + signal(SIGQUIT, hugepage_restore_settings_sighandler); +} diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index 436f4ce02984..c07722b7f102 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -6,6 +6,8 @@ #include #include +void hugepage_save_settings(bool thp, bool hugetlb); + /* Transparent Huge Pages (THP) */ enum thp_enabled { @@ -79,7 +81,11 @@ struct thp_settings *thp_current_settings(void); void thp_push_settings(struct thp_settings *settings); void thp_pop_settings(void); void thp_restore_settings(void); -void thp_save_settings(void); + +static inline void thp_save_settings(void) +{ + hugepage_save_settings(/* thp = */ true, /* hugetlb = */ false); +} void thp_set_read_ahead_path(char *path); unsigned long thp_supported_orders(void); @@ -97,6 +103,13 @@ unsigned long hugetlb_nr_pages(unsigned long size); void hugetlb_set_nr_pages(unsigned long size, unsigned long nr); unsigned long hugetlb_free_pages(unsigned long size); +static inline void hugetlb_save_settings(void) +{ + hugepage_save_settings(/* thp = */ false, /* hugetlb = */ true); +} + +void hugetlb_restore_settings(void); + static inline unsigned long hugetlb_nr_default_pages(void) { unsigned long size = default_huge_page_size(); @@ -127,4 +140,20 @@ static inline unsigned long hugetlb_free_default_pages(void) return hugetlb_free_pages(size); } +static inline bool hugetlb_available(void) +{ + return default_huge_page_size() != 0; +} + +bool hugetlb_setup_default(unsigned long nr); +bool hugetlb_setup_default_exact(unsigned long nr); +unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[], + int max); + +#define HUGETLB_SETUP_DEFAULT_PAGES(nr_pages) \ +static void __attribute__((constructor)) __hugetlb_setup_default(void) \ +{ \ + hugetlb_setup_default((nr_pages)); \ +} + #endif /* __HUGEPAGE_SETTINGS_H__ */ From 505c9605ee3305511cfcda9f23a3a354290a1ddd Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:13 +0300 Subject: [PATCH 4231/5214] selftests/mm: move read_file(), read_num() and write_num() to vm_util These are useful helpers for writing and reading sysfs and proc files. Make them available to the tests that don't use thp_settings. While on it make write_num() use "%lu" instead of "%ld" to match 'unsigned long num' argument type. Link: https://lore.kernel.org/20260511162840.375890-30-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../testing/selftests/mm/hugepage_settings.c | 41 ------------------- .../testing/selftests/mm/hugepage_settings.h | 4 -- tools/testing/selftests/mm/vm_util.c | 39 ++++++++++++++++++ tools/testing/selftests/mm/vm_util.h | 3 ++ 4 files changed, 42 insertions(+), 45 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 01a557f372d1..2eab2110ac6a 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -48,47 +48,6 @@ static const char * const shmem_enabled_strings[] = { NULL }; -int read_file(const char *path, char *buf, size_t buflen) -{ - int fd; - ssize_t numread; - - fd = open(path, O_RDONLY); - if (fd == -1) - return 0; - - numread = read(fd, buf, buflen - 1); - if (numread < 1) { - close(fd); - return 0; - } - - buf[numread] = '\0'; - close(fd); - - return (unsigned int) numread; -} - -unsigned long read_num(const char *path) -{ - char buf[21]; - - if (read_file(path, buf, sizeof(buf)) < 0) { - perror("read_file()"); - exit(EXIT_FAILURE); - } - - return strtoul(buf, NULL, 10); -} - -void write_num(const char *path, unsigned long num) -{ - char buf[21]; - - sprintf(buf, "%ld", num); - write_file(path, buf, strlen(buf) + 1); -} - int thp_read_string(const char *name, const char * const strings[]) { char path[PATH_MAX]; diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index c07722b7f102..726c73c43c05 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -66,10 +66,6 @@ struct thp_settings { struct shmem_hugepages_settings shmem_hugepages[NR_ORDERS]; }; -int read_file(const char *path, char *buf, size_t buflen); -unsigned long read_num(const char *path); -void write_num(const char *path, unsigned long num); - int thp_read_string(const char *name, const char * const strings[]); void thp_write_string(const char *name, const char *val); unsigned long thp_read_num(const char *name); diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 412e980cce9a..9256bbbe38ea 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -698,6 +698,27 @@ int unpoison_memory(unsigned long pfn) return ret > 0 ? 0 : -errno; } +int read_file(const char *path, char *buf, size_t buflen) +{ + int fd; + ssize_t numread; + + fd = open(path, O_RDONLY); + if (fd == -1) + return 0; + + numread = read(fd, buf, buflen - 1); + if (numread < 1) { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return (unsigned int) numread; +} + void write_file(const char *path, const char *buf, size_t buflen) { int fd, saved_errno; @@ -721,3 +742,21 @@ void write_file(const char *path, const char *buf, size_t buflen) ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n", path, (int)(buflen - 1), buf, buflen - 1, numwritten); } + +unsigned long read_num(const char *path) +{ + char buf[21]; + + if (read_file(path, buf, sizeof(buf)) < 0) + ksft_exit_fail_perror("read_file()"); + + return strtoul(buf, NULL, 10); +} + +void write_num(const char *path, unsigned long num) +{ + char buf[21]; + + sprintf(buf, "%lu", num); + write_file(path, buf, strlen(buf) + 1); +} diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 195bf2e26792..5fc9707f6b9a 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -165,3 +165,6 @@ int unpoison_memory(unsigned long pfn); #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1)) void write_file(const char *path, const char *buf, size_t buflen); +int read_file(const char *path, char *buf, size_t buflen); +unsigned long read_num(const char *path); +void write_num(const char *path, unsigned long num); From fb271590fa19f15f65d5f13b58e17b3683d7eec4 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:14 +0300 Subject: [PATCH 4232/5214] selftests/mm: vm_util: add helpers to set and restore shm limits hugetlb-shm and thuge-gen tests require that limits defined by /proc/sys/kernel/{shmmax,shmall} should be higher than certain values. Add helpers that allow setting these limits and restoring their settings on a test exit. They will be used later in hugetlb-shm and thuge-gen. Link: https://lore.kernel.org/20260511162840.375890-31-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/vm_util.c | 28 ++++++++++++++++++++++++++++ tools/testing/selftests/mm/vm_util.h | 9 +++++++++ 2 files changed, 37 insertions(+) diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 9256bbbe38ea..336a26751d3f 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -760,3 +760,31 @@ void write_num(const char *path, unsigned long num) sprintf(buf, "%lu", num); write_file(path, buf, strlen(buf) + 1); } + +static unsigned long shmall, shmmax; + +void __shm_limits_restore(void) +{ + if (shmmax) + write_num("/proc/sys/kernel/shmmax", shmmax); + if (shmall) + write_num("/proc/sys/kernel/shmall", shmall); +} + +void shm_limits_prepare(unsigned long length) +{ + unsigned long nr = length / psize(); + unsigned long val; + + val = read_num("/proc/sys/kernel/shmmax"); + if (val < length) { + write_num("/proc/sys/kernel/shmmax", length); + shmmax = val; + } + + val = read_num("/proc/sys/kernel/shmall"); + if (val < nr) { + write_num("/proc/sys/kernel/shmall", nr); + shmall = val; + } +} diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 5fc9707f6b9a..ea8fc8fdf0eb 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -168,3 +168,12 @@ void write_file(const char *path, const char *buf, size_t buflen); int read_file(const char *path, char *buf, size_t buflen); unsigned long read_num(const char *path); void write_num(const char *path, unsigned long num); + +void shm_limits_prepare(unsigned long length); +void __shm_limits_restore(void); + +#define SHM_LIMITS_RESTORE() \ +static void __attribute__((destructor)) shm_limits_restore(void) \ +{ \ + __shm_limits_restore(); \ +} From bf2f989814330ed58145c106e86efa82d93799e5 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:15 +0300 Subject: [PATCH 4233/5214] selftests/mm: compaction_test: use HugeTLB helpers ... ... instead of open coded access of HugeTLB parameters via /proc. Link: https://lore.kernel.org/20260511162840.375890-32-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/compaction_test.c | 115 +++---------------- 1 file changed, 17 insertions(+), 98 deletions(-) diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c index 30209c40b697..de0633f9a7e5 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -17,6 +17,7 @@ #include #include "kselftest.h" +#include "hugepage_settings.h" #define MAP_SIZE_MB 100 #define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024) @@ -82,124 +83,44 @@ int prereq(void) return -1; } -int check_compaction(unsigned long mem_free, unsigned long hugepage_size, - unsigned long initial_nr_hugepages) +int check_compaction(unsigned long mem_free, unsigned long hugepage_size) { - unsigned long nr_hugepages_ul; - int fd, ret = -1; + unsigned long nr_hugepages; int compaction_index = 0; - char nr_hugepages[20] = {0}; - char init_nr_hugepages[24] = {0}; - char target_nr_hugepages[24] = {0}; - int slen; - - snprintf(init_nr_hugepages, sizeof(init_nr_hugepages), - "%lu", initial_nr_hugepages); + int ret = -1; /* We want to test with 80% of available memory. Else, OOM killer comes in to play */ mem_free = mem_free * 0.8; - fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK); - if (fd < 0) { - ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); - ret = -1; - goto out; - } - /* * Request huge pages for about half of the free memory. The Kernel * will allocate as much as it can, and we expect it will get at least 1/3 */ - nr_hugepages_ul = mem_free / hugepage_size / 2; - snprintf(target_nr_hugepages, sizeof(target_nr_hugepages), - "%lu", nr_hugepages_ul); - - slen = strlen(target_nr_hugepages); - if (write(fd, target_nr_hugepages, slen) != slen) { - ksft_print_msg("Failed to write %lu to /proc/sys/vm/nr_hugepages: %s\n", - nr_hugepages_ul, strerror(errno)); - goto close_fd; - } - - lseek(fd, 0, SEEK_SET); - - if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { - ksft_print_msg("Failed to re-read from /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); - goto close_fd; - } + nr_hugepages = mem_free / hugepage_size / 2; + hugetlb_set_nr_default_pages(nr_hugepages); /* We should have been able to request at least 1/3 rd of the memory in huge pages */ - nr_hugepages_ul = strtoul(nr_hugepages, NULL, 10); - if (!nr_hugepages_ul) { + nr_hugepages = hugetlb_nr_default_pages(); + if (!nr_hugepages) { ksft_print_msg("ERROR: No memory is available as huge pages\n"); - goto close_fd; + goto out; } - compaction_index = mem_free/(nr_hugepages_ul * hugepage_size); + compaction_index = mem_free/(nr_hugepages * hugepage_size); - lseek(fd, 0, SEEK_SET); - - if (write(fd, init_nr_hugepages, strlen(init_nr_hugepages)) - != strlen(init_nr_hugepages)) { - ksft_print_msg("Failed to write value to /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); - goto close_fd; - } - - ksft_print_msg("Number of huge pages allocated = %lu\n", - nr_hugepages_ul); + ksft_print_msg("Number of huge pages allocated = %lu\n", nr_hugepages); if (compaction_index > 3) { ksft_print_msg("ERROR: Less than 1/%d of memory is available\n" "as huge pages\n", compaction_index); - goto close_fd; - } - - ret = 0; - - close_fd: - close(fd); - out: - ksft_test_result(ret == 0, "check_compaction\n"); - return ret; -} - -int set_zero_hugepages(unsigned long *initial_nr_hugepages) -{ - int fd, ret = -1; - char nr_hugepages[20] = {0}; - - fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK); - if (fd < 0) { - ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); goto out; } - if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { - ksft_print_msg("Failed to read from /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); - goto close_fd; - } - lseek(fd, 0, SEEK_SET); - - /* Start with the initial condition of 0 huge pages */ - if (write(fd, "0", sizeof(char)) != sizeof(char)) { - ksft_print_msg("Failed to write 0 to /proc/sys/vm/nr_hugepages: %s\n", - strerror(errno)); - goto close_fd; - } - - *initial_nr_hugepages = strtoul(nr_hugepages, NULL, 10); ret = 0; - close_fd: - close(fd); - out: + ksft_test_result(ret == 0, "check_compaction\n"); return ret; } @@ -212,18 +133,17 @@ int main(int argc, char **argv) unsigned long mem_free = 0; unsigned long hugepage_size = 0; long mem_fragmentable_MB = 0; - unsigned long initial_nr_hugepages; ksft_print_header(); if (prereq() || geteuid()) ksft_exit_skip("Prerequisites unsatisfied\n"); - ksft_set_plan(1); - /* Start the test without hugepages reducing mem_free */ - if (set_zero_hugepages(&initial_nr_hugepages)) - ksft_exit_fail(); + if (!hugetlb_setup_default_exact(0)) + ksft_exit_skip("Could not reset nr_hugepages\n"); + + ksft_set_plan(1); lim.rlim_cur = RLIM_INFINITY; lim.rlim_max = RLIM_INFINITY; @@ -268,8 +188,7 @@ int main(int argc, char **argv) entry = entry->next; } - if (check_compaction(mem_free, hugepage_size, - initial_nr_hugepages) == 0) + if (check_compaction(mem_free, hugepage_size) == 0) ksft_exit_pass(); ksft_exit_fail(); From 11893d36c9bb2cc58a9683521f0132494625763c Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:16 +0300 Subject: [PATCH 4234/5214] selftests/mm: cow: add setup of HugeTLB pages cow tests skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-33-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/cow.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index 5e571216b8d2..0c627ea89ff7 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -1881,21 +1881,21 @@ int main(int argc, char **argv) ksft_print_header(); + thp_save_settings(); + pagesize = getpagesize(); pmdsize = read_pmd_pagesize(); if (pmdsize) { /* Only if THP is supported. */ thp_read_settings(&default_settings); default_settings.hugepages[sz2ord(pmdsize, pagesize)].enabled = THP_INHERIT; - thp_save_settings(); thp_push_settings(&default_settings); ksft_print_msg("[INFO] detected PMD size: %zu KiB\n", pmdsize / 1024); nr_thpsizes = detect_thp_sizes(thpsizes, ARRAY_SIZE(thpsizes)); } - nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes, - ARRAY_SIZE(hugetlbsizes)); + nr_hugetlbsizes = hugetlb_setup(2, hugetlbsizes, ARRAY_SIZE(hugetlbsizes)); has_huge_zeropage = detect_huge_zeropage(); ksft_set_plan(ARRAY_SIZE(anon_test_cases) * tests_per_anon_test_case() + From fa84e69570cda23b433678ee3af56427bf5857f6 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:17 +0300 Subject: [PATCH 4235/5214] selftests/mm: gup_longterm: add setup of HugeTLB pages gup_longterm tests skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-34-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/gup_longterm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c index 96dae0acd11a..eb8963e9d98f 100644 --- a/tools/testing/selftests/mm/gup_longterm.c +++ b/tools/testing/selftests/mm/gup_longterm.c @@ -510,7 +510,7 @@ int main(int argc, char **argv) int i; pagesize = getpagesize(); - nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes, + nr_hugetlbsizes = hugetlb_setup(2, hugetlbsizes, ARRAY_SIZE(hugetlbsizes)); ksft_print_header(); From ff47c6008775ccbf1efc903137008b19785bbfe3 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:18 +0300 Subject: [PATCH 4236/5214] selftests/mm: gup_test: add setup of HugeTLB pages gup_test fails to run HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-35-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/gup_test.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c index fb8f9ae49efa..3f841a96f870 100644 --- a/tools/testing/selftests/mm/gup_test.c +++ b/tools/testing/selftests/mm/gup_test.c @@ -14,6 +14,7 @@ #include #include "kselftest.h" #include "vm_util.h" +#include "hugepage_settings.h" #define MB (1UL << 20) @@ -94,6 +95,7 @@ int main(int argc, char **argv) int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret; int flags = MAP_PRIVATE; char *file = "/dev/zero"; + bool hugetlb = false; pthread_t *tid; char *p; @@ -168,6 +170,7 @@ int main(int argc, char **argv) break; case 'H': flags |= (MAP_HUGETLB | MAP_ANONYMOUS); + hugetlb = true; break; default: ksft_exit_fail_msg("Wrong argument\n"); @@ -199,6 +202,18 @@ int main(int argc, char **argv) } ksft_print_header(); + + if (hugetlb) { + unsigned long hp_size = default_huge_page_size(); + + if (!hp_size) + ksft_exit_skip("HugeTLB is unavailable\n"); + + size = (size + hp_size - 1) & ~(hp_size - 1); + if (!hugetlb_setup_default(size / hp_size)) + ksft_exit_skip("Not enough huge pages\n"); + } + ksft_set_plan(nthreads); filed = open(file, O_RDWR|O_CREAT, 0664); From 28ef1fb4e5ce9dad1a10da85330b4911ce44d1d8 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:19 +0300 Subject: [PATCH 4237/5214] selftests/mm: hmm-tests: add setup of HugeTLB pages hmm-tests skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Since kselftest_harness runs fixture setup and the tests in child processes, use HUGETLB_SETUP_DEFAULT_PAGES() that defines a constructor that runs in the main process and add verification that there are enough free huge pages to the tests that use them. Replace exit() calls with _exit() to avoid restoring HugeTLB settings in the middle of test and use SIGKILL to kill a child process in hmm_cow_in_device test to avoid interference with signal handlers in hugepage_restore_settings(). Link: https://lore.kernel.org/20260511162840.375890-36-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 43341154f07c..46e0c8c921c3 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -69,6 +69,9 @@ enum { #ifndef FOLL_LONGTERM #define FOLL_LONGTERM 0x100 /* mapping lifetime is indefinite */ #endif + +HUGETLB_SETUP_DEFAULT_PAGES(1) + FIXTURE(hmm) { int fd; @@ -632,7 +635,7 @@ TEST_F(hmm, anon_write_child) } close(child_fd); - exit(0); + _exit(0); } } } @@ -712,7 +715,7 @@ TEST_F(hmm, anon_write_child_shared) ASSERT_EQ(ptr[i], -i); close(child_fd); - exit(0); + _exit(0); } /* @@ -784,8 +787,8 @@ TEST_F(hmm, anon_write_hugetlbfs) int *ptr; int ret; - if (!default_hsize) - SKIP(return, "Huge page size could not be determined"); + if (!hugetlb_free_default_pages()) + SKIP(return, "Not enough huge pages"); size = ALIGN(TWOMEG, default_hsize); npages = size >> self->page_shift; @@ -1614,9 +1617,8 @@ TEST_F(hmm, compound) unsigned long i; /* Skip test if we can't allocate a hugetlbfs page. */ - - if (!default_hsize) - SKIP(return, "Huge page size could not be determined"); + if (!hugetlb_free_default_pages()) + SKIP(return, "Not enough huge pages"); size = ALIGN(TWOMEG, default_hsize); npages = size >> self->page_shift; @@ -2062,7 +2064,7 @@ TEST_F(hmm, hmm_cow_in_device) if (pid == -1) ASSERT_EQ(pid, 0); if (!pid) { - /* Child process waits for SIGTERM from the parent. */ + /* Child process waits for SIGKILL from the parent. */ while (1) { } /* Should not reach this */ @@ -2075,10 +2077,10 @@ TEST_F(hmm, hmm_cow_in_device) ptr[i] = i; /* Terminate child and wait */ - EXPECT_EQ(0, kill(pid, SIGTERM)); + EXPECT_EQ(0, kill(pid, SIGKILL)); EXPECT_EQ(pid, waitpid(pid, &status, 0)); EXPECT_NE(0, WIFSIGNALED(status)); - EXPECT_EQ(SIGTERM, WTERMSIG(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); /* Take snapshot to CPU pagetables */ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); From 642d1fd449ff3fd714a8ba53b78cef65c8142990 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:20 +0300 Subject: [PATCH 4238/5214] selftests/mm: hugepage_dio: add setup of HugeTLB pages hugepage_dio test fails if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-37-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb_dio.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c index 47019433ddaf..fb4600570e13 100644 --- a/tools/testing/selftests/mm/hugetlb_dio.c +++ b/tools/testing/selftests/mm/hugetlb_dio.c @@ -85,8 +85,6 @@ static void run_dio_using_hugetlb(int fd, unsigned int start_off, /* Get the default huge page size */ h_pagesize = default_huge_page_size(); - if (!h_pagesize) - ksft_exit_fail_msg("Unable to determine huge page size\n"); /* Reset file position since fd is shared across tests */ if (lseek(fd, 0, SEEK_SET) < 0) @@ -94,10 +92,6 @@ static void run_dio_using_hugetlb(int fd, unsigned int start_off, /* Get the free huge pages before allocation */ free_hpage_b = hugetlb_free_default_pages(); - if (free_hpage_b == 0) { - close(fd); - ksft_exit_skip("No free hugepage, exiting!\n"); - } /* Allocate a hugetlb page */ orig_buffer = mmap(NULL, h_pagesize, mmap_prot, mmap_flags, -1, 0); @@ -141,8 +135,8 @@ int main(void) ksft_print_header(); - /* Check if huge pages are free */ - if (!hugetlb_free_default_pages()) + /* request a huge page */ + if (!hugetlb_setup_default(1)) ksft_exit_skip("No free hugepage, exiting\n"); fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); From 2b6746a3b44b2d874a92548f2d3d4c6828ba47a0 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:21 +0300 Subject: [PATCH 4239/5214] selftests/mm: hugetlb_fault_after_madv: add setup of HugeTLB pages hugetlb_fault_after_madv test skips testing if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-38-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb_fault_after_madv.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c index c718dd065d53..2dc158054f66 100644 --- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c +++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c @@ -54,7 +54,6 @@ void *madv(void *unused) int main(void) { - unsigned long free_hugepages; pthread_t thread1, thread2; /* * On kernel 6.4, we are able to reproduce the problem with ~1000 @@ -78,11 +77,8 @@ int main(void) ksft_print_msg("[INFO] detected default hugetlb page size: %zu KiB\n", huge_page_size / 1024); - free_hugepages = hugetlb_free_default_pages(); - if (free_hugepages != 1) { - ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", - free_hugepages); - } + if (!hugetlb_setup_default(1)) + ksft_exit_skip("Not enough HugeTLB pages\n"); while (max--) { huge_ptr = mmap(NULL, huge_page_size, PROT_READ | PROT_WRITE, From 06fbc9506c2d39933c6a302453d2de9a40cebc40 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:22 +0300 Subject: [PATCH 4240/5214] selftests/mm: hugetlb-madvise: add setup of HugeTLB pages hugetlb-madvise test skips testing if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-39-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-madvise.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c index 8adcd91d078d..555b4b3d1430 100644 --- a/tools/testing/selftests/mm/hugetlb-madvise.c +++ b/tools/testing/selftests/mm/hugetlb-madvise.c @@ -4,12 +4,6 @@ * * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE * on hugetlb mappings. - * - * Before running this test, make sure the administrator has pre-allocated - * at least MIN_FREE_PAGES hugetlb pages and they are free. In addition, - * the test takes an argument that is the path to a file in a hugetlbfs - * filesystem. Therefore, a hugetlbfs filesystem must be mounted on some - * directory. */ #define _GNU_SOURCE @@ -68,9 +62,9 @@ int main(int argc, char **argv) if (!base_page_size) ksft_exit_fail_msg("Unable to determine base page size\n"); + if (!hugetlb_setup_default(MIN_FREE_PAGES)) + ksft_exit_skip("Not enough free huge pages (have %lu, need %d)\n", hugetlb_free_default_pages(), MIN_FREE_PAGES); free_hugepages = hugetlb_free_default_pages(); - if (free_hugepages < MIN_FREE_PAGES) - ksft_exit_skip("Not enough free huge pages (have %lu, need %d)\n", free_hugepages, MIN_FREE_PAGES); fd = memfd_create(argv[0], MFD_HUGETLB); if (fd < 0) From afa1b71900f3bc9126d1aa01a105d16054286ddb Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:23 +0300 Subject: [PATCH 4241/5214] selftests/mm: hugetlb_madv_vs_map: add setup of HugeTLB pages hugetlb_madv_vs_map test skips testing if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-40-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb_madv_vs_map.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c index dfbd71a7f709..f94549efcc6f 100644 --- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c +++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c @@ -77,7 +77,6 @@ void *map_extra(void *unused) int main(void) { pthread_t thread1, thread2, thread3; - unsigned long free_hugepages; void *ret; /* @@ -89,11 +88,9 @@ int main(void) ksft_print_header(); ksft_set_plan(1); - free_hugepages = hugetlb_free_default_pages(); - - if (free_hugepages != 1) + if (!hugetlb_setup_default_exact(1)) ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", - free_hugepages); + hugetlb_free_default_pages()); mmap_size = default_huge_page_size(); From ae571cd6015c6ebb023a4b9ccd49125b7cf93610 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:24 +0300 Subject: [PATCH 4242/5214] selftests/mm: hugetlb-mmap: add setup of HugeTLB pages hugetlb-mmap test fails if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-41-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-mmap.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c index 395be5d0dc1a..0f2aad1b7dbd 100644 --- a/tools/testing/selftests/mm/hugetlb-mmap.c +++ b/tools/testing/selftests/mm/hugetlb-mmap.c @@ -105,28 +105,35 @@ int main(int argc, char **argv) { size_t hugepage_size; size_t length = LENGTH; - int shift = 0; + int shift = 0, nr; ksft_print_header(); - ksft_set_plan(2); if (argc > 1) length = atol(argv[1]) << 20; if (argc > 2) shift = atoi(argv[2]); + hugetlb_save_settings(); if (shift) { hugepage_size = (1UL << shift); ksft_print_msg("%lu kB hugepages\n", 1UL << (shift - 10)); } else { hugepage_size = default_huge_page_size(); + if (!hugepage_size) + ksft_exit_skip("Could not detect default hugetlb page size."); ksft_print_msg("Default size hugepages (%lu kB)\n", hugepage_size >> 10); } /* munmap will fail if the length is not page aligned */ - if (hugepage_size > length) - length = hugepage_size; + length = (length + hugepage_size - 1) & ~(hugepage_size - 1); + nr = length / hugepage_size; + hugetlb_set_nr_pages(hugepage_size, nr); + if (hugetlb_free_pages(hugepage_size) < nr) + ksft_exit_skip("Not enough %lu Kb pages\n", hugepage_size >> 10); + + ksft_set_plan(2); ksft_print_msg("Mapping %lu Mbytes\n", (unsigned long)length >> 20); test_anon_mmap(length, shift); From a914785f6349da754e1226420bf4e51961956769 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:25 +0300 Subject: [PATCH 4243/5214] selftests/mm: hugetlb-mremap: add setup of HugeTLB pages hugetlb-mremap test fails if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-42-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-mremap.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c index 1c87c39780c5..d239905790dd 100644 --- a/tools/testing/selftests/mm/hugetlb-mremap.c +++ b/tools/testing/selftests/mm/hugetlb-mremap.c @@ -26,6 +26,7 @@ #include #include "kselftest.h" #include "vm_util.h" +#include "hugepage_settings.h" #define DEFAULT_LENGTH_MB 10UL #define MB_TO_BYTES(x) (x * 1024 * 1024) @@ -108,8 +109,9 @@ static void register_region_with_uffd(char *addr, size_t len) int main(int argc, char *argv[]) { + unsigned long hugepage_size; + int ret = 0, fd, nr; size_t length = 0; - int ret = 0, fd; ksft_print_header(); ksft_set_plan(1); @@ -125,7 +127,16 @@ int main(int argc, char *argv[]) else length = DEFAULT_LENGTH_MB; + hugepage_size = default_huge_page_size(); + if (!hugepage_size) + ksft_exit_skip("Could not detect default hugetlb page size\n"); length = MB_TO_BYTES(length); + length = (length + hugepage_size - 1) & ~(hugepage_size - 1); + nr = length / hugepage_size; + + if (!hugetlb_setup_default(nr)) + ksft_exit_skip("Not enough huge pages\n"); + fd = memfd_create(argv[0], MFD_HUGETLB); if (fd < 0) ksft_exit_fail_msg("Open failed: %s\n", strerror(errno)); From 2d6d040ccf00ecf560516c362c3ee72016930a07 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:26 +0300 Subject: [PATCH 4244/5214] selftests/mm: hugetlb-shm: add setup of HugeTLB pages hugetlb-shm test fails if there are no free huge pages prepared by a wrapper script and shm liimts in proc are too low. Add setup of HugeTLB pages and shm limits to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-43-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-shm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c index c2c7aee85b96..3ff7f062b7eb 100644 --- a/tools/testing/selftests/mm/hugetlb-shm.c +++ b/tools/testing/selftests/mm/hugetlb-shm.c @@ -29,9 +29,27 @@ #include #include "vm_util.h" +#include "hugepage_settings.h" #define LENGTH (256UL*1024*1024) +static void prepare(void) +{ + unsigned long length, hugepage_size, nr; + + hugepage_size = default_huge_page_size(); + if (!hugepage_size) + ksft_exit_skip("Unable to determine huge page size\n"); + + length = (LENGTH + hugepage_size - 1) & ~(hugepage_size - 1); + nr = length / hugepage_size; + + if (!hugetlb_setup_default(nr)) + ksft_exit_skip("Not enough free huge pages\n"); + + shm_limits_prepare(length); +} + int main(void) { int shmid; @@ -41,6 +59,8 @@ int main(void) ksft_print_header(); ksft_set_plan(1); + prepare(); + shmid = shmget(2, LENGTH, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); if (shmid < 0) ksft_exit_fail_perror("shmget"); @@ -76,3 +96,5 @@ int main(void) ksft_test_result_pass("hugepage using SysV shmget/shmat\n"); ksft_finished(); } + +SHM_LIMITS_RESTORE() From c764cfa251d2d9cda8d695f8c4e8972be508ce0f Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:27 +0300 Subject: [PATCH 4245/5214] selftests/mm: hugetlb-soft-offline: add setup of HugeTLB pages hugetlb-soft-offline test uses open coded access to /proc to determine availability of huge pages and fails if there are no enough free huget pages.. Replace open coded access to /proc with hugepage helpers and add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-44-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../selftests/mm/hugetlb-soft-offline.c | 45 ++++--------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/testing/selftests/mm/hugetlb-soft-offline.c index a8bc02688085..bc202e4ed2bd 100644 --- a/tools/testing/selftests/mm/hugetlb-soft-offline.c +++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c @@ -6,9 +6,7 @@ * - if enable_soft_offline = 1, a hugepage should be dissolved and * nr_hugepages/free_hugepages should be reduced by 1. * - * Before running, make sure more than 2 hugepages of default_hugepagesz - * are allocated. For example, if /proc/meminfo/Hugepagesize is 2048kB: - * echo 8 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages + * The test allocates 8 default hugepages */ #define _GNU_SOURCE @@ -25,6 +23,7 @@ #include #include "kselftest.h" +#include "hugepage_settings.h" #ifndef MADV_SOFT_OFFLINE #define MADV_SOFT_OFFLINE 101 @@ -100,32 +99,6 @@ static int set_enable_soft_offline(int value) return 0; } -static int read_nr_hugepages(unsigned long hugepage_size, - unsigned long *nr_hugepages) -{ - char buffer[256] = {0}; - char cmd[256] = {0}; - - sprintf(cmd, "cat /sys/kernel/mm/hugepages/hugepages-%ldkB/nr_hugepages", - hugepage_size); - FILE *cmdfile = popen(cmd, "r"); - - if (cmdfile == NULL) { - ksft_perror(EPREFIX "failed to popen nr_hugepages"); - return -1; - } - - if (!fgets(buffer, sizeof(buffer), cmdfile)) { - ksft_perror(EPREFIX "failed to read nr_hugepages"); - pclose(cmdfile); - return -1; - } - - *nr_hugepages = atoll(buffer); - pclose(cmdfile); - return 0; -} - static int create_hugetlbfs_file(struct statfs *file_stat) { int fd; @@ -177,20 +150,14 @@ static void test_soft_offline_common(int enable_soft_offline) ksft_exit_fail_msg("Failed to set enable_soft_offline\n"); } - if (read_nr_hugepages(hugepagesize_kb, &nr_hugepages_before) != 0) { - close(fd); - ksft_exit_fail_msg("Failed to read nr_hugepages\n"); - } + nr_hugepages_before = hugetlb_nr_default_pages(); ksft_print_msg("Before MADV_SOFT_OFFLINE nr_hugepages=%ld\n", nr_hugepages_before); ret = do_soft_offline(fd, 2 * file_stat.f_bsize, expect_errno); - if (read_nr_hugepages(hugepagesize_kb, &nr_hugepages_after) != 0) { - close(fd); - ksft_exit_fail_msg("Failed to read nr_hugepages\n"); - } + nr_hugepages_after = hugetlb_nr_default_pages(); ksft_print_msg("After MADV_SOFT_OFFLINE nr_hugepages=%ld\n", nr_hugepages_after); @@ -219,6 +186,10 @@ static void test_soft_offline_common(int enable_soft_offline) int main(int argc, char **argv) { ksft_print_header(); + + if (!hugetlb_setup_default(8)) + ksft_exit_skip("not enough hugetlb pages\n"); + ksft_set_plan(2); test_soft_offline_common(1); From 496a662f73292d98e1d91917f2b2f9ec1d130fdc Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:28 +0300 Subject: [PATCH 4246/5214] selftests/mm: hugetlb-vmemmap: add setup of HugeTLB pages hugetlb-vmemmap test fails if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-45-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-vmemmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c index af5786bebfd1..507df78a158d 100644 --- a/tools/testing/selftests/mm/hugetlb-vmemmap.c +++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c @@ -97,6 +97,9 @@ int main(int argc, char **argv) ksft_print_header(); ksft_set_plan(1); + if (!hugetlb_setup_default(1)) + ksft_exit_skip("Not enough free huge pages\n"); + pagesize = psize(); maplength = default_huge_page_size(); if (!maplength) From a73a71aaea1d2fac50542d1cd1ba7d0ec219d52f Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:29 +0300 Subject: [PATCH 4247/5214] selftests/mm: migration: add setup of HugeTLB pages migration skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Since kselftest_harness runs fixture setup and the tests in child processes, use HUGETLB_SETUP_DEFAULT_PAGES() that defines a constructor that runs in the main process and add verification that there are enough free huge pages to the tests that use them. Reset signal handlers to defaults in FIXTURE_SETUP() so that sending SIGTERM and SIGHUP during the tests won't cause restoration of HugeTLB settings. Link: https://lore.kernel.org/20260511162840.375890-46-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/migration.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 7781a59bb9f8..29f7492453d4 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -23,6 +23,8 @@ #define MAX_RETRIES 100 #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) +HUGETLB_SETUP_DEFAULT_PAGES(1) + FIXTURE(migration) { pthread_t *threads; @@ -32,10 +34,23 @@ FIXTURE(migration) int n2; }; +static void reset_signals(void) +{ + struct sigaction sa = { .sa_handler = SIG_DFL }; + + sigemptyset(&sa.sa_mask); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); +} + FIXTURE_SETUP(migration) { int n; + reset_signals(); + if (numa_available() < 0) SKIP(return, "NUMA not available"); self->nthreads = numa_num_task_cpus() - 2; @@ -288,6 +303,9 @@ TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME) if (!hugepage_size) SKIP(return, "Reading HugeTLB pagesize failed"); + if (hugetlb_free_default_pages() < 1) + SKIP(return, "Not enough huge pages"); + ptr = mmap(NULL, hugepage_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); ASSERT_NE(ptr, MAP_FAILED); @@ -316,6 +334,9 @@ TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME) if (!hugepage_size) SKIP(return, "Reading HugeTLB pagesize failed"); + if (hugetlb_free_default_pages() < 1) + SKIP(return, "Not enough huge pages"); + ptr = mmap(NULL, hugepage_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); ASSERT_NE(ptr, MAP_FAILED); From 857f2f816932573b4080f0683ca75848f7f2508a Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:30 +0300 Subject: [PATCH 4248/5214] selftests/mm: pagemap_ioctl: add setup of HugeTLB pages pagemap-ioctl skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-47-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/pagemap_ioctl.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 7f9428d6062c..762306177ad8 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -7,8 +7,6 @@ #include #include #include -#include "vm_util.h" -#include "kselftest.h" #include #include #include @@ -23,6 +21,10 @@ #include #include +#include "vm_util.h" +#include "kselftest.h" +#include "hugepage_settings.h" + #define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \ PAGE_IS_FILE | PAGE_IS_PRESENT | \ PAGE_IS_SWAPPED | PAGE_IS_PFNZERO | \ @@ -1554,6 +1556,9 @@ int main(int __attribute__((unused)) argc, char *argv[]) if (init_uffd()) ksft_exit_skip("Failed to initialize userfaultfd\n"); + if (!hugetlb_setup_default(4)) + ksft_print_msg("HugeTLB test will be skipped\n"); + ksft_set_plan(117); page_size = getpagesize(); @@ -1605,7 +1610,7 @@ int main(int __attribute__((unused)) argc, char *argv[]) } /* 5. SHM Hugetlb page testing */ - mem_size = 2*1024*1024; + mem_size = default_huge_page_size(); mem = gethugetlb_mem(mem_size, &shmid); if (mem) { wp_init(mem, mem_size); @@ -1633,7 +1638,7 @@ int main(int __attribute__((unused)) argc, char *argv[]) } /* 7. File Hugetlb testing */ - mem_size = 2*1024*1024; + mem_size = default_huge_page_size(); fd = memfd_create("uffd-test", MFD_HUGETLB | MFD_NOEXEC_SEAL); if (fd < 0) ksft_exit_fail_msg("uffd-test creation failed %d %s\n", errno, strerror(errno)); From 25356081894df2ebcb140fcd28191e55c2d82a22 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:31 +0300 Subject: [PATCH 4249/5214] selftests/mm: protection_keys: use library code for HugeTLB setup protection_keys open codes setup of HugeTLB pages. Replace it with the library functions from hugepage_setup. Replace exit() calls with _exit() to avoid restoring HugeTLB settings in the middle of test. Link: https://lore.kernel.org/20260511162840.375890-48-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/protection_keys.c | 50 ++++++-------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c index 4e4aa3b326c1..9a6d954ee371 100644 --- a/tools/testing/selftests/mm/protection_keys.c +++ b/tools/testing/selftests/mm/protection_keys.c @@ -62,6 +62,7 @@ noinline int read_ptr(int *ptr) return *ptr; } +#if CONTROL_TRACING > 0 static void cat_into_file(char *str, char *file) { int fd = open(file, O_RDWR); @@ -87,7 +88,6 @@ static void cat_into_file(char *str, char *file) close(fd); } -#if CONTROL_TRACING > 0 static int warned_tracing; static int tracing_root_ok(void) { @@ -710,50 +710,28 @@ static void *malloc_pkey_anon_huge(long size, int prot, u16 pkey) } static int hugetlb_setup_ok; -#define SYSFS_FMT_NR_HUGE_PAGES "/sys/kernel/mm/hugepages/hugepages-%ldkB/nr_hugepages" #define GET_NR_HUGE_PAGES 10 static void setup_hugetlbfs(void) { - int err; - int fd; - char buf[256]; - long hpagesz_kb; - long hpagesz_mb; + long hpagesz_mb = HPAGE_SIZE / 1024 / 1024; + unsigned long free_pages; if (geteuid() != 0) { ksft_print_msg("WARNING: not run as root, can not do hugetlb test\n"); return; } - cat_into_file(__stringify(GET_NR_HUGE_PAGES), "/proc/sys/vm/nr_hugepages"); - /* - * Now go make sure that we got the pages and that they + * Make sure that we got the pages and that they * are PMD-level pages. Someone might have made PUD-level * pages the default. */ - hpagesz_kb = HPAGE_SIZE / 1024; - hpagesz_mb = hpagesz_kb / 1024; - sprintf(buf, SYSFS_FMT_NR_HUGE_PAGES, hpagesz_kb); - fd = open(buf, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "opening sysfs %ldM hugetlb config: %s\n", - hpagesz_mb, strerror(errno)); - return; - } - - /* -1 to guarantee leaving the trailing \0 */ - err = read(fd, buf, sizeof(buf)-1); - close(fd); - if (err <= 0) { - fprintf(stderr, "reading sysfs %ldM hugetlb config: %s\n", - hpagesz_mb, strerror(errno)); - return; - } - - if (atoi(buf) != GET_NR_HUGE_PAGES) { - fprintf(stderr, "could not confirm %ldM pages, got: '%s' expected %d\n", - hpagesz_mb, buf, GET_NR_HUGE_PAGES); + hugetlb_save_settings(); + hugetlb_set_nr_pages(HPAGE_SIZE, GET_NR_HUGE_PAGES); + free_pages = hugetlb_free_pages(HPAGE_SIZE); + if (free_pages < GET_NR_HUGE_PAGES) { + ksft_print_msg("could not confirm %ldM pages, got: '%lu' expected %d\n", + hpagesz_mb, free_pages, GET_NR_HUGE_PAGES); return; } @@ -1130,7 +1108,7 @@ static void become_child(void) /* in the child */ return; } - exit(0); + _exit(0); } /* Assumes that all pkeys other than 'pkey' are unallocated */ @@ -1509,18 +1487,18 @@ static void test_ptrace_modifies_pkru(int *ptr, u16 pkey) * checking */ if (__read_pkey_reg() != new_pkru) - exit(1); + _exit(1); /* Stop and allow the tracer to clear XSTATE_BV for PKRU */ raise(SIGSTOP); if (__read_pkey_reg() != 0) - exit(1); + _exit(1); /* Stop and allow the tracer to examine PKRU */ raise(SIGSTOP); - exit(0); + _exit(0); } pkey_assert(child == waitpid(child, &status, 0)); From 49a4e7186b08ff56884d8a1b439e60f514886834 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:32 +0300 Subject: [PATCH 4250/5214] selftests/mm: thuge-gen: add setup of HugeTLB pages thuge-gen skips tests if there are no free huge pages prepared by a wrapper script and shm liimts in proc are too low. Replace custom detection of huge pages with the library functions and add setup of HugeTLB pages and shm limits to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-49-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/thuge-gen.c | 95 ++++---------------------- 1 file changed, 12 insertions(+), 83 deletions(-) diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c index 1007bc8aa57c..22b9c2f1c35d 100644 --- a/tools/testing/selftests/mm/thuge-gen.c +++ b/tools/testing/selftests/mm/thuge-gen.c @@ -1,17 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Test selecting other page sizes for mmap/shmget. - - Before running this huge pages for each huge page size must have been - reserved. - For large pages beyond MAX_PAGE_ORDER (like 1GB on x86) boot options must - be used. 1GB wouldn't be tested if it isn't available. - Also shmmax must be increased. - And you need to run as root to work around some weird permissions in shm. - And nothing using huge pages should run in parallel. - When the program aborts you may need to clean up the shm segments with - ipcrm -m by hand, like this - sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m - (warning this will remove all if someone else uses them) */ +/* Test selecting other page sizes for mmap/shmget. */ #define _GNU_SOURCE #include @@ -21,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -38,15 +24,6 @@ #ifndef SHM_HUGE_SHIFT #define SHM_HUGE_SHIFT 26 #endif -#ifndef SHM_HUGE_MASK -#define SHM_HUGE_MASK 0x3f -#endif -#ifndef SHM_HUGE_2MB -#define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) -#endif -#ifndef SHM_HUGE_1GB -#define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) -#endif #define NUM_PAGESIZES 5 #define NUM_PAGES 4 @@ -64,32 +41,10 @@ int ilog2(unsigned long v) void show(unsigned long ps) { - char buf[100]; - if (ps == getpagesize()) return; - ksft_print_msg("%luMB: ", ps >> 20); - - fflush(stdout); - snprintf(buf, sizeof buf, - "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", - ps >> 10); - system(buf); -} - -unsigned long read_free(unsigned long ps) -{ - unsigned long val = 0; - char buf[100]; - - snprintf(buf, sizeof(buf), - "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", - ps >> 10); - if (read_sysfs(buf, &val) && ps != getpagesize()) - ksft_print_msg("missing %s\n", buf); - - return val; + ksft_print_msg("%luMB: %lu\n", ps >> 20, hugetlb_free_pages(ps)); } void test_mmap(unsigned long size, unsigned flags) @@ -97,14 +52,14 @@ void test_mmap(unsigned long size, unsigned flags) char *map; unsigned long before, after; - before = read_free(size); + before = hugetlb_free_pages(size); map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0); if (map == MAP_FAILED) ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); memset(map, 0xff, size*NUM_PAGES); - after = read_free(size); + after = hugetlb_free_pages(size); show(size); ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, @@ -121,7 +76,7 @@ void test_shmget(unsigned long size, unsigned flags) struct shm_info i; char *map; - before = read_free(size); + before = hugetlb_free_pages(size); id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); if (id < 0) { if (errno == EPERM) { @@ -142,7 +97,7 @@ void test_shmget(unsigned long size, unsigned flags) shmctl(id, IPC_RMID, NULL); memset(map, 0xff, size*NUM_PAGES); - after = read_free(size); + after = hugetlb_free_pages(size); show(size); ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, @@ -154,43 +109,15 @@ void test_shmget(unsigned long size, unsigned flags) void find_pagesizes(void) { unsigned long largest = getpagesize(); - unsigned long shmmax_val = 0; int i; - glob_t g; - glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); - assert(g.gl_pathc <= NUM_PAGESIZES); - for (i = 0; (i < g.gl_pathc) && (num_page_sizes < NUM_PAGESIZES); i++) { - sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", - &page_sizes[num_page_sizes]); - page_sizes[num_page_sizes] <<= 10; - ksft_print_msg("Found %luMB\n", page_sizes[i] >> 20); + num_page_sizes = hugetlb_setup(NUM_PAGES, page_sizes, ARRAY_SIZE(page_sizes)); - if (page_sizes[num_page_sizes] > largest) + for (i = 0; i < num_page_sizes; i++) + if (page_sizes[i] > largest) largest = page_sizes[i]; - if (read_free(page_sizes[num_page_sizes]) >= NUM_PAGES) - num_page_sizes++; - else - ksft_print_msg("SKIP for size %lu MB as not enough huge pages, need %u\n", - page_sizes[num_page_sizes] >> 20, NUM_PAGES); - } - globfree(&g); - - read_sysfs("/proc/sys/kernel/shmmax", &shmmax_val); - if (shmmax_val < NUM_PAGES * largest) { - ksft_print_msg("WARNING: shmmax is too small to run this test.\n"); - ksft_print_msg("Please run the following command to increase shmmax:\n"); - ksft_print_msg("echo %lu > /proc/sys/kernel/shmmax\n", largest * NUM_PAGES); - ksft_exit_skip("Test skipped due to insufficient shmmax value.\n"); - } - -#if defined(__x86_64__) - if (largest != 1U<<30) { - ksft_exit_skip("No GB pages available on x86-64\n" - "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); - } -#endif + shm_limits_prepare(NUM_PAGES * largest); } int main(void) @@ -233,3 +160,5 @@ int main(void) ksft_finished(); } + +SHM_LIMITS_RESTORE() From db8cef5bac46cf70e7faefc2c6c58f8673dc8672 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:33 +0300 Subject: [PATCH 4251/5214] selftests/mm: uffd-stress: use hugetlb_save and alloc huge pages uffd-stress skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-50-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-stress.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 7c719d789249..43cc79590136 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -480,9 +480,12 @@ int main(int argc, char **argv) * Ensure nr_parallel - 1 hugepages on top of that to account * for racy extra reservation of hugepages. */ - if (gopts->test_type == TEST_HUGETLB && - hugetlb_free_default_pages() < 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1) - ksft_exit_skip("Skipping userfaultfd... not enough hugepages\n"); + if (gopts->test_type == TEST_HUGETLB) { + unsigned long nr = 2 * (bytes / gopts->page_size) + gopts->nr_parallel - 1; + + if (!hugetlb_setup_default(nr)) + ksft_exit_skip("Skipping userfaultfd... not enough hugepages\n"); + } gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; if (!gopts->nr_pages_per_cpu) { From 1d95be59ca0698c1095a08e11b6bc60df7bfaa26 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:34 +0300 Subject: [PATCH 4252/5214] selftests/mm: uffd-unit-tests: add setup of HugeTLB pages uffd-unit-tests skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Replace exit() calls with _exit() to avoid restoring HugeTLB settings in the middle of test. Link: https://lore.kernel.org/20260511162840.375890-51-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-unit-tests.c | 33 +++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index db7c26835487..a6c14109e818 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -299,7 +299,7 @@ static int pagemap_test_fork(uffd_global_test_opts_t *gopts, bool with_event, bo if (test_pin) unpin_pages(&args); /* Succeed */ - exit(0); + _exit(0); } waitpid(child, &result, 0); @@ -767,7 +767,7 @@ static void uffd_sigbus_test_common(uffd_global_test_opts_t *gopts, bool wp) err("fork"); if (!pid) - exit(faulting_process(gopts, 2, wp)); + _exit(faulting_process(gopts, 2, wp)); waitpid(pid, &err, 0); if (err) @@ -821,7 +821,7 @@ static void uffd_events_test_common(uffd_global_test_opts_t *gopts, bool wp) err("fork"); if (!pid) - exit(faulting_process(gopts, 0, wp)); + _exit(faulting_process(gopts, 0, wp)); waitpid(pid, &err, 0); if (err) @@ -1700,11 +1700,32 @@ static int uffd_count_tests(int n_tests, int n_mems, const char *test_filter) return count; } +static unsigned long uffd_setup_hugetlb(void) +{ + unsigned long nr_hugepages, hp_size; + + hugetlb_save_settings(); + hp_size = default_huge_page_size(); + + if (!hp_size) + return 0; + + /* need twice UFFD_TEST_MEM_SIZE, one for src area and one for dst */ + nr_hugepages = 2 * MAX(UFFD_TEST_MEM_SIZE, hp_size * 2) / hp_size; + hugetlb_set_nr_default_pages(nr_hugepages); + + if (hugetlb_free_default_pages() < nr_hugepages) + return 0; + + return hp_size; +} + int main(int argc, char *argv[]) { int n_tests = sizeof(uffd_tests) / sizeof(uffd_test_case_t); int n_mems = sizeof(mem_types) / sizeof(mem_type_t); const char *test_filter = NULL; + unsigned long hugepage_size; bool list_only = false; uffd_test_case_t *test; mem_type_t *mem_type; @@ -1738,6 +1759,8 @@ int main(int argc, char *argv[]) return KSFT_PASS; } + hugepage_size = uffd_setup_hugetlb(); + ksft_print_header(); ksft_set_plan(uffd_count_tests(n_tests, n_mems, test_filter)); @@ -1765,9 +1788,9 @@ int main(int argc, char *argv[]) uffd_test_start("%s on %s", test->name, mem_type->name); if (mem_type->mem_flag & (MEM_HUGETLB_PRIVATE | MEM_HUGETLB)) { - gopts.page_size = default_huge_page_size(); + gopts.page_size = hugepage_size; if (gopts.page_size == 0) { - uffd_test_skip("huge page size is 0, feature missing?"); + uffd_test_skip("not enough HugeTLB pages"); continue; } } else { From df7ab28eeae653779a3a0b91b586505995cbafe1 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:35 +0300 Subject: [PATCH 4253/5214] selftests/mm: uffd-wp-mremap: add setup of HugeTLB pages uffd-wp-remap skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-52-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-wp-mremap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index b44e02840a5e..90ac410c6c6f 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -336,14 +336,14 @@ int main(int argc, char **argv) struct thp_settings settings; int i, j, plan = 0; + hugepage_save_settings(true, true); + pagesize = getpagesize(); nr_thpsizes = detect_thp_sizes(thpsizes, ARRAY_SIZE(thpsizes)); - nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes, - ARRAY_SIZE(hugetlbsizes)); + nr_hugetlbsizes = hugetlb_setup(1, hugetlbsizes, ARRAY_SIZE(hugetlbsizes)); - /* If THP is supported, save THP settings and initially disable THP. */ + /* If THP is supported, initially disable THP. */ if (nr_thpsizes) { - thp_save_settings(); thp_read_settings(&settings); for (i = 0; i < NR_ORDERS; i++) { settings.hugepages[i].enabled = THP_NEVER; From c9e5d1fc951dbce768dc7e7d998c72fd3d589764 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:36 +0300 Subject: [PATCH 4254/5214] selftests/mm: va_high_addr_switch: add setup of HugeTLB pages va_high_addr_switch skips HugeTLB tests if there are no free huge pages prepared by a wrapper script. Add setup of HugeTLB pages to the test and make sure that the original settings are restored on the test exit. Link: https://lore.kernel.org/20260511162840.375890-53-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/va_high_addr_switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c index 0b69bd4b901d..e24d7ba00b44 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -325,7 +325,7 @@ int main(int argc, char **argv) if (!supported_arch()) ksft_exit_skip("Architecture not supported\n"); - if (argc == 2 && !strcmp(argv[1], "--run-hugetlb")) + if (hugetlb_setup_default(6)) run_hugetlb = true; testcases_init(); From e8a83b3a129c4eeccf627030ef442da9b891c741 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:37 +0300 Subject: [PATCH 4255/5214] selftests/mm: va_high_addr_switch.sh: drop huge pages setup Since va_high_addr_switch takes care of setting up huge pages, there is no need to set them up in the va_high_addr_switch.sh wrapper script. Link: https://lore.kernel.org/20260511162840.375890-54-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Luiz Capitulino Tested-by: Luiz Capitulino Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Sarthak Sharma Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- .../selftests/mm/va_high_addr_switch.sh | 41 +------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.sh b/tools/testing/selftests/mm/va_high_addr_switch.sh index 9492c2d72634..01c15fe3c799 100755 --- a/tools/testing/selftests/mm/va_high_addr_switch.sh +++ b/tools/testing/selftests/mm/va_high_addr_switch.sh @@ -9,7 +9,6 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -orig_nr_hugepages=0 skip() { @@ -77,43 +76,5 @@ check_test_requirements() esac } -save_nr_hugepages() -{ - orig_nr_hugepages=$(cat /proc/sys/vm/nr_hugepages) -} - -restore_nr_hugepages() -{ - echo "$orig_nr_hugepages" > /proc/sys/vm/nr_hugepages -} - -setup_nr_hugepages() -{ - local needpgs=$1 - while read -r name size unit; do - if [ "$name" = "HugePages_Free:" ]; then - freepgs="$size" - break - fi - done < /proc/meminfo - if [ "$freepgs" -ge "$needpgs" ]; then - return - fi - local hpgs=$((orig_nr_hugepages + needpgs)) - echo $hpgs > /proc/sys/vm/nr_hugepages - - local nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) - if [ "$nr_hugepgs" != "$hpgs" ]; then - restore_nr_hugepages - skip "$0: no enough hugepages for testing" - fi -} - check_test_requirements -save_nr_hugepages -# The HugeTLB tests require 6 pages -setup_nr_hugepages 6 -./va_high_addr_switch --run-hugetlb -retcode=$? -restore_nr_hugepages -exit $retcode +./va_high_addr_switch From 157adc22f01cdd0c0dc1b9e4389ed2377d9b69d3 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:38 +0300 Subject: [PATCH 4256/5214] selftests/mm: run_vmtests.sh: free memory if available memory is low Currently when running THP and HugeTLB tests, if HAVE_HUGEPAGES is set run_test() drops caches, compacts memory and runs the test. But if HAVE_HUGEPAGES is not set it skips the tests entirely, even if THP tests have nothing to do with HAVE_HUGEPAGES. Replace the check if HAVE_HUGEPAGES is set with a check of how much memory is available. If there is less than 256 MB of available memory, drop caches and run compaction and then continue to run a test regardless of HAVE_HUGEPAGES value. Link: https://lore.kernel.org/20260511162840.375890-55-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/run_vmtests.sh | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 8453be45ab18..524c79b42344 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -234,19 +234,17 @@ pretty_name() { # Usage: run_test [test binary] [arbitrary test arguments...] run_test() { if test_selected ${CATEGORY}; then - local skip=0 - # On memory constrainted systems some tests can fail to allocate hugepages. # perform some cleanup before the test for a higher success rate. if [ ${CATEGORY} == "thp" -o ${CATEGORY} == "hugetlb" ]; then - if [ "${HAVE_HUGEPAGES}" = "1" ]; then + mem_kb=$(awk '/MemAvailable/ {print $2}' /proc/meminfo) + mem_Mb=$((mem_kb / 1024)) + + if (( $mem_Mb < 256 )); then echo 3 > /proc/sys/vm/drop_caches sleep 2 echo 1 > /proc/sys/vm/compact_memory sleep 2 - else - echo "hugepages not supported" | tap_prefix - skip=1 fi fi @@ -255,12 +253,8 @@ run_test() { local sep=$(echo -n "$title" | tr "[:graph:][:space:]" -) printf "%s\n%s\n%s\n" "$sep" "$title" "$sep" | tap_prefix - if [ "${skip}" != "1" ]; then - ("$@" 2>&1) | tap_prefix - local ret=${PIPESTATUS[0]} - else - local ret=$ksft_skip - fi + ("$@" 2>&1) | tap_prefix + local ret=${PIPESTATUS[0]} count_total=$(( count_total + 1 )) if [ $ret -eq 0 ]; then count_pass=$(( count_pass + 1 )) From 08de77b1c99fd2311526e362d7f4500a322da51f Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Mon, 11 May 2026 19:28:39 +0300 Subject: [PATCH 4257/5214] selftests/mm: run_vmtests.sh: drop detection and setup of HugeTLB All the tests that use HugeTLB can detect and setup HugeTLB pages on their own. Drop detection and setup of HugeTLB from run_vmtests.sh. Link: https://lore.kernel.org/20260511162840.375890-56-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Donet Tom Cc: Jason Gunthorpe Cc: John Hubbard Cc: Lance Yang Cc: Leon Romanovsky Cc: Liam Howlett Cc: Li Wang Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Nico Pache Cc: Peter Xu Cc: Ryan Roberts Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/run_vmtests.sh | 125 ++-------------------- 1 file changed, 7 insertions(+), 118 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 524c79b42344..043aa3ed2596 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -132,7 +132,7 @@ test_selected() { run_gup_matrix() { # -t: thp=on, -T: thp=off, -H: hugetlb=on - local hugetlb_mb=$(( needmem_KB / 1024 )) + local hugetlb_mb=256 for huge in -t -T "-H -m $hugetlb_mb"; do # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pin-longterm @@ -154,60 +154,6 @@ run_gup_matrix() { done } -# get huge pagesize and freepages from /proc/meminfo -while read -r name size unit; do - if [ "$name" = "HugePages_Free:" ]; then - freepgs="$size" - fi - if [ "$name" = "Hugepagesize:" ]; then - hpgsize_KB="$size" - fi -done < /proc/meminfo - -# Simple hugetlbfs tests have a hardcoded minimum requirement of -# huge pages totaling 256MB (262144KB) in size. The userfaultfd -# hugetlb test requires a minimum of 2 * nr_cpus huge pages. Take -# both of these requirements into account and attempt to increase -# number of huge pages available. -nr_cpus=$(nproc) -uffd_min_KB=$((hpgsize_KB * nr_cpus * 2)) -hugetlb_min_KB=$((256 * 1024)) -if [[ $uffd_min_KB -gt $hugetlb_min_KB ]]; then - needmem_KB=$uffd_min_KB -else - needmem_KB=$hugetlb_min_KB -fi - -# set proper nr_hugepages -if [ -n "$freepgs" ] && [ -n "$hpgsize_KB" ]; then - orig_nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) - needpgs=$((needmem_KB / hpgsize_KB)) - tries=2 - while [ "$tries" -gt 0 ] && [ "$freepgs" -lt "$needpgs" ]; do - lackpgs=$((needpgs - freepgs)) - echo 3 > /proc/sys/vm/drop_caches - if ! echo $((lackpgs + orig_nr_hugepgs)) > /proc/sys/vm/nr_hugepages; then - echo "Please run this test as root" - exit $ksft_skip - fi - while read -r name size unit; do - if [ "$name" = "HugePages_Free:" ]; then - freepgs=$size - fi - done < /proc/meminfo - tries=$((tries - 1)) - done - nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) - if [ "$freepgs" -lt "$needpgs" ]; then - printf "Not enough huge pages available (%d < %d)\n" \ - "$freepgs" "$needpgs" - fi - HAVE_HUGEPAGES=1 -else - echo "no hugetlbfs support in kernel?" - HAVE_HUGEPAGES=0 -fi - # filter 64bit architectures ARCH64STR="arm64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sparc64 x86_64" if [ -z "$ARCH" ]; then @@ -277,29 +223,13 @@ run_test() { echo "TAP version 13" | tap_output CATEGORY="hugetlb" run_test ./hugetlb-mmap - -shmmax=$(cat /proc/sys/kernel/shmmax) -shmall=$(cat /proc/sys/kernel/shmall) -echo 268435456 > /proc/sys/kernel/shmmax -echo 4194304 > /proc/sys/kernel/shmall CATEGORY="hugetlb" run_test ./hugetlb-shm -echo "$shmmax" > /proc/sys/kernel/shmmax -echo "$shmall" > /proc/sys/kernel/shmall - CATEGORY="hugetlb" run_test ./hugetlb-mremap CATEGORY="hugetlb" run_test ./hugetlb-vmemmap CATEGORY="hugetlb" run_test ./hugetlb-madvise CATEGORY="hugetlb" run_test ./hugetlb_dio - -if [ "${HAVE_HUGEPAGES}" = "1" ]; then - nr_hugepages_tmp=$(cat /proc/sys/vm/nr_hugepages) - # For this test, we need one and just one huge page - echo 1 > /proc/sys/vm/nr_hugepages - CATEGORY="hugetlb" run_test ./hugetlb_fault_after_madv - CATEGORY="hugetlb" run_test ./hugetlb_madv_vs_map - # Restore the previous number of huge pages, since further tests rely on it - echo "$nr_hugepages_tmp" > /proc/sys/vm/nr_hugepages -fi +CATEGORY="hugetlb" run_test ./hugetlb_fault_after_madv +CATEGORY="hugetlb" run_test ./hugetlb_madv_vs_map if test_selected "hugetlb"; then echo "NOTE: These hugetlb tests provide minimal coverage. Use" | tap_prefix @@ -326,44 +256,11 @@ CATEGORY="gup_test" run_test ./gup_longterm CATEGORY="userfaultfd" run_test ./uffd-unit-tests uffd_stress_bin=./uffd-stress CATEGORY="userfaultfd" run_test ${uffd_stress_bin} anon 20 16 -# Hugetlb tests require source and destination huge pages. Pass in almost half -# the size of the free pages we have, which is used for *each*. An adjustment -# of (nr_parallel - 1) is done (see nr_parallel in uffd-stress.c) to have some -# extra hugepages - this is done to prevent the test from failing by racily -# reserving more hugepages than strictly required. -# uffd-stress expects a region expressed in MiB, so we adjust -# half_ufd_size_MB accordingly. -adjustment=$(( (31 < (nr_cpus - 1)) ? 31 : (nr_cpus - 1) )) -half_ufd_size_MB=$((((freepgs - adjustment) * hpgsize_KB) / 1024 / 2)) -CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb "$half_ufd_size_MB" 32 -CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb-private "$half_ufd_size_MB" 32 +CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb 128 32 +CATEGORY="userfaultfd" run_test ${uffd_stress_bin} hugetlb-private 128 32 CATEGORY="userfaultfd" run_test ${uffd_stress_bin} shmem 20 16 CATEGORY="userfaultfd" run_test ${uffd_stress_bin} shmem-private 20 16 -# uffd-wp-mremap requires at least one page of each size. -have_all_size_hugepgs=true -declare -A nr_size_hugepgs -for f in /sys/kernel/mm/hugepages/**/nr_hugepages; do - old=$(cat $f) - nr_size_hugepgs["$f"]="$old" - if [ "$old" == 0 ]; then - echo 1 > "$f" - fi - if [ $(cat "$f") == 0 ]; then - have_all_size_hugepgs=false - break - fi -done -if $have_all_size_hugepgs; then - CATEGORY="userfaultfd" run_test ./uffd-wp-mremap -else - echo "# SKIP ./uffd-wp-mremap" -fi - -#cleanup -for f in "${!nr_size_hugepgs[@]}"; do - echo "${nr_size_hugepgs["$f"]}" > "$f" -done -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages +CATEGORY="userfaultfd" run_test ./uffd-wp-mremap CATEGORY="compaction" run_test ./compaction_test @@ -388,12 +285,10 @@ CATEGORY="mremap" run_test ./mremap_test CATEGORY="hugetlb" run_test ./thuge-gen CATEGORY="hugetlb" run_test ./charge_reserved_hugetlb.sh -cgroup-v2 CATEGORY="hugetlb" run_test ./hugetlb_reparenting_test.sh -cgroup-v2 + if $RUN_DESTRUCTIVE; then -nr_hugepages_tmp=$(cat /proc/sys/vm/nr_hugepages) enable_soft_offline=$(cat /proc/sys/vm/enable_soft_offline) -echo 8 > /proc/sys/vm/nr_hugepages CATEGORY="hugetlb" run_test ./hugetlb-soft-offline -echo "$nr_hugepages_tmp" > /proc/sys/vm/nr_hugepages echo "$enable_soft_offline" > /proc/sys/vm/enable_soft_offline CATEGORY="hugetlb" run_test ./hugetlb-read-hwpoison fi @@ -449,7 +344,6 @@ CATEGORY="ksm_numa" run_test ./ksm_tests -N -m 0 CATEGORY="ksm" run_test ./ksm_functional_tests # protection_keys tests -nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) if [ -x ./protection_keys_32 ] then CATEGORY="pkey" run_test ./protection_keys_32 @@ -459,7 +353,6 @@ if [ -x ./protection_keys_64 ] then CATEGORY="pkey" run_test ./protection_keys_64 fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages if [ -x ./soft-dirty ] then @@ -548,10 +441,6 @@ if [ -n "${LOADED_MOD}" ]; then modprobe -r hwpoison_inject > /dev/null 2>&1 fi -if [ "${HAVE_HUGEPAGES}" = 1 ]; then - echo "$orig_nr_hugepgs" > /proc/sys/vm/nr_hugepages -fi - echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix echo "1..${count_total}" | tap_output From b89a641056227403bf54847caf228b8a607c5fc4 Mon Sep 17 00:00:00 2001 From: Chen Wandun Date: Wed, 13 May 2026 13:54:28 +0800 Subject: [PATCH 4258/5214] mm/khugepaged: avoid underflow in madvise_collapse for sub-PMD MADV_COLLAPSE madvise_collapse() computes the THP-aligned window: hstart = ALIGN(start, HPAGE_PMD_SIZE); /* round up */ hend = ALIGN_DOWN(end, HPAGE_PMD_SIZE); /* round down */ The following case will cause hstart > hend, and result in underflow in the return statement, avoid it by returning zero early when hstart > hend. The return value is due to input is valid to madvise(), and there is nothing to collapse. madvise(PMD-aligned + PAGE_SIZE, PAGE_SIZE, MADV_COLLAPSE); In addition, kmalloc_obj(), mmgrab() and lru_add_drain_all() are unnecessary when hstart == hend, so skip these operations by returning early too. Link: https://lore.kernel.org/20260513055428.1664898-1-chenwandun@lixiang.com Signed-off-by: Chen Wandun Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- mm/khugepaged.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index e69a0fb4c7cc..617bca76db49 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -3198,6 +3198,12 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, if (!collapse_possible(vma, vma->vm_flags, TVA_FORCED_COLLAPSE)) return -EINVAL; + hstart = ALIGN(start, HPAGE_PMD_SIZE); + hend = ALIGN_DOWN(end, HPAGE_PMD_SIZE); + + if (hstart >= hend) + return 0; + cc = kmalloc_obj(*cc); if (!cc) return -ENOMEM; @@ -3207,9 +3213,6 @@ int madvise_collapse(struct vm_area_struct *vma, unsigned long start, mmgrab(mm); lru_add_drain_all(); - hstart = ALIGN(start, HPAGE_PMD_SIZE); - hend = ALIGN_DOWN(end, HPAGE_PMD_SIZE); - for (addr = hstart; addr < hend; addr += HPAGE_PMD_SIZE) { enum scan_result result = SCAN_FAIL; From a179686b917ca30232cb70eb4408d34d9de3814d Mon Sep 17 00:00:00 2001 From: Hongfu Li Date: Wed, 13 May 2026 10:52:23 +0800 Subject: [PATCH 4259/5214] selftests/mm: fix incorrect mmap() error handling with NULL instead of MAP_FAILED mmap() returns MAP_FAILED, which is defined as (void *)-1, on error, not NULL. Several selftests incorrectly check the return value of mmap() using !ptr or ptr == NULL, which would erroneously treat MAP_FAILED as a valid pointer since MAP_FAILED is non-zero and non-NULL. This can lead to segfaults when mmap() actually fails under memory pressure. Link: https://lore.kernel.org/20260513025223.592766-1-lihongfu@kylinos.cn Signed-off-by: Hongfu Li Reviewed-by: Dev Jain Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/ksm_tests.c | 2 +- tools/testing/selftests/mm/madv_populate.c | 2 +- tools/testing/selftests/mm/soft-dirty.c | 4 ++-- tools/testing/selftests/mm/vm_util.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c index 64d1c63ae8c0..a050f4840cfa 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -174,7 +174,7 @@ static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_ { void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0); - if (!map_ptr) { + if (map_ptr == MAP_FAILED) { ksft_perror("mmap"); return NULL; } diff --git a/tools/testing/selftests/mm/madv_populate.c b/tools/testing/selftests/mm/madv_populate.c index 88050e0f829a..7fce5d0b622b 100644 --- a/tools/testing/selftests/mm/madv_populate.c +++ b/tools/testing/selftests/mm/madv_populate.c @@ -34,7 +34,7 @@ static void sense_support(void) addr = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - if (!addr) + if (addr == MAP_FAILED) ksft_exit_fail_msg("mmap failed\n"); ret = madvise(addr, pagesize, MADV_POPULATE_READ); diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c index c426c4636ef5..fb1864a68e1c 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -143,7 +143,7 @@ static void test_mprotect(int pagemap_fd, int pagesize, bool anon) if (anon) { map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - if (!map) + if (map == MAP_FAILED) ksft_exit_fail_msg("anon mmap failed\n"); } else { test_fd = open(fname, O_RDWR | O_CREAT, 0664); @@ -155,7 +155,7 @@ static void test_mprotect(int pagemap_fd, int pagesize, bool anon) ftruncate(test_fd, pagesize); map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, test_fd, 0); - if (!map) + if (map == MAP_FAILED) ksft_exit_fail_msg("file mmap failed\n"); } diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 336a26751d3f..311fc5b4513e 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -396,7 +396,7 @@ bool softdirty_supported(void) /* New mappings are expected to be marked with VM_SOFTDIRTY (sd). */ addr = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - if (!addr) + if (addr == MAP_FAILED) ksft_exit_fail_msg("mmap failed\n"); supported = check_vmflag(addr, "sd"); From a3f66e9b6e4dad38444656ffc28c28c33a4d4e1f Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:03:27 +0530 Subject: [PATCH 4260/5214] selftests/mm: restore default nr_hugepages value via exit trap in charge_reserved_hugetlb.sh Patch series "selftests/mm: fix failures and robustness improvements", v7. Powerpc systems with a 64K base page size exposed several issues while running mm selftests. Some tests assume specific hugetlb configurations, use incorrect interfaces, or fail instead of skipping when the required kernel features are not available. This series fixes these issues and improves test robustness. This patch (of 13): cleanup() resets nr_hugepages to 0 on every invocation, while the test reconfigures it again in the next iteration. This leads to repeated allocation and freeing of large numbers of hugepages, especially when the original value is high. Additionally, with set -e, failures in earlier cleanup steps (e.g., rmdir or umount returning EBUSY while background activity is still ongoing) can cause the script to exit before restoring the original value, leaving the system in a modified state. Introduce a trap on EXIT, INT, and TERM to restore the original nr_hugepages value once at script termination. This avoids unnecessary allocation churn and ensures the original value is reliably restored on all exit paths. Link: https://lore.kernel.org/cover.1779296493.git.sayalip@linux.ibm.com Link: https://lore.kernel.org/5b8fbb29cd6ceffe6752e0af104f60cec072aa10.1779296493.git.sayalip@linux.ibm.com Fixes: 7d695b1c3695 ("selftests/mm: save and restore nr_hugepages value") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/charge_reserved_hugetlb.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index 44f4e703deb9..e1945901fd20 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -17,6 +17,7 @@ if ! command -v killall >/dev/null 2>&1; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM fault_limit_file=limit_in_bytes reservation_limit_file=rsvd.limit_in_bytes @@ -70,7 +71,6 @@ function cleanup() { if [[ -e $cgroup_path/hugetlb_cgroup_test2 ]]; then rmdir $cgroup_path/hugetlb_cgroup_test2 fi - echo 0 >/proc/sys/vm/nr_hugepages echo CLEANUP DONE } @@ -599,4 +599,3 @@ if [[ $do_umount ]]; then rmdir $cgroup_path fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 404c04e010da2edc93cada07d4d50deda12a68e4 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:03:28 +0530 Subject: [PATCH 4261/5214] selftests/mm: fix hugetlb pathname construction in charge_reserved_hugetlb.sh The charge_reserved_hugetlb.sh script assumes hugetlb cgroup memory interface file names use the "MB" format (e.g. hugetlb.1024MB.current). This assumption breaks on systems with larger huge pages such as 1GB, where the kernel exposes normalized units: hugetlb.1GB.current hugetlb.1GB.max hugetlb.1GB.rsvd.max ... As a result, the script attempts to access files like hugetlb.1024MB.current, which do not exist when the kernel reports the size in GB. Normalize the huge page size and construct the pathname using the appropriate unit (MB or GB), matching the hugetlb controller naming. Link: https://lore.kernel.org/04b6b49e4a2acf46319f627caf82b09e6dc1ad7f.1779296493.git.sayalip@linux.ibm.com Fixes: 209376ed2a84 ("selftests/vm: make charge_reserved_hugetlb.sh work with existing cgroup setting") Fixes: 29750f71a9b4 ("hugetlb_cgroup: add hugetlb_cgroup reservation tests") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- .../selftests/mm/charge_reserved_hugetlb.sh | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index e1945901fd20..a1cfd3a349db 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -94,6 +94,15 @@ function get_machine_hugepage_size() { } MB=$(get_machine_hugepage_size) +if (( MB >= 1024 )); then + # For 1GB hugepages + UNIT="GB" + MB_DISPLAY=$((MB / 1024)) +else + # For 2MB hugepages + UNIT="MB" + MB_DISPLAY=$MB +fi function setup_cgroup() { local name="$1" @@ -103,11 +112,12 @@ function setup_cgroup() { mkdir $cgroup_path/$name echo writing cgroup limit: "$cgroup_limit" - echo "$cgroup_limit" >$cgroup_path/$name/hugetlb.${MB}MB.$fault_limit_file + echo "$cgroup_limit" > \ + $cgroup_path/$name/hugetlb.${MB_DISPLAY}${UNIT}.$fault_limit_file echo writing reservation limit: "$reservation_limit" echo "$reservation_limit" > \ - $cgroup_path/$name/hugetlb.${MB}MB.$reservation_limit_file + $cgroup_path/$name/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_limit_file if [ -e "$cgroup_path/$name/cpuset.cpus" ]; then echo 0 >$cgroup_path/$name/cpuset.cpus @@ -142,7 +152,7 @@ function wait_for_file_value() { function wait_for_hugetlb_memory_to_get_depleted() { local cgroup="$1" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file" wait_for_file_value "$path" "0" } @@ -150,7 +160,7 @@ function wait_for_hugetlb_memory_to_get_depleted() { function wait_for_hugetlb_memory_to_get_reserved() { local cgroup="$1" local size="$2" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file" wait_for_file_value "$path" "$size" } @@ -158,7 +168,7 @@ function wait_for_hugetlb_memory_to_get_reserved() { function wait_for_hugetlb_memory_to_get_written() { local cgroup="$1" local size="$2" - local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file" + local path="$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file" wait_for_file_value "$path" "$size" } @@ -180,8 +190,8 @@ function write_hugetlbfs_and_get_usage() { hugetlb_difference=0 reserved_difference=0 - local hugetlb_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file - local reserved_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file + local hugetlb_usage=$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local reserved_usage=$cgroup_path/$cgroup/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file local hugetlb_before=$(cat $hugetlb_usage) local reserved_before=$(cat $reserved_usage) @@ -312,8 +322,10 @@ function run_test() { cleanup_hugetlb_memory "hugetlb_cgroup_test" - local final_hugetlb=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$fault_usage_file) - local final_reservation=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$reservation_usage_file) + local final_hugetlb=$(cat \ + $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file) + local final_reservation=$(cat \ + $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file) echo $hugetlb_difference echo $reserved_difference @@ -369,10 +381,14 @@ function run_multiple_cgroup_test() { reservation_failed1=$reservation_failed oom_killed1=$oom_killed - local cgroup1_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$fault_usage_file - local cgroup1_reservation_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$reservation_usage_file - local cgroup2_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$fault_usage_file - local cgroup2_reservation_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$reservation_usage_file + local cgroup1_hugetlb_usage=\ + $cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local cgroup1_reservation_usage=\ + $cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file + local cgroup2_hugetlb_usage=\ + $cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB_DISPLAY}${UNIT}.$fault_usage_file + local cgroup2_reservation_usage=\ + $cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB_DISPLAY}${UNIT}.$reservation_usage_file local usage_before_second_write=$(cat $cgroup1_hugetlb_usage) local reservation_usage_before_second_write=$(cat $cgroup1_reservation_usage) From 7f9c0920ff3debae3cb4d60260e3daf56ab68395 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:43 +0530 Subject: [PATCH 4262/5214] selftests/mm: restore default nr_hugepages value via exit trap in hugetlb_reparenting_test.sh The test modifies nr_hugepages during execution and restores it from cleanup() and again reconfigure it setup, which is invoked multiple times across test flow. This can lead to repeated allocation/freeing of hugepages. With set -e, failures in cleanup (e.g., rmdir/umount) can also cause early exit before restoring the original value at the end. Move restoration of the original nr_hugepages value to a trap handler registered for EXIT, INT, and TERM signals so it is always restored on all exit paths. This also avoids unnecessary allocation churn across repeated cleanup/setup cycles. Link: https://lore.kernel.org/29db637c3c6ba6c168f6b33f59f059a0b39c35c8.1779296493.git.sayalip@linux.ibm.com Fixes: 585a9145886a ("selftests/mm: restore default nr_hugepages value during cleanup in hugetlb_reparenting_test.sh") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb_reparenting_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 0dd31892ff67..11f914831146 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -12,6 +12,8 @@ if [[ $(id -u) -ne 0 ]]; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM + usage_file=usage_in_bytes if [[ "$1" == "-cgroup-v2" ]]; then @@ -56,7 +58,6 @@ function cleanup() { rmdir "$CGROUP_ROOT"/a/b 2>/dev/null rmdir "$CGROUP_ROOT"/a 2>/dev/null rmdir "$CGROUP_ROOT"/test1 2>/dev/null - echo $nr_hugepgs >/proc/sys/vm/nr_hugepages set -e } @@ -240,4 +241,3 @@ if [[ $do_umount ]]; then rm -rf $CGROUP_ROOT fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 76f9a212a0a93572a28b58d9869e9187c4022342 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:44 +0530 Subject: [PATCH 4263/5214] selftests/mm: fix hugetlb pathname construction in hugetlb_reparenting_test.sh The hugetlb_reparenting_test.sh script constructs hugetlb cgroup memory interface file names based on the configured huge page size. The script formats the size only in MB units, which causes mismatches on systems using larger huge pages where the kernel exposes normalized units (e.g. "1GB" instead of "1024MB"). As a result, the test fails to locate the corresponding cgroup files when 1GB huge pages are configured. Update the script to detect the huge page size and select the appropriate unit (MB or GB) so that the constructed paths match the kernel's hugetlb controller naming. Also print an explicit "Fail" message when a test failure occurs to improve result visibility. Link: https://lore.kernel.org/837ce751965c93f74c95d89587debf1e93281364.1779296493.git.sayalip@linux.ibm.com Fixes: e487a5d513cb ("selftest/mm: make hugetlb_reparenting_test tolerant to async reparenting") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- .../selftests/mm/hugetlb_reparenting_test.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 11f914831146..d724b6e45432 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -48,6 +48,13 @@ function get_machine_hugepage_size() { } MB=$(get_machine_hugepage_size) +if (( MB >= 1024 )); then + UNIT="GB" + MB_DISPLAY=$((MB / 1024)) +else + UNIT="MB" + MB_DISPLAY=$MB +fi function cleanup() { echo cleanup @@ -88,6 +95,7 @@ function assert_with_retry() { if [[ $elapsed -ge $timeout ]]; then echo "actual = $((${actual%% *} / 1024 / 1024)) MB" echo "expected = $((${expected%% *} / 1024 / 1024)) MB" + echo FAIL cleanup exit 1 fi @@ -108,11 +116,13 @@ function assert_state() { fi assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" - assert_with_retry "$CGROUP_ROOT/a/hugetlb.${MB}MB.$usage_file" "$expected_a_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" - assert_with_retry "$CGROUP_ROOT/a/b/hugetlb.${MB}MB.$usage_file" "$expected_b_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi } From 7bbdc459527075c4d0b96133de25a53822397af0 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:45 +0530 Subject: [PATCH 4264/5214] selftests/mm: fix cgroup task placement and drop memory.current checks in hugetlb_reparenting_test.sh The test currently moves the calling shell ($$) into the target cgroup before executing write_to_hugetlbfs. This results in the shell and any intermediate allocations being charged to the cgroup, introducing noise and nondeterminism in accounting. It also requires moving the shell back to the root cgroup after execution. Spawn a helper process that joins the target cgroup and exec()'s write_to_hugetlbfs. This ensures that only the workload is accounted to the cgroup and avoids unintended charging from the shell. The test currently validates both hugetlb usage and memory.current. However, memory.current includes internal memcg allocations and per-CPU batched accounting (MEMCG_CHARGE_BATCH), which are not synchronized and can vary across systems, leading to non-deterministic results. Since hugetlb memory is accounted via hugetlb..current, memory.current is not a reliable indicator here. Drop memory.current checks and rely only on hugetlb controller statistics for stable and accurate validation. Link: https://lore.kernel.org/fb57491ba83cb0a499c72922e1579b61bee514db.1779296493.git.sayalip@linux.ibm.com Fixes: 29750f71a9b4 ("hugetlb_cgroup: add hugetlb_cgroup reservation tests") Signed-off-by: Sayali Patil Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Venkat Rao Bagalkote Cc: Zi Yan Signed-off-by: Andrew Morton --- .../selftests/mm/hugetlb_reparenting_test.sh | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index d724b6e45432..95f517c3bd16 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -105,22 +105,17 @@ function assert_with_retry() { } function assert_state() { - local expected_a="$1" - local expected_a_hugetlb="$2" - local expected_b="" + local expected_a_hugetlb="$1" local expected_b_hugetlb="" - if [ ! -z ${3:-} ] && [ ! -z ${4:-} ]; then - expected_b="$3" - expected_b_hugetlb="$4" + if [ ! -z ${2:-} ]; then + expected_b_hugetlb="$2" fi - assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" assert_with_retry \ "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" - if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then - assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" + if [[ -n "$expected_b_hugetlb" ]]; then assert_with_retry \ "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi @@ -154,18 +149,17 @@ write_hugetlbfs() { local size="$3" if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/$cgroup/cgroup.procs + cg_file="$CGROUP_ROOT/$cgroup/cgroup.procs" else echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus - echo $$ >"$CGROUP_ROOT/$cgroup/tasks" - fi - ./write_to_hugetlbfs -p "$path" -s "$size" -m 0 -o - if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/cgroup.procs - else - echo $$ >"$CGROUP_ROOT/tasks" + cg_file="$CGROUP_ROOT/$cgroup/tasks" fi + + # Spawn helper to join cgroup before exec to ensure correct cgroup accounting + bash -c 'echo $$ > "$1"; exec ./write_to_hugetlbfs -p "$2" -s "$3" -m 0 -o' _ \ + "$cg_file" "$path" "$size" & pid=$! + wait "$pid" echo } @@ -203,21 +197,21 @@ if [[ ! $cgroup2 ]]; then write_hugetlbfs a "$MNT"/test $size echo Assert memory charged correctly for parent use. - assert_state 0 $size 0 0 + assert_state $size 0 write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child use. - assert_state 0 $(($size * 2)) 0 $size + assert_state $(($size * 2)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. - assert_state 0 $(($size * 2)) + assert_state $(($size * 2)) rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. - assert_state 0 0 + assert_state 0 cleanup fi @@ -231,16 +225,16 @@ echo write write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child only use. -assert_state 0 $(($size)) 0 $size +assert_state $(($size)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. -assert_state 0 $size +assert_state $size rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. -assert_state 0 0 +assert_state 0 cleanup From a2dde8a065d5c8d63b53b1c8f035017e4aeac0c2 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:46 +0530 Subject: [PATCH 4265/5214] selftests/mm: size tmpfs according to PMD page size in split_huge_page_test The split_file_backed_thp() test mounts a tmpfs with a fixed size of "4m". This works on systems with smaller PMD page sizes, but fails on configurations where the PMD huge page size is larger (e.g. 16MB). On such systems, the fixed 4MB tmpfs is insufficient to allocate even a single PMD-sized THP, causing the test to fail. Fix this by sizing the tmpfs dynamically based on the runtime pmd_pagesize, allocating space for two PMD-sized pages. Before patch: running ./split_huge_page_test /tmp/xfs_dir_YTrI5E -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Failed to write data to testing file: Success (0) Bail out! Error occurred Planned tests != run tests (55 != 9) Totals: pass:9 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL] After patch: running ./split_huge_page_test /tmp/xfs_dir_bMvj6o -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 10 File-backed THP split to order 0 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 11 File-backed THP split to order 1 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 12 File-backed THP split to order 2 test done ... ok 55 Split PMD-mapped pagecache folio to order 7 at in-folio offset 128 passed Totals: pass:55 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 split_huge_page_test /tmp/xfs_dir_bMvj6o Link: https://lore.kernel.org/33e1bc10753fe82d1217613d8cd496020778cf2b.1779296493.git.sayalip@linux.ibm.com Fixes: fbe37501b252 ("mm: huge_memory: debugfs for file-backed THP split") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Reviewed-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/split_huge_page_test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index a1ccfabc5367..9a50557069ad 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -470,6 +470,8 @@ static void split_file_backed_thp(int order) char tmpfs_template[] = "/tmp/thp_split_XXXXXX"; const char *tmpfs_loc = mkdtemp(tmpfs_template); char testfile[INPUT_MAX]; + unsigned long size = 2 * pmd_pagesize; + char opts[64]; ssize_t num_written, num_read; char *file_buf1, *file_buf2; uint64_t pgoff_start = 0, pgoff_end = 1024; @@ -489,7 +491,8 @@ static void split_file_backed_thp(int order) file_buf1[i] = (char)i; memset(file_buf2, 0, pmd_pagesize); - status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m"); + snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); + status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); if (status) ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); From e5d3e1422e92d54e28e81fad8bfc7c1ecb41a353 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:47 +0530 Subject: [PATCH 4266/5214] selftests/mm: free dynamically allocated PMD-sized buffers in split_huge_page_test Dynamically allocated buffers of PMD size for file-backed THP operations (file_buf1 and file_buf2) were not freed on the success path and some failure paths. Since the function is called repeatedly in a loop for each split order, this can cause significant memory leaks. On architectures with large PMD sizes, repeated leaks could exhaust system memory and trigger the OOM killer during test execution. Ensure all allocated buffers are freed to maintain stable repeated test runs. Link: https://lore.kernel.org/060c673b376bbeeed2b1fb1d48a825e846654191.1779296493.git.sayalip@linux.ibm.com Fixes: 035a112e5fd5 ("selftests/mm: make file-backed THP split work by writing PMD size data") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- .../selftests/mm/split_huge_page_test.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 9a50557069ad..32b991472f74 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -473,12 +473,15 @@ static void split_file_backed_thp(int order) unsigned long size = 2 * pmd_pagesize; char opts[64]; ssize_t num_written, num_read; - char *file_buf1, *file_buf2; + char *file_buf1 = NULL, *file_buf2 = NULL; uint64_t pgoff_start = 0, pgoff_end = 1024; int i; ksft_print_msg("Please enable pr_debug in split_huge_pages_in_file() for more info.\n"); + if (!tmpfs_loc) + ksft_exit_fail_msg("mkdtemp failed\n"); + file_buf1 = (char *)malloc(pmd_pagesize); file_buf2 = (char *)malloc(pmd_pagesize); @@ -494,8 +497,10 @@ static void split_file_backed_thp(int order) snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); - if (status) - ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); + if (status) { + ksft_print_msg("Unable to create a tmpfs for testing\n"); + goto out; + } status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc); if (status >= INPUT_MAX) { @@ -547,10 +552,13 @@ static void split_file_backed_thp(int order) status = umount(tmpfs_loc); if (status) { - rmdir(tmpfs_loc); - ksft_exit_fail_msg("Unable to umount %s\n", tmpfs_loc); + ksft_print_msg("Unable to umount %s\n", tmpfs_loc); + goto out; } + free(file_buf1); + free(file_buf2); + status = rmdir(tmpfs_loc); if (status) ksft_exit_fail_msg("cannot remove tmp dir: %s\n", strerror(errno)); @@ -563,8 +571,10 @@ static void split_file_backed_thp(int order) close(fd); cleanup: umount(tmpfs_loc); - rmdir(tmpfs_loc); out: + free(file_buf1); + free(file_buf2); + rmdir(tmpfs_loc); ksft_exit_fail_msg("Error occurred\n"); } From 1df31d5ef58dc6bdf0f2a8b809152d835c60b28e Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:48 +0530 Subject: [PATCH 4267/5214] selftest/mm: register existing mapping with userfaultfd in hugetlb-mremap Previously, register_region_with_uffd() created a new anonymous mapping and overwrote the address supplied by the caller before registering the range with userfaultfd. As a result, userfaultfd was applied to an unrelated anonymous mapping instead of the hugetlb region used by the test. Remove the extra mmap() and register the caller-provided address range directly using UFFDIO_REGISTER_MODE_MISSING, so that faults are generated for the hugetlb mapping used by the test. This ensures userfaultfd operates on the actual hugetlb test region and validates the expected fault handling. Before patch: running ./hugetlb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Address returned by mmap() = 0x7fff9d000000 Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap After patch: running ./hugetb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Registered memory at address 0x7eaa40000000 with userfaultfd Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap Link: https://lore.kernel.org/13845da872ed174316173e8996dbb5f181994017.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-mremap.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c index d239905790dd..00b4c2cc95a6 100644 --- a/tools/testing/selftests/mm/hugetlb-mremap.c +++ b/tools/testing/selftests/mm/hugetlb-mremap.c @@ -86,25 +86,14 @@ static void register_region_with_uffd(char *addr, size_t len) if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) ksft_exit_fail_msg("ioctl-UFFDIO_API: %s\n", strerror(errno)); - /* Create a private anonymous mapping. The memory will be - * demand-zero paged--that is, not yet allocated. When we - * actually touch the memory, it will be allocated via - * the userfaultfd. - */ - - addr = mmap(NULL, len, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (addr == MAP_FAILED) - ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); - - ksft_print_msg("Address returned by mmap() = %p\n", addr); - - /* Register the memory range of the mapping we just created for - * handling by the userfaultfd object. In mode, we request to track - * missing pages (i.e., pages that have not yet been faulted in). + /* Register the passed memory range for handling by the userfaultfd object. + * In mode, we request to track missing pages + * (i.e., pages that have not yet been faulted in). */ if (uffd_register(uffd, addr, len, true, false, false)) ksft_exit_fail_msg("ioctl-UFFDIO_REGISTER: %s\n", strerror(errno)); + + ksft_print_msg("Registered memory at address %p with userfaultfd\n", addr); } int main(int argc, char *argv[]) From f20ca8972e83f111c4300c60d74314568c8964bc Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:49 +0530 Subject: [PATCH 4268/5214] selftests/mm: ensure destination is hugetlb-backed in hugetlb-mremap The hugetlb-mremap selftest reserves the destination address using a anonymous base-page mapping before calling mremap() with MREMAP_FIXED, while the source region is hugetlb-backed. When remapping a hugetlb mapping into a base-page VMA may fail with: mremap: Device or resource busy This is observed on powerpc hash MMU systems where slice constraints and page size incompatibilities prevent the remap. Ensure the destination region is created using MAP_HUGETLB so that both source and destination VMAs are hugetlb-backed and compatible. Update the FLAGS macro to include MAP_HUGETLB | MAP_SHARED so that both mappings are hugetlb-backed and compatible. Also use the macro for the mmap() calls to avoid repeating the flag combination. This ensures the test reliably exercises hugetlb mremap instead of failing due to VMA type mismatch. Link: https://lore.kernel.org/367644df45c65098f23e3945c6a80f4b8a8964a6.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hugetlb-mremap.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c index 00b4c2cc95a6..ed3d92e862d8 100644 --- a/tools/testing/selftests/mm/hugetlb-mremap.c +++ b/tools/testing/selftests/mm/hugetlb-mremap.c @@ -32,7 +32,7 @@ #define MB_TO_BYTES(x) (x * 1024 * 1024) #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC) -#define FLAGS (MAP_SHARED | MAP_ANONYMOUS) +#define FLAGS (MAP_HUGETLB | MAP_SHARED) static void check_bytes(char *addr) { @@ -132,23 +132,20 @@ int main(int argc, char *argv[]) /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */ unsigned long suggested_addr = 0x7eaa40000000; - void *haddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *haddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map haddr: Returned address is %p\n", haddr); if (haddr == MAP_FAILED) ksft_exit_fail_msg("mmap1: %s\n", strerror(errno)); /* mmap again to a dummy address to hopefully trigger pmd sharing. */ suggested_addr = 0x7daa40000000; - void *daddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *daddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map daddr: Returned address is %p\n", daddr); if (daddr == MAP_FAILED) ksft_exit_fail_msg("mmap3: %s\n", strerror(errno)); suggested_addr = 0x7faa40000000; - void *vaddr = - mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0); + void *vaddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map vaddr: Returned address is %p\n", vaddr); if (vaddr == MAP_FAILED) ksft_exit_fail_msg("mmap2: %s\n", strerror(errno)); From 3640d3be3d693a3660718fd8ba92393ab3e67e15 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:50 +0530 Subject: [PATCH 4269/5214] selftests/mm: skip uffd-wp-mremap if UFFD write-protect is unsupported The uffd-wp-mremap test requires the UFFD_FEATURE_PAGEFAULT_FLAG_WP capability. On systems where userfaultfd write-protect is not supported, uffd_register() fails and the test reports failures. Check for the required feature at startup and skip the test when the UFFD_FEATURE_PAGEFAULT_FLAG_WP capability is not present, preventing false failures on unsupported configurations. Before patch: running ./uffd-wp-mremap ------------------------ [INFO] detected THP size: 256 KiB [INFO] detected THP size: 512 KiB [INFO] detected THP size: 1024 KiB [INFO] detected THP size: 2048 KiB [INFO] detected hugetlb page size: 2048 KiB [INFO] detected hugetlb page size: 1048576 KiB 1..24 [RUN] test_one_folio(size=65536, private=false, swapout=false, hugetlb=false) not ok 1 uffd_register() failed [RUN] test_one_folio(size=65536, private=true, swapout=false, hugetlb=false) not ok 2 uffd_register() failed [RUN] test_one_folio(size=65536, private=false, swapout=true, hugetlb=false) not ok 3 uffd_register() failed [RUN] test_one_folio(size=65536, private=true, swapout=true, hugetlb=false) not ok 4 uffd_register() failed [RUN] test_one_folio(size=262144, private=false, swapout=false, hugetlb=false) not ok 5 uffd_register() failed [RUN] test_one_folio(size=524288, private=false, swapout=false, hugetlb=false) not ok 6 uffd_register() failed . . . Bail out! 24 out of 24 tests failed Totals: pass:0 fail:24 xfail:0 xpass:0 skip:0 error:0 [FAIL] not ok 1 uffd-wp-mremap # exit=1 After patch: running ./uffd-wp-mremap ------------------------ 1..0 # SKIP uffd-wp feature not supported [SKIP] ok 1 uffd-wp-mremap # SKIP Link: https://lore.kernel.org/c3c5af76d71d5f4446f773f4de94882efc33ebe4.1779296493.git.sayalip@linux.ibm.com Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-wp-mremap.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index 90ac410c6c6f..c973d6722720 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -19,6 +19,17 @@ static size_t thpsizes[20]; static int nr_hugetlbsizes; static unsigned long hugetlbsizes[10]; +static void check_uffd_wp_feature_supported(void) +{ + uint64_t features = 0; + + if (uffd_get_features(&features)) + ksft_exit_skip("failed to get available features (%d)\n", errno); + + if (!(features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) + ksft_exit_skip("uffd-wp feature not supported\n"); +} + static int detect_thp_sizes(size_t sizes[], int max) { int count = 0; @@ -338,6 +349,8 @@ int main(int argc, char **argv) hugepage_save_settings(true, true); + check_uffd_wp_feature_supported(); + pagesize = getpagesize(); nr_thpsizes = detect_thp_sizes(thpsizes, ARRAY_SIZE(thpsizes)); nr_hugetlbsizes = hugetlb_setup(1, hugetlbsizes, ARRAY_SIZE(hugetlbsizes)); From f3bd00507f226a419125df6064632bcbd47d8e70 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:51 +0530 Subject: [PATCH 4270/5214] selftests/mm: skip uffd-stress test when nr_pages_per_cpu is zero uffd-stress currently fails when the computed nr_pages_per_cpu evaluates to zero: nr_pages_per_cpu = bytes / page_size / nr_parallel This can occur on systems with large hugepage sizes (e.g. 1GB) and a high number of CPUs, where the total allocated memory is sufficient overall but not enough to provide at least one page per cpu. In such cases, the failure is due to insufficient test resources rather than incorrect kernel behaviour. Update the test to treat this condition as a test skip instead of reporting an error. [sayalip@linux.ibm.com: use ksft_exit_skip() instead of KSFT_SKIP] Link: https://lore.kernel.org/88202b56-1dc5-43e2-9d1f-a0823a9531f0@linux.ibm.com Link: https://lore.kernel.org/0707e9a0f1b3dd904c4a069b91db317f9c160faa.1779296493.git.sayalip@linux.ibm.com Fixes: db0f1c138f18 ("selftests/mm: print some details when uffd-stress gets bad params") Signed-off-by: Sayali Patil Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/uffd-stress.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 43cc79590136..3401dd6028f0 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -489,9 +489,8 @@ int main(int argc, char **argv) gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; if (!gopts->nr_pages_per_cpu) { - _err("pages_per_cpu = 0, cannot test (%lu / %lu / %lu)", - bytes, gopts->page_size, gopts->nr_parallel); - usage(); + ksft_exit_skip("pages_per_cpu = 0, cannot test (%zu / %lu / %lu)\n", + bytes, gopts->page_size, gopts->nr_parallel); } bounces = atoi(argv[3]); From 90c132f8379b788a0482dbeabc884d3ae5d82205 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:52 +0530 Subject: [PATCH 4271/5214] selftests/mm: move hwpoison setup into run_test() and silence modprobe output for memory-failure category run_vmtests.sh contains special handling to ensure the hwpoison_inject module is available for the memory-failure tests. This logic was implemented outside of run_test(), making the setup category-specific but managed globally. Move the hwpoison_inject handling into run_test() and restrict it to the memory-failure category so that: 1. the module is checked and loaded only when memory-failure tests run, 2. the test is skipped if the module or the debugfs interface (/sys/kernel/debug/hwpoison/) is not available. 3. the module is unloaded after the test if it was loaded by the script. This localizes category-specific setup and makes the test flow consistent with other per-category preparations. While updating this logic, fix the module availability check. The script previously used: modprobe -R hwpoison_inject The -R option prints the resolved module name to stdout, causing every run to print: hwpoison_inject in the test output, even when no action is required, introducing unnecessary noise. Replace this with: modprobe -n hwpoison_inject which verifies that the module is loadable without producing output, keeping the selftest logs clean and consistent. Also, ensure that skipped tests do not override a previously recorded failure. A skipped test currently sets exitcode to ksft_skip even if a prior test has failed, which can mask failures in the final exit status. Update the logic to only set exitcode to ksft_skip when no failure has been recorded. Link: https://lore.kernel.org/93441f34f7ef5add47d1a130d03daa79e21b5050.1779296493.git.sayalip@linux.ibm.com Fixes: ff4ef2fbd101 ("selftests/mm: add memory failure anonymous page test") Signed-off-by: Sayali Patil Reviewed-by: Miaohe Lin Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/run_vmtests.sh | 62 +++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 043aa3ed2596..8c296dedf047 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -180,6 +180,9 @@ pretty_name() { # Usage: run_test [test binary] [arbitrary test arguments...] run_test() { if test_selected ${CATEGORY}; then + local skip=0 + local LOADED_HWPOISON_INJECT_MOD=0 + # On memory constrainted systems some tests can fail to allocate hugepages. # perform some cleanup before the test for a higher success rate. if [ ${CATEGORY} == "thp" -o ${CATEGORY} == "hugetlb" ]; then @@ -194,13 +197,45 @@ run_test() { fi fi + # Ensure hwpoison_inject is available for memory-failure tests + if [ "${CATEGORY}" = "memory-failure" ]; then + # Try to load hwpoison_inject if not present. + HWPOISON_DIR=/sys/kernel/debug/hwpoison/ + if [ ! -d "$HWPOISON_DIR" ]; then + if ! modprobe -n hwpoison_inject > /dev/null 2>&1; then + echo "Module hwpoison_inject not found, skipping..." \ + | tap_prefix + skip=1 + else + modprobe hwpoison_inject > /dev/null 2>&1 + LOADED_HWPOISON_INJECT_MOD=1 + if [ ! -d "$HWPOISON_DIR" ]; then + echo "hwpoison debugfs interface not present" \ + | tap_prefix + skip=1 + fi + fi + fi + + fi + local test=$(pretty_name "$*") local title="running $*" local sep=$(echo -n "$title" | tr "[:graph:][:space:]" -) printf "%s\n%s\n%s\n" "$sep" "$title" "$sep" | tap_prefix - ("$@" 2>&1) | tap_prefix - local ret=${PIPESTATUS[0]} + if [ $skip -eq 1 ]; then + local ret=$ksft_skip + else + ("$@" 2>&1) | tap_prefix + local ret=${PIPESTATUS[0]} + fi + + # Unload hwpoison_inject if we loaded it + if [ "${LOADED_HWPOISON_INJECT_MOD}" = "1" ]; then + modprobe -r hwpoison_inject > /dev/null 2>&1 + fi + count_total=$(( count_total + 1 )) if [ $ret -eq 0 ]; then count_pass=$(( count_pass + 1 )) @@ -210,7 +245,9 @@ run_test() { count_skip=$(( count_skip + 1 )) echo "[SKIP]" | tap_prefix echo "ok ${count_total} ${test} # SKIP" | tap_output - exitcode=$ksft_skip + if [ $exitcode -eq 0 ]; then + exitcode=$ksft_skip + fi else count_fail=$(( count_fail + 1 )) echo "[FAIL]" | tap_prefix @@ -422,24 +459,7 @@ CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned CATEGORY="rmap" run_test ./rmap -# Try to load hwpoison_inject if not present. -HWPOISON_DIR=/sys/kernel/debug/hwpoison/ -if [ ! -d "$HWPOISON_DIR" ]; then - if ! modprobe -q -R hwpoison_inject; then - echo "Module hwpoison_inject not found, skipping..." - else - modprobe hwpoison_inject > /dev/null 2>&1 - LOADED_MOD=1 - fi -fi - -if [ -d "$HWPOISON_DIR" ]; then - CATEGORY="memory-failure" run_test ./memory-failure -fi - -if [ -n "${LOADED_MOD}" ]; then - modprobe -r hwpoison_inject > /dev/null 2>&1 -fi +CATEGORY="memory-failure" run_test ./memory-failure echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix echo "1..${count_total}" | tap_output From 69ba1d76d93ab818245333cf400928477ba72053 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:53 +0530 Subject: [PATCH 4272/5214] selftests/mm: clarify alternate unmapping in compaction_test Add a comment explaining that every other entry in the list is unmapped to intentionally create fragmentation with locked pages before invoking check_compaction(). Link: https://lore.kernel.org/da5e0a8d5152e54152c0d2f456aac2fac35af291.1779296493.git.sayalip@linux.ibm.com Fixes: bd67d5c15cc1 ("Test compaction of mlocked memory") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/compaction_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c index de0633f9a7e5..5b582588e015 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -181,6 +181,9 @@ int main(int argc, char **argv) mem_fragmentable_MB -= MAP_SIZE_MB; } + /* Unmap every other entry in the list to create fragmentation with + * locked pages before invoking check_compaction(). + */ for (entry = list; entry != NULL; entry = entry->next) { munmap(entry->map, MAP_SIZE); if (!entry->next) From 1d0ac576f34260e0b7878598c79c8b141039d799 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Tue, 26 May 2026 17:26:41 +0800 Subject: [PATCH 4273/5214] MAINTAINERS: add Hao Ge as reviewer for codetag and alloc_tag I've been contributing to the codetag and alloc_tag subsystems since 2024, Memory allocation profiling is indeed a great tool and I'd like to stay involved as a reviewer to keep up with ongoing development and not miss any of the details. I'm happy to help review patches and contribute to the ongoing development of this subsystem. Link: https://lore.kernel.org/20260526092641.299399-1-hao.ge@linux.dev Signed-off-by: Hao Ge Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Cc: "David Hildenbrand (Arm)" Cc: Lorenzo Stoakes Signed-off-by: Andrew Morton --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 63fa4f9fa4c8..1cbb117b75f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6349,6 +6349,7 @@ F: Documentation/process/code-of-conduct.rst CODE TAGGING M: Suren Baghdasaryan M: Kent Overstreet +R: Hao Ge S: Maintained F: include/asm-generic/codetag.lds.h F: include/linux/codetag.h @@ -16706,6 +16707,7 @@ F: tools/testing/memblock/ MEMORY ALLOCATION PROFILING M: Suren Baghdasaryan M: Kent Overstreet +R: Hao Ge L: linux-mm@kvack.org S: Maintained F: Documentation/mm/allocation-profiling.rst From 6f64c06f43098ea0aa0a67d0ad15124b5d2ba0fe Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 1 Jun 2026 13:34:28 +0200 Subject: [PATCH 4274/5214] mm: merge writeout into pageout writeout is only called from pageout, and a straight flow at the end, so merge the two functions. Link: https://lore.kernel.org/20260601113449.3464734-3-hch@lst.de Signed-off-by: Christoph Hellwig Reviewed-by: Baoquan He Reviewed-by: Nhat Pham Acked-by: David Hildenbrand (Arm) Cc: Chris Li Cc: Kairui Song Cc: Kemeng Shi Signed-off-by: Andrew Morton --- mm/vmscan.c | 63 ++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 3f3ff25e561a..299b5d9e8836 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -612,11 +612,38 @@ typedef enum { PAGE_CLEAN, } pageout_t; -static pageout_t writeout(struct folio *folio, struct address_space *mapping, - struct swap_iocb **plug, struct list_head *folio_list) +/* + * pageout is called by shrink_folio_list() for each dirty folio. + */ +static pageout_t pageout(struct folio *folio, struct address_space *mapping, + struct swap_iocb **plug, struct list_head *folio_list) { int res; + /* + * We no longer attempt to writeback filesystem folios here, other + * than tmpfs/shmem. That's taken care of in page-writeback. + * If we find a dirty filesystem folio at the end of the LRU list, + * typically that means the filesystem is saturating the storage + * with contiguous writes and telling it to write a folio here + * would only make the situation worse by injecting an element + * of random access. + * + * If the folio is swapcache, write it back even if that would + * block, for some throttling. This happens by accident, because + * swap_backing_dev_info is bust: it doesn't reflect the + * congestion state of the swapdevs. Easy to fix, if needed. + * + * A freeable shmem or swapcache folio is referenced only by the + * caller that isolated the folio and the page cache. + */ + if (folio_ref_count(folio) != 1 + folio_nr_pages(folio) || !mapping) + return PAGE_KEEP; + if (!shmem_mapping(mapping) && !folio_test_anon(folio)) + return PAGE_ACTIVATE; + if (!folio_clear_dirty_for_io(folio)) + return PAGE_CLEAN; + folio_set_reclaim(folio); /* @@ -645,38 +672,6 @@ static pageout_t writeout(struct folio *folio, struct address_space *mapping, return PAGE_SUCCESS; } -/* - * pageout is called by shrink_folio_list() for each dirty folio. - */ -static pageout_t pageout(struct folio *folio, struct address_space *mapping, - struct swap_iocb **plug, struct list_head *folio_list) -{ - /* - * We no longer attempt to writeback filesystem folios here, other - * than tmpfs/shmem. That's taken care of in page-writeback. - * If we find a dirty filesystem folio at the end of the LRU list, - * typically that means the filesystem is saturating the storage - * with contiguous writes and telling it to write a folio here - * would only make the situation worse by injecting an element - * of random access. - * - * If the folio is swapcache, write it back even if that would - * block, for some throttling. This happens by accident, because - * swap_backing_dev_info is bust: it doesn't reflect the - * congestion state of the swapdevs. Easy to fix, if needed. - * - * A freeable shmem or swapcache folio is referenced only by the - * caller that isolated the folio and the page cache. - */ - if (folio_ref_count(folio) != 1 + folio_nr_pages(folio) || !mapping) - return PAGE_KEEP; - if (!shmem_mapping(mapping) && !folio_test_anon(folio)) - return PAGE_ACTIVATE; - if (!folio_clear_dirty_for_io(folio)) - return PAGE_CLEAN; - return writeout(folio, mapping, plug, folio_list); -} - /* * Same as remove_mapping, but if the folio is removed from the mapping, it * gets returned with a refcount of 0. From cc13a7a618fe8354f16d74c06aaf9565a68e9ebd Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Thu, 11 Jun 2026 12:01:55 +0200 Subject: [PATCH 4275/5214] selftests: mm: fix and speedup "droppable" test The droppable test currently relies on creating memory pressure in a child process to trigger dropping the droppable pages. That not only takes a long time on some machines (allocating and filling all that memory), on large machines this will not work as we hardcode the area size to 134217728 bytes. ... further, we rely on timeouts to detect that memory was not dropped, which is really suboptimal. Instead, let's just use MADV_PAGEOUT on a 2 MiB region. MADV_PAGEOUT works with droppable memory even without swap. There is the low chance of MADV_PAGEOUT failing to drop a page because of speculative references. We'll wait 1s and retry 10 times to rule that unlikely case out as best as we can. On a machine without swap: $ ./droppable TAP version 13 1..1 ok 1 madvise(MADV_PAGEOUT) behavior # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Link: https://lore.kernel.org/20260611-droppable_test-v1-1-b6a73d99f658@kernel.org Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: David Hildenbrand (Arm) Reported-by: Aishwarya TCV Tested-by: Sarthak Sharma Tested-by: Lance Yang Reviewed-by: Dev Jain Reviewed-by: SeongJae Park Tested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Reviewed-by: Jason A. Donenfeld Cc: Anthony Yznaga Cc: Liam R. Howlett Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/droppable.c | 48 +++++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/mm/droppable.c b/tools/testing/selftests/mm/droppable.c index 30c8be37fcb9..57e1b6fc5569 100644 --- a/tools/testing/selftests/mm/droppable.c +++ b/tools/testing/selftests/mm/droppable.c @@ -17,10 +17,10 @@ int main(int argc, char *argv[]) { - size_t alloc_size = 134217728; - size_t page_size = getpagesize(); + const size_t alloc_size = 2 * 1024 * 1024; + int retry_count = 10; + bool dropped; void *alloc; - pid_t child; ksft_print_header(); ksft_set_plan(1); @@ -35,26 +35,32 @@ int main(int argc, char *argv[]) exit(KSFT_FAIL); } memset(alloc, 'A', alloc_size); - for (size_t i = 0; i < alloc_size; i += page_size) - assert(*(uint8_t *)(alloc + i)); - child = fork(); - assert(child >= 0); - if (!child) { - for (;;) - *(char *)malloc(page_size) = 'B'; - } - - for (bool done = false; !done;) { - for (size_t i = 0; i < alloc_size; i += page_size) { - if (!*(uint8_t *)(alloc + i)) { - done = true; - break; + while (retry_count--) { + if (madvise(alloc, alloc_size, MADV_PAGEOUT)) { + if (errno == EINVAL) { + ksft_test_result_skip("madvise(MADV_PAGEOUT) not supported\n"); + exit(KSFT_SKIP); } + ksft_test_result_fail("madvise(MADV_PAGEOUT) error: %s\n", strerror(errno)); + exit(KSFT_FAIL); } - } - kill(child, SIGTERM); - ksft_test_result_pass("MAP_DROPPABLE: PASS\n"); - exit(KSFT_PASS); + dropped = memchr(alloc, 'A', alloc_size) == NULL; + + /* + * Speculative reference can temporarily prevent some + * pages from getting dropped. So sleep and retry. + * + * If a page is not droppable for 10s, something + * is seriously messed up and we want to fail. + */ + if (dropped) + break; + sleep(1); + } + + ksft_test_result(dropped, "madvise(MADV_PAGEOUT) behavior\n"); + + ksft_finished(); } From b902890c62d200b3509cb5e09cf1e0a66553c128 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Wed, 10 Jun 2026 16:20:48 -0700 Subject: [PATCH 4276/5214] mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show() Reading the debugfs "count" file of a memcg-aware shrinker can sleep inside an RCU read-side critical section: BUG: sleeping function called from invalid context at kernel/cgroup/rstat.c:421 RCU nest depth: 1, expected: 0 css_rstat_flush mem_cgroup_flush_stats zswap_shrinker_count shrinker_debugfs_count_show shrinker_debugfs_count_show() invokes the ->count_objects() callback under rcu_read_lock(). The zswap callback flushes memcg stats via css_rstat_flush(), which may sleep, so it must not run under RCU. The RCU lock is not needed here. mem_cgroup_iter() takes RCU internally and returns a memcg holding a css reference (dropped on the next iteration or by mem_cgroup_iter_break()), so the memcg stays alive without it. The shrinker is kept alive by the open debugfs file: shrinker_free() removes the debugfs entries via debugfs_remove_recursive(), which waits for in-flight readers to drain, before call_rcu(..., shrinker_free_rcu_cb). The sibling "scan" handler already invokes the sleeping ->scan_objects() callback with no RCU section. Drop the rcu_read_lock()/rcu_read_unlock(). Link: https://lore.kernel.org/20260610232048.62930-1-shakeel.butt@linux.dev Fixes: 5035ebc644ae ("mm: shrinkers: introduce debugfs interface for memory shrinkers") Signed-off-by: Shakeel Butt Reported-by: Zenghui Yu Closes: https://lore.kernel.org/all/c052a064-cddb-494f-a0d8-f8a10b4b1c4d@linux.dev/ Suggested-by: Nhat Pham Reviewed-by: SeongJae Park Reviewed-by: Qi Zheng Tested-by: Zenghui Yu (Huawei) Reviewed-by: Nhat Pham Acked-by: Muchun Song Reviewed-by: Roman Gushchin Cc: Dave Chinner Cc: Signed-off-by: Andrew Morton --- mm/shrinker_debug.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index affa64437302..cda4e86428c8 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -57,8 +57,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) if (!count_per_node) return -ENOMEM; - rcu_read_lock(); - memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE; memcg = mem_cgroup_iter(NULL, NULL, NULL); @@ -88,8 +86,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) } } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); - rcu_read_unlock(); - kfree(count_per_node); return ret; } From 878f41243c0ddc8201856c1d0c530df47cdaec87 Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Tue, 2 Jun 2026 21:07:55 +0800 Subject: [PATCH 4277/5214] mm: page_isolation: avoid unsafe folio reads while scanning compound pages page_is_unmovable() can inspect compound pages without holding a folio reference or any lock. The folio can therefore be freed, split or reused while the scanner is still looking at it. The existing HugeTLB handling already avoids folio_hstate() for this reason, but it still derives the hstate from folio_size() and later derives the scan step from folio_nr_pages() and folio_page_idx(). These helpers rely on the folio still being a valid folio head. If the folio changed concurrently, the scanner can read inconsistent folio metadata and compute a wrong step. In the worst case, folio_nr_pages() can return 1 for what used to be a tail page and the subtraction from folio_page_idx() can underflow. There is a similar issue for non-Hugetlb compound pages: folio_test_lru() expects a valid folio. If the previously observed head page has been reused as a tail page of another compound page, the folio flag checks can trigger VM_BUG_ON_PGFLAGS(). Read the compound order once with compound_order(), reject obviously bogus orders, and derive the hstate and scan step from that order instead of querying folio size information again. Also use PageLRU(page), which is safe for the page being scanned, instead of folio_test_lru() on a potentially stale folio pointer. Treat an unknown HugeTLB hstate as unmovable so the scanner does not try to skip over an unstable HugeTLB folio. Link: https://lore.kernel.org/20260602130755.38794-1-kaitao.cheng@linux.dev Fixes: a0a9f2180b90 ("mm: page_isolation: avoid calling folio_hstate() without hugetlb_lock") Signed-off-by: Kaitao Cheng Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador (SUSE) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liu Shixin Cc: Michal Hocko Cc: Muchun Song Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/page_isolation.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 7a9d631945a3..32ce8a7d9df3 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -41,8 +41,14 @@ bool page_is_unmovable(struct zone *zone, struct page *page, * We need not scan over tail pages because we don't * handle each tail page individually in migration. */ - if (PageHuge(page) || PageCompound(page)) { + if (PageCompound(page)) { struct folio *folio = page_folio(page); + unsigned long nr_pages, pfn; + unsigned int order; + + order = compound_order(&folio->page); + if (order > MAX_FOLIO_ORDER) + return true; if (folio_test_hugetlb(folio)) { struct hstate *h; @@ -54,15 +60,16 @@ bool page_is_unmovable(struct zone *zone, struct page *page, * The huge page may be freed so can not * use folio_hstate() directly. */ - h = size_to_hstate(folio_size(folio)); - if (h && !hugepage_migration_supported(h)) + h = size_to_hstate(PAGE_SIZE << order); + if (!h || !hugepage_migration_supported(h)) return true; - - } else if (!folio_test_lru(folio)) { + } else if (!PageLRU(page)) { return true; } - *step = folio_nr_pages(folio) - folio_page_idx(folio, page); + nr_pages = 1UL << order; + pfn = page_to_pfn(page); + *step = (pfn | (nr_pages - 1)) + 1 - pfn; return false; } From 6a66c557a2ab2609575bafd15e093669c05f9711 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 4 Jun 2026 18:38:48 -0700 Subject: [PATCH 4278/5214] mm/damon/core: always put unsuccessfully committed target pids damon_commit_target() puts and gets the destination and the source target pids. It puts the destination target pid because it will be overwritten by the source target pid. It gets the source pid because the caller is supposed to eventually put the pids. In more detail, the caller will call damon_destroy_ctx() after damon_commit_ctx() to destroy the entire source context. And in this case, [f]vaddr operation set's cleanup_target() callback will put the pids. The commit operation is made at the context level. The operation can fail in multiple places including in the middle and after the targets commit operations. For any such failures, immediately the error is returned to the damon_commit_ctx() caller. If some or all of the source target pids were committed to the destination during the unsuccessful context commit attempt, those pids should be put twice. The source context will do the put operations using the above explained routine. However, let's suppose the destination context was not originally using [f]vaddr operation set and the commit failed before the ops of the source context is committed. The destination does not have the cleanup_target() ops callback, so it cannot put the pids via the damon_destroy_ctx(). As a result, the pids are leaked. The issue in the real world would be not very common. The commit feature is for changing parameters of running DAMON context while inheriting internal status like the monitoring results. The monitoring results of a physical address range ain't have things that are beneficial to be inherited to a virtual address ranges monitoring. So the problem-causing DAMON control would be not very common in the real world. That said, it is a supported feature. And damon_commit_target() failure due to memory allocation is relatively realistic [1] if there are a huge number of target regions. Fix by putting the pids in the commit operation in case of the failures. The issue was discovered [2] by Sashiko. Link: https://lore.kernel.org/20260605013849.83750-1-sj@kernel.org Link: https://lore.kernel.org/20260603112306.58490-1-akinobu.mita@gmail.com [1] Link: https://lore.kernel.org/20260320020056.835-1-sj@kernel.org [2] Fixes: 83dc7bbaecae ("mm/damon/sysfs: use damon_commit_ctx()") Signed-off-by: SeongJae Park Cc: # 6.11.x Signed-off-by: Andrew Morton --- mm/damon/core.c | 55 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 265d51ade25b..7e4b9affc5b0 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1387,10 +1387,36 @@ static int damon_commit_target( return 0; } +/* + * damon_revert_target_commits() - revert unsuccessful target commits. + * @dst: Commit destination context + * @failed: Commit failed destination target + * @src: Commit source context + * + * Revert target states that changed by damon_commit_target(), and cannot be + * cleaned up by the destination context's ops.cleanup_target(). + */ +static void damon_revert_target_commits(struct damon_ctx *dst, + struct damon_target *failed, struct damon_ctx *src) +{ + struct damon_target *target; + + if (!damon_target_has_pid(src)) + return; + if (dst->ops.cleanup_target) + return; + damon_for_each_target(target, dst) { + if (target == failed) + return; + put_pid(target->pid); + } +} + static int damon_commit_targets( struct damon_ctx *dst, struct damon_ctx *src) { struct damon_target *dst_target, *next, *src_target, *new_target; + struct damon_target *failed; int i = 0, j = 0, err; damon_for_each_target_safe(dst_target, next, dst) { @@ -1404,8 +1430,10 @@ static int damon_commit_targets( dst_target, damon_target_has_pid(dst), src_target, damon_target_has_pid(src), src->min_region_sz); - if (err) - return err; + if (err) { + failed = dst_target; + goto out; + } } else { struct damos *s; @@ -1419,25 +1447,34 @@ static int damon_commit_targets( } } + failed = NULL; damon_for_each_target_safe(src_target, next, src) { if (j++ < i) continue; /* target to remove has no matching dst */ - if (src_target->obsolete) - return -EINVAL; + if (src_target->obsolete) { + err = -EINVAL; + goto out; + } new_target = damon_new_target(); - if (!new_target) - return -ENOMEM; + if (!new_target) { + err = -ENOMEM; + goto out; + } err = damon_commit_target(new_target, false, src_target, damon_target_has_pid(src), src->min_region_sz); if (err) { damon_destroy_target(new_target, NULL); - return err; + goto out; } damon_add_target(dst, new_target); } return 0; + +out: + damon_revert_target_commits(dst, failed, src); + return err; } static void damon_commit_filter(struct damon_filter *dst, @@ -1571,8 +1608,10 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) */ if (!damon_attrs_equals(&dst->attrs, &src->attrs)) { err = damon_set_attrs(dst, &src->attrs); - if (err) + if (err) { + damon_revert_target_commits(dst, NULL, src); return err; + } } dst->pause = src->pause; dst->ops = src->ops; From 5419cac89c0224b5acdbcda183a3c4809510ce95 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 18:41:52 +0000 Subject: [PATCH 4279/5214] mm/page_frag: reject invalid CPUs in page_frag_test The page_frag selftest module accepts test_push_cpu and test_pop_cpu as signed module parameters, then validates them by passing them directly to cpu_active(). That validation is itself unsafe for negative or out-of-range CPU numbers. For example, test_push_cpu=-1 is converted to a very large unsigned CPU number before cpu_active() reaches cpumask_test_cpu(), which trips the cpumask range check with CONFIG_DEBUG_PER_CPU_MAPS enabled. Reject CPU values outside [0, nr_cpu_ids) before asking whether the CPU is active. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260605184157.2490353-1-sam.moelius@trailofbits.com Signed-off-by: Samuel Moelius Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/page_frag/page_frag_test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c index e806c1866e36..c8584d0fdeab 100644 --- a/tools/testing/selftests/mm/page_frag/page_frag_test.c +++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c @@ -131,6 +131,8 @@ static int __init page_frag_test_init(void) init_completion(&wait); if (test_alloc_len > PAGE_SIZE || test_alloc_len <= 0 || + test_push_cpu < 0 || test_push_cpu >= nr_cpu_ids || + test_pop_cpu < 0 || test_pop_cpu >= nr_cpu_ids || !cpu_active(test_push_cpu) || !cpu_active(test_pop_cpu)) return -EINVAL; From e8ae6fd67021fafa9205330b3b248c9fc7216e0b Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 00:48:15 +0000 Subject: [PATCH 4280/5214] mm/gup_test: reject wrapped user ranges gup_test accepts an address and size from the debugfs ioctl and repeatedly compares against addr + size. If that addition wraps, the loop can be skipped and the ioctl returns success with size rewritten to zero. Compute the end address once with overflow checking and use that checked end for the loop bounds. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260609004814.1240586.6294d614ac80.gup-test-range-end-wrap@trailofbits.com Signed-off-by: Samuel Moelius Acked-by: David Hildenbrand (Arm) Cc: Jason Gunthorpe Cc: John Hubbard Cc: Peter Xu Signed-off-by: Andrew Morton --- mm/gup_test.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mm/gup_test.c b/mm/gup_test.c index 9dd48db897b9..eb4c9cda16ed 100644 --- a/mm/gup_test.c +++ b/mm/gup_test.c @@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd, unsigned long i, nr_pages, addr, next; long nr; struct page **pages; + unsigned long end; int ret = 0; bool needs_mmap_lock = cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK; - if (gup->size > ULONG_MAX) + if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX) + return -EINVAL; + if (check_add_overflow((unsigned long)gup->addr, + (unsigned long)gup->size, &end)) return -EINVAL; nr_pages = gup->size / PAGE_SIZE; @@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd, i = 0; nr = gup->nr_pages_per_call; start_time = ktime_get(); - for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) { + for (addr = gup->addr; addr < end; addr = next) { if (nr != gup->nr_pages_per_call) break; next = addr + nr * PAGE_SIZE; - if (next > gup->addr + gup->size) { - next = gup->addr + gup->size; + if (next > end) { + next = end; nr = (next - addr) / PAGE_SIZE; } From 224ed0e019b122d08580362abe57574a78f2b5fa Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 11 Jun 2026 09:11:00 +0530 Subject: [PATCH 4281/5214] selftests/mm: allow PUD-level entries in compound testcase of hmm tests Patch series "selftests/mm: assorted fixes for hmm-tests", v3. This series fixes a few issues in hmm-tests that show up when page-size and huge-page configuration differ from the hardcoded assumptions the tests were written for (PMD/THP sizing, default hugepage size, and related cases). It also includes a fix to exclusive_cow: the test ignored the return value of fork(), so both parent and child ran the same teardown path. This patch (of 3): The HMM compound testcase currently assumes only PMD-level mappings and fails on systems where default_hugepagesz=1G is set, because the region is then reported by the device at PUD level. Determine the mapping level (PMD or PUD) the device reports for the first page of the range and require every page to match that level exactly via ASSERT_EQ(). This accepts PUD-level mappings while preserving the expected/observed protection values printed on failure, and rejects a fragmented mapping that mixes PMD- and PUD-level entries within the same range (which a per-page OR check would have let pass). Link: https://lore.kernel.org/20260611034102.1030738-1-aboorvad@linux.ibm.com Link: https://lore.kernel.org/20260611034102.1030738-2-aboorvad@linux.ibm.com Fixes: e478425bec93 ("mm/hmm: add tests for hmm_pfn_to_map_order()") Signed-off-by: Sayali Patil Co-developed-by: Aboorva Devarajan Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 46e0c8c921c3..deb6a7485650 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1602,8 +1602,8 @@ TEST_F(hmm2, snapshot) } /* - * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that - * should be mapped by a large page table entry. + * Test the hmm_range_fault() handling of large pages (PMD or PUD) + * that should be mapped by a large page table entry. */ TEST_F(hmm, compound) { @@ -1613,6 +1613,7 @@ TEST_F(hmm, compound) unsigned long default_hsize = default_huge_page_size(); int *ptr; unsigned char *m; + unsigned char prot; int ret; unsigned long i; @@ -1648,11 +1649,20 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw. The region is backed by a single huge + * page that the device reports either at PMD or at PUD level depending + * on the configured default hugepage size. Determine that level from + * the first page and require every page in the range to match it + * exactly, so that a fragmented mapping mixing levels (or a missing + * large-page bit) is still caught and reported with its actual value. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_WRITE | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); /* Make the region read-only. */ ret = mprotect(buffer->ptr, size, PROT_READ); @@ -1663,11 +1673,17 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw after mprotect(PROT_READ). Same + * approach as above: determine the mapping level from the first + * page and require every page to match it exactly. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_READ | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); munmap(buffer->ptr, buffer->size); buffer->ptr = NULL; From 8edb0e769ce2a996774df83485781dd9f4bc2d44 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 11 Jun 2026 09:11:01 +0530 Subject: [PATCH 4282/5214] selftests/mm: remove hardcoded THP sizing assumptions in hmm tests migrate_partial_unmap_fault() and migrate_remap_fault() use hardcoded offsets based on a 2MB PMD size. Similarly, benchmark_thp_migration() assumes a fixed 2MB THP size when generating test buffer sizes. Derive offsets and test sizes from the runtime PMD page size returned by read_pmd_pagesize(). If unavailable, fall back to TWOMEG. This allows the tests to adapt correctly on systems where PMD-sized THP differs from 2MB. Also replace the fixed 1MB unmap size with a PMD-relative value derived from the runtime PMD size. On systems with larger PMD sizes, computed test buffer sizes can exceed INT_MAX. Skip such test cases to avoid overflow. Link: https://lore.kernel.org/20260611034102.1030738-3-aboorvad@linux.ibm.com Fixes: 24c2c5b8ffbd ("selftests/mm/hmm-tests: partial unmap, mremap and anon_write tests") Fixes: 271a7b2e3c13 ("selftests/mm/hmm-tests: new throughput tests including THP") Signed-off-by: Sayali Patil Signed-off-by: Aboorva Devarajan Acked-by: Balbir Singh Cc: Alex Sierra Cc: Alistair Popple Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 62 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index deb6a7485650..8f4f82467043 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2380,12 +2381,21 @@ TEST_F(hmm, migrate_partial_unmap_fault) struct hmm_buffer *buffer; unsigned long npages; unsigned long size = read_pmd_pagesize(); + unsigned long unmap_size; + unsigned long offsets[3]; unsigned long i; void *old_ptr; void *map; int *ptr; int ret, j, use_thp; - int offsets[] = { 0, 512 * ONEKB, ONEMEG }; + + if (!size) + size = TWOMEG; + + unmap_size = size / 2; + offsets[0] = 0; + offsets[1] = size / 4; + offsets[2] = size / 2; for (use_thp = 0; use_thp < 2; ++use_thp) { for (j = 0; j < ARRAY_SIZE(offsets); ++j) { @@ -2427,12 +2437,12 @@ TEST_F(hmm, migrate_partial_unmap_fault) for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i], i); - munmap(buffer->ptr + offsets[j], ONEMEG); + munmap(buffer->ptr + offsets[j], unmap_size); /* Fault pages back to system memory and check them. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) if (i * sizeof(int) < offsets[j] || - i * sizeof(int) >= offsets[j] + ONEMEG) + i * sizeof(int) >= offsets[j] + unmap_size) ASSERT_EQ(ptr[i], i); buffer->ptr = old_ptr; @@ -2446,12 +2456,19 @@ TEST_F(hmm, migrate_remap_fault) struct hmm_buffer *buffer; unsigned long npages; unsigned long size = read_pmd_pagesize(); + unsigned long offsets[3]; unsigned long i; void *old_ptr, *new_ptr = NULL; void *map; int *ptr; int ret, j, use_thp, dont_unmap, before; - int offsets[] = { 0, 512 * ONEKB, ONEMEG }; + + if (!size) + size = TWOMEG; + + offsets[0] = 0; + offsets[1] = size / 4; + offsets[2] = size / 2; for (before = 0; before < 2; ++before) { for (dont_unmap = 0; dont_unmap < 2; ++dont_unmap) { @@ -2854,38 +2871,45 @@ static inline int run_migration_benchmark(int fd, int use_thp, size_t buffer_siz TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120) { struct benchmark_results thp_results, regular_results; - size_t thp_size = 2 * 1024 * 1024; /* 2MB - typical THP size */ + size_t thp_size = read_pmd_pagesize(); int iterations = 5; + if (!thp_size) + thp_size = TWOMEG; + printf("\nHMM THP Migration Benchmark\n"); printf("---------------------------\n"); printf("System page size: %ld bytes\n", sysconf(_SC_PAGESIZE)); /* Test different buffer sizes */ size_t test_sizes[] = { - thp_size / 4, /* 512KB - smaller than THP */ - thp_size / 2, /* 1MB - half THP */ - thp_size, /* 2MB - single THP */ - thp_size * 2, /* 4MB - two THPs */ - thp_size * 4, /* 8MB - four THPs */ - thp_size * 8, /* 16MB - eight THPs */ - thp_size * 128, /* 256MB - one twenty eight THPs */ + thp_size / 4, /* quarter THP */ + thp_size / 2, /* half THP */ + thp_size, /* single THP */ + thp_size * 2, /* two THPs */ + thp_size * 4, /* four THPs */ + thp_size * 8, /* eight THPs */ + thp_size * 128, /* one twenty eight THPs */ }; static const char *const test_names[] = { - "Small Buffer (512KB)", - "Half THP Size (1MB)", - "Single THP Size (2MB)", - "Two THP Size (4MB)", - "Four THP Size (8MB)", - "Eight THP Size (16MB)", - "One twenty eight THP Size (256MB)" + "Small Buffer", + "Half THP Size", + "Single THP Size", + "Two THP Size", + "Four THP Size", + "Eight THP Size", + "One twenty eight THP Size" }; int num_tests = ARRAY_SIZE(test_sizes); /* Run all tests */ for (int i = 0; i < num_tests; i++) { + /* Skip test sizes exceeding INT_MAX to avoid overflow */ + if (test_sizes[i] > INT_MAX) + break; + /* Test with THP */ ASSERT_EQ(run_migration_benchmark(self->fd, 1, test_sizes[i], iterations, &thp_results), 0); From cea5702144615878600d3a39b5d8b3cc34719012 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 11 Jun 2026 09:11:02 +0530 Subject: [PATCH 4283/5214] selftests/mm: fix exclusive_cow test fork() handling The test ignores the return value of fork(), so both the parent and the (newly created) child run the COW verification loops and then call hmm_buffer_free() before returning into the kselftest harness, which _exit()s each side. This duplicated teardown sequence has been observed to manifest as a SIGSEGV in the test child, e.g.: hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1 in hmm-tests[6964,10000000+30000] Fix this by adopting the same fork()-then-wait pattern already used by the nearby anon_write_child / anon_write_child_shared tests in this file: the child performs the COW verification and then _exit(0)s so it does not run the test teardown, while the parent independently verifies COW, waits for the child, and only then frees the buffer. Link: https://lore.kernel.org/20260611034102.1030738-4-aboorvad@linux.ibm.com Fixes: b659baea75469 ("mm: selftests for exclusive device memory") Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Sayali Patil Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 8f4f82467043..e4c49699f3f7 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1884,6 +1884,8 @@ TEST_F(hmm, exclusive_cow) unsigned long i; int *ptr; int ret; + pid_t pid; + int status; npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; ASSERT_NE(npages, 0); @@ -1912,14 +1914,37 @@ TEST_F(hmm, exclusive_cow) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - fork(); + pid = fork(); + if (pid == -1) + ASSERT_EQ(pid, 0); - /* Fault pages back to system memory and check them. */ + if (pid == 0) { + /* + * Child verifies COW independently, then _exit(0)s so it does + * not run the test teardown. A failed ASSERT_* here makes the + * harness abort() the child, so the parent sees + * !WIFEXITED(status) below and fails in turn. + */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i]++, i); + + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i], i + 1); + + _exit(0); + } + + /* Parent: also increment to verify COW works for both processes. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i]++, i); for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) - ASSERT_EQ(ptr[i], i+1); + ASSERT_EQ(ptr[i], i + 1); + + /* Parent: wait for child and then free the buffer. */ + ASSERT_EQ(waitpid(pid, &status, 0), pid); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); hmm_buffer_free(buffer); } From c565c009d0c00aa1a2e813aef11cfc685f148d1a Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 12 Jun 2026 15:30:32 +0800 Subject: [PATCH 4284/5214] mm: use mapping_mapped to simplify the code Use mapping_mapped() to simplify the code, make the code tidy and clean. Link: https://lore.kernel.org/20260612073032.33228-1-huangsj@hygon.cn Signed-off-by: Huang Shijie Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes Reviewed-by: Muchun Song Reviewed-by: Oscar Salvador (SUSE) Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- fs/hugetlbfs/inode.c | 4 ++-- mm/memory.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 78d61bf2bd9b..216e1a0dd0b2 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -614,7 +614,7 @@ static void hugetlb_vmtruncate(struct inode *inode, loff_t offset) i_size_write(inode, offset); i_mmap_lock_write(mapping); - if (!RB_EMPTY_ROOT(&mapping->i_mmap.rb_root)) + if (mapping_mapped(mapping)) hugetlb_vmdelete_list(&mapping->i_mmap, pgoff, 0, ZAP_FLAG_DROP_MARKER); i_mmap_unlock_write(mapping); @@ -675,7 +675,7 @@ static long hugetlbfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) /* Unmap users of full pages in the hole. */ if (hole_end > hole_start) { - if (!RB_EMPTY_ROOT(&mapping->i_mmap.rb_root)) + if (mapping_mapped(mapping)) hugetlb_vmdelete_list(&mapping->i_mmap, hole_start >> PAGE_SHIFT, hole_end >> PAGE_SHIFT, 0); diff --git a/mm/memory.c b/mm/memory.c index 56be920c56d7..ff338c2abe92 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4386,7 +4386,7 @@ void unmap_mapping_folio(struct folio *folio) details.zap_flags = ZAP_FLAG_DROP_MARKER; i_mmap_lock_read(mapping); - if (unlikely(!RB_EMPTY_ROOT(&mapping->i_mmap.rb_root))) + if (unlikely(mapping_mapped(mapping))) unmap_mapping_range_tree(&mapping->i_mmap, first_index, last_index, &details); i_mmap_unlock_read(mapping); @@ -4416,7 +4416,7 @@ void unmap_mapping_pages(struct address_space *mapping, pgoff_t start, last_index = ULONG_MAX; i_mmap_lock_read(mapping); - if (unlikely(!RB_EMPTY_ROOT(&mapping->i_mmap.rb_root))) + if (unlikely(mapping_mapped(mapping))) unmap_mapping_range_tree(&mapping->i_mmap, first_index, last_index, &details); i_mmap_unlock_read(mapping); From 44238b122ae834ac52748e59809a139a2cb8409b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 16 Jun 2026 10:59:06 +0100 Subject: [PATCH 4285/5214] mm/vmscan: pass NULL to trace vmscan node reclaim The tracepoint for node relcaims takes a `struct mem_cgroup *` as the third argument, so pass NULL instead of 0 to fix warning about using an integer as a pointer. Fixes the following warnings: mm/vmscan.c:6753:66: warning: Using plain integer as NULL pointer mm/vmscan.c:6757:58: warning: Using plain integer as NULL pointer mm/vmscan.c:7818:60: warning: Using plain integer as NULL pointer Link: https://lore.kernel.org/20260616095906.210016-1-ben.dooks@codethink.co.uk Signed-off-by: Ben Dooks Cc: Johannes Weiner Signed-off-by: Andrew Morton --- mm/vmscan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 299b5d9e8836..8190c4abec84 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -6706,11 +6706,11 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, return 1; set_task_reclaim_state(current, &sc.reclaim_state); - trace_mm_vmscan_direct_reclaim_begin(sc.gfp_mask, order, 0); + trace_mm_vmscan_direct_reclaim_begin(sc.gfp_mask, order, NULL); nr_reclaimed = do_try_to_free_pages(zonelist, &sc); - trace_mm_vmscan_direct_reclaim_end(nr_reclaimed, 0); + trace_mm_vmscan_direct_reclaim_end(nr_reclaimed, NULL); set_task_reclaim_state(current, NULL); return nr_reclaimed; @@ -7776,7 +7776,7 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, delayacct_freepages_end(); psi_memstall_leave(&pflags); - trace_mm_vmscan_node_reclaim_end(sc->nr_reclaimed, 0); + trace_mm_vmscan_node_reclaim_end(sc->nr_reclaimed, NULL); return sc->nr_reclaimed; } From 13a1e1a618858407fa12c391f664ea750651f6b2 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Fri, 19 Jun 2026 12:28:51 +0100 Subject: [PATCH 4286/5214] Revert "mm: limit filemap_fault readahead to VMA boundaries" This reverts commit 7b32f64bc512b40b268776c5ac4d354b325b3197. This patch caused a significant performance regression, so revert it, and we can determine whether the approach is sensible or not moving forwards, and if so how to avoid this. There was a merge conflict with commit de97ae6222c1 ("mm/readahead: no PG_readahead on EOF"), care was taken to ensure that the revert retained the behaviour of this patch and cleanly reverts commit 7b32f64bc512 ("mm: limit filemap_fault readahead to VMA boundaries") only. Link: https://lore.kernel.org/20260619112852.104213-1-ljs@kernel.org Fixes: 7b32f64bc512 ("mm: limit filemap_fault readahead to VMA boundaries") Signed-off-by: Lorenzo Stoakes Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202606181547.617a6967-lkp@intel.com Acked-by: David Hildenbrand (Arm) Reviewed-by: Pedro Falcato Reviewed-by: Matthew Wilcox (Oracle) Cc: Jan Kara Cc: Kalesh Singh Signed-off-by: Andrew Morton --- include/linux/pagemap.h | 2 -- mm/filemap.c | 4 ---- mm/readahead.c | 6 +----- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 627771e82eb1..2c3718d592d6 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -1348,7 +1348,6 @@ struct readahead_control { struct file_ra_state *ra; /* private: use the readahead_* accessors instead */ pgoff_t _index; - pgoff_t _max_index; /* limit readahead to _max_index, inclusive */ unsigned int _nr_pages; unsigned int _batch_count; bool dropbehind; @@ -1362,7 +1361,6 @@ struct readahead_control { .mapping = m, \ .ra = r, \ ._index = i, \ - ._max_index = ULONG_MAX, \ } #define VM_READAHEAD_PAGES (SZ_128K / PAGE_SIZE) diff --git a/mm/filemap.c b/mm/filemap.c index dc3a0e960b9f..17a64837597c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3312,8 +3312,6 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) unsigned int thp_order = 0; unsigned short mmap_miss; - ractl._max_index = vmf->vma->vm_pgoff + vma_pages(vmf->vma) - 1; - /* Use the readahead code, even if readahead is disabled */ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { /* @@ -3409,7 +3407,6 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) * mmap read-around */ ra->start = max_t(long, 0, vmf->pgoff - ra->ra_pages / 2); - ra->start = max(ra->start, vmf->vma->vm_pgoff); ra->size = ra->ra_pages; ra->async_size = ra->ra_pages / 4; ra->order = 0; @@ -3457,7 +3454,6 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf, } if (folio_test_readahead(folio)) { - ractl._max_index = vmf->vma->vm_pgoff + vma_pages(vmf->vma) - 1; fpin = maybe_unlock_mmap_for_io(vmf, fpin); page_cache_async_ra(&ractl, folio, ra->ra_pages); } diff --git a/mm/readahead.c b/mm/readahead.c index 38ce16e3fcbd..558c92957518 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -335,8 +335,6 @@ static void do_page_cache_ra(struct readahead_control *ractl, return; end_index = (isize - 1) >> PAGE_SHIFT; - if (end_index > ractl->_max_index) - end_index = ractl->_max_index; if (index > end_index) return; /* Don't read past the page containing the last byte of the file */ @@ -487,7 +485,7 @@ void page_cache_ra_order(struct readahead_control *ractl, pgoff_t start = readahead_index(ractl); pgoff_t index = start; unsigned int min_order = mapping_min_folio_order(mapping); - pgoff_t limit; + pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT; pgoff_t mark; unsigned int nofs; int err = 0; @@ -500,8 +498,6 @@ void page_cache_ra_order(struct readahead_control *ractl, goto fallback; } - limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT; - limit = min(limit, ractl->_max_index); if (limit > index + ra->size - 1) { limit = index + ra->size - 1; mark = index + ra->size - ra->async_size; From 673db10729fb121ea1b16fe57791a0cb9eac1eb5 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:17 +0000 Subject: [PATCH 4287/5214] cpu: hotplug: Preserve per instance callback errors cpuhp_invoke_callback() unwinds earlier callbacks for the same hotplug state when one instance fails. The rollback path currently reuses ret, so a successful rollback can hide the original error and make the failed transition look successful. Keep the rollback result separate from the original error. Fixes: 724a86881d03 ("smp/hotplug: Callback vs state-machine consistency") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-1-include@grrlz.net --- kernel/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 8df2d773fe3b..3ed24c711e3f 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -175,7 +175,7 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, struct cpuhp_step *step = cpuhp_get_step(state); int (*cbm)(unsigned int cpu, struct hlist_node *node); int (*cb)(unsigned int cpu); - int ret, cnt; + int ret, cnt, rollback_ret; if (st->fail == state) { st->fail = CPUHP_INVALID; @@ -239,12 +239,12 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, break; trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); - ret = cbm(cpu, node); - trace_cpuhp_exit(cpu, st->state, state, ret); + rollback_ret = cbm(cpu, node); + trace_cpuhp_exit(cpu, st->state, state, rollback_ret); /* * Rollback must not fail, */ - WARN_ON_ONCE(ret); + WARN_ON_ONCE(rollback_ret); } return ret; } From 86f436567f2516a0083b210bedc933544826a2c3 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:18 +0000 Subject: [PATCH 4288/5214] cpu: hotplug: Bound hotplug states sysfs output states_show() adds CPU hotplug state names into a single sysfs buffer using sprintf(). With enough registered states, this can write past the end of the PAGE_SIZE buffer. Use sysfs_emit_at() so output is bounded. Fixes: 98f8cdce1db5 ("cpu/hotplug: Add sysfs state interface") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-2-include@grrlz.net --- kernel/cpu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 3ed24c711e3f..77fdb2013ab8 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2865,21 +2865,17 @@ static const struct attribute_group cpuhp_cpu_attr_group = { .name = "hotplug", }; -static ssize_t states_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t states_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t cur, res = 0; + ssize_t res = 0; int i; mutex_lock(&cpuhp_state_mutex); for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) { struct cpuhp_step *sp = cpuhp_get_step(i); - if (sp->name) { - cur = sprintf(buf, "%3d: %s\n", i, sp->name); - buf += cur; - res += cur; - } + if (sp->name) + res += sysfs_emit_at(buf, res, "%3d: %s\n", i, sp->name); } mutex_unlock(&cpuhp_state_mutex); return res; From ef0c9f75a19532d7675384708fc8621e10850104 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 21 Jun 2026 14:09:49 -0700 Subject: [PATCH 4289/5214] lib: Add stale 'raid6' directory to .gitignore file I keep having to do this, because people think they can just move directories around and move the gitignore files around with them. You really can't do that - the old generated files stay around for others, and still need to be ignored in the old location. So when moving gitignore entries around because you moved the files (or when moving a whole gitignore file around because the directory it was in moved), the old gitignore situation needs to be dealt with. Yes, those files may have moved in *your* tree when you moved the directory. And yes, new repositories will never even have seen them. But all those other developers that see the result of your move still likely have a working tree with the old state, and the files that were hidden from git by an old gitignore file do not suddenly become relevant. Fixes: 3626738bc714 ("raid6: move to lib/raid/") Signed-off-by: Linus Torvalds --- lib/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/.gitignore b/lib/.gitignore index 101a4aa92fb5..42aee3ddb374 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -5,3 +5,4 @@ /gen_crc32table /gen_crc64table /oid_registry_data.c +/raid6 From 62b01f72d93c7bc8fde3b2e5b5f783eca5f53324 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 03:32:28 +0800 Subject: [PATCH 4290/5214] net: marvell: prestera: initialize err in prestera_port_sfp_bind prestera_port_sfp_bind() returns err after walking the ports node. If no child node matches the port's front-panel id, err is never assigned. Initialize err to 0 because absence of a matching optional port device tree node is not an error. In that case no phylink is created and port creation should continue with port->phy_link left NULL. Errors from malformed matched nodes and phylink_create() still propagate. Fixes: 52323ef75414 ("net: marvell: prestera: add phylink support") Signed-off-by: Ruoyu Wang Reviewed-by: Maxime Chevallier Acked-by: Elad Nachman Link: https://patch.msgid.link/20260617193228.1653582-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/prestera/prestera_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 41e19e9ad28d..a82e7a802985 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -373,7 +373,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port) struct device_node *ports, *node; struct fwnode_handle *fwnode; struct phylink *phy_link; - int err; + int err = 0; if (!sw->np) return 0; From 1579342d71133da7f00daa02c75cebec7372097b Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 21:57:45 +0800 Subject: [PATCH 4291/5214] tipc: fix use-after-free of the discoverer in tipc_disc_rcv() bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but tipc_disc_rcv() still dereferences b->disc in RX softirq under rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv). L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(), but the UDP bearer defers that call to the cleanup_bearer() workqueue, so the discoverer is freed with no grace period: BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001) The bearer is freed with kfree_rcu(); free the discoverer the same way. Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU callback. Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away. Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP. Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260617135744.3383175-3-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/net/tipc/core.c b/net/tipc/core.c index 434e70eabe08..1ddecea1df6e 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -218,6 +218,11 @@ static void __exit tipc_exit(void) unregister_pernet_device(&tipc_net_ops); tipc_unregister_sysctl(); + /* TODO: Wait for all timers that called call_rcu() to finish before + * calling rcu_barrier(). + */ + rcu_barrier(); + pr_info("Deactivated\n"); } diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 3e54d2df5683..b9d06595b067 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -58,6 +58,7 @@ * @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) + * @rcu: RCU head for deferred freeing */ struct tipc_discoverer { u32 bearer_id; @@ -69,6 +70,7 @@ struct tipc_discoverer { struct sk_buff *skb; struct timer_list timer; unsigned long timer_intv; + struct rcu_head rcu; }; /** @@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, return 0; } +static void tipc_disc_free_rcu(struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + + kfree_skb(d->skb); + kfree(d); +} + /** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure @@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, void tipc_disc_delete(struct tipc_discoverer *d) { timer_shutdown_sync(&d->timer); - kfree_skb(d->skb); - kfree(d); + call_rcu(&d->rcu, tipc_disc_free_rcu); } /** From 4c6d43db2a4d2cef3921e885cf34798f790d34ea Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Tue, 16 Jun 2026 12:03:29 +0200 Subject: [PATCH 4292/5214] net: dst_metadata: fix false-positive memcpy overflow in tun_dst_unclone kmalloc_flex() in metadata_dst_alloc() sets __counted_by for the structure to the options_len, which is then initialized to zero. Later, we're initializing the structure by copying the tunnel info together with the options, and this triggers a warning for a potential memcpy overflow, since the compiler estimates that the options can't fit into the structure, even though the memory for them is actually allocated. memcpy: detected buffer overflow: 104 byte write of buffer size 96 WARNING: CPU: X PID: Y at lib/string_helpers.c:1036 __fortify_report skb_tunnel_info_unclone+0x179/0x190 geneve_xmit+0x7fe/0xe00 The issue is triggered when built with clang and source fortification. Fix that by doing the copy in two stages: first - the main data with the options_len, then the options. This way the correct length should be known at the time of the copy. It would be better if the options_len never changed after allocation, but the allocation code is a little separate from the initialization and it would be awkward and potentially dangerous to return a struct with options_len set to a non-zero value from the metadata_dst_alloc(). Another option would be to use ip_tunnel_info_opts_set(), but it is doing too many unnecessary operations for the use case here. Fixes: 69050f8d6d07 ("treewide: Replace kmalloc with kmalloc_obj for non-scalar types") Reported-by: Johan Thomsen Closes: https://lore.kernel.org/netdev/CAKv6aAM8_EWgXScnKmKYm_4SwGDVBK++dzfP+Y6msUXbp99QUw@mail.gmail.com/ Signed-off-by: Ilya Maximets Link: https://patch.msgid.link/20260616100332.1308294-1-i.maximets@ovn.org Signed-off-by: Jakub Kicinski --- include/net/dst_metadata.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index 1fc2fb03ce3f..f45d1e3163f0 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -164,8 +164,11 @@ static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb) if (!new_md) return ERR_PTR(-ENOMEM); - memcpy(&new_md->u.tun_info, &md_dst->u.tun_info, - sizeof(struct ip_tunnel_info) + md_size); + /* Copy in two stages to keep the __counted_by happy. */ + new_md->u.tun_info = md_dst->u.tun_info; + memcpy(ip_tunnel_info_opts(&new_md->u.tun_info), + ip_tunnel_info_opts(&md_dst->u.tun_info), md_size); + #ifdef CONFIG_DST_CACHE /* Unclone the dst cache if there is one */ if (new_md->u.tun_info.dst_cache.cache) { From 41782770be567abc6509169d0ffdada31c783a66 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 17 Jun 2026 13:48:13 +0800 Subject: [PATCH 4293/5214] net: ethernet: mtk_ppe: Fix rhashtable leak in mtk_ppe_init error paths In mtk_ppe_init(), when accounting is enabled, the error paths for dmam_alloc_coherent(mib) and devm_kzalloc(acct) failures return NULL directly, bypassing the err_free_l2_flows label that destroys the rhashtable initialized earlier. While this leak only occurs during probe (not runtime) and the leaked memory is minimal (an empty rhash table), fixing it ensures proper error path cleanup consistency. Fix by changing the two return NULL statements to goto err_free_l2_flows. Fixes: 603ea5e7ffa7 ("net: ethernet: mtk_eth_soc: fix memory leak in error path") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178167550101.2217645.14579307712717502425@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_ppe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index 18279e2a7022..8451dc3fd00a 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -918,7 +918,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib), &ppe->mib_phys, GFP_KERNEL); if (!mib) - return NULL; + goto err_free_l2_flows; ppe->mib_table = mib; @@ -926,7 +926,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) GFP_KERNEL); if (!acct) - return NULL; + goto err_free_l2_flows; ppe->acct_table = acct; } From 48b67c0e8af65acd59d81ddaedd3442b5e4c27b7 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Wed, 17 Jun 2026 15:51:49 +0530 Subject: [PATCH 4294/5214] octeontx2-af: npc: cn20k: fix NPC defrag npc_defrag_alloc_free_slots() always passed NPC_MCAM_KEY_X2 into __npc_subbank_alloc(), which must match sb->key_type, so defrag never allocated replacement slots on X4 banks. Pass the subbank key type for bank 0, and only extend the search into bank 1 for X2 (X4 MCAM indices are confined to b0b..b0t). Fixes: 645c6e3c1999 ("octeontx2-af: npc: cn20k: virtual index support") Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617102149.1309913-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 354c4e881c6a..9052b88f3685 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -3569,15 +3569,18 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, alloc_cnt2 = 0; rc = __npc_subbank_alloc(rvu, sb, - NPC_MCAM_KEY_X2, sb->b0b, + f->key_type, sb->b0b, sb->b0t, NPC_MCAM_LOWER_PRIO, false, cnt, save, cnt, true, &alloc_cnt1); - if (alloc_cnt1 < cnt) { + /* X4 entries only occupy bank 0 (b0b..b0t); see npc_subbank_idx_2_mcam_idx(). + * X2 uses both halves of the subbank, so spill into bank 1 if needed. + */ + if (alloc_cnt1 < cnt && f->key_type == NPC_MCAM_KEY_X2) { rc = __npc_subbank_alloc(rvu, sb, - NPC_MCAM_KEY_X2, sb->b1b, + f->key_type, sb->b1b, sb->b1t, NPC_MCAM_LOWER_PRIO, false, cnt - alloc_cnt1, From d4b7440f7316e76f013f57d8b6da069a1b9c34e7 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:18 +0530 Subject: [PATCH 4295/5214] octeontx2-af: mcs: Fix unsupported secy stats read Secy control stats counter doesn't exist for CNF10KB platform. Skip reading this respective register for CNF10KB silicon while fetching secy stats. Fixes: 9312150af8da ("octeontx2-af: cn10k: mcs: Support for stats collection") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 6 +++--- drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index c1775bd01c2b..a07e0b3d8d00 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -120,13 +120,13 @@ void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(id); stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg); - reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); - stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); - if (mcs->hw->mcs_blks > 1) { reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id); stats->pkt_notag_cnt = mcs_reg_read(mcs, reg); + return; } + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); + stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); } void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index fa461489acdd..ca2704b188a5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -482,10 +482,11 @@ static int rvu_dbg_mcs_rx_secy_stats_display(struct seq_file *filp, void *unused seq_printf(filp, "secy%d: Tagged ctrl pkts: %lld\n", secy_id, stats.pkt_tagged_ctl_cnt); seq_printf(filp, "secy%d: Untaged pkts: %lld\n", secy_id, stats.pkt_untaged_cnt); - seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); if (mcs->hw->mcs_blks > 1) seq_printf(filp, "secy%d: pkts notag: %lld\n", secy_id, stats.pkt_notag_cnt); + else + seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); } mutex_unlock(&mcs->stats_lock); return 0; From fd4460721fb4062ef470ecdfdaedadfe7e415c09 Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Wed, 17 Jun 2026 00:30:19 +0530 Subject: [PATCH 4296/5214] octeontx2-pf: Clear stats of all resources when freeing resources When all MCS resources mapped to a PF are being freed then clear stats of all those resources too. Fixes: 815debbbf7b5 ("octeontx2-pf: mcs: Clear stats before freeing resource") Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-2-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 2cc1bdfd9b2e..702cf07490e7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -182,6 +182,7 @@ static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, clear_req->id = hw_rsrc_id; clear_req->type = type; clear_req->dir = dir; + clear_req->all = all; req = otx2_mbox_alloc_msg_mcs_free_resources(mbox); if (!req) From 450d0e90b10393bd9f50c127875a9fdd4cc81c30 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:20 +0530 Subject: [PATCH 4297/5214] octeontx2-pf: mcs: Fix mcs resources free on PF shutdown On PF shutdown, the current driver free mcs hardware resources though mcs resources are not allocated to it. This patch checks the mcs resources status and if resources are allocated then only sends mailbox message to free them. Fixes: c54ffc73601c ("octeontx2-pf: mcs: Introduce MACSEC hardware offloading") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-3-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 702cf07490e7..9524d38f1582 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -1777,11 +1777,16 @@ int cn10k_mcs_init(struct otx2_nic *pfvf) void cn10k_mcs_free(struct otx2_nic *pfvf) { + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) return; - cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); - cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + if (!list_empty(&cfg->txsc_list)) { + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + } + kfree(pfvf->macsec_cfg); pfvf->macsec_cfg = NULL; } From f623d38fe6c4e8c40b23f42cc6fe6963fa49997b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 17 Jun 2026 19:34:05 -0700 Subject: [PATCH 4298/5214] net: emac: Fix NULL pointer dereference in emac_probe Move devm_request_irq() after devm_platform_ioremap_resource() so that dev->emacp is mapped before the interrupt handler can fire. An early interrupt hitting emac_irq() would dereference the NULL dev->emacp and crash. Also remove redundant error message. devm_platform_ioremap_resource() already returns an error message with dev_err_probe(). Fixes: dcc34ef7c834 ("net: ibm: emac: manage emac_irq with devm") Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260618023405.415644-1-rosenp@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/emac/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index c0e1d85fce3f..1d46cf6c2c12 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3044,6 +3044,12 @@ static int emac_probe(struct platform_device *ofdev) if (err) goto err_gone; + dev->emacp = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(dev->emacp)) { + err = PTR_ERR(dev->emacp); + goto err_gone; + } + /* Setup error IRQ handler */ dev->emac_irq = platform_get_irq(ofdev, 0); if (dev->emac_irq < 0) { @@ -3061,13 +3067,6 @@ static int emac_probe(struct platform_device *ofdev) ndev->irq = dev->emac_irq; - dev->emacp = devm_platform_ioremap_resource(ofdev, 0); - if (IS_ERR(dev->emacp)) { - dev_err(&ofdev->dev, "can't map device registers"); - err = PTR_ERR(dev->emacp); - goto err_gone; - } - /* Wait for dependent devices */ err = emac_wait_deps(dev); if (err) From 16e088016f38cf728a0de709c3335cc5a3850476 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 17:57:08 -0400 Subject: [PATCH 4299/5214] net/sched: act_ct: fix nf_connlabels leak on two error paths tcf_ct_fill_params() calls nf_connlabels_get() (setting put_labels) when TCA_CT_LABELS is present, but two later error sites use a bare return instead of "goto err", skipping the err: nf_connlabels_put() cleanup. They also precede the "p->put_labels = put_labels" assignment, so the tcf_ct_params_free() fallback does not release the count either. Each failed RTM_NEWACTION on these paths leaks one nf_connlabels reference: net->ct.labels_used is incremented and never released. The action is reachable with CAP_NET_ADMIN over the netns, i.e. from an unprivileged user namespace on default-userns kernels. Impact: an unprivileged user with CAP_NET_ADMIN over a network namespace (e.g. via user namespaces) leaks one nf_connlabels reference per failed RTM_NEWACTION on the two error paths; net->ct.labels_used is never released. The err: label is safe to reach from both sites: p->tmpl is still NULL there (kzalloc'd, not yet assigned) and nf_ct_put(NULL) is a no-op, so no inline release is needed. Fixes: 70f06c115bcc ("sched: act_ct: switch to per-action label counting") Signed-off-by: Michael Bommarito Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260617215708.1115818-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/act_ct.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index d47a82f9ac6c..be535a261fa0 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1296,7 +1296,8 @@ static int tcf_ct_fill_params(struct net *net, if (tb[TCA_CT_ZONE]) { if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { NL_SET_ERR_MSG_MOD(extack, "Conntrack zones isn't enabled."); - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto err; } tcf_ct_set_key_val(tb, @@ -1309,7 +1310,8 @@ static int tcf_ct_fill_params(struct net *net, tmpl = nf_ct_tmpl_alloc(net, &zone, GFP_KERNEL); if (!tmpl) { NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); - return -ENOMEM; + err = -ENOMEM; + goto err; } p->tmpl = tmpl; if (tb[TCA_CT_HELPER_NAME]) { From 86e51aa24686cc95bb35613059e8b94b9b81e3f0 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Sat, 20 Jun 2026 16:17:44 +0800 Subject: [PATCH 4300/5214] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In airoha_dev_select_queue(), the expression: queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; implicitly converts to unsigned arithmetic: when skb->priority is 0 (the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX, and UINT_MAX % 8 = 7, routing default best-effort packets to the highest-priority QoS queue. This causes QoS inversion where the majority of traffic on a PON gateway starves actual high-priority flows (VoIP, gaming, etc.). The "- 1" offset was a leftover from the ETS offload implementation that has since been removed. The correct mapping is a direct modulo: queue = skb->priority % AIROHA_NUM_QOS_QUEUES; This maps priority 0 → queue 0 (lowest), priority 7 → queue 7 (highest), with higher priorities wrapping around. This is the standard Linux sk_prio → HW queue mapping used by other drivers. Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback") Link: https://lore.kernel.org/netdev/178185573207.2378135.3729126358670287878@gmail.com/ Acked-by: Lorenzo Bianconi Reviewed-by: Joe Damato Signed-off-by: Wayen Yan Link: https://patch.msgid.link/178194366700.2485734.5368768965976693502@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 64dde6464f3f..3370c3df7c10 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2110,7 +2110,7 @@ static u16 airoha_dev_select_queue(struct net_device *netdev, */ channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id; channel = channel % AIROHA_NUM_QOS_CHANNELS; - queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ + queue = skb->priority % AIROHA_NUM_QOS_QUEUES; queue = channel * AIROHA_NUM_QOS_QUEUES + queue; return queue < netdev->num_tx_queues ? queue : 0; From 27ccb68e7cccead5d8c611665a45d23032d468b3 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:08:17 +0800 Subject: [PATCH 4301/5214] net: sit: require CAP_NET_ADMIN in the device netns for changelink ipip6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. sit was the one tunnel type not covered by the recent series that added this check to the other changelink() handlers. Fixes: 5e6700b3bf98 ("sit: add support of x-netns") Link: https://lore.kernel.org/netdev/20260612085941.3158249-1-maoyixie.tju@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Nicolas Dichtel Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260618070817.3378283-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/sit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 64f0d1b622d3..a38b24fb8384 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1613,6 +1613,9 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == sitn->fb_tunnel_dev) return -EINVAL; From d186e942365acece7c56d39da05dd63bf95b280a Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 14:55:13 +0800 Subject: [PATCH 4302/5214] ipv6: ndisc: fix NULL deref in accept_untracked_na() accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev) and dereferences idev->cnf.accept_untracked_na without a NULL check, even though its only caller ndisc_recv_na() already fetched and NULL-checked idev for the same device. Both reads of dev->ip6_ptr run in the same RCU read-side critical section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown() without the synchronize_net() that orders the unregister path, so the re-fetch returns NULL and oopses: BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974) Read of size 4 at addr 0000000000000364 Call Trace: ndisc_recv_na (net/ipv6/ndisc.c:974) icmpv6_rcv (net/ipv6/icmp.c:1193) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479) ip6_input_finish (net/ipv6/ip6_input.c:534) ip6_input (net/ipv6/ip6_input.c:545) ip6_mc_input (net/ipv6/ip6_input.c:635) ipv6_rcv (net/ipv6/ip6_input.c:351) It is reachable by an unprivileged user via a network namespace. Pass the caller's already validated idev instead of re-fetching it; the idev stays alive for the whole RCU critical section, so it is safe even after dev->ip6_ptr has been cleared. Fixes: aaa5f515b16b ("net: ipv6: new accept_untracked_na option to accept na only if in-network") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260617065512.2529757-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index e7ad13c5bd26..f867ec8d3d90 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -967,10 +967,8 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb) return reason; } -static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) +static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr) { - struct inet6_dev *idev = __in6_dev_get(dev); - switch (READ_ONCE(idev->cnf.accept_untracked_na)) { case 0: /* Don't accept untracked na (absent in neighbor cache) */ return 0; @@ -980,7 +978,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) * same subnet as an address configured on the interface that * received the na */ - return !!ipv6_chk_prefix(saddr, dev); + return !!ipv6_chk_prefix(saddr, idev->dev); default: return 0; } @@ -1078,7 +1076,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb) */ new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE; if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) { - if (accept_untracked_na(dev, saddr)) { + if (accept_untracked_na(idev, saddr)) { neigh = neigh_create(&nd_tbl, &msg->target, dev); new_state = NUD_STALE; } From 05ed733b65ab977dd931e7f7ac0f62fdb81205c2 Mon Sep 17 00:00:00 2001 From: Xingquan Liu Date: Fri, 19 Jun 2026 11:13:47 -0400 Subject: [PATCH 4303/5214] net/sched: dualpi2: fix GSO backlog accounting When DualPI2 splits a GSO skb into N segments, it propagates N additional packets to its parent before returning NET_XMIT_SUCCESS. The parent then accounts for the original skb once more, leaving its qlen one larger than the number of packets actually queued. With QFQ as the parent, after all real packets are dequeued, QFQ still has a non-zero qlen while its in-service aggregate has no active classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes the result to qfq_peek_skb(), causing a NULL pointer dereference. Follow the same pattern used by tbf_segment() and taprio: count only successfully queued segments, propagate the difference between the original skb and those segments, and return NET_XMIT_SUCCESS whenever at least one segment was queued. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Cc: stable@vger.kernel.org Signed-off-by: Xingquan Liu Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260619151447.223640-1-b1n@b1n.io Signed-off-by: Jakub Kicinski --- net/sched/sch_dualpi2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index d7c3254ef800..5434df6ca8ef 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -461,7 +461,7 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (IS_ERR_OR_NULL(nskb)) return qdisc_drop(skb, sch, to_free); - cnt = 1; + cnt = 0; byte_len = 0; orig_len = qdisc_pkt_len(skb); skb_list_walk_safe(nskb, nskb, next) { @@ -488,16 +488,15 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, byte_len += nskb->len; } } - if (cnt > 1) { + if (cnt > 0) { /* The caller will add the original skb stats to its * backlog, compensate this if any nskb is enqueued. */ - --cnt; - byte_len -= orig_len; + qdisc_tree_reduce_backlog(sch, 1 - cnt, + orig_len - byte_len); } - qdisc_tree_reduce_backlog(sch, -cnt, -byte_len); consume_skb(skb); - return err; + return cnt > 0 ? NET_XMIT_SUCCESS : err; } return dualpi2_enqueue_skb(skb, sch, to_free); } From 54704b32b2abd62dbb63082c89fa35685c34674a Mon Sep 17 00:00:00 2001 From: Xingquan Liu Date: Fri, 19 Jun 2026 11:13:48 -0400 Subject: [PATCH 4304/5214] selftests/tc-testing: Add DualPI2 GSO backlog accounting test Add a regression test for DualPI2 GSO backlog accounting when it is used as a child qdisc of QFQ. The test sends one UDP GSO datagram through a QFQ class with DualPI2 as the leaf qdisc. DualPI2 splits the skb into two segments. After the traffic drains, both QFQ and DualPI2 must report zero backlog and zero qlen. On kernels with the broken accounting, QFQ can keep a stale non-zero qlen after all real packets have been dequeued. Signed-off-by: Xingquan Liu Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260619151447.223640-2-b1n@b1n.io Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/qdiscs/dualpi2.json | 44 +++++++++++++++++++ tools/testing/selftests/tc-testing/tdc_gso.py | 43 ++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100755 tools/testing/selftests/tc-testing/tdc_gso.py diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json index cd1f2ee8f354..ed6a900bb568 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/dualpi2.json @@ -250,5 +250,49 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "891f", + "name": "Verify DualPI2 GSO backlog accounting with QFQ parent", + "category": [ + "qdisc", + "dualpi2", + "qfq", + "gso" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: qfq", + "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 1 maxpkt 4096", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: dualpi2", + "$TC filter add dev $DUMMY parent 1: matchall classid 1:1" + ], + "cmdUnderTest": "./tdc_gso.py 10.10.10.10 10.10.10.1 9000 1200 2400", + "expExitCode": "0", + "verifyCmd": "$TC -j -s qdisc ls dev $DUMMY", + "matchJSON": [ + { + "kind": "qfq", + "handle": "1:", + "packets": 2, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "dualpi2", + "handle": "2:", + "packets": 2, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root", + "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tdc_gso.py b/tools/testing/selftests/tc-testing/tdc_gso.py new file mode 100755 index 000000000000..b66528ea4b68 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc_gso.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +""" +tdc_gso.py - send a UDP GSO datagram + +Copyright (C) 2026 Xingquan Liu +""" + +import argparse +import socket +import struct +import sys + +UDP_MAX_SEGMENTS = 1 << 7 + + +parser = argparse.ArgumentParser(description="UDP GSO datagram sender") +parser.add_argument("src", help="source IPv4 address") +parser.add_argument("dst", help="destination IPv4 address") +parser.add_argument("port", type=int, help="destination UDP port") +parser.add_argument("gso_size", type=int, help="UDP GSO segment payload size") +parser.add_argument("payload_len", type=int, help="total UDP payload length") +args = parser.parse_args() + +if args.gso_size <= 0 or args.gso_size > 0xFFFF: + parser.error("gso_size must fit in an unsigned 16-bit integer") +if args.payload_len <= args.gso_size: + parser.error("payload_len must be larger than gso_size") +if args.payload_len > args.gso_size * UDP_MAX_SEGMENTS: + parser.error("payload_len exceeds UDP_MAX_SEGMENTS") + +SOL_UDP = getattr(socket, "SOL_UDP", socket.IPPROTO_UDP) +UDP_SEGMENT = getattr(socket, "UDP_SEGMENT", 103) + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.bind((args.src, 0)) + +payload = b"b" * args.payload_len +cmsg = [(SOL_UDP, UDP_SEGMENT, struct.pack("=H", args.gso_size))] + +sent = sock.sendmsg([payload], cmsg, 0, (args.dst, args.port)) +sys.exit(sent != len(payload)) From eca856950f7cb1a221e02b99d758409f2c5cec42 Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:38:29 +0900 Subject: [PATCH 4305/5214] ipv4: account for fraggap on the paged allocation path In __ip_append_data(), when the paged-allocation branch is taken, alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap, but the fraggap bytes carried over from the previous skb are copied into the new skb's linear area at offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski --- net/ipv4/ip_output.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 3b4e9b8af044..e6dd1e5b8c32 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1116,8 +1116,8 @@ static int __ip_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1164,9 +1164,6 @@ static int __ip_append_data(struct sock *sk, } copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy will be negative if pagedlen>0 - * because then the equation reduces to -fraggap. - */ if (copy > 0 && INDIRECT_CALL_1(getfrag, ip_generic_getfrag, from, data + transhdrlen, offset, From 736b380e28d0480c7bc3e022f1950f31fe53a7c5 Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:46:17 +0900 Subject: [PATCH 4306/5214] ipv6: account for fraggap on the paged allocation path In __ip6_append_data(), when the paged-allocation branch is taken (MSG_MORE / NETIF_F_SG / large fraglen), alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap (datalen = length + fraggap). When fraggap is non-zero, this is not the first skb and transhdrlen is zero. The fraggap bytes carried over from the previous skb are copied just past the fragment headers in the new skb's linear area. The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount, and the copy writes past skb->end into the trailing skb_shared_info. An unprivileged user can trigger this via a UDPv6 socket using MSG_MORE together with MSG_SPLICE_PAGES. The bad accounting was introduced by commit 773ba4fe9104 ("ipv6: avoid partial copy for zc"). Before commit ce650a166335 ("udp6: Fix __ip6_append_data()'s handling of MSG_SPLICE_PAGES"), the negative copy value caused -EINVAL to be returned. That later commit allowed MSG_SPLICE_PAGES to proceed in this case, making the corruption triggerable. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Since a negative copy is no longer expected for a valid MSG_SPLICE_PAGES case, remove the MSG_SPLICE_PAGES exception from the negative copy check. Fixes: 773ba4fe9104 ("ipv6: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFTqRljatR17fFy@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_output.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9f1e0e4f7464..368e4fa3b43c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1667,8 +1667,8 @@ static int __ip6_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1683,10 +1683,7 @@ static int __ip6_append_data(struct sock *sk, fraglen = datalen + fragheaderlen; copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy may be negative if pagedlen>0 - * because then the equation may reduces to -fraggap. - */ - if (copy < 0 && !(flags & MSG_SPLICE_PAGES)) { + if (copy < 0) { err = -EINVAL; goto error; } From 9ed19e11d2146076d117d51a940643990118449b Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Thu, 18 Jun 2026 18:43:35 +0800 Subject: [PATCH 4307/5214] ipv6: ioam: fix type confusion of dst_entry IOAM uses a dummy dst_entry(null_dst) to mark that the destination should not be changed after the transformation. This dst is stored in the IOAM lwt state and may be passed to dst_cache_set_ip6(). However, the IPv6 dst cache path eventually calls rt6_get_cookie(), which treats the dst_entry as part of a struct rt6_info. Since the null_dst was embedded directly as a struct dst_entry in struct ioam6_lwt, this resulted in an invalid cast and rt6_get_cookie() reading fields from the wrong object. In practice, the wrong cookie is not used while dst->obsolete is zero, but rt6_get_cookie() may also access per-cpu value when rt->sernum is zero. In this case, rt->sernum aliases ioam6_lwt::cache::reset_ts, which can become zero, making this a potential invalid pointer access. Fix this by embedding a full struct rt6_info for the dummy IPv6 route and passing its dst member to the dst APIs. Fixes: 47ce7c854563 ("net: ipv6: ioam6: fix double reallocation") Signed-off-by: Jiayuan Chen Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260618104336.48934-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv6/ioam6_iptunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index b9f6d892a566..cfb2c41634a0 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -35,7 +35,7 @@ struct ioam6_lwt_freq { }; struct ioam6_lwt { - struct dst_entry null_dst; + struct rt6_info null_rt; struct dst_cache cache; struct ioam6_lwt_freq freq; atomic_t pkt_cnt; @@ -176,7 +176,7 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, * it is stored in the cache. Then, +1/-1 each time we read the cache * and release it. Long story short, we're fine. */ - dst_init(&ilwt->null_dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); + dst_init(&ilwt->null_rt.dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); atomic_set(&ilwt->pkt_cnt, 0); ilwt->freq.k = freq_k; @@ -360,7 +360,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) /* This is how we notify that the destination does not change after * transformation and that we need to use orig_dst instead of the cache */ - if (dst == &ilwt->null_dst) { + if (dst == &ilwt->null_rt.dst) { dst_release(dst); dst = orig_dst; @@ -429,7 +429,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) local_bh_disable(); if (orig_dst->lwtstate == dst->lwtstate) dst_cache_set_ip6(&ilwt->cache, - &ilwt->null_dst, &fl6.saddr); + &ilwt->null_rt.dst, &fl6.saddr); else dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr); local_bh_enable(); From d07d80b6a129a44538cda1549b7acf95154fb197 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 18 Jun 2026 12:28:12 +0300 Subject: [PATCH 4308/5214] dpaa2-switch: do not accept VLAN uppers while bridged The dpaa2-switch driver does not support VLAN uppers while its ports are bridged. This scenario tried to be prevented by rejecting a bridge join while VLAN uppers exist but the reverse order was still possible. This patches adds a check so that the dpaa2-switch also does not accept VLAN uppers while bridged. Fixes: f48298d3fbfa ("staging: dpaa2-switch: move the driver out of staging") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260618092813.432535-2-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 9f75cd66ae38..858ba844ac51 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2233,6 +2233,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, struct netdev_notifier_changeupper_info *info) { + struct ethsw_port_priv *port_priv; struct netlink_ext_ack *extack; struct net_device *upper_dev; int err; @@ -2251,6 +2252,13 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, if (!info->linking) dpaa2_switch_port_pre_bridge_leave(netdev); + } else if (is_vlan_dev(upper_dev)) { + port_priv = netdev_priv(netdev); + if (port_priv->fdb->bridge_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot accept VLAN uppers while bridged"); + return -EOPNOTSUPP; + } } return 0; From e87827da8c351db0de504534e6aa17be3014bc25 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 22 Jun 2026 03:44:14 +0800 Subject: [PATCH 4309/5214] erofs: add sparse support to pcluster layout Although zeros can be compressed transparently on EROFS using fixed-size output compression so that it is never prioritized in the Android use cases, indicating entire pclusters as holes is still useful to preserve holes in the sparse datasets; otherwise overlayfs will allocate more space when copying up, and SEEK_HOLE won't report any hole. This patch introduces two ways to mark a pcluster as a hole: - A new Z_EROFS_LI_HOLE compatible flag (bit 14) in the HEAD lcluster advise field for non-compact (full) indexes; - A 0-block CBLKCNT value on the first NONHEAD lcluster. The hole tag is preferred for maximum compatibility since pre-existing kernels that do not understand Z_EROFS_LI_HOLE will decompress at the stored blkaddr (the same blkaddr will be shared among all sparse pclusters). Only the 0-block CBLKCNT approach also works for compact indexes, but it is limited to big pclusters and new kernels. Signed-off-by: Gao Xiang --- fs/erofs/erofs_fs.h | 2 ++ fs/erofs/zmap.c | 33 ++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h index 7871b16c1d33..16ec4fd33ac6 100644 --- a/fs/erofs/erofs_fs.h +++ b/fs/erofs/erofs_fs.h @@ -396,6 +396,8 @@ enum { /* (noncompact only, HEAD) This pcluster refers to partial decompressed data */ #define Z_EROFS_LI_PARTIAL_REF (1 << 15) +/* (noncompact only, HEAD) This pcluster can also be regarded as a HOLE */ +#define Z_EROFS_LI_HOLE (1 << 14) /* Set on 1st non-head lcluster to store compressed block counti (in blocks) */ #define Z_EROFS_LI_D0_CBLKCNT (1 << 11) diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index e1a02a2c8406..bab521613552 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -15,8 +15,9 @@ struct z_erofs_maprecorder { u8 type, headtype; u16 clusterofs; u16 delta[2]; - erofs_blk_t pblk, compressedblks; + erofs_blk_t pblk; erofs_off_t nextpackoff; + int compressedblks; bool partialref, in_mbox; }; @@ -54,7 +55,12 @@ static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m, u64 lcn) } else { m->partialref = !!(advise & Z_EROFS_LI_PARTIAL_REF); m->clusterofs = le16_to_cpu(di->di_clusterofs); - m->pblk = le32_to_cpu(di->di_u.blkaddr); + if (advise & Z_EROFS_LI_HOLE) { + m->compressedblks = 0; + m->pblk = EROFS_NULL_ADDR; + } else { + m->pblk = le32_to_cpu(di->di_u.blkaddr); + } } return 0; } @@ -309,9 +315,10 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m, ((m->headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN || m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2) && !bigpcl2) || (lcn << vi->z_lclusterbits) >= inode->i_size) - m->compressedblks = 1; + if (m->compressedblks < 0) + m->compressedblks = 1; - if (m->compressedblks) + if (m->compressedblks >= 0) goto out; err = z_erofs_load_lcluster_from_disk(m, lcn, false); @@ -329,19 +336,22 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m, DBG_BUGON(lcn == initial_lcn && m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD); - if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD && m->delta[0] != 1) { + if (m->type != Z_EROFS_LCLUSTER_TYPE_NONHEAD) { + /* + * if the 1st NONHEAD lcluster is actually PLAIN or HEAD type + * rather than CBLKCNT, it's a 1 block-sized pcluster. + */ + if (m->compressedblks < 0) + m->compressedblks = 1; + } else if (m->delta[0] != 1 || m->compressedblks < 0) { erofs_err(sb, "bogus CBLKCNT @ lcn %llu of nid %llu", lcn, vi->nid); DBG_BUGON(1); return -EFSCORRUPTED; } - /* - * if the 1st NONHEAD lcluster is actually PLAIN or HEAD type rather - * than CBLKCNT, it's a 1 block-sized pcluster. - */ - if (m->type != Z_EROFS_LCLUSTER_TYPE_NONHEAD || !m->compressedblks) - m->compressedblks = 1; out: + if (!m->compressedblks) + m->map->m_flags &= ~EROFS_MAP_MAPPED; m->map->m_plen = erofs_pos(sb, m->compressedblks); return 0; } @@ -395,6 +405,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode, .inode = inode, .map = map, .in_mbox = erofs_inode_in_metabox(inode), + .compressedblks = -1, }; unsigned int endoff; unsigned long initial_lcn; From 59397c6b755a35e5a33dbcbe22240cd86ebb935b Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Fri, 19 Jun 2026 16:34:03 +0800 Subject: [PATCH 4310/5214] erofs: simplify RCU read critical sections - use scoped_guard() for RCU read critical section in z_erofs_decompress_kickoff(); - simplify the RCU critical section loop in z_erofs_pcluster_begin(). Signed-off-by: Gao Xiang --- fs/erofs/zdata.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index c6240dccbb0f..1c65b741b288 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -806,6 +806,7 @@ static int z_erofs_pcluster_begin(struct z_erofs_frontend *fe) struct super_block *sb = fe->inode->i_sb; struct z_erofs_pcluster *pcl = NULL; void *ptr = NULL; + bool needretry; int ret; DBG_BUGON(fe->pcl); @@ -825,19 +826,16 @@ static int z_erofs_pcluster_begin(struct z_erofs_frontend *fe) } ptr = map->buf.page; } else { - while (1) { + do { rcu_read_lock(); pcl = xa_load(&EROFS_SB(sb)->managed_pslots, map->m_pa); - if (!pcl || z_erofs_get_pcluster(pcl)) { - DBG_BUGON(pcl && map->m_pa != pcl->pos); - rcu_read_unlock(); - break; - } + needretry = pcl && !z_erofs_get_pcluster(pcl); rcu_read_unlock(); - } + } while (needretry); } if (pcl) { + DBG_BUGON(map->m_pa != pcl->pos); fe->pcl = pcl; ret = -EEXIST; } else { @@ -1459,21 +1457,19 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io, if (sbi->sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO) sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON; #ifdef CONFIG_EROFS_FS_PCPU_KTHREAD - struct kthread_worker *worker; + scoped_guard(rcu) { + struct kthread_worker *worker; - rcu_read_lock(); - worker = rcu_dereference( + worker = rcu_dereference( z_erofs_pcpu_workers[raw_smp_processor_id()]); - if (!worker) { - INIT_WORK(&io->u.work, z_erofs_decompressqueue_work); - queue_work(z_erofs_workqueue, &io->u.work); - } else { - kthread_queue_work(worker, &io->u.kthread_work); + if (worker) { + kthread_queue_work(worker, &io->u.kthread_work); + return; + } } - rcu_read_unlock(); -#else - queue_work(z_erofs_workqueue, &io->u.work); + INIT_WORK(&io->u.work, z_erofs_decompressqueue_work); #endif + queue_work(z_erofs_workqueue, &io->u.work); return; } gfp_flag = memalloc_noio_save(); From c7ab7504631d8d9aecee3d1509cc779eb4778844 Mon Sep 17 00:00:00 2001 From: Udaya Kiran Challa Date: Thu, 14 May 2026 23:03:32 +0530 Subject: [PATCH 4311/5214] dt-bindings: rtc: epson,rx6110: Convert to DT Schema Convert the Epson RX6110 Real Time Clock devicetree binding from the legacy text format to DT schema. Signed-off-by: Udaya Kiran Challa Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260514173851.25088-1-challauday369@gmail.com Signed-off-by: Alexandre Belloni --- .../devicetree/bindings/rtc/epson,rx6110.txt | 39 ----------- .../devicetree/bindings/rtc/epson,rx6110.yaml | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 39 deletions(-) delete mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.txt create mode 100644 Documentation/devicetree/bindings/rtc/epson,rx6110.yaml diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt deleted file mode 100644 index 3dc313e01f77..000000000000 --- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt +++ /dev/null @@ -1,39 +0,0 @@ -Epson RX6110 Real Time Clock -============================ - -The Epson RX6110 can be used with SPI or I2C busses. The kind of -bus depends on the SPISEL pin and can not be configured via software. - -I2C mode --------- - -Required properties: - - compatible: should be: "epson,rx6110" - - reg : the I2C address of the device for I2C - -Example: - - rtc: rtc@32 { - compatible = "epson,rx6110" - reg = <0x32>; - }; - -SPI mode --------- - -Required properties: - - compatible: should be: "epson,rx6110" - - reg: chip select number - - spi-cs-high: RX6110 needs chipselect high - - spi-cpha: RX6110 works with SPI shifted clock phase - - spi-cpol: RX6110 works with SPI inverse clock polarity - -Example: - - rtc: rtc@3 { - compatible = "epson,rx6110" - reg = <3> - spi-cs-high; - spi-cpha; - spi-cpol; - }; diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml new file mode 100644 index 000000000000..55086ac7d1e2 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Epson RX6110 Real Time Clock + +description: + The Epson RX6110 can be used with SPI or I2C busses. The kind of bus depends + on the SPISEL pin and cannot be configured via software. + +maintainers: + - Alexandre Belloni + +allOf: + - $ref: rtc.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: epson,rx6110 + + reg: + maxItems: 1 + + spi-cs-high: true + spi-cpha: true + spi-cpol: true + +required: + - compatible + - reg + +dependencies: + spi-cs-high: [ spi-cpha, spi-cpol ] + spi-cpha: [ spi-cs-high, spi-cpol ] + spi-cpol: [ spi-cs-high, spi-cpha ] + +unevaluatedProperties: false + +examples: + # I2C mode + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@32 { + compatible = "epson,rx6110"; + reg = <0x32>; + }; + }; + + # SPI mode + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + rtc@3 { + compatible = "epson,rx6110"; + reg = <3>; + spi-cs-high; + spi-cpha; + spi-cpol; + }; + }; From 7e342d87aa8e6b831cf6d21ca41b1f7e032d0fcf Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:55 +0100 Subject: [PATCH 4312/5214] rtc: renesas-rtca3: Fix PIE clear polling condition in alarm setup error path In rtca3_set_alarm(), the setup_failed path attempts to disable the Periodic Interrupt Enable (PIE) bit and wait until it is cleared. However, the polling condition passed to readb_poll_timeout_atomic() uses an incorrect expression: !(tmp & ~RTCA3_RCR1_PIE) As ~RTCA3_RCR1_PIE evaluates to a mask of all bits except PIE, the condition effectively waits for all non-PIE bits to become zero, which is unrelated to the intended operation and is unlikely to ever be true. This causes the poll to time out unnecessarily. Fix the condition to check for the PIE bit itself being cleared: !(tmp & RTCA3_RCR1_PIE) This correctly waits until PIE is deasserted after being cleared. Fixes: d4488377609e3 ("rtc: renesas-rtca3: Add driver for RTCA-3 available on Renesas RZ/G3S SoC") Cc: stable@vger.kernel.org Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-renesas-rtca3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index cbabaa4dc96a..2dc080d0eb6c 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -455,7 +455,7 @@ static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) * specified timeout for setup. */ writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); - readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE), + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & RTCA3_RCR1_PIE), 10, RTCA3_DEFAULT_TIMEOUT_US); atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); } From fafb016d081200c7652e84202f8ba5951e659a53 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:56 +0100 Subject: [PATCH 4313/5214] rtc: renesas-rtca3: Check RADJ poll result during initial setup In rtca3_initial_setup(), the driver clears the RTCA3_RADJ register and waits for it to reach zero using readb_poll_timeout(). Check the return value of readb_poll_timeout() and propagate the error if the poll fails. Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-renesas-rtca3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index 2dc080d0eb6c..af2a3878289e 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -634,6 +634,8 @@ static int rtca3_initial_setup(struct clk *clk, struct rtca3_priv *priv) writeb(0, priv->base + RTCA3_RADJ); ret = readb_poll_timeout(priv->base + RTCA3_RADJ, tmp, !tmp, 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; /* Start the RTC and enable automatic time error adjustment. */ mask = RTCA3_RCR2_START | RTCA3_RCR2_AADJE; From 09939630aad95fc3dc4cc2d94a5a96d7c4d6642f Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:57 +0100 Subject: [PATCH 4314/5214] rtc: renesas-rtca3: Fix incorrect error message for reset assert Update the message to "assert reset" to accurately reflect the operation being performed. Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-renesas-rtca3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index af2a3878289e..8763745b9172 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -702,7 +702,7 @@ static void rtca3_action(void *data) ret = reset_control_assert(priv->rstc); if (ret) - dev_err(dev, "Failed to de-assert reset!"); + dev_err(dev, "Failed to assert reset!"); ret = pm_runtime_put_sync(dev); if (ret < 0) From 9fb12656a7a5473b1ce46e27f78e92e5e8df7c58 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:58 +0100 Subject: [PATCH 4315/5214] rtc: renesas-rtca3: Fix typo in rtca3_ppb_per_cycle documentation Correct a typo in the kernel-doc comment for struct rtca3_ppb_per_cycle by fixing "adjutment" to "adjustment". Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-5-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-renesas-rtca3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index 8763745b9172..97e7e65f59a5 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -103,7 +103,7 @@ enum rtca3_alrm_set_step { /** * struct rtca3_ppb_per_cycle - PPB per cycle - * @ten_sec: PPB per cycle in 10 seconds adjutment mode + * @ten_sec: PPB per cycle in 10 seconds adjustment mode * @sixty_sec: PPB per cycle in 60 seconds adjustment mode */ struct rtca3_ppb_per_cycle { From 2098bb8ac5f5a66b1a0e02512ae11b6208936c92 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:59 +0100 Subject: [PATCH 4316/5214] rtc: renesas-rtca3: Factor out year decoding helper The logic to decode the year value from the hardware registers is duplicated in both rtca3_read_time() and rtca3_read_alarm(). Introduce a helper rtca3_decode_year() to centralize this conversion. Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-6-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-renesas-rtca3.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index 97e7e65f59a5..b3875d041de5 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -228,12 +228,19 @@ static void rtca3_prepare_cntalrm_regs_for_read(struct rtca3_priv *priv, bool cn } } +static u32 rtca3_decode_year(u8 mask, u16 year) +{ + u8 y = FIELD_GET(mask, year); + u32 century = bcd2bin((y == 0x99) ? 0x19 : 0x20); + + return (century * 100 + bcd2bin(y)) - 1900; +} + static int rtca3_read_time(struct device *dev, struct rtc_time *tm) { struct rtca3_priv *priv = dev_get_drvdata(dev); u8 sec, min, hour, wday, mday, month, tmp; u8 trials = 0; - u32 year100; u16 year; guard(spinlock_irqsave)(&priv->lock); @@ -274,9 +281,7 @@ static int rtca3_read_time(struct device *dev, struct rtc_time *tm) tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKCNT_WK, wday)); tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYCNT_DAY, mday)); tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONCNT_MONTH, month)) - 1; - year = FIELD_GET(RTCA3_RYRCNT_YEAR, year); - year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); - tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + tm->tm_year = rtca3_decode_year(RTCA3_RYRCNT_YEAR, year); return 0; } @@ -354,7 +359,6 @@ static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) struct rtca3_priv *priv = dev_get_drvdata(dev); u8 sec, min, hour, wday, mday, month; struct rtc_time *tm = &wkalrm->time; - u32 year100; u16 year; guard(spinlock_irqsave)(&priv->lock); @@ -373,9 +377,7 @@ static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKAR_DAYW, wday)); tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYAR_DATE, mday)); tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONAR_MON, month)) - 1; - year = FIELD_GET(RTCA3_RYRAR_YR, year); - year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); - tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + tm->tm_year = rtca3_decode_year(RTCA3_RYRAR_YR, year); wkalrm->enabled = !!(readb(priv->base + RTCA3_RCR1) & RTCA3_RCR1_AIE); From 419719c514252a2dbb2e2976f564c83417dd6d0a Mon Sep 17 00:00:00 2001 From: Antoni Pokusinski Date: Wed, 15 Apr 2026 18:06:11 +0200 Subject: [PATCH 4317/5214] rtc: abx80x: fix the RTC_VL_CLR clearing all status flags The RTC_VL_CLR ioctl intends to clear only the battery low flag (BLF), however the current implementation writes 0 to the status register, clearing all status bits. Fix this by writing back the masked status value so that only BLF is cleared, preserving other status flags. Fixes: ffe1c5a2d427 ("rtc: abx80x: Implement RTC_VL_READ,CLR ioctls") Signed-off-by: Antoni Pokusinski Link: https://patch.msgid.link/20260415160610.127155-2-apokusinski01@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-abx80x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 00d7de64ed3e..008a70baa69f 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -545,7 +545,8 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) status &= ~ABX8XX_STATUS_BLF; - tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, + status); if (tmp < 0) return tmp; From 9792ff8afa9017fe14f436f3ef3cd75f41f9f145 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 13 May 2026 18:55:55 +0100 Subject: [PATCH 4318/5214] rtc: mpfs: fix counter upload completion condition The condition that needs to be checked for upload completion is the UPLOAD bit in the completion register going low. The original iterations of this driver used a do-while and this was converted to a read_poll_timeout() during upstreaming without the condition being inverted as it should have been. I suspect that this went unnoticed until now because a) the first read was done when the bit was still set, immediately completing the read_poll_timeout() and b) because the RTC doesn't hold time when power is removed from the SoC reducing its utility (I for one keep it disabled). If my first suspicion was true when the driver was upstreamed, it's not true any longer though, hence the detection of the problem. Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC") CC: stable@vger.kernel.org Signed-off-by: Conor Dooley Tested-by: Valentina Fernandez Link: https://patch.msgid.link/20260513-panhandle-ashy-70c6abf84d59@spud Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index 6aa3eae575d2..ece6de4a6adb 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) ctrl |= CONTROL_UPLOAD_BIT; writel(ctrl, rtcdev->base + CONTROL_REG); - ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US, false, rtcdev->base + CONTROL_REG); if (ret) { dev_err(dev, "timed out uploading time to rtc"); From a091e1ba3b68cabc9caedafc6f81d9fe9b3b2200 Mon Sep 17 00:00:00 2001 From: Ronan Dalton Date: Fri, 8 May 2026 15:24:49 +1200 Subject: [PATCH 4319/5214] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231 Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was checked during device probe for the ds1337, ds1339, ds1341, and ds3231 chips; if it was set, it would be cleared and a warning would be logged saying "SET TIME!". Since that commit, the OSF bit is no longer cleared, but the warning is still printed. Directly following that commit, there was no way to get rid of this warning because nothing cleared the OSF bit on these chips. The commit associated with the previous commit, 523923cfd5d6 ("rtc: ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use of the OSF when getting and setting the time in the RTC. However, the other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding change made. Given that the OSF bit is no longer cleared at probe time when it is set, the remaining three chips should have the same handling as the ds1341 chip has for the OSF bit. Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the same logic as the ds1341 has to these chips. Note that any devices brought up between the first referenced commit and this one may begin mistrusting the time reported by the RTC until it is set again, if the bit was never explicitly cleared. Note that only the ds1339 was tested with this change, but the datasheets for the other chips contain essentially identical descriptions of the OSF bit so the same change should work. Signed-off-by: Ronan Dalton Cc: linux-rtc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Alexandre Belloni Cc: Tyler Hicks Cc: Sasha Levin Cc: Meagan Lloyd Cc: Rodolfo Giometti Cc: Chris Packham Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe") Reviewed-by: Meagan Lloyd Reviewed-by: Tyler Hicks Link: https://patch.msgid.link/20260508032518.3696705-2-ronan.dalton@alliedtelesis.co.nz Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7205c59ff729..edf81b975dec 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1338_BIT_OSF) return -EINVAL; break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); + if (ret) + return ret; + if (tmp & DS1337_BIT_OSF) + return -EINVAL; + break; case ds_1340: if (tmp & DS1340_BIT_nEOSC) return -EINVAL; @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1340_BIT_OSF) return -EINVAL; break; - case ds_1341: - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); - if (ret) - return ret; - if (tmp & DS1337_BIT_OSF) - return -EINVAL; - break; case ds_1388: ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); if (ret) @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, DS1338_BIT_OSF, 0); break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, + DS1337_BIT_OSF, 0); + break; case ds_1340: regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, DS1340_BIT_OSF, 0); break; - case ds_1341: - regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, - DS1337_BIT_OSF, 0); - break; case ds_1388: regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, DS1388_BIT_OSF, 0); From 3eebec1cb5dc1abd9d0b6a97a752800bf1a4e035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 17:47:20 +0200 Subject: [PATCH 4320/5214] rtc: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Tóth János Link: https://patch.msgid.link/20260515154720.406128-2-u.kleine-koenig@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ab-b5ze-s3.c | 2 +- drivers/rtc/rtc-ab-eoz9.c | 2 +- drivers/rtc/rtc-abx80x.c | 20 ++++++++++---------- drivers/rtc/rtc-bq32k.c | 2 +- drivers/rtc/rtc-ds1307.c | 36 ++++++++++++++++++------------------ drivers/rtc/rtc-ds1374.c | 2 +- drivers/rtc/rtc-ds1672.c | 2 +- drivers/rtc/rtc-ds3232.c | 2 +- drivers/rtc/rtc-em3027.c | 2 +- drivers/rtc/rtc-fm3130.c | 2 +- drivers/rtc/rtc-hym8563.c | 4 ++-- drivers/rtc/rtc-isl12022.c | 2 +- drivers/rtc/rtc-isl12026.c | 4 ++-- drivers/rtc/rtc-isl1208.c | 10 +++++----- drivers/rtc/rtc-m41t80.c | 22 +++++++++++----------- drivers/rtc/rtc-max31335.c | 4 ++-- drivers/rtc/rtc-max6900.c | 2 +- drivers/rtc/rtc-nct3018y.c | 2 +- drivers/rtc/rtc-pcf2127.c | 8 ++++---- drivers/rtc/rtc-pcf85063.c | 12 ++++++------ drivers/rtc/rtc-pcf8523.c | 2 +- drivers/rtc/rtc-pcf8563.c | 6 +++--- drivers/rtc/rtc-pcf8583.c | 2 +- drivers/rtc/rtc-rs5c372.c | 12 ++++++------ drivers/rtc/rtc-rv3029c2.c | 4 ++-- drivers/rtc/rtc-rv8803.c | 8 ++++---- drivers/rtc/rtc-rx6110.c | 2 +- drivers/rtc/rtc-rx8010.c | 2 +- drivers/rtc/rtc-rx8025.c | 4 ++-- drivers/rtc/rtc-rx8581.c | 2 +- drivers/rtc/rtc-s35390a.c | 2 +- drivers/rtc/rtc-sd2405al.c | 2 +- drivers/rtc/rtc-sd3078.c | 2 +- drivers/rtc/rtc-x1205.c | 2 +- 34 files changed, 97 insertions(+), 97 deletions(-) diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c index 684f9898d768..6439ca427c32 100644 --- a/drivers/rtc/rtc-ab-b5ze-s3.c +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -933,7 +933,7 @@ MODULE_DEVICE_TABLE(of, abb5zes3_dt_match); #endif static const struct i2c_device_id abb5zes3_id[] = { - { "abb5zes3" }, + { .name = "abb5zes3" }, { } }; MODULE_DEVICE_TABLE(i2c, abb5zes3_id); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c index de002f7a39bf..b75f4f665076 100644 --- a/drivers/rtc/rtc-ab-eoz9.c +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -546,7 +546,7 @@ MODULE_DEVICE_TABLE(of, abeoz9_dt_match); #endif static const struct i2c_device_id abeoz9_id[] = { - { "abeoz9" }, + { .name = "abeoz9" }, { } }; diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 008a70baa69f..5486d9d0b1e5 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -753,16 +753,16 @@ static int abx80x_setup_nvmem(struct abx80x_priv *priv) } static const struct i2c_device_id abx80x_id[] = { - { "abx80x", ABX80X }, - { "ab0801", AB0801 }, - { "ab0803", AB0803 }, - { "ab0804", AB0804 }, - { "ab0805", AB0805 }, - { "ab1801", AB1801 }, - { "ab1803", AB1803 }, - { "ab1804", AB1804 }, - { "ab1805", AB1805 }, - { "rv1805", RV1805 }, + { .name = "abx80x", .driver_data = ABX80X }, + { .name = "ab0801", .driver_data = AB0801 }, + { .name = "ab0803", .driver_data = AB0803 }, + { .name = "ab0804", .driver_data = AB0804 }, + { .name = "ab0805", .driver_data = AB0805 }, + { .name = "ab1801", .driver_data = AB1801 }, + { .name = "ab1803", .driver_data = AB1803 }, + { .name = "ab1804", .driver_data = AB1804 }, + { .name = "ab1805", .driver_data = AB1805 }, + { .name = "rv1805", .driver_data = RV1805 }, { } }; MODULE_DEVICE_TABLE(i2c, abx80x_id); diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 7ad34539be4d..20cd92d00fa1 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -304,7 +304,7 @@ static void bq32k_remove(struct i2c_client *client) } static const struct i2c_device_id bq32k_id[] = { - { "bq32000" }, + { .name = "bq32000" }, { } }; MODULE_DEVICE_TABLE(i2c, bq32k_id); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index edf81b975dec..ee6e75a5efc5 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1069,24 +1069,24 @@ static const struct chip_desc chips[last_ds_type] = { }; static const struct i2c_device_id ds1307_id[] = { - { "ds1307", ds_1307 }, - { "ds1308", ds_1308 }, - { "ds1337", ds_1337 }, - { "ds1338", ds_1338 }, - { "ds1339", ds_1339 }, - { "ds1388", ds_1388 }, - { "ds1340", ds_1340 }, - { "ds1341", ds_1341 }, - { "ds3231", ds_3231 }, - { "m41t0", m41t0 }, - { "m41t00", m41t00 }, - { "m41t11", m41t11 }, - { "mcp7940x", mcp794xx }, - { "mcp7941x", mcp794xx }, - { "pt7c4338", ds_1307 }, - { "rx8025", rx_8025 }, - { "isl12057", ds_1337 }, - { "rx8130", rx_8130 }, + { .name = "ds1307", .driver_data = ds_1307 }, + { .name = "ds1308", .driver_data = ds_1308 }, + { .name = "ds1337", .driver_data = ds_1337 }, + { .name = "ds1338", .driver_data = ds_1338 }, + { .name = "ds1339", .driver_data = ds_1339 }, + { .name = "ds1388", .driver_data = ds_1388 }, + { .name = "ds1340", .driver_data = ds_1340 }, + { .name = "ds1341", .driver_data = ds_1341 }, + { .name = "ds3231", .driver_data = ds_3231 }, + { .name = "m41t0", .driver_data = m41t0 }, + { .name = "m41t00", .driver_data = m41t00 }, + { .name = "m41t11", .driver_data = m41t11 }, + { .name = "mcp7940x", .driver_data = mcp794xx }, + { .name = "mcp7941x", .driver_data = mcp794xx }, + { .name = "pt7c4338", .driver_data = ds_1307 }, + { .name = "rx8025", .driver_data = rx_8025 }, + { .name = "isl12057", .driver_data = ds_1337 }, + { .name = "rx8130", .driver_data = rx_8130 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1307_id); diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index c2359eb86bc9..8c247215d611 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -52,7 +52,7 @@ #define DS1374_REG_TCR 0x09 /* Trickle Charge */ static const struct i2c_device_id ds1374_id[] = { - { "ds1374" }, + { .name = "ds1374" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1374_id); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 6e5314215d00..c610beb55bb5 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -133,7 +133,7 @@ static int ds1672_probe(struct i2c_client *client) } static const struct i2c_device_id ds1672_id[] = { - { "ds1672" }, + { .name = "ds1672" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1672_id); diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 18f35823b4b5..d1ef9e0dad34 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -566,7 +566,7 @@ static int ds3232_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ds3232_id[] = { - { "ds3232" }, + { .name = "ds3232" }, { } }; MODULE_DEVICE_TABLE(i2c, ds3232_id); diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index dc1ccbc65dcb..d555e5d59881 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -129,7 +129,7 @@ static int em3027_probe(struct i2c_client *client) } static const struct i2c_device_id em3027_id[] = { - { "em3027" }, + { .name = "em3027" }, { } }; MODULE_DEVICE_TABLE(i2c, em3027_id); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index f82728ebac0c..28bb6d3f644e 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -53,7 +53,7 @@ struct fm3130 { int data_valid; }; static const struct i2c_device_id fm3130_id[] = { - { "fm3130" }, + { .name = "fm3130" }, { } }; MODULE_DEVICE_TABLE(i2c, fm3130_id); diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index 7a170c0f9710..3156aa5f2d9f 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -564,8 +564,8 @@ static int hym8563_probe(struct i2c_client *client) } static const struct i2c_device_id hym8563_id[] = { - { "hym8563" }, - {} + { .name = "hym8563" }, + { } }; MODULE_DEVICE_TABLE(i2c, hym8563_id); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 5fc52dc64213..bc36288854ee 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -604,7 +604,7 @@ static const struct of_device_id isl12022_dt_match[] = { MODULE_DEVICE_TABLE(of, isl12022_dt_match); static const struct i2c_device_id isl12022_id[] = { - { "isl12022" }, + { .name = "isl12022" }, { } }; MODULE_DEVICE_TABLE(i2c, isl12022_id); diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c index 45a2c9f676c5..b86a325d8a23 100644 --- a/drivers/rtc/rtc-isl12026.c +++ b/drivers/rtc/rtc-isl12026.c @@ -485,8 +485,8 @@ static const struct of_device_id isl12026_dt_match[] = { MODULE_DEVICE_TABLE(of, isl12026_dt_match); static const struct i2c_device_id isl12026_id[] = { - { "isl12026" }, - { }, + { .name = "isl12026" }, + { } }; MODULE_DEVICE_TABLE(i2c, isl12026_id); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index f71a6bb77b2a..bcaa766a5068 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -110,11 +110,11 @@ static const struct isl1208_config config_raa215300_a0 = { }; static const struct i2c_device_id isl1208_id[] = { - { "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 }, - { "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 }, - { "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 }, - { "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 }, - { "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 }, + { .name = "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 }, + { .name = "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 }, + { .name = "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 }, + { .name = "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 }, + { .name = "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 }, { } }; MODULE_DEVICE_TABLE(i2c, isl1208_id); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index b26afef37d9c..3c8c379392c1 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -71,17 +71,17 @@ #define M41T80_FEATURE_SQ_ALT BIT(4) /* RSx bits are in reg 4 */ static const struct i2c_device_id m41t80_id[] = { - { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, - { "m41t65", M41T80_FEATURE_WD }, - { "m41t80", M41T80_FEATURE_SQ }, - { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, - { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "rv4162", M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, + { .name = "m41t62", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, + { .name = "m41t65", .driver_data = M41T80_FEATURE_WD }, + { .name = "m41t80", .driver_data = M41T80_FEATURE_SQ }, + { .name = "m41t81", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, + { .name = "m41t81s", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t82", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t83", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st84", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st85", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st87", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "rv4162", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, { } }; MODULE_DEVICE_TABLE(i2c, m41t80_id); diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c index 952b455071d6..595816973851 100644 --- a/drivers/rtc/rtc-max31335.c +++ b/drivers/rtc/rtc-max31335.c @@ -745,8 +745,8 @@ static int max31335_probe(struct i2c_client *client) } static const struct i2c_device_id max31335_id[] = { - { "max31331", (kernel_ulong_t)&chip[ID_MAX31331] }, - { "max31335", (kernel_ulong_t)&chip[ID_MAX31335] }, + { .name = "max31331", .driver_data = (kernel_ulong_t)&chip[ID_MAX31331] }, + { .name = "max31335", .driver_data = (kernel_ulong_t)&chip[ID_MAX31335] }, { } }; diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 7be31fce5bc7..8ef6d0fcd032 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -215,7 +215,7 @@ static int max6900_probe(struct i2c_client *client) } static const struct i2c_device_id max6900_id[] = { - { "max6900" }, + { .name = "max6900" }, { } }; MODULE_DEVICE_TABLE(i2c, max6900_id); diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c index cd4b1db902e9..700a395fad3a 100644 --- a/drivers/rtc/rtc-nct3018y.c +++ b/drivers/rtc/rtc-nct3018y.c @@ -572,7 +572,7 @@ static int nct3018y_probe(struct i2c_client *client) } static const struct i2c_device_id nct3018y_id[] = { - { "nct3018y" }, + { .name = "nct3018y" }, { } }; MODULE_DEVICE_TABLE(i2c, nct3018y_id); diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index e4785c5a55d0..1995e9f2756d 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -1449,10 +1449,10 @@ static const struct regmap_bus pcf2127_i2c_regmap = { static struct i2c_driver pcf2127_i2c_driver; static const struct i2c_device_id pcf2127_i2c_id[] = { - { "pcf2127", (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, - { "pcf2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, - { "pca2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, - { "pcf2131", (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, + { .name = "pcf2127", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, + { .name = "pcf2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pca2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pcf2131", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, { } }; MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index f643e0bd7351..01e209d88f5f 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -665,12 +665,12 @@ static const struct pcf85063_config config_rv8263 = { }; static const struct i2c_device_id pcf85063_ids[] = { - { "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, - { "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, - { "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, - { "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, - { "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, - {} + { .name = "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, + { .name = "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, + { .name = "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, + { } }; MODULE_DEVICE_TABLE(i2c, pcf85063_ids); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 2c63c0ffd05a..e8354953836c 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -495,7 +495,7 @@ static int pcf8523_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8523_id[] = { - { "pcf8523" }, + { .name = "pcf8523" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8523_id); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index b281e9489df1..81d13733b1e9 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -557,9 +557,9 @@ static int pcf8563_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8563_id[] = { - { "pcf8563" }, - { "rtc8564" }, - { "pca8565" }, + { .name = "pcf8563" }, + { .name = "rtc8564" }, + { .name = "pca8565" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8563_id); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 652b9dfa7566..df5e20cbc26c 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -297,7 +297,7 @@ static int pcf8583_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8583_id[] = { - { "pcf8583" }, + { .name = "pcf8583" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8583_id); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 936f4f05c8c7..24bd795d9d95 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -75,12 +75,12 @@ enum rtc_type { }; static const struct i2c_device_id rs5c372_id[] = { - { "r2025sd", rtc_r2025sd }, - { "r2221tl", rtc_r2221tl }, - { "rs5c372a", rtc_rs5c372a }, - { "rs5c372b", rtc_rs5c372b }, - { "rv5c386", rtc_rv5c386 }, - { "rv5c387a", rtc_rv5c387a }, + { .name = "r2025sd", .driver_data = rtc_r2025sd }, + { .name = "r2221tl", .driver_data = rtc_r2221tl }, + { .name = "rs5c372a", .driver_data = rtc_rs5c372a }, + { .name = "rs5c372b", .driver_data = rtc_rs5c372b }, + { .name = "rv5c386", .driver_data = rtc_rv5c386 }, + { .name = "rv5c387a", .driver_data = rtc_rv5c387a }, { } }; MODULE_DEVICE_TABLE(i2c, rs5c372_id); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 83331d1fcab0..98953af2a24a 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -807,8 +807,8 @@ static int rv3029_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id rv3029_id[] = { - { "rv3029" }, - { "rv3029c2" }, + { .name = "rv3029" }, + { .name = "rv3029c2" }, { } }; MODULE_DEVICE_TABLE(i2c, rv3029_id); diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 2bf988a89fd7..b9b5fee16ee4 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -631,10 +631,10 @@ static int rv8803_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(rv8803_pm_ops, rv8803_suspend, rv8803_resume); static const struct i2c_device_id rv8803_id[] = { - { "rv8803", rv_8803 }, - { "rv8804", rx_8804 }, - { "rx8803", rx_8803 }, - { "rx8900", rx_8900 }, + { .name = "rv8803", .driver_data = rv_8803 }, + { .name = "rv8804", .driver_data = rx_8804 }, + { .name = "rx8803", .driver_data = rx_8803 }, + { .name = "rx8900", .driver_data = rx_8900 }, { } }; MODULE_DEVICE_TABLE(i2c, rv8803_id); diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c index 07bf35ac8d79..1eacd470a27c 100644 --- a/drivers/rtc/rtc-rx6110.c +++ b/drivers/rtc/rtc-rx6110.c @@ -449,7 +449,7 @@ static const struct acpi_device_id rx6110_i2c_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rx6110_i2c_acpi_match); static const struct i2c_device_id rx6110_i2c_id[] = { - { "rx6110" }, + { .name = "rx6110" }, { } }; MODULE_DEVICE_TABLE(i2c, rx6110_i2c_id); diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c index 171240e50f48..ce3954132336 100644 --- a/drivers/rtc/rtc-rx8010.c +++ b/drivers/rtc/rtc-rx8010.c @@ -50,7 +50,7 @@ #define RX8010_ALARM_AE BIT(7) static const struct i2c_device_id rx8010_id[] = { - { "rx8010" }, + { .name = "rx8010" }, { } }; MODULE_DEVICE_TABLE(i2c, rx8010_id); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index c57081f9e02b..5eeaa929f3ef 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -71,8 +71,8 @@ enum rx_model { }; static const struct i2c_device_id rx8025_id[] = { - { "rx8025", model_rx_8025 }, - { "rx8035", model_rx_8035 }, + { .name = "rx8025", .driver_data = model_rx_8025 }, + { .name = "rx8035", .driver_data = model_rx_8035 }, { } }; MODULE_DEVICE_TABLE(i2c, rx8025_id); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index 20c2dff01bae..cf4dab94c337 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -294,7 +294,7 @@ static int rx8581_probe(struct i2c_client *client) } static const struct i2c_device_id rx8581_id[] = { - { "rx8581" }, + { .name = "rx8581" }, { } }; MODULE_DEVICE_TABLE(i2c, rx8581_id); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index a4678d7c6cf6..31394f34fb70 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -51,7 +51,7 @@ #define S35390A_INT2_MODE_PMIN (BIT(3) | BIT(2)) /* INT2FE | INT2ME */ static const struct i2c_device_id s35390a_id[] = { - { "s35390a" }, + { .name = "s35390a" }, { } }; MODULE_DEVICE_TABLE(i2c, s35390a_id); diff --git a/drivers/rtc/rtc-sd2405al.c b/drivers/rtc/rtc-sd2405al.c index 708ea5d964de..7982a910a537 100644 --- a/drivers/rtc/rtc-sd2405al.c +++ b/drivers/rtc/rtc-sd2405al.c @@ -202,7 +202,7 @@ static int sd2405al_probe(struct i2c_client *client) } static const struct i2c_device_id sd2405al_id[] = { - { "sd2405al" }, + { .name = "sd2405al" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, sd2405al_id); diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 10cc1dcfc774..2c61f0e204a4 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -186,7 +186,7 @@ static int sd3078_probe(struct i2c_client *client) } static const struct i2c_device_id sd3078_id[] = { - { "sd3078" }, + { .name = "sd3078" }, { } }; MODULE_DEVICE_TABLE(i2c, sd3078_id); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index b8a0fccef14e..bb1a5c86f621 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -663,7 +663,7 @@ static void x1205_remove(struct i2c_client *client) } static const struct i2c_device_id x1205_id[] = { - { "x1205" }, + { .name = "x1205" }, { } }; MODULE_DEVICE_TABLE(i2c, x1205_id); From ba5dca876b54c848fe4ba6526454a6eed7856f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 08:48:10 +0200 Subject: [PATCH 4321/5214] rtc: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two drivers explicitly set the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignments. While touching these array unify spacing, usage of commas and use named initializers for .name. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Tzung-Bi Shih Link: https://patch.msgid.link/9ec7a174605a17dd19c011ee2253de28d09b02bd.1779950275.git.u.kleine-koenig@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-cros-ec.c | 4 ++-- drivers/rtc/rtc-max8997.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index e956505a06fb..f3ecd017e2f7 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -388,8 +388,8 @@ static void cros_ec_rtc_remove(struct platform_device *pdev) } static const struct platform_device_id cros_ec_rtc_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id); diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c index e7618d715bd8..89203c92e2cd 100644 --- a/drivers/rtc/rtc-max8997.c +++ b/drivers/rtc/rtc-max8997.c @@ -512,8 +512,8 @@ static void max8997_rtc_shutdown(struct platform_device *pdev) } static const struct platform_device_id rtc_id[] = { - { "max8997-rtc", 0 }, - {}, + { .name = "max8997-rtc" }, + { } }; MODULE_DEVICE_TABLE(platform, rtc_id); From 6e2f1f0184dad775c95c09e5374234d3df39de65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 08:48:11 +0200 Subject: [PATCH 4322/5214] rtc: ab8500: Simplify driver_data handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of hiding the rtc ops for the only supported device behind an abstraction for multi-device support, hardcode the used ops which gets rid of the need to call platform_get_device_id and two casts. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/a909d3c59d00756130ac16051ceedbec0ce9cec7.1779950275.git.u.kleine-koenig@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ab8500.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index c6147837f957..0978bd0a3393 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -323,14 +323,13 @@ static const struct rtc_class_ops ab8500_rtc_ops = { }; static const struct platform_device_id ab85xx_rtc_ids[] = { - { "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, }, + { .name = "ab8500-rtc" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, ab85xx_rtc_ids); static int ab8500_rtc_probe(struct platform_device *pdev) { - const struct platform_device_id *platid = platform_get_device_id(pdev); int err; struct rtc_device *rtc; u8 rtc_ctrl; @@ -366,7 +365,7 @@ static int ab8500_rtc_probe(struct platform_device *pdev) if (IS_ERR(rtc)) return PTR_ERR(rtc); - rtc->ops = (struct rtc_class_ops *)platid->driver_data; + rtc->ops = &ab8500_rtc_ops; err = devm_request_threaded_irq(&pdev->dev, irq, NULL, rtc_alarm_handler, IRQF_ONESHOT, From 041ca8884410a4f70b40521f34258c7b773ea5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 28 May 2026 08:48:12 +0200 Subject: [PATCH 4323/5214] rtc: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Matti Vaittinen Reviewed-by: Linus Walleij Acked-by: Karel Balej # for Marvell 88PM886 Link: https://patch.msgid.link/d14b9076b2c7703708bcc5cc35f339cd97fc10cd.1779950275.git.u.kleine-koenig@baylibre.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-88pm886.c | 2 +- drivers/rtc/rtc-bd70528.c | 8 ++++---- drivers/rtc/rtc-max77686.c | 10 +++++----- drivers/rtc/rtc-max8998.c | 4 ++-- drivers/rtc/rtc-s5m.c | 12 ++++++------ drivers/rtc/rtc-tps6594.c | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/rtc/rtc-88pm886.c b/drivers/rtc/rtc-88pm886.c index 57e9b0a66eed..13aa3ae82239 100644 --- a/drivers/rtc/rtc-88pm886.c +++ b/drivers/rtc/rtc-88pm886.c @@ -78,7 +78,7 @@ static int pm886_rtc_probe(struct platform_device *pdev) } static const struct platform_device_id pm886_rtc_id_table[] = { - { "88pm886-rtc", }, + { .name = "88pm886-rtc" }, { } }; MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table); diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c index 4c8599761b2e..482810b61495 100644 --- a/drivers/rtc/rtc-bd70528.c +++ b/drivers/rtc/rtc-bd70528.c @@ -341,10 +341,10 @@ static int bd70528_probe(struct platform_device *pdev) } static const struct platform_device_id bd718x7_rtc_id[] = { - { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 }, - { "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 }, - { "bd72720-rtc", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71828-rtc", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd71815-rtc", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd72720-rtc", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 3cdfd78a07cc..375565a3bddf 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -866,11 +866,11 @@ static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, max77686_rtc_suspend, max77686_rtc_resume); static const struct platform_device_id rtc_id[] = { - { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, - { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, - { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, }, - { "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data, }, - {}, + { .name = "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data }, + { .name = "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data }, + { .name = "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data }, + { .name = "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data }, + { } }; MODULE_DEVICE_TABLE(platform, rtc_id); diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index c873b4509b3c..a2c946edcd1a 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -299,8 +299,8 @@ static int max8998_rtc_probe(struct platform_device *pdev) } static const struct platform_device_id max8998_rtc_id[] = { - { "max8998-rtc", TYPE_MAX8998 }, - { "lp3974-rtc", TYPE_LP3974 }, + { .name = "max8998-rtc", .driver_data = TYPE_MAX8998 }, + { .name = "lp3974-rtc", .driver_data = TYPE_LP3974 }, { } }; MODULE_DEVICE_TABLE(platform, max8998_rtc_id); diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index c6ed5a4ca8a0..aa706074ec3e 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -807,12 +807,12 @@ static int s5m_rtc_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); static const struct platform_device_id s5m_rtc_id[] = { - { "s5m-rtc", S5M8767X }, - { "s2mpg10-rtc", S2MPG10 }, - { "s2mps13-rtc", S2MPS13X }, - { "s2mps14-rtc", S2MPS14X }, - { "s2mps15-rtc", S2MPS15X }, - { }, + { .name = "s5m-rtc", .driver_data = S5M8767X }, + { .name = "s2mpg10-rtc", .driver_data = S2MPG10 }, + { .name = "s2mps13-rtc", .driver_data = S2MPS13X }, + { .name = "s2mps14-rtc", .driver_data = S2MPS14X }, + { .name = "s2mps15-rtc", .driver_data = S2MPS15X }, + { } }; MODULE_DEVICE_TABLE(platform, s5m_rtc_id); diff --git a/drivers/rtc/rtc-tps6594.c b/drivers/rtc/rtc-tps6594.c index 7c6246e3f029..2cebd54c2dbf 100644 --- a/drivers/rtc/rtc-tps6594.c +++ b/drivers/rtc/rtc-tps6594.c @@ -485,8 +485,8 @@ static int tps6594_rtc_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_rtc_pm_ops, tps6594_rtc_suspend, tps6594_rtc_resume); static const struct platform_device_id tps6594_rtc_id_table[] = { - { "tps6594-rtc", }, - {} + { .name = "tps6594-rtc" }, + { } }; MODULE_DEVICE_TABLE(platform, tps6594_rtc_id_table); From d1d53aa30ab3b5ae89161c9cc840b3f7489ad386 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Thu, 18 Jun 2026 01:50:26 +0800 Subject: [PATCH 4324/5214] bpf: Fix stack slot index in nospec checks check_stack_write_fixed_off() computes the byte slot for a fixed-offset stack write as -off - 1, and records each written byte in slot_type[] with (slot - i) % BPF_REG_SIZE. The Spectre v4 sanitization pre-check uses slot_type[i] instead. For a 4-byte write at fp-8 after the lower half of fp-8 has been zeroed, the pre-check scans bytes 0..3 and sees STACK_ZERO while the actual write updates bytes 7..4. That can leave the second half-slot write without nospec_result even though the bytes being overwritten still require sanitization. Use the same slot index in the sanitization pre-check that the write path uses when updating slot_type[]. Fixes: 2039f26f3aca ("bpf: Fix leakage due to insufficient speculative store bypass mitigation") Acked-by: Luis Gerhorst Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260618-f01-11-stack-nospec-slot-index-v3-1-780297041721@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 2abc79dbf281..50e80dbbc178 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3479,7 +3479,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, bool sanitize = reg && is_spillable_regtype(reg->type); for (i = 0; i < size; i++) { - u8 type = state->stack[spi].slot_type[i]; + u8 type = state->stack[spi].slot_type[(slot - i) % + BPF_REG_SIZE]; if (type != STACK_MISC && type != STACK_ZERO) { sanitize = true; From a93ae7ed5972fa4cf7c0244395dc1bcc7e0016be Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Thu, 18 Jun 2026 01:50:27 +0800 Subject: [PATCH 4325/5214] selftests/bpf: Cover stack nospec slot indexing Add a verifier test for the fixed-offset stack write case where two 4-byte stores initialize opposite halves of the same stack slot. The test runs through the unprivileged loader lane and expects both half-slot writes to emit nospec in the translated program. Acked-by: Luis Gerhorst Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260618-f01-11-stack-nospec-slot-index-v3-2-780297041721@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/verifier_unpriv.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c index c16f8382cf17..49f7bd05edad 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -976,4 +976,26 @@ l0_%=: exit; \ : __clobber_all); } +SEC("socket") +__description("unpriv: Spectre v4 stack write slot index") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V4 +__xlated_unpriv("r0 = 0") +__xlated_unpriv("*(u32 *)(r10 -4) = r0") +__xlated_unpriv("nospec") +__xlated_unpriv("*(u32 *)(r10 -8) = r0") +__xlated_unpriv("nospec") +__xlated_unpriv("exit") +#endif +__naked void stack_write_nospec_slot_index(void) +{ + asm volatile (" \ + r0 = 0; \ + *(u32 *)(r10 - 4) = r0; \ + *(u32 *)(r10 - 8) = r0; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; From b5f3534268e3f91c9d3e9dc79ee5a32555880ee9 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Wed, 17 Jun 2026 17:35:56 +0800 Subject: [PATCH 4326/5214] bpf: Fix partial copy of non-linear test_run output For non-linear test_run output, bpf_test_finish() derives the linear data copy length from copy_size - frag_size. This only matches the linear data length when copy_size is the full packet size. When userspace provides a short data_out buffer, copy_size is clamped to that buffer size. If copy_size is smaller than frag_size, the computed length becomes negative and bpf_test_finish() returns -ENOSPC before copying the packet prefix or updating data_size_out. Compute the linear data length from the packet layout instead, and clamp the linear copy length to copy_size. This preserves the expected partial-copy semantics: return -ENOSPC, copy the packet prefix that fits in data_out, and report the full packet length through data_size_out. Fixes: 7855e0db150ad ("bpf: test_run: add xdp_shared_info pointer in bpf_test_finish signature") Signed-off-by: Sun Jian Acked-by: Paul Chaignon Link: https://lore.kernel.org/r/20260617093557.63880-2-sun.jian.kdev@gmail.com Signed-off-by: Alexei Starovoitov --- net/bpf/test_run.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 7fdee8f52ee2..5d51f6cb7d15 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -452,12 +452,8 @@ static int bpf_test_finish(const union bpf_attr *kattr, } if (data_out) { - int len = sinfo ? copy_size - frag_size : copy_size; - - if (len < 0) { - err = -ENOSPC; - goto out; - } + u32 head_len = size - frag_size; + u32 len = min(copy_size, head_len); if (copy_to_user(data_out, data, len)) goto out; From 5e72b5b157299f703d0c08c543e68916d263b4a4 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Mon, 15 Jun 2026 12:55:36 -0700 Subject: [PATCH 4327/5214] bpf: Fix build_id caching in stack_map_get_build_id_offset() This patch is a follow up to recent implementation of stack_map_get_build_id_offset_sleepable() [1]. stack_map_get_build_id_offset() and its sleepable variant each cached only the last successfully resolved VMA, with separate bookkeeping in each function. A run of IPs in a VMA with no usable build ID will repeat the lookup for every frame: find_vma() in the non-sleepable path, a VMA lock and a blocking build_id_parse_file() in the sleepable. Factor the per-call cache into a shared struct stack_map_build_id_cache with two independent slots [2][3], used by both functions: * resolved - last VMA that produced a build ID (file, build_id and range), reused to skip the lookup and the parse; * unresolved - last VMA with no usable build ID (range only), reused to emit a raw IP without another lookup or parse. Keeping the slots independent means a build-ID-less VMA no longer evicts the last resolved build ID, so a trace alternating between a binary and a region without one stops re-resolving the binary on every return. The shared lookup tests [vm_start, vm_end), matching the sleepable path; the non-sleepable path previously reused a build ID for ip == vm_end (range_in_vma() is inclusive) and now re-resolves it correctly. [1] https://lore.kernel.org/bpf/20260525223948.1920986-1-ihor.solodrai@linux.dev/ [2] https://lore.kernel.org/bpf/CAEf4Bza2fRDGhLQoPE-EzM7F34xaEJfi5Exmxb-iWVUN3F06=g@mail.gmail.com/ [3] https://lore.kernel.org/bpf/CAEf4BzZXJFr=1iiVx937ht=4PYQkQHg=eFk810zhMDzXQG3ihw@mail.gmail.com/ Suggested-by: Andrii Nakryiko Signed-off-by: Ihor Solodrai Link: https://lore.kernel.org/r/20260615195536.1065107-1-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov --- kernel/bpf/stackmap.c | 183 ++++++++++++++++++++++++++++++------------ 1 file changed, 130 insertions(+), 53 deletions(-) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 77ba03216c09..41fe87d7302f 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -175,6 +175,95 @@ static inline void stack_map_build_id_set_valid(struct bpf_stack_build_id *id, memcpy(id->build_id, build_id, BUILD_ID_SIZE_MAX); } +/* + * A cached VMA lookup result. The range [vm_start, vm_end) is always set. + * vm_pgoff, file, build_id are set only when the build ID was resolved. + * Zero vm_end marks the slot empty. build_id aliases the id_offs[] entry. + */ +struct stack_map_cached_vma { + unsigned long vm_start; + unsigned long vm_end; + unsigned long vm_pgoff; + struct file *file; /* pinned in the sleepable path; NULL otherwise */ + const unsigned char *build_id; +}; + +/* + * Per stack_map_get_build_id_offset() call cache of the last VMA with a build ID + * resolved and the last VMA with no usable build ID. Adjacent stack frames tend + * to land in the same VMA or the same backing file, so caching the last result + * of each kind lets us skip unnecessary VMA lookups and build ID parse calls. + * Keeping the two slots independent means a build-ID-less VMA doesn't evict the + * last resolved build ID. + */ +struct stack_map_build_id_cache { + struct stack_map_cached_vma resolved; + struct stack_map_cached_vma unresolved; +}; + +/* + * Fill @id from a cached range covering @ip. On a hit this writes @id (resolved + * range -> build ID + offset, unresolved range -> raw ip) and returns 0; on a + * miss it leaves @id untouched and returns -ENOENT. + */ +static int stack_map_build_id_set_from_cache(struct stack_map_build_id_cache *cache, + struct bpf_stack_build_id *id, u64 ip) +{ + unsigned long vm_start, vm_end, vm_pgoff; + u64 offset; + + vm_start = cache->resolved.vm_start; + vm_end = cache->resolved.vm_end; + if (vm_end && ip >= vm_start && ip < vm_end) { + vm_pgoff = cache->resolved.vm_pgoff; + offset = stack_map_build_id_offset(vm_pgoff, vm_start, ip); + stack_map_build_id_set_valid(id, offset, cache->resolved.build_id); + return 0; + } + + vm_start = cache->unresolved.vm_start; + vm_end = cache->unresolved.vm_end; + if (vm_end && ip >= vm_start && ip < vm_end) { + stack_map_build_id_set_ip(id); + return 0; + } + + return -ENOENT; +} + +/* + * Record @vma's build ID as the last resolved one. @file is the pinned backing + * file in the sleepable path (released when evicted), or NULL otherwise. + */ +static void stack_map_build_id_cache_set_resolved(struct stack_map_build_id_cache *cache, + struct file *file, + const unsigned char *build_id, + unsigned long vm_start, + unsigned long vm_end, + unsigned long vm_pgoff) +{ + if (cache->resolved.file) + fput(cache->resolved.file); + cache->resolved = (struct stack_map_cached_vma){ + .vm_start = vm_start, + .vm_end = vm_end, + .vm_pgoff = vm_pgoff, + .file = file, + .build_id = build_id, + }; +} + +/* Record [vm_start, vm_end) as a range with no usable build ID. */ +static void stack_map_build_id_cache_set_unresolved(struct stack_map_build_id_cache *cache, + unsigned long vm_start, + unsigned long vm_end) +{ + cache->unresolved = (struct stack_map_cached_vma){ + .vm_start = vm_start, + .vm_end = vm_end, + }; +} + struct stack_map_vma_lock { struct vm_area_struct *vma; struct mm_struct *mm; @@ -244,15 +333,9 @@ static void stack_map_unlock_vma(struct stack_map_vma_lock *lock) static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *id_offs, u32 trace_nr) { - struct mm_struct *mm = current->mm; - struct stack_map_vma_lock lock = { .mm = mm }; - struct { - struct file *file; - const unsigned char *build_id; - unsigned long vm_start; - unsigned long vm_end; - unsigned long vm_pgoff; - } cache = {}; + struct stack_map_vma_lock lock = { .mm = current->mm }; + struct stack_map_build_id_cache cache = {}; + struct stack_map_cached_vma *res = &cache.resolved; unsigned long vm_pgoff, vm_start, vm_end; struct vm_area_struct *vma; struct file *file; @@ -262,44 +345,39 @@ static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *i for (u32 i = 0; i < trace_nr; i++) { ip = READ_ONCE(id_offs[i].ip); - /* - * Range cache fast path: if ip falls within the previously - * resolved VMA range, reuse the cache build_id without - * re-acquiring the VMA lock. - */ - if (cache.build_id && ip >= cache.vm_start && ip < cache.vm_end) { - offset = stack_map_build_id_offset(cache.vm_pgoff, cache.vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, cache.build_id); + if (!stack_map_build_id_set_from_cache(&cache, &id_offs[i], ip)) continue; - } vma = stack_map_lock_vma(&lock, ip); if (!vma) { stack_map_build_id_set_ip(&id_offs[i]); continue; } + + vm_pgoff = vma->vm_pgoff; + vm_start = vma->vm_start; + vm_end = vma->vm_end; + if (vma_is_anonymous(vma) || !vma->vm_file) { - stack_map_build_id_set_ip(&id_offs[i]); stack_map_unlock_vma(&lock); + stack_map_build_id_set_ip(&id_offs[i]); + stack_map_build_id_cache_set_unresolved(&cache, vm_start, vm_end); continue; } file = vma->vm_file; - vm_pgoff = vma->vm_pgoff; - vm_start = vma->vm_start; - vm_end = vma->vm_end; offset = stack_map_build_id_offset(vm_pgoff, vm_start, ip); /* - * Same backing file as previous (e.g. different VMAs - * of the same ELF binary). Reuse the cache build_id. + * Same backing file as the last resolved VMA (another mapping + * of the same ELF binary): reuse its build_id without re-parsing. */ - if (file == cache.file) { + if (file == res->file) { stack_map_unlock_vma(&lock); - stack_map_build_id_set_valid(&id_offs[i], offset, cache.build_id); - cache.vm_start = vm_start; - cache.vm_end = vm_end; - cache.vm_pgoff = vm_pgoff; + stack_map_build_id_set_valid(&id_offs[i], offset, res->build_id); + res->vm_start = vm_start; + res->vm_end = vm_end; + res->vm_pgoff = vm_pgoff; continue; } @@ -310,21 +388,17 @@ static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *i if (build_id_parse_file(file, id_offs[i].build_id, NULL)) { stack_map_build_id_set_ip(&id_offs[i]); fput(file); + stack_map_build_id_cache_set_unresolved(&cache, vm_start, vm_end); continue; } stack_map_build_id_set_valid(&id_offs[i], offset, id_offs[i].build_id); - if (cache.file) - fput(cache.file); - cache.file = file; - cache.build_id = id_offs[i].build_id; - cache.vm_start = vm_start; - cache.vm_end = vm_end; - cache.vm_pgoff = vm_pgoff; + stack_map_build_id_cache_set_resolved(&cache, file, id_offs[i].build_id, + vm_start, vm_end, vm_pgoff); } - if (cache.file) - fput(cache.file); + if (res->file) + fput(res->file); } /* @@ -343,8 +417,8 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, struct mmap_unlock_irq_work *work = NULL; bool irq_work_busy = bpf_mmap_unlock_get_irq_work(&work); bool has_user_ctx = user && current && current->mm; - struct vm_area_struct *vma, *prev_vma = NULL; - const unsigned char *prev_build_id = NULL; + struct stack_map_build_id_cache cache = {}; + struct vm_area_struct *vma; int i; if (may_fault && has_user_ctx) { @@ -365,27 +439,30 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, for (i = 0; i < trace_nr; i++) { u64 ip = READ_ONCE(id_offs[i].ip); - u64 offset; - if (prev_build_id && range_in_vma(prev_vma, ip, ip)) { - vma = prev_vma; - offset = stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, prev_build_id); + if (!stack_map_build_id_set_from_cache(&cache, &id_offs[i], ip)) continue; - } + vma = find_vma(current->mm, ip); if (!vma || vma_is_anonymous(vma) || fetch_build_id(vma, id_offs[i].build_id, may_fault)) { - /* per entry fall back to ips */ + /* per entry fall back to ips; cache build-ID-less range */ stack_map_build_id_set_ip(&id_offs[i]); - prev_vma = vma; - prev_build_id = NULL; + if (vma) + stack_map_build_id_cache_set_unresolved(&cache, + vma->vm_start, vma->vm_end); continue; } - offset = stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, id_offs[i].build_id); - prev_vma = vma; - prev_build_id = id_offs[i].build_id; + /* + * mmap_lock is held for the whole loop, so the cached VMA + * fields stay valid; no file pinning is needed here. + */ + stack_map_build_id_set_valid(&id_offs[i], + stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip), + id_offs[i].build_id); + stack_map_build_id_cache_set_resolved(&cache, NULL, id_offs[i].build_id, + vma->vm_start, vma->vm_end, + vma->vm_pgoff); } bpf_mmap_unlock_mm(work, current->mm); } From a933bade82b9cd9197c6c9a390623cfb1f8c0da7 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 15 Jun 2026 16:21:46 -0700 Subject: [PATCH 4328/5214] bpf: Emit verbose message when prog-specific btf_struct_access rejects a write When BPF_WRITE goes through a PTR_TO_BTF_ID register, check_ptr_to_btf_access() delegates to env->ops->btf_struct_access(). Most implementations (bpf_scx_btf_struct_access, tc_cls_act_btf_struct_access, etc.) return -EACCES for disallowed fields without logging anything, so the verifier rejects the program with an empty message. For example a scx program doing 1: R1=trusted_ptr_task_struct() ... 4: (7b) *(u64 *)(r1 +0) = r2 verification time 83 usec the program is rejected leaves the user guessing which field is off-limits. Emit verbose message. Signed-off-by: Alexei Starovoitov Reviewed-by: Emil Tsalapatis Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260615232146.5491-1-alexei.starovoitov@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 50e80dbbc178..a2b348f98080 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5787,6 +5787,10 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, return -EFAULT; } ret = env->ops->btf_struct_access(&env->log, reg, off, size); + if (ret < 0) + verbose(env, + "%s cannot write into ptr_%s at off=%d size=%d\n", + reg_arg_name(env, argno), tname, off, size); } else { /* Writes are permitted with default btf_struct_access for * program allocated objects (which always have id > 0), From 39799c63578ec64488e14aced9ea07af6f958f35 Mon Sep 17 00:00:00 2001 From: Emil Tsalapatis Date: Tue, 16 Jun 2026 02:14:54 -0400 Subject: [PATCH 4329/5214] bpf: Allow type tag BTF records to succeed other modifier records llvm commit [1] allowed attaching type tag records to modifier BTF records. This is useful for using typedefs that encompass a base type and a type tag, e.g.: typedef struct rbtree __arena rbtree_t; Modify btf_check_type_tags() so that it allows this sequence of records. The function now only checks for record loops in BTF modifier record chains. Rename to btf_check_modifier_chain_length to reflect this. Also expand the BTF modifier traversal code to take into account that type record can be interleaved with other modifier records. In effect this means traversing all modifiers to collect the type tags. Also modify existing selftests to now accept modifier records (const, typedef) that point to type tag records. [1] https://github.com/llvm/llvm-project/pull/203089 Signed-off-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260616061454.7869-1-emil@etsalapatis.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/btf.c | 209 +++++++++++-------- tools/testing/selftests/bpf/prog_tests/btf.c | 12 +- 2 files changed, 125 insertions(+), 96 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 15ae7c43f594..64572f85edc8 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -3472,12 +3473,69 @@ static int btf_find_struct(const struct btf *btf, const struct btf_type *t, return BTF_FIELD_FOUND; } +struct btf_type_tag_match { + const char *name; + u32 flag; +}; + +struct btf_type_tag_walk_ctx { + const struct btf_type *t; /* Input/Output */ + u32 id; /* Output */ + u32 res; /* Output */ +}; + +static int btf_type_tag_walk(const struct btf *btf, + struct btf_type_tag_walk_ctx *ctx, + const struct btf_type_tag_match *matches, + u32 match_cnt) +{ + const struct btf_type *t = ctx->t; + u32 res = 0; + const char *tag; + u32 id, i; + + do { + id = t->type; + t = btf_type_by_id(btf, id); + + if (!btf_type_is_modifier(t)) + break; + + if (!btf_type_is_type_tag(t) || btf_type_kflag(t)) + continue; + + tag = __btf_name_by_offset(btf, t->name_off); + for (i = 0; i < match_cnt; i++) { + if (strcmp(tag, matches[i].name)) + continue; + res |= matches[i].flag; + break; + } + } while (true); + + /* We only support a single tag. */ + if (hweight32(res) > 1) + return -EINVAL; + + ctx->t = t; + ctx->id = id; + ctx->res = res; + + return 0; +} + static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, u32 off, int sz, struct btf_field_info *info, u32 field_mask) { - enum btf_field_type type; - const char *tag_value; - bool is_type_tag; + static const struct btf_type_tag_match kptr_type_tags[] = { + { "kptr_untrusted", BPF_KPTR_UNREF }, + { "kptr", BPF_KPTR_REF }, + { "percpu_kptr", BPF_KPTR_PERCPU }, + { "uptr", BPF_UPTR }, + }; + struct btf_type_tag_walk_ctx ctx; + enum btf_field_type type = 0; + int err; u32 res_id; /* Permit modifiers on the pointer itself */ @@ -3486,30 +3544,20 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, /* For PTR, sz is always == 8 */ if (!btf_type_is_ptr(t)) return BTF_FIELD_IGNORE; - t = btf_type_by_id(btf, t->type); - is_type_tag = btf_type_is_type_tag(t) && !btf_type_kflag(t); - if (!is_type_tag) - return BTF_FIELD_IGNORE; - /* Reject extra tags */ - if (btf_type_is_type_tag(btf_type_by_id(btf, t->type))) - return -EINVAL; - tag_value = __btf_name_by_offset(btf, t->name_off); - if (!strcmp("kptr_untrusted", tag_value)) - type = BPF_KPTR_UNREF; - else if (!strcmp("kptr", tag_value)) - type = BPF_KPTR_REF; - else if (!strcmp("percpu_kptr", tag_value)) - type = BPF_KPTR_PERCPU; - else if (!strcmp("uptr", tag_value)) - type = BPF_UPTR; - else - return -EINVAL; + + ctx.t = t; + err = btf_type_tag_walk(btf, &ctx, kptr_type_tags, + ARRAY_SIZE(kptr_type_tags)); + if (err) + return err; + + t = ctx.t; + res_id = ctx.id; + type = ctx.res; if (!(type & field_mask)) return BTF_FIELD_IGNORE; - /* Get the base type */ - t = btf_type_skip_modifiers(btf, t->type, &res_id); /* Only pointer to struct is allowed */ if (!__btf_type_is_struct(t)) return -EINVAL; @@ -5859,11 +5907,10 @@ struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id) return bsearch(&btf_id, tab->types, tab->cnt, sizeof(tab->types[0]), btf_id_cmp_func); } -static int btf_check_type_tags(struct btf_verifier_env *env, - struct btf *btf, int start_id) +static int btf_check_modifier_chain_length(struct btf_verifier_env *env, + struct btf *btf, int start_id) { int i, n, good_id = start_id - 1; - bool in_tags; n = btf_nr_types(btf); for (i = start_id; i < n; i++) { @@ -5879,20 +5926,12 @@ static int btf_check_type_tags(struct btf_verifier_env *env, cond_resched(); - in_tags = btf_type_is_type_tag(t); while (btf_type_is_modifier(t)) { if (!chain_limit--) { btf_verifier_log(env, "Max chain length or cycle detected"); return -ELOOP; } - if (btf_type_is_type_tag(t)) { - if (!in_tags) { - btf_verifier_log(env, "Type tags don't precede modifiers"); - return -EINVAL; - } - } else if (in_tags) { - in_tags = false; - } + if (cur_id <= good_id) break; /* Move to next type */ @@ -5970,7 +6009,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, if (err) goto errout; - err = btf_check_type_tags(env, btf, 1); + err = btf_check_modifier_chain_length(env, btf, 1); if (err) goto errout; @@ -6378,7 +6417,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name if (err) goto errout; - err = btf_check_type_tags(env, btf, 1); + err = btf_check_modifier_chain_length(env, btf, 1); if (err) goto errout; @@ -6504,7 +6543,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, if (err) goto errout; - err = btf_check_type_tags(env, btf, btf_nr_types(base_btf)); + err = btf_check_modifier_chain_length(env, btf, btf_nr_types(base_btf)); if (err) goto errout; @@ -6810,14 +6849,18 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { + static const struct btf_type_tag_match ctx_type_tags[] = { + { "user", MEM_USER }, + { "percpu", MEM_PERCPU }, + }; const struct btf_type *t = prog->aux->attach_func_proto; struct bpf_prog *tgt_prog = prog->aux->dst_prog; struct btf *btf = bpf_prog_get_target_btf(prog); const char *tname = prog->aux->attach_func_name; struct bpf_verifier_log *log = info->log; + struct btf_type_tag_walk_ctx ctx; const struct btf_param *args; bool ptr_err_raw_tp = false; - const char *tag_value; u32 nr_args, arg; int i, ret; @@ -7020,22 +7063,18 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, } info->btf = btf; - info->btf_id = t->type; - t = btf_type_by_id(btf, t->type); - - if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) { - tag_value = __btf_name_by_offset(btf, t->name_off); - if (strcmp(tag_value, "user") == 0) - info->reg_type |= MEM_USER; - if (strcmp(tag_value, "percpu") == 0) - info->reg_type |= MEM_PERCPU; + ctx.t = t; + ret = btf_type_tag_walk(btf, &ctx, ctx_type_tags, + ARRAY_SIZE(ctx_type_tags)); + if (ret) { + bpf_log(log, "func '%s' arg%d type %s has multiple type tags\n", + tname, arg, btf_type_str(t)); + return false; } + info->reg_type |= ctx.res; + info->btf_id = ctx.id; + t = ctx.t; - /* skip modifiers */ - while (btf_type_is_modifier(t)) { - info->btf_id = t->type; - t = btf_type_by_id(btf, t->type); - } if (!btf_type_is_struct(t)) { bpf_log(log, "func '%s' arg%d type %s is not a struct\n", @@ -7074,7 +7113,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; const struct btf_type *mtype, *elem_type = NULL; const struct btf_member *member; - const char *tname, *mname, *tag_value; + const char *tname, *mname; u32 vlen, elem_id, mid; again: @@ -7270,8 +7309,15 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, } if (btf_type_is_ptr(mtype)) { - const struct btf_type *stype, *t; + static const struct btf_type_tag_match walk_type_tags[] = { + { "user", MEM_USER }, + { "percpu", MEM_PERCPU }, + { "rcu", MEM_RCU }, + }; enum bpf_type_flag tmp_flag = 0; + struct btf_type_tag_walk_ctx ctx = { .t = mtype }; + const struct btf_type *stype; + int err; u32 id; if (msize != size || off != moff) { @@ -7281,22 +7327,17 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, return -EACCES; } - /* check type tag */ - t = btf_type_by_id(btf, mtype->type); - if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) { - tag_value = __btf_name_by_offset(btf, t->name_off); - /* check __user tag */ - if (strcmp(tag_value, "user") == 0) - tmp_flag = MEM_USER; - /* check __percpu tag */ - if (strcmp(tag_value, "percpu") == 0) - tmp_flag = MEM_PERCPU; - /* check __rcu tag */ - if (strcmp(tag_value, "rcu") == 0) - tmp_flag = MEM_RCU; + err = btf_type_tag_walk(btf, &ctx, walk_type_tags, + ARRAY_SIZE(walk_type_tags)); + if (err) { + bpf_log(log, "type '%s' has multiple type tags\n", + btf_type_str(mtype)); + return err; } + tmp_flag = ctx.res; + id = ctx.id; + stype = ctx.t; - stype = btf_type_skip_modifiers(btf, mtype->type, &id); if (btf_type_is_struct(stype)) { *next_btf_id = id; *flag |= tmp_flag; @@ -7867,7 +7908,12 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id, u32 *tags) { + static const struct btf_type_tag_match func_type_tags[] = { + { "arena", ARG_TAG_ARENA }, + }; + struct btf_type_tag_walk_ctx ctx; const struct btf_type *t; + int err; /* Find the first pointer type in the chain. */ t = btf_type_skip_modifiers(btf, type_id, NULL); @@ -7879,24 +7925,15 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, if (!t || !btf_type_is_ptr(t)) return 0; - /* We got a pointer, get all associated type tags. */ - for (t = btf_type_by_id(btf, t->type); t && btf_type_is_modifier(t); - t = btf_type_by_id(btf, t->type)) { - - /* Skip non-type tag modifiers. */ - if (!btf_type_is_type_tag(t)) - continue; - - const char *tag = __btf_name_by_offset(btf, t->name_off); - - if (strcmp(tag, "arena") == 0) { - *tags |= ARG_TAG_ARENA; - } else { - bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", - tag); - return -EOPNOTSUPP; - } + ctx.t = t; + err = btf_type_tag_walk(btf, &ctx, func_type_tags, + ARRAY_SIZE(func_type_tags)); + if (err) { + bpf_log(&env->log, + "function signature member has multiple type tags\n"); + return err; } + *tags |= ctx.res; return 0; } diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 96f719a0cec9..66855cbd6b73 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -4121,8 +4121,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #3, type tag order", @@ -4141,8 +4139,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #4, type tag order", @@ -4161,8 +4157,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #5, type tag order", @@ -4198,11 +4192,9 @@ static struct btf_raw_test raw_tests[] = { .map_name = "tag_type_check_btf", .key_size = sizeof(int), .value_size = 4, - .key_type_id = 1, - .value_type_id = 1, + .key_type_id = 4, + .value_type_id = 4, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #7, tag with kflag", From d5dc200c3a3f217de072af269dd90adddf90e48d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 16 Jun 2026 10:30:56 +0200 Subject: [PATCH 4330/5214] bpf: Add missing access_ok call to copy_user_syms As reported by sashiko we use __get_user without prior access_ok call on the user space pointer. Adding the missing call for the whole pointer array. Plus removing the err check in the error path, because it's not needed and also we can return -ENOMEM directly from the first kvmalloc_array fail path. Cc: stable@vger.kernel.org [1] https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Fixes: 0236fec57a15 ("bpf: Resolve symbols with ftrace_lookup_symbols for kprobe multi link") Reported-by: Sashiko Closes: https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Signed-off-by: Jiri Olsa Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260616083056.405652-1-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/trace/bpf_trace.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 82f8feea6931..75495a5c3507 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2376,9 +2376,12 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 int err = -ENOMEM; unsigned int i; + if (!access_ok(usyms, cnt * sizeof(*usyms))) + return -EFAULT; + syms = kvmalloc_array(cnt, sizeof(*syms), GFP_KERNEL); if (!syms) - goto error; + return -ENOMEM; buf = kvmalloc_array(cnt, KSYM_NAME_LEN, GFP_KERNEL); if (!buf) @@ -2403,10 +2406,8 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 return 0; error: - if (err) { - kvfree(syms); - kvfree(buf); - } + kvfree(syms); + kvfree(buf); return err; } From bda6a7308ef8e79cfbb7d09e48e1c7ffaa522269 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 17:01:17 +0800 Subject: [PATCH 4331/5214] bpftool: Fix vmlinux BTF leak in cgroup commands bpftool cgroup show and tree call libbpf_find_kernel_btf() to resolve attach_btf names, but never release the returned BTF object. For cgroup tree, do_show_tree_fn() is called once for each cgroup visited by nftw(). When more than one cgroup has attached programs, each callback overwrites btf_vmlinux with a new object and loses the previous allocation. Load vmlinux BTF only once during a tree walk and release it when cgroup show or tree completes. Reset btf_vmlinux_id at the same time so batch mode starts with clean state. Fixes: 596f5fb2ea2a ("bpftool: implement cgroup tree for BPF_LSM_CGROUP") Signed-off-by: Yichong Chen Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/24357C69B4405079+20260617090117.280222-1-chenyichong@uniontech.com Signed-off-by: Alexei Starovoitov --- tools/bpf/bpftool/cgroup.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index ec356deb27c9..ce69d1e5468e 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -78,6 +78,13 @@ static unsigned int query_flags; static struct btf *btf_vmlinux; static __u32 btf_vmlinux_id; +static void free_btf_vmlinux(void) +{ + btf__free(btf_vmlinux); + btf_vmlinux = NULL; + btf_vmlinux_id = 0; +} + static enum bpf_attach_type parse_attach_type(const char *str) { const char *attach_type_str; @@ -388,6 +395,8 @@ static int do_show(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); + exit_cgroup: close(cgroup_fd); exit: @@ -437,7 +446,9 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, printf("%s\n", fpath); } - btf_vmlinux = libbpf_find_kernel_btf(); + if (!btf_vmlinux) + btf_vmlinux = libbpf_find_kernel_btf(); + for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level); @@ -540,6 +551,7 @@ static int do_show_tree(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); free(cgroup_alloced); return ret; From 0dfcb68a6a5ac517b22dff6a1f01cb4f126dfc57 Mon Sep 17 00:00:00 2001 From: Avinash Duduskar Date: Thu, 18 Jun 2026 04:17:19 +0530 Subject: [PATCH 4332/5214] bpf: zero-initialize the fib lookup flow struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bpf_ipv4_fib_lookup() and bpf_ipv6_fib_lookup() build the flow key on the stack with a bare "struct flowi4 fl4;" / "struct flowi6 fl6;" and fill it field by field, but never set flowi4_l3mdev / flowi6_l3mdev. On the non-DIRECT path the lookup goes through the fib rules whenever the netns has custom rules, which a VRF installs: bpf_ipv4_fib_lookup() -> fib_lookup() -> __fib_lookup() -> l3mdev_update_flow() reads !fl->flowi_l3mdev -> fib_rules_lookup() -> fib_rule_match() -> l3mdev_fib_rule_match() uses fl->flowi_l3mdev l3mdev_update_flow() resolves the l3mdev master from the ingress device only while the field is still zero. Left at a nonzero stack value the resolution is skipped, and l3mdev_fib_rule_match() then tests that value as an ifindex, so the VRF master is not resolved and the rule fails to match: an ingress enslaved to a VRF can fail to select its table. FIB rules matching on an L3 master device (l3mdev_fib_rule_iif_match()/ _oif_match()) read the same value, so an "ip rule iif/oif " mismatches the same way. Zero-initialize the whole flow struct rather than adding one more field assignment, so any flowi field added later is covered too. ip_route_input_slow() likewise zeroes the field before its input lookup. CONFIG_INIT_STACK_ALL_ZERO masks this by default, but it depends on compiler support (CC_HAS_AUTO_VAR_INIT_ZERO), so INIT_STACK_NONE builds, including older toolchains that fall back to it, are exposed. Built with INIT_STACK_ALL_PATTERN, a plain bpf_fib_lookup (no VLAN, no DIRECT) over a VRF slave whose destination is routed only in the VRF table returns BPF_FIB_LKUP_RET_NOT_FWDED, and resolves with this patch. On the default config the lookup succeeds either way, so ordinary testing does not catch the bug. Fixes: 40867d74c374 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices") Signed-off-by: Avinash Duduskar Reviewed-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20260617224719.1428599-1-avinash.duduskar@gmail.com Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 2e96b4b847ce..1dd5e37ae130 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6221,7 +6221,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct in_device *in_dev; struct net_device *dev; struct fib_result res; - struct flowi4 fl4; + struct flowi4 fl4 = {}; u32 mtu = 0; int err; @@ -6361,7 +6361,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct neighbour *neigh; struct net_device *dev; struct inet6_dev *idev; - struct flowi6 fl6; + struct flowi6 fl6 = {}; int strict = 0; int oif, err; u32 mtu = 0; From 8405c4626460503027461652f96d8bb10c2a9173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Weksteen?= Date: Thu, 18 Jun 2026 14:09:33 +1000 Subject: [PATCH 4333/5214] bpf: Fix BPF_PROG_ASSOC_STRUCT_OPS last field check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When struct prog_assoc_struct_ops was added, BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD referenced prog_fd instead of the actual last field, flags. Fixes: b5709f6d26d6 ("bpf: Support associating BPF program with struct_ops") Signed-off-by: Thiébaud Weksteen Reviewed-by: Jakub Sitnicki Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260618040934.4113938-1-tweek@google.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b44106c8ea75..6db306d23b47 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -6308,7 +6308,7 @@ static int prog_stream_read(union bpf_attr *attr) return ret; } -#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.prog_fd +#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.flags static int prog_assoc_struct_ops(union bpf_attr *attr) { From f08aaee3152d0dfc578b3f2586932d82062701dd Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 18 Jun 2026 23:35:19 -0700 Subject: [PATCH 4334/5214] bpf: Fix effective prog array index with BPF_F_PREORDER replace_effective_prog() and purge_effective_progs() located the slot in the effective array by walking the program hlist and counting entries linearly. That count does not match the array layout: compute_effective_ progs() places BPF_F_PREORDER programs at the front (ancestor cgroup first, attach order within a cgroup) and the rest after them (descendant cgroup first). So when a preorder program is present, the linear hlist position no longer equals the program's index in the effective array. For replace_effective_prog() (bpf_link_update()) this overwrote the wrong slot, corrupting the effective order. For purge_effective_progs(), it could dummy out a slot belonging to a different program and leave the detached program in the array while bpf_prog_put() drops its reference, i.e. a use-after-free. Fix both by replaying compute_effective_progs()'s placement (including the per-cgroup preorder reversal) in a shared effective_prog_pos() helper. Identify the entry by its struct bpf_prog_list pointer rather than by (prog, link) value, so the lookup resolves to exactly the attachment the syscall selected even when the same bpf_prog is attached to several cgroups in the hierarchy. Fixes: 4b82b181a26c ("bpf: Allow pre-ordering for bpf cgroup progs") Signed-off-by: Amery Hung Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260619063520.2690547-2-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/cgroup.c | 108 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 83ce66296ac1..4355ccb78a9c 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -939,19 +939,65 @@ static int cgroup_bpf_attach(struct cgroup *cgrp, return ret; } +static int effective_prog_pos(struct cgroup *cgrp, + enum cgroup_bpf_attach_type atype, + struct bpf_prog_list *target_pl) +{ + int cnt = 0, preorder_cnt = 0, fstart, bstart, init_bstart, pos = -1; + struct bpf_prog_list *pl; + struct cgroup *p = cgrp; + + /* count effective programs to find where the preorder region ends */ + do { + if (cnt == 0 || (p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + cnt += prog_list_length(&p->bpf.progs[atype], &preorder_cnt); + p = cgroup_parent(p); + } while (p); + + /* replay compute_effective_progs() placement and record target's slot */ + cnt = 0; + p = cgrp; + fstart = preorder_cnt; + bstart = preorder_cnt - 1; + do { + if (cnt > 0 && !(p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + continue; + + init_bstart = bstart; + hlist_for_each_entry(pl, &p->bpf.progs[atype], node) { + if (!prog_list_prog(pl)) + continue; + + if (pl->flags & BPF_F_PREORDER) { + if (pl == target_pl) + pos = bstart; + bstart--; + } else { + if (pl == target_pl) + pos = fstart; + fstart++; + } + cnt++; + } + + /* reverse pre-ordering progs at this cgroup level */ + if (pos >= bstart + 1 && pos <= init_bstart) + pos = bstart + 1 + init_bstart - pos; + } while ((p = cgroup_parent(p))); + + return pos; +} + /* Swap updated BPF program for given link in effective program arrays across * all descendant cgroups. This function is guaranteed to succeed. */ static void replace_effective_prog(struct cgroup *cgrp, enum cgroup_bpf_attach_type atype, - struct bpf_cgroup_link *link) + struct bpf_prog_list *pl) { struct bpf_prog_array_item *item; struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; css_for_each_descendant_pre(css, &cgrp->self) { @@ -960,27 +1006,15 @@ static void replace_effective_prog(struct cgroup *cgrp, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; + pos = effective_prog_pos(desc, atype, pl); + if (WARN_ON_ONCE(pos < 0)) + continue; - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->link == link) - goto found; - pos++; - } - } -found: - BUG_ON(!cg); progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); item = &progs->items[pos]; - WRITE_ONCE(item->prog, link->link.prog); + WRITE_ONCE(item->prog, pl->link->link.prog); } } @@ -1024,7 +1058,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp, cgrp->bpf.revisions[atype] += 1; old_prog = xchg(&link->link.prog, new_prog); - replace_effective_prog(cgrp, atype, link); + replace_effective_prog(cgrp, atype, pl); bpf_prog_put(old_prog); return 0; } @@ -1091,19 +1125,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs, * recomputing the array in place. * * @cgrp: The cgroup which descendants to travers - * @prog: A program to detach or NULL - * @link: A link to detach or NULL + * @pl: The prog_list entry being detached * @atype: Type of detach operation */ -static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, - struct bpf_cgroup_link *link, +static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl, enum cgroup_bpf_attach_type atype) { struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; /* recompute effective prog array in place */ @@ -1113,24 +1142,11 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link or prog in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; - - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->prog == prog && pl->link == link) - goto found; - pos++; - } - } - + pos = effective_prog_pos(desc, atype, pl); /* no link or prog match, skip the cgroup of this layer */ - continue; -found: + if (pos < 0) + continue; + progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); @@ -1196,7 +1212,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* if update effective array failed replace the prog with a dummy prog*/ pl->prog = old_prog; pl->link = link; - purge_effective_progs(cgrp, old_prog, link, atype); + purge_effective_progs(cgrp, pl, atype); } /* now can actually delete it from this cgroup list */ From a6ff26f360c87b7c9f76f493bcecb9223d87e9db Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 18 Jun 2026 23:35:20 -0700 Subject: [PATCH 4335/5214] selftests/bpf: Test cgroup link replace with BPF_F_PREORDER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a cgroup_preorder case that attaches a normal and a BPF_F_PREORDER program to a cgroup (effective order [2, 1]), then replaces the normal link's program via bpf_link_update() and checks the effective order becomes [2, 3] — i.e. only the non-preorder slot changes. Without the replace_effective_prog() fix the array is corrupted and the order is wrong. Signed-off-by: Amery Hung Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260619063520.2690547-3-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- .../bpf/prog_tests/cgroup_preorder.c | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c b/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c index d4d583872fa2..d2ccf409dfe3 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c @@ -102,6 +102,82 @@ static int run_getsockopt_test(int cg_parent, int cg_child, int sock_fd, bool al return err; } +/* + * Replacing a link's program (bpf_link_update) must target the correct slot in + * the effective array even when a BPF_F_PREORDER program is attached to the + * same cgroup. All programs here are attached to a single cgroup; "parent" is + * reused only as a third distinct program. + * + * Attach child(1) normally and child_2(2) with BPF_F_PREORDER, so the effective + * order is [2, 1]. Then replace child(1)'s program with parent(3): only the + * non-preorder slot changes, giving [2, 3]. + */ +static int run_link_replace_test(int cgroup_fd, int sock_fd) +{ + LIBBPF_OPTS(bpf_link_create_opts, create_opts); + int err = 0, normal_link = -1, preorder_link = -1; + struct cgroup_preorder *skel = NULL; + enum bpf_attach_type atype; + __u8 *result, buf = 0x00; + socklen_t optlen = 1; + + skel = cgroup_preorder__open_and_load(); + if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load")) + return -1; + + err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); + if (!ASSERT_OK(err, "setsockopt")) + goto close_skel; + + atype = bpf_program__expected_attach_type(skel->progs.child); + + create_opts.flags = 0; + normal_link = bpf_link_create(bpf_program__fd(skel->progs.child), + cgroup_fd, atype, &create_opts); + if (!ASSERT_GE(normal_link, 0, "create_normal_link")) { + err = normal_link; + goto close_skel; + } + + create_opts.flags = BPF_F_PREORDER; + preorder_link = bpf_link_create(bpf_program__fd(skel->progs.child_2), + cgroup_fd, atype, &create_opts); + if (!ASSERT_GE(preorder_link, 0, "create_preorder_link")) { + err = preorder_link; + goto close_links; + } + + result = skel->bss->result; + skel->bss->idx = 0; + memset(result, 0, 4); + + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); + if (!ASSERT_OK(err, "getsockopt-before")) + goto close_links; + ASSERT_TRUE(result[0] == 2 && result[1] == 1, "order before update"); + + /* Replace the normal link's program child(1) -> parent(3). */ + err = bpf_link_update(normal_link, bpf_program__fd(skel->progs.parent), NULL); + if (!ASSERT_OK(err, "bpf_link_update")) + goto close_links; + + skel->bss->idx = 0; + memset(result, 0, 4); + + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); + if (!ASSERT_OK(err, "getsockopt-after")) + goto close_links; + ASSERT_TRUE(result[0] == 2 && result[1] == 3, "order after update"); + +close_links: + if (preorder_link >= 0) + close(preorder_link); + close(normal_link); +close_skel: + cgroup_preorder__destroy(skel); + return err; +} + void test_cgroup_preorder(void) { int cg_parent = -1, cg_child = -1, sock_fd = -1; @@ -120,6 +196,7 @@ void test_cgroup_preorder(void) ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, false), "getsockopt_test_1"); ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, true), "getsockopt_test_2"); + ASSERT_OK(run_link_replace_test(cg_child, sock_fd), "link_replace_test"); out: close(sock_fd); From c37460cd9b2fcb61ec66b7eb4fde737e65ec2a56 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 22 Jun 2026 09:36:22 +0800 Subject: [PATCH 4336/5214] erofs: remove fscache backend entirely EROFS over fscache was introduced to provide image lazy pulling functionality. After the feature landed, the fscache subsystem made netfs a new hard dependency, which is unexpected for a local filesystem and has an kernel-defined caching hierarchy which could be inflexible compared to the fanotify pre-content hooks. Therefore, this feature has been deprecated for almost two years. As EROFS file-backed mounts and fanotify pre-content hooks both upstream for a while and already providing equivalent functionality (erofs-utils has supported fanotify pre-content hooks), let's remove the fscache backend now. The main application of this feature is Nydus [1], and they plan to move to use fanotify pre-content hooks in the near future too. I hope this patch can be merged into Linux 7.2, which is also motivated by newly found implementation issues [2][3] that are not worth investigating given the deprecation and limited development resources. The associated fscache/cachefiles cleanup patch will follow separately through the vfs tree (netfs) later: it seems fine since the codebase is isolated by CONFIG_CACHEFILES_ONDEMAND. [1] https://github.com/dragonflyoss/nydus/blob/v2.1.0/docs/nydus-fscache.md [2] https://github.com/dragonflyoss/nydus/pull/1824 [3] https://lore.kernel.org/r/20260619135800.1594811-1-michael.bommarito@gmail.com Acked-by: Jingbo Xu Signed-off-by: Gao Xiang --- Documentation/filesystems/erofs.rst | 9 +- fs/erofs/Kconfig | 21 +- fs/erofs/Makefile | 1 - fs/erofs/data.c | 4 +- fs/erofs/fscache.c | 664 ---------------------------- fs/erofs/inode.c | 2 +- fs/erofs/internal.h | 70 +-- fs/erofs/ishare.c | 2 +- fs/erofs/super.c | 94 +--- fs/erofs/zdata.c | 6 - 10 files changed, 26 insertions(+), 847 deletions(-) delete mode 100644 fs/erofs/fscache.c diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst index e4f84ba91052..4230884fb359 100644 --- a/Documentation/filesystems/erofs.rst +++ b/Documentation/filesystems/erofs.rst @@ -130,12 +130,9 @@ dax A legacy option which is an alias for ``dax=always``. device=%s Specify a path to an extra device to be used together. directio (For file-backed mounts) Use direct I/O to access backing files, and asynchronous I/O will be enabled if supported. -fsid=%s Specify a filesystem image ID for Fscache back-end. -domain_id=%s Specify a trusted domain ID for fscache mode so that - different images with the same blobs, identified by blob IDs, - can share storage within the same trusted domain. - Also used for different filesystems with inode page sharing - enabled to share page cache within the trusted domain. +domain_id=%s Specify a trusted domain ID. Filesystems sharing the same + domain ID can share page cache across mounts when inode + page sharing is enabled. (not shown in mountinfo output) fsoffset=%llu Specify block-aligned filesystem offset for the primary device. inode_share Enable inode page sharing for this filesystem. Inodes with identical content within the same domain ID can share the diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 97c48ebe8458..4789b1077d8c 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -3,13 +3,11 @@ config EROFS_FS tristate "EROFS filesystem support" depends on BLOCK - select CACHEFILES if EROFS_FS_ONDEMAND select CRC32 select CRYPTO if EROFS_FS_ZIP_ACCEL select CRYPTO_DEFLATE if EROFS_FS_ZIP_ACCEL select FS_IOMAP select LZ4_DECOMPRESS if EROFS_FS_ZIP - select NETFS_SUPPORT if EROFS_FS_ONDEMAND select XXHASH if EROFS_FS_XATTR select XZ_DEC if EROFS_FS_ZIP_LZMA select XZ_DEC_MICROLZMA if EROFS_FS_ZIP_LZMA @@ -109,9 +107,6 @@ config EROFS_FS_BACKED_BY_FILE be used to simplify error-prone lifetime management of unnecessary virtual block devices. - Note that this feature, along with ongoing fanotify pre-content - hooks, will eventually replace "EROFS over fscache." - If you don't want to enable this feature, say N. config EROFS_FS_ZIP @@ -172,20 +167,6 @@ config EROFS_FS_ZIP_ACCEL If unsure, say N. -config EROFS_FS_ONDEMAND - bool "EROFS fscache-based on-demand read support (deprecated)" - depends on EROFS_FS - select FSCACHE - select CACHEFILES_ONDEMAND - help - This permits EROFS to use fscache-backed data blobs with on-demand - read support. - - It is now deprecated and scheduled to be removed from the kernel - after fanotify pre-content hooks are landed. - - If unsure, say N. - config EROFS_FS_PCPU_KTHREAD bool "EROFS per-cpu decompression kthread workers" depends on EROFS_FS_ZIP @@ -207,7 +188,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI config EROFS_FS_PAGE_CACHE_SHARE bool "EROFS page cache share support (experimental)" - depends on EROFS_FS && EROFS_FS_XATTR && !EROFS_FS_ONDEMAND + depends on EROFS_FS && EROFS_FS_XATTR help This enables page cache sharing among inodes with identical content fingerprints on the same machine. diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile index a80e1762b607..30423496786f 100644 --- a/fs/erofs/Makefile +++ b/fs/erofs/Makefile @@ -9,5 +9,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o erofs-$(CONFIG_EROFS_FS_ZIP_ACCEL) += decompressor_crypto.o erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o -erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o diff --git a/fs/erofs/data.c b/fs/erofs/data.c index cdf2e2ef8ea8..9aa48c8d67d1 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -80,9 +80,7 @@ int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb, if (erofs_is_fileio_mode(sbi)) { buf->file = sbi->dif0.file; /* some fs like FUSE needs it */ buf->mapping = buf->file->f_mapping; - } else if (erofs_is_fscache_mode(sb)) - buf->mapping = sbi->dif0.fscache->inode->i_mapping; - else + } else buf->mapping = sb->s_bdev->bd_mapping; return 0; } diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c deleted file mode 100644 index 685c68774379..000000000000 --- a/fs/erofs/fscache.c +++ /dev/null @@ -1,664 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2022, Alibaba Cloud - * Copyright (C) 2022, Bytedance Inc. All rights reserved. - */ -#include -#include "internal.h" - -static DEFINE_MUTEX(erofs_domain_list_lock); -static DEFINE_MUTEX(erofs_domain_cookies_lock); -static LIST_HEAD(erofs_domain_list); -static LIST_HEAD(erofs_domain_cookies_list); -static struct vfsmount *erofs_pseudo_mnt; - -struct erofs_fscache_io { - struct netfs_cache_resources cres; - struct iov_iter iter; - netfs_io_terminated_t end_io; - void *private; - refcount_t ref; -}; - -struct erofs_fscache_rq { - struct address_space *mapping; /* The mapping being accessed */ - loff_t start; /* Start position */ - size_t len; /* Length of the request */ - size_t submitted; /* Length of submitted */ - short error; /* 0 or error that occurred */ - refcount_t ref; -}; - -static bool erofs_fscache_io_put(struct erofs_fscache_io *io) -{ - if (!refcount_dec_and_test(&io->ref)) - return false; - if (io->cres.ops) - io->cres.ops->end_operation(&io->cres); - kfree(io); - return true; -} - -static void erofs_fscache_req_complete(struct erofs_fscache_rq *req) -{ - struct folio *folio; - bool failed = req->error; - pgoff_t start_page = req->start / PAGE_SIZE; - pgoff_t last_page = ((req->start + req->len) / PAGE_SIZE) - 1; - - XA_STATE(xas, &req->mapping->i_pages, start_page); - - rcu_read_lock(); - xas_for_each(&xas, folio, last_page) { - if (xas_retry(&xas, folio)) - continue; - if (!failed) - folio_mark_uptodate(folio); - folio_unlock(folio); - } - rcu_read_unlock(); -} - -static void erofs_fscache_req_put(struct erofs_fscache_rq *req) -{ - if (!refcount_dec_and_test(&req->ref)) - return; - erofs_fscache_req_complete(req); - kfree(req); -} - -static struct erofs_fscache_rq *erofs_fscache_req_alloc(struct address_space *mapping, - loff_t start, size_t len) -{ - struct erofs_fscache_rq *req = kzalloc_obj(*req); - - if (!req) - return NULL; - req->mapping = mapping; - req->start = start; - req->len = len; - refcount_set(&req->ref, 1); - return req; -} - -static void erofs_fscache_req_io_put(struct erofs_fscache_io *io) -{ - struct erofs_fscache_rq *req = io->private; - - if (erofs_fscache_io_put(io)) - erofs_fscache_req_put(req); -} - -static void erofs_fscache_req_end_io(void *priv, ssize_t transferred_or_error) -{ - struct erofs_fscache_io *io = priv; - struct erofs_fscache_rq *req = io->private; - - if (IS_ERR_VALUE(transferred_or_error)) - req->error = transferred_or_error; - erofs_fscache_req_io_put(io); -} - -static struct erofs_fscache_io *erofs_fscache_req_io_alloc(struct erofs_fscache_rq *req) -{ - struct erofs_fscache_io *io = kzalloc_obj(*io); - - if (!io) - return NULL; - io->end_io = erofs_fscache_req_end_io; - io->private = req; - refcount_inc(&req->ref); - refcount_set(&io->ref, 1); - return io; -} - -/* - * Read data from fscache described by cookie at pstart physical address - * offset, and fill the read data into buffer described by io->iter. - */ -static int erofs_fscache_read_io_async(struct fscache_cookie *cookie, - loff_t pstart, struct erofs_fscache_io *io) -{ - enum netfs_io_source source; - struct netfs_cache_resources *cres = &io->cres; - struct iov_iter *iter = &io->iter; - int ret; - - ret = fscache_begin_read_operation(cres, cookie); - if (ret) - return ret; - - while (iov_iter_count(iter)) { - size_t orig_count = iov_iter_count(iter), len = orig_count; - unsigned long flags = 1 << NETFS_SREQ_ONDEMAND; - - source = cres->ops->prepare_ondemand_read(cres, - pstart, &len, LLONG_MAX, &flags, 0); - if (WARN_ON(len == 0)) - source = NETFS_INVALID_READ; - if (source != NETFS_READ_FROM_CACHE) { - erofs_err(NULL, "prepare_ondemand_read failed (source %d)", source); - return -EIO; - } - - iov_iter_truncate(iter, len); - refcount_inc(&io->ref); - ret = fscache_read(cres, pstart, iter, NETFS_READ_HOLE_FAIL, - io->end_io, io); - if (ret == -EIOCBQUEUED) - ret = 0; - if (ret) { - erofs_err(NULL, "fscache_read failed (ret %d)", ret); - return ret; - } - if (WARN_ON(iov_iter_count(iter))) - return -EIO; - - iov_iter_reexpand(iter, orig_count - len); - pstart += len; - } - return 0; -} - -struct erofs_fscache_bio { - struct erofs_fscache_io io; - struct bio bio; /* w/o bdev to share bio_add_page/endio() */ - struct bio_vec bvecs[BIO_MAX_VECS]; -}; - -static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error) -{ - struct erofs_fscache_bio *io = priv; - - if (IS_ERR_VALUE(transferred_or_error)) - io->bio.bi_status = errno_to_blk_status(transferred_or_error); - bio_endio(&io->bio); - BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0); - erofs_fscache_io_put(&io->io); -} - -struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) -{ - struct erofs_fscache_bio *io; - - io = kmalloc_obj(*io, GFP_KERNEL | __GFP_NOFAIL); - bio_init(&io->bio, NULL, io->bvecs, BIO_MAX_VECS, REQ_OP_READ); - io->io.private = mdev->m_dif->fscache->cookie; - io->io.end_io = erofs_fscache_bio_endio; - refcount_set(&io->io.ref, 1); - return &io->bio; -} - -void erofs_fscache_submit_bio(struct bio *bio) -{ - struct erofs_fscache_bio *io = container_of(bio, - struct erofs_fscache_bio, bio); - int ret; - - iov_iter_bvec(&io->io.iter, ITER_DEST, io->bvecs, bio->bi_vcnt, - bio->bi_iter.bi_size); - ret = erofs_fscache_read_io_async(io->io.private, - bio->bi_iter.bi_sector << 9, &io->io); - erofs_fscache_io_put(&io->io); - if (!ret) - return; - bio->bi_status = errno_to_blk_status(ret); - bio_endio(bio); -} - -static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) -{ - struct erofs_fscache *ctx = folio->mapping->host->i_private; - int ret = -ENOMEM; - struct erofs_fscache_rq *req; - struct erofs_fscache_io *io; - - req = erofs_fscache_req_alloc(folio->mapping, - folio_pos(folio), folio_size(folio)); - if (!req) { - folio_unlock(folio); - return ret; - } - - io = erofs_fscache_req_io_alloc(req); - if (!io) { - req->error = ret; - goto out; - } - iov_iter_xarray(&io->iter, ITER_DEST, &folio->mapping->i_pages, - folio_pos(folio), folio_size(folio)); - - ret = erofs_fscache_read_io_async(ctx->cookie, folio_pos(folio), io); - if (ret) - req->error = ret; - - erofs_fscache_req_io_put(io); -out: - erofs_fscache_req_put(req); - return ret; -} - -static int erofs_fscache_data_read_slice(struct erofs_fscache_rq *req) -{ - struct address_space *mapping = req->mapping; - struct inode *inode = mapping->host; - struct super_block *sb = inode->i_sb; - struct erofs_fscache_io *io; - struct erofs_map_blocks map; - struct erofs_map_dev mdev; - loff_t pos = req->start + req->submitted; - size_t count; - int ret; - - map.m_la = pos; - ret = erofs_map_blocks(inode, &map); - if (ret) - return ret; - - if (map.m_flags & EROFS_MAP_META) { - struct erofs_buf buf = __EROFS_BUF_INITIALIZER; - struct iov_iter iter; - size_t size = map.m_llen; - void *src; - - src = erofs_read_metabuf(&buf, sb, map.m_pa, - erofs_inode_in_metabox(inode)); - if (IS_ERR(src)) - return PTR_ERR(src); - - iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE); - if (copy_to_iter(src, size, &iter) != size) { - erofs_put_metabuf(&buf); - return -EFAULT; - } - iov_iter_zero(PAGE_SIZE - size, &iter); - erofs_put_metabuf(&buf); - req->submitted += PAGE_SIZE; - return 0; - } - - count = req->len - req->submitted; - if (!(map.m_flags & EROFS_MAP_MAPPED)) { - struct iov_iter iter; - - iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count); - iov_iter_zero(count, &iter); - req->submitted += count; - return 0; - } - - count = min_t(size_t, map.m_llen - (pos - map.m_la), count); - DBG_BUGON(!count || count % PAGE_SIZE); - - mdev = (struct erofs_map_dev) { - .m_deviceid = map.m_deviceid, - .m_pa = map.m_pa, - }; - ret = erofs_map_dev(sb, &mdev); - if (ret) - return ret; - - io = erofs_fscache_req_io_alloc(req); - if (!io) - return -ENOMEM; - iov_iter_xarray(&io->iter, ITER_DEST, &mapping->i_pages, pos, count); - ret = erofs_fscache_read_io_async(mdev.m_dif->fscache->cookie, - mdev.m_pa + (pos - map.m_la), io); - erofs_fscache_req_io_put(io); - - req->submitted += count; - return ret; -} - -static int erofs_fscache_data_read(struct erofs_fscache_rq *req) -{ - int ret; - - do { - ret = erofs_fscache_data_read_slice(req); - if (ret) - req->error = ret; - } while (!ret && req->submitted < req->len); - return ret; -} - -static int erofs_fscache_read_folio(struct file *file, struct folio *folio) -{ - struct erofs_fscache_rq *req; - int ret; - - req = erofs_fscache_req_alloc(folio->mapping, - folio_pos(folio), folio_size(folio)); - if (!req) { - folio_unlock(folio); - return -ENOMEM; - } - - ret = erofs_fscache_data_read(req); - erofs_fscache_req_put(req); - return ret; -} - -static void erofs_fscache_readahead(struct readahead_control *rac) -{ - struct erofs_fscache_rq *req; - - if (!readahead_count(rac)) - return; - - req = erofs_fscache_req_alloc(rac->mapping, - readahead_pos(rac), readahead_length(rac)); - if (!req) - return; - - /* The request completion will drop refs on the folios. */ - while (readahead_folio(rac)) - ; - - erofs_fscache_data_read(req); - erofs_fscache_req_put(req); -} - -static const struct address_space_operations erofs_fscache_meta_aops = { - .read_folio = erofs_fscache_meta_read_folio, -}; - -const struct address_space_operations erofs_fscache_access_aops = { - .read_folio = erofs_fscache_read_folio, - .readahead = erofs_fscache_readahead, -}; - -static void erofs_fscache_domain_put(struct erofs_domain *domain) -{ - mutex_lock(&erofs_domain_list_lock); - if (refcount_dec_and_test(&domain->ref)) { - list_del(&domain->list); - if (list_empty(&erofs_domain_list)) { - kern_unmount(erofs_pseudo_mnt); - erofs_pseudo_mnt = NULL; - } - fscache_relinquish_volume(domain->volume, NULL, false); - mutex_unlock(&erofs_domain_list_lock); - kfree_sensitive(domain->domain_id); - kfree(domain); - return; - } - mutex_unlock(&erofs_domain_list_lock); -} - -static int erofs_fscache_register_volume(struct super_block *sb) -{ - struct erofs_sb_info *sbi = EROFS_SB(sb); - char *domain_id = sbi->domain_id; - struct fscache_volume *volume; - char *name; - int ret = 0; - - name = kasprintf(GFP_KERNEL, "erofs,%s", - domain_id ? domain_id : sbi->fsid); - if (!name) - return -ENOMEM; - - volume = fscache_acquire_volume(name, NULL, NULL, 0); - if (IS_ERR_OR_NULL(volume)) { - erofs_err(sb, "failed to register volume for %s", name); - ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; - volume = NULL; - } - - sbi->volume = volume; - kfree(name); - return ret; -} - -static int erofs_fscache_init_domain(struct super_block *sb) -{ - int err; - struct erofs_domain *domain; - struct erofs_sb_info *sbi = EROFS_SB(sb); - - domain = kzalloc_obj(struct erofs_domain); - if (!domain) - return -ENOMEM; - - domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); - if (!domain->domain_id) { - kfree(domain); - return -ENOMEM; - } - - err = erofs_fscache_register_volume(sb); - if (err) - goto out; - - if (!erofs_pseudo_mnt) { - struct vfsmount *mnt = kern_mount(&erofs_anon_fs_type); - if (IS_ERR(mnt)) { - err = PTR_ERR(mnt); - goto out; - } - erofs_pseudo_mnt = mnt; - } - - domain->volume = sbi->volume; - refcount_set(&domain->ref, 1); - list_add(&domain->list, &erofs_domain_list); - sbi->domain = domain; - return 0; -out: - kfree_sensitive(domain->domain_id); - kfree(domain); - return err; -} - -static int erofs_fscache_register_domain(struct super_block *sb) -{ - int err; - struct erofs_domain *domain; - struct erofs_sb_info *sbi = EROFS_SB(sb); - - mutex_lock(&erofs_domain_list_lock); - list_for_each_entry(domain, &erofs_domain_list, list) { - if (!strcmp(domain->domain_id, sbi->domain_id)) { - sbi->domain = domain; - sbi->volume = domain->volume; - refcount_inc(&domain->ref); - mutex_unlock(&erofs_domain_list_lock); - return 0; - } - } - err = erofs_fscache_init_domain(sb); - mutex_unlock(&erofs_domain_list_lock); - return err; -} - -static struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, - char *name, unsigned int flags) -{ - struct fscache_volume *volume = EROFS_SB(sb)->volume; - struct erofs_fscache *ctx; - struct fscache_cookie *cookie; - struct super_block *isb; - struct inode *inode; - int ret; - - ctx = kzalloc_obj(*ctx); - if (!ctx) - return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&ctx->node); - refcount_set(&ctx->ref, 1); - - cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, - name, strlen(name), NULL, 0, 0); - if (!cookie) { - erofs_err(sb, "failed to get cookie for %s", name); - ret = -EINVAL; - goto err; - } - fscache_use_cookie(cookie, false); - - /* - * Allocate anonymous inode in global pseudo mount for shareable blobs, - * so that they are accessible among erofs fs instances. - */ - isb = flags & EROFS_REG_COOKIE_SHARE ? erofs_pseudo_mnt->mnt_sb : sb; - inode = new_inode(isb); - if (!inode) { - erofs_err(sb, "failed to get anon inode for %s", name); - ret = -ENOMEM; - goto err_cookie; - } - - inode->i_size = OFFSET_MAX; - inode->i_mapping->a_ops = &erofs_fscache_meta_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_KERNEL); - inode->i_blkbits = EROFS_SB(sb)->blkszbits; - inode->i_private = ctx; - - ctx->cookie = cookie; - ctx->inode = inode; - return ctx; - -err_cookie: - fscache_unuse_cookie(cookie, NULL, NULL); - fscache_relinquish_cookie(cookie, false); -err: - kfree(ctx); - return ERR_PTR(ret); -} - -static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) -{ - fscache_unuse_cookie(ctx->cookie, NULL, NULL); - fscache_relinquish_cookie(ctx->cookie, false); - iput(ctx->inode); - kfree(ctx->name); - kfree(ctx); -} - -static struct erofs_fscache *erofs_domain_init_cookie(struct super_block *sb, - char *name, unsigned int flags) -{ - struct erofs_fscache *ctx; - struct erofs_domain *domain = EROFS_SB(sb)->domain; - - ctx = erofs_fscache_acquire_cookie(sb, name, flags); - if (IS_ERR(ctx)) - return ctx; - - ctx->name = kstrdup(name, GFP_KERNEL); - if (!ctx->name) { - erofs_fscache_relinquish_cookie(ctx); - return ERR_PTR(-ENOMEM); - } - - refcount_inc(&domain->ref); - ctx->domain = domain; - list_add(&ctx->node, &erofs_domain_cookies_list); - return ctx; -} - -static struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, - char *name, unsigned int flags) -{ - struct erofs_fscache *ctx; - struct erofs_domain *domain = EROFS_SB(sb)->domain; - - flags |= EROFS_REG_COOKIE_SHARE; - mutex_lock(&erofs_domain_cookies_lock); - list_for_each_entry(ctx, &erofs_domain_cookies_list, node) { - if (ctx->domain != domain || strcmp(ctx->name, name)) - continue; - if (!(flags & EROFS_REG_COOKIE_NEED_NOEXIST)) { - refcount_inc(&ctx->ref); - } else { - erofs_err(sb, "%s already exists in domain %s", name, - domain->domain_id); - ctx = ERR_PTR(-EEXIST); - } - mutex_unlock(&erofs_domain_cookies_lock); - return ctx; - } - ctx = erofs_domain_init_cookie(sb, name, flags); - mutex_unlock(&erofs_domain_cookies_lock); - return ctx; -} - -struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, - char *name, - unsigned int flags) -{ - if (EROFS_SB(sb)->domain_id) - return erofs_domain_register_cookie(sb, name, flags); - return erofs_fscache_acquire_cookie(sb, name, flags); -} - -void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) -{ - struct erofs_domain *domain = NULL; - - if (!ctx) - return; - if (!ctx->domain) - return erofs_fscache_relinquish_cookie(ctx); - - mutex_lock(&erofs_domain_cookies_lock); - if (refcount_dec_and_test(&ctx->ref)) { - domain = ctx->domain; - list_del(&ctx->node); - erofs_fscache_relinquish_cookie(ctx); - } - mutex_unlock(&erofs_domain_cookies_lock); - if (domain) - erofs_fscache_domain_put(domain); -} - -int erofs_fscache_register_fs(struct super_block *sb) -{ - int ret; - struct erofs_sb_info *sbi = EROFS_SB(sb); - struct erofs_fscache *fscache; - unsigned int flags = 0; - - if (sbi->domain_id) - ret = erofs_fscache_register_domain(sb); - else - ret = erofs_fscache_register_volume(sb); - if (ret) - return ret; - - /* - * When shared domain is enabled, using NEED_NOEXIST to guarantee - * the primary data blob (aka fsid) is unique in the shared domain. - * - * For non-shared-domain case, fscache_acquire_volume() invoked by - * erofs_fscache_register_volume() has already guaranteed - * the uniqueness of primary data blob. - * - * Acquired domain/volume will be relinquished in kill_sb() on error. - */ - if (sbi->domain_id) - flags |= EROFS_REG_COOKIE_NEED_NOEXIST; - fscache = erofs_fscache_register_cookie(sb, sbi->fsid, flags); - if (IS_ERR(fscache)) - return PTR_ERR(fscache); - - sbi->dif0.fscache = fscache; - return 0; -} - -void erofs_fscache_unregister_fs(struct super_block *sb) -{ - struct erofs_sb_info *sbi = EROFS_SB(sb); - - erofs_fscache_unregister_cookie(sbi->dif0.fscache); - - if (sbi->domain) - erofs_fscache_domain_put(sbi->domain); - else - fscache_relinquish_volume(sbi->volume, NULL, false); - - sbi->dif0.fscache = NULL; - sbi->volume = NULL; - sbi->domain = NULL; -} diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index a188c570087a..1df38b7c82a7 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -255,7 +255,7 @@ static int erofs_fill_inode(struct inode *inode) } mapping_set_large_folios(inode->i_mapping); - aops = erofs_get_aops(inode, false); + aops = erofs_get_aops(inode); if (IS_ERR(aops)) return PTR_ERR(aops); inode->i_mapping->a_ops = aops; diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 9e2ae7b61977..580f8d9f14e7 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -43,7 +43,6 @@ typedef u64 erofs_blk_t; struct erofs_device_info { char *path; - struct erofs_fscache *fscache; struct file *file; struct dax_device *dax_dev; u64 fsoff, dax_part_off; @@ -80,24 +79,6 @@ struct erofs_sb_lz4_info { u16 max_pclusterblks; }; -struct erofs_domain { - refcount_t ref; - struct list_head list; - struct fscache_volume *volume; - char *domain_id; -}; - -struct erofs_fscache { - struct fscache_cookie *cookie; - struct inode *inode; /* anonymous inode for the blob */ - - /* used for share domain mode */ - struct erofs_domain *domain; - struct list_head node; - refcount_t ref; - char *name; -}; - struct erofs_xattr_prefix_item { struct erofs_xattr_long_prefix *prefix; u8 infix_len; @@ -162,10 +143,6 @@ struct erofs_sb_info { struct completion s_kobj_unregister; erofs_off_t dir_ra_bytes; - /* fscache support */ - struct fscache_volume *volume; - struct erofs_domain *domain; - char *fsid; char *domain_id; }; @@ -191,12 +168,6 @@ static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi) extern struct file_system_type erofs_anon_fs_type; -static inline bool erofs_is_fscache_mode(struct super_block *sb) -{ - return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && - !erofs_is_fileio_mode(EROFS_SB(sb)) && !sb->s_bdev; -} - enum { EROFS_ZIP_CACHE_DISABLED, EROFS_ZIP_CACHE_READAHEAD, @@ -413,11 +384,9 @@ struct erofs_map_dev { }; extern const struct super_operations erofs_sops; - extern const struct address_space_operations erofs_aops; extern const struct address_space_operations erofs_fileio_aops; extern const struct address_space_operations z_erofs_aops; -extern const struct address_space_operations erofs_fscache_access_aops; extern const struct inode_operations erofs_generic_iops; extern const struct inode_operations erofs_symlink_iops; @@ -430,10 +399,6 @@ extern const struct file_operations erofs_ishare_fops; extern const struct iomap_ops z_erofs_iomap_report_ops; -/* flags for erofs_fscache_register_cookie() */ -#define EROFS_REG_COOKIE_SHARE 0x0001 -#define EROFS_REG_COOKIE_NEED_NOEXIST 0x0002 - void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, erofs_off_t *offset, int *lengthp); void erofs_unmap_metabuf(struct erofs_buf *buf); @@ -473,7 +438,7 @@ static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count) } static inline const struct address_space_operations * -erofs_get_aops(struct inode *realinode, bool no_fscache) +erofs_get_aops(struct inode *realinode) { if (erofs_inode_is_data_compressed(EROFS_I(realinode)->datalayout)) { if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP)) @@ -483,9 +448,6 @@ erofs_get_aops(struct inode *realinode, bool no_fscache) "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!"); return &z_erofs_aops; } - if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !no_fscache && - erofs_is_fscache_mode(realinode->i_sb)) - return &erofs_fscache_access_aops; if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && erofs_is_fileio_mode(EROFS_SB(realinode->i_sb))) return &erofs_fileio_aops; @@ -548,36 +510,6 @@ static inline struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev) { r static inline void erofs_fileio_submit_bio(struct bio *bio) {} #endif -#ifdef CONFIG_EROFS_FS_ONDEMAND -int erofs_fscache_register_fs(struct super_block *sb); -void erofs_fscache_unregister_fs(struct super_block *sb); - -struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, - char *name, unsigned int flags); -void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache); -struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev); -void erofs_fscache_submit_bio(struct bio *bio); -#else -static inline int erofs_fscache_register_fs(struct super_block *sb) -{ - return -EOPNOTSUPP; -} -static inline void erofs_fscache_unregister_fs(struct super_block *sb) {} - -static inline -struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, - char *name, unsigned int flags) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache) -{ -} -static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) { return NULL; } -static inline void erofs_fscache_submit_bio(struct bio *bio) {} -#endif - #ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE int __init erofs_init_ishare(void); void erofs_exit_ishare(void); diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c index 35cbd0bc04d7..0868c12fc15b 100644 --- a/fs/erofs/ishare.c +++ b/fs/erofs/ishare.c @@ -45,7 +45,7 @@ bool erofs_ishare_fill_inode(struct inode *inode) struct erofs_inode_fingerprint fp; struct inode *si; - aops = erofs_get_aops(inode, true); + aops = erofs_get_aops(inode); if (IS_ERR(aops)) return false; if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id)) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 579443e6acfe..86fa5c6a0c70 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -126,7 +126,6 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, struct erofs_device_info *dif, erofs_off_t *pos) { struct erofs_sb_info *sbi = EROFS_SB(sb); - struct erofs_fscache *fscache; struct erofs_deviceslot *dis; struct file *file; bool _48bit; @@ -145,12 +144,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, return -ENOMEM; } - if (erofs_is_fscache_mode(sb)) { - fscache = erofs_fscache_register_cookie(sb, dif->path, 0); - if (IS_ERR(fscache)) - return PTR_ERR(fscache); - dif->fscache = fscache; - } else if (!sbi->devs->flatdev) { + if (!sbi->devs->flatdev) { file = erofs_is_fileio_mode(sbi) ? filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) : bdev_file_open_by_path(dif->path, @@ -216,7 +210,7 @@ static int erofs_scan_devices(struct super_block *sb, if (!ondisk_extradevs) return 0; - if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb)) + if (!sbi->devs->extra_devices) sbi->devs->flatdev = true; sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1; @@ -372,8 +366,6 @@ static int erofs_read_superblock(struct super_block *sb) erofs_info(sb, "EXPERIMENTAL 48-bit layout support in use. Use at your own risk!"); if (erofs_sb_has_metabox(sbi)) erofs_info(sb, "EXPERIMENTAL metadata compression support in use. Use at your own risk!"); - if (erofs_is_fscache_mode(sb)) - erofs_info(sb, "[deprecated] fscache-based on-demand read feature in use. Use at your own risk!"); out: erofs_put_metabuf(&buf); return ret; @@ -393,8 +385,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi) enum { Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum, - Opt_device, Opt_fsid, Opt_domain_id, Opt_directio, Opt_fsoffset, - Opt_inode_share, + Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share, }; static const struct constant_table erofs_param_cache_strategy[] = { @@ -418,7 +409,6 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = { fsparam_flag("dax", Opt_dax), fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums), fsparam_string("device", Opt_device), - fsparam_string("fsid", Opt_fsid), fsparam_string("domain_id", Opt_domain_id), fsparam_flag_no("directio", Opt_directio), fsparam_u64("fsoffset", Opt_fsoffset), @@ -509,25 +499,14 @@ static int erofs_fc_parse_param(struct fs_context *fc, } ++sbi->devs->extra_devices; break; -#ifdef CONFIG_EROFS_FS_ONDEMAND - case Opt_fsid: - kfree(sbi->fsid); - sbi->fsid = kstrdup(param->string, GFP_KERNEL); - if (!sbi->fsid) - return -ENOMEM; - break; -#endif -#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) case Opt_domain_id: - kfree_sensitive(sbi->domain_id); - sbi->domain_id = no_free_ptr(param->string); + if (!IS_ENABLED(CONFIG_EROFS_FS_PAGE_CACHE_SHARE)) { + errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); + } else { + kfree_sensitive(sbi->domain_id); + sbi->domain_id = no_free_ptr(param->string); + } break; -#else - case Opt_fsid: - case Opt_domain_id: - errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); - break; -#endif case Opt_directio: if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE)) errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); @@ -620,12 +599,7 @@ static void erofs_set_sysfs_name(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); - if (sbi->domain_id && sbi->fsid) - super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id, - sbi->fsid); - else if (sbi->fsid) - super_set_sysfs_name_generic(sb, "%s", sbi->fsid); - else if (erofs_is_fileio_mode(sbi)) + if (erofs_is_fileio_mode(sbi)) super_set_sysfs_name_generic(sb, "%s", bdi_dev_name(sb->s_bdi)); else @@ -680,11 +654,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; - if (erofs_is_fscache_mode(sb)) { - err = erofs_fscache_register_fs(sb); - if (err) - return err; - } err = super_setup_bdi(sb); if (err) return err; @@ -703,11 +672,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) return err; if (sb->s_blocksize_bits != sbi->blkszbits) { - if (erofs_is_fscache_mode(sb)) { - errorfc(fc, "unsupported blksize for fscache mode"); - return -EINVAL; - } - if (erofs_is_fileio_mode(sbi)) { sb->s_blocksize = 1 << sbi->blkszbits; sb->s_blocksize_bits = sbi->blkszbits; @@ -716,14 +680,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) return -EINVAL; } } - - if (sbi->dif0.fsoff) { - if (sbi->dif0.fsoff & (sb->s_blocksize - 1)) - return invalfc(fc, "fsoffset %llu is not aligned to block size %lu", - sbi->dif0.fsoff, sb->s_blocksize); - if (erofs_is_fscache_mode(sb)) - return invalfc(fc, "cannot use fsoffset in fscache mode"); - } + if (sbi->dif0.fsoff & (sb->s_blocksize - 1)) + return invalfc(fc, "fsoffset %llu is not aligned to block size %lu", + sbi->dif0.fsoff, sb->s_blocksize); if (test_opt(&sbi->opt, DAX_ALWAYS) && sbi->blkszbits != PAGE_SHIFT) { erofs_info(sb, "unsupported blocksize for DAX"); @@ -793,16 +752,13 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) static int erofs_fc_get_tree(struct fs_context *fc) { - struct erofs_sb_info *sbi = fc->s_fs_info; int ret; - if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) - return get_tree_nodev(fc, erofs_fc_fill_super); - ret = get_tree_bdev_flags(fc, erofs_fc_fill_super, IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ? GET_TREE_BDEV_QUIET_LOOKUP : 0); if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && ret == -ENOTBLK) { + struct erofs_sb_info *sbi = fc->s_fs_info; struct file *file; if (!fc->source) @@ -827,8 +783,8 @@ static int erofs_fc_reconfigure(struct fs_context *fc) DBG_BUGON(!sb_rdonly(sb)); - if (new_sbi->fsid || new_sbi->domain_id) - erofs_info(sb, "ignoring reconfiguration for fsid|domain_id."); + if (new_sbi->domain_id) + erofs_info(sb, "ignoring reconfiguration for domain_id."); if (test_opt(&new_sbi->opt, POSIX_ACL)) fc->sb_flags |= SB_POSIXACL; @@ -848,8 +804,6 @@ static int erofs_release_device_info(int id, void *ptr, void *data) fs_put_dax(dif->dax_dev, NULL); if (dif->file) fput(dif->file); - erofs_fscache_unregister_cookie(dif->fscache); - dif->fscache = NULL; kfree(dif->path); kfree(dif); return 0; @@ -867,7 +821,6 @@ static void erofs_free_dev_context(struct erofs_dev_context *devs) static void erofs_sb_free(struct erofs_sb_info *sbi) { erofs_free_dev_context(sbi->devs); - kfree(sbi->fsid); kfree_sensitive(sbi->domain_id); if (sbi->dif0.file) fput(sbi->dif0.file); @@ -928,14 +881,12 @@ static void erofs_kill_sb(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); - if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) || - sbi->dif0.file) + if (sbi->dif0.file) kill_anon_super(sb); else kill_block_super(sb); erofs_drop_internal_inodes(sbi); fs_put_dax(sbi->dif0.dax_dev, NULL); - erofs_fscache_unregister_fs(sb); erofs_sb_free(sbi); sb->s_fs_info = NULL; } @@ -950,7 +901,6 @@ static void erofs_put_super(struct super_block *sb) erofs_drop_internal_inodes(sbi); erofs_free_dev_context(sbi->devs); sbi->devs = NULL; - erofs_fscache_unregister_fs(sb); } static struct file_system_type erofs_fs_type = { @@ -962,14 +912,12 @@ static struct file_system_type erofs_fs_type = { }; MODULE_ALIAS_FS("erofs"); -#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE static void erofs_free_anon_inode(struct inode *inode) { struct erofs_inode *vi = EROFS_I(inode); -#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE kfree(vi->fingerprint.opaque); -#endif kmem_cache_free(erofs_inode_cachep, vi); } @@ -1099,12 +1047,6 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",dax=never"); if (erofs_is_fileio_mode(sbi) && test_opt(opt, DIRECT_IO)) seq_puts(seq, ",directio"); - if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND)) { - if (sbi->fsid) - seq_printf(seq, ",fsid=%s", sbi->fsid); - if (sbi->domain_id) - seq_printf(seq, ",domain_id=%s", sbi->domain_id); - } if (sbi->dif0.fsoff) seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff); if (test_opt(opt, INODE_SHARE)) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 1c65b741b288..74520e910259 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1710,8 +1710,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, drain_io: if (erofs_is_fileio_mode(EROFS_SB(sb))) erofs_fileio_submit_bio(bio); - else if (erofs_is_fscache_mode(sb)) - erofs_fscache_submit_bio(bio); else submit_bio(bio); @@ -1740,8 +1738,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, if (!bio) { if (erofs_is_fileio_mode(EROFS_SB(sb))) bio = erofs_fileio_bio_alloc(&mdev); - else if (erofs_is_fscache_mode(sb)) - bio = erofs_fscache_bio_alloc(&mdev); else bio = bio_alloc(mdev.m_bdev, BIO_MAX_VECS, REQ_OP_READ, GFP_NOIO); @@ -1770,8 +1766,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, if (bio) { if (erofs_is_fileio_mode(EROFS_SB(sb))) erofs_fileio_submit_bio(bio); - else if (erofs_is_fscache_mode(sb)) - erofs_fscache_submit_bio(bio); else submit_bio(bio); } From 257595adf9dac15ae1edd9d07753fbc576a7583d Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 16 Jun 2026 15:10:49 +0000 Subject: [PATCH 4337/5214] pwrseq: core: fix use-after-free in pwrseq_debugfs_seq_next() pwrseq_debugfs_seq_next() declares 'next' with __free(put_device), which causes put_device() to be called on the returned pointer when the variable goes out of scope. This results in a use-after-free since the seq_file framework receives a pointer whose reference has already been dropped. Simply removing __free(put_device) would fix the UAF but would leak the reference acquired by bus_find_next_device(), as stop() only calls up_read(&pwrseq_sem) and never releases the device reference. Fix this by making the reference counting consistent across all seq_file callbacks, matching the standard pattern used by PCI and SCSI: - start(): use get_device() so it returns a referenced pointer. - next(): explicitly put_device(curr) to release the previous device's reference (no NULL check needed - the seq_file framework only calls next() while the previous return was non-NULL). - stop(): put_device(data) to release the last iterated device's reference, with a NULL guard since stop() may be called with NULL when start() returned NULL or next() reached end-of-sequence. Cc: stable@vger.kernel.org Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260616151049.1705503-1-vulab@iscas.ac.cn Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 89694092981f..c1c2265160a3 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -1012,8 +1012,9 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) ctx.index = *pos; /* - * We're holding the lock for the entire printout so no need to fiddle - * with device reference count. + * Hold the lock for the entire printout to prevent device removal. + * Reference counts are managed by start()/next()/stop() as required + * by the seq_file contract. */ down_read(&pwrseq_sem); @@ -1021,7 +1022,7 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) if (!ctx.index) return NULL; - return ctx.dev; + return get_device(ctx.dev); } static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, @@ -1031,8 +1032,9 @@ static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, ++*pos; - struct device *next __free(put_device) = - bus_find_next_device(&pwrseq_bus, curr); + struct device *next = bus_find_next_device(&pwrseq_bus, curr); + + put_device(curr); return next; } @@ -1081,6 +1083,8 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data) { + if (data) + put_device(data); up_read(&pwrseq_sem); } From 251c8e86539aefa88980b3dccee7d8ae4974af06 Mon Sep 17 00:00:00 2001 From: Wei Deng Date: Wed, 17 Jun 2026 20:00:55 +0530 Subject: [PATCH 4338/5214] power: sequencing: pcie-m2: Sort PCI device IDs in ascending order Sort the entries in pwrseq_m2_pci_ids[] by device ID in ascending order: 0x1103 (WCN6855) before 0x1107 (WCN7850). Fixes: 2abcfdd91e6a ("power: sequencing: pcie-m2: Add PCI ID 0x1103 for WCN6855 Bluetooth") Reviewed-by: Manivannan Sadhasivam Signed-off-by: Wei Deng Link: https://patch.msgid.link/20260617143055.820096-1-wei.deng@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index 94c3f4b7ee36..b5ed80d03953 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -186,10 +186,10 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, } static const struct pci_device_id pwrseq_m2_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107), - .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1103), .driver_data = (kernel_ulong_t)"qcom,wcn6855-bt" }, + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107), + .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" }, { } /* Sentinel */ }; From 2d5a7d406ecece5837af1e278ffbbf6c0315560a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 18 Jun 2026 10:45:12 +0200 Subject: [PATCH 4339/5214] power: sequencing: fix ABBA deadlock in pwrseq_device_unregister() The pwrseq core takes three locks in consistent order everywhere: pwrseq_sem -> pwrseq->rw_lock -> pwrseq->state_lock pwrseq_get() -> pwrseq_match_device() takes pwrseq_sem for reading, then rw_lock for reading. pwrseq_power_on()/pwrseq_power_off() take rw_lock for reading and then state_lock. pwrseq_device_unregister() is the only exception, it takes: state_lock, then rw_lock for writing and finally pwrseq_sem for writing. This created two potential ABBA deadlock situations that sashiko pointed out. - pwrseq_power_on/off() take rw_lock for reading then state_lock, while pwrseq_unregister() takes state_lock then rw_lock for writing - pwrseq_get() takes pwrseq_sem for reading then rw_lock for reading, while pwrseq_unregister() takes rw_lock for writing then pwrseq_sem for writing Reorder the unregister path to taking pwrseq_sem for writing -> rw_lock for writing and drop the state_lock entirely. This is safe as enable_count is only ever written under rw_lock held for read (via pwrseq_unit_enable()/disable(), reached only from pwrseq_power_on/off()), so holding rw_lock for writing already excludes every other writer and reader and the active-users WARN() stays race-free without state_lock. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Closes: https://sashiko.dev/#/patchset/20260616151049.1705503-1-vulab%40iscas.ac.cn Link: https://patch.msgid.link/20260618-pwrseq-abba-deadlock-v1-1-943a3fd81c06@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index c1c2265160a3..02f42da91598 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -543,15 +543,18 @@ void pwrseq_device_unregister(struct pwrseq_device *pwrseq) struct device *dev = &pwrseq->dev; struct pwrseq_target *target; - scoped_guard(mutex, &pwrseq->state_lock) { + scoped_guard(rwsem_write, &pwrseq_sem) { guard(rwsem_write)(&pwrseq->rw_lock); + /* + * Holding rw_lock for write excludes all power on/off callers + * (they hold it for read), so it's safe to read enable_count + * here without taking the state_lock. + */ list_for_each_entry(target, &pwrseq->targets, list) WARN(target->unit->enable_count, "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n"); - guard(rwsem_write)(&pwrseq_sem); - device_del(dev); } From 99dfa46baba29513d1094c8f30bc86c6ef88543a Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:19 +0800 Subject: [PATCH 4340/5214] gpiolib: initialize return value in gpiochip_set_multiple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpiochip_set_multiple() falls back to setting lines one by one when the chip does not provide set_multiple(). If the fallback path receives an empty mask, the loop is skipped and ret is returned without being initialized. Initialize ret to 0 so an empty mask is treated as a successful no-op. Fixes: 9b407312755f ("gpiolib: rework the wrapper around gpio_chip::set_multiple()") Signed-off-by: Ruoyu Wang Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260620155319.79994-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8a5ff78a1149..e5fb60111151 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3803,7 +3803,7 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { unsigned int i; - int ret; + int ret = 0; lockdep_assert_held(&gc->gpiodev->srcu); From 9068c631d5af20000d873e4f299fa0bac4e294d9 Mon Sep 17 00:00:00 2001 From: Igor Putko Date: Thu, 18 Jun 2026 18:56:24 +0300 Subject: [PATCH 4341/5214] gpio: tb10x: fix struct tb10x_gpio kernel-doc Fix build warning by adding the missing structure name and description to the kernel-doc comment block. Signed-off-by: Igor Putko Link: https://patch.msgid.link/20260618155626.18751-2-igorpetindev@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tb10x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 3c8fd322a713..705bfd80a8d0 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -33,6 +33,7 @@ /** + * struct tb10x_gpio - TB10x GPIO controller structure * @base: register base address * @domain: IRQ domain of GPIO generated interrupts managed by this controller * @irq: Interrupt line of parent interrupt controller From faaa1e1155833e7d4ce7e3cfaf64c0d636b190db Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 11:47:37 -0700 Subject: [PATCH 4342/5214] drm/edid: fix OOB read in drm_parse_tiled_block() drm_parse_tiled_block() casts the DisplayID block to a struct displayid_tiled_block and reads the full fixed layout up to tile->topology_id[7] without checking block->num_bytes. The DisplayID iterator only validates the declared payload length, so a crafted EDID can advertise a tiled-display block (tag DATA_BLOCK_TILED_DISPLAY, or DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY for v2.0) with a small num_bytes at the end of a DisplayID extension. The read then runs past the end of the exact-sized kmemdup()'d EDID allocation, a heap out-of-bounds read. Reject blocks shorter than the spec's 22-byte tiled payload before reading the fixed struct, as drm_parse_vesa_mso_data() already does. BUG: KASAN: slab-out-of-bounds in drm_edid_connector_update Read of size 2 at addr ffff888010077700 by task exploit/147 dump_stack_lvl (lib/dump_stack.c:94 ...) print_report (mm/kasan/report.c:378 ...) kasan_report (mm/kasan/report.c:595) drm_edid_connector_update (drivers/gpu/drm/drm_edid.c:7581) bochs_connector_helper_get_modes (drivers/gpu/drm/tiny/bochs.c:574) drm_helper_probe_single_connector_modes (drivers/gpu/drm/drm_probe_helper.c:426) status_store (drivers/gpu/drm/drm_sysfs.c:219) ... vfs_write (fs/read_write.c:595 fs/read_write.c:688) ksys_write (fs/read_write.c:740) Fixes: 40d9b043a89e ("drm/connector: store tile information from displayid (v3)") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260615184737.899892-1-xmei5@asu.edu Signed-off-by: Jani Nikula --- drivers/gpu/drm/drm_edid.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 404208bf23a6..df3c25bac761 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -7575,6 +7575,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector, u8 num_v_tile, num_h_tile; struct drm_tile_group *tg; + /* tiled block payload per spec: cap 1 + topo 3 + size 4 + bezel 5 + id 9 = 22 */ + if (block->num_bytes < 22) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected tiled block size %u\n", + connector->base.id, connector->name, block->num_bytes); + return; + } + w = tile->tile_size[0] | tile->tile_size[1] << 8; h = tile->tile_size[2] | tile->tile_size[3] << 8; From b70f007a9fc665ee988683fd5085ab34e2c10ad3 Mon Sep 17 00:00:00 2001 From: Chris Aherin Date: Sun, 21 Jun 2026 19:12:10 -0500 Subject: [PATCH 4343/5214] ALSA: hda/realtek: Add quirk for Lenovo Yoga 7 16IAP7 The Yoga 7 16IAP7 (board LNVNB161216, codec SSID 17aa:386a) has pin complex 0x17 (bass speakers) wrongly reported as unconnected, causing only one of four speaker pins (0x14) to be configured and resulting in mono/tinny audio. SOF corrupts the PCI subsystem ID to 17aa:0000, preventing SND_PCI_QUIRK from matching. HDA_CODEC_QUIRK is used instead, which matches against codec->core.subsystem_id read directly from the HDA codec register and unaffected by the SOF bug. Applies ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, the same fixup used for the Yoga 7 14IAL7, which corrects pin 0x17's default configuration and enables both speaker pairs. Signed-off-by: Chris Aherin Link: https://patch.msgid.link/20260622001210.20553-1-chrisaherin@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2df491335a76..c83c1654ed45 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7755,6 +7755,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ From 6efb1897209ab50940c58384d15a68fd4212821c Mon Sep 17 00:00:00 2001 From: Dirk Su Date: Mon, 22 Jun 2026 15:20:04 +0800 Subject: [PATCH 4344/5214] ALSA: hda/realtek: Add LED fixup for HP EliteBook 6 G2i Laptops The HP EliteBook 6 G2i laptops requires specific LED control method ALC236_FIXUP_HP_GPIO_LED to work Signed-off-by: Dirk Su Link: https://patch.msgid.link/20260622072019.56351-1-dirk.su@canonical.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c83c1654ed45..e88ca720f4e5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7328,6 +7328,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f2d, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f2e, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8f37, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f38, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f39, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f3a, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f3c, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8f3d, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), From 134844856c399bfa9462a159dcf860bfdb748055 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:57 +0200 Subject: [PATCH 4345/5214] drm/sysfb: Do not page-align visible size of the framebuffer Only return the actually visible size of the system framebuffer in drm_sysfb_get_visible_size_si(). Drivers use this size value for reserving access to framebuffer memory. Increasing the value can make later attempts to do so fail. Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reviewed-by: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Link: https://patch.msgid.link/20260618084327.46567-2-tzimmermann@suse.de --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 749290196c6a..361b7233600c 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -67,7 +67,7 @@ EXPORT_SYMBOL(drm_sysfb_get_stride_si); u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { - u64 vsize = PAGE_ALIGN(height * stride); + u64 vsize = height * stride; return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size); } From b771974988ec7ce077a7246fa0fa588c246fe581 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:58 +0200 Subject: [PATCH 4346/5214] drm/sysfb: Avoid possible truncation with calculating visible size Calculating the visible size of the system framebuffer can result in truncation of the result. The calculation uses 32-bit arithmetics, which can overflow if the values for height and stride are large. Fix the issue by multiplying with mul_u32_u32(). Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260617114027.1F2A71F000E9@smtp.kernel.org/ Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260618084327.46567-3-tzimmermann@suse.de --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 361b7233600c..8b14eaa304c0 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -67,7 +68,7 @@ EXPORT_SYMBOL(drm_sysfb_get_stride_si); u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { - u64 vsize = height * stride; + u64 vsize = mul_u32_u32(height, stride); return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size); } From 7bab0f09d753f098977bbba3955d694c2e2c25da Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:59 +0200 Subject: [PATCH 4347/5214] drm/sysfb: Return errno code from drm_sysfb_get_visible_size() Change the return type of drm_sysfb_get_visible_size() to s64 so that it returns a possible errno code from _get_validated_size0(). Fix callers to handle the errno code. The currently returned unsigned type converts an errno code to a very large size value, which drivers interpret as visible size of the system framebuffer. Later efforts to reserve the framebuffer resource fail. The bug has been present since efidrm and vesadrm got merged. It was then part of each driver. Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reviewed-by: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Link: https://patch.msgid.link/20260618084327.46567-4-tzimmermann@suse.de --- drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 2 +- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 2 +- drivers/gpu/drm/sysfb/efidrm.c | 7 ++++--- drivers/gpu/drm/sysfb/vesadrm.c | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index b14df5b54bc9..d48b92a903f7 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -50,7 +50,7 @@ struct resource *drm_sysfb_get_memory_si(struct drm_device *dev, int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si, const struct drm_format_info *format, unsigned int width, unsigned int height, u64 size); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size); #endif diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 8b14eaa304c0..042d1b796696 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -65,7 +65,7 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si } EXPORT_SYMBOL(drm_sysfb_get_stride_si); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { u64 vsize = mul_u32_u32(height, stride); diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c index a335c94a7bd7..d5adef5deb63 100644 --- a/drivers/gpu/drm/sysfb/efidrm.c +++ b/drivers/gpu/drm/sysfb/efidrm.c @@ -150,7 +150,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize, mem_flags; + s64 vsize; + u64 mem_flags; struct resource resbuf; struct resource *res; struct efidrm_device *efi; @@ -204,8 +205,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c index 4e00113e5c77..d60a67fc1d5b 100644 --- a/drivers/gpu/drm/sysfb/vesadrm.c +++ b/drivers/gpu/drm/sysfb/vesadrm.c @@ -400,7 +400,7 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize; + s64 vsize; struct resource resbuf; struct resource *res; struct vesadrm_device *vesa; @@ -455,8 +455,8 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); From 9206b22fb959f4a9cf1921f34aed0df1dcb1ab04 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:42:00 +0200 Subject: [PATCH 4348/5214] drm/sysfb: Avoid truncating maximum stride Passing a maximum as 64-bit type to drm_sysfb_get_validated_int0() can truncate the value to 32 bits. Use drm_sysfb_get_validated_size0(), which uses 64-bit arithmetics. Then test the returned stride against the limits of int to avoid truncations in the returned value. A valid stride is in the range of [1, INT_MAX] inclusive. Signed-off-by: Thomas Zimmermann Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260617114016.5A5991F000E9@smtp.kernel.org/ Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260618084327.46567-5-tzimmermann@suse.de --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 042d1b796696..1c479ce0e503 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -57,11 +57,17 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si unsigned int width, unsigned int height, u64 size) { u64 lfb_linelength = si->lfb_linelength; + s64 stride; if (!lfb_linelength) lfb_linelength = drm_format_info_min_pitch(format, 0, width); - return drm_sysfb_get_validated_int0(dev, "stride", lfb_linelength, div64_u64(size, height)); + stride = drm_sysfb_get_validated_size0(dev, "stride", lfb_linelength, + div64_u64(size, height)); + if (stride < INT_MIN || stride > INT_MAX) + return -EINVAL; + + return (int)stride; /* stride or negative errno code */ } EXPORT_SYMBOL(drm_sysfb_get_stride_si); From 803d09a554055aba160a62abd1e4b1260b899dc1 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Mon, 22 Jun 2026 16:11:36 +0800 Subject: [PATCH 4349/5214] erofs: handle 48-bit blocks_hi for compressed inodes Combine i_nb.blocks_hi with i_u.blocks_lo when computing inode->i_blocks for compressed inodes, mirroring the startblk_hi handling for unencoded inodes a few lines above. Also evaluate the shift in u64 to avoid truncation. Fixes: efb2aef569b3 ("erofs: add encoded extent on-disk definition") Fixes: 1d191b4ca51d ("erofs: implement encoded extent metadata") Reviewed-by: Gao Xiang Signed-off-by: Zhan Xusheng Signed-off-by: Gao Xiang --- fs/erofs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index 1df38b7c82a7..45afe5c50de8 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -191,8 +191,9 @@ static int erofs_read_inode(struct inode *inode) err = -EFSCORRUPTED; goto err_out; } else { - inode->i_blocks = le32_to_cpu(copied.i_u.blocks_lo) << - (sb->s_blocksize_bits - 9); + inode->i_blocks = (le32_to_cpu(copied.i_u.blocks_lo) | + ((u64)le16_to_cpu(copied.i_nb.blocks_hi) << 32)) << + (sb->s_blocksize_bits - 9); } if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { From 493765b8e922a506e09e22e80b6cc9ff05e8295b Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Tue, 19 May 2026 17:49:48 +0200 Subject: [PATCH 4350/5214] ALSA: hda/realtek: Fix noisy mic for Clevo V6xxAW Add a PCI quirk to reduce the volume of the internal microphone to prevent extremely noisy signal. Signed-off-by: Aaron Erhardt Signed-off-by: Werner Sembach Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260519155047.106096-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e88ca720f4e5..0d932f6ae3e8 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7603,6 +7603,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x6480, "Clevo V6xxAW", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), From 269f2b43fae692d1f3988c9f888a6301aa537b82 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Mon, 22 Jun 2026 18:33:48 +0800 Subject: [PATCH 4351/5214] time: Fix off-by-one in compat settimeofday() usec validation The compat version of settimeofday() uses '>' instead of '>=' when validating tv_usec against USEC_PER_SEC, allowing the value 1000000 to pass the check. After the subsequent conversion to nanoseconds (tv_nsec *= NSEC_PER_USEC), this results in tv_nsec == NSEC_PER_SEC, which violates the timespec invariant that tv_nsec must be strictly less than NSEC_PER_SEC. The native settimeofday() was already fixed in commit ce4abda5e126 ("time: Fix off-by-one in settimeofday() usec validation"), but the compat counterpart was missed. Fix it by using '>=' to reject tv_usec values outside the valid range [0, USEC_PER_SEC - 1]. Fixes: 5e0fb1b57bea ("y2038: time: avoid timespec usage in settimeofday()") Signed-off-by: Wang Yan Signed-off-by: Thomas Gleixner Acked-by: Arnd Bergmann Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260622103348.120255-1-wangyan01@kylinos.cn --- kernel/time/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/time.c b/kernel/time/time.c index 771cef87ad3b..0dd63a91e7c5 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -251,7 +251,7 @@ COMPAT_SYSCALL_DEFINE2(settimeofday, struct old_timeval32 __user *, tv, get_user(new_ts.tv_nsec, &tv->tv_usec)) return -EFAULT; - if (new_ts.tv_nsec > USEC_PER_SEC || new_ts.tv_nsec < 0) + if (new_ts.tv_nsec >= USEC_PER_SEC || new_ts.tv_nsec < 0) return -EINVAL; new_ts.tv_nsec *= NSEC_PER_USEC; From b81dde13cc163450dcb402dcc915ef13ba241e01 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2026 16:47:44 +0200 Subject: [PATCH 4352/5214] debugobjects: Plug race against a concurrent OOM disable syzbot reported a puzzling splat: WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup. Though the provided console log lacks this warning and instead has the following a few seconds before the splat: ODEBUG: Out of memory. ODEBUG disabled So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects: debug_object_assert_init() if (!debug_objects_enabled) return; obj = alloc(); if (!obj) { // Out of memory debug_objects_enabled = false; free_objects(); obj = lookup_or_alloc(); // The lookup failed because the other side // removed the objects, so this returns // an error code as the object in question // is not statically initialized if (!IS_ERR_OR_NULL(obj)) return; if (!obj) { debug_oom(); return; } print(...) if (!debug_objects_enabled) return; fixup(...) The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional. This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object. Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places. Fixes: b84d435cc228 ("debugobjects: Extend to assert that an object is initialized") Reported-by: syzbot+5e8dda76ca21dae314b6@syzkaller.appspotmail.com Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/874iiwlzlb.ffs@fw13 --- lib/debugobjects.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 6fb00e08a4e2..877f7675ba26 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -894,6 +894,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr) } raw_spin_unlock_irqrestore(&db->lock, flags); + + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. + */ + if (!debug_objects_enabled) + return 0; + debug_print_object(&o, "activate"); switch (o.state) { @@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr) return; } + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. Don't run the fixup + * as it might turn a valid object useless. See for example + * hrtimer_fixup_assert_init(). + */ + if (!debug_objects_enabled) + return; + /* Object is neither tracked nor static. It's not initialized. */ debug_print_object(&o, "assert_init"); debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE); From bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 22 Jun 2026 05:32:54 -1000 Subject: [PATCH 4353/5214] sched_ext: Move sources under kernel/sched/ext/ The sched_ext sources had grown to ten ext* files directly under kernel/sched/. Move them into a new kernel/sched/ext/ subdirectory and drop the now-redundant ext_ prefix. ext.c/h keep their names. kernel/sched/ext.{c,h} -> kernel/sched/ext/ext.{c,h} kernel/sched/ext_internal.h -> kernel/sched/ext/internal.h kernel/sched/ext_types.h -> kernel/sched/ext/types.h kernel/sched/ext_idle.{c,h} -> kernel/sched/ext/idle.{c,h} kernel/sched/ext_cid.{c,h} -> kernel/sched/ext/cid.{c,h} kernel/sched/ext_arena.{c,h} -> kernel/sched/ext/arena.{c,h} The include paths in build_policy.c and sched.h, the MAINTAINERS glob, and a few documentation and comment references are updated to match. No code or symbol changes. Suggested-by: Linus Torvalds Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- Documentation/scheduler/sched-ext.rst | 8 ++++---- MAINTAINERS | 2 +- kernel/sched/build_policy.c | 18 +++++++++--------- kernel/sched/{ext_arena.c => ext/arena.c} | 0 kernel/sched/{ext_arena.h => ext/arena.h} | 0 kernel/sched/{ext_cid.c => ext/cid.c} | 2 +- kernel/sched/{ext_cid.h => ext/cid.h} | 2 +- kernel/sched/{ => ext}/ext.c | 4 ++-- kernel/sched/{ => ext}/ext.h | 0 kernel/sched/{ext_idle.c => ext/idle.c} | 0 kernel/sched/{ext_idle.h => ext/idle.h} | 0 .../sched/{ext_internal.h => ext/internal.h} | 0 kernel/sched/{ext_types.h => ext/types.h} | 0 kernel/sched/sched.h | 2 +- tools/sched_ext/include/scx/cid.bpf.h | 6 +++--- 15 files changed, 22 insertions(+), 22 deletions(-) rename kernel/sched/{ext_arena.c => ext/arena.c} (100%) rename kernel/sched/{ext_arena.h => ext/arena.h} (100%) rename kernel/sched/{ext_cid.c => ext/cid.c} (99%) rename kernel/sched/{ext_cid.h => ext/cid.h} (99%) rename kernel/sched/{ => ext}/ext.c (99%) rename kernel/sched/{ => ext}/ext.h (100%) rename kernel/sched/{ext_idle.c => ext/idle.c} (100%) rename kernel/sched/{ext_idle.h => ext/idle.h} (100%) rename kernel/sched/{ext_internal.h => ext/internal.h} (100%) rename kernel/sched/{ext_types.h => ext/types.h} (100%) diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst index c4f59c08d8a4..4b1ffd03f516 100644 --- a/Documentation/scheduler/sched-ext.rst +++ b/Documentation/scheduler/sched-ext.rst @@ -114,7 +114,7 @@ counters. Each counter occupies one ``name value`` line: SCX_EV_INSERT_NOT_OWNED 0 SCX_EV_SUB_BYPASS_DISPATCH 0 -The counters are described in ``kernel/sched/ext_internal.h``; briefly: +The counters are described in ``kernel/sched/ext/internal.h``; briefly: * ``SCX_EV_SELECT_CPU_FALLBACK``: ops.select_cpu() returned a CPU unusable by the task and the core scheduler silently picked a fallback CPU. @@ -496,11 +496,11 @@ Where to Look * ``include/linux/sched/ext.h`` defines the core data structures, ops table and constants. -* ``kernel/sched/ext.c`` contains sched_ext core implementation and helpers. +* ``kernel/sched/ext/ext.c`` contains sched_ext core implementation and helpers. The functions prefixed with ``scx_bpf_`` can be called from the BPF scheduler. -* ``kernel/sched/ext_idle.c`` contains the built-in idle CPU selection policy. +* ``kernel/sched/ext/idle.c`` contains the built-in idle CPU selection policy. * ``tools/sched_ext/`` hosts example BPF scheduler implementations. @@ -557,7 +557,7 @@ ABI Instability The APIs provided by sched_ext to BPF schedulers programs have no stability guarantees. This includes the ops table callbacks and constants defined in ``include/linux/sched/ext.h``, as well as the ``scx_bpf_`` kfuncs defined in -``kernel/sched/ext.c`` and ``kernel/sched/ext_idle.c``. +``kernel/sched/ext/ext.c`` and ``kernel/sched/ext/idle.c``. While we will attempt to provide a relatively stable API surface when possible, they are subject to change without warning between kernel diff --git a/MAINTAINERS b/MAINTAINERS index ba45953bb805..2d853aadd919 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24164,7 +24164,7 @@ S: Maintained W: https://github.com/sched-ext/scx T: git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git F: include/linux/sched/ext.h -F: kernel/sched/ext* +F: kernel/sched/ext/ F: tools/sched_ext/ F: tools/testing/selftests/sched_ext diff --git a/kernel/sched/build_policy.c b/kernel/sched/build_policy.c index 067979a7b69e..d74b54f81992 100644 --- a/kernel/sched/build_policy.c +++ b/kernel/sched/build_policy.c @@ -61,15 +61,15 @@ # include # include # include -# include "ext_types.h" -# include "ext_internal.h" -# include "ext_cid.h" -# include "ext_arena.h" -# include "ext_idle.h" -# include "ext.c" -# include "ext_cid.c" -# include "ext_arena.c" -# include "ext_idle.c" +# include "ext/types.h" +# include "ext/internal.h" +# include "ext/cid.h" +# include "ext/arena.h" +# include "ext/idle.h" +# include "ext/ext.c" +# include "ext/cid.c" +# include "ext/arena.c" +# include "ext/idle.c" #endif #include "syscalls.c" diff --git a/kernel/sched/ext_arena.c b/kernel/sched/ext/arena.c similarity index 100% rename from kernel/sched/ext_arena.c rename to kernel/sched/ext/arena.c diff --git a/kernel/sched/ext_arena.h b/kernel/sched/ext/arena.h similarity index 100% rename from kernel/sched/ext_arena.h rename to kernel/sched/ext/arena.h diff --git a/kernel/sched/ext_cid.c b/kernel/sched/ext/cid.c similarity index 99% rename from kernel/sched/ext_cid.c rename to kernel/sched/ext/cid.c index 66944a7ef79d..aeaea88f34c5 100644 --- a/kernel/sched/ext_cid.c +++ b/kernel/sched/ext/cid.c @@ -71,7 +71,7 @@ static s32 scx_cid_arrays_alloc(void) * scx_cid_init - build the cid mapping * @sch: the scx_sched being initialized; used as the scx_error() target * - * See "Topological CPU IDs" in ext_cid.h for the model. Walk online cpus by + * See "Topological CPU IDs" in cid.h for the model. Walk online cpus by * intersection at each level (parent_scratch & this_level_mask), which keeps * containment correct by construction and naturally splits a physical LLC * straddling two NUMA nodes into two LLC units. The caller must hold diff --git a/kernel/sched/ext_cid.h b/kernel/sched/ext/cid.h similarity index 99% rename from kernel/sched/ext_cid.h rename to kernel/sched/ext/cid.h index 5745e5785e89..6e657fd147b0 100644 --- a/kernel/sched/ext_cid.h +++ b/kernel/sched/ext/cid.h @@ -43,7 +43,7 @@ struct scx_sched; * possible-but-not-online cpus and carries all-(-1) topo info (see * scx_cid_topo); callers detect it via the -1 sentinels. * - * See the comment above the table definitions in ext_cid.c for the + * See the comment above the table definitions in cid.c for the * memory-ordering and visibility contract. */ extern s16 *scx_cid_to_cpu_tbl; diff --git a/kernel/sched/ext.c b/kernel/sched/ext/ext.c similarity index 99% rename from kernel/sched/ext.c rename to kernel/sched/ext/ext.c index 0db6fa2daea3..00fe6cc6d7e2 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext/ext.c @@ -513,7 +513,7 @@ do { \ } while (0) /* - * Flipped on enable per sch->is_cid_type. Declared in ext_internal.h so + * Flipped on enable per sch->is_cid_type. Declared in internal.h so * subsystem inlines can read it. */ DEFINE_STATIC_KEY_FALSE(__scx_is_cid_type); @@ -608,7 +608,7 @@ do { \ * @cpumask: new cpumask * * For cid-form schedulers, translate @cpumask to a cmask via the per-cpu - * scratch in ext_cid.c and dispatch through the ops_cid union view. Caller + * scratch in cid.c and dispatch through the ops_cid union view. Caller * must hold @rq's rq lock so this_cpu_ptr is stable across the call. */ static inline void scx_call_op_set_cpumask(struct scx_sched *sch, struct rq *rq, diff --git a/kernel/sched/ext.h b/kernel/sched/ext/ext.h similarity index 100% rename from kernel/sched/ext.h rename to kernel/sched/ext/ext.h diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext/idle.c similarity index 100% rename from kernel/sched/ext_idle.c rename to kernel/sched/ext/idle.c diff --git a/kernel/sched/ext_idle.h b/kernel/sched/ext/idle.h similarity index 100% rename from kernel/sched/ext_idle.h rename to kernel/sched/ext/idle.h diff --git a/kernel/sched/ext_internal.h b/kernel/sched/ext/internal.h similarity index 100% rename from kernel/sched/ext_internal.h rename to kernel/sched/ext/internal.h diff --git a/kernel/sched/ext_types.h b/kernel/sched/ext/types.h similarity index 100% rename from kernel/sched/ext_types.h rename to kernel/sched/ext/types.h diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c7c2dea65edd..56acf502ba26 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -4211,6 +4211,6 @@ DEFINE_CLASS(sched_change, struct sched_change_ctx *, DEFINE_CLASS_IS_UNCONDITIONAL(sched_change) -#include "ext.h" +#include "ext/ext.h" #endif /* _KERNEL_SCHED_SCHED_H */ diff --git a/tools/sched_ext/include/scx/cid.bpf.h b/tools/sched_ext/include/scx/cid.bpf.h index 9d89bb57e201..db247e42fb45 100644 --- a/tools/sched_ext/include/scx/cid.bpf.h +++ b/tools/sched_ext/include/scx/cid.bpf.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * BPF-side helpers for cids and cmasks. See kernel/sched/ext_cid.h for the + * BPF-side helpers for cids and cmasks. See kernel/sched/ext/cid.h for the * authoritative layout and semantics. The BPF-side helpers use the cmask_* * naming (no scx_ prefix); cmask is the SCX bitmap type so the prefix is * redundant in BPF code. Atomics use __sync_val_compare_and_swap and every @@ -33,7 +33,7 @@ #endif /* - * Mirrors SCX_CMASK_NR_WORDS in kernel/sched/ext_types.h. The u64 cast keeps + * Mirrors SCX_CMASK_NR_WORDS in kernel/sched/ext/types.h. The u64 cast keeps * the +63 from wrapping when @nr_cids is near U32_MAX, so cmask_reframe() * bounds-checking the result against alloc_words catches the overflow instead * of seeing a small value. @@ -281,7 +281,7 @@ static __always_inline void cmask_zero(struct scx_cmask __arena *m) /* * BPF_-prefixed to avoid colliding with the kernel's anonymous CMASK_OP_* - * enum in ext_cid.c, which is exported via BTF and reachable through + * enum in ext/cid.c, which is exported via BTF and reachable through * vmlinux.h. */ enum { From 37738fdf2ab1e504d1c63ce5bc0aeb6452d8f057 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Thu, 18 Jun 2026 10:13:52 +0800 Subject: [PATCH 4354/5214] irqchip/imgpdc: Fix resource leak, add missing chained handler cleanup on remove The driver allocates domain generic chips using irq_alloc_domain_generic_chips() during probe and sets up chained handlers using irq_set_chained_handler_and_data(). However, on driver removal, the generic chips are not freed and the chained handlers are not removed. The generic chips remain on the global gc_list and may later be accessed by generic interrupt chip suspend, resume, or shutdown callbacks after the driver has been removed, potentially resulting in a use-after-free and kernel crash. The chained handlers that were installed in probe for peripheral and syswake interrupts are also left dangling, which can lead to spurious interrupts accessing freed memory. Fix these issues by: - Setting IRQ_DOMAIN_FLAG_DESTROY_GC flag in domain->flags, so the core code automatically removes generic chips when irq_domain_remove() is called - Clearing all chained handlers with NULL in pdc_intc_remove() Fixes: b6ef9161e43a ("irq-imgpdc: add ImgTec PDC irqchip driver") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618021352.661773-1-fffsqian@163.com --- drivers/irqchip/irq-imgpdc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index e9ef2f5a7207..4feef4ab5fec 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -378,6 +378,7 @@ static int pdc_intc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot add IRQ domain\n"); return -ENOMEM; } + priv->domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC; /* * Set up 2 generic irq chips with 2 chip types. @@ -465,6 +466,11 @@ static void pdc_intc_remove(struct platform_device *pdev) { struct pdc_intc_priv *priv = platform_get_drvdata(pdev); + for (unsigned int i = 0; i < priv->nr_perips; ++i) + irq_set_chained_handler_and_data(priv->perip_irqs[i], NULL, NULL); + + irq_set_chained_handler_and_data(priv->syswake_irq, NULL, NULL); + irq_domain_remove(priv->domain); } From cf81b260916c92cf610c907d41627a175e86e37d Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Wed, 17 Jun 2026 16:47:53 +0200 Subject: [PATCH 4355/5214] ASoC: cs530x: Fix expected MCLK rates for CS5302/4/8 When this driver was first added, it accepted rates of 24.56 MHz and 22.572 MHz for the MCLK when PLL bypass is enabled. These rates seem to have no basis in the datasheets and were thus replaced with 45.1584 MHz and 49.152 MHz, respectively, in commit e7ab858390f2 ("ASoC: cs530x: Correct MCLK reference frequency values"). While the new rates are indeed correct for the CS4xxx ICs[0][1][2][3], they are incorrect for the CS530x ICs the driver was originally written to support as the MCLK frequencies are halved there[4][5][6]. Fix this by checking against the correct type-appropriate rates. While at it, drop the CS530X_SYSCLK_REF_* macros. They arguably confuse more than they help, especially as they are not applicable to the cs5302/4/8. [0]: https://statics.cirrus.com/pubs/proDatasheet/CS4282P_DS1318F1.pdf [1]: https://statics.cirrus.com/pubs/proDatasheet/CS4302P_DS1315F1.pdf [2]: https://statics.cirrus.com/pubs/proDatasheet/CS4304P_DS1316F1.pdf [3]: https://statics.cirrus.com/pubs/proDatasheet/CS4308P_DS1317F1.pdf [4]: https://statics.cirrus.com/pubs/proDatasheet/CS5302P_DS1312F1.pdf [5]: https://statics.cirrus.com/pubs/proDatasheet/CS5304P_DS1313F1.pdf [6]: https://statics.cirrus.com/pubs/proDatasheet/CS5308P_DS1314F1.pdf Fixes: 2884c29152c0 ("ASoC: cs530x: Support for cs530x ADCs") Signed-off-by: Ahmad Fatoum Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260617-cs530x-mclk-v1-1-0215b5f1a0a4@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/cs530x.c | 29 ++++++++++++++++++++++++----- sound/soc/codecs/cs530x.h | 6 ------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 18b5ff75feec..2c7e33135911 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component) return 0; } +static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x, + unsigned int freq) +{ + /* + * All these chips support 48 kHz- and 44.1 kHz-related sample rates, + * but they differ in what MCLK frequency is required for achieving + * the sample rate. + */ + switch (cs530x->devtype) { + case CS4282: + case CS4302: + case CS4304: + case CS4308: + return freq == 49152000 || freq == 45158400; + case CS5302: + case CS5304: + case CS5308: + return freq == 24576000 || freq == 22579200; + } + + return false; +} + static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, switch (source) { case CS530X_SYSCLK_SRC_MCLK: - switch (freq) { - case CS530X_SYSCLK_REF_45_1MHZ: - case CS530X_SYSCLK_REF_49_1MHZ: - break; - default: + if (!cs530x_mclk_freq_is_valid(cs530x, freq)) { dev_err(component->dev, "Invalid MCLK source rate %d\n", freq); return -EINVAL; } diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1e2f6a7a589c..18aa4dfd0c86 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -200,12 +200,6 @@ /* IN_VOL_CTL5 and OUT_VOL_CTL5 */ #define CS530X_INOUT_VU BIT(0) -/* MCLK Reference Source Frequency */ -/* 41KHz related */ -#define CS530X_SYSCLK_REF_45_1MHZ 45158400 -/* 48KHz related */ -#define CS530X_SYSCLK_REF_49_1MHZ 49152000 - /* System Clock Source */ #define CS530X_SYSCLK_SRC_MCLK 0 #define CS530X_SYSCLK_SRC_PLL 1 From 3287a1881ca528b89b964d9fa6d28880d277d9e2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 17 Jun 2026 14:00:38 +0100 Subject: [PATCH 4356/5214] perf bpf: Fix up build failure due to change of btf_vlen() return type Fix: util/btf.c: In function '__btf_type__find_member_by_name': util/btf.c:19:43: error: comparison of integer expressions of different signedness: 'int' and '__u32' {aka 'unsigned int'} [-Werror=sign-compare] 19 | for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) { | ^ builtin-trace.c: In function 'syscall_arg__strtoul_btf_enum': builtin-trace.c:967:27: error: comparison of integer expressions of different signedness: 'int' and '__u32' {aka 'unsigned int'} [-Werror=sign-compare] 967 | for (int i = 0; i < btf_vlen(bt); ++i, ++be) { | ^ by making the variable the same type as the function. Committer note: Add an extra hunk from Alan Maguire, fixing btf_enum_scnprintf(). Reviewed-by: Alan Maguire Signed-off-by: Mark Brown Cc: Alexei Starovoitov Cc: Linus Torvalds Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 +++--- tools/perf/util/btf.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 57f3f14c5d43..ba0f8749fc7d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -972,7 +972,7 @@ static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_ struct btf *btf = arg->trace->btf; struct btf_enum *be = btf_enum(bt); - for (int i = 0; i < btf_vlen(bt); ++i, ++be) { + for (u32 i = 0; i < btf_vlen(bt); ++i, ++be) { const char *name = btf__name_by_offset(btf, be->name_off); int max_len = max(size, strlen(name)); @@ -1017,9 +1017,9 @@ static bool syscall_arg__strtoul_btf_type(char *bf, size_t size, struct syscall_ static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val) { struct btf_enum *be = btf_enum(type); - const int nr_entries = btf_vlen(type); + const unsigned int nr_entries = btf_vlen(type); - for (int i = 0; i < nr_entries; ++i, ++be) { + for (unsigned int i = 0; i < nr_entries; ++i, ++be) { if (be->val == val) { return scnprintf(bf, size, "%s", btf__name_by_offset(btf, be->name_off)); diff --git a/tools/perf/util/btf.c b/tools/perf/util/btf.c index bb163fe87767..50d98f3e83bf 100644 --- a/tools/perf/util/btf.c +++ b/tools/perf/util/btf.c @@ -14,7 +14,7 @@ const struct btf_member *__btf_type__find_member_by_name(struct btf *btf, { const struct btf_type *t = btf__type_by_id(btf, type_id); const struct btf_member *m; - int i; + u32 i; for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) { const char *current_member_name = btf__name_by_offset(btf, m->name_off); From dd015b566d505d698386103e9c80b739c7336eb8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 11:06:51 -0700 Subject: [PATCH 4357/5214] fscrypt: Fix key setup in edge case with multiple data unit sizes The addition of support for customizable data unit sizes introduced an edge case where a file's contents can be en/decrypted with the wrong data unit size. It occurs when there are multiple v2 policies that: - Have *different* data unit sizes, via the log2_data_unit_size field - Share the same master_key_identifier, contents_encryption_mode, and either FSCRYPT_POLICY_FLAG_DIRECT_KEY, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, or FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 - Are being used on the same filesystem, which also must be mounted with the "inlinecrypt" mount option. Fortunately this edge case doesn't actually occur in practice. I just found it via code review. But it needs to be fixed regardless. The bug is caused by the data unit size not being fully considered when blk_crypto_keys are cached in mk_direct_keys, mk_iv_ino_lblk_32_keys, and mk_iv_ino_lblk_64_keys. They're differentiated only by master key, encryption mode, and flag. However, each one actually has a data unit size too. Only the first data unit size that is cached is used. To fix this, start using the data unit size to differentiate the cached keys. For several reasons, including avoiding increasing the size of struct fscrypt_master_key, just replace all three arrays with a single linked list instead of changing them into two-dimensional arrays. This works well when considering that in practice at most 2 entries are used across all three arrays, so it was already mostly wasted space. For simplicity, make the list also take over the publish/subscribe of the prepared key itself. That is, create separate list nodes for blk_crypto_keys vs crypto_skciphers, and add nodes to the list only when their key is actually prepared. (Note that the legacy fscrypt_direct_keys table in fs/crypto/keysetup_v1.c already works this way.) This eliminates the need for the additional memory barriers when reading and writing the fields of struct fscrypt_prepared_key. Note that I technically should have included the data unit size in the HKDF info string as well. But it's too late to change that. Fixes: 5b1188847180 ("fscrypt: support crypto data unit size less than filesystem block size") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618180652.52742-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 52 +++++++++------- fs/crypto/inline_crypt.c | 8 +-- fs/crypto/keyring.c | 23 ++++--- fs/crypto/keysetup.c | 118 ++++++++++++++++++++++-------------- 4 files changed, 120 insertions(+), 81 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8d3c278a7591..4263cac24b32 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -236,7 +236,7 @@ struct fscrypt_symlink_data { * @tfm: crypto API transform object * @blk_key: key for blk-crypto * - * Normally only one of the fields will be non-NULL. + * Only one of the fields is non-NULL. */ struct fscrypt_prepared_key { struct crypto_sync_skcipher *tfm; @@ -245,6 +245,15 @@ struct fscrypt_prepared_key { #endif }; +/* An entry in the linked list ->mk_mode_keys */ +struct fscrypt_mode_key { + struct fscrypt_prepared_key key; + struct list_head link; + u8 hkdf_context; + u8 mode_num; + u8 data_unit_bits; +}; + /* * fscrypt_inode_info - the "encryption key" for an inode * @@ -430,20 +439,12 @@ int fscrypt_derive_sw_secret(struct super_block *sb, * @prep_key, depending on which encryption implementation the file will use. */ static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - /* - * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). - * I.e., in some cases (namely, if this prep_key is a per-mode - * encryption key) another task can publish blk_key or tfm concurrently, - * executing a RELEASE barrier. We need to use smp_load_acquire() here - * to safely ACQUIRE the memory the other task published. - */ if (fscrypt_using_inline_encryption(ci)) - return smp_load_acquire(&prep_key->blk_key) != NULL; - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->blk_key != NULL; + return prep_key->tfm != NULL; } #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -486,10 +487,10 @@ fscrypt_derive_sw_secret(struct super_block *sb, } static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->tfm != NULL; } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -577,8 +578,8 @@ struct fscrypt_master_key { /* * Active and structural reference counts. An active ref guarantees * that the struct continues to exist, continues to be in the keyring - * ->s_master_keys, and that any embedded subkeys (e.g. - * ->mk_direct_keys) that have been prepared continue to exist. + * ->s_master_keys, and that any non-file-scoped subkeys (e.g. + * ->mk_mode_keys) that have been prepared continue to exist. * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_present being true, and @@ -632,12 +633,21 @@ struct fscrypt_master_key { spinlock_t mk_decrypted_inodes_lock; /* - * Per-mode encryption keys for the various types of encryption policies - * that use them. Allocated and derived on-demand. + * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num, + * data_unit_bits, inlinecrypt) combinations that are in use for this + * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY, + * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY]. + * + * This is a linked list and not a hash table because in practice + * there's just a single encryption policy per master key, using + * _at most_ 2 nodes in this list. Per-file keys don't use this at all. + * + * This list is append-only until the master key is fully removed, at + * which time the list is cleared. Before then, + * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use + * the RCU read lock together with ->mk_sem held for read. */ - struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct list_head mk_mode_keys; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 37d42d357925..47324062fee5 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, goto fail; } - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->blk_key with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->blk_key, blk_key); + prep_key->blk_key = blk_key; return 0; fail: diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index be8e6e8011f2..5fe0d985a58d 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -87,14 +87,14 @@ void fscrypt_put_master_key(struct fscrypt_master_key *mk) void fscrypt_put_master_key_activeref(struct super_block *sb, struct fscrypt_master_key *mk) { - size_t i; + struct fscrypt_mode_key *node, *tmp; if (!refcount_dec_and_test(&mk->mk_active_refs)) return; /* * No active references left, so complete the full removal of this * fscrypt_master_key struct by removing it from the keyring and - * destroying any subkeys embedded in it. + * destroying any non-file-scoped subkeys. */ if (WARN_ON_ONCE(!sb->s_master_keys)) @@ -110,13 +110,16 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); - for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { - fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + /* + * Destroy any non-file-scoped subkeys. Since ->mk_active_refs == 0, + * they're no longer referenced by any inodes. Nor can key setup run + * and use them again. So they're no longer needed. (This implies no + * concurrent readers, so we don't need list_del_rcu() for example.) + */ + list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) { + fscrypt_destroy_prepared_key(sb, &node->key); + list_del(&node->link); + kfree(node); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); @@ -445,6 +448,8 @@ static int add_new_master_key(struct super_block *sb, INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); + INIT_LIST_HEAD(&mk->mk_mode_keys); + if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { err = allocate_master_key_users_keyring(mk); if (err) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ce327bfdada4..f905f9f94bdd 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm = tfm; return 0; } @@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context == hkdf_context && + node->mode_num == mode_num && + node->data_unit_bits == ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode = ci->ci_inode; @@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; bool use_hw_wrapped_key = false; @@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, use_hw_wrapped_key = true; } - prep_key = &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key = *prep_key; return 0; } - mutex_lock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key = *prep_key; + return 0; + } + + new_node = kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context = hkdf_context; + new_node->mode_num = mode_num; + new_node->data_unit_bits = ci->ci_data_unit_bits; + prep_key = &new_node->key; if (use_hw_wrapped_key) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, ci); - if (err) - goto out_unlock; - goto done_unlock; + } else { + static_assert(sizeof(mode_num) == 1); + static_assert(sizeof(sb->s_uuid) == 16); + static_assert(sizeof(hkdf_info) == 17); + hkdf_info[hkdf_infolen++] = mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen += sizeof(sb->s_uuid); + } + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, mode->keysize); } - - BUILD_BUG_ON(sizeof(mode_num) != 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); - BUILD_BUG_ON(sizeof(hkdf_info) != 17); - hkdf_info[hkdf_infolen++] = mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen += sizeof(sb->s_uuid); + if (err) { + kfree(new_node); + return err; } - fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, - hkdf_infolen, mode_key, mode->keysize); - err = fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key = *prep_key; - err = 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } /* @@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, { int err; - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; @@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err = setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); From 696c030e1e3438955aba443b308ee8b6faa3983e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 15:19:21 -0700 Subject: [PATCH 4358/5214] fscrypt: Replace mk_users keyring with simple list Change mk_users (the set of user claims to an fscrypt master key) from a 'struct key' keyring to a simple linked list. It's still a collection of 'struct key' for quota tracking. It was originally thought to be natural that a collection of 'struct key' should be held in a 'struct key' keyring. In reality, it's just been causing problems, similar to how using 'struct key' for the filesystem keyring caused problems and was removed in commit d7e7b9af104c ("fscrypt: stop using keyrings subsystem for fscrypt_master_key"). Commit d3a7bd420076 ("fscrypt: clear keyring before calling key_put()") fixed mk_users cleanup to be synchronous. But that apparently wasn't enough: the keyring subsystem's redundant locking is still generating lockdep false positives due to the interaction with filesystem reclaim. With the simple list, the redundant locking and lockdep issue goes away. Of course, searching a linked list is linear-time whereas the 'struct key' keyring used a fancy constant-time associative array. But that's fine here, since in practice there's just one entry in the list. In fact the new code is much faster in practice, since it's much smaller and doesn't have to convert the kuid_t into a string to search for it. Reported-by: syzbot+f55b043dacf43776b50c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f55b043dacf43776b50c Reported-by: Mohammed EL Kadiri Closes: https://lore.kernel.org/keyrings/20260614150041.21172-1-med08elkadiri@gmail.com/ Fixes: 23c688b54016 ("fscrypt: allow unprivileged users to add/remove keys for v2 policies") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618221921.87896-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 32 ++++-- fs/crypto/keyring.c | 216 +++++++++++++++--------------------- 2 files changed, 113 insertions(+), 135 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4263cac24b32..0053b5c45412 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -496,6 +496,19 @@ fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, /* keyring.c */ +/* + * fscrypt_master_key_user - a user's claim to a master key + */ +struct fscrypt_master_key_user { + struct list_head link; + kuid_t uid; + /* + * This 'struct key' contains no secret. It exists solely to charge the + * appropriate user's key quota. + */ + struct key *quota_key; +}; + /* * fscrypt_master_key_secret - secret key material of an in-use master key */ @@ -611,19 +624,18 @@ struct fscrypt_master_key { struct fscrypt_key_specifier mk_spec; /* - * Keyring which contains a key of type 'key_type_fscrypt_user' for each - * user who has added this key. Normally each key will be added by just - * one user, but it's possible that multiple users share a key, and in - * that case we need to keep track of those users so that one user can't - * remove the key before the others want it removed too. + * List of user claims to this key (struct fscrypt_master_key_user). + * Normally each key will be added by just one user, but it's possible + * that multiple users share a key, and in that case we need to keep + * track of those users so that one user can't remove the key before the + * others want it removed too. * - * This is NULL for v1 policy keys; those can only be added by root. + * Used only for v2 policy keys. v1 policy keys can be added only by + * root, so user tracking doesn't apply to them. * - * Locking: protected by ->mk_sem. (We don't just rely on the keyrings - * subsystem semaphore ->mk_users->sem, as we need support for atomic - * search+insert along with proper synchronization with other fields.) + * Locking: protected by ->mk_sem. */ - struct key *mk_users; + struct list_head mk_users; /* * List of inodes that were unlocked using this key. This allows the diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 5fe0d985a58d..38b73e703073 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -65,22 +65,19 @@ static void fscrypt_free_master_key(struct rcu_head *head) kfree_sensitive(mk); } +static void clear_mk_users(struct fscrypt_master_key *mk); + void fscrypt_put_master_key(struct fscrypt_master_key *mk) { if (!refcount_dec_and_test(&mk->mk_struct_refs)) return; /* - * No structural references left, so free ->mk_users, and also free the + * No structural references left, so clear ->mk_users, and also free the * fscrypt_master_key struct itself after an RCU grace period ensures * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 0); - if (mk->mk_users) { - /* Clear the keyring so the quota gets released right away. */ - keyring_clear(mk->mk_users); - key_put(mk->mk_users); - mk->mk_users = NULL; - } + clear_mk_users(mk); call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } @@ -165,8 +162,8 @@ static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) } /* - * Type of key in ->mk_users. Each key of this type represents a particular - * user who has added a particular master key. + * Type of fscrypt_master_key_user::quota_key. This contains no secret; it + * exists solely to charge a user's key quota. * * Note that the name of this key type really should be something like * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen @@ -180,30 +177,9 @@ static struct key_type key_type_fscrypt_user = { .describe = fscrypt_user_key_describe, }; -#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ - CONST_STRLEN("-users") + 1) - #define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) -static void format_mk_users_keyring_description( - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - sprintf(description, "fscrypt-%*phN-users", - FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); -} - -static void format_mk_user_description( - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - - sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, - mk_identifier, __kuid_val(current_fsuid())); -} - /* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mutex. */ static int allocate_filesystem_keyring(struct super_block *sb) { @@ -338,91 +314,94 @@ fscrypt_find_master_key(struct super_block *sb, return mk; } -static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) +/* Find the current user's claim in ->mk_users. ->mk_sem must be held. */ +static struct fscrypt_master_key_user * +find_master_key_user(struct fscrypt_master_key *mk) { - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; - struct key *keyring; + struct fscrypt_master_key_user *mk_user; + kuid_t uid = current_fsuid(); - format_mk_users_keyring_description(description, - mk->mk_spec.u.identifier); - keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), KEY_POS_SEARCH | - KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) - return PTR_ERR(keyring); - - mk->mk_users = keyring; - return 0; -} - -/* - * Find the current user's "key" in the master key's ->mk_users. - * Returns ERR_PTR(-ENOKEY) if not found. - */ -static struct key *find_master_key_user(struct fscrypt_master_key *mk) -{ - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - key_ref_t keyref; - - format_mk_user_description(description, mk->mk_spec.u.identifier); - - /* - * We need to mark the keyring reference as "possessed" so that we - * acquire permission to search it, via the KEY_POS_SEARCH permission. - */ - keyref = keyring_search(make_key_ref(mk->mk_users, true /*possessed*/), - &key_type_fscrypt_user, description, false); - if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) == -EAGAIN || /* not found */ - PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ - keyref = ERR_PTR(-ENOKEY); - return ERR_CAST(keyref); + list_for_each_entry(mk_user, &mk->mk_users, link) { + if (uid_eq(mk_user->uid, uid)) + return mk_user; } - return key_ref_to_ptr(keyref); + return NULL; } /* - * Give the current user a "key" in ->mk_users. This charges the user's quota + * Give the current user a claim in ->mk_users. This charges the user's quota * and marks the master key as added by the current user, so that it cannot be * removed by another user with the key. Either ->mk_sem must be held for * write, or the master key must be still undergoing initialization. */ static int add_master_key_user(struct fscrypt_master_key *mk) { + kuid_t uid = current_fsuid(); char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - struct key *mk_user; + struct key *quota_key; + struct fscrypt_master_key_user *mk_user; int err; - format_mk_user_description(description, mk->mk_spec.u.identifier); - mk_user = key_alloc(&key_type_fscrypt_user, description, - current_fsuid(), current_gid(), current_cred(), - KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); + snprintf(description, sizeof(description), "%*phN.uid.%u", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk->mk_spec.u.identifier, + __kuid_val(uid)); + quota_key = key_alloc(&key_type_fscrypt_user, description, uid, + current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(quota_key)) + return PTR_ERR(quota_key); - err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); - key_put(mk_user); - return err; + err = key_instantiate_and_link(quota_key, NULL, 0, NULL, NULL); + if (err) { + key_put(quota_key); + return err; + } + + mk_user = kzalloc_obj(*mk_user); + if (!mk_user) { + key_put(quota_key); + return -ENOMEM; + } + mk_user->uid = uid; + mk_user->quota_key = quota_key; + list_add(&mk_user->link, &mk->mk_users); + return 0; +} + +static void unlink_and_free_mk_user(struct fscrypt_master_key_user *mk_user) +{ + list_del(&mk_user->link); + key_put(mk_user->quota_key); + kfree(mk_user); } /* - * Remove the current user's "key" from ->mk_users. + * Remove the current user's claim from ->mk_users. * ->mk_sem must be held for write. * - * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + * Returns 0 if removed or -ENOKEY if not found. */ static int remove_master_key_user(struct fscrypt_master_key *mk) { - struct key *mk_user; - int err; + struct fscrypt_master_key_user *mk_user; mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - err = key_unlink(mk->mk_users, mk_user); - key_put(mk_user); - return err; + if (!mk_user) + return -ENOKEY; + unlink_and_free_mk_user(mk_user); + return 0; +} + +/* + * Clear ->mk_users. Either ->mk_sem must be held for write, or 'mk' must have + * no structural references left. + */ +static void clear_mk_users(struct fscrypt_master_key *mk) +{ + struct fscrypt_master_key_user *mk_user, *tmp; + + list_for_each_entry_safe(mk_user, tmp, &mk->mk_users, link) + unlink_and_free_mk_user(mk_user); } /* @@ -445,15 +424,14 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec = *mk_spec; + INIT_LIST_HEAD(&mk->mk_users); + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); INIT_LIST_HEAD(&mk->mk_mode_keys); if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err = allocate_master_key_users_keyring(mk); - if (err) - goto out_put; err = add_master_key_user(mk); if (err) goto out_put; @@ -482,19 +460,13 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, int err; /* - * If the current user is already in ->mk_users, then there's nothing to - * do. Otherwise, we need to add the user to ->mk_users. (Neither is - * applicable for v1 policy keys, which have NULL ->mk_users.) + * For v2 policy keys (FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER): If the current + * user is already in ->mk_users, then there's nothing to do. + * Otherwise, add the user to ->mk_users. */ - if (mk->mk_users) { - struct key *mk_user = find_master_key_user(mk); - - if (mk_user != ERR_PTR(-ENOKEY)) { - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - key_put(mk_user); + if (mk->mk_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + if (find_master_key_user(mk) != NULL) return 0; - } err = add_master_key_user(mk); if (err) return err; @@ -893,7 +865,6 @@ int fscrypt_verify_key_added(struct super_block *sb, { struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; - struct key *mk_user; int err; mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; @@ -905,13 +876,10 @@ int fscrypt_verify_key_added(struct super_block *sb, goto out; } down_read(&mk->mk_sem); - mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) { - err = PTR_ERR(mk_user); - } else { - key_put(mk_user); + if (find_master_key_user(mk) != NULL) err = 0; - } + else + err = -ENOKEY; up_read(&mk->mk_sem); fscrypt_put_master_key(mk); out: @@ -1103,16 +1071,18 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) down_write(&mk->mk_sem); /* If relevant, remove current user's (or all users) claim to the key */ - if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { - if (all_users) - err = keyring_clear(mk->mk_users); - else + if (!list_empty(&mk->mk_users)) { + if (all_users) { + clear_mk_users(mk); + err = 0; + } else { err = remove_master_key_user(mk); + } if (err) { up_write(&mk->mk_sem); goto out_put_key; } - if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (!list_empty(&mk->mk_users)) { /* * Other users have still added the key too. We removed * the current user's claim to the key, but we still @@ -1198,6 +1168,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) struct super_block *sb = file_inode(filp)->i_sb; struct fscrypt_get_key_status_arg arg; struct fscrypt_master_key *mk; + kuid_t uid; + const struct fscrypt_master_key_user *mk_user; int err; if (copy_from_user(&arg, uarg, sizeof(arg))) @@ -1230,19 +1202,13 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } arg.status = FSCRYPT_KEY_STATUS_PRESENT; - if (mk->mk_users) { - struct key *mk_user; - arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; - mk_user = find_master_key_user(mk); - if (!IS_ERR(mk_user)) { + uid = current_fsuid(); + list_for_each_entry(mk_user, &mk->mk_users, link) { + arg.user_count++; + if (uid_eq(mk_user->uid, uid)) arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; - key_put(mk_user); - } else if (mk_user != ERR_PTR(-ENOKEY)) { - err = PTR_ERR(mk_user); - goto out_release_key; - } } err = 0; out_release_key: From b952837f734c3a627877bf922408dac04588a643 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 6 May 2026 09:07:09 +0800 Subject: [PATCH 4359/5214] f2fs: annotate lockless last_time[] accesses f2fs stores mount-wide activity timestamps in sbi->last_time[] and samples them from background discard, GC, and balance paths without a dedicated lock. The timestamps are used as best-effort heuristics to decide whether background work should run now or sleep a bit longer. The current helpers use plain loads and stores, so KCSAN can report races between frequent foreground updates and background readers. Exact freshness is not required here, but the intentional lockless accesses should be marked explicitly. Use WRITE_ONCE() in f2fs_update_time() and READ_ONCE() in f2fs_time_over() and f2fs_time_to_wait(). This preserves the existing heuristic behavior and avoids adding locking to hot paths. Signed-off-by: Cen Zhang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e555c1d794df..6d2048cffa66 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2125,12 +2125,12 @@ static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) { unsigned long now = jiffies; - sbi->last_time[type] = now; + WRITE_ONCE(sbi->last_time[type], now); /* DISCARD_TIME and GC_TIME are based on REQ_TIME */ if (type == REQ_TIME) { - sbi->last_time[DISCARD_TIME] = now; - sbi->last_time[GC_TIME] = now; + WRITE_ONCE(sbi->last_time[DISCARD_TIME], now); + WRITE_ONCE(sbi->last_time[GC_TIME], now); } } @@ -2138,7 +2138,7 @@ static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type) { unsigned long interval = sbi->interval_time[type] * HZ; - return time_after(jiffies, sbi->last_time[type] + interval); + return time_after(jiffies, READ_ONCE(sbi->last_time[type]) + interval); } static inline unsigned int f2fs_time_to_wait(struct f2fs_sb_info *sbi, @@ -2148,7 +2148,7 @@ static inline unsigned int f2fs_time_to_wait(struct f2fs_sb_info *sbi, unsigned int wait_ms = 0; long delta; - delta = (sbi->last_time[type] + interval) - jiffies; + delta = (READ_ONCE(sbi->last_time[type]) + interval) - jiffies; if (delta > 0) wait_ms = jiffies_to_msecs(delta); From fb645a976f53b175a49bdf52cfcd2c56f4b1456a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Tue, 5 May 2026 20:55:10 +0800 Subject: [PATCH 4360/5214] f2fs: annotate lockless NAT counter reads nat_cnt[] is updated while callers hold nat_tree_lock, but F2FS samples the counters locklessly in f2fs_available_free_memory(), excess_dirty_nats(), and excess_cached_nats(). Those helpers only steer cache reclaim and background sync heuristics; they do not control NAT entry lifetime or checkpoint correctness. Document the intent with data_race(READ_ONCE()) and a short comment instead of adding locking to the balance path. Signed-off-by: Cen Zhang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 +++++- fs/f2fs/node.h | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b1247de25411..a464d5ee1124 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -73,7 +73,11 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type) sizeof(struct free_nid)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { - mem_size = (nm_i->nat_cnt[TOTAL_NAT] * + /* + * nat_cnt[] is heuristic accounting. Sample it locklessly here + * to avoid taking nat_tree_lock in the balance path. + */ + mem_size = (data_race(READ_ONCE(nm_i->nat_cnt[TOTAL_NAT])) * sizeof(struct nat_entry)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); if (excess_cached_nats(sbi)) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index bcf2034e4263..5e114f352099 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -129,13 +129,17 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne, static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi) { - return NM_I(sbi)->nat_cnt[DIRTY_NAT] >= NM_I(sbi)->max_nid * + /* nat_cnt[] is heuristic accounting sampled locklessly here. */ + return data_race(READ_ONCE(NM_I(sbi)->nat_cnt[DIRTY_NAT])) >= + NM_I(sbi)->max_nid * NM_I(sbi)->dirty_nats_ratio / 100; } static inline bool excess_cached_nats(struct f2fs_sb_info *sbi) { - return NM_I(sbi)->nat_cnt[TOTAL_NAT] >= DEF_NAT_CACHE_THRESHOLD; + /* nat_cnt[] is heuristic accounting sampled locklessly here. */ + return data_race(READ_ONCE(NM_I(sbi)->nat_cnt[TOTAL_NAT])) >= + DEF_NAT_CACHE_THRESHOLD; } enum mem_type { From f6b24566035835dc22796f24b1e8738581840b17 Mon Sep 17 00:00:00 2001 From: liujinbao1 Date: Wed, 6 May 2026 17:57:31 +0800 Subject: [PATCH 4361/5214] f2fs: Add trace_f2fs_fault_report Add trace_f2fs_fault_report to trigger reporting upon f2fs_bug_on, need_fsck, stop_checkpoint, and handle_eio. Since f2fs_bug_on and need_fsck can be triggered in hundreds of scenarios, define set_sbi_flag as a macro to help capture the effective fault function and line number. Signed-off-by: shengyong1 Signed-off-by: liujinbao1 Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 18 +++++++++++++++++- fs/f2fs/super.c | 9 +++++++++ include/trace/events/f2fs.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6d2048cffa66..b83ff4bd96ec 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -96,6 +96,15 @@ extern const char *f2fs_fault_name[FAULT_MAX]; #define DEFAULT_FAILURE_RETRY_COUNT 1 #endif +enum { + REPORT_FAULT_NEED_FSCK, + REPORT_FAULT_STOP_CP, + REPORT_FAULT_MAX, +}; + +void f2fs_fault_report(struct super_block *sb, unsigned int err_code, + const char *func, unsigned int data); + /* * For mount options */ @@ -2279,11 +2288,18 @@ static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type) return test_bit(type, &sbi->s_flag); } -static inline void set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) +static inline void __set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { set_bit(type, &sbi->s_flag); } +#define set_sbi_flag(sbi, type) \ +do { \ + __set_sbi_flag(sbi, type); \ + if ((type) == SBI_NEED_FSCK) \ + f2fs_fault_report(sbi->sb, REPORT_FAULT_NEED_FSCK, __func__, __LINE__); \ +} while (0) + static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { clear_bit(type, &sbi->s_flag); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index aab4345f3ee7..f4ab39b24a30 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4750,9 +4750,18 @@ static void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, */ } +void f2fs_fault_report(struct super_block *sb, unsigned int err_code, + const char *func, unsigned int data) +{ + trace_f2fs_fault_report(sb, err_code, func, data); +} + void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io, unsigned char reason) { + if (reason != STOP_CP_REASON_SHUTDOWN) + f2fs_fault_report(sbi->sb, REPORT_FAULT_STOP_CP, __func__, reason); + f2fs_build_fault_attr(sbi, 0, 0, FAULT_ALL); if (!end_io) f2fs_flush_merged_writes(sbi); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index b5188d2671d7..270c1a2c24c4 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -2595,6 +2595,34 @@ DEFINE_EVENT(f2fs_priority_update, f2fs_priority_restore, TP_ARGS(sbi, lock_name, is_write, p, orig_prio, new_prio) ); +TRACE_EVENT(f2fs_fault_report, + + TP_PROTO(struct super_block *sb, unsigned int err_code, + const char *func, unsigned int data), + + TP_ARGS(sb, err_code, func, data), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, err_code) + __string(func, func) + __field(unsigned int, data) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->err_code = err_code; + __assign_str(func); + __entry->data = data; + ), + + TP_printk("dev = (%d,%d), err_code = %u, func = %s, data = %u", + show_dev(__entry->dev), + __entry->err_code, + __get_str(func), + __entry->data) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ From 5dfb768326b95c6dd6554f34dec71b44a01a0bc8 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Thu, 14 May 2026 13:55:13 -0700 Subject: [PATCH 4362/5214] f2fs: optimize representative type determination in GC In large section mode, do_garbage_collect() previously determined the section's representative type by looking only at the first segment of the section. However, if data was fsynced into an area previously used as a node section, and this area is recovered during roll-forward recovery after sudden power off (SPO), GC would incorrectly assume the section's type based on an empty or obsolete first segment. This caused the recovered data segment to be misunderstood as being stuck inside a node section, triggering false inconsistency panics (Inconsistent segment type in SSA and SIT) and subsequent mount failures. This patch optimizes do_garbage_collect() to determine the section's representative type by identifying the first segment that actually contains valid blocks (valid_blocks > 0) during the main GC loop. This eliminates false alarms from empty/obsolete leading segments while maintaining strict section-level type consistency checks for genuine corruption. Signed-off-by: Daeho Jeong Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ba93010924c0..99bc59889825 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1754,9 +1754,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi); unsigned int sec_end_segno; int seg_freed = 0, migrated = 0; - unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ? - SUM_TYPE_DATA : SUM_TYPE_NODE; - unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE; + unsigned char type; + unsigned char data_type; int submitted = 0, sum_blk_cnt; if (__is_large_section(sbi)) { @@ -1855,6 +1854,12 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, migrated >= sbi->migration_granularity) continue; + if (migrated == 0) { + type = IS_DATASEG(get_seg_entry(sbi, cur_segno)->type) ? + SUM_TYPE_DATA : SUM_TYPE_NODE; + data_type = (type == SUM_TYPE_DATA) ? DATA : NODE; + } + sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno); if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) { f2fs_err(sbi, "Inconsistent segment (%u) type " From caac757a3d2dddeb88d79a7524ca0033fc337b3d Mon Sep 17 00:00:00 2001 From: liujinbao1 Date: Wed, 13 May 2026 22:14:36 +0800 Subject: [PATCH 4363/5214] f2fs: add iostat latency tracking for direct IO F2FS did not collect iostat latency for direct IO reads and writes, hook iomap_dio_ops.submit_io to bind an iostat context and record the submission timestamp. Replace bi_end_io with f2fs_dio_end_bio() to collect IO latency on completion before calling back to the original iomap_dio_bio_end_io(), to add iostat latency tracking support for F2FS DIO. Signed-off-by: shengyong1 Signed-off-by: liujinbao1 Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2d8b383ecf52..6edf0105dbc8 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -4784,6 +4784,30 @@ static bool f2fs_should_use_dio(struct inode *inode, struct kiocb *iocb, return true; } +#ifdef CONFIG_F2FS_IOSTAT +static void f2fs_dio_end_bio(struct bio *bio) +{ + struct bio_iostat_ctx *iostat_ctx = bio->bi_private; + void *orig_bi_private = iostat_ctx->post_read_ctx; + + iostat_update_and_unbind_ctx(bio); + bio->bi_private = orig_bi_private; + iomap_dio_bio_end_io(bio); +} + +static void f2fs_dio_iostat_start(struct f2fs_sb_info *sbi, struct bio *bio) +{ + void *bi_private = bio->bi_private; + + iostat_alloc_and_bind_ctx(sbi, bio, bi_private); + iostat_update_submit_ctx(bio, DATA); + bio->bi_end_io = f2fs_dio_end_bio; +} +#else +static inline void f2fs_dio_iostat_start(struct f2fs_sb_info *sbi, + struct bio *bio) {} +#endif + static int f2fs_dio_read_end_io(struct kiocb *iocb, ssize_t size, int error, unsigned int flags) { @@ -4796,8 +4820,18 @@ static int f2fs_dio_read_end_io(struct kiocb *iocb, ssize_t size, int error, return 0; } +static void f2fs_dio_read_submit_io(const struct iomap_iter *iter, + struct bio *bio, loff_t file_offset) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(iter->inode); + + f2fs_dio_iostat_start(sbi, bio); + blk_crypto_submit_bio(bio); +} + static const struct iomap_dio_ops f2fs_iomap_dio_read_ops = { .end_io = f2fs_dio_read_end_io, + .submit_io = f2fs_dio_read_submit_io, }; static ssize_t f2fs_dio_read_iter(struct kiocb *iocb, struct iov_iter *to) @@ -5078,6 +5112,7 @@ static void f2fs_dio_write_submit_io(const struct iomap_iter *iter, bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp); bio->bi_write_stream = f2fs_io_type_to_write_stream(bio->bi_bdev, DATA, temp); + f2fs_dio_iostat_start(sbi, bio); blk_crypto_submit_bio(bio); } From 47a60629caca7ea6475deb21d16112b35645b59f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 11 May 2026 00:30:33 +0800 Subject: [PATCH 4364/5214] f2fs: add logs in f2fs_disable_checkpoint() In order to troubleshoot in which step we may block on during mount w/ checkpoint_disable mount option. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f4ab39b24a30..629548d78db0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2633,6 +2633,9 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) /* check if we need more GC first */ unusable = f2fs_get_unusable_blocks(sbi); + + f2fs_info(sbi, "%s starts, unusable: %u", __func__, unusable); + if (!f2fs_disable_cp_again(sbi, unusable)) goto skip_gc; @@ -2640,6 +2643,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) sbi->gc_mode = GC_URGENT_HIGH; + f2fs_info(sbi, "%s: run f2fs_gc() to migrate blocks", __func__); + while (!f2fs_time_over(sbi, DISABLE_TIME)) { struct f2fs_gc_control gc_control = { .victim_segno = NULL_SEGNO, @@ -2660,6 +2665,12 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) break; } + f2fs_info(sbi, "%s: call sync_filesystem() to persist meta: %lld, node: %lld, data: %lld", + __func__, + get_pages(sbi, F2FS_DIRTY_META), + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DATA)); + ret = sync_filesystem(sbi->sb); if (ret || err) { err = ret ? ret : err; @@ -2673,6 +2684,12 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) } skip_gc: + f2fs_info(sbi, "%s: call f2fs_write_checkpoint(), meta: %lld, node: %lld, data: %lld", + __func__, + get_pages(sbi, F2FS_DIRTY_META), + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DATA)); + f2fs_down_write_trace(&sbi->gc_lock, &lc); cpc.reason = CP_PAUSE; set_sbi_flag(sbi, SBI_CP_DISABLED); @@ -2690,7 +2707,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) restore_flag: sbi->gc_mode = gc_mode; sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ - f2fs_info(sbi, "f2fs_disable_checkpoint() finish, err:%d", err); + f2fs_info(sbi, "%s finishes, err:%d", __func__, err); return err; } From 8b4468ec023d0d1b4669dfb867588997cc03a06b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 19 May 2026 01:14:38 +0000 Subject: [PATCH 4365/5214] f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs() When we mount device w/ gc_merge mount option, we may suffer below potential deadlock: Kworker GC trehad Truncator - f2fs_write_cache_pages - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback --- set writeback flag on folio - f2fs_outplace_write_data : cached folio in internal bio cache - f2fs_balance_fs - wake_up(gc_thread) : wake up gc thread to run foreground GC - finish_wait(fggc_wq) : wait on the waitqueue --- wait on GC thread to finish the work - truncate_inode_pages_range - __filemap_get_folio(, FGP_LOCK) --- lock folio - truncate_inode_partial_folio - folio_wait_writeback --- wait on writeback being cleared - do_garbage_collect - move_data_page - f2fs_get_lock_data_folio - lock on folio --- blocked on folio's lock In order to avoid such deadlock, let's call below functions to commit cached bios in GC_MERGE path of f2fs_balance_fs() as the same as we did in NOGC_MERGE path. - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); Cc: stable@kernel.org Fixes: 351df4b20115 ("f2fs: add segment operations") Cc: Ruipeng Qi Reported: Sandeep Dhavale Signed-off-by: Chao Yu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7c8ac62b1b0d..1ef4edb77078 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -445,6 +445,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) if (has_enough_free_secs(sbi, 0, 0)) return; + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && sbi->gc_thread->f2fs_gc_task) { DEFINE_WAIT(wait); @@ -464,13 +471,6 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .err_gc_skipped = false, .nr_free_secs = 1 }; - /* - * Submit all cached OPU/IPU DATA bios before triggering - * foreground GC to avoid potential deadlocks. - */ - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); - f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From e0288584baa5dc41df4a829a023c4c1b33fe53d7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 21 May 2026 10:15:05 +0800 Subject: [PATCH 4366/5214] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode - ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink - f2fs_gc - gc_data_segment - ra_data_block(cow_inode) - mapping = F2FS_I(inode)->atomic_inode->i_mapping : f2fs_is_cow_file(cow_inode) is true - f2fs_evict_inode(atomic_inode) - clear_inode_flag(fi->cow_inode, FI_COW_FILE) - F2FS_I(fi->cow_inode)->atomic_inode = NULL ... - truncate_inode_pages_final(atomic_inode) - f2fs_grab_cache_folio(mapping) : create folio in atomic_inode->mapping - clear_inode(atomic_inode) - BUG_ON(atomic_inode->i_data.nrpages) We need to add a reference on fi->atomic_inode before using its mapping field during garbage collection, otherwise, it will cause UAF issue. Cc: stable@kernel.org Cc: Daeho Jeong Cc: Sunmin Jeong Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 50 +++++++++++++++++++++++++++++++++++++++++-------- fs/f2fs/inode.c | 11 ++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 99bc59889825..69e0a867219d 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct dnode_of_data dn; struct folio *folio, *efolio; struct f2fs_io_info fio = { @@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index) }; int err = 0; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + folio = f2fs_grab_cache_folio(mapping, index, true); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { @@ -1299,11 +1312,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index) f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE); f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE); + if (atomic_inode) + iput(atomic_inode); return 0; put_encrypted_page: f2fs_put_page(fio.encrypted_page, true); put_folio: f2fs_folio_put(folio, true); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } @@ -1314,8 +1332,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index) static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .ino = inode->i_ino, @@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx, (fio.sbi->gc_mode != GC_URGENT_HIGH) ? CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + /* do not read out */ folio = f2fs_grab_cache_folio(mapping, bidx, false); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { err = -ENOENT; @@ -1473,6 +1504,9 @@ static int move_data_block(struct inode *inode, block_t bidx, folio_unlock(folio); folio_end_dropbehind(folio); folio_put(folio); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1694726122e6..939d75663a40 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -863,10 +863,15 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { - clear_inode_flag(fi->cow_inode, FI_COW_FILE); - F2FS_I(fi->cow_inode)->atomic_inode = NULL; - iput(fi->cow_inode); + struct inode *cow_inode = fi->cow_inode; + + f2fs_down_write(&F2FS_I(cow_inode)->i_sem); + clear_inode_flag(cow_inode, FI_COW_FILE); + F2FS_I(cow_inode)->atomic_inode = NULL; fi->cow_inode = NULL; + f2fs_up_write(&F2FS_I(cow_inode)->i_sem); + + iput(cow_inode); } trace_f2fs_evict_inode(inode); From 74c8d2ec95c59a5651ecd975c466998af1961fd4 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 20 May 2026 17:52:04 +0800 Subject: [PATCH 4367/5214] f2fs: fix missing read bio submission on large folio error f2fs_read_data_large_folio() can keep a read bio across multiple readahead folios. If a later folio hits an error before any of its blocks are added to the bio, folio_in_bio is false and the current error path returns immediately after ending that folio. This can leave the bio accumulated for earlier folios unsubmitted. Those folios then never receive read completion, and readers can wait indefinitely on the locked folios. Route errors through the common out path so any pending bio is submitted before returning. Stop consuming more readahead folios once an error is seen, and only wait on and clear the current folio when it was actually added to the bio. Cc: stable@kernel.org Fixes: a5d8b9d94e18 ("f2fs: fix to unlock folio in f2fs_read_data_large_folio()") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5d18119a214a..d83a21998ec2 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2529,7 +2529,7 @@ static int f2fs_read_data_large_folio(struct inode *inode, unsigned nrpages; struct f2fs_folio_state *ffs; int ret = 0; - bool folio_in_bio; + bool folio_in_bio = false; if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) { if (folio) @@ -2645,18 +2645,17 @@ static int f2fs_read_data_large_folio(struct inode *inode, } trace_f2fs_read_folio(folio, DATA); err_out: - if (!folio_in_bio) { + if (!folio_in_bio) folio_end_read(folio, !ret); - if (ret) - return ret; - } + if (ret) + goto out; if (rac) { folio = readahead_folio(rac); goto next_folio; } out: f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); - if (ret) { + if (ret && folio_in_bio) { /* Wait bios and clear uptodate. */ folio_lock(folio); folio_clear_uptodate(folio); From fcb05c26c2a67953b420739b85f49386efc9b6c0 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 20 May 2026 20:07:05 +0800 Subject: [PATCH 4368/5214] f2fs: pass correct iostat type for single node writes f2fs_write_single_node_folio() takes an io_type argument, but still passes FS_GC_NODE_IO to __write_node_folio() unconditionally. This was harmless while the helper was only used by f2fs_move_node_folio(), whose caller passes FS_GC_NODE_IO. However, commit fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") made f2fs_inline_data_fiemap() call the helper with FS_NODE_IO for FIEMAP_FLAG_SYNC. Honor the caller supplied io_type so inline-data FIEMAP sync writeback is accounted as normal node IO instead of GC node IO, while the GC path continues to pass FS_GC_NODE_IO explicitly. Cc: stable@kernel.org Fixes: fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index a464d5ee1124..cd5a394f6111 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1885,7 +1885,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode, } if (!__write_node_folio(node_folio, false, false, NULL, - &wbc, false, FS_GC_NODE_IO, NULL)) + &wbc, false, io_type, NULL)) err = -EAGAIN; goto release_folio; out_folio: From 5073c66a96a9c23c0c2533ed4ed06e42f9021208 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Thu, 21 May 2026 11:16:18 +0800 Subject: [PATCH 4369/5214] f2fs: validate compress cache inode only when enabled F2FS_COMPRESS_INO() uses NM_I(sbi)->max_nid as the synthetic inode number for the compressed page cache inode. That inode only exists when the compress_cache mount option is enabled. When compress_cache is disabled, max_nid is outside the valid inode range. A corrupted directory entry that points to ino == max_nid should therefore be rejected by f2fs_check_nid_range(). However, is_meta_ino() currently treats F2FS_COMPRESS_INO() as a meta inode unconditionally, so f2fs_iget() bypasses do_read_inode() and its nid range check, and instantiates a fake internal inode instead. Gate the compressed cache inode case on COMPRESS_CACHE, matching f2fs_init_compress_inode(). With compress_cache disabled, ino == max_nid now follows the normal inode path and is rejected as an out-of-range nid. Cc: stable@kernel.org Fixes: 6ce19aff0b8c ("f2fs: compress: add compress_inode to cache compressed blocks") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 939d75663a40..25f30b8eadc5 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -564,8 +564,13 @@ static int do_read_inode(struct inode *inode) static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino) { - return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) || - ino == F2FS_COMPRESS_INO(sbi); + if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) + return true; +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi)) + return true; +#endif + return false; } struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) From 484c84ecc1a497d09239ca3a12dff3cc832830ce Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Thu, 21 May 2026 18:37:48 +0800 Subject: [PATCH 4370/5214] f2fs: avoid false shutdown fserror reports F2FS records image errors and checkpoint-stop reasons through the same s_error_work worker. The ordinary f2fs_handle_error() path only updates s_errors, but the worker still calls fserror_report_shutdown() unconditionally after committing the superblock. As a result, a metadata corruption report can be followed by a synthetic FAN_FS_ERROR event with ESHUTDOWN and an invalid superblock file handle, even though no stop reason was recorded. Track whether save_stop_reason() actually changed the stop_reason array and only report the shutdown fserror for that case. Pure s_errors updates still commit the superblock, but no longer generate a false shutdown event. Fixes: 50faed607d32 ("f2fs: support to report fserror") Cc: stable@kernel.org Reviewed-by: Chao Yu Signed-off-by: Wenjie Qi Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b83ff4bd96ec..9f24287de4c3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1989,6 +1989,7 @@ struct f2fs_sb_info { unsigned char stop_reason[MAX_STOP_REASON]; /* stop reason */ spinlock_t error_lock; /* protect errors/stop_reason array */ bool error_dirty; /* errors of sb is dirty */ + bool stop_reason_dirty; /* stop reason of sb is dirty */ /* For reclaimed segs statistics per each GC mode */ unsigned int gc_segment_mode; /* GC state for reclaimed segments */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 629548d78db0..b277807c8185 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4626,6 +4626,7 @@ static void save_stop_reason(struct f2fs_sb_info *sbi, unsigned char reason) spin_lock_irqsave(&sbi->error_lock, flags); if (sbi->stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0)) sbi->stop_reason[reason]++; + sbi->stop_reason_dirty = true; spin_unlock_irqrestore(&sbi->error_lock, flags); } @@ -4633,6 +4634,7 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); unsigned long flags; + bool report_shutdown = false; int err; f2fs_down_write(&sbi->sb_lock); @@ -4644,6 +4646,10 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi) sbi->error_dirty = false; } memcpy(raw_super->s_stop_reason, sbi->stop_reason, MAX_STOP_REASON); + if (sbi->stop_reason_dirty) { + report_shutdown = true; + sbi->stop_reason_dirty = false; + } spin_unlock_irqrestore(&sbi->error_lock, flags); err = f2fs_commit_super(sbi, false); @@ -4654,7 +4660,8 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi) "f2fs_commit_super fails to record stop_reason, err:%d", err); - fserror_report_shutdown(sbi->sb, GFP_NOFS); + if (report_shutdown) + fserror_report_shutdown(sbi->sb, GFP_NOFS); } void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag) From a2b251259bf2bdf550893bac078e9ce0a10e76c9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 May 2026 14:59:11 +0800 Subject: [PATCH 4371/5214] Revert: "f2fs: check in-memory block bitmap" Commit 355e78913c0d ("f2fs: check in-memory block bitmap") added a mirror for valid block bitmap, it expects to detect in-memory corruption, however we never got any reports from the check points for almost decade, let's remove the code, it can help to save memories. Cc: wallentx Suggested-by: Jaegeuk Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 38 -------------------------------------- fs/f2fs/segment.h | 6 ------ 2 files changed, 44 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1ef4edb77078..9926ba9d77ba 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2455,9 +2455,6 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent unsigned int segno, block_t blkaddr, unsigned int offset, int del) { bool exist; -#ifdef CONFIG_F2FS_CHECK_FS - bool mir_exist; -#endif int i; int del_count = -del; @@ -2465,15 +2462,6 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent for (i = 0; i < del_count; i++) { exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map); -#ifdef CONFIG_F2FS_CHECK_FS - mir_exist = f2fs_test_and_clear_bit(offset + i, - se->cur_valid_map_mir); - if (unlikely(exist != mir_exist)) { - f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", - blkaddr + i, exist); - f2fs_bug_on(sbi, 1); - } -#endif if (unlikely(!exist)) { f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", blkaddr + i); f2fs_bug_on(sbi, 1); @@ -2514,20 +2502,8 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry unsigned int segno, block_t blkaddr, unsigned int offset, int del) { bool exist; -#ifdef CONFIG_F2FS_CHECK_FS - bool mir_exist; -#endif exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); -#ifdef CONFIG_F2FS_CHECK_FS - mir_exist = f2fs_test_and_set_bit(offset, - se->cur_valid_map_mir); - if (unlikely(exist != mir_exist)) { - f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", - blkaddr, exist); - f2fs_bug_on(sbi, 1); - } -#endif if (unlikely(exist)) { f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); @@ -4771,11 +4747,6 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) int offset, sit_offset; se = get_seg_entry(sbi, segno); -#ifdef CONFIG_F2FS_CHECK_FS - if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, - SIT_VBLOCK_MAP_SIZE)) - f2fs_bug_on(sbi, 1); -#endif /* add discard candidates */ if (!(cpc->reason & CP_DISCARD)) { @@ -4866,11 +4837,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) if (!sit_i->dirty_sentries_bitmap) return -ENOMEM; -#ifdef CONFIG_F2FS_CHECK_FS - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map); -#else bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); -#endif sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); if (!sit_i->bitmap) return -ENOMEM; @@ -4884,11 +4851,6 @@ static int build_sit_info(struct f2fs_sb_info *sbi) sit_i->sentries[start].ckpt_valid_map = bitmap; bitmap += SIT_VBLOCK_MAP_SIZE; -#ifdef CONFIG_F2FS_CHECK_FS - sit_i->sentries[start].cur_valid_map_mir = bitmap; - bitmap += SIT_VBLOCK_MAP_SIZE; -#endif - if (discard_map) { sit_i->sentries[start].discard_map = bitmap; bitmap += SIT_VBLOCK_MAP_SIZE; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 08735a165433..38a56b8ab2cc 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -177,9 +177,6 @@ struct seg_entry { unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */ unsigned int padding:6; /* padding */ unsigned char *cur_valid_map; /* validity bitmap of blocks */ -#ifdef CONFIG_F2FS_CHECK_FS - unsigned char *cur_valid_map_mir; /* mirror of current valid bitmap */ -#endif /* * # of valid blocks and the validity bitmap stored in the last * checkpoint pack. This information is used by the SSR mode. @@ -408,9 +405,6 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se, se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); -#ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); -#endif se->type = GET_SIT_TYPE(rs); se->mtime = le64_to_cpu(rs->mtime); } From 7967d563bcbaac60711f09d699bce95e91a7cb5c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 May 2026 14:59:12 +0800 Subject: [PATCH 4372/5214] Revert: "f2fs: check in-memory sit version bitmap" Commit ae27d62e6bef ("f2fs: check in-memory sit version bitmap") added a mirror for sit version bitmap, it expects to detect in-memory corruption, however we never got any reports from the check points for almost decade, let's remove the code, it can help to save memories. Cc: wallentx Suggested-by: Jaegeuk Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ------ fs/f2fs/segment.h | 16 ---------------- 2 files changed, 22 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9926ba9d77ba..993555042876 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -4882,11 +4882,6 @@ static int build_sit_info(struct f2fs_sb_info *sbi) return -ENOMEM; #ifdef CONFIG_F2FS_CHECK_FS - sit_i->sit_bitmap_mir = kmemdup(src_bitmap, - sit_bitmap_size, GFP_KERNEL); - if (!sit_i->sit_bitmap_mir) - return -ENOMEM; - sit_i->invalid_segmap = f2fs_kvzalloc(sbi, main_bitmap_size, GFP_KERNEL); if (!sit_i->invalid_segmap) @@ -5849,7 +5844,6 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) SM_I(sbi)->sit_info = NULL; kfree(sit_i->sit_bitmap); #ifdef CONFIG_F2FS_CHECK_FS - kfree(sit_i->sit_bitmap_mir); kvfree(sit_i->invalid_segmap); #endif kfree(sit_i); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 38a56b8ab2cc..b0c06b3580b4 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -206,8 +206,6 @@ struct sit_info { char *bitmap; /* all bitmaps pointer */ char *sit_bitmap; /* SIT bitmap pointer */ #ifdef CONFIG_F2FS_CHECK_FS - char *sit_bitmap_mir; /* SIT bitmap mirror */ - /* bitmap of segments to be ignored by GC in case of errors */ unsigned long *invalid_segmap; #endif @@ -549,11 +547,6 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, { struct sit_info *sit_i = SIT_I(sbi); -#ifdef CONFIG_F2FS_CHECK_FS - if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, - sit_i->bitmap_size)) - f2fs_bug_on(sbi, 1); -#endif memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); } @@ -894,12 +887,6 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, !valid_main_segno(sbi, start)); -#ifdef CONFIG_F2FS_CHECK_FS - if (f2fs_test_bit(offset, sit_i->sit_bitmap) != - f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) - f2fs_bug_on(sbi, 1); -#endif - /* calculate sit block address */ if (f2fs_test_bit(offset, sit_i->sit_bitmap)) blk_addr += sit_i->sit_blocks; @@ -925,9 +912,6 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) unsigned int block_off = SIT_BLOCK_OFFSET(start); f2fs_change_bit(block_off, sit_i->sit_bitmap); -#ifdef CONFIG_F2FS_CHECK_FS - f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); -#endif } static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi, From 8712353ed80f87271d732297567dcdbe4b84e8c7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 May 2026 15:53:29 +0800 Subject: [PATCH 4373/5214] f2fs: fix to do sanity check on f2fs_get_node_folio_ra() kernel BUG at fs/f2fs/file.c:845! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 0 UID: 0 PID: 5336 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:f2fs_do_truncate_blocks+0x1115/0x1140 fs/f2fs/file.c:845 Code: fc fc 90 0f 0b e8 8b 9d 9a fd 90 0f 0b e8 83 9d 9a fd 48 89 df 48 c7 c6 60 d1 1a 8c e8 54 f1 fc fc 90 0f 0b e8 6c 9d 9a fd 90 <0f> 0b e8 64 9d 9a fd 90 0f 0b 90 e9 93 fd ff ff e8 56 9d 9a fd 90 RSP: 0018:ffffc9000e4474c0 EFLAGS: 00010283 RAX: ffffffff842b1d34 RBX: 0000000000000003 RCX: 0000000000100000 RDX: ffffc9000f03a000 RSI: 0000000000035503 RDI: 0000000000035504 RBP: ffffc9000e447608 R08: ffff8880123b0000 R09: 0000000000000002 R10: 00000000fffffffe R11: 0000000000000002 R12: 0000000000000001 R13: 0000000000000000 R14: 1ffff92001c88ea0 R15: 00000000ffff039c FS: 00007f7e02ee36c0(0000) GS:ffff88808c887000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff0305c4000 CR3: 0000000012d4c000 CR4: 0000000000352ef0 Call Trace: f2fs_truncate_blocks+0x10a/0x300 fs/f2fs/file.c:882 f2fs_truncate+0x471/0x7c0 fs/f2fs/file.c:940 f2fs_evict_inode+0xa3f/0x1ac0 fs/f2fs/inode.c:907 evict+0x61e/0xb10 fs/inode.c:841 f2fs_fill_super+0x5f43/0x78f0 fs/f2fs/super.c:5224 get_tree_bdev_flags+0x431/0x4f0 fs/super.c:1694 vfs_get_tree+0x92/0x2a0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] do_new_mount_fc fs/namespace.c:3758 [inline] do_new_mount+0x341/0xd30 fs/namespace.c:3834 do_mount fs/namespace.c:4167 [inline] __do_sys_mount fs/namespace.c:4383 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4360 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f count = ADDRS_PER_PAGE(dn.node_folio, inode); count -= dn.ofs_in_node; f2fs_bug_on(sbi, count < 0); The fuzz test will trigger above bug_on in f2fs. The root cause should be: in the corrupted inode, there is a direct node which has the same ino and nid in its footer, so in f2fs_do_truncate_blocks(), after f2fs_get_dnode_of_data() finds such dnode: 1) ADDRS_PER_PAGE(dn.node_folio, inode) will return 923 2) once dn.ofs_in_node points to addr[923, 1017] Then it will trigger the system panic. Let's introduce NODE_TYPE_NON_IXNODE to indicate current node should not be an inode or xattr node, and then use it in below path to detect inconsistent node chain in inode mapping table: - f2fs_do_truncate_blocks - f2fs_get_dnode_of_data - f2fs_get_node_folio_ra - __get_node_folio - f2fs_sanity_check_node_footer - case NODE_TYPE_NON_IXNODE -> check whether it is inode|xnode Cc: stable@kernel.org Reported-by: syzbot+2488d8d751b27f7ce268@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69fa3697.170a0220.59368.0018.GAE@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9f24287de4c3..086bc5243979 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1592,6 +1592,7 @@ enum node_type { NODE_TYPE_INODE, NODE_TYPE_XATTR, NODE_TYPE_NON_INODE, + NODE_TYPE_NON_IXNODE, /* non inode and xnode */ }; /* a threshold of maximum elapsed time in critical region to print tracepoint */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index cd5a394f6111..38917e4a7319 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1548,6 +1548,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, if (is_inode) goto out_err; break; + case NODE_TYPE_NON_IXNODE: + if (is_inode || is_xnode) + goto out_err; + break; default: break; } @@ -1643,7 +1647,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start) struct f2fs_sb_info *sbi = F2FS_F_SB(parent); nid_t nid = get_nid(parent, start, false); - return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR); + return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE); } static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) From 28ebb922b99d415e8bf51bf8b065a14fd7672167 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Fri, 22 May 2026 14:12:06 +0800 Subject: [PATCH 4374/5214] f2fs: honor per-I/O write streams for direct writes io_uring can pass a per-I/O write stream through kiocb->ki_write_stream, and block direct I/O propagates that value to bio->bi_write_stream. F2FS added FDP stream mapping for DATA writes, but its direct write submit hook always rewrites bio->bi_write_stream from the inode write hint and F2FS temperature. As a result, a direct write with an explicit io_uring write_stream is submitted to the F2FS-selected stream instead of the user-requested stream. Validate an explicit write stream before starting F2FS direct I/O, pass the kiocb through the iomap private pointer, and preserve the per-I/O stream in the direct write bio. When no per-I/O stream is supplied, keep using the existing F2FS temperature-to-stream mapping. Fixes: 42f7a7a50a33 ("f2fs: map data writes to FDP streams") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6edf0105dbc8..69aad1060c48 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -5101,16 +5101,33 @@ static int f2fs_dio_write_end_io(struct kiocb *iocb, ssize_t size, int error, return 0; } +static bool f2fs_valid_write_stream(struct f2fs_sb_info *sbi, u8 write_stream) +{ + int i; + + if (!write_stream) + return true; + if (!f2fs_is_multi_device(sbi)) + return write_stream <= bdev_max_write_streams(sbi->sb->s_bdev); + + for (i = 0; i < sbi->s_ndevs; i++) + if (write_stream > bdev_max_write_streams(FDEV(i).bdev)) + return false; + return true; +} + static void f2fs_dio_write_submit_io(const struct iomap_iter *iter, struct bio *bio, loff_t file_offset) { struct inode *inode = iter->inode; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct kiocb *iocb = iter->private; enum log_type type = f2fs_rw_hint_to_seg_type(sbi, inode->i_write_hint); enum temp_type temp = f2fs_get_segment_temp(sbi, type); bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp); bio->bi_write_stream = + iocb->ki_write_stream ? iocb->ki_write_stream : f2fs_io_type_to_write_stream(bio->bi_bdev, DATA, temp); f2fs_dio_iostat_start(sbi, bio); blk_crypto_submit_bio(bio); @@ -5150,6 +5167,11 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from, trace_f2fs_direct_IO_enter(inode, iocb, count, WRITE); + if (!f2fs_valid_write_stream(sbi, iocb->ki_write_stream)) { + ret = -EINVAL; + goto out; + } + if (iocb->ki_flags & IOCB_NOWAIT) { /* f2fs_convert_inline_inode() and block allocation can block */ if (f2fs_has_inline_data(inode) || @@ -5187,7 +5209,7 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from, if (pos + count > inode->i_size) dio_flags |= IOMAP_DIO_FORCE_WAIT; dio = __iomap_dio_rw(iocb, from, &f2fs_iomap_ops, - &f2fs_iomap_dio_write_ops, dio_flags, NULL, 0); + &f2fs_iomap_dio_write_ops, dio_flags, iocb, 0); if (IS_ERR_OR_NULL(dio)) { ret = PTR_ERR_OR_ZERO(dio); if (ret == -ENOTBLK) From 846c499a65816d13f1186e3090e825e8bb8bcb8b Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Tue, 26 May 2026 13:35:57 +0800 Subject: [PATCH 4375/5214] f2fs: validate orphan inode entry count f2fs_recover_orphan_inodes() trusts the orphan block entry_count when replaying orphan inodes from the checkpoint pack. A corrupted entry_count larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the ino[] array and interpret footer or following data as inode numbers. On a crafted image, mounting an unpatched kernel can drive orphan recovery into f2fs_bug_on() and panic the kernel. Validate entry_count before consuming entries so corrupted checkpoint data fails the mount with -EFSCORRUPTED and requests fsck instead. Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be recorded in the superblock s_errors[] field. This gives fsck a persistent hint even though mount-time orphan recovery failure may leave no chance to persist SBI_NEED_FSCK through a checkpoint. Cc: stable@kernel.org Fixes: 127e670abfa7 ("f2fs: add checkpoint operations") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 14 +++++++++++++- include/linux/f2fs_fs.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c00a6b6ebcbd..064f5b537423 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -943,6 +943,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) for (i = 0; i < orphan_blocks; i++) { struct folio *folio; struct f2fs_orphan_block *orphan_blk; + unsigned int entry_count; folio = f2fs_get_meta_folio(sbi, start_blk + i); if (IS_ERR(folio)) { @@ -951,7 +952,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) } orphan_blk = folio_address(folio); - for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { + entry_count = le32_to_cpu(orphan_blk->entry_count); + if (entry_count > F2FS_ORPHANS_PER_BLOCK) { + f2fs_err(sbi, "invalid orphan inode entry count %u", + entry_count); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN); + err = -EFSCORRUPTED; + f2fs_folio_put(folio, true); + goto out; + } + + for (j = 0; j < entry_count; j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); err = recover_orphan_inode(sbi, ino); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 829a59399dac..bb2b6cd5d507 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -107,6 +107,7 @@ enum f2fs_error { ERROR_CORRUPTED_XATTR, ERROR_INVALID_NODE_REFERENCE, ERROR_INCONSISTENT_NAT, + ERROR_INCONSISTENT_ORPHAN, ERROR_MAX, }; From 6d874b65aadce56ac78f76129dbcfc2599b638f8 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 27 May 2026 20:06:28 +0800 Subject: [PATCH 4376/5214] f2fs: keep atomic write retry from zeroing original data A partial atomic write reserves a block in the COW inode before reading the original data page for the untouched bytes in that page. If that read fails, write_begin returns an error but leaves the COW inode entry as NEW_ADDR. A retry of the same partial write then finds the COW entry, treats it as existing COW data, and f2fs_write_begin() zeroes the whole folio because blkaddr is NEW_ADDR. If the retry is committed, the bytes outside the retried write range are committed as zeroes instead of preserving the original file contents. Only use the COW inode as the read source when it already has a real data block. If the COW entry is still NEW_ADDR, treat it as a reservation to reuse: keep reading the old data from the original inode and avoid reserving or accounting the same atomic block again. Cc: stable@kernel.org Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d83a21998ec2..edda2ff72073 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3862,6 +3862,7 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, pgoff_t index = folio->index; int err = 0; block_t ori_blk_addr = NULL_ADDR; + bool cow_has_reserved_block = false; /* If pos is beyond the end of file, reserve a new block in COW inode */ if ((pos & PAGE_MASK) >= i_size_read(inode)) @@ -3871,9 +3872,11 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, err = __find_data_block(cow_inode, index, blk_addr); if (err) { return err; - } else if (*blk_addr != NULL_ADDR) { + } else if (__is_valid_data_blkaddr(*blk_addr)) { *use_cow = true; return 0; + } else if (*blk_addr == NEW_ADDR) { + cow_has_reserved_block = true; } if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) @@ -3886,10 +3889,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, reserve_block: /* Finally, we should reserve a new block in COW inode for the update */ - err = __reserve_data_block(cow_inode, index, blk_addr, node_changed); - if (err) - return err; - inc_atomic_write_cnt(inode); + if (!cow_has_reserved_block) { + err = __reserve_data_block(cow_inode, index, blk_addr, + node_changed); + if (err) + return err; + inc_atomic_write_cnt(inode); + } if (ori_blk_addr != NULL_ADDR) *blk_addr = ori_blk_addr; From 222bc257a151cc1b01d926199a0d5a7ba61d2e53 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Fri, 29 May 2026 10:29:24 +0800 Subject: [PATCH 4377/5214] f2fs: skip inode folio lookup for cached overwrite prepare_write_begin() first gets the inode folio and builds a dnode, then checks the read extent cache. For an ordinary overwrite of a non-inline and non-compressed file, an extent-cache hit already gives the data block address and the following path does not need to allocate or update any node state. Check the read extent cache before fetching the inode folio for that narrow case. Keep the existing paths for inline data, compressed files, and writes that may extend past EOF, where the helper may need inline conversion, compression preparation, or block reservation. This avoids a node-folio lookup in the buffered overwrite fast path when the mapping is already cached. In a QEMU/KASAN x86_64 VM, using a small buffered overwrite workload on an existing 1MiB file, median time improved as follows: 64-byte overwrites: 1724.93 ns/write -> 1560.24 ns/write 256-byte overwrites: 1713.38 ns/write -> 1577.85 ns/write Function profiling of 20k 64-byte overwrites showed f2fs_get_inode_folio() calls drop from 20004 to 4. Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index edda2ff72073..cea34b4595b1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3719,6 +3719,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, int flag = F2FS_GET_BLOCK_PRE_AIO; int err = 0; + if (!f2fs_has_inline_data(inode) && !f2fs_compressed_file(inode) && + (pos & PAGE_MASK) < i_size_read(inode) && + f2fs_lookup_read_extent_cache_block(inode, index, blk_addr)) + return 0; + /* * If a whole page is being written and we already preallocated all the * blocks, then there is no need to get a block address now. From a41075acde0124d2f8a5f563068a5d63e8ffd57b Mon Sep 17 00:00:00 2001 From: Mikhail Lobanov Date: Mon, 15 Jun 2026 14:36:13 +0300 Subject: [PATCH 4378/5214] f2fs: read COW data with the original inode during atomic write When updating an atomic-write file, f2fs_write_begin() may read the previously written data back from the COW inode: prepare_atomic_write_begin() locates the block in the COW inode and sets use_cow, and the read bio is then built with the COW inode: f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode : inode, ...); and f2fs_grab_read_bio() decides whether to schedule fs-layer decryption (STEP_DECRYPT) for the bio based on that inode via fscrypt_inode_uses_fs_layer_crypto(). However, the folio being filled belongs to the original inode (folio->mapping->host == inode), and the data stored in the COW block was encrypted (or left as plaintext) using the original inode's context, not the COW inode's -- see f2fs_encrypt_one_page(), which keys off fio->page->mapping->host. fscrypt_decrypt_pagecache_blocks() likewise operates on folio->mapping->host. The COW inode is created as a tmpfile in the parent directory and inherits its encryption policy from there. With test_dummy_encryption the newly created COW inode gets the dummy policy and becomes encrypted, while a pre-existing regular file -- created before the policy applied, e.g. already present in the on-disk image -- stays unencrypted. The read path then sets STEP_DECRYPT based on the encrypted COW inode and calls fscrypt_decrypt_pagecache_blocks() on a folio whose host (the unencrypted original inode) has a NULL ->i_crypt_info, dereferencing it: Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:fscrypt_decrypt_pagecache_blocks+0xa0/0x310 Workqueue: f2fs_post_read_wq f2fs_post_read_work Call Trace: fscrypt_decrypt_bio+0x1eb/0x340 f2fs_post_read_work+0xba/0x140 process_one_work+0x91c/0x1a40 worker_thread+0x677/0xe90 kthread+0x2bc/0x3a0 The COW inode is only needed to locate the on-disk block, and that block address is already resolved into @blkaddr by prepare_atomic_write_begin() via __find_data_block(cow_inode, ...); f2fs_submit_page_read() then reads from that physical @blkaddr directly, so the inode argument only selects the post-read crypto context, not which block is fetched. Reading with @inode therefore returns the same (latest, not-yet-committed) COW data, while making both the fs-layer decryption decision and the inline crypto path use the correct (original inode's) key. With the COW inode no longer used at the read site, the use_cow flag has no remaining consumer; drop it from f2fs_write_begin() and prepare_atomic_write_begin(). Fixes: 591fc34e1f98 ("f2fs: use cow inode data when updating atomic write") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Lobanov Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cea34b4595b1..60dea95b0295 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3860,7 +3860,7 @@ static int __reserve_data_block(struct inode *inode, pgoff_t index, static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, struct folio *folio, loff_t pos, unsigned int len, - block_t *blk_addr, bool *node_changed, bool *use_cow) + block_t *blk_addr, bool *node_changed) { struct inode *inode = folio->mapping->host; struct inode *cow_inode = F2FS_I(inode)->cow_inode; @@ -3875,14 +3875,14 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, /* Look for the block in COW inode first */ err = __find_data_block(cow_inode, index, blk_addr); - if (err) { + if (err) return err; - } else if (__is_valid_data_blkaddr(*blk_addr)) { - *use_cow = true; + + if (__is_valid_data_blkaddr(*blk_addr)) return 0; - } else if (*blk_addr == NEW_ADDR) { + + if (*blk_addr == NEW_ADDR) cow_has_reserved_block = true; - } if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) goto reserve_block; @@ -3917,7 +3917,6 @@ static int f2fs_write_begin(const struct kiocb *iocb, struct folio *folio; pgoff_t index = pos >> PAGE_SHIFT; bool need_balance = false; - bool use_cow = false; block_t blkaddr = NULL_ADDR; int err = 0; @@ -3980,7 +3979,7 @@ static int f2fs_write_begin(const struct kiocb *iocb, if (f2fs_is_atomic_file(inode)) err = prepare_atomic_write_begin(sbi, folio, pos, len, - &blkaddr, &need_balance, &use_cow); + &blkaddr, &need_balance); else err = prepare_write_begin(sbi, folio, pos, len, &blkaddr, &need_balance); @@ -4020,8 +4019,15 @@ static int f2fs_write_begin(const struct kiocb *iocb, err = -EFSCORRUPTED; goto put_folio; } - f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode : - inode, + /* + * Although the block may be stored in the COW inode, the folio + * belongs to @inode and its data was encrypted (or not) using + * @inode's context (see f2fs_encrypt_one_page()). Read with + * @inode so the post-read decryption decision matches the + * folio's owner; otherwise an unencrypted @inode whose COW inode + * is encrypted hits a NULL ->i_crypt_info on decryption. + */ + f2fs_submit_page_read(inode, NULL, /* can't write to fsverity files */ folio, blkaddr, 0, true); From cfcd0e49a178b3dac2c0ece656079081dbf5da74 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 3 Jun 2026 15:11:40 +0000 Subject: [PATCH 4379/5214] f2fs: validate inline dentry name lengths before conversion Inline dentry conversion copies names out of the inline dentry area before checking that each recorded name length fits in the available filename slots. A corrupted image can therefore make the conversion path read past the inline filename storage while building the regular dentry block. Validate each inline dentry name length against the inline filename area before copying it. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 099f72089701..e2f7bedf1552 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -510,6 +510,12 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry) bit_pos++; continue; } + if (unlikely(le16_to_cpu(de->name_len) > F2FS_NAME_LEN || + bit_pos + GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)) > + d.max)) { + err = -EFSCORRUPTED; + goto punch_dentry_pages; + } /* * We only need the disk_name and hash to move the dentry. @@ -530,6 +536,7 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry) bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } return 0; + punch_dentry_pages: truncate_inode_pages(&dir->i_data, 0); f2fs_truncate_blocks(dir, 0, false); From 90e02a8e1b6863c41876473f844c8e24b06d55f7 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 3 Jun 2026 16:11:26 +0000 Subject: [PATCH 4380/5214] f2fs: validate dentry name length before lookup compares it The f2fs dentry lookup path can use the on-disk name length before checking that the name fits in the dentry filename area. A corrupted dentry can then make lookup read beyond the filename slots. The bounds check needs to happen before any comparison that consumes the name length from disk. Reject dentries with invalid name lengths before comparing their names. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index b1697194c3c4..a9563f7fcd88 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -250,6 +250,11 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d, continue; } + if (unlikely(le16_to_cpu(de->name_len) > F2FS_NAME_LEN || + bit_pos + GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)) > + d->max)) + return ERR_PTR(-EFSCORRUPTED); + if (!use_hash || de->hash_code == fname->hash) { res = f2fs_match_name(d->inode, fname, d->filename[bit_pos], From 242d30bfc0a84b8b5de0a88821b53c9ad7fd31c4 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 10 Jun 2026 22:37:35 +0800 Subject: [PATCH 4381/5214] f2fs: reject setattr size changes on large folio files F2FS large folios are only enabled for immutable non-compressed files. Writable open and writable mmap reject such mappings, but truncate(2) through f2fs_setattr() misses the same guard. If FS_IMMUTABLE_FL is cleared while the inode is still cached, the mapping can keep large-folio support and ATTR_SIZE can change i_size. Reject size changes in that state. Cc: stable@kernel.org Fixes: 05e65c14ea59 ("f2fs: support large folio for immutable non-compressed case") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 69aad1060c48..d240ca78a31f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1098,6 +1098,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EPERM; if ((attr->ia_valid & ATTR_SIZE)) { + if (mapping_large_folio_support(inode->i_mapping)) + return -EOPNOTSUPP; if (!f2fs_is_compress_backend_ready(inode) || IS_DEVICE_ALIASING(inode)) return -EOPNOTSUPP; From 41b7928813b5db9f61bf55e0ce395f69eb0473eb Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 10 Jun 2026 12:34:17 -0700 Subject: [PATCH 4382/5214] f2fs: Prepare for supporting delayed bio completion Use bio frontpadding to allocate memory for a work_struct when allocating a bio. Reviewed-by: Chao Yu Signed-off-by: Bart Van Assche Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 60dea95b0295..9bddc8bf0af6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -41,12 +41,17 @@ struct f2fs_folio_state { unsigned int read_pages_pending; }; +struct f2fs_bio { + struct work_struct work; + struct bio bio; +}; + #define F2FS_BIO_POOL_SIZE NR_CURSEG_TYPE int __init f2fs_init_bioset(void) { return bioset_init(&f2fs_bioset, F2FS_BIO_POOL_SIZE, - 0, BIOSET_NEED_BVECS); + offsetof(struct f2fs_bio, bio), BIOSET_NEED_BVECS); } void f2fs_destroy_bioset(void) From 7ba36a9ea81c018c828138e8071c3658620e2f0f Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 10 Jun 2026 12:34:18 -0700 Subject: [PATCH 4383/5214] f2fs: Rename f2fs_post_read_wq into f2fs_wq Rename f2fs_post_read_wq into f2fs_wq. Create it unconditionally. Prepare for using this workqueue for completing write bios. Reviewed-by: Chao Yu Signed-off-by: Bart Van Assche Signed-off-by: Jaegeuk Kim --- fs/f2fs/compress.c | 2 +- fs/f2fs/data.c | 22 ++++++++-------------- fs/f2fs/f2fs.h | 6 +++--- fs/f2fs/super.c | 8 ++++---- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index caf522d667d6..372e07c58e21 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1809,7 +1809,7 @@ static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task) f2fs_free_dic(dic, false); } else { INIT_WORK(&dic->free_work, f2fs_late_free_dic); - queue_work(dic->sbi->post_read_wq, &dic->free_work); + queue_work(dic->sbi->wq, &dic->free_work); } } } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 9bddc8bf0af6..64250e6a94d1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -342,7 +342,7 @@ static void f2fs_read_end_io(struct bio *bio) f2fs_handle_step_decompress(ctx, intask); } else if (enabled_steps) { INIT_WORK(&ctx->work, f2fs_post_read_work); - queue_work(ctx->sbi->post_read_wq, &ctx->work); + queue_work(ctx->sbi->wq, &ctx->work); return; } } @@ -4527,23 +4527,17 @@ void f2fs_destroy_post_read_processing(void) kmem_cache_destroy(bio_post_read_ctx_cache); } -int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi) +int f2fs_init_wq(struct f2fs_sb_info *sbi) { - if (!f2fs_sb_has_encrypt(sbi) && - !f2fs_sb_has_verity(sbi) && - !f2fs_sb_has_compression(sbi)) - return 0; - - sbi->post_read_wq = alloc_workqueue("f2fs_post_read_wq", - WQ_UNBOUND | WQ_HIGHPRI, - num_online_cpus()); - return sbi->post_read_wq ? 0 : -ENOMEM; + sbi->wq = alloc_workqueue("f2fs_wq", WQ_UNBOUND | WQ_HIGHPRI, + num_online_cpus()); + return sbi->wq ? 0 : -ENOMEM; } -void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi) +void f2fs_destroy_wq(struct f2fs_sb_info *sbi) { - if (sbi->post_read_wq) - destroy_workqueue(sbi->post_read_wq); + if (sbi->wq) + destroy_workqueue(sbi->wq); } int __init f2fs_init_bio_entry_cache(void) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 086bc5243979..8f3e632f315c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1979,7 +1979,7 @@ struct f2fs_sb_info { /* Precomputed FS UUID checksum for seeding other checksums */ __u32 s_chksum_seed; - struct workqueue_struct *post_read_wq; /* post read workqueue */ + struct workqueue_struct *wq; /* bio completion workqueue */ /* * If we are in irq context, let's update error information into @@ -4214,8 +4214,8 @@ bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len); void f2fs_clear_page_cache_dirty_tag(struct folio *folio); int f2fs_init_post_read_processing(void); void f2fs_destroy_post_read_processing(void); -int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi); -void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi); +int f2fs_init_wq(struct f2fs_sb_info *sbi); +void f2fs_destroy_wq(struct f2fs_sb_info *sbi); extern const struct iomap_ops f2fs_iomap_ops; /* diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b277807c8185..438616597e53 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2075,7 +2075,7 @@ static void f2fs_put_super(struct super_block *sb) /* flush s_error_work before sbi destroy */ flush_work(&sbi->s_error_work); - f2fs_destroy_post_read_wq(sbi); + f2fs_destroy_wq(sbi); kvfree(sbi->ckpt); @@ -5189,9 +5189,9 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc) goto free_devices; } - err = f2fs_init_post_read_wq(sbi); + err = f2fs_init_wq(sbi); if (err) { - f2fs_err(sbi, "Failed to initialize post read workqueue"); + f2fs_err(sbi, "Failed to create workqueue"); goto free_devices; } @@ -5478,7 +5478,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc) f2fs_stop_ckpt_thread(sbi); /* flush s_error_work before sbi destroy */ flush_work(&sbi->s_error_work); - f2fs_destroy_post_read_wq(sbi); + f2fs_destroy_wq(sbi); free_devices: destroy_device_list(sbi); kvfree(sbi->ckpt); From bdc7cfd780c79228099674d419de93be25971ff9 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 10 Jun 2026 12:34:19 -0700 Subject: [PATCH 4384/5214] f2fs: Split f2fs_write_end_io() Prepare for running most of the write completion work asynchronously. Reviewed-by: Chao Yu Signed-off-by: Bart Van Assche Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 64250e6a94d1..a765fda71536 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -350,14 +350,11 @@ static void f2fs_read_end_io(struct bio *bio) f2fs_verify_and_finish_bio(bio, intask); } -static void f2fs_write_end_io(struct bio *bio) +static void f2fs_write_end_bio(struct bio *bio) { - struct f2fs_sb_info *sbi; + struct f2fs_sb_info *sbi = bio->bi_private; struct folio_iter fi; - iostat_update_and_unbind_ctx(bio); - sbi = bio->bi_private; - if (time_to_inject(sbi, FAULT_WRITE_IO)) bio->bi_status = BLK_STS_IOERR; @@ -414,6 +411,13 @@ static void f2fs_write_end_io(struct bio *bio) bio_put(bio); } +static void f2fs_write_end_io(struct bio *bio) +{ + iostat_update_and_unbind_ctx(bio); + + f2fs_write_end_bio(bio); +} + #ifdef CONFIG_BLK_DEV_ZONED static void f2fs_zone_write_end_io(struct bio *bio) { From ccaba785821970f422c47770331c7e3271763f17 Mon Sep 17 00:00:00 2001 From: Zhaoyang Huang Date: Mon, 8 Jun 2026 17:09:39 +0800 Subject: [PATCH 4385/5214] Revert "f2fs: remove non-uptodate folio from the page cache in move_data_block" This reverts commit 9609dd704725a40cd63d915f2ab6c44248a44598. The kernel panics are keeping to be reported especially when the f2fs partition get almost full. By investigation, we find that the reason is one f2fs page got freed to buddy without being deleted from LRU and the root cause is the race happened in [2] which is enrolled by this commit. There are 3 race processes in this scenario, please find below for their main activities. The changed code in move_data_block() lets the GC path evict the tail-end folio from the page cache through folio_end_dropbehind(). Once folio_unmap_invalidate() removes the folio from mapping->i_pages, the page-cache references for all pages in the folio are dropped. The folio is then kept alive only by temporary external references, which allows a later split to operate on a folio whose subpages are no longer protected by page-cache references. After the page-cache references are gone, split_folio_to_order() can split the big folio into individual pages and put the resulting subpages back on the LRU. For tail pages beyond EOF, split removes them from the page cache and drops their page-cache references. A tail page can then remain on the LRU with PG_lru set while holding only the split caller's temporary reference. When free_folio_and_swap_cache() drops that final reference, the page enters the final folio_put() release path. In parallel, folio_isolate_lru() can observe the same tail page with a non-zero refcount and PG_lru set. It clears PG_lru before taking its own reference. If this races with the final folio_put() from the split path, __folio_put() sees PG_lru already cleared and skips lruvec_del_folio(). The page is then freed back to the allocator while its lru links are still present in the LRU list. A later LRU operation on a neighboring page detects the stale link and reports list corruption. [1] [ 22.486082] list_del corruption. next->prev should be fffffffec10e0ac8, but was dead000000000122. (next=fffffffec10e0a88) [ 22.486130] ------------[ cut here ]------------ [ 22.486134] kernel BUG at lib/list_debug.c:67! [ 22.486141] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP [ 22.488502] Tainted: [W]=WARN, [O]=OOT_MODULE [ 22.488506] Hardware name: Spreadtrum UMS9230 1H10 SoC (DT) [ 22.488511] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 22.488517] pc : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488531] lr : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488539] sp : ffffffc08006b830 [ 22.488542] x29: ffffffc08006b868 x28: 0000000000003020 x27: 0000000000000000 [ 22.488553] x26: 0000000000000000 x25: 0000000000000004 x24: fffffffec10e0ac0 [ 22.488564] x23: 00000000000000e8 x22: 0000000000000024 x21: dead000000000122 [ 22.488574] x20: fffffffec10e0a88 x19: fffffffec10e0ac8 x18: ffffffc080061060 [ 22.488585] x17: 20747562202c3863 x16: 6130653031636566 x15: 0000000000000058 [ 22.488595] x14: 0000000000000004 x13: ffffff80f91e0000 x12: 0000000000000003 [ 22.488605] x11: 0000000000000003 x10: 0000000000000001 x9 : ffe85721f0e25f00 [ 22.488615] x8 : ffe85721f0e25f00 x7 : 0000000000000000 x6 : 6c65645f7473696c [ 22.488625] x5 : ffffffed39b23026 x4 : 0000000000000000 x3 : 0000000000000010 [ 22.488636] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 000000000000006d [ 22.488647] Call trace: [ 22.488651] __list_del_entry_valid_or_report+0x14c/0x154 (P) [ 22.488661] __folio_put+0x2bc/0x434 [ 22.488670] folio_put+0x28/0x58 [ 22.488678] do_garbage_collect+0x1a34/0x2584 [ 22.488689] f2fs_gc+0x230/0x9b4 [ 22.488697] f2fs_fallocate+0xb90/0xdf4 [ 22.488706] vfs_fallocate+0x1b4/0x2bc [ 22.488716] __arm64_sys_fallocate+0x44/0x78 [ 22.488725] invoke_syscall+0x58/0xe4 [ 22.488732] do_el0_svc+0x48/0xdc [ 22.488739] el0_svc+0x3c/0x98 [ 22.488747] el0t_64_sync_handler+0x20/0x130 [ 22.488754] el0t_64_sync+0x1c4/0x1c8 [2] CPU0 (f2fs GC) CPU1 (split_folio_to_order) CPU2 (folio_isolate_lru) F: pagecache refs = n F: extra refs = GC + split F: PG_lru set move_data_block() folio = f2fs_grab_cache_folio(F) ... __folio_set_dropbehind(F) folio_unlock(F) folio_end_dropbehind(F) folio_unmap_invalidate(F) __filemap_remove_folio(F) folio_put_refs(F, n) folio_put(F) split_folio_to_order(F) folio_ref_freeze(F, 1) ... lru_add_split_folio(T) list_add_tail(&T->lru, &F->lru) folio_set_lru(T) __filemap_remove_folio(T) folio_put_refs(T, 1) /* T refcount == 1, PageLRU set */ folio_isolate_lru(T) folio_test_clear_lru(T) free_folio_and_swap_cache(T) folio_put(T) /* refcount: 1 -> 0 */ __folio_put(T) __page_cache_release(T) folio_test_lru(T) == false /* skip lruvec_del_folio(T) */ free_frozen_pages(T) folio_get(T) lruvec_del_folio(T) later: list_del(adjacent->lru) next == &T->lru next->prev == LIST_POISON / PCP freelist BUG Cc: stable@vger.kernel.org Fixes: 9609dd704725 ("f2fs: remove non-uptodate folio from the page cache in move_data_block") Signed-off-by: Zhaoyang Huang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 69e0a867219d..56a1c0547d76 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1499,11 +1499,7 @@ static int move_data_block(struct inode *inode, block_t bidx, put_out: f2fs_put_dnode(&dn); out: - if (!folio_test_uptodate(folio)) - __folio_set_dropbehind(folio); - folio_unlock(folio); - folio_end_dropbehind(folio); - folio_put(folio); + f2fs_folio_put(folio, true); out_iput: if (atomic_inode) iput(atomic_inode); From c4810ada31e80cbe4011467c4f3b1e93f94134f3 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 15 Jun 2026 15:19:54 +0800 Subject: [PATCH 4386/5214] f2fs: validate ACL entry sizes in f2fs_acl_from_disk() f2fs_acl_count() only validates the aggregate ACL xattr length. A malformed ACL can still place ACL_USER or ACL_GROUP in a slot that only contains struct f2fs_acl_entry_short bytes, and f2fs_acl_from_disk() then reads entry->e_id before verifying that a full entry fits. Require a short entry before reading e_tag and e_perm, and require a full entry before reading e_id for ACL_USER and ACL_GROUP. Return -EFSCORRUPTED from these new truncated-entry checks, while keeping the pre-existing -EINVAL paths unchanged. Validation reproduced this kernel report: KASAN slab-out-of-bounds in __f2fs_get_acl+0x6fb/0x7e0 RIP: 0033:0x7f4b835ea7aa The buggy address belongs to the object at ffff888114589960 which belongs to the cache kmalloc-8 of size 8 The buggy address is located 0 bytes to the right of allocated 8-byte region [ffff888114589960, ffff888114589968) Read of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) __f2fs_get_acl+0x6fb/0x7e0 (fs/f2fs/acl.c:169) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x224/0x430 (?:?) kasan_report+0xe0/0x110 (?:?) __f2fs_get_acl+0x5/0x7e0 (fs/f2fs/acl.c:169) __get_acl+0x281/0x380 (?:?) vfs_get_acl+0x10b/0x190 (?:?) do_get_acl+0x2a/0x410 (?:?) do_get_acl+0x9/0x410 (?:?) do_getxattr+0xe8/0x260 (?:?) filename_getxattr+0xd1/0x140 (?:?) do_getname+0x2d/0x2d0 (?:?) path_getxattrat+0x16c/0x200 (?:?) lock_release+0xc8/0x290 (?:?) cgroup_update_frozen+0x9d/0x320 (?:?) lockdep_hardirqs_on_prepare+0xea/0x1a0 (?:?) trace_hardirqs_on+0x1a/0x170 (?:?) _raw_spin_unlock_irq+0x28/0x50 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Cc: stable@kernel.org Fixes: af48b85b8cd3 ("f2fs: add xattr and acl functionalities") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index fa8d81a30fb9..d3253549173e 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -47,6 +47,7 @@ static inline int f2fs_acl_count(size_t size) static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) { int i, count; + int err = -EINVAL; struct posix_acl *acl; struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); @@ -70,8 +71,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) for (i = 0; i < count; i++) { - if ((char *)entry > end) + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry_short) > end)) { + err = -EFSCORRUPTED; goto fail; + } acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); @@ -86,6 +90,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) break; case ACL_USER: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_uid = make_kuid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -93,6 +102,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) sizeof(struct f2fs_acl_entry)); break; case ACL_GROUP: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_gid = make_kgid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -108,7 +122,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) return acl; fail: posix_acl_release(acl); - return ERR_PTR(-EINVAL); + return ERR_PTR(err); } static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi, From 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 11 Jun 2026 23:00:36 -0500 Subject: [PATCH 4387/5214] f2fs: bound i_inline_xattr_size for non-inline-xattr inodes When the flexible_inline_xattr feature is enabled, do_read_inode() loads the on-disk i_inline_xattr_size unconditionally: if (f2fs_sb_has_flexible_inline_xattr(sbi)) fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); but sanity_check_inode() only range-checks it when the inode also has the FI_INLINE_XATTR flag set. An inode that carries an inline dentry or inline data but not FI_INLINE_XATTR -- the normal layout for an inline directory -- therefore keeps a fully attacker-controlled i_inline_xattr_size from a crafted image. get_inline_xattr_addrs() returns that value with no flag gating, so it feeds the inode geometry: MAX_INLINE_DATA() = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1) NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...) addrs_per_page() = CUR_ADDRS_PER_INODE - i_inline_xattr_size A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY() negative, so make_dentry_ptr_inline() sets d->max (int) to a negative value. The inline directory walk then compares an unsigned long bit_pos against that negative d->max, which is promoted to a huge unsigned bound, and reads far past the inline area: while (bit_pos < d->max) /* fs/f2fs/dir.c */ ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ... Mounting a crafted image and reading such a directory triggers an out-of-bounds read in f2fs_fill_dentries(); the same underflow also corrupts ADDRS_PER_INODE for regular files. Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the flexible_inline_xattr feature is enabled -- i.e. whenever the value is loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE bound gated on inodes that actually carry an inline xattr, so legitimate inodes with i_inline_xattr_size == 0 are still accepted. Cc: stable@vger.kernel.org Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size") Signed-off-by: Bryam Vargas Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 25f30b8eadc5..c95e0b126da4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -325,9 +325,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio) } if (f2fs_sb_has_flexible_inline_xattr(sbi) && - f2fs_has_inline_xattr(inode) && - (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE || - fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { + (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE || + (f2fs_has_inline_xattr(inode) && + fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) { f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu", __func__, inode->i_ino, fi->i_inline_xattr_size, MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE); From 98fd20b9cf472d7e0518517ea9e587a9a2b8b311 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jun 2026 21:08:17 +0800 Subject: [PATCH 4388/5214] f2fs: fix wrong description in printed log This patch fixes wrong description in printed log: "SSA and SIT" -> "SIT and SSA" Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 56a1c0547d76..ffaa7ba76a1b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1893,7 +1893,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno); if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) { f2fs_err(sbi, "Inconsistent segment (%u) type " - "[%d, %d] in SSA and SIT", + "[%d, %d] in SIT and SSA", cur_segno, type, GET_SUM_TYPE( sum_footer(sbi, sum))); From d27e4431023770249a341aeb94cb40f4ff12b21e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jun 2026 21:08:18 +0800 Subject: [PATCH 4389/5214] f2fs: misc cleanup in f2fs_record_stop_reason() Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 438616597e53..2b8d96411156 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4641,8 +4641,7 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi) spin_lock_irqsave(&sbi->error_lock, flags); if (sbi->error_dirty) { - memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors, - MAX_F2FS_ERRORS); + memcpy(raw_super->s_errors, sbi->errors, MAX_F2FS_ERRORS); sbi->error_dirty = false; } memcpy(raw_super->s_stop_reason, sbi->stop_reason, MAX_STOP_REASON); From cf716276b0dca934aad5fe3c46df04e1dc596734 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jun 2026 21:08:19 +0800 Subject: [PATCH 4390/5214] f2fs: avoid unnecessary sanity check on ckpt_valid_blocks The calculation of sec->ckpt_valid_blocks are the same in both set_ckpt_valid_blocks() and sanity_check_valid_blocks(), so it doesn't necessary to call sanity_check_valid_blocks() right after set_ckpt_valid_blocks(). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 993555042876..d71ddb3ee918 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -4773,10 +4773,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) } /* update ckpt_valid_block */ - if (__is_large_section(sbi)) { + if (__is_large_section(sbi)) set_ckpt_valid_blocks(sbi, segno); - sanity_check_valid_blocks(sbi, segno); - } __clear_bit(segno, bitmap); sit_i->dirty_sentries--; @@ -5090,10 +5088,8 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) if (__is_large_section(sbi)) { unsigned int segno; - for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) { + for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) set_ckpt_valid_blocks(sbi, segno); - sanity_check_valid_blocks(sbi, segno); - } } if (err) From 8b938ae6f0766559dfc4ad5acac958b1eff8664d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jun 2026 21:08:20 +0800 Subject: [PATCH 4391/5214] f2fs: avoid unnecessary fscrypt_finalize_bounce_page() fscrypt_finalize_bounce_page() should be called only if we use fs layer crypto, let's avoid unnecessary fscrypt_finalize_bounce_page() in error path of f2fs_write_compressed_pages(). BTW, fscrypt_finalize_bounce_page() will check mapping of bounced page before retrieving original page, so, previously it won't cause any issue w/ fscrypt_finalize_bounce_page(), but still we'd better avoid coupling w/ any logic inside fscrypt_finalize_bounce_page(). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/compress.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 372e07c58e21..6e1c0ea9f6c9 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1455,6 +1455,9 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc, out_destroy_crypt: page_array_free(sbi, cic->rpages, cc->cluster_size); + if (!fio.encrypted) + goto out_put_cic; + for (--i; i >= 0; i--) { if (!cc->cpages[i]) continue; From 70210492be5ac8e4f42b383b75dfa11810afab86 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jun 2026 21:08:22 +0800 Subject: [PATCH 4392/5214] f2fs: remove unneeded f2fs_is_compressed_page() We have checked f2fs_is_compressed_page() before f2fs_compress_write_end_io(), so we don't need to check the status again, remove it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/compress.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 6e1c0ea9f6c9..91855d91bbdd 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1487,8 +1487,7 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio) struct page *page = &folio->page; struct f2fs_sb_info *sbi = bio->bi_private; struct compress_io_ctx *cic = folio->private; - enum count_type type = WB_DATA_TYPE(folio, - f2fs_is_compressed_page(folio)); + enum count_type type = WB_DATA_TYPE(folio, true); int i; if (unlikely(bio->bi_status != BLK_STS_OK)) From 34636c6dcd6f75570c553a4188b5dbe0f758159a Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Tue, 16 Jun 2026 11:06:55 +0800 Subject: [PATCH 4393/5214] f2fs: skip direct I/O iostat context when disabled F2FS iostat is optional and is disabled by default. Direct I/O still allocates and binds a bio_iostat_ctx, updates the submit timestamp, and replaces bi_end_io for every DIO bio even when sbi->iostat_enable is false. The byte accounting calls do not need an extra guard because f2fs_update_iostat() already checks sbi->iostat_enable. Only skip the DIO bio context setup when iostat is disabled. If iostat is enabled through sysfs before submission, the existing context allocation and latency accounting path is still used. QEMU benchmark on a 1GiB F2FS virtio-blk image, with iostat_enable=0, 4KiB O_DIRECT I/O over a 64MiB file, 50000 iterations per run: baseline patched direct_read median 65264.50 ns 55470.95 ns direct_read recheck 65553.75 ns 55470.95 ns direct_write median 68054.62 ns 56309.44 ns direct_write recheck 66873.51 ns 56309.44 ns Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d240ca78a31f..8acdd94272a0 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -4801,6 +4801,9 @@ static void f2fs_dio_iostat_start(struct f2fs_sb_info *sbi, struct bio *bio) { void *bi_private = bio->bi_private; + if (!sbi->iostat_enable) + return; + iostat_alloc_and_bind_ctx(sbi, bio, bi_private); iostat_update_submit_ctx(bio, DATA); bio->bi_end_io = f2fs_dio_end_bio; From 5ef5bc304f23c3fe255d4936472378dcb74d0e94 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Mon, 22 Jun 2026 20:44:21 +0530 Subject: [PATCH 4394/5214] f2fs: fix listxattr handling of corrupted xattr entries Validate the xattr entry before reading its fields in f2fs_listxattr(). Return -EFSCORRUPTED when the entry is outside the valid xattr storage area instead of returning a successful partial result. Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr") Cc: stable@kernel.org Reviewed-by: Chao Yu Signed-off-by: Keshav Verma Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 24cef7e1f56a..ed33e5110f2a 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -583,8 +583,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) size_t prefix_len; size_t size; - prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); - if ((void *)(entry) + sizeof(__u32) > last_base_addr || (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { f2fs_err(F2FS_I_SB(inode), "list inode (%llu) has corrupted xattr", @@ -594,9 +592,11 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) ERROR_CORRUPTED_XATTR); fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS); - break; + error = -EFSCORRUPTED; + goto cleanup; } + prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); if (!prefix) continue; From 4275b59673eb60b02eec3997816c83f1f4b909c4 Mon Sep 17 00:00:00 2001 From: Sunmin Jeong Date: Mon, 22 Jun 2026 14:28:17 +0900 Subject: [PATCH 4395/5214] f2fs: fix to round down start offset of fallocate for pin file Currently, the length of fallocate for pin file is section-aligned to keep allocated sections from being selected as victims of GC. However, for the case that the start offset of fallocate is not aligned in section, the allocated sections can't be fully utilized. It's because a new section is allocated by f2fs_allocate_pinning_section() after using blks_per_sec blocks regardless of the start offset. As a result, several unexpected dirty segments may be created, including blocks assigned to the pinned file. To address this issue, let's round down the start offset of fallocate to the length of section. The reproducing scenario is as below chunk=$(((2<<20)+4096)) # 2MB + 4KB touch test f2fs_io pinfile set test f2fs_io fallocate 0 0 $chunk test f2fs_io fallocate 0 $chunk $chunk test f2fs_io fallocate 0 $((chunk*2)) $chunk test f2fs_io fiemap 0 $((chunk*3)) test Fiemap: offset = 0 len = 12288 logical addr. physical addr. length flags 0 0000000000000000 000000068c600000 0000000000400000 00001088 1 0000000000400000 000000003d400000 0000000000001000 00001088 2 0000000000401000 00000003eb200000 0000000000200000 00001088 3 0000000000601000 00000005e4200000 0000000000001000 00001088 4 0000000000602000 0000000605400000 0000000000200000 00001089 Cc: stable@vger.kernel.org Fixes: f5a53edcf01e ("f2fs: support aligned pinned file") Reviewed-by: Yunji Kang Reviewed-by: Yeongjin Gil Reviewed-by: Sungjong Seo Signed-off-by: Sunmin Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8acdd94272a0..4b52c56d71f0 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1916,8 +1916,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, if (f2fs_is_pinned_file(inode)) { block_t sec_blks = CAP_BLKS_PER_SEC(sbi); - block_t sec_len = roundup(map.m_len, sec_blks); + block_t sec_len; + if (map.m_lblk % sec_blks) { + map.m_lblk = rounddown(map.m_lblk, sec_blks); + map.m_len = pg_end - map.m_lblk; + if (off_end) + map.m_len++; + } + sec_len = roundup(map.m_len, sec_blks); map.m_len = sec_blks; next_alloc: f2fs_down_write(&sbi->pin_sem); From 854bd081c7680029d7886689f6bef8f740625fde Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Fri, 10 Apr 2026 16:02:59 -0700 Subject: [PATCH 4396/5214] misc: pci_endpoint_test: Validate BAR index in doorbell test pci_endpoint_test_doorbell() reads the BAR number directly from an endpoint test register and uses it as an index into test->bar[]. Add a defensive bounds check before the dereference: positive values >= PCI_STD_NUM_BARS are out of range, and NO_BAR (-1) as a negative signed value would slip past an upper-bound-only check. Signed-off-by: Carlos Bilbao (Lambda) [mani: changed errno to -ERANGE] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260410230300.135631-2-carlos.bilbao@kernel.org --- drivers/misc/pci_endpoint_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index dbd017cabbb9..64ac7c7c90af 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1108,6 +1108,11 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test) pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR); + if (bar < BAR_0 || bar >= PCI_STD_NUM_BARS) { + dev_err(dev, "BAR %d reported by endpoint out of range [0, %u]\n", + bar, PCI_STD_NUM_BARS - 1); + return -ERANGE; + } writel(data, test->bar[bar] + addr); From 7b6e7e975705159f0ce7915811cf10f56133fdda Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Fri, 10 Apr 2026 16:03:00 -0700 Subject: [PATCH 4397/5214] misc: pci_endpoint_test: Remove dead BAR read before doorbell trigger The assignment before the writel sequence is dead code (bar is unconditionally overwritten by the re-read immediately after) so remove the assignment entirely. Note that the DB_BAR register is a plain value written by the endpoint firmware; reading it carries no side effect. Signed-off-by: Carlos Bilbao (Lambda) Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Koichiro Den Link: https://patch.msgid.link/20260410230300.135631-3-carlos.bilbao@kernel.org --- drivers/misc/pci_endpoint_test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 64ac7c7c90af..3635741c3e7a 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1100,7 +1100,6 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test) data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA); addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET); - bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); From fcba26efe5efc7441f5505f4ccc69791214b40be Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:27 +0900 Subject: [PATCH 4398/5214] NTB: epf: Fix request_irq() unwind in ntb_epf_init_isr() ntb_epf_init_isr() requests multiple MSI/MSI-X vectors in a loop. If request_irq() fails part-way through, it jumps straight to pci_free_irq_vectors() without freeing already requested IRQs. Fix the error path by freeing any successfully requested IRQs before releasing the vectors. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-2-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index d3ecf25a5162..5a35f341f821 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -355,7 +355,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) 0, "ntb_epf", ndev); if (ret) { dev_err(dev, "Failed to request irq\n"); - goto err_request_irq; + goto err_free_irq; } } @@ -365,16 +365,14 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument | irq); if (ret) { dev_err(dev, "Failed to configure doorbell\n"); - goto err_configure_db; + goto err_free_irq; } return 0; -err_configure_db: - for (i = 0; i < ndev->db_count + 1; i++) +err_free_irq: + while (i--) free_irq(pci_irq_vector(pdev, i), ndev); - -err_request_irq: pci_free_irq_vectors(pdev); return ret; From 4dcddc1c794d1c65eda68f1f8dd04a0fecc0870f Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:28 +0900 Subject: [PATCH 4399/5214] NTB: epf: Avoid calling pci_irq_vector() from hardirq context ntb_epf_vec_isr() calls pci_irq_vector() in hardirq context to derive the vector number. pci_irq_vector() calls msi_get_virq() that takes a mutex and can therefore trigger "scheduling while atomic" splats: BUG: scheduling while atomic: kworker/u33:0/55/0x00010001 ... Call trace: ... schedule+0x38/0x110 schedule_preempt_disabled+0x28/0x50 __mutex_lock.constprop.0+0x848/0x908 __mutex_lock_slowpath+0x18/0x30 mutex_lock+0x4c/0x60 msi_domain_get_virq+0xe8/0x138 pci_irq_vector+0x2c/0x60 ntb_epf_vec_isr+0x28/0x120 [ntb_hw_epf] __handle_irq_event_percpu+0x70/0x3a8 handle_irq_event+0x48/0x100 handle_edge_irq+0x100/0x1c8 ... Cache the Linux IRQ number for vector 0 when vectors are allocated and use it as a base in the ISR. Running the ISR in a threaded IRQ handler would also avoid the problem, but that would be unnecessary here. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-3-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 5a35f341f821..8925c688930c 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -92,6 +92,7 @@ struct ntb_epf_dev { int db_val; u64 db_valid_mask; + int irq_base; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -318,7 +319,7 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) struct ntb_epf_dev *ndev = dev; int irq_no; - irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); + irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; if (irq_no == 0) @@ -350,6 +351,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } + ndev->irq_base = pci_irq_vector(pdev, 0); for (i = 0; i < irq; i++) { ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, 0, "ntb_epf", ndev); From e5c6b06a97c88227da49874d805393f960b661c6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:39:31 +0200 Subject: [PATCH 4400/5214] rbd: switch to dynamic root device Driver core expects devices to be dynamically allocated and will, for example, complain loudly when no release function has been provided. Use root_device_register() to allocate and register the root device instead of open coding using a static device. Signed-off-by: Johan Hovold Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- drivers/block/rbd.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6c1e7347e6a7..94709466ad19 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -580,14 +580,7 @@ static const struct bus_type rbd_bus_type = { .bus_groups = rbd_bus_groups, }; -static void rbd_root_dev_release(struct device *dev) -{ -} - -static struct device rbd_root_dev = { - .init_name = "rbd", - .release = rbd_root_dev_release, -}; +static struct device *rbd_root_dev; static __printf(2, 3) void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...) @@ -5378,7 +5371,7 @@ static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec) rbd_dev->dev.bus = &rbd_bus_type; rbd_dev->dev.type = &rbd_device_type; - rbd_dev->dev.parent = &rbd_root_dev; + rbd_dev->dev.parent = rbd_root_dev; device_initialize(&rbd_dev->dev); return rbd_dev; @@ -7327,15 +7320,13 @@ static int __init rbd_sysfs_init(void) { int ret; - ret = device_register(&rbd_root_dev); - if (ret < 0) { - put_device(&rbd_root_dev); - return ret; - } + rbd_root_dev = root_device_register("rbd"); + if (IS_ERR(rbd_root_dev)) + return PTR_ERR(rbd_root_dev); ret = bus_register(&rbd_bus_type); if (ret < 0) - device_unregister(&rbd_root_dev); + root_device_unregister(rbd_root_dev); return ret; } @@ -7343,7 +7334,7 @@ static int __init rbd_sysfs_init(void) static void __exit rbd_sysfs_cleanup(void) { bus_unregister(&rbd_bus_type); - device_unregister(&rbd_root_dev); + root_device_unregister(rbd_root_dev); } static int __init rbd_slab_init(void) From 33bd1ea748bc897c4d13437284e08c658e8d1340 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:20 +0530 Subject: [PATCH 4401/5214] PCI: endpoint: pci-epf-vntb: Add check to detect 'db_count' value of 0 epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' assignment. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407124421.282766-2-mani@kernel.org --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index b493a300da4d..d59870fd3430 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -488,7 +488,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; struct device *dev; - u32 db_count; int ret; dev = &ntb->epf->dev; @@ -500,14 +499,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; - if (epc_features->msi_capable) { ret = pci_epc_set_msi(ntb->epf->epc, ntb->epf->func_no, From d1db6d7c2485ee475a04d8354fbb5b26ea10d978 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:21 +0530 Subject: [PATCH 4402/5214] PCI: endpoint: pci-epf-ntb: Add check to detect 'db_count' value of 0 epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' variable from epf_ntb_configure_interrupt(). Fixes: 8b821cf76150 ("PCI: endpoint: Add EP function driver to provide NTB functionality") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260407124421.282766-3-mani@kernel.org --- drivers/pci/endpoint/functions/pci-epf-ntb.c | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 2bdcc35b652c..5314aca2188a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb, struct pci_epc *epc; int ret; - if (db_count > MAX_DB_COUNT) - return -EINVAL; - ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; + if (!db_count || db_count > MAX_DB_COUNT) { + dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n", + db_count, MAX_DB_COUNT); + return -EINVAL; + } + if (msix) ret = epf_ntb_configure_msix(ntb, type, db_count); else @@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; - u32 db_count; int ret; ntb_epc = ntb->epc[type]; @@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, func_no = ntb_epc->func_no; vfunc_no = ntb_epc->vfunc_no; - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; epc = ntb_epc->epc; if (msi_capable) { - ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count); if (ret) { dev_err(dev, "%s intf: MSI configuration failed\n", pci_epc_interface_string(type)); @@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, } if (msix_capable) { - ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, ntb_epc->msix_bar, ntb_epc->msix_table_offset); if (ret) { From 6a7db4fcc6c23b1d25e676400d764bdcb7dc1ccb Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:12 +0900 Subject: [PATCH 4403/5214] PCI: endpoint: pci-epf-vntb: Document legacy MSI doorbell offset vntb_epf_peer_db_set() raises an MSI interrupt to notify the RC side of a doorbell event. pci_epc_raise_irq(..., PCI_IRQ_MSI, interrupt_num) takes a 1-based MSI interrupt number. The ntb_hw_epf driver reserves MSI #1 for link events, so doorbells would naturally start at MSI #2 (doorbell bit 0 -> MSI #2). However, pci-epf-vntb has historically applied an extra offset and mapped doorbell bit 0 to MSI #3. This matches the legacy behavior of ntb_hw_epf and has been preserved since commit e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP"). This offset has not surfaced as a functional issue because: - ntb_hw_epf typically allocates enough MSI vectors, so the off-by-one still hits a valid MSI vector, and - ntb_hw_epf does not implement .db_vector_count()/.db_vector_mask(), so client drivers such as ntb_transport effectively ignore the vector number and schedule all QPs. Correcting the MSI number would break interoperability with peers running older kernels. Document the legacy offset to avoid confusion when enabling per-db-vector handling. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-2-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index d59870fd3430..668d25abc7f2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1419,6 +1419,25 @@ static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) func_no = ntb->epf->func_no; vfunc_no = ntb->epf->vfunc_no; + /* + * pci_epc_raise_irq() for MSI expects a 1-based interrupt number. + * ffs() returns a 1-based index (bit 0 -> 1). interrupt_num has already + * been computed as ffs(db_bits) + 1 above. Adding one more +1 when + * calling pci_epc_raise_irq() therefore results in: + * + * doorbell bit 0 -> MSI #3 + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking interoperability with + * older peers. + */ ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, PCI_IRQ_MSI, interrupt_num + 1); if (ret) From 18355c1e986582aaff2c488b1a2ce79bac8f3cf9 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:13 +0900 Subject: [PATCH 4404/5214] PCI: endpoint: pci-epf-vntb: Defer pci_epc_raise_irq() out of atomic context The NTB .peer_db_set() callback may be invoked from atomic context. pci-epf-vntb currently calls pci_epc_raise_irq() directly, but pci_epc_raise_irq() may sleep (it takes epc->lock). Avoid sleeping in atomic context by coalescing doorbell bits into an atomic64 pending mask and raising MSIs from a work item. Limit the amount of work per run to avoid monopolizing the workqueue under a doorbell storm. Clear stale pending bits before enabling the work item and after disabling it during cleanup. Also mask requested doorbells against the currently valid doorbell mask before queueing work, and iterate the pending u64 with __ffs64() so high doorbell bits are handled correctly. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-3-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 112 +++++++++++++----- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 668d25abc7f2..cc0b356973f3 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -37,6 +37,7 @@ */ #include +#include #include #include #include @@ -69,6 +70,9 @@ static struct workqueue_struct *kpcintb_workqueue; #define MAX_DB_COUNT 32 #define MAX_MW 4 +/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */ +#define VNTB_PEER_DB_WORK_BUDGET 5 + enum epf_ntb_bar { BAR_CONFIG, BAR_DB, @@ -129,6 +133,8 @@ struct epf_ntb { u32 spad_count; u64 mws_size[MAX_MW]; atomic64_t db; + atomic64_t peer_db_pending; + struct work_struct peer_db_work; u32 vbus_number; u16 vntb_pid; u16 vntb_vid; @@ -972,6 +978,9 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + atomic64_set(&ntb->peer_db_pending, 0); + enable_work(&ntb->peer_db_work); + return 0; err_write_header: @@ -995,6 +1004,8 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { disable_delayed_work_sync(&ntb->cmd_handler); + disable_work_sync(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); epf_ntb_db_bar_clear(ntb); epf_ntb_config_sspad_bar_clear(ntb); @@ -1409,41 +1420,84 @@ static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 return 0; } -static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +static void vntb_epf_peer_db_work(struct work_struct *work) { - u32 interrupt_num = ffs(db_bits) + 1; - struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work); + struct pci_epf *epf = ntb->epf; + unsigned int budget = VNTB_PEER_DB_WORK_BUDGET; u8 func_no, vfunc_no; + unsigned int db_bit; + u32 interrupt_num; + u64 db_bits; int ret; - func_no = ntb->epf->func_no; - vfunc_no = ntb->epf->vfunc_no; + if (!epf || !epf->epc) + return; + + func_no = epf->func_no; + vfunc_no = epf->vfunc_no; /* - * pci_epc_raise_irq() for MSI expects a 1-based interrupt number. - * ffs() returns a 1-based index (bit 0 -> 1). interrupt_num has already - * been computed as ffs(db_bits) + 1 above. Adding one more +1 when - * calling pci_epc_raise_irq() therefore results in: - * - * doorbell bit 0 -> MSI #3 - * - * Legacy mapping (kept for compatibility): - * - * MSI #1 : link event (reserved) - * MSI #2 : unused (historical offset) - * MSI #3 : doorbell bit 0 (DB#0) - * MSI #4 : doorbell bit 1 (DB#1) - * ... - * - * Do not change this mapping to avoid breaking interoperability with - * older peers. + * Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()). + * Limit the number of snapshots handled per run so we don't monopolize + * the workqueue under a doorbell storm. */ - ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, - PCI_IRQ_MSI, interrupt_num + 1); - if (ret) - dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + while (budget--) { + db_bits = atomic64_xchg(&ntb->peer_db_pending, 0); + if (!db_bits) + return; - return ret; + while (db_bits) { + /* + * pci_epc_raise_irq() for MSI expects a 1-based + * interrupt number. db_bit is zero-based, so add 3 to + * preserve the historical slot offset. + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking + * interoperability with older peers. + */ + db_bit = __ffs64(db_bits); + interrupt_num = db_bit + 3; + db_bits &= ~BIT_ULL(db_bit); + + ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, + PCI_IRQ_MSI, interrupt_num); + if (ret) + dev_err(&ntb->ntb.dev, + "Failed to raise IRQ for interrupt_num %u: %d\n", + interrupt_num, ret); + } + } + + if (atomic64_read(&ntb->peer_db_pending)) + queue_work(kpcintb_workqueue, &ntb->peer_db_work); +} + +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + db_bits &= vntb_epf_db_valid_mask(ndev); + if (!db_bits) + return 0; + + /* + * .peer_db_set() may be called from atomic context. pci_epc_raise_irq() + * can sleep (it takes epc->lock), so defer MSI raising to process + * context. Doorbell requests are coalesced in peer_db_pending. + */ + atomic64_or(db_bits, &ntb->peer_db_pending); + queue_work(kpcintb_workqueue, &ntb->peer_db_work); + + return 0; } static u64 vntb_epf_db_read(struct ntb_dev *ndev) @@ -1690,6 +1744,10 @@ static int epf_ntb_probe(struct pci_epf *epf, ntb->epf = epf; ntb->vbus_number = 0xff; + INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work); + disable_work(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); + /* Initially, no bar is assigned */ for (i = 0; i < VNTB_BAR_NUM; i++) ntb->epf_ntb_bar[i] = NO_BAR; From 91fb4488cd615a39360bc4160a10cb3236189ba1 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:14 +0900 Subject: [PATCH 4405/5214] PCI: endpoint: pci-epf-vntb: Report 0-based doorbell vector via ntb_db_event() ntb_db_event() expects the vector number to be relative to the first doorbell vector starting at 0. pci-epf-vntb reserves vector 0 for link events and uses higher vector indices for doorbells. By passing the raw slot index to ntb_db_event(), it effectively assumes that doorbell 0 maps to vector 1. However, because the host uses a legacy slot layout and writes doorbell 0 into the third slot, doorbell 0 ultimately appears as vector 2 from the NTB core perspective. Adjust pci-epf-vntb to: - skip the unused second slot, and - report doorbells as 0-based vectors (DB#0 -> vector 0). This change does not introduce a behavioral difference until .db_vector_count()/.db_vector_mask() are implemented, because without those callbacks NTB clients effectively ignore the vector number. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-4-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index cc0b356973f3..d31e2eee0869 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -83,6 +83,12 @@ enum epf_ntb_bar { VNTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + /* * +--------------------------------------------------+ Base * | | @@ -272,10 +278,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work) ntb = container_of(work, struct epf_ntb, cmd_handler.work); - for (i = 1; i < ntb->db_count && !ntb->msi_doorbell; i++) { + for (i = EPF_IRQ_DB_START; i < ntb->db_count && !ntb->msi_doorbell; + i++) { if (ntb->epf_db[i]) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); ntb->epf_db[i] = 0; } } @@ -341,10 +348,10 @@ static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data) struct epf_ntb *ntb = data; int i; - for (i = 1; i < ntb->db_count; i++) + for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++) if (irq == ntb->epf->db_msg[i].virq) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); } return IRQ_HANDLED; @@ -1450,8 +1457,8 @@ static void vntb_epf_peer_db_work(struct work_struct *work) while (db_bits) { /* * pci_epc_raise_irq() for MSI expects a 1-based - * interrupt number. db_bit is zero-based, so add 3 to - * preserve the historical slot offset. + * interrupt number. The first usable doorbell starts + * at EPF_IRQ_DB_START in the legacy slot layout. * * Legacy mapping (kept for compatibility): * @@ -1465,7 +1472,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work) * interoperability with older peers. */ db_bit = __ffs64(db_bits); - interrupt_num = db_bit + 3; + interrupt_num = db_bit + EPF_IRQ_DB_START + 1; db_bits &= ~BIT_ULL(db_bit); ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, From f417c400a6683c62cdead42013f60872fda84013 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:15 +0900 Subject: [PATCH 4406/5214] PCI: endpoint: pci-epf-vntb: Reject unusable doorbell counts pci-epf-vntb reserves slot 0 for link events and keeps slot 1 unused for legacy layout compatibility. A db_count smaller than MIN_DB_COUNT leaves no usable doorbell slot after those reservations. Reject such configurations when configuring interrupts. While at it, move MAX_DB_COUNT next to MIN_DB_COUNT. They are used as a pair in the range check, and keeping them together makes the valid doorbell range easier to read. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-5-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index d31e2eee0869..818ae5999976 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -67,7 +67,6 @@ static struct workqueue_struct *kpcintb_workqueue; #define NTB_MW_OFFSET 2 #define DB_COUNT_MASK GENMASK(15, 0) #define MSIX_ENABLE BIT(16) -#define MAX_DB_COUNT 32 #define MAX_MW 4 /* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */ @@ -89,6 +88,9 @@ enum epf_irq_slot { EPF_IRQ_DB_START, }; +#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1) +#define MAX_DB_COUNT 32 + /* * +--------------------------------------------------+ Base * | | @@ -512,9 +514,9 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count %d out of range (1 - %d)\n", - ntb->db_count, MAX_DB_COUNT); + if (ntb->db_count < MIN_DB_COUNT || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (%d - %d)\n", + ntb->db_count, MIN_DB_COUNT, MAX_DB_COUNT); return -EINVAL; } From 6c39350aaa2d6338ec30ef0a5632b0d6dad856ec Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:16 +0900 Subject: [PATCH 4407/5214] PCI: endpoint: pci-epf-vntb: Guard configfs writes after EPC attach db_count controls how many doorbell slots are allocated and exposed. It is also used by the doorbell mask helpers. After an EPC has been attached, changing it from configfs can leave runtime paths using a different count than the one used to set up the doorbell resources. Reject db_count writes after EPC attach, and reject values outside MIN_DB_COUNT..MAX_DB_COUNT before attach. Now that MIN_DB_COUNT documents the usable doorbell floor, use it in the store path too. While at it, apply the same after-attach guard to the other vNTB configfs knobs. BAR choices, spad_count, memory-window counts and sizes, and the virtual PCI IDs are also consumed during bind, so changing them later at runtime is meaningless and unsafe. Return -EOPNOTSUPP for after-attach writes. The value itself may be valid, but changing it in that state is not supported. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-6-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 818ae5999976..524355a8b4be 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1020,6 +1020,11 @@ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) epf_ntb_config_sspad_bar_clear(ntb); } +static bool epf_ntb_epc_attached(struct epf_ntb *ntb) +{ + return ntb->epf->epc || ntb->epf->sec_epc; +} + #define EPF_NTB_R(_name) \ static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ char *page) \ @@ -1039,6 +1044,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ u32 val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtou32(page, 0, &val); \ if (ret) \ return ret; \ @@ -1081,6 +1089,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ u64 val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtou64(page, 0, &val); \ if (ret) \ return ret; \ @@ -1119,6 +1130,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ int val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtoint(page, 0, &val); \ if (ret) \ return ret; \ @@ -1139,6 +1153,9 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item, u32 val; int ret; + if (epf_ntb_epc_attached(ntb)) + return -EOPNOTSUPP; + ret = kstrtou32(page, 0, &val); if (ret) return ret; @@ -1151,10 +1168,32 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item, return len; } +static ssize_t epf_ntb_db_count_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct epf_ntb *ntb = to_epf_ntb(group); + u32 val; + int ret; + + if (epf_ntb_epc_attached(ntb)) + return -EOPNOTSUPP; + + ret = kstrtou32(page, 0, &val); + if (ret) + return ret; + + if (val < MIN_DB_COUNT || val > MAX_DB_COUNT) + return -EINVAL; + + WRITE_ONCE(ntb->db_count, val); + + return len; +} + EPF_NTB_R(spad_count) EPF_NTB_W(spad_count) EPF_NTB_R(db_count) -EPF_NTB_W(db_count) EPF_NTB_R(num_mws) EPF_NTB_R(vbus_number) EPF_NTB_W(vbus_number) From 865730eec5435ce40a5dc3c615077d04c8f95098 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Sat, 13 Jun 2026 14:35:43 -0700 Subject: [PATCH 4408/5214] genirq/msi: Correct CONFIG_PCI_MSI_ARCH_FALLBACKS macro name in comment A comment in kernel/irq/msi.c incorrectly refers to CONFIG_PCI_MSI_ARCH_FALLBACK instead of CONFIG_PCI_MSI_ARCH_FALLBACKS. Correct it. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260613213544.90613-1-enelsonmoore@gmail.com --- kernel/irq/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 3cafa40e6ce3..903be7289c53 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -591,7 +591,7 @@ void msi_device_destroy_sysfs(struct device *dev) msi_for_each_desc(desc, dev, MSI_DESC_ALL) msi_sysfs_remove_desc(dev, desc); } -#endif /* CONFIG_PCI_MSI_ARCH_FALLBACK || CONFIG_PCI_XEN */ +#endif /* CONFIG_PCI_MSI_ARCH_FALLBACKS || CONFIG_PCI_XEN */ #else /* CONFIG_SYSFS */ static inline int msi_sysfs_create_group(struct device *dev) { return 0; } static inline int msi_sysfs_populate_desc(struct device *dev, struct msi_desc *desc) { return 0; } From 3a354149bceacadbcf7d7b4766f5ef26a85892ab Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Wed, 17 Jun 2026 23:20:20 +0800 Subject: [PATCH 4409/5214] bpf: Preserve pointer spill metadata during half-slot cleanup __clean_func_state() cleans dead stack slots in 4-byte halves. When the high half of a STACK_SPILL slot is dead and the low half remains live, cleanup converts the live low half to STACK_MISC or STACK_ZERO and clears the saved spilled_ptr metadata. That conversion is safe only for scalar spills. For a pointer spill, this metadata clear lets a later 32-bit fill from the still-live half avoid the normal non-scalar register-fill check and be treated as an ordinary scalar stack read. Leave non-scalar spill slots intact in this half-live shape. This is conservative for pruning and preserves the existing check_stack_read_fixed_off() rejection path for partial fills from pointer spills. Fixes: be23266b4a08 ("bpf: 4-byte precise clean_verifier_state") Acked-by: Eduard Zingerman Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260617-f01-06-half-slot-pointer-spill-v2-1-42b9cdc3cf64@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- kernel/bpf/states.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 32f346ce3ffc..ea2153cf28d0 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -436,12 +436,10 @@ static void __clean_func_state(struct bpf_verifier_env *env, continue; /* - * Only destroy spilled_ptr when hi half is dead. - * If hi half is still live with STACK_SPILL, the - * spilled_ptr metadata is needed for correct state - * comparison in stacksafe(). - * is_spilled_reg() is using slot_type[7], but - * is_spilled_scalar_after() check either slot_type[0] or [4] + * Only scalar spills can be degraded to raw stack bytes + * when their high half is dead. Pointer spills need the + * saved spilled_ptr metadata so partial fills keep + * rejecting as non-scalar register fills. */ if (!hi_live) { struct bpf_reg_state *spill = &st->stack[i].spilled_ptr; @@ -449,6 +447,9 @@ static void __clean_func_state(struct bpf_verifier_env *env, if (lo_live && stype == STACK_SPILL) { u8 val = STACK_MISC; + if (spill->type != SCALAR_VALUE) + continue; + /* * 8 byte spill of scalar 0 where half slot is dead * should become STACK_ZERO in lo 4 bytes. From 8816d94303f09222332e39a0119b7d0ce016e7a2 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Wed, 17 Jun 2026 23:20:21 +0800 Subject: [PATCH 4410/5214] selftests/bpf: Cover half-slot cleanup of pointer spills Add a verifier regression test for a pointer spill whose high half is cleaned dead while the low half remains live. Force checkpoint creation with BPF_F_TEST_STATE_FREQ and assert the verifier log reaches the checkpoint and the subsequent 32-bit fill before rejecting the partial fill from a non-scalar spill. Acked-by: Eduard Zingerman Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260617-f01-06-half-slot-pointer-spill-v2-2-42b9cdc3cf64@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/verifier_spill_fill.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c index 6bc721accbae..0174887e28f5 100644 --- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c +++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c @@ -1359,4 +1359,22 @@ __naked void var_off_write_over_scalar_spill(void) : __clobber_all); } +SEC("socket") +__description("partial fill from cleaned pointer spill") +__failure +__log_level(2) +__msg("1: (05) goto pc+0") +__msg("2: (61) r0 = *(u32 *)(r10 -4)") +__msg("invalid size of register fill") +__flag(BPF_F_TEST_STATE_FREQ) +__naked void partial_fill_from_cleaned_pointer_spill(void) +{ + /* Spill R1(ctx), then force a checkpoint and half-slot cleanup. */ + asm volatile ("*(u64 *)(r10 - 8) = r1;" + "goto +0;" + "r0 = *(u32 *)(r10 - 4);" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 3cd1f76be638b7386201171e7bb4c88095774dd5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 22 Jun 2026 07:29:39 -1000 Subject: [PATCH 4411/5214] sched_ext: Make kernel/sched/ext/ sources self-contained for clangd The sources under kernel/sched/ext/ build as a single translation unit: build_policy.c includes the source files and headers. An LSP/clangd editor parses each as a standalone unit, sees no types, and reports a flood of errors. Give each header its dependencies and include guard, and have each source include the headers it uses. ext.c, arena.c and the ext headers now parse clean standalone. idle.c and cid.c still reference a few macros and helpers defined in ext.c. The next patch moves those to shared headers. Suggested-by: Peter Zijlstra Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/arena.c | 4 ++++ kernel/sched/ext/arena.h | 2 ++ kernel/sched/ext/cid.c | 3 +++ kernel/sched/ext/cid.h | 2 ++ kernel/sched/ext/ext.c | 13 +++++++++++++ kernel/sched/ext/idle.c | 3 +++ kernel/sched/ext/idle.h | 4 ++++ kernel/sched/ext/internal.h | 8 ++++++++ kernel/sched/ext/types.h | 6 ++++++ 9 files changed, 45 insertions(+) diff --git a/kernel/sched/ext/arena.c b/kernel/sched/ext/arena.c index 493c2424f842..5783694ec21d 100644 --- a/kernel/sched/ext/arena.c +++ b/kernel/sched/ext/arena.c @@ -15,6 +15,10 @@ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. * Copyright (c) 2026 Tejun Heo */ +#include + +#include "internal.h" +#include "arena.h" enum scx_arena_consts { SCX_ARENA_MIN_ORDER = 3, /* 8-byte minimum sub-allocation */ diff --git a/kernel/sched/ext/arena.h b/kernel/sched/ext/arena.h index 4f3610160102..c378ae5fbc02 100644 --- a/kernel/sched/ext/arena.h +++ b/kernel/sched/ext/arena.h @@ -8,6 +8,8 @@ #ifndef _KERNEL_SCHED_EXT_ARENA_H #define _KERNEL_SCHED_EXT_ARENA_H +#include + struct scx_sched; s32 scx_arena_pool_init(struct scx_sched *sch); diff --git a/kernel/sched/ext/cid.c b/kernel/sched/ext/cid.c index aeaea88f34c5..af83084ec740 100644 --- a/kernel/sched/ext/cid.c +++ b/kernel/sched/ext/cid.c @@ -7,6 +7,9 @@ */ #include +#include "internal.h" +#include "cid.h" + /* * cid tables. * diff --git a/kernel/sched/ext/cid.h b/kernel/sched/ext/cid.h index 6e657fd147b0..41d0802c6af3 100644 --- a/kernel/sched/ext/cid.h +++ b/kernel/sched/ext/cid.h @@ -33,6 +33,8 @@ #ifndef _KERNEL_SCHED_EXT_CID_H #define _KERNEL_SCHED_EXT_CID_H +#include "internal.h" + struct scx_sched; /* diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 00fe6cc6d7e2..f3253c946764 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -6,6 +6,19 @@ * Copyright (c) 2022 Tejun Heo * Copyright (c) 2022 David Vernet */ +#include +#include +#include +#include +#include +#include +#include + +#include "../pelt.h" +#include "internal.h" +#include "cid.h" +#include "arena.h" +#include "idle.h" static DEFINE_RAW_SPINLOCK(scx_sched_lock); diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c index 2077373d8da3..8e8c6201b7df 100644 --- a/kernel/sched/ext/idle.c +++ b/kernel/sched/ext/idle.c @@ -9,6 +9,9 @@ * Copyright (c) 2022 David Vernet * Copyright (c) 2024 Andrea Righi */ +#include "internal.h" +#include "cid.h" +#include "idle.h" /* Enable/disable built-in idle CPU selection policy */ static DEFINE_STATIC_KEY_FALSE(scx_builtin_idle_enabled); diff --git a/kernel/sched/ext/idle.h b/kernel/sched/ext/idle.h index 8d169d3bbdf9..87a0e58f1eb7 100644 --- a/kernel/sched/ext/idle.h +++ b/kernel/sched/ext/idle.h @@ -10,7 +10,11 @@ #ifndef _KERNEL_SCHED_EXT_IDLE_H #define _KERNEL_SCHED_EXT_IDLE_H +#include + +struct cpumask; struct sched_ext_ops; +struct task_struct; extern struct btf_id_set8 scx_kfunc_ids_idle; extern struct btf_id_set8 scx_kfunc_ids_select_cpu; diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index b04701190b23..1f5312b3b387 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -5,6 +5,12 @@ * Copyright (c) 2025 Meta Platforms, Inc. and affiliates. * Copyright (c) 2025 Tejun Heo */ +#ifndef _KERNEL_SCHED_EXT_INTERNAL_H +#define _KERNEL_SCHED_EXT_INTERNAL_H + +#include "../sched.h" +#include "types.h" + #define SCX_OP_IDX(op) (offsetof(struct sched_ext_ops, op) / sizeof(void (*)(void))) #define SCX_MOFF_IDX(moff) ((moff) / sizeof(void (*)(void))) @@ -1651,3 +1657,5 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux) return rcu_dereference_all(scx_root); } #endif /* CONFIG_EXT_SUB_SCHED */ + +#endif /* _KERNEL_SCHED_EXT_INTERNAL_H */ diff --git a/kernel/sched/ext/types.h b/kernel/sched/ext/types.h index 8b3527e21fca..bc74eafd43f1 100644 --- a/kernel/sched/ext/types.h +++ b/kernel/sched/ext/types.h @@ -8,6 +8,12 @@ #ifndef _KERNEL_SCHED_EXT_TYPES_H #define _KERNEL_SCHED_EXT_TYPES_H +#include +#include +#include +#include +#include + enum scx_consts { SCX_DSP_DFL_MAX_BATCH = 32, SCX_DSP_MAX_LOOPS = 32, From 4437ad129cf5b37c00a5bc9fa5989d1da4d64d07 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 22 Jun 2026 07:29:39 -1000 Subject: [PATCH 4412/5214] sched_ext: Move shared helpers from ext.c into internal.h and cid.h idle.c and cid.c are included into build_policy.c together with ext.c and use helpers that ext.c defines. Because the helpers live in ext.c, the two files can not parse as standalone units and clangd reports errors in them. Move the helpers to the headers they belong to. The op-dispatch macros and helpers plus scx_parent() to internal.h, and scx_cpu_arg()/scx_cpu_ret() to cid.h. No functional change. idle.c and cid.c now parse clean standalone. Suggested-by: Peter Zijlstra Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/cid.h | 21 ++++++ kernel/sched/ext/ext.c | 141 ------------------------------------ kernel/sched/ext/internal.h | 121 +++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 141 deletions(-) diff --git a/kernel/sched/ext/cid.h b/kernel/sched/ext/cid.h index 41d0802c6af3..9c4f4b907f12 100644 --- a/kernel/sched/ext/cid.h +++ b/kernel/sched/ext/cid.h @@ -270,4 +270,25 @@ static inline u32 scx_cmask_nr_used_words(const struct scx_cmask *m) __w && ((cid) = __bs + __wi * 64 + __ffs64(__w), true); \ __w &= __w - 1) +/* + * scx_cpu_arg() wraps a cpu arg being handed to an SCX op. For cid-form + * schedulers it resolves to the matching cid; for cpu-form it passes @cpu + * through. scx_cpu_ret() is the inverse for a cpu/cid returned from an op + * (currently only ops.select_cpu); it validates the BPF-supplied cid and + * triggers scx_error() on @sch if invalid. + */ +static inline s32 scx_cpu_arg(s32 cpu) +{ + if (scx_is_cid_type()) + return __scx_cpu_to_cid(cpu); + return cpu; +} + +static inline s32 scx_cpu_ret(struct scx_sched *sch, s32 cpu_or_cid) +{ + if (cpu_or_cid < 0 || !scx_is_cid_type()) + return cpu_or_cid; + return scx_cid_to_cpu(sch, cpu_or_cid); +} + #endif /* _KERNEL_SCHED_EXT_CID_H */ diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index f3253c946764..691d53fe0f64 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -259,8 +259,6 @@ __printf(5, 6) bool __scx_exit(struct scx_sched *sch, return ret; } -#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op) - static long jiffies_delta_msecs(unsigned long at, unsigned long now) { if (time_after(at, now)) @@ -275,20 +273,6 @@ static bool u32_before(u32 a, u32 b) } #ifdef CONFIG_EXT_SUB_SCHED -/** - * scx_parent - Find the parent sched - * @sch: sched to find the parent of - * - * Returns the parent scheduler or %NULL if @sch is root. - */ -static struct scx_sched *scx_parent(struct scx_sched *sch) -{ - if (sch->level) - return sch->ancestors[sch->level - 1]; - else - return NULL; -} - /** * scx_next_descendant_pre - find the next descendant for pre-order walk * @pos: the current position (%NULL to initiate traversal) @@ -336,7 +320,6 @@ static void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch) rcu_assign_pointer(p->scx.sched, sch); } #else /* CONFIG_EXT_SUB_SCHED */ -static inline struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; } static inline struct scx_sched *scx_next_descendant_pre(struct scx_sched *pos, struct scx_sched *root) { return pos ? NULL : root; } static inline void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch) {} #endif /* CONFIG_EXT_SUB_SCHED */ @@ -496,123 +479,12 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags) */ DEFINE_PER_CPU(struct rq *, scx_locked_rq_state); -static inline void update_locked_rq(struct rq *rq) -{ - /* - * Check whether @rq is actually locked. This can help expose bugs - * or incorrect assumptions about the context in which a kfunc or - * callback is executed. - */ - if (rq) - lockdep_assert_rq_held(rq); - __this_cpu_write(scx_locked_rq_state, rq); -} - -/* - * SCX ops can recurse via scx_bpf_sub_dispatch() - the inner call must not - * clobber the outer's scx_locked_rq_state. Save it on entry, restore on exit. - */ -#define SCX_CALL_OP(sch, op, locked_rq, args...) \ -do { \ - struct rq *__prev_locked_rq; \ - \ - if (locked_rq) { \ - __prev_locked_rq = scx_locked_rq(); \ - update_locked_rq(locked_rq); \ - } \ - (sch)->ops.op(args); \ - if (locked_rq) \ - update_locked_rq(__prev_locked_rq); \ -} while (0) - /* * Flipped on enable per sch->is_cid_type. Declared in internal.h so * subsystem inlines can read it. */ DEFINE_STATIC_KEY_FALSE(__scx_is_cid_type); -/* - * scx_cpu_arg() wraps a cpu arg being handed to an SCX op. For cid-form - * schedulers it resolves to the matching cid; for cpu-form it passes @cpu - * through. scx_cpu_ret() is the inverse for a cpu/cid returned from an op - * (currently only ops.select_cpu); it validates the BPF-supplied cid and - * triggers scx_error() on @sch if invalid. - */ -static s32 scx_cpu_arg(s32 cpu) -{ - if (scx_is_cid_type()) - return __scx_cpu_to_cid(cpu); - return cpu; -} - -static s32 scx_cpu_ret(struct scx_sched *sch, s32 cpu_or_cid) -{ - if (cpu_or_cid < 0 || !scx_is_cid_type()) - return cpu_or_cid; - return scx_cid_to_cpu(sch, cpu_or_cid); -} - -#define SCX_CALL_OP_RET(sch, op, locked_rq, args...) \ -({ \ - struct rq *__prev_locked_rq; \ - __typeof__((sch)->ops.op(args)) __ret; \ - \ - if (locked_rq) { \ - __prev_locked_rq = scx_locked_rq(); \ - update_locked_rq(locked_rq); \ - } \ - __ret = (sch)->ops.op(args); \ - if (locked_rq) \ - update_locked_rq(__prev_locked_rq); \ - __ret; \ -}) - -/* - * SCX_CALL_OP_TASK*() invokes an SCX op that takes one or two task arguments - * and records them in current->scx.kf_tasks[] for the duration of the call. A - * kfunc invoked from inside such an op can then use - * scx_kf_arg_task_ok() to verify that its task argument is one of - * those subject tasks. - * - * Every SCX_CALL_OP_TASK*() call site invokes its op with @p's rq lock held - - * either via the @locked_rq argument here, or (for ops.select_cpu()) via @p's - * pi_lock held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu. - * So if kf_tasks[] is set, @p's scheduler-protected fields are stable. - * - * kf_tasks[] can not stack, so task-based SCX ops must not nest. The - * WARN_ON_ONCE() in each macro catches a re-entry of any of the three variants - * while a previous one is still in progress. - */ -#define SCX_CALL_OP_TASK(sch, op, locked_rq, task, args...) \ -do { \ - WARN_ON_ONCE(current->scx.kf_tasks[0]); \ - current->scx.kf_tasks[0] = task; \ - SCX_CALL_OP((sch), op, locked_rq, task, ##args); \ - current->scx.kf_tasks[0] = NULL; \ -} while (0) - -#define SCX_CALL_OP_TASK_RET(sch, op, locked_rq, task, args...) \ -({ \ - __typeof__((sch)->ops.op(task, ##args)) __ret; \ - WARN_ON_ONCE(current->scx.kf_tasks[0]); \ - current->scx.kf_tasks[0] = task; \ - __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task, ##args); \ - current->scx.kf_tasks[0] = NULL; \ - __ret; \ -}) - -#define SCX_CALL_OP_2TASKS_RET(sch, op, locked_rq, task0, task1, args...) \ -({ \ - __typeof__((sch)->ops.op(task0, task1, ##args)) __ret; \ - WARN_ON_ONCE(current->scx.kf_tasks[0]); \ - current->scx.kf_tasks[0] = task0; \ - current->scx.kf_tasks[1] = task1; \ - __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task0, task1, ##args); \ - current->scx.kf_tasks[0] = NULL; \ - current->scx.kf_tasks[1] = NULL; \ - __ret; \ -}) - /** * scx_call_op_set_cpumask - invoke ops.set_cpumask / ops_cid.set_cmask for @task * @sch: scx_sched being invoked @@ -651,19 +523,6 @@ static inline void scx_call_op_set_cpumask(struct scx_sched *sch, struct rq *rq, current->scx.kf_tasks[0] = NULL; } -/* see SCX_CALL_OP_TASK() */ -static __always_inline bool scx_kf_arg_task_ok(struct scx_sched *sch, - struct task_struct *p) -{ - if (unlikely((p != current->scx.kf_tasks[0] && - p != current->scx.kf_tasks[1]))) { - scx_error(sch, "called on a task not being operated on"); - return false; - } - - return true; -} - enum scx_dsq_iter_flags { /* iterate in the reverse dispatch order */ SCX_DSQ_ITER_REV = 1U << 16, diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index 1f5312b3b387..145272cb4d8a 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -1553,6 +1553,111 @@ static inline struct rq *scx_locked_rq(void) return __this_cpu_read(scx_locked_rq_state); } +static inline void update_locked_rq(struct rq *rq) +{ + /* + * Check whether @rq is actually locked. This can help expose bugs + * or incorrect assumptions about the context in which a kfunc or + * callback is executed. + */ + if (rq) + lockdep_assert_rq_held(rq); + __this_cpu_write(scx_locked_rq_state, rq); +} + +#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op) + +/* + * SCX ops can recurse via scx_bpf_sub_dispatch() - the inner call must not + * clobber the outer's scx_locked_rq_state. Save it on entry, restore on exit. + */ +#define SCX_CALL_OP(sch, op, locked_rq, args...) \ +do { \ + struct rq *__prev_locked_rq; \ + \ + if (locked_rq) { \ + __prev_locked_rq = scx_locked_rq(); \ + update_locked_rq(locked_rq); \ + } \ + (sch)->ops.op(args); \ + if (locked_rq) \ + update_locked_rq(__prev_locked_rq); \ +} while (0) + +#define SCX_CALL_OP_RET(sch, op, locked_rq, args...) \ +({ \ + struct rq *__prev_locked_rq; \ + __typeof__((sch)->ops.op(args)) __ret; \ + \ + if (locked_rq) { \ + __prev_locked_rq = scx_locked_rq(); \ + update_locked_rq(locked_rq); \ + } \ + __ret = (sch)->ops.op(args); \ + if (locked_rq) \ + update_locked_rq(__prev_locked_rq); \ + __ret; \ +}) + +/* + * SCX_CALL_OP_TASK*() invokes an SCX op that takes one or two task arguments + * and records them in current->scx.kf_tasks[] for the duration of the call. A + * kfunc invoked from inside such an op can then use + * scx_kf_arg_task_ok() to verify that its task argument is one of + * those subject tasks. + * + * Every SCX_CALL_OP_TASK*() call site invokes its op with @p's rq lock held - + * either via the @locked_rq argument here, or (for ops.select_cpu()) via @p's + * pi_lock held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu. + * So if kf_tasks[] is set, @p's scheduler-protected fields are stable. + * + * kf_tasks[] can not stack, so task-based SCX ops must not nest. The + * WARN_ON_ONCE() in each macro catches a re-entry of any of the three variants + * while a previous one is still in progress. + */ +#define SCX_CALL_OP_TASK(sch, op, locked_rq, task, args...) \ +do { \ + WARN_ON_ONCE(current->scx.kf_tasks[0]); \ + current->scx.kf_tasks[0] = task; \ + SCX_CALL_OP((sch), op, locked_rq, task, ##args); \ + current->scx.kf_tasks[0] = NULL; \ +} while (0) + +#define SCX_CALL_OP_TASK_RET(sch, op, locked_rq, task, args...) \ +({ \ + __typeof__((sch)->ops.op(task, ##args)) __ret; \ + WARN_ON_ONCE(current->scx.kf_tasks[0]); \ + current->scx.kf_tasks[0] = task; \ + __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task, ##args); \ + current->scx.kf_tasks[0] = NULL; \ + __ret; \ +}) + +#define SCX_CALL_OP_2TASKS_RET(sch, op, locked_rq, task0, task1, args...) \ +({ \ + __typeof__((sch)->ops.op(task0, task1, ##args)) __ret; \ + WARN_ON_ONCE(current->scx.kf_tasks[0]); \ + current->scx.kf_tasks[0] = task0; \ + current->scx.kf_tasks[1] = task1; \ + __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task0, task1, ##args); \ + current->scx.kf_tasks[0] = NULL; \ + current->scx.kf_tasks[1] = NULL; \ + __ret; \ +}) + +/* see SCX_CALL_OP_TASK() */ +static __always_inline bool scx_kf_arg_task_ok(struct scx_sched *sch, + struct task_struct *p) +{ + if (unlikely((p != current->scx.kf_tasks[0] && + p != current->scx.kf_tasks[1]))) { + scx_error(sch, "called on a task not being operated on"); + return false; + } + + return true; +} + static inline bool scx_bypassing(struct scx_sched *sch, s32 cpu) { return unlikely(per_cpu_ptr(sch->pcpu, cpu)->flags & @@ -1633,6 +1738,20 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux) return NULL; } + +/** + * scx_parent - Find the parent sched + * @sch: sched to find the parent of + * + * Returns the parent scheduler or %NULL if @sch is root. + */ +static inline struct scx_sched *scx_parent(struct scx_sched *sch) +{ + if (sch->level) + return sch->ancestors[sch->level - 1]; + else + return NULL; +} #else /* CONFIG_EXT_SUB_SCHED */ static inline struct scx_sched *scx_task_sched(const struct task_struct *p) { @@ -1656,6 +1775,8 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux) { return rcu_dereference_all(scx_root); } + +static inline struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; } #endif /* CONFIG_EXT_SUB_SCHED */ #endif /* _KERNEL_SCHED_EXT_INTERNAL_H */ From e120e2b666851c4c0c7bffd315ff69a09f9fe4ac Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Thu, 7 May 2026 08:05:30 +0000 Subject: [PATCH 4413/5214] ceph: convert inode flags to named bit positions and atomic bitops Define named bit-position constants for all CEPH_I_* inode flags and derive the bitmask values from them. This gives every flag a named _BIT constant usable with the test_bit/set_bit/clear_bit family. The intentionally unused bit position 1 is documented inline. Convert all flag modifications to use atomic bitops (set_bit, clear_bit, test_and_clear_bit). The previous code mixed lockless atomic ops on some flags (ERROR_WRITE, ODIRECT) with non-atomic read-modify-write (|= / &= ~) on other flags sharing the same unsigned long. A concurrent non-atomic RMW can clobber an adjacent lockless atomic update -- for example, a lockless clear_bit(ERROR_WRITE) could be silently resurrected by a concurrent ci->i_ceph_flags |= CEPH_I_FLUSH under the spinlock. Using atomic bitops for all modifications eliminates this class of race entirely. Flags whose only users are now the _BIT form (ERROR_WRITE, ASYNC_CHECK_CAPS) have their old mask defines removed to document that callers must use the _BIT constant with the set_bit/test_bit family. ERROR_FILELOCK and SHUTDOWN retain their mask defines because they are still used via bitmask tests in lockless readers (ceph_inode_is_shutdown, reconnect_caps_cb). The direct assignment in ceph_finish_async_create() is converted from i_ceph_flags = CEPH_I_ASYNC_CREATE to set_bit(). This inode is I_NEW at this point -- still invisible to other threads and guaranteed to have zero flags from alloc_inode -- so either form is safe, but set_bit() keeps the conversion uniform. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/addr.c | 20 +++++++------- fs/ceph/caps.c | 24 ++++++++--------- fs/ceph/file.c | 13 ++++----- fs/ceph/inode.c | 4 +-- fs/ceph/locks.c | 22 ++++----------- fs/ceph/mds_client.c | 3 ++- fs/ceph/mds_client.h | 2 +- fs/ceph/snap.c | 2 +- fs/ceph/super.h | 64 +++++++++++++++++++++++--------------------- fs/ceph/xattr.c | 2 +- 10 files changed, 74 insertions(+), 82 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 0a86f672cc09..61bd6d92ff25 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -2561,7 +2561,8 @@ int ceph_pool_perm_check(struct inode *inode, int need) struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_string *pool_ns; s64 pool; - int ret, flags; + int ret; + unsigned long flags; /* Only need to do this for regular files */ if (!S_ISREG(inode->i_mode)) @@ -2603,20 +2604,19 @@ int ceph_pool_perm_check(struct inode *inode, int need) if (ret < 0) return ret; - flags = CEPH_I_POOL_PERM; - if (ret & POOL_READ) - flags |= CEPH_I_POOL_RD; - if (ret & POOL_WRITE) - flags |= CEPH_I_POOL_WR; - spin_lock(&ci->i_ceph_lock); if (pool == ci->i_layout.pool_id && pool_ns == rcu_dereference_raw(ci->i_layout.pool_ns)) { - ci->i_ceph_flags |= flags; - } else { + set_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); + if (ret & POOL_READ) + set_bit(CEPH_I_POOL_RD_BIT, &ci->i_ceph_flags); + if (ret & POOL_WRITE) + set_bit(CEPH_I_POOL_WR_BIT, &ci->i_ceph_flags); + } else { pool = ci->i_layout.pool_id; - flags = ci->i_ceph_flags; } + /* Re-read flags under the lock so check: sees the updated bits. */ + flags = ci->i_ceph_flags; spin_unlock(&ci->i_ceph_lock); goto check; } diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d51454e995a8..cb9e78b713d9 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -549,7 +549,7 @@ static void __cap_delay_requeue_front(struct ceph_mds_client *mdsc, doutc(mdsc->fsc->client, "%p %llx.%llx\n", inode, ceph_vinop(inode)); spin_lock(&mdsc->cap_delay_lock); - ci->i_ceph_flags |= CEPH_I_FLUSH; + set_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); if (!list_empty(&ci->i_cap_delay_list)) list_del_init(&ci->i_cap_delay_list); list_add(&ci->i_cap_delay_list, &mdsc->cap_delay_list); @@ -1409,7 +1409,7 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap, ceph_cap_string(revoking)); BUG_ON((retain & CEPH_CAP_PIN) == 0); - ci->i_ceph_flags &= ~CEPH_I_FLUSH; + clear_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); cap->issued &= retain; /* drop bits we don't want */ /* @@ -1666,7 +1666,7 @@ static void __ceph_flush_snaps(struct ceph_inode_info *ci, last_tid = capsnap->cap_flush.tid; } - ci->i_ceph_flags &= ~CEPH_I_FLUSH_SNAPS; + clear_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); while (first_tid <= last_tid) { struct ceph_cap *cap = ci->i_auth_cap; @@ -2026,7 +2026,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags) spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) { - ci->i_ceph_flags |= CEPH_I_ASYNC_CHECK_CAPS; + set_bit(CEPH_I_ASYNC_CHECK_CAPS_BIT, &ci->i_ceph_flags); /* Don't send messages until we get async create reply */ spin_unlock(&ci->i_ceph_lock); @@ -2577,7 +2577,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc, if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) return; - ci->i_ceph_flags &= ~CEPH_I_KICK_FLUSH; + clear_bit(CEPH_I_KICK_FLUSH_BIT, &ci->i_ceph_flags); list_for_each_entry_reverse(cf, &ci->i_cap_flush_list, i_list) { if (cf->is_capsnap) { @@ -2686,7 +2686,7 @@ void ceph_early_kick_flushing_caps(struct ceph_mds_client *mdsc, __kick_flushing_caps(mdsc, session, ci, oldest_flush_tid); } else { - ci->i_ceph_flags |= CEPH_I_KICK_FLUSH; + set_bit(CEPH_I_KICK_FLUSH_BIT, &ci->i_ceph_flags); } spin_unlock(&ci->i_ceph_lock); @@ -2829,7 +2829,7 @@ static int try_get_cap_refs(struct inode *inode, int need, int want, spin_lock(&ci->i_ceph_lock); if ((flags & CHECK_FILELOCK) && - (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK)) { + test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { doutc(cl, "%p %llx.%llx error filelock\n", inode, ceph_vinop(inode)); ret = -EIO; @@ -3207,7 +3207,7 @@ static int ceph_try_drop_cap_snap(struct ceph_inode_info *ci, BUG_ON(capsnap->cap_flush.tid > 0); ceph_put_snap_context(capsnap->context); if (!list_is_last(&capsnap->ci_item, &ci->i_cap_snaps)) - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); list_del(&capsnap->ci_item); ceph_put_cap_snap(capsnap); @@ -3396,7 +3396,7 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, if (ceph_try_drop_cap_snap(ci, capsnap)) { put++; } else { - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); flush_snaps = true; } } @@ -3648,7 +3648,7 @@ static void handle_cap_grant(struct inode *inode, if (ci->i_layout.pool_id != old_pool || extra_info->pool_ns != old_ns) - ci->i_ceph_flags &= ~CEPH_I_POOL_PERM; + clear_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); extra_info->pool_ns = old_ns; @@ -4815,7 +4815,7 @@ int ceph_drop_caps_for_unlink(struct inode *inode) doutc(mdsc->fsc->client, "%p %llx.%llx\n", inode, ceph_vinop(inode)); spin_lock(&mdsc->cap_delay_lock); - ci->i_ceph_flags |= CEPH_I_FLUSH; + set_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); if (!list_empty(&ci->i_cap_delay_list)) list_del_init(&ci->i_cap_delay_list); list_add_tail(&ci->i_cap_delay_list, @@ -5080,7 +5080,7 @@ int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invali if (atomic_read(&ci->i_filelock_ref) > 0) { /* make further file lock syscall return -EIO */ - ci->i_ceph_flags |= CEPH_I_ERROR_FILELOCK; + set_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags); pr_warn_ratelimited_client(cl, " dropping file locks for %p %llx.%llx\n", inode, ceph_vinop(inode)); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d54d71669176..7ca9f60fb0e5 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -598,12 +598,12 @@ static void wake_async_create_waiters(struct inode *inode, spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) { - clear_and_wake_up_bit(CEPH_ASYNC_CREATE_BIT, &ci->i_ceph_flags); + /* Serialized by i_ceph_lock; the two ops touch different bits. */ + clear_and_wake_up_bit(CEPH_I_ASYNC_CREATE_BIT, &ci->i_ceph_flags); - if (ci->i_ceph_flags & CEPH_I_ASYNC_CHECK_CAPS) { - ci->i_ceph_flags &= ~CEPH_I_ASYNC_CHECK_CAPS; + if (test_and_clear_bit(CEPH_I_ASYNC_CHECK_CAPS_BIT, + &ci->i_ceph_flags)) check_cap = true; - } } ceph_kick_flushing_inode_caps(session, ci); spin_unlock(&ci->i_ceph_lock); @@ -766,7 +766,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode, * that point and don't worry about setting * CEPH_I_ASYNC_CREATE. */ - ceph_inode(inode)->i_ceph_flags = CEPH_I_ASYNC_CREATE; + set_bit(CEPH_I_ASYNC_CREATE_BIT, + &ceph_inode(inode)->i_ceph_flags); unlock_new_inode(inode); } if (d_in_lookup(dentry) || d_really_is_negative(dentry)) { @@ -2482,7 +2483,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || (iocb->ki_flags & IOCB_DIRECT) || (fi->flags & CEPH_F_SYNC) || - (ci->i_ceph_flags & CEPH_I_ERROR_WRITE)) { + test_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags)) { struct ceph_snap_context *snapc; struct iov_iter data; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 22c7da1ea61c..4871d7ab2730 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1180,7 +1180,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, rcu_assign_pointer(ci->i_layout.pool_ns, pool_ns); if (ci->i_layout.pool_id != old_pool || pool_ns != old_ns) - ci->i_ceph_flags &= ~CEPH_I_POOL_PERM; + clear_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); pool_ns = old_ns; @@ -3240,7 +3240,7 @@ void ceph_inode_shutdown(struct inode *inode) bool invalidate = false; spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags |= CEPH_I_SHUTDOWN; + set_bit(CEPH_I_SHUTDOWN_BIT, &ci->i_ceph_flags); p = rb_first(&ci->i_caps); while (p) { struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index dd764f9c64b9..c4ff2266bb94 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -57,9 +57,7 @@ static void ceph_fl_release_lock(struct file_lock *fl) ci = ceph_inode(inode); if (atomic_dec_and_test(&ci->i_filelock_ref)) { /* clear error when all locks are released */ - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags &= ~CEPH_I_ERROR_FILELOCK; - spin_unlock(&ci->i_ceph_lock); + clear_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags); } fl->fl_u.ceph.inode = NULL; iput(inode); @@ -271,15 +269,10 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl) else if (IS_SETLKW(cmd)) wait = 1; - spin_lock(&ci->i_ceph_lock); - if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { - err = -EIO; - } - spin_unlock(&ci->i_ceph_lock); - if (err < 0) { + if (test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { if (op == CEPH_MDS_OP_SETFILELOCK && lock_is_unlock(fl)) posix_lock_file(file, fl, NULL); - return err; + return -EIO; } if (lock_is_read(fl)) @@ -331,15 +324,10 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) doutc(cl, "fl_file: %p\n", fl->c.flc_file); - spin_lock(&ci->i_ceph_lock); - if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { - err = -EIO; - } - spin_unlock(&ci->i_ceph_lock); - if (err < 0) { + if (test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { if (lock_is_unlock(fl)) locks_lock_file_wait(file, fl); - return err; + return -EIO; } if (IS_SETLKW(cmd)) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ed17e0023705..53f1012a9e7d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -3657,7 +3657,8 @@ static void __do_request(struct ceph_mds_client *mdsc, spin_lock(&ci->i_ceph_lock); cap = ci->i_auth_cap; - if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE && mds != cap->mds) { + if (test_bit(CEPH_I_ASYNC_CREATE_BIT, &ci->i_ceph_flags) && + mds != cap->mds) { doutc(cl, "session changed for auth cap %d -> %d\n", cap->session->s_mds, session->s_mds); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 4e6c87f8414c..d873e784b025 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -670,7 +670,7 @@ static inline int ceph_wait_on_async_create(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); - return wait_on_bit(&ci->i_ceph_flags, CEPH_ASYNC_CREATE_BIT, + return wait_on_bit(&ci->i_ceph_flags, CEPH_I_ASYNC_CREATE_BIT, TASK_KILLABLE); } diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 52b4c2684f92..9b79a5eaca93 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -700,7 +700,7 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci, return 0; } - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); doutc(cl, "%p %llx.%llx cap_snap %p snapc %p %llu %s s=%llu\n", inode, ceph_vinop(inode), capsnap, capsnap->context, capsnap->context->seq, ceph_cap_string(capsnap->dirty), diff --git a/fs/ceph/super.h b/fs/ceph/super.h index afc89ce91804..cb45a59dbb19 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -665,23 +665,34 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, /* * Ceph inode. */ -#define CEPH_I_DIR_ORDERED (1 << 0) /* dentries in dir are ordered */ -#define CEPH_I_FLUSH (1 << 2) /* do not delay flush of dirty metadata */ -#define CEPH_I_POOL_PERM (1 << 3) /* pool rd/wr bits are valid */ -#define CEPH_I_POOL_RD (1 << 4) /* can read from pool */ -#define CEPH_I_POOL_WR (1 << 5) /* can write to pool */ -#define CEPH_I_SEC_INITED (1 << 6) /* security initialized */ -#define CEPH_I_KICK_FLUSH (1 << 7) /* kick flushing caps */ -#define CEPH_I_FLUSH_SNAPS (1 << 8) /* need flush snapss */ -#define CEPH_I_ERROR_WRITE (1 << 9) /* have seen write errors */ -#define CEPH_I_ERROR_FILELOCK (1 << 10) /* have seen file lock errors */ -#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */ -#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) -#define CEPH_ASYNC_CREATE_BIT (12) /* async create in flight for this */ -#define CEPH_I_ASYNC_CREATE (1 << CEPH_ASYNC_CREATE_BIT) -#define CEPH_I_SHUTDOWN (1 << 13) /* inode is no longer usable */ -#define CEPH_I_ASYNC_CHECK_CAPS (1 << 14) /* check caps immediately after async - creating finishes */ +#define CEPH_I_DIR_ORDERED_BIT (0) /* dentries in dir are ordered */ + /* bit 1 historically unused */ +#define CEPH_I_FLUSH_BIT (2) /* do not delay flush of dirty metadata */ +#define CEPH_I_POOL_PERM_BIT (3) /* pool rd/wr bits are valid */ +#define CEPH_I_POOL_RD_BIT (4) /* can read from pool */ +#define CEPH_I_POOL_WR_BIT (5) /* can write to pool */ +#define CEPH_I_SEC_INITED_BIT (6) /* security initialized */ +#define CEPH_I_KICK_FLUSH_BIT (7) /* kick flushing caps */ +#define CEPH_I_FLUSH_SNAPS_BIT (8) /* need flush snaps */ +#define CEPH_I_ERROR_WRITE_BIT (9) /* have seen write errors */ +#define CEPH_I_ERROR_FILELOCK_BIT (10) /* have seen file lock errors */ +#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */ +#define CEPH_I_ASYNC_CREATE_BIT (12) /* async create in flight for this */ +#define CEPH_I_SHUTDOWN_BIT (13) /* inode is no longer usable */ +#define CEPH_I_ASYNC_CHECK_CAPS_BIT (14) /* check caps after async creating finishes */ + +#define CEPH_I_DIR_ORDERED (1 << CEPH_I_DIR_ORDERED_BIT) +#define CEPH_I_FLUSH (1 << CEPH_I_FLUSH_BIT) +#define CEPH_I_POOL_PERM (1 << CEPH_I_POOL_PERM_BIT) +#define CEPH_I_POOL_RD (1 << CEPH_I_POOL_RD_BIT) +#define CEPH_I_POOL_WR (1 << CEPH_I_POOL_WR_BIT) +#define CEPH_I_SEC_INITED (1 << CEPH_I_SEC_INITED_BIT) +#define CEPH_I_KICK_FLUSH (1 << CEPH_I_KICK_FLUSH_BIT) +#define CEPH_I_FLUSH_SNAPS (1 << CEPH_I_FLUSH_SNAPS_BIT) +#define CEPH_I_ERROR_FILELOCK (1 << CEPH_I_ERROR_FILELOCK_BIT) +#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) +#define CEPH_I_ASYNC_CREATE (1 << CEPH_I_ASYNC_CREATE_BIT) +#define CEPH_I_SHUTDOWN (1 << CEPH_I_SHUTDOWN_BIT) /* * Masks of ceph inode work. @@ -694,27 +705,18 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, /* * We set the ERROR_WRITE bit when we start seeing write errors on an inode - * and then clear it when they start succeeding. Note that we do a lockless - * check first, and only take the lock if it looks like it needs to be changed. - * The write submission code just takes this as a hint, so we're not too - * worried if a few slip through in either direction. + * and then clear it when they start succeeding. The write submission code + * just takes this as a hint, so we're not too worried if a few slip through + * in either direction. */ static inline void ceph_set_error_write(struct ceph_inode_info *ci) { - if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ERROR_WRITE)) { - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags |= CEPH_I_ERROR_WRITE; - spin_unlock(&ci->i_ceph_lock); - } + set_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags); } static inline void ceph_clear_error_write(struct ceph_inode_info *ci) { - if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ERROR_WRITE) { - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags &= ~CEPH_I_ERROR_WRITE; - spin_unlock(&ci->i_ceph_lock); - } + clear_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags); } static inline void __ceph_dir_set_complete(struct ceph_inode_info *ci, diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index e773be07f767..860fc8e1867d 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1054,7 +1054,7 @@ ssize_t __ceph_getxattr(struct inode *inode, const char *name, void *value, if (current->journal_info && !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && security_ismaclabel(name + XATTR_SECURITY_PREFIX_LEN)) - ci->i_ceph_flags |= CEPH_I_SEC_INITED; + set_bit(CEPH_I_SEC_INITED_BIT, &ci->i_ceph_flags); out: spin_unlock(&ci->i_ceph_lock); return err; From 248e514000d552095025de0473165becfe8e810d Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Tue, 28 Apr 2026 07:41:33 +0000 Subject: [PATCH 4414/5214] ceph: use proper endian conversion for flock_len in reconnect Replace the __force __le32 cast with cpu_to_le32() for the flock_len field in reconnect_caps_cb(). The old code used a type-system bypass to silence sparse; the new form uses the proper endian conversion macro. Also switch from a raw bitmask test against i_ceph_flags to test_bit() on the named CEPH_I_ERROR_FILELOCK_BIT, which is the correct accessor for the unsigned long flags field after the bit-position conversion. Remove the now-unused CEPH_I_ERROR_FILELOCK mask define since all callers use the _BIT form with test_bit/set_bit/clear_bit. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/mds_client.c | 5 +++-- fs/ceph/super.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 53f1012a9e7d..d9543399b129 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -4747,8 +4747,9 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) rec.v2.issued = cpu_to_le32(cap->issued); rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); rec.v2.pathbase = cpu_to_le64(path_info.vino.ino); - rec.v2.flock_len = (__force __le32) - ((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1); + rec.v2.flock_len = cpu_to_le32( + test_bit(CEPH_I_ERROR_FILELOCK_BIT, + &ci->i_ceph_flags) ? 0 : 1); } else { struct timespec64 ts; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index cb45a59dbb19..8afc6f3a10da 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -689,7 +689,6 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, #define CEPH_I_SEC_INITED (1 << CEPH_I_SEC_INITED_BIT) #define CEPH_I_KICK_FLUSH (1 << CEPH_I_KICK_FLUSH_BIT) #define CEPH_I_FLUSH_SNAPS (1 << CEPH_I_FLUSH_SNAPS_BIT) -#define CEPH_I_ERROR_FILELOCK (1 << CEPH_I_ERROR_FILELOCK_BIT) #define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) #define CEPH_I_ASYNC_CREATE (1 << CEPH_I_ASYNC_CREATE_BIT) #define CEPH_I_SHUTDOWN (1 << CEPH_I_SHUTDOWN_BIT) From 39fe3031589386ae7ce3fd7132beb6bb229e22ce Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Tue, 28 Apr 2026 07:43:03 +0000 Subject: [PATCH 4415/5214] ceph: harden send_mds_reconnect and handle active-MDS peer reset Change send_mds_reconnect() to return an error code so callers can detect and report reconnect failures instead of silently ignoring them. Add early bailout checks for sessions that are already closed, rejected, or unregistered, which avoids sending reconnect messages for sessions that can no longer be recovered. The early -ESTALE and -ENOENT bailouts use a separate fail_return label that skips the pr_err_client diagnostic, since these codes indicate expected concurrent-teardown races rather than genuine reconnect build failures. Move the "reconnect start" log after the early-bailout checks so it only appears for sessions that actually proceed with reconnect. Save the prior session state before transitioning to RECONNECTING, and restore it in the failure path. Without this, a transient build or encoding failure (-ENOMEM, -ENOSPC) strands the session in RECONNECTING indefinitely because check_new_map() only retries sessions in RESTARTING state. Rewrite mds_peer_reset() to handle the case where the MDS is past its RECONNECT phase (i.e. active). An active MDS rejects CLIENT_RECONNECT messages because it only accepts them during its own RECONNECT window after restart. Previously, the client would send a doomed reconnect that the MDS would reject or ignore. Now, the client tears the session down locally and lets new requests re-open a fresh session, which is the correct recovery for this scenario. The RECONNECTING state is handled on the same teardown path, since the MDS will reject reconnect attempts from an active client regardless of the session's local state. Add explicit cases for CLOSED and REJECTED session states in mds_peer_reset() since these are terminal states where a connection drop is expected behavior. The session teardown path in mds_peer_reset() follows the established drop-and-reacquire locking pattern from check_new_map(): take mdsc->mutex for session unregistration, release it, then take s->s_mutex separately for cleanup. This avoids introducing a new simultaneous lock nesting pattern. Log reconnect failures from check_new_map() and mds_peer_reset() at pr_warn level rather than pr_err, since return codes like -ESTALE (closed/rejected session) and -ENOENT (unregistered session) are expected during concurrent teardown. Log dropped messages for unregistered sessions via doutc() (dynamic debug) rather than pr_info, as post-reset message arrival is routine and does not warrant unconditional logging. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/mds_client.c | 180 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 164 insertions(+), 16 deletions(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index d9543399b129..249419c17d3c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -4470,9 +4470,14 @@ static void handle_session(struct ceph_mds_session *session, break; case CEPH_SESSION_REJECT: - WARN_ON(session->s_state != CEPH_MDS_SESSION_OPENING); - pr_info_client(cl, "mds%d rejected session\n", - session->s_mds); + WARN_ON(session->s_state != CEPH_MDS_SESSION_OPENING && + session->s_state != CEPH_MDS_SESSION_RECONNECTING); + if (session->s_state == CEPH_MDS_SESSION_RECONNECTING) + pr_info_client(cl, "mds%d reconnect rejected\n", + session->s_mds); + else + pr_info_client(cl, "mds%d rejected session\n", + session->s_mds); session->s_state = CEPH_MDS_SESSION_REJECTED; cleanup_session_requests(mdsc, session); remove_session_caps(session); @@ -4732,6 +4737,14 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) cap->mseq = 0; /* and migrate_seq */ cap->cap_gen = atomic_read(&cap->session->s_cap_gen); + /* + * Note: CEPH_I_ERROR_FILELOCK is not set during reconnect. + * Instead, locks are submitted for best-effort MDS reclaim + * via the flock_len field below. If reclaim fails (e.g., + * another client grabbed a conflicting lock), future lock + * operations will fail and set the error flag at that point. + */ + /* These are lost when the session goes away */ if (S_ISDIR(inode->i_mode)) { if (cap->issued & CEPH_CAP_DIR_CREATE) { @@ -4946,20 +4959,19 @@ static int encode_snap_realms(struct ceph_mds_client *mdsc, * * This is a relatively heavyweight operation, but it's rare. */ -static void send_mds_reconnect(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) +static int send_mds_reconnect(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) { struct ceph_client *cl = mdsc->fsc->client; struct ceph_msg *reply; int mds = session->s_mds; int err = -ENOMEM; + int old_state; struct ceph_reconnect_state recon_state = { .session = session, }; LIST_HEAD(dispose); - pr_info_client(cl, "mds%d reconnect start\n", mds); - recon_state.pagelist = ceph_pagelist_alloc(GFP_NOFS); if (!recon_state.pagelist) goto fail_nopagelist; @@ -4968,9 +4980,37 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, if (!reply) goto fail_nomsg; - xa_destroy(&session->s_delegated_inos); - mutex_lock(&session->s_mutex); + + /* Serialized by s_mutex against concurrent ceph_get_deleg_ino(). */ + xa_destroy(&session->s_delegated_inos); + if (session->s_state == CEPH_MDS_SESSION_CLOSED || + session->s_state == CEPH_MDS_SESSION_REJECTED) { + pr_info_client(cl, "mds%d skipping reconnect, session %s\n", + mds, + ceph_session_state_name(session->s_state)); + mutex_unlock(&session->s_mutex); + ceph_msg_put(reply); + err = -ESTALE; + goto fail_return; + } + + /* s_mutex -> mdsc->mutex matches cleanup_session_requests() order. */ + mutex_lock(&mdsc->mutex); + if (mds >= mdsc->max_sessions || mdsc->sessions[mds] != session) { + mutex_unlock(&mdsc->mutex); + pr_info_client(cl, + "mds%d skipping reconnect, session unregistered\n", + mds); + mutex_unlock(&session->s_mutex); + ceph_msg_put(reply); + err = -ENOENT; + goto fail_return; + } + mutex_unlock(&mdsc->mutex); + + pr_info_client(cl, "mds%d reconnect start\n", mds); + old_state = session->s_state; session->s_state = CEPH_MDS_SESSION_RECONNECTING; session->s_seq = 0; @@ -5100,7 +5140,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, up_read(&mdsc->snap_rwsem); ceph_pagelist_release(recon_state.pagelist); - return; + return 0; fail_clear_cap_reconnect: spin_lock(&session->s_cap_lock); @@ -5109,13 +5149,29 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, fail: ceph_msg_put(reply); up_read(&mdsc->snap_rwsem); + /* + * Restore prior session state so map-driven reconnect logic + * (check_new_map) can retry. Without this, a transient build + * failure strands the session in RECONNECTING indefinitely. + */ + session->s_state = old_state; mutex_unlock(&session->s_mutex); fail_nomsg: ceph_pagelist_release(recon_state.pagelist); fail_nopagelist: pr_err_client(cl, "error %d preparing reconnect for mds%d\n", err, mds); - return; + return err; + +fail_return: + /* + * Early-exit path for expected concurrent-teardown races + * (-ESTALE for closed/rejected sessions, -ENOENT for + * unregistered sessions). Skip the pr_err_client diagnostic + * since these are not genuine reconnect build failures. + */ + ceph_pagelist_release(recon_state.pagelist); + return err; } @@ -5196,9 +5252,15 @@ static void check_new_map(struct ceph_mds_client *mdsc, */ if (s->s_state == CEPH_MDS_SESSION_RESTARTING && newstate >= CEPH_MDS_STATE_RECONNECT) { + int rc; + mutex_unlock(&mdsc->mutex); clear_bit(i, targets); - send_mds_reconnect(mdsc, s); + rc = send_mds_reconnect(mdsc, s); + if (rc) + pr_warn_client(cl, + "mds%d reconnect failed: %d\n", + i, rc); mutex_lock(&mdsc->mutex); } @@ -5262,7 +5324,11 @@ static void check_new_map(struct ceph_mds_client *mdsc, } doutc(cl, "send reconnect to export target mds.%d\n", i); mutex_unlock(&mdsc->mutex); - send_mds_reconnect(mdsc, s); + err = send_mds_reconnect(mdsc, s); + if (err) + pr_warn_client(cl, + "mds%d export target reconnect failed: %d\n", + i, err); ceph_put_mds_session(s); mutex_lock(&mdsc->mutex); } @@ -6350,12 +6416,92 @@ static void mds_peer_reset(struct ceph_connection *con) { struct ceph_mds_session *s = con->private; struct ceph_mds_client *mdsc = s->s_mdsc; + int session_state; pr_warn_client(mdsc->fsc->client, "mds%d closed our session\n", s->s_mds); - if (READ_ONCE(mdsc->fsc->mount_state) != CEPH_MOUNT_FENCE_IO && - ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) >= CEPH_MDS_STATE_RECONNECT) - send_mds_reconnect(mdsc, s); + + if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_FENCE_IO || + ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) < CEPH_MDS_STATE_RECONNECT) + return; + + /* + * Only reconnect if MDS is in its RECONNECT phase. An MDS past + * RECONNECT (REJOIN, CLIENTREPLAY, ACTIVE) will reject reconnect + * attempts, so those states fall through to session teardown below. + */ + if (ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) == CEPH_MDS_STATE_RECONNECT) { + int rc = send_mds_reconnect(mdsc, s); + + if (rc) + pr_warn_client(mdsc->fsc->client, + "mds%d reconnect failed: %d\n", + s->s_mds, rc); + return; + } + + /* + * MDS is active (past RECONNECT). It will not accept a + * CLIENT_RECONNECT from us, so tear the session down locally + * and let new requests re-open a fresh session. + * + * Snapshot session state with READ_ONCE, then revalidate under + * mdsc->mutex before acting. The subsequent mdsc->mutex + * section rechecks s_state to catch concurrent transitions, so + * the lockless snapshot here is safe. s->s_mutex is taken + * separately for cleanup after unregistration, which avoids + * introducing a new s->s_mutex + mdsc->mutex nesting. + */ + session_state = READ_ONCE(s->s_state); + + switch (session_state) { + case CEPH_MDS_SESSION_RESTARTING: + case CEPH_MDS_SESSION_RECONNECTING: + case CEPH_MDS_SESSION_CLOSING: + case CEPH_MDS_SESSION_OPEN: + case CEPH_MDS_SESSION_HUNG: + case CEPH_MDS_SESSION_OPENING: + mutex_lock(&mdsc->mutex); + if (s->s_mds >= mdsc->max_sessions || + mdsc->sessions[s->s_mds] != s || + s->s_state != session_state) { + pr_info_client(mdsc->fsc->client, + "mds%d state changed to %s during peer reset\n", + s->s_mds, + ceph_session_state_name(s->s_state)); + mutex_unlock(&mdsc->mutex); + return; + } + + ceph_get_mds_session(s); + s->s_state = CEPH_MDS_SESSION_CLOSED; + __unregister_session(mdsc, s); + __wake_requests(mdsc, &s->s_waiting); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&s->s_mutex); + cleanup_session_requests(mdsc, s); + remove_session_caps(s); + mutex_unlock(&s->s_mutex); + + wake_up_all(&mdsc->session_close_wq); + + mutex_lock(&mdsc->mutex); + kick_requests(mdsc, s->s_mds); + mutex_unlock(&mdsc->mutex); + + ceph_put_mds_session(s); + break; + case CEPH_MDS_SESSION_CLOSED: + case CEPH_MDS_SESSION_REJECTED: + break; + default: + pr_warn_client(mdsc->fsc->client, + "mds%d peer reset in unexpected state %s\n", + s->s_mds, + ceph_session_state_name(session_state)); + break; + } } static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg) @@ -6367,6 +6513,8 @@ static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg) mutex_lock(&mdsc->mutex); if (__verify_registered_session(mdsc, s) < 0) { + doutc(cl, "dropping tid %llu from unregistered session %d\n", + le64_to_cpu(msg->hdr.tid), s->s_mds); mutex_unlock(&mdsc->mutex); goto out; } From ebbbab66bd74dbd213d51afc3b029dc8b109ee47 Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Thu, 7 May 2026 08:45:27 +0000 Subject: [PATCH 4416/5214] ceph: add diagnostic timeout loop to wait_caps_flush() Convert wait_caps_flush() from a silent indefinite wait into a diagnostic wait loop that periodically dumps pending cap flush state. The underlying wait semantics remain intact: callers still wait until the requested cap flushes complete. The difference is that long stalls now produce actionable diagnostics instead of looking like a silent hang. CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES limits the number of entries emitted per diagnostic dump, and CEPH_CAP_FLUSH_MAX_DUMP_ITERS limits the number of timed diagnostic dumps before the wait continues silently. When more entries exist than the per-dump limit, a truncation count is reported. When the dump iteration limit is reached, a final suppression message is emitted so the transition to silence is explicit. The diagnostic dump collects flush entry data under cap_dirty_lock into a bounded on-stack array, then prints after releasing the lock. This avoids holding the spinlock across printk calls. A null cf->ci on the global flush list indicates a bug since all cap_flush entries are initialized with a valid ci before being added. Signal this with WARN_ON_ONCE while still printing enough context for debugging. READ_ONCE is used for the i_last_cap_flush_ack field, which is read outside the inode lock domain. Flush tids are monotonically increasing and acks are processed in order under i_ceph_lock, so the latest ack tid is always the most recently written value. Add a ci pointer to struct ceph_cap_flush so that the diagnostic dump can identify which inode each pending flush belongs to. The new i_last_cap_flush_ack field tracks the latest acknowledged flush tid per inode for diagnostic correlation. This improves reset-drain observability and is also useful for existing sync and writeback troubleshooting paths. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 10 +++++ fs/ceph/inode.c | 1 + fs/ceph/mds_client.c | 101 +++++++++++++++++++++++++++++++++++++++++-- fs/ceph/mds_client.h | 3 ++ fs/ceph/super.h | 6 +++ 5 files changed, 117 insertions(+), 4 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index cb9e78b713d9..4b37d9ffdf7f 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1648,6 +1648,7 @@ static void __ceph_flush_snaps(struct ceph_inode_info *ci, spin_lock(&mdsc->cap_dirty_lock); capsnap->cap_flush.tid = ++mdsc->last_cap_flush_tid; + capsnap->cap_flush.ci = ci; list_add_tail(&capsnap->cap_flush.g_list, &mdsc->cap_flush_list); if (oldest_flush_tid == 0) @@ -1846,6 +1847,7 @@ struct ceph_cap_flush *ceph_alloc_cap_flush(void) return NULL; cf->is_capsnap = false; + cf->ci = NULL; return cf; } @@ -1931,6 +1933,7 @@ static u64 __mark_caps_flushing(struct inode *inode, doutc(cl, "%p %llx.%llx now !dirty\n", inode, ceph_vinop(inode)); swap(cf, ci->i_prealloc_cap_flush); + cf->ci = ci; cf->caps = flushing; cf->wake = wake; @@ -3826,6 +3829,13 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, bool wake_ci = false; bool wake_mdsc = false; + /* + * Flush tids are monotonically increasing and acks arrive in + * order under i_ceph_lock, so this is always the latest tid. + * Diagnostic readers use READ_ONCE() without holding the lock. + */ + WRITE_ONCE(ci->i_last_cap_flush_ack, flush_tid); + list_for_each_entry_safe(cf, tmp_cf, &ci->i_cap_flush_list, i_list) { /* Is this the one that was flushed? */ if (cf->tid == flush_tid) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 4871d7ab2730..61d7c0b8161f 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -671,6 +671,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&ci->i_cap_snaps); ci->i_head_snapc = NULL; ci->i_snap_caps = 0; + ci->i_last_cap_flush_ack = 0; ci->i_last_rd = ci->i_last_wr = jiffies - 3600 * HZ; for (i = 0; i < CEPH_FILE_MODE_BITS; i++) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 249419c17d3c..eddd3ccf5bba 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2330,19 +2330,112 @@ static int check_caps_flush(struct ceph_mds_client *mdsc, } /* - * flush all dirty inode data to disk. + * Snapshot of a single cap_flush entry for diagnostic dump. + * Collected under cap_dirty_lock, printed after releasing it. + */ +struct flush_dump_entry { + u64 ino; /* inode number */ + u64 snap; /* snap id */ + int caps; /* dirty cap bits */ + u64 tid; /* flush transaction id */ + u64 last_ack; /* most recent ack tid for this inode */ + bool wake; /* whether completion was requested */ + bool is_capsnap; /* true if this is a cap snap flush */ + bool ci_null; /* true if cf->ci was unexpectedly NULL */ +}; + +/* + * Dump pending cap flushes for diagnostic purposes. * - * returns true if we've flushed through want_flush_tid + * cf->ci is safe to dereference here: cap_flush entries hold a + * reference on the inode (via the cap), and entries are removed from + * cap_flush_list under cap_dirty_lock before the cap (and thus the + * inode reference) is released. Holding cap_dirty_lock therefore + * guarantees the inode remains valid for the lifetime of the scan. + */ + +static void dump_cap_flushes(struct ceph_mds_client *mdsc, u64 want_tid) +{ + struct ceph_client *cl = mdsc->fsc->client; + struct flush_dump_entry entries[CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES]; + struct ceph_cap_flush *cf; + int n = 0, remaining = 0; + int i; + + spin_lock(&mdsc->cap_dirty_lock); + list_for_each_entry(cf, &mdsc->cap_flush_list, g_list) { + if (cf->tid > want_tid) + break; + if (n < CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES) { + struct flush_dump_entry *e = &entries[n++]; + + e->ci_null = WARN_ON_ONCE(!cf->ci); + if (!e->ci_null) { + e->ino = ceph_ino(&cf->ci->netfs.inode); + e->snap = ceph_snap(&cf->ci->netfs.inode); + e->last_ack = READ_ONCE(cf->ci->i_last_cap_flush_ack); + } + e->caps = cf->caps; + e->tid = cf->tid; + e->wake = cf->wake; + e->is_capsnap = cf->is_capsnap; + } else { + remaining++; + } + } + spin_unlock(&mdsc->cap_dirty_lock); + + pr_info_client(cl, "still waiting for cap flushes through %llu:\n", + want_tid); + for (i = 0; i < n; i++) { + struct flush_dump_entry *e = &entries[i]; + + if (e->ci_null) + pr_info_client(cl, + " (null ci) %s tid=%llu wake=%d%s\n", + ceph_cap_string(e->caps), e->tid, + e->wake, + e->is_capsnap ? " is_capsnap" : ""); + else + pr_info_client(cl, + " %llx.%llx %s tid=%llu last_ack=%llu wake=%d%s\n", + e->ino, e->snap, + ceph_cap_string(e->caps), e->tid, + e->last_ack, e->wake, + e->is_capsnap ? " is_capsnap" : ""); + } + if (remaining) + pr_info_client(cl, " ... and %d more pending flushes\n", + remaining); +} + +/* + * Wait for all cap flushes through @want_flush_tid to complete. + * Periodically dumps pending cap flush state for diagnostics. */ static void wait_caps_flush(struct ceph_mds_client *mdsc, u64 want_flush_tid) { struct ceph_client *cl = mdsc->fsc->client; + int i = 0; + long ret; doutc(cl, "want %llu\n", want_flush_tid); - wait_event(mdsc->cap_flushing_wq, - check_caps_flush(mdsc, want_flush_tid)); + do { + /* 60 * HZ fits in a long on all supported architectures. */ + ret = wait_event_timeout(mdsc->cap_flushing_wq, + check_caps_flush(mdsc, want_flush_tid), + CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC * HZ); + if (ret == 0) { + if (i < CEPH_CAP_FLUSH_MAX_DUMP_ITERS) + dump_cap_flushes(mdsc, want_flush_tid); + else if (i == CEPH_CAP_FLUSH_MAX_DUMP_ITERS) + pr_info_client(cl, + "still waiting for cap flushes; suppressing further dumps\n"); + i++; + } + } while (ret == 0); doutc(cl, "ok, flushed thru %llu\n", want_flush_tid); } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index d873e784b025..8208fdf02efe 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -77,6 +77,9 @@ struct ceph_fs_client; struct ceph_cap; #define MDS_AUTH_UID_ANY -1 +#define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60 +#define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5 +#define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5 struct ceph_mds_cap_match { s64 uid; /* default to MDS_AUTH_UID_ANY */ diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 8afc6f3a10da..a4993644d543 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -239,6 +239,7 @@ struct ceph_cap_flush { bool is_capsnap; /* true means capsnap */ struct list_head g_list; // global struct list_head i_list; // per inode + struct ceph_inode_info *ci; }; /* @@ -453,6 +454,11 @@ struct ceph_inode_info { struct ceph_snap_context *i_head_snapc; /* set if wr_buffer_head > 0 or dirty|flushing caps */ unsigned i_snap_caps; /* cap bits for snapped files */ + /* + * Written under i_ceph_lock, read via READ_ONCE() + * from diagnostic paths. + */ + u64 i_last_cap_flush_ack; unsigned long i_last_rd; unsigned long i_last_wr; From b52861897be8a7ba563077ae62cd48158d16a5d9 Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Thu, 7 May 2026 09:11:59 +0000 Subject: [PATCH 4417/5214] ceph: add client reset state machine and session teardown Add the client-side reset state machine, request gating, and manual session teardown implementation. Manual reset is an operator-triggered escape hatch for client/MDS stalemates in which caps, locks, or unsafe metadata state stop making forward progress. The reset blocks new metadata work, attempts a bounded best-effort drain of dirty client state while sessions are still alive, and finally asks the MDS to close sessions before tearing local session state down directly. The reset state machine tracks four phases: IDLE -> QUIESCING -> DRAINING -> TEARDOWN -> IDLE. QUIESCING is set synchronously by schedule_reset() before the workqueue item is dispatched, so that new metadata requests and file-lock acquisitions are gated immediately -- even before the work function begins running. All non-IDLE phases block callers on blocked_wq, preventing races with session teardown. The drain phase flushes mdlog state, dirty caps, and pending cap releases for a bounded interval. State that still cannot make progress within that interval is discarded during teardown, which is the point of the reset: break the stalemate and allow fresh sessions to rebuild clean state. The session teardown follows the established check_new_map() forced-close pattern: unregister sessions under mdsc->mutex, then clean up caps and requests under s->s_mutex. Reconnect is not attempted because the MDS only accepts reconnects during its own RECONNECT phase after restart, not from an active client. Blocked callers are released when reset completes and observe the final result via -EAGAIN (reset failed) or 0 (success). Internal work-function errors such as -ENOMEM are not propagated to unrelated callers like open() or flock(); the detailed error remains in debugfs and tracepoints. The work function checks st->shutdown before each phase transition (DRAINING, TEARDOWN) so that a concurrent ceph_mdsc_destroy() is not overwritten. If destroy already took ownership, the work function releases session references and returns without touching the state. The timeout calculation for blocked-request waiters uses max_t() to prevent jiffies underflow when the deadline has already passed. The close-grace sleep before teardown is a best-effort nudge to let queued REQUEST_CLOSE messages egress; it is not a correctness requirement since the MDS still has session_autoclose as a fallback. The destroy path marks reset as failed and wakes blocked waiters before cancel_work_sync() so unmount does not stall. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/locks.c | 16 ++ fs/ceph/mds_client.c | 508 +++++++++++++++++++++++++++++++++++++++++++ fs/ceph/mds_client.h | 47 ++++ 3 files changed, 571 insertions(+) diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index c4ff2266bb94..677221bd64e0 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -249,6 +249,7 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl) { struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); struct ceph_client *cl = ceph_inode_to_client(inode); int err = 0; u16 op = CEPH_MDS_OP_SETFILELOCK; @@ -275,6 +276,13 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl) return -EIO; } + /* Wait for reset to complete before acquiring new locks */ + if (op == CEPH_MDS_OP_SETFILELOCK && !lock_is_unlock(fl)) { + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) + return err; + } + if (lock_is_read(fl)) lock_cmd = CEPH_LOCK_SHARED; else if (lock_is_write(fl)) @@ -311,6 +319,7 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) { struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); struct ceph_client *cl = ceph_inode_to_client(inode); int err = 0; u8 wait = 0; @@ -330,6 +339,13 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) return -EIO; } + /* Wait for reset to complete before acquiring new locks */ + if (!lock_is_unlock(fl)) { + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) + return err; + } + if (IS_SETLKW(cmd)) wait = 1; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index eddd3ccf5bba..ddafbd6917b0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ static void __wake_requests(struct ceph_mds_client *mdsc, struct list_head *head); static void ceph_cap_release_work(struct work_struct *work); static void ceph_cap_reclaim_work(struct work_struct *work); +static void ceph_mdsc_reset_workfn(struct work_struct *work); static const struct ceph_connection_operations mds_con_ops; @@ -3845,6 +3847,22 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir, struct ceph_client *cl = mdsc->fsc->client; int err = 0; + /* + * If a reset is in progress, wait for it to complete. + * + * This is best-effort: a request can pass this check just + * before the phase leaves IDLE and proceed concurrently with + * reset. That is acceptable because (a) such requests will + * either complete normally or fail and be retried by the + * caller, and (b) adding lock serialization here would + * penalize every request for a rare manual operation. + */ + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) { + doutc(cl, "wait_for_reset failed: %d\n", err); + return err; + } + /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */ if (req->r_inode) ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN); @@ -5267,6 +5285,474 @@ static int send_mds_reconnect(struct ceph_mds_client *mdsc, return err; } +const char *ceph_reset_phase_name(enum ceph_client_reset_phase phase) +{ + switch (phase) { + case CEPH_CLIENT_RESET_IDLE: return "idle"; + case CEPH_CLIENT_RESET_QUIESCING: return "quiescing"; + case CEPH_CLIENT_RESET_DRAINING: return "draining"; + case CEPH_CLIENT_RESET_TEARDOWN: return "teardown"; + default: return "unknown"; + } +} + +/** + * ceph_mdsc_wait_for_reset - wait for an active reset to complete + * @mdsc: MDS client + * + * Returns 0 if reset completed successfully or no reset was active. + * Returns -EAGAIN if reset completed with an error, signalling the + * caller to retry. The internal error (e.g. -ENOMEM) is not propagated + * because callers like open() or flock() have no way to act on + * work-function internals. The detailed error is available via debugfs + * reset/status and tracepoints. + * Returns -ETIMEDOUT if we timed out waiting. + * Returns -ERESTARTSYS if interrupted by signal. + */ +int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_client *cl = mdsc->fsc->client; + unsigned long deadline = jiffies + CEPH_CLIENT_RESET_WAIT_TIMEOUT_SEC * HZ; + int blocked_count; + long remaining; + long wait_ret; + int ret; + + if (ceph_reset_is_idle(st)) + return 0; + + blocked_count = atomic_inc_return(&st->blocked_requests); + doutc(cl, "request blocked during reset, %d total blocked\n", + blocked_count); + +retry: + remaining = max_t(long, deadline - jiffies, 1); + wait_ret = wait_event_interruptible_timeout(st->blocked_wq, + ceph_reset_is_idle(st), + remaining); + + if (wait_ret == 0) { + atomic_dec(&st->blocked_requests); + pr_warn_client(cl, "timed out waiting for reset to complete\n"); + return -ETIMEDOUT; + } + if (wait_ret < 0) { + atomic_dec(&st->blocked_requests); + return (int)wait_ret; /* -ERESTARTSYS */ + } + + /* + * Verify phase is still IDLE under the lock. If another reset + * was scheduled between the wake-up and this check, loop back + * and wait for it to finish rather than returning a stale result. + */ + spin_lock(&st->lock); + if (st->phase != CEPH_CLIENT_RESET_IDLE) { + spin_unlock(&st->lock); + if (time_before(jiffies, deadline)) + goto retry; + atomic_dec(&st->blocked_requests); + return -ETIMEDOUT; + } + ret = st->last_errno; + spin_unlock(&st->lock); + + atomic_dec(&st->blocked_requests); + return ret ? -EAGAIN : 0; +} + +static void ceph_mdsc_reset_complete(struct ceph_mds_client *mdsc, int ret) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + + spin_lock(&st->lock); + /* + * If destroy already marked us as shut down, it owns the + * final bookkeeping and waiter wakeup. Just bail so we + * don't overwrite its state. + */ + if (st->shutdown) { + spin_unlock(&st->lock); + return; + } + st->last_finish = jiffies; + st->last_errno = ret; + st->phase = CEPH_CLIENT_RESET_IDLE; + if (ret) + st->failure_count++; + else + st->success_count++; + spin_unlock(&st->lock); + + /* Wake up all requests that were blocked waiting for reset */ + wake_up_all(&st->blocked_wq); + +} + +static void ceph_mdsc_reset_workfn(struct work_struct *work) +{ + struct ceph_mds_client *mdsc = + container_of(work, struct ceph_mds_client, reset_work); + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_client *cl = mdsc->fsc->client; + struct ceph_mds_session **sessions = NULL; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + unsigned long drain_deadline; + int max_sessions, i, n = 0, torn_down = 0; + int ret = 0; + + spin_lock(&st->lock); + strscpy(reason, st->last_reason, sizeof(reason)); + spin_unlock(&st->lock); + + mutex_lock(&mdsc->mutex); + max_sessions = mdsc->max_sessions; + if (max_sessions <= 0) { + mutex_unlock(&mdsc->mutex); + goto out_complete; + } + + sessions = kcalloc(max_sessions, sizeof(*sessions), GFP_KERNEL); + if (!sessions) { + mutex_unlock(&mdsc->mutex); + ret = -ENOMEM; + pr_err_client(cl, + "manual session reset failed to allocate session array\n"); + ceph_mdsc_reset_complete(mdsc, ret); + return; + } + + for (i = 0; i < max_sessions; i++) { + struct ceph_mds_session *session = mdsc->sessions[i]; + + if (!session) + continue; + + /* + * Read session state without s_mutex to avoid nesting + * mdsc->mutex -> s_mutex, which would invert the + * s_mutex -> mdsc->mutex order used by + * cleanup_session_requests(). s_state is an int + * so loads are atomic; the teardown loop below + * handles races with concurrent state transitions. + */ + switch (READ_ONCE(session->s_state)) { + case CEPH_MDS_SESSION_OPEN: + case CEPH_MDS_SESSION_HUNG: + case CEPH_MDS_SESSION_OPENING: + case CEPH_MDS_SESSION_RESTARTING: + case CEPH_MDS_SESSION_RECONNECTING: + case CEPH_MDS_SESSION_CLOSING: + sessions[n++] = ceph_get_mds_session(session); + break; + default: + pr_info_client(cl, + "mds%d in state %s, skipping reset\n", + session->s_mds, + ceph_session_state_name(session->s_state)); + break; + } + } + mutex_unlock(&mdsc->mutex); + + pr_info_client(cl, + "manual session reset executing (sessions=%d, reason=\"%s\")\n", + n, reason); + + if (n == 0) { + kfree(sessions); + goto out_complete; + } + + spin_lock(&st->lock); + if (st->shutdown) { + spin_unlock(&st->lock); + goto out_sessions; + } + st->phase = CEPH_CLIENT_RESET_DRAINING; + spin_unlock(&st->lock); + + /* + * Best-effort drain: flush dirty state while sessions are still + * alive. New requests are blocked while phase != IDLE. + * The sessions are functional, so non-stuck state drains normally. + * Stuck state (the cause of the stalemate the operator is trying + * to break) will not drain -- that is expected, and we proceed to + * forced teardown after the timeout. + * + * Four things are drained: + * 1. MDS journal -- send_flush_mdlog asks each MDS to journal + * pending unsafe operations (creates, renames, setattrs). + * 2. Unsafe requests -- bounded wait for each unsafe write + * request to reach safe status via r_safe_completion. + * 3. Dirty caps -- ceph_flush_dirty_caps triggers cap flush on + * all sessions. Non-stuck caps flush in milliseconds. + * 4. Cap releases -- push pending cap release messages. + * + * The unsafe-request wait and cap-flush wait below provide + * the bounded drain window during which all categories can + * make progress. + */ + for (i = 0; i < n; i++) + send_flush_mdlog(sessions[i]); + + /* + * Both drain legs (unsafe requests and cap flushes) share a + * single deadline so the total drain time is bounded at + * CEPH_CLIENT_RESET_DRAIN_SEC. + */ + drain_deadline = jiffies + CEPH_CLIENT_RESET_DRAIN_SEC * HZ; + + /* + * Wait for unsafe write requests (creates, renames, setattrs) + * to reach safe status. Uses the same pattern as + * flush_mdlog_and_wait_mdsc_unsafe_requests() but bounded by + * the shared drain deadline. Requests that do not complete within + * the window are force-dropped during teardown. + */ + { + struct ceph_mds_request *req; + struct rb_node *rn; + u64 last_tid; + + mutex_lock(&mdsc->mutex); + last_tid = mdsc->last_tid; + mutex_unlock(&mdsc->mutex); + + mutex_lock(&mdsc->mutex); + rn = rb_first(&mdsc->request_tree); + while (rn) { + req = rb_entry(rn, struct ceph_mds_request, r_node); + if (req->r_tid > last_tid) + break; + if (req->r_op == CEPH_MDS_OP_SETFILELOCK || + !(req->r_op & CEPH_MDS_OP_WRITE)) { + rn = rb_next(rn); + continue; + } + ceph_mdsc_get_request(req); + mutex_unlock(&mdsc->mutex); + + wait_for_completion_timeout(&req->r_safe_completion, + max_t(long, drain_deadline - jiffies, 1)); + + mutex_lock(&mdsc->mutex); + ceph_mdsc_put_request(req); + if (time_after(jiffies, drain_deadline)) + break; + rn = rb_first(&mdsc->request_tree); + } + mutex_unlock(&mdsc->mutex); + + if (time_after_eq(jiffies, drain_deadline)) + WRITE_ONCE(st->drain_timed_out, true); + } + + ceph_flush_dirty_caps(mdsc); + ceph_flush_cap_releases(mdsc); + + spin_lock(&mdsc->cap_dirty_lock); + if (!list_empty(&mdsc->cap_flush_list)) { + struct ceph_cap_flush *cf = + list_last_entry(&mdsc->cap_flush_list, + struct ceph_cap_flush, g_list); + u64 want_flush = mdsc->last_cap_flush_tid; + long drain_ret; + + /* + * Setting wake on the last entry is sufficient: flush + * entries complete in order, so when this entry finishes + * all earlier ones are already done. + */ + cf->wake = true; + spin_unlock(&mdsc->cap_dirty_lock); + pr_info_client(cl, + "draining (want_flush=%llu, %d sessions)\n", + want_flush, n); + drain_ret = wait_event_timeout(mdsc->cap_flushing_wq, + check_caps_flush(mdsc, + want_flush), + max_t(long, + drain_deadline - jiffies, + 1)); + if (drain_ret == 0) { + pr_info_client(cl, + "drain timed out, proceeding with forced teardown\n"); + WRITE_ONCE(st->drain_timed_out, true); + } else { + pr_info_client(cl, "drain completed successfully\n"); + } + } else { + spin_unlock(&mdsc->cap_dirty_lock); + } + + spin_lock(&st->lock); + if (st->shutdown) { + spin_unlock(&st->lock); + goto out_sessions; + } + st->phase = CEPH_CLIENT_RESET_TEARDOWN; + spin_unlock(&st->lock); + + /* + * Ask each MDS to close the session before we tear it down + * locally. Without this the MDS sees only a connection drop and + * waits for the client to reconnect (up to session_autoclose + * seconds) before evicting the session and releasing locks. + * + * Reuse the normal close machinery so the session state/sequence + * snapshot is serialized under s_mutex and a racing s_seq bump + * retransmits REQUEST_CLOSE while the session remains CLOSING. + * We send all close requests first, then yield briefly to let the + * network stack transmit them before __unregister_session() + * closes the connections. + */ + for (i = 0; i < n; i++) { + int err; + + mutex_lock(&sessions[i]->s_mutex); + err = __close_session(mdsc, sessions[i]); + mutex_unlock(&sessions[i]->s_mutex); + if (err < 0) + pr_warn_client(cl, + "mds%d failed to queue close request before reset: %d\n", + sessions[i]->s_mds, err); + } + /* + * Best-effort grace period: yield briefly so the network stack + * can transmit the queued REQUEST_CLOSE messages before we tear + * down connections. Not a correctness requirement -- the MDS + * will still evict via session_autoclose if it never receives + * the close request. + * + * Event-based waiting is not viable here: there is no completion + * event for "message left the NIC," and waiting for the MDS + * SESSION_CLOSE response would re-create the stalemate that the + * reset is meant to break. + */ + if (n > 0) + msleep(CEPH_CLIENT_RESET_CLOSE_GRACE_MS); + + /* + * Tear down each session: close the connection, remove all + * caps, clean up requests, then kick pending requests so they + * re-open a fresh session on the next attempt. + * + * This is modeled on the check_new_map() forced-close path + * for stopped MDS ranks - a proven pattern for hard session + * teardown. We do NOT attempt send_mds_reconnect() because + * the MDS only accepts reconnects during its own RECONNECT + * phase (after MDS restart), not from an active client. + * + * Any state that did not drain (caps that didn't flush, unsafe + * requests that the MDS didn't journal) is force-dropped here. + * This is intentional: that state is stuck and is the reason + * the operator triggered the reset. + */ + for (i = 0; i < n; i++) { + int mds = sessions[i]->s_mds; + + pr_info_client(cl, "mds%d resetting session\n", mds); + + mutex_lock(&mdsc->mutex); + if (mds >= mdsc->max_sessions || + mdsc->sessions[mds] != sessions[i]) { + pr_info_client(cl, + "mds%d session already torn down, skipping\n", + mds); + mutex_unlock(&mdsc->mutex); + ceph_put_mds_session(sessions[i]); + sessions[i] = NULL; + continue; + } + sessions[i]->s_state = CEPH_MDS_SESSION_CLOSED; + __unregister_session(mdsc, sessions[i]); + __wake_requests(mdsc, &sessions[i]->s_waiting); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&sessions[i]->s_mutex); + cleanup_session_requests(mdsc, sessions[i]); + remove_session_caps(sessions[i]); + mutex_unlock(&sessions[i]->s_mutex); + + wake_up_all(&mdsc->session_close_wq); + + ceph_put_mds_session(sessions[i]); + + mutex_lock(&mdsc->mutex); + kick_requests(mdsc, mds); + mutex_unlock(&mdsc->mutex); + + torn_down++; + pr_info_client(cl, "mds%d session reset complete\n", mds); + } + + kfree(sessions); + + spin_lock(&st->lock); + st->sessions_reset = torn_down; + spin_unlock(&st->lock); + +out_complete: + ceph_mdsc_reset_complete(mdsc, ret); + return; + +out_sessions: + /* shutdown == true: ceph_mdsc_destroy() owns the final transition. */ + for (i = 0; i < n; i++) + ceph_put_mds_session(sessions[i]); + kfree(sessions); +} + +int ceph_mdsc_schedule_reset(struct ceph_mds_client *mdsc, + const char *reason) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_fs_client *fsc = mdsc->fsc; + const char *msg = (reason && reason[0]) ? reason : "manual"; + int mount_state; + + mount_state = READ_ONCE(fsc->mount_state); + if (mount_state != CEPH_MOUNT_MOUNTED) { + pr_warn_client(fsc->client, + "reset rejected: mount_state=%d (not mounted)\n", + mount_state); + return -EINVAL; + } + + spin_lock(&st->lock); + if (st->phase != CEPH_CLIENT_RESET_IDLE) { + spin_unlock(&st->lock); + return -EBUSY; + } + + st->phase = CEPH_CLIENT_RESET_QUIESCING; + st->last_start = jiffies; + st->last_errno = 0; + st->drain_timed_out = false; + st->sessions_reset = 0; + st->trigger_count++; + strscpy(st->last_reason, msg, sizeof(st->last_reason)); + spin_unlock(&st->lock); + + if (WARN_ON_ONCE(!queue_work(system_unbound_wq, &mdsc->reset_work))) { + spin_lock(&st->lock); + st->phase = CEPH_CLIENT_RESET_IDLE; + st->last_errno = -EALREADY; + st->last_finish = jiffies; + st->failure_count++; + spin_unlock(&st->lock); + wake_up_all(&st->blocked_wq); + return -EALREADY; + } + + pr_info_client(mdsc->fsc->client, + "manual session reset scheduled (reason=\"%s\")\n", + msg); + return 0; +} + /* * compare old and new mdsmaps, kicking requests @@ -5812,6 +6298,11 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) INIT_LIST_HEAD(&mdsc->dentry_leases); INIT_LIST_HEAD(&mdsc->dentry_dir_leases); + spin_lock_init(&mdsc->reset_state.lock); + init_waitqueue_head(&mdsc->reset_state.blocked_wq); + atomic_set(&mdsc->reset_state.blocked_requests, 0); + INIT_WORK(&mdsc->reset_work, ceph_mdsc_reset_workfn); + ceph_caps_init(mdsc); ceph_adjust_caps_max_min(mdsc, fsc->mount_options); @@ -6337,6 +6828,23 @@ void ceph_mdsc_destroy(struct ceph_fs_client *fsc) /* flush out any connection work with references to us */ ceph_msgr_flush(); + /* + * Mark reset as failed and wake any blocked waiters before + * cancelling, so unmount doesn't stall on blocked_wq timeout + * if cancel_work_sync() prevents the work from running. + */ + spin_lock(&mdsc->reset_state.lock); + mdsc->reset_state.shutdown = true; + if (mdsc->reset_state.phase != CEPH_CLIENT_RESET_IDLE) { + mdsc->reset_state.phase = CEPH_CLIENT_RESET_IDLE; + mdsc->reset_state.last_errno = -ESHUTDOWN; + mdsc->reset_state.last_finish = jiffies; + mdsc->reset_state.failure_count++; + } + spin_unlock(&mdsc->reset_state.lock); + wake_up_all(&mdsc->reset_state.blocked_wq); + + cancel_work_sync(&mdsc->reset_work); ceph_mdsc_stop(mdsc); ceph_metric_destroy(&mdsc->metric); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 8208fdf02efe..731d6ad04956 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -80,6 +80,47 @@ struct ceph_cap; #define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60 #define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5 #define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5 +#define CEPH_CLIENT_RESET_REASON_LEN 64 +#define CEPH_CLIENT_RESET_DRAIN_SEC 30 +#define CEPH_CLIENT_RESET_CLOSE_GRACE_MS 100 +#define CEPH_CLIENT_RESET_WAIT_TIMEOUT_SEC 120 + +enum ceph_client_reset_phase { + CEPH_CLIENT_RESET_IDLE = 0, + /* + * QUIESCING is set synchronously by schedule_reset() before the + * workqueue item is dispatched. It gates new requests (any + * phase != IDLE blocks callers) during the window between + * scheduling and the work function's transition to DRAINING. + */ + CEPH_CLIENT_RESET_QUIESCING, + CEPH_CLIENT_RESET_DRAINING, + CEPH_CLIENT_RESET_TEARDOWN, +}; + +struct ceph_client_reset_state { + spinlock_t lock; /* protects all fields below */ + u64 trigger_count; /* number of resets triggered */ + u64 success_count; /* number of successful resets */ + u64 failure_count; /* number of failed resets */ + unsigned long last_start; /* jiffies when last reset started */ + unsigned long last_finish; /* jiffies when last reset finished */ + int last_errno; /* result of most recent reset */ + enum ceph_client_reset_phase phase; /* current reset phase */ + bool drain_timed_out; /* drain exceeded timeout */ + bool shutdown; /* destroy in progress */ + int sessions_reset; /* sessions torn down in last reset */ + char last_reason[CEPH_CLIENT_RESET_REASON_LEN]; /* operator-supplied reason */ + + /* Request blocking during reset */ + wait_queue_head_t blocked_wq; /* waitqueue for blocked callers */ + atomic_t blocked_requests; /* count of blocked callers */ +}; + +static inline bool ceph_reset_is_idle(struct ceph_client_reset_state *st) +{ + return READ_ONCE(st->phase) == CEPH_CLIENT_RESET_IDLE; +} struct ceph_mds_cap_match { s64 uid; /* default to MDS_AUTH_UID_ANY */ @@ -543,6 +584,8 @@ struct ceph_mds_client { struct list_head dentry_dir_leases; /* lru list */ struct ceph_client_metric metric; + struct work_struct reset_work; + struct ceph_client_reset_state reset_state; struct ceph_subvolume_metrics_tracker subvol_metrics; /* Subvolume metrics send tracking */ @@ -574,10 +617,14 @@ extern struct ceph_mds_session * __ceph_lookup_mds_session(struct ceph_mds_client *, int mds); extern const char *ceph_session_state_name(int s); +extern const char *ceph_reset_phase_name(enum ceph_client_reset_phase phase); extern struct ceph_mds_session * ceph_get_mds_session(struct ceph_mds_session *s); extern void ceph_put_mds_session(struct ceph_mds_session *s); +int ceph_mdsc_schedule_reset(struct ceph_mds_client *mdsc, + const char *reason); +int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc); extern int ceph_mdsc_init(struct ceph_fs_client *fsc); extern void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc); From 7e1f9e2cd2d0e780c394a4402c40e125109fec72 Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Thu, 7 May 2026 08:54:07 +0000 Subject: [PATCH 4418/5214] ceph: add manual reset debugfs control and tracepoints Add the debugfs and trace plumbing used to trigger and observe manual client reset. The reset interface exposes a trigger file for operator-initiated reset and a status file for tracking the most recent run. The tracepoints record scheduling, completion, and blocked caller behavior so reset progress can be diagnosed from the client side. debugfs layout under /sys/kernel/debug/ceph//reset/: trigger - write to initiate a manual reset status - read to see the most recent reset result The reset directory is cleaned up via debugfs_remove_recursive() on the parent, so individual file dentries are not stored. Tracepoints: ceph_client_reset_schedule - reset queued ceph_client_reset_complete - reset finished (success or failure) ceph_client_reset_blocked - caller blocked waiting for reset ceph_client_reset_unblocked - caller unblocked after reset All tracepoints use a null-safe access for monc.auth->global_id to guard against early-init or late-teardown edge cases. Signed-off-by: Alex Markuze Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/debugfs.c | 103 ++++++++++++++++++++++++++++++++++++ fs/ceph/mds_client.c | 7 +++ fs/ceph/super.h | 1 + include/trace/events/ceph.h | 67 +++++++++++++++++++++++ 4 files changed, 178 insertions(+) diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index e2463f93cf6b..18eb5da03411 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -392,6 +393,90 @@ static int status_show(struct seq_file *s, void *p) return 0; } +static int reset_status_show(struct seq_file *s, void *p) +{ + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_client_reset_state *st; + u64 trigger = 0, success = 0, failure = 0; + unsigned long last_start = 0, last_finish = 0; + int last_errno = 0; + enum ceph_client_reset_phase phase = CEPH_CLIENT_RESET_IDLE; + bool drain_timed_out = false; + int sessions_reset = 0; + int blocked_requests = 0; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + + if (!mdsc) + return 0; + + st = &mdsc->reset_state; + + spin_lock(&st->lock); + trigger = st->trigger_count; + success = st->success_count; + failure = st->failure_count; + last_start = st->last_start; + last_finish = st->last_finish; + last_errno = st->last_errno; + phase = st->phase; + drain_timed_out = st->drain_timed_out; + sessions_reset = st->sessions_reset; + strscpy(reason, st->last_reason, sizeof(reason)); + spin_unlock(&st->lock); + + blocked_requests = atomic_read(&st->blocked_requests); + + seq_printf(s, "phase: %s\n", ceph_reset_phase_name(phase)); + seq_printf(s, "trigger_count: %llu\n", trigger); + seq_printf(s, "success_count: %llu\n", success); + seq_printf(s, "failure_count: %llu\n", failure); + if (last_start) + seq_printf(s, "last_start_ms_ago: %u\n", + jiffies_to_msecs(jiffies - last_start)); + else + seq_puts(s, "last_start_ms_ago: (never)\n"); + if (last_finish) + seq_printf(s, "last_finish_ms_ago: %u\n", + jiffies_to_msecs(jiffies - last_finish)); + else + seq_puts(s, "last_finish_ms_ago: (never)\n"); + seq_printf(s, "last_errno: %d\n", last_errno); + seq_printf(s, "last_reason: %s\n", + reason[0] ? reason : "(none)"); + seq_printf(s, "drain_timed_out: %s\n", + drain_timed_out ? "yes" : "no"); + seq_printf(s, "sessions_reset: %d\n", sessions_reset); + seq_printf(s, "blocked_requests: %d\n", blocked_requests); + + return 0; +} + +static ssize_t reset_trigger_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct ceph_fs_client *fsc = file->private_data; + struct ceph_mds_client *mdsc = fsc->mdsc; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + size_t copy; + int ret; + + if (!mdsc) + return -ENODEV; + + copy = min_t(size_t, len, sizeof(reason) - 1); + if (copy && copy_from_user(reason, buf, copy)) + return -EFAULT; + reason[copy] = '\0'; + strim(reason); + + ret = ceph_mdsc_schedule_reset(mdsc, reason); + if (ret) + return ret; + + return len; +} + static int subvolume_metrics_show(struct seq_file *s, void *p) { struct ceph_fs_client *fsc = s->private; @@ -450,6 +535,7 @@ DEFINE_SHOW_ATTRIBUTE(mdsc); DEFINE_SHOW_ATTRIBUTE(caps); DEFINE_SHOW_ATTRIBUTE(mds_sessions); DEFINE_SHOW_ATTRIBUTE(status); +DEFINE_SHOW_ATTRIBUTE(reset_status); DEFINE_SHOW_ATTRIBUTE(metrics_file); DEFINE_SHOW_ATTRIBUTE(metrics_latency); DEFINE_SHOW_ATTRIBUTE(metrics_size); @@ -521,6 +607,13 @@ static int metric_features_show(struct seq_file *s, void *p) DEFINE_SHOW_ATTRIBUTE(metric_features); +static const struct file_operations ceph_reset_trigger_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = reset_trigger_write, + .llseek = noop_llseek, +}; + /* * debugfs */ @@ -554,6 +647,7 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) debugfs_remove(fsc->debugfs_caps); debugfs_remove(fsc->debugfs_status); debugfs_remove(fsc->debugfs_mdsc); + debugfs_remove_recursive(fsc->debugfs_reset_dir); debugfs_remove(fsc->debugfs_subvolume_metrics); debugfs_remove_recursive(fsc->debugfs_metrics_dir); doutc(fsc->client, "done\n"); @@ -602,6 +696,15 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc) fsc, &caps_fops); + fsc->debugfs_reset_dir = debugfs_create_dir("reset", + fsc->client->debugfs_dir); + debugfs_create_file("trigger", 0200, + fsc->debugfs_reset_dir, fsc, + &ceph_reset_trigger_fops); + debugfs_create_file("status", 0400, + fsc->debugfs_reset_dir, fsc, + &reset_status_fops); + fsc->debugfs_status = debugfs_create_file("status", 0400, fsc->client->debugfs_dir, diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ddafbd6917b0..9f84ef2ac6e4 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -5325,6 +5325,7 @@ int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc) blocked_count = atomic_inc_return(&st->blocked_requests); doutc(cl, "request blocked during reset, %d total blocked\n", blocked_count); + trace_ceph_client_reset_blocked(mdsc, blocked_count); retry: remaining = max_t(long, deadline - jiffies, 1); @@ -5335,10 +5336,12 @@ int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc) if (wait_ret == 0) { atomic_dec(&st->blocked_requests); pr_warn_client(cl, "timed out waiting for reset to complete\n"); + trace_ceph_client_reset_unblocked(mdsc, -ETIMEDOUT); return -ETIMEDOUT; } if (wait_ret < 0) { atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, (int)wait_ret); return (int)wait_ret; /* -ERESTARTSYS */ } @@ -5353,12 +5356,14 @@ int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc) if (time_before(jiffies, deadline)) goto retry; atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, -ETIMEDOUT); return -ETIMEDOUT; } ret = st->last_errno; spin_unlock(&st->lock); atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, ret); return ret ? -EAGAIN : 0; } @@ -5388,6 +5393,7 @@ static void ceph_mdsc_reset_complete(struct ceph_mds_client *mdsc, int ret) /* Wake up all requests that were blocked waiting for reset */ wake_up_all(&st->blocked_wq); + trace_ceph_client_reset_complete(mdsc, ret); } static void ceph_mdsc_reset_workfn(struct work_struct *work) @@ -5750,6 +5756,7 @@ int ceph_mdsc_schedule_reset(struct ceph_mds_client *mdsc, pr_info_client(mdsc->fsc->client, "manual session reset scheduled (reason=\"%s\")\n", msg); + trace_ceph_client_reset_schedule(mdsc, msg); return 0; } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index a4993644d543..1d6aab060780 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -179,6 +179,7 @@ struct ceph_fs_client { struct dentry *debugfs_status; struct dentry *debugfs_mds_sessions; struct dentry *debugfs_metrics_dir; + struct dentry *debugfs_reset_dir; struct dentry *debugfs_subvolume_metrics; #endif diff --git a/include/trace/events/ceph.h b/include/trace/events/ceph.h index 08cb0659fbfc..1b990632f62b 100644 --- a/include/trace/events/ceph.h +++ b/include/trace/events/ceph.h @@ -226,6 +226,73 @@ TRACE_EVENT(ceph_handle_caps, __entry->mseq) ); +/* + * Client reset tracepoints - identify the client by its monitor- + * assigned global_id so traces remain meaningful when kernel pointer + * hashing is enabled. + */ +TRACE_EVENT(ceph_client_reset_schedule, + TP_PROTO(const struct ceph_mds_client *mdsc, const char *reason), + TP_ARGS(mdsc, reason), + TP_STRUCT__entry( + __field(u64, client_id) + __string(reason, reason ? reason : "") + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __assign_str(reason); + ), + TP_printk("client_id=%llu reason=%s", + __entry->client_id, __get_str(reason)) +); + +TRACE_EVENT(ceph_client_reset_complete, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + +TRACE_EVENT(ceph_client_reset_blocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int blocked_count), + TP_ARGS(mdsc, blocked_count), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, blocked_count) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->blocked_count = blocked_count; + ), + TP_printk("client_id=%llu blocked_count=%d", __entry->client_id, + __entry->blocked_count) +); + +TRACE_EVENT(ceph_client_reset_unblocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + #undef EM #undef E_ #endif /* _TRACE_CEPH_H */ From ebbe8868cf473f698e0fbaf436d2618b2bcda806 Mon Sep 17 00:00:00 2001 From: Dawid Osuchowski Date: Fri, 27 Mar 2026 08:22:32 +0100 Subject: [PATCH 4419/5214] ice: fix FDIR CTRL VSI resource leak in ice_reset_all_vfs() Resetting all VFs causes resource leak on VFs with FDIR filters enabled as CTRL VSIs are only invalidated and not freed. Fix by using ice_vf_ctrl_vsi_release() instead of ice_vf_ctrl_invalidate_vsi() which aligns behavior with the ice_reset_vf() function. Reproduction: echo 1 > /sys/class/net/$pf/device/sriov_numvfs ethtool -N $vf flow-type ether proto 0x9000 action 0 echo 1 > /sys/class/net/$pf/device/reset Fixes: da62c5ff9dcd ("ice: Add support for per VF ctrl VSI enabling") Signed-off-by: Dawid Osuchowski Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index b1f46707dcc0..27e4acb1620f 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -801,7 +801,7 @@ void ice_reset_all_vfs(struct ice_pf *pf) * setup only when VF creates its first FDIR rule. */ if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_invalidate_vsi(vf); + ice_vf_ctrl_vsi_release(vf); ice_vf_pre_vsi_rebuild(vf); if (ice_vf_rebuild_vsi(vf)) { From 2bf7744bc3221a63b95c76c94eab1dad832fa401 Mon Sep 17 00:00:00 2001 From: Lukasz Czapnik Date: Fri, 27 Mar 2026 08:22:35 +0100 Subject: [PATCH 4420/5214] ice: fix AQ error code comparison in ice_set_pauseparam() Fix unreachable code: the conditionals in ice_set_pauseparam() used the bitwise-AND operator suggesting aq_failures is a bitmap, but it is actually an enum, making the third condition logically unreachable. Replace the if-else ladder with a switch statement. Also move the aq_failures initialization to the variable declaration and remove the redundant zeroing from ice_set_fc(). Fixes: fcea6f3da546 ("ice: Add stats and ethtool support") Signed-off-by: Lukasz Czapnik Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_common.c | 1 - drivers/net/ethernet/intel/ice/ice_ethtool.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 31e0de9e7f60..ef1ce106f81b 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -3882,7 +3882,6 @@ ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool ena_auto_link_update) if (!pi || !aq_failures) return -EINVAL; - *aq_failures = 0; hw = pi->hw; pcaps = kzalloc_obj(*pcaps); diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index 236d293aba98..49371b065845 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3508,7 +3508,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) struct ice_vsi *vsi = np->vsi; struct ice_hw *hw = &pf->hw; struct ice_port_info *pi; - u8 aq_failures; + u8 aq_failures = 0; bool link_up; u32 is_an; int err; @@ -3579,18 +3579,22 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) /* Set the FC mode and only restart AN if link is up */ err = ice_set_fc(pi, &aq_failures, link_up); - if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) { + switch (aq_failures) { + case ICE_SET_FC_AQ_FAIL_GET: netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) { + break; + case ICE_SET_FC_AQ_FAIL_SET: netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) { + break; + case ICE_SET_FC_AQ_FAIL_UPDATE: netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; + break; } return err; From eb509638686b0f8a98a0dd9c809f6a8db4d73a45 Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Wed, 8 Apr 2026 16:11:05 +0200 Subject: [PATCH 4421/5214] ice: fix ice_init_link() error return preventing probe ice_init_link() can return an error status from ice_update_link_info() or ice_init_phy_user_cfg(), causing probe to fail. An incorrect NVM update procedure can result in link/PHY errors, and the recommended resolution is to update the NVM using the correct procedure. If the driver fails probe due to link errors, the user cannot update the NVM to recover. The link/PHY errors logged are non-fatal: they are already annotated as 'not a fatal error if this fails'. Since none of the errors inside ice_init_link() should prevent probe from completing, convert it to void and remove the error check in the caller. All failures are already logged; callers have no meaningful recovery path for link init errors. Fixes: 5b246e533d01 ("ice: split probe into smaller functions") Cc: stable@vger.kernel.org Signed-off-by: Paul Greenwalt Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index e2fbe111f849..e2fd2dab03e3 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4789,16 +4789,14 @@ static void ice_init_wakeup(struct ice_pf *pf) device_set_wakeup_enable(ice_pf_to_dev(pf), false); } -static int ice_init_link(struct ice_pf *pf) +static void ice_init_link(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); int err; err = ice_init_link_events(pf->hw.port_info); - if (err) { + if (err) dev_err(dev, "ice_init_link_events failed: %d\n", err); - return err; - } /* not a fatal error if this fails */ err = ice_init_nvm_phy_type(pf->hw.port_info); @@ -4838,8 +4836,6 @@ static int ice_init_link(struct ice_pf *pf) } else { set_bit(ICE_FLAG_NO_MEDIA, pf->flags); } - - return err; } static int ice_init_pf_sw(struct ice_pf *pf) @@ -4982,13 +4978,11 @@ static int ice_init(struct ice_pf *pf) ice_init_wakeup(pf); - err = ice_init_link(pf); - if (err) - goto err_init_link; + ice_init_link(pf); err = ice_send_version(pf); if (err) - goto err_init_link; + goto err_deinit_pf_sw; ice_verify_cacheline_size(pf); @@ -5007,7 +5001,7 @@ static int ice_init(struct ice_pf *pf) return 0; -err_init_link: +err_deinit_pf_sw: ice_deinit_pf_sw(pf); err_init_pf_sw: ice_dealloc_vsis(pf); From c0d00c882bc432990d57052a659f9a8bd1f60687 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Wed, 8 Apr 2026 16:14:29 +0200 Subject: [PATCH 4422/5214] ice: call netif_keep_dst() once when entering switchdev mode netif_keep_dst() only needs to be called once for the uplink VSI, not once for each port representor. Move it from ice_eswitch_setup_repr() to ice_eswitch_enable_switchdev(). Fixes: defd52455aee ("ice: do Tx through PF netdev in slow-path") Signed-off-by: Marcin Szycik Signed-off-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_eswitch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index 2e4f0969035f..c30e27bbfe6e 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -117,8 +117,6 @@ static int ice_eswitch_setup_repr(struct ice_pf *pf, struct ice_repr *repr) if (!repr->dst) return -ENOMEM; - netif_keep_dst(uplink_vsi->netdev); - dst = repr->dst; dst->u.port_info.port_id = vsi->vsi_num; dst->u.port_info.lower_dev = uplink_vsi->netdev; @@ -312,6 +310,8 @@ static int ice_eswitch_enable_switchdev(struct ice_pf *pf) if (ice_eswitch_br_offloads_init(pf)) goto err_br_offloads; + netif_keep_dst(uplink_vsi->netdev); + pf->eswitch.is_running = true; return 0; From 3996771b8f759729cba0a28007438c085f814d61 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 21 Jun 2026 06:59:33 +0530 Subject: [PATCH 4423/5214] io_uring/memmap: bound io_pin_pages() by page array byte size io_pin_pages() checks that nr_pages does not exceed INT_MAX, then allocates a struct page * array of nr_pages entries. kvmalloc() limits allocations to INT_MAX bytes, but the check counts pages, not bytes. On 64-bit each entry is 8 bytes, so the array hits the INT_MAX byte limit at INT_MAX / sizeof(struct page *) pages, well before the page count check fires. Since commit b4e41050b212 ("io_uring/rsrc: raise registered buffer 1GB limit") raised the per-buffer cap to 1TB, a buffer near that cap maps ~2^28 pages, making the array allocation exceed INT_MAX bytes. This passes the page count check, reaches kvmalloc(), and triggers the WARN_ON_ONCE() for oversized allocations in __kvmalloc_node_noprof(). Check nr_pages against INT_MAX / sizeof(struct page *) so the buffer is rejected with -EOVERFLOW before the allocation is attempted. Reported-by: syzbot+f99b00a963915b6b52c6@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f99b00a963915b6b52c6 Fixes: b4e41050b212 ("io_uring/rsrc: raise registered buffer 1GB limit") Tested-by: syzbot+f99b00a963915b6b52c6@syzkaller.appspotmail.com Signed-off-by: Deepanshu Kartikey Reviewed-by: Gabriel Krisman Bertazi Link: https://patch.msgid.link/20260621012933.50571-1-kartikey406@gmail.com Signed-off-by: Jens Axboe --- io_uring/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/memmap.c b/io_uring/memmap.c index 4f9b439319c4..da1f6c5d07f8 100644 --- a/io_uring/memmap.c +++ b/io_uring/memmap.c @@ -53,7 +53,7 @@ struct page **io_pin_pages(unsigned long uaddr, unsigned long len, int *npages) nr_pages = end - start; if (WARN_ON_ONCE(!nr_pages)) return ERR_PTR(-EINVAL); - if (WARN_ON_ONCE(nr_pages > INT_MAX)) + if (nr_pages > INT_MAX / sizeof(struct page *)) return ERR_PTR(-EOVERFLOW); pages = kvmalloc_objs(struct page *, nr_pages, GFP_KERNEL_ACCOUNT); From 1afe4f19d6ad404621150f0e91feeccf12fb1037 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Sat, 25 Apr 2026 15:49:59 +0000 Subject: [PATCH 4424/5214] rtc: isl1208: Balance enable_irq_wake() with disable_irq_wake() on cleanup isl1208_setup_irq() calls enable_irq_wake() after a successful IRQ request, but the driver has no remove path that balances it. The driver is devm-only, so on unbind devm releases the IRQ - but enable_irq_wake() is not undone by IRQ release, so the wake count for that IRQ stays incremented. Each rebind therefore leaks one wake reference; the leak doubles for the chip variant that has a separate evdet IRQ, since isl1208_setup_irq() is then called twice during probe. Register a devm action that calls disable_irq_wake() per IRQ. While at it, check enable_irq_wake()'s return value: on failure, propagate the error rather than silently registering a disable action for an IRQ whose wake state was never enabled. Fixes: 9ece7cd833a3 ("rtc: isl1208: Add "evdet" interrupt source for isl1219") Signed-off-by: John Madieu Link: https://patch.msgid.link/20260425154959.2796261-3-john.madieu.xa@bp.renesas.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-isl1208.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index bcaa766a5068..9bdd5d121571 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -822,6 +822,11 @@ static const struct nvmem_config isl1208_nvmem_config = { .reg_write = isl1208_nvmem_write, }; +static void isl1208_disable_irq_wake_action(void *data) +{ + disable_irq_wake((unsigned long)data); +} + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -831,7 +836,15 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq) client); if (!rc) { device_init_wakeup(&client->dev, true); - enable_irq_wake(irq); + rc = enable_irq_wake(irq); + if (rc) + return rc; + + rc = devm_add_action_or_reset(&client->dev, + isl1208_disable_irq_wake_action, + (void *)(unsigned long)irq); + if (rc) + return rc; } else { dev_err(&client->dev, "Unable to request irq %d, no alarm support\n", From a903afff66d7379c6ece42bd18b2a17f4c79d1a9 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:32 +0800 Subject: [PATCH 4425/5214] ice: dpll: set pointers to NULL after kfree in ice_dpll_deinit_info ice_dpll_deinit_info() calls kfree() on several pf->dplls fields (inputs, outputs, eec.input_prio, pps.input_prio) but does not set the pointers to NULL afterward. This leaves dangling pointers in the pf->dplls structure. While not currently exploitable through existing code paths, this is unsafe because: 1. If ice_dpll_init_info() is called again after a deinit (e.g. during driver recovery), and a subsequent allocation within init fails, the error path will jump to deinit_info and call ice_dpll_deinit_info() again. Since some pointers still hold the old freed addresses, this would result in a double-free. 2. Any future code that checks these pointers before use or after free would be unprotected against use-after-free. Follow the common kernel convention of setting pointers to NULL after kfree() so that: - kfree(NULL) is a safe no-op, preventing double-free - NULL checks on these pointers become meaningful This is a preparatory fix for a subsequent patch that routes additional error paths in ice_dpll_init_info() to the deinit_info label. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_dpll.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 462c69cc11e1..3876ee7255ac 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4645,9 +4645,13 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type) static void ice_dpll_deinit_info(struct ice_pf *pf) { kfree(pf->dplls.inputs); + pf->dplls.inputs = NULL; kfree(pf->dplls.outputs); + pf->dplls.outputs = NULL; kfree(pf->dplls.eec.input_prio); + pf->dplls.eec.input_prio = NULL; kfree(pf->dplls.pps.input_prio); + pf->dplls.pps.input_prio = NULL; } /** From 20da495f2df0fd4adc435d3d621366e8c807539c Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:33 +0800 Subject: [PATCH 4426/5214] ice: dpll: fix memory leak in ice_dpll_init_info error paths Several error return paths in ice_dpll_init_info() directly return without freeing previously allocated resources, causing memory leaks: - When de->input_prio allocation fails, d->inputs is leaked - When dp->input_prio allocation fails, d->inputs and de->input_prio are leaked - When ice_get_cgu_rclk_pin_info() fails, all previously allocated inputs/outputs/input_prio are leaked - When ice_dpll_init_pins_info(RCLK_INPUT) fails, same resources are leaked Fix this by jumping to the deinit_info label which properly calls ice_dpll_deinit_info() to free all allocated resources. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_dpll.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 3876ee7255ac..30c3a4db7d61 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4752,12 +4752,16 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) alloc_size = sizeof(*de->input_prio) * d->num_inputs; de->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!de->input_prio) - return -ENOMEM; + if (!de->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } dp->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!dp->input_prio) - return -ENOMEM; + if (!dp->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT); if (ret) @@ -4782,12 +4786,12 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx, &pf->dplls.rclk.num_parents); if (ret) - return ret; + goto deinit_info; for (i = 0; i < pf->dplls.rclk.num_parents; i++) pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i; ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT); if (ret) - return ret; + goto deinit_info; de->mode = DPLL_MODE_AUTOMATIC; dp->mode = DPLL_MODE_AUTOMATIC; From 798f94603eb0737033861efd320a9159f382a7c5 Mon Sep 17 00:00:00 2001 From: Mohamed Khalfella Date: Wed, 6 May 2026 15:41:23 -0700 Subject: [PATCH 4427/5214] i40e: Fix i40e_debug() to use struct i40e_hw argument i40e_debug() macro takes struct i40e_hw *h as first argument. But the macro body uses hw instead of h. This has been working so far because hw happens to be the name of the variable in the context where the macro is expanded. Fix the macro to use the passed argument. Fixes: 5dfd37c37a44 ("i40e: Split i40e_osdep.h") Signed-off-by: Mohamed Khalfella Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debug.h b/drivers/net/ethernet/intel/i40e/i40e_debug.h index e9871dfb32bd..01fd70db9086 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debug.h +++ b/drivers/net/ethernet/intel/i40e/i40e_debug.h @@ -42,7 +42,7 @@ struct device *i40e_hw_to_dev(struct i40e_hw *hw); #define i40e_debug(h, m, s, ...) \ do { \ if (((m) & (h)->debug_mask)) \ - dev_info(i40e_hw_to_dev(hw), s, ##__VA_ARGS__); \ + dev_info(i40e_hw_to_dev(h), s, ##__VA_ARGS__); \ } while (0) #endif /* _I40E_DEBUG_H_ */ From 578294b8b60d2c630991f221838f9ec61ae89df0 Mon Sep 17 00:00:00 2001 From: Dima Ruinskiy Date: Fri, 17 Apr 2026 13:43:30 +0300 Subject: [PATCH 4428/5214] e1000e: Reconfigure PLL clock gate timeout and re-enable K1 on Meteor Lake Commit 3c7bf5af21960 ("e1000e: Introduce private flag to disable K1") disabled K1 by default on Meteor Lake and newer systems due to packet loss observed on various platforms. However, disabling K1 caused an increase in power consumption. To mitigate this, reconfigure the PLL clock gate value so that K1 can remain enabled without incurring the additional power consumption. Re-enable K1 by default, but keep the private flag to support disabling it via ethtool. Additionally, introduce a DMI quirk table, so that K1 may be disabled by default on known problematic systems. Currently, this includes the Dell Pro 16 Plus, where the issue has been reported to persist despite the changes to the PLL lock timeout. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220954 Link: https://lists.osuosl.org/pipermail/intel-wired-lan/Week-of-Mon-20250623/048860.html Link: https://lists.osuosl.org/pipermail/intel-wired-lan/Week-of-Mon-20260330/054059.html Signed-off-by: Dima Ruinskiy Co-developed-by: Vitaly Lifshits Signed-off-by: Vitaly Lifshits Fixes: 3c7bf5af21960 ("e1000e: Introduce private flag to disable K1") Tested-by: Moriya Kadosh Tested-by: Todd Brandt Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 3 +++ drivers/net/ethernet/intel/e1000e/netdev.c | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index dea208db1be5..aa90e0ce8aca 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1594,6 +1594,9 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) phy_reg &= ~I217_PLL_CLOCK_GATE_MASK; if (speed == SPEED_100 || speed == SPEED_10) phy_reg |= 0x3E8; + else if (hw->mac.type == e1000_pch_mtp || + hw->mac.type == e1000_pch_ptp) + phy_reg |= 0x1D5; else phy_reg |= 0xFA; e1e_wphy_locked(hw, I217_PLL_CLOCK_GATE_REG, phy_reg); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 808e5cddd6a9..844f31ab37ad 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "e1000.h" #define CREATE_TRACE_POINTS @@ -58,6 +59,17 @@ static const struct e1000_info *e1000_info_tbl[] = { [board_pch_ptp] = &e1000_pch_ptp_info, }; +static const struct dmi_system_id disable_k1_list[] = { + { + .ident = "Dell Pro 16 Plus PB16250", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell Pro 16 Plus PB16250"), + }, + }, + {} +}; + struct e1000_reg_info { u32 ofs; char *name; @@ -7670,7 +7682,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* init PTP hardware clock */ e1000e_ptp_init(adapter); - if (hw->mac.type >= e1000_pch_mtp) + /* disable K1 by default on known problematic systems */ + if (hw->mac.type >= e1000_pch_mtp && dmi_check_system(disable_k1_list)) adapter->flags2 |= FLAG2_DISABLE_K1; /* reset the hardware with the new settings */ From 214cdae69dba9bb1fc0b517b7fb97bab385a2e3a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 22 Jun 2026 18:07:52 +0200 Subject: [PATCH 4429/5214] block: fix incorrect error injection static key decrement Only decrement the static key when we had items and thus it was incremented before. Fixes: e8dcf2d142bd ("block: add configurable error injection") Reported-by: Damien Le Moal Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260622160752.1552516-1-hch@lst.de Signed-off-by: Jens Axboe --- block/error-injection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/error-injection.c b/block/error-injection.c index d24c90e9a25f..cfb83138960c 100644 --- a/block/error-injection.c +++ b/block/error-injection.c @@ -120,13 +120,13 @@ static void error_inject_removeall(struct gendisk *disk) struct blk_error_inject *inj; mutex_lock(&disk->error_injection_lock); - clear_bit(GD_ERROR_INJECT, &disk->state); + if (test_and_clear_bit(GD_ERROR_INJECT, &disk->state)) + static_branch_dec(&blk_error_injection_enabled); while ((inj = list_first_entry_or_null(&disk->error_injection_list, struct blk_error_inject, entry))) { list_del_rcu(&inj->entry); kfree_rcu_mightsleep(inj); } - static_branch_dec(&blk_error_injection_enabled); mutex_unlock(&disk->error_injection_lock); } From 9280e6edf65662b6aafc8b704ad065b54c08b519 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Mon, 22 Jun 2026 05:22:55 +0530 Subject: [PATCH 4430/5214] nbd: don't warn when reclassifying a busy socket lock nbd_reclassify_socket() warns via WARN_ON_ONCE() if the socket lock is held at the point of reclassification. That assertion was copied from nvme-tcp, where the socket is created internally by the kernel (sock_create_kern()) and is never visible to user space, so the lock is guaranteed to be free. NBD is different: the socket is looked up from a user-supplied fd in nbd_get_socket(), and user space retains that fd. A concurrent syscall on the same socket (or softirq processing taking bh_lock_sock() on a connected TCP socket) can legitimately hold the lock at the instant NBD reclassifies it. sock_allow_reclassification() then returns false and the WARN_ON_ONCE() fires, which turns into a crash under panic_on_warn. This is reachable by simply racing NBD_CMD_CONNECT against socket activity on the same fd, as reported by syzbot. Hitting a held lock here is expected for an externally owned socket and is not a kernel bug, so skip reclassification silently instead of warning. Reclassification is a lockdep-only annotation, so skipping it in the rare racing case is harmless. Reported-by: syzbot+6b85d1e39a5b8ed9a954@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6b85d1e39a5b8ed9a954 Fixes: d532cddb6c60 ("nbd: Reclassify sockets to avoid lockdep circular dependency") Signed-off-by: Deepanshu Kartikey Acked-by: Eric Dumazet Link: https://patch.msgid.link/20260621235255.66015-1-kartikey406@gmail.com Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 3a585a0c882a..8f10762e90ef 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1246,7 +1246,7 @@ static void nbd_reclassify_socket(struct socket *sock) { struct sock *sk = sock->sk; - if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) + if (!sock_allow_reclassification(sk)) return; switch (sk->sk_family) { From 17b2d950a3c0328ed749476e6118ca869b3ca8b5 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 21 Jun 2026 21:59:30 +0800 Subject: [PATCH 4431/5214] block, bfq: protect async queue reset with blkcg locks Writing 0 to BFQ's low_latency attribute ends weight raising for active, idle and async queues. The async cgroup path walks q->blkg_list, converts each blkg to BFQ policy data and then reads bfqg->async_bfqq and bfqg->async_idle_bfqq. That walk was protected only by bfqd->lock. blkcg release work is serialized by q->blkcg_mutex and q->queue_lock instead, and blkg_free_workfn() can call BFQ's pd_free_fn before it removes blkg->q_node from q->blkg_list. A low_latency reset can therefore still find the blkg on the queue list after the BFQ policy data has been freed. The buggy scenario involves two paths, with each column showing the order within that path: BFQ low_latency reset: blkcg blkg release work: 1. bfq_low_latency_store() 1. blkg_free_workfn() takes calls bfq_end_wr(). q->blkcg_mutex. 2. bfq_end_wr_async() walks 2. BFQ pd_free_fn drops the q->blkg_list. final bfq_group reference. 3. blkg_to_bfqg() returns 3. blkg->q_node remains on the stale policy data. q->blkg_list until list_del_init(). 4. bfq_end_wr_async_queues() reads async queue fields. Fix this by taking q->blkcg_mutex and q->queue_lock around the q->blkg_list walk, then taking bfqd->lock before touching BFQ async queues. The mutex serializes against policy-data free and queue_lock stabilizes the list. Move the async reset out of bfq_end_wr()'s existing bfqd->lock critical section so the lock order matches blkcg policy callbacks. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in bfq_end_wr_async_queues+0x246/0x340 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? bfq_end_wr_async_queues+0x246/0x340 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? bfq_end_wr_async_queues+0x246/0x340 kasan_report+0xe0/0x110 ? bfq_end_wr_async_queues+0x246/0x340 bfq_end_wr_async_queues+0x246/0x340 bfq_end_wr_async+0xba/0x180 bfq_low_latency_store+0x4e5/0x690 ? 0xffffffffc02150da ? __pfx_bfq_low_latency_store+0x10/0x10 ? __pfx_bfq_low_latency_store+0x10/0x10 elv_attr_store+0xc4/0x110 kernfs_fop_write_iter+0x2f5/0x4a0 vfs_write+0x604/0x11f0 ? __pfx_locks_remove_posix+0x10/0x10 ? __pfx_vfs_write+0x10/0x10 ksys_write+0xf9/0x1d0 ? __pfx_ksys_write+0x10/0x10 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 bfq_pd_alloc+0xc0/0x1b0 blkg_alloc+0x346/0x960 blkg_create+0x8c2/0x10d0 bio_associate_blkg_from_css+0x9f3/0xfa0 bio_associate_blkg+0xd9/0x200 bio_init+0x303/0x640 __blkdev_direct_IO_simple+0x56b/0x8a0 blkdev_direct_IO+0x8e7/0x2580 blkdev_read_iter+0x205/0x400 vfs_read+0x7b0/0xda0 ksys_read+0xf9/0x1d0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 465: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 blkg_free_workfn+0xef/0x460 process_one_work+0x8d0/0x1870 worker_thread+0x575/0xf80 kthread+0x2e7/0x3c0 ret_from_fork+0x576/0x810 ret_from_fork_asm+0x1a/0x30 Fixes: 44e44a1b329e ("block, bfq: improve responsiveness") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Tao Cui Link: https://patch.msgid.link/20260621135930.2657810-1-zzzccc427@gmail.com Signed-off-by: Jens Axboe --- block/bfq-cgroup.c | 13 ++++++++++++- block/bfq-iosched.c | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c index 0bd0332b3d78..d8fdace464b4 100644 --- a/block/bfq-cgroup.c +++ b/block/bfq-cgroup.c @@ -936,14 +936,23 @@ static void bfq_pd_offline(struct blkg_policy_data *pd) void bfq_end_wr_async(struct bfq_data *bfqd) { + struct request_queue *q = bfqd->queue; struct blkcg_gq *blkg; - list_for_each_entry(blkg, &bfqd->queue->blkg_list, q_node) { + mutex_lock(&q->blkcg_mutex); + spin_lock_irq(&q->queue_lock); + spin_lock(&bfqd->lock); + + list_for_each_entry(blkg, &q->blkg_list, q_node) { struct bfq_group *bfqg = blkg_to_bfqg(blkg); bfq_end_wr_async_queues(bfqd, bfqg); } bfq_end_wr_async_queues(bfqd, bfqd->root_group); + + spin_unlock(&bfqd->lock); + spin_unlock_irq(&q->queue_lock); + mutex_unlock(&q->blkcg_mutex); } static int bfq_io_show_weight_legacy(struct seq_file *sf, void *v) @@ -1416,7 +1425,9 @@ void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio) {} void bfq_end_wr_async(struct bfq_data *bfqd) { + spin_lock_irq(&bfqd->lock); bfq_end_wr_async_queues(bfqd, bfqd->root_group); + spin_unlock_irq(&bfqd->lock); } struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 141c602d5e85..eec9be62061b 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -2653,9 +2653,10 @@ static void bfq_end_wr(struct bfq_data *bfqd) } list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) bfq_bfqq_end_wr(bfqq); - bfq_end_wr_async(bfqd); spin_unlock_irq(&bfqd->lock); + + bfq_end_wr_async(bfqd); } static sector_t bfq_io_struct_pos(void *io_struct, bool request) From 0ab5ee5a1badb58cbb2242617cb01a4972b1f2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Koutn=C3=BD?= Date: Thu, 5 Feb 2026 23:54:23 +0800 Subject: [PATCH 4432/5214] blk-cgroup: fix UAF in __blkcg_rstat_flush() When multiple blkgs in the same blkcg are released concurrently, a use-after-free can occur. The race happens when one blkg's __blkcg_rstat_flush() removes another blkg's iostat entries via llist_del_all(). The second blkg sees an empty list and proceeds to free itself while the first is still iterating over its entries. Move the flush from __blkg_release() (RCU callback) to blkg_release() (before call_rcu). This ensures the RCU grace period waits for any concurrent flush's rcu_read_lock() section to complete before freeing. Cc: stable@vger.kernel.org Cc: Jay Shin Cc: Tejun Heo Cc: Waiman Long Fixes: 20cb1c2fb756 ("blk-cgroup: Flush stats before releasing blkcg_gq") Reported-by: coregee2000@gmail.com Closes: https://lore.kernel.org/linux-block/CAHPqNmwT9oRpem3J3erS_W0uSQND47LGGSBsNxP8E6uSUish1w@mail.gmail.com/ Signed-off-by: Ming Lei Tested-by: Jose Fernandez (Anthropic) Link: https://patch.msgid.link/20260205155425.342084-1-ming.lei@redhat.com Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 3093c1c03902..342816cbbd1b 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -164,20 +164,10 @@ static void blkg_free(struct blkcg_gq *blkg) static void __blkg_release(struct rcu_head *rcu) { struct blkcg_gq *blkg = container_of(rcu, struct blkcg_gq, rcu_head); - struct blkcg *blkcg = blkg->blkcg; - int cpu; #ifdef CONFIG_BLK_CGROUP_PUNT_BIO WARN_ON(!bio_list_empty(&blkg->async_bios)); #endif - /* - * Flush all the non-empty percpu lockless lists before releasing - * us, given these stat belongs to us. - * - * blkg_stat_lock is for serializing blkg stat update - */ - for_each_possible_cpu(cpu) - __blkcg_rstat_flush(blkcg, cpu); /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); @@ -195,6 +185,17 @@ static void __blkg_release(struct rcu_head *rcu) static void blkg_release(struct percpu_ref *ref) { struct blkcg_gq *blkg = container_of(ref, struct blkcg_gq, refcnt); + struct blkcg *blkcg = blkg->blkcg; + int cpu; + + /* + * Flush all the non-empty percpu lockless lists before releasing + * us, given these stat belongs to us. + * + * blkg_stat_lock is for serializing blkg stat update + */ + for_each_possible_cpu(cpu) + __blkcg_rstat_flush(blkcg, cpu); call_rcu(&blkg->rcu_head, __blkg_release); } From 3ed9b4779a4aa3f44cd9f78627498d7adac40daa Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Tue, 16 Jun 2026 09:17:46 +0800 Subject: [PATCH 4433/5214] blk-cgroup: defer blkcg css_put until blkg is unlinked from queue [BUG] Our fuzz testing triggered a blkcg use-after-free issue: BUG: KASAN: slab-use-after-free in _raw_spin_lock+0x75/0xe0 Call Trace: ... blkcg_deactivate_policy+0x244/0x4d0 ioc_rqos_exit+0x44/0xe0 rq_qos_exit+0xba/0x120 __del_gendisk+0x50b/0x800 del_gendisk+0xff/0x190 ... [CAUSE] process1 process2 cgroup_rmdir ... css_killed_work_fn offline_css ... blkcg_destroy_blkgs ... __blkg_release css_put(&blkg->blkcg->css) blkg_free INIT_WORK(xxx, blkg_free_workfn) schedule_work css_put ... blkcg_css_free kfree(blkcg)--------blkcg has been freed!!! ====================================schedule_work blkg_free_workfn __del_gendisk rq_qos_exit ioc_rqos_exit blkcg_deactivate_policy mutex_lock(&q->blkcg_mutex) spin_lock_irq(&q->queue_lock) list_for_each_entry(blkg, xxx) blkcg = blkg->blkcg spin_lock(&blkcg->lock)-------UAF!!! mutex_lock(&q->blkcg_mutex) spin_lock_irq(&q->queue_lock) /* Only then is the blkg removed from the list */ list_del_init(&blkg->q_node) As a result, a blkg can still be reachable through q->blkg_list while its ->blkcg has already been freed. [Fix] Fix this by deferring the blkcg css_put() until after the blkg has been unlinked from q->blkg_list in blkg_free_workfn(). This ensures that the blkcg outlives every blkg still reachable through q->blkg_list, so any iterator holding q->queue_lock is guaranteed to observe a valid blkg->blkcg. While at it, move css_tryget_online() from blkg_create() into blkg_alloc() so that the css reference is owned by the alloc/free pair rather than straddling layers: blkg_alloc() <-> blkg_free() blkg_create() <-> blkg_destroy() Fixes: f1c006f1c685 ("blk-cgroup: synchronize pd_free_fn() from blkg_free_workfn() and blkcg_deactivate_policy()") Suggested-by: Hou Tao Signed-off-by: Zizhi Wo Reviewed-by: Yu Kuai Reviewed-by: Tang Yizhou Link: https://patch.msgid.link/20260616011746.2451461-1-wozizhi@huaweicloud.com Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 342816cbbd1b..ee076ab795d3 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -136,6 +136,11 @@ static void blkg_free_workfn(struct work_struct *work) spin_unlock_irq(&q->queue_lock); mutex_unlock(&q->blkcg_mutex); + /* + * Release blkcg css ref only after blkg is removed from q->blkg_list, + * so concurrent iterators won't see a blkg with a freed blkcg. + */ + css_put(&blkg->blkcg->css); blk_put_queue(q); free_percpu(blkg->iostat_cpu); percpu_ref_exit(&blkg->refcnt); @@ -169,8 +174,6 @@ static void __blkg_release(struct rcu_head *rcu) WARN_ON(!bio_list_empty(&blkg->async_bios)); #endif - /* release the blkcg and parent blkg refs this blkg has been holding */ - css_put(&blkg->blkcg->css); blkg_free(blkg); } @@ -314,6 +317,9 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct gendisk *disk, goto out_exit_refcnt; if (!blk_get_queue(disk->queue)) goto out_free_iostat; + /* blkg holds a reference to blkcg */ + if (!css_tryget_online(&blkcg->css)) + goto out_put_queue; blkg->q = disk->queue; INIT_LIST_HEAD(&blkg->q_node); @@ -354,6 +360,8 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct gendisk *disk, while (--i >= 0) if (blkg->pd[i]) blkcg_policy[i]->pd_free_fn(blkg->pd[i]); + css_put(&blkcg->css); +out_put_queue: blk_put_queue(disk->queue); out_free_iostat: free_percpu(blkg->iostat_cpu); @@ -382,18 +390,12 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, goto err_free_blkg; } - /* blkg holds a reference to blkcg */ - if (!css_tryget_online(&blkcg->css)) { - ret = -ENODEV; - goto err_free_blkg; - } - /* allocate */ if (!new_blkg) { new_blkg = blkg_alloc(blkcg, disk, GFP_NOWAIT); if (unlikely(!new_blkg)) { ret = -ENOMEM; - goto err_put_css; + goto err_free_blkg; } } blkg = new_blkg; @@ -403,7 +405,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, blkg->parent = blkg_lookup(blkcg_parent(blkcg), disk->queue); if (WARN_ON_ONCE(!blkg->parent)) { ret = -ENODEV; - goto err_put_css; + goto err_free_blkg; } blkg_get(blkg->parent); } @@ -443,8 +445,6 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, blkg_put(blkg); return ERR_PTR(ret); -err_put_css: - css_put(&blkcg->css); err_free_blkg: if (new_blkg) blkg_free(new_blkg); From 947d7ea6f60b5fe24d8f8b69e55e0d6e1e8855e7 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 20 May 2026 23:37:13 -0500 Subject: [PATCH 4434/5214] rtc: interface: Add rtc_read_next_alarm() to read next expiring timer Add a new function rtc_read_next_alarm() that reads the next expiring alarm from the RTC timerqueue. This is different from rtc_read_alarm(), which only reads the aie_timer. The wakealarm sysfs file programs the rtc->aie_timer, whereas the alarmtimer suspend routine programs its own timer into the RTC timerqueue. Both timers end up in the RTC's timerqueue, and the first expiring timer is what gets armed in the hardware. This new function allows code to query which alarm will actually fire next, regardless of which subsystem programmed it. This is needed by platform code that needs to program secondary timers based on the actual next wakeup time. Link: https://lore.kernel.org/all/87ed50z0le.ffs@tglx Suggested-by: Thomas Gleixner Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Mario Limonciello Link: https://patch.msgid.link/20260521043714.1022930-2-mario.limonciello@amd.com Signed-off-by: Alexandre Belloni --- drivers/rtc/interface.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/linux/rtc.h | 2 ++ 2 files changed, 42 insertions(+) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 1906f4884a83..7859be8f2a92 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -384,6 +384,46 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) return err; } +/** + * rtc_read_next_alarm - read the next expiring alarm + * @rtc: RTC device + * @alarm: storage for the alarm information + * + * Read the next expiring alarm from the RTC timerqueue. This returns + * the alarm that will actually fire next, which may be different from + * rtc_read_alarm() if multiple timers are queued (e.g., alarmtimer + * and wakealarm sysfs both active). + * + * Returns: 0 on success, -ENOENT if no alarm is pending, or other error. + */ +int rtc_read_next_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + struct timerqueue_node *next; + int err; + + if (!rtc || !alarm) + return -EINVAL; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + next = timerqueue_getnext(&rtc->timerqueue); + if (!next) { + err = -ENOENT; + goto unlock; + } + + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->time = rtc_ktime_to_tm(next->expires); + alarm->enabled = 1; + +unlock: + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_next_alarm); + int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { int err; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 95da051fb155..c09fc22819d0 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -190,6 +190,8 @@ extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); +extern int rtc_read_next_alarm(struct rtc_device *rtc, + struct rtc_wkalrm *alrm); extern int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); extern int rtc_initialize_alarm(struct rtc_device *rtc, From a50b23a57fce4157156014bc00b0cf9f4ab1a69f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 27 May 2026 21:39:03 +0200 Subject: [PATCH 4435/5214] rtc: remove unused pcap driver The platform was removed a few years ago, and the mfd driver is also gone now, so it is impossible to build or use it. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260527193927.3523952-1-arnd@kernel.org Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 7 -- drivers/rtc/Makefile | 1 - drivers/rtc/rtc-pcap.c | 179 ----------------------------------------- 3 files changed, 187 deletions(-) delete mode 100644 drivers/rtc/rtc-pcap.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 364afc73f8ab..01def8231873 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1763,13 +1763,6 @@ config RTC_DRV_STMP This driver can also be built as a module. If so, the module will be called rtc-stmp3xxx. -config RTC_DRV_PCAP - tristate "PCAP RTC" - depends on EZX_PCAP - help - If you say Y here you will get support for the RTC found on - the PCAP2 ASIC used on some Motorola phones. - config RTC_DRV_MC13XXX depends on MFD_MC13XXX tristate "Freescale MC13xxx RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6cf7e066314e..0347645b021f 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -128,7 +128,6 @@ obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o -obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c deleted file mode 100644 index d6651611a0c6..000000000000 --- a/drivers/rtc/rtc-pcap.c +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * pcap rtc code for Motorola EZX phones - * - * Copyright (c) 2008 guiming zhuo - * Copyright (c) 2009 Daniel Ribeiro - * - * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola - */ - -#include -#include -#include -#include -#include -#include -#include - -struct pcap_rtc { - struct pcap_chip *pcap; - struct rtc_device *rtc; -}; - -static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc) -{ - struct pcap_rtc *pcap_rtc = _pcap_rtc; - unsigned long rtc_events; - - if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ)) - rtc_events = RTC_IRQF | RTC_UF; - else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA)) - rtc_events = RTC_IRQF | RTC_AF; - else - rtc_events = 0; - - rtc_update_irq(pcap_rtc->rtc, 1, rtc_events); - return IRQ_HANDLED; -} - -static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - struct rtc_time *tm = &alrm->time; - unsigned long secs; - u32 tod; /* time of day, seconds since midnight */ - u32 days; /* days since 1/1/1970 */ - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod); - secs = tod & PCAP_RTC_TOD_MASK; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days); - secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; - - rtc_time64_to_tm(secs, tm); - - return 0; -} - -static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs = rtc_tm_to_time64(&alrm->time); - u32 tod, days; - - tod = secs % SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod); - - days = secs / SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days); - - return 0; -} - -static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs; - u32 tod, days; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod); - secs = tod & PCAP_RTC_TOD_MASK; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days); - secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; - - rtc_time64_to_tm(secs, tm); - - return 0; -} - -static int pcap_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs = rtc_tm_to_time64(tm); - u32 tod, days; - - tod = secs % SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod); - - days = secs / SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days); - - return 0; -} - -static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - - if (en) - enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); - else - disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); - - return 0; -} - -static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) -{ - return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); -} - -static const struct rtc_class_ops pcap_rtc_ops = { - .read_time = pcap_rtc_read_time, - .set_time = pcap_rtc_set_time, - .read_alarm = pcap_rtc_read_alarm, - .set_alarm = pcap_rtc_set_alarm, - .alarm_irq_enable = pcap_rtc_alarm_irq_enable, -}; - -static int __init pcap_rtc_probe(struct platform_device *pdev) -{ - struct pcap_rtc *pcap_rtc; - int timer_irq, alarm_irq; - int err = -ENOMEM; - - pcap_rtc = devm_kzalloc(&pdev->dev, sizeof(struct pcap_rtc), - GFP_KERNEL); - if (!pcap_rtc) - return err; - - pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent); - - platform_set_drvdata(pdev, pcap_rtc); - - pcap_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); - if (IS_ERR(pcap_rtc->rtc)) - return PTR_ERR(pcap_rtc->rtc); - - pcap_rtc->rtc->ops = &pcap_rtc_ops; - pcap_rtc->rtc->range_max = (1 << 14) * 86400ULL - 1; - - timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); - alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); - - err = devm_request_irq(&pdev->dev, timer_irq, pcap_rtc_irq, 0, - "RTC Timer", pcap_rtc); - if (err) - return err; - - err = devm_request_irq(&pdev->dev, alarm_irq, pcap_rtc_irq, 0, - "RTC Alarm", pcap_rtc); - if (err) - return err; - - return devm_rtc_register_device(pcap_rtc->rtc); -} - -static struct platform_driver pcap_rtc_driver = { - .driver = { - .name = "pcap-rtc", - }, -}; - -module_platform_driver_probe(pcap_rtc_driver, pcap_rtc_probe); - -MODULE_DESCRIPTION("Motorola pcap rtc driver"); -MODULE_AUTHOR("guiming zhuo "); -MODULE_LICENSE("GPL"); From a369f48be8de426a7d2bca18dbd46c2ad1138803 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 08:27:03 +0500 Subject: [PATCH 4436/5214] rtc: msc313: fix NULL deref in shared IRQ handler at probe msc313_rtc_probe() calls devm_request_irq() with IRQF_SHARED and &pdev->dev as the cookie, but platform_set_drvdata() is only called later after the clock setup. With a shared IRQ line, another device on the same line can trigger the handler in that window. The handler does dev_get_drvdata() on the cookie, gets NULL, and dereferences priv->rtc_base in interrupt context. Pass priv as the cookie directly so the handler reads it from dev_id without the lookup, removing the dependency on probe order. Fixes: be7d9c9161b9 ("rtc: Add support for the MSTAR MSC313 RTC") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260511032703.48262-1-sozdayvek@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-msc313.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c index 8d7737e0e2e0..6ef9c4efd7c9 100644 --- a/drivers/rtc/rtc-msc313.c +++ b/drivers/rtc/rtc-msc313.c @@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = { static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id) { - struct msc313_rtc *priv = dev_get_drvdata(dev_id); + struct msc313_rtc *priv = dev_id; u16 reg; reg = readw(priv->rtc_base + REG_RTC_STATUS_INT); @@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev) priv->rtc_dev->range_max = U32_MAX; ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED, - dev_name(&pdev->dev), &pdev->dev); + dev_name(&pdev->dev), priv); if (ret) { dev_err(dev, "Could not request IRQ\n"); return ret; From 4202e4254403156a00711990ec99982f43bd99f5 Mon Sep 17 00:00:00 2001 From: Yahya Saqban Date: Wed, 13 May 2026 00:02:35 +0300 Subject: [PATCH 4437/5214] rtc: interface: fix typos in rtc_handle_legacy_irq() documentation Fix spelling of 'occurence' to 'occurrence' and 'of' to 'or' in the kernel-doc comment for rtc_handle_legacy_irq(). Signed-off-by: Yahya Saqban Link: https://patch.msgid.link/20260512210235.343070-1-yahyasaqban@gmail.com Signed-off-by: Alexandre Belloni --- drivers/rtc/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7859be8f2a92..96626f8068f9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -675,8 +675,8 @@ EXPORT_SYMBOL_GPL(rtc_update_irq_enable); /** * rtc_handle_legacy_irq - AIE, UIE and PIE event hook * @rtc: pointer to the rtc device - * @num: number of occurence of the event - * @mode: type of the event, RTC_AF, RTC_UF of RTC_PF + * @num: number of occurrence of the event + * @mode: type of the event, RTC_AF, RTC_UF or RTC_PF * * This function is called when an AIE, UIE or PIE mode interrupt * has occurred (or been emulated). From 5e7f746bc106ad9cd300e161bff42f62a8bf1e6b Mon Sep 17 00:00:00 2001 From: Tommy Huang Date: Mon, 1 Jun 2026 17:14:06 +0800 Subject: [PATCH 4438/5214] dt-bindings: rtc: add ASPEED AST2700 compatible Document the compatible string for the RTC controller found on ASPEED AST2700 SoCs. Signed-off-by: Tommy Huang Acked-by: Conor Dooley Link: https://patch.msgid.link/20260601-ast2700-rtc-v1-1-15d4ca46500a@aspeedtech.com Signed-off-by: Alexandre Belloni --- Documentation/devicetree/bindings/rtc/trivial-rtc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index 722176c831aa..f4d0eed98a08 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -30,6 +30,8 @@ properties: - aspeed,ast2500-rtc # ASPEED BMC ast2600 Real-time Clock - aspeed,ast2600-rtc + # ASPEED BMC ast2700 Real-time Clock + - aspeed,ast2700-rtc # Conexant Digicolor Real Time Clock Controller - cnxt,cx92755-rtc # I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output From 3319cfeeb8c4047026f84df045c438f7bbd338a6 Mon Sep 17 00:00:00 2001 From: Tommy Huang Date: Mon, 1 Jun 2026 17:14:07 +0800 Subject: [PATCH 4439/5214] rtc: aspeed: add AST2700 compatible Add support for matching the RTC controller on ASPEED AST2700 SoCs. The AST2700 RTC controller is compatible with the existing ASPEED RTC driver implementation. Signed-off-by: Tommy Huang Link: https://patch.msgid.link/20260601-ast2700-rtc-v1-2-15d4ca46500a@aspeedtech.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-aspeed.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c index 0d0053b52f9b..8f5b440f8c0a 100644 --- a/drivers/rtc/rtc-aspeed.c +++ b/drivers/rtc/rtc-aspeed.c @@ -111,6 +111,7 @@ static const struct of_device_id aspeed_rtc_match[] = { { .compatible = "aspeed,ast2400-rtc", }, { .compatible = "aspeed,ast2500-rtc", }, { .compatible = "aspeed,ast2600-rtc", }, + { .compatible = "aspeed,ast2700-rtc", }, {} }; MODULE_DEVICE_TABLE(of, aspeed_rtc_match); From 851d961ff248218f681c53cf0f7f08cf8201a117 Mon Sep 17 00:00:00 2001 From: Xue Lei Date: Thu, 11 Jun 2026 10:33:50 +0800 Subject: [PATCH 4440/5214] rtc: mv: add suspend/resume support for wakeup Add PM suspend/resume callbacks to enable/disable IRQ wake for the RTC alarm interrupt. This allows the RTC alarm to wake the system from STR (e.g. via rtcwake -m mem -s N). Without this, the RTC IRQ is masked during suspend by the MPIC's IRQCHIP_MASK_ON_SUSPEND behavior, preventing alarm-based wakeup. Signed-off-by: Xue Lei Link: https://patch.msgid.link/20260611023350.1370881-1-Xue.Lei@windriver.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mv.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index c27ad626d09f..f88976fd6d5d 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -301,6 +301,28 @@ static const struct of_device_id rtc_mv_of_match_table[] = { MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table); #endif +#ifdef CONFIG_PM_SLEEP +static int mv_rtc_suspend(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + enable_irq_wake(pdata->irq); + return 0; +} + +static int mv_rtc_resume(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + disable_irq_wake(pdata->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mv_rtc_pm_ops, mv_rtc_suspend, mv_rtc_resume); + /* * mv_rtc_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at @@ -312,6 +334,7 @@ static struct platform_driver mv_rtc_driver __refdata = { .driver = { .name = "rtc-mv", .of_match_table = of_match_ptr(rtc_mv_of_match_table), + .pm = &mv_rtc_pm_ops, }, }; From 6f6183a39533d727deaa5061cadae6dd9e6744d0 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 18 Jun 2026 10:18:43 +0000 Subject: [PATCH 4441/5214] bpf: Guard conntrack opts error writes The conntrack lookup and allocation kfuncs take an opts pointer together with an opts__sz argument. The verifier checks only the memory range described by opts__sz, but the wrappers unconditionally write opts->error whenever the internal lookup or allocation helper returns an error. For an invalid size smaller than the end of opts->error, that write can land outside the verifier-checked range. Keep returning NULL for invalid arguments, but only report the error through opts->error when the supplied size includes the field. This preserves error reporting for the supported 12-byte and 16-byte layouts, and for other invalid sizes that still include opts->error. Fixes: b4c2b9593a1c ("net/netfilter: Add unstable CT lookup helpers for XDP and TC-BPF") Fixes: d7e79c97c00c ("net: netfilter: Add kfuncs to allocate and insert CT") Signed-off-by: Yiyang Chen Link: https://lore.kernel.org/r/9535e781fe14449b1d4e9bbc3baa7566a93bf512.1781765747.git.chenyy23@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- net/netfilter/nf_conntrack_bpf.c | 35 ++++++++++++-------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index 40c261cd0af3..f98d1d4b42c3 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -65,6 +65,15 @@ enum { NF_BPF_CT_OPTS_SZ = 16, }; +static void *bpf_ct_opts_result(struct bpf_ct_opts *opts, u32 opts__sz, void *ret) +{ + if (!IS_ERR(ret)) + return ret; + if (opts__sz >= offsetofend(struct bpf_ct_opts, error)) + opts->error = PTR_ERR(ret); + return NULL; +} + static int bpf_nf_ct_tuple_parse(struct bpf_sock_tuple *bpf_tuple, u32 tuple_len, u8 protonum, u8 dir, struct nf_conntrack_tuple *tuple) @@ -297,12 +306,7 @@ bpf_xdp_ct_alloc(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, nfct = __bpf_nf_ct_alloc_entry(dev_net(ctx->rxq->dev), bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -331,11 +335,7 @@ bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = dev_net(ctx->rxq->dev); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_alloc - Allocate a new CT entry @@ -363,12 +363,7 @@ bpf_skb_ct_alloc(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_alloc_entry(net, bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -397,11 +392,7 @@ bpf_skb_ct_lookup(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_ct_insert_entry - Add the provided entry into a CT map From 38ba6d43af3844ae502092ee9dcc47214e82acb8 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 18 Jun 2026 10:18:44 +0000 Subject: [PATCH 4442/5214] selftests/bpf: Cover small conntrack opts error writes Add a conntrack kfunc regression check for opts__sz values that do not cover opts->error. The BPF program initializes opts->error with a guard value, calls the lookup and allocation kfuncs with opts__sz set to sizeof(opts->netns_id), and verifies that the guard is still intact after the kfunc returns NULL. Without the conntrack wrapper guard, the kfunc error path overwrites that guard with -EINVAL even though the verifier checked only the first four bytes of the options object. Fixes: b4c2b9593a1c ("net/netfilter: Add unstable CT lookup helpers for XDP and TC-BPF") Fixes: d7e79c97c00c ("net: netfilter: Add kfuncs to allocate and insert CT") Signed-off-by: Yiyang Chen Link: https://lore.kernel.org/r/007dfd0341cd84560e4795a2a951cc56d4adff1d.1781765747.git.chenyy23@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov --- .../testing/selftests/bpf/prog_tests/bpf_nf.c | 6 +++++ .../testing/selftests/bpf/progs/test_bpf_nf.c | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c index b33dba4b126e..14d4c1793aed 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -5,6 +5,8 @@ #include "test_bpf_nf.skel.h" #include "test_bpf_nf_fail.skel.h" +#define CT_OPTS_ERROR_GUARD 0x12345678 + static char log_buf[1024 * 1024]; struct { @@ -119,6 +121,10 @@ static void test_bpf_nf_ct(int mode) ASSERT_EQ(skel->bss->test_einval_reserved_new, -EINVAL, "Test EINVAL for reserved in new struct not set to 0"); ASSERT_EQ(skel->bss->test_einval_netns_id, -EINVAL, "Test EINVAL for netns_id < -1"); ASSERT_EQ(skel->bss->test_einval_len_opts, -EINVAL, "Test EINVAL for len__opts != NF_BPF_CT_OPTS_SZ"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_lookup, CT_OPTS_ERROR_GUARD, + "Test no error write for lookup opts__sz before error field"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_alloc, CT_OPTS_ERROR_GUARD, + "Test no error write for alloc opts__sz before error field"); ASSERT_EQ(skel->bss->test_eproto_l4proto, -EPROTO, "Test EPROTO for l4proto != TCP or UDP"); ASSERT_EQ(skel->bss->test_enonet_netns_id, -ENONET, "Test ENONET for bad but valid netns_id"); ASSERT_EQ(skel->bss->test_enoent_lookup, -ENOENT, "Test ENOENT for failed lookup"); diff --git a/tools/testing/selftests/bpf/progs/test_bpf_nf.c b/tools/testing/selftests/bpf/progs/test_bpf_nf.c index 076fbf03a126..df43649ecb78 100644 --- a/tools/testing/selftests/bpf/progs/test_bpf_nf.c +++ b/tools/testing/selftests/bpf/progs/test_bpf_nf.c @@ -10,6 +10,8 @@ #define EINVAL 22 #define ENOENT 2 +#define CT_OPTS_ERROR_GUARD 0x12345678 + #define NF_CT_ZONE_DIR_ORIG (1 << IP_CT_DIR_ORIGINAL) #define NF_CT_ZONE_DIR_REPL (1 << IP_CT_DIR_REPLY) @@ -19,6 +21,8 @@ int test_einval_reserved = 0; int test_einval_reserved_new = 0; int test_einval_netns_id = 0; int test_einval_len_opts = 0; +int test_einval_len_opts_small_lookup = 0; +int test_einval_len_opts_small_alloc = 0; int test_eproto_l4proto = 0; int test_enonet_netns_id = 0; int test_enoent_lookup = 0; @@ -124,6 +128,28 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, else test_einval_len_opts = opts_def.error; + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + bpf_ct_release(ct); + test_einval_len_opts_small_lookup = -EINVAL; + } else { + test_einval_len_opts_small_lookup = opts_def.error; + } + + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + ct = bpf_ct_insert_entry(ct); + if (ct) + bpf_ct_release(ct); + test_einval_len_opts_small_alloc = -EINVAL; + } else { + test_einval_len_opts_small_alloc = opts_def.error; + } + opts_def.l4proto = IPPROTO_ICMP; ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); From 53442aad1d5790932ed82220bd3c9e1ee9388b83 Mon Sep 17 00:00:00 2001 From: Ziran Zhang Date: Tue, 16 Jun 2026 09:32:45 +0800 Subject: [PATCH 4443/5214] rocker: Fix memory leak in ofdpa_port_fdb() In ofdpa_port_fdb(), the hash_del() only unlinks the node from hash table, but does not free it. Fix this by adding kfree(found) after the !found == removing check, where the pointer value is no longer needed. Found by Coccinelle kfree script. Cc: # rocker is a test harness, it's never loaded on production systems Signed-off-by: Ziran Zhang Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260616013245.7098-1-zhangcoder@yeah.net Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/rocker/rocker_ofdpa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 66a8ae67c3ea..15d19a8a1710 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1924,6 +1924,9 @@ static int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port, flags |= OFDPA_OP_FLAG_REFRESH; } + if (found && removing) + kfree(found); + return ofdpa_port_fdb_learn(ofdpa_port, flags, addr, vlan_id); } From 5e0b273e0a62cc04ec338c7b502797c66c2ed42a Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 22 Jun 2026 23:01:22 +0000 Subject: [PATCH 4444/5214] bpf: Reset register bounds before narrowing retval range in check_mem_access() When the BPF verifier processes a context load of an LSM hook return value, it calls __mark_reg_s32_range() to narrow the register to the hook's valid range. However, __mark_reg_s32_range() intersects the new range with the register's existing bounds using max_t()/min_t() rather than replacing them. If the destination register carries stale bounds from a prior instruction (e.g. BPF_MOV64_IMM), the intersection can produce a range narrower than reality. The verifier then believes it knows the register's exact value, while at runtime the actual hook return value is loaded, creating a verifier/runtime mismatch that can be used to bypass BPF memory safety checks. The else branch already calls mark_reg_unknown() to reset register state before any narrowing. Apply the same reset in the is_retval path so stale bounds are cleared before __mark_reg_s32_range() intersects. Fixes: 5d99e198be27 ("bpf, lsm: Add check for BPF LSM return value") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260622230123.3695446-2-tristmd@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a2b348f98080..21a365d436a5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6201,6 +6201,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b */ if (info.reg_type == SCALAR_VALUE) { if (info.is_retval && get_func_retval_range(env->prog, &range)) { + mark_reg_unknown(env, regs, value_regno); err = __mark_reg_s32_range(env, regs, value_regno, range.minval, range.maxval); if (err) From 644332f48fc22995d056a3c6ca04dac64a74457b Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 22 Jun 2026 23:01:23 +0000 Subject: [PATCH 4445/5214] selftests/bpf: Add test for stale bounds on LSM retval context load Add a verifier test that catches the stale-bounds issue fixed in the previous patch. The test sets r6 = 0 to create known bounds, then loads the LSM hook return value into r6 from the context. Without the fix, the verifier intersects the retval range with the stale bounds and incorrectly narrows r6 to a single value, pruning the fall-through branch as dead code and missing the div-by-zero. Suggested-by: Eduard Zingerman Signed-off-by: Tristan Madani Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260622230123.3695446-3-tristmd@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/verifier_lsm.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm.c b/tools/testing/selftests/bpf/progs/verifier_lsm.c index 2f8103bfa14e..c724bf389f5c 100644 --- a/tools/testing/selftests/bpf/progs/verifier_lsm.c +++ b/tools/testing/selftests/bpf/progs/verifier_lsm.c @@ -197,4 +197,19 @@ int BPF_PROG(sleepable_lsm_cgroup) return 0; } +SEC("lsm/file_mprotect") +__description("lsm retval load must reset stale register bounds") +__failure __msg("div by zero") +__naked int retval_load_resets_bounds(void *ctx) +{ + asm volatile ( + "r6 = 0;" + "r6 = *(u64 *)(r1 + 24);" + "if r6 == 0 goto +1;" + "r6 /= 0;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 27b9daba50609335db6ca81e4cccf50ded21ec76 Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Thu, 18 Jun 2026 11:30:24 +0200 Subject: [PATCH 4446/5214] net: ethernet: ti: icssg: guard PA stat lookups icssg_ndo_get_stats64() unconditionally calls emac_get_stat_by_name() with FW PA stat names regardless of whether the PA stats block is present on the hardware. emac_get_stat_by_name() already guards the PA stats lookup with `if (emac->prueth->pa_stats)`; when that pointer is NULL the lookup falls through to netdev_err() and returns -EINVAL. Because ndo_get_stats64 is polled regularly by the networking stack this produces thousands of log entries of the form: icssg-prueth icssg1-eth end0: Invalid stats FW_RX_ERROR A secondary consequence is that the int(-EINVAL) return value is implicitly widened to a near-ULLONG_MAX unsigned value when accumulated into the __u64 fields of rtnl_link_stats64, silently corrupting the rx_errors, rx_dropped and tx_dropped counters reported by `ip -s link`. Every other PA-aware code path in the driver is already guarded with the same `if (emac->prueth->pa_stats)` check. Apply the same guard here. Fixes: 0d15a26b247d ("net: ti: icssg-prueth: Add ICSSG FW Stats") Signed-off-by: Philippe Schenker Reviewed-by: Simon Horman Cc: danishanwar@ti.com Cc: rogerq@kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618093037.3448858-1-dev@pschenker.ch Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/icssg/icssg_common.c | 49 +++++++++++--------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index 82ddef9c17d5..9c0280bedefb 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -1652,28 +1652,35 @@ void icssg_ndo_get_stats64(struct net_device *ndev, stats->rx_over_errors = emac_get_stat_by_name(emac, "rx_over_errors"); stats->multicast = emac_get_stat_by_name(emac, "rx_multicast_frames"); - stats->rx_errors = ndev->stats.rx_errors + - emac_get_stat_by_name(emac, "FW_RX_ERROR") + - emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + - emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + - emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + - emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); - stats->rx_dropped = ndev->stats.rx_dropped + - emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + - emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + - emac_get_stat_by_name(emac, "FW_INF_SAV") + - emac_get_stat_by_name(emac, "FW_INF_SA_DL") + - emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->rx_errors = ndev->stats.rx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; stats->tx_errors = ndev->stats.tx_errors; - stats->tx_dropped = ndev->stats.tx_dropped + - emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + - emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); + stats->tx_dropped = ndev->stats.tx_dropped; + + if (!emac->prueth->pa_stats) + return; + + stats->rx_errors += + emac_get_stat_by_name(emac, "FW_RX_ERROR") + + emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + + emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + + emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + + emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); + stats->rx_dropped += + emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + + emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + + emac_get_stat_by_name(emac, "FW_INF_SAV") + + emac_get_stat_by_name(emac, "FW_INF_SA_DL") + + emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->tx_dropped += + emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + + emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); } EXPORT_SYMBOL_GPL(icssg_ndo_get_stats64); From c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 15 Jun 2026 10:00:00 +0900 Subject: [PATCH 4447/5214] ksmbd: track the connection owning a byte-range lock SMB2_LOCK adds each granted byte-range lock to both the file lock list and the lock list of the connection which handled the request. The final close and durable handle paths, however, remove the connection list entry while holding fp->conn->llist_lock. With SMB3 multichannel, the connection handling the LOCK request can be different from the connection which opened the file. The entry can therefore be removed under a different spinlock from the one protecting the list it belongs to. A concurrent traversal can then access freed struct ksmbd_lock and struct file_lock objects. Record the connection owning each lock's clist entry and hold a reference to it while the entry is linked. Use that connection and its llist_lock for unlock, rollback, close, and durable preserve. Durable reconnect assigns the new connection as the owner when publishing the locks again. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 10 ++++++++-- fs/smb/server/vfs_cache.c | 23 +++++++++++++++++------ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c1c6c1a64f60..ba24040d05eb 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7774,9 +7774,11 @@ int smb2_lock(struct ksmbd_work *work) nolock = 0; list_del(&cmp_lock->flist); list_del(&cmp_lock->clist); + cmp_lock->conn = NULL; spin_unlock(&conn->llist_lock); up_read(&conn_list_lock); + ksmbd_conn_put(conn); locks_free_lock(cmp_lock->fl); kfree(cmp_lock); goto out_check_cl; @@ -7911,6 +7913,7 @@ int smb2_lock(struct ksmbd_work *work) goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); + smb_lock->conn = ksmbd_conn_get(work->conn); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); @@ -7965,11 +7968,14 @@ int smb2_lock(struct ksmbd_work *work) } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); + conn = smb_lock->conn; + spin_lock(&conn->llist_lock); if (!list_empty(&smb_lock->flist)) list_del(&smb_lock->flist); list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); locks_free_lock(smb_lock->fl); if (rlock) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 8c556e46cc10..39c56942ae44 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -484,10 +484,14 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) * there are not accesses to fp->lock_list. */ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - if (!list_empty(&smb_lock->clist) && fp->conn) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + struct ksmbd_conn *conn = smb_lock->conn; + + if (conn) { + spin_lock(&conn->llist_lock); + list_del_init(&smb_lock->clist); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); } list_del(&smb_lock->flist); @@ -1303,9 +1307,15 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&conn->llist_lock); + struct ksmbd_conn *lock_conn = smb_lock->conn; + + if (!lock_conn) + continue; + spin_lock(&lock_conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&lock_conn->llist_lock); + ksmbd_conn_put(lock_conn); } fp->conn = NULL; @@ -1435,6 +1445,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } list_for_each_entry(smb_lock, &fp->lock_list, flist) { + smb_lock->conn = ksmbd_conn_get(conn); spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); spin_unlock(&conn->llist_lock); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 7d547e1a74f7..a3a9fda6de91 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -32,6 +32,7 @@ struct ksmbd_session; struct ksmbd_lock { struct file_lock *fl; + struct ksmbd_conn *conn; struct list_head clist; struct list_head flist; struct list_head llist; From 37ee476071bcbf4fa97a5a7b208b3a51073b4fcc Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Wed, 17 Jun 2026 02:36:32 +0000 Subject: [PATCH 4448/5214] smb/server: fix debug log endianness in smb2_cancel() Convert to CPU byte order to avoid incorrect debug log on big-endian architectures. Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index ba24040d05eb..95c7b09c6743 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7459,7 +7459,8 @@ int smb2_cancel(struct ksmbd_work *work) hdr = ksmbd_resp_buf_next(work); ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", - hdr->MessageId, hdr->Flags); + le64_to_cpu(hdr->MessageId), + le32_to_cpu(hdr->Flags)); if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { command_list = &conn->async_requests; From b69be2c58615950ee7353b61a21acdf8508c0cbb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:32:44 +0900 Subject: [PATCH 4449/5214] ksmbd: validate SMB2 lease create contexts Validate SMB2 lease context lengths, requested lease state bits, and v2 flags before using the context. Return errors via ERR_PTR so CREATE can distinguish a missing lease context from a malformed one. Also ignore lease v2 contexts for SMB 2.1, where they are not valid. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 33 ++++++++++++++++++++++++++++----- fs/smb/server/smb2pdu.c | 24 +++++++++++++++++++++++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 60e7e821c245..5c6c0550a477 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -5,6 +5,7 @@ */ #include +#include #include "glob.h" #include "oplock.h" @@ -19,6 +20,20 @@ static LIST_HEAD(lease_table_list); static DEFINE_RWLOCK(lease_list_lock); +#define SMB2_LEASE_STATE_MASK_LE (SMB2_LEASE_READ_CACHING_LE | \ + SMB2_LEASE_HANDLE_CACHING_LE | \ + SMB2_LEASE_WRITE_CACHING_LE) + +static bool lease_state_valid(__le32 state) +{ + return !(state & ~SMB2_LEASE_STATE_MASK_LE); +} + +static bool lease_v2_flags_valid(__le32 flags) +{ + return !(flags & ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE); +} + /** * alloc_opinfo() - allocate a new opinfo object for oplock info * @work: smb work @@ -1537,12 +1552,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req) struct lease_ctx_info *lreq; cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); - if (IS_ERR_OR_NULL(cc)) + if (IS_ERR(cc)) + return ERR_CAST(cc); + if (!cc) return NULL; lreq = kzalloc_obj(struct lease_ctx_info, KSMBD_DEFAULT_GFP); if (!lreq) - return NULL; + return ERR_PTR(-ENOMEM); if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; @@ -1556,11 +1573,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req) lreq->flags = lc->lcontext.LeaseFlags; lreq->epoch = lc->lcontext.Epoch; lreq->duration = lc->lcontext.LeaseDuration; + if (!lease_state_valid(lreq->req_state) || + !lease_v2_flags_valid(lreq->flags)) + goto err_out; if (lreq->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, SMB2_LEASE_KEY_SIZE); lreq->version = 2; - } else { + } else if (sizeof(struct lease_context) == le32_to_cpu(cc->DataLength)) { struct create_lease *lc = (struct create_lease *)cc; if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) < @@ -1571,12 +1591,15 @@ struct lease_ctx_info *parse_lease_state(void *open_req) lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->duration = lc->lcontext.LeaseDuration; + if (!lease_state_valid(lreq->req_state)) + goto err_out; lreq->version = 1; - } + } else + goto err_out; return lreq; err_out: kfree(lreq); - return NULL; + return ERR_PTR(-EINVAL); } /** diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 95c7b09c6743..19e819898fd0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3108,6 +3108,17 @@ int smb2_open(struct ksmbd_work *work) if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && req->CreateContextsOffset) { lc = parse_lease_state(req); + if (IS_ERR(lc)) { + rc = PTR_ERR(lc); + lc = NULL; + goto err_out2; + } + if (lc && lc->version == 2 && conn->dialect < SMB30_PROT_ID) { + kfree(lc); + lc = NULL; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + } rc = parse_durable_handle_context(work, req, lc, &dh_info); if (rc) { ksmbd_debug(SMB, "error parsing durable handle context\n"); @@ -3139,8 +3150,19 @@ int smb2_open(struct ksmbd_work *work) goto reconnected_fp; } - } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { lc = parse_lease_state(req); + if (IS_ERR(lc)) { + rc = PTR_ERR(lc); + lc = NULL; + goto err_out2; + } + if (lc && lc->version == 2 && conn->dialect < SMB30_PROT_ID) { + kfree(lc); + lc = NULL; + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + } + } if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { pr_err("Invalid impersonationlevel : 0x%x\n", From fa111daae1a02dbff5693dfc12f368bccd9eb5f4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:33:20 +0900 Subject: [PATCH 4450/5214] ksmbd: use connection ClientGUID for lease lookup MS-SMB2 defines the lease table lookup key as Connection.ClientGuid. Use the connection ClientGUID consistently when checking for same-client leases and duplicate lease keys. Also preserve directory and parent lease metadata when copying an existing lease state to a new open. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 16 +++++++++------- fs/smb/server/oplock.h | 2 +- fs/smb/server/smb2pdu.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 5c6c0550a477..627cea7fd7ea 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -520,7 +520,7 @@ static inline int compare_guid_key(struct oplock_info *opinfo, * Return: oplock(lease) object on success, otherwise NULL */ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, + const char *client_guid, struct lease_ctx_info *lctx) { int ret; @@ -1012,7 +1012,7 @@ void destroy_lease_table(struct ksmbd_conn *conn) write_unlock(&lease_list_lock); } -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, +int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, struct lease_ctx_info *lctx) { struct oplock_info *opinfo; @@ -1029,7 +1029,7 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, } list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, sess->ClientGUID, + if (!memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) goto found; } @@ -1045,7 +1045,7 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, rcu_read_unlock(); if (opinfo->o_fp->f_ci == ci) goto op_next; - err = compare_guid_key(opinfo, sess->ClientGUID, + err = compare_guid_key(opinfo, conn->ClientGUID, lctx->lease_key); if (err) { err = -EINVAL; @@ -1078,6 +1078,9 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) lease2->flags = lease1->flags; lease2->epoch = lease1->epoch; lease2->version = lease1->version; + lease2->is_dir = lease1->is_dir; + memcpy(lease2->parent_lease_key, lease1->parent_lease_key, + SMB2_LEASE_KEY_SIZE); } static void add_lease_global_list(struct oplock_info *opinfo, @@ -1216,7 +1219,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct ksmbd_file *fp, __u16 tid, struct lease_ctx_info *lctx, int share_ret) { - struct ksmbd_session *sess = work->sess; int err = 0; struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; struct ksmbd_inode *ci = fp->f_ci; @@ -1259,12 +1261,12 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct oplock_info *m_opinfo; /* is lease already granted ? */ - m_opinfo = same_client_has_lease(ci, sess->ClientGUID, + m_opinfo = same_client_has_lease(ci, work->conn->ClientGUID, lctx); if (m_opinfo) { copy_lease(m_opinfo, opinfo); if (atomic_read(&m_opinfo->breaking_cnt)) - opinfo->o_lease->flags = + opinfo->o_lease->flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; opinfo_put(m_opinfo); goto out; diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index d91a8266e065..795a9119dad9 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -116,7 +116,7 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key); -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, +int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, struct lease_ctx_info *lctx); void destroy_lease_table(struct ksmbd_conn *conn); void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 19e819898fd0..0766f0d662be 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3637,7 +3637,7 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", name, req_op_level, lc->req_state); - rc = find_same_lease_key(sess, fp->f_ci, lc); + rc = find_same_lease_key(conn, fp->f_ci, lc); if (rc) goto err_out1; } else if (open_flags == O_RDONLY && From 5015191096db311759fef98769270336cd8b1324 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:33:53 +0900 Subject: [PATCH 4451/5214] ksmbd: fix lease break and ack state handling Do not skip valid lease states containing WRITE_CACHING when breaking level-II/read leases for writes and truncates. Handle lease break acknowledgments according to the SMB2 rule that the acknowledged state must be a subset of the server's break target. Apply the acknowledged state directly and keep the break pending on failed ACKs. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 24 +++------ fs/smb/server/smb2pdu.c | 106 ++++++++++------------------------------ 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 627cea7fd7ea..cc5eb95ab3f2 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1419,14 +1419,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, continue; } - if (brk_op->is_lease && (brk_op->o_lease->state & - (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)))) { - ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", - brk_op->o_lease->state); - goto next; - } else if (brk_op->level != - SMB2_OPLOCK_LEVEL_II) { + if (!brk_op->is_lease && + brk_op->level != SMB2_OPLOCK_LEVEL_II) { ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", brk_op->level); goto next; @@ -1478,15 +1472,13 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) */ __u8 smb2_map_lease_to_oplock(__le32 lease_state) { - if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE)) { + if ((lease_state & SMB2_LEASE_WRITE_CACHING_LE) && + (lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) { return SMB2_OPLOCK_LEVEL_BATCH; - } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && - lease_state & SMB2_LEASE_WRITE_CACHING_LE) { - if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) - return SMB2_OPLOCK_LEVEL_EXCLUSIVE; - } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + } else if (lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) { return SMB2_OPLOCK_LEVEL_II; } return 0; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 0766f0d662be..75ee9184e553 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9013,16 +9013,17 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) ksmbd_fd_put(work, fp); } +static bool smb2_lease_state_valid(__le32 state) +{ + return !(state & ~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)); +} + static int check_lease_state(struct lease *lease, __le32 req_state) { - if ((lease->new_state == - (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && - !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { - lease->new_state = req_state; - return 0; - } - - if (lease->new_state == req_state) + if (smb2_lease_state_valid(req_state) && + !(req_state & ~lease->new_state)) return 0; return 1; @@ -9040,9 +9041,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) struct smb2_lease_ack *req; struct smb2_lease_ack *rsp; struct oplock_info *opinfo; - __le32 err = 0; int ret = 0; - unsigned int lease_change_type; __le32 lease_state; struct lease *lease; @@ -9066,6 +9065,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) goto err_out; } + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + if (check_lease_state(lease, req->LeaseState)) { rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; ksmbd_debug(OPLOCK, @@ -9074,72 +9078,10 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) goto err_out; } - if (!atomic_read(&opinfo->breaking_cnt)) { - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - /* check for bad lease state */ - if (req->LeaseState & - (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && - req->LeaseState != SMB2_LEASE_NONE_LE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else { - /* valid lease state changes */ - err = STATUS_INVALID_DEVICE_STATE; - if (req->LeaseState == SMB2_LEASE_NONE_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_READ; - else - lease_change_type = OPLOCK_READ_HANDLE_TO_READ; - } else { - lease_change_type = 0; - } - } - - switch (lease_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - break; - case OPLOCK_READ_HANDLE_TO_READ: - ret = opinfo_read_handle_to_read(opinfo); - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - break; - default: - ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } - - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } - - lease_state = lease->state; + lease_state = req->LeaseState; + lease->state = lease_state; + lease->new_state = SMB2_LEASE_NONE_LE; + opinfo->level = smb2_map_lease_to_oplock(lease_state); rsp->StructureSize = cpu_to_le16(36); rsp->Reserved = 0; @@ -9148,16 +9090,20 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); - if (ret) { -err_out: - smb2_set_err_rsp(work); - } + if (ret) + goto err_out; opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); atomic_dec(&opinfo->breaking_cnt); wake_up_interruptible_all(&opinfo->oplock_brk); opinfo_put(opinfo); + return; + +err_out: + smb2_set_err_rsp(work); + opinfo_put(opinfo); + return; } /** From 0fa8abc6ab6dcc571f76c1e05ad3582540fb56d5 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:34:24 +0900 Subject: [PATCH 4452/5214] ksmbd: clean up lease response flags and directory leases Do not echo reserved v1 lease flags back to clients. For lease v2 responses, only return BREAK_IN_PROGRESS and PARENT_LEASE_KEY_SET when they are meaningful, and preserve the parent lease key in the response. Allow directory leases whenever the request is a valid lease v2 request, and initialize v2 lease epochs from the first server-granted state change. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index cc5eb95ab3f2..8ae6a6afbe04 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -34,6 +34,11 @@ static bool lease_v2_flags_valid(__le32 flags) return !(flags & ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE); } +static bool lease_has_parent_key(struct lease *lease) +{ + return lease->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; +} + /** * alloc_opinfo() - allocate a new opinfo object for oplock info * @work: smb work @@ -126,7 +131,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) lease->is_dir = lctx->is_dir; memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); lease->version = lctx->version; - lease->epoch = le16_to_cpu(lctx->epoch) + 1; + lease->epoch = lctx->version == 2 ? 1 : 0; INIT_LIST_HEAD(&opinfo->lease_entry); opinfo->o_lease = lease; @@ -1228,9 +1233,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, /* Only v2 leases handle the directory */ if (S_ISDIR(file_inode(fp->filp)->i_mode)) { - if (!lctx || lctx->version != 2 || - (lctx->flags != SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE && - !lctx->epoch)) + if (!lctx || lctx->version != 2) return 0; } @@ -1493,14 +1496,17 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) { if (lease->version == 2) { struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + __le32 flags = lease->flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; memset(buf, 0, sizeof(struct create_lease_v2)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; + if (lease_has_parent_key(lease)) + flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; + buf->lcontext.LeaseFlags = flags; buf->lcontext.Epoch = cpu_to_le16(lease->epoch); buf->lcontext.LeaseState = lease->state; - if (lease->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) + if (lease_has_parent_key(lease)) memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, SMB2_LEASE_KEY_SIZE); buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -1518,7 +1524,8 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) memset(buf, 0, sizeof(struct create_lease)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseFlags = + lease->flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; buf->lcontext.LeaseState = lease->state; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease, lcontext)); @@ -1583,7 +1590,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; + lreq->flags = 0; lreq->duration = lc->lcontext.LeaseDuration; if (!lease_state_valid(lreq->req_state)) goto err_out; From 079927f5fda5a05355a620d794e2f4b9eb1f70fd Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:35:06 +0900 Subject: [PATCH 4453/5214] ksmbd: share SMB2 lease state across opens Model SMB2 leases as per-client/per-key objects instead of keeping a separate lease copy in every oplock_info. The lease table now stores lease objects and each lease tracks the opens that reference it. This makes same ClientGuid/LeaseKey opens observe a single lease state, so lease upgrades, breaks, ACKs, and close teardown do not diverge across per-open copies. Keep one reference for the lease table entry and one reference for each open, and remove the table entry when the last open is detached. Update lease break ACK handling to refresh all open oplock levels from the shared lease state. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 262 ++++++++++++++++++++++------------------ fs/smb/server/oplock.h | 6 + fs/smb/server/smb2pdu.c | 2 +- 3 files changed, 151 insertions(+), 119 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 8ae6a6afbe04..81716317bb61 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -65,6 +65,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, opinfo->fid = id; opinfo->Tid = Tid; INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->lease_entry); init_waitqueue_head(&opinfo->oplock_q); init_waitqueue_head(&opinfo->oplock_brk); atomic_set(&opinfo->refcount, 1); @@ -73,31 +74,44 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, return opinfo; } -static void lease_add_list(struct oplock_info *opinfo) +static void lease_get(struct lease *lease) { - struct lease_table *lb = opinfo->o_lease->l_lb; + atomic_inc(&lease->refcount); +} +static void lease_put(struct lease *lease) +{ + if (lease && atomic_dec_and_test(&lease->refcount)) + kfree(lease); +} + +static void lease_add_table(struct lease *lease, struct lease_table *lb) +{ + lease_get(lease); + lease->l_lb = lb; spin_lock(&lb->lb_lock); - list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + list_add_rcu(&lease->l_entry, &lb->lease_list); spin_unlock(&lb->lb_lock); } -static void lease_del_list(struct oplock_info *opinfo) +static void lease_del_table(struct lease *lease) { - struct lease_table *lb = opinfo->o_lease->l_lb; + struct lease_table *lb = lease->l_lb; if (!lb) return; spin_lock(&lb->lb_lock); - if (list_empty(&opinfo->lease_entry)) { + if (list_empty(&lease->l_entry)) { spin_unlock(&lb->lb_lock); return; } - list_del_init(&opinfo->lease_entry); - opinfo->o_lease->l_lb = NULL; + list_del_init(&lease->l_entry); + lease->l_lb = NULL; spin_unlock(&lb->lb_lock); + + lease_put(lease); } static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) @@ -115,13 +129,14 @@ static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) return lb; } -static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +static struct lease *alloc_lease(struct lease_ctx_info *lctx, + struct ksmbd_inode *ci) { struct lease *lease; lease = kmalloc_obj(struct lease, KSMBD_DEFAULT_GFP); if (!lease) - return -ENOMEM; + return NULL; memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); lease->state = lctx->req_state; @@ -132,18 +147,48 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); lease->version = lctx->version; lease->epoch = lctx->version == 2 ? 1 : 0; - INIT_LIST_HEAD(&opinfo->lease_entry); - opinfo->o_lease = lease; + lease->ci = ci; + lease->l_lb = NULL; + INIT_LIST_HEAD(&lease->l_entry); + INIT_LIST_HEAD(&lease->open_list); + spin_lock_init(&lease->lock); + atomic_set(&lease->refcount, 1); - return 0; + return lease; +} + +static void lease_add_open(struct lease *lease, struct oplock_info *opinfo) +{ + spin_lock(&lease->lock); + list_add(&opinfo->lease_entry, &lease->open_list); + spin_unlock(&lease->lock); +} + +static void lease_del_open(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + bool remove_table = false; + + if (!lease) + return; + + spin_lock(&lease->lock); + if (!list_empty(&opinfo->lease_entry)) { + list_del_init(&opinfo->lease_entry); + remove_table = list_empty(&lease->open_list); + } + spin_unlock(&lease->lock); + + if (remove_table) { + write_lock(&lease_list_lock); + lease_del_table(lease); + write_unlock(&lease_list_lock); + } } static void free_lease(struct oplock_info *opinfo) { - struct lease *lease; - - lease = opinfo->o_lease; - kfree(lease); + lease_put(opinfo->o_lease); } static void __free_opinfo(struct oplock_info *opinfo) @@ -166,6 +211,21 @@ static void free_opinfo(struct oplock_info *opinfo) call_rcu(&opinfo->rcu, free_opinfo_rcu); } +void lease_update_oplock_levels(struct lease *lease) +{ + struct oplock_info *opinfo; + __u8 level; + + if (!lease) + return; + + level = smb2_map_lease_to_oplock(lease->state); + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) + opinfo->level = level; + spin_unlock(&lease->lock); +} + struct oplock_info *opinfo_get(struct ksmbd_file *fp) { struct oplock_info *opinfo; @@ -226,11 +286,9 @@ static void opinfo_del(struct oplock_info *opinfo) { struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - if (opinfo->is_lease) { - write_lock(&lease_list_lock); - lease_del_list(opinfo); - write_unlock(&lease_list_lock); - } + if (opinfo->is_lease) + lease_del_open(opinfo); + down_write(&ci->m_lock); list_del(&opinfo->op_entry); up_write(&ci->m_lock); @@ -279,8 +337,10 @@ int opinfo_write_to_read(struct oplock_info *opinfo) } opinfo->level = SMB2_OPLOCK_LEVEL_II; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -295,7 +355,7 @@ int opinfo_read_handle_to_read(struct oplock_info *opinfo) struct lease *lease = opinfo->o_lease; lease->state = lease->new_state; - opinfo->level = SMB2_OPLOCK_LEVEL_II; + lease_update_oplock_levels(lease); return 0; } @@ -317,8 +377,10 @@ int opinfo_write_to_none(struct oplock_info *opinfo) return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -339,8 +401,10 @@ int opinfo_read_to_none(struct oplock_info *opinfo) return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -361,10 +425,7 @@ int lease_read_to_write(struct oplock_info *opinfo) lease->new_state = SMB2_LEASE_NONE_LE; lease->state |= SMB2_LEASE_WRITE_CACHING_LE; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + lease_update_oplock_levels(lease); return 0; } @@ -386,15 +447,7 @@ static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) lease->new_state = SMB2_LEASE_NONE_LE; lease->state = new_state; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_II; - else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - else if (lease->state & SMB2_LEASE_READ_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_II; + lease_update_oplock_levels(lease); return 0; } @@ -577,6 +630,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, SMB2_LEASE_HANDLE_CACHING_LE)) { lease->epoch++; lease->state = lctx->req_state; + lease_update_oplock_levels(lease); } } @@ -603,8 +657,10 @@ static void wait_for_break_ack(struct oplock_info *opinfo) /* is this a timeout ? */ if (!rc) { - if (opinfo->is_lease) + if (opinfo->is_lease) { opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + lease_update_oplock_levels(opinfo->o_lease); + } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; } @@ -884,8 +940,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) } else { __smb2_lease_break_noti(&work->work); if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + lease_update_oplock_levels(opinfo->o_lease); } } return 0; @@ -990,7 +1046,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, void destroy_lease_table(struct ksmbd_conn *conn) { struct lease_table *lb, *lbtmp; - struct oplock_info *opinfo; + struct lease *lease, *ltmp; write_lock(&lease_list_lock); if (list_empty(&lease_table_list)) { @@ -1002,15 +1058,8 @@ void destroy_lease_table(struct ksmbd_conn *conn) if (conn && memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) continue; -again: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { - rcu_read_unlock(); - lease_del_list(opinfo); - goto again; - } - rcu_read_unlock(); + list_for_each_entry_safe(lease, ltmp, &lb->lease_list, l_entry) + lease_del_table(lease); list_del(&lb->l_entry); kfree(lb); } @@ -1020,7 +1069,7 @@ void destroy_lease_table(struct ksmbd_conn *conn) int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, struct lease_ctx_info *lctx) { - struct oplock_info *opinfo; + struct lease *lease; int err = 0; struct lease_table *lb; @@ -1043,70 +1092,40 @@ int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, return 0; found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + list_for_each_entry(lease, &lb->lease_list, l_entry) { + if (lease->ci == ci) continue; - rcu_read_unlock(); - if (opinfo->o_fp->f_ci == ci) - goto op_next; - err = compare_guid_key(opinfo, conn->ClientGUID, - lctx->lease_key); - if (err) { + if (!memcmp(lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { err = -EINVAL; ksmbd_debug(OPLOCK, "found same lease key is already used in other files\n"); - opinfo_put(opinfo); goto out; } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); } - rcu_read_unlock(); out: read_unlock(&lease_list_lock); return err; } -static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) -{ - struct lease *lease1 = op1->o_lease; - struct lease *lease2 = op2->o_lease; - - op2->level = op1->level; - lease2->state = lease1->state; - memcpy(lease2->lease_key, lease1->lease_key, - SMB2_LEASE_KEY_SIZE); - lease2->duration = lease1->duration; - lease2->flags = lease1->flags; - lease2->epoch = lease1->epoch; - lease2->version = lease1->version; - lease2->is_dir = lease1->is_dir; - memcpy(lease2->parent_lease_key, lease1->parent_lease_key, - SMB2_LEASE_KEY_SIZE); -} - -static void add_lease_global_list(struct oplock_info *opinfo, +static void add_lease_global_list(struct lease *lease, struct ksmbd_conn *conn, struct lease_table *new_lb) { struct lease_table *lb; write_lock(&lease_list_lock); list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + if (!memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); + lease_add_table(lease, lb); write_unlock(&lease_list_lock); kfree(new_lb); return; } } - opinfo->o_lease->l_lb = new_lb; - lease_add_list(opinfo); + lease_add_table(lease, new_lb); list_add(&new_lb->l_entry, &lease_table_list); write_unlock(&lease_list_lock); } @@ -1229,6 +1248,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct ksmbd_inode *ci = fp->f_ci; struct lease_table *new_lb = NULL; bool prev_op_has_lease; + bool new_lease = false; __le32 prev_op_state = 0; /* Only v2 leases handle the directory */ @@ -1242,10 +1262,13 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, return -ENOMEM; if (lctx) { - err = alloc_lease(opinfo, lctx); - if (err) + opinfo->o_lease = alloc_lease(lctx, ci); + if (!opinfo->o_lease) { + err = -ENOMEM; goto err_out; + } opinfo->is_lease = 1; + new_lease = true; } /* ci does not have any oplock */ @@ -1267,7 +1290,11 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, m_opinfo = same_client_has_lease(ci, work->conn->ClientGUID, lctx); if (m_opinfo) { - copy_lease(m_opinfo, opinfo); + lease_put(opinfo->o_lease); + lease_get(m_opinfo->o_lease); + opinfo->o_lease = m_opinfo->o_lease; + opinfo->level = m_opinfo->level; + new_lease = false; if (atomic_read(&m_opinfo->breaking_cnt)) opinfo->o_lease->flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; @@ -1330,17 +1357,13 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, out: /* - * Set o_fp before any publication so that concurrent readers - * (e.g. find_same_lease_key() on the lease list) that - * dereference opinfo->o_fp don't hit a NULL pointer. - * * Keep the original publication order so concurrent opens can * still observe the in-flight grant via ci->m_op_list, but make * everything after opinfo_add() no-fail by preallocating any new * lease_table first. */ opinfo->o_fp = fp; - if (opinfo->is_lease) { + if (new_lease) { new_lb = alloc_lease_table(opinfo); if (!new_lb) { err = -ENOMEM; @@ -1351,8 +1374,10 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, opinfo_count_inc(fp); opinfo_add(opinfo, fp); + if (new_lease) + add_lease_global_list(opinfo->o_lease, opinfo->conn, new_lb); if (opinfo->is_lease) - add_lease_global_list(opinfo, new_lb); + lease_add_open(opinfo->o_lease, opinfo); rcu_assign_pointer(fp->f_opinfo, opinfo); @@ -1834,8 +1859,8 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key) { struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease *lease; struct lease_table *lt; - int ret; read_lock(&lease_list_lock); list_for_each_entry(lt, &lease_table_list, l_entry) { @@ -1848,29 +1873,30 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, return NULL; found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + list_for_each_entry(lease, <->lease_list, l_entry) { + if (memcmp(lease->lease_key, lease_key, SMB2_LEASE_KEY_SIZE)) continue; - rcu_read_unlock(); - if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) - goto op_next; - if (!(opinfo->o_lease->state & - (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE))) - goto op_next; - ret = compare_guid_key(opinfo, conn->ClientGUID, - lease_key); - if (ret) { - ksmbd_debug(OPLOCK, "found opinfo\n"); + if (!(lease->state & (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + break; + + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) { + if (!opinfo->op_state || + opinfo->op_state == OPLOCK_CLOSING) + continue; + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; ret_op = opinfo; + break; + } + spin_unlock(&lease->lock); + if (ret_op) { + ksmbd_debug(OPLOCK, "found opinfo\n"); goto out; } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); + break; } - rcu_read_unlock(); out: read_unlock(&lease_list_lock); diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 795a9119dad9..30bad6d65048 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -49,7 +49,12 @@ struct lease { int version; unsigned short epoch; bool is_dir; + struct ksmbd_inode *ci; struct lease_table *l_lb; + struct list_head l_entry; + struct list_head open_list; + spinlock_t lock; + atomic_t refcount; }; struct oplock_info { @@ -105,6 +110,7 @@ void opinfo_put(struct oplock_info *opinfo); void create_lease_buf(u8 *rbuf, struct lease *lease); struct lease_ctx_info *parse_lease_state(void *open_req); __u8 smb2_map_lease_to_oplock(__le32 lease_state); +void lease_update_oplock_levels(struct lease *lease); int lease_read_to_write(struct oplock_info *opinfo); /* Durable related functions */ diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 75ee9184e553..f3a57a32c4a8 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9081,7 +9081,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) lease_state = req->LeaseState; lease->state = lease_state; lease->new_state = SMB2_LEASE_NONE_LE; - opinfo->level = smb2_map_lease_to_oplock(lease_state); + lease_update_oplock_levels(lease); rsp->StructureSize = cpu_to_le16(36); rsp->Reserved = 0; From 80a56d4a826c6c84430286fcf7d8655f7c5b0868 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:35:42 +0900 Subject: [PATCH 4454/5214] ksmbd: align SMB2 oplock break ack handling Handle SMB2 oplock break acknowledgments according to the server-side validation rules in MS-SMB2. Return STATUS_INVALID_DEVICE_STATE when an ACK arrives while the open is not breaking, reject SMB2_OPLOCK_LEVEL_LEASE with STATUS_INVALID_PARAMETER, allow BATCH acknowledgments to EXCLUSIVE, and make invalid ACK levels fail with STATUS_INVALID_OPLOCK_PROTOCOL after lowering the oplock to NONE. Update the successful response from the final granted oplock level instead of relying on the oplock transition helpers, which could turn invalid ACKs into successful responses. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 108 ++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f3a57a32c4a8..b84062b16a75 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8900,11 +8900,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) struct smb2_oplock_break *rsp; struct ksmbd_file *fp; struct oplock_info *opinfo = NULL; - __le32 err = 0; - int ret = 0; + __le32 status = STATUS_SUCCESS; + int ret; u64 volatile_id, persistent_id; char req_oplevel = 0, rsp_oplevel = 0; - unsigned int oplock_change_type; WORK_BUFFERS(work, req, rsp); @@ -8930,70 +8929,54 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) return; } + if (opinfo->op_state != OPLOCK_ACK_WAIT) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", + opinfo->op_state); + status = STATUS_INVALID_DEVICE_STATE; + goto err_out; + } + + if (req_oplevel == SMB2_OPLOCK_LEVEL_LEASE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_PARAMETER; + goto err_out; + } + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + status = STATUS_INVALID_OPLOCK_PROTOCOL; goto err_out; } - if (opinfo->op_state == OPLOCK_STATE_NONE) { - ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; + if (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE && + req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; goto err_out; } - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - (req_oplevel != SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_READ_TO_NONE; - } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_DEVICE_STATE; - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_II) { - oplock_change_type = OPLOCK_WRITE_TO_READ; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_READ_TO_NONE; - } else { - oplock_change_type = 0; - } - } else { - oplock_change_type = 0; + if (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH && + req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE && + req_oplevel != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; } - switch (oplock_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_II; - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (req_oplevel == SMB2_OPLOCK_LEVEL_EXCLUSIVE) rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - default: - pr_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); - } + else + rsp_oplevel = req_oplevel; - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } + opinfo->level = rsp_oplevel; rsp->StructureSize = cpu_to_le16(24); rsp->OplockLevel = rsp_oplevel; @@ -9002,11 +8985,16 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp->VolatileFid = volatile_id; rsp->PersistentFid = persistent_id; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); - if (ret) { -err_out: - smb2_set_err_rsp(work); - } + if (ret) + ksmbd_debug(SMB, "failed to pin oplock break response: %d\n", + ret); + goto out; +err_out: + rsp->hdr.Status = status; + smb2_set_err_rsp(work); + +out: opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); opinfo_put(opinfo); From 171b5d72dd80f99271c073c6e38d5263687c3b6d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:36:17 +0900 Subject: [PATCH 4455/5214] ksmbd: treat unnamed DATA stream as base file The SMB path suffix :: names the unnamed data stream of the base file, not an alternate data stream backed by a DosStream xattr. Canonicalize an empty stream name with an explicit type to a NULL stream name after parsing. This keeps the base filename produced by strsep() and lets open continue through the normal base-file path instead of looking for a non-existent empty stream xattr. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/misc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index 966004c414a8..2dd91c9e956a 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -121,7 +121,9 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) char *stream_type; char *s_name; int rc = 0; + bool has_stream_type = false; + *stream_name = NULL; s_name = filename; filename = strsep(&s_name, ":"); ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); @@ -137,14 +139,20 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, stream_type); - if (!strncasecmp("$data", stream_type, 5)) + if (!strncasecmp("$data", stream_type, 5)) { *s_type = DATA_STREAM; - else if (!strncasecmp("$index_allocation", stream_type, 17)) + has_stream_type = true; + } else if (!strncasecmp("$index_allocation", stream_type, 17)) { *s_type = DIR_STREAM; - else + has_stream_type = true; + } else { rc = -ENOENT; + } } + if (has_stream_type && !s_name[0] && *s_type == DATA_STREAM) + goto out; + *stream_name = s_name; out: return rc; From 890825ed5c427fcb542ae1a0fd7495e551dcacaf Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 10:36:59 +0900 Subject: [PATCH 4456/5214] ksmbd: compute lease break-in-progress flag on response SMB2_LEASE_FLAG_BREAK_IN_PROGRESS is a transient create response flag, not persistent lease state. Do not store the flag in lease->flags when a same-key open is granted during a pending break. Instead, derive it from lease opens that are still waiting for a break ACK while building the lease create response, and keep lease->flags for persistent lease flags such as the parent lease key. This clears the flag naturally after the break ACK completes and fixes reopen responses that report BREAK_IN_PROGRESS after the lease is no longer breaking. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 81716317bb61..b1740fe988e6 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -39,6 +39,23 @@ static bool lease_has_parent_key(struct lease *lease) return lease->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; } +static bool lease_break_in_progress(struct lease *lease) +{ + struct oplock_info *opinfo; + bool ret = false; + + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) { + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + ret = true; + break; + } + } + spin_unlock(&lease->lock); + + return ret; +} + /** * alloc_opinfo() - allocate a new opinfo object for oplock info * @work: smb work @@ -1292,15 +1309,12 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (m_opinfo) { lease_put(opinfo->o_lease); lease_get(m_opinfo->o_lease); - opinfo->o_lease = m_opinfo->o_lease; - opinfo->level = m_opinfo->level; - new_lease = false; - if (atomic_read(&m_opinfo->breaking_cnt)) - opinfo->o_lease->flags |= - SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; - opinfo_put(m_opinfo); - goto out; - } + opinfo->o_lease = m_opinfo->o_lease; + opinfo->level = m_opinfo->level; + new_lease = false; + opinfo_put(m_opinfo); + goto out; + } } prev_opinfo = opinfo_get_list(ci); if (!prev_opinfo || @@ -1521,13 +1535,15 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) { if (lease->version == 2) { struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; - __le32 flags = lease->flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + __le32 flags = 0; memset(buf, 0, sizeof(struct create_lease_v2)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); if (lease_has_parent_key(lease)) flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; + if (lease_break_in_progress(lease)) + flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; buf->lcontext.LeaseFlags = flags; buf->lcontext.Epoch = cpu_to_le16(lease->epoch); buf->lcontext.LeaseState = lease->state; @@ -1549,8 +1565,9 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) memset(buf, 0, sizeof(struct create_lease)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = - lease->flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + if (lease_break_in_progress(lease)) + buf->lcontext.LeaseFlags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; buf->lcontext.LeaseState = lease->state; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease, lcontext)); From 7efb3f2458a8d260ce6ab37807f4b2a926483f76 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 23 Jun 2026 08:28:55 +0900 Subject: [PATCH 4457/5214] ksmbd: chain pending lease breaks before waking waiters A pending open can require more than one lease break before the existing lease becomes compatible with the operation that triggered the break. smb2.lease.breaking3 expects the server to hold the pending normal open through RWH->RH and RH->R, while a later overwrite waiter must not collapse that second break directly to RH->NONE. Keep pending_break held for lease breaks until the current triggering operation is compatible with the lease state. Snapshot the truncate request per oplock_break() call so another waiter cannot overwrite the state of the active break. Use the requested oplock level when deciding whether to chain another break. A second lease open only needs RWH->RH, while a normal none-oplock open can continue down to R and then NONE. For non-truncating metadata operations, break leases only down to read caching. Operations such as delete-on-close need to drop handle caching, but should not send a second R->NONE break after the client acknowledges RH->R. Also send STATUS_PENDING for levelII/read-lease break waiters. An async SMB2 create becomes cancelable only after the server sends an NT_STATUS_PENDING interim response. A waiter that blocks behind an already active lease break must receive the interim response before sleeping on pending_break, otherwise the client can process a later lease break while the create request is still not marked pending. Avoid duplicate interim responses when an overwrite first breaks a write oplock and then scans levelII/read leases. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 108 ++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index b1740fe988e6..608961c838c9 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -727,6 +727,17 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) return 0; } +static bool lease_break_needed(struct oplock_info *opinfo, int req_op_level, + bool open_trunc) +{ + struct lease *lease = opinfo->o_lease; + + if (open_trunc) + return lease->state != SMB2_LEASE_NONE_LE; + + return opinfo->level > req_op_level; +} + /** * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn * to client @@ -985,6 +996,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, struct ksmbd_work *in_work) { int err = 0; + bool sent_interim = false; /* Need to break exclusive/batch oplock, write lease or overwrite_if */ ksmbd_debug(OPLOCK, @@ -993,13 +1005,22 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (brk_opinfo->is_lease) { struct lease *lease = brk_opinfo->o_lease; + bool open_trunc = brk_opinfo->open_trunc; + + if (in_work && test_bit(0, &brk_opinfo->pending_break)) { + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + release_async_work(in_work); + sent_interim = true; + } - atomic_inc(&brk_opinfo->breaking_cnt); err = oplock_break_pending(brk_opinfo, req_op_level); if (err) return err < 0 ? err : 0; - if (brk_opinfo->open_trunc) { +again: + atomic_inc(&brk_opinfo->breaking_cnt); + if (open_trunc) { /* * Create overwrite break trigger the lease break to * none. @@ -1024,17 +1045,31 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, } } + if (in_work && !sent_interim) { + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + release_async_work(in_work); + sent_interim = true; + } + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) { - if (in_work) { - setup_async_work(in_work, NULL, NULL); - smb2_send_interim_resp(in_work, STATUS_PENDING); - release_async_work(in_work); - } - brk_opinfo->op_state = OPLOCK_ACK_WAIT; } else atomic_dec(&brk_opinfo->breaking_cnt); + + err = smb2_lease_break_noti(brk_opinfo); + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + + wait_lease_breaking(brk_opinfo); + if (!err && lease_break_needed(brk_opinfo, req_op_level, open_trunc)) + goto again; + + wake_up_oplock_break(brk_opinfo); + return err; } else { err = oplock_break_pending(brk_opinfo, req_op_level); if (err) @@ -1045,18 +1080,13 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, brk_opinfo->op_state = OPLOCK_ACK_WAIT; } - if (brk_opinfo->is_lease) - err = smb2_lease_break_noti(brk_opinfo); - else - err = smb2_oplock_break_noti(brk_opinfo); + err = smb2_oplock_break_noti(brk_opinfo); ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) err = -ENOENT; wake_up_oplock_break(brk_opinfo); - wait_lease_breaking(brk_opinfo); - return err; } @@ -1261,6 +1291,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct lease_ctx_info *lctx, int share_ret) { int err = 0; + int break_level = SMB2_OPLOCK_LEVEL_II; struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; struct ksmbd_inode *ci = fp->f_ci; struct lease_table *new_lb = NULL; @@ -1325,6 +1356,9 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_op_has_lease = prev_opinfo->is_lease; if (prev_op_has_lease) prev_op_state = prev_opinfo->o_lease->state; + if (prev_op_has_lease && !lctx && + prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + break_level = SMB2_OPLOCK_LEVEL_NONE; if (share_ret < 0 && prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { @@ -1339,7 +1373,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto op_break_not_needed; } - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work); + err = oplock_break(prev_opinfo, break_level, work); opinfo_put(prev_opinfo); if (err == -ENOENT) goto set_lev; @@ -1408,38 +1442,46 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, * @fp: ksmbd file pointer * @is_trunc: truncate on open */ -static void smb_break_all_write_oplock(struct ksmbd_work *work, +static bool smb_break_all_write_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc) { struct oplock_info *brk_opinfo; + bool sent_break = false; brk_opinfo = opinfo_get_list(fp->f_ci); if (!brk_opinfo) - return; + return false; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { opinfo_put(brk_opinfo); - return; + return false; } brk_opinfo->open_trunc = is_trunc; oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work); + sent_break = true; opinfo_put(brk_opinfo); + + return sent_break; } /** - * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * __smb_break_all_levII_oplock() - send level2 oplock or read lease break command * from server to client - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + * @send_interim: send interim response to the client + * @send_oplock_break: send oplock break notification to the client */ -void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, - int is_trunc) +static void __smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc, + bool send_interim) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; struct ksmbd_conn *conn = work->conn; + bool sent_interim = false; if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) @@ -1481,7 +1523,11 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL); + oplock_break(brk_op, + brk_op->is_lease && !is_trunc ? + SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, + send_interim && !sent_interim ? work : NULL); + sent_interim = true; next: opinfo_put(brk_op); } @@ -1491,6 +1537,12 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, opinfo_put(op); } +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + __smb_break_all_levII_oplock(work, fp, is_trunc, true); +} + /** * smb_break_all_oplock() - break both batch/exclusive and level2 oplock * @work: smb work @@ -1498,12 +1550,14 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, */ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) { + bool sent_break; + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) return; - smb_break_all_write_oplock(work, fp, 1); - smb_break_all_levII_oplock(work, fp, 1); + sent_break = smb_break_all_write_oplock(work, fp, 1); + __smb_break_all_levII_oplock(work, fp, 1, !sent_break); } /** From 752bbd323e779691998920035b6e9417d5a6a78c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 21:31:53 +0900 Subject: [PATCH 4458/5214] ksmbd: do not wait for RH lease break ack on overwrite smb2.lease.breaking4 expects an overwrite against an RH lease to send RH->NONE lease break notification but complete the triggering create without waiting for the break ack. Keep the lease in break-in-progress state until the client eventually acknowledges the downgrade, but do not hold the overwrite request behind that ack. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 608961c838c9..4d89fbb6c9c5 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -927,7 +927,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) * * Return: 0 on success, otherwise error */ -static int smb2_lease_break_noti(struct oplock_info *opinfo) +static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack) { struct ksmbd_conn *conn; struct ksmbd_work *work; @@ -964,7 +964,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) if (opinfo->op_state == OPLOCK_ACK_WAIT) { INIT_WORK(&work->work, __smb2_lease_break_noti); ksmbd_queue_work(work); - wait_for_break_ack(opinfo); + if (wait_ack) + wait_for_break_ack(opinfo); } else { __smb2_lease_break_noti(&work->work); if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { @@ -1006,6 +1007,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (brk_opinfo->is_lease) { struct lease *lease = brk_opinfo->o_lease; bool open_trunc = brk_opinfo->open_trunc; + bool wait_ack; if (in_work && test_bit(0, &brk_opinfo->pending_break)) { setup_async_work(in_work, NULL, NULL); @@ -1058,14 +1060,19 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, } else atomic_dec(&brk_opinfo->breaking_cnt); - err = smb2_lease_break_noti(brk_opinfo); + wait_ack = !(open_trunc && + lease->state == (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)); + err = smb2_lease_break_noti(brk_opinfo, wait_ack); ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) err = -ENOENT; - wait_lease_breaking(brk_opinfo); - if (!err && lease_break_needed(brk_opinfo, req_op_level, open_trunc)) + if (wait_ack) + wait_lease_breaking(brk_opinfo); + if (wait_ack && !err && + lease_break_needed(brk_opinfo, req_op_level, open_trunc)) goto again; wake_up_oplock_break(brk_opinfo); From dc2264a9efe7b56040b018024d1dfe0b3839d5ae Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 21:32:29 +0900 Subject: [PATCH 4459/5214] ksmbd: honor SMB2 v2 lease epochs v2 lease responses should continue from the client supplied epoch. Initialize a new v2 lease from the requested epoch plus one so create responses match the epoch returned by Windows and expected by smbtorture. For a single chained break sequence, increment the epoch only for the first break notification. Follow-up breaks such as RH->R and R->NONE in smb2.lease.v2_breaking3 reuse the same epoch. Record when a waiter slept behind pending_break and let the later truncate/open overwrite break consume that marker to reuse the current epoch instead of assigning a new one. Do not increment the epoch when a same-client, same-key create asks for the already granted RH state. The epoch changes only when the granted lease state changes. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 39 +++++++++++++++++++++++++++++---------- fs/smb/server/oplock.h | 1 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 4d89fbb6c9c5..fe0bbb0e1c89 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -163,8 +163,9 @@ static struct lease *alloc_lease(struct lease_ctx_info *lctx, lease->is_dir = lctx->is_dir; memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); lease->version = lctx->version; - lease->epoch = lctx->version == 2 ? 1 : 0; + lease->epoch = lctx->version == 2 ? le16_to_cpu(lctx->epoch) + 1 : 0; lease->ci = ci; + lease->reuse_epoch = false; lease->l_lb = NULL; INIT_LIST_HEAD(&lease->l_entry); INIT_LIST_HEAD(&lease->open_list); @@ -645,9 +646,11 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, if (lctx->req_state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) { - lease->epoch++; - lease->state = lctx->req_state; - lease_update_oplock_levels(lease); + if (lease->state != lctx->req_state) { + lease->epoch++; + lease->state = lctx->req_state; + lease_update_oplock_levels(lease); + } } } @@ -694,6 +697,9 @@ static void wake_up_oplock_break(struct oplock_info *opinfo) static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) { while (test_and_set_bit(0, &opinfo->pending_break)) { + if (opinfo->is_lease) + opinfo->o_lease->reuse_epoch = true; + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); /* Not immediately break to none. */ @@ -927,7 +933,8 @@ static void __smb2_lease_break_noti(struct work_struct *wk) * * Return: 0 on success, otherwise error */ -static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack) +static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, + bool inc_epoch) { struct ksmbd_conn *conn; struct ksmbd_work *work; @@ -950,10 +957,13 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack) br_info->curr_state = lease->state; br_info->new_state = lease->new_state; - if (lease->version == 2) - br_info->epoch = cpu_to_le16(++lease->epoch); - else + if (lease->version == 2) { + if (inc_epoch) + lease->epoch++; + br_info->epoch = cpu_to_le16(lease->epoch); + } else { br_info->epoch = 0; + } memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; @@ -1007,9 +1017,11 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (brk_opinfo->is_lease) { struct lease *lease = brk_opinfo->o_lease; bool open_trunc = brk_opinfo->open_trunc; + bool was_pending = test_bit(0, &brk_opinfo->pending_break); bool wait_ack; + bool inc_epoch = true; - if (in_work && test_bit(0, &brk_opinfo->pending_break)) { + if (in_work && was_pending) { setup_async_work(in_work, NULL, NULL); smb2_send_interim_resp(in_work, STATUS_PENDING); release_async_work(in_work); @@ -1019,6 +1031,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, err = oplock_break_pending(brk_opinfo, req_op_level); if (err) return err < 0 ? err : 0; + if (was_pending) + open_trunc = brk_opinfo->open_trunc; again: atomic_inc(&brk_opinfo->breaking_cnt); @@ -1063,7 +1077,12 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, wait_ack = !(open_trunc && lease->state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)); - err = smb2_lease_break_noti(brk_opinfo, wait_ack); + if (lease->reuse_epoch) { + inc_epoch = false; + lease->reuse_epoch = false; + } + err = smb2_lease_break_noti(brk_opinfo, wait_ack, inc_epoch); + inc_epoch = false; ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 30bad6d65048..1dd52b44557f 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -49,6 +49,7 @@ struct lease { int version; unsigned short epoch; bool is_dir; + bool reuse_epoch; struct ksmbd_inode *ci; struct lease_table *l_lb; struct list_head l_entry; From 1f1083c36fa11c5d9011451c7b9ab380545c72ea Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 21:33:07 +0900 Subject: [PATCH 4460/5214] ksmbd: break RH leases before delete-on-close The delete paths only marked the opened file delete pending or delete-on-close. When another client still held a read/handle lease, no lease break was sent before the delete state changed. smb2.lease.unlink uses a create request with FILE_DELETE_ON_CLOSE and expects the second client's unlink to break the first client's RH lease to R with ACK_REQUIRED set. SetInfo(FileDispositionInformation) has the same lease-breaking requirement. Break level-II/read-handle leases before setting delete pending or delete-on-close so clients are notified before the file is removed. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b84062b16a75..f1a0bce0c1f7 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3653,8 +3653,10 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + smb_break_all_levII_oplock(work, fp, 0); ksmbd_fd_set_delete_on_close(fp, file_info); + } if (need_truncate) { rc = smb2_create_truncate(&fp->filp->f_path); @@ -6584,7 +6586,8 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, return smb2_rename(work, fp, rename_info, work->conn->local_nls); } -static int set_file_disposition_info(struct ksmbd_file *fp, +static int set_file_disposition_info(struct ksmbd_work *work, + struct ksmbd_file *fp, struct smb2_file_disposition_info *file_info) { struct inode *inode; @@ -6599,6 +6602,7 @@ static int set_file_disposition_info(struct ksmbd_file *fp, if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; + smb_break_all_levII_oplock(work, fp, 0); ksmbd_set_inode_pending_delete(fp); } else { ksmbd_clear_inode_pending_delete(fp); @@ -6725,7 +6729,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, if (buf_len < sizeof(struct smb2_file_disposition_info)) return -EMSGSIZE; - return set_file_disposition_info(fp, + return set_file_disposition_info(work, fp, (struct smb2_file_disposition_info *)buffer); } case FILE_FULL_EA_INFORMATION: From 2145945feb2c27d8e20f113ef81dc373631c4f05 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 21:33:40 +0900 Subject: [PATCH 4461/5214] ksmbd: route v2 lease breaks on the client lease channel v2 leases are scoped by ClientGuid. When the same client uses multiple connections, smbtorture expects lease break notifications to be sent on the connection associated with the client lease table, not necessarily on the connection that owns the individual open being broken. Keep a referenced connection in the lease table and use it for v2 lease break notifications while it is still active. Fall back to the open's connection if the table connection is being released. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 17 +++++++++++++++-- fs/smb/server/oplock.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index fe0bbb0e1c89..d936c338f7c7 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -141,11 +141,21 @@ static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) memcpy(lb->client_guid, opinfo->conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + lb->conn = ksmbd_conn_get(opinfo->conn); INIT_LIST_HEAD(&lb->lease_list); spin_lock_init(&lb->lb_lock); return lb; } +static void free_lease_table(struct lease_table *lb) +{ + if (!lb) + return; + + ksmbd_conn_put(lb->conn); + kfree(lb); +} + static struct lease *alloc_lease(struct lease_ctx_info *lctx, struct ksmbd_inode *ci) { @@ -942,6 +952,9 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, struct lease *lease = opinfo->o_lease; conn = READ_ONCE(opinfo->conn); + if (lease->version == 2 && lease->l_lb && lease->l_lb->conn && + !ksmbd_conn_releasing(lease->l_lb->conn)) + conn = lease->l_lb->conn; if (!conn) return 0; @@ -1134,7 +1147,7 @@ void destroy_lease_table(struct ksmbd_conn *conn) list_for_each_entry_safe(lease, ltmp, &lb->lease_list, l_entry) lease_del_table(lease); list_del(&lb->l_entry); - kfree(lb); + free_lease_table(lb); } write_unlock(&lease_list_lock); } @@ -1193,7 +1206,7 @@ static void add_lease_global_list(struct lease *lease, struct ksmbd_conn *conn, SMB2_CLIENT_GUID_SIZE)) { lease_add_table(lease, lb); write_unlock(&lease_list_lock); - kfree(new_lb); + free_lease_table(new_lb); return; } } diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 1dd52b44557f..8d1943586246 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -34,6 +34,7 @@ struct lease_ctx_info { struct lease_table { char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct ksmbd_conn *conn; struct list_head lease_list; struct list_head l_entry; spinlock_t lb_lock; From 411398c9a1414094f4e52c8893ef55ae3c61bf2a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 18 Jun 2026 21:54:43 +0900 Subject: [PATCH 4462/5214] ksmbd: keep common response iovecs in the work item Most SMB responses need no more than four kvec entries, but every work item currently allocates a separate four-entry array and frees it after the response is sent. Embed the common array in struct ksmbd_work and allocate a larger array only when a response exceeds the inline capacity. This removes one allocation and one free from the common request path while preserving support for larger compound and read responses. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/ksmbd_work.c | 58 ++++++++++++++++++++++++-------------- fs/smb/server/ksmbd_work.h | 3 ++ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index a5ab6799a65c..e2c2f45264be 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -16,6 +16,36 @@ static struct kmem_cache *work_cache; static struct workqueue_struct *ksmbd_wq; +static int ksmbd_reserve_iov(struct ksmbd_work *work, int need_iov_cnt) +{ + struct kvec *new; + int new_alloc_cnt = work->iov_alloc_cnt; + + if (work->iov_alloc_cnt >= work->iov_cnt + need_iov_cnt) + return 0; + + do { + new_alloc_cnt += KSMBD_WORK_INLINE_IOVS; + } while (new_alloc_cnt < work->iov_cnt + need_iov_cnt); + + if (work->iov == work->iov_inline) { + new = kcalloc(new_alloc_cnt, sizeof(*new), KSMBD_DEFAULT_GFP); + if (!new) + return -ENOMEM; + + memcpy(new, work->iov_inline, sizeof(work->iov_inline)); + } else { + new = krealloc(work->iov, sizeof(*new) * new_alloc_cnt, + KSMBD_DEFAULT_GFP | __GFP_ZERO); + if (!new) + return -ENOMEM; + } + + work->iov = new; + work->iov_alloc_cnt = new_alloc_cnt; + return 0; +} + struct ksmbd_work *ksmbd_alloc_work_struct(void) { struct ksmbd_work *work = kmem_cache_zalloc(work_cache, KSMBD_DEFAULT_GFP); @@ -27,13 +57,8 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) INIT_LIST_HEAD(&work->async_request_entry); INIT_LIST_HEAD(&work->fp_entry); INIT_LIST_HEAD(&work->aux_read_list); - work->iov_alloc_cnt = 4; - work->iov = kzalloc_objs(struct kvec, work->iov_alloc_cnt, - KSMBD_DEFAULT_GFP); - if (!work->iov) { - kmem_cache_free(work_cache, work); - work = NULL; - } + work->iov_alloc_cnt = ARRAY_SIZE(work->iov_inline); + work->iov = work->iov_inline; } return work; } @@ -55,7 +80,8 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) kfree(work->tr_buf); kvfree(work->compress_buf); kvfree(work->request_buf); - kfree(work->iov); + if (work->iov != work->iov_inline) + kfree(work->iov); if (work->async_id) ksmbd_release_id(&work->conn->async_ida, work->async_id); @@ -117,19 +143,9 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, return -ENOMEM; } - if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) { - struct kvec *new; - - work->iov_alloc_cnt += 4; - new = krealloc(work->iov, - sizeof(struct kvec) * work->iov_alloc_cnt, - KSMBD_DEFAULT_GFP | __GFP_ZERO); - if (!new) { - kfree(ar); - work->iov_alloc_cnt -= 4; - return -ENOMEM; - } - work->iov = new; + if (ksmbd_reserve_iov(work, need_iov_cnt)) { + kfree(ar); + return -ENOMEM; } /* Plus rfc_length size on first iov */ diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index 0da8cc0972d6..5368430561fb 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -13,6 +13,8 @@ struct ksmbd_conn; struct ksmbd_session; struct ksmbd_tree_connect; +#define KSMBD_WORK_INLINE_IOVS 4 + enum { KSMBD_WORK_ACTIVE = 0, KSMBD_WORK_CANCELLED, @@ -42,6 +44,7 @@ struct ksmbd_work { int iov_alloc_cnt; int iov_cnt; int iov_idx; + struct kvec iov_inline[KSMBD_WORK_INLINE_IOVS]; /* Next cmd hdr in compound req buf*/ int next_smb2_rcv_hdr_off; From 0c054227479ed7e36ebccb3a558bc0ef698264f6 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 19 Jun 2026 08:25:46 +0900 Subject: [PATCH 4463/5214] ksmbd: fix use-after-free of conn->preauth_info in concurrent SMB2 NEGOTIATE conn->preauth_info is shared connection state (struct preauth_integrity_info, kmalloc-96) that is allocated and freed by the SMB2 NEGOTIATE handler and read by the response send path. smb2_handle_negotiate() allocates conn->preauth_info, and on a deassemble_neg_contexts() failure kfrees it and sets it to NULL. Both the allocation and the free/NULL happen under ksmbd_conn_lock(conn) (the connection srv_mutex), which is held across the whole handler body. The response send path smb3_preauth_hash_rsp(), called from the send: block of __handle_ksmbd_work(), reads conn->preauth_info and dereferences conn->preauth_info->Preauth_HashValue (via ksmbd_gen_preauth_integrity_hash()) without taking conn_lock. When a client drives two SMB2 NEGOTIATE requests on the same connection, one worker can free conn->preauth_info on the failing-negotiate path while a concurrent send-path worker is reading it, producing a slab use-after-free read (KASAN-confirmed). The send-path read tested conn->preauth_info for NULL but raced with the free that occurs between the NULL check and the dereference, so the NULL guard alone does not close the window. Serialize the NEGOTIATE-branch read in smb3_preauth_hash_rsp() under ksmbd_conn_lock(conn) and re-check conn->preauth_info inside the lock. Because the negotiate handler holds conn_lock across its kfree + NULL assignment, a reader that also takes conn_lock either runs fully before the allocation or fully after the NULL store, and can never observe the freed-but-not-yet-NULLed pointer. ksmbd_gen_preauth_integrity_hash() takes no locks itself (it only computes a SHA-512 over the buffer), so no lock-ordering inversion is introduced, and conn_lock is a sleepable mutex which is safe on this send path (it already performs network I/O). Fixes: aa7253c2393f ("ksmbd: fix memory leak in smb2_handle_negotiate") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f1a0bce0c1f7..2bc275ed450a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9368,10 +9368,13 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) { + ksmbd_conn_lock(conn); + if (conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + ksmbd_conn_unlock(conn); + } if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { __u8 *hash_value; From a04159d96c27fd4836538ea6e8ff653b65242eac Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:32:18 +0900 Subject: [PATCH 4464/5214] ksmbd: handle missing create contexts for lease opens smb2_find_context_vals() assumes that callers only search create contexts when the SMB2 CREATE request contains a non-empty create context area. That is not always true. a client can send RequestedOplockLevel set to SMB2_OPLOCK_LEVEL_LEASE without a lease create context. In that case parse_lease_state() searches for a lease context and smb2_find_context_vals() starts parsing from offset 0 with length 0, returning -EINVAL. This makes the open fail with STATUS_INVALID_PARAMETER. The smbtorture smb2.lease.duplicate_open test hits this while creating a second file without a lease request. Return NULL when the request has no create context area so the missing context is treated the same as any other absent create context. The open then continues without granting a lease. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index d936c338f7c7..5424f2a5cf3d 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1760,6 +1760,9 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag, i * CreateContextsOffset and CreateContextsLength are guaranteed to * be valid because of ksmbd_smb2_check_message(). */ + if (!req->CreateContextsOffset || !req->CreateContextsLength) + return NULL; + cc = (struct create_context *)((char *)req + le32_to_cpu(req->CreateContextsOffset)); remain_len = le32_to_cpu(req->CreateContextsLength); From 166e4c07023b9c5d076f69e63164b6e1d52709c9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:33:03 +0900 Subject: [PATCH 4465/5214] ksmbd: supersede disconnected delete-on-close durable handle A durable handle opened with FILE_DELETE_ON_CLOSE is preserved across a disconnect so it can be reclaimed by a durable reconnect. smb2.durable-open.delete_on_close2 disconnects such a handle and then reconnects it, expecting the reconnect to succeed. When the client does not reconnect but instead opens the same name with a new delete-on-close create, the preserved handle keeps the file present with delete-on-close set. ksmbd then rejects the new open with STATUS_ACCESS_DENIED on the file_present + FILE_DELETE_ON_CLOSE + OPEN_IF/OVERWRITE_IF path. smb2.durable-open.delete_on_close1 expects this open to create a fresh, empty file instead, i.e. the disconnected handle's delete-on-close must take effect first. Add ksmbd_close_disconnected_durable_delete_on_close(), which closes disconnected (conn == NULL) durable handles that keep a delete-on-close file present. The final close promotes S_DEL_ON_CLS to S_DEL_PENDING and unlinks the file, so a re-resolved path is absent and the new open creates it fresh. Call it from smb2_open() before the delete-on-close conflict check, only for the conflicting open shapes. A live (connected) handle still keeps the file and blocks the open as before. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 17 ++++++++++++ fs/smb/server/vfs_cache.c | 57 +++++++++++++++++++++++++++++++++++++++ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 75 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 2bc275ed450a..37a20c5740cd 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3272,6 +3272,23 @@ int smb2_open(struct ksmbd_work *work) rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + + /* + * A durable handle opened with delete-on-close is preserved across a + * disconnect so it can be reclaimed by a durable reconnect. When a new + * delete-on-close open for the same name arrives instead, the + * disconnected handle must give way: close it so its delete-on-close + * removes the file, then re-resolve so this open can create a fresh one. + */ + if (!rc && (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) && + (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) && + ksmbd_close_disconnected_durable_delete_on_close(path.dentry)) { + path_put(&path); + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, + &path, 1); + } + if (!rc) { file_present = true; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 39c56942ae44..96fa3f160d5b 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -517,6 +517,63 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) kmem_cache_free(filp_cache, fp); } +/** + * ksmbd_close_disconnected_durable_delete_on_close() - drop a delete-on-close + * file kept present only by disconnected durable handles + * @dentry: dentry of the file being opened + * + * A durable handle opened with delete-on-close is preserved across a + * disconnect so it can be reclaimed by a durable reconnect. When a new + * (non-reconnect) open arrives for the same name instead, the disconnected + * handle has to give way. Close such handles so their delete-on-close is + * applied and the file is removed once the last handle is gone, letting the + * new open create a fresh file. + * + * The caller's inode reference is dropped before closing so that the final + * close can promote S_DEL_ON_CLS to S_DEL_PENDING and unlink the file. + * + * Return: true if a disconnected durable handle was closed. + */ +bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry) +{ + struct ksmbd_inode *ci; + struct ksmbd_file *fp, *tmp; + LIST_HEAD(dispose); + bool closed = false; + + ci = ksmbd_inode_lookup_lock(dentry); + if (!ci) + return false; + + down_write(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_ON_CLS_STREAM | S_DEL_PENDING)) { + list_for_each_entry_safe(fp, tmp, &ci->m_fp_list, node) { + if (fp->conn || !fp->is_durable || + fp->f_state != FP_INITED) + continue; + list_move_tail(&fp->node, &dispose); + } + } + up_write(&ci->m_lock); + + /* + * Drop our lookup reference before closing so the last __ksmbd_close_fd() + * can drop m_count to zero and unlink the delete-on-close file. The + * collected handles still hold references, so ci stays valid until they + * are closed below. + */ + ksmbd_inode_put(ci); + + while (!list_empty(&dispose)) { + fp = list_first_entry(&dispose, struct ksmbd_file, node); + list_del_init(&fp->node); + __ksmbd_close_fd(NULL, fp); + closed = true; + } + + return closed; +} + static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) { if (fp->f_state != FP_INITED) diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index a3a9fda6de91..21c24956c7c2 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -160,6 +160,7 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); void ksmbd_inode_put(struct ksmbd_inode *ci); +bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry); struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); From 26fa88dc877ccd2736e9125d37c22e058a4c7b86 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:33:54 +0900 Subject: [PATCH 4466/5214] ksmbd: invalidate durable handles on oplock break When a durable handle is preserved after a disconnect, its oplock state can still block later opens. If another client opens the same file and the preserved oplock or lease has to be broken, the old durable handle must no longer be reconnectable after the break cannot be acknowledged. ksmbd was treating a missing connection, or an oplock break timeout, as a successful break only by downgrading the oplock state. The old durable handle remained reconnectable, so a later durable reconnect for that stale handle could succeed. The open path can also see a detached durable handle before the break notification helpers fully dispose of it. Invalidate such a preserved durable handle directly when a competing open has to break its batch or exclusive oplock, while leaving ordinary durable reconnects without a competing open untouched. If the old handle still reaches the reconnect path, reject it when the same inode already has another active open. This matches the smb2.durable-open.open2-lease/open2-oplock sequence where a later open replaces the disconnected durable owner and the stale first handle must not be reclaimed. Also, reconnect lookup used only the persistent id. A new durable open can get a persistent id that matches the stale reconnect request after the old durable state is invalidated. Preserve the disconnected handle's old volatile id and require durable reconnect contexts to match it, so a stale reconnect cannot attach to a different durable open. Windows allows the later open to proceed and rejects the old reconnect with STATUS_OBJECT_NAME_NOT_FOUND. The smbtorture smb2.durable-open.oplock test covers this case. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 40 +++++++++++++++++++++++++------ fs/smb/server/smb2pdu.c | 14 +++++++++++ fs/smb/server/vfs_cache.c | 50 ++++++++++++++++++++++++++++++++++++++- fs/smb/server/vfs_cache.h | 4 ++++ 4 files changed, 100 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 5424f2a5cf3d..0fddbaa5ba63 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -676,7 +676,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, return m_opinfo; } -static void wait_for_break_ack(struct oplock_info *opinfo) +static bool wait_for_break_ack(struct oplock_info *opinfo) { int rc = 0; @@ -693,7 +693,10 @@ static void wait_for_break_ack(struct oplock_info *opinfo) } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; + return true; } + + return false; } static void wake_up_oplock_break(struct oplock_info *opinfo) @@ -843,7 +846,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) conn = READ_ONCE(opinfo->conn); if (!conn) - return 0; + return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); if (!work) @@ -868,7 +871,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) INIT_WORK(&work->work, __smb2_oplock_break_noti); ksmbd_queue_work(work); - wait_for_break_ack(opinfo); + if (wait_for_break_ack(opinfo)) + ret = ksmbd_invalidate_durable_fd(opinfo->fid); } else { __smb2_oplock_break_noti(&work->work); if (opinfo->level == SMB2_OPLOCK_LEVEL_II) @@ -950,13 +954,14 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, struct ksmbd_work *work; struct lease_break_info *br_info; struct lease *lease = opinfo->o_lease; + int ret = 0; conn = READ_ONCE(opinfo->conn); if (lease->version == 2 && lease->l_lb && lease->l_lb->conn && !ksmbd_conn_releasing(lease->l_lb->conn)) conn = lease->l_lb->conn; if (!conn) - return 0; + return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); if (!work) @@ -987,8 +992,10 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, if (opinfo->op_state == OPLOCK_ACK_WAIT) { INIT_WORK(&work->work, __smb2_lease_break_noti); ksmbd_queue_work(work); - if (wait_ack) - wait_for_break_ack(opinfo); + if (wait_ack) { + if (wait_for_break_ack(opinfo)) + ret = ksmbd_invalidate_durable_fd(opinfo->fid); + } } else { __smb2_lease_break_noti(&work->work); if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { @@ -996,7 +1003,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, lease_update_oplock_levels(opinfo->o_lease); } } - return 0; + return ret; } static void wait_lease_breaking(struct oplock_info *opinfo) @@ -1335,6 +1342,9 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct ksmbd_inode *ci = fp->f_ci; struct lease_table *new_lb = NULL; bool prev_op_has_lease; + bool prev_durable_open = false; + bool prev_durable_detached = false; + unsigned long long prev_fid = KSMBD_NO_FID; bool new_lease = false; __le32 prev_op_state = 0; @@ -1412,7 +1422,17 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto op_break_not_needed; } + if (prev_opinfo->o_fp && prev_opinfo->o_fp != fp && + prev_opinfo->o_fp->is_durable) { + prev_durable_open = true; + prev_durable_detached = !prev_opinfo->o_fp->conn || + !prev_opinfo->o_fp->tcon; + prev_fid = prev_opinfo->fid; + } + err = oplock_break(prev_opinfo, break_level, work); + if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) + ksmbd_invalidate_durable_fd(prev_fid); opinfo_put(prev_opinfo); if (err == -ENOENT) goto set_lev; @@ -2029,6 +2049,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, if (!opinfo) return 0; + if (ksmbd_has_other_active_fd(fp)) { + ksmbd_debug(SMB, "Durable handle reconnect failed: competing open\n"); + ret = -EBADF; + goto out; + } + if (ksmbd_vfs_compare_durable_owner(fp, user) == false) { ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n"); ret = -EBADF; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 37a20c5740cd..f700f2f94ff2 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2861,6 +2861,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + if (dh_info->fp->durable_volatile_id != + recon_v2->dcontext.Fid.VolatileFileId) { + err = -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid, SMB2_CREATE_GUID_SIZE)) { err = -EBADF; @@ -2901,6 +2908,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + if (dh_info->fp->durable_volatile_id != + recon->Data.Fid.VolatileFileId) { + err = -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + dh_info->type = dh_idx; dh_info->reconnected = true; ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n", diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 96fa3f160d5b..3546d95df76f 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -731,7 +731,8 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) struct ksmbd_file *fp; fp = __ksmbd_lookup_fd(&global_ft, id); - if (fp && (fp->conn || + if (fp && (fp->durable_reconnect_disabled || + fp->conn || (fp->durable_scavenger_timeout && (fp->durable_scavenger_timeout < jiffies_to_msecs(jiffies))))) { @@ -750,6 +751,52 @@ void ksmbd_put_durable_fd(struct ksmbd_file *fp) __ksmbd_close_fd(NULL, fp); } +bool ksmbd_has_other_active_fd(struct ksmbd_file *fp) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci = fp->f_ci; + bool ret = false; + + down_read(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (lfp == fp) + continue; + + if (lfp->f_state == FP_INITED && + (READ_ONCE(lfp->conn) || READ_ONCE(lfp->tcon))) { + ret = true; + break; + } + } + up_read(&ci->m_lock); + + return ret; +} + +int ksmbd_invalidate_durable_fd(unsigned long long id) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_global_fd(id); + if (!fp) + return -ENOENT; + + fp->durable_reconnect_disabled = true; + + if (fp->conn) { + ksmbd_put_durable_fd(fp); + return -ENOENT; + } + + fp->durable_timeout = 1; + fp->durable_scavenger_timeout = jiffies_to_msecs(jiffies); + ksmbd_put_durable_fd(fp); + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); + + return -ENOENT; +} + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) { struct ksmbd_file *fp = NULL; @@ -990,6 +1037,7 @@ __close_file_table_ids(struct ksmbd_session *sess, * global_ft. */ idr_remove(ft->idr, id); + fp->durable_volatile_id = fp->volatile_id; fp->volatile_id = KSMBD_NO_FID; write_unlock(&ft->lock); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 21c24956c7c2..4803f41a91ef 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -81,6 +81,7 @@ struct ksmbd_file { struct file *filp; u64 persistent_id; u64 volatile_id; + u64 durable_volatile_id; spinlock_t f_lock; @@ -122,6 +123,7 @@ struct ksmbd_file { bool is_durable; bool is_persistent; bool is_resilient; + bool durable_reconnect_disabled; bool is_posix_ctxt; struct durable_owner owner; @@ -164,6 +166,8 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry); struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); +int ksmbd_invalidate_durable_fd(unsigned long long id); +bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); From 73cd6295d01d8d49abaa03472878ef133e43d26a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:34:37 +0900 Subject: [PATCH 4467/5214] ksmbd: fix durable reconnect context parsing SMB2 create context DataLength describes only the create context data payload. It does not include the create context header, name field, or any local padding that exists in ksmbd's helper structures. ksmbd validated durable reconnect contexts by comparing DataOffset + DataLength against sizeof the whole helper structure. This rejects a valid durable v2 reconnect context because the wire DH2C data is 36 bytes while struct create_durable_handle_reconnect_v2 contains an extra four byte pad. Validate the durable context payload length against the corresponding payload member instead. Also keep the reconnect context authoritative when a later durable request context is present, matching the existing durable v1 reconnect behavior. This fixes smbtorture smb2.durable-v2-open.durable-v2-setinfo, where the durable v2 reconnect after SET_INFO was rejected with STATUS_INVALID_PARAMETER. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f700f2f94ff2..35db86da79d3 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2845,9 +2845,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_durable_handle_reconnect_v2)) { + if (le32_to_cpu(context->DataLength) < + sizeof(recon_v2->dcontext)) { err = -EINVAL; goto out; } @@ -2892,9 +2891,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(create_durable_reconn_t)) { + if (le32_to_cpu(context->DataLength) < + sizeof(recon->Data)) { err = -EINVAL; goto out; } @@ -2931,9 +2929,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_durable_req_v2)) { + if (le32_to_cpu(context->DataLength) < + sizeof(durable_v2_blob->dcontext)) { err = -EINVAL; goto out; } From 16c30649709d949b439273e61769028b1e706327 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:35:12 +0900 Subject: [PATCH 4468/5214] ksmbd: handle durable v2 app instance id The SMB2_CREATE_APP_INSTANCE_ID create context is used with durable v2 opens to identify another open from the same application instance. When a new durable v2 open arrives with the same AppInstanceId as an existing open, the server should close the previous open without sending an oplock break notification. ksmbd ignored this create context. A second durable v2 batch oplock open with the same AppInstanceId therefore went through the normal competing open path and sent an oplock break to the first opener. smbtorture smb2.durable-v2-open.app-instance expects no oplock break and then expects the old handle to be closed. Parse and store AppInstanceId for durable v2 opens. Before creating the new open, find an existing file with the same AppInstanceId and close it through the normal close teardown path without issuing an oplock break. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 37 ++++++++++++++++++++ fs/smb/server/vfs_cache.c | 72 +++++++++++++++++++++++++++++++++++++++ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 110 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 35db86da79d3..a21760394637 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2807,8 +2807,10 @@ struct durable_info { unsigned short int type; bool persistent; bool reconnected; + bool app_instance_id; unsigned int timeout; char *CreateGuid; + char AppInstanceId[SMB2_CREATE_GUID_SIZE]; }; static int parse_durable_handle_context(struct ksmbd_work *work, @@ -2993,6 +2995,31 @@ static int parse_durable_handle_context(struct ksmbd_work *work, return err; } +static int parse_app_instance_id(struct smb2_create_req *req, + struct durable_info *dh_info) +{ + struct create_context *context; + char *data; + + context = smb2_find_context_vals(req, SMB2_CREATE_APP_INSTANCE_ID, + SMB2_CREATE_GUID_SIZE); + if (IS_ERR(context)) + return PTR_ERR(context); + if (!context) + return 0; + + if (le32_to_cpu(context->DataLength) < 20) + return -EINVAL; + + data = (char *)context + le16_to_cpu(context->DataOffset); + if (data[0] != 20 || data[1]) + return -EINVAL; + + memcpy(dh_info->AppInstanceId, data + 4, SMB2_CREATE_GUID_SIZE); + dh_info->app_instance_id = true; + return 0; +} + /** * smb2_open() - handler for smb file open request * @work: smb work containing request buffer @@ -3135,6 +3162,9 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "error parsing durable handle context\n"); goto err_out2; } + rc = parse_app_instance_id(req, &dh_info); + if (rc) + goto err_out2; if (dh_info.reconnected == true) { rc = smb2_check_durable_oplock(conn, share, dh_info.fp, @@ -3161,6 +3191,9 @@ int smb2_open(struct ksmbd_work *work) goto reconnected_fp; } + + if (dh_info.type == DURABLE_REQ_V2 && dh_info.app_instance_id) + ksmbd_close_fd_app_instance_id(dh_info.AppInstanceId); } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { lc = parse_lease_state(req); if (IS_ERR(lc)) { @@ -3775,6 +3808,10 @@ int smb2_open(struct ksmbd_work *work) if (dh_info.type == DURABLE_REQ_V2) { memcpy(fp->create_guid, dh_info.CreateGuid, SMB2_CREATE_GUID_SIZE); + if (dh_info.app_instance_id) + memcpy(fp->app_instance_id, + dh_info.AppInstanceId, + SMB2_CREATE_GUID_SIZE); if (dh_info.timeout) fp->durable_timeout = min_t(unsigned int, dh_info.timeout, diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 3546d95df76f..5a2fddadcddf 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -37,6 +37,8 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +static int ksmbd_mark_fp_closed(struct ksmbd_file *fp); + #define OPLOCK_NONE 0 #define OPLOCK_EXCLUSIVE 1 #define OPLOCK_BATCH 2 @@ -773,6 +775,76 @@ bool ksmbd_has_other_active_fd(struct ksmbd_file *fp) return ret; } +static struct ksmbd_file *ksmbd_lookup_fd_app_instance_id(char *app_instance_id) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + if (!memchr_inv(app_instance_id, 0, SMB2_CREATE_GUID_SIZE)) + return NULL; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->app_instance_id, app_instance_id, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +int ksmbd_close_fd_app_instance_id(char *app_instance_id) +{ + struct ksmbd_file_table *ft; + struct ksmbd_file *fp; + struct oplock_info *opinfo; + int n_to_drop = 0; + + fp = ksmbd_lookup_fd_app_instance_id(app_instance_id); + if (!fp) + return 0; + + opinfo = opinfo_get(fp); + if (!opinfo || !opinfo->sess) + goto out; + + ft = &opinfo->sess->file_table; + write_lock(&ft->lock); + if (fp->f_state == FP_INITED) { + if (has_file_id(fp->volatile_id)) { + idr_remove(ft->idr, fp->volatile_id); + fp->volatile_id = KSMBD_NO_FID; + } + n_to_drop = ksmbd_mark_fp_closed(fp); + } + write_unlock(&ft->lock); + opinfo_put(opinfo); + opinfo = NULL; + + if (!n_to_drop) + goto out; + + down_write(&fp->f_ci->m_lock); + list_del_init(&fp->node); + up_write(&fp->f_ci->m_lock); + + if (atomic_sub_and_test(n_to_drop, &fp->refcount)) { + if (fp->conn) + atomic_dec(&fp->conn->stats.open_files_count); + __ksmbd_close_fd(NULL, fp); + } + return 0; + +out: + if (opinfo) + opinfo_put(opinfo); + ksmbd_put_durable_fd(fp); + return 0; +} + int ksmbd_invalidate_durable_fd(unsigned long long id) { struct ksmbd_file *fp; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 4803f41a91ef..ca391d597e2e 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -168,6 +168,7 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); int ksmbd_invalidate_durable_fd(unsigned long long id); bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); +int ksmbd_close_fd_app_instance_id(char *app_instance_id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); From 24dcee8305d6ba18c69594ca6fb42eaa32f8e63e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:36:40 +0900 Subject: [PATCH 4469/5214] ksmbd: preserve open change time across rename inode ctime is updated when a file is renamed. ksmbd returned that ctime directly as SMB2 ChangeTime for handle-based query information. This makes ChangeTime change after a rename through an already-open handle, while Windows keeps the handle's ChangeTime stable for this case. Store the SMB ChangeTime in struct ksmbd_file when the handle is opened and use that value for create, close, and handle-based query information responses. If a client explicitly sets FILE_BASIC_INFORMATION ChangeTime, update the stored value as well. This fixes smbtorture smb2.rename.simple_modtime, which expects ChangeTime and LastWriteTime to remain unchanged after renaming an already-open file. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 23 ++++++++++------------- fs/smb/server/vfs_cache.h | 1 + 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a21760394637..f2ba52c7e325 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3783,6 +3783,7 @@ int smb2_open(struct ksmbd_work *work) fp->create_time = ksmbd_UnixTimeToNT(stat.btime); else fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + fp->change_time = ksmbd_UnixTimeToNT(stat.ctime); if (req->FileAttributes || fp->f_ci->m_fattr == 0) fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); @@ -3832,8 +3833,7 @@ int smb2_open(struct ksmbd_work *work) rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); + rsp->ChangeTime = cpu_to_le64(fp->change_time); rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.blocks << 9); rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); @@ -5102,8 +5102,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, basic_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); basic_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - basic_info->ChangeTime = cpu_to_le64(time); + basic_info->ChangeTime = cpu_to_le64(fp->change_time); basic_info->Attributes = fp->f_ci->m_fattr; basic_info->Pad = 0; rsp->OutputBufferLength = @@ -5205,8 +5204,7 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; if (ksmbd_stream_fd(fp) == false) { @@ -5415,8 +5413,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->Attributes = fp->f_ci->m_fattr; if (ksmbd_stream_fd(fp) == false) { file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); @@ -5547,8 +5544,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->DosAttributes = fp->f_ci->m_fattr; file_info->Inode = cpu_to_le64(stat.ino); if (ksmbd_stream_fd(fp) == false) { @@ -6271,8 +6267,7 @@ int smb2_close(struct ksmbd_work *work) rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); + rsp->ChangeTime = cpu_to_le64(fp->change_time); ksmbd_fd_put(work, fp); } else { rsp->Flags = 0; @@ -6485,9 +6480,11 @@ static int set_file_basic_info(struct ksmbd_file *fp, attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); } - if (file_info->ChangeTime) + if (file_info->ChangeTime) { + fp->change_time = le64_to_cpu(file_info->ChangeTime); inode_set_ctime_to_ts(inode, ksmbd_NTtimeToUnix(file_info->ChangeTime)); + } if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index ca391d597e2e..8c456484cab2 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -97,6 +97,7 @@ struct ksmbd_file { __le32 coption; __le32 cdoption; __u64 create_time; + __u64 change_time; __u64 itime; bool is_nt_open; From 9a5784f4d58b0ad772998329f0309f39b3bc1a09 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:37:22 +0900 Subject: [PATCH 4470/5214] ksmbd: check parent directory sharing conflicts on rename When renaming a file, some existing opens on the parent directory must block the rename with STATUS_SHARING_VIOLATION. This includes parent directory handles opened with DELETE access and handles opened without FILE_SHARE_DELETE. ksmbd checked only the parent's desired access for FILE_DELETE. That handled smb2.rename.share_delete_and_delete_access, but missed the case where the parent directory was opened without delete access and without delete sharing, so smb2.rename.no_share_delete_no_delete_access incorrectly succeeded. Attribute-only parent opens, however, must not block the rename. smb2.rename.msword opens the parent directory with only SYNCHRONIZE and FILE_READ_ATTRIBUTES, no share access, and then renames an already-open child file. Windows allows this pattern. Reject parent directory handles that request DELETE access, and reject non-attribute-only parent opens that deny FILE_SHARE_DELETE, while allowing attribute-only parent opens to coexist with child rename. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 74b0307cb100..80fd27752b2c 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -702,8 +702,10 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); if (parent_fp) { - if (parent_fp->daccess & FILE_DELETE_LE) { - pr_err("parent dir is opened with delete access\n"); + if ((parent_fp->daccess & FILE_DELETE_LE) || + (!parent_fp->attrib_only && + !(parent_fp->saccess & FILE_SHARE_DELETE_LE))) { + pr_err("parent dir blocks delete sharing\n"); err = -ESHARE; ksmbd_fd_put(work, parent_fp); goto out3; From c841bd3d8dec33a000d6e31b7e7fafb22c39e4e9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:37:56 +0900 Subject: [PATCH 4471/5214] ksmbd: deny renaming directory with open children Windows denies renaming a directory while a file below that directory is still open. smb2.rename.rename_dir_openfile checks this by keeping a file handle open under the directory and then attempting to rename the directory handle. ksmbd did not check open children before calling vfs_rename(), so the rename incorrectly succeeded. For non-POSIX clients, scan the global open file table for active handles whose dentries are below the directory being renamed. If any child is open, fail the rename with -EACCES so the client receives STATUS_ACCESS_DENIED. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 6 ++++++ fs/smb/server/vfs_cache.c | 25 +++++++++++++++++++++++++ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 32 insertions(+) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 80fd27752b2c..f5fa22d87603 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -700,6 +700,12 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, if (err) goto out_drop_write; + if (!work->tcon->posix_extensions && d_is_dir(old_child) && + ksmbd_has_open_files(old_child)) { + err = -EACCES; + goto out3; + } + parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); if (parent_fp) { if ((parent_fp->daccess & FILE_DELETE_LE) || diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 5a2fddadcddf..98c5bac93d63 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "glob.h" #include "vfs_cache.h" @@ -914,6 +915,30 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) return NULL; } +bool ksmbd_has_open_files(struct dentry *dentry) +{ + struct ksmbd_file *fp; + unsigned int id; + bool ret = false; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + struct dentry *fp_dentry = fp->filp->f_path.dentry; + + if (fp->f_state != FP_INITED) + continue; + if (fp_dentry == dentry) + continue; + if (is_subdir(fp_dentry, dentry)) { + ret = true; + break; + } + } + read_unlock(&global_ft.lock); + + return ret; +} + #define OPEN_ID_TYPE_VOLATILE_ID (0) #define OPEN_ID_TYPE_PERSISTENT_ID (1) diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 8c456484cab2..52a0e8b1f79f 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -172,6 +172,7 @@ bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); int ksmbd_close_fd_app_instance_id(char *app_instance_id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); +bool ksmbd_has_open_files(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); void ksmbd_launch_ksmbd_durable_scavenger(void); From 3f67e624e591747c2b2c9c607a76d79f7ffdcabc Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:39:28 +0900 Subject: [PATCH 4472/5214] ksmbd: propagate failed command status in related compounds In a related compound request, later commands can refer to the file handle from an earlier command using the related FID value. If the earlier command fails without producing a valid compound FID, the later related commands must fail with the same status instead of operating on an invalid or stale handle. smb2.compound.related4 sends CREATE followed by IOCTL, CLOSE and SET_INFO. The CREATE is expected to fail with STATUS_ACCESS_DENIED, and the remaining related commands are expected to return STATUS_ACCESS_DENIED as well. ksmbd only stored the compound FID on successful CREATE and did not remember failed compound statuses. Store the failed status in the work item and make related handle-based requests fail immediately with that status only when the compound FID is invalid. Also preserve and consume the related FID across successful FLUSH, READ and WRITE requests whose responses do not carry a file id. Keep a valid compound FID across non-close failures so later related commands can continue to use the handle. When extracting the FID from a successful READ, WRITE or FLUSH request, use the request structure matching the SMB2 command: READ and WRITE place PersistentFileId and VolatileFileId at a different offset than FLUSH, so a single smb2_flush_req cast can save the wrong value as compound_fid and make the following related request fail with STATUS_FILE_CLOSED (smb2.compound_async.write_write after smb2.compound_async.flush_flush). Only update the saved compound FID when the request carries a valid volatile FID. otherwise an all-ones related FID would overwrite the CREATE FID and break smb2.compound.related6. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/ksmbd_work.h | 1 + fs/smb/server/smb2pdu.c | 159 ++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index 5368430561fb..df0554a2c50d 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -60,6 +60,7 @@ struct ksmbd_work { u64 compound_fid; u64 compound_pfid; u64 compound_sid; + __le32 compound_status; const struct cred *saved_cred; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index f2ba52c7e325..df79533dc0a2 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -405,6 +405,59 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = STATUS_SUCCESS; + } else if ((req->Command == SMB2_FLUSH || + req->Command == SMB2_READ || + req->Command == SMB2_WRITE) && + rsp->Status == STATUS_SUCCESS) { + u64 volatile_id = KSMBD_NO_FID; + u64 persistent_id = KSMBD_NO_FID; + + if (req->Command == SMB2_FLUSH) { + struct smb2_flush_req *flush_req = + (struct smb2_flush_req *)req; + + volatile_id = flush_req->VolatileFileId; + persistent_id = flush_req->PersistentFileId; + } else if (req->Command == SMB2_READ) { + struct smb2_read_req *read_req = + (struct smb2_read_req *)req; + + volatile_id = read_req->VolatileFileId; + persistent_id = read_req->PersistentFileId; + } else { + struct smb2_write_req *write_req = + (struct smb2_write_req *)req; + + volatile_id = write_req->VolatileFileId; + persistent_id = write_req->PersistentFileId; + } + + if (has_file_id(volatile_id)) { + work->compound_fid = volatile_id; + work->compound_pfid = persistent_id; + work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = STATUS_SUCCESS; + } + } else if (req->Command == SMB2_CREATE) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = rsp->Status; + } else if (rsp->Status != STATUS_SUCCESS) { + work->compound_sid = le64_to_cpu(rsp->SessionId); + /* + * Only carry the failed status forward when the failing command + * was itself part of the related chain. An unrelated command + * that fails (e.g. a standalone request with a bad session id) + * must not seed the status for a following related command, + * which has to be evaluated on its own (and may legitimately + * fail with a different status such as INVALID_PARAMETER). The + * compound session id is still tracked so a following related + * command can validate it. + */ + if (req->Flags & SMB2_FLAGS_RELATED_OPERATIONS) + work->compound_status = rsp->Status; } len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; @@ -430,6 +483,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) ksmbd_debug(SMB, "related flag should be set\n"); work->compound_fid = KSMBD_NO_FID; work->compound_pfid = KSMBD_NO_FID; + work->compound_status = STATUS_SUCCESS; } memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; @@ -449,6 +503,19 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); } +static bool smb2_compound_has_failed(struct ksmbd_work *work, + struct smb2_hdr *rsp) +{ + if (!work->next_smb2_rcv_hdr_off || + has_file_id(work->compound_fid) || + work->compound_status == STATUS_SUCCESS) + return false; + + rsp->Status = work->compound_status; + smb2_set_err_rsp(work); + return true; +} + /** * is_chained_smb2_message() - check for chained command * @work: smb work containing smb request buffer @@ -4608,11 +4675,28 @@ int smb2_query_dir(struct ksmbd_work *work) unsigned char srch_flag; int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received smb2 query directory request\n"); WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + if (ksmbd_override_fsids(work)) { rsp->hdr.Status = STATUS_NO_MEMORY; smb2_set_err_rsp(work); @@ -4625,7 +4709,7 @@ int smb2_query_dir(struct ksmbd_work *work) goto err_out2; } - dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + dir_fp = ksmbd_lookup_fd_slow(work, id, pid); if (!dir_fp) { rc = -EBADF; goto err_out2; @@ -6088,6 +6172,9 @@ int smb2_query_info(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (ksmbd_override_fsids(work)) { rc = -ENOMEM; goto err_out; @@ -6192,6 +6279,9 @@ int smb2_close(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe close request\n"); @@ -6862,6 +6952,8 @@ int smb2_set_info(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -7093,6 +7185,8 @@ int smb2_read(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -7366,11 +7460,28 @@ int smb2_write(struct ksmbd_work *work) bool writethrough = false, is_rdma_channel = false; int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received smb2 write request\n"); WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe write request\n"); return smb2_write_pipe(work); @@ -7415,7 +7526,7 @@ int smb2_write(struct ksmbd_work *work) goto out; } - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + fp = ksmbd_lookup_fd_slow(work, id, pid); if (!fp) { err = -ENOENT; goto out; @@ -7509,13 +7620,30 @@ int smb2_flush(struct ksmbd_work *work) { struct smb2_flush_req *req; struct smb2_flush_rsp *rsp; + u64 id = KSMBD_NO_FID, pid = KSMBD_NO_FID; int err; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId); - err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + err = ksmbd_vfs_fsync(work, id, pid); if (err) goto out; @@ -7734,11 +7862,29 @@ int smb2_lock(struct ksmbd_work *work) LIST_HEAD(lock_list); LIST_HEAD(rollback_list); int prior_lock = 0, bkt; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "Received smb2 lock request\n"); - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); err = -ENOENT; @@ -8544,6 +8690,8 @@ int smb2_ioctl(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -9208,6 +9356,9 @@ int smb2_notify(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { rsp->hdr.Status = STATUS_INTERNAL_ERROR; smb2_set_err_rsp(work); From 7d258465ea49d82668f52de96f3f0c84727003e4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:39:59 +0900 Subject: [PATCH 4473/5214] ksmbd: validate handle for create or get object id FSCTL_CREATE_OR_GET_OBJECT_ID returned a dummy successful response without checking whether the request handle was valid. That let an invalid related compound handle succeed in smb2.compound.related5, although the client expected STATUS_FILE_CLOSED. Look up the file handle before building the object id response and fail with STATUS_FILE_CLOSED when the handle is invalid or already closed. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index df79533dc0a2..d3bd198ec938 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8789,6 +8789,15 @@ int smb2_ioctl(struct ksmbd_work *work) case FSCTL_CREATE_OR_GET_OBJECT_ID: { struct file_object_buf_type1_ioctl_rsp *obj_buf; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -EBADF; + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out2; + } + ksmbd_fd_put(work, fp); nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); obj_buf = (struct file_object_buf_type1_ioctl_rsp *) From e50a07437a9ef5a3b2efe414643e2cdcb6b2e644 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:40:37 +0900 Subject: [PATCH 4474/5214] ksmbd: preserve compound responses for chained errors set_smb2_rsp_status() resets the response iov and compound offsets before building an error response. That is fine for a single request, but it corrupts a compound response when an error is detected after an earlier compound element has already been completed. smb2.compound.invalid4 sends a READ as the first compound element and a bogus command as the second one. The READ response must remain in the compound response with STATUS_END_OF_FILE, followed by the bogus command response with STATUS_INVALID_PARAMETER. Resetting the response state for the second command breaks the compound framing and the client reports NT_STATUS_INVALID_NETWORK_RESPONSE. When setting an error for a chained command, update and pin only the current compound response slot instead of resetting the whole response. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d3bd198ec938..35f23b427bd1 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -246,6 +246,13 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) { struct smb2_hdr *rsp_hdr; + if (work->next_smb2_rcv_hdr_off) { + rsp_hdr = ksmbd_resp_buf_next(work); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); + return; + } + rsp_hdr = smb_get_msg(work->response_buf); rsp_hdr->Status = err; From c5db4de8988f1a621556ca5c4537f77b766ca07d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:41:08 +0900 Subject: [PATCH 4475/5214] ksmbd: return success for deferred final close ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table reference. If another in-flight request still holds a reference, the final close is deferred until that request drops its reference. The function currently returns -EINVAL in that deferred-final-close case because fp is cleared when the reference count does not reach zero. That turns a valid close into STATUS_FILE_CLOSED. smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then closes the same directory handle before receiving the find response. The query holds a reference while it builds the response, so close must mark the handle closed and return success even though final teardown is delayed. Track whether the handle was successfully transitioned to FP_CLOSED and return success when only the final close is deferred. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 98c5bac93d63..b617edef950a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -640,6 +640,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp; struct ksmbd_file_table *ft; + bool closed = false; if (!has_file_id(id)) return 0; @@ -654,6 +655,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; } @@ -661,7 +663,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) write_unlock(&ft->lock); if (!fp) - return -EINVAL; + return closed ? 0 : -EINVAL; __put_fd_final(work, fp); return 0; From affcd98fddc57d81648d8ede8546d31e4a3f8450 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:43:35 +0900 Subject: [PATCH 4476/5214] ksmbd: send pending interim for last compound I/O smb2.compound_async.write_write and smb2.compound_async.read_read expect the last I/O request in a compound request to become cancellable before its final response is received. smb clients mark a request cancellable after receiving an interim STATUS_PENDING response. ksmbd handled the last READ/WRITE synchronously and returned the final response directly, so the client never observed STATUS_PENDING and req->cancel.can_cancel remained false. For the last READ or WRITE in a compound request, register the work briefly as async and send a STATUS_PENDING interim response before continuing with the normal synchronous completion. The final READ/WRITE response remains unchanged. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 35f23b427bd1..cc9cd9297557 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7176,7 +7176,7 @@ int smb2_read(struct ksmbd_work *work) size_t length, mincount; ssize_t nbytes = 0, remain_bytes = 0; int err = 0; - bool is_rdma_channel = false; + bool is_rdma_channel = false, async_interim = false; unsigned int max_read_size = conn->vals->max_read_size; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; void *aux_payload_buf; @@ -7248,6 +7248,14 @@ int smb2_read(struct ksmbd_work *work) goto out; } + if (work->next_smb2_rcv_hdr_off && !req->hdr.NextCommand) { + err = setup_async_work(work, NULL, NULL); + if (err) + goto out; + smb2_send_interim_resp(work, STATUS_PENDING); + async_interim = true; + } + offset = le64_to_cpu(req->Offset); if (offset < 0) { err = -EINVAL; @@ -7283,6 +7291,8 @@ int smb2_read(struct ksmbd_work *work) kvfree(aux_payload_buf); rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); + if (async_interim) + release_async_work(work); ksmbd_fd_put(work, fp); return -ENODATA; } @@ -7317,6 +7327,8 @@ int smb2_read(struct ksmbd_work *work) kvfree(aux_payload_buf); goto out; } + if (async_interim) + release_async_work(work); /* * RDMA responses are transferred through channel buffers and encrypted * responses use the encryption transform, so only normal SMB transport @@ -7330,6 +7342,8 @@ int smb2_read(struct ksmbd_work *work) return 0; out: + if (async_interim) + release_async_work(work); if (err) { if (err == -EISDIR) rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; @@ -7465,6 +7479,7 @@ int smb2_write(struct ksmbd_work *work) ssize_t nbytes; char *data_buf; bool writethrough = false, is_rdma_channel = false; + bool async_interim = false; int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; @@ -7545,6 +7560,14 @@ int smb2_write(struct ksmbd_work *work) goto out; } + if (work->next_smb2_rcv_hdr_off && !req->hdr.NextCommand) { + err = setup_async_work(work, NULL, NULL); + if (err) + goto out; + smb2_send_interim_resp(work, STATUS_PENDING); + async_interim = true; + } + if (length > max_write_size) { ksmbd_debug(SMB, "limiting write size to max size(%u)\n", max_write_size); @@ -7593,10 +7616,15 @@ int smb2_write(struct ksmbd_work *work) err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); if (err) goto out; + if (async_interim) + release_async_work(work); ksmbd_fd_put(work, fp); return 0; out: + if (async_interim) + release_async_work(work); + if (err == -EAGAIN) rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; else if (err == -ENOSPC || err == -EFBIG) From 19043971c947d307c9fc76e8b5e750ce7140b486 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:44:09 +0900 Subject: [PATCH 4477/5214] ksmbd: honor stream delete sharing for base file smb2.streams.delete opens an alternate data stream without FILE_SHARE_DELETE and then tries to delete the base file. Windows rejects the base-file delete with STATUS_SHARING_VIOLATION while the stream handle is open. ksmbd tracks stream opens on the same ksmbd_inode as the base file, but the delete-on-close path only checked delete access on the base handle before marking the inode delete-pending. As a result, deleting the base file succeeded even though an open stream handle denied delete sharing. Add a helper to detect open stream handles on the same inode that do not allow FILE_SHARE_DELETE, and reject base-file delete pending and DELETE opens with a sharing violation in that case. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 9 +++++++++ fs/smb/server/vfs_cache.c | 27 +++++++++++++++++++++++++++ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 37 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index cc9cd9297557..a3ae37e8b24d 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3737,6 +3737,12 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } + if (!stream_name && daccess & FILE_DELETE_LE && + ksmbd_has_stream_without_delete_share(fp)) { + rc = -EPERM; + goto err_out; + } + if (file_present || created) path_put(&path); @@ -6758,6 +6764,9 @@ static int set_file_disposition_info(struct ksmbd_work *work, inode = file_inode(fp->filp); if (file_info->DeletePending) { + if (ksmbd_has_stream_without_delete_share(fp)) + return -ESHARE; + if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index b617edef950a..11b51320b96e 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -254,6 +254,33 @@ void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) up_write(&ci->m_lock); } +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp) +{ + struct ksmbd_file *prev_fp; + struct ksmbd_inode *ci = fp->f_ci; + bool ret = false; + + if (ksmbd_stream_fd(fp)) + return false; + + down_read(&ci->m_lock); + list_for_each_entry(prev_fp, &ci->m_fp_list, node) { + if (prev_fp == fp || !ksmbd_stream_fd(prev_fp)) + continue; + + if (file_inode(fp->filp) != file_inode(prev_fp->filp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE)) { + ret = true; + break; + } + } + up_read(&ci->m_lock); + + return ret; +} + void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info) { diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 52a0e8b1f79f..8aa87843025f 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -169,6 +169,7 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); int ksmbd_invalidate_durable_fd(unsigned long long id); bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp); int ksmbd_close_fd_app_instance_id(char *app_instance_id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); From 4687da7b28cff019a61d802afac44e2bf92edb98 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:44:40 +0900 Subject: [PATCH 4478/5214] ksmbd: reject empty-attribute synchronize-only create smb2.create.gentest checks each desired access bit independently and expects an open that requests only SYNCHRONIZE with CreateDisposition OPEN_IF and FileAttributes 0 to fail with STATUS_ACCESS_DENIED. Rejecting all SYNCHRONIZE-only opens is too broad: SYNCHRONIZE does not imply read, write, or delete data access, and smb2.sharemode.sharemode-access expects a SYNCHRONIZE-only open to succeed when it does not conflict with the existing share mode. Limit the rejection to the gentest create shape: SYNCHRONIZE-only access, OPEN_IF disposition, and no file attributes. Other synchronize-only opens are handled by the normal permission and share-mode checks. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a3ae37e8b24d..9a1308f32f45 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3332,6 +3332,13 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } + if (req->DesiredAccess == FILE_SYNCHRONIZE_LE && + req->CreateDisposition == FILE_OPEN_IF_LE && + !req->FileAttributes) { + rc = -EACCES; + goto err_out2; + } + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); From 0984b1f058fe1f501a04dea2c97897735be1d9f4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:45:07 +0900 Subject: [PATCH 4479/5214] ksmbd: tighten create file attribute validation smb2.create.gentest checks each create FileAttributes bit independently and expects FILE_ATTRIBUTE_INTEGRITY_STREAM and FILE_ATTRIBUTE_NO_SCRUB_DATA to be rejected with STATUS_INVALID_PARAMETER. ksmbd validates create FileAttributes against FILE_ATTRIBUTE_MASK, which includes those bits. It also rejects only requests that have no known attribute bit at all, so a request containing both known and unknown bits can pass validation. Use a create-specific attribute mask that excludes INTEGRITY_STREAM and NO_SCRUB_DATA, and reject any bit outside that mask. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 9a1308f32f45..d6001cdce085 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -57,6 +57,10 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) #define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) +#define SMB2_CREATE_FILE_ATTRIBUTE_MASK \ + (FILE_ATTRIBUTE_MASK & ~(FILE_ATTRIBUTE_INTEGRITY_STREAM | \ + FILE_ATTRIBUTE_NO_SCRUB_DATA)) + /** * check_session_id() - check for valid session id in smb header * @conn: connection instance @@ -3339,7 +3343,8 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } - if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + if (req->FileAttributes && + (req->FileAttributes & ~cpu_to_le32(SMB2_CREATE_FILE_ATTRIBUTE_MASK))) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; From 3b41d8b05dc89b1f3c53ca7f1a084c7c93712ebf Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:47:02 +0900 Subject: [PATCH 4480/5214] ksmbd: return requested create allocation size smb2.create.blob sends an SMB2_CREATE_ALLOCATION_SIZE create context with a 1MiB allocation size and expects the create response AllocationSize field to match the requested size. smb2.create.open additionally compares the AllocationSize returned in the CREATE response with the AllocationSize returned by FILE_ALL_INFORMATION on the same handle. ksmbd applies the allocation with fallocate(), but then fills both the create response and handle-based information from stat.blocks << 9. On filesystems such as ext4 this can include filesystem allocation rounding and metadata effects, causing a response larger than the SMB2 allocation size context and a disagreement between the two queries. Remember the requested allocation size while processing the create context, store the reported allocation size in struct ksmbd_file, and use it for both the create response and handle-based allocation size responses. Update the stored value when FILE_ALLOCATION_INFORMATION changes it, and fall back to stat.blocks << 9 when no allocation size context was provided. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 34 +++++++++++++++++++++++----------- fs/smb/server/vfs_cache.h | 1 + 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d6001cdce085..68d761690002 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3133,7 +3133,7 @@ int smb2_open(struct ksmbd_work *work) char *stream_name = NULL; bool file_present = false, created = false, already_permitted = false; int share_ret, need_truncate = 0; - u64 time; + u64 time, alloc_size = 0; umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; int iov_len = 0; @@ -3826,7 +3826,6 @@ int smb2_open(struct ksmbd_work *work) rc = PTR_ERR(az_req); goto err_out1; } else if (az_req) { - loff_t alloc_size; int err; if (le16_to_cpu(az_req->ccontext.DataOffset) + @@ -3876,6 +3875,8 @@ int smb2_open(struct ksmbd_work *work) else fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); fp->change_time = ksmbd_UnixTimeToNT(stat.ctime); + fp->allocation_size = S_ISDIR(stat.mode) ? 0 : + (alloc_size ?: stat.blocks << 9); if (req->FileAttributes || fp->f_ci->m_fattr == 0) fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); @@ -3926,8 +3927,19 @@ int smb2_open(struct ksmbd_work *work) time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); rsp->ChangeTime = cpu_to_le64(fp->change_time); - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); + /* + * The cached allocation size hides filesystem rounding for the + * requested allocation, but it can go stale when the file grows past + * it via writes (e.g. across a durable reconnect). Refresh it once the + * file exceeds the cached value, rounding the end of file up to the + * volume allocation unit (the filesystem block size, matching the + * SectorsPerAllocationUnit/BytesPerSector ksmbd advertises) rather than + * using the raw on-disk block count, which can include filesystem + * preallocation and metadata rounding. + */ + if (!S_ISDIR(stat.mode) && stat.size > fp->allocation_size) + fp->allocation_size = round_up(stat.size, stat.blksize); + rsp->AllocationSize = cpu_to_le64(fp->allocation_size); rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); rsp->FileAttributes = fp->f_ci->m_fattr; @@ -5236,7 +5248,7 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp, delete_pending = ksmbd_inode_pending_delete(fp); if (ksmbd_stream_fd(fp) == false) { - sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9); + sinfo->AllocationSize = cpu_to_le64(fp->allocation_size); sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { sinfo->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5317,8 +5329,7 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; if (ksmbd_stream_fd(fp) == false) { - file_info->AllocationSize = - cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5525,7 +5536,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->Attributes = fp->f_ci->m_fattr; if (ksmbd_stream_fd(fp) == false) { - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5658,7 +5669,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->Inode = cpu_to_le64(stat.ino); if (ksmbd_stream_fd(fp) == false) { file_info->EndOfFile = cpu_to_le64(stat.size); - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); } else { file_info->EndOfFile = cpu_to_le64(fp->stream.size); file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -6373,8 +6384,7 @@ int smb2_close(struct ksmbd_work *work) } rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); + rsp->AllocationSize = cpu_to_le64(fp->allocation_size); rsp->EndOfFile = cpu_to_le64(stat.size); rsp->Attributes = fp->f_ci->m_fattr; rsp->CreationTime = cpu_to_le64(fp->create_time); @@ -6707,6 +6717,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (size < alloc_blks * 512) i_size_write(inode, size); } + + fp->allocation_size = le64_to_cpu(file_alloc_info->AllocationSize); return 0; } diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 8aa87843025f..f85021c11d6e 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -98,6 +98,7 @@ struct ksmbd_file { __le32 cdoption; __u64 create_time; __u64 change_time; + __u64 allocation_size; __u64 itime; bool is_nt_open; From ba3cf6ee4f0eacc1f8c607b80188e3b32ef5e0e3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:47:56 +0900 Subject: [PATCH 4481/5214] ksmbd: apply create security descriptor first smb2.create.aclfile creates files with an SMB2_CREATE_SD_BUFFER create context and expects the resulting security descriptor to match the descriptor supplied by the client. ksmbd currently tries to inherit the parent DACL first and only parses the SMB2_CREATE_SD_BUFFER context when DACL inheritance fails. If inheritance succeeds, the explicit security descriptor supplied on create is ignored. This breaks create requests that include owner/group information in the security descriptor. Apply the create security descriptor first when the context is present. Fall back to the existing inherited/default ACL path only when no create security descriptor was supplied. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 68d761690002..6b84a8ea5b15 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3651,14 +3651,16 @@ int smb2_open(struct ksmbd_work *work) if (posix_acl_rc) ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - rc = smb_inherit_dacl(conn, &path, sess->user->uid, - sess->user->gid); - } + rc = smb2_create_sd_buffer(work, req, &path); + if (rc && rc != -ENOENT) + goto err_out; - if (rc) { - rc = smb2_create_sd_buffer(work, req, &path); + if (rc == -ENOENT) { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } if (rc) { if (posix_acl_rc) ksmbd_vfs_set_init_posix_acl(idmap, From 7db0da9915fdfd40156d01f20faac08f040fcfa3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:48:27 +0900 Subject: [PATCH 4482/5214] ksmbd: downgrade oplock after break timeout smb2.oplock.batch22a opens a file with a batch oplock and then issues a second open that waits for the oplock break timeout. After the timeout the second open should succeed, but the granted oplock level must be level II. When the break times out, oplock_break() returns -ENOENT after invalidating the previous opener. smb_grant_oplock() went straight to set_lev with the original requested oplock level, so the second open could be granted a new batch oplock. Downgrade the requested oplock to level II on the -ENOENT break-timeout path before granting the oplock to the new open. A break that completes because the previous owner closed its handle from the oplock break handler must be distinguished from a real timeout. smb2.oplock.batch7 closes the first handle during the break wait, and the second open is then expected to be granted the originally requested batch oplock. Return -EAGAIN from the non-lease break path when the previous opener closed during the break wait, recheck sharing in smb_grant_oplock(), and grant the requested oplock if the close removed the conflict. Real break timeouts still return -ENOENT and keep the downgrade to level II. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 0fddbaa5ba63..3f35ee7f7d00 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1130,7 +1130,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) - err = -ENOENT; + err = -EAGAIN; wake_up_oplock_break(brk_opinfo); return err; @@ -1434,8 +1434,19 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) ksmbd_invalidate_durable_fd(prev_fid); opinfo_put(prev_opinfo); - if (err == -ENOENT) + if (err == -EAGAIN) { + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (share_ret < 0) { + err = share_ret; + goto err_out; + } goto set_lev; + } + if (err == -ENOENT) { + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + goto set_lev; + } /* Check all oplock was freed by close */ else if (err < 0) goto err_out; From ec476c2580050ea050c562eeeb508519fc69ea21 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:49:05 +0900 Subject: [PATCH 4483/5214] ksmbd: avoid level II oplock break notification on unlink smb2_util_unlink() opens the target with FILE_DELETE_ON_CLOSE and then closes that handle. Other clients can also mark a file for delete with SMB2 SET_INFO FileDispositionInformation. When these unlink paths break existing SMB2 level II oplocks, ksmbd sends an unsolicited SMB2_OPLOCK_BREAK notification to none. This races with the synchronous CREATE or SET_INFO response expected by the client, and smbtorture reports NT_STATUS_INVALID_NETWORK_RESPONSE while running smb2.oplock.exclusive2. SMB2 level II oplock breaks do not require an acknowledgment in the delete path. Keep lease handling unchanged, but drop plain SMB2 level II oplocks locally for unlink requests without sending a break notification. Normal write/truncate paths still send the level II to none notification, preserving the behavior covered by smb2.oplock.levelII500. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 31 ++++++++++++++++++++++++------- fs/smb/server/oplock.h | 4 ++++ fs/smb/server/smb2pdu.c | 4 ++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 3f35ee7f7d00..5abeb90ebebb 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1546,7 +1546,7 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, */ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc, - bool send_interim) + bool send_interim, bool send_oplock_break) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; @@ -1593,10 +1593,15 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; - oplock_break(brk_op, - brk_op->is_lease && !is_trunc ? - SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, - send_interim && !sent_interim ? work : NULL); + if (!brk_op->is_lease && !send_oplock_break) { + brk_op->level = SMB2_OPLOCK_LEVEL_NONE; + brk_op->op_state = OPLOCK_STATE_NONE; + } else { + oplock_break(brk_op, + brk_op->is_lease && !is_trunc ? + SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, + send_interim && !sent_interim ? work : NULL); + } sent_interim = true; next: opinfo_put(brk_op); @@ -1610,7 +1615,19 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc) { - __smb_break_all_levII_oplock(work, fp, is_trunc, true); + __smb_break_all_levII_oplock(work, fp, is_trunc, true, true); +} + +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + __smb_break_all_levII_oplock(work, fp, is_trunc, false, true); +} + +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + __smb_break_all_levII_oplock(work, fp, 0, false, false); } /** @@ -1627,7 +1644,7 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) return; sent_break = smb_break_all_write_oplock(work, fp, 1); - __smb_break_all_levII_oplock(work, fp, 1, !sent_break); + __smb_break_all_levII_oplock(work, fp, 1, !sent_break, true); } /** diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 8d1943586246..3f581d22bb67 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -99,6 +99,10 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, struct lease_ctx_info *lctx, int share_ret); void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp); int opinfo_write_to_read(struct oplock_info *opinfo); int opinfo_read_handle_to_read(struct oplock_info *opinfo); int opinfo_write_to_none(struct oplock_info *opinfo); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6b84a8ea5b15..d4a40cede7bd 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3809,7 +3809,7 @@ int smb2_open(struct ksmbd_work *work) } if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { - smb_break_all_levII_oplock(work, fp, 0); + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_fd_set_delete_on_close(fp, file_info); } @@ -6796,7 +6796,7 @@ static int set_file_disposition_info(struct ksmbd_work *work, if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; - smb_break_all_levII_oplock(work, fp, 0); + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_set_inode_pending_delete(fp); } else { ksmbd_clear_inode_pending_delete(fp); From 854b4f8f80f879aceead300028faeda8c90b4b91 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:49:41 +0900 Subject: [PATCH 4484/5214] ksmbd: return oplock protocol error for level II ack SMB2 level II to none oplock breaks do not require an acknowledgment from the client. smb2.oplock.levelii500 intentionally acknowledges such a break and expects the server to reject it with STATUS_INVALID_OPLOCK_PROTOCOL. ksmbd drops the local level II oplock to none immediately after sending the break notification because it does not wait for an ACK. When the client then sends the invalid ACK, smb20_oplock_break_ack() sees that the oplock is not in OPLOCK_ACK_WAIT state and returns STATUS_INVALID_DEVICE_STATE before checking the current oplock level. If the oplock is already none when an unexpected SMB2 oplock break ACK arrives, report STATUS_INVALID_OPLOCK_PROTOCOL. Keep the existing STATUS_INVALID_DEVICE_STATE response for other unexpected non-wait states. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d4a40cede7bd..705cef655702 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9225,7 +9225,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) if (opinfo->op_state != OPLOCK_ACK_WAIT) { ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); - status = STATUS_INVALID_DEVICE_STATE; + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) + status = STATUS_INVALID_OPLOCK_PROTOCOL; + else + status = STATUS_INVALID_DEVICE_STATE; goto err_out; } From 43f21349427dc964bfc7aa450e7e560f292698bb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:50:12 +0900 Subject: [PATCH 4485/5214] ksmbd: normalize ungrantable lease states smb2.lease.request verifies which SMB2 lease state combinations are granted by the server. Requests for H-only, W-only, and HW leases are valid lease state bitmasks, but they are not grantable combinations and should be returned as lease state none. ksmbd only checked that the requested bits were inside the SMB2 lease state mask. As a result it could grant H-only, W-only, or HW requests and return non-zero lease states where the client expects no lease. Keep the bitmask validation, but normalize ungrantable combinations to zero before allocating or looking up the lease. The grantable combinations remain unchanged: R, RH, RW, and RHW. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 5abeb90ebebb..331ce10dbb66 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -29,6 +29,17 @@ static bool lease_state_valid(__le32 state) return !(state & ~SMB2_LEASE_STATE_MASK_LE); } +static __le32 lease_state_grantable(__le32 state) +{ + if (state == SMB2_LEASE_READ_CACHING_LE || + state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE) || + state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE) || + state == SMB2_LEASE_STATE_MASK_LE) + return state; + + return 0; +} + static bool lease_v2_flags_valid(__le32 flags) { return !(flags & ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE); @@ -1760,6 +1771,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (!lease_state_valid(lreq->req_state) || !lease_v2_flags_valid(lreq->flags)) goto err_out; + lreq->req_state = lease_state_grantable(lreq->req_state); if (lreq->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, SMB2_LEASE_KEY_SIZE); @@ -1777,6 +1789,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) lreq->duration = lc->lcontext.LeaseDuration; if (!lease_state_valid(lreq->req_state)) goto err_out; + lreq->req_state = lease_state_grantable(lreq->req_state); lreq->version = 1; } else goto err_out; From b35f8f898cd99d61ac9acbdc87ca3955922e956a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:50:41 +0900 Subject: [PATCH 4486/5214] ksmbd: break handle caching for share conflicts smb2.lease.break_twice first opens a file with an RHW lease and then tries a second open with restrictive sharing. That open must fail with a sharing violation, but the existing lease should be broken from RHW to RW because only handle caching conflicts with the requested sharing. ksmbd used the normal write-cache break calculation for this path, so RHW was broken to RH. The following successful open then did not generate the expected second break from RW to R. Pass share-conflict context into the lease break helper and, for lease breaks caused by sharing, drop only SMB2_LEASE_HANDLE_CACHING from the current lease state. Other break paths keep the existing write/truncate break behavior. A share-conflict break must also remain a single break. The triggering open fails with a sharing violation and is never granted, so there is no target oplock level to converge on. The lease break retry loop, however, keeps breaking while the lease level is still above req_op_level, which broke RHW all the way down to R in one open (lease_break_info.count became 2 instead of 1). Skip the again loop for share-conflict breaks so the sharing open produces exactly one RHW->RW break and the later successful open produces the separate RW->R break the test expects. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 331ce10dbb66..33e93084c3b5 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1035,7 +1035,7 @@ static void wait_lease_breaking(struct oplock_info *opinfo) } static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, - struct ksmbd_work *in_work) + struct ksmbd_work *in_work, bool share_break) { int err = 0; bool sent_interim = false; @@ -1073,6 +1073,10 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, * none. */ lease->new_state = SMB2_LEASE_NONE_LE; + } else if (share_break && + lease->state & SMB2_LEASE_HANDLE_CACHING_LE) { + lease->new_state = + lease->state & ~SMB2_LEASE_HANDLE_CACHING_LE; } else { if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) @@ -1121,7 +1125,13 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (wait_ack) wait_lease_breaking(brk_opinfo); - if (wait_ack && !err && + /* + * A break caused by a share-mode conflict only drops the + * conflicting caching bit and the triggering open still fails + * with a sharing violation, so it must stay a single break. + * Do not cascade down to req_op_level through the again loop. + */ + if (wait_ack && !err && !share_break && lease_break_needed(brk_opinfo, req_op_level, open_trunc)) goto again; @@ -1281,7 +1291,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); opinfo_put(opinfo); } } @@ -1322,7 +1332,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); opinfo_put(opinfo); } } @@ -1441,7 +1451,8 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_fid = prev_opinfo->fid; } - err = oplock_break(prev_opinfo, break_level, work); + err = oplock_break(prev_opinfo, break_level, work, + share_ret < 0 && prev_opinfo->is_lease); if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) ksmbd_invalidate_durable_fd(prev_fid); opinfo_put(prev_opinfo); @@ -1539,7 +1550,7 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, } brk_opinfo->open_trunc = is_trunc; - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false); sent_break = true; opinfo_put(brk_opinfo); @@ -1611,7 +1622,8 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, oplock_break(brk_op, brk_op->is_lease && !is_trunc ? SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, - send_interim && !sent_interim ? work : NULL); + send_interim && !sent_interim ? work : NULL, + false); } sent_interim = true; next: From 889d2e38943ad9ab253dfd7520e6d92867825e7d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:51:35 +0900 Subject: [PATCH 4487/5214] ksmbd: break conflicting-open leases only as far as needed smb2.lease.oplock and smb2.lease.breaking1 hold a lease and then issue a single conflicting open on the same file. The held lease must break one step to drop write caching (RWH->RH, RW->R) and then stop, so lease_break_info.count is 1 and the lease keeps its read/handle caching. ksmbd instead cascaded the break all the way down to none (e.g. RWH->RH->R->none), so the break count was 2 or 3 and the reported lease state ended at 0. Commit "chain pending lease breaks before waking waiters" forces break_level to SMB2_OPLOCK_LEVEL_NONE for any non-lease open against a handle-caching lease, which drives oplock_break()'s retry loop down to none even when only one open is contending. Drop that break_level override so a conflicting open breaks a lease only to its own compatible level (level II, i.e. RH/R). A deeper break is still required when a truncating open is also waiting behind the same lease break. smb2.lease.breaking3 keeps a normal open pending through RWH->RH and an overwrite open pending behind it, and expects the lease to continue RH->R->none before either open completes. The overwrite waiter sets open_trunc on the lease while it blocks on the pending break, so extend the retry loop to chain another break while that truncating waiter still needs the lease at none. The per-break open_trunc snapshot stays cleared, so the cascade steps down (RH->R->none) instead of collapsing straight to none, and the normal open stays pending until the lease is fully broken. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 33e93084c3b5..afd492be88fb 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1126,13 +1126,22 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (wait_ack) wait_lease_breaking(brk_opinfo); /* - * A break caused by a share-mode conflict only drops the - * conflicting caching bit and the triggering open still fails - * with a sharing violation, so it must stay a single break. - * Do not cascade down to req_op_level through the again loop. + * A share-mode conflict break only drops the conflicting + * caching bit; the triggering open fails with a sharing + * violation, so keep it to a single break. + * + * Otherwise chain another break while the lease is still + * incompatible with this open (req_op_level), or while a + * truncating waiter that arrived during the break still needs + * the lease dropped to none. open_trunc snapshotted for this + * break stays cleared, so the next state is computed from the + * lease state and the cascade steps down (e.g. RH->R->none) + * instead of collapsing straight to none. */ if (wait_ack && !err && !share_break && - lease_break_needed(brk_opinfo, req_op_level, open_trunc)) + (lease_break_needed(brk_opinfo, req_op_level, open_trunc) || + (brk_opinfo->open_trunc && + lease->state != SMB2_LEASE_NONE_LE))) goto again; wake_up_oplock_break(brk_opinfo); @@ -1426,10 +1435,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_op_has_lease = prev_opinfo->is_lease; if (prev_op_has_lease) prev_op_state = prev_opinfo->o_lease->state; - if (prev_op_has_lease && !lctx && - prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) - break_level = SMB2_OPLOCK_LEVEL_NONE; - if (share_ret < 0 && prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { err = share_ret; From 256257279c187386ceb18540a1f8e19252fd01f6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:52:55 +0900 Subject: [PATCH 4488/5214] ksmbd: validate :: stream type against directory create smb2.streams.dir opens ::$DATA with FILE_DIRECTORY_FILE and expects STATUS_NOT_A_DIRECTORY, then opens ::$DATA without it and expects STATUS_FILE_IS_A_DIRECTORY. Commit "treat unnamed DATA stream as base file" canonicalizes the ::$DATA suffix to a NULL stream name so the open continues through the base-file path. That skipped the stream/directory type validation, which was guarded by "if (stream_name)", so opening a directory's ::$DATA stream with FILE_DIRECTORY_FILE incorrectly returned STATUS_OK and a plain open of it no longer reported STATUS_FILE_IS_A_DIRECTORY. parse_stream_name() still records the explicit $DATA type in s_type even when it clears stream_name. Run the data-stream vs directory validation whenever s_type is DATA_STREAM, not only when stream_name is set, so the canonicalized ::$DATA open is rejected with the correct status. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 705cef655702..c0d3062e4d93 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3453,7 +3453,14 @@ int smb2_open(struct ksmbd_work *work) rc = 0; } - if (stream_name) { + /* + * An explicit ::$DATA suffix names the unnamed data stream and is + * canonicalized to a NULL stream name (base file), but the request + * still has to be validated against the data-stream type, e.g. opening + * ::$DATA with FILE_DIRECTORY_FILE must fail with + * STATUS_NOT_A_DIRECTORY. + */ + if (stream_name || s_type == DATA_STREAM) { if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { if (s_type == DATA_STREAM) { rc = -EIO; From be939e11c4724d1de3650e8bafd4c3583d9684b2 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:57:55 +0900 Subject: [PATCH 4489/5214] ksmbd: treat read-control opens as stat opens only for leases A second open that requests only metadata-level access must not break the existing caching state. ksmbd already skips the break for such opens via fp->attrib_only (FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES and FILE_SYNCHRONIZE). An open requesting only READ_CONTROL (reading the security descriptor) must be treated differently depending on the existing caching state. smbtorture smb2.lease.statopen4 expects a read-control open NOT to break a caching lease, while smb2.oplock.statopen1 expects the same open to break a batch oplock. So READ_CONTROL is a stat open for leases but not for oplocks. Extend the stat-open break-skip in smb_grant_oplock() to also cover a read-control-only open, but only when the existing holder is a lease. The global fp->attrib_only flag (used for share-mode, rename and truncate decisions) is left unchanged so oplock behaviour is preserved. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index afd492be88fb..99cbd7aa03fc 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -312,6 +312,18 @@ void opinfo_put(struct oplock_info *opinfo) free_opinfo(opinfo); } +static bool ksmbd_inode_has_lease(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo = opinfo_get_list(ci); + bool is_lease; + + if (!opinfo) + return false; + is_lease = opinfo->is_lease; + opinfo_put(opinfo); + return is_lease; +} + static void opinfo_add(struct oplock_info *opinfo, struct ksmbd_file *fp) { struct ksmbd_inode *ci = fp->f_ci; @@ -1402,10 +1414,22 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (!opinfo_count(fp)) goto set_lev; - /* grant none-oplock if second open is trunc */ - if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + /* + * A stat open that only requests metadata access must not break the + * existing caching state. READ_CONTROL (reading the security + * descriptor) does not conflict with a lease, but it does conflict + * with an oplock, so only treat a read-control-only open as a stat + * open when the existing holder is a lease. + */ + if (fp->cdoption != FILE_OVERWRITE_IF_LE && fp->cdoption != FILE_OVERWRITE_LE && - fp->cdoption != FILE_SUPERSEDE_LE) { + fp->cdoption != FILE_SUPERSEDE_LE && + (fp->attrib_only || + (!(fp->daccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | + FILE_SYNCHRONIZE_LE | + FILE_READ_CONTROL_LE)) && + ksmbd_inode_has_lease(ci)))) { req_op_level = SMB2_OPLOCK_LEVEL_NONE; goto set_lev; } From 6b375be0b4e1be89e9a817880515311503a19114 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:59:06 +0900 Subject: [PATCH 4490/5214] ksmbd: start file id allocation at 1 ksmbd allocates both the volatile id (per-session file table) and the persistent id (global file table) with idr_alloc_cyclic() starting at 0. The first open after the module loads therefore gets volatile id 0 and persistent id 0, and ksmbd returns an SMB2 FileId of {0, 0} in the create response. Clients treat an all-zero FileId as a null handle. smbtorture's smb2_util_handle_empty() considers {0, 0} empty, so tests that guard the close with it (e.g. smb2.oplock.statopen1, smb2.lease.statopen*) never close that first handle. The leaked open keeps the inode's oplock count non-zero, so a later batch oplock request on the same file is downgraded to level II and the test fails. Start the id allocation at 1 (KSMBD_START_FID) so no handle is ever assigned a {0, 0} FileId, matching the behaviour of other SMB servers. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 3 ++- fs/smb/server/vfs_cache.h | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 11b51320b96e..7495166b0262 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -992,7 +992,8 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, idr_preload(KSMBD_DEFAULT_GFP); write_lock(&ft->lock); - ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); + ret = idr_alloc_cyclic(ft->idr, fp, KSMBD_START_FID, INT_MAX - 1, + GFP_NOWAIT); if (ret >= 0) { id = ret; ret = 0; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index f85021c11d6e..287f3e675cd3 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -23,7 +23,12 @@ #define FILE_GENERIC_WRITE 0x120116 #define FILE_GENERIC_EXECUTE 0X1200a0 -#define KSMBD_START_FID 0 +/* + * Start volatile/persistent file id allocation at 1. A file id of 0 yields an + * SMB2 FileId of {0, 0}, which clients (e.g. Windows, Samba) treat as a null + * handle and never close, leaking the open on the server. + */ +#define KSMBD_START_FID 1 #define KSMBD_NO_FID (INT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) From 5a7f4d6d8e7fc9c3b67412f1b8e5b56c9aec21af Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:59:56 +0900 Subject: [PATCH 4491/5214] ksmbd: sleep interruptibly in the durable handle scavenger The durable handle scavenger kthread waits up to DURABLE_HANDLE_MAX_TIMEOUT (300 seconds) between scans using wait_event_timeout(), which sleeps in TASK_UNINTERRUPTIBLE. When there are no durable handles pending expiry the task stays in D state far longer than 120 seconds, so the hung task detector prints a bogus "task ksmbd-durable-s blocked for more than 120 seconds" warning with a backtrace, even though the thread is only idle. Use wait_event_interruptible_timeout() so the thread sleeps in TASK_INTERRUPTIBLE, which the hung task detector ignores. This also suits the already-freezable kthread. Treat a negative return (e.g. -ERESTARTSYS) like a timeout when recomputing the next wake interval. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 7495166b0262..fde22742d193 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1344,10 +1344,10 @@ static int ksmbd_durable_scavenger(void *dummy) if (try_to_freeze()) continue; - remaining_jiffies = wait_event_timeout(dh_wq, + remaining_jiffies = wait_event_interruptible_timeout(dh_wq, ksmbd_durable_scavenger_alive() == false, __msecs_to_jiffies(min_timeout)); - if (remaining_jiffies) + if ((long)remaining_jiffies > 0) min_timeout = jiffies_to_msecs(remaining_jiffies); else min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; From 474fd91f3828a89dd7dc0a862f77f14e9f9240ff Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 21:21:01 +0900 Subject: [PATCH 4492/5214] ksmbd: fix UBSAN array-index-out-of-bounds in decode_compress_ctxt() decode_compress_ctxt() walks CompressionAlgorithms[] using the client supplied CompressionAlgorithmCount. That field is declared in struct smb2_compression_capabilities_context as a fixed 4-element array, but the number of algorithms is actually variable and clients such as Windows advertise more than four (e.g. LZ77, LZ77+Huffman, LZNT1, Pattern_V1 and LZ4). The on-wire context length is already validated, so the access is within the received buffer, but indexing the statically sized [4] array makes UBSAN report an out-of-bounds access: UBSAN: array-index-out-of-bounds in smb2pdu.c:1122:48 index 4 is out of range for type '__le16 [4]' Call Trace: smb2_handle_negotiate+0xda7/0xde0 [ksmbd] ksmbd_smb_negotiate_common+0x27b/0x3e0 [ksmbd] smb2_negotiate_request+0x14/0x20 [ksmbd] handle_ksmbd_work+0x181/0x500 [ksmbd] Walk the algorithms through a pointer so the fixed-array bounds check is not applied, while keeping the existing length validation that bounds the loop to the data actually received. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c0d3062e4d93..5859fa68bb84 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1094,6 +1094,7 @@ static __le32 decode_compress_ctxt(struct ksmbd_conn *conn, int ctxt_len) { int alg_cnt, algs_size, i; + __le16 *algs; if (sizeof(struct smb2_neg_context) + 10 > ctxt_len) { pr_err("Invalid SMB2_COMPRESSION_CAPABILITIES context length\n"); @@ -1118,8 +1119,15 @@ static __le32 decode_compress_ctxt(struct ksmbd_conn *conn, return STATUS_INVALID_PARAMETER; } + /* + * CompressionAlgorithms[] is declared as a fixed 4-element array, but + * the actual element count is variable (clients such as Windows may + * advertise more). The on-wire length was validated above, so walk the + * algorithms through a pointer to avoid a fixed-array bounds check. + */ + algs = pneg_ctxt->CompressionAlgorithms; for (i = 0; i < alg_cnt; i++) { - __le16 alg = pneg_ctxt->CompressionAlgorithms[i]; + __le16 alg = algs[i]; /* * LZ77 is the required general-purpose codec. Pattern_V1 is an From 4429b56506f45891d445f4dc4c8a22b3ec9b12de Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 22 Jun 2026 10:16:41 +0900 Subject: [PATCH 4493/5214] ksmbd: increase SMB3_DEFAULT_TRANS_SIZE from 1MB to 4MB This patch raises `SMB3_DEFAULT_TRANS_SIZE` to 4MB to align it with `smb2 max read/write`. This allows better I/O negotiation with modern clients and improves sequential read/write performance on high-speed networks. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 3bed676bb5ad..c2512dbcdec8 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -23,7 +23,7 @@ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ #define SMB21_DEFAULT_IOSIZE (1024 * 1024) -#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (4 * 1024 * 1024) #define SMB3_MIN_IOSIZE (64 * 1024) #define SMB3_MAX_IOSIZE (8 * 1024 * 1024) #define SMB3_MAX_MSGSIZE (4 * 4096) From c78a4e41ab5ead6193ad8a2dd92e8906bae659fa Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 17 Jun 2026 02:05:18 +0000 Subject: [PATCH 4494/5214] hdlc_ppp: sync per-proto timers before freeing hdlc state Each PPP control protocol (LCP/IPCP/IPV6CP) embedded in struct ppp registers a timer via timer_setup(). That struct ppp is the hdlc->state allocation, which detach_hdlc_protocol() frees with kfree() in both teardown paths: unregister_hdlc_device() and the re-attach inside attach_hdlc_protocol(). The ppp proto never registered a .detach callback, so detach_hdlc_protocol() performs no timer synchronization before the kfree(). The only cancel, timer_delete(&proto->timer) in ppp_cp_event(), is partial (it does not wait for a running callback) and only runs on the ->CLOSED transition; ppp_stop()/ppp_close() do not sync either. A ppp_timer callback already executing (blocked on ppp->lock) survives the kfree and then dereferences proto->state / ppp->lock in freed memory, leading to a use-after-free. Fix this by adding a .detach helper that calls timer_shutdown_sync() on every per-proto timer. detach_hdlc_protocol() invokes proto->detach(dev) before kfree(hdlc->state), so timer_shutdown_sync() now runs on both free paths. timer_shutdown_sync() is used instead of timer_delete_sync() because the keepalive path re-arms the timer through add_timer()/mod_timer() and shutdown blocks any re-activation during teardown. Initialize the per-protocol timers in ppp_ioctl() when the protocol is attached, and remove the now-redundant timer_setup() from ppp_start(), so that the timers are initialized exactly once at attach time and ppp_timer_release() never operates on uninitialized timer_list structures. attach_hdlc_protocol() uses kmalloc() (not kzalloc), so struct ppp's protos[i].timer is uninitialized garbage until the first timer_setup(); without this init-at-attach, attaching the PPP protocol without ever bringing the device up would leave timer_shutdown_sync() operating on uninitialized memory in .detach. Moving the init out of ppp_start() (which only runs on NETDEV_UP) into the attach path makes the initialization unconditional and avoids initializing the same timer_list twice. This bug was found by static analysis. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260617020518.116319-1-fanwu01@zju.edu.cn Signed-off-by: Jakub Kicinski --- drivers/net/wan/hdlc_ppp.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 159295c4bd6d..302ed27944e7 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -619,7 +619,6 @@ static void ppp_start(struct net_device *dev) struct proto *proto = &ppp->protos[i]; proto->dev = dev; - timer_setup(&proto->timer, ppp_timer, 0); proto->state = CLOSED; } ppp->protos[IDX_LCP].pid = PID_LCP; @@ -639,6 +638,15 @@ static void ppp_close(struct net_device *dev) ppp_tx_flush(); } +static void ppp_timer_release(struct net_device *dev) +{ + struct ppp *ppp = get_ppp(dev); + int i; + + for (i = 0; i < IDX_COUNT; i++) + timer_shutdown_sync(&ppp->protos[i].timer); +} + static struct hdlc_proto proto = { .start = ppp_start, .stop = ppp_stop, @@ -647,6 +655,7 @@ static struct hdlc_proto proto = { .ioctl = ppp_ioctl, .netif_rx = ppp_rx, .module = THIS_MODULE, + .detach = ppp_timer_release, }; static const struct header_ops ppp_header_ops = { @@ -657,7 +666,7 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) { hdlc_device *hdlc = dev_to_hdlc(dev); struct ppp *ppp; - int result; + int i, result; switch (ifs->type) { case IF_GET_PROTO: @@ -685,6 +694,8 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) return result; ppp = get_ppp(dev); + for (i = 0; i < IDX_COUNT; i++) + timer_setup(&ppp->protos[i].timer, ppp_timer, 0); spin_lock_init(&ppp->lock); ppp->req_timeout = 2; ppp->cr_retries = 10; From 46c3b8191aad3d032776bf3bebf03efdf5f4b905 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 18 Jun 2026 21:53:34 -0700 Subject: [PATCH 4495/5214] ipv6: Fix null-ptr-deref in fib6_nh_mtu_change(). fib6_nh_mtu_change() re-fetches idev via __in6_dev_get(arg->dev) and dereferences idev->cnf.mtu6 without a NULL check. addrconf_ifdown() clears dev->ip6_ptr with RCU_INIT_POINTER() after rt6_disable_ip() has released tb6_lock, so the RA-driven MTU walk can observe a NULL idev and oops. The caller rt6_mtu_change_route() guards its own __in6_dev_get(), but this re-fetch is unguarded; nexthop-backed routes survive addrconf_ifdown()'s flush, so the walk still reaches it after ip6_ptr is nulled. Return 0 when idev is NULL, matching rt6_mtu_change_route() and the fib6_mtu() fix in commit 5ad509c1fdad ("ipv6: Fix null-ptr-deref in fib6_mtu()."). Oops: general protection fault, ... KASAN: null-ptr-deref in range [0x00000000000002a8-0x00000000000002af] RIP: 0010:fib6_nh_mtu_change+0x203/0x990 rt6_mtu_change_route+0x141/0x1d0 __fib6_clean_all+0xd0/0x160 rt6_mtu_change+0xb4/0x100 ndisc_router_discovery+0x24b5/0x2cb0 icmpv6_rcv+0x12e9/0x1710 ipv6_rcv+0x39b/0x410 Fixes: c0b220cf7d80 ("ipv6: Refactor exception functions") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619045334.2427073-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/ipv6/route.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6361ad2fcf77..a1301334da48 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5055,6 +5055,9 @@ static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg) struct inet6_dev *idev = __in6_dev_get(arg->dev); u32 mtu = f6i->fib6_pmtu; + if (!idev) + return 0; + if (mtu >= arg->mtu || (mtu < arg->mtu && mtu == idev->cnf.mtu6)) fib6_metric_set(f6i, RTAX_MTU, arg->mtu); From 40529e58629baa9ce72143cb46cf1b3d2ca0d465 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 19 Jun 2026 12:15:38 -0700 Subject: [PATCH 4496/5214] eth: bnxt: improve the timing of stats Kernel selftests wait 1.25x of the promised stats refresh time (as read from ethtool -c). bnxt reports 1sec by default, but the stats update process has two steps. First device DMAs the new values, then the service task performs update in full-width SW counters. So the worst case delay is actually 2x. Note that the behavior is different for ring stats and port stats. Port stats are fetched synchronously by the service worker, so there's no risk of doubling up the delay there. The problem of stale stats impacts not only tests but real workloads which monitor egress bandwidth of a NIC. The inaccuracy causes double counting in the next cycle and spurious overload alarms. Try to read from the DMA buffer more aggressively, to mitigate timing issues between DMA and service task. The SW update should be cheap. Fixes: 51f307856b60 ("bnxt_en: Allow statistics DMA to be configurable using ethtool -C.") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260619191538.104165-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 48 ++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 5 ++ .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 055e93a417b6..7513618793da 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -10530,7 +10530,7 @@ static void bnxt_accumulate_stats(struct bnxt_stats_mem *stats) stats->hw_masks, stats->len / 8, false); } -static void bnxt_accumulate_all_stats(struct bnxt *bp) +static void bnxt_accumulate_ring_stats(struct bnxt *bp) { struct bnxt_stats_mem *ring0_stats; bool ignore_zero = false; @@ -10553,6 +10553,10 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) ring0_stats->hw_masks, ring0_stats->len / 8, ignore_zero); } +} + +static void bnxt_accumulate_port_stats(struct bnxt *bp) +{ if (bp->flags & BNXT_FLAG_PORT_STATS) { struct bnxt_stats_mem *stats = &bp->port_stats; __le64 *hw_stats = stats->hw_stats; @@ -10575,6 +10579,41 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) } } +static void bnxt_accumulate_all_stats(struct bnxt *bp) +{ + bnxt_accumulate_ring_stats(bp); + bnxt_accumulate_port_stats(bp); +} + +/* Re-accumulate ring stats from DMA buffers if stale. + * uAPIs for reading sw_stats should call this first. + * + * We promise user space update frequency of bp->stats_coal_ticks but + * the update is a two step process - first device updates the DMA buffer, + * then we have to update from that buffer to driver stats in the service work. + * Worst case we would be 2x off from the desired frequency. + * Sync the stats sooner, if stale. The 20% threshold was chosen arbitrarily. + * + * Ideally we would split the user-configured time into two portions, + * i.e. also lower the DMA period by the 20%. But the DMA timer seems to have + * too coarse granularity to play such tricks. + */ +void bnxt_sync_ring_stats(struct bnxt *bp) +{ + unsigned long stale; + + if (!netif_running(bp->dev) || !bp->stats_coal_ticks) + return; + + spin_lock(&bp->stats_lock); + stale = usecs_to_jiffies(bp->stats_coal_ticks / 5); + if (time_after_eq(jiffies, bp->stats_updated_jiffies + stale)) { + bnxt_accumulate_ring_stats(bp); + bp->stats_updated_jiffies = jiffies; + } + spin_unlock(&bp->stats_lock); +} + static int bnxt_hwrm_port_qstats(struct bnxt *bp, u8 flags) { struct hwrm_port_qstats_input *req; @@ -13577,6 +13616,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) return; } + bnxt_sync_ring_stats(bp); bnxt_get_ring_stats(bp, stats); bnxt_add_prev_stats(bp, stats); @@ -14753,7 +14793,10 @@ static void bnxt_sp_task(struct work_struct *work) if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) { bnxt_hwrm_port_qstats(bp, 0); bnxt_hwrm_port_qstats_ext(bp, 0); + spin_lock(&bp->stats_lock); bnxt_accumulate_all_stats(bp); + bp->stats_updated_jiffies = jiffies; + spin_unlock(&bp->stats_lock); } if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) { @@ -15488,6 +15531,7 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) INIT_DELAYED_WORK(&bp->fw_reset_task, bnxt_fw_reset_task); spin_lock_init(&bp->ntp_fltr_lock); + spin_lock_init(&bp->stats_lock); #if BITS_PER_LONG == 32 spin_lock_init(&bp->db_lock); #endif @@ -16056,6 +16100,7 @@ static void bnxt_get_queue_stats_rx(struct net_device *dev, int i, if (!bp->bnapi) return; + bnxt_sync_ring_stats(bp); cpr = &bp->bnapi[i]->cp_ring; sw = cpr->stats.sw_stats; @@ -16084,6 +16129,7 @@ static void bnxt_get_queue_stats_tx(struct net_device *dev, int i, if (!bp->tx_ring) return; + bnxt_sync_ring_stats(bp); bnapi = bp->tx_ring[bp->tx_ring_map[i]].bnapi; sw = bnapi->cp_ring.stats.sw_stats; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 6d312259f852..6335dfc14c98 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2620,6 +2620,10 @@ struct bnxt { #define BNXT_MIN_STATS_COAL_TICKS 250000 #define BNXT_MAX_STATS_COAL_TICKS 1000000 + /* Protects stats_updated_jiffies and writes to sw_stats */ + spinlock_t stats_lock; + unsigned long stats_updated_jiffies; + struct work_struct sp_task; unsigned long sp_event; #define BNXT_RX_NTP_FLTR_SP_EVENT 1 @@ -3027,6 +3031,7 @@ void bnxt_reenable_sriov(struct bnxt *bp); void bnxt_close_nic(struct bnxt *, bool, bool); void bnxt_get_ring_drv_stats(struct bnxt *bp, struct bnxt_total_ring_drv_stats *stats); +void bnxt_sync_ring_stats(struct bnxt *bp); bool bnxt_rfs_capable(struct bnxt *bp, bool new_rss_ctx); int bnxt_dbg_hwrm_rd_reg(struct bnxt *bp, u32 reg_off, u16 num_words, u32 *reg_buf); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 56d74a3c24b7..62bc9cae613c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -606,6 +606,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, goto skip_ring_stats; } + bnxt_sync_ring_stats(bp); tpa_stats = bnxt_get_num_tpa_ring_stats(bp); for (i = 0; i < bp->cp_nr_rings; i++) { struct bnxt_napi *bnapi = bp->bnapi[i]; From b72f0db64205d9ce462038ba995d5d31eff32dc1 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 19 Jun 2026 21:27:20 +0000 Subject: [PATCH 4497/5214] ipv4: fib: Don't ignore error route in local/main tables. When CONFIG_IP_MULTIPLE_TABLES is enabled but no rule is added, fib_lookup() performs route lookup directly on two tables. Since the first lookup does not properly bail out, the result of an error route in the merged local/main table could be overwritten by another route in the default table: # unshare -n # ip link set lo up # ip route add 192.168.0.0/24 dev lo table 253 # ip route add unreachable 192.168.0.0/24 # ip route get 192.168.0.1 192.168.0.1 dev lo table default uid 0 cache Once a random rule is added, the error route is respected: # ip rule add table 0 # ip rule del table 0 # ip route get 192.168.0.1 RTNETLINK answers: No route to host Let's fix the inconsistent behaviour. Fixes: f4530fa574df ("ipv4: Avoid overhead when no custom FIB rules are installed.") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619212753.3367244-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- include/net/ip_fib.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index a71a98505650..c63a3c4967ae 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -374,7 +374,7 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { struct fib_table *tb; - int err = -ENETUNREACH; + int err = -EAGAIN; flags |= FIB_LOOKUP_NOREF; if (net->ipv4.fib_has_custom_rules) @@ -388,17 +388,16 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, if (tb) err = fib_table_lookup(tb, flp, res, flags); - if (!err) + if (err != -EAGAIN) goto out; tb = rcu_dereference_rtnl(net->ipv4.fib_default); if (tb) err = fib_table_lookup(tb, flp, res, flags); -out: if (err == -EAGAIN) err = -ENETUNREACH; - +out: rcu_read_unlock(); return err; From a986fde914d88af47eb78fd29c5d1af7952c3500 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 11:53:50 +0530 Subject: [PATCH 4498/5214] bnx2x: fix potential memory leak in bnx2x_alloc_mem_bp() If the allocation of fp[i].tpa_info fails, the error path will not free the struct bnx2x_fastpath allocated earlier, as it is not linked to the bp structure yet. Fix that by linking it immediately after allocation. Cc: stable@vger.kernel.org Fixes: 15192a8cf8a8 ("bnx2x: Split the FP structure") Signed-off-by: Abdun Nihaal Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260620062402.89549-1-nihaal@cse.iitm.ac.in Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 19e078479b0d..5b2640bd31c3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4748,6 +4748,7 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) fp = kzalloc_objs(*fp, bp->fp_array_size); if (!fp) goto alloc_err; + bp->fp = fp; for (i = 0; i < bp->fp_array_size; i++) { fp[i].tpa_info = kzalloc_objs(struct bnx2x_agg_info, @@ -4756,8 +4757,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) goto alloc_err; } - bp->fp = fp; - /* allocate sp objs */ bp->sp_objs = kzalloc_objs(struct bnx2x_sp_objs, bp->fp_array_size); if (!bp->sp_objs) From 245043dfc2101e7dc6268bf123b75305a91e4e00 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Fri, 19 Jun 2026 21:12:06 +0800 Subject: [PATCH 4499/5214] net: airoha: Fix TX scheduler queue mask loop upper bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In airoha_qdma_set_chan_tx_sched(), the loop clearing queue mask was using AIROHA_NUM_TX_RING (32) instead of AIROHA_NUM_QOS_QUEUES (8). Each channel has 8 queues, and TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i) computes BIT(i + (channel * 8)). With i ranging 0..31, this causes: - channel 0: clears bit 0..31 (all 4 channels) instead of 0..7 - channel 1: clears bit 8..31 (channels 1-3) instead of 8..15 - channel 2: clears bit 16..31 (channels 2-3) instead of 16..23 - channel 3: clears bit 24..31 (channel 3 only) - correct by accident While BIT(32+) on arm64 produces 64-bit values truncated to 0 in u32 mask parameter, the loop still incorrectly clears queues within the same channel beyond queue 7. Even though this is functionally harmless (the register resets to 0 and is only ever cleared, never set — so clearing extra bits is a no-op), the loop bound is semantically wrong and should be fixed for correctness and clarity. Fix by using AIROHA_NUM_QOS_QUEUES (8) as the loop upper bound. Fixes: ef1ca9271313 ("net: airoha: Add sched HTB offload support") Acked-by: Lorenzo Bianconi Signed-off-by: Wayen Yan Link: https://patch.msgid.link/178187479434.2400840.1312143943526335838@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 3370c3df7c10..2eab69c81dcf 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2395,7 +2395,7 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, struct airoha_gdm_dev *dev = netdev_priv(netdev); int i; - for (i = 0; i < AIROHA_NUM_TX_RING; i++) + for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++) airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel), TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); From e38fec239d923de5bfb65f7fce15ca52c5a3aa7f Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Sun, 21 Jun 2026 16:01:18 -0400 Subject: [PATCH 4500/5214] selftests: drv-net: so_txtime: relax variance bounds The net-next-hw spinners on netdev.bots.linux.dev observe failing so-txtime-py tests. A review of stdout shows most failures to be due to exceeding the 4ms grace period. All I saw were within 8ms. So increase to that. Double the bounds from 4 to 8ms. This is still is small enough to differentiate the delays programmed by the test, 10 and 20ms. Fixes: 5c6baef3885c ("selftests: drv-net: convert so_txtime to drv-net") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20260610170651.1b644001@kernel.org/ Signed-off-by: Willem de Bruijn Link: https://patch.msgid.link/20260621200137.1564776-1-willemdebruijn.kernel@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/so_txtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c index 75f3beef13d9..55a386f3d1b9 100644 --- a/tools/testing/selftests/drivers/net/so_txtime.c +++ b/tools/testing/selftests/drivers/net/so_txtime.c @@ -37,7 +37,7 @@ static int cfg_clockid = CLOCK_TAI; static uint16_t cfg_port = 8000; -static int cfg_variance_us = 4000; +static int cfg_variance_us = 8000; static bool cfg_machine_slow; static uint64_t cfg_start_time_ns; static int cfg_mark; From 9b249f5ffbeda24c57e6c56ed896c5ca4fc70549 Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Thu, 18 Jun 2026 21:47:48 +0800 Subject: [PATCH 4501/5214] md/raid5: use stripe state snapshot in break_stripe_batch_list() The patch just suppress KCSAN noise. No functional change. RAID-5 can group multi full-stripe-write aka stripe_head into a batch aka batch_list, with one head_sh leading them. Call break_stripe_batch_list() when the batch is finished, or, a stripe has to be dropped out of the batch. break_stripe_batch_list() reads stripe state several times while request paths can update thost state words concurrently with lockless bitops, which reported by KCSAN. Use a snapshot to guarantees that the value used for warning, copying, and handle checks is internally consistent at current read moment. KCSAN report: ============================================== BUG: KCSAN: data-race in __add_stripe_bio / break_stripe_batch_list write (marked) to 0xffff8e89d4f0b988 of 8 bytes by task 4323 on cpu 3: __add_stripe_bio+0x35e/0x400 raid5_make_request+0x6ac/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 read to 0xffff8e89d4f0b988 of 8 bytes by task 4290 on cpu 4: break_stripe_batch_list+0x3ce/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260618134748.1168360-1-chencheng@fnnas.com Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index f35c2a7b2be1..6d982c54f2d1 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4852,31 +4852,35 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, { struct stripe_head *sh, *next; int i; + unsigned long state; list_for_each_entry_safe(sh, next, &head_sh->batch_list, batch_list) { list_del_init(&sh->batch_list); - WARN_ONCE(sh->state & ((1 << STRIPE_ACTIVE) | - (1 << STRIPE_SYNCING) | - (1 << STRIPE_REPLACED) | - (1 << STRIPE_DELAYED) | - (1 << STRIPE_BIT_DELAY) | - (1 << STRIPE_FULL_WRITE) | - (1 << STRIPE_BIOFILL_RUN) | - (1 << STRIPE_COMPUTE_RUN) | - (1 << STRIPE_DISCARD) | - (1 << STRIPE_BATCH_READY) | - (1 << STRIPE_BATCH_ERR)), - "stripe state: %lx\n", sh->state); - WARN_ONCE(head_sh->state & ((1 << STRIPE_DISCARD) | - (1 << STRIPE_REPLACED)), - "head stripe state: %lx\n", head_sh->state); + state = READ_ONCE(sh->state); + WARN_ONCE(state & ((1 << STRIPE_ACTIVE) | + (1 << STRIPE_SYNCING) | + (1 << STRIPE_REPLACED) | + (1 << STRIPE_DELAYED) | + (1 << STRIPE_BIT_DELAY) | + (1 << STRIPE_FULL_WRITE) | + (1 << STRIPE_BIOFILL_RUN) | + (1 << STRIPE_COMPUTE_RUN) | + (1 << STRIPE_DISCARD) | + (1 << STRIPE_BATCH_READY) | + (1 << STRIPE_BATCH_ERR)), + "stripe state: %lx\n", state); + + state = READ_ONCE(head_sh->state); + WARN_ONCE(state & ((1 << STRIPE_DISCARD) | + (1 << STRIPE_REPLACED)), + "head stripe state: %lx\n", state); set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS | (1 << STRIPE_PREREAD_ACTIVE) | (1 << STRIPE_ON_UNPLUG_LIST)), - head_sh->state & (1 << STRIPE_INSYNC)); + state & (1 << STRIPE_INSYNC)); sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; @@ -4889,8 +4893,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->dev[i].flags = head_sh->dev[i].flags & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } - if (handle_flags == 0 || - sh->state & handle_flags) + + state = READ_ONCE(sh->state); + if (handle_flags == 0 || (state & handle_flags)) set_bit(STRIPE_HANDLE, &sh->state); raid5_release_stripe(sh); } @@ -4900,7 +4905,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); - if (head_sh->state & handle_flags) + + state = READ_ONCE(head_sh->state); + if (state & handle_flags) set_bit(STRIPE_HANDLE, &head_sh->state); } From 55b77337bdd088c77461588e5ec094421b89911b Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 19 Jun 2026 12:10:13 +0800 Subject: [PATCH 4502/5214] md/raid5: avoid R5_Overlap races while breaking stripe batches KCSAN report a race in break_stripe_batch_list() vs. raid5_make_request() on sh->dev[i].flags (plain word write vs. atomic bit op).. and .. one possible scenario is: CPU1 CPU2 break_stripe_batch_list(sh1) -> handle sh2 -> lock(sh2) -> sh2->batch_head = NULL -> unlock(sh2) -> test_and_clear_bit(R5_Overlap, sh2->dev[i].flags) -> wake_up_bit(sh2->dev[i].flags) raid5_make_request() -> add_all_stripe_bios(sh2) -> lock(sh2) -> stripe_bio_overlaps(sh2) returns true batch_head is NULL, so new bio overlap exist bio on sh2 -> true -> set_bit(R5_Overlap, sh2->dev[i].flags) -> unlock(sh2) -> wait_on_bit(sh2->dev[i].flags) -> sh2->dev[i].flags = sh1->dev[i].flags & ~R5_Overlap No wait_up_bit(), CPU2 could be wait_on_bit() forever... Fix by : - Expand the protect zone. - Use batch_head's device flag's snaphot when no held head_sh->stripe_lock. - Move sh/head_sh->batch_head = NULL to the end of protected zone , and , any concurrent add_all_stripe_bios() grabs sh->stripe_lock now either: - see batch_head != null, and , is rejected by stripe_bio_overlaps() under the lock (no R5_Overlap wait ) , or , - sees batch_head == NULL, only after dev[i].flags has already been set and the prior R5_Overlap waiters worken. KCSAN report: ================================================ BUG: KCSAN: data-race in break_stripe_batch_list / raid5_make_request write (marked) to 0xffff8e89c8117548 of 8 bytes by task 4042 on cpu 0: raid5_make_request+0xea0/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 __x64_sys_io_submit+0xf7/0x220 x64_sys_call+0x1907/0x1c60 do_syscall_64+0x130/0x570 entry_SYSCALL_64_after_hwframe+0x76/0x7e read to 0xffff8e89c8117548 of 8 bytes by task 4010 on cpu 5: break_stripe_batch_list+0x249/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 md_thread+0x15a/0x2d0 kthread+0x1e3/0x220 ret_from_fork+0x37a/0x410 ret_from_fork_asm+0x1a/0x30 value changed: 0x0000000000000019 -> 0x0000000000000099 --> R5_Overlap Fixes: fb642b92c267 ("md/raid5: duplicate some more handle_stripe_clean_event code in break_stripe_batch_list") Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260619041013.1207148-1-chencheng@fnnas.com Signed-off-by: Yu Kuai --- drivers/md/raid5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6d982c54f2d1..0c5c9fb0606e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4885,14 +4885,14 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; spin_lock_irq(&sh->stripe_lock); - sh->batch_head = NULL; - spin_unlock_irq(&sh->stripe_lock); for (i = 0; i < sh->disks; i++) { if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up_bit(&sh->dev[i].flags, R5_Overlap); - sh->dev[i].flags = head_sh->dev[i].flags & + sh->dev[i].flags = READ_ONCE(head_sh->dev[i].flags) & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } + sh->batch_head = NULL; + spin_unlock_irq(&sh->stripe_lock); state = READ_ONCE(sh->state); if (handle_flags == 0 || (state & handle_flags)) @@ -4900,11 +4900,11 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, raid5_release_stripe(sh); } spin_lock_irq(&head_sh->stripe_lock); - head_sh->batch_head = NULL; - spin_unlock_irq(&head_sh->stripe_lock); for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); + head_sh->batch_head = NULL; + spin_unlock_irq(&head_sh->stripe_lock); state = READ_ONCE(head_sh->state); if (state & handle_flags) From 12091470c6b4c1c14b2de12dcbae2ada6cb6d20b Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 13:03:03 +0000 Subject: [PATCH 4503/5214] bpf: Disable xfrm_decode_session hook attachment BPF LSM programs can currently attach to xfrm_decode_session(). That hook may return an error, but security_skb_classify_flow() calls it from a void path and triggers BUG_ON() if an error is returned. Disable BPF attachment to the hook to prevent a BPF LSM program from turning packet classification into a full panic. Fixes: 9e4e01dfd325 ("bpf: lsm: Implement attach, detach and execution") Signed-off-by: Bradley Morgan Link: https://lore.kernel.org/r/20260619130305.27779-1-include@grrlz.net Signed-off-by: Alexei Starovoitov --- kernel/bpf/bpf_lsm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 564071a92d7d..1433809bb166 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -51,6 +51,9 @@ BTF_ID(func, bpf_lsm_key_getsecurity) #ifdef CONFIG_AUDIT BTF_ID(func, bpf_lsm_audit_rule_match) #endif +#ifdef CONFIG_SECURITY_NETWORK_XFRM +BTF_ID(func, bpf_lsm_xfrm_decode_session) +#endif BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_SET_END(bpf_lsm_disabled_hooks) From 5ed62a96e06be4e94b8296b7932afee550a70e04 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 21:10:33 -0500 Subject: [PATCH 4504/5214] Input: goodix - clamp the device-reported contact count goodix_ts_read_input_report() copies the number of touch points reported by the device into an on-stack buffer u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; which is sized for at most GOODIX_MAX_CONTACTS (10) contacts. The only runtime check bounds the per-interrupt count against ts->max_touch_num, but that value is taken verbatim from a 4-bit field of the device configuration block and is never clamped: ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; The nibble can be 0..15, so a malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can advertise up to 15 contacts. goodix_ts_read_input_report() then accepts a touch_num of up to 15 and the second goodix_i2c_read() writes ts->contact_size * (touch_num - 1) bytes past the one-contact header into point_data - up to 30 bytes (45 with the 9-byte report format) beyond the 92-byte buffer: a stack out-of-bounds write. Clamp max_touch_num to GOODIX_MAX_CONTACTS, the number of contacts point_data[] is sized for, when reading it from the configuration. Fixes: a7ac7c95d468 ("Input: goodix - use max touch number from device config") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260612-b4-disp-6844625d-v1-1-df0aed080c9d@proton.me Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/goodix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 2c48ad8ee2e6..03efc0c792c2 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -1057,7 +1057,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) } ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03; - ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; + ts->max_touch_num = min(ts->config[MAX_CONTACTS_LOC] & 0x0f, + GOODIX_MAX_CONTACTS); x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]); y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]); From 0e9943d2e4c63496b6ca84bc66fd3c71d40558e2 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 20:47:50 -0700 Subject: [PATCH 4505/5214] Input: iforce - bound the device-reported force-feedback effect index iforce_process_packet() handles a status report (packet id 0x02) by taking a force-feedback effect index straight from the device wire and using it to address the per-effect state array: i = data[1] & 0x7f; if (data[1] & 0x80) { if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) ... } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { ... } The index is masked only with 0x7f, so it ranges 0..127, but core_effects[] holds only IFORCE_EFFECTS_MAX (32) entries. For an index of 32..127 the test_and_set_bit()/test_and_clear_bit() is an out-of-bounds single-bit read-modify-write past the array. core_effects[] is the second-to-last member of struct iforce, so the write lands in the trailing members and beyond the embedding kzalloc()'d iforce_serio / iforce_usb object. data[1] is unvalidated device payload on both transports (the USB interrupt endpoint and serio), and the status path is not gated on force feedback being present, so a malicious or counterfeit device can set or clear a bit at an attacker-chosen offset past the object. Reject an out-of-range index instead of indexing with it. Bound against the array dimension IFORCE_EFFECTS_MAX rather than dev->ff->max_effects so the check guarantees memory safety regardless of how many effects the device registered. A legitimate "effect started/stopped" status always carries an index below IFORCE_EFFECTS_MAX, so well-formed devices are unaffected; the neighbouring mark_core_as_ready() loop is already bounded and is left untouched. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260613-b4-disp-4828d263-v1-1-02320e1a89dd@proton.me Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-packets.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index fd1cd731d781..effa76bfd8f9 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -186,14 +186,18 @@ void iforce_process_packet(struct iforce *iforce, /* Check if an effect was just started or stopped */ i = data[1] & 0x7f; - if (data[1] & 0x80) { - if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report play event */ - input_report_ff_status(dev, i, FF_STATUS_PLAYING); + if (i < IFORCE_EFFECTS_MAX) { + if (data[1] & 0x80) { + if (!test_and_set_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); + } + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report stop event */ + input_report_ff_status(dev, i, FF_STATUS_STOPPED); } - } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report stop event */ - input_report_ff_status(dev, i, FF_STATUS_STOPPED); } for (j = 3; j < len; j += 2) From ef166ce08801c5237662868d9ec0331d53a38ece Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 6 Jun 2026 15:04:20 -0700 Subject: [PATCH 4506/5214] Input: stop force-feedback timer when unregistering input devices Memoryless force-feedback devices use a timer to manage playback of effects. When a driver for such a device is unbound (or the device is unregistered for other reasons), the driver typically frees its private data synchronously. However, the input_dev structure (and its associated force-feedback structures, including the timer) is only freed when the last user closes the corresponding device node. If userspace keeps the device node open while the device is unregistered (e.g., during driver unbind), the force-feedback timer can still fire after the driver's private data has been freed. Introduce a new 'stop' callback to struct ff_device, and call it from input_unregister_device() before the device is deleted. Implement this callback for memoryless devices and synchronously shut down the timer to ensure it is stopped and cannot be rearmed once unregistration happens. Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov --- drivers/input/ff-memless.c | 27 +++++++++++++++++++++------ drivers/input/input.c | 3 +++ include/linux/input.h | 3 +++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 937370d04928..d1fefd1dfc0d 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -484,17 +484,31 @@ static void ml_ff_destroy(struct ff_device *ff) struct ml_device *ml = ff->private; /* - * Even though we stop all playing effects when tearing down - * an input device (via input_device_flush() that calls into - * input_ff_flush() that stops and erases all effects), we - * do not actually stop the timer, and therefore we should - * do it here. + * The timer is normally shut down in ml_ff_stop() when the device + * is unregistered. However, we still shut it down here as a safety + * net and for cases where the device was never registered (e.g. + * error paths during probe). */ - timer_delete_sync(&ml->timer); + timer_shutdown_sync(&ml->timer); kfree(ml->private); } +static void ml_ff_stop(struct ff_device *ff) +{ + struct ml_device *ml = ff->private; + + /* + * Even though we stop all playing effects when tearing down an + * input device (by the way of evdev calling input_flush_device() + * that calls into input_ff_flush() that stops and erases all + * effects), we do not actually shutdown the timer, and therefore + * we should do it here to prevent it firing after the input + * device is unregistered and its associated resources are freed. + */ + timer_shutdown_sync(&ml->timer); +} + /** * input_ff_create_memless() - create memoryless force-feedback device * @dev: input device supporting force-feedback @@ -529,6 +543,7 @@ int input_ff_create_memless(struct input_dev *dev, void *data, ff->playback = ml_ff_playback; ff->set_gain = ml_ff_set_gain; ff->destroy = ml_ff_destroy; + ff->stop = ml_ff_stop; /* we can emulate periodic effects with RUMBLE */ if (test_bit(FF_RUMBLE, ff->ffbit)) { diff --git a/drivers/input/input.c b/drivers/input/input.c index 39d9d2b1e3ca..cf6fecea79b8 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -2212,6 +2212,9 @@ static void __input_unregister_device(struct input_dev *dev) input_wakeup_procfs_readers(); } + if (dev->ff && dev->ff->stop) + dev->ff->stop(dev->ff); + device_del(&dev->dev); } diff --git a/include/linux/input.h b/include/linux/input.h index 06ca62328db1..3022bb730898 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -543,6 +543,8 @@ extern const struct class input_class; * @set_autocenter: Called to auto-center device * @destroy: called by input core when parent input device is being * destroyed + * @stop: called by input core when parent input device is being + * unregistered * @private: driver-specific data, will be freed automatically * @ffbit: bitmap of force feedback capabilities truly supported by * device (not emulated like ones in input_dev->ffbit) @@ -571,6 +573,7 @@ struct ff_device { void (*set_autocenter)(struct input_dev *dev, u16 magnitude); void (*destroy)(struct ff_device *); + void (*stop)(struct ff_device *); void *private; From df2b818fa009c10ff6ba875a1663ff001cda9558 Mon Sep 17 00:00:00 2001 From: Ranjan Kumar Date: Mon, 22 Jun 2026 22:31:05 -0700 Subject: [PATCH 4507/5214] Input: elan_i2c - prevent division by zero and arithmetic underflow The Elan I2C touchpad driver queries the device for its physical dimensions and trace counts to calculate the device resolution and width. However, if the device firmware or device tree provides invalid zero values for x_traces or y_traces, it results in a fatal division-by-zero exception leading to a kernel panic during device probe. Add checks to ensure these parameters are non-zero before performing the division. If invalid trace values are detected, fall back to a safe default of 1. Additionally, prevent an arithmetic underflow in the touch reporting logic. Previously, if the calculated or fallback width was smaller than ETP_FWIDTH_REDUCE (90), the subtraction would underflow, resulting in a massive unsigned integer being reported to userspace. Clamp the adjusted width to a minimum of 0 to safely handle small physical dimensions and fallback scenarios. Completing the probe with safe fallback values ensures the sysfs nodes are created, keeping the firmware update path intact so a recovery firmware can be flashed to the device. Fixes: 6696777c6506 ("Input: add driver for Elan I2C/SMbus touchpad") Fixes: e3a9a1290688 ("Input: elan_i2c - do not query the info if they are provided") Signed-off-by: Ranjan Kumar Link: https://patch.msgid.link/20260612060339.3829666-1-kumarranja@chromium.org Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 36 ++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 69a473636613..f93dd545d66b 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -428,8 +428,17 @@ static int elan_query_device_parameters(struct elan_tp_data *data) if (error) return error; } - data->width_x = data->max_x / x_traces; - data->width_y = data->max_y / y_traces; + + if (!x_traces || !y_traces) { + dev_warn(&client->dev, + "invalid trace numbers: x=%u, y=%u\n", + x_traces, y_traces); + data->width_x = 1; + data->width_y = 1; + } else { + data->width_x = data->max_x / x_traces; + data->width_y = data->max_y / y_traces; + } if (device_property_read_u32(&client->dev, "touchscreen-x-mm", &x_mm) || @@ -443,8 +452,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) data->x_res = elan_convert_resolution(hw_x_res, data->pattern); data->y_res = elan_convert_resolution(hw_y_res, data->pattern); } else { - data->x_res = (data->max_x + 1) / x_mm; - data->y_res = (data->max_y + 1) / y_mm; + if (unlikely(x_mm == 0 || y_mm == 0)) { + dev_warn(&client->dev, + "invalid physical dimensions: x_mm=%u, y_mm=%u\n", + x_mm, y_mm); + data->x_res = 1; + data->y_res = 1; + } else { + data->x_res = (data->max_x + 1) / x_mm; + data->y_res = (data->max_y + 1) / y_mm; + } } if (device_property_read_bool(&client->dev, "elan,clickpad")) @@ -956,6 +973,7 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, if (data->report_features & ETP_FEATURE_REPORT_MK) { unsigned int mk_x, mk_y, area_x, area_y; + int adj_width_x, adj_width_y; u8 mk_data = high_precision ? packet[ETP_MK_DATA_OFFSET + contact_num] : finger_data[3]; @@ -967,8 +985,14 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, * To avoid treating large finger as palm, let's reduce * the width x and y per trace. */ - area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE); - area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE); + + adj_width_x = data->width_x > ETP_FWIDTH_REDUCE ? + data->width_x - ETP_FWIDTH_REDUCE : 0; + adj_width_y = data->width_y > ETP_FWIDTH_REDUCE ? + data->width_y - ETP_FWIDTH_REDUCE : 0; + + area_x = mk_x * adj_width_x; + area_y = mk_y * adj_width_y; input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, From a6ac4e24c1a8a533bb61035184fdcc7eede4cc8d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:35:18 -0700 Subject: [PATCH 4508/5214] Input: mms114 - fix touch indexing for MMS134S and MMS136 The MMS134S and MMS136 touch controllers have an event size of 6 bytes rather than 8 bytes. When __mms114_read_reg() reads the touch data packet from the device into the touch buffer, the events are packed tightly at 6-byte intervals. However, the driver iterates through the events using standard C array indexing (touch[index]), where each element is sizeof(struct mms114_touch) (8 bytes) apart. As a result, any touch events beyond the first one are read from incorrect offsets and parsed improperly. Fix this by explicitly calculating the byte offset for each touch event based on the device's specific event size. Fixes: 53fefdd1d3a3 ("Input: mms114 - support MMS136") Fixes: ab108678195f ("Input: mms114 - support MMS134S") Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Reviewed-by: Bryam Vargas Link: https://patch.msgid.link/20260616050912.1531241-1-dmitry.torokhov@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 4d748a13408d..53ad35d61d47 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -217,7 +217,9 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) struct mms114_data *data = dev_id; struct i2c_client *client = data->client; struct mms114_touch touch[MMS114_MAX_TOUCH]; + struct mms114_touch *t; int packet_size; + int event_size; int touch_size; int index; int error; @@ -234,9 +236,11 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) - touch_size = packet_size / MMS136_EVENT_SIZE; + event_size = MMS136_EVENT_SIZE; else - touch_size = packet_size / MMS114_EVENT_SIZE; + event_size = MMS114_EVENT_SIZE; + + touch_size = packet_size / event_size; error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); @@ -244,18 +248,20 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) goto out; for (index = 0; index < touch_size; index++) { - switch (touch[index].type) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); + + switch (t->type) { case MMS114_TYPE_TOUCHSCREEN: - mms114_process_mt(data, touch + index); + mms114_process_mt(data, t); break; case MMS114_TYPE_TOUCHKEY: - mms114_process_touchkey(data, touch + index); + mms114_process_touchkey(data, t); break; default: dev_err(&client->dev, "Wrong touch type (%d)\n", - touch[index].type); + t->type); break; } } From 069cfe3de2a5e16069485893cd04665ab769c1d8 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 28 Apr 2026 11:09:17 +0200 Subject: [PATCH 4509/5214] netfilter: nf_nat: avoid invalid nat_net pointer use on failed nf_nat_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We ran into below KASAN splat, which is mostly uninteresting, beside for having nf_nat_register_fn() in the call chain as a cause for the offending access: ================================================================== BUG: KASAN: slab-out-of-bounds in nf_nat_register_fn+0x5f9/0x640 Read of size 8 at addr ffff890031e54c20 by task iptables/9510 CPU: 0 UID: 0 PID: 9510 Comm: iptables Not tainted 6.18.18-grsec-full-20260320181326 #1 PREEMPT(voluntary) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: […] dump_stack_lvl+0xee/0x160 ffff88004117eeb8 […] print_report+0x6e/0x640 ffff88004117eee0 […] ? __phys_addr+0x8e/0x140 ffff88004117eef0 […] ? kasan_addr_to_slab+0x51/0xe0 ffff88004117ef08 […] ? complete_report_info+0xec/0x1c0 ffff88004117ef20 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef48 […] kasan_report+0xbc/0x140 ffff88004117ef50 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef90 […] nf_nat_register_fn+0x5f9/0x640 ffff88004117eff8 […] ? nf_nat_icmp_reply_translation+0x6e0/0x6e0 ffff88004117f070 […] nf_tables_register_hook.part.0+0xa0/0x220 ffff88004117f080 […] nf_tables_addchain.constprop.0+0x1054/0x1fc0 ffff88004117f0b8 […] ? nft_chain_lookup.part.0+0x4ce/0xac0 ffff88004117f130 […] ? nf_tables_abort+0x3d80/0x3d80 ffff88004117f190 […] ? nf_tables_dumpreset_obj+0x100/0x100 ffff88004117f1c8 […] ? nft_table_lookup.part.0+0x255/0x300 ffff88004117f310 […] ? nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f358 […] nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f360 […] ? nf_tables_addchain.constprop.0+0x1fc0/0x1fc0 ffff88004117f458 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f488 […] ? lock_acquire+0x16f/0x320 ffff88004117f490 […] ? find_held_lock+0x3b/0xe0 ffff88004117f4b0 […] ? __nla_parse+0x45/0x80 ffff88004117f500 […] nfnetlink_rcv_batch+0xbca/0x19a0 ffff88004117f550 […] ? nfnetlink_net_exit_batch+0x120/0x120 ffff88004117f618 […] ? __sanitizer_cov_trace_switch+0x63/0xe0 ffff88004117f720 […] ? gr_acl_handle_mmap+0x1c4/0x320 ffff88004117f7c0 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f7e8 […] ? gr_is_capable+0x6f/0xe0 ffff88004117f830 […] ? __nla_parse+0x45/0x80 ffff88004117f860 […] ? skb_pull+0x103/0x1a0 ffff88004117f880 […] nfnetlink_rcv+0x3db/0x4a0 ffff88004117f8b0 […] ? nfnetlink_rcv_batch+0x19a0/0x19a0 ffff88004117f8d8 […] ? netlink_lookup+0xe2/0x240 ffff88004117f900 […] netlink_unicast+0x74b/0xb00 ffff88004117f930 […] ? netlink_attachskb+0xb20/0xb20 ffff88004117f980 […] ? __check_object_size+0x3e/0xaa0 ffff88004117f998 […] ? security_netlink_send+0x51/0x160 ffff88004117f9c8 […] netlink_sendmsg+0xa03/0x1200 ffff88004117f9f8 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fa70 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fac8 […] ? ____sys_sendmsg+0xe2a/0x1040 ffff88004117faf8 […] ____sys_sendmsg+0xe2a/0x1040 ffff88004117fb00 […] ? kernel_recvmsg+0x300/0x300 ffff88004117fb60 […] ? reacquire_held_locks+0xe9/0x260 ffff88004117fbc8 […] ___sys_sendmsg+0x138/0x200 ffff88004117fbf8 […] ? do_recvmmsg+0x7e0/0x7e0 ffff88004117fc30 […] ? lockdep_hardirqs_on_prepare+0x101/0x1e0 ffff88004117fc50 […] ? lock_acquire+0x16f/0x320 ffff88004117fd20 […] ? lock_acquire+0x16f/0x320 ffff88004117fd58 […] ? find_held_lock+0x3b/0xe0 ffff88004117fd70 […] __sys_sendmsg+0x17a/0x260 ffff88004117fdc8 […] ? __sys_sendmsg_sock+0x80/0x80 ffff88004117fdf0 […] ? syscall_trace_enter+0x15e/0x2c0 ffff88004117fe98 […] do_syscall_64+0x7d/0x400 ffff88004117fec8 […] entry_SYSCALL_64_safe_stack+0x4a/0x60 ffff88004117fef8 ================================================================== The out-of-bounds report, though, is a red herring as it is for an access that shouldn't have happened in the first place. When nf_nat_init() fails to register its BPF kfuncs, it'll unwind and, among others, call unregister_pernet_subsys() to deregister its per-net ops. This makes the previously allocated net id available for reuse by the next caller of register_pernet_subsys(), in our case, synproxy. However, 'nat_net_id' will still hold the previously allocated value. If nf_nat.o gets build as a module, all this doesn't matter. A failed initialization routine makes the module fail to load and any dependent module won't be able to load either. However, if nf_nat.o is built-in, a failing init won't /completely/ make its functionality unavailable to dependent modules, namely the code and static data is still there, free to be called by modules like nft_chain_nat.ko. Case in point, nft_chain_nat registers hooks that'll call into nf_nat which, in our case, failed to initialize and therefore won't have a valid net id nor related net_nat object any more. Code in nf_nat, namely nf_nat_register_fn() and nf_nat_unregister_fn(), still making use of the reallocated net id, lead to a type confusion as the call to net_generic() will no longer return memory belonging to an object suited to fit 'struct nat_net' but 'struct synproxy_net' instead. The latter is only 24 bytes on 64-bit systems, much smaller than struct nat_net which is 176 bytes, perfectly explaining the OOB KASAN report. Detect and handle a failed nf_nat_init() by testing the 'nf_nat_hook' pointer which will be reset to NULL on initialization errors to prevent the usage of an invalid nat_net pointer. As this check is only needed when nf_nat.o is built-in, guard it by '#ifndef MODULE...'. Fixes: cbc1dd5b659f ("netfilter: nf_nat: Fix possible memory leak in nf_nat_init()") Signed-off-by: Mathias Krause Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_nat_core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 2bbf5163c0e2..63ff6b4d5d21 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1181,6 +1181,16 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, struct nf_hook_ops *nat_ops; int i, ret; +#ifndef MODULE + /* If nf_nat_core is built-in and nf_nat_init() fails, dependent + * modules like nft_chain_nat.ko may still call this function. + * However, nat_net would be invalid, likely pointing to some other + * per-net structure. + */ + if (WARN_ON_ONCE(!nf_nat_hook)) + return -EOPNOTSUPP; +#endif + if (WARN_ON_ONCE(pf >= ARRAY_SIZE(nat_net->nat_proto_net))) return -EINVAL; From c8b6f36f766991e3ebebec6596daee4b04dcbc49 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 14 May 2026 16:16:28 +0200 Subject: [PATCH 4510/5214] netfilter: nf_conncount: prevent connlimit drops for early confirmed ct Commit 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") introduced a regression where packets for valid connections are dropped when using connlimit for soft-limiting scenarios. The issue occurs when a new connection reuses a socket currently in the TIME_WAIT state. In this scenario, the connection tracking entry is evaluated as already confirmed. Previously, __nf_conncount_add() assumed that if a connection was confirmed and did not originate from the loopback interface, it should skip the addition and return -EEXIST. Skipping the addition triggers a garbage collection run that cleans up the TIME_WAIT connection. Consequently, the active connection count drops to 0, which xt_connlimit mishandles, leading to the false rejection of the perfectly valid new connection. Fix this by replacing the interface check with protocol-agnostic state checks. We now skip the tree insertion and preserve the lockless garbage collection optimization only if the connection is IPS_ASSURED. This allows early-confirmed setup packets (such as reused TIME_WAIT sockets or locally generated SYN-ACKs) to be properly evaluated and counted without falsely dropping. The goto check_connections path is maintained to ensure these setup packets are deduplicated correctly. This has been tested with slowhttptest and HTTP server configured locally to ensure we are not breaking soft-limiting scenarios for local or external connections. In addition, it was tested with a OVS zone limit too. Fixes: 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") Reported-by: Alejandro Olivan Alvarez Closes: https://lore.kernel.org/netfilter-devel/177349610461.3071718.4083978280323144323@eldamar.lan/ Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conncount.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index dd67004a5cc0..91582069f6d2 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -183,17 +183,16 @@ static int __nf_conncount_add(struct net *net, return -ENOENT; if (ct && nf_ct_is_confirmed(ct)) { - /* local connections are confirmed in postrouting so confirmation - * might have happened before hitting connlimit + /* Connection is confirmed but might still be in the setup phase. + * Only skip the tracking if it is fully assured. This guarantees + * that setup packets or retransmissions are properly counted and + * deduplicated. */ - if (skb->skb_iif != LOOPBACK_IFINDEX) { + if (test_bit(IPS_ASSURED_BIT, &ct->status)) { err = -EEXIST; goto out_put; } - /* this is likely a local connection, skip optimization to avoid - * adding duplicates from a 'packet train' - */ goto check_connections; } From 84460b644329e25809b4a6d9279d6359d7fd8ebc Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jun 2026 14:33:23 +0200 Subject: [PATCH 4511/5214] netfilter: flowtable: Validate iph->ihl in nf_flow_ip4_tunnel_proto() Add sanity check for iph->ihl field in nf_flow_ip4_tunnel_proto() before using it to compute the header size, avoiding out-of-bounds access with malformed IP headers. While at it, use iph->protocol instead of the hardcoded IPPROTO_IPIP constant when setting ctx->tun.proto and reference ctx->tun.hdr_size when updating ctx->offset. Fixes: ab427db178858 ("netfilter: flowtable: Add IPIP rx sw acceleration") Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_ip.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index e7a3fb2b2d94..29e93ac1e2e4 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -326,8 +326,10 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx, return false; iph = (struct iphdr *)(skb_network_header(skb) + ctx->offset); - size = iph->ihl << 2; + if (iph->ihl < 5) + return false; + size = iph->ihl << 2; if (ip_is_fragment(iph) || unlikely(ip_has_options(size))) return false; @@ -335,9 +337,9 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx, return false; if (iph->protocol == IPPROTO_IPIP) { - ctx->tun.proto = IPPROTO_IPIP; + ctx->tun.proto = iph->protocol; ctx->tun.hdr_size = size; - ctx->offset += size; + ctx->offset += ctx->tun.hdr_size; } return true; From 22f9dbed18bcc865d750ed109c6ae2dd4cf2af55 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 13 Jun 2026 22:25:24 -0700 Subject: [PATCH 4512/5214] netfilter: x_tables.h: fix all kernel-doc warnings - use correct names in kernel-doc comments - add missing struct members to kernel-doc comments Warning: include/linux/netfilter/x_tables.h:41 struct member 'targinfo' not described in 'xt_action_param' Warning: include/linux/netfilter/x_tables.h:41 Excess struct member 'targetinfo' description in 'xt_action_param' Warning: include/linux/netfilter/x_tables.h:90 struct member 'family' not described in 'xt_mtchk_param' Warning: include/linux/netfilter/x_tables.h:90 struct member 'nft_compat' not described in 'xt_mtchk_param' Warning: include/linux/netfilter/x_tables.h:101 expecting prototype for struct xt_mdtor_param. Prototype was for struct xt_mtdtor_param instead Warning: include/linux/netfilter/x_tables.h:121 struct member 'net' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'table' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'target' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'targinfo' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'hook_mask' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'family' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:121 struct member 'nft_compat' not described in 'xt_tgchk_param' Warning: include/linux/netfilter/x_tables.h:345 expecting prototype for xt_recseq(). Prototype was for DECLARE_PER_CPU() instead Signed-off-by: Randy Dunlap Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 20d70dddbe50..25062f4a0dd5 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -18,7 +18,7 @@ * @match: the match extension * @target: the target extension * @matchinfo: per-match data - * @targetinfo: per-target data + * @targinfo: per-target data * @state: pointer to hook state this packet came from * @fragoff: packet is a fragment, this is the data offset * @thoff: position of transport header relative to skb->data @@ -77,7 +77,9 @@ static inline u_int8_t xt_family(const struct xt_action_param *par) * @match: struct xt_match through which this function was invoked * @matchinfo: per-match data * @hook_mask: via which hooks the new rule is reachable - * Other fields as above. + * @family: actual NFPROTO_* through which the function is invoked + * (helpful when match->family == NFPROTO_UNSPEC) + * @nft_compat: running from the nft compat layer if true */ struct xt_mtchk_param { struct net *net; @@ -91,8 +93,13 @@ struct xt_mtchk_param { }; /** - * struct xt_mdtor_param - match destructor parameters - * Fields as above. + * struct xt_mtdtor_param - match destructor parameters + * + * @net: network namespace through which the check was invoked + * @match: struct xt_match through which this function was invoked + * @matchinfo: per-match data + * @family: actual NFPROTO_* through which the function is invoked + * (helpful when match->family == NFPROTO_UNSPEC) */ struct xt_mtdtor_param { struct net *net; @@ -105,10 +112,16 @@ struct xt_mtdtor_param { * struct xt_tgchk_param - parameters for target extensions' * checkentry functions * + * @net: network namespace through which the check was invoked + * @table: table the rule is tried to be inserted into * @entryinfo: the family-specific rule data * (struct ipt_entry, ip6t_entry, arpt_entry, ebt_entry) - * - * Other fields see above. + * @target: the target extension + * @targinfo: per-target data + * @hook_mask: via which hooks the new rule is reachable + * @family: actual NFPROTO_* through which the function is invoked + * (helpful when match->family == NFPROTO_UNSPEC) + * @nft_compat: running from the nft compat layer if true */ struct xt_tgchk_param { struct net *net; @@ -336,9 +349,9 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size); void xt_free_table_info(struct xt_table_info *info); /** - * xt_recseq - recursive seqcount for netfilter use + * var xt_recseq - recursive seqcount for netfilter use * - * Packet processing changes the seqcount only if no recursion happened + * Packet processing changes the seqcount only if no recursion happened. * get_counters() can use read_seqcount_begin()/read_seqcount_retry(), * because we use the normal seqcount convention : * Low order bit set to 1 if a writer is active. From 11d4bc4e26fb66040a5b5d95e9abf37deac2b1fc Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 11 Jun 2026 12:21:20 +0800 Subject: [PATCH 4513/5214] netfilter: nft_synproxy: stop bypassing the priv->info snapshot nft_synproxy_eval_v4() and nft_synproxy_eval_v6() already take a whole-object READ_ONCE() snapshot of the shared priv->info state before building the SYNACK reply, but nft_synproxy_tcp_options() still masks opts->options with priv->info.options from the live shared object. When a named synproxy object is updated concurrently with SYN traffic, the eval path can then mix mss and timestamp handling from the local snapshot with an options mask taken from a newer configuration, so one SYNACK no longer reflects a coherent synproxy configuration. Use info->options so nft_synproxy_tcp_options() stays on the same local snapshot that the eval path already copied from priv->info. Fixes: ee394f96ad75 ("netfilter: nft_synproxy: add synproxy stateful object support") Signed-off-by: Runyu Xiao Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_synproxy.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 7641f249614c..9ed288c9d168 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -24,14 +24,13 @@ static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { static void nft_synproxy_tcp_options(struct synproxy_options *opts, const struct tcphdr *tcp, struct synproxy_net *snet, - struct nf_synproxy_info *info, - const struct nft_synproxy *priv) + struct nf_synproxy_info *info) { this_cpu_inc(snet->stats->syn_received); if (tcp->ece && tcp->cwr) opts->options |= NF_SYNPROXY_OPT_ECN; - opts->options &= priv->info.options; + opts->options &= info->options; opts->mss_encode = opts->mss_option; opts->mss_option = info->mss; if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) @@ -56,7 +55,7 @@ static void nft_synproxy_eval_v4(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; @@ -87,7 +86,7 @@ static void nft_synproxy_eval_v6(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack_ipv6(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; From a49a8e51eebc605d5fd674ba7a451eabf553f5cb Mon Sep 17 00:00:00 2001 From: Yi Chen Date: Thu, 11 Jun 2026 16:50:13 +0200 Subject: [PATCH 4514/5214] selftests: netfilter: conntrack_sctp_collision.sh: Introduce SCTP INIT collision test The existing test covered a scenario where a delayed INIT_ACK chunk updates the vtag in conntrack after the association has already been established. A similar issue can occur with a delayed SCTP INIT chunk. Add a new simultaneous-open test case where the client's INIT is delayed, allowing conntrack to establish the association based on the server-initiated handshake. When the stale INIT arrives later, it may get recorded and cause a following INIT_ACK from the peer to be accepted instead of dropped. This INIT_ACK overwrites the vtag in conntrack, causing subsequent SCTP DATA chunks to be considered as invalid and then dropped by nft rules matching on ct state invalid. This test verifies such stale INIT chunks do not cause problems. Signed-off-by: Yi Chen Acked-by: Xin Long Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- .../net/netfilter/conntrack_sctp_collision.sh | 89 ++++++++++++++----- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh index d860f7d9744b..7261975957ef 100755 --- a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh +++ b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh @@ -2,18 +2,32 @@ # SPDX-License-Identifier: GPL-2.0 # # Testing For SCTP COLLISION SCENARIO as Below: -# +# 1. Stale INIT_ACK capture: # 14:35:47.655279 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT] [init tag: 2017837359] # 14:35:48.353250 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT] [init tag: 1187206187] # 14:35:48.353275 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT ACK] [init tag: 2017837359] # 14:35:48.353283 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [COOKIE ECHO] # 14:35:48.353977 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [COOKIE ACK] # 14:35:48.855335 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT ACK] [init tag: 164579970] +# (Delayed) +# +# 2. Stale INIT capture: +# 14:35:48.353250 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT] [init tag: 1187206187] +# 14:35:48.353275 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT ACK] [init tag: 2017837359] +# 14:35:48.353283 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [COOKIE ECHO] +# 14:35:48.353977 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [COOKIE ACK] +# 14:35:47.655279 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT] [init tag: 2017837359] +# (Delayed) +# 14:35:48.855335 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT ACK] [init tag: 164579970] # # TOPO: SERVER_NS (link0)<--->(link1) ROUTER_NS (link2)<--->(link3) CLIENT_NS source lib.sh +checktool "nft --version" "run test without nft" +checktool "tc -h" "run test without tc" +checktool "modprobe -q sctp" "load sctp module" + CLIENT_IP="198.51.200.1" CLIENT_PORT=1234 @@ -24,7 +38,8 @@ CLIENT_GW="198.51.200.2" SERVER_GW="198.51.100.2" # setup the topo -setup() { +topo_setup() { + # setup_ns cleans up existing net namespaces first. setup_ns CLIENT_NS SERVER_NS ROUTER_NS ip -n "$SERVER_NS" link add link0 type veth peer name link1 netns "$ROUTER_NS" ip -n "$CLIENT_NS" link add link3 type veth peer name link2 netns "$ROUTER_NS" @@ -38,35 +53,53 @@ setup() { ip -n "$ROUTER_NS" addr add $SERVER_GW/24 dev link1 ip -n "$ROUTER_NS" addr add $CLIENT_GW/24 dev link2 ip net exec "$ROUTER_NS" sysctl -wq net.ipv4.ip_forward=1 + sysctl -wq net.netfilter.nf_log_all_netns=1 ip -n "$CLIENT_NS" link set link3 up ip -n "$CLIENT_NS" addr add $CLIENT_IP/24 dev link3 ip -n "$CLIENT_NS" route add $SERVER_IP dev link3 via $CLIENT_GW +} - # simulate the delay on OVS upcall by setting up a delay for INIT_ACK with - # tc on $SERVER_NS side - tc -n "$SERVER_NS" qdisc add dev link0 root handle 1: htb r2q 64 - tc -n "$SERVER_NS" class add dev link0 parent 1: classid 1:1 htb rate 100mbit - tc -n "$SERVER_NS" filter add dev link0 parent 1: protocol ip u32 match ip protocol 132 \ - 0xff match u8 2 0xff at 32 flowid 1:1 - if ! tc -n "$SERVER_NS" qdisc add dev link0 parent 1:1 handle 10: netem delay 1200ms; then +conf_delay() +{ + # simulate the delay on OVS upcall by setting up a delay for INIT_ACK/INIT with + local ns=$1 + local link=$2 + local chunk_type=$3 + + # use a smaller number for assoc's max_retrans to reproduce the issue + ip net exec "$CLIENT_NS" sysctl -wq net.sctp.association_max_retrans=3 + + tc -n "$ns" qdisc add dev "$link" root handle 1: htb r2q 64 + tc -n "$ns" class add dev "$link" parent 1: classid 1:1 htb rate 100mbit + tc -n "$ns" filter add dev "$link" parent 1: protocol ip \ + u32 match ip protocol 132 0xff match u8 "$chunk_type" 0xff at 32 flowid 1:1 + if ! tc -n "$ns" qdisc add dev "$link" parent 1:1 handle 10: netem delay 1200ms; then echo "SKIP: Cannot add netem qdisc" - exit $ksft_skip + return $ksft_skip fi # simulate the ctstate check on OVS nf_conntrack - ip net exec "$ROUTER_NS" iptables -A FORWARD -m state --state INVALID,UNTRACKED -j DROP - ip net exec "$ROUTER_NS" iptables -A INPUT -p sctp -j DROP - - # use a smaller number for assoc's max_retrans to reproduce the issue - modprobe -q sctp - ip net exec "$CLIENT_NS" sysctl -wq net.sctp.association_max_retrans=3 + ip net exec "$ROUTER_NS" nft -f - <<-EOF + table ip t { + chain forward { + type filter hook forward priority filter; policy accept; + meta l4proto icmp counter accept + ct state new counter accept + ct state established,related counter accept + ct state invalid log flags all counter drop comment \ + "Expect to drop stale INIT/INIT_ACK chunks" + counter + } + } + EOF + return 0 } cleanup() { - ip net exec "$CLIENT_NS" pkill sctp_collision >/dev/null 2>&1 - ip net exec "$SERVER_NS" pkill sctp_collision >/dev/null 2>&1 + # cleanup_all_ns terminates running processes in the namespaces. cleanup_all_ns + sysctl -wq net.netfilter.nf_log_all_netns=0 } do_test() { @@ -81,7 +114,19 @@ do_test() { # run the test case trap cleanup EXIT -setup && \ -echo "Test for SCTP Collision in nf_conntrack:" && \ -do_test && echo "PASS!" -exit $? + +echo "Test for SCTP INIT_ACK Collision in nf_conntrack:" +topo_setup || exit $? +conf_delay $SERVER_NS link0 2 || exit $? + +if ! do_test; then + exit $ksft_fail +fi + +echo "Test for SCTP INIT Collision in nf_conntrack:" +topo_setup || exit $? +conf_delay $CLIENT_NS link3 1 || exit $? + +if ! do_test; then + exit $ksft_fail +fi From 9dbba7e694ec045f21ede2f892fb42b81b4e1692 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 15 Jun 2026 20:10:44 +0200 Subject: [PATCH 4515/5214] netfilter: nft_compat: ebtables emulation must reject non-bridge targets xtables targets return netfilter verdicts: NF_ACCEPT, NF_DROP, and so on. ebtables targets return incompatible verdicts: EBT_ACCEPT, EBT_DROP, ... We cannot allow fallback to NFPROTO_UNSPEC. ebtables doesn't permit this since 11ff7288beb2 ("netfilter: ebtables: reject non-bridge targets") but that commit missed the nft_compat layer. Reported-by: Ren Wei Reported-by: Wyatt Feng Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_compat.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 0caa9304d2d0..63864b928259 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -397,6 +397,22 @@ static int nft_target_validate(const struct nft_ctx *ctx, return 0; } +static int nft_target_bridge_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct xt_target *target = expr->ops->data; + + /* Do not allow UNSPEC to stand-in for NFPROTO_BRIDGE + * targets: they are incompatible. ebtables targets return + * EBT_ACCEPT, DROP and so on which are not compatible with + * NF_ACCEPT, NF_DROP and so on. + */ + if (target->family != NFPROTO_BRIDGE) + return -ENOENT; + + return nft_target_validate(ctx, expr); +} + static void __nft_match_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt, @@ -932,13 +948,15 @@ nft_target_select_ops(const struct nft_ctx *ctx, ops->init = nft_target_init; ops->destroy = nft_target_destroy; ops->dump = nft_target_dump; - ops->validate = nft_target_validate; ops->data = target; - if (family == NFPROTO_BRIDGE) + if (family == NFPROTO_BRIDGE) { ops->eval = nft_target_eval_bridge; - else + ops->validate = nft_target_bridge_validate; + } else { ops->eval = nft_target_eval_xt; + ops->validate = nft_target_validate; + } return ops; err: From 8a2cfe7951f679350d39239de4c610448731a68e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 2 Jun 2026 15:31:06 +0200 Subject: [PATCH 4516/5214] selftests: nft_queue.sh: add a bridge queue test Add a test queueing from bridge family. This was lacking: we queued from inet for ipv4 and ipv6 but we had no bridge queue test so far. Given kernel MUST validate that in/out port are still part of a bridge device on reinject add a test case for this before adding this check. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- .../selftests/net/netfilter/nft_queue.sh | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/netfilter/nft_queue.sh b/tools/testing/selftests/net/netfilter/nft_queue.sh index d80390848e85..7c857a2e0f34 100755 --- a/tools/testing/selftests/net/netfilter/nft_queue.sh +++ b/tools/testing/selftests/net/netfilter/nft_queue.sh @@ -85,11 +85,12 @@ ip -net "$ns3" route add default via 10.0.3.1 ip -net "$ns3" route add default via dead:3::1 load_ruleset() { - local name=$1 - local prio=$2 + local family=$1 + local name=$2 + local prio=$3 ip netns exec "$nsrouter" nft -f /dev/stdin <&2 ip netns exec "$nsrouter" nft list ruleset + echo -n "$TMPFILE0: ";cat "$TMPFILE0" + echo -n "$TMPFILE1: ";cat "$TMPFILE1" exit 1 fi done - echo "PASS: Expected and received $last" + echo "PASS: Expected and received $last ($family)" } listener_ready() @@ -400,6 +404,8 @@ EOF kill "$nfqpid" echo "PASS: icmp+nfqueue via vrf" + ip -net "$ns1" link del tvrf + ip netns exec "$ns1" nft flush ruleset } sctp_listener_ready() @@ -814,12 +820,53 @@ EOF check_tainted "queue program exiting while packets queued" } +test_queue_bridge() +{ + ip -net "$nsrouter" addr flush dev veth0 + ip -net "$nsrouter" addr flush dev veth1 + + ip -net "$nsrouter" link add br0 type bridge + ip -net "$nsrouter" link set veth0 master br0 + ip -net "$nsrouter" link set veth1 master br0 + + ip -net "$nsrouter" link set br0 up + + ip -net "$nsrouter" addr add 10.0.2.1/16 dev br0 + ip -net "$nsrouter" addr add dead:2::1/64 dev br0 nodad + + ip -net "$ns1" addr flush dev eth0 + ip -net "$ns2" addr flush dev eth0 + + ip -net "$ns1" addr add 10.0.1.1/16 dev eth0 + ip -net "$ns1" addr add dead:2::2/64 dev eth0 nodad + + ip -net "$ns2" addr add 10.0.2.99/16 dev eth0 + ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad + + ip netns exec "$nsrouter" nft flush ruleset + + ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=0 > /dev/null + ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=0 > /dev/null + ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=0 > /dev/null + + if ! test_ping;then + echo "FAIL: netns bridge connectivity" 1>&2 + exit $ret + fi + + load_ruleset "bridge" "filter" 10 + test_queue 10 "bridge" + + load_ruleset "bridge" "filter2" 20 + test_queue 20 "bridge" +} + ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth2.forwarding=1 > /dev/null -load_ruleset "filter" 0 +load_ruleset "inet" "filter" 0 if test_ping; then # queue bypass works (rules were skipped, no listener) @@ -842,11 +889,11 @@ load_counter_ruleset 10 # 1x icmp prerouting,forward,postrouting -> 3 queue events (6 incl. reply). # 1x icmp prerouting,input,output postrouting -> 4 queue events incl. reply. # so we expect that userspace program receives 10 packets. -test_queue 10 +test_queue 10 "inet" # same. We queue to a second program as well. -load_ruleset "filter2" 20 -test_queue 20 +load_ruleset "inet" "filter2" 20 +test_queue 20 "inet" ip netns exec "$ns1" nft flush ruleset test_tcp_forward @@ -863,4 +910,7 @@ test_queue_stress test_icmp_vrf test_queue_removal +# turns router into a bridge +test_queue_bridge + exit $ret From aaa0cd698ffa5dffbb0a1e81474a63a9f3ee47b1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Jun 2026 13:12:49 +0200 Subject: [PATCH 4517/5214] netfilter: ctnetlink: do not allow to reset helper on existing conntrack This feature allows to reset a helper for an existing conntrack, but it is not safe. This requires a synchronized_rcu() call after resetting the helper, which is going to be expensive for a large batch of conntrack entries. This also needs to call to the .destroy callback to release the GRE/PPTP mappings to fix it. This feature antedates the creation of the conntrack-tools and I cannot find a good use-case for this. Given that I cannot find any user in the netfilter.org userspace tree, I prefer to remove this feature. Fixes: c1d10adb4a52 ("[NETFILTER]: Add ctnetlink port for nf_conntrack") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4e78d2482989..cb38ef42e9e6 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1953,19 +1953,6 @@ static int ctnetlink_change_helper(struct nf_conn *ct, return err; } - if (!strcmp(helpname, "") && help) { - helper = rcu_dereference(help->helper); - if (helper) { - /* we had a helper before ... */ - nf_ct_remove_expectations(ct); - RCU_INIT_POINTER(help->helper, NULL); - if (refcount_dec_and_test(&helper->ct_refcnt)) - kfree_rcu(helper, rcu); - } - rcu_read_unlock(); - return 0; - } - helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), nf_ct_protonum(ct)); if (helper == NULL) { From 4d587cd8a72155089a627130bbd4716ec0856e21 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 15:57:38 -0500 Subject: [PATCH 4518/5214] apparmor: mediate the implicit connect of TCP fast open sendmsg sendmsg()/sendto() with MSG_FASTOPEN is a combination of connect(2) and write(2): it opens the connection in the SYN. apparmor_socket_sendmsg() only checks AA_MAY_SEND, so a profile that grants send but denies connect lets a confined task open an outbound TCP/MPTCP connection that connect(2) would have refused, bypassing connect mediation. Mediate the implicit connect when MSG_FASTOPEN is set and a destination is supplied. Add it to apparmor_socket_sendmsg() (not the shared aa_sock_msg_perm() helper, which recvmsg also uses) and call aa_sk_perm() directly, mirroring the selinux and tomoyo fixes. sk_is_tcp() does not cover MPTCP fast open, so the SOCK_STREAM/IPPROTO_MPTCP arm is explicit. Fixes: cf60af03ca4e ("net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: John Johansen --- security/apparmor/lsm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7457067e8192..88d12e89d115 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1422,7 +1422,21 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, static int apparmor_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + + if (error) + return error; + + /* TCP fast open carries connect() semantics in sendmsg(); mediate + * the implicit connect so it cannot bypass the connect permission. + */ + if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) + error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + + return error; } static int apparmor_socket_recvmsg(struct socket *sock, From fc36dd30412a3b9df3b408e36e8e2cd24bd9e33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 17 Jun 2026 11:44:08 +0200 Subject: [PATCH 4519/5214] fbdev: vga16fb: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct platform_device_id to zero without relying on that value. Drop these unused assignments. While touching this array unify spacing and usage of commas and use named initializers for .name. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Helge Deller --- drivers/video/fbdev/vga16fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index 22085d3668e8..cdd6b8de0ceb 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -1421,8 +1421,8 @@ static void vga16fb_remove(struct platform_device *dev) } static const struct platform_device_id vga16fb_driver_id_table[] = { - {"ega-framebuffer", 0}, - {"vga-framebuffer", 0}, + { .name = "ega-framebuffer" }, + { .name = "vga-framebuffer" }, { } }; MODULE_DEVICE_TABLE(platform, vga16fb_driver_id_table); From 914a76a9f08366434bf595700f62026b7a19a9cc Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Mon, 22 Jun 2026 16:25:39 +0300 Subject: [PATCH 4520/5214] drm/i915/gem: Add missing nospec on parallel submit slot Add missing Spectre mitigation for userspace controlled parallel submission slot. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: e5e32171a2cf ("drm/i915/guc: Connect UAPI to GuC multi-lrc interface") Cc: Matthew Brost Cc: Tvrtko Ursulin Signed-off-by: Joonas Lahtinen Reviewed-by: Matthew Brost Reviewed-by: Tvrtko Ursulin Cc: # v5.16+ Link: https://patch.msgid.link/20260622132539.165558-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 15b9353deff3cf72331c387780de3cf9c316b643) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 6ac0f23570f3..aeafe1742d30 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -613,6 +613,7 @@ set_proto_ctx_engines_parallel_submit(struct i915_user_extension __user *base, return -EINVAL; } + slot = array_index_nospec(slot, set->num_engines); if (set->engines[slot].type != I915_GEM_ENGINE_TYPE_INVALID) { drm_dbg(&i915->drm, "Invalid placement[%d], already occupied\n", slot); From d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 23:24:39 +0800 Subject: [PATCH 4521/5214] gpio: tegra: do not call pinctrl for GPIO direction tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook. The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context. This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path. A directed runtime validation kept the same non-sleeping chip registration and drove: gpio_shared_proxy_direction_output() gpiod_direction_output_raw_commit() tegra_gpio_direction_output() pinctrl_gpio_direction_output() Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack. Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path. Fixes: 11da90541283 ("gpio: tegra: Fix offset of pinctrl calls") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index df06b56a2ade..fa6c8ee92093 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_input(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl input direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_output(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl output direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_get_direction(struct gpio_chip *chip, From 4e8eb6952aa6749726c6c3763ae0032a6332c24f Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 10:31:06 +0800 Subject: [PATCH 4522/5214] gpio: davinci: fix IRQ domain leak on devm_kzalloc failure In davinci_gpio_irq_setup(), after successfully creating an IRQ domain with irq_domain_create_legacy(), a subsequent devm_kzalloc() failure in the bank loop causes the function to return -ENOMEM without removing the IRQ domain. Unlike devm-managed resources, irq_domain_create_legacy() does not auto-clean up on probe failure, so the domain is leaked. Fix by calling irq_domain_remove() before returning on allocation failure. Fixes: b5cf3fd827d2 ("gpio: davinci: Redesign driver to accommodate ngpios in one gpio chip") Signed-off-by: Qingshuang Fu Link: https://patch.msgid.link/20260623023106.117229-1-fffsqian@163.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 97780f27ce5b..270cd7c88812 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -568,8 +568,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + irq_domain_remove(chips->irq_domain); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank; From e0ecb324246be9cf3a0689346a658e48a38546b2 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Tue, 23 Jun 2026 02:59:04 +0930 Subject: [PATCH 4523/5214] ALSA: FCP: Add Focusrite ISA C8X support Add USB PID 0x821e to the list of devices handled by the Focusrite Control Protocol (FCP) driver. Cc: stable@vger.kernel.org Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/ajlw4HK+2RSW3nUl@m.b4.vu Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 628f841b04aa..f6f2bc8e97a7 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4526,6 +4526,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */ case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */ case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */ + case USB_ID(0x1235, 0x821e): /* Focusrite ISA C8X */ err = snd_fcp_init(mixer); break; From 57f940017a777aadf38b99db44cf35f727c26f4c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 12 Jun 2026 08:03:50 +0200 Subject: [PATCH 4524/5214] netfilter: conntrack: add deprecation warnings for irc and pptp trackers IRC Direct client-to-client requires plaintext. IRC over TLS should be preferred, making this helper ineffective. Add a deprecation warning and update the help text to better reflect that this is needed for the DCC extension, not IRC itself. PPTP is esoteric these days and it is the only helper that requires the destroy callback in the conntrack helper API. Removal would simplify the conntrack core. Both helpers are IPv4 only. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 4 ++++ net/netfilter/Kconfig | 11 ++++++----- net/netfilter/nf_conntrack_irc.c | 2 ++ net/netfilter/nf_conntrack_pptp.c | 2 ++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 81025101f86d..c761cd8158b2 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -114,6 +114,10 @@ int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int, void nf_conntrack_helpers_unregister(struct nf_conntrack_helper **, unsigned int); +#define nf_conntrack_helper_deprecated(name) \ + pr_warn("The %s conntrack helper is scheduled for removal.\n" \ + "Please contact the netfilter-devel mailing list if you still need this.\n", name) + struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 665f8008cc4b..4c04cd8d40a2 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -256,8 +256,7 @@ config NF_CONNTRACK_H323 To compile it as a module, choose M here. If unsure, say N. config NF_CONNTRACK_IRC - tristate "IRC protocol support" - default m if NETFILTER_ADVANCED=n + tristate "IRC DCC protocol support (obsolete)" help There is a commonly-used extension to IRC called Direct Client-to-Client Protocol (DCC). This enables users to send @@ -267,6 +266,8 @@ config NF_CONNTRACK_IRC using NAT, this extension will enable you to send files and initiate chats. Note that you do NOT need this extension to get files or have others initiate chats, or everything else in IRC. + DCC tracking behind NAT requires plaintext (unencrypted) IRC, so + this helper is of limited use these days. To compile it as a module, choose M here. If unsure, say N. @@ -308,17 +309,17 @@ config NF_CONNTRACK_SNMP To compile it as a module, choose M here. If unsure, say N. config NF_CONNTRACK_PPTP - tristate "PPtP protocol support" + tristate "PPtP protocol support (deprecated)" depends on NETFILTER_ADVANCED select NF_CT_PROTO_GRE help This module adds support for PPTP (Point to Point Tunnelling Protocol, RFC2637) connection tracking and NAT. - If you are running PPTP sessions over a stateful firewall or NAT + If you are still running PPTP sessions over a stateful firewall or NAT box, you may want to enable this feature. - Please note that not all PPTP modes of operation are supported yet. + Please note that not all PPTP modes of operation are supported. Specifically these limitations exist: - Blindly assumes that control connections are always established in PNS->PAC direction. This is a violation of RFC2637. diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 0c117b8492e9..193ab34db795 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -262,6 +262,8 @@ static int __init nf_conntrack_irc_init(void) { int i, ret; + nf_conntrack_helper_deprecated(HELPER_NAME); + if (max_dcc_channels < 1) { pr_err("max_dcc_channels must not be zero\n"); return -EINVAL; diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 776505a78e64..80fc14c87ddc 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -545,6 +545,8 @@ static int __init nf_conntrack_pptp_init(void) pptp.destroy = gre_pptp_destroy_siblings; + nf_conntrack_helper_deprecated(pptp.name); + return nf_conntrack_helper_register(&pptp, &pptp_ptr); } From 979c13114c0bb6ab9135e2c93e00c79c412aef09 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Jun 2026 21:35:14 +0200 Subject: [PATCH 4525/5214] netfilter: nf_conntrack_expect: store master_tuple in expectation Store master conntrack tuple in the expectation since exp->master might refer to a different conntrack when accessed from rcu read side lock area due to typesafe rcu rules. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 1 + net/netfilter/nf_conntrack_broadcast.c | 1 + net/netfilter/nf_conntrack_expect.c | 2 ++ net/netfilter/nf_conntrack_netlink.c | 10 ++++------ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index be4a120d549e..c024345c9bd8 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -26,6 +26,7 @@ struct nf_conntrack_expect { possible_net_t net; /* We expect this tuple, with the following mask */ + struct nf_conntrack_tuple master_tuple; struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_mask mask; diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 400119b6320e..bf78828c7549 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -62,6 +62,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, if (exp == NULL) goto out; + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; helper = rcu_dereference(help->helper); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 49e18eda037e..9454913e1b33 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -355,6 +355,8 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, exp->tuple.src.l3num = family; exp->tuple.dst.protonum = proto; + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + if (saddr) { memcpy(&exp->tuple.src.u3, saddr, len); if (sizeof(exp->tuple.src.u3) > len) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index cb38ef42e9e6..4217715d42dc 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3002,7 +3002,6 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, const struct nf_conntrack_expect *exp) { __s32 timeout = (__s32)(READ_ONCE(exp->timeout) - nfct_time_stamp) / HZ; - struct nf_conn *master = exp->master; struct nf_conntrack_helper *helper; #if IS_ENABLED(CONFIG_NF_NAT) struct nlattr *nest_parms; @@ -3017,9 +3016,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, goto nla_put_failure; if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) goto nla_put_failure; - if (ctnetlink_exp_dump_tuple(skb, - &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - CTA_EXPECT_MASTER) < 0) + if (ctnetlink_exp_dump_tuple(skb, &exp->master_tuple, CTA_EXPECT_MASTER) < 0) goto nla_put_failure; #if IS_ENABLED(CONFIG_NF_NAT) @@ -3032,9 +3029,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, if (nla_put_be32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir))) goto nla_put_failure; - nat_tuple.src.l3num = nf_ct_l3num(master); + nat_tuple.src.l3num = exp->master_tuple.src.l3num; nat_tuple.src.u3 = exp->saved_addr; - nat_tuple.dst.protonum = nf_ct_protonum(master); + nat_tuple.dst.protonum = exp->master_tuple.dst.protonum; nat_tuple.src.u = exp->saved_proto; if (ctnetlink_exp_dump_tuple(skb, &nat_tuple, @@ -3576,6 +3573,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, #endif rcu_assign_pointer(exp->helper, helper); rcu_assign_pointer(exp->assign_helper, assign_helper); + exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all; From be57dd9c1c1796e368582313af2b3849f78ac224 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 22 Jun 2026 21:35:18 +0200 Subject: [PATCH 4526/5214] netfilter: nf_conntrack_expect: run expectation eviction with no helper Run expectation eviction if no helper is specified to deal with the nft_ct expectation support. Cap the maximum expectation limit per master conntrack to NF_CT_EXPECT_MAX_CNT (255). Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_expect.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 9454913e1b33..113bb1cb1683 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -499,6 +499,13 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, if (p->max_expected && master_help->expecting[expect->class] >= p->max_expected) evict_oldest_expect(master_help, expect, p); + } else { + const struct nf_conntrack_expect_policy default_exp_policy = { + .max_expected = NF_CT_EXPECT_MAX_CNT, + }; + + if (master_help->expecting[expect->class] >= default_exp_policy.max_expected) + evict_oldest_expect(master_help, expect, &default_exp_policy); } cnet = nf_ct_pernet(net); From 6fb421bd07f156cdf0cdede062d31f1c21def326 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 23 Jun 2026 07:30:34 +0200 Subject: [PATCH 4527/5214] netfilter: nft_ct: expectation timeouts are passed in milliseconds Userspace passes '5000' in case user asks for 5 seconds. Allowing for sub-second expectation lifetimes makes sense to me. so fix up the kernel side instead of munging nft to send a value rounded up to next second. Also note that this violates nft convention of passing integers in network byte order, but we can't change this anymore. Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_ct.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 958054dd2e2e..03a88c77e0f0 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1215,11 +1215,23 @@ struct nft_ct_expect_obj { u32 timeout; }; +static int nft_ct_expect_timeout_get(const struct nlattr *attr, u32 *val) +{ + unsigned long jiffies_val = msecs_to_jiffies(nla_get_u32(attr)); + + if (jiffies_val > UINT_MAX) + return -ERANGE; + + *val = jiffies_val; + return 0; +} + static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, const struct nlattr * const tb[], struct nft_object *obj) { struct nft_ct_expect_obj *priv = nft_obj_data(obj); + int err; if (!tb[NFTA_CT_EXPECT_L4PROTO] || !tb[NFTA_CT_EXPECT_DPORT] || @@ -1254,8 +1266,11 @@ static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } + err = nft_ct_expect_timeout_get(tb[NFTA_CT_EXPECT_TIMEOUT], &priv->timeout); + if (err) + return err; + priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]); - priv->timeout = nla_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); priv->size = nla_get_u8(tb[NFTA_CT_EXPECT_SIZE]); return nf_ct_netns_get(ctx->net, ctx->family); @@ -1275,7 +1290,7 @@ static int nft_ct_expect_obj_dump(struct sk_buff *skb, if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || - nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout) || + nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, jiffies_to_msecs(priv->timeout)) || nla_put_u8(skb, NFTA_CT_EXPECT_SIZE, priv->size)) return -1; @@ -1325,7 +1340,7 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, priv->l4proto, NULL, &priv->dport); - exp->timeout += priv->timeout * HZ; + exp->timeout += priv->timeout; if (nf_ct_expect_related(exp, 0) != 0) regs->verdict.code = NF_DROP; From 397c8300972f6e1486fd1afd99a044648a401cd5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 23 Jun 2026 12:56:55 +0200 Subject: [PATCH 4528/5214] netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration On helper registration, the maximum number of expectations cannot go over NF_CT_EXPECT_MAX_CNT (255), but zero can be specified then nf_conntrack_expect_max applies. Turn zero into NF_CT_EXPECT_MAX_CNT otherwise, expectation LRU eviction on insertion is disabled. Moreover, expand this sanity check all expectation classes. This max_expecy policy is only tunable since userspace helpers are available, set Fixes: tag to the commit that adds such infrastructure. Remove the check for p->max_expected given this field must always be non-zero after this patch. Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_expect.c | 3 +-- net/netfilter/nf_conntrack_helper.c | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 113bb1cb1683..38630c5e006f 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -496,8 +496,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, lockdep_is_held(&nf_conntrack_expect_lock)); if (helper) { p = &helper->expect_policy[expect->class]; - if (p->max_expected && - master_help->expecting[expect->class] >= p->max_expected) + if (master_help->expecting[expect->class] >= p->max_expected) evict_oldest_expect(master_help, expect, p); } else { const struct nf_conntrack_expect_policy default_exp_policy = { diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 8b94001c2430..500509b17663 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -374,8 +374,13 @@ int __nf_conntrack_helper_register(struct nf_conntrack_helper *me) if (!nf_ct_helper_hash) return -ENOENT; - if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) - return -EINVAL; + for (i = 0; i <= me->expect_class_max; i++) { + if (!me->expect_policy[i].max_expected) + me->expect_policy[i].max_expected = NF_CT_EXPECT_MAX_CNT; + + if (me->expect_policy[i].max_expected > NF_CT_EXPECT_MAX_CNT) + return -EINVAL; + } mutex_lock(&nf_ct_helper_mutex); for (i = 0; i < nf_ct_helper_hsize; i++) { From 5714c8359f4fc171ab8e0bd0dfc4c61fb36e1db6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 Jun 2026 11:18:10 +0100 Subject: [PATCH 4529/5214] soundwire: Move wait for initialisation helper to header As SoundWire devices tend to enumerate on the bus after probe, drivers frequently need to wait for the device to initialise from common driver code. The common system is to split drivers into a core module and then a module for each communication bus. These two facts tend to cause Kconfig issues, the issue tends to be when SOUNDWIRE=m and DRIVER_I2C=y, this usually selects DRIVER=y. The driver code then wants to call sdw_slave_wait_for_init(), but this results in calling a module function from built in code. A depends on SOUNDWIRE | !SOUNDWIRE could be added to the end driver but this seems slightly off as it adds a lot of counter intuitive depends. A simpler solution is to make sdw_slave_wait_for_init() a static inline function. As part of doing this add a check for the slave device being NULL acknowledging that this is likely called from code that is shared between control buses. It does require dropping the call to sdw_show_ping_status() but this can be added back in end drivers that used it originally. Currently this is causing rand config issues on RT5682 and will soon also cause similar problems on cs42l43. Acked-by: Vinod Koul Acked-by: Arnd Bergmann Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260623101814.24044-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/soundwire/bus.c | 28 -------------------------- include/linux/soundwire/sdw.h | 38 +++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index b7bdf19ebb42..fe5316d93fef 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1372,34 +1372,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *slave) } EXPORT_SYMBOL_GPL(sdw_slave_get_current_bank); -/** - * sdw_slave_wait_for_init - Wait for device initialisation - * @slave: Pointer to the SoundWire peripheral. - * @timeout_ms: Timeout in milliseconds. - * - * Wait for a peripheral device to enumerate and be initialised by the - * SoundWire core. - * - * Return: Zero on success, and a negative error code on failure. - */ -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - unsigned long time; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(timeout_ms)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete\n"); - sdw_show_ping_status(slave->bus, true); - return -ETIMEDOUT; - } - - slave->unattach_request = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(sdw_slave_wait_for_init); - static int sdw_slave_set_frequency(struct sdw_slave *slave) { int scale_index; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a46cbaec5949..b484784e2690 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1093,8 +1094,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *sdev); int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base); -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms); - /* messaging and data APIs */ int sdw_read(struct sdw_slave *slave, u32 addr); int sdw_write(struct sdw_slave *slave, u32 addr, u8 value); @@ -1138,12 +1137,6 @@ static inline int sdw_slave_get_current_bank(struct sdw_slave *sdev) return -EINVAL; } -static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - WARN_ONCE(1, "SoundWire API is disabled"); - return -EINVAL; -} - /* messaging and data APIs */ static inline int sdw_read(struct sdw_slave *slave, u32 addr) { @@ -1207,4 +1200,33 @@ static inline int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u #endif /* CONFIG_SOUNDWIRE */ +/** + * sdw_slave_wait_for_init - Wait for device initialisation + * @slave: Pointer to the SoundWire peripheral. + * @timeout_ms: Timeout in milliseconds. + * + * Wait for a peripheral device to enumerate and be initialised by the + * SoundWire core. + * + * Return: Zero on success, and a negative error code on failure. + */ +static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) +{ + unsigned long time; + + if (!slave) + return 0; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(timeout_ms)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete\n"); + return -ETIMEDOUT; + } + + slave->unattach_request = 0; + + return 0; +} + #endif /* __SOUNDWIRE_H */ From ce52450319fb0ee122e5be5ec8dfb888ee1e0237 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 Jun 2026 11:18:11 +0100 Subject: [PATCH 4530/5214] ASoC: es9356: Add back local call to sdw_show_ping_status() As the core no longer calls this debug helper add it back to the drivers that originally called it. Acked-by: Arnd Bergmann Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260623101814.24044-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/es9356.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index 670e918b56a4..8db81d574624 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -1111,8 +1111,10 @@ static int es9356_sdca_dev_resume(struct device *dev) es9356->disable_irq = false; ret = sdw_slave_wait_for_init(slave, es9356_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(es9356->regmap, false); regcache_sync(es9356->regmap); From 1921303a1d2f26ae70446aa18fb218767bddd913 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 Jun 2026 11:18:12 +0100 Subject: [PATCH 4531/5214] ASoC: max98373: Add back local call to sdw_show_ping_status() As the core no longer calls this debug helper add it back to the drivers that originally called it. Acked-by: Arnd Bergmann Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260623101814.24044-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98373-sdw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 6829fa07c9ec..7a42052dc051 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -272,8 +272,10 @@ static int max98373_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, MAX98373_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(max98373->regmap, false); regcache_sync(max98373->regmap); From ea9ff3b7bcfbcd1e61d34590c7c632005ef3d9aa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 Jun 2026 11:18:13 +0100 Subject: [PATCH 4532/5214] ASoC: ti: Add back local call to sdw_show_ping_status() As the core no longer calls this debug helper add it back to the drivers that originally called it. Acked-by: Arnd Bergmann Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260623101814.24044-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/tac5xx2-sdw.c | 4 +++- sound/soc/codecs/tas2783-sdw.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index bb12cfb6da12..ace06f5ab58c 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -1445,8 +1445,10 @@ static s32 tac5xx2_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, TAC5XX2_PROBE_TIMEOUT_MS); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(tac_dev->regmap, false); regcache_mark_dirty(tac_dev->regmap); diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 7d70e7e3f24f..1127ea59b5e4 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1083,8 +1083,10 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) int ret; ret = sdw_slave_wait_for_init(slave, TAS2783_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(tas_dev->regmap, false); regcache_sync(tas_dev->regmap); From 6540b9d9ccc32ad1546dcc7b4d4bcbb68c667714 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 Jun 2026 11:18:14 +0100 Subject: [PATCH 4533/5214] ASoC: realtek: Add back local call to sdw_show_ping_status() As the core no longer calls this debug helper add it back to the drivers that originally called it. Acked-by: Arnd Bergmann Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260623101814.24044-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1017-sdca-sdw.c | 4 +++- sound/soc/codecs/rt1308-sdw.c | 4 +++- sound/soc/codecs/rt1316-sdw.c | 4 +++- sound/soc/codecs/rt5682-sdw.c | 4 +++- sound/soc/codecs/rt700-sdw.c | 4 +++- sound/soc/codecs/rt711-sdca-sdw.c | 4 +++- sound/soc/codecs/rt712-sdca-dmic.c | 4 +++- sound/soc/codecs/rt712-sdca-sdw.c | 4 +++- sound/soc/codecs/rt715-sdca-sdw.c | 4 +++- sound/soc/codecs/rt715-sdw.c | 4 +++- sound/soc/codecs/rt721-sdca-sdw.c | 4 +++- sound/soc/codecs/rt722-sdca-sdw.c | 4 +++- 12 files changed, 36 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index d62e8a253676..91d3d43cd998 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -779,8 +779,10 @@ static int rt1017_sdca_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1017_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1017->regmap, false); regcache_sync(rt1017->regmap); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 39e06a3a7560..60e5040b6dd9 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -774,8 +774,10 @@ static int rt1308_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1308_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1308->regmap, false); regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 1828fd9d5af6..5e8eda6a5f7f 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -751,8 +751,10 @@ static int rt1316_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1316_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1316->regmap, false); regcache_sync(rt1316->regmap); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index ec2a35a0cacd..dec8c2147d68 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -769,8 +769,10 @@ static int rt5682_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 30fcca210f05..6bc636c86f42 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -528,8 +528,10 @@ static int rt700_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT700_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt700->regmap, false); regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index a8164fc3979a..461315844ba9 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -454,8 +454,10 @@ static int rt711_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt711->regmap, false); regcache_sync(rt711->regmap); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 4c5c2f5ba5ed..8b7d50a80ff9 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -911,8 +911,10 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 581732180473..2787524c796e 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -467,8 +467,10 @@ static int rt712_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 4b9815b5628d..fabd21bbbe5b 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -230,8 +230,10 @@ static int rt715_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 7f83a8f1a06e..a4a3945522e8 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -507,8 +507,10 @@ static int rt715_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 58606209316a..02df04a0ddad 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -505,8 +505,10 @@ static int rt721_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt721->regmap, false); regcache_sync(rt721->regmap); diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 0f76492ff915..284900933ebf 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -552,8 +552,10 @@ static int rt722_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt722->regmap, false); regcache_sync(rt722->regmap); From 68ff4a3ccda9f98c74f23c70c8c7c581f9eee931 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Jun 2026 18:16:16 +0200 Subject: [PATCH 4534/5214] cpuidle: Allow exit latency to exceed target residency Commit 76934e495cdc ("cpuidle: Add sanity check for exit latency and target residency") had added a check to prevent the exit latency of an idle state from exceeding its target residency that later was limited to printing a warning message in that case in commit 4bf944f3fcb6 ("cpuidle: Warn instead of bailing out if target residency check fails"). However, a thorough code inspection with that in mind leads to the conclusion that actually there are no assumptions in cpuidle regarding the relationship between the exit latency and target residency of a given idle state. It is generally assumed that the idle states table provided by a cpuidle driver will be sorted by both the target residency and exit latency in ascending order, but that's a different matter. Accordingly, drop the check in question along with the message printed when it triggers and the inaccurate comment preceding it. Fixes: 4bf944f3fcb6 ("cpuidle: Warn instead of bailing out if target residency check fails") Signed-off-by: Rafael J. Wysocki Reviewed-by: Christian Loehle [ rjw: Subject fixup ] Link: https://patch.msgid.link/3444162.aeNJFYEL58@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 370664c47e65..e355b42043cf 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -195,14 +195,6 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) s->exit_latency_ns = 0; else s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); - - /* - * Warn if the exit latency of a CPU idle state exceeds its - * target residency which is assumed to never happen in cpuidle - * in multiple places. - */ - if (s->exit_latency_ns > s->target_residency_ns) - pr_warn("Idle state %d target residency too low\n", i); } } From 1ce42a11bed134903e352010a01fa53073a6b395 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 23 Jun 2026 20:05:26 +0900 Subject: [PATCH 4535/5214] ASoC: SDCA: Validate written enum value in ge_put_enum_double() ge_put_enum_double() passes the user-supplied enumeration index item[0] to snd_soc_enum_item_to_val() without checking it against the number of items in the enum: ret = snd_soc_enum_item_to_val(e, item[0]); snd_soc_enum_item_to_val() indexes the heap-allocated e->values[] array with that index (e->values is set from a devm_kcalloc() of e->items entries), so a control write with an out-of-range item[0] reads past the end of the values buffer. The bounds check in snd_soc_dapm_put_enum_double() only runs afterwards, so it does not prevent the read here. Reject an out-of-range item before using it, matching the other enum put handlers. This issue was pointed out by the Sashiko AI review bot while reviewing a related enum-validation series: https://lore.kernel.org/all/20260609125735.CEB651F00893@smtp.kernel.org/ Fixes: 812ff1baa764 ("ASoC: SDCA: Limit values user can write to Selected Mode") Signed-off-by: HyeongJun An Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260623110526.813217-1-sammiee5311@gmail.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index e76afa396b0a..b4dedba719dc 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -160,6 +160,9 @@ static int ge_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int reg = e->reg; int ret; + if (item[0] >= e->items) + return -EINVAL; + reg &= ~SDW_SDCA_CTL_CSEL(0x3F); reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE); From e26bb459d0f3dad83c6a31d5e4480e60760c262b Mon Sep 17 00:00:00 2001 From: Bartosz Juraszewski Date: Mon, 22 Jun 2026 20:27:33 +0200 Subject: [PATCH 4536/5214] ASoC: tas2783: Update loaded firmware names to linux-firmware 20260519 In linux-firmware commit from 2026-05-19 `2f90f4fe5c67f51a8410907a...` `ASoC: tas2783: Add Firmware files for tas2783A projects` by Baojun Xu 138 new firmware files for tas2783 were added, none of which are loaded by the kernel. Kernel expects files to be named with the following convention: "%04X-%1X-%1X.bin". However the added firmware files follow "-0x%1X.bin" naming instead with `0x` hex prefix, which fails to load resulting in following dmesg log: slave-tas2783 sdw:0:1:0102:0000:01:8: Direct firmware load for 1714-1-8.bin failed with error -2 slave-tas2783 sdw:0:1:0102:0000:01:8: Failed to read fw binary 1714-1-8.bin slave-tas2783 sdw:0:1:0102:0000:01:b: Direct firmware load for 1714-1-B.bin failed with error -2 slave-tas2783 sdw:0:1:0102:0000:01:b: Failed to read fw binary 1714-1-B.bin slave-tas2783 sdw:0:1:0102:0000:01:8: error playback without fw download slave-tas2783 sdw:0:1:0102:0000:01:8: ASoC error (-22): at snd_soc_dai_hw_params() on tas2783-codec This same commit removes all 22 symlinks from WHENCE, that used naming without the '0x' prefix to only 6 prevoiusly existing .bin files. This patch adds `0x` prefix explicitly to the generated firmware name allowing file to successfully load. In case prefixed firmware is missing due to out of date linux-firmware, we set the fallback flag and attempt to load firmware again based on the old file names. This prefix change results in functioning firmware loading on ASUS ProArt PX13 HN7306EAC, which uses 1714-1-0x8.bin and 1714-1-0xB.bin firmware files. Tested on top of 7.1 and next-20260619 with SND_SOC_AMD_ACP7X set to no. Signed-off-by: Bartosz Juraszewski Link: https://patch.msgid.link/20260622182733.23947-1-bjuraszewski@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2783-sdw.c | 73 +++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 1127ea59b5e4..3d0b116544cc 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -100,6 +100,8 @@ struct tas2783_prv { wait_queue_head_t fw_wait; bool fw_dl_task_done; bool fw_dl_success; + /* use fallback fw name */ + bool fw_use_fallback; }; static const struct reg_default tas2783_reg_default[] = { @@ -740,11 +742,19 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) goto out; } + /* firmware binary not found*/ if (!fmw || !fmw->data) { - /* firmware binary not found*/ - dev_err(tas_dev->dev, - "Failed to read fw binary %s\n", - tas_dev->rca_binaryname); + if (!tas_dev->fw_use_fallback) { + tas_dev->fw_use_fallback = true; + dev_info(tas_dev->dev, + "Failed to read preferred fw binary: %s, attempting fallback binary load\n", + tas_dev->rca_binaryname); + } else { + dev_err(tas_dev->dev, + "Failed to read fallback fw binary %s\n", + tas_dev->rca_binaryname); + } + ret = -EINVAL; goto out; } @@ -1105,13 +1115,16 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bool pci_found = false; #if IS_ENABLED(CONFIG_PCI) struct device *dev = &slave->dev; + struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev); struct pci_dev *pci = NULL; + const char *fw_uid_prefix = tas_dev->fw_use_fallback ? "" : "0x"; for (; dev; dev = dev->parent) { if (dev->bus == &pci_bus_type) { pci = to_pci_dev(dev); - scnprintf(name, size, "%04X-%1X-%1X.bin", - pci->subsystem_device, bus->link_id, unique_id); + scnprintf(name, size, "%04X-%1X-%s%1X.bin", + pci->subsystem_device, bus->link_id, + fw_uid_prefix, unique_id); pci_found = true; break; } @@ -1123,28 +1136,15 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bus->link_id, unique_id); } -static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave) { - struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; - if (tas_dev->hw_init) - return 0; - - tas_dev->fw_dl_task_done = false; - tas_dev->fw_dl_success = false; - - ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); - if (ret) { - dev_err(dev, "sw reset failed, err=%d", ret); - return ret; - } - usleep_range(2000, 2200); - tas_generate_fw_name(slave, tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname)); + tas_dev->fw_dl_task_done = false; ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev, tas2783_fw_ready); @@ -1159,8 +1159,35 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) msecs_to_jiffies(TIMEOUT_FW_DL_MS)); if (!ret) { dev_err(tas_dev->dev, "fw request, wait_event timeout\n"); - ret = -EAGAIN; - } else { + return -EAGAIN; + } + + return 0; +} + +static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct tas2783_prv *tas_dev = dev_get_drvdata(dev); + s32 ret; + + if (tas_dev->hw_init) + return 0; + + tas_dev->fw_dl_success = false; + + ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); + if (ret) { + dev_err(dev, "sw reset failed, err=%d", ret); + return ret; + } + usleep_range(2000, 2200); + + tas_dev->fw_use_fallback = false; + ret = tas_fw_load(tas_dev, slave); + if (!ret && tas_dev->fw_use_fallback) + ret = tas_fw_load(tas_dev, slave); + + if (!ret) { if (tas_dev->sa_func_data) ret = sdca_regmap_write_init(dev, tas_dev->regmap, tas_dev->sa_func_data); From d0c415f0076b71b956f62ff6f31de885f0fa2489 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 23 Jun 2026 18:25:14 +0800 Subject: [PATCH 4537/5214] ASoC: rt5575: Use __le32 for SPI burst write address The addr field in the SPI burst write buffer represents on-wire little-endian data. Define it as __le32 so that the assignment of cpu_to_le32() is type correct and avoids sparse endian warnings. Closes: https://lore.kernel.org/oe-kbuild-all/202606230139.rFZUVpCa-lkp@intel.com/ Fixes: 420739112e95 ("ASoC: rt5575: Add the codec driver for the ALC5575") Reported-by: kernel test robot Signed-off-by: Oder Chiou Link: https://patch.msgid.link/20260623102514.2422990-1-oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5575-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index 9a349965435b..d5b3a57c8866 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -17,7 +17,7 @@ struct rt5575_spi_burst_write { u8 cmd; - u32 addr; + __le32 addr; u8 data[RT5575_SPI_BUF_LEN]; u8 dummy; } __packed; From 2e9a7f68329be41792c0b123c28e6c53c2fa2249 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Jun 2026 16:49:34 +0200 Subject: [PATCH 4538/5214] i2c: mpc: Fix timeout calculations At first glance the harmless cleanup of the driver does nothing bad. However, as the operator precedence list states the '*' (multiplication) and '/' division operators have order 5 with left-to-right associativity the *= has order 17 and associativity right-to-left. It wouldn't be a problem to replace foo = foo * HZ / 1000000; with foo *= HZ / 1000000; if HZ constant is in Hertz. The problem is that in the Linux kernel HZ is defined in jiffy units, which is order of magnitude smaller than a million. That's why operator precedence has a crucial role here. Fix the regression by reverting pre-optimized calculations. Fixes: be40a3ae719f ("i2c: mpc: Use of_property_read_u32 instead of of_get_property") Signed-off-by: Andy Shevchenko Cc: # v6.4+ Reviewed-by: Chris Packham Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260618144934.3249950-1-andriy.shevchenko@linux.intel.com --- drivers/i2c/busses/i2c-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 28c5c5c1fb7a..a21fa45bd64c 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -844,7 +844,7 @@ static int fsl_i2c_probe(struct platform_device *op) "fsl,timeout", &mpc_ops.timeout); if (!result) { - mpc_ops.timeout *= HZ / 1000000; + mpc_ops.timeout = mpc_ops.timeout * HZ / 1000000; if (mpc_ops.timeout < 5) mpc_ops.timeout = 5; } else { From 823468a4ea1613f1c1235bd16af8b9c6eb9c9677 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:17 +0900 Subject: [PATCH 4539/5214] PCI: endpoint: pci-epf-vntb: Exclude reserved slots from db_valid_mask In pci-epf-vntb, db_count represents the total number of doorbell slots exposed to the peer, including: - slot #0 reserved for link events, and - slot #1 historically unused (kept for compatibility). Only the remaining slots correspond to actual doorbell bits. The current db_valid_mask() exposes all slots as valid doorbells. Limit db_valid_mask() to the real doorbell bits by returning BIT_ULL(db_count - 2) - 1, and guard against db_count < 2. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-7-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 524355a8b4be..58e41d95d029 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1364,7 +1364,10 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) { - return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; + if (ntb_ndev(ntb)->db_count < EPF_IRQ_DB_START) + return 0; + + return BIT_ULL(ntb_ndev(ntb)->db_count - EPF_IRQ_DB_START) - 1; } static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) From 10dd1a736d557e310a77117832874729a0175d57 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Tue, 12 May 2026 17:35:34 +0800 Subject: [PATCH 4540/5214] i2c: i801: fix hardware state machine corruption in error path A severe livelock and subsequent Hung Task panic were observed in the i2c-i801 driver during concurrent Fuzzing. The crash is caused by an unconditional hardware register cleanup in the error handling path of i801_access(). When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus controller is actively used by BIOS/ACPI), the kernel does not actually acquire the hardware ownership. However, the code jumps to the 'out' label and executes: iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); This forcefully clears the INUSE_STS lock and resets the hardware status flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI transactions and totally corrupts the SMBus hardware state machine. Consequently, all subsequent i801_access() calls fail at the pre-check stage, triggering an endless stream of "SMBus is busy, can't use it!" error logs. Over a slow serial console, this printk flood monopolizes the CPU (Console Livelock), starving other processes trying to acquire the mmap_lock down_read semaphore, ultimately triggering the hung task watchdog. Fix this by moving the 'out' label below the hardware register cleanup. If i801_check_pre() fails, we safely bypass the iowrite8() and only release the software locks (pm_runtime and mutex), strictly adhering to the rule of not releasing resources that were never acquired. Fixes: 1f760b87e54c ("i2c: i801: Call i801_check_pre() from i801_access()") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Cc: # v6.3+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260512093534.348655-1-w15303746062@163.com --- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 32a3cef02c7b..b29c99ed3883 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -931,13 +931,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, */ if (hwpec) iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); -out: /* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); +out: pm_runtime_put_autosuspend(&priv->pci_dev->dev); mutex_unlock(&priv->acpi_lock); return ret; From 2579f3f7f52090463596765040aaad71e91ef4d5 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:18 +0900 Subject: [PATCH 4541/5214] PCI: endpoint: pci-epf-vntb: Implement .db_vector_count()/mask() for doorbells Implement .db_vector_count() and .db_vector_mask() so NTB core/clients can map doorbell events to per-vector work and avoid the thundering-herd behavior. pci-epf-vntb reserves two slots in db_count: slot 0 for link events and slot 1 which is historically unused. Therefore the number of doorbell vectors is (db_count - 2). Report vectors as 0..N-1 and return BIT_ULL(db_vector) for the corresponding doorbell bit. Build db_valid_mask from a validated vector count so out-of-range db_count values cannot create invalid shifts. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-8-den@valinux.co.jp --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 58e41d95d029..c3caec927d74 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1362,12 +1362,48 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) return ntb_ndev(ntb)->num_mws; } -static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) +static int vntb_epf_db_vector_count(struct ntb_dev *ntb) { - if (ntb_ndev(ntb)->db_count < EPF_IRQ_DB_START) + struct epf_ntb *ndev = ntb_ndev(ntb); + u32 db_count = READ_ONCE(ndev->db_count); + + /* + * db_count is the total number of doorbell slots exposed to + * the peer, including: + * - slot #0 reserved for link events + * - slot #1 historically unused (kept for protocol compatibility) + * + * Report only usable per-vector doorbell interrupts. + */ + if (db_count < MIN_DB_COUNT || db_count > MAX_DB_COUNT) return 0; - return BIT_ULL(ntb_ndev(ntb)->db_count - EPF_IRQ_DB_START) - 1; + return db_count - EPF_IRQ_DB_START; +} + +static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) +{ + int nr_vec = vntb_epf_db_vector_count(ntb); + + if (!nr_vec) + return 0; + + return GENMASK_ULL(nr_vec - 1, 0); +} + +static u64 vntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + int nr_vec; + + /* + * Doorbell vectors are numbered [0 .. nr_vec - 1], where nr_vec + * excludes the two reserved slots described above. + */ + nr_vec = vntb_epf_db_vector_count(ntb); + if (db_vector < 0 || db_vector >= nr_vec) + return 0; + + return BIT_ULL(db_vector); } static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) @@ -1617,6 +1653,8 @@ static const struct ntb_dev_ops vntb_epf_ops = { .spad_count = vntb_epf_spad_count, .peer_mw_count = vntb_epf_peer_mw_count, .db_valid_mask = vntb_epf_db_valid_mask, + .db_vector_count = vntb_epf_db_vector_count, + .db_vector_mask = vntb_epf_db_vector_mask, .db_set_mask = vntb_epf_db_set_mask, .mw_set_trans = vntb_epf_mw_set_trans, .mw_clear_trans = vntb_epf_mw_clear_trans, From 84af8a5f5ef24b74f44470e7e6af7089ef125e84 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:19 +0900 Subject: [PATCH 4542/5214] NTB: epf: Document legacy doorbell slot offset in ntb_epf_peer_db_set() ntb_epf_peer_db_set() uses ffs(db_bits) to select a doorbell to ring. ffs() returns a 1-based bit index (bit 0 -> 1). Entry 0 is reserved for link events, so doorbell bit 0 must map to entry 1. However, since the initial commit 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge"), the implementation has been adding an extra +1, ending up using entry 2 for bit 0. Fixing the extra increment would break interoperability with peers running older kernels. Keep the legacy behavior and document the offset and the resulting slot layout to avoid confusion when enabling per-db-vector handling. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-9-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 8925c688930c..21d942824983 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -43,6 +43,18 @@ #define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4) #define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4) +/* + * Legacy doorbell slot layout when paired with pci-epf-*ntb: + * + * slot 0 : reserved for link events + * slot 1 : unused (historical extra offset) + * slot 2 : DB#0 + * slot 3 : DB#1 + * ... + * + * Thus, NTB_EPF_MIN_DB_COUNT=3 means that we at least create vectors for + * doorbells DB#0 and DB#1. + */ #define NTB_EPF_MIN_DB_COUNT 3 #define NTB_EPF_MAX_DB_COUNT 31 @@ -473,6 +485,14 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); + /* + * ffs() returns a 1-based bit index (bit 0 -> 1). + * + * With slot 0 reserved for link events, DB#0 would naturally map to + * slot 1. Historically an extra +1 offset was added, so DB#0 maps to + * slot 2 and slot 1 remains unused. Keep this mapping for + * backward-compatibility. + */ u32 interrupt_num = ffs(db_bits) + 1; struct device *dev = ndev->dev; u32 db_entry_size; From 6eb7e28f1f24a28234add38755687a7ed8bc2654 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:20 +0900 Subject: [PATCH 4543/5214] NTB: epf: Make db_valid_mask cover only real doorbell bits ndev->db_count includes an unused doorbell slot due to the legacy extra offset in the peer doorbell path. db_valid_mask must cover only the real doorbell bits and exclude the unused slot. Set db_valid_mask to BIT_ULL(db_count - 1) - 1. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260513024923.451765-10-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 21d942824983..c0bab3292075 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -580,7 +580,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) return ret; } - ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; + /* + * ndev->db_count includes an extra skipped slot due to the legacy + * doorbell layout, hence -1. + */ + ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); From 3147f0964cecca15e349cbfbdd6a28eb6d50379d Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:21 +0900 Subject: [PATCH 4544/5214] NTB: epf: Report 0-based doorbell vector via ntb_db_event() ntb_db_event() expects the vector number to be relative to the first doorbell vector starting at 0. Vector 0 is reserved for link events in the EPF driver, so doorbells start at vector 1. However, both supported peers (ntb_hw_epf with pci-epf-ntb, and pci-epf-vntb) have historically skipped vector 1 and started doorbells at vector 2. Pass (irq_no - 2) to ntb_db_event() so doorbells are reported as 0..N-1. If irq_no == 1 is ever observed, warn and ignore it, since the slot is reserved in the legacy layout and reporting it as DB#0 would collide with the real DB#0 slot. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-11-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index c0bab3292075..7b0fc7ef00c6 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -81,6 +81,12 @@ enum epf_ntb_bar { NTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) struct ntb_epf_dev { @@ -334,10 +340,14 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; - if (irq_no == 0) + if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); - else - ntb_db_event(&ndev->ntb, irq_no); + } else if (irq_no == EPF_IRQ_RESERVED_DB) { + dev_warn_ratelimited(ndev->dev, + "Unexpected reserved doorbell slot IRQ received\n"); + } else { + ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + } return IRQ_HANDLED; } From 4fdea8dbb62d746429498e07385a3ba0b0f6d1ab Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:22 +0900 Subject: [PATCH 4545/5214] NTB: epf: Fix doorbell bitmask and IRQ vector handling The EPF driver currently stores the incoming doorbell as a vector number (irq_no + 1) in db_val and db_clear() clears all bits unconditionally. This breaks db_read()/db_clear() semantics when multiple doorbells are used. Store doorbells as a bitmask (BIT_ULL(vector)) and make db_clear(db_bits) clear only the specified bits. Use atomic64 operations as db_val is updated from interrupt context. Once db_val is stored as a bitmask, the ISR's doorbell vector is used not only for ntb_db_event(), but also as the bit index for BIT_ULL(). The existing ISR derives that vector by subtracting pci_irq_vector(pdev, 0) from the Linux IRQ number passed to the handler, but Linux IRQ numbers are not guaranteed to be contiguous. Pass per-vector context as request_irq() dev_id instead, so the ISR gets the device vector directly. Validate the doorbell vector before updating db_val or calling ntb_db_event(), so an unexpected vector cannot create an invalid shift or be reported to NTB clients. While at it, read and validate mw_count before requesting interrupt vectors. An unsupported memory-window count does not need IRQs, and failing before ntb_epf_init_isr() keeps the probe error path simple. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-12-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 61 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 7b0fc7ef00c6..10618e462229 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I */ +#include #include #include #include @@ -89,6 +90,13 @@ enum epf_irq_slot { #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) +struct ntb_epf_dev; + +struct ntb_epf_irq_ctx { + struct ntb_epf_dev *ndev; + unsigned int irq_no; +}; + struct ntb_epf_dev { struct ntb_dev ntb; struct device *dev; @@ -108,9 +116,9 @@ struct ntb_epf_dev { unsigned int self_spad; unsigned int peer_spad; - int db_val; + atomic64_t db_val; u64 db_valid_mask; - int irq_base; + struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1]; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -334,11 +342,10 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb) static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) { - struct ntb_epf_dev *ndev = dev; - int irq_no; - - irq_no = irq - ndev->irq_base; - ndev->db_val = irq_no + 1; + struct ntb_epf_irq_ctx *ctx = dev; + struct ntb_epf_dev *ndev = ctx->ndev; + unsigned int db_vector; + unsigned int irq_no = ctx->irq_no; if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); @@ -346,7 +353,17 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) dev_warn_ratelimited(ndev->dev, "Unexpected reserved doorbell slot IRQ received\n"); } else { - ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + db_vector = irq_no - EPF_IRQ_DB_START; + if (ndev->db_count < NTB_EPF_MIN_DB_COUNT || + db_vector >= ndev->db_count - 1) { + dev_warn_ratelimited(ndev->dev, + "Unexpected doorbell vector %u (db_count %u)\n", + db_vector, ndev->db_count); + return IRQ_HANDLED; + } + + atomic64_or(BIT_ULL(db_vector), &ndev->db_val); + ntb_db_event(&ndev->ntb, db_vector); } return IRQ_HANDLED; @@ -373,18 +390,18 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } - ndev->irq_base = pci_irq_vector(pdev, 0); + ndev->db_count = irq - 1; for (i = 0; i < irq; i++) { + ndev->irq_ctx[i].ndev = ndev; + ndev->irq_ctx[i].irq_no = i; ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, - 0, "ntb_epf", ndev); + 0, "ntb_epf", &ndev->irq_ctx[i]); if (ret) { dev_err(dev, "Failed to request irq\n"); goto err_free_irq; } } - ndev->db_count = irq - 1; - ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, argument | irq); if (ret) { @@ -396,7 +413,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) err_free_irq: while (i--) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); return ret; @@ -529,7 +546,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - return ndev->db_val; + return atomic64_read(&ndev->db_val); } static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) @@ -541,7 +558,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - ndev->db_val = 0; + atomic64_and(~db_bits, &ndev->db_val); return 0; } @@ -582,6 +599,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) struct device *dev = ndev->dev; int ret; + ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { + dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); + return -EINVAL; + } + /* One Link interrupt and rest doorbell interrupt */ ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, NTB_EPF_MAX_DB_COUNT + 1); @@ -595,14 +618,8 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) * doorbell layout, hence -1. */ ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; - ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); - if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { - dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); - return -EINVAL; - } - return 0; } @@ -696,7 +713,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); for (i = 0; i < ndev->db_count + 1; i++) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); } From 1fda82e37e00eb679d762756867b2e7508dd73f9 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:23 +0900 Subject: [PATCH 4546/5214] NTB: epf: Implement .db_vector_count()/mask() for doorbells Implement .db_vector_count() and .db_vector_mask() so NTB core/clients can map doorbell events to per-vector work. Report vectors as 0..(db_count - 2) (skipping the unused slot) and return BIT_ULL(db_vector) for the corresponding doorbell bit. Use ntb_epf_db_vector_count() for bounds checks in ntb_epf_db_vector_mask(), so the same lower-bound guard is applied before building the bitmask. Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260513024923.451765-13-den@valinux.co.jp --- drivers/ntb/hw/epf/ntb_hw_epf.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 10618e462229..af5755472842 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -434,6 +434,36 @@ static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb) return ntb_ndev(ntb)->db_valid_mask; } +static int ntb_epf_db_vector_count(struct ntb_dev *ntb) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + unsigned int db_count = ndev->db_count; + + /* + * db_count includes an extra skipped slot due to the legacy + * doorbell layout. Expose only the real doorbell vectors. + */ + if (db_count < NTB_EPF_MIN_DB_COUNT) + return 0; + + return db_count - 1; +} + +static u64 ntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + int nr_vec; + + /* + * db_count includes one skipped slot in the legacy layout. Valid + * doorbell vectors are therefore [0 .. (db_count - 2)]. + */ + nr_vec = ntb_epf_db_vector_count(ntb); + if (db_vector < 0 || db_vector >= nr_vec) + return 0; + + return BIT_ULL(db_vector); +} + static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) { return 0; @@ -568,6 +598,8 @@ static const struct ntb_dev_ops ntb_epf_ops = { .spad_count = ntb_epf_spad_count, .peer_mw_count = ntb_epf_peer_mw_count, .db_valid_mask = ntb_epf_db_valid_mask, + .db_vector_count = ntb_epf_db_vector_count, + .db_vector_mask = ntb_epf_db_vector_mask, .db_set_mask = ntb_epf_db_set_mask, .mw_set_trans = ntb_epf_mw_set_trans, .mw_clear_trans = ntb_epf_mw_clear_trans, From 29bfc3523fa4e41aef2b67ce086dbfb2ccb2564f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:08 +0300 Subject: [PATCH 4547/5214] PCI: Rename 'added' to 'add_list' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The resource fitting algorithm uses different names from the list holding the optional sizes: added, add_head, add_list, and realloc_head. 'add_list' sounds the most natural and some of the related variables also use 'add' such as 'add_size'. To reduce variation, rename 'added' and 'add_head' to 'add_list'. Also rename some 'realloc_head' cases selectively to 'add_list'. While it would be nice to rename every 'realloc_head' to 'add_list' for consistency, it might create a backport headache with all the work going into this algorithm that may need to be eventually backported. Thus, it's better to leave 'realloc_head' as is for now. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-3-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.h | 2 +- drivers/pci/setup-bus.c | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4a14f88e543a..4fcf5a25ad9e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -515,7 +515,7 @@ int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); void __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *realloc_head, + struct list_head *add_list, struct list_head *fail_head); bool pci_bus_clip_resource(struct pci_dev *dev, int idx); void pci_walk_bus_locked(struct pci_bus *top, diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 4cf120ebe5ad..3765693e95f0 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -756,13 +756,13 @@ static void __assign_resources_sorted(struct list_head *head, } static void pdev_assign_resources_sorted(struct pci_dev *dev, - struct list_head *add_head, + struct list_head *add_list, struct list_head *fail_head) { LIST_HEAD(head); pdev_sort_resources(dev, &head); - __assign_resources_sorted(&head, add_head, fail_head); + __assign_resources_sorted(&head, add_list, fail_head); } @@ -1502,13 +1502,13 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev) } void __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *realloc_head, + struct list_head *add_list, struct list_head *fail_head) { struct pci_bus *b; struct pci_dev *dev; - pbus_assign_resources_sorted(bus, realloc_head, fail_head); + pbus_assign_resources_sorted(bus, add_list, fail_head); list_for_each_entry(dev, &bus->devices, bus_list) { pdev_assign_fixed_resources(dev); @@ -1517,7 +1517,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, if (!b) continue; - __pci_bus_assign_resources(b, realloc_head, fail_head); + __pci_bus_assign_resources(b, add_list, fail_head); switch (dev->hdr_type) { case PCI_HEADER_TYPE_BRIDGE: @@ -1613,19 +1613,19 @@ void pci_bus_claim_resources(struct pci_bus *b) EXPORT_SYMBOL(pci_bus_claim_resources); static void __pci_bridge_assign_resources(const struct pci_dev *bridge, - struct list_head *add_head, + struct list_head *add_list, struct list_head *fail_head) { struct pci_bus *b; pdev_assign_resources_sorted((struct pci_dev *)bridge, - add_head, fail_head); + add_list, fail_head); b = bridge->subordinate; if (!b) return; - __pci_bus_assign_resources(b, add_head, fail_head); + __pci_bus_assign_resources(b, add_list, fail_head); switch (bridge->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -2303,7 +2303,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * unsigned long type = res->flags; struct pci_dev_resource *dev_res; struct pci_dev *bridge = NULL; - LIST_HEAD(added); + LIST_HEAD(add_list); LIST_HEAD(failed); unsigned int i; int ret = 0; @@ -2337,10 +2337,10 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * if (!bridge) return -ENOENT; - __pci_bus_size_bridges(bridge->subordinate, &added); - __pci_bridge_assign_resources(bridge, &added, &failed); - if (WARN_ON_ONCE(!list_empty(&added))) - pci_dev_res_free_list(&added); + __pci_bus_size_bridges(bridge->subordinate, &add_list); + __pci_bridge_assign_resources(bridge, &add_list, &failed); + if (WARN_ON_ONCE(!list_empty(&add_list))) + pci_dev_res_free_list(&add_list); if (!list_empty(&failed)) { if (pci_required_resource_failed(&failed, type)) From 2c90aeab5a5598f8bb17214579a889f1eed71bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:09 +0300 Subject: [PATCH 4548/5214] PCI: Consolidate add_list (aka realloc_head) empty sanity checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Callers of __pci_bridge_assign_resources() and __pci_bus_assign_resources() perform WARN_ON_ONCE(list_empty(add_list))) checks to sanity check that all optional sizes were processed (and removed) from the list. The empty list sanity check is duplicated code so the more appropriate place for it would be inside the called function. Placing the empty list check into __pci_bus_assign_resources() also ensures all callsites do perform the sanity check which currently is not the case when being called from enable_slot(). This inconsistency was noted by Sashiko though only inside its in depth log but not flagged as a real problem, possibly because this is only a sanity check that should never fire. Nonetheless, this sanity check has been very useful to catch problems early in the past so it's good to do it consistently everywhere. As __pci_bus_assign_resources() is a recursive function, it needs to be renamed to __pci_bus_assign_resources_one() to only perform the empty list check at the end of processing the entire hierarchy in __pci_bus_assign_resources(). Suggested-by: sashiko.dev # Sanity check missing from enable_slot() Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-4-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-bus.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 3765693e95f0..1e0e28efe8b8 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1501,9 +1501,9 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev) } } -void __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *add_list, - struct list_head *fail_head) +static void __pci_bus_assign_resources_one(const struct pci_bus *bus, + struct list_head *add_list, + struct list_head *fail_head) { struct pci_bus *b; struct pci_dev *dev; @@ -1517,7 +1517,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, if (!b) continue; - __pci_bus_assign_resources(b, add_list, fail_head); + __pci_bus_assign_resources_one(b, add_list, fail_head); switch (dev->hdr_type) { case PCI_HEADER_TYPE_BRIDGE: @@ -1537,6 +1537,16 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, } } +void __pci_bus_assign_resources(const struct pci_bus *bus, + struct list_head *add_list, + struct list_head *fail_head) +{ + __pci_bus_assign_resources_one(bus, add_list, fail_head); + + if (WARN_ON_ONCE(add_list && !list_empty(add_list))) + pci_dev_res_free_list(add_list); +} + void pci_bus_assign_resources(const struct pci_bus *bus) { __pci_bus_assign_resources(bus, NULL, NULL); @@ -1641,6 +1651,9 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, pci_domain_nr(b), b->number); break; } + + if (WARN_ON_ONCE(add_list && !list_empty(add_list))) + pci_dev_res_free_list(add_list); } static void pci_bridge_release_resources(struct pci_bus *bus, @@ -2205,8 +2218,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) /* Depth last, allocate resources and update the hardware. */ __pci_bus_assign_resources(bus, add_list, &fail_head); - if (WARN_ON_ONCE(add_list && !list_empty(add_list))) - pci_dev_res_free_list(add_list); tried_times++; /* Any device complain? */ @@ -2268,8 +2279,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) pci_bridge_distribute_available_resources(bridge, &add_list); __pci_bridge_assign_resources(bridge, &add_list, &fail_head); - if (WARN_ON_ONCE(!list_empty(&add_list))) - pci_dev_res_free_list(&add_list); tried_times++; if (list_empty(&fail_head)) @@ -2339,8 +2348,6 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * __pci_bus_size_bridges(bridge->subordinate, &add_list); __pci_bridge_assign_resources(bridge, &add_list, &failed); - if (WARN_ON_ONCE(!list_empty(&add_list))) - pci_dev_res_free_list(&add_list); if (!list_empty(&failed)) { if (pci_required_resource_failed(&failed, type)) @@ -2473,7 +2480,5 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) __pci_bus_size_bridges(dev->subordinate, &add_list); up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); - if (WARN_ON_ONCE(!list_empty(&add_list))) - pci_dev_res_free_list(&add_list); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); From 854f9522a20b6b61a0d6fc9db53e8a1437f65f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:10 +0300 Subject: [PATCH 4549/5214] PCI: Remove const removal cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __pci_bridge_assign_resources() inputs const pci_dev *bridge, but then immediately casts const away to pass the bridge to pdev_assign_resources_sorted(). As pdev_assign_resources_sorted() performs assignment of resources, it is not possible to make its input parameter to const. Neither of the __pci_bridge_assign_resources() callers requires the bridge parameter to be const. Thus, simply remove the out of place cast and convert the input parameter to non-const. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-5-ilpo.jarvinen@linux.intel.com --- drivers/pci/setup-bus.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1e0e28efe8b8..c0a949f2c995 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1622,14 +1622,13 @@ void pci_bus_claim_resources(struct pci_bus *b) } EXPORT_SYMBOL(pci_bus_claim_resources); -static void __pci_bridge_assign_resources(const struct pci_dev *bridge, +static void __pci_bridge_assign_resources(struct pci_dev *bridge, struct list_head *add_list, struct list_head *fail_head) { struct pci_bus *b; - pdev_assign_resources_sorted((struct pci_dev *)bridge, - add_list, fail_head); + pdev_assign_resources_sorted(bridge, add_list, fail_head); b = bridge->subordinate; if (!b) From 71c6e7808ee99a6e1b29bc30486baf825f9ec728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:11 +0300 Subject: [PATCH 4550/5214] resource: Make resource_alignment() input const resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resource_alignment() does not need to change resource so it can be made const. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-6-ilpo.jarvinen@linux.intel.com --- include/linux/ioport.h | 2 +- kernel/resource.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 3c73c9c0d4f7..f7930b3dfd0a 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -261,7 +261,7 @@ extern int allocate_resource(struct resource *root, struct resource *new, struct resource *lookup_resource(struct resource *root, resource_size_t start); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); -resource_size_t resource_alignment(struct resource *res); +resource_size_t resource_alignment(const struct resource *res); /** * resource_set_size - Calculate resource end address from size and start diff --git a/kernel/resource.c b/kernel/resource.c index d02a53fb95d8..3d17e3196a3e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1238,7 +1238,7 @@ reserve_region_with_split(struct resource *root, resource_size_t start, * * Returns alignment on success, 0 (invalid alignment) on failure. */ -resource_size_t resource_alignment(struct resource *res) +resource_size_t resource_alignment(const struct resource *res) { switch (res->flags & (IORESOURCE_SIZEALIGN | IORESOURCE_STARTALIGN)) { case IORESOURCE_SIZEALIGN: From 9f331c50b31fd03c7b9e36cc5790e81675a054f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:12 +0300 Subject: [PATCH 4551/5214] powerpc/pseries: Make pseries_get_iov_fw_value() & pnv_iov_get() pci_dev const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert input pci_dev for pseries_get_iov_fw_value() and pnv_iov_get() to const to be able to convert pcibios_iov_resource_alignment() as well in an upcoming change. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-7-ilpo.jarvinen@linux.intel.com --- arch/powerpc/platforms/powernv/pci.h | 2 +- arch/powerpc/platforms/pseries/setup.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 42075501663b..032b2081aedb 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -251,7 +251,7 @@ struct pnv_iov_data { struct resource holes[PCI_SRIOV_NUM_BARS]; }; -static inline struct pnv_iov_data *pnv_iov_get(struct pci_dev *pdev) +static inline struct pnv_iov_data *pnv_iov_get(const struct pci_dev *pdev) { return pdev->dev.archdata.iov_data; } diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 50b26ed8432d..b670c6fdfcea 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -658,7 +658,8 @@ enum get_iov_fw_value_index { WDW_SIZE = 3 /* Get Window Size */ }; -static resource_size_t pseries_get_iov_fw_value(struct pci_dev *dev, int resno, +static resource_size_t pseries_get_iov_fw_value(const struct pci_dev *dev, + int resno, enum get_iov_fw_value_index value) { const int *indexes; From 79a648209dadcc0f2ff0f27100fd79d1c890ccf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:13 +0300 Subject: [PATCH 4552/5214] PCI: Make pci_sriov_resource_alignment() pci_dev const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_sriov_resource_alignment() inputs struct pci_dev which it should not need to alter to calculate alignment. Make pci_dev pci_sriov_resource_alignment() inputs const. It requires making pci_iov_resource_size() input const as well. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-8-ilpo.jarvinen@linux.intel.com --- arch/powerpc/include/asm/machdep.h | 2 +- arch/powerpc/kernel/pci-common.c | 2 +- arch/powerpc/platforms/powernv/pci-sriov.c | 4 ++-- arch/powerpc/platforms/powernv/pci.h | 3 ++- arch/powerpc/platforms/pseries/setup.c | 2 +- drivers/pci/iov.c | 7 ++++--- drivers/pci/pci.h | 5 +++-- include/linux/pci.h | 8 +++++--- 8 files changed, 19 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 3298eec123a3..256f9309bf4f 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -169,7 +169,7 @@ struct machdep_calls { #ifdef CONFIG_PCI_IOV void (*pcibios_fixup_sriov)(struct pci_dev *pdev); - resource_size_t (*pcibios_iov_resource_alignment)(struct pci_dev *, int resno); + resource_size_t (*pcibios_iov_resource_alignment)(const struct pci_dev *, int resno); int (*pcibios_sriov_enable)(struct pci_dev *pdev, u16 num_vfs); int (*pcibios_sriov_disable)(struct pci_dev *pdev); #endif /* CONFIG_PCI_IOV */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8efe95a0c4ff..3c4ca90e2ab7 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -254,7 +254,7 @@ resource_size_t pcibios_default_alignment(void) } #ifdef CONFIG_PCI_IOV -resource_size_t pcibios_iov_resource_alignment(struct pci_dev *pdev, int resno) +resource_size_t pcibios_iov_resource_alignment(const struct pci_dev *pdev, int resno) { if (ppc_md.pcibios_iov_resource_alignment) return ppc_md.pcibios_iov_resource_alignment(pdev, resno); diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 7105a573aec4..8652078801f2 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -244,8 +244,8 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) } } -resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, - int resno) +resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev, + int resno) { resource_size_t align = pci_iov_resource_size(pdev, resno); struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 032b2081aedb..3ac718d471c2 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -257,7 +257,8 @@ static inline struct pnv_iov_data *pnv_iov_get(const struct pci_dev *pdev) } void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev); -resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno); +resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev, + int resno); int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); int pnv_pcibios_sriov_disable(struct pci_dev *pdev); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index b670c6fdfcea..1223dc961242 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -789,7 +789,7 @@ static void pseries_pci_fixup_iov_resources(struct pci_dev *pdev) pseries_disable_sriov_resources(pdev); } -static resource_size_t pseries_pci_iov_resource_alignment(struct pci_dev *pdev, +static resource_size_t pseries_pci_iov_resource_alignment(const struct pci_dev *pdev, int resno) { const __be32 *reg; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 91ac4e37ecb9..c86409835f73 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -150,7 +150,7 @@ static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) pci_remove_bus(virtbus); } -resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno) { if (!dev->is_physfn) return 0; @@ -1084,7 +1084,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno) } } -resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, +resource_size_t __weak pcibios_iov_resource_alignment(const struct pci_dev *dev, int resno) { return pci_iov_resource_size(dev, resno); @@ -1100,7 +1100,8 @@ resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, * the VF BAR size multiplied by the number of VFs. The alignment * is just the VF BAR size. */ -resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) +resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, + int resno) { return pcibios_iov_resource_alignment(dev, resno); } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4fcf5a25ad9e..710803be3a79 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -947,7 +947,8 @@ int pci_iov_init(struct pci_dev *dev); void pci_iov_release(struct pci_dev *dev); void pci_iov_remove(struct pci_dev *dev); void pci_iov_update_resource(struct pci_dev *dev, int resno); -resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); +resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, + int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size); @@ -981,7 +982,7 @@ static inline int pci_iov_init(struct pci_dev *dev) static inline void pci_iov_release(struct pci_dev *dev) { } static inline void pci_iov_remove(struct pci_dev *dev) { } static inline void pci_iov_update_resource(struct pci_dev *dev, int resno) { } -static inline resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, +static inline resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, int resno) { return 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..0ced3bbd08c0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2540,7 +2540,7 @@ int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); int pci_sriov_get_totalvfs(struct pci_dev *dev); int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn); -resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno); +resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno); int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size); u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs); void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe); @@ -2548,7 +2548,8 @@ void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe); /* Arch may override these (weak) */ int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); int pcibios_sriov_disable(struct pci_dev *pdev); -resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno); +resource_size_t pcibios_iov_resource_alignment(const struct pci_dev *dev, + int resno); #else static inline int pci_iov_virtfn_bus(struct pci_dev *dev, int id) { @@ -2593,7 +2594,8 @@ static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) static inline int pci_sriov_get_totalvfs(struct pci_dev *dev) { return 0; } #define pci_sriov_configure_simple NULL -static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +static inline resource_size_t pci_iov_resource_size(const struct pci_dev *dev, + int resno) { return 0; } static inline int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size) { return -ENODEV; } From 1845201aa572807e1639fe20b06625d738391fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:14 +0300 Subject: [PATCH 4553/5214] PCI: Convert pci_resource_alignment() input parameters to const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_resource_alignment() calculates resource alignment and should not alter its input structs. Make its input parameters const. It requires making also pci_cardbus_resource_alignment() input const. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-9-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.h | 8 ++++---- drivers/pci/setup-cardbus.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 710803be3a79..e0fcc33dfef6 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -419,7 +419,7 @@ static inline bool pci_is_cardbus_bridge(struct pci_dev *dev) return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; } #ifdef CONFIG_CARDBUS -unsigned long pci_cardbus_resource_alignment(struct resource *res); +unsigned long pci_cardbus_resource_alignment(const struct resource *res); int pci_bus_size_cardbus_bridge(struct pci_bus *bus, struct list_head *realloc_head); int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, @@ -428,7 +428,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int pci_setup_cardbus(char *str); #else -static inline unsigned long pci_cardbus_resource_alignment(struct resource *res) +static inline unsigned long pci_cardbus_resource_alignment(const struct resource *res) { return 0; } @@ -1044,8 +1044,8 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { } static inline void pci_resume_ptm(struct pci_dev *dev) { } #endif -static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, - struct resource *res) +static inline resource_size_t pci_resource_alignment(const struct pci_dev *dev, + const struct resource *res) { int resno = pci_resource_num(dev, res); diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c index 1ebd13a1f730..0cba404080ad 100644 --- a/drivers/pci/setup-cardbus.c +++ b/drivers/pci/setup-cardbus.c @@ -22,7 +22,7 @@ static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; -unsigned long pci_cardbus_resource_alignment(struct resource *res) +unsigned long pci_cardbus_resource_alignment(const struct resource *res) { if (res->flags & IORESOURCE_IO) return pci_cardbus_io_size; From e9310aa3d2a1fa155565e253841fff841e31db64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 29 Apr 2026 15:26:15 +0300 Subject: [PATCH 4554/5214] PCI: Move pci_resource_alignment() to setup-res.c file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_resource_alignment() is a bit on the complex side to have in a header so put it into setup-res.c. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260429122617.7324-10-ilpo.jarvinen@linux.intel.com --- drivers/pci/pci.h | 13 ++----------- drivers/pci/setup-res.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e0fcc33dfef6..472b6c2f7c4d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1044,17 +1044,8 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { } static inline void pci_resume_ptm(struct pci_dev *dev) { } #endif -static inline resource_size_t pci_resource_alignment(const struct pci_dev *dev, - const struct resource *res) -{ - int resno = pci_resource_num(dev, res); - - if (pci_resource_is_iov(resno)) - return pci_sriov_resource_alignment(dev, resno); - if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) - return pci_cardbus_resource_alignment(res); - return resource_alignment(res); -} +resource_size_t pci_resource_alignment(const struct pci_dev *dev, + const struct resource *res); resource_size_t pci_min_window_alignment(struct pci_bus *bus, unsigned long type); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 0d203325562b..18e8775ea848 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -246,6 +246,18 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, return 0; } +resource_size_t pci_resource_alignment(const struct pci_dev *dev, + const struct resource *res) +{ + int resno = pci_resource_num(dev, res); + + if (pci_resource_is_iov(resno)) + return pci_sriov_resource_alignment(dev, resno); + if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) + return pci_cardbus_resource_alignment(res); + return resource_alignment(res); +} + /* * For mem bridge windows, try to relocate tail remainder space to space * before res->start if there's enough free space there. This enables From c6d51515ee0b79a52b7e532b6b20067fa0cec96f Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 1 Apr 2026 01:12:47 +0800 Subject: [PATCH 4555/5214] dt-bindings: PCI: sophgo: Add dma-coherent property for SG2042 Add dma-coherent as an allowed property in the SG2042 PCIe host controller binding. SG2042's PCIe Root Complexes are cache-coherent with the CPU. Signed-off-by: Han Gao Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260331171248.973014-2-gaohan@iscas.ac.cn --- .../devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml index f8b7ca57fff1..ab482488b047 100644 --- a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml @@ -30,6 +30,8 @@ properties: device-id: const: 0x2042 + dma-coherent: true + msi-parent: true allOf: @@ -60,5 +62,6 @@ examples: vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; }; From e8433f632171c5a8c9c43dfa61d347b09ab1ecc3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 1 May 2026 11:24:07 +0100 Subject: [PATCH 4556/5214] dt-bindings: PCI: renesas,r9a08g045-pcie: Add RZ/V2N support Document the Renesas RZ/V2N PCIe host controller, which is compatible with the RZ/G3E PCIe IP and therefore uses it as a fallback compatible. The only difference is that it uses device ID 0x003B. Make the binding title generic to avoid extending the title for each new SoC, and update the description to list the supported SoCs and their capabilities. Signed-off-by: Lad Prabhakar Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Claudiu Beznea Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260501102407.29462-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- .../bindings/pci/renesas,r9a08g045-pcie.yaml | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml index a67108c48feb..90086909e921 100644 --- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml @@ -4,21 +4,27 @@ $id: http://devicetree.org/schemas/pci/renesas,r9a08g045-pcie.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/G3S PCIe host controller +title: Renesas RZ/G3S PCIe host controller (and similar SoCs) maintainers: - Claudiu Beznea -description: - Renesas RZ/G3{E,S} PCIe host controllers comply with PCIe - Base Specification 4.0 and support up to 5 GT/s (Gen2) for RZ/G3S and - up to 8 GT/s (Gen3) for RZ/G3E. +description: | + PCIe host controller found in Renesas RZ/G3S and similar SoCs complies + with PCIe Base Specification 4.0 and supports different link speeds + depending on the SoC variant: + - Gen2 (5 GT/s): RZ/G3S + - Gen3 (8 GT/s): RZ/G3E, RZ/V2N properties: compatible: - enum: - - renesas,r9a08g045-pcie # RZ/G3S - - renesas,r9a09g047-pcie # RZ/G3E + oneOf: + - enum: + - renesas,r9a08g045-pcie # RZ/G3S + - renesas,r9a09g047-pcie # RZ/G3E + - items: + - const: renesas,r9a09g056-pcie # RZ/V2N + - const: renesas,r9a09g047-pcie reg: maxItems: 1 @@ -152,6 +158,7 @@ patternProperties: enum: - 0x0033 - 0x0039 + - 0x003b clocks: items: From ad4c2a8fd8fda50bc97ca40ad679d5c92311ae15 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Mon, 8 Jun 2026 14:18:14 +0530 Subject: [PATCH 4557/5214] dt-bindings: PCI: qcom,pcie-sm8550: Add Eliza compatible PCIe controller present in Eliza SoC is backwards compatible with the controller present in SM8550 SoC. Hence, add the compatible with SM8550 fallback. Eliza requires 6 reg entries, 8 clocks and 9 interrupts, so add the corresponding allOf constraints. Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260608-eliza-v3-2-9bdeb7434b28@oss.qualcomm.com --- .../bindings/pci/qcom,pcie-sm8550.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml index 3a94a9c1bb15..fb706b1397a3 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml @@ -20,6 +20,7 @@ properties: - const: qcom,pcie-sm8550 - items: - enum: + - qcom,eliza-pcie - qcom,kaanapali-pcie - qcom,sar2130p-pcie - qcom,pcie-sm8650 @@ -91,6 +92,55 @@ required: allOf: - $ref: qcom,pcie-common.yaml# + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + reg: + minItems: 6 + reg-names: + minItems: 6 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + clocks: + minItems: 8 + maxItems: 8 + clock-names: + minItems: 8 + maxItems: 8 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + interrupts: + minItems: 9 + interrupt-names: + minItems: 9 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + resets: + minItems: 2 + reset-names: + minItems: 2 unevaluatedProperties: false From 57d3400df05222ee8ab32223150df2472a471d02 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:42 +0800 Subject: [PATCH 4558/5214] PCI: cadence-hpa: Add post-link delay The Cadence HPA (High Performance Architecture IP) specific link setup function cdns_pcie_hpa_host_link_setup() waits for the link to come up but does not implement the required 100 ms delay after link training completes for speeds > 5.0 GT/s (PCIe r6.0 sec 6.6.1). Add a call to pci_host_common_link_train_delay() immediately after the link is confirmed to be up, using the max_link_speed field. Also, in the HPA host setup function, read the device tree property "max-link-speed" to initialize max_link_speed if not already set by a glue driver. This ensures compliance for HPA-based platforms. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam [bhelgaas: driver tag "cadence: HPA:" -> "cadence-hpa:"] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260518004246.1384532-4-18255117159@163.com --- drivers/pci/controller/cadence/pcie-cadence-host-hpa.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c index 0f540bed58e8..8ef58ed01daa 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c @@ -15,6 +15,8 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../pci-host-common.h" +#include "../../pci.h" static u8 bar_aperture_mask[] = { [RP_BAR0] = 0x3F, @@ -304,6 +306,8 @@ int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc) ret = cdns_pcie_host_wait_for_link(pcie, cdns_pcie_hpa_link_up); if (ret) dev_dbg(dev, "PCIe link never came up\n"); + else + pci_host_common_link_train_delay(pcie->max_link_speed); return ret; } @@ -313,6 +317,7 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; struct pci_host_bridge *bridge; enum cdns_pcie_rp_bar bar; struct cdns_pcie *pcie; @@ -343,6 +348,9 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc) rc->cfg_res = res; } + if (pcie->max_link_speed < 1) + pcie->max_link_speed = of_pci_get_max_link_speed(np); + /* Put EROM Bar aperture to 0 */ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_EROM, 0x0); From 0a46957df45af0382ccf0cc43f9b0a1df438605d Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:43 +0800 Subject: [PATCH 4559/5214] PCI: dwc: Use common pci_host_common_link_train_delay() helper The DWC driver already implements the 100 ms delay required by PCIe r6.0 sec 6.6.1 by checking pci->max_link_speed and calling msleep(100). Replace the open-coded msleep() with the new common helper pci_host_common_link_train_delay() to reduce code duplication and improve maintainability. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260518004246.1384532-5-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..7021d21bb601 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -22,6 +22,7 @@ #include #include +#include "../pci-host-common.h" #include "../../pci.h" #include "pcie-designware.h" @@ -799,13 +800,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) return -ETIMEDOUT; } - /* - * As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link - * speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms - * after Link training completes before sending a Configuration Request. - */ - if (pci->max_link_speed > 2) - msleep(PCIE_RESET_CONFIG_WAIT_MS); + pci_host_common_link_train_delay(pci->max_link_speed); offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); From 6bba1de54cebcded567563311710f9b3111e2652 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:44 +0800 Subject: [PATCH 4560/5214] PCI: aardvark: Add 100 ms delay after link training The Aardvark PCIe controller driver waits for the link to come up but does not implement the mandatory 100 ms delay after link training completes for speeds greater than 5.0 GT/s (PCIe r6.0 sec 6.6.1). The driver already maintains a 'link_gen' field that holds the negotiated link speed. Use it together with pci_host_common_link_train_delay() to insert the required delay immediately after confirming that the link is up. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260518004246.1384532-6-18255117159@163.com --- drivers/pci/controller/pci-aardvark.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index e34bea1ff0ac..fd9c7d53e8a7 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -26,6 +26,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" #include "../pci-bridge-emul.h" @@ -350,8 +351,10 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie) /* check if the link is up or not */ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (advk_pcie_link_up(pcie)) + if (advk_pcie_link_up(pcie)) { + pci_host_common_link_train_delay(pcie->link_gen); return 0; + } usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } From d24e3fab6ee23c2d7076b0e5ffe5c7210cc9dae3 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:45 +0800 Subject: [PATCH 4561/5214] PCI: mediatek-gen3: Add 100 ms delay after link up The MediaTek Gen3 PCIe host driver lacks the required 100 ms delay after link training completes for speeds > 5.0 GT/s, as specified in PCIe r6.0 sec 6.6.1. The driver already stores max_link_speed (from the device tree). After mtk_pcie_startup_port() successfully brings up the link, call pci_host_common_link_train_delay() to comply with the specification. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260518004246.1384532-7-18255117159@163.com --- drivers/pci/controller/pcie-mediatek-gen3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b0accd828589..5abddec4e9be 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -30,6 +30,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" #define PCIE_BASE_CFG_REG 0x14 @@ -570,6 +571,8 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) goto err_power_down_device; } + pci_host_common_link_train_delay(pcie->max_link_speed); + return 0; err_power_down_device: From 2b0d1a605ef22c063be9de475d7a66c311b710f4 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Mon, 18 May 2026 08:42:46 +0800 Subject: [PATCH 4562/5214] PCI: rzg3s-host: Use common pci_host_common_link_train_delay() helper Replace the unconditional msleep(100) with the common helper pci_host_common_link_train_delay(). The helper only waits when max_link_speed > 2, as required by PCIe r6.0 sec 6.6.1. This avoids unnecessary delay for Gen1/Gen2 links while retaining the mandatory 100 ms for higher speeds. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260518004246.1384532-8-18255117159@163.com --- drivers/pci/controller/pcie-rzg3s-host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index d86e7516dcc2..66f687304c1c 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -35,6 +35,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" /* AXI registers */ @@ -1663,7 +1664,7 @@ rzg3s_pcie_host_setup(struct rzg3s_pcie_host *host, if (ret) dev_info(dev, "Failed to set max link speed\n"); - msleep(PCIE_RESET_CONFIG_WAIT_MS); + pci_host_common_link_train_delay(host->max_link_speed); return 0; From 8857f6578b001bcf5f53c8c6a3936647f05291a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Thu, 11 Jun 2026 15:05:43 +0000 Subject: [PATCH 4563/5214] PCI/proc: Fix race between pci_proc_init() and pci_bus_add_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_proc_attach_device() creates procfs entries for PCI devices and is called from pci_bus_add_device(). It lazily creates the per-bus procfs directory (bus->procdir) via proc_mkdir() on first use, and returns early if proc_initialized is not yet set. On x86 with ACPI, PCI enumeration occurs at subsys_initcall, before pci_proc_init() sets proc_initialized at device_initcall. The for_each_pci_dev() loop in pci_proc_init() then creates procfs entries for these already-enumerated devices, but runs without holding pci_rescan_remove_lock. On ARM64 with devicetree, PCI host bridges probe at device_initcall. With async probing enabled, pci_bus_add_device() can run concurrently with pci_proc_init(), and both may call pci_proc_attach_device() for the same device or for different devices on the same bus. As pci_host_probe() holds pci_rescan_remove_lock while pci_proc_init() does not, there is no serialisation between the two paths. When two threads concurrently call pci_proc_attach_device() for devices on the same bus, both observe bus->procdir as NULL and both call proc_mkdir(). The proc filesystem serialises directory creation internally, so only one caller succeeds. The other results in a warning like: proc_dir_entry '000c:00/00.0' already registered The caller receives NULL (duplicate entry) and unconditionally stores it to bus->procdir, corrupting the valid pointer set by the first caller. Serialise access to proc_initialized, proc_bus_pci_dir, bus->procdir and dev->procent with a new mutex local to drivers/pci/proc.c, and store the created entries to bus->procdir and dev->procent only on success, so a failed creation can never overwrite a valid pointer. Additionally, wrap the for_each_pci_dev() loop in pci_proc_init() with pci_lock_rescan_remove() to serialise against concurrent PCI bus operations, add an early return in pci_proc_attach_device() when dev->procent is already set to make the function idempotent, and clear bus->procdir in pci_proc_detach_bus() to prevent use of a dangling pointer after proc_remove(). Reported-by: Shuan He Closes: https://lore.kernel.org/linux-pci/20250702155112.40124-2-heshuan@bytedance.com/ Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20260611150543.511422-1-kwilczynski@kernel.org --- drivers/pci/proc.c | 79 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index ce36e35681e8..71ad289fcb8e 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -18,6 +18,7 @@ #include "pci.h" static int proc_initialized; /* = 0 */ +static DEFINE_MUTEX(pci_proc_lock); static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) { @@ -416,40 +417,64 @@ static const struct seq_operations proc_bus_pci_devices_op = { static struct proc_dir_entry *proc_bus_pci_dir; -int pci_proc_attach_device(struct pci_dev *dev) +static int __pci_proc_attach_bus(struct pci_bus *bus) { - struct pci_bus *bus = dev->bus; - struct proc_dir_entry *e; + struct proc_dir_entry *dir; char name[16]; + lockdep_assert_held(&pci_proc_lock); + if (!proc_initialized) return -EACCES; - if (!bus->procdir) { - if (pci_proc_domain(bus)) { - sprintf(name, "%04x:%02x", pci_domain_nr(bus), - bus->number); - } else { - sprintf(name, "%02x", bus->number); - } - bus->procdir = proc_mkdir(name, proc_bus_pci_dir); - if (!bus->procdir) - return -ENOMEM; - } + if (bus->procdir) + return 0; + + if (pci_proc_domain(bus)) + sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number); + else + sprintf(name, "%02x", bus->number); + + dir = proc_mkdir(name, proc_bus_pci_dir); + if (!dir) + return -ENOMEM; + + bus->procdir = dir; + + return 0; +} + +int pci_proc_attach_device(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct proc_dir_entry *entry; + char name[16]; + int ret; + + guard(mutex)(&pci_proc_lock); + + if (dev->procent) + return 0; + + ret = __pci_proc_attach_bus(bus); + if (ret) + return ret; sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir, - &proc_bus_pci_ops, dev); - if (!e) + entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, + bus->procdir, &proc_bus_pci_ops, dev); + if (!entry) return -ENOMEM; - proc_set_size(e, dev->cfg_size); - dev->procent = e; + + proc_set_size(entry, dev->cfg_size); + dev->procent = entry; return 0; } int pci_proc_detach_device(struct pci_dev *dev) { + guard(mutex)(&pci_proc_lock); proc_remove(dev->procent); dev->procent = NULL; return 0; @@ -457,19 +482,27 @@ int pci_proc_detach_device(struct pci_dev *dev) int pci_proc_detach_bus(struct pci_bus *bus) { + guard(mutex)(&pci_proc_lock); proc_remove(bus->procdir); + bus->procdir = NULL; return 0; } static int __init pci_proc_init(void) { struct pci_dev *dev = NULL; - proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); - proc_create_seq("devices", 0, proc_bus_pci_dir, - &proc_bus_pci_devices_op); - proc_initialized = 1; + + scoped_guard(mutex, &pci_proc_lock) { + proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); + proc_create_seq("devices", 0, proc_bus_pci_dir, + &proc_bus_pci_devices_op); + proc_initialized = 1; + } + + pci_lock_rescan_remove(); for_each_pci_dev(dev) pci_proc_attach_device(dev); + pci_unlock_rescan_remove(); return 0; } From ca617c60af9f6bb74216645ce4362dcb96697d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:26 +0000 Subject: [PATCH 4564/5214] PCI/sysfs: Convert PCI resource files to static attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the PCI resource files (resourceN, resourceN_wc) are dynamically created by pci_create_sysfs_dev_files(), called from both pci_bus_add_device() and the pci_sysfs_init() late_initcall, with only a sysfs_initialized flag for synchronisation. This has caused warnings and boot panics when both paths race on the same device, e.g.: sysfs: cannot create duplicate filename '/devices/pci0000:3c/0000:3c:01.0/0000:3e:00.2/resource2' This is especially likely on Devicetree-based platforms, where the PCI host controllers are platform drivers that probe via the driver model, which can happen during or after the late_initcall. As such, pci_bus_add_device() and pci_sysfs_init() are more likely to overlap. Convert to static const attributes with three attribute groups (I/O, UC, WC), each with an .is_bin_visible() callback that checks resource flags, BAR length, and non_mappable_bars. A .bin_size() callback provides pci_resource_len() to the kernfs node for correct stat and lseek behaviour. As part of this conversion: - Rename pci_read_resource_io() and pci_write_resource_io() to pci_read_resource() and pci_write_resource() since the callbacks are no longer I/O-specific in the static attribute context. - Update __resource_resize_store() to use sysfs_create_groups() and sysfs_remove_groups(), which re-evaluates visibility and runs the .bin_size() callback for the static resource attribute groups. - Remove pci_create_resource_files(), pci_remove_resource_files(), and pci_create_attr() which are no longer needed. - Move the __weak stubs outside the #if guard so they remain available for callers converted in subsequent commits. Platforms that do not define the HAVE_PCI_MMAP macro or the ARCH_GENERIC_PCI_MMAP_RESOURCE macro, such as Alpha architecture, continue using their platform-specific resource file creation. For reference, the dynamic creation dates back to the pre-Git era: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/drivers/pci/pci-sysfs.c?id=42298be0eeb5ae98453b3374c36161b05a46c5dc The write-combine support was added in commit 45aec1ae72fc ("x86: PAT export resource_wc in pci sysfs"). Many other reports mentioned in the cover letter (first Link: below). Link: https://lore.kernel.org/r/20260508043543.217179-1-kwilczynski@kernel.org/ Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215515 Closes: https://github.com/openwrt/openwrt/issues/17143 Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-8-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 285 +++++++++++++++++++++------------------- include/linux/pci.h | 2 - 2 files changed, 152 insertions(+), 135 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a74eca96918b..3bb808e0e80a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -890,19 +890,6 @@ pci_llseek_resource_legacy(struct file *filep, return fixed_size_llseek(filep, offset, whence, attr->size); } -static __maybe_unused loff_t -pci_llseek_resource(struct file *filep, - struct kobject *kobj, - const struct bin_attribute *attr, - loff_t offset, int whence) -{ - struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - int bar = (unsigned long)attr->private; - - return fixed_size_llseek(filep, offset, whence, - pci_resource_len(pdev, bar)); -} - #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -1177,14 +1164,14 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, #endif } -static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj, +static ssize_t pci_read_resource(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { return pci_resource_io(filp, kobj, attr, buf, off, count, false); } -static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, +static ssize_t pci_write_resource(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { @@ -1197,6 +1184,18 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, return pci_resource_io(filp, kobj, attr, buf, off, count, true); } +static loff_t pci_llseek_resource(struct file *filep, + struct kobject *kobj, + const struct bin_attribute *attr, + loff_t offset, int whence) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + int bar = (unsigned long)attr->private; + + return fixed_size_llseek(filep, offset, whence, + pci_resource_len(pdev, bar)); +} + /* * generic_file_llseek() consults f_mapping->host to determine * the file size. As iomem_inode knows nothing about the @@ -1215,8 +1214,8 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, static const struct bin_attribute dev_resource##_bar##_io_attr = { \ .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ .private = (void *)(unsigned long)(_bar), \ - .read = pci_read_resource_io, \ - .write = pci_write_resource_io, \ + .read = pci_read_resource, \ + .write = pci_write_resource, \ __PCI_RESOURCE_IO_MMAP_ATTRS \ } @@ -1238,129 +1237,144 @@ static const struct bin_attribute dev_resource##_bar##_wc_attr = { \ .mmap = pci_mmap_resource_wc, \ } -/** - * pci_remove_resource_files - cleanup resource files - * @pdev: dev to cleanup - * - * If we created resource files for @pdev, remove them from sysfs and - * free their resources. - */ -static void pci_remove_resource_files(struct pci_dev *pdev) +static inline umode_t +__pci_resource_attr_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar, bool write_combine, + unsigned long flags) { - int i; + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - struct bin_attribute *res_attr; - - res_attr = pdev->res_attr[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - - res_attr = pdev->res_attr_wc[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - } -} - -static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) -{ - /* allocate attribute structure, piggyback attribute name */ - int name_len = write_combine ? 13 : 10; - struct bin_attribute *res_attr; - char *res_attr_name; - int retval; - - res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); - if (!res_attr) - return -ENOMEM; - - res_attr_name = (char *)(res_attr + 1); - - sysfs_bin_attr_init(res_attr); - if (write_combine) { - sprintf(res_attr_name, "resource%d_wc", num); - res_attr->mmap = pci_mmap_resource_wc; - } else { - sprintf(res_attr_name, "resource%d", num); - if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { - res_attr->read = pci_read_resource_io; - res_attr->write = pci_write_resource_io; - if (arch_can_pci_mmap_io()) - res_attr->mmap = pci_mmap_resource_uc; - } else { - res_attr->mmap = pci_mmap_resource_uc; - } - } - if (res_attr->mmap) { - res_attr->f_mapping = iomem_get_mapping; - /* - * generic_file_llseek() consults f_mapping->host to determine - * the file size. As iomem_inode knows nothing about the - * attribute, it's not going to work, so override it as well. - */ - res_attr->llseek = pci_llseek_resource; - } - res_attr->attr.name = res_attr_name; - res_attr->attr.mode = 0600; - res_attr->size = pci_resource_len(pdev, num); - res_attr->private = (void *)(unsigned long)num; - retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); - if (retval) { - kfree(res_attr); - return retval; - } - - if (write_combine) - pdev->res_attr_wc[num] = res_attr; - else - pdev->res_attr[num] = res_attr; - - return 0; -} - -/** - * pci_create_resource_files - create resource files in sysfs for @dev - * @pdev: dev in question - * - * Walk the resources in @pdev creating files for each resource available. - */ -static int pci_create_resource_files(struct pci_dev *pdev) -{ - int i; - int retval; - - /* Skip devices with non-mappable BARs */ if (pdev->non_mappable_bars) return 0; - /* Expose the PCI resources from this device as files */ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { + if (!pci_resource_len(pdev, bar)) + return 0; - /* skip empty resources */ - if (!pci_resource_len(pdev, i)) - continue; + if ((pci_resource_flags(pdev, bar) & flags) != flags) + return 0; - retval = pci_create_attr(pdev, i, 0); - /* for prefetchable resources, create a WC mappable file */ - if (!retval && arch_can_pci_mmap_wc() && - pci_resource_flags(pdev, i) & IORESOURCE_PREFETCH) - retval = pci_create_attr(pdev, i, 1); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } - return 0; + if (write_combine && !arch_can_pci_mmap_wc()) + return 0; + + return a->attr.mode; } -#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */ -int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } -void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } + +static umode_t pci_dev_resource_io_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, false, + IORESOURCE_IO); +} + +static umode_t pci_dev_resource_uc_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, false, + IORESOURCE_MEM); +} + +static umode_t pci_dev_resource_wc_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, true, + IORESOURCE_MEM | IORESOURCE_PREFETCH); +} + +static size_t pci_dev_resource_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + return pci_resource_len(pdev, n); +} + +pci_dev_resource_io_attr(0); +pci_dev_resource_io_attr(1); +pci_dev_resource_io_attr(2); +pci_dev_resource_io_attr(3); +pci_dev_resource_io_attr(4); +pci_dev_resource_io_attr(5); + +pci_dev_resource_uc_attr(0); +pci_dev_resource_uc_attr(1); +pci_dev_resource_uc_attr(2); +pci_dev_resource_uc_attr(3); +pci_dev_resource_uc_attr(4); +pci_dev_resource_uc_attr(5); + +pci_dev_resource_wc_attr(0); +pci_dev_resource_wc_attr(1); +pci_dev_resource_wc_attr(2); +pci_dev_resource_wc_attr(3); +pci_dev_resource_wc_attr(4); +pci_dev_resource_wc_attr(5); + +static const struct bin_attribute *const pci_dev_resource_io_attrs[] = { + &dev_resource0_io_attr, + &dev_resource1_io_attr, + &dev_resource2_io_attr, + &dev_resource3_io_attr, + &dev_resource4_io_attr, + &dev_resource5_io_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = { + &dev_resource0_uc_attr, + &dev_resource1_uc_attr, + &dev_resource2_uc_attr, + &dev_resource3_uc_attr, + &dev_resource4_uc_attr, + &dev_resource5_uc_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = { + &dev_resource0_wc_attr, + &dev_resource1_wc_attr, + &dev_resource2_wc_attr, + &dev_resource3_wc_attr, + &dev_resource4_wc_attr, + &dev_resource5_wc_attr, + NULL, +}; + +static const struct attribute_group pci_dev_resource_io_attr_group = { + .bin_attrs = pci_dev_resource_io_attrs, + .is_bin_visible = pci_dev_resource_io_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group pci_dev_resource_uc_attr_group = { + .bin_attrs = pci_dev_resource_uc_attrs, + .is_bin_visible = pci_dev_resource_uc_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group pci_dev_resource_wc_attr_group = { + .bin_attrs = pci_dev_resource_wc_attrs, + .is_bin_visible = pci_dev_resource_wc_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group *pci_dev_resource_attr_groups[] = { + &pci_dev_resource_io_attr_group, + &pci_dev_resource_uc_attr_group, + &pci_dev_resource_wc_attr_group, + NULL, +}; +#else +#define pci_dev_resource_attr_groups NULL #endif +int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } +void __weak pci_remove_resource_files(struct pci_dev *dev) { } + /** * pci_write_rom - used to enable access to the PCI ROM display * @filp: sysfs file @@ -1662,14 +1676,14 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_write_config_word(pdev, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); - pci_remove_resource_files(pdev); + sysfs_remove_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups); ret = pci_resize_resource(pdev, n, size, 0); pci_assign_unassigned_bus_resources(bus); - if (pci_create_resource_files(pdev)) - pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n"); + if (sysfs_create_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups)) + pci_warn(pdev, "Failed to recreate resource groups after BAR resizing\n"); pci_write_config_word(pdev, PCI_COMMAND, cmd); pm_put: @@ -1838,6 +1852,11 @@ static const struct attribute_group pci_dev_group = { const struct attribute_group *pci_dev_groups[] = { &pci_dev_group, +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) + &pci_dev_resource_io_attr_group, + &pci_dev_resource_uc_attr_group, + &pci_dev_resource_wc_attr_group, +#endif &pci_dev_config_attr_group, &pci_dev_rom_attr_group, &pci_dev_reset_attr_group, diff --git a/include/linux/pci.h b/include/linux/pci.h index e93fbe6b57fe..974605c9fce2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2531,10 +2531,8 @@ int pcibios_alloc_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev); resource_size_t pcibios_default_alignment(void); -#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) extern int pci_create_resource_files(struct pci_dev *dev); extern void pci_remove_resource_files(struct pci_dev *dev); -#endif #if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) void __init pci_mmcfg_early_init(void); From cf616e0b188beac528f46d656bffd065f4f19529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:27 +0000 Subject: [PATCH 4565/5214] PCI/sysfs: Warn about BAR resize failure in __resource_resize_store() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a pci_warn() to __resource_resize_store(), so that BAR resize failures are visible to the user, which can help troubleshoot any potential resource resize issues. While at it, rename the resource_resize_is_visible() to resource_resize_attr_is_visible() along with the corresponding group variable to align with the naming convention used by the resource attribute groups. Also, change the order of pci_dev_groups[] such that the resize group is now located alongside the other resource groups. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-9-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 3bb808e0e80a..3db44df63b53 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1679,6 +1679,9 @@ static ssize_t __resource_resize_store(struct device *dev, int n, sysfs_remove_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups); ret = pci_resize_resource(pdev, n, size, 0); + if (ret) + pci_warn(pdev, "Failed to resize BAR %d: %pe\n", + n, ERR_PTR(ret)); pci_assign_unassigned_bus_resources(bus); @@ -1726,7 +1729,7 @@ static struct attribute *resource_resize_attrs[] = { NULL, }; -static umode_t resource_resize_is_visible(struct kobject *kobj, +static umode_t resource_resize_attr_is_visible(struct kobject *kobj, struct attribute *a, int n) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); @@ -1734,9 +1737,9 @@ static umode_t resource_resize_is_visible(struct kobject *kobj, return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode; } -static const struct attribute_group pci_dev_resource_resize_group = { +static const struct attribute_group pci_dev_resource_resize_attr_group = { .attrs = resource_resize_attrs, - .is_visible = resource_resize_is_visible, + .is_visible = resource_resize_attr_is_visible, }; int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) @@ -1857,6 +1860,7 @@ const struct attribute_group *pci_dev_groups[] = { &pci_dev_resource_uc_attr_group, &pci_dev_resource_wc_attr_group, #endif + &pci_dev_resource_resize_attr_group, &pci_dev_config_attr_group, &pci_dev_rom_attr_group, &pci_dev_reset_attr_group, @@ -1868,7 +1872,6 @@ const struct attribute_group *pci_dev_groups[] = { #ifdef CONFIG_ACPI &pci_dev_acpi_attr_group, #endif - &pci_dev_resource_resize_group, ARCH_PCI_DEV_GROUPS NULL, }; From 2c31a9675f50a61c9ceed6dab2545be7aa896d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:28 +0000 Subject: [PATCH 4566/5214] PCI/sysfs: Add stubs for pci_{create,remove}_sysfs_dev_files() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On platforms with HAVE_PCI_MMAP or ARCH_GENERIC_PCI_MMAP_RESOURCE, resource files are now handled by static attribute groups registered via pci_dev_groups[]. Stub out the pci_create_sysfs_dev_files() and pci_remove_sysfs_dev_files(), as the dynamic resource file creation is no longer needed. Also, simplify pci_sysfs_init() on these platforms to only iterate buses for legacy attributes creation, skipping the per-device loop. Move the __weak stubs for pci_create_resource_files() and pci_remove_resource_files() into the #else branch since only platforms without HAVE_PCI_MMAP (such as Alpha architecture) still need them. Guard the res_attr[] and res_attr_wc[] fields in struct pci_dev the same way. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-10-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 15 ++++++++++++--- include/linux/pci.h | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 3db44df63b53..102147500596 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1370,10 +1370,9 @@ static const struct attribute_group *pci_dev_resource_attr_groups[] = { }; #else #define pci_dev_resource_attr_groups NULL -#endif - int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } void __weak pci_remove_resource_files(struct pci_dev *dev) { } +#endif /** * pci_write_rom - used to enable access to the PCI ROM display @@ -1742,6 +1741,10 @@ static const struct attribute_group pci_dev_resource_resize_attr_group = { .is_visible = resource_resize_attr_is_visible, }; +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) +int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; } +void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { } +#else int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) { if (!sysfs_initialized) @@ -1763,9 +1766,15 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) pci_remove_resource_files(pdev); } +#endif static int __init pci_sysfs_init(void) { +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) + struct pci_bus *pbus = NULL; + + sysfs_initialized = 1; +#else struct pci_dev *pdev = NULL; struct pci_bus *pbus = NULL; int retval; @@ -1778,7 +1787,7 @@ static int __init pci_sysfs_init(void) return retval; } } - +#endif while ((pbus = pci_find_next_bus(pbus))) pci_create_legacy_files(pbus); diff --git a/include/linux/pci.h b/include/linux/pci.h index 974605c9fce2..b998a56f6010 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -515,8 +515,10 @@ struct pci_dev { spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */ u32 saved_config_space[16]; /* Config space saved at suspend time */ struct hlist_head saved_cap_space; +#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ +#endif #ifdef CONFIG_HOTPLUG_PCI_PCIE unsigned int broken_cmd_compl:1; /* No compl for some cmds */ @@ -2531,8 +2533,10 @@ int pcibios_alloc_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev); resource_size_t pcibios_default_alignment(void); +#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) extern int pci_create_resource_files(struct pci_dev *dev); extern void pci_remove_resource_files(struct pci_dev *dev); +#endif #if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) void __init pci_mmcfg_early_init(void); From 4eee7eef239c61d11a2fdd03691d60c5cd91940d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:29 +0000 Subject: [PATCH 4567/5214] PCI/sysfs: Limit pci_sysfs_init() late_initcall compile scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, pci_sysfs_init() and sysfs_initialized compile unconditionally, even on platforms where static attribute groups handle all resource file creation. Place them behind a new HAVE_PCI_SYSFS_INIT macro, especially as the late_initcall is only needed when: - HAVE_PCI_LEGACY is set, to iterate buses and create legacy I/O and memory files. - Neither HAVE_PCI_MMAP nor ARCH_GENERIC_PCI_MMAP_RESOURCE is set, to iterate devices and create resource files via the __weak pci_create_resource_files() stub override (this is how the Alpha architecture handles this currently). On most systems both conditions are false and the entire late_initcall compiles away. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-11-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 102147500596..0ba4fbf40cdc 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -37,7 +37,14 @@ #define ARCH_PCI_DEV_GROUPS #endif +#if defined(HAVE_PCI_LEGACY) || \ + !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) +#define HAVE_PCI_SYSFS_INIT +#endif + +#ifdef HAVE_PCI_SYSFS_INIT static int sysfs_initialized; /* = 0 */ +#endif /* show configuration fields */ #define pci_config_attr(field, format_string) \ @@ -1768,6 +1775,7 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) } #endif +#ifdef HAVE_PCI_SYSFS_INIT static int __init pci_sysfs_init(void) { #if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) @@ -1794,6 +1802,7 @@ static int __init pci_sysfs_init(void) return 0; } late_initcall(pci_sysfs_init); +#endif static struct attribute *pci_dev_dev_attrs[] = { &dev_attr_boot_vga.attr, From 78a228f0aa0e9eba31955950c8a40a9945e2c8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:30 +0000 Subject: [PATCH 4568/5214] alpha/PCI: Add security_locked_down() check to pci_mmap_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, Alpha's pci_mmap_resource() does not check security_locked_down(LOCKDOWN_PCI_ACCESS) before allowing userspace to mmap PCI BARs. The generic version has had this check since commit eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") to prevent DMA attacks when the kernel is locked down. Add the same check to Alpha's pci_mmap_resource(). Fixes: eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-12-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 3048758304b5..2324720c3e83 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -71,7 +72,11 @@ static int pci_mmap_resource(struct kobject *kobj, struct resource *res = attr->private; enum pci_mmap_state mmap_type; struct pci_bus_region bar; - int i; + int i, ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; for (i = 0; i < PCI_STD_NUM_BARS; i++) if (res == &pdev->resource[i]) From 7d9779cb10265f34f405eff1eb50bb955d3f6e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:31 +0000 Subject: [PATCH 4569/5214] alpha/PCI: Use BAR index in sysfs attr->private instead of resource pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, Alpha's pci_create_one_attr() stores a resource pointer in attr->private, and pci_mmap_resource() loops through all BARs to find the matching index. Store the BAR index directly in attr->private and retrieve the resource via pci_resource_n(). This eliminates the loop and aligns with the convention used by the generic PCI sysfs code. The PCI core change was first added in the commit dca40b186b75 ("PCI: Use BAR index in sysfs attr->private instead of resource pointer"). Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-13-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2324720c3e83..2330ab84d59c 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -69,25 +69,20 @@ static int pci_mmap_resource(struct kobject *kobj, struct vm_area_struct *vma, int sparse) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - struct resource *res = attr->private; + int barno = (unsigned long)attr->private; + struct resource *res = pci_resource_n(pdev, barno); enum pci_mmap_state mmap_type; struct pci_bus_region bar; - int i, ret; + int ret; ret = security_locked_down(LOCKDOWN_PCI_ACCESS); if (ret) return ret; - for (i = 0; i < PCI_STD_NUM_BARS; i++) - if (res == &pdev->resource[i]) - break; - if (i >= PCI_STD_NUM_BARS) - return -ENODEV; - if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) return -EINVAL; - if (!__pci_mmap_fits(pdev, i, vma, sparse)) + if (!__pci_mmap_fits(pdev, barno, vma, sparse)) return -EINVAL; pcibios_resource_to_bus(pdev->bus, &bar, res); @@ -170,7 +165,7 @@ static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name, res_attr->attr.name = name; res_attr->attr.mode = S_IRUSR | S_IWUSR; res_attr->size = sparse ? size << 5 : size; - res_attr->private = &pdev->resource[num]; + res_attr->private = (void *)(unsigned long)num; return sysfs_create_bin_file(&pdev->dev.kobj, res_attr); } From 30d01a8c3a13d217921985324ebdce5b404c1ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:32 +0000 Subject: [PATCH 4570/5214] alpha/PCI: Use PCI resource accessor macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct pdev->resource[] accesses with pci_resource_n(), and open-coded res->flags type checks with pci_resource_is_mem() and pci_resource_start() helpers. While at it, move the pci_resource_n() call directly into pcibios_resource_to_bus() and drop the local struct resource pointer. No functional changes intended. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-14-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2330ab84d59c..5c29f1d2821c 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -70,7 +70,6 @@ static int pci_mmap_resource(struct kobject *kobj, { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int barno = (unsigned long)attr->private; - struct resource *res = pci_resource_n(pdev, barno); enum pci_mmap_state mmap_type; struct pci_bus_region bar; int ret; @@ -79,15 +78,16 @@ static int pci_mmap_resource(struct kobject *kobj, if (ret) return ret; - if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) + if (pci_resource_is_mem(pdev, barno) && + iomem_is_exclusive(pci_resource_start(pdev, barno))) return -EINVAL; if (!__pci_mmap_fits(pdev, barno, vma, sparse)) return -EINVAL; - pcibios_resource_to_bus(pdev->bus, &bar, res); + pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, barno)); vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0)); - mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + mmap_type = pci_resource_is_mem(pdev, barno) ? pci_mmap_mem : pci_mmap_io; return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse); } @@ -141,7 +141,7 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) long dense_offset; unsigned long sparse_size; - pcibios_resource_to_bus(pdev->bus, &bar, &pdev->resource[num]); + pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, num)); /* All core logic chips have 4G sparse address space, except CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM @@ -181,7 +181,7 @@ static int pci_create_attr(struct pci_dev *pdev, int num) suffix = ""; /* Assume bwx machine, normal resourceN files. */ nlen1 = 10; - if (pdev->resource[num].flags & IORESOURCE_MEM) { + if (pci_resource_is_mem(pdev, num)) { sparse_base = hose->sparse_mem_base; dense_base = hose->dense_mem_base; if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) { From 802a3b3f470b9bc1148f26e01fc9cbfeb4dfcb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:33 +0000 Subject: [PATCH 4571/5214] alpha/PCI: Fix __pci_mmap_fits() overflow for zero-length BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, __pci_mmap_fits() computes the BAR size using "pci_resource_len() - 1", which wraps to a large value when the BAR length is zero, causing the bounds check to incorrectly succeed. Add an early return for empty resources. Fixes: 10a0ef39fbd1 ("PCI/alpha: pci sysfs resources") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-15-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 5c29f1d2821c..8802f955256e 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -37,12 +37,16 @@ static int hose_mmap_page_range(struct pci_controller *hose, static int __pci_mmap_fits(struct pci_dev *pdev, int num, struct vm_area_struct *vma, int sparse) { + resource_size_t len = pci_resource_len(pdev, num); unsigned long nr, start, size; int shift = sparse ? 5 : 0; + if (!len) + return 0; + nr = vma_pages(vma); start = vma->vm_pgoff; - size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; + size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1; if (start < size && size - start >= nr) return 1; From 29840080f5a68de131ae5bf89138d6ae11c591d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:34 +0000 Subject: [PATCH 4572/5214] alpha/PCI: Remove WARN from __pci_mmap_fits() and __legacy_mmap_fits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the WARN() that fires when userspace attempts to mmap beyond the BAR bounds. The check still returns 0 to reject the mapping, but the warning is excessive for normal operation. A similar warning was removed from the PCI core in the commit 3b519e4ea618 ("PCI: fix size checks for mmap() on /proc/bus/pci files"). Signed-off-by: Krzysztof Wilczyński [bhelgaas: squash https://lore.kernel.org/all/20260508045824.GA3160093@rocinante] Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-16-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 8802f955256e..6cf688621ea9 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -48,13 +48,7 @@ static int __pci_mmap_fits(struct pci_dev *pdev, int num, start = vma->vm_pgoff; size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1; - if (start < size && size - start >= nr) - return 1; - WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d " - "(size 0x%08lx)\n", - current->comm, sparse ? " sparse" : "", start, start + nr, - pci_name(pdev), num, size); - return 0; + return start < size && size - start >= nr; } /** @@ -257,9 +251,8 @@ int pci_create_resource_files(struct pci_dev *pdev) /* Legacy I/O bus mapping stuff. */ -static int __legacy_mmap_fits(struct pci_controller *hose, - struct vm_area_struct *vma, - unsigned long res_size, int sparse) +static int __legacy_mmap_fits(struct vm_area_struct *vma, + unsigned long res_size) { unsigned long nr, start, size; @@ -267,13 +260,7 @@ static int __legacy_mmap_fits(struct pci_controller *hose, start = vma->vm_pgoff; size = ((res_size - 1) >> PAGE_SHIFT) + 1; - if (start < size && size - start >= nr) - return 1; - WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d " - "(size 0x%08lx)\n", - current->comm, sparse ? " sparse" : "", start, start + nr, - hose->index, size); - return 0; + return start < size && size - start >= nr; } static inline int has_sparse(struct pci_controller *hose, @@ -296,7 +283,7 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size : bus->legacy_io->size; - if (!__legacy_mmap_fits(hose, vma, res_size, sparse)) + if (!__legacy_mmap_fits(vma, res_size)) return -EINVAL; return hose_mmap_page_range(hose, vma, mmap_type, sparse); From 5980dcbc4b25e0164cfebc90c32bca7cff33d1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:35 +0000 Subject: [PATCH 4573/5214] alpha/PCI: Add static PCI resource attribute macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add macros for declaring static binary attributes for Alpha's PCI resource files: - pci_dev_resource_attr(), for dense/BWX systems (mmap dense) - pci_dev_resource_sparse_attr(), for sparse systems (mmap sparse) - pci_dev_resource_dense_attr(), for dense companion files (mmap dense) Each macro creates a const bin_attribute with the BAR index stored in the .private property and the appropriate .mmap() callback. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-17-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 6cf688621ea9..e1a4546ac2db 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -104,6 +104,26 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj, return pci_mmap_resource(kobj, attr, vma, 0); } +#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \ +static const struct bin_attribute \ +pci_dev_resource##_bar##_suffix##_attr = { \ + .attr = { .name = __stringify(_name), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .mmap = (_mmap), \ +} + +#define pci_dev_resource_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar,, \ + pci_mmap_resource_dense) + +#define pci_dev_resource_sparse_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \ + pci_mmap_resource_sparse) + +#define pci_dev_resource_dense_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \ + pci_mmap_resource_dense) + /** * pci_remove_resource_files - cleanup resource files * @pdev: pci_dev to cleanup From b7a57ffd4d9f4db3e95001db427c63053b78cf1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:36 +0000 Subject: [PATCH 4574/5214] alpha/PCI: Convert resource files to static attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, Alpha's PCI resource files (resourceN, resourceN_sparse, resourceN_dense) were dynamically created by pci_create_resource_files(), which overrides the generic __weak implementation. The previous code allocated bin_attributes at runtime and managed them via the res_attr[] and res_attr_wc[] fields in struct pci_dev. Convert to static const attributes with three attribute groups (plain, sparse, dense), each with an .is_bin_visible() callback that checks resource length, has_sparse(), and sparse_mem_mmap_fits(). A .bin_size() callback provides the resource size to the kernfs node, with the sparse variant shifting by 5 bits for byte-level addressing. Register the groups via ARCH_PCI_DEV_GROUPS so the driver model handles creation and removal automatically. Use the new pci_resource_is_mem() helper for the type check, replacing the open-coded bitwise flag test. Finally, remove pci_create_resource_files(), pci_remove_resource_files(), pci_create_attr(), and pci_create_one_attr() which are no longer needed. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-18-kwilczynski@kernel.org --- arch/alpha/include/asm/pci.h | 9 ++ arch/alpha/kernel/pci-sysfs.c | 291 +++++++++++++++++++--------------- 2 files changed, 172 insertions(+), 128 deletions(-) diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 6c04fcbdc8ed..ef19295f2e33 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -88,4 +88,13 @@ extern void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type); #define HAVE_PCI_LEGACY 1 +extern const struct attribute_group pci_dev_resource_attr_group; +extern const struct attribute_group pci_dev_resource_sparse_attr_group; +extern const struct attribute_group pci_dev_resource_dense_attr_group; + +#define ARCH_PCI_DEV_GROUPS \ + &pci_dev_resource_attr_group, \ + &pci_dev_resource_sparse_attr_group, \ + &pci_dev_resource_dense_attr_group, + #endif /* __ALPHA_PCI_H */ diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index e1a4546ac2db..435654ffa4a0 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -12,8 +12,6 @@ #include #include -#include -#include #include static int hose_mmap_page_range(struct pci_controller *hose, @@ -124,34 +122,6 @@ pci_dev_resource##_bar##_suffix##_attr = { \ __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \ pci_mmap_resource_dense) -/** - * pci_remove_resource_files - cleanup resource files - * @pdev: pci_dev to cleanup - * - * If we created resource files for @dev, remove them from sysfs and - * free their resources. - */ -void pci_remove_resource_files(struct pci_dev *pdev) -{ - int i; - - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - struct bin_attribute *res_attr; - - res_attr = pdev->res_attr[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - - res_attr = pdev->res_attr_wc[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - } -} - static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) { struct pci_bus_region bar; @@ -171,104 +141,6 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) return bar.end < sparse_size; } -static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name, - char *suffix, struct bin_attribute *res_attr, - unsigned long sparse) -{ - size_t size = pci_resource_len(pdev, num); - - sprintf(name, "resource%d%s", num, suffix); - res_attr->mmap = sparse ? pci_mmap_resource_sparse : - pci_mmap_resource_dense; - res_attr->attr.name = name; - res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->size = sparse ? size << 5 : size; - res_attr->private = (void *)(unsigned long)num; - return sysfs_create_bin_file(&pdev->dev.kobj, res_attr); -} - -static int pci_create_attr(struct pci_dev *pdev, int num) -{ - /* allocate attribute structure, piggyback attribute name */ - int retval, nlen1, nlen2 = 0, res_count = 1; - unsigned long sparse_base, dense_base; - struct bin_attribute *attr; - struct pci_controller *hose = pdev->sysdata; - char *suffix, *attr_name; - - suffix = ""; /* Assume bwx machine, normal resourceN files. */ - nlen1 = 10; - - if (pci_resource_is_mem(pdev, num)) { - sparse_base = hose->sparse_mem_base; - dense_base = hose->dense_mem_base; - if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) { - sparse_base = 0; - suffix = "_dense"; - nlen1 = 16; /* resourceN_dense */ - } - } else { - sparse_base = hose->sparse_io_base; - dense_base = hose->dense_io_base; - } - - if (sparse_base) { - suffix = "_sparse"; - nlen1 = 17; - if (dense_base) { - nlen2 = 16; /* resourceN_dense */ - res_count = 2; - } - } - - attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC); - if (!attr) - return -ENOMEM; - - /* Create bwx, sparse or single dense file */ - attr_name = (char *)(attr + res_count); - pdev->res_attr[num] = attr; - retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr, - sparse_base); - if (retval || res_count == 1) - return retval; - - /* Create dense file */ - attr_name += nlen1; - attr++; - pdev->res_attr_wc[num] = attr; - return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0); -} - -/** - * pci_create_resource_files - create resource files in sysfs for @pdev - * @pdev: pci_dev in question - * - * Walk the resources in @dev creating files for each resource available. - * - * Return: %0 on success, or negative error code - */ -int pci_create_resource_files(struct pci_dev *pdev) -{ - int i; - int retval; - - /* Expose the PCI resources from this device as files */ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - - /* skip empty resources */ - if (!pci_resource_len(pdev, i)) - continue; - - retval = pci_create_attr(pdev, i); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } - return 0; -} - /* Legacy I/O bus mapping stuff. */ static int __legacy_mmap_fits(struct vm_area_struct *vma, @@ -381,3 +253,166 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) } return -EINVAL; } + +pci_dev_resource_attr(0); +pci_dev_resource_attr(1); +pci_dev_resource_attr(2); +pci_dev_resource_attr(3); +pci_dev_resource_attr(4); +pci_dev_resource_attr(5); + +pci_dev_resource_sparse_attr(0); +pci_dev_resource_sparse_attr(1); +pci_dev_resource_sparse_attr(2); +pci_dev_resource_sparse_attr(3); +pci_dev_resource_sparse_attr(4); +pci_dev_resource_sparse_attr(5); + +pci_dev_resource_dense_attr(0); +pci_dev_resource_dense_attr(1); +pci_dev_resource_dense_attr(2); +pci_dev_resource_dense_attr(3); +pci_dev_resource_dense_attr(4); +pci_dev_resource_dense_attr(5); + +static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev, + int bar) +{ + return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io; +} + +static inline umode_t __pci_dev_resource_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + if (!pci_resource_len(pdev, bar)) + return 0; + + return a->attr.mode; +} + +static umode_t pci_dev_resource_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + + if (has_sparse(hose, pci_bar_mmap_type(pdev, bar))) + return 0; + + return __pci_dev_resource_is_visible(kobj, a, bar); +} + +static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar); + + if (!has_sparse(hose, type)) + return 0; + + if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) + return 0; + + return __pci_dev_resource_is_visible(kobj, a, bar); +} + +static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar); + unsigned long dense_base; + + if (!has_sparse(hose, type)) + return 0; + + if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) + return __pci_dev_resource_is_visible(kobj, a, bar); + + dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base : + hose->dense_io_base; + if (!dense_base) + return 0; + + return __pci_dev_resource_is_visible(kobj, a, bar); +} + +static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj, + int bar, bool sparse) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + size_t size = pci_resource_len(pdev, bar); + + return sparse ? size << 5 : size; +} + +static size_t pci_dev_resource_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + return __pci_dev_resource_bin_size(kobj, bar, false); +} + +static size_t pci_dev_resource_sparse_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + return __pci_dev_resource_bin_size(kobj, bar, true); +} + +static const struct bin_attribute *const pci_dev_resource_attrs[] = { + &pci_dev_resource0_attr, + &pci_dev_resource1_attr, + &pci_dev_resource2_attr, + &pci_dev_resource3_attr, + &pci_dev_resource4_attr, + &pci_dev_resource5_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_sparse_attrs[] = { + &pci_dev_resource0_sparse_attr, + &pci_dev_resource1_sparse_attr, + &pci_dev_resource2_sparse_attr, + &pci_dev_resource3_sparse_attr, + &pci_dev_resource4_sparse_attr, + &pci_dev_resource5_sparse_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_dense_attrs[] = { + &pci_dev_resource0_dense_attr, + &pci_dev_resource1_dense_attr, + &pci_dev_resource2_dense_attr, + &pci_dev_resource3_dense_attr, + &pci_dev_resource4_dense_attr, + &pci_dev_resource5_dense_attr, + NULL, +}; + +const struct attribute_group pci_dev_resource_attr_group = { + .bin_attrs = pci_dev_resource_attrs, + .is_bin_visible = pci_dev_resource_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +const struct attribute_group pci_dev_resource_sparse_attr_group = { + .bin_attrs = pci_dev_resource_sparse_attrs, + .is_bin_visible = pci_dev_resource_sparse_is_visible, + .bin_size = pci_dev_resource_sparse_bin_size, +}; + +const struct attribute_group pci_dev_resource_dense_attr_group = { + .bin_attrs = pci_dev_resource_dense_attrs, + .is_bin_visible = pci_dev_resource_dense_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; From b45bebbfa0be8b1c8be5d3c5946f8f04d556fdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:37 +0000 Subject: [PATCH 4575/5214] PCI/sysfs: Remove pci_{create,remove}_sysfs_dev_files() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, pci_create_sysfs_dev_files() and pci_remove_sysfs_dev_files() are no-op stubs. With both the generic and Alpha resource files now handled by static attribute groups, no platform needs dynamic per-device sysfs file creation. Remove both functions, their declarations, and the call sites in pci_bus_add_device() and pci_stop_dev(). Remove __weak pci_create_resource_files() and pci_remove_resource_files() stubs and their declarations in pci.h, as no architecture overrides them anymore. Remove the res_attr[] and res_attr_wc[] fields from struct pci_dev which were used to track dynamically allocated resource attributes. Finally, simplify pci_sysfs_init() to only handle legacy file creation under HAVE_PCI_LEGACY, removing the per-device loop and the HAVE_PCI_SYSFS_INIT helper added earlier. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-19-kwilczynski@kernel.org --- drivers/pci/bus.c | 1 - drivers/pci/pci-sysfs.c | 52 ++--------------------------------------- drivers/pci/pci.h | 4 ---- drivers/pci/remove.c | 1 - include/linux/pci.h | 9 ------- 5 files changed, 2 insertions(+), 65 deletions(-) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 6c1ad1f542d9..655ed53436d3 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -354,7 +354,6 @@ void pci_bus_add_device(struct pci_dev *dev) pci_fixup_device(pci_fixup_final, dev); if (pci_is_bridge(dev)) of_pci_make_dev_node(dev); - pci_create_sysfs_dev_files(dev); pci_proc_attach_device(dev); pci_bridge_d3_update(dev); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 0ba4fbf40cdc..97a482d6d67d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -37,12 +37,7 @@ #define ARCH_PCI_DEV_GROUPS #endif -#if defined(HAVE_PCI_LEGACY) || \ - !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) -#define HAVE_PCI_SYSFS_INIT -#endif - -#ifdef HAVE_PCI_SYSFS_INIT +#ifdef HAVE_PCI_LEGACY static int sysfs_initialized; /* = 0 */ #endif @@ -1377,8 +1372,6 @@ static const struct attribute_group *pci_dev_resource_attr_groups[] = { }; #else #define pci_dev_resource_attr_groups NULL -int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } -void __weak pci_remove_resource_files(struct pci_dev *dev) { } #endif /** @@ -1748,54 +1741,13 @@ static const struct attribute_group pci_dev_resource_resize_attr_group = { .is_visible = resource_resize_attr_is_visible, }; -#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) -int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; } -void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { } -#else -int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) -{ - if (!sysfs_initialized) - return -EACCES; - - return pci_create_resource_files(pdev); -} - -/** - * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files - * @pdev: device whose entries we should free - * - * Cleanup when @pdev is removed from sysfs. - */ -void pci_remove_sysfs_dev_files(struct pci_dev *pdev) -{ - if (!sysfs_initialized) - return; - - pci_remove_resource_files(pdev); -} -#endif - -#ifdef HAVE_PCI_SYSFS_INIT +#ifdef HAVE_PCI_LEGACY static int __init pci_sysfs_init(void) { -#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) struct pci_bus *pbus = NULL; sysfs_initialized = 1; -#else - struct pci_dev *pdev = NULL; - struct pci_bus *pbus = NULL; - int retval; - sysfs_initialized = 1; - for_each_pci_dev(pdev) { - retval = pci_create_sysfs_dev_files(pdev); - if (retval) { - pci_dev_put(pdev); - return retval; - } - } -#endif while ((pbus = pci_find_next_bus(pbus))) pci_create_legacy_files(pbus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4a14f88e543a..71a1fde1e505 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -393,16 +393,12 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } #ifdef CONFIG_SYSFS -int pci_create_sysfs_dev_files(struct pci_dev *pdev); -void pci_remove_sysfs_dev_files(struct pci_dev *pdev); extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pci_dev_attr_groups[]; extern const struct attribute_group *pcibus_groups[]; extern const struct attribute_group *pci_bus_groups[]; extern const struct attribute_group pci_doe_sysfs_group; #else -static inline int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; } -static inline void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { } #define pci_dev_groups NULL #define pci_dev_attr_groups NULL #define pcibus_groups NULL diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index e9d519993853..6e796dbc5b29 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -26,7 +26,6 @@ static void pci_stop_dev(struct pci_dev *dev) device_release_driver(&dev->dev); pci_proc_detach_device(dev); - pci_remove_sysfs_dev_files(dev); of_pci_remove_node(dev); } diff --git a/include/linux/pci.h b/include/linux/pci.h index b998a56f6010..c56f2cf0d2ab 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -515,10 +515,6 @@ struct pci_dev { spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */ u32 saved_config_space[16]; /* Config space saved at suspend time */ struct hlist_head saved_cap_space; -#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) - struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ - struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ -#endif #ifdef CONFIG_HOTPLUG_PCI_PCIE unsigned int broken_cmd_compl:1; /* No compl for some cmds */ @@ -2533,11 +2529,6 @@ int pcibios_alloc_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev); resource_size_t pcibios_default_alignment(void); -#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) -extern int pci_create_resource_files(struct pci_dev *dev); -extern void pci_remove_resource_files(struct pci_dev *dev); -#endif - #if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) void __init pci_mmcfg_early_init(void); void __init pci_mmcfg_late_init(void); From e52e497e3778c99c3fb26ef6a47af6a219133f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:38 +0000 Subject: [PATCH 4576/5214] PCI: Add macros for legacy I/O and memory address space sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add defines for the standard PCI legacy address space sizes, replacing the raw literals used by the legacy sysfs attributes. Then, replace open-coded values with the newly added macros. No functional changes intended. Suggested-by: Ilpo Järvinen Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-20-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 4 ++-- include/linux/pci.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 97a482d6d67d..1e60f072f746 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1019,7 +1019,7 @@ void pci_create_legacy_files(struct pci_bus *b) sysfs_bin_attr_init(b->legacy_io); b->legacy_io->attr.name = "legacy_io"; - b->legacy_io->size = 0xffff; + b->legacy_io->size = PCI_LEGACY_IO_SIZE; b->legacy_io->attr.mode = 0600; b->legacy_io->read = pci_read_legacy_io; b->legacy_io->write = pci_write_legacy_io; @@ -1036,7 +1036,7 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_mem = b->legacy_io + 1; sysfs_bin_attr_init(b->legacy_mem); b->legacy_mem->attr.name = "legacy_mem"; - b->legacy_mem->size = 1024*1024; + b->legacy_mem->size = PCI_LEGACY_MEM_SIZE; b->legacy_mem->attr.mode = 0600; b->legacy_mem->mmap = pci_mmap_legacy_mem; /* See pci_create_attr() for motivation */ diff --git a/include/linux/pci.h b/include/linux/pci.h index c56f2cf0d2ab..e37677a8dd3c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -1169,6 +1170,10 @@ enum { /* These external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI +/* PCI legacy I/O port and memory address space sizes. */ +#define PCI_LEGACY_IO_SIZE (SZ_64K - 1) +#define PCI_LEGACY_MEM_SIZE SZ_1M + extern unsigned int pci_flags; static inline void pci_set_flags(int flags) { pci_flags = flags; } From 385ec1d40756e3bcb868814e305089b4edd32149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:39 +0000 Subject: [PATCH 4577/5214] alpha/PCI: Compute legacy size in pci_mmap_legacy_page_range() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, pci_mmap_legacy_page_range() reads the legacy resource size from bus->legacy_mem->size or bus->legacy_io->size. This couples the mmap bounds check to the struct pci_bus fields that will be removed when legacy attributes are converted to static definitions. Compute the size directly using PCI_LEGACY_MEM_SIZE (0x100000) and PCI_LEGACY_IO_SIZE (0xffff) macros, and shift by 5 bits for sparse systems. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-21-kwilczynski@kernel.org --- arch/alpha/kernel/pci-sysfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 435654ffa4a0..2038acbcfd2a 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -173,8 +173,11 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, int sparse = has_sparse(hose, mmap_type); unsigned long res_size; - res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size : - bus->legacy_io->size; + res_size = (mmap_type == pci_mmap_mem) ? PCI_LEGACY_MEM_SIZE : + PCI_LEGACY_IO_SIZE; + if (sparse) + res_size <<= 5; + if (!__legacy_mmap_fits(vma, res_size)) return -EINVAL; From 574b5470f8d43c85bf3dcb8c48efc2788a2bfb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:40 +0000 Subject: [PATCH 4578/5214] PCI/sysfs: Add __weak pci_legacy_has_sparse() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, Alpha's sparse/dense legacy attribute handling is done via pci_adjust_legacy_attr(), which updates dynamically allocated attributes at runtime. The upcoming conversion to static attributes needs a way to determine sparse support at visibility check time. Add a __weak pci_legacy_has_sparse() that returns false by default. Alpha overrides it to check has_sparse() on the bus host controller. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-22-kwilczynski@kernel.org --- arch/alpha/include/asm/pci.h | 2 ++ arch/alpha/kernel/pci-sysfs.c | 7 +++++++ drivers/pci/pci-sysfs.c | 6 ++++++ drivers/pci/pci.h | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index ef19295f2e33..95de7ffd59e8 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -86,6 +86,8 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, enum pci_mmap_state mmap_state); extern void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type); +extern bool pci_legacy_has_sparse(struct pci_bus *bus, + enum pci_mmap_state type); #define HAVE_PCI_LEGACY 1 extern const struct attribute_group pci_dev_resource_attr_group; diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2038acbcfd2a..20736a0c1a39 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -184,6 +184,13 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, return hose_mmap_page_range(hose, vma, mmap_type, sparse); } +bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type) +{ + struct pci_controller *hose = bus->sysdata; + + return has_sparse(hose, type); +} + /** * pci_adjust_legacy_attr - adjustment of legacy file attributes * @bus: bus to create files under diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 1e60f072f746..c1ba706844c9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -983,6 +983,12 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); } +bool __weak pci_legacy_has_sparse(struct pci_bus *bus, + enum pci_mmap_state type) +{ + return false; +} + /** * pci_adjust_legacy_attr - adjustment of legacy file attributes * @b: bus to create files under diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 71a1fde1e505..c64c7f5f0bcf 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -392,6 +392,10 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } +#ifdef HAVE_PCI_LEGACY +bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type); +#endif + #ifdef CONFIG_SYSFS extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pci_dev_attr_groups[]; From 4e14b965a625f2c2813bec0a6a7530426ae508fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:41 +0000 Subject: [PATCH 4579/5214] PCI/sysfs: Convert legacy I/O and memory attributes to static definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, legacy_io and legacy_mem are dynamically allocated and created by pci_create_legacy_files(), with pci_adjust_legacy_attr() updating the attributes at runtime on Alpha to rename them and shift the size for sparse addressing. Convert to four static const attributes (legacy_io, legacy_io_sparse, legacy_mem, legacy_mem_sparse) with .is_bin_visible() callbacks that use pci_legacy_has_sparse() to select the appropriate variant per bus. The sizes are compile-time constants and .size is set directly on each attribute. Register the groups in pcibus_groups[] under a HAVE_PCI_LEGACY guard so the driver model handles creation and removal automatically. Stub out pci_create_legacy_files() and pci_remove_legacy_files() as the dynamic creation is no longer needed. Remove the __weak pci_adjust_legacy_attr(), Alpha's override, and its declaration from both Alpha and PowerPC asm/pci.h headers. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-23-kwilczynski@kernel.org --- arch/alpha/include/asm/pci.h | 2 - arch/alpha/kernel/pci-sysfs.c | 38 ++---- arch/powerpc/include/asm/pci.h | 2 - drivers/pci/pci-sysfs.c | 221 +++++++++++++++++++-------------- 4 files changed, 135 insertions(+), 128 deletions(-) diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 95de7ffd59e8..ad5d1391e1fa 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -84,8 +84,6 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, extern int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, enum pci_mmap_state mmap_state); -extern void pci_adjust_legacy_attr(struct pci_bus *bus, - enum pci_mmap_state mmap_type); extern bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type); #define HAVE_PCI_LEGACY 1 diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 20736a0c1a39..94dbc470cd6c 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -191,30 +191,6 @@ bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type) return has_sparse(hose, type); } -/** - * pci_adjust_legacy_attr - adjustment of legacy file attributes - * @bus: bus to create files under - * @mmap_type: I/O port or memory - * - * Adjust file name and size for sparse mappings. - */ -void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type) -{ - struct pci_controller *hose = bus->sysdata; - - if (!has_sparse(hose, mmap_type)) - return; - - if (mmap_type == pci_mmap_mem) { - bus->legacy_mem->attr.name = "legacy_mem_sparse"; - bus->legacy_mem->size <<= 5; - } else { - bus->legacy_io->attr.name = "legacy_io_sparse"; - bus->legacy_io->size <<= 5; - } - return; -} - /* Legacy I/O bus read/write functions */ int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) { @@ -291,9 +267,9 @@ static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev, return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io; } -static inline umode_t __pci_dev_resource_is_visible(struct kobject *kobj, - const struct bin_attribute *a, - int bar) +static inline umode_t __pci_resource_attr_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); @@ -313,7 +289,7 @@ static umode_t pci_dev_resource_is_visible(struct kobject *kobj, if (has_sparse(hose, pci_bar_mmap_type(pdev, bar))) return 0; - return __pci_dev_resource_is_visible(kobj, a, bar); + return __pci_resource_attr_is_visible(kobj, a, bar); } static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj, @@ -330,7 +306,7 @@ static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj, if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) return 0; - return __pci_dev_resource_is_visible(kobj, a, bar); + return __pci_resource_attr_is_visible(kobj, a, bar); } static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj, @@ -346,14 +322,14 @@ static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj, return 0; if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) - return __pci_dev_resource_is_visible(kobj, a, bar); + return __pci_resource_attr_is_visible(kobj, a, bar); dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base : hose->dense_io_base; if (!dense_base) return 0; - return __pci_dev_resource_is_visible(kobj, a, bar); + return __pci_resource_attr_is_visible(kobj, a, bar); } static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj, diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 46a9c4491ed0..72f286e74786 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -82,8 +82,6 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, extern int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, enum pci_mmap_state mmap_state); -extern void pci_adjust_legacy_attr(struct pci_bus *bus, - enum pci_mmap_state mmap_type); #define HAVE_PCI_LEGACY 1 extern void pcibios_claim_one_bus(struct pci_bus *b); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c1ba706844c9..ae5470c185fa 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -676,11 +676,6 @@ static const struct attribute_group pcibus_group = { .attrs = pcibus_attrs, }; -const struct attribute_group *pcibus_groups[] = { - &pcibus_group, - NULL, -}; - static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -879,19 +874,6 @@ static const struct attribute_group pci_dev_config_attr_group = { .bin_size = pci_dev_config_attr_bin_size, }; -/* - * llseek operation for mmappable PCI resources. - * May be left unused if the arch doesn't provide them. - */ -static __maybe_unused loff_t -pci_llseek_resource_legacy(struct file *filep, - struct kobject *kobj __always_unused, - const struct bin_attribute *attr, - loff_t offset, int whence) -{ - return fixed_size_llseek(filep, offset, whence, attr->size); -} - #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -989,91 +971,144 @@ bool __weak pci_legacy_has_sparse(struct pci_bus *bus, return false; } -/** - * pci_adjust_legacy_attr - adjustment of legacy file attributes - * @b: bus to create files under - * @mmap_type: I/O port or memory - * - * Stub implementation. Can be overridden by arch if necessary. - */ -void __weak pci_adjust_legacy_attr(struct pci_bus *b, - enum pci_mmap_state mmap_type) +static inline umode_t __pci_legacy_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + enum pci_mmap_state type, + bool sparse) { + struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + + if (pci_legacy_has_sparse(bus, type) != sparse) + return 0; + + return a->attr.mode; } -/** - * pci_create_legacy_files - create legacy I/O port and memory files - * @b: bus to create files under - * - * Some platforms allow access to legacy I/O port and ISA memory space on - * a per-bus basis. This routine creates the files and ties them into - * their associated read, write and mmap files from pci-sysfs.c - * - * On error unwind, but don't propagate the error to the caller - * as it is ok to set up the PCI bus without these files. - */ -void pci_create_legacy_files(struct pci_bus *b) +static umode_t pci_legacy_io_is_visible(struct kobject *kobj, + const struct bin_attribute *a, int n) { - int error; - - if (!sysfs_initialized) - return; - - b->legacy_io = kzalloc_objs(struct bin_attribute, 2, GFP_ATOMIC); - if (!b->legacy_io) - goto kzalloc_err; - - sysfs_bin_attr_init(b->legacy_io); - b->legacy_io->attr.name = "legacy_io"; - b->legacy_io->size = PCI_LEGACY_IO_SIZE; - b->legacy_io->attr.mode = 0600; - b->legacy_io->read = pci_read_legacy_io; - b->legacy_io->write = pci_write_legacy_io; - /* See pci_create_attr() for motivation */ - b->legacy_io->llseek = pci_llseek_resource_legacy; - b->legacy_io->mmap = pci_mmap_legacy_io; - b->legacy_io->f_mapping = iomem_get_mapping; - pci_adjust_legacy_attr(b, pci_mmap_io); - error = device_create_bin_file(&b->dev, b->legacy_io); - if (error) - goto legacy_io_err; - - /* Allocated above after the legacy_io struct */ - b->legacy_mem = b->legacy_io + 1; - sysfs_bin_attr_init(b->legacy_mem); - b->legacy_mem->attr.name = "legacy_mem"; - b->legacy_mem->size = PCI_LEGACY_MEM_SIZE; - b->legacy_mem->attr.mode = 0600; - b->legacy_mem->mmap = pci_mmap_legacy_mem; - /* See pci_create_attr() for motivation */ - b->legacy_mem->llseek = pci_llseek_resource_legacy; - b->legacy_mem->f_mapping = iomem_get_mapping; - pci_adjust_legacy_attr(b, pci_mmap_mem); - error = device_create_bin_file(&b->dev, b->legacy_mem); - if (error) - goto legacy_mem_err; - - return; - -legacy_mem_err: - device_remove_bin_file(&b->dev, b->legacy_io); -legacy_io_err: - kfree(b->legacy_io); - b->legacy_io = NULL; -kzalloc_err: - dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n"); + return __pci_legacy_is_visible(kobj, a, pci_mmap_io, false); } -void pci_remove_legacy_files(struct pci_bus *b) +static umode_t pci_legacy_io_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) { - if (b->legacy_io) { - device_remove_bin_file(&b->dev, b->legacy_io); - device_remove_bin_file(&b->dev, b->legacy_mem); - kfree(b->legacy_io); /* both are allocated here */ - } + return __pci_legacy_is_visible(kobj, a, pci_mmap_io, true); } + +static umode_t pci_legacy_mem_is_visible(struct kobject *kobj, + const struct bin_attribute *a, int n) +{ + return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, false); +} + +static umode_t pci_legacy_mem_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, true); +} + +static loff_t pci_llseek_resource_legacy(struct file *filep, + struct kobject *kobj __always_unused, + const struct bin_attribute *attr, + loff_t offset, int whence) +{ + return fixed_size_llseek(filep, offset, whence, attr->size); +} + +static const struct bin_attribute pci_legacy_io_attr = { + .attr = { .name = "legacy_io", .mode = 0600 }, + .size = PCI_LEGACY_IO_SIZE, + .read = pci_read_legacy_io, + .write = pci_write_legacy_io, + .mmap = pci_mmap_legacy_io, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_io_sparse_attr = { + .attr = { .name = "legacy_io_sparse", .mode = 0600 }, + .size = PCI_LEGACY_IO_SIZE << 5, + .read = pci_read_legacy_io, + .write = pci_write_legacy_io, + .mmap = pci_mmap_legacy_io, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_mem_attr = { + .attr = { .name = "legacy_mem", .mode = 0600 }, + .size = PCI_LEGACY_MEM_SIZE, + .mmap = pci_mmap_legacy_mem, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_mem_sparse_attr = { + .attr = { .name = "legacy_mem_sparse", .mode = 0600 }, + .size = PCI_LEGACY_MEM_SIZE << 5, + .mmap = pci_mmap_legacy_mem, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute *const pci_legacy_io_attrs[] = { + &pci_legacy_io_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_io_sparse_attrs[] = { + &pci_legacy_io_sparse_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_mem_attrs[] = { + &pci_legacy_mem_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_mem_sparse_attrs[] = { + &pci_legacy_mem_sparse_attr, + NULL, +}; + +static const struct attribute_group pci_legacy_io_group = { + .bin_attrs = pci_legacy_io_attrs, + .is_bin_visible = pci_legacy_io_is_visible, +}; + +static const struct attribute_group pci_legacy_io_sparse_group = { + .bin_attrs = pci_legacy_io_sparse_attrs, + .is_bin_visible = pci_legacy_io_sparse_is_visible, +}; + +static const struct attribute_group pci_legacy_mem_group = { + .bin_attrs = pci_legacy_mem_attrs, + .is_bin_visible = pci_legacy_mem_is_visible, +}; + +static const struct attribute_group pci_legacy_mem_sparse_group = { + .bin_attrs = pci_legacy_mem_sparse_attrs, + .is_bin_visible = pci_legacy_mem_sparse_is_visible, +}; + +void pci_create_legacy_files(struct pci_bus *b) { } +void pci_remove_legacy_files(struct pci_bus *b) { } #endif /* HAVE_PCI_LEGACY */ +const struct attribute_group *pcibus_groups[] = { + &pcibus_group, +#ifdef HAVE_PCI_LEGACY + &pci_legacy_io_group, + &pci_legacy_io_sparse_group, + &pci_legacy_mem_group, + &pci_legacy_mem_sparse_group, +#endif + NULL, +}; + #if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) /** * pci_mmap_resource - map a PCI resource into user memory space From 2800a911ced1540baa78de7494218aaf1c7dc4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:42 +0000 Subject: [PATCH 4580/5214] PCI/sysfs: Remove pci_create_legacy_files() and pci_sysfs_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, pci_create_legacy_files() and pci_remove_legacy_files() are no-op stubs. With legacy attributes now handled by static groups registered via pcibus_groups[], no call site needs them. Remove both functions, their declarations, and the call sites in pci_register_host_bridge(), pci_alloc_child_bus(), and pci_remove_bus(). Remove the pci_sysfs_init() late_initcall and sysfs_initialized. The late_initcall originally existed to create all the dynamic PCI sysfs files, but with both resource and legacy attributes now handled by static groups, it is no longer needed. Remove the legacy_io and legacy_mem fields from struct pci_bus which were used to track the dynamically allocated legacy attributes. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-24-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 21 --------------------- drivers/pci/pci.h | 8 -------- drivers/pci/probe.c | 6 ------ drivers/pci/remove.c | 2 -- include/linux/pci.h | 2 -- 5 files changed, 39 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ae5470c185fa..15969930b4d1 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -37,10 +37,6 @@ #define ARCH_PCI_DEV_GROUPS #endif -#ifdef HAVE_PCI_LEGACY -static int sysfs_initialized; /* = 0 */ -#endif - /* show configuration fields */ #define pci_config_attr(field, format_string) \ static ssize_t \ @@ -1094,8 +1090,6 @@ static const struct attribute_group pci_legacy_mem_sparse_group = { .is_bin_visible = pci_legacy_mem_sparse_is_visible, }; -void pci_create_legacy_files(struct pci_bus *b) { } -void pci_remove_legacy_files(struct pci_bus *b) { } #endif /* HAVE_PCI_LEGACY */ const struct attribute_group *pcibus_groups[] = { @@ -1782,21 +1776,6 @@ static const struct attribute_group pci_dev_resource_resize_attr_group = { .is_visible = resource_resize_attr_is_visible, }; -#ifdef HAVE_PCI_LEGACY -static int __init pci_sysfs_init(void) -{ - struct pci_bus *pbus = NULL; - - sysfs_initialized = 1; - - while ((pbus = pci_find_next_bus(pbus))) - pci_create_legacy_files(pbus); - - return 0; -} -late_initcall(pci_sysfs_init); -#endif - static struct attribute *pci_dev_dev_attrs[] = { &dev_attr_boot_vga.attr, &dev_attr_serial_number.attr, diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index c64c7f5f0bcf..4d17dab4662c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -358,14 +358,6 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; } int pci_hp_add_bridge(struct pci_dev *dev); bool pci_hp_spurious_link_change(struct pci_dev *pdev); -#if defined(CONFIG_SYSFS) && defined(HAVE_PCI_LEGACY) -void pci_create_legacy_files(struct pci_bus *bus); -void pci_remove_legacy_files(struct pci_bus *bus); -#else -static inline void pci_create_legacy_files(struct pci_bus *bus) { } -static inline void pci_remove_legacy_files(struct pci_bus *bus) { } -#endif - /* Lock for read/write access to pci device and bus lists */ extern struct rw_semaphore pci_bus_sem; extern struct mutex pci_slot_mutex; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc..748c7a198262 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1073,9 +1073,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) dev_err(&bus->dev, "failed to add bus: %d\n", err); } - /* Create legacy_io and legacy_mem files for this bus */ - pci_create_legacy_files(bus); - if (parent) dev_info(parent, "PCI host bridge to bus %s\n", name); else @@ -1281,9 +1278,6 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, dev_err(&child->dev, "failed to add bus: %d\n", ret); } - /* Create legacy_io and legacy_mem files for this bus */ - pci_create_legacy_files(child); - return child; } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 6e796dbc5b29..d8bffa21498a 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -65,8 +65,6 @@ void pci_remove_bus(struct pci_bus *bus) list_del(&bus->node); pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); - pci_remove_legacy_files(bus); - if (bus->ops->remove_bus) bus->ops->remove_bus(bus); diff --git a/include/linux/pci.h b/include/linux/pci.h index e37677a8dd3c..74b767012766 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -726,8 +726,6 @@ struct pci_bus { pci_bus_flags_t bus_flags; /* Inherited by child buses */ struct device *bridge; struct device dev; - struct bin_attribute *legacy_io; /* Legacy I/O for this bus */ - struct bin_attribute *legacy_mem; /* Legacy mem */ unsigned int is_added:1; unsigned int unsafe_warn:1; /* warned about RW1C config write */ unsigned int flit_mode:1; /* Link in Flit mode */ From c9b4bd6c0839c855b7cc221af0283c1900036fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:43 +0000 Subject: [PATCH 4581/5214] PCI/sysfs: Limit BAR resize attribute scope to platforms with PCI mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, __resource_resize_store() uses sysfs_remove_groups() and sysfs_create_groups() on pci_dev_resource_attr_groups to tear down and recreate the resourceN files after a BAR resize, so the updated BAR sizes are visible in sysfs. The resourceN files only exist on platforms that define HAVE_PCI_MMAP or ARCH_GENERIC_PCI_MMAP_RESOURCE. On platforms that define neither, pci_dev_resource_attr_groups is NULL and the sysfs_remove_groups() and sysfs_create_groups() calls in __resource_resize_store() become no-ops. Resizable BAR (ReBAR) is a PCI Express Extended Capability (PCI_EXT_CAP_ID_REBAR) that requires PCIe extended config space. Every PCIe-capable architecture defines HAVE_PCI_MMAP or ARCH_GENERIC_PCI_MMAP_RESOURCE (via arch headers or the asm-generic/pci.h fallback). Architectures without either only support conventional PCI and cannot have any ReBAR-capable devices. Move the resize show and store helpers, the per-BAR attribute definitions, and the attribute group behind the existing #ifdef HAVE_PCI_MMAP || ARCH_GENERIC_PCI_MMAP_RESOURCE guard, and fold the group reference in pci_dev_groups[] into the existing #if block. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Shivaprasad G Bhat Link: https://patch.msgid.link/20260508043543.217179-25-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 15969930b4d1..d4b5f385a6cd 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1661,6 +1661,7 @@ static const struct attribute_group pci_dev_reset_method_attr_group = { .is_visible = pci_dev_reset_attr_is_visible, }; +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) static ssize_t __resource_resize_show(struct device *dev, int n, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); @@ -1775,6 +1776,7 @@ static const struct attribute_group pci_dev_resource_resize_attr_group = { .attrs = resource_resize_attrs, .is_visible = resource_resize_attr_is_visible, }; +#endif static struct attribute *pci_dev_dev_attrs[] = { &dev_attr_boot_vga.attr, @@ -1849,8 +1851,8 @@ const struct attribute_group *pci_dev_groups[] = { &pci_dev_resource_io_attr_group, &pci_dev_resource_uc_attr_group, &pci_dev_resource_wc_attr_group, -#endif &pci_dev_resource_resize_attr_group, +#endif &pci_dev_config_attr_group, &pci_dev_rom_attr_group, &pci_dev_reset_attr_group, From 92742802ecbf215a2b60dcfd326d2213595010f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 12 Jun 2026 18:24:48 +0000 Subject: [PATCH 4582/5214] PCI/sysfs: Use kstrtobool() to parse the ROM attribute input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_write_rom() controls access to the ROM content through the corresponding sysfs attribute, and treats the input as a request to disable only when it matches the string "0\n" exactly: if ((off == 0) && (*buf == '0') && (count == 2)) The count == 2 condition encodes the trailing newline that echo(1) appends. This was found when userspace wrote "0" without a trailing newline aiming to disable access, which failed to match the condition above and enabled access instead. For example: $ echo 0 > rom # "0\n", count 2, access disabled $ echo -n 0 > rom # "0", count 1, access enabled $ echo > rom # "", count 1, access enabled (likely not desirable) Parse the input with kstrtobool(), which handles common boolean inputs such as "0", "1", "n", "y" or "off", "on", with or without a trailing newline, so both of the above disable access, and update the now stale comment. As a side effect, input that does not parse as a boolean is rejected with -EINVAL rather than enabling access. The documented "0" and "1" continue to work as before, and rejecting malformed input brings the attribute in line with how sysfs attributes typically handle it. Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260612182448.552406-1-kwilczynski@kernel.org --- drivers/pci/pci-sysfs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d4b5f385a6cd..5ec0b245a69b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1418,18 +1418,19 @@ static const struct attribute_group *pci_dev_resource_attr_groups[] = { * @off: file offset * @count: number of byte in input * - * writing anything except 0 enables it + * Writing a boolean value enables or disables the ROM display. */ static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + bool enable; - if ((off == 0) && (*buf == '0') && (count == 2)) - pdev->rom_attr_enabled = 0; - else - pdev->rom_attr_enabled = 1; + if (kstrtobool(buf, &enable)) + return -EINVAL; + + pdev->rom_attr_enabled = enable; return count; } From 3b61bb3fe1e6582673f19cc83f8d81644f2a5092 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 23 Jun 2026 12:00:04 -0700 Subject: [PATCH 4583/5214] kernel-doc: xforms: support __SYSFS_FUNCTION_ALTERNATIVE() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for __SYSFS_FUNCTION_ALTERNATIVE() to create a union of its members (as though CONFIG_CFI is unset). Fixes these docs build warnings: WARNING: include/linux/device.h:117 Invalid param: __SYSFS_FUNCTION_ALTERNATIVE( ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf) WARNING: include/linux/device.h:117 struct member '__SYSFS_FUNCTION_ALTERNATIVE( ssize_t (*show' not described in 'device_attribute' WARNING: include/linux/device.h:117 Invalid param: __SYSFS_FUNCTION_ALTERNATIVE( ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) WARNING: include/linux/device.h:117 struct member '__SYSFS_FUNCTION_ALTERNATIVE( ssize_t (*store' not described in 'device_attribute' Signed-off-by: Randy Dunlap Reviewed-by: Thomas Weißschuh Signed-off-by: Jonathan Corbet Message-ID: <20260623190006.406571-1-rdunlap@infradead.org> --- tools/lib/python/kdoc/xforms_lists.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lib/python/kdoc/xforms_lists.py b/tools/lib/python/kdoc/xforms_lists.py index aab70e5eaa6f..0734f9f94bf8 100644 --- a/tools/lib/python/kdoc/xforms_lists.py +++ b/tools/lib/python/kdoc/xforms_lists.py @@ -49,6 +49,7 @@ class CTransforms: (CMatch("DEFINE_DMA_UNMAP_ADDR"), r"dma_addr_t \1"), (CMatch("DEFINE_DMA_UNMAP_LEN"), r"__u32 \1"), (CMatch("VIRTIO_DECLARE_FEATURES"), r"union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }"), + (CMatch("__SYSFS_FUNCTION_ALTERNATIVE"), r"union { \1+ }"), (CMatch("__attribute__"), ""), # From afb5a55498e121b1382f139512c8a3ea3de68e49 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Mon, 20 Apr 2026 23:03:16 +0200 Subject: [PATCH 4584/5214] docs/mm: clarify that we are not looking for LLM generated content Let's make it clear that we are not looking for LLM generated content from contributors not familiar with the details of MM, as it shifts the real work onto reviewers. Signed-off-by: David Hildenbrand (Arm) Acked-by: Dev Jain Acked-by: Michal Hocko Acked-by: Zi Yan Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Lorenzo Stoakes Acked-by: Mike Rapoport (Microsoft) Acked-by: Harry Yoo (Oracle) Acked-by: SeongJae Park Signed-off-by: Jonathan Corbet Message-ID: <20260420-llmdoc-v1-1-47d2091177c4@kernel.org> --- Documentation/mm/index.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/mm/index.rst b/Documentation/mm/index.rst index 7aa2a8886908..13a79f5d092c 100644 --- a/Documentation/mm/index.rst +++ b/Documentation/mm/index.rst @@ -7,6 +7,19 @@ of Linux. If you are looking for advice on simply allocating memory, see the :ref:`memory_allocation`. For controlling and tuning guides, see the :doc:`admin guide <../admin-guide/mm/index>`. +.. note:: + + Unfortunately, parts of this guide are still incomplete or missing. + While we appreciate contributions, documentation in this area is hard + to get right and requires a lot of attention to detail. New contributors + should reach out to the relevant maintainers early. + + This guide is expected to reflect reality, which requires contributors + to have a detailed understanding. Documentation generated with LLMs + by contributors unfamiliar with these details shifts the real work onto + reviewers, which is why such contributions will be rejected without + further comment. + .. toctree:: :maxdepth: 1 From de5c46373eb8148aa92c024cf30d26a6d495e278 Mon Sep 17 00:00:00 2001 From: Doehyun Baek Date: Mon, 22 Jun 2026 18:18:21 +0000 Subject: [PATCH 4585/5214] Docs/driver-api/uio-howto: document mmap_prepare callback The UIO howto still documents an mmap callback in struct uio_info. That field was replaced by mmap_prepare, which takes a struct vm_area_desc. A UIO driver following the current howto no longer builds because struct uio_info has no mmap member. Update the documented callback signature and matching text to match the current API. Fixes: 933f05f58ac6 ("uio: replace deprecated mmap hook with mmap_prepare in uio_info") Signed-off-by: Doehyun Baek Reviewed-by: Lorenzo Stoakes Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260622181821.1195257-1-doehyunbaek@gmail.com> --- Documentation/driver-api/uio-howto.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst index 907ffa3b38f5..c08472dfbcfe 100644 --- a/Documentation/driver-api/uio-howto.rst +++ b/Documentation/driver-api/uio-howto.rst @@ -246,10 +246,10 @@ the members are required, others are optional. hardware interrupt number. The flags given here will be used in the call to :c:func:`request_irq()`. -- ``int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)``: +- ``int (*mmap_prepare)(struct uio_info *info, struct vm_area_desc *desc)``: Optional. If you need a special :c:func:`mmap()` function, you can set it here. If this pointer is not NULL, your - :c:func:`mmap()` will be called instead of the built-in one. + ``mmap_prepare`` will be called instead of the built-in one. - ``int (*open)(struct uio_info *info, struct inode *inode)``: Optional. You might want to have your own :c:func:`open()`, From 8a66c094793ec68cf15260538017b0f661ae400a Mon Sep 17 00:00:00 2001 From: Yudistira Putra Date: Mon, 22 Jun 2026 10:37:35 -0400 Subject: [PATCH 4586/5214] Documentation: tracing: fix typo in events documentation Fix a typo in the tracing events documentation: "can by built up" should be "can be built up". Signed-off-by: Yudistira Putra Signed-off-by: Jonathan Corbet Message-ID: <20260622143735.71778-1-pyudistira519@gmail.com> --- Documentation/trace/events.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst index 18d112963dec..581f2260614b 100644 --- a/Documentation/trace/events.rst +++ b/Documentation/trace/events.rst @@ -1064,7 +1064,7 @@ correct command type, and a pointer to an event-specific run_command() callback that will be called to actually execute the event-specific command function. -Once that's done, the command string can by built up by successive +Once that's done, the command string can be built up by successive calls to argument-adding functions. To add a single argument, define and initialize a struct dynevent_arg From dd07489fead45f0947aaa4cfb72066594df0fde4 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 21 Jun 2026 07:40:35 +0800 Subject: [PATCH 4587/5214] docs: kgdb: Fix path of driver options The correct path of driver options should be /sys/module//parameters/